This commit was manufactured by cvs2svn to create branch 'vserver'.
authorPlanet-Lab Support <support@planet-lab.org>
Mon, 8 Aug 2005 20:49:13 +0000 (20:49 +0000)
committerPlanet-Lab Support <support@planet-lab.org>
Mon, 8 Aug 2005 20:49:13 +0000 (20:49 +0000)
1177 files changed:
Documentation/DocBook/librs.tmpl [new file with mode: 0644]
Documentation/DocBook/mtdnand.tmpl [new file with mode: 0644]
Documentation/PCIEBUS-HOWTO.txt [new file with mode: 0644]
Documentation/README.cycladesZ [new file with mode: 0644]
Documentation/SecurityBugs [new file with mode: 0644]
Documentation/aoe/aoe.txt [new file with mode: 0644]
Documentation/aoe/autoload.sh [new file with mode: 0644]
Documentation/aoe/mkdevs.sh [new file with mode: 0644]
Documentation/aoe/mkshelf.sh [new file with mode: 0644]
Documentation/aoe/status.sh [new file with mode: 0644]
Documentation/arm/Samsung-S3C24XX/Suspend.txt [new file with mode: 0644]
Documentation/atomic_ops.txt [new file with mode: 0644]
Documentation/cdrom/packet-writing.txt [new file with mode: 0644]
Documentation/cpu-freq/cpufreq-nforce2.txt [new file with mode: 0644]
Documentation/dvb/README.dibusb [new file with mode: 0644]
Documentation/dvb/get_dvb_firmware [new file with mode: 0644]
Documentation/dvb/udev.txt [new file with mode: 0644]
Documentation/feature-removal-schedule.txt [new file with mode: 0644]
Documentation/filesystems/sysfs-pci.txt [new file with mode: 0644]
Documentation/fujitsu/frv/README.txt [new file with mode: 0644]
Documentation/fujitsu/frv/atomic-ops.txt [new file with mode: 0644]
Documentation/fujitsu/frv/booting.txt [new file with mode: 0644]
Documentation/fujitsu/frv/clock.txt [new file with mode: 0644]
Documentation/fujitsu/frv/configuring.txt [new file with mode: 0644]
Documentation/fujitsu/frv/features.txt [new file with mode: 0644]
Documentation/fujitsu/frv/gdbinit [new file with mode: 0644]
Documentation/fujitsu/frv/gdbstub.txt [new file with mode: 0644]
Documentation/fujitsu/frv/mmu-layout.txt [new file with mode: 0644]
Documentation/i2c/chips/smsc47b397.txt [new file with mode: 0644]
Documentation/i2c/i2c-stub [new file with mode: 0644]
Documentation/ia64/serial.txt [new file with mode: 0644]
Documentation/ibm-acpi.txt [new file with mode: 0644]
Documentation/infiniband/ipoib.txt [new file with mode: 0644]
Documentation/infiniband/sysfs.txt [new file with mode: 0644]
Documentation/infiniband/user_mad.txt [new file with mode: 0644]
Documentation/ioctl/cdrom.txt [new file with mode: 0644]
Documentation/ioctl/hdio.txt [new file with mode: 0644]
Documentation/keys.txt [new file with mode: 0644]
Documentation/networking/README.ipw2100 [new file with mode: 0644]
Documentation/networking/README.ipw2200 [new file with mode: 0644]
Documentation/networking/proc_net_tcp.txt [new file with mode: 0644]
Documentation/nommu-mmap.txt [new file with mode: 0644]
Documentation/power/kernel_threads.txt [new file with mode: 0644]
Documentation/power/video_extension.txt [new file with mode: 0644]
Documentation/powerpc/cpu_features.txt [new file with mode: 0644]
Documentation/powerpc/eeh-pci-error-recovery.txt [new file with mode: 0644]
Documentation/prio_tree.txt [new file with mode: 0644]
Documentation/s390/monreader.txt [new file with mode: 0644]
Documentation/scsi/ChangeLog.1992-1997 [new file with mode: 0644]
Documentation/seclvl.txt [new file with mode: 0644]
Documentation/sound/alsa/Bt87x.txt [new file with mode: 0644]
Documentation/sparse.txt [new file with mode: 0644]
Documentation/stable_api_nonsense.txt [new file with mode: 0644]
Documentation/usb/gadget_serial.txt [new file with mode: 0644]
Documentation/w1/w1.generic [new file with mode: 0644]
arch/arm/boot/compressed/head-sharpsl.S [new file with mode: 0644]
arch/arm/common/icst307.c [new file with mode: 0644]
arch/arm/common/rtctime.c [new file with mode: 0644]
arch/arm/common/scoop.c [new file with mode: 0644]
arch/arm/configs/iq80332_defconfig [new file with mode: 0644]
arch/arm/configs/omap_h2_1610_defconfig [new file with mode: 0644]
arch/arm/configs/simpad_defconfig [new file with mode: 0644]
arch/arm/kernel/smp.c [new file with mode: 0644]
arch/arm/lib/io-readsl.S [new file with mode: 0644]
arch/arm/mach-clps711x/Makefile.boot [new file with mode: 0644]
arch/arm/mach-clps711x/common.h [new file with mode: 0644]
arch/arm/mach-clps7500/Makefile.boot [new file with mode: 0644]
arch/arm/mach-ebsa110/Makefile.boot [new file with mode: 0644]
arch/arm/mach-epxa10db/Makefile.boot [new file with mode: 0644]
arch/arm/mach-footbridge/Makefile.boot [new file with mode: 0644]
arch/arm/mach-footbridge/co285.c [new file with mode: 0644]
arch/arm/mach-footbridge/common.c [new file with mode: 0644]
arch/arm/mach-footbridge/common.h [new file with mode: 0644]
arch/arm/mach-footbridge/dc21285-timer.c [new file with mode: 0644]
arch/arm/mach-footbridge/ebsa285.c [new file with mode: 0644]
arch/arm/mach-footbridge/isa-timer.c [new file with mode: 0644]
arch/arm/mach-footbridge/isa.c [new file with mode: 0644]
arch/arm/mach-footbridge/personal.c [new file with mode: 0644]
arch/arm/mach-h720x/Makefile.boot [new file with mode: 0644]
arch/arm/mach-h720x/common.h [new file with mode: 0644]
arch/arm/mach-imx/Makefile.boot [new file with mode: 0644]
arch/arm/mach-integrator/Makefile.boot [new file with mode: 0644]
arch/arm/mach-integrator/common.h [new file with mode: 0644]
arch/arm/mach-iop3xx/Makefile.boot [new file with mode: 0644]
arch/arm/mach-iop3xx/iq80332-mm.c [new file with mode: 0644]
arch/arm/mach-iop3xx/iq80332-pci.c [new file with mode: 0644]
arch/arm/mach-ixp2000/Makefile.boot [new file with mode: 0644]
arch/arm/mach-ixp4xx/Makefile.boot [new file with mode: 0644]
arch/arm/mach-ixp4xx/gtwx5715-pci.c [new file with mode: 0644]
arch/arm/mach-ixp4xx/gtwx5715-setup.c [new file with mode: 0644]
arch/arm/mach-ixp4xx/ixdpg425-pci.c [new file with mode: 0644]
arch/arm/mach-l7200/Makefile.boot [new file with mode: 0644]
arch/arm/mach-lh7a40x/Makefile.boot [new file with mode: 0644]
arch/arm/mach-lh7a40x/common.h [new file with mode: 0644]
arch/arm/mach-omap/Makefile.boot [new file with mode: 0644]
arch/arm/mach-omap/clock.c [new file with mode: 0644]
arch/arm/mach-omap/clock.h [new file with mode: 0644]
arch/arm/mach-omap/pm.c [new file with mode: 0644]
arch/arm/mach-omap/sleep.S [new file with mode: 0644]
arch/arm/mach-pxa/Makefile.boot [new file with mode: 0644]
arch/arm/mach-pxa/corgi.c [new file with mode: 0644]
arch/arm/mach-pxa/corgi_ssp.c [new file with mode: 0644]
arch/arm/mach-pxa/ssp.c [new file with mode: 0644]
arch/arm/mach-rpc/Makefile.boot [new file with mode: 0644]
arch/arm/mach-s3c2410/Makefile.boot [new file with mode: 0644]
arch/arm/mach-s3c2410/mach-rx3715.c [new file with mode: 0644]
arch/arm/mach-s3c2410/pm.c [new file with mode: 0644]
arch/arm/mach-s3c2410/pm.h [new file with mode: 0644]
arch/arm/mach-s3c2410/sleep.S [new file with mode: 0644]
arch/arm/mach-sa1100/Makefile.boot [new file with mode: 0644]
arch/arm/mach-shark/Makefile.boot [new file with mode: 0644]
arch/arm/mach-versatile/Kconfig [new file with mode: 0644]
arch/arm/mach-versatile/Makefile.boot [new file with mode: 0644]
arch/arm/mach-versatile/core.h [new file with mode: 0644]
arch/arm/mach-versatile/versatile_ab.c [new file with mode: 0644]
arch/arm/mach-versatile/versatile_pb.c [new file with mode: 0644]
arch/arm26/kernel/calls.S [new file with mode: 0644]
arch/arm26/kernel/head.S [new file with mode: 0644]
arch/arm26/lib/io-readsl.S [new file with mode: 0644]
arch/arm26/lib/io-readsw.S [new file with mode: 0644]
arch/arm26/lib/io-writesw.S [new file with mode: 0644]
arch/arm26/machine/latches.c [new file with mode: 0644]
arch/arm26/mm/memc.c [new file with mode: 0644]
arch/arm26/mm/small_page.c [new file with mode: 0644]
arch/cris/arch-v10/kernel/crisksyms.c [new file with mode: 0644]
arch/frv/Kconfig [new file with mode: 0644]
arch/frv/Makefile [new file with mode: 0644]
arch/frv/boot/Makefile [new file with mode: 0644]
arch/frv/kernel/Makefile [new file with mode: 0644]
arch/frv/kernel/break.S [new file with mode: 0644]
arch/frv/kernel/cmode.S [new file with mode: 0644]
arch/frv/kernel/debug-stub.c [new file with mode: 0644]
arch/frv/kernel/dma.c [new file with mode: 0644]
arch/frv/kernel/entry-table.S [new file with mode: 0644]
arch/frv/kernel/entry.S [new file with mode: 0644]
arch/frv/kernel/frv_ksyms.c [new file with mode: 0644]
arch/frv/kernel/gdb-io.c [new file with mode: 0644]
arch/frv/kernel/gdb-io.h [new file with mode: 0644]
arch/frv/kernel/gdb-stub.c [new file with mode: 0644]
arch/frv/kernel/head-mmu-fr451.S [new file with mode: 0644]
arch/frv/kernel/head-uc-fr401.S [new file with mode: 0644]
arch/frv/kernel/head-uc-fr451.S [new file with mode: 0644]
arch/frv/kernel/head-uc-fr555.S [new file with mode: 0644]
arch/frv/kernel/head.S [new file with mode: 0644]
arch/frv/kernel/head.inc [new file with mode: 0644]
arch/frv/kernel/init_task.c [new file with mode: 0644]
arch/frv/kernel/irq-mb93091.c [new file with mode: 0644]
arch/frv/kernel/irq-mb93093.c [new file with mode: 0644]
arch/frv/kernel/irq-mb93493.c [new file with mode: 0644]
arch/frv/kernel/irq-routing.c [new file with mode: 0644]
arch/frv/kernel/irq.c [new file with mode: 0644]
arch/frv/kernel/kernel_thread.S [new file with mode: 0644]
arch/frv/kernel/local.h [new file with mode: 0644]
arch/frv/kernel/pm-mb93093.c [new file with mode: 0644]
arch/frv/kernel/pm.c [new file with mode: 0644]
arch/frv/kernel/process.c [new file with mode: 0644]
arch/frv/kernel/ptrace.c [new file with mode: 0644]
arch/frv/kernel/semaphore.c [new file with mode: 0644]
arch/frv/kernel/setup.c [new file with mode: 0644]
arch/frv/kernel/signal.c [new file with mode: 0644]
arch/frv/kernel/sleep.S [new file with mode: 0644]
arch/frv/kernel/switch_to.S [new file with mode: 0644]
arch/frv/kernel/sys_frv.c [new file with mode: 0644]
arch/frv/kernel/sysctl.c [new file with mode: 0644]
arch/frv/kernel/time.c [new file with mode: 0644]
arch/frv/kernel/traps.c [new file with mode: 0644]
arch/frv/kernel/uaccess.c [new file with mode: 0644]
arch/frv/kernel/vmlinux.lds.S [new file with mode: 0644]
arch/frv/lib/Makefile [new file with mode: 0644]
arch/frv/lib/__ashldi3.S [new file with mode: 0644]
arch/frv/lib/__ashrdi3.S [new file with mode: 0644]
arch/frv/lib/__lshrdi3.S [new file with mode: 0644]
arch/frv/lib/__muldi3.S [new file with mode: 0644]
arch/frv/lib/__negdi2.S [new file with mode: 0644]
arch/frv/lib/atomic-ops.S [new file with mode: 0644]
arch/frv/lib/cache.S [new file with mode: 0644]
arch/frv/lib/checksum.c [new file with mode: 0644]
arch/frv/lib/insl_ns.S [new file with mode: 0644]
arch/frv/lib/insl_sw.S [new file with mode: 0644]
arch/frv/lib/memcpy.S [new file with mode: 0644]
arch/frv/lib/memset.S [new file with mode: 0644]
arch/frv/lib/outsl_ns.S [new file with mode: 0644]
arch/frv/lib/outsl_sw.S [new file with mode: 0644]
arch/frv/mb93090-mb00/Makefile [new file with mode: 0644]
arch/frv/mb93090-mb00/pci-dma-nommu.c [new file with mode: 0644]
arch/frv/mb93090-mb00/pci-dma.c [new file with mode: 0644]
arch/frv/mb93090-mb00/pci-frv.c [new file with mode: 0644]
arch/frv/mb93090-mb00/pci-frv.h [new file with mode: 0644]
arch/frv/mb93090-mb00/pci-irq.c [new file with mode: 0644]
arch/frv/mb93090-mb00/pci-vdk.c [new file with mode: 0644]
arch/frv/mm/Makefile [new file with mode: 0644]
arch/frv/mm/cache-page.c [new file with mode: 0644]
arch/frv/mm/dma-alloc.c [new file with mode: 0644]
arch/frv/mm/elf-fdpic.c [new file with mode: 0644]
arch/frv/mm/extable.c [new file with mode: 0644]
arch/frv/mm/fault.c [new file with mode: 0644]
arch/frv/mm/highmem.c [new file with mode: 0644]
arch/frv/mm/init.c [new file with mode: 0644]
arch/frv/mm/kmap.c [new file with mode: 0644]
arch/frv/mm/mmu-context.c [new file with mode: 0644]
arch/frv/mm/pgalloc.c [new file with mode: 0644]
arch/frv/mm/tlb-flush.S [new file with mode: 0644]
arch/frv/mm/tlb-miss.S [new file with mode: 0644]
arch/frv/mm/unaligned.c [new file with mode: 0644]
arch/i386/kernel/acpi/earlyquirk.c [new file with mode: 0644]
arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c [new file with mode: 0644]
arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c [new file with mode: 0644]
arch/i386/kernel/cpu/cpufreq/speedstep-est-common.h [new file with mode: 0644]
arch/i386/kernel/cpu/intel_cacheinfo.c [new file with mode: 0644]
arch/i386/kernel/crash_dump.c [new file with mode: 0644]
arch/i386/kernel/quirks.c [new file with mode: 0644]
arch/i386/oprofile/backtrace.c [new file with mode: 0644]
arch/ia64/hp/common/hwsw_iommu.c [new file with mode: 0644]
arch/ia64/hp/zx1/hpzx1_swiotlb_machvec.c [new file with mode: 0644]
arch/ia64/kernel/domain.c [new file with mode: 0644]
arch/ia64/kernel/topology.c [new file with mode: 0644]
arch/ia64/oprofile/backtrace.c [new file with mode: 0644]
arch/ia64/sn/include/ioerror.h [new file with mode: 0644]
arch/ia64/sn/include/pci/pcibr_provider.h [new file with mode: 0644]
arch/ia64/sn/include/pci/pcibus_provider_defs.h [new file with mode: 0644]
arch/ia64/sn/include/pci/pcidev.h [new file with mode: 0644]
arch/ia64/sn/include/pci/pic.h [new file with mode: 0644]
arch/ia64/sn/include/pci/tiocp.h [new file with mode: 0644]
arch/ia64/sn/include/tio.h [new file with mode: 0644]
arch/ia64/sn/include/xtalk/hubdev.h [new file with mode: 0644]
arch/ia64/sn/include/xtalk/xbow.h [new file with mode: 0644]
arch/ia64/sn/include/xtalk/xwidgetdev.h [new file with mode: 0644]
arch/ia64/sn/kernel/bte_error.c [new file with mode: 0644]
arch/ia64/sn/kernel/huberror.c [new file with mode: 0644]
arch/ia64/sn/kernel/io_init.c [new file with mode: 0644]
arch/ia64/sn/kernel/iomv.c [new file with mode: 0644]
arch/ia64/sn/kernel/klconflib.c [new file with mode: 0644]
arch/ia64/sn/pci/Makefile [new file with mode: 0644]
arch/ia64/sn/pci/pci_dma.c [new file with mode: 0644]
arch/ia64/sn/pci/pcibr/Makefile [new file with mode: 0644]
arch/ia64/sn/pci/pcibr/pcibr_ate.c [new file with mode: 0644]
arch/ia64/sn/pci/pcibr/pcibr_dma.c [new file with mode: 0644]
arch/ia64/sn/pci/pcibr/pcibr_provider.c [new file with mode: 0644]
arch/ia64/sn/pci/pcibr/pcibr_reg.c [new file with mode: 0644]
arch/m32r/Kconfig.debug [new file with mode: 0644]
arch/m32r/m32700ut/dot.gdbinit_400MHz_32MB [new file with mode: 0644]
arch/m32r/mappi2/defconfig.vdec2 [new file with mode: 0644]
arch/m32r/mappi2/dot.gdbinit.vdec2 [new file with mode: 0644]
arch/m68k/configs/amiga_defconfig [new file with mode: 0644]
arch/m68k/configs/apollo_defconfig [new file with mode: 0644]
arch/m68k/configs/atari_defconfig [new file with mode: 0644]
arch/m68k/configs/bvme6000_defconfig [new file with mode: 0644]
arch/m68k/configs/hp300_defconfig [new file with mode: 0644]
arch/m68k/configs/mac_defconfig [new file with mode: 0644]
arch/m68k/configs/mvme147_defconfig [new file with mode: 0644]
arch/m68k/configs/mvme16x_defconfig [new file with mode: 0644]
arch/m68k/configs/q40_defconfig [new file with mode: 0644]
arch/m68k/configs/sun3_defconfig [new file with mode: 0644]
arch/m68k/configs/sun3x_defconfig [new file with mode: 0644]
arch/m68knommu/lib/delay.c [new file with mode: 0644]
arch/m68knommu/platform/5272/CANCam/crt0_ram.S [new file with mode: 0644]
arch/m68knommu/platform/5272/SCALES/crt0_ram.S [new file with mode: 0644]
arch/m68knommu/platform/527x/M5271EVB/crt0_ram.S [new file with mode: 0644]
arch/m68knommu/platform/527x/M5275EVB/crt0_ram.S [new file with mode: 0644]
arch/m68knommu/platform/527x/Makefile [new file with mode: 0644]
arch/m68knommu/platform/527x/config.c [new file with mode: 0644]
arch/m68knommu/platform/528x/M5282EVB/crt0_ram.S [new file with mode: 0644]
arch/m68knommu/platform/528x/Makefile [new file with mode: 0644]
arch/m68knommu/platform/528x/config.c [new file with mode: 0644]
arch/m68knommu/platform/528x/senTec/crt0_ram.S [new file with mode: 0644]
arch/m68knommu/platform/5307/head.S [new file with mode: 0644]
arch/m68knommu/platform/5307/pit.c [new file with mode: 0644]
arch/mips/au1000/common/platform.c [new file with mode: 0644]
arch/mips/configs/db1550_defconfig [new file with mode: 0644]
arch/mips/configs/ocelot_3_defconfig [new file with mode: 0644]
arch/mips/kernel/irix5sys.S [new file with mode: 0644]
arch/mips/kernel/irq-msc01.c [new file with mode: 0644]
arch/mips/kernel/irq-rm9000.c [new file with mode: 0644]
arch/mips/kernel/signal-common.h [new file with mode: 0644]
arch/mips/lib/iomap.c [new file with mode: 0644]
arch/mips/mm/dma-ip32.c [new file with mode: 0644]
arch/mips/mm/tlbex-fault.S [new file with mode: 0644]
arch/mips/mm/tlbex.c [new file with mode: 0644]
arch/mips/momentum/ocelot_3/Makefile [new file with mode: 0644]
arch/mips/momentum/ocelot_3/int-handler.S [new file with mode: 0644]
arch/mips/momentum/ocelot_3/irq.c [new file with mode: 0644]
arch/mips/momentum/ocelot_3/ocelot_3_fpga.h [new file with mode: 0644]
arch/mips/momentum/ocelot_3/prom.c [new file with mode: 0644]
arch/mips/momentum/ocelot_3/reset.c [new file with mode: 0644]
arch/mips/momentum/ocelot_3/setup.c [new file with mode: 0644]
arch/mips/oprofile/Kconfig [new file with mode: 0644]
arch/mips/oprofile/Makefile [new file with mode: 0644]
arch/mips/oprofile/common.c [new file with mode: 0644]
arch/mips/oprofile/op_impl.h [new file with mode: 0644]
arch/mips/oprofile/op_model_rm9000.c [new file with mode: 0644]
arch/mips/pci/fixup-ocelot3.c [new file with mode: 0644]
arch/mips/pci/fixup-sb1250.c [new file with mode: 0644]
arch/mips/pci/fixup-vr4133.c [new file with mode: 0644]
arch/mips/sgi-ip27/ip27-dbgio.c [new file with mode: 0644]
arch/mips/sgi-ip32/ip32-memory.c [new file with mode: 0644]
arch/mips/vr41xx/nec-cmbvr4133/Makefile [new file with mode: 0644]
arch/mips/vr41xx/nec-cmbvr4133/init.c [new file with mode: 0644]
arch/mips/vr41xx/nec-cmbvr4133/irq.c [new file with mode: 0644]
arch/mips/vr41xx/nec-cmbvr4133/m1535plus.c [new file with mode: 0644]
arch/mips/vr41xx/nec-cmbvr4133/setup.c [new file with mode: 0644]
arch/parisc/install.sh [new file with mode: 0644]
arch/parisc/kernel/topology.c [new file with mode: 0644]
arch/parisc/lib/fixup.S [new file with mode: 0644]
arch/parisc/lib/iomap.c [new file with mode: 0644]
arch/parisc/lib/memcpy.c [new file with mode: 0644]
arch/ppc/boot/include/mpsc_defs.h [new file with mode: 0644]
arch/ppc/boot/simple/misc-chestnut.S [new file with mode: 0644]
arch/ppc/boot/simple/misc-cpci690.c [new file with mode: 0644]
arch/ppc/boot/simple/misc-katana.c [new file with mode: 0644]
arch/ppc/configs/chestnut_defconfig [new file with mode: 0644]
arch/ppc/configs/cpci690_defconfig [new file with mode: 0644]
arch/ppc/configs/katana_defconfig [new file with mode: 0644]
arch/ppc/configs/luan_defconfig [new file with mode: 0644]
arch/ppc/configs/mpc8540_ads_defconfig [new file with mode: 0644]
arch/ppc/configs/mpc8555_cds_defconfig [new file with mode: 0644]
arch/ppc/configs/mpc8560_ads_defconfig [new file with mode: 0644]
arch/ppc/configs/stx_gp3_defconfig [new file with mode: 0644]
arch/ppc/kernel/machine_kexec.c [new file with mode: 0644]
arch/ppc/kernel/perfmon.c [new file with mode: 0644]
arch/ppc/kernel/perfmon_fsl_booke.c [new file with mode: 0644]
arch/ppc/kernel/relocate_kernel.S [new file with mode: 0644]
arch/ppc/oprofile/common.c [new file with mode: 0644]
arch/ppc/oprofile/op_impl.h [new file with mode: 0644]
arch/ppc/oprofile/op_model_fsl_booke.c [new file with mode: 0644]
arch/ppc/platforms/4xx/ibm440sp.c [new file with mode: 0644]
arch/ppc/platforms/4xx/ibm440sp.h [new file with mode: 0644]
arch/ppc/platforms/4xx/luan.c [new file with mode: 0644]
arch/ppc/platforms/4xx/luan.h [new file with mode: 0644]
arch/ppc/platforms/4xx/virtex-ii_pro.c [new file with mode: 0644]
arch/ppc/platforms/4xx/virtex-ii_pro.h [new file with mode: 0644]
arch/ppc/platforms/4xx/xilinx_ml300.c [new file with mode: 0644]
arch/ppc/platforms/4xx/xilinx_ml300.h [new file with mode: 0644]
arch/ppc/platforms/4xx/xparameters/xparameters_ml300.h [new file with mode: 0644]
arch/ppc/platforms/85xx/mpc85xx_devices.c [new file with mode: 0644]
arch/ppc/platforms/85xx/mpc85xx_sys.c [new file with mode: 0644]
arch/ppc/platforms/85xx/stx_gp3.c [new file with mode: 0644]
arch/ppc/platforms/85xx/stx_gp3.h [new file with mode: 0644]
arch/ppc/platforms/chestnut.c [new file with mode: 0644]
arch/ppc/platforms/chestnut.h [new file with mode: 0644]
arch/ppc/platforms/cpci690.c [new file with mode: 0644]
arch/ppc/platforms/cpci690.h [new file with mode: 0644]
arch/ppc/platforms/katana.c [new file with mode: 0644]
arch/ppc/platforms/katana.h [new file with mode: 0644]
arch/ppc/platforms/pmac_cache.S [new file with mode: 0644]
arch/ppc/syslib/gen550.h [new file with mode: 0644]
arch/ppc/syslib/ibm440sp_common.c [new file with mode: 0644]
arch/ppc/syslib/ibm440sp_common.h [new file with mode: 0644]
arch/ppc/syslib/mv64x60_dbg.c [new file with mode: 0644]
arch/ppc/syslib/mv64x60_win.c [new file with mode: 0644]
arch/ppc/syslib/ppc403_pic.c [new file with mode: 0644]
arch/ppc/syslib/ppc_sys.c [new file with mode: 0644]
arch/ppc/syslib/xilinx_pic.c [new file with mode: 0644]
arch/ppc64/configs/maple_defconfig [new file with mode: 0644]
arch/ppc64/kernel/iSeries_smp.c [new file with mode: 0644]
arch/ppc64/kernel/kprobes.c [new file with mode: 0644]
arch/ppc64/kernel/maple_pci.c [new file with mode: 0644]
arch/ppc64/kernel/maple_setup.c [new file with mode: 0644]
arch/ppc64/kernel/maple_time.c [new file with mode: 0644]
arch/ppc64/kernel/mpic.c [new file with mode: 0644]
arch/ppc64/kernel/mpic.h [new file with mode: 0644]
arch/ppc64/kernel/pSeries_smp.c [new file with mode: 0644]
arch/ppc64/kernel/pci_direct_iommu.c [new file with mode: 0644]
arch/ppc64/lib/sstep.c [new file with mode: 0644]
arch/ppc64/xmon/setjmp.S [new file with mode: 0644]
arch/sh/boards/renesas/edosk7705/Makefile [new file with mode: 0644]
arch/sh/boards/renesas/edosk7705/io.c [new file with mode: 0644]
arch/sh/boards/renesas/edosk7705/setup.c [new file with mode: 0644]
arch/sh/boards/se/73180/Makefile [new file with mode: 0644]
arch/sh/boards/se/73180/io.c [new file with mode: 0644]
arch/sh/boards/se/73180/irq.c [new file with mode: 0644]
arch/sh/boards/se/73180/led.c [new file with mode: 0644]
arch/sh/boards/se/73180/setup.c [new file with mode: 0644]
arch/sh/boards/sh03/Makefile [new file with mode: 0644]
arch/sh/boards/sh03/led.c [new file with mode: 0644]
arch/sh/boards/sh03/rtc.c [new file with mode: 0644]
arch/sh/boards/sh03/setup.c [new file with mode: 0644]
arch/sh/boards/superh/microdev/Makefile [new file with mode: 0644]
arch/sh/boards/superh/microdev/io.c [new file with mode: 0644]
arch/sh/boards/superh/microdev/irq.c [new file with mode: 0644]
arch/sh/boards/superh/microdev/led.c [new file with mode: 0644]
arch/sh/boards/superh/microdev/setup.c [new file with mode: 0644]
arch/sh/configs/microdev_defconfig [new file with mode: 0644]
arch/sh/configs/se73180_defconfig [new file with mode: 0644]
arch/sh/configs/se7705_defconfig [new file with mode: 0644]
arch/sh/configs/sh03_defconfig [new file with mode: 0644]
arch/sh/drivers/pci/fixups-sh03.c [new file with mode: 0644]
arch/sh/drivers/pci/ops-sh03.c [new file with mode: 0644]
arch/sh/kernel/asm-offsets.c [new file with mode: 0644]
arch/sh/kernel/cpu/sh2/probe.c [new file with mode: 0644]
arch/sh/kernel/cpu/sh3/probe.c [new file with mode: 0644]
arch/sh/kernel/cpu/sh4/probe.c [new file with mode: 0644]
arch/sh/lib/memcpy-sh4.S [new file with mode: 0644]
arch/sh/mm/cache-sh7705.c [new file with mode: 0644]
arch/sh/mm/pg-sh7705.c [new file with mode: 0644]
arch/sh/oprofile/op_model_sh7750.c [new file with mode: 0644]
arch/sh/tools/gen-mach-types [new file with mode: 0644]
arch/sparc64/prom/cif.S [new file with mode: 0644]
arch/um/Kconfig_i386 [new file with mode: 0644]
arch/um/Kconfig_x86_64 [new file with mode: 0644]
arch/um/Makefile-x86_64 [new file with mode: 0644]
arch/um/drivers/stderr_console.c [new file with mode: 0644]
arch/um/include/elf_user.h [new file with mode: 0644]
arch/um/include/registers.h [new file with mode: 0644]
arch/um/include/sysdep-i386/signal.h [new file with mode: 0644]
arch/um/include/sysdep-x86_64/checksum.h [new file with mode: 0644]
arch/um/include/sysdep-x86_64/ptrace.h [new file with mode: 0644]
arch/um/include/sysdep-x86_64/ptrace_user.h [new file with mode: 0644]
arch/um/include/sysdep-x86_64/sigcontext.h [new file with mode: 0644]
arch/um/include/sysdep-x86_64/signal.h [new file with mode: 0644]
arch/um/include/sysdep-x86_64/syscalls.h [new file with mode: 0644]
arch/um/kernel/skas/include/mmu-skas.h [new file with mode: 0644]
arch/um/kernel/skas/include/mode-skas.h [new file with mode: 0644]
arch/um/kernel/skas/include/mode_kern-skas.h [new file with mode: 0644]
arch/um/kernel/skas/include/uaccess-skas.h [new file with mode: 0644]
arch/um/kernel/skas/util/mk_ptregs-i386.c [new file with mode: 0644]
arch/um/kernel/skas/util/mk_ptregs-x86_64.c [new file with mode: 0644]
arch/um/kernel/tt/include/mmu-tt.h [new file with mode: 0644]
arch/um/kernel/tt/include/mode-tt.h [new file with mode: 0644]
arch/um/kernel/tt/include/mode_kern-tt.h [new file with mode: 0644]
arch/um/kernel/tt/include/uaccess-tt.h [new file with mode: 0644]
arch/um/os-Linux/elf_aux.c [new file with mode: 0644]
arch/um/os-Linux/signal.c [new file with mode: 0644]
arch/um/os-Linux/sys-i386/Makefile [new file with mode: 0644]
arch/um/os-Linux/sys-i386/registers.c [new file with mode: 0644]
arch/um/os-Linux/sys-x86_64/Makefile [new file with mode: 0644]
arch/um/os-Linux/sys-x86_64/registers.c [new file with mode: 0644]
arch/um/sys-i386/delay.c [new file with mode: 0644]
arch/um/sys-i386/signal.c [new file with mode: 0644]
arch/um/sys-x86_64/Makefile [new file with mode: 0644]
arch/um/sys-x86_64/bugs.c [new file with mode: 0644]
arch/um/sys-x86_64/delay.c [new file with mode: 0644]
arch/um/sys-x86_64/fault.c [new file with mode: 0644]
arch/um/sys-x86_64/mem.c [new file with mode: 0644]
arch/um/sys-x86_64/ptrace.c [new file with mode: 0644]
arch/um/sys-x86_64/ptrace_user.c [new file with mode: 0644]
arch/um/sys-x86_64/sigcontext.c [new file with mode: 0644]
arch/um/sys-x86_64/signal.c [new file with mode: 0644]
arch/um/sys-x86_64/syscalls.c [new file with mode: 0644]
arch/um/sys-x86_64/sysrq.c [new file with mode: 0644]
arch/um/sys-x86_64/util/Makefile [new file with mode: 0644]
arch/um/sys-x86_64/util/mk_sc.c [new file with mode: 0644]
arch/um/sys-x86_64/util/mk_thread_kern.c [new file with mode: 0644]
arch/um/sys-x86_64/util/mk_thread_user.c [new file with mode: 0644]
arch/x86_64/kernel/genapic.c [new file with mode: 0644]
arch/x86_64/kernel/genapic_cluster.c [new file with mode: 0644]
arch/x86_64/kernel/genapic_flat.c [new file with mode: 0644]
arch/x86_64/kernel/kprobes.c [new file with mode: 0644]
arch/x86_64/kernel/machine_kexec.c [new file with mode: 0644]
arch/x86_64/kernel/mce_intel.c [new file with mode: 0644]
arch/x86_64/kernel/relocate_kernel.S [new file with mode: 0644]
arch/x86_64/mm/srat.c [new file with mode: 0644]
crypto/anubis.c [new file with mode: 0644]
drivers/acpi/container.c [new file with mode: 0644]
drivers/acpi/ibm_acpi.c [new file with mode: 0644]
drivers/acpi/processor_core.c [new file with mode: 0644]
drivers/acpi/processor_idle.c [new file with mode: 0644]
drivers/acpi/processor_perflib.c [new file with mode: 0644]
drivers/acpi/processor_thermal.c [new file with mode: 0644]
drivers/acpi/processor_throttling.c [new file with mode: 0644]
drivers/acpi/video.c [new file with mode: 0644]
drivers/base/attribute_container.c [new file with mode: 0644]
drivers/base/transport_class.c [new file with mode: 0644]
drivers/block/aoe/Makefile [new file with mode: 0644]
drivers/block/aoe/aoe.h [new file with mode: 0644]
drivers/block/aoe/aoeblk.c [new file with mode: 0644]
drivers/block/aoe/aoechr.c [new file with mode: 0644]
drivers/block/aoe/aoecmd.c [new file with mode: 0644]
drivers/block/aoe/aoedev.c [new file with mode: 0644]
drivers/block/aoe/aoemain.c [new file with mode: 0644]
drivers/block/aoe/aoenet.c [new file with mode: 0644]
drivers/block/pktcdvd.c [new file with mode: 0644]
drivers/bluetooth/bpa10x.c [new file with mode: 0644]
drivers/char/drm/ati_pcigart.c [new file with mode: 0644]
drivers/char/drm/drm_agpsupport.c [new file with mode: 0644]
drivers/char/drm/drm_auth.c [new file with mode: 0644]
drivers/char/drm/drm_bufs.c [new file with mode: 0644]
drivers/char/drm/drm_context.c [new file with mode: 0644]
drivers/char/drm/drm_dma.c [new file with mode: 0644]
drivers/char/drm/drm_drawable.c [new file with mode: 0644]
drivers/char/drm/drm_drv.c [new file with mode: 0644]
drivers/char/drm/drm_fops.c [new file with mode: 0644]
drivers/char/drm/drm_init.c [new file with mode: 0644]
drivers/char/drm/drm_ioctl.c [new file with mode: 0644]
drivers/char/drm/drm_irq.c [new file with mode: 0644]
drivers/char/drm/drm_lock.c [new file with mode: 0644]
drivers/char/drm/drm_memory.c [new file with mode: 0644]
drivers/char/drm/drm_pci.c [new file with mode: 0644]
drivers/char/drm/drm_proc.c [new file with mode: 0644]
drivers/char/drm/drm_scatter.c [new file with mode: 0644]
drivers/char/drm/drm_stub.c [new file with mode: 0644]
drivers/char/drm/drm_sysfs.c [new file with mode: 0644]
drivers/char/drm/drm_vm.c [new file with mode: 0644]
drivers/char/drm/tdfx_drv.h [new file with mode: 0644]
drivers/char/ds1302.c [new file with mode: 0644]
drivers/char/mxser.h [new file with mode: 0644]
drivers/char/s3c2410-rtc.c [new file with mode: 0644]
drivers/char/watchdog/s3c2410_wdt.c [new file with mode: 0644]
drivers/cpufreq/cpufreq_stats.c [new file with mode: 0644]
drivers/crypto/Kconfig [new file with mode: 0644]
drivers/crypto/Makefile [new file with mode: 0644]
drivers/crypto/padlock-aes.c [new file with mode: 0644]
drivers/crypto/padlock-generic.c [new file with mode: 0644]
drivers/crypto/padlock.h [new file with mode: 0644]
drivers/dio/dio-driver.c [new file with mode: 0644]
drivers/dio/dio-sysfs.c [new file with mode: 0644]
drivers/i2c/algos/i2c-algo-sgi.c [new file with mode: 0644]
drivers/i2c/algos/i2c-algo-sibyte.c [new file with mode: 0644]
drivers/i2c/busses/i2c-amd756-s4882.c [new file with mode: 0644]
drivers/i2c/busses/i2c-au1550.c [new file with mode: 0644]
drivers/i2c/busses/i2c-au1550.h [new file with mode: 0644]
drivers/i2c/busses/i2c-s3c2410.c [new file with mode: 0644]
drivers/i2c/busses/i2c-sibyte.c [new file with mode: 0644]
drivers/i2c/busses/i2c-stub.c [new file with mode: 0644]
drivers/i2c/chips/adm1026.c [new file with mode: 0644]
drivers/i2c/chips/lm63.c [new file with mode: 0644]
drivers/i2c/chips/lm87.c [new file with mode: 0644]
drivers/i2c/chips/pc87360.c [new file with mode: 0644]
drivers/i2c/chips/smsc47b397.c [new file with mode: 0644]
drivers/ide/cris/Makefile [new file with mode: 0644]
drivers/ide/cris/ide-v10.c [new file with mode: 0644]
drivers/ide/pci/it821x.c [new file with mode: 0644]
drivers/infiniband/Kconfig [new file with mode: 0644]
drivers/infiniband/Makefile [new file with mode: 0644]
drivers/infiniband/core/Makefile [new file with mode: 0644]
drivers/infiniband/core/agent.c [new file with mode: 0644]
drivers/infiniband/core/agent.h [new file with mode: 0644]
drivers/infiniband/core/agent_priv.h [new file with mode: 0644]
drivers/infiniband/core/cache.c [new file with mode: 0644]
drivers/infiniband/core/core_priv.h [new file with mode: 0644]
drivers/infiniband/core/device.c [new file with mode: 0644]
drivers/infiniband/core/fmr_pool.c [new file with mode: 0644]
drivers/infiniband/core/mad.c [new file with mode: 0644]
drivers/infiniband/core/mad_priv.h [new file with mode: 0644]
drivers/infiniband/core/packer.c [new file with mode: 0644]
drivers/infiniband/core/sa_query.c [new file with mode: 0644]
drivers/infiniband/core/smi.c [new file with mode: 0644]
drivers/infiniband/core/smi.h [new file with mode: 0644]
drivers/infiniband/core/sysfs.c [new file with mode: 0644]
drivers/infiniband/core/ud_header.c [new file with mode: 0644]
drivers/infiniband/core/user_mad.c [new file with mode: 0644]
drivers/infiniband/core/verbs.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/Kconfig [new file with mode: 0644]
drivers/infiniband/hw/mthca/Makefile [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_allocator.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_av.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_cmd.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_cmd.h [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_config_reg.h [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_cq.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_dev.h [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_doorbell.h [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_eq.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_mad.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_main.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_mcg.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_memfree.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_memfree.h [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_mr.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_pd.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_profile.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_profile.h [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_provider.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_provider.h [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_qp.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_reset.c [new file with mode: 0644]
drivers/infiniband/include/ib_cache.h [new file with mode: 0644]
drivers/infiniband/include/ib_fmr_pool.h [new file with mode: 0644]
drivers/infiniband/include/ib_mad.h [new file with mode: 0644]
drivers/infiniband/include/ib_pack.h [new file with mode: 0644]
drivers/infiniband/include/ib_sa.h [new file with mode: 0644]
drivers/infiniband/include/ib_smi.h [new file with mode: 0644]
drivers/infiniband/include/ib_user_mad.h [new file with mode: 0644]
drivers/infiniband/include/ib_verbs.h [new file with mode: 0644]
drivers/infiniband/ulp/ipoib/Kconfig [new file with mode: 0644]
drivers/infiniband/ulp/ipoib/Makefile [new file with mode: 0644]
drivers/infiniband/ulp/ipoib/ipoib.h [new file with mode: 0644]
drivers/infiniband/ulp/ipoib/ipoib_fs.c [new file with mode: 0644]
drivers/infiniband/ulp/ipoib/ipoib_ib.c [new file with mode: 0644]
drivers/infiniband/ulp/ipoib/ipoib_main.c [new file with mode: 0644]
drivers/infiniband/ulp/ipoib/ipoib_multicast.c [new file with mode: 0644]
drivers/infiniband/ulp/ipoib/ipoib_verbs.c [new file with mode: 0644]
drivers/infiniband/ulp/ipoib/ipoib_vlan.c [new file with mode: 0644]
drivers/input/mouse/alps.c [new file with mode: 0644]
drivers/input/mouse/alps.h [new file with mode: 0644]
drivers/input/serio/i8042-x86ia64io.h [new file with mode: 0644]
drivers/input/serio/libps2.c [new file with mode: 0644]
drivers/md/faulty.c [new file with mode: 0644]
drivers/md/raid6altivec.uc [new file with mode: 0644]
drivers/media/dvb/b2c2/b2c2-common.c [new file with mode: 0644]
drivers/media/dvb/b2c2/b2c2-usb-core.c [new file with mode: 0644]
drivers/media/dvb/bt8xx/dst.c [new file with mode: 0644]
drivers/media/dvb/bt8xx/dst.h [new file with mode: 0644]
drivers/media/dvb/bt8xx/dst_priv.h [new file with mode: 0644]
drivers/media/dvb/cinergyT2/Kconfig [new file with mode: 0644]
drivers/media/dvb/cinergyT2/Makefile [new file with mode: 0644]
drivers/media/dvb/cinergyT2/cinergyT2.c [new file with mode: 0644]
drivers/media/dvb/dibusb/Kconfig [new file with mode: 0644]
drivers/media/dvb/dibusb/Makefile [new file with mode: 0644]
drivers/media/dvb/dibusb/dvb-dibusb-core.c [new file with mode: 0644]
drivers/media/dvb/dibusb/dvb-dibusb-dvb.c [new file with mode: 0644]
drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c [new file with mode: 0644]
drivers/media/dvb/dibusb/dvb-dibusb-firmware.c [new file with mode: 0644]
drivers/media/dvb/dibusb/dvb-dibusb-pid.c [new file with mode: 0644]
drivers/media/dvb/dibusb/dvb-dibusb-remote.c [new file with mode: 0644]
drivers/media/dvb/dibusb/dvb-dibusb-usb.c [new file with mode: 0644]
drivers/media/dvb/dibusb/dvb-dibusb.h [new file with mode: 0644]
drivers/media/dvb/frontends/at76c651.h [new file with mode: 0644]
drivers/media/dvb/frontends/cx22700.c [new file with mode: 0644]
drivers/media/dvb/frontends/cx22700.h [new file with mode: 0644]
drivers/media/dvb/frontends/cx22702.c [new file with mode: 0644]
drivers/media/dvb/frontends/cx22702.h [new file with mode: 0644]
drivers/media/dvb/frontends/cx24110.h [new file with mode: 0644]
drivers/media/dvb/frontends/dib3000-common.c [new file with mode: 0644]
drivers/media/dvb/frontends/dib3000-common.h [new file with mode: 0644]
drivers/media/dvb/frontends/dib3000.h [new file with mode: 0644]
drivers/media/dvb/frontends/dib3000mb.c [new file with mode: 0644]
drivers/media/dvb/frontends/dib3000mb_priv.h [new file with mode: 0644]
drivers/media/dvb/frontends/dib3000mc.c [new file with mode: 0644]
drivers/media/dvb/frontends/dib3000mc_priv.h [new file with mode: 0644]
drivers/media/dvb/frontends/dvb_dummy_fe.h [new file with mode: 0644]
drivers/media/dvb/frontends/l64781.c [new file with mode: 0644]
drivers/media/dvb/frontends/l64781.h [new file with mode: 0644]
drivers/media/dvb/frontends/mt312_priv.h [new file with mode: 0644]
drivers/media/dvb/frontends/mt352.c [new file with mode: 0644]
drivers/media/dvb/frontends/mt352.h [new file with mode: 0644]
drivers/media/dvb/frontends/mt352_priv.h [new file with mode: 0644]
drivers/media/dvb/frontends/nxt2002.c [new file with mode: 0644]
drivers/media/dvb/frontends/nxt2002.h [new file with mode: 0644]
drivers/media/dvb/frontends/nxt6000_priv.h [new file with mode: 0644]
drivers/media/dvb/frontends/sp8870.c [new file with mode: 0644]
drivers/media/dvb/frontends/sp8870.h [new file with mode: 0644]
drivers/media/dvb/frontends/sp887x.h [new file with mode: 0644]
drivers/media/dvb/frontends/stv0297.c [new file with mode: 0644]
drivers/media/dvb/frontends/stv0297.h [new file with mode: 0644]
drivers/media/dvb/frontends/stv0299.h [new file with mode: 0644]
drivers/media/dvb/frontends/tda10021.c [new file with mode: 0644]
drivers/media/dvb/frontends/tda10021.h [new file with mode: 0644]
drivers/media/dvb/frontends/tda1004x.h [new file with mode: 0644]
drivers/media/dvb/frontends/tda8083.c [new file with mode: 0644]
drivers/media/dvb/frontends/tda8083.h [new file with mode: 0644]
drivers/media/dvb/frontends/tda80xx.c [new file with mode: 0644]
drivers/media/dvb/frontends/tda80xx.h [new file with mode: 0644]
drivers/media/dvb/frontends/ves1820.h [new file with mode: 0644]
drivers/media/dvb/frontends/ves1x93.h [new file with mode: 0644]
drivers/media/dvb/ttusb-dec/ttusbdecfe.c [new file with mode: 0644]
drivers/media/dvb/ttusb-dec/ttusbdecfe.h [new file with mode: 0644]
drivers/media/video/arv.c [new file with mode: 0644]
drivers/media/video/cx88/cx88-blackbird.c [new file with mode: 0644]
drivers/media/video/cx88/cx88-dvb.c [new file with mode: 0644]
drivers/media/video/cx88/cx88-mpeg.c [new file with mode: 0644]
drivers/media/video/saa7134/saa7134-dvb.c [new file with mode: 0644]
drivers/media/video/saa7134/saa7134-empress.c [new file with mode: 0644]
drivers/media/video/tveeprom.c [new file with mode: 0644]
drivers/media/video/video-buf-dvb.c [new file with mode: 0644]
drivers/mmc/wbsd.c [new file with mode: 0644]
drivers/mmc/wbsd.h [new file with mode: 0644]
drivers/mtd/chips/fwh_lock.h [new file with mode: 0644]
drivers/mtd/devices/block2mtd.c [new file with mode: 0644]
drivers/mtd/maps/bast-flash.c [new file with mode: 0644]
drivers/mtd/maps/chestnut.c [new file with mode: 0644]
drivers/mtd/maps/ipaq-flash.c [new file with mode: 0644]
drivers/mtd/maps/ocotea.c [new file with mode: 0644]
drivers/mtd/maps/sharpsl-flash.c [new file with mode: 0644]
drivers/mtd/maps/ts5500_flash.c [new file with mode: 0644]
drivers/mtd/maps/walnut.c [new file with mode: 0644]
drivers/mtd/nand/h1910.c [new file with mode: 0644]
drivers/mtd/nand/nandsim.c [new file with mode: 0644]
drivers/mtd/nand/rtc_from4.c [new file with mode: 0644]
drivers/mtd/nand/s3c2410.c [new file with mode: 0644]
drivers/mtd/nand/sharpsl.c [new file with mode: 0755]
drivers/net/arcnet/capmode.c [new file with mode: 0644]
drivers/net/cris/Makefile [new file with mode: 0644]
drivers/net/cris/eth_v10.c [new file with mode: 0644]
drivers/net/sk98lin/skethtool.c [new file with mode: 0644]
drivers/pci/pci-acpi.c [new file with mode: 0644]
drivers/pci/pcie/Kconfig [new file with mode: 0644]
drivers/pci/pcie/Makefile [new file with mode: 0644]
drivers/pci/pcie/portdrv.h [new file with mode: 0644]
drivers/pci/pcie/portdrv_bus.c [new file with mode: 0644]
drivers/pci/pcie/portdrv_core.c [new file with mode: 0644]
drivers/pci/pcie/portdrv_pci.c [new file with mode: 0644]
drivers/pci/rom.c [new file with mode: 0644]
drivers/pcmcia/au1000_db1x00.c [new file with mode: 0644]
drivers/pcmcia/au1000_generic.h [new file with mode: 0644]
drivers/pcmcia/au1000_xxs1500.c [new file with mode: 0644]
drivers/pcmcia/m32r_cfc.c [new file with mode: 0644]
drivers/pcmcia/m32r_cfc.h [new file with mode: 0644]
drivers/pcmcia/m32r_pcc.c [new file with mode: 0644]
drivers/pcmcia/m32r_pcc.h [new file with mode: 0644]
drivers/pcmcia/pcmcia_compat.c [new file with mode: 0644]
drivers/pcmcia/pxa2xx_sharpsl.c [new file with mode: 0644]
drivers/pcmcia/rsrc_nonstatic.c [new file with mode: 0644]
drivers/pcmcia/vrc4171_card.c [new file with mode: 0644]
drivers/pcmcia/vrc4173_cardu.c [new file with mode: 0644]
drivers/pcmcia/vrc4173_cardu.h [new file with mode: 0644]
drivers/pnp/pnpacpi/Kconfig [new file with mode: 0644]
drivers/pnp/pnpacpi/Makefile [new file with mode: 0644]
drivers/pnp/pnpacpi/core.c [new file with mode: 0644]
drivers/pnp/pnpacpi/pnpacpi.h [new file with mode: 0644]
drivers/pnp/pnpacpi/rsparser.c [new file with mode: 0644]
drivers/s390/char/monreader.c [new file with mode: 0644]
drivers/s390/char/sclp_quiesce.c [new file with mode: 0644]
drivers/s390/char/vmlogrdr.c [new file with mode: 0644]
drivers/s390/char/vmwatchdog.c [new file with mode: 0644]
drivers/scsi/a100u2w.c [new file with mode: 0644]
drivers/scsi/a100u2w.h [new file with mode: 0644]
drivers/scsi/ahci.c [new file with mode: 0644]
drivers/scsi/aic7xxx/aic79xx_pci.h [new file with mode: 0644]
drivers/scsi/aic7xxx/aic7xxx_pci.h [new file with mode: 0644]
drivers/scsi/gdth_kcompat.h [new file with mode: 0644]
drivers/scsi/initio.c [new file with mode: 0644]
drivers/scsi/initio.h [new file with mode: 0644]
drivers/scsi/lpfc/Makefile [new file with mode: 0644]
drivers/scsi/lpfc/lpfc.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_compat.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_crtn.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_ct.c [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_disc.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_els.c [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_hbadisc.c [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_hw.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_init.c [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_logmsg.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_mbox.c [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_mem.c [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_nportdisc.c [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_scsi.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_sli.c [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_sli.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_version.h [new file with mode: 0644]
drivers/scsi/ql1040_fw.h [new file with mode: 0644]
drivers/scsi/sata_qstor.c [new file with mode: 0644]
drivers/scsi/sata_uli.c [new file with mode: 0644]
drivers/scsi/scsi_transport_iscsi.c [new file with mode: 0644]
drivers/serial/8250_early.c [new file with mode: 0644]
drivers/serial/8250_hp300.c [new file with mode: 0644]
drivers/serial/crisv10.c [new file with mode: 0644]
drivers/serial/crisv10.h [new file with mode: 0644]
drivers/serial/imx.c [new file with mode: 0644]
drivers/serial/m32r_sio.c [new file with mode: 0644]
drivers/serial/m32r_sio.h [new file with mode: 0644]
drivers/serial/m32r_sio_reg.h [new file with mode: 0644]
drivers/serial/mpsc.c [new file with mode: 0644]
drivers/serial/mpsc.h [new file with mode: 0644]
drivers/serial/serial_txx9.c [new file with mode: 0644]
drivers/usb/atm/Kconfig [new file with mode: 0644]
drivers/usb/atm/Makefile [new file with mode: 0644]
drivers/usb/atm/speedtch.c [new file with mode: 0644]
drivers/usb/atm/usb_atm.c [new file with mode: 0644]
drivers/usb/atm/usb_atm.h [new file with mode: 0644]
drivers/usb/host/hc_crisv10.c [new file with mode: 0644]
drivers/usb/host/hc_crisv10.h [new file with mode: 0644]
drivers/usb/host/ohci-au1xxx.c [new file with mode: 0644]
drivers/usb/host/ohci-pxa27x.c [new file with mode: 0644]
drivers/usb/host/sl811-hcd.c [new file with mode: 0644]
drivers/usb/host/sl811.h [new file with mode: 0644]
drivers/usb/media/pwc/Makefile [new file with mode: 0644]
drivers/usb/media/pwc/pwc-ctrl.c [new file with mode: 0644]
drivers/usb/media/pwc/pwc-if.c [new file with mode: 0644]
drivers/usb/media/pwc/pwc-ioctl.h [new file with mode: 0644]
drivers/usb/media/pwc/pwc-kiara.c [new file with mode: 0644]
drivers/usb/media/pwc/pwc-timon.c [new file with mode: 0644]
drivers/usb/media/pwc/pwc-uncompress.c [new file with mode: 0644]
drivers/usb/media/pwc/pwc.h [new file with mode: 0644]
drivers/usb/media/sn9c102_hv7131d.c [new file with mode: 0644]
drivers/usb/media/sn9c102_mi0343.c [new file with mode: 0644]
drivers/usb/misc/idmouse.c [new file with mode: 0644]
drivers/usb/misc/phidgetkit.c [new file with mode: 0644]
drivers/usb/serial/cypress_m8.c [new file with mode: 0644]
drivers/usb/serial/cypress_m8.h [new file with mode: 0644]
drivers/usb/serial/garmin_gps.c [new file with mode: 0644]
drivers/usb/serial/ipw.c [new file with mode: 0644]
drivers/usb/serial/ti_fw_3410.h [new file with mode: 0644]
drivers/usb/serial/ti_fw_5052.h [new file with mode: 0644]
drivers/usb/serial/ti_usb_3410_5052.c [new file with mode: 0644]
drivers/usb/serial/ti_usb_3410_5052.h [new file with mode: 0644]
drivers/video/au1100fb.c [new file with mode: 0644]
drivers/video/au1100fb.h [new file with mode: 0644]
drivers/video/backlight/Kconfig [new file with mode: 0644]
drivers/video/backlight/Makefile [new file with mode: 0644]
drivers/video/backlight/backlight.c [new file with mode: 0644]
drivers/video/backlight/corgi_bl.c [new file with mode: 0644]
drivers/video/backlight/lcd.c [new file with mode: 0644]
drivers/video/bt431.h [new file with mode: 0644]
drivers/video/bt455.h [new file with mode: 0644]
drivers/video/console/bitblit.c [new file with mode: 0644]
drivers/video/console/tileblit.c [new file with mode: 0644]
drivers/video/intelfb/Makefile [new file with mode: 0644]
drivers/video/intelfb/intelfb.h [new file with mode: 0644]
drivers/video/intelfb/intelfbdrv.c [new file with mode: 0644]
drivers/video/intelfb/intelfbdrv.h [new file with mode: 0644]
drivers/video/intelfb/intelfbhw.c [new file with mode: 0644]
drivers/video/intelfb/intelfbhw.h [new file with mode: 0644]
drivers/video/pmag-aa-fb.c [new file with mode: 0644]
drivers/video/savage/Makefile [new file with mode: 0644]
drivers/video/savage/savagefb-i2c.c [new file with mode: 0644]
drivers/video/savage/savagefb.c [new file with mode: 0644]
drivers/video/savage/savagefb.h [new file with mode: 0644]
drivers/video/savage/savagefb_accel.c [new file with mode: 0644]
drivers/video/w100fb.c [new file with mode: 0644]
drivers/video/w100fb.h [new file with mode: 0644]
fs/affs/affs.h [new file with mode: 0644]
fs/binfmt_elf_fdpic.c [new file with mode: 0644]
fs/cifs/readdir.c [new file with mode: 0644]
fs/debugfs/Makefile [new file with mode: 0644]
fs/debugfs/file.c [new file with mode: 0644]
fs/debugfs/inode.c [new file with mode: 0644]
fs/hfs/attr.c [new file with mode: 0644]
fs/nfsd/nfs4callback.c [new file with mode: 0644]
fs/ntfs/aops.h [new file with mode: 0644]
fs/ntfs/runlist.c [new file with mode: 0644]
fs/ntfs/runlist.h [new file with mode: 0644]
fs/proc/internal.h [new file with mode: 0644]
fs/proc/mmu.c [new file with mode: 0644]
fs/proc/nommu.c [new file with mode: 0644]
fs/proc/vmcore.c [new file with mode: 0644]
fs/xfs/Kconfig [new file with mode: 0644]
fs/xfs/linux-2.6/xfs_export.c [new file with mode: 0644]
include/acpi/container.h [new file with mode: 0644]
include/asm-alpha/cputime.h [new file with mode: 0644]
include/asm-arm/arch-cl7500/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-cl7500/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-clps711x/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-clps711x/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-ebsa110/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-ebsa110/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-ebsa285/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-ebsa285/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-epxa10db/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-epxa10db/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-h720x/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-h720x/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-imx/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-imx/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-integrator/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-integrator/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-iop3xx/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-iop3xx/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-iop3xx/iq80332.h [new file with mode: 0644]
include/asm-arm/arch-ixp2000/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-ixp2000/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-ixp4xx/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-ixp4xx/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-ixp4xx/gtwx5715.h [new file with mode: 0644]
include/asm-arm/arch-l7200/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-l7200/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-lh7a40x/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-lh7a40x/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-omap/cpu.h [new file with mode: 0644]
include/asm-arm/arch-omap/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-omap/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-omap/omap16xx.h [new file with mode: 0644]
include/asm-arm/arch-omap/tc.h [new file with mode: 0644]
include/asm-arm/arch-pxa/audio.h [new file with mode: 0644]
include/asm-arm/arch-pxa/corgi.h [new file with mode: 0644]
include/asm-arm/arch-pxa/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-pxa/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-pxa/ssp.h [new file with mode: 0644]
include/asm-arm/arch-rpc/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-rpc/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-s3c2410/bast-pmu.h [new file with mode: 0644]
include/asm-arm/arch-s3c2410/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-s3c2410/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-s3c2410/idle.h [new file with mode: 0644]
include/asm-arm/arch-s3c2410/iic.h [new file with mode: 0644]
include/asm-arm/arch-sa1100/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-sa1100/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-shark/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-shark/entry-macro.S [new file with mode: 0644]
include/asm-arm/arch-versatile/debug-macro.S [new file with mode: 0644]
include/asm-arm/arch-versatile/entry-macro.S [new file with mode: 0644]
include/asm-arm/cpu.h [new file with mode: 0644]
include/asm-arm/cputime.h [new file with mode: 0644]
include/asm-arm/hardware/entry-macro-iomd.S [new file with mode: 0644]
include/asm-arm/hardware/icst307.h [new file with mode: 0644]
include/asm-arm/hardware/scoop.h [new file with mode: 0644]
include/asm-arm/mach/irda.h [new file with mode: 0644]
include/asm-arm/rtc.h [new file with mode: 0644]
include/asm-arm26/cputime.h [new file with mode: 0644]
include/asm-arm26/dma-mapping.h [new file with mode: 0644]
include/asm-cris/cputime.h [new file with mode: 0644]
include/asm-frv/a.out.h [new file with mode: 0644]
include/asm-frv/atomic.h [new file with mode: 0644]
include/asm-frv/ax88796.h [new file with mode: 0644]
include/asm-frv/bitops.h [new file with mode: 0644]
include/asm-frv/bug.h [new file with mode: 0644]
include/asm-frv/bugs.h [new file with mode: 0644]
include/asm-frv/busctl-regs.h [new file with mode: 0644]
include/asm-frv/byteorder.h [new file with mode: 0644]
include/asm-frv/cache.h [new file with mode: 0644]
include/asm-frv/cacheflush.h [new file with mode: 0644]
include/asm-frv/checksum.h [new file with mode: 0644]
include/asm-frv/cpu-irqs.h [new file with mode: 0644]
include/asm-frv/cpumask.h [new file with mode: 0644]
include/asm-frv/cputime.h [new file with mode: 0644]
include/asm-frv/current.h [new file with mode: 0644]
include/asm-frv/delay.h [new file with mode: 0644]
include/asm-frv/div64.h [new file with mode: 0644]
include/asm-frv/dm9000.h [new file with mode: 0644]
include/asm-frv/dma-mapping.h [new file with mode: 0644]
include/asm-frv/dma.h [new file with mode: 0644]
include/asm-frv/elf.h [new file with mode: 0644]
include/asm-frv/errno.h [new file with mode: 0644]
include/asm-frv/fcntl.h [new file with mode: 0644]
include/asm-frv/fpu.h [new file with mode: 0644]
include/asm-frv/gdb-stub.h [new file with mode: 0644]
include/asm-frv/gpio-regs.h [new file with mode: 0644]
include/asm-frv/hardirq.h [new file with mode: 0644]
include/asm-frv/highmem.h [new file with mode: 0644]
include/asm-frv/hw_irq.h [new file with mode: 0644]
include/asm-frv/ide.h [new file with mode: 0644]
include/asm-frv/init.h [new file with mode: 0644]
include/asm-frv/io.h [new file with mode: 0644]
include/asm-frv/ioctl.h [new file with mode: 0644]
include/asm-frv/ioctls.h [new file with mode: 0644]
include/asm-frv/ipc.h [new file with mode: 0644]
include/asm-frv/ipcbuf.h [new file with mode: 0644]
include/asm-frv/irc-regs.h [new file with mode: 0644]
include/asm-frv/irq-routing.h [new file with mode: 0644]
include/asm-frv/irq.h [new file with mode: 0644]
include/asm-frv/kmap_types.h [new file with mode: 0644]
include/asm-frv/linkage.h [new file with mode: 0644]
include/asm-frv/local.h [new file with mode: 0644]
include/asm-frv/math-emu.h [new file with mode: 0644]
include/asm-frv/mb-regs.h [new file with mode: 0644]
include/asm-frv/mb86943a.h [new file with mode: 0644]
include/asm-frv/mb93091-fpga-irqs.h [new file with mode: 0644]
include/asm-frv/mb93093-fpga-irqs.h [new file with mode: 0644]
include/asm-frv/mb93493-irqs.h [new file with mode: 0644]
include/asm-frv/mb93493-regs.h [new file with mode: 0644]
include/asm-frv/mem-layout.h [new file with mode: 0644]
include/asm-frv/mman.h [new file with mode: 0644]
include/asm-frv/mmu.h [new file with mode: 0644]
include/asm-frv/mmu_context.h [new file with mode: 0644]
include/asm-frv/module.h [new file with mode: 0644]
include/asm-frv/msgbuf.h [new file with mode: 0644]
include/asm-frv/namei.h [new file with mode: 0644]
include/asm-frv/page.h [new file with mode: 0644]
include/asm-frv/param.h [new file with mode: 0644]
include/asm-frv/pci.h [new file with mode: 0644]
include/asm-frv/percpu.h [new file with mode: 0644]
include/asm-frv/pgalloc.h [new file with mode: 0644]
include/asm-frv/pgtable.h [new file with mode: 0644]
include/asm-frv/poll.h [new file with mode: 0644]
include/asm-frv/posix_types.h [new file with mode: 0644]
include/asm-frv/processor.h [new file with mode: 0644]
include/asm-frv/ptrace.h [new file with mode: 0644]
include/asm-frv/registers.h [new file with mode: 0644]
include/asm-frv/resource.h [new file with mode: 0644]
include/asm-frv/scatterlist.h [new file with mode: 0644]
include/asm-frv/sections.h [new file with mode: 0644]
include/asm-frv/segment.h [new file with mode: 0644]
include/asm-frv/semaphore.h [new file with mode: 0644]
include/asm-frv/sembuf.h [new file with mode: 0644]
include/asm-frv/serial-regs.h [new file with mode: 0644]
include/asm-frv/serial.h [new file with mode: 0644]
include/asm-frv/setup.h [new file with mode: 0644]
include/asm-frv/shmbuf.h [new file with mode: 0644]
include/asm-frv/shmparam.h [new file with mode: 0644]
include/asm-frv/sigcontext.h [new file with mode: 0644]
include/asm-frv/siginfo.h [new file with mode: 0644]
include/asm-frv/signal.h [new file with mode: 0644]
include/asm-frv/smp.h [new file with mode: 0644]
include/asm-frv/socket.h [new file with mode: 0644]
include/asm-frv/sockios.h [new file with mode: 0644]
include/asm-frv/spinlock.h [new file with mode: 0644]
include/asm-frv/spr-regs.h [new file with mode: 0644]
include/asm-frv/stat.h [new file with mode: 0644]
include/asm-frv/statfs.h [new file with mode: 0644]
include/asm-frv/string.h [new file with mode: 0644]
include/asm-frv/suspend.h [new file with mode: 0644]
include/asm-frv/system.h [new file with mode: 0644]
include/asm-frv/termbits.h [new file with mode: 0644]
include/asm-frv/termios.h [new file with mode: 0644]
include/asm-frv/thread_info.h [new file with mode: 0644]
include/asm-frv/timer-regs.h [new file with mode: 0644]
include/asm-frv/timex.h [new file with mode: 0644]
include/asm-frv/tlb.h [new file with mode: 0644]
include/asm-frv/tlbflush.h [new file with mode: 0644]
include/asm-frv/topology.h [new file with mode: 0644]
include/asm-frv/types.h [new file with mode: 0644]
include/asm-frv/uaccess.h [new file with mode: 0644]
include/asm-frv/ucontext.h [new file with mode: 0644]
include/asm-frv/unaligned.h [new file with mode: 0644]
include/asm-frv/unistd.h [new file with mode: 0644]
include/asm-frv/user.h [new file with mode: 0644]
include/asm-frv/virtconvert.h [new file with mode: 0644]
include/asm-generic/4level-fixup.h [new file with mode: 0644]
include/asm-generic/cputime.h [new file with mode: 0644]
include/asm-generic/pgtable-nopmd.h [new file with mode: 0644]
include/asm-generic/pgtable-nopud.h [new file with mode: 0644]
include/asm-generic/resource.h [new file with mode: 0644]
include/asm-generic/termios.h [new file with mode: 0644]
include/asm-h8300/cputime.h [new file with mode: 0644]
include/asm-i386/cputime.h [new file with mode: 0644]
include/asm-i386/pci-direct.h [new file with mode: 0644]
include/asm-ia64/cputime.h [new file with mode: 0644]
include/asm-ia64/machvec_hpzx1_swiotlb.h [new file with mode: 0644]
include/asm-ia64/sn/l1.h [new file with mode: 0644]
include/asm-ia64/sn/shub_mmr.h [new file with mode: 0644]
include/asm-ia64/sn/shubio.h [new file with mode: 0644]
include/asm-m32r/cputime.h [new file with mode: 0644]
include/asm-m68k/cputime.h [new file with mode: 0644]
include/asm-m68k/hp300hw.h [new file with mode: 0644]
include/asm-m68knommu/cputime.h [new file with mode: 0644]
include/asm-m68knommu/m527xsim.h [new file with mode: 0644]
include/asm-m68knommu/m528xsim.h [new file with mode: 0644]
include/asm-m68knommu/mcfcache.h [new file with mode: 0644]
include/asm-mips/compiler.h [new file with mode: 0644]
include/asm-mips/cpu-info.h [new file with mode: 0644]
include/asm-mips/cputime.h [new file with mode: 0644]
include/asm-mips/dec/serial.h [new file with mode: 0644]
include/asm-mips/interrupt.h [new file with mode: 0644]
include/asm-mips/m48t37.h [new file with mode: 0644]
include/asm-mips/mach-ip22/spaces.h [new file with mode: 0644]
include/asm-mips/mach-ip32/cpu-feature-overrides.h [new file with mode: 0644]
include/asm-mips/mach-ip32/spaces.h [new file with mode: 0644]
include/asm-mips/mach-mips/cpu-feature-overrides.h [new file with mode: 0644]
include/asm-mips/mach-ocelot3/cpu-feature-overrides.h [new file with mode: 0644]
include/asm-mips/mach-sibyte/cpu-feature-overrides.h [new file with mode: 0644]
include/asm-mips/msc01_ic.h [new file with mode: 0644]
include/asm-mips/reg.h [new file with mode: 0644]
include/asm-mips/tx4927/smsc_fdc37m81x.h [new file with mode: 0644]
include/asm-mips/vr41xx/cmbvr4133.h [new file with mode: 0644]
include/asm-parisc/cputime.h [new file with mode: 0644]
include/asm-ppc/cputime.h [new file with mode: 0644]
include/asm-ppc/perfmon.h [new file with mode: 0644]
include/asm-ppc/ppc_sys.h [new file with mode: 0644]
include/asm-ppc/xparameters.h [new file with mode: 0644]
include/asm-ppc64/cputime.h [new file with mode: 0644]
include/asm-ppc64/kdebug.h [new file with mode: 0644]
include/asm-ppc64/kprobes.h [new file with mode: 0644]
include/asm-ppc64/lppaca.h [new file with mode: 0644]
include/asm-ppc64/sstep.h [new file with mode: 0644]
include/asm-s390/cputime.h [new file with mode: 0644]
include/asm-sh/cputime.h [new file with mode: 0644]
include/asm-sh/edosk7705/io.h [new file with mode: 0644]
include/asm-sh/irq-sh73180.h [new file with mode: 0644]
include/asm-sh/microdev/io.h [new file with mode: 0644]
include/asm-sh/microdev/irq.h [new file with mode: 0644]
include/asm-sh/se73180/io.h [new file with mode: 0644]
include/asm-sh/se73180/se73180.h [new file with mode: 0644]
include/asm-sh/sh03/io.h [new file with mode: 0644]
include/asm-sh/sh03/sh03.h [new file with mode: 0644]
include/asm-sh64/cputime.h [new file with mode: 0644]
include/asm-sparc/cputime.h [new file with mode: 0644]
include/asm-sparc64/cputime.h [new file with mode: 0644]
include/asm-um/apic.h [new file with mode: 0644]
include/asm-um/archparam-x86_64.h [new file with mode: 0644]
include/asm-um/calling.h [new file with mode: 0644]
include/asm-um/cputime.h [new file with mode: 0644]
include/asm-um/dwarf2.h [new file with mode: 0644]
include/asm-um/module-x86_64.h [new file with mode: 0644]
include/asm-um/pda.h [new file with mode: 0644]
include/asm-um/pgtable-2level.h [new file with mode: 0644]
include/asm-um/pgtable-3level.h [new file with mode: 0644]
include/asm-um/prctl.h [new file with mode: 0644]
include/asm-um/processor-x86_64.h [new file with mode: 0644]
include/asm-um/ptrace-x86_64.h [new file with mode: 0644]
include/asm-um/sigcontext-x86_64.h [new file with mode: 0644]
include/asm-um/system-x86_64.h [new file with mode: 0644]
include/asm-um/vm-flags-i386.h [new file with mode: 0644]
include/asm-um/vm-flags-x86_64.h [new file with mode: 0644]
include/asm-v850/cputime.h [new file with mode: 0644]
include/asm-x86_64/cputime.h [new file with mode: 0644]
include/asm-x86_64/crash.h [new file with mode: 0644]
include/asm-x86_64/genapic.h [new file with mode: 0644]
include/asm-x86_64/ipi.h [new file with mode: 0644]
include/asm-x86_64/kexec.h [new file with mode: 0644]
include/asm-x86_64/kprobes.h [new file with mode: 0644]
include/asm-x86_64/mach_apic.h [new file with mode: 0644]
include/linux/attribute_container.h [new file with mode: 0644]
include/linux/backlight.h [new file with mode: 0644]
include/linux/crash_dump.h [new file with mode: 0644]
include/linux/debugfs.h [new file with mode: 0644]
include/linux/elf-fdpic.h [new file with mode: 0644]
include/linux/fsl_devices.h [new file with mode: 0644]
include/linux/i2c-algo-sgi.h [new file with mode: 0644]
include/linux/i2c-algo-sibyte.h [new file with mode: 0644]
include/linux/if_infiniband.h [new file with mode: 0644]
include/linux/key-ui.h [new file with mode: 0644]
include/linux/key.h [new file with mode: 0644]
include/linux/keyctl.h [new file with mode: 0644]
include/linux/kfifo.h [new file with mode: 0644]
include/linux/kobject_uevent.h [new file with mode: 0644]
include/linux/lcd.h [new file with mode: 0644]
include/linux/libps2.h [new file with mode: 0644]
include/linux/mtd/xip.h [new file with mode: 0644]
include/linux/netfilter_bridge/ebt_ulog.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_CLUSTERIP.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_CONNMARK.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_connmark.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_hashlimit.h [new file with mode: 0644]
include/linux/nodemask.h [new file with mode: 0644]
include/linux/pci-acpi.h [new file with mode: 0644]
include/linux/pcieport_if.h [new file with mode: 0644]
include/linux/pktcdvd.h [new file with mode: 0644]
include/linux/rslib.h [new file with mode: 0644]
include/linux/scatterlist.h [new file with mode: 0644]
include/linux/serial_8250.h [new file with mode: 0644]
include/linux/tc_act/tc_ipt.h [new file with mode: 0644]
include/linux/tc_act/tc_mirred.h [new file with mode: 0644]
include/linux/tc_act/tc_pedit.h [new file with mode: 0644]
include/linux/transport_class.h [new file with mode: 0644]
include/linux/usb_sl811.h [new file with mode: 0644]
include/linux/via.h [new file with mode: 0644]
include/media/tveeprom.h [new file with mode: 0644]
include/media/video-buf-dvb.h [new file with mode: 0644]
include/net/act_api.h [new file with mode: 0644]
include/net/sch_generic.h [new file with mode: 0644]
include/net/tc_act/tc_ipt.h [new file with mode: 0644]
include/net/tc_act/tc_mirred.h [new file with mode: 0644]
include/net/tc_act/tc_pedit.h [new file with mode: 0644]
include/net/x25device.h [new file with mode: 0644]
include/scsi/scsi_transport_iscsi.h [new file with mode: 0644]
include/video/w100fb.h [new file with mode: 0644]
init/calibrate.c [new file with mode: 0644]
kernel/irq/Makefile [new file with mode: 0644]
kernel/irq/autoprobe.c [new file with mode: 0644]
kernel/irq/handle.c [new file with mode: 0644]
kernel/irq/internals.h [new file with mode: 0644]
kernel/irq/manage.c [new file with mode: 0644]
kernel/irq/proc.c [new file with mode: 0644]
kernel/irq/spurious.c [new file with mode: 0644]
kernel/kfifo.c [new file with mode: 0644]
kernel/ksysfs.c [new file with mode: 0644]
kernel/module-verify-sig.c [new file with mode: 0644]
kernel/sys_ni.c [new file with mode: 0644]
kernel/wait.c [new file with mode: 0644]
lib/find_next_bit.c [new file with mode: 0644]
lib/kernel_lock.c [new file with mode: 0644]
lib/kobject_uevent.c [new file with mode: 0644]
lib/prio_tree.c [new file with mode: 0644]
lib/reed_solomon/Makefile [new file with mode: 0644]
lib/reed_solomon/decode_rs.c [new file with mode: 0644]
lib/reed_solomon/encode_rs.c [new file with mode: 0644]
lib/reed_solomon/reed_solomon.c [new file with mode: 0644]
mm/internal.h [new file with mode: 0644]
net/appletalk/dev.c [new file with mode: 0644]
net/bridge/netfilter/ebt_ulog.c [new file with mode: 0644]
net/ipv4/netfilter/ipt_CLUSTERIP.c [new file with mode: 0644]
net/ipv4/netfilter/ipt_CONNMARK.c [new file with mode: 0644]
net/ipv4/netfilter/ipt_connmark.c [new file with mode: 0644]
net/ipv4/netfilter/ipt_hashlimit.c [new file with mode: 0644]
net/sched/ipt.c [new file with mode: 0644]
net/sched/mirred.c [new file with mode: 0644]
net/sched/pedit.c [new file with mode: 0644]
scripts/gen_initramfs_list.sh [new file with mode: 0644]
scripts/kconfig/util.c [new file with mode: 0644]
security/keys/Makefile [new file with mode: 0644]
security/keys/compat.c [new file with mode: 0644]
security/keys/internal.h [new file with mode: 0644]
security/keys/key.c [new file with mode: 0644]
security/keys/keyctl.c [new file with mode: 0644]
security/keys/keyring.c [new file with mode: 0644]
security/keys/proc.c [new file with mode: 0644]
security/keys/process_keys.c [new file with mode: 0644]
security/keys/request_key.c [new file with mode: 0644]
security/keys/user_defined.c [new file with mode: 0644]
security/seclvl.c [new file with mode: 0644]
sound/mips/Kconfig [new file with mode: 0644]
sound/mips/Makefile [new file with mode: 0644]
sound/mips/au1x00.c [new file with mode: 0644]
sound/oss/au1550_ac97.c [new file with mode: 0644]
sound/pci/ca0106/Makefile [new file with mode: 0644]
sound/pci/ca0106/ca0106.h [new file with mode: 0644]
sound/pci/ca0106/ca0106_main.c [new file with mode: 0644]
sound/pci/ca0106/ca0106_mixer.c [new file with mode: 0644]
sound/pci/ca0106/ca0106_proc.c [new file with mode: 0644]
sound/pci/emu10k1/emu10k1x.c [new file with mode: 0644]
sound/pci/emu10k1/timer.c [new file with mode: 0644]
sound/pci/ice1712/prodigy192.c [new file with mode: 0644]
sound/pci/ice1712/prodigy192.h [new file with mode: 0644]
sound/pci/ice1712/stac946x.h [new file with mode: 0644]
sound/pci/via82xx_modem.c [new file with mode: 0644]
sound/usb/usx2y/usx2yhwdeppcm.c [new file with mode: 0644]
sound/usb/usx2y/usx2yhwdeppcm.h [new file with mode: 0644]

diff --git a/Documentation/DocBook/librs.tmpl b/Documentation/DocBook/librs.tmpl
new file mode 100644 (file)
index 0000000..be482c0
--- /dev/null
@@ -0,0 +1,287 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="Reed-Solomon-Library-Guide">
+ <bookinfo>
+  <title>Reed-Solomon Library Programming Interface</title>
+  
+  <authorgroup>
+   <author>
+    <firstname>Thomas</firstname>
+    <surname>Gleixner</surname>
+    <affiliation>
+     <address>
+      <email>tglx@linutronix.de</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <copyright>
+   <year>2004</year>
+   <holder>Thomas Gleixner</holder>
+  </copyright>
+
+  <legalnotice>
+   <para>
+     This documentation 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.
+   </para>
+      
+   <para>
+     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.
+   </para>
+      
+   <para>
+     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
+   </para>
+      
+   <para>
+     For more details see the file COPYING in the source
+     distribution of Linux.
+   </para>
+  </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+  <chapter id="intro">
+      <title>Introduction</title>
+  <para>
+       The generic Reed-Solomon Library provides encoding, decoding
+       and error correction functions.
+  </para>
+  <para>
+       Reed-Solomon codes are used in communication and storage
+       applications to ensure data integrity. 
+  </para>
+  <para>
+       This documentation is provided for developers who want to utilize
+       the functions provided by the library.
+  </para>
+  </chapter>
+  
+  <chapter id="bugs">
+     <title>Known Bugs And Assumptions</title>
+  <para>
+       None.   
+  </para>
+  </chapter>
+
+  <chapter id="usage">
+       <title>Usage</title>
+       <para>
+               This chapter provides examples how to use the library.
+       </para>
+       <sect1>
+               <title>Initializing</title>
+               <para>
+                       The init function init_rs returns a pointer to a
+                       rs decoder structure, which holds the necessary
+                       information for encoding, decoding and error correction
+                       with the given polynomial. It either uses an existing
+                       matching decoder or creates a new one. On creation all
+                       the lookup tables for fast en/decoding are created.
+                       The function may take a while, so make sure not to 
+                       call it in critical code paths.
+               </para>
+               <programlisting>
+/* the Reed Solomon control structure */
+static struct rs_control *rs_decoder;
+
+/* Symbolsize is 10 (bits)
+ * Primitve polynomial is x^10+x^3+1
+ * first consecutive root is 0
+ * primitve element to generate roots = 1
+ * generator polinomial degree (number of roots) = 6
+ */
+rs_decoder = init_rs (10, 0x409, 0, 1, 6);
+               </programlisting>
+       </sect1>
+       <sect1>
+               <title>Encoding</title>
+               <para>
+                       The encoder calculates the Reed-Solomon code over
+                       the given data length and stores the result in 
+                       the parity buffer. Note that the parity buffer must
+                       be initialized before calling the encoder.
+               </para>
+               <para>
+                       The expanded data can be inverted on the fly by
+                       providing a non zero inversion mask. The expanded data is
+                       XOR'ed with the mask. This is used e.g. for FLASH
+                       ECC, where the all 0xFF is inverted to an all 0x00.
+                       The Reed-Solomon code for all 0x00 is all 0x00. The
+                       code is inverted before storing to FLASH so it is 0xFF
+                       too. This prevent's that reading from an erased FLASH
+                       results in ECC errors.
+               </para>
+               <para>
+                       The databytes are expanded to the given symbol size
+                       on the fly. There is no support for encoding continuous
+                       bitstreams with a symbol size != 8 at the moment. If
+                       it is necessary it should be not a big deal to implement
+                       such functionality.
+               </para>
+               <programlisting>
+/* Parity buffer. Size = number of roots */
+uint16_t par[6];
+/* Initialize the parity buffer */
+memset(par, 0, sizeof(par));
+/* Encode 512 byte in data8. Store parity in buffer par */
+encode_rs8 (rs_decoder, data8, 512, par, 0);
+               </programlisting>
+       </sect1>
+       <sect1>
+               <title>Decoding</title>
+               <para>
+                       The decoder calculates the syndrome over
+                       the given data length and the received parity symbols
+                       and corrects errors in the data.
+               </para>
+               <para>
+                       If a syndrome is available from a hardware decoder
+                       then the syndrome calculation is skipped.
+               </para>
+               <para>
+                       The correction of the data buffer can be suppressed
+                       by providing a correction pattern buffer and an error
+                       location buffer to the decoder. The decoder stores the
+                       calculated error location and the correction bitmask
+                       in the given buffers. This is useful for hardware
+                       decoders which use a weird bit ordering scheme.
+               </para>
+               <para>
+                       The databytes are expanded to the given symbol size
+                       on the fly. There is no support for decoding continuous
+                       bitstreams with a symbolsize != 8 at the moment. If
+                       it is necessary it should be not a big deal to implement
+                       such functionality.
+               </para>
+               
+               <sect2>
+               <title>
+                       Decoding with syndrome calculation, direct data correction
+               </title>
+               <programlisting>
+/* Parity buffer. Size = number of roots */
+uint16_t par[6];
+uint8_t  data[512];
+int numerr;
+/* Receive data */
+.....
+/* Receive parity */
+.....
+/* Decode 512 byte in data8.*/
+numerr = decode_rs8 (rs_decoder, data8, par, 512, NULL, 0, NULL, 0, NULL);
+               </programlisting>
+               </sect2>
+
+               <sect2>
+               <title>
+                       Decoding with syndrome given by hardware decoder, direct data correction
+               </title>
+               <programlisting>
+/* Parity buffer. Size = number of roots */
+uint16_t par[6], syn[6];
+uint8_t  data[512];
+int numerr;
+/* Receive data */
+.....
+/* Receive parity */
+.....
+/* Get syndrome from hardware decoder */
+.....
+/* Decode 512 byte in data8.*/
+numerr = decode_rs8 (rs_decoder, data8, par, 512, syn, 0, NULL, 0, NULL);
+               </programlisting>
+               </sect2>
+
+               <sect2>
+               <title>
+                       Decoding with syndrome given by hardware decoder, no direct data correction.
+               </title>
+               <para>
+                       Note: It's not necessary to give data and received parity to the decoder.
+               </para>
+               <programlisting>
+/* Parity buffer. Size = number of roots */
+uint16_t par[6], syn[6], corr[8];
+uint8_t  data[512];
+int numerr, errpos[8];
+/* Receive data */
+.....
+/* Receive parity */
+.....
+/* Get syndrome from hardware decoder */
+.....
+/* Decode 512 byte in data8.*/
+numerr = decode_rs8 (rs_decoder, NULL, NULL, 512, syn, 0, errpos, 0, corr);
+for (i = 0; i < numerr; i++) {
+       do_error_correction_in_your_buffer(errpos[i], corr[i]);
+}
+               </programlisting>
+               </sect2>
+       </sect1>
+       <sect1>
+               <title>Cleanup</title>
+               <para>
+                       The function free_rs frees the allocated resources,
+                       if the caller is the last user of the decoder.
+               </para>
+               <programlisting>
+/* Release resources */
+free_rs(rs_decoder);
+               </programlisting>
+       </sect1>
+
+  </chapter>
+       
+  <chapter id="structs">
+     <title>Structures</title>
+     <para>
+     This chapter contains the autogenerated documentation of the structures which are
+     used in the Reed-Solomon Library and are relevant for a developer.
+     </para>
+!Iinclude/linux/rslib.h
+  </chapter>
+
+  <chapter id="pubfunctions">
+     <title>Public Functions Provided</title>
+     <para>
+     This chapter contains the autogenerated documentation of the Reed-Solomon functions
+     which are exported.
+     </para>
+!Elib/reed_solomon/reed_solomon.c
+  </chapter>
+  
+  <chapter id="credits">
+     <title>Credits</title>
+       <para>
+               The library code for encoding and decoding was written by Phil Karn.
+       </para>
+       <programlisting>
+               Copyright 2002, Phil Karn, KA9Q
+               May be used under the terms of the GNU General Public License (GPL)
+       </programlisting>
+       <para>
+               The wrapper functions and interfaces are written by Thomas Gleixner
+       </para>
+       <para>
+               Many users have provided bugfixes, improvements and helping hands for testing.
+               Thanks a lot.
+       </para>
+       <para>
+               The following people have contributed to this document:
+       </para>
+       <para>
+               Thomas Gleixner<email>tglx@linutronix.de</email>
+       </para>
+  </chapter>
+</book>
diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl
new file mode 100644 (file)
index 0000000..435bb52
--- /dev/null
@@ -0,0 +1,1318 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="MTD-NAND-Guide">
+ <bookinfo>
+  <title>MTD NAND Driver Programming Interface</title>
+  
+  <authorgroup>
+   <author>
+    <firstname>Thomas</firstname>
+    <surname>Gleixner</surname>
+    <affiliation>
+     <address>
+      <email>tglx@linutronix.de</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <copyright>
+   <year>2004</year>
+   <holder>Thomas Gleixner</holder>
+  </copyright>
+
+  <legalnotice>
+   <para>
+     This documentation 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.
+   </para>
+      
+   <para>
+     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.
+   </para>
+      
+   <para>
+     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
+   </para>
+      
+   <para>
+     For more details see the file COPYING in the source
+     distribution of Linux.
+   </para>
+  </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+  <chapter id="intro">
+      <title>Introduction</title>
+  <para>
+       The generic NAND driver supports almost all NAND and AG-AND based
+       chips and connects them to the Memory Technology Devices (MTD)
+       subsystem of the Linux Kernel.
+  </para>
+  <para>
+       This documentation is provided for developers who want to implement
+       board drivers or filesystem drivers suitable for NAND devices.
+  </para>
+  </chapter>
+  
+  <chapter id="bugs">
+     <title>Known Bugs And Assumptions</title>
+  <para>
+       None.   
+  </para>
+  </chapter>
+
+  <chapter id="dochints">
+     <title>Documentation hints</title>
+     <para>
+     The function and structure docs are autogenerated. Each function and 
+     struct member has a short description which is marked with an [XXX] identifier.
+     The following chapters explain the meaning of those identifiers.
+     </para>
+     <sect1>   
+       <title>Function identifiers [XXX]</title>
+       <para>
+       The functions are marked with [XXX] identifiers in the short
+       comment. The identifiers explain the usage and scope of the
+       functions. Following identifiers are used:
+       </para>
+       <itemizedlist>
+               <listitem><para>
+               [MTD Interface]</para><para>
+               These functions provide the interface to the MTD kernel API. 
+               They are not replacable and provide functionality
+               which is complete hardware independent.
+               </para></listitem>
+               <listitem><para>
+               [NAND Interface]</para><para>
+               These functions are exported and provide the interface to the NAND kernel API. 
+               </para></listitem>
+               <listitem><para>
+               [GENERIC]</para><para>
+               Generic functions are not replacable and provide functionality
+               which is complete hardware independent.
+               </para></listitem>
+               <listitem><para>
+               [DEFAULT]</para><para>
+               Default functions provide hardware related functionality which is suitable
+               for most of the implementations. These functions can be replaced by the
+               board driver if neccecary. Those functions are called via pointers in the
+               NAND chip description structure. The board driver can set the functions which
+               should be replaced by board dependend functions before calling nand_scan().
+               If the function pointer is NULL on entry to nand_scan() then the pointer
+               is set to the default function which is suitable for the detected chip type.
+               </para></listitem>
+       </itemizedlist>
+     </sect1>
+     <sect1>   
+       <title>Struct member identifiers [XXX]</title>
+       <para>
+       The struct members are marked with [XXX] identifiers in the 
+       comment. The identifiers explain the usage and scope of the
+       members. Following identifiers are used:
+       </para>
+       <itemizedlist>
+               <listitem><para>
+               [INTERN]</para><para>
+               These members are for NAND driver internal use only and must not be
+               modified. Most of these values are calculated from the chip geometry
+               information which is evaluated during nand_scan().
+               </para></listitem>
+               <listitem><para>
+               [REPLACEABLE]</para><para>
+               Replaceable members hold hardware related functions which can be 
+               provided by the board driver. The board driver can set the functions which
+               should be replaced by board dependend functions before calling nand_scan().
+               If the function pointer is NULL on entry to nand_scan() then the pointer
+               is set to the default function which is suitable for the detected chip type.
+               </para></listitem>
+               <listitem><para>
+               [BOARDSPECIFIC]</para><para>
+               Board specific members hold hardware related information which must
+               be provided by the board driver. The board driver must set the function
+               pointers and datafields before calling nand_scan().
+               </para></listitem>
+               <listitem><para>
+               [OPTIONAL]</para><para>
+               Optional members can hold information relevant for the board driver. The
+               generic NAND driver code does not use this information.
+               </para></listitem>
+       </itemizedlist>
+     </sect1>
+  </chapter>   
+
+  <chapter id="basicboarddriver">
+       <title>Basic board driver</title>
+       <para>
+               For most boards it will be sufficient to provide just the
+               basic functions and fill out some really board dependend
+               members in the nand chip description structure.
+               See drivers/mtd/nand/skeleton for reference.
+       </para>
+       <sect1>
+               <title>Basic defines</title>
+               <para>
+                       At least you have to provide a mtd structure and
+                       a storage for the ioremap'ed chip address.
+                       You can allocate the mtd structure using kmalloc
+                       or you can allocate it statically.
+                       In case of static allocation you have to allocate
+                       a nand_chip structure too.
+               </para>
+               <para>
+                       Kmalloc based example
+               </para>
+               <programlisting>
+static struct mtd_info *board_mtd;
+static unsigned long baseaddr;
+               </programlisting>
+               <para>
+                       Static example
+               </para>
+               <programlisting>
+static struct mtd_info board_mtd;
+static struct nand_chip board_chip;
+static unsigned long baseaddr;
+               </programlisting>
+       </sect1>
+       <sect1>
+               <title>Partition defines</title>
+               <para>
+                       If you want to divide your device into parititions, then
+                       enable the configuration switch CONFIG_MTD_PARITIONS and define
+                       a paritioning scheme suitable to your board.
+               </para>
+               <programlisting>
+#define NUM_PARTITIONS 2
+static struct mtd_partition partition_info[] = {
+       { .name = "Flash partition 1",
+         .offset =  0,
+         .size =    8 * 1024 * 1024 },
+       { .name = "Flash partition 2",
+         .offset =  MTDPART_OFS_NEXT,
+         .size =    MTDPART_SIZ_FULL },
+};
+               </programlisting>
+       </sect1>
+       <sect1>
+               <title>Hardware control function</title>
+               <para>
+                       The hardware control function provides access to the 
+                       control pins of the NAND chip(s). 
+                       The access can be done by GPIO pins or by address lines.
+                       If you use address lines, make sure that the timing
+                       requirements are met.
+               </para>
+               <para>
+                       <emphasis>GPIO based example</emphasis>
+               </para>
+               <programlisting>
+static void board_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+       switch(cmd){
+               case NAND_CTL_SETCLE: /* Set CLE pin high */ break;
+               case NAND_CTL_CLRCLE: /* Set CLE pin low */ break;
+               case NAND_CTL_SETALE: /* Set ALE pin high */ break;
+               case NAND_CTL_CLRALE: /* Set ALE pin low */ break;
+               case NAND_CTL_SETNCE: /* Set nCE pin low */ break;
+               case NAND_CTL_CLRNCE: /* Set nCE pin high */ break;
+       }
+}
+               </programlisting>
+               <para>
+                       <emphasis>Address lines based example.</emphasis> It's assumed that the
+                       nCE pin is driven by a chip select decoder.
+               </para>
+               <programlisting>
+static void board_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+       struct nand_chip *this = (struct nand_chip *) mtd->priv;
+       switch(cmd){
+               case NAND_CTL_SETCLE: this->IO_ADDR_W |= CLE_ADRR_BIT;  break;
+               case NAND_CTL_CLRCLE: this->IO_ADDR_W &= ~CLE_ADRR_BIT; break;
+               case NAND_CTL_SETALE: this->IO_ADDR_W |= ALE_ADRR_BIT;  break;
+               case NAND_CTL_CLRALE: this->IO_ADDR_W &= ~ALE_ADRR_BIT; break;
+       }
+}
+               </programlisting>
+       </sect1>
+       <sect1>
+               <title>Device ready function</title>
+               <para>
+                       If the hardware interface has the ready busy pin of the NAND chip connected to a
+                       GPIO or other accesible I/O pin, this function is used to read back the state of the
+                       pin. The function has no arguments and should return 0, if the device is busy (R/B pin 
+                       is low) and 1, if the device is ready (R/B pin is high).
+                       If the hardware interface does not give access to the ready busy pin, then
+                       the function must not be defined and the function pointer this->dev_ready is set to NULL.               
+               </para>
+       </sect1>
+       <sect1>
+               <title>Init function</title>
+               <para>
+                       The init function allocates memory and sets up all the board
+                       specific parameters and function pointers. When everything
+                       is set up nand_scan() is called. This function tries to
+                       detect and identify then chip. If a chip is found all the
+                       internal data fields are initialized accordingly.
+                       The structure(s) have to be zeroed out first and then filled with the neccecary 
+                       information about the device.
+               </para>
+               <programlisting>
+int __init board_init (void)
+{
+       struct nand_chip *this;
+       int err = 0;
+
+       /* Allocate memory for MTD device structure and private data */
+       board_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL);
+       if (!board_mtd) {
+               printk ("Unable to allocate NAND MTD device structure.\n");
+               err = -ENOMEM;
+               goto out;
+       }
+
+       /* Initialize structures */
+       memset ((char *) board_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip));
+
+       /* map physical adress */
+       baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024);
+       if(!baseaddr){
+               printk("Ioremap to access NAND chip failed\n");
+               err = -EIO;
+               goto out_mtd;
+       }
+
+       /* Get pointer to private data */
+       this = (struct nand_chip *) ();
+       /* Link the private data with the MTD structure */
+       board_mtd->priv = this;
+
+       /* Set address of NAND IO lines */
+       this->IO_ADDR_R = baseaddr;
+       this->IO_ADDR_W = baseaddr;
+       /* Reference hardware control function */
+       this->hwcontrol = board_hwcontrol;
+       /* Set command delay time, see datasheet for correct value */
+       this->chip_delay = CHIP_DEPENDEND_COMMAND_DELAY;
+       /* Assign the device ready function, if available */
+       this->dev_ready = board_dev_ready;
+       this->eccmode = NAND_ECC_SOFT;
+
+       /* Scan to find existance of the device */
+       if (nand_scan (board_mtd, 1)) {
+               err = -ENXIO;
+               goto out_ior;
+       }
+       
+       add_mtd_partitions(board_mtd, partition_info, NUM_PARTITIONS);
+       goto out;
+
+out_ior:
+       iounmap((void *)baseaddr);
+out_mtd:
+       kfree (board_mtd);
+out:
+       return err;
+}
+module_init(board_init);
+               </programlisting>
+       </sect1>
+       <sect1>
+               <title>Exit function</title>
+               <para>
+                       The exit function is only neccecary if the driver is
+                       compiled as a module. It releases all resources which
+                       are held by the chip driver and unregisters the partitions
+                       in the MTD layer.
+               </para>
+               <programlisting>
+#ifdef MODULE
+static void __exit board_cleanup (void)
+{
+       /* Release resources, unregister device */
+       nand_release (board_mtd);
+
+       /* unmap physical adress */
+       iounmap((void *)baseaddr);
+       
+       /* Free the MTD device structure */
+       kfree (board_mtd);
+}
+module_exit(board_cleanup);
+#endif
+               </programlisting>
+       </sect1>
+  </chapter>
+
+  <chapter id="boarddriversadvanced">
+       <title>Advanced board driver functions</title>
+       <para>
+               This chapter describes the advanced functionality of the NAND
+               driver. For a list of functions which can be overridden by the board
+               driver see the documentation of the nand_chip structure.
+       </para>
+       <sect1>
+               <title>Multiple chip control</title>
+               <para>
+                       The nand driver can control chip arrays. Therefor the
+                       board driver must provide an own select_chip function. This
+                       function must (de)select the requested chip.
+                       The function pointer in the nand_chip structure must
+                       be set before calling nand_scan(). The maxchip parameter
+                       of nand_scan() defines the maximum number of chips to
+                       scan for. Make sure that the select_chip function can
+                       handle the requested number of chips.
+               </para>
+               <para>
+                       The nand driver concatenates the chips to one virtual
+                       chip and provides this virtual chip to the MTD layer.
+               </para>
+               <para>
+                       <emphasis>Note: The driver can only handle linear chip arrays
+                       of equally sized chips. There is no support for
+                       parallel arrays which extend the buswidth.</emphasis>
+               </para>
+               <para>
+                       <emphasis>GPIO based example</emphasis>
+               </para>
+               <programlisting>
+static void board_select_chip (struct mtd_info *mtd, int chip)
+{
+       /* Deselect all chips, set all nCE pins high */
+       GPIO(BOARD_NAND_NCE) |= 0xff;   
+       if (chip >= 0)
+               GPIO(BOARD_NAND_NCE) &= ~ (1 << chip);  
+}
+               </programlisting>
+               <para>
+                       <emphasis>Address lines based example.</emphasis>
+                       Its assumed that the nCE pins are connected to an
+                       address decoder.
+               </para>
+               <programlisting>
+static void board_select_chip (struct mtd_info *mtd, int chip)
+{
+       struct nand_chip *this = (struct nand_chip *) mtd->priv;
+       
+       /* Deselect all chips */
+       this->IO_ADDR_R &= ~BOARD_NAND_ADDR_MASK;
+       this->IO_ADDR_W &= ~BOARD_NAND_ADDR_MASK;
+       switch (chip) {
+       case 0:
+               this->IO_ADDR_R |= BOARD_NAND_ADDR_CHIP0;
+               this->IO_ADDR_W |= BOARD_NAND_ADDR_CHIP0;
+               break;
+       ....    
+       case n:
+               this->IO_ADDR_R |= BOARD_NAND_ADDR_CHIPn;
+               this->IO_ADDR_W |= BOARD_NAND_ADDR_CHIPn;
+               break;
+       }       
+}
+               </programlisting>
+       </sect1>
+       <sect1>
+               <title>Hardware ECC support</title>
+               <sect2>
+                       <title>Functions and constants</title>
+                       <para>
+                               The nand driver supports three different types of
+                               hardware ECC.
+                               <itemizedlist>
+                               <listitem><para>NAND_ECC_HW3_256</para><para>
+                               Hardware ECC generator providing 3 bytes ECC per
+                               256 byte.
+                               </para> </listitem>
+                               <listitem><para>NAND_ECC_HW3_512</para><para>
+                               Hardware ECC generator providing 3 bytes ECC per
+                               512 byte.
+                               </para> </listitem>
+                               <listitem><para>NAND_ECC_HW6_512</para><para>
+                               Hardware ECC generator providing 6 bytes ECC per
+                               512 byte.
+                               </para> </listitem>
+                               <listitem><para>NAND_ECC_HW8_512</para><para>
+                               Hardware ECC generator providing 6 bytes ECC per
+                               512 byte.
+                               </para> </listitem>
+                               </itemizedlist>
+                               If your hardware generator has a different functionality
+                               add it at the appropriate place in nand_base.c
+                       </para>
+                       <para>
+                               The board driver must provide following functions:
+                               <itemizedlist>
+                               <listitem><para>enable_hwecc</para><para>
+                               This function is called before reading / writing to
+                               the chip. Reset or initialize the hardware generator
+                               in this function. The function is called with an
+                               argument which let you distinguish between read 
+                               and write operations.
+                               </para> </listitem>
+                               <listitem><para>calculate_ecc</para><para>
+                               This function is called after read / write from / to
+                               the chip. Transfer the ECC from the hardware to
+                               the buffer. If the option NAND_HWECC_SYNDROME is set
+                               then the function is only called on write. See below.
+                               </para> </listitem>
+                               <listitem><para>correct_data</para><para>
+                               In case of an ECC error this function is called for
+                               error detection and correction. Return 1 respectively 2
+                               in case the error can be corrected. If the error is
+                               not correctable return -1. If your hardware generator
+                               matches the default algorithm of the nand_ecc software
+                               generator then use the correction function provided
+                               by nand_ecc instead of implementing duplicated code.
+                               </para> </listitem>
+                               </itemizedlist>
+                       </para>
+               </sect2>
+               <sect2>
+               <title>Hardware ECC with syndrome calculation</title>
+                       <para>
+                               Many hardware ECC implementations provide Reed-Solomon
+                               codes and calculate an error syndrome on read. The syndrome
+                               must be converted to a standard Reed-Solomon syndrome
+                               before calling the error correction code in the generic
+                               Reed-Solomon library.
+                       </para>
+                       <para>
+                               The ECC bytes must be placed immidiately after the data
+                               bytes in order to make the syndrome generator work. This
+                               is contrary to the usual layout used by software ECC. The
+                               seperation of data and out of band area is not longer
+                               possible. The nand driver code handles this layout and
+                               the remaining free bytes in the oob area are managed by 
+                               the autoplacement code. Provide a matching oob-layout
+                               in this case. See rts_from4.c and diskonchip.c for 
+                               implementation reference. In those cases we must also
+                               use bad block tables on FLASH, because the ECC layout is
+                               interferring with the bad block marker positions.
+                               See bad block table support for details.
+                       </para>
+               </sect2>
+       </sect1>
+       <sect1>
+               <title>Bad block table support</title>
+               <para>
+                       Most NAND chips mark the bad blocks at a defined
+                       position in the spare area. Those blocks must 
+                       not be erased under any circumstances as the bad 
+                       block information would be lost.
+                       It is possible to check the bad block mark each
+                       time when the blocks are accessed by reading the
+                       spare area of the first page in the block. This
+                       is time consuming so a bad block table is used.
+               </para>
+               <para>
+                       The nand driver supports various types of bad block
+                       tables.
+                       <itemizedlist>
+                       <listitem><para>Per device</para><para>
+                       The bad block table contains all bad block information
+                       of the device which can consist of multiple chips.
+                       </para> </listitem>
+                       <listitem><para>Per chip</para><para>
+                       A bad block table is used per chip and contains the
+                       bad block information for this particular chip.
+                       </para> </listitem>
+                       <listitem><para>Fixed offset</para><para>
+                       The bad block table is located at a fixed offset
+                       in the chip (device). This applies to various
+                       DiskOnChip devices.
+                       </para> </listitem>
+                       <listitem><para>Automatic placed</para><para>
+                       The bad block table is automatically placed and
+                       detected either at the end or at the beginning
+                       of a chip (device)
+                       </para> </listitem>
+                       <listitem><para>Mirrored tables</para><para>
+                       The bad block table is mirrored on the chip (device) to
+                       allow updates of the bad block table without data loss.
+                       </para> </listitem>
+                       </itemizedlist>
+               </para>
+               <para>  
+                       nand_scan() calls the function nand_default_bbt(). 
+                       nand_default_bbt() selects appropriate default
+                       bad block table desriptors depending on the chip information
+                       which was retrieved by nand_scan().
+               </para>
+               <para>
+                       The standard policy is scanning the device for bad 
+                       blocks and build a ram based bad block table which
+                       allows faster access than always checking the
+                       bad block information on the flash chip itself.
+               </para>
+               <sect2>
+                       <title>Flash based tables</title>
+                       <para>
+                               It may be desired or neccecary to keep a bad block table in FLASH. 
+                               For AG-AND chips this is mandatory, as they have no factory marked
+                               bad blocks. They have factory marked good blocks. The marker pattern
+                               is erased when the block is erased to be reused. So in case of
+                               powerloss before writing the pattern back to the chip this block 
+                               would be lost and added to the bad blocks. Therefor we scan the 
+                               chip(s) when we detect them the first time for good blocks and 
+                               store this information in a bad block table before erasing any 
+                               of the blocks.
+                       </para>
+                       <para>
+                               The blocks in which the tables are stored are procteted against
+                               accidental access by marking them bad in the memory bad block
+                               table. The bad block table managment functions are allowed
+                               to circumvernt this protection.
+                       </para>
+                       <para>
+                               The simplest way to activate the FLASH based bad block table support 
+                               is to set the option NAND_USE_FLASH_BBT in the option field of
+                               the nand chip structure before calling nand_scan(). For AG-AND
+                               chips is this done by default.
+                               This activates the default FLASH based bad block table functionality 
+                               of the NAND driver. The default bad block table options are
+                               <itemizedlist>
+                               <listitem><para>Store bad block table per chip</para></listitem>
+                               <listitem><para>Use 2 bits per block</para></listitem>
+                               <listitem><para>Automatic placement at the end of the chip</para></listitem>
+                               <listitem><para>Use mirrored tables with version numbers</para></listitem>
+                               <listitem><para>Reserve 4 blocks at the end of the chip</para></listitem>
+                               </itemizedlist>
+                       </para>
+               </sect2>
+               <sect2>
+                       <title>User defined tables</title>
+                       <para>
+                               User defined tables are created by filling out a 
+                               nand_bbt_descr structure and storing the pointer in the
+                               nand_chip structure member bbt_td before calling nand_scan(). 
+                               If a mirror table is neccecary a second structure must be
+                               created and a pointer to this structure must be stored
+                               in bbt_md inside the nand_chip structure. If the bbt_md 
+                               member is set to NULL then only the main table is used
+                               and no scan for the mirrored table is performed.
+                       </para>
+                       <para>
+                               The most important field in the nand_bbt_descr structure
+                               is the options field. The options define most of the 
+                               table properties. Use the predefined constants from
+                               nand.h to define the options.
+                               <itemizedlist>
+                               <listitem><para>Number of bits per block</para>
+                               <para>The supported number of bits is 1, 2, 4, 8.</para></listitem>
+                               <listitem><para>Table per chip</para>
+                               <para>Setting the constant NAND_BBT_PERCHIP selects that
+                               a bad block table is managed for each chip in a chip array.
+                               If this option is not set then a per device bad block table
+                               is used.</para></listitem>
+                               <listitem><para>Table location is absolute</para>
+                               <para>Use the option constant NAND_BBT_ABSPAGE and
+                               define the absolute page number where the bad block
+                               table starts in the field pages. If you have selected bad block
+                               tables per chip and you have a multi chip array then the start page
+                               must be given for each chip in the chip array. Note: there is no scan
+                               for a table ident pattern performed, so the fields 
+                               pattern, veroffs, offs, len can be left uninitialized</para></listitem>
+                               <listitem><para>Table location is automatically detected</para>
+                               <para>The table can either be located in the first or the last good
+                               blocks of the chip (device). Set NAND_BBT_LASTBLOCK to place
+                               the bad block table at the end of the chip (device). The
+                               bad block tables are marked and identified by a pattern which
+                               is stored in the spare area of the first page in the block which
+                               holds the bad block table. Store a pointer to the pattern  
+                               in the pattern field. Further the length of the pattern has to be 
+                               stored in len and the offset in the spare area must be given
+                               in the offs member of the nand_bbt_descr stucture. For mirrored
+                               bad block tables different patterns are mandatory.</para></listitem>
+                               <listitem><para>Table creation</para>
+                               <para>Set the option NAND_BBT_CREATE to enable the table creation
+                               if no table can be found during the scan. Usually this is done only 
+                               once if a new chip is found. </para></listitem>
+                               <listitem><para>Table write support</para>
+                               <para>Set the option NAND_BBT_WRITE to enable the table write support.
+                               This allows the update of the bad block table(s) in case a block has
+                               to be marked bad due to wear. The MTD interface function block_markbad
+                               is calling the update function of the bad block table. If the write
+                               support is enabled then the table is updated on FLASH.</para>
+                               <para>
+                               Note: Write support should only be enabled for mirrored tables with
+                               version control.
+                               </para></listitem>
+                               <listitem><para>Table version control</para>
+                               <para>Set the option NAND_BBT_VERSION to enable the table version control.
+                               It's highly recommended to enable this for mirrored tables with write
+                               support. It makes sure that the risk of loosing the bad block
+                               table information is reduced to the loss of the information about the
+                               one worn out block which should be marked bad. The version is stored in
+                               4 consecutive bytes in the spare area of the device. The position of
+                               the version number is defined by the member veroffs in the bad block table
+                               descriptor.</para></listitem>
+                               <listitem><para>Save block contents on write</para>
+                               <para>
+                               In case that the block which holds the bad block table does contain
+                               other useful information, set the option NAND_BBT_SAVECONTENT. When
+                               the bad block table is written then the whole block is read the bad
+                               block table is updated and the block is erased and everything is 
+                               written back. If this option is not set only the bad block table
+                               is written and everything else in the block is ignored and erased.
+                               </para></listitem>
+                               <listitem><para>Number of reserved blocks</para>
+                               <para>
+                               For automatic placement some blocks must be reserved for
+                               bad block table storage. The number of reserved blocks is defined 
+                               in the maxblocks member of the babd block table description structure.
+                               Reserving 4 blocks for mirrored tables should be a reasonable number. 
+                               This also limits the number of blocks which are scanned for the bad
+                               block table ident pattern.
+                               </para></listitem>
+                               </itemizedlist>
+                       </para>
+               </sect2>
+       </sect1>
+       <sect1>
+               <title>Spare area (auto)placement</title>
+               <para>
+                       The nand driver implements different possibilities for
+                       placement of filesystem data in the spare area, 
+                       <itemizedlist>
+                       <listitem><para>Placement defined by fs driver</para></listitem>
+                       <listitem><para>Automatic placement</para></listitem>
+                       </itemizedlist>
+                       The default placement function is automatic placement. The
+                       nand driver has built in default placement schemes for the
+                       various chiptypes. If due to hardware ECC functionality the
+                       default placement does not fit then the board driver can
+                       provide a own placement scheme.
+               </para>
+               <para>
+                       File system drivers can provide a own placement scheme which
+                       is used instead of the default placement scheme.
+               </para>
+               <para>
+                       Placement schemes are defined by a nand_oobinfo structure
+                       <programlisting>
+struct nand_oobinfo {
+       int     useecc;
+       int     eccbytes;
+       int     eccpos[24];
+       int     oobfree[8][2];
+};
+                       </programlisting>
+                       <itemizedlist>
+                       <listitem><para>useecc</para><para>
+                               The useecc member controls the ecc and placement function. The header
+                               file include/mtd/mtd-abi.h contains constants to select ecc and
+                               placement. MTD_NANDECC_OFF switches off the ecc complete. This is
+                               not recommended and available for testing and diagnosis only.
+                               MTD_NANDECC_PLACE selects caller defined placement, MTD_NANDECC_AUTOPLACE
+                               selects automatic placement.
+                       </para></listitem>
+                       <listitem><para>eccbytes</para><para>
+                               The eccbytes member defines the number of ecc bytes per page.
+                       </para></listitem>
+                       <listitem><para>eccpos</para><para>
+                               The eccpos array holds the byte offsets in the spare area where
+                               the ecc codes are placed.
+                       </para></listitem>
+                       <listitem><para>oobfree</para><para>
+                               The oobfree array defines the areas in the spare area which can be
+                               used for automatic placement. The information is given in the format
+                               {offset, size}. offset defines the start of the usable area, size the
+                               length in bytes. More than one area can be defined. The list is terminated
+                               by an {0, 0} entry.
+                       </para></listitem>
+                       </itemizedlist>
+               </para>
+               <sect2>
+                       <title>Placement defined by fs driver</title>
+                       <para>
+                               The calling function provides a pointer to a nand_oobinfo
+                               structure which defines the ecc placement. For writes the
+                               caller must provide a spare area buffer along with the
+                               data buffer. The spare area buffer size is (number of pages) *
+                               (size of spare area). For reads the buffer size is
+                               (number of pages) * ((size of spare area) + (number of ecc
+                               steps per page) * sizeof (int)). The driver stores the
+                               result of the ecc check for each tuple in the spare buffer.
+                               The storage sequence is 
+                       </para>
+                       <para>
+                               &lt;spare data page 0&gt;&lt;ecc result 0&gt;...&lt;ecc result n&gt;
+                       </para>
+                       <para>
+                               ...
+                       </para>
+                       <para>
+                               &lt;spare data page n&gt;&lt;ecc result 0&gt;...&lt;ecc result n&gt;
+                       </para>
+                       <para>
+                               This is a legacy mode used by YAFFS1.
+                       </para>
+                       <para>
+                               If the spare area buffer is NULL then only the ECC placement is
+                               done according to the given scheme in the nand_oobinfo structure.
+                       </para>
+               </sect2>
+               <sect2>
+                       <title>Automatic placement</title>
+                       <para>
+                               Automatic placement uses the built in defaults to place the
+                               ecc bytes in the spare area. If filesystem data have to be stored /
+                               read into the spare area then the calling function must provide a
+                               buffer. The buffer size per page is determined by the oobfree array in
+                               the nand_oobinfo structure.
+                       </para>
+                       <para>
+                               If the spare area buffer is NULL then only the ECC placement is
+                               done according to the default builtin scheme.
+                       </para>
+               </sect2>
+               <sect2>
+                       <title>User space placement selection</title>
+               <para>
+                       All non ecc functions like mtd->read and mtd->write use an internal 
+                       structure, which can be set by an ioctl. This structure is preset 
+                       to the autoplacement default.
+                       <programlisting>
+       ioctl (fd, MEMSETOOBSEL, oobsel);
+                       </programlisting>
+                       oobsel is a pointer to a user supplied structure of type
+                       nand_oobconfig. The contents of this structure must match the 
+                       criteria of the filesystem, which will be used. See an example in utils/nandwrite.c.
+               </para>
+               </sect2>
+       </sect1>        
+       <sect1>
+               <title>Spare area autoplacement default schemes</title>
+               <sect2>
+                       <title>256 byte pagesize</title>
+<informaltable><tgroup cols="3"><tbody>
+<row>
+<entry>Offset</entry>
+<entry>Content</entry>
+<entry>Comment</entry>
+</row>
+<row>
+<entry>0x00</entry>
+<entry>ECC byte 0</entry>
+<entry>Error correction code byte 0</entry>
+</row>
+<row>
+<entry>0x01</entry>
+<entry>ECC byte 1</entry>
+<entry>Error correction code byte 1</entry>
+</row>
+<row>
+<entry>0x02</entry>
+<entry>ECC byte 2</entry>
+<entry>Error correction code byte 2</entry>
+</row>
+<row>
+<entry>0x03</entry>
+<entry>Autoplace 0</entry>
+<entry></entry>
+</row>
+<row>
+<entry>0x04</entry>
+<entry>Autoplace 1</entry>
+<entry></entry>
+</row>
+<row>
+<entry>0x05</entry>
+<entry>Bad block marker</entry>
+<entry>If any bit in this byte is zero, then this block is bad.
+This applies only to the first page in a block. In the remaining
+pages this byte is reserved</entry>
+</row>
+<row>
+<entry>0x06</entry>
+<entry>Autoplace 2</entry>
+<entry></entry>
+</row>
+<row>
+<entry>0x07</entry>
+<entry>Autoplace 3</entry>
+<entry></entry>
+</row>
+</tbody></tgroup></informaltable>
+               </sect2>
+               <sect2>
+                       <title>512 byte pagesize</title>
+<informaltable><tgroup cols="3"><tbody>
+<row>
+<entry>Offset</entry>
+<entry>Content</entry>
+<entry>Comment</entry>
+</row>
+<row>
+<entry>0x00</entry>
+<entry>ECC byte 0</entry>
+<entry>Error correction code byte 0 of the lower 256 Byte data in
+this page</entry>
+</row>
+<row>
+<entry>0x01</entry>
+<entry>ECC byte 1</entry>
+<entry>Error correction code byte 1 of the lower 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x02</entry>
+<entry>ECC byte 2</entry>
+<entry>Error correction code byte 2 of the lower 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x03</entry>
+<entry>ECC byte 3</entry>
+<entry>Error correction code byte 0 of the upper 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x04</entry>
+<entry>reserved</entry>
+<entry>reserved</entry>
+</row>
+<row>
+<entry>0x05</entry>
+<entry>Bad block marker</entry>
+<entry>If any bit in this byte is zero, then this block is bad.
+This applies only to the first page in a block. In the remaining
+pages this byte is reserved</entry>
+</row>
+<row>
+<entry>0x06</entry>
+<entry>ECC byte 4</entry>
+<entry>Error correction code byte 1 of the upper 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x07</entry>
+<entry>ECC byte 5</entry>
+<entry>Error correction code byte 2 of the upper 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x08 - 0x0F</entry>
+<entry>Autoplace 0 - 7</entry>
+<entry></entry>
+</row>
+</tbody></tgroup></informaltable>
+               </sect2>
+               <sect2>
+                       <title>2048 byte pagesize</title>
+<informaltable><tgroup cols="3"><tbody>
+<row>
+<entry>Offset</entry>
+<entry>Content</entry>
+<entry>Comment</entry>
+</row>
+<row>
+<entry>0x00</entry>
+<entry>Bad block marker</entry>
+<entry>If any bit in this byte is zero, then this block is bad.
+This applies only to the first page in a block. In the remaining
+pages this byte is reserved</entry>
+</row>
+<row>
+<entry>0x01</entry>
+<entry>Reserved</entry>
+<entry>Reserved</entry>
+</row>
+<row>
+<entry>0x02-0x27</entry>
+<entry>Autoplace 0 - 37</entry>
+<entry></entry>
+</row>
+<row>
+<entry>0x28</entry>
+<entry>ECC byte 0</entry>
+<entry>Error correction code byte 0 of the first 256 Byte data in
+this page</entry>
+</row>
+<row>
+<entry>0x29</entry>
+<entry>ECC byte 1</entry>
+<entry>Error correction code byte 1 of the first 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x2A</entry>
+<entry>ECC byte 2</entry>
+<entry>Error correction code byte 2 of the first 256 Bytes data in
+this page</entry>
+</row>
+<row>
+<entry>0x2B</entry>
+<entry>ECC byte 3</entry>
+<entry>Error correction code byte 0 of the second 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x2C</entry>
+<entry>ECC byte 4</entry>
+<entry>Error correction code byte 1 of the second 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x2D</entry>
+<entry>ECC byte 5</entry>
+<entry>Error correction code byte 2 of the second 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x2E</entry>
+<entry>ECC byte 6</entry>
+<entry>Error correction code byte 0 of the third 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x2F</entry>
+<entry>ECC byte 7</entry>
+<entry>Error correction code byte 1 of the third 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x30</entry>
+<entry>ECC byte 8</entry>
+<entry>Error correction code byte 2 of the third 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x31</entry>
+<entry>ECC byte 9</entry>
+<entry>Error correction code byte 0 of the fourth 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x32</entry>
+<entry>ECC byte 10</entry>
+<entry>Error correction code byte 1 of the fourth 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x33</entry>
+<entry>ECC byte 11</entry>
+<entry>Error correction code byte 2 of the fourth 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x34</entry>
+<entry>ECC byte 12</entry>
+<entry>Error correction code byte 0 of the fifth 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x35</entry>
+<entry>ECC byte 13</entry>
+<entry>Error correction code byte 1 of the fifth 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x36</entry>
+<entry>ECC byte 14</entry>
+<entry>Error correction code byte 2 of the fifth 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x37</entry>
+<entry>ECC byte 15</entry>
+<entry>Error correction code byte 0 of the sixt 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x38</entry>
+<entry>ECC byte 16</entry>
+<entry>Error correction code byte 1 of the sixt 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x39</entry>
+<entry>ECC byte 17</entry>
+<entry>Error correction code byte 2 of the sixt 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x3A</entry>
+<entry>ECC byte 18</entry>
+<entry>Error correction code byte 0 of the seventh 256 Bytes of
+data in this page</entry>
+</row>
+<row>
+<entry>0x3B</entry>
+<entry>ECC byte 19</entry>
+<entry>Error correction code byte 1 of the seventh 256 Bytes of
+data in this page</entry>
+</row>
+<row>
+<entry>0x3C</entry>
+<entry>ECC byte 20</entry>
+<entry>Error correction code byte 2 of the seventh 256 Bytes of
+data in this page</entry>
+</row>
+<row>
+<entry>0x3D</entry>
+<entry>ECC byte 21</entry>
+<entry>Error correction code byte 0 of the eigth 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x3E</entry>
+<entry>ECC byte 22</entry>
+<entry>Error correction code byte 1 of the eigth 256 Bytes of data
+in this page</entry>
+</row>
+<row>
+<entry>0x3F</entry>
+<entry>ECC byte 23</entry>
+<entry>Error correction code byte 2 of the eigth 256 Bytes of data
+in this page</entry>
+</row>
+</tbody></tgroup></informaltable>
+               </sect2>
+       </sect1>
+  </chapter>
+
+  <chapter id="filesystems">
+       <title>Filesystem support</title>
+       <para>
+               The NAND driver provides all neccecary functions for a
+               filesystem via the MTD interface.
+       </para>
+       <para>
+               Filesystems must be aware of the NAND pecularities and
+               restrictions. One major restrictions of NAND Flash is, that you cannot 
+               write as often as you want to a page. The consecutive writes to a page, 
+               before erasing it again, are restricted to 1-3 writes, depending on the 
+               manufacturers specifications. This applies similar to the spare area. 
+       </para>
+       <para>
+               Therefor NAND aware filesystems must either write in page size chunks
+               or hold a writebuffer to collect smaller writes until they sum up to 
+               pagesize. Available NAND aware filesystems: JFFS2, YAFFS.               
+       </para>
+       <para>
+               The spare area usage to store filesystem data is controlled by
+               the spare area placement functionality which is described in one
+               of the earlier chapters.
+       </para>
+  </chapter>   
+  <chapter id="tools">
+       <title>Tools</title>
+       <para>
+               The MTD project provides a couple of helpful tools to handle NAND Flash.
+               <itemizedlist>
+               <listitem><para>flasherase, flasheraseall: Erase and format FLASH partitions</para></listitem>
+               <listitem><para>nandwrite: write filesystem images to NAND FLASH</para></listitem>
+               <listitem><para>nanddump: dump the contents of a NAND FLASH partitions</para></listitem>
+               </itemizedlist>
+       </para>
+       <para>
+               These tools are aware of the NAND restrictions. Please use those tools
+               instead of complaining about errors which are caused by non NAND aware
+               access methods.
+       </para>
+  </chapter>   
+
+  <chapter id="defines">
+     <title>Constants</title>
+     <para>
+     This chapter describes the constants which might be relevant for a driver developer.
+     </para>
+     <sect1>   
+       <title>Chip option constants</title>
+       <sect2>   
+               <title>Constants for chip id table</title>
+               <para>
+               These constants are defined in nand.h. They are ored together to describe
+               the chip functionality.
+               <programlisting>
+/* Chip can not auto increment pages */
+#define NAND_NO_AUTOINCR       0x00000001
+/* Buswitdh is 16 bit */
+#define NAND_BUSWIDTH_16       0x00000002
+/* Device supports partial programming without padding */
+#define NAND_NO_PADDING                0x00000004
+/* Chip has cache program function */
+#define NAND_CACHEPRG          0x00000008
+/* Chip has copy back function */
+#define NAND_COPYBACK          0x00000010
+/* AND Chip which has 4 banks and a confusing page / block 
+ * assignment. See Renesas datasheet for further information */
+#define NAND_IS_AND            0x00000020
+/* Chip has a array of 4 pages which can be read without
+ * additional ready /busy waits */
+#define NAND_4PAGE_ARRAY       0x00000040 
+               </programlisting>
+               </para>
+       </sect2>
+       <sect2>   
+               <title>Constants for runtime options</title>
+               <para>
+               These constants are defined in nand.h. They are ored together to describe
+               the functionality.
+               <programlisting>
+/* Use a flash based bad block table. This option is parsed by the
+ * default bad block table function (nand_default_bbt). */
+#define NAND_USE_FLASH_BBT     0x00010000
+/* The hw ecc generator provides a syndrome instead a ecc value on read 
+ * This can only work if we have the ecc bytes directly behind the 
+ * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
+#define NAND_HWECC_SYNDROME    0x00020000
+               </programlisting>
+               </para>
+       </sect2>
+     </sect1>  
+
+     <sect1>   
+       <title>ECC selection constants</title>
+       <para>
+       Use these constants to select the ECC algorithm.
+       <programlisting>
+/* No ECC. Usage is not recommended ! */
+#define NAND_ECC_NONE          0
+/* Software ECC 3 byte ECC per 256 Byte data */
+#define NAND_ECC_SOFT          1
+/* Hardware ECC 3 byte ECC per 256 Byte data */
+#define NAND_ECC_HW3_256       2
+/* Hardware ECC 3 byte ECC per 512 Byte data */
+#define NAND_ECC_HW3_512       3
+/* Hardware ECC 6 byte ECC per 512 Byte data */
+#define NAND_ECC_HW6_512       4
+/* Hardware ECC 6 byte ECC per 512 Byte data */
+#define NAND_ECC_HW8_512       6
+       </programlisting>
+       </para>
+     </sect1>  
+
+     <sect1>   
+       <title>Hardware control related constants</title>
+       <para>
+       These constants describe the requested hardware access function when
+       the boardspecific hardware control function is called
+       <programlisting>
+/* Select the chip by setting nCE to low */
+#define NAND_CTL_SETNCE        1
+/* Deselect the chip by setting nCE to high */
+#define NAND_CTL_CLRNCE                2
+/* Select the command latch by setting CLE to high */
+#define NAND_CTL_SETCLE                3
+/* Deselect the command latch by setting CLE to low */
+#define NAND_CTL_CLRCLE                4
+/* Select the address latch by setting ALE to high */
+#define NAND_CTL_SETALE                5
+/* Deselect the address latch by setting ALE to low */
+#define NAND_CTL_CLRALE                6
+/* Set write protection by setting WP to high. Not used! */
+#define NAND_CTL_SETWP         7
+/* Clear write protection by setting WP to low. Not used! */
+#define NAND_CTL_CLRWP         8
+       </programlisting>
+       </para>
+     </sect1>  
+
+     <sect1>   
+       <title>Bad block table related constants</title>
+       <para>
+       These constants describe the options used for bad block
+       table descriptors.
+       <programlisting>
+/* Options for the bad block table descriptors */
+
+/* The number of bits used per block in the bbt on the device */
+#define NAND_BBT_NRBITS_MSK    0x0000000F
+#define NAND_BBT_1BIT          0x00000001
+#define NAND_BBT_2BIT          0x00000002
+#define NAND_BBT_4BIT          0x00000004
+#define NAND_BBT_8BIT          0x00000008
+/* The bad block table is in the last good block of the device */
+#define        NAND_BBT_LASTBLOCK      0x00000010
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_ABSPAGE       0x00000020
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_SEARCH                0x00000040
+/* bbt is stored per chip on multichip devices */
+#define NAND_BBT_PERCHIP       0x00000080
+/* bbt has a version counter at offset veroffs */
+#define NAND_BBT_VERSION       0x00000100
+/* Create a bbt if none axists */
+#define NAND_BBT_CREATE                0x00000200
+/* Search good / bad pattern through all pages of a block */
+#define NAND_BBT_SCANALLPAGES  0x00000400
+/* Scan block empty during good / bad block scan */
+#define NAND_BBT_SCANEMPTY     0x00000800
+/* Write bbt if neccecary */
+#define NAND_BBT_WRITE         0x00001000
+/* Read and write back block contents when writing bbt */
+#define NAND_BBT_SAVECONTENT   0x00002000
+       </programlisting>
+       </para>
+     </sect1>  
+
+  </chapter>
+       
+  <chapter id="structs">
+     <title>Structures</title>
+     <para>
+     This chapter contains the autogenerated documentation of the structures which are
+     used in the NAND driver and might be relevant for a driver developer. Each  
+     struct member has a short description which is marked with an [XXX] identifier.
+     See the chapter "Documentation hints" for an explanation.
+     </para>
+!Iinclude/linux/mtd/nand.h
+  </chapter>
+
+  <chapter id="pubfunctions">
+     <title>Public Functions Provided</title>
+     <para>
+     This chapter contains the autogenerated documentation of the NAND kernel API functions
+      which are exported. Each function has a short description which is marked with an [XXX] identifier.
+     See the chapter "Documentation hints" for an explanation.
+     </para>
+!Edrivers/mtd/nand/nand_base.c
+!Edrivers/mtd/nand/nand_bbt.c
+!Edrivers/mtd/nand/nand_ecc.c
+  </chapter>
+  
+  <chapter id="intfunctions">
+     <title>Internal Functions Provided</title>
+     <para>
+     This chapter contains the autogenerated documentation of the NAND driver internal functions.
+     Each function has a short description which is marked with an [XXX] identifier.
+     See the chapter "Documentation hints" for an explanation.
+     The functions marked with [DEFAULT] might be relevant for a board driver developer.
+     </para>
+!Idrivers/mtd/nand/nand_base.c
+!Idrivers/mtd/nand/nand_bbt.c
+!Idrivers/mtd/nand/nand_ecc.c
+  </chapter>
+
+  <chapter id="credits">
+     <title>Credits</title>
+       <para>
+               The following people have contributed to the NAND driver:
+               <orderedlist>
+                       <listitem><para>Steven J. Hill<email>sjhill@realitydiluted.com</email></para></listitem>
+                       <listitem><para>David Woodhouse<email>dwmw2@infradead.org</email></para></listitem>
+                       <listitem><para>Thomas Gleixner<email>tglx@linutronix.de</email></para></listitem>
+               </orderedlist>
+               A lot of users have provided bugfixes, improvements and helping hands for testing.
+               Thanks a lot.
+       </para>
+       <para>
+               The following people have contributed to this document:
+               <orderedlist>
+                       <listitem><para>Thomas Gleixner<email>tglx@linutronix.de</email></para></listitem>
+               </orderedlist>
+       </para>
+  </chapter>
+</book>
diff --git a/Documentation/PCIEBUS-HOWTO.txt b/Documentation/PCIEBUS-HOWTO.txt
new file mode 100644 (file)
index 0000000..c93f42a
--- /dev/null
@@ -0,0 +1,217 @@
+               The PCI Express Port Bus Driver Guide HOWTO
+       Tom L Nguyen tom.l.nguyen@intel.com
+                       11/03/2004
+
+1. About this guide
+
+This guide describes the basics of the PCI Express Port Bus driver
+and provides information on how to enable the service drivers to
+register/unregister with the PCI Express Port Bus Driver.
+
+2. Copyright 2004 Intel Corporation
+
+3. What is the PCI Express Port Bus Driver
+
+A PCI Express Port is a logical PCI-PCI Bridge structure. There
+are two types of PCI Express Port: the Root Port and the Switch
+Port. The Root Port originates a PCI Express link from a PCI Express
+Root Complex and the Switch Port connects PCI Express links to
+internal logical PCI buses. The Switch Port, which has its secondary
+bus representing the switch's internal routing logic, is called the
+switch's Upstream Port. The switch's Downstream Port is bridging from
+switch's internal routing bus to a bus representing the downstream
+PCI Express link from the PCI Express Switch.
+
+A PCI Express Port can provide up to four distinct functions,
+referred to in this document as services, depending on its port type.
+PCI Express Port's services include native hotplug support (HP),
+power management event support (PME), advanced error reporting
+support (AER), and virtual channel support (VC). These services may
+be handled by a single complex driver or be individually distributed
+and handled by corresponding service drivers.
+
+4. Why use the PCI Express Port Bus Driver?
+
+In existing Linux kernels, the Linux Device Driver Model allows a
+physical device to be handled by only a single driver. The PCI
+Express Port is a PCI-PCI Bridge device with multiple distinct
+services. To maintain a clean and simple solution each service
+may have its own software service driver. In this case several
+service drivers will compete for a single PCI-PCI Bridge device.
+For example, if the PCI Express Root Port native hotplug service
+driver is loaded first, it claims a PCI-PCI Bridge Root Port. The
+kernel therefore does not load other service drivers for that Root
+Port. In other words, it is impossible to have multiple service
+drivers load and run on a PCI-PCI Bridge device simultaneously
+using the current driver model.
+
+To enable multiple service drivers running simultaneously requires
+having a PCI Express Port Bus driver, which manages all populated
+PCI Express Ports and distributes all provided service requests
+to the corresponding service drivers as required. Some key
+advantages of using the PCI Express Port Bus driver are listed below:
+
+       - Allow multiple service drivers to run simultaneously on
+         a PCI-PCI Bridge Port device.
+
+       - Allow service drivers implemented in an independent
+         staged approach.
+       
+       - Allow one service driver to run on multiple PCI-PCI Bridge
+         Port devices. 
+
+       - Manage and distribute resources of a PCI-PCI Bridge Port
+         device to requested service drivers.
+
+5. Configuring the PCI Express Port Bus Driver vs. Service Drivers
+
+5.1 Including the PCI Express Port Bus Driver Support into the Kernel
+
+Including the PCI Express Port Bus driver depends on whether the PCI
+Express support is included in the kernel config. The kernel will
+automatically include the PCI Express Port Bus driver as a kernel
+driver when the PCI Express support is enabled in the kernel.
+
+5.2 Enabling Service Driver Support
+
+PCI device drivers are implemented based on Linux Device Driver Model.
+All service drivers are PCI device drivers. As discussed above, it is
+impossible to load any service driver once the kernel has loaded the
+PCI Express Port Bus Driver. To meet the PCI Express Port Bus Driver
+Model requires some minimal changes on existing service drivers that
+imposes no impact on the functionality of existing service drivers.
+
+A service driver is required to use the two APIs shown below to
+register its service with the PCI Express Port Bus driver (see 
+section 5.2.1 & 5.2.2). It is important that a service driver
+initializes the pcie_port_service_driver data structure, included in
+header file /include/linux/pcieport_if.h, before calling these APIs.
+Failure to do so will result an identity mismatch, which prevents
+the PCI Express Port Bus driver from loading a service driver.
+
+5.2.1 pcie_port_service_register
+
+int pcie_port_service_register(struct pcie_port_service_driver *new)
+
+This API replaces the Linux Driver Model's pci_module_init API. A
+service driver should always calls pcie_port_service_register at
+module init. Note that after service driver being loaded, calls
+such as pci_enable_device(dev) and pci_set_master(dev) are no longer
+necessary since these calls are executed by the PCI Port Bus driver.
+
+5.2.2 pcie_port_service_unregister
+
+void pcie_port_service_unregister(struct pcie_port_service_driver *new)
+
+pcie_port_service_unregister replaces the Linux Driver Model's
+pci_unregister_driver. It's always called by service driver when a
+module exits.
+
+5.2.3 Sample Code
+
+Below is sample service driver code to initialize the port service
+driver data structure.
+
+static struct pcie_port_service_id service_id[] = { {
+       .vendor = PCI_ANY_ID,
+       .device = PCI_ANY_ID,
+       .port_type = PCIE_RC_PORT,
+       .service_type = PCIE_PORT_SERVICE_AER,
+       }, { /* end: all zeroes */ }
+};
+
+static struct pcie_port_service_driver root_aerdrv = {
+       .name           = (char *)device_name,
+       .id_table       = &service_id[0],
+
+       .probe          = aerdrv_load,
+       .remove         = aerdrv_unload,
+
+       .suspend        = aerdrv_suspend,
+       .resume         = aerdrv_resume,
+};
+
+Below is a sample code for registering/unregistering a service
+driver.
+
+static int __init aerdrv_service_init(void)
+{
+       int retval = 0;
+       
+       retval = pcie_port_service_register(&root_aerdrv);
+       if (!retval) {
+               /*
+                * FIX ME
+                */
+       }
+       return retval;
+}
+
+static void __exit aerdrv_service_exit(void) 
+{
+       pcie_port_service_unregister(&root_aerdrv);
+}
+
+module_init(aerdrv_service_init);
+module_exit(aerdrv_service_exit);
+
+6. Possible Resource Conflicts
+
+Since all service drivers of a PCI-PCI Bridge Port device are
+allowed to run simultaneously, below lists a few of possible resource
+conflicts with proposed solutions.
+
+6.1 MSI Vector Resource
+
+The MSI capability structure enables a device software driver to call
+pci_enable_msi to request MSI based interrupts. Once MSI interrupts
+are enabled on a device, it stays in this mode until a device driver
+calls pci_disable_msi to disable MSI interrupts and revert back to
+INTx emulation mode. Since service drivers of the same PCI-PCI Bridge
+port share the same physical device, if an individual service driver
+calls pci_enable_msi/pci_disable_msi it may result unpredictable
+behavior. For example, two service drivers run simultaneously on the
+same physical Root Port. Both service drivers call pci_enable_msi to
+request MSI based interrupts. A service driver may not know whether
+any other service drivers have run on this Root Port. If either one
+of them calls pci_disable_msi, it puts the other service driver
+in a wrong interrupt mode. 
+
+To avoid this situation all service drivers are not permitted to
+switch interrupt mode on its device. The PCI Express Port Bus driver
+is responsible for determining the interrupt mode and this should be
+transparent to service drivers. Service drivers need to know only
+the vector IRQ assigned to the field irq of struct pcie_device, which
+is passed in when the PCI Express Port Bus driver probes each service
+driver. Service drivers should use (struct pcie_device*)dev->irq to
+call request_irq/free_irq. In addition, the interrupt mode is stored
+in the field interrupt_mode of struct pcie_device.
+
+6.2 MSI-X Vector Resources
+
+Similar to the MSI a device driver for an MSI-X capable device can
+call pci_enable_msix to request MSI-X interrupts. All service drivers
+are not permitted to switch interrupt mode on its device. The PCI
+Express Port Bus driver is responsible for determining the interrupt
+mode and this should be transparent to service drivers. Any attempt
+by service driver to call pci_enable_msix/pci_disable_msix may
+result unpredictable behavior. Service drivers should use
+(struct pcie_device*)dev->irq and call request_irq/free_irq.
+
+6.3 PCI Memory/IO Mapped Regions
+
+Service drivers for PCI Express Power Management (PME), Advanced
+Error Reporting (AER), Hot-Plug (HP) and Virtual Channel (VC) access
+PCI configuration space on the PCI Express port. In all cases the
+registers accessed are independent of each other. This patch assumes
+that all service drivers will be well behaved and not overwrite
+other service driver's configuration settings.
+
+6.4 PCI Config Registers
+
+Each service driver runs its PCI config operations on its own
+capability structure except the PCI Express capability structure, in
+which Root Control register and Device Control register are shared
+between PME and AER. This patch assumes that all service drivers
+will be well behaved and not overwrite other service driver's
+configuration settings.
diff --git a/Documentation/README.cycladesZ b/Documentation/README.cycladesZ
new file mode 100644 (file)
index 0000000..024a694
--- /dev/null
@@ -0,0 +1,8 @@
+
+The Cyclades-Z must have firmware loaded onto the card before it will
+operate.  This operation should be performed during system startup,
+
+The firmware, loader program and the latest device driver code are
+available from Cyclades at
+    ftp://ftp.cyclades.com/pub/cyclades/cyclades-z/linux/
+
diff --git a/Documentation/SecurityBugs b/Documentation/SecurityBugs
new file mode 100644 (file)
index 0000000..26c3b36
--- /dev/null
@@ -0,0 +1,38 @@
+Linux kernel developers take security very seriously.  As such, we'd
+like to know when a security bug is found so that it can be fixed and
+disclosed as quickly as possible.  Please report security bugs to the
+Linux kernel security team.
+
+1) Contact
+
+The Linux kernel security team can be contacted by email at
+<security@kernel.org>.  This is a private list of security officers
+who will help verify the bug report and develop and release a fix.
+It is possible that the security team will bring in extra help from
+area maintainers to understand and fix the security vulnerability.
+
+As it is with any bug, the more information provided the easier it
+will be to diagnose and fix.  Please review the procedure outlined in
+REPORTING-BUGS if you are unclear about what information is helpful.
+Any exploit code is very helpful and will not be released without
+consent from the reporter unless it has already been made public.
+
+2) Disclosure
+
+The goal of the Linux kernel security team is to work with the
+bug submitter to bug resolution as well as disclosure.  We prefer
+to fully disclose the bug as soon as possible.  It is reasonable to
+delay disclosure when the bug or the fix is not yet fully understood,
+the solution is not well-tested or for vendor coordination.  However, we
+expect these delays to be short, measurable in days, not weeks or months.
+A disclosure date is negotiated by the security team working with the
+bug submitter as well as vendors.  However, the kernel security team
+holds the final say when setting a disclosure date.  The timeframe for
+disclosure is from immediate (esp. if it's already publically known)
+to a few weeks.  As a basic default policy, we expect report date to
+disclosure date to be on the order of 7 days.
+
+3) Non-disclosure agreements
+
+The Linux kernel security team is not a formal body and therefore unable
+to enter any non-disclosure agreements.
diff --git a/Documentation/aoe/aoe.txt b/Documentation/aoe/aoe.txt
new file mode 100644 (file)
index 0000000..7af899f
--- /dev/null
@@ -0,0 +1,84 @@
+The EtherDrive (R) HOWTO for users of 2.6 kernels is found at ...
+
+  http://www.coraid.com/support/linux/EtherDrive-2.6-HOWTO.html
+
+  It has many tips and hints!
+
+CREATING DEVICE NODES
+
+  Users of udev should find device nodes created automatically.  Two
+  scripts are provided in Documentation/aoe as examples of static
+  device node creation for using the aoe driver.
+
+    rm -rf /dev/etherd
+    sh Documentation/aoe/mkdevs.sh /dev/etherd
+
+  ... or to make just one shelf's worth of block device nodes ...
+
+    sh Documentation/aoe/mkshelf.sh /dev/etherd 0
+
+  There is also an autoload script that shows how to edit
+  /etc/modprobe.conf to ensure that the aoe module is loaded when
+  necessary.
+
+USING DEVICE NODES
+
+  "cat /dev/etherd/err" blocks, waiting for error diagnostic output,
+  like any retransmitted packets.
+
+  "echo eth2 eth4 > /dev/etherd/interfaces" tells the aoe driver to
+  limit ATA over Ethernet traffic to eth2 and eth4.  AoE traffic from
+  untrusted networks should be ignored as a matter of security.
+
+  "echo > /dev/etherd/discover" tells the driver to find out what AoE
+  devices are available.
+
+  These character devices may disappear and be replaced by sysfs
+  counterparts, so distribution maintainers are encouraged to create
+  scripts that use these devices.
+
+  The block devices are named like this:
+
+       e{shelf}.{slot}
+       e{shelf}.{slot}p{part}
+
+  ... so that "e0.2" is the third blade from the left (slot 2) in the
+  first shelf (shelf address zero).  That's the whole disk.  The first
+  partition on that disk would be "e0.2p1".
+
+USING SYSFS
+
+  Each aoe block device in /sys/block has the extra attributes of
+  state, mac, and netif.  The state attribute is "up" when the device
+  is ready for I/O and "down" if detected but unusable.  The
+  "down,closewait" state shows that the device is still open and
+  cannot come up again until it has been closed.
+
+  The mac attribute is the ethernet address of the remote AoE device.
+  The netif attribute is the network interface on the localhost
+  through which we are communicating with the remote AoE device.
+
+  There is a script in this directory that formats this information
+  in a convenient way.
+
+  root@makki root# sh Documentation/aoe/status.sh 
+     e10.0            eth3              up
+     e10.1            eth3              up
+     e10.2            eth3              up
+     e10.3            eth3              up
+     e10.4            eth3              up
+     e10.5            eth3              up
+     e10.6            eth3              up
+     e10.7            eth3              up
+     e10.8            eth3              up
+     e10.9            eth3              up
+      e4.0            eth1              up
+      e4.1            eth1              up
+      e4.2            eth1              up
+      e4.3            eth1              up
+      e4.4            eth1              up
+      e4.5            eth1              up
+      e4.6            eth1              up
+      e4.7            eth1              up
+      e4.8            eth1              up
+      e4.9            eth1              up
diff --git a/Documentation/aoe/autoload.sh b/Documentation/aoe/autoload.sh
new file mode 100644 (file)
index 0000000..78dad13
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+# set aoe to autoload by installing the
+# aliases in /etc/modprobe.conf
+
+f=/etc/modprobe.conf
+
+if test ! -r $f || test ! -w $f; then
+       echo "cannot configure $f for module autoloading" 1>&2
+       exit 1
+fi
+
+grep major-152 $f >/dev/null
+if [ $? = 1 ]; then
+       echo alias block-major-152 aoe >> $f
+       echo alias char-major-152 aoe >> $f
+fi
+
diff --git a/Documentation/aoe/mkdevs.sh b/Documentation/aoe/mkdevs.sh
new file mode 100644 (file)
index 0000000..6ce7070
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+n_shelves=${n_shelves:-10}
+n_partitions=${n_partitions:-16}
+
+if test "$#" != "1"; then
+       echo "Usage: sh `basename $0` {dir}" 1>&2
+       exit 1
+fi
+dir=$1
+
+MAJOR=152
+
+echo "Creating AoE devnode files in $dir ..."
+
+set -e
+
+mkdir -p $dir
+
+# (Status info is in sysfs.  See status.sh.)
+# rm -f $dir/stat
+# mknod -m 0400 $dir/stat c $MAJOR 1
+rm -f $dir/err
+mknod -m 0400 $dir/err c $MAJOR 2
+rm -f $dir/discover
+mknod -m 0200 $dir/discover c $MAJOR 3
+rm -f $dir/interfaces
+mknod -m 0200 $dir/interfaces c $MAJOR 4
+
+export n_partitions
+mkshelf=`echo $0 | sed 's!mkdevs!mkshelf!'`
+i=0
+while test $i -lt $n_shelves; do
+       sh -xc "sh $mkshelf $dir $i"
+       i=`expr $i + 1`
+done
diff --git a/Documentation/aoe/mkshelf.sh b/Documentation/aoe/mkshelf.sh
new file mode 100644 (file)
index 0000000..4093283
--- /dev/null
@@ -0,0 +1,25 @@
+#! /bin/sh
+
+if test "$#" != "2"; then
+       echo "Usage: sh `basename $0` {dir} {shelfaddress}" 1>&2
+       exit 1
+fi
+n_partitions=${n_partitions:-16}
+dir=$1
+shelf=$2
+MAJOR=152
+
+set -e
+
+minor=`echo 10 \* $shelf \* $n_partitions | bc`
+endp=`echo $n_partitions - 1 | bc`
+for slot in `seq 0 9`; do
+       for part in `seq 0 $endp`; do
+               name=e$shelf.$slot
+               test "$part" != "0" && name=${name}p$part
+               rm -f $dir/$name
+               mknod -m 0660 $dir/$name b $MAJOR $minor
+
+               minor=`expr $minor + 1`
+       done
+done
diff --git a/Documentation/aoe/status.sh b/Documentation/aoe/status.sh
new file mode 100644 (file)
index 0000000..8934ccc
--- /dev/null
@@ -0,0 +1,28 @@
+#! /bin/sh
+# collate and present sysfs information about AoE storage
+
+set -e
+format="%8s\t%8s\t%8s\n"
+me=`basename $0`
+
+# printf "$format" device mac netif state
+
+test -z "`mount | grep sysfs`" && {
+       echo "$me Error: sysfs is not mounted" 1>&2
+       exit 1
+}
+test -z "`lsmod | grep '^aoe'`" && {
+       echo  "$me Error: aoe module is not loaded" 1>&2
+       exit 1
+}
+
+for d in `ls -d /sys/block/etherd* 2>/dev/null | grep -v p` end; do
+       # maybe ls comes up empty, so we use "end"
+       test $d = end && continue
+
+       dev=`echo "$d" | sed 's/.*!//'`
+       printf "$format" \
+               "$dev" \
+               "`cat \"$d/netif\"`" \
+               "`cat \"$d/state\"`"
+done | sort
diff --git a/Documentation/arm/Samsung-S3C24XX/Suspend.txt b/Documentation/arm/Samsung-S3C24XX/Suspend.txt
new file mode 100644 (file)
index 0000000..e12bc32
--- /dev/null
@@ -0,0 +1,106 @@
+                       S3C24XX Suspend Support
+                       =======================
+
+
+Introduction
+------------
+
+  The S3C2410 supports a low-power suspend mode, where the SDRAM is kept
+  in Self-Refresh mode, and all but the essential peripheral blocks are
+  powered down. For more information on how this works, please look
+  at the S3C2410 datasheets from Samsung.
+
+
+Requirements
+------------
+
+  1) A bootloader that can support the necessary resume operation
+
+  2) Support for at least 1 source for resume
+
+  3) CONFIG_PM enabled in the kernel
+
+  4) Any peripherals that are going to be powered down at the same
+     time require suspend/resume support.
+
+
+Resuming
+--------
+
+  The S3C2410 user manual defines the process of sending the CPU to
+  sleep and how it resumes. The default behaviour of the Linux code
+  is to set the GSTATUS3 register to the physical address of the
+  code to resume Linux operation.
+
+  GSTATUS4 is currently left alone by the sleep code, and is free to
+  use for any other purposes (for example, the EB2410ITX uses this to
+  save memory configuration in).
+
+
+Machine Support
+---------------
+
+  The machine specific functions must call the s3c2410_pm_init() function
+  to say that its bootloader is capable of resuming. This can be as
+  simple as adding the following to the machine's definition:
+
+  INITMACHINE(s3c2410_pm_init)
+
+  A board can do its own setup before calling s3c2410_pm_init, if it
+  needs to setup anything else for power management support.
+
+  There is currently no support for over-riding the default method of
+  saving the resume address, if your board requires it, then contact
+  the maintainer and discuss what is required.
+
+  Note, the original method of adding an late_initcall() is wrong,
+  and will end up initialising all compiled machines' pm init!
+
+
+Debugging
+---------
+
+  There are several important things to remember when using PM suspend:
+
+  1) The uart drivers will disable the clocks to the UART blocks when
+     suspending, which means that use of printascii() or similar direct
+     access to the UARTs will cause the debug to stop.
+
+  2) Whilst the pm code itself will attempt to re-enable the UART clocks,
+     care should be taken that any external clock sources that the UARTs
+     rely on are still enabled at that point.
+
+
+Configuration
+-------------
+
+  The S3C2410 specific configuration in `System Type` defines various
+  aspects of how the S3C2410 suspend and resume support is configured
+
+  `S3C2410 PM Suspend debug`
+
+    This option prints messages to the serial console before and after
+    the actual suspend, giving detailed information on what is
+    happening
+
+
+  `S3C2410 PM Suspend Memory CRC`
+
+    Allows the entire memory to be checksummed before and after the
+    suspend to see if there has been any corruption of the contents.
+
+    This support requires the CRC32 function to be enabled.
+
+
+  `S3C2410 PM Suspend CRC Chunksize (KiB)`
+
+    Defines the size of memory each CRC chunk covers. A smaller value
+    will mean that the CRC data block will take more memory, but will
+    identify any faults with better precision
+
+
+Document Author
+---------------
+
+Ben Dooks, (c) 2004 Simtec Electronics
+
diff --git a/Documentation/atomic_ops.txt b/Documentation/atomic_ops.txt
new file mode 100644 (file)
index 0000000..8eedaa2
--- /dev/null
@@ -0,0 +1,456 @@
+               Semantics and Behavior of Atomic and
+                        Bitmask Operations
+
+                         David S. Miller        
+
+       This document is intended to serve as a guide to Linux port
+maintainers on how to implement atomic counter, bitops, and spinlock
+interfaces properly.
+
+       The atomic_t type should be defined as a signed integer.
+Also, it should be made opaque such that any kind of cast to a normal
+C integer type will fail.  Something like the following should
+suffice:
+
+       typedef struct { volatile int counter; } atomic_t;
+
+       The first operations to implement for atomic_t's are the
+initializers and plain reads.
+
+       #define ATOMIC_INIT(i)          { (i) }
+       #define atomic_set(v, i)        ((v)->counter = (i))
+
+The first macro is used in definitions, such as:
+
+static atomic_t my_counter = ATOMIC_INIT(1);
+
+The second interface can be used at runtime, as in:
+
+       struct foo { atomic_t counter; };
+       ...
+
+       struct foo *k;
+
+       k = kmalloc(sizeof(*k), GFP_KERNEL);
+       if (!k)
+               return -ENOMEM;
+       atomic_set(&k->counter, 0);
+
+Next, we have:
+
+       #define atomic_read(v)  ((v)->counter)
+
+which simply reads the current value of the counter.
+
+Now, we move onto the actual atomic operation interfaces.
+
+       void atomic_add(int i, atomic_t *v);
+       void atomic_sub(int i, atomic_t *v);
+       void atomic_inc(atomic_t *v);
+       void atomic_dec(atomic_t *v);
+
+These four routines add and subtract integral values to/from the given
+atomic_t value.  The first two routines pass explicit integers by
+which to make the adjustment, whereas the latter two use an implicit
+adjustment value of "1".
+
+One very important aspect of these two routines is that they DO NOT
+require any explicit memory barriers.  They need only perform the
+atomic_t counter update in an SMP safe manner.
+
+Next, we have:
+
+       int atomic_inc_return(atomic_t *v);
+       int atomic_dec_return(atomic_t *v);
+
+These routines add 1 and subtract 1, respectively, from the given
+atomic_t and return the new counter value after the operation is
+performed.
+
+Unlike the above routines, it is required that explicit memory
+barriers are performed before and after the operation.  It must be
+done such that all memory operations before and after the atomic
+operation calls are strongly ordered with respect to the atomic
+operation itself.
+
+For example, it should behave as if a smp_mb() call existed both
+before and after the atomic operation.
+
+If the atomic instructions used in an implementation provide explicit
+memory barrier semantics which satisfy the above requirements, that is
+fine as well.
+
+Let's move on:
+
+       int atomic_add_return(int i, atomic_t *v);
+       int atomic_sub_return(int i, atomic_t *v);
+
+These behave just like atomic_{inc,dec}_return() except that an
+explicit counter adjustment is given instead of the implicit "1".
+This means that like atomic_{inc,dec}_return(), the memory barrier
+semantics are required.
+
+Next:
+
+       int atomic_inc_and_test(atomic_t *v);
+       int atomic_dec_and_test(atomic_t *v);
+
+These two routines increment and decrement by 1, respectively, the
+given atomic counter.  They return a boolean indicating whether the
+resulting counter value was zero or not.
+
+It requires explicit memory barrier semantics around the operation as
+above.
+
+       int atomic_sub_and_test(int i, atomic_t *v);
+
+This is identical to atomic_dec_and_test() except that an explicit
+decrement is given instead of the implicit "1".  It requires explicit
+memory barrier semantics around the operation.
+
+       int atomic_add_negative(int i, atomic_t *v);
+
+The given increment is added to the given atomic counter value.  A
+boolean is return which indicates whether the resulting counter value
+is negative.  It requires explicit memory barrier semantics around the
+operation.
+
+If a caller requires memory barrier semantics around an atomic_t
+operation which does not return a value, a set of interfaces are
+defined which accomplish this:
+
+       void smp_mb__before_atomic_dec(void);
+       void smp_mb__after_atomic_dec(void);
+       void smp_mb__before_atomic_inc(void);
+       void smp_mb__after_atomic_dec(void);
+
+For example, smp_mb__before_atomic_dec() can be used like so:
+
+       obj->dead = 1;
+       smp_mb__before_atomic_dec();
+       atomic_dec(&obj->ref_count);
+
+It makes sure that all memory operations preceeding the atomic_dec()
+call are strongly ordered with respect to the atomic counter
+operation.  In the above example, it guarentees that the assignment of
+"1" to obj->dead will be globally visible to other cpus before the
+atomic counter decrement.
+
+Without the explicitl smp_mb__before_atomic_dec() call, the
+implementation could legally allow the atomic counter update visible
+to other cpus before the "obj->dead = 1;" assignment.
+
+The other three interfaces listed are used to provide explicit
+ordering with respect to memory operations after an atomic_dec() call
+(smp_mb__after_atomic_dec()) and around atomic_inc() calls
+(smp_mb__{before,after}_atomic_inc()).
+
+A missing memory barrier in the cases where they are required by the
+atomic_t implementation above can have disasterous results.  Here is
+an example, which follows a pattern occuring frequently in the Linux
+kernel.  It is the use of atomic counters to implement reference
+counting, and it works such that once the counter falls to zero it can
+be guarenteed that no other entity can be accessing the object:
+
+static void obj_list_add(struct obj *obj)
+{
+       obj->active = 1;
+       list_add(&obj->list);
+}
+
+static void obj_list_del(struct obj *obj)
+{
+       list_del(&obj->list);
+       obj->active = 0;
+}
+
+static void obj_destroy(struct obj *obj)
+{
+       BUG_ON(obj->active);
+       kfree(obj);
+}
+
+struct obj *obj_list_peek(struct list_head *head)
+{
+       if (!list_empty(head)) {
+               struct obj *obj;
+
+               obj = list_entry(head->next, struct obj, list);
+               atomic_inc(&obj->refcnt);
+               return obj;
+       }
+       return NULL;
+}
+
+void obj_poke(void)
+{
+       struct obj *obj;
+
+       spin_lock(&global_list_lock);
+       obj = obj_list_peek(&global_list);
+       spin_unlock(&global_list_lock);
+
+       if (obj) {
+               obj->ops->poke(obj);
+               if (atomic_dec_and_test(&obj->refcnt))
+                       obj_destroy(obj);
+       }
+}
+
+void obj_timeout(struct obj *obj)
+{
+       spin_lock(&global_list_lock);
+       obj_list_del(obj);
+       spin_unlock(&global_list_lock);
+
+       if (atomic_dec_and_test(&obj->refcnt))
+               obj_destroy(obj);
+}
+
+(This is a simplification of the ARP queue management in the
+ generic neighbour discover code of the networking.  Olaf Kirch
+ found a bug wrt. memory barriers in kfree_skb() that exposed
+ the atomic_t memory barrier requirements quite clearly.)
+
+Given the above scheme, it must be the case that the obj->active
+update done by the obj list deletion be visible to other processors
+before the atomic counter decrement is performed.
+
+Otherwise, the counter could fall to zero, yet obj->active would still
+be set, thus triggering the assertion in obj_destroy().  The error
+sequence looks like this:
+
+       cpu 0                           cpu 1
+       obj_poke()                      obj_timeout()
+       obj = obj_list_peek();
+       ... gains ref to obj, refcnt=2
+                                       obj_list_del(obj);
+                                       obj->active = 0 ...
+                                       ... visibility delayed ...
+                                       atomic_dec_and_test()
+                                       ... refcnt drops to 1 ...
+       atomic_dec_and_test()
+       ... refcount drops to 0 ...
+       obj_destroy()
+       BUG() triggers since obj->active
+       still seen as one
+                                       obj->active update visibility occurs
+
+With the memory barrier semantics required of the atomic_t operations
+which return values, the above sequence of memory visibility can never
+happen.  Specifically, in the above case the atomic_dec_and_test()
+counter decrement would not become globally visible until the
+obj->active update does.
+
+As a historical note, 32-bit Sparc used to only allow usage of
+24-bits of it's atomic_t type.  This was because it used 8 bits
+as a spinlock for SMP safety.  Sparc32 lacked a "compare and swap"
+type instruction.  However, 32-bit Sparc has since been moved over
+to a "hash table of spinlocks" scheme, that allows the full 32-bit
+counter to be realized.  Essentially, an array of spinlocks are
+indexed into based upon the address of the atomic_t being operated
+on, and that lock protects the atomic operation.  Parisc uses the
+same scheme.
+
+Another note is that the atomic_t operations returning values are
+extremely slow on an old 386.
+
+We will now cover the atomic bitmask operations.  You will find that
+their SMP and memory barrier semantics are similar in shape and scope
+to the atomic_t ops above.
+
+Native atomic bit operations are defined to operate on objects aligned
+to the size of an "unsigned long" C data type, and are least of that
+size.  The endianness of the bits within each "unsigned long" are the
+native endianness of the cpu.
+
+       void set_bit(unsigned long nr, volatils unsigned long *addr);
+       void clear_bit(unsigned long nr, volatils unsigned long *addr);
+       void change_bit(unsigned long nr, volatils unsigned long *addr);
+
+These routines set, clear, and change, respectively, the bit number
+indicated by "nr" on the bit mask pointed to by "ADDR".
+
+They must execute atomically, yet there are no implicit memory barrier
+semantics required of these interfaces.
+
+       int test_and_set_bit(unsigned long nr, volatils unsigned long *addr);
+       int test_and_clear_bit(unsigned long nr, volatils unsigned long *addr);
+       int test_and_change_bit(unsigned long nr, volatils unsigned long *addr);
+
+Like the above, except that these routines return a boolean which
+indicates whether the changed bit was set _BEFORE_ the atomic bit
+operation.
+
+WARNING! It is incredibly important that the value be a boolean,
+ie. "0" or "1".  Do not try to be fancy and save a few instructions by
+declaring the above to return "long" and just returning something like
+"old_val & mask" because that will not work.
+
+For one thing, this return value gets truncated to int in many code
+paths using these interfaces, so on 64-bit if the bit is set in the
+upper 32-bits then testers will never see that.
+
+One great example of where this problem crops up are the thread_info
+flag operations.  Routines such as test_and_set_ti_thread_flag() chop
+the return value into an int.  There are other places where things
+like this occur as well.
+
+These routines, like the atomic_t counter operations returning values,
+require explicit memory barrier semantics around their execution.  All
+memory operations before the atomic bit operation call must be made
+visible globally before the atomic bit operation is made visible.
+Likewise, the atomic bit operation must be visible globally before any
+subsequent memory operation is made visible.  For example:
+
+       obj->dead = 1;
+       if (test_and_set_bit(0, &obj->flags))
+               /* ... */;
+       obj->killed = 1;
+
+The implementation of test_and_set_bit() must guarentee that
+"obj->dead = 1;" is visible to cpus before the atomic memory operation
+done by test_and_set_bit() becomes visible.  Likewise, the atomic
+memory operation done by test_and_set_bit() must become visible before
+"obj->killed = 1;" is visible.
+
+Finally there is the basic operation:
+
+       int test_bit(unsigned long nr, __const__ volatile unsigned long *addr);
+
+Which returns a boolean indicating if bit "nr" is set in the bitmask
+pointed to by "addr".
+
+If explicit memory barriers are required around clear_bit() (which
+does not return a value, and thus does not need to provide memory
+barrier semantics), two interfaces are provided:
+
+       void smp_mb__before_clear_bit(void);
+       void smp_mb__after_clear_bit(void);
+
+They are used as follows, and are akin to their atomic_t operation
+brothers:
+
+       /* All memory operations before this call will
+        * be globally visible before the clear_bit().
+        */
+       smp_mb__before_clear_bit();
+       clear_bit( ... );
+
+       /* The clear_bit() will be visible before all
+        * subsequent memory operations.
+        */
+        smp_mb__after_clear_bit();
+
+Finally, there are non-atomic versions of the bitmask operations
+provided.  They are used in contexts where some other higher-level SMP
+locking scheme is being used to protect the bitmask, and thus less
+expensive non-atomic operations may be used in the implementation.
+They have names similar to the above bitmask operation interfaces,
+except that two underscores are prefixed to the interface name.
+
+       void __set_bit(unsigned long nr, volatile unsigned long *addr);
+       void __clear_bit(unsigned long nr, volatile unsigned long *addr);
+       void __change_bit(unsigned long nr, volatile unsigned long *addr);
+       int __test_and_set_bit(unsigned long nr, volatile unsigned long *addr);
+       int __test_and_clear_bit(unsigned long nr, volatile unsigned long *addr);
+       int __test_and_change_bit(unsigned long nr, volatile unsigned long *addr);
+
+These non-atomic variants also do not require any special memory
+barrier semantics.
+
+The routines xchg() and cmpxchg() need the same exact memory barriers
+as the atomic and bit operations returning values.
+
+Spinlocks and rwlocks have memory barrier expectations as well.
+The rule to follow is simple:
+
+1) When acquiring a lock, the implementation must make it globally
+   visible before any subsequent memory operation.
+
+2) When releasing a lock, the implementation must make it such that
+   all previous memory operations are globally visible before the
+   lock release.
+
+Which finally brings us to _atomic_dec_and_lock().  There is an
+architecture-neutral version implemented in lib/dec_and_lock.c,
+but most platforms will wish to optimize this in assembler.
+
+       int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock);
+
+Atomically decrement the given counter, and if will drop to zero
+atomically acquire the given spinlock and perform the decrement
+of the counter to zero.  If it does not drop to zero, do nothing
+with the spinlock.
+
+It is actually pretty simple to get the memory barrier correct.
+Simply satisfy the spinlock grab requirements, which is make
+sure the spinlock operation is globally visible before any
+subsequent memory operation.
+
+We can demonstrate this operation more clearly if we define
+an abstract atomic operation:
+
+       long cas(long *mem, long old, long new);
+
+"cas" stands for "compare and swap".  It atomically:
+
+1) Compares "old" with the value currently at "mem".
+2) If they are equal, "new" is written to "mem".
+3) Regardless, the current value at "mem" is returned.
+
+As an example usage, here is what an atomic counter update
+might look like:
+
+void example_atomic_inc(long *counter)
+{
+       long old, new, ret;
+
+       while (1) {
+               old = *counter;
+               new = old + 1;
+
+               ret = cas(counter, old, new);
+               if (ret == old)
+                       break;
+       }
+}
+
+Let's use cas() in order to build a pseudo-C atomic_dec_and_lock():
+
+int _atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock)
+{
+       long old, new, ret;
+       int went_to_zero;
+
+       went_to_zero = 0;
+       while (1) {
+               old = atomic_read(atomic);
+               new = old - 1;
+               if (new == 0) {
+                       went_to_zero = 1;
+                       spin_lock(lock);
+               }
+               ret = cas(atomic, old, new);
+               if (ret == old)
+                       break;
+               if (went_to_zero) {
+                       spin_unlock(lock);
+                       went_to_zero = 0;
+               }
+       }
+
+       return went_to_zero;
+}
+
+Now, as far as memory barriers go, as long as spin_lock()
+strictly orders all subsequent memory operations (including
+the cas()) with respect to itself, things will be fine.
+
+Said another way, _atomic_dec_and_lock() must guarentee that
+a counter dropping to zero is never made visible before the
+spinlock being acquired.
+
+Note that this also means that for the case where the counter
+is not dropping to zero, there are no memory ordering
+requirements.
diff --git a/Documentation/cdrom/packet-writing.txt b/Documentation/cdrom/packet-writing.txt
new file mode 100644 (file)
index 0000000..d34fcbc
--- /dev/null
@@ -0,0 +1,86 @@
+Getting started quick
+---------------------
+
+- Select packet support in the block device section and UDF support in
+  the file system section.
+
+- Compile and install kernel and modules, reboot.
+
+- You need the udftools package (pktsetup, mkudffs, cdrwtool).
+  Download from http://sourceforge.net/projects/linux-udf/
+
+- Grab a new CD-RW disc and format it (assuming CD-RW is hdc, substitute
+  as appropriate):
+       # cdrwtool -d /dev/hdc -q
+
+- Setup your writer
+       # pktsetup dev_name /dev/hdc
+
+- Now you can mount /dev/pktcdvd/dev_name and copy files to it. Enjoy!
+       # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime
+
+
+Packet writing for DVD-RW media
+-------------------------------
+
+DVD-RW discs can be written to much like CD-RW discs if they are in
+the so called "restricted overwrite" mode. To put a disc in restricted
+overwrite mode, run:
+
+       # dvd+rw-format /dev/hdc
+
+You can then use the disc the same way you would use a CD-RW disc:
+
+       # pktsetup dev_name /dev/hdc
+       # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime
+
+
+Packet writing for DVD+RW media
+-------------------------------
+
+According to the DVD+RW specification, a drive supporting DVD+RW discs
+shall implement "true random writes with 2KB granularity", which means
+that it should be possible to put any filesystem with a block size >=
+2KB on such a disc. For example, it should be possible to do:
+
+       # mkudffs /dev/hdc
+       # mount /dev/hdc /cdrom -t udf -o rw,noatime
+
+However, some drives don't follow the specification and expect the
+host to perform aligned writes at 32KB boundaries. Other drives do
+follow the specification, but suffer bad performance problems if the
+writes are not 32KB aligned.
+
+Both problems can be solved by using the pktcdvd driver, which always
+generates aligned writes.
+
+       # pktsetup dev_name /dev/hdc
+       # mkudffs /dev/pktcdvd/dev_name
+       # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime
+
+
+Notes
+-----
+
+- CD-RW media can usually not be overwritten more than about 1000
+  times, so to avoid unnecessary wear on the media, you should always
+  use the noatime mount option.
+
+- Defect management (ie automatic remapping of bad sectors) has not
+  been implemented yet, so you are likely to get at least some
+  filesystem corruption if the disc wears out.
+
+- Since the pktcdvd driver makes the disc appear as a regular block
+  device with a 2KB block size, you can put any filesystem you like on
+  the disc. For example, run:
+
+       # /sbin/mke2fs /dev/pktcdvd/dev_name
+
+  to create an ext2 filesystem on the disc.
+
+
+Links
+-----
+
+See http://fy.chalmers.se/~appro/linux/DVD+RW/ for more information
+about DVD writing.
diff --git a/Documentation/cpu-freq/cpufreq-nforce2.txt b/Documentation/cpu-freq/cpufreq-nforce2.txt
new file mode 100644 (file)
index 0000000..9188337
--- /dev/null
@@ -0,0 +1,19 @@
+
+The cpufreq-nforce2 driver changes the FSB on nVidia nForce2 plattforms.
+
+This works better than on other plattforms, because the FSB of the CPU
+can be controlled independently from the PCI/AGP clock.
+
+The module has two options:
+
+       fid:     multiplier * 10 (for example 8.5 = 85)
+       min_fsb: minimum FSB
+
+If not set, fid is calculated from the current CPU speed and the FSB.
+min_fsb defaults to FSB at boot time - 50 MHz.
+
+IMPORTANT: The available range is limited downwards!
+           Also the minimum available FSB can differ, for systems 
+           booting with 200 MHz, 150 should always work.
+
+
diff --git a/Documentation/dvb/README.dibusb b/Documentation/dvb/README.dibusb
new file mode 100644 (file)
index 0000000..e3d650b
--- /dev/null
@@ -0,0 +1,247 @@
+Documentation for dib3000mb frontend driver and dibusb device driver
+====================================================================
+
+Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de),
+
+dibusb and dib3000mb/mc drivers based on GPL code, which has
+
+Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+
+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, version 2.
+
+
+Supported devices USB1.1
+========================
+
+Produced and reselled by Twinhan:
+---------------------------------
+- TwinhanDTV USB-Ter DVB-T Device (VP7041)
+       http://www.twinhan.com/product_terrestrial_3.asp
+
+- TwinhanDTV Magic Box (VP7041e)
+       http://www.twinhan.com/product_terrestrial_4.asp
+
+- HAMA DVB-T USB device
+       http://www.hama.de/portal/articleId*110620/action*2598
+
+- CTS Portable (Chinese Television System)
+       http://www.2cts.tv/ctsportable/
+
+- Unknown USB DVB-T device with vendor ID Hyper-Paltek
+
+
+Produced and reselled by KWorld:
+--------------------------------
+- KWorld V-Stream XPERT DTV DVB-T USB
+       http://www.kworld.com.tw/en/product/DVBT-USB/DVBT-USB.html
+
+- JetWay DTV DVB-T USB
+       http://www.jetway.com.tw/evisn/product/lcd-tv/DVT-USB/dtv-usb.htm
+
+- ADSTech Instant TV DVB-T USB
+       http://www.adstech.com/products/PTV-333/intro/PTV-333_intro.asp?pid=PTV-333
+
+
+Others:
+-------
+- Ultima Electronic/Artec T1 USB TVBOX (AN2135 and AN2235)
+       http://82.161.246.249/products-tvbox.html
+
+- Compro Videomate DVB-U2000 - DVB-T USB
+       http://www.comprousa.com/products/vmu2000.htm
+
+- Grandtec USB DVB-T
+       http://www.grand.com.tw/
+
+- Avermedia AverTV DVBT USB
+       http://www.avermedia.com/
+
+- DiBcom USB DVB-T reference device (non-public)
+
+
+Supported devices USB2.0
+========================
+- Twinhan MagicBox II
+       http://www.twinhan.com/product_terrestrial_7.asp
+
+- Yakumo DVB-T mobile
+       http://www.yakumo.de/produkte/index.php?pid=1&ag=DVB-T
+
+- DiBcom USB2.0 DVB-T reference device (non-public)
+
+
+0. NEWS:
+  2004-12-06 - possibility for demod i2c-address probing
+             - new usb IDs (Compro,Artec)
+  2004-11-23 - merged changes from DiB3000MC_ver2.1
+             - revised the debugging
+             - possibility to deliver the complete TS for USB2.0
+  2004-11-21 - first working version of the dib3000mc/p frontend driver.
+  2004-11-12 - added additional remote control keys. Thanks to Uwe Hanke.
+  2004-11-07 - added remote control support. Thanks to David Matthews.
+  2004-11-05 - added support for a new devices (Grandtec/Avermedia/Artec)
+             - merged my changes (for dib3000mb/dibusb) to the FE_REFACTORING, because it became HEAD
+             - moved transfer control (pid filter, fifo control) from usb driver to frontend, it seems
+               better settled there (added xfer_ops-struct)
+             - created a common files for frontends (mc/p/mb)
+  2004-09-28 - added support for a new device (Unkown, vendor ID is Hyper-Paltek)
+  2004-09-20 - added support for a new device (Compro DVB-U2000), thanks
+               to Amaury Demol for reporting
+             - changed usb TS transfer method (several urbs, stopping transfer 
+               before setting a new pid)
+  2004-09-13 - added support for a new device (Artec T1 USB TVBOX), thanks
+               to Christian Motschke for reporting
+  2004-09-05 - released the dibusb device and dib3000mb-frontend driver
+
+  (old news for vp7041.c)
+  2004-07-15 - found out, by accident, that the device has a TUA6010XS for
+               PLL
+  2004-07-12 - figured out, that the driver should also work with the
+               CTS Portable (Chinese Television System)
+  2004-07-08 - firmware-extraction-2.422-problem solved, driver is now working
+               properly with firmware extracted from 2.422
+                        - #if for 2.6.4 (dvb), compile issue
+                        - changed firmware handling, see vp7041.txt sec 1.1
+  2004-07-02 - some tuner modifications, v0.1, cleanups, first public
+  2004-06-28 - now using the dvb_dmx_swfilter_packets, everything
+               runs fine now
+  2004-06-27 - able to watch and switching channels (pre-alpha)
+             - no section filtering yet
+  2004-06-06 - first TS received, but kernel oops :/
+  2004-05-14 - firmware loader is working
+  2004-05-11 - start writing the driver
+
+1. How to use?
+NOTE: This driver was developed using Linux 2.6.6.,
+it is working with 2.6.7, 2.6.8.1, 2.6.9 .
+
+Linux 2.4.x support is not planned, but patches are very welcome.
+
+NOTE: I'm using Debian testing, so the following explaination (especially
+the hotplug-path) needn't match your system, but probably it will :).
+
+1.1. Firmware
+
+The USB driver needs to download a firmware to start working.
+
+You can either use "get_dvb_firmware dibusb" to download the firmware or you
+can get it directly via
+
+for USB1.1 (AN2135)
+http://linuxtv.org/cgi-bin/cvsweb.cgi/dvb-kernel/firmware/dvb-dibusb-5.0.0.11.fw?rev=1.1&content-type=text/plain
+
+for USB1.1 (AN2235) (a few Artec T1 devices)
+http://linuxtv.org/cgi-bin/cvsweb.cgi/dvb-kernel/firmware/dvb-dibusb-an2235-1.fw?rev=1.1&content-type=text/plain
+
+for USB2.0 (FX2)
+http://linuxtv.org/cgi-bin/cvsweb.cgi/dvb-kernel/firmware/dvb-dibusb-6.0.0.5.fw?rev=1.1&content-type=text/plain
+
+1.2. Compiling
+
+Since the driver is in the linux kernel, activating the driver in
+your favorite config-environment should sufficient. I recommend
+to compile the driver as module. Hotplug does the rest.
+
+1.3. Loading the drivers
+
+Hotplug is able to load the driver, when it is needed (because you plugged
+in the device).
+
+If you want to enable debug output, you have to load the driver manually and
+from withing the dvb-kernel cvs repository.
+
+first have a look, which debug level are available:
+
+modinfo dib3000mb
+modinfo dvb-dibusb
+
+modprobe dib3000mb debug=<level>
+modprobe dvb-dibusb debug=<level>
+
+should do the trick.
+
+When the driver is loaded successfully, the firmware file was in
+the right place and the device is connected, the "Power"-LED should be
+turned on.
+
+At this point you should be able to start a dvb-capable application. For myself
+I used mplayer, dvbscan, tzap and kaxtv, they are working. Using the device
+as a slave device in vdr, was not working for me. Some work has to be done
+(patches and comments are very welcome).
+
+2. Known problems and bugs
+
+TODO:
+- signal-quality and strength calculations
+
+2.1. Adding support for devices 
+
+It is not possible to determine the range of devices based on the DiBcom
+reference designs. This is because the reference design of DiBcom can be sold
+to thirds, without telling DiBcom (so done with the Twinhan VP7041 and
+the HAMA device).
+
+When you think you have a device like this and the driver does not recognizes it,
+please send the ****load*.inf and the ****cap*.inf of the Windows driver to me.
+
+Sometimes the Vendor or Product ID is identical to the ones of Twinhan, even
+though it is not a Twinhan device (e.g. HAMA), then please send me the name
+of the device. I will add it to this list in order to make this clear to
+others.
+
+If you are familar with C you can also add the VID and PID of the device to
+the dvb-dibusb.h-file and create a patch and send it over to me or to 
+the linux-dvb mailing list, _after_ you have tried compiling and modprobing
+it.
+
+2.2. USB1.1 Bandwidth limitation
+
+Most of the current supported devices are USB1.1 and thus they have a
+maximum bandwidth of about 5-6 MBit/s when connected to a USB2.0 hub.
+This is not enough for receiving the complete transport stream of a
+DVB-T channel (which can be about 16 MBit/s). Normally this is not a
+problem, if you only want to watch TV, but watching a channel while
+recording another channel on the same frequency simply does not work.
+This applies to all USB1.1 DVB-T devices.
+
+A special problem of the dibusb for the USB1.1 is, that the USB control
+IC has a problem with write accesses while having MPEG2-streaming
+enabled. When you set another pid while receiving MPEG2-TS it happens, that
+the stream is disturbed and probably data is lost (results in distortions of
+the video or strange beeps within the audio stream). DiBcom is preparing a
+firmware especially for Linux which perhaps solves the problem.
+
+Especially VDR users are victoms of this bug. VDR frequently requests new PIDs
+due the automatic scanning (introduced in 1.3.x, afaik) and epg-scan. Disabling
+these features is maybe a solution. Additionally this behaviour of VDR exceeds
+the USB1.1 bandwidth.
+
+2.3. Comments
+
+Patches, comments and suggestions are very very welcome
+
+3. Acknowledgements
+       Amaury Demol (ademol@dibcom.fr) and Francois Kanounnikoff from DiBcom for
+       providing specs, code and help, on which the dvb-dibusb and dib3000mb are
+       based.
+
+   David Matthews for identifying a new device type (Artec T1 with AN2235)
+    and for extending dibusb with remote control event handling. Thank you.
+
+   Alex Woods for frequently answering question about usb and dvb
+    stuff, a big thank you
+
+   Bernd Wagner for helping with huge bug reports and discussions.
+
+   Some guys on the linux-dvb mailing list for encouraging me
+
+   Peter Schildmann >peter.schildmann-nospam-at-web.de< for his
+    user-level firmware loader, which saves a lot of time
+    (when writing the vp7041 driver)
+
+   Ulf Hermenau for helping me out with traditional chinese.
+
+   André Smoktun and Christian Frömmel for supporting me with
+    hardware and listening to my problems very patient
diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
new file mode 100644 (file)
index 0000000..e9964b7
--- /dev/null
@@ -0,0 +1,339 @@
+#!/usr/bin/perl
+#     DVB firmware extractor
+#
+#     (c) 2004 Andrew de Quincey
+#
+#     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.
+
+use File::Temp qw/ tempdir /;
+use IO::Handle;
+
+@components = ( "sp8870", "sp887x", "tda10045", "tda10046", "av7110", "dec2000t", "dec2540t", "dec3000s", "vp7041", "dibusb" );
+
+# Check args
+syntax() if (scalar(@ARGV) != 1);
+$cid = $ARGV[0];
+
+# Do it!
+for($i=0; $i < scalar(@components); $i++) {
+    if ($cid eq $components[$i]) {
+       $outfile = eval($cid);
+       die $@ if $@;
+       print STDERR "Firmware $outfile extracted successfully. Now copy it to either /lib/firmware or /usr/lib/hotplug/firmware/ (depending on your hotplug version).\n";
+       exit(0);
+    }
+}
+
+# If we get here, it wasn't found
+print STDERR "Unknown component \"$cid\"\n";
+syntax();
+
+
+
+
+# ---------------------------------------------------------------
+# Firmware-specific extraction subroutines
+
+sub sp8870 {
+    my $sourcefile = "tt_Premium_217g.zip";
+    my $url = "http://www.technotrend.de/new/217g/$sourcefile";
+    my $hash = "53970ec17a538945a6d8cb608a7b3899";
+    my $outfile = "dvb-fe-sp8870.fw";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+
+    checkstandard();
+
+    wgetfile($sourcefile, $url);
+    unzip($sourcefile, $tmpdir);
+    verify("$tmpdir/software/OEM/HE/App/boot/SC_MAIN.MC", $hash);
+    copy("$tmpdir/software/OEM/HE/App/boot/SC_MAIN.MC", $outfile);
+
+    $outfile;
+}
+
+sub sp887x {
+    my $sourcefile = "Dvbt1.3.57.6.zip";
+    my $url = "http://www.avermedia.com/software/$sourcefile";
+    my $cabfile = "DVBT Net  Ver1.3.57.6/disk1/data1.cab";
+    my $hash = "237938d53a7f834c05c42b894ca68ac3";
+    my $outfile = "dvb-fe-sp887x.fw";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+
+    checkstandard();
+    checkunshield();
+
+    wgetfile($sourcefile, $url);
+    unzip($sourcefile, $tmpdir);
+    unshield("$tmpdir/$cabfile", $tmpdir);
+    verify("$tmpdir/sc_main.mc", $hash);
+    copy("$tmpdir/sc_main.mc", $outfile);
+
+    $outfile;
+}
+
+sub tda10045 {
+    my $sourcefile = "tt_budget_217g.zip";
+    my $url = "http://www.technotrend.de/new/217g/$sourcefile";
+    my $hash = "2105fd5bf37842fbcdfa4bfd58f3594a";
+    my $outfile = "dvb-fe-tda10045.fw";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+
+    checkstandard();
+
+    wgetfile($sourcefile, $url);
+    unzip($sourcefile, $tmpdir);
+    extract("$tmpdir/software/OEM/PCI/App/ttlcdacc.dll", 0x37ef9, 30555, "$tmpdir/fwtmp");
+    verify("$tmpdir/fwtmp", $hash);
+    copy("$tmpdir/fwtmp", $outfile);
+
+    $outfile;
+}
+
+sub tda10046 {
+    my $sourcefile = "tt_budget_217g.zip";
+    my $url = "http://www.technotrend.de/new/217g/$sourcefile";
+    my $hash = "a25b579e37109af60f4a36c37893957c";
+    my $outfile = "dvb-fe-tda10046.fw";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+
+    checkstandard();
+
+    wgetfile($sourcefile, $url);
+    unzip($sourcefile, $tmpdir);
+    extract("$tmpdir/software/OEM/PCI/App/ttlcdacc.dll", 0x3f731, 24479, "$tmpdir/fwtmp");
+    verify("$tmpdir/fwtmp", $hash);
+    copy("$tmpdir/fwtmp", $outfile);
+
+    $outfile;
+}
+
+sub av7110 {
+    my $sourcefile = "dvb-ttpci-01.fw-261c";
+    my $url = "http://www.linuxtv.org/download/dvb/firmware/$sourcefile";
+    my $hash = "7b263de6b0b92d2347319c65adc7d4fb";
+    my $outfile = "dvb-ttpci-01.fw";
+
+    checkstandard();
+
+    wgetfile($sourcefile, $url);
+    verify($sourcefile, $hash);
+    copy($sourcefile, $outfile);
+
+    $outfile;
+}
+
+sub dec2000t {
+    my $sourcefile = "dec217g.exe";
+    my $url = "http://hauppauge.lightpath.net/de/$sourcefile";
+    my $hash = "bd86f458cee4a8f0a8ce2d20c66215a9";
+    my $outfile = "dvb-ttusb-dec-2000t.fw";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+
+    checkstandard();
+
+    wgetfile($sourcefile, $url);
+    unzip($sourcefile, $tmpdir);
+    verify("$tmpdir/software/OEM/STB/App/Boot/STB_PC_T.bin", $hash);
+    copy("$tmpdir/software/OEM/STB/App/Boot/STB_PC_T.bin", $outfile);
+
+    $outfile;
+}
+
+sub dec2540t {
+    my $sourcefile = "dec217g.exe";
+    my $url = "http://hauppauge.lightpath.net/de/$sourcefile";
+    my $hash = "53e58f4f5b5c2930beee74a7681fed92";
+    my $outfile = "dvb-ttusb-dec-2540t.fw";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+
+    checkstandard();
+
+    wgetfile($sourcefile, $url);
+    unzip($sourcefile, $tmpdir);
+    verify("$tmpdir/software/OEM/STB/App/Boot/STB_PC_X.bin", $hash);
+    copy("$tmpdir/software/OEM/STB/App/Boot/STB_PC_X.bin", $outfile);
+
+    $outfile;
+}
+
+sub dec3000s {
+    my $sourcefile = "dec217g.exe";
+    my $url = "http://hauppauge.lightpath.net/de/$sourcefile";
+    my $hash = "b013ececea83f4d6d8d2a29ac7c1b448";
+    my $outfile = "dvb-ttusb-dec-3000s.fw";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+
+    checkstandard();
+
+    wgetfile($sourcefile, $url);
+    unzip($sourcefile, $tmpdir);
+    verify("$tmpdir/software/OEM/STB/App/Boot/STB_PC_S.bin", $hash);
+    copy("$tmpdir/software/OEM/STB/App/Boot/STB_PC_S.bin", $outfile);
+
+    $outfile;
+}
+
+sub vp7041 {
+    my $sourcefile = "2.422.zip";
+    my $url = "http://www.twinhan.com/files/driver/USB-Ter/$sourcefile";
+    my $hash = "e88c9372d1f66609a3e7b072c53fbcfe";
+    my $outfile = "dvb-vp7041-2.422.fw";
+    my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+
+    checkstandard();
+
+    wgetfile($sourcefile, $url);
+    unzip($sourcefile, $tmpdir);
+    extract("$tmpdir/VisionDTV/Drivers/Win2K&XP/UDTTload.sys", 12503, 3036, "$tmpdir/fwtmp1");
+    extract("$tmpdir/VisionDTV/Drivers/Win2K&XP/UDTTload.sys", 2207, 10274, "$tmpdir/fwtmp2");
+
+    my $CMD = "\000\001\000\222\177\000";
+    my $PAD = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000";
+    my ($FW);
+    open $FW, ">$tmpdir/fwtmp3";
+    print $FW "$CMD\001$PAD";
+    print $FW "$CMD\001$PAD";
+    appendfile($FW, "$tmpdir/fwtmp1");
+    print $FW "$CMD\000$PAD";
+    print $FW "$CMD\001$PAD";
+    appendfile($FW, "$tmpdir/fwtmp2");
+    print $FW "$CMD\001$PAD";
+    print $FW "$CMD\000$PAD";
+    close($FW);
+
+    verify("$tmpdir/fwtmp3", $hash);
+    copy("$tmpdir/fwtmp3", $outfile);
+
+    $outfile;
+}
+
+sub dibusb {
+       my $url = "http://linuxtv.org/cgi-bin/cvsweb.cgi/dvb-kernel/firmware/dvb-dibusb-5.0.0.11.fw?rev=1.1&content-type=text/plain";
+       my $outfile = "dvb-dibusb-5.0.0.11.fw";
+       my $hash = "fa490295a527360ca16dcdf3224ca243";
+
+       checkstandard();
+
+       wgetfile($outfile, $url);
+       verify($outfile,$hash);
+
+       $outfile;
+}
+
+# ---------------------------------------------------------------
+# Utilities
+
+sub checkstandard {
+    if (system("which unzip > /dev/null 2>&1")) {
+       die "This firmware requires the unzip command - see ftp://ftp.info-zip.org/pub/infozip/UnZip.html\n";
+    }
+    if (system("which md5sum > /dev/null 2>&1")) {
+       die "This firmware requires the md5sum command - see http://www.gnu.org/software/coreutils/\n";
+    }
+    if (system("which wget > /dev/null 2>&1")) {
+       die "This firmware requires the wget command - see http://wget.sunsite.dk/\n";
+    }
+}
+
+sub checkunshield {
+    if (system("which unshield > /dev/null 2>&1")) {
+       die "This firmware requires the unshield command - see http://sourceforge.net/projects/synce/\n";
+    }
+}
+
+sub wgetfile {
+    my ($sourcefile, $url) = @_;
+
+    if (! -f $sourcefile) {
+       system("wget -O \"$sourcefile\" \"$url\"") and die "wget failed - unable to download firmware";
+    }
+}
+
+sub unzip {
+    my ($sourcefile, $todir) = @_;
+
+    $status = system("unzip -q -o -d \"$todir\" \"$sourcefile\" 2>/dev/null" );
+    if ((($status >> 8) > 2) || (($status & 0xff) != 0)) {
+       die ("unzip failed - unable to extract firmware");
+    }
+}
+
+sub unshield {
+    my ($sourcefile, $todir) = @_;
+
+    system("unshield -d \"$todir\" \"$sourcefile\" > /dev/null" ) and die ("unshield failed - unable to extract firmware");
+}
+
+sub verify {
+    my ($filename, $hash) = @_;
+    my ($testhash);
+
+    open(CMD, "md5sum \"$filename\"|");
+    $testhash = <CMD>;
+    $testhash =~ /([a-zA-Z0-9]*)/;
+    $testhash = $1;
+    close CMD;
+    die "Hash of extracted file does not match!\n" if ($testhash ne $hash);
+}
+
+sub copy {
+    my ($from, $to) = @_;
+
+    system("cp -f \"$from\" \"$to\"") and die ("cp failed");
+}
+
+sub extract {
+    my ($infile, $offset, $length, $outfile) = @_;
+    my ($chunklength, $buf, $rcount);
+
+    open INFILE, "<$infile";
+    open OUTFILE, ">$outfile";
+    sysseek(INFILE, $offset, SEEK_SET);
+    while($length > 0) {
+       # Calc chunk size
+       $chunklength = 2048;
+       $chunklength = $length if ($chunklength > $length);
+
+       $rcount = sysread(INFILE, $buf, $chunklength);
+       die "Ran out of data\n" if ($rcount != $chunklength);
+       syswrite(OUTFILE, $buf);
+       $length -= $rcount;
+    }
+    close INFILE;
+    close OUTFILE;
+}
+
+sub appendfile {
+    my ($FH, $infile) = @_;
+    my ($buf);
+
+    open INFILE, "<$infile";
+    while(1) {
+       $rcount = sysread(INFILE, $buf, 2048);
+       last if ($rcount == 0);
+       print $FH $buf;
+    }
+    close(INFILE);
+}
+
+sub syntax() {
+    print STDERR "syntax: get_dvb_firmware <component>\n";
+    print STDERR "Supported components:\n";
+    for($i=0; $i < scalar(@components); $i++) {
+       print STDERR "\t" . $components[$i] . "\n";
+    }
+    exit(1);
+}
diff --git a/Documentation/dvb/udev.txt b/Documentation/dvb/udev.txt
new file mode 100644 (file)
index 0000000..68ee224
--- /dev/null
@@ -0,0 +1,46 @@
+The DVB subsystem currently registers to the sysfs subsystem using the
+"class_simple" interface.
+
+This means that only the basic informations like module loading parameters
+are presented through sysfs. Other things that might be interesting are
+currently *not* available.
+
+Nevertheless it's now possible to add proper udev rules so that the
+DVB device nodes are created automatically.
+
+We assume that you have udev already up and running and that have been
+creating the DVB device nodes manually up to now due to the missing sysfs
+support.
+
+0. Don't forget to disable your current method of creating the
+device nodes manually.
+
+1. Unfortunately, you'll need a helper script to transform the kernel
+sysfs device name into the well known dvb adapter / device naming scheme.
+The script should be called "dvb.sh" and should be placed into a script
+dir where udev can execute it, most likely /etc/udev/scripts/
+
+So, create a new file /etc/udev/scripts/dvb.sh and add the following:
+------------------------------schnipp------------------------------------------------
+#!/bin/sh
+/bin/echo $1 | /bin/sed -e 's,dvb\([0-9]\)\.\([^0-9]*\)\([0-9]\),dvb/adapter\1/\2\3,'
+------------------------------schnipp------------------------------------------------
+
+Don't forget to make the script executable with "chmod".
+
+1. You need to create a proper udev rule that will create the device nodes
+like you know them. All real distributions out there scan the /etc/udev/rules.d
+directory for rule files. The main udev configuration file /etc/udev/udev.conf
+will tell you the directory where the rules are, most likely it's /etc/udev/rules.d/
+
+Create a new rule file in that directory called "dvb.rule" and add the following line:
+------------------------------schnipp------------------------------------------------
+KERNEL="dvb*", PROGRAM="/etc/udev/scripts/dvb.sh %k", NAME="%c"
+------------------------------schnipp------------------------------------------------
+
+If you want more control over the device nodes (for example a special group membership)
+have a look at "man udev".
+
+For every device that registers to the sysfs subsystem with a "dvb" prefix,
+the helper script /etc/udev/scripts/dvb.sh is invoked, which will then
+create the proper device node in your /dev/ directory.
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
new file mode 100644 (file)
index 0000000..2da4a39
--- /dev/null
@@ -0,0 +1,17 @@
+The following is a list of files and features that are going to be
+removed in the kernel source tree.  Every entry should contain what
+exactly is going away, why it is happening, and who is going to be doing
+the work.  When the feature is removed from the kernel, it should also
+be removed from this file.
+
+---------------------------
+
+What:  devfs
+When:  July 2005
+Files: fs/devfs/*, include/linux/devfs_fs*.h and assorted devfs
+       function calls throughout the kernel tree
+Why:   It has been unmaintained for a number of years, has unfixable
+       races, contains a naming policy within the kernel that is
+       against the LSB, and can be replaced by using udev.
+Who:   Greg Kroah-Hartman <greg@kroah.com>
+
diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt
new file mode 100644 (file)
index 0000000..e97d024
--- /dev/null
@@ -0,0 +1,88 @@
+Accessing PCI device resources through sysfs
+
+sysfs, usually mounted at /sys, provides access to PCI resources on platforms
+that support it.  For example, a given bus might look like this:
+
+     /sys/devices/pci0000:17
+     |-- 0000:17:00.0
+     |   |-- class
+     |   |-- config
+     |   |-- detach_state
+     |   |-- device
+     |   |-- irq
+     |   |-- local_cpus
+     |   |-- resource
+     |   |-- resource0
+     |   |-- resource1
+     |   |-- resource2
+     |   |-- rom
+     |   |-- subsystem_device
+     |   |-- subsystem_vendor
+     |   `-- vendor
+     `-- detach_state
+
+The topmost element describes the PCI domain and bus number.  In this case,
+the domain number is 0000 and the bus number is 17 (both values are in hex).
+This bus contains a single function device in slot 0.  The domain and bus
+numbers are reproduced for convenience.  Under the device directory are several
+files, each with their own function.
+
+       file               function
+       ----               --------
+       class              PCI class (ascii, ro)
+       config             PCI config space (binary, rw)
+       detach_state       connection status (bool, rw)
+       device             PCI device (ascii, ro)
+       irq                IRQ number (ascii, ro)
+       local_cpus         nearby CPU mask (cpumask, ro)
+       resource                   PCI resource host addresses (ascii, ro)
+       resource0..N       PCI resource N, if present (binary, mmap)
+       rom                PCI ROM resource, if present (binary, ro)
+       subsystem_device           PCI subsystem device (ascii, ro)
+       subsystem_vendor           PCI subsystem vendor (ascii, ro)
+       vendor             PCI vendor (ascii, ro)
+
+  ro - read only file
+  rw - file is readable and writable
+  mmap - file is mmapable
+  ascii - file contains ascii text
+  binary - file contains binary data
+  cpumask - file contains a cpumask type
+
+The read only files are informational, writes to them will be ignored.
+Writable files can be used to perform actions on the device (e.g. changing
+config space, detaching a device).  mmapable files are available via an
+mmap of the file at offset 0 and can be used to do actual device programming
+from userspace.  Note that some platforms don't support mmapping of certain
+resources, so be sure to check the return value from any attempted mmap.
+
+Accessing legacy resources through sysfs
+
+Legacy I/O port and ISA memory resources are also provided in sysfs if the
+underlying platform supports them.  They're located in the PCI class heirarchy,
+e.g.
+
+       /sys/class/pci_bus/0000:17/
+       |-- bridge -> ../../../devices/pci0000:17
+       |-- cpuaffinity
+       |-- legacy_io
+       `-- legacy_mem
+
+The legacy_io file is a read/write file that can be used by applications to
+do legacy port I/O.  The application should open the file, seek to the desired
+port (e.g. 0x3e8) and do a read or a write of 1, 2 or 4 bytes.  The legacy_mem
+file should be mmapped with an offset corresponding to the memory offset
+desired, e.g. 0xa0000 for the VGA frame buffer.  The application can then
+simply dereference the returned pointer (after checking for errors of course)
+to access legacy memory space.
+
+Supporting PCI access on new platforms
+
+In order to support PCI resource mapping as described above, Linux platform
+code must define HAVE_PCI_MMAP and provide a pci_mmap_page_range function.
+Platforms are free to only support subsets of the mmap functionality, but
+useful return codes should be provided.
+
+Legacy resources are protected by the HAVE_PCI_LEGACY define.  Platforms
+wishing to support legacy functionality should define it and provide
+pci_legacy_read, pci_legacy_write and pci_mmap_legacy_page_range functions.
\ No newline at end of file
diff --git a/Documentation/fujitsu/frv/README.txt b/Documentation/fujitsu/frv/README.txt
new file mode 100644 (file)
index 0000000..a984faa
--- /dev/null
@@ -0,0 +1,51 @@
+                      ================================
+                      Fujitsu FR-V LINUX DOCUMENTATION
+                      ================================
+
+This directory contains documentation for the Fujitsu FR-V CPU architecture
+port of Linux.
+
+The following documents are available:
+
+ (*) features.txt
+
+     A description of the basic features inherent in this architecture port.
+
+
+ (*) configuring.txt
+
+     A summary of the configuration options particular to this architecture.
+
+
+ (*) booting.txt
+
+     A description of how to boot the kernel image and a summary of the kernel
+     command line options.
+
+
+ (*) gdbstub.txt
+
+     A description of how to debug the kernel using GDB attached by serial
+     port, and a summary of the services available.
+
+
+ (*) mmu-layout.txt
+
+     A description of the virtual and physical memory layout used in the
+     MMU linux kernel, and the registers used to support it.
+
+
+ (*) gdbinit
+
+     An example .gdbinit file for use with GDB. It includes macros for viewing
+     MMU state on the FR451. See mmu-layout.txt for more information.
+
+
+ (*) clock.txt
+
+     A description of the CPU clock scaling interface.
+
+
+ (*) atomic-ops.txt
+
+     A description of how the FR-V kernel's atomic operations work.
diff --git a/Documentation/fujitsu/frv/atomic-ops.txt b/Documentation/fujitsu/frv/atomic-ops.txt
new file mode 100644 (file)
index 0000000..96638e9
--- /dev/null
@@ -0,0 +1,134 @@
+                              =====================================
+                              FUJITSU FR-V KERNEL ATOMIC OPERATIONS
+                              =====================================
+
+On the FR-V CPUs, there is only one atomic Read-Modify-Write operation: the SWAP/SWAPI
+instruction. Unfortunately, this alone can't be used to implement the following operations:
+
+ (*) Atomic add to memory
+
+ (*) Atomic subtract from memory
+
+ (*) Atomic bit modification (set, clear or invert)
+
+ (*) Atomic compare and exchange
+
+On such CPUs, the standard way of emulating such operations in uniprocessor mode is to disable
+interrupts, but on the FR-V CPUs, modifying the PSR takes a lot of clock cycles, and it has to be
+done twice. This means the CPU runs for a relatively long time with interrupts disabled,
+potentially having a great effect on interrupt latency.
+
+
+=============
+NEW ALGORITHM
+=============
+
+To get around this, the following algorithm has been implemented. It operates in a way similar to
+the LL/SC instruction pairs supported on a number of platforms.
+
+ (*) The CCCR.CC3 register is reserved within the kernel to act as an atomic modify abort flag.
+
+ (*) In the exception prologues run on kernel->kernel entry, CCCR.CC3 is set to 0 (Undefined
+     state).
+
+ (*) All atomic operations can then be broken down into the following algorithm:
+
+     (1) Set ICC3.Z to true and set CC3 to True (ORCC/CKEQ/ORCR).
+
+     (2) Load the value currently in the memory to be modified into a register.
+
+     (3) Make changes to the value.
+
+     (4) If CC3 is still True, simultaneously and atomically (by VLIW packing):
+
+        (a) Store the modified value back to memory.
+
+        (b) Set ICC3.Z to false (CORCC on GR29 is sufficient for this - GR29 holds the current
+            task pointer in the kernel, and so is guaranteed to be non-zero).
+
+     (5) If ICC3.Z is still true, go back to step (1).
+
+This works in a non-SMP environment because any interrupt or other exception that happens between
+steps (1) and (4) will set CC3 to the Undefined, thus aborting the store in (4a), and causing the
+condition in ICC3 to remain with the Z flag set, thus causing step (5) to loop back to step (1).
+
+
+This algorithm suffers from two problems:
+
+ (1) The condition CCCR.CC3 is cleared unconditionally by an exception, irrespective of whether or
+     not any changes were made to the target memory location during that exception.
+
+ (2) The branch from step (5) back to step (1) may have to happen more than once until the store
+     manages to take place. In theory, this loop could cycle forever because there are too many
+     interrupts coming in, but it's unlikely.
+
+
+=======
+EXAMPLE
+=======
+
+Taking an example from include/asm-frv/atomic.h:
+
+       static inline int atomic_add_return(int i, atomic_t *v)
+       {
+               unsigned long val;
+
+               asm("0:                                         \n"
+
+It starts by setting ICC3.Z to true for later use, and also transforming that into CC3 being in the
+True state.
+
+                   "   orcc            gr0,gr0,gr0,icc3        \n"     <-- (1)
+                   "   ckeq            icc3,cc7                \n"     <-- (1)
+
+Then it does the load. Note that the final phase of step (1) is done at the same time as the
+load. The VLIW packing ensures they are done simultaneously. The ".p" on the load must not be
+removed without swapping the order of these two instructions.
+
+                   "   ld.p            %M0,%1                  \n"     <-- (2)
+                   "   orcr            cc7,cc7,cc3             \n"     <-- (1)
+
+Then the proposed modification is generated. Note that the old value can be retained if required
+(such as in test_and_set_bit()).
+
+                   "   add%I2          %1,%2,%1                \n"     <-- (3)
+
+Then it attempts to store the value back, contingent on no exception having cleared CC3 since it
+was set to True.
+
+                   "   cst.p           %1,%M0          ,cc3,#1 \n"     <-- (4a)
+
+It simultaneously records the success or failure of the store in ICC3.Z.
+
+                   "   corcc           gr29,gr29,gr0   ,cc3,#1 \n"     <-- (4b)
+
+Such that the branch can then be taken if the operation was aborted.
+
+                   "   beq             icc3,#0,0b              \n"     <-- (5)
+                   : "+U"(v->counter), "=&r"(val)
+                   : "NPr"(i)
+                   : "memory", "cc7", "cc3", "icc3"
+                   );
+
+               return val;
+       }
+
+
+=============
+CONFIGURATION
+=============
+
+The atomic ops implementation can be made inline or out-of-line by changing the
+CONFIG_FRV_OUTOFLINE_ATOMIC_OPS configuration variable. Making it out-of-line has a number of
+advantages:
+
+ - The resulting kernel image may be smaller
+ - Debugging is easier as atomic ops can just be stepped over and they can be breakpointed
+
+Keeping it inline also has a number of advantages:
+
+ - The resulting kernel may be Faster
+   - no out-of-line function calls need to be made
+   - the compiler doesn't have half its registers clobbered by making a call
+
+The out-of-line implementations live in arch/frv/lib/atomic-ops.S.
diff --git a/Documentation/fujitsu/frv/booting.txt b/Documentation/fujitsu/frv/booting.txt
new file mode 100644 (file)
index 0000000..4e22905
--- /dev/null
@@ -0,0 +1,181 @@
+                         =========================
+                         BOOTING FR-V LINUX KERNEL
+                         =========================
+
+======================
+PROVIDING A FILESYSTEM
+======================
+
+First of all, a root filesystem must be made available. This can be done in
+one of two ways:
+
+  (1) NFS Export
+
+      A filesystem should be constructed in a directory on an NFS server that
+      the target board can reach. This directory should then be NFS exported
+      such that the target board can read and write into it as root.
+
+  (2) Flash Filesystem (JFFS2 Recommended)
+
+      In this case, the image must be stored or built up on flash before it
+      can be used. A complete image can be built using the mkfs.jffs2 or
+      similar program and then downloaded and stored into flash by RedBoot.
+
+
+========================
+LOADING THE KERNEL IMAGE
+========================
+
+The kernel will need to be loaded into RAM by RedBoot (or by some alternative
+boot loader) before it can be run. The kernel image (arch/frv/boot/Image) may
+be loaded in one of three ways:
+
+  (1) Load from Flash
+
+      This is the simplest. RedBoot can store an image in the flash (see the
+      RedBoot documentation) and then load it back into RAM. RedBoot keeps
+      track of the load address, entry point and size, so the command to do
+      this is simply:
+
+       fis load linux
+
+      The image is then ready to be executed.
+
+  (2) Load by TFTP
+
+      The following command will download a raw binary kernel image from the
+      default server (as negotiated by BOOTP) and store it into RAM:
+
+       load -b 0x00100000 -r /tftpboot/image.bin
+
+      The image is then ready to be executed.
+
+  (3) Load by Y-Modem
+
+      The following command will download a raw binary kernel image across the
+      serial port that RedBoot is currently using:
+
+       load -m ymodem -b 0x00100000 -r zImage
+
+      The serial client (such as minicom) must then be told to transmit the
+      program by Y-Modem.
+
+      When finished, the image will then be ready to be executed.
+
+
+==================
+BOOTING THE KERNEL
+==================
+
+Boot the image with the following RedBoot command:
+
+       exec -c "<CMDLINE>" 0x00100000
+
+For example:
+
+       exec -c "console=ttySM0,115200 ip=:::::dhcp root=/dev/mtdblock2 rw"
+
+This will start the kernel running. Note that if the GDB-stub is compiled in,
+then the kernel will immediately wait for GDB to connect over serial before
+doing anything else. See the section on kernel debugging with GDB.
+
+The kernel command line <CMDLINE> tells the kernel where its console is and
+how to find its root filesystem. This is made up of the following components,
+separated by spaces:
+
+  (*) console=ttyS<x>[,<baud>[<parity>[<bits>[<flow>]]]]
+
+      This specifies that the system console should output through on-chip
+      serial port <x> (which can be "0" or "1").
+
+      <baud> is a standard baud rate between 1200 and 115200 (default 9600).
+
+      <parity> is a parity setting of "N", "O", "E", "M" or "S" for None, Odd,
+      Even, Mark or Space. "None" is the default.
+
+      <stop> is "7" or "8" for the number of bits per character. "8" is the
+      default.
+
+      <flow> is "r" to use flow control (XCTS on serial port 2 only). The
+      default is to not use flow control.
+
+      For example:
+
+       console=ttyS0,115200
+
+      To use the first on-chip serial port at baud rate 115200, no parity, 8
+      bits, and no flow control.
+
+  (*) root=/dev/<xxxx>
+
+      This specifies the device upon which the root filesystem resides. For
+      example:
+
+       /dev/nfs        NFS root filesystem
+       /dev/mtdblock3  Fourth RedBoot partition on the System Flash
+
+  (*) rw
+
+      Start with the root filesystem mounted Read/Write.
+
+  The remaining components are all optional:
+
+  (*) ip=<ip>::::<host>:<iface>:<cfg>
+
+      Configure the network interface. If <cfg> is "off" then <ip> should
+      specify the IP address for the network device <iface>. <host> provide
+      the hostname for the device.
+
+      If <cfg> is "bootp" or "dhcp", then all of these parameters will be
+      discovered by consulting a BOOTP or DHCP server.
+
+      For example, the following might be used:
+
+       ip=192.168.73.12::::frv:eth0:off
+
+      This sets the IP address on the VDK motherboard RTL8029 ethernet chipset
+      (eth0) to be 192.168.73.12, and sets the board's hostname to be "frv".
+
+  (*) nfsroot=<server>:<dir>[,v<vers>]
+
+      This is mandatory if "root=/dev/nfs" is given as an option. It tells the
+      kernel the IP address of the NFS server providing its root filesystem,
+      and the pathname on that server of the filesystem.
+
+      The NFS version to use can also be specified. v2 and v3 are supported by
+      Linux.
+
+      For example:
+
+       nfsroot=192.168.73.1:/nfsroot-frv
+
+  (*) profile=1
+
+      Turns on the kernel profiler (accessible through /proc/profile).
+
+  (*) console=gdb0
+
+      This can be used as an alternative to the "console=ttyS..." listed
+      above. I tells the kernel to pass the console output to GDB if the
+      gdbstub is compiled in to the kernel.
+
+      If this is used, then the gdbstub passes the text to GDB, which then
+      simply dumps it to its standard output.
+
+  (*) mem=<xxx>M
+
+      Normally the kernel will work out how much SDRAM it has by reading the
+      SDRAM controller registers. That can be overridden with this
+      option. This allows the kernel to be told that it has <xxx> megabytes of
+      memory available.
+
+  (*) init=<prog> [<arg> [<arg> [<arg> ...]]]
+
+      This tells the kernel what program to run initially. By default this is
+      /sbin/init, but /sbin/sash or /bin/sh are common alternatives.
+
+  (*) vdc=...
+
+      This option configures the MB93493 companion chip visual display
+      driver. Please see Documentation/fujitsu/mb93493/vdc.txt for more
+      information.
diff --git a/Documentation/fujitsu/frv/clock.txt b/Documentation/fujitsu/frv/clock.txt
new file mode 100644 (file)
index 0000000..c72d350
--- /dev/null
@@ -0,0 +1,65 @@
+Clock scaling
+-------------
+
+The kernel supports scaling of CLCK.CMODE, CLCK.CM and CLKC.P0 clock
+registers. If built with CONFIG_PM and CONFIG_SYSCTL options enabled, four
+extra files will appear in the directory /proc/sys/pm/. Reading these files
+will show:
+
+      p0               -- current value of the P0 bit in CLKC register.
+      cm               -- current value of the CM bits in CLKC register.
+      cmode            -- current value of the CMODE bits in CLKC register.
+
+On all boards, the 'p0' file should also be writable, and either '1' or '0'
+can be rewritten, to set or clear the CLKC_P0 bit respectively, hence
+controlling whether the resource bus rate clock is halved.
+
+The 'cm' file should also be available on all boards. '0' can be written to it
+to shift the board into High-Speed mode (normal), and '1' can be written to
+shift the board into Medium-Speed mode. Selecting Low-Speed mode is not
+supported by this interface, even though some CPUs do support it.
+
+On the boards with FR405 CPU (i.e. CB60 and CB70), the 'cmode' file is also
+writable, allowing the CPU core speed (and other clock speeds) to be
+controlled from userspace.
+
+
+Determining current and possible settings
+-----------------------------------------
+
+The current state and the available masks can be found in /proc/cpuinfo. For
+example, on the CB70:
+
+       # cat /proc/cpuinfo
+       CPU-Series:     fr400
+       CPU-Core:       fr405, gr0-31, BE, CCCR
+       CPU:            mb93405
+       MMU:            Prot
+       FP-Media:       fr0-31, Media
+       System:         mb93091-cb70, mb93090-mb00
+       PM-Controls:    cmode=0xd31f, cm=0x3, p0=0x3, suspend=0x9
+       PM-Status:      cmode=3, cm=0, p0=0
+       Clock-In:       50.00 MHz
+       Clock-Core:     300.00 MHz
+       Clock-SDRAM:    100.00 MHz
+       Clock-CBus:     100.00 MHz
+       Clock-Res:      50.00 MHz
+       Clock-Ext:      50.00 MHz
+       Clock-DSU:      25.00 MHz
+       BogoMips:       300.00
+
+And on the PDK, the PM lines look like the following:
+
+       PM-Controls:    cm=0x3, p0=0x3, suspend=0x9
+       PM-Status:      cmode=9, cm=0, p0=0
+
+The PM-Controls line, if present, will indicate which /proc/sys/pm files can
+be set to what values. The specification values are bitmasks; so, for example,
+"suspend=0x9" indicates that 0 and 3 can be written validly to
+/proc/sys/pm/suspend.
+
+The PM-Controls line will only be present if CONFIG_PM is configured to Y.
+
+The PM-Status line indicates which clock controls are set to which value. If
+the file can be read, then the suspend value must be 0, and so that's not
+included.
diff --git a/Documentation/fujitsu/frv/configuring.txt b/Documentation/fujitsu/frv/configuring.txt
new file mode 100644 (file)
index 0000000..36e76a2
--- /dev/null
@@ -0,0 +1,125 @@
+                  =======================================
+                  FUJITSU FR-V LINUX KERNEL CONFIGURATION
+                  =======================================
+
+=====================
+CONFIGURATION OPTIONS
+=====================
+
+The most important setting is in the "MMU support options" tab (the first
+presented in the configuration tools available):
+
+ (*) "Kernel Type"
+
+     This options allows selection of normal, MMU-requiring linux, and uClinux
+     (which doesn't require an MMU and doesn't have inter-process protection).
+
+There are a number of settings in the "Processor type and features" section of
+the kernel configuration that need to be considered.
+
+ (*) "CPU"
+
+     The register and instruction sets at the core of the processor. This can
+     only be set to "FR40x/45x/55x" at the moment - but this permits usage of
+     the kernel with MB93091 CB10, CB11, CB30, CB41, CB60, CB70 and CB451
+     CPU boards, and with the MB93093 PDK board.
+
+ (*) "System"
+
+     This option allows a choice of basic system. This governs the peripherals
+     that are expected to be available.
+
+ (*) "Motherboard"
+
+     This specifies the type of motherboard being used, and the peripherals
+     upon it. Currently only "MB93090-MB00" can be set here.
+
+ (*) "Default cache-write mode"
+
+     This controls the initial data cache write management mode. By default
+     Write-Through is selected, but Write-Back (Copy-Back) can also be
+     selected. This can be changed dynamically once the kernel is running (see
+     features.txt).
+
+There are some architecture specific configuration options in the "General
+Setup" section of the kernel configuration too:
+
+ (*) "Reserve memory uncached for (PCI) DMA"
+
+     This requests that a uClinux kernel set aside some memory in an uncached
+     window for the use as consistent DMA memory (mainly for PCI). At least a
+     megabyte will be allocated in this way, possibly more. Any memory so
+     reserved will not be available for normal allocations.
+
+ (*) "Kernel support for ELF-FDPIC binaries"
+
+     This enables the binary-format driver for the new FDPIC ELF binaries that
+     this platform normally uses. These binaries are totally relocatable -
+     their separate sections can relocated independently, allowing them to be
+     shared on uClinux where possible. This should normally be enabled.
+
+ (*) "Kernel image protection"
+
+     This makes the protection register governing access to the core kernel
+     image prohibit access by userspace programs. This option is available on
+     uClinux only.
+
+There are also a number of settings in the "Kernel Hacking" section of the
+kernel configuration especially for debugging a kernel on this
+architecture. See the "gdbstub.txt" file for information about those.
+
+
+======================
+DEFAULT CONFIGURATIONS
+======================
+
+The kernel sources include a number of example default configurations:
+
+ (*) defconfig-mb93091
+
+     Default configuration for the MB93091-VDK with both CPU board and
+     MB93090-MB00 motherboard running uClinux.
+
+
+ (*) defconfig-mb93091-fb
+
+     Default configuration for the MB93091-VDK with CPU board,
+     MB93090-MB00 motherboard, and DAV board running uClinux.
+     Includes framebuffer driver.
+
+
+ (*) defconfig-mb93093
+
+     Default configuration for the MB93093-PDK board running uClinux.
+
+
+ (*) defconfig-cb70-standalone
+
+     Default configuration for the MB93091-VDK with only CB70 CPU board
+     running uClinux. This will use the CB70's DM9000 for network access.
+
+
+ (*) defconfig-mmu
+
+     Default configuration for the MB93091-VDK with both CB451 CPU board and
+     MB93090-MB00 motherboard running MMU linux.
+
+ (*) defconfig-mmu-audio
+
+     Default configuration for the MB93091-VDK with CB451 CPU board, DAV
+     board, and MB93090-MB00 motherboard running MMU linux. Includes
+     audio driver.
+
+ (*) defconfig-mmu-fb
+
+     Default configuration for the MB93091-VDK with CB451 CPU board, DAV
+     board, and MB93090-MB00 motherboard running MMU linux. Includes
+     framebuffer driver.
+
+ (*) defconfig-mmu-standalone
+
+     Default configuration for the MB93091-VDK with only CB451 CPU board
+     running MMU linux.
+
+
+
diff --git a/Documentation/fujitsu/frv/features.txt b/Documentation/fujitsu/frv/features.txt
new file mode 100644 (file)
index 0000000..fa20c0e
--- /dev/null
@@ -0,0 +1,310 @@
+                        ===========================
+                        FUJITSU FR-V LINUX FEATURES
+                        ===========================
+
+This kernel port has a number of features of which the user should be aware:
+
+ (*) Linux and uClinux
+
+     The FR-V architecture port supports both normal MMU linux and uClinux out
+     of the same sources.
+
+
+ (*) CPU support
+
+     Support for the FR401, FR403, FR405, FR451 and FR555 CPUs should work with
+     the same uClinux kernel configuration.
+
+     In normal (MMU) Linux mode, only the FR451 CPU will work as that is the
+     only one with a suitably featured CPU.
+
+     The kernel is written and compiled with the assumption that only the
+     bottom 32 GR registers and no FR registers will be used by the kernel
+     itself, however all extra userspace registers will be saved on context
+     switch. Note that since most CPUs can't support lazy switching, no attempt
+     is made to do lazy register saving where that would be possible (FR555
+     only currently).
+
+
+ (*) Board support
+
+     The board on which the kernel will run can be configured on the "Processor
+     type and features" configuration tab.
+
+     Set the System to "MB93093-PDK" to boot from the MB93093 (FR403) PDK.
+
+     Set the System to "MB93091-VDK" to boot from the CB11, CB30, CB41, CB60,
+     CB70 or CB451 VDK boards. Set the Motherboard setting to "MB93090-MB00" to
+     boot with the standard ATA90590B VDK motherboard, and set it to "None" to
+     boot without any motherboard.
+
+
+ (*) Binary Formats
+
+     The only userspace binary format supported is FDPIC ELF. Normal ELF, FLAT
+     and AOUT binaries are not supported for this architecture.
+
+     FDPIC ELF supports shared library and program interpreter facilities.
+
+
+ (*) Scheduler Speed
+
+     The kernel scheduler runs at 100Hz irrespective of the clock speed on this
+     architecture. This value is set in asm/param.h (see the HZ macro defined
+     there).
+
+
+ (*) Normal (MMU) Linux Memory Layout.
+
+     See mmu-layout.txt in this directory for a description of the normal linux
+     memory layout
+
+     See include/asm-frv/mem-layout.h for constants pertaining to the memory
+     layout.
+
+     See include/asm-frv/mb-regs.h for the constants pertaining to the I/O bus
+     controller configuration.
+
+
+ (*) uClinux Memory Layout
+
+     The memory layout used by the uClinux kernel is as follows:
+
+       0x00000000 - 0x00000FFF         Null pointer catch page
+       0x20000000 - 0x200FFFFF CS2#    [PDK] FPGA
+       0xC0000000 - 0xCFFFFFFF         SDRAM
+       0xC0000000                      Base of Linux kernel image
+       0xE0000000 - 0xEFFFFFFF CS2#    [VDK] SLBUS/PCI window
+       0xF0000000 - 0xF0FFFFFF CS5#    MB93493 CSC area (DAV daughter board)
+       0xF1000000 - 0xF1FFFFFF CS7#    [CB70/CB451] CPU-card PCMCIA port space
+       0xFC000000 - 0xFC0FFFFF CS1#    [VDK] MB86943 config space
+       0xFC100000 - 0xFC1FFFFF CS6#    [CB70/CB451] CPU-card DM9000 NIC space
+       0xFC100000 - 0xFC1FFFFF CS6#    [PDK] AX88796 NIC space
+       0xFC200000 - 0xFC2FFFFF CS3#    MB93493 CSR area (DAV daughter board)
+       0xFD000000 - 0xFDFFFFFF CS4#    [CB70/CB451] CPU-card extra flash space
+       0xFE000000 - 0xFEFFFFFF         Internal CPU peripherals
+       0xFF000000 - 0xFF1FFFFF CS0#    Flash 1
+       0xFF200000 - 0xFF3FFFFF CS0#    Flash 2
+       0xFFC00000 - 0xFFC0001F CS0#    [VDK] FPGA
+
+     The kernel reads the size of the SDRAM from the memory bus controller
+     registers by default.
+
+     The kernel initialisation code (1) adjusts the SDRAM base addresses to
+     move the SDRAM to desired address, (2) moves the kernel image down to the
+     bottom of SDRAM, (3) adjusts the bus controller registers to move I/O
+     windows, and (4) rearranges the protection registers to protect all of
+     this.
+
+     The reasons for doing this are: (1) the page at address 0 should be
+     inaccessible so that NULL pointer errors can be caught; and (2) the bottom
+     three quarters are left unoccupied so that an FR-V CPU with an MMU can use
+     it for virtual userspace mappings.
+
+     See include/asm-frv/mem-layout.h for constants pertaining to the memory
+     layout.
+
+     See include/asm-frv/mb-regs.h for the constants pertaining to the I/O bus
+     controller configuration.
+
+
+ (*) uClinux Memory Protection
+
+     A DAMPR register is used to cover the entire region used for I/O
+     (0xE0000000 - 0xFFFFFFFF). This permits the kernel to make uncached
+     accesses to this region. Userspace is not permitted to access it.
+
+     The DAMPR/IAMPR protection registers not in use for any other purpose are
+     tiled over the top of the SDRAM such that:
+
+       (1) The core kernel image is covered by as small a tile as possible
+            granting only the kernel access to the underlying data, whilst
+            making sure no SDRAM is actually made unavailable by this approach.
+
+       (2) All other tiles are arranged to permit userspace access to the rest
+            of the SDRAM.
+
+     Barring point (1), there is nothing to protect kernel data against
+     userspace damage - but this is uClinux.
+
+
+ (*) Exceptions and Fixups
+
+     Since the FR40x and FR55x CPUs that do not have full MMUs generate
+     imprecise data error exceptions, there are currently no automatic fixup
+     services available in uClinux. This includes misaligned memory access
+     fixups.
+
+     Userspace EFAULT errors can be trapped by issuing a MEMBAR instruction and
+     forcing the fault to happen there.
+
+     On the FR451, however, data exceptions are mostly precise, and so
+     exception fixup handling is implemented as normal.
+
+
+ (*) Userspace Breakpoints
+
+     The ptrace() system call supports the following userspace debugging
+     features:
+
+       (1) Hardware assisted single step.
+
+       (2) Breakpoint via the FR-V "BREAK" instruction.
+
+       (3) Breakpoint via the FR-V "TIRA GR0, #1" instruction.
+
+       (4) Syscall entry/exit trap.
+
+     Each of the above generates a SIGTRAP.
+
+
+ (*) On-Chip Serial Ports
+
+     The FR-V on-chip serial ports are made available as ttyS0 and ttyS1. Note
+     that if the GDB stub is compiled in, ttyS1 will not actually be available
+     as it will be being used for the GDB stub.
+
+     These ports can be made by:
+
+       mknod /dev/ttyS0 c 4 64
+       mknod /dev/ttyS1 c 4 65
+
+
+ (*) Maskable Interrupts
+
+     Level 15 (Non-maskable) interrupts are dealt with by the GDB stub if
+     present, and cause a panic if not. If the GDB stub is present, ttyS1's
+     interrupts are rated at level 15.
+
+     All other interrupts are distributed over the set of available priorities
+     so that no IRQs are shared where possible. The arch interrupt handling
+     routines attempt to disentangle the various sources available through the
+     CPU's own multiplexor, and those on off-CPU peripherals.
+
+
+ (*) Accessing PCI Devices
+
+     Where PCI is available, care must be taken when dealing with drivers that
+     access PCI devices. PCI devices present their data in little-endian form,
+     but the CPU sees it in big-endian form. The macros in asm/io.h try to get
+     this right, but may not under all circumstances...
+
+
+ (*) Ax88796 Ethernet Driver
+
+     The MB93093 PDK board has an Ax88796 ethernet chipset (an NE2000 clone). A
+     driver has been written to deal specifically with this. The driver
+     provides MII services for the card.
+
+     The driver can be configured by running make xconfig, and going to:
+
+       (*) Network device support
+           - turn on "Network device support"
+           (*) Ethernet (10 or 100Mbit)
+               - turn on "Ethernet (10 or 100Mbit)"
+               - turn on "AX88796 NE2000 compatible chipset"
+
+     The driver can be found in:
+
+       drivers/net/ax88796.c
+       include/asm/ax88796.h
+
+
+ (*) WorkRAM Driver
+
+     This driver provides a character device that permits access to the WorkRAM
+     that can be found on the FR451 CPU. Each page is accessible through a
+     separate minor number, thereby permitting each page to have its own
+     filesystem permissions set on the device file.
+
+     The device files should be:
+
+       mknod /dev/frv/workram0 c 240 0
+       mknod /dev/frv/workram1 c 240 1
+       mknod /dev/frv/workram2 c 240 2
+       ...
+
+     The driver will not permit the opening of any device file that does not
+     correspond to at least a partial page of WorkRAM. So the first device file
+     is the only one available on the FR451. If any other CPU is detected, none
+     of the devices will be openable.
+
+     The devices can be accessed with read, write and llseek, and can also be
+     mmapped. If they're mmapped, they will only map at the appropriate
+     0x7e8nnnnn address on linux and at the 0xfe8nnnnn address on uClinux. If
+     MAP_FIXED is not specified, the appropriate address will be chosen anyway.
+
+     The mappings must be MAP_SHARED not MAP_PRIVATE, and must not be
+     PROT_EXEC. They must also start at file offset 0, and must not be longer
+     than one page in size.
+
+     This driver can be configured by running make xconfig, and going to:
+
+       (*) Character devices
+           - turn on "Fujitsu FR-V CPU WorkRAM support"
+
+
+ (*) Dynamic data cache write mode changing
+
+     It is possible to view and to change the data cache's write mode through
+     the /proc/sys/frv/cache-mode file while the kernel is running. There are
+     two modes available:
+
+       NAME    MEANING
+       =====   ==========================================
+       wthru   Data cache is in Write-Through mode
+       wback   Data cache is in Write-Back/Copy-Back mode
+
+     To read the cache mode:
+
+       # cat /proc/sys/frv/cache-mode
+       wthru
+
+     To change the cache mode:
+
+       # echo wback >/proc/sys/frv/cache-mode
+       # cat /proc/sys/frv/cache-mode
+       wback
+
+
+ (*) MMU Context IDs and Pinning
+
+     On MMU Linux the CPU supports the concept of a context ID in its MMU to
+     make it more efficient (TLB entries are labelled with a context ID to link
+     them to specific tasks).
+
+     Normally once a context ID is allocated, it will remain affixed to a task
+     or CLONE_VM'd group of tasks for as long as it exists. However, since the
+     kernel is capable of supporting more tasks than there are possible ID
+     numbers, the kernel will pass context IDs from one task to another if
+     there are insufficient available.
+
+     The context ID currently in use by a task can be viewed in /proc:
+
+       # grep CXNR /proc/1/status
+       CXNR: 1
+
+     Note that kernel threads do not have a userspace context, and so will not
+     show a CXNR entry in that file.
+
+     Under some circumstances, however, it is desirable to pin a context ID on
+     a process such that the kernel won't pass it on. This can be done by
+     writing the process ID of the target process to a special file:
+
+       # echo 17 >/proc/sys/frv/pin-cxnr
+
+     Reading from the file will then show the context ID pinned.
+
+       # cat /proc/sys/frv/pin-cxnr
+       4
+
+     The context ID will remain pinned as long as any process is using that
+     context, i.e.: when the all the subscribing processes have exited or
+     exec'd; or when an unpinning request happens:
+
+       # echo 0 >/proc/sys/frv/pin-cxnr
+
+     When there isn't a pinned context, the file shows -1:
+
+       # cat /proc/sys/frv/pin-cxnr
+       -1
diff --git a/Documentation/fujitsu/frv/gdbinit b/Documentation/fujitsu/frv/gdbinit
new file mode 100644 (file)
index 0000000..51517b6
--- /dev/null
@@ -0,0 +1,102 @@
+set remotebreak 1
+
+define _amr
+
+printf "AMRx           DAMR                    IAMR         \n"
+printf "====   =====================   =====================\n"
+printf "amr0 : L:%08lx P:%08lx : L:%08lx P:%08lx\n",__debug_mmu.damr[0x0].L,__debug_mmu.damr[0x0].P,__debug_mmu.iamr[0x0].L,__debug_mmu.iamr[0x0].P
+printf "amr1 : L:%08lx P:%08lx : L:%08lx P:%08lx\n",__debug_mmu.damr[0x1].L,__debug_mmu.damr[0x1].P,__debug_mmu.iamr[0x1].L,__debug_mmu.iamr[0x1].P
+printf "amr2 : L:%08lx P:%08lx : L:%08lx P:%08lx\n",__debug_mmu.damr[0x2].L,__debug_mmu.damr[0x2].P,__debug_mmu.iamr[0x2].L,__debug_mmu.iamr[0x2].P
+printf "amr3 : L:%08lx P:%08lx : L:%08lx P:%08lx\n",__debug_mmu.damr[0x3].L,__debug_mmu.damr[0x3].P,__debug_mmu.iamr[0x3].L,__debug_mmu.iamr[0x3].P
+printf "amr4 : L:%08lx P:%08lx : L:%08lx P:%08lx\n",__debug_mmu.damr[0x4].L,__debug_mmu.damr[0x4].P,__debug_mmu.iamr[0x4].L,__debug_mmu.iamr[0x4].P
+printf "amr5 : L:%08lx P:%08lx : L:%08lx P:%08lx\n",__debug_mmu.damr[0x5].L,__debug_mmu.damr[0x5].P,__debug_mmu.iamr[0x5].L,__debug_mmu.iamr[0x5].P
+printf "amr6 : L:%08lx P:%08lx : L:%08lx P:%08lx\n",__debug_mmu.damr[0x6].L,__debug_mmu.damr[0x6].P,__debug_mmu.iamr[0x6].L,__debug_mmu.iamr[0x6].P
+printf "amr7 : L:%08lx P:%08lx : L:%08lx P:%08lx\n",__debug_mmu.damr[0x7].L,__debug_mmu.damr[0x7].P,__debug_mmu.iamr[0x7].L,__debug_mmu.iamr[0x7].P
+
+printf "amr8 : L:%08lx P:%08lx\n",__debug_mmu.damr[0x8].L,__debug_mmu.damr[0x8].P
+printf "amr9 : L:%08lx P:%08lx\n",__debug_mmu.damr[0x9].L,__debug_mmu.damr[0x9].P
+printf "amr10: L:%08lx P:%08lx\n",__debug_mmu.damr[0xa].L,__debug_mmu.damr[0xa].P
+printf "amr11: L:%08lx P:%08lx\n",__debug_mmu.damr[0xb].L,__debug_mmu.damr[0xb].P
+
+end
+
+
+define _tlb
+printf "tlb[0x00]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x0].L,__debug_mmu.tlb[0x0].P,__debug_mmu.tlb[0x40+0x0].L,__debug_mmu.tlb[0x40+0x0].P
+printf "tlb[0x01]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x1].L,__debug_mmu.tlb[0x1].P,__debug_mmu.tlb[0x40+0x1].L,__debug_mmu.tlb[0x40+0x1].P
+printf "tlb[0x02]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x2].L,__debug_mmu.tlb[0x2].P,__debug_mmu.tlb[0x40+0x2].L,__debug_mmu.tlb[0x40+0x2].P
+printf "tlb[0x03]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x3].L,__debug_mmu.tlb[0x3].P,__debug_mmu.tlb[0x40+0x3].L,__debug_mmu.tlb[0x40+0x3].P
+printf "tlb[0x04]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x4].L,__debug_mmu.tlb[0x4].P,__debug_mmu.tlb[0x40+0x4].L,__debug_mmu.tlb[0x40+0x4].P
+printf "tlb[0x05]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x5].L,__debug_mmu.tlb[0x5].P,__debug_mmu.tlb[0x40+0x5].L,__debug_mmu.tlb[0x40+0x5].P
+printf "tlb[0x06]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x6].L,__debug_mmu.tlb[0x6].P,__debug_mmu.tlb[0x40+0x6].L,__debug_mmu.tlb[0x40+0x6].P
+printf "tlb[0x07]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x7].L,__debug_mmu.tlb[0x7].P,__debug_mmu.tlb[0x40+0x7].L,__debug_mmu.tlb[0x40+0x7].P
+printf "tlb[0x08]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x8].L,__debug_mmu.tlb[0x8].P,__debug_mmu.tlb[0x40+0x8].L,__debug_mmu.tlb[0x40+0x8].P
+printf "tlb[0x09]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x9].L,__debug_mmu.tlb[0x9].P,__debug_mmu.tlb[0x40+0x9].L,__debug_mmu.tlb[0x40+0x9].P
+printf "tlb[0x0a]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0xa].L,__debug_mmu.tlb[0xa].P,__debug_mmu.tlb[0x40+0xa].L,__debug_mmu.tlb[0x40+0xa].P
+printf "tlb[0x0b]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0xb].L,__debug_mmu.tlb[0xb].P,__debug_mmu.tlb[0x40+0xb].L,__debug_mmu.tlb[0x40+0xb].P
+printf "tlb[0x0c]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0xc].L,__debug_mmu.tlb[0xc].P,__debug_mmu.tlb[0x40+0xc].L,__debug_mmu.tlb[0x40+0xc].P
+printf "tlb[0x0d]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0xd].L,__debug_mmu.tlb[0xd].P,__debug_mmu.tlb[0x40+0xd].L,__debug_mmu.tlb[0x40+0xd].P
+printf "tlb[0x0e]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0xe].L,__debug_mmu.tlb[0xe].P,__debug_mmu.tlb[0x40+0xe].L,__debug_mmu.tlb[0x40+0xe].P
+printf "tlb[0x0f]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0xf].L,__debug_mmu.tlb[0xf].P,__debug_mmu.tlb[0x40+0xf].L,__debug_mmu.tlb[0x40+0xf].P
+printf "tlb[0x10]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x10].L,__debug_mmu.tlb[0x10].P,__debug_mmu.tlb[0x40+0x10].L,__debug_mmu.tlb[0x40+0x10].P
+printf "tlb[0x11]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x11].L,__debug_mmu.tlb[0x11].P,__debug_mmu.tlb[0x40+0x11].L,__debug_mmu.tlb[0x40+0x11].P
+printf "tlb[0x12]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x12].L,__debug_mmu.tlb[0x12].P,__debug_mmu.tlb[0x40+0x12].L,__debug_mmu.tlb[0x40+0x12].P
+printf "tlb[0x13]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x13].L,__debug_mmu.tlb[0x13].P,__debug_mmu.tlb[0x40+0x13].L,__debug_mmu.tlb[0x40+0x13].P
+printf "tlb[0x14]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x14].L,__debug_mmu.tlb[0x14].P,__debug_mmu.tlb[0x40+0x14].L,__debug_mmu.tlb[0x40+0x14].P
+printf "tlb[0x15]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x15].L,__debug_mmu.tlb[0x15].P,__debug_mmu.tlb[0x40+0x15].L,__debug_mmu.tlb[0x40+0x15].P
+printf "tlb[0x16]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x16].L,__debug_mmu.tlb[0x16].P,__debug_mmu.tlb[0x40+0x16].L,__debug_mmu.tlb[0x40+0x16].P
+printf "tlb[0x17]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x17].L,__debug_mmu.tlb[0x17].P,__debug_mmu.tlb[0x40+0x17].L,__debug_mmu.tlb[0x40+0x17].P
+printf "tlb[0x18]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x18].L,__debug_mmu.tlb[0x18].P,__debug_mmu.tlb[0x40+0x18].L,__debug_mmu.tlb[0x40+0x18].P
+printf "tlb[0x19]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x19].L,__debug_mmu.tlb[0x19].P,__debug_mmu.tlb[0x40+0x19].L,__debug_mmu.tlb[0x40+0x19].P
+printf "tlb[0x1a]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x1a].L,__debug_mmu.tlb[0x1a].P,__debug_mmu.tlb[0x40+0x1a].L,__debug_mmu.tlb[0x40+0x1a].P
+printf "tlb[0x1b]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x1b].L,__debug_mmu.tlb[0x1b].P,__debug_mmu.tlb[0x40+0x1b].L,__debug_mmu.tlb[0x40+0x1b].P
+printf "tlb[0x1c]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x1c].L,__debug_mmu.tlb[0x1c].P,__debug_mmu.tlb[0x40+0x1c].L,__debug_mmu.tlb[0x40+0x1c].P
+printf "tlb[0x1d]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x1d].L,__debug_mmu.tlb[0x1d].P,__debug_mmu.tlb[0x40+0x1d].L,__debug_mmu.tlb[0x40+0x1d].P
+printf "tlb[0x1e]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x1e].L,__debug_mmu.tlb[0x1e].P,__debug_mmu.tlb[0x40+0x1e].L,__debug_mmu.tlb[0x40+0x1e].P
+printf "tlb[0x1f]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x1f].L,__debug_mmu.tlb[0x1f].P,__debug_mmu.tlb[0x40+0x1f].L,__debug_mmu.tlb[0x40+0x1f].P
+printf "tlb[0x20]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x20].L,__debug_mmu.tlb[0x20].P,__debug_mmu.tlb[0x40+0x20].L,__debug_mmu.tlb[0x40+0x20].P
+printf "tlb[0x21]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x21].L,__debug_mmu.tlb[0x21].P,__debug_mmu.tlb[0x40+0x21].L,__debug_mmu.tlb[0x40+0x21].P
+printf "tlb[0x22]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x22].L,__debug_mmu.tlb[0x22].P,__debug_mmu.tlb[0x40+0x22].L,__debug_mmu.tlb[0x40+0x22].P
+printf "tlb[0x23]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x23].L,__debug_mmu.tlb[0x23].P,__debug_mmu.tlb[0x40+0x23].L,__debug_mmu.tlb[0x40+0x23].P
+printf "tlb[0x24]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x24].L,__debug_mmu.tlb[0x24].P,__debug_mmu.tlb[0x40+0x24].L,__debug_mmu.tlb[0x40+0x24].P
+printf "tlb[0x25]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x25].L,__debug_mmu.tlb[0x25].P,__debug_mmu.tlb[0x40+0x25].L,__debug_mmu.tlb[0x40+0x25].P
+printf "tlb[0x26]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x26].L,__debug_mmu.tlb[0x26].P,__debug_mmu.tlb[0x40+0x26].L,__debug_mmu.tlb[0x40+0x26].P
+printf "tlb[0x27]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x27].L,__debug_mmu.tlb[0x27].P,__debug_mmu.tlb[0x40+0x27].L,__debug_mmu.tlb[0x40+0x27].P
+printf "tlb[0x28]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x28].L,__debug_mmu.tlb[0x28].P,__debug_mmu.tlb[0x40+0x28].L,__debug_mmu.tlb[0x40+0x28].P
+printf "tlb[0x29]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x29].L,__debug_mmu.tlb[0x29].P,__debug_mmu.tlb[0x40+0x29].L,__debug_mmu.tlb[0x40+0x29].P
+printf "tlb[0x2a]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x2a].L,__debug_mmu.tlb[0x2a].P,__debug_mmu.tlb[0x40+0x2a].L,__debug_mmu.tlb[0x40+0x2a].P
+printf "tlb[0x2b]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x2b].L,__debug_mmu.tlb[0x2b].P,__debug_mmu.tlb[0x40+0x2b].L,__debug_mmu.tlb[0x40+0x2b].P
+printf "tlb[0x2c]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x2c].L,__debug_mmu.tlb[0x2c].P,__debug_mmu.tlb[0x40+0x2c].L,__debug_mmu.tlb[0x40+0x2c].P
+printf "tlb[0x2d]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x2d].L,__debug_mmu.tlb[0x2d].P,__debug_mmu.tlb[0x40+0x2d].L,__debug_mmu.tlb[0x40+0x2d].P
+printf "tlb[0x2e]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x2e].L,__debug_mmu.tlb[0x2e].P,__debug_mmu.tlb[0x40+0x2e].L,__debug_mmu.tlb[0x40+0x2e].P
+printf "tlb[0x2f]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x2f].L,__debug_mmu.tlb[0x2f].P,__debug_mmu.tlb[0x40+0x2f].L,__debug_mmu.tlb[0x40+0x2f].P
+printf "tlb[0x30]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x30].L,__debug_mmu.tlb[0x30].P,__debug_mmu.tlb[0x40+0x30].L,__debug_mmu.tlb[0x40+0x30].P
+printf "tlb[0x31]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x31].L,__debug_mmu.tlb[0x31].P,__debug_mmu.tlb[0x40+0x31].L,__debug_mmu.tlb[0x40+0x31].P
+printf "tlb[0x32]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x32].L,__debug_mmu.tlb[0x32].P,__debug_mmu.tlb[0x40+0x32].L,__debug_mmu.tlb[0x40+0x32].P
+printf "tlb[0x33]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x33].L,__debug_mmu.tlb[0x33].P,__debug_mmu.tlb[0x40+0x33].L,__debug_mmu.tlb[0x40+0x33].P
+printf "tlb[0x34]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x34].L,__debug_mmu.tlb[0x34].P,__debug_mmu.tlb[0x40+0x34].L,__debug_mmu.tlb[0x40+0x34].P
+printf "tlb[0x35]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x35].L,__debug_mmu.tlb[0x35].P,__debug_mmu.tlb[0x40+0x35].L,__debug_mmu.tlb[0x40+0x35].P
+printf "tlb[0x36]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x36].L,__debug_mmu.tlb[0x36].P,__debug_mmu.tlb[0x40+0x36].L,__debug_mmu.tlb[0x40+0x36].P
+printf "tlb[0x37]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x37].L,__debug_mmu.tlb[0x37].P,__debug_mmu.tlb[0x40+0x37].L,__debug_mmu.tlb[0x40+0x37].P
+printf "tlb[0x38]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x38].L,__debug_mmu.tlb[0x38].P,__debug_mmu.tlb[0x40+0x38].L,__debug_mmu.tlb[0x40+0x38].P
+printf "tlb[0x39]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x39].L,__debug_mmu.tlb[0x39].P,__debug_mmu.tlb[0x40+0x39].L,__debug_mmu.tlb[0x40+0x39].P
+printf "tlb[0x3a]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x3a].L,__debug_mmu.tlb[0x3a].P,__debug_mmu.tlb[0x40+0x3a].L,__debug_mmu.tlb[0x40+0x3a].P
+printf "tlb[0x3b]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x3b].L,__debug_mmu.tlb[0x3b].P,__debug_mmu.tlb[0x40+0x3b].L,__debug_mmu.tlb[0x40+0x3b].P
+printf "tlb[0x3c]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x3c].L,__debug_mmu.tlb[0x3c].P,__debug_mmu.tlb[0x40+0x3c].L,__debug_mmu.tlb[0x40+0x3c].P
+printf "tlb[0x3d]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x3d].L,__debug_mmu.tlb[0x3d].P,__debug_mmu.tlb[0x40+0x3d].L,__debug_mmu.tlb[0x40+0x3d].P
+printf "tlb[0x3e]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x3e].L,__debug_mmu.tlb[0x3e].P,__debug_mmu.tlb[0x40+0x3e].L,__debug_mmu.tlb[0x40+0x3e].P
+printf "tlb[0x3f]: %08lx %08lx  %08lx %08lx\n",__debug_mmu.tlb[0x3f].L,__debug_mmu.tlb[0x3f].P,__debug_mmu.tlb[0x40+0x3f].L,__debug_mmu.tlb[0x40+0x3f].P
+end
+
+
+define _pgd
+p (pgd_t[0x40])*(pgd_t*)(__debug_mmu.damr[0x3].L)
+end
+
+define _ptd_i
+p (pte_t[0x1000])*(pte_t*)(__debug_mmu.damr[0x4].L)
+end
+
+define _ptd_d
+p (pte_t[0x1000])*(pte_t*)(__debug_mmu.damr[0x5].L)
+end
diff --git a/Documentation/fujitsu/frv/gdbstub.txt b/Documentation/fujitsu/frv/gdbstub.txt
new file mode 100644 (file)
index 0000000..6ce5aa9
--- /dev/null
@@ -0,0 +1,130 @@
+                            ====================
+                            DEBUGGING FR-V LINUX
+                            ====================
+
+
+The kernel contains a GDB stub that talks GDB remote protocol across a serial
+port. This permits GDB to single step through the kernel, set breakpoints and
+trap exceptions that happen in kernel space and interrupt execution. It also
+permits the NMI interrupt button or serial port events to jump the kernel into
+the debugger.
+
+On the CPUs that have on-chip UARTs (FR400, FR403, FR405, FR555), the
+GDB stub hijacks a serial port for its own purposes, and makes it
+generate level 15 interrupts (NMI). The kernel proper cannot see the serial
+port in question under these conditions.
+
+On the MB93091-VDK CPU boards, the GDB stub uses UART1, which would otherwise
+be /dev/ttyS1. On the MB93093-PDK, the GDB stub uses UART0. Therefore, on the
+PDK there is no externally accessible serial port and the serial port to
+which the touch screen is attached becomes /dev/ttyS0.
+
+Note that the GDB stub runs entirely within CPU debug mode, and so should not
+incur any exceptions or interrupts whilst it is active. In particular, note
+that the clock will lose time since it is implemented in software.
+
+
+==================
+KERNEL PREPARATION
+==================
+
+Firstly, a debuggable kernel must be built. To do this, unpack the kernel tree
+and copy the configuration that you wish to use to .config. Then reconfigure
+the following things on the "Kernel Hacking" tab:
+
+  (*) "Include debugging information"
+
+      Set this to "Y". This causes all C and Assembly files to be compiled
+      to include debugging information.
+
+  (*) "In-kernel GDB stub"
+
+      Set this to "Y". This causes the GDB stub to be compiled into the
+      kernel.
+
+  (*) "Immediate activation"
+
+      Set this to "Y" if you want the GDB stub to activate as soon as possible
+      and wait for GDB to connect. This allows you to start tracing right from
+      the beginning of start_kernel() in init/main.c.
+
+  (*) "Console through GDB stub"
+
+      Set this to "Y" if you wish to be able to use "console=gdb0" on the
+      command line. That tells the kernel to pass system console messages to
+      GDB (which then prints them on its standard output). This is useful when
+      debugging the serial drivers that'd otherwise be used to pass console
+      messages to the outside world.
+
+Then build as usual, download to the board and execute. Note that if
+"Immediate activation" was selected, then the kernel will wait for GDB to
+attach. If not, then the kernel will boot immediately and GDB will have to
+interupt it or wait for an exception to occur if before doing anything with
+the kernel.
+
+
+=========================
+KERNEL DEBUGGING WITH GDB
+=========================
+
+Set the serial port on the computer that's going to run GDB to the appropriate
+baud rate. Assuming the board's debug port is connected to ttyS0/COM1 on the
+computer doing the debugging:
+
+       stty -F /dev/ttyS0 115200
+
+Then start GDB in the base of the kernel tree:
+
+       frv-uclinux-gdb linux           [uClinux]
+
+Or:
+
+       frv-uclinux-gdb vmlinux         [MMU linux]
+
+When the prompt appears:
+
+       GNU gdb frv-031024
+       Copyright 2003 Free Software Foundation, Inc.
+       GDB is free software, covered by the GNU General Public License, and you are
+       welcome to change it and/or distribute copies of it under certain conditions.
+       Type "show copying" to see the conditions.
+       There is absolutely no warranty for GDB.  Type "show warranty" for details.
+       This GDB was configured as "--host=i686-pc-linux-gnu --target=frv-uclinux"...
+       (gdb)
+
+Attach to the board like this:
+
+        (gdb) target remote /dev/ttyS0
+       Remote debugging using /dev/ttyS0
+       start_kernel () at init/main.c:395
+       (gdb)
+
+This should show the appropriate lines from the source too. The kernel can
+then be debugged almost as if it's any other program.
+
+
+===============================
+INTERRUPTING THE RUNNING KERNEL
+===============================
+
+The kernel can be interrupted whilst it is running, causing a jump back to the
+GDB stub and the debugger:
+
+  (*) Pressing Ctrl-C in GDB. This will cause GDB to try and interrupt the
+      kernel by sending an RS232 BREAK over the serial line to the GDB
+      stub. This will (mostly) immediately interrupt the kernel and return it
+      to the debugger.
+
+  (*) Pressing the NMI button on the board will also cause a jump into the
+      debugger.
+
+  (*) Setting a software breakpoint. This sets a break instruction at the
+      desired location which the GDB stub then traps the exception for.
+
+  (*) Setting a hardware breakpoint. The GDB stub is capable of using the IBAR
+      and DBAR registers to assist debugging.
+
+Furthermore, the GDB stub will intercept a number of exceptions automatically
+if they are caused by kernel execution. It will also intercept BUG() macro
+invokation.
+
diff --git a/Documentation/fujitsu/frv/mmu-layout.txt b/Documentation/fujitsu/frv/mmu-layout.txt
new file mode 100644 (file)
index 0000000..11dcc56
--- /dev/null
@@ -0,0 +1,306 @@
+                                =================================
+                                FR451 MMU LINUX MEMORY MANAGEMENT
+                                =================================
+
+============
+MMU HARDWARE
+============
+
+FR451 MMU Linux puts the MMU into EDAT mode whilst running. This means that it uses both the SAT
+registers and the DAT TLB to perform address translation.
+
+There are 8 IAMLR/IAMPR register pairs and 16 DAMLR/DAMPR register pairs for SAT mode.
+
+In DAT mode, there is also a TLB organised in cache format as 64 lines x 2 ways. Each line spans a
+16KB range of addresses, but can match a larger region.
+
+
+===========================
+MEMORY MANAGEMENT REGISTERS
+===========================
+
+Certain control registers are used by the kernel memory management routines:
+
+       REGISTERS               USAGE
+       ======================  ==================================================
+       IAMR0, DAMR0            Kernel image and data mappings
+       IAMR1, DAMR1            First-chance TLB lookup mapping
+       DAMR2                   Page attachment for cache flush by page
+       DAMR3                   Current PGD mapping
+       SCR0, DAMR4             Instruction TLB PGE/PTD cache
+       SCR1, DAMR5             Data TLB PGE/PTD cache
+       DAMR6-10                kmap_atomic() mappings
+       DAMR11                  I/O mapping
+       CXNR                    mm_struct context ID
+       TTBR                    Page directory (PGD) pointer (physical address)
+
+
+=====================
+GENERAL MEMORY LAYOUT
+=====================
+
+The physical memory layout is as follows:
+
+  PHYSICAL ADDRESS     CONTROLLER      DEVICE
+  ===================  ==============  =======================================
+  00000000 - BFFFFFFF  SDRAM           SDRAM area
+  E0000000 - EFFFFFFF  L-BUS CS2#      VDK SLBUS/PCI window
+  F0000000 - F0FFFFFF  L-BUS CS5#      MB93493 CSC area (DAV daughter board)
+  F1000000 - F1FFFFFF  L-BUS CS7#      (CB70 CPU-card PCMCIA port I/O space)
+  FC000000 - FC0FFFFF  L-BUS CS1#      VDK MB86943 config space
+  FC100000 - FC1FFFFF  L-BUS CS6#      DM9000 NIC I/O space
+  FC200000 - FC2FFFFF  L-BUS CS3#      MB93493 CSR area (DAV daughter board)
+  FD000000 - FDFFFFFF  L-BUS CS4#      (CB70 CPU-card extra flash space)
+  FE000000 - FEFFFFFF                  Internal CPU peripherals
+  FF000000 - FF1FFFFF  L-BUS CS0#      Flash 1
+  FF200000 - FF3FFFFF  L-BUS CS0#      Flash 2
+  FFC00000 - FFC0001F  L-BUS CS0#      FPGA
+
+The virtual memory layout is:
+
+  VIRTUAL ADDRESS    PHYSICAL  TRANSLATOR      FLAGS   SIZE    OCCUPATION
+  =================  ========  ==============  ======= ======= ===================================
+  00004000-BFFFFFFF  various   TLB,xAMR1       D-N-??V 3GB     Userspace
+  C0000000-CFFFFFFF  00000000  xAMPR0          -L-S--V 256MB   Kernel image and data
+  D0000000-D7FFFFFF  various   TLB,xAMR1       D-NS??V 128MB   vmalloc area
+  D8000000-DBFFFFFF  various   TLB,xAMR1       D-NS??V 64MB    kmap() area
+  DC000000-DCFFFFFF  various   TLB                     1MB     Secondary kmap_atomic() frame
+  DD000000-DD27FFFF  various   DAMR                    160KB   Primary kmap_atomic() frame
+  DD040000                     DAMR2/IAMR2     -L-S--V page    Page cache flush attachment point
+  DD080000                     DAMR3           -L-SC-V page    Page Directory (PGD)
+  DD0C0000                     DAMR4           -L-SC-V page    Cached insn TLB Page Table lookup
+  DD100000                     DAMR5           -L-SC-V page    Cached data TLB Page Table lookup
+  DD140000                     DAMR6           -L-S--V page    kmap_atomic(KM_BOUNCE_READ)
+  DD180000                     DAMR7           -L-S--V page    kmap_atomic(KM_SKB_SUNRPC_DATA)
+  DD1C0000                     DAMR8           -L-S--V page    kmap_atomic(KM_SKB_DATA_SOFTIRQ)
+  DD200000                     DAMR9           -L-S--V page    kmap_atomic(KM_USER0)
+  DD240000                     DAMR10          -L-S--V page    kmap_atomic(KM_USER1)
+  E0000000-FFFFFFFF  E0000000  DAMR11          -L-SC-V 512MB   I/O region
+
+IAMPR1 and DAMPR1 are used as an extension to the TLB.
+
+
+====================
+KMAP AND KMAP_ATOMIC
+====================
+
+To access pages in the page cache (which may not be directly accessible if highmem is available),
+the kernel calls kmap(), does the access and then calls kunmap(); or it calls kmap_atomic(), does
+the access and then calls kunmap_atomic().
+
+kmap() creates an attachment between an arbitrary inaccessible page and a range of virtual
+addresses by installing a PTE in a special page table. The kernel can then access this page as it
+wills. When it's finished, the kernel calls kunmap() to clear the PTE.
+
+kmap_atomic() does something slightly different. In the interests of speed, it chooses one of two
+strategies:
+
+ (1) If possible, kmap_atomic() attaches the requested page to one of DAMPR5 through DAMPR10
+     register pairs; and the matching kunmap_atomic() clears the DAMPR. This makes high memory
+     support really fast as there's no need to flush the TLB or modify the page tables. The DAMLR
+     registers being used for this are preset during boot and don't change over the lifetime of the
+     process. There's a direct mapping between the first few kmap_atomic() types, DAMR number and
+     virtual address slot.
+
+     However, there are more kmap_atomic() types defined than there are DAMR registers available,
+     so we fall back to:
+
+ (2) kmap_atomic() uses a slot in the secondary frame (determined by the type parameter), and then
+     locks an entry in the TLB to translate that slot to the specified page. The number of slots is
+     obviously limited, and their positions are controlled such that each slot is matched by a
+     different line in the TLB. kunmap() ejects the entry from the TLB.
+
+Note that the first three kmap atomic types are really just declared as placeholders. The DAMPR
+registers involved are actually modified directly.
+
+Also note that kmap() itself may sleep, kmap_atomic() may never sleep and both always succeed;
+furthermore, a driver using kmap() may sleep before calling kunmap(), but may not sleep before
+calling kunmap_atomic() if it had previously called kmap_atomic().
+
+
+===============================
+USING MORE THAN 256MB OF MEMORY
+===============================
+
+The kernel cannot access more than 256MB of memory directly. The physical layout, however, permits
+up to 3GB of SDRAM (possibly 3.25GB) to be made available. By using CONFIG_HIGHMEM, the kernel can
+allow userspace (by way of page tables) and itself (by way of kmap) to deal with the memory
+allocation.
+
+External devices can, of course, still DMA to and from all of the SDRAM, even if the kernel can't
+see it directly. The kernel translates page references into real addresses for communicating to the
+devices.
+
+
+===================
+PAGE TABLE TOPOLOGY
+===================
+
+The page tables are arranged in 2-layer format. There is a middle layer (PMD) that would be used in
+3-layer format tables but that is folded into the top layer (PGD) and so consumes no extra memory
+or processing power.
+
+  +------+     PGD    PMD
+  | TTBR |--->+-------------------+
+  +------+    |      |      : STE |
+              | PGE0 | PME0 : STE |
+              |      |      : STE |
+              +-------------------+              Page Table
+              |      |      : STE -------------->+--------+ +0x0000
+              | PGE1 | PME0 : STE -----------+   | PTE0   |
+              |      |      : STE -------+   |   +--------+
+              +-------------------+      |   |   | PTE63  |
+              |      |      : STE |      |   +-->+--------+ +0x0100
+              | PGE2 | PME0 : STE |      |       | PTE64  |
+              |      |      : STE |      |       +--------+
+              +-------------------+      |       | PTE127 |
+              |      |      : STE |      +------>+--------+ +0x0200
+              | PGE3 | PME0 : STE |              | PTE128 |
+              |      |      : STE |              +--------+
+              +-------------------+              | PTE191 |
+                                                 +--------+ +0x0300
+
+Each Page Directory (PGD) is 16KB (page size) in size and is divided into 64 entries (PGEs). Each
+PGE contains one Page Mid Directory (PMD).
+
+Each PMD is 256 bytes in size and contains a single entry (PME). Each PME holds 64 FR451 MMU
+segment table entries of 4 bytes apiece. Each PME "points to" a page table. In practice, each STE
+points to a subset of the page table, the first to PT+0x0000, the second to PT+0x0100, the third to
+PT+0x200, and so on.
+
+Each PGE and PME covers 64MB of the total virtual address space.
+
+Each Page Table (PTD) is 16KB (page size) in size, and is divided into 4096 entries (PTEs). Each
+entry can point to one 16KB page. In practice, each Linux page table is subdivided into 64 FR451
+MMU page tables. But they are all grouped together to make management easier, in particular rmap
+support is then trivial.
+
+Grouping page tables in this fashion makes PGE caching in SCR0/SCR1 more efficient because the
+coverage of the cached item is greater.
+
+Page tables for the vmalloc area are allocated at boot time and shared between all mm_structs.
+
+
+=================
+USER SPACE LAYOUT
+=================
+
+For MMU capable Linux, the regions userspace code are allowed to access are kept entirely separate
+from those dedicated to the kernel:
+
+       VIRTUAL ADDRESS    SIZE   PURPOSE
+       =================  =====  ===================================
+       00000000-00003fff  4KB    NULL pointer access trap
+       00004000-01ffffff  ~32MB  lower mmap space (grows up)
+       02000000-021fffff  2MB    Stack space (grows down from top)
+       02200000-nnnnnnnn         Executable mapping
+        nnnnnnnn-                 brk space (grows up)
+               -bfffffff         upper mmap space (grows down)
+
+This is so arranged so as to make best use of the 16KB page tables and the way in which PGEs/PMEs
+are cached by the TLB handler. The lower mmap space is filled first, and then the upper mmap space
+is filled.
+
+
+===============================
+GDB-STUB MMU DEBUGGING SERVICES
+===============================
+
+The gdb-stub included in this kernel provides a number of services to aid in the debugging of MMU
+related kernel services:
+
+ (*) Every time the kernel stops, certain state information is dumped into __debug_mmu. This
+     variable is defined in arch/frv/kernel/gdb-stub.c. Note that the gdbinit file in this
+     directory has some useful macros for dealing with this.
+
+     (*) __debug_mmu.tlb[]
+
+        This receives the current TLB contents. This can be viewed with the _tlb GDB macro:
+
+               (gdb) _tlb
+               tlb[0x00]: 01000005 00718203  01000002 00718203
+               tlb[0x01]: 01004002 006d4201  01004005 006d4203
+               tlb[0x02]: 01008002 006d0201  01008006 00004200
+               tlb[0x03]: 0100c006 007f4202  0100c002 0064c202
+               tlb[0x04]: 01110005 00774201  01110002 00774201
+               tlb[0x05]: 01114005 00770201  01114002 00770201
+               tlb[0x06]: 01118002 0076c201  01118005 0076c201
+               ...
+               tlb[0x3d]: 010f4002 00790200  001f4002 0054ca02
+               tlb[0x3e]: 010f8005 0078c201  010f8002 0078c201
+               tlb[0x3f]: 001fc002 0056ca01  001fc005 00538a01
+
+     (*) __debug_mmu.iamr[]
+     (*) __debug_mmu.damr[]
+
+        These receive the current IAMR and DAMR contents. These can be viewed with with the _amr
+        GDB macro:
+
+               (gdb) _amr
+               AMRx           DAMR                    IAMR
+               ====   =====================   =====================
+               amr0 : L:c0000000 P:00000cb9 : L:c0000000 P:000004b9
+               amr1 : L:01070005 P:006f9203 : L:0102c005 P:006a1201
+               amr2 : L:d8d00000 P:00000000 : L:d8d00000 P:00000000
+               amr3 : L:d8d04000 P:00534c0d : L:00000000 P:00000000
+               amr4 : L:d8d08000 P:00554c0d : L:00000000 P:00000000
+               amr5 : L:d8d0c000 P:00554c0d : L:00000000 P:00000000
+               amr6 : L:d8d10000 P:00000000 : L:00000000 P:00000000
+               amr7 : L:d8d14000 P:00000000 : L:00000000 P:00000000
+               amr8 : L:d8d18000 P:00000000
+               amr9 : L:d8d1c000 P:00000000
+               amr10: L:d8d20000 P:00000000
+               amr11: L:e0000000 P:e0000ccd
+
+ (*) The current task's page directory is bound to DAMR3.
+
+     This can be viewed with the _pgd GDB macro:
+
+       (gdb) _pgd
+       $3 = {{pge = {{ste = {0x554001, 0x554101, 0x554201, 0x554301, 0x554401,
+                 0x554501, 0x554601, 0x554701, 0x554801, 0x554901, 0x554a01,
+                 0x554b01, 0x554c01, 0x554d01, 0x554e01, 0x554f01, 0x555001,
+                 0x555101, 0x555201, 0x555301, 0x555401, 0x555501, 0x555601,
+                 0x555701, 0x555801, 0x555901, 0x555a01, 0x555b01, 0x555c01,
+                 0x555d01, 0x555e01, 0x555f01, 0x556001, 0x556101, 0x556201,
+                 0x556301, 0x556401, 0x556501, 0x556601, 0x556701, 0x556801,
+                 0x556901, 0x556a01, 0x556b01, 0x556c01, 0x556d01, 0x556e01,
+                 0x556f01, 0x557001, 0x557101, 0x557201, 0x557301, 0x557401,
+                 0x557501, 0x557601, 0x557701, 0x557801, 0x557901, 0x557a01,
+                 0x557b01, 0x557c01, 0x557d01, 0x557e01, 0x557f01}}}}, {pge = {{
+               ste = {0x0 <repeats 64 times>}}}} <repeats 51 times>, {pge = {{ste = {
+                 0x248001, 0x248101, 0x248201, 0x248301, 0x248401, 0x248501,
+                 0x248601, 0x248701, 0x248801, 0x248901, 0x248a01, 0x248b01,
+                 0x248c01, 0x248d01, 0x248e01, 0x248f01, 0x249001, 0x249101,
+                 0x249201, 0x249301, 0x249401, 0x249501, 0x249601, 0x249701,
+                 0x249801, 0x249901, 0x249a01, 0x249b01, 0x249c01, 0x249d01,
+                 0x249e01, 0x249f01, 0x24a001, 0x24a101, 0x24a201, 0x24a301,
+                 0x24a401, 0x24a501, 0x24a601, 0x24a701, 0x24a801, 0x24a901,
+                 0x24aa01, 0x24ab01, 0x24ac01, 0x24ad01, 0x24ae01, 0x24af01,
+                 0x24b001, 0x24b101, 0x24b201, 0x24b301, 0x24b401, 0x24b501,
+                 0x24b601, 0x24b701, 0x24b801, 0x24b901, 0x24ba01, 0x24bb01,
+                 0x24bc01, 0x24bd01, 0x24be01, 0x24bf01}}}}, {pge = {{ste = {
+                 0x0 <repeats 64 times>}}}} <repeats 11 times>}
+
+ (*) The PTD last used by the instruction TLB miss handler is attached to DAMR4.
+ (*) The PTD last used by the data TLB miss handler is attached to DAMR5.
+
+     These can be viewed with the _ptd_i and _ptd_d GDB macros:
+
+       (gdb) _ptd_d
+       $5 = {{pte = 0x0} <repeats 127 times>, {pte = 0x539b01}, {
+           pte = 0x0} <repeats 896 times>, {pte = 0x719303}, {pte = 0x6d5303}, {
+           pte = 0x0}, {pte = 0x0}, {pte = 0x0}, {pte = 0x0}, {pte = 0x0}, {
+           pte = 0x0}, {pte = 0x0}, {pte = 0x0}, {pte = 0x0}, {pte = 0x6a1303}, {
+           pte = 0x0} <repeats 12 times>, {pte = 0x709303}, {pte = 0x0}, {pte = 0x0},
+         {pte = 0x6fd303}, {pte = 0x6f9303}, {pte = 0x6f5303}, {pte = 0x0}, {
+           pte = 0x6ed303}, {pte = 0x531b01}, {pte = 0x50db01}, {
+           pte = 0x0} <repeats 13 times>, {pte = 0x5303}, {pte = 0x7f5303}, {
+           pte = 0x509b01}, {pte = 0x505b01}, {pte = 0x7c9303}, {pte = 0x7b9303}, {
+           pte = 0x7b5303}, {pte = 0x7b1303}, {pte = 0x7ad303}, {pte = 0x0}, {
+           pte = 0x0}, {pte = 0x7a1303}, {pte = 0x0}, {pte = 0x795303}, {pte = 0x0}, {
+           pte = 0x78d303}, {pte = 0x0}, {pte = 0x0}, {pte = 0x0}, {pte = 0x0}, {
+           pte = 0x0}, {pte = 0x775303}, {pte = 0x771303}, {pte = 0x76d303}, {
+           pte = 0x0}, {pte = 0x765303}, {pte = 0x7c5303}, {pte = 0x501b01}, {
+           pte = 0x4f1b01}, {pte = 0x4edb01}, {pte = 0x0}, {pte = 0x4f9b01}, {
+           pte = 0x4fdb01}, {pte = 0x0} <repeats 2992 times>}
diff --git a/Documentation/i2c/chips/smsc47b397.txt b/Documentation/i2c/chips/smsc47b397.txt
new file mode 100644 (file)
index 0000000..389edae
--- /dev/null
@@ -0,0 +1,146 @@
+November 23, 2004
+
+The following specification describes the SMSC LPC47B397-NC sensor chip
+(for which there is no public datasheet available).  This document was
+provided by Craig Kelly (In-Store Broadcast Network) and edited/corrected
+by Mark M. Hoffman <mhoffman@lightlink.com>.
+
+* * * * *
+
+Methods for detecting the HP SIO and reading the thermal data on a dc7100.
+
+The thermal information on the dc7100 is contained in the SIO Hardware Monitor
+(HWM).  The information is accessed through an index/data pair.  The index/data
+pair is located at the HWM Base Address + 0 and the HWM Base Address + 1.  The
+HWM Base address can be obtained from Logical Device 8, registers 0x60 (MSB)
+and 0x61 (LSB).  Currently we are using 0x480 for the HWM Base Address and
+0x480 and 0x481 for the index/data pair.
+
+Reading temperature information.
+The temperature information is located in the following registers:
+Temp1          0x25    (Currently, this reflects the CPU temp on all systems).
+Temp2          0x26
+Temp3          0x27
+Temp4          0x80
+
+Programming Example
+The following is an example of how to read the HWM temperature registers:
+MOV    DX,480H
+MOV    AX,25H
+OUT    DX,AL
+MOV    DX,481H
+IN     AL,DX
+
+AL contains the data in hex, the temperature in Celsius is the decimal
+equivalent.
+
+Ex: If AL contains 0x2A, the temperature is 42 degrees C.
+
+Reading tach information.
+The fan speed information is located in the following registers:
+               LSB     MSB
+Tach1          0x28    0x29    (Currently, this reflects the CPU
+                               fan speed on all systems).
+Tach2          0x2A    0x2B
+Tach3          0x2C    0x2D
+Tach4          0x2E    0x2F
+
+Important!!!
+Reading the tach LSB locks the tach MSB.
+The LSB Must be read first.
+
+How to convert the tach reading to RPM.
+The tach reading (TCount) is given by:  (Tach MSB * 256) + (Tach LSB)
+The SIO counts the number of 90kHz (11.111us) pulses per revolution.
+RPM = 60/(TCount * 11.111us)
+
+Example:
+Reg 0x28 = 0x9B
+Reg 0x29 = 0x08
+
+TCount = 0x89B = 2203
+
+RPM = 60 / (2203 * 11.11111 E-6) = 2451 RPM
+
+Obtaining the SIO version.
+
+CONFIGURATION SEQUENCE
+To program the configuration registers, the following sequence must be followed:
+1. Enter Configuration Mode
+2. Configure the Configuration Registers
+3. Exit Configuration Mode.
+
+Enter Configuration Mode
+To place the chip into the Configuration State The config key (0x55) is written
+to the CONFIG PORT (0x2E). 
+
+Configuration Mode
+In configuration mode, the INDEX PORT is located at the CONFIG PORT address and
+the DATA PORT is at INDEX PORT address + 1.
+
+The desired configuration registers are accessed in two steps: 
+a.     Write the index of the Logical Device Number Configuration Register
+       (i.e., 0x07) to the INDEX PORT and then write the number of the
+       desired logical device to the DATA PORT.
+
+b.     Write the address of the desired configuration register within the
+       logical device to the INDEX PORT and then write or read the config-
+       uration register through the DATA PORT.  
+
+Note: If accessing the Global Configuration Registers, step (a) is not required.
+
+Exit Configuration Mode
+To exit the Configuration State the write 0xAA to the CONFIG PORT (0x2E).
+The chip returns to the RUN State.  (This is important).
+
+Programming Example
+The following is an example of how to read the SIO Device ID located at 0x20
+
+; ENTER CONFIGURATION MODE   
+MOV    DX,02EH
+MOV    AX,055H
+OUT    DX,AL
+; GLOBAL CONFIGURATION  REGISTER 
+MOV    DX,02EH
+MOV    AL,20H
+OUT    DX,AL 
+; READ THE DATA
+MOV    DX,02FH
+IN     AL,DX
+; EXIT CONFIGURATION MODE     
+MOV    DX,02EH
+MOV    AX,0AAH
+OUT    DX,AL
+
+The registers of interest for identifying the SIO on the dc7100 are Device ID
+(0x20) and Device Rev  (0x21).
+
+The Device ID will read 0X6F
+The Device Rev currently reads 0x01
+
+Obtaining the HWM Base Address.
+The following is an example of how to read the HWM Base Address located in
+Logical Device 8.
+
+; ENTER CONFIGURATION MODE   
+MOV    DX,02EH
+MOV    AX,055H
+OUT    DX,AL
+; CONFIGURE REGISTER CRE0,   
+; LOGICAL DEVICE 8           
+MOV    DX,02EH
+MOV    AL,07H
+OUT    DX,AL ;Point to LD# Config Reg
+MOV    DX,02FH
+MOV    AL, 08H
+OUT    DX,AL;Point to Logical Device 8
+;
+MOV    DX,02EH 
+MOV    AL,60H
+OUT    DX,AL   ; Point to HWM Base Addr MSB
+MOV    DX,02FH
+IN     AL,DX   ; Get MSB of HWM Base Addr
+; EXIT CONFIGURATION MODE     
+MOV    DX,02EH
+MOV    AX,0AAH
+OUT    DX,AL
diff --git a/Documentation/i2c/i2c-stub b/Documentation/i2c/i2c-stub
new file mode 100644 (file)
index 0000000..2626ba9
--- /dev/null
@@ -0,0 +1,33 @@
+MODULE: i2c-stub
+
+DESCRIPTION:
+
+This module is a very simple fake I2C/SMBus driver.  It implements three
+types of SMBus commands: write quick, (r/w) byte data, and (r/w) word data.
+
+No hardware is needed nor associated with this module.  It will accept write
+quick commands to all addresses; it will respond to the other commands (also
+to all addresses) by reading from or writing to an array in memory.  It will
+also spam the kernel logs for every command it handles.
+
+The typical use-case is like this:
+       1. load this module
+       2. use i2cset (from lm_sensors project) to pre-load some data
+       3. load the target sensors chip driver module
+       4. observe its behavior in the kernel log
+
+CAVEATS:
+
+There are independent arrays for byte/data and word/data commands.  Depending
+on if/how a target driver mixes them, you'll need to be careful.
+
+If your target driver polls some byte or word waiting for it to change, the
+stub could lock it up.  Use i2cset to unlock it.
+
+If the hardware for your driver has banked registers (e.g. Winbond sensors
+chips) this module will not work well - although it could be extended to
+support that pretty easily.
+
+If you spam it hard enough, printk can be lossy.  This module really wants
+something like relayfs.
+
diff --git a/Documentation/ia64/serial.txt b/Documentation/ia64/serial.txt
new file mode 100644 (file)
index 0000000..f51eb4b
--- /dev/null
@@ -0,0 +1,144 @@
+SERIAL DEVICE NAMING
+
+    As of 2.6.10, serial devices on ia64 are named based on the
+    order of ACPI and PCI enumeration.  The first device in the
+    ACPI namespace (if any) becomes /dev/ttyS0, the second becomes
+    /dev/ttyS1, etc., and PCI devices are named sequentially
+    starting after the ACPI devices.
+
+    Prior to 2.6.10, there were confusing exceptions to this:
+
+       - Firmware on some machines (mostly from HP) provides an HCDP
+         table[1] that tells the kernel about devices that can be used
+         as a serial console.  If the user specified "console=ttyS0"
+         or the EFI ConOut path contained only UART devices, the
+         kernel registered the device described by the HCDP as
+         /dev/ttyS0.
+
+       - If there was no HCDP, we assumed there were UARTs at the
+         legacy COM port addresses (I/O ports 0x3f8 and 0x2f8), so
+         the kernel registered those as /dev/ttyS0 and /dev/ttyS1.
+
+    Any additional ACPI or PCI devices were registered sequentially
+    after /dev/ttyS0 as they were discovered.
+
+    With an HCDP, device names changed depending on EFI configuration
+    and "console=" arguments.  Without an HCDP, device names didn't
+    change, but we registered devices that might not really exist.
+
+    For example, an HP rx1600 with a single built-in serial port
+    (described in the ACPI namespace) plus an MP[2] (a PCI device) has
+    these ports:
+
+                                  pre-2.6.10      pre-2.6.10
+                    MMIO         (EFI console    (EFI console
+                   address        on builtin)     on MP port)    2.6.10
+                  ==========      ==========      ==========     ======
+      builtin     0xff5e0000        ttyS0           ttyS1         ttyS0
+      MP UPS      0xf8031000        ttyS1           ttyS2         ttyS1
+      MP Console  0xf8030000        ttyS2           ttyS0         ttyS2
+      MP 2        0xf8030010        ttyS3           ttyS3         ttyS3
+      MP 3        0xf8030038        ttyS4           ttyS4         ttyS4
+
+CONSOLE SELECTION
+
+    EFI knows what your console devices are, but it doesn't tell the
+    kernel quite enough to actually locate them.  The DIG64 HCDP
+    table[1] does tell the kernel where potential serial console
+    devices are, but not all firmware supplies it.  Also, EFI supports
+    multiple simultaneous consoles and doesn't tell the kernel which
+    should be the "primary" one.
+
+    So how do you tell Linux which console device to use?
+
+       - If your firmware supplies the HCDP, it is simplest to
+         configure EFI with a single device (either a UART or a VGA
+         card) as the console.  Then you don't need to tell Linux
+         anything; the kernel will automatically use the EFI console.
+
+         (This works only in 2.6.6 or later; prior to that you had
+         to specify "console=ttyS0" to get a serial console.)
+
+       - Without an HCDP, Linux defaults to a VGA console unless you
+         specify a "console=" argument.
+
+    NOTE: Don't assume that a serial console device will be /dev/ttyS0.
+    It might be ttyS1, ttyS2, etc.  Make sure you have the appropriate
+    entries in /etc/inittab (for getty) and /etc/securetty (to allow
+    root login).
+
+EARLY SERIAL CONSOLE
+
+    The kernel can't start using a serial console until it knows where
+    the device lives.  Normally this happens when the driver enumerates
+    all the serial devices, which can happen a minute or more after the
+    kernel starts booting.
+
+    2.6.10 and later kernels have an "early uart" driver that works
+    very early in the boot process.  The kernel will automatically use
+    this if the user supplies an argument like "console=uart,io,0x3f8",
+    or if the EFI console path contains only a UART device and the
+    firmware supplies an HCDP.
+
+TROUBLESHOOTING SERIAL CONSOLE PROBLEMS
+
+    No kernel output after elilo prints "Uncompressing Linux... done":
+
+       - You specified "console=ttyS0" but Linux changed the device
+         to which ttyS0 refers.  Configure exactly one EFI console
+         device[3] and remove the "console=" option.
+
+       - The EFI console path contains both a VGA device and a UART.
+         EFI and elilo use both, but Linux defaults to VGA.  Remove
+         the VGA device from the EFI console path[3].
+
+       - Multiple UARTs selected as EFI console devices.  EFI and
+         elilo use all selected devices, but Linux uses only one.
+         Make sure only one UART is selected in the EFI console
+         path[3].
+
+       - You're connected to an HP MP port[2] but have a non-MP UART
+         selected as EFI console device.  EFI uses the MP as a
+         console device even when it isn't explicitly selected.
+         Either move the console cable to the non-MP UART, or change
+         the EFI console path[3] to the MP UART.
+
+    Long pause (60+ seconds) between "Uncompressing Linux... done" and
+    start of kernel output:
+
+       - No early console because you used "console=ttyS<n>".  Remove
+         the "console=" option if your firmware supplies an HCDP.
+
+       - If you don't have an HCDP, the kernel doesn't know where
+         your console lives until the driver discovers serial
+         devices.  Use "console=uart, io,0x3f8" (or appropriate
+         address for your machine).
+
+    Kernel and init script output works fine, but no "login:" prompt:
+
+       - Add getty entry to /etc/inittab for console tty.  Look for
+         the "Adding console on ttyS<n>" message that tells you which
+         device is the console.
+
+    "login:" prompt, but can't login as root:
+
+       - Add entry to /etc/securetty for console tty.
+
+
+
+[1] http://www.dig64.org/specifications/DIG64_PCDPv20.pdf
+    The table was originally defined as the "HCDP" for "Headless
+    Console/Debug Port."  The current version is the "PCDP" for
+    "Primary Console and Debug Port Devices."
+
+[2] The HP MP (management processor) is a PCI device that provides
+    several UARTs.  One of the UARTs is often used as a console; the
+    EFI Boot Manager identifies it as "Acpi(HWP0002,700)/Pci(...)/Uart".
+    The external connection is usually a 25-pin connector, and a
+    special dongle converts that to three 9-pin connectors, one of
+    which is labelled "Console."
+
+[3] EFI console devices are configured using the EFI Boot Manager
+    "Boot option maintenance" menu.  You may have to interrupt the
+    boot sequence to use this menu, and you will have to reset the
+    box after changing console configuration.
diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt
new file mode 100644 (file)
index 0000000..c437b1a
--- /dev/null
@@ -0,0 +1,474 @@
+                   IBM ThinkPad ACPI Extras Driver
+
+                            Version 0.8
+                          8 November 2004
+
+               Borislav Deianov <borislav@users.sf.net>
+                     http://ibm-acpi.sf.net/
+
+
+This is a Linux ACPI driver for the IBM ThinkPad laptops. It aims to
+support various features of these laptops which are accessible through
+the ACPI framework but not otherwise supported by the generic Linux
+ACPI drivers.
+
+
+Status
+------
+
+The features currently supported are the following (see below for
+detailed description):
+
+       - Fn key combinations
+       - Bluetooth enable and disable
+       - video output switching, expansion control     
+       - ThinkLight on and off
+       - limited docking and undocking
+       - UltraBay eject
+       - Experimental: CMOS control
+       - Experimental: LED control
+       - Experimental: ACPI sounds
+
+A compatibility table by model and feature is maintained on the web
+site, http://ibm-acpi.sf.net/. I appreciate any success or failure
+reports, especially if they add to or correct the compatibility table.
+Please include the following information in your report:
+
+       - ThinkPad model name
+       - a copy of your DSDT, from /proc/acpi/dsdt
+       - which driver features work and which don't
+       - the observed behavior of non-working features
+
+Any other comments or patches are also more than welcome.
+
+
+Installation
+------------
+
+If you are compiling this driver as included in the Linux kernel
+sources, simply enable the CONFIG_ACPI_IBM option (Power Management /
+ACPI / IBM ThinkPad Laptop Extras). The rest of this section describes
+how to install this driver when downloaded from the web site.
+
+First, you need to get a kernel with ACPI support up and running.
+Please refer to http://acpi.sourceforge.net/ for help with this
+step. How successful you will be depends a lot on you ThinkPad model,
+the kernel you are using and any additional patches applied. The
+kernel provided with your distribution may not be good enough. I
+needed to compile a 2.6.7 kernel with the 20040715 ACPI patch to get
+ACPI working reliably on my ThinkPad X40. Old ThinkPad models may not
+be supported at all.
+
+Assuming you have the basic ACPI support working (e.g. you can see the
+/proc/acpi directory), follow the following steps to install this
+driver:
+
+       - unpack the archive:
+
+               tar xzvf ibm-acpi-x.y.tar.gz; cd ibm-acpi-x.y
+
+       - compile the driver:
+
+               make
+
+       - install the module in your kernel modules directory:
+
+               make install
+
+       - load the module:
+
+               modprobe ibm_acpi
+
+After loading the module, check the "dmesg" output for any error messages.
+
+
+Features
+--------
+
+The driver creates the /proc/acpi/ibm directory. There is a file under
+that directory for each feature described below. Note that while the
+driver is still in the alpha stage, the exact proc file format and
+commands supported by the various features is guaranteed to change
+frequently.
+
+Driver Version -- /proc/acpi/ibm/driver
+--------------------------------------
+
+The driver name and version. No commands can be written to this file.
+
+Hot Keys -- /proc/acpi/ibm/hotkey
+---------------------------------
+
+Without this driver, only the Fn-F4 key (sleep button) generates an
+ACPI event. With the driver loaded, the hotkey feature enabled and the
+mask set (see below), the various hot keys generate ACPI events in the
+following format:
+
+       ibm/hotkey HKEY 00000080 0000xxxx
+
+The last four digits vary depending on the key combination pressed.
+All labeled Fn-Fx key combinations generate distinct events. In
+addition, the lid microswitch and some docking station buttons may
+also generate such events.
+
+The following commands can be written to this file:
+
+       echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature
+       echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature
+       echo 0xffff > /proc/acpi/ibm/hotkey -- enable all possible hot keys
+       echo 0x0000 > /proc/acpi/ibm/hotkey -- disable all possible hot keys
+       ... any other 4-hex-digit mask ...
+       echo reset > /proc/acpi/ibm/hotkey -- restore the original mask
+
+The bit mask allows some control over which hot keys generate ACPI
+events. Not all bits in the mask can be modified. Not all bits that
+can be modified do anything. Not all hot keys can be individually
+controlled by the mask. Most recent ThinkPad models honor the
+following bits (assuming the hot keys feature has been enabled):
+
+       key     bit     behavior when set       behavior when unset
+
+       Fn-F3                   always generates ACPI event
+       Fn-F4                   always generates ACPI event
+       Fn-F5   0010    generate ACPI event     enable/disable Bluetooth
+       Fn-F7   0040    generate ACPI event     switch LCD and external display
+       Fn-F8   0080    generate ACPI event     expand screen or none
+       Fn-F9   0100    generate ACPI event     none
+       Fn-F12                  always generates ACPI event
+
+Some models do not support all of the above. For example, the T30 does
+not support Fn-F5 and Fn-F9. Other models do not support the mask at
+all. On those models, hot keys cannot be controlled individually.
+
+Note that enabling ACPI events for some keys prevents their default
+behavior. For example, if events for Fn-F5 are enabled, that key will
+no longer enable/disable Bluetooth by itself. This can still be done
+from an acpid handler for the ibm/hotkey event.
+
+Note also that not all Fn key combinations are supported through
+ACPI. For example, on the X40, the brightness, volume and "Access IBM"
+buttons do not generate ACPI events even with this driver. They *can*
+be used through the "ThinkPad Buttons" utility, see
+http://www.nongnu.org/tpb/
+
+Bluetooth -- /proc/acpi/ibm/bluetooth
+-------------------------------------
+
+This feature shows the presence and current state of a Bluetooth
+device. If Bluetooth is installed, the following commands can be used:
+
+       echo enable > /proc/acpi/ibm/bluetooth
+       echo disable > /proc/acpi/ibm/bluetooth
+
+Video output control -- /proc/acpi/ibm/video
+--------------------------------------------
+
+This feature allows control over the devices used for video output -
+LCD, CRT or DVI (if available). The following commands are available:
+
+       echo lcd_enable > /proc/acpi/ibm/video
+       echo lcd_disable > /proc/acpi/ibm/video
+       echo crt_enable > /proc/acpi/ibm/video
+       echo crt_disable > /proc/acpi/ibm/video
+       echo dvi_enable > /proc/acpi/ibm/video
+       echo dvi_disable > /proc/acpi/ibm/video
+       echo auto_enable > /proc/acpi/ibm/video
+       echo auto_disable > /proc/acpi/ibm/video
+       echo expand_toggle > /proc/acpi/ibm/video
+       echo video_switch > /proc/acpi/ibm/video
+
+Each video output device can be enabled or disabled individually.
+Reading /proc/acpi/ibm/video shows the status of each device.
+
+Automatic video switching can be enabled or disabled.  When automatic
+video switching is enabled, certain events (e.g. opening the lid,
+docking or undocking) cause the video output device to change
+automatically. While this can be useful, it also causes flickering
+and, on the X40, video corruption. By disabling automatic switching,
+the flickering or video corruption can be avoided.
+
+The video_switch command cycles through the available video outputs
+(it sumulates the behavior of Fn-F7).
+
+Video expansion can be toggled through this feature. This controls
+whether the display is expanded to fill the entire LCD screen when a
+mode with less than full resolution is used. Note that the current
+video expansion status cannot be determined through this feature.
+
+Note that on many models (particularly those using Radeon graphics
+chips) the X driver configures the video card in a way which prevents
+Fn-F7 from working. This also disables the video output switching
+features of this driver, as it uses the same ACPI methods as
+Fn-F7. Video switching on the console should still work.
+
+ThinkLight control -- /proc/acpi/ibm/light
+------------------------------------------
+
+The current status of the ThinkLight can be found in this file. A few
+models which do not make the status available will show it as
+"unknown". The available commands are:
+
+       echo on  > /proc/acpi/ibm/light
+       echo off > /proc/acpi/ibm/light
+
+Docking / Undocking -- /proc/acpi/ibm/dock
+------------------------------------------
+
+Docking and undocking (e.g. with the X4 UltraBase) requires some
+actions to be taken by the operating system to safely make or break
+the electrical connections with the dock.
+
+The docking feature of this driver generates the following ACPI events:
+
+       ibm/dock GDCK 00000003 00000001 -- eject request
+       ibm/dock GDCK 00000003 00000002 -- undocked
+       ibm/dock GDCK 00000000 00000003 -- docked
+
+NOTE: These events will only be generated if the laptop was docked
+when originally booted. This is due to the current lack of support for
+hot plugging of devices in the Linux ACPI framework. If the laptop was
+booted while not in the dock, the following message is shown in the
+logs: "ibm_acpi: dock device not present". No dock-related events are
+generated but the dock and undock commands described below still
+work. They can be executed manually or triggered by Fn key
+combinations (see the example acpid configuration files included in
+the driver tarball package available on the web site).
+
+When the eject request button on the dock is pressed, the first event
+above is generated. The handler for this event should issue the
+following command:
+
+       echo undock > /proc/acpi/ibm/dock
+
+After the LED on the dock goes off, it is safe to eject the laptop.
+Note: if you pressed this key by mistake, go ahead and eject the
+laptop, then dock it back in. Otherwise, the dock may not function as
+expected.
+
+When the laptop is docked, the third event above is generated. The
+handler for this event should issue the following command to fully
+enable the dock:
+
+       echo dock > /proc/acpi/ibm/dock
+
+The contents of the /proc/acpi/ibm/dock file shows the current status
+of the dock, as provided by the ACPI framework.
+
+The docking support in this driver does not take care of enabling or
+disabling any other devices you may have attached to the dock. For
+example, a CD drive plugged into the UltraBase needs to be disabled or
+enabled separately. See the provided example acpid configuration files
+for how this can be accomplished.
+
+There is no support yet for PCI devices that may be attached to a
+docking station, e.g. in the ThinkPad Dock II. The driver currently
+does not recognize, enable or disable such devices. This means that
+the only docking stations currently supported are the X-series
+UltraBase docks and "dumb" port replicators like the Mini Dock (the
+latter don't need any ACPI support, actually).
+
+UltraBay Eject -- /proc/acpi/ibm/bay
+------------------------------------
+
+Inserting or ejecting an UltraBay device requires some actions to be
+taken by the operating system to safely make or break the electrical
+connections with the device.
+
+This feature generates the following ACPI events:
+
+       ibm/bay MSTR 00000003 00000000 -- eject request
+       ibm/bay MSTR 00000001 00000000 -- eject lever inserted
+
+NOTE: These events will only be generated if the UltraBay was present
+when the laptop was originally booted (on the X series, the UltraBay
+is in the dock, so it may not be present if the laptop was undocked).
+This is due to the current lack of support for hot plugging of devices
+in the Linux ACPI framework. If the laptop was booted without the
+UltraBay, the following message is shown in the logs: "ibm_acpi: bay
+device not present". No bay-related events are generated but the eject
+command described below still works. It can be executed manually or
+triggered by a hot key combination.
+
+Sliding the eject lever generates the first event shown above. The
+handler for this event should take whatever actions are necessary to
+shut down the device in the UltraBay (e.g. call idectl), then issue
+the following command:
+
+       echo eject > /proc/acpi/ibm/bay
+
+After the LED on the UltraBay goes off, it is safe to pull out the
+device.
+
+When the eject lever is inserted, the second event above is
+generated. The handler for this event should take whatever actions are
+necessary to enable the UltraBay device (e.g. call idectl).
+
+The contents of the /proc/acpi/ibm/bay file shows the current status
+of the UltraBay, as provided by the ACPI framework.
+
+Experimental Features
+---------------------
+
+The following features are marked experimental because using them
+involves guessing the correct values of some parameters. Guessing
+incorrectly may have undesirable effects like crashing your
+ThinkPad. USE THESE WITH CAUTION! To activate them, you'll need to
+supply the experimental=1 parameter when loading the module.
+
+Experimental: CMOS control - /proc/acpi/ibm/cmos
+------------------------------------------------
+
+This feature is used internally by the ACPI firmware to control the
+ThinkLight on most newer ThinkPad models. It appears that it can also
+control LCD brightness, sounds volume and more, but only on some
+models.
+
+The commands are non-negative integer numbers:
+
+       echo 0 >/proc/acpi/ibm/cmos
+       echo 1 >/proc/acpi/ibm/cmos
+       echo 2 >/proc/acpi/ibm/cmos
+       ...
+
+The range of numbers which are used internally by various models is 0
+to 21, but it's possible that numbers outside this range have
+interesting behavior. Here is the behavior on the X40 (tpb is the
+ThinkPad Buttons utility):
+
+       0 - no effect but tpb reports "Volume down"
+       1 - no effect but tpb reports "Volume up"
+       2 - no effect but tpb reports "Mute on"
+       3 - simulate pressing the "Access IBM" button
+       4 - LCD brightness up
+       5 - LCD brightness down
+       11 - toggle screen expansion
+       12 - ThinkLight on
+       13 - ThinkLight off
+       14 - no effect but tpb reports ThinkLight status change
+
+If you try this feature, please send me a report similar to the
+above. On models which allow control of LCD brightness or sound
+volume, I'd like to provide this functionality in an user-friendly
+way, but first I need a way to identify the models which this is
+possible.
+
+Experimental: LED control - /proc/acpi/ibm/LED
+----------------------------------------------
+
+Some of the LED indicators can be controlled through this feature. The
+available commands are:
+
+       echo <led number> on >/proc/acpi/ibm/led
+       echo <led number> off >/proc/acpi/ibm/led
+       echo <led number> blink >/proc/acpi/ibm/led
+
+The <led number> parameter is a non-negative integer. The range of LED
+numbers used internally by various models is 0 to 7 but it's possible
+that numbers outside this range are also valid. Here is the mapping on
+the X40:
+
+       0 - power
+       1 - battery (orange)
+       2 - battery (green)
+       3 - UltraBase
+       4 - UltraBay
+       7 - standby
+
+All of the above can be turned on and off and can be made to blink.
+
+If you try this feature, please send me a report similar to the
+above. I'd like to provide this functionality in an user-friendly way,
+but first I need to identify the which numbers correspond to which
+LEDs on various models.
+
+Experimental: ACPI sounds - /proc/acpi/ibm/beep
+-----------------------------------------------
+
+The BEEP method is used internally by the ACPI firmware to provide
+audible alerts in various situtation. This feature allows the same
+sounds to be triggered manually.
+
+The commands are non-negative integer numbers:
+
+       echo 0 >/proc/acpi/ibm/beep
+       echo 1 >/proc/acpi/ibm/beep
+       echo 2 >/proc/acpi/ibm/beep
+       ...
+
+The range of numbers which are used internally by various models is 0
+to 17, but it's possible that numbers outside this range are also
+valid. Here is the behavior on the X40:
+
+       2 - two beeps, pause, third beep
+       3 - single beep
+       4 - "unable"    
+       5 - single beep
+       6 - "AC/DC"
+       7 - high-pitched beep
+       9 - three short beeps
+       10 - very long beep
+       12 - low-pitched beep
+
+(I've only been able to identify a couple of them).
+
+If you try this feature, please send me a report similar to the
+above. I'd like to provide this functionality in an user-friendly way,
+but first I need to identify the which numbers correspond to which
+sounds on various models.
+
+
+Multiple Command, Module Parameters
+-----------------------------------
+
+Multiple commands can be written to the proc files in one shot by
+separating them with commas, for example:
+
+       echo enable,0xffff > /proc/acpi/ibm/hotkey
+       echo lcd_disable,crt_enable > /proc/acpi/ibm/video
+
+Commands can also be specified when loading the ibm_acpi module, for
+example:
+
+       modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable
+
+
+Example Configuration
+---------------------
+
+The ACPI support in the kernel is intended to be used in conjunction
+with a user-space daemon, acpid. The configuration files for this
+daemon control what actions are taken in response to various ACPI
+events. An example set of configuration files are included in the
+config/ directory of the tarball package available on the web
+site. Note that these are provided for illustration purposes only and
+may need to be adapted to your particular setup.
+
+The following utility scripts are used by the example action
+scripts (included with ibm-acpi for completeness):
+
+       /usr/local/sbin/idectl -- from the hdparm source distribution,
+               see http://www.ibiblio.org/pub/Linux/system/hardware
+       /usr/local/sbin/laptop_mode -- from the Linux kernel source
+               distribution, see Documentation/laptop-mode.txt
+       /sbin/service -- comes with Redhat/Fedora distributions
+
+Toan T Nguyen <ntt@control.uchicago.edu> has written a SuSE powersave
+script for the X20, included in config/usr/sbin/ibm_hotkeys_X20
+
+Henrik Brix Andersen <brix@gentoo.org> has written a Gentoo ACPI event
+handler script for the X31. You can get the latest version from
+http://dev.gentoo.org/~brix/files/x31.sh
+
+David Schweikert <dws@ee.eth.ch> has written an alternative blank.sh
+script which works on Debian systems, included in
+configs/etc/acpi/actions/blank-debian.sh
+
+
+TODO
+----
+
+I'd like to implement the following features but haven't yet found the
+time and/or I don't yet know how to implement them:
+
+- UltraBay floppy drive support
+
diff --git a/Documentation/infiniband/ipoib.txt b/Documentation/infiniband/ipoib.txt
new file mode 100644 (file)
index 0000000..18e184b
--- /dev/null
@@ -0,0 +1,56 @@
+IP OVER INFINIBAND
+
+  The ib_ipoib driver is an implementation of the IP over InfiniBand
+  protocol as specified by the latest Internet-Drafts issued by the
+  IETF ipoib working group.  It is a "native" implementation in the
+  sense of setting the interface type to ARPHRD_INFINIBAND and the
+  hardware address length to 20 (earlier proprietary implementations
+  masqueraded to the kernel as ethernet interfaces).
+
+Partitions and P_Keys
+
+  When the IPoIB driver is loaded, it creates one interface for each
+  port using the P_Key at index 0.  To create an interface with a
+  different P_Key, write the desired P_Key into the main interface's
+  /sys/class/net/<intf name>/create_child file.  For example:
+
+    echo 0x8001 > /sys/class/net/ib0/create_child
+
+  This will create an interface named ib0.8001 with P_Key 0x8001.  To
+  remove a subinterface, use the "delete_child" file:
+
+    echo 0x8001 > /sys/class/net/ib0/delete_child
+
+  The P_Key for any interface is given by the "pkey" file, and the
+  main interface for a subinterface is in "parent."
+
+Debugging Information
+
+  By compiling the IPoIB driver with CONFIG_INFINIBAND_IPOIB_DEBUG set
+  to 'y', tracing messages are compiled into the driver.  They are
+  turned on by setting the module parameters debug_level and
+  mcast_debug_level to 1.  These parameters can be controlled at
+  runtime through files in /sys/module/ib_ipoib/.
+
+  CONFIG_INFINIBAND_IPOIB_DEBUG also enables the "ipoib_debugfs"
+  virtual filesystem.  By mounting this filesystem, for example with
+
+    mkdir -p /ipoib_debugfs
+    mount -t ipoib_debugfs none /ipoib_debufs
+
+  it is possible to get statistics about multicast groups from the
+  files /ipoib_debugfs/ib0_mcg and so on.
+
+  The performance impact of this option is negligible, so it
+  is safe to enable this option with debug_level set to 0 for normal
+  operation.
+
+  CONFIG_INFINIBAND_IPOIB_DEBUG_DATA enables even more debug output in
+  the data path when data_debug_level is set to 1.  However, even with
+  the output disabled, enabling this configuration option will affect
+  performance, because it adds tests to the fast path.
+
+References
+
+  IETF IP over InfiniBand (ipoib) Working Group
+    http://ietf.org/html.charters/ipoib-charter.html
diff --git a/Documentation/infiniband/sysfs.txt b/Documentation/infiniband/sysfs.txt
new file mode 100644 (file)
index 0000000..ddd519b
--- /dev/null
@@ -0,0 +1,66 @@
+SYSFS FILES
+
+  For each InfiniBand device, the InfiniBand drivers create the
+  following files under /sys/class/infiniband/<device name>:
+
+    node_type      - Node type (CA, switch or router)
+    node_guid      - Node GUID
+    sys_image_guid - System image GUID
+
+  In addition, there is a "ports" subdirectory, with one subdirectory
+  for each port.  For example, if mthca0 is a 2-port HCA, there will
+  be two directories:
+
+    /sys/class/infiniband/mthca0/ports/1
+    /sys/class/infiniband/mthca0/ports/2
+
+  (A switch will only have a single "0" subdirectory for switch port
+  0; no subdirectory is created for normal switch ports)
+
+  In each port subdirectory, the following files are created:
+
+    cap_mask       - Port capability mask
+    lid            - Port LID
+    lid_mask_count - Port LID mask count
+    rate           - Port data rate (active width * active speed)
+    sm_lid         - Subnet manager LID for port's subnet
+    sm_sl          - Subnet manager SL for port's subnet
+    state          - Port state (DOWN, INIT, ARMED, ACTIVE or ACTIVE_DEFER)
+    phys_state     - Port physical state (Sleep, Polling, LinkUp, etc)
+
+  There is also a "counters" subdirectory, with files
+
+    VL15_dropped
+    excessive_buffer_overrun_errors
+    link_downed
+    link_error_recovery
+    local_link_integrity_errors
+    port_rcv_constraint_errors
+    port_rcv_data
+    port_rcv_errors
+    port_rcv_packets
+    port_rcv_remote_physical_errors
+    port_rcv_switch_relay_errors
+    port_xmit_constraint_errors
+    port_xmit_data
+    port_xmit_discards
+    port_xmit_packets
+    symbol_error
+
+  Each of these files contains the corresponding value from the port's
+  Performance Management PortCounters attribute, as described in
+  section 16.1.3.5 of the InfiniBand Architecture Specification.
+
+  The "pkeys" and "gids" subdirectories contain one file for each
+  entry in the port's P_Key or GID table respectively.  For example,
+  ports/1/pkeys/10 contains the value at index 10 in port 1's P_Key
+  table.
+
+MTHCA
+
+  The Mellanox HCA driver also creates the files:
+
+    hw_rev   - Hardware revision number
+    fw_ver   - Firmware version
+    hca_type - HCA type: "MT23108", "MT25208 (MT23108 compat mode)",
+               or "MT25208"
diff --git a/Documentation/infiniband/user_mad.txt b/Documentation/infiniband/user_mad.txt
new file mode 100644 (file)
index 0000000..cae0c83
--- /dev/null
@@ -0,0 +1,99 @@
+USERSPACE MAD ACCESS
+
+Device files
+
+  Each port of each InfiniBand device has a "umad" device and an
+  "issm" device attached.  For example, a two-port HCA will have two
+  umad devices and two issm devices, while a switch will have one
+  device of each type (for switch port 0).
+
+Creating MAD agents
+
+  A MAD agent can be created by filling in a struct ib_user_mad_reg_req
+  and then calling the IB_USER_MAD_REGISTER_AGENT ioctl on a file
+  descriptor for the appropriate device file.  If the registration
+  request succeeds, a 32-bit id will be returned in the structure.
+  For example:
+
+       struct ib_user_mad_reg_req req = { /* ... */ };
+       ret = ioctl(fd, IB_USER_MAD_REGISTER_AGENT, (char *) &req);
+        if (!ret)
+               my_agent = req.id;
+       else
+               perror("agent register");
+
+  Agents can be unregistered with the IB_USER_MAD_UNREGISTER_AGENT
+  ioctl.  Also, all agents registered through a file descriptor will
+  be unregistered when the descriptor is closed.
+
+Receiving MADs
+
+  MADs are received using read().  The buffer passed to read() must be
+  large enough to hold at least one struct ib_user_mad.  For example:
+
+       struct ib_user_mad mad;
+       ret = read(fd, &mad, sizeof mad);
+       if (ret != sizeof mad)
+               perror("read");
+
+  In addition to the actual MAD contents, the other struct ib_user_mad
+  fields will be filled in with information on the received MAD.  For
+  example, the remote LID will be in mad.lid.
+
+  If a send times out, a receive will be generated with mad.status set
+  to ETIMEDOUT.  Otherwise when a MAD has been successfully received,
+  mad.status will be 0.
+
+  poll()/select() may be used to wait until a MAD can be read.
+
+Sending MADs
+
+  MADs are sent using write().  The agent ID for sending should be
+  filled into the id field of the MAD, the destination LID should be
+  filled into the lid field, and so on.  For example:
+
+       struct ib_user_mad mad;
+
+       /* fill in mad.data */
+
+       mad.id  = my_agent;     /* req.id from agent registration */
+       mad.lid = my_dest;      /* in network byte order... */
+       /* etc. */
+
+       ret = write(fd, &mad, sizeof mad);
+       if (ret != sizeof mad)
+               perror("write");
+
+Setting IsSM Capability Bit
+
+  To set the IsSM capability bit for a port, simply open the
+  corresponding issm device file.  If the IsSM bit is already set,
+  then the open call will block until the bit is cleared (or return
+  immediately with errno set to EAGAIN if the O_NONBLOCK flag is
+  passed to open()).  The IsSM bit will be cleared when the issm file
+  is closed.  No read, write or other operations can be performed on
+  the issm file.
+
+/dev files
+
+  To create the appropriate character device files automatically with
+  udev, a rule like
+
+    KERNEL="umad*", NAME="infiniband/%k"
+    KERNEL="issm*", NAME="infiniband/%k"
+
+  can be used.  This will create device nodes named
+
+    /dev/infiniband/umad0
+    /dev/infiniband/issm0
+
+  for the first port, and so on.  The InfiniBand device and port
+  associated with these devices can be determined from the files
+
+    /sys/class/infiniband_mad/umad0/ibdev
+    /sys/class/infiniband_mad/umad0/port
+
+  and
+
+    /sys/class/infiniband_mad/issm0/ibdev
+    /sys/class/infiniband_mad/issm0/port
diff --git a/Documentation/ioctl/cdrom.txt b/Documentation/ioctl/cdrom.txt
new file mode 100644 (file)
index 0000000..4ccdcc6
--- /dev/null
@@ -0,0 +1,966 @@
+               Summary of CDROM ioctl calls.
+               ============================
+
+               Edward A. Falk <efalk@google.com>
+
+               November, 2004
+
+This document attempts to describe the ioctl(2) calls supported by
+the CDROM layer.  These are by-and-large implemented (as of Linux 2.6)
+in drivers/cdrom/cdrom.c and drivers/block/scsi_ioctl.c
+
+ioctl values are listed in <linux/cdrom.h>.  As of this writing, they
+are as follows:
+
+       CDROMPAUSE              Pause Audio Operation
+       CDROMRESUME             Resume paused Audio Operation
+       CDROMPLAYMSF            Play Audio MSF (struct cdrom_msf)
+       CDROMPLAYTRKIND         Play Audio Track/index (struct cdrom_ti)
+       CDROMREADTOCHDR         Read TOC header (struct cdrom_tochdr)
+       CDROMREADTOCENTRY       Read TOC entry (struct cdrom_tocentry)
+       CDROMSTOP               Stop the cdrom drive
+       CDROMSTART              Start the cdrom drive
+       CDROMEJECT              Ejects the cdrom media
+       CDROMVOLCTRL            Control output volume (struct cdrom_volctrl)
+       CDROMSUBCHNL            Read subchannel data (struct cdrom_subchnl)
+       CDROMREADMODE2          Read CDROM mode 2 data (2336 Bytes)
+                                          (struct cdrom_read)
+       CDROMREADMODE1          Read CDROM mode 1 data (2048 Bytes)
+                                          (struct cdrom_read)
+       CDROMREADAUDIO          (struct cdrom_read_audio)
+       CDROMEJECT_SW           enable(1)/disable(0) auto-ejecting
+       CDROMMULTISESSION       Obtain the start-of-last-session
+                                 address of multi session disks
+                                 (struct cdrom_multisession)
+       CDROM_GET_MCN           Obtain the "Universal Product Code"
+                                  if available (struct cdrom_mcn)
+       CDROM_GET_UPC           Deprecated, use CDROM_GET_MCN instead.
+       CDROMRESET              hard-reset the drive
+       CDROMVOLREAD            Get the drive's volume setting
+                                         (struct cdrom_volctrl)
+       CDROMREADRAW            read data in raw mode (2352 Bytes)
+                                          (struct cdrom_read)
+       CDROMREADCOOKED         read data in cooked mode
+       CDROMSEEK               seek msf address
+       CDROMPLAYBLK            scsi-cd only, (struct cdrom_blk)
+       CDROMREADALL            read all 2646 bytes
+       CDROMGETSPINDOWN        return 4-bit spindown value
+       CDROMSETSPINDOWN        set 4-bit spindown value
+       CDROMCLOSETRAY          pendant of CDROMEJECT
+       CDROM_SET_OPTIONS       Set behavior options
+       CDROM_CLEAR_OPTIONS     Clear behavior options
+       CDROM_SELECT_SPEED      Set the CD-ROM speed
+       CDROM_SELECT_DISC       Select disc (for juke-boxes)
+       CDROM_MEDIA_CHANGED     Check is media changed
+       CDROM_DRIVE_STATUS      Get tray position, etc.
+       CDROM_DISC_STATUS       Get disc type, etc.
+       CDROM_CHANGER_NSLOTS    Get number of slots
+       CDROM_LOCKDOOR          lock or unlock door
+       CDROM_DEBUG             Turn debug messages on/off
+       CDROM_GET_CAPABILITY    get capabilities
+       CDROMAUDIOBUFSIZ        set the audio buffer size
+       DVD_READ_STRUCT         Read structure
+       DVD_WRITE_STRUCT        Write structure
+       DVD_AUTH                Authentication
+       CDROM_SEND_PACKET       send a packet to the drive
+       CDROM_NEXT_WRITABLE     get next writable block
+       CDROM_LAST_WRITTEN      get last block written on disc
+
+
+The information that follows was determined from reading kernel source
+code.  It is likely that some corrections will be made over time.
+
+
+
+
+
+
+
+General:
+
+       Unless otherwise specified, all ioctl calls return 0 on success
+       and -1 with errno set to an appropriate value on error.  (Some
+       ioctls return non-negative data values.)
+
+       Unless otherwise specified, all ioctl calls return -1 and set
+       errno to EFAULT on a failed attempt to copy data to or from user
+       address space.
+
+       Individual drivers may return error codes not listed here.
+
+       Unless otherwise specified, all data structures and constants
+       are defined in <linux/cdrom.h>
+
+
+
+
+CDROMPAUSE                     Pause Audio Operation
+
+       usage:
+
+         ioctl(fd, CDROMPAUSE, 0);
+
+       inputs:         none
+
+       outputs:        none
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+
+
+CDROMRESUME                    Resume paused Audio Operation
+
+       usage:
+
+         ioctl(fd, CDROMRESUME, 0);
+
+       inputs:         none
+
+       outputs:        none
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+
+
+CDROMPLAYMSF                   Play Audio MSF (struct cdrom_msf)
+
+       usage:
+
+         struct cdrom_msf msf;
+         ioctl(fd, CDROMPLAYMSF, &msf);
+
+       inputs:
+         cdrom_msf structure, describing a segment of music to play
+
+       outputs:        none
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+
+       notes:
+         MSF stands for minutes-seconds-frames
+         LBA stands for logical block address
+
+         Segment is described as start and end times, where each time
+         is described as minutes:seconds:frames.  A frame is 1/75 of
+         a second.
+
+
+CDROMPLAYTRKIND                        Play Audio Track/index (struct cdrom_ti)
+
+       usage:
+
+         struct cdrom_ti ti;
+         ioctl(fd, CDROMPLAYTRKIND, &ti);
+
+       inputs:
+         cdrom_ti structure, describing a segment of music to play
+
+       outputs:        none
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+
+       notes:
+         Segment is described as start and end times, where each time
+         is described as a track and an index.
+
+
+
+CDROMREADTOCHDR                        Read TOC header (struct cdrom_tochdr)
+
+       usage:
+
+         cdrom_tochdr header;
+         ioctl(fd, CDROMREADTOCHDR, &header);
+
+       inputs:
+         cdrom_tochdr structure
+
+       outputs:
+         cdrom_tochdr structure
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+
+
+
+CDROMREADTOCENTRY              Read TOC entry (struct cdrom_tocentry)
+
+       usage:
+
+         struct cdrom_tocentry entry;
+         ioctl(fd, CDROMREADTOCENTRY, &entry);
+
+       inputs:
+         cdrom_tocentry structure
+
+       outputs:
+         cdrom_tocentry structure
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+         EINVAL        entry.cdte_format not CDROM_MSF or CDROM_LBA
+         EINVAL        requested track out of bounds
+         EIO           I/O error reading TOC
+
+       notes:
+         TOC stands for Table Of Contents
+         MSF stands for minutes-seconds-frames
+         LBA stands for logical block address
+
+
+
+CDROMSTOP                      Stop the cdrom drive
+
+       usage:
+
+         ioctl(fd, CDROMSTOP, 0);
+
+       inputs:         none
+
+       outputs:        none
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+
+       notes:
+         Exact interpretation of this ioctl depends on the device,
+         but most seem to spin the drive down.
+
+
+CDROMSTART                     Start the cdrom drive
+
+       usage:
+
+         ioctl(fd, CDROMSTART, 0);
+
+       inputs:         none
+
+       outputs:        none
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+
+       notes:
+         Exact interpretation of this ioctl depends on the device,
+         but most seem to spin the drive up and/or close the tray.
+         Other devices ignore the ioctl completely.
+
+
+CDROMEJECT                     Ejects the cdrom media
+
+       usage:
+
+         ioctl(fd, CDROMEJECT, 0);
+
+       inputs:         none
+
+       outputs:        none
+
+       error returns:
+         ENOSYS        cd drive not capable of ejecting
+         EBUSY         other processes are accessing drive, or door is locked
+
+       notes:
+         See CDROM_LOCKDOOR, below.
+
+
+
+CDROMCLOSETRAY                 pendant of CDROMEJECT
+
+       usage:
+
+         ioctl(fd, CDROMEJECT, 0);
+
+       inputs:         none
+
+       outputs:        none
+
+       error returns:
+         ENOSYS        cd drive not capable of ejecting
+         EBUSY         other processes are accessing drive, or door is locked
+
+       notes:
+         See CDROM_LOCKDOOR, below.
+
+
+
+CDROMVOLCTRL                   Control output volume (struct cdrom_volctrl)
+
+       usage:
+
+         struct cdrom_volctrl volume;
+         ioctl(fd, CDROMVOLCTRL, &volume);
+
+       inputs:
+         cdrom_volctrl structure containing volumes for up to 4
+         channels.
+
+       outputs:        none
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+
+
+
+CDROMVOLREAD                   Get the drive's volume setting
+                                         (struct cdrom_volctrl)
+
+       usage:
+
+         struct cdrom_volctrl volume;
+         ioctl(fd, CDROMVOLREAD, &volume);
+
+       inputs:         none
+
+       outputs:
+         The current volume settings.
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+
+
+
+CDROMSUBCHNL                   Read subchannel data (struct cdrom_subchnl)
+
+       usage:
+
+         struct cdrom_subchnl q;
+         ioctl(fd, CDROMSUBCHNL, &q);
+
+       inputs:
+         cdrom_subchnl structure
+
+       outputs:
+         cdrom_subchnl structure
+
+       error return:
+         ENOSYS        cd drive not audio-capable.
+         EINVAL        format not CDROM_MSF or CDROM_LBA
+
+       notes:
+         Format is converted to CDROM_MSF on return
+
+
+
+CDROMREADRAW                   read data in raw mode (2352 Bytes)
+                                          (struct cdrom_read)
+
+       usage:
+
+         union {
+           struct cdrom_msf msf;               /* input */
+           char buffer[CD_FRAMESIZE_RAW];      /* return */
+         } arg;
+         ioctl(fd, CDROMREADRAW, &arg);
+
+       inputs:
+         cdrom_msf structure indicating an address to read.
+         Only the start values are significant.
+
+       outputs:
+         Data written to address provided by user.
+
+       error return:
+         EINVAL        address less than 0, or msf less than 0:2:0
+         ENOMEM        out of memory
+
+       notes:
+         As of 2.6.8.1, comments in <linux/cdrom.h> indicate that this
+         ioctl accepts a cdrom_read structure, but actual source code
+         reads a cdrom_msf structure and writes a buffer of data to
+         the same address.
+
+         MSF values are converted to LBA values via this formula:
+
+           lba = (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
+
+
+
+
+CDROMREADMODE1                 Read CDROM mode 1 data (2048 Bytes)
+                                          (struct cdrom_read)
+
+       notes:
+         Identical to CDROMREADRAW except that block size is
+         CD_FRAMESIZE (2048) bytes
+
+
+
+CDROMREADMODE2                 Read CDROM mode 2 data (2336 Bytes)
+                                          (struct cdrom_read)
+
+       notes:
+         Identical to CDROMREADRAW except that block size is
+         CD_FRAMESIZE_RAW0 (2336) bytes
+
+
+
+CDROMREADAUDIO                 (struct cdrom_read_audio)
+
+       usage:
+
+         struct cdrom_read_audio ra;
+         ioctl(fd, CDROMREADAUDIO, &ra);
+
+       inputs:
+         cdrom_read_audio structure containing read start
+         point and length
+
+       outputs:
+         audio data, returned to buffer indicated by ra
+
+       error return:
+         EINVAL        format not CDROM_MSF or CDROM_LBA
+         EINVAL        nframes not in range [1 75]
+         ENXIO         drive has no queue (probably means invalid fd)
+         ENOMEM        out of memory
+
+
+CDROMEJECT_SW                  enable(1)/disable(0) auto-ejecting
+
+       usage:
+
+         int val;
+         ioctl(fd, CDROMEJECT_SW, val);
+
+       inputs:
+         Flag specifying auto-eject flag.
+
+       outputs:        none
+
+       error return:
+         ENOSYS        Drive is not capable of ejecting.
+         EBUSY         Door is locked
+
+
+
+
+CDROMMULTISESSION              Obtain the start-of-last-session
+                                 address of multi session disks
+                                 (struct cdrom_multisession)
+       usage:
+
+         struct cdrom_multisession ms_info;
+         ioctl(fd, CDROMMULTISESSION, &ms_info);
+
+       inputs:
+         cdrom_multisession structure containing desired
+         format.
+
+       outputs:
+         cdrom_multisession structure is filled with last_session
+         information.
+
+       error return:
+         EINVAL        format not CDROM_MSF or CDROM_LBA
+
+
+CDROM_GET_MCN                  Obtain the "Universal Product Code"
+                                  if available (struct cdrom_mcn)
+
+       usage:
+
+         struct cdrom_mcn mcn;
+         ioctl(fd, CDROM_GET_MCN, &mcn);
+
+       inputs:         none
+
+       outputs:
+         Universal Product Code
+
+       error return:
+         ENOSYS        Drive is not capable of reading MCN data.
+
+       notes:
+         Source code comments state:
+
+           The following function is implemented, although very few
+           audio discs give Universal Product Code information, which
+           should just be the Medium Catalog Number on the box.  Note,
+           that the way the code is written on the CD is /not/ uniform
+           across all discs!
+
+
+
+
+CDROM_GET_UPC                  CDROM_GET_MCN  (deprecated)
+
+       Not implemented, as of 2.6.8.1
+
+
+
+CDROMRESET                     hard-reset the drive
+
+       usage:
+
+         ioctl(fd, CDROMRESET, 0);
+
+       inputs:         none
+
+       outputs:        none
+
+       error return:
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         ENOSYS        Drive is not capable of resetting.
+
+
+
+
+CDROMREADCOOKED                        read data in cooked mode
+
+       usage:
+
+         u8 buffer[CD_FRAMESIZE]
+         ioctl(fd, CDROMREADCOOKED, buffer);
+
+       inputs:         none
+
+       outputs:
+         2048 bytes of data, "cooked" mode.
+
+       notes:
+         Not implemented on all drives.
+
+
+
+
+CDROMREADALL                   read all 2646 bytes
+
+       Same as CDROMREADCOOKED, but reads 2646 bytes.
+
+
+
+CDROMSEEK                      seek msf address
+
+       usage:
+
+         struct cdrom_msf msf;
+         ioctl(fd, CDROMSEEK, &msf);
+
+       inputs:
+         MSF address to seek to.
+
+       outputs:        none
+
+
+
+CDROMPLAYBLK                   scsi-cd only, (struct cdrom_blk)
+
+       usage:
+
+         struct cdrom_blk blk;
+         ioctl(fd, CDROMPLAYBLK, &blk);
+
+       inputs:
+         Region to play
+
+       outputs:        none
+
+
+
+CDROMGETSPINDOWN
+
+       usage:
+
+         char spindown;
+         ioctl(fd, CDROMGETSPINDOWN, &spindown);
+
+       inputs:         none
+
+       outputs:
+         The value of the current 4-bit spindown value.
+
+
+
+
+CDROMSETSPINDOWN
+
+       usage:
+
+         char spindown
+         ioctl(fd, CDROMSETSPINDOWN, &spindown);
+
+       inputs:
+         4-bit value used to control spindown (TODO: more detail here)
+
+       outputs:        none
+
+
+
+
+
+CDROM_SET_OPTIONS              Set behavior options
+
+       usage:
+
+         int options;
+         ioctl(fd, CDROM_SET_OPTIONS, options);
+
+       inputs:
+         New values for drive options.  The logical 'or' of:
+           CDO_AUTO_CLOSE      close tray on first open(2)
+           CDO_AUTO_EJECT      open tray on last release
+           CDO_USE_FFLAGS      use O_NONBLOCK information on open
+           CDO_LOCK            lock tray on open files
+           CDO_CHECK_TYPE      check type on open for data
+
+       outputs:
+         Returns the resulting options settings in the
+         ioctl return value.  Returns -1 on error.
+
+       error return:
+         ENOSYS        selected option(s) not supported by drive.
+
+
+
+
+CDROM_CLEAR_OPTIONS            Clear behavior options
+
+       Same as CDROM_SET_OPTIONS, except that selected options are
+       turned off.
+
+
+
+CDROM_SELECT_SPEED             Set the CD-ROM speed
+
+       usage:
+
+         int speed;
+         ioctl(fd, CDROM_SELECT_SPEED, speed);
+
+       inputs:
+         New drive speed.
+
+       outputs:        none
+
+       error return:
+         ENOSYS        speed selection not supported by drive.
+
+
+
+CDROM_SELECT_DISC              Select disc (for juke-boxes)
+
+       usage:
+
+         int disk;
+         ioctl(fd, CDROM_SELECT_DISC, disk);
+
+       inputs:
+         Disk to load into drive.
+
+       outputs:        none
+
+       error return:
+         EINVAL        Disk number beyond capacity of drive
+
+
+
+CDROM_MEDIA_CHANGED            Check is media changed
+
+       usage:
+
+         int slot;
+         ioctl(fd, CDROM_MEDIA_CHANGED, slot);
+
+       inputs:
+         Slot number to be tested, always zero except for jukeboxes.
+         May also be special values CDSL_NONE or CDSL_CURRENT
+
+       outputs:
+         Ioctl return value is 0 or 1 depending on whether the media
+         has been changed, or -1 on error.
+
+       error returns:
+         ENOSYS        Drive can't detect media change
+         EINVAL        Slot number beyond capacity of drive
+         ENOMEM        Out of memory
+
+
+
+CDROM_DRIVE_STATUS             Get tray position, etc.
+
+       usage:
+
+         int slot;
+         ioctl(fd, CDROM_DRIVE_STATUS, slot);
+
+       inputs:
+         Slot number to be tested, always zero except for jukeboxes.
+         May also be special values CDSL_NONE or CDSL_CURRENT
+
+       outputs:
+         Ioctl return value will be one of the following values
+         from <linux/cdrom.h>:
+
+           CDS_NO_INFO         Information not available.
+           CDS_NO_DISC
+           CDS_TRAY_OPEN
+           CDS_DRIVE_NOT_READY
+           CDS_DISC_OK
+           -1                  error
+
+       error returns:
+         ENOSYS        Drive can't detect drive status
+         EINVAL        Slot number beyond capacity of drive
+         ENOMEM        Out of memory
+
+
+
+
+CDROM_DISC_STATUS              Get disc type, etc.
+
+       usage:
+
+         ioctl(fd, CDROM_DISC_STATUS, 0);
+
+       inputs:         none
+
+       outputs:
+         Ioctl return value will be one of the following values
+         from <linux/cdrom.h>:
+           CDS_NO_INFO
+           CDS_AUDIO
+           CDS_MIXED
+           CDS_XA_2_2
+           CDS_XA_2_1
+           CDS_DATA_1
+
+       error returns:  none at present
+
+       notes:
+         Source code comments state:
+
+           Ok, this is where problems start.  The current interface for
+           the CDROM_DISC_STATUS ioctl is flawed.  It makes the false
+           assumption that CDs are all CDS_DATA_1 or all CDS_AUDIO, etc.
+           Unfortunatly, while this is often the case, it is also
+           very common for CDs to have some tracks with data, and some
+           tracks with audio.  Just because I feel like it, I declare
+           the following to be the best way to cope.  If the CD has
+           ANY data tracks on it, it will be returned as a data CD.
+           If it has any XA tracks, I will return it as that.  Now I
+           could simplify this interface by combining these returns with
+           the above, but this more clearly demonstrates the problem
+           with the current interface.  Too bad this wasn't designed
+           to use bitmasks...         -Erik
+
+           Well, now we have the option CDS_MIXED: a mixed-type CD.
+           User level programmers might feel the ioctl is not very
+           useful.
+                       ---david
+
+
+
+
+CDROM_CHANGER_NSLOTS           Get number of slots
+
+       usage:
+
+         ioctl(fd, CDROM_CHANGER_NSLOTS, 0);
+
+       inputs:         none
+
+       outputs:
+         The ioctl return value will be the number of slots in a
+         CD changer.  Typically 1 for non-multi-disk devices.
+
+       error returns:  none
+
+
+
+CDROM_LOCKDOOR                 lock or unlock door
+
+       usage:
+
+         int lock;
+         ioctl(fd, CDROM_LOCKDOOR, lock);
+
+       inputs:
+         Door lock flag, 1=lock, 0=unlock
+
+       outputs:        none
+
+       error returns:
+         EDRIVE_CANT_DO_THIS   Door lock function not supported.
+         EBUSY                 Attempt to unlock when multiple users
+                               have the drive open and not CAP_SYS_ADMIN
+
+       notes:
+         As of 2.6.8.1, the lock flag is a global lock, meaning that
+         all CD drives will be locked or unlocked together.  This is
+         probably a bug.
+
+         The EDRIVE_CANT_DO_THIS value is defined in <linux/cdrom.h>
+         and is currently (2.6.8.1) the same as EOPNOTSUPP
+
+
+
+CDROM_DEBUG                    Turn debug messages on/off
+
+       usage:
+
+         int debug;
+         ioctl(fd, CDROM_DEBUG, debug);
+
+       inputs:
+         Cdrom debug flag, 0=disable, 1=enable
+
+       outputs:
+         The ioctl return value will be the new debug flag.
+
+       error return:
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+
+
+
+CDROM_GET_CAPABILITY           get capabilities
+
+       usage:
+
+         ioctl(fd, CDROM_GET_CAPABILITY, 0);
+
+       inputs:         none
+
+       outputs:
+         The ioctl return value is the current device capability
+         flags.  See CDC_CLOSE_TRAY, CDC_OPEN_TRAY, etc.
+
+
+
+CDROMAUDIOBUFSIZ               set the audio buffer size
+
+       usage:
+
+         int arg;
+         ioctl(fd, CDROMAUDIOBUFSIZ, val);
+
+       inputs:
+         New audio buffer size
+
+       outputs:
+         The ioctl return value is the new audio buffer size, or -1
+         on error.
+
+       error return:
+         ENOSYS        Not supported by this driver.
+
+       notes:
+         Not supported by all drivers.
+
+
+
+DVD_READ_STRUCT                        Read structure
+
+       usage:
+
+         dvd_struct s;
+         ioctl(fd, DVD_READ_STRUCT, &s);
+
+       inputs:
+         dvd_struct structure, containing:
+           type                specifies the information desired, one of
+                               DVD_STRUCT_PHYSICAL, DVD_STRUCT_COPYRIGHT,
+                               DVD_STRUCT_DISCKEY, DVD_STRUCT_BCA,
+                               DVD_STRUCT_MANUFACT
+           physical.layer_num  desired layer, indexed from 0
+           copyright.layer_num desired layer, indexed from 0
+           disckey.agid
+
+       outputs:
+         dvd_struct structure, containing:
+           physical            for type == DVD_STRUCT_PHYSICAL
+           copyright           for type == DVD_STRUCT_COPYRIGHT
+           disckey.value       for type == DVD_STRUCT_DISCKEY
+           bca.{len,value}     for type == DVD_STRUCT_BCA
+           manufact.{len,valu} for type == DVD_STRUCT_MANUFACT
+
+       error returns:
+         EINVAL        physical.layer_num exceeds number of layers
+         EIO           Recieved invalid response from drive
+
+
+
+DVD_WRITE_STRUCT               Write structure
+
+       Not implemented, as of 2.6.8.1
+
+
+
+DVD_AUTH                       Authentication
+
+       usage:
+
+         dvd_authinfo ai;
+         ioctl(fd, DVD_AUTH, &ai);
+
+       inputs:
+         dvd_authinfo structure.  See <linux/cdrom.h>
+
+       outputs:
+         dvd_authinfo structure.
+
+       error return:
+         ENOTTY        ai.type not recognized.
+
+
+
+CDROM_SEND_PACKET              send a packet to the drive
+
+       usage:
+
+         struct cdrom_generic_command cgc;
+         ioctl(fd, CDROM_SEND_PACKET, &cgc);
+
+       inputs:
+         cdrom_generic_command structure containing the packet to send.
+
+       outputs:        none
+         cdrom_generic_command structure containing results.
+
+       error return:
+         EIO           command failed.
+         EPERM         Operation not permitted, either because a
+                       write command was attempted on a drive which
+                       is opened read-only, or because the command
+                       requires CAP_SYS_RAWIO
+         EINVAL        cgc.data_direction not set
+
+
+
+CDROM_NEXT_WRITABLE            get next writable block
+
+       usage:
+
+         long next;
+         ioctl(fd, CDROM_NEXT_WRITABLE, &next);
+
+       inputs:         none
+
+       outputs:
+         The next writable block.
+
+       notes:
+         If the device does not support this ioctl directly, the
+         ioctl will return CDROM_LAST_WRITTEN + 7.
+
+
+
+CDROM_LAST_WRITTEN             get last block written on disc
+
+       usage:
+
+         long last;
+         ioctl(fd, CDROM_LAST_WRITTEN, &last);
+
+       inputs:         none
+
+       outputs:
+         The last block written on disc
+
+       notes:
+         If the device does not support this ioctl directly, the
+         result is derived from the disc's table of contents.  If the
+         table of contents can't be read, this ioctl returns an
+         error.
diff --git a/Documentation/ioctl/hdio.txt b/Documentation/ioctl/hdio.txt
new file mode 100644 (file)
index 0000000..c42d3b6
--- /dev/null
@@ -0,0 +1,965 @@
+               Summary of HDIO_ ioctl calls.
+               ============================
+
+               Edward A. Falk <efalk@google.com>
+
+               November, 2004
+
+This document attempts to describe the ioctl(2) calls supported by
+the HD/IDE layer.  These are by-and-large implemented (as of Linux 2.6)
+in drivers/ide/ide.c and drivers/block/scsi_ioctl.c
+
+ioctl values are listed in <linux/hdreg.h>.  As of this writing, they
+are as follows:
+
+    ioctls that pass argument pointers to user space:
+
+       HDIO_GETGEO             get device geometry
+       HDIO_GET_UNMASKINTR     get current unmask setting
+       HDIO_GET_MULTCOUNT      get current IDE blockmode setting
+       HDIO_GET_QDMA           get use-qdma flag
+       HDIO_SET_XFER           set transfer rate via proc
+       HDIO_OBSOLETE_IDENTITY  OBSOLETE, DO NOT USE
+       HDIO_GET_KEEPSETTINGS   get keep-settings-on-reset flag
+       HDIO_GET_32BIT          get current io_32bit setting
+       HDIO_GET_NOWERR         get ignore-write-error flag
+       HDIO_GET_DMA            get use-dma flag
+       HDIO_GET_NICE           get nice flags
+       HDIO_GET_IDENTITY       get IDE identification info
+       HDIO_GET_WCACHE         get write cache mode on|off
+       HDIO_GET_ACOUSTIC       get acoustic value
+       HDIO_GET_ADDRESS        get sector addressing mode
+       HDIO_GET_BUSSTATE       get the bus state of the hwif
+       HDIO_TRISTATE_HWIF      execute a channel tristate
+       HDIO_DRIVE_RESET        execute a device reset
+       HDIO_DRIVE_TASKFILE     execute raw taskfile
+       HDIO_DRIVE_TASK         execute task and special drive command
+       HDIO_DRIVE_CMD          execute a special drive command
+       HDIO_DRIVE_CMD_AEB      HDIO_DRIVE_TASK
+
+    ioctls that pass non-pointer values:
+
+       HDIO_SET_MULTCOUNT      change IDE blockmode
+       HDIO_SET_UNMASKINTR     permit other irqs during I/O
+       HDIO_SET_KEEPSETTINGS   keep ioctl settings on reset
+       HDIO_SET_32BIT          change io_32bit flags
+       HDIO_SET_NOWERR         change ignore-write-error flag
+       HDIO_SET_DMA            change use-dma flag
+       HDIO_SET_PIO_MODE       reconfig interface to new speed
+       HDIO_SCAN_HWIF          register and (re)scan interface
+       HDIO_SET_NICE           set nice flags
+       HDIO_UNREGISTER_HWIF    unregister interface
+       HDIO_SET_WCACHE         change write cache enable-disable
+       HDIO_SET_ACOUSTIC       change acoustic behavior
+       HDIO_SET_BUSSTATE       set the bus state of the hwif
+       HDIO_SET_QDMA           change use-qdma flag
+       HDIO_SET_ADDRESS        change lba addressing modes
+
+       HDIO_SET_IDE_SCSI       Set scsi emulation mode on/off
+       HDIO_SET_SCSI_IDE       not implemented yet
+
+
+The information that follows was determined from reading kernel source
+code.  It is likely that some corrections will be made over time.
+
+
+
+
+
+
+
+General:
+
+       Unless otherwise specified, all ioctl calls return 0 on success
+       and -1 with errno set to an appropriate value on error.
+
+       Unless otherwise specified, all ioctl calls return -1 and set
+       errno to EFAULT on a failed attempt to copy data to or from user
+       address space.
+
+       Unless otherwise specified, all data structures and constants
+       are defined in <linux/hdreg.h>
+
+
+
+HDIO_GETGEO                    get device geometry
+
+       usage:
+
+         struct hd_geometry geom;
+         ioctl(fd, HDIO_GETGEO, &geom);
+
+
+       inputs:         none
+
+       outputs:
+
+         hd_geometry structure containing:
+
+           heads       number of heads
+           sectors     number of sectors/track
+           cylinders   number of cylinders, mod 65536
+           start       starting sector of this partition.
+
+
+       error returns:
+         EINVAL        if the device is not a disk drive or floppy drive,
+                       or if the user passes a null pointer
+
+
+       notes:
+
+         Not particularly useful with modern disk drives, whose geometry
+         is a polite fiction anyway.  Modern drives are addressed
+         purely by sector number nowadays (lba addressing), and the
+         drive geometry is an abstraction which is actually subject
+         to change.  Currently (as of Nov 2004), the geometry values
+         are the "bios" values -- presumably the values the drive had
+         when Linux first booted.
+
+         In addition, the cylinders field of the hd_geometry is an
+         unsigned short, meaning that on most architectures, this
+         ioctl will not return a meaningful value on drives with more
+         than 65535 tracks.
+
+         The start field is unsigned long, meaning that it will not
+         contain a meaningful value for disks over 219 Gb in size.
+
+
+
+
+HDIO_GET_UNMASKINTR            get current unmask setting
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_GET_UNMASKINTR, &val);
+
+       inputs:         none
+
+       outputs:
+         The value of the drive's current unmask setting
+
+
+
+HDIO_SET_UNMASKINTR            permit other irqs during I/O
+
+       usage:
+
+         unsigned long val;
+         ioctl(fd, HDIO_SET_UNMASKINTR, val);
+
+       inputs:
+         New value for unmask flag
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range [0 1]
+         EBUSY         Controller busy
+
+
+
+
+HDIO_GET_MULTCOUNT             get current IDE blockmode setting
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_GET_MULTCOUNT, &val);
+
+       inputs:         none
+
+       outputs:
+         The value of the current IDE block mode setting.  This
+         controls how many sectors the drive will transfer per
+         interrupt.
+
+
+
+HDIO_SET_MULTCOUNT             change IDE blockmode
+
+       usage:
+
+         int val;
+         ioctl(fd, HDIO_SET_MULTCOUNT, val);
+
+       inputs:
+         New value for IDE block mode setting.  This controls how many
+         sectors the drive will transfer per interrupt.
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range supported by disk.
+         EBUSY         Controller busy or blockmode already set.
+         EIO           Drive did not accept new block mode.
+
+       notes:
+
+         Source code comments read:
+
+           This is tightly woven into the driver->do_special can not
+           touch.  DON'T do it again until a total personality rewrite
+           is committed.
+
+         If blockmode has already been set, this ioctl will fail with
+         EBUSY
+
+
+
+HDIO_GET_QDMA                  get use-qdma flag
+
+       Not implemented, as of 2.6.8.1
+
+
+
+HDIO_SET_XFER                  set transfer rate via proc
+
+       Not implemented, as of 2.6.8.1
+
+
+
+HDIO_OBSOLETE_IDENTITY         OBSOLETE, DO NOT USE
+
+       Same as HDIO_GET_IDENTITY (see below), except that it only
+       returns the first 142 bytes of drive identity information.
+
+
+
+HDIO_GET_IDENTITY              get IDE identification info
+
+       usage:
+
+         unsigned char identity[512];
+         ioctl(fd, HDIO_GET_IDENTITY, identity);
+
+       inputs:         none
+
+       outputs:
+
+         ATA drive identity information.  For full description, see
+         the IDENTIFY DEVICE and IDENTIFY PACKET DEVICE commands in
+         the ATA specification.
+
+       error returns:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         ENOMSG        IDENTIFY DEVICE information not available
+
+       notes:
+
+         Returns information that was obtained when the drive was
+         probed.  Some of this information is subject to change, and
+         this ioctl does not re-probe the drive to update the
+         information.
+
+         This information is also available from /proc/ide/hdX/identify
+
+
+
+HDIO_GET_KEEPSETTINGS          get keep-settings-on-reset flag
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_GET_KEEPSETTINGS, &val);
+
+       inputs:         none
+
+       outputs:
+         The value of the current "keep settings" flag
+
+       notes:
+
+         When set, indicates that kernel should restore settings
+         after a drive reset.
+
+
+
+HDIO_SET_KEEPSETTINGS          keep ioctl settings on reset
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_SET_KEEPSETTINGS, val);
+
+       inputs:
+         New value for keep_settings flag
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range [0 1]
+         EBUSY         Controller busy
+
+
+
+HDIO_GET_32BIT                 get current io_32bit setting
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_GET_32BIT, &val);
+
+       inputs:         none
+
+       outputs:
+         The value of the current io_32bit setting
+
+       notes:
+
+         0=16-bit, 1=32-bit, 2,3 = 32bit+sync
+
+
+
+HDIO_GET_NOWERR                        get ignore-write-error flag
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_GET_NOWERR, &val);
+
+       inputs:         none
+
+       outputs:
+         The value of the current ignore-write-error flag
+
+
+
+HDIO_GET_DMA                   get use-dma flag
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_GET_DMA, &val);
+
+       inputs:         none
+
+       outputs:
+         The value of the current use-dma flag
+
+
+
+HDIO_GET_NICE                  get nice flags
+
+       usage:
+
+         long nice;
+         ioctl(fd, HDIO_GET_NICE, &nice);
+
+       inputs:         none
+
+       outputs:
+
+         The drive's "nice" values.
+
+       notes:
+
+         Per-drive flags which determine when the system will give more
+         bandwidth to other devices sharing the same IDE bus.
+         See <linux/hdreg.h>, near symbol IDE_NICE_DSC_OVERLAP.
+
+
+
+
+HDIO_SET_NICE                  set nice flags
+
+       usage:
+
+         unsigned long nice;
+         ...
+         ioctl(fd, HDIO_SET_NICE, nice);
+
+       inputs:
+         bitmask of nice flags.
+
+       outputs:        none
+
+       error returns:
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EPERM         Flags other than DSC_OVERLAP and NICE_1 set.
+         EPERM         DSC_OVERLAP specified but not supported by drive
+
+       notes:
+
+         This ioctl sets the DSC_OVERLAP and NICE_1 flags from values
+         provided by the user.
+
+         Nice flags are listed in <linux/hdreg.h>, starting with
+         IDE_NICE_DSC_OVERLAP.  These values represent shifts.
+
+
+
+
+
+HDIO_GET_WCACHE                        get write cache mode on|off
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_GET_WCACHE, &val);
+
+       inputs:         none
+
+       outputs:
+         The value of the current write cache mode
+
+
+
+HDIO_GET_ACOUSTIC              get acoustic value
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_GET_ACOUSTIC, &val);
+
+       inputs:         none
+
+       outputs:
+         The value of the current acoustic settings
+
+       notes:
+
+         See HDIO_SET_ACOUSTIC
+
+
+
+HDIO_GET_ADDRESS
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_GET_ADDRESS, &val);
+
+       inputs:         none
+
+       outputs:
+         The value of the current addressing mode:
+           0 = 28-bit
+           1 = 48-bit
+           2 = 48-bit doing 28-bit
+           3 = 64-bit
+
+
+
+HDIO_GET_BUSSTATE              get the bus state of the hwif
+
+       usage:
+
+         long state;
+         ioctl(fd, HDIO_SCAN_HWIF, &state);
+
+       inputs:         none
+
+       outputs:
+         Current power state of the IDE bus.  One of BUSSTATE_OFF,
+         BUSSTATE_ON, or BUSSTATE_TRISTATE
+
+       error returns:
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+
+
+
+
+HDIO_SET_BUSSTATE              set the bus state of the hwif
+
+       usage:
+
+         int state;
+         ...
+         ioctl(fd, HDIO_SCAN_HWIF, state);
+
+       inputs:
+         Desired IDE power state.  One of BUSSTATE_OFF, BUSSTATE_ON,
+         or BUSSTATE_TRISTATE
+
+       outputs:        none
+
+       error returns:
+         EACCES        Access denied:  requires CAP_SYS_RAWIO
+         EOPNOTSUPP    Hardware interface does not support bus power control
+
+
+
+
+HDIO_TRISTATE_HWIF             execute a channel tristate
+
+       Not implemented, as of 2.6.8.1.  See HDIO_SET_BUSSTATE
+
+
+
+HDIO_DRIVE_RESET               execute a device reset
+
+       usage:
+
+         int args[3]
+         ...
+         ioctl(fd, HDIO_DRIVE_RESET, args);
+
+       inputs:         none
+
+       outputs:        none
+
+       error returns:
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+
+       notes:
+
+         Abort any current command, prevent anything else from being
+         queued, execute a reset on the device, and issue BLKRRPART
+         ioctl on the block device.
+
+         Executes an ATAPI soft reset if applicable, otherwise
+         executes an ATA soft reset on the controller.
+
+
+
+HDIO_DRIVE_TASKFILE            execute raw taskfile
+
+       Note:  If you don't have a copy of the ANSI ATA specification
+       handy, you should probably ignore this ioctl.
+
+       Execute an ATA disk command directly by writing the "taskfile"
+       registers of the drive.  Requires ADMIN and RAWIO access
+       privileges.
+
+       usage:
+
+         struct {
+           ide_task_request_t req_task;
+           u8 outbuf[OUTPUT_SIZE];
+           u8 inbuf[INPUT_SIZE];
+         } task;
+         memset(&task.req_task, 0, sizeof(task.req_task));
+         task.req_task.out_size = sizeof(task.outbuf);
+         task.req_task.in_size = sizeof(task.inbuf);
+         ...
+         ioctl(fd, HDIO_DRIVE_TASKFILE, &task);
+         ...
+
+       inputs:
+
+         (See below for details on memory area passed to ioctl.)
+
+         io_ports[8]   values to be written to taskfile registers
+         hob_ports[8]  high-order bytes, for extended commands.
+         out_flags     flags indicating which registers are valid
+         in_flags      flags indicating which registers should be returned
+         data_phase    see below
+         req_cmd       command type to be executed
+         out_size      size of output buffer
+         outbuf        buffer of data to be transmitted to disk
+         inbuf         buffer of data to be received from disk (see [1])
+
+       outputs:
+
+         io_ports[]    values returned in the taskfile registers
+         hob_ports[]   high-order bytes, for extended commands.
+         out_flags     flags indicating which registers are valid (see [2])
+         in_flags      flags indicating which registers should be returned
+         outbuf        buffer of data to be transmitted to disk (see [1])
+         inbuf         buffer of data to be received from disk
+
+       error returns:
+         EACCES        CAP_SYS_ADMIN or CAP_SYS_RAWIO privilege not set.
+         ENOMSG        Device is not a disk drive.
+         ENOMEM        Unable to allocate memory for task
+         EFAULT        req_cmd == TASKFILE_IN_OUT (not implemented as of 2.6.8)
+         EPERM         req_cmd == TASKFILE_MULTI_OUT and drive
+                       multi-count not yet set.
+
+
+       notes:
+
+         [1] Currently (2.6.8), both the input and output buffers are
+         copied from the user and written back to the user, even when
+         not used.  This may be a bug.
+
+         [2] The out_flags and in_flags are returned to the user after
+         the ioctl completes.  Currently (2.6.8) these are the same
+         as the input values, unchanged.  In the future, they may have
+         more significance.
+
+         Extreme caution should be used with using this ioctl.  A
+         mistake can easily corrupt data or hang the system.
+
+         The argument to the ioctl is a pointer to a region of memory
+         containing a ide_task_request_t structure, followed by an
+         optional buffer of data to be transmitted to the drive,
+         followed by an optional buffer to receive data from the drive.
+
+         Command is passed to the disk drive via the ide_task_request_t
+         structure, which contains these fields:
+
+           io_ports[8]         values for the taskfile registers
+           hob_ports[8]        high-order bytes, for extended commands
+           out_flags           flags indicating which entries in the
+                               io_ports[] and hob_ports[] arrays
+                               contain valid values.  Type ide_reg_valid_t.
+           in_flags            flags indicating which entries in the
+                               io_ports[] and hob_ports[] arrays
+                               are expected to contain valid values
+                               on return.
+           data_phase          See below
+           req_cmd             Command type, see below
+           out_size            output (user->drive) buffer size, bytes
+           in_size             input (drive->user) buffer size, bytes
+
+         This ioctl does not necessarily respect all flags in the
+         out_flags and in_flags values -- some taskfile registers
+         may be written or read even if not requested in the flags.
+         Unused fields of io_ports[] and hob_ports[] should be set
+         to zero.
+
+         The data_phase field describes the data transfer to be
+         performed.  Value is one of:
+
+           TASKFILE_IN
+           TASKFILE_MULTI_IN
+           TASKFILE_OUT
+           TASKFILE_MULTI_OUT
+           TASKFILE_IN_OUT
+           TASKFILE_IN_DMA
+           TASKFILE_IN_DMAQ
+           TASKFILE_OUT_DMA
+           TASKFILE_OUT_DMAQ
+           TASKFILE_P_IN
+           TASKFILE_P_IN_DMA
+           TASKFILE_P_IN_DMAQ
+           TASKFILE_P_OUT
+           TASKFILE_P_OUT_DMA
+           TASKFILE_P_OUT_DMAQ
+
+         The req_cmd field classifies the command type.  It may be
+         one of:
+
+           IDE_DRIVE_TASK_NO_DATA
+           IDE_DRIVE_TASK_SET_XFER
+           IDE_DRIVE_TASK_IN
+           IDE_DRIVE_TASK_OUT
+           IDE_DRIVE_TASK_RAW_WRITE
+
+
+
+
+
+
+HDIO_DRIVE_CMD                 execute a special drive command
+
+       Note:  If you don't have a copy of the ANSI ATA specification
+       handy, you should probably ignore this ioctl.
+
+       usage:
+
+         u8 args[4+XFER_SIZE];
+         ...
+         ioctl(fd, HDIO_DRIVE_CMD, args);
+
+       inputs:
+
+         Taskfile register values:
+           args[0]     COMMAND
+           args[1]     SECTOR
+           args[2]     FEATURE
+           args[3]     NSECTOR
+
+       outputs:
+
+         args[] buffer is filled with register values followed by any
+         data returned by the disk.
+           args[0]     status
+           args[1]     error
+           args[2]     NSECTOR
+           args[3]     undefined
+           args[4+]    NSECTOR * 512 bytes of data returned by the command.
+
+       error returns:
+         EACCES        Access denied:  requires CAP_SYS_RAWIO
+         ENOMEM        Unable to allocate memory for task
+
+       notes:
+
+         Taskfile registers IDE_LCYL, IDE_HCYL, and IDE_SELECT are
+         set to zero before executing the command.
+
+
+
+HDIO_DRIVE_TASK                        execute task and special drive command
+
+       Note:  If you don't have a copy of the ANSI ATA specification
+       handy, you should probably ignore this ioctl.
+
+       usage:
+
+         u8 args[7];
+         ...
+         ioctl(fd, HDIO_DRIVE_TASK, args);
+
+       inputs:
+
+         Taskfile register values:
+           args[0]     COMMAND
+           args[1]     FEATURE
+           args[2]     NSECTOR
+           args[3]     SECTOR
+           args[4]     LCYL
+           args[5]     HCYL
+           args[6]     SELECT
+
+       outputs:
+
+         Taskfile register values:
+           args[0]     status
+           args[1]     error
+           args[2]     NSECTOR
+           args[3]     SECTOR
+           args[4]     LCYL
+           args[5]     HCYL
+           args[6]     SELECT
+
+       error returns:
+         EACCES        Access denied:  requires CAP_SYS_RAWIO
+         ENOMEM        Unable to allocate memory for task
+
+
+
+
+HDIO_DRIVE_CMD_AEB             HDIO_DRIVE_TASK
+
+       Not implemented, as of 2.6.8.1
+
+
+
+HDIO_SET_32BIT                 change io_32bit flags
+
+       usage:
+
+         int val;
+         ioctl(fd, HDIO_SET_32BIT, val);
+
+       inputs:
+         New value for io_32bit flag
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range [0 3]
+         EBUSY         Controller busy
+
+
+
+
+HDIO_SET_NOWERR                        change ignore-write-error flag
+
+       usage:
+
+         int val;
+         ioctl(fd, HDIO_SET_NOWERR, val);
+
+       inputs:
+         New value for ignore-write-error flag.  Used for ignoring
+         WRERR_STAT
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range [0 1]
+         EBUSY         Controller busy
+
+
+
+HDIO_SET_DMA                   change use-dma flag
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_SET_DMA, val);
+
+       inputs:
+         New value for use-dma flag
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range [0 1]
+         EBUSY         Controller busy
+
+
+
+HDIO_SET_PIO_MODE              reconfig interface to new speed
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_SET_PIO_MODE, val);
+
+       inputs:
+         New interface speed.
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range [0 255]
+         EBUSY         Controller busy
+
+
+
+HDIO_SCAN_HWIF                 register and (re)scan interface
+
+       usage:
+
+         int args[3]
+         ...
+         ioctl(fd, HDIO_SCAN_HWIF, args);
+
+       inputs:
+         args[0]       io address to probe
+         args[1]       control address to probe
+         args[2]       irq number
+
+       outputs:        none
+
+       error returns:
+         EACCES        Access denied:  requires CAP_SYS_RAWIO
+         EIO           Probe failed.
+
+       notes:
+
+         This ioctl initializes the addresses and irq for a disk
+         controller, probes for drives, and creates /proc/ide
+         interfaces as appropiate.
+
+
+
+HDIO_UNREGISTER_HWIF           unregister interface
+
+       usage:
+
+         int index;
+         ioctl(fd, HDIO_UNREGISTER_HWIF, index);
+
+       inputs:
+         index         index of hardware interface to unregister
+
+       outputs:        none
+
+       error returns:
+         EACCES        Access denied:  requires CAP_SYS_RAWIO
+
+       notes:
+
+         This ioctl removes a hardware interface from the kernel.
+
+         Currently (2.6.8) this ioctl silently fails if any drive on
+         the interface is busy.
+
+
+
+HDIO_SET_WCACHE                        change write cache enable-disable
+
+       usage:
+
+         int val;
+         ioctl(fd, HDIO_SET_WCACHE, val);
+
+       inputs:
+         New value for write cache enable
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range [0 1]
+         EBUSY         Controller busy
+
+
+
+HDIO_SET_ACOUSTIC              change acoustic behavior
+
+       usage:
+
+         int val;
+         ioctl(fd, HDIO_SET_ACOUSTIC, val);
+
+       inputs:
+         New value for drive acoustic settings
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range [0 254]
+         EBUSY         Controller busy
+
+
+
+HDIO_SET_QDMA                  change use-qdma flag
+
+       Not implemented, as of 2.6.8.1
+
+
+
+HDIO_SET_ADDRESS               change lba addressing modes
+
+       usage:
+
+         int val;
+         ioctl(fd, HDIO_SET_ADDRESS, val);
+
+       inputs:
+         New value for addressing mode
+           0 = 28-bit
+           1 = 48-bit
+           2 = 48-bit doing 28-bit
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range [0 2]
+         EBUSY         Controller busy
+         EIO           Drive does not support lba48 mode.
+
+
+HDIO_SET_IDE_SCSI
+
+       usage:
+
+         long val;
+         ioctl(fd, HDIO_SET_IDE_SCSI, val);
+
+       inputs:
+         New value for scsi emulation mode (?)
+
+       outputs:        none
+
+       error return:
+         EINVAL        (bdev != bdev->bd_contains) (not sure what this means)
+         EACCES        Access denied:  requires CAP_SYS_ADMIN
+         EINVAL        value out of range [0 1]
+         EBUSY         Controller busy
+
+
+
+HDIO_SET_SCSI_IDE
+
+       Not implemented, as of 2.6.8.1
+
+
diff --git a/Documentation/keys.txt b/Documentation/keys.txt
new file mode 100644 (file)
index 0000000..36cbb0d
--- /dev/null
@@ -0,0 +1,836 @@
+                        ============================
+                        KERNEL KEY RETENTION SERVICE
+                        ============================
+
+This service allows cryptographic keys, authentication tokens, cross-domain
+user mappings, and similar to be cached in the kernel for the use of
+filesystems other kernel services.
+
+Keyrings are permitted; these are a special type of key that can hold links to
+other keys. Processes each have three standard keyring subscriptions that a
+kernel service can search for relevant keys.
+
+The key service can be configured on by enabling:
+
+       "Security options"/"Enable access key retention support" (CONFIG_KEYS)
+
+This document has the following sections:
+
+       - Key overview
+       - Key service overview
+       - Key access permissions
+       - New procfs files
+       - Userspace system call interface
+       - Kernel services
+       - Defining a key type
+       - Request-key callback service
+       - Key access filesystem
+
+
+============
+KEY OVERVIEW
+============
+
+In this context, keys represent units of cryptographic data, authentication
+tokens, keyrings, etc.. These are represented in the kernel by struct key.
+
+Each key has a number of attributes:
+
+       - A serial number.
+       - A type.
+       - A description (for matching a key in a search).
+       - Access control information.
+       - An expiry time.
+       - A payload.
+       - State.
+
+
+ (*) Each key is issued a serial number of type key_serial_t that is unique
+     for the lifetime of that key. All serial numbers are positive non-zero
+     32-bit integers.
+
+     Userspace programs can use a key's serial numbers as a way to gain access
+     to it, subject to permission checking.
+
+ (*) Each key is of a defined "type". Types must be registered inside the
+     kernel by a kernel service (such as a filesystem) before keys of that
+     type can be added or used. Userspace programs cannot define new types
+     directly.
+
+     Key types are represented in the kernel by struct key_type. This defines
+     a number of operations that can be performed on a key of that type.
+
+     Should a type be removed from the system, all the keys of that type will
+     be invalidated.
+
+ (*) Each key has a description. This should be a printable string. The key
+     type provides an operation to perform a match between the description on
+     a key and a criterion string.
+
+ (*) Each key has an owner user ID, a group ID and a permissions mask. These
+     are used to control what a process may do to a key from userspace, and
+     whether a kernel service will be able to find the key.
+
+ (*) Each key can be set to expire at a specific time by the key type's
+     instantiation function. Keys can also be immortal.
+
+ (*) Each key can have a payload. This is a quantity of data that represent
+     the actual "key". In the case of a keyring, this is a list of keys to
+     which the keyring links; in the case of a user-defined key, it's an
+     arbitrary blob of data.
+
+     Having a payload is not required; and the payload can, in fact, just be a
+     value stored in the struct key itself.
+
+     When a key is instantiated, the key type's instantiation function is
+     called with a blob of data, and that then creates the key's payload in
+     some way.
+
+     Similarly, when userspace wants to read back the contents of the key, if
+     permitted, another key type operation will be called to convert the key's
+     attached payload back into a blob of data.
+
+ (*) Each key can be in one of a number of basic states:
+
+     (*) Uninstantiated. The key exists, but does not have any data
+        attached. Keys being requested from userspace will be in this state.
+
+     (*) Instantiated. This is the normal state. The key is fully formed, and
+        has data attached.
+
+     (*) Negative. This is a relatively short-lived state. The key acts as a
+        note saying that a previous call out to userspace failed, and acts as
+        a throttle on key lookups. A negative key can be updated to a normal
+        state.
+
+     (*) Expired. Keys can have lifetimes set. If their lifetime is exceeded,
+        they traverse to this state. An expired key can be updated back to a
+        normal state.
+
+     (*) Revoked. A key is put in this state by userspace action. It can't be
+        found or operated upon (apart from by unlinking it).
+
+     (*) Dead. The key's type was unregistered, and so the key is now useless.
+
+
+====================
+KEY SERVICE OVERVIEW
+====================
+
+The key service provides a number of features besides keys:
+
+ (*) The key service defines two special key types:
+
+     (+) "keyring"
+
+        Keyrings are special keys that contain a list of other keys. Keyring
+        lists can be modified using various system calls. Keyrings should not
+        be given a payload when created.
+
+     (+) "user"
+
+        A key of this type has a description and a payload that are arbitrary
+        blobs of data. These can be created, updated and read by userspace,
+        and aren't intended for use by kernel services.
+
+ (*) Each process subscribes to three keyrings: a thread-specific keyring, a
+     process-specific keyring, and a session-specific keyring.
+
+     The thread-specific keyring is discarded from the child when any sort of
+     clone, fork, vfork or execve occurs. A new keyring is created only when
+     required.
+
+     The process-specific keyring is replaced with an empty one in the child
+     on clone, fork, vfork unless CLONE_THREAD is supplied, in which case it
+     is shared. execve also discards the process's process keyring and creates
+     a new one.
+
+     The session-specific keyring is persistent across clone, fork, vfork and
+     execve, even when the latter executes a set-UID or set-GID binary. A
+     process can, however, replace its current session keyring with a new one
+     by using PR_JOIN_SESSION_KEYRING. It is permitted to request an anonymous
+     new one, or to attempt to create or join one of a specific name.
+
+     The ownership of the thread and process-specific keyrings changes when
+     the real UID and GID of the thread changes.
+
+ (*) Each user ID resident in the system holds two special keyrings: a user
+     specific keyring and a default user session keyring. The default session
+     keyring is initialised with a link to the user-specific keyring.
+
+     When a process changes its real UID, if it used to have no session key, it
+     will be subscribed to the default session key for the new UID.
+
+     If a process attempts to access its session key when it doesn't have one,
+     it will be subscribed to the default for its current UID.
+
+ (*) Each user has two quotas against which the keys they own are tracked. One
+     limits the total number of keys and keyrings, the other limits the total
+     amount of description and payload space that can be consumed.
+
+     The user can view information on this and other statistics through procfs
+     files.
+
+     Process-specific and thread-specific keyrings are not counted towards a
+     user's quota.
+
+     If a system call that modifies a key or keyring in some way would put the
+     user over quota, the operation is refused and error EDQUOT is returned.
+
+ (*) There's a system call interface by which userspace programs can create
+     and manipulate keys and keyrings.
+
+ (*) There's a kernel interface by which services can register types and
+     search for keys.
+
+ (*) There's a way for the a search done from the kernel to call back to
+     userspace to request a key that can't be found in a process's keyrings.
+
+ (*) An optional filesystem is available through which the key database can be
+     viewed and manipulated.
+
+
+======================
+KEY ACCESS PERMISSIONS
+======================
+
+Keys have an owner user ID, a group access ID, and a permissions mask. The
+mask has up to eight bits each for user, group and other access. Only five of
+each set of eight bits are defined. These permissions granted are:
+
+ (*) View
+
+     This permits a key or keyring's attributes to be viewed - including key
+     type and description.
+
+ (*) Read
+
+     This permits a key's payload to be viewed or a keyring's list of linked
+     keys.
+
+ (*) Write
+
+     This permits a key's payload to be instantiated or updated, or it allows
+     a link to be added to or removed from a keyring.
+
+ (*) Search
+
+     This permits keyrings to be searched and keys to be found. Searches can
+     only recurse into nested keyrings that have search permission set.
+
+ (*) Link
+
+     This permits a key or keyring to be linked to. To create a link from a
+     keyring to a key, a process must have Write permission on the keyring and
+     Link permission on the key.
+
+For changing the ownership, group ID or permissions mask, being the owner of
+the key or having the sysadmin capability is sufficient.
+
+
+================
+NEW PROCFS FILES
+================
+
+Two files have been added to procfs by which an administrator can find out
+about the status of the key service:
+
+ (*) /proc/keys
+
+     This lists all the keys on the system, giving information about their
+     type, description and permissions. The payload of the key is not
+     available this way:
+
+       SERIAL   FLAGS  USAGE EXPY PERM   UID   GID   TYPE      DESCRIPTION: SUMMARY
+       00000001 I-----    39 perm 1f0000     0     0 keyring   _uid_ses.0: 1/4
+       00000002 I-----     2 perm 1f0000     0     0 keyring   _uid.0: empty
+       00000007 I-----     1 perm 1f0000     0     0 keyring   _pid.1: empty
+       0000018d I-----     1 perm 1f0000     0     0 keyring   _pid.412: empty
+       000004d2 I--Q--     1 perm 1f0000    32    -1 keyring   _uid.32: 1/4
+       000004d3 I--Q--     3 perm 1f0000    32    -1 keyring   _uid_ses.32: empty
+       00000892 I--QU-     1 perm 1f0000     0     0 user      metal:copper: 0
+       00000893 I--Q-N     1  35s 1f0000     0     0 user      metal:silver: 0
+       00000894 I--Q--     1  10h 1f0000     0     0 user      metal:gold: 0
+
+     The flags are:
+
+       I       Instantiated
+       R       Revoked
+       D       Dead
+       Q       Contributes to user's quota
+       U       Under contruction by callback to userspace
+       N       Negative key
+
+     This file must be enabled at kernel configuration time as it allows anyone
+     to list the keys database.
+
+ (*) /proc/key-users
+
+     This file lists the tracking data for each user that has at least one key
+     on the system. Such data includes quota information and statistics:
+
+       [root@andromeda root]# cat /proc/key-users
+       0:     46 45/45 1/100 13/10000
+       29:     2 2/2 2/100 40/10000
+       32:     2 2/2 2/100 40/10000
+       38:     2 2/2 2/100 40/10000
+
+     The format of each line is
+       <UID>:                  User ID to which this applies
+       <usage>                 Structure refcount
+       <inst>/<keys>           Total number of keys and number instantiated
+       <keys>/<max>            Key count quota
+       <bytes>/<max>           Key size quota
+
+
+===============================
+USERSPACE SYSTEM CALL INTERFACE
+===============================
+
+Userspace can manipulate keys directly through three new syscalls: add_key,
+request_key and keyctl. The latter provides a number of functions for
+manipulating keys.
+
+When referring to a key directly, userspace programs should use the key's
+serial number (a positive 32-bit integer). However, there are some special
+values available for referring to special keys and keyrings that relate to the
+process making the call:
+
+       CONSTANT                        VALUE   KEY REFERENCED
+       ==============================  ======  ===========================
+       KEY_SPEC_THREAD_KEYRING         -1      thread-specific keyring
+       KEY_SPEC_PROCESS_KEYRING        -2      process-specific keyring
+       KEY_SPEC_SESSION_KEYRING        -3      session-specific keyring
+       KEY_SPEC_USER_KEYRING           -4      UID-specific keyring
+       KEY_SPEC_USER_SESSION_KEYRING   -5      UID-session keyring
+       KEY_SPEC_GROUP_KEYRING          -6      GID-specific keyring
+
+
+The main syscalls are:
+
+ (*) Create a new key of given type, description and payload and add it to the
+     nominated keyring:
+
+       key_serial_t add_key(const char *type, const char *desc,
+                            const void *payload, size_t plen,
+                            key_serial_t keyring);
+
+     If a key of the same type and description as that proposed already exists
+     in the keyring, this will try to update it with the given payload, or it
+     will return error EEXIST if that function is not supported by the key
+     type. The process must also have permission to write to the key to be
+     able to update it. The new key will have all user permissions granted and
+     no group or third party permissions.
+
+     Otherwise, this will attempt to create a new key of the specified type
+     and description, and to instantiate it with the supplied payload and
+     attach it to the keyring. In this case, an error will be generated if the
+     process does not have permission to write to the keyring.
+
+     The payload is optional, and the pointer can be NULL if not required by
+     the type. The payload is plen in size, and plen can be zero for an empty
+     payload.
+
+     A new keyring can be generated by setting type "keyring", the keyring
+     name as the description (or NULL) and setting the payload to NULL.
+
+     User defined keys can be created by specifying type "user". It is
+     recommended that a user defined key's description by prefixed with a type
+     ID and a colon, such as "krb5tgt:" for a Kerberos 5 ticket granting
+     ticket.
+
+     Any other type must have been registered with the kernel in advance by a
+     kernel service such as a filesystem.
+
+     The ID of the new or updated key is returned if successful.
+
+
+ (*) Search the process's keyrings for a key, potentially calling out to
+     userspace to create it.
+
+       key_serial_t request_key(const char *type, const char *description,
+                                const char *callout_info,
+                                key_serial_t dest_keyring);
+
+     This function searches all the process's keyrings in the order thread,
+     process, session for a matching key. This works very much like
+     KEYCTL_SEARCH, including the optional attachment of the discovered key to
+     a keyring.
+
+     If a key cannot be found, and if callout_info is not NULL, then
+     /sbin/request-key will be invoked in an attempt to obtain a key. The
+     callout_info string will be passed as an argument to the program.
+
+
+The keyctl syscall functions are:
+
+ (*) Map a special key ID to a real key ID for this process:
+
+       key_serial_t keyctl(KEYCTL_GET_KEYRING_ID, key_serial_t id,
+                           int create);
+
+     The special key specified by "id" is looked up (with the key being
+     created if necessary) and the ID of the key or keyring thus found is
+     returned if it exists.
+
+     If the key does not yet exist, the key will be created if "create" is
+     non-zero; and the error ENOKEY will be returned if "create" is zero.
+
+
+ (*) Replace the session keyring this process subscribes to with a new one:
+
+       key_serial_t keyctl(KEYCTL_JOIN_SESSION_KEYRING, const char *name);
+
+     If name is NULL, an anonymous keyring is created attached to the process
+     as its session keyring, displacing the old session keyring.
+
+     If name is not NULL, if a keyring of that name exists, the process
+     attempts to attach it as the session keyring, returning an error if that
+     is not permitted; otherwise a new keyring of that name is created and
+     attached as the session keyring.
+
+     To attach to a named keyring, the keyring must have search permission for
+     the process's ownership.
+
+     The ID of the new session keyring is returned if successful.
+
+
+ (*) Update the specified key:
+
+       long keyctl(KEYCTL_UPDATE, key_serial_t key, const void *payload,
+                   size_t plen);
+
+     This will try to update the specified key with the given payload, or it
+     will return error EOPNOTSUPP if that function is not supported by the key
+     type. The process must also have permission to write to the key to be
+     able to update it.
+
+     The payload is of length plen, and may be absent or empty as for
+     add_key().
+
+
+ (*) Revoke a key:
+
+       long keyctl(KEYCTL_REVOKE, key_serial_t key);
+
+     This makes a key unavailable for further operations. Further attempts to
+     use the key will be met with error EKEYREVOKED, and the key will no longer
+     be findable.
+
+
+ (*) Change the ownership of a key:
+
+       long keyctl(KEYCTL_CHOWN, key_serial_t key, uid_t uid, gid_t gid);
+
+     This function permits a key's owner and group ID to be changed. Either
+     one of uid or gid can be set to -1 to suppress that change.
+
+     Only the superuser can change a key's owner to something other than the
+     key's current owner. Similarly, only the superuser can change a key's
+     group ID to something other than the calling process's group ID or one of
+     its group list members.
+
+
+ (*) Change the permissions mask on a key:
+
+       long keyctl(KEYCTL_SETPERM, key_serial_t key, key_perm_t perm);
+
+     This function permits the owner of a key or the superuser to change the
+     permissions mask on a key.
+
+     Only bits the available bits are permitted; if any other bits are set,
+     error EINVAL will be returned.
+
+
+ (*) Describe a key:
+
+       long keyctl(KEYCTL_DESCRIBE, key_serial_t key, char *buffer,
+                   size_t buflen);
+
+     This function returns a summary of the key's attributes (but not its
+     payload data) as a string in the buffer provided.
+
+     Unless there's an error, it always returns the amount of data it could
+     produce, even if that's too big for the buffer, but it won't copy more
+     than requested to userspace. If the buffer pointer is NULL then no copy
+     will take place.
+
+     A process must have view permission on the key for this function to be
+     successful.
+
+     If successful, a string is placed in the buffer in the following format:
+
+       <type>;<uid>;<gid>;<perm>;<description>
+
+     Where type and description are strings, uid and gid are decimal, and perm
+     is hexadecimal. A NUL character is included at the end of the string if
+     the buffer is sufficiently big.
+
+     This can be parsed with
+
+       sscanf(buffer, "%[^;];%d;%d;%o;%s", type, &uid, &gid, &mode, desc);
+
+
+ (*) Clear out a keyring:
+
+       long keyctl(KEYCTL_CLEAR, key_serial_t keyring);
+
+     This function clears the list of keys attached to a keyring. The calling
+     process must have write permission on the keyring, and it must be a
+     keyring (or else error ENOTDIR will result).
+
+
+ (*) Link a key into a keyring:
+
+       long keyctl(KEYCTL_LINK, key_serial_t keyring, key_serial_t key);
+
+     This function creates a link from the keyring to the key. The process
+     must have write permission on the keyring and must have link permission
+     on the key.
+
+     Should the keyring not be a keyring, error ENOTDIR will result; and if
+     the keyring is full, error ENFILE will result.
+
+     The link procedure checks the nesting of the keyrings, returning ELOOP if
+     it appears to deep or EDEADLK if the link would introduce a cycle.
+
+
+ (*) Unlink a key or keyring from another keyring:
+
+       long keyctl(KEYCTL_UNLINK, key_serial_t keyring, key_serial_t key);
+
+     This function looks through the keyring for the first link to the
+     specified key, and removes it if found. Subsequent links to that key are
+     ignored. The process must have write permission on the keyring.
+
+     If the keyring is not a keyring, error ENOTDIR will result; and if the
+     key is not present, error ENOENT will be the result.
+
+
+ (*) Search a keyring tree for a key:
+
+       key_serial_t keyctl(KEYCTL_SEARCH, key_serial_t keyring,
+                           const char *type, const char *description,
+                           key_serial_t dest_keyring);
+
+     This searches the keyring tree headed by the specified keyring until a
+     key is found that matches the type and description criteria. Each keyring
+     is checked for keys before recursion into its children occurs.
+
+     The process must have search permission on the top level keyring, or else
+     error EACCES will result. Only keyrings that the process has search
+     permission on will be recursed into, and only keys and keyrings for which
+     a process has search permission can be matched. If the specified keyring
+     is not a keyring, ENOTDIR will result.
+
+     If the search succeeds, the function will attempt to link the found key
+     into the destination keyring if one is supplied (non-zero ID). All the
+     constraints applicable to KEYCTL_LINK apply in this case too.
+
+     Error ENOKEY, EKEYREVOKED or EKEYEXPIRED will be returned if the search
+     fails. On success, the resulting key ID will be returned.
+
+
+ (*) Read the payload data from a key:
+
+       key_serial_t keyctl(KEYCTL_READ, key_serial_t keyring, char *buffer,
+                           size_t buflen);
+
+     This function attempts to read the payload data from the specified key
+     into the buffer. The process must have read permission on the key to
+     succeed.
+
+     The returned data will be processed for presentation by the key type. For
+     instance, a keyring will return an array of key_serial_t entries
+     representing the IDs of all the keys to which it is subscribed. The user
+     defined key type will return its data as is. If a key type does not
+     implement this function, error EOPNOTSUPP will result.
+
+     As much of the data as can be fitted into the buffer will be copied to
+     userspace if the buffer pointer is not NULL.
+
+     On a successful return, the function will always return the amount of
+     data available rather than the amount copied.
+
+
+ (*) Instantiate a partially constructed key.
+
+       key_serial_t keyctl(KEYCTL_INSTANTIATE, key_serial_t key,
+                           const void *payload, size_t plen,
+                           key_serial_t keyring);
+
+     If the kernel calls back to userspace to complete the instantiation of a
+     key, userspace should use this call to supply data for the key before the
+     invoked process returns, or else the key will be marked negative
+     automatically.
+
+     The process must have write access on the key to be able to instantiate
+     it, and the key must be uninstantiated.
+
+     If a keyring is specified (non-zero), the key will also be linked into
+     that keyring, however all the constraints applying in KEYCTL_LINK apply
+     in this case too.
+
+     The payload and plen arguments describe the payload data as for add_key().
+
+
+ (*) Negatively instantiate a partially constructed key.
+
+       key_serial_t keyctl(KEYCTL_NEGATE, key_serial_t key,
+                           unsigned timeout, key_serial_t keyring);
+
+     If the kernel calls back to userspace to complete the instantiation of a
+     key, userspace should use this call mark the key as negative before the
+     invoked process returns if it is unable to fulfil the request.
+
+     The process must have write access on the key to be able to instantiate
+     it, and the key must be uninstantiated.
+
+     If a keyring is specified (non-zero), the key will also be linked into
+     that keyring, however all the constraints applying in KEYCTL_LINK apply
+     in this case too.
+
+
+===============
+KERNEL SERVICES
+===============
+
+The kernel services for key managment are fairly simple to deal with. They can
+be broken down into two areas: keys and key types.
+
+Dealing with keys is fairly straightforward. Firstly, the kernel service
+registers its type, then it searches for a key of that type. It should retain
+the key as long as it has need of it, and then it should release it. For a
+filesystem or device file, a search would probably be performed during the
+open call, and the key released upon close. How to deal with conflicting keys
+due to two different users opening the same file is left to the filesystem
+author to solve.
+
+When accessing a key's payload data, the key->lock should be at least read
+locked, or else the data may be changed by update during the access.
+
+(*) To search for a key, call:
+
+       struct key *request_key(const struct key_type *type,
+                               const char *description,
+                               const char *callout_string);
+
+    This is used to request a key or keyring with a description that matches
+    the description specified according to the key type's match function. This
+    permits approximate matching to occur. If callout_string is not NULL, then
+    /sbin/request-key will be invoked in an attempt to obtain the key from
+    userspace. In that case, callout_string will be passed as an argument to
+    the program.
+
+    Should the function fail error ENOKEY, EKEYEXPIRED or EKEYREVOKED will be
+    returned.
+
+
+(*) When it is no longer required, the key should be released using:
+
+       void key_put(struct key *key);
+
+    This can be called from interrupt context. If CONFIG_KEYS is not set then
+    the argument will not be parsed.
+
+
+(*) Extra references can be made to a key by calling the following function:
+
+       struct key *key_get(struct key *key);
+
+    These need to be disposed of by calling key_put() when they've been
+    finished with. The key pointer passed in will be returned. If the pointer
+    is NULL or CONFIG_KEYS is not set then the key will not be dereferenced and
+    no increment will take place.
+
+
+(*) A key's serial number can be obtained by calling:
+
+       key_serial_t key_serial(struct key *key);
+
+    If key is NULL or if CONFIG_KEYS is not set then 0 will be returned (in the
+    latter case without parsing the argument).
+
+
+(*) If a keyring was found in the search, this can be further searched by:
+
+       struct key *keyring_search(struct key *keyring,
+                                  const struct key_type *type,
+                                  const char *description)
+
+    This searches the keyring tree specified for a matching key. Error ENOKEY
+    is returned upon failure. If successful, the returned key will need to be
+    released.
+
+
+(*) To check the validity of a key, this function can be called:
+
+       int validate_key(struct key *key);
+
+    This checks that the key in question hasn't expired or and hasn't been
+    revoked. Should the key be invalid, error EKEYEXPIRED or EKEYREVOKED will
+    be returned. If the key is NULL or if CONFIG_KEYS is not set then 0 will be
+    returned (in the latter case without parsing the argument).
+
+
+(*) To register a key type, the following function should be called:
+
+       int register_key_type(struct key_type *type);
+
+    This will return error EEXIST if a type of the same name is already
+    present.
+
+
+(*) To unregister a key type, call:
+
+       void unregister_key_type(struct key_type *type);
+
+
+===================
+DEFINING A KEY TYPE
+===================
+
+A kernel service may want to define its own key type. For instance, an AFS
+filesystem might want to define a Kerberos 5 ticket key type. To do this, it
+author fills in a struct key_type and registers it with the system.
+
+The structure has a number of fields, some of which are mandatory:
+
+ (*) const char *name
+
+     The name of the key type. This is used to translate a key type name
+     supplied by userspace into a pointer to the structure.
+
+
+ (*) size_t def_datalen
+
+     This is optional - it supplies the default payload data length as
+     contributed to the quota. If the key type's payload is always or almost
+     always the same size, then this is a more efficient way to do things.
+
+     The data length (and quota) on a particular key can always be changed
+     during instantiation or update by calling:
+
+       int key_payload_reserve(struct key *key, size_t datalen);
+
+     With the revised data length. Error EDQUOT will be returned if this is
+     not viable.
+
+
+ (*) int (*instantiate)(struct key *key, const void *data, size_t datalen);
+
+     This method is called to attach a payload to a key during
+     construction. The payload attached need not bear any relation to the data
+     passed to this function.
+
+     If the amount of data attached to the key differs from the size in
+     keytype->def_datalen, then key_payload_reserve() should be called.
+
+
+ (*) int (*duplicate)(struct key *key, const struct key *source);
+
+     If this type of key can be duplicated, then this method should be
+     provided. It is called to copy the payload attached to the source into
+     the new key. The data length on the new key will have been updated and
+     the quota adjusted already.
+
+     The source key will be locked against change on the source->sem, so it is
+     safe to sleep here.
+
+
+ (*) int (*update)(struct key *key, const void *data, size_t datalen);
+
+     If this type of key can be updated, then this method should be
+     provided. It is called to update a key's payload from the blob of data
+     provided.
+
+     key_payload_reserve() should be called if the data length might change
+     before any changes are actually made. Note that if this succeeds, the
+     type is committed to changing the key because it's already been altered,
+     so all memory allocation must be done first.
+
+     The key will be locked against other changers on key->sem, so it is safe
+     to sleep here.
+
+     key_payload_reserve() should be called with the key->lock write locked,
+     and the changes to the key's attached payload should be made before the
+     key is locked.
+
+
+ (*) int (*match)(const struct key *key, const void *desc);
+
+     This method is called to match a key against a description. It should
+     return non-zero if the two match, zero if they don't.
+
+
+ (*) void (*destroy)(struct key *key);
+
+     This method is optional. It is called to discard the payload data on a
+     key when it is being destroyed.
+
+
+ (*) void (*describe)(const struct key *key, struct seq_file *p);
+
+     This method is optional. It is called during /proc/keys reading to
+     summarise a key in text form.
+
+
+ (*) long (*read)(const struct key *key, char __user *buffer, size_t buflen);
+
+     This method is optional. It is called by KEYCTL_READ to translate the
+     key's payload into something a blob of data for userspace to deal
+     with. Ideally, the blob should be in the same format as that passed in to
+     the instantiate and update methods.
+
+     If successful, the blob size that could be produced should be returned
+     rather than the size copied.
+
+
+============================
+REQUEST-KEY CALLBACK SERVICE
+============================
+
+To create a new key, the kernel will attempt to execute the following command
+line:
+
+       /sbin/request-key create <key> <uid> <gid> \
+               <threadring> <processring> <sessionring> <callout_info>
+
+<key> is the key being constructed, and the three keyrings are the process
+keyrings from the process that caused the search to be issued. These are
+included for two reasons:
+
+  (1) There may be an authentication token in one of the keyrings that is
+      required to obtain the key, eg: a Kerberos Ticket-Granting Ticket.
+
+  (2) The new key should probably be cached in one of these rings.
+
+This program should set it UID and GID to those specified before attempting to
+access any more keys. It may then look around for a user specific process to
+hand the request off to (perhaps a path held in placed in another key by, for
+example, the KDE desktop manager).
+
+The program (or whatever it calls) should finish construction of the key by
+calling KEYCTL_INSTANTIATE, which also permits it to cache the key in one of
+the keyrings (probably the session ring) before returning. Alternatively, the
+key can be marked as negative with KEYCTL_NEGATE; this also permits the key to
+be cached in one of the keyrings.
+
+If it returns with the key remaining in the unconstructed state, the key will
+be marked as being negative, it will be added to the session keyring, and an
+error will be returned to the key requestor.
+
+Supplementary information may be provided from whoever or whatever invoked
+this service. This will be passed as the <callout_info> parameter. If no such
+information was made available, then "-" will be passed as this parameter
+instead.
+
+
+Similarly, the kernel may attempt to update an expired or a soon to expire key
+by executing:
+
+       /sbin/request-key update <key> <uid> <gid> \
+               <threadring> <processring> <sessionring>
+
+In this case, the program isn't required to actually attach the key to a ring;
+the rings are provided for reference.
diff --git a/Documentation/networking/README.ipw2100 b/Documentation/networking/README.ipw2100
new file mode 100644 (file)
index 0000000..93bba8b
--- /dev/null
@@ -0,0 +1,179 @@
+
+Intel PRO/Wireless 2100 802.11b Driver for Linux
+README.ipw2100
+
+October 13, 2004
+
+
+Release 0.56 Current Features
+------------ -----   -----       ----       ---       --         -     
+
+- IBSS and BSS modes
+- 802.11 fragmentation
+- WEP (shared key and open)
+- wireless extension support 
+- 802.1x EAP via xsupplicant
+- Monitor/RFMon mode
+- transmit power control
+- long/short preamble support
+- power states support (ACPI)
+
+TODO
+------------ -----   -----       ----       ---       --         -     
+- Fix bugs...  The biggies:
+  C3 corruption
+  Fragmentation
+
+
+Command Line Parameters
+------------ -----   -----       ----       ---       --         -     
+
+If the driver is built as a module, the following optional parameters are used
+by entering them on the command line with the modprobe command using this
+syntax:
+
+       modprobe ipw2100 [<option>=<VAL1><,VAL2>...]
+
+For example, to set the interface name for driver, entering:
+
+       modprobe ipw2100 if_name=wlan%d
+
+results in the ipw2100 driver defaulting to the wlan prefix, with the system
+assigning a unique number in place of %d.  The default interface name is eth%d.
+
+The ipw2100 driver supports the following module parameters:
+
+Name           Value           Example:
+debug          0x0-0xffffffff  debug=1024
+if_name                string          if_name=wlan%d
+mode           0,1,2           mode=1   /* AdHoc */
+channel                int             channel=3 /* Only valid in AdHoc or Monitor */
+associate      boolean         associate=0 /* Do NOT auto associate */
+disable                boolean         disable=1 /* Do not power the HW */
+
+
+Radio Kill Switch
+------------ -----   -----       ----       ---       --         -
+Most laptops provide the ability for the user to physically disable the radio.
+Some vendors have implemented this as a physical switch that requires no
+software to turn the radio off and on.  On other laptops, however, the switch
+is controlled through a button being pressed and a software driver then making
+calls to turn the radio off and on.  This is referred to as a "software based
+RF kill switch"
+
+To determine if you have such a switch, you can check the contents of:
+
+       /sys/bus/pci/drivers/ipw2100/*/rf_kill
+
+A value of:
+       
+       Radio is {en,dis}abled by RF switch
+
+means that you have an RF switch and the radio is in the state 
+described.
+
+A value of:
+
+       Your hardware does not have an RF switch
+
+is self explanatory.  In this case you should not need to worry about 
+enabling the radio.
+
+
+Dynamic Firmware
+------------ -----   -----       ----       ---       --         -     
+As the firmware is licensed under a restricted use license, it can not be 
+included within the kernel sources.  To enable the IPW2100 you will need a 
+firmware image to load into the wireless NIC's processors.
+
+You can obtain these images from <http://ipw2100.sf.net/firmware.php>.
+
+See INSTALL for instructions on installing the firmware.
+
+
+Power Management
+------------ -----   -----       ----       ---       --         -     
+The IPW2100 supports the configuration of the Power Save Protocol 
+through a private wireless extension interface.  The IPW2100 supports 
+the following different modes:
+
+       off     No power management.  Radio is always on.
+       on      Automatic power management
+       1-5     Different levels of power management.  The higher the 
+               number the greater the power savings, but with an impact to 
+               packet latencies. 
+
+Power management works by powering down the radio after a certain 
+interval of time has passed where no packets are passed through the 
+radio.  Once powered down, the radio remains in that state for a given 
+period of time.  For higher power savings, the interval between last 
+packet processed to sleep is shorter and the sleep period is longer.
+
+When the radio is asleep, the access point sending data to the station 
+must buffer packets at the AP until the station wakes up and requests 
+any buffered packets.  If you have an AP that does not correctly support 
+the PSP protocol you may experience packet loss or very poor performance 
+while power management is enabled.  If this is the case, you will need 
+to try and find a firmware update for your AP, or disable power 
+management (via `iwconfig eth1 power off`)
+
+To configure the power level on the IPW2100 you use a combination of 
+iwconfig and iwpriv.  iwconfig is used to turn power management on, off, 
+and set it to auto.
+
+       iwconfig eth1 power off    Disables radio power down
+       iwconfig eth1 power on     Enables radio power management to 
+                                  last set level (defaults to AUTO)
+       iwpriv eth1 set_power 0    Sets power level to AUTO and enables 
+                                  power management if not previously 
+                                  enabled.
+       iwpriv eth1 set_power 1-5  Set the power level as specified, 
+                                  enabling power management if not 
+                                  previously enabled.
+
+You can view the current power level setting via:
+       
+       iwpriv eth1 get_power
+
+It will return the current period or timeout that is configured as a string
+in the form of xxxx/yyyy (z) where xxxx is the timeout interval (amount of
+time after packet processing), yyyy is the period to sleep (amount of time to 
+wait before powering the radio and querying the access point for buffered
+packets), and z is the 'power level'.  If power management is turned off the
+xxxx/yyyy will be replaced with 'off' -- the level reported will be the active
+level if `iwconfig eth1 power on` is invoked.
+
+
+Support
+------------ -----   -----       ----       ---       --         -     
+
+For general information and support, go to:
+       
+    http://ipw2100.sf.net/
+
+License
+------------ -----   -----       ----       ---       --         -     
+
+  Copyright(c) 2003 - 2004 Intel Corporation. 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.  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.
+  
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+  
+  Contact Information:
+  James P. Ketrenos <ipw2100-admin@linux.intel.com>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
diff --git a/Documentation/networking/README.ipw2200 b/Documentation/networking/README.ipw2200
new file mode 100644 (file)
index 0000000..42690b6
--- /dev/null
@@ -0,0 +1,194 @@
+
+Intel PRO/Wireless 2200 802.11bg Driver for Linux
+README.ipw2200
+
+October 13, 2004
+
+Release 0.12 Current Features
+------------ -----   -----       ----       ---       --         -     
+- BSS mode (Infrastructure, Managed)
+- IBSS mode (Ad-Hoc)
+- WEP (OPEN and SHARED KEY mode)
+- 802.1x EAP via xsupplicant
+- Wireless Extension support 
+- long/short preamble support
+- Full B and G rate support (2200 and 2915)
+- Full A rate support (2915 only)
+- Transmit power control
+- S state support (ACPI suspend/resume)
+
+TODO
+------------ -----   -----       ----       ---       --         -     
+- Fix statistics returned by iwconfig and /proc/net/wireless
+- Add firmware restart backoff algorithm (see ipw2100 project)
+- Look into (and hopefully enable) Monitor/RFMon mode
+- Add WPA support
+
+
+Command Line Parameters
+------------ -----   -----       ----       ---       --         -     
+  associate
+       Set to 0 to disable the auto scan-and-associate functionality of the
+       driver.  Default is 1 (auto-associate)
+
+  auto_create
+       Set to 0 to disable the auto creation of an Ad-Hoc network 
+       matching the channel and network name parameters provided.  
+       Default is 1.
+
+  channel
+       channel number for association.  The normal method for setting
+        the channel would be to use the standard wireless tools
+        (i.e. `iwconfig eth1 channel 10`), but it is useful sometimes
+       to set this while debugging.  Channel 0 means 'ANY'
+
+  debug
+       If using a debug build, this is used to control the amount of debug
+       info is logged.  See the 'dval' and 'load' script for more info on
+       how to use this.
+
+  ifname
+       Can be used to override the default interface name of eth%.  For 
+       example: 
+
+               modprobe ipw2200 ifname=wlan%d
+
+       You can also specify a specific interface number -- be warned 
+       that if that number conflicts with an already assigned interface
+       the driver will not load correctly.
+
+  mode
+       Can be used to set the default mode of the adapter.  
+       0 = Managed, 1 = Ad-Hoc
+
+Wireless Extension Private Methods
+------------ -----   -----       ----       ---       --         -     
+  get_mode
+       Can be used to report out which IEEE mode the driver is 
+       configured to support.  Example:
+       
+       % iwpriv eth1 get_mode
+       eth1    get_mode:802.11bg (6)
+
+  set_mode
+       Can be used to configure which IEEE mode the driver will 
+       support.  
+
+       Usage:
+       % iwpriv eth1 set_mode {mode}
+       Where {mode} is a number in the range 1-7:
+       1       802.11a (2915 only)
+       2       802.11b
+       3       802.11ab (2915 only)
+       4       802.11g 
+       5       802.11ag (2915 only)
+       6       802.11bg
+       7       802.11abg (2915 only)
+
+
+Sysfs Helper Files: (NOTE: All of these are only useful for developers)
+------------ -----   -----       ----       ---       --         -     
+
+----- Driver Level ------
+For the driver level files, look in /sys/bus/pci/drivers/ipw2200/
+
+  debug_level  
+       
+       This controls the same global as the 'debug' module parameter
+
+----- Device Level ------
+For the device level files, look in
+       
+       /sys/bus/pci/drivers/ipw2200/{PCI-ID}/
+
+For example:
+       /sys/bus/pci/drivers/ipw2200/0000:02:01.0
+
+For the device level files, see /sys/bus/pci/[drivers/ipw2200:
+
+  command_event_reg 
+       read access to the the Command Event register
+
+  eeprom 
+       reading from this fill will cause our private copy of the 
+       contents of the EEPROM to be flushed to the log
+
+  eeprom_sram 
+       reading this file will behave like the 'eeprom' file, except
+       that instead of pulling from the device's cached copy of the
+       eeprom data, the region of the device's sram that should
+       hold eeprom data is dumped.
+
+  eeprom_clear 
+       reading from this file will cause the eeprom info in sram to be 
+       cleared.
+
+  error_log 
+       reading this file will cause the contents of the device's error 
+       log to be flushed to our log.  normally the event_log is empty, 
+       but if the device's fw get's into an odd state, this log contains 
+       some hints.
+
+  fw_date 
+       read-only access to the firmware release date
+
+  fw_version 
+       read-only access to the firmware release version
+
+  rf_kill
+       read - 
+       0 = RF kill not enabled (radio on)
+       1 = HW based RF kill active (radio off)
+       2 = SW based RF kill active (radio off)
+       write -
+       0 = If SW based RF kill active, turn the radio back on
+       1 = If radio is on, activate SW based RF kill
+
+       NOTE: If you enable the SW based RF kill and then toggle the HW
+       based RF kill from ON -> OFF -> ON, the radio will come back on
+       (resetting the SW based RF kill to the 'radio on' state)
+       
+  ucode 
+       read-only access to the ucode version number
+
+  rtc 
+       read-only access the the device's real-time clock
+
+  [in]direct_byte
+  [in]direct_word
+       enables read-only access to the device's sram by first writing
+       the address of the data to read, and then reading from the file 
+       will return the word/byte the address points to.
+
+Support
+------------ -----   -----       ----       ---       --         -     
+
+For general information and support, go to:
+       
+    http://ipw2200.sf.net/
+
+License
+------------ -----   -----       ----       ---       --         -     
+
+  Copyright(c) 2003 - 2004 Intel Corporation. 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.
+  
+  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.
+  
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+  
+  Contact Information:
+  James P. Ketrenos <ipw2100-admin@linux.intel.com>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
diff --git a/Documentation/networking/proc_net_tcp.txt b/Documentation/networking/proc_net_tcp.txt
new file mode 100644 (file)
index 0000000..59cb915
--- /dev/null
@@ -0,0 +1,47 @@
+This document describes the interfaces /proc/net/tcp and /proc/net/tcp6.
+
+These /proc interfaces provide information about currently active TCP 
+connections, and are implemented by tcp_get_info() in net/ipv4/tcp_ipv4.c and
+tcp6_get_info() in net/ipv6/tcp_ipv6.c, respectively.
+
+It will first list all listening TCP sockets, and next list all established
+TCP connections. A typical entry of /proc/net/tcp would look like this (split 
+up into 3 parts because of the length of the line):
+
+   46: 010310AC:9C4C 030310AC:1770 01 
+   |      |      |      |      |   |--> connection state
+   |      |      |      |      |------> remote TCP port number
+   |      |      |      |-------------> remote IPv4 address
+   |      |      |--------------------> local TCP port number
+   |      |---------------------------> local IPv4 address
+   |----------------------------------> number of entry
+
+   00000150:00000000 01:00000019 00000000  
+      |        |     |     |       |--> number of unrecovered RTO timeouts
+      |        |     |     |----------> number of jiffies until timer expires
+      |        |     |----------------> timer_active (see below)
+      |        |----------------------> receive-queue
+      |-------------------------------> transmit-queue
+
+   1000        0 54165785 4 cd1e6040 25 4 27 3 -1
+    |          |    |     |    |     |  | |  | |--> slow start size threshold, 
+    |          |    |     |    |     |  | |  |      or -1 if the treshold
+    |          |    |     |    |     |  | |  |      is >= 0xFFFF
+    |          |    |     |    |     |  | |  |----> sending congestion window
+    |          |    |     |    |     |  | |-------> (ack.quick<<1)|ack.pingpong
+    |          |    |     |    |     |  |---------> Predicted tick of soft clock
+    |          |    |     |    |     |              (delayed ACK control data)
+    |          |    |     |    |     |------------> retransmit timeout
+    |          |    |     |    |------------------> location of socket in memory
+    |          |    |     |-----------------------> socket reference count
+    |          |    |-----------------------------> inode
+    |          |----------------------------------> unanswered 0-window probes
+    |---------------------------------------------> uid
+
+timer_active:
+  0  no timer is pending
+  1  retransmit-timer is pending
+  2  another timer (e.g. delayed ack or keepalive) is pending
+  3  this is a socket in TIME_WAIT state. Not all fields will contain 
+     data (or even exist)
+  4  zero window probe timer is pending
diff --git a/Documentation/nommu-mmap.txt b/Documentation/nommu-mmap.txt
new file mode 100644 (file)
index 0000000..fcf1c08
--- /dev/null
@@ -0,0 +1,141 @@
+                        =============================
+                        NO-MMU MEMORY MAPPING SUPPORT
+                        =============================
+
+The kernel has limited support for memory mapping under no-MMU conditions, such
+as are used in uClinux environments. From the userspace point of view, memory
+mapping is made use of in conjunction with the mmap() system call, the shmat()
+call and the execve() system call. From the kernel's point of view, execve()
+mapping is actually performed by the binfmt drivers, which call back into the
+mmap() routines to do the actual work.
+
+Memory mapping behaviour also involves the way fork(), vfork(), clone() and
+ptrace() work. Under uClinux there is no fork(), and clone() must be supplied
+the CLONE_VM flag.
+
+The behaviour is similar between the MMU and no-MMU cases, but not identical;
+and it's also much more restricted in the latter case:
+
+ (*) Anonymous mapping, MAP_PRIVATE
+
+       In the MMU case: VM regions backed by arbitrary pages; copy-on-write
+       across fork.
+
+       In the no-MMU case: VM regions backed by arbitrary contiguous runs of
+       pages.
+
+ (*) Anonymous mapping, MAP_SHARED
+
+       These behave very much like private mappings, except that they're
+       shared across fork() or clone() without CLONE_VM in the MMU case. Since
+       the no-MMU case doesn't support these, behaviour is identical to
+       MAP_PRIVATE there.
+
+ (*) File, MAP_PRIVATE, PROT_READ / PROT_EXEC, !PROT_WRITE
+
+       In the MMU case: VM regions backed by pages read from file; changes to
+       the underlying file are reflected in the mapping; copied across fork.
+
+       In the no-MMU case: VM regions backed by arbitrary contiguous runs of
+       pages into which the appropriate bit of the file is read; any remaining
+       bit of the mapping is cleared; such mappings are shared if possible;
+       writes to the file do not affect the mapping; writes to the mapping are
+       visible in other processes (no MMU protection), but should not happen.
+
+ (*) File, MAP_PRIVATE, PROT_READ / PROT_EXEC, PROT_WRITE
+
+       In the MMU case: like the non-PROT_WRITE case, except that the pages in
+       question get copied before the write actually happens. From that point
+       on writes to that page in the file no longer get reflected into the
+       mapping's backing pages.
+
+       In the no-MMU case: works exactly as for the non-PROT_WRITE case.
+
+ (*) Regular file / blockdev, MAP_SHARED, PROT_READ / PROT_EXEC / PROT_WRITE
+
+       In the MMU case: VM regions backed by pages read from file; changes to
+       pages written back to file; writes to file reflected into pages backing
+       mapping; shared across fork.
+
+       In the no-MMU case: not supported.
+
+ (*) Memory backed regular file, MAP_SHARED, PROT_READ / PROT_EXEC / PROT_WRITE
+
+       In the MMU case: As for ordinary regular files.
+
+       In the no-MMU case: The filesystem providing the memory-backed file
+       (such as ramfs or tmpfs) may choose to honour an open, truncate, mmap
+       sequence by providing a contiguous sequence of pages to map. In that
+       case, a shared-writable memory mapping will be possible. It will work
+       as for the MMU case. If the filesystem does not provide any such
+       support, then the mapping request will be denied.
+
+ (*) Memory backed chardev, MAP_SHARED, PROT_READ / PROT_EXEC / PROT_WRITE
+
+       In the MMU case: As for ordinary regular files.
+
+       In the no-MMU case: The character device driver may choose to honour
+       the mmap() by providing direct access to the underlying device if it
+       provides memory or quasi-memory that can be accessed directly. Examples
+       of such are frame buffers and flash devices. If the driver does not
+       provide any such support, then the mapping request will be denied.
+
+
+============================
+FURTHER NOTES ON NO-MMU MMAP
+============================
+
+ (*) A request for a private mapping of less than a page in size may not return
+     a page-aligned buffer. This is because the kernel calls kmalloc() to
+     allocate the buffer, not get_free_page().
+
+ (*) A list of all the mappings on the system is visible through /proc/maps in
+     no-MMU mode.
+
+ (*) Supplying MAP_FIXED or a requesting a particular mapping address will
+     result in an error.
+
+ (*) Files mapped privately must have a read method provided by the driver or
+     filesystem so that the contents can be read into the memory allocated. An
+     error will result if they don't. This is most likely to be encountered
+     with character device files, pipes, fifos and sockets.
+
+
+============================================
+PROVIDING SHAREABLE CHARACTER DEVICE SUPPORT
+============================================
+
+To provide shareable character device support, a driver must provide a
+file->f_op->get_unmapped_area() operation. The mmap() routines will call this
+to get a proposed address for the mapping. This may return an error if it
+doesn't wish to honour the mapping because it's too long, at a weird offset,
+under some unsupported combination of flags or whatever.
+
+The vm_ops->close() routine will be invoked when the last mapping on a chardev
+is removed. An existing mapping will be shared, partially or not, if possible
+without notifying the driver.
+
+It is permitted also for the file->f_op->get_unmapped_area() operation to
+return -ENOSYS. This will be taken to mean that this operation just doesn't
+want to handle it, despite the fact it's got an operation. For instance, it
+might try directing the call to a secondary driver which turns out not to
+implement it. Such is the case for the framebuffer driver which attempts to
+direct the call to the device-specific driver.
+
+
+==============================================
+PROVIDING SHAREABLE MEMORY-BACKED FILE SUPPORT
+==============================================
+
+Provision of shared mappings on memory backed files is similar to the provision
+of support for shared mapped character devices. The main difference is that the
+filesystem providing the service will probably allocate a contiguous collection
+of pages and permit mappings to be made on that.
+
+It is recommended that a truncate operation applied to such a file that
+increases the file size, if that file is empty, be taken as a request to gather
+enough pages to honour a mapping. This is required to support POSIX shared
+memory.
+
+Memory backed devices are indicated by the mapping's backing device info having
+the memory_backed flag set.
diff --git a/Documentation/power/kernel_threads.txt b/Documentation/power/kernel_threads.txt
new file mode 100644 (file)
index 0000000..60b5481
--- /dev/null
@@ -0,0 +1,41 @@
+KERNEL THREADS
+
+
+Freezer
+
+Upon entering a suspended state the system will freeze all
+tasks. This is done by delivering pseudosignals. This affects
+kernel threads, too. To successfully freeze a kernel thread
+the thread has to check for the pseudosignal and enter the
+refrigerator. Code to do this looks like this:
+
+       do {
+               hub_events();
+               wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list));
+               if (current->flags & PF_FREEZE)
+                       refrigerator(PF_FREEZE);
+       } while (!signal_pending(current));
+
+from drivers/usb/core/hub.c::hub_thread()
+
+
+The Unfreezable
+
+Some kernel threads however, must not be frozen. The kernel must
+be able to finish pending IO operations and later on be able to
+write the memory image to disk. Kernel threads needed to do IO
+must stay awake. Such threads must mark themselves unfreezable
+like this:
+
+       /*
+        * This thread doesn't need any user-level access,
+        * so get rid of all our resources.
+        */
+       daemonize("usb-storage");
+
+       current->flags |= PF_NOFREEZE;
+
+from drivers/usb/storage/usb.c::usb_stor_control_thread()
+
+Such drivers are themselves responsible for staying quiet during
+the actual snapshotting.
diff --git a/Documentation/power/video_extension.txt b/Documentation/power/video_extension.txt
new file mode 100644 (file)
index 0000000..8e33d7c
--- /dev/null
@@ -0,0 +1,34 @@
+This driver implement the ACPI Extensions For Display Adapters
+for integrated graphics devices on motherboard, as specified in
+ACPI 2.0 Specification, Appendix B, allowing to perform some basic
+control like defining the video POST device, retrieving EDID information
+or to setup a video output, etc.  Note that this is an ref. implementation only.
+It may or may not work for your integrated video device.
+
+Interfaces exposed to userland through /proc/acpi/video:
+
+VGA/info : display the supported video bus device capability like ,Video ROM, CRT/LCD/TV.
+VGA/ROM :  Used to get a copy of the display devices' ROM data (up to 4k).
+VGA/POST_info : Used to determine what options are implemented.
+VGA/POST : Used to get/set POST device.
+VGA/DOS : Used to get/set ownership of output switching:
+       Please refer ACPI spec B.4.1 _DOS
+VGA/CRT : CRT output
+VGA/LCD : LCD output
+VGA/TV : TV output 
+VGA/*/brightness : Used to get/set brightness of output device
+
+Notify event through /proc/acpi/event:
+
+#define ACPI_VIDEO_NOTIFY_SWITCH        0x80
+#define ACPI_VIDEO_NOTIFY_PROBE         0x81
+#define ACPI_VIDEO_NOTIFY_CYCLE         0x82
+#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT   0x83
+#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT   0x84
+
+#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS      0x82
+#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS        0x83
+#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS        0x84
+#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS       0x85
+#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF           0x86
+
diff --git a/Documentation/powerpc/cpu_features.txt b/Documentation/powerpc/cpu_features.txt
new file mode 100644 (file)
index 0000000..4727398
--- /dev/null
@@ -0,0 +1,56 @@
+Hollis Blanchard <hollis@austin.ibm.com>
+5 Jun 2002
+
+This document describes the system (including self-modifying code) used in the
+PPC Linux kernel to support a variety of PowerPC CPUs without requiring
+compile-time selection.
+
+Early in the boot process the ppc32 kernel detects the current CPU type and
+chooses a set of features accordingly. Some examples include Altivec support,
+split instruction and data caches, and if the CPU supports the DOZE and NAP
+sleep modes.
+
+Detection of the feature set is simple. A list of processors can be found in
+arch/ppc/kernel/cputable.c. The PVR register is masked and compared with each
+value in the list. If a match is found, the cpu_features of cur_cpu_spec is
+assigned to the feature bitmask for this processor and a __setup_cpu function
+is called.
+
+C code may test 'cur_cpu_spec[smp_processor_id()]->cpu_features' for a
+particular feature bit. This is done in quite a few places, for example
+in ppc_setup_l2cr().
+
+Implementing cpufeatures in assembly is a little more involved. There are
+several paths that are performance-critical and would suffer if an array
+index, structure dereference, and conditional branch were added. To avoid the
+performance penalty but still allow for runtime (rather than compile-time) CPU
+selection, unused code is replaced by 'nop' instructions. This nop'ing is
+based on CPU 0's capabilities, so a multi-processor system with non-identical
+processors will not work (but such a system would likely have other problems
+anyways).
+
+After detecting the processor type, the kernel patches out sections of code
+that shouldn't be used by writing nop's over it. Using cpufeatures requires
+just 2 macros (found in include/asm-ppc/cputable.h), as seen in head.S
+transfer_to_handler:
+
+       #ifdef CONFIG_ALTIVEC
+       BEGIN_FTR_SECTION
+               mfspr   r22,SPRN_VRSAVE         /* if G4, save vrsave register value */
+               stw     r22,THREAD_VRSAVE(r23)
+       END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
+       #endif /* CONFIG_ALTIVEC */
+
+If CPU 0 supports Altivec, the code is left untouched. If it doesn't, both
+instructions are replaced with nop's.
+
+The END_FTR_SECTION macro has two simpler variations: END_FTR_SECTION_IFSET
+and END_FTR_SECTION_IFCLR. These simply test if a flag is set (in
+cur_cpu_spec[0]->cpu_features) or is cleared, respectively. These two macros
+should be used in the majority of cases.
+
+The END_FTR_SECTION macros are implemented by storing information about this
+code in the '__ftr_fixup' ELF section. When do_cpu_ftr_fixups
+(arch/ppc/kernel/misc.S) is invoked, it will iterate over the records in
+__ftr_fixup, and if the required feature is not present it will loop writing
+nop's from each BEGIN_FTR_SECTION to END_FTR_SECTION.
diff --git a/Documentation/powerpc/eeh-pci-error-recovery.txt b/Documentation/powerpc/eeh-pci-error-recovery.txt
new file mode 100644 (file)
index 0000000..2bfe71b
--- /dev/null
@@ -0,0 +1,332 @@
+
+
+                      PCI Bus EEH Error Recovery
+                      --------------------------
+                           Linas Vepstas
+                       <linas@austin.ibm.com>
+                          12 January 2005
+
+
+Overview:
+---------
+The IBM POWER-based pSeries and iSeries computers include PCI bus
+controller chips that have extended capabilities for detecting and
+reporting a large variety of PCI bus error conditions.  These features
+go under the name of "EEH", for "Extended Error Handling".  The EEH
+hardware features allow PCI bus errors to be cleared and a PCI
+card to be "rebooted", without also having to reboot the operating
+system.
+
+This is in contrast to traditional PCI error handling, where the
+PCI chip is wired directly to the CPU, and an error would cause
+a CPU machine-check/check-stop condition, halting the CPU entirely.
+Another "traditional" technique is to ignore such errors, which
+can lead to data corruption, both of user data or of kernel data,
+hung/unresponsive adapters, or system crashes/lockups.  Thus,
+the idea behind EEH is that the operating system can become more
+reliable and robust by protecting it from PCI errors, and giving
+the OS the ability to "reboot"/recover individual PCI devices.
+
+Future systems from other vendors, based on the PCI-E specification,
+may contain similar features.
+
+
+Causes of EEH Errors
+--------------------
+EEH was originally designed to guard against hardware failure, such
+as PCI cards dying from heat, humidity, dust, vibration and bad
+electrical connections. The vast majority of EEH errors seen in
+"real life" are due to eithr poorly seated PCI cards, or,
+unfortunately quite commonly, due device driver bugs, device firmware
+bugs, and sometimes PCI card hardware bugs.
+
+The most common software bug, is one that causes the device to
+attempt to DMA to a location in system memory that has not been
+reserved for DMA access for that card.  This is a powerful feature,
+as it prevents what; otherwise, would have been silent memory
+corruption caused by the bad DMA.  A number of device driver
+bugs have been found and fixed in this way over the past few
+years.  Other possible causes of EEH errors include data or
+address line parity errors (for example, due to poor electrical
+connectivity due to a poorly seated card), and PCI-X split-completion
+errors (due to software, device firmware, or device PCI hardware bugs).
+The vast majority of "true hardware failures" can be cured by
+physically removing and re-seating the PCI card.
+
+
+Detection and Recovery
+----------------------
+In the following discussion, a generic overview of how to detect
+and recover from EEH errors will be presented. This is followed
+by an overview of how the current implementation in the Linux
+kernel does it.  The actual implementation is subject to change,
+and some of the finer points are still being debated.  These
+may in turn be swayed if or when other architectures implement
+similar functionality.
+
+When a PCI Host Bridge (PHB, the bus controller connecting the
+PCI bus to the system CPU electronics complex) detects a PCI error
+condition, it will "isolate" the affected PCI card.  Isolation
+will block all writes (either to the card from the system, or
+from the card to the system), and it will cause all reads to
+return all-ff's (0xff, 0xffff, 0xffffffff for 8/16/32-bit reads).
+This value was chosen because it is the same value you would
+get if the device was physically unplugged from the slot.
+This includes access to PCI memory, I/O space, and PCI config
+space.  Interrupts; however, will continued to be delivered.
+
+Detection and recovery are performed with the aid of ppc64
+firmware.  The programming interfaces in the Linux kernel
+into the firmware are referred to as RTAS (Run-Time Abstraction
+Services).  The Linux kernel does not (should not) access
+the EEH function in the PCI chipsets directly, primarily because
+there are a number of different chipsets out there, each with
+different interfaces and quirks. The firmware provides a
+uniform abstraction layer that will work with all pSeries
+and iSeries hardware (and be forwards-compatible).
+
+If the OS or device driver suspects that a PCI slot has been
+EEH-isolated, there is a firmware call it can make to determine if
+this is the case. If so, then the device driver should put itself
+into a consistent state (given that it won't be able to complete any
+pending work) and start recovery of the card.  Recovery normally
+would consist of reseting the PCI device (holding the PCI #RST
+line high for two seconds), followed by setting up the device
+config space (the base address registers (BAR's), latency timer,
+cache line size, interrupt line, and so on).  This is followed by a
+reinitialization of the device driver.  In a worst-case scenario,
+the power to the card can be toggled, at least on hot-plug-capable
+slots.  In principle, layers far above the device driver probably
+do not need to know that the PCI card has been "rebooted" in this
+way; ideally, there should be at most a pause in Ethernet/disk/USB
+I/O while the card is being reset.
+
+If the card cannot be recovered after three or four resets, the
+kernel/device driver should assume the worst-case scenario, that the
+card has died completely, and report this error to the sysadmin.
+In addition, error messages are reported through RTAS and also through
+syslogd (/var/log/messages) to alert the sysadmin of PCI resets.
+The correct way to deal with failed adapters is to use the standard
+PCI hotplug tools to remove and replace the dead card.
+
+
+Current PPC64 Linux EEH Implementation
+--------------------------------------
+At this time, a generic EEH recovery mechanism has been implemented,
+so that individual device drivers do not need to be modified to support
+EEH recovery.  This generic mechanism piggy-backs on the PCI hotplug
+infrastructure,  and percolates events up through the hotplug/udev
+infrastructure.  Followiing is a detailed description of how this is
+accomplished.
+
+EEH must be enabled in the PHB's very early during the boot process,
+and if a PCI slot is hot-plugged. The former is performed by
+eeh_init() in arch/ppc64/kernel/eeh.c, and the later by
+drivers/pci/hotplug/pSeries_pci.c calling in to the eeh.c code.
+EEH must be enabled before a PCI scan of the device can proceed.
+Current Power5 hardware will not work unless EEH is enabled;
+although older Power4 can run with it disabled.  Effectively,
+EEH can no longer be turned off.  PCI devices *must* be
+registered with the EEH code; the EEH code needs to know about
+the I/O address ranges of the PCI device in order to detect an
+error.  Given an arbitrary address, the routine
+pci_get_device_by_addr() will find the pci device associated
+with that address (if any).
+
+The default include/asm-ppc64/io.h macros readb(), inb(), insb(),
+etc. include a check to see if the the i/o read returned all-0xff's.
+If so, these make a call to eeh_dn_check_failure(), which in turn
+asks the firmware if the all-ff's value is the sign of a true EEH
+error.  If it is not, processing continues as normal.  The grand
+total number of these false alarms or "false positives" can be
+seen in /proc/ppc64/eeh (subject to change).  Normally, almost
+all of these occur during boot, when the PCI bus is scanned, where
+a large number of 0xff reads are part of the bus scan procedure.
+
+If a frozen slot is detected, code in arch/ppc64/kernel/eeh.c will
+print a stack trace to syslog (/var/log/messages).  This stack trace
+has proven to be very useful to device-driver authors for finding
+out at what point the EEH error was detected, as the error itself
+usually occurs slightly beforehand.
+
+Next, it uses the Linux kernel notifier chain/work queue mechanism to
+allow any interested parties to find out about the failure.  Device
+drivers, or other parts of the kernel, can use
+eeh_register_notifier(struct notifier_block *) to find out about EEH
+events.  The event will include a pointer to the pci device, the
+device node and some state info.  Receivers of the event can "do as
+they wish"; the default handler will be described further in this
+section.
+
+To assist in the recovery of the device, eeh.c exports the
+following functions:
+
+rtas_set_slot_reset() -- assert the  PCI #RST line for 1/8th of a second
+rtas_configure_bridge() -- ask firmware to configure any PCI bridges
+   located topologically under the pci slot.
+eeh_save_bars() and eeh_restore_bars(): save and restore the PCI
+   config-space info for a device and any devices under it.
+
+
+A handler for the EEH notifier_block events is implemented in
+drivers/pci/hotplug/pSeries_pci.c, called handle_eeh_events().
+It saves the device BAR's and then calls rpaphp_unconfig_pci_adapter().
+This last call causes the device driver for the card to be stopped,
+which causes hotplug events to go out to user space. This triggers
+user-space scripts that might issue commands such as "ifdown eth0"
+for ethernet cards, and so on.  This handler then sleeps for 5 seconds,
+hoping to give the user-space scripts enough time to complete.
+It then resets the PCI card, reconfigures the device BAR's, and
+any bridges underneath. It then calls rpaphp_enable_pci_slot(),
+which restarts the device driver and triggers more user-space
+events (for example, calling "ifup eth0" for ethernet cards).
+
+
+Device Shutdown and User-Space Events
+-------------------------------------
+This section documents what happens when a pci slot is unconfigured,
+focusing on how the device driver gets shut down, and on how the
+events get delivered to user-space scripts.
+
+Following is an example sequence of events that cause a device driver
+close function to be called during the first phase of an EEH reset.
+The following sequence is an example of the pcnet32 device driver.
+
+    rpa_php_unconfig_pci_adapter (struct slot *)  // in rpaphp_pci.c
+    {
+      calls
+      pci_remove_bus_device (struct pci_dev *) // in /drivers/pci/remove.c
+      {
+        calls
+        pci_destroy_dev (struct pci_dev *)
+        {
+          calls
+          device_unregister (&dev->dev) // in /drivers/base/core.c
+          {
+            calls
+            device_del (struct device *)
+            {
+              calls
+              bus_remove_device() // in /drivers/base/bus.c
+              {
+                calls
+                device_release_driver()
+                {
+                  calls
+                  struct device_driver->remove() which is just
+                  pci_device_remove()  // in /drivers/pci/pci_driver.c
+                  {
+                    calls
+                    struct pci_driver->remove() which is just
+                    pcnet32_remove_one() // in /drivers/net/pcnet32.c
+                    {
+                      calls
+                      unregister_netdev() // in /net/core/dev.c
+                      {
+                        calls
+                        dev_close()  // in /net/core/dev.c
+                        {
+                           calls dev->stop();
+                           which is just pcnet32_close() // in pcnet32.c
+                           {
+                             which does what you wanted
+                             to stop the device
+                           }
+                        }
+                     }
+                   which
+                   frees pcnet32 device driver memory
+                }
+     }}}}}}
+
+
+    in drivers/pci/pci_driver.c,
+    struct device_driver->remove() is just pci_device_remove()
+    which calls struct pci_driver->remove() which is pcnet32_remove_one()
+    which calls unregister_netdev()  (in net/core/dev.c)
+    which calls dev_close()  (in net/core/dev.c)
+    which calls dev->stop() which is pcnet32_close()
+    which then does the appropriate shutdown.
+
+---
+Following is the analogous stack trace for events sent to user-space
+when the pci device is unconfigured.
+
+rpa_php_unconfig_pci_adapter() {             // in rpaphp_pci.c
+  calls
+  pci_remove_bus_device (struct pci_dev *) { // in /drivers/pci/remove.c
+    calls
+    pci_destroy_dev (struct pci_dev *) {
+      calls
+      device_unregister (&dev->dev) {      // in /drivers/base/core.c
+        calls
+        device_del(struct device * dev) {  // in /drivers/base/core.c
+          calls
+          kobject_del() {                  //in /libs/kobject.c
+            calls
+            kobject_hotplug() {            // in /libs/kobject.c
+              calls
+              kset_hotplug() {             // in /lib/kobject.c
+                calls
+                kset->hotplug_ops->hotplug() which is really just
+                a call to
+                dev_hotplug() {           // in /drivers/base/core.c
+                  calls
+                  dev->bus->hotplug() which is really just a call to
+                  pci_hotplug () {      // in drivers/pci/hotplug.c
+                    which prints device name, etc....
+                 }
+               }
+               then kset_hotplug() calls
+                call_usermodehelper () with
+                   argv[0]=hotplug_path[] which is "/sbin/hotplug"
+             --> event to userspace,
+           }
+         }
+         kobject_del() then calls sysfs_remove_dir(), which would
+         trigger any user-space daemon that was watching /sysfs,
+         and notice the delete event.
+
+
+Pro's and Con's of the Current Design
+-------------------------------------
+There are several issues with the current EEH software recovery design,
+which may be addressed in future revisions.  But first, note that the
+big plus of the current design is that no changes need to be made to
+individual device drivers, so that the current design throws a wide net.
+The biggest negative of the design is that it potentially disturbs
+network daemons and file systems that didn't need to be disturbed.
+
+-- A minor complaint is that resetting the network card causes
+   user-space back-to-back ifdown/ifup burps that potentially disturb
+   network daemons, that didn't need to even know that the pci
+   card was being rebooted.
+
+-- A more serious concern is that the same reset, for SCSI devices,
+   causes havoc to mounted file systems.  Scripts cannot post-facto
+   unmount a file system without flushing pending buffers, but this
+   is impossible, because I/O has already been stopped.  Thus,
+   ideally, the reset should happen at or below the block layer,
+   so that the file systems are not disturbed.
+
+   Reiserfs does not tolerate errors returned from the block device.
+   Ext3fs seems to be tolerant, retrying reads/writes until it does
+   succeed. Both have been only lightly tested in this scenario.
+
+   The SCSI-generic subsystem already has built-in code for performing
+   SCSI device resets, SCSI bus resets, and SCSI host-bus-adapter
+   (HBA) resets.  These are cascaded into a chain of attempted
+   resets if a SCSI command fails. These are completely hidden
+   from the block layer.  It would be very natural to add an EEH
+   reset into this chain of events.
+
+-- If a SCSI error occurs for the root device, all is lost unless
+   the sysadmin had the foresight to run /bin, /sbin, /etc, /var
+   and so on, out of ramdisk/tmpfs.
+
+
+Conclusions
+-----------
+There's forward progress ...
+
+
diff --git a/Documentation/prio_tree.txt b/Documentation/prio_tree.txt
new file mode 100644 (file)
index 0000000..2fbb0c4
--- /dev/null
@@ -0,0 +1,107 @@
+The prio_tree.c code indexes vmas using 3 different indexes:
+       * heap_index  = vm_pgoff + vm_size_in_pages : end_vm_pgoff
+       * radix_index = vm_pgoff : start_vm_pgoff
+       * size_index = vm_size_in_pages
+
+A regular radix-priority-search-tree indexes vmas using only heap_index and
+radix_index. The conditions for indexing are:
+       * ->heap_index >= ->left->heap_index &&
+               ->heap_index >= ->right->heap_index
+       * if (->heap_index == ->left->heap_index)
+               then ->radix_index < ->left->radix_index;
+       * if (->heap_index == ->right->heap_index)
+               then ->radix_index < ->right->radix_index;
+       * nodes are hashed to left or right subtree using radix_index
+         similar to a pure binary radix tree.
+
+A regular radix-priority-search-tree helps to store and query
+intervals (vmas). However, a regular radix-priority-search-tree is only
+suitable for storing vmas with different radix indices (vm_pgoff).
+
+Therefore, the prio_tree.c extends the regular radix-priority-search-tree
+to handle many vmas with the same vm_pgoff. Such vmas are handled in
+2 different ways: 1) All vmas with the same radix _and_ heap indices are
+linked using vm_set.list, 2) if there are many vmas with the same radix
+index, but different heap indices and if the regular radix-priority-search
+tree cannot index them all, we build an overflow-sub-tree that indexes such
+vmas using heap and size indices instead of heap and radix indices. For
+example, in the figure below some vmas with vm_pgoff = 0 (zero) are
+indexed by regular radix-priority-search-tree whereas others are pushed
+into an overflow-subtree. Note that all vmas in an overflow-sub-tree have
+the same vm_pgoff (radix_index) and if necessary we build different
+overflow-sub-trees to handle each possible radix_index. For example,
+in figure we have 3 overflow-sub-trees corresponding to radix indices
+0, 2, and 4.
+
+In the final tree the first few (prio_tree_root->index_bits) levels
+are indexed using heap and radix indices whereas the overflow-sub-trees below
+those levels (i.e. levels prio_tree_root->index_bits + 1 and higher) are
+indexed using heap and size indices. In overflow-sub-trees the size_index
+is used for hashing the nodes to appropriate places.
+
+Now, an example prio_tree:
+
+  vmas are represented [radix_index, size_index, heap_index]
+                 i.e., [start_vm_pgoff, vm_size_in_pages, end_vm_pgoff]
+
+level  prio_tree_root->index_bits = 3
+-----
+                                                                                               _
+  0                                                    [0,7,7]                                  |
+                                                       /     \                                  |
+                                     ------------------       ------------                      |     Regular
+                                    /                                     \                     |  radix priority
+  1                            [1,6,7]                                   [4,3,7]                |   search tree
+                               /     \                                   /     \                |
+                        -------       -----                        ------       -----           |  heap-and-radix
+                       /                   \                      /                  \          |      indexed
+  2                [0,6,6]                [2,5,7]              [5,2,7]             [6,1,7]      |
+                   /     \                /     \              /     \             /     \      |
+  3            [0,5,5] [1,5,6]         [2,4,6] [3,4,7]     [4,2,6] [5,1,6]     [6,0,6] [7,0,7]  |
+                  /                       /                   /                                _
+                  /                      /                   /                                 _
+  4          [0,4,4]                 [2,3,5]              [4,1,5]                               |
+                /                       /                    /                                  |
+  5         [0,3,3]                 [2,2,4]              [4,0,4]                                |  Overflow-sub-trees
+               /                       /                                                        |
+  6        [0,2,2]                 [2,1,3]                                                      |    heap-and-size
+              /                       /                                                         |       indexed
+  7       [0,1,1]                 [2,0,2]                                                       |
+             /                                                                                  |
+  8      [0,0,0]                                                                                |
+                                                                                               _
+
+Note that we use prio_tree_root->index_bits to optimize the height
+of the heap-and-radix indexed tree. Since prio_tree_root->index_bits is
+set according to the maximum end_vm_pgoff mapped, we are sure that all
+bits (in vm_pgoff) above prio_tree_root->index_bits are 0 (zero). Therefore,
+we only use the first prio_tree_root->index_bits as radix_index.
+Whenever index_bits is increased in prio_tree_expand, we shuffle the tree
+to make sure that the first prio_tree_root->index_bits levels of the tree
+is indexed properly using heap and radix indices.
+
+We do not optimize the height of overflow-sub-trees using index_bits.
+The reason is: there can be many such overflow-sub-trees and all of
+them have to be suffled whenever the index_bits increases. This may involve
+walking the whole prio_tree in prio_tree_insert->prio_tree_expand code
+path which is not desirable. Hence, we do not optimize the height of the
+heap-and-size indexed overflow-sub-trees using prio_tree->index_bits.
+Instead the overflow sub-trees are indexed using full BITS_PER_LONG bits
+of size_index. This may lead to skewed sub-trees because most of the
+higher significant bits of the size_index are likely to be be 0 (zero). In
+the example above, all 3 overflow-sub-trees are skewed. This may marginally
+affect the performance. However, processes rarely map many vmas with the
+same start_vm_pgoff but different end_vm_pgoffs. Therefore, we normally
+do not require overflow-sub-trees to index all vmas.
+
+From the above discussion it is clear that the maximum height of
+a prio_tree can be prio_tree_root->index_bits + BITS_PER_LONG.
+However, in most of the common cases we do not need overflow-sub-trees,
+so the tree height in the common cases will be prio_tree_root->index_bits.
+
+It is fair to mention here that the prio_tree_root->index_bits
+is increased on demand, however, the index_bits is not decreased when
+vmas are removed from the prio_tree. That's tricky to do. Hence, it's
+left as a home work problem.
+
+
diff --git a/Documentation/s390/monreader.txt b/Documentation/s390/monreader.txt
new file mode 100644 (file)
index 0000000..d843bb0
--- /dev/null
@@ -0,0 +1,197 @@
+
+Date  : 2004-Nov-26
+Author: Gerald Schaefer (geraldsc@de.ibm.com)
+
+
+             Linux API for read access to z/VM Monitor Records
+             =================================================
+
+
+Description
+===========
+This item delivers a new Linux API in the form of a misc char device that is
+useable from user space and allows read access to the z/VM Monitor Records
+collected by the *MONITOR System Service of z/VM.
+
+
+User Requirements
+=================
+The z/VM guest on which you want to access this API needs to be configured in
+order to allow IUCV connections to the *MONITOR service, i.e. it needs the
+IUCV *MONITOR statement in its user entry. If the monitor DCSS to be used is
+restricted (likely), you also need the NAMESAVE <DCSS NAME> statement.
+This item will use the IUCV device driver to access the z/VM services, so you
+need a kernel with IUCV support. You also need z/VM version 4.4 or 5.1.
+
+There are two options for being able to load the monitor DCSS (examples assume
+that the monitor DCSS begins at 144 MB and ends at 152 MB). You can query the
+location of the monitor DCSS with the Class E privileged CP command Q NSS MAP
+(the values BEGPAG and ENDPAG are given in units of 4K pages).
+
+See also "CP Command and Utility Reference" (SC24-6081-00) for more information
+on the DEF STOR and Q NSS MAP commands, as well as "Saved Segments Planning
+and Administration" (SC24-6116-00) for more information on DCSSes.
+
+1st option:
+-----------
+You can use the CP command DEF STOR CONFIG to define a "memory hole" in your
+guest virtual storage around the address range of the DCSS.
+
+Example: DEF STOR CONFIG 0.140M 200M.200M
+
+This defines two blocks of storage, the first is 140MB in size an begins at
+address 0MB, the second is 200MB in size and begins at address 200MB,
+resulting in a total storage of 340MB. Note that the first block should
+always start at 0 and be at least 64MB in size.
+
+2nd option:
+-----------
+Your guest virtual storage has to end below the starting address of the DCSS
+and you have to specify the "mem=" kernel parameter in your parmfile with a
+value greater than the ending address of the DCSS.
+
+Example: DEF STOR 140M
+
+This defines 140MB storage size for your guest, the parameter "mem=160M" is
+added to the parmfile.
+
+
+User Interface
+==============
+The char device is implemented as a kernel module named "monreader",
+which can be loaded via the modprobe command, or it can be compiled into the
+kernel instead. There is one optional module (or kernel) parameter, "mondcss",
+to specify the name of the monitor DCSS. If the module is compiled into the
+kernel, the kernel parameter "monreader.mondcss=<DCSS NAME>" can be specified
+in the parmfile.
+
+The default name for the DCSS is "MONDCSS" if none is specified. In case that
+there are other users already connected to the *MONITOR service (e.g.
+Performance Toolkit), the monitor DCSS is already defined and you have to use
+the same DCSS. The CP command Q MONITOR (Class E privileged) shows the name
+of the monitor DCSS, if already defined, and the users connected to the
+*MONITOR service.
+Refer to the "z/VM Performance" book (SC24-6109-00) on how to create a monitor
+DCSS if your z/VM doesn't have one already, you need Class E privileges to
+define and save a DCSS.
+
+Example:
+--------
+modprobe monreader mondcss=MYDCSS
+
+This loads the module and sets the DCSS name to "MYDCSS".
+
+NOTE:
+-----
+This API provides no interface to control the *MONITOR service, e.g. specifiy
+which data should be collected. This can be done by the CP command MONITOR
+(Class E privileged), see "CP Command and Utility Reference".
+
+Device nodes with udev:
+-----------------------
+After loading the module, a char device will be created along with the device
+node /<udev directory>/monreader.
+
+Device nodes without udev:
+--------------------------
+If your distribution does not support udev, a device node will not be created
+automatically and you have to create it manually after loading the module.
+Therefore you need to know the major and minor numbers of the device. These
+numbers can be found in /sys/class/misc/monreader/dev.
+Typing cat /sys/class/misc/monreader/dev will give an output of the form
+<major>:<minor>. The device node can be created via the mknod command, enter
+mknod <name> c <major> <minor>, where <name> is the name of the device node
+to be created.
+
+Example:
+--------
+# modprobe monreader
+# cat /sys/class/misc/monreader/dev
+10:63
+# mknod /dev/monreader c 10 63
+
+This loads the module with the default monitor DCSS (MONDCSS) and creates a
+device node.
+
+File operations:
+----------------
+The following file operations are supported: open, release, read, poll.
+There are two alternative methods for reading: either non-blocking read in
+conjunction with polling, or blocking read without polling. IOCTLs are not
+supported.
+
+Read:
+-----
+Reading from the device provides a 12 Byte monitor control element (MCE),
+followed by a set of one or more contiguous monitor records (similar to the
+output of the CMS utility MONWRITE without the 4K control blocks). The MCE
+contains information on the type of the following record set (sample/event
+data), the monitor domains contained within it and the start and end address
+of the record set in the monitor DCSS. The start and end address can be used
+to determine the size of the record set, the end address is the address of the
+last byte of data. The start address is needed to handle "end-of-frame" records
+correctly (domain 1, record 13), i.e. it can be used to determine the record
+start offset relative to a 4K page (frame) boundary.
+
+See "Appendix A: *MONITOR" in the "z/VM Performance" document for a description
+of the monitor control element layout. The layout of the monitor records can
+be found here (z/VM 5.1): http://www.vm.ibm.com/pubs/mon510/index.html
+
+The layout of the data stream provided by the monreader device is as follows:
+...
+<0 byte read>
+<first MCE>              \
+<first set of records>    |
+...                       |- data set
+<last MCE>                |
+<last set of records>    /
+<0 byte read>
+...
+
+There may be more than one combination of MCE and corresponding record set
+within one data set and the end of each data set is indicated by a successful
+read with a return value of 0 (0 byte read).
+Any received data must be considered invalid until a complete set was
+read successfully, including the closing 0 byte read. Therefore you should
+always read the complete set into a buffer before processing the data.
+
+The maximum size of a data set can be as large as the size of the
+monitor DCSS, so design the buffer adequately or use dynamic memory allocation.
+The size of the monitor DCSS will be printed into syslog after loading the
+module. You can also use the (Class E privileged) CP command Q NSS MAP to
+list all available segments and information about them.
+
+As with most char devices, error conditions are indicated by returning a
+negative value for the number of bytes read. In this case, the errno variable
+indicates the error condition:
+
+EIO: reply failed, read data is invalid and the application
+     should discard the data read since the last successful read with 0 size.
+EFAULT: copy_to_user failed, read data is invalid and the application should
+        discard the data read since the last successful read with 0 size.
+EAGAIN: occurs on a non-blocking read if there is no data available at the
+        moment. There is no data missing or corrupted, just try again or rather
+        use polling for non-blocking reads.
+EOVERFLOW: message limit reached, the data read since the last successful
+           read with 0 size is valid but subsequent records may be missing.
+
+In the last case (EOVERFLOW) there may be missing data, in the first two cases
+(EIO, EFAULT) there will be missing data. It's up to the application if it will
+continue reading subsequent data or rather exit.
+
+Open:
+-----
+Only one user is allowed to open the char device. If it is already in use, the
+open function will fail (return a negative value) and set errno to EBUSY.
+The open function may also fail if an IUCV connection to the *MONITOR service
+cannot be established. In this case errno will be set to EIO and an error
+message with an IPUSER SEVER code will be printed into syslog. The IPUSER SEVER
+codes are described in the "z/VM Performance" book, Appendix A.
+
+NOTE:
+-----
+As soon as the device is opened, incoming messages will be accepted and they
+will account for the message limit, i.e. opening the device without reading
+from it will provoke the "message limit reached" error (EOVERFLOW error code)
+eventually.
+
diff --git a/Documentation/scsi/ChangeLog.1992-1997 b/Documentation/scsi/ChangeLog.1992-1997
new file mode 100644 (file)
index 0000000..dc88ee2
--- /dev/null
@@ -0,0 +1,2023 @@
+Sat Jan 18 15:51:45 1997  Richard Henderson  <rth@tamu.edu>
+
+       * Don't play with usage_count directly, instead hand around
+       the module header and use the module macros.
+
+Fri May 17 00:00:00 1996  Leonard N. Zubkoff <lnz@dandelion.com>
+
+       * BusLogic Driver Version 2.0.3 Released.
+
+Tue Apr 16 21:00:00 1996  Leonard N. Zubkoff <lnz@dandelion.com>
+
+       * BusLogic Driver Version 1.3.2 Released.
+
+Sun Dec 31 23:26:00 1995  Leonard N. Zubkoff <lnz@dandelion.com>
+
+       * BusLogic Driver Version 1.3.1 Released.
+
+Fri Nov 10 15:29:49 1995  Leonard N. Zubkoff <lnz@dandelion.com>
+
+       * Released new BusLogic driver.
+
+Wed Aug  9 22:37:04 1995  Andries Brouwer  <aeb@cwi.nl>
+
+       As a preparation for new device code, separated the various
+       functions the request->dev field had into the device proper,
+       request->rq_dev and a status field request->rq_status.
+
+       The 2nd argument of bios_param is now a kdev_t.
+
+Wed Jul 19 10:43:15 1995  Michael Neuffer  <neuffer@goofy.zdv.uni-mainz.de>
+
+        * scsi.c (scsi_proc_info): /proc/scsi/scsi now also lists all
+       attached devices.
+
+       * scsi_proc.c (proc_print_scsidevice): Added. Used by scsi.c and
+       eata_dma_proc.c to produce some device info for /proc/scsi.
+
+       * eata_dma.c (eata_queue)(eata_int_handler)(eata_scsi_done):
+       Changed handling of internal SCSI commands send to the HBA.
+
+
+Wed Jul 19 10:09:17 1995  Michael Neuffer  <neuffer@goofy.zdv.uni-mainz.de>
+
+       * Linux 1.3.11 released.
+
+       * eata_dma.c (eata_queue)(eata_int_handler): Added code to do
+       command latency measurements if requested by root through
+       /proc/scsi interface.
+       Throughout Use HZ constant for time references.
+
+       * eata_pio.c: Use HZ constant for time references.
+
+       * aic7xxx.c, aic7xxx.h, aic7xxx_asm.c: Changed copyright from BSD
+       to GNU style.
+
+       * scsi.h: Added READ_12 command opcode constant
+
+Wed Jul 19 09:25:30 1995  Michael Neuffer <neuffer@goofy.zdv.uni-mainz.de>
+
+       * Linux 1.3.10 released.
+
+       * scsi_proc.c (dispatch_scsi_info): Removed unused variable.
+
+Wed Jul 19 09:25:30 1995  Michael Neuffer  <neuffer@goofy.zdv.uni-mainz.de>
+
+       * Linux 1.3.9 released.
+
+       * scsi.c Blacklist concept expanded to 'support' more device
+       deficiencies. blacklist[] renamed to device_list[]
+       (scan_scsis): Code cleanup.
+
+       * scsi_debug.c (scsi_debug_proc_info): Added support to control
+       device lockup simulation via /proc/scsi interface.
+
+
+Wed Jul 19 09:22:34 1995  Michael Neuffer  <neuffer@goofy.zdv.uni-mainz.de>
+
+       * Linux 1.3.7 released.
+
+       * scsi_proc.c: Fixed a number of bugs in directory handling
+
+Wed Jul 19 09:18:28 1995  Michael Neuffer  <neuffer@goofy.zdv.uni-mainz.de>
+
+       * Linux 1.3.5 released.
+
+       * Native wide, multichannel and /proc/scsi support now in official
+       kernel distribution.
+
+        * scsi.c/h, hosts.c/h et al reindented to increase readability
+       (especially on 80 column wide terminals).
+
+       * scsi.c, scsi_proc.c, ../../fs/proc/inode.c: Added
+       /proc/scsi/scsi which allows root to scan for hotplugged devices.
+
+       * scsi.c (scsi_proc_info): Added, to support /proc/scsi/scsi.
+       (scan_scsis): Added some 'spaghetti' code to allow scanning for
+       single devices.
+       
+
+Thu Jun 20 15:20:27 1995  Michael Neuffer  <neuffer@goofy.zdv.uni-mainz.de>
+
+        * proc.c: Renamed to scsi_proc.c
+
+Mon Jun 12 20:32:45 1995  Michael Neuffer  <neuffer@goofy.zdv.uni-mainz.de>
+
+       * Linux 1.3.0 released.
+
+Mon May 15 19:33:14 1995  Michael Neuffer  <neuffer@goofy.zdv.uni-mainz.de>
+
+       * scsi.c: Added native multichannel and wide scsi support.
+
+       * proc.c (dispatch_scsi_info) (build_proc_dir_hba_entries):
+       Updated /proc/scsi interface.
+
+Thu May  4 17:58:48 1995  Michael Neuffer  <neuffer@goofy.zdv.uni-mainz.de>
+
+       * sd.c (requeue_sd_request): Zero out the scatterlist only if
+       scsi_malloc returned memory for it.
+
+       * eata_dma.c (register_HBA) (eata_queue): Add support for
+       large scatter/gather tables and set use_clustering accordingly
+
+       * hosts.c: Make use_clustering changeable in the Scsi_Host structure.
+
+Wed Apr 12 15:25:52 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.2.5 released.
+
+       * buslogic.c: Update to version 1.15 (From Leonard N. Zubkoff).
+       Fixed interrupt routine to avoid races when handling multiple
+       complete commands per interrupt.  Seems to come up with faster
+       cards.
+
+       * eata_dma.c: Update to 2.3.5r. Modularize. Improved error handling
+        throughout and fixed bug interrupt routine which resulted in shifted
+        status bytes. Added blink LED state checks for ISA and EISA HBAs.
+        Memory management bug seems to have disappeared ==> increasing
+        C_P_L_CURRENT_MAX to 16 for now. Decreasing C_P_L_DIV to 3 for
+        performance reasons.
+
+       * scsi.c: If we get a FMK, EOM, or ILI when attempting to scan
+       the bus, assume that it was just noise on the bus, and ignore
+       the device.
+
+       * scsi.h: Update and add a bunch of missing commands which we
+       were never using.
+
+       * sd.c: Use restore_flags in do_sd_request - this may result in
+       latency conditions, but it gets rid of races and crashes.
+       Do not save flags again when searching for a second command to
+       queue.
+
+       * st.c: Use bytes, not STP->buffer->buffer_size when reading
+       from tape.
+
+
+Tue Apr  4 09:42:08 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.2.4 released.
+
+       * st.c: Fix typo - restoring wrong flags.
+
+Wed Mar 29 06:55:12 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.2.3 released.
+
+       * st.c: Perform some waiting operations with interrupts off.
+       Is this correct???
+
+Wed Mar 22 10:34:26 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.2.2 released.
+
+       * aha152x.c: Modularize.  Add support for PCMCIA.
+
+       * eata.c: Update to version 2.0.  Fixed bug preventing media
+       detection.  If scsi_register_host returns NULL, fail gracefully.
+
+       * scsi.c: Detect as NEC (for photo-cd purposes) for the 84
+       and 25 models as "NEC_OLDCDR".
+
+       * scsi.h: Add define for NEC_OLDCDR
+
+       * sr.c: Add handling for NEC_OLDCDR.  Treat as unknown.
+
+       * u14-34f.c: Update to version 2.0.  Fixed same bug as in
+       eata.c.
+
+
+Mon Mar  6 11:11:20 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.2.0 released.  Yeah!!!
+
+       * Minor spelling/punctuation changes throughout.  Nothing
+       substantive.
+
+Mon Feb 20 21:33:03 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.95 released.
+
+       * qlogic.c: Update to version 0.41.
+
+       * seagate.c: Change some message to be more descriptive about what
+       we detected.
+
+       * sr.c: spelling/whitespace changes.
+
+Mon Feb 20 21:33:03 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.94 released.
+
+Mon Feb 20 08:57:17 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.93 released.
+
+       * hosts.h: Change io_port to long int from short.
+
+       * 53c7,8xx.c: crash on AEN fixed, SCSI reset is no longer a NOP,
+         NULL pointer panic on odd UDCs fixed, two bugs in diagnostic output
+         fixed, should initialize correctly if left running, now loadable,
+         new memory allocation, extraneous diagnostic output suppressed,
+         splx() replaced with save/restore flags. [ Drew ]
+
+       * hosts.c, hosts.h, scsi_ioctl.c, sd.c, sd_ioctl.c, sg.c, sr.c,
+       sr_ioctl.c: Add special junk at end that Emacs will use for
+       formatting the file.
+
+       * qlogic.c: Update to v0.40a.  Improve parity handling.
+
+       * scsi.c: Add Hitachi DK312C to blacklist.  Change "};" to "}" in
+       many places.  Use scsi_init_malloc to get command block - may
+       need this to be dma compatible for some host adapters.
+       Restore interrupts after unregistering a host.
+
+       * sd.c: Use sti instead of restore flags - causes latency problems.
+
+       * seagate.c: Use controller_type to determine string used when
+       registering irq.
+
+       * sr.c: More photo-cd hacks to make sure we get the xa stuff right.
+       * sr.h, sr.c: Change is_xa to xa_flags field.
+
+       * st.c: Disable retries for write operations.
+
+Wed Feb 15 10:52:56 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.92 released.
+
+       * eata.c: Update to 1.17.
+
+       * eata_dma.c: Update to 2.31a. Add more support for /proc/scsi.
+        Continuing modularization. Less crashes because of the bug in the
+        memory management ==> increase C_P_L_CURRENT_MAX to 10
+        and decrease C_P_L_DIV to 4.
+
+       * hosts.c: If we remove last host registered, reuse host number.
+       When freeing memory from host being deregistered, free extra_bytes
+       too.
+
+       * scsi.c (scan_scsis): memset(SDpnt, 0) and set SCmd.device to SDpnt.
+       Change memory allocation to work around bugs in __get_dma_pages.
+       Do not free host if usage count is not zero (for modules).
+
+       * sr_ioctl.c: Increase IOCTL_TIMEOUT to 3000.
+
+       * st.c: Allow for ST_EXTRA_DEVS in st data structures.
+
+       * u14-34f.c: Update to 1.17.
+
+Thu Feb  9 10:11:16 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.91 released.
+
+       * eata.c: Update to 1.16.  Use wish_block instead of host->block.
+
+       * hosts.c: Initialize wish_block to 0.
+
+       * hosts.h: Add wish_block.
+
+       * scsi.c: Use wish_block as indicator that the host should be added
+       to block list.
+
+       * sg.c: Add SG_EXTRA_DEVS to number of slots.
+
+       * u14-34f.c: Use wish_block.
+
+Tue Feb  7 11:46:04 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.90 released.
+
+       * eata.c: Change naming from eata_* to eata2x_*.  Now at vers 1.15.
+       Update interrupt handler to take pt_regs as arg.  Allow blocking
+       even if loaded as module.  Initialize target_time_out array.
+       Do not put sti(); in timing loop.
+
+       * hosts.c: Do not reuse host numbers.
+       Use scsi_make_blocked_list to generate blocking list.
+
+       * script_asm.pl:  Beats me.  Don't know perl.  Something to do with
+       phase index.
+
+       * scsi.c (scsi_make_blocked_list): New function - code copied from
+       hosts.c.
+
+       * scsi.c: Update code to disable photo CD for Toshiba cdroms.
+       Use just manufacturer name, not model number.
+
+       * sr.c: Fix setting density for Toshiba drives.
+
+       * u14-34f.c: Clear target_time_out array during reset.
+
+Wed Feb  1 09:20:45 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.89 released.
+
+       * Makefile, u14-34f.c: Modularize.
+
+       * Makefile, eata.c: Modularize.  Now version 1.14
+
+       * NCR5380.c: Update interrupt handler with new arglist.  Minor
+       cleanups.
+
+       * eata_dma.c: Begin to modularize.  Add hooks for /proc/scsi.
+       New version 2.3.0a. Add code in interrupt handler to allow
+        certain CDROM drivers to be detected which return a
+        CHECK_CONDITION during SCSI bus scan. Add opcode check to get
+        all DATA IN and DATA OUT phases right. Utilize HBA_interpret flag.
+       Improvements in HBA identification. Various other minor stuff.
+
+       * hosts.c: Initialize ->dma_channel and ->io_port when registering
+       a new host.
+
+       * qlogic.c: Modularize and add PCMCIA support.
+
+       * scsi.c: Add Hitachi to blacklist.
+
+       * scsi.c: Change default to no lun scan (too many problem devices).
+
+       * scsi.h: Define QUEUE_FULL condition.
+
+       * sd.c: Do not check for non-existent partition until after
+       new media check.
+
+       * sg.c: Undo previous change which was wrong.
+
+       * sr_ioctl.c: Increase IOCTL_TIMEOUT to 2000.
+
+       * st.c: Patches from Kai - improve filemark handling.
+
+Tue Jan 31 17:32:12 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.88 released.
+
+       * Throughout - spelling/grammar fixups.
+
+       * scsi.c: Make sure that all buffers are 16 byte aligned - some
+       drivers (buslogic) need this.
+
+       * scsi.c (scan_scsis): Remove message printed.
+
+       * scsi.c (scsi_init): Move message here.
+
+Mon Jan 30 06:40:25 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.87 released.
+
+       * sr.c: Photo-cd related changes. (Gerd Knorr??).
+
+       * st.c: Changes from Kai related to EOM detection.
+
+Mon Jan 23 23:53:10 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.86 released.
+
+       * 53c7,8xx.h: Change SG size to 127.
+
+       * eata_dma: Update to version 2.10i. Remove bug in the registration
+        of multiple HBAs and channels. Minor other improvements and stylistic
+        changes.
+
+       * scsi.c: Test for Toshiba XM-3401TA and exclude from detection
+       as toshiba drive - photo cd does not work with this drive.
+
+       * sr.c:  Update photocd code.
+
+Mon Jan 23 23:53:10 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.85 released.
+
+       * st.c, st_ioctl.c, sg.c, sd_ioctl.c, scsi_ioctl.c, hosts.c:
+       include linux/mm.h
+
+       * qlogic.c, buslogic.c, aha1542.c: Include linux/module.h.
+
+Sun Jan 22 22:08:46 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.84 released.
+
+       * Makefile: Support for loadable QLOGIC boards.
+
+       * aha152x.c: Update to version 1.8 from Juergen.
+
+       * eata_dma.c: Update from Michael Neuffer.
+        Remove hard limit of 2 commands per lun and make it better
+        configurable. Improvements in HBA identification.
+
+       * in2000.c: Fix biosparam to support large disks.
+
+       * qlogic.c: Minor changes (change sti -> restore_flags).
+
+Wed Jan 18 23:33:09 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.83 released.
+
+       * aha1542.c(aha1542_intr_handle): Use arguments handed down to find
+       which irq.
+
+       * buslogic.c: Likewise.
+
+       * eata_dma.c: Use min of 2 cmd_per_lun for OCS_enabled boards.
+
+       * scsi.c: Make RECOVERED_ERROR a SUGGEST_IS_OK.
+
+       * sd.c: Fail if we are opening a non-existent partition.
+
+       * sr.c: Bump SR_TIMEOUT to 15000.
+       Do not probe for media size at boot time(hard on changers).
+       Flag device as needing sector size instead.
+
+       * sr_ioctl.c: Remove CDROMMULTISESSION_SYS ioctl.
+
+       * ultrastor.c: Fix bug in call to ultrastor_interrupt (wrong #args).
+
+Mon Jan 16 07:18:23 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.82 released.
+
+       Throughout.
+       - Change all interrupt handlers to accept new calling convention.
+       In particular, we now receive the irq number as one of the arguments.
+
+       * More minor spelling corrections in some of the new files.
+
+       * aha1542.c, buslogic.c: Clean up interrupt handler a little now
+       that we receive the irq as an arg.
+
+       * aha274x.c: s/snarf_region/request_region/
+
+       * eata.c: Update to version 1.12.   Fix some comments and display a
+       message if we cannot reserve the port addresses.
+
+       * u14-34f.c: Update to version 1.13.   Fix some comments and display a
+       message if we cannot reserve the port addresses.
+
+       * eata_dma.c: Define get_board_data function (send INQUIRY command).
+       Use to improve detection of variants of different DPT boards.  Change
+       version subnumber to "0g".
+
+       * fdomain.c:  Update to version 5.26.  Improve detection of some boards
+       repackaged by IBM.
+
+       * scsi.c (scsi_register_host): Change "name" to const char *.
+
+       * sr.c: Fix problem in set mode command for Toshiba drives.
+
+       * sr.c: Fix typo from patch 81.
+
+Fri Jan 13 12:54:46 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.81 released.  Codefreeze for 1.2 release announced.
+
+       Big changes here.
+
+       * eata_dma.*: New files from Michael Neuffer.
+       (neuffer@goofy.zdv.uni-mainz.de).  Should support
+       all eata/dpt cards.
+
+       * hosts.c, Makefile: Add eata_dma.
+
+       * README.st: Document MTEOM.
+
+       Patches from me (ERY) to finish support for low-level loadable scsi.
+       It now works, and is actually useful.
+
+       * Throughout - add new argument to scsi_init_malloc that takes an
+       additional parameter.  This is used as a priority to kmalloc,
+       and you can specify the GFP_DMA flag if you need DMA-able memory.
+
+       * Makefile: For source files that are loadable, always add name
+       to SCSI_SRCS.  Fill in modules: target.
+
+       * hosts.c:  Change next_host to next_scsi_host, and make global.
+       Print hosts after we have identified all of them.  Use info()
+       function if present, otherwise use name field.
+
+       * hosts.h: Change attach function to return int, not void.
+       Define number of device slots to allow for loadable devices.
+       Define tags to tell scsi module code what type of module we
+       are loading.
+
+       * scsi.c: Fix scan_scsis so that it can be run by a user process.
+       Do not use waiting loops - use up and down mechanism as long
+       as current != task[0].
+
+       * scsi.c(scan_scsis): Do not use stack variables for I/O - this
+       could be > 16Mb if we are loading a module at runtime (i.e. use
+       scsi_init_malloc to get some memory we know will be safe).
+
+       * scsi.c: Change dma freelist to be a set of pages.  This allows
+       us to dynamically adjust the size of the list by adding more pages
+       to the pagelist.  Fix scsi_malloc and scsi_free accordingly.
+
+       * scsi_module.c: Fix include.
+
+       * sd.c: Declare detach function.  Increment/decrement module usage
+       count as required.  Fix init functions to allow loaded devices.
+       Revalidate all new disks so we get the partition tables.  Define
+       detach function.
+
+       * sr.c: Likewise.
+
+       * sg.c: Declare detach function.  Allow attachment of devices on
+       loaded drivers.
+
+       * st.c: Declare detach function.  Increment/decrement module usage
+       count as required.
+
+Tue Jan 10 10:09:58 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.79 released.
+
+       Patch from some undetermined individual who needs to get a life :-).
+
+       * sr.c: Attacked by spelling bee...
+
+       Patches from Gerd Knorr:
+
+       * sr.c: make printk messages for photoCD a little more informative.
+
+       * sr_ioctl.c: Fix CDROMMULTISESSION_SYS ioctl.
+
+Mon Jan  9 10:01:37 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.78 released.
+
+       * Makefile: Add empty modules: target.
+
+       * Wheee.  Now change register_iomem to request_region.
+
+       * in2000.c: Bugfix - apparently this is the fix that we have
+       all been waiting for.  It fixes a problem whereby the driver
+       is not stable under heavy load.  Race condition and all that.
+       Patch from Peter Lu.
+
+Wed Jan  4 21:17:40 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.77 released.
+
+       * 53c7,8xx.c: Fix from Linus - emulate splx.
+
+       Throughout:
+
+               Change "snarf_region" with "register_iomem".
+
+       * scsi_module.c: New file.  Contains support for low-level loadable
+         scsi drivers. [ERY].
+
+       * sd.c: More s/int/long/ changes.
+
+       * seagate.c: Explicitly include linux/config.h
+
+       * sg.c: Increment/decrement module usage count on open/close.
+
+       * sg.c: Be a bit more careful about the user not supplying enough
+         information for a valid command.  Pass correct size down to
+         scsi_do_cmd.
+
+       * sr.c:  More changes for Photo-CD.  This apparently breaks NEC drives.
+
+       * sr_ioctl.c:  Support CDROMMULTISESSION ioctl.
+
+
+Sun Jan  1 19:55:21 1995  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.76 released.
+
+       * constants.c: Add type cast in switch statement.
+
+       * scsi.c (scsi_free): Change datatype of "offset" to long.
+         (scsi_malloc): Change a few more variables to long.  Who
+         did this and why was it important?  64 bit machines?
+
+
+       Lots of changes to use save_state/restore_state instead of cli/sti.
+       Files changed include:
+
+       * aha1542.c:
+       * aha1740.c:
+       * buslogic.c:
+       * in2000.c:
+       * scsi.c:
+       * scsi_debug.c:
+       * sd.c:
+       * sr.c:
+       * st.c:
+
+Wed Dec 28 16:38:29 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.75 released.
+
+       * buslogic.c: Spelling fix.
+
+       * scsi.c: Add HP C1790A and C2500A scanjet to blacklist.
+
+       * scsi.c: Spelling fixup.
+
+       * sd.c: Add support for sd_hardsizes (hard sector sizes).
+
+       * ultrastor.c: Use save_flags/restore_flags instead of cli/sti.
+
+Fri Dec 23 13:36:25 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.74 released.
+
+       * README.st: Update from Kai Makisara.
+
+       * eata.c: New version from Dario - version 1.11.
+         use scsicam bios_param routine.  Add support for 2011
+         and 2021 boards.
+
+       * hosts.c: Add support for blocking.  Linked list automatically
+         generated when shpnt->block is set.
+
+       * scsi.c: Add sankyo & HP scanjet to blacklist.  Add support for
+         kicking things loose when we deadlock.
+
+       * scsi.c: Recognize scanners and processors in scan_scsis.
+
+       * scsi_ioctl.h: Increase timeout to 9 seconds.
+
+       * st.c: New version from Kai - add better support for backspace.
+
+       * u14-34f.c: New version from Dario.  Supports blocking.
+
+Wed Dec 14 14:46:30 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.73 released.
+
+       * buslogic.c: Update from Dave Gentzel.  Version 1.14.
+         Add module related stuff.   More fault tolerant if out of
+         DMA memory.
+
+       * fdomain.c: New version from Rik Faith - version 5.22.  Add support
+         for ISA-200S SCSI adapter.
+
+       * hosts.c: Spelling.
+
+       * qlogic.c: Update to version 0.38a.  Add more support for PCMCIA.
+
+       * scsi.c: Mask device type with 0x1f during scan_scsis.
+         Add support for deadlocking, err, make that getting out of
+         deadlock situations that are created when we allow the user
+         to limit requests to one host adapter at a time.
+
+       * scsi.c: Bugfix - pass pid, not SCpnt as second arg to
+         scsi_times_out.
+
+       * scsi.c: Restore interrupt state to previous value instead of using
+         cli/sti pairs.
+
+       * scsi.c: Add a bunch of module stuff (all commented out for now).
+
+       * scsi.c: Clean up scsi_dump_status.
+
+Tue Dec  6 12:34:20 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.72 released.
+
+       * sg.c: Bugfix - always use sg_free, since we might have big buff.
+
+Fri Dec  2 11:24:53 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.71 released.
+
+       * sg.c: Clear buff field when not in use.  Only call scsi_free if
+       non-null.
+
+       * scsi.h: Call wake_up(&wait_for_request) when done with a
+       command.
+
+       * scsi.c (scsi_times_out): Pass pid down so that we can protect
+       against race conditions.
+
+       * scsi.c (scsi_abort): Zero timeout field if we get the
+       NOT_RUNNING message back from low-level driver.
+
+
+       * scsi.c (scsi_done): Restore cmd_len, use_sg here.
+
+       * scsi.c (request_sense): Not here.
+
+       * hosts.h: Add new forbidden_addr, forbidden_size fields.  Who
+       added these and why????
+
+       * hosts.c (scsi_mem_init): Mark pages as reserved if they fall in
+       the forbidden regions.  I am not sure - I think this is so that
+       we can deal with boards that do incomplete decoding of their
+       address lines for the bios chips, but I am not entirely sure.
+
+       * buslogic.c: Set forbidden_addr stuff if using a buggy board.
+
+       * aha1740.c: Test for NULL pointer in SCtmp.  This should not
+       occur, but a nice message is better than a kernel segfault.
+
+       * 53c7,8xx.c: Add new PCI chip ID for 815.
+
+Fri Dec  2 11:24:53 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.70 released.
+
+       * ChangeLog, st.c: Spelling.
+
+Tue Nov 29 18:48:42 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.69 released.
+
+       * u14-34f.h: Non-functional change.  [Dario].
+
+       * u14-34f.c: Use block field in Scsi_Host to prevent commands from
+       being queued to more than one host at the same time (used when
+       motherboard does not deal with multiple bus-masters very well).
+       Only when SINGLE_HOST_OPERATIONS is defined.
+       Use new cmd_per_lun field.  [Dario]
+
+       * eata.c: Likewise.
+
+       * st.c: More changes from Kai.  Add ready flag to indicate drive
+       status.
+
+       * README.st: Document this.
+
+       * sr.c: Bugfix (do not subtract CD_BLOCK_OFFSET) for photo-cd
+       code.
+
+       * sg.c: Bugfix - fix problem where opcode is not correctly set up.
+
+       * seagate.[c,h]: Use #defines to set driver name.
+
+       * scsi_ioctl.c: Zero buffer before executing command.
+
+       * scsi.c: Use new cmd_per_lun field in Scsi_Hosts as appropriate.
+       Add Sony CDU55S to blacklist.
+
+       * hosts.h: Add new cmd_per_lun field to Scsi_Hosts.
+
+       * hosts.c: Initialize cmd_per_lun in Scsi_Hosts from template.
+
+       * buslogic.c: Use cmd_per_lun field - initialize to different
+       values depending upon bus type (i.e. use 1 if ISA, so we do not
+       hog memory).  Use other patches which got lost from 1.1.68.
+
+       * aha1542.c: Spelling.
+
+Tue Nov 29 15:43:50 1994  Eric Youngdale  (eric@andante.aib.com)
+
+       * Linux 1.1.68 released.
+
+       Add support for 12 byte vendor specific commands in scsi-generics,
+       more (i.e. the last mandatory) low-level changes to support
+       loadable modules, plus a few other changes people have requested
+       lately.  Changes by me (ERY) unless otherwise noted.  Spelling
+       changes appear from some unknown corner of the universe.
+
+       * Throughout: Change COMMAND_SIZE() to use SCpnt->cmd_len.
+
+       * Throughout: Change info() low level function to take a Scsi_Host
+       pointer.  This way the info function can return specific
+       information about the host in question, if desired.
+
+       * All low-level drivers: Add NULL in initializer for the
+       usage_count field added to Scsi_Host_Template.
+
+       * aha152x.[c,h]: Remove redundant info() function.
+
+       * aha1542.[c,h]: Likewise.
+
+       * aha1740.[c,h]: Likewise.
+
+       * aha274x.[c,h]: Likewise.
+
+       * eata.[c,h]: Likewise.
+
+       * pas16.[c,h]: Likewise.
+
+       * scsi_debug.[c,h]: Likewise.
+
+       * t128.[c,h]: Likewise.
+
+       * u14-34f.[c,h]: Likewise.
+
+       * ultrastor.[c,h]: Likewise.
+
+       * wd7000.[c,h]: Likewise.
+
+       * aha1542.c: Add support for command line options with lilo to set
+       DMA parameters, I/O port.  From Matt Aarnio.
+
+       * buslogic.[c,h]: New version (1.13) from Dave Gentzel.
+
+       * hosts.h: Add new field to Scsi_Hosts "block" to allow blocking
+       all I/O to certain other cards.  Helps prevent problems with some
+       ISA motherboards.
+
+       * hosts.h: Add usage_count to Scsi_Host_Template.
+
+       * hosts.h: Add n_io_port to Scsi_Host (used when releasing module).
+
+       * hosts.c: Initialize block field.
+
+       * in2000.c: Remove "static" declarations from exported functions.
+
+       * in2000.h: Likewise.
+
+       * scsi.c: Correctly set cmd_len field as required.  Save and
+       change setting when doing a request_sense, restore when done.
+       Move abort timeout message.  Fix panic in request_queueable to
+       print correct function name.
+
+       * scsi.c: When incrementing usage count, walk block linked list
+       for host, and or in SCSI_HOST_BLOCK bit.  When decrementing usage
+       count to 0, clear this bit to allow usage to continue, wake up
+       processes waiting.
+
+
+       * scsi_ioctl.c: If we have an info() function, call it, otherwise
+       if we have a "name" field, use it, else do nothing.
+
+       * sd.c, sr.c: Clear cmd_len field prior to each command we
+       generate.
+
+       * sd.h: Add "has_part_table" bit to rscsi_disks.
+
+       * sg.[c,h]: Add support for vendor specific 12 byte commands (i.e.
+       override command length in COMMAND_SIZE).
+
+       * sr.c: Bugfix from Gerd in photocd code.
+
+       * sr.c: Bugfix in get_sectorsize - always use scsi_malloc buffer -
+       we cannot guarantee that the stack is < 16Mb.
+
+Tue Nov 22 15:40:46 1994  Eric Youngdale  (eric@andante.aib.com)
+
+       * Linux 1.1.67 released.
+
+       * sr.c: Change spelling of manufactor to manufacturer.
+
+       * scsi.h: Likewise.
+
+       * scsi.c: Likewise.
+
+       * qlogic.c: Spelling corrections.
+
+       * in2000.h: Spelling corrections.
+
+       * in2000.c: Update from Bill Earnest, change from
+       jshiffle@netcom.com.  Support new bios versions.
+
+       * README.qlogic: Spelling correction.
+
+Tue Nov 22 15:40:46 1994  Eric Youngdale  (eric@andante.aib.com)
+
+       * Linux 1.1.66 released.
+
+       * u14-34f.c: Spelling corrections.
+
+       * sr.[h,c]: Add support for multi-session CDs from Gerd Knorr.
+
+       * scsi.h: Add manufactor field for keeping track of device
+       manufacturer.
+
+       * scsi.c: More spelling corrections.
+
+       * qlogic.h, qlogic.c, README.qlogic: New driver from Tom Zerucha.
+
+       * in2000.c, in2000.h: New driver from Brad McLean/Bill Earnest.
+
+       * fdomain.c: Spelling correction.
+
+       * eata.c: Spelling correction.
+
+Fri Nov 18 15:22:44 1994  Eric Youngdale  (eric@andante.aib.com)
+
+       * Linux 1.1.65 released.
+
+       * eata.h: Update version string to 1.08.00.
+
+       * eata.c: Set sg_tablesize correctly for DPT PM2012 boards.
+
+       * aha274x.seq: Spell checking.
+
+       * README.st: Likewise.
+
+       * README.aha274x: Likewise.
+
+       * ChangeLog: Likewise.
+
+Tue Nov 15 15:35:08 1994  Eric Youngdale  (eric@andante.aib.com)
+
+       * Linux 1.1.64 released.
+
+       * u14-34f.h: Update version number to 1.10.01.
+
+       * u14-34f.c: Use Scsi_Host can_queue variable instead of one from template.
+
+       * eata.[c,h]: New driver for DPT boards from Dario Ballabio.
+
+       * buslogic.c: Use can_queue field.
+
+Wed Nov 30 12:09:09 1994  Eric Youngdale  (eric@andante.aib.com)
+
+       * Linux 1.1.63 released.
+
+       * sd.c: Give I/O error if we attempt 512 byte I/O to a disk with
+       1024 byte sectors.
+
+       * scsicam.c: Make sure we do read from whole disk (mask off
+       partition).
+
+       * scsi.c: Use can_queue in Scsi_Host structure.
+       Fix panic message about invalid host.
+
+       * hosts.c: Initialize can_queue from template.
+
+       * hosts.h: Add can_queue to Scsi_Host structure.
+
+       * aha1740.c: Print out warning about NULL ecbptr.
+
+Fri Nov  4 12:40:30 1994  Eric Youngdale  (eric@andante.aib.com)
+
+       * Linux 1.1.62 released.
+
+       * fdomain.c: Update to version 5.20. (From Rik Faith).  Support
+       BIOS version 3.5.
+
+       * st.h: Add ST_EOD symbol.
+
+       * st.c: Patches from Kai Makisara - support additional densities,
+       add support for MTFSS, MTBSS, MTWSM commands.
+
+       * README.st: Update to document new commands.
+
+       * scsi.c: Add Mediavision CDR-H93MV to blacklist.
+
+Sat Oct 29 20:57:36 1994  Eric Youngdale  (eric@andante.aib.com)
+
+       * Linux 1.1.60 released.
+
+       * u14-34f.[c,h]: New driver from Dario Ballabio.
+
+       * aic7770.c, aha274x_seq.h, aha274x.seq, aha274x.h, aha274x.c,
+       README.aha274x: New files, new driver from John Aycock.
+
+
+Tue Oct 11 08:47:39 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.54 released.
+
+       * Add third PCI chip id.  [Drew]
+
+       * buslogic.c: Set BUSLOGIC_CMDLUN back to 1 [Eric].
+
+       * ultrastor.c: Fix asm directives for new GCC.
+
+       * sr.c, sd.c: Use new end_scsi_request function.
+
+       * scsi.h(end_scsi_request): Return pointer to block if still
+       active, else return NULL if inactive.  Fixes race condition.
+
+Sun Oct  9 20:23:14 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.53 released.
+
+       * scsi.c: Do not allocate dma bounce buffers if we have exactly
+       16Mb.
+
+Fri Sep  9 05:35:30 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.51 released.
+
+       * aha152x.c: Add support for disabling the parity check.  Update
+       to version 1.4. [Juergen].
+
+       * seagate.c: Tweak debugging message.
+
+Wed Aug 31 10:15:55 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.50 released.
+
+       * aha152x.c: Add eb800 for Vtech Platinum SMP boards. [Juergen].
+
+       * scsi.c: Add Quantum PD1225S to blacklist.
+
+Fri Aug 26 09:38:45 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.49 released.
+
+       * sd.c: Fix bug when we were deleting the wrong entry if we
+       get an unsupported sector size device.
+
+       * sr.c: Another spelling patch.
+
+Thu Aug 25 09:15:27 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.48 released.
+
+       * Throughout: Use new semantics for request_dma, as appropriate.
+
+       * sr.c: Print correct device number.
+
+Sun Aug 21 17:49:23 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.47 released.
+
+       * NCR5380.c: Add support for LIMIT_TRANSFERSIZE.
+
+       * constants.h: Add prototype for print_Scsi_Cmnd.
+
+       * pas16.c: Some more minor tweaks.  Test for Mediavision board.
+       Allow for disks > 1Gb.  [Drew??]
+
+       * sr.c: Set SCpnt->transfersize.
+
+Tue Aug 16 17:29:35 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.46 released.
+
+       * Throughout: More spelling fixups.
+
+       * buslogic.c: Add a few more fixups from Dave.  Disk translation
+       mainly.
+
+       * pas16.c: Add a few patches (Drew?).
+
+
+Thu Aug 11 20:45:15 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.44 released.
+
+       * hosts.c: Add type casts for scsi_init_malloc.
+
+       * scsicam.c: Add type cast.
+
+Wed Aug 10 19:23:01 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.43 released.
+
+       * Throughout: Spelling cleanups. [??]
+
+       * aha152x.c, NCR53*.c, fdomain.c, g_NCR5380.c, pas16.c, seagate.c,
+        t128.c: Use request_irq, not irqaction. [??]
+
+       * aha1542.c: Move test for shost before we start to use shost.
+
+       * aha1542.c, aha1740.c, ultrastor.c, wd7000.c: Use new
+       calling sequence for request_irq.
+
+       * buslogic.c: Update from Dave Gentzel.
+
+Tue Aug  9 09:32:59 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.42 released.
+
+       * NCR5380.c: Change NCR5380_print_status to static.
+
+       * seagate.c: A few more bugfixes.  Only Drew knows what they are
+       for.
+
+       * ultrastor.c: Tweak some __asm__ directives so that it works
+       with newer compilers. [??]
+
+Sat Aug  6 21:29:36 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.40 released.
+
+       * NCR5380.c: Return SCSI_RESET_WAKEUP from reset function.
+
+       * aha1542.c: Reset mailbox status after a bus device reset.
+
+       * constants.c: Fix typo (;;).
+
+       * g_NCR5380.c:
+       * pas16.c:  Correct usage of NCR5380_init.
+
+       * scsi.c: Remove redundant (and unused variables).
+
+       * sd.c: Use memset to clear all of rscsi_disks before we use it.
+
+       * sg.c: Ditto, except for scsi_generics.
+
+       * sr.c: Ditto, except for scsi_CDs.
+
+       * st.c: Initialize STp->device.
+
+       * seagate.c: Fix bug. [Drew]
+
+Thu Aug  4 08:47:27 1994  Eric Youngdale  (eric@andante)
+
+       * Linux 1.1.39 released.
+
+       * Makefile: Fix typo in NCR53C7xx.
+
+       * st.c: Print correct number for device.
+
+Tue Aug  2 11:29:14 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.38 released.
+
+       Lots of changes in 1.1.38.  All from Drew unless otherwise noted.
+
+       * 53c7,8xx.c: New file from Drew.  PCI driver.
+
+       * 53c7,8xx.h: Likewise.
+
+       * 53c7,8xx.scr: Likewise.
+
+       * 53c8xx_d.h, 53c8xx_u.h, script_asm.pl: Likewise.
+
+       * scsicam.c: New file from Drew.  Read block 0 on the disk and
+       read the partition table.  Attempt to deduce the geometry from
+       the partition table if possible.  Only used by 53c[7,8]xx right
+       now, but could be used by any device for which we have no way
+       of identifying the geometry.
+
+       * sd.c: Use device letters instead of sd%d in a lot of messages.
+
+       * seagate.c: Fix bug that resulted in lockups with some devices.
+
+       * sr.c (sr_open): Return -EROFS, not -EACCES if we attempt to open
+       device for write.
+
+       * hosts.c, Makefile: Update for new driver.
+
+       * NCR5380.c, NCR5380.h, g_NCR5380.h: Update from Drew to support
+       53C400 chip.
+
+       * constants.c: Define CONST_CMND and CONST_MSG.  Other minor
+       cleanups along the way.  Improve handling of CONST_MSG.
+
+       * fdomain.c, fdomain.h: New version from Rik Faith.  Update to
+       5.18.  Should now support TMC-3260 PCI card with 18C30 chip.
+
+       * pas16.c: Update with new irq initialization.
+
+       * t128.c: Update with minor cleanups.
+
+       * scsi.c (scsi_pid): New variable - gives each command a unique
+       id. Add Quantum LPS5235S to blacklist.  Change in_scan to
+       in_scan_scsis and make global.
+
+       * scsi.h: Add some defines for extended message handling,
+       INITIATE/RELEASE_RECOVERY.  Add a few new fields to support sync
+       transfers.
+
+       * scsi_ioctl.h: Add ioctl to request synchronous transfers.
+
+
+Tue Jul 26 21:36:58 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.37 released.
+
+       * aha1542.c: Always call aha1542_mbenable, use new udelay
+       mechanism so we do not wait a long time if the board does not
+       implement this command.
+
+       * g_NCR5380.c: Remove #include <linux/config.h> and #if
+       defined(CONFIG_SCSI_*).
+
+       * seagate.c: Likewise.
+
+       Next round of changes to support loadable modules.  Getting closer
+       now, still not possible to do anything remotely usable.
+
+       hosts.c: Create a linked list of detected high level devices.
+       (scsi_register_device): New function to insert into this list.
+       (scsi_init): Call scsi_register_device for each of the known high
+       level drivers.
+
+       hosts.h: Add prototype for linked list header.  Add structure
+       definition for device template structure which defines the linked
+       list.
+
+       scsi.c: (scan_scsis): Use linked list instead of knowledge about
+       existing high level device drivers.
+       (scsi_dev_init): Use init functions for drivers on linked list
+       instead of explicit list to initialize and attach devices to high
+       level drivers.
+
+       scsi.h: Add new field "attached" to scsi_device - count of number
+       of high level devices attached.
+
+       sd.c, sr.c, sg.c, st.c: Adjust init/attach functions to use new
+       scheme.
+
+Sat Jul 23 13:03:17 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.35 released.
+
+       * ultrastor.c: Change constraint on asm() operand so that it works
+       with gcc 2.6.0.
+
+Thu Jul 21 10:37:39 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.33 released.
+
+       * sr.c(sr_open): Do not allow opens with write access.
+
+Mon Jul 18 09:51:22 1994 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.31 released.
+
+       * sd.c: Increase SD_TIMEOUT from 300 to 600.
+
+       * sr.c: Remove stray task_struct* variable that was no longer
+       used.
+
+       * sr_ioctl.c: Fix typo in up() call.
+
+Sun Jul 17 16:25:29 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.30 released.
+
+       * scsi.c (scan_scsis): Fix detection of some Toshiba CDROM drives
+       that report themselves as disk drives.
+
+       * (Throughout): Use request.sem instead of request.waiting.
+       Should fix swap problem with fdomain.
+
+Thu Jul 14 10:51:42 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.29 released.
+
+       * scsi.c (scan_scsis): Add new devices to end of linked list, not
+       to the beginning.
+
+       * scsi.h (SCSI_SLEEP): Remove brain dead hack to try to save
+       the task state before sleeping.
+
+Sat Jul  9 15:01:03 1994  Eric Youngdale  (eric@esp22)
+
+       More changes to eventually support loadable modules.  Mainly
+       we want to use linked lists instead of arrays because it is easier
+       to dynamically add and remove things this way.
+
+       Quite a bit more work is needed before loadable modules are
+       possible (and usable) with scsi, but this is most of the grunge
+       work.
+
+       * Linux 1.1.28 released.
+
+       * scsi.c, scsi.h (allocate_device, request_queueable): Change
+       argument from index into scsi_devices to a pointer to the
+       Scsi_Device struct.
+
+       * Throughout: Change all calls to allocate_device,
+       request_queueable to use new calling sequence.
+
+       * Throughout: Use SCpnt->device instead of
+       scsi_devices[SCpnt->index].  Ugh - the pointer was there all along
+       - much cleaner this way.
+
+       * scsi.c (scsi_init_malloc, scsi_free_malloc): New functions -
+       allow us to pretend that we have a working malloc when we
+       initialize.  Use this instead of passing memory_start, memory_end
+       around all over the place.
+
+       * scsi.h, st.c, sr.c, sd.c, sg.c: Change *_init1 functions to use
+       scsi_init_malloc, remove all arguments, no return value.
+
+       * scsi.h: Remove index field from Scsi_Device and Scsi_Cmnd
+       structs.
+
+       * scsi.c (scsi_dev_init): Set up for scsi_init_malloc.
+       (scan_scsis): Get SDpnt from scsi_init_malloc, and refresh
+       when we discover a device.  Free pointer before returning.
+       Change scsi_devices into a linked list.
+
+       * scsi.c (scan_scsis): Change to only scan one host.
+       (scsi_dev_init): Loop over all detected hosts, and scan them.
+
+       * hosts.c  (scsi_init_free): Change so that  number of extra bytes
+       is stored in struct, and we do not have to pass it each time.
+
+       * hosts.h: Change Scsi_Host_Template struct to include "next" and
+       "release" functions.  Initialize to NULL in all low level
+       adapters.
+
+       * hosts.c: Rename scsi_hosts to builtin_scsi_hosts, create linked
+       list scsi_hosts, linked together with the new "next" field.
+
+Wed Jul  6 05:45:02 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.25 released.
+
+       * aha152x.c: Changes from Juergen - cleanups and updates.
+
+       * sd.c, sr.c: Use new check_media_change and revalidate
+       file_operations fields.
+
+       * st.c, st.h: Add changes from Kai Makisara, dated Jun 22.
+
+       * hosts.h: Change SG_ALL back to 0xff.  Apparently soft error
+       in /dev/brain resulted in having this bumped up.
+       Change first parameter in bios_param function to be Disk * instead
+       of index into rscsi_disks.
+
+       * sd_ioctl.c: Pass pointer to rscsi_disks element instead of index
+       to array.
+
+       * sd.h: Add struct name "scsi_disk" to typedef for Scsi_Disk.
+
+       * scsi.c: Remove redundant Maxtor XT8760S from blacklist.
+       In scsi_reset, add printk when DEBUG defined.
+
+       * All low level drivers: Modify definitions of bios_param in
+       appropriate way.
+
+Thu Jun 16 10:31:59 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.20 released.
+
+       * scsi_ioctl.c: Only pass down the actual number of characters
+       required to scsi_do_cmd, not the one rounded up to a even number
+       of sectors.
+
+       * ultrastor.c: Changes from Caleb Epstein for 24f cards.  Support
+       larger SG lists.
+
+       * ultrastor.c: Changes from me - use scsi_register to register
+       host.  Add some consistency checking,
+
+Wed Jun  1 21:12:13 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.19 released.
+
+       * scsi.h: Add new return code for reset() function:
+       SCSI_RESET_PUNT.
+
+       * scsi.c: Make SCSI_RESET_PUNT the same as SCSI_RESET_WAKEUP for
+       now.
+
+       * aha1542.c: If the command responsible for the reset is not
+       pending, return SCSI_RESET_PUNT.
+
+       * aha1740.c, buslogic.c, wd7000.c, ultrastor.c: Return
+       SCSI_RESET_PUNT instead of SCSI_RESET_SNOOZE.
+
+Tue May 31 19:36:01 1994  Eric Youngdale  (eric@esp22)
+
+       * buslogic.c: Do not print out message about "must be Adaptec"
+       if we have detected a buslogic card.  Print out a warning message
+       if we are configuring for >16Mb, since the 445S at board level
+       D or earlier does not work right.  The "D" level board can be made
+       to work by flipping an undocumented switch, but this is too subtle.
+
+    Changes based upon patches in Yggdrasil distribution.
+
+       * sg.c, sg.h: Return sense data to user.
+
+       * aha1542.c, aha1740.c, buslogic.c: Do not panic if
+       sense buffer is wrong size.
+
+       * hosts.c: Test for ultrastor card before any of the others.
+
+       * scsi.c: Allow boot-time option for max_scsi_luns=? so that
+       buggy firmware has an easy work-around.
+
+Sun May 15 20:24:34 1994  Eric Youngdale  (eric@esp22)
+
+       * Linux 1.1.15 released.
+
+       Post-codefreeze thaw...
+
+       * buslogic.[c,h]: New driver from David Gentzel.
+
+       * hosts.h: Add use_clustering field to explicitly say whether
+       clustering should be used for devices attached to this host
+       adapter.  The buslogic board apparently supports large SG lists,
+       but it is apparently faster if sd.c condenses this into a smaller
+       list.
+
+       * sd.c: Use this field instead of heuristic.
+
+       * All host adapter include files: Add appropriate initializer for
+       use_clustering field.
+
+       * scsi.h: Add #defines for return codes for the abort and reset
+       functions.  There are now a specific set of return codes to fully
+       specify all of the possible things that the low-level adapter
+       could do.
+
+       * scsi.c: Act based upon return codes from abort/reset functions.
+
+       * All host adapter abort/reset functions: Return new return code.
+
+       * Add code in scsi.c to help debug timeouts.  Use #define
+       DEBUG_TIMEOUT to enable this.
+
+       * scsi.c: If the host->irq field is set, use
+       disable_irq/enable_irq before calling queuecommand if we
+       are not already in an interrupt.  Reduce races, and we
+       can be sloppier about cli/sti in the interrupt routines now
+       (reduce interrupt latency).
+
+       * constants.c: Fix some things to eliminate warnings.  Add some
+       sense descriptions that were omitted before.
+
+       * aha1542.c: Watch for SCRD from host adapter - if we see it, set
+       a flag.  Currently we only print out the number of pending
+       commands that might need to be restarted.
+
+       * aha1542.c (aha1542_abort): Look for lost interrupts, OGMB still
+       full, and attempt to recover.  Otherwise give up.
+
+       * aha1542.c (aha1542_reset): Try BUS DEVICE RESET, and then pass
+       DID_RESET back up to the upper level code for all commands running
+       on this target (even on different LUNs).
+
+Sat May  7 14:54:01 1994
+
+       * Linux 1.1.12 released.
+
+       * st.c, st.h: New version from Kai.  Supports boot time
+       specification of number of buffers.
+
+       * wd7000.[c,h]: Updated driver from John Boyd.  Now supports
+       more than one wd7000 board in machine at one time, among other things.
+
+Wed Apr 20 22:20:35 1994
+
+       * Linux 1.1.8 released.
+
+       * sd.c: Add a few type casts where scsi_malloc is called.
+
+Wed Apr 13 12:53:29 1994
+
+       * Linux 1.1.4 released.
+
+       * scsi.c: Clean up a few printks (use %p to print pointers).
+
+Wed Apr 13 11:33:02 1994
+
+       * Linux 1.1.3 released.
+
+       * fdomain.c: Update to version 5.16 (Handle different FIFO sizes
+       better).
+
+Fri Apr  8 08:57:19 1994
+
+       * Linux 1.1.2 released.
+
+       * Throughout: SCSI portion of cluster diffs added.
+
+Tue Apr  5 07:41:50 1994
+
+       * Linux 1.1 development tree initiated.
+
+       * The linux 1.0 development tree is now effectively frozen except
+       for obvious bugfixes.
+
+******************************************************************
+******************************************************************
+******************************************************************
+******************************************************************
+
+Sun Apr 17 00:17:39 1994
+
+       * Linux 1.0, patchlevel 9 released.
+
+       * fdomain.c: Update to version 5.16 (Handle different FIFO sizes
+       better).
+
+Thu Apr  7 08:36:20 1994
+
+       * Linux 1.0, patchlevel8 released.
+
+       * fdomain.c: Update to version 5.15 from 5.9.  Handles 3.4 bios.
+
+Sun Apr  3 14:43:03 1994
+
+       * Linux 1.0, patchlevel6 released.
+
+       * wd7000.c: Make stab at fixing race condition.
+
+Sat Mar 26 14:14:50 1994
+
+       * Linux 1.0, patchlevel5 released.
+
+       * aha152x.c, Makefile: Fix a few bugs (too much data message).
+       Add a few more bios signatures. (Patches from Juergen).
+
+       * aha1542.c: Fix race condition in aha1542_out.
+
+Mon Mar 21 16:36:20 1994
+
+       * Linux 1.0, patchlevel3 released.
+
+       * sd.c, st.c, sr.c, sg.c: Return -ENXIO, not -ENODEV if we attempt
+       to open a non-existent device.
+
+       * scsi.c: Add Chinon cdrom to blacklist.
+
+       * sr_ioctl.c: Check return status of verify_area.
+
+Sat Mar  6 16:06:19 1994
+
+       * Linux 1.0 released (technically a pre-release).
+
+       * scsi.c: Add IMS CDD521, Maxtor XT-8760S to blacklist.
+
+Tue Feb 15 10:58:20 1994
+
+        * pl15e released.
+
+        * aha1542.c: For 1542C, allow dynamic device scan with >1Gb turned
+       off.
+
+       * constants.c: Fix typo in definition of CONSTANTS.
+
+        * pl15d released.
+
+Fri Feb 11 10:10:16 1994
+
+        * pl15c released.
+
+       * scsi.c: Add Maxtor XT-3280 and Rodime RO3000S to blacklist.
+
+       * scsi.c: Allow tagged queueing for scsi 3 devices as well.
+       Some really old devices report a version number of 0.  Disallow
+       LUN != 0 for these.
+
+Thu Feb 10 09:48:57 1994
+
+        * pl15b released.
+
+Sun Feb  6 12:19:46 1994
+
+        * pl15a released.
+
+Fri Feb  4 09:02:17 1994
+
+        * scsi.c: Add Teac cdrom to blacklist.
+
+Thu Feb  3 14:16:43 1994
+
+       * pl15 released.
+
+Tue Feb  1 15:47:43 1994
+
+       * pl14w released.
+
+       * wd7000.c (wd_bases): Fix typo in last change.
+
+Mon Jan 24 17:37:23 1994
+
+       * pl14u released.
+
+       * aha1542.c: Support 1542CF/extended bios.  Different from 1542C
+
+       * wd7000.c: Allow bios at 0xd8000 as well.
+
+       * ultrastor.c: Do not truncate cylinders to 1024.
+
+       * fdomain.c: Update to version 5.9 (add new bios signature).
+
+       * NCR5380.c: Update from Drew - should work a lot better now.
+
+Sat Jan  8 15:13:10 1994
+
+       * pl14o released.
+
+       * sr_ioctl.c: Zero reserved field before trying to set audio volume.
+
+Wed Jan  5 13:21:10 1994
+
+       * pl14m released.
+
+       * fdomain.c: Update to version 5.8.  No functional difference???
+
+Tue Jan  4 14:26:13 1994
+
+       * pl14l released.
+
+       * ultrastor.c: Remove outl, inl functions (now provided elsewhere).
+
+Mon Jan  3 12:27:25 1994
+
+       * pl14k released.
+
+       * aha152x.c: Remove insw and outsw functions.
+
+       * fdomain.c: Ditto.
+
+Wed Dec 29 09:47:20 1993
+
+       * pl14i released.
+
+       * scsi.c: Support RECOVERED_ERROR for tape drives.
+
+       * st.c: Update of tape driver from Kai.
+
+Tue Dec 21 09:18:30 1993
+
+       * pl14g released.
+
+       * aha1542.[c,h]: Support extended BIOS stuff.
+
+       * scsi.c: Clean up messages about disks, so they are displayed as
+       sda, sdb, etc instead of sd0, sd1, etc.
+
+       * sr.c:  Force reread of capacity if disk was changed.
+       Clear buffer before asking for capacity/sectorsize (some drives
+       do not report this properly).  Set needs_sector_size flag if
+       drive did not return sensible sector size.
+
+Mon Dec 13 12:13:47 1993
+
+       * aha152x.c: Update to version .101 from Juergen.
+
+Mon Nov 29 03:03:00 1993
+
+        * linux 0.99.14 released.
+
+       * All scsi stuff moved from kernel/blk_drv/scsi to drivers/scsi.
+
+       * Throughout: Grammatical corrections to various comments.
+
+       * Makefile: fix so that we do not need to compile things we are
+       not going to use.
+
+       * NCR5380.c, NCR5380.h, g_NCR5380.c, g_NCR5380.h, pas16.c,
+       pas16.h, t128.c, t128.h:  New files from Drew.
+
+       * aha152x.c, aha152x.h: New files from Juergen Fischer.
+
+       * aha1542.c: Support for more than one 1542 in the machine
+       at the same time.  Make functions static that do not need
+       visibility.
+
+       * aha1740.c: Set NEEDS_JUMPSTART flag in reset function, so we
+       know to restart the command.  Change prototype of aha1740_reset
+       to take a command pointer.
+
+       * constants.c: Clean up a few things.
+
+       * fdomain.c: Update to version 5.6.  Move snarf_region.  Allow
+       board to be set at different SCSI ids.  Remove support for
+       reselection (did not work well).  Set JUMPSTART flag in reset
+       code.
+
+       * hosts.c: Support new low-level adapters.  Allow for more than
+       one adapter of a given type.
+
+       * hosts.h: Allow for more than one adapter of a given type.
+
+       * scsi.c:  Add scsi_device_types array, if NEEDS_JUMPSTART is set
+       after a low-level reset, start the command again.  Sort blacklist,
+       and add Maxtor MXT-1240S, XT-4170S, NEC CDROM 84, Seagate ST157N.
+
+       * scsi.h: Add constants for tagged queueing.
+
+       * Throughout: Use constants from major.h instead of hardcoded
+       numbers for major numbers.
+
+       * scsi_ioctl.c: Fix bug in buffer length in ioctl_command.  Use
+       verify_area in GET_IDLUN ioctl.  Add new ioctls for
+       TAGGED_QUEUE_ENABLE, DISABLE.  Only allow IOCTL_SEND_COMMAND by
+       superuser.
+
+       * sd.c: Only pay attention to UNIT_ATTENTION for removable disks.
+       Fix bug where sometimes portions of blocks would get lost
+       resulting in processes hanging.  Add messages when we spin up a
+       disk, and fix a bug in the timing.  Increase read-ahead for disks
+       that are on a scatter-gather capable host adapter.
+
+       * seagate.c: Fix so that some parameters can be set from the lilo
+       prompt.  Supply jumpstart flag if we are resetting and need the
+       command restarted.   Fix so that we return 1 if we detect a card
+       so that multiple card detection works correctly.  Add yet another
+       signature for FD cards (950).  Add another signature for ST0x.
+
+       * sg.c, sg.h: New files from Lawrence Foard for generic scsi
+       access.
+
+       * sr.c:  Add type casts for (void*) so that we can do pointer
+       arithmetic.  Works with GCC without this, but it is not strictly
+       correct.  Same bugfix as was in sd.c.  Increase read-ahead a la
+       disk driver.
+
+       * sr_ioctl.c: Use scsi_malloc buffer instead of buffer from stack
+       since we cannot guarantee that the stack is < 16Mb.
+
+       ultrastor.c: Update to support 24f properly (JFC's driver).
+
+       wd7000.c: Supply jumpstart flag for reset.  Do not round up
+       number of cylinders in biosparam function.
+
+Sat Sep  4 20:49:56 1993
+
+    * 0.99pl13 released.
+
+    * Throughout:  Use check_region/snarf_region for all low-level
+    drivers.
+
+    * aha1542.c: Do hard reset instead of soft (some ethercard probes
+    screw us up).
+
+    * scsi.c: Add new flag ASKED_FOR_SENSE so that we can tell if we are
+    in a loop whereby the device returns null sense data.
+
+    * sd.c: Add code to spin up a drive if it is not already spinning.
+    Do this one at a time to make it easier on power supplies.
+
+    * sd_ioctl.c: Use sync_dev instead of fsync_dev in BLKFLSBUF ioctl.
+
+    * seagate.c: Switch around DATA/CONTROL lines.
+
+    * st.c: Change sense to unsigned.
+
+Thu Aug  5 11:59:18 1993
+
+    * 0.99pl12 released.
+
+    * constants.c, constants.h: New files with ascii descriptions of
+    various conditions.
+
+    * Makefile: Do not try to count the number of low-level drivers,
+    just generate the list of .o files.
+
+    * aha1542.c: Replace 16 with sizeof(SCpnt->sense_buffer).  Add tests
+    for addresses > 16Mb, panic if we find one.
+
+    * aha1740.c: Ditto with sizeof().
+
+    * fdomain.c: Update to version 3.18.  Add new signature, register IRQ
+    with irqaction.  Use ID 7 for new board.  Be more intelligent about
+    obtaining the h/s/c numbers for biosparam.
+
+    * hosts.c: Do not depend upon Makefile generated count of the number
+    of low-level host adapters.
+
+    * scsi.c: Use array for scsi_command_size instead of a function.  Add
+    Texel cdrom and Maxtor XT-4380S to blacklist.  Allow compile time
+    option for no-multi lun scan.  Add semaphore for possible problems
+    with handshaking, assume device is faulty until we know it not to be
+    the case.  Add DEBUG_INIT symbol to dump info as we scan for devices.
+    Zero sense buffer so we can tell if we need to request it.  When
+    examining sense information, request sense if buffer is all zero.
+    If RESET, request sense information to see what to do next.
+
+    * scsi_debug.c: Change some constants to use symbols like INT_MAX.
+
+    * scsi_ioctl.c (kernel_scsi_ioctl): New function -for making ioctl
+    calls from kernel space.
+
+    * sd.c: Increase timeout to 300.  Use functions in constants.h to
+    display info.  Use scsi_malloc buffer for READ_CAPACITY, since
+    we cannot guarantee that a stack based buffer is < 16Mb.
+
+    * sd_ioctl.c: Add BLKFLSBUF ioctl.
+
+    * seagate.c: Add new compile time options for ARBITRATE,
+    SLOW_HANDSHAKE, and SLOW_RATE.  Update assembly loops for transferring
+    data.  Use kernel_scsi_ioctl to request mode page with geometry.
+
+    * sr.c: Use functions in constants.c to display messages.
+
+    * st.c: Support for variable block size.
+
+    * ultrastor.c: Do not use cache for tape drives.  Set
+    unchecked_isa_dma flag, even though this may not be needed (gets set
+    later).
+
+Sat Jul 17 18:32:44 1993
+
+    * 0.99pl11 released.  C++ compilable.
+
+    * Throughout: Add type casts all over the place, and use "ip" instead
+    of "info" in the various biosparam functions.
+
+    * Makefile: Compile seagate.c with C++ compiler.
+
+    * aha1542.c: Always set ccb pointer as this gets trashed somehow on
+    some systems.  Add a few type casts.  Update biosparam function a little.
+
+    * aha1740.c: Add a few type casts.
+
+    * fdomain.c: Update to version 3.17 from 3.6.  Now works with
+    TMC-18C50.
+
+    * scsi.c: Minor changes here and there with datatypes.  Save use_sg
+    when requesting sense information so that this can properly be
+    restored if we retry the command.  Set aside dma buffers assuming each
+    block is 1 page, not 1Kb minix block.
+
+    * scsi_ioctl.c: Add a few type casts.  Other minor changes.
+
+    * sd.c:  Correctly  free all scsi_malloc'd memory if we run out of
+    dma_pool. Store blocksize information for each partition.
+
+    * seagate.c: Minor cleanups here and there.
+
+    * sr.c: Set up blocksize array for all discs.  Fix bug in freeing
+    buffers if we run out of dma pool.
+
+Thu Jun  2 17:58:11 1993
+
+    * 0.99pl10 released.
+
+    * aha1542.c: Support for BT 445S (VL-bus board with no dma channel).
+
+    * fdomain.c: Upgrade to version 3.6. Preliminary support for TNC-18C50.
+
+    * scsi.c: First attempt to fix problem with old_use_sg.  Change
+    NOT_READY to a SUGGEST_ABORT.  Fix timeout race where time might
+    get decremented past zero.
+
+    * sd.c: Add block_fsync function to dispatch table.
+
+    * sr.c: Increase timeout to 500 from 250.  Add entry for sync in
+    dispatch table (supply NULL).  If we do not have a sectorsize,
+    try to get it in the sd_open function.  Add new function just to
+    obtain sectorsize.
+
+    * sr.h: Add needs_sector_size semaphore.
+
+    * st.c: Add NULL for fsync in dispatch table.
+
+    * wd7000.c: Allow another condition for power on that are normal
+    and do not require a panic.
+
+Thu Apr 22 23:10:11 1993
+
+    * 0.99pl9 released.
+
+    * aha1542.c: Use (void) instead of () in setup_mailboxes.
+
+    * scsi.c: Initialize transfersize and underflow fields in SCmd to 0.
+    Do not panic for unsupported message bytes.
+
+    * scsi.h: Allocate 12 bytes instead of 10 for commands.  Add
+    transfersize and underflow fields.
+
+    * scsi_ioctl.c: Further bugfix to ioctl_probe.
+
+    * sd.c: Use long instead of int for last parameter in sd_ioctl.
+    Initialize transfersize and underflow fields.
+
+    * sd_ioctl.c: Ditto for sd_ioctl(,,,,);
+
+    * seagate.c: New version from Drew.  Includes new signatures for FD
+    cards.  Support for 0ws jumper. Correctly initialize
+    scsi_hosts[hostnum].this_id.  Improved handing of
+    disconnect/reconnect, and support command linking.  Use
+    transfersize and underflow fields.  Support scatter-gather.
+
+    * sr.c, sr_ioctl.c: Use long instead of int for last parameter in sr_ioctl.
+    Use buffer and buflength in do_ioctl.  Patches from Chris Newbold for
+    scsi-2 audio commands.
+
+    * ultrastor.c: Comment out in_byte (compiler warning).
+
+    * wd7000.c: Change () to (void) in wd7000_enable_dma.
+
+Wed Mar 31 16:36:25 1993
+
+    * 0.99pl8 released.
+
+    * aha1542.c: Handle mailboxes better for 1542C.
+        Do not truncate number of cylinders at 1024 for biosparam call.
+
+    * aha1740.c: Fix a few minor bugs for multiple devices.
+        Same as above for biosparam.
+
+    * scsi.c: Add lockable semaphore for removable devices that can have
+    media removal prevented.  Add another signature for flopticals.
+    (allocate_device): Fix race condition.  Allow more space in dma pool
+    for blocksizes of up to 4Kb.
+
+    * scsi.h: Define COMMAND_SIZE.  Define a SCSI specific version of
+    INIT_REQUEST that can run with interrupts off.
+
+    * scsi_ioctl.c: Make ioctl_probe function more idiot-proof.  If
+    a removable device says ILLEGAL REQUEST to a door-locking command,
+    clear lockable flag.  Add SCSI_IOCTL_GET_IDLUN ioctl.  Do not attempt
+    to lock door for devices that do not have lockable semaphore set.
+
+    * sd.c: Fix race condition for multiple disks.  Use INIT_SCSI_REQUEST
+    instead of INIT_REQUEST.  Allow sector sizes of 1024 and 256.  For
+    removable disks that are not ready, mark them as having a media change
+    (some drives do not report this later).
+
+    * seagate.c: Use volatile keyword for memory-mapped register pointers.
+
+    * sr.c: Fix race condition, a la sd.c.  Increase the number of retries
+    to 1.  Use INIT_SCSI_REQUEST.  Allow 512 byte sector sizes.  Do a
+    read_capacity when we init the device so we know the size and
+    sectorsize.
+
+    * st.c: If ioctl not found in st.c, try scsi_ioctl for others.
+
+    * ultrastor.c: Do not truncate number of cylinders at 1024 for
+    biosparam call.
+
+    * wd7000.c: Ditto.
+    Throughout: Use COMMAND_SIZE macro to determine length of scsi
+    command.
+
+
+
+Sat Mar 13 17:31:29 1993
+
+    * 0.99pl7 released.
+
+    Throughout: Improve punctuation in some messages, and use new
+    verify_area syntax.
+
+    * aha1542.c: Handle unexpected interrupts better.
+
+    * scsi.c: Ditto.  Handle reset conditions a bit better, asking for
+    sense information and retrying if required.
+
+    * scsi_ioctl.c: Allow for 12 byte scsi commands.
+
+    * ultrastor.c: Update to use scatter-gather.
+
+Sat Feb 20 17:57:15 1993
+
+    * 0.99pl6 released.
+
+    * fdomain.c: Update to version 3.5.  Handle spurious interrupts
+    better.
+
+    * sd.c: Use register_blkdev function.
+
+    * sr.c: Ditto.
+
+    * st.c: Use register_chrdev function.
+
+    * wd7000.c: Undo previous change.
+
+Sat Feb  6 11:20:43 1993
+
+    * 0.99pl5 released.
+
+    * scsi.c: Fix bug in testing for UNIT_ATTENTION.
+
+    * wd7000.c: Check at more addresses for bios.  Fix bug in biosparam
+    (heads & sectors turned around).
+
+Wed Jan 20 18:13:59 1993
+
+    * 0.99pl4 released.
+
+    * scsi.c: Ignore leading spaces when looking for blacklisted devices.
+
+    * seagate.c: Add a few new signatures for FD cards.  Another patch
+    with SCint to fix race condition.  Use recursion_depth to keep track
+    of how many times we have been recursively called, and do not start
+    another command unless we are on the outer level.  Fixes bug
+    with Syquest cartridge drives (used to crash kernel), because
+    they do not disconnect with large data transfers.
+
+Tue Jan 12 14:33:36 1993
+
+    * 0.99pl3 released.
+
+    * fdomain.c: Update to version 3.3 (a few new signatures).
+
+    * scsi.c: Add CDU-541, Denon DRD-25X to blacklist.
+    (allocate_request, request_queueable): Init request.waiting to NULL if
+    non-buffer type of request.
+
+    * seagate.c:  Allow controller to be overridden with CONTROLLER symbol.
+    Set SCint=NULL when we are done, to remove race condition.
+
+    * st.c: Changes from Kai.
+
+Wed Dec 30 20:03:47 1992
+
+    * 0.99pl2 released.
+
+    * scsi.c: Blacklist back in.  Remove Newbury drive as other bugfix
+    eliminates need for it here.
+
+    * sd.c: Return ENODEV instead of EACCES if no such device available.
+    (sd_init) Init blkdev_fops earlier so that sd_open is available sooner.
+
+    * sr.c: Same as above for sd.c.
+
+    * st.c: Return ENODEV instead of ENXIO if no device.  Init chrdev_fops
+    sooner, so that it is always there even if no tapes.
+
+    * seagate.c (controller_type): New variable to keep track of ST0x or
+    FD.  Modify signatures list to indicate controller type, and init
+    controller_type once we find a match.
+
+    * wd7000.c (wd7000_set_sync): Remove redundant function.
+
+Sun Dec 20 16:26:24 1992
+
+    * 0.99pl1 released.
+
+    * scsi_ioctl.c: Bugfix - check dev->index, not dev->id against
+    NR_SCSI_DEVICES.
+
+    * sr_ioctl.c: Verify that device exists before allowing an ioctl.
+
+    * st.c: Patches from Kai - change timeout values, improve end of tape
+    handling.
+
+Sun Dec 13 18:15:23 1992
+
+    * 0.99 kernel released.  Baseline for this ChangeLog.
diff --git a/Documentation/seclvl.txt b/Documentation/seclvl.txt
new file mode 100644 (file)
index 0000000..97274d1
--- /dev/null
@@ -0,0 +1,97 @@
+BSD Secure Levels Linux Security Module
+Michael A. Halcrow <mike@halcrow.us>
+
+
+Introduction
+
+Under the BSD Secure Levels security model, sets of policies are
+associated with levels. Levels range from -1 to 2, with -1 being the
+weakest and 2 being the strongest. These security policies are
+enforced at the kernel level, so not even the superuser is able to
+disable or circumvent them. This hardens the machine against attackers
+who gain root access to the system.
+
+
+Levels and Policies
+
+Level -1 (Permanently Insecure):
+ - Cannot increase the secure level
+
+Level 0 (Insecure):
+ - Cannot ptrace the init process
+
+Level 1 (Default):
+ - /dev/mem and /dev/kmem are read-only
+ - IMMUTABLE and APPEND extended attributes, if set, may not be unset
+ - Cannot load or unload kernel modules
+ - Cannot write directly to a mounted block device
+ - Cannot perform raw I/O operations
+ - Cannot perform network administrative tasks
+ - Cannot setuid any file
+
+Level 2 (Secure):
+ - Cannot decrement the system time
+ - Cannot write to any block device, whether mounted or not
+ - Cannot unmount any mounted filesystems
+
+
+Compilation
+
+To compile the BSD Secure Levels LSM, seclvl.ko, enable the
+SECURITY_SECLVL configuration option.  This is found under Security
+options -> BSD Secure Levels in the kernel configuration menu.
+
+
+Basic Usage
+
+Once the machine is in a running state, with all the necessary modules
+loaded and all the filesystems mounted, you can load the seclvl.ko
+module:
+
+# insmod seclvl.ko
+
+The module defaults to secure level 1, except when compiled directly
+into the kernel, in which case it defaults to secure level 0. To raise
+the secure level to 2, the administrator writes ``2'' to the
+seclvl/seclvl file under the sysfs mount point (assumed to be /sys in
+these examples):
+
+# echo -n "2" > /sys/seclvl/seclvl
+
+Alternatively, you can initialize the module at secure level 2 with
+the initlvl module parameter:
+
+# insmod seclvl.ko initlvl=2
+
+At this point, it is impossible to remove the module or reduce the
+secure level.  If the administrator wishes to have the option of doing
+so, he must provide a module parameter, sha1_passwd, that specifies
+the SHA1 hash of the password that can be used to reduce the secure
+level to 0.
+
+To generate this SHA1 hash, the administrator can use OpenSSL:
+
+# echo -n "boogabooga" | openssl sha1
+abeda4e0f33defa51741217592bf595efb8d289c
+
+In order to use password-instigated secure level reduction, the SHA1
+crypto module must be loaded or compiled into the kernel:
+
+# insmod sha1.ko
+
+The administrator can then insmod the seclvl module, including the
+SHA1 hash of the password:
+
+# insmod seclvl.ko
+         sha1_passwd=abeda4e0f33defa51741217592bf595efb8d289c
+
+To reduce the secure level, write the password to seclvl/passwd under
+your sysfs mount point:
+
+# echo -n "boogabooga" > /sys/seclvl/passwd
+
+The September 2004 edition of Sys Admin Magazine has an article about
+the BSD Secure Levels LSM.  I encourage you to refer to that article
+for a more in-depth treatment of this security module:
+
+http://www.samag.com/documents/s=9304/sam0409a/0409a.htm
diff --git a/Documentation/sound/alsa/Bt87x.txt b/Documentation/sound/alsa/Bt87x.txt
new file mode 100644 (file)
index 0000000..11edb2f
--- /dev/null
@@ -0,0 +1,78 @@
+Intro
+=====
+
+You might have noticed that the bt878 grabber cards have actually
+_two_ PCI functions:
+
+$ lspci
+[ ... ]
+00:0a.0 Multimedia video controller: Brooktree Corporation Bt878 (rev 02)
+00:0a.1 Multimedia controller: Brooktree Corporation Bt878 (rev 02)
+[ ... ]
+
+The first does video, it is backward compatible to the bt848.  The second
+does audio.  snd-bt87x is a driver for the second function.  It's a sound
+driver which can be used for recording sound (and _only_ recording, no
+playback).  As most TV cards come with a short cable which can be plugged
+into your sound card's line-in you probably don't need this driver if all
+you want to do is just watching TV...
+
+Some cards do not bother to connect anything to the audio input pins of
+the chip, and some other cards use the audio function to transport MPEG
+video data, so it's quite possible that audio recording may not work
+with your card.
+
+
+Driver Status
+=============
+
+The driver is now stable.  However, it doesn't know about many TV cards,
+and it refuses to load for cards it doesn't know.
+
+If the driver complains ("Unknown TV card found, the audio driver will
+not load"), you can specify the load_all=1 option to force the driver to
+try to use the audio capture function of your card.  If the frequency of
+recorded data is not right, try to specify the digital_rate option with
+other values than the default 32000 (often it's 44100 or 64000).
+
+If you have an unknown card, please mail the ID and board name to
+<alsa-devel@lists.sf.net>, regardless of whether audio capture works or
+not, so that future versions of this driver know about your card.
+
+
+Audio modes
+===========
+
+The chip knows two different modes (digital/analog).  snd-bt87x
+registers two PCM devices, one for each mode.  They cannot be used at
+the same time.
+
+
+Digital audio mode
+==================
+
+The first device (hw:X,0) gives you 16 bit stereo sound.  The sample
+rate depends on the external source which feeds the Bt87x with digital
+sound via I2S interface.
+
+
+Analog audio mode (A/D)
+=======================
+
+The second device (hw:X,1) gives you 8 or 16 bit mono sound.  Supported
+sample rates are between 119466 and 448000 Hz (yes, these numbers are
+that high).  If you've set the CONFIG_SND_BT87X_OVERCLOCK option, the
+maximum sample rate is 1792000 Hz, but audio data becomes unusable
+beyond 896000 Hz on my card.
+
+The chip has three analog inputs.  Consequently you'll get a mixer
+device to control these.
+
+
+Have fun,
+
+  Clemens
+
+
+Written by Clemens Ladisch <clemens@ladisch.de>
+big parts copied from btaudio.txt by Gerd Knorr <kraxel@bytesex.org>
diff --git a/Documentation/sparse.txt b/Documentation/sparse.txt
new file mode 100644 (file)
index 0000000..f978414
--- /dev/null
@@ -0,0 +1,72 @@
+Copyright 2004 Linus Torvalds
+Copyright 2004 Pavel Machek <pavel@suse.cz>
+
+Using sparse for typechecking
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+"__bitwise" is a type attribute, so you have to do something like this:
+
+        typedef int __bitwise pm_request_t;
+
+        enum pm_request {
+                PM_SUSPEND = (__force pm_request_t) 1,
+                PM_RESUME = (__force pm_request_t) 2
+        };
+
+which makes PM_SUSPEND and PM_RESUME "bitwise" integers (the "__force" is
+there because sparse will complain about casting to/from a bitwise type,
+but in this case we really _do_ want to force the conversion). And because
+the enum values are all the same type, now "enum pm_request" will be that
+type too.
+
+And with gcc, all the __bitwise/__force stuff goes away, and it all ends
+up looking just like integers to gcc.
+
+Quite frankly, you don't need the enum there. The above all really just
+boils down to one special "int __bitwise" type.
+
+So the simpler way is to just do
+
+        typedef int __bitwise pm_request_t;
+
+        #define PM_SUSPEND ((__force pm_request_t) 1)
+        #define PM_RESUME ((__force pm_request_t) 2)
+
+and you now have all the infrastructure needed for strict typechecking.
+
+One small note: the constant integer "0" is special. You can use a
+constant zero as a bitwise integer type without sparse ever complaining.
+This is because "bitwise" (as the name implies) was designed for making
+sure that bitwise types don't get mixed up (little-endian vs big-endian
+vs cpu-endian vs whatever), and there the constant "0" really _is_
+special.
+
+Modify top-level Makefile to say
+
+CHECK           = sparse -Wbitwise
+
+or you don't get any checking at all.
+
+
+Where to get sparse
+~~~~~~~~~~~~~~~~~~~
+
+With BK, you can just get it from
+
+        bk://sparse.bkbits.net/sparse
+
+and DaveJ has tar-balls at
+
+       http://www.codemonkey.org.uk/projects/bitkeeper/sparse/
+
+
+Once you have it, just do
+
+        make
+        make install
+
+as your regular user, and it will install sparse in your ~/bin directory.
+After that, doing a kernel make with "make C=1" will run sparse on all the
+C files that get recompiled, or with "make C=2" will run sparse on the
+files whether they need to be recompiled or not (ie the latter is fast way
+to check the whole tree if you have already built it).
diff --git a/Documentation/stable_api_nonsense.txt b/Documentation/stable_api_nonsense.txt
new file mode 100644 (file)
index 0000000..c33c99c
--- /dev/null
@@ -0,0 +1,193 @@
+The Linux Kernel Driver Interface
+(all of your questions answered and then some)
+
+Greg Kroah-Hartman <greg@kroah.com>
+
+This is being written to try to explain why Linux does not have a binary
+kernel interface, nor does it have a stable kernel interface.  Please
+realize that this article describes the _in kernel_ interfaces, not the
+kernel to userspace interfaces.  The kernel to userspace interface is
+the one that application programs use, the syscall interface.  That
+interface is _very_ stable over time, and will not break.  I have old
+programs that were built on a pre 0.9something kernel that still works
+just fine on the latest 2.6 kernel release.  This interface is the one
+that users and application programmers can count on being stable.
+
+
+Executive Summary
+-----------------
+You think you want a stable kernel interface, but you really do not, and
+you don't even know it.  What you want is a stable running driver, and
+you get that only if your driver is in the main kernel tree.  You also
+get lots of other good benefits if your driver is in the main kernel
+tree, all of which has made Linux into such a strong, stable, and mature
+operating system which is the reason you are using it in the first
+place.
+
+
+Intro
+-----
+
+It's only the odd person who wants to write a kernel driver that needs
+to worry about the in-kernel interfaces changing.  For the majority of
+the world, they neither see this interface, nor do they care about it at
+all.
+
+First off, I'm not going to address _any_ legal issues about closed
+source, hidden source, binary blobs, source wrappers, or any other term
+that describes kernel drivers that do not have their source code
+released under the GPL.  Please consult a lawyer if you have any legal
+questions, I'm a programmer and hence, I'm just going to be describing
+the technical issues here (not to make light of the legal issues, they
+are real, and you do need to be aware of them at all times.)
+
+So, there are two main topics here, binary kernel interfaces and stable
+kernel source interfaces.  They both depend on each other, but we will
+discuss the binary stuff first to get it out of the way.
+
+
+Binary Kernel Interface
+-----------------------
+Assuming that we had a stable kernel source interface for the kernel, a
+binary interface would naturally happen too, right?  Wrong.  Please
+consider the following facts about the Linux kernel:
+  - Depending on the version of the C compiler you use, different kernel
+    data structures will contain different alignment of structures, and
+    possibly include different functions in different ways (putting
+    functions inline or not.)  The individual function organization
+    isn't that important, but the different data structure padding is
+    very important.
+  - Depending on what kernel build options you select, a wide range of
+    different things can be assumed by the kernel:
+      - different structures can contain different fields
+      - Some functions may not be implemented at all, (i.e. some locks
+       compile away to nothing for non-SMP builds.)
+      - Parameter passing of variables from function to function can be
+       done in different ways (the CONFIG_REGPARM option controls
+       this.)
+      - Memory within the kernel can be aligned in different ways,
+       depending on the build options.
+  - Linux runs on a wide range of different processor architectures.
+    There is no way that binary drivers from one architecture will run
+    on another architecture properly.
+
+Now a number of these issues can be addressed by simply compiling your
+module for the exact specific kernel configuration, using the same exact
+C compiler that the kernel was built with.  This is sufficient if you
+want to provide a module for a specific release version of a specific
+Linux distribution.  But multiply that single build by the number of
+different Linux distributions and the number of different supported
+releases of the Linux distribution and you quickly have a nightmare of
+different build options on different releases.  Also realize that each
+Linux distribution release contains a number of different kernels, all
+tuned to different hardware types (different processor types and
+different options), so for even a single release you will need to create
+multiple versions of your module.
+
+Trust me, you will go insane over time if you try to support this kind
+of release, I learned this the hard way a long time ago...
+
+
+Stable Kernel Source Interfaces
+-------------------------------
+
+This is a much more "volatile" topic if you talk to people who try to
+keep a Linux kernel driver that is not in the main kernel tree up to
+date over time.
+
+Linux kernel development is continuous and at a rapid pace, never
+stopping to slow down.  As such, the kernel developers find bugs in
+current interfaces, or figure out a better way to do things.  If they do
+that, they then fix the current interfaces to work better.  When they do
+so, function names may change, structures may grow or shrink, and
+function parameters may be reworked.  If this happens, all of the
+instances of where this interface is used within the kernel are fixed up
+at the same time, ensuring that everything continues to work properly.
+
+As a specific examples of this, the in-kernel USB interfaces have
+undergone at least three different reworks over the lifetime of this
+subsystem.  These reworks were done to address a number of different
+issues:
+  - A change from a synchronous model of data streams to an asynchronous
+    one.  This reduced the complexity of a number of drivers and
+    increased the throughput of all USB drivers such that we are now
+    running almost all USB devices at their maximum speed possible.
+  - A change was made in the way data packets were allocated from the
+    USB core by USB drivers so that all drivers now needed to provide
+    more information to the USB core to fix a number of documented
+    deadlocks.
+
+This is in stark contrast to a number of closed source operating systems
+which have had to maintain their older USB interfaces over time.  This
+provides the ability for new developers to accidentally use the old
+interfaces and do things in improper ways, causing the stability of the
+operating system to suffer.
+
+In both of these instances, all developers agreed that these were
+important changes that needed to be made, and they were made, with
+relatively little pain.  If Linux had to ensure that it preserve a
+stable source interface, a new interface would have been created, and
+the older, broken one would have had to be maintained over time, leading
+to extra work for the USB developers.  Since all Linux USB developers do
+their work on their own time, asking programmers to do extra work for no
+gain, for free, is not a possibility.
+
+Security issues are also a very important for Linux.  When a
+security issue is found, it is fixed in a very short amount of time.  A
+number of times this has caused internal kernel interfaces to be
+reworked to prevent the security problem from occurring.  When this
+happens, all drivers that use the interfaces were also fixed at the
+same time, ensuring that the security problem was fixed and could not
+come back at some future time accidentally.  If the internal interfaces
+were not allowed to change, fixing this kind of security problem and
+insuring that it could not happen again would not be possible.
+
+Kernel interfaces are cleaned up over time.  If there is no one using a
+current interface, it is deleted.  This ensures that the kernel remains
+as small as possible, and that all potential interfaces are tested as
+well as they can be (unused interfaces are pretty much impossible to
+test for validity.)
+
+
+What to do
+----------
+
+So, if you have a Linux kernel driver that is not in the main kernel
+tree, what are you, a developer, supposed to do?  Releasing a binary
+driver for every different kernel version for every distribution is a
+nightmare, and trying to keep up with an ever changing kernel interface
+is also a rough job.
+
+Simple, get your kernel driver into the main kernel tree (remember we
+are talking about GPL released drivers here, if your code doesn't fall
+under this category, good luck, you are on your own here, you leech
+<insert link to leech comment from Andrew and Linus here>.)  If your
+driver is in the tree, and a kernel interface changes, it will be fixed
+up by the person who did the kernel change in the first place.  This
+ensures that your driver is always buildable, and works over time, with
+very little effort on your part.
+
+The very good side affects of having your driver in the main kernel tree
+are:
+  - The quality of the driver will rise as the maintenance costs (to the
+    original developer) will decrease.
+  - Other developers will add features to your driver.
+  - Other people will find and fix bugs in your driver.
+  - Other people will find tuning opportunities in your driver.
+  - Other people will update the driver for you when external interface
+    changes require it.
+  - The driver automatically gets shipped in all Linux distributions
+    without having to ask the distros to add it.
+    
+As Linux supports a larger number of different devices "out of the box"
+than any other operating system, and it supports these devices on more
+different processor architectures than any other operating system, this
+proven type of development model must be doing something right :)
+
+
+
+------
+
+Thanks to Randy Dunlap, Andrew Morton, David Brownell, Hanna Linder,
+Robert Love, and Nishanth Aravamudan for their review and comments on
+early drafts of this paper.
diff --git a/Documentation/usb/gadget_serial.txt b/Documentation/usb/gadget_serial.txt
new file mode 100644 (file)
index 0000000..a938c3d
--- /dev/null
@@ -0,0 +1,332 @@
+
+                 Linux Gadget Serial Driver v2.0
+                           11/20/2004
+
+
+License and Disclaimer
+----------------------
+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.
+
+This document and the the gadget serial driver itself are
+Copyright (C) 2004 by Al Borchers (alborchers@steinerpoint.com).
+
+If you have questions, problems, or suggestions for this driver
+please contact Al Borchers at alborchers@steinerpoint.com.
+
+
+Prerequisites
+-------------
+Versions of the gadget serial driver are available for the
+2.4 Linux kernels, but this document assumes you are using
+version 2.0 or later of the gadget serial driver in a 2.6
+Linux kernel.
+
+This document assumes that you are familiar with Linux and
+Windows and know how to configure and build Linux kernels, run
+standard utilities, use minicom and HyperTerminal, and work with
+USB and serial devices.  It also assumes you configure the Linux
+gadget and usb drivers as modules.
+
+
+Overview
+--------
+The gadget serial driver is a Linux USB gadget driver, a USB device
+side driver.  It runs on a Linux system that has USB device side
+hardware; for example, a PDA, an embedded Linux system, or a PC
+with a USB development card.
+
+The gadget serial driver talks over USB to either a CDC ACM driver
+or a generic USB serial driver running on a host PC.
+
+   Host
+   --------------------------------------
+  | Host-Side   CDC ACM       USB Host   |
+  | Operating |   or        | Controller |   USB
+  | System    | Generic USB | Driver     |--------
+  | (Linux or | Serial      | and        |        |
+  | Windows)    Driver        USB Stack  |        |
+   --------------------------------------         |
+                                                  |
+                                                  |
+                                                  |
+   Gadget                                         |
+   --------------------------------------         |
+  | Gadget                   USB Periph. |        |
+  | Device-Side |  Gadget  | Controller  |        |
+  | Linux       |  Serial  | Driver      |--------
+  | Operating   |  Driver  | and         |
+  | System                   USB Stack   |
+   --------------------------------------
+
+On the device-side Linux system, the gadget serial driver looks
+like a serial device.
+
+On the host-side system, the gadget serial device looks like a
+CDC ACM compliant class device or a simple vendor specific device
+with bulk in and bulk out endpoints, and it is treated similarly
+to other serial devices.
+
+The host side driver can potentially be any ACM compliant driver
+or any driver that can talk to a device with a simple bulk in/out
+interface.  Gadget serial has been tested with the Linux ACM driver,
+the Windows usbser.sys ACM driver, and the Linux USB generic serial
+driver.
+
+With the gadget serial driver and the host side ACM or generic
+serial driver running, you should be able to communicate between
+the host and the gadget side systems as if they were connected by a
+serial cable.
+
+The gadget serial driver only provides simple unreliable data
+communication.  It does not yet handle flow control or many other
+features of normal serial devices.
+
+
+Installing the Gadget Serial Driver
+-----------------------------------
+To use the gadget serial driver you must configure the Linux gadget
+side kernel for "Support for USB Gadgets", for a "USB Peripheral
+Controller" (for example, net2280), and for the "Serial Gadget"
+driver.  All this are listed under "USB Gadget Support" when
+configuring the kernel.  Then rebuild and install the kernel or
+modules.
+
+The gadget serial driver uses major number 127, for now.  So you
+will need to create a device node for it, like this:
+
+  mknod /dev/ttygserial c 127 0
+
+You only need to do this once.
+
+Then you must load the gadget serial driver.  To load it as an
+ACM device, do this:
+
+  modprobe g_serial use_acm=1
+
+To load it as a vendor specific bulk in/out device, do this:
+
+  modprobe g_serial
+
+This will also automatically load the underlying gadget peripheral
+controller driver.  This must be done each time you reboot the gadget
+side Linux system.  You can add this to the start up scripts, if
+desired.
+
+If gadget serial is loaded as an ACM device you will want to use
+either the Windows or Linux ACM driver on the host side.  If gadget
+serial is loaded as a bulk in/out device, you will want to use the
+Linux generic serial driver on the host side.  Follow the appropriate
+instructions below to install the host side driver.
+
+
+Installing the Windows Host ACM Driver
+--------------------------------------
+To use the Windows ACM driver you must have the files "gserial.inf"
+and "usbser.sys" together in a folder on the Windows machine.
+
+The "gserial.inf" file is given here.
+
+-------------------- CUT HERE --------------------
+[Version]
+Signature="$Windows NT$"
+Class=Ports
+ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318}
+Provider=%LINUX%
+DriverVer=08/17/2004,0.0.2.0
+; Copyright (C) 2004 Al Borchers (alborchers@steinerpoint.com)
+
+[Manufacturer]
+%LINUX%=GSerialDeviceList
+
+[GSerialDeviceList]
+%GSERIAL%=GSerialInstall, USB\VID_0525&PID_A4A7
+
+[DestinationDirs]
+DefaultDestDir=10,System32\Drivers
+
+[GSerialInstall]
+CopyFiles=GSerialCopyFiles
+AddReg=GSerialAddReg
+
+[GSerialCopyFiles]
+usbser.sys
+
+[GSerialAddReg]
+HKR,,DevLoader,,*ntkern
+HKR,,NTMPDriver,,usbser.sys
+HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider"
+
+[GSerialInstall.Services]
+AddService = usbser,0x0002,GSerialService
+
+[GSerialService]
+DisplayName = %GSERIAL_DISPLAY_NAME%
+ServiceType = 1                  ; SERVICE_KERNEL_DRIVER
+StartType = 3                    ; SERVICE_DEMAND_START
+ErrorControl = 1                 ; SERVICE_ERROR_NORMAL
+ServiceBinary = %10%\System32\Drivers\usbser.sys
+LoadOrderGroup = Base
+
+[Strings]
+LINUX = "Linux"
+GSERIAL = "Gadget Serial"
+GSERIAL_DISPLAY_NAME = "USB Gadget Serial Driver"
+-------------------- CUT HERE --------------------
+
+The "usbser.sys" file comes with various versions of Windows.
+For example, it can be found on Windows XP typically in
+
+  C:\WINDOWS\Driver Cache\i386\driver.cab
+
+Or it can be found on the Windows 98SE CD in the "win98" folder
+in the "DRIVER11.CAB" through "DRIVER20.CAB" cab files.  You will
+need the DOS "expand" program, the Cygwin "cabextract" program, or
+a similar program to unpack these cab files and extract "usbser.sys".
+
+For example, to extract "usbser.sys" into the current directory
+on Windows XP, open a DOS window and run a command like
+
+  expand C:\WINDOWS\Driver~1\i386\driver.cab -F:usbser.sys .
+
+(Thanks to Nishant Kamat for pointing out this DOS command.)
+
+When the gadget serial driver is loaded and the USB device connected
+to the Windows host with a USB cable, Windows should recognize the
+gadget serial device and ask for a driver.  Tell Windows to find the
+driver in the folder that contains "gserial.inf" and "usbser.sys".
+
+For example, on Windows XP, when the gadget serial device is first
+plugged in, the "Found New Hardware Wizard" starts up.  Select
+"Install from a list or specific location (Advanced)", then on
+the next screen select "Include this location in the search" and
+enter the path or browse to the folder containing "gserial.inf" and
+"usbser.sys".  Windows will complain that the Gadget Serial driver
+has not passed Windows Logo testing, but select "Continue anyway"
+and finish the driver installation.
+
+On Windows XP, in the "Device Manager" (under "Control Panel",
+"System", "Hardware") expand the "Ports (COM & LPT)" entry and you
+should see "Gadget Serial" listed as the driver for one of the COM
+ports.
+
+To uninstall the Windows XP driver for "Gadget Serial", right click
+on the "Gadget Serial" entry in the "Device Manager" and select
+"Uninstall".
+
+
+Installing the Linux Host ACM Driver
+------------------------------------
+To use the Linux ACM driver you must configure the Linux host side
+kernel for "Support for Host-side USB" and for "USB Modem (CDC ACM)
+support".
+
+Once the gadget serial driver is loaded and the USB device connected
+to the Linux host with a USB cable, the host system should recognize
+the gadget serial device.  For example, the command
+
+  cat /proc/bus/usb/devices
+
+should show something like this:
+
+T:  Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#=  5 Spd=480 MxCh= 0
+D:  Ver= 2.00 Cls=02(comm.) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
+P:  Vendor=0525 ProdID=a4a7 Rev= 2.01
+S:  Manufacturer=Linux 2.6.8.1 with net2280
+S:  Product=Gadget Serial
+S:  SerialNumber=0
+C:* #Ifs= 2 Cfg#= 2 Atr=c0 MxPwr=  2mA
+I:  If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
+E:  Ad=83(I) Atr=03(Int.) MxPS=   8 Ivl=32ms
+I:  If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
+E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+
+If the host side Linux system is configured properly, the ACM driver
+should be loaded automatically.  The command "lsmod" should show the
+"acm" module is loaded.
+
+
+Installing the Linux Host Generic USB Serial Driver
+---------------------------------------------------
+To use the Linux generic USB serial driver you must configure the
+Linux host side kernel for "Support for Host-side USB", for "USB
+Serial Converter support", and for the "USB Generic Serial Driver".
+
+Once the gadget serial driver is loaded and the USB device connected
+to the Linux host with a USB cable, the host system should recognize
+the gadget serial device.  For example, the command
+
+  cat /proc/bus/usb/devices
+
+should show something like this:
+
+T:  Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#=  6 Spd=480 MxCh= 0
+D:  Ver= 2.00 Cls=ff(vend.) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
+P:  Vendor=0525 ProdID=a4a6 Rev= 2.01
+S:  Manufacturer=Linux 2.6.8.1 with net2280
+S:  Product=Gadget Serial
+S:  SerialNumber=0
+C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr=  2mA
+I:  If#= 0 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=serial
+E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+
+You must explicitly load the usbserial driver with parameters to
+configure it to recognize the gadget serial device, like this:
+
+  modprobe usbserial vendor=0x0525 product=0xA4A6
+
+If everything is working, usbserial will print a message in the
+system log saying something like "Gadget Serial converter now
+attached to ttyUSB0".
+
+
+Testing with Minicom or HyperTerminal
+-------------------------------------
+Once the gadget serial driver and the host driver are both installed,
+and a USB cable connects the gadget device to the host, you should
+be able to communicate over USB between the gadget and host systems.
+You can use minicom or HyperTerminal to try this out.
+
+On the gadget side run "minicom -s" to configure a new minicom
+session.  Under "Serial port setup" set "/dev/ttygserial" as the
+"Serial Device".  Set baud rate, data bits, parity, and stop bits,
+to 9600, 8, none, and 1--these settings mostly do not matter.
+Under "Modem and dialing" erase all the modem and dialing strings.
+
+On a Linux host running the ACM driver, configure minicom similarly
+but use "/dev/ttyACM0" as the "Serial Device".  (If you have other
+ACM devices connected, change the device name appropriately.)
+
+On a Linux host running the USB generic serial driver, configure
+minicom similarly, but use "/dev/ttyUSB0" as the "Serial Device".
+(If you have other USB serial devices connected, change the device
+name appropriately.)
+
+On a Windows host configure a new HyperTerminal session to use the
+COM port assigned to Gadget Serial.  The "Port Settings" will be
+set automatically when HyperTerminal connects to the gadget serial
+device, so you can leave them set to the default values--these
+settings mostly do not matter.
+
+With minicom configured and running on the gadget side and with
+minicom or HyperTerminal configured and running on the host side,
+you should be able to send data back and forth between the gadget
+side and host side systems.  Anything you type on the terminal
+window on the gadget side should appear in the terminal window on
+the host side and vice versa.
+
+
diff --git a/Documentation/w1/w1.generic b/Documentation/w1/w1.generic
new file mode 100644 (file)
index 0000000..eace304
--- /dev/null
@@ -0,0 +1,19 @@
+Any w1 device must be connected to w1 bus master device - for example
+ds9490 usb device or w1-over-GPIO or RS232 converter.
+Driver for w1 bus master must provide several functions(you can find
+them in struct w1_bus_master definition in w1.h) which then will be
+called by w1 core to send various commands over w1 bus(by default it is
+reset and search commands). When some device is found on the bus, w1 core
+checks if driver for it's family is loaded.
+If driver is loaded w1 core creates new w1_slave object and registers it
+in the system(creates some generic sysfs files(struct w1_family_ops in
+w1_family.h), notifies any registered listener and so on...).
+It is device driver's business to provide any communication method
+upstream.
+For example w1_therm driver(ds18?20 thermal sensor family driver)
+provides temperature reading function which is bound to ->rbin() method
+of the above w1_family_ops structure.
+w1_smem - driver for simple 64bit memory cell provides ID reading
+method.
+
+You can call above methods by reading appropriate sysfs files.
diff --git a/arch/arm/boot/compressed/head-sharpsl.S b/arch/arm/boot/compressed/head-sharpsl.S
new file mode 100644 (file)
index 0000000..d6bf8a2
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * linux/arch/arm/boot/compressed/head-sharpsl.S
+ *
+ * Copyright (C) 2004-2005 Richard Purdie <rpurdie@rpsys.net>
+ *
+ * Sharp's bootloader doesn't pass any kind of machine ID
+ * so we have to figure out the machine for ourselves...
+ *
+ * Support for Poodle, Corgi (SL-C700), Shepherd (SL-C750)
+ * and Husky (SL-C760).
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+#ifndef CONFIG_PXA_SHARPSL
+#error What am I doing here...
+#endif
+
+               .section        ".start", "ax"
+
+__SharpSL_start:
+
+       ldr     r1, .W100ADDR           @ Base address of w100 chip + regs offset
+
+       mov r6, #0x31                   @ Load Magic Init value
+       str     r6, [r1, #0x280]        @ to SCRATCH_UMSK
+       mov r5, #0x3000
+.W100LOOP:
+       subs r5, r5, #1
+    bne .W100LOOP
+       mov r6, #0x30                   @ Load 2nd Magic Init value
+       str     r6, [r1, #0x280]        @ to SCRATCH_UMSK
+
+       ldr     r6, [r1, #0]            @ Load Chip ID
+       ldr     r3, .W100ID
+       ldr     r7, .POODLEID
+       cmp     r6, r3
+       bne     .SHARPEND                       @ We have no w100 - Poodle
+
+       mrc p15, 0, r6, c0, c0  @ Get Processor ID
+       and     r6, r6, #0xffffff00
+       ldr     r7, .CORGIID
+       ldr     r3, .PXA255ID
+       cmp     r6, r3
+       blo     .SHARPEND                       @ We have a PXA250 - Corgi
+
+       mov     r1, #0x0c000000         @ Base address of NAND chip
+       ldrb    r3, [r1, #24]   @ Load FLASHCTL
+       bic     r3, r3, #0x11           @ SET NCE
+       orr     r3, r3, #0x0a           @ SET CLR + FLWP
+       strb    r3, [r1, #24]   @ Save to FLASHCTL
+       mov     r2, #0x90               @ Command "readid"
+       strb    r2, [r1, #20]   @ Save to FLASHIO
+       bic     r3, r3, #2                      @ CLR CLE
+       orr     r3, r3, #4                      @ SET ALE
+       strb    r3, [r1, #24]   @ Save to FLASHCTL
+       mov             r2, #0                  @ Address 0x00
+       strb    r2, [r1, #20]   @ Save to FLASHIO
+       bic     r3, r3, #4                      @ CLR ALE
+       strb    r3, [r1, #24]   @ Save to FLASHCTL
+.SHARP1:
+       ldrb    r3, [r1, #24]   @ Load FLASHCTL
+       tst     r3, #32                         @ Is chip ready?
+       beq     .SHARP1
+       ldrb    r2, [r1, #20]   @ NAND Manufacturer ID
+       ldrb    r3, [r1, #20]   @ NAND Chip ID
+       ldr     r7, .SHEPHERDID
+       cmp     r3, #0x76                       @ 64MiB flash
+       beq     .SHARPEND                       @ We have Shepherd
+       ldr     r7, .HUSKYID            @ Must be Husky
+       b .SHARPEND
+
+.PXA255ID:
+       .word   0x69052d00              @ PXA255 Processor ID
+.W100ID:
+       .word   0x57411002              @ w100 Chip ID
+.W100ADDR:
+       .word   0x08010000              @ w100 Chip ID Reg Address
+.POODLEID:
+       .word   MACH_TYPE_POODLE
+.CORGIID:
+       .word   MACH_TYPE_CORGI
+.SHEPHERDID:
+       .word   MACH_TYPE_SHEPHERD
+.HUSKYID:
+       .word   MACH_TYPE_HUSKY
+.SHARPEND:
+
+
diff --git a/arch/arm/common/icst307.c b/arch/arm/common/icst307.c
new file mode 100644 (file)
index 0000000..bafe8b1
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ *  linux/arch/arm/common/icst307.c
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Support functions for calculating clocks/divisors for the ICST307
+ *  clock generators.  See http://www.icst.com/ for more information
+ *  on these devices.
+ *
+ *  This is an almost identical implementation to the ICST525 clock generator.
+ *  The s2div and idx2s files are different
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <asm/hardware/icst307.h>
+
+/*
+ * Divisors for each OD setting.
+ */
+static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 3, 6 };
+
+unsigned long icst307_khz(const struct icst307_params *p, struct icst307_vco vco)
+{
+       return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]);
+}
+
+EXPORT_SYMBOL(icst307_khz);
+
+/*
+ * Ascending divisor S values.
+ */
+static unsigned char idx2s[8] = { 1, 6, 3, 4, 7, 5, 2, 0 };
+
+struct icst307_vco
+icst307_khz_to_vco(const struct icst307_params *p, unsigned long freq)
+{
+       struct icst307_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max };
+       unsigned long f;
+       unsigned int i = 0, rd, best = (unsigned int)-1;
+
+       /*
+        * First, find the PLL output divisor such
+        * that the PLL output is within spec.
+        */
+       do {
+               f = freq * s2div[idx2s[i]];
+
+               /*
+                * f must be between 6MHz and 200MHz (3.3 or 5V)
+                */
+               if (f > 6000 && f <= p->vco_max)
+                       break;
+       } while (i < ARRAY_SIZE(idx2s));
+
+       if (i > ARRAY_SIZE(idx2s))
+               return vco;
+
+       vco.s = idx2s[i];
+
+       /*
+        * Now find the closest divisor combination
+        * which gives a PLL output of 'f'.
+        */
+       for (rd = p->rd_min; rd <= p->rd_max; rd++) {
+               unsigned long fref_div, f_pll;
+               unsigned int vd;
+               int f_diff;
+
+               fref_div = (2 * p->ref) / rd;
+
+               vd = (f + fref_div / 2) / fref_div;
+               if (vd < p->vd_min || vd > p->vd_max)
+                       continue;
+
+               f_pll = fref_div * vd;
+               f_diff = f_pll - f;
+               if (f_diff < 0)
+                       f_diff = -f_diff;
+
+               if ((unsigned)f_diff < best) {
+                       vco.v = vd - 8;
+                       vco.r = rd - 2;
+                       if (f_diff == 0)
+                               break;
+                       best = f_diff;
+               }
+       }
+
+       return vco;
+}
+
+EXPORT_SYMBOL(icst307_khz_to_vco);
+
+struct icst307_vco
+icst307_ps_to_vco(const struct icst307_params *p, unsigned long period)
+{
+       struct icst307_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max };
+       unsigned long f, ps;
+       unsigned int i = 0, rd, best = (unsigned int)-1;
+
+       ps = 1000000000UL / p->vco_max;
+
+       /*
+        * First, find the PLL output divisor such
+        * that the PLL output is within spec.
+        */
+       do {
+               f = period / s2div[idx2s[i]];
+
+               /*
+                * f must be between 6MHz and 200MHz (3.3 or 5V)
+                */
+               if (f >= ps && f < 1000000000UL / 6000 + 1)
+                       break;
+       } while (i < ARRAY_SIZE(idx2s));
+
+       if (i > ARRAY_SIZE(idx2s))
+               return vco;
+
+       vco.s = idx2s[i];
+
+       ps = 500000000UL / p->ref;
+
+       /*
+        * Now find the closest divisor combination
+        * which gives a PLL output of 'f'.
+        */
+       for (rd = p->rd_min; rd <= p->rd_max; rd++) {
+               unsigned long f_in_div, f_pll;
+               unsigned int vd;
+               int f_diff;
+
+               f_in_div = ps * rd;
+
+               vd = (f_in_div + f / 2) / f;
+               if (vd < p->vd_min || vd > p->vd_max)
+                       continue;
+
+               f_pll = (f_in_div + vd / 2) / vd;
+               f_diff = f_pll - f;
+               if (f_diff < 0)
+                       f_diff = -f_diff;
+
+               if ((unsigned)f_diff < best) {
+                       vco.v = vd - 8;
+                       vco.r = rd - 2;
+                       if (f_diff == 0)
+                               break;
+                       best = f_diff;
+               }
+       }
+
+       return vco;
+}
+
+EXPORT_SYMBOL(icst307_ps_to_vco);
diff --git a/arch/arm/common/rtctime.c b/arch/arm/common/rtctime.c
new file mode 100644 (file)
index 0000000..dc170b3
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ *  linux/arch/arm/common/rtctime.c
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions Ltd.
+ *  Based on sa1100-rtc.c, Nils Faerber, CIH, Nicolas Pitre.
+ *  Based on rtc.c by Paul Gortmaker
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#include <asm/rtc.h>
+#include <asm/semaphore.h>
+
+static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
+static struct fasync_struct *rtc_async_queue;
+
+/*
+ * rtc_lock protects rtc_irq_data
+ */
+static spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+static unsigned long rtc_irq_data;
+
+/*
+ * rtc_sem protects rtc_inuse and rtc_ops
+ */
+static DECLARE_MUTEX(rtc_sem);
+static unsigned long rtc_inuse;
+static struct rtc_ops *rtc_ops;
+
+#define rtc_epoch 1900UL
+
+static const unsigned char days_in_month[] = {
+       31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
+#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
+
+static int month_days(unsigned int month, unsigned int year)
+{
+       return days_in_month[month] + (LEAP_YEAR(year) && month == 1);
+}
+
+/*
+ * Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
+ */
+void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
+{
+       int days, month, year;
+
+       days = time / 86400;
+       time -= days * 86400;
+
+       tm->tm_wday = (days + 4) % 7;
+
+       year = 1970 + days / 365;
+       days -= (year - 1970) * 365
+               + LEAPS_THRU_END_OF(year - 1)
+               - LEAPS_THRU_END_OF(1970 - 1);
+       if (days < 0) {
+               year -= 1;
+               days += 365 + LEAP_YEAR(year);
+       }
+       tm->tm_year = year - 1900;
+       tm->tm_yday = days + 1;
+
+       for (month = 0; month < 11; month++) {
+               int newdays;
+
+               newdays = days - month_days(month, year);
+               if (newdays < 0)
+                       break;
+               days = newdays;
+       }
+       tm->tm_mon = month;
+       tm->tm_mday = days + 1;
+
+       tm->tm_hour = time / 3600;
+       time -= tm->tm_hour * 3600;
+       tm->tm_min = time / 60;
+       tm->tm_sec = time - tm->tm_min * 60;
+}
+EXPORT_SYMBOL(rtc_time_to_tm);
+
+/*
+ * Convert Gregorian date to seconds since 01-01-1970 00:00:00.
+ */
+int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
+{
+       unsigned int yrs = tm->tm_year + 1900;
+
+       *time = 0;
+
+       if (yrs < 1970 ||
+           tm->tm_mon >= 12 ||
+           tm->tm_mday < 1 ||
+           tm->tm_mday > month_days(tm->tm_mon, yrs) ||
+           tm->tm_hour >= 24 ||
+           tm->tm_min >= 60 ||
+           tm->tm_sec >= 60)
+               return -EINVAL;
+
+       *time = mktime(yrs, tm->tm_mon + 1, tm->tm_mday,
+                      tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+       return 0;
+}
+EXPORT_SYMBOL(rtc_tm_to_time);
+
+/*
+ * Calculate the next alarm time given the requested alarm time mask
+ * and the current time.
+ *
+ * FIXME: for now, we just copy the alarm time because we're lazy (and
+ * is therefore buggy - setting a 10am alarm at 8pm will not result in
+ * the alarm triggering.)
+ */
+void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm)
+{
+       next->tm_year = now->tm_year;
+       next->tm_mon = now->tm_mon;
+       next->tm_mday = now->tm_mday;
+       next->tm_hour = alrm->tm_hour;
+       next->tm_min = alrm->tm_min;
+       next->tm_sec = alrm->tm_sec;
+}
+
+static inline void rtc_read_time(struct rtc_ops *ops, struct rtc_time *tm)
+{
+       memset(tm, 0, sizeof(struct rtc_time));
+       ops->read_time(tm);
+}
+
+static inline int rtc_set_time(struct rtc_ops *ops, struct rtc_time *tm)
+{
+       return ops->set_time(tm);
+}
+
+static inline int rtc_read_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
+{
+       int ret = -EINVAL;
+       if (ops->read_alarm) {
+               memset(alrm, 0, sizeof(struct rtc_wkalrm));
+               ops->read_alarm(alrm);
+               ret = 0;
+       }
+       return ret;
+}
+
+static inline int rtc_set_alarm(struct rtc_ops *ops, struct rtc_wkalrm *alrm)
+{
+       int ret = -EINVAL;
+       if (ops->set_alarm)
+               ret = ops->set_alarm(alrm);
+       return ret;
+}
+
+void rtc_update(unsigned long num, unsigned long events)
+{
+       spin_lock(&rtc_lock);
+       rtc_irq_data = (rtc_irq_data + (num << 8)) | events;
+       spin_unlock(&rtc_lock);
+
+       wake_up_interruptible(&rtc_wait);
+       kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
+}
+EXPORT_SYMBOL(rtc_update);
+
+
+static ssize_t
+rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       unsigned long data;
+       ssize_t ret;
+
+       if (count < sizeof(unsigned long))
+               return -EINVAL;
+
+       add_wait_queue(&rtc_wait, &wait);
+       do {
+               __set_current_state(TASK_INTERRUPTIBLE);
+
+               spin_lock_irq(&rtc_lock);
+               data = rtc_irq_data;
+               rtc_irq_data = 0;
+               spin_unlock_irq(&rtc_lock);
+
+               if (data != 0) {
+                       ret = 0;
+                       break;
+               }
+               if (file->f_flags & O_NONBLOCK) {
+                       ret = -EAGAIN;
+                       break;
+               }
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+               schedule();
+       } while (1);
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&rtc_wait, &wait);
+
+       if (ret == 0) {
+               ret = put_user(data, (unsigned long __user *)buf);
+               if (ret == 0)
+                       ret = sizeof(unsigned long);
+       }
+       return ret;
+}
+
+static unsigned int rtc_poll(struct file *file, poll_table *wait)
+{
+       unsigned long data;
+
+       poll_wait(file, &rtc_wait, wait);
+
+       spin_lock_irq(&rtc_lock);
+       data = rtc_irq_data;
+       spin_unlock_irq(&rtc_lock);
+
+       return data != 0 ? POLLIN | POLLRDNORM : 0;
+}
+
+static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+                    unsigned long arg)
+{
+       struct rtc_ops *ops = file->private_data;
+       struct rtc_time tm;
+       struct rtc_wkalrm alrm;
+       void __user *uarg = (void __user *)arg;
+       int ret = -EINVAL;
+
+       switch (cmd) {
+       case RTC_ALM_READ:
+               ret = rtc_read_alarm(ops, &alrm);
+               if (ret)
+                       break;
+               ret = copy_to_user(uarg, &alrm.time, sizeof(tm));
+               if (ret)
+                       ret = -EFAULT;
+               break;
+
+       case RTC_ALM_SET:
+               ret = copy_from_user(&alrm.time, uarg, sizeof(tm));
+               if (ret) {
+                       ret = -EFAULT;
+                       break;
+               }
+               alrm.enabled = 0;
+               alrm.pending = 0;
+               alrm.time.tm_mday = -1;
+               alrm.time.tm_mon = -1;
+               alrm.time.tm_year = -1;
+               alrm.time.tm_wday = -1;
+               alrm.time.tm_yday = -1;
+               alrm.time.tm_isdst = -1;
+               ret = rtc_set_alarm(ops, &alrm);
+               break;
+
+       case RTC_RD_TIME:
+               rtc_read_time(ops, &tm);
+               ret = copy_to_user(uarg, &tm, sizeof(tm));
+               if (ret)
+                       ret = -EFAULT;
+               break;
+
+       case RTC_SET_TIME:
+               if (!capable(CAP_SYS_TIME)) {
+                       ret = -EACCES;
+                       break;
+               }
+               ret = copy_from_user(&tm, uarg, sizeof(tm));
+               if (ret) {
+                       ret = -EFAULT;
+                       break;
+               }
+               ret = rtc_set_time(ops, &tm);
+               break;
+
+       case RTC_EPOCH_SET:
+#ifndef rtc_epoch
+               /*
+                * There were no RTC clocks before 1900.
+                */
+               if (arg < 1900) {
+                       ret = -EINVAL;
+                       break;
+               }
+               if (!capable(CAP_SYS_TIME)) {
+                       ret = -EACCES;
+                       break;
+               }
+               rtc_epoch = arg;
+               ret = 0;
+#endif
+               break;
+
+       case RTC_EPOCH_READ:
+               ret = put_user(rtc_epoch, (unsigned long __user *)uarg);
+               break;
+
+       case RTC_WKALM_SET:
+               ret = copy_from_user(&alrm, uarg, sizeof(alrm));
+               if (ret) {
+                       ret = -EFAULT;
+                       break;
+               }
+               ret = rtc_set_alarm(ops, &alrm);
+               break;
+
+       case RTC_WKALM_RD:
+               ret = rtc_read_alarm(ops, &alrm);
+               if (ret)
+                       break;
+               ret = copy_to_user(uarg, &alrm, sizeof(alrm));
+               if (ret)
+                       ret = -EFAULT;
+               break;
+
+       default:
+               if (ops->ioctl)
+                       ret = ops->ioctl(cmd, arg);
+               break;
+       }
+       return ret;
+}
+
+static int rtc_open(struct inode *inode, struct file *file)
+{
+       int ret;
+
+       down(&rtc_sem);
+
+       if (rtc_inuse) {
+               ret = -EBUSY;
+       } else if (!rtc_ops || !try_module_get(rtc_ops->owner)) {
+               ret = -ENODEV;
+       } else {
+               file->private_data = rtc_ops;
+
+               ret = rtc_ops->open ? rtc_ops->open() : 0;
+               if (ret == 0) {
+                       spin_lock_irq(&rtc_lock);
+                       rtc_irq_data = 0;
+                       spin_unlock_irq(&rtc_lock);
+
+                       rtc_inuse = 1;
+               }
+       }
+       up(&rtc_sem);
+
+       return ret;
+}
+
+static int rtc_release(struct inode *inode, struct file *file)
+{
+       struct rtc_ops *ops = file->private_data;
+
+       if (ops->release)
+               ops->release();
+
+       spin_lock_irq(&rtc_lock);
+       rtc_irq_data = 0;
+       spin_unlock_irq(&rtc_lock);
+
+       module_put(rtc_ops->owner);
+       rtc_inuse = 0;
+
+       return 0;
+}
+
+static int rtc_fasync(int fd, struct file *file, int on)
+{
+       return fasync_helper(fd, file, on, &rtc_async_queue);
+}
+
+static struct file_operations rtc_fops = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .read           = rtc_read,
+       .poll           = rtc_poll,
+       .ioctl          = rtc_ioctl,
+       .open           = rtc_open,
+       .release        = rtc_release,
+       .fasync         = rtc_fasync,
+};
+
+static struct miscdevice rtc_miscdev = {
+       .minor          = RTC_MINOR,
+       .name           = "rtc",
+       .fops           = &rtc_fops,
+};
+
+
+static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+       struct rtc_ops *ops = data;
+       struct rtc_wkalrm alrm;
+       struct rtc_time tm;
+       char *p = page;
+       int len;
+
+       rtc_read_time(ops, &tm);
+
+       p += sprintf(p,
+               "rtc_time\t: %02d:%02d:%02d\n"
+               "rtc_date\t: %04d-%02d-%02d\n"
+               "rtc_epoch\t: %04lu\n",
+               tm.tm_hour, tm.tm_min, tm.tm_sec,
+               tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+               rtc_epoch);
+
+       if (rtc_read_alarm(ops, &alrm) == 0) {
+               p += sprintf(p, "alrm_time\t: ");
+               if ((unsigned int)alrm.time.tm_hour <= 24)
+                       p += sprintf(p, "%02d:", alrm.time.tm_hour);
+               else
+                       p += sprintf(p, "**:");
+               if ((unsigned int)alrm.time.tm_min <= 59)
+                       p += sprintf(p, "%02d:", alrm.time.tm_min);
+               else
+                       p += sprintf(p, "**:");
+               if ((unsigned int)alrm.time.tm_sec <= 59)
+                       p += sprintf(p, "%02d\n", alrm.time.tm_sec);
+               else
+                       p += sprintf(p, "**\n");
+
+               p += sprintf(p, "alrm_date\t: ");
+               if ((unsigned int)alrm.time.tm_year <= 200)
+                       p += sprintf(p, "%04d-", alrm.time.tm_year + 1900);
+               else
+                       p += sprintf(p, "****-");
+               if ((unsigned int)alrm.time.tm_mon <= 11)
+                       p += sprintf(p, "%02d-", alrm.time.tm_mon + 1);
+               else
+                       p += sprintf(p, "**-");
+               if ((unsigned int)alrm.time.tm_mday <= 31)
+                       p += sprintf(p, "%02d\n", alrm.time.tm_mday);
+               else
+                       p += sprintf(p, "**\n");
+               p += sprintf(p, "alrm_wakeup\t: %s\n",
+                            alrm.enabled ? "yes" : "no");
+               p += sprintf(p, "alrm_pending\t: %s\n",
+                            alrm.pending ? "yes" : "no");
+       }
+
+       if (ops->proc)
+               p += ops->proc(p);
+
+       len = (p - page) - off;
+       if (len < 0)
+               len = 0;
+       *eof = len <= count;
+       *start = page + off;
+
+       return len;
+}
+
+int register_rtc(struct rtc_ops *ops)
+{
+       int ret = -EBUSY;
+
+       down(&rtc_sem);
+       if (rtc_ops == NULL) {
+               rtc_ops = ops;
+
+               ret = misc_register(&rtc_miscdev);
+               if (ret == 0)
+                       create_proc_read_entry("driver/rtc", 0, NULL,
+                                              rtc_read_proc, ops);
+       }
+       up(&rtc_sem);
+
+       return ret;
+}
+EXPORT_SYMBOL(register_rtc);
+
+void unregister_rtc(struct rtc_ops *rtc)
+{
+       down(&rtc_sem);
+       if (rtc == rtc_ops) {
+               remove_proc_entry("driver/rtc", NULL);
+               misc_deregister(&rtc_miscdev);
+               rtc_ops = NULL;
+       }
+       up(&rtc_sem);
+}
+EXPORT_SYMBOL(unregister_rtc);
diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c
new file mode 100644 (file)
index 0000000..811c554
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Support code for the SCOOP interface found on various Sharp PDAs
+ *
+ * Copyright (c) 2004 Richard Purdie
+ *
+ *     Based on code written by Sharp/Lineo for 2.4 kernels
+ *
+ * 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 <linux/device.h>
+#include <asm/io.h>
+#include <asm/hardware/scoop.h>
+
+static void __iomem *scoop_io_base;
+
+#define SCOOP_REG(adr) (*(volatile unsigned short*)(scoop_io_base+(adr)))
+
+void reset_scoop(void)
+{
+       SCOOP_REG(SCOOP_MCR) = 0x0100;  // 00
+       SCOOP_REG(SCOOP_CDR) = 0x0000;  // 04
+       SCOOP_REG(SCOOP_CPR) = 0x0000;  // 0C
+       SCOOP_REG(SCOOP_CCR) = 0x0000;  // 10
+       SCOOP_REG(SCOOP_IMR) = 0x0000;  // 18
+       SCOOP_REG(SCOOP_IRM) = 0x00FF;  // 14
+       SCOOP_REG(SCOOP_ISR) = 0x0000;  // 1C
+       SCOOP_REG(SCOOP_IRM) = 0x0000;
+}
+
+static DEFINE_SPINLOCK(scoop_lock);
+static u32 scoop_gpwr;
+
+unsigned short set_scoop_gpio(unsigned short bit)
+{
+       unsigned short gpio_bit;
+       unsigned long flag;
+
+       spin_lock_irqsave(&scoop_lock, flag);
+       gpio_bit = SCOOP_REG(SCOOP_GPWR) | bit;
+       SCOOP_REG(SCOOP_GPWR) = gpio_bit;
+       spin_unlock_irqrestore(&scoop_lock, flag);
+
+       return gpio_bit;
+}
+
+unsigned short reset_scoop_gpio(unsigned short bit)
+{
+       unsigned short gpio_bit;
+       unsigned long flag;
+
+       spin_lock_irqsave(&scoop_lock, flag);
+       gpio_bit = SCOOP_REG(SCOOP_GPWR) & ~bit;
+       SCOOP_REG(SCOOP_GPWR) = gpio_bit;
+       spin_unlock_irqrestore(&scoop_lock, flag);
+
+       return gpio_bit;
+}
+
+EXPORT_SYMBOL(set_scoop_gpio);
+EXPORT_SYMBOL(reset_scoop_gpio);
+
+unsigned short read_scoop_reg(unsigned short reg)
+{
+       return SCOOP_REG(reg);
+}
+
+void write_scoop_reg(unsigned short reg, unsigned short data)
+{
+       SCOOP_REG(reg)=data;
+}
+
+EXPORT_SYMBOL(reset_scoop);
+EXPORT_SYMBOL(read_scoop_reg);
+EXPORT_SYMBOL(write_scoop_reg);
+
+static int scoop_suspend(struct device *dev, uint32_t state, uint32_t level)
+{
+       if (level == SUSPEND_POWER_DOWN) {
+               scoop_gpwr = SCOOP_REG(SCOOP_GPWR);
+               SCOOP_REG(SCOOP_GPWR) = 0;
+       }
+       return 0;
+}
+
+static int scoop_resume(struct device *dev, uint32_t level)
+{
+       if (level == RESUME_POWER_ON) {
+               SCOOP_REG(SCOOP_GPWR) = scoop_gpwr;
+       }
+       return 0;
+}
+
+int __init scoop_probe(struct device *dev)
+{
+       struct scoop_config *inf;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       if (!mem)
+               return -EINVAL;
+
+       inf = dev->platform_data;
+       scoop_io_base = ioremap(mem->start, 0x1000);
+       if (!scoop_io_base)
+               return -ENOMEM;
+
+       SCOOP_REG(SCOOP_MCR) = 0x0140;
+
+       reset_scoop();
+
+       SCOOP_REG(SCOOP_GPCR) = inf->io_dir & 0xffff;
+       SCOOP_REG(SCOOP_GPWR) = inf->io_out & 0xffff;
+
+       return 0;
+}
+
+static struct device_driver scoop_driver = {
+       .name           = "sharp-scoop",
+       .bus            = &platform_bus_type,
+       .probe          = scoop_probe,
+       .suspend        = scoop_suspend,
+       .resume         = scoop_resume,
+};
+
+int __init scoop_init(void)
+{
+       return driver_register(&scoop_driver);
+}
+
+subsys_initcall(scoop_init);
diff --git a/arch/arm/configs/iq80332_defconfig b/arch/arm/configs/iq80332_defconfig
new file mode 100644 (file)
index 0000000..f6dacc4
--- /dev/null
@@ -0,0 +1,864 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10
+# Thu Jan  6 10:51:02 2005
+#
+CONFIG_ARM=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_IOMAP=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 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=y
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_H720X is not set
+
+#
+# IOP3xx Implementation Options
+#
+
+#
+# IOP3xx Platform Types
+#
+# CONFIG_ARCH_IQ80321 is not set
+# CONFIG_ARCH_IQ31244 is not set
+# CONFIG_ARCH_IQ80331 is not set
+CONFIG_MACH_IQ80332=y
+# CONFIG_ARCH_EP80219 is not set
+CONFIG_ARCH_IOP331=y
+
+#
+# IOP3xx Chipset Features
+#
+# CONFIG_IOP331_STEPD is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSCALE=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_CACHE_VIVT=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_PCI=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_PCI_LEGACY_PROC is not set
+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=y
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_PM is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_ARTHUR is not set
+CONFIG_CMDLINE="ip=boot root=nfs console=ttyS0,115200"
+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_REDBOOT_PARTS_UNALLOCATED=y
+CONFIG_MTD_REDBOOT_PARTS_READONLY=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 is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+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_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_START=0xc0000000
+CONFIG_MTD_PHYSMAP_LEN=0x00800000
+CONFIG_MTD_PHYSMAP_BANKWIDTH=1
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_EDB7312 is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM 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_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 is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=8192
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=y
+CONFIG_MD_RAID0=y
+CONFIG_MD_RAID1=y
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=y
+# CONFIG_MD_RAID6 is not set
+# CONFIG_MD_MULTIPATH is not set
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=y
+# CONFIG_DM_CRYPT is not set
+# CONFIG_DM_SNAPSHOT is not set
+# CONFIG_DM_MIRROR is not set
+# CONFIG_DM_ZERO is not set
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# 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_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_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+# CONFIG_NET_ETHERNET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+CONFIG_E1000=y
+CONFIG_E1000_NAPI=y
+# 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC 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=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=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+CONFIG_SCSI_QLA2XXX=y
+# CONFIG_SCSI_QLA21XX is not set
+# CONFIG_SCSI_QLA22XX is not set
+# CONFIG_SCSI_QLA2300 is not set
+# CONFIG_SCSI_QLA2322 is not set
+# CONFIG_SCSI_QLA6312 is not set
+# CONFIG_SCSI_QLA6322 is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG 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 is not set
+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
+
+#
+# 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=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+# 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
+
+#
+# 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_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# 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 is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA 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_IOP3XX=y
+# CONFIG_I2C_ISA is not set
+# 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_STUB is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_VOODOO3 is not set
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Hardware Sensors Chip support
+#
+# CONFIG_I2C_SENSOR is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1031 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_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 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_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_SMSC47M1 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 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 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 is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# 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_XFS_FS=y
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+CONFIG_XFS_SECURITY=y
+CONFIG_XFS_POSIX_ACL=y
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# 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_MSDOS_FS is not set
+# 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=y
+# CONFIG_TMPFS_XATTR 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=y
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=y
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V4 is not set
+# CONFIG_NFSD_TCP is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 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_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
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# Misc devices
+#
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_INFO is not set
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_USER=y
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC32 is not set
+# CONFIG_LIBCRC32C is not set
diff --git a/arch/arm/configs/omap_h2_1610_defconfig b/arch/arm/configs/omap_h2_1610_defconfig
new file mode 100644 (file)
index 0000000..f569a6f
--- /dev/null
@@ -0,0 +1,935 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11-rc2
+# Tue Feb  1 14:01:46 2005
+#
+CONFIG_ARM=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_IOMAP=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+# CONFIG_KMOD is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 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 is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+CONFIG_ARCH_OMAP=y
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_H720X is not set
+
+#
+# TI OMAP Implementations
+#
+
+#
+# OMAP Core Type
+#
+# CONFIG_ARCH_OMAP730 is not set
+# CONFIG_ARCH_OMAP1510 is not set
+CONFIG_ARCH_OMAP16XX=y
+CONFIG_ARCH_OMAP_OTG=y
+
+#
+# OMAP Board Type
+#
+# CONFIG_MACH_OMAP_INNOVATOR is not set
+CONFIG_MACH_OMAP_H2=y
+# CONFIG_MACH_OMAP_H3 is not set
+# CONFIG_MACH_OMAP_H4 is not set
+# CONFIG_MACH_OMAP_OSK is not set
+# CONFIG_MACH_OMAP_GENERIC is not set
+
+#
+# OMAP Feature Selections
+#
+CONFIG_OMAP_MUX=y
+# CONFIG_OMAP_MUX_DEBUG is not set
+CONFIG_OMAP_MUX_WARNINGS=y
+CONFIG_OMAP_LL_DEBUG_UART1=y
+# CONFIG_OMAP_LL_DEBUG_UART2 is not set
+# CONFIG_OMAP_LL_DEBUG_UART3 is not set
+CONFIG_OMAP_ARM_192MHZ=y
+# CONFIG_OMAP_ARM_168MHZ is not set
+# CONFIG_OMAP_ARM_120MHZ is not set
+# CONFIG_OMAP_ARM_60MHZ is not set
+# CONFIG_OMAP_ARM_30MHZ is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM926T=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5TJ=y
+CONFIG_CPU_CACHE_VIVT=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
+# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+
+#
+# General setup
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+# CONFIG_XIP_KERNEL is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PC-card bridges
+#
+
+#
+# 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_VFP is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=y
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+CONFIG_DEBUG_DRIVER=y
+CONFIG_PM=y
+CONFIG_PREEMPT=y
+# CONFIG_APM is not set
+# CONFIG_ARTHUR is not set
+CONFIG_CMDLINE="mem=32M console=ttyS0,115200n8 root=0801 ro init=/bin/sh"
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+CONFIG_MTD=y
+CONFIG_MTD_DEBUG=y
+CONFIG_MTD_DEBUG_VERBOSE=3
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_CONCAT is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# 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_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+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_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_XIP 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_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLKMTD is not set
+# CONFIG_MTD_BLOCK2MTD 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_COW_COMMON 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_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_ATA_OVER_ETH=m
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP 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=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_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_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+CONFIG_PPP=y
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+# CONFIG_PPP_ASYNC is not set
+# CONFIG_PPP_SYNC_TTY is not set
+# CONFIG_PPP_DEFLATE is not set
+# CONFIG_PPP_BSDCOMP is not set
+# CONFIG_PPPOE is not set
+CONFIG_SLIP=y
+CONFIG_SLIP_COMPRESSED=y
+# CONFIG_SLIP_SMART is not set
+# CONFIG_SLIP_MODE_SLIP6 is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 is not set
+# 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 is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# 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=y
+
+#
+# Input I/O drivers
+#
+# CONFIG_GAMEPORT is not set
+CONFIG_SOUND_GAMEPORT=y
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_RAW 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=y
+CONFIG_INPUT_UINPUT=y
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+# 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 is not set
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# 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 is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_ISA is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Hardware Sensors Chip support
+#
+# CONFIG_I2C_SENSOR is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1031 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_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 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_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_SMSC47M1 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 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 is not set
+CONFIG_ISP1301_OMAP=y
+# 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 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_DNOTIFY=y
+# 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_FAT_DEFAULT_CODEPAGE=437
+# 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=2
+# CONFIG_JFFS2_FS_NAND is not set
+# CONFIG_JFFS2_FS_NOR_ECC is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+CONFIG_CRAMFS=y
+# 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=y
+# 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_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 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 is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# 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_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# 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=y
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+
+#
+# Advanced Linux Sound Architecture
+#
+# CONFIG_SND is not set
+
+#
+# Open Sound System
+#
+CONFIG_SOUND_PRIME=y
+# CONFIG_SOUND_BT878 is not set
+# CONFIG_SOUND_FUSION is not set
+# CONFIG_SOUND_CS4281 is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_TRIDENT is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_TVMIXER is not set
+# CONFIG_SOUND_AD1980 is not set
+
+#
+# Misc devices
+#
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_PXA2XX is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_SA1100 is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_OMAP=y
+CONFIG_USB_OMAP=y
+# CONFIG_USB_GADGET_DUALSPEED is not set
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=y
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+CONFIG_DEBUG_PREEMPT=y
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_FS is not set
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_USER=y
+# CONFIG_DEBUG_WAITQ is not set
+CONFIG_DEBUG_ERRORS=y
+CONFIG_DEBUG_LL=y
+# CONFIG_DEBUG_ICEDCC is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Hardware crypto devices
+#
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
diff --git a/arch/arm/configs/simpad_defconfig b/arch/arm/configs/simpad_defconfig
new file mode 100644 (file)
index 0000000..10effbc
--- /dev/null
@@ -0,0 +1,896 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.9-rc2
+# Thu Sep 16 15:42:43 2004
+#
+CONFIG_ARM=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_IOMAP=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="oe1"
+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=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KALLSYMS_EXTRA_PASS=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
+CONFIG_SHMEM=y
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODULE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 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 is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+CONFIG_ARCH_SA1100=y
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_VERSATILE_PB is not set
+# CONFIG_ARCH_IMX is not set
+
+#
+# SA11x0 Implementations
+#
+# CONFIG_SA1100_ASSABET is not set
+# CONFIG_SA1100_ADSBITSY is not set
+# CONFIG_SA1100_BRUTUS is not set
+# CONFIG_SA1100_CERF is not set
+# CONFIG_SA1100_COLLIE is not set
+# CONFIG_SA1100_H3100 is not set
+# CONFIG_SA1100_H3600 is not set
+# CONFIG_SA1100_H3800 is not set
+# CONFIG_SA1100_EXTENEX1 is not set
+# CONFIG_SA1100_FLEXANET is not set
+# CONFIG_SA1100_FREEBIRD is not set
+# CONFIG_SA1100_GRAPHICSCLIENT is not set
+# CONFIG_SA1100_GRAPHICSMASTER is not set
+# CONFIG_SA1100_BADGE4 is not set
+# CONFIG_SA1100_JORNADA720 is not set
+# CONFIG_SA1100_HACKKIT is not set
+# CONFIG_SA1100_HUW_WEBPANEL is not set
+# CONFIG_SA1100_ITSY is not set
+# CONFIG_SA1100_LART is not set
+# CONFIG_SA1100_NANOENGINE is not set
+# CONFIG_SA1100_OMNIMETER is not set
+# CONFIG_SA1100_PANGOLIN is not set
+# CONFIG_SA1100_PLEB is not set
+# CONFIG_SA1100_PT_SYSTEM3 is not set
+# CONFIG_SA1100_SHANNON is not set
+# CONFIG_SA1100_SHERMAN is not set
+CONFIG_SA1100_SIMPAD=y
+# CONFIG_SA1100_PFS168 is not set
+# CONFIG_SA1100_VICTOR is not set
+# CONFIG_SA1100_XP860 is not set
+# CONFIG_SA1100_YOPY is not set
+# CONFIG_SA1100_STORK is not set
+# CONFIG_SA1100_SSP is not set
+# CONFIG_SA1100_USB is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_SA1100=y
+CONFIG_CPU_32v4=y
+CONFIG_CPU_ABRT_EV4=y
+CONFIG_CPU_CACHE_V4WB=y
+CONFIG_CPU_TLB_V4WB=y
+CONFIG_CPU_MINICACHE=y
+
+#
+# Processor Features
+#
+
+#
+# General setup
+#
+CONFIG_DISCONTIGMEM=y
+CONFIG_ISA=y
+# CONFIG_ZBOOT_ROM is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+# CONFIG_CPU_FREQ is not set
+
+#
+# PCMCIA/CardBus support
+#
+CONFIG_PCMCIA=y
+# CONFIG_PCMCIA_DEBUG is not set
+# CONFIG_I82365 is not set
+# CONFIG_TCIC is not set
+CONFIG_PCMCIA_SA1100=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=m
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=m
+# CONFIG_DEBUG_DRIVER is not set
+CONFIG_PM=y
+CONFIG_PREEMPT=y
+CONFIG_APM=y
+# CONFIG_ARTHUR is not set
+CONFIG_CMDLINE="mtdparts=sa1100:512k(boot),1m(kernel),-(root) console=ttySA0 root=1f02 noinitrd mem=64M jffs2_orphaned_inodes=delete rootfstype=jffs2"
+CONFIG_LEDS=y
+CONFIG_LEDS_TIMER=y
+# CONFIG_LEDS_CPU is not set
+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=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# 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=y
+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_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+# CONFIG_MTD_CFI_I2 is not set
+# 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_CFI_UTIL=y
+CONFIG_MTD_RAM=y
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT 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_SA1100=y
+# CONFIG_MTD_EDB7312 is not set
+# CONFIG_MTD_IMPA7 is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM 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
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+CONFIG_BLK_DEV_RAM=m
+CONFIG_BLK_DEV_RAM_SIZE=8192
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+# 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=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_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_INET_TUNNEL 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_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=m
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+
+#
+# Old SIR device drivers
+#
+CONFIG_IRPORT_SIR=m
+
+#
+# Old Serial dongle support
+#
+# CONFIG_DONGLE_OLD is not set
+
+#
+# FIR device drivers
+#
+# CONFIG_NSC_FIR is not set
+# CONFIG_WINBOND_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+# CONFIG_ALI_FIR is not set
+CONFIG_SA1100_FIR=m
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+# CONFIG_BT_HIDP is not set
+
+#
+# Bluetooth device drivers
+#
+# CONFIG_BT_HCIUART is not set
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+# CONFIG_BT_HCIVHCI is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_SMC91X is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_PCI=y
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# 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
+# CONFIG_ARLAN is not set
+# CONFIG_WAVELAN is not set
+CONFIG_PCMCIA_WAVELAN=m
+# CONFIG_PCMCIA_NETWAVE is not set
+
+#
+# Wireless 802.11 Frequency Hopping cards support
+#
+# CONFIG_PCMCIA_RAYCS is not set
+
+#
+# Wireless 802.11b ISA/PCI cards support
+#
+# CONFIG_HERMES is not set
+# CONFIG_ATMEL is not set
+
+#
+# Wireless 802.11b Pcmcia/Cardbus cards support
+#
+CONFIG_AIRO_CS=m
+# CONFIG_PCMCIA_WL3501 is not set
+CONFIG_NET_WIRELESS=y
+
+#
+# PCMCIA network device support
+#
+CONFIG_NET_PCMCIA=y
+CONFIG_PCMCIA_3C589=m
+CONFIG_PCMCIA_3C574=m
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=m
+# CONFIG_PCMCIA_NMCLAN is not set
+CONFIG_PCMCIA_SMC91C92=m
+CONFIG_PCMCIA_XIRC2PS=m
+# CONFIG_PCMCIA_AXNET is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+# 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
+#
+
+#
+# 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=800
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=600
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_TSDEV=y
+CONFIG_INPUT_TSDEV_SCREEN_X=800
+CONFIG_INPUT_TSDEV_SCREEN_Y=600
+CONFIG_INPUT_EVDEV=m
+CONFIG_INPUT_EVBUG=y
+
+#
+# Input I/O drivers
+#
+# CONFIG_GAMEPORT is not set
+CONFIG_SOUND_GAMEPORT=y
+CONFIG_SERIO=m
+CONFIG_SERIO_SERPORT=m
+CONFIG_SERIO_CT82C710=m
+
+#
+# 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_SERIAL_SA1100=y
+CONFIG_SERIAL_SA1100_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# 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_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# 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=m
+# CONFIG_EXT2_FS_XATTR is not set
+CONFIG_EXT3_FS=m
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+CONFIG_JBD=m
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=m
+CONFIG_REISERFS_FS=m
+# CONFIG_REISERFS_CHECK is not set
+CONFIG_REISERFS_PROC_INFO=y
+# CONFIG_REISERFS_FS_XATTR 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=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG 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=m
+CONFIG_JFFS_FS_VERBOSE=0
+# CONFIG_JFFS_PROC_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS_NAND is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+CONFIG_CRAMFS=m
+# 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 is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+# CONFIG_EXPORTFS is not set
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT 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 is not set
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+CONFIG_NLS_CODEPAGE_850=y
+# 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_ASCII 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=y
+# 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=y
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_SA1100 is not set
+CONFIG_FB_MQ200=y
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# 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_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+
+#
+# Advanced Linux Sound Architecture
+#
+# CONFIG_SND is not set
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+
+#
+# Misc devices
+#
+
+#
+# USB support
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_USER=y
+# CONFIG_DEBUG_WAITQ is not set
+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_CRC_CCITT=m
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
new file mode 100644 (file)
index 0000000..ecc8c33
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ *  linux/arch/arm/kernel/smp.c
+ *
+ *  Copyright (C) 2002 ARM Limited, 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/cache.h>
+#include <linux/profile.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/seq_file.h>
+
+#include <asm/atomic.h>
+#include <asm/cacheflush.h>
+#include <asm/cpu.h>
+#include <asm/processor.h>
+#include <asm/tlbflush.h>
+#include <asm/ptrace.h>
+
+/*
+ * bitmask of present and online CPUs.
+ * The present bitmask indicates that the CPU is physically present.
+ * The online bitmask indicates that the CPU is up and running.
+ */
+cpumask_t cpu_present_mask;
+cpumask_t cpu_online_map;
+
+/*
+ * structures for inter-processor calls
+ * - A collection of single bit ipi messages.
+ */
+struct ipi_data {
+       spinlock_t lock;
+       unsigned long ipi_count;
+       unsigned long bits;
+};
+
+static DEFINE_PER_CPU(struct ipi_data, ipi_data) = {
+       .lock   = SPIN_LOCK_UNLOCKED,
+};
+
+enum ipi_msg_type {
+       IPI_TIMER,
+       IPI_RESCHEDULE,
+       IPI_CALL_FUNC,
+       IPI_CPU_STOP,
+};
+
+struct smp_call_struct {
+       void (*func)(void *info);
+       void *info;
+       int wait;
+       cpumask_t pending;
+       cpumask_t unfinished;
+};
+
+static struct smp_call_struct * volatile smp_call_function_data;
+static DEFINE_SPINLOCK(smp_call_function_lock);
+
+int __init __cpu_up(unsigned int cpu)
+{
+       struct task_struct *idle;
+       int ret;
+
+       /*
+        * Spawn a new process manually.  Grab a pointer to
+        * its task struct so we can mess with it
+        */
+       idle = fork_idle(cpu);
+       if (IS_ERR(idle)) {
+               printk(KERN_ERR "CPU%u: fork() failed\n", cpu);
+               return PTR_ERR(idle);
+       }
+
+       /*
+        * Now bring the CPU into our world.
+        */
+       ret = boot_secondary(cpu, idle);
+       if (ret) {
+               printk(KERN_CRIT "cpu_up: processor %d failed to boot\n", cpu);
+               /*
+                * FIXME: We need to clean up the new idle thread. --rmk
+                */
+       }
+
+       return ret;
+}
+
+/*
+ * Called by both boot and secondaries to move global data into
+ * per-processor storage.
+ */
+void __init smp_store_cpu_info(unsigned int cpuid)
+{
+       struct cpuinfo_arm *cpu_info = &per_cpu(cpu_data, cpuid);
+
+       cpu_info->loops_per_jiffy = loops_per_jiffy;
+}
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+       int cpu;
+       unsigned long bogosum = 0;
+
+       for_each_online_cpu(cpu)
+               bogosum += per_cpu(cpu_data, cpu).loops_per_jiffy;
+
+       printk(KERN_INFO "SMP: Total of %d processors activated "
+              "(%lu.%02lu BogoMIPS).\n",
+              num_online_cpus(),
+              bogosum / (500000/HZ),
+              (bogosum / (5000/HZ)) % 100);
+}
+
+void __init smp_prepare_boot_cpu(void)
+{
+       unsigned int cpu = smp_processor_id();
+
+       cpu_set(cpu, cpu_present_mask);
+       cpu_set(cpu, cpu_online_map);
+}
+
+static void send_ipi_message(cpumask_t callmap, enum ipi_msg_type msg)
+{
+       unsigned long flags;
+       unsigned int cpu;
+
+       local_irq_save(flags);
+
+       for_each_cpu_mask(cpu, callmap) {
+               struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
+
+               spin_lock(&ipi->lock);
+               ipi->bits |= 1 << msg;
+               spin_unlock(&ipi->lock);
+       }
+
+       /*
+        * Call the platform specific cross-CPU call function.
+        */
+       smp_cross_call(callmap);
+
+       local_irq_restore(flags);
+}
+
+/*
+ * You must not call this function with disabled interrupts, from a
+ * hardware interrupt handler, nor from a bottom half handler.
+ */
+int smp_call_function_on_cpu(void (*func)(void *info), void *info, int retry,
+                             int wait, cpumask_t callmap)
+{
+       struct smp_call_struct data;
+       unsigned long timeout;
+       int ret = 0;
+
+       data.func = func;
+       data.info = info;
+       data.wait = wait;
+
+       cpu_clear(smp_processor_id(), callmap);
+       if (cpus_empty(callmap))
+               goto out;
+
+       data.pending = callmap;
+       if (wait)
+               data.unfinished = callmap;
+
+       /*
+        * try to get the mutex on smp_call_function_data
+        */
+       spin_lock(&smp_call_function_lock);
+       smp_call_function_data = &data;
+
+       send_ipi_message(callmap, IPI_CALL_FUNC);
+
+       timeout = jiffies + HZ;
+       while (!cpus_empty(data.pending) && time_before(jiffies, timeout))
+               barrier();
+
+       /*
+        * did we time out?
+        */
+       if (!cpus_empty(data.pending)) {
+               /*
+                * this may be causing our panic - report it
+                */
+               printk(KERN_CRIT
+                      "CPU%u: smp_call_function timeout for %p(%p)\n"
+                      "      callmap %lx pending %lx, %swait\n",
+                      smp_processor_id(), func, info, callmap, data.pending,
+                      wait ? "" : "no ");
+
+               /*
+                * TRACE
+                */
+               timeout = jiffies + (5 * HZ);
+               while (!cpus_empty(data.pending) && time_before(jiffies, timeout))
+                       barrier();
+
+               if (cpus_empty(data.pending))
+                       printk(KERN_CRIT "     RESOLVED\n");
+               else
+                       printk(KERN_CRIT "     STILL STUCK\n");
+       }
+
+       /*
+        * whatever happened, we're done with the data, so release it
+        */
+       smp_call_function_data = NULL;
+       spin_unlock(&smp_call_function_lock);
+
+       if (!cpus_empty(data.pending)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       if (wait)
+               while (!cpus_empty(data.unfinished))
+                       barrier();
+ out:
+
+       return 0;
+}
+
+int smp_call_function(void (*func)(void *info), void *info, int retry,
+                      int wait)
+{
+       return smp_call_function_on_cpu(func, info, retry, wait,
+                                       cpu_online_map);
+}
+
+void show_ipi_list(struct seq_file *p)
+{
+       unsigned int cpu;
+
+       seq_puts(p, "IPI:");
+
+       for_each_online_cpu(cpu)
+               seq_printf(p, " %10lu", per_cpu(ipi_data, cpu).ipi_count);
+
+       seq_putc(p, '\n');
+}
+
+static void ipi_timer(struct pt_regs *regs)
+{
+       int user = user_mode(regs);
+
+       irq_enter();
+       profile_tick(CPU_PROFILING, regs);
+       update_process_times(user);
+       irq_exit();
+}
+
+/*
+ * ipi_call_function - handle IPI from smp_call_function()
+ *
+ * Note that we copy data out of the cross-call structure and then
+ * let the caller know that we're here and have done with their data
+ */
+static void ipi_call_function(unsigned int cpu)
+{
+       struct smp_call_struct *data = smp_call_function_data;
+       void (*func)(void *info) = data->func;
+       void *info = data->info;
+       int wait = data->wait;
+
+       cpu_clear(cpu, data->pending);
+
+       func(info);
+
+       if (wait)
+               cpu_clear(cpu, data->unfinished);
+}
+
+static DEFINE_SPINLOCK(stop_lock);
+
+/*
+ * ipi_cpu_stop - handle IPI from smp_send_stop()
+ */
+static void ipi_cpu_stop(unsigned int cpu)
+{
+       spin_lock(&stop_lock);
+       printk(KERN_CRIT "CPU%u: stopping\n", cpu);
+       dump_stack();
+       spin_unlock(&stop_lock);
+
+       cpu_clear(cpu, cpu_online_map);
+
+       local_fiq_disable();
+       local_irq_disable();
+
+       while (1)
+               cpu_relax();
+}
+
+/*
+ * Main handler for inter-processor interrupts
+ *
+ * For ARM, the ipimask now only identifies a single
+ * category of IPI (Bit 1 IPIs have been replaced by a
+ * different mechanism):
+ *
+ *  Bit 0 - Inter-processor function call
+ */
+void do_IPI(struct pt_regs *regs)
+{
+       unsigned int cpu = smp_processor_id();
+       struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
+
+       ipi->ipi_count++;
+
+       for (;;) {
+               unsigned long msgs;
+
+               spin_lock(&ipi->lock);
+               msgs = ipi->bits;
+               ipi->bits = 0;
+               spin_unlock(&ipi->lock);
+
+               if (!msgs)
+                       break;
+
+               do {
+                       unsigned nextmsg;
+
+                       nextmsg = msgs & -msgs;
+                       msgs &= ~nextmsg;
+                       nextmsg = ffz(~nextmsg);
+
+                       switch (nextmsg) {
+                       case IPI_TIMER:
+                               ipi_timer(regs);
+                               break;
+
+                       case IPI_RESCHEDULE:
+                               /*
+                                * nothing more to do - eveything is
+                                * done on the interrupt return path
+                                */
+                               break;
+
+                       case IPI_CALL_FUNC:
+                               ipi_call_function(cpu);
+                               break;
+
+                       case IPI_CPU_STOP:
+                               ipi_cpu_stop(cpu);
+                               break;
+
+                       default:
+                               printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n",
+                                      cpu, nextmsg);
+                               break;
+                       }
+               } while (msgs);
+       }
+}
+
+void smp_send_reschedule(int cpu)
+{
+       send_ipi_message(cpumask_of_cpu(cpu), IPI_RESCHEDULE);
+}
+
+void smp_send_timer(void)
+{
+       cpumask_t mask = cpu_online_map;
+       cpu_clear(smp_processor_id(), mask);
+       send_ipi_message(mask, IPI_TIMER);
+}
+
+void smp_send_stop(void)
+{
+       cpumask_t mask = cpu_online_map;
+       cpu_clear(smp_processor_id(), mask);
+       send_ipi_message(mask, IPI_CPU_STOP);
+}
+
+/*
+ * not supported here
+ */
+int __init setup_profiling_timer(unsigned int multiplier)
+{
+       return -EINVAL;
+}
diff --git a/arch/arm/lib/io-readsl.S b/arch/arm/lib/io-readsl.S
new file mode 100644 (file)
index 0000000..75a9121
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *  linux/arch/arm/lib/io-readsl.S
+ *
+ *  Copyright (C) 1995-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.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+ENTRY(__raw_readsl)
+               teq     r2, #0          @ do we have to check for the zero len?
+               moveq   pc, lr
+               ands    ip, r1, #3
+               bne     3f
+
+               subs    r2, r2, #4
+               bmi     2f
+               stmfd   sp!, {r4, lr}
+1:             ldr     r3, [r0, #0]
+               ldr     r4, [r0, #0]
+               ldr     ip, [r0, #0]
+               ldr     lr, [r0, #0]
+               subs    r2, r2, #4
+               stmia   r1!, {r3, r4, ip, lr}
+               bpl     1b
+               ldmfd   sp!, {r4, lr}
+2:             movs    r2, r2, lsl #31
+               ldrcs   r3, [r0, #0]
+               ldrcs   ip, [r0, #0]
+               stmcsia r1!, {r3, ip}
+               ldrne   r3, [r0, #0]
+               strne   r3, [r1, #0]
+               mov     pc, lr
+
+3:             ldr     r3, [r0]
+               cmp     ip, #2
+               mov     ip, r3, get_byte_0
+               strb    ip, [r1], #1
+               bgt     6f
+               mov     ip, r3, get_byte_1
+               strb    ip, [r1], #1
+               beq     5f
+               mov     ip, r3, get_byte_2
+               strb    ip, [r1], #1
+
+4:             subs    r2, r2, #1
+               mov     ip, r3, pull #24
+               ldrne   r3, [r0]
+               orrne   ip, ip, r3, push #8
+               strne   ip, [r1], #4
+               bne     4b
+               b       8f
+
+5:             subs    r2, r2, #1
+               mov     ip, r3, pull #16
+               ldrne   r3, [r0]
+               orrne   ip, ip, r3, push #16
+               strne   ip, [r1], #4
+               bne     5b
+               b       7f
+
+6:             subs    r2, r2, #1
+               mov     ip, r3, pull #8
+               ldrne   r3, [r0]
+               orrne   ip, ip, r3, push #24
+               strne   ip, [r1], #4
+               bne     6b
+
+               mov     r3, ip, get_byte_2
+               strb    r3, [r1, #2]
+7:             mov     r3, ip, get_byte_1
+               strb    r3, [r1, #1]
+8:             mov     r3, ip, get_byte_0
+               strb    r3, [r1, #0]
+               mov     pc, lr
diff --git a/arch/arm/mach-clps711x/Makefile.boot b/arch/arm/mach-clps711x/Makefile.boot
new file mode 100644 (file)
index 0000000..d3d2933
--- /dev/null
@@ -0,0 +1,7 @@
+# The standard locations for stuff on CLPS711x type processors
+   zreladdr-y                          := 0xc0028000 
+params_phys-y                          := 0xc0000100
+# Should probably have some agreement on these...
+initrd_phys-$(CONFIG_ARCH_P720T)       := 0xc0400000
+initrd_phys-$(CONFIG_ARCH_CDB89712)    := 0x00700000
+
diff --git a/arch/arm/mach-clps711x/common.h b/arch/arm/mach-clps711x/common.h
new file mode 100644 (file)
index 0000000..2b8b801
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * linux/arch/arm/mach-clps711x/common.h
+ *
+ * Common bits.
+ */
+
+struct sys_timer;
+
+extern void clps711x_map_io(void);
+extern void clps711x_init_irq(void);
+extern struct sys_timer clps711x_timer;
diff --git a/arch/arm/mach-clps7500/Makefile.boot b/arch/arm/mach-clps7500/Makefile.boot
new file mode 100644 (file)
index 0000000..fe16506
--- /dev/null
@@ -0,0 +1,2 @@
+   zreladdr-y  := 0x10008000
+
diff --git a/arch/arm/mach-ebsa110/Makefile.boot b/arch/arm/mach-ebsa110/Makefile.boot
new file mode 100644 (file)
index 0000000..2321260
--- /dev/null
@@ -0,0 +1,4 @@
+   zreladdr-y  := 0x00008000
+params_phys-y  := 0x00000400
+initrd_phys-y  := 0x00800000
+
diff --git a/arch/arm/mach-epxa10db/Makefile.boot b/arch/arm/mach-epxa10db/Makefile.boot
new file mode 100644 (file)
index 0000000..28bec7d
--- /dev/null
@@ -0,0 +1,2 @@
+   zreladdr-y  := 0x00008000
+
diff --git a/arch/arm/mach-footbridge/Makefile.boot b/arch/arm/mach-footbridge/Makefile.boot
new file mode 100644 (file)
index 0000000..c7e75ac
--- /dev/null
@@ -0,0 +1,4 @@
+   zreladdr-y  := 0x00008000
+params_phys-y  := 0x00000100
+initrd_phys-y  := 0x00800000
+
diff --git a/arch/arm/mach-footbridge/co285.c b/arch/arm/mach-footbridge/co285.c
new file mode 100644 (file)
index 0000000..e154191
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * linux/arch/arm/mach-footbridge/co285.c
+ *
+ * CO285 machine fixup
+ */
+#include <linux/init.h>
+
+#include <asm/hardware/dec21285.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+
+#include "common.h"
+
+static void __init
+fixup_coebsa285(struct machine_desc *desc, struct tag *tags,
+               char **cmdline, struct meminfo *mi)
+{
+       extern unsigned long boot_memory_end;
+       extern char boot_command_line[];
+
+       mi->nr_banks      = 1;
+       mi->bank[0].start = PHYS_OFFSET;
+       mi->bank[0].size  = boot_memory_end;
+       mi->bank[0].node  = 0;
+
+       *cmdline = boot_command_line;
+}
+
+MACHINE_START(CO285, "co-EBSA285")
+       MAINTAINER("Mark van Doesburg")
+       BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0x7cf00000)
+       FIXUP(fixup_coebsa285)
+       MAPIO(footbridge_map_io)
+       INITIRQ(footbridge_init_irq)
+       .timer          = &footbridge_timer,
+MACHINE_END
+
diff --git a/arch/arm/mach-footbridge/common.c b/arch/arm/mach-footbridge/common.c
new file mode 100644 (file)
index 0000000..eb8238c
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ *  linux/arch/arm/mach-footbridge/common.c
+ *
+ *  Copyright (C) 1998-2000 Russell King, Dave Gilbert.
+ *
+ * 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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/setup.h>
+#include <asm/hardware/dec21285.h>
+
+#include <asm/mach/irq.h>
+#include <asm/mach/map.h>
+
+#include "common.h"
+
+extern void __init isa_init_irq(unsigned int irq);
+
+unsigned int mem_fclk_21285 = 50000000;
+
+EXPORT_SYMBOL(mem_fclk_21285);
+
+static int __init parse_tag_memclk(const struct tag *tag)
+{
+       mem_fclk_21285 = tag->u.memclk.fmemclk;
+       return 0;
+}
+
+__tagtable(ATAG_MEMCLK, parse_tag_memclk);
+
+/*
+ * Footbridge IRQ translation table
+ *  Converts from our IRQ numbers into FootBridge masks
+ */
+static const int fb_irq_mask[] = {
+       IRQ_MASK_UART_RX,       /*  0 */
+       IRQ_MASK_UART_TX,       /*  1 */
+       IRQ_MASK_TIMER1,        /*  2 */
+       IRQ_MASK_TIMER2,        /*  3 */
+       IRQ_MASK_TIMER3,        /*  4 */
+       IRQ_MASK_IN0,           /*  5 */
+       IRQ_MASK_IN1,           /*  6 */
+       IRQ_MASK_IN2,           /*  7 */
+       IRQ_MASK_IN3,           /*  8 */
+       IRQ_MASK_DOORBELLHOST,  /*  9 */
+       IRQ_MASK_DMA1,          /* 10 */
+       IRQ_MASK_DMA2,          /* 11 */
+       IRQ_MASK_PCI,           /* 12 */
+       IRQ_MASK_SDRAMPARITY,   /* 13 */
+       IRQ_MASK_I2OINPOST,     /* 14 */
+       IRQ_MASK_PCI_ABORT,     /* 15 */
+       IRQ_MASK_PCI_SERR,      /* 16 */
+       IRQ_MASK_DISCARD_TIMER, /* 17 */
+       IRQ_MASK_PCI_DPERR,     /* 18 */
+       IRQ_MASK_PCI_PERR,      /* 19 */
+};
+
+static void fb_mask_irq(unsigned int irq)
+{
+       *CSR_IRQ_DISABLE = fb_irq_mask[_DC21285_INR(irq)];
+}
+
+static void fb_unmask_irq(unsigned int irq)
+{
+       *CSR_IRQ_ENABLE = fb_irq_mask[_DC21285_INR(irq)];
+}
+
+static struct irqchip fb_chip = {
+       .ack    = fb_mask_irq,
+       .mask   = fb_mask_irq,
+       .unmask = fb_unmask_irq,
+};
+
+static void __init __fb_init_irq(void)
+{
+       unsigned int irq;
+
+       /*
+        * setup DC21285 IRQs
+        */
+       *CSR_IRQ_DISABLE = -1;
+       *CSR_FIQ_DISABLE = -1;
+
+       for (irq = _DC21285_IRQ(0); irq < _DC21285_IRQ(20); irq++) {
+               set_irq_chip(irq, &fb_chip);
+               set_irq_handler(irq, do_level_IRQ);
+               set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+       }
+}
+
+void __init footbridge_init_irq(void)
+{
+       __fb_init_irq();
+
+       if (!footbridge_cfn_mode())
+               return;
+
+       if (machine_is_ebsa285())
+               /* The following is dependent on which slot
+                * you plug the Southbridge card into.  We
+                * currently assume that you plug it into
+                * the right-hand most slot.
+                */
+               isa_init_irq(IRQ_PCI);
+
+       if (machine_is_cats())
+               isa_init_irq(IRQ_IN2);
+
+       if (machine_is_netwinder())
+               isa_init_irq(IRQ_IN3);
+}
+
+/*
+ * Common mapping for all systems.  Note that the outbound write flush is
+ * commented out since there is a "No Fix" problem with it.  Not mapping
+ * it means that we have extra bullet protection on our feet.
+ */
+static struct map_desc fb_common_io_desc[] __initdata = {
+ { ARMCSR_BASE,         DC21285_ARMCSR_BASE,       ARMCSR_SIZE,  MT_DEVICE },
+ { XBUS_BASE,    0x40000000,               XBUS_SIZE,    MT_DEVICE }
+};
+
+/*
+ * The mapping when the footbridge is in host mode.  We don't map any of
+ * this when we are in add-in mode.
+ */
+static struct map_desc ebsa285_host_io_desc[] __initdata = {
+#if defined(CONFIG_ARCH_FOOTBRIDGE) && defined(CONFIG_FOOTBRIDGE_HOST)
+ { PCIMEM_BASE,  DC21285_PCI_MEM,          PCIMEM_SIZE,  MT_DEVICE },
+ { PCICFG0_BASE, DC21285_PCI_TYPE_0_CONFIG, PCICFG0_SIZE, MT_DEVICE },
+ { PCICFG1_BASE, DC21285_PCI_TYPE_1_CONFIG, PCICFG1_SIZE, MT_DEVICE },
+ { PCIIACK_BASE, DC21285_PCI_IACK,         PCIIACK_SIZE, MT_DEVICE },
+ { PCIO_BASE,    DC21285_PCI_IO,           PCIO_SIZE,    MT_DEVICE }
+#endif
+};
+
+/*
+ * The CO-ebsa285 mapping.
+ */
+static struct map_desc co285_io_desc[] __initdata = {
+#ifdef CONFIG_ARCH_CO285
+ { PCIO_BASE,   DC21285_PCI_IO,            PCIO_SIZE,    MT_DEVICE },
+ { PCIMEM_BASE,         DC21285_PCI_MEM,           PCIMEM_SIZE,  MT_DEVICE }
+#endif
+};
+
+void __init footbridge_map_io(void)
+{
+       /*
+        * Set up the common mapping first; we need this to
+        * determine whether we're in host mode or not.
+        */
+       iotable_init(fb_common_io_desc, ARRAY_SIZE(fb_common_io_desc));
+
+       /*
+        * Now, work out what we've got to map in addition on this
+        * platform.
+        */
+       if (machine_is_co285())
+               iotable_init(co285_io_desc, ARRAY_SIZE(co285_io_desc));
+       if (footbridge_cfn_mode())
+               iotable_init(ebsa285_host_io_desc, ARRAY_SIZE(ebsa285_host_io_desc));
+}
+
+#ifdef CONFIG_FOOTBRIDGE_ADDIN
+
+/*
+ * These two functions convert virtual addresses to PCI addresses and PCI
+ * addresses to virtual addresses.  Note that it is only legal to use these
+ * on memory obtained via get_zeroed_page or kmalloc.
+ */
+unsigned long __virt_to_bus(unsigned long res)
+{
+       WARN_ON(res < PAGE_OFFSET || res >= (unsigned long)high_memory);
+
+       return (res - PAGE_OFFSET) + (*CSR_PCISDRAMBASE & 0xfffffff0);
+}
+EXPORT_SYMBOL(__virt_to_bus);
+
+unsigned long __bus_to_virt(unsigned long res)
+{
+       res -= (*CSR_PCISDRAMBASE & 0xfffffff0);
+       res += PAGE_OFFSET;
+
+       WARN_ON(res < PAGE_OFFSET || res >= (unsigned long)high_memory);
+
+       return res;
+}
+EXPORT_SYMBOL(__bus_to_virt);
+
+#endif
diff --git a/arch/arm/mach-footbridge/common.h b/arch/arm/mach-footbridge/common.h
new file mode 100644 (file)
index 0000000..580e31b
--- /dev/null
@@ -0,0 +1,9 @@
+
+extern struct sys_timer footbridge_timer;
+extern struct sys_timer isa_timer;
+
+extern void isa_rtc_init(void);
+
+extern void footbridge_map_io(void);
+extern void footbridge_init_irq(void);
+
diff --git a/arch/arm/mach-footbridge/dc21285-timer.c b/arch/arm/mach-footbridge/dc21285-timer.c
new file mode 100644 (file)
index 0000000..580e1d4
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *  linux/arch/arm/mach-footbridge/dc21285-timer.c
+ *
+ *  Copyright (C) 1998 Russell King.
+ *  Copyright (C) 1998 Phil Blundell
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+
+#include <asm/hardware/dec21285.h>
+#include <asm/mach/time.h>
+
+#include "common.h"
+
+/*
+ * Footbridge timer 1 support.
+ */
+static unsigned long timer1_latch;
+
+static unsigned long timer1_gettimeoffset (void)
+{
+       unsigned long value = timer1_latch - *CSR_TIMER1_VALUE;
+
+       return ((tick_nsec / 1000) * value) / timer1_latch;
+}
+
+static irqreturn_t
+timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       write_seqlock(&xtime_lock);
+
+       *CSR_TIMER1_CLR = 0;
+
+       timer_tick(regs);
+
+       write_sequnlock(&xtime_lock);
+
+       return IRQ_HANDLED;
+}
+
+static struct irqaction footbridge_timer_irq = {
+       .name           = "Timer1 timer tick",
+       .handler        = timer1_interrupt,
+       .flags          = SA_INTERRUPT,
+};
+
+/*
+ * Set up timer interrupt.
+ */
+static void __init footbridge_timer_init(void)
+{
+       isa_rtc_init();
+
+       timer1_latch = (mem_fclk_21285 + 8 * HZ) / (16 * HZ);
+
+       *CSR_TIMER1_CLR  = 0;
+       *CSR_TIMER1_LOAD = timer1_latch;
+       *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD | TIMER_CNTL_DIV16;
+
+       setup_irq(IRQ_TIMER1, &footbridge_timer_irq);
+}
+
+struct sys_timer footbridge_timer = {
+       .init           = footbridge_timer_init,
+       .offset         = timer1_gettimeoffset,
+};
diff --git a/arch/arm/mach-footbridge/ebsa285.c b/arch/arm/mach-footbridge/ebsa285.c
new file mode 100644 (file)
index 0000000..d0931f5
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * linux/arch/arm/mach-footbridge/ebsa285.c
+ *
+ * EBSA285 machine fixup
+ */
+#include <linux/init.h>
+
+#include <asm/hardware/dec21285.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+
+#include "common.h"
+
+MACHINE_START(EBSA285, "EBSA285")
+       MAINTAINER("Russell King")
+       BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0xfe000000)
+       BOOT_PARAMS(0x00000100)
+       VIDEO(0x000a0000, 0x000bffff)
+       MAPIO(footbridge_map_io)
+       INITIRQ(footbridge_init_irq)
+       .timer          = &footbridge_timer,
+MACHINE_END
+
diff --git a/arch/arm/mach-footbridge/isa-timer.c b/arch/arm/mach-footbridge/isa-timer.c
new file mode 100644 (file)
index 0000000..a4fefa0
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ *  linux/arch/arm/mach-footbridge/isa-timer.c
+ *
+ *  Copyright (C) 1998 Russell King.
+ *  Copyright (C) 1998 Phil Blundell
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/mach/time.h>
+
+#include "common.h"
+
+/*
+ * ISA timer tick support
+ */
+#define mSEC_10_from_14 ((14318180 + 100) / 200)
+
+static unsigned long isa_gettimeoffset(void)
+{
+       int count;
+
+       static int count_p = (mSEC_10_from_14/6);    /* for the first call after boot */
+       static unsigned long jiffies_p = 0;
+
+       /*
+        * cache volatile jiffies temporarily; we have IRQs turned off. 
+        */
+       unsigned long jiffies_t;
+
+       /* timer count may underflow right here */
+       outb_p(0x00, 0x43);     /* latch the count ASAP */
+
+       count = inb_p(0x40);    /* read the latched count */
+
+       /*
+        * We do this guaranteed double memory access instead of a _p 
+        * postfix in the previous port access. Wheee, hackady hack
+        */
+       jiffies_t = jiffies;
+
+       count |= inb_p(0x40) << 8;
+
+       /* Detect timer underflows.  If we haven't had a timer tick since 
+          the last time we were called, and time is apparently going
+          backwards, the counter must have wrapped during this routine. */
+       if ((jiffies_t == jiffies_p) && (count > count_p))
+               count -= (mSEC_10_from_14/6);
+       else
+               jiffies_p = jiffies_t;
+
+       count_p = count;
+
+       count = (((mSEC_10_from_14/6)-1) - count) * (tick_nsec / 1000);
+       count = (count + (mSEC_10_from_14/6)/2) / (mSEC_10_from_14/6);
+
+       return count;
+}
+
+static irqreturn_t
+isa_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       write_seqlock(&xtime_lock);
+       timer_tick(regs);
+       write_sequnlock(&xtime_lock);
+       return IRQ_HANDLED;
+}
+
+static struct irqaction isa_timer_irq = {
+       .name           = "ISA timer tick",
+       .handler        = isa_timer_interrupt,
+       .flags          = SA_INTERRUPT,
+};
+
+static void __init isa_timer_init(void)
+{
+       isa_rtc_init();
+
+       /* enable PIT timer */
+       /* set for periodic (4) and LSB/MSB write (0x30) */
+       outb(0x34, 0x43);
+       outb((mSEC_10_from_14/6) & 0xFF, 0x40);
+       outb((mSEC_10_from_14/6) >> 8, 0x40);
+
+       setup_irq(IRQ_ISA_TIMER, &isa_timer_irq);
+}
+
+struct sys_timer isa_timer = {
+       .init           = isa_timer_init,
+       .offset         = isa_gettimeoffset,
+};
diff --git a/arch/arm/mach-footbridge/isa.c b/arch/arm/mach-footbridge/isa.c
new file mode 100644 (file)
index 0000000..aa3a1fe
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  linux/arch/arm/mach-footbridge/isa.c
+ *
+ *  Copyright (C) 2004 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.
+ */
+#include <linux/init.h>
+#include <linux/serial_8250.h>
+
+#include <asm/irq.h>
+
+static struct plat_serial8250_port serial_platform_data[] = {
+       {
+               .iobase         = 0x3f8,
+               .irq            = IRQ_ISA_UART,
+               .uartclk        = 1843200,
+               .regshift       = 0,
+               .iotype         = UPIO_PORT,
+               .flags          = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+       },
+       {
+               .iobase         = 0x2f8,
+               .irq            = IRQ_ISA_UART2,
+               .uartclk        = 1843200,
+               .regshift       = 0,
+               .iotype         = UPIO_PORT,
+               .flags          = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+       },
+       { },
+};
+
+static struct platform_device serial_device = {
+       .name                   = "serial8250",
+       .id                     = 0,
+       .dev                    = {
+               .platform_data  = serial_platform_data,
+       },
+};
+
+static int __init footbridge_isa_init(void)
+{
+       return platform_device_register(&serial_device);
+}
+
+arch_initcall(footbridge_isa_init);
diff --git a/arch/arm/mach-footbridge/personal.c b/arch/arm/mach-footbridge/personal.c
new file mode 100644 (file)
index 0000000..415086d
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * linux/arch/arm/mach-footbridge/personal.c
+ *
+ * Personal server (Skiff) machine fixup
+ */
+#include <linux/init.h>
+
+#include <asm/hardware/dec21285.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+
+#include "common.h"
+
+MACHINE_START(PERSONAL_SERVER, "Compaq-PersonalServer")
+       MAINTAINER("Jamey Hicks / George France")
+       BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0xfe000000)
+       BOOT_PARAMS(0x00000100)
+       MAPIO(footbridge_map_io)
+       INITIRQ(footbridge_init_irq)
+       .timer          = &footbridge_timer,
+MACHINE_END
+
diff --git a/arch/arm/mach-h720x/Makefile.boot b/arch/arm/mach-h720x/Makefile.boot
new file mode 100644 (file)
index 0000000..5298401
--- /dev/null
@@ -0,0 +1,2 @@
+   zreladdr-$(CONFIG_ARCH_H720X)       := 0x40008000
+
diff --git a/arch/arm/mach-h720x/common.h b/arch/arm/mach-h720x/common.h
new file mode 100644 (file)
index 0000000..d8798db
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * linux/arch/arm/mach-h720x/common.h
+ *
+ * Copyright (C) 2003 Thomas Gleixner <tglx@linutronix.de>
+ *               2003 Robert Schwebel <r.schwebel@pengutronix.de>
+ *               2004 Sascha Hauer    <s.hauer@pengutronix.de>
+ *
+ * Architecture specific stuff for Hynix GMS30C7201 development 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.
+ *
+ */
+
+extern unsigned long h720x_gettimeoffset(void);
+extern void __init h720x_init_irq (void);
+extern void __init h720x_map_io(void);
+
+#ifdef CONFIG_ARCH_H7202
+extern struct sys_timer h7202_timer;
+extern void __init init_hw_h7202(void);
+extern void __init h7202_init_irq (void);
+extern void __init h7202_init_time(void);
+#endif
+
+#ifdef CONFIG_ARCH_H7201
+extern struct sys_timer h7201_timer;
+#endif
diff --git a/arch/arm/mach-imx/Makefile.boot b/arch/arm/mach-imx/Makefile.boot
new file mode 100644 (file)
index 0000000..fd72ce5
--- /dev/null
@@ -0,0 +1,2 @@
+    zreladdr-$(CONFIG_ARCH_MX1ADS)     := 0x08008000
+
diff --git a/arch/arm/mach-integrator/Makefile.boot b/arch/arm/mach-integrator/Makefile.boot
new file mode 100644 (file)
index 0000000..c7e75ac
--- /dev/null
@@ -0,0 +1,4 @@
+   zreladdr-y  := 0x00008000
+params_phys-y  := 0x00000100
+initrd_phys-y  := 0x00800000
+
diff --git a/arch/arm/mach-integrator/common.h b/arch/arm/mach-integrator/common.h
new file mode 100644 (file)
index 0000000..609c49d
--- /dev/null
@@ -0,0 +1,2 @@
+extern void integrator_time_init(unsigned long, unsigned int);
+extern unsigned long integrator_gettimeoffset(void);
diff --git a/arch/arm/mach-iop3xx/Makefile.boot b/arch/arm/mach-iop3xx/Makefile.boot
new file mode 100644 (file)
index 0000000..6387aa2
--- /dev/null
@@ -0,0 +1,9 @@
+   zreladdr-y  := 0xa0008000
+params_phys-y  := 0xa0000100
+initrd_phys-y  := 0xa0800000
+ifeq ($(CONFIG_ARCH_IOP331),y)
+   zreladdr-y  := 0x00008000
+params_phys-y  := 0x00000100
+initrd_phys-y  := 0x00800000
+endif
+
diff --git a/arch/arm/mach-iop3xx/iq80332-mm.c b/arch/arm/mach-iop3xx/iq80332-mm.c
new file mode 100644 (file)
index 0000000..084afcd
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * linux/arch/arm/mach-iop3xx/mm.c
+ *
+ * Low level memory initialization for iq80332 platform
+ *
+ * Author: Dave Jiang <dave.jiang@intel.com>
+ * Copyright (C) 2004 Intel Corp.
+ *
+ * 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 <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+
+
+/*
+ * IQ80332 specific IO mappings
+ *
+ * We use RedBoot's setup for the onboard devices.
+ */
+
+void __init iq80332_map_io(void)
+{
+       iop331_map_io();
+}
diff --git a/arch/arm/mach-iop3xx/iq80332-pci.c b/arch/arm/mach-iop3xx/iq80332-pci.c
new file mode 100644 (file)
index 0000000..b9807aa
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * arch/arm/mach-iop3xx/iq80332-pci.c
+ *
+ * PCI support for the Intel IQ80332 reference board
+ *
+ * Author: Dave Jiang <dave.jiang@intel.com>
+ * Copyright (C) 2004 Intel Corp.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach/pci.h>
+#include <asm/mach-types.h>
+
+/*
+ * The following macro is used to lookup irqs in a standard table
+ * format for those systems that do not already have PCI
+ * interrupts properly routed.  We assume 1 <= pin <= 4
+ */
+#define PCI_IRQ_TABLE_LOOKUP(minid,maxid)      \
+({ int _ctl_ = -1;                             \
+   unsigned int _idsel = idsel - minid;                \
+   if (_idsel <= maxid)                                \
+      _ctl_ = pci_irq_table[_idsel][pin-1];    \
+   _ctl_; })
+
+#define INTA   IRQ_IQ80332_INTA
+#define INTB   IRQ_IQ80332_INTB
+#define INTC   IRQ_IQ80332_INTC
+#define INTD   IRQ_IQ80332_INTD
+
+//#define INTE IRQ_IQ80332_I82544
+
+static inline int __init
+iq80332_map_irq(struct pci_dev *dev, u8 idsel, u8 pin)
+{
+       static int pci_irq_table[][8] = {
+               /*
+                * PCI IDSEL/INTPIN->INTLINE
+                * A       B       C       D
+                */
+               {-1,   -1,   -1,   -1},
+               {-1,   -1,   -1,   -1},
+               {-1,   -1,   -1,   -1},
+               {INTA, INTB, INTC, INTD}, /* PCI-X Slot */
+               {-1,   -1,   -1,   -1},
+               {INTC, INTC, INTC, INTC}, /* GigE  */
+               {-1,   -1,   -1,   -1},
+               {-1,   -1,   -1,   -1},
+       };
+
+       BUG_ON(pin < 1 || pin > 4);
+
+       return PCI_IRQ_TABLE_LOOKUP(1, 7);
+}
+
+static int iq80332_setup(int nr, struct pci_sys_data *sys)
+{
+       struct resource *res;
+
+       if(nr != 0)
+               return 0;
+
+       res = kmalloc(sizeof(struct resource) * 2, GFP_KERNEL);
+       if (!res)
+               panic("PCI: unable to alloc resources");
+
+       memset(res, 0, sizeof(struct resource) * 2);
+
+       res[0].start = IOP331_PCI_LOWER_IO_VA;
+       res[0].end   = IOP331_PCI_UPPER_IO_VA;
+       res[0].name  = "IQ80332 PCI I/O Space";
+       res[0].flags = IORESOURCE_IO;
+
+       res[1].start = IOP331_PCI_LOWER_MEM_PA;
+       res[1].end   = IOP331_PCI_UPPER_MEM_PA;
+       res[1].name  = "IQ80332 PCI Memory Space";
+       res[1].flags = IORESOURCE_MEM;
+
+       request_resource(&ioport_resource, &res[0]);
+       request_resource(&iomem_resource, &res[1]);
+
+       sys->mem_offset = IOP331_PCI_MEM_OFFSET;
+       sys->io_offset  = IOP331_PCI_IO_OFFSET;
+
+       sys->resource[0] = &res[0];
+       sys->resource[1] = &res[1];
+       sys->resource[2] = NULL;
+
+       return 1;
+}
+
+static void iq80332_preinit(void)
+{
+       iop331_init();
+}
+
+static struct hw_pci iq80332_pci __initdata = {
+       .swizzle        = pci_std_swizzle,
+       .nr_controllers = 1,
+       .setup          = iq80332_setup,
+       .scan           = iop331_scan_bus,
+       .preinit        = iq80332_preinit,
+       .map_irq        = iq80332_map_irq
+};
+
+static int __init iq80332_pci_init(void)
+{
+       if (machine_is_iq80332())
+               pci_common_init(&iq80332_pci);
+       return 0;
+}
+
+subsys_initcall(iq80332_pci_init);
+
+
+
+
diff --git a/arch/arm/mach-ixp2000/Makefile.boot b/arch/arm/mach-ixp2000/Makefile.boot
new file mode 100644 (file)
index 0000000..d84c580
--- /dev/null
@@ -0,0 +1,3 @@
+   zreladdr-y  := 0x00008000
+params_phys-y  := 0x00000100
+
diff --git a/arch/arm/mach-ixp4xx/Makefile.boot b/arch/arm/mach-ixp4xx/Makefile.boot
new file mode 100644 (file)
index 0000000..d84c580
--- /dev/null
@@ -0,0 +1,3 @@
+   zreladdr-y  := 0x00008000
+params_phys-y  := 0x00000100
+
diff --git a/arch/arm/mach-ixp4xx/gtwx5715-pci.c b/arch/arm/mach-ixp4xx/gtwx5715-pci.c
new file mode 100644 (file)
index 0000000..b180358
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * arch/arm/mach-ixp4xx/gtwx5715-pci.c
+ *
+ * Gemtek GTWX5715 (Linksys WRV54G) board setup
+ *
+ * Copyright (C) 2004 George T. Joseph
+ * Derived from Coyote
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/arch/gtwx5715.h>
+#include <asm/mach/pci.h>
+
+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);
+
+        /*
+        * The exact GPIO pins and IRQs are defined in arch-ixp4xx/gtwx5715.h
+        * Slot 0 isn't actually populated with a card connector but
+        * we initialize it anyway in case a future version has the
+        * slot populated or someone with good soldering skills has
+        * some free time.
+        */
+
+
+static void gtwx5715_init_gpio(u8 pin, u32 style)
+{
+       gpio_line_config(pin, style | IXP4XX_GPIO_ACTIVE_LOW);
+
+       if (style & IXP4XX_GPIO_IN) gpio_line_isr_clear(pin);
+}
+
+void __init gtwx5715_pci_preinit(void)
+{
+       gtwx5715_init_gpio(GTWX5715_PCI_SLOT0_INTA_GPIO,        IXP4XX_GPIO_IN);
+       gtwx5715_init_gpio(GTWX5715_PCI_SLOT1_INTA_GPIO,        IXP4XX_GPIO_IN);
+
+       ixp4xx_pci_preinit();
+}
+
+
+static int __init gtwx5715_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+       int rc;
+       static int gtwx5715_irqmap
+                       [GTWX5715_PCI_SLOT_COUNT]
+                       [GTWX5715_PCI_INT_PIN_COUNT] = {
+       {GTWX5715_PCI_SLOT0_INTA_IRQ, GTWX5715_PCI_SLOT0_INTB_IRQ},
+       {GTWX5715_PCI_SLOT1_INTA_IRQ, GTWX5715_PCI_SLOT1_INTB_IRQ},
+};
+
+       if (slot >= GTWX5715_PCI_SLOT_COUNT ||
+                       pin >= GTWX5715_PCI_INT_PIN_COUNT) rc = -1;
+       else
+               rc = gtwx5715_irqmap[slot][pin-1];
+
+       printk("%s: Mapped slot %d pin %d to IRQ %d\n", __FUNCTION__,slot, pin, rc);
+       return(rc);
+}
+
+struct hw_pci gtwx5715_pci __initdata = {
+       .nr_controllers = 1,
+       .preinit =        gtwx5715_pci_preinit,
+       .swizzle =        pci_std_swizzle,
+       .setup =          ixp4xx_setup,
+       .scan =           ixp4xx_scan_bus,
+       .map_irq =        gtwx5715_map_irq,
+};
+
+int __init gtwx5715_pci_init(void)
+{
+       if (machine_is_gtwx5715())
+       {
+               pci_common_init(&gtwx5715_pci);
+       }
+
+       return 0;
+}
+
+subsys_initcall(gtwx5715_pci_init);
diff --git a/arch/arm/mach-ixp4xx/gtwx5715-setup.c b/arch/arm/mach-ixp4xx/gtwx5715-setup.c
new file mode 100644 (file)
index 0000000..e77c86e
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * arch/arm/mach-ixp4xx/gtwx5715-setup.c
+ *
+ * Gemtek GTWX5715 (Linksys WRV54G) board settup
+ *
+ * Copyright (C) 2004 George T. Joseph
+ * Derived from Coyote
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/serial_8250.h>
+
+#include <asm/types.h>
+#include <asm/setup.h>
+#include <asm/memory.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/arch/gtwx5715.h>
+
+/*
+ * Xscale UART registers are 32 bits wide with only the least
+ * significant 8 bits having any meaning.  From a configuration
+ * perspective, this means 2 things...
+ *
+ *   Setting .regshift = 2 so that the standard 16550 registers
+ *   line up on every 4th byte.
+ *
+ *   Shifting the register start virtual address +3 bytes when
+ *   compiled big-endian.  Since register writes are done on a
+ *   single byte basis, if the shift isn't done the driver will
+ *   write the value into the most significant byte of the register,
+ *   which is ignored, instead of the least significant.
+ */
+
+#ifdef __ARMEB__
+#define        REG_OFFSET      3
+#else
+#define        REG_OFFSET      0
+#endif
+
+/*
+ * Only the second or "console" uart is connected on the gtwx5715.
+ */
+
+static struct resource gtwx5715_uart_resources[] = {
+       {
+               .start  = IXP4XX_UART2_BASE_PHYS,
+               .end    = IXP4XX_UART2_BASE_PHYS + 0x0fff,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .start  = IRQ_IXP4XX_UART2,
+               .end    = IRQ_IXP4XX_UART2,
+               .flags  = IORESOURCE_IRQ,
+       },
+       { },
+};
+
+
+static struct plat_serial8250_port gtwx5715_uart_platform_data[] = {
+       {
+       .mapbase        = IXP4XX_UART2_BASE_PHYS,
+       .membase        = (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET,
+       .irq            = IRQ_IXP4XX_UART2,
+       .flags          = UPF_BOOT_AUTOCONF,
+       .iotype         = UPIO_MEM,
+       .regshift       = 2,
+       .uartclk        = IXP4XX_UART_XTAL,
+       },
+       { },
+};
+
+static struct platform_device gtwx5715_uart_device = {
+       .name           = "serial8250",
+       .id             = 0,
+       .dev                    = {
+               .platform_data  = gtwx5715_uart_platform_data,
+       },
+       .num_resources  = 2,
+       .resource       = gtwx5715_uart_resources,
+};
+
+
+void __init gtwx5715_map_io(void)
+{
+       ixp4xx_map_io();
+}
+
+static struct flash_platform_data gtwx5715_flash_data = {
+       .map_name       = "cfi_probe",
+       .width          = 2,
+};
+
+static struct resource gtwx5715_flash_resource = {
+       .start          = GTWX5715_FLASH_BASE,
+       .end            = GTWX5715_FLASH_BASE + GTWX5715_FLASH_SIZE,
+       .flags          = IORESOURCE_MEM,
+};
+
+static struct platform_device gtwx5715_flash = {
+       .name           = "IXP4XX-Flash",
+       .id             = 0,
+       .dev            = {
+               .platform_data = &gtwx5715_flash_data,
+       },
+       .num_resources  = 1,
+       .resource       = &gtwx5715_flash_resource,
+};
+
+static struct platform_device *gtwx5715_devices[] __initdata = {
+       &gtwx5715_uart_device,
+       &gtwx5715_flash,
+};
+
+static void __init gtwx5715_init(void)
+{
+       platform_add_devices(gtwx5715_devices, ARRAY_SIZE(gtwx5715_devices));
+}
+
+
+MACHINE_START(GTWX5715, "Gemtek GTWX5715 (Linksys WRV54G)")
+        MAINTAINER("George Joseph")
+        BOOT_MEM(PHYS_OFFSET, IXP4XX_UART2_BASE_PHYS,
+                IXP4XX_UART2_BASE_VIRT)
+        MAPIO(gtwx5715_map_io)
+        INITIRQ(ixp4xx_init_irq)
+                 .timer                = &ixp4xx_timer,
+        BOOT_PARAMS(0x0100)
+        INIT_MACHINE(gtwx5715_init)
+MACHINE_END
+
+
diff --git a/arch/arm/mach-ixp4xx/ixdpg425-pci.c b/arch/arm/mach-ixp4xx/ixdpg425-pci.c
new file mode 100644 (file)
index 0000000..0f90433
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * arch/arch/mach-ixp4xx/ixdpg425-pci.c
+ *
+ * PCI setup routines for Intel IXDPG425 Platform
+ *
+ * Copyright (C) 2004 MontaVista Softwrae, Inc.
+ *
+ * Maintainer: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * 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 <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+
+#include <asm/mach/pci.h>
+
+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 ixdpg425_pci_preinit(void)
+{
+       gpio_line_config(6, IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
+       gpio_line_config(7, IXP4XX_GPIO_IN | IXP4XX_GPIO_ACTIVE_LOW);
+
+       gpio_line_isr_clear(6);
+       gpio_line_isr_clear(7);
+
+       ixp4xx_pci_preinit();
+}
+
+static int __init ixdpg425_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+       if (slot == 12 || slot == 13)
+               return IRQ_IXP4XX_GPIO7;
+       else if (slot == 14)
+               return IRQ_IXP4XX_GPIO6;
+       else return -1;
+}
+
+struct hw_pci ixdpg425_pci __initdata = {
+       .nr_controllers = 1,
+       .preinit =        ixdpg425_pci_preinit,
+       .swizzle =        pci_std_swizzle,
+       .setup =          ixp4xx_setup,
+       .scan =           ixp4xx_scan_bus,
+       .map_irq =        ixdpg425_map_irq,
+};
+
+int __init ixdpg425_pci_init(void)
+{
+       if (machine_is_ixdpg425())
+               pci_common_init(&ixdpg425_pci);
+       return 0;
+}
+
+subsys_initcall(ixdpg425_pci_init);
diff --git a/arch/arm/mach-l7200/Makefile.boot b/arch/arm/mach-l7200/Makefile.boot
new file mode 100644 (file)
index 0000000..6c72ecb
--- /dev/null
@@ -0,0 +1,2 @@
+   zreladdr-y  := 0xf0008000
+
diff --git a/arch/arm/mach-lh7a40x/Makefile.boot b/arch/arm/mach-lh7a40x/Makefile.boot
new file mode 100644 (file)
index 0000000..af941be
--- /dev/null
@@ -0,0 +1,4 @@
+   zreladdr-y  := 0xc0008000
+params_phys-y  := 0xc0000100
+initrd_phys-y  := 0xc4000000
+
diff --git a/arch/arm/mach-lh7a40x/common.h b/arch/arm/mach-lh7a40x/common.h
new file mode 100644 (file)
index 0000000..05564ec
--- /dev/null
@@ -0,0 +1,14 @@
+/* arch/arm/mach-lh7a40x/common.h
+ *
+ *  Copyright (C) 2004 Marc Singer
+ *
+ *  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.
+ *
+ */
+
+extern struct sys_timer lh7a40x_timer;
+
+extern void lh7a400_init_irq (void);
+extern void lh7a404_init_irq (void);
diff --git a/arch/arm/mach-omap/Makefile.boot b/arch/arm/mach-omap/Makefile.boot
new file mode 100644 (file)
index 0000000..fee1a6a
--- /dev/null
@@ -0,0 +1,4 @@
+   zreladdr-y          := 0x10008000
+params_phys-y          := 0x10000100
+initrd_phys-y          := 0x10800000
+
diff --git a/arch/arm/mach-omap/clock.c b/arch/arm/mach-omap/clock.c
new file mode 100644 (file)
index 0000000..39d8503
--- /dev/null
@@ -0,0 +1,955 @@
+/*
+ *  linux/arch/arm/mach-omap/clock.c
+ *
+ *  Copyright (C) 2004 Nokia corporation
+ *  Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+
+#include <asm/semaphore.h>
+#include <asm/hardware/clock.h>
+#include <asm/arch/board.h>
+
+#include "clock.h"
+
+static LIST_HEAD(clocks);
+static DECLARE_MUTEX(clocks_sem);
+static spinlock_t clockfw_lock = SPIN_LOCK_UNLOCKED;
+static void propagate_rate(struct clk *  clk);
+/* MPU virtual clock functions */
+static int select_table_rate(unsigned long rate);
+static long round_to_table_rate(unsigned long rate);
+void clk_setdpll(__u16, __u16);
+
+struct mpu_rate rate_table[] = {
+       /* MPU MHz, xtal MHz, dpll1 MHz, CKCTL, DPLL_CTL
+        * armdiv, dspdiv, dspmmu, tcdiv, perdiv, lcddiv
+        */
+#if defined(CONFIG_OMAP_ARM_216MHZ) && defined(CONFIG_ARCH_OMAP16XX)
+       { 216000000, 12000000, 216000000, 0x050d, 0x2910 }, /* 1/1/2/2/2/8 */
+#endif
+#if defined(CONFIG_OMAP_ARM_195MHZ) && defined(CONFIG_ARCH_OMAP730)
+       { 195000000, 13000000, 195000000, 0x050e, 0x2790 }, /* 1/1/2/2/4/8 */
+#endif
+#if defined(CONFIG_OMAP_ARM_192MHZ) && defined(CONFIG_ARCH_OMAP16XX)
+       { 192000000, 19200000, 192000000, 0x050f, 0x2510 }, /* 1/1/2/2/8/8 */
+       { 192000000, 12000000, 192000000, 0x050f, 0x2810 }, /* 1/1/2/2/8/8 */
+       {  96000000, 12000000, 192000000, 0x055f, 0x2810 }, /* 2/2/2/2/8/8 */
+       {  48000000, 12000000, 192000000, 0x0ccf, 0x2810 }, /* 4/4/4/4/8/8 */
+       {  24000000, 12000000, 192000000, 0x0fff, 0x2810 }, /* 8/8/8/8/8/8 */
+#endif
+#if defined(CONFIG_OMAP_ARM_182MHZ) && defined(CONFIG_ARCH_OMAP730)
+       { 182000000, 13000000, 182000000, 0x050e, 0x2710 }, /* 1/1/2/2/4/8 */
+#endif
+#if defined(CONFIG_OMAP_ARM_168MHZ)
+       { 168000000, 12000000, 168000000, 0x010f, 0x2710 }, /* 1/1/1/2/8/8 */
+#endif
+#if defined(CONFIG_OMAP_ARM_120MHZ)
+       { 120000000, 12000000, 120000000, 0x010a, 0x2510 }, /* 1/1/1/2/4/4 */
+#endif
+#if defined(CONFIG_OMAP_ARM_96MHZ)
+       {  96000000, 12000000,  96000000, 0x0005, 0x2410 }, /* 1/1/1/1/2/2 */
+#endif
+#if defined(CONFIG_OMAP_ARM_60MHZ)
+       {  60000000, 12000000,  60000000, 0x0005, 0x2290 }, /* 1/1/1/1/2/2 */
+#endif
+#if defined(CONFIG_OMAP_ARM_30MHZ)
+       {  30000000, 12000000,  60000000, 0x0555, 0x2290 }, /* 2/2/2/2/2/2 */
+#endif
+       { 0, 0, 0, 0, 0 },
+};
+
+
+static void ckctl_recalc(struct clk *  clk)
+{
+       int dsor;
+
+       /* Calculate divisor encoded as 2-bit exponent */
+       dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset));
+       if (unlikely(clk->rate == clk->parent->rate / dsor))
+               return; /* No change, quick exit */
+       clk->rate = clk->parent->rate / dsor;
+
+       if (unlikely(clk->flags & RATE_PROPAGATES))
+               propagate_rate(clk);
+}
+
+
+static void followparent_recalc(struct clk *  clk)
+{
+       clk->rate = clk->parent->rate;
+}
+
+
+static void watchdog_recalc(struct clk *  clk)
+{
+       clk->rate = clk->parent->rate / 14;
+}
+
+
+static struct clk ck_ref = {
+       .name           = "ck_ref",
+       .rate           = 12000000,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         ALWAYS_ENABLED,
+};
+
+static struct clk ck_dpll1 = {
+       .name           = "ck_dpll1",
+       .parent         = &ck_ref,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_PROPAGATES | ALWAYS_ENABLED,
+};
+
+static struct clk ck_dpll1out = {
+       .name           = "ck_dpll1out",
+       .parent         = &ck_dpll1,
+       .flags          = CLOCK_IN_OMAP16XX,
+       .enable_reg     = ARM_IDLECT2,
+       .enable_bit     = EN_CKOUT_ARM,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk arm_ck = {
+       .name           = "arm_ck",
+       .parent         = &ck_dpll1,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED,
+       .rate_offset    = CKCTL_ARMDIV_OFFSET,
+       .recalc         = &ckctl_recalc,
+};
+
+static struct clk armper_ck = {
+       .name           = "armper_ck",
+       .parent         = &ck_dpll1,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_CKCTL,
+       .enable_reg     = ARM_IDLECT2,
+       .enable_bit     = EN_PERCK,
+       .rate_offset    = CKCTL_PERDIV_OFFSET,
+       .recalc         = &ckctl_recalc,
+};
+
+static struct clk arm_gpio_ck = {
+       .name           = "arm_gpio_ck",
+       .parent         = &ck_dpll1,
+       .flags          = CLOCK_IN_OMAP1510,
+       .enable_reg     = ARM_IDLECT2,
+       .enable_bit     = EN_GPIOCK,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk armxor_ck = {
+       .name           = "armxor_ck",
+       .parent         = &ck_ref,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+       .enable_reg     = ARM_IDLECT2,
+       .enable_bit     = EN_XORPCK,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk armtim_ck = {
+       .name           = "armtim_ck",
+       .parent         = &ck_ref,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+       .enable_reg     = ARM_IDLECT2,
+       .enable_bit     = EN_TIMCK,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk armwdt_ck = {
+       .name           = "armwdt_ck",
+       .parent         = &ck_ref,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+       .enable_reg     = ARM_IDLECT2,
+       .enable_bit     = EN_WDTCK,
+       .recalc         = &watchdog_recalc,
+};
+
+static struct clk arminth_ck1610 = {
+       .name           = "arminth_ck",
+       .parent         = &arm_ck,
+       .flags          = CLOCK_IN_OMAP16XX,
+       .recalc         = &followparent_recalc,
+       /* Note: On 1610/1710 frequency can be divided by 2 by programming
+        * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1
+        *
+        * 1510 version is in TC clocks.
+        */
+};
+
+static struct clk dsp_ck = {
+       .name           = "dsp_ck",
+       .parent         = &ck_dpll1,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_CKCTL,
+       .enable_reg     = ARM_CKCTL,
+       .enable_bit     = EN_DSPCK,
+       .rate_offset    = CKCTL_DSPDIV_OFFSET,
+       .recalc         = &ckctl_recalc,
+};
+
+static struct clk dspmmu_ck = {
+       .name           = "dspmmu_ck",
+       .parent         = &ck_dpll1,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_CKCTL | ALWAYS_ENABLED,
+       .rate_offset    = CKCTL_DSPMMUDIV_OFFSET,
+       .recalc         = &ckctl_recalc,
+};
+
+static struct clk tc_ck = {
+       .name           = "tc_ck",
+       .parent         = &ck_dpll1,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED,
+       .rate_offset    = CKCTL_TCDIV_OFFSET,
+       .recalc         = &ckctl_recalc,
+};
+
+static struct clk arminth_ck1510 = {
+       .name           = "arminth_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP1510,
+       .recalc         = &followparent_recalc,
+       /* Note: On 1510 frequency follows TC_CK
+        *
+        * 1610/1710 version is in MPU clocks.
+        */
+};
+
+static struct clk tipb_ck = {
+       .name           = "tibp_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP1510,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk l3_ocpi_ck = {
+       .name           = "l3_ocpi_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP16XX,
+       .enable_reg     = ARM_IDLECT3,
+       .enable_bit     = EN_OCPI_CK,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk tc1_ck = {
+       .name           = "tc1_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP16XX,
+       .enable_reg     = ARM_IDLECT3,
+       .enable_bit     = EN_TC1_CK,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk tc2_ck = {
+       .name           = "tc2_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP16XX,
+       .enable_reg     = ARM_IDLECT3,
+       .enable_bit     = EN_TC2_CK,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk dma_ck = {
+       .name           = "dma_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk dma_lcdfree_ck = {
+       .name           = "dma_lcdfree_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP16XX,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk api_ck = {
+       .name           = "api_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+       .enable_reg     = ARM_IDLECT2,
+       .enable_bit     = EN_APICK,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk lb_ck = {
+       .name           = "lb_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP1510,
+       .enable_reg     = ARM_IDLECT2,
+       .enable_bit     = EN_LBCK,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk rhea1_ck = {
+       .name           = "rhea1_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP16XX,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk rhea2_ck = {
+       .name           = "rhea2_ck",
+       .parent         = &tc_ck,
+       .flags          = CLOCK_IN_OMAP16XX,
+       .recalc         = &followparent_recalc,
+};
+
+static struct clk lcd_ck = {
+       .name           = "lcd_ck",
+       .parent         = &ck_dpll1,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_CKCTL,
+       .enable_reg     = ARM_IDLECT2,
+       .enable_bit     = EN_LCDCK,
+       .rate_offset    = CKCTL_LCDDIV_OFFSET,
+       .recalc         = &ckctl_recalc,
+};
+
+static struct clk uart1_ck = {
+       .name           = "uart1_ck",
+       /* Direct from ULPD, no parent */
+       .rate           = 48000000,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_FIXED | ENABLE_REG_32BIT,
+       .enable_reg     = MOD_CONF_CTRL_0,
+       .enable_bit     = 29,
+       /* (Only on 1510)
+        * The "enable bit" actually chooses between 48MHz and 12MHz.
+        */
+};
+
+static struct clk uart2_ck = {
+       .name           = "uart2_ck",
+       /* Direct from ULPD, no parent */
+       .rate           = 48000000,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_FIXED | ENABLE_REG_32BIT,
+       .enable_reg     = MOD_CONF_CTRL_0,
+       .enable_bit     = 30,
+       /* (1510/1610/1710)
+        * The "enable bit" actually chooses between 48MHz and 12MHz/32kHz.
+        */
+};
+
+static struct clk uart3_ck = {
+       .name           = "uart3_ck",
+       /* Direct from ULPD, no parent */
+       .rate           = 48000000,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_FIXED | ENABLE_REG_32BIT,
+       .enable_reg     = MOD_CONF_CTRL_0,
+       .enable_bit     = 31,
+       /* (Only on 1510)
+        * The "enable bit" actually chooses between 48MHz and 12MHz.
+        */
+};
+
+static struct clk usb_ck1610 = {
+       .name           = "usb_ck",
+       /* Direct from ULPD, no parent */
+       .rate           = 48000000,
+       .flags          = CLOCK_IN_OMAP16XX |
+                         RATE_FIXED | ENABLE_REG_32BIT,
+       .enable_reg     = ULPD_CLOCK_CTRL,
+       .enable_bit     = USB_MCLK_EN,
+};
+
+static struct clk usb_ck1510 = {
+       .name           = "usb_ck",
+       /* Direct from ULPD, no parent */
+       .rate           = 48000000,
+       .flags          = CLOCK_IN_OMAP1510 | RATE_FIXED,
+};
+
+static struct clk usb_hhc_ck = {
+       .name           = "usb_hhc_ck",
+       /* Direct from ULPD, no parent */
+       .rate           = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_FIXED | ENABLE_REG_32BIT,
+       .enable_reg     = MOD_CONF_CTRL_0,
+       .enable_bit     = USB_HOST_HHC_UHOST_EN,
+};
+
+/* To be done --
+static struct clk mclk = {
+       .name           = "mclk",
+};
+
+static struct clk bclk = {
+       .name           = "bclk",
+};
+-- to be done */
+
+static struct clk mmc1_ck = {
+       .name           = "mmc1_ck",
+       /* Functional clock is direct from ULPD, interface clock is ARMPER */
+       .parent         = &armper_ck,
+       .rate           = 48000000,
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         RATE_FIXED | ENABLE_REG_32BIT,
+       .enable_reg     = MOD_CONF_CTRL_0,
+       .enable_bit     = 23,
+};
+
+static struct clk mmc2_ck = {
+       .name           = "mmc2_ck",
+       /* Functional clock is direct from ULPD, interface clock is ARMPER */
+       .parent         = &armper_ck,
+       .rate           = 48000000,
+       .flags          = CLOCK_IN_OMAP16XX |
+                         RATE_FIXED | ENABLE_REG_32BIT,
+       .enable_reg     = MOD_CONF_CTRL_0,
+       .enable_bit     = 20,
+};
+
+static struct clk virtual_ck_mpu = {
+       .name           = "mpu",
+       .flags          = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+                         VIRTUAL_CLOCK | ALWAYS_ENABLED,
+       .parent         = &arm_ck, /* Is smarter alias for */
+       .recalc         = &followparent_recalc,
+       .set_rate       = &select_table_rate,
+       .round_rate     = &round_to_table_rate,
+};
+
+
+static struct clk *  onchip_clks[] = {
+       /* non-ULPD clocks */
+       &ck_ref,
+       &ck_dpll1,
+       /* CK_GEN1 clocks */
+       &ck_dpll1out,
+       &arm_ck,
+       &armper_ck,
+       &arm_gpio_ck,
+       &armxor_ck,
+       &armtim_ck,
+       &armwdt_ck,
+       &arminth_ck1510,
+       &arminth_ck1610,
+       /* CK_GEN2 clocks */
+       &dsp_ck,
+       &dspmmu_ck,
+       /* CK_GEN3 clocks */
+       &tc_ck,
+       &tipb_ck,
+       &l3_ocpi_ck,
+       &tc1_ck,
+       &tc2_ck,
+       &dma_ck,
+       &dma_lcdfree_ck,
+       &api_ck,
+       &lb_ck,
+       &rhea1_ck,
+       &rhea2_ck,
+       &lcd_ck,
+       /* ULPD clocks */
+       &uart1_ck,
+       &uart2_ck,
+       &uart3_ck,
+       &usb_ck1510,
+       &usb_ck1610,
+       &usb_hhc_ck,
+       /* To be done --
+       &mclk,
+       &bclk,
+       -- to be done */
+       &mmc1_ck,
+       &mmc2_ck,
+       /* Virtual clocks */
+       &virtual_ck_mpu,
+};
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+       struct clk *p, *clk = ERR_PTR(-ENOENT);
+
+       down(&clocks_sem);
+       list_for_each_entry(p, &clocks, node) {
+               if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
+                       clk = p;
+                       break;
+               }
+       }
+       up(&clocks_sem);
+
+       return clk;
+}
+EXPORT_SYMBOL(clk_get);
+
+
+void clk_put(struct clk *clk)
+{
+       if (clk && !IS_ERR(clk))
+               module_put(clk->owner);
+}
+EXPORT_SYMBOL(clk_put);
+
+
+int __clk_enable(struct clk *clk)
+{
+       __u16 regval16;
+       __u32 regval32;
+
+       if (clk->flags & ALWAYS_ENABLED)
+               return 0;
+
+       if (unlikely(clk->enable_reg == 0)) {
+               printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
+                      clk->name);
+               return 0;
+       }
+
+       if (clk->flags & ENABLE_REG_32BIT) {
+               regval32 = omap_readl(clk->enable_reg);
+               regval32 |= (1 << clk->enable_bit);
+               omap_writel(regval32, clk->enable_reg);
+       } else {
+               regval16 = omap_readw(clk->enable_reg);
+               regval16 |= (1 << clk->enable_bit);
+               omap_writew(regval16, clk->enable_reg);
+       }
+
+       return 0;
+}
+
+
+void __clk_disable(struct clk *clk)
+{
+       __u16 regval16;
+       __u32 regval32;
+
+       if (clk->enable_reg == 0)
+               return;
+
+       if (clk->flags & ENABLE_REG_32BIT) {
+               regval32 = omap_readl(clk->enable_reg);
+               regval32 &= ~(1 << clk->enable_bit);
+               omap_writel(regval32, clk->enable_reg);
+       } else {
+               regval16 = omap_readw(clk->enable_reg);
+               regval16 &= ~(1 << clk->enable_bit);
+               omap_writew(regval16, clk->enable_reg);
+       }
+}
+
+
+void __clk_unuse(struct clk *clk)
+{
+       if (clk->usecount > 0 && !(--clk->usecount)) {
+               __clk_disable(clk);
+               if (likely(clk->parent))
+                       __clk_unuse(clk->parent);
+       }
+}
+
+
+int __clk_use(struct clk *clk)
+{
+       int ret = 0;
+       if (clk->usecount++ == 0) {
+               if (likely(clk->parent))
+                       ret = __clk_use(clk->parent);
+
+               if (unlikely(ret != 0)) {
+                       clk->usecount--;
+                       return ret;
+               }
+
+               ret = __clk_enable(clk);
+
+               if (unlikely(ret != 0) && clk->parent) {
+                       __clk_unuse(clk->parent);
+                       clk->usecount--;
+               }
+       }
+
+       return ret;
+}
+
+
+int clk_enable(struct clk *clk)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&clockfw_lock, flags);
+       ret = __clk_enable(clk);
+       spin_unlock_irqrestore(&clockfw_lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL(clk_enable);
+
+
+void clk_disable(struct clk *clk)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&clockfw_lock, flags);
+       __clk_disable(clk);
+       spin_unlock_irqrestore(&clockfw_lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+
+int clk_use(struct clk *clk)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&clockfw_lock, flags);
+       ret = __clk_use(clk);
+       spin_unlock_irqrestore(&clockfw_lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL(clk_use);
+
+
+void clk_unuse(struct clk *clk)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&clockfw_lock, flags);
+       __clk_unuse(clk);
+       spin_unlock_irqrestore(&clockfw_lock, flags);
+}
+EXPORT_SYMBOL(clk_unuse);
+
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+       return clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+
+static __u16 verify_ckctl_value(__u16 newval)
+{
+       /* This function checks for following limitations set
+        * by the hardware (all conditions must be true):
+        * DSPMMU_CK == DSP_CK  or  DSPMMU_CK == DSP_CK/2
+        * ARM_CK >= TC_CK
+        * DSP_CK >= TC_CK
+        * DSPMMU_CK >= TC_CK
+        *
+        * In addition following rules are enforced:
+        * LCD_CK <= TC_CK
+        * ARMPER_CK <= TC_CK
+        *
+        * However, maximum frequencies are not checked for!
+        */
+       __u8 per_exp;
+       __u8 lcd_exp;
+       __u8 arm_exp;
+       __u8 dsp_exp;
+       __u8 tc_exp;
+       __u8 dspmmu_exp;
+
+       per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3;
+       lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3;
+       arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3;
+       dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3;
+       tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3;
+       dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3;
+
+       if (dspmmu_exp < dsp_exp)
+               dspmmu_exp = dsp_exp;
+       if (dspmmu_exp > dsp_exp+1)
+               dspmmu_exp = dsp_exp+1;
+       if (tc_exp < arm_exp)
+               tc_exp = arm_exp;
+       if (tc_exp < dspmmu_exp)
+               tc_exp = dspmmu_exp;
+       if (tc_exp > lcd_exp)
+               lcd_exp = tc_exp;
+       if (tc_exp > per_exp)
+               per_exp = tc_exp;
+
+       newval &= 0xf000;
+       newval |= per_exp << CKCTL_PERDIV_OFFSET;
+       newval |= lcd_exp << CKCTL_LCDDIV_OFFSET;
+       newval |= arm_exp << CKCTL_ARMDIV_OFFSET;
+       newval |= dsp_exp << CKCTL_DSPDIV_OFFSET;
+       newval |= tc_exp << CKCTL_TCDIV_OFFSET;
+       newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET;
+
+       return newval;
+}
+
+
+static int calc_dsor_exp(struct clk *clk, unsigned long rate)
+{
+       /* Note: If target frequency is too low, this function will return 4,
+        * which is invalid value. Caller must check for this value and act
+        * accordingly.
+        *
+        * Note: This function does not check for following limitations set
+        * by the hardware (all conditions must be true):
+        * DSPMMU_CK == DSP_CK  or  DSPMMU_CK == DSP_CK/2
+        * ARM_CK >= TC_CK
+        * DSP_CK >= TC_CK
+        * DSPMMU_CK >= TC_CK
+        */
+       unsigned long realrate;
+       struct clk *  parent;
+       unsigned  dsor_exp;
+
+       if (unlikely(!(clk->flags & RATE_CKCTL)))
+               return -EINVAL;
+
+       parent = clk->parent;
+       if (unlikely(parent == 0))
+               return -EIO;
+
+       realrate = parent->rate;
+       for (dsor_exp=0; dsor_exp<4; dsor_exp++) {
+               if (realrate <= rate)
+                       break;
+
+               realrate /= 2;
+       }
+
+       return dsor_exp;
+}
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+       int dsor_exp;
+
+       if (clk->flags & RATE_FIXED)
+               return clk->rate;
+
+       if (clk->flags & RATE_CKCTL) {
+               dsor_exp = calc_dsor_exp(clk, rate);
+               if (dsor_exp < 0)
+                       return dsor_exp;
+               if (dsor_exp > 3)
+                       dsor_exp = 3;
+               return clk->parent->rate / (1 << dsor_exp);
+       }
+
+       if(clk->round_rate != 0)
+               return clk->round_rate(rate);
+
+       return clk->rate;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+
+static void propagate_rate(struct clk *  clk)
+{
+       struct clk **  clkp;
+
+       for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) {
+               if (likely((*clkp)->parent != clk)) continue;
+               if (likely((*clkp)->recalc))
+                       (*clkp)->recalc(*clkp);
+       }
+}
+
+
+static int select_table_rate(unsigned long rate)
+{
+       /* Find the highest supported frequency <= rate and switch to it */
+       struct mpu_rate *  ptr;
+
+       for (ptr = rate_table; ptr->rate; ptr++) {
+               if (ptr->xtal != ck_ref.rate)
+                       continue;
+
+               /* DPLL1 cannot be reprogrammed without risking system crash */
+               if (likely(ck_dpll1.rate!=0) && ptr->pll_rate != ck_dpll1.rate)
+                       continue;
+
+               /* Can check only after xtal frequency check */
+               if (ptr->rate <= rate)
+                       break;
+       }
+
+       if (!ptr->rate)
+               return -EINVAL;
+
+       if (unlikely(ck_dpll1.rate == 0)) {
+               omap_writew(ptr->dpllctl_val, DPLL_CTL);
+               ck_dpll1.rate = ptr->pll_rate;
+       }
+       omap_writew(ptr->ckctl_val, ARM_CKCTL);
+       propagate_rate(&ck_dpll1);
+       return 0;
+}
+
+
+static long round_to_table_rate(unsigned long rate)
+{
+       /* Find the highest supported frequency <= rate */
+       struct mpu_rate *  ptr;
+       long  highest_rate;
+
+       highest_rate = -EINVAL;
+
+       for (ptr = rate_table; ptr->rate; ptr++) {
+               if (ptr->xtal != ck_ref.rate)
+                       continue;
+
+               highest_rate = ptr->rate;
+
+               /* Can check only after xtal frequency check */
+               if (ptr->rate <= rate)
+                       break;
+       }
+
+       return highest_rate;
+}
+
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+       int  ret = -EINVAL;
+       int  dsor_exp;
+       __u16  regval;
+       unsigned long  flags;
+
+       if (clk->flags & RATE_CKCTL) {
+               dsor_exp = calc_dsor_exp(clk, rate);
+               if (dsor_exp > 3)
+                       dsor_exp = -EINVAL;
+               if (dsor_exp < 0)
+                       return dsor_exp;
+
+               spin_lock_irqsave(&clockfw_lock, flags);
+               regval = omap_readw(ARM_CKCTL);
+               regval &= ~(3 << clk->rate_offset);
+               regval |= dsor_exp << clk->rate_offset;
+               regval = verify_ckctl_value(regval);
+               omap_writew(regval, ARM_CKCTL);
+               clk->rate = clk->parent->rate / (1 << dsor_exp);
+               spin_unlock_irqrestore(&clockfw_lock, flags);
+               ret = 0;
+       } else if(clk->set_rate != 0) {
+               spin_lock_irqsave(&clockfw_lock, flags);
+               ret = clk->set_rate(rate);
+               spin_unlock_irqrestore(&clockfw_lock, flags);
+       }
+
+       if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES)))
+               propagate_rate(clk);
+
+       return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+
+int clk_register(struct clk *clk)
+{
+       down(&clocks_sem);
+       list_add(&clk->node, &clocks);
+       up(&clocks_sem);
+       return 0;
+}
+EXPORT_SYMBOL(clk_register);
+
+void clk_unregister(struct clk *clk)
+{
+       down(&clocks_sem);
+       list_del(&clk->node);
+       up(&clocks_sem);
+}
+EXPORT_SYMBOL(clk_unregister);
+
+
+
+int __init clk_init(void)
+{
+       struct clk **  clkp;
+       const struct omap_clock_config *info;
+       int crystal_type = 0; /* Default 12 MHz */
+
+       for (clkp = onchip_clks; clkp < onchip_clks+ARRAY_SIZE(onchip_clks); clkp++) {
+               if (((*clkp)->flags &CLOCK_IN_OMAP1510) && cpu_is_omap1510()) {
+                       clk_register(*clkp);
+                       continue;
+               }
+
+               if (((*clkp)->flags &CLOCK_IN_OMAP16XX) && cpu_is_omap16xx()) {
+                       clk_register(*clkp);
+                       continue;
+               }
+       }
+
+       info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config);
+       if (info != NULL) {
+               if (!cpu_is_omap1510())
+                       crystal_type = info->system_clock_type;
+       }
+
+#if defined(CONFIG_ARCH_OMAP730)
+       ck_ref.rate = 13000000;
+#elif defined(CONFIG_ARCH_OMAP16XX)
+       if (crystal_type == 2)
+               ck_ref.rate = 19200000;
+#endif
+
+       /* We want to be in syncronous scalable mode */
+       omap_writew(0x1000, ARM_SYSST);
+
+       /* Find the highest supported frequency and enable it */
+       if (select_table_rate(~0)) {
+               printk(KERN_ERR "System frequencies not set. Check your config.\n");
+               /* Guess sane values (60MHz) */
+               omap_writew(0x2290, DPLL_CTL);
+               omap_writew(0x1005, ARM_CKCTL);
+               ck_dpll1.rate = 60000000;
+               propagate_rate(&ck_dpll1);
+               printk(KERN_INFO "Clocking rate (xtal/DPLL1/MPU): %ld/%ld/%ld\n",
+                      ck_ref.rate, ck_dpll1.rate, arm_ck.rate);
+       }
+
+       /* Cache rates for clocks connected to ck_ref (not dpll1) */
+       propagate_rate(&ck_ref);
+
+#ifdef CONFIG_MACH_OMAP_PERSEUS2
+       /* Select slicer output as OMAP input clock */
+       omap_writew(omap_readw(OMAP730_PCC_UPLD_CTRL) & ~0x1, OMAP730_PCC_UPLD_CTRL);
+#endif
+
+       /* Turn off DSP and ARM_TIMXO. Make sure ARM_INTHCK is not divided */
+       omap_writew(omap_readw(ARM_CKCTL) & 0x0fff, ARM_CKCTL);
+
+       /* Put DSP/MPUI into reset until needed */
+       omap_writew(0, ARM_RSTCT1);
+       omap_writew(1, ARM_RSTCT2);
+       omap_writew(0x400, ARM_IDLECT1);
+
+       /*
+        * According to OMAP5910 Erratum SYS_DMA_1, bit DMACK_REQ (bit 8)
+        * of the ARM_IDLECT2 register must be set to zero. The power-on
+        * default value of this bit is one.
+        */
+       omap_writew(0x0000, ARM_IDLECT2);       /* Turn LCD clock off also */
+
+       /*
+        * Only enable those clocks we will need, let the drivers
+        * enable other clocks as necessary
+        */
+       clk_use(&armper_ck);
+       clk_use(&armxor_ck);
+       clk_use(&armtim_ck);
+
+       if (cpu_is_omap1510())
+               clk_enable(&arm_gpio_ck);
+
+       return 0;
+}
diff --git a/arch/arm/mach-omap/clock.h b/arch/arm/mach-omap/clock.h
new file mode 100644 (file)
index 0000000..f3037bc
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *  linux/arch/arm/mach-omap/clock.h
+ *
+ *  Copyright (C) 2004 Nokia corporation
+ *  Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
+ *  Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, 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.
+ */
+
+#ifndef __ARCH_ARM_OMAP_CLOCK_H
+#define __ARCH_ARM_OMAP_CLOCK_H
+
+struct module;
+
+struct clk {
+       struct list_head        node;
+       struct module           *owner;
+       const char              *name;
+       struct clk              *parent;
+       unsigned long           rate;
+       __s8                    usecount;
+       __u8                    flags;
+       __u32                   enable_reg;
+       __u8                    enable_bit;
+       __u8                    rate_offset;
+       void                    (*recalc)(struct clk *);
+       int                     (*set_rate)(unsigned long);
+       long                    (*round_rate)(unsigned long);
+};
+
+
+struct mpu_rate {
+       unsigned long           rate;
+       unsigned long           xtal;
+       unsigned long           pll_rate;
+       __u16                   ckctl_val;
+       __u16                   dpllctl_val;
+};
+
+
+/* Clock flags */
+#define RATE_CKCTL             1
+#define RATE_FIXED             2
+#define RATE_PROPAGATES                4
+#define VIRTUAL_CLOCK          8
+#define ALWAYS_ENABLED         16
+#define ENABLE_REG_32BIT       32
+#define CLOCK_IN_OMAP16XX      64
+#define CLOCK_IN_OMAP1510      128
+
+/* ARM_CKCTL bit shifts */
+#define CKCTL_PERDIV_OFFSET    0
+#define CKCTL_LCDDIV_OFFSET    2
+#define CKCTL_ARMDIV_OFFSET    4
+#define CKCTL_DSPDIV_OFFSET    6
+#define CKCTL_TCDIV_OFFSET     8
+#define CKCTL_DSPMMUDIV_OFFSET 10
+/*#define ARM_TIMXO            12*/
+#define EN_DSPCK               13
+/*#define ARM_INTHCK_SEL       14*/ /* Divide-by-2 for mpu inth_ck */
+
+/* ARM_IDLECT1 bit shifts */
+/*#define IDLWDT_ARM   0*/
+/*#define IDLXORP_ARM  1*/
+/*#define IDLPER_ARM   2*/
+/*#define IDLLCD_ARM   3*/
+/*#define IDLLB_ARM    4*/
+/*#define IDLHSAB_ARM  5*/
+/*#define IDLIF_ARM    6*/
+/*#define IDLDPLL_ARM  7*/
+/*#define IDLAPI_ARM   8*/
+/*#define IDLTIM_ARM   9*/
+/*#define SETARM_IDLE  11*/
+
+/* ARM_IDLECT2 bit shifts */
+#define EN_WDTCK       0
+#define EN_XORPCK      1
+#define EN_PERCK       2
+#define EN_LCDCK       3
+#define EN_LBCK                4 /* Not on 1610/1710 */
+/*#define EN_HSABCK    5*/
+#define EN_APICK       6
+#define EN_TIMCK       7
+#define DMACK_REQ      8
+#define EN_GPIOCK      9 /* Not on 1610/1710 */
+/*#define EN_LBFREECK  10*/
+#define EN_CKOUT_ARM   11
+
+/* ARM_IDLECT3 bit shifts */
+#define EN_OCPI_CK     0
+#define EN_TC1_CK      2
+#define EN_TC2_CK      4
+
+/* Various register defines for clock controls scattered around OMAP chip */
+#define USB_MCLK_EN            4       /* In ULPD_CLKC_CTRL */
+#define USB_HOST_HHC_UHOST_EN  9       /* In MOD_CONF_CTRL_0 */
+
+
+int clk_register(struct clk *clk);
+void clk_unregister(struct clk *clk);
+int clk_init(void);
+
+#endif
diff --git a/arch/arm/mach-omap/pm.c b/arch/arm/mach-omap/pm.c
new file mode 100644 (file)
index 0000000..095322c
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * linux/arch/arm/mach-omap/pm.c
+ *
+ * OMAP Power Management Routines
+ *
+ * Original code for the SA11x0:
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * Modified for the PXA250 by Nicolas Pitre:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Modified for the OMAP1510 by David Singleton:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Cleanup 2004 for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS 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 <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/pm.h>
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/arch/omap16xx.h>
+#include <asm/arch/pm.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/tps65010.h>
+
+#include "clock.h"
+
+static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE];
+static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE];
+static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE];
+static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE];
+
+/*
+ * Let's power down on idle, but only if we are really
+ * idle, because once we start down the path of
+ * going idle we continue to do idle even if we get
+ * a clock tick interrupt . .
+ */
+void omap_pm_idle(void)
+{
+       int (*func_ptr)(void) = 0;
+       unsigned int mask32 = 0;
+
+       /*
+        * If the DSP is being used let's just idle the CPU, the overhead
+        * to wake up from Big Sleep is big, milliseconds versus micro
+        * seconds for wait for interrupt.
+        */
+
+       local_irq_disable();
+       local_fiq_disable();
+       if (need_resched()) {
+               local_fiq_enable();
+               local_irq_enable();
+               return;
+       }
+       mask32 = omap_readl(ARM_SYSST);
+       local_fiq_enable();
+       local_irq_enable();
+       if ((mask32 & DSP_IDLE) == 0) {
+               __asm__ volatile ("mcr  p15, 0, r0, c7, c0, 4");
+       } else {
+
+               if (cpu_is_omap1510()) {
+                       func_ptr = (void *)(OMAP1510_SRAM_IDLE_SUSPEND);
+               } else if (cpu_is_omap1610() || cpu_is_omap1710()) {
+                       func_ptr = (void *)(OMAP1610_SRAM_IDLE_SUSPEND);
+               } else if (cpu_is_omap5912()) {
+                       func_ptr = (void *)(OMAP5912_SRAM_IDLE_SUSPEND);
+               }
+
+               func_ptr();
+       }
+}
+
+/*
+ * Configuration of the wakeup event is board specific. For the
+ * moment we put it into this helper function. Later it may move
+ * to board specific files.
+ */
+static void omap_pm_wakeup_setup(void)
+{
+       /*
+        * Enable ARM XOR clock and release peripheral from reset by
+        * writing 1 to PER_EN bit in ARM_RSTCT2, this is required
+        * for UART configuration to use UART2 to wake up.
+        */
+
+       omap_writel(omap_readl(ARM_IDLECT2) | ENABLE_XORCLK, ARM_IDLECT2);
+       omap_writel(omap_readl(ARM_RSTCT2) | PER_EN, ARM_RSTCT2);
+       omap_writew(MODEM_32K_EN, ULPD_CLOCK_CTRL);
+
+       /*
+        * Turn off all interrupts except L1-2nd level cascade,
+        * and the L2 wakeup interrupts: keypad and UART2.
+        */
+
+       omap_writel(~IRQ_LEVEL2, OMAP_IH1_MIR);
+
+       if (cpu_is_omap1510()) {
+               omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD),  OMAP_IH2_MIR);
+       }
+
+       if (cpu_is_omap16xx()) {
+               omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_0_MIR);
+
+               omap_writel(~0x0, OMAP_IH2_1_MIR);
+               omap_writel(~0x0, OMAP_IH2_2_MIR);
+               omap_writel(~0x0, OMAP_IH2_3_MIR);
+       }
+
+       /*  New IRQ agreement */
+       omap_writel(1, OMAP_IH1_CONTROL);
+
+       /* external PULL to down, bit 22 = 0 */
+       omap_writel(omap_readl(PULL_DWN_CTRL_2) & ~(1<<22), PULL_DWN_CTRL_2);
+}
+
+void omap_pm_suspend(void)
+{
+       unsigned int mask32 = 0;
+       unsigned long arg0 = 0, arg1 = 0;
+       int (*func_ptr)(unsigned short, unsigned short) = 0;
+       unsigned short save_dsp_idlect2;
+
+       printk("PM: OMAP%x is entering deep sleep now ...\n", system_rev);
+
+       if (machine_is_omap_osk()) {
+               /* Stop LED1 (D9) blink */
+               tps65010_set_led(LED1, OFF);
+       }
+
+       /*
+        * Step 1: turn off interrupts
+        */
+
+       local_irq_disable();
+       local_fiq_disable();
+
+       /*
+        * Step 2: save registers
+        *
+        * The omap is a strange/beautiful device. The caches, memory
+        * and register state are preserved across power saves.
+        * We have to save and restore very little register state to
+        * idle the omap.
+         *
+        * Save interrupt, MPUI, ARM and UPLD control registers.
+        */
+
+       if (cpu_is_omap1510()) {
+               MPUI1510_SAVE(OMAP_IH1_MIR);
+               MPUI1510_SAVE(OMAP_IH2_MIR);
+               MPUI1510_SAVE(MPUI_CTRL);
+               MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1510_SAVE(MPUI_DSP_API_CONFIG);
+               MPUI1510_SAVE(EMIFS_CONFIG);
+               MPUI1510_SAVE(EMIFF_SDRAM_CONFIG);
+       } else if (cpu_is_omap16xx()) {
+               MPUI1610_SAVE(OMAP_IH1_MIR);
+               MPUI1610_SAVE(OMAP_IH2_0_MIR);
+               MPUI1610_SAVE(OMAP_IH2_1_MIR);
+               MPUI1610_SAVE(OMAP_IH2_2_MIR);
+               MPUI1610_SAVE(OMAP_IH2_3_MIR);
+               MPUI1610_SAVE(MPUI_CTRL);
+               MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1610_SAVE(MPUI_DSP_API_CONFIG);
+               MPUI1610_SAVE(EMIFS_CONFIG);
+               MPUI1610_SAVE(EMIFF_SDRAM_CONFIG);
+       }
+
+       ARM_SAVE(ARM_CKCTL);
+       ARM_SAVE(ARM_IDLECT1);
+       ARM_SAVE(ARM_IDLECT2);
+       ARM_SAVE(ARM_EWUPCT);
+       ARM_SAVE(ARM_RSTCT1);
+       ARM_SAVE(ARM_RSTCT2);
+       ARM_SAVE(ARM_SYSST);
+       ULPD_SAVE(ULPD_CLOCK_CTRL);
+       ULPD_SAVE(ULPD_STATUS_REQ);
+
+       /*
+        * Step 3: LOW_PWR signal enabling
+        *
+        * Allow the LOW_PWR signal to be visible on MPUIO5 ball.
+        */
+       if (cpu_is_omap1510()) {
+               /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */
+               omap_writew(omap_readw(ULPD_POWER_CTRL) |
+                           OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL);
+       } else if (cpu_is_omap16xx()) {
+               /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */
+               omap_writew(omap_readw(ULPD_POWER_CTRL) |
+                           OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL);
+       }
+
+       /* configure LOW_PWR pin */
+       omap_cfg_reg(T20_1610_LOW_PWR);
+
+       /*
+        * Step 4: OMAP DSP Shutdown
+        */
+
+       /* Set DSP_RST = 1 and DSP_EN = 0, put DSP block into reset */
+       omap_writel((omap_readl(ARM_RSTCT1) | DSP_RST) & ~DSP_ENABLE,
+                   ARM_RSTCT1);
+
+       /* Set DSP boot mode to DSP-IDLE, DSP_BOOT_MODE = 0x2 */
+        omap_writel(DSP_IDLE_MODE, MPUI_DSP_BOOT_CONFIG);
+
+       /* Set EN_DSPCK = 0, stop DSP block clock */
+       omap_writel(omap_readl(ARM_CKCTL) & ~DSP_CLOCK_ENABLE, ARM_CKCTL);
+
+       /* Stop any DSP domain clocks */
+       omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2);
+       save_dsp_idlect2 = __raw_readw(DSP_IDLECT2);
+       __raw_writew(0, DSP_IDLECT2);
+
+       /*
+        * Step 5: Wakeup Event Setup
+        */
+
+       omap_pm_wakeup_setup();
+
+       /*
+        * Step 6a: ARM and Traffic controller shutdown
+        *
+        * Step 6 starts here with clock and watchdog disable
+        */
+
+       /* stop clocks */
+       mask32 = omap_readl(ARM_IDLECT2);
+       mask32 &= ~(1<<EN_WDTCK);  /* bit 0 -> 0 (WDT clock) */
+       mask32 |=  (1<<EN_XORPCK); /* bit 1 -> 1 (XORPCK clock) */
+       mask32 &= ~(1<<EN_PERCK);  /* bit 2 -> 0 (MPUPER_CK clock) */
+       mask32 &= ~(1<<EN_LCDCK);  /* bit 3 -> 0 (LCDC clock) */
+       mask32 &= ~(1<<EN_LBCK);   /* bit 4 -> 0 (local bus clock) */
+       mask32 |=  (1<<EN_APICK);  /* bit 6 -> 1 (MPUI clock) */
+       mask32 &= ~(1<<EN_TIMCK);  /* bit 7 -> 0 (MPU timer clock) */
+       mask32 &= ~(1<<DMACK_REQ); /* bit 8 -> 0 (DMAC clock) */
+       mask32 &= ~(1<<EN_GPIOCK); /* bit 9 -> 0 (GPIO clock) */
+       omap_writel(mask32, ARM_IDLECT2);
+
+       /* disable ARM watchdog */
+       omap_writel(0x00F5, OMAP_WDT_TIMER_MODE);
+       omap_writel(0x00A0, OMAP_WDT_TIMER_MODE);
+
+       /*
+        * Step 6b: ARM and Traffic controller shutdown
+        *
+        * Step 6 continues here. Prepare jump to power management
+        * assembly code in internal SRAM.
+        *
+        * Since the omap_cpu_suspend routine has been copied to
+        * SRAM, we'll do an indirect procedure call to it and pass the
+        * contents of arm_idlect1 and arm_idlect2 so it can restore
+        * them when it wakes up and it will return.
+        */
+
+       arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1];
+       arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2];
+
+       if (cpu_is_omap1510()) {
+               func_ptr = (void *)(OMAP1510_SRAM_API_SUSPEND);
+       } else if (cpu_is_omap1610() || cpu_is_omap1710()) {
+               func_ptr = (void *)(OMAP1610_SRAM_API_SUSPEND);
+       } else if (cpu_is_omap5912()) {
+               func_ptr = (void *)(OMAP5912_SRAM_API_SUSPEND);
+       }
+
+       /*
+        * Step 6c: ARM and Traffic controller shutdown
+        *
+        * Jump to assembly code. The processor will stay there
+        * until wake up.
+        */
+
+        func_ptr(arg0, arg1);
+
+       /*
+        * If we are here, processor is woken up!
+        */
+
+       if (cpu_is_omap1510()) {
+               /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */
+               omap_writew(omap_readw(ULPD_POWER_CTRL) &
+                           ~OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL);
+       } else if (cpu_is_omap16xx()) {
+               /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */
+               omap_writew(omap_readw(ULPD_POWER_CTRL) &
+                           ~OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL);
+       }
+
+
+       /* Restore DSP clocks */
+       omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2);
+       __raw_writew(save_dsp_idlect2, DSP_IDLECT2);
+       ARM_RESTORE(ARM_IDLECT2);
+
+       /*
+        * Restore ARM state, except ARM_IDLECT1/2 which omap_cpu_suspend did
+        */
+
+       ARM_RESTORE(ARM_CKCTL);
+       ARM_RESTORE(ARM_EWUPCT);
+       ARM_RESTORE(ARM_RSTCT1);
+       ARM_RESTORE(ARM_RSTCT2);
+       ARM_RESTORE(ARM_SYSST);
+       ULPD_RESTORE(ULPD_CLOCK_CTRL);
+       ULPD_RESTORE(ULPD_STATUS_REQ);
+
+       if (cpu_is_omap1510()) {
+               MPUI1510_RESTORE(MPUI_CTRL);
+               MPUI1510_RESTORE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1510_RESTORE(MPUI_DSP_API_CONFIG);
+               MPUI1510_RESTORE(EMIFS_CONFIG);
+               MPUI1510_RESTORE(EMIFF_SDRAM_CONFIG);
+               MPUI1510_RESTORE(OMAP_IH1_MIR);
+               MPUI1510_RESTORE(OMAP_IH2_MIR);
+       } else if (cpu_is_omap16xx()) {
+               MPUI1610_RESTORE(MPUI_CTRL);
+               MPUI1610_RESTORE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1610_RESTORE(MPUI_DSP_API_CONFIG);
+               MPUI1610_RESTORE(EMIFS_CONFIG);
+               MPUI1610_RESTORE(EMIFF_SDRAM_CONFIG);
+
+               MPUI1610_RESTORE(OMAP_IH1_MIR);
+               MPUI1610_RESTORE(OMAP_IH2_0_MIR);
+               MPUI1610_RESTORE(OMAP_IH2_1_MIR);
+               MPUI1610_RESTORE(OMAP_IH2_2_MIR);
+               MPUI1610_RESTORE(OMAP_IH2_3_MIR);
+       }
+
+       /*
+        * Reenable interrupts
+        */
+
+       local_irq_enable();
+       local_fiq_enable();
+
+       printk("PM: OMAP%x is re-starting from deep sleep...\n", system_rev);
+
+       if (machine_is_omap_osk()) {
+               /* Let LED1 (D9) blink again */
+               tps65010_set_led(LED1, BLINK);
+       }
+}
+
+#if defined(DEBUG) && defined(CONFIG_PROC_FS)
+static int g_read_completed;
+
+/*
+ * Read system PM registers for debugging
+ */
+static int omap_pm_read_proc(
+       char *page_buffer,
+       char **my_first_byte,
+       off_t virtual_start,
+       int length,
+       int *eof,
+       void *data)
+{
+       int my_buffer_offset = 0;
+       char * const my_base = page_buffer;
+
+       ARM_SAVE(ARM_CKCTL);
+       ARM_SAVE(ARM_IDLECT1);
+       ARM_SAVE(ARM_IDLECT2);
+       ARM_SAVE(ARM_EWUPCT);
+       ARM_SAVE(ARM_RSTCT1);
+       ARM_SAVE(ARM_RSTCT2);
+       ARM_SAVE(ARM_SYSST);
+
+       ULPD_SAVE(ULPD_IT_STATUS);
+       ULPD_SAVE(ULPD_CLOCK_CTRL);
+       ULPD_SAVE(ULPD_SOFT_REQ);
+       ULPD_SAVE(ULPD_STATUS_REQ);
+       ULPD_SAVE(ULPD_DPLL_CTRL);
+       ULPD_SAVE(ULPD_POWER_CTRL);
+
+       if (cpu_is_omap1510()) {
+               MPUI1510_SAVE(MPUI_CTRL);
+               MPUI1510_SAVE(MPUI_DSP_STATUS);
+               MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1510_SAVE(MPUI_DSP_API_CONFIG);
+               MPUI1510_SAVE(EMIFF_SDRAM_CONFIG);
+               MPUI1510_SAVE(EMIFS_CONFIG);
+       } else if (cpu_is_omap16xx()) {
+               MPUI1610_SAVE(MPUI_CTRL);
+               MPUI1610_SAVE(MPUI_DSP_STATUS);
+               MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG);
+               MPUI1610_SAVE(MPUI_DSP_API_CONFIG);
+               MPUI1610_SAVE(EMIFF_SDRAM_CONFIG);
+               MPUI1610_SAVE(EMIFS_CONFIG);
+       }
+
+       if (virtual_start == 0) {
+               g_read_completed = 0;
+
+               my_buffer_offset += sprintf(my_base + my_buffer_offset,
+                  "ARM_CKCTL_REG:            0x%-8x     \n"
+                  "ARM_IDLECT1_REG:          0x%-8x     \n"
+                  "ARM_IDLECT2_REG:          0x%-8x     \n"
+                  "ARM_EWUPCT_REG:           0x%-8x     \n"
+                  "ARM_RSTCT1_REG:           0x%-8x     \n"
+                  "ARM_RSTCT2_REG:           0x%-8x     \n"
+                  "ARM_SYSST_REG:            0x%-8x     \n"
+                  "ULPD_IT_STATUS_REG:       0x%-4x     \n"
+                  "ULPD_CLOCK_CTRL_REG:      0x%-4x     \n"
+                  "ULPD_SOFT_REQ_REG:        0x%-4x     \n"
+                  "ULPD_DPLL_CTRL_REG:       0x%-4x     \n"
+                  "ULPD_STATUS_REQ_REG:      0x%-4x     \n"
+                  "ULPD_POWER_CTRL_REG:      0x%-4x     \n",
+                  ARM_SHOW(ARM_CKCTL),
+                  ARM_SHOW(ARM_IDLECT1),
+                  ARM_SHOW(ARM_IDLECT2),
+                  ARM_SHOW(ARM_EWUPCT),
+                  ARM_SHOW(ARM_RSTCT1),
+                  ARM_SHOW(ARM_RSTCT2),
+                  ARM_SHOW(ARM_SYSST),
+                  ULPD_SHOW(ULPD_IT_STATUS),
+                  ULPD_SHOW(ULPD_CLOCK_CTRL),
+                  ULPD_SHOW(ULPD_SOFT_REQ),
+                  ULPD_SHOW(ULPD_DPLL_CTRL),
+                  ULPD_SHOW(ULPD_STATUS_REQ),
+                  ULPD_SHOW(ULPD_POWER_CTRL));
+
+               if (cpu_is_omap1510()) {
+                       my_buffer_offset += sprintf(my_base + my_buffer_offset,
+                          "MPUI1510_CTRL_REG             0x%-8x \n"
+                          "MPUI1510_DSP_STATUS_REG:      0x%-8x \n"
+                          "MPUI1510_DSP_BOOT_CONFIG_REG: 0x%-8x \n"
+                          "MPUI1510_DSP_API_CONFIG_REG:  0x%-8x \n"
+                          "MPUI1510_SDRAM_CONFIG_REG:    0x%-8x \n"
+                          "MPUI1510_EMIFS_CONFIG_REG:    0x%-8x \n",
+                          MPUI1510_SHOW(MPUI_CTRL),
+                          MPUI1510_SHOW(MPUI_DSP_STATUS),
+                          MPUI1510_SHOW(MPUI_DSP_BOOT_CONFIG),
+                          MPUI1510_SHOW(MPUI_DSP_API_CONFIG),
+                          MPUI1510_SHOW(EMIFF_SDRAM_CONFIG),
+                          MPUI1510_SHOW(EMIFS_CONFIG));
+               } else if (cpu_is_omap16xx()) {
+                       my_buffer_offset += sprintf(my_base + my_buffer_offset,
+                          "MPUI1610_CTRL_REG             0x%-8x \n"
+                          "MPUI1610_DSP_STATUS_REG:      0x%-8x \n"
+                          "MPUI1610_DSP_BOOT_CONFIG_REG: 0x%-8x \n"
+                          "MPUI1610_DSP_API_CONFIG_REG:  0x%-8x \n"
+                          "MPUI1610_SDRAM_CONFIG_REG:    0x%-8x \n"
+                          "MPUI1610_EMIFS_CONFIG_REG:    0x%-8x \n",
+                          MPUI1610_SHOW(MPUI_CTRL),
+                          MPUI1610_SHOW(MPUI_DSP_STATUS),
+                          MPUI1610_SHOW(MPUI_DSP_BOOT_CONFIG),
+                          MPUI1610_SHOW(MPUI_DSP_API_CONFIG),
+                          MPUI1610_SHOW(EMIFF_SDRAM_CONFIG),
+                          MPUI1610_SHOW(EMIFS_CONFIG));
+               }
+
+               g_read_completed++;
+       } else if (g_read_completed >= 1) {
+                *eof = 1;
+                return 0;
+       }
+       g_read_completed++;
+
+       *my_first_byte = page_buffer;
+       return  my_buffer_offset;
+}
+
+static void omap_pm_init_proc(void)
+{
+       struct proc_dir_entry *entry;
+
+       entry = create_proc_read_entry("driver/omap_pm",
+                                      S_IWUSR | S_IRUGO, NULL,
+                                      omap_pm_read_proc, 0);
+}
+
+#endif /* DEBUG && CONFIG_PROC_FS */
+
+/*
+ *     omap_pm_prepare - Do preliminary suspend work.
+ *     @state:         suspend state we're entering.
+ *
+ */
+//#include <asm/arch/hardware.h>
+
+static int omap_pm_prepare(u32 state)
+{
+       int error = 0;
+
+       switch (state)
+       {
+       case PM_SUSPEND_STANDBY:
+       case PM_SUSPEND_MEM:
+               break;
+
+       case PM_SUSPEND_DISK:
+               return -ENOTSUPP;
+
+       default:
+               return -EINVAL;
+       }
+
+       return error;
+}
+
+
+/*
+ *     omap_pm_enter - Actually enter a sleep state.
+ *     @state:         State we're entering.
+ *
+ */
+
+static int omap_pm_enter(u32 state)
+{
+       switch (state)
+       {
+       case PM_SUSPEND_STANDBY:
+       case PM_SUSPEND_MEM:
+               omap_pm_suspend();
+               break;
+
+       case PM_SUSPEND_DISK:
+               return -ENOTSUPP;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+
+/**
+ *     omap_pm_finish - Finish up suspend sequence.
+ *     @state:         State we're coming out of.
+ *
+ *     This is called after we wake back up (or if entering the sleep state
+ *     failed).
+ */
+
+static int omap_pm_finish(u32 state)
+{
+       return 0;
+}
+
+
+struct pm_ops omap_pm_ops ={
+       .pm_disk_mode = 0,
+        .prepare        = omap_pm_prepare,
+        .enter          = omap_pm_enter,
+        .finish         = omap_pm_finish,
+};
+
+static int __init omap_pm_init(void)
+{
+       printk("Power Management for TI OMAP.\n");
+       pm_idle = omap_pm_idle;
+       /*
+        * We copy the assembler sleep/wakeup routines to SRAM.
+        * These routines need to be in SRAM as that's the only
+        * memory the MPU can see when it wakes up.
+        */
+
+#ifdef CONFIG_ARCH_OMAP1510
+       if (cpu_is_omap1510()) {
+               memcpy((void *)OMAP1510_SRAM_IDLE_SUSPEND,
+                      omap1510_idle_loop_suspend,
+                      omap1510_idle_loop_suspend_sz);
+               memcpy((void *)OMAP1510_SRAM_API_SUSPEND, omap1510_cpu_suspend,
+                      omap1510_cpu_suspend_sz);
+       } else
+#endif
+       if (cpu_is_omap1610() || cpu_is_omap1710()) {
+               memcpy((void *)OMAP1610_SRAM_IDLE_SUSPEND,
+                      omap1610_idle_loop_suspend,
+                      omap1610_idle_loop_suspend_sz);
+               memcpy((void *)OMAP1610_SRAM_API_SUSPEND, omap1610_cpu_suspend,
+                      omap1610_cpu_suspend_sz);
+       } else if (cpu_is_omap5912()) {
+               memcpy((void *)OMAP5912_SRAM_IDLE_SUSPEND,
+                      omap1610_idle_loop_suspend,
+                      omap1610_idle_loop_suspend_sz);
+               memcpy((void *)OMAP5912_SRAM_API_SUSPEND, omap1610_cpu_suspend,
+                      omap1610_cpu_suspend_sz);
+       }
+
+       pm_set_ops(&omap_pm_ops);
+
+#if defined(DEBUG) && defined(CONFIG_PROC_FS)
+       omap_pm_init_proc();
+#endif
+
+       return 0;
+}
+__initcall(omap_pm_init);
+
diff --git a/arch/arm/mach-omap/sleep.S b/arch/arm/mach-omap/sleep.S
new file mode 100644 (file)
index 0000000..4d426d1
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * linux/arch/arm/mach-omap/sleep.S
+ *
+ * Low-level OMAP1510/1610 sleep/wakeUp support
+ *
+ * Initial SA1110 code:
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * Adapted for PXA by Nicolas Pitre:
+ * Copyright (c) 2002 Monta Vista Software, Inc.
+ *
+ * Support for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS 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 <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/arch/io.h>
+#include <asm/arch/pm.h>
+
+               .text
+
+/*
+ * Forces OMAP into idle state
+ *
+ * omapXXXX_idle_loop_suspend()
+ *
+ * Note: This code get's copied to internal SRAM at boot. When the OMAP
+ *      wakes up it continues execution at the point it went to sleep.
+ *
+ * Note: Because of slightly different configuration values we have
+ *       processor specific functions here.
+ */
+
+#ifdef CONFIG_ARCH_OMAP1510
+ENTRY(omap1510_idle_loop_suspend)
+
+       stmfd   sp!, {r0 - r12, lr}             @ save registers on stack
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       @ get ARM_IDLECT2 into r2
+       ldrh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       mov     r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff
+       orr     r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       @ get ARM_IDLECT1 into r1
+       ldrh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+       orr     r3, r1, #OMAP1510_IDLE_LOOP_REQUEST & 0xffff
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1510:        subs    r5, r5, #1
+       bne     l_1510
+/*
+ * Let's wait for the next clock tick to wake us up.
+ */
+       mov     r0, #0
+       mcr     p15, 0, r0, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1510_idle_loop_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+
+       @ restore ARM_IDLECT1 and ARM_IDLECT2 and return
+       @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2
+       strh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       ldmfd   sp!, {r0 - r12, pc}     @ restore regs and return
+
+ENTRY(omap1510_idle_loop_suspend_sz)
+       .word   . - omap1510_idle_loop_suspend
+#endif /* CONFIG_ARCH_OMAP1510 */
+
+#if defined(CONFIG_ARCH_OMAP16XX)
+ENTRY(omap1610_idle_loop_suspend)
+
+       stmfd   sp!, {r0 - r12, lr}             @ save registers on stack
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       @ get ARM_IDLECT2 into r2
+       ldrh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       mov     r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff
+       orr     r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       @ get ARM_IDLECT1 into r1
+       ldrh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+       orr     r3, r1, #OMAP1610_IDLE_LOOP_REQUEST & 0xffff
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1610:        subs    r5, r5, #1
+       bne     l_1610
+/*
+ * Let's wait for the next clock tick to wake us up.
+ */
+       mov     r0, #0
+       mcr     p15, 0, r0, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1610_idle_loop_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+
+       @ restore ARM_IDLECT1 and ARM_IDLECT2 and return
+       @ r1 has ARM_IDLECT1 and r2 still has ARM_IDLECT2
+       strh    r2, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r1, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       ldmfd   sp!, {r0 - r12, pc}     @ restore regs and return
+
+ENTRY(omap1610_idle_loop_suspend_sz)
+       .word   . - omap1610_idle_loop_suspend
+#endif /* CONFIG_ARCH_OMAP16XX */
+
+/*
+ * Forces OMAP into deep sleep state
+ *
+ * omapXXXX_cpu_suspend()
+ *
+ * The values of the registers ARM_IDLECT1 and ARM_IDLECT2 are passed
+ * as arg0 and arg1 from caller. arg0 is stored in register r0 and arg1
+ * in register r1.
+ *
+ * Note: This code get's copied to internal SRAM at boot. When the OMAP
+ *      wakes up it continues execution at the point it went to sleep.
+ *
+ * Note: Because of errata work arounds we have processor specific functions
+ *       here. They are mostly the same, but slightly different.
+ *
+ */
+
+#ifdef CONFIG_ARCH_OMAP1510
+ENTRY(omap1510_cpu_suspend)
+
+       @ save registers on stack
+       stmfd   sp!, {r0 - r12, lr}
+
+       @ load base address of Traffic Controller
+       mov     r4, #TCMIF_ASM_BASE & 0xff000000
+       orr     r4, r4, #TCMIF_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #TCMIF_ASM_BASE & 0x0000ff00
+
+       @ work around errata of OMAP1510 PDE bit for TC shut down
+       @ clear PDE bit
+       ldr     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+       bic     r5, r5, #PDE_BIT & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ set PWD_EN bit
+       and     r5, r5, #PWD_EN_BIT & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ prepare to put SDRAM into self-refresh manually
+       ldr     r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+       orr     r5, r5, #SELF_REFRESH_MODE & 0xff000000
+       orr     r5, r5, #SELF_REFRESH_MODE & 0x000000ff
+       str     r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+
+       @ prepare to put EMIFS to Sleep
+       ldr     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+       orr     r5, r5, #IDLE_EMIFS_REQUEST & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       mov     r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff
+       orr     r5,r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       mov     r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff
+       orr     r3, r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff00
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1510_2:
+       subs    r5, r5, #1
+       bne     l_1510_2
+/*
+ * Let's wait for the next wake up event to wake us up. r0 can't be
+ * used here because r0 holds ARM_IDLECT1
+ */
+       mov     r2, #0
+       mcr     p15, 0, r2, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1510_cpu_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+       strh    r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       @ restore regs and return
+       ldmfd   sp!, {r0 - r12, pc}
+
+ENTRY(omap1510_cpu_suspend_sz)
+       .word   . - omap1510_cpu_suspend
+#endif /* CONFIG_ARCH_OMAP1510 */
+
+#if defined(CONFIG_ARCH_OMAP16XX)
+ENTRY(omap1610_cpu_suspend)
+
+       @ save registers on stack
+       stmfd   sp!, {r0 - r12, lr}
+
+       @ load base address of Traffic Controller
+       mov     r4, #TCMIF_ASM_BASE & 0xff000000
+       orr     r4, r4, #TCMIF_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #TCMIF_ASM_BASE & 0x0000ff00
+
+       @ prepare to put SDRAM into self-refresh manually
+       ldr     r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+       orr     r5, r5, #SELF_REFRESH_MODE & 0xff000000
+       orr     r5, r5, #SELF_REFRESH_MODE & 0x000000ff
+       str     r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff]
+
+       @ prepare to put EMIFS to Sleep
+       ldr     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+       orr     r5, r5, #IDLE_EMIFS_REQUEST & 0xff
+       str     r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff]
+
+       @ load base address of ARM_IDLECT1 and ARM_IDLECT2
+       mov     r4, #CLKGEN_REG_ASM_BASE & 0xff000000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000
+       orr     r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00
+
+       @ turn off clock domains
+       mov     r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff
+       orr     r5,r5, #OMAP1610_IDLE_CLOCK_DOMAINS & 0xff00
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ work around errata of OMAP1610/5912. Enable (!) peripheral
+       @ clock to let the chip go into deep sleep
+       ldrh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       orr     r5,r5, #EN_PERCK_BIT & 0xff
+       strh    r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+
+       @ request ARM idle
+       mov     r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff
+       orr     r3, r3, #OMAP1610_DEEP_SLEEP_REQUEST & 0xff00
+       strh    r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       mov     r5, #IDLE_WAIT_CYCLES & 0xff
+       orr     r5, r5, #IDLE_WAIT_CYCLES & 0xff00
+l_1610_2:
+       subs    r5, r5, #1
+       bne     l_1610_2
+/*
+ * Let's wait for the next wake up event to wake us up. r0 can't be
+ * used here because r0 holds ARM_IDLECT1
+ */
+       mov     r2, #0
+       mcr     p15, 0, r2, c7, c0, 4           @ wait for interrupt
+/*
+ * omap1610_cpu_suspend()'s resume point.
+ *
+ * It will just start executing here, so we'll restore stuff from the
+ * stack, reset the ARM_IDLECT1 and ARM_IDLECT2.
+ */
+       strh    r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff]
+       strh    r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff]
+
+       @ restore regs and return
+       ldmfd   sp!, {r0 - r12, pc}
+
+ENTRY(omap1610_cpu_suspend_sz)
+       .word   . - omap1610_cpu_suspend
+#endif /* CONFIG_ARCH_OMAP16XX */
diff --git a/arch/arm/mach-pxa/Makefile.boot b/arch/arm/mach-pxa/Makefile.boot
new file mode 100644 (file)
index 0000000..1ead671
--- /dev/null
@@ -0,0 +1,2 @@
+   zreladdr-y  := 0xa0008000
+
diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c
new file mode 100644 (file)
index 0000000..5260436
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Support for Sharp SL-C7xx PDAs
+ * Models: SL-C700 (Corgi), SL-C750 (Shepherd), SL-C760 (Husky)
+ *
+ * Copyright (c) 2004-2005 Richard Purdie
+ *
+ * Based on Sharp's 2.4 kernel patches/lubbock.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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/mmc/host.h>
+
+#include <asm/setup.h>
+#include <asm/memory.h>
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/irq.h>
+#include <asm/arch/mmc.h>
+#include <asm/arch/udc.h>
+#include <asm/arch/corgi.h>
+
+#include <asm/hardware/scoop.h>
+#include <video/w100fb.h>
+
+#include "generic.h"
+
+
+/*
+ * Corgi SCOOP Device
+ */
+static struct resource corgi_scoop_resources[] = {
+       [0] = {
+               .start          = 0x10800000,
+               .end            = 0x10800fff,
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct scoop_config corgi_scoop_setup = {
+       .io_dir         = CORGI_SCOOP_IO_DIR,
+       .io_out         = CORGI_SCOOP_IO_OUT,
+};
+
+static struct platform_device corgiscoop_device = {
+       .name           = "sharp-scoop",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &corgi_scoop_setup,
+       },
+       .num_resources  = ARRAY_SIZE(corgi_scoop_resources),
+       .resource       = corgi_scoop_resources,
+};
+
+
+/*
+ * Corgi SSP Device
+ *
+ * Set the parent as the scoop device because a lot of SSP devices
+ * also use scoop functions and this makes the power up/down order
+ * work correctly.
+ */
+static struct platform_device corgissp_device = {
+       .name           = "corgi-ssp",
+       .dev            = {
+               .parent = &corgiscoop_device.dev,
+       },
+       .id             = -1,
+};
+
+
+/*
+ * Corgi w100 Frame Buffer Device
+ */
+static struct w100fb_mach_info corgi_fb_info = {
+       .w100fb_ssp_send        = corgi_ssp_lcdtg_send,
+       .comadj                         = -1,
+       .phadadj                        = -1,
+};
+
+static struct resource corgi_fb_resources[] = {
+       [0] = {
+               .start          = 0x08000000,
+               .end            = 0x08ffffff,
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device corgifb_device = {
+       .name           = "w100fb",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &corgi_fb_info,
+               .parent = &corgissp_device.dev,
+       },
+       .num_resources  = ARRAY_SIZE(corgi_fb_resources),
+       .resource       = corgi_fb_resources,
+};
+
+
+/*
+ * Corgi Backlight Device
+ */
+static struct platform_device corgibl_device = {
+       .name           = "corgi-bl",
+       .dev            = {
+               .parent = &corgifb_device.dev,
+       },
+       .id             = -1,
+};
+
+
+/*
+ * MMC/SD Device
+ *
+ * The card detect interrupt isn't debounced so we delay it by HZ/4
+ * to give the card a chance to fully insert/eject.
+ */
+static struct mmc_detect {
+       struct timer_list detect_timer;
+       void *devid;
+} mmc_detect;
+
+static void mmc_detect_callback(unsigned long data)
+{
+       mmc_detect_change(mmc_detect.devid);
+}
+
+static irqreturn_t corgi_mmc_detect_int(int irq, void *devid, struct pt_regs *regs)
+{
+       mmc_detect.devid=devid;
+       mod_timer(&mmc_detect.detect_timer, jiffies + HZ/4);
+       return IRQ_HANDLED;
+}
+
+static int corgi_mci_init(struct device *dev, irqreturn_t (*unused_detect_int)(int, void *, struct pt_regs *), void *data)
+{
+       int err;
+
+       /* setup GPIO for PXA25x MMC controller */
+       pxa_gpio_mode(GPIO6_MMCCLK_MD);
+       pxa_gpio_mode(GPIO8_MMCCS0_MD);
+       pxa_gpio_mode(CORGI_GPIO_nSD_DETECT | GPIO_IN);
+       pxa_gpio_mode(CORGI_GPIO_SD_PWR | GPIO_OUT);
+
+       init_timer(&mmc_detect.detect_timer);
+       mmc_detect.detect_timer.function = mmc_detect_callback;
+       mmc_detect.detect_timer.data = (unsigned long) &mmc_detect;
+
+       err = request_irq(CORGI_IRQ_GPIO_nSD_DETECT, corgi_mmc_detect_int, SA_INTERRUPT,
+                            "MMC card detect", data);
+       if (err) {
+               printk(KERN_ERR "corgi_mci_init: MMC/SD: can't request MMC card detect IRQ\n");
+               return -1;
+       }
+
+       set_irq_type(CORGI_IRQ_GPIO_nSD_DETECT, IRQT_BOTHEDGE);
+
+       return 0;
+}
+
+static void corgi_mci_setpower(struct device *dev, unsigned int vdd)
+{
+       struct pxamci_platform_data* p_d = dev->platform_data;
+
+       if (( 1 << vdd) & p_d->ocr_mask) {
+               printk(KERN_DEBUG "%s: on\n", __FUNCTION__);
+               GPSR1 = GPIO_bit(CORGI_GPIO_SD_PWR);
+       } else {
+               printk(KERN_DEBUG "%s: off\n", __FUNCTION__);
+               GPCR1 = GPIO_bit(CORGI_GPIO_SD_PWR);
+       }
+}
+
+static void corgi_mci_exit(struct device *dev, void *data)
+{
+       free_irq(CORGI_IRQ_GPIO_nSD_DETECT, data);
+       del_timer(&mmc_detect.detect_timer);
+}
+
+static struct pxamci_platform_data corgi_mci_platform_data = {
+       .ocr_mask       = MMC_VDD_32_33|MMC_VDD_33_34,
+       .init           = corgi_mci_init,
+       .setpower       = corgi_mci_setpower,
+       .exit           = corgi_mci_exit,
+};
+
+
+/*
+ * USB Device Controller
+ */
+static void corgi_udc_command(int cmd)
+{
+       switch(cmd)     {
+       case PXA2XX_UDC_CMD_CONNECT:
+               GPSR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
+               break;
+       case PXA2XX_UDC_CMD_DISCONNECT:
+               GPCR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
+               break;
+       }
+}
+
+static struct pxa2xx_udc_mach_info udc_info __initdata = {
+       /* no connect GPIO; corgi can't tell connection status */
+       .udc_command            = corgi_udc_command,
+};
+
+
+static struct platform_device *devices[] __initdata = {
+       &corgiscoop_device,
+       &corgissp_device,
+       &corgifb_device,
+       &corgibl_device,
+};
+
+static struct sharpsl_flash_param_info sharpsl_flash_param;
+
+static void corgi_get_param(void)
+{
+       sharpsl_flash_param.comadj_keyword = readl(FLASH_MEM_BASE + FLASH_COMADJ_MAGIC_ADR);
+       sharpsl_flash_param.comadj = readl(FLASH_MEM_BASE + FLASH_COMADJ_DATA_ADR);
+
+       sharpsl_flash_param.phad_keyword = readl(FLASH_MEM_BASE + FLASH_PHAD_MAGIC_ADR);
+       sharpsl_flash_param.phadadj = readl(FLASH_MEM_BASE + FLASH_PHAD_DATA_ADR);
+}
+
+static void __init corgi_init(void)
+{
+       if (sharpsl_flash_param.comadj_keyword == FLASH_COMADJ_MAJIC)
+               corgi_fb_info.comadj=sharpsl_flash_param.comadj;
+       else
+               corgi_fb_info.comadj=-1;
+
+       if (sharpsl_flash_param.phad_keyword == FLASH_PHAD_MAJIC)
+               corgi_fb_info.phadadj=sharpsl_flash_param.phadadj;
+       else
+               corgi_fb_info.phadadj=-1;
+
+       pxa_gpio_mode(CORGI_GPIO_USB_PULLUP | GPIO_OUT);
+       pxa_set_udc_info(&udc_info);
+       pxa_set_mci_info(&corgi_mci_platform_data);
+
+       platform_add_devices(devices, ARRAY_SIZE(devices));
+}
+
+static void __init fixup_corgi(struct machine_desc *desc,
+               struct tag *tags, char **cmdline, struct meminfo *mi)
+{
+       corgi_get_param();
+       mi->nr_banks=1;
+       mi->bank[0].start = 0xa0000000;
+       mi->bank[0].node = 0;
+       if (machine_is_corgi())
+               mi->bank[0].size = (32*1024*1024);
+       else
+               mi->bank[0].size = (64*1024*1024);
+}
+
+static void __init corgi_init_irq(void)
+{
+       pxa_init_irq();
+}
+
+static struct map_desc corgi_io_desc[] __initdata = {
+/*    virtual     physical    length      */
+/*     { 0xf1000000, 0x08000000, 0x01000000, MT_DEVICE },*/ /* LCDC (readable for Qt driver) */
+/*     { 0xef700000, 0x10800000, 0x00001000, MT_DEVICE },*/  /* SCOOP */
+       { 0xef800000, 0x00000000, 0x00800000, MT_DEVICE }, /* Boot Flash */
+};
+
+static void __init corgi_map_io(void)
+{
+       pxa_map_io();
+       iotable_init(corgi_io_desc,ARRAY_SIZE(corgi_io_desc));
+
+       /* setup sleep mode values */
+       PWER  = 0x00000002;
+       PFER  = 0x00000000;
+       PRER  = 0x00000002;
+       PGSR0 = 0x0158C000;
+       PGSR1 = 0x00FF0080;
+       PGSR2 = 0x0001C004;
+       /* Stop 3.6MHz and drive HIGH to PCMCIA and CS */
+       PCFR |= PCFR_OPDE;
+}
+
+#ifdef CONFIG_MACH_CORGI
+MACHINE_START(CORGI, "SHARP Corgi")
+       BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000))
+       FIXUP(fixup_corgi)
+       MAPIO(corgi_map_io)
+       INITIRQ(corgi_init_irq)
+       .init_machine = corgi_init,
+       .timer = &pxa_timer,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_SHEPHERD
+MACHINE_START(SHEPHERD, "SHARP Shepherd")
+       BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000))
+       FIXUP(fixup_corgi)
+       MAPIO(corgi_map_io)
+       INITIRQ(corgi_init_irq)
+       .init_machine = corgi_init,
+       .timer = &pxa_timer,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_HUSKY
+MACHINE_START(HUSKY, "SHARP Husky")
+       BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000))
+       FIXUP(fixup_corgi)
+       MAPIO(corgi_map_io)
+       INITIRQ(corgi_init_irq)
+       .init_machine = corgi_init,
+       .timer = &pxa_timer,
+MACHINE_END
+#endif
+
diff --git a/arch/arm/mach-pxa/corgi_ssp.c b/arch/arm/mach-pxa/corgi_ssp.c
new file mode 100644 (file)
index 0000000..9e01883
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ *  SSP control code for Sharp Corgi devices
+ *
+ *  Copyright (c) 2004 Richard Purdie
+ *
+ *  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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <asm/hardware.h>
+
+#include <asm/arch/ssp.h>
+#include <asm/arch/corgi.h>
+#include <asm/arch/pxa-regs.h>
+
+static spinlock_t corgi_ssp_lock = SPIN_LOCK_UNLOCKED;
+static struct ssp_dev corgi_ssp_dev;
+static struct ssp_state corgi_ssp_state;
+
+/*
+ * There are three devices connected to the SSP interface:
+ *   1. A touchscreen controller (TI ADS7846 compatible)
+ *   2. An LCD contoller (with some Backlight functionality)
+ *   3. A battery moinitoring IC (Maxim MAX1111)
+ *
+ * Each device uses a different speed/mode of communication.
+ *
+ * The touchscreen is very sensitive and the most frequently used
+ * so the port is left configured for this.
+ *
+ * Devices are selected using Chip Selects on GPIOs.
+ */
+
+/*
+ *  ADS7846 Routines
+ */
+unsigned long corgi_ssp_ads7846_putget(ulong data)
+{
+       unsigned long ret,flag;
+
+       spin_lock_irqsave(&corgi_ssp_lock, flag);
+       GPCR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS);
+
+       ssp_write_word(&corgi_ssp_dev,data);
+       ret = ssp_read_word(&corgi_ssp_dev);
+
+       GPSR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS);
+       spin_unlock_irqrestore(&corgi_ssp_lock, flag);
+
+       return ret;
+}
+
+/*
+ * NOTE: These functions should always be called in interrupt context
+ * and use the _lock and _unlock functions. They are very time sensitive.
+ */
+void corgi_ssp_ads7846_lock(void)
+{
+       spin_lock(&corgi_ssp_lock);
+       GPCR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS);
+}
+
+void corgi_ssp_ads7846_unlock(void)
+{
+       GPSR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS);
+       spin_unlock(&corgi_ssp_lock);
+}
+
+void corgi_ssp_ads7846_put(ulong data)
+{
+       ssp_write_word(&corgi_ssp_dev,data);
+}
+
+unsigned long corgi_ssp_ads7846_get(void)
+{
+       return ssp_read_word(&corgi_ssp_dev);
+}
+
+EXPORT_SYMBOL(corgi_ssp_ads7846_putget);
+EXPORT_SYMBOL(corgi_ssp_ads7846_lock);
+EXPORT_SYMBOL(corgi_ssp_ads7846_unlock);
+EXPORT_SYMBOL(corgi_ssp_ads7846_put);
+EXPORT_SYMBOL(corgi_ssp_ads7846_get);
+
+
+/*
+ *  LCD/Backlight Routines
+ */
+unsigned long corgi_ssp_dac_put(ulong data)
+{
+       unsigned long flag;
+
+       spin_lock_irqsave(&corgi_ssp_lock, flag);
+       GPCR0 = GPIO_bit(CORGI_GPIO_LCDCON_CS);
+
+       ssp_disable(&corgi_ssp_dev);
+       ssp_config(&corgi_ssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), SSCR1_SPH, 0, SSCR0_SerClkDiv(76));
+       ssp_enable(&corgi_ssp_dev);
+
+       ssp_write_word(&corgi_ssp_dev,data);
+       /* Read null data back from device to prevent SSP overflow */
+       ssp_read_word(&corgi_ssp_dev);
+
+       ssp_disable(&corgi_ssp_dev);
+       ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(2));
+       ssp_enable(&corgi_ssp_dev);
+       GPSR0 = GPIO_bit(CORGI_GPIO_LCDCON_CS);
+       spin_unlock_irqrestore(&corgi_ssp_lock, flag);
+
+       return 0;
+}
+
+void corgi_ssp_lcdtg_send(u8 adrs, u8 data)
+{
+       corgi_ssp_dac_put(((adrs & 0x07) << 5) | (data & 0x1f));
+}
+
+void corgi_ssp_blduty_set(int duty)
+{
+       corgi_ssp_lcdtg_send(0x02,duty);
+}
+
+EXPORT_SYMBOL(corgi_ssp_lcdtg_send);
+EXPORT_SYMBOL(corgi_ssp_blduty_set);
+
+/*
+ *  Max1111 Routines
+ */
+int corgi_ssp_max1111_get(ulong data)
+{
+       unsigned long flag;
+       int voltage,voltage1,voltage2;
+
+       spin_lock_irqsave(&corgi_ssp_lock, flag);
+       GPCR0 = GPIO_bit(CORGI_GPIO_MAX1111_CS);
+       ssp_disable(&corgi_ssp_dev);
+       ssp_config(&corgi_ssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(8));
+       ssp_enable(&corgi_ssp_dev);
+
+       udelay(1);
+
+       /* TB1/RB1 */
+       ssp_write_word(&corgi_ssp_dev,data);
+       ssp_read_word(&corgi_ssp_dev); /* null read */
+
+       /* TB12/RB2 */
+       ssp_write_word(&corgi_ssp_dev,0);
+       voltage1=ssp_read_word(&corgi_ssp_dev);
+
+       /* TB13/RB3*/
+       ssp_write_word(&corgi_ssp_dev,0);
+       voltage2=ssp_read_word(&corgi_ssp_dev);
+
+       ssp_disable(&corgi_ssp_dev);
+       ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(2));
+       ssp_enable(&corgi_ssp_dev);
+       GPSR0 = GPIO_bit(CORGI_GPIO_MAX1111_CS);
+       spin_unlock_irqrestore(&corgi_ssp_lock, flag);
+
+       if (voltage1 & 0xc0 || voltage2 & 0x3f)
+               voltage = -1;
+       else
+               voltage = ((voltage1 << 2) & 0xfc) | ((voltage2 >> 6) & 0x03);
+
+       return voltage;
+}
+
+EXPORT_SYMBOL(corgi_ssp_max1111_get);
+
+/*
+ *  Support Routines
+ */
+int __init corgi_ssp_probe(struct device *dev)
+{
+       int ret;
+
+       /* Chip Select - Disable All */
+       GPDR0 |= GPIO_bit(CORGI_GPIO_LCDCON_CS); /* output */
+       GPSR0 = GPIO_bit(CORGI_GPIO_LCDCON_CS);  /* High - Disable LCD Control/Timing Gen */
+       GPDR0 |= GPIO_bit(CORGI_GPIO_MAX1111_CS); /* output */
+       GPSR0 = GPIO_bit(CORGI_GPIO_MAX1111_CS);  /* High - Disable MAX1111*/
+       GPDR0 |= GPIO_bit(CORGI_GPIO_ADS7846_CS);  /* output */
+       GPSR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS);   /* High - Disable ADS7846*/
+
+       ret=ssp_init(&corgi_ssp_dev,1);
+
+       if (ret)
+               printk(KERN_ERR "Unable to register SSP handler!\n");
+       else {
+               ssp_disable(&corgi_ssp_dev);
+               ssp_config(&corgi_ssp_dev, (SSCR0_National | (SSCR0_DSS & 0x0b )), 0, 0, SSCR0_SerClkDiv(2));
+               ssp_enable(&corgi_ssp_dev);
+       }
+
+       return ret;
+}
+
+static int corgi_ssp_remove(struct device *dev)
+{
+       ssp_exit(&corgi_ssp_dev);
+       return 0;
+}
+
+static int corgi_ssp_suspend(struct device *dev, u32 state, u32 level)
+{
+       if (level == SUSPEND_POWER_DOWN) {
+               ssp_flush(&corgi_ssp_dev);
+               ssp_save_state(&corgi_ssp_dev,&corgi_ssp_state);
+       }
+       return 0;
+}
+
+static int corgi_ssp_resume(struct device *dev, u32 level)
+{
+       if (level == RESUME_POWER_ON) {
+               GPSR0 = GPIO_bit(CORGI_GPIO_LCDCON_CS);  /* High - Disable LCD Control/Timing Gen */
+               GPSR0 = GPIO_bit(CORGI_GPIO_MAX1111_CS); /* High - Disable MAX1111*/
+               GPSR0 = GPIO_bit(CORGI_GPIO_ADS7846_CS); /* High - Disable ADS7846*/
+               ssp_restore_state(&corgi_ssp_dev,&corgi_ssp_state);
+               ssp_enable(&corgi_ssp_dev);
+       }
+       return 0;
+}
+
+static struct device_driver corgissp_driver = {
+       .name           = "corgi-ssp",
+       .bus            = &platform_bus_type,
+       .probe          = corgi_ssp_probe,
+       .remove         = corgi_ssp_remove,
+       .suspend        = corgi_ssp_suspend,
+       .resume         = corgi_ssp_resume,
+};
+
+int __init corgi_ssp_init(void)
+{
+       return driver_register(&corgissp_driver);
+}
+
+arch_initcall(corgi_ssp_init);
diff --git a/arch/arm/mach-pxa/ssp.c b/arch/arm/mach-pxa/ssp.c
new file mode 100644 (file)
index 0000000..50132a7
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ *  linux/arch/arm/mach-pxa/ssp.c
+ *
+ *  based on linux/arch/arm/mach-sa1100/ssp.c by Russell King
+ *
+ *  Copyright (C) 2003 Russell King.
+ *  Copyright (C) 2003 Wolfson Microelectronics PLC
+ *
+ * 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.
+ *
+ *  PXA2xx SSP driver.  This provides the generic core for simple
+ *  IO-based SSP applications and allows easy port setup for DMA access.
+ *
+ *  Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ *
+ *  Revision history:
+ *   22nd Aug 2003 Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/arch/ssp.h>
+#include <asm/arch/pxa-regs.h>
+
+static irqreturn_t ssp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct ssp_dev *dev = (struct ssp_dev*) dev_id;
+       unsigned int status = SSSR_P(dev->port);
+
+       SSSR_P(dev->port) = status; /* clear status bits */
+
+       if (status & SSSR_ROR)
+               printk(KERN_WARNING "SSP(%d): receiver overrun\n", dev->port);
+
+       if (status & SSSR_TUR)
+               printk(KERN_WARNING "SSP(%d): transmitter underrun\n", dev->port);
+
+       if (status & SSSR_BCE)
+               printk(KERN_WARNING "SSP(%d): bit count error\n", dev->port);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ssp_write_word - write a word to the SSP port
+ * @data: 32-bit, MSB justified data to write.
+ *
+ * Wait for a free entry in the SSP transmit FIFO, and write a data
+ * word to the SSP port.
+ *
+ * The caller is expected to perform the necessary locking.
+ *
+ * Returns:
+ *   %-ETIMEDOUT       timeout occurred (for future)
+ *   0                 success
+ */
+int ssp_write_word(struct ssp_dev *dev, u32 data)
+{
+       while (!(SSSR_P(dev->port) & SSSR_TNF))
+               cpu_relax();
+
+       SSDR_P(dev->port) = data;
+
+       return 0;
+}
+
+/**
+ * ssp_read_word - read a word from the SSP port
+ *
+ * Wait for a data word in the SSP receive FIFO, and return the
+ * received data.  Data is LSB justified.
+ *
+ * Note: Currently, if data is not expected to be received, this
+ * function will wait for ever.
+ *
+ * The caller is expected to perform the necessary locking.
+ *
+ * Returns:
+ *   %-ETIMEDOUT       timeout occurred (for future)
+ *   32-bit data       success
+ */
+int ssp_read_word(struct ssp_dev *dev)
+{
+       while (!(SSSR_P(dev->port) & SSSR_RNE))
+               cpu_relax();
+
+       return SSDR_P(dev->port);
+}
+
+/**
+ * ssp_flush - flush the transmit and receive FIFOs
+ *
+ * Wait for the SSP to idle, and ensure that the receive FIFO
+ * is empty.
+ *
+ * The caller is expected to perform the necessary locking.
+ */
+void ssp_flush(struct ssp_dev *dev)
+{
+       do {
+               while (SSSR_P(dev->port) & SSSR_RNE) {
+                       (void) SSDR_P(dev->port);
+               }
+       } while (SSSR_P(dev->port) & SSSR_BSY);
+}
+
+/**
+ * ssp_enable - enable the SSP port
+ *
+ * Turn on the SSP port.
+ */
+void ssp_enable(struct ssp_dev *dev)
+{
+       SSCR0_P(dev->port) |= SSCR0_SSE;
+}
+
+/**
+ * ssp_disable - shut down the SSP port
+ *
+ * Turn off the SSP port, optionally powering it down.
+ */
+void ssp_disable(struct ssp_dev *dev)
+{
+       SSCR0_P(dev->port) &= ~SSCR0_SSE;
+}
+
+/**
+ * ssp_save_state - save the SSP configuration
+ * @ssp: pointer to structure to save SSP configuration
+ *
+ * Save the configured SSP state for suspend.
+ */
+void ssp_save_state(struct ssp_dev *dev, struct ssp_state *ssp)
+{
+       ssp->cr0 = SSCR0_P(dev->port);
+       ssp->cr1 = SSCR1_P(dev->port);
+       ssp->to = SSTO_P(dev->port);
+       ssp->psp = SSPSP_P(dev->port);
+
+       SSCR0_P(dev->port) &= ~SSCR0_SSE;
+}
+
+/**
+ * ssp_restore_state - restore a previously saved SSP configuration
+ * @ssp: pointer to configuration saved by ssp_save_state
+ *
+ * Restore the SSP configuration saved previously by ssp_save_state.
+ */
+void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *ssp)
+{
+       SSSR_P(dev->port) = SSSR_ROR | SSSR_TUR | SSSR_BCE;
+
+       SSCR0_P(dev->port) = ssp->cr0 & ~SSCR0_SSE;
+       SSCR1_P(dev->port) = ssp->cr1;
+       SSTO_P(dev->port) = ssp->to;
+       SSPSP_P(dev->port) = ssp->psp;
+
+       SSCR0_P(dev->port) = ssp->cr0;
+}
+
+/**
+ * ssp_init - setup the SSP port
+ *
+ * initialise and claim resources for the SSP port.
+ *
+ * Returns:
+ *   %-ENODEV  if the SSP port is unavailable
+ *   %-EBUSY   if the resources are already in use
+ *   %0                on success
+ */
+int ssp_init(struct ssp_dev *dev, u32 port, u32 mode, u32 flags, u32 psp_flags,
+                                               u32 speed)
+{
+       int ret, irq;
+
+       if (!request_mem_region(__PREG(SSCR0_P(port)), 0x2c, "SSP")) {
+               return -EBUSY;
+       }
+
+       switch (port) {
+               case 1:
+                       irq = IRQ_SSP;
+                       break;
+#if defined (CONFIG_PXA27x)
+               case 2:
+                       irq = IRQ_SSP2;
+                       break;
+               case 3:
+                       irq = IRQ_SSP3;
+                       break;
+#else
+               case 2:
+                       irq = IRQ_NSSP;
+                       break;
+               case 3:
+                       irq = IRQ_ASSP;
+                       break;
+#endif
+               default:
+                       return -ENODEV;
+       }
+
+       dev->port = port;
+       dev->mode = mode;
+       dev->flags = flags;
+       dev->psp_flags = psp_flags;
+       dev->speed = speed;
+
+       /* set up port type, speed, port settings */
+       SSCR0_P(dev->port) = (dev->speed | dev->mode);
+       SSCR1_P(dev->port) = dev->flags;
+       SSPSP_P(dev->port) = dev->psp_flags;
+
+       ret = request_irq(irq, ssp_interrupt, 0, "SSP", dev);
+       if (ret)
+               goto out_region;
+
+       /* turn on SSP port clock */
+       switch (dev->port) {
+#if defined (CONFIG_PXA27x)
+               case 1:
+                       pxa_set_cken(CKEN23_SSP1, 1);
+                       break;
+               case 2:
+                       pxa_set_cken(CKEN3_SSP2, 1);
+                       break;
+               case 3:
+                       pxa_set_cken(CKEN4_SSP3, 1);
+                       break;
+#else
+               case 1:
+                       pxa_set_cken(CKEN3_SSP, 1);
+                       break;
+               case 2:
+                       pxa_set_cken(CKEN9_NSSP, 1);
+                       break;
+               case 3:
+                       pxa_set_cken(CKEN10_ASSP, 1);
+                       break;
+#endif
+       }
+
+       return 0;
+
+out_region:
+       release_mem_region(__PREG(SSCR0_P(dev->port)), 0x2c);
+       return ret;
+}
+
+/**
+ * ssp_exit - undo the effects of ssp_init
+ *
+ * release and free resources for the SSP port.
+ */
+void ssp_exit(struct ssp_dev *dev)
+{
+       int irq;
+
+       SSCR0_P(dev->port) &= ~SSCR0_SSE;
+
+       /* find irq, save power and turn off SSP port clock */
+       switch (dev->port) {
+#if defined (CONFIG_PXA27x)
+               case 1:
+                       irq = IRQ_SSP;
+                       pxa_set_cken(CKEN23_SSP1, 0);
+                       break;
+               case 2:
+                       irq = IRQ_SSP2;
+                       pxa_set_cken(CKEN3_SSP2, 0);
+                       break;
+               case 3:
+                       irq = IRQ_SSP3;
+                       pxa_set_cken(CKEN4_SSP3, 0);
+                       break;
+#else
+               case 1:
+                       irq = IRQ_SSP;
+                       pxa_set_cken(CKEN3_SSP, 0);
+                       break;
+               case 2:
+                       irq = IRQ_NSSP;
+                       pxa_set_cken(CKEN9_NSSP, 0);
+                       break;
+               case 3:
+                       irq = IRQ_ASSP;
+                       pxa_set_cken(CKEN10_ASSP, 0);
+                       break;
+#endif
+               default:
+                       printk(KERN_WARNING "SSP: tried to close invalid port\n");
+                       return;
+       }
+
+       free_irq(irq, dev);
+       release_mem_region(__PREG(SSCR0_P(dev->port)), 0x2c);
+}
+
+EXPORT_SYMBOL(ssp_write_word);
+EXPORT_SYMBOL(ssp_read_word);
+EXPORT_SYMBOL(ssp_flush);
+EXPORT_SYMBOL(ssp_enable);
+EXPORT_SYMBOL(ssp_disable);
+EXPORT_SYMBOL(ssp_save_state);
+EXPORT_SYMBOL(ssp_restore_state);
+EXPORT_SYMBOL(ssp_init);
+EXPORT_SYMBOL(ssp_exit);
diff --git a/arch/arm/mach-rpc/Makefile.boot b/arch/arm/mach-rpc/Makefile.boot
new file mode 100644 (file)
index 0000000..9c9e768
--- /dev/null
@@ -0,0 +1,4 @@
+   zreladdr-y  := 0x10008000
+params_phys-y  := 0x10000100
+initrd_phys-y  := 0x18000000
+
diff --git a/arch/arm/mach-s3c2410/Makefile.boot b/arch/arm/mach-s3c2410/Makefile.boot
new file mode 100644 (file)
index 0000000..7dab2a0
--- /dev/null
@@ -0,0 +1,3 @@
+   zreladdr-y  := 0x30008000
+params_phys-y  := 0x30000100
+
diff --git a/arch/arm/mach-s3c2410/mach-rx3715.c b/arch/arm/mach-s3c2410/mach-rx3715.c
new file mode 100644 (file)
index 0000000..c577c5f
--- /dev/null
@@ -0,0 +1,124 @@
+/* linux/arch/arm/mach-s3c2410/mach-rx3715.c
+ *
+ * Copyright (c) 2003,2004 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * http://www.handhelds.org/projects/rx3715.html
+ *
+ * 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.
+ *
+ * Modifications:
+ *     16-Sep-2004 BJD Copied from mach-h1940.c
+ *     25-Oct-2004 BJD Updates for 2.6.10-rc1
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/iomd.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/regs-serial.h>
+#include <asm/arch/regs-gpio.h>
+
+#include "s3c2410.h"
+#include "s3c2440.h"
+#include "clock.h"
+#include "devs.h"
+#include "cpu.h"
+#include "pm.h"
+
+static struct map_desc rx3715_iodesc[] __initdata = {
+       /* dump ISA space somewhere unused */
+
+       { S3C2410_VA_ISA_WORD, S3C2410_CS3, SZ_16M, MT_DEVICE },
+       { S3C2410_VA_ISA_BYTE, S3C2410_CS3, SZ_16M, MT_DEVICE },
+};
+
+static struct s3c2410_uartcfg rx3715_uartcfgs[] = {
+       [0] = {
+               .hwport      = 0,
+               .flags       = 0,
+               .ucon        = 0x3c5,
+               .ulcon       = 0x03,
+               .ufcon       = 0x51,
+       },
+       [1] = {
+               .hwport      = 1,
+               .flags       = 0,
+               .ucon        = 0x3c5,
+               .ulcon       = 0x03,
+               .ufcon       = 0x00,
+       },
+       /* IR port */
+       [2] = {
+               .hwport      = 2,
+               .uart_flags  = UPF_CONS_FLOW,
+               .ucon        = 0x3c5,
+               .ulcon       = 0x43,
+               .ufcon       = 0x51,
+       }
+};
+
+static struct platform_device *rx3715_devices[] __initdata = {
+       &s3c_device_usb,
+       &s3c_device_lcd,
+       &s3c_device_wdt,
+       &s3c_device_i2c,
+       &s3c_device_iis,
+};
+
+static struct s3c24xx_board rx3715_board __initdata = {
+       .devices       = rx3715_devices,
+       .devices_count = ARRAY_SIZE(rx3715_devices)
+};
+
+void __init rx3715_map_io(void)
+{
+       s3c24xx_xtal = 16934000;
+
+       s3c24xx_init_io(rx3715_iodesc, ARRAY_SIZE(rx3715_iodesc));
+       s3c2440_init_uarts(rx3715_uartcfgs, ARRAY_SIZE(rx3715_uartcfgs));
+       s3c24xx_set_board(&rx3715_board);
+}
+
+void __init rx3715_init_irq(void)
+{
+       s3c2410_init_irq();
+}
+
+#ifdef CONFIG_PM
+static void __init rx3715_init_machine(void)
+{
+       s3c2410_pm_init();
+}
+#else
+#define rx3715_init_machine NULL
+#endif
+
+MACHINE_START(RX3715, "IPAQ-RX3715")
+     MAINTAINER("Ben Dooks <ben@fluff.org>")
+     BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, S3C2410_VA_UART)
+     BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100)
+     MAPIO(rx3715_map_io)
+     INITIRQ(rx3715_init_irq)
+     INIT_MACHINE(rx3715_init_machine)
+     .timer            = &s3c2410_timer,
+MACHINE_END
diff --git a/arch/arm/mach-s3c2410/pm.c b/arch/arm/mach-s3c2410/pm.c
new file mode 100644 (file)
index 0000000..ea7c25a
--- /dev/null
@@ -0,0 +1,667 @@
+/* linux/arch/arm/mach-s3c2410/pm.c
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 Power Manager (Suspend-To-RAM) support
+ *
+ * See Documentation/arm/Samsung-S3C24XX/Suspend.txt for more information
+ *
+ * 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
+ *
+ * Parts based on arch/arm/mach-pxa/pm.c
+ *
+ * Thanks to Dimitry Andric for debugging
+*/
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/crc32.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+
+#include <asm/arch/regs-serial.h>
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-mem.h>
+#include <asm/arch/regs-irq.h>
+
+#include <asm/mach/time.h>
+
+#include "pm.h"
+
+/* for external use */
+
+unsigned long s3c_pm_flags;
+
+/* cache functions from arch/arm/mm/proc-arm920.S */
+
+extern void arm920_flush_kern_cache_all(void);
+
+#define PFX "s3c24xx-pm: "
+
+static struct sleep_save core_save[] = {
+       SAVE_ITEM(S3C2410_LOCKTIME),
+       SAVE_ITEM(S3C2410_CLKCON),
+
+       /* we restore the timings here, with the proviso that the board
+        * brings the system up in an slower, or equal frequency setting
+        * to the original system.
+        *
+        * if we cannot guarantee this, then things are going to go very
+        * wrong here, as we modify the refresh and both pll settings.
+        */
+
+       SAVE_ITEM(S3C2410_BWSCON),
+       SAVE_ITEM(S3C2410_BANKCON0),
+       SAVE_ITEM(S3C2410_BANKCON1),
+       SAVE_ITEM(S3C2410_BANKCON2),
+       SAVE_ITEM(S3C2410_BANKCON3),
+       SAVE_ITEM(S3C2410_BANKCON4),
+       SAVE_ITEM(S3C2410_BANKCON5),
+
+       SAVE_ITEM(S3C2410_CLKDIVN),
+       SAVE_ITEM(S3C2410_MPLLCON),
+       SAVE_ITEM(S3C2410_UPLLCON),
+       SAVE_ITEM(S3C2410_CLKSLOW),
+       SAVE_ITEM(S3C2410_REFRESH),
+};
+
+/* this lot should be really saved by the IRQ code */
+static struct sleep_save irq_save[] = {
+       SAVE_ITEM(S3C2410_EXTINT0),
+       SAVE_ITEM(S3C2410_EXTINT1),
+       SAVE_ITEM(S3C2410_EXTINT2),
+       SAVE_ITEM(S3C2410_EINFLT0),
+       SAVE_ITEM(S3C2410_EINFLT1),
+       SAVE_ITEM(S3C2410_EINFLT2),
+       SAVE_ITEM(S3C2410_EINFLT3),
+       SAVE_ITEM(S3C2410_EINTMASK),
+       SAVE_ITEM(S3C2410_INTMSK)
+};
+
+static struct sleep_save gpio_save[] = {
+       SAVE_ITEM(S3C2410_GPACON),
+       SAVE_ITEM(S3C2410_GPADAT),
+
+       SAVE_ITEM(S3C2410_GPBCON),
+       SAVE_ITEM(S3C2410_GPBDAT),
+       SAVE_ITEM(S3C2410_GPBUP),
+
+       SAVE_ITEM(S3C2410_GPCCON),
+       SAVE_ITEM(S3C2410_GPCDAT),
+       SAVE_ITEM(S3C2410_GPCUP),
+
+       SAVE_ITEM(S3C2410_GPDCON),
+       SAVE_ITEM(S3C2410_GPDDAT),
+       SAVE_ITEM(S3C2410_GPDUP),
+
+       SAVE_ITEM(S3C2410_GPECON),
+       SAVE_ITEM(S3C2410_GPEDAT),
+       SAVE_ITEM(S3C2410_GPEUP),
+
+       SAVE_ITEM(S3C2410_GPFCON),
+       SAVE_ITEM(S3C2410_GPFDAT),
+       SAVE_ITEM(S3C2410_GPFUP),
+
+       SAVE_ITEM(S3C2410_GPGCON),
+       SAVE_ITEM(S3C2410_GPGDAT),
+       SAVE_ITEM(S3C2410_GPGUP),
+
+       SAVE_ITEM(S3C2410_GPHCON),
+       SAVE_ITEM(S3C2410_GPHDAT),
+       SAVE_ITEM(S3C2410_GPHUP),
+
+       SAVE_ITEM(S3C2410_DCLKCON),
+};
+
+#ifdef CONFIG_S3C2410_PM_DEBUG
+
+#define SAVE_UART(va) \
+       SAVE_ITEM((va) + S3C2410_ULCON), \
+       SAVE_ITEM((va) + S3C2410_UCON), \
+       SAVE_ITEM((va) + S3C2410_UFCON), \
+       SAVE_ITEM((va) + S3C2410_UMCON), \
+       SAVE_ITEM((va) + S3C2410_UBRDIV)
+
+static struct sleep_save uart_save[] = {
+       SAVE_UART(S3C2410_VA_UART0),
+       SAVE_UART(S3C2410_VA_UART1),
+       SAVE_UART(S3C2410_VA_UART2),
+};
+
+/* debug
+ *
+ * we send the debug to printascii() to allow it to be seen if the
+ * system never wakes up from the sleep
+*/
+
+extern void printascii(const char *);
+
+static void pm_dbg(const char *fmt, ...)
+{
+       va_list va;
+       char buff[256];
+
+       va_start(va, fmt);
+       vsprintf(buff, fmt, va);
+       va_end(va);
+
+       printascii(buff);
+}
+
+static void s3c2410_pm_debug_init(void)
+{
+       unsigned long tmp = __raw_readl(S3C2410_CLKCON);
+
+       /* re-start uart clocks */
+       tmp |= S3C2410_CLKCON_UART0;
+       tmp |= S3C2410_CLKCON_UART1;
+       tmp |= S3C2410_CLKCON_UART2;
+
+       __raw_writel(tmp, S3C2410_CLKCON);
+       udelay(10);
+}
+
+#define DBG(fmt...) pm_dbg(fmt)
+#else
+#define DBG(fmt...) printk(KERN_DEBUG fmt)
+
+#define s3c2410_pm_debug_init() do { } while(0)
+
+static struct sleep_save uart_save[] = {};
+#endif
+
+#if defined(CONFIG_S3C2410_PM_CHECK) && CONFIG_S3C2410_PM_CHECK_CHUNKSIZE != 0
+
+/* suspend checking code...
+ *
+ * this next area does a set of crc checks over all the installed
+ * memory, so the system can verify if the resume was ok.
+ *
+ * CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC,
+ * increasing it will mean that the area corrupted will be less easy to spot,
+ * and reducing the size will cause the CRC save area to grow
+*/
+
+#define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024)
+
+static u32 crc_size;   /* size needed for the crc block */
+static u32 *crcs;      /* allocated over suspend/resume */
+
+typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg);
+
+/* s3c2410_pm_run_res
+ *
+ * go thorugh the given resource list, and look for system ram
+*/
+
+static void s3c2410_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg)
+{
+       while (ptr != NULL) {
+               if (ptr->child != NULL)
+                       s3c2410_pm_run_res(ptr->child, fn, arg);
+
+               if ((ptr->flags & IORESOURCE_MEM) &&
+                   strcmp(ptr->name, "System RAM") == 0) {
+                       DBG("Found system RAM at %08lx..%08lx\n",
+                           ptr->start, ptr->end);
+                       arg = (fn)(ptr, arg);
+               }
+
+               ptr = ptr->sibling;
+       }
+}
+
+static void s3c2410_pm_run_sysram(run_fn_t fn, u32 *arg)
+{
+       s3c2410_pm_run_res(&iomem_resource, fn, arg);
+}
+
+static u32 *s3c2410_pm_countram(struct resource *res, u32 *val)
+{
+       u32 size = (u32)(res->end - res->start)+1;
+
+       size += CHECK_CHUNKSIZE-1;
+       size /= CHECK_CHUNKSIZE;
+
+       DBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size);
+
+       *val += size * sizeof(u32);
+       return val;
+}
+
+/* s3c2410_pm_prepare_check
+ *
+ * prepare the necessary information for creating the CRCs. This
+ * must be done before the final save, as it will require memory
+ * allocating, and thus touching bits of the kernel we do not
+ * know about.
+*/
+
+static void s3c2410_pm_check_prepare(void)
+{
+       crc_size = 0;
+
+       s3c2410_pm_run_sysram(s3c2410_pm_countram, &crc_size);
+
+       DBG("s3c2410_pm_prepare_check: %u checks needed\n", crc_size);
+
+       crcs = kmalloc(crc_size+4, GFP_KERNEL);
+       if (crcs == NULL)
+               printk(KERN_ERR "Cannot allocated CRC save area\n");
+}
+
+static u32 *s3c2410_pm_makecheck(struct resource *res, u32 *val)
+{
+       unsigned long addr, left;
+
+       for (addr = res->start; addr < res->end;
+            addr += CHECK_CHUNKSIZE) {
+               left = res->end - addr;
+
+               if (left > CHECK_CHUNKSIZE)
+                       left = CHECK_CHUNKSIZE;
+
+               *val = crc32_le(~0, phys_to_virt(addr), left);
+               val++;
+       }
+
+       return val;
+}
+
+/* s3c2410_pm_check_store
+ *
+ * compute the CRC values for the memory blocks before the final
+ * sleep.
+*/
+
+static void s3c2410_pm_check_store(void)
+{
+       if (crcs != NULL)
+               s3c2410_pm_run_sysram(s3c2410_pm_makecheck, crcs);
+}
+
+/* in_region
+ *
+ * return TRUE if the area defined by ptr..ptr+size contatins the
+ * what..what+whatsz
+*/
+
+static inline int in_region(void *ptr, int size, void *what, size_t whatsz)
+{
+       if ((what+whatsz) < ptr)
+               return 0;
+
+       if (what > (ptr+size))
+               return 0;
+
+       return 1;
+}
+
+static u32 *s3c2410_pm_runcheck(struct resource *res, u32 *val)
+{
+       void *save_at = phys_to_virt(s3c2410_sleep_save_phys);
+       unsigned long addr;
+       unsigned long left;
+       void *ptr;
+       u32 calc;
+
+       for (addr = res->start; addr < res->end;
+            addr += CHECK_CHUNKSIZE) {
+               left = res->end - addr;
+
+               if (left > CHECK_CHUNKSIZE)
+                       left = CHECK_CHUNKSIZE;
+
+               ptr = phys_to_virt(addr);
+
+               if (in_region(ptr, left, crcs, crc_size)) {
+                       DBG("skipping %08lx, has crc block in\n", addr);
+                       goto skip_check;
+               }
+
+               if (in_region(ptr, left, save_at, 32*4 )) {
+                       DBG("skipping %08lx, has save block in\n", addr);
+                       goto skip_check;
+               }
+
+               /* calculate and check the checksum */
+
+               calc = crc32_le(~0, ptr, left);
+               if (calc != *val) {
+                       printk(KERN_ERR PFX "Restore CRC error at "
+                              "%08lx (%08x vs %08x)\n", addr, calc, *val);
+
+                       DBG("Restore CRC error at %08lx (%08x vs %08x)\n",
+                           addr, calc, *val);
+               }
+
+       skip_check:
+               val++;
+       }
+
+       return val;
+}
+
+/* s3c2410_pm_check_restore
+ *
+ * check the CRCs after the restore event and free the memory used
+ * to hold them
+*/
+
+static void s3c2410_pm_check_restore(void)
+{
+       if (crcs != NULL) {
+               s3c2410_pm_run_sysram(s3c2410_pm_runcheck, crcs);
+               kfree(crcs);
+               crcs = NULL;
+       }
+}
+
+#else
+
+#define s3c2410_pm_check_prepare() do { } while(0)
+#define s3c2410_pm_check_restore() do { } while(0)
+#define s3c2410_pm_check_store()   do { } while(0)
+#endif
+
+/* helper functions to save and restore register state */
+
+void s3c2410_pm_do_save(struct sleep_save *ptr, int count)
+{
+       for (; count > 0; count--, ptr++) {
+               ptr->val = __raw_readl(ptr->reg);
+               DBG("saved %08lx value %08lx\n", ptr->reg, ptr->val);
+       }
+}
+
+/* s3c2410_pm_do_restore
+ *
+ * restore the system from the given list of saved registers
+ *
+ * Note, we do not use DBG() in here, as the system may not have
+ * restore the UARTs state yet
+*/
+
+void s3c2410_pm_do_restore(struct sleep_save *ptr, int count)
+{
+       for (; count > 0; count--, ptr++) {
+               printk(KERN_DEBUG "restore %08lx (restore %08lx, was %08x)\n",
+                      ptr->reg, ptr->val, __raw_readl(ptr->reg));
+
+               __raw_writel(ptr->val, ptr->reg);
+       }
+}
+
+/* s3c2410_pm_do_restore_core
+ *
+ * similar to s3c2410_pm_do_restore_core
+ *
+ * WARNING: Do not put any debug in here that may effect memory or use
+ * peripherals, as things may be changing!
+*/
+
+static void s3c2410_pm_do_restore_core(struct sleep_save *ptr, int count)
+{
+       for (; count > 0; count--, ptr++) {
+               __raw_writel(ptr->val, ptr->reg);
+       }
+}
+
+/* s3c2410_pm_show_resume_irqs
+ *
+ * print any IRQs asserted at resume time (ie, we woke from)
+*/
+
+static void s3c2410_pm_show_resume_irqs(int start, unsigned long which,
+                                       unsigned long mask)
+{
+       int i;
+
+       which &= ~mask;
+
+       for (i = 0; i <= 31; i++) {
+               if ((which) & (1L<<i)) {
+                       DBG("IRQ %d asserted at resume\n", start+i);
+               }
+       }
+}
+
+/* s3c2410_pm_check_resume_pin
+ *
+ * check to see if the pin is configured correctly for sleep mode, and
+ * make any necessary adjustments if it is not
+*/
+
+static void s3c2410_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs)
+{
+       unsigned long irqstate;
+       unsigned long pinstate;
+       int irq = s3c2410_gpio_getirq(pin);
+
+       if (irqoffs < 4)
+               irqstate = s3c_irqwake_intmask & (1L<<irqoffs);
+       else
+               irqstate = s3c_irqwake_eintmask & (1L<<irqoffs);
+
+       pinstate = s3c2410_gpio_getcfg(pin);
+       pinstate >>= S3C2410_GPIO_OFFSET(pin)*2;
+
+       if (!irqstate) {
+               if (pinstate == 0x02)
+                       DBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin);
+       } else {
+               if (pinstate == 0x02) {
+                       DBG("Disabling IRQ %d (pin %d)\n", irq, pin);
+                       s3c2410_gpio_cfgpin(pin, 0x00);
+               }
+       }
+}
+
+/* s3c2410_pm_configure_extint
+ *
+ * configure all external interrupt pins
+*/
+
+static void s3c2410_pm_configure_extint(void)
+{
+       int pin;
+
+       /* for each of the external interrupts (EINT0..EINT15) we
+        * need to check wether it is an external interrupt source,
+        * and then configure it as an input if it is not
+       */
+
+       for (pin = S3C2410_GPF0; pin <= S3C2410_GPF7; pin++) {
+               s3c2410_pm_check_resume_pin(pin, pin - S3C2410_GPF0);
+       }
+
+       for (pin = S3C2410_GPG0; pin <= S3C2410_GPG7; pin++) {
+               s3c2410_pm_check_resume_pin(pin, (pin - S3C2410_GPG0)+8);
+       }
+}
+
+#define any_allowed(mask, allow) (((mask) & (allow)) != (allow))
+
+/* s3c2410_pm_enter
+ *
+ * central control for sleep/resume process
+*/
+
+static int s3c2410_pm_enter(suspend_state_t state)
+{
+       unsigned long regs_save[16];
+       unsigned long tmp;
+
+       /* ensure the debug is initialised (if enabled) */
+
+       s3c2410_pm_debug_init();
+
+       DBG("s3c2410_pm_enter(%d)\n", state);
+
+       if (state != PM_SUSPEND_MEM) {
+               printk(KERN_ERR PFX "error: only PM_SUSPEND_MEM supported\n");
+               return -EINVAL;
+       }
+
+       /* check if we have anything to wake-up with... bad things seem
+        * to happen if you suspend with no wakeup (system will often
+        * require a full power-cycle)
+       */
+
+       if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
+           !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
+               printk(KERN_ERR PFX "No sources enabled for wake-up!\n");
+               printk(KERN_ERR PFX "Aborting sleep\n");
+               return -EINVAL;
+       }
+
+       /* prepare check area if configured */
+
+       s3c2410_pm_check_prepare();
+
+       /* store the physical address of the register recovery block */
+
+       s3c2410_sleep_save_phys = virt_to_phys(regs_save);
+
+       DBG("s3c2410_sleep_save_phys=0x%08lx\n", s3c2410_sleep_save_phys);
+
+       /* ensure at least GESTATUS3 has the resume address */
+
+       __raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2410_GSTATUS3);
+
+       DBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3));
+       DBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4));
+
+       /* save all necessary core registers not covered by the drivers */
+
+       s3c2410_pm_do_save(gpio_save, ARRAY_SIZE(gpio_save));
+       s3c2410_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
+       s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save));
+       s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save));
+
+       /* set the irq configuration for wake */
+
+       s3c2410_pm_configure_extint();
+
+       DBG("sleep: irq wakeup masks: %08lx,%08lx\n",
+           s3c_irqwake_intmask, s3c_irqwake_eintmask);
+
+       __raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK);
+       __raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK);
+
+       /* ack any outstanding external interrupts before we go to sleep */
+
+       __raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND);
+
+       /* flush cache back to ram */
+
+       arm920_flush_kern_cache_all();
+
+       s3c2410_pm_check_store();
+
+       // need to make some form of time-delta
+
+       /* send the cpu to sleep... */
+
+       __raw_writel(0x00, S3C2410_CLKCON);  /* turn off clocks over sleep */
+
+       s3c2410_cpu_suspend(regs_save);
+
+       /* unset the return-from-sleep flag, to ensure reset */
+
+       tmp = __raw_readl(S3C2410_GSTATUS2);
+       tmp &= S3C2410_GSTATUS2_OFFRESET;
+       __raw_writel(tmp, S3C2410_GSTATUS2);
+
+       /* restore the system state */
+
+       s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
+       s3c2410_pm_do_restore(gpio_save, ARRAY_SIZE(gpio_save));
+       s3c2410_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
+       s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save));
+
+       s3c2410_pm_debug_init();
+
+       /* check what irq (if any) restored the system */
+
+       DBG("post sleep: IRQs 0x%08x, 0x%08x\n",
+           __raw_readl(S3C2410_SRCPND),
+           __raw_readl(S3C2410_EINTPEND));
+
+       s3c2410_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND),
+                                   s3c_irqwake_intmask);
+
+       s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND),
+                                   s3c_irqwake_eintmask);
+
+       DBG("post sleep, preparing to return\n");
+
+       s3c2410_pm_check_restore();
+
+       /* ok, let's return from sleep */
+
+       DBG("S3C2410 PM Resume (post-restore)\n");
+       return 0;
+}
+
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+static int s3c2410_pm_prepare(suspend_state_t state)
+{
+       return 0;
+}
+
+/*
+ * Called after devices are re-setup, but before processes are thawed.
+ */
+static int s3c2410_pm_finish(suspend_state_t state)
+{
+       return 0;
+}
+
+/*
+ * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
+ */
+static struct pm_ops s3c2410_pm_ops = {
+       .pm_disk_mode   = PM_DISK_FIRMWARE,
+       .prepare        = s3c2410_pm_prepare,
+       .enter          = s3c2410_pm_enter,
+       .finish         = s3c2410_pm_finish,
+};
+
+/* s3c2410_pm_init
+ *
+ * Attach the power management functions. This should be called
+ * from the board specific initialisation if the board supports
+ * it.
+*/
+
+int __init s3c2410_pm_init(void)
+{
+       printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n");
+
+       pm_set_ops(&s3c2410_pm_ops);
+       return 0;
+}
diff --git a/arch/arm/mach-s3c2410/pm.h b/arch/arm/mach-s3c2410/pm.h
new file mode 100644 (file)
index 0000000..dcbe714
--- /dev/null
@@ -0,0 +1,59 @@
+/* linux/arch/arm/mach-s3c2410/pm.h
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ *     Written by Ben Dooks, <ben@simtec.co.uk>
+ *
+ * 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.
+*/
+
+/* s3c2410_pm_init
+ *
+ * called from board at initialisation time to setup the power
+ * management
+*/
+
+#ifdef CONFIG_PM
+
+extern __init int s3c2410_pm_init(void);
+
+#else
+
+static inline int s3c2410_pm_init(void)
+{
+       return 0;
+}
+#endif
+
+/* configuration for the IRQ mask over sleep */
+extern unsigned long s3c_irqwake_intmask;
+extern unsigned long s3c_irqwake_eintmask;
+
+/* IRQ masks for IRQs allowed to go to sleep (see irq.c) */
+extern unsigned long s3c_irqwake_intallow;
+extern unsigned long s3c_irqwake_eintallow;
+
+/* Flags for PM Control */
+
+extern unsigned long s3c_pm_flags;
+
+/* from sleep.S */
+
+extern void s3c2410_cpu_suspend(unsigned long *saveblk);
+extern void s3c2410_cpu_resume(void);
+
+extern unsigned long s3c2410_sleep_save_phys;
+
+/* sleep save info */
+
+struct sleep_save {
+       unsigned long   reg;
+       unsigned long   val;
+};
+
+#define SAVE_ITEM(x) \
+       { .reg = (x) }
+
+extern void s3c2410_pm_do_save(struct sleep_save *ptr, int count);
+extern void s3c2410_pm_do_restore(struct sleep_save *ptr, int count);
diff --git a/arch/arm/mach-s3c2410/sleep.S b/arch/arm/mach-s3c2410/sleep.S
new file mode 100644 (file)
index 0000000..61768da
--- /dev/null
@@ -0,0 +1,180 @@
+/* linux/arch/arm/mach-s3c2410/sleep.S
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 Power Manager (Suspend-To-RAM) support
+ *
+ * Based on PXA/SA1100 sleep code by:
+ *     Nicolas Pitre, (c) 2002 Monta Vista Software Inc
+ *     Cliff Brake, (c) 2001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/hardware.h>
+#include <asm/arch/map.h>
+
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/regs-mem.h>
+#include <asm/arch/regs-serial.h>
+
+/* CONFIG_DEBUG_RESUME is dangerous if your bootloader does not
+ * reset the UART configuration, only enable if you really need this!
+*/
+//#define CONFIG_DEBUG_RESUME
+
+       .text
+
+       /* s3c2410_cpu_suspend
+        *
+        * put the cpu into sleep mode
+        *
+        * entry:
+        *      r0 = sleep save block
+       */
+
+ENTRY(s3c2410_cpu_suspend)
+       stmfd   sp!, { r4 - r12, lr }
+
+       @@ store co-processor registers
+
+       mrc     p15, 0, r4, c15, c1, 0  @ CP access register
+       mrc     p15, 0, r5, c13, c0, 0  @ PID
+       mrc     p15, 0, r6, c3, c0, 0   @ Domain ID
+       mrc     p15, 0, r7, c2, c0, 0   @ translation table base address
+       mrc     p15, 0, r8, c2, c0, 0   @ auxiliary control register
+       mrc     p15, 0, r9, c1, c0, 0   @ control register
+
+       stmia   r0, { r4 - r13 }
+
+       @@ flush the caches to ensure everything is back out to
+       @@ SDRAM before the core powers down
+
+       bl      arm920_flush_kern_cache_all
+
+       @@ prepare cpu to sleep
+
+       ldr     r4, =S3C2410_REFRESH
+       ldr     r5, =S3C2410_MISCCR
+       ldr     r6, =S3C2410_CLKCON
+       ldr     r7, [ r4 ]              @ get REFRESH (and ensure in TLB)
+       ldr     r8, [ r5 ]              @ get MISCCR (and ensure in TLB)
+       ldr     r9, [ r6 ]              @ get CLKCON (and ensure in TLB)
+
+       orr     r7, r7, #S3C2410_REFRESH_SELF   @ SDRAM sleep command
+       orr     r8, r8, #S3C2410_MISCCR_SDSLEEP @ SDRAM power-down signals
+       orr     r9, r9, #S3C2410_CLKCON_POWER   @ power down command
+
+       teq     pc, #0                  @ first as a trial-run to load cache
+       bl      s3c2410_do_sleep
+       teq     r0, r0                  @ now do it for real
+       b       s3c2410_do_sleep        @
+
+       @@ align next bit of code to cache line
+       .align  8
+s3c2410_do_sleep:
+       streq   r7, [ r4 ]                      @ SDRAM sleep command
+       streq   r8, [ r5 ]                      @ SDRAM power-down config
+       streq   r9, [ r6 ]                      @ CPU sleep
+1:     beq     1b
+       mov     pc, r14
+
+       @@ return to the caller, after having the MMU
+       @@ turned on, this restores the last bits from the
+       @@ stack
+resume_with_mmu:
+       ldmfd   sp!, { r4 - r12, pc }
+
+       .ltorg
+
+       @@ the next bits sit in the .data segment, even though they
+       @@ happen to be code... the s3c2410_sleep_save_phys needs to be
+       @@ accessed by the resume code before it can restore the MMU.
+       @@ This means that the variable has to be close enough for the
+       @@ code to read it... since the .text segment needs to be RO,
+       @@ the data segment can be the only place to put this code.
+
+       .data
+
+       .global s3c2410_sleep_save_phys
+s3c2410_sleep_save_phys:
+       .word   0
+
+       /* s3c2410_cpu_resume
+        *
+        * resume code entry for bootloader to call
+        *
+        * we must put this code here in the data segment as we have no
+        * other way of restoring the stack pointer after sleep, and we
+        * must not write to the code segment (code is read-only)
+       */
+
+ENTRY(s3c2410_cpu_resume)
+       mov     r0, #PSR_I_BIT | PSR_F_BIT | MODE_SVC
+       msr     cpsr_c, r0
+
+       @@ load UART to allow us to print the two characters for
+       @@ resume debug
+
+       mov     r2, #S3C2410_PA_UART & 0xff000000
+       orr     r2, r2, #S3C2410_PA_UART & 0xff000
+
+#if 0
+       /* SMDK2440 LED set */
+       mov     r14, #S3C2410_PA_GPIO
+       ldr     r12, [ r14, #0x54 ]
+       bic     r12, r12, #3<<4
+       orr     r12, r12, #1<<7
+       str     r12, [ r14, #0x54 ]
+#endif
+
+#ifdef CONFIG_DEBUG_RESUME
+       mov     r3, #'L'
+       strb    r3, [ r2, #S3C2410_UTXH ]
+1001:
+       ldrb    r14, [ r3, #S3C2410_UTRSTAT ]
+       tst     r14, #S3C2410_UTRSTAT_TXE
+       beq     1001b
+#endif /* CONFIG_DEBUG_RESUME */
+
+       mov     r1, #0
+       mcr     p15, 0, r1, c8, c7, 0           @@ invalidate I & D TLBs
+       mcr     p15, 0, r1, c7, c7, 0           @@ invalidate I & D caches
+
+       ldr     r0, s3c2410_sleep_save_phys     @ address of restore block
+       ldmia   r0, { r4 - r13 }
+
+       mcr     p15, 0, r4, c15, c1, 0          @ CP access register
+       mcr     p15, 0, r5, c13, c0, 0          @ PID
+       mcr     p15, 0, r6, c3, c0, 0           @ Domain ID
+       mcr     p15, 0, r7, c2, c0, 0           @ translation table base
+       mcr     p15, 0, r8, c1, c1, 0           @ auxilliary control
+
+#ifdef CONFIG_DEBUG_RESUME
+       mov     r3, #'R'
+       strb    r3, [ r2, #S3C2410_UTXH ]
+#endif
+
+       ldr     r2, =resume_with_mmu
+       mcr     p15, 0, r9, c1, c0, 0           @ turn on MMU, etc
+       nop                                     @ second-to-last before mmu
+       mov     pc, r2                          @ go back to virtual address
+
+       .ltorg
diff --git a/arch/arm/mach-sa1100/Makefile.boot b/arch/arm/mach-sa1100/Makefile.boot
new file mode 100644 (file)
index 0000000..a56ad04
--- /dev/null
@@ -0,0 +1,7 @@
+   zreladdr-y  := 0xc0008000
+ifeq ($(CONFIG_ARCH_SA1100),y)
+   zreladdr-$(CONFIG_SA1111)           := 0xc0208000
+endif
+params_phys-y  := 0xc0000100
+initrd_phys-y  := 0xc0800000
+
diff --git a/arch/arm/mach-shark/Makefile.boot b/arch/arm/mach-shark/Makefile.boot
new file mode 100644 (file)
index 0000000..4320f8b
--- /dev/null
@@ -0,0 +1,2 @@
+   zreladdr-y  := 0x08008000
+
diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig
new file mode 100644 (file)
index 0000000..8d787f4
--- /dev/null
@@ -0,0 +1,16 @@
+menu "Versatile platform type"
+       depends on ARCH_VERSATILE
+
+config ARCH_VERSATILE_PB
+       bool "Support Versatile/PB platform"
+       default y
+       help
+         Include support for the ARM(R) Versatile/PB platform.
+
+config MACH_VERSATILE_AB
+       bool "Support Versatile/AB platform"
+       default n
+       help
+         Include support for the ARM(R) Versatile/AP platform.
+
+endmenu
diff --git a/arch/arm/mach-versatile/Makefile.boot b/arch/arm/mach-versatile/Makefile.boot
new file mode 100644 (file)
index 0000000..c7e75ac
--- /dev/null
@@ -0,0 +1,4 @@
+   zreladdr-y  := 0x00008000
+params_phys-y  := 0x00000100
+initrd_phys-y  := 0x00800000
+
diff --git a/arch/arm/mach-versatile/core.h b/arch/arm/mach-versatile/core.h
new file mode 100644 (file)
index 0000000..588c206
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ *  linux/arch/arm/mach-versatile/core.h
+ *
+ *  Copyright (C) 2004 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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 __ASM_ARCH_VERSATILE_H
+#define __ASM_ARCH_VERSATILE_H
+
+#include <asm/hardware/amba.h>
+
+extern void __init versatile_init(void);
+extern void __init versatile_init_irq(void);
+extern void __init versatile_map_io(void);
+extern struct sys_timer versatile_timer;
+extern unsigned int mmc_status(struct device *dev);
+
+#define AMBA_DEVICE(name,busid,base,plat)                      \
+static struct amba_device name##_device = {                    \
+       .dev            = {                                     \
+               .coherent_dma_mask = ~0,                        \
+               .bus_id = busid,                                \
+               .platform_data = plat,                          \
+       },                                                      \
+       .res            = {                                     \
+               .start  = VERSATILE_##base##_BASE,              \
+               .end    = (VERSATILE_##base##_BASE) + SZ_4K - 1,\
+               .flags  = IORESOURCE_MEM,                       \
+       },                                                      \
+       .dma_mask       = ~0,                                   \
+       .irq            = base##_IRQ,                           \
+       /* .dma         = base##_DMA,*/                         \
+}
+
+#endif
diff --git a/arch/arm/mach-versatile/versatile_ab.c b/arch/arm/mach-versatile/versatile_ab.c
new file mode 100644 (file)
index 0000000..d332084
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *  linux/arch/arm/mach-versatile/versatile_ab.c
+ *
+ *  Copyright (C) 2004 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/amba.h>
+
+#include <asm/mach/arch.h>
+
+#include "core.h"
+
+MACHINE_START(VERSATILE_AB, "ARM-Versatile AB")
+       MAINTAINER("ARM Ltd/Deep Blue Solutions Ltd")
+       BOOT_MEM(0x00000000, 0x101f1000, 0xf11f1000)
+       BOOT_PARAMS(0x00000100)
+       MAPIO(versatile_map_io)
+       INITIRQ(versatile_init_irq)
+       .timer          = &versatile_timer,
+       INIT_MACHINE(versatile_init)
+MACHINE_END
diff --git a/arch/arm/mach-versatile/versatile_pb.c b/arch/arm/mach-versatile/versatile_pb.c
new file mode 100644 (file)
index 0000000..2702099
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *  linux/arch/arm/mach-versatile/versatile_pb.c
+ *
+ *  Copyright (C) 2004 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/amba.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/mmc.h>
+
+#include "core.h"
+
+#if 1
+#define IRQ_MMCI1A     IRQ_VICSOURCE23
+#else
+#define IRQ_MMCI1A     IRQ_SIC_MMCI1A
+#endif
+
+static struct mmc_platform_data mmc1_plat_data = {
+       .ocr_mask       = MMC_VDD_32_33|MMC_VDD_33_34,
+       .status         = mmc_status,
+};
+
+#define UART3_IRQ      { IRQ_SIC_UART3, NO_IRQ }
+#define UART3_DMA      { 0x86, 0x87 }
+#define SCI1_IRQ       { IRQ_SIC_SCI3, NO_IRQ }
+#define SCI1_DMA       { 0x88, 0x89 }
+#define MMCI1_IRQ      { IRQ_MMCI1A, IRQ_SIC_MMCI1B }
+#define MMCI1_DMA      { 0x85, 0 }
+
+/*
+ * These devices are connected via the core APB bridge
+ */
+#define GPIO2_IRQ      { IRQ_GPIOINT2, NO_IRQ }
+#define GPIO2_DMA      { 0, 0 }
+#define GPIO3_IRQ      { IRQ_GPIOINT3, NO_IRQ }
+#define GPIO3_DMA      { 0, 0 }
+
+/*
+ * These devices are connected via the DMA APB bridge
+ */
+
+/* FPGA Primecells */
+AMBA_DEVICE(uart3, "fpga:09", UART3,    NULL);
+AMBA_DEVICE(sci1,  "fpga:0a", SCI1,     NULL);
+AMBA_DEVICE(mmc1,  "fpga:0b", MMCI1,    &mmc1_plat_data);
+
+/* DevChip Primecells */
+AMBA_DEVICE(gpio2, "dev:e6",  GPIO2,    NULL);
+AMBA_DEVICE(gpio3, "dev:e7",  GPIO3,    NULL);
+
+static struct amba_device *amba_devs[] __initdata = {
+       &uart3_device,
+       &gpio2_device,
+       &gpio3_device,
+       &sci1_device,
+       &mmc1_device,
+};
+
+static int __init versatile_pb_init(void)
+{
+       int i;
+
+       if (machine_is_versatile_pb()) {
+               for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
+                       struct amba_device *d = amba_devs[i];
+                       amba_device_register(d, &iomem_resource);
+               }
+       }
+
+       return 0;
+}
+
+arch_initcall(versatile_pb_init);
+
+MACHINE_START(VERSATILE_PB, "ARM-Versatile PB")
+       MAINTAINER("ARM Ltd/Deep Blue Solutions Ltd")
+       BOOT_MEM(0x00000000, 0x101f1000, 0xf11f1000)
+       BOOT_PARAMS(0x00000100)
+       MAPIO(versatile_map_io)
+       INITIRQ(versatile_init_irq)
+       .timer          = &versatile_timer,
+       INIT_MACHINE(versatile_init)
+MACHINE_END
diff --git a/arch/arm26/kernel/calls.S b/arch/arm26/kernel/calls.S
new file mode 100644 (file)
index 0000000..e3d2768
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ *  linux/arch/arm26/kernel/calls.S
+ *
+ *  Copyright (C) 2003 Ian Molton
+ *
+ * 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.
+ *
+ *  FIXME
+ *  This file is included twice in entry.S which may not be necessary
+ */
+
+//FIXME - clearly NR_syscalls is never defined here
+
+#ifndef NR_syscalls
+#define NR_syscalls 256
+#else
+
+__syscall_start:
+/* 0 */                .long   sys_ni_syscall
+               .long   sys_exit
+               .long   sys_fork_wrapper
+               .long   sys_read
+               .long   sys_write
+/* 5 */                .long   sys_open
+               .long   sys_close
+               .long   sys_ni_syscall          /* was sys_waitpid */
+               .long   sys_creat
+               .long   sys_link
+/* 10 */       .long   sys_unlink
+               .long   sys_execve_wrapper
+               .long   sys_chdir
+               .long   sys_time                /* used by libc4 */
+               .long   sys_mknod
+/* 15 */       .long   sys_chmod
+               .long   sys_lchown16
+               .long   sys_ni_syscall          /* was sys_break */
+               .long   sys_ni_syscall          /* was sys_stat */
+               .long   sys_lseek
+/* 20 */       .long   sys_getpid
+               .long   sys_mount
+               .long   sys_oldumount           /* used by libc4 */
+               .long   sys_setuid16
+               .long   sys_getuid16
+/* 25 */       .long   sys_stime
+               .long   sys_ptrace
+               .long   sys_alarm               /* used by libc4 */
+               .long   sys_ni_syscall          /* was sys_fstat */
+               .long   sys_pause
+/* 30 */       .long   sys_utime               /* used by libc4 */
+               .long   sys_ni_syscall          /* was sys_stty */
+               .long   sys_ni_syscall          /* was sys_getty */
+               .long   sys_access
+               .long   sys_nice
+/* 35 */       .long   sys_ni_syscall          /* was sys_ftime */
+               .long   sys_sync
+               .long   sys_kill
+               .long   sys_rename
+               .long   sys_mkdir
+/* 40 */       .long   sys_rmdir
+               .long   sys_dup
+               .long   sys_pipe
+               .long   sys_times
+               .long   sys_ni_syscall          /* was sys_prof */
+/* 45 */       .long   sys_brk
+               .long   sys_setgid16
+               .long   sys_getgid16
+               .long   sys_ni_syscall          /* was sys_signal */
+               .long   sys_geteuid16
+/* 50 */       .long   sys_getegid16
+               .long   sys_acct
+               .long   sys_umount
+               .long   sys_ni_syscall          /* was sys_lock */
+               .long   sys_ioctl
+/* 55 */       .long   sys_fcntl
+               .long   sys_ni_syscall          /* was sys_mpx */
+               .long   sys_setpgid
+               .long   sys_ni_syscall          /* was sys_ulimit */
+               .long   sys_ni_syscall          /* was sys_olduname */
+/* 60 */       .long   sys_umask
+               .long   sys_chroot
+               .long   sys_ustat
+               .long   sys_dup2
+               .long   sys_getppid
+/* 65 */       .long   sys_getpgrp
+               .long   sys_setsid
+               .long   sys_sigaction
+               .long   sys_ni_syscall          /* was sys_sgetmask */
+               .long   sys_ni_syscall          /* was sys_ssetmask */
+/* 70 */       .long   sys_setreuid16
+               .long   sys_setregid16
+               .long   sys_sigsuspend_wrapper
+               .long   sys_sigpending
+               .long   sys_sethostname
+/* 75 */       .long   sys_setrlimit
+               .long   sys_old_getrlimit       /* used by libc4 */
+               .long   sys_getrusage
+               .long   sys_gettimeofday
+               .long   sys_settimeofday
+/* 80 */       .long   sys_getgroups16
+               .long   sys_setgroups16
+               .long   old_select              /* used by libc4 */
+               .long   sys_symlink
+               .long   sys_ni_syscall          /* was sys_lstat */
+/* 85 */       .long   sys_readlink
+               .long   sys_uselib
+               .long   sys_swapon
+               .long   sys_reboot
+               .long   old_readdir             /* used by libc4 */
+/* 90 */       .long   old_mmap                /* used by libc4 */
+               .long   sys_munmap
+               .long   sys_truncate
+               .long   sys_ftruncate
+               .long   sys_fchmod
+/* 95 */       .long   sys_fchown16
+               .long   sys_getpriority
+               .long   sys_setpriority
+               .long   sys_ni_syscall          /* was sys_profil */
+               .long   sys_statfs
+/* 100 */      .long   sys_fstatfs
+               .long   sys_ni_syscall
+               .long   sys_socketcall
+               .long   sys_syslog
+               .long   sys_setitimer
+/* 105 */      .long   sys_getitimer
+               .long   sys_newstat
+               .long   sys_newlstat
+               .long   sys_newfstat
+               .long   sys_ni_syscall          /* was sys_uname */
+/* 110 */      .long   sys_ni_syscall          /* was sys_iopl */
+               .long   sys_vhangup
+               .long   sys_ni_syscall
+               .long   sys_syscall             /* call a syscall */
+               .long   sys_wait4
+/* 115 */      .long   sys_swapoff
+               .long   sys_sysinfo
+               .long   sys_ipc
+               .long   sys_fsync
+               .long   sys_sigreturn_wrapper
+/* 120 */      .long   sys_clone_wapper
+               .long   sys_setdomainname
+               .long   sys_newuname
+               .long   sys_ni_syscall
+               .long   sys_adjtimex
+/* 125 */      .long   sys_mprotect
+               .long   sys_sigprocmask
+               .long   sys_ni_syscall  /* WAS: sys_create_module */
+               .long   sys_init_module
+               .long   sys_delete_module
+/* 130 */      .long   sys_ni_syscall  /* WAS: sys_get_kernel_syms */
+               .long   sys_quotactl
+               .long   sys_getpgid
+               .long   sys_fchdir
+               .long   sys_bdflush
+/* 135 */      .long   sys_sysfs
+               .long   sys_personality
+               .long   sys_ni_syscall          /* .long        _sys_afs_syscall */
+               .long   sys_setfsuid16
+               .long   sys_setfsgid16
+/* 140 */      .long   sys_llseek
+               .long   sys_getdents
+               .long   sys_select
+               .long   sys_flock
+               .long   sys_msync
+/* 145 */      .long   sys_readv
+               .long   sys_writev
+               .long   sys_getsid
+               .long   sys_fdatasync
+               .long   sys_sysctl
+/* 150 */      .long   sys_mlock
+               .long   sys_munlock
+               .long   sys_mlockall
+               .long   sys_munlockall
+               .long   sys_sched_setparam
+/* 155 */      .long   sys_sched_getparam
+               .long   sys_sched_setscheduler
+               .long   sys_sched_getscheduler
+               .long   sys_sched_yield
+               .long   sys_sched_get_priority_max
+/* 160 */      .long   sys_sched_get_priority_min
+               .long   sys_sched_rr_get_interval
+               .long   sys_nanosleep
+               .long   sys_arm_mremap
+               .long   sys_setresuid16
+/* 165 */      .long   sys_getresuid16
+               .long   sys_ni_syscall
+               .long   sys_ni_syscall /* WAS: sys_query_module */
+               .long   sys_poll
+               .long   sys_nfsservctl
+/* 170 */      .long   sys_setresgid16
+               .long   sys_getresgid16
+               .long   sys_prctl
+               .long   sys_rt_sigreturn_wrapper
+               .long   sys_rt_sigaction
+/* 175 */      .long   sys_rt_sigprocmask
+               .long   sys_rt_sigpending
+               .long   sys_rt_sigtimedwait
+               .long   sys_rt_sigqueueinfo
+               .long   sys_rt_sigsuspend_wrapper
+/* 180 */      .long   sys_pread64
+               .long   sys_pwrite64
+               .long   sys_chown16
+               .long   sys_getcwd
+               .long   sys_capget
+/* 185 */      .long   sys_capset
+               .long   sys_sigaltstack_wrapper
+               .long   sys_sendfile
+               .long   sys_ni_syscall
+               .long   sys_ni_syscall
+/* 190 */      .long   sys_vfork_wrapper
+               .long   sys_getrlimit
+               .long   sys_mmap2
+               .long   sys_truncate64
+               .long   sys_ftruncate64
+/* 195 */      .long   sys_stat64
+               .long   sys_lstat64
+               .long   sys_fstat64
+               .long   sys_lchown
+               .long   sys_getuid
+/* 200 */      .long   sys_getgid
+               .long   sys_geteuid
+               .long   sys_getegid
+               .long   sys_setreuid
+               .long   sys_setregid
+/* 205 */      .long   sys_getgroups
+               .long   sys_setgroups
+               .long   sys_fchown
+               .long   sys_setresuid
+               .long   sys_getresuid
+/* 210 */      .long   sys_setresgid
+               .long   sys_getresgid
+               .long   sys_chown
+               .long   sys_setuid
+               .long   sys_setgid
+/* 215 */      .long   sys_setfsuid
+               .long   sys_setfsgid
+               .long   sys_getdents64
+               .long   sys_pivot_root
+               .long   sys_mincore
+/* 220 */      .long   sys_madvise
+               .long   sys_fcntl64
+               .long   sys_ni_syscall /* TUX */
+               .long   sys_ni_syscall /* WAS: sys_security */
+               .long   sys_gettid
+/* 225 */      .long   sys_readahead
+               .long   sys_setxattr
+               .long   sys_lsetxattr
+               .long   sys_fsetxattr
+               .long   sys_getxattr
+/* 230 */      .long   sys_lgetxattr
+               .long   sys_fgetxattr
+               .long   sys_listxattr
+               .long   sys_llistxattr
+               .long   sys_flistxattr
+/* 235 */      .long   sys_removexattr
+               .long   sys_lremovexattr
+               .long   sys_fremovexattr
+               .long   sys_tkill
+__syscall_end:
+
+               .rept   NR_syscalls - (__syscall_end - __syscall_start) / 4
+                       .long   sys_ni_syscall
+               .endr
+#endif
diff --git a/arch/arm26/kernel/head.S b/arch/arm26/kernel/head.S
new file mode 100644 (file)
index 0000000..8bfc625
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *  linux/arch/arm26/kernel/head.S
+ *
+ *  Copyright (C) 1994-2000 Russell King
+ *  Copyright (C) 2003 Ian Molton
+ *
+ * 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.
+ *
+ *  26-bit kernel startup code
+ */
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+               .globl  swapper_pg_dir
+               .equ    swapper_pg_dir, 0x0207d000
+
+/*
+ * Entry point.
+ */
+               .section ".init.text",#alloc,#execinstr
+ENTRY(stext)
+
+__entry:
+               cmp     pc, #0x02000000
+               ldrlt   pc, LC0                 @ if 0x01800000, call at 0x02080000
+               teq     r0, #0                  @ Check for old calling method
+               blne    oldparams               @ Move page if old
+
+               adr     r0, LC0
+               ldmib   r0, {r2-r5, sp}         @ Setup stack (and fetch other values)
+
+               mov     r0, #0                  @ Clear BSS
+1:             cmp     r2, r3
+               strcc   r0, [r2], #4
+               bcc     1b
+
+               bl      detect_proc_type
+               str     r0, [r4]
+               bl      detect_arch_type
+               str     r0, [r5]
+
+#ifdef CONFIG_XIP_KERNEL
+               ldr     r3, ETEXT                       @ data section copy
+               ldr     r4, SDATA
+               ldr     r5, EDATA
+1:
+               ldr     r6, [r3], #4
+               str     r6, [r4], #4
+               cmp     r4, r5
+               blt     1b
+#endif
+               mov     fp, #0
+               b       start_kernel
+
+LC0:           .word   _stext
+               .word   __bss_start             @ r2
+               .word   _end                    @ r3
+               .word   processor_id            @ r4
+               .word   __machine_arch_type     @ r5
+               .word   init_thread_union+8192  @ sp
+#ifdef CONFIG_XIP_KERNEL
+ETEXT:         .word   _endtext
+SDATA:         .word   _sdata
+EDATA:         .word   __bss_start
+#endif
+
+arm2_id:       .long   0x41560200  @ ARM2 and 250 dont have a CPUID
+arm250_id:     .long   0x41560250  @ So we create some after probing for them
+               .align
+
+oldparams:     mov     r4, #0x02000000
+               add     r3, r4, #0x00080000
+               add     r4, r4, #0x0007c000
+1:             ldmia   r0!, {r5 - r12}
+               stmia   r4!, {r5 - r12}
+               cmp     r4, r3
+               blt     1b
+               mov     pc, lr
+
+/*
+ * We need some way to automatically detect the difference between
+ * these two machines.  Unfortunately, it is not possible to detect
+ * the presence of the SuperIO chip, because that will hang the old
+ * Archimedes machines solid.
+ */
+/* DAG: Outdated, these have been combined !!!!!!! */
+detect_arch_type:
+#if defined(CONFIG_ARCH_ARC)
+               mov     r0, #MACH_TYPE_ARCHIMEDES
+#elif defined(CONFIG_ARCH_A5K)
+               mov     r0, #MACH_TYPE_A5K
+#endif
+               mov     pc, lr
+
+detect_proc_type:
+               mov     ip, lr
+               mov     r2, #0xea000000         @ Point undef instr to continuation
+               adr     r0, continue - 12
+               orr     r0, r2, r0, lsr #2
+               mov     r1, #0
+               str     r0, [r1, #4]
+               ldr     r0, arm2_id
+               swp     r2, r2, [r1]            @ check for swp (ARM2 cant)
+               ldr     r0, arm250_id
+               mrc     15, 0, r3, c0, c0       @ check for CP#15 (ARM250 cant)
+               mov     r0, r3
+continue:      mov     r2, #0xeb000000         @ Make undef vector loop
+               sub     r2, r2, #2
+               str     r2, [r1, #4]
+               mov     pc, ip
diff --git a/arch/arm26/lib/io-readsl.S b/arch/arm26/lib/io-readsl.S
new file mode 100644 (file)
index 0000000..7be208b
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *  linux/arch/arm26/lib/io-readsl.S
+ *
+ *  Copyright (C) 1995-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.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/hardware.h>
+
+/*
+ * Note that some reads can be aligned on half-word boundaries.
+ */
+ENTRY(__raw_readsl)
+               teq     r2, #0          @ do we have to check for the zero len?
+               moveq   pc, lr
+               ands    ip, r1, #3
+               bne     2f
+
+1:             ldr     r3, [r0]
+               str     r3, [r1], #4
+               subs    r2, r2, #1
+               bne     1b
+               mov     pc, lr
+
+2:             cmp     ip, #2
+               ldr     ip, [r0]
+               blt     4f
+               bgt     6f
+
+               strb    ip, [r1], #1
+               mov     ip, ip, lsr #8
+               strb    ip, [r1], #1
+               mov     ip, ip, lsr #8
+3:             subs    r2, r2, #1
+               ldrne   r3, [r0]
+               orrne   ip, ip, r3, lsl #16
+               strne   ip, [r1], #4
+               movne   ip, r3, lsr #16
+               bne     3b
+               strb    ip, [r1], #1
+               mov     ip, ip, lsr #8
+               strb    ip, [r1], #1
+               mov     pc, lr
+
+4:             strb    ip, [r1], #1
+               mov     ip, ip, lsr #8
+               strb    ip, [r1], #1
+               mov     ip, ip, lsr #8
+               strb    ip, [r1], #1
+               mov     ip, ip, lsr #8
+5:             subs    r2, r2, #1
+               ldrne   r3, [r0]
+               orrne   ip, ip, r3, lsl #8
+               strne   ip, [r1], #4
+               movne   ip, r3, lsr #24
+               bne     5b
+               strb    ip, [r1], #1
+               mov     pc, lr
+
+6:             strb    ip, [r1], #1
+               mov     ip, ip, lsr #8
+7:             subs    r2, r2, #1
+               ldrne   r3, [r0]
+               orrne   ip, ip, r3, lsl #24
+               strne   ip, [r1], #4
+               movne   ip, r3, lsr #8
+               bne     7b
+               strb    ip, [r1], #1
+               mov     ip, ip, lsr #8
+               strb    ip, [r1], #1
+               mov     ip, ip, lsr #8
+               strb    ip, [r1], #1
+               mov     pc, lr
+
diff --git a/arch/arm26/lib/io-readsw.S b/arch/arm26/lib/io-readsw.S
new file mode 100644 (file)
index 0000000..c65c1f2
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *  linux/arch/arm26/lib/io-readsw.S
+ *
+ *  Copyright (C) 1995-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.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/hardware.h>
+
+.insw_bad_alignment:
+               adr     r0, .insw_bad_align_msg
+               mov     r2, lr
+               b       panic
+.insw_bad_align_msg:
+               .asciz  "insw: bad buffer alignment (0x%p, lr=0x%08lX)\n"
+               .align
+
+.insw_align:   tst     r1, #1
+               bne     .insw_bad_alignment
+
+               ldr     r3, [r0]
+               strb    r3, [r1], #1
+               mov     r3, r3, lsr #8
+               strb    r3, [r1], #1
+
+               subs    r2, r2, #1
+               RETINSTR(moveq, pc, lr)
+
+ENTRY(__raw_readsw)
+               teq     r2, #0          @ do we have to check for the zero len?
+               moveq   pc, lr
+               tst     r1, #3
+               bne     .insw_align
+
+.insw_aligned: mov     ip, #0xff
+               orr     ip, ip, ip, lsl #8
+               stmfd   sp!, {r4, r5, r6, lr}
+
+               subs    r2, r2, #8
+               bmi     .no_insw_8
+
+.insw_8_lp:    ldr     r3, [r0]
+               and     r3, r3, ip
+               ldr     r4, [r0]
+               orr     r3, r3, r4, lsl #16
+
+               ldr     r4, [r0]
+               and     r4, r4, ip
+               ldr     r5, [r0]
+               orr     r4, r4, r5, lsl #16
+
+               ldr     r5, [r0]
+               and     r5, r5, ip
+               ldr     r6, [r0]
+               orr     r5, r5, r6, lsl #16
+
+               ldr     r6, [r0]
+               and     r6, r6, ip
+               ldr     lr, [r0]
+               orr     r6, r6, lr, lsl #16
+
+               stmia   r1!, {r3 - r6}
+
+               subs    r2, r2, #8
+               bpl     .insw_8_lp
+
+               tst     r2, #7
+               LOADREGS(eqfd, sp!, {r4, r5, r6, pc})
+
+.no_insw_8:    tst     r2, #4
+               beq     .no_insw_4
+
+               ldr     r3, [r0]
+               and     r3, r3, ip
+               ldr     r4, [r0]
+               orr     r3, r3, r4, lsl #16
+
+               ldr     r4, [r0]
+               and     r4, r4, ip
+               ldr     r5, [r0]
+               orr     r4, r4, r5, lsl #16
+
+               stmia   r1!, {r3, r4}
+
+.no_insw_4:    tst     r2, #2
+               beq     .no_insw_2
+
+               ldr     r3, [r0]
+               and     r3, r3, ip
+               ldr     r4, [r0]
+               orr     r3, r3, r4, lsl #16
+
+               str     r3, [r1], #4
+
+.no_insw_2:    tst     r2, #1
+               ldrne   r3, [r0]
+               strneb  r3, [r1], #1
+               movne   r3, r3, lsr #8
+               strneb  r3, [r1]
+
+               LOADREGS(fd, sp!, {r4, r5, r6, pc})
+
+
diff --git a/arch/arm26/lib/io-writesw.S b/arch/arm26/lib/io-writesw.S
new file mode 100644 (file)
index 0000000..a24f891
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *  linux/arch/arm26/lib/io-writesw.S
+ *
+ *  Copyright (C) 1995-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.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/hardware.h>
+
+.outsw_bad_alignment:
+               adr     r0, .outsw_bad_align_msg
+               mov     r2, lr
+               b       panic
+.outsw_bad_align_msg:
+               .asciz  "outsw: bad buffer alignment (0x%p, lr=0x%08lX)\n"
+               .align
+
+.outsw_align:  tst     r1, #1
+               bne     .outsw_bad_alignment
+
+               add     r1, r1, #2
+
+               ldr     r3, [r1, #-4]
+               mov     r3, r3, lsr #16
+               orr     r3, r3, r3, lsl #16
+               str     r3, [r0]
+               subs    r2, r2, #1
+               RETINSTR(moveq, pc, lr)
+
+ENTRY(__raw_writesw)
+               teq     r2, #0          @ do we have to check for the zero len?
+               moveq   pc, lr
+               tst     r1, #3
+               bne     .outsw_align
+
+.outsw_aligned:        stmfd   sp!, {r4, r5, r6, lr}
+
+               subs    r2, r2, #8
+               bmi     .no_outsw_8
+
+.outsw_8_lp:   ldmia   r1!, {r3, r4, r5, r6}
+
+               mov     ip, r3, lsl #16
+               orr     ip, ip, ip, lsr #16
+               str     ip, [r0]
+
+               mov     ip, r3, lsr #16
+               orr     ip, ip, ip, lsl #16
+               str     ip, [r0]
+
+               mov     ip, r4, lsl #16
+               orr     ip, ip, ip, lsr #16
+               str     ip, [r0]
+
+               mov     ip, r4, lsr #16
+               orr     ip, ip, ip, lsl #16
+               str     ip, [r0]
+
+               mov     ip, r5, lsl #16
+               orr     ip, ip, ip, lsr #16
+               str     ip, [r0]
+
+               mov     ip, r5, lsr #16
+               orr     ip, ip, ip, lsl #16
+               str     ip, [r0]
+
+               mov     ip, r6, lsl #16
+               orr     ip, ip, ip, lsr #16
+               str     ip, [r0]
+
+               mov     ip, r6, lsr #16
+               orr     ip, ip, ip, lsl #16
+               str     ip, [r0]
+
+               subs    r2, r2, #8
+               bpl     .outsw_8_lp
+
+               tst     r2, #7
+               LOADREGS(eqfd, sp!, {r4, r5, r6, pc})
+
+.no_outsw_8:   tst     r2, #4
+               beq     .no_outsw_4
+
+               ldmia   r1!, {r3, r4}
+
+               mov     ip, r3, lsl #16
+               orr     ip, ip, ip, lsr #16
+               str     ip, [r0]
+
+               mov     ip, r3, lsr #16
+               orr     ip, ip, ip, lsl #16
+               str     ip, [r0]
+
+               mov     ip, r4, lsl #16
+               orr     ip, ip, ip, lsr #16
+               str     ip, [r0]
+
+               mov     ip, r4, lsr #16
+               orr     ip, ip, ip, lsl #16
+               str     ip, [r0]
+
+.no_outsw_4:   tst     r2, #2
+               beq     .no_outsw_2
+
+               ldr     r3, [r1], #4
+
+               mov     ip, r3, lsl #16
+               orr     ip, ip, ip, lsr #16
+               str     ip, [r0]
+
+               mov     ip, r3, lsr #16
+               orr     ip, ip, ip, lsl #16
+               str     ip, [r0]
+
+.no_outsw_2:   tst     r2, #1
+
+               ldrne   r3, [r1]
+
+               movne   ip, r3, lsl #16
+               orrne   ip, ip, ip, lsr #16
+               strne   ip, [r0]
+
+               LOADREGS(fd, sp!, {r4, r5, r6, pc})
diff --git a/arch/arm26/machine/latches.c b/arch/arm26/machine/latches.c
new file mode 100644 (file)
index 0000000..94f05d2
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  linux/arch/arm26/kernel/latches.c
+ *
+ *  Copyright (C) David Alan Gilbert 1995/1996,2000
+ *  Copyright (C) Ian Molton 2003
+ *
+ * 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.
+ *
+ *  Support for the latches on the old Archimedes which control the floppy,
+ *  hard disc and printer
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/oldlatches.h>
+
+static unsigned char latch_a_copy;
+static unsigned char latch_b_copy;
+
+/* newval=(oldval & ~mask)|newdata */
+void oldlatch_aupdate(unsigned char mask,unsigned char newdata)
+{
+       unsigned long flags;
+
+       BUG_ON(!machine_is_archimedes());
+
+       local_irq_save(flags); //FIXME: was local_save_flags
+       latch_a_copy = (latch_a_copy & ~mask) | newdata;
+       __raw_writeb(latch_a_copy, LATCHA_BASE);
+       local_irq_restore(flags);
+
+       printk("Latch: A = 0x%02x\n", latch_a_copy);
+}
+
+
+/* newval=(oldval & ~mask)|newdata */
+void oldlatch_bupdate(unsigned char mask,unsigned char newdata)
+{
+       unsigned long flags;
+
+       BUG_ON(!machine_is_archimedes());
+
+
+       local_irq_save(flags);//FIXME: was local_save_flags
+       latch_b_copy = (latch_b_copy & ~mask) | newdata;
+       __raw_writeb(latch_b_copy, LATCHB_BASE);
+       local_irq_restore(flags);
+
+       printk("Latch: B = 0x%02x\n", latch_b_copy);
+}
+
+static int __init oldlatch_init(void)
+{
+       if (machine_is_archimedes()) {
+               oldlatch_aupdate(0xff, 0xff);
+               /* Thats no FDC reset...*/
+               oldlatch_bupdate(0xff, LATCHB_FDCRESET);
+       }
+       return 0;
+}
+
+arch_initcall(oldlatch_init);
+
+EXPORT_SYMBOL(oldlatch_aupdate);
+EXPORT_SYMBOL(oldlatch_bupdate);
diff --git a/arch/arm26/mm/memc.c b/arch/arm26/mm/memc.c
new file mode 100644 (file)
index 0000000..8e8a2bb
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *  linux/arch/arm26/mm/memc.c
+ *
+ *  Copyright (C) 1998-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.
+ *
+ *  Page table sludge for older ARM processor architectures.
+ */
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/page.h>
+#include <asm/memory.h>
+#include <asm/hardware.h>
+
+#include <asm/map.h>
+
+#define MEMC_TABLE_SIZE (256*sizeof(unsigned long))
+
+kmem_cache_t *pte_cache, *pgd_cache;
+int page_nr;
+
+/*
+ * Allocate space for a page table and a MEMC table.
+ * Note that we place the MEMC
+ * table before the page directory.  This means we can
+ * easily get to both tightly-associated data structures
+ * with a single pointer.
+ */
+static inline pgd_t *alloc_pgd_table(void)
+{
+       void *pg2k = kmem_cache_alloc(pgd_cache, GFP_KERNEL);
+
+       if (pg2k)
+               pg2k += MEMC_TABLE_SIZE;
+
+       return (pgd_t *)pg2k;
+}
+
+/*
+ * Free a page table. this function is the counterpart to get_pgd_slow
+ * below, not alloc_pgd_table above.
+ */
+void free_pgd_slow(pgd_t *pgd)
+{
+       unsigned long tbl = (unsigned long)pgd;
+
+       tbl -= MEMC_TABLE_SIZE;
+
+       kmem_cache_free(pgd_cache, (void *)tbl);
+}
+
+/*
+ * Allocate a new pgd and fill it in ready for use
+ *
+ * A new tasks pgd is completely empty (all pages !present) except for:
+ *
+ * o The machine vectors at virtual address 0x0
+ * o The vmalloc region at the top of address space
+ *
+ */
+#define FIRST_KERNEL_PGD_NR     (FIRST_USER_PGD_NR + USER_PTRS_PER_PGD)
+
+pgd_t *get_pgd_slow(struct mm_struct *mm)
+{
+       pgd_t *new_pgd, *init_pgd;
+       pmd_t *new_pmd, *init_pmd;
+       pte_t *new_pte, *init_pte;
+
+       new_pgd = alloc_pgd_table();
+       if (!new_pgd)
+               goto no_pgd;
+
+       /*
+        * This lock is here just to satisfy pmd_alloc and pte_lock
+         * FIXME: I bet we could avoid taking it pretty much altogether
+        */
+       spin_lock(&mm->page_table_lock);
+
+       /*
+        * On ARM, first page must always be allocated since it contains
+        * the machine vectors.
+        */
+       new_pmd = pmd_alloc(mm, new_pgd, 0);
+       if (!new_pmd)
+               goto no_pmd;
+
+       new_pte = pte_alloc_kernel(mm, new_pmd, 0);
+       if (!new_pte)
+               goto no_pte;
+
+       init_pgd = pgd_offset(&init_mm, 0);
+       init_pmd = pmd_offset(init_pgd, 0);
+       init_pte = pte_offset(init_pmd, 0);
+
+       set_pte(new_pte, *init_pte);
+
+       /*
+        * the page table entries are zeroed
+        * when the table is created. (see the cache_ctor functions below)
+        * Now we need to plonk the kernel (vmalloc) area at the end of
+        * the address space. We copy this from the init thread, just like
+        * the init_pte we copied above...
+        */
+       memcpy(new_pgd + FIRST_KERNEL_PGD_NR, init_pgd + FIRST_KERNEL_PGD_NR,
+               (PTRS_PER_PGD - FIRST_KERNEL_PGD_NR) * sizeof(pgd_t));
+
+       spin_unlock(&mm->page_table_lock);
+
+       /* update MEMC tables */
+       cpu_memc_update_all(new_pgd);
+       return new_pgd;
+
+no_pte:
+       spin_unlock(&mm->page_table_lock);
+       pmd_free(new_pmd);
+       free_pgd_slow(new_pgd);
+       return NULL;
+
+no_pmd:
+       spin_unlock(&mm->page_table_lock);
+       free_pgd_slow(new_pgd);
+       return NULL;
+
+no_pgd:
+       return NULL;
+}
+
+/*
+ * No special code is required here.
+ */
+void setup_mm_for_reboot(char mode)
+{
+}
+
+/*
+ * This contains the code to setup the memory map on an ARM2/ARM250/ARM3
+ *  o swapper_pg_dir = 0x0207d000
+ *  o kernel proper starts at 0x0208000
+ *  o create (allocate) a pte to contain the machine vectors
+ *  o populate the pte (points to 0x02078000) (FIXME - is it zeroed?)
+ *  o populate the init tasks page directory (pgd) with the new pte
+ *  o zero the rest of the init tasks pgdir (FIXME - what about vmalloc?!)
+ */
+void __init memtable_init(struct meminfo *mi)
+{
+       pte_t *pte;
+       int i;
+
+       page_nr = max_low_pfn;
+
+       pte = alloc_bootmem_low_pages(PTRS_PER_PTE * sizeof(pte_t));
+       pte[0] = mk_pte_phys(PAGE_OFFSET + SCREEN_SIZE, PAGE_READONLY);
+       pmd_populate(&init_mm, pmd_offset(swapper_pg_dir, 0), pte);
+
+       for (i = 1; i < PTRS_PER_PGD; i++)
+               pgd_val(swapper_pg_dir[i]) = 0;
+}
+
+void __init iotable_init(struct map_desc *io_desc)
+{
+       /* nothing to do */
+}
+
+/*
+ * We never have holes in the memmap
+ */
+void __init create_memmap_holes(struct meminfo *mi)
+{
+}
+
+static void pte_cache_ctor(void *pte, kmem_cache_t *cache, unsigned long flags)
+{
+       memzero(pte, sizeof(pte_t) * PTRS_PER_PTE);
+}
+
+static void pgd_cache_ctor(void *pgd, kmem_cache_t *cache, unsigned long flags)
+{
+       memzero(pgd + MEMC_TABLE_SIZE, USER_PTRS_PER_PGD * sizeof(pgd_t));
+}
+
+void __init pgtable_cache_init(void)
+{
+       pte_cache = kmem_cache_create("pte-cache",
+                               sizeof(pte_t) * PTRS_PER_PTE,
+                               0, 0, pte_cache_ctor, NULL);
+       if (!pte_cache)
+               BUG();
+
+       pgd_cache = kmem_cache_create("pgd-cache", MEMC_TABLE_SIZE +
+                               sizeof(pgd_t) * PTRS_PER_PGD,
+                               0, 0, pgd_cache_ctor, NULL);
+       if (!pgd_cache)
+               BUG();
+}
diff --git a/arch/arm26/mm/small_page.c b/arch/arm26/mm/small_page.c
new file mode 100644 (file)
index 0000000..77be86c
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ *  linux/arch/arm26/mm/small_page.c
+ *
+ *  Copyright (C) 1996  Russell King
+ *  Copyright (C) 2003  Ian Molton
+ *
+ * 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.
+ *
+ *  Changelog:
+ *   26/01/1996        RMK     Cleaned up various areas to make little more generic
+ *   07/02/1999        RMK     Support added for 16K and 32K page sizes
+ *                     containing 8K blocks
+ *   23/05/2004 IM     Fixed to use struct page->lru (thanks wli)
+ *
+ */
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/smp.h>
+#include <linux/bitops.h>
+
+#include <asm/pgtable.h>
+
+#define PEDANTIC
+
+/*
+ * Requirement:
+ *  We need to be able to allocate naturally aligned memory of finer
+ *  granularity than the page size.  This is typically used for the
+ *  second level page tables on 32-bit ARMs.
+ *
+ * FIXME - this comment is *out of date*
+ * Theory:
+ *  We "misuse" the Linux memory management system.  We use alloc_page
+ *  to allocate a page and then mark it as reserved.  The Linux memory
+ *  management system will then ignore the "offset", "next_hash" and
+ *  "pprev_hash" entries in the mem_map for this page.
+ *
+ *  We then use a bitstring in the "offset" field to mark which segments
+ *  of the page are in use, and manipulate this as required during the
+ *  allocation and freeing of these small pages.
+ *
+ *  We also maintain a queue of pages being used for this purpose using
+ *  the "next_hash" and "pprev_hash" entries of mem_map;
+ */
+
+struct order {
+       struct list_head queue;
+       unsigned int mask;              /* (1 << shift) - 1             */
+       unsigned int shift;             /* (1 << shift) size of page    */
+       unsigned int block_mask;        /* nr_blocks - 1                */
+       unsigned int all_used;          /* (1 << nr_blocks) - 1         */
+};
+
+
+static struct order orders[] = {
+#if PAGE_SIZE == 32768
+       { LIST_HEAD_INIT(orders[0].queue), 2047, 11, 15, 0x0000ffff },
+       { LIST_HEAD_INIT(orders[1].queue), 8191, 13,  3, 0x0000000f }
+#else
+#error unsupported page size (ARGH!)
+#endif
+};
+
+#define USED_MAP(pg)                   ((pg)->index)
+#define TEST_AND_CLEAR_USED(pg,off)    (test_and_clear_bit(off, &USED_MAP(pg)))
+#define SET_USED(pg,off)               (set_bit(off, &USED_MAP(pg)))
+
+static DEFINE_SPINLOCK(small_page_lock);
+
+static unsigned long __get_small_page(int priority, struct order *order)
+{
+       unsigned long flags;
+       struct page *page;
+       int offset;
+
+       do {
+               spin_lock_irqsave(&small_page_lock, flags);
+
+               if (list_empty(&order->queue))
+                       goto need_new_page;
+
+               page = list_entry(order->queue.next, struct page, lru);
+again:
+#ifdef PEDANTIC
+               if (USED_MAP(page) & ~order->all_used)
+                       PAGE_BUG(page);
+#endif
+               offset = ffz(USED_MAP(page));
+               SET_USED(page, offset);
+               if (USED_MAP(page) == order->all_used)
+                       list_del_init(&page->lru);
+               spin_unlock_irqrestore(&small_page_lock, flags);
+
+               return (unsigned long) page_address(page) + (offset << order->shift);
+
+need_new_page:
+               spin_unlock_irqrestore(&small_page_lock, flags);
+               page = alloc_page(priority);
+               spin_lock_irqsave(&small_page_lock, flags);
+
+               if (list_empty(&order->queue)) {
+                       if (!page)
+                               goto no_page;
+                       SetPageReserved(page);
+                       USED_MAP(page) = 0;
+                       list_add(&page->lru, &order->queue);
+                       goto again;
+               }
+
+               spin_unlock_irqrestore(&small_page_lock, flags);
+               __free_page(page);
+       } while (1);
+
+no_page:
+       spin_unlock_irqrestore(&small_page_lock, flags);
+       return 0;
+}
+
+static void __free_small_page(unsigned long spage, struct order *order)
+{
+       unsigned long flags;
+       struct page *page;
+
+       if (virt_addr_valid(spage)) {
+               page = virt_to_page(spage);
+
+               /*
+                * The container-page must be marked Reserved
+                */
+               if (!PageReserved(page) || spage & order->mask)
+                       goto non_small;
+
+#ifdef PEDANTIC
+               if (USED_MAP(page) & ~order->all_used)
+                       PAGE_BUG(page);
+#endif
+
+               spage = spage >> order->shift;
+               spage &= order->block_mask;
+
+               /*
+                * the following must be atomic wrt get_page
+                */
+               spin_lock_irqsave(&small_page_lock, flags);
+
+               if (USED_MAP(page) == order->all_used)
+                       list_add(&page->lru, &order->queue);
+
+               if (!TEST_AND_CLEAR_USED(page, spage))
+                       goto already_free;
+
+               if (USED_MAP(page) == 0)
+                       goto free_page;
+
+               spin_unlock_irqrestore(&small_page_lock, flags);
+       }
+       return;
+
+free_page:
+       /*
+        * unlink the page from the small page queue and free it
+        */
+       list_del_init(&page->lru);
+       spin_unlock_irqrestore(&small_page_lock, flags);
+       ClearPageReserved(page);
+       __free_page(page);
+       return;
+
+non_small:
+       printk("Trying to free non-small page from %p\n", __builtin_return_address(0));
+       return;
+already_free:
+       printk("Trying to free free small page from %p\n", __builtin_return_address(0));
+}
+
+unsigned long get_page_8k(int priority)
+{
+       return __get_small_page(priority, orders+1);
+}
+
+void free_page_8k(unsigned long spage)
+{
+       __free_small_page(spage, orders+1);
+}
diff --git a/arch/cris/arch-v10/kernel/crisksyms.c b/arch/cris/arch-v10/kernel/crisksyms.c
new file mode 100644 (file)
index 0000000..b332bf9
--- /dev/null
@@ -0,0 +1,17 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/io.h>
+#include <asm/arch/svinto.h>
+
+/* Export shadow registers for the CPU I/O pins */
+EXPORT_SYMBOL(genconfig_shadow);
+EXPORT_SYMBOL(port_pa_data_shadow);
+EXPORT_SYMBOL(port_pa_dir_shadow);
+EXPORT_SYMBOL(port_pb_data_shadow);
+EXPORT_SYMBOL(port_pb_dir_shadow);
+EXPORT_SYMBOL(port_pb_config_shadow);
+EXPORT_SYMBOL(port_g_data_shadow);
+
+/* Cache flush functions */
+EXPORT_SYMBOL(flush_etrax_cache);
+EXPORT_SYMBOL(prepare_rx_descriptor);
diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig
new file mode 100644 (file)
index 0000000..78e5505
--- /dev/null
@@ -0,0 +1,501 @@
+#
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/kconfig-language.txt.
+#
+config FRV
+       bool
+       default y
+
+config UID16
+       bool
+       default y
+
+config RWSEM_GENERIC_SPINLOCK
+       bool
+       default y
+
+config RWSEM_XCHGADD_ALGORITHM
+       bool
+
+config GENERIC_FIND_NEXT_BIT
+       bool
+       default y
+
+config GENERIC_CALIBRATE_DELAY
+       bool
+       default n
+
+config GENERIC_HARDIRQS
+       bool
+       default n
+
+mainmenu "Fujitsu FR-V Kernel Configuration"
+
+source "init/Kconfig"
+
+
+menu "Fujitsu FR-V system setup"
+
+config MMU
+       bool "MMU support"
+       help
+         This options switches on and off support for the FR-V MMU
+         (effectively switching between vmlinux and uClinux). Not all FR-V
+         CPUs support this. Currently only the FR451 has a sufficiently
+         featured MMU.
+
+config FRV_OUTOFLINE_ATOMIC_OPS
+       bool "Out-of-line the FRV atomic operations"
+       default n
+       help
+         Setting this option causes the FR-V atomic operations to be mostly
+         implemented out-of-line.
+
+         See Documentation/fujitsu/frv/atomic-ops.txt for more information.
+
+config HIGHMEM
+       bool "High memory support"
+       depends on MMU
+       default y
+       help
+         If you wish to use more than 256MB of memory with your MMU based
+         system, you will need to select this option. The kernel can only see
+         the memory between 0xC0000000 and 0xD0000000 directly... everything
+         else must be kmapped.
+
+         The arch is, however, capable of supporting up to 3GB of SDRAM.
+
+config HIGHPTE
+       bool "Allocate page tables in highmem"
+       depends on HIGHMEM
+       default y
+       help
+         The VM uses one page of memory for each page table.  For systems
+         with a lot of RAM, this can be wasteful of precious low memory.
+         Setting this option will put user-space page tables in high memory.
+
+choice
+       prompt "uClinux kernel load address"
+       depends on !MMU
+       default UCPAGE_OFFSET_C0000000
+       help
+         This option sets the base address for the uClinux kernel. The kernel
+         will rearrange the SDRAM layout to start at this address, and move
+         itself to start there. It must be greater than 0, and it must be
+         sufficiently less than 0xE0000000 that the SDRAM does not intersect
+         the I/O region.
+
+         The base address must also be aligned such that the SDRAM controller
+         can decode it. For instance, a 512MB SDRAM bank must be 512MB aligned.
+
+config UCPAGE_OFFSET_20000000
+       bool "0x20000000"
+
+config UCPAGE_OFFSET_40000000
+       bool "0x40000000"
+
+config UCPAGE_OFFSET_60000000
+       bool "0x60000000"
+
+config UCPAGE_OFFSET_80000000
+       bool "0x80000000"
+
+config UCPAGE_OFFSET_A0000000
+       bool "0xA0000000"
+
+config UCPAGE_OFFSET_C0000000
+       bool "0xC0000000 (Recommended)"
+
+endchoice
+
+config PROTECT_KERNEL
+       bool "Protect core kernel against userspace"
+       depends on !MMU
+       default y
+       help
+         Selecting this option causes the uClinux kernel to change the
+         permittivity of DAMPR register covering the core kernel image to
+         prevent userspace accessing the underlying memory directly.
+
+choice
+       prompt "CPU Caching mode"
+       default FRV_DEFL_CACHE_WBACK
+       help
+         This option determines the default caching mode for the kernel.
+
+         Write-Back caching mode involves the all reads and writes causing
+         the affected cacheline to be read into the cache first before being
+         operated upon. Memory is not then updated by a write until the cache
+         is filled and a cacheline needs to be displaced from the cache to
+         make room. Only at that point is it written back.
+
+         Write-Behind caching is similar to Write-Back caching, except that a
+         write won't fetch a cacheline into the cache if there isn't already
+         one there; it will write directly to memory instead.
+
+         Write-Through caching only fetches cachelines from memory on a
+         read. Writes always get written directly to memory. If the affected
+         cacheline is also in cache, it will be updated too.
+
+         The final option is to turn of caching entirely.
+
+         Note that not all CPUs support Write-Behind caching. If the CPU on
+         which the kernel is running doesn't, it'll fall back to Write-Back
+         caching.
+
+config FRV_DEFL_CACHE_WBACK
+       bool "Write-Back"
+
+config FRV_DEFL_CACHE_WBEHIND
+       bool "Write-Behind"
+
+config FRV_DEFL_CACHE_WTHRU
+       bool "Write-Through"
+
+config FRV_DEFL_CACHE_DISABLED
+       bool "Disabled"
+
+endchoice
+
+menu "CPU core support"
+
+config CPU_FR401
+       bool "Include FR401 core support"
+       depends on !MMU
+       default y
+       help
+         This enables support for the FR401, FR401A and FR403 CPUs
+
+config CPU_FR405
+       bool "Include FR405 core support"
+       depends on !MMU
+       default y
+       help
+         This enables support for the FR405 CPU
+
+config CPU_FR451
+       bool "Include FR451 core support"
+       default y
+       help
+         This enables support for the FR451 CPU
+
+config CPU_FR451_COMPILE
+       bool "Specifically compile for FR451 core"
+       depends on CPU_FR451 && !CPU_FR401 && !CPU_FR405 && !CPU_FR551
+       default y
+       help
+         This causes appropriate flags to be passed to the compiler to
+         optimise for the FR451 CPU
+
+config CPU_FR551
+       bool "Include FR551 core support"
+       depends on !MMU
+       default y
+       help
+         This enables support for the FR555 CPU
+
+config CPU_FR551_COMPILE
+       bool "Specifically compile for FR551 core"
+       depends on CPU_FR551 && !CPU_FR401 && !CPU_FR405 && !CPU_FR451
+       default y
+       help
+         This causes appropriate flags to be passed to the compiler to
+         optimise for the FR555 CPU
+
+config FRV_L1_CACHE_SHIFT
+       int
+       default "5" if CPU_FR401 || CPU_FR405 || CPU_FR451
+       default "6" if CPU_FR551
+
+endmenu
+
+choice
+       prompt "System support"
+       default MB93091_VDK
+
+config MB93091_VDK
+       bool "MB93091 CPU board with or without motherboard"
+
+config MB93093_PDK
+       bool "MB93093 PDK unit"
+
+endchoice
+
+if MB93091_VDK
+choice
+       prompt "Motherboard support"
+       default MB93090_MB00
+
+config MB93090_MB00
+       bool "Use the MB93090-MB00 motherboard"
+       help
+         Select this option if the MB93091 CPU board is going to be used with
+         a MB93090-MB00 VDK motherboard
+
+config MB93091_NO_MB
+       bool "Use standalone"
+       help
+         Select this option if the MB93091 CPU board is going to be used
+         without a motherboard
+
+endchoice
+endif
+
+choice
+       prompt "GP-Relative data support"
+       default GPREL_DATA_8
+       help
+         This option controls what data, if any, should be placed in the GP
+         relative data sections. Using this means that the compiler can
+         generate accesses to the data using GR16-relative addressing which
+         is faster than absolute instructions and saves space (2 instructions
+         per access).
+
+         However, the GPREL region is limited in size because the immediate
+         value used in the load and store instructions is limited to a 12-bit
+         signed number.
+
+         So if the linker starts complaining that accesses to GPREL data are
+         out of range, try changing this option from the default.
+
+         Note that modules will always be compiled with this feature disabled
+         as the module data will not be in range of the GP base address.
+
+config GPREL_DATA_8
+       bool "Put data objects of up to 8 bytes into GP-REL"
+
+config GPREL_DATA_4
+       bool "Put data objects of up to 4 bytes into GP-REL"
+
+config GPREL_DATA_NONE
+       bool "Don't use GP-REL"
+
+endchoice
+
+config PCI
+       bool "Use PCI"
+       depends on MB93090_MB00
+       default y
+       help
+         Some FR-V systems (such as the MB93090-MB00 VDK) have PCI
+         onboard. If you have one of these boards and you wish to use the PCI
+         facilities, say Y here.
+
+         The PCI-HOWTO, available from
+         <http://www.tldp.org/docs.html#howto>, contains valuable
+         information about which PCI hardware does work under Linux and which
+         doesn't.
+
+config RESERVE_DMA_COHERENT
+       bool "Reserve DMA coherent memory"
+       depends on PCI && !MMU
+       default y
+       help
+         Many PCI drivers require access to uncached memory for DMA device
+         communications (such as is done with some Ethernet buffer rings). If
+         a fully featured MMU is available, this can be done through page
+         table settings, but if not, a region has to be set aside and marked
+         with a special DAMPR register.
+
+         Setting this option causes uClinux to set aside a portion of the
+         available memory for use in this manner. The memory will then be
+         unavailable for normal kernel use.
+
+source "drivers/pci/Kconfig"
+
+config PCMCIA
+       tristate "Use PCMCIA"
+       help
+         Say Y here if you want to attach PCMCIA- or PC-cards to your FR-V
+         board.  These are credit-card size devices such as network cards,
+         modems or hard drives often used with laptops computers.  There are
+         actually two varieties of these cards: the older 16 bit PCMCIA cards
+         and the newer 32 bit CardBus cards.  If you want to use CardBus
+         cards, you need to say Y here and also to "CardBus support" below.
+
+         To use your PC-cards, you will need supporting software from David
+         Hinds pcmcia-cs package (see the file <file:Documentation/Changes>
+         for location).  Please also read the PCMCIA-HOWTO, available from
+         <http://www.tldp.org/docs.html#howto>.
+
+         To compile this driver as modules, choose M here: the
+         modules will be called pcmcia_core and ds.
+
+#config MATH_EMULATION
+#      bool "Math emulation support (EXPERIMENTAL)"
+#      depends on EXPERIMENTAL
+#      help
+#        At some point in the future, this will cause floating-point math
+#        instructions to be emulated by the kernel on machines that lack a
+#        floating-point math coprocessor.  Thrill-seekers and chronically
+#        sleep-deprived psychotic hacker types can say Y now, everyone else
+#        should probably wait a while.
+
+menu "Power management options"
+source kernel/power/Kconfig
+endmenu
+
+endmenu
+
+
+menu "Executable formats"
+
+source "fs/Kconfig.binfmt"
+
+endmenu
+
+source "drivers/Kconfig"
+
+source "fs/Kconfig"
+
+menu "Kernel hacking"
+
+config DEBUG_KERNEL
+       bool "Kernel debugging"
+       help
+         Say Y here if you are developing drivers or trying to debug and
+         identify kernel problems.
+
+config EARLY_PRINTK
+       bool "Early printk"
+       depends on EMBEDDED && DEBUG_KERNEL
+       default n
+       help
+         Write kernel log output directly into the VGA buffer or to a serial
+         port.
+
+         This is useful for kernel debugging when your machine crashes very
+         early before the console code is initialized. For normal operation
+         it is not recommended because it looks ugly and doesn't cooperate
+         with klogd/syslogd or the X server. You should normally N here,
+         unless you want to debug such a crash.
+
+config DEBUG_STACKOVERFLOW
+       bool "Check for stack overflows"
+       depends on DEBUG_KERNEL
+
+config DEBUG_SLAB
+       bool "Debug memory allocations"
+       depends on DEBUG_KERNEL
+       help
+         Say Y here to have the kernel do limited verification on memory
+         allocation as well as poisoning memory on free to catch use of freed
+         memory.
+
+config MAGIC_SYSRQ
+       bool "Magic SysRq key"
+       depends on DEBUG_KERNEL
+       help
+         If you say Y here, you will have some control over the system even
+         if the system crashes for example during kernel debugging (e.g., you
+         will be able to flush the buffer cache to disk, reboot the system
+         immediately or dump some status information). This is accomplished
+         by pressing various keys while holding SysRq (Alt+PrintScreen). It
+         also works on a serial console (on PC hardware at least), if you
+         send a BREAK and then within 5 seconds a command keypress. The
+         keys are documented in <file:Documentation/sysrq.txt>. Don't say Y
+         unless you really know what this hack does.
+
+config DEBUG_SPINLOCK
+       bool "Spinlock debugging"
+       depends on DEBUG_KERNEL
+       help
+         Say Y here and build SMP to catch missing spinlock initialization
+         and certain other kinds of spinlock errors commonly made.  This is
+         best used in conjunction with the NMI watchdog so that spinlock
+         deadlocks are also debuggable.
+
+config DEBUG_SPINLOCK_SLEEP
+       bool "Sleep-inside-spinlock checking"
+       depends on DEBUG_KERNEL
+       help
+         If you say Y here, various routines which may sleep will become very
+         noisy if they are called with a spinlock held.
+
+config DEBUG_PAGEALLOC
+       bool "Page alloc debugging"
+       depends on DEBUG_KERNEL
+       help
+         Unmap pages from the kernel linear mapping after free_pages().
+         This results in a large slowdown, but helps to find certain types
+         of memory corruptions.
+
+config DEBUG_HIGHMEM
+       bool "Highmem debugging"
+       depends on DEBUG_KERNEL && HIGHMEM
+       help
+         This options enables addition error checking for high memory systems.
+         Disable for production systems.
+
+config DEBUG_INFO
+       bool "Compile the kernel with debug info"
+       depends on DEBUG_KERNEL
+       help
+          If you say Y here the resulting kernel image will include
+         debugging info resulting in a larger kernel image.
+         Say Y here only if you plan to use gdb to debug the kernel.
+         If you don't debug the kernel, you can say N.
+
+config DEBUG_BUGVERBOSE
+       bool "Verbose BUG() reporting"
+       depends on DEBUG_KERNEL
+
+config FRAME_POINTER
+       bool "Compile the kernel with frame pointers"
+       depends on DEBUG_KERNEL
+       help
+         If you say Y here the resulting kernel image will be slightly larger
+         and slower, but it will give very useful debugging information.
+         If you don't debug the kernel, you can say N, but we may not be able
+         to solve problems without frame pointers.
+
+config GDBSTUB
+       bool "Remote GDB kernel debugging"
+       depends on DEBUG_KERNEL
+       select DEBUG_INFO
+       select FRAME_POINTER
+       help
+         If you say Y here, it will be possible to remotely debug the kernel
+         using gdb. This enlarges your kernel ELF image disk size by several
+         megabytes and requires a machine with more than 16 MB, better 32 MB
+         RAM to avoid excessive linking time. This is only useful for kernel
+         hackers. If unsure, say N.
+
+choice
+       prompt "GDB stub port"
+       default GDBSTUB_UART1
+       depends on GDBSTUB
+       help
+         Select the on-CPU port used for GDB-stub
+
+config GDBSTUB_UART0
+       bool "/dev/ttyS0"
+
+config GDBSTUB_UART1
+       bool "/dev/ttyS1"
+
+endchoice
+
+config GDBSTUB_IMMEDIATE
+       bool "Break into GDB stub immediately"
+       depends on GDBSTUB
+       help
+         If you say Y here, GDB stub will break into the program as soon as
+         possible, leaving the program counter at the beginning of
+         start_kernel() in init/main.c.
+
+config GDB_CONSOLE
+       bool "Console output to GDB"
+       depends on KGDB
+       help
+         If you are using GDB for remote debugging over a serial port and
+         would like kernel messages to be formatted into GDB $O packets so
+         that GDB prints them as program output, say 'Y'.
+
+endmenu
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
diff --git a/arch/frv/Makefile b/arch/frv/Makefile
new file mode 100644 (file)
index 0000000..54046d2
--- /dev/null
@@ -0,0 +1,118 @@
+#
+# frv/Makefile
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies. Remember to do have actions
+# for "archclean" and "archdep" for cleaning up and making dependencies for
+# this architecture
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (c) 2003, 2004 Red Hat Inc.
+# - Written by David Howells <dhowells@redhat.com>
+# - Derived from arch/m68knommu/Makefile,
+#      Copyright (c) 1999,2001  D. Jeff Dionne <jeff@lineo.ca>,
+#      Rt-Control Inc. / Lineo, Inc.
+#
+# Copyright (C) 1998,1999  D. Jeff Dionne <jeff@uclinux.org>,
+#                          Kenneth Albanowski <kjahds@kjahds.com>,
+#
+# Based on arch/m68k/Makefile:
+# Copyright (C) 1994 by Hamish Macdonald
+#
+
+CCSPECS        := $(shell $(CC) -v 2>&1 | grep "^Reading specs from " | head -1 | cut -c20-)
+CCDIR  := $(strip $(patsubst %/specs,%,$(CCSPECS)))
+CPUCLASS := fr400
+
+# test for cross compiling
+COMPILE_ARCH = $(shell uname -m)
+
+ifdef CONFIG_MMU
+UTS_SYSNAME = -DUTS_SYSNAME=\"Linux\"
+else
+UTS_SYSNAME = -DUTS_SYSNAME=\"uClinux\"
+endif
+
+ARCHMODFLAGS   += -G0 -mlong-calls
+
+ifdef CONFIG_GPREL_DATA_8
+CFLAGS         += -G8
+else
+ifdef CONFIG_GPREL_DATA_4
+CFLAGS         += -G4
+else
+ifdef CONFIG_GPREL_DATA_NONE
+CFLAGS         += -G0
+endif
+endif
+endif
+
+#LDFLAGS_vmlinux       := -Map linkmap.txt
+
+ifdef CONFIG_GC_SECTIONS
+CFLAGS         += -ffunction-sections -fdata-sections
+LINKFLAGS      += --gc-sections
+endif
+
+ifndef CONFIG_FRAME_POINTER
+CFLAGS         += -mno-linked-fp
+endif
+
+ifdef CONFIG_CPU_FR451_COMPILE
+CFLAGS         += -mcpu=fr450
+AFLAGS         += -mcpu=fr450
+ASFLAGS                += -mcpu=fr450
+else
+ifdef CONFIG_CPU_FR551_COMPILE
+CFLAGS         += -mcpu=fr550
+AFLAGS         += -mcpu=fr550
+ASFLAGS                += -mcpu=fr550
+else
+CFLAGS         += -mcpu=fr400
+AFLAGS         += -mcpu=fr400
+ASFLAGS                += -mcpu=fr400
+endif
+endif
+
+# pretend the kernel is going to run on an FR400 with no media-fp unit
+# - reserve CC3 for use with atomic ops
+# - all the extra registers are dealt with only at context switch time
+CFLAGS         += -mno-fdpic -mgpr-32 -msoft-float -mno-media
+CFLAGS         += -ffixed-fcc3 -ffixed-cc3 -ffixed-gr15
+AFLAGS         += -mno-fdpic
+ASFLAGS                += -mno-fdpic
+
+# make sure the .S files get compiled with debug info
+# and disable optimisations that are unhelpful whilst debugging
+ifdef CONFIG_DEBUG_INFO
+CFLAGS         += -O1
+AFLAGS         += -Wa,--gdwarf2
+ASFLAGS                += -Wa,--gdwarf2
+endif
+
+head-y         := arch/frv/kernel/head.o arch/frv/kernel/init_task.o
+
+core-y         += arch/frv/kernel/ arch/frv/mm/
+libs-y         += arch/frv/lib/
+
+core-$(CONFIG_MB93090_MB00)    += arch/frv/mb93090-mb00/
+
+all: Image
+
+Image: vmlinux
+       $(Q)$(MAKE) $(build)=arch/frv/boot $@
+
+bootstrap:
+       $(Q)$(MAKEBOOT) bootstrap
+
+archmrproper:
+       $(Q)$(MAKE) -C arch/frv/boot mrproper
+
+archclean:
+       $(Q)$(MAKE) -C arch/frv/boot clean
+
+archdep: scripts/mkdep symlinks
+       $(Q)$(MAKE) -C arch/frv/boot dep
diff --git a/arch/frv/boot/Makefile b/arch/frv/boot/Makefile
new file mode 100644 (file)
index 0000000..d75e0d7
--- /dev/null
@@ -0,0 +1,73 @@
+#
+# arch/arm/boot/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995-2000 Russell King
+#
+
+SYSTEM =$(TOPDIR)/$(LINUX)
+
+ZTEXTADDR       = 0x02080000
+PARAMS_PHYS     = 0x0207c000
+INITRD_PHYS     = 0x02180000
+INITRD_VIRT     = 0x02180000
+
+#
+# If you don't define ZRELADDR above,
+# then it defaults to ZTEXTADDR
+#
+ifeq ($(ZRELADDR),)
+ZRELADDR       = $(ZTEXTADDR)
+endif
+
+export SYSTEM ZTEXTADDR ZBSSADDR ZRELADDR INITRD_PHYS INITRD_VIRT PARAMS_PHYS
+
+Image: $(obj)/Image
+
+targets: $(obj)/Image
+
+$(obj)/Image: vmlinux FORCE
+       $(OBJCOPY) -O binary -R .note -R .comment -S vmlinux $@
+
+#$(obj)/Image: $(CONFIGURE) $(SYSTEM)
+#      $(OBJCOPY) -O binary -R .note -R .comment -g -S $(SYSTEM) $@
+
+bzImage: zImage
+
+zImage:        $(CONFIGURE) compressed/$(LINUX)
+       $(OBJCOPY) -O binary -R .note -R .comment -S compressed/$(LINUX) $@
+
+bootpImage: bootp/bootp
+       $(OBJCOPY) -O binary -R .note -R .comment -S bootp/bootp $@
+
+compressed/$(LINUX): $(TOPDIR)/$(LINUX) dep
+       @$(MAKE) -C compressed $(LINUX)
+
+bootp/bootp: zImage initrd
+       @$(MAKE) -C bootp bootp
+
+initrd:
+       @test "$(INITRD_VIRT)" != "" || (echo This architecture does not support INITRD; exit -1)
+       @test "$(INITRD)" != "" || (echo You must specify INITRD; exit -1)
+
+#
+# installation
+#
+install: $(CONFIGURE) Image
+       sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
+
+zinstall: $(CONFIGURE) zImage
+       sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
+
+#
+# miscellany
+#
+mrproper clean:
+       $(RM) Image zImage bootpImage
+#      @$(MAKE) -C compressed clean
+#      @$(MAKE) -C bootp clean
+
+dep:
diff --git a/arch/frv/kernel/Makefile b/arch/frv/kernel/Makefile
new file mode 100644 (file)
index 0000000..981c2c7
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# Makefile for the linux kernel.
+#
+
+heads-y                                := head-uc-fr401.o head-uc-fr451.o head-uc-fr555.o
+heads-$(CONFIG_MMU)            := head-mmu-fr451.o
+
+extra-y:= head.o init_task.o vmlinux.lds
+
+obj-y := $(heads-y) entry.o entry-table.o break.o switch_to.o kernel_thread.o \
+        process.o traps.o ptrace.o signal.o dma.o \
+        sys_frv.o time.o semaphore.o setup.o frv_ksyms.o \
+        debug-stub.o irq.o irq-routing.o sleep.o uaccess.o
+
+obj-$(CONFIG_GDBSTUB)          += gdb-stub.o gdb-io.o
+
+obj-$(CONFIG_MB93091_VDK)      += irq-mb93091.o
+obj-$(CONFIG_MB93093_PDK)      += irq-mb93093.o
+obj-$(CONFIG_FUJITSU_MB93493)  += irq-mb93493.o
+obj-$(CONFIG_PM)               += pm.o cmode.o
+obj-$(CONFIG_MB93093_PDK)      += pm-mb93093.o
+obj-$(CONFIG_SYSCTL)           += sysctl.o
diff --git a/arch/frv/kernel/break.S b/arch/frv/kernel/break.S
new file mode 100644 (file)
index 0000000..33233dc
--- /dev/null
@@ -0,0 +1,720 @@
+/* break.S: Break interrupt handling (kept separate from entry.S)
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/ptrace.h>
+#include <asm/spr-regs.h>
+
+#include <asm/errno.h>
+
+#
+# the break handler has its own stack
+#
+       .section        .bss.stack
+       .globl          __break_user_context
+       .balign         8192
+__break_stack:
+       .space          (8192 - (USER_CONTEXT_SIZE + REG__DEBUG_XTRA)) & ~7
+__break_stack_tos:
+       .space          REG__DEBUG_XTRA
+__break_user_context:
+       .space          USER_CONTEXT_SIZE
+
+#
+# miscellaneous variables
+#
+       .section        .bss
+#ifdef CONFIG_MMU
+       .globl          __break_tlb_miss_real_return_info
+__break_tlb_miss_real_return_info:
+       .balign         8
+       .space          2*4                     /* saved PCSR, PSR for TLB-miss handler fixup */
+#endif
+
+__break_trace_through_exceptions:
+       .space          4
+
+#define CS2_ECS1       0xe1200000
+#define CS2_USERLED    0x4
+
+.macro LEDS val,reg
+#      sethi.p         %hi(CS2_ECS1+CS2_USERLED),gr30
+#      setlo           %lo(CS2_ECS1+CS2_USERLED),gr30
+#      setlos          #~\val,\reg
+#      st              \reg,@(gr30,gr0)
+#      setlos          #0x5555,\reg
+#      sethi.p         %hi(0xffc00100),gr30
+#      setlo           %lo(0xffc00100),gr30
+#      sth             \reg,@(gr30,gr0)
+#      membar
+.endm
+
+###############################################################################
+#
+# entry point for Break Exceptions/Interrupts
+#
+###############################################################################
+       .text
+       .balign         4
+       .globl          __entry_break
+__entry_break:
+#ifdef CONFIG_MMU
+       movgs           gr31,scr3
+#endif
+       LEDS            0x1001,gr31
+
+       sethi.p         %hi(__break_user_context),gr31
+       setlo           %lo(__break_user_context),gr31
+
+       stdi            gr2,@(gr31,#REG_GR(2))
+       movsg           ccr,gr3
+       sti             gr3,@(gr31,#REG_CCR)
+
+       # catch the return from a TLB-miss handler that had single-step disabled
+       # traps will be enabled, so we have to do this now
+#ifdef CONFIG_MMU
+       movsg           bpcsr,gr3
+       sethi.p         %hi(__break_tlb_miss_return_breaks_here),gr2
+       setlo           %lo(__break_tlb_miss_return_breaks_here),gr2
+       subcc           gr2,gr3,gr0,icc0
+       beq             icc0,#2,__break_return_singlestep_tlbmiss
+#endif
+
+       # determine whether we have stepped through into an exception
+       # - we need to take special action to suspend h/w single stepping if we've done
+       #   that, so that the gdbstub doesn't get bogged down endlessly stepping through
+       #   external interrupt handling
+       movsg           bpsr,gr3
+       andicc          gr3,#BPSR_BET,gr0,icc0
+       bne             icc0,#2,__break_maybe_userspace /* jump if PSR.ET was 1 */
+
+       LEDS            0x1003,gr2
+
+       movsg           brr,gr3
+       andicc          gr3,#BRR_ST,gr0,icc0
+       andicc.p        gr3,#BRR_SB,gr0,icc1
+       bne             icc0,#2,__break_step            /* jump if single-step caused break */
+       beq             icc1,#2,__break_continue        /* jump if BREAK didn't cause break */
+
+       LEDS            0x1007,gr2
+
+       # handle special breaks
+       movsg           bpcsr,gr3
+
+       sethi.p         %hi(__entry_return_singlestep_breaks_here),gr2
+       setlo           %lo(__entry_return_singlestep_breaks_here),gr2
+       subcc           gr2,gr3,gr0,icc0
+       beq             icc0,#2,__break_return_singlestep
+
+       bra             __break_continue
+
+
+###############################################################################
+#
+# handle BREAK instruction in kernel-mode exception epilogue
+#
+###############################################################################
+__break_return_singlestep:
+       LEDS            0x100f,gr2
+
+       # special break insn requests single-stepping to be turned back on
+       #               HERE            RETT
+       # PSR.ET        0               0
+       # PSR.PS        old PSR.S       ?
+       # PSR.S         1               1
+       # BPSR.ET       0               1 (can't have caused orig excep otherwise)
+       # BPSR.BS       1               old PSR.S
+       movsg           dcr,gr2
+       sethi.p         %hi(DCR_SE),gr3
+       setlo           %lo(DCR_SE),gr3
+       or              gr2,gr3,gr2
+       movgs           gr2,dcr
+
+       movsg           psr,gr2
+       andi            gr2,#PSR_PS,gr2
+       slli            gr2,#11,gr2                     /* PSR.PS -> BPSR.BS */
+       ori             gr2,#BPSR_BET,gr2               /* 1 -> BPSR.BET */
+       movgs           gr2,bpsr
+
+       # return to the invoker of the original kernel exception
+       movsg           pcsr,gr2
+       movgs           gr2,bpcsr
+
+       LEDS            0x101f,gr2
+
+       ldi             @(gr31,#REG_CCR),gr3
+       movgs           gr3,ccr
+       lddi.p          @(gr31,#REG_GR(2)),gr2
+       xor             gr31,gr31,gr31
+       movgs           gr0,brr
+#ifdef CONFIG_MMU
+       movsg           scr3,gr31
+#endif
+       rett            #1
+
+###############################################################################
+#
+# handle BREAK instruction in TLB-miss handler return path
+#
+###############################################################################
+#ifdef CONFIG_MMU
+__break_return_singlestep_tlbmiss:
+       LEDS            0x1100,gr2
+
+       sethi.p         %hi(__break_tlb_miss_real_return_info),gr3
+       setlo           %lo(__break_tlb_miss_real_return_info),gr3
+       lddi            @(gr3,#0),gr2
+       movgs           gr2,pcsr
+       movgs           gr3,psr
+
+       bra             __break_return_singlestep
+#endif
+
+
+###############################################################################
+#
+# handle single stepping into an exception prologue from kernel mode
+# - we try and catch it whilst it is still in the main vector table
+# - if we catch it there, we have to jump to the fixup handler
+#   - there is a fixup table that has a pointer for every 16b slot in the trap
+#     table
+#
+###############################################################################
+__break_step:
+       LEDS            0x2003,gr2
+
+       # external interrupts seem to escape from the trap table before single
+       # step catches up with them
+       movsg           bpcsr,gr2
+       sethi.p         %hi(__entry_kernel_external_interrupt),gr3
+       setlo           %lo(__entry_kernel_external_interrupt),gr3
+       subcc           gr2,gr3,gr0,icc0
+       beq             icc0,#2,__break_step_kernel_external_interrupt
+       sethi.p         %hi(__entry_uspace_external_interrupt),gr3
+       setlo           %lo(__entry_uspace_external_interrupt),gr3
+       subcc           gr2,gr3,gr0,icc0
+       beq             icc0,#2,__break_step_uspace_external_interrupt
+
+       LEDS            0x2007,gr2
+
+       # the two main vector tables are adjacent on one 8Kb slab
+       movsg           bpcsr,gr2
+       setlos          #0xffffe000,gr3
+       and             gr2,gr3,gr2
+       sethi.p         %hi(__trap_tables),gr3
+       setlo           %lo(__trap_tables),gr3
+       subcc           gr2,gr3,gr0,icc0
+       bne             icc0,#2,__break_continue
+
+       LEDS            0x200f,gr2
+
+       # skip workaround if so requested by GDB
+       sethi.p         %hi(__break_trace_through_exceptions),gr3
+       setlo           %lo(__break_trace_through_exceptions),gr3
+       ld              @(gr3,gr0),gr3
+       subcc           gr3,gr0,gr0,icc0
+       bne             icc0,#0,__break_continue
+
+       LEDS            0x201f,gr2
+
+       # access the fixup table - there's a 1:1 mapping between the slots in the trap tables and
+       # the slots in the trap fixup tables allowing us to simply divide the offset into the
+       # former by 4 to access the latter
+       sethi.p         %hi(__trap_tables),gr3
+       setlo           %lo(__trap_tables),gr3
+       movsg           bpcsr,gr2
+       sub             gr2,gr3,gr2
+       srli.p          gr2,#2,gr2
+
+       sethi           %hi(__trap_fixup_tables),gr3
+       setlo.p         %lo(__trap_fixup_tables),gr3
+       andi            gr2,#~3,gr2
+       ld              @(gr2,gr3),gr2
+       jmpil           @(gr2,#0)
+
+# step through an internal exception from kernel mode
+       .globl          __break_step_kernel_softprog_interrupt
+__break_step_kernel_softprog_interrupt:
+       sethi.p         %hi(__entry_kernel_softprog_interrupt_reentry),gr3
+       setlo           %lo(__entry_kernel_softprog_interrupt_reentry),gr3
+       bra             __break_return_as_kernel_prologue
+
+# step through an external interrupt from kernel mode
+       .globl          __break_step_kernel_external_interrupt
+__break_step_kernel_external_interrupt:
+       sethi.p         %hi(__entry_kernel_external_interrupt_reentry),gr3
+       setlo           %lo(__entry_kernel_external_interrupt_reentry),gr3
+
+__break_return_as_kernel_prologue:
+       LEDS            0x203f,gr2
+
+       movgs           gr3,bpcsr
+
+       # do the bit we had to skip
+#ifdef CONFIG_MMU
+       movsg           ear0,gr2                /* EAR0 can get clobbered by gdb-stub (ICI/ICEI) */
+       movgs           gr2,scr2
+#endif
+
+       or.p            sp,gr0,gr2              /* set up the stack pointer */
+       subi            sp,#REG__END,sp
+       sti.p           gr2,@(sp,#REG_SP)
+
+       setlos          #REG__STATUS_STEP,gr2
+       sti             gr2,@(sp,#REG__STATUS)          /* record single step status */
+
+       # cancel single-stepping mode
+       movsg           dcr,gr2
+       sethi.p         %hi(~DCR_SE),gr3
+       setlo           %lo(~DCR_SE),gr3
+       and             gr2,gr3,gr2
+       movgs           gr2,dcr
+
+       LEDS            0x207f,gr2
+
+       ldi             @(gr31,#REG_CCR),gr3
+       movgs           gr3,ccr
+       lddi.p          @(gr31,#REG_GR(2)),gr2
+       xor             gr31,gr31,gr31
+       movgs           gr0,brr
+#ifdef CONFIG_MMU
+       movsg           scr3,gr31
+#endif
+       rett            #1
+
+# step through an internal exception from uspace mode
+       .globl          __break_step_uspace_softprog_interrupt
+__break_step_uspace_softprog_interrupt:
+       sethi.p         %hi(__entry_uspace_softprog_interrupt_reentry),gr3
+       setlo           %lo(__entry_uspace_softprog_interrupt_reentry),gr3
+       bra             __break_return_as_uspace_prologue
+
+# step through an external interrupt from kernel mode
+       .globl          __break_step_uspace_external_interrupt
+__break_step_uspace_external_interrupt:
+       sethi.p         %hi(__entry_uspace_external_interrupt_reentry),gr3
+       setlo           %lo(__entry_uspace_external_interrupt_reentry),gr3
+
+__break_return_as_uspace_prologue:
+       LEDS            0x20ff,gr2
+
+       movgs           gr3,bpcsr
+
+       # do the bit we had to skip
+       sethi.p         %hi(__kernel_frame0_ptr),gr28
+       setlo           %lo(__kernel_frame0_ptr),gr28
+       ldi.p           @(gr28,#0),gr28
+
+       setlos          #REG__STATUS_STEP,gr2
+       sti             gr2,@(gr28,#REG__STATUS)        /* record single step status */
+
+       # cancel single-stepping mode
+       movsg           dcr,gr2
+       sethi.p         %hi(~DCR_SE),gr3
+       setlo           %lo(~DCR_SE),gr3
+       and             gr2,gr3,gr2
+       movgs           gr2,dcr
+
+       LEDS            0x20fe,gr2
+
+       ldi             @(gr31,#REG_CCR),gr3
+       movgs           gr3,ccr
+       lddi.p          @(gr31,#REG_GR(2)),gr2
+       xor             gr31,gr31,gr31
+       movgs           gr0,brr
+#ifdef CONFIG_MMU
+       movsg           scr3,gr31
+#endif
+       rett            #1
+
+#ifdef CONFIG_MMU
+# step through an ITLB-miss handler from user mode
+       .globl          __break_user_insn_tlb_miss
+__break_user_insn_tlb_miss:
+       # we'll want to try the trap stub again
+       sethi.p         %hi(__trap_user_insn_tlb_miss),gr2
+       setlo           %lo(__trap_user_insn_tlb_miss),gr2
+       movgs           gr2,bpcsr
+
+__break_tlb_miss_common:
+       LEDS            0x2101,gr2
+
+       # cancel single-stepping mode
+       movsg           dcr,gr2
+       sethi.p         %hi(~DCR_SE),gr3
+       setlo           %lo(~DCR_SE),gr3
+       and             gr2,gr3,gr2
+       movgs           gr2,dcr
+
+       # we'll swap the real return address for one with a BREAK insn so that we can re-enable
+       # single stepping on return
+       movsg           pcsr,gr2
+       sethi.p         %hi(__break_tlb_miss_real_return_info),gr3
+       setlo           %lo(__break_tlb_miss_real_return_info),gr3
+       sti             gr2,@(gr3,#0)
+
+       sethi.p         %hi(__break_tlb_miss_return_break),gr2
+       setlo           %lo(__break_tlb_miss_return_break),gr2
+       movgs           gr2,pcsr
+
+       # we also have to fudge PSR because the return BREAK is in kernel space and we want
+       # to get a BREAK fault not an access violation should the return be to userspace
+       movsg           psr,gr2
+       sti.p           gr2,@(gr3,#4)
+       ori             gr2,#PSR_PS,gr2
+       movgs           gr2,psr
+
+       LEDS            0x2102,gr2
+
+       ldi             @(gr31,#REG_CCR),gr3
+       movgs           gr3,ccr
+       lddi            @(gr31,#REG_GR(2)),gr2
+       movsg           scr3,gr31
+       movgs           gr0,brr
+       rett            #1
+
+# step through a DTLB-miss handler from user mode
+       .globl          __break_user_data_tlb_miss
+__break_user_data_tlb_miss:
+       # we'll want to try the trap stub again
+       sethi.p         %hi(__trap_user_data_tlb_miss),gr2
+       setlo           %lo(__trap_user_data_tlb_miss),gr2
+       movgs           gr2,bpcsr
+       bra             __break_tlb_miss_common
+
+# step through an ITLB-miss handler from kernel mode
+       .globl          __break_kernel_insn_tlb_miss
+__break_kernel_insn_tlb_miss:
+       # we'll want to try the trap stub again
+       sethi.p         %hi(__trap_kernel_insn_tlb_miss),gr2
+       setlo           %lo(__trap_kernel_insn_tlb_miss),gr2
+       movgs           gr2,bpcsr
+       bra             __break_tlb_miss_common
+
+# step through a DTLB-miss handler from kernel mode
+       .globl          __break_kernel_data_tlb_miss
+__break_kernel_data_tlb_miss:
+       # we'll want to try the trap stub again
+       sethi.p         %hi(__trap_kernel_data_tlb_miss),gr2
+       setlo           %lo(__trap_kernel_data_tlb_miss),gr2
+       movgs           gr2,bpcsr
+       bra             __break_tlb_miss_common
+#endif
+
+###############################################################################
+#
+# handle debug events originating with userspace
+#
+###############################################################################
+__break_maybe_userspace:
+       LEDS            0x3003,gr2
+
+       setlos          #BPSR_BS,gr2
+       andcc           gr3,gr2,gr0,icc0
+       bne             icc0,#0,__break_continue        /* skip if PSR.S was 1 */
+
+       movsg           brr,gr2
+       andicc          gr2,#BRR_ST|BRR_SB,gr0,icc0
+       beq             icc0,#0,__break_continue        /* jump if not BREAK or single-step */
+
+       LEDS            0x3007,gr2
+
+       # do the first part of the exception prologue here
+       sethi.p         %hi(__kernel_frame0_ptr),gr28
+       setlo           %lo(__kernel_frame0_ptr),gr28
+       ldi             @(gr28,#0),gr28
+       andi            gr28,#~7,gr28
+
+       # set up the kernel stack pointer
+       sti             sp  ,@(gr28,#REG_SP)
+       ori             gr28,0,sp
+       sti             gr0 ,@(gr28,#REG_GR(28))
+
+       stdi            gr20,@(gr28,#REG_GR(20))
+       stdi            gr22,@(gr28,#REG_GR(22))
+
+       movsg           tbr,gr20
+       movsg           bpcsr,gr21
+       movsg           psr,gr22
+
+       # determine the exception type and cancel single-stepping mode
+       or              gr0,gr0,gr23
+
+       movsg           dcr,gr2
+       sethi.p         %hi(DCR_SE),gr3
+       setlo           %lo(DCR_SE),gr3
+       andcc           gr2,gr3,gr0,icc0
+       beq             icc0,#0,__break_no_user_sstep   /* must have been a BREAK insn */
+
+       not             gr3,gr3
+       and             gr2,gr3,gr2
+       movgs           gr2,dcr
+       ori             gr23,#REG__STATUS_STEP,gr23
+
+__break_no_user_sstep:
+       LEDS            0x300f,gr2
+
+       movsg           brr,gr2
+       andi            gr2,#BRR_ST|BRR_SB,gr2
+       slli            gr2,#1,gr2
+       or              gr23,gr2,gr23
+       sti.p           gr23,@(gr28,#REG__STATUS)       /* record single step status */
+
+       # adjust the value acquired from TBR - this indicates the exception
+       setlos          #~TBR_TT,gr2
+       and.p           gr20,gr2,gr20
+       setlos          #TBR_TT_BREAK,gr2
+       or.p            gr20,gr2,gr20
+
+       # fudge PSR.PS and BPSR.BS to return to kernel mode through the trap
+       # table as trap 126
+       andi            gr22,#~PSR_PS,gr22              /* PSR.PS should be 0 */
+       movgs           gr22,psr
+
+       setlos          #BPSR_BS,gr2                    /* BPSR.BS should be 1 and BPSR.BET 0 */
+       movgs           gr2,bpsr
+
+       # return through remainder of the exception prologue
+       # - need to load gr23 with return handler address
+       sethi.p         %hi(__entry_return_from_user_exception),gr23
+       setlo           %lo(__entry_return_from_user_exception),gr23
+       sethi.p         %hi(__entry_common),gr3
+       setlo           %lo(__entry_common),gr3
+       movgs           gr3,bpcsr
+
+       LEDS            0x301f,gr2
+
+       ldi             @(gr31,#REG_CCR),gr3
+       movgs           gr3,ccr
+       lddi.p          @(gr31,#REG_GR(2)),gr2
+       xor             gr31,gr31,gr31
+       movgs           gr0,brr
+#ifdef CONFIG_MMU
+       movsg           scr3,gr31
+#endif
+       rett            #1
+
+###############################################################################
+#
+# resume normal debug-mode entry
+#
+###############################################################################
+__break_continue:
+       LEDS            0x4003,gr2
+
+       # set up the kernel stack pointer
+       sti             sp,@(gr31,#REG_SP)
+
+       sethi.p         %hi(__break_stack_tos),sp
+       setlo           %lo(__break_stack_tos),sp
+
+       # finish building the exception frame
+       stdi            gr4 ,@(gr31,#REG_GR(4))
+       stdi            gr6 ,@(gr31,#REG_GR(6))
+       stdi            gr8 ,@(gr31,#REG_GR(8))
+       stdi            gr10,@(gr31,#REG_GR(10))
+       stdi            gr12,@(gr31,#REG_GR(12))
+       stdi            gr14,@(gr31,#REG_GR(14))
+       stdi            gr16,@(gr31,#REG_GR(16))
+       stdi            gr18,@(gr31,#REG_GR(18))
+       stdi            gr20,@(gr31,#REG_GR(20))
+       stdi            gr22,@(gr31,#REG_GR(22))
+       stdi            gr24,@(gr31,#REG_GR(24))
+       stdi            gr26,@(gr31,#REG_GR(26))
+       sti             gr0 ,@(gr31,#REG_GR(28))        /* NULL frame pointer */
+       sti             gr29,@(gr31,#REG_GR(29))
+       sti             gr30,@(gr31,#REG_GR(30))
+       sti             gr8 ,@(gr31,#REG_ORIG_GR8)
+
+#ifdef CONFIG_MMU
+       movsg           scr3,gr19
+       sti             gr19,@(gr31,#REG_GR(31))
+#endif
+
+       movsg           bpsr ,gr19
+       movsg           tbr  ,gr20
+       movsg           bpcsr,gr21
+       movsg           psr  ,gr22
+       movsg           isr  ,gr23
+       movsg           cccr ,gr25
+       movsg           lr   ,gr26
+       movsg           lcr  ,gr27
+
+       andi.p          gr22,#~(PSR_S|PSR_ET),gr5       /* rebuild PSR */
+       andi            gr19,#PSR_ET,gr4
+       or.p            gr4,gr5,gr5
+       srli            gr19,#10,gr4
+       andi            gr4,#PSR_S,gr4
+       or.p            gr4,gr5,gr5
+
+       setlos          #-1,gr6
+       sti             gr20,@(gr31,#REG_TBR)
+       sti             gr21,@(gr31,#REG_PC)
+       sti             gr5 ,@(gr31,#REG_PSR)
+       sti             gr23,@(gr31,#REG_ISR)
+       sti             gr25,@(gr31,#REG_CCCR)
+       stdi            gr26,@(gr31,#REG_LR)
+       sti             gr6 ,@(gr31,#REG_SYSCALLNO)
+
+       # store CPU-specific regs
+       movsg           iacc0h,gr4
+       movsg           iacc0l,gr5
+       stdi            gr4,@(gr31,#REG_IACC0)
+
+       movsg           gner0,gr4
+       movsg           gner1,gr5
+       stdi            gr4,@(gr31,#REG_GNER0)
+
+       # build the debug register frame
+       movsg           brr,gr4
+       movgs           gr0,brr
+       movsg           nmar,gr5
+       movsg           dcr,gr6
+
+       stdi            gr4 ,@(gr31,#REG_BRR)
+       sti             gr19,@(gr31,#REG_BPSR)
+       sti.p           gr6 ,@(gr31,#REG_DCR)
+
+       # trap exceptions during break handling and disable h/w breakpoints/watchpoints
+       sethi           %hi(DCR_EBE),gr5
+       setlo.p         %lo(DCR_EBE),gr5
+       sethi           %hi(__entry_breaktrap_table),gr4
+       setlo           %lo(__entry_breaktrap_table),gr4
+       movgs           gr5,dcr
+       movgs           gr4,tbr
+
+       # set up kernel global registers
+       sethi.p         %hi(__kernel_current_task),gr5
+       setlo           %lo(__kernel_current_task),gr5
+       ld              @(gr5,gr0),gr29
+       ldi.p           @(gr29,#4),gr15         ; __current_thread_info = current->thread_info
+
+       sethi           %hi(_gp),gr16
+       setlo.p         %lo(_gp),gr16
+
+       # make sure we (the kernel) get div-zero and misalignment exceptions
+       setlos          #ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5
+       movgs           gr5,isr
+
+       # enter the GDB stub
+       LEDS            0x4007,gr2
+
+       or.p            gr0,gr0,fp
+       call            debug_stub
+
+       LEDS            0x403f,gr2
+
+       # return from break
+       lddi            @(gr31,#REG_IACC0),gr4
+       movgs           gr4,iacc0h
+       movgs           gr5,iacc0l
+
+       lddi            @(gr31,#REG_GNER0),gr4
+       movgs           gr4,gner0
+       movgs           gr5,gner1
+
+       lddi            @(gr31,#REG_LR)  ,gr26
+       lddi            @(gr31,#REG_CCR) ,gr24
+       lddi            @(gr31,#REG_PSR) ,gr22
+       ldi             @(gr31,#REG_PC)  ,gr21
+       ldi             @(gr31,#REG_TBR) ,gr20
+       ldi.p           @(gr31,#REG_DCR) ,gr6
+
+       andi            gr22,#PSR_S,gr19                /* rebuild BPSR */
+       andi.p          gr22,#PSR_ET,gr5
+       slli            gr19,#10,gr19
+       or              gr5,gr19,gr19
+
+       movgs           gr6 ,dcr
+       movgs           gr19,bpsr
+       movgs           gr20,tbr
+       movgs           gr21,bpcsr
+       movgs           gr23,isr
+       movgs           gr24,ccr
+       movgs           gr25,cccr
+       movgs           gr26,lr
+       movgs           gr27,lcr
+
+       LEDS            0x407f,gr2
+
+#ifdef CONFIG_MMU
+       ldi             @(gr31,#REG_GR(31)),gr2
+       movgs           gr2,scr3
+#endif
+
+       ldi             @(gr31,#REG_GR(30)),gr30
+       ldi             @(gr31,#REG_GR(29)),gr29
+       lddi            @(gr31,#REG_GR(26)),gr26
+       lddi            @(gr31,#REG_GR(24)),gr24
+       lddi            @(gr31,#REG_GR(22)),gr22
+       lddi            @(gr31,#REG_GR(20)),gr20
+       lddi            @(gr31,#REG_GR(18)),gr18
+       lddi            @(gr31,#REG_GR(16)),gr16
+       lddi            @(gr31,#REG_GR(14)),gr14
+       lddi            @(gr31,#REG_GR(12)),gr12
+       lddi            @(gr31,#REG_GR(10)),gr10
+       lddi            @(gr31,#REG_GR(8)) ,gr8
+       lddi            @(gr31,#REG_GR(6)) ,gr6
+       lddi            @(gr31,#REG_GR(4)) ,gr4
+       lddi            @(gr31,#REG_GR(2)) ,gr2
+       ldi.p           @(gr31,#REG_SP)    ,sp
+
+       xor             gr31,gr31,gr31
+       movgs           gr0,brr
+#ifdef CONFIG_MMU
+       movsg           scr3,gr31
+#endif
+       rett            #1
+
+###################################################################################################
+#
+# GDB stub "system calls"
+#
+###################################################################################################
+
+#ifdef CONFIG_GDBSTUB
+       # void gdbstub_console_write(struct console *con, const char *p, unsigned n)
+       .globl          gdbstub_console_write
+gdbstub_console_write:
+       break
+       bralr
+#endif
+
+       # GDB stub BUG() trap
+       # GR8 is the proposed signal number
+       .globl          __debug_bug_trap
+__debug_bug_trap:
+       break
+       bralr
+
+       # transfer kernel exeception to GDB for handling
+       .globl          __break_hijack_kernel_event
+__break_hijack_kernel_event:
+       break
+       .globl          __break_hijack_kernel_event_breaks_here
+__break_hijack_kernel_event_breaks_here:
+       nop
+
+#ifdef CONFIG_MMU
+       # handle a return from TLB-miss that requires single-step reactivation
+       .globl          __break_tlb_miss_return_break
+__break_tlb_miss_return_break:
+       break
+__break_tlb_miss_return_breaks_here:
+       nop
+#endif
+
+       # guard the first .text label in the next file from confusion
+       nop
diff --git a/arch/frv/kernel/cmode.S b/arch/frv/kernel/cmode.S
new file mode 100644 (file)
index 0000000..6591e6a
--- /dev/null
@@ -0,0 +1,190 @@
+/* cmode.S: clock mode management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Woodhouse (dwmw2@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/ptrace.h>
+#include <asm/errno.h>
+#include <asm/cache.h>
+#include <asm/spr-regs.h>
+
+#define __addr_MASK    0xfeff9820      /* interrupt controller mask */
+
+#define __addr_SDRAMC  0xfe000400      /* SDRAM controller regs */
+#define SDRAMC_DSTS    0x28            /* SDRAM status */
+#define SDRAMC_DSTS_SSI        0x00000001      /* indicates that the SDRAM is in self-refresh mode */
+#define SDRAMC_DRCN    0x30            /* SDRAM refresh control */
+#define SDRAMC_DRCN_SR 0x00000001      /* transition SDRAM into self-refresh mode */
+#define __addr_CLKC    0xfeff9a00
+#define CLKC_SWCMODE   0x00000008
+#define __addr_LEDS    0xe1200004
+
+.macro li v r
+       sethi.p         %hi(\v),\r
+       setlo           %lo(\v),\r
+.endm
+
+       .text
+       .balign         4
+
+
+###############################################################################
+#
+# Change CMODE
+# - void frv_change_cmode(int cmode)
+#
+###############################################################################
+       .globl          frv_change_cmode
+        .type          frv_change_cmode,@function
+
+.macro LEDS v
+#ifdef DEBUG_CMODE
+       setlos  #~\v,gr10
+       sti     gr10,@(gr11,#0)
+       membar
+#endif
+.endm
+
+frv_change_cmode:
+       movsg           lr,gr9
+#ifdef DEBUG_CMODE
+       li              __addr_LEDS,gr11
+#endif
+       dcef            @(gr0,gr0),#1
+
+       # Shift argument left by 24 bits to fit in SWCMODE register later.
+       slli            gr8,#24,gr8
+
+       # (1) Set '0' in the PSR.ET bit, and prohibit interrupts.
+       movsg           psr,gr14
+       andi            gr14,#~PSR_ET,gr3
+       movgs           gr3,psr
+
+#if 0 // Fujitsu recommend to skip this and will update docs.
+       # (2) Set '0' to all bits of the MASK register of the interrupt
+       #     controller, and mask interrupts.
+       li              __addr_MASK,gr12
+       ldi             @(gr12,#0),gr13
+       li              0xffff0000,gr4
+       sti             gr4,@(gr12,#0)
+#endif
+
+       # (3) Stop the transfer function of DMAC. Stop all the bus masters
+       #     to access SDRAM and the internal resources.
+
+       # (already done by caller)
+
+       # (4) Preload a series of following instructions to the instruction
+       #     cache.
+       li              #__cmode_icache_lock_start,gr3
+       li              #__cmode_icache_lock_end,gr4
+
+1:     icpl            gr3,gr0,#1
+       addi            gr3,#L1_CACHE_BYTES,gr3
+       cmp             gr4,gr3,icc0
+       bhi             icc0,#0,1b
+
+       # Set up addresses in regs for later steps.
+       setlos          SDRAMC_DRCN_SR,gr3
+       li              __addr_SDRAMC,gr4
+       li              __addr_CLKC,gr5
+       ldi             @(gr5,#0),gr6
+       li              #0x80000000,gr7
+       or              gr6,gr7,gr6
+
+       bra             __cmode_icache_lock_start
+
+       .balign L1_CACHE_BYTES
+__cmode_icache_lock_start:
+
+       # (5) Flush the content of all caches by the DCEF instruction.
+       dcef            @(gr0,gr0),#1
+
+       # (6) Execute loading the dummy for SDRAM.
+       ldi             @(gr9,#0),gr0
+
+       # (7) Set '1' to the DRCN.SR bit, and change SDRAM to the
+       #     self-refresh mode. Execute the dummy load to all memory
+       #     devices set to cacheable on the external bus side in parallel
+       #     with this.
+       sti             gr3,@(gr4,#SDRAMC_DRCN)
+
+       # (8) Execute memory barrier instruction (MEMBAR).
+       membar
+
+       # (9) Read the DSTS register repeatedly until '1' stands in the
+       #     DSTS.SSI field.
+1:     ldi             @(gr4,#SDRAMC_DSTS),gr3
+       andicc          gr3,#SDRAMC_DSTS_SSI,gr3,icc0
+       beq             icc0,#0,1b
+
+       # (10) Execute memory barrier instruction (MEMBAR).
+       membar
+
+#if 1
+       # (11) Set the value of CMODE that you want to change to
+       #      SWCMODE.SWCM[3:0].
+       sti             gr8,@(gr5,#CLKC_SWCMODE)
+
+       # (12) Set '1' to the CLKC.SWEN bit. In that case, do not change
+       #      fields other than SWEN of the CLKC register.
+       sti             gr6,@(gr5,#0)
+#endif
+       # (13) Execute the instruction just after the memory barrier
+       # instruction that executes the self-loop 256 times. (Meanwhile,
+       # the CMODE switch is done.)
+       membar
+       setlos          #256,gr7
+2:     subicc          gr7,#1,gr7,icc0
+       bne             icc0,#2,2b
+
+       LEDS    0x36
+
+       # (14) Release the self-refresh of SDRAM.
+       sti             gr0,@(gr4,#SDRAMC_DRCN)
+
+       # Wait for it...
+3:     ldi             @(gr4,#SDRAMC_DSTS),gr3
+       andicc          gr3,#SDRAMC_DSTS_SSI,gr3,icc0
+       bne             icc0,#2,3b
+
+#if 0
+       li              0x0100000,gr10
+4:     subicc          gr10,#1,gr10,icc0
+
+       bne             icc0,#0,4b
+#endif
+
+__cmode_icache_lock_end:
+
+       li              #__cmode_icache_lock_start,gr3
+       li              #__cmode_icache_lock_end,gr4
+
+4:     icul            gr3
+       addi            gr3,#L1_CACHE_BYTES,gr3
+       cmp             gr4,gr3,icc0
+       bhi             icc0,#0,4b
+
+#if 0 // Fujitsu recommend to skip this and will update docs.
+       # (15) Release the interrupt mask setting of the MASK register of
+       # the interrupt controller if necessary.
+       sti             gr13,@(gr12,#0)
+#endif
+       # (16) Set  1' in the PSR.ET bit, and permit interrupt.
+       movgs           gr14,psr
+
+       bralr
+
+       .size           frv_change_cmode, .-frv_change_cmode
diff --git a/arch/frv/kernel/debug-stub.c b/arch/frv/kernel/debug-stub.c
new file mode 100644 (file)
index 0000000..4761cc4
--- /dev/null
@@ -0,0 +1,259 @@
+/* debug-stub.c: debug-mode stub
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/serial_reg.h>
+
+#include <asm/system.h>
+#include <asm/serial-regs.h>
+#include <asm/timer-regs.h>
+#include <asm/irc-regs.h>
+#include <asm/gdb-stub.h>
+#include "gdb-io.h"
+
+/* CPU board CON5 */
+#define __UART0(X) (*(volatile uint8_t *)(UART0_BASE + (UART_##X)))
+
+#define LSR_WAIT_FOR0(STATE)                   \
+do {                                           \
+} while (!(__UART0(LSR) & UART_LSR_##STATE))
+
+#define FLOWCTL_QUERY0(LINE)   ({ __UART0(MSR) & UART_MSR_##LINE; })
+#define FLOWCTL_CLEAR0(LINE)   do { __UART0(MCR) &= ~UART_MCR_##LINE; } while (0)
+#define FLOWCTL_SET0(LINE)     do { __UART0(MCR) |= UART_MCR_##LINE; } while (0)
+
+#define FLOWCTL_WAIT_FOR0(LINE)                        \
+do {                                           \
+       gdbstub_do_rx();                        \
+} while(!FLOWCTL_QUERY(LINE))
+
+static void __init debug_stub_init(void);
+
+extern asmlinkage void __break_hijack_kernel_event(void);
+extern asmlinkage void __break_hijack_kernel_event_breaks_here(void);
+
+/*****************************************************************************/
+/*
+ * debug mode handler stub
+ * - we come here with the CPU in debug mode and with exceptions disabled
+ * - handle debugging services for userspace
+ */
+asmlinkage void debug_stub(void)
+{
+       unsigned long hsr0;
+       int type = 0;
+
+       static u8 inited = 0;
+       if (!inited) {
+               debug_stub_init();
+               type = -1;
+               inited = 1;
+       }
+
+       hsr0 = __get_HSR(0);
+       if (hsr0 & HSR0_ETMD)
+               __set_HSR(0, hsr0 & ~HSR0_ETMD);
+
+       /* disable single stepping */
+       __debug_regs->dcr &= ~DCR_SE;
+
+       /* kernel mode can propose an exception be handled in debug mode by jumping to a special
+        * location */
+       if (__debug_frame->pc == (unsigned long) __break_hijack_kernel_event_breaks_here) {
+               /* replace the debug frame with the kernel frame and discard
+                * the top kernel context */
+               *__debug_frame = *__frame;
+               __frame = __debug_frame->next_frame;
+               __debug_regs->brr = (__debug_frame->tbr & TBR_TT) << 12;
+               __debug_regs->brr |= BRR_EB;
+       }
+
+       if (__debug_frame->pc == (unsigned long) __debug_bug_trap + 4) {
+               __debug_frame->pc = __debug_frame->lr;
+               type = __debug_frame->gr8;
+       }
+
+#ifdef CONFIG_GDBSTUB
+       gdbstub(type);
+#endif
+
+       if (hsr0 & HSR0_ETMD)
+               __set_HSR(0, __get_HSR(0) | HSR0_ETMD);
+
+} /* end debug_stub() */
+
+/*****************************************************************************/
+/*
+ * debug stub initialisation
+ */
+static void __init debug_stub_init(void)
+{
+       __set_IRR(6, 0xff000000);       /* map ERRs to NMI */
+       __set_IITMR(1, 0x20000000);     /* ERR0/1, UART0/1 IRQ detect levels */
+
+       asm volatile("  movgs   gr0,ibar0       \n"
+                    "  movgs   gr0,ibar1       \n"
+                    "  movgs   gr0,ibar2       \n"
+                    "  movgs   gr0,ibar3       \n"
+                    "  movgs   gr0,dbar0       \n"
+                    "  movgs   gr0,dbmr00      \n"
+                    "  movgs   gr0,dbmr01      \n"
+                    "  movgs   gr0,dbdr00      \n"
+                    "  movgs   gr0,dbdr01      \n"
+                    "  movgs   gr0,dbar1       \n"
+                    "  movgs   gr0,dbmr10      \n"
+                    "  movgs   gr0,dbmr11      \n"
+                    "  movgs   gr0,dbdr10      \n"
+                    "  movgs   gr0,dbdr11      \n"
+                    );
+
+       /* deal with debugging stub initialisation and initial pause */
+       if (__debug_frame->pc == (unsigned long) __debug_stub_init_break)
+               __debug_frame->pc = (unsigned long) start_kernel;
+
+       /* enable the debug events we want to trap */
+       __debug_regs->dcr = DCR_EBE;
+
+#ifdef CONFIG_GDBSTUB
+       gdbstub_init();
+#endif
+
+       __clr_MASK_all();
+       __clr_MASK(15);
+       __clr_RC(15);
+
+} /* end debug_stub_init() */
+
+/*****************************************************************************/
+/*
+ * kernel "exit" trap for gdb stub
+ */
+void debug_stub_exit(int status)
+{
+
+#ifdef CONFIG_GDBSTUB
+       gdbstub_exit(status);
+#endif
+
+} /* end debug_stub_exit() */
+
+/*****************************************************************************/
+/*
+ * send string to serial port
+ */
+void debug_to_serial(const char *p, int n)
+{
+       char ch;
+
+       for (; n > 0; n--) {
+               ch = *p++;
+               FLOWCTL_SET0(DTR);
+               LSR_WAIT_FOR0(THRE);
+               // FLOWCTL_WAIT_FOR(CTS);
+
+               if (ch == 0x0a) {
+                       __UART0(TX) = 0x0d;
+                       mb();
+                       LSR_WAIT_FOR0(THRE);
+                       // FLOWCTL_WAIT_FOR(CTS);
+               }
+               __UART0(TX) = ch;
+               mb();
+
+               FLOWCTL_CLEAR0(DTR);
+       }
+
+} /* end debug_to_serial() */
+
+/*****************************************************************************/
+/*
+ * send string to serial port
+ */
+void debug_to_serial2(const char *fmt, ...)
+{
+       va_list va;
+       char buf[64];
+       int n;
+
+       va_start(va, fmt);
+       n = vsprintf(buf, fmt, va);
+       va_end(va);
+
+       debug_to_serial(buf, n);
+
+} /* end debug_to_serial2() */
+
+/*****************************************************************************/
+/*
+ * set up the ttyS0 serial port baud rate timers
+ */
+void __init console_set_baud(unsigned baud)
+{
+       unsigned value, high, low;
+       u8 lcr;
+
+       /* work out the divisor to give us the nearest higher baud rate */
+       value = __serial_clock_speed_HZ / 16 / baud;
+
+       /* determine the baud rate range */
+       high = __serial_clock_speed_HZ / 16 / value;
+       low = __serial_clock_speed_HZ / 16 / (value + 1);
+
+       /* pick the nearest bound */
+       if (low + (high - low) / 2 > baud)
+               value++;
+
+       lcr = __UART0(LCR);
+       __UART0(LCR) |= UART_LCR_DLAB;
+       mb();
+       __UART0(DLL) = value & 0xff;
+       __UART0(DLM) = (value >> 8) & 0xff;
+       mb();
+       __UART0(LCR) = lcr;
+       mb();
+
+} /* end console_set_baud() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+int __init console_get_baud(void)
+{
+       unsigned value;
+       u8 lcr;
+
+       lcr = __UART0(LCR);
+       __UART0(LCR) |= UART_LCR_DLAB;
+       mb();
+       value =  __UART0(DLM) << 8;
+       value |= __UART0(DLL);
+       __UART0(LCR) = lcr;
+       mb();
+
+       return value;
+} /* end console_get_baud() */
+
+/*****************************************************************************/
+/*
+ * display BUG() info
+ */
+#ifndef CONFIG_NO_KERNEL_MSG
+void __debug_bug_printk(const char *file, unsigned line)
+{
+       printk("kernel BUG at %s:%d!\n", file, line);
+
+} /* end __debug_bug_printk() */
+#endif
diff --git a/arch/frv/kernel/dma.c b/arch/frv/kernel/dma.c
new file mode 100644 (file)
index 0000000..f5de6cf
--- /dev/null
@@ -0,0 +1,464 @@
+/* dma.c: DMA controller management on FR401 and the like
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <asm/dma.h>
+#include <asm/gpio-regs.h>
+#include <asm/irc-regs.h>
+#include <asm/cpu-irqs.h>
+
+struct frv_dma_channel {
+       uint8_t                 flags;
+#define FRV_DMA_FLAGS_RESERVED 0x01
+#define FRV_DMA_FLAGS_INUSE    0x02
+#define FRV_DMA_FLAGS_PAUSED   0x04
+       uint8_t                 cap;            /* capabilities available */
+       int                     irq;            /* completion IRQ */
+       uint32_t                dreqbit;
+       uint32_t                dackbit;
+       uint32_t                donebit;
+       const unsigned long     ioaddr;         /* DMA controller regs addr */
+       const char              *devname;
+       dma_irq_handler_t       handler;
+       void                    *data;
+};
+
+
+#define __get_DMAC(IO,X)       ({ *(volatile unsigned long *)((IO) + DMAC_##X##x); })
+
+#define __set_DMAC(IO,X,V)                                     \
+do {                                                           \
+       *(volatile unsigned long *)((IO) + DMAC_##X##x) = (V);  \
+       mb();                                                   \
+} while(0)
+
+#define ___set_DMAC(IO,X,V)                                    \
+do {                                                           \
+       *(volatile unsigned long *)((IO) + DMAC_##X##x) = (V);  \
+} while(0)
+
+
+static struct frv_dma_channel frv_dma_channels[FRV_DMA_NCHANS] = {
+       [0] = {
+               .cap            = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK | FRV_DMA_CAP_DONE,
+               .irq            = IRQ_CPU_DMA0,
+               .dreqbit        = SIR_DREQ0_INPUT,
+               .dackbit        = SOR_DACK0_OUTPUT,
+               .donebit        = SOR_DONE0_OUTPUT,
+               .ioaddr         = 0xfe000900,
+       },
+       [1] = {
+               .cap            = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK | FRV_DMA_CAP_DONE,
+               .irq            = IRQ_CPU_DMA1,
+               .dreqbit        = SIR_DREQ1_INPUT,
+               .dackbit        = SOR_DACK1_OUTPUT,
+               .donebit        = SOR_DONE1_OUTPUT,
+               .ioaddr         = 0xfe000980,
+       },
+       [2] = {
+               .cap            = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK,
+               .irq            = IRQ_CPU_DMA2,
+               .dreqbit        = SIR_DREQ2_INPUT,
+               .dackbit        = SOR_DACK2_OUTPUT,
+               .ioaddr         = 0xfe000a00,
+       },
+       [3] = {
+               .cap            = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK,
+               .irq            = IRQ_CPU_DMA3,
+               .dreqbit        = SIR_DREQ3_INPUT,
+               .dackbit        = SOR_DACK3_OUTPUT,
+               .ioaddr         = 0xfe000a80,
+       },
+       [4] = {
+               .cap            = FRV_DMA_CAP_DREQ,
+               .irq            = IRQ_CPU_DMA4,
+               .dreqbit        = SIR_DREQ4_INPUT,
+               .ioaddr         = 0xfe001000,
+       },
+       [5] = {
+               .cap            = FRV_DMA_CAP_DREQ,
+               .irq            = IRQ_CPU_DMA5,
+               .dreqbit        = SIR_DREQ5_INPUT,
+               .ioaddr         = 0xfe001080,
+       },
+       [6] = {
+               .cap            = FRV_DMA_CAP_DREQ,
+               .irq            = IRQ_CPU_DMA6,
+               .dreqbit        = SIR_DREQ6_INPUT,
+               .ioaddr         = 0xfe001100,
+       },
+       [7] = {
+               .cap            = FRV_DMA_CAP_DREQ,
+               .irq            = IRQ_CPU_DMA7,
+               .dreqbit        = SIR_DREQ7_INPUT,
+               .ioaddr         = 0xfe001180,
+       },
+};
+
+static DEFINE_RWLOCK(frv_dma_channels_lock);
+
+unsigned long frv_dma_inprogress;
+
+#define frv_clear_dma_inprogress(channel) \
+       atomic_clear_mask(1 << (channel), &frv_dma_inprogress);
+
+#define frv_set_dma_inprogress(channel) \
+       atomic_set_mask(1 << (channel), &frv_dma_inprogress);
+
+/*****************************************************************************/
+/*
+ * DMA irq handler - determine channel involved, grab status and call real handler
+ */
+static irqreturn_t dma_irq_handler(int irq, void *_channel, struct pt_regs *regs)
+{
+       struct frv_dma_channel *channel = _channel;
+
+       frv_clear_dma_inprogress(channel - frv_dma_channels);
+       return channel->handler(channel - frv_dma_channels,
+                               __get_DMAC(channel->ioaddr, CSTR),
+                               channel->data,
+                               regs);
+
+} /* end dma_irq_handler() */
+
+/*****************************************************************************/
+/*
+ * Determine which DMA controllers are present on this CPU
+ */
+void __init frv_dma_init(void)
+{
+       unsigned long psr = __get_PSR();
+       int num_dma, i;
+
+       /* First, determine how many DMA channels are available */
+       switch (PSR_IMPLE(psr)) {
+       case PSR_IMPLE_FR405:
+       case PSR_IMPLE_FR451:
+       case PSR_IMPLE_FR501:
+       case PSR_IMPLE_FR551:
+               num_dma = FRV_DMA_8CHANS;
+               break;
+
+       case PSR_IMPLE_FR401:
+       default:
+               num_dma = FRV_DMA_4CHANS;
+               break;
+       }
+
+       /* Now mark all of the non-existent channels as reserved */
+       for(i = num_dma; i < FRV_DMA_NCHANS; i++)
+               frv_dma_channels[i].flags = FRV_DMA_FLAGS_RESERVED;
+
+} /* end frv_dma_init() */
+
+/*****************************************************************************/
+/*
+ * allocate a DMA controller channel and the IRQ associated with it
+ */
+int frv_dma_open(const char *devname,
+                unsigned long dmamask,
+                int dmacap,
+                dma_irq_handler_t handler,
+                unsigned long irq_flags,
+                void *data)
+{
+       struct frv_dma_channel *channel;
+       int dma, ret;
+       uint32_t val;
+
+       write_lock(&frv_dma_channels_lock);
+
+       ret = -ENOSPC;
+
+       for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) {
+               channel = &frv_dma_channels[dma];
+
+               if (!test_bit(dma, &dmamask))
+                       continue;
+
+               if ((channel->cap & dmacap) != dmacap)
+                       continue;
+
+               if (!frv_dma_channels[dma].flags)
+                       goto found;
+       }
+
+       goto out;
+
+ found:
+       ret = request_irq(channel->irq, dma_irq_handler, irq_flags, devname, channel);
+       if (ret < 0)
+               goto out;
+
+       /* okay, we've allocated all the resources */
+       channel = &frv_dma_channels[dma];
+
+       channel->flags          |= FRV_DMA_FLAGS_INUSE;
+       channel->devname        = devname;
+       channel->handler        = handler;
+       channel->data           = data;
+
+       /* Now make sure we are set up for DMA and not GPIO */
+       /* SIR bit must be set for DMA to work */
+       __set_SIR(channel->dreqbit | __get_SIR());
+       /* SOR bits depend on what the caller requests */
+       val = __get_SOR();
+       if(dmacap & FRV_DMA_CAP_DACK)
+               val |= channel->dackbit;
+       else
+               val &= ~channel->dackbit;
+       if(dmacap & FRV_DMA_CAP_DONE)
+               val |= channel->donebit;
+       else
+               val &= ~channel->donebit;
+       __set_SOR(val);
+
+       ret = dma;
+ out:
+       write_unlock(&frv_dma_channels_lock);
+       return ret;
+} /* end frv_dma_open() */
+
+EXPORT_SYMBOL(frv_dma_open);
+
+/*****************************************************************************/
+/*
+ * close a DMA channel and its associated interrupt
+ */
+void frv_dma_close(int dma)
+{
+       struct frv_dma_channel *channel = &frv_dma_channels[dma];
+       unsigned long flags;
+
+       write_lock_irqsave(&frv_dma_channels_lock, flags);
+
+       free_irq(channel->irq, channel);
+       frv_dma_stop(dma);
+
+       channel->flags &= ~FRV_DMA_FLAGS_INUSE;
+
+       write_unlock_irqrestore(&frv_dma_channels_lock, flags);
+} /* end frv_dma_close() */
+
+EXPORT_SYMBOL(frv_dma_close);
+
+/*****************************************************************************/
+/*
+ * set static configuration on a DMA channel
+ */
+void frv_dma_config(int dma, unsigned long ccfr, unsigned long cctr, unsigned long apr)
+{
+       unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+
+       ___set_DMAC(ioaddr, CCFR, ccfr);
+       ___set_DMAC(ioaddr, CCTR, cctr);
+       ___set_DMAC(ioaddr, APR,  apr);
+       mb();
+
+} /* end frv_dma_config() */
+
+EXPORT_SYMBOL(frv_dma_config);
+
+/*****************************************************************************/
+/*
+ * start a DMA channel
+ */
+void frv_dma_start(int dma,
+                  unsigned long sba, unsigned long dba,
+                  unsigned long pix, unsigned long six, unsigned long bcl)
+{
+       unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+
+       ___set_DMAC(ioaddr, SBA,  sba);
+       ___set_DMAC(ioaddr, DBA,  dba);
+       ___set_DMAC(ioaddr, PIX,  pix);
+       ___set_DMAC(ioaddr, SIX,  six);
+       ___set_DMAC(ioaddr, BCL,  bcl);
+       ___set_DMAC(ioaddr, CSTR, 0);
+       mb();
+
+       __set_DMAC(ioaddr, CCTR, __get_DMAC(ioaddr, CCTR) | DMAC_CCTRx_ACT);
+       frv_set_dma_inprogress(dma);
+
+} /* end frv_dma_start() */
+
+EXPORT_SYMBOL(frv_dma_start);
+
+/*****************************************************************************/
+/*
+ * restart a DMA channel that's been stopped in circular addressing mode by comparison-end
+ */
+void frv_dma_restart_circular(int dma, unsigned long six)
+{
+       unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+
+       ___set_DMAC(ioaddr, SIX,  six);
+       ___set_DMAC(ioaddr, CSTR, __get_DMAC(ioaddr, CSTR) & ~DMAC_CSTRx_CE);
+       mb();
+
+       __set_DMAC(ioaddr, CCTR, __get_DMAC(ioaddr, CCTR) | DMAC_CCTRx_ACT);
+       frv_set_dma_inprogress(dma);
+
+} /* end frv_dma_restart_circular() */
+
+EXPORT_SYMBOL(frv_dma_restart_circular);
+
+/*****************************************************************************/
+/*
+ * stop a DMA channel
+ */
+void frv_dma_stop(int dma)
+{
+       unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+       uint32_t cctr;
+
+       ___set_DMAC(ioaddr, CSTR, 0);
+       cctr = __get_DMAC(ioaddr, CCTR);
+       cctr &= ~(DMAC_CCTRx_IE | DMAC_CCTRx_ACT);
+       cctr |= DMAC_CCTRx_FC;  /* fifo clear */
+       __set_DMAC(ioaddr, CCTR, cctr);
+       __set_DMAC(ioaddr, BCL,  0);
+       frv_clear_dma_inprogress(dma);
+} /* end frv_dma_stop() */
+
+EXPORT_SYMBOL(frv_dma_stop);
+
+/*****************************************************************************/
+/*
+ * test interrupt status of DMA channel
+ */
+int is_frv_dma_interrupting(int dma)
+{
+       unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+
+       return __get_DMAC(ioaddr, CSTR) & (1 << 23);
+
+} /* end is_frv_dma_interrupting() */
+
+EXPORT_SYMBOL(is_frv_dma_interrupting);
+
+/*****************************************************************************/
+/*
+ * dump data about a DMA channel
+ */
+void frv_dma_dump(int dma)
+{
+       unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+       unsigned long cstr, pix, six, bcl;
+
+       cstr = __get_DMAC(ioaddr, CSTR);
+       pix  = __get_DMAC(ioaddr, PIX);
+       six  = __get_DMAC(ioaddr, SIX);
+       bcl  = __get_DMAC(ioaddr, BCL);
+
+       printk("DMA[%d] cstr=%lx pix=%lx six=%lx bcl=%lx\n", dma, cstr, pix, six, bcl);
+
+} /* end frv_dma_dump() */
+
+EXPORT_SYMBOL(frv_dma_dump);
+
+/*****************************************************************************/
+/*
+ * pause all DMA controllers
+ * - called by clock mangling routines
+ * - caller must be holding interrupts disabled
+ */
+void frv_dma_pause_all(void)
+{
+       struct frv_dma_channel *channel;
+       unsigned long ioaddr;
+       unsigned long cstr, cctr;
+       int dma;
+
+       write_lock(&frv_dma_channels_lock);
+
+       for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) {
+               channel = &frv_dma_channels[dma];
+
+               if (!(channel->flags & FRV_DMA_FLAGS_INUSE))
+                       continue;
+
+               ioaddr = channel->ioaddr;
+               cctr = __get_DMAC(ioaddr, CCTR);
+               if (cctr & DMAC_CCTRx_ACT) {
+                       cctr &= ~DMAC_CCTRx_ACT;
+                       __set_DMAC(ioaddr, CCTR, cctr);
+
+                       do {
+                               cstr = __get_DMAC(ioaddr, CSTR);
+                       } while (cstr & DMAC_CSTRx_BUSY);
+
+                       if (cstr & DMAC_CSTRx_FED)
+                               channel->flags |= FRV_DMA_FLAGS_PAUSED;
+                       frv_clear_dma_inprogress(dma);
+               }
+       }
+
+} /* end frv_dma_pause_all() */
+
+EXPORT_SYMBOL(frv_dma_pause_all);
+
+/*****************************************************************************/
+/*
+ * resume paused DMA controllers
+ * - called by clock mangling routines
+ * - caller must be holding interrupts disabled
+ */
+void frv_dma_resume_all(void)
+{
+       struct frv_dma_channel *channel;
+       unsigned long ioaddr;
+       unsigned long cstr, cctr;
+       int dma;
+
+       for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) {
+               channel = &frv_dma_channels[dma];
+
+               if (!(channel->flags & FRV_DMA_FLAGS_PAUSED))
+                       continue;
+
+               ioaddr = channel->ioaddr;
+               cstr = __get_DMAC(ioaddr, CSTR);
+               cstr &= ~(DMAC_CSTRx_FED | DMAC_CSTRx_INT);
+               __set_DMAC(ioaddr, CSTR, cstr);
+
+               cctr = __get_DMAC(ioaddr, CCTR);
+               cctr |= DMAC_CCTRx_ACT;
+               __set_DMAC(ioaddr, CCTR, cctr);
+
+               channel->flags &= ~FRV_DMA_FLAGS_PAUSED;
+               frv_set_dma_inprogress(dma);
+       }
+
+       write_unlock(&frv_dma_channels_lock);
+
+} /* end frv_dma_resume_all() */
+
+EXPORT_SYMBOL(frv_dma_resume_all);
+
+/*****************************************************************************/
+/*
+ * dma status clear
+ */
+void frv_dma_status_clear(int dma)
+{
+       unsigned long ioaddr = frv_dma_channels[dma].ioaddr;
+       uint32_t cctr;
+       ___set_DMAC(ioaddr, CSTR, 0);
+
+       cctr = __get_DMAC(ioaddr, CCTR);
+} /* end frv_dma_status_clear() */
+
+EXPORT_SYMBOL(frv_dma_status_clear);
diff --git a/arch/frv/kernel/entry-table.S b/arch/frv/kernel/entry-table.S
new file mode 100644 (file)
index 0000000..9b9243e
--- /dev/null
@@ -0,0 +1,295 @@
+/* entry-table.S: main trap vector tables and exception jump table
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/spr-regs.h>
+
+###############################################################################
+#
+# Declare the main trap and vector tables
+#
+# There are six tables:
+#
+# (1) The trap table for debug mode
+# (2) The trap table for kernel mode
+# (3) The trap table for user mode
+#
+#     The CPU jumps to an appropriate slot in the appropriate table to perform
+#     exception processing. We have three different tables for the three
+#     different CPU modes because there is no hardware differentiation between
+#     stack pointers for these three modes, and so we have to invent one when
+#     crossing mode boundaries.
+#
+# (4) The exception handler vector table
+#
+#     The user and kernel trap tables use the same prologue for normal
+#     exception processing. The prologue then jumps to the handler in this
+#     table, as indexed by the exception ID from the TBR.
+#
+# (5) The fixup table for kernel-trap single-step
+# (6) The fixup table for user-trap single-step
+#
+#     Due to the way single-stepping works on this CPU (single-step is not
+#     disabled when crossing exception boundaries, only when in debug mode),
+#     we have to catch the single-step event in break.S and jump to the fixup
+#     routine pointed to by this table.
+#
+# The linker script places the user mode and kernel mode trap tables on to
+# the same 8Kb page, so that break.S can be more efficient when performing
+# single-step bypass management
+#
+###############################################################################
+
+       # trap table for entry from debug mode
+       .section        .trap.break,"ax"
+       .balign         256*16
+       .globl          __entry_breaktrap_table
+__entry_breaktrap_table:
+
+       # trap table for entry from user mode
+       .section        .trap.user,"ax"
+       .balign         256*16
+       .globl          __entry_usertrap_table
+__entry_usertrap_table:
+
+       # trap table for entry from kernel mode
+       .section        .trap.kernel,"ax"
+       .balign         256*16
+       .globl          __entry_kerneltrap_table
+__entry_kerneltrap_table:
+
+       # exception handler jump table
+       .section        .trap.vector,"ax"
+       .balign         256*4
+       .globl          __entry_vector_table
+__entry_vector_table:
+
+       # trap fixup table for single-stepping in user mode
+       .section        .trap.fixup.user,"a"
+       .balign         256*4
+       .globl          __break_usertrap_fixup_table
+__break_usertrap_fixup_table:
+
+       # trap fixup table for single-stepping in user mode
+       .section        .trap.fixup.kernel,"a"
+       .balign         256*4
+       .globl          __break_kerneltrap_fixup_table
+__break_kerneltrap_fixup_table:
+
+       # handler declaration for a sofware or program interrupt
+.macro VECTOR_SOFTPROG tbr_tt, vec
+       .section .trap.user
+       .org            \tbr_tt
+       bra             __entry_uspace_softprog_interrupt
+       .section .trap.fixup.user
+       .org            \tbr_tt >> 2
+       .long           __break_step_uspace_softprog_interrupt
+       .section .trap.kernel
+       .org            \tbr_tt
+       bra             __entry_kernel_softprog_interrupt
+       .section .trap.fixup.kernel
+       .org            \tbr_tt >> 2
+       .long           __break_step_kernel_softprog_interrupt
+       .section .trap.vector
+       .org            \tbr_tt >> 2
+       .long           \vec
+.endm
+
+       # handler declaration for a maskable external interrupt
+.macro VECTOR_IRQ tbr_tt, vec
+       .section .trap.user
+       .org            \tbr_tt
+       bra             __entry_uspace_external_interrupt
+       .section .trap.fixup.user
+       .org            \tbr_tt >> 2
+       .long           __break_step_uspace_external_interrupt
+       .section .trap.kernel
+       .org            \tbr_tt
+       bra             __entry_kernel_external_interrupt
+       .section .trap.fixup.kernel
+       .org            \tbr_tt >> 2
+       .long           __break_step_kernel_external_interrupt
+       .section .trap.vector
+       .org            \tbr_tt >> 2
+       .long           \vec
+.endm
+
+       # handler declaration for an NMI external interrupt
+.macro VECTOR_NMI tbr_tt, vec
+       .section .trap.user
+       .org            \tbr_tt
+       break
+       break
+       break
+       break
+       .section .trap.kernel
+       .org            \tbr_tt
+       break
+       break
+       break
+       break
+       .section .trap.vector
+       .org            \tbr_tt >> 2
+       .long           \vec
+.endm
+
+       # handler declaration for an MMU only sofware or program interrupt
+.macro VECTOR_SP_MMU tbr_tt, vec
+#ifdef CONFIG_MMU
+       VECTOR_SOFTPROG \tbr_tt, \vec
+#else
+       VECTOR_NMI      \tbr_tt, 0
+#endif
+.endm
+
+
+###############################################################################
+#
+# specification of the vectors
+# - note: each macro inserts code into multiple sections
+#
+###############################################################################
+       VECTOR_SP_MMU   TBR_TT_INSTR_MMU_MISS,  __entry_insn_mmu_miss
+       VECTOR_SOFTPROG TBR_TT_INSTR_ACC_ERROR, __entry_insn_access_error
+       VECTOR_SOFTPROG TBR_TT_INSTR_ACC_EXCEP, __entry_insn_access_exception
+       VECTOR_SOFTPROG TBR_TT_PRIV_INSTR,      __entry_privileged_instruction
+       VECTOR_SOFTPROG TBR_TT_ILLEGAL_INSTR,   __entry_illegal_instruction
+       VECTOR_SOFTPROG TBR_TT_FP_EXCEPTION,    __entry_media_exception
+       VECTOR_SOFTPROG TBR_TT_MP_EXCEPTION,    __entry_media_exception
+       VECTOR_SOFTPROG TBR_TT_DATA_ACC_ERROR,  __entry_data_access_error
+       VECTOR_SP_MMU   TBR_TT_DATA_MMU_MISS,   __entry_data_mmu_miss
+       VECTOR_SOFTPROG TBR_TT_DATA_ACC_EXCEP,  __entry_data_access_exception
+       VECTOR_SOFTPROG TBR_TT_DATA_STR_ERROR,  __entry_data_store_error
+       VECTOR_SOFTPROG TBR_TT_DIVISION_EXCEP,  __entry_division_exception
+
+#ifdef CONFIG_MMU
+       .section .trap.user
+       .org            TBR_TT_INSTR_TLB_MISS
+       .globl          __trap_user_insn_tlb_miss
+__trap_user_insn_tlb_miss:
+       movsg           ear0,gr28                       /* faulting address */
+       movsg           scr0,gr31                       /* get mapped PTD coverage start address */
+       xor.p           gr28,gr31,gr31                  /* compare addresses */
+       bra             __entry_user_insn_tlb_miss
+
+       .org            TBR_TT_DATA_TLB_MISS
+       .globl          __trap_user_data_tlb_miss
+__trap_user_data_tlb_miss:
+       movsg           ear0,gr28                       /* faulting address */
+       movsg           scr1,gr31                       /* get mapped PTD coverage start address */
+       xor.p           gr28,gr31,gr31                  /* compare addresses */
+       bra             __entry_user_data_tlb_miss
+
+       .section .trap.kernel
+       .org            TBR_TT_INSTR_TLB_MISS
+       .globl          __trap_kernel_insn_tlb_miss
+__trap_kernel_insn_tlb_miss:
+       movsg           ear0,gr29                       /* faulting address */
+       movsg           scr0,gr31                       /* get mapped PTD coverage start address */
+       xor.p           gr29,gr31,gr31                  /* compare addresses */
+       bra             __entry_kernel_insn_tlb_miss
+
+       .org            TBR_TT_DATA_TLB_MISS
+       .globl          __trap_kernel_data_tlb_miss
+__trap_kernel_data_tlb_miss:
+       movsg           ear0,gr29                       /* faulting address */
+       movsg           scr1,gr31                       /* get mapped PTD coverage start address */
+       xor.p           gr29,gr31,gr31                  /* compare addresses */
+       bra             __entry_kernel_data_tlb_miss
+
+       .section .trap.fixup.user
+       .org            TBR_TT_INSTR_TLB_MISS >> 2
+       .globl          __trap_fixup_user_insn_tlb_miss
+__trap_fixup_user_insn_tlb_miss:
+       .long           __break_user_insn_tlb_miss
+       .org            TBR_TT_DATA_TLB_MISS >> 2
+       .globl          __trap_fixup_user_data_tlb_miss
+__trap_fixup_user_data_tlb_miss:
+       .long           __break_user_data_tlb_miss
+
+       .section .trap.fixup.kernel
+       .org            TBR_TT_INSTR_TLB_MISS >> 2
+       .globl          __trap_fixup_kernel_insn_tlb_miss
+__trap_fixup_kernel_insn_tlb_miss:
+       .long           __break_kernel_insn_tlb_miss
+       .org            TBR_TT_DATA_TLB_MISS >> 2
+       .globl          __trap_fixup_kernel_data_tlb_miss
+__trap_fixup_kernel_data_tlb_miss:
+       .long           __break_kernel_data_tlb_miss
+
+       .section .trap.vector
+       .org            TBR_TT_INSTR_TLB_MISS >> 2
+       .long           __entry_insn_mmu_fault
+       .org            TBR_TT_DATA_TLB_MISS >> 2
+       .long           __entry_data_mmu_fault
+#endif
+
+       VECTOR_SP_MMU   TBR_TT_DATA_DAT_EXCEP,  __entry_data_dat_fault
+       VECTOR_NMI      TBR_TT_DECREMENT_TIMER, __entry_do_NMI
+       VECTOR_SOFTPROG TBR_TT_COMPOUND_EXCEP,  __entry_compound_exception
+       VECTOR_IRQ      TBR_TT_INTERRUPT_1,     __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_2,     __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_3,     __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_4,     __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_5,     __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_6,     __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_7,     __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_8,     __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_9,     __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_10,    __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_11,    __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_12,    __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_13,    __entry_do_IRQ
+       VECTOR_IRQ      TBR_TT_INTERRUPT_14,    __entry_do_IRQ
+       VECTOR_NMI      TBR_TT_INTERRUPT_15,    __entry_do_NMI
+
+       # miscellaneous user mode entry points
+       .section        .trap.user
+       .org            TBR_TT_TRAP0
+       .rept           127
+       bra             __entry_uspace_softprog_interrupt
+       bra             __break_step_uspace_softprog_interrupt
+       .long           0,0
+       .endr
+       .org            TBR_TT_BREAK
+       bra             __entry_break
+       .long           0,0,0
+
+       # miscellaneous kernel mode entry points
+       .section        .trap.kernel
+       .org            TBR_TT_TRAP0
+       .rept           127
+       bra             __entry_kernel_softprog_interrupt
+       bra             __break_step_kernel_softprog_interrupt
+       .long           0,0
+       .endr
+       .org            TBR_TT_BREAK
+       bra             __entry_break
+       .long           0,0,0
+
+       # miscellaneous debug mode entry points
+       .section        .trap.break
+       .org            TBR_TT_BREAK
+       movsg           bpcsr,gr30
+       jmpl            @(gr30,gr0)
+
+       # miscellaneous vectors
+       .section        .trap.vector
+       .org            TBR_TT_TRAP0 >> 2
+       .long           system_call
+       .rept           126
+       .long           __entry_unsupported_trap
+       .endr
+       .org            TBR_TT_BREAK >> 2
+       .long           __entry_debug_exception
diff --git a/arch/frv/kernel/entry.S b/arch/frv/kernel/entry.S
new file mode 100644 (file)
index 0000000..ad10ea5
--- /dev/null
@@ -0,0 +1,1428 @@
+/* entry.S: FR-V entry
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * Entry to the kernel is "interesting":
+ *  (1) There are no stack pointers, not even for the kernel
+ *  (2) General Registers should not be clobbered
+ *  (3) There are no kernel-only data registers
+ *  (4) Since all addressing modes are wrt to a General Register, no global
+ *      variables can be reached
+ *
+ * We deal with this by declaring that we shall kill GR28 on entering the
+ * kernel from userspace
+ *
+ * However, since break interrupts can interrupt the CPU even when PSR.ET==0,
+ * they can't rely on GR28 to be anything useful, and so need to clobber a
+ * separate register (GR31). Break interrupts are managed in break.S
+ *
+ * GR29 _is_ saved, and holds the current task pointer globally
+ *
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/thread_info.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/ptrace.h>
+#include <asm/errno.h>
+#include <asm/cache.h>
+#include <asm/spr-regs.h>
+
+#define nr_syscalls ((syscall_table_size)/4)
+
+       .text
+       .balign         4
+
+.macro LEDS val
+#      sethi.p         %hi(0xe1200004),gr30
+#      setlo           %lo(0xe1200004),gr30
+#      setlos          #~\val,gr31
+#      st              gr31,@(gr30,gr0)
+#      sethi.p         %hi(0xffc00100),gr30
+#      setlo           %lo(0xffc00100),gr30
+#      sth             gr0,@(gr30,gr0)
+#      membar
+.endm
+
+.macro LEDS32
+#      not             gr31,gr31
+#      sethi.p         %hi(0xe1200004),gr30
+#      setlo           %lo(0xe1200004),gr30
+#      st.p            gr31,@(gr30,gr0)
+#      srli            gr31,#16,gr31
+#      sethi.p         %hi(0xffc00100),gr30
+#      setlo           %lo(0xffc00100),gr30
+#      sth             gr31,@(gr30,gr0)
+#      membar
+.endm
+
+###############################################################################
+#
+# entry point for External interrupts received whilst executing userspace code
+#
+###############################################################################
+       .globl          __entry_uspace_external_interrupt
+        .type          __entry_uspace_external_interrupt,@function
+__entry_uspace_external_interrupt:
+       LEDS            0x6200
+       sethi.p         %hi(__kernel_frame0_ptr),gr28
+       setlo           %lo(__kernel_frame0_ptr),gr28
+       ldi             @(gr28,#0),gr28
+
+       # handle h/w single-step through exceptions
+       sti             gr0,@(gr28,#REG__STATUS)
+
+       .globl          __entry_uspace_external_interrupt_reentry
+__entry_uspace_external_interrupt_reentry:
+       LEDS            0x6201
+
+       setlos          #REG__END,gr30
+       dcpl            gr28,gr30,#0
+
+       # finish building the exception frame
+       sti             sp,  @(gr28,#REG_SP)
+       stdi            gr2, @(gr28,#REG_GR(2))
+       stdi            gr4, @(gr28,#REG_GR(4))
+       stdi            gr6, @(gr28,#REG_GR(6))
+       stdi            gr8, @(gr28,#REG_GR(8))
+       stdi            gr10,@(gr28,#REG_GR(10))
+       stdi            gr12,@(gr28,#REG_GR(12))
+       stdi            gr14,@(gr28,#REG_GR(14))
+       stdi            gr16,@(gr28,#REG_GR(16))
+       stdi            gr18,@(gr28,#REG_GR(18))
+       stdi            gr20,@(gr28,#REG_GR(20))
+       stdi            gr22,@(gr28,#REG_GR(22))
+       stdi            gr24,@(gr28,#REG_GR(24))
+       stdi            gr26,@(gr28,#REG_GR(26))
+       sti             gr0, @(gr28,#REG_GR(28))
+       sti             gr29,@(gr28,#REG_GR(29))
+       stdi.p          gr30,@(gr28,#REG_GR(30))
+
+       # set up the kernel stack pointer
+       ori             gr28,0,sp
+
+       movsg           tbr ,gr20
+       movsg           psr ,gr22
+       movsg           pcsr,gr21
+       movsg           isr ,gr23
+       movsg           ccr ,gr24
+       movsg           cccr,gr25
+       movsg           lr  ,gr26
+       movsg           lcr ,gr27
+
+       setlos.p        #-1,gr4
+       andi            gr22,#PSR_PS,gr5                /* try to rebuild original PSR value */
+       andi.p          gr22,#~(PSR_PS|PSR_S),gr6
+       slli            gr5,#1,gr5
+       or              gr6,gr5,gr5
+       andi            gr5,#~PSR_ET,gr5
+
+       sti             gr20,@(gr28,#REG_TBR)
+       sti             gr21,@(gr28,#REG_PC)
+       sti             gr5 ,@(gr28,#REG_PSR)
+       sti             gr23,@(gr28,#REG_ISR)
+       stdi            gr24,@(gr28,#REG_CCR)
+       stdi            gr26,@(gr28,#REG_LR)
+       sti             gr4 ,@(gr28,#REG_SYSCALLNO)
+
+       movsg           iacc0h,gr4
+       movsg           iacc0l,gr5
+       stdi            gr4,@(gr28,#REG_IACC0)
+
+       movsg           gner0,gr4
+       movsg           gner1,gr5
+       stdi            gr4,@(gr28,#REG_GNER0)
+
+       # set up kernel global registers
+       sethi.p         %hi(__kernel_current_task),gr5
+       setlo           %lo(__kernel_current_task),gr5
+       sethi.p         %hi(_gp),gr16
+       setlo           %lo(_gp),gr16
+       ldi             @(gr5,#0),gr29
+       ldi.p           @(gr29,#4),gr15         ; __current_thread_info = current->thread_info
+
+       # make sure we (the kernel) get div-zero and misalignment exceptions
+       setlos          #ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5
+       movgs           gr5,isr
+
+       # switch to the kernel trap table
+       sethi.p         %hi(__entry_kerneltrap_table),gr6
+       setlo           %lo(__entry_kerneltrap_table),gr6
+       movgs           gr6,tbr
+
+       # set the return address
+       sethi.p         %hi(__entry_return_from_user_interrupt),gr4
+       setlo           %lo(__entry_return_from_user_interrupt),gr4
+       movgs           gr4,lr
+
+       # raise the minimum interrupt priority to 15 (NMI only) and enable exceptions
+       movsg           psr,gr4
+
+       ori             gr4,#PSR_PIL_14,gr4
+       movgs           gr4,psr
+       ori             gr4,#PSR_PIL_14|PSR_ET,gr4
+       movgs           gr4,psr
+
+       LEDS            0x6202
+       bra             do_IRQ
+
+       .size           __entry_uspace_external_interrupt,.-__entry_uspace_external_interrupt
+
+###############################################################################
+#
+# entry point for External interrupts received whilst executing kernel code
+# - on arriving here, the following registers should already be set up:
+#      GR15    - current thread_info struct pointer
+#      GR16    - kernel GP-REL pointer
+#      GR29    - current task struct pointer
+#      TBR     - kernel trap vector table
+#      ISR     - kernel's preferred integer controls
+#
+###############################################################################
+       .globl          __entry_kernel_external_interrupt
+        .type          __entry_kernel_external_interrupt,@function
+__entry_kernel_external_interrupt:
+       LEDS            0x6210
+
+       sub             sp,gr15,gr31
+       LEDS32
+
+       # set up the stack pointer
+       or.p            sp,gr0,gr30
+       subi            sp,#REG__END,sp
+       sti             gr30,@(sp,#REG_SP)
+
+       # handle h/w single-step through exceptions
+       sti             gr0,@(sp,#REG__STATUS)
+
+       .globl          __entry_kernel_external_interrupt_reentry
+__entry_kernel_external_interrupt_reentry:
+       LEDS            0x6211
+
+       # set up the exception frame
+       setlos          #REG__END,gr30
+       dcpl            sp,gr30,#0
+
+       sti.p           gr28,@(sp,#REG_GR(28))
+       ori             sp,0,gr28
+
+       # finish building the exception frame
+       stdi            gr2,@(gr28,#REG_GR(2))
+       stdi            gr4,@(gr28,#REG_GR(4))
+       stdi            gr6,@(gr28,#REG_GR(6))
+       stdi            gr8,@(gr28,#REG_GR(8))
+       stdi            gr10,@(gr28,#REG_GR(10))
+       stdi            gr12,@(gr28,#REG_GR(12))
+       stdi            gr14,@(gr28,#REG_GR(14))
+       stdi            gr16,@(gr28,#REG_GR(16))
+       stdi            gr18,@(gr28,#REG_GR(18))
+       stdi            gr20,@(gr28,#REG_GR(20))
+       stdi            gr22,@(gr28,#REG_GR(22))
+       stdi            gr24,@(gr28,#REG_GR(24))
+       stdi            gr26,@(gr28,#REG_GR(26))
+       sti             gr29,@(gr28,#REG_GR(29))
+       stdi            gr30,@(gr28,#REG_GR(30))
+
+       movsg           tbr ,gr20
+       movsg           psr ,gr22
+       movsg           pcsr,gr21
+       movsg           isr ,gr23
+       movsg           ccr ,gr24
+       movsg           cccr,gr25
+       movsg           lr  ,gr26
+       movsg           lcr ,gr27
+
+       setlos.p        #-1,gr4
+       andi            gr22,#PSR_PS,gr5                /* try to rebuild original PSR value */
+       andi.p          gr22,#~(PSR_PS|PSR_S),gr6
+       slli            gr5,#1,gr5
+       or              gr6,gr5,gr5
+       andi.p          gr5,#~PSR_ET,gr5
+
+       # set CCCR.CC3 to Undefined to abort atomic-modify completion inside the kernel
+       # - for an explanation of how it works, see: Documentation/fujitsu/frv/atomic-ops.txt
+       andi            gr25,#~0xc0,gr25
+
+       sti             gr20,@(gr28,#REG_TBR)
+       sti             gr21,@(gr28,#REG_PC)
+       sti             gr5 ,@(gr28,#REG_PSR)
+       sti             gr23,@(gr28,#REG_ISR)
+       stdi            gr24,@(gr28,#REG_CCR)
+       stdi            gr26,@(gr28,#REG_LR)
+       sti             gr4 ,@(gr28,#REG_SYSCALLNO)
+
+       movsg           iacc0h,gr4
+       movsg           iacc0l,gr5
+       stdi            gr4,@(gr28,#REG_IACC0)
+
+       movsg           gner0,gr4
+       movsg           gner1,gr5
+       stdi            gr4,@(gr28,#REG_GNER0)
+
+       # set the return address
+       sethi.p         %hi(__entry_return_from_kernel_interrupt),gr4
+       setlo           %lo(__entry_return_from_kernel_interrupt),gr4
+       movgs           gr4,lr
+
+       # clear power-saving mode flags
+       movsg           hsr0,gr4
+       andi            gr4,#~HSR0_PDM,gr4
+       movgs           gr4,hsr0
+
+       # raise the minimum interrupt priority to 15 (NMI only) and enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_PIL_14,gr4
+       movgs           gr4,psr
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+
+       LEDS            0x6212
+       bra             do_IRQ
+
+       .size           __entry_kernel_external_interrupt,.-__entry_kernel_external_interrupt
+
+
+###############################################################################
+#
+# entry point for Software and Progam interrupts generated whilst executing userspace code
+#
+###############################################################################
+       .globl          __entry_uspace_softprog_interrupt
+        .type          __entry_uspace_softprog_interrupt,@function
+       .globl          __entry_uspace_handle_mmu_fault
+__entry_uspace_softprog_interrupt:
+       LEDS            0x6000
+#ifdef CONFIG_MMU
+       movsg           ear0,gr28
+__entry_uspace_handle_mmu_fault:
+       movgs           gr28,scr2
+#endif
+       sethi.p         %hi(__kernel_frame0_ptr),gr28
+       setlo           %lo(__kernel_frame0_ptr),gr28
+       ldi             @(gr28,#0),gr28
+
+       # handle h/w single-step through exceptions
+       sti             gr0,@(gr28,#REG__STATUS)
+
+       .globl          __entry_uspace_softprog_interrupt_reentry
+__entry_uspace_softprog_interrupt_reentry:
+       LEDS            0x6001
+
+       setlos          #REG__END,gr30
+       dcpl            gr28,gr30,#0
+
+       # set up the kernel stack pointer
+       sti.p           sp,@(gr28,#REG_SP)
+       ori             gr28,0,sp
+       sti             gr0,@(gr28,#REG_GR(28))
+
+       stdi            gr20,@(gr28,#REG_GR(20))
+       stdi            gr22,@(gr28,#REG_GR(22))
+
+       movsg           tbr,gr20
+       movsg           pcsr,gr21
+       movsg           psr,gr22
+
+       sethi.p         %hi(__entry_return_from_user_exception),gr23
+       setlo           %lo(__entry_return_from_user_exception),gr23
+       bra             __entry_common
+
+       .size           __entry_uspace_softprog_interrupt,.-__entry_uspace_softprog_interrupt
+
+       # single-stepping was disabled on entry to a TLB handler that then faulted
+#ifdef CONFIG_MMU
+       .globl          __entry_uspace_handle_mmu_fault_sstep
+__entry_uspace_handle_mmu_fault_sstep:
+       movgs           gr28,scr2
+       sethi.p         %hi(__kernel_frame0_ptr),gr28
+       setlo           %lo(__kernel_frame0_ptr),gr28
+       ldi             @(gr28,#0),gr28
+
+       # flag single-step re-enablement
+       sti             gr0,@(gr28,#REG__STATUS)
+       bra             __entry_uspace_softprog_interrupt_reentry
+#endif
+
+
+###############################################################################
+#
+# entry point for Software and Progam interrupts generated whilst executing kernel code
+#
+###############################################################################
+       .globl          __entry_kernel_softprog_interrupt
+        .type          __entry_kernel_softprog_interrupt,@function
+__entry_kernel_softprog_interrupt:
+       LEDS            0x6004
+
+#ifdef CONFIG_MMU
+       movsg           ear0,gr30
+       movgs           gr30,scr2
+#endif
+
+       .globl          __entry_kernel_handle_mmu_fault
+__entry_kernel_handle_mmu_fault:
+       # set up the stack pointer
+       subi            sp,#REG__END,sp
+       sti             sp,@(sp,#REG_SP)
+       sti             sp,@(sp,#REG_SP-4)
+       andi            sp,#~7,sp
+
+       # handle h/w single-step through exceptions
+       sti             gr0,@(sp,#REG__STATUS)
+
+       .globl          __entry_kernel_softprog_interrupt_reentry
+__entry_kernel_softprog_interrupt_reentry:
+       LEDS            0x6005
+
+       setlos          #REG__END,gr30
+       dcpl            sp,gr30,#0
+
+       # set up the exception frame
+       sti.p           gr28,@(sp,#REG_GR(28))
+       ori             sp,0,gr28
+
+       stdi            gr20,@(gr28,#REG_GR(20))
+       stdi            gr22,@(gr28,#REG_GR(22))
+
+       ldi             @(sp,#REG_SP),gr22              /* reconstruct the old SP */
+       addi            gr22,#REG__END,gr22
+       sti             gr22,@(sp,#REG_SP)
+
+       # set CCCR.CC3 to Undefined to abort atomic-modify completion inside the kernel
+       # - for an explanation of how it works, see: Documentation/fujitsu/frv/atomic-ops.txt
+       movsg           cccr,gr20
+       andi            gr20,#~0xc0,gr20
+       movgs           gr20,cccr
+
+       movsg           tbr,gr20
+       movsg           pcsr,gr21
+       movsg           psr,gr22
+
+       sethi.p         %hi(__entry_return_from_kernel_exception),gr23
+       setlo           %lo(__entry_return_from_kernel_exception),gr23
+       bra             __entry_common
+
+       .size           __entry_kernel_softprog_interrupt,.-__entry_kernel_softprog_interrupt
+
+       # single-stepping was disabled on entry to a TLB handler that then faulted
+#ifdef CONFIG_MMU
+       .globl          __entry_kernel_handle_mmu_fault_sstep
+__entry_kernel_handle_mmu_fault_sstep:
+       # set up the stack pointer
+       subi            sp,#REG__END,sp
+       sti             sp,@(sp,#REG_SP)
+       sti             sp,@(sp,#REG_SP-4)
+       andi            sp,#~7,sp
+
+       # flag single-step re-enablement
+       sethi           #REG__STATUS_STEP,gr30
+       sti             gr30,@(sp,#REG__STATUS)
+       bra             __entry_kernel_softprog_interrupt_reentry
+#endif
+
+
+###############################################################################
+#
+# the rest of the kernel entry point code
+# - on arriving here, the following registers should be set up:
+#      GR1     - kernel stack pointer
+#      GR7     - syscall number (trap 0 only)
+#      GR8-13  - syscall args (trap 0 only)
+#      GR20    - saved TBR
+#      GR21    - saved PC
+#      GR22    - saved PSR
+#      GR23    - return handler address
+#      GR28    - exception frame on stack
+#      SCR2    - saved EAR0 where applicable (clobbered by ICI & ICEF insns on FR451)
+#      PSR     - PSR.S 1, PSR.ET 0
+#
+###############################################################################
+       .globl          __entry_common
+        .type          __entry_common,@function
+__entry_common:
+       LEDS            0x6008
+
+       # finish building the exception frame
+       stdi            gr2,@(gr28,#REG_GR(2))
+       stdi            gr4,@(gr28,#REG_GR(4))
+       stdi            gr6,@(gr28,#REG_GR(6))
+       stdi            gr8,@(gr28,#REG_GR(8))
+       stdi            gr10,@(gr28,#REG_GR(10))
+       stdi            gr12,@(gr28,#REG_GR(12))
+       stdi            gr14,@(gr28,#REG_GR(14))
+       stdi            gr16,@(gr28,#REG_GR(16))
+       stdi            gr18,@(gr28,#REG_GR(18))
+       stdi            gr24,@(gr28,#REG_GR(24))
+       stdi            gr26,@(gr28,#REG_GR(26))
+       sti             gr29,@(gr28,#REG_GR(29))
+       stdi            gr30,@(gr28,#REG_GR(30))
+
+       movsg           lcr ,gr27
+       movsg           lr  ,gr26
+       movgs           gr23,lr
+       movsg           cccr,gr25
+       movsg           ccr ,gr24
+       movsg           isr ,gr23
+
+       setlos.p        #-1,gr4
+       andi            gr22,#PSR_PS,gr5                /* try to rebuild original PSR value */
+       andi.p          gr22,#~(PSR_PS|PSR_S),gr6
+       slli            gr5,#1,gr5
+       or              gr6,gr5,gr5
+       andi            gr5,#~PSR_ET,gr5
+
+       sti             gr20,@(gr28,#REG_TBR)
+       sti             gr21,@(gr28,#REG_PC)
+       sti             gr5 ,@(gr28,#REG_PSR)
+       sti             gr23,@(gr28,#REG_ISR)
+       stdi            gr24,@(gr28,#REG_CCR)
+       stdi            gr26,@(gr28,#REG_LR)
+       sti             gr4 ,@(gr28,#REG_SYSCALLNO)
+
+       movsg           iacc0h,gr4
+       movsg           iacc0l,gr5
+       stdi            gr4,@(gr28,#REG_IACC0)
+
+       movsg           gner0,gr4
+       movsg           gner1,gr5
+       stdi            gr4,@(gr28,#REG_GNER0)
+
+       # set up kernel global registers
+       sethi.p         %hi(__kernel_current_task),gr5
+       setlo           %lo(__kernel_current_task),gr5
+       sethi.p         %hi(_gp),gr16
+       setlo           %lo(_gp),gr16
+       ldi             @(gr5,#0),gr29
+       ldi             @(gr29,#4),gr15         ; __current_thread_info = current->thread_info
+
+       # switch to the kernel trap table
+       sethi.p         %hi(__entry_kerneltrap_table),gr6
+       setlo           %lo(__entry_kerneltrap_table),gr6
+       movgs           gr6,tbr
+
+       # make sure we (the kernel) get div-zero and misalignment exceptions
+       setlos          #ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5
+       movgs           gr5,isr
+
+       # clear power-saving mode flags
+       movsg           hsr0,gr4
+       andi            gr4,#~HSR0_PDM,gr4
+       movgs           gr4,hsr0
+
+       # multiplex again using old TBR as a guide
+       setlos.p        #TBR_TT,gr3
+       sethi           %hi(__entry_vector_table),gr6
+       and.p           gr20,gr3,gr5
+       setlo           %lo(__entry_vector_table),gr6
+       srli            gr5,#2,gr5
+       ld              @(gr5,gr6),gr5
+
+       LEDS            0x6009
+       jmpl            @(gr5,gr0)
+
+
+       .size           __entry_common,.-__entry_common
+
+###############################################################################
+#
+# handle instruction MMU fault
+#
+###############################################################################
+#ifdef CONFIG_MMU
+       .globl          __entry_insn_mmu_fault
+__entry_insn_mmu_fault:
+       LEDS            0x6010
+       setlos          #0,gr8
+       movsg           esr0,gr9
+       movsg           scr2,gr10
+
+       # now that we've accessed the exception regs, we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+
+       sethi.p         %hi(do_page_fault),gr5
+       setlo           %lo(do_page_fault),gr5
+       jmpl            @(gr5,gr0)      ; call do_page_fault(0,esr0,ear0)
+#endif
+
+
+###############################################################################
+#
+# handle instruction access error
+#
+###############################################################################
+       .globl          __entry_insn_access_error
+__entry_insn_access_error:
+       LEDS            0x6011
+       sethi.p         %hi(insn_access_error),gr5
+       setlo           %lo(insn_access_error),gr5
+       movsg           esfr1,gr8
+       movsg           epcr0,gr9
+       movsg           esr0,gr10
+
+       # now that we've accessed the exception regs, we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+       jmpl            @(gr5,gr0)      ; call insn_access_error(esfr1,epcr0,esr0)
+
+###############################################################################
+#
+# handle various instructions of dubious legality
+#
+###############################################################################
+       .globl          __entry_unsupported_trap
+       .globl          __entry_illegal_instruction
+       .globl          __entry_privileged_instruction
+       .globl          __entry_debug_exception
+__entry_unsupported_trap:
+       subi            gr21,#4,gr21
+       sti             gr21,@(gr28,#REG_PC)
+__entry_illegal_instruction:
+__entry_privileged_instruction:
+__entry_debug_exception:
+       LEDS            0x6012
+       sethi.p         %hi(illegal_instruction),gr5
+       setlo           %lo(illegal_instruction),gr5
+       movsg           esfr1,gr8
+       movsg           epcr0,gr9
+       movsg           esr0,gr10
+
+       # now that we've accessed the exception regs, we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+       jmpl            @(gr5,gr0)      ; call ill_insn(esfr1,epcr0,esr0)
+
+###############################################################################
+#
+# handle media exception
+#
+###############################################################################
+       .globl          __entry_media_exception
+__entry_media_exception:
+       LEDS            0x6013
+       sethi.p         %hi(media_exception),gr5
+       setlo           %lo(media_exception),gr5
+       movsg           msr0,gr8
+       movsg           msr1,gr9
+
+       # now that we've accessed the exception regs, we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+       jmpl            @(gr5,gr0)      ; call media_excep(msr0,msr1)
+
+###############################################################################
+#
+# handle data MMU fault
+# handle data DAT fault (write-protect exception)
+#
+###############################################################################
+#ifdef CONFIG_MMU
+       .globl          __entry_data_mmu_fault
+__entry_data_mmu_fault:
+       .globl          __entry_data_dat_fault
+__entry_data_dat_fault:
+       LEDS            0x6014
+       setlos          #1,gr8
+       movsg           esr0,gr9
+       movsg           scr2,gr10       ; saved EAR0
+
+       # now that we've accessed the exception regs, we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+
+       sethi.p         %hi(do_page_fault),gr5
+       setlo           %lo(do_page_fault),gr5
+       jmpl            @(gr5,gr0)      ; call do_page_fault(1,esr0,ear0)
+#endif
+
+###############################################################################
+#
+# handle data and instruction access exceptions
+#
+###############################################################################
+       .globl          __entry_insn_access_exception
+       .globl          __entry_data_access_exception
+__entry_insn_access_exception:
+__entry_data_access_exception:
+       LEDS            0x6016
+       sethi.p         %hi(memory_access_exception),gr5
+       setlo           %lo(memory_access_exception),gr5
+       movsg           esr0,gr8
+       movsg           scr2,gr9        ; saved EAR0
+       movsg           epcr0,gr10
+
+       # now that we've accessed the exception regs, we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+       jmpl            @(gr5,gr0)      ; call memory_access_error(esr0,ear0,epcr0)
+
+###############################################################################
+#
+# handle data access error
+#
+###############################################################################
+       .globl          __entry_data_access_error
+__entry_data_access_error:
+       LEDS            0x6016
+       sethi.p         %hi(data_access_error),gr5
+       setlo           %lo(data_access_error),gr5
+       movsg           esfr1,gr8
+       movsg           esr15,gr9
+       movsg           ear15,gr10
+
+       # now that we've accessed the exception regs, we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+       jmpl            @(gr5,gr0)      ; call data_access_error(esfr1,esr15,ear15)
+
+###############################################################################
+#
+# handle data store error
+#
+###############################################################################
+       .globl          __entry_data_store_error
+__entry_data_store_error:
+       LEDS            0x6017
+       sethi.p         %hi(data_store_error),gr5
+       setlo           %lo(data_store_error),gr5
+       movsg           esfr1,gr8
+       movsg           esr14,gr9
+
+       # now that we've accessed the exception regs, we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+       jmpl            @(gr5,gr0)      ; call data_store_error(esfr1,esr14)
+
+###############################################################################
+#
+# handle division exception
+#
+###############################################################################
+       .globl          __entry_division_exception
+__entry_division_exception:
+       LEDS            0x6018
+       sethi.p         %hi(division_exception),gr5
+       setlo           %lo(division_exception),gr5
+       movsg           esfr1,gr8
+       movsg           esr0,gr9
+       movsg           isr,gr10
+
+       # now that we've accessed the exception regs, we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+       jmpl            @(gr5,gr0)      ; call div_excep(esfr1,esr0,isr)
+
+###############################################################################
+#
+# handle compound exception
+#
+###############################################################################
+       .globl          __entry_compound_exception
+__entry_compound_exception:
+       LEDS            0x6019
+       sethi.p         %hi(compound_exception),gr5
+       setlo           %lo(compound_exception),gr5
+       movsg           esfr1,gr8
+       movsg           esr0,gr9
+       movsg           esr14,gr10
+       movsg           esr15,gr11
+       movsg           msr0,gr12
+       movsg           msr1,gr13
+
+       # now that we've accessed the exception regs, we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+       jmpl            @(gr5,gr0)      ; call comp_excep(esfr1,esr0,esr14,esr15,msr0,msr1)
+
+###############################################################################
+#
+# handle interrupts and NMIs
+#
+###############################################################################
+       .globl          __entry_do_IRQ
+__entry_do_IRQ:
+       LEDS            0x6020
+
+       # we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+       bra             do_IRQ
+
+       .globl          __entry_do_NMI
+__entry_do_NMI:
+       LEDS            0x6021
+
+       # we can enable exceptions
+       movsg           psr,gr4
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+       bra             do_NMI
+
+###############################################################################
+#
+# the return path for a newly forked child process
+# - __switch_to() saved the old current pointer in GR8 for us
+#
+###############################################################################
+       .globl          ret_from_fork
+ret_from_fork:
+       LEDS            0x6100
+       call            schedule_tail
+
+       # fork & co. return 0 to child
+       setlos.p        #0,gr8
+       bra             __syscall_exit
+
+###################################################################################################
+#
+# Return to user mode is not as complex as all this looks,
+# but we want the default path for a system call return to
+# go as quickly as possible which is why some of this is
+# less clear than it otherwise should be.
+#
+###################################################################################################
+       .balign         L1_CACHE_BYTES
+       .globl          system_call
+system_call:
+       LEDS            0x6101
+       movsg           psr,gr4                 ; enable exceptions
+       ori             gr4,#PSR_ET,gr4
+       movgs           gr4,psr
+
+       sti             gr7,@(gr28,#REG_SYSCALLNO)
+       sti.p           gr8,@(gr28,#REG_ORIG_GR8)
+
+       subicc          gr7,#nr_syscalls,gr0,icc0
+       bnc             icc0,#0,__syscall_badsys
+
+       ldi             @(gr15,#TI_FLAGS),gr4
+       ori             gr4,#_TIF_SYSCALL_TRACE,gr4
+       andicc          gr4,#_TIF_SYSCALL_TRACE,gr0,icc0
+       bne             icc0,#0,__syscall_trace_entry
+
+__syscall_call:
+       slli.p          gr7,#2,gr7
+       sethi           %hi(sys_call_table),gr5
+       setlo           %lo(sys_call_table),gr5
+       ld              @(gr5,gr7),gr4
+       calll           @(gr4,gr0)
+
+
+###############################################################################
+#
+# return to interrupted process
+#
+###############################################################################
+__syscall_exit:
+       LEDS            0x6300
+
+       sti             gr8,@(gr28,#REG_GR(8))  ; save return value
+
+       # rebuild saved psr - execve will change it for init/main.c
+       ldi             @(gr28,#REG_PSR),gr22
+       srli            gr22,#1,gr5
+       andi.p          gr22,#~PSR_PS,gr22
+       andi            gr5,#PSR_PS,gr5
+       or              gr5,gr22,gr22
+       ori             gr22,#PSR_S,gr22
+
+       # keep current PSR in GR23
+       movsg           psr,gr23
+
+       # make sure we don't miss an interrupt setting need_resched or sigpending between
+       # sampling and the RETT
+       ori             gr23,#PSR_PIL_14,gr23
+       movgs           gr23,psr
+
+       ldi             @(gr15,#TI_FLAGS),gr4
+       sethi.p         %hi(_TIF_ALLWORK_MASK),gr5
+       setlo           %lo(_TIF_ALLWORK_MASK),gr5
+       andcc           gr4,gr5,gr0,icc0
+       bne             icc0,#0,__syscall_exit_work
+
+       # restore all registers and return
+__entry_return_direct:
+       LEDS            0x6301
+
+       andi            gr22,#~PSR_ET,gr22
+       movgs           gr22,psr
+
+       ldi             @(gr28,#REG_ISR),gr23
+       lddi            @(gr28,#REG_CCR),gr24
+       lddi            @(gr28,#REG_LR) ,gr26
+       ldi             @(gr28,#REG_PC) ,gr21
+       ldi             @(gr28,#REG_TBR),gr20
+
+       movgs           gr20,tbr
+       movgs           gr21,pcsr
+       movgs           gr23,isr
+       movgs           gr24,ccr
+       movgs           gr25,cccr
+       movgs           gr26,lr
+       movgs           gr27,lcr
+
+       lddi            @(gr28,#REG_GNER0),gr4
+       movgs           gr4,gner0
+       movgs           gr5,gner1
+
+       lddi            @(gr28,#REG_IACC0),gr4
+       movgs           gr4,iacc0h
+       movgs           gr5,iacc0l
+
+       lddi            @(gr28,#REG_GR(4)) ,gr4
+       lddi            @(gr28,#REG_GR(6)) ,gr6
+       lddi            @(gr28,#REG_GR(8)) ,gr8
+       lddi            @(gr28,#REG_GR(10)),gr10
+       lddi            @(gr28,#REG_GR(12)),gr12
+       lddi            @(gr28,#REG_GR(14)),gr14
+       lddi            @(gr28,#REG_GR(16)),gr16
+       lddi            @(gr28,#REG_GR(18)),gr18
+       lddi            @(gr28,#REG_GR(20)),gr20
+       lddi            @(gr28,#REG_GR(22)),gr22
+       lddi            @(gr28,#REG_GR(24)),gr24
+       lddi            @(gr28,#REG_GR(26)),gr26
+       ldi             @(gr28,#REG_GR(29)),gr29
+       lddi            @(gr28,#REG_GR(30)),gr30
+
+       # check to see if a debugging return is required
+       LEDS            0x67f0
+       movsg           ccr,gr2
+       ldi             @(gr28,#REG__STATUS),gr3
+       andicc          gr3,#REG__STATUS_STEP,gr0,icc0
+       bne             icc0,#0,__entry_return_singlestep
+       movgs           gr2,ccr
+
+       ldi             @(gr28,#REG_SP)    ,sp
+       lddi            @(gr28,#REG_GR(2)) ,gr2
+       ldi             @(gr28,#REG_GR(28)),gr28
+
+       LEDS            0x67fe
+//     movsg           pcsr,gr31
+//     LEDS32
+
+#if 0
+       # store the current frame in the workram on the FR451
+       movgs           gr28,scr2
+       sethi.p         %hi(0xfe800000),gr28
+       setlo           %lo(0xfe800000),gr28
+
+       stdi            gr2,@(gr28,#REG_GR(2))
+       stdi            gr4,@(gr28,#REG_GR(4))
+       stdi            gr6,@(gr28,#REG_GR(6))
+       stdi            gr8,@(gr28,#REG_GR(8))
+       stdi            gr10,@(gr28,#REG_GR(10))
+       stdi            gr12,@(gr28,#REG_GR(12))
+       stdi            gr14,@(gr28,#REG_GR(14))
+       stdi            gr16,@(gr28,#REG_GR(16))
+       stdi            gr18,@(gr28,#REG_GR(18))
+       stdi            gr24,@(gr28,#REG_GR(24))
+       stdi            gr26,@(gr28,#REG_GR(26))
+       sti             gr29,@(gr28,#REG_GR(29))
+       stdi            gr30,@(gr28,#REG_GR(30))
+
+       movsg           tbr ,gr30
+       sti             gr30,@(gr28,#REG_TBR)
+       movsg           pcsr,gr30
+       sti             gr30,@(gr28,#REG_PC)
+       movsg           psr ,gr30
+       sti             gr30,@(gr28,#REG_PSR)
+       movsg           isr ,gr30
+       sti             gr30,@(gr28,#REG_ISR)
+       movsg           ccr ,gr30
+       movsg           cccr,gr31
+       stdi            gr30,@(gr28,#REG_CCR)
+       movsg           lr  ,gr30
+       movsg           lcr ,gr31
+       stdi            gr30,@(gr28,#REG_LR)
+       sti             gr0 ,@(gr28,#REG_SYSCALLNO)
+       movsg           scr2,gr28
+#endif
+
+       rett            #0
+
+       # return via break.S
+__entry_return_singlestep:
+       movgs           gr2,ccr
+       lddi            @(gr28,#REG_GR(2)) ,gr2
+       ldi             @(gr28,#REG_SP)    ,sp
+       ldi             @(gr28,#REG_GR(28)),gr28
+       LEDS            0x67ff
+       break
+       .globl          __entry_return_singlestep_breaks_here
+__entry_return_singlestep_breaks_here:
+       nop
+
+
+###############################################################################
+#
+# return to a process interrupted in kernel space
+# - we need to consider preemption if that is enabled
+#
+###############################################################################
+       .balign         L1_CACHE_BYTES
+__entry_return_from_kernel_exception:
+       LEDS            0x6302
+       movsg           psr,gr23
+       ori             gr23,#PSR_PIL_14,gr23
+       movgs           gr23,psr
+       bra             __entry_return_direct
+
+       .balign         L1_CACHE_BYTES
+__entry_return_from_kernel_interrupt:
+       LEDS            0x6303
+       movsg           psr,gr23
+       ori             gr23,#PSR_PIL_14,gr23
+       movgs           gr23,psr
+
+#ifdef CONFIG_PREEMPT
+       ldi             @(gr15,#TI_PRE_COUNT),gr5
+       subicc          gr5,#0,gr0,icc0
+       beq             icc0,#0,__entry_return_direct
+
+__entry_preempt_need_resched:
+       ldi             @(gr15,#TI_FLAGS),gr4
+       andicc          gr4,#_TIF_NEED_RESCHED,gr0,icc0
+       beq             icc0,#1,__entry_return_direct
+
+       setlos          #PREEMPT_ACTIVE,gr5
+       sti             gr5,@(gr15,#TI_FLAGS)
+
+       andi            gr23,#~PSR_PIL,gr23
+       movgs           gr23,psr
+
+       call            schedule
+       sti             gr0,@(gr15,#TI_PRE_COUNT)
+
+       movsg           psr,gr23
+       ori             gr23,#PSR_PIL_14,gr23
+       movgs           gr23,psr
+       bra             __entry_preempt_need_resched
+#else
+       bra             __entry_return_direct
+#endif
+
+
+###############################################################################
+#
+# perform work that needs to be done immediately before resumption
+#
+###############################################################################
+       .globl          __entry_return_from_user_exception
+       .balign         L1_CACHE_BYTES
+__entry_return_from_user_exception:
+       LEDS            0x6501
+
+__entry_resume_userspace:
+       # make sure we don't miss an interrupt setting need_resched or sigpending between
+       # sampling and the RETT
+       movsg           psr,gr23
+       ori             gr23,#PSR_PIL_14,gr23
+       movgs           gr23,psr
+
+__entry_return_from_user_interrupt:
+       LEDS            0x6402
+       ldi             @(gr15,#TI_FLAGS),gr4
+       sethi.p         %hi(_TIF_WORK_MASK),gr5
+       setlo           %lo(_TIF_WORK_MASK),gr5
+       andcc           gr4,gr5,gr0,icc0
+       beq             icc0,#1,__entry_return_direct
+
+__entry_work_pending:
+       LEDS            0x6404
+       andicc          gr4,#_TIF_NEED_RESCHED,gr0,icc0
+       beq             icc0,#1,__entry_work_notifysig
+
+__entry_work_resched:
+       LEDS            0x6408
+       movsg           psr,gr23
+       andi            gr23,#~PSR_PIL,gr23
+       movgs           gr23,psr
+       call            schedule
+       movsg           psr,gr23
+       ori             gr23,#PSR_PIL_14,gr23
+       movgs           gr23,psr
+
+       LEDS            0x6401
+       ldi             @(gr15,#TI_FLAGS),gr4
+       sethi.p         %hi(_TIF_WORK_MASK),gr5
+       setlo           %lo(_TIF_WORK_MASK),gr5
+       andcc           gr4,gr5,gr0,icc0
+       beq             icc0,#1,__entry_return_direct
+       andicc          gr4,#_TIF_NEED_RESCHED,gr0,icc0
+       bne             icc0,#1,__entry_work_resched
+
+__entry_work_notifysig:
+       LEDS            0x6410
+       ori.p           gr4,#0,gr8
+       call            do_notify_resume
+       bra             __entry_return_direct
+
+       # perform syscall entry tracing
+__syscall_trace_entry:
+       LEDS            0x6320
+       setlos.p        #0,gr8
+       call            do_syscall_trace
+
+       ldi             @(gr28,#REG_SYSCALLNO),gr7
+       lddi            @(gr28,#REG_GR(8)) ,gr8
+       lddi            @(gr28,#REG_GR(10)),gr10
+       lddi.p          @(gr28,#REG_GR(12)),gr12
+
+       subicc          gr7,#nr_syscalls,gr0,icc0
+       bnc             icc0,#0,__syscall_badsys
+       bra             __syscall_call
+
+       # perform syscall exit tracing
+__syscall_exit_work:
+       LEDS            0x6340
+       andicc          gr4,#_TIF_SYSCALL_TRACE,gr0,icc0
+       beq             icc0,#1,__entry_work_pending
+
+       movsg           psr,gr23
+       andi            gr23,#~PSR_PIL,gr23     ; could let do_syscall_trace() call schedule()
+       movgs           gr23,psr
+
+       setlos.p        #1,gr8
+       call            do_syscall_trace
+       bra             __entry_resume_userspace
+
+__syscall_badsys:
+       LEDS            0x6380
+       setlos          #-ENOSYS,gr8
+       sti             gr8,@(gr28,#REG_GR(8))  ; save return value
+       bra             __entry_resume_userspace
+
+
+###############################################################################
+#
+# syscall vector table
+#
+###############################################################################
+#ifdef CONFIG_MMU
+#define __MMU(X) X
+#else
+#define __MMU(X) sys_ni_syscall
+#endif
+
+       .section .rodata
+ALIGN
+       .globl          sys_call_table
+sys_call_table:
+       .long sys_restart_syscall       /* 0 - old "setup()" system call, used for restarting */
+       .long sys_exit
+       .long sys_fork
+       .long sys_read
+       .long sys_write
+       .long sys_open          /* 5 */
+       .long sys_close
+       .long sys_waitpid
+       .long sys_creat
+       .long sys_link
+       .long sys_unlink                /* 10 */
+       .long sys_execve
+       .long sys_chdir
+       .long sys_time
+       .long sys_mknod
+       .long sys_chmod         /* 15 */
+       .long sys_lchown16
+       .long sys_ni_syscall                    /* old break syscall holder */
+       .long sys_stat
+       .long sys_lseek
+       .long sys_getpid                /* 20 */
+       .long sys_mount
+       .long sys_oldumount
+       .long sys_setuid16
+       .long sys_getuid16
+       .long sys_ni_syscall // sys_stime               /* 25 */
+       .long sys_ptrace
+       .long sys_alarm
+       .long sys_fstat
+       .long sys_pause
+       .long sys_utime         /* 30 */
+       .long sys_ni_syscall                    /* old stty syscall holder */
+       .long sys_ni_syscall                    /* old gtty syscall holder */
+       .long sys_access
+       .long sys_nice
+       .long sys_ni_syscall    /* 35 */        /* old ftime syscall holder */
+       .long sys_sync
+       .long sys_kill
+       .long sys_rename
+       .long sys_mkdir
+       .long sys_rmdir         /* 40 */
+       .long sys_dup
+       .long sys_pipe
+       .long sys_times
+       .long sys_ni_syscall                    /* old prof syscall holder */
+       .long sys_brk           /* 45 */
+       .long sys_setgid16
+       .long sys_getgid16
+       .long sys_ni_syscall // sys_signal
+       .long sys_geteuid16
+       .long sys_getegid16     /* 50 */
+       .long sys_acct
+       .long sys_umount                                /* recycled never used phys( */
+       .long sys_ni_syscall                    /* old lock syscall holder */
+       .long sys_ioctl
+       .long sys_fcntl         /* 55 */
+       .long sys_ni_syscall                    /* old mpx syscall holder */
+       .long sys_setpgid
+       .long sys_ni_syscall                    /* old ulimit syscall holder */
+       .long sys_ni_syscall                    /* old old uname syscall */
+       .long sys_umask         /* 60 */
+       .long sys_chroot
+       .long sys_ustat
+       .long sys_dup2
+       .long sys_getppid
+       .long sys_getpgrp       /* 65 */
+       .long sys_setsid
+       .long sys_sigaction
+       .long sys_ni_syscall // sys_sgetmask
+       .long sys_ni_syscall // sys_ssetmask
+       .long sys_setreuid16    /* 70 */
+       .long sys_setregid16
+       .long sys_sigsuspend
+       .long sys_ni_syscall // sys_sigpending
+       .long sys_sethostname
+       .long sys_setrlimit     /* 75 */
+       .long sys_ni_syscall // sys_old_getrlimit
+       .long sys_getrusage
+       .long sys_gettimeofday
+       .long sys_settimeofday
+       .long sys_getgroups16   /* 80 */
+       .long sys_setgroups16
+       .long sys_ni_syscall                    /* old_select slot */
+       .long sys_symlink
+       .long sys_lstat
+       .long sys_readlink              /* 85 */
+       .long sys_uselib
+       .long sys_swapon
+       .long sys_reboot
+       .long sys_ni_syscall // old_readdir
+       .long sys_ni_syscall    /* 90 */        /* old_mmap slot */
+       .long sys_munmap
+       .long sys_truncate
+       .long sys_ftruncate
+       .long sys_fchmod
+       .long sys_fchown16              /* 95 */
+       .long sys_getpriority
+       .long sys_setpriority
+       .long sys_ni_syscall                    /* old profil syscall holder */
+       .long sys_statfs
+       .long sys_fstatfs               /* 100 */
+       .long sys_ni_syscall                    /* ioperm for i386 */
+       .long sys_socketcall
+       .long sys_syslog
+       .long sys_setitimer
+       .long sys_getitimer     /* 105 */
+       .long sys_newstat
+       .long sys_newlstat
+       .long sys_newfstat
+       .long sys_ni_syscall    /* obsolete olduname( syscall */
+       .long sys_ni_syscall    /* iopl for i386 */ /* 110 */
+       .long sys_vhangup
+       .long sys_ni_syscall    /* obsolete idle( syscall */
+       .long sys_ni_syscall    /* vm86old for i386 */
+       .long sys_wait4
+       .long sys_swapoff               /* 115 */
+       .long sys_sysinfo
+       .long sys_ipc
+       .long sys_fsync
+       .long sys_sigreturn
+       .long sys_clone         /* 120 */
+       .long sys_setdomainname
+       .long sys_newuname
+       .long sys_ni_syscall    /* old "cacheflush" */
+       .long sys_adjtimex
+       .long __MMU(sys_mprotect) /* 125 */
+       .long sys_sigprocmask
+       .long sys_ni_syscall    /* old "create_module" */
+       .long sys_init_module
+       .long sys_delete_module
+       .long sys_ni_syscall    /* old "get_kernel_syms" */
+       .long sys_quotactl
+       .long sys_getpgid
+       .long sys_fchdir
+       .long sys_bdflush
+       .long sys_sysfs         /* 135 */
+       .long sys_personality
+       .long sys_ni_syscall    /* for afs_syscall */
+       .long sys_setfsuid16
+       .long sys_setfsgid16
+       .long sys_llseek                /* 140 */
+       .long sys_getdents
+       .long sys_select
+       .long sys_flock
+       .long __MMU(sys_msync)
+       .long sys_readv         /* 145 */
+       .long sys_writev
+       .long sys_getsid
+       .long sys_fdatasync
+       .long sys_sysctl
+       .long __MMU(sys_mlock)          /* 150 */
+       .long __MMU(sys_munlock)
+       .long __MMU(sys_mlockall)
+       .long __MMU(sys_munlockall)
+       .long sys_sched_setparam
+       .long sys_sched_getparam   /* 155 */
+       .long sys_sched_setscheduler
+       .long sys_sched_getscheduler
+       .long sys_sched_yield
+       .long sys_sched_get_priority_max
+       .long sys_sched_get_priority_min  /* 160 */
+       .long sys_sched_rr_get_interval
+       .long sys_nanosleep
+       .long __MMU(sys_mremap)
+       .long sys_setresuid16
+       .long sys_getresuid16   /* 165 */
+       .long sys_ni_syscall    /* for vm86 */
+       .long sys_ni_syscall    /* Old sys_query_module */
+       .long sys_poll
+       .long sys_nfsservctl
+       .long sys_setresgid16   /* 170 */
+       .long sys_getresgid16
+       .long sys_prctl
+       .long sys_rt_sigreturn
+       .long sys_rt_sigaction
+       .long sys_rt_sigprocmask        /* 175 */
+       .long sys_rt_sigpending
+       .long sys_rt_sigtimedwait
+       .long sys_rt_sigqueueinfo
+       .long sys_rt_sigsuspend
+       .long sys_pread64               /* 180 */
+       .long sys_pwrite64
+       .long sys_chown16
+       .long sys_getcwd
+       .long sys_capget
+       .long sys_capset           /* 185 */
+       .long sys_sigaltstack
+       .long sys_sendfile
+       .long sys_ni_syscall            /* streams1 */
+       .long sys_ni_syscall            /* streams2 */
+       .long sys_vfork            /* 190 */
+       .long sys_getrlimit
+       .long sys_mmap2
+       .long sys_truncate64
+       .long sys_ftruncate64
+       .long sys_stat64                /* 195 */
+       .long sys_lstat64
+       .long sys_fstat64
+       .long sys_lchown
+       .long sys_getuid
+       .long sys_getgid                /* 200 */
+       .long sys_geteuid
+       .long sys_getegid
+       .long sys_setreuid
+       .long sys_setregid
+       .long sys_getgroups     /* 205 */
+       .long sys_setgroups
+       .long sys_fchown
+       .long sys_setresuid
+       .long sys_getresuid
+       .long sys_setresgid     /* 210 */
+       .long sys_getresgid
+       .long sys_chown
+       .long sys_setuid
+       .long sys_setgid
+       .long sys_setfsuid              /* 215 */
+       .long sys_setfsgid
+       .long sys_pivot_root
+       .long __MMU(sys_mincore)
+       .long __MMU(sys_madvise)
+       .long sys_getdents64    /* 220 */
+       .long sys_fcntl64
+       .long sys_ni_syscall    /* reserved for TUX */
+       .long sys_ni_syscall    /* Reserved for Security */
+       .long sys_gettid
+       .long sys_readahead     /* 225 */
+       .long sys_setxattr
+       .long sys_lsetxattr
+       .long sys_fsetxattr
+       .long sys_getxattr
+       .long sys_lgetxattr     /* 230 */
+       .long sys_fgetxattr
+       .long sys_listxattr
+       .long sys_llistxattr
+       .long sys_flistxattr
+       .long sys_removexattr   /* 235 */
+       .long sys_lremovexattr
+       .long sys_fremovexattr
+       .long sys_tkill
+       .long sys_sendfile64
+       .long sys_futex         /* 240 */
+       .long sys_sched_setaffinity
+       .long sys_sched_getaffinity
+       .long sys_ni_syscall    //sys_set_thread_area
+       .long sys_ni_syscall    //sys_get_thread_area
+       .long sys_io_setup      /* 245 */
+       .long sys_io_destroy
+       .long sys_io_getevents
+       .long sys_io_submit
+       .long sys_io_cancel
+       .long sys_fadvise64     /* 250 */
+       .long sys_ni_syscall
+       .long sys_exit_group
+       .long sys_lookup_dcookie
+       .long sys_epoll_create
+       .long sys_epoll_ctl     /* 255 */
+       .long sys_epoll_wait
+       .long __MMU(sys_remap_file_pages)
+       .long sys_set_tid_address
+       .long sys_timer_create
+       .long sys_timer_settime         /* 260 */
+       .long sys_timer_gettime
+       .long sys_timer_getoverrun
+       .long sys_timer_delete
+       .long sys_clock_settime
+       .long sys_clock_gettime         /* 265 */
+       .long sys_clock_getres
+       .long sys_clock_nanosleep
+       .long sys_statfs64
+       .long sys_fstatfs64
+       .long sys_tgkill        /* 270 */
+       .long sys_utimes
+       .long sys_fadvise64_64
+       .long sys_ni_syscall    /* sys_vserver */
+       .long sys_mbind
+       .long sys_get_mempolicy
+       .long sys_set_mempolicy
+       .long sys_mq_open
+       .long sys_mq_unlink
+       .long sys_mq_timedsend
+       .long sys_mq_timedreceive       /* 280 */
+       .long sys_mq_notify
+       .long sys_mq_getsetattr
+       .long sys_ni_syscall            /* reserved for kexec */
+       .long sys_waitid
+       .long sys_ni_syscall            /* 285 */ /* available */
+       .long sys_add_key
+       .long sys_request_key
+       .long sys_keyctl
+       .long sys_ni_syscall // sys_vperfctr_open
+       .long sys_ni_syscall // sys_vperfctr_control    /* 290 */
+       .long sys_ni_syscall // sys_vperfctr_unlink
+       .long sys_ni_syscall // sys_vperfctr_iresume
+       .long sys_ni_syscall // sys_vperfctr_read
+
+
+syscall_table_size = (. - sys_call_table)
diff --git a/arch/frv/kernel/frv_ksyms.c b/arch/frv/kernel/frv_ksyms.c
new file mode 100644 (file)
index 0000000..62cfbd9
--- /dev/null
@@ -0,0 +1,124 @@
+#include <linux/module.h>
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/user.h>
+#include <linux/elfcore.h>
+#include <linux/in6.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+
+#include <asm/setup.h>
+#include <asm/pgalloc.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <asm/checksum.h>
+#include <asm/hardirq.h>
+#include <asm/current.h>
+
+extern void dump_thread(struct pt_regs *, struct user *);
+extern long __memcpy_user(void *dst, const void *src, size_t count);
+
+/* platform dependent support */
+
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(iounmap);
+
+EXPORT_SYMBOL(dump_thread);
+EXPORT_SYMBOL(strnlen);
+EXPORT_SYMBOL(strrchr);
+EXPORT_SYMBOL(strstr);
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strcmp);
+EXPORT_SYMBOL(strncmp);
+EXPORT_SYMBOL(strncpy);
+
+EXPORT_SYMBOL(ip_fast_csum);
+
+#if 0
+EXPORT_SYMBOL(local_irq_count);
+EXPORT_SYMBOL(local_bh_count);
+#endif
+EXPORT_SYMBOL(kernel_thread);
+
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
+EXPORT_SYMBOL(__res_bus_clock_speed_HZ);
+EXPORT_SYMBOL(__page_offset);
+EXPORT_SYMBOL(__memcpy_user);
+EXPORT_SYMBOL(flush_dcache_page);
+
+#ifndef CONFIG_MMU
+EXPORT_SYMBOL(memory_start);
+EXPORT_SYMBOL(memory_end);
+#endif
+
+EXPORT_SYMBOL(__debug_bug_trap);
+
+/* Networking helper routines. */
+EXPORT_SYMBOL(csum_partial_copy);
+
+/* The following are special because they're not called
+   explicitly (the C compiler generates them).  Fortunately,
+   their interface isn't gonna change any time soon now, so
+   it's OK to leave it out of version control.  */
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memscan);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(strtok);
+
+EXPORT_SYMBOL(get_wchan);
+
+#ifdef CONFIG_FRV_OUTOFLINE_ATOMIC_OPS
+EXPORT_SYMBOL(atomic_test_and_ANDNOT_mask);
+EXPORT_SYMBOL(atomic_test_and_OR_mask);
+EXPORT_SYMBOL(atomic_test_and_XOR_mask);
+EXPORT_SYMBOL(atomic_add_return);
+EXPORT_SYMBOL(atomic_sub_return);
+EXPORT_SYMBOL(__xchg_8);
+EXPORT_SYMBOL(__xchg_16);
+EXPORT_SYMBOL(__xchg_32);
+EXPORT_SYMBOL(__cmpxchg_8);
+EXPORT_SYMBOL(__cmpxchg_16);
+EXPORT_SYMBOL(__cmpxchg_32);
+#endif
+
+/*
+ * libgcc functions - functions that are used internally by the
+ * compiler...  (prototypes are not correct though, but that
+ * doesn't really matter since they're not versioned).
+ */
+extern void __gcc_bcmp(void);
+extern void __ashldi3(void);
+extern void __ashrdi3(void);
+extern void __cmpdi2(void);
+extern void __divdi3(void);
+extern void __lshrdi3(void);
+extern void __moddi3(void);
+extern void __muldi3(void);
+extern void __negdi2(void);
+extern void __ucmpdi2(void);
+extern void __udivdi3(void);
+extern void __udivmoddi4(void);
+extern void __umoddi3(void);
+
+        /* gcc lib functions */
+//EXPORT_SYMBOL(__gcc_bcmp);
+EXPORT_SYMBOL(__ashldi3);
+EXPORT_SYMBOL(__ashrdi3);
+//EXPORT_SYMBOL(__cmpdi2);
+//EXPORT_SYMBOL(__divdi3);
+EXPORT_SYMBOL(__lshrdi3);
+//EXPORT_SYMBOL(__moddi3);
+EXPORT_SYMBOL(__muldi3);
+EXPORT_SYMBOL(__negdi2);
+//EXPORT_SYMBOL(__ucmpdi2);
+//EXPORT_SYMBOL(__udivdi3);
+//EXPORT_SYMBOL(__udivmoddi4);
+//EXPORT_SYMBOL(__umoddi3);
diff --git a/arch/frv/kernel/gdb-io.c b/arch/frv/kernel/gdb-io.c
new file mode 100644 (file)
index 0000000..c997bcc
--- /dev/null
@@ -0,0 +1,216 @@
+/* gdb-io.c: FR403 GDB stub I/O
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/serial_reg.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/irc-regs.h>
+#include <asm/timer-regs.h>
+#include <asm/gdb-stub.h>
+#include "gdb-io.h"
+
+#ifdef CONFIG_GDBSTUB_UART0
+#define __UART(X) (*(volatile uint8_t *)(UART0_BASE + (UART_##X)))
+#define __UART_IRR_NMI 0xff0f0000
+#else /* CONFIG_GDBSTUB_UART1 */
+#define __UART(X) (*(volatile uint8_t *)(UART1_BASE + (UART_##X)))
+#define __UART_IRR_NMI 0xfff00000
+#endif
+
+#define LSR_WAIT_FOR(STATE)                    \
+do {                                           \
+       gdbstub_do_rx();                        \
+} while (!(__UART(LSR) & UART_LSR_##STATE))
+
+#define FLOWCTL_QUERY(LINE)    ({ __UART(MSR) & UART_MSR_##LINE; })
+#define FLOWCTL_CLEAR(LINE)    do { __UART(MCR) &= ~UART_MCR_##LINE; mb(); } while (0)
+#define FLOWCTL_SET(LINE)      do { __UART(MCR) |= UART_MCR_##LINE;  mb(); } while (0)
+
+#define FLOWCTL_WAIT_FOR(LINE)                 \
+do {                                           \
+       gdbstub_do_rx();                        \
+} while(!FLOWCTL_QUERY(LINE))
+
+/*****************************************************************************/
+/*
+ * initialise the GDB stub
+ * - called with PSR.ET==0, so can't incur external interrupts
+ */
+void gdbstub_io_init(void)
+{
+       /* set up the serial port */
+       __UART(LCR) = UART_LCR_WLEN8; /* 1N8 */
+       __UART(FCR) =
+               UART_FCR_ENABLE_FIFO |
+               UART_FCR_CLEAR_RCVR |
+               UART_FCR_CLEAR_XMIT |
+               UART_FCR_TRIGGER_1;
+
+       FLOWCTL_CLEAR(DTR);
+       FLOWCTL_SET(RTS);
+
+//     gdbstub_set_baud(115200);
+
+       /* we want to get serial receive interrupts */
+       __UART(IER) = UART_IER_RDI | UART_IER_RLSI;
+       mb();
+
+       __set_IRR(6, __UART_IRR_NMI);   /* map ERRs and UARTx to NMI */
+
+} /* end gdbstub_io_init() */
+
+/*****************************************************************************/
+/*
+ * set up the GDB stub serial port baud rate timers
+ */
+void gdbstub_set_baud(unsigned baud)
+{
+       unsigned value, high, low;
+       u8 lcr;
+
+       /* work out the divisor to give us the nearest higher baud rate */
+       value = __serial_clock_speed_HZ / 16 / baud;
+
+       /* determine the baud rate range */
+       high = __serial_clock_speed_HZ / 16 / value;
+       low = __serial_clock_speed_HZ / 16 / (value + 1);
+
+       /* pick the nearest bound */
+       if (low + (high - low) / 2 > baud)
+               value++;
+
+       lcr = __UART(LCR);
+       __UART(LCR) |= UART_LCR_DLAB;
+       mb();
+       __UART(DLL) = value & 0xff;
+       __UART(DLM) = (value >> 8) & 0xff;
+       mb();
+       __UART(LCR) = lcr;
+       mb();
+
+} /* end gdbstub_set_baud() */
+
+/*****************************************************************************/
+/*
+ * receive characters into the receive FIFO
+ */
+void gdbstub_do_rx(void)
+{
+       unsigned ix, nix;
+
+       ix = gdbstub_rx_inp;
+
+       while (__UART(LSR) & UART_LSR_DR) {
+               nix = (ix + 2) & 0xfff;
+               if (nix == gdbstub_rx_outp)
+                       break;
+
+               gdbstub_rx_buffer[ix++] = __UART(LSR);
+               gdbstub_rx_buffer[ix++] = __UART(RX);
+               ix = nix;
+       }
+
+       gdbstub_rx_inp = ix;
+
+       __clr_RC(15);
+       __clr_IRL();
+
+} /* end gdbstub_do_rx() */
+
+/*****************************************************************************/
+/*
+ * wait for a character to come from the debugger
+ */
+int gdbstub_rx_char(unsigned char *_ch, int nonblock)
+{
+       unsigned ix;
+       u8 ch, st;
+
+       *_ch = 0xff;
+
+       if (gdbstub_rx_unget) {
+               *_ch = gdbstub_rx_unget;
+               gdbstub_rx_unget = 0;
+               return 0;
+       }
+
+ try_again:
+       gdbstub_do_rx();
+
+       /* pull chars out of the buffer */
+       ix = gdbstub_rx_outp;
+       if (ix == gdbstub_rx_inp) {
+               if (nonblock)
+                       return -EAGAIN;
+               //watchdog_alert_counter = 0;
+               goto try_again;
+       }
+
+       st = gdbstub_rx_buffer[ix++];
+       ch = gdbstub_rx_buffer[ix++];
+       gdbstub_rx_outp = ix & 0x00000fff;
+
+       if (st & UART_LSR_BI) {
+               gdbstub_proto("### GDB Rx Break Detected ###\n");
+               return -EINTR;
+       }
+       else if (st & (UART_LSR_FE|UART_LSR_OE|UART_LSR_PE)) {
+               gdbstub_proto("### GDB Rx Error (st=%02x) ###\n",st);
+               return -EIO;
+       }
+       else {
+               gdbstub_proto("### GDB Rx %02x (st=%02x) ###\n",ch,st);
+               *_ch = ch & 0x7f;
+               return 0;
+       }
+
+} /* end gdbstub_rx_char() */
+
+/*****************************************************************************/
+/*
+ * send a character to the debugger
+ */
+void gdbstub_tx_char(unsigned char ch)
+{
+       FLOWCTL_SET(DTR);
+       LSR_WAIT_FOR(THRE);
+//     FLOWCTL_WAIT_FOR(CTS);
+
+       if (ch == 0x0a) {
+               __UART(TX) = 0x0d;
+               mb();
+               LSR_WAIT_FOR(THRE);
+//             FLOWCTL_WAIT_FOR(CTS);
+       }
+       __UART(TX) = ch;
+       mb();
+
+       FLOWCTL_CLEAR(DTR);
+} /* end gdbstub_tx_char() */
+
+/*****************************************************************************/
+/*
+ * send a character to the debugger
+ */
+void gdbstub_tx_flush(void)
+{
+       LSR_WAIT_FOR(TEMT);
+       LSR_WAIT_FOR(THRE);
+       FLOWCTL_CLEAR(DTR);
+} /* end gdbstub_tx_flush() */
diff --git a/arch/frv/kernel/gdb-io.h b/arch/frv/kernel/gdb-io.h
new file mode 100644 (file)
index 0000000..138714b
--- /dev/null
@@ -0,0 +1,55 @@
+/* gdb-io.h: FR403 GDB I/O port defs
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _GDB_IO_H
+#define _GDB_IO_H
+
+#include <asm/serial-regs.h>
+
+#undef UART_RX
+#undef UART_TX
+#undef UART_DLL
+#undef UART_DLM
+#undef UART_IER
+#undef UART_IIR
+#undef UART_FCR
+#undef UART_LCR
+#undef UART_MCR
+#undef UART_LSR
+#undef UART_MSR
+#undef UART_SCR
+
+#define UART_RX                0*8     /* In:  Receive buffer (DLAB=0) */
+#define UART_TX                0*8     /* Out: Transmit buffer (DLAB=0) */
+#define UART_DLL       0*8     /* Out: Divisor Latch Low (DLAB=1) */
+#define UART_DLM       1*8     /* Out: Divisor Latch High (DLAB=1) */
+#define UART_IER       1*8     /* Out: Interrupt Enable Register */
+#define UART_IIR       2*8     /* In:  Interrupt ID Register */
+#define UART_FCR       2*8     /* Out: FIFO Control Register */
+#define UART_LCR       3*8     /* Out: Line Control Register */
+#define UART_MCR       4*8     /* Out: Modem Control Register */
+#define UART_LSR       5*8     /* In:  Line Status Register */
+#define UART_MSR       6*8     /* In:  Modem Status Register */
+#define UART_SCR       7*8     /* I/O: Scratch Register */
+
+#define UART_LCR_DLAB  0x80    /* Divisor latch access bit */
+#define UART_LCR_SBC   0x40    /* Set break control */
+#define UART_LCR_SPAR  0x20    /* Stick parity (?) */
+#define UART_LCR_EPAR  0x10    /* Even parity select */
+#define UART_LCR_PARITY        0x08    /* Parity Enable */
+#define UART_LCR_STOP  0x04    /* Stop bits: 0=1 stop bit, 1= 2 stop bits */
+#define UART_LCR_WLEN5  0x00   /* Wordlength: 5 bits */
+#define UART_LCR_WLEN6  0x01   /* Wordlength: 6 bits */
+#define UART_LCR_WLEN7  0x02   /* Wordlength: 7 bits */
+#define UART_LCR_WLEN8  0x03   /* Wordlength: 8 bits */
+
+
+#endif /* _GDB_IO_H */
diff --git a/arch/frv/kernel/gdb-stub.c b/arch/frv/kernel/gdb-stub.c
new file mode 100644 (file)
index 0000000..8f860d9
--- /dev/null
@@ -0,0 +1,2084 @@
+/* gdb-stub.c: FRV GDB stub
+ *
+ * Copyright (C) 2003,4 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from Linux/MIPS version, Copyright (C) 1995 Andreas Busse
+ *
+ * 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.
+ */
+
+/*
+ *  To enable debugger support, two things need to happen.  One, a
+ *  call to set_debug_traps() is necessary in order to allow any breakpoints
+ *  or error conditions to be properly intercepted and reported to gdb.
+ *  Two, a breakpoint needs to be generated to begin communication.  This
+ *  is most easily accomplished by a call to breakpoint().  Breakpoint()
+ *  simulates a breakpoint by executing a BREAK instruction.
+ *
+ *
+ *    The following gdb commands are supported:
+ *
+ * command          function                               Return value
+ *
+ *    g             return the value of the CPU registers  hex data or ENN
+ *    G             set the value of the CPU registers     OK or ENN
+ *
+ *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
+ *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
+ *
+ *    c             Resume at current address              SNN   ( signal NN)
+ *    cAA..AA       Continue at address AA..AA             SNN
+ *
+ *    s             Step one instruction                   SNN
+ *    sAA..AA       Step one instruction from AA..AA       SNN
+ *
+ *    k             kill
+ *
+ *    ?             What was the last sigval ?             SNN   (signal NN)
+ *
+ *    bBB..BB      Set baud rate to BB..BB                OK or BNN, then sets
+ *                                                        baud rate
+ *
+ * All commands and responses are sent with a packet which includes a
+ * checksum.  A packet consists of
+ *
+ * $<packet info>#<checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * When a packet is received, it is first acknowledged with either '+' or '-'.
+ * '+' indicates a successful transfer.  '-' indicates a failed transfer.
+ *
+ * Example:
+ *
+ * Host:                  Reply:
+ * $m0,10#2a               +$00010203040506070809101112131415#42
+ *
+ *
+ *  ==============
+ *  MORE EXAMPLES:
+ *  ==============
+ *
+ *  For reference -- the following are the steps that one
+ *  company took (RidgeRun Inc) to get remote gdb debugging
+ *  going. In this scenario the host machine was a PC and the
+ *  target platform was a Galileo EVB64120A MIPS evaluation
+ *  board.
+ *
+ *  Step 1:
+ *  First download gdb-5.0.tar.gz from the internet.
+ *  and then build/install the package.
+ *
+ *  Example:
+ *    $ tar zxf gdb-5.0.tar.gz
+ *    $ cd gdb-5.0
+ *    $ ./configure --target=frv-elf-gdb
+ *    $ make
+ *    $ frv-elf-gdb
+ *
+ *  Step 2:
+ *  Configure linux for remote debugging and build it.
+ *
+ *  Example:
+ *    $ cd ~/linux
+ *    $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging>
+ *    $ make dep; make vmlinux
+ *
+ *  Step 3:
+ *  Download the kernel to the remote target and start
+ *  the kernel running. It will promptly halt and wait
+ *  for the host gdb session to connect. It does this
+ *  since the "Kernel Hacking" option has defined
+ *  CONFIG_REMOTE_DEBUG which in turn enables your calls
+ *  to:
+ *     set_debug_traps();
+ *     breakpoint();
+ *
+ *  Step 4:
+ *  Start the gdb session on the host.
+ *
+ *  Example:
+ *    $ frv-elf-gdb vmlinux
+ *    (gdb) set remotebaud 115200
+ *    (gdb) target remote /dev/ttyS1
+ *    ...at this point you are connected to
+ *       the remote target and can use gdb
+ *       in the normal fasion. Setting
+ *       breakpoints, single stepping,
+ *       printing variables, etc.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/nmi.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/gdb-stub.h>
+
+#define LEDS(x) do { /* *(u32*)0xe1200004 = ~(x); mb(); */ } while(0)
+
+#undef GDBSTUB_DEBUG_PROTOCOL
+
+extern void debug_to_serial(const char *p, int n);
+extern void gdbstub_console_write(struct console *co, const char *p, unsigned n);
+
+extern volatile uint32_t __break_error_detect[3]; /* ESFR1, ESR15, EAR15 */
+extern struct user_context __break_user_context;
+
+struct __debug_amr {
+       unsigned long L, P;
+} __attribute__((aligned(8)));
+
+struct __debug_mmu {
+       struct {
+               unsigned long   hsr0, pcsr, esr0, ear0, epcr0;
+#ifdef CONFIG_MMU
+               unsigned long   tplr, tppr, tpxr, cxnr;
+#endif
+       } regs;
+
+       struct __debug_amr      iamr[16];
+       struct __debug_amr      damr[16];
+
+#ifdef CONFIG_MMU
+       struct __debug_amr      tlb[64*2];
+#endif
+};
+
+static struct __debug_mmu __debug_mmu;
+
+/*
+ * BUFMAX defines the maximum number of characters in inbound/outbound buffers
+ * at least NUMREGBYTES*2 are needed for register packets
+ */
+#define BUFMAX 2048
+
+#define BREAK_INSN     0x801000c0      /* use "break" as bkpt */
+
+static const char gdbstub_banner[] = "Linux/FR-V GDB Stub (c) RedHat 2003\n";
+
+volatile u8    gdbstub_rx_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+volatile u32   gdbstub_rx_inp = 0;
+volatile u32   gdbstub_rx_outp = 0;
+volatile u8    gdbstub_rx_overflow = 0;
+u8             gdbstub_rx_unget = 0;
+
+/* set with GDB whilst running to permit step through exceptions */
+extern volatile u32 __attribute__((section(".bss"))) gdbstub_trace_through_exceptions;
+
+static char    input_buffer[BUFMAX];
+static char    output_buffer[BUFMAX];
+
+static const char hexchars[] = "0123456789abcdef";
+
+static const char *regnames[] = {
+       "PSR ", "ISR ", "CCR ", "CCCR",
+       "LR  ", "LCR ", "PC  ", "_stt",
+       "sys ", "GR8*", "GNE0", "GNE1",
+       "IACH", "IACL",
+       "TBR ", "SP  ", "FP  ", "GR3 ",
+       "GR4 ", "GR5 ", "GR6 ", "GR7 ",
+       "GR8 ", "GR9 ", "GR10", "GR11",
+       "GR12", "GR13", "GR14", "GR15",
+       "GR16", "GR17", "GR18", "GR19",
+       "GR20", "GR21", "GR22", "GR23",
+       "GR24", "GR25", "GR26", "GR27",
+       "EFRM", "CURR", "GR30", "BFRM"
+};
+
+struct gdbstub_bkpt {
+       unsigned long   addr;           /* address of breakpoint */
+       unsigned        len;            /* size of breakpoint */
+       uint32_t        originsns[7];   /* original instructions */
+};
+
+static struct gdbstub_bkpt gdbstub_bkpts[256];
+
+/*
+ * local prototypes
+ */
+
+static void gdbstub_recv_packet(char *buffer);
+static int gdbstub_send_packet(char *buffer);
+static int gdbstub_compute_signal(unsigned long tbr);
+static int hex(unsigned char ch);
+static int hexToInt(char **ptr, unsigned long *intValue);
+static unsigned char *mem2hex(const void *mem, char *buf, int count, int may_fault);
+static char *hex2mem(const char *buf, void *_mem, int count);
+
+/*
+ * Convert ch from a hex digit to an int
+ */
+static int hex(unsigned char ch)
+{
+       if (ch >= 'a' && ch <= 'f')
+               return ch-'a'+10;
+       if (ch >= '0' && ch <= '9')
+               return ch-'0';
+       if (ch >= 'A' && ch <= 'F')
+               return ch-'A'+10;
+       return -1;
+}
+
+void gdbstub_printk(const char *fmt, ...)
+{
+       static char buf[1024];
+       va_list args;
+       int len;
+
+       /* Emit the output into the temporary buffer */
+       va_start(args, fmt);
+       len = vsnprintf(buf, sizeof(buf), fmt, args);
+       va_end(args);
+       debug_to_serial(buf, len);
+}
+
+static inline char *gdbstub_strcpy(char *dst, const char *src)
+{
+       int loop = 0;
+       while ((dst[loop] = src[loop]))
+              loop++;
+       return dst;
+}
+
+static void gdbstub_purge_cache(void)
+{
+       asm volatile("  dcef    @(gr0,gr0),#1   \n"
+                    "  icei    @(gr0,gr0),#1   \n"
+                    "  membar                  \n"
+                    "  bar                     \n"
+                    );
+}
+
+/*****************************************************************************/
+/*
+ * scan for the sequence $<data>#<checksum>
+ */
+static void gdbstub_recv_packet(char *buffer)
+{
+       unsigned char checksum;
+       unsigned char xmitcsum;
+       unsigned char ch;
+       int count, i, ret, error;
+
+       for (;;) {
+               /* wait around for the start character, ignore all other characters */
+               do {
+                       gdbstub_rx_char(&ch, 0);
+               } while (ch != '$');
+
+               checksum = 0;
+               xmitcsum = -1;
+               count = 0;
+               error = 0;
+
+               /* now, read until a # or end of buffer is found */
+               while (count < BUFMAX) {
+                       ret = gdbstub_rx_char(&ch, 0);
+                       if (ret < 0)
+                               error = ret;
+
+                       if (ch == '#')
+                               break;
+                       checksum += ch;
+                       buffer[count] = ch;
+                       count++;
+               }
+
+               if (error == -EIO) {
+                       gdbstub_proto("### GDB Rx Error - Skipping packet ###\n");
+                       gdbstub_proto("### GDB Tx NAK\n");
+                       gdbstub_tx_char('-');
+                       continue;
+               }
+
+               if (count >= BUFMAX || error)
+                       continue;
+
+               buffer[count] = 0;
+
+               /* read the checksum */
+               ret = gdbstub_rx_char(&ch, 0);
+               if (ret < 0)
+                       error = ret;
+               xmitcsum = hex(ch) << 4;
+
+               ret = gdbstub_rx_char(&ch, 0);
+               if (ret < 0)
+                       error = ret;
+               xmitcsum |= hex(ch);
+
+               if (error) {
+                       if (error == -EIO)
+                               gdbstub_proto("### GDB Rx Error - Skipping packet\n");
+                       gdbstub_proto("### GDB Tx NAK\n");
+                       gdbstub_tx_char('-');
+                       continue;
+               }
+
+               /* check the checksum */
+               if (checksum != xmitcsum) {
+                       gdbstub_proto("### GDB Tx NAK\n");
+                       gdbstub_tx_char('-');   /* failed checksum */
+                       continue;
+               }
+
+               gdbstub_proto("### GDB Rx '$%s#%02x' ###\n", buffer, checksum);
+               gdbstub_proto("### GDB Tx ACK\n");
+               gdbstub_tx_char('+'); /* successful transfer */
+
+               /* if a sequence char is present, reply the sequence ID */
+               if (buffer[2] == ':') {
+                       gdbstub_tx_char(buffer[0]);
+                       gdbstub_tx_char(buffer[1]);
+
+                       /* remove sequence chars from buffer */
+                       count = 0;
+                       while (buffer[count]) count++;
+                       for (i=3; i <= count; i++)
+                               buffer[i - 3] = buffer[i];
+               }
+
+               break;
+       }
+} /* end gdbstub_recv_packet() */
+
+/*****************************************************************************/
+/*
+ * send the packet in buffer.
+ * - return 0 if successfully ACK'd
+ * - return 1 if abandoned due to new incoming packet
+ */
+static int gdbstub_send_packet(char *buffer)
+{
+       unsigned char checksum;
+       int count;
+       unsigned char ch;
+
+       /* $<packet info>#<checksum> */
+       gdbstub_proto("### GDB Tx '%s' ###\n", buffer);
+
+       do {
+               gdbstub_tx_char('$');
+               checksum = 0;
+               count = 0;
+
+               while ((ch = buffer[count]) != 0) {
+                       gdbstub_tx_char(ch);
+                       checksum += ch;
+                       count += 1;
+               }
+
+               gdbstub_tx_char('#');
+               gdbstub_tx_char(hexchars[checksum >> 4]);
+               gdbstub_tx_char(hexchars[checksum & 0xf]);
+
+       } while (gdbstub_rx_char(&ch,0),
+#ifdef GDBSTUB_DEBUG_PROTOCOL
+                ch=='-' && (gdbstub_proto("### GDB Rx NAK\n"),0),
+                ch!='-' && ch!='+' && (gdbstub_proto("### GDB Rx ??? %02x\n",ch),0),
+#endif
+                ch!='+' && ch!='$');
+
+       if (ch=='+') {
+               gdbstub_proto("### GDB Rx ACK\n");
+               return 0;
+       }
+
+       gdbstub_proto("### GDB Tx Abandoned\n");
+       gdbstub_rx_unget = ch;
+       return 1;
+} /* end gdbstub_send_packet() */
+
+/*
+ * While we find nice hex chars, build an int.
+ * Return number of chars processed.
+ */
+static int hexToInt(char **ptr, unsigned long *_value)
+{
+       int count = 0, ch;
+
+       *_value = 0;
+       while (**ptr) {
+               ch = hex(**ptr);
+               if (ch < 0)
+                       break;
+
+               *_value = (*_value << 4) | ((uint8_t) ch & 0xf);
+               count++;
+
+               (*ptr)++;
+       }
+
+       return count;
+}
+
+/*****************************************************************************/
+/*
+ * probe an address to see whether it maps to anything
+ */
+static inline int gdbstub_addr_probe(const void *vaddr)
+{
+#ifdef CONFIG_MMU
+       unsigned long paddr;
+
+       asm("lrad %1,%0,#1,#0,#0" : "=r"(paddr) : "r"(vaddr));
+       if (!(paddr & xAMPRx_V))
+               return 0;
+#endif
+
+       return 1;
+} /* end gdbstub_addr_probe() */
+
+#ifdef CONFIG_MMU
+static unsigned long __saved_dampr, __saved_damlr;
+
+static inline unsigned long gdbstub_virt_to_pte(unsigned long vaddr)
+{
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+       pte_t *pte;
+       unsigned long val, dampr5;
+
+       pgd = (pgd_t *) __get_DAMLR(3) + pgd_index(vaddr);
+       pud = pud_offset(pgd, vaddr);
+       pmd = pmd_offset(pud, vaddr);
+
+       if (pmd_bad(*pmd) || !pmd_present(*pmd))
+               return 0;
+
+       /* make sure dampr5 maps to the correct pmd */
+       dampr5 = __get_DAMPR(5);
+       val = pmd_val(*pmd);
+       __set_DAMPR(5, val | xAMPRx_L | xAMPRx_SS_16Kb | xAMPRx_S | xAMPRx_C | xAMPRx_V);
+
+       /* now its safe to access pmd */
+       pte = (pte_t *)__get_DAMLR(5) + __pte_index(vaddr);
+       if (pte_present(*pte))
+               val = pte_val(*pte);
+       else
+               val = 0;
+
+       /* restore original dampr5 */
+       __set_DAMPR(5, dampr5);
+
+       return val;
+}
+#endif
+
+static inline int gdbstub_addr_map(const void *vaddr)
+{
+#ifdef CONFIG_MMU
+       unsigned long pte;
+
+       __saved_dampr = __get_DAMPR(2);
+       __saved_damlr = __get_DAMLR(2);
+#endif
+       if (gdbstub_addr_probe(vaddr))
+               return 1;
+#ifdef CONFIG_MMU
+       pte = gdbstub_virt_to_pte((unsigned long) vaddr);
+       if (pte) {
+               __set_DAMPR(2, pte);
+               __set_DAMLR(2, (unsigned long) vaddr & PAGE_MASK);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+static inline void gdbstub_addr_unmap(void)
+{
+#ifdef CONFIG_MMU
+       __set_DAMPR(2, __saved_dampr);
+       __set_DAMLR(2, __saved_damlr);
+#endif
+}
+
+/*
+ * access potentially dodgy memory through a potentially dodgy pointer
+ */
+static inline int gdbstub_read_dword(const void *addr, uint32_t *_res)
+{
+       unsigned long brr;
+       uint32_t res;
+
+       if (!gdbstub_addr_map(addr))
+               return 0;
+
+       asm volatile("  movgs   gr0,brr \n"
+                    "  ld%I2   %M2,%0  \n"
+                    "  movsg   brr,%1  \n"
+                    : "=r"(res), "=r"(brr)
+                    : "m"(*(uint32_t *) addr));
+       *_res = res;
+       gdbstub_addr_unmap();
+       return likely(!brr);
+}
+
+static inline int gdbstub_write_dword(void *addr, uint32_t val)
+{
+       unsigned long brr;
+
+       if (!gdbstub_addr_map(addr))
+               return 0;
+
+       asm volatile("  movgs   gr0,brr \n"
+                    "  st%I2   %1,%M2  \n"
+                    "  movsg   brr,%0  \n"
+                    : "=r"(brr)
+                    : "r"(val), "m"(*(uint32_t *) addr));
+       gdbstub_addr_unmap();
+       return likely(!brr);
+}
+
+static inline int gdbstub_read_word(const void *addr, uint16_t *_res)
+{
+       unsigned long brr;
+       uint16_t res;
+
+       if (!gdbstub_addr_map(addr))
+               return 0;
+
+       asm volatile("  movgs   gr0,brr \n"
+                    "  lduh%I2 %M2,%0  \n"
+                    "  movsg   brr,%1  \n"
+                    : "=r"(res), "=r"(brr)
+                    : "m"(*(uint16_t *) addr));
+       *_res = res;
+       gdbstub_addr_unmap();
+       return likely(!brr);
+}
+
+static inline int gdbstub_write_word(void *addr, uint16_t val)
+{
+       unsigned long brr;
+
+       if (!gdbstub_addr_map(addr))
+               return 0;
+
+       asm volatile("  movgs   gr0,brr \n"
+                    "  sth%I2  %1,%M2  \n"
+                    "  movsg   brr,%0  \n"
+                    : "=r"(brr)
+                    : "r"(val), "m"(*(uint16_t *) addr));
+       gdbstub_addr_unmap();
+       return likely(!brr);
+}
+
+static inline int gdbstub_read_byte(const void *addr, uint8_t *_res)
+{
+       unsigned long brr;
+       uint8_t res;
+
+       if (!gdbstub_addr_map(addr))
+               return 0;
+
+       asm volatile("  movgs   gr0,brr \n"
+                    "  ldub%I2 %M2,%0  \n"
+                    "  movsg   brr,%1  \n"
+                    : "=r"(res), "=r"(brr)
+                    : "m"(*(uint8_t *) addr));
+       *_res = res;
+       gdbstub_addr_unmap();
+       return likely(!brr);
+}
+
+static inline int gdbstub_write_byte(void *addr, uint8_t val)
+{
+       unsigned long brr;
+
+       if (!gdbstub_addr_map(addr))
+               return 0;
+
+       asm volatile("  movgs   gr0,brr \n"
+                    "  stb%I2  %1,%M2  \n"
+                    "  movsg   brr,%0  \n"
+                    : "=r"(brr)
+                    : "r"(val), "m"(*(uint8_t *) addr));
+       gdbstub_addr_unmap();
+       return likely(!brr);
+}
+
+static void __gdbstub_console_write(struct console *co, const char *p, unsigned n)
+{
+       char outbuf[26];
+       int qty;
+
+       outbuf[0] = 'O';
+
+       while (n > 0) {
+               qty = 1;
+
+               while (n > 0 && qty < 20) {
+                       mem2hex(p, outbuf + qty, 2, 0);
+                       qty += 2;
+                       if (*p == 0x0a) {
+                               outbuf[qty++] = '0';
+                               outbuf[qty++] = 'd';
+                       }
+                       p++;
+                       n--;
+               }
+
+               outbuf[qty] = 0;
+               gdbstub_send_packet(outbuf);
+       }
+}
+
+#if 0
+void debug_to_serial(const char *p, int n)
+{
+       gdbstub_console_write(NULL,p,n);
+}
+#endif
+
+#ifdef CONFIG_GDBSTUB_CONSOLE
+
+static kdev_t gdbstub_console_dev(struct console *con)
+{
+       return MKDEV(1,3); /* /dev/null */
+}
+
+static struct console gdbstub_console = {
+       .name   = "gdb",
+       .write  = gdbstub_console_write,        /* in break.S */
+       .device = gdbstub_console_dev,
+       .flags  = CON_PRINTBUFFER,
+       .index  = -1,
+};
+
+#endif
+
+/*****************************************************************************/
+/*
+ * Convert the memory pointed to by mem into hex, placing result in buf.
+ * - if successful, return a pointer to the last char put in buf (NUL)
+ * - in case of mem fault, return NULL
+ * may_fault is non-zero if we are reading from arbitrary memory, but is currently
+ * not used.
+ */
+static unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault)
+{
+       const uint8_t *mem = _mem;
+       uint8_t ch[4] __attribute__((aligned(4)));
+
+       if ((uint32_t)mem&1 && count>=1) {
+               if (!gdbstub_read_byte(mem,ch))
+                       return NULL;
+               *buf++ = hexchars[ch[0] >> 4];
+               *buf++ = hexchars[ch[0] & 0xf];
+               mem++;
+               count--;
+       }
+
+       if ((uint32_t)mem&3 && count>=2) {
+               if (!gdbstub_read_word(mem,(uint16_t *)ch))
+                       return NULL;
+               *buf++ = hexchars[ch[0] >> 4];
+               *buf++ = hexchars[ch[0] & 0xf];
+               *buf++ = hexchars[ch[1] >> 4];
+               *buf++ = hexchars[ch[1] & 0xf];
+               mem += 2;
+               count -= 2;
+       }
+
+       while (count>=4) {
+               if (!gdbstub_read_dword(mem,(uint32_t *)ch))
+                       return NULL;
+               *buf++ = hexchars[ch[0] >> 4];
+               *buf++ = hexchars[ch[0] & 0xf];
+               *buf++ = hexchars[ch[1] >> 4];
+               *buf++ = hexchars[ch[1] & 0xf];
+               *buf++ = hexchars[ch[2] >> 4];
+               *buf++ = hexchars[ch[2] & 0xf];
+               *buf++ = hexchars[ch[3] >> 4];
+               *buf++ = hexchars[ch[3] & 0xf];
+               mem += 4;
+               count -= 4;
+       }
+
+       if (count>=2) {
+               if (!gdbstub_read_word(mem,(uint16_t *)ch))
+                       return NULL;
+               *buf++ = hexchars[ch[0] >> 4];
+               *buf++ = hexchars[ch[0] & 0xf];
+               *buf++ = hexchars[ch[1] >> 4];
+               *buf++ = hexchars[ch[1] & 0xf];
+               mem += 2;
+               count -= 2;
+       }
+
+       if (count>=1) {
+               if (!gdbstub_read_byte(mem,ch))
+                       return NULL;
+               *buf++ = hexchars[ch[0] >> 4];
+               *buf++ = hexchars[ch[0] & 0xf];
+       }
+
+       *buf = 0;
+
+       return buf;
+} /* end mem2hex() */
+
+/*****************************************************************************/
+/*
+ * convert the hex array pointed to by buf into binary to be placed in mem
+ * return a pointer to the character AFTER the last byte of buffer consumed
+ */
+static char *hex2mem(const char *buf, void *_mem, int count)
+{
+       uint8_t *mem = _mem;
+       union {
+               uint32_t l;
+               uint16_t w;
+               uint8_t  b[4];
+       } ch;
+
+       if ((u32)mem&1 && count>=1) {
+               ch.b[0]  = hex(*buf++) << 4;
+               ch.b[0] |= hex(*buf++);
+               if (!gdbstub_write_byte(mem,ch.b[0]))
+                       return NULL;
+               mem++;
+               count--;
+       }
+
+       if ((u32)mem&3 && count>=2) {
+               ch.b[0]  = hex(*buf++) << 4;
+               ch.b[0] |= hex(*buf++);
+               ch.b[1]  = hex(*buf++) << 4;
+               ch.b[1] |= hex(*buf++);
+               if (!gdbstub_write_word(mem,ch.w))
+                       return NULL;
+               mem += 2;
+               count -= 2;
+       }
+
+       while (count>=4) {
+               ch.b[0]  = hex(*buf++) << 4;
+               ch.b[0] |= hex(*buf++);
+               ch.b[1]  = hex(*buf++) << 4;
+               ch.b[1] |= hex(*buf++);
+               ch.b[2]  = hex(*buf++) << 4;
+               ch.b[2] |= hex(*buf++);
+               ch.b[3]  = hex(*buf++) << 4;
+               ch.b[3] |= hex(*buf++);
+               if (!gdbstub_write_dword(mem,ch.l))
+                       return NULL;
+               mem += 4;
+               count -= 4;
+       }
+
+       if (count>=2) {
+               ch.b[0]  = hex(*buf++) << 4;
+               ch.b[0] |= hex(*buf++);
+               ch.b[1]  = hex(*buf++) << 4;
+               ch.b[1] |= hex(*buf++);
+               if (!gdbstub_write_word(mem,ch.w))
+                       return NULL;
+               mem += 2;
+               count -= 2;
+       }
+
+       if (count>=1) {
+               ch.b[0]  = hex(*buf++) << 4;
+               ch.b[0] |= hex(*buf++);
+               if (!gdbstub_write_byte(mem,ch.b[0]))
+                       return NULL;
+       }
+
+       return (char *) buf;
+} /* end hex2mem() */
+
+/*****************************************************************************/
+/*
+ * This table contains the mapping between FRV TBR.TT exception codes,
+ * and signals, which are primarily what GDB understands.  It also
+ * indicates which hardware traps we need to commandeer when
+ * initializing the stub.
+ */
+static const struct brr_to_sig_map {
+       unsigned long   brr_mask;       /* BRR bitmask */
+       unsigned long   tbr_tt;         /* TBR.TT code (in BRR.EBTT) */
+       unsigned int    signo;          /* Signal that we map this into */
+} brr_to_sig_map[] = {
+       { BRR_EB,       TBR_TT_INSTR_ACC_ERROR, SIGSEGV         },
+       { BRR_EB,       TBR_TT_ILLEGAL_INSTR,   SIGILL          },
+       { BRR_EB,       TBR_TT_PRIV_INSTR,      SIGILL          },
+       { BRR_EB,       TBR_TT_MP_EXCEPTION,    SIGFPE          },
+       { BRR_EB,       TBR_TT_DATA_ACC_ERROR,  SIGSEGV         },
+       { BRR_EB,       TBR_TT_DATA_STR_ERROR,  SIGSEGV         },
+       { BRR_EB,       TBR_TT_DIVISION_EXCEP,  SIGFPE          },
+       { BRR_EB,       TBR_TT_COMPOUND_EXCEP,  SIGSEGV         },
+       { BRR_EB,       TBR_TT_INTERRUPT_13,    SIGALRM         },      /* watchdog */
+       { BRR_EB,       TBR_TT_INTERRUPT_14,    SIGINT          },      /* GDB serial */
+       { BRR_EB,       TBR_TT_INTERRUPT_15,    SIGQUIT         },      /* NMI */
+       { BRR_CB,       0,                      SIGUSR1         },
+       { BRR_TB,       0,                      SIGUSR2         },
+       { BRR_DBNEx,    0,                      SIGTRAP         },
+       { BRR_DBx,      0,                      SIGTRAP         },      /* h/w watchpoint */
+       { BRR_IBx,      0,                      SIGTRAP         },      /* h/w breakpoint */
+       { BRR_CBB,      0,                      SIGTRAP         },
+       { BRR_SB,       0,                      SIGTRAP         },
+       { BRR_ST,       0,                      SIGTRAP         },      /* single step */
+       { 0,            0,                      SIGHUP          }       /* default */
+};
+
+/*****************************************************************************/
+/*
+ * convert the FRV BRR register contents into a UNIX signal number
+ */
+static inline int gdbstub_compute_signal(unsigned long brr)
+{
+       const struct brr_to_sig_map *map;
+       unsigned long tbr = (brr & BRR_EBTT) >> 12;
+
+       for (map = brr_to_sig_map; map->brr_mask; map++)
+               if (map->brr_mask & brr)
+                       if (!map->tbr_tt || map->tbr_tt == tbr)
+                               break;
+
+       return map->signo;
+} /* end gdbstub_compute_signal() */
+
+/*****************************************************************************/
+/*
+ * set a software breakpoint or a hardware breakpoint or watchpoint
+ */
+static int gdbstub_set_breakpoint(unsigned long type, unsigned long addr, unsigned long len)
+{
+       unsigned long tmp;
+       int bkpt, loop, xloop;
+
+       union {
+               struct {
+                       unsigned long mask0, mask1;
+               };
+               uint8_t bytes[8];
+       } dbmr;
+
+       //gdbstub_printk("setbkpt(%ld,%08lx,%ld)\n", type, addr, len);
+
+       switch (type) {
+               /* set software breakpoint */
+       case 0:
+               if (addr & 3 || len > 7*4)
+                       return -EINVAL;
+
+               for (bkpt = 255; bkpt >= 0; bkpt--)
+                       if (!gdbstub_bkpts[bkpt].addr)
+                               break;
+               if (bkpt < 0)
+                       return -ENOSPC;
+
+               for (loop = 0; loop < len/4; loop++)
+                       if (!gdbstub_read_dword(&((uint32_t *) addr)[loop],
+                                               &gdbstub_bkpts[bkpt].originsns[loop]))
+                               return -EFAULT;
+
+               for (loop = 0; loop < len/4; loop++)
+                       if (!gdbstub_write_dword(&((uint32_t *) addr)[loop],
+                                                BREAK_INSN)
+                           ) {
+                               /* need to undo the changes if possible */
+                               for (xloop = 0; xloop < loop; xloop++)
+                                       gdbstub_write_dword(&((uint32_t *) addr)[xloop],
+                                                           gdbstub_bkpts[bkpt].originsns[xloop]);
+                               return -EFAULT;
+                       }
+
+               gdbstub_bkpts[bkpt].addr = addr;
+               gdbstub_bkpts[bkpt].len = len;
+
+#if 0
+               gdbstub_printk("Set BKPT[%02x]: %08lx #%d {%04x, %04x} -> { %04x, %04x }\n",
+                              bkpt,
+                              gdbstub_bkpts[bkpt].addr,
+                              gdbstub_bkpts[bkpt].len,
+                              gdbstub_bkpts[bkpt].originsns[0],
+                              gdbstub_bkpts[bkpt].originsns[1],
+                              ((uint32_t *) addr)[0],
+                              ((uint32_t *) addr)[1]
+                              );
+#endif
+               return 0;
+
+               /* set hardware breakpoint */
+       case 1:
+               if (addr & 3 || len != 4)
+                       return -EINVAL;
+
+               if (!(__debug_regs->dcr & DCR_IBE0)) {
+                       //gdbstub_printk("set h/w break 0: %08lx\n", addr);
+                       __debug_regs->dcr |= DCR_IBE0;
+                       asm volatile("movgs %0,ibar0" : : "r"(addr));
+                       return 0;
+               }
+
+               if (!(__debug_regs->dcr & DCR_IBE1)) {
+                       //gdbstub_printk("set h/w break 1: %08lx\n", addr);
+                       __debug_regs->dcr |= DCR_IBE1;
+                       asm volatile("movgs %0,ibar1" : : "r"(addr));
+                       return 0;
+               }
+
+               if (!(__debug_regs->dcr & DCR_IBE2)) {
+                       //gdbstub_printk("set h/w break 2: %08lx\n", addr);
+                       __debug_regs->dcr |= DCR_IBE2;
+                       asm volatile("movgs %0,ibar2" : : "r"(addr));
+                       return 0;
+               }
+
+               if (!(__debug_regs->dcr & DCR_IBE3)) {
+                       //gdbstub_printk("set h/w break 3: %08lx\n", addr);
+                       __debug_regs->dcr |= DCR_IBE3;
+                       asm volatile("movgs %0,ibar3" : : "r"(addr));
+                       return 0;
+               }
+
+               return -ENOSPC;
+
+               /* set data read/write/access watchpoint */
+       case 2:
+       case 3:
+       case 4:
+               if ((addr & ~7) != ((addr + len - 1) & ~7))
+                       return -EINVAL;
+
+               tmp = addr & 7;
+
+               memset(dbmr.bytes, 0xff, sizeof(dbmr.bytes));
+               for (loop = 0; loop < len; loop++)
+                       dbmr.bytes[tmp + loop] = 0;
+
+               addr &= ~7;
+
+               if (!(__debug_regs->dcr & (DCR_DRBE0|DCR_DWBE0))) {
+                       //gdbstub_printk("set h/w watchpoint 0 type %ld: %08lx\n", type, addr);
+                       tmp = type==2 ? DCR_DWBE0 : type==3 ? DCR_DRBE0 : DCR_DRBE0|DCR_DWBE0;
+                       __debug_regs->dcr |= tmp;
+                       asm volatile("  movgs   %0,dbar0        \n"
+                                    "  movgs   %1,dbmr00       \n"
+                                    "  movgs   %2,dbmr01       \n"
+                                    "  movgs   gr0,dbdr00      \n"
+                                    "  movgs   gr0,dbdr01      \n"
+                                    : : "r"(addr), "r"(dbmr.mask0), "r"(dbmr.mask1));
+                       return 0;
+               }
+
+               if (!(__debug_regs->dcr & (DCR_DRBE1|DCR_DWBE1))) {
+                       //gdbstub_printk("set h/w watchpoint 1 type %ld: %08lx\n", type, addr);
+                       tmp = type==2 ? DCR_DWBE1 : type==3 ? DCR_DRBE1 : DCR_DRBE1|DCR_DWBE1;
+                       __debug_regs->dcr |= tmp;
+                       asm volatile("  movgs   %0,dbar1        \n"
+                                    "  movgs   %1,dbmr10       \n"
+                                    "  movgs   %2,dbmr11       \n"
+                                    "  movgs   gr0,dbdr10      \n"
+                                    "  movgs   gr0,dbdr11      \n"
+                                    : : "r"(addr), "r"(dbmr.mask0), "r"(dbmr.mask1));
+                       return 0;
+               }
+
+               return -ENOSPC;
+
+       default:
+               return -EINVAL;
+       }
+
+} /* end gdbstub_set_breakpoint() */
+
+/*****************************************************************************/
+/*
+ * clear a breakpoint or watchpoint
+ */
+int gdbstub_clear_breakpoint(unsigned long type, unsigned long addr, unsigned long len)
+{
+       unsigned long tmp;
+       int bkpt, loop;
+
+       union {
+               struct {
+                       unsigned long mask0, mask1;
+               };
+               uint8_t bytes[8];
+       } dbmr;
+
+       //gdbstub_printk("clearbkpt(%ld,%08lx,%ld)\n", type, addr, len);
+
+       switch (type) {
+               /* clear software breakpoint */
+       case 0:
+               for (bkpt = 255; bkpt >= 0; bkpt--)
+                       if (gdbstub_bkpts[bkpt].addr == addr && gdbstub_bkpts[bkpt].len == len)
+                               break;
+               if (bkpt < 0)
+                       return -ENOENT;
+
+               gdbstub_bkpts[bkpt].addr = 0;
+
+               for (loop = 0; loop < len/4; loop++)
+                       if (!gdbstub_write_dword(&((uint32_t *) addr)[loop],
+                                                gdbstub_bkpts[bkpt].originsns[loop]))
+                               return -EFAULT;
+               return 0;
+
+               /* clear hardware breakpoint */
+       case 1:
+               if (addr & 3 || len != 4)
+                       return -EINVAL;
+
+#define __get_ibar(X) ({ unsigned long x; asm volatile("movsg ibar"#X",%0" : "=r"(x)); x; })
+
+               if (__debug_regs->dcr & DCR_IBE0 && __get_ibar(0) == addr) {
+                       //gdbstub_printk("clear h/w break 0: %08lx\n", addr);
+                       __debug_regs->dcr &= ~DCR_IBE0;
+                       asm volatile("movgs gr0,ibar0");
+                       return 0;
+               }
+
+               if (__debug_regs->dcr & DCR_IBE1 && __get_ibar(1) == addr) {
+                       //gdbstub_printk("clear h/w break 1: %08lx\n", addr);
+                       __debug_regs->dcr &= ~DCR_IBE1;
+                       asm volatile("movgs gr0,ibar1");
+                       return 0;
+               }
+
+               if (__debug_regs->dcr & DCR_IBE2 && __get_ibar(2) == addr) {
+                       //gdbstub_printk("clear h/w break 2: %08lx\n", addr);
+                       __debug_regs->dcr &= ~DCR_IBE2;
+                       asm volatile("movgs gr0,ibar2");
+                       return 0;
+               }
+
+               if (__debug_regs->dcr & DCR_IBE3 && __get_ibar(3) == addr) {
+                       //gdbstub_printk("clear h/w break 3: %08lx\n", addr);
+                       __debug_regs->dcr &= ~DCR_IBE3;
+                       asm volatile("movgs gr0,ibar3");
+                       return 0;
+               }
+
+               return -EINVAL;
+
+               /* clear data read/write/access watchpoint */
+       case 2:
+       case 3:
+       case 4:
+               if ((addr & ~7) != ((addr + len - 1) & ~7))
+                       return -EINVAL;
+
+               tmp = addr & 7;
+
+               memset(dbmr.bytes, 0xff, sizeof(dbmr.bytes));
+               for (loop = 0; loop < len; loop++)
+                       dbmr.bytes[tmp + loop] = 0;
+
+               addr &= ~7;
+
+#define __get_dbar(X) ({ unsigned long x; asm volatile("movsg dbar"#X",%0" : "=r"(x)); x; })
+#define __get_dbmr0(X) ({ unsigned long x; asm volatile("movsg dbmr"#X"0,%0" : "=r"(x)); x; })
+#define __get_dbmr1(X) ({ unsigned long x; asm volatile("movsg dbmr"#X"1,%0" : "=r"(x)); x; })
+
+               /* consider DBAR 0 */
+               tmp = type==2 ? DCR_DWBE0 : type==3 ? DCR_DRBE0 : DCR_DRBE0|DCR_DWBE0;
+
+               if ((__debug_regs->dcr & (DCR_DRBE0|DCR_DWBE0)) != tmp ||
+                   __get_dbar(0) != addr ||
+                   __get_dbmr0(0) != dbmr.mask0 ||
+                   __get_dbmr1(0) != dbmr.mask1)
+                       goto skip_dbar0;
+
+               //gdbstub_printk("clear h/w watchpoint 0 type %ld: %08lx\n", type, addr);
+               __debug_regs->dcr &= ~(DCR_DRBE0|DCR_DWBE0);
+               asm volatile("  movgs   gr0,dbar0       \n"
+                            "  movgs   gr0,dbmr00      \n"
+                            "  movgs   gr0,dbmr01      \n"
+                            "  movgs   gr0,dbdr00      \n"
+                            "  movgs   gr0,dbdr01      \n");
+               return 0;
+
+       skip_dbar0:
+               /* consider DBAR 0 */
+               tmp = type==2 ? DCR_DWBE1 : type==3 ? DCR_DRBE1 : DCR_DRBE1|DCR_DWBE1;
+
+               if ((__debug_regs->dcr & (DCR_DRBE1|DCR_DWBE1)) != tmp ||
+                   __get_dbar(1) != addr ||
+                   __get_dbmr0(1) != dbmr.mask0 ||
+                   __get_dbmr1(1) != dbmr.mask1)
+                       goto skip_dbar1;
+
+               //gdbstub_printk("clear h/w watchpoint 1 type %ld: %08lx\n", type, addr);
+               __debug_regs->dcr &= ~(DCR_DRBE1|DCR_DWBE1);
+               asm volatile("  movgs   gr0,dbar1       \n"
+                            "  movgs   gr0,dbmr10      \n"
+                            "  movgs   gr0,dbmr11      \n"
+                            "  movgs   gr0,dbdr10      \n"
+                            "  movgs   gr0,dbdr11      \n");
+               return 0;
+
+       skip_dbar1:
+               return -ENOSPC;
+
+       default:
+               return -EINVAL;
+       }
+} /* end gdbstub_clear_breakpoint() */
+
+/*****************************************************************************/
+/*
+ * check a for an internal software breakpoint, and wind the PC back if necessary
+ */
+static void gdbstub_check_breakpoint(void)
+{
+       unsigned long addr = __debug_frame->pc - 4;
+       int bkpt;
+
+       for (bkpt = 255; bkpt >= 0; bkpt--)
+               if (gdbstub_bkpts[bkpt].addr == addr)
+                       break;
+       if (bkpt >= 0)
+               __debug_frame->pc = addr;
+
+       //gdbstub_printk("alter pc [%d] %08lx\n", bkpt, __debug_frame->pc);
+
+} /* end gdbstub_check_breakpoint() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+static void __attribute__((unused)) gdbstub_show_regs(void)
+{
+       uint32_t *reg;
+       int loop;
+
+       gdbstub_printk("\n");
+
+       gdbstub_printk("Frame: @%p [%s]\n",
+                      __debug_frame,
+                      __debug_frame->psr & PSR_S ? "kernel" : "user");
+
+       reg = (uint32_t *) __debug_frame;
+       for (loop = 0; loop < REG__END; loop++) {
+               printk("%s %08x", regnames[loop + 0], reg[loop + 0]);
+
+               if (loop == REG__END - 1 || loop % 5 == 4)
+                       printk("\n");
+               else
+                       printk(" | ");
+       }
+
+       gdbstub_printk("Process %s (pid: %d)\n", current->comm, current->pid);
+} /* end gdbstub_show_regs() */
+
+/*****************************************************************************/
+/*
+ * dump debugging regs
+ */
+static void __attribute__((unused)) gdbstub_dump_debugregs(void)
+{
+       unsigned long x;
+
+       x = __debug_regs->dcr;
+       gdbstub_printk("DCR    %08lx  ", x);
+
+       x = __debug_regs->brr;
+       gdbstub_printk("BRR %08lx\n", x);
+
+       gdbstub_printk("IBAR0  %08lx  ", __get_ibar(0));
+       gdbstub_printk("IBAR1  %08lx  ", __get_ibar(1));
+       gdbstub_printk("IBAR2  %08lx  ", __get_ibar(2));
+       gdbstub_printk("IBAR3  %08lx\n", __get_ibar(3));
+
+       gdbstub_printk("DBAR0  %08lx  ", __get_dbar(0));
+       gdbstub_printk("DBMR00 %08lx  ", __get_dbmr0(0));
+       gdbstub_printk("DBMR01 %08lx\n", __get_dbmr1(0));
+
+       gdbstub_printk("DBAR1  %08lx  ", __get_dbar(1));
+       gdbstub_printk("DBMR10 %08lx  ", __get_dbmr0(1));
+       gdbstub_printk("DBMR11 %08lx\n", __get_dbmr1(1));
+
+       gdbstub_printk("\n");
+} /* end gdbstub_dump_debugregs() */
+
+/*****************************************************************************/
+/*
+ * dump the MMU state into a structure so that it can be accessed with GDB
+ */
+void gdbstub_get_mmu_state(void)
+{
+       asm volatile("movsg hsr0,%0" : "=r"(__debug_mmu.regs.hsr0));
+       asm volatile("movsg pcsr,%0" : "=r"(__debug_mmu.regs.pcsr));
+       asm volatile("movsg esr0,%0" : "=r"(__debug_mmu.regs.esr0));
+       asm volatile("movsg ear0,%0" : "=r"(__debug_mmu.regs.ear0));
+       asm volatile("movsg epcr0,%0" : "=r"(__debug_mmu.regs.epcr0));
+
+       /* read the protection / SAT registers */
+       __debug_mmu.iamr[0].L  = __get_IAMLR(0);
+       __debug_mmu.iamr[0].P  = __get_IAMPR(0);
+       __debug_mmu.iamr[1].L  = __get_IAMLR(1);
+       __debug_mmu.iamr[1].P  = __get_IAMPR(1);
+       __debug_mmu.iamr[2].L  = __get_IAMLR(2);
+       __debug_mmu.iamr[2].P  = __get_IAMPR(2);
+       __debug_mmu.iamr[3].L  = __get_IAMLR(3);
+       __debug_mmu.iamr[3].P  = __get_IAMPR(3);
+       __debug_mmu.iamr[4].L  = __get_IAMLR(4);
+       __debug_mmu.iamr[4].P  = __get_IAMPR(4);
+       __debug_mmu.iamr[5].L  = __get_IAMLR(5);
+       __debug_mmu.iamr[5].P  = __get_IAMPR(5);
+       __debug_mmu.iamr[6].L  = __get_IAMLR(6);
+       __debug_mmu.iamr[6].P  = __get_IAMPR(6);
+       __debug_mmu.iamr[7].L  = __get_IAMLR(7);
+       __debug_mmu.iamr[7].P  = __get_IAMPR(7);
+       __debug_mmu.iamr[8].L  = __get_IAMLR(8);
+       __debug_mmu.iamr[8].P  = __get_IAMPR(8);
+       __debug_mmu.iamr[9].L  = __get_IAMLR(9);
+       __debug_mmu.iamr[9].P  = __get_IAMPR(9);
+       __debug_mmu.iamr[10].L = __get_IAMLR(10);
+       __debug_mmu.iamr[10].P = __get_IAMPR(10);
+       __debug_mmu.iamr[11].L = __get_IAMLR(11);
+       __debug_mmu.iamr[11].P = __get_IAMPR(11);
+       __debug_mmu.iamr[12].L = __get_IAMLR(12);
+       __debug_mmu.iamr[12].P = __get_IAMPR(12);
+       __debug_mmu.iamr[13].L = __get_IAMLR(13);
+       __debug_mmu.iamr[13].P = __get_IAMPR(13);
+       __debug_mmu.iamr[14].L = __get_IAMLR(14);
+       __debug_mmu.iamr[14].P = __get_IAMPR(14);
+       __debug_mmu.iamr[15].L = __get_IAMLR(15);
+       __debug_mmu.iamr[15].P = __get_IAMPR(15);
+
+       __debug_mmu.damr[0].L  = __get_DAMLR(0);
+       __debug_mmu.damr[0].P  = __get_DAMPR(0);
+       __debug_mmu.damr[1].L  = __get_DAMLR(1);
+       __debug_mmu.damr[1].P  = __get_DAMPR(1);
+       __debug_mmu.damr[2].L  = __get_DAMLR(2);
+       __debug_mmu.damr[2].P  = __get_DAMPR(2);
+       __debug_mmu.damr[3].L  = __get_DAMLR(3);
+       __debug_mmu.damr[3].P  = __get_DAMPR(3);
+       __debug_mmu.damr[4].L  = __get_DAMLR(4);
+       __debug_mmu.damr[4].P  = __get_DAMPR(4);
+       __debug_mmu.damr[5].L  = __get_DAMLR(5);
+       __debug_mmu.damr[5].P  = __get_DAMPR(5);
+       __debug_mmu.damr[6].L  = __get_DAMLR(6);
+       __debug_mmu.damr[6].P  = __get_DAMPR(6);
+       __debug_mmu.damr[7].L  = __get_DAMLR(7);
+       __debug_mmu.damr[7].P  = __get_DAMPR(7);
+       __debug_mmu.damr[8].L  = __get_DAMLR(8);
+       __debug_mmu.damr[8].P  = __get_DAMPR(8);
+       __debug_mmu.damr[9].L  = __get_DAMLR(9);
+       __debug_mmu.damr[9].P  = __get_DAMPR(9);
+       __debug_mmu.damr[10].L = __get_DAMLR(10);
+       __debug_mmu.damr[10].P = __get_DAMPR(10);
+       __debug_mmu.damr[11].L = __get_DAMLR(11);
+       __debug_mmu.damr[11].P = __get_DAMPR(11);
+       __debug_mmu.damr[12].L = __get_DAMLR(12);
+       __debug_mmu.damr[12].P = __get_DAMPR(12);
+       __debug_mmu.damr[13].L = __get_DAMLR(13);
+       __debug_mmu.damr[13].P = __get_DAMPR(13);
+       __debug_mmu.damr[14].L = __get_DAMLR(14);
+       __debug_mmu.damr[14].P = __get_DAMPR(14);
+       __debug_mmu.damr[15].L = __get_DAMLR(15);
+       __debug_mmu.damr[15].P = __get_DAMPR(15);
+
+#ifdef CONFIG_MMU
+       do {
+               /* read the DAT entries from the TLB */
+               struct __debug_amr *p;
+               int loop;
+
+               asm volatile("movsg tplr,%0" : "=r"(__debug_mmu.regs.tplr));
+               asm volatile("movsg tppr,%0" : "=r"(__debug_mmu.regs.tppr));
+               asm volatile("movsg tpxr,%0" : "=r"(__debug_mmu.regs.tpxr));
+               asm volatile("movsg cxnr,%0" : "=r"(__debug_mmu.regs.cxnr));
+
+               p = __debug_mmu.tlb;
+
+               /* way 0 */
+               asm volatile("movgs %0,tpxr" :: "r"(0 << TPXR_WAY_SHIFT));
+               for (loop = 0; loop < 64; loop++) {
+                       asm volatile("tlbpr %0,gr0,#1,#0" :: "r"(loop << PAGE_SHIFT));
+                       asm volatile("movsg tplr,%0" : "=r"(p->L));
+                       asm volatile("movsg tppr,%0" : "=r"(p->P));
+                       p++;
+               }
+
+               /* way 1 */
+               asm volatile("movgs %0,tpxr" :: "r"(1 << TPXR_WAY_SHIFT));
+               for (loop = 0; loop < 64; loop++) {
+                       asm volatile("tlbpr %0,gr0,#1,#0" :: "r"(loop << PAGE_SHIFT));
+                       asm volatile("movsg tplr,%0" : "=r"(p->L));
+                       asm volatile("movsg tppr,%0" : "=r"(p->P));
+                       p++;
+               }
+
+               asm volatile("movgs %0,tplr" :: "r"(__debug_mmu.regs.tplr));
+               asm volatile("movgs %0,tppr" :: "r"(__debug_mmu.regs.tppr));
+               asm volatile("movgs %0,tpxr" :: "r"(__debug_mmu.regs.tpxr));
+       } while(0);
+#endif
+
+} /* end gdbstub_get_mmu_state() */
+
+/*****************************************************************************/
+/*
+ * handle event interception and GDB remote protocol processing
+ * - on entry:
+ *     PSR.ET==0, PSR.S==1 and the CPU is in debug mode
+ *     __debug_frame points to the saved registers
+ *     __frame points to the kernel mode exception frame, if it was in kernel
+ *      mode when the break happened
+ */
+void gdbstub(int sigval)
+{
+       unsigned long addr, length, loop, dbar, temp, temp2, temp3;
+       uint32_t zero;
+       char *ptr;
+       int flush_cache = 0;
+
+       LEDS(0x5000);
+
+       if (sigval < 0) {
+#ifndef CONFIG_GDBSTUB_IMMEDIATE
+               /* return immediately if GDB immediate activation option not set */
+               return;
+#else
+               sigval = SIGINT;
+#endif
+       }
+
+       save_user_regs(&__break_user_context);
+
+#if 0
+       gdbstub_printk("--> gdbstub() %08x %p %08x %08x\n",
+                      __debug_frame->pc,
+                      __debug_frame,
+                      __debug_regs->brr,
+                      __debug_regs->bpsr);
+//     gdbstub_show_regs();
+#endif
+
+       LEDS(0x5001);
+
+       /* if we were interrupted by input on the serial gdbstub serial port,
+        * restore the context prior to the interrupt so that we return to that
+        * directly
+        */
+       temp = (unsigned long) __entry_kerneltrap_table;
+       temp2 = (unsigned long) __entry_usertrap_table;
+       temp3 = __debug_frame->pc & ~15;
+
+       if (temp3 == temp + TBR_TT_INTERRUPT_15 ||
+           temp3 == temp2 + TBR_TT_INTERRUPT_15
+           ) {
+               asm volatile("movsg pcsr,%0" : "=r"(__debug_frame->pc));
+               __debug_frame->psr |= PSR_ET;
+               __debug_frame->psr &= ~PSR_S;
+               if (__debug_frame->psr & PSR_PS)
+                       __debug_frame->psr |= PSR_S;
+               __debug_regs->brr = (__debug_frame->tbr & TBR_TT) << 12;
+               __debug_regs->brr |= BRR_EB;
+               sigval = SIGINT;
+       }
+
+       /* handle the decrement timer going off (FR451 only) */
+       if (temp3 == temp + TBR_TT_DECREMENT_TIMER ||
+           temp3 == temp2 + TBR_TT_DECREMENT_TIMER
+           ) {
+               asm volatile("movgs %0,timerd" :: "r"(10000000));
+               asm volatile("movsg pcsr,%0" : "=r"(__debug_frame->pc));
+               __debug_frame->psr |= PSR_ET;
+               __debug_frame->psr &= ~PSR_S;
+               if (__debug_frame->psr & PSR_PS)
+                       __debug_frame->psr |= PSR_S;
+               __debug_regs->brr = (__debug_frame->tbr & TBR_TT) << 12;
+               __debug_regs->brr |= BRR_EB;
+               sigval = SIGXCPU;;
+       }
+
+       LEDS(0x5002);
+
+       /* after a BREAK insn, the PC lands on the far side of it */
+       if (__debug_regs->brr & BRR_SB)
+               gdbstub_check_breakpoint();
+
+       LEDS(0x5003);
+
+       /* handle attempts to write console data via GDB "O" commands */
+       if (__debug_frame->pc == (unsigned long) gdbstub_console_write + 4) {
+               __gdbstub_console_write((struct console *) __debug_frame->gr8,
+                                       (const char *) __debug_frame->gr9,
+                                       (unsigned) __debug_frame->gr10);
+               goto done;
+       }
+
+       if (gdbstub_rx_unget) {
+               sigval = SIGINT;
+               goto packet_waiting;
+       }
+
+       if (!sigval)
+               sigval = gdbstub_compute_signal(__debug_regs->brr);
+
+       LEDS(0x5004);
+
+       /* send a message to the debugger's user saying what happened if it may
+        * not be clear cut (we can't map exceptions onto signals properly)
+        */
+       if (sigval != SIGINT && sigval != SIGTRAP && sigval != SIGILL) {
+               static const char title[] = "Break ";
+               static const char crlf[] = "\r\n";
+               unsigned long brr = __debug_regs->brr;
+               char hx;
+
+               ptr = output_buffer;
+               *ptr++ = 'O';
+               ptr = mem2hex(title, ptr, sizeof(title) - 1,0);
+
+               hx = hexchars[(brr & 0xf0000000) >> 28];
+               *ptr++ = hexchars[hx >> 4];     *ptr++ = hexchars[hx & 0xf];
+               hx = hexchars[(brr & 0x0f000000) >> 24];
+               *ptr++ = hexchars[hx >> 4];     *ptr++ = hexchars[hx & 0xf];
+               hx = hexchars[(brr & 0x00f00000) >> 20];
+               *ptr++ = hexchars[hx >> 4];     *ptr++ = hexchars[hx & 0xf];
+               hx = hexchars[(brr & 0x000f0000) >> 16];
+               *ptr++ = hexchars[hx >> 4];     *ptr++ = hexchars[hx & 0xf];
+               hx = hexchars[(brr & 0x0000f000) >> 12];
+               *ptr++ = hexchars[hx >> 4];     *ptr++ = hexchars[hx & 0xf];
+               hx = hexchars[(brr & 0x00000f00) >> 8];
+               *ptr++ = hexchars[hx >> 4];     *ptr++ = hexchars[hx & 0xf];
+               hx = hexchars[(brr & 0x000000f0) >> 4];
+               *ptr++ = hexchars[hx >> 4];     *ptr++ = hexchars[hx & 0xf];
+               hx = hexchars[(brr & 0x0000000f)];
+               *ptr++ = hexchars[hx >> 4];     *ptr++ = hexchars[hx & 0xf];
+
+               ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0);
+               *ptr = 0;
+               gdbstub_send_packet(output_buffer);     /* send it off... */
+       }
+
+       LEDS(0x5005);
+
+       /* tell the debugger that an exception has occurred */
+       ptr = output_buffer;
+
+       /* Send trap type (converted to signal) */
+       *ptr++ = 'T';
+       *ptr++ = hexchars[sigval >> 4];
+       *ptr++ = hexchars[sigval & 0xf];
+
+       /* Send Error PC */
+       *ptr++ = hexchars[GDB_REG_PC >> 4];
+       *ptr++ = hexchars[GDB_REG_PC & 0xf];
+       *ptr++ = ':';
+       ptr = mem2hex(&__debug_frame->pc, ptr, 4, 0);
+       *ptr++ = ';';
+
+       /*
+        * Send frame pointer
+        */
+       *ptr++ = hexchars[GDB_REG_FP >> 4];
+       *ptr++ = hexchars[GDB_REG_FP & 0xf];
+       *ptr++ = ':';
+       ptr = mem2hex(&__debug_frame->fp, ptr, 4, 0);
+       *ptr++ = ';';
+
+       /*
+        * Send stack pointer
+        */
+       *ptr++ = hexchars[GDB_REG_SP >> 4];
+       *ptr++ = hexchars[GDB_REG_SP & 0xf];
+       *ptr++ = ':';
+       ptr = mem2hex(&__debug_frame->sp, ptr, 4, 0);
+       *ptr++ = ';';
+
+       *ptr++ = 0;
+       gdbstub_send_packet(output_buffer);     /* send it off... */
+
+       LEDS(0x5006);
+
+ packet_waiting:
+       gdbstub_get_mmu_state();
+
+       /* wait for input from remote GDB */
+       while (1) {
+               output_buffer[0] = 0;
+
+               LEDS(0x5007);
+               gdbstub_recv_packet(input_buffer);
+               LEDS(0x5600 | input_buffer[0]);
+
+               switch (input_buffer[0]) {
+                       /* request repeat of last signal number */
+               case '?':
+                       output_buffer[0] = 'S';
+                       output_buffer[1] = hexchars[sigval >> 4];
+                       output_buffer[2] = hexchars[sigval & 0xf];
+                       output_buffer[3] = 0;
+                       break;
+
+               case 'd':
+                       /* toggle debug flag */
+                       break;
+
+                       /* return the value of the CPU registers
+                        * - GR0,  GR1,  GR2,  GR3,  GR4,  GR5,  GR6,  GR7,
+                        * - GR8,  GR9,  GR10, GR11, GR12, GR13, GR14, GR15,
+                        * - GR16, GR17, GR18, GR19, GR20, GR21, GR22, GR23,
+                        * - GR24, GR25, GR26, GR27, GR28, GR29, GR30, GR31,
+                        * - GR32, GR33, GR34, GR35, GR36, GR37, GR38, GR39,
+                        * - GR40, GR41, GR42, GR43, GR44, GR45, GR46, GR47,
+                        * - GR48, GR49, GR50, GR51, GR52, GR53, GR54, GR55,
+                        * - GR56, GR57, GR58, GR59, GR60, GR61, GR62, GR63,
+                        * - FP0,  FP1,  FP2,  FP3,  FP4,  FP5,  FP6,  FP7,
+                        * - FP8,  FP9,  FP10, FP11, FP12, FP13, FP14, FP15,
+                        * - FP16, FP17, FP18, FP19, FP20, FP21, FP22, FP23,
+                        * - FP24, FP25, FP26, FP27, FP28, FP29, FP30, FP31,
+                        * - FP32, FP33, FP34, FP35, FP36, FP37, FP38, FP39,
+                        * - FP40, FP41, FP42, FP43, FP44, FP45, FP46, FP47,
+                        * - FP48, FP49, FP50, FP51, FP52, FP53, FP54, FP55,
+                        * - FP56, FP57, FP58, FP59, FP60, FP61, FP62, FP63,
+                        * - PC, PSR, CCR, CCCR,
+                        * - _X132, _X133, _X134
+                        * - TBR, BRR, DBAR0, DBAR1, DBAR2, DBAR3,
+                        * - _X141, _X142, _X143, _X144,
+                        * - LR, LCR
+                        */
+               case 'g':
+                       zero = 0;
+                       ptr = output_buffer;
+
+                       /* deal with GR0, GR1-GR27, GR28-GR31, GR32-GR63 */
+                       ptr = mem2hex(&zero, ptr, 4, 0);
+
+                       for (loop = 1; loop <= 27; loop++)
+                               ptr = mem2hex((unsigned long *)__debug_frame + REG_GR(loop),
+                                             ptr, 4, 0);
+                       temp = (unsigned long) __frame;
+                       ptr = mem2hex(&temp, ptr, 4, 0);
+                       ptr = mem2hex((unsigned long *)__debug_frame + REG_GR(29), ptr, 4, 0);
+                       ptr = mem2hex((unsigned long *)__debug_frame + REG_GR(30), ptr, 4, 0);
+#ifdef CONFIG_MMU
+                       ptr = mem2hex((unsigned long *)__debug_frame + REG_GR(31), ptr, 4, 0);
+#else
+                       temp = (unsigned long) __debug_frame;
+                       ptr = mem2hex(&temp, ptr, 4, 0);
+#endif
+
+                       for (loop = 32; loop <= 63; loop++)
+                               ptr = mem2hex((unsigned long *)__debug_frame + REG_GR(loop),
+                                             ptr, 4, 0);
+
+                       /* deal with FR0-FR63 */
+                       for (loop = 0; loop <= 63; loop++)
+                               ptr = mem2hex((unsigned long *)&__break_user_context +
+                                             __FPMEDIA_FR(loop),
+                                             ptr, 4, 0);
+
+                       /* deal with special registers */
+                       ptr = mem2hex(&__debug_frame->pc,    ptr, 4, 0);
+                       ptr = mem2hex(&__debug_frame->psr,   ptr, 4, 0);
+                       ptr = mem2hex(&__debug_frame->ccr,   ptr, 4, 0);
+                       ptr = mem2hex(&__debug_frame->cccr,  ptr, 4, 0);
+                       ptr = mem2hex(&zero, ptr, 4, 0);
+                       ptr = mem2hex(&zero, ptr, 4, 0);
+                       ptr = mem2hex(&zero, ptr, 4, 0);
+                       ptr = mem2hex(&__debug_frame->tbr,   ptr, 4, 0);
+                       ptr = mem2hex(&__debug_regs->brr ,   ptr, 4, 0);
+
+                       asm volatile("movsg dbar0,%0" : "=r"(dbar));
+                       ptr = mem2hex(&dbar, ptr, 4, 0);
+                       asm volatile("movsg dbar1,%0" : "=r"(dbar));
+                       ptr = mem2hex(&dbar, ptr, 4, 0);
+                       asm volatile("movsg dbar2,%0" : "=r"(dbar));
+                       ptr = mem2hex(&dbar, ptr, 4, 0);
+                       asm volatile("movsg dbar3,%0" : "=r"(dbar));
+                       ptr = mem2hex(&dbar, ptr, 4, 0);
+
+                       asm volatile("movsg scr0,%0" : "=r"(dbar));
+                       ptr = mem2hex(&dbar, ptr, 4, 0);
+                       asm volatile("movsg scr1,%0" : "=r"(dbar));
+                       ptr = mem2hex(&dbar, ptr, 4, 0);
+                       asm volatile("movsg scr2,%0" : "=r"(dbar));
+                       ptr = mem2hex(&dbar, ptr, 4, 0);
+                       asm volatile("movsg scr3,%0" : "=r"(dbar));
+                       ptr = mem2hex(&dbar, ptr, 4, 0);
+
+                       ptr = mem2hex(&__debug_frame->lr, ptr, 4, 0);
+                       ptr = mem2hex(&__debug_frame->lcr, ptr, 4, 0);
+
+                       ptr = mem2hex(&__debug_frame->iacc0, ptr, 8, 0);
+
+                       ptr = mem2hex(&__break_user_context.f.fsr[0], ptr, 4, 0);
+
+                       for (loop = 0; loop <= 7; loop++)
+                               ptr = mem2hex(&__break_user_context.f.acc[loop], ptr, 4, 0);
+
+                       ptr = mem2hex(&__break_user_context.f.accg, ptr, 8, 0);
+
+                       for (loop = 0; loop <= 1; loop++)
+                               ptr = mem2hex(&__break_user_context.f.msr[loop], ptr, 4, 0);
+
+                       ptr = mem2hex(&__debug_frame->gner0, ptr, 4, 0);
+                       ptr = mem2hex(&__debug_frame->gner1, ptr, 4, 0);
+
+                       ptr = mem2hex(&__break_user_context.f.fner[0], ptr, 4, 0);
+                       ptr = mem2hex(&__break_user_context.f.fner[1], ptr, 4, 0);
+
+                       break;
+
+                       /* set the values of the CPU registers */
+               case 'G':
+                       ptr = &input_buffer[1];
+
+                       /* deal with GR0, GR1-GR27, GR28-GR31, GR32-GR63 */
+                       ptr = hex2mem(ptr, &temp, 4);
+
+                       for (loop = 1; loop <= 27; loop++)
+                               ptr = hex2mem(ptr, (unsigned long *)__debug_frame + REG_GR(loop),
+                                             4);
+
+                       ptr = hex2mem(ptr, &temp, 4);
+                       __frame = (struct pt_regs *) temp;
+                       ptr = hex2mem(ptr, &__debug_frame->gr29, 4);
+                       ptr = hex2mem(ptr, &__debug_frame->gr30, 4);
+#ifdef CONFIG_MMU
+                       ptr = hex2mem(ptr, &__debug_frame->gr31, 4);
+#else
+                       ptr = hex2mem(ptr, &temp, 4);
+#endif
+
+                       for (loop = 32; loop <= 63; loop++)
+                               ptr = hex2mem(ptr, (unsigned long *)__debug_frame + REG_GR(loop),
+                                             4);
+
+                       /* deal with FR0-FR63 */
+                       for (loop = 0; loop <= 63; loop++)
+                               ptr = mem2hex((unsigned long *)&__break_user_context +
+                                             __FPMEDIA_FR(loop),
+                                             ptr, 4, 0);
+
+                       /* deal with special registers */
+                       ptr = hex2mem(ptr, &__debug_frame->pc,  4);
+                       ptr = hex2mem(ptr, &__debug_frame->psr, 4);
+                       ptr = hex2mem(ptr, &__debug_frame->ccr, 4);
+                       ptr = hex2mem(ptr, &__debug_frame->cccr,4);
+
+                       for (loop = 132; loop <= 140; loop++)
+                               ptr = hex2mem(ptr, &temp, 4);
+
+                       ptr = hex2mem(ptr, &temp, 4);
+                       asm volatile("movgs %0,scr0" :: "r"(temp));
+                       ptr = hex2mem(ptr, &temp, 4);
+                       asm volatile("movgs %0,scr1" :: "r"(temp));
+                       ptr = hex2mem(ptr, &temp, 4);
+                       asm volatile("movgs %0,scr2" :: "r"(temp));
+                       ptr = hex2mem(ptr, &temp, 4);
+                       asm volatile("movgs %0,scr3" :: "r"(temp));
+
+                       ptr = hex2mem(ptr, &__debug_frame->lr,  4);
+                       ptr = hex2mem(ptr, &__debug_frame->lcr, 4);
+
+                       ptr = hex2mem(ptr, &__debug_frame->iacc0, 8);
+
+                       ptr = hex2mem(ptr, &__break_user_context.f.fsr[0], 4);
+
+                       for (loop = 0; loop <= 7; loop++)
+                               ptr = hex2mem(ptr, &__break_user_context.f.acc[loop], 4);
+
+                       ptr = hex2mem(ptr, &__break_user_context.f.accg, 8);
+
+                       for (loop = 0; loop <= 1; loop++)
+                               ptr = hex2mem(ptr, &__break_user_context.f.msr[loop], 4);
+
+                       ptr = hex2mem(ptr, &__debug_frame->gner0, 4);
+                       ptr = hex2mem(ptr, &__debug_frame->gner1, 4);
+
+                       ptr = hex2mem(ptr, &__break_user_context.f.fner[0], 4);
+                       ptr = hex2mem(ptr, &__break_user_context.f.fner[1], 4);
+
+                       gdbstub_strcpy(output_buffer,"OK");
+                       break;
+
+                       /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
+               case 'm':
+                       ptr = &input_buffer[1];
+
+                       if (hexToInt(&ptr, &addr) &&
+                           *ptr++ == ',' &&
+                           hexToInt(&ptr, &length)
+                           ) {
+                               if (mem2hex((char *)addr, output_buffer, length, 1))
+                                       break;
+                               gdbstub_strcpy (output_buffer, "E03");
+                       }
+                       else {
+                               gdbstub_strcpy(output_buffer,"E01");
+                       }
+                       break;
+
+                       /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
+               case 'M':
+                       ptr = &input_buffer[1];
+
+                       if (hexToInt(&ptr, &addr) &&
+                           *ptr++ == ',' &&
+                           hexToInt(&ptr, &length) &&
+                           *ptr++ == ':'
+                           ) {
+                               if (hex2mem(ptr, (char *)addr, length)) {
+                                       gdbstub_strcpy(output_buffer, "OK");
+                               }
+                               else {
+                                       gdbstub_strcpy(output_buffer, "E03");
+                               }
+                       }
+                       else
+                               gdbstub_strcpy(output_buffer, "E02");
+
+                       flush_cache = 1;
+                       break;
+
+                       /* PNN,=RRRRRRRR: Write value R to reg N return OK */
+               case 'P':
+                       ptr = &input_buffer[1];
+
+                       if (!hexToInt(&ptr, &addr) ||
+                           *ptr++ != '=' ||
+                           !hexToInt(&ptr, &temp)
+                           ) {
+                               gdbstub_strcpy(output_buffer, "E01");
+                               break;
+                       }
+
+                       temp2 = 1;
+                       switch (addr) {
+                       case GDB_REG_GR(0):
+                               break;
+                       case GDB_REG_GR(1) ... GDB_REG_GR(63):
+                               __break_user_context.i.gr[addr - GDB_REG_GR(0)] = temp;
+                               break;
+                       case GDB_REG_FR(0) ... GDB_REG_FR(63):
+                               __break_user_context.f.fr[addr - GDB_REG_FR(0)] = temp;
+                               break;
+                       case GDB_REG_PC:
+                               __break_user_context.i.pc = temp;
+                               break;
+                       case GDB_REG_PSR:
+                               __break_user_context.i.psr = temp;
+                               break;
+                       case GDB_REG_CCR:
+                               __break_user_context.i.ccr = temp;
+                               break;
+                       case GDB_REG_CCCR:
+                               __break_user_context.i.cccr = temp;
+                               break;
+                       case GDB_REG_BRR:
+                               __debug_regs->brr = temp;
+                               break;
+                       case GDB_REG_LR:
+                               __break_user_context.i.lr = temp;
+                               break;
+                       case GDB_REG_LCR:
+                               __break_user_context.i.lcr = temp;
+                               break;
+                       case GDB_REG_FSR0:
+                               __break_user_context.f.fsr[0] = temp;
+                               break;
+                       case GDB_REG_ACC(0) ... GDB_REG_ACC(7):
+                               __break_user_context.f.acc[addr - GDB_REG_ACC(0)] = temp;
+                               break;
+                       case GDB_REG_ACCG(0):
+                               *(uint32_t *) &__break_user_context.f.accg[0] = temp;
+                               break;
+                       case GDB_REG_ACCG(4):
+                               *(uint32_t *) &__break_user_context.f.accg[4] = temp;
+                               break;
+                       case GDB_REG_MSR(0) ... GDB_REG_MSR(1):
+                               __break_user_context.f.msr[addr - GDB_REG_MSR(0)] = temp;
+                               break;
+                       case GDB_REG_GNER(0) ... GDB_REG_GNER(1):
+                               __break_user_context.i.gner[addr - GDB_REG_GNER(0)] = temp;
+                               break;
+                       case GDB_REG_FNER(0) ... GDB_REG_FNER(1):
+                               __break_user_context.f.fner[addr - GDB_REG_FNER(0)] = temp;
+                               break;
+                       default:
+                               temp2 = 0;
+                               break;
+                       }
+
+                       if (temp2) {
+                               gdbstub_strcpy(output_buffer, "OK");
+                       }
+                       else {
+                               gdbstub_strcpy(output_buffer, "E02");
+                       }
+                       break;
+
+                       /* cAA..AA    Continue at address AA..AA(optional) */
+               case 'c':
+                       /* try to read optional parameter, pc unchanged if no parm */
+                       ptr = &input_buffer[1];
+                       if (hexToInt(&ptr, &addr))
+                               __debug_frame->pc = addr;
+                       goto done;
+
+                       /* kill the program */
+               case 'k' :
+                       goto done;      /* just continue */
+
+
+                       /* reset the whole machine (FIXME: system dependent) */
+               case 'r':
+                       break;
+
+
+                       /* step to next instruction */
+               case 's':
+                       __debug_regs->dcr |= DCR_SE;
+                       goto done;
+
+                       /* set baud rate (bBB) */
+               case 'b':
+                       ptr = &input_buffer[1];
+                       if (!hexToInt(&ptr, &temp)) {
+                               gdbstub_strcpy(output_buffer,"B01");
+                               break;
+                       }
+
+                       if (temp) {
+                               /* ack before changing speed */
+                               gdbstub_send_packet("OK");
+                               gdbstub_set_baud(temp);
+                       }
+                       break;
+
+                       /* set breakpoint */
+               case 'Z':
+                       ptr = &input_buffer[1];
+
+                       if (!hexToInt(&ptr,&temp) || *ptr++ != ',' ||
+                           !hexToInt(&ptr,&addr) || *ptr++ != ',' ||
+                           !hexToInt(&ptr,&length)
+                           ) {
+                               gdbstub_strcpy(output_buffer,"E01");
+                               break;
+                       }
+
+                       if (temp >= 5) {
+                               gdbstub_strcpy(output_buffer,"E03");
+                               break;
+                       }
+
+                       if (gdbstub_set_breakpoint(temp, addr, length) < 0) {
+                               gdbstub_strcpy(output_buffer,"E03");
+                               break;
+                       }
+
+                       if (temp == 0)
+                               flush_cache = 1; /* soft bkpt by modified memory */
+
+                       gdbstub_strcpy(output_buffer,"OK");
+                       break;
+
+                       /* clear breakpoint */
+               case 'z':
+                       ptr = &input_buffer[1];
+
+                       if (!hexToInt(&ptr,&temp) || *ptr++ != ',' ||
+                           !hexToInt(&ptr,&addr) || *ptr++ != ',' ||
+                           !hexToInt(&ptr,&length)
+                           ) {
+                               gdbstub_strcpy(output_buffer,"E01");
+                               break;
+                       }
+
+                       if (temp >= 5) {
+                               gdbstub_strcpy(output_buffer,"E03");
+                               break;
+                       }
+
+                       if (gdbstub_clear_breakpoint(temp, addr, length) < 0) {
+                               gdbstub_strcpy(output_buffer,"E03");
+                               break;
+                       }
+
+                       if (temp == 0)
+                               flush_cache = 1; /* soft bkpt by modified memory */
+
+                       gdbstub_strcpy(output_buffer,"OK");
+                       break;
+
+               default:
+                       gdbstub_proto("### GDB Unsupported Cmd '%s'\n",input_buffer);
+                       break;
+               }
+
+               /* reply to the request */
+               LEDS(0x5009);
+               gdbstub_send_packet(output_buffer);
+       }
+
+ done:
+       restore_user_regs(&__break_user_context);
+
+       //gdbstub_dump_debugregs();
+       //gdbstub_printk("<-- gdbstub() %08x\n", __debug_frame->pc);
+
+       /* need to flush the instruction cache before resuming, as we may have
+        * deposited a breakpoint, and the icache probably has no way of
+        * knowing that a data ref to some location may have changed something
+        * that is in the instruction cache.  NB: We flush both caches, just to
+        * be sure...
+        */
+
+       /* note: flushing the icache will clobber EAR0 on the FR451 */
+       if (flush_cache)
+               gdbstub_purge_cache();
+
+       LEDS(0x5666);
+
+} /* end gdbstub() */
+
+/*****************************************************************************/
+/*
+ * initialise the GDB stub
+ */
+void __init gdbstub_init(void)
+{
+#ifdef CONFIG_GDBSTUB_IMMEDIATE
+       unsigned char ch;
+       int ret;
+#endif
+
+       gdbstub_printk("%s", gdbstub_banner);
+       gdbstub_printk("DCR: %x\n", __debug_regs->dcr);
+
+       gdbstub_io_init();
+
+       /* try to talk to GDB (or anyone insane enough to want to type GDB protocol by hand) */
+       gdbstub_proto("### GDB Tx ACK\n");
+       gdbstub_tx_char('+'); /* 'hello world' */
+
+#ifdef CONFIG_GDBSTUB_IMMEDIATE
+       gdbstub_printk("GDB Stub waiting for packet\n");
+
+       /*
+        * In case GDB is started before us, ack any packets
+        * (presumably "$?#xx") sitting there.
+        */
+       do { gdbstub_rx_char(&ch, 0); } while (ch != '$');
+       do { gdbstub_rx_char(&ch, 0); } while (ch != '#');
+       do { ret = gdbstub_rx_char(&ch, 0); } while (ret != 0); /* eat first csum byte */
+       do { ret = gdbstub_rx_char(&ch, 0); } while (ret != 0); /* eat second csum byte */
+
+       gdbstub_proto("### GDB Tx NAK\n");
+       gdbstub_tx_char('-'); /* nak it */
+
+#else
+       gdbstub_printk("GDB Stub set\n");
+#endif
+
+#if 0
+       /* send banner */
+       ptr = output_buffer;
+       *ptr++ = 'O';
+       ptr = mem2hex(gdbstub_banner, ptr, sizeof(gdbstub_banner) - 1, 0);
+       gdbstub_send_packet(output_buffer);
+#endif
+#if defined(CONFIG_GDBSTUB_CONSOLE) && defined(CONFIG_GDBSTUB_IMMEDIATE)
+       register_console(&gdbstub_console);
+#endif
+
+} /* end gdbstub_init() */
+
+/*****************************************************************************/
+/*
+ * register the console at a more appropriate time
+ */
+#if defined (CONFIG_GDBSTUB_CONSOLE) && !defined(CONFIG_GDBSTUB_IMMEDIATE)
+static int __init gdbstub_postinit(void)
+{
+       printk("registering console\n");
+       register_console(&gdbstub_console);
+       return 0;
+} /* end gdbstub_postinit() */
+
+__initcall(gdbstub_postinit);
+#endif
+
+/*****************************************************************************/
+/*
+ * send an exit message to GDB
+ */
+void gdbstub_exit(int status)
+{
+       unsigned char checksum;
+       int count;
+       unsigned char ch;
+
+       sprintf(output_buffer,"W%02x",status&0xff);
+
+       gdbstub_tx_char('$');
+       checksum = 0;
+       count = 0;
+
+       while ((ch = output_buffer[count]) != 0) {
+               gdbstub_tx_char(ch);
+               checksum += ch;
+               count += 1;
+       }
+
+       gdbstub_tx_char('#');
+       gdbstub_tx_char(hexchars[checksum >> 4]);
+       gdbstub_tx_char(hexchars[checksum & 0xf]);
+
+       /* make sure the output is flushed, or else RedBoot might clobber it */
+       gdbstub_tx_char('-');
+       gdbstub_tx_flush();
+
+} /* end gdbstub_exit() */
+
+/*****************************************************************************/
+/*
+ * GDB wants to call malloc() and free() to allocate memory for calling kernel
+ * functions directly from its command line
+ */
+static void *malloc(size_t size) __attribute__((unused));
+static void *malloc(size_t size)
+{
+       return kmalloc(size, GFP_ATOMIC);
+}
+
+static void free(void *p) __attribute__((unused));
+static void free(void *p)
+{
+       kfree(p);
+}
+
+static uint32_t ___get_HSR0(void) __attribute__((unused));
+static uint32_t ___get_HSR0(void)
+{
+       return __get_HSR(0);
+}
+
+static uint32_t ___set_HSR0(uint32_t x) __attribute__((unused));
+static uint32_t ___set_HSR0(uint32_t x)
+{
+       __set_HSR(0, x);
+       return __get_HSR(0);
+}
diff --git a/arch/frv/kernel/head-mmu-fr451.S b/arch/frv/kernel/head-mmu-fr451.S
new file mode 100644 (file)
index 0000000..a143c2f
--- /dev/null
@@ -0,0 +1,374 @@
+/* head-mmu-fr451.S: FR451 mmu-linux specific bits of initialisation
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/mem-layout.h>
+#include <asm/spr-regs.h>
+#include <asm/mb86943a.h>
+#include "head.inc"
+
+
+#define __400_DBR0     0xfe000e00
+#define __400_DBR1     0xfe000e08
+#define __400_DBR2     0xfe000e10
+#define __400_DBR3     0xfe000e18
+#define __400_DAM0     0xfe000f00
+#define __400_DAM1     0xfe000f08
+#define __400_DAM2     0xfe000f10
+#define __400_DAM3     0xfe000f18
+#define __400_LGCR     0xfe000010
+#define __400_LCR      0xfe000100
+#define __400_LSBR     0xfe000c00
+
+       .section        .text.init,"ax"
+       .balign         4
+
+###############################################################################
+#
+# describe the position and layout of the SDRAM controller registers
+#
+#      ENTRY:                  EXIT:
+# GR5  -                       cacheline size
+# GR11 -                       displacement of 2nd SDRAM addr reg from GR14
+# GR12 -                       displacement of 3rd SDRAM addr reg from GR14
+# GR13 -                       displacement of 4th SDRAM addr reg from GR14
+# GR14 -                       address of 1st SDRAM addr reg
+# GR15 -                       amount to shift address by to match SDRAM addr reg
+# GR26 &__head_reference       [saved]
+# GR30 LED address             [saved]
+# CC0  -                       T if DBR0 is present
+# CC1  -                       T if DBR1 is present
+# CC2  -                       T if DBR2 is present
+# CC3  -                       T if DBR3 is present
+#
+###############################################################################
+       .globl          __head_fr451_describe_sdram
+__head_fr451_describe_sdram:
+       sethi.p         %hi(__400_DBR0),gr14
+       setlo           %lo(__400_DBR0),gr14
+       setlos.p        #__400_DBR1-__400_DBR0,gr11
+       setlos          #__400_DBR2-__400_DBR0,gr12
+       setlos.p        #__400_DBR3-__400_DBR0,gr13
+       setlos          #32,gr5                 ; cacheline size
+       setlos.p        #0,gr15                 ; amount to shift addr reg by
+       setlos          #0x00ff,gr4
+       movgs           gr4,cccr                ; extant DARS/DAMK regs
+       bralr
+
+###############################################################################
+#
+# rearrange the bus controller registers
+#
+#      ENTRY:                  EXIT:
+# GR26 &__head_reference       [saved]
+# GR30 LED address             revised LED address
+#
+###############################################################################
+       .globl          __head_fr451_set_busctl
+__head_fr451_set_busctl:
+       sethi.p         %hi(__400_LGCR),gr4
+       setlo           %lo(__400_LGCR),gr4
+       sethi.p         %hi(__400_LSBR),gr10
+       setlo           %lo(__400_LSBR),gr10
+       sethi.p         %hi(__400_LCR),gr11
+       setlo           %lo(__400_LCR),gr11
+
+       # set the bus controller
+       ldi             @(gr4,#0),gr5
+       ori             gr5,#0xff,gr5           ; make sure all chip-selects are enabled
+       sti             gr5,@(gr4,#0)
+
+       sethi.p         %hi(__region_CS1),gr4
+       setlo           %lo(__region_CS1),gr4
+       sethi.p         %hi(__region_CS1_M),gr5
+       setlo           %lo(__region_CS1_M),gr5
+       sethi.p         %hi(__region_CS1_C),gr6
+       setlo           %lo(__region_CS1_C),gr6
+       sti             gr4,@(gr10,#1*0x08)
+       sti             gr5,@(gr10,#1*0x08+0x100)
+       sti             gr6,@(gr11,#1*0x08)
+       sethi.p         %hi(__region_CS2),gr4
+       setlo           %lo(__region_CS2),gr4
+       sethi.p         %hi(__region_CS2_M),gr5
+       setlo           %lo(__region_CS2_M),gr5
+       sethi.p         %hi(__region_CS2_C),gr6
+       setlo           %lo(__region_CS2_C),gr6
+       sti             gr4,@(gr10,#2*0x08)
+       sti             gr5,@(gr10,#2*0x08+0x100)
+       sti             gr6,@(gr11,#2*0x08)
+       sethi.p         %hi(__region_CS3),gr4
+       setlo           %lo(__region_CS3),gr4
+       sethi.p         %hi(__region_CS3_M),gr5
+       setlo           %lo(__region_CS3_M),gr5
+       sethi.p         %hi(__region_CS3_C),gr6
+       setlo           %lo(__region_CS3_C),gr6
+       sti             gr4,@(gr10,#3*0x08)
+       sti             gr5,@(gr10,#3*0x08+0x100)
+       sti             gr6,@(gr11,#3*0x08)
+       sethi.p         %hi(__region_CS4),gr4
+       setlo           %lo(__region_CS4),gr4
+       sethi.p         %hi(__region_CS4_M),gr5
+       setlo           %lo(__region_CS4_M),gr5
+       sethi.p         %hi(__region_CS4_C),gr6
+       setlo           %lo(__region_CS4_C),gr6
+       sti             gr4,@(gr10,#4*0x08)
+       sti             gr5,@(gr10,#4*0x08+0x100)
+       sti             gr6,@(gr11,#4*0x08)
+       sethi.p         %hi(__region_CS5),gr4
+       setlo           %lo(__region_CS5),gr4
+       sethi.p         %hi(__region_CS5_M),gr5
+       setlo           %lo(__region_CS5_M),gr5
+       sethi.p         %hi(__region_CS5_C),gr6
+       setlo           %lo(__region_CS5_C),gr6
+       sti             gr4,@(gr10,#5*0x08)
+       sti             gr5,@(gr10,#5*0x08+0x100)
+       sti             gr6,@(gr11,#5*0x08)
+       sethi.p         %hi(__region_CS6),gr4
+       setlo           %lo(__region_CS6),gr4
+       sethi.p         %hi(__region_CS6_M),gr5
+       setlo           %lo(__region_CS6_M),gr5
+       sethi.p         %hi(__region_CS6_C),gr6
+       setlo           %lo(__region_CS6_C),gr6
+       sti             gr4,@(gr10,#6*0x08)
+       sti             gr5,@(gr10,#6*0x08+0x100)
+       sti             gr6,@(gr11,#6*0x08)
+       sethi.p         %hi(__region_CS7),gr4
+       setlo           %lo(__region_CS7),gr4
+       sethi.p         %hi(__region_CS7_M),gr5
+       setlo           %lo(__region_CS7_M),gr5
+       sethi.p         %hi(__region_CS7_C),gr6
+       setlo           %lo(__region_CS7_C),gr6
+       sti             gr4,@(gr10,#7*0x08)
+       sti             gr5,@(gr10,#7*0x08+0x100)
+       sti             gr6,@(gr11,#7*0x08)
+       membar
+       bar
+
+       # adjust LED bank address
+#ifdef CONFIG_MB93091_VDK
+       sethi.p         %hi(__region_CS2 + 0x01200004),gr30
+       setlo           %lo(__region_CS2 + 0x01200004),gr30
+#endif
+       bralr
+
+###############################################################################
+#
+# determine the total SDRAM size
+#
+#      ENTRY:                  EXIT:
+# GR25 -                       SDRAM size
+# GR26 &__head_reference       [saved]
+# GR30 LED address             [saved]
+#
+###############################################################################
+       .globl          __head_fr451_survey_sdram
+__head_fr451_survey_sdram:
+       sethi.p         %hi(__400_DAM0),gr11
+       setlo           %lo(__400_DAM0),gr11
+       sethi.p         %hi(__400_DBR0),gr12
+       setlo           %lo(__400_DBR0),gr12
+
+       sethi.p         %hi(0xfe000000),gr17            ; unused SDRAM DBR value
+       setlo           %lo(0xfe000000),gr17
+       setlos          #0,gr25
+
+       ldi             @(gr12,#0x00),gr4               ; DAR0
+       subcc           gr4,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS0
+       ldi             @(gr11,#0x00),gr6               ; DAM0: bits 31:20 match addr 31:20
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS0:
+
+       ldi             @(gr12,#0x08),gr4               ; DAR1
+       subcc           gr4,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS1
+       ldi             @(gr11,#0x08),gr6               ; DAM1: bits 31:20 match addr 31:20
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS1:
+
+       ldi             @(gr12,#0x10),gr4               ; DAR2
+       subcc           gr4,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS2
+       ldi             @(gr11,#0x10),gr6               ; DAM2: bits 31:20 match addr 31:20
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS2:
+
+       ldi             @(gr12,#0x18),gr4               ; DAR3
+       subcc           gr4,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS3
+       ldi             @(gr11,#0x18),gr6               ; DAM3: bits 31:20 match addr 31:20
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS3:
+       bralr
+
+###############################################################################
+#
+# set the protection map with the I/DAMPR registers
+#
+#      ENTRY:                  EXIT:
+# GR25 SDRAM size              [saved]
+# GR26 &__head_reference       [saved]
+# GR30 LED address             [saved]
+#
+#
+# Using this map:
+#      REGISTERS       ADDRESS RANGE           VIEW
+#      =============== ======================  ===============================
+#      IAMPR0/DAMPR0   0xC0000000-0xCFFFFFFF   Cached kernel RAM Window
+#      DAMPR11         0xE0000000-0xFFFFFFFF   Uncached I/O
+#
+###############################################################################
+       .globl          __head_fr451_set_protection
+__head_fr451_set_protection:
+       movsg           lr,gr27
+
+       # set the I/O region protection registers for FR451 in MMU mode
+#define PGPROT_IO      xAMPRx_L|xAMPRx_M|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V
+
+       sethi.p         %hi(__region_IO),gr5
+       setlo           %lo(__region_IO),gr5
+       setlos          #PGPROT_IO|xAMPRx_SS_512Mb,gr4
+       or              gr4,gr5,gr4
+       movgs           gr5,damlr11                     ; General I/O tile
+       movgs           gr4,dampr11
+
+       # need to open a window onto at least part of the RAM for the kernel's use
+       sethi.p         %hi(__sdram_base),gr8
+       setlo           %lo(__sdram_base),gr8           ; physical address
+       sethi.p         %hi(__page_offset),gr9
+       setlo           %lo(__page_offset),gr9          ; virtual address
+
+       setlos          #xAMPRx_L|xAMPRx_M|xAMPRx_SS_256Mb|xAMPRx_S_KERNEL|xAMPRx_V,gr11
+       or              gr8,gr11,gr8
+
+       movgs           gr9,iamlr0                      ; mapped from real address 0
+       movgs           gr8,iampr0                      ; cached kernel memory at 0xC0000000
+       movgs           gr9,damlr0
+       movgs           gr8,dampr0
+
+       # set a temporary mapping for the kernel running at address 0 until we've turned on the MMU
+       sethi.p         %hi(__sdram_base),gr9
+       setlo           %lo(__sdram_base),gr9           ; virtual address
+
+       and.p           gr4,gr11,gr4
+       and             gr5,gr11,gr5
+       or.p            gr4,gr11,gr4
+       or              gr5,gr11,gr5
+
+       movgs           gr9,iamlr1                      ; mapped from real address 0
+       movgs           gr8,iampr1                      ; cached kernel memory at 0x00000000
+       movgs           gr9,damlr1
+       movgs           gr8,dampr1
+
+       # we use DAMR2-10 for kmap_atomic(), cache flush and TLB management
+       # since the DAMLR regs are not going to change, we can set them now
+       # also set up IAMLR2 to the same as DAMLR5
+       sethi.p         %hi(KMAP_ATOMIC_PRIMARY_FRAME),gr4
+       setlo           %lo(KMAP_ATOMIC_PRIMARY_FRAME),gr4
+       sethi.p         %hi(PAGE_SIZE),gr5
+       setlo           %lo(PAGE_SIZE),gr5
+
+       movgs           gr4,damlr2
+       movgs           gr4,iamlr2
+       add             gr4,gr5,gr4
+       movgs           gr4,damlr3
+       add             gr4,gr5,gr4
+       movgs           gr4,damlr4
+       add             gr4,gr5,gr4
+       movgs           gr4,damlr5
+       add             gr4,gr5,gr4
+       movgs           gr4,damlr6
+       add             gr4,gr5,gr4
+       movgs           gr4,damlr7
+       add             gr4,gr5,gr4
+       movgs           gr4,damlr8
+       add             gr4,gr5,gr4
+       movgs           gr4,damlr9
+       add             gr4,gr5,gr4
+       movgs           gr4,damlr10
+
+       movgs           gr0,dampr2
+       movgs           gr0,dampr4
+       movgs           gr0,dampr5
+       movgs           gr0,dampr6
+       movgs           gr0,dampr7
+       movgs           gr0,dampr8
+       movgs           gr0,dampr9
+       movgs           gr0,dampr10
+
+       movgs           gr0,iamlr3
+       movgs           gr0,iamlr4
+       movgs           gr0,iamlr5
+       movgs           gr0,iamlr6
+       movgs           gr0,iamlr7
+
+       movgs           gr0,iampr2
+       movgs           gr0,iampr3
+       movgs           gr0,iampr4
+       movgs           gr0,iampr5
+       movgs           gr0,iampr6
+       movgs           gr0,iampr7
+
+       # start in TLB context 0 with the swapper's page tables
+       movgs           gr0,cxnr
+
+       sethi.p         %hi(swapper_pg_dir),gr4
+       setlo           %lo(swapper_pg_dir),gr4
+       sethi.p         %hi(__page_offset),gr5
+       setlo           %lo(__page_offset),gr5
+       sub             gr4,gr5,gr4
+       movgs           gr4,ttbr
+       setlos          #xAMPRx_L|xAMPRx_M|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr5
+       or              gr4,gr5,gr4
+       movgs           gr4,dampr3
+
+       # the FR451 also has an extra trap base register
+       movsg           tbr,gr4
+       movgs           gr4,btbr
+
+       LEDS            0x3300
+       jmpl            @(gr27,gr0)
+
+###############################################################################
+#
+# finish setting up the protection registers
+#
+###############################################################################
+       .globl          __head_fr451_finalise_protection
+__head_fr451_finalise_protection:
+       # turn on the timers as appropriate
+       movgs           gr0,timerh
+       movgs           gr0,timerl
+       movgs           gr0,timerd
+       movsg           hsr0,gr4
+       sethi.p         %hi(HSR0_ETMI),gr5
+       setlo           %lo(HSR0_ETMI),gr5
+       or              gr4,gr5,gr4
+       movgs           gr4,hsr0
+
+       # clear the TLB entry cache
+       movgs           gr0,iamlr1
+       movgs           gr0,iampr1
+       movgs           gr0,damlr1
+       movgs           gr0,dampr1
+
+       # clear the PGE cache
+       sethi.p         %hi(__flush_tlb_all),gr4
+       setlo           %lo(__flush_tlb_all),gr4
+       jmpl            @(gr4,gr0)
diff --git a/arch/frv/kernel/head-uc-fr401.S b/arch/frv/kernel/head-uc-fr401.S
new file mode 100644 (file)
index 0000000..4ccf841
--- /dev/null
@@ -0,0 +1,311 @@
+/* head-uc-fr401.S: FR401/3/5 uc-linux specific bits of initialisation
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/spr-regs.h>
+#include <asm/mb86943a.h>
+#include "head.inc"
+
+
+#define __400_DBR0     0xfe000e00
+#define __400_DBR1     0xfe000e08
+#define __400_DBR2     0xfe000e10      /* not on FR401 */
+#define __400_DBR3     0xfe000e18      /* not on FR401 */
+#define __400_DAM0     0xfe000f00
+#define __400_DAM1     0xfe000f08
+#define __400_DAM2     0xfe000f10      /* not on FR401 */
+#define __400_DAM3     0xfe000f18      /* not on FR401 */
+#define __400_LGCR     0xfe000010
+#define __400_LCR      0xfe000100
+#define __400_LSBR     0xfe000c00
+
+       .section        .text.init,"ax"
+       .balign         4
+
+###############################################################################
+#
+# describe the position and layout of the SDRAM controller registers
+#
+#      ENTRY:                  EXIT:
+# GR5  -                       cacheline size
+# GR11 -                       displacement of 2nd SDRAM addr reg from GR14
+# GR12 -                       displacement of 3rd SDRAM addr reg from GR14
+# GR13 -                       displacement of 4th SDRAM addr reg from GR14
+# GR14 -                       address of 1st SDRAM addr reg
+# GR15 -                       amount to shift address by to match SDRAM addr reg
+# GR26 &__head_reference       [saved]
+# GR30 LED address             [saved]
+# CC0  -                       T if DBR0 is present
+# CC1  -                       T if DBR1 is present
+# CC2  -                       T if DBR2 is present (not FR401/FR401A)
+# CC3  -                       T if DBR3 is present (not FR401/FR401A)
+#
+###############################################################################
+       .globl          __head_fr401_describe_sdram
+__head_fr401_describe_sdram:
+       sethi.p         %hi(__400_DBR0),gr14
+       setlo           %lo(__400_DBR0),gr14
+       setlos.p        #__400_DBR1-__400_DBR0,gr11
+       setlos          #__400_DBR2-__400_DBR0,gr12
+       setlos.p        #__400_DBR3-__400_DBR0,gr13
+       setlos          #32,gr5                 ; cacheline size
+       setlos.p        #0,gr15                 ; amount to shift addr reg by
+
+       # specify which DBR regs are present
+       setlos          #0x00ff,gr4
+       movgs           gr4,cccr
+       movsg           psr,gr3                 ; check for FR401/FR401A
+       srli            gr3,#25,gr3
+       subicc          gr3,#0x20>>1,gr0,icc0
+       bnelr           icc0,#1
+       setlos          #0x000f,gr4
+       movgs           gr4,cccr
+       bralr
+
+###############################################################################
+#
+# rearrange the bus controller registers
+#
+#      ENTRY:                  EXIT:
+# GR26 &__head_reference       [saved]
+# GR30 LED address             revised LED address
+#
+###############################################################################
+       .globl          __head_fr401_set_busctl
+__head_fr401_set_busctl:
+       sethi.p         %hi(__400_LGCR),gr4
+       setlo           %lo(__400_LGCR),gr4
+       sethi.p         %hi(__400_LSBR),gr10
+       setlo           %lo(__400_LSBR),gr10
+       sethi.p         %hi(__400_LCR),gr11
+       setlo           %lo(__400_LCR),gr11
+
+       # set the bus controller
+       ldi             @(gr4,#0),gr5
+       ori             gr5,#0xff,gr5           ; make sure all chip-selects are enabled
+       sti             gr5,@(gr4,#0)
+
+       sethi.p         %hi(__region_CS1),gr4
+       setlo           %lo(__region_CS1),gr4
+       sethi.p         %hi(__region_CS1_M),gr5
+       setlo           %lo(__region_CS1_M),gr5
+       sethi.p         %hi(__region_CS1_C),gr6
+       setlo           %lo(__region_CS1_C),gr6
+       sti             gr4,@(gr10,#1*0x08)
+       sti             gr5,@(gr10,#1*0x08+0x100)
+       sti             gr6,@(gr11,#1*0x08)
+       sethi.p         %hi(__region_CS2),gr4
+       setlo           %lo(__region_CS2),gr4
+       sethi.p         %hi(__region_CS2_M),gr5
+       setlo           %lo(__region_CS2_M),gr5
+       sethi.p         %hi(__region_CS2_C),gr6
+       setlo           %lo(__region_CS2_C),gr6
+       sti             gr4,@(gr10,#2*0x08)
+       sti             gr5,@(gr10,#2*0x08+0x100)
+       sti             gr6,@(gr11,#2*0x08)
+       sethi.p         %hi(__region_CS3),gr4
+       setlo           %lo(__region_CS3),gr4
+       sethi.p         %hi(__region_CS3_M),gr5
+       setlo           %lo(__region_CS3_M),gr5
+       sethi.p         %hi(__region_CS3_C),gr6
+       setlo           %lo(__region_CS3_C),gr6
+       sti             gr4,@(gr10,#3*0x08)
+       sti             gr5,@(gr10,#3*0x08+0x100)
+       sti             gr6,@(gr11,#3*0x08)
+       sethi.p         %hi(__region_CS4),gr4
+       setlo           %lo(__region_CS4),gr4
+       sethi.p         %hi(__region_CS4_M),gr5
+       setlo           %lo(__region_CS4_M),gr5
+       sethi.p         %hi(__region_CS4_C),gr6
+       setlo           %lo(__region_CS4_C),gr6
+       sti             gr4,@(gr10,#4*0x08)
+       sti             gr5,@(gr10,#4*0x08+0x100)
+       sti             gr6,@(gr11,#4*0x08)
+       sethi.p         %hi(__region_CS5),gr4
+       setlo           %lo(__region_CS5),gr4
+       sethi.p         %hi(__region_CS5_M),gr5
+       setlo           %lo(__region_CS5_M),gr5
+       sethi.p         %hi(__region_CS5_C),gr6
+       setlo           %lo(__region_CS5_C),gr6
+       sti             gr4,@(gr10,#5*0x08)
+       sti             gr5,@(gr10,#5*0x08+0x100)
+       sti             gr6,@(gr11,#5*0x08)
+       sethi.p         %hi(__region_CS6),gr4
+       setlo           %lo(__region_CS6),gr4
+       sethi.p         %hi(__region_CS6_M),gr5
+       setlo           %lo(__region_CS6_M),gr5
+       sethi.p         %hi(__region_CS6_C),gr6
+       setlo           %lo(__region_CS6_C),gr6
+       sti             gr4,@(gr10,#6*0x08)
+       sti             gr5,@(gr10,#6*0x08+0x100)
+       sti             gr6,@(gr11,#6*0x08)
+       sethi.p         %hi(__region_CS7),gr4
+       setlo           %lo(__region_CS7),gr4
+       sethi.p         %hi(__region_CS7_M),gr5
+       setlo           %lo(__region_CS7_M),gr5
+       sethi.p         %hi(__region_CS7_C),gr6
+       setlo           %lo(__region_CS7_C),gr6
+       sti             gr4,@(gr10,#7*0x08)
+       sti             gr5,@(gr10,#7*0x08+0x100)
+       sti             gr6,@(gr11,#7*0x08)
+       membar
+       bar
+
+       # adjust LED bank address
+       sethi.p         %hi(LED_ADDR - 0x20000000 +__region_CS2),gr30
+       setlo           %lo(LED_ADDR - 0x20000000 +__region_CS2),gr30
+       bralr
+
+###############################################################################
+#
+# determine the total SDRAM size
+#
+#      ENTRY:                  EXIT:
+# GR25 -                       SDRAM size
+# GR26 &__head_reference       [saved]
+# GR30 LED address             [saved]
+#
+###############################################################################
+       .globl          __head_fr401_survey_sdram
+__head_fr401_survey_sdram:
+       sethi.p         %hi(__400_DAM0),gr11
+       setlo           %lo(__400_DAM0),gr11
+       sethi.p         %hi(__400_DBR0),gr12
+       setlo           %lo(__400_DBR0),gr12
+
+       sethi.p         %hi(0xfe000000),gr17            ; unused SDRAM DBR value
+       setlo           %lo(0xfe000000),gr17
+       setlos          #0,gr25
+
+       ldi             @(gr12,#0x00),gr4               ; DAR0
+       subcc           gr4,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS0
+       ldi             @(gr11,#0x00),gr6               ; DAM0: bits 31:20 match addr 31:20
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS0:
+
+       ldi             @(gr12,#0x08),gr4               ; DAR1
+       subcc           gr4,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS1
+       ldi             @(gr11,#0x08),gr6               ; DAM1: bits 31:20 match addr 31:20
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS1:
+
+       # FR401/FR401A does not have DCS2/3
+       movsg           psr,gr3
+       srli            gr3,#25,gr3
+       subicc          gr3,#0x20>>1,gr0,icc0
+       beq             icc0,#0,__head_no_DCS3
+
+       ldi             @(gr12,#0x10),gr4               ; DAR2
+       subcc           gr4,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS2
+       ldi             @(gr11,#0x10),gr6               ; DAM2: bits 31:20 match addr 31:20
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS2:
+
+       ldi             @(gr12,#0x18),gr4               ; DAR3
+       subcc           gr4,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS3
+       ldi             @(gr11,#0x18),gr6               ; DAM3: bits 31:20 match addr 31:20
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS3:
+       bralr
+
+###############################################################################
+#
+# set the protection map with the I/DAMPR registers
+#
+#      ENTRY:                  EXIT:
+# GR25 SDRAM size              [saved]
+# GR26 &__head_reference       [saved]
+# GR30 LED address             [saved]
+#
+###############################################################################
+       .globl          __head_fr401_set_protection
+__head_fr401_set_protection:
+       movsg           lr,gr27
+
+       # set the I/O region protection registers for FR401/3/5
+       sethi.p         %hi(__region_IO),gr5
+       setlo           %lo(__region_IO),gr5
+       ori             gr5,#xAMPRx_SS_512Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr5
+       movgs           gr0,iampr7
+       movgs           gr5,dampr7                      ; General I/O tile
+
+       # need to tile the remaining IAMPR/DAMPR registers to cover as much of the RAM as possible
+       # - start with the highest numbered registers
+       sethi.p         %hi(__kernel_image_end),gr8
+       setlo           %lo(__kernel_image_end),gr8
+       sethi.p         %hi(32768),gr4                  ; allow for a maximal allocator bitmap
+       setlo           %lo(32768),gr4
+       add             gr8,gr4,gr8
+       sethi.p         %hi(1024*2048-1),gr4            ; round up to nearest 2MiB
+       setlo           %lo(1024*2048-1),gr4
+       add.p           gr8,gr4,gr8
+       not             gr4,gr4
+       and             gr8,gr4,gr8
+
+       sethi.p         %hi(__page_offset),gr9
+       setlo           %lo(__page_offset),gr9
+       add             gr9,gr25,gr9
+
+       # GR8 = base of uncovered RAM
+       # GR9 = top of uncovered RAM
+
+#ifdef CONFIG_MB93093_PDK
+       sethi.p         %hi(__region_CS2),gr4
+       setlo           %lo(__region_CS2),gr4
+       ori             gr4,#xAMPRx_SS_1Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr4
+       movgs           gr4,dampr6
+       movgs           gr0,iampr6
+#else
+       call            __head_split_region
+       movgs           gr4,iampr6
+       movgs           gr5,dampr6
+#endif
+       call            __head_split_region
+       movgs           gr4,iampr5
+       movgs           gr5,dampr5
+       call            __head_split_region
+       movgs           gr4,iampr4
+       movgs           gr5,dampr4
+       call            __head_split_region
+       movgs           gr4,iampr3
+       movgs           gr5,dampr3
+       call            __head_split_region
+       movgs           gr4,iampr2
+       movgs           gr5,dampr2
+       call            __head_split_region
+       movgs           gr4,iampr1
+       movgs           gr5,dampr1
+
+       # cover kernel core image with kernel-only segment
+       sethi.p         %hi(__page_offset),gr8
+       setlo           %lo(__page_offset),gr8
+       call            __head_split_region
+
+#ifdef CONFIG_PROTECT_KERNEL
+       ori.p           gr4,#xAMPRx_S_KERNEL,gr4
+       ori             gr5,#xAMPRx_S_KERNEL,gr5
+#endif
+
+       movgs           gr4,iampr0
+       movgs           gr5,dampr0
+       jmpl            @(gr27,gr0)
diff --git a/arch/frv/kernel/head-uc-fr451.S b/arch/frv/kernel/head-uc-fr451.S
new file mode 100644 (file)
index 0000000..31cb54a
--- /dev/null
@@ -0,0 +1,174 @@
+/* head-uc-fr451.S: FR451 uc-linux specific bits of initialisation
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/spr-regs.h>
+#include <asm/mb86943a.h>
+#include "head.inc"
+
+
+#define __400_DBR0     0xfe000e00
+#define __400_DBR1     0xfe000e08
+#define __400_DBR2     0xfe000e10
+#define __400_DBR3     0xfe000e18
+#define __400_DAM0     0xfe000f00
+#define __400_DAM1     0xfe000f08
+#define __400_DAM2     0xfe000f10
+#define __400_DAM3     0xfe000f18
+#define __400_LGCR     0xfe000010
+#define __400_LCR      0xfe000100
+#define __400_LSBR     0xfe000c00
+
+       .section        .text.init,"ax"
+       .balign         4
+
+###############################################################################
+#
+# set the protection map with the I/DAMPR registers
+#
+#      ENTRY:                  EXIT:
+# GR25 SDRAM size              [saved]
+# GR26 &__head_reference       [saved]
+# GR30 LED address             [saved]
+#
+###############################################################################
+       .globl          __head_fr451_set_protection
+__head_fr451_set_protection:
+       movsg           lr,gr27
+
+       movgs           gr0,dampr10
+       movgs           gr0,damlr10
+       movgs           gr0,dampr9
+       movgs           gr0,damlr9
+       movgs           gr0,dampr8
+       movgs           gr0,damlr8
+
+       # set the I/O region protection registers for FR401/3/5
+       sethi.p         %hi(__region_IO),gr5
+       setlo           %lo(__region_IO),gr5
+       sethi.p         %hi(0x1fffffff),gr7
+       setlo           %lo(0x1fffffff),gr7
+       ori             gr5,#xAMPRx_SS_512Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr5
+       movgs           gr5,dampr11                     ; General I/O tile
+       movgs           gr7,damlr11
+
+       # need to tile the remaining IAMPR/DAMPR registers to cover as much of the RAM as possible
+       # - start with the highest numbered registers
+       sethi.p         %hi(__kernel_image_end),gr8
+       setlo           %lo(__kernel_image_end),gr8
+       sethi.p         %hi(32768),gr4                  ; allow for a maximal allocator bitmap
+       setlo           %lo(32768),gr4
+       add             gr8,gr4,gr8
+       sethi.p         %hi(1024*2048-1),gr4            ; round up to nearest 2MiB
+       setlo           %lo(1024*2048-1),gr4
+       add.p           gr8,gr4,gr8
+       not             gr4,gr4
+       and             gr8,gr4,gr8
+
+       sethi.p         %hi(__page_offset),gr9
+       setlo           %lo(__page_offset),gr9
+       add             gr9,gr25,gr9
+
+       sethi.p         %hi(0xffffc000),gr11
+       setlo           %lo(0xffffc000),gr11
+
+       # GR8 = base of uncovered RAM
+       # GR9 = top of uncovered RAM
+       # GR11 = xAMLR mask
+       LEDS            0x3317
+       call            __head_split_region
+       movgs           gr4,iampr7
+       movgs           gr6,iamlr7
+       movgs           gr5,dampr7
+       movgs           gr7,damlr7
+
+       LEDS            0x3316
+       call            __head_split_region
+       movgs           gr4,iampr6
+       movgs           gr6,iamlr6
+       movgs           gr5,dampr6
+       movgs           gr7,damlr6
+
+       LEDS            0x3315
+       call            __head_split_region
+       movgs           gr4,iampr5
+       movgs           gr6,iamlr5
+       movgs           gr5,dampr5
+       movgs           gr7,damlr5
+
+       LEDS            0x3314
+       call            __head_split_region
+       movgs           gr4,iampr4
+       movgs           gr6,iamlr4
+       movgs           gr5,dampr4
+       movgs           gr7,damlr4
+
+       LEDS            0x3313
+       call            __head_split_region
+       movgs           gr4,iampr3
+       movgs           gr6,iamlr3
+       movgs           gr5,dampr3
+       movgs           gr7,damlr3
+
+       LEDS            0x3312
+       call            __head_split_region
+       movgs           gr4,iampr2
+       movgs           gr6,iamlr2
+       movgs           gr5,dampr2
+       movgs           gr7,damlr2
+
+       LEDS            0x3311
+       call            __head_split_region
+       movgs           gr4,iampr1
+       movgs           gr6,iamlr1
+       movgs           gr5,dampr1
+       movgs           gr7,damlr1
+
+       # cover kernel core image with kernel-only segment
+       LEDS            0x3310
+       sethi.p         %hi(__page_offset),gr8
+       setlo           %lo(__page_offset),gr8
+       call            __head_split_region
+
+#ifdef CONFIG_PROTECT_KERNEL
+       ori.p           gr4,#xAMPRx_S_KERNEL,gr4
+       ori             gr5,#xAMPRx_S_KERNEL,gr5
+#endif
+
+       movgs           gr4,iampr0
+       movgs           gr6,iamlr0
+       movgs           gr5,dampr0
+       movgs           gr7,damlr0
+
+       # start in TLB context 0 with no page tables
+       movgs           gr0,cxnr
+       movgs           gr0,ttbr
+
+       # the FR451 also has an extra trap base register
+       movsg           tbr,gr4
+       movgs           gr4,btbr
+
+       # turn on the timers as appropriate
+       movgs           gr0,timerh
+       movgs           gr0,timerl
+       movgs           gr0,timerd
+       movsg           hsr0,gr4
+       sethi.p         %hi(HSR0_ETMI),gr5
+       setlo           %lo(HSR0_ETMI),gr5
+       or              gr4,gr5,gr4
+       movgs           gr4,hsr0
+
+       LEDS            0x3300
+       jmpl            @(gr27,gr0)
diff --git a/arch/frv/kernel/head-uc-fr555.S b/arch/frv/kernel/head-uc-fr555.S
new file mode 100644 (file)
index 0000000..d088db2
--- /dev/null
@@ -0,0 +1,347 @@
+/* head-uc-fr555.S: FR555 uc-linux specific bits of initialisation
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/spr-regs.h>
+#include <asm/mb86943a.h>
+#include "head.inc"
+
+
+#define __551_DARS0    0xfeff0100
+#define __551_DARS1    0xfeff0104
+#define __551_DARS2    0xfeff0108
+#define __551_DARS3    0xfeff010c
+#define __551_DAMK0    0xfeff0110
+#define __551_DAMK1    0xfeff0114
+#define __551_DAMK2    0xfeff0118
+#define __551_DAMK3    0xfeff011c
+#define __551_LCR      0xfeff1100
+#define __551_LSBR     0xfeff1c00
+
+       .section        .text.init,"ax"
+       .balign         4
+
+###############################################################################
+#
+# describe the position and layout of the SDRAM controller registers
+#
+#      ENTRY:                  EXIT:
+# GR5  -                       cacheline size
+# GR11 -                       displacement of 2nd SDRAM addr reg from GR14
+# GR12 -                       displacement of 3rd SDRAM addr reg from GR14
+# GR13 -                       displacement of 4th SDRAM addr reg from GR14
+# GR14 -                       address of 1st SDRAM addr reg
+# GR15 -                       amount to shift address by to match SDRAM addr reg
+# GR26 &__head_reference       [saved]
+# GR30 LED address             [saved]
+# CC0  -                       T if DARS0 is present
+# CC1  -                       T if DARS1 is present
+# CC2  -                       T if DARS2 is present
+# CC3  -                       T if DARS3 is present
+#
+###############################################################################
+       .globl          __head_fr555_describe_sdram
+__head_fr555_describe_sdram:
+       sethi.p         %hi(__551_DARS0),gr14
+       setlo           %lo(__551_DARS0),gr14
+       setlos.p        #__551_DARS1-__551_DARS0,gr11
+       setlos          #__551_DARS2-__551_DARS0,gr12
+       setlos.p        #__551_DARS3-__551_DARS0,gr13
+       setlos          #64,gr5                 ; cacheline size
+       setlos          #20,gr15                ; amount to shift addr by
+       setlos          #0x00ff,gr4
+       movgs           gr4,cccr                ; extant DARS/DAMK regs
+       bralr
+
+###############################################################################
+#
+# rearrange the bus controller registers
+#
+#      ENTRY:                  EXIT:
+# GR26 &__head_reference       [saved]
+# GR30 LED address             revised LED address
+#
+###############################################################################
+       .globl          __head_fr555_set_busctl
+__head_fr555_set_busctl:
+       LEDS            0x100f
+       sethi.p         %hi(__551_LSBR),gr10
+       setlo           %lo(__551_LSBR),gr10
+       sethi.p         %hi(__551_LCR),gr11
+       setlo           %lo(__551_LCR),gr11
+
+       # set the bus controller
+       sethi.p         %hi(__region_CS1),gr4
+       setlo           %lo(__region_CS1),gr4
+       sethi.p         %hi(__region_CS1_M),gr5
+       setlo           %lo(__region_CS1_M),gr5
+       sethi.p         %hi(__region_CS1_C),gr6
+       setlo           %lo(__region_CS1_C),gr6
+       sti             gr4,@(gr10,#1*0x08)
+       sti             gr5,@(gr10,#1*0x08+0x100)
+       sti             gr6,@(gr11,#1*0x08)
+       sethi.p         %hi(__region_CS2),gr4
+       setlo           %lo(__region_CS2),gr4
+       sethi.p         %hi(__region_CS2_M),gr5
+       setlo           %lo(__region_CS2_M),gr5
+       sethi.p         %hi(__region_CS2_C),gr6
+       setlo           %lo(__region_CS2_C),gr6
+       sti             gr4,@(gr10,#2*0x08)
+       sti             gr5,@(gr10,#2*0x08+0x100)
+       sti             gr6,@(gr11,#2*0x08)
+       sethi.p         %hi(__region_CS3),gr4
+       setlo           %lo(__region_CS3),gr4
+       sethi.p         %hi(__region_CS3_M),gr5
+       setlo           %lo(__region_CS3_M),gr5
+       sethi.p         %hi(__region_CS3_C),gr6
+       setlo           %lo(__region_CS3_C),gr6
+       sti             gr4,@(gr10,#3*0x08)
+       sti             gr5,@(gr10,#3*0x08+0x100)
+       sti             gr6,@(gr11,#3*0x08)
+       sethi.p         %hi(__region_CS4),gr4
+       setlo           %lo(__region_CS4),gr4
+       sethi.p         %hi(__region_CS4_M),gr5
+       setlo           %lo(__region_CS4_M),gr5
+       sethi.p         %hi(__region_CS4_C),gr6
+       setlo           %lo(__region_CS4_C),gr6
+       sti             gr4,@(gr10,#4*0x08)
+       sti             gr5,@(gr10,#4*0x08+0x100)
+       sti             gr6,@(gr11,#4*0x08)
+       sethi.p         %hi(__region_CS5),gr4
+       setlo           %lo(__region_CS5),gr4
+       sethi.p         %hi(__region_CS5_M),gr5
+       setlo           %lo(__region_CS5_M),gr5
+       sethi.p         %hi(__region_CS5_C),gr6
+       setlo           %lo(__region_CS5_C),gr6
+       sti             gr4,@(gr10,#5*0x08)
+       sti             gr5,@(gr10,#5*0x08+0x100)
+       sti             gr6,@(gr11,#5*0x08)
+       sethi.p         %hi(__region_CS6),gr4
+       setlo           %lo(__region_CS6),gr4
+       sethi.p         %hi(__region_CS6_M),gr5
+       setlo           %lo(__region_CS6_M),gr5
+       sethi.p         %hi(__region_CS6_C),gr6
+       setlo           %lo(__region_CS6_C),gr6
+       sti             gr4,@(gr10,#6*0x08)
+       sti             gr5,@(gr10,#6*0x08+0x100)
+       sti             gr6,@(gr11,#6*0x08)
+       sethi.p         %hi(__region_CS7),gr4
+       setlo           %lo(__region_CS7),gr4
+       sethi.p         %hi(__region_CS7_M),gr5
+       setlo           %lo(__region_CS7_M),gr5
+       sethi.p         %hi(__region_CS7_C),gr6
+       setlo           %lo(__region_CS7_C),gr6
+       sti             gr4,@(gr10,#7*0x08)
+       sti             gr5,@(gr10,#7*0x08+0x100)
+       sti             gr6,@(gr11,#7*0x08)
+       membar
+       bar
+
+       # adjust LED bank address
+#ifdef CONFIG_MB93091_VDK
+       sethi.p         %hi(LED_ADDR - 0x20000000 +__region_CS2),gr30
+       setlo           %lo(LED_ADDR - 0x20000000 +__region_CS2),gr30
+#endif
+       bralr
+
+###############################################################################
+#
+# determine the total SDRAM size
+#
+#      ENTRY:                  EXIT:
+# GR25 -                       SDRAM size
+# GR26 &__head_reference       [saved]
+# GR30 LED address             [saved]
+#
+###############################################################################
+       .globl          __head_fr555_survey_sdram
+__head_fr555_survey_sdram:
+       sethi.p         %hi(__551_DAMK0),gr11
+       setlo           %lo(__551_DAMK0),gr11
+       sethi.p         %hi(__551_DARS0),gr12
+       setlo           %lo(__551_DARS0),gr12
+
+       sethi.p         %hi(0xfff),gr17                 ; unused SDRAM AMK value
+       setlo           %lo(0xfff),gr17
+       setlos          #0,gr25
+
+       ldi             @(gr11,#0x00),gr6               ; DAMK0: bits 11:0 match addr 11:0
+       subcc           gr6,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS0
+       ldi             @(gr12,#0x00),gr4               ; DARS0
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS0:
+
+       ldi             @(gr11,#0x04),gr6               ; DAMK1: bits 11:0 match addr 11:0
+       subcc           gr6,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS1
+       ldi             @(gr12,#0x04),gr4               ; DARS1
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS1:
+
+       ldi             @(gr11,#0x8),gr6                ; DAMK2: bits 11:0 match addr 11:0
+       subcc           gr6,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS2
+       ldi             @(gr12,#0x8),gr4                ; DARS2
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS2:
+
+       ldi             @(gr11,#0xc),gr6                ; DAMK3: bits 11:0 match addr 11:0
+       subcc           gr6,gr17,gr0,icc0
+       beq             icc0,#0,__head_no_DCS3
+       ldi             @(gr12,#0xc),gr4                ; DARS3
+       add             gr25,gr6,gr25
+       addi            gr25,#1,gr25
+__head_no_DCS3:
+
+       slli            gr25,#20,gr25                   ; shift [11:0] -> [31:20]
+       bralr
+
+###############################################################################
+#
+# set the protection map with the I/DAMPR registers
+#
+#      ENTRY:                  EXIT:
+# GR25 SDRAM size              saved
+# GR30 LED address             saved
+#
+###############################################################################
+       .globl          __head_fr555_set_protection
+__head_fr555_set_protection:
+       movsg           lr,gr27
+
+       sethi.p         %hi(0xfff00000),gr11
+       setlo           %lo(0xfff00000),gr11
+
+       # set the I/O region protection registers for FR555
+       sethi.p         %hi(__region_IO),gr7
+       setlo           %lo(__region_IO),gr7
+       ori             gr7,#xAMPRx_SS_512Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr5
+       movgs           gr0,iampr15
+       movgs           gr0,iamlr15
+       movgs           gr5,dampr15
+       movgs           gr7,damlr15
+
+       # need to tile the remaining IAMPR/DAMPR registers to cover as much of the RAM as possible
+       # - start with the highest numbered registers
+       sethi.p         %hi(__kernel_image_end),gr8
+       setlo           %lo(__kernel_image_end),gr8
+       sethi.p         %hi(32768),gr4                  ; allow for a maximal allocator bitmap
+       setlo           %lo(32768),gr4
+       add             gr8,gr4,gr8
+       sethi.p         %hi(1024*2048-1),gr4            ; round up to nearest 2MiB
+       setlo           %lo(1024*2048-1),gr4
+       add.p           gr8,gr4,gr8
+       not             gr4,gr4
+       and             gr8,gr4,gr8
+
+       sethi.p         %hi(__page_offset),gr9
+       setlo           %lo(__page_offset),gr9
+       add             gr9,gr25,gr9
+
+       # GR8 = base of uncovered RAM
+       # GR9 = top of uncovered RAM
+       # GR11 - mask for DAMLR/IAMLR regs
+       #
+       call            __head_split_region
+       movgs           gr4,iampr14
+       movgs           gr6,iamlr14
+       movgs           gr5,dampr14
+       movgs           gr7,damlr14
+       call            __head_split_region
+       movgs           gr4,iampr13
+       movgs           gr6,iamlr13
+       movgs           gr5,dampr13
+       movgs           gr7,damlr13
+       call            __head_split_region
+       movgs           gr4,iampr12
+       movgs           gr6,iamlr12
+       movgs           gr5,dampr12
+       movgs           gr7,damlr12
+       call            __head_split_region
+       movgs           gr4,iampr11
+       movgs           gr6,iamlr11
+       movgs           gr5,dampr11
+       movgs           gr7,damlr11
+       call            __head_split_region
+       movgs           gr4,iampr10
+       movgs           gr6,iamlr10
+       movgs           gr5,dampr10
+       movgs           gr7,damlr10
+       call            __head_split_region
+       movgs           gr4,iampr9
+       movgs           gr6,iamlr9
+       movgs           gr5,dampr9
+       movgs           gr7,damlr9
+       call            __head_split_region
+       movgs           gr4,iampr8
+       movgs           gr6,iamlr8
+       movgs           gr5,dampr8
+       movgs           gr7,damlr8
+
+       call            __head_split_region
+       movgs           gr4,iampr7
+       movgs           gr6,iamlr7
+       movgs           gr5,dampr7
+       movgs           gr7,damlr7
+       call            __head_split_region
+       movgs           gr4,iampr6
+       movgs           gr6,iamlr6
+       movgs           gr5,dampr6
+       movgs           gr7,damlr6
+       call            __head_split_region
+       movgs           gr4,iampr5
+       movgs           gr6,iamlr5
+       movgs           gr5,dampr5
+       movgs           gr7,damlr5
+       call            __head_split_region
+       movgs           gr4,iampr4
+       movgs           gr6,iamlr4
+       movgs           gr5,dampr4
+       movgs           gr7,damlr4
+       call            __head_split_region
+       movgs           gr4,iampr3
+       movgs           gr6,iamlr3
+       movgs           gr5,dampr3
+       movgs           gr7,damlr3
+       call            __head_split_region
+       movgs           gr4,iampr2
+       movgs           gr6,iamlr2
+       movgs           gr5,dampr2
+       movgs           gr7,damlr2
+       call            __head_split_region
+       movgs           gr4,iampr1
+       movgs           gr6,iamlr1
+       movgs           gr5,dampr1
+       movgs           gr7,damlr1
+
+       # cover kernel core image with kernel-only segment
+       sethi.p         %hi(__page_offset),gr8
+       setlo           %lo(__page_offset),gr8
+       call            __head_split_region
+
+#ifdef CONFIG_PROTECT_KERNEL
+       ori.p           gr4,#xAMPRx_S_KERNEL,gr4
+       ori             gr5,#xAMPRx_S_KERNEL,gr5
+#endif
+
+       movgs           gr4,iampr0
+       movgs           gr6,iamlr0
+       movgs           gr5,dampr0
+       movgs           gr7,damlr0
+       jmpl            @(gr27,gr0)
diff --git a/arch/frv/kernel/head.S b/arch/frv/kernel/head.S
new file mode 100644 (file)
index 0000000..c73b4fe
--- /dev/null
@@ -0,0 +1,639 @@
+/* head.S: kernel entry point for FR-V kernel
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/spr-regs.h>
+#include <asm/mb86943a.h>
+#include <asm/cache.h>
+#include "head.inc"
+
+###############################################################################
+#
+# void _boot(unsigned long magic, char *command_line) __attribute__((noreturn))
+#
+# - if magic is 0xdead1eaf, then command_line is assumed to point to the kernel
+#   command line string
+#
+###############################################################################
+       .section        .text.head,"ax"
+       .balign         4
+
+       .globl          _boot, __head_reference
+        .type          _boot,@function
+_boot:
+__head_reference:
+       sethi.p         %hi(LED_ADDR),gr30
+       setlo           %lo(LED_ADDR),gr30
+
+       LEDS            0x0000
+
+       # calculate reference address for PC-relative stuff
+       call            0f
+0:     movsg           lr,gr26
+       addi            gr26,#__head_reference-0b,gr26
+
+       # invalidate and disable both of the caches and turn off the memory access checking
+       dcef            @(gr0,gr0),1
+       bar
+
+       sethi.p         %hi(~(HSR0_ICE|HSR0_DCE|HSR0_CBM|HSR0_EIMMU|HSR0_EDMMU)),gr4
+       setlo           %lo(~(HSR0_ICE|HSR0_DCE|HSR0_CBM|HSR0_EIMMU|HSR0_EDMMU)),gr4
+       movsg           hsr0,gr5
+       and             gr4,gr5,gr5
+       movgs           gr5,hsr0
+       movsg           hsr0,gr5
+
+       LEDS            0x0001
+
+       icei            @(gr0,gr0),1
+       dcei            @(gr0,gr0),1
+       bar
+
+       # turn the instruction cache back on
+       sethi.p         %hi(HSR0_ICE),gr4
+       setlo           %lo(HSR0_ICE),gr4
+       movsg           hsr0,gr5
+       or              gr4,gr5,gr5
+       movgs           gr5,hsr0
+       movsg           hsr0,gr5
+
+       bar
+
+       LEDS            0x0002
+
+       # retrieve the parameters (including command line) before we overwrite them
+       sethi.p         %hi(0xdead1eaf),gr7
+       setlo           %lo(0xdead1eaf),gr7
+       subcc           gr7,gr8,gr0,icc0
+       bne             icc0,#0,__head_no_parameters
+
+       sethi.p         %hi(redboot_command_line-1),gr6
+       setlo           %lo(redboot_command_line-1),gr6
+       sethi.p         %hi(__head_reference),gr4
+       setlo           %lo(__head_reference),gr4
+       sub             gr6,gr4,gr6
+       add.p           gr6,gr26,gr6
+       subi            gr9,#1,gr9
+       setlos.p        #511,gr4
+       setlos          #1,gr5
+
+__head_copy_cmdline:
+       ldubu.p         @(gr9,gr5),gr16
+       subicc          gr4,#1,gr4,icc0
+       stbu.p          gr16,@(gr6,gr5)
+       subicc          gr16,#0,gr0,icc1
+       bls             icc0,#0,__head_end_cmdline
+       bne             icc1,#1,__head_copy_cmdline
+__head_end_cmdline:
+       stbu            gr0,@(gr6,gr5)
+__head_no_parameters:
+
+###############################################################################
+#
+# we need to relocate the SDRAM to 0x00000000 (linux) or 0xC0000000 (uClinux)
+# - note that we're going to have to run entirely out of the icache whilst
+#   fiddling with the SDRAM controller registers
+#
+###############################################################################
+#ifdef CONFIG_MMU
+       call            __head_fr451_describe_sdram
+
+#else
+       movsg           psr,gr5
+       srli            gr5,#28,gr5
+       subicc          gr5,#3,gr0,icc0
+       beq             icc0,#0,__head_fr551_sdram
+
+       call            __head_fr401_describe_sdram
+       bra             __head_do_sdram
+
+__head_fr551_sdram:
+       call            __head_fr555_describe_sdram
+       LEDS            0x000d
+
+__head_do_sdram:
+#endif
+
+       # preload the registers with invalid values in case any DBR/DARS are marked not present
+       sethi.p         %hi(0xfe000000),gr17            ; unused SDRAM DBR value
+       setlo           %lo(0xfe000000),gr17
+       or.p            gr17,gr0,gr20
+       or              gr17,gr0,gr21
+       or.p            gr17,gr0,gr22
+       or              gr17,gr0,gr23
+
+       # consult the SDRAM controller CS address registers
+       cld             @(gr14,gr0 ),gr20,      cc0,#1  ; DBR0 / DARS0
+       cld             @(gr14,gr11),gr21,      cc1,#1  ; DBR1 / DARS1
+       cld             @(gr14,gr12),gr22,      cc2,#1  ; DBR2 / DARS2
+       cld.p           @(gr14,gr13),gr23,      cc3,#1  ; DBR3 / DARS3
+
+       sll             gr20,gr15,gr20                  ; shift values up for FR551
+       sll             gr21,gr15,gr21
+       sll             gr22,gr15,gr22
+       sll             gr23,gr15,gr23
+
+       LEDS            0x0003
+
+       # assume the lowest valid CS line to be the SDRAM base and get its address
+       subcc           gr20,gr17,gr0,icc0
+       subcc.p         gr21,gr17,gr0,icc1
+       subcc           gr22,gr17,gr0,icc2
+       subcc.p         gr23,gr17,gr0,icc3
+       ckne            icc0,cc4                        ; T if DBR0 != 0xfe000000
+       ckne            icc1,cc5
+       ckne            icc2,cc6
+       ckne            icc3,cc7
+       cor             gr23,gr0,gr24,          cc7,#1  ; GR24 = SDRAM base
+       cor             gr22,gr0,gr24,          cc6,#1
+       cor             gr21,gr0,gr24,          cc5,#1
+       cor             gr20,gr0,gr24,          cc4,#1
+
+       # calculate the displacement required to get the SDRAM into the right place in memory
+       sethi.p         %hi(__sdram_base),gr16
+       setlo           %lo(__sdram_base),gr16
+       sub             gr16,gr24,gr16                  ; delta = __sdram_base - DBRx
+
+       # calculate the new values to go in the controller regs
+       cadd.p          gr20,gr16,gr20,         cc4,#1  ; DCS#0 (new) = DCS#0 (old) + delta
+       cadd            gr21,gr16,gr21,         cc5,#1
+       cadd.p          gr22,gr16,gr22,         cc6,#1
+       cadd            gr23,gr16,gr23,         cc7,#1
+
+       srl             gr20,gr15,gr20                  ; shift values down for FR551
+       srl             gr21,gr15,gr21
+       srl             gr22,gr15,gr22
+       srl             gr23,gr15,gr23
+
+       # work out the address at which the reg updater resides and lock it into icache
+       # also work out the address the updater will jump to when finished
+       sethi.p         %hi(__head_move_sdram-__head_reference),gr18
+       setlo           %lo(__head_move_sdram-__head_reference),gr18
+       sethi.p         %hi(__head_sdram_moved-__head_reference),gr19
+       setlo           %lo(__head_sdram_moved-__head_reference),gr19
+       add.p           gr18,gr26,gr18
+       add             gr19,gr26,gr19
+       add.p           gr19,gr16,gr19                  ; moved = addr + (__sdram_base - DBRx)
+       add             gr18,gr5,gr4                    ; two cachelines probably required
+
+       icpl            gr18,gr0,#1                     ; load and lock the cachelines
+       icpl            gr4,gr0,#1
+       LEDS            0x0004
+       membar
+       bar
+       jmpl            @(gr18,gr0)
+
+       .balign         L1_CACHE_BYTES
+__head_move_sdram:
+       cst             gr20,@(gr14,gr0 ),      cc4,#1
+       cst             gr21,@(gr14,gr11),      cc5,#1
+       cst             gr22,@(gr14,gr12),      cc6,#1
+       cst             gr23,@(gr14,gr13),      cc7,#1
+       cld             @(gr14,gr0 ),gr20,      cc4,#1
+       cld             @(gr14,gr11),gr21,      cc5,#1
+       cld             @(gr14,gr12),gr22,      cc4,#1
+       cld             @(gr14,gr13),gr23,      cc7,#1
+       bar
+       membar
+       jmpl            @(gr19,gr0)
+
+       .balign         L1_CACHE_BYTES
+__head_sdram_moved:
+       icul            gr18
+       add             gr18,gr5,gr4
+       icul            gr4
+       icei            @(gr0,gr0),1
+       dcei            @(gr0,gr0),1
+
+       LEDS            0x0005
+
+       # recalculate reference address
+       call            0f
+0:     movsg           lr,gr26
+       addi            gr26,#__head_reference-0b,gr26
+
+
+###############################################################################
+#
+# move the kernel image down to the bottom of the SDRAM
+#
+###############################################################################
+       sethi.p         %hi(__kernel_image_size_no_bss+15),gr4
+       setlo           %lo(__kernel_image_size_no_bss+15),gr4
+       srli.p          gr4,#4,gr4                      ; count
+       or              gr26,gr26,gr16                  ; source
+
+       sethi.p         %hi(__sdram_base),gr17          ; destination
+       setlo           %lo(__sdram_base),gr17
+
+       setlos          #8,gr5
+       sub.p           gr16,gr5,gr16                   ; adjust src for LDDU
+       sub             gr17,gr5,gr17                   ; adjust dst for LDDU
+
+       sethi.p         %hi(__head_move_kernel-__head_reference),gr18
+       setlo           %lo(__head_move_kernel-__head_reference),gr18
+       sethi.p         %hi(__head_kernel_moved-__head_reference+__sdram_base),gr19
+       setlo           %lo(__head_kernel_moved-__head_reference+__sdram_base),gr19
+       add             gr18,gr26,gr18
+       icpl            gr18,gr0,#1
+       jmpl            @(gr18,gr0)
+
+       .balign         32
+__head_move_kernel:
+       lddu            @(gr16,gr5),gr10
+       lddu            @(gr16,gr5),gr12
+       stdu.p          gr10,@(gr17,gr5)
+       subicc          gr4,#1,gr4,icc0
+       stdu.p          gr12,@(gr17,gr5)
+       bhi             icc0,#0,__head_move_kernel
+       jmpl            @(gr19,gr0)
+
+       .balign         32
+__head_kernel_moved:
+       icul            gr18
+       icei            @(gr0,gr0),1
+       dcei            @(gr0,gr0),1
+
+       LEDS            0x0006
+
+       # recalculate reference address
+       call            0f
+0:     movsg           lr,gr26
+       addi            gr26,#__head_reference-0b,gr26
+
+
+###############################################################################
+#
+# rearrange the iomem map and set the protection registers
+#
+###############################################################################
+
+#ifdef CONFIG_MMU
+       LEDS            0x3301
+       call            __head_fr451_set_busctl
+       LEDS            0x3303
+       call            __head_fr451_survey_sdram
+       LEDS            0x3305
+       call            __head_fr451_set_protection
+
+#else
+       movsg           psr,gr5
+       srli            gr5,#PSR_IMPLE_SHIFT,gr5
+       subicc          gr5,#PSR_IMPLE_FR551,gr0,icc0
+       beq             icc0,#0,__head_fr555_memmap
+       subicc          gr5,#PSR_IMPLE_FR451,gr0,icc0
+       beq             icc0,#0,__head_fr451_memmap
+
+       LEDS            0x3101
+       call            __head_fr401_set_busctl
+       LEDS            0x3103
+       call            __head_fr401_survey_sdram
+       LEDS            0x3105
+       call            __head_fr401_set_protection
+       bra             __head_done_memmap
+
+__head_fr451_memmap:
+       LEDS            0x3301
+       call            __head_fr401_set_busctl
+       LEDS            0x3303
+       call            __head_fr401_survey_sdram
+       LEDS            0x3305
+       call            __head_fr451_set_protection
+       bra             __head_done_memmap
+
+__head_fr555_memmap:
+       LEDS            0x3501
+       call            __head_fr555_set_busctl
+       LEDS            0x3503
+       call            __head_fr555_survey_sdram
+       LEDS            0x3505
+       call            __head_fr555_set_protection
+
+__head_done_memmap:
+#endif
+       LEDS            0x0007
+
+###############################################################################
+#
+# turn the data cache and MMU on
+# - for the FR451 this'll mean that the window through which the kernel is
+#   viewed will change
+#
+###############################################################################
+
+#ifdef CONFIG_MMU
+#define MMUMODE                HSR0_EIMMU|HSR0_EDMMU|HSR0_EXMMU|HSR0_EDAT|HSR0_XEDAT
+#else
+#define MMUMODE                HSR0_EIMMU|HSR0_EDMMU
+#endif
+
+       movsg           hsr0,gr5
+
+       sethi.p         %hi(MMUMODE),gr4
+       setlo           %lo(MMUMODE),gr4
+       or              gr4,gr5,gr5
+
+#if defined(CONFIG_FRV_DEFL_CACHE_WTHRU)
+       sethi.p         %hi(HSR0_DCE|HSR0_CBM_WRITE_THRU),gr4
+       setlo           %lo(HSR0_DCE|HSR0_CBM_WRITE_THRU),gr4
+#elif defined(CONFIG_FRV_DEFL_CACHE_WBACK)
+       sethi.p         %hi(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
+       setlo           %lo(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
+#elif defined(CONFIG_FRV_DEFL_CACHE_WBEHIND)
+       sethi.p         %hi(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
+       setlo           %lo(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
+
+       movsg           psr,gr6
+       srli            gr6,#24,gr6
+       cmpi            gr6,#0x50,icc0          // FR451
+       beq             icc0,#0,0f
+       cmpi            gr6,#0x40,icc0          // FR405
+       bne             icc0,#0,1f
+0:
+       # turn off write-allocate
+       sethi.p         %hi(HSR0_NWA),gr6
+       setlo           %lo(HSR0_NWA),gr6
+       or              gr4,gr6,gr4
+1:
+
+#else
+#error No default cache configuration set
+#endif
+
+       or              gr4,gr5,gr5
+       movgs           gr5,hsr0
+       bar
+
+       LEDS            0x0008
+
+       sethi.p         %hi(__head_mmu_enabled),gr19
+       setlo           %lo(__head_mmu_enabled),gr19
+       jmpl            @(gr19,gr0)
+
+__head_mmu_enabled:
+       icei            @(gr0,gr0),#1
+       dcei            @(gr0,gr0),#1
+
+       LEDS            0x0009
+
+#ifdef CONFIG_MMU
+       call            __head_fr451_finalise_protection
+#endif
+
+       LEDS            0x000a
+
+###############################################################################
+#
+# set up the runtime environment
+#
+###############################################################################
+
+       # clear the BSS area
+       sethi.p         %hi(__bss_start),gr4
+       setlo           %lo(__bss_start),gr4
+       sethi.p         %hi(_end),gr5
+       setlo           %lo(_end),gr5
+       or.p            gr0,gr0,gr18
+       or              gr0,gr0,gr19
+
+0:
+       stdi            gr18,@(gr4,#0)
+       stdi            gr18,@(gr4,#8)
+       stdi            gr18,@(gr4,#16)
+       stdi.p          gr18,@(gr4,#24)
+       addi            gr4,#24,gr4
+       subcc           gr5,gr4,gr0,icc0
+       bhi             icc0,#2,0b
+
+       LEDS            0x000b
+
+       # save the SDRAM details
+       sethi.p         %hi(__sdram_old_base),gr4
+       setlo           %lo(__sdram_old_base),gr4
+       st              gr24,@(gr4,gr0)
+
+       sethi.p         %hi(__sdram_base),gr5
+       setlo           %lo(__sdram_base),gr5
+       sethi.p         %hi(memory_start),gr4
+       setlo           %lo(memory_start),gr4
+       st              gr5,@(gr4,gr0)
+
+       add             gr25,gr5,gr25
+       sethi.p         %hi(memory_end),gr4
+       setlo           %lo(memory_end),gr4
+       st              gr25,@(gr4,gr0)
+
+       # point the TBR at the kernel trap table
+       sethi.p         %hi(__entry_kerneltrap_table),gr4
+       setlo           %lo(__entry_kerneltrap_table),gr4
+       movgs           gr4,tbr
+
+       # set up the exception frame for init
+       sethi.p         %hi(__kernel_frame0_ptr),gr28
+       setlo           %lo(__kernel_frame0_ptr),gr28
+       sethi.p         %hi(_gp),gr16
+       setlo           %lo(_gp),gr16
+       sethi.p         %hi(__entry_usertrap_table),gr4
+       setlo           %lo(__entry_usertrap_table),gr4
+
+       lddi            @(gr28,#0),gr28         ; load __frame & current
+       ldi.p           @(gr29,#4),gr15         ; set current_thread
+
+       or              gr0,gr0,fp
+       or              gr28,gr0,sp
+
+       sti.p           gr4,@(gr28,REG_TBR)
+       setlos          #ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5
+       movgs           gr5,isr
+
+       # turn on and off various CPU services
+       movsg           psr,gr22
+       sethi.p         %hi(#PSR_EM|PSR_EF|PSR_CM|PSR_NEM),gr4
+       setlo           %lo(#PSR_EM|PSR_EF|PSR_CM|PSR_NEM),gr4
+       or              gr22,gr4,gr22
+       movgs           gr22,psr
+
+       andi            gr22,#~(PSR_PIL|PSR_PS|PSR_S),gr22
+       ori             gr22,#PSR_ET,gr22
+       sti             gr22,@(gr28,REG_PSR)
+
+
+###############################################################################
+#
+# set up the registers and jump into the kernel
+#
+###############################################################################
+
+       LEDS            0x000c
+
+       # initialise the processor and the peripherals
+       #call           SYMBOL_NAME(processor_init)
+       #call           SYMBOL_NAME(unit_init)
+       #LEDS           0x0aff
+
+       sethi.p         #0xe5e5,gr3
+       setlo           #0xe5e5,gr3
+       or.p            gr3,gr0,gr4
+       or              gr3,gr0,gr5
+       or.p            gr3,gr0,gr6
+       or              gr3,gr0,gr7
+       or.p            gr3,gr0,gr8
+       or              gr3,gr0,gr9
+       or.p            gr3,gr0,gr10
+       or              gr3,gr0,gr11
+       or.p            gr3,gr0,gr12
+       or              gr3,gr0,gr13
+       or.p            gr3,gr0,gr14
+       or              gr3,gr0,gr17
+       or.p            gr3,gr0,gr18
+       or              gr3,gr0,gr19
+       or.p            gr3,gr0,gr20
+       or              gr3,gr0,gr21
+       or.p            gr3,gr0,gr23
+       or              gr3,gr0,gr24
+       or.p            gr3,gr0,gr25
+       or              gr3,gr0,gr26
+       or.p            gr3,gr0,gr27
+#      or              gr3,gr0,gr30
+       or              gr3,gr0,gr31
+       movgs           gr0,lr
+       movgs           gr0,lcr
+       movgs           gr0,ccr
+       movgs           gr0,cccr
+
+#ifdef CONFIG_MMU
+       movgs           gr3,scr2
+       movgs           gr3,scr3
+#endif
+
+       LEDS            0x0fff
+
+       # invoke the debugging stub if present
+       # - arch/frv/kernel/debug-stub.c will shift control directly to init/main.c
+       #   (it will not return here)
+       break
+       .globl          __debug_stub_init_break
+__debug_stub_init_break:
+
+       # however, if you need to use an ICE, and don't care about using any userspace
+       # debugging tools (such as the ptrace syscall), you can just step over the break
+       # above and get to the kernel this way
+       # look at arch/frv/kernel/debug-stub.c: debug_stub_init() to see what you've missed
+       call            start_kernel
+
+       .globl          __head_end
+__head_end:
+       .size           _boot, .-_boot
+
+       # provide a point for GDB to place a break
+       .section        .text.start,"ax"
+       .globl          _start
+       .balign         4
+_start:
+       call            _boot
+
+       .previous
+###############################################################################
+#
+# split a tile off of the region defined by GR8-GR9
+#
+#      ENTRY:                  EXIT:
+# GR4  -                       IAMPR value representing tile
+# GR5  -                       DAMPR value representing tile
+# GR6  -                       IAMLR value representing tile
+# GR7  -                       DAMLR value representing tile
+# GR8  region base pointer     [saved]
+# GR9  region top pointer      updated to exclude new tile
+# GR11 xAMLR mask              [saved]
+# GR25 SDRAM size              [saved]
+# GR30 LED address             [saved]
+#
+# - GR8 and GR9 should be rounded up/down to the nearest megabyte before calling
+#
+###############################################################################
+       .globl          __head_split_region
+       .type           __head_split_region,@function
+__head_split_region:
+       subcc.p         gr9,gr8,gr4,icc0
+       setlos          #31,gr5
+       scan.p          gr4,gr0,gr6
+       beq             icc0,#0,__head_region_empty
+       sub.p           gr5,gr6,gr6                     ; bit number of highest set bit (1MB=>20)
+       setlos          #1,gr4
+       sll.p           gr4,gr6,gr4                     ; size of region (1 << bitno)
+       subi            gr6,#17,gr6                     ; 1MB => 0x03
+       slli.p          gr6,#4,gr6                      ; 1MB => 0x30
+       sub             gr9,gr4,gr9                     ; move uncovered top down
+
+       or              gr9,gr6,gr4
+       ori             gr4,#xAMPRx_S_USER|xAMPRx_C_CACHED|xAMPRx_V,gr4
+       or.p            gr4,gr0,gr5
+
+       and             gr4,gr11,gr6
+       and.p           gr5,gr11,gr7
+       bralr
+
+__head_region_empty:
+       or.p            gr0,gr0,gr4
+       or              gr0,gr0,gr5
+       or.p            gr0,gr0,gr6
+       or              gr0,gr0,gr7
+       bralr
+       .size           __head_split_region, .-__head_split_region
+
+###############################################################################
+#
+# write the 32-bit hex number in GR8 to ttyS0
+#
+###############################################################################
+#if 0
+       .globl          __head_write_to_ttyS0
+       .type           __head_write_to_ttyS0,@function
+__head_write_to_ttyS0:
+       sethi.p         %hi(0xfeff9c00),gr31
+       setlo           %lo(0xfeff9c00),gr31
+       setlos          #8,gr20
+
+0:     ldubi           @(gr31,#5*8),gr21
+       andi            gr21,#0x60,gr21
+       subicc          gr21,#0x60,gr21,icc0
+       bne             icc0,#0,0b
+
+1:     srli            gr8,#28,gr21
+       slli            gr8,#4,gr8
+
+       addi            gr21,#'0',gr21
+       subicc          gr21,#'9',gr0,icc0
+       bls             icc0,#2,2f
+       addi            gr21,#'A'-'0'-10,gr21
+2:
+       stbi            gr21,@(gr31,#0*8)
+       subicc          gr20,#1,gr20,icc0
+       bhi             icc0,#2,1b
+
+       setlos          #'\r',gr21
+       stbi            gr21,@(gr31,#0*8)
+
+       setlos          #'\n',gr21
+       stbi            gr21,@(gr31,#0*8)
+
+3:     ldubi           @(gr31,#5*8),gr21
+       andi            gr21,#0x60,gr21
+       subicc          gr21,#0x60,gr21,icc0
+       bne             icc0,#0,3b
+       bralr
+
+       .size           __head_write_to_ttyS0, .-__head_write_to_ttyS0
+#endif
diff --git a/arch/frv/kernel/head.inc b/arch/frv/kernel/head.inc
new file mode 100644 (file)
index 0000000..d424cd2
--- /dev/null
@@ -0,0 +1,50 @@
+/* head.inc: head common definitions -*- asm -*-
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+#if defined(CONFIG_MB93090_MB00)
+#define LED_ADDR (0x21200000+4)
+
+.macro LEDS val
+       sethi.p         %hi(0xFFC00030),gr3
+       setlo           %lo(0xFFC00030),gr3
+       lduh            @(gr3,gr0),gr3
+       andicc          gr3,#0x100,gr0,icc0
+       bne             icc0,0,999f
+
+       setlos          #~\val,gr3
+       st              gr3,@(gr30,gr0)
+       membar
+       dcf             @(gr30,gr0)
+    999:
+.endm
+
+#elif defined(CONFIG_MB93093_PDK)
+#define LED_ADDR (0x20000023)
+
+.macro LEDS val
+       setlos          #\val,gr3
+       stb             gr3,@(gr30,gr0)
+       membar
+.endm
+
+#else
+#define LED_ADDR 0
+
+.macro LEDS val
+.endm
+#endif
+
+#ifdef CONFIG_MMU
+__sdram_base = 0x00000000              /* base address to which SDRAM relocated */
+#else
+__sdram_base = 0xc0000000              /* base address to which SDRAM relocated */
+#endif
diff --git a/arch/frv/kernel/init_task.c b/arch/frv/kernel/init_task.c
new file mode 100644 (file)
index 0000000..2299393
--- /dev/null
@@ -0,0 +1,39 @@
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/init_task.h>
+#include <linux/fs.h>
+#include <linux/mqueue.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+
+static struct fs_struct init_fs = INIT_FS;
+static struct files_struct init_files = INIT_FILES;
+static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
+static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
+struct mm_struct init_mm = INIT_MM(init_mm);
+
+EXPORT_SYMBOL(init_mm);
+
+/*
+ * Initial thread structure.
+ *
+ * We need to make sure that this is THREAD_SIZE aligned due to the
+ * way process stacks are handled. This is done by having a special
+ * "init_task" linker map entry..
+ */
+union thread_union init_thread_union
+       __attribute__((__section__(".data.init_task"))) =
+               { INIT_THREAD_INFO(init_task) };
+
+/*
+ * Initial task structure.
+ *
+ * All other task structs will be allocated on slabs in fork.c
+ */
+struct task_struct init_task = INIT_TASK(init_task);
+
+EXPORT_SYMBOL(init_task);
diff --git a/arch/frv/kernel/irq-mb93091.c b/arch/frv/kernel/irq-mb93091.c
new file mode 100644 (file)
index 0000000..9778e0f
--- /dev/null
@@ -0,0 +1,116 @@
+/* irq-mb93091.c: MB93091 FPGA interrupt handling
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/irc-regs.h>
+#include <asm/irq-routing.h>
+
+#define __reg16(ADDR) (*(volatile unsigned short *)(ADDR))
+
+#define __get_IMR()    ({ __reg16(0xffc00004); })
+#define __set_IMR(M)   do { __reg16(0xffc00004) = (M); wmb(); } while(0)
+#define __get_IFR()    ({ __reg16(0xffc0000c); })
+#define __clr_IFR(M)   do { __reg16(0xffc0000c) = ~(M); wmb(); } while(0)
+
+static void frv_fpga_doirq(struct irq_source *source);
+static void frv_fpga_control(struct irq_group *group, int irq, int on);
+
+/*****************************************************************************/
+/*
+ * FPGA IRQ multiplexor
+ */
+static struct irq_source frv_fpga[4] = {
+#define __FPGA(X, M)                                   \
+       [X] = {                                         \
+               .muxname        = "fpga."#X,            \
+               .irqmask        = M,                    \
+               .doirq          = frv_fpga_doirq,       \
+       }
+
+       __FPGA(0, 0x0028),
+       __FPGA(1, 0x0050),
+       __FPGA(2, 0x1c00),
+       __FPGA(3, 0x6386),
+};
+
+static struct irq_group frv_fpga_irqs = {
+       .first_irq      = IRQ_BASE_FPGA,
+       .control        = frv_fpga_control,
+       .sources = {
+               [ 1] = &frv_fpga[3],
+               [ 2] = &frv_fpga[3],
+               [ 3] = &frv_fpga[0],
+               [ 4] = &frv_fpga[1],
+               [ 5] = &frv_fpga[0],
+               [ 6] = &frv_fpga[1],
+               [ 7] = &frv_fpga[3],
+               [ 8] = &frv_fpga[3],
+               [ 9] = &frv_fpga[3],
+               [10] = &frv_fpga[2],
+               [11] = &frv_fpga[2],
+               [12] = &frv_fpga[2],
+               [13] = &frv_fpga[3],
+               [14] = &frv_fpga[3],
+       },
+};
+
+
+static void frv_fpga_control(struct irq_group *group, int index, int on)
+{
+       uint16_t imr = __get_IMR();
+
+       if (on)
+               imr &= ~(1 << index);
+       else
+               imr |= 1 << index;
+
+       __set_IMR(imr);
+}
+
+static void frv_fpga_doirq(struct irq_source *source)
+{
+       uint16_t mask, imr;
+
+       imr = __get_IMR();
+       mask = source->irqmask & ~imr & __get_IFR();
+       if (mask) {
+               __set_IMR(imr | mask);
+               __clr_IFR(mask);
+               distribute_irqs(&frv_fpga_irqs, mask);
+               __set_IMR(imr);
+       }
+}
+
+void __init fpga_init(void)
+{
+       __set_IMR(0x7ffe);
+       __clr_IFR(0x0000);
+
+       frv_irq_route_external(&frv_fpga[0], IRQ_CPU_EXTERNAL0);
+       frv_irq_route_external(&frv_fpga[1], IRQ_CPU_EXTERNAL1);
+       frv_irq_route_external(&frv_fpga[2], IRQ_CPU_EXTERNAL2);
+       frv_irq_route_external(&frv_fpga[3], IRQ_CPU_EXTERNAL3);
+       frv_irq_set_group(&frv_fpga_irqs);
+}
diff --git a/arch/frv/kernel/irq-mb93093.c b/arch/frv/kernel/irq-mb93093.c
new file mode 100644 (file)
index 0000000..21ca2b2
--- /dev/null
@@ -0,0 +1,99 @@
+/* irq-mb93093.c: MB93093 FPGA interrupt handling
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/irc-regs.h>
+#include <asm/irq-routing.h>
+
+#define __reg16(ADDR) (*(volatile unsigned short *)(__region_CS2 + (ADDR)))
+
+#define __get_IMR()    ({ __reg16(0x0a); })
+#define __set_IMR(M)   do { __reg16(0x0a) = (M);  wmb(); } while(0)
+#define __get_IFR()    ({ __reg16(0x02); })
+#define __clr_IFR(M)   do { __reg16(0x02) = ~(M); wmb(); } while(0)
+
+static void frv_fpga_doirq(struct irq_source *source);
+static void frv_fpga_control(struct irq_group *group, int irq, int on);
+
+/*****************************************************************************/
+/*
+ * FPGA IRQ multiplexor
+ */
+static struct irq_source frv_fpga[4] = {
+#define __FPGA(X, M)                                   \
+       [X] = {                                         \
+               .muxname        = "fpga."#X,            \
+               .irqmask        = M,                    \
+               .doirq          = frv_fpga_doirq,       \
+       }
+
+       __FPGA(0, 0x0700),
+};
+
+static struct irq_group frv_fpga_irqs = {
+       .first_irq      = IRQ_BASE_FPGA,
+       .control        = frv_fpga_control,
+       .sources = {
+               [ 8] = &frv_fpga[0],
+               [ 9] = &frv_fpga[0],
+               [10] = &frv_fpga[0],
+       },
+};
+
+
+static void frv_fpga_control(struct irq_group *group, int index, int on)
+{
+       uint16_t imr = __get_IMR();
+
+       if (on)
+               imr &= ~(1 << index);
+       else
+               imr |= 1 << index;
+
+       __set_IMR(imr);
+}
+
+static void frv_fpga_doirq(struct irq_source *source)
+{
+       uint16_t mask, imr;
+
+       imr = __get_IMR();
+       mask = source->irqmask & ~imr & __get_IFR();
+       if (mask) {
+               __set_IMR(imr | mask);
+               __clr_IFR(mask);
+               distribute_irqs(&frv_fpga_irqs, mask);
+               __set_IMR(imr);
+       }
+}
+
+void __init fpga_init(void)
+{
+       __set_IMR(0x0700);
+       __clr_IFR(0x0000);
+
+       frv_irq_route_external(&frv_fpga[0], IRQ_CPU_EXTERNAL2);
+       frv_irq_set_group(&frv_fpga_irqs);
+}
diff --git a/arch/frv/kernel/irq-mb93493.c b/arch/frv/kernel/irq-mb93493.c
new file mode 100644 (file)
index 0000000..c003ae5
--- /dev/null
@@ -0,0 +1,108 @@
+/* irq-mb93493.c: MB93493 companion chip interrupt handler
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/irc-regs.h>
+#include <asm/irq-routing.h>
+#include <asm/mb93493-irqs.h>
+
+static void frv_mb93493_doirq(struct irq_source *source);
+
+/*****************************************************************************/
+/*
+ * MB93493 companion chip IRQ multiplexor
+ */
+static struct irq_source frv_mb93493[2] = {
+       [0] = {
+               .muxname                = "mb93493.0",
+               .muxdata                = __region_CS3 + 0x3d0,
+               .doirq                  = frv_mb93493_doirq,
+               .irqmask                = 0x0000,
+       },
+       [1] = {
+               .muxname                = "mb93493.1",
+               .muxdata                = __region_CS3 + 0x3d4,
+               .doirq                  = frv_mb93493_doirq,
+               .irqmask                = 0x0000,
+       },
+};
+
+static void frv_mb93493_control(struct irq_group *group, int index, int on)
+{
+       struct irq_source *source;
+       uint32_t iqsr;
+
+       if ((frv_mb93493[0].irqmask & (1 << index)))
+               source = &frv_mb93493[0];
+       else
+               source = &frv_mb93493[1];
+
+       iqsr = readl(source->muxdata);
+       if (on)
+               iqsr |= 1 << (index + 16);
+       else
+               iqsr &= ~(1 << (index + 16));
+
+       writel(iqsr, source->muxdata);
+}
+
+static struct irq_group frv_mb93493_irqs = {
+       .first_irq      = IRQ_BASE_MB93493,
+       .control        = frv_mb93493_control,
+};
+
+static void frv_mb93493_doirq(struct irq_source *source)
+{
+       uint32_t mask = readl(source->muxdata);
+       mask = mask & (mask >> 16) & 0xffff;
+
+       if (mask)
+               distribute_irqs(&frv_mb93493_irqs, mask);
+}
+
+static void __init mb93493_irq_route(int irq, int source)
+{
+       frv_mb93493[source].irqmask |= 1 << (irq - IRQ_BASE_MB93493);
+       frv_mb93493_irqs.sources[irq - IRQ_BASE_MB93493] = &frv_mb93493[source];
+}
+
+void __init route_mb93493_irqs(void)
+{
+       frv_irq_route_external(&frv_mb93493[0], IRQ_CPU_MB93493_0);
+       frv_irq_route_external(&frv_mb93493[1], IRQ_CPU_MB93493_1);
+
+       frv_irq_set_group(&frv_mb93493_irqs);
+
+       mb93493_irq_route(IRQ_MB93493_VDC,              IRQ_MB93493_VDC_ROUTE);
+       mb93493_irq_route(IRQ_MB93493_VCC,              IRQ_MB93493_VCC_ROUTE);
+       mb93493_irq_route(IRQ_MB93493_AUDIO_IN,         IRQ_MB93493_AUDIO_IN_ROUTE);
+       mb93493_irq_route(IRQ_MB93493_I2C_0,            IRQ_MB93493_I2C_0_ROUTE);
+       mb93493_irq_route(IRQ_MB93493_I2C_1,            IRQ_MB93493_I2C_1_ROUTE);
+       mb93493_irq_route(IRQ_MB93493_USB,              IRQ_MB93493_USB_ROUTE);
+       mb93493_irq_route(IRQ_MB93493_LOCAL_BUS,        IRQ_MB93493_LOCAL_BUS_ROUTE);
+       mb93493_irq_route(IRQ_MB93493_PCMCIA,           IRQ_MB93493_PCMCIA_ROUTE);
+       mb93493_irq_route(IRQ_MB93493_GPIO,             IRQ_MB93493_GPIO_ROUTE);
+       mb93493_irq_route(IRQ_MB93493_AUDIO_OUT,        IRQ_MB93493_AUDIO_OUT_ROUTE);
+}
diff --git a/arch/frv/kernel/irq-routing.c b/arch/frv/kernel/irq-routing.c
new file mode 100644 (file)
index 0000000..d4776d1
--- /dev/null
@@ -0,0 +1,291 @@
+/* irq-routing.c: IRQ routing
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/serial_reg.h>
+#include <asm/io.h>
+#include <asm/irq-routing.h>
+#include <asm/irc-regs.h>
+#include <asm/serial-regs.h>
+#include <asm/dma.h>
+
+struct irq_level frv_irq_levels[16] = {
+       [0 ... 15] = {
+               .lock   = SPIN_LOCK_UNLOCKED,
+       }
+};
+
+struct irq_group *irq_groups[NR_IRQ_GROUPS];
+
+extern struct irq_group frv_cpu_irqs;
+
+void __init frv_irq_route(struct irq_source *source, int irqlevel)
+{
+       source->level = &frv_irq_levels[irqlevel];
+       source->next = frv_irq_levels[irqlevel].sources;
+       frv_irq_levels[irqlevel].sources = source;
+}
+
+void __init frv_irq_route_external(struct irq_source *source, int irq)
+{
+       int irqlevel = 0;
+
+       switch (irq) {
+       case IRQ_CPU_EXTERNAL0: irqlevel = IRQ_XIRQ0_LEVEL; break;
+       case IRQ_CPU_EXTERNAL1: irqlevel = IRQ_XIRQ1_LEVEL; break;
+       case IRQ_CPU_EXTERNAL2: irqlevel = IRQ_XIRQ2_LEVEL; break;
+       case IRQ_CPU_EXTERNAL3: irqlevel = IRQ_XIRQ3_LEVEL; break;
+       case IRQ_CPU_EXTERNAL4: irqlevel = IRQ_XIRQ4_LEVEL; break;
+       case IRQ_CPU_EXTERNAL5: irqlevel = IRQ_XIRQ5_LEVEL; break;
+       case IRQ_CPU_EXTERNAL6: irqlevel = IRQ_XIRQ6_LEVEL; break;
+       case IRQ_CPU_EXTERNAL7: irqlevel = IRQ_XIRQ7_LEVEL; break;
+       default: BUG();
+       }
+
+       source->level = &frv_irq_levels[irqlevel];
+       source->next = frv_irq_levels[irqlevel].sources;
+       frv_irq_levels[irqlevel].sources = source;
+}
+
+void __init frv_irq_set_group(struct irq_group *group)
+{
+       irq_groups[group->first_irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP] = group;
+}
+
+void distribute_irqs(struct irq_group *group, unsigned long irqmask)
+{
+       struct irqaction *action;
+       int irq;
+
+       while (irqmask) {
+               asm("scan %1,gr0,%0" : "=r"(irq) : "r"(irqmask));
+               if (irq < 0 || irq > 31)
+                       asm volatile("break");
+               irq = 31 - irq;
+
+               irqmask &= ~(1 << irq);
+               action = group->actions[irq];
+
+               irq += group->first_irq;
+
+               if (action) {
+                       int status = 0;
+
+//                     if (!(action->flags & SA_INTERRUPT))
+//                             local_irq_enable();
+
+                       do {
+                               status |= action->flags;
+                               action->handler(irq, action->dev_id, __frame);
+                               action = action->next;
+                       } while (action);
+
+                       if (status & SA_SAMPLE_RANDOM)
+                               add_interrupt_randomness(irq);
+                       local_irq_disable();
+               }
+       }
+}
+
+/*****************************************************************************/
+/*
+ * CPU UART interrupts
+ */
+static void frv_cpuuart_doirq(struct irq_source *source)
+{
+//     uint8_t iir = readb(source->muxdata + UART_IIR * 8);
+//     if ((iir & 0x0f) != UART_IIR_NO_INT)
+               distribute_irqs(&frv_cpu_irqs, source->irqmask);
+}
+
+struct irq_source frv_cpuuart[2] = {
+#define __CPUUART(X, A)                                                \
+       [X] = {                                                 \
+               .muxname        = "uart",                       \
+               .muxdata        = (volatile void __iomem *) A,  \
+               .irqmask        = 1 << IRQ_CPU_UART##X,         \
+               .doirq          = frv_cpuuart_doirq,            \
+       }
+
+       __CPUUART(0, UART0_BASE),
+       __CPUUART(1, UART1_BASE),
+};
+
+/*****************************************************************************/
+/*
+ * CPU DMA interrupts
+ */
+static void frv_cpudma_doirq(struct irq_source *source)
+{
+       uint32_t cstr = readl(source->muxdata + DMAC_CSTRx);
+       if (cstr & DMAC_CSTRx_INT)
+               distribute_irqs(&frv_cpu_irqs, source->irqmask);
+}
+
+struct irq_source frv_cpudma[8] = {
+#define __CPUDMA(X, A)                                         \
+       [X] = {                                                 \
+               .muxname        = "dma",                        \
+               .muxdata        = (volatile void __iomem *) A,  \
+               .irqmask        = 1 << IRQ_CPU_DMA##X,          \
+               .doirq          = frv_cpudma_doirq,             \
+       }
+
+       __CPUDMA(0, 0xfe000900),
+       __CPUDMA(1, 0xfe000980),
+       __CPUDMA(2, 0xfe000a00),
+       __CPUDMA(3, 0xfe000a80),
+       __CPUDMA(4, 0xfe001000),
+       __CPUDMA(5, 0xfe001080),
+       __CPUDMA(6, 0xfe001100),
+       __CPUDMA(7, 0xfe001180),
+};
+
+/*****************************************************************************/
+/*
+ * CPU timer interrupts - can't tell whether they've generated an interrupt or not
+ */
+static void frv_cputimer_doirq(struct irq_source *source)
+{
+       distribute_irqs(&frv_cpu_irqs, source->irqmask);
+}
+
+struct irq_source frv_cputimer[3] = {
+#define __CPUTIMER(X)                                          \
+       [X] = {                                                 \
+               .muxname        = "timer",                      \
+               .muxdata        = 0,                            \
+               .irqmask        = 1 << IRQ_CPU_TIMER##X,        \
+               .doirq          = frv_cputimer_doirq,           \
+       }
+
+       __CPUTIMER(0),
+       __CPUTIMER(1),
+       __CPUTIMER(2),
+};
+
+/*****************************************************************************/
+/*
+ * external CPU interrupts - can't tell directly whether they've generated an interrupt or not
+ */
+static void frv_cpuexternal_doirq(struct irq_source *source)
+{
+       distribute_irqs(&frv_cpu_irqs, source->irqmask);
+}
+
+struct irq_source frv_cpuexternal[8] = {
+#define __CPUEXTERNAL(X)                                       \
+       [X] = {                                                 \
+               .muxname        = "ext",                        \
+               .muxdata        = 0,                            \
+               .irqmask        = 1 << IRQ_CPU_EXTERNAL##X,     \
+               .doirq          = frv_cpuexternal_doirq,        \
+       }
+
+       __CPUEXTERNAL(0),
+       __CPUEXTERNAL(1),
+       __CPUEXTERNAL(2),
+       __CPUEXTERNAL(3),
+       __CPUEXTERNAL(4),
+       __CPUEXTERNAL(5),
+       __CPUEXTERNAL(6),
+       __CPUEXTERNAL(7),
+};
+
+#define set_IRR(N,A,B,C,D) __set_IRR(N, (A << 28) | (B << 24) | (C << 20) | (D << 16))
+
+struct irq_group frv_cpu_irqs = {
+       .sources = {
+               [IRQ_CPU_UART0]         = &frv_cpuuart[0],
+               [IRQ_CPU_UART1]         = &frv_cpuuart[1],
+               [IRQ_CPU_TIMER0]        = &frv_cputimer[0],
+               [IRQ_CPU_TIMER1]        = &frv_cputimer[1],
+               [IRQ_CPU_TIMER2]        = &frv_cputimer[2],
+               [IRQ_CPU_DMA0]          = &frv_cpudma[0],
+               [IRQ_CPU_DMA1]          = &frv_cpudma[1],
+               [IRQ_CPU_DMA2]          = &frv_cpudma[2],
+               [IRQ_CPU_DMA3]          = &frv_cpudma[3],
+               [IRQ_CPU_DMA4]          = &frv_cpudma[4],
+               [IRQ_CPU_DMA5]          = &frv_cpudma[5],
+               [IRQ_CPU_DMA6]          = &frv_cpudma[6],
+               [IRQ_CPU_DMA7]          = &frv_cpudma[7],
+               [IRQ_CPU_EXTERNAL0]     = &frv_cpuexternal[0],
+               [IRQ_CPU_EXTERNAL1]     = &frv_cpuexternal[1],
+               [IRQ_CPU_EXTERNAL2]     = &frv_cpuexternal[2],
+               [IRQ_CPU_EXTERNAL3]     = &frv_cpuexternal[3],
+               [IRQ_CPU_EXTERNAL4]     = &frv_cpuexternal[4],
+               [IRQ_CPU_EXTERNAL5]     = &frv_cpuexternal[5],
+               [IRQ_CPU_EXTERNAL6]     = &frv_cpuexternal[6],
+               [IRQ_CPU_EXTERNAL7]     = &frv_cpuexternal[7],
+       },
+};
+
+/*****************************************************************************/
+/*
+ * route the CPU's interrupt sources
+ */
+void __init route_cpu_irqs(void)
+{
+       frv_irq_set_group(&frv_cpu_irqs);
+
+       __set_IITMR(0, 0x003f0000);     /* DMA0-3, TIMER0-2 IRQ detect levels */
+       __set_IITMR(1, 0x20000000);     /* ERR0-1, UART0-1, DMA4-7 IRQ detect levels */
+
+       /* route UART and error interrupts */
+       frv_irq_route(&frv_cpuuart[0],  IRQ_UART0_LEVEL);
+       frv_irq_route(&frv_cpuuart[1],  IRQ_UART1_LEVEL);
+
+       set_IRR(6, IRQ_GDBSTUB_LEVEL, IRQ_GDBSTUB_LEVEL, IRQ_UART1_LEVEL, IRQ_UART0_LEVEL);
+
+       /* route DMA channel interrupts */
+       frv_irq_route(&frv_cpudma[0],   IRQ_DMA0_LEVEL);
+       frv_irq_route(&frv_cpudma[1],   IRQ_DMA1_LEVEL);
+       frv_irq_route(&frv_cpudma[2],   IRQ_DMA2_LEVEL);
+       frv_irq_route(&frv_cpudma[3],   IRQ_DMA3_LEVEL);
+       frv_irq_route(&frv_cpudma[4],   IRQ_DMA4_LEVEL);
+       frv_irq_route(&frv_cpudma[5],   IRQ_DMA5_LEVEL);
+       frv_irq_route(&frv_cpudma[6],   IRQ_DMA6_LEVEL);
+       frv_irq_route(&frv_cpudma[7],   IRQ_DMA7_LEVEL);
+
+       set_IRR(4, IRQ_DMA3_LEVEL, IRQ_DMA2_LEVEL, IRQ_DMA1_LEVEL, IRQ_DMA0_LEVEL);
+       set_IRR(7, IRQ_DMA7_LEVEL, IRQ_DMA6_LEVEL, IRQ_DMA5_LEVEL, IRQ_DMA4_LEVEL);
+
+       /* route timer interrupts */
+       frv_irq_route(&frv_cputimer[0], IRQ_TIMER0_LEVEL);
+       frv_irq_route(&frv_cputimer[1], IRQ_TIMER1_LEVEL);
+       frv_irq_route(&frv_cputimer[2], IRQ_TIMER2_LEVEL);
+
+       set_IRR(5, 0, IRQ_TIMER2_LEVEL, IRQ_TIMER1_LEVEL, IRQ_TIMER0_LEVEL);
+
+       /* route external interrupts */
+       frv_irq_route(&frv_cpuexternal[0], IRQ_XIRQ0_LEVEL);
+       frv_irq_route(&frv_cpuexternal[1], IRQ_XIRQ1_LEVEL);
+       frv_irq_route(&frv_cpuexternal[2], IRQ_XIRQ2_LEVEL);
+       frv_irq_route(&frv_cpuexternal[3], IRQ_XIRQ3_LEVEL);
+       frv_irq_route(&frv_cpuexternal[4], IRQ_XIRQ4_LEVEL);
+       frv_irq_route(&frv_cpuexternal[5], IRQ_XIRQ5_LEVEL);
+       frv_irq_route(&frv_cpuexternal[6], IRQ_XIRQ6_LEVEL);
+       frv_irq_route(&frv_cpuexternal[7], IRQ_XIRQ7_LEVEL);
+
+       set_IRR(2, IRQ_XIRQ7_LEVEL, IRQ_XIRQ6_LEVEL, IRQ_XIRQ5_LEVEL, IRQ_XIRQ4_LEVEL);
+       set_IRR(3, IRQ_XIRQ3_LEVEL, IRQ_XIRQ2_LEVEL, IRQ_XIRQ1_LEVEL, IRQ_XIRQ0_LEVEL);
+
+#if defined(CONFIG_MB93091_VDK)
+       __set_TM1(0x55550000);          /* XIRQ7-0 all active low */
+#elif defined(CONFIG_MB93093_PDK)
+       __set_TM1(0x15550000);          /* XIRQ7 active high, 6-0 all active low */
+#else
+#error dont know external IRQ trigger levels for this setup
+#endif
+
+} /* end route_cpu_irqs() */
diff --git a/arch/frv/kernel/irq.c b/arch/frv/kernel/irq.c
new file mode 100644 (file)
index 0000000..8c524cd
--- /dev/null
@@ -0,0 +1,764 @@
+/* irq.c: FRV IRQ handling
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * (mostly architecture independent, will move to kernel/irq.c in 2.5.)
+ *
+ * IRQs are in fact implemented a bit like signal handlers for the kernel.
+ * Naturally it's not a 1:1 relation, but there are similarities.
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/irq.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/atomic.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/irc-regs.h>
+#include <asm/irq-routing.h>
+#include <asm/gdb-stub.h>
+
+extern void __init fpga_init(void);
+extern void __init route_mb93493_irqs(void);
+
+static void register_irq_proc (unsigned int irq);
+
+/*
+ * Special irq handlers.
+ */
+
+irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs) { return IRQ_HANDLED; }
+
+atomic_t irq_err_count;
+
+/*
+ * Generic, controller-independent functions:
+ */
+int show_interrupts(struct seq_file *p, void *v)
+{
+       struct irqaction *action;
+       struct irq_group *group;
+       unsigned long flags;
+       int level, grp, ix, i, j;
+
+       i = *(loff_t *) v;
+
+       switch (i) {
+       case 0:
+               seq_printf(p, "           ");
+               for (j = 0; j < NR_CPUS; j++)
+                       if (cpu_online(j))
+                               seq_printf(p, "CPU%d       ",j);
+
+               seq_putc(p, '\n');
+               break;
+
+       case 1 ... NR_IRQ_GROUPS * NR_IRQ_ACTIONS_PER_GROUP:
+               local_irq_save(flags);
+
+               grp = (i - 1) / NR_IRQ_ACTIONS_PER_GROUP;
+               group = irq_groups[grp];
+               if (!group)
+                       goto skip;
+
+               ix = (i - 1) % NR_IRQ_ACTIONS_PER_GROUP;
+               action = group->actions[ix];
+               if (!action)
+                       goto skip;
+
+               seq_printf(p, "%3d: ", i - 1);
+
+#ifndef CONFIG_SMP
+               seq_printf(p, "%10u ", kstat_irqs(i));
+#else
+               for (j = 0; j < NR_CPUS; j++)
+                       if (cpu_online(j))
+                               seq_printf(p, "%10u ", kstat_cpu(j).irqs[i - 1]);
+#endif
+
+               level = group->sources[ix]->level - frv_irq_levels;
+
+               seq_printf(p, " %12s@%x", group->sources[ix]->muxname, level);
+               seq_printf(p, "  %s", action->name);
+
+               for (action = action->next; action; action = action->next)
+                       seq_printf(p, ", %s", action->name);
+
+               seq_putc(p, '\n');
+skip:
+               local_irq_restore(flags);
+               break;
+
+       case NR_IRQ_GROUPS * NR_IRQ_ACTIONS_PER_GROUP + 1:
+               seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+
+/*
+ * Generic enable/disable code: this just calls
+ * down into the PIC-specific version for the actual
+ * hardware disable after having gotten the irq
+ * controller lock.
+ */
+
+/**
+ *     disable_irq_nosync - disable an irq without waiting
+ *     @irq: Interrupt to disable
+ *
+ *     Disable the selected interrupt line.  Disables and Enables are
+ *     nested.
+ *     Unlike disable_irq(), this function does not ensure existing
+ *     instances of the IRQ handler have completed before returning.
+ *
+ *     This function may be called from IRQ context.
+ */
+
+void disable_irq_nosync(unsigned int irq)
+{
+       struct irq_source *source;
+       struct irq_group *group;
+       struct irq_level *level;
+       unsigned long flags;
+       int idx = irq & (NR_IRQ_ACTIONS_PER_GROUP - 1);
+
+       group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
+       if (!group)
+               BUG();
+
+       source = group->sources[idx];
+       if (!source)
+               BUG();
+
+       level = source->level;
+
+       spin_lock_irqsave(&level->lock, flags);
+
+       if (group->control) {
+               if (!group->disable_cnt[idx]++)
+                       group->control(group, idx, 0);
+       } else if (!level->disable_count++) {
+               __set_MASK(level - frv_irq_levels);
+       }
+
+       spin_unlock_irqrestore(&level->lock, flags);
+}
+
+/**
+ *     disable_irq - disable an irq and wait for completion
+ *     @irq: Interrupt to disable
+ *
+ *     Disable the selected interrupt line.  Enables and Disables are
+ *     nested.
+ *     This function waits for any pending IRQ handlers for this interrupt
+ *     to complete before returning. If you use this function while
+ *     holding a resource the IRQ handler may need you will deadlock.
+ *
+ *     This function may be called - with care - from IRQ context.
+ */
+
+void disable_irq(unsigned int irq)
+{
+       disable_irq_nosync(irq);
+
+#ifdef CONFIG_SMP
+       if (!local_irq_count(smp_processor_id())) {
+               do {
+                       barrier();
+               } while (irq_desc[irq].status & IRQ_INPROGRESS);
+       }
+#endif
+}
+
+/**
+ *     enable_irq - enable handling of an irq
+ *     @irq: Interrupt to enable
+ *
+ *     Undoes the effect of one call to disable_irq().  If this
+ *     matches the last disable, processing of interrupts on this
+ *     IRQ line is re-enabled.
+ *
+ *     This function may be called from IRQ context.
+ */
+
+void enable_irq(unsigned int irq)
+{
+       struct irq_source *source;
+       struct irq_group *group;
+       struct irq_level *level;
+       unsigned long flags;
+       int idx = irq & (NR_IRQ_ACTIONS_PER_GROUP - 1);
+       int count;
+
+       group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
+       if (!group)
+               BUG();
+
+       source = group->sources[idx];
+       if (!source)
+               BUG();
+
+       level = source->level;
+
+       spin_lock_irqsave(&level->lock, flags);
+
+       if (group->control)
+               count = group->disable_cnt[idx];
+       else
+               count = level->disable_count;
+
+       switch (count) {
+       case 1:
+               if (group->control) {
+                       if (group->actions[idx])
+                               group->control(group, idx, 1);
+               } else {
+                       if (level->usage)
+                               __clr_MASK(level - frv_irq_levels);
+               }
+               /* fall-through */
+
+       default:
+               count--;
+               break;
+
+       case 0:
+               printk("enable_irq(%u) unbalanced from %p\n", irq, __builtin_return_address(0));
+       }
+
+       if (group->control)
+               group->disable_cnt[idx] = count;
+       else
+               level->disable_count = count;
+
+       spin_unlock_irqrestore(&level->lock, flags);
+}
+
+/*****************************************************************************/
+/*
+ * handles all normal device IRQ's
+ * - registers are referred to by the __frame variable (GR28)
+ * - IRQ distribution is complicated in this arch because of the many PICs, the
+ *   way they work and the way they cascade
+ */
+asmlinkage void do_IRQ(void)
+{
+       struct irq_source *source;
+       int level, cpu;
+
+       level = (__frame->tbr >> 4) & 0xf;
+       cpu = smp_processor_id();
+
+#if 0
+       {
+               static u32 irqcount;
+               *(volatile u32 *) 0xe1200004 = ~((irqcount++ << 8) | level);
+               *(volatile u16 *) 0xffc00100 = (u16) ~0x9999;
+               mb();
+       }
+#endif
+
+       if ((unsigned long) __frame - (unsigned long) (current + 1) < 512)
+               BUG();
+
+       __set_MASK(level);
+       __clr_RC(level);
+       __clr_IRL();
+
+       kstat_this_cpu.irqs[level]++;
+
+       irq_enter();
+
+       for (source = frv_irq_levels[level].sources; source; source = source->next)
+               source->doirq(source);
+
+       irq_exit();
+
+       __clr_MASK(level);
+
+       /* only process softirqs if we didn't interrupt another interrupt handler */
+       if ((__frame->psr & PSR_PIL) == PSR_PIL_0)
+               if (local_softirq_pending())
+                       do_softirq();
+
+#ifdef CONFIG_PREEMPT
+       local_irq_disable();
+       while (--current->preempt_count == 0) {
+               if (!(__frame->psr & PSR_S) ||
+                   current->need_resched == 0 ||
+                   in_interrupt())
+                       break;
+               current->preempt_count++;
+               local_irq_enable();
+               preempt_schedule();
+               local_irq_disable();
+       }
+#endif
+
+#if 0
+       {
+               *(volatile u16 *) 0xffc00100 = (u16) ~0x6666;
+               mb();
+       }
+#endif
+
+} /* end do_IRQ() */
+
+/*****************************************************************************/
+/*
+ * handles all NMIs when not co-opted by the debugger
+ * - registers are referred to by the __frame variable (GR28)
+ */
+asmlinkage void do_NMI(void)
+{
+} /* end do_NMI() */
+
+/*****************************************************************************/
+/**
+ *     request_irq - allocate an interrupt line
+ *     @irq: Interrupt line to allocate
+ *     @handler: Function to be called when the IRQ occurs
+ *     @irqflags: Interrupt type flags
+ *     @devname: An ascii name for the claiming device
+ *     @dev_id: A cookie passed back to the handler function
+ *
+ *     This call allocates interrupt resources and enables the
+ *     interrupt line and IRQ handling. From the point this
+ *     call is made your handler function may be invoked. Since
+ *     your handler function must clear any interrupt the board
+ *     raises, you must take care both to initialise your hardware
+ *     and to set up the interrupt handler in the right order.
+ *
+ *     Dev_id must be globally unique. Normally the address of the
+ *     device data structure is used as the cookie. Since the handler
+ *     receives this value it makes sense to use it.
+ *
+ *     If your interrupt is shared you must pass a non NULL dev_id
+ *     as this is required when freeing the interrupt.
+ *
+ *     Flags:
+ *
+ *     SA_SHIRQ                Interrupt is shared
+ *
+ *     SA_INTERRUPT            Disable local interrupts while processing
+ *
+ *     SA_SAMPLE_RANDOM        The interrupt can be used for entropy
+ *
+ */
+
+int request_irq(unsigned int irq,
+               irqreturn_t (*handler)(int, void *, struct pt_regs *),
+               unsigned long irqflags,
+               const char * devname,
+               void *dev_id)
+{
+       int retval;
+       struct irqaction *action;
+
+#if 1
+       /*
+        * Sanity-check: shared interrupts should REALLY pass in
+        * a real dev-ID, otherwise we'll have trouble later trying
+        * to figure out which interrupt is which (messes up the
+        * interrupt freeing logic etc).
+        */
+       if (irqflags & SA_SHIRQ) {
+               if (!dev_id)
+                       printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n",
+                              devname, (&irq)[-1]);
+       }
+#endif
+
+       if ((irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP) >= NR_IRQ_GROUPS)
+               return -EINVAL;
+       if (!handler)
+               return -EINVAL;
+
+       action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+       if (!action)
+               return -ENOMEM;
+
+       action->handler = handler;
+       action->flags = irqflags;
+       action->mask = CPU_MASK_NONE;
+       action->name = devname;
+       action->next = NULL;
+       action->dev_id = dev_id;
+
+       retval = setup_irq(irq, action);
+       if (retval)
+               kfree(action);
+       return retval;
+}
+
+/**
+ *     free_irq - free an interrupt
+ *     @irq: Interrupt line to free
+ *     @dev_id: Device identity to free
+ *
+ *     Remove an interrupt handler. The handler is removed and if the
+ *     interrupt line is no longer in use by any driver it is disabled.
+ *     On a shared IRQ the caller must ensure the interrupt is disabled
+ *     on the card it drives before calling this function. The function
+ *     does not return until any executing interrupts for this IRQ
+ *     have completed.
+ *
+ *     This function may be called from interrupt context.
+ *
+ *     Bugs: Attempting to free an irq in a handler for the same irq hangs
+ *           the machine.
+ */
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+       struct irq_source *source;
+       struct irq_group *group;
+       struct irq_level *level;
+       struct irqaction **p, **pp;
+       unsigned long flags;
+
+       if ((irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP) >= NR_IRQ_GROUPS)
+               return;
+
+       group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
+       if (!group)
+               BUG();
+
+       source = group->sources[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
+       if (!source)
+               BUG();
+
+       level = source->level;
+       p = &group->actions[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
+
+       spin_lock_irqsave(&level->lock, flags);
+
+       for (pp = p; *pp; pp = &(*pp)->next) {
+               struct irqaction *action = *pp;
+
+               if (action->dev_id != dev_id)
+                       continue;
+
+               /* found it - remove from the list of entries */
+               *pp = action->next;
+
+               level->usage--;
+
+               if (p == pp && group->control)
+                       group->control(group, irq & (NR_IRQ_ACTIONS_PER_GROUP - 1), 0);
+
+               if (level->usage == 0)
+                       __set_MASK(level - frv_irq_levels);
+
+               spin_unlock_irqrestore(&level->lock,flags);
+
+#ifdef CONFIG_SMP
+               /* Wait to make sure it's not being used on another CPU */
+               while (desc->status & IRQ_INPROGRESS)
+                       barrier();
+#endif
+               kfree(action);
+               return;
+       }
+}
+
+/*
+ * IRQ autodetection code..
+ *
+ * This depends on the fact that any interrupt that comes in on to an
+ * unassigned IRQ will cause GxICR_DETECT to be set
+ */
+
+static DECLARE_MUTEX(probe_sem);
+
+/**
+ *     probe_irq_on    - begin an interrupt autodetect
+ *
+ *     Commence probing for an interrupt. The interrupts are scanned
+ *     and a mask of potential interrupt lines is returned.
+ *
+ */
+
+unsigned long probe_irq_on(void)
+{
+       down(&probe_sem);
+       return 0;
+}
+
+/*
+ * Return a mask of triggered interrupts (this
+ * can handle only legacy ISA interrupts).
+ */
+
+/**
+ *     probe_irq_mask - scan a bitmap of interrupt lines
+ *     @val:   mask of interrupts to consider
+ *
+ *     Scan the ISA bus interrupt lines and return a bitmap of
+ *     active interrupts. The interrupt probe logic state is then
+ *     returned to its previous value.
+ *
+ *     Note: we need to scan all the irq's even though we will
+ *     only return ISA irq numbers - just so that we reset them
+ *     all to a known state.
+ */
+unsigned int probe_irq_mask(unsigned long xmask)
+{
+       up(&probe_sem);
+       return 0;
+}
+
+/*
+ * Return the one interrupt that triggered (this can
+ * handle any interrupt source).
+ */
+
+/**
+ *     probe_irq_off   - end an interrupt autodetect
+ *     @xmask: mask of potential interrupts (unused)
+ *
+ *     Scans the unused interrupt lines and returns the line which
+ *     appears to have triggered the interrupt. If no interrupt was
+ *     found then zero is returned. If more than one interrupt is
+ *     found then minus the first candidate is returned to indicate
+ *     their is doubt.
+ *
+ *     The interrupt probe logic state is returned to its previous
+ *     value.
+ *
+ *     BUGS: When used in a module (which arguably shouldnt happen)
+ *     nothing prevents two IRQ probe callers from overlapping. The
+ *     results of this are non-optimal.
+ */
+
+int probe_irq_off(unsigned long xmask)
+{
+       up(&probe_sem);
+       return -1;
+}
+
+/* this was setup_x86_irq but it seems pretty generic */
+int setup_irq(unsigned int irq, struct irqaction *new)
+{
+       struct irq_source *source;
+       struct irq_group *group;
+       struct irq_level *level;
+       struct irqaction **p, **pp;
+       unsigned long flags;
+
+       group = irq_groups[irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP];
+       if (!group)
+               BUG();
+
+       source = group->sources[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
+       if (!source)
+               BUG();
+
+       level = source->level;
+
+       p = &group->actions[irq & (NR_IRQ_ACTIONS_PER_GROUP - 1)];
+
+       /*
+        * Some drivers like serial.c use request_irq() heavily,
+        * so we have to be careful not to interfere with a
+        * running system.
+        */
+       if (new->flags & SA_SAMPLE_RANDOM) {
+               /*
+                * This function might sleep, we want to call it first,
+                * outside of the atomic block.
+                * Yes, this might clear the entropy pool if the wrong
+                * driver is attempted to be loaded, without actually
+                * installing a new handler, but is this really a problem,
+                * only the sysadmin is able to do this.
+                */
+               rand_initialize_irq(irq);
+       }
+
+       /* must juggle the interrupt processing stuff with interrupts disabled */
+       spin_lock_irqsave(&level->lock, flags);
+
+       /* can't share interrupts unless all parties agree to */
+       if (level->usage != 0 && !(level->flags & new->flags & SA_SHIRQ)) {
+               spin_unlock_irqrestore(&level->lock,flags);
+               return -EBUSY;
+       }
+
+       /* add new interrupt at end of irq queue */
+       pp = p;
+       while (*pp)
+               pp = &(*pp)->next;
+
+       *pp = new;
+
+       level->usage++;
+       level->flags = new->flags;
+
+       /* turn the interrupts on */
+       if (level->usage == 1)
+               __clr_MASK(level - frv_irq_levels);
+
+       if (p == pp && group->control)
+               group->control(group, irq & (NR_IRQ_ACTIONS_PER_GROUP - 1), 1);
+
+       spin_unlock_irqrestore(&level->lock, flags);
+       register_irq_proc(irq);
+       return 0;
+}
+
+static struct proc_dir_entry * root_irq_dir;
+static struct proc_dir_entry * irq_dir [NR_IRQS];
+
+#define HEX_DIGITS 8
+
+static unsigned int parse_hex_value (const char *buffer,
+                                    unsigned long count, unsigned long *ret)
+{
+       unsigned char hexnum [HEX_DIGITS];
+       unsigned long value;
+       int i;
+
+       if (!count)
+               return -EINVAL;
+       if (count > HEX_DIGITS)
+               count = HEX_DIGITS;
+       if (copy_from_user(hexnum, buffer, count))
+               return -EFAULT;
+
+       /*
+        * Parse the first 8 characters as a hex string, any non-hex char
+        * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same.
+        */
+       value = 0;
+
+       for (i = 0; i < count; i++) {
+               unsigned int c = hexnum[i];
+
+               switch (c) {
+                       case '0' ... '9': c -= '0'; break;
+                       case 'a' ... 'f': c -= 'a'-10; break;
+                       case 'A' ... 'F': c -= 'A'-10; break;
+               default:
+                       goto out;
+               }
+               value = (value << 4) | c;
+       }
+out:
+       *ret = value;
+       return 0;
+}
+
+
+static int prof_cpu_mask_read_proc (char *page, char **start, off_t off,
+                       int count, int *eof, void *data)
+{
+       unsigned long *mask = (unsigned long *) data;
+       if (count < HEX_DIGITS+1)
+               return -EINVAL;
+       return sprintf (page, "%08lx\n", *mask);
+}
+
+static int prof_cpu_mask_write_proc (struct file *file, const char *buffer,
+                                       unsigned long count, void *data)
+{
+       unsigned long *mask = (unsigned long *) data, full_count = count, err;
+       unsigned long new_value;
+
+       show_state();
+       err = parse_hex_value(buffer, count, &new_value);
+       if (err)
+               return err;
+
+       *mask = new_value;
+       return full_count;
+}
+
+#define MAX_NAMELEN 10
+
+static void register_irq_proc (unsigned int irq)
+{
+       char name [MAX_NAMELEN];
+
+       if (!root_irq_dir || irq_dir[irq])
+               return;
+
+       memset(name, 0, MAX_NAMELEN);
+       sprintf(name, "%d", irq);
+
+       /* create /proc/irq/1234 */
+       irq_dir[irq] = proc_mkdir(name, root_irq_dir);
+}
+
+unsigned long prof_cpu_mask = -1;
+
+void init_irq_proc (void)
+{
+       struct proc_dir_entry *entry;
+       int i;
+
+       /* create /proc/irq */
+       root_irq_dir = proc_mkdir("irq", 0);
+
+       /* create /proc/irq/prof_cpu_mask */
+       entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir);
+       if (!entry)
+           return;
+
+       entry->nlink = 1;
+       entry->data = (void *)&prof_cpu_mask;
+       entry->read_proc = prof_cpu_mask_read_proc;
+       entry->write_proc = prof_cpu_mask_write_proc;
+
+       /*
+        * Create entries for all existing IRQs.
+        */
+       for (i = 0; i < NR_IRQS; i++)
+               register_irq_proc(i);
+}
+
+/*****************************************************************************/
+/*
+ * initialise the interrupt system
+ */
+void __init init_IRQ(void)
+{
+       route_cpu_irqs();
+       fpga_init();
+#ifdef CONFIG_FUJITSU_MB93493
+       route_mb93493_irqs();
+#endif
+} /* end init_IRQ() */
diff --git a/arch/frv/kernel/kernel_thread.S b/arch/frv/kernel/kernel_thread.S
new file mode 100644 (file)
index 0000000..4531c83
--- /dev/null
@@ -0,0 +1,77 @@
+/* kernel_thread.S: kernel thread creation
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/linkage.h>
+#include <asm/unistd.h>
+
+#define CLONE_VM       0x00000100      /* set if VM shared between processes */
+#define        KERN_ERR        "<3>"
+
+       .section .rodata
+kernel_thread_emsg:
+       .asciz  KERN_ERR "failed to create kernel thread: error=%d\n"
+
+       .text
+       .balign         4
+
+###############################################################################
+#
+# Create a kernel thread
+#
+# int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+#
+###############################################################################
+       .globl          kernel_thread
+       .type           kernel_thread,@function
+kernel_thread:
+       or.p            gr8,gr0,gr4
+       or              gr9,gr0,gr5
+
+       # start by forking the current process, but with shared VM
+       setlos.p        #__NR_clone,gr7         ; syscall number
+       ori             gr10,#CLONE_VM,gr8      ; first syscall arg     [clone_flags]
+       sethi.p         #0xe4e4,gr9             ; second syscall arg    [newsp]
+       setlo           #0xe4e4,gr9
+       setlos.p        #0,gr10                 ; third syscall arg     [parent_tidptr]
+       setlos          #0,gr11                 ; fourth syscall arg    [child_tidptr]
+       tira            gr0,#0
+       setlos.p        #4095,gr7
+       andcc           gr8,gr8,gr0,icc0
+       addcc.p         gr8,gr7,gr0,icc1
+       bnelr           icc0,#2
+       bc              icc1,#0,kernel_thread_error
+
+       # now invoke the work function
+       or              gr5,gr0,gr8
+       calll           @(gr4,gr0)
+
+       # and finally exit the thread
+       setlos          #__NR_exit,gr7          ; syscall number
+       tira            gr0,#0
+
+kernel_thread_error:
+       subi            sp,#8,sp
+       movsg           lr,gr4
+       sti             gr8,@(sp,#0)
+       sti.p           gr4,@(sp,#4)
+
+       or              gr8,gr0,gr9
+       sethi.p         %hi(kernel_thread_emsg),gr8
+       setlo           %lo(kernel_thread_emsg),gr8
+
+       call            printk
+
+       ldi             @(sp,#4),gr4
+       ldi             @(sp,#0),gr8
+       subi            sp,#8,sp
+       jmpl            @(gr4,gr0)
+
+       .size           kernel_thread,.-kernel_thread
diff --git a/arch/frv/kernel/local.h b/arch/frv/kernel/local.h
new file mode 100644 (file)
index 0000000..e947176
--- /dev/null
@@ -0,0 +1,56 @@
+/* local.h: local definitions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _FRV_LOCAL_H
+#define _FRV_LOCAL_H
+
+#include <asm/sections.h>
+
+#ifndef __ASSEMBLY__
+
+/* dma.c */
+extern unsigned long frv_dma_inprogress;
+
+extern void frv_dma_pause_all(void);
+extern void frv_dma_resume_all(void);
+
+/* sleep.S */
+extern asmlinkage void frv_cpu_suspend(unsigned long);
+extern asmlinkage void frv_cpu_core_sleep(void);
+
+/* setup.c */
+extern unsigned long __nongprelbss pdm_suspend_mode;
+extern void determine_clocks(int verbose);
+extern int __nongprelbss clock_p0_current;
+extern int __nongprelbss clock_cm_current;
+extern int __nongprelbss clock_cmode_current;
+
+#ifdef CONFIG_PM
+extern int __nongprelbss clock_cmodes_permitted;
+extern unsigned long __nongprelbss clock_bits_settable;
+#define CLOCK_BIT_CM           0x0000000f
+#define CLOCK_BIT_CM_H         0x00000001      /* CLKC.CM can be set to 0 */
+#define CLOCK_BIT_CM_M         0x00000002      /* CLKC.CM can be set to 1 */
+#define CLOCK_BIT_CM_L         0x00000004      /* CLKC.CM can be set to 2 */
+#define CLOCK_BIT_P0           0x00000010      /* CLKC.P0 can be changed */
+#define CLOCK_BIT_CMODE                0x00000020      /* CLKC.CMODE can be changed */
+
+extern void (*__power_switch_wake_setup)(void);
+extern int  (*__power_switch_wake_check)(void);
+extern void (*__power_switch_wake_cleanup)(void);
+#endif
+
+/* time.c */
+extern void time_divisor_init(void);
+
+
+#endif /* __ASSEMBLY__ */
+#endif /* _FRV_LOCAL_H */
diff --git a/arch/frv/kernel/pm-mb93093.c b/arch/frv/kernel/pm-mb93093.c
new file mode 100644 (file)
index 0000000..34d01d7
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * FR-V MB93093 Power Management Routines
+ *
+ * Copyright (c) 2004 Red Hat, Inc.
+ *
+ * Written by: msalter@redhat.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/sysctl.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+#include <asm/mb86943a.h>
+
+#include "local.h"
+
+static unsigned long imask;
+/*
+ * Setup interrupt masks, etc to enable wakeup by power switch
+ */
+static void mb93093_power_switch_setup(void)
+{
+       /* mask all but FPGA interrupt sources. */
+       imask = *(volatile unsigned long *)0xfeff9820;
+       *(volatile unsigned long *)0xfeff9820 = ~(1 << (IRQ_XIRQ2_LEVEL + 16)) & 0xfffe0000;
+}
+
+/*
+ * Cleanup interrupt masks, etc after wakeup by power switch
+ */
+static void mb93093_power_switch_cleanup(void)
+{
+       *(volatile unsigned long *)0xfeff9820 = imask;
+}
+
+/*
+ * Return non-zero if wakeup irq was caused by power switch
+ */
+static int mb93093_power_switch_check(void)
+{
+       return 1;
+}
+
+/*
+ * Initialize power interface
+ */
+static int __init mb93093_pm_init(void)
+{
+       __power_switch_wake_setup = mb93093_power_switch_setup;
+       __power_switch_wake_check = mb93093_power_switch_check;
+       __power_switch_wake_cleanup = mb93093_power_switch_cleanup;
+       return 0;
+}
+
+__initcall(mb93093_pm_init);
+
diff --git a/arch/frv/kernel/pm.c b/arch/frv/kernel/pm.c
new file mode 100644 (file)
index 0000000..1a1e8a1
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * FR-V Power Management Routines
+ *
+ * Copyright (c) 2004 Red Hat, Inc.
+ *
+ * Based on SA1100 version:
+ * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/sysctl.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+#include <asm/mb86943a.h>
+
+#include "local.h"
+
+void (*pm_power_off)(void);
+
+extern void frv_change_cmode(int);
+
+/*
+ * Debug macros
+ */
+#define DEBUG
+
+int pm_do_suspend(void)
+{
+       local_irq_disable();
+
+       __set_LEDS(0xb1);
+
+       /* go zzz */
+       frv_cpu_suspend(pdm_suspend_mode);
+
+       __set_LEDS(0xb2);
+
+       local_irq_enable();
+
+       return 0;
+}
+
+static unsigned long __irq_mask;
+
+/*
+ * Setup interrupt masks, etc to enable wakeup by power switch
+ */
+static void __default_power_switch_setup(void)
+{
+       /* default is to mask all interrupt sources. */
+       __irq_mask = *(unsigned long *)0xfeff9820;
+       *(unsigned long *)0xfeff9820 = 0xfffe0000;
+}
+
+/*
+ * Cleanup interrupt masks, etc after wakeup by power switch
+ */
+static void __default_power_switch_cleanup(void)
+{
+       *(unsigned long *)0xfeff9820 = __irq_mask;
+}
+
+/*
+ * Return non-zero if wakeup irq was caused by power switch
+ */
+static int __default_power_switch_check(void)
+{
+       return 1;
+}
+
+void (*__power_switch_wake_setup)(void) = __default_power_switch_setup;
+int  (*__power_switch_wake_check)(void) = __default_power_switch_check;
+void (*__power_switch_wake_cleanup)(void) = __default_power_switch_cleanup;
+
+int pm_do_bus_sleep(void)
+{
+       local_irq_disable();
+
+       /*
+         * Here is where we need some platform-dependent setup
+        * of the interrupt state so that appropriate wakeup
+        * sources are allowed and all others are masked.
+        */
+       __power_switch_wake_setup();
+
+       __set_LEDS(0xa1);
+
+       /* go zzz
+        *
+        * This is in a loop in case power switch shares an irq with other
+        * devices. The wake_check() tells us if we need to finish waking
+        * or go back to sleep.
+        */
+       do {
+               frv_cpu_suspend(HSR0_PDM_BUS_SLEEP);
+       } while (__power_switch_wake_check && !__power_switch_wake_check());
+
+       __set_LEDS(0xa2);
+
+       /*
+         * Here is where we need some platform-dependent restore
+        * of the interrupt state prior to being called.
+        */
+       __power_switch_wake_cleanup();
+
+       local_irq_enable();
+
+       return 0;
+}
+
+unsigned long sleep_phys_sp(void *sp)
+{
+       return virt_to_phys(sp);
+}
+
+#ifdef CONFIG_SYSCTL
+/*
+ * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6
+ * when all the PM interfaces exist nicely.
+ */
+#define CTL_PM 9899
+#define CTL_PM_SUSPEND 1
+#define CTL_PM_CMODE 2
+#define CTL_PM_P0 4
+#define CTL_PM_CM 5
+
+static int user_atoi(char *ubuf, size_t len)
+{
+       char buf[16];
+       unsigned long ret;
+
+       if (len > 15)
+               return -EINVAL;
+
+       if (copy_from_user(buf, ubuf, len))
+               return -EFAULT;
+
+       buf[len] = 0;
+       ret = simple_strtoul(buf, NULL, 0);
+       if (ret > INT_MAX)
+               return -ERANGE;
+       return ret;
+}
+
+/*
+ * Send us to sleep.
+ */
+static int sysctl_pm_do_suspend(ctl_table *ctl, int write, struct file *filp,
+                               void *buffer, size_t *lenp, loff_t *fpos)
+{
+       int retval, mode;
+
+       if (*lenp <= 0)
+               return -EIO;
+
+       mode = user_atoi(buffer, *lenp);
+       if ((mode != 1) && (mode != 5))
+               return -EINVAL;
+
+       retval = pm_send_all(PM_SUSPEND, (void *)3);
+
+       if (retval == 0) {
+               if (mode == 5)
+                   retval = pm_do_bus_sleep();
+               else
+                   retval = pm_do_suspend();
+               pm_send_all(PM_RESUME, (void *)0);
+       }
+
+       return retval;
+}
+
+static int try_set_cmode(int new_cmode)
+{
+       if (new_cmode > 15)
+               return -EINVAL;
+       if (!(clock_cmodes_permitted & (1<<new_cmode)))
+               return -EINVAL;
+
+       /* tell all the drivers we're suspending */
+       pm_send_all(PM_SUSPEND, (void *)3);
+
+       /* now change cmode */
+       local_irq_disable();
+       frv_dma_pause_all();
+
+       frv_change_cmode(new_cmode);
+
+       determine_clocks(0);
+       time_divisor_init();
+
+#ifdef DEBUG
+       determine_clocks(1);
+#endif
+       frv_dma_resume_all();
+       local_irq_enable();
+
+       /* tell all the drivers we're resuming */
+       pm_send_all(PM_RESUME, (void *)0);
+       return 0;
+}
+
+
+static int cmode_procctl(ctl_table *ctl, int write, struct file *filp,
+                        void *buffer, size_t *lenp, loff_t *fpos)
+{
+       int new_cmode;
+
+       if (!write)
+               return proc_dointvec(ctl, write, filp, buffer, lenp, fpos);
+
+       new_cmode = user_atoi(buffer, *lenp);
+
+       return try_set_cmode(new_cmode)?:*lenp;
+}
+
+static int cmode_sysctl(ctl_table *table, int *name, int nlen,
+                       void *oldval, size_t *oldlenp,
+                       void *newval, size_t newlen, void **context)
+{
+       if (oldval && oldlenp) {
+               size_t oldlen;
+
+               if (get_user(oldlen, oldlenp))
+                       return -EFAULT;
+
+               if (oldlen != sizeof(int))
+                       return -EINVAL;
+
+               if (put_user(clock_cmode_current, (unsigned int *)oldval) ||
+                   put_user(sizeof(int), oldlenp))
+                       return -EFAULT;
+       }
+       if (newval && newlen) {
+               int new_cmode;
+
+               if (newlen != sizeof(int))
+                       return -EINVAL;
+
+               if (get_user(new_cmode, (int *)newval))
+                       return -EFAULT;
+
+               return try_set_cmode(new_cmode)?:1;
+       }
+       return 1;
+}
+
+static int try_set_p0(int new_p0)
+{
+       unsigned long flags, clkc;
+
+       if (new_p0 < 0 || new_p0 > 1)
+               return -EINVAL;
+
+       local_irq_save(flags);
+       __set_PSR(flags & ~PSR_ET);
+
+       frv_dma_pause_all();
+
+       clkc = __get_CLKC();
+       if (new_p0)
+               clkc |= CLKC_P0;
+       else
+               clkc &= ~CLKC_P0;
+       __set_CLKC(clkc);
+
+       determine_clocks(0);
+       time_divisor_init();
+
+#ifdef DEBUG
+       determine_clocks(1);
+#endif
+       frv_dma_resume_all();
+       local_irq_restore(flags);
+       return 0;
+}
+
+static int try_set_cm(int new_cm)
+{
+       unsigned long flags, clkc;
+
+       if (new_cm < 0 || new_cm > 1)
+               return -EINVAL;
+
+       local_irq_save(flags);
+       __set_PSR(flags & ~PSR_ET);
+
+       frv_dma_pause_all();
+
+       clkc = __get_CLKC();
+       clkc &= ~CLKC_CM;
+       clkc |= new_cm;
+       __set_CLKC(clkc);
+
+       determine_clocks(0);
+       time_divisor_init();
+
+#if 1 //def DEBUG
+       determine_clocks(1);
+#endif
+
+       frv_dma_resume_all();
+       local_irq_restore(flags);
+       return 0;
+}
+
+static int p0_procctl(ctl_table *ctl, int write, struct file *filp,
+                     void *buffer, size_t *lenp, loff_t *fpos)
+{
+       int new_p0;
+
+       if (!write)
+               return proc_dointvec(ctl, write, filp, buffer, lenp, fpos);
+
+       new_p0 = user_atoi(buffer, *lenp);
+
+       return try_set_p0(new_p0)?:*lenp;
+}
+
+static int p0_sysctl(ctl_table *table, int *name, int nlen,
+                    void *oldval, size_t *oldlenp,
+                    void *newval, size_t newlen, void **context)
+{
+       if (oldval && oldlenp) {
+               size_t oldlen;
+
+               if (get_user(oldlen, oldlenp))
+                       return -EFAULT;
+
+               if (oldlen != sizeof(int))
+                       return -EINVAL;
+
+               if (put_user(clock_p0_current, (unsigned int *)oldval) ||
+                   put_user(sizeof(int), oldlenp))
+                       return -EFAULT;
+       }
+       if (newval && newlen) {
+               int new_p0;
+
+               if (newlen != sizeof(int))
+                       return -EINVAL;
+
+               if (get_user(new_p0, (int *)newval))
+                       return -EFAULT;
+
+               return try_set_p0(new_p0)?:1;
+       }
+       return 1;
+}
+
+static int cm_procctl(ctl_table *ctl, int write, struct file *filp,
+                     void *buffer, size_t *lenp, loff_t *fpos)
+{
+       int new_cm;
+
+       if (!write)
+               return proc_dointvec(ctl, write, filp, buffer, lenp, fpos);
+
+       new_cm = user_atoi(buffer, *lenp);
+
+       return try_set_cm(new_cm)?:*lenp;
+}
+
+static int cm_sysctl(ctl_table *table, int *name, int nlen,
+                    void *oldval, size_t *oldlenp,
+                    void *newval, size_t newlen, void **context)
+{
+       if (oldval && oldlenp) {
+               size_t oldlen;
+
+               if (get_user(oldlen, oldlenp))
+                       return -EFAULT;
+
+               if (oldlen != sizeof(int))
+                       return -EINVAL;
+
+               if (put_user(clock_cm_current, (unsigned int *)oldval) ||
+                   put_user(sizeof(int), oldlenp))
+                       return -EFAULT;
+       }
+       if (newval && newlen) {
+               int new_cm;
+
+               if (newlen != sizeof(int))
+                       return -EINVAL;
+
+               if (get_user(new_cm, (int *)newval))
+                       return -EFAULT;
+
+               return try_set_cm(new_cm)?:1;
+       }
+       return 1;
+}
+
+
+static struct ctl_table pm_table[] =
+{
+       {CTL_PM_SUSPEND, "suspend", NULL, 0, 0200, NULL, &sysctl_pm_do_suspend},
+       {CTL_PM_CMODE, "cmode", &clock_cmode_current, sizeof(int), 0644, NULL, &cmode_procctl, &cmode_sysctl, NULL},
+       {CTL_PM_P0, "p0", &clock_p0_current, sizeof(int), 0644, NULL, &p0_procctl, &p0_sysctl, NULL},
+       {CTL_PM_CM, "cm", &clock_cm_current, sizeof(int), 0644, NULL, &cm_procctl, &cm_sysctl, NULL},
+       {0}
+};
+
+static struct ctl_table pm_dir_table[] =
+{
+       {CTL_PM, "pm", NULL, 0, 0555, pm_table},
+       {0}
+};
+
+/*
+ * Initialize power interface
+ */
+static int __init pm_init(void)
+{
+       register_sysctl_table(pm_dir_table, 1);
+       return 0;
+}
+
+__initcall(pm_init);
+
+#endif
diff --git a/arch/frv/kernel/process.c b/arch/frv/kernel/process.c
new file mode 100644 (file)
index 0000000..3001b82
--- /dev/null
@@ -0,0 +1,388 @@
+/* process.c: FRV specific parts of process handling
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/process.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.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/elf.h>
+#include <linux/reboot.h>
+#include <linux/interrupt.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/setup.h>
+#include <asm/pgtable.h>
+#include <asm/gdb-stub.h>
+#include <asm/mb-regs.h>
+
+#include "local.h"
+
+asmlinkage void ret_from_fork(void);
+
+#include <asm/pgalloc.h>
+
+struct task_struct *alloc_task_struct(void)
+{
+       struct task_struct *p = kmalloc(THREAD_SIZE, GFP_KERNEL);
+       if (p)
+               atomic_set((atomic_t *)(p+1), 1);
+       return p;
+}
+
+void free_task_struct(struct task_struct *p)
+{
+       if (atomic_dec_and_test((atomic_t *)(p+1)))
+               kfree(p);
+}
+
+static void core_sleep_idle(void)
+{
+#ifdef LED_DEBUG_SLEEP
+       /* Show that we're sleeping... */
+       __set_LEDS(0x55aa);
+#endif
+       frv_cpu_core_sleep();
+#ifdef LED_DEBUG_SLEEP
+       /* ... and that we woke up */
+       __set_LEDS(0);
+#endif
+       mb();
+}
+
+void (*idle)(void) = core_sleep_idle;
+
+/*
+ * The idle thread. There's no useful work to be
+ * done, so just try to conserve power and have a
+ * low exit latency (ie sit in a loop waiting for
+ * somebody to say that they'd like to reschedule)
+ */
+void cpu_idle(void)
+{
+       /* endless idle loop with no priority at all */
+       while (1) {
+               while (!need_resched()) {
+                       irq_stat[smp_processor_id()].idle_timestamp = jiffies;
+
+                       if (!frv_dma_inprogress && idle)
+                               idle();
+               }
+
+               schedule();
+       }
+}
+
+void machine_restart(char * __unused)
+{
+       unsigned long reset_addr;
+#ifdef CONFIG_GDBSTUB
+       gdbstub_exit(0);
+#endif
+
+       if (PSR_IMPLE(__get_PSR()) == PSR_IMPLE_FR551)
+               reset_addr = 0xfefff500;
+       else
+               reset_addr = 0xfeff0500;
+
+       /* Software reset. */
+       asm volatile("      dcef @(gr0,gr0),1 ! membar !"
+                    "      sti     %1,@(%0,0) !"
+                    "      nop ! nop ! nop ! nop ! nop ! "
+                    "      nop ! nop ! nop ! nop ! nop ! "
+                    "      nop ! nop ! nop ! nop ! nop ! "
+                    "      nop ! nop ! nop ! nop ! nop ! "
+                    : : "r" (reset_addr), "r" (1) );
+
+       for (;;)
+               ;
+}
+
+void machine_halt(void)
+{
+#ifdef CONFIG_GDBSTUB
+       gdbstub_exit(0);
+#endif
+
+       for (;;);
+}
+
+void machine_power_off(void)
+{
+#ifdef CONFIG_GDBSTUB
+       gdbstub_exit(0);
+#endif
+
+       for (;;);
+}
+
+void flush_thread(void)
+{
+#if 0 //ndef NO_FPU
+       unsigned long zero = 0;
+#endif
+       set_fs(USER_DS);
+}
+
+inline unsigned long user_stack(const struct pt_regs *regs)
+{
+       while (regs->next_frame)
+               regs = regs->next_frame;
+       return user_mode(regs) ? regs->sp : 0;
+}
+
+asmlinkage int sys_fork(void)
+{
+#ifndef CONFIG_MMU
+       /* fork almost works, enough to trick you into looking elsewhere:-( */
+       return -EINVAL;
+#else
+       return do_fork(SIGCHLD, user_stack(__frame), __frame, 0, NULL, NULL);
+#endif
+}
+
+asmlinkage int sys_vfork(void)
+{
+       return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, user_stack(__frame), __frame, 0,
+                      NULL, NULL);
+}
+
+/*****************************************************************************/
+/*
+ * clone a process
+ * - tlsptr is retrieved by copy_thread()
+ */
+asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
+                        int __user *parent_tidptr, int __user *child_tidptr,
+                        int __user *tlsptr)
+{
+       if (!newsp)
+               newsp = user_stack(__frame);
+       return do_fork(clone_flags, newsp, __frame, 0, parent_tidptr, child_tidptr);
+} /* end sys_clone() */
+
+/*****************************************************************************/
+/*
+ * This gets called before we allocate a new thread and copy
+ * the current task into it.
+ */
+void prepare_to_copy(struct task_struct *tsk)
+{
+       //unlazy_fpu(tsk);
+} /* end prepare_to_copy() */
+
+/*****************************************************************************/
+/*
+ * set up the kernel stack and exception frames for a new process
+ */
+int copy_thread(int nr, unsigned long clone_flags,
+               unsigned long usp, unsigned long topstk,
+               struct task_struct *p, struct pt_regs *regs)
+{
+       struct pt_regs *childregs0, *childregs, *regs0;
+
+       regs0 = __kernel_frame0_ptr;
+       childregs0 = (struct pt_regs *)
+               ((unsigned long) p->thread_info + THREAD_SIZE - USER_CONTEXT_SIZE);
+       childregs = childregs0;
+
+       /* set up the userspace frame (the only place that the USP is stored) */
+       *childregs0 = *regs0;
+
+       childregs0->gr8         = 0;
+       childregs0->sp          = usp;
+       childregs0->next_frame  = NULL;
+
+       /* set up the return kernel frame if called from kernel_thread() */
+       if (regs != regs0) {
+               childregs--;
+               *childregs = *regs;
+               childregs->sp = (unsigned long) childregs0;
+               childregs->next_frame = childregs0;
+               childregs->gr15 = (unsigned long) p->thread_info;
+               childregs->gr29 = (unsigned long) p;
+       }
+
+       p->set_child_tid = p->clear_child_tid = NULL;
+
+       p->thread.frame  = childregs;
+       p->thread.curr   = p;
+       p->thread.sp     = (unsigned long) childregs;
+       p->thread.fp     = 0;
+       p->thread.lr     = 0;
+       p->thread.pc     = (unsigned long) ret_from_fork;
+       p->thread.frame0 = childregs0;
+
+       /* the new TLS pointer is passed in as arg #5 to sys_clone() */
+       if (clone_flags & CLONE_SETTLS)
+               childregs->gr29 = childregs->gr12;
+
+       save_user_regs(p->thread.user);
+
+       return 0;
+} /* end copy_thread() */
+
+/*
+ * fill in the user structure for a core dump..
+ */
+void dump_thread(struct pt_regs *regs, struct user *dump)
+{
+#if 0
+       /* changed the size calculations - should hopefully work better. lbt */
+       dump->magic = CMAGIC;
+       dump->start_code = 0;
+       dump->start_stack = user_stack(regs) & ~(PAGE_SIZE - 1);
+       dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
+       dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
+       dump->u_dsize -= dump->u_tsize;
+       dump->u_ssize = 0;
+
+       if (dump->start_stack < TASK_SIZE)
+               dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
+
+       dump->regs = *(struct user_context *) regs;
+#endif
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage int sys_execve(char *name, char **argv, char **envp)
+{
+       int error;
+       char * filename;
+
+       lock_kernel();
+       filename = getname(name);
+       error = PTR_ERR(filename);
+       if (IS_ERR(filename))
+               goto out;
+       error = do_execve(filename, argv, envp, __frame);
+       putname(filename);
+ out:
+       unlock_kernel();
+       return error;
+}
+
+unsigned long get_wchan(struct task_struct *p)
+{
+       struct pt_regs *regs0;
+       unsigned long fp, pc;
+       unsigned long stack_limit;
+       int count = 0;
+       if (!p || p == current || p->state == TASK_RUNNING)
+               return 0;
+
+       stack_limit = (unsigned long) (p + 1);
+       fp = p->thread.fp;
+       regs0 = p->thread.frame0;
+
+       do {
+               if (fp < stack_limit || fp >= (unsigned long) regs0 || fp & 3)
+                       return 0;
+
+               pc = ((unsigned long *) fp)[2];
+
+               /* FIXME: This depends on the order of these functions. */
+               if (!in_sched_functions(pc))
+                       return pc;
+
+               fp = *(unsigned long *) fp;
+       } while (count++ < 16);
+
+       return 0;
+}
+
+unsigned long thread_saved_pc(struct task_struct *tsk)
+{
+       /* Check whether the thread is blocked in resume() */
+       if (in_sched_functions(tsk->thread.pc))
+               return ((unsigned long *)tsk->thread.fp)[2];
+       else
+               return tsk->thread.pc;
+}
+
+int elf_check_arch(const struct elf32_hdr *hdr)
+{
+       unsigned long hsr0 = __get_HSR(0);
+       unsigned long psr = __get_PSR();
+
+       if (hdr->e_machine != EM_FRV)
+               return 0;
+
+       switch (hdr->e_flags & EF_FRV_GPR_MASK) {
+       case EF_FRV_GPR64:
+               if ((hsr0 & HSR0_GRN) == HSR0_GRN_32)
+                       return 0;
+       case EF_FRV_GPR32:
+       case 0:
+               break;
+       default:
+               return 0;
+       }
+
+       switch (hdr->e_flags & EF_FRV_FPR_MASK) {
+       case EF_FRV_FPR64:
+               if ((hsr0 & HSR0_FRN) == HSR0_FRN_32)
+                       return 0;
+       case EF_FRV_FPR32:
+       case EF_FRV_FPR_NONE:
+       case 0:
+               break;
+       default:
+               return 0;
+       }
+
+       if ((hdr->e_flags & EF_FRV_MULADD) == EF_FRV_MULADD)
+               if (PSR_IMPLE(psr) != PSR_IMPLE_FR405 &&
+                   PSR_IMPLE(psr) != PSR_IMPLE_FR451)
+                       return 0;
+
+       switch (hdr->e_flags & EF_FRV_CPU_MASK) {
+       case EF_FRV_CPU_GENERIC:
+               break;
+       case EF_FRV_CPU_FR300:
+       case EF_FRV_CPU_SIMPLE:
+       case EF_FRV_CPU_TOMCAT:
+       default:
+               return 0;
+       case EF_FRV_CPU_FR400:
+               if (PSR_IMPLE(psr) != PSR_IMPLE_FR401 &&
+                   PSR_IMPLE(psr) != PSR_IMPLE_FR405 &&
+                   PSR_IMPLE(psr) != PSR_IMPLE_FR451 &&
+                   PSR_IMPLE(psr) != PSR_IMPLE_FR551)
+                       return 0;
+               break;
+       case EF_FRV_CPU_FR450:
+               if (PSR_IMPLE(psr) != PSR_IMPLE_FR451)
+                       return 0;
+               break;
+       case EF_FRV_CPU_FR500:
+               if (PSR_IMPLE(psr) != PSR_IMPLE_FR501)
+                       return 0;
+               break;
+       case EF_FRV_CPU_FR550:
+               if (PSR_IMPLE(psr) != PSR_IMPLE_FR551)
+                       return 0;
+               break;
+       }
+
+       return 1;
+}
diff --git a/arch/frv/kernel/ptrace.c b/arch/frv/kernel/ptrace.c
new file mode 100644 (file)
index 0000000..2a0efb7
--- /dev/null
@@ -0,0 +1,764 @@
+/* ptrace.c: FRV specific parts of process tracing
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/ptrace.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/config.h>
+#include <linux/security.h>
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/unistd.h>
+
+/*
+ * does not yet catch signals sent when the child dies.
+ * in exit.c or in signal.c.
+ */
+
+/*
+ * Get contents of register REGNO in task TASK.
+ */
+static inline long get_reg(struct task_struct *task, int regno)
+{
+       struct user_context *user = task->thread.user;
+
+       if (regno < 0 || regno >= PT__END)
+               return 0;
+
+       return ((unsigned long *) user)[regno];
+}
+
+/*
+ * Write contents of register REGNO in task TASK.
+ */
+static inline int put_reg(struct task_struct *task, int regno,
+                         unsigned long data)
+{
+       struct user_context *user = task->thread.user;
+
+       if (regno < 0 || regno >= PT__END)
+               return -EIO;
+
+       switch (regno) {
+       case PT_GR(0):
+               return 0;
+       case PT_PSR:
+       case PT__STATUS:
+               return -EIO;
+       default:
+               ((unsigned long *) user)[regno] = data;
+               return 0;
+       }
+}
+
+/*
+ * check that an address falls within the bounds of the target process's memory mappings
+ */
+static inline int is_user_addr_valid(struct task_struct *child,
+                                    unsigned long start, unsigned long len)
+{
+#ifdef CONFIG_MMU
+       if (start >= PAGE_OFFSET || len > PAGE_OFFSET - start)
+               return -EIO;
+       return 0;
+#else
+       struct vm_list_struct *vml;
+
+       for (vml = child->mm->context.vmlist; vml; vml = vml->next)
+               if (start >= vml->vma->vm_start && start + len <= vml->vma->vm_end)
+                       return 0;
+
+       return -EIO;
+#endif
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Control h/w single stepping
+ */
+void ptrace_disable(struct task_struct *child)
+{
+       child->thread.frame0->__status &= ~REG__STATUS_STEP;
+}
+
+void ptrace_enable(struct task_struct *child)
+{
+       child->thread.frame0->__status |= REG__STATUS_STEP;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+       struct task_struct *child;
+       unsigned long tmp;
+       int ret;
+
+       lock_kernel();
+       ret = -EPERM;
+       if (request == PTRACE_TRACEME) {
+               /* are we already being traced? */
+               if (current->ptrace & PT_PTRACED)
+                       goto out;
+               ret = security_ptrace(current->parent, current);
+               if (ret)
+                       goto out;
+               /* set the ptrace bit in the process flags. */
+               current->ptrace |= PT_PTRACED;
+               ret = 0;
+               goto out;
+       }
+       ret = -ESRCH;
+       read_lock(&tasklist_lock);
+       child = find_task_by_pid(pid);
+       if (child)
+               get_task_struct(child);
+       read_unlock(&tasklist_lock);
+       if (!child)
+               goto out;
+
+       ret = -EPERM;
+       if (pid == 1)           /* you may not mess with init */
+               goto out_tsk;
+
+       if (request == PTRACE_ATTACH) {
+               ret = ptrace_attach(child);
+               goto out_tsk;
+       }
+
+       ret = ptrace_check_attach(child, request == PTRACE_KILL);
+       if (ret < 0)
+               goto out_tsk;
+
+       switch (request) {
+               /* when I and D space are separate, these will need to be fixed. */
+       case PTRACE_PEEKTEXT: /* read word at location addr. */
+       case PTRACE_PEEKDATA: {
+               int copied;
+
+               ret = -EIO;
+               if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0)
+                       break;
+
+               copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+               if (copied != sizeof(tmp))
+                       break;
+
+               ret = put_user(tmp,(unsigned long *) data);
+               break;
+       }
+
+               /* read the word at location addr in the USER area. */
+       case PTRACE_PEEKUSR: {
+               tmp = 0;
+               ret = -EIO;
+               if ((addr & 3) || addr < 0)
+                       break;
+
+               ret = 0;
+               switch (addr >> 2) {
+               case 0 ... PT__END - 1:
+                       tmp = get_reg(child, addr >> 2);
+                       break;
+
+               case PT__END + 0:
+                       tmp = child->mm->end_code - child->mm->start_code;
+                       break;
+
+               case PT__END + 1:
+                       tmp = child->mm->end_data - child->mm->start_data;
+                       break;
+
+               case PT__END + 2:
+                       tmp = child->mm->start_stack - child->mm->start_brk;
+                       break;
+
+               case PT__END + 3:
+                       tmp = child->mm->start_code;
+                       break;
+
+               case PT__END + 4:
+                       tmp = child->mm->start_stack;
+                       break;
+
+               default:
+                       ret = -EIO;
+                       break;
+               }
+
+               if (ret == 0)
+                       ret = put_user(tmp, (unsigned long *) data);
+               break;
+       }
+
+               /* when I and D space are separate, this will have to be fixed. */
+       case PTRACE_POKETEXT: /* write the word at location addr. */
+       case PTRACE_POKEDATA:
+               ret = -EIO;
+               if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0)
+                       break;
+               if (access_process_vm(child, addr, &data, sizeof(data), 1) != sizeof(data))
+                       break;
+               ret = 0;
+               break;
+
+       case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+               ret = -EIO;
+               if ((addr & 3) || addr < 0)
+                       break;
+
+               ret = 0;
+               switch (addr >> 2) {
+               case 0 ... PT__END-1:
+                       ret = put_reg(child, addr >> 2, data);
+                       break;
+
+               default:
+                       ret = -EIO;
+                       break;
+               }
+               break;
+
+       case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+       case PTRACE_CONT: /* restart after signal. */
+               ret = -EIO;
+               if ((unsigned long) data > _NSIG)
+                       break;
+               if (request == PTRACE_SYSCALL)
+                       set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+               else
+                       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+               child->exit_code = data;
+               ptrace_disable(child);
+               wake_up_process(child);
+               ret = 0;
+               break;
+
+               /* make the child exit.  Best I can do is send it a sigkill.
+                * perhaps it should be put in the status that it wants to
+                * exit.
+                */
+       case PTRACE_KILL:
+               ret = 0;
+               if (child->exit_state == EXIT_ZOMBIE)   /* already dead */
+                       break;
+               child->exit_code = SIGKILL;
+               clear_tsk_thread_flag(child, TIF_SINGLESTEP);
+               ptrace_disable(child);
+               wake_up_process(child);
+               break;
+
+       case PTRACE_SINGLESTEP:  /* set the trap flag. */
+               ret = -EIO;
+               if ((unsigned long) data > _NSIG)
+                       break;
+               clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+               ptrace_enable(child);
+               child->exit_code = data;
+               wake_up_process(child);
+               ret = 0;
+               break;
+
+       case PTRACE_DETACH:     /* detach a process that was attached. */
+               ret = ptrace_detach(child, data);
+               break;
+
+       case PTRACE_GETREGS: { /* Get all integer regs from the child. */
+               int i;
+               for (i = 0; i < PT__GPEND; i++) {
+                       tmp = get_reg(child, i);
+                       if (put_user(tmp, (unsigned long *) data)) {
+                               ret = -EFAULT;
+                               break;
+                       }
+                       data += sizeof(long);
+               }
+               ret = 0;
+               break;
+       }
+
+       case PTRACE_SETREGS: { /* Set all integer regs in the child. */
+               int i;
+               for (i = 0; i < PT__GPEND; i++) {
+                       if (get_user(tmp, (unsigned long *) data)) {
+                               ret = -EFAULT;
+                               break;
+                       }
+                       put_reg(child, i, tmp);
+                       data += sizeof(long);
+               }
+               ret = 0;
+               break;
+       }
+
+       case PTRACE_GETFPREGS: { /* Get the child FP/Media state. */
+               ret = 0;
+               if (copy_to_user((void *) data,
+                                &child->thread.user->f,
+                                sizeof(child->thread.user->f)))
+                       ret = -EFAULT;
+               break;
+       }
+
+       case PTRACE_SETFPREGS: { /* Set the child FP/Media state. */
+               ret = 0;
+               if (copy_from_user(&child->thread.user->f,
+                                  (void *) data,
+                                  sizeof(child->thread.user->f)))
+                       ret = -EFAULT;
+               break;
+       }
+
+       case PTRACE_GETFDPIC:
+               tmp = 0;
+               switch (addr) {
+               case PTRACE_GETFDPIC_EXEC:
+                       tmp = child->mm->context.exec_fdpic_loadmap;
+                       break;
+               case PTRACE_GETFDPIC_INTERP:
+                       tmp = child->mm->context.interp_fdpic_loadmap;
+                       break;
+               default:
+                       break;
+               }
+
+               ret = 0;
+               if (put_user(tmp, (unsigned long *) data)) {
+                       ret = -EFAULT;
+                       break;
+               }
+               break;
+
+       default:
+               ret = -EIO;
+               break;
+       }
+out_tsk:
+       put_task_struct(child);
+out:
+       unlock_kernel();
+       return ret;
+}
+
+int __nongprelbss kstrace;
+
+static const struct {
+       const char      *name;
+       unsigned        argmask;
+} __syscall_name_table[NR_syscalls] = {
+       [0]     = { "restart_syscall"                   },
+       [1]     = { "exit",             0x000001        },
+       [2]     = { "fork",             0xffffff        },
+       [3]     = { "read",             0x000141        },
+       [4]     = { "write",            0x000141        },
+       [5]     = { "open",             0x000235        },
+       [6]     = { "close",            0x000001        },
+       [7]     = { "waitpid",          0x000141        },
+       [8]     = { "creat",            0x000025        },
+       [9]     = { "link",             0x000055        },
+       [10]    = { "unlink",           0x000005        },
+       [11]    = { "execve",           0x000445        },
+       [12]    = { "chdir",            0x000005        },
+       [13]    = { "time",             0x000004        },
+       [14]    = { "mknod",            0x000325        },
+       [15]    = { "chmod",            0x000025        },
+       [16]    = { "lchown",           0x000025        },
+       [17]    = { "break" },
+       [18]    = { "oldstat",          0x000045        },
+       [19]    = { "lseek",            0x000131        },
+       [20]    = { "getpid",           0xffffff        },
+       [21]    = { "mount",            0x043555        },
+       [22]    = { "umount",           0x000005        },
+       [23]    = { "setuid",           0x000001        },
+       [24]    = { "getuid",           0xffffff        },
+       [25]    = { "stime",            0x000004        },
+       [26]    = { "ptrace",           0x004413        },
+       [27]    = { "alarm",            0x000001        },
+       [28]    = { "oldfstat",         0x000041        },
+       [29]    = { "pause",            0xffffff        },
+       [30]    = { "utime",            0x000045        },
+       [31]    = { "stty" },
+       [32]    = { "gtty" },
+       [33]    = { "access",           0x000025        },
+       [34]    = { "nice",             0x000001        },
+       [35]    = { "ftime" },
+       [36]    = { "sync",             0xffffff        },
+       [37]    = { "kill",             0x000011        },
+       [38]    = { "rename",           0x000055        },
+       [39]    = { "mkdir",            0x000025        },
+       [40]    = { "rmdir",            0x000005        },
+       [41]    = { "dup",              0x000001        },
+       [42]    = { "pipe",             0x000004        },
+       [43]    = { "times",            0x000004        },
+       [44]    = { "prof" },
+       [45]    = { "brk",              0x000004        },
+       [46]    = { "setgid",           0x000001        },
+       [47]    = { "getgid",           0xffffff        },
+       [48]    = { "signal",           0x000041        },
+       [49]    = { "geteuid",          0xffffff        },
+       [50]    = { "getegid",          0xffffff        },
+       [51]    = { "acct",             0x000005        },
+       [52]    = { "umount2",          0x000035        },
+       [53]    = { "lock" },
+       [54]    = { "ioctl",            0x000331        },
+       [55]    = { "fcntl",            0x000331        },
+       [56]    = { "mpx" },
+       [57]    = { "setpgid",          0x000011        },
+       [58]    = { "ulimit" },
+       [60]    = { "umask",            0x000002        },
+       [61]    = { "chroot",           0x000005        },
+       [62]    = { "ustat",            0x000043        },
+       [63]    = { "dup2",             0x000011        },
+       [64]    = { "getppid",          0xffffff        },
+       [65]    = { "getpgrp",          0xffffff        },
+       [66]    = { "setsid",           0xffffff        },
+       [67]    = { "sigaction" },
+       [68]    = { "sgetmask" },
+       [69]    = { "ssetmask" },
+       [70]    = { "setreuid" },
+       [71]    = { "setregid" },
+       [72]    = { "sigsuspend" },
+       [73]    = { "sigpending" },
+       [74]    = { "sethostname" },
+       [75]    = { "setrlimit" },
+       [76]    = { "getrlimit" },
+       [77]    = { "getrusage" },
+       [78]    = { "gettimeofday" },
+       [79]    = { "settimeofday" },
+       [80]    = { "getgroups" },
+       [81]    = { "setgroups" },
+       [82]    = { "select" },
+       [83]    = { "symlink" },
+       [84]    = { "oldlstat" },
+       [85]    = { "readlink" },
+       [86]    = { "uselib" },
+       [87]    = { "swapon" },
+       [88]    = { "reboot" },
+       [89]    = { "readdir" },
+       [91]    = { "munmap",           0x000034        },
+       [92]    = { "truncate" },
+       [93]    = { "ftruncate" },
+       [94]    = { "fchmod" },
+       [95]    = { "fchown" },
+       [96]    = { "getpriority" },
+       [97]    = { "setpriority" },
+       [99]    = { "statfs" },
+       [100]   = { "fstatfs" },
+       [102]   = { "socketcall" },
+       [103]   = { "syslog" },
+       [104]   = { "setitimer" },
+       [105]   = { "getitimer" },
+       [106]   = { "stat" },
+       [107]   = { "lstat" },
+       [108]   = { "fstat" },
+       [111]   = { "vhangup" },
+       [114]   = { "wait4" },
+       [115]   = { "swapoff" },
+       [116]   = { "sysinfo" },
+       [117]   = { "ipc" },
+       [118]   = { "fsync" },
+       [119]   = { "sigreturn" },
+       [120]   = { "clone" },
+       [121]   = { "setdomainname" },
+       [122]   = { "uname" },
+       [123]   = { "modify_ldt" },
+       [123]   = { "cacheflush" },
+       [124]   = { "adjtimex" },
+       [125]   = { "mprotect" },
+       [126]   = { "sigprocmask" },
+       [127]   = { "create_module" },
+       [128]   = { "init_module" },
+       [129]   = { "delete_module" },
+       [130]   = { "get_kernel_syms" },
+       [131]   = { "quotactl" },
+       [132]   = { "getpgid" },
+       [133]   = { "fchdir" },
+       [134]   = { "bdflush" },
+       [135]   = { "sysfs" },
+       [136]   = { "personality" },
+       [137]   = { "afs_syscall" },
+       [138]   = { "setfsuid" },
+       [139]   = { "setfsgid" },
+       [140]   = { "_llseek",                  0x014331        },
+       [141]   = { "getdents" },
+       [142]   = { "_newselect",               0x000141        },
+       [143]   = { "flock" },
+       [144]   = { "msync" },
+       [145]   = { "readv" },
+       [146]   = { "writev" },
+       [147]   = { "getsid",                   0x000001        },
+       [148]   = { "fdatasync",                0x000001        },
+       [149]   = { "_sysctl",                  0x000004        },
+       [150]   = { "mlock" },
+       [151]   = { "munlock" },
+       [152]   = { "mlockall" },
+       [153]   = { "munlockall" },
+       [154]   = { "sched_setparam" },
+       [155]   = { "sched_getparam" },
+       [156]   = { "sched_setscheduler" },
+       [157]   = { "sched_getscheduler" },
+       [158]   = { "sched_yield" },
+       [159]   = { "sched_get_priority_max" },
+       [160]   = { "sched_get_priority_min" },
+       [161]   = { "sched_rr_get_interval" },
+       [162]   = { "nanosleep",                0x000044        },
+       [163]   = { "mremap" },
+       [164]   = { "setresuid" },
+       [165]   = { "getresuid" },
+       [166]   = { "vm86" },
+       [167]   = { "query_module" },
+       [168]   = { "poll" },
+       [169]   = { "nfsservctl" },
+       [170]   = { "setresgid" },
+       [171]   = { "getresgid" },
+       [172]   = { "prctl",                    0x333331        },
+       [173]   = { "rt_sigreturn",             0xffffff        },
+       [174]   = { "rt_sigaction",             0x001441        },
+       [175]   = { "rt_sigprocmask",           0x001441        },
+       [176]   = { "rt_sigpending",            0x000014        },
+       [177]   = { "rt_sigtimedwait",          0x001444        },
+       [178]   = { "rt_sigqueueinfo",          0x000411        },
+       [179]   = { "rt_sigsuspend",            0x000014        },
+       [180]   = { "pread",                    0x003341        },
+       [181]   = { "pwrite",                   0x003341        },
+       [182]   = { "chown",                    0x000115        },
+       [183]   = { "getcwd" },
+       [184]   = { "capget" },
+       [185]   = { "capset" },
+       [186]   = { "sigaltstack" },
+       [187]   = { "sendfile" },
+       [188]   = { "getpmsg" },
+       [189]   = { "putpmsg" },
+       [190]   = { "vfork",                    0xffffff        },
+       [191]   = { "ugetrlimit" },
+       [192]   = { "mmap2",                    0x313314        },
+       [193]   = { "truncate64" },
+       [194]   = { "ftruncate64" },
+       [195]   = { "stat64",                   0x000045        },
+       [196]   = { "lstat64",                  0x000045        },
+       [197]   = { "fstat64",                  0x000041        },
+       [198]   = { "lchown32" },
+       [199]   = { "getuid32",                 0xffffff        },
+       [200]   = { "getgid32",                 0xffffff        },
+       [201]   = { "geteuid32",                0xffffff        },
+       [202]   = { "getegid32",                0xffffff        },
+       [203]   = { "setreuid32" },
+       [204]   = { "setregid32" },
+       [205]   = { "getgroups32" },
+       [206]   = { "setgroups32" },
+       [207]   = { "fchown32" },
+       [208]   = { "setresuid32" },
+       [209]   = { "getresuid32" },
+       [210]   = { "setresgid32" },
+       [211]   = { "getresgid32" },
+       [212]   = { "chown32" },
+       [213]   = { "setuid32" },
+       [214]   = { "setgid32" },
+       [215]   = { "setfsuid32" },
+       [216]   = { "setfsgid32" },
+       [217]   = { "pivot_root" },
+       [218]   = { "mincore" },
+       [219]   = { "madvise" },
+       [220]   = { "getdents64" },
+       [221]   = { "fcntl64" },
+       [223]   = { "security" },
+       [224]   = { "gettid" },
+       [225]   = { "readahead" },
+       [226]   = { "setxattr" },
+       [227]   = { "lsetxattr" },
+       [228]   = { "fsetxattr" },
+       [229]   = { "getxattr" },
+       [230]   = { "lgetxattr" },
+       [231]   = { "fgetxattr" },
+       [232]   = { "listxattr" },
+       [233]   = { "llistxattr" },
+       [234]   = { "flistxattr" },
+       [235]   = { "removexattr" },
+       [236]   = { "lremovexattr" },
+       [237]   = { "fremovexattr" },
+       [238]   = { "tkill" },
+       [239]   = { "sendfile64" },
+       [240]   = { "futex" },
+       [241]   = { "sched_setaffinity" },
+       [242]   = { "sched_getaffinity" },
+       [243]   = { "set_thread_area" },
+       [244]   = { "get_thread_area" },
+       [245]   = { "io_setup" },
+       [246]   = { "io_destroy" },
+       [247]   = { "io_getevents" },
+       [248]   = { "io_submit" },
+       [249]   = { "io_cancel" },
+       [250]   = { "fadvise64" },
+       [252]   = { "exit_group",               0x000001        },
+       [253]   = { "lookup_dcookie" },
+       [254]   = { "epoll_create" },
+       [255]   = { "epoll_ctl" },
+       [256]   = { "epoll_wait" },
+       [257]   = { "remap_file_pages" },
+       [258]   = { "set_tid_address" },
+       [259]   = { "timer_create" },
+       [260]   = { "timer_settime" },
+       [261]   = { "timer_gettime" },
+       [262]   = { "timer_getoverrun" },
+       [263]   = { "timer_delete" },
+       [264]   = { "clock_settime" },
+       [265]   = { "clock_gettime" },
+       [266]   = { "clock_getres" },
+       [267]   = { "clock_nanosleep" },
+       [268]   = { "statfs64" },
+       [269]   = { "fstatfs64" },
+       [270]   = { "tgkill" },
+       [271]   = { "utimes" },
+       [272]   = { "fadvise64_64" },
+       [273]   = { "vserver" },
+       [274]   = { "mbind" },
+       [275]   = { "get_mempolicy" },
+       [276]   = { "set_mempolicy" },
+       [277]   = { "mq_open" },
+       [278]   = { "mq_unlink" },
+       [279]   = { "mq_timedsend" },
+       [280]   = { "mq_timedreceive" },
+       [281]   = { "mq_notify" },
+       [282]   = { "mq_getsetattr" },
+       [283]   = { "sys_kexec_load" },
+};
+
+asmlinkage void do_syscall_trace(int leaving)
+{
+#if 0
+       unsigned long *argp;
+       const char *name;
+       unsigned argmask;
+       char buffer[16];
+
+       if (!kstrace)
+               return;
+
+       if (!current->mm)
+               return;
+
+       if (__frame->gr7 == __NR_close)
+               return;
+
+#if 0
+       if (__frame->gr7 != __NR_mmap2 &&
+           __frame->gr7 != __NR_vfork &&
+           __frame->gr7 != __NR_execve &&
+           __frame->gr7 != __NR_exit)
+               return;
+#endif
+
+       argmask = 0;
+       name = NULL;
+       if (__frame->gr7 < NR_syscalls) {
+               name = __syscall_name_table[__frame->gr7].name;
+               argmask = __syscall_name_table[__frame->gr7].argmask;
+       }
+       if (!name) {
+               sprintf(buffer, "sys_%lx", __frame->gr7);
+               name = buffer;
+       }
+
+       if (!leaving) {
+               if (!argmask) {
+                       printk(KERN_CRIT "[%d] %s(%lx,%lx,%lx,%lx,%lx,%lx)\n",
+                              current->pid,
+                              name,
+                              __frame->gr8,
+                              __frame->gr9,
+                              __frame->gr10,
+                              __frame->gr11,
+                              __frame->gr12,
+                              __frame->gr13);
+               }
+               else if (argmask == 0xffffff) {
+                       printk(KERN_CRIT "[%d] %s()\n",
+                              current->pid,
+                              name);
+               }
+               else {
+                       printk(KERN_CRIT "[%d] %s(",
+                              current->pid,
+                              name);
+
+                       argp = &__frame->gr8;
+
+                       do {
+                               switch (argmask & 0xf) {
+                               case 1:
+                                       printk("%ld", (long) *argp);
+                                       break;
+                               case 2:
+                                       printk("%lo", *argp);
+                                       break;
+                               case 3:
+                                       printk("%lx", *argp);
+                                       break;
+                               case 4:
+                                       printk("%p", (void *) *argp);
+                                       break;
+                               case 5:
+                                       printk("\"%s\"", (char *) *argp);
+                                       break;
+                               }
+
+                               argp++;
+                               argmask >>= 4;
+                               if (argmask)
+                                       printk(",");
+
+                       } while (argmask);
+
+                       printk(")\n");
+               }
+       }
+       else {
+               if ((int)__frame->gr8 > -4096 && (int)__frame->gr8 < 4096)
+                       printk(KERN_CRIT "[%d] %s() = %ld\n", current->pid, name, __frame->gr8);
+               else
+                       printk(KERN_CRIT "[%d] %s() = %lx\n", current->pid, name, __frame->gr8);
+       }
+       return;
+#endif
+
+       if (!test_thread_flag(TIF_SYSCALL_TRACE))
+               return;
+
+       if (!(current->ptrace & PT_PTRACED))
+               return;
+
+       /* we need to indicate entry or exit to strace */
+       if (leaving)
+               __frame->__status |= REG__STATUS_SYSC_EXIT;
+       else
+               __frame->__status |= REG__STATUS_SYSC_ENTRY;
+
+       ptrace_notify(SIGTRAP);
+
+       /*
+        * this isn't the same as continuing with a signal, but it will do
+        * for normal use.  strace only continues with a signal if the
+        * stopping signal is not SIGTRAP.  -brl
+        */
+       if (current->exit_code) {
+               send_sig(current->exit_code, current, 1);
+               current->exit_code = 0;
+       }
+}
diff --git a/arch/frv/kernel/semaphore.c b/arch/frv/kernel/semaphore.c
new file mode 100644 (file)
index 0000000..5cba9c1
--- /dev/null
@@ -0,0 +1,156 @@
+/* semaphore.c: FR-V semaphores
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from lib/rwsem-spinlock.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.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <asm/semaphore.h>
+
+struct sem_waiter {
+       struct list_head        list;
+       struct task_struct      *task;
+};
+
+#if SEM_DEBUG
+void semtrace(struct semaphore *sem, const char *str)
+{
+       if (sem->debug)
+               printk("[%d] %s({%d,%d})\n",
+                      current->pid,
+                      str,
+                      sem->counter,
+                      list_empty(&sem->wait_list) ? 0 : 1);
+}
+#else
+#define semtrace(SEM,STR) do { } while(0)
+#endif
+
+/*
+ * wait for a token to be granted from a semaphore
+ * - entered with lock held and interrupts disabled
+ */
+void __down(struct semaphore *sem, unsigned long flags)
+{
+       struct task_struct *tsk = current;
+       struct sem_waiter waiter;
+
+       semtrace(sem, "Entering __down");
+
+       /* set up my own style of waitqueue */
+       waiter.task = tsk;
+       get_task_struct(tsk);
+
+       list_add_tail(&waiter.list, &sem->wait_list);
+
+       /* we don't need to touch the semaphore struct anymore */
+       spin_unlock_irqrestore(&sem->wait_lock, flags);
+
+       /* wait to be given the semaphore */
+       set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+
+       for (;;) {
+               if (list_empty(&waiter.list))
+                       break;
+               schedule();
+               set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+       }
+
+       tsk->state = TASK_RUNNING;
+       semtrace(sem, "Leaving __down");
+}
+
+EXPORT_SYMBOL(__down);
+
+/*
+ * interruptibly wait for a token to be granted from a semaphore
+ * - entered with lock held and interrupts disabled
+ */
+int __down_interruptible(struct semaphore *sem, unsigned long flags)
+{
+       struct task_struct *tsk = current;
+       struct sem_waiter waiter;
+       int ret;
+
+       semtrace(sem,"Entering __down_interruptible");
+
+       /* set up my own style of waitqueue */
+       waiter.task = tsk;
+       get_task_struct(tsk);
+
+       list_add_tail(&waiter.list, &sem->wait_list);
+
+       /* we don't need to touch the semaphore struct anymore */
+       set_task_state(tsk, TASK_INTERRUPTIBLE);
+
+       spin_unlock_irqrestore(&sem->wait_lock, flags);
+
+       /* wait to be given the semaphore */
+       ret = 0;
+       for (;;) {
+               if (list_empty(&waiter.list))
+                       break;
+               if (unlikely(signal_pending(current)))
+                       goto interrupted;
+               schedule();
+               set_task_state(tsk, TASK_INTERRUPTIBLE);
+       }
+
+ out:
+       tsk->state = TASK_RUNNING;
+       semtrace(sem, "Leaving __down_interruptible");
+       return ret;
+
+ interrupted:
+       spin_lock_irqsave(&sem->wait_lock, flags);
+
+       if (!list_empty(&waiter.list)) {
+               list_del(&waiter.list);
+               ret = -EINTR;
+       }
+
+       spin_unlock_irqrestore(&sem->wait_lock, flags);
+       if (ret == -EINTR)
+               put_task_struct(current);
+       goto out;
+}
+
+EXPORT_SYMBOL(__down_interruptible);
+
+/*
+ * release a single token back to a semaphore
+ * - entered with lock held and interrupts disabled
+ */
+void __up(struct semaphore *sem)
+{
+       struct task_struct *tsk;
+       struct sem_waiter *waiter;
+
+       semtrace(sem,"Entering __up");
+
+       /* grant the token to the process at the front of the queue */
+       waiter = list_entry(sem->wait_list.next, struct sem_waiter, list);
+
+       /* We must be careful not to touch 'waiter' after we set ->task = NULL.
+        * It is an allocated on the waiter's stack and may become invalid at
+        * any time after that point (due to a wakeup from another source).
+        */
+       list_del_init(&waiter->list);
+       tsk = waiter->task;
+       mb();
+       waiter->task = NULL;
+       wake_up_process(tsk);
+       put_task_struct(tsk);
+
+       semtrace(sem,"Leaving __up");
+}
+
+EXPORT_SYMBOL(__up);
diff --git a/arch/frv/kernel/setup.c b/arch/frv/kernel/setup.c
new file mode 100644 (file)
index 0000000..9ba4979
--- /dev/null
@@ -0,0 +1,1197 @@
+/* setup.c: FRV specific setup
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/setup.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.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/genhd.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/bootmem.h>
+#include <linux/highmem.h>
+#include <linux/seq_file.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+#include <asm/setup.h>
+#include <asm/serial.h>
+#include <asm/irq.h>
+#include <asm/sections.h>
+#include <asm/pgalloc.h>
+#include <asm/busctl-regs.h>
+#include <asm/serial-regs.h>
+#include <asm/timer-regs.h>
+#include <asm/irc-regs.h>
+#include <asm/spr-regs.h>
+#include <asm/mb-regs.h>
+#include <asm/mb93493-regs.h>
+#include <asm/gdb-stub.h>
+#include <asm/irq-routing.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_BLK_DEV_INITRD
+#include <linux/blk.h>
+#include <asm/pgtable.h>
+#endif
+
+#include "local.h"
+
+#ifdef CONFIG_MB93090_MB00
+static void __init mb93090_display(void);
+#endif
+#ifdef CONFIG_MMU
+static void __init setup_linux_memory(void);
+#else
+static void __init setup_uclinux_memory(void);
+#endif
+
+#ifdef CONFIG_CONSOLE
+extern struct consw *conswitchp;
+#ifdef CONFIG_FRAMEBUFFER
+extern struct consw fb_con;
+#endif
+#endif
+
+#ifdef CONFIG_MB93090_MB00
+static char __initdata mb93090_banner[] = "FJ/RH FR-V Linux";
+static char __initdata mb93090_version[] = UTS_RELEASE;
+
+int __nongprelbss mb93090_mb00_detected;
+#endif
+
+const char __frv_unknown_system[] = "unknown";
+const char __frv_mb93091_cb10[] = "mb93091-cb10";
+const char __frv_mb93091_cb11[] = "mb93091-cb11";
+const char __frv_mb93091_cb30[] = "mb93091-cb30";
+const char __frv_mb93091_cb41[] = "mb93091-cb41";
+const char __frv_mb93091_cb60[] = "mb93091-cb60";
+const char __frv_mb93091_cb70[] = "mb93091-cb70";
+const char __frv_mb93091_cb451[] = "mb93091-cb451";
+const char __frv_mb93090_mb00[] = "mb93090-mb00";
+
+const char __frv_mb93493[] = "mb93493";
+
+const char __frv_mb93093[] = "mb93093";
+
+static const char *__nongprelbss cpu_series;
+static const char *__nongprelbss cpu_core;
+static const char *__nongprelbss cpu_silicon;
+static const char *__nongprelbss cpu_mmu;
+static const char *__nongprelbss cpu_system;
+static const char *__nongprelbss cpu_board1;
+static const char *__nongprelbss cpu_board2;
+
+static unsigned long __nongprelbss cpu_psr_all;
+static unsigned long __nongprelbss cpu_hsr0_all;
+
+unsigned long __nongprelbss pdm_suspend_mode;
+
+unsigned long __nongprelbss rom_length;
+unsigned long __nongprelbss memory_start;
+unsigned long __nongprelbss memory_end;
+
+unsigned long __nongprelbss dma_coherent_mem_start;
+unsigned long __nongprelbss dma_coherent_mem_end;
+
+unsigned long __initdata __sdram_old_base;
+unsigned long __initdata num_mappedpages;
+
+struct cpuinfo_frv __nongprelbss boot_cpu_data;
+
+char command_line[COMMAND_LINE_SIZE];
+char __initdata redboot_command_line[COMMAND_LINE_SIZE];
+
+#ifdef CONFIG_PM
+#define __pminit
+#define __pminitdata
+#else
+#define __pminit __init
+#define __pminitdata __initdata
+#endif
+
+struct clock_cmode {
+       uint8_t xbus, sdram, corebus, core, dsu;
+};
+
+#define _frac(N,D) ((N)<<4 | (D))
+#define _x0_16 _frac(1,6)
+#define _x0_25 _frac(1,4)
+#define _x0_33 _frac(1,3)
+#define _x0_375        _frac(3,8)
+#define _x0_5  _frac(1,2)
+#define _x0_66 _frac(2,3)
+#define _x0_75 _frac(3,4)
+#define _x1    _frac(1,1)
+#define _x1_5  _frac(3,2)
+#define _x2    _frac(2,1)
+#define _x3    _frac(3,1)
+#define _x4    _frac(4,1)
+#define _x4_5  _frac(9,2)
+#define _x6    _frac(6,1)
+#define _x8    _frac(8,1)
+#define _x9    _frac(9,1)
+
+int __nongprelbss clock_p0_current;
+int __nongprelbss clock_cm_current;
+int __nongprelbss clock_cmode_current;
+#ifdef CONFIG_PM
+int __nongprelbss clock_cmodes_permitted;
+unsigned long __nongprelbss clock_bits_settable;
+#endif
+
+static struct clock_cmode __pminitdata undef_clock_cmode = { _x1, _x1, _x1, _x1, _x1 };
+
+static struct clock_cmode __pminitdata clock_cmodes_fr401_fr403[16] = {
+       [4]     = {     _x1,    _x1,    _x2,    _x2,    _x0_25  },
+       [5]     = {     _x1,    _x2,    _x4,    _x4,    _x0_5   },
+       [8]     = {     _x1,    _x1,    _x1,    _x2,    _x0_25  },
+       [9]     = {     _x1,    _x2,    _x2,    _x4,    _x0_5   },
+       [11]    = {     _x1,    _x4,    _x4,    _x8,    _x1     },
+       [12]    = {     _x1,    _x1,    _x2,    _x4,    _x0_5   },
+       [13]    = {     _x1,    _x2,    _x4,    _x8,    _x1     },
+};
+
+static struct clock_cmode __pminitdata clock_cmodes_fr405[16] = {
+       [0]     = {     _x1,    _x1,    _x1,    _x1,    _x0_5   },
+       [1]     = {     _x1,    _x1,    _x1,    _x3,    _x0_25  },
+       [2]     = {     _x1,    _x1,    _x2,    _x6,    _x0_5   },
+       [3]     = {     _x1,    _x2,    _x2,    _x6,    _x0_5   },
+       [4]     = {     _x1,    _x1,    _x2,    _x2,    _x0_16  },
+       [8]     = {     _x1,    _x1,    _x1,    _x2,    _x0_16  },
+       [9]     = {     _x1,    _x2,    _x2,    _x4,    _x0_33  },
+       [12]    = {     _x1,    _x1,    _x2,    _x4,    _x0_33  },
+       [14]    = {     _x1,    _x3,    _x3,    _x9,    _x0_75  },
+       [15]    = {     _x1,    _x1_5,  _x1_5,  _x4_5,  _x0_375 },
+
+#define CLOCK_CMODES_PERMITTED_FR405 0xd31f
+};
+
+static struct clock_cmode __pminitdata clock_cmodes_fr555[16] = {
+       [0]     = {     _x1,    _x2,    _x2,    _x4,    _x0_33  },
+       [1]     = {     _x1,    _x3,    _x3,    _x6,    _x0_5   },
+       [2]     = {     _x1,    _x2,    _x4,    _x8,    _x0_66  },
+       [3]     = {     _x1,    _x1_5,  _x3,    _x6,    _x0_5   },
+       [4]     = {     _x1,    _x3,    _x3,    _x9,    _x0_75  },
+       [5]     = {     _x1,    _x2,    _x2,    _x6,    _x0_5   },
+       [6]     = {     _x1,    _x1_5,  _x1_5,  _x4_5,  _x0_375 },
+};
+
+static const struct clock_cmode __pminitdata *clock_cmodes;
+static int __pminitdata clock_doubled;
+
+static struct uart_port __initdata __frv_uart0 = {
+       .uartclk                = 0,
+       .membase                = (char *) UART0_BASE,
+       .irq                    = IRQ_CPU_UART0,
+       .regshift               = 3,
+       .iotype                 = UPIO_MEM,
+       .flags                  = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+};
+
+static struct uart_port __initdata __frv_uart1 = {
+       .uartclk                = 0,
+       .membase                = (char *) UART1_BASE,
+       .irq                    = IRQ_CPU_UART1,
+       .regshift               = 3,
+       .iotype                 = UPIO_MEM,
+       .flags                  = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+};
+
+#if 0
+static void __init printk_xampr(unsigned long ampr, unsigned long amlr, char i_d, int n)
+{
+       unsigned long phys, virt, cxn, size;
+
+#ifdef CONFIG_MMU
+       virt = amlr & 0xffffc000;
+       cxn = amlr & 0x3fff;
+#else
+       virt = ampr & 0xffffc000;
+       cxn = 0;
+#endif
+       phys = ampr & xAMPRx_PPFN;
+       size = 1 << (((ampr & xAMPRx_SS) >> 4) + 17);
+
+       printk("%cAMPR%d: va %08lx-%08lx [pa %08lx] %c%c%c%c [cxn:%04lx]\n",
+              i_d, n,
+              virt, virt + size - 1,
+              phys,
+              ampr & xAMPRx_S  ? 'S' : '-',
+              ampr & xAMPRx_C  ? 'C' : '-',
+              ampr & DAMPRx_WP ? 'W' : '-',
+              ampr & xAMPRx_V  ? 'V' : '-',
+              cxn
+              );
+}
+#endif
+
+/*****************************************************************************/
+/*
+ * dump the memory map
+ */
+static void __init dump_memory_map(void)
+{
+
+#if 0
+       /* dump the protection map */
+       printk_xampr(__get_IAMPR(0),  __get_IAMLR(0),  'I', 0);
+       printk_xampr(__get_IAMPR(1),  __get_IAMLR(1),  'I', 1);
+       printk_xampr(__get_IAMPR(2),  __get_IAMLR(2),  'I', 2);
+       printk_xampr(__get_IAMPR(3),  __get_IAMLR(3),  'I', 3);
+       printk_xampr(__get_IAMPR(4),  __get_IAMLR(4),  'I', 4);
+       printk_xampr(__get_IAMPR(5),  __get_IAMLR(5),  'I', 5);
+       printk_xampr(__get_IAMPR(6),  __get_IAMLR(6),  'I', 6);
+       printk_xampr(__get_IAMPR(7),  __get_IAMLR(7),  'I', 7);
+       printk_xampr(__get_IAMPR(8),  __get_IAMLR(8),  'I', 8);
+       printk_xampr(__get_IAMPR(9),  __get_IAMLR(9),  'i', 9);
+       printk_xampr(__get_IAMPR(10), __get_IAMLR(10), 'I', 10);
+       printk_xampr(__get_IAMPR(11), __get_IAMLR(11), 'I', 11);
+       printk_xampr(__get_IAMPR(12), __get_IAMLR(12), 'I', 12);
+       printk_xampr(__get_IAMPR(13), __get_IAMLR(13), 'I', 13);
+       printk_xampr(__get_IAMPR(14), __get_IAMLR(14), 'I', 14);
+       printk_xampr(__get_IAMPR(15), __get_IAMLR(15), 'I', 15);
+
+       printk_xampr(__get_DAMPR(0),  __get_DAMLR(0),  'D', 0);
+       printk_xampr(__get_DAMPR(1),  __get_DAMLR(1),  'D', 1);
+       printk_xampr(__get_DAMPR(2),  __get_DAMLR(2),  'D', 2);
+       printk_xampr(__get_DAMPR(3),  __get_DAMLR(3),  'D', 3);
+       printk_xampr(__get_DAMPR(4),  __get_DAMLR(4),  'D', 4);
+       printk_xampr(__get_DAMPR(5),  __get_DAMLR(5),  'D', 5);
+       printk_xampr(__get_DAMPR(6),  __get_DAMLR(6),  'D', 6);
+       printk_xampr(__get_DAMPR(7),  __get_DAMLR(7),  'D', 7);
+       printk_xampr(__get_DAMPR(8),  __get_DAMLR(8),  'D', 8);
+       printk_xampr(__get_DAMPR(9),  __get_DAMLR(9),  'D', 9);
+       printk_xampr(__get_DAMPR(10), __get_DAMLR(10), 'D', 10);
+       printk_xampr(__get_DAMPR(11), __get_DAMLR(11), 'D', 11);
+       printk_xampr(__get_DAMPR(12), __get_DAMLR(12), 'D', 12);
+       printk_xampr(__get_DAMPR(13), __get_DAMLR(13), 'D', 13);
+       printk_xampr(__get_DAMPR(14), __get_DAMLR(14), 'D', 14);
+       printk_xampr(__get_DAMPR(15), __get_DAMLR(15), 'D', 15);
+#endif
+
+#if 0
+       /* dump the bus controller registers */
+       printk("LGCR: %08lx\n", __get_LGCR());
+       printk("Master: %08lx-%08lx CR=%08lx\n",
+              __get_LEMBR(), __get_LEMBR() + __get_LEMAM(),
+              __get_LMAICR());
+
+       int loop;
+       for (loop = 1; loop <= 7; loop++) {
+               unsigned long lcr = __get_LCR(loop), lsbr = __get_LSBR(loop);
+               printk("CS#%d: %08lx-%08lx %c%c%c%c%c%c%c%c%c\n",
+                      loop,
+                      lsbr, lsbr + __get_LSAM(loop),
+                      lcr & 0x80000000 ? 'r' : '-',
+                      lcr & 0x40000000 ? 'w' : '-',
+                      lcr & 0x08000000 ? 'b' : '-',
+                      lcr & 0x04000000 ? 'B' : '-',
+                      lcr & 0x02000000 ? 'C' : '-',
+                      lcr & 0x01000000 ? 'D' : '-',
+                      lcr & 0x00800000 ? 'W' : '-',
+                      lcr & 0x00400000 ? 'R' : '-',
+                      (lcr & 0x00030000) == 0x00000000 ? '4' :
+                      (lcr & 0x00030000) == 0x00010000 ? '2' :
+                      (lcr & 0x00030000) == 0x00020000 ? '1' :
+                      '-'
+                      );
+       }
+#endif
+
+#if 0
+       printk("\n");
+#endif
+} /* end dump_memory_map() */
+
+/*****************************************************************************/
+/*
+ * attempt to detect a VDK motherboard and DAV daughter board on an MB93091 system
+ */
+#ifdef CONFIG_MB93091_VDK
+static void __init detect_mb93091(void)
+{
+#ifdef CONFIG_MB93090_MB00
+       /* Detect CB70 without motherboard */
+       if (!(cpu_system == __frv_mb93091_cb70 && ((*(unsigned short *)0xffc00030) & 0x100))) {
+               cpu_board1 = __frv_mb93090_mb00;
+               mb93090_mb00_detected = 1;
+       }
+#endif
+
+#ifdef CONFIG_FUJITSU_MB93493
+       cpu_board2 = __frv_mb93493;
+#endif
+
+} /* end detect_mb93091() */
+#endif
+
+/*****************************************************************************/
+/*
+ * determine the CPU type and set appropriate parameters
+ *
+ * Family     Series      CPU Core    Silicon    Imple  Vers
+ * ----------------------------------------------------------
+ * FR-V --+-> FR400 --+-> FR401 --+-> MB93401     02     00 [1]
+ *        |           |           |
+ *        |           |           +-> MB93401/A   02     01
+ *        |           |           |
+ *        |           |           +-> MB93403     02     02
+ *        |           |
+ *        |           +-> FR405 ----> MB93405     04     00
+ *        |
+ *        +-> FR450 ----> FR451 ----> MB93451     05     00
+ *        |
+ *        +-> FR500 ----> FR501 --+-> MB93501     01     01 [2]
+ *        |                       |
+ *        |                       +-> MB93501/A   01     02
+ *        |
+ *        +-> FR550 --+-> FR551 ----> MB93555     03     01
+ *
+ *  [1] The MB93401 is an obsolete CPU replaced by the MB93401A
+ *  [2] The MB93501 is an obsolete CPU replaced by the MB93501A
+ *
+ * Imple is PSR(Processor Status Register)[31:28].
+ * Vers is PSR(Processor Status Register)[27:24].
+ *
+ * A "Silicon" consists of CPU core and some on-chip peripherals.
+ */
+static void __init determine_cpu(void)
+{
+       unsigned long hsr0 = __get_HSR(0);
+       unsigned long psr = __get_PSR();
+
+       /* work out what selectable services the CPU supports */
+       __set_PSR(psr | PSR_EM | PSR_EF | PSR_CM | PSR_NEM);
+       cpu_psr_all = __get_PSR();
+       __set_PSR(psr);
+
+       __set_HSR(0, hsr0 | HSR0_GRLE | HSR0_GRHE | HSR0_FRLE | HSR0_FRHE);
+       cpu_hsr0_all = __get_HSR(0);
+       __set_HSR(0, hsr0);
+
+       /* derive other service specs from the CPU type */
+       cpu_series              = "unknown";
+       cpu_core                = "unknown";
+       cpu_silicon             = "unknown";
+       cpu_mmu                 = "Prot";
+       cpu_system              = __frv_unknown_system;
+       clock_cmodes            = NULL;
+       clock_doubled           = 0;
+#ifdef CONFIG_PM
+       clock_bits_settable     = CLOCK_BIT_CM_H | CLOCK_BIT_CM_M | CLOCK_BIT_P0;
+#endif
+
+       switch (PSR_IMPLE(psr)) {
+       case PSR_IMPLE_FR401:
+               cpu_series      = "fr400";
+               cpu_core        = "fr401";
+               pdm_suspend_mode = HSR0_PDM_PLL_RUN;
+
+               switch (PSR_VERSION(psr)) {
+               case PSR_VERSION_FR401_MB93401:
+                       cpu_silicon     = "mb93401";
+                       cpu_system      = __frv_mb93091_cb10;
+                       clock_cmodes    = clock_cmodes_fr401_fr403;
+                       clock_doubled   = 1;
+                       break;
+               case PSR_VERSION_FR401_MB93401A:
+                       cpu_silicon     = "mb93401/A";
+                       cpu_system      = __frv_mb93091_cb11;
+                       clock_cmodes    = clock_cmodes_fr401_fr403;
+                       break;
+               case PSR_VERSION_FR401_MB93403:
+                       cpu_silicon     = "mb93403";
+#ifndef CONFIG_MB93093_PDK
+                       cpu_system      = __frv_mb93091_cb30;
+#else
+                       cpu_system      = __frv_mb93093;
+#endif
+                       clock_cmodes    = clock_cmodes_fr401_fr403;
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       case PSR_IMPLE_FR405:
+               cpu_series      = "fr400";
+               cpu_core        = "fr405";
+               pdm_suspend_mode = HSR0_PDM_PLL_STOP;
+
+               switch (PSR_VERSION(psr)) {
+               case PSR_VERSION_FR405_MB93405:
+                       cpu_silicon     = "mb93405";
+                       cpu_system      = __frv_mb93091_cb60;
+                       clock_cmodes    = clock_cmodes_fr405;
+#ifdef CONFIG_PM
+                       clock_bits_settable |= CLOCK_BIT_CMODE;
+                       clock_cmodes_permitted = CLOCK_CMODES_PERMITTED_FR405;
+#endif
+
+                       /* the FPGA on the CB70 has extra registers
+                        * - it has 0x0046 in the VDK_ID FPGA register at 0x1a0, which is
+                        *   how we tell the difference between it and a CB60
+                        */
+                       if (*(volatile unsigned short *) 0xffc001a0 == 0x0046)
+                               cpu_system = __frv_mb93091_cb70;
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       case PSR_IMPLE_FR451:
+               cpu_series      = "fr450";
+               cpu_core        = "fr451";
+               pdm_suspend_mode = HSR0_PDM_PLL_STOP;
+#ifdef CONFIG_PM
+               clock_bits_settable |= CLOCK_BIT_CMODE;
+               clock_cmodes_permitted = CLOCK_CMODES_PERMITTED_FR405;
+#endif
+               switch (PSR_VERSION(psr)) {
+               case PSR_VERSION_FR451_MB93451:
+                       cpu_silicon     = "mb93451";
+                       cpu_mmu         = "Prot, SAT, xSAT, DAT";
+                       cpu_system      = __frv_mb93091_cb451;
+                       clock_cmodes    = clock_cmodes_fr405;
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       case PSR_IMPLE_FR501:
+               cpu_series      = "fr500";
+               cpu_core        = "fr501";
+               pdm_suspend_mode = HSR0_PDM_PLL_STOP;
+
+               switch (PSR_VERSION(psr)) {
+               case PSR_VERSION_FR501_MB93501:  cpu_silicon = "mb93501";   break;
+               case PSR_VERSION_FR501_MB93501A: cpu_silicon = "mb93501/A"; break;
+               default:
+                       break;
+               }
+               break;
+
+       case PSR_IMPLE_FR551:
+               cpu_series      = "fr550";
+               cpu_core        = "fr551";
+               pdm_suspend_mode = HSR0_PDM_PLL_RUN;
+
+               switch (PSR_VERSION(psr)) {
+               case PSR_VERSION_FR551_MB93555:
+                       cpu_silicon     = "mb93555";
+                       cpu_mmu         = "Prot, SAT";
+                       cpu_system      = __frv_mb93091_cb41;
+                       clock_cmodes    = clock_cmodes_fr555;
+                       clock_doubled   = 1;
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       printk("- Series:%s CPU:%s Silicon:%s\n",
+              cpu_series, cpu_core, cpu_silicon);
+
+#ifdef CONFIG_MB93091_VDK
+       detect_mb93091();
+#endif
+
+#if defined(CONFIG_MB93093_PDK) && defined(CONFIG_FUJITSU_MB93493)
+       cpu_board2 = __frv_mb93493;
+#endif
+
+} /* end determine_cpu() */
+
+/*****************************************************************************/
+/*
+ * calculate the bus clock speed
+ */
+void __pminit determine_clocks(int verbose)
+{
+       const struct clock_cmode *mode, *tmode;
+       unsigned long clkc, psr, quot;
+
+       clkc = __get_CLKC();
+       psr = __get_PSR();
+
+       clock_p0_current = !!(clkc & CLKC_P0);
+       clock_cm_current = clkc & CLKC_CM;
+       clock_cmode_current = (clkc & CLKC_CMODE) >> CLKC_CMODE_s;
+
+       if (verbose)
+               printk("psr=%08lx hsr0=%08lx clkc=%08lx\n", psr, __get_HSR(0), clkc);
+
+       /* the CB70 has some alternative ways of setting the clock speed through switches accessed
+        * through the FPGA.  */
+       if (cpu_system == __frv_mb93091_cb70) {
+               unsigned short clkswr = *(volatile unsigned short *) 0xffc00104UL & 0x1fffUL;
+
+               if (clkswr & 0x1000)
+                       __clkin_clock_speed_HZ = 60000000UL;
+               else
+                       __clkin_clock_speed_HZ =
+                               ((clkswr >> 8) & 0xf) * 10000000 +
+                               ((clkswr >> 4) & 0xf) * 1000000 +
+                               ((clkswr     ) & 0xf) * 100000;
+       }
+       /* the FR451 is currently fixed at 24MHz */
+       else if (cpu_system == __frv_mb93091_cb451) {
+               //__clkin_clock_speed_HZ = 24000000UL; // CB451-FPGA
+               unsigned short clkswr = *(volatile unsigned short *) 0xffc00104UL & 0x1fffUL;
+
+               if (clkswr & 0x1000)
+                       __clkin_clock_speed_HZ = 60000000UL;
+               else
+                       __clkin_clock_speed_HZ =
+                               ((clkswr >> 8) & 0xf) * 10000000 +
+                               ((clkswr >> 4) & 0xf) * 1000000 +
+                               ((clkswr     ) & 0xf) * 100000;
+       }
+       /* otherwise determine the clockspeed from VDK or other registers */
+       else {
+               __clkin_clock_speed_HZ = __get_CLKIN();
+       }
+
+       /* look up the appropriate clock relationships table entry */
+       mode = &undef_clock_cmode;
+       if (clock_cmodes) {
+               tmode = &clock_cmodes[(clkc & CLKC_CMODE) >> CLKC_CMODE_s];
+               if (tmode->xbus)
+                       mode = tmode;
+       }
+
+#define CLOCK(SRC,RATIO) ((SRC) * (((RATIO) >> 4) & 0x0f) / ((RATIO) & 0x0f))
+
+       if (clock_doubled)
+               __clkin_clock_speed_HZ <<= 1;
+
+       __ext_bus_clock_speed_HZ        = CLOCK(__clkin_clock_speed_HZ, mode->xbus);
+       __sdram_clock_speed_HZ          = CLOCK(__clkin_clock_speed_HZ, mode->sdram);
+       __dsu_clock_speed_HZ            = CLOCK(__clkin_clock_speed_HZ, mode->dsu);
+
+       switch (clkc & CLKC_CM) {
+       case 0: /* High */
+               __core_bus_clock_speed_HZ       = CLOCK(__clkin_clock_speed_HZ, mode->corebus);
+               __core_clock_speed_HZ           = CLOCK(__clkin_clock_speed_HZ, mode->core);
+               break;
+       case 1: /* Medium */
+               __core_bus_clock_speed_HZ       = CLOCK(__clkin_clock_speed_HZ, mode->sdram);
+               __core_clock_speed_HZ           = CLOCK(__clkin_clock_speed_HZ, mode->sdram);
+               break;
+       case 2: /* Low; not supported */
+       case 3: /* UNDEF */
+               printk("Unsupported CLKC CM %ld\n", clkc & CLKC_CM);
+               panic("Bye");
+       }
+
+       __res_bus_clock_speed_HZ = __ext_bus_clock_speed_HZ;
+       if (clkc & CLKC_P0)
+               __res_bus_clock_speed_HZ >>= 1;
+
+       if (verbose) {
+               printk("CLKIN: %lu.%3.3luMHz\n",
+                      __clkin_clock_speed_HZ / 1000000,
+                      (__clkin_clock_speed_HZ / 1000) % 1000);
+
+               printk("CLKS:"
+                      " ext=%luMHz res=%luMHz sdram=%luMHz cbus=%luMHz core=%luMHz dsu=%luMHz\n",
+                      __ext_bus_clock_speed_HZ / 1000000,
+                      __res_bus_clock_speed_HZ / 1000000,
+                      __sdram_clock_speed_HZ / 1000000,
+                      __core_bus_clock_speed_HZ / 1000000,
+                      __core_clock_speed_HZ / 1000000,
+                      __dsu_clock_speed_HZ / 1000000
+                      );
+       }
+
+       /* calculate the number of __delay() loop iterations per sec (2 insn loop) */
+       __delay_loops_MHz = __core_clock_speed_HZ / (1000000 * 2);
+
+       /* set the serial prescaler */
+       __serial_clock_speed_HZ = __res_bus_clock_speed_HZ;
+       quot = 1;
+       while (__serial_clock_speed_HZ / quot / 16 / 65536 > 3000)
+               quot += 1;
+
+       /* double the divisor if P0 is clear, so that if/when P0 is set, it's still achievable
+        * - we have to be careful - dividing too much can mean we can't get 115200 baud
+        */
+       if (__serial_clock_speed_HZ > 32000000 && !(clkc & CLKC_P0))
+               quot <<= 1;
+
+       __serial_clock_speed_HZ /= quot;
+       __frv_uart0.uartclk = __serial_clock_speed_HZ;
+       __frv_uart1.uartclk = __serial_clock_speed_HZ;
+
+       if (verbose)
+               printk("      uart=%luMHz\n", __serial_clock_speed_HZ / 1000000 * quot);
+
+       while (!(__get_UART0_LSR() & UART_LSR_TEMT))
+               continue;
+
+       while (!(__get_UART1_LSR() & UART_LSR_TEMT))
+               continue;
+
+       __set_UCPVR(quot);
+       __set_UCPSR(0);
+} /* end determine_clocks() */
+
+/*****************************************************************************/
+/*
+ * reserve some DMA consistent memory
+ */
+#ifdef CONFIG_RESERVE_DMA_COHERENT
+static void __init reserve_dma_coherent(void)
+{
+       unsigned long ampr;
+
+       /* find the first non-kernel memory tile and steal it */
+#define __steal_AMPR(r)                                                \
+       if (__get_DAMPR(r) & xAMPRx_V) {                        \
+               ampr = __get_DAMPR(r);                          \
+               __set_DAMPR(r, ampr | xAMPRx_S | xAMPRx_C);     \
+               __set_IAMPR(r, 0);                              \
+               goto found;                                     \
+       }
+
+       __steal_AMPR(1);
+       __steal_AMPR(2);
+       __steal_AMPR(3);
+       __steal_AMPR(4);
+       __steal_AMPR(5);
+       __steal_AMPR(6);
+
+       if (PSR_IMPLE(__get_PSR()) == PSR_IMPLE_FR551) {
+               __steal_AMPR(7);
+               __steal_AMPR(8);
+               __steal_AMPR(9);
+               __steal_AMPR(10);
+               __steal_AMPR(11);
+               __steal_AMPR(12);
+               __steal_AMPR(13);
+               __steal_AMPR(14);
+       }
+
+       /* unable to grant any DMA consistent memory */
+       printk("No DMA consistent memory reserved\n");
+       return;
+
+ found:
+       dma_coherent_mem_start = ampr & xAMPRx_PPFN;
+       ampr &= xAMPRx_SS;
+       ampr >>= 4;
+       ampr = 1 << (ampr - 3 + 20);
+       dma_coherent_mem_end = dma_coherent_mem_start + ampr;
+
+       printk("DMA consistent memory reserved %lx-%lx\n",
+              dma_coherent_mem_start, dma_coherent_mem_end);
+
+} /* end reserve_dma_coherent() */
+#endif
+
+/*****************************************************************************/
+/*
+ * calibrate the delay loop
+ */
+void __init calibrate_delay(void)
+{
+       loops_per_jiffy = __delay_loops_MHz * (1000000 / HZ);
+
+       printk("Calibrating delay loop... %lu.%02lu BogoMIPS\n",
+              loops_per_jiffy / (500000 / HZ),
+              (loops_per_jiffy / (5000 / HZ)) % 100);
+
+} /* end calibrate_delay() */
+
+/*****************************************************************************/
+/*
+ * look through the command line for some things we need to know immediately
+ */
+static void __init parse_cmdline_early(char *cmdline)
+{
+       if (!cmdline)
+               return;
+
+       while (*cmdline) {
+               if (*cmdline == ' ')
+                       cmdline++;
+
+               /* "mem=XXX[kKmM]" sets SDRAM size to <mem>, overriding the value we worked
+                * out from the SDRAM controller mask register
+                */
+               if (!memcmp(cmdline, "mem=", 4)) {
+                       unsigned long long mem_size;
+
+                       mem_size = memparse(cmdline + 4, &cmdline);
+                       memory_end = memory_start + mem_size;
+               }
+
+               while (*cmdline && *cmdline != ' ')
+                       cmdline++;
+       }
+
+} /* end parse_cmdline_early() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+void __init setup_arch(char **cmdline_p)
+{
+#ifdef CONFIG_MMU
+       printk("Linux FR-V port done by Red Hat Inc <dhowells@redhat.com>\n");
+#else
+       printk("uClinux FR-V port done by Red Hat Inc <dhowells@redhat.com>\n");
+#endif
+
+       memcpy(saved_command_line, redboot_command_line, COMMAND_LINE_SIZE);
+
+       determine_cpu();
+       determine_clocks(1);
+
+       /* For printk-directly-beats-on-serial-hardware hack */
+       console_set_baud(115200);
+#ifdef CONFIG_GDBSTUB
+       gdbstub_set_baud(115200);
+#endif
+
+#ifdef CONFIG_RESERVE_DMA_COHERENT
+       reserve_dma_coherent();
+#endif
+       dump_memory_map();
+
+#ifdef CONFIG_MB93090_MB00
+       if (mb93090_mb00_detected)
+               mb93090_display();
+#endif
+
+       /* register those serial ports that are available */
+#ifndef CONFIG_GDBSTUB_UART0
+       __reg(UART0_BASE + UART_IER * 8) = 0;
+       early_serial_setup(&__frv_uart0);
+//     register_serial(&__frv_uart0);
+#endif
+#ifndef CONFIG_GDBSTUB_UART1
+       __reg(UART1_BASE + UART_IER * 8) = 0;
+       early_serial_setup(&__frv_uart1);
+//     register_serial(&__frv_uart1);
+#endif
+
+#if defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH)
+       /* we need to initialize the Flashrom device here since we might
+        * do things with flash early on in the boot
+        */
+       flash_probe();
+#endif
+
+       /* deal with the command line - RedBoot may have passed one to the kernel */
+       memcpy(command_line, saved_command_line, sizeof(command_line));
+       *cmdline_p = &command_line[0];
+       parse_cmdline_early(command_line);
+
+       /* set up the memory description
+        * - by now the stack is part of the init task */
+       printk("Memory %08lx-%08lx\n", memory_start, memory_end);
+
+       if (memory_start == memory_end) BUG();
+
+       init_mm.start_code = (unsigned long) &_stext;
+       init_mm.end_code = (unsigned long) &_etext;
+       init_mm.end_data = (unsigned long) &_edata;
+#if 0 /* DAVIDM - don't set brk just incase someone decides to use it */
+       init_mm.brk = (unsigned long) &_end;
+#else
+       init_mm.brk = (unsigned long) 0;
+#endif
+
+#ifdef DEBUG
+       printk("KERNEL -> TEXT=0x%06x-0x%06x DATA=0x%06x-0x%06x BSS=0x%06x-0x%06x\n",
+              (int) &_stext, (int) &_etext,
+              (int) &_sdata, (int) &_edata,
+              (int) &_sbss, (int) &_ebss);
+#endif
+
+#ifdef CONFIG_VT
+#if defined(CONFIG_VGA_CONSOLE)
+        conswitchp = &vga_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)
+        conswitchp = &dummy_con;
+#endif
+#endif
+
+#ifdef CONFIG_BLK_DEV_BLKMEM
+       ROOT_DEV = MKDEV(BLKMEM_MAJOR,0);
+#endif
+       /*rom_length = (unsigned long)&_flashend - (unsigned long)&_romvec;*/
+
+#ifdef CONFIG_MMU
+       setup_linux_memory();
+#else
+       setup_uclinux_memory();
+#endif
+
+       /* get kmalloc into gear */
+       paging_init();
+
+       /* init DMA */
+       frv_dma_init();
+#ifdef DEBUG
+       printk("Done setup_arch\n");
+#endif
+
+       /* start the decrement timer running */
+//     asm volatile("movgs %0,timerd" :: "r"(10000000));
+//     __set_HSR(0, __get_HSR(0) | HSR0_ETMD);
+
+} /* end setup_arch() */
+
+#if 0
+/*****************************************************************************/
+/*
+ *
+ */
+static int __devinit setup_arch_serial(void)
+{
+       /* register those serial ports that are available */
+#ifndef CONFIG_GDBSTUB_UART0
+       early_serial_setup(&__frv_uart0);
+#endif
+#ifndef CONFIG_GDBSTUB_UART1
+       early_serial_setup(&__frv_uart1);
+#endif
+
+       return 0;
+} /* end setup_arch_serial() */
+
+late_initcall(setup_arch_serial);
+#endif
+
+/*****************************************************************************/
+/*
+ * set up the memory map for normal MMU linux
+ */
+#ifdef CONFIG_MMU
+static void __init setup_linux_memory(void)
+{
+       unsigned long bootmap_size, low_top_pfn, kstart, kend, high_mem;
+
+       kstart  = (unsigned long) &__kernel_image_start - PAGE_OFFSET;
+       kend    = (unsigned long) &__kernel_image_end - PAGE_OFFSET;
+
+       kstart = kstart & PAGE_MASK;
+       kend = (kend + PAGE_SIZE - 1) & PAGE_MASK;
+
+       /* give all the memory to the bootmap allocator,  tell it to put the
+        * boot mem_map immediately following the kernel image
+        */
+       bootmap_size = init_bootmem_node(NODE_DATA(0),
+                                        kend >> PAGE_SHIFT,            /* map addr */
+                                        memory_start >> PAGE_SHIFT,    /* start of RAM */
+                                        memory_end >> PAGE_SHIFT       /* end of RAM */
+                                        );
+
+       /* pass the memory that the kernel can immediately use over to the bootmem allocator */
+       max_mapnr = num_physpages = (memory_end - memory_start) >> PAGE_SHIFT;
+       low_top_pfn = (KERNEL_LOWMEM_END - KERNEL_LOWMEM_START) >> PAGE_SHIFT;
+       high_mem = 0;
+
+       if (num_physpages > low_top_pfn) {
+#ifdef CONFIG_HIGHMEM
+               high_mem = num_physpages - low_top_pfn;
+#else
+               max_mapnr = num_physpages = low_top_pfn;
+#endif
+       }
+       else {
+               low_top_pfn = num_physpages;
+       }
+
+       min_low_pfn = memory_start >> PAGE_SHIFT;
+       max_low_pfn = low_top_pfn;
+       max_pfn = memory_end >> PAGE_SHIFT;
+
+       num_mappedpages = low_top_pfn;
+
+       printk(KERN_NOTICE "%ldMB LOWMEM available.\n", low_top_pfn >> (20 - PAGE_SHIFT));
+
+       free_bootmem(memory_start, low_top_pfn << PAGE_SHIFT);
+
+#ifdef CONFIG_HIGHMEM
+       if (high_mem)
+               printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", high_mem >> (20 - PAGE_SHIFT));
+#endif
+
+       /* take back the memory occupied by the kernel image and the bootmem alloc map */
+       reserve_bootmem(kstart, kend - kstart + bootmap_size);
+
+       /* reserve the memory occupied by the initial ramdisk */
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (LOADER_TYPE && INITRD_START) {
+               if (INITRD_START + INITRD_SIZE <= (low_top_pfn << PAGE_SHIFT)) {
+                       reserve_bootmem(INITRD_START, INITRD_SIZE);
+                       initrd_start = INITRD_START ? INITRD_START + PAGE_OFFSET : 0;
+                       initrd_end = initrd_start + INITRD_SIZE;
+               }
+               else {
+                       printk(KERN_ERR
+                              "initrd extends beyond end of memory (0x%08lx > 0x%08lx)\n"
+                              "disabling initrd\n",
+                              INITRD_START + INITRD_SIZE,
+                              low_top_pfn << PAGE_SHIFT);
+                       initrd_start = 0;
+               }
+       }
+#endif
+
+} /* end setup_linux_memory() */
+#endif
+
+/*****************************************************************************/
+/*
+ * set up the memory map for uClinux
+ */
+#ifndef CONFIG_MMU
+static void __init setup_uclinux_memory(void)
+{
+#ifdef CONFIG_PROTECT_KERNEL
+       unsigned long dampr;
+#endif
+       unsigned long kend;
+       int bootmap_size;
+
+       kend = (unsigned long) &__kernel_image_end;
+       kend = (kend + PAGE_SIZE - 1) & PAGE_MASK;
+
+       /* give all the memory to the bootmap allocator,  tell it to put the
+        * boot mem_map immediately following the kernel image
+        */
+       bootmap_size = init_bootmem_node(NODE_DATA(0),
+                                        kend >> PAGE_SHIFT,            /* map addr */
+                                        memory_start >> PAGE_SHIFT,    /* start of RAM */
+                                        memory_end >> PAGE_SHIFT       /* end of RAM */
+                                        );
+
+       /* free all the usable memory */
+       free_bootmem(memory_start, memory_end - memory_start);
+
+       high_memory = (void *) (memory_end & PAGE_MASK);
+       max_mapnr = num_physpages = ((unsigned long) high_memory - PAGE_OFFSET) >> PAGE_SHIFT;
+
+       min_low_pfn = memory_start >> PAGE_SHIFT;
+       max_low_pfn = memory_end >> PAGE_SHIFT;
+       max_pfn = max_low_pfn;
+
+       /* now take back the bits the core kernel is occupying */
+#ifndef CONFIG_PROTECT_KERNEL
+       reserve_bootmem(kend, bootmap_size);
+       reserve_bootmem((unsigned long) &__kernel_image_start,
+                       kend - (unsigned long) &__kernel_image_start);
+
+#else
+       dampr = __get_DAMPR(0);
+       dampr &= xAMPRx_SS;
+       dampr = (dampr >> 4) + 17;
+       dampr = 1 << dampr;
+
+       reserve_bootmem(__get_DAMPR(0) & xAMPRx_PPFN, dampr);
+#endif
+
+       /* reserve some memory to do uncached DMA through if requested */
+#ifdef CONFIG_RESERVE_DMA_COHERENT
+       if (dma_coherent_mem_start)
+               reserve_bootmem(dma_coherent_mem_start,
+                               dma_coherent_mem_end - dma_coherent_mem_start);
+#endif
+
+} /* end setup_uclinux_memory() */
+#endif
+
+/*****************************************************************************/
+/*
+ * get CPU information for use by procfs
+ */
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+       const char *gr, *fr, *fm, *fp, *cm, *nem, *ble;
+#ifdef CONFIG_PM
+       const char *sep;
+#endif
+
+       gr  = cpu_hsr0_all & HSR0_GRHE  ? "gr0-63"      : "gr0-31";
+       fr  = cpu_hsr0_all & HSR0_FRHE  ? "fr0-63"      : "fr0-31";
+       fm  = cpu_psr_all  & PSR_EM     ? ", Media"     : "";
+       fp  = cpu_psr_all  & PSR_EF     ? ", FPU"       : "";
+       cm  = cpu_psr_all  & PSR_CM     ? ", CCCR"      : "";
+       nem = cpu_psr_all  & PSR_NEM    ? ", NE"        : "";
+       ble = cpu_psr_all  & PSR_BE     ? "BE"          : "LE";
+
+       seq_printf(m,
+                  "CPU-Series:\t%s\n"
+                  "CPU-Core:\t%s, %s, %s%s%s\n"
+                  "CPU:\t\t%s\n"
+                  "MMU:\t\t%s\n"
+                  "FP-Media:\t%s%s%s\n"
+                  "System:\t\t%s",
+                  cpu_series,
+                  cpu_core, gr, ble, cm, nem,
+                  cpu_silicon,
+                  cpu_mmu,
+                  fr, fm, fp,
+                  cpu_system);
+
+       if (cpu_board1)
+               seq_printf(m, ", %s", cpu_board1);
+
+       if (cpu_board2)
+               seq_printf(m, ", %s", cpu_board2);
+
+       seq_printf(m, "\n");
+
+#ifdef CONFIG_PM
+       seq_printf(m, "PM-Controls:");
+       sep = "\t";
+
+       if (clock_bits_settable & CLOCK_BIT_CMODE) {
+               seq_printf(m, "%scmode=0x%04hx", sep, clock_cmodes_permitted);
+               sep = ", ";
+       }
+
+       if (clock_bits_settable & CLOCK_BIT_CM) {
+               seq_printf(m, "%scm=0x%lx", sep, clock_bits_settable & CLOCK_BIT_CM);
+               sep = ", ";
+       }
+
+       if (clock_bits_settable & CLOCK_BIT_P0) {
+               seq_printf(m, "%sp0=0x3", sep);
+               sep = ", ";
+       }
+
+       seq_printf(m, "%ssuspend=0x22\n", sep);
+#endif
+
+       seq_printf(m,
+                  "PM-Status:\tcmode=%d, cm=%d, p0=%d\n",
+                  clock_cmode_current, clock_cm_current, clock_p0_current);
+
+#define print_clk(TAG, VAR) \
+       seq_printf(m, "Clock-" TAG ":\t%lu.%2.2lu MHz\n", VAR / 1000000, (VAR / 10000) % 100)
+
+       print_clk("In",    __clkin_clock_speed_HZ);
+       print_clk("Core",  __core_clock_speed_HZ);
+       print_clk("SDRAM", __sdram_clock_speed_HZ);
+       print_clk("CBus",  __core_bus_clock_speed_HZ);
+       print_clk("Res",   __res_bus_clock_speed_HZ);
+       print_clk("Ext",   __ext_bus_clock_speed_HZ);
+       print_clk("DSU",   __dsu_clock_speed_HZ);
+
+       seq_printf(m,
+                  "BogoMips:\t%lu.%02lu\n",
+                  (loops_per_jiffy * HZ) / 500000, ((loops_per_jiffy * HZ) / 5000) % 100);
+
+       return 0;
+} /* end show_cpuinfo() */
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+       return *pos < NR_CPUS ? (void *) 0x12345678 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+       ++*pos;
+       return c_start(m, pos);
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+struct seq_operations cpuinfo_op = {
+       .start  = c_start,
+       .next   = c_next,
+       .stop   = c_stop,
+       .show   = show_cpuinfo,
+};
+
+void arch_gettod(int *year, int *mon, int *day, int *hour,
+                int *min, int *sec)
+{
+       *year = *mon = *day = *hour = *min = *sec = 0;
+}
+
+/*****************************************************************************/
+/*
+ *
+ */
+#ifdef CONFIG_MB93090_MB00
+static void __init mb93090_sendlcdcmd(uint32_t cmd)
+{
+       unsigned long base = __addr_LCD();
+       int loop;
+
+       /* request reading of the busy flag */
+       __set_LCD(base, LCD_CMD_READ_BUSY);
+       __set_LCD(base, LCD_CMD_READ_BUSY & ~LCD_E);
+
+       /* wait for the busy flag to become clear */
+       for (loop = 10000; loop > 0; loop--)
+               if (!(__get_LCD(base) & 0x80))
+                       break;
+
+       /* send the command */
+       __set_LCD(base, cmd);
+       __set_LCD(base, cmd & ~LCD_E);
+
+} /* end mb93090_sendlcdcmd() */
+
+/*****************************************************************************/
+/*
+ * write to the MB93090 LEDs and LCD
+ */
+static void __init mb93090_display(void)
+{
+       const char *p;
+
+       __set_LEDS(0);
+
+       /* set up the LCD */
+       mb93090_sendlcdcmd(LCD_CMD_CLEAR);
+       mb93090_sendlcdcmd(LCD_CMD_FUNCSET(1,1,0));
+       mb93090_sendlcdcmd(LCD_CMD_ON(0,0));
+       mb93090_sendlcdcmd(LCD_CMD_HOME);
+
+       mb93090_sendlcdcmd(LCD_CMD_SET_DD_ADDR(0));
+       for (p = mb93090_banner; *p; p++)
+               mb93090_sendlcdcmd(LCD_DATA_WRITE(*p));
+
+       mb93090_sendlcdcmd(LCD_CMD_SET_DD_ADDR(64));
+       for (p = mb93090_version; *p; p++)
+               mb93090_sendlcdcmd(LCD_DATA_WRITE(*p));
+
+} /* end mb93090_display() */
+
+#endif // CONFIG_MB93090_MB00
diff --git a/arch/frv/kernel/signal.c b/arch/frv/kernel/signal.c
new file mode 100644 (file)
index 0000000..1fccc1c
--- /dev/null
@@ -0,0 +1,589 @@
+/* signal.c: FRV specific bits of signal handling
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/signal.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.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/personality.h>
+#include <linux/suspend.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/cacheflush.h>
+
+#define DEBUG_SIG 0
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+struct fdpic_func_descriptor {
+       unsigned long   text;
+       unsigned long   GOT;
+};
+
+asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset);
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+asmlinkage int sys_sigsuspend(int history0, int history1, old_sigset_t mask)
+{
+       sigset_t saveset;
+
+       mask &= _BLOCKABLE;
+       spin_lock_irq(&current->sighand->siglock);
+       saveset = current->blocked;
+       siginitset(&current->blocked, mask);
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       __frame->gr8 = -EINTR;
+       while (1) {
+               current->state = TASK_INTERRUPTIBLE;
+               schedule();
+               if (do_signal(__frame, &saveset))
+                       /* return the signal number as the return value of this function
+                        * - this is an utterly evil hack. syscalls should not invoke do_signal()
+                        *   as entry.S sets regs->gr8 to the return value of the system call
+                        * - we can't just use sigpending() as we'd have to discard SIG_IGN signals
+                        *   and call waitpid() if SIGCHLD needed discarding
+                        * - this only works on the i386 because it passes arguments to the signal
+                        *   handler on the stack, and the return value in EAX is effectively
+                        *   discarded
+                        */
+                       return __frame->gr8;
+       }
+}
+
+asmlinkage int sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize)
+{
+       sigset_t saveset, newset;
+
+       /* XXX: Don't preclude handling different sized sigset_t's.  */
+       if (sigsetsize != sizeof(sigset_t))
+               return -EINVAL;
+
+       if (copy_from_user(&newset, unewset, sizeof(newset)))
+               return -EFAULT;
+       sigdelsetmask(&newset, ~_BLOCKABLE);
+
+       spin_lock_irq(&current->sighand->siglock);
+       saveset = current->blocked;
+       current->blocked = newset;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       __frame->gr8 = -EINTR;
+       while (1) {
+               current->state = TASK_INTERRUPTIBLE;
+               schedule();
+               if (do_signal(__frame, &saveset))
+                       /* return the signal number as the return value of this function
+                        * - this is an utterly evil hack. syscalls should not invoke do_signal()
+                        *   as entry.S sets regs->gr8 to the return value of the system call
+                        * - we can't just use sigpending() as we'd have to discard SIG_IGN signals
+                        *   and call waitpid() if SIGCHLD needed discarding
+                        * - this only works on the i386 because it passes arguments to the signal
+                        *   handler on the stack, and the return value in EAX is effectively
+                        *   discarded
+                        */
+                       return __frame->gr8;
+       }
+}
+
+asmlinkage int sys_sigaction(int sig,
+                            const struct old_sigaction __user *act,
+                            struct old_sigaction __user *oact)
+{
+       struct k_sigaction new_ka, old_ka;
+       int ret;
+
+       if (act) {
+               old_sigset_t mask;
+               if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
+                   __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+                   __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
+                       return -EFAULT;
+               __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+               __get_user(mask, &act->sa_mask);
+               siginitset(&new_ka.sa.sa_mask, mask);
+       }
+
+       ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+       if (!ret && oact) {
+               if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
+                   __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+                   __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
+                       return -EFAULT;
+               __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+               __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
+       }
+
+       return ret;
+}
+
+asmlinkage
+int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
+{
+       return do_sigaltstack(uss, uoss, __frame->sp);
+}
+
+
+/*
+ * Do a signal return; undo the signal stack.
+ */
+
+struct sigframe
+{
+       void (*pretcode)(void);
+       int sig;
+       struct sigcontext sc;
+       unsigned long extramask[_NSIG_WORDS-1];
+       uint32_t retcode[2];
+};
+
+struct rt_sigframe
+{
+       void (*pretcode)(void);
+       int sig;
+       struct siginfo *pinfo;
+       void *puc;
+       struct siginfo info;
+       struct ucontext uc;
+       uint32_t retcode[2];
+};
+
+static int restore_sigcontext(struct sigcontext __user *sc, int *_gr8)
+{
+       struct user_context *user = current->thread.user;
+       unsigned long tbr, psr;
+
+       tbr = user->i.tbr;
+       psr = user->i.psr;
+       if (copy_from_user(user, &sc->sc_context, sizeof(sc->sc_context)))
+               goto badframe;
+       user->i.tbr = tbr;
+       user->i.psr = psr;
+
+       restore_user_regs(user);
+
+       user->i.syscallno = -1;         /* disable syscall checks */
+
+       *_gr8 = user->i.gr[8];
+       return 0;
+
+ badframe:
+       return 1;
+}
+
+asmlinkage int sys_sigreturn(void)
+{
+       struct sigframe __user *frame = (struct sigframe __user *) __frame->sp;
+       sigset_t set;
+       int gr8;
+
+       if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+               goto badframe;
+       if (__get_user(set.sig[0], &frame->sc.sc_oldmask))
+               goto badframe;
+
+       if (_NSIG_WORDS > 1 &&
+           __copy_from_user(&set.sig[1], &frame->extramask, sizeof(frame->extramask)))
+               goto badframe;
+
+       sigdelsetmask(&set, ~_BLOCKABLE);
+       spin_lock_irq(&current->sighand->siglock);
+       current->blocked = set;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       if (restore_sigcontext(&frame->sc, &gr8))
+               goto badframe;
+       return gr8;
+
+ badframe:
+       force_sig(SIGSEGV, current);
+       return 0;
+}
+
+asmlinkage int sys_rt_sigreturn(void)
+{
+       struct rt_sigframe __user *frame = (struct rt_sigframe __user *) __frame->sp;
+       sigset_t set;
+       stack_t st;
+       int gr8;
+
+       if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+               goto badframe;
+       if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+               goto badframe;
+
+       sigdelsetmask(&set, ~_BLOCKABLE);
+       spin_lock_irq(&current->sighand->siglock);
+       current->blocked = set;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       if (restore_sigcontext(&frame->uc.uc_mcontext, &gr8))
+               goto badframe;
+
+       if (do_sigaltstack(&frame->uc.uc_stack, NULL, __frame->sp) == -EFAULT)
+               goto badframe;
+
+       return gr8;
+
+badframe:
+       force_sig(SIGSEGV, current);
+       return 0;
+}
+
+/*
+ * Set up a signal frame
+ */
+static int setup_sigcontext(struct sigcontext __user *sc, unsigned long mask)
+{
+       save_user_regs(current->thread.user);
+
+       if (copy_to_user(&sc->sc_context, current->thread.user, sizeof(sc->sc_context)) != 0)
+               goto badframe;
+
+       /* non-iBCS2 extensions.. */
+       if (__put_user(mask, &sc->sc_oldmask) < 0)
+               goto badframe;
+
+       return 0;
+
+ badframe:
+       return 1;
+}
+
+/*****************************************************************************/
+/*
+ * Determine which stack to use..
+ */
+static inline void __user *get_sigframe(struct k_sigaction *ka,
+                                       struct pt_regs *regs,
+                                       size_t frame_size)
+{
+       unsigned long sp;
+
+       /* Default to using normal stack */
+       sp = regs->sp;
+
+       /* This is the X/Open sanctioned signal stack switching.  */
+       if (ka->sa.sa_flags & SA_ONSTACK) {
+               if (! on_sig_stack(sp))
+                       sp = current->sas_ss_sp + current->sas_ss_size;
+       }
+
+       return (void __user *) ((sp - frame_size) & ~7UL);
+} /* end get_sigframe() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+static void setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs)
+{
+       struct sigframe __user *frame;
+       int rsig;
+
+       frame = get_sigframe(ka, regs, sizeof(*frame));
+
+       if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+               goto give_sigsegv;
+
+       rsig = sig;
+       if (sig < 32 &&
+           __current_thread_info->exec_domain &&
+           __current_thread_info->exec_domain->signal_invmap)
+               rsig = __current_thread_info->exec_domain->signal_invmap[sig];
+
+       if (__put_user(rsig, &frame->sig) < 0)
+               goto give_sigsegv;
+
+       if (setup_sigcontext(&frame->sc, set->sig[0]))
+               goto give_sigsegv;
+
+       if (_NSIG_WORDS > 1) {
+               if (__copy_to_user(frame->extramask, &set->sig[1],
+                                  sizeof(frame->extramask)))
+                       goto give_sigsegv;
+       }
+
+       /* Set up to return from userspace.  If provided, use a stub
+        * already in userspace.  */
+       if (ka->sa.sa_flags & SA_RESTORER) {
+               if (__put_user(ka->sa.sa_restorer, &frame->pretcode) < 0)
+                       goto give_sigsegv;
+       }
+       else {
+               /* Set up the following code on the stack:
+                *      setlos  #__NR_sigreturn,gr7
+                *      tira    gr0,0
+                */
+               if (__put_user((void (*)(void))frame->retcode, &frame->pretcode) ||
+                   __put_user(0x8efc0000|__NR_sigreturn, &frame->retcode[0]) ||
+                   __put_user(0xc0700000, &frame->retcode[1]))
+                       goto give_sigsegv;
+
+               flush_icache_range((unsigned long) frame->retcode,
+                                  (unsigned long) (frame->retcode + 2));
+       }
+
+       /* set up registers for signal handler */
+       regs->sp   = (unsigned long) frame;
+       regs->lr   = (unsigned long) &frame->retcode;
+       regs->gr8  = sig;
+
+       if (get_personality & FDPIC_FUNCPTRS) {
+               struct fdpic_func_descriptor __user *funcptr =
+                       (struct fdpic_func_descriptor *) ka->sa.sa_handler;
+               __get_user(regs->pc, &funcptr->text);
+               __get_user(regs->gr15, &funcptr->GOT);
+       } else {
+               regs->pc   = (unsigned long) ka->sa.sa_handler;
+               regs->gr15 = 0;
+       }
+
+       set_fs(USER_DS);
+
+#if DEBUG_SIG
+       printk("SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
+               sig, current->comm, current->pid, frame, regs->pc, frame->pretcode);
+#endif
+
+       return;
+
+give_sigsegv:
+       if (sig == SIGSEGV)
+               ka->sa.sa_handler = SIG_DFL;
+
+       force_sig(SIGSEGV, current);
+} /* end setup_frame() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+                          sigset_t *set, struct pt_regs * regs)
+{
+       struct rt_sigframe __user *frame;
+       int rsig;
+
+       frame = get_sigframe(ka, regs, sizeof(*frame));
+
+       if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+               goto give_sigsegv;
+
+       rsig = sig;
+       if (sig < 32 &&
+           __current_thread_info->exec_domain &&
+           __current_thread_info->exec_domain->signal_invmap)
+               rsig = __current_thread_info->exec_domain->signal_invmap[sig];
+
+       if (__put_user(rsig,            &frame->sig) ||
+           __put_user(&frame->info,    &frame->pinfo) ||
+           __put_user(&frame->uc,      &frame->puc))
+               goto give_sigsegv;
+
+       if (copy_siginfo_to_user(&frame->info, info))
+               goto give_sigsegv;
+
+       /* Create the ucontext.  */
+       if (__put_user(0, &frame->uc.uc_flags) ||
+           __put_user(0, &frame->uc.uc_link) ||
+           __put_user((void*)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp) ||
+           __put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags) ||
+           __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size))
+               goto give_sigsegv;
+
+       if (setup_sigcontext(&frame->uc.uc_mcontext, set->sig[0]))
+               goto give_sigsegv;
+
+       if (__copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)))
+               goto give_sigsegv;
+
+       /* Set up to return from userspace.  If provided, use a stub
+        * already in userspace.  */
+       if (ka->sa.sa_flags & SA_RESTORER) {
+               if (__put_user(ka->sa.sa_restorer, &frame->pretcode))
+                       goto give_sigsegv;
+       }
+       else {
+               /* Set up the following code on the stack:
+                *      setlos  #__NR_sigreturn,gr7
+                *      tira    gr0,0
+                */
+               if (__put_user((void (*)(void))frame->retcode, &frame->pretcode) ||
+                   __put_user(0x8efc0000|__NR_rt_sigreturn, &frame->retcode[0]) ||
+                   __put_user(0xc0700000, &frame->retcode[1]))
+                       goto give_sigsegv;
+
+               flush_icache_range((unsigned long) frame->retcode,
+                                  (unsigned long) (frame->retcode + 2));
+       }
+
+       /* Set up registers for signal handler */
+       regs->sp  = (unsigned long) frame;
+       regs->lr   = (unsigned long) &frame->retcode;
+       regs->gr8 = sig;
+       regs->gr9 = (unsigned long) &frame->info;
+
+       if (get_personality & FDPIC_FUNCPTRS) {
+               struct fdpic_func_descriptor *funcptr =
+                       (struct fdpic_func_descriptor __user *) ka->sa.sa_handler;
+               __get_user(regs->pc, &funcptr->text);
+               __get_user(regs->gr15, &funcptr->GOT);
+       } else {
+               regs->pc   = (unsigned long) ka->sa.sa_handler;
+               regs->gr15 = 0;
+       }
+
+       set_fs(USER_DS);
+
+#if DEBUG_SIG
+       printk("SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n",
+               sig, current->comm, current->pid, frame, regs->pc, frame->pretcode);
+#endif
+
+       return;
+
+give_sigsegv:
+       if (sig == SIGSEGV)
+               ka->sa.sa_handler = SIG_DFL;
+       force_sig(SIGSEGV, current);
+
+} /* end setup_rt_frame() */
+
+/*****************************************************************************/
+/*
+ * OK, we're invoking a handler
+ */
+static void handle_signal(unsigned long sig, siginfo_t *info,
+                         struct k_sigaction *ka, sigset_t *oldset,
+                         struct pt_regs *regs)
+{
+       /* Are we from a system call? */
+       if (in_syscall(regs)) {
+               /* If so, check system call restarting.. */
+               switch (regs->gr8) {
+               case -ERESTART_RESTARTBLOCK:
+               case -ERESTARTNOHAND:
+                       regs->gr8 = -EINTR;
+                       break;
+
+               case -ERESTARTSYS:
+                       if (!(ka->sa.sa_flags & SA_RESTART)) {
+                               regs->gr8 = -EINTR;
+                               break;
+                       }
+                       /* fallthrough */
+               case -ERESTARTNOINTR:
+                       regs->gr8 = regs->orig_gr8;
+                       regs->pc -= 4;
+               }
+       }
+
+       /* Set up the stack frame */
+       if (ka->sa.sa_flags & SA_SIGINFO)
+               setup_rt_frame(sig, ka, info, oldset, regs);
+       else
+               setup_frame(sig, ka, oldset, regs);
+
+       if (!(ka->sa.sa_flags & SA_NODEFER)) {
+               spin_lock_irq(&current->sighand->siglock);
+               sigorsets(&current->blocked, &current->blocked, &ka->sa.sa_mask);
+               sigaddset(&current->blocked, sig);
+               recalc_sigpending();
+               spin_unlock_irq(&current->sighand->siglock);
+       }
+} /* end handle_signal() */
+
+/*****************************************************************************/
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ */
+int do_signal(struct pt_regs *regs, sigset_t *oldset)
+{
+       struct k_sigaction ka;
+       siginfo_t info;
+       int signr;
+
+       /*
+        * We want the common case to go fast, which
+        * is why we may in certain cases get here from
+        * kernel mode. Just return without doing anything
+        * if so.
+        */
+       if (!user_mode(regs))
+               return 1;
+
+       if (current->flags & PF_FREEZE) {
+               refrigerator(0);
+               goto no_signal;
+       }
+
+       if (!oldset)
+               oldset = &current->blocked;
+
+       signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+       if (signr > 0) {
+               handle_signal(signr, &info, &ka, oldset, regs);
+               return 1;
+       }
+
+ no_signal:
+       /* Did we come from a system call? */
+       if (regs->syscallno >= 0) {
+               /* Restart the system call - no handlers present */
+               if (regs->gr8 == -ERESTARTNOHAND ||
+                   regs->gr8 == -ERESTARTSYS ||
+                   regs->gr8 == -ERESTARTNOINTR) {
+                       regs->gr8 = regs->orig_gr8;
+                       regs->pc -= 4;
+               }
+
+               if (regs->gr8 == -ERESTART_RESTARTBLOCK){
+                       regs->gr8 = __NR_restart_syscall;
+                       regs->pc -= 4;
+               }
+       }
+
+       return 0;
+} /* end do_signal() */
+
+/*****************************************************************************/
+/*
+ * notification of userspace execution resumption
+ * - triggered by current->work.notify_resume
+ */
+asmlinkage void do_notify_resume(__u32 thread_info_flags)
+{
+       /* pending single-step? */
+       if (thread_info_flags & _TIF_SINGLESTEP)
+               clear_thread_flag(TIF_SINGLESTEP);
+
+       /* deal with pending signal delivery */
+       if (thread_info_flags & _TIF_SIGPENDING)
+               do_signal(__frame, NULL);
+
+} /* end do_notify_resume() */
diff --git a/arch/frv/kernel/sleep.S b/arch/frv/kernel/sleep.S
new file mode 100644 (file)
index 0000000..e6079b8
--- /dev/null
@@ -0,0 +1,374 @@
+/* sleep.S: power saving mode entry
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Woodhouse (dwmw2@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/errno.h>
+#include <asm/cache.h>
+#include <asm/spr-regs.h>
+
+#define __addr_MASK    0xfeff9820      /* interrupt controller mask */
+
+#define __addr_FR55X_DRCN      0xfeff0218      /* Address of DRCN register */
+#define FR55X_DSTS_OFFSET      -4              /* Offset from DRCN to DSTS */
+#define FR55X_SDRAMC_DSTS_SSI  0x00000002      /* indicates that the SDRAM is in self-refresh mode */
+
+#define __addr_FR4XX_DRCN      0xfe000430      /* Address of DRCN register */
+#define FR4XX_DSTS_OFFSET      -8              /* Offset from DRCN to DSTS */
+#define FR4XX_SDRAMC_DSTS_SSI  0x00000001      /* indicates that the SDRAM is in self-refresh mode */
+
+#define SDRAMC_DRCN_SR 0x00000001      /* transition SDRAM into self-refresh mode */
+
+       .section        .bss
+       .balign         8
+       .globl          __sleep_save_area
+__sleep_save_area:
+       .space          16
+
+
+       .text
+       .balign         4
+
+.macro li v r
+       sethi.p         %hi(\v),\r
+       setlo           %lo(\v),\r
+.endm
+
+#ifdef CONFIG_PM
+###############################################################################
+#
+# CPU suspension routine
+# - void frv_cpu_suspend(unsigned long pdm_mode)
+#
+###############################################################################
+       .globl          frv_cpu_suspend
+        .type          frv_cpu_suspend,@function
+frv_cpu_suspend:
+
+       #----------------------------------------------------
+       # save hsr0, psr, isr, and lr for resume code
+       #----------------------------------------------------
+       li              __sleep_save_area,gr11
+
+       movsg           hsr0,gr4
+       movsg           psr,gr5
+       movsg           isr,gr6
+       movsg           lr,gr7
+       stdi            gr4,@(gr11,#0)
+       stdi            gr6,@(gr11,#8)
+
+       # store the return address from sleep in GR14, and its complement in GR13 as a check
+       li              __ramboot_resume,gr14
+#ifdef CONFIG_MMU
+       # Resume via RAMBOOT# will turn MMU off, so bootloader needs a physical address.
+       sethi.p         %hi(__page_offset),gr13
+       setlo           %lo(__page_offset),gr13
+       sub             gr14,gr13,gr14
+#endif
+       not             gr14,gr13
+
+       #----------------------------------------------------
+       # preload and lock into icache that code which may have to run
+       # when dram is in self-refresh state.
+       #----------------------------------------------------
+       movsg           hsr0, gr3
+       li              HSR0_ICE,gr4
+       or              gr3,gr4,gr3
+       movgs           gr3,hsr0
+       or              gr3,gr8,gr7     // add the sleep bits for later
+
+       li              #__icache_lock_start,gr3
+       li              #__icache_lock_end,gr4
+1:     icpl            gr3,gr0,#1
+       addi            gr3,#L1_CACHE_BYTES,gr3
+       cmp             gr4,gr3,icc0
+       bhi             icc0,#0,1b
+
+       # disable exceptions
+       movsg           psr,gr8
+       andi.p          gr8,#~PSR_PIL,gr8
+       andi            gr8,~PSR_ET,gr8
+       movgs           gr8,psr
+       ori             gr8,#PSR_ET,gr8
+
+       srli            gr8,#28,gr4
+       subicc          gr4,#3,gr0,icc0
+       beq             icc0,#0,1f
+       # FR4xx
+       li              __addr_FR4XX_DRCN,gr4
+       li              FR4XX_SDRAMC_DSTS_SSI,gr5
+       li              FR4XX_DSTS_OFFSET,gr6
+       bra             __icache_lock_start
+1:
+       # FR5xx
+       li              __addr_FR55X_DRCN,gr4
+       li              FR55X_SDRAMC_DSTS_SSI,gr5
+       li              FR55X_DSTS_OFFSET,gr6
+       bra             __icache_lock_start
+
+       .size           frv_cpu_suspend, .-frv_cpu_suspend
+
+#
+# the final part of the sleep sequence...
+# - we want it to be be cacheline aligned so we can lock it into the icache easily
+#  On entry:   gr7 holds desired hsr0 sleep value
+#               gr8 holds desired psr sleep value
+#
+       .balign         L1_CACHE_BYTES
+        .type          __icache_lock_start,@function
+__icache_lock_start:
+
+       #----------------------------------------------------
+       # put SDRAM in self-refresh mode
+       #----------------------------------------------------
+
+       # Flush all data in the cache using the DCEF instruction.
+       dcef            @(gr0,gr0),#1
+
+       # Stop DMAC transfer
+
+       # Execute dummy load from SDRAM
+       ldi             @(gr11,#0),gr11
+
+       # put the SDRAM into self-refresh mode
+       ld              @(gr4,gr0),gr11
+       ori             gr11,#SDRAMC_DRCN_SR,gr11
+       st              gr11,@(gr4,gr0)
+       membar
+
+       # wait for SDRAM to reach self-refresh mode
+1:     ld              @(gr4,gr6),gr11
+       andcc           gr11,gr5,gr11,icc0
+       beq             icc0,#0,1b
+
+       #  Set the GPIO register so that the IRQ[3:0] pins become valid, as required.
+       #  Set the clock mode (CLKC register) as required.
+       #     - At this time, also set the CLKC register P0 bit.
+
+       # Set the HSR0 register PDM field.
+       movgs           gr7,hsr0
+
+       # Execute NOP 32 times.
+       .rept           32
+       nop
+       .endr
+
+#if 0 // Fujitsu recommend to skip this and will update docs.
+       #      Release the interrupt mask setting of the MASK register of the
+       #      interrupt controller if necessary.
+       sti             gr10,@(gr9,#0)
+       membar
+#endif
+
+       # Set the PSR register ET bit to 1 to enable interrupts.
+       movgs           gr8,psr
+
+       ###################################################
+       # this is only reached if waking up via interrupt
+       ###################################################
+
+       # Execute NOP 32 times.
+       .rept           32
+       nop
+       .endr
+
+       #----------------------------------------------------
+       # wake SDRAM from self-refresh mode
+       #----------------------------------------------------
+       ld              @(gr4,gr0),gr11
+       andi            gr11,#~SDRAMC_DRCN_SR,gr11
+       st              gr11,@(gr4,gr0)
+       membar
+2:
+       ld              @(gr4,gr6),gr11 // Wait for it to come back...
+       andcc           gr11,gr5,gr0,icc0
+       bne             icc0,0,2b
+
+       # wait for the SDRAM to stabilise
+       li              0x0100000,gr3
+3:     subicc          gr3,#1,gr3,icc0
+       bne             icc0,#0,3b
+
+       # now that DRAM is back, this is the end of the code which gets
+       # locked in icache.
+__icache_lock_end:
+       .size           __icache_lock_start, .-__icache_lock_start
+
+       # Fall-through to the RAMBOOT# wakeup path
+
+###############################################################################
+#
+#  resume from suspend re-entry point reached via RAMBOOT# and bootloader
+#
+###############################################################################
+__ramboot_resume:
+
+       #----------------------------------------------------
+       # restore hsr0, psr, isr, and leave saved lr in gr7
+       #----------------------------------------------------
+       li              __sleep_save_area,gr11
+#ifdef CONFIG_MMU
+       movsg           hsr0,gr4
+       sethi.p         %hi(HSR0_EXMMU),gr3
+       setlo           %lo(HSR0_EXMMU),gr3
+       andcc           gr3,gr4,gr0,icc0
+       bne             icc0,#0,2f
+
+       # need to use physical address
+       sethi.p         %hi(__page_offset),gr3
+       setlo           %lo(__page_offset),gr3
+       sub             gr11,gr3,gr11
+
+       # flush all tlb entries
+       setlos          #64,gr4
+       setlos.p        #PAGE_SIZE,gr5
+       setlos          #0,gr6
+1:
+       tlbpr           gr6,gr0,#6,#0
+       subicc.p        gr4,#1,gr4,icc0
+       add             gr6,gr5,gr6
+       bne             icc0,#2,1b
+
+       # need a temporary mapping for the current physical address we are
+       # using between time MMU is enabled and jump to virtual address is
+       # made.
+       sethi.p         %hi(0x00000000),gr4
+       setlo           %lo(0x00000000),gr4             ; physical address
+       setlos          #xAMPRx_L|xAMPRx_M|xAMPRx_SS_256Mb|xAMPRx_S_KERNEL|xAMPRx_V,gr5
+       or              gr4,gr5,gr5
+
+       movsg           cxnr,gr13
+       or              gr4,gr13,gr4
+
+       movgs           gr4,iamlr1                      ; mapped from real address 0
+       movgs           gr5,iampr1                      ; cached kernel memory at 0x00000000
+2:
+#endif
+
+       lddi            @(gr11,#0),gr4 ; hsr0, psr
+       lddi            @(gr11,#8),gr6 ; isr, lr
+       movgs           gr4,hsr0
+       bar
+
+#ifdef CONFIG_MMU
+       sethi.p         %hi(1f),gr11
+       setlo           %lo(1f),gr11
+       jmpl            @(gr11,gr0)
+1:
+       movgs           gr0,iampr1      ; get rid of temporary mapping
+#endif
+       movgs           gr5,psr
+       movgs           gr6,isr
+
+       #----------------------------------------------------
+       # unlock the icache which was locked before going to sleep
+       #----------------------------------------------------
+       li              __icache_lock_start,gr3
+       li              __icache_lock_end,gr4
+1:     icul            gr3
+       addi            gr3,#L1_CACHE_BYTES,gr3
+       cmp             gr4,gr3,icc0
+       bhi             icc0,#0,1b
+
+       #----------------------------------------------------
+       # back to business as usual
+       #----------------------------------------------------
+       jmpl            @(gr7,gr0)              ;
+
+#endif /* CONFIG_PM */
+
+###############################################################################
+#
+# CPU core sleep mode routine
+#
+###############################################################################
+       .globl          frv_cpu_core_sleep
+        .type          frv_cpu_core_sleep,@function
+frv_cpu_core_sleep:
+
+       # Preload into icache.
+       li              #__core_sleep_icache_lock_start,gr3
+       li              #__core_sleep_icache_lock_end,gr4
+
+1:     icpl            gr3,gr0,#1
+       addi            gr3,#L1_CACHE_BYTES,gr3
+       cmp             gr4,gr3,icc0
+       bhi             icc0,#0,1b
+
+       bra     __core_sleep_icache_lock_start
+
+       .balign L1_CACHE_BYTES
+__core_sleep_icache_lock_start:
+
+       # (1) Set the PSR register ET bit to 0 to disable interrupts.
+       movsg           psr,gr8
+       andi.p          gr8,#~(PSR_PIL),gr8
+       andi            gr8,#~(PSR_ET),gr4
+       movgs           gr4,psr
+
+#if 0 // Fujitsu recommend to skip this and will update docs.
+       # (2) Set '1' to all bits in the MASK register of the interrupt
+       #     controller and mask interrupts.
+       sethi.p         %hi(__addr_MASK),gr9
+       setlo           %lo(__addr_MASK),gr9
+       sethi.p         %hi(0xffff0000),gr4
+       setlo           %lo(0xffff0000),gr4
+       ldi             @(gr9,#0),gr10
+       sti             gr4,@(gr9,#0)
+#endif
+       # (3) Flush all data in the cache using the DCEF instruction.
+       dcef            @(gr0,gr0),#1
+
+       # (4) Execute the memory barrier instruction
+       membar
+
+       # (5) Set the GPIO register so that the IRQ[3:0] pins become valid, as required.
+       # (6) Set the clock mode (CLKC register) as required.
+       #     - At this time, also set the CLKC register P0 bit.
+       # (7) Set the HSR0 register PDM field to  001 .
+       movsg           hsr0,gr4
+       ori             gr4,HSR0_PDM_CORE_SLEEP,gr4
+       movgs           gr4,hsr0
+
+       # (8) Execute NOP 32 times.
+       .rept           32
+       nop
+       .endr
+
+#if 0 // Fujitsu recommend to skip this and will update docs.
+       # (9) Release the interrupt mask setting of the MASK register of the
+       #     interrupt controller if necessary.
+       sti             gr10,@(gr9,#0)
+       membar
+#endif
+
+       # (10) Set the PSR register ET bit to 1 to enable interrupts.
+       movgs           gr8,psr
+
+__core_sleep_icache_lock_end:
+
+       # Unlock from icache
+       li      __core_sleep_icache_lock_start,gr3
+       li      __core_sleep_icache_lock_end,gr4
+1:     icul            gr3
+       addi            gr3,#L1_CACHE_BYTES,gr3
+       cmp             gr4,gr3,icc0
+       bhi             icc0,#0,1b
+
+       bralr
+
+       .size           frv_cpu_core_sleep, .-frv_cpu_core_sleep
diff --git a/arch/frv/kernel/switch_to.S b/arch/frv/kernel/switch_to.S
new file mode 100644 (file)
index 0000000..1703dc2
--- /dev/null
@@ -0,0 +1,496 @@
+###############################################################################
+#
+# switch_to.S: context switch operation
+#
+# Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version
+# 2 of the License, or (at your option) any later version.
+#
+###############################################################################
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/thread_info.h>
+#include <asm/processor.h>
+#include <asm/registers.h>
+#include <asm/spr-regs.h>
+
+.macro LEDS val
+       setlos          #~\val,gr27
+       st              gr27,@(gr30,gr0)
+       membar
+       dcf             @(gr30,gr0)
+.endm
+
+       .section        .sdata
+       .balign         8
+
+       # address of frame 0 (userspace) on current kernel stack
+       .globl          __kernel_frame0_ptr
+__kernel_frame0_ptr:
+       .long           init_thread_union + THREAD_SIZE - USER_CONTEXT_SIZE
+
+       # address of current task
+       .globl          __kernel_current_task
+__kernel_current_task:
+       .long           init_task
+
+       .section        .text
+       .balign         4
+
+###############################################################################
+#
+# struct task_struct *__switch_to(struct thread_struct *prev_thread,
+#                                struct thread_struct *next_thread,
+#                                struct task_struct *prev)
+#
+###############################################################################
+       .globl          __switch_to
+__switch_to:
+       # save outgoing process's context
+       sethi.p         %hi(__switch_back),gr13
+       setlo           %lo(__switch_back),gr13
+       movsg           lr,gr12
+
+       stdi            gr28,@(gr8,#__THREAD_FRAME)
+       sti             sp  ,@(gr8,#__THREAD_SP)
+       sti             fp  ,@(gr8,#__THREAD_FP)
+       stdi            gr12,@(gr8,#__THREAD_LR)
+       stdi            gr16,@(gr8,#__THREAD_GR(16))
+       stdi            gr18,@(gr8,#__THREAD_GR(18))
+       stdi            gr20,@(gr8,#__THREAD_GR(20))
+       stdi            gr22,@(gr8,#__THREAD_GR(22))
+       stdi            gr24,@(gr8,#__THREAD_GR(24))
+       stdi.p          gr26,@(gr8,#__THREAD_GR(26))
+
+       or              gr8,gr8,gr22
+       ldi.p           @(gr8,#__THREAD_USER),gr8
+       call            save_user_regs
+       or              gr22,gr22,gr8
+       
+       # retrieve the new context
+       sethi.p         %hi(__kernel_frame0_ptr),gr6
+       setlo           %lo(__kernel_frame0_ptr),gr6
+       movsg           psr,gr4
+
+       lddi.p          @(gr9,#__THREAD_FRAME),gr10
+       or              gr10,gr10,gr27          ; save prev for the return value
+
+       ldi             @(gr11,#4),gr19         ; get new_current->thread_info
+
+       lddi            @(gr9,#__THREAD_SP),gr12
+       ldi             @(gr9,#__THREAD_LR),gr14
+       ldi             @(gr9,#__THREAD_PC),gr18
+       ldi.p           @(gr9,#__THREAD_FRAME0),gr7
+
+       # actually switch kernel contexts with ordinary exceptions disabled
+       andi            gr4,#~PSR_ET,gr5
+       movgs           gr5,psr
+
+       or.p            gr10,gr0,gr28           ; set __frame
+       or              gr11,gr0,gr29           ; set __current
+       or.p            gr12,gr0,sp
+       or              gr13,gr0,fp
+       or              gr19,gr0,gr15           ; set __current_thread_info
+
+       sti             gr7,@(gr6,#0)           ; set __kernel_frame0_ptr
+       sti             gr29,@(gr6,#4)          ; set __kernel_current_task
+
+       movgs           gr14,lr
+       bar
+
+       srli            gr15,#28,gr5
+       subicc          gr5,#0xc,gr0,icc0
+       beq             icc0,#0,111f
+       break
+       nop
+111:
+
+       # jump to __switch_back or ret_from_fork as appropriate
+       # - move prev to GR8
+       movgs           gr4,psr
+       jmpl.p          @(gr18,gr0)
+       or              gr27,gr27,gr8
+
+###############################################################################
+#
+# restore incoming process's context
+# - on entry:
+#   - SP, FP, LR, GR15, GR28 and GR29 will have been set up appropriately
+#   - GR8 will point to the outgoing task_struct
+#   - GR9 will point to the incoming thread_struct
+#
+###############################################################################
+__switch_back:
+       lddi            @(gr9,#__THREAD_GR(16)),gr16
+       lddi            @(gr9,#__THREAD_GR(18)),gr18
+       lddi            @(gr9,#__THREAD_GR(20)),gr20
+       lddi            @(gr9,#__THREAD_GR(22)),gr22
+       lddi            @(gr9,#__THREAD_GR(24)),gr24
+       lddi            @(gr9,#__THREAD_GR(26)),gr26
+
+       # fall through into restore_user_regs()
+       ldi.p           @(gr9,#__THREAD_USER),gr8
+       or              gr8,gr8,gr9
+
+###############################################################################
+#
+# restore extra general regs and FP/Media regs
+# - void *restore_user_regs(const struct user_context *target, void *retval)
+# - on entry:
+#   - GR8 will point to the user context to swap in
+#   - GR9 will contain the value to be returned in GR8 (prev task on context switch)
+#
+###############################################################################
+       .globl          restore_user_regs
+restore_user_regs:
+       movsg           hsr0,gr6
+       ori             gr6,#HSR0_GRHE|HSR0_FRLE|HSR0_FRHE,gr6
+       movgs           gr6,hsr0
+       movsg           hsr0,gr6
+
+       movsg           psr,gr7
+       ori             gr7,#PSR_EF|PSR_EM,gr7
+       movgs           gr7,psr
+       movsg           psr,gr7
+       srli            gr7,#24,gr7
+       bar
+
+       lddi            @(gr8,#__FPMEDIA_MSR(0)),gr4
+
+       movgs           gr4,msr0
+       movgs           gr5,msr1
+
+       lddfi           @(gr8,#__FPMEDIA_ACC(0)),fr16
+       lddfi           @(gr8,#__FPMEDIA_ACC(2)),fr18
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(0)),fr20
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(1)),fr21
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(2)),fr22
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(3)),fr23
+
+       mwtacc          fr16,acc0
+       mwtacc          fr17,acc1
+       mwtacc          fr18,acc2
+       mwtacc          fr19,acc3
+       mwtaccg         fr20,accg0
+       mwtaccg         fr21,accg1
+       mwtaccg         fr22,accg2
+       mwtaccg         fr23,accg3
+
+       # some CPUs have extra ACCx and ACCGx regs and maybe FSRx regs
+       subicc.p        gr7,#0x50,gr0,icc0
+       subicc          gr7,#0x31,gr0,icc1
+       beq             icc0,#0,__restore_acc_fr451
+       beq             icc1,#0,__restore_acc_fr555
+__restore_acc_cont:
+
+       # some CPU's have GR32-GR63
+       setlos          #HSR0_FRHE,gr4
+       andcc           gr6,gr4,gr0,icc0
+       beq             icc0,#1,__restore_skip_gr32_gr63
+
+       lddi            @(gr8,#__INT_GR(32)),gr32
+       lddi            @(gr8,#__INT_GR(34)),gr34
+       lddi            @(gr8,#__INT_GR(36)),gr36
+       lddi            @(gr8,#__INT_GR(38)),gr38
+       lddi            @(gr8,#__INT_GR(40)),gr40
+       lddi            @(gr8,#__INT_GR(42)),gr42
+       lddi            @(gr8,#__INT_GR(44)),gr44
+       lddi            @(gr8,#__INT_GR(46)),gr46
+       lddi            @(gr8,#__INT_GR(48)),gr48
+       lddi            @(gr8,#__INT_GR(50)),gr50
+       lddi            @(gr8,#__INT_GR(52)),gr52
+       lddi            @(gr8,#__INT_GR(54)),gr54
+       lddi            @(gr8,#__INT_GR(56)),gr56
+       lddi            @(gr8,#__INT_GR(58)),gr58
+       lddi            @(gr8,#__INT_GR(60)),gr60
+       lddi            @(gr8,#__INT_GR(62)),gr62
+__restore_skip_gr32_gr63:
+
+       # all CPU's have FR0-FR31
+       lddfi           @(gr8,#__FPMEDIA_FR( 0)),fr0
+       lddfi           @(gr8,#__FPMEDIA_FR( 2)),fr2
+       lddfi           @(gr8,#__FPMEDIA_FR( 4)),fr4
+       lddfi           @(gr8,#__FPMEDIA_FR( 6)),fr6
+       lddfi           @(gr8,#__FPMEDIA_FR( 8)),fr8
+       lddfi           @(gr8,#__FPMEDIA_FR(10)),fr10
+       lddfi           @(gr8,#__FPMEDIA_FR(12)),fr12
+       lddfi           @(gr8,#__FPMEDIA_FR(14)),fr14
+       lddfi           @(gr8,#__FPMEDIA_FR(16)),fr16
+       lddfi           @(gr8,#__FPMEDIA_FR(18)),fr18
+       lddfi           @(gr8,#__FPMEDIA_FR(20)),fr20
+       lddfi           @(gr8,#__FPMEDIA_FR(22)),fr22
+       lddfi           @(gr8,#__FPMEDIA_FR(24)),fr24
+       lddfi           @(gr8,#__FPMEDIA_FR(26)),fr26
+       lddfi           @(gr8,#__FPMEDIA_FR(28)),fr28
+       lddfi.p         @(gr8,#__FPMEDIA_FR(30)),fr30
+
+       # some CPU's have FR32-FR63
+       setlos          #HSR0_FRHE,gr4
+       andcc           gr6,gr4,gr0,icc0
+       beq             icc0,#1,__restore_skip_fr32_fr63
+
+       lddfi           @(gr8,#__FPMEDIA_FR(32)),fr32
+       lddfi           @(gr8,#__FPMEDIA_FR(34)),fr34
+       lddfi           @(gr8,#__FPMEDIA_FR(36)),fr36
+       lddfi           @(gr8,#__FPMEDIA_FR(38)),fr38
+       lddfi           @(gr8,#__FPMEDIA_FR(40)),fr40
+       lddfi           @(gr8,#__FPMEDIA_FR(42)),fr42
+       lddfi           @(gr8,#__FPMEDIA_FR(44)),fr44
+       lddfi           @(gr8,#__FPMEDIA_FR(46)),fr46
+       lddfi           @(gr8,#__FPMEDIA_FR(48)),fr48
+       lddfi           @(gr8,#__FPMEDIA_FR(50)),fr50
+       lddfi           @(gr8,#__FPMEDIA_FR(52)),fr52
+       lddfi           @(gr8,#__FPMEDIA_FR(54)),fr54
+       lddfi           @(gr8,#__FPMEDIA_FR(56)),fr56
+       lddfi           @(gr8,#__FPMEDIA_FR(58)),fr58
+       lddfi           @(gr8,#__FPMEDIA_FR(60)),fr60
+       lddfi           @(gr8,#__FPMEDIA_FR(62)),fr62
+__restore_skip_fr32_fr63:
+
+       lddi            @(gr8,#__FPMEDIA_FNER(0)),gr4
+       movsg           fner0,gr4
+       movsg           fner1,gr5
+       or.p            gr9,gr9,gr8
+       bralr
+
+       # the FR451 also has ACC8-11/ACCG8-11 regs (but not 4-7...)
+__restore_acc_fr451:
+       lddfi           @(gr8,#__FPMEDIA_ACC(4)),fr16
+       lddfi           @(gr8,#__FPMEDIA_ACC(6)),fr18
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(4)),fr20
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(5)),fr21
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(6)),fr22
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(7)),fr23
+
+       mwtacc          fr16,acc8
+       mwtacc          fr17,acc9
+       mwtacc          fr18,acc10
+       mwtacc          fr19,acc11
+       mwtaccg         fr20,accg8
+       mwtaccg         fr21,accg9
+       mwtaccg         fr22,accg10
+       mwtaccg         fr23,accg11
+       bra             __restore_acc_cont
+
+       # the FR555 also has ACC4-7/ACCG4-7 regs and an FSR0 reg
+__restore_acc_fr555:
+       lddfi           @(gr8,#__FPMEDIA_ACC(4)),fr16
+       lddfi           @(gr8,#__FPMEDIA_ACC(6)),fr18
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(4)),fr20
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(5)),fr21
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(6)),fr22
+       ldbfi           @(gr8,#__FPMEDIA_ACCG(7)),fr23
+
+       mnop.p
+       mwtacc          fr16,acc4
+       mnop.p
+       mwtacc          fr17,acc5
+       mnop.p
+       mwtacc          fr18,acc6
+       mnop.p
+       mwtacc          fr19,acc7
+       mnop.p
+       mwtaccg         fr20,accg4
+       mnop.p
+       mwtaccg         fr21,accg5
+       mnop.p
+       mwtaccg         fr22,accg6
+       mnop.p
+       mwtaccg         fr23,accg7
+
+       ldi             @(gr8,#__FPMEDIA_FSR(0)),gr4
+       movgs           gr4,fsr0
+
+       bra             __restore_acc_cont
+
+
+###############################################################################
+#
+# save extra general regs and FP/Media regs
+# - void save_user_regs(struct user_context *target)
+#
+###############################################################################
+       .globl          save_user_regs
+save_user_regs:
+       movsg           hsr0,gr6
+       ori             gr6,#HSR0_GRHE|HSR0_FRLE|HSR0_FRHE,gr6
+       movgs           gr6,hsr0
+       movsg           hsr0,gr6
+
+       movsg           psr,gr7
+       ori             gr7,#PSR_EF|PSR_EM,gr7
+       movgs           gr7,psr
+       movsg           psr,gr7
+       srli            gr7,#24,gr7
+       bar
+
+       movsg           fner0,gr4
+       movsg           fner1,gr5
+       stdi.p          gr4,@(gr8,#__FPMEDIA_FNER(0))
+
+       # some CPU's have GR32-GR63
+       setlos          #HSR0_GRHE,gr4
+       andcc           gr6,gr4,gr0,icc0
+       beq             icc0,#1,__save_skip_gr32_gr63
+
+       stdi            gr32,@(gr8,#__INT_GR(32))
+       stdi            gr34,@(gr8,#__INT_GR(34))
+       stdi            gr36,@(gr8,#__INT_GR(36))
+       stdi            gr38,@(gr8,#__INT_GR(38))
+       stdi            gr40,@(gr8,#__INT_GR(40))
+       stdi            gr42,@(gr8,#__INT_GR(42))
+       stdi            gr44,@(gr8,#__INT_GR(44))
+       stdi            gr46,@(gr8,#__INT_GR(46))
+       stdi            gr48,@(gr8,#__INT_GR(48))
+       stdi            gr50,@(gr8,#__INT_GR(50))
+       stdi            gr52,@(gr8,#__INT_GR(52))
+       stdi            gr54,@(gr8,#__INT_GR(54))
+       stdi            gr56,@(gr8,#__INT_GR(56))
+       stdi            gr58,@(gr8,#__INT_GR(58))
+       stdi            gr60,@(gr8,#__INT_GR(60))
+       stdi            gr62,@(gr8,#__INT_GR(62))
+__save_skip_gr32_gr63:
+
+       # all CPU's have FR0-FR31
+       stdfi           fr0 ,@(gr8,#__FPMEDIA_FR( 0))
+       stdfi           fr2 ,@(gr8,#__FPMEDIA_FR( 2))
+       stdfi           fr4 ,@(gr8,#__FPMEDIA_FR( 4))
+       stdfi           fr6 ,@(gr8,#__FPMEDIA_FR( 6))
+       stdfi           fr8 ,@(gr8,#__FPMEDIA_FR( 8))
+       stdfi           fr10,@(gr8,#__FPMEDIA_FR(10))
+       stdfi           fr12,@(gr8,#__FPMEDIA_FR(12))
+       stdfi           fr14,@(gr8,#__FPMEDIA_FR(14))
+       stdfi           fr16,@(gr8,#__FPMEDIA_FR(16))
+       stdfi           fr18,@(gr8,#__FPMEDIA_FR(18))
+       stdfi           fr20,@(gr8,#__FPMEDIA_FR(20))
+       stdfi           fr22,@(gr8,#__FPMEDIA_FR(22))
+       stdfi           fr24,@(gr8,#__FPMEDIA_FR(24))
+       stdfi           fr26,@(gr8,#__FPMEDIA_FR(26))
+       stdfi           fr28,@(gr8,#__FPMEDIA_FR(28))
+       stdfi.p         fr30,@(gr8,#__FPMEDIA_FR(30))
+
+       # some CPU's have FR32-FR63
+       setlos          #HSR0_FRHE,gr4
+       andcc           gr6,gr4,gr0,icc0
+       beq             icc0,#1,__save_skip_fr32_fr63
+
+       stdfi           fr32,@(gr8,#__FPMEDIA_FR(32))
+       stdfi           fr34,@(gr8,#__FPMEDIA_FR(34))
+       stdfi           fr36,@(gr8,#__FPMEDIA_FR(36))
+       stdfi           fr38,@(gr8,#__FPMEDIA_FR(38))
+       stdfi           fr40,@(gr8,#__FPMEDIA_FR(40))
+       stdfi           fr42,@(gr8,#__FPMEDIA_FR(42))
+       stdfi           fr44,@(gr8,#__FPMEDIA_FR(44))
+       stdfi           fr46,@(gr8,#__FPMEDIA_FR(46))
+       stdfi           fr48,@(gr8,#__FPMEDIA_FR(48))
+       stdfi           fr50,@(gr8,#__FPMEDIA_FR(50))
+       stdfi           fr52,@(gr8,#__FPMEDIA_FR(52))
+       stdfi           fr54,@(gr8,#__FPMEDIA_FR(54))
+       stdfi           fr56,@(gr8,#__FPMEDIA_FR(56))
+       stdfi           fr58,@(gr8,#__FPMEDIA_FR(58))
+       stdfi           fr60,@(gr8,#__FPMEDIA_FR(60))
+       stdfi           fr62,@(gr8,#__FPMEDIA_FR(62))
+__save_skip_fr32_fr63:
+
+       mrdacc          acc0 ,fr4
+       mrdacc          acc1 ,fr5
+
+       stdfi.p         fr4 ,@(gr8,#__FPMEDIA_ACC(0))
+
+       mrdacc          acc2 ,fr6
+       mrdacc          acc3 ,fr7
+
+       stdfi.p         fr6 ,@(gr8,#__FPMEDIA_ACC(2))
+
+       mrdaccg         accg0,fr4
+       stbfi.p         fr4 ,@(gr8,#__FPMEDIA_ACCG(0))
+
+       mrdaccg         accg1,fr5
+       stbfi.p         fr5 ,@(gr8,#__FPMEDIA_ACCG(1))
+
+       mrdaccg         accg2,fr6
+       stbfi.p         fr6 ,@(gr8,#__FPMEDIA_ACCG(2))
+
+       mrdaccg         accg3,fr7
+       stbfi           fr7 ,@(gr8,#__FPMEDIA_ACCG(3))
+
+       movsg           msr0 ,gr4
+       movsg           msr1 ,gr5
+
+       stdi            gr4 ,@(gr8,#__FPMEDIA_MSR(0))
+
+       # some CPUs have extra ACCx and ACCGx regs and maybe FSRx regs
+       subicc.p        gr7,#0x50,gr0,icc0
+       subicc          gr7,#0x31,gr0,icc1
+       beq             icc0,#0,__save_acc_fr451
+       beq             icc1,#0,__save_acc_fr555
+__save_acc_cont:
+
+       lddfi           @(gr8,#__FPMEDIA_FR(4)),fr4
+       lddfi.p         @(gr8,#__FPMEDIA_FR(6)),fr6
+       bralr
+
+       # the FR451 also has ACC8-11/ACCG8-11 regs (but not 4-7...)
+__save_acc_fr451:
+       mrdacc          acc8 ,fr4
+       mrdacc          acc9 ,fr5
+
+       stdfi.p         fr4 ,@(gr8,#__FPMEDIA_ACC(4))
+
+       mrdacc          acc10,fr6
+       mrdacc          acc11,fr7
+
+       stdfi.p         fr6 ,@(gr8,#__FPMEDIA_ACC(6))
+
+       mrdaccg         accg8,fr4
+       stbfi.p         fr4 ,@(gr8,#__FPMEDIA_ACCG(4))
+
+       mrdaccg         accg9,fr5
+       stbfi.p         fr5 ,@(gr8,#__FPMEDIA_ACCG(5))
+
+       mrdaccg         accg10,fr6
+       stbfi.p         fr6 ,@(gr8,#__FPMEDIA_ACCG(6))
+
+       mrdaccg         accg11,fr7
+       stbfi           fr7 ,@(gr8,#__FPMEDIA_ACCG(7))
+       bra             __save_acc_cont
+
+       # the FR555 also has ACC4-7/ACCG4-7 regs and an FSR0 reg
+__save_acc_fr555:
+       mnop.p
+       mrdacc          acc4 ,fr4
+       mnop.p
+       mrdacc          acc5 ,fr5
+
+       stdfi           fr4 ,@(gr8,#__FPMEDIA_ACC(4))
+
+       mnop.p
+       mrdacc          acc6 ,fr6
+       mnop.p
+       mrdacc          acc7 ,fr7
+
+       stdfi           fr6 ,@(gr8,#__FPMEDIA_ACC(6))
+
+       mnop.p
+       mrdaccg         accg4,fr4
+       stbfi           fr4 ,@(gr8,#__FPMEDIA_ACCG(4))
+
+       mnop.p
+       mrdaccg         accg5,fr5
+       stbfi           fr5 ,@(gr8,#__FPMEDIA_ACCG(5))
+
+       mnop.p
+       mrdaccg         accg6,fr6
+       stbfi           fr6 ,@(gr8,#__FPMEDIA_ACCG(6))
+
+       mnop.p
+       mrdaccg         accg7,fr7
+       stbfi           fr7 ,@(gr8,#__FPMEDIA_ACCG(7))
+
+       movsg           fsr0 ,gr4
+       sti             gr4 ,@(gr8,#__FPMEDIA_FSR(0))
+       bra             __save_acc_cont
diff --git a/arch/frv/kernel/sys_frv.c b/arch/frv/kernel/sys_frv.c
new file mode 100644 (file)
index 0000000..931aa6d
--- /dev/null
@@ -0,0 +1,214 @@
+/* sys_frv.c: FRV arch-specific syscall wrappers
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/sys_m68k.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.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/stat.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+#include <linux/utsname.h>
+#include <linux/syscalls.h>
+
+#include <asm/setup.h>
+#include <asm/uaccess.h>
+#include <asm/ipc.h>
+
+/*
+ * sys_pipe() is the normal C calling standard for creating
+ * a pipe. It's not the way unix traditionally does this, though.
+ */
+asmlinkage long sys_pipe(unsigned long * fildes)
+{
+       int fd[2];
+       int error;
+
+       error = do_pipe(fd);
+       if (!error) {
+               if (copy_to_user(fildes, fd, 2*sizeof(int)))
+                       error = -EFAULT;
+       }
+       return error;
+}
+
+asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
+                         unsigned long prot, unsigned long flags,
+                         unsigned long fd, unsigned long pgoff)
+{
+       int error = -EBADF;
+       struct file * file = NULL;
+
+       flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+       if (!(flags & MAP_ANONYMOUS)) {
+               file = fget(fd);
+               if (!file)
+                       goto out;
+       }
+
+       /* As with sparc32, make sure the shift for mmap2 is constant
+          (12), no matter what PAGE_SIZE we have.... */
+
+       /* But unlike sparc32, don't just silently break if we're
+          trying to map something we can't */
+       if (pgoff & ((1<<(PAGE_SHIFT-12))-1))
+               return -EINVAL;
+
+       pgoff >>= (PAGE_SHIFT - 12);
+
+       down_write(&current->mm->mmap_sem);
+       error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+       up_write(&current->mm->mmap_sem);
+
+       if (file)
+               fput(file);
+out:
+       return error;
+}
+
+#if 0 /* DAVIDM - do we want this */
+struct mmap_arg_struct64 {
+       __u32 addr;
+       __u32 len;
+       __u32 prot;
+       __u32 flags;
+       __u64 offset; /* 64 bits */
+       __u32 fd;
+};
+
+asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
+{
+       int error = -EFAULT;
+       struct file * file = NULL;
+       struct mmap_arg_struct64 a;
+       unsigned long pgoff;
+
+       if (copy_from_user(&a, arg, sizeof(a)))
+               return -EFAULT;
+
+       if ((long)a.offset & ~PAGE_MASK)
+               return -EINVAL;
+
+       pgoff = a.offset >> PAGE_SHIFT;
+       if ((a.offset >> PAGE_SHIFT) != pgoff)
+               return -EINVAL;
+
+       if (!(a.flags & MAP_ANONYMOUS)) {
+               error = -EBADF;
+               file = fget(a.fd);
+               if (!file)
+                       goto out;
+       }
+       a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+
+       down_write(&current->mm->mmap_sem);
+       error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
+       up_write(&current->mm->mmap_sem);
+       if (file)
+               fput(file);
+out:
+       return error;
+}
+#endif
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls..
+ *
+ * This is really horribly ugly.
+ */
+asmlinkage long sys_ipc(unsigned long call,
+                       unsigned long first,
+                       unsigned long second,
+                       unsigned long third,
+                       void __user *ptr,
+                       unsigned long fifth)
+{
+       int version, ret;
+
+       version = call >> 16; /* hack for backward compatibility */
+       call &= 0xffff;
+
+       switch (call) {
+       case SEMOP:
+               return sys_semtimedop(first, (struct sembuf __user *)ptr, second, NULL);
+       case SEMTIMEDOP:
+               return sys_semtimedop(first, (struct sembuf __user *)ptr, second,
+                                     (const struct timespec __user *)fifth);
+
+       case SEMGET:
+               return sys_semget (first, second, third);
+       case SEMCTL: {
+               union semun fourth;
+               if (!ptr)
+                       return -EINVAL;
+               if (get_user(fourth.__pad, (void * __user *) ptr))
+                       return -EFAULT;
+               return sys_semctl (first, second, third, fourth);
+       }
+
+       case MSGSND:
+               return sys_msgsnd (first, (struct msgbuf __user *) ptr,
+                                  second, third);
+       case MSGRCV:
+               switch (version) {
+               case 0: {
+                       struct ipc_kludge tmp;
+                       if (!ptr)
+                               return -EINVAL;
+
+                       if (copy_from_user(&tmp,
+                                          (struct ipc_kludge __user *) ptr,
+                                          sizeof (tmp)))
+                               return -EFAULT;
+                       return sys_msgrcv (first, tmp.msgp, second,
+                                          tmp.msgtyp, third);
+               }
+               default:
+                       return sys_msgrcv (first,
+                                          (struct msgbuf __user *) ptr,
+                                          second, fifth, third);
+               }
+       case MSGGET:
+               return sys_msgget ((key_t) first, second);
+       case MSGCTL:
+               return sys_msgctl (first, second, (struct msqid_ds __user *) ptr);
+
+       case SHMAT:
+               switch (version) {
+               default: {
+                       ulong raddr;
+                       ret = do_shmat (first, (char __user *) ptr, second, &raddr);
+                       if (ret)
+                               return ret;
+                       return put_user (raddr, (ulong __user *) third);
+               }
+               case 1: /* iBCS2 emulator entry point */
+                       if (!segment_eq(get_fs(), get_ds()))
+                               return -EINVAL;
+                       /* The "(ulong *) third" is valid _only_ because of the kernel segment thing */
+                       return do_shmat (first, (char __user *) ptr, second, (ulong *) third);
+               }
+       case SHMDT:
+               return sys_shmdt ((char __user *)ptr);
+       case SHMGET:
+               return sys_shmget (first, second, third);
+       case SHMCTL:
+               return sys_shmctl (first, second,
+                                  (struct shmid_ds __user *) ptr);
+       default:
+               return -ENOSYS;
+       }
+}
diff --git a/arch/frv/kernel/sysctl.c b/arch/frv/kernel/sysctl.c
new file mode 100644 (file)
index 0000000..408b0f3
--- /dev/null
@@ -0,0 +1,206 @@
+/* sysctl.c: implementation of /proc/sys files relating to FRV specifically
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+static const char frv_cache_wback[] = "wback";
+static const char frv_cache_wthru[] = "wthru";
+
+static void frv_change_dcache_mode(unsigned long newmode)
+{
+       unsigned long flags, hsr0;
+
+       local_irq_save(flags);
+
+       hsr0 = __get_HSR(0);
+       hsr0 &= ~HSR0_DCE;
+       __set_HSR(0, hsr0);
+
+       asm volatile("  dcef    @(gr0,gr0),#1   \n"
+                    "  membar                  \n"
+                    : : : "memory"
+                    );
+
+       hsr0 = (hsr0 & ~HSR0_CBM) | newmode;
+       __set_HSR(0, hsr0);
+       hsr0 |= HSR0_DCE;
+       __set_HSR(0, hsr0);
+
+       local_irq_restore(flags);
+
+       //printk("HSR0 now %08lx\n", hsr0);
+}
+
+/*****************************************************************************/
+/*
+ * handle requests to dynamically switch the write caching mode delivered by /proc
+ */
+static int procctl_frv_cachemode(ctl_table *table, int write, struct file *filp,
+                                void *buffer, size_t *lenp, loff_t *ppos)
+{
+       unsigned long hsr0;
+       char buff[8];
+       int len;
+
+       len = *lenp;
+
+       if (write) {
+               /* potential state change */
+               if (len <= 1 || len > sizeof(buff) - 1)
+                       return -EINVAL;
+
+               if (copy_from_user(buff, buffer, len) != 0)
+                       return -EFAULT;
+
+               if (buff[len - 1] == '\n')
+                       buff[len - 1] = '\0';
+               else
+                       buff[len] = '\0';
+
+               if (strcmp(buff, frv_cache_wback) == 0) {
+                       /* switch dcache into write-back mode */
+                       frv_change_dcache_mode(HSR0_CBM_COPY_BACK);
+                       return 0;
+               }
+
+               if (strcmp(buff, frv_cache_wthru) == 0) {
+                       /* switch dcache into write-through mode */
+                       frv_change_dcache_mode(HSR0_CBM_WRITE_THRU);
+                       return 0;
+               }
+
+               return -EINVAL;
+       }
+
+       /* read the state */
+       if (filp->f_pos > 0) {
+               *lenp = 0;
+               return 0;
+       }
+
+       hsr0 = __get_HSR(0);
+       switch (hsr0 & HSR0_CBM) {
+       case HSR0_CBM_WRITE_THRU:
+               memcpy(buff, frv_cache_wthru, sizeof(frv_cache_wthru) - 1);
+               buff[sizeof(frv_cache_wthru) - 1] = '\n';
+               len = sizeof(frv_cache_wthru);
+               break;
+       default:
+               memcpy(buff, frv_cache_wback, sizeof(frv_cache_wback) - 1);
+               buff[sizeof(frv_cache_wback) - 1] = '\n';
+               len = sizeof(frv_cache_wback);
+               break;
+       }
+
+       if (len > *lenp)
+               len = *lenp;
+
+       if (copy_to_user(buffer, buff, len) != 0)
+               return -EFAULT;
+
+       *lenp = len;
+       filp->f_pos = len;
+       return 0;
+
+} /* end procctl_frv_cachemode() */
+
+/*****************************************************************************/
+/*
+ * permit the mm_struct the nominated process is using have its MMU context ID pinned
+ */
+#ifdef CONFIG_MMU
+static int procctl_frv_pin_cxnr(ctl_table *table, int write, struct file *filp,
+                               void *buffer, size_t *lenp, loff_t *ppos)
+{
+       pid_t pid;
+       char buff[16], *p;
+       int len;
+
+       len = *lenp;
+
+       if (write) {
+               /* potential state change */
+               if (len <= 1 || len > sizeof(buff) - 1)
+                       return -EINVAL;
+
+               if (copy_from_user(buff, buffer, len) != 0)
+                       return -EFAULT;
+
+               if (buff[len - 1] == '\n')
+                       buff[len - 1] = '\0';
+               else
+                       buff[len] = '\0';
+
+               pid = simple_strtoul(buff, &p, 10);
+               if (*p)
+                       return -EINVAL;
+
+               return cxn_pin_by_pid(pid);
+       }
+
+       /* read the currently pinned CXN */
+       if (filp->f_pos > 0) {
+               *lenp = 0;
+               return 0;
+       }
+
+       len = snprintf(buff, sizeof(buff), "%d\n", cxn_pinned);
+       if (len > *lenp)
+               len = *lenp;
+
+       if (copy_to_user(buffer, buff, len) != 0)
+               return -EFAULT;
+
+       *lenp = len;
+       filp->f_pos = len;
+       return 0;
+
+} /* end procctl_frv_pin_cxnr() */
+#endif
+
+/*
+ * FR-V specific sysctls
+ */
+static struct ctl_table frv_table[] =
+{
+       { 1, "cache-mode",      NULL, 0, 0644, NULL, &procctl_frv_cachemode },
+#ifdef CONFIG_MMU
+       { 2, "pin-cxnr",        NULL, 0, 0644, NULL, &procctl_frv_pin_cxnr },
+#endif
+       { 0 }
+};
+
+/*
+ * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6
+ * when all the PM interfaces exist nicely.
+ */
+#define CTL_FRV 9898
+static struct ctl_table frv_dir_table[] =
+{
+       {CTL_FRV, "frv", NULL, 0, 0555, frv_table},
+       {0}
+};
+
+/*
+ * Initialize power interface
+ */
+static int __init frv_sysctl_init(void)
+{
+       register_sysctl_table(frv_dir_table, 1);
+       return 0;
+}
+
+__initcall(frv_sysctl_init);
diff --git a/arch/frv/kernel/time.c b/arch/frv/kernel/time.c
new file mode 100644 (file)
index 0000000..075db66
--- /dev/null
@@ -0,0 +1,234 @@
+/* time.c: FRV arch-specific time handling
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/kernel/time.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.
+ */
+
+#include <linux/config.h> /* CONFIG_HEARTBEAT */
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/profile.h>
+#include <linux/irq.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/timer-regs.h>
+#include <asm/mb-regs.h>
+#include <asm/mb86943a.h>
+#include <asm/irq-routing.h>
+
+#include <linux/timex.h>
+
+#define TICK_SIZE (tick_nsec / 1000)
+
+extern unsigned long wall_jiffies;
+
+u64 jiffies_64 = INITIAL_JIFFIES;
+EXPORT_SYMBOL(jiffies_64);
+
+unsigned long __nongprelbss __clkin_clock_speed_HZ;
+unsigned long __nongprelbss __ext_bus_clock_speed_HZ;
+unsigned long __nongprelbss __res_bus_clock_speed_HZ;
+unsigned long __nongprelbss __sdram_clock_speed_HZ;
+unsigned long __nongprelbss __core_bus_clock_speed_HZ;
+unsigned long __nongprelbss __core_clock_speed_HZ;
+unsigned long __nongprelbss __dsu_clock_speed_HZ;
+unsigned long __nongprelbss __serial_clock_speed_HZ;
+unsigned long __delay_loops_MHz;
+
+static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs *regs);
+
+static struct irqaction timer_irq  = {
+       timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL
+};
+
+static inline int set_rtc_mmss(unsigned long nowtime)
+{
+       return -1;
+}
+
+/*
+ * timer_interrupt() needs to keep up the real-time clock,
+ * as well as call the "do_timer()" routine every clocktick
+ */
+static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs * regs)
+{
+       /* last time the cmos clock got updated */
+       static long last_rtc_update = 0;
+
+       /*
+        * Here we are in the timer irq handler. We just have irqs locally
+        * disabled but we don't know if the timer_bh is running on the other
+        * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
+        * the irq version of write_lock because as just said we have irq
+        * locally disabled. -arca
+        */
+       write_seqlock(&xtime_lock);
+
+       do_timer(regs);
+       update_process_times(user_mode(regs));
+       profile_tick(CPU_PROFILING, regs);
+
+       /*
+        * If we have an externally synchronized Linux clock, then update
+        * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+        * called as close as possible to 500 ms before the new second starts.
+        */
+       if ((time_status & STA_UNSYNC) == 0 &&
+           xtime.tv_sec > last_rtc_update + 660 &&
+           (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 &&
+           (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2
+           ) {
+               if (set_rtc_mmss(xtime.tv_sec) == 0)
+                       last_rtc_update = xtime.tv_sec;
+               else
+                       last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+       }
+
+#ifdef CONFIG_HEARTBEAT
+       static unsigned short n;
+       n++;
+       __set_LEDS(n);
+#endif /* CONFIG_HEARTBEAT */
+
+       write_sequnlock(&xtime_lock);
+       return IRQ_HANDLED;
+}
+
+void time_divisor_init(void)
+{
+       unsigned short base, pre, prediv;
+
+       /* set the scheduling timer going */
+       pre = 1;
+       prediv = 4;
+       base = __res_bus_clock_speed_HZ / pre / HZ / (1 << prediv);
+
+       __set_TPRV(pre);
+       __set_TxCKSL_DATA(0, prediv);
+       __set_TCTR(TCTR_SC_CTR0 | TCTR_RL_RW_LH8 | TCTR_MODE_2);
+       __set_TCSR_DATA(0, base & 0xff);
+       __set_TCSR_DATA(0, base >> 8);
+}
+
+void time_init(void)
+{
+       unsigned int year, mon, day, hour, min, sec;
+
+       extern void arch_gettod(int *year, int *mon, int *day, int *hour, int *min, int *sec);
+
+       /* FIX by dqg : Set to zero for platforms that don't have tod */
+       /* without this time is undefined and can overflow time_t, causing  */
+       /* very stange errors */
+       year = 1980;
+       mon = day = 1;
+       hour = min = sec = 0;
+       arch_gettod (&year, &mon, &day, &hour, &min, &sec);
+
+       if ((year += 1900) < 1970)
+               year += 100;
+       xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+       xtime.tv_nsec = 0;
+
+       /* install scheduling interrupt handler */
+       setup_irq(IRQ_CPU_TIMER0, &timer_irq);
+
+       time_divisor_init();
+}
+
+/*
+ * This version of gettimeofday has near microsecond resolution.
+ */
+void do_gettimeofday(struct timeval *tv)
+{
+       unsigned long seq;
+       unsigned long usec, sec;
+       unsigned long max_ntp_tick;
+
+       do {
+               unsigned long lost;
+
+               seq = read_seqbegin(&xtime_lock);
+
+               usec = 0;
+               lost = jiffies - wall_jiffies;
+
+               /*
+                * If time_adjust is negative then NTP is slowing the clock
+                * so make sure not to go into next possible interval.
+                * Better to lose some accuracy than have time go backwards..
+                */
+               if (unlikely(time_adjust < 0)) {
+                       max_ntp_tick = (USEC_PER_SEC / HZ) - tickadj;
+                       usec = min(usec, max_ntp_tick);
+
+                       if (lost)
+                               usec += lost * max_ntp_tick;
+               }
+               else if (unlikely(lost))
+                       usec += lost * (USEC_PER_SEC / HZ);
+
+               sec = xtime.tv_sec;
+               usec += (xtime.tv_nsec / 1000);
+       } while (read_seqretry(&xtime_lock, seq));
+
+       while (usec >= 1000000) {
+               usec -= 1000000;
+               sec++;
+       }
+
+       tv->tv_sec = sec;
+       tv->tv_usec = usec;
+}
+
+int do_settimeofday(struct timespec *tv)
+{
+       time_t wtm_sec, sec = tv->tv_sec;
+       long wtm_nsec, nsec = tv->tv_nsec;
+
+       if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+               return -EINVAL;
+
+       write_seqlock_irq(&xtime_lock);
+       /*
+        * This is revolting. We need to set "xtime" correctly. However, the
+        * value in this location is the value at the most recent update of
+        * wall time.  Discover what correction gettimeofday() would have
+        * made, and then undo it!
+        */
+       nsec -= 0 * NSEC_PER_USEC;
+       nsec -= (jiffies - wall_jiffies) * TICK_NSEC;
+
+       wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
+       wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
+
+       set_normalized_timespec(&xtime, sec, nsec);
+       set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
+
+       time_adjust = 0;                /* stop active adjtime() */
+       time_status |= STA_UNSYNC;
+       time_maxerror = NTP_PHASE_LIMIT;
+       time_esterror = NTP_PHASE_LIMIT;
+       write_sequnlock_irq(&xtime_lock);
+       clock_was_set();
+       return 0;
+}
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+       return jiffies_64 * (1000000000 / HZ);
+}
diff --git a/arch/frv/kernel/traps.c b/arch/frv/kernel/traps.c
new file mode 100644 (file)
index 0000000..89073ca
--- /dev/null
@@ -0,0 +1,431 @@
+/* traps.c: high-level exception handler for FR-V
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/user.h>
+#include <linux/string.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/fpu.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/siginfo.h>
+#include <asm/unaligned.h>
+
+void show_backtrace(struct pt_regs *, unsigned long);
+
+extern asmlinkage void __break_hijack_kernel_event(void);
+
+/*****************************************************************************/
+/*
+ * instruction access error
+ */
+asmlinkage void insn_access_error(unsigned long esfr1, unsigned long epcr0, unsigned long esr0)
+{
+       siginfo_t info;
+
+       die_if_kernel("-- Insn Access Error --\n"
+                     "EPCR0 : %08lx\n"
+                     "ESR0  : %08lx\n",
+                     epcr0, esr0);
+
+       info.si_signo   = SIGSEGV;
+       info.si_code    = SEGV_ACCERR;
+       info.si_errno   = 0;
+       info.si_addr    = (void *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc);
+
+       force_sig_info(info.si_signo, &info, current);
+} /* end insn_access_error() */
+
+/*****************************************************************************/
+/*
+ * handler for:
+ * - illegal instruction
+ * - privileged instruction
+ * - unsupported trap
+ * - debug exceptions
+ */
+asmlinkage void illegal_instruction(unsigned long esfr1, unsigned long epcr0, unsigned long esr0)
+{
+       siginfo_t info;
+
+       die_if_kernel("-- Illegal Instruction --\n"
+                     "EPCR0 : %08lx\n"
+                     "ESR0  : %08lx\n"
+                     "ESFR1 : %08lx\n",
+                     epcr0, esr0, esfr1);
+
+       info.si_errno   = 0;
+       info.si_addr    = (void *) ((epcr0 & EPCR0_PC) ? (epcr0 & EPCR0_PC) : __frame->pc);
+
+       switch (__frame->tbr & TBR_TT) {
+       case TBR_TT_ILLEGAL_INSTR:
+               info.si_signo   = SIGILL;
+               info.si_code    = ILL_ILLOPC;
+               break;
+       case TBR_TT_PRIV_INSTR:
+               info.si_signo   = SIGILL;
+               info.si_code    = ILL_PRVOPC;
+               break;
+       case TBR_TT_TRAP2 ... TBR_TT_TRAP126:
+               info.si_signo   = SIGILL;
+               info.si_code    = ILL_ILLTRP;
+               break;
+       /* GDB uses "tira gr0, #1" as a breakpoint instruction.  */
+       case TBR_TT_TRAP1:
+       case TBR_TT_BREAK:
+               info.si_signo   = SIGTRAP;
+               info.si_code    =
+                       (__frame->__status & REG__STATUS_STEPPED) ? TRAP_TRACE : TRAP_BRKPT;
+               break;
+       }
+
+       force_sig_info(info.si_signo, &info, current);
+} /* end illegal_instruction() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+asmlinkage void media_exception(unsigned long msr0, unsigned long msr1)
+{
+       siginfo_t info;
+
+       die_if_kernel("-- Media Exception --\n"
+                     "MSR0 : %08lx\n"
+                     "MSR1 : %08lx\n",
+                     msr0, msr1);
+
+       info.si_signo   = SIGFPE;
+       info.si_code    = FPE_MDAOVF;
+       info.si_errno   = 0;
+       info.si_addr    = (void *) __frame->pc;
+
+       force_sig_info(info.si_signo, &info, current);
+} /* end media_exception() */
+
+/*****************************************************************************/
+/*
+ * instruction or data access exception
+ */
+asmlinkage void memory_access_exception(unsigned long esr0,
+                                       unsigned long ear0,
+                                       unsigned long epcr0)
+{
+       siginfo_t info;
+
+#ifdef CONFIG_MMU
+       unsigned long fixup;
+
+       if ((esr0 & ESRx_EC) == ESRx_EC_DATA_ACCESS)
+               if (handle_misalignment(esr0, ear0, epcr0) == 0)
+                       return;
+
+       if ((fixup = search_exception_table(__frame->pc)) != 0) {
+               __frame->pc = fixup;
+               return;
+       }
+#endif
+
+       die_if_kernel("-- Memory Access Exception --\n"
+                     "ESR0  : %08lx\n"
+                     "EAR0  : %08lx\n"
+                     "EPCR0 : %08lx\n",
+                     esr0, ear0, epcr0);
+
+       info.si_signo   = SIGSEGV;
+       info.si_code    = SEGV_ACCERR;
+       info.si_errno   = 0;
+       info.si_addr    = NULL;
+
+       if ((esr0 & (ESRx_VALID | ESR0_EAV)) == (ESRx_VALID | ESR0_EAV))
+               info.si_addr = (void *) ear0;
+
+       force_sig_info(info.si_signo, &info, current);
+
+} /* end memory_access_exception() */
+
+/*****************************************************************************/
+/*
+ * data access error
+ * - double-word data load from CPU control area (0xFExxxxxx)
+ * - read performed on inactive or self-refreshing SDRAM
+ * - error notification from slave device
+ * - misaligned address
+ * - access to out of bounds memory region
+ * - user mode accessing privileged memory region
+ * - write to R/O memory region
+ */
+asmlinkage void data_access_error(unsigned long esfr1, unsigned long esr15, unsigned long ear15)
+{
+       siginfo_t info;
+
+       die_if_kernel("-- Data Access Error --\n"
+                     "ESR15 : %08lx\n"
+                     "EAR15 : %08lx\n",
+                     esr15, ear15);
+
+       info.si_signo   = SIGSEGV;
+       info.si_code    = SEGV_ACCERR;
+       info.si_errno   = 0;
+       info.si_addr    = (void *)
+               (((esr15 & (ESRx_VALID|ESR15_EAV)) == (ESRx_VALID|ESR15_EAV)) ? ear15 : 0);
+
+       force_sig_info(info.si_signo, &info, current);
+} /* end data_access_error() */
+
+/*****************************************************************************/
+/*
+ * data store error - should only happen if accessing inactive or self-refreshing SDRAM
+ */
+asmlinkage void data_store_error(unsigned long esfr1, unsigned long esr15)
+{
+       die_if_kernel("-- Data Store Error --\n"
+                     "ESR15 : %08lx\n",
+                     esr15);
+       BUG();
+} /* end data_store_error() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+asmlinkage void division_exception(unsigned long esfr1, unsigned long esr0, unsigned long isr)
+{
+       siginfo_t info;
+
+       die_if_kernel("-- Division Exception --\n"
+                     "ESR0 : %08lx\n"
+                     "ISR  : %08lx\n",
+                     esr0, isr);
+
+       info.si_signo   = SIGFPE;
+       info.si_code    = FPE_INTDIV;
+       info.si_errno   = 0;
+       info.si_addr    = (void *) __frame->pc;
+
+       force_sig_info(info.si_signo, &info, current);
+} /* end division_exception() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+asmlinkage void compound_exception(unsigned long esfr1,
+                                  unsigned long esr0, unsigned long esr14, unsigned long esr15,
+                                  unsigned long msr0, unsigned long msr1)
+{
+       die_if_kernel("-- Compound Exception --\n"
+                     "ESR0  : %08lx\n"
+                     "ESR15 : %08lx\n"
+                     "ESR15 : %08lx\n"
+                     "MSR0  : %08lx\n"
+                     "MSR1  : %08lx\n",
+                     esr0, esr14, esr15, msr0, msr1);
+       BUG();
+} /* end compound_exception() */
+
+/*****************************************************************************/
+/*
+ * The architecture-independent backtrace generator
+ */
+void dump_stack(void)
+{
+       show_stack(NULL, NULL);
+}
+
+void show_stack(struct task_struct *task, unsigned long *sp)
+{
+}
+
+void show_trace_task(struct task_struct *tsk)
+{
+       printk("CONTEXT: stack=0x%lx frame=0x%p LR=0x%lx RET=0x%lx\n",
+              tsk->thread.sp, tsk->thread.frame, tsk->thread.lr, tsk->thread.sched_lr);
+}
+
+static const char *regnames[] = {
+       "PSR ", "ISR ", "CCR ", "CCCR",
+       "LR  ", "LCR ", "PC  ", "_stt",
+       "sys ", "GR8*", "GNE0", "GNE1",
+       "IACH", "IACL",
+       "TBR ", "SP  ", "FP  ", "GR3 ",
+       "GR4 ", "GR5 ", "GR6 ", "GR7 ",
+       "GR8 ", "GR9 ", "GR10", "GR11",
+       "GR12", "GR13", "GR14", "GR15",
+       "GR16", "GR17", "GR18", "GR19",
+       "GR20", "GR21", "GR22", "GR23",
+       "GR24", "GR25", "GR26", "GR27",
+       "EFRM", "CURR", "GR30", "BFRM"
+};
+
+void show_regs(struct pt_regs *regs)
+{
+       uint32_t *reg;
+       int loop;
+
+       printk("\n");
+
+       printk("Frame: @%08x [%s]\n",
+              (uint32_t) regs,
+              regs->psr & PSR_S ? "kernel" : "user");
+
+       reg = (uint32_t *) regs;
+       for (loop = 0; loop < REG__END; loop++) {
+               printk("%s %08x", regnames[loop + 0], reg[loop + 0]);
+
+               if (loop == REG__END - 1 || loop % 5 == 4)
+                       printk("\n");
+               else
+                       printk(" | ");
+       }
+
+       printk("Process %s (pid: %d)\n", current->comm, current->pid);
+}
+
+void die_if_kernel(const char *str, ...)
+{
+       char buffer[256];
+       va_list va;
+
+       if (user_mode(__frame))
+               return;
+
+       va_start(va, str);
+       vsprintf(buffer, str, va);
+       va_end(va);
+
+       console_verbose();
+       printk("\n===================================\n");
+       printk("%s\n", buffer);
+       show_backtrace(__frame, 0);
+
+       __break_hijack_kernel_event();
+       do_exit(SIGSEGV);
+}
+
+/*****************************************************************************/
+/*
+ * dump the contents of an exception frame
+ */
+static void show_backtrace_regs(struct pt_regs *frame)
+{
+       uint32_t *reg;
+       int loop;
+
+       /* print the registers for this frame */
+       printk("<-- %s Frame: @%p -->\n",
+              frame->psr & PSR_S ? "Kernel Mode" : "User Mode",
+              frame);
+
+       reg = (uint32_t *) frame;
+       for (loop = 0; loop < REG__END; loop++) {
+               printk("%s %08x", regnames[loop + 0], reg[loop + 0]);
+
+               if (loop == REG__END - 1 || loop % 5 == 4)
+                       printk("\n");
+               else
+                       printk(" | ");
+       }
+
+       printk("--------\n");
+} /* end show_backtrace_regs() */
+
+/*****************************************************************************/
+/*
+ * generate a backtrace of the kernel stack
+ */
+void show_backtrace(struct pt_regs *frame, unsigned long sp)
+{
+       struct pt_regs *frame0;
+       unsigned long tos = 0, stop = 0, base;
+       int format;
+
+       base = ((((unsigned long) frame) + 8191) & ~8191) - sizeof(struct user_context);
+       frame0 = (struct pt_regs *) base;
+
+       if (sp) {
+               tos = sp;
+               stop = (unsigned long) frame;
+       }
+
+       printk("\nProcess %s (pid: %d)\n\n", current->comm, current->pid);
+
+       for (;;) {
+               /* dump stack segment between frames */
+               //printk("%08lx -> %08lx\n", tos, stop);
+               format = 0;
+               while (tos < stop) {
+                       if (format == 0)
+                               printk(" %04lx :", tos & 0xffff);
+
+                       printk(" %08lx", *(unsigned long *) tos);
+
+                       tos += 4;
+                       format++;
+                       if (format == 8) {
+                               printk("\n");
+                               format = 0;
+                       }
+               }
+
+               if (format > 0)
+                       printk("\n");
+
+               /* dump frame 0 outside of the loop */
+               if (frame == frame0)
+                       break;
+
+               tos = frame->sp;
+               if (((unsigned long) frame) + sizeof(*frame) != tos) {
+                       printk("-- TOS %08lx does not follow frame %p --\n",
+                              tos, frame);
+                       break;
+               }
+
+               show_backtrace_regs(frame);
+
+               /* dump the stack between this frame and the next */
+               stop = (unsigned long) frame->next_frame;
+               if (stop != base &&
+                   (stop < tos ||
+                    stop > base ||
+                    (stop < base && stop + sizeof(*frame) > base) ||
+                    stop & 3)) {
+                       printk("-- next_frame %08lx is invalid (range %08lx-%08lx) --\n",
+                              stop, tos, base);
+                       break;
+               }
+
+               /* move to next frame */
+               frame = frame->next_frame;
+       }
+
+       /* we can always dump frame 0, even if the rest of the stack is corrupt */
+       show_backtrace_regs(frame0);
+
+} /* end show_backtrace() */
+
+/*****************************************************************************/
+/*
+ * initialise traps
+ */
+void __init trap_init (void)
+{
+} /* end trap_init() */
diff --git a/arch/frv/kernel/uaccess.c b/arch/frv/kernel/uaccess.c
new file mode 100644 (file)
index 0000000..f3fd58a
--- /dev/null
@@ -0,0 +1,95 @@
+/* uaccess.c: userspace access functions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+
+/*****************************************************************************/
+/*
+ * copy a null terminated string from userspace
+ */
+long strncpy_from_user(char *dst, const char *src, long count)
+{
+       unsigned long max;
+       char *p, ch;
+       long err = -EFAULT;
+
+       if (count < 0)
+               BUG();
+
+       p = dst;
+
+#ifndef CONFIG_MMU
+       if ((unsigned long) src < memory_start)
+               goto error;
+#endif
+
+       if ((unsigned long) src >= get_addr_limit())
+               goto error;
+
+       max = get_addr_limit() - (unsigned long) src;
+       if ((unsigned long) count > max) {
+               memset(dst + max, 0, count - max);
+               count = max;
+       }
+
+       err = 0;
+       for (; count > 0; count--, p++, src++) {
+               __get_user_asm(err, ch, src, "ub", "=r");
+               if (err < 0)
+                       goto error;
+               if (!ch)
+                       break;
+               *p = ch;
+       }
+
+       err = p - dst; /* return length excluding NUL */
+
+ error:
+       if (count > 0)
+               memset(p, 0, count); /* clear remainder of buffer [security] */
+
+       return err;
+} /* end strncpy_from_user() */
+
+/*****************************************************************************/
+/*
+ * Return the size of a string (including the ending 0)
+ *
+ * Return 0 on exception, a value greater than N if too long
+ */
+long strnlen_user(const char *src, long count)
+{
+       const char *p;
+       long err = 0;
+       char ch;
+
+       if (count < 0)
+               BUG();
+
+#ifndef CONFIG_MMU
+       if ((unsigned long) src < memory_start)
+               return 0;
+#endif
+
+       if ((unsigned long) src >= get_addr_limit())
+               return 0;
+
+       for (p = src; count > 0; count--, p++) {
+               __get_user_asm(err, ch, p, "ub", "=r");
+               if (err < 0)
+                       return 0;
+               if (!ch)
+                       break;
+       }
+
+       return p - src + 1; /* return length including NUL */
+} /* end strnlen_user() */
diff --git a/arch/frv/kernel/vmlinux.lds.S b/arch/frv/kernel/vmlinux.lds.S
new file mode 100644 (file)
index 0000000..fceafd2
--- /dev/null
@@ -0,0 +1,187 @@
+/* ld script to make FRV Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-frv", "elf32-frv", "elf32-frv")
+OUTPUT_ARCH(frv)
+ENTRY(_start)
+
+#include <asm-generic/vmlinux.lds.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+#include <asm/thread_info.h>
+
+jiffies = jiffies_64 + 4;
+
+__page_offset = 0xc0000000;            /* start of area covered by struct pages */
+__kernel_image_start = __page_offset;  /* address at which kernel image resides */
+
+SECTIONS
+{
+  . = __kernel_image_start;
+
+  /* discardable initialisation code and data */
+  . = ALIGN(PAGE_SIZE);                        /* Init code and data */
+  __init_begin = .;
+
+  _sinittext = .;
+  .init.text : {
+       *(.text.head)
+#ifndef CONFIG_DEBUG_INFO
+       *(.init.text)
+       *(.exit.text)
+       *(.exit.data)
+       *(.exitcall.exit)
+#endif
+  }
+  _einittext = .;
+  .init.data : { *(.init.data) }
+
+  . = ALIGN(8);
+  __setup_start = .;
+  .setup.init : { KEEP(*(.init.setup)) }
+  __setup_end = .;
+
+  __initcall_start = .;
+  .initcall.init : {
+       *(.initcall1.init)
+       *(.initcall2.init)
+       *(.initcall3.init)
+       *(.initcall4.init)
+       *(.initcall5.init)
+       *(.initcall6.init)
+       *(.initcall7.init)
+  }
+  __initcall_end = .;
+  __con_initcall_start = .;
+  .con_initcall.init : { *(.con_initcall.init) }
+  __con_initcall_end = .;
+  SECURITY_INIT
+  . = ALIGN(4);
+  __alt_instructions = .;
+  .altinstructions : { *(.altinstructions) }
+  __alt_instructions_end = .;
+ .altinstr_replacement : { *(.altinstr_replacement) }
+
+  __per_cpu_start = .;
+  .data.percpu  : { *(.data.percpu) }
+  __per_cpu_end = .;
+
+  . = ALIGN(4096);
+  __initramfs_start = .;
+  .init.ramfs : { *(.init.ramfs) }
+  __initramfs_end = .;
+
+  . = ALIGN(THREAD_SIZE);
+  __init_end = .;
+
+  /* put sections together that have massive alignment issues */
+  . = ALIGN(THREAD_SIZE);
+  .data.init_task : {
+         /* init task record & stack */
+         *(.data.init_task)
+  }
+
+  .trap : {
+       /* trap table management - read entry-table.S before modifying */
+       . = ALIGN(8192);
+       __trap_tables = .;
+       *(.trap.user)
+       *(.trap.kernel)
+       . = ALIGN(4096);
+       *(.trap.break)
+  }
+
+  . = ALIGN(4096);
+  .data.page_aligned : { *(.data.idt) }
+
+  . = ALIGN(L1_CACHE_BYTES);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  /* Text and read-only data */
+  . = ALIGN(4);
+  _text = .;
+  _stext = .;
+  .text : {
+       *(
+               .text.start .text .text.*
+#ifdef CONFIG_DEBUG_INFO
+       .init.text
+       .exit.text
+       .exitcall.exit
+#endif
+       )
+       SCHED_TEXT
+       *(.fixup)
+       *(.gnu.warning)
+       *(.exitcall.exit)
+       } = 0x9090
+
+  _etext = .;                  /* End of text section */
+
+  RODATA
+
+  .rodata : {
+       *(.trap.vector)
+
+       /* this clause must not be modified - the ordering and adjacency are imperative */
+       __trap_fixup_tables = .;
+       *(.trap.fixup.user .trap.fixup.kernel)
+
+       }
+
+  . = ALIGN(8);                /* Exception table */
+  __start___ex_table = .;
+  __ex_table : { KEEP(*(__ex_table)) }
+  __stop___ex_table = .;
+
+  _sdata = .;
+  .data : {                    /* Data */
+       *(.data .data.*)
+       *(.exit.data)
+       CONSTRUCTORS
+       }
+
+  _edata = .;                  /* End of data section */
+
+  /* GP section */
+  . = ALIGN(L1_CACHE_BYTES);
+  _gp = . + 2048;
+  PROVIDE (gp = _gp);
+
+  .sdata : { *(.sdata .sdata.*) }
+
+  /* BSS */
+  . = ALIGN(L1_CACHE_BYTES);
+  __bss_start = .;
+
+  .sbss                : { *(.sbss .sbss.*) }
+  .bss         : { *(.bss .bss.*) }
+  .bss.stack   : { *(.bss) }
+
+  __bss_stop = .;
+  _end = . ;
+  . = ALIGN(PAGE_SIZE);
+  __kernel_image_end = .;
+
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+
+  .debug_line          0 : { *(.debug_line) }
+  .debug_info          0 : { *(.debug_info) }
+  .debug_abbrev                0 : { *(.debug_abbrev) }
+  .debug_aranges       0 : { *(.debug_aranges) }
+  .debug_frame         0 : { *(.debug_frame) }
+  .debug_pubnames      0 : { *(.debug_pubnames) }
+  .debug_str           0 : { *(.debug_str) }
+  .debug_ranges                0 : { *(.debug_ranges) }
+
+  .comment 0 : { *(.comment) }
+}
+
+__kernel_image_size_no_bss = __bss_start - __kernel_image_start;
diff --git a/arch/frv/lib/Makefile b/arch/frv/lib/Makefile
new file mode 100644 (file)
index 0000000..19be262
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for FRV-specific library files..
+#
+
+lib-y := \
+       __ashldi3.o __lshrdi3.o __muldi3.o __ashrdi3.o __negdi2.o \
+       checksum.o memcpy.o memset.o atomic-ops.o \
+       outsl_ns.o outsl_sw.o insl_ns.o insl_sw.o cache.o
diff --git a/arch/frv/lib/__ashldi3.S b/arch/frv/lib/__ashldi3.S
new file mode 100644 (file)
index 0000000..db5b6dc
--- /dev/null
@@ -0,0 +1,40 @@
+/* __ashldi3.S:        64-bit arithmetic shift left
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# unsigned long long __ashldi3(unsigned long long value [GR8:GR9], unsigned by [GR10])
+#
+###############################################################################
+        .globl         __ashldi3
+        .type          __ashldi3,@function
+__ashldi3:
+       andicc.p        gr10,#63,gr10,icc0
+       setlos          #32,gr5
+       andicc.p        gr10,#32,gr0,icc1
+       beqlr           icc0,#0
+       ckeq            icc1,cc4                        ; cc4 is true if 0<N<32
+
+       # deal with a shift in the range 1<=N<=31
+       csll.p          gr8,gr10,gr8    ,cc4,#1         ; MSW <<= N
+       csub            gr5,gr10,gr5    ,cc4,#1         ; M = 32 - N
+       csrl.p          gr9,gr5,gr4     ,cc4,#1
+       csll            gr9,gr10,gr9    ,cc4,#1         ; LSW <<= N
+       cor.p           gr4,gr8,gr8     ,cc4,#1         ; MSW |= LSW >> M
+
+       # deal with a shift in the range 32<=N<=63
+       csll            gr9,gr10,gr8    ,cc4,#0         ; MSW = LSW << (N & 31 [implicit AND])
+       cor.p           gr0,gr0,gr9     ,cc4,#0         ; LSW = 0
+       bralr
+       .size           __ashldi3, .-__ashldi3
diff --git a/arch/frv/lib/__ashrdi3.S b/arch/frv/lib/__ashrdi3.S
new file mode 100644 (file)
index 0000000..5742665
--- /dev/null
@@ -0,0 +1,41 @@
+/* __ashrdi3.S:        64-bit arithmetic shift right
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# signed long long __ashrdi3(signed long long value [GR8:GR9], unsigned by [GR10])
+#
+###############################################################################
+        .globl         __ashrdi3
+        .type          __ashrdi3,@function
+__ashrdi3:
+       andicc.p        gr10,#63,gr10,icc0
+       setlos          #32,gr5
+       andicc.p        gr10,#32,gr0,icc1
+       beqlr           icc0,#0
+       setlos.p        #31,gr6
+       ckeq            icc1,cc4                        ; cc4 is true if 0<N<32
+
+       # deal with a shift in the range 1<=N<=31
+       csrl.p          gr9,gr10,gr9    ,cc4,#1         ; LSW >>= N
+       csub            gr5,gr10,gr5    ,cc4,#1         ; M = 32 - N
+       csll.p          gr8,gr5,gr4     ,cc4,#1
+       csra            gr8,gr10,gr8    ,cc4,#1         ; MSW >>= N
+       cor.p           gr4,gr9,gr9     ,cc4,#1         ; LSW |= MSW << M
+
+       # deal with a shift in the range 32<=N<=63
+       csra            gr8,gr10,gr9    ,cc4,#0         ; LSW = MSW >> (N & 31 [implicit AND])
+       csra.p          gr8,gr6,gr8     ,cc4,#0         ; MSW >>= 31
+       bralr
+       .size           __ashrdi3, .-__ashrdi3
diff --git a/arch/frv/lib/__lshrdi3.S b/arch/frv/lib/__lshrdi3.S
new file mode 100644 (file)
index 0000000..7b41f63
--- /dev/null
@@ -0,0 +1,40 @@
+/* __lshrdi3.S:        64-bit logical shift right
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# unsigned long long __lshrdi3(unsigned long long value [GR8:GR9], unsigned by [GR10])
+#
+###############################################################################
+        .globl         __lshrdi3
+        .type          __lshrdi3,@function
+__lshrdi3:
+       andicc.p        gr10,#63,gr10,icc0
+       setlos          #32,gr5
+       andicc.p        gr10,#32,gr0,icc1
+       beqlr           icc0,#0
+       ckeq            icc1,cc4                        ; cc4 is true if 0<N<32
+
+       # deal with a shift in the range 1<=N<=31
+       csrl.p          gr9,gr10,gr9    ,cc4,#1         ; LSW >>= N
+       csub            gr5,gr10,gr5    ,cc4,#1         ; M = 32 - N
+       csll.p          gr8,gr5,gr4     ,cc4,#1
+       csrl            gr8,gr10,gr8    ,cc4,#1         ; MSW >>= N
+       cor.p           gr4,gr9,gr9     ,cc4,#1         ; LSW |= MSW << M
+
+       # deal with a shift in the range 32<=N<=63
+       csrl            gr8,gr10,gr9    ,cc4,#0         ; LSW = MSW >> (N & 31 [implicit AND])
+       cor.p           gr0,gr0,gr8     ,cc4,#0         ; MSW = 0
+       bralr
+       .size           __lshrdi3, .-__lshrdi3
diff --git a/arch/frv/lib/__muldi3.S b/arch/frv/lib/__muldi3.S
new file mode 100644 (file)
index 0000000..2703d9b
--- /dev/null
@@ -0,0 +1,32 @@
+/* __muldi3.S: 64-bit multiply
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# unsigned long long __muldi3(unsigned long long x [GR8:GR9],
+#                             unsigned long long y [GR10:GR11])
+#
+###############################################################################
+        .globl         __muldi3, __mulll, __umulll
+        .type          __muldi3,@function
+__muldi3:
+__mulll:
+__umulll:
+       umul            gr8,gr11,gr4            ; GR4:GR5 = x.MSW * y.LSW
+       umul            gr9,gr10,gr6            ; GR6:GR7 = x.LSW * y.MSW
+       umul.p          gr9,gr11,gr8            ; GR8:GR9 = x.LSW * y.LSW
+       add             gr5,gr7,gr5
+       add.p           gr8,gr5,gr8             ; GR8 += GR5 + GR7
+       bralr
+       .size           __muldi3, .-__muldi3
diff --git a/arch/frv/lib/__negdi2.S b/arch/frv/lib/__negdi2.S
new file mode 100644 (file)
index 0000000..d1747bf
--- /dev/null
@@ -0,0 +1,28 @@
+/* __negdi2.S: 64-bit negate
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# unsigned long long __negdi2(unsigned long long value [GR8:GR9])
+#
+###############################################################################
+        .globl         __negdi2
+        .type          __negdi2,@function
+__negdi2:
+       subcc           gr0,gr9,gr9,icc0
+       subx            gr0,gr8,gr8,icc0
+       bralr
+       .size           __negdi2, .-__negdi2
+
diff --git a/arch/frv/lib/atomic-ops.S b/arch/frv/lib/atomic-ops.S
new file mode 100644 (file)
index 0000000..b03d510
--- /dev/null
@@ -0,0 +1,265 @@
+/* atomic-ops.S: kernel atomic operations
+ *
+ * For an explanation of how atomic ops work in this arch, see:
+ *   Documentation/fujitsu/frv/atomic-ops.txt
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/spr-regs.h>
+
+       .text
+       .balign 4
+
+###############################################################################
+#
+# unsigned long atomic_test_and_ANDNOT_mask(unsigned long mask, volatile unsigned long *v);
+#
+###############################################################################
+       .globl          atomic_test_and_ANDNOT_mask
+        .type          atomic_test_and_ANDNOT_mask,@function
+atomic_test_and_ANDNOT_mask:
+       not.p           gr8,gr10
+0:
+       orcc            gr0,gr0,gr0,icc3                /* set ICC3.Z */
+       ckeq            icc3,cc7
+       ld.p            @(gr9,gr0),gr8                  /* LD.P/ORCR must be atomic */
+       orcr            cc7,cc7,cc3                     /* set CC3 to true */
+       and             gr8,gr10,gr11
+       cst.p           gr11,@(gr9,gr0)         ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1 /* clear ICC3.Z if store happens */
+       beq             icc3,#0,0b
+       bralr
+
+       .size           atomic_test_and_ANDNOT_mask, .-atomic_test_and_ANDNOT_mask
+
+###############################################################################
+#
+# unsigned long atomic_test_and_OR_mask(unsigned long mask, volatile unsigned long *v);
+#
+###############################################################################
+       .globl          atomic_test_and_OR_mask
+        .type          atomic_test_and_OR_mask,@function
+atomic_test_and_OR_mask:
+       or.p            gr8,gr8,gr10
+0:
+       orcc            gr0,gr0,gr0,icc3                /* set ICC3.Z */
+       ckeq            icc3,cc7
+       ld.p            @(gr9,gr0),gr8                  /* LD.P/ORCR must be atomic */
+       orcr            cc7,cc7,cc3                     /* set CC3 to true */
+       or              gr8,gr10,gr11
+       cst.p           gr11,@(gr9,gr0)         ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1 /* clear ICC3.Z if store happens */
+       beq             icc3,#0,0b
+       bralr
+
+       .size           atomic_test_and_OR_mask, .-atomic_test_and_OR_mask
+
+###############################################################################
+#
+# unsigned long atomic_test_and_XOR_mask(unsigned long mask, volatile unsigned long *v);
+#
+###############################################################################
+       .globl          atomic_test_and_XOR_mask
+        .type          atomic_test_and_XOR_mask,@function
+atomic_test_and_XOR_mask:
+       or.p            gr8,gr8,gr10
+0:
+       orcc            gr0,gr0,gr0,icc3                /* set ICC3.Z */
+       ckeq            icc3,cc7
+       ld.p            @(gr9,gr0),gr8                  /* LD.P/ORCR must be atomic */
+       orcr            cc7,cc7,cc3                     /* set CC3 to true */
+       xor             gr8,gr10,gr11
+       cst.p           gr11,@(gr9,gr0)         ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1 /* clear ICC3.Z if store happens */
+       beq             icc3,#0,0b
+       bralr
+
+       .size           atomic_test_and_XOR_mask, .-atomic_test_and_XOR_mask
+
+###############################################################################
+#
+# int atomic_add_return(int i, atomic_t *v)
+#
+###############################################################################
+       .globl          atomic_add_return
+        .type          atomic_add_return,@function
+atomic_add_return:
+       or.p            gr8,gr8,gr10
+0:
+       orcc            gr0,gr0,gr0,icc3                /* set ICC3.Z */
+       ckeq            icc3,cc7
+       ld.p            @(gr9,gr0),gr8                  /* LD.P/ORCR must be atomic */
+       orcr            cc7,cc7,cc3                     /* set CC3 to true */
+       add             gr8,gr10,gr8
+       cst.p           gr8,@(gr9,gr0)          ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1 /* clear ICC3.Z if store happens */
+       beq             icc3,#0,0b
+       bralr
+
+       .size           atomic_add_return, .-atomic_add_return
+
+###############################################################################
+#
+# int atomic_sub_return(int i, atomic_t *v)
+#
+###############################################################################
+       .globl          atomic_sub_return
+        .type          atomic_sub_return,@function
+atomic_sub_return:
+       or.p            gr8,gr8,gr10
+0:
+       orcc            gr0,gr0,gr0,icc3                /* set ICC3.Z */
+       ckeq            icc3,cc7
+       ld.p            @(gr9,gr0),gr8                  /* LD.P/ORCR must be atomic */
+       orcr            cc7,cc7,cc3                     /* set CC3 to true */
+       sub             gr8,gr10,gr8
+       cst.p           gr8,@(gr9,gr0)          ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1 /* clear ICC3.Z if store happens */
+       beq             icc3,#0,0b
+       bralr
+
+       .size           atomic_sub_return, .-atomic_sub_return
+
+###############################################################################
+#
+# uint8_t __xchg_8(uint8_t i, uint8_t *v)
+#
+###############################################################################
+       .globl          __xchg_8
+        .type          __xchg_8,@function
+__xchg_8:
+       or.p            gr8,gr8,gr10
+0:
+       orcc            gr0,gr0,gr0,icc3                /* set ICC3.Z */
+       ckeq            icc3,cc7
+       ldub.p          @(gr9,gr0),gr8                  /* LD.P/ORCR must be atomic */
+       orcr            cc7,cc7,cc3                     /* set CC3 to true */
+       cstb.p          gr10,@(gr9,gr0)         ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1 /* clear ICC3.Z if store happens */
+       beq             icc3,#0,0b
+       bralr
+
+       .size           __xchg_8, .-__xchg_8
+
+###############################################################################
+#
+# uint16_t __xchg_16(uint16_t i, uint16_t *v)
+#
+###############################################################################
+       .globl          __xchg_16
+        .type          __xchg_16,@function
+__xchg_16:
+       or.p            gr8,gr8,gr10
+0:
+       orcc            gr0,gr0,gr0,icc3                /* set ICC3.Z */
+       ckeq            icc3,cc7
+       lduh.p          @(gr9,gr0),gr8                  /* LD.P/ORCR must be atomic */
+       orcr            cc7,cc7,cc3                     /* set CC3 to true */
+       csth.p          gr10,@(gr9,gr0)         ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1 /* clear ICC3.Z if store happens */
+       beq             icc3,#0,0b
+       bralr
+
+       .size           __xchg_16, .-__xchg_16
+
+###############################################################################
+#
+# uint32_t __xchg_32(uint32_t i, uint32_t *v)
+#
+###############################################################################
+       .globl          __xchg_32
+        .type          __xchg_32,@function
+__xchg_32:
+       or.p            gr8,gr8,gr10
+0:
+       orcc            gr0,gr0,gr0,icc3                /* set ICC3.Z */
+       ckeq            icc3,cc7
+       ld.p            @(gr9,gr0),gr8                  /* LD.P/ORCR must be atomic */
+       orcr            cc7,cc7,cc3                     /* set CC3 to true */
+       cst.p           gr10,@(gr9,gr0)         ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1 /* clear ICC3.Z if store happens */
+       beq             icc3,#0,0b
+       bralr
+
+       .size           __xchg_32, .-__xchg_32
+
+###############################################################################
+#
+# uint8_t __cmpxchg_8(uint8_t *v, uint8_t test, uint8_t new)
+#
+###############################################################################
+       .globl          __cmpxchg_8
+        .type          __cmpxchg_8,@function
+__cmpxchg_8:
+       or.p            gr8,gr8,gr11
+0:
+       orcc            gr0,gr0,gr0,icc3
+       ckeq            icc3,cc7
+       ldub.p          @(gr11,gr0),gr8
+       orcr            cc7,cc7,cc3
+       sub             gr8,gr9,gr7
+       sllicc          gr7,#24,gr0,icc0
+       bne             icc0,#0,1f
+       cstb.p          gr10,@(gr11,gr0)        ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1
+       beq             icc3,#0,0b
+1:
+       bralr
+
+       .size           __cmpxchg_8, .-__cmpxchg_8
+
+###############################################################################
+#
+# uint16_t __cmpxchg_16(uint16_t *v, uint16_t test, uint16_t new)
+#
+###############################################################################
+       .globl          __cmpxchg_16
+        .type          __cmpxchg_16,@function
+__cmpxchg_16:
+       or.p            gr8,gr8,gr11
+0:
+       orcc            gr0,gr0,gr0,icc3
+       ckeq            icc3,cc7
+       lduh.p          @(gr11,gr0),gr8
+       orcr            cc7,cc7,cc3
+       sub             gr8,gr9,gr7
+       sllicc          gr7,#16,gr0,icc0
+       bne             icc0,#0,1f
+       csth.p          gr10,@(gr11,gr0)        ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1
+       beq             icc3,#0,0b
+1:
+       bralr
+
+       .size           __cmpxchg_16, .-__cmpxchg_16
+
+###############################################################################
+#
+# uint32_t __cmpxchg_32(uint32_t *v, uint32_t test, uint32_t new)
+#
+###############################################################################
+       .globl          __cmpxchg_32
+        .type          __cmpxchg_32,@function
+__cmpxchg_32:
+       or.p            gr8,gr8,gr11
+0:
+       orcc            gr0,gr0,gr0,icc3
+       ckeq            icc3,cc7
+       ld.p            @(gr11,gr0),gr8
+       orcr            cc7,cc7,cc3
+       subcc           gr8,gr9,gr7,icc0
+       bne             icc0,#0,1f
+       cst.p           gr10,@(gr11,gr0)        ,cc3,#1
+       corcc           gr29,gr29,gr0           ,cc3,#1
+       beq             icc3,#0,0b
+1:
+       bralr
+
+       .size           __cmpxchg_32, .-__cmpxchg_32
diff --git a/arch/frv/lib/cache.S b/arch/frv/lib/cache.S
new file mode 100644 (file)
index 0000000..0e10ad8
--- /dev/null
@@ -0,0 +1,98 @@
+/* cache.S: cache managment routines
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/spr-regs.h>
+#include <asm/cache.h>
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# Write back a range of dcache
+# - void frv_dcache_writeback(unsigned long start [GR8], unsigned long size [GR9])
+#
+###############################################################################
+        .globl         frv_dcache_writeback
+        .type          frv_dcache_writeback,@function
+frv_dcache_writeback:
+       andi            gr8,~(L1_CACHE_BYTES-1),gr8
+
+2:     dcf             @(gr8,gr0)
+       addi            gr8,#L1_CACHE_BYTES,gr8
+       cmp             gr9,gr8,icc0
+       bhi             icc0,#2,2b
+
+       membar
+       bralr
+       .size           frv_dcache_writeback, .-frv_dcache_writeback
+
+##############################################################################
+#
+# Invalidate a range of dcache and icache
+# - void frv_cache_invalidate(unsigned long start [GR8], unsigned long end [GR9]);
+#
+###############################################################################
+        .globl         frv_cache_invalidate
+        .type          frv_cache_invalidate,@function
+frv_cache_invalidate:
+       andi            gr8,~(L1_CACHE_BYTES-1),gr8
+
+2:     dci             @(gr8,gr0)
+       ici             @(gr8,gr0)
+       addi            gr8,#L1_CACHE_BYTES,gr8
+       cmp             gr9,gr8,icc0
+       bhi             icc0,#2,2b
+
+       membar
+       bralr
+       .size           frv_cache_invalidate, .-frv_cache_invalidate
+
+##############################################################################
+#
+# Invalidate a range of icache
+# - void frv_icache_invalidate(unsigned long start [GR8], unsigned long end [GR9]);
+#
+###############################################################################
+        .globl         frv_icache_invalidate
+        .type          frv_icache_invalidate,@function
+frv_icache_invalidate:
+       andi            gr8,~(L1_CACHE_BYTES-1),gr8
+
+2:     ici             @(gr8,gr0)
+       addi            gr8,#L1_CACHE_BYTES,gr8
+       cmp             gr9,gr8,icc0
+       bhi             icc0,#2,2b
+
+       membar
+       bralr
+       .size           frv_icache_invalidate, .-frv_icache_invalidate
+
+###############################################################################
+#
+# Write back and invalidate a range of dcache and icache
+# - void frv_cache_wback_inv(unsigned long start [GR8], unsigned long end [GR9])
+#
+###############################################################################
+        .globl         frv_cache_wback_inv
+        .type          frv_cache_wback_inv,@function
+frv_cache_wback_inv:
+       andi            gr8,~(L1_CACHE_BYTES-1),gr8
+
+2:     dcf             @(gr8,gr0)
+       ici             @(gr8,gr0)
+       addi            gr8,#L1_CACHE_BYTES,gr8
+       cmp             gr9,gr8,icc0
+       bhi             icc0,#2,2b
+
+       membar
+       bralr
+       .size           frv_cache_wback_inv, .-frv_cache_wback_inv
diff --git a/arch/frv/lib/checksum.c b/arch/frv/lib/checksum.c
new file mode 100644 (file)
index 0000000..7bf5bd6
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * INET                An implementation of the TCP/IP protocol suite for the LINUX
+ *             operating system.  INET is implemented using the  BSD Socket
+ *             interface as the means of communication with the user level.
+ *
+ *             IP/TCP/UDP checksumming routines
+ *
+ * Authors:    Jorge Cwik, <jorge@laser.satlink.net>
+ *             Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ *             Tom May, <ftom@netcom.com>
+ *             Andreas Schwab, <schwab@issan.informatik.uni-dortmund.de>
+ *             Lots of code moved from tcp.c and ip.c; see those files
+ *             for more names.
+ *
+ * 03/02/96    Jes Sorensen, Andreas Schwab, Roman Hodek:
+ *             Fixed some nasty bugs, causing some horrible crashes.
+ *             A: At some points, the sum (%0) was used as
+ *             length-counter instead of the length counter
+ *             (%1). Thanks to Roman Hodek for pointing this out.
+ *             B: GCC seems to mess up if one uses too many
+ *             data-registers to hold input values and one tries to
+ *             specify d0 and d1 as scratch registers. Letting gcc choose these
+ *      registers itself solves the problem.
+ *
+ *             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.
+ */
+
+/* Revised by Kenneth Albanowski for m68knommu. Basic problem: unaligned access kills, so most
+   of the assembly has to go. */
+
+#include <net/checksum.h>
+#include <asm/checksum.h>
+
+static inline unsigned short from32to16(unsigned long x)
+{
+       /* add up 16-bit and 16-bit for 16+c bit */
+       x = (x & 0xffff) + (x >> 16);
+       /* add up carry.. */
+       x = (x & 0xffff) + (x >> 16);
+       return x;
+}
+
+static unsigned long do_csum(const unsigned char * buff, int len)
+{
+       int odd, count;
+       unsigned long result = 0;
+
+       if (len <= 0)
+               goto out;
+       odd = 1 & (unsigned long) buff;
+       if (odd) {
+               result = *buff;
+               len--;
+               buff++;
+       }
+       count = len >> 1;               /* nr of 16-bit words.. */
+       if (count) {
+               if (2 & (unsigned long) buff) {
+                       result += *(unsigned short *) buff;
+                       count--;
+                       len -= 2;
+                       buff += 2;
+               }
+               count >>= 1;            /* nr of 32-bit words.. */
+               if (count) {
+                       unsigned long carry = 0;
+                       do {
+                               unsigned long w = *(unsigned long *) buff;
+                               count--;
+                               buff += 4;
+                               result += carry;
+                               result += w;
+                               carry = (w > result);
+                       } while (count);
+                       result += carry;
+                       result = (result & 0xffff) + (result >> 16);
+               }
+               if (len & 2) {
+                       result += *(unsigned short *) buff;
+                       buff += 2;
+               }
+       }
+       if (len & 1)
+               result += (*buff << 8);
+       result = from32to16(result);
+       if (odd)
+               result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+       return result;
+}
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
+{
+       unsigned int result = do_csum(buff, len);
+
+       /* add in old sum, and carry.. */
+       result += sum;
+       if (sum > result)
+               result += 1;
+       return result;
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+unsigned short ip_compute_csum(const unsigned char * buff, int len)
+{
+       return ~do_csum(buff,len);
+}
+
+/*
+ * copy from fs while checksumming, otherwise like csum_partial
+ */
+
+unsigned int
+csum_partial_copy_from_user(const char *src, char *dst, int len, int sum, int *csum_err)
+{
+       if (csum_err) *csum_err = 0;
+       memcpy(dst, src, len);
+       return csum_partial(dst, len, sum);
+}
+
+/*
+ * copy from ds while checksumming, otherwise like csum_partial
+ */
+
+unsigned int
+csum_partial_copy(const char *src, char *dst, int len, int sum)
+{
+       memcpy(dst, src, len);
+       return csum_partial(dst, len, sum);
+}
diff --git a/arch/frv/lib/insl_ns.S b/arch/frv/lib/insl_ns.S
new file mode 100644 (file)
index 0000000..d165842
--- /dev/null
@@ -0,0 +1,52 @@
+/* insl_ns.S: input array of 4b words from device port without byte swapping
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# void __insl_ns(unsigned int port, void *buf, int n)
+#
+###############################################################################
+        .globl         __insl_ns
+        .type          __insl_ns,@function
+__insl_ns:
+       andicc.p        gr9,#3,gr0,icc0
+       setlos          #4,gr4
+       bne             icc0,#0,__insl_ns_misaligned
+       subi            gr9,#4,gr9
+0:
+       ldi.p           @(gr8,#0),gr5
+       subicc          gr10,#1,gr10,icc0
+       stu.p           gr5,@(gr9,gr4)
+       bhi             icc0,#2,0b
+       bralr
+
+__insl_ns_misaligned:
+       subi.p          gr9,#1,gr9
+       setlos          #1,gr4
+0:
+       ldi             @(gr8,#0),gr5
+
+       srli            gr5,#24,gr6
+       stbu.p          gr6,@(gr9,gr4)
+       srli            gr5,#16,gr6
+       stbu.p          gr6,@(gr9,gr4)
+       srli            gr5,#8,gr6
+       stbu.p          gr6,@(gr9,gr4)
+       subicc          gr10,#1,gr10,icc0
+       stbu.p          gr5,@(gr9,gr4)
+       bhi             icc0,#2,0b
+       bralr
+
+       .size           __insl_ns, .-__insl_ns
diff --git a/arch/frv/lib/insl_sw.S b/arch/frv/lib/insl_sw.S
new file mode 100644 (file)
index 0000000..9b5aa95
--- /dev/null
@@ -0,0 +1,40 @@
+/* insl_sw.S: input array of 4b words from device port with byte swapping
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# void __insl_sw(unsigned int port, void *buf, int n)
+#
+###############################################################################
+        .globl         __insl_sw
+        .type          __insl_sw,@function
+__insl_sw:
+       subi.p          gr9,#1,gr9
+       setlos          #1,gr4
+0:
+       ldi.p           @(gr8,#0),gr5           ; get 0xAABBCCDD
+       subicc          gr10,#1,gr10,icc0
+
+       stbu.p          gr5,@(gr9,gr4)          ; write 0xDD
+       srli            gr5,#8,gr5
+       stbu.p          gr5,@(gr9,gr4)          ; write 0xCC
+       srli            gr5,#8,gr5
+       stbu.p          gr5,@(gr9,gr4)          ; write 0xBB
+       srli            gr5,#8,gr5
+       stbu.p          gr5,@(gr9,gr4)          ; write 0xAA
+       bhi             icc0,#2,0b
+       bralr
+
+       .size           __insl_sw, .-__insl_sw
diff --git a/arch/frv/lib/memcpy.S b/arch/frv/lib/memcpy.S
new file mode 100644 (file)
index 0000000..9c59652
--- /dev/null
@@ -0,0 +1,135 @@
+/* memcpy.S: optimised assembly memcpy
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# void *memcpy(void *to, const char *from, size_t count)
+#
+# - NOTE: must not use any stack. exception detection performs function return
+#         to caller's fixup routine, aborting the remainder of the copy
+#
+###############################################################################
+        .globl         memcpy,__memcpy_end
+        .type          memcpy,@function
+memcpy:
+       or.p            gr8,gr9,gr4
+       orcc            gr10,gr0,gr0,icc3
+       or.p            gr10,gr4,gr4
+       beqlr           icc3,#0
+
+       # optimise based on best common alignment for to, from & count
+       andicc.p        gr4,#0x0f,gr0,icc0
+       setlos          #8,gr11
+       andicc.p        gr4,#0x07,gr0,icc1
+       beq             icc0,#0,memcpy_16
+       andicc.p        gr4,#0x03,gr0,icc0
+       beq             icc1,#0,memcpy_8
+       andicc.p        gr4,#0x01,gr0,icc1
+       beq             icc0,#0,memcpy_4
+       setlos.p        #1,gr11
+       beq             icc1,#0,memcpy_2
+
+       # do byte by byte copy
+       sub.p           gr8,gr11,gr3
+       sub             gr9,gr11,gr9
+0:     ldubu.p         @(gr9,gr11),gr4
+       subicc          gr10,#1,gr10,icc0
+       stbu.p          gr4,@(gr3,gr11)
+       bne             icc0,#2,0b
+       bralr
+
+       # do halfword by halfword copy
+memcpy_2:
+       setlos          #2,gr11
+       sub.p           gr8,gr11,gr3
+       sub             gr9,gr11,gr9
+0:     lduhu.p         @(gr9,gr11),gr4
+       subicc          gr10,#2,gr10,icc0
+       sthu.p          gr4,@(gr3,gr11)
+       bne             icc0,#2,0b
+       bralr
+
+       # do word by word copy
+memcpy_4:
+       setlos          #4,gr11
+       sub.p           gr8,gr11,gr3
+       sub             gr9,gr11,gr9
+0:     ldu.p           @(gr9,gr11),gr4
+       subicc          gr10,#4,gr10,icc0
+       stu.p           gr4,@(gr3,gr11)
+       bne             icc0,#2,0b
+       bralr
+
+       # do double-word by double-word copy
+memcpy_8:
+       sub.p           gr8,gr11,gr3
+       sub             gr9,gr11,gr9
+0:     lddu.p          @(gr9,gr11),gr4
+       subicc          gr10,#8,gr10,icc0
+       stdu.p          gr4,@(gr3,gr11)
+       bne             icc0,#2,0b
+       bralr
+
+       # do quad-word by quad-word copy
+memcpy_16:
+       sub.p           gr8,gr11,gr3
+       sub             gr9,gr11,gr9
+0:     lddu            @(gr9,gr11),gr4
+       lddu.p          @(gr9,gr11),gr6
+       subicc          gr10,#16,gr10,icc0
+       stdu            gr4,@(gr3,gr11)
+       stdu.p          gr6,@(gr3,gr11)
+       bne             icc0,#2,0b
+       bralr
+__memcpy_end:
+
+       .size           memcpy, __memcpy_end-memcpy
+
+###############################################################################
+#
+# copy to/from userspace
+# - return the number of bytes that could not be copied (0 on complete success)
+#
+# long __memcpy_user(void *dst, const void *src, size_t count)
+#
+###############################################################################
+        .globl         __memcpy_user, __memcpy_user_error_lr, __memcpy_user_error_handler
+        .type          __memcpy_user,@function
+__memcpy_user:
+       movsg           lr,gr7
+       subi.p          sp,#8,sp
+       add             gr8,gr10,gr6            ; calculate expected end address
+       stdi            gr6,@(sp,#0)
+
+       # abuse memcpy to do the dirty work
+       call            memcpy
+__memcpy_user_error_lr:
+       ldi.p           @(sp,#4),gr7
+       setlos          #0,gr8
+       jmpl.p          @(gr7,gr0)
+       addi            sp,#8,sp
+
+       # deal any exception generated by memcpy
+       # GR8 - memcpy's current dest address
+       # GR11 - memset's step value (index register for store insns)
+__memcpy_user_error_handler:
+       lddi.p          @(sp,#0),gr4            ; load GR4 with dst+count, GR5 with ret addr
+       add             gr11,gr3,gr7
+       sub.p           gr4,gr7,gr8
+
+       addi            sp,#8,sp
+       jmpl            @(gr5,gr0)
+
+       .size           __memcpy_user, .-__memcpy_user
diff --git a/arch/frv/lib/memset.S b/arch/frv/lib/memset.S
new file mode 100644 (file)
index 0000000..55a3526
--- /dev/null
@@ -0,0 +1,182 @@
+/* memset.S: optimised assembly memset
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# void *memset(void *p, char ch, size_t count)
+#
+# - NOTE: must not use any stack. exception detection performs function return
+#         to caller's fixup routine, aborting the remainder of the set
+#         GR4, GR7, GR8, and GR11 must be managed
+#
+###############################################################################
+        .globl         memset,__memset_end
+        .type          memset,@function
+memset:
+       orcc.p          gr10,gr0,gr5,icc3               ; GR5 = count
+       andi            gr9,#0xff,gr9
+       or.p            gr8,gr0,gr4                     ; GR4 = address
+       beqlr           icc3,#0
+
+       # conditionally write a byte to 2b-align the address
+       setlos.p        #1,gr6
+       andicc          gr4,#1,gr0,icc0
+       ckne            icc0,cc7
+       cstb.p          gr9,@(gr4,gr0)          ,cc7,#1
+       csubcc          gr5,gr6,gr5             ,cc7,#1 ; also set ICC3
+       cadd.p          gr4,gr6,gr4             ,cc7,#1
+       beqlr           icc3,#0
+
+       # conditionally write a word to 4b-align the address
+       andicc.p        gr4,#2,gr0,icc0
+       subicc          gr5,#2,gr0,icc1
+       setlos.p        #2,gr6
+       ckne            icc0,cc7
+       slli.p          gr9,#8,gr12                     ; need to double up the pattern
+       cknc            icc1,cc5
+       or.p            gr9,gr12,gr12
+       andcr           cc7,cc5,cc7
+
+       csth.p          gr12,@(gr4,gr0)         ,cc7,#1
+       csubcc          gr5,gr6,gr5             ,cc7,#1 ; also set ICC3
+       cadd.p          gr4,gr6,gr4             ,cc7,#1
+       beqlr           icc3,#0
+
+       # conditionally write a dword to 8b-align the address
+       andicc.p        gr4,#4,gr0,icc0
+       subicc          gr5,#4,gr0,icc1
+       setlos.p        #4,gr6
+       ckne            icc0,cc7
+       slli.p          gr12,#16,gr13                   ; need to quadruple-up the pattern
+       cknc            icc1,cc5
+       or.p            gr13,gr12,gr12
+       andcr           cc7,cc5,cc7
+
+       cst.p           gr12,@(gr4,gr0)         ,cc7,#1
+       csubcc          gr5,gr6,gr5             ,cc7,#1 ; also set ICC3
+       cadd.p          gr4,gr6,gr4             ,cc7,#1
+       beqlr           icc3,#0
+
+       or.p            gr12,gr12,gr13                  ; need to octuple-up the pattern
+
+       # the address is now 8b-aligned - loop around writing 64b chunks
+       setlos          #8,gr7
+       subi.p          gr4,#8,gr4                      ; store with update index does weird stuff
+       setlos          #64,gr6
+
+       subicc          gr5,#64,gr0,icc0
+0:     cknc            icc0,cc7
+       cstdu           gr12,@(gr4,gr7)         ,cc7,#1
+       cstdu           gr12,@(gr4,gr7)         ,cc7,#1
+       cstdu           gr12,@(gr4,gr7)         ,cc7,#1
+       cstdu           gr12,@(gr4,gr7)         ,cc7,#1
+       cstdu           gr12,@(gr4,gr7)         ,cc7,#1
+       cstdu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       csubcc          gr5,gr6,gr5             ,cc7,#1 ; also set ICC3
+       cstdu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       subicc          gr5,#64,gr0,icc0
+       cstdu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       beqlr           icc3,#0
+       bnc             icc0,#2,0b
+
+       # now do 32-byte remnant
+       subicc.p        gr5,#32,gr0,icc0
+       setlos          #32,gr6
+       cknc            icc0,cc7
+       cstdu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       csubcc          gr5,gr6,gr5             ,cc7,#1 ; also set ICC3
+       cstdu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       setlos          #16,gr6
+       cstdu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       subicc          gr5,#16,gr0,icc0
+       cstdu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       beqlr           icc3,#0
+
+       # now do 16-byte remnant
+       cknc            icc0,cc7
+       cstdu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       csubcc          gr5,gr6,gr5             ,cc7,#1 ; also set ICC3
+       cstdu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       beqlr           icc3,#0
+
+       # now do 8-byte remnant
+       subicc          gr5,#8,gr0,icc1
+       cknc            icc1,cc7
+       cstdu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       csubcc          gr5,gr7,gr5             ,cc7,#1 ; also set ICC3
+       setlos.p        #4,gr7
+       beqlr           icc3,#0
+
+       # now do 4-byte remnant
+       subicc          gr5,#4,gr0,icc0
+       addi.p          gr4,#4,gr4
+       cknc            icc0,cc7
+       cstu.p          gr12,@(gr4,gr7)         ,cc7,#1
+       csubcc          gr5,gr7,gr5             ,cc7,#1 ; also set ICC3
+       subicc.p        gr5,#2,gr0,icc1
+       beqlr           icc3,#0
+
+       # now do 2-byte remnant
+       setlos          #2,gr7
+       addi.p          gr4,#2,gr4
+       cknc            icc1,cc7
+       csthu.p         gr12,@(gr4,gr7)         ,cc7,#1
+       csubcc          gr5,gr7,gr5             ,cc7,#1 ; also set ICC3
+       subicc.p        gr5,#1,gr0,icc0
+       beqlr           icc3,#0
+
+       # now do 1-byte remnant
+       setlos          #0,gr7
+       addi.p          gr4,#2,gr4
+       cknc            icc0,cc7
+       cstb.p          gr12,@(gr4,gr0)         ,cc7,#1
+       bralr
+__memset_end:
+
+       .size           memset, __memset_end-memset
+
+###############################################################################
+#
+# clear memory in userspace
+# - return the number of bytes that could not be cleared (0 on complete success)
+#
+# long __memset_user(void *p, size_t count)
+#
+###############################################################################
+        .globl         __memset_user, __memset_user_error_lr, __memset_user_error_handler
+        .type          __memset_user,@function
+__memset_user:
+       movsg           lr,gr11
+
+       # abuse memset to do the dirty work
+       or.p            gr9,gr9,gr10
+       setlos          #0,gr9
+       call            memset
+__memset_user_error_lr:
+       jmpl.p          @(gr11,gr0)
+       setlos          #0,gr8
+
+       # deal any exception generated by memset
+       # GR4  - memset's address tracking pointer
+       # GR7  - memset's step value (index register for store insns)
+       # GR8  - memset's original start address
+       # GR10 - memset's original count
+__memset_user_error_handler:
+       add.p           gr4,gr7,gr4
+       add             gr8,gr10,gr8
+       jmpl.p          @(gr11,gr0)
+       sub             gr8,gr4,gr8             ; we return the amount left uncleared
+
+       .size           __memset_user, .-__memset_user
diff --git a/arch/frv/lib/outsl_ns.S b/arch/frv/lib/outsl_ns.S
new file mode 100644 (file)
index 0000000..4cd4c46
--- /dev/null
@@ -0,0 +1,59 @@
+/* outsl_ns.S: output array of 4b words to device without byte swapping
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# void __outsl_ns(unsigned int port, const void *buf, int n)
+#
+###############################################################################
+        .globl         __outsl_ns
+        .type          __outsl_ns,@function
+__outsl_ns:
+       andicc.p        gr9,#3,gr0,icc0
+       setlos          #4,gr4
+       bne             icc0,#0,__outsl_ns_misaligned
+       subi            gr9,#4,gr9
+0:
+       ldu.p           @(gr9,gr4),gr5
+       subicc          gr10,#1,gr10,icc0
+       sti.p           gr5,@(gr8,#0)
+       bhi             icc0,#2,0b
+
+       membar
+       bralr
+
+__outsl_ns_misaligned:
+       subi.p          gr9,#1,gr9
+       setlos          #1,gr4
+0:
+       ldubu           @(gr9,gr4),gr5
+       ldubu.p         @(gr9,gr4),gr6
+       slli            gr5,#8,gr5
+       ldubu.p         @(gr9,gr4),gr7
+       or              gr5,gr6,gr5
+       ldubu.p         @(gr9,gr4),gr6
+       slli            gr5,#16,gr5
+       slli.p          gr7,#8,gr7
+       or              gr5,gr6,gr5
+       subicc.p        gr10,#1,gr10,icc0
+       or              gr5,gr7,gr5
+
+       sti.p           gr5,@(gr8,#0)
+       bhi             icc0,#2,0b
+
+       membar
+       bralr
+
+       .size           __outsl_ns, .-__outsl_ns
diff --git a/arch/frv/lib/outsl_sw.S b/arch/frv/lib/outsl_sw.S
new file mode 100644 (file)
index 0000000..7eb56d3
--- /dev/null
@@ -0,0 +1,45 @@
+/* outsl_ns.S: output array of 4b words to device with byte swapping
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+        .text
+        .p2align       4
+
+###############################################################################
+#
+# void __outsl_sw(unsigned int port, const void *buf, int n)
+#
+###############################################################################
+        .globl         __outsl_sw
+        .type          __outsl_sw,@function
+__outsl_sw:
+       subi.p          gr9,#1,gr9
+       setlos          #1,gr4
+0:
+       ldubu           @(gr9,gr4),gr5
+       ldubu           @(gr9,gr4),gr6
+       slli            gr6,#8,gr6
+       ldubu.p         @(gr9,gr4),gr7
+       or              gr5,gr6,gr5
+       ldubu.p         @(gr9,gr4),gr6
+       slli            gr7,#16,gr7
+       slli.p          gr6,#24,gr6
+       or              gr5,gr7,gr5
+       subicc.p        gr10,#1,gr10,icc0
+       or              gr5,gr6,gr5
+
+       sti.p           gr5,@(gr8,#0)
+       bhi             icc0,#2,0b
+
+       membar
+       bralr
+
+       .size           __outsl_sw, .-__outsl_sw
diff --git a/arch/frv/mb93090-mb00/Makefile b/arch/frv/mb93090-mb00/Makefile
new file mode 100644 (file)
index 0000000..3faf0f8
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# Makefile for the MB93090-MB00 motherboard stuff
+#
+
+ifeq "$(CONFIG_PCI)" "y"
+obj-y := pci-frv.o pci-irq.o pci-vdk.o
+
+ifeq "$(CONFIG_MMU)" "y"
+obj-y += pci-dma.o
+else
+obj-y += pci-dma-nommu.o
+endif
+endif
diff --git a/arch/frv/mb93090-mb00/pci-dma-nommu.c b/arch/frv/mb93090-mb00/pci-dma-nommu.c
new file mode 100644 (file)
index 0000000..819895c
--- /dev/null
@@ -0,0 +1,152 @@
+/* pci-dma-nommu.c: Dynamic DMA mapping support for the FRV
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Woodhouse (dwmw2@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#if 1
+#define DMA_SRAM_START dma_coherent_mem_start
+#define DMA_SRAM_END   dma_coherent_mem_end
+#else // Use video RAM on Matrox
+#define DMA_SRAM_START 0xe8900000
+#define DMA_SRAM_END   0xe8a00000
+#endif
+
+struct dma_alloc_record {
+       struct list_head        list;
+       unsigned long           ofs;
+       unsigned long           len;
+};
+
+static DEFINE_SPINLOCK(dma_alloc_lock);
+static LIST_HEAD(dma_alloc_list);
+
+void *dma_alloc_coherent(struct device *hwdev, size_t size, dma_addr_t *dma_handle, int gfp)
+{
+       struct dma_alloc_record *new;
+       struct list_head *this = &dma_alloc_list;
+       unsigned long flags;
+       unsigned long start = DMA_SRAM_START;
+       unsigned long end;
+
+       if (!DMA_SRAM_START) {
+               printk("%s called without any DMA area reserved!\n", __func__);
+               return NULL;
+       }
+
+       new = kmalloc(sizeof (*new), GFP_ATOMIC);
+       if (!new)
+               return NULL;
+
+       /* Round up to a reasonable alignment */
+       new->len = (size + 31) & ~31;
+
+       spin_lock_irqsave(&dma_alloc_lock, flags);
+
+       list_for_each (this, &dma_alloc_list) {
+               struct dma_alloc_record *this_r = list_entry(this, struct dma_alloc_record, list);
+               end = this_r->ofs;
+
+               if (end - start >= size)
+                       goto gotone;
+
+               start = this_r->ofs + this_r->len;
+       }
+       /* Reached end of list. */
+       end = DMA_SRAM_END;
+       this = &dma_alloc_list;
+
+       if (end - start >= size) {
+       gotone:
+               new->ofs = start;
+               list_add_tail(&new->list, this);
+               spin_unlock_irqrestore(&dma_alloc_lock, flags);
+
+               *dma_handle = start;
+               return (void *)start;
+       }
+
+       kfree(new);
+       spin_unlock_irqrestore(&dma_alloc_lock, flags);
+       return NULL;
+}
+
+void dma_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle)
+{
+       struct dma_alloc_record *rec;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dma_alloc_lock, flags);
+
+       list_for_each_entry(rec, &dma_alloc_list, list) {
+               if (rec->ofs == dma_handle) {
+                       list_del(&rec->list);
+                       kfree(rec);
+                       spin_unlock_irqrestore(&dma_alloc_lock, flags);
+                       return;
+               }
+       }
+       spin_unlock_irqrestore(&dma_alloc_lock, flags);
+       BUG();
+}
+
+/*
+ * Map a single buffer of the indicated size for DMA in streaming mode.
+ * The 32-bit bus address to use is returned.
+ *
+ * Once the device is given the dma address, the device owns this memory
+ * until either pci_unmap_single or pci_dma_sync_single is performed.
+ */
+dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
+                         enum dma_data_direction direction)
+{
+       if (direction == DMA_NONE)
+                BUG();
+
+       frv_cache_wback_inv((unsigned long) ptr, (unsigned long) ptr + size);
+
+       return virt_to_bus(ptr);
+}
+
+/*
+ * Map a set of buffers described by scatterlist in streaming
+ * mode for DMA.  This is the scather-gather version of the
+ * above pci_map_single interface.  Here the scatter gather list
+ * elements are each tagged with the appropriate dma address
+ * and length.  They are obtained via sg_dma_{address,length}(SG).
+ *
+ * NOTE: An implementation may be able to use a smaller number of
+ *       DMA address/length pairs than there are SG table elements.
+ *       (for example via virtual mapping capabilities)
+ *       The routine returns the number of addr/length pairs actually
+ *       used, at most nents.
+ *
+ * Device ownership issues as mentioned above for pci_map_single are
+ * the same here.
+ */
+int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+              enum dma_data_direction direction)
+{
+       int i;
+
+       for (i=0; i<nents; i++)
+               frv_cache_wback_inv(sg_dma_address(&sg[i]),
+                                   sg_dma_address(&sg[i]) + sg_dma_len(&sg[i]));
+
+       if (direction == DMA_NONE)
+                BUG();
+
+       return nents;
+}
diff --git a/arch/frv/mb93090-mb00/pci-dma.c b/arch/frv/mb93090-mb00/pci-dma.c
new file mode 100644 (file)
index 0000000..27eb120
--- /dev/null
@@ -0,0 +1,105 @@
+/* pci-dma.c: Dynamic DMA mapping support for the FRV CPUs that have MMUs
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/highmem.h>
+#include <asm/io.h>
+
+void *dma_alloc_coherent(struct device *hwdev, size_t size, dma_addr_t *dma_handle, int gfp)
+{
+       void *ret;
+
+       ret = consistent_alloc(gfp, size, dma_handle);
+       if (ret)
+               memset(ret, 0, size);
+
+       return ret;
+}
+
+void dma_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle)
+{
+       consistent_free(vaddr);
+}
+
+/*
+ * Map a single buffer of the indicated size for DMA in streaming mode.
+ * The 32-bit bus address to use is returned.
+ *
+ * Once the device is given the dma address, the device owns this memory
+ * until either pci_unmap_single or pci_dma_sync_single is performed.
+ */
+dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
+                         enum dma_data_direction direction)
+{
+       if (direction == DMA_NONE)
+                BUG();
+
+       frv_cache_wback_inv((unsigned long) ptr, (unsigned long) ptr + size);
+
+       return virt_to_bus(ptr);
+}
+
+/*
+ * Map a set of buffers described by scatterlist in streaming
+ * mode for DMA.  This is the scather-gather version of the
+ * above pci_map_single interface.  Here the scatter gather list
+ * elements are each tagged with the appropriate dma address
+ * and length.  They are obtained via sg_dma_{address,length}(SG).
+ *
+ * NOTE: An implementation may be able to use a smaller number of
+ *       DMA address/length pairs than there are SG table elements.
+ *       (for example via virtual mapping capabilities)
+ *       The routine returns the number of addr/length pairs actually
+ *       used, at most nents.
+ *
+ * Device ownership issues as mentioned above for pci_map_single are
+ * the same here.
+ */
+int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+              enum dma_data_direction direction)
+{
+       unsigned long dampr2;
+       void *vaddr;
+       int i;
+
+       if (direction == DMA_NONE)
+                BUG();
+
+       dampr2 = __get_DAMPR(2);
+
+       for (i = 0; i < nents; i++) {
+               vaddr = kmap_atomic(sg[i].page, __KM_CACHE);
+
+               frv_dcache_writeback((unsigned long) vaddr,
+                                    (unsigned long) vaddr + PAGE_SIZE);
+
+       }
+
+       kunmap_atomic(vaddr, __KM_CACHE);
+       if (dampr2) {
+               __set_DAMPR(2, dampr2);
+               __set_IAMPR(2, dampr2);
+       }
+
+       return nents;
+}
+
+dma_addr_t dma_map_page(struct device *dev, struct page *page, unsigned long offset,
+                       size_t size, enum dma_data_direction direction)
+{
+       BUG_ON(direction == DMA_NONE);
+       flush_dcache_page(page);
+       return (dma_addr_t) page_to_phys(page) + offset;
+}
diff --git a/arch/frv/mb93090-mb00/pci-frv.c b/arch/frv/mb93090-mb00/pci-frv.c
new file mode 100644 (file)
index 0000000..263a7dd
--- /dev/null
@@ -0,0 +1,288 @@
+/* pci-frv.c: low-level PCI access routines
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from the i386 equivalent stuff
+ *
+ * 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 <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+
+#include "pci-frv.h"
+
+#if 0
+void
+pcibios_update_resource(struct pci_dev *dev, struct resource *root,
+                       struct resource *res, int resource)
+{
+       u32 new, check;
+       int reg;
+
+       new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
+       if (resource < 6) {
+               reg = PCI_BASE_ADDRESS_0 + 4*resource;
+       } else if (resource == PCI_ROM_RESOURCE) {
+               res->flags |= PCI_ROM_ADDRESS_ENABLE;
+               new |= PCI_ROM_ADDRESS_ENABLE;
+               reg = dev->rom_base_reg;
+       } else {
+               /* Somebody might have asked allocation of a non-standard resource */
+               return;
+       }
+
+       pci_write_config_dword(dev, reg, new);
+       pci_read_config_dword(dev, reg, &check);
+       if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) {
+               printk(KERN_ERR "PCI: Error while updating region "
+                      "%s/%d (%08x != %08x)\n", dev->slot_name, resource,
+                      new, check);
+       }
+}
+#endif
+
+/*
+ * We need to avoid collisions with `mirrored' VGA ports
+ * and other strange ISA hardware, so we always want the
+ * addresses to be allocated in the 0x000-0x0ff region
+ * modulo 0x400.
+ *
+ * Why? Because some silly external IO cards only decode
+ * the low 10 bits of the IO address. The 0x00-0xff region
+ * is reserved for motherboard devices that decode all 16
+ * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
+ * but we want to try to avoid allocating at 0x2900-0x2bff
+ * which might have be mirrored at 0x0100-0x03ff..
+ */
+void
+pcibios_align_resource(void *data, struct resource *res,
+                      unsigned long size, unsigned long align)
+{
+       if (res->flags & IORESOURCE_IO) {
+               unsigned long start = res->start;
+
+               if (start & 0x300) {
+                       start = (start + 0x3ff) & ~0x3ff;
+                       res->start = start;
+               }
+       }
+}
+
+
+/*
+ *  Handle resources of PCI devices.  If the world were perfect, we could
+ *  just allocate all the resource regions and do nothing more.  It isn't.
+ *  On the other hand, we cannot just re-allocate all devices, as it would
+ *  require us to know lots of host bridge internals.  So we attempt to
+ *  keep as much of the original configuration as possible, but tweak it
+ *  when it's found to be wrong.
+ *
+ *  Known BIOS problems we have to work around:
+ *     - I/O or memory regions not configured
+ *     - regions configured, but not enabled in the command register
+ *     - bogus I/O addresses above 64K used
+ *     - expansion ROMs left enabled (this may sound harmless, but given
+ *       the fact the PCI specs explicitly allow address decoders to be
+ *       shared between expansion ROMs and other resource regions, it's
+ *       at least dangerous)
+ *
+ *  Our solution:
+ *     (1) Allocate resources for all buses behind PCI-to-PCI bridges.
+ *         This gives us fixed barriers on where we can allocate.
+ *     (2) Allocate resources for all enabled devices.  If there is
+ *         a collision, just mark the resource as unallocated. Also
+ *         disable expansion ROMs during this step.
+ *     (3) Try to allocate resources for disabled devices.  If the
+ *         resources were assigned correctly, everything goes well,
+ *         if they weren't, they won't disturb allocation of other
+ *         resources.
+ *     (4) Assign new addresses to resources which were either
+ *         not configured at all or misconfigured.  If explicitly
+ *         requested by the user, configure expansion ROM address
+ *         as well.
+ */
+
+static void __init pcibios_allocate_bus_resources(struct list_head *bus_list)
+{
+       struct list_head *ln;
+       struct pci_bus *bus;
+       struct pci_dev *dev;
+       int idx;
+       struct resource *r, *pr;
+
+       /* Depth-First Search on bus tree */
+       for (ln=bus_list->next; ln != bus_list; ln=ln->next) {
+               bus = pci_bus_b(ln);
+               if ((dev = bus->self)) {
+                       for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) {
+                               r = &dev->resource[idx];
+                               if (!r->start)
+                                       continue;
+                               pr = pci_find_parent_resource(dev, r);
+                               if (!pr || request_resource(pr, r) < 0)
+                                       printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, dev->slot_name);
+                       }
+               }
+               pcibios_allocate_bus_resources(&bus->children);
+       }
+}
+
+static void __init pcibios_allocate_resources(int pass)
+{
+       struct pci_dev *dev = NULL;
+       int idx, disabled;
+       u16 command;
+       struct resource *r, *pr;
+
+       while (dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev),
+              dev != NULL
+              ) {
+               pci_read_config_word(dev, PCI_COMMAND, &command);
+               for(idx = 0; idx < 6; idx++) {
+                       r = &dev->resource[idx];
+                       if (r->parent)          /* Already allocated */
+                               continue;
+                       if (!r->start)          /* Address not assigned at all */
+                               continue;
+                       if (r->flags & IORESOURCE_IO)
+                               disabled = !(command & PCI_COMMAND_IO);
+                       else
+                               disabled = !(command & PCI_COMMAND_MEMORY);
+                       if (pass == disabled) {
+                               DBG("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n",
+                                   r->start, r->end, r->flags, disabled, pass);
+                               pr = pci_find_parent_resource(dev, r);
+                               if (!pr || request_resource(pr, r) < 0) {
+                                       printk(KERN_ERR "PCI: Cannot allocate resource region %d of device %s\n", idx, pci_name(dev));
+                                       /* We'll assign a new address later */
+                                       r->end -= r->start;
+                                       r->start = 0;
+                               }
+                       }
+               }
+               if (!pass) {
+                       r = &dev->resource[PCI_ROM_RESOURCE];
+                       if (r->flags & PCI_ROM_ADDRESS_ENABLE) {
+                               /* Turn the ROM off, leave the resource region, but keep it unregistered. */
+                               u32 reg;
+                               DBG("PCI: Switching off ROM of %s\n", pci_name(dev));
+                               r->flags &= ~PCI_ROM_ADDRESS_ENABLE;
+                               pci_read_config_dword(dev, dev->rom_base_reg, &reg);
+                               pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE);
+                       }
+               }
+       }
+}
+
+static void __init pcibios_assign_resources(void)
+{
+       struct pci_dev *dev = NULL;
+       int idx;
+       struct resource *r;
+
+       while (dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev),
+              dev != NULL
+              ) {
+               int class = dev->class >> 8;
+
+               /* Don't touch classless devices and host bridges */
+               if (!class || class == PCI_CLASS_BRIDGE_HOST)
+                       continue;
+
+               for(idx=0; idx<6; idx++) {
+                       r = &dev->resource[idx];
+
+                       /*
+                        *  Don't touch IDE controllers and I/O ports of video cards!
+                        */
+                       if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) ||
+                           (class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO)))
+                               continue;
+
+                       /*
+                        *  We shall assign a new address to this resource, either because
+                        *  the BIOS forgot to do so or because we have decided the old
+                        *  address was unusable for some reason.
+                        */
+                       if (!r->start && r->end)
+                               pci_assign_resource(dev, idx);
+               }
+
+               if (pci_probe & PCI_ASSIGN_ROMS) {
+                       r = &dev->resource[PCI_ROM_RESOURCE];
+                       r->end -= r->start;
+                       r->start = 0;
+                       if (r->end)
+                               pci_assign_resource(dev, PCI_ROM_RESOURCE);
+               }
+       }
+}
+
+void __init pcibios_resource_survey(void)
+{
+       DBG("PCI: Allocating resources\n");
+       pcibios_allocate_bus_resources(&pci_root_buses);
+       pcibios_allocate_resources(0);
+       pcibios_allocate_resources(1);
+       pcibios_assign_resources();
+}
+
+int pcibios_enable_resources(struct pci_dev *dev, int mask)
+{
+       u16 cmd, old_cmd;
+       int idx;
+       struct resource *r;
+
+       pci_read_config_word(dev, PCI_COMMAND, &cmd);
+       old_cmd = cmd;
+       for(idx=0; idx<6; idx++) {
+               /* Only set up the requested stuff */
+               if (!(mask & (1<<idx)))
+                       continue;
+
+               r = &dev->resource[idx];
+               if (!r->start && r->end) {
+                       printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev));
+                       return -EINVAL;
+               }
+               if (r->flags & IORESOURCE_IO)
+                       cmd |= PCI_COMMAND_IO;
+               if (r->flags & IORESOURCE_MEM)
+                       cmd |= PCI_COMMAND_MEMORY;
+       }
+       if (dev->resource[PCI_ROM_RESOURCE].start)
+               cmd |= PCI_COMMAND_MEMORY;
+       if (cmd != old_cmd) {
+               printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd);
+               pci_write_config_word(dev, PCI_COMMAND, cmd);
+       }
+       return 0;
+}
+
+/*
+ *  If we set up a device for bus mastering, we need to check the latency
+ *  timer as certain crappy BIOSes forget to set it properly.
+ */
+unsigned int pcibios_max_latency = 255;
+
+void pcibios_set_master(struct pci_dev *dev)
+{
+       u8 lat;
+       pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+       if (lat < 16)
+               lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
+       else if (lat > pcibios_max_latency)
+               lat = pcibios_max_latency;
+       else
+               return;
+       printk(KERN_DEBUG "PCI: Setting latency timer of device %s to %d\n", pci_name(dev), lat);
+       pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
+}
diff --git a/arch/frv/mb93090-mb00/pci-frv.h b/arch/frv/mb93090-mb00/pci-frv.h
new file mode 100644 (file)
index 0000000..7481797
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *     Low-Level PCI Access for FRV machines.
+ *
+ *     (c) 1999 Martin Mares <mj@ucw.cz>
+ */
+
+#include <asm/sections.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+#define PCI_PROBE_BIOS         0x0001
+#define PCI_PROBE_CONF1                0x0002
+#define PCI_PROBE_CONF2                0x0004
+#define PCI_NO_SORT            0x0100
+#define PCI_BIOS_SORT          0x0200
+#define PCI_NO_CHECKS          0x0400
+#define PCI_ASSIGN_ROMS                0x1000
+#define PCI_BIOS_IRQ_SCAN      0x2000
+#define PCI_ASSIGN_ALL_BUSSES  0x4000
+
+extern unsigned int __nongpreldata pci_probe;
+
+/* pci-frv.c */
+
+extern unsigned int pcibios_max_latency;
+
+void pcibios_resource_survey(void);
+int pcibios_enable_resources(struct pci_dev *, int);
+
+/* pci-vdk.c */
+
+extern int __nongpreldata pcibios_last_bus;
+extern struct pci_bus *__nongpreldata pci_root_bus;
+extern struct pci_ops *__nongpreldata pci_root_ops;
+
+/* pci-irq.c */
+extern unsigned int pcibios_irq_mask;
+
+void pcibios_irq_init(void);
+void pcibios_fixup_irqs(void);
+void pcibios_enable_irq(struct pci_dev *dev);
diff --git a/arch/frv/mb93090-mb00/pci-irq.c b/arch/frv/mb93090-mb00/pci-irq.c
new file mode 100644 (file)
index 0000000..24622d8
--- /dev/null
@@ -0,0 +1,70 @@
+/* pci-irq.c: PCI IRQ routing on the FRV motherboard
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * derived from: arch/i386/kernel/pci-irq.c: (c) 1999--2000 Martin Mares <mj@suse.cz>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/irq-routing.h>
+
+#include "pci-frv.h"
+
+/*
+ *     DEVICE  DEVNO   INT#A   INT#B   INT#C   INT#D
+ *     ======= ======= ======= ======= ======= =======
+ *     MB86943 0       fpga.10 -       -       -
+ *     RTL8029 16      fpga.12 -       -       -
+ *     SLOT 1  19      fpga.6  fpga.5  fpga.4  fpga.3
+ *     SLOT 2  18      fpga.5  fpga.4  fpga.3  fpga.6
+ *     SLOT 3  17      fpga.4  fpga.3  fpga.6  fpga.5
+ *
+ */
+
+static const uint8_t __initdata pci_bus0_irq_routing[32][4] = {
+       [0 ] {  IRQ_FPGA_MB86943_PCI_INTA },
+       [16] {  IRQ_FPGA_RTL8029_INTA },
+       [17] {  IRQ_FPGA_PCI_INTC, IRQ_FPGA_PCI_INTD, IRQ_FPGA_PCI_INTA, IRQ_FPGA_PCI_INTB },
+       [18] {  IRQ_FPGA_PCI_INTB, IRQ_FPGA_PCI_INTC, IRQ_FPGA_PCI_INTD, IRQ_FPGA_PCI_INTA },
+       [19] {  IRQ_FPGA_PCI_INTA, IRQ_FPGA_PCI_INTB, IRQ_FPGA_PCI_INTC, IRQ_FPGA_PCI_INTD },
+};
+
+void __init pcibios_irq_init(void)
+{
+}
+
+void __init pcibios_fixup_irqs(void)
+{
+       struct pci_dev *dev = NULL;
+       uint8_t line, pin;
+
+       while (dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev),
+              dev != NULL
+              ) {
+               pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+               if (pin) {
+                       dev->irq = pci_bus0_irq_routing[PCI_SLOT(dev->devfn)][pin - 1];
+                       pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+               }
+               pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line);
+       }
+}
+
+void __init pcibios_penalize_isa_irq(int irq)
+{
+}
+
+void pcibios_enable_irq(struct pci_dev *dev)
+{
+       pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+}
diff --git a/arch/frv/mb93090-mb00/pci-vdk.c b/arch/frv/mb93090-mb00/pci-vdk.c
new file mode 100644 (file)
index 0000000..11b5c06
--- /dev/null
@@ -0,0 +1,467 @@
+/* pci-vdk.c: MB93090-MB00 (VDK) PCI support
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/mb-regs.h>
+#include <asm/mb86943a.h>
+#include "pci-frv.h"
+
+unsigned int __nongpreldata pci_probe = 1;
+
+int  __nongpreldata pcibios_last_bus = -1;
+struct pci_bus *__nongpreldata pci_root_bus;
+struct pci_ops *__nongpreldata pci_root_ops;
+
+/*
+ * Functions for accessing PCI configuration space
+ */
+
+#define CONFIG_CMD(bus, dev, where) \
+       (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
+
+#define __set_PciCfgAddr(A) writel((A), (volatile void __iomem *) __region_CS1 + 0x80)
+
+#define __get_PciCfgDataB(A) readb((volatile void __iomem *) __region_CS1 + 0x88 + ((A) & 3))
+#define __get_PciCfgDataW(A) readw((volatile void __iomem *) __region_CS1 + 0x88 + ((A) & 2))
+#define __get_PciCfgDataL(A) readl((volatile void __iomem *) __region_CS1 + 0x88)
+
+#define __set_PciCfgDataB(A,V) \
+       writeb((V), (volatile void __iomem *) __region_CS1 + 0x88 + (3 - ((A) & 3)))
+
+#define __set_PciCfgDataW(A,V) \
+       writew((V), (volatile void __iomem *) __region_CS1 + 0x88 + (2 - ((A) & 2)))
+
+#define __set_PciCfgDataL(A,V) \
+       writel((V), (volatile void __iomem *) __region_CS1 + 0x88)
+
+#define __get_PciBridgeDataB(A) readb((volatile void __iomem *) __region_CS1 + 0x800 + (A))
+#define __get_PciBridgeDataW(A) readw((volatile void __iomem *) __region_CS1 + 0x800 + (A))
+#define __get_PciBridgeDataL(A) readl((volatile void __iomem *) __region_CS1 + 0x800 + (A))
+
+#define __set_PciBridgeDataB(A,V) writeb((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A))
+#define __set_PciBridgeDataW(A,V) writew((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A))
+#define __set_PciBridgeDataL(A,V) writel((V), (volatile void __iomem *) __region_CS1 + 0x800 + (A))
+
+static inline int __query(const struct pci_dev *dev)
+{
+//     return dev->bus->number==0 && (dev->devfn==PCI_DEVFN(0,0));
+//     return dev->bus->number==1;
+//     return dev->bus->number==0 &&
+//             (dev->devfn==PCI_DEVFN(2,0) || dev->devfn==PCI_DEVFN(3,0));
+       return 0;
+}
+
+/*****************************************************************************/
+/*
+ *
+ */
+static int pci_frv_read_config(struct pci_bus *bus, unsigned int devfn, int where, int size,
+                              u32 *val)
+{
+       u32 _value;
+
+       if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) {
+               _value = __get_PciBridgeDataL(where & ~3);
+       }
+       else {
+               __set_PciCfgAddr(CONFIG_CMD(bus, devfn, where));
+               _value = __get_PciCfgDataL(where & ~3);
+       }
+
+       switch (size) {
+       case 1:
+               _value = _value >> ((where & 3) * 8);
+               break;
+
+       case 2:
+               _value = _value >> ((where & 2) * 8);
+               break;
+
+       case 4:
+               break;
+
+       default:
+               BUG();
+       }
+
+       *val = _value;
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_frv_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size,
+                               u32 value)
+{
+       switch (size) {
+       case 1:
+               if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) {
+                       __set_PciBridgeDataB(where, value);
+               }
+               else {
+                       __set_PciCfgAddr(CONFIG_CMD(bus, devfn, where));
+                       __set_PciCfgDataB(where, value);
+               }
+               break;
+
+       case 2:
+               if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) {
+                       __set_PciBridgeDataW(where, value);
+               }
+               else {
+                       __set_PciCfgAddr(CONFIG_CMD(bus, devfn, where));
+                       __set_PciCfgDataW(where, value);
+               }
+               break;
+
+       case 4:
+               if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) {
+                       __set_PciBridgeDataL(where, value);
+               }
+               else {
+                       __set_PciCfgAddr(CONFIG_CMD(bus, devfn, where));
+                       __set_PciCfgDataL(where, value);
+               }
+               break;
+
+       default:
+               BUG();
+       }
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops pci_direct_frv = {
+       pci_frv_read_config,
+       pci_frv_write_config,
+};
+
+/*
+ * Before we decide to use direct hardware access mechanisms, we try to do some
+ * trivial checks to ensure it at least _seems_ to be working -- we just test
+ * whether bus 00 contains a host bridge (this is similar to checking
+ * techniques used in XFree86, but ours should be more reliable since we
+ * attempt to make use of direct access hints provided by the PCI BIOS).
+ *
+ * This should be close to trivial, but it isn't, because there are buggy
+ * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
+ */
+static int __init pci_sanity_check(struct pci_ops *o)
+{
+       struct pci_bus bus;             /* Fake bus and device */
+       u32 id;
+
+       bus.number      = 0;
+
+       if (o->read(&bus, 0, PCI_VENDOR_ID, 4, &id) == PCIBIOS_SUCCESSFUL) {
+               printk("PCI: VDK Bridge device:vendor: %08x\n", id);
+               if (id == 0x200e10cf)
+                       return 1;
+       }
+
+       printk("PCI: VDK Bridge: Sanity check failed\n");
+       return 0;
+}
+
+static struct pci_ops * __init pci_check_direct(void)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       /* check if access works */
+       if (pci_sanity_check(&pci_direct_frv)) {
+               local_irq_restore(flags);
+               printk("PCI: Using configuration frv\n");
+//             request_mem_region(0xBE040000, 256, "FRV bridge");
+//             request_mem_region(0xBFFFFFF4, 12, "PCI frv");
+               return &pci_direct_frv;
+       }
+
+       local_irq_restore(flags);
+       return NULL;
+}
+
+/*
+ * Several buggy motherboards address only 16 devices and mirror
+ * them to next 16 IDs. We try to detect this `feature' on all
+ * primary buses (those containing host bridges as they are
+ * expected to be unique) and remove the ghost devices.
+ */
+
+static void __init pcibios_fixup_ghosts(struct pci_bus *b)
+{
+       struct list_head *ln, *mn;
+       struct pci_dev *d, *e;
+       int mirror = PCI_DEVFN(16,0);
+       int seen_host_bridge = 0;
+       int i;
+
+       for (ln=b->devices.next; ln != &b->devices; ln=ln->next) {
+               d = pci_dev_b(ln);
+               if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST)
+                       seen_host_bridge++;
+               for (mn=ln->next; mn != &b->devices; mn=mn->next) {
+                       e = pci_dev_b(mn);
+                       if (e->devfn != d->devfn + mirror ||
+                           e->vendor != d->vendor ||
+                           e->device != d->device ||
+                           e->class != d->class)
+                               continue;
+                       for(i=0; i<PCI_NUM_RESOURCES; i++)
+                               if (e->resource[i].start != d->resource[i].start ||
+                                   e->resource[i].end != d->resource[i].end ||
+                                   e->resource[i].flags != d->resource[i].flags)
+                                       continue;
+                       break;
+               }
+               if (mn == &b->devices)
+                       return;
+       }
+       if (!seen_host_bridge)
+               return;
+       printk("PCI: Ignoring ghost devices on bus %02x\n", b->number);
+
+       ln = &b->devices;
+       while (ln->next != &b->devices) {
+               d = pci_dev_b(ln->next);
+               if (d->devfn >= mirror) {
+                       list_del(&d->global_list);
+                       list_del(&d->bus_list);
+                       kfree(d);
+               } else
+                       ln = ln->next;
+       }
+}
+
+/*
+ * Discover remaining PCI buses in case there are peer host bridges.
+ * We use the number of last PCI bus provided by the PCI BIOS.
+ */
+static void __init pcibios_fixup_peer_bridges(void)
+{
+       struct pci_bus bus;
+       struct pci_dev dev;
+       int n;
+       u16 l;
+
+       if (pcibios_last_bus <= 0 || pcibios_last_bus >= 0xff)
+               return;
+       printk("PCI: Peer bridge fixup\n");
+       for (n=0; n <= pcibios_last_bus; n++) {
+               if (pci_find_bus(0, n))
+                       continue;
+               bus.number = n;
+               bus.ops = pci_root_ops;
+               dev.bus = &bus;
+               for(dev.devfn=0; dev.devfn<256; dev.devfn += 8)
+                       if (!pci_read_config_word(&dev, PCI_VENDOR_ID, &l) &&
+                           l != 0x0000 && l != 0xffff) {
+                               printk("Found device at %02x:%02x [%04x]\n", n, dev.devfn, l);
+                               printk("PCI: Discovered peer bus %02x\n", n);
+                               pci_scan_bus(n, pci_root_ops, NULL);
+                               break;
+                       }
+       }
+}
+
+/*
+ * Exceptions for specific devices. Usually work-arounds for fatal design flaws.
+ */
+
+static void __init pci_fixup_umc_ide(struct pci_dev *d)
+{
+       /*
+        * UM8886BF IDE controller sets region type bits incorrectly,
+        * therefore they look like memory despite of them being I/O.
+        */
+       int i;
+
+       printk("PCI: Fixing base address flags for device %s\n", d->slot_name);
+       for(i=0; i<4; i++)
+               d->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO;
+}
+
+static void __init pci_fixup_ide_bases(struct pci_dev *d)
+{
+       int i;
+
+       /*
+        * PCI IDE controllers use non-standard I/O port decoding, respect it.
+        */
+       if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE)
+               return;
+       printk("PCI: IDE base address fixup for %s\n", d->slot_name);
+       for(i=0; i<4; i++) {
+               struct resource *r = &d->resource[i];
+               if ((r->start & ~0x80) == 0x374) {
+                       r->start |= 2;
+                       r->end = r->start;
+               }
+       }
+}
+
+static void __init pci_fixup_ide_trash(struct pci_dev *d)
+{
+       int i;
+
+       /*
+        * There exist PCI IDE controllers which have utter garbage
+        * in first four base registers. Ignore that.
+        */
+       printk("PCI: IDE base address trash cleared for %s\n", d->slot_name);
+       for(i=0; i<4; i++)
+               d->resource[i].start = d->resource[i].end = d->resource[i].flags = 0;
+}
+
+static void __devinit  pci_fixup_latency(struct pci_dev *d)
+{
+       /*
+        *  SiS 5597 and 5598 chipsets require latency timer set to
+        *  at most 32 to avoid lockups.
+        */
+       DBG("PCI: Setting max latency to 32\n");
+       pcibios_max_latency = 32;
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, pci_fixup_umc_ide);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513, pci_fixup_ide_trash);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, pci_fixup_latency);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency);
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases);
+
+/*
+ *  Called after each bus is probed, but before its children
+ *  are examined.
+ */
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+#if 0
+       printk("### PCIBIOS_FIXUP_BUS(%d)\n",bus->number);
+#endif
+       pcibios_fixup_ghosts(bus);
+       pci_read_bridge_bases(bus);
+
+       if (bus->number == 0) {
+               struct list_head *ln;
+               struct pci_dev *dev;
+               for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
+                       dev = pci_dev_b(ln);
+                       if (dev->devfn == 0) {
+                               dev->resource[0].start = 0;
+                               dev->resource[0].end = 0;
+                       }
+               }
+       }
+}
+
+/*
+ * Initialization. Try all known PCI access methods. Note that we support
+ * using both PCI BIOS and direct access: in such cases, we use I/O ports
+ * to access config space, but we still keep BIOS order of cards to be
+ * compatible with 2.0.X. This should go away some day.
+ */
+
+int __init pcibios_init(void)
+{
+       struct pci_ops *dir = NULL;
+
+       if (!mb93090_mb00_detected)
+               return -ENXIO;
+
+       __reg_MB86943_sl_ctl |= MB86943_SL_CTL_DRCT_MASTER_SWAP | MB86943_SL_CTL_DRCT_SLAVE_SWAP;
+
+       __reg_MB86943_ecs_base(1)       = ((__region_CS2 + 0x01000000) >> 9) | 0x08000000;
+       __reg_MB86943_ecs_base(2)       = ((__region_CS2 + 0x00000000) >> 9) | 0x08000000;
+
+       *(volatile uint32_t *) (__region_CS1 + 0x848) = 0xe0000000;
+       *(volatile uint32_t *) (__region_CS1 + 0x8b8) = 0x00000000;
+
+       __reg_MB86943_sl_pci_io_base    = (__region_CS2 + 0x04000000) >> 9;
+       __reg_MB86943_sl_pci_mem_base   = (__region_CS2 + 0x08000000) >> 9;
+       __reg_MB86943_pci_sl_io_base    = __region_CS2 + 0x04000000;
+       __reg_MB86943_pci_sl_mem_base   = __region_CS2 + 0x08000000;
+       mb();
+
+       *(volatile unsigned long *)(__region_CS2+0x01300014) == 1;
+
+       ioport_resource.start   = (__reg_MB86943_sl_pci_io_base << 9) & 0xfffffc00;
+       ioport_resource.end     = (__reg_MB86943_sl_pci_io_range << 9) | 0x3ff;
+       ioport_resource.end     += ioport_resource.start;
+
+       printk("PCI IO window:  %08lx-%08lx\n", ioport_resource.start, ioport_resource.end);
+
+       iomem_resource.start    = (__reg_MB86943_sl_pci_mem_base << 9) & 0xfffffc00;
+
+       /* Reserve somewhere to write to flush posted writes. */
+       iomem_resource.start += 0x400;
+
+       iomem_resource.end      = (__reg_MB86943_sl_pci_mem_range << 9) | 0x3ff;
+       iomem_resource.end      += iomem_resource.start;
+
+       printk("PCI MEM window: %08lx-%08lx\n", iomem_resource.start, iomem_resource.end);
+       printk("PCI DMA memory: %08lx-%08lx\n", dma_coherent_mem_start, dma_coherent_mem_end);
+
+       if (!pci_probe)
+               return -ENXIO;
+
+       dir = pci_check_direct();
+       if (dir)
+               pci_root_ops = dir;
+       else {
+               printk("PCI: No PCI bus detected\n");
+               return -ENXIO;
+       }
+
+       printk("PCI: Probing PCI hardware\n");
+       pci_root_bus = pci_scan_bus(0, pci_root_ops, NULL);
+
+       pcibios_irq_init();
+       pcibios_fixup_peer_bridges();
+       pcibios_fixup_irqs();
+       pcibios_resource_survey();
+
+       return 0;
+}
+
+arch_initcall(pcibios_init);
+
+char * __init pcibios_setup(char *str)
+{
+       if (!strcmp(str, "off")) {
+               pci_probe = 0;
+               return NULL;
+       } else if (!strncmp(str, "lastbus=", 8)) {
+               pcibios_last_bus = simple_strtol(str+8, NULL, 0);
+               return NULL;
+       }
+       return str;
+}
+
+int pcibios_enable_device(struct pci_dev *dev, int mask)
+{
+       int err;
+
+       if ((err = pcibios_enable_resources(dev, mask)) < 0)
+               return err;
+       pcibios_enable_irq(dev);
+       return 0;
+}
diff --git a/arch/frv/mm/Makefile b/arch/frv/mm/Makefile
new file mode 100644 (file)
index 0000000..fb8b1d8
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for the arch-specific parts of the memory manager.
+#
+
+obj-y := init.o kmap.o
+
+obj-$(CONFIG_MMU) += \
+       pgalloc.o highmem.o fault.o extable.o cache-page.o tlb-flush.o tlb-miss.o \
+       mmu-context.o dma-alloc.o unaligned.o elf-fdpic.o
diff --git a/arch/frv/mm/cache-page.c b/arch/frv/mm/cache-page.c
new file mode 100644 (file)
index 0000000..683b5e3
--- /dev/null
@@ -0,0 +1,66 @@
+/* cache-page.c: whole-page cache wrangling functions for MMU linux
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <asm/pgalloc.h>
+
+/*****************************************************************************/
+/*
+ * DCF takes a virtual address and the page may not currently have one
+ * - temporarily hijack a kmap_atomic() slot and attach the page to it
+ */
+void flush_dcache_page(struct page *page)
+{
+       unsigned long dampr2;
+       void *vaddr;
+
+       dampr2 = __get_DAMPR(2);
+
+       vaddr = kmap_atomic(page, __KM_CACHE);
+
+       frv_dcache_writeback((unsigned long) vaddr, (unsigned long) vaddr + PAGE_SIZE);
+
+       kunmap_atomic(vaddr, __KM_CACHE);
+
+       if (dampr2) {
+               __set_DAMPR(2, dampr2);
+               __set_IAMPR(2, dampr2);
+       }
+
+} /* end flush_dcache_page() */
+
+/*****************************************************************************/
+/*
+ * ICI takes a virtual address and the page may not currently have one
+ * - so we temporarily attach the page to a bit of virtual space so that is can be flushed
+ */
+void flush_icache_user_range(struct vm_area_struct *vma, struct page *page,
+                            unsigned long start, unsigned long len)
+{
+       unsigned long dampr2;
+       void *vaddr;
+
+       dampr2 = __get_DAMPR(2);
+
+       vaddr = kmap_atomic(page, __KM_CACHE);
+
+       start = (start & ~PAGE_MASK) | (unsigned long) vaddr;
+       frv_cache_wback_inv(start, start + len);
+
+       kunmap_atomic(vaddr, __KM_CACHE);
+
+       if (dampr2) {
+               __set_DAMPR(2, dampr2);
+               __set_IAMPR(2, dampr2);
+       }
+
+} /* end flush_icache_user_range() */
diff --git a/arch/frv/mm/dma-alloc.c b/arch/frv/mm/dma-alloc.c
new file mode 100644 (file)
index 0000000..4b38d45
--- /dev/null
@@ -0,0 +1,188 @@
+/* dma-alloc.c: consistent DMA memory allocation
+ *
+ * Derived from arch/ppc/mm/cachemap.c
+ *
+ *  PowerPC version derived from arch/arm/mm/consistent.c
+ *    Copyright (C) 2001 Dan Malek (dmalek@jlc.net)
+ *
+ *  linux/arch/arm/mm/consistent.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
+ * Modified for 36-bit support.  -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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/stddef.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/pgalloc.h>
+#include <asm/io.h>
+#include <asm/hardirq.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/mmu.h>
+#include <asm/uaccess.h>
+#include <asm/smp.h>
+
+static int map_page(unsigned long va, unsigned long pa, pgprot_t prot)
+{
+       pgd_t *pge;
+       pud_t *pue;
+       pmd_t *pme;
+       pte_t *pte;
+       int err = -ENOMEM;
+
+       spin_lock(&init_mm.page_table_lock);
+
+       /* Use upper 10 bits of VA to index the first level map */
+       pge = pgd_offset_k(va);
+       pue = pud_offset(pge, va);
+       pme = pmd_offset(pue, va);
+
+       /* Use middle 10 bits of VA to index the second-level map */
+       pte = pte_alloc_kernel(&init_mm, pme, va);
+       if (pte != 0) {
+               err = 0;
+               set_pte(pte, mk_pte_phys(pa & PAGE_MASK, prot));
+       }
+
+       spin_unlock(&init_mm.page_table_lock);
+       return err;
+}
+
+/*
+ * 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)
+{
+       struct vm_struct *area;
+       unsigned long page, va, pa;
+       void *ret;
+       int order, err, i;
+
+       if (in_interrupt())
+               BUG();
+
+       /* only allocate page size areas */
+       size = PAGE_ALIGN(size);
+       order = get_order(size);
+
+       page = __get_free_pages(gfp, order);
+       if (!page) {
+               BUG();
+               return NULL;
+       }
+
+       /* allocate some common virtual space to map the new pages */
+       area = get_vm_area(size, VM_ALLOC);
+       if (area == 0) {
+               free_pages(page, order);
+               return NULL;
+       }
+       va = VMALLOC_VMADDR(area->addr);
+       ret = (void *) va;
+
+       /* this gives us the real physical address of the first page */
+       *dma_handle = pa = virt_to_bus((void *) page);
+
+       /* set refcount=1 on all pages in an order>0 allocation so that vfree() will actually free
+        * all pages that were allocated.
+        */
+       if (order > 0) {
+               struct page *rpage = virt_to_page(page);
+
+               for (i = 1; i < (1 << order); i++)
+                       set_page_count(rpage + i, 1);
+       }
+
+       err = 0;
+       for (i = 0; i < size && err == 0; i += PAGE_SIZE)
+               err = map_page(va + i, pa + i, PAGE_KERNEL_NOCACHE);
+
+       if (err) {
+               vfree((void *) va);
+               return NULL;
+       }
+
+       /* we need to ensure that there are no cachelines in use, or worse dirty in this area
+        * - can't do until after virtual address mappings are created
+        */
+       frv_cache_invalidate(va, va + size);
+
+       return ret;
+}
+
+/*
+ * free page(s) as defined by the above mapping.
+ */
+void consistent_free(void *vaddr)
+{
+       if (in_interrupt())
+               BUG();
+       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 PCI_DMA_NONE:
+               BUG();
+       case PCI_DMA_FROMDEVICE:        /* invalidate only */
+               frv_cache_invalidate(start, end);
+               break;
+       case PCI_DMA_TODEVICE:          /* writeback only */
+               frv_dcache_writeback(start, end);
+               break;
+       case PCI_DMA_BIDIRECTIONAL:     /* writeback and invalidate */
+               frv_dcache_writeback(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)
+{
+       void *start;
+
+       start = page_address(page) + offset;
+       consistent_sync(start, size, direction);
+}
diff --git a/arch/frv/mm/elf-fdpic.c b/arch/frv/mm/elf-fdpic.c
new file mode 100644 (file)
index 0000000..f5a6530
--- /dev/null
@@ -0,0 +1,123 @@
+/* elf-fdpic.c: ELF FDPIC memory layout management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/elf-fdpic.h>
+
+/*****************************************************************************/
+/*
+ * lay out the userspace VM according to our grand design
+ */
+#ifdef CONFIG_MMU
+void elf_fdpic_arch_lay_out_mm(struct elf_fdpic_params *exec_params,
+                              struct elf_fdpic_params *interp_params,
+                              unsigned long *start_stack,
+                              unsigned long *start_brk)
+{
+       *start_stack = 0x02200000UL;
+
+       /* if the only executable is a shared object, assume that it is an interpreter rather than
+        * a true executable, and map it such that "ld.so --list" comes out right
+        */
+       if (!(interp_params->flags & ELF_FDPIC_FLAG_PRESENT) &&
+           exec_params->hdr.e_type != ET_EXEC
+           ) {
+               exec_params->load_addr = PAGE_SIZE;
+
+               *start_brk = 0x80000000UL;
+       }
+       else {
+               exec_params->load_addr = 0x02200000UL;
+
+               if ((exec_params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) ==
+                   ELF_FDPIC_FLAG_INDEPENDENT
+                   ) {
+                       exec_params->flags &= ~ELF_FDPIC_FLAG_ARRANGEMENT;
+                       exec_params->flags |= ELF_FDPIC_FLAG_CONSTDISP;
+               }
+       }
+
+} /* end elf_fdpic_arch_lay_out_mm() */
+#endif
+
+/*****************************************************************************/
+/*
+ * place non-fixed mmaps firstly in the bottom part of memory, working up, and then in the top part
+ * of memory, working down
+ */
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len,
+                                    unsigned long pgoff, unsigned long flags)
+{
+       struct vm_area_struct *vma;
+       unsigned long limit;
+
+       if (len > TASK_SIZE)
+               return -ENOMEM;
+
+       /* only honour a hint if we're not going to clobber something doing so */
+       if (addr) {
+               addr = PAGE_ALIGN(addr);
+               vma = find_vma(current->mm, addr);
+               if (TASK_SIZE - len >= addr &&
+                   (!vma || addr + len <= vma->vm_start))
+                       goto success;
+       }
+
+       /* search between the bottom of user VM and the stack grow area */
+       addr = PAGE_SIZE;
+       limit = (current->mm->start_stack - 0x00200000);
+       if (addr + len <= limit) {
+               limit -= len;
+
+               if (addr <= limit) {
+                       vma = find_vma(current->mm, PAGE_SIZE);
+                       for (; vma; vma = vma->vm_next) {
+                               if (addr > limit)
+                                       break;
+                               if (addr + len <= vma->vm_start)
+                                       goto success;
+                               addr = vma->vm_end;
+                       }
+               }
+       }
+
+       /* search from just above the WorkRAM area to the top of memory */
+       addr = PAGE_ALIGN(0x80000000);
+       limit = TASK_SIZE - len;
+       if (addr <= limit) {
+               vma = find_vma(current->mm, addr);
+               for (; vma; vma = vma->vm_next) {
+                       if (addr > limit)
+                               break;
+                       if (addr + len <= vma->vm_start)
+                               goto success;
+                       addr = vma->vm_end;
+               }
+
+               if (!vma && addr <= limit)
+                       goto success;
+       }
+
+#if 0
+       printk("[area] l=%lx (ENOMEM) f='%s'\n",
+              len, filp ? filp->f_dentry->d_name.name : "");
+#endif
+       return -ENOMEM;
+
+ success:
+#if 0
+       printk("[area] l=%lx ad=%lx f='%s'\n",
+              len, addr, filp ? filp->f_dentry->d_name.name : "");
+#endif
+       return addr;
+} /* end arch_get_unmapped_area() */
diff --git a/arch/frv/mm/extable.c b/arch/frv/mm/extable.c
new file mode 100644 (file)
index 0000000..41be112
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * linux/arch/frv/mm/extable.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+
+extern const struct exception_table_entry __attribute__((aligned(8))) __start___ex_table[];
+extern const struct exception_table_entry __attribute__((aligned(8))) __stop___ex_table[];
+extern const void __memset_end, __memset_user_error_lr, __memset_user_error_handler;
+extern const void __memcpy_end, __memcpy_user_error_lr, __memcpy_user_error_handler;
+extern spinlock_t modlist_lock;
+
+/*****************************************************************************/
+/*
+ *
+ */
+static inline unsigned long search_one_table(const struct exception_table_entry *first,
+                                            const struct exception_table_entry *last,
+                                            unsigned long value)
+{
+        while (first <= last) {
+               const struct exception_table_entry __attribute__((aligned(8))) *mid;
+               long diff;
+
+               mid = (last - first) / 2 + first;
+               diff = mid->insn - value;
+                if (diff == 0)
+                        return mid->fixup;
+                else if (diff < 0)
+                        first = mid + 1;
+                else
+                        last = mid - 1;
+        }
+        return 0;
+} /* end search_one_table() */
+
+/*****************************************************************************/
+/*
+ * see if there's a fixup handler available to deal with a kernel fault
+ */
+unsigned long search_exception_table(unsigned long pc)
+{
+       unsigned long ret = 0;
+
+       /* determine if the fault lay during a memcpy_user or a memset_user */
+       if (__frame->lr == (unsigned long) &__memset_user_error_lr &&
+           (unsigned long) &memset <= pc && pc < (unsigned long) &__memset_end
+           ) {
+               /* the fault occurred in a protected memset
+                * - we search for the return address (in LR) instead of the program counter
+                * - it was probably during a clear_user()
+                */
+               return (unsigned long) &__memset_user_error_handler;
+       }
+       else if (__frame->lr == (unsigned long) &__memcpy_user_error_lr &&
+                (unsigned long) &memcpy <= pc && pc < (unsigned long) &__memcpy_end
+                ) {
+               /* the fault occurred in a protected memset
+                * - we search for the return address (in LR) instead of the program counter
+                * - it was probably during a copy_to/from_user()
+                */
+               return (unsigned long) &__memcpy_user_error_handler;
+       }
+
+#ifndef CONFIG_MODULES
+       /* there is only the kernel to search.  */
+       ret = search_one_table(__start___ex_table, __stop___ex_table - 1, pc);
+       return ret;
+
+#else
+       /* the kernel is the last "module" -- no need to treat it special */
+       unsigned long flags;
+       struct module *mp;
+
+       spin_lock_irqsave(&modlist_lock, flags);
+
+       for (mp = module_list; mp != NULL; mp = mp->next) {
+               if (mp->ex_table_start == NULL || !(mp->flags & (MOD_RUNNING | MOD_INITIALIZING)))
+                       continue;
+               ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, pc);
+               if (ret)
+                       break;
+       }
+
+       spin_unlock_irqrestore(&modlist_lock, flags);
+       return ret;
+#endif
+} /* end search_exception_table() */
diff --git a/arch/frv/mm/fault.c b/arch/frv/mm/fault.c
new file mode 100644 (file)
index 0000000..41d02ac
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ *  linux/arch/frv/mm/fault.c
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * - Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68knommu/mm/fault.c
+ *   - Copyright (C) 1998  D. Jeff Dionne <jeff@lineo.ca>,
+ *   - Copyright (C) 2000  Lineo, Inc.  (www.lineo.com)
+ *
+ *  Based on:
+ *
+ *  linux/arch/m68k/mm/fault.c
+ *
+ *  Copyright (C) 1995  Hamish Macdonald
+ */
+
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/hardirq.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/gdb-stub.h>
+
+/*****************************************************************************/
+/*
+ * This routine handles page faults.  It determines the problem, and
+ * then passes it off to one of the appropriate routines.
+ */
+asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear0)
+{
+       struct vm_area_struct *vma;
+       struct mm_struct *mm;
+       unsigned long _pme, lrai, lrad, fixup;
+       siginfo_t info;
+       pgd_t *pge;
+       pud_t *pue;
+       pte_t *pte;
+       int write;
+
+#if 0
+       const char *atxc[16] = {
+               [0x0] = "mmu-miss", [0x8] = "multi-dat", [0x9] = "multi-sat",
+               [0xa] = "tlb-miss", [0xc] = "privilege", [0xd] = "write-prot",
+       };
+
+       printk("do_page_fault(%d,%lx [%s],%lx)\n",
+              datammu, esr0, atxc[esr0 >> 20 & 0xf], ear0);
+#endif
+
+       mm = current->mm;
+
+       /*
+        * We fault-in kernel-space virtual memory on-demand. The
+        * 'reference' page table is init_mm.pgd.
+        *
+        * NOTE! We MUST NOT take any locks for this case. We may
+        * be in an interrupt or a critical region, and should
+        * only copy the information from the master page table,
+        * nothing more.
+        *
+        * This verifies that the fault happens in kernel space
+        * and that the fault was a page not present (invalid) error
+        */
+       if (!user_mode(__frame) && (esr0 & ESR0_ATXC) == ESR0_ATXC_AMRTLB_MISS) {
+               if (ear0 >= VMALLOC_START && ear0 < VMALLOC_END)
+                       goto kernel_pte_fault;
+               if (ear0 >= PKMAP_BASE && ear0 < PKMAP_END)
+                       goto kernel_pte_fault;
+       }
+
+       info.si_code = SEGV_MAPERR;
+
+       /*
+        * If we're in an interrupt or have no user
+        * context, we must not take the fault..
+        */
+       if (in_interrupt() || !mm)
+               goto no_context;
+
+       down_read(&mm->mmap_sem);
+
+       vma = find_vma(mm, ear0);
+       if (!vma)
+               goto bad_area;
+       if (vma->vm_start <= ear0)
+               goto good_area;
+       if (!(vma->vm_flags & VM_GROWSDOWN))
+               goto bad_area;
+
+       if (user_mode(__frame)) {
+               /*
+                * accessing the stack below %esp is always a bug.
+                * The "+ 32" is there due to some instructions (like
+                * pusha) doing post-decrement on the stack and that
+                * doesn't show up until later..
+                */
+               if ((ear0 & PAGE_MASK) + 2 * PAGE_SIZE < __frame->sp) {
+#if 0
+                       printk("[%d] ### Access below stack @%lx (sp=%lx)\n",
+                              current->pid, ear0, __frame->sp);
+                       show_registers(__frame);
+                       printk("[%d] ### Code: [%08lx] %02x %02x %02x %02x %02x %02x %02x %02x\n",
+                              current->pid,
+                              __frame->pc,
+                              ((u8*)__frame->pc)[0],
+                              ((u8*)__frame->pc)[1],
+                              ((u8*)__frame->pc)[2],
+                              ((u8*)__frame->pc)[3],
+                              ((u8*)__frame->pc)[4],
+                              ((u8*)__frame->pc)[5],
+                              ((u8*)__frame->pc)[6],
+                              ((u8*)__frame->pc)[7]
+                              );
+#endif
+                       goto bad_area;
+               }
+       }
+
+       if (expand_stack(vma, ear0))
+               goto bad_area;
+
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+ good_area:
+       info.si_code = SEGV_ACCERR;
+       write = 0;
+       switch (esr0 & ESR0_ATXC) {
+       default:
+               /* handle write to write protected page */
+       case ESR0_ATXC_WP_EXCEP:
+#ifdef TEST_VERIFY_AREA
+               if (!(user_mode(__frame)))
+                       printk("WP fault at %08lx\n", __frame->pc);
+#endif
+               if (!(vma->vm_flags & VM_WRITE))
+                       goto bad_area;
+               write = 1;
+               break;
+
+                /* handle read from protected page */
+       case ESR0_ATXC_PRIV_EXCEP:
+               goto bad_area;
+
+                /* handle read, write or exec on absent page
+                 * - can't support write without permitting read
+                 * - don't support execute without permitting read and vice-versa
+                 */
+       case ESR0_ATXC_AMRTLB_MISS:
+               if (!(vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)))
+                       goto bad_area;
+               break;
+       }
+
+       /*
+        * If for any reason at all we couldn't handle the fault,
+        * make sure we exit gracefully rather than endlessly redo
+        * the fault.
+        */
+       switch (handle_mm_fault(mm, vma, ear0, write)) {
+       case 1:
+               current->min_flt++;
+               break;
+       case 2:
+               current->maj_flt++;
+               break;
+       case 0:
+               goto do_sigbus;
+       default:
+               goto out_of_memory;
+       }
+
+       up_read(&mm->mmap_sem);
+       return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+ bad_area:
+       up_read(&mm->mmap_sem);
+
+       /* User mode accesses just cause a SIGSEGV */
+       if (user_mode(__frame)) {
+               info.si_signo = SIGSEGV;
+               info.si_errno = 0;
+               /* info.si_code has been set above */
+               info.si_addr = (void *) ear0;
+               force_sig_info(SIGSEGV, &info, current);
+               return;
+       }
+
+ no_context:
+       /* are we prepared to handle this kernel fault? */
+       if ((fixup = search_exception_table(__frame->pc)) != 0) {
+               __frame->pc = fixup;
+               return;
+       }
+
+/*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+
+       bust_spinlocks(1);
+
+       if (ear0 < PAGE_SIZE)
+               printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+       else
+               printk(KERN_ALERT "Unable to handle kernel paging request");
+       printk(" at virtual addr %08lx\n", ear0);
+       printk("  PC  : %08lx\n", __frame->pc);
+       printk("  EXC : esr0=%08lx ear0=%08lx\n", esr0, ear0);
+
+       asm("lrai %1,%0,#1,#0,#0" : "=&r"(lrai) : "r"(ear0));
+       asm("lrad %1,%0,#1,#0,#0" : "=&r"(lrad) : "r"(ear0));
+
+       printk(KERN_ALERT "  LRAI: %08lx\n", lrai);
+       printk(KERN_ALERT "  LRAD: %08lx\n", lrad);
+
+       __break_hijack_kernel_event();
+
+       pge = pgd_offset(current->mm, ear0);
+       pue = pud_offset(pge, ear0);
+       _pme = pue->pue[0].ste[0];
+
+       printk(KERN_ALERT "  PGE : %8p { PME %08lx }\n", pge, _pme);
+
+       if (_pme & xAMPRx_V) {
+               unsigned long dampr, damlr, val;
+
+               asm volatile("movsg dampr2,%0 ! movgs %2,dampr2 ! movsg damlr2,%1"
+                            : "=&r"(dampr), "=r"(damlr)
+                            : "r" (_pme | xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V)
+                            );
+
+               pte = (pte_t *) damlr + __pte_index(ear0);
+               val = pte_val(*pte);
+
+               asm volatile("movgs %0,dampr2" :: "r" (dampr));
+
+               printk(KERN_ALERT "  PTE : %8p { %08lx }\n", pte, val);
+       }
+
+       die_if_kernel("Oops\n");
+       do_exit(SIGKILL);
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+ out_of_memory:
+       up_read(&mm->mmap_sem);
+       printk("VM: killing process %s\n", current->comm);
+       if (user_mode(__frame))
+               do_exit(SIGKILL);
+       goto no_context;
+
+ do_sigbus:
+       up_read(&mm->mmap_sem);
+
+       /*
+        * Send a sigbus, regardless of whether we were in kernel
+        * or user mode.
+        */
+       info.si_signo = SIGBUS;
+       info.si_errno = 0;
+       info.si_code = BUS_ADRERR;
+       info.si_addr = (void *) ear0;
+       force_sig_info(SIGBUS, &info, current);
+
+       /* Kernel mode? Handle exceptions or die */
+       if (!user_mode(__frame))
+               goto no_context;
+       return;
+
+/*
+ * The fault was caused by a kernel PTE (such as installed by vmalloc or kmap)
+ */
+ kernel_pte_fault:
+       {
+               /*
+                * Synchronize this task's top level page-table
+                * with the 'reference' page table.
+                *
+                * Do _not_ use "tsk" here. We might be inside
+                * an interrupt in the middle of a task switch..
+                */
+               int index = pgd_index(ear0);
+               pgd_t *pgd, *pgd_k;
+               pud_t *pud, *pud_k;
+               pmd_t *pmd, *pmd_k;
+               pte_t *pte_k;
+
+               pgd = (pgd_t *) __get_TTBR();
+               pgd = (pgd_t *)__va(pgd) + index;
+               pgd_k = ((pgd_t *)(init_mm.pgd)) + index;
+
+               if (!pgd_present(*pgd_k))
+                       goto no_context;
+               //set_pgd(pgd, *pgd_k); /////// gcc ICE's on this line
+
+               pud_k = pud_offset(pgd_k, ear0);
+               if (!pud_present(*pud_k))
+                       goto no_context;
+
+               pmd_k = pmd_offset(pud_k, ear0);
+               if (!pmd_present(*pmd_k))
+                       goto no_context;
+
+               pud = pud_offset(pgd, ear0);
+               pmd = pmd_offset(pud, ear0);
+               set_pmd(pmd, *pmd_k);
+
+               pte_k = pte_offset_kernel(pmd_k, ear0);
+               if (!pte_present(*pte_k))
+                       goto no_context;
+               return;
+       }
+} /* end do_page_fault() */
diff --git a/arch/frv/mm/highmem.c b/arch/frv/mm/highmem.c
new file mode 100644 (file)
index 0000000..7dc8fbf
--- /dev/null
@@ -0,0 +1,33 @@
+/* highmem.c: arch-specific highmem stuff
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/highmem.h>
+
+void *kmap(struct page *page)
+{
+       might_sleep();
+       if (!PageHighMem(page))
+               return page_address(page);
+       return kmap_high(page);
+}
+
+void kunmap(struct page *page)
+{
+       if (in_interrupt())
+               BUG();
+       if (!PageHighMem(page))
+               return;
+       kunmap_high(page);
+}
+
+struct page *kmap_atomic_to_page(void *ptr)
+{
+       return virt_to_page(ptr);
+}
diff --git a/arch/frv/mm/init.c b/arch/frv/mm/init.c
new file mode 100644 (file)
index 0000000..41958f5
--- /dev/null
@@ -0,0 +1,241 @@
+/* init.c: memory initialisation for FRV
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Derived from:
+ *  - linux/arch/m68knommu/mm/init.c
+ *    - Copyright (C) 1998  D. Jeff Dionne <jeff@lineo.ca>, Kenneth Albanowski <kjahds@kjahds.com>,
+ *    - Copyright (C) 2000  Lineo, Inc.  (www.lineo.com)
+ *  - linux/arch/m68k/mm/init.c
+ *    - Copyright (C) 1995  Hamish Macdonald
+ */
+
+#include <linux/config.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/bootmem.h>
+#include <linux/highmem.h>
+
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/mmu_context.h>
+#include <asm/virtconvert.h>
+#include <asm/sections.h>
+#include <asm/tlb.h>
+
+#undef DEBUG
+
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
+
+/*
+ * BAD_PAGE is the page that is used for page faults when linux
+ * is out-of-memory. Older versions of linux just did a
+ * do_exit(), but using this instead means there is less risk
+ * for a process dying in kernel mode, possibly leaving a inode
+ * unused etc..
+ *
+ * BAD_PAGETABLE is the accompanying page-table: it is initialized
+ * to point to BAD_PAGE entries.
+ *
+ * ZERO_PAGE is a special page that is used for zero-initialized
+ * data and COW.
+ */
+static unsigned long empty_bad_page_table;
+static unsigned long empty_bad_page;
+unsigned long empty_zero_page;
+
+/*****************************************************************************/
+/*
+ *
+ */
+void show_mem(void)
+{
+       unsigned long i;
+       int free = 0, total = 0, reserved = 0, shared = 0;
+
+       printk("\nMem-info:\n");
+       show_free_areas();
+       i = max_mapnr;
+       while (i-- > 0) {
+               struct page *page = &mem_map[i];
+
+               total++;
+               if (PageReserved(page))
+                       reserved++;
+               else if (!page_count(page))
+                       free++;
+               else
+                       shared += page_count(page) - 1;
+       }
+
+       printk("%d pages of RAM\n",total);
+       printk("%d free pages\n",free);
+       printk("%d reserved pages\n",reserved);
+       printk("%d pages shared\n",shared);
+
+} /* end show_mem() */
+
+/*****************************************************************************/
+/*
+ * paging_init() continues the virtual memory environment setup which
+ * was begun by the code in arch/head.S.
+ * The parameters are pointers to where to stick the starting and ending
+ * addresses  of available kernel virtual memory.
+ */
+void __init paging_init(void)
+{
+       unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
+
+       /* allocate some pages for kernel housekeeping tasks */
+       empty_bad_page_table    = (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
+       empty_bad_page          = (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
+       empty_zero_page         = (unsigned long) alloc_bootmem_pages(PAGE_SIZE);
+
+       memset((void *) empty_zero_page, 0, PAGE_SIZE);
+
+#if CONFIG_HIGHMEM
+       if (num_physpages - num_mappedpages) {
+               pgd_t *pge;
+               pud_t *pue;
+               pmd_t *pme;
+
+               pkmap_page_table = alloc_bootmem_pages(PAGE_SIZE);
+
+               memset(pkmap_page_table, 0, PAGE_SIZE);
+
+               pge = swapper_pg_dir + pgd_index_k(PKMAP_BASE);
+               pue = pud_offset(pge, PKMAP_BASE);
+               pme = pmd_offset(pue, PKMAP_BASE);
+               __set_pmd(pme, virt_to_phys(pkmap_page_table) | _PAGE_TABLE);
+       }
+#endif
+
+       /* distribute the allocatable pages across the various zones and pass them to the allocator
+        */
+       zones_size[ZONE_DMA]     = max_low_pfn - min_low_pfn;
+       zones_size[ZONE_NORMAL]  = 0;
+#ifdef CONFIG_HIGHMEM
+       zones_size[ZONE_HIGHMEM] = num_physpages - num_mappedpages;
+#endif
+
+       free_area_init(zones_size);
+
+#ifdef CONFIG_MMU
+       /* initialise init's MMU context */
+       init_new_context(&init_task, &init_mm);
+#endif
+
+} /* end paging_init() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+void __init mem_init(void)
+{
+       unsigned long npages = (memory_end - memory_start) >> PAGE_SHIFT;
+       unsigned long tmp;
+#ifdef CONFIG_MMU
+       unsigned long loop, pfn;
+       int datapages = 0;
+#endif
+       int codek = 0, datak = 0;
+
+       /* this will put all memory onto the freelists */
+       totalram_pages = free_all_bootmem();
+
+#ifdef CONFIG_MMU
+       for (loop = 0 ; loop < npages ; loop++)
+               if (PageReserved(&mem_map[loop]))
+                       datapages++;
+
+#ifdef CONFIG_HIGHMEM
+       for (pfn = num_physpages - 1; pfn >= num_mappedpages; pfn--) {
+               struct page *page = &mem_map[pfn];
+
+               ClearPageReserved(page);
+               set_bit(PG_highmem, &page->flags);
+               set_page_count(page, 1);
+               __free_page(page);
+               totalram_pages++;
+       }
+#endif
+
+       codek = ((unsigned long) &_etext - (unsigned long) &_stext) >> 10;
+       datak = datapages << (PAGE_SHIFT - 10);
+
+#else
+       codek = (_etext - _stext) >> 10;
+       datak = 0; //(_ebss - _sdata) >> 10;
+#endif
+
+       tmp = nr_free_pages() << PAGE_SHIFT;
+       printk("Memory available: %luKiB/%luKiB RAM, %luKiB/%luKiB ROM (%dKiB kernel code, %dKiB data)\n",
+              tmp >> 10,
+              npages << (PAGE_SHIFT - 10),
+              (rom_length > 0) ? ((rom_length >> 10) - codek) : 0,
+              rom_length >> 10,
+              codek,
+              datak
+              );
+
+} /* end mem_init() */
+
+/*****************************************************************************/
+/*
+ * free the memory that was only required for initialisation
+ */
+void __init free_initmem(void)
+{
+#if defined(CONFIG_RAMKERNEL) && !defined(CONFIG_PROTECT_KERNEL)
+       unsigned long start, end, addr;
+
+       start = PAGE_ALIGN((unsigned long) &__init_begin);      /* round up */
+       end   = ((unsigned long) &__init_end) & PAGE_MASK;      /* round down */
+
+       /* next to check that the page we free is not a partial page */
+       for (addr = start; addr < end; addr += PAGE_SIZE) {
+               ClearPageReserved(virt_to_page(addr));
+               set_page_count(virt_to_page(addr), 1);
+               free_page(addr);
+               totalram_pages++;
+       }
+
+       printk("Freeing unused kernel memory: %ldKiB freed (0x%lx - 0x%lx)\n",
+              (end - start) >> 10, start, end);
+#endif
+} /* end free_initmem() */
+
+/*****************************************************************************/
+/*
+ * free the initial ramdisk memory
+ */
+#ifdef CONFIG_BLK_DEV_INITRD
+void __init free_initrd_mem(unsigned long start, unsigned long end)
+{
+       int pages = 0;
+       for (; start < end; start += PAGE_SIZE) {
+               ClearPageReserved(virt_to_page(start));
+               set_page_count(virt_to_page(start), 1);
+               free_page(start);
+               totalram_pages++;
+               pages++;
+       }
+       printk("Freeing initrd memory: %dKiB freed\n", (pages * PAGE_SIZE) >> 10);
+} /* end free_initrd_mem() */
+#endif
diff --git a/arch/frv/mm/kmap.c b/arch/frv/mm/kmap.c
new file mode 100644 (file)
index 0000000..539f45e
--- /dev/null
@@ -0,0 +1,62 @@
+/* kmap.c: ioremapping handlers
+ *
+ * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from arch/m68k/mm/kmap.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.
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#undef DEBUG
+
+/*****************************************************************************/
+/*
+ * Map some physical address range into the kernel address space.
+ */
+
+void *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag)
+{
+       return (void *)physaddr;
+}
+
+/*
+ * Unmap a ioremap()ed region again
+ */
+void iounmap(void *addr)
+{
+}
+
+/*
+ * __iounmap unmaps nearly everything, so be careful
+ * it doesn't free currently pointer/page tables anymore but it
+ * wans't used anyway and might be added later.
+ */
+void __iounmap(void *addr, unsigned long size)
+{
+}
+
+/*
+ * Set new cache mode for some kernel address space.
+ * The caller must push data for that range itself, if such data may already
+ * be in the cache.
+ */
+void kernel_set_cachemode(void *addr, unsigned long size, int cmode)
+{
+}
diff --git a/arch/frv/mm/mmu-context.c b/arch/frv/mm/mmu-context.c
new file mode 100644 (file)
index 0000000..f2c6866
--- /dev/null
@@ -0,0 +1,208 @@
+/* mmu-context.c: MMU context allocation and management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/tlbflush.h>
+
+#define NR_CXN 4096
+
+static unsigned long cxn_bitmap[NR_CXN / (sizeof(unsigned long) * 8)];
+static LIST_HEAD(cxn_owners_lru);
+static DEFINE_SPINLOCK(cxn_owners_lock);
+
+int __nongpreldata cxn_pinned = -1;
+
+
+/*****************************************************************************/
+/*
+ * initialise a new context
+ */
+int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+       memset(&mm->context, 0, sizeof(mm->context));
+       INIT_LIST_HEAD(&mm->context.id_link);
+       mm->context.itlb_cached_pge = 0xffffffffUL;
+       mm->context.dtlb_cached_pge = 0xffffffffUL;
+
+       return 0;
+} /* end init_new_context() */
+
+/*****************************************************************************/
+/*
+ * make sure a kernel MMU context has a CPU context number
+ * - call with cxn_owners_lock held
+ */
+static unsigned get_cxn(mm_context_t *ctx)
+{
+       struct list_head *_p;
+       mm_context_t *p;
+       unsigned cxn;
+
+       if (!list_empty(&ctx->id_link)) {
+               list_move_tail(&ctx->id_link, &cxn_owners_lru);
+       }
+       else {
+               /* find the first unallocated context number
+                * - 0 is reserved for the kernel
+                */
+               cxn = find_next_zero_bit(&cxn_bitmap, NR_CXN, 1);
+               if (cxn < NR_CXN) {
+                       set_bit(cxn, &cxn_bitmap);
+               }
+               else {
+                       /* none remaining - need to steal someone else's cxn */
+                       p = NULL;
+                       list_for_each(_p, &cxn_owners_lru) {
+                               p = list_entry(_p, mm_context_t, id_link);
+                               if (!p->id_busy && p->id != cxn_pinned)
+                                       break;
+                       }
+
+                       BUG_ON(_p == &cxn_owners_lru);
+
+                       cxn = p->id;
+                       p->id = 0;
+                       list_del_init(&p->id_link);
+                       __flush_tlb_mm(cxn);
+               }
+
+               ctx->id = cxn;
+               list_add_tail(&ctx->id_link, &cxn_owners_lru);
+       }
+
+       return ctx->id;
+} /* end get_cxn() */
+
+/*****************************************************************************/
+/*
+ * restore the current TLB miss handler mapped page tables into the MMU context and set up a
+ * mapping for the page directory
+ */
+void change_mm_context(mm_context_t *old, mm_context_t *ctx, pgd_t *pgd)
+{
+       unsigned long _pgd;
+
+       _pgd = virt_to_phys(pgd);
+
+       /* save the state of the outgoing MMU context */
+       old->id_busy = 0;
+
+       asm volatile("movsg scr0,%0"   : "=r"(old->itlb_cached_pge));
+       asm volatile("movsg dampr4,%0" : "=r"(old->itlb_ptd_mapping));
+       asm volatile("movsg scr1,%0"   : "=r"(old->dtlb_cached_pge));
+       asm volatile("movsg dampr5,%0" : "=r"(old->dtlb_ptd_mapping));
+
+       /* select an MMU context number */
+       spin_lock(&cxn_owners_lock);
+       get_cxn(ctx);
+       ctx->id_busy = 1;
+       spin_unlock(&cxn_owners_lock);
+
+       asm volatile("movgs %0,cxnr"   : : "r"(ctx->id));
+
+       /* restore the state of the incoming MMU context */
+       asm volatile("movgs %0,scr0"   : : "r"(ctx->itlb_cached_pge));
+       asm volatile("movgs %0,dampr4" : : "r"(ctx->itlb_ptd_mapping));
+       asm volatile("movgs %0,scr1"   : : "r"(ctx->dtlb_cached_pge));
+       asm volatile("movgs %0,dampr5" : : "r"(ctx->dtlb_ptd_mapping));
+
+       /* map the PGD into uncached virtual memory */
+       asm volatile("movgs %0,ttbr"   : : "r"(_pgd));
+       asm volatile("movgs %0,dampr3"
+                    :: "r"(_pgd | xAMPRx_L | xAMPRx_M | xAMPRx_SS_16Kb |
+                           xAMPRx_S | xAMPRx_C | xAMPRx_V));
+
+} /* end change_mm_context() */
+
+/*****************************************************************************/
+/*
+ * finished with an MMU context number
+ */
+void destroy_context(struct mm_struct *mm)
+{
+       mm_context_t *ctx = &mm->context;
+
+       spin_lock(&cxn_owners_lock);
+
+       if (!list_empty(&ctx->id_link)) {
+               if (ctx->id == cxn_pinned)
+                       cxn_pinned = -1;
+
+               list_del_init(&ctx->id_link);
+               clear_bit(ctx->id, &cxn_bitmap);
+               __flush_tlb_mm(ctx->id);
+               ctx->id = 0;
+       }
+
+       spin_unlock(&cxn_owners_lock);
+} /* end destroy_context() */
+
+/*****************************************************************************/
+/*
+ * display the MMU context currently a process is currently using
+ */
+#ifdef CONFIG_PROC_FS
+char *proc_pid_status_frv_cxnr(struct mm_struct *mm, char *buffer)
+{
+       spin_lock(&cxn_owners_lock);
+       buffer += sprintf(buffer, "CXNR: %u\n", mm->context.id);
+       spin_unlock(&cxn_owners_lock);
+
+       return buffer;
+} /* end proc_pid_status_frv_cxnr() */
+#endif
+
+/*****************************************************************************/
+/*
+ * (un)pin a process's mm_struct's MMU context ID
+ */
+int cxn_pin_by_pid(pid_t pid)
+{
+       struct task_struct *tsk;
+       struct mm_struct *mm = NULL;
+       int ret;
+
+       /* unpin if pid is zero */
+       if (pid == 0) {
+               cxn_pinned = -1;
+               return 0;
+       }
+
+       ret = -ESRCH;
+
+       /* get a handle on the mm_struct */
+       read_lock(&tasklist_lock);
+       tsk = find_task_by_pid(pid);
+       if (tsk) {
+               ret = -EINVAL;
+
+               task_lock(tsk);
+               if (tsk->mm) {
+                       mm = tsk->mm;
+                       atomic_inc(&mm->mm_users);
+                       ret = 0;
+               }
+               task_unlock(tsk);
+       }
+       read_unlock(&tasklist_lock);
+
+       if (ret < 0)
+               return ret;
+
+       /* make sure it has a CXN and pin it */
+       spin_lock(&cxn_owners_lock);
+       cxn_pinned = get_cxn(&mm->context);
+       spin_unlock(&cxn_owners_lock);
+
+       mmput(mm);
+       return 0;
+} /* end cxn_pin_by_pid() */
diff --git a/arch/frv/mm/pgalloc.c b/arch/frv/mm/pgalloc.c
new file mode 100644 (file)
index 0000000..4eaec0f
--- /dev/null
@@ -0,0 +1,159 @@
+/* pgalloc.c: page directory & page table allocation
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <asm/pgalloc.h>
+#include <asm/page.h>
+#include <asm/cacheflush.h>
+
+pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((aligned(PAGE_SIZE)));
+kmem_cache_t *pgd_cache;
+
+pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address)
+{
+       pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT);
+       if (pte)
+               clear_page(pte);
+       return pte;
+}
+
+struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+       struct page *page;
+
+#ifdef CONFIG_HIGHPTE
+       page = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT, 0);
+#else
+       page = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0);
+#endif
+       if (page)
+               clear_highpage(page);
+       flush_dcache_page(page);
+       return page;
+}
+
+void __set_pmd(pmd_t *pmdptr, unsigned long pmd)
+{
+       unsigned long *__ste_p = pmdptr->ste;
+       int loop;
+
+       if (!pmd) {
+               memset(__ste_p, 0, PME_SIZE);
+       }
+       else {
+               BUG_ON(pmd & (0x3f00 | xAMPRx_SS | 0xe));
+
+               for (loop = PME_SIZE; loop > 0; loop -= 4) {
+                       *__ste_p++ = pmd;
+                       pmd += __frv_PT_SIZE;
+               }
+       }
+
+       frv_dcache_writeback((unsigned long) pmdptr, (unsigned long) (pmdptr + 1));
+}
+
+/*
+ * List of all pgd's needed for non-PAE so it can invalidate entries
+ * in both cached and uncached pgd's; not needed for PAE since the
+ * kernel pmd is shared. If PAE were not to share the pmd a similar
+ * tactic would be needed. This is essentially codepath-based locking
+ * against pageattr.c; it is the unique case in which a valid change
+ * of kernel pagetables can't be lazily synchronized by vmalloc faults.
+ * vmalloc faults work because attached pagetables are never freed.
+ * If the locking proves to be non-performant, a ticketing scheme with
+ * checks at dup_mmap(), exec(), and other mmlist addition points
+ * could be used. The locking scheme was chosen on the basis of
+ * manfred's recommendations and having no core impact whatsoever.
+ * -- wli
+ */
+DEFINE_SPINLOCK(pgd_lock);
+struct page *pgd_list;
+
+static inline void pgd_list_add(pgd_t *pgd)
+{
+       struct page *page = virt_to_page(pgd);
+       page->index = (unsigned long) pgd_list;
+       if (pgd_list)
+               pgd_list->private = (unsigned long) &page->index;
+       pgd_list = page;
+       page->private = (unsigned long) &pgd_list;
+}
+
+static inline void pgd_list_del(pgd_t *pgd)
+{
+       struct page *next, **pprev, *page = virt_to_page(pgd);
+       next = (struct page *) page->index;
+       pprev = (struct page **) page->private;
+       *pprev = next;
+       if (next)
+               next->private = (unsigned long) pprev;
+}
+
+void pgd_ctor(void *pgd, kmem_cache_t *cache, unsigned long unused)
+{
+       unsigned long flags;
+
+       if (PTRS_PER_PMD == 1)
+               spin_lock_irqsave(&pgd_lock, flags);
+
+       memcpy((pgd_t *) pgd + USER_PGDS_IN_LAST_PML4,
+              swapper_pg_dir + USER_PGDS_IN_LAST_PML4,
+              (PTRS_PER_PGD - USER_PGDS_IN_LAST_PML4) * sizeof(pgd_t));
+
+       if (PTRS_PER_PMD > 1)
+               return;
+
+       pgd_list_add(pgd);
+       spin_unlock_irqrestore(&pgd_lock, flags);
+       memset(pgd, 0, USER_PGDS_IN_LAST_PML4 * sizeof(pgd_t));
+}
+
+/* never called when PTRS_PER_PMD > 1 */
+void pgd_dtor(void *pgd, kmem_cache_t *cache, unsigned long unused)
+{
+       unsigned long flags; /* can be called from interrupt context */
+
+       spin_lock_irqsave(&pgd_lock, flags);
+       pgd_list_del(pgd);
+       spin_unlock_irqrestore(&pgd_lock, flags);
+}
+
+pgd_t *pgd_alloc(struct mm_struct *mm)
+{
+       pgd_t *pgd;
+
+       pgd = kmem_cache_alloc(pgd_cache, GFP_KERNEL);
+       if (!pgd)
+               return pgd;
+
+       return pgd;
+}
+
+void pgd_free(pgd_t *pgd)
+{
+       /* in the non-PAE case, clear_page_tables() clears user pgd entries */
+       kmem_cache_free(pgd_cache, pgd);
+}
+
+void __init pgtable_cache_init(void)
+{
+       pgd_cache = kmem_cache_create("pgd",
+                                     PTRS_PER_PGD * sizeof(pgd_t),
+                                     PTRS_PER_PGD * sizeof(pgd_t),
+                                     0,
+                                     pgd_ctor,
+                                     pgd_dtor);
+       if (!pgd_cache)
+               panic("pgtable_cache_init(): Cannot create pgd cache");
+}
diff --git a/arch/frv/mm/tlb-flush.S b/arch/frv/mm/tlb-flush.S
new file mode 100644 (file)
index 0000000..6f43c74
--- /dev/null
@@ -0,0 +1,185 @@
+/* tlb-flush.S: TLB flushing routines
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/spr-regs.h>
+
+.macro DEBUG ch
+#      sethi.p         %hi(0xfeff9c00),gr4
+#      setlo           %lo(0xfeff9c00),gr4
+#      setlos          #\ch,gr5
+#      stbi            gr5,@(gr4,#0)
+#      membar
+.endm
+
+       .section        .rodata
+
+       # sizes corresponding to TPXR.LMAX
+       .balign         1
+__tlb_lmax_sizes:
+       .byte           0, 64, 0, 0
+       .byte           0, 0, 0, 0
+       .byte           0, 0, 0, 0
+       .byte           0, 0, 0, 0
+
+       .section        .text
+       .balign         4
+
+###############################################################################
+#
+# flush everything
+# - void __flush_tlb_all(void)
+#
+###############################################################################
+       .globl          __flush_tlb_all
+       .type           __flush_tlb_all,@function
+__flush_tlb_all:
+       DEBUG           'A'
+
+       # kill cached PGE value
+       setlos          #0xffffffff,gr4
+       movgs           gr4,scr0
+       movgs           gr4,scr1
+
+       # kill AMPR-cached TLB values
+       movgs           gr0,iamlr1
+       movgs           gr0,iampr1
+       movgs           gr0,damlr1
+       movgs           gr0,dampr1
+
+       # find out how many lines there are
+       movsg           tpxr,gr5
+       sethi.p         %hi(__tlb_lmax_sizes),gr4
+       srli            gr5,#TPXR_LMAX_SHIFT,gr5
+       setlo.p         %lo(__tlb_lmax_sizes),gr4
+       andi            gr5,#TPXR_LMAX_SMASK,gr5
+       ldub            @(gr4,gr5),gr4
+
+       # now, we assume that the TLB line step is page size in size
+       setlos.p        #PAGE_SIZE,gr5
+       setlos          #0,gr6
+1:
+       tlbpr           gr6,gr0,#6,#0
+       subicc.p        gr4,#1,gr4,icc0
+       add             gr6,gr5,gr6
+       bne             icc0,#2,1b
+
+       DEBUG           'B'
+       bralr
+
+       .size           __flush_tlb_all, .-__flush_tlb_all
+
+###############################################################################
+#
+# flush everything to do with one context
+# - void __flush_tlb_mm(unsigned long contextid [GR8])
+#
+###############################################################################
+       .globl          __flush_tlb_mm
+       .type           __flush_tlb_mm,@function
+__flush_tlb_mm:
+       DEBUG           'M'
+
+       # kill cached PGE value
+       setlos          #0xffffffff,gr4
+       movgs           gr4,scr0
+       movgs           gr4,scr1
+
+       # specify the context we want to flush
+       movgs           gr8,tplr
+
+       # find out how many lines there are
+       movsg           tpxr,gr5
+       sethi.p         %hi(__tlb_lmax_sizes),gr4
+       srli            gr5,#TPXR_LMAX_SHIFT,gr5
+       setlo.p         %lo(__tlb_lmax_sizes),gr4
+       andi            gr5,#TPXR_LMAX_SMASK,gr5
+       ldub            @(gr4,gr5),gr4
+
+       # now, we assume that the TLB line step is page size in size
+       setlos.p        #PAGE_SIZE,gr5
+       setlos          #0,gr6
+0:
+       tlbpr           gr6,gr0,#5,#0
+       subicc.p        gr4,#1,gr4,icc0
+       add             gr6,gr5,gr6
+       bne             icc0,#2,0b
+
+       DEBUG           'N'
+       bralr
+
+       .size           __flush_tlb_mm, .-__flush_tlb_mm
+
+###############################################################################
+#
+# flush a range of addresses from the TLB
+# - void __flush_tlb_page(unsigned long contextid [GR8],
+#                        unsigned long start [GR9])
+#
+###############################################################################
+       .globl          __flush_tlb_page
+       .type           __flush_tlb_page,@function
+__flush_tlb_page:
+       # kill cached PGE value
+       setlos          #0xffffffff,gr4
+       movgs           gr4,scr0
+       movgs           gr4,scr1
+
+       # specify the context we want to flush
+       movgs           gr8,tplr
+
+       # zap the matching TLB line and AMR values
+       setlos          #~(PAGE_SIZE-1),gr5
+       and             gr9,gr5,gr9
+       tlbpr           gr9,gr0,#5,#0
+
+       bralr
+
+       .size           __flush_tlb_page, .-__flush_tlb_page
+
+###############################################################################
+#
+# flush a range of addresses from the TLB
+# - void __flush_tlb_range(unsigned long contextid [GR8],
+#                         unsigned long start [GR9],
+#                         unsigned long end [GR10])
+#
+###############################################################################
+       .globl          __flush_tlb_range
+       .type           __flush_tlb_range,@function
+__flush_tlb_range:
+       # kill cached PGE value
+       setlos          #0xffffffff,gr4
+       movgs           gr4,scr0
+       movgs           gr4,scr1
+
+       # specify the context we want to flush
+       movgs           gr8,tplr
+
+       # round the start down to beginning of TLB line and end up to beginning of next TLB line
+       setlos.p        #~(PAGE_SIZE-1),gr5
+       setlos          #PAGE_SIZE,gr6
+       subi.p          gr10,#1,gr10
+       and             gr9,gr5,gr9
+       and             gr10,gr5,gr10
+2:
+       tlbpr           gr9,gr0,#5,#0
+       subcc.p         gr9,gr10,gr0,icc0
+       add             gr9,gr6,gr9
+       bne             icc0,#0,2b              ; most likely a 1-page flush
+
+       bralr
+
+       .size           __flush_tlb_range, .-__flush_tlb_range
diff --git a/arch/frv/mm/tlb-miss.S b/arch/frv/mm/tlb-miss.S
new file mode 100644 (file)
index 0000000..8729f7d
--- /dev/null
@@ -0,0 +1,631 @@
+/* tlb-miss.S: TLB miss handlers
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sys.h>
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/highmem.h>
+#include <asm/spr-regs.h>
+
+       .section        .text
+       .balign         4
+
+       .globl          __entry_insn_mmu_miss
+__entry_insn_mmu_miss:
+       break
+       nop
+
+       .globl          __entry_insn_mmu_exception
+__entry_insn_mmu_exception:
+       break
+       nop
+
+       .globl          __entry_data_mmu_miss
+__entry_data_mmu_miss:
+       break
+       nop
+
+       .globl          __entry_data_mmu_exception
+__entry_data_mmu_exception:
+       break
+       nop
+
+###############################################################################
+#
+# handle a lookup failure of one sort or another in a kernel TLB handler
+# On entry:
+#   GR29 - faulting address
+#   SCR2 - saved CCR
+#
+###############################################################################
+       .type           __tlb_kernel_fault,@function
+__tlb_kernel_fault:
+       # see if we're supposed to re-enable single-step mode upon return
+       sethi.p         %hi(__break_tlb_miss_return_break),gr30
+       setlo           %lo(__break_tlb_miss_return_break),gr30
+       movsg           pcsr,gr31
+
+       subcc           gr31,gr30,gr0,icc0
+       beq             icc0,#0,__tlb_kernel_fault_sstep
+
+       movsg           scr2,gr30
+       movgs           gr30,ccr
+       movgs           gr29,scr2                       /* save EAR0 value */
+       sethi.p         %hi(__kernel_current_task),gr29
+       setlo           %lo(__kernel_current_task),gr29
+       ldi.p           @(gr29,#0),gr29                 /* restore GR29 */
+
+       bra             __entry_kernel_handle_mmu_fault
+
+       # we've got to re-enable single-stepping
+__tlb_kernel_fault_sstep:
+       sethi.p         %hi(__break_tlb_miss_real_return_info),gr30
+       setlo           %lo(__break_tlb_miss_real_return_info),gr30
+       lddi            @(gr30,0),gr30
+       movgs           gr30,pcsr
+       movgs           gr31,psr
+
+       movsg           scr2,gr30
+       movgs           gr30,ccr
+       movgs           gr29,scr2                       /* save EAR0 value */
+       sethi.p         %hi(__kernel_current_task),gr29
+       setlo           %lo(__kernel_current_task),gr29
+       ldi.p           @(gr29,#0),gr29                 /* restore GR29 */
+       bra             __entry_kernel_handle_mmu_fault_sstep
+
+       .size           __tlb_kernel_fault, .-__tlb_kernel_fault
+
+###############################################################################
+#
+# handle a lookup failure of one sort or another in a user TLB handler
+# On entry:
+#   GR28 - faulting address
+#   SCR2 - saved CCR
+#
+###############################################################################
+       .type           __tlb_user_fault,@function
+__tlb_user_fault:
+       # see if we're supposed to re-enable single-step mode upon return
+       sethi.p         %hi(__break_tlb_miss_return_break),gr30
+       setlo           %lo(__break_tlb_miss_return_break),gr30
+       movsg           pcsr,gr31
+       subcc           gr31,gr30,gr0,icc0
+       beq             icc0,#0,__tlb_user_fault_sstep
+
+       movsg           scr2,gr30
+       movgs           gr30,ccr
+       bra             __entry_uspace_handle_mmu_fault
+
+       # we've got to re-enable single-stepping
+__tlb_user_fault_sstep:
+       sethi.p         %hi(__break_tlb_miss_real_return_info),gr30
+       setlo           %lo(__break_tlb_miss_real_return_info),gr30
+       lddi            @(gr30,0),gr30
+       movgs           gr30,pcsr
+       movgs           gr31,psr
+       movsg           scr2,gr30
+       movgs           gr30,ccr
+       bra             __entry_uspace_handle_mmu_fault_sstep
+
+       .size           __tlb_user_fault, .-__tlb_user_fault
+
+###############################################################################
+#
+# Kernel instruction TLB miss handler
+# On entry:
+#   GR1   - kernel stack pointer
+#   GR28  - saved exception frame pointer
+#   GR29  - faulting address
+#   GR31  - EAR0 ^ SCR0
+#   SCR0  - base of virtual range covered by cached PGE from last ITLB miss (or 0xffffffff)
+#   DAMR3 - mapped page directory
+#   DAMR4 - mapped page table as matched by SCR0
+#
+###############################################################################
+       .globl          __entry_kernel_insn_tlb_miss
+       .type           __entry_kernel_insn_tlb_miss,@function
+__entry_kernel_insn_tlb_miss:
+#if 0
+       sethi.p         %hi(0xe1200004),gr30
+       setlo           %lo(0xe1200004),gr30
+       st              gr0,@(gr30,gr0)
+       sethi.p         %hi(0xffc00100),gr30
+       setlo           %lo(0xffc00100),gr30
+       sth             gr30,@(gr30,gr0)
+       membar
+#endif
+
+       movsg           ccr,gr30                        /* save CCR */
+       movgs           gr30,scr2
+
+       # see if the cached page table mapping is appropriate
+       srlicc.p        gr31,#26,gr0,icc0
+       setlos          0x3ffc,gr30
+       srli.p          gr29,#12,gr31                   /* use EAR0[25:14] as PTE index */
+       bne             icc0,#0,__itlb_k_PTD_miss
+
+__itlb_k_PTD_mapped:
+       # access the PTD with EAR0[25:14]
+       # - DAMLR4 points to the virtual address of the appropriate page table
+       # - the PTD holds 4096 PTEs
+       # - the PTD must be accessed uncached
+       # - the PTE must be marked accessed if it was valid
+       #
+       and             gr31,gr30,gr31
+       movsg           damlr4,gr30
+       add             gr30,gr31,gr31
+       ldi             @(gr31,#0),gr30                 /* fetch the PTE */
+       andicc          gr30,#_PAGE_PRESENT,gr0,icc0
+       ori.p           gr30,#_PAGE_ACCESSED,gr30
+       beq             icc0,#0,__tlb_kernel_fault      /* jump if PTE invalid */
+       sti.p           gr30,@(gr31,#0)                 /* update the PTE */
+       andi            gr30,#~_PAGE_ACCESSED,gr30
+
+       # we're using IAMR1 as an extra TLB entry
+       # - punt the entry here (if valid) to the real TLB and then replace with the new PTE
+       # - need to check DAMR1 lest we cause an multiple-DAT-hit exception
+       # - IAMPR1 has no WP bit, and we mustn't lose WP information
+       movsg           iampr1,gr31
+       andicc          gr31,#xAMPRx_V,gr0,icc0
+       setlos.p        0xfffff000,gr31
+       beq             icc0,#0,__itlb_k_nopunt         /* punt not required */
+
+       movsg           iamlr1,gr31
+       movgs           gr31,tplr                       /* set TPLR.CXN */
+       tlbpr           gr31,gr0,#4,#0                  /* delete matches from TLB, IAMR1, DAMR1 */
+
+       movsg           dampr1,gr31
+       ori             gr31,#xAMPRx_V,gr31             /* entry was invalidated by tlbpr #4 */
+       movgs           gr31,tppr
+       movsg           iamlr1,gr31                     /* set TPLR.CXN */
+       movgs           gr31,tplr
+       tlbpr           gr31,gr0,#2,#0                  /* save to the TLB */
+       movsg           tpxr,gr31                       /* check the TLB write error flag */
+       andicc.p        gr31,#TPXR_E,gr0,icc0
+       setlos          #0xfffff000,gr31
+       bne             icc0,#0,__tlb_kernel_fault
+
+__itlb_k_nopunt:
+
+       # assemble the new TLB entry
+       and             gr29,gr31,gr29
+       movsg           cxnr,gr31
+       or              gr29,gr31,gr29
+       movgs           gr29,iamlr1                     /* xAMLR = address | context number */
+       movgs           gr30,iampr1
+       movgs           gr29,damlr1
+       movgs           gr30,dampr1
+
+       # return, restoring registers
+       movsg           scr2,gr30
+       movgs           gr30,ccr
+       sethi.p         %hi(__kernel_current_task),gr29
+       setlo           %lo(__kernel_current_task),gr29
+       ldi             @(gr29,#0),gr29
+       rett            #0
+       beq             icc0,#3,0                       /* prevent icache prefetch */
+
+       # the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more
+       # appropriate page table and map that instead
+       #   - access the PGD with EAR0[31:26]
+       #   - DAMLR3 points to the virtual address of the page directory
+       #   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables
+__itlb_k_PTD_miss:
+       srli            gr29,#26,gr31                   /* calculate PGE offset */
+       slli            gr31,#8,gr31                    /* and clear bottom bits */
+
+       movsg           damlr3,gr30
+       ld              @(gr31,gr30),gr30               /* access the PGE */
+
+       andicc.p        gr30,#_PAGE_PRESENT,gr0,icc0
+       andicc          gr30,#xAMPRx_SS,gr0,icc1
+
+       # map this PTD instead and record coverage address
+       ori.p           gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30
+       beq             icc0,#0,__tlb_kernel_fault      /* jump if PGE not present */
+       slli.p          gr31,#18,gr31
+       bne             icc1,#0,__itlb_k_bigpage
+       movgs           gr30,dampr4
+       movgs           gr31,scr0
+
+       # we can now resume normal service
+       setlos          0x3ffc,gr30
+       srli.p          gr29,#12,gr31                   /* use EAR0[25:14] as PTE index */
+       bra             __itlb_k_PTD_mapped
+
+__itlb_k_bigpage:
+       break
+       nop
+
+       .size           __entry_kernel_insn_tlb_miss, .-__entry_kernel_insn_tlb_miss
+
+###############################################################################
+#
+# Kernel data TLB miss handler
+# On entry:
+#   GR1   - kernel stack pointer
+#   GR28  - saved exception frame pointer
+#   GR29  - faulting address
+#   GR31  - EAR0 ^ SCR1
+#   SCR1  - base of virtual range covered by cached PGE from last DTLB miss (or 0xffffffff)
+#   DAMR3 - mapped page directory
+#   DAMR5 - mapped page table as matched by SCR1
+#
+###############################################################################
+       .globl          __entry_kernel_data_tlb_miss
+       .type           __entry_kernel_data_tlb_miss,@function
+__entry_kernel_data_tlb_miss:
+#if 0
+       sethi.p         %hi(0xe1200004),gr30
+       setlo           %lo(0xe1200004),gr30
+       st              gr0,@(gr30,gr0)
+       sethi.p         %hi(0xffc00100),gr30
+       setlo           %lo(0xffc00100),gr30
+       sth             gr30,@(gr30,gr0)
+       membar
+#endif
+
+       movsg           ccr,gr30                        /* save CCR */
+       movgs           gr30,scr2
+
+       # see if the cached page table mapping is appropriate
+       srlicc.p        gr31,#26,gr0,icc0
+       setlos          0x3ffc,gr30
+       srli.p          gr29,#12,gr31                   /* use EAR0[25:14] as PTE index */
+       bne             icc0,#0,__dtlb_k_PTD_miss
+
+__dtlb_k_PTD_mapped:
+       # access the PTD with EAR0[25:14]
+       # - DAMLR5 points to the virtual address of the appropriate page table
+       # - the PTD holds 4096 PTEs
+       # - the PTD must be accessed uncached
+       # - the PTE must be marked accessed if it was valid
+       #
+       and             gr31,gr30,gr31
+       movsg           damlr5,gr30
+       add             gr30,gr31,gr31
+       ldi             @(gr31,#0),gr30                 /* fetch the PTE */
+       andicc          gr30,#_PAGE_PRESENT,gr0,icc0
+       ori.p           gr30,#_PAGE_ACCESSED,gr30
+       beq             icc0,#0,__tlb_kernel_fault      /* jump if PTE invalid */
+       sti.p           gr30,@(gr31,#0)                 /* update the PTE */
+       andi            gr30,#~_PAGE_ACCESSED,gr30
+
+       # we're using DAMR1 as an extra TLB entry
+       # - punt the entry here (if valid) to the real TLB and then replace with the new PTE
+       # - need to check IAMR1 lest we cause an multiple-DAT-hit exception
+       movsg           dampr1,gr31
+       andicc          gr31,#xAMPRx_V,gr0,icc0
+       setlos.p        0xfffff000,gr31
+       beq             icc0,#0,__dtlb_k_nopunt         /* punt not required */
+
+       movsg           damlr1,gr31
+       movgs           gr31,tplr                       /* set TPLR.CXN */
+       tlbpr           gr31,gr0,#4,#0                  /* delete matches from TLB, IAMR1, DAMR1 */
+
+       movsg           dampr1,gr31
+       ori             gr31,#xAMPRx_V,gr31             /* entry was invalidated by tlbpr #4 */
+       movgs           gr31,tppr
+       movsg           damlr1,gr31                     /* set TPLR.CXN */
+       movgs           gr31,tplr
+       tlbpr           gr31,gr0,#2,#0                  /* save to the TLB */
+       movsg           tpxr,gr31                       /* check the TLB write error flag */
+       andicc.p        gr31,#TPXR_E,gr0,icc0
+       setlos          #0xfffff000,gr31
+       bne             icc0,#0,__tlb_kernel_fault
+
+__dtlb_k_nopunt:
+
+       # assemble the new TLB entry
+       and             gr29,gr31,gr29
+       movsg           cxnr,gr31
+       or              gr29,gr31,gr29
+       movgs           gr29,iamlr1                     /* xAMLR = address | context number */
+       movgs           gr30,iampr1
+       movgs           gr29,damlr1
+       movgs           gr30,dampr1
+
+       # return, restoring registers
+       movsg           scr2,gr30
+       movgs           gr30,ccr
+       sethi.p         %hi(__kernel_current_task),gr29
+       setlo           %lo(__kernel_current_task),gr29
+       ldi             @(gr29,#0),gr29
+       rett            #0
+       beq             icc0,#3,0                       /* prevent icache prefetch */
+
+       # the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more
+       # appropriate page table and map that instead
+       #   - access the PGD with EAR0[31:26]
+       #   - DAMLR3 points to the virtual address of the page directory
+       #   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables
+__dtlb_k_PTD_miss:
+       srli            gr29,#26,gr31                   /* calculate PGE offset */
+       slli            gr31,#8,gr31                    /* and clear bottom bits */
+
+       movsg           damlr3,gr30
+       ld              @(gr31,gr30),gr30               /* access the PGE */
+
+       andicc.p        gr30,#_PAGE_PRESENT,gr0,icc0
+       andicc          gr30,#xAMPRx_SS,gr0,icc1
+
+       # map this PTD instead and record coverage address
+       ori.p           gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30
+       beq             icc0,#0,__tlb_kernel_fault      /* jump if PGE not present */
+       slli.p          gr31,#18,gr31
+       bne             icc1,#0,__dtlb_k_bigpage
+       movgs           gr30,dampr5
+       movgs           gr31,scr1
+
+       # we can now resume normal service
+       setlos          0x3ffc,gr30
+       srli.p          gr29,#12,gr31                   /* use EAR0[25:14] as PTE index */
+       bra             __dtlb_k_PTD_mapped
+
+__dtlb_k_bigpage:
+       break
+       nop
+
+       .size           __entry_kernel_data_tlb_miss, .-__entry_kernel_data_tlb_miss
+
+###############################################################################
+#
+# Userspace instruction TLB miss handler (with PGE prediction)
+# On entry:
+#   GR28  - faulting address
+#   GR31  - EAR0 ^ SCR0
+#   SCR0  - base of virtual range covered by cached PGE from last ITLB miss (or 0xffffffff)
+#   DAMR3 - mapped page directory
+#   DAMR4 - mapped page table as matched by SCR0
+#
+###############################################################################
+       .globl          __entry_user_insn_tlb_miss
+       .type           __entry_user_insn_tlb_miss,@function
+__entry_user_insn_tlb_miss:
+#if 0
+       sethi.p         %hi(0xe1200004),gr30
+       setlo           %lo(0xe1200004),gr30
+       st              gr0,@(gr30,gr0)
+       sethi.p         %hi(0xffc00100),gr30
+       setlo           %lo(0xffc00100),gr30
+       sth             gr30,@(gr30,gr0)
+       membar
+#endif
+
+       movsg           ccr,gr30                        /* save CCR */
+       movgs           gr30,scr2
+
+       # see if the cached page table mapping is appropriate
+       srlicc.p        gr31,#26,gr0,icc0
+       setlos          0x3ffc,gr30
+       srli.p          gr28,#12,gr31                   /* use EAR0[25:14] as PTE index */
+       bne             icc0,#0,__itlb_u_PTD_miss
+
+__itlb_u_PTD_mapped:
+       # access the PTD with EAR0[25:14]
+       # - DAMLR4 points to the virtual address of the appropriate page table
+       # - the PTD holds 4096 PTEs
+       # - the PTD must be accessed uncached
+       # - the PTE must be marked accessed if it was valid
+       #
+       and             gr31,gr30,gr31
+       movsg           damlr4,gr30
+       add             gr30,gr31,gr31
+       ldi             @(gr31,#0),gr30                 /* fetch the PTE */
+       andicc          gr30,#_PAGE_PRESENT,gr0,icc0
+       ori.p           gr30,#_PAGE_ACCESSED,gr30
+       beq             icc0,#0,__tlb_user_fault        /* jump if PTE invalid */
+       sti.p           gr30,@(gr31,#0)                 /* update the PTE */
+       andi            gr30,#~_PAGE_ACCESSED,gr30
+
+       # we're using IAMR1/DAMR1 as an extra TLB entry
+       # - punt the entry here (if valid) to the real TLB and then replace with the new PTE
+       movsg           dampr1,gr31
+       andicc          gr31,#xAMPRx_V,gr0,icc0
+       setlos.p        0xfffff000,gr31
+       beq             icc0,#0,__itlb_u_nopunt         /* punt not required */
+
+       movsg           dampr1,gr31
+       movgs           gr31,tppr
+       movsg           damlr1,gr31                     /* set TPLR.CXN */
+       movgs           gr31,tplr
+       tlbpr           gr31,gr0,#2,#0                  /* save to the TLB */
+       movsg           tpxr,gr31                       /* check the TLB write error flag */
+       andicc.p        gr31,#TPXR_E,gr0,icc0
+       setlos          #0xfffff000,gr31
+       bne             icc0,#0,__tlb_user_fault
+
+__itlb_u_nopunt:
+
+       # assemble the new TLB entry
+       and             gr28,gr31,gr28
+       movsg           cxnr,gr31
+       or              gr28,gr31,gr28
+       movgs           gr28,iamlr1                     /* xAMLR = address | context number */
+       movgs           gr30,iampr1
+       movgs           gr28,damlr1
+       movgs           gr30,dampr1
+
+       # return, restoring registers
+       movsg           scr2,gr30
+       movgs           gr30,ccr
+       rett            #0
+       beq             icc0,#3,0                       /* prevent icache prefetch */
+
+       # the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more
+       # appropriate page table and map that instead
+       #   - access the PGD with EAR0[31:26]
+       #   - DAMLR3 points to the virtual address of the page directory
+       #   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables
+__itlb_u_PTD_miss:
+       srli            gr28,#26,gr31                   /* calculate PGE offset */
+       slli            gr31,#8,gr31                    /* and clear bottom bits */
+
+       movsg           damlr3,gr30
+       ld              @(gr31,gr30),gr30               /* access the PGE */
+
+       andicc.p        gr30,#_PAGE_PRESENT,gr0,icc0
+       andicc          gr30,#xAMPRx_SS,gr0,icc1
+
+       # map this PTD instead and record coverage address
+       ori.p           gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30
+       beq             icc0,#0,__tlb_user_fault        /* jump if PGE not present */
+       slli.p          gr31,#18,gr31
+       bne             icc1,#0,__itlb_u_bigpage
+       movgs           gr30,dampr4
+       movgs           gr31,scr0
+
+       # we can now resume normal service
+       setlos          0x3ffc,gr30
+       srli.p          gr28,#12,gr31                   /* use EAR0[25:14] as PTE index */
+       bra             __itlb_u_PTD_mapped
+
+__itlb_u_bigpage:
+       break
+       nop
+
+       .size           __entry_user_insn_tlb_miss, .-__entry_user_insn_tlb_miss
+
+###############################################################################
+#
+# Userspace data TLB miss handler
+# On entry:
+#   GR28  - faulting address
+#   GR31  - EAR0 ^ SCR1
+#   SCR1  - base of virtual range covered by cached PGE from last DTLB miss (or 0xffffffff)
+#   DAMR3 - mapped page directory
+#   DAMR5 - mapped page table as matched by SCR1
+#
+###############################################################################
+       .globl          __entry_user_data_tlb_miss
+       .type           __entry_user_data_tlb_miss,@function
+__entry_user_data_tlb_miss:
+#if 0
+       sethi.p         %hi(0xe1200004),gr30
+       setlo           %lo(0xe1200004),gr30
+       st              gr0,@(gr30,gr0)
+       sethi.p         %hi(0xffc00100),gr30
+       setlo           %lo(0xffc00100),gr30
+       sth             gr30,@(gr30,gr0)
+       membar
+#endif
+
+       movsg           ccr,gr30                        /* save CCR */
+       movgs           gr30,scr2
+
+       # see if the cached page table mapping is appropriate
+       srlicc.p        gr31,#26,gr0,icc0
+       setlos          0x3ffc,gr30
+       srli.p          gr28,#12,gr31                   /* use EAR0[25:14] as PTE index */
+       bne             icc0,#0,__dtlb_u_PTD_miss
+
+__dtlb_u_PTD_mapped:
+       # access the PTD with EAR0[25:14]
+       # - DAMLR5 points to the virtual address of the appropriate page table
+       # - the PTD holds 4096 PTEs
+       # - the PTD must be accessed uncached
+       # - the PTE must be marked accessed if it was valid
+       #
+       and             gr31,gr30,gr31
+       movsg           damlr5,gr30
+
+__dtlb_u_using_iPTD:
+       add             gr30,gr31,gr31
+       ldi             @(gr31,#0),gr30                 /* fetch the PTE */
+       andicc          gr30,#_PAGE_PRESENT,gr0,icc0
+       ori.p           gr30,#_PAGE_ACCESSED,gr30
+       beq             icc0,#0,__tlb_user_fault        /* jump if PTE invalid */
+       sti.p           gr30,@(gr31,#0)                 /* update the PTE */
+       andi            gr30,#~_PAGE_ACCESSED,gr30
+
+       # we're using DAMR1 as an extra TLB entry
+       # - punt the entry here (if valid) to the real TLB and then replace with the new PTE
+       movsg           dampr1,gr31
+       andicc          gr31,#xAMPRx_V,gr0,icc0
+       setlos.p        0xfffff000,gr31
+       beq             icc0,#0,__dtlb_u_nopunt         /* punt not required */
+
+       movsg           dampr1,gr31
+       movgs           gr31,tppr
+       movsg           damlr1,gr31                     /* set TPLR.CXN */
+       movgs           gr31,tplr
+       tlbpr           gr31,gr0,#2,#0                  /* save to the TLB */
+       movsg           tpxr,gr31                       /* check the TLB write error flag */
+       andicc.p        gr31,#TPXR_E,gr0,icc0
+       setlos          #0xfffff000,gr31
+       bne             icc0,#0,__tlb_user_fault
+
+__dtlb_u_nopunt:
+
+       # assemble the new TLB entry
+       and             gr28,gr31,gr28
+       movsg           cxnr,gr31
+       or              gr28,gr31,gr28
+       movgs           gr28,iamlr1                     /* xAMLR = address | context number */
+       movgs           gr30,iampr1
+       movgs           gr28,damlr1
+       movgs           gr30,dampr1
+
+       # return, restoring registers
+       movsg           scr2,gr30
+       movgs           gr30,ccr
+       rett            #0
+       beq             icc0,#3,0                       /* prevent icache prefetch */
+
+       # the PTE we want wasn't in the PTD we have mapped, so we need to go looking for a more
+       # appropriate page table and map that instead
+       #   - first of all, check the insn PGE cache - we may well get a hit there
+       #   - access the PGD with EAR0[31:26]
+       #   - DAMLR3 points to the virtual address of the page directory
+       #   - the PGD holds 64 PGEs and each PGE/PME points to a set of page tables
+__dtlb_u_PTD_miss:
+       movsg           scr0,gr31                       /* consult the insn-PGE-cache key */
+       xor             gr28,gr31,gr31
+       srlicc          gr31,#26,gr0,icc0
+       srli            gr28,#12,gr31                   /* use EAR0[25:14] as PTE index */
+       bne             icc0,#0,__dtlb_u_iPGE_miss
+
+       # what we're looking for is covered by the insn-PGE-cache
+       setlos          0x3ffc,gr30
+       and             gr31,gr30,gr31
+       movsg           damlr4,gr30
+       bra             __dtlb_u_using_iPTD
+
+__dtlb_u_iPGE_miss:
+       srli            gr28,#26,gr31                   /* calculate PGE offset */
+       slli            gr31,#8,gr31                    /* and clear bottom bits */
+
+       movsg           damlr3,gr30
+       ld              @(gr31,gr30),gr30               /* access the PGE */
+
+       andicc.p        gr30,#_PAGE_PRESENT,gr0,icc0
+       andicc          gr30,#xAMPRx_SS,gr0,icc1
+
+       # map this PTD instead and record coverage address
+       ori.p           gr30,#xAMPRx_L|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr30
+       beq             icc0,#0,__tlb_user_fault        /* jump if PGE not present */
+       slli.p          gr31,#18,gr31
+       bne             icc1,#0,__dtlb_u_bigpage
+       movgs           gr30,dampr5
+       movgs           gr31,scr1
+
+       # we can now resume normal service
+       setlos          0x3ffc,gr30
+       srli.p          gr28,#12,gr31                   /* use EAR0[25:14] as PTE index */
+       bra             __dtlb_u_PTD_mapped
+
+__dtlb_u_bigpage:
+       break
+       nop
+
+       .size           __entry_user_data_tlb_miss, .-__entry_user_data_tlb_miss
diff --git a/arch/frv/mm/unaligned.c b/arch/frv/mm/unaligned.c
new file mode 100644 (file)
index 0000000..09b3614
--- /dev/null
@@ -0,0 +1,218 @@
+/* unaligned.c: unalignment fixup handler for CPUs on which it is supported (FR451 only)
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+#include <linux/user.h>
+#include <linux/string.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#if 0
+#define kdebug(fmt, ...) printk("FDPIC "fmt"\n" ,##__VA_ARGS__ )
+#else
+#define kdebug(fmt, ...) do {} while(0)
+#endif
+
+#define _MA_SIGNED     0x01
+#define _MA_HALF       0x02
+#define _MA_WORD       0x04
+#define _MA_DWORD      0x08
+#define _MA_SZ_MASK    0x0e
+#define _MA_LOAD       0x10
+#define _MA_STORE      0x20
+#define _MA_UPDATE     0x40
+#define _MA_IMM                0x80
+
+#define _MA_LDxU       _MA_LOAD | _MA_UPDATE
+#define _MA_LDxI       _MA_LOAD | _MA_IMM
+#define _MA_STxU       _MA_STORE | _MA_UPDATE
+#define _MA_STxI       _MA_STORE | _MA_IMM
+
+static const uint8_t tbl_LDGRk_reg[0x40] = {
+       [0x02] = _MA_LOAD | _MA_HALF | _MA_SIGNED,      /* LDSH  @(GRi,GRj),GRk */
+       [0x03] = _MA_LOAD | _MA_HALF,                   /* LDUH  @(GRi,GRj),GRk */
+       [0x04] = _MA_LOAD | _MA_WORD,                   /* LD    @(GRi,GRj),GRk */
+       [0x05] = _MA_LOAD | _MA_DWORD,                  /* LDD   @(GRi,GRj),GRk */
+       [0x12] = _MA_LDxU | _MA_HALF | _MA_SIGNED,      /* LDSHU @(GRi,GRj),GRk */
+       [0x13] = _MA_LDxU | _MA_HALF,                   /* LDUHU @(GRi,GRj),GRk */
+       [0x14] = _MA_LDxU | _MA_WORD,                   /* LDU   @(GRi,GRj),GRk */
+       [0x15] = _MA_LDxU | _MA_DWORD,                  /* LDDU  @(GRi,GRj),GRk */
+};
+
+static const uint8_t tbl_STGRk_reg[0x40] = {
+       [0x01] = _MA_STORE | _MA_HALF,                  /* STH   @(GRi,GRj),GRk */
+       [0x02] = _MA_STORE | _MA_WORD,                  /* ST    @(GRi,GRj),GRk */
+       [0x03] = _MA_STORE | _MA_DWORD,                 /* STD   @(GRi,GRj),GRk */
+       [0x11] = _MA_STxU  | _MA_HALF,                  /* STHU  @(GRi,GRj),GRk */
+       [0x12] = _MA_STxU  | _MA_WORD,                  /* STU   @(GRi,GRj),GRk */
+       [0x13] = _MA_STxU  | _MA_DWORD,                 /* STDU  @(GRi,GRj),GRk */
+};
+
+static const uint8_t tbl_LDSTGRk_imm[0x80] = {
+       [0x31] = _MA_LDxI | _MA_HALF | _MA_SIGNED,      /* LDSHI @(GRi,d12),GRk */
+       [0x32] = _MA_LDxI | _MA_WORD,                   /* LDI   @(GRi,d12),GRk */
+       [0x33] = _MA_LDxI | _MA_DWORD,                  /* LDDI  @(GRi,d12),GRk */
+       [0x36] = _MA_LDxI | _MA_HALF,                   /* LDUHI @(GRi,d12),GRk */
+       [0x51] = _MA_STxI | _MA_HALF,                   /* STHI  @(GRi,d12),GRk */
+       [0x52] = _MA_STxI | _MA_WORD,                   /* STI   @(GRi,d12),GRk */
+       [0x53] = _MA_STxI | _MA_DWORD,                  /* STDI  @(GRi,d12),GRk */
+};
+
+
+/*****************************************************************************/
+/*
+ * see if we can handle the exception by fixing up a misaligned memory access
+ */
+int handle_misalignment(unsigned long esr0, unsigned long ear0, unsigned long epcr0)
+{
+       unsigned long insn, addr, *greg;
+       int GRi, GRj, GRk, D12, op;
+
+       union {
+               uint64_t _64;
+               uint32_t _32[2];
+               uint16_t _16;
+               uint8_t _8[8];
+       } x;
+
+       if (!(esr0 & ESR0_EAV) || !(epcr0 & EPCR0_V) || !(ear0 & 7))
+               return -EAGAIN;
+
+       epcr0 &= EPCR0_PC;
+
+       if (__frame->pc != epcr0) {
+               kdebug("MISALIGN: Execution not halted on excepting instruction\n");
+               BUG();
+       }
+
+       if (__get_user(insn, (unsigned long *) epcr0) < 0)
+               return -EFAULT;
+
+       /* determine the instruction type first */
+       switch ((insn >> 18) & 0x7f) {
+       case 0x2:
+               /* LDx @(GRi,GRj),GRk */
+               op = tbl_LDGRk_reg[(insn >> 6) & 0x3f];
+               break;
+
+       case 0x3:
+               /* STx GRk,@(GRi,GRj) */
+               op = tbl_STGRk_reg[(insn >> 6) & 0x3f];
+               break;
+
+       default:
+               op = tbl_LDSTGRk_imm[(insn >> 18) & 0x7f];
+               break;
+       }
+
+       if (!op)
+               return -EAGAIN;
+
+       kdebug("MISALIGN: pc=%08lx insn=%08lx ad=%08lx op=%02x\n", epcr0, insn, ear0, op);
+
+       memset(&x, 0xba, 8);
+
+       /* validate the instruction parameters */
+       greg = (unsigned long *) &__frame->tbr;
+
+       GRi = (insn >> 12) & 0x3f;
+       GRk = (insn >> 25) & 0x3f;
+
+       if (GRi > 31 || GRk > 31)
+               return -ENOENT;
+
+       if (op & _MA_DWORD && GRk & 1)
+               return -EINVAL;
+
+       if (op & _MA_IMM) {
+               D12 = insn & 0xfff;
+               asm ("slli %0,#20,%0 ! srai %0,#20,%0" : "=r"(D12) : "0"(D12)); /* sign extend */
+               addr = (GRi ? greg[GRi] : 0) + D12;
+       }
+       else {
+               GRj = (insn >>  0) & 0x3f;
+               if (GRj > 31)
+                       return -ENOENT;
+               addr = (GRi ? greg[GRi] : 0) + (GRj ? greg[GRj] : 0);
+       }
+
+       if (addr != ear0) {
+               kdebug("MISALIGN: Calculated addr (%08lx) does not match EAR0 (%08lx)\n",
+                      addr, ear0);
+               return -EFAULT;
+       }
+
+       /* check the address is okay */
+       if (user_mode(__frame) && ___range_ok(ear0, 8) < 0)
+               return -EFAULT;
+
+       /* perform the memory op */
+       if (op & _MA_STORE) {
+               /* perform a store */
+               x._32[0] = 0;
+               if (GRk != 0) {
+                       if (op & _MA_HALF) {
+                               x._16 = greg[GRk];
+                       }
+                       else {
+                               x._32[0] = greg[GRk];
+                       }
+               }
+               if (op & _MA_DWORD)
+                       x._32[1] = greg[GRk + 1];
+
+               kdebug("MISALIGN: Store GR%d { %08x:%08x } -> %08lx (%dB)\n",
+                      GRk, x._32[1], x._32[0], addr, op & _MA_SZ_MASK);
+
+               if (__memcpy_user((void *) addr, &x, op & _MA_SZ_MASK) != 0)
+                       return -EFAULT;
+       }
+       else {
+               /* perform a load */
+               if (__memcpy_user(&x, (void *) addr, op & _MA_SZ_MASK) != 0)
+                       return -EFAULT;
+
+               if (op & _MA_HALF) {
+                       if (op & _MA_SIGNED)
+                               asm ("slli %0,#16,%0 ! srai %0,#16,%0"
+                                    : "=r"(x._32[0]) : "0"(x._16));
+                       else
+                               asm ("sethi #0,%0"
+                                    : "=r"(x._32[0]) : "0"(x._16));
+               }
+
+               kdebug("MISALIGN: Load %08lx (%dB) -> GR%d, { %08x:%08x }\n",
+                      addr, op & _MA_SZ_MASK, GRk, x._32[1], x._32[0]);
+
+               if (GRk != 0)
+                       greg[GRk] = x._32[0];
+               if (op & _MA_DWORD)
+                       greg[GRk + 1] = x._32[1];
+       }
+
+       /* update the base pointer if required */
+       if (op & _MA_UPDATE)
+               greg[GRi] = addr;
+
+       /* well... we've done that insn */
+       __frame->pc = __frame->pc + 4;
+
+       return 0;
+} /* end handle_misalignment() */
diff --git a/arch/i386/kernel/acpi/earlyquirk.c b/arch/i386/kernel/acpi/earlyquirk.c
new file mode 100644 (file)
index 0000000..726a5ca
--- /dev/null
@@ -0,0 +1,51 @@
+/* 
+ * Do early PCI probing for bug detection when the main PCI subsystem is 
+ * not up yet.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <asm/pci-direct.h>
+#include <asm/acpi.h>
+
+static int __init check_bridge(int vendor, int device) 
+{
+       /* According to Nvidia all timer overrides are bogus. Just ignore
+          them all. */
+       if (vendor == PCI_VENDOR_ID_NVIDIA) { 
+               acpi_skip_timer_override = 1;           
+       }
+       return 0;
+}
+   
+void __init check_acpi_pci(void) 
+{ 
+       int num,slot,func; 
+
+       /* Assume the machine supports type 1. If not it will 
+          always read ffffffff and should not have any side effect. */
+
+       /* Poor man's PCI discovery */
+       for (num = 0; num < 32; num++) { 
+               for (slot = 0; slot < 32; slot++) { 
+                       for (func = 0; func < 8; func++) { 
+                               u32 class;
+                               u32 vendor;
+                               class = read_pci_config(num,slot,func,
+                                                       PCI_CLASS_REVISION);
+                               if (class == 0xffffffff)
+                                       break; 
+
+                               if ((class >> 16) != PCI_CLASS_BRIDGE_PCI)
+                                       continue; 
+                               
+                               vendor = read_pci_config(num, slot, func, 
+                                                        PCI_VENDOR_ID);
+                               
+                               if (check_bridge(vendor&0xffff, vendor >> 16))
+                                       return; 
+                       } 
+                       
+               }
+       }
+}
diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c
new file mode 100644 (file)
index 0000000..1e11401
--- /dev/null
@@ -0,0 +1,509 @@
+/*
+ * acpi-cpufreq.c - ACPI Processor P-States Driver ($Revision: 1.3 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *  Copyright (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/uaccess.h>
+
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg)
+
+MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski");
+MODULE_DESCRIPTION("ACPI Processor P-States Driver");
+MODULE_LICENSE("GPL");
+
+
+struct cpufreq_acpi_io {
+       struct acpi_processor_performance       acpi_data;
+       struct cpufreq_frequency_table          *freq_table;
+};
+
+static struct cpufreq_acpi_io  *acpi_io_data[NR_CPUS];
+
+
+static int
+acpi_processor_write_port(
+       u16     port,
+       u8      bit_width,
+       u32     value)
+{
+       if (bit_width <= 8) {
+               outb(value, port);
+       } else if (bit_width <= 16) {
+               outw(value, port);
+       } else if (bit_width <= 32) {
+               outl(value, port);
+       } else {
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static int
+acpi_processor_read_port(
+       u16     port,
+       u8      bit_width,
+       u32     *ret)
+{
+       *ret = 0;
+       if (bit_width <= 8) {
+               *ret = inb(port);
+       } else if (bit_width <= 16) {
+               *ret = inw(port);
+       } else if (bit_width <= 32) {
+               *ret = inl(port);
+       } else {
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static int
+acpi_processor_set_performance (
+       struct cpufreq_acpi_io  *data,
+       unsigned int            cpu,
+       int                     state)
+{
+       u16                     port = 0;
+       u8                      bit_width = 0;
+       int                     ret = 0;
+       u32                     value = 0;
+       int                     i = 0;
+       struct cpufreq_freqs    cpufreq_freqs;
+       cpumask_t               saved_mask;
+       int                     retval;
+
+       dprintk("acpi_processor_set_performance\n");
+
+       /*
+        * TBD: Use something other than set_cpus_allowed.
+        * As set_cpus_allowed is a bit racy, 
+        * with any other set_cpus_allowed for this process.
+        */
+       saved_mask = current->cpus_allowed;
+       set_cpus_allowed(current, cpumask_of_cpu(cpu));
+       if (smp_processor_id() != cpu) {
+               return (-EAGAIN);
+       }
+       
+       if (state == data->acpi_data.state) {
+               dprintk("Already at target state (P%d)\n", state);
+               retval = 0;
+               goto migrate_end;
+       }
+
+       dprintk("Transitioning from P%d to P%d\n",
+               data->acpi_data.state, state);
+
+       /* cpufreq frequency struct */
+       cpufreq_freqs.cpu = cpu;
+       cpufreq_freqs.old = data->freq_table[data->acpi_data.state].frequency;
+       cpufreq_freqs.new = data->freq_table[state].frequency;
+
+       /* notify cpufreq */
+       cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
+
+       /*
+        * First we write the target state's 'control' value to the
+        * control_register.
+        */
+
+       port = data->acpi_data.control_register.address;
+       bit_width = data->acpi_data.control_register.bit_width;
+       value = (u32) data->acpi_data.states[state].control;
+
+       dprintk("Writing 0x%08x to port 0x%04x\n", value, port);
+
+       ret = acpi_processor_write_port(port, bit_width, value);
+       if (ret) {
+               dprintk("Invalid port width 0x%04x\n", bit_width);
+               retval = ret;
+               goto migrate_end;
+       }
+
+       /*
+        * Then we read the 'status_register' and compare the value with the
+        * target state's 'status' to make sure the transition was successful.
+        * Note that we'll poll for up to 1ms (100 cycles of 10us) before
+        * giving up.
+        */
+
+       port = data->acpi_data.status_register.address;
+       bit_width = data->acpi_data.status_register.bit_width;
+
+       dprintk("Looking for 0x%08x from port 0x%04x\n",
+               (u32) data->acpi_data.states[state].status, port);
+
+       for (i=0; i<100; i++) {
+               ret = acpi_processor_read_port(port, bit_width, &value);
+               if (ret) {      
+                       dprintk("Invalid port width 0x%04x\n", bit_width);
+                       retval = ret;
+                       goto migrate_end;
+               }
+               if (value == (u32) data->acpi_data.states[state].status)
+                       break;
+               udelay(10);
+       }
+
+       /* notify cpufreq */
+       cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
+
+       if (value != (u32) data->acpi_data.states[state].status) {
+               unsigned int tmp = cpufreq_freqs.new;
+               cpufreq_freqs.new = cpufreq_freqs.old;
+               cpufreq_freqs.old = tmp;
+               cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
+               cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
+               printk(KERN_WARNING "acpi-cpufreq: Transition failed\n");
+               retval = -ENODEV;
+               goto migrate_end;
+       }
+
+       dprintk("Transition successful after %d microseconds\n", i * 10);
+
+       data->acpi_data.state = state;
+
+       retval = 0;
+migrate_end:
+       set_cpus_allowed(current, saved_mask);
+       return (retval);
+}
+
+
+static int
+acpi_cpufreq_target (
+       struct cpufreq_policy   *policy,
+       unsigned int target_freq,
+       unsigned int relation)
+{
+       struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
+       unsigned int next_state = 0;
+       unsigned int result = 0;
+
+       dprintk("acpi_cpufreq_setpolicy\n");
+
+       result = cpufreq_frequency_table_target(policy,
+                       data->freq_table,
+                       target_freq,
+                       relation,
+                       &next_state);
+       if (result)
+               return (result);
+
+       result = acpi_processor_set_performance (data, policy->cpu, next_state);
+
+       return (result);
+}
+
+
+static int
+acpi_cpufreq_verify (
+       struct cpufreq_policy   *policy)
+{
+       unsigned int result = 0;
+       struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
+
+       dprintk("acpi_cpufreq_verify\n");
+
+       result = cpufreq_frequency_table_verify(policy, 
+                       data->freq_table);
+
+       return (result);
+}
+
+
+static unsigned long
+acpi_cpufreq_guess_freq (
+       struct cpufreq_acpi_io  *data,
+       unsigned int            cpu)
+{
+       if (cpu_khz) {
+               /* search the closest match to cpu_khz */
+               unsigned int i;
+               unsigned long freq;
+               unsigned long freqn = data->acpi_data.states[0].core_frequency * 1000;
+
+               for (i=0; i < (data->acpi_data.state_count - 1); i++) {
+                       freq = freqn;
+                       freqn = data->acpi_data.states[i+1].core_frequency * 1000;
+                       if ((2 * cpu_khz) > (freqn + freq)) {
+                               data->acpi_data.state = i;
+                               return (freq);
+                       }
+               }
+               data->acpi_data.state = data->acpi_data.state_count - 1;
+               return (freqn);
+       } else
+               /* assume CPU is at P0... */
+               data->acpi_data.state = 0;
+               return data->acpi_data.states[0].core_frequency * 1000;
+       
+}
+
+
+/* 
+ * acpi_processor_cpu_init_pdc_est - let BIOS know about the SMP capabilities
+ * of this driver
+ * @perf: processor-specific acpi_io_data struct
+ * @cpu: CPU being initialized
+ *
+ * To avoid issues with legacy OSes, some BIOSes require to be informed of
+ * the SMP capabilities of OS P-state driver. Here we set the bits in _PDC 
+ * accordingly, for Enhanced Speedstep. Actual call to _PDC is done in
+ * driver/acpi/processor.c
+ */
+static void 
+acpi_processor_cpu_init_pdc_est(
+               struct acpi_processor_performance *perf, 
+               unsigned int cpu,
+               struct acpi_object_list *obj_list
+               )
+{
+       union acpi_object *obj;
+       u32 *buf;
+       struct cpuinfo_x86 *c = cpu_data + cpu;
+       dprintk("acpi_processor_cpu_init_pdc_est\n");
+
+       if (!cpu_has(c, X86_FEATURE_EST))
+               return;
+
+       /* Initialize pdc. It will be used later. */
+       if (!obj_list)
+               return;
+               
+       if (!(obj_list->count && obj_list->pointer))
+               return;
+
+       obj = obj_list->pointer;
+       if ((obj->buffer.length == 12) && obj->buffer.pointer) {
+               buf = (u32 *)obj->buffer.pointer;
+                       buf[0] = ACPI_PDC_REVISION_ID;
+                       buf[1] = 1;
+                       buf[2] = ACPI_PDC_EST_CAPABILITY_SMP;
+               perf->pdc = obj_list;
+       }
+       return;
+}
+
+/* CPU specific PDC initialization */
+static void 
+acpi_processor_cpu_init_pdc(
+               struct acpi_processor_performance *perf, 
+               unsigned int cpu,
+               struct acpi_object_list *obj_list
+               )
+{
+       struct cpuinfo_x86 *c = cpu_data + cpu;
+       dprintk("acpi_processor_cpu_init_pdc\n");
+       perf->pdc = NULL;
+       if (cpu_has(c, X86_FEATURE_EST))
+               acpi_processor_cpu_init_pdc_est(perf, cpu, obj_list);
+       return;
+}
+
+
+static int
+acpi_cpufreq_cpu_init (
+       struct cpufreq_policy   *policy)
+{
+       unsigned int            i;
+       unsigned int            cpu = policy->cpu;
+       struct cpufreq_acpi_io  *data;
+       unsigned int            result = 0;
+
+       union acpi_object               arg0 = {ACPI_TYPE_BUFFER};
+       u32                             arg0_buf[3];
+       struct acpi_object_list         arg_list = {1, &arg0};
+
+       dprintk("acpi_cpufreq_cpu_init\n");
+       /* setup arg_list for _PDC settings */
+        arg0.buffer.length = 12;
+        arg0.buffer.pointer = (u8 *) arg0_buf;
+
+       data = kmalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL);
+       if (!data)
+               return (-ENOMEM);
+       memset(data, 0, sizeof(struct cpufreq_acpi_io));
+
+       acpi_io_data[cpu] = data;
+
+       acpi_processor_cpu_init_pdc(&data->acpi_data, cpu, &arg_list);
+       result = acpi_processor_register_performance(&data->acpi_data, cpu);
+       data->acpi_data.pdc = NULL;
+
+       if (result)
+               goto err_free;
+
+       /* capability check */
+       if (data->acpi_data.state_count <= 1) {
+               dprintk("No P-States\n");
+               result = -ENODEV;
+               goto err_unreg;
+       }
+       if ((data->acpi_data.control_register.space_id != ACPI_ADR_SPACE_SYSTEM_IO) ||
+           (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_SYSTEM_IO)) {
+               dprintk("Unsupported address space [%d, %d]\n",
+                       (u32) (data->acpi_data.control_register.space_id),
+                       (u32) (data->acpi_data.status_register.space_id));
+               result = -ENODEV;
+               goto err_unreg;
+       }
+
+       /* alloc freq_table */
+       data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * (data->acpi_data.state_count + 1), GFP_KERNEL);
+       if (!data->freq_table) {
+               result = -ENOMEM;
+               goto err_unreg;
+       }
+
+       /* detect transition latency */
+       policy->cpuinfo.transition_latency = 0;
+       for (i=0; i<data->acpi_data.state_count; i++) {
+               if ((data->acpi_data.states[i].transition_latency * 1000) > policy->cpuinfo.transition_latency)
+                       policy->cpuinfo.transition_latency = data->acpi_data.states[i].transition_latency * 1000;
+       }
+       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+       /* The current speed is unknown and not detectable by ACPI...  */
+       policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu);
+
+       /* table init */
+       for (i=0; i<=data->acpi_data.state_count; i++)
+       {
+               data->freq_table[i].index = i;
+               if (i<data->acpi_data.state_count)
+                       data->freq_table[i].frequency = data->acpi_data.states[i].core_frequency * 1000;
+               else
+                       data->freq_table[i].frequency = CPUFREQ_TABLE_END;
+       }
+
+       result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
+       if (result) {
+               goto err_freqfree;
+       }
+
+       /* notify BIOS that we exist */
+       acpi_processor_notify_smm(THIS_MODULE);
+
+       printk(KERN_INFO "acpi-cpufreq: CPU%u - ACPI performance management activated.\n",
+              cpu);
+       for (i = 0; i < data->acpi_data.state_count; i++)
+               dprintk("     %cP%d: %d MHz, %d mW, %d uS\n",
+                       (i == data->acpi_data.state?'*':' '), i,
+                       (u32) data->acpi_data.states[i].core_frequency,
+                       (u32) data->acpi_data.states[i].power,
+                       (u32) data->acpi_data.states[i].transition_latency);
+
+       cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu);
+       return (result);
+
+ err_freqfree:
+       kfree(data->freq_table);
+ err_unreg:
+       acpi_processor_unregister_performance(&data->acpi_data, cpu);
+ err_free:
+       kfree(data);
+       acpi_io_data[cpu] = NULL;
+
+       return (result);
+}
+
+
+static int
+acpi_cpufreq_cpu_exit (
+       struct cpufreq_policy   *policy)
+{
+       struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
+
+
+       dprintk("acpi_cpufreq_cpu_exit\n");
+
+       if (data) {
+               cpufreq_frequency_table_put_attr(policy->cpu);
+               acpi_io_data[policy->cpu] = NULL;
+               acpi_processor_unregister_performance(&data->acpi_data, policy->cpu);
+               kfree(data);
+       }
+
+       return (0);
+}
+
+
+static struct freq_attr* acpi_cpufreq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
+static struct cpufreq_driver acpi_cpufreq_driver = {
+       .verify         = acpi_cpufreq_verify,
+       .target         = acpi_cpufreq_target,
+       .init           = acpi_cpufreq_cpu_init,
+       .exit           = acpi_cpufreq_cpu_exit,
+       .name           = "acpi-cpufreq",
+       .owner          = THIS_MODULE,
+       .attr           = acpi_cpufreq_attr,
+};
+
+
+static int __init
+acpi_cpufreq_init (void)
+{
+       int                     result = 0;
+
+       dprintk("acpi_cpufreq_init\n");
+
+       result = cpufreq_register_driver(&acpi_cpufreq_driver);
+       
+       return (result);
+}
+
+
+static void __exit
+acpi_cpufreq_exit (void)
+{
+       dprintk("acpi_cpufreq_exit\n");
+
+       cpufreq_unregister_driver(&acpi_cpufreq_driver);
+
+       return;
+}
+
+
+late_initcall(acpi_cpufreq_init);
+module_exit(acpi_cpufreq_exit);
+
+MODULE_ALIAS("acpi");
diff --git a/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c b/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c
new file mode 100644 (file)
index 0000000..e654e7b
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * (C) 2004  Sebastian Witt <se.witt@gmx.net>
+ *
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *  Based upon reverse engineered information
+ *
+ *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#define NFORCE2_XTAL 25
+#define NFORCE2_BOOTFSB 0x48
+#define NFORCE2_PLLENABLE 0xa8
+#define NFORCE2_PLLREG 0xa4
+#define NFORCE2_PLLADR 0xa0
+#define NFORCE2_PLL(mul, div) (0x100000 | (mul << 8) | div)
+
+#define NFORCE2_MIN_FSB 50
+#define NFORCE2_SAFE_DISTANCE 50
+
+/* Delay in ms between FSB changes */
+//#define NFORCE2_DELAY 10
+
+/* nforce2_chipset:
+ * FSB is changed using the chipset
+ */
+static struct pci_dev *nforce2_chipset_dev;
+
+/* fid:
+ * multiplier * 10
+ */
+static int fid = 0;
+
+/* min_fsb, max_fsb:
+ * minimum and maximum FSB (= FSB at boot time) 
+ */
+static int min_fsb = 0;
+static int max_fsb = 0;
+
+MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
+MODULE_DESCRIPTION("nForce2 FSB changing cpufreq driver");
+MODULE_LICENSE("GPL");
+
+module_param(fid, int, 0444);
+module_param(min_fsb, int, 0444);
+
+MODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)");
+MODULE_PARM_DESC(min_fsb,
+                 "Minimum FSB to use, if not defined: current FSB - 50");
+
+/* DEBUG
+ *   Define it if you want verbose debug output, e.g. for bug reporting
+ */
+//#define NFORCE2_DEBUG
+
+#ifdef NFORCE2_DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+/*
+ * nforce2_calc_fsb - calculate FSB
+ * @pll: PLL value
+ * 
+ *   Calculates FSB from PLL value
+ */
+static int nforce2_calc_fsb(int pll)
+{
+       unsigned char mul, div;
+
+       mul = (pll >> 8) & 0xff;
+       div = pll & 0xff;
+
+       if (div > 0)
+               return NFORCE2_XTAL * mul / div;
+
+       return 0;
+}
+
+/*
+ * nforce2_calc_pll - calculate PLL value
+ * @fsb: FSB
+ * 
+ *   Calculate PLL value for given FSB
+ */
+static int nforce2_calc_pll(unsigned int fsb)
+{
+       unsigned char xmul, xdiv;
+       unsigned char mul = 0, div = 0;
+       int tried = 0;
+
+       /* Try to calculate multiplier and divider up to 4 times */
+       while (((mul == 0) || (div == 0)) && (tried <= 3)) {
+               for (xdiv = 1; xdiv <= 0x80; xdiv++)
+                       for (xmul = 1; xmul <= 0xfe; xmul++)
+                               if (nforce2_calc_fsb(NFORCE2_PLL(xmul, xdiv)) ==
+                                   fsb + tried) {
+                                       mul = xmul;
+                                       div = xdiv;
+                               }
+               tried++;
+       }
+
+       if ((mul == 0) || (div == 0))
+               return -1;
+
+       return NFORCE2_PLL(mul, div);
+}
+
+/*
+ * nforce2_write_pll - write PLL value to chipset
+ * @pll: PLL value
+ * 
+ *   Writes new FSB PLL value to chipset
+ */
+static void nforce2_write_pll(int pll)
+{
+       int temp;
+
+       /* Set the pll addr. to 0x00 */
+       temp = 0x00;
+       pci_write_config_dword(nforce2_chipset_dev, NFORCE2_PLLADR, temp);
+
+       /* Now write the value in all 64 registers */
+       for (temp = 0; temp <= 0x3f; temp++) {
+               pci_write_config_dword(nforce2_chipset_dev, 
+                                       NFORCE2_PLLREG, pll);
+       }
+
+       return;
+}
+
+/*
+ * nforce2_fsb_read - Read FSB
+ *
+ *   Read FSB from chipset
+ *   If bootfsb != 0, return FSB at boot-time
+ */
+static unsigned int nforce2_fsb_read(int bootfsb)
+{
+       struct pci_dev *nforce2_sub5;
+       u32 fsb, temp = 0;
+
+       
+       /* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */
+       nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
+                                      0x01EF,
+                                      PCI_ANY_ID,
+                                      PCI_ANY_ID,
+                                      NULL);
+       
+       if (!nforce2_sub5)
+               return 0;
+
+       pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb);
+       fsb /= 1000000;
+       
+       /* Check if PLL register is already set */
+       pci_read_config_byte(nforce2_chipset_dev, 
+                             NFORCE2_PLLENABLE, (u8 *)&temp);
+       
+       if(bootfsb || !temp)
+               return fsb;
+               
+       /* Use PLL register FSB value */
+       pci_read_config_dword(nforce2_chipset_dev, 
+                              NFORCE2_PLLREG, &temp);
+       fsb = nforce2_calc_fsb(temp);
+
+       return fsb;
+}
+
+/*
+ * nforce2_set_fsb - set new FSB
+ * @fsb: New FSB
+ * 
+ *   Sets new FSB
+ */
+static int nforce2_set_fsb(unsigned int fsb)
+{
+       u32 pll, temp = 0;
+       unsigned int tfsb;
+       int diff;
+
+       if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) {
+               printk(KERN_ERR "cpufreq: FSB %d is out of range!\n", fsb);
+               return -EINVAL;
+       }
+       
+       tfsb = nforce2_fsb_read(0);
+       if (!tfsb) {
+               printk(KERN_ERR "cpufreq: Error while reading the FSB\n");
+               return -EINVAL;
+       }
+
+       /* First write? Then set actual value */
+       pci_read_config_byte(nforce2_chipset_dev, 
+                             NFORCE2_PLLENABLE, (u8 *)&temp);
+       if (!temp) {
+               pll = nforce2_calc_pll(tfsb);
+
+               if (pll < 0)
+                       return -EINVAL;
+
+               nforce2_write_pll(pll);
+       }
+
+       /* Enable write access */
+       temp = 0x01;
+       pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLENABLE, (u8)temp);
+
+       diff = tfsb - fsb;
+
+       if (!diff)
+               return 0;
+
+       while ((tfsb != fsb) && (tfsb <= max_fsb) && (tfsb >= min_fsb)) {
+               if (diff < 0)
+                       tfsb++;
+               else
+                       tfsb--;
+
+               /* Calculate the PLL reg. value */
+               if ((pll = nforce2_calc_pll(tfsb)) == -1)
+                       return -EINVAL;
+               
+               nforce2_write_pll(pll);
+#ifdef NFORCE2_DELAY
+               mdelay(NFORCE2_DELAY);
+#endif
+       }
+
+       temp = 0x40;
+       pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLADR, (u8)temp);
+
+       return 0;
+}
+
+/**
+ * nforce2_get - get the CPU frequency
+ * @cpu: CPU number
+ * 
+ * Returns the CPU frequency
+ */
+static unsigned int nforce2_get(unsigned int cpu)
+{
+       if (cpu)
+               return 0;
+       return nforce2_fsb_read(0) * fid * 100;
+}
+
+/**
+ * nforce2_target - set a new CPUFreq policy
+ * @policy: new policy
+ * @target_freq: the target frequency
+ * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
+ *
+ * Sets a new CPUFreq policy.
+ */
+static int nforce2_target(struct cpufreq_policy *policy,
+                         unsigned int target_freq, unsigned int relation)
+{
+//        unsigned long         flags;
+       struct cpufreq_freqs freqs;
+       unsigned int target_fsb;
+
+       if ((target_freq > policy->max) || (target_freq < policy->min))
+               return -EINVAL;
+
+       target_fsb = target_freq / (fid * 100);
+
+       freqs.old = nforce2_get(policy->cpu);
+       freqs.new = target_fsb * fid * 100;
+       freqs.cpu = 0;          /* Only one CPU on nForce2 plattforms */
+
+       if (freqs.old == freqs.new)
+               return 0;
+
+       dprintk(KERN_INFO "cpufreq: Old CPU frequency %d kHz, new %d kHz\n",
+              freqs.old, freqs.new);
+
+       cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+       /* Disable IRQs */
+       //local_irq_save(flags);
+
+       if (nforce2_set_fsb(target_fsb) < 0)
+               printk(KERN_ERR "cpufreq: Changing FSB to %d failed\n",
+                       target_fsb);
+       else
+               dprintk(KERN_INFO "cpufreq: Changed FSB successfully to %d\n",
+                       target_fsb);
+
+       /* Enable IRQs */
+       //local_irq_restore(flags);
+
+       cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+       return 0;
+}
+
+/**
+ * nforce2_verify - verifies a new CPUFreq policy
+ * @policy: new policy
+ */
+static int nforce2_verify(struct cpufreq_policy *policy)
+{
+       unsigned int fsb_pol_max;
+
+       fsb_pol_max = policy->max / (fid * 100);
+
+       if (policy->min < (fsb_pol_max * fid * 100))
+               policy->max = (fsb_pol_max + 1) * fid * 100;
+
+       cpufreq_verify_within_limits(policy,
+                                     policy->cpuinfo.min_freq,
+                                     policy->cpuinfo.max_freq);
+       return 0;
+}
+
+static int nforce2_cpu_init(struct cpufreq_policy *policy)
+{
+       unsigned int fsb;
+       unsigned int rfid;
+
+       /* capability check */
+       if (policy->cpu != 0)
+               return -ENODEV;
+
+       /* Get current FSB */
+       fsb = nforce2_fsb_read(0);
+
+       if (!fsb)
+               return -EIO;
+
+       /* FIX: Get FID from CPU */
+       if (!fid) {
+               if (!cpu_khz) {
+                       printk(KERN_WARNING
+                              "cpufreq: cpu_khz not set, can't calculate multiplier!\n");
+                       return -ENODEV;
+               }
+
+               fid = cpu_khz / (fsb * 100);
+               rfid = fid % 5;
+
+               if (rfid) {
+                       if (rfid > 2)
+                               fid += 5 - rfid;
+                       else
+                               fid -= rfid;
+               }
+       }
+
+       printk(KERN_INFO "cpufreq: FSB currently at %i MHz, FID %d.%d\n", fsb,
+              fid / 10, fid % 10);
+       
+       /* Set maximum FSB to FSB at boot time */
+       max_fsb = nforce2_fsb_read(1);
+       
+       if(!max_fsb)
+               return -EIO;
+
+       if (!min_fsb)
+               min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE;
+
+       if (min_fsb < NFORCE2_MIN_FSB)
+               min_fsb = NFORCE2_MIN_FSB;
+
+       /* cpuinfo and default policy values */
+       policy->cpuinfo.min_freq = min_fsb * fid * 100;
+       policy->cpuinfo.max_freq = max_fsb * fid * 100;
+       policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+       policy->cur = nforce2_get(policy->cpu);
+       policy->min = policy->cpuinfo.min_freq;
+       policy->max = policy->cpuinfo.max_freq;
+       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+       return 0;
+}
+
+static int nforce2_cpu_exit(struct cpufreq_policy *policy)
+{
+       return 0;
+}
+
+static struct cpufreq_driver nforce2_driver = {
+       .name = "nforce2",
+       .verify = nforce2_verify,
+       .target = nforce2_target,
+       .get = nforce2_get,
+       .init = nforce2_cpu_init,
+       .exit = nforce2_cpu_exit,
+       .owner = THIS_MODULE,
+};
+
+/**
+ * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic
+ *
+ * Detects nForce2 A2 and C1 stepping
+ * 
+ */
+static unsigned int nforce2_detect_chipset(void)
+{
+       u8 revision;
+
+       nforce2_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA,
+                                             PCI_DEVICE_ID_NVIDIA_NFORCE2,
+                                             PCI_ANY_ID,
+                                             PCI_ANY_ID,
+                                             NULL);
+
+       if (nforce2_chipset_dev == NULL)
+               return -ENODEV;
+
+       pci_read_config_byte(nforce2_chipset_dev, PCI_REVISION_ID, &revision);
+
+       printk(KERN_INFO "cpufreq: Detected nForce2 chipset revision %X\n",
+              revision);
+       printk(KERN_INFO
+              "cpufreq: FSB changing is maybe unstable and can lead to crashes and data loss.\n");
+
+       return 0;
+}
+
+/**
+ * nforce2_init - initializes the nForce2 CPUFreq driver
+ *
+ * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported
+ * devices, -EINVAL on problems during initiatization, and zero on
+ * success.
+ */
+static int __init nforce2_init(void)
+{
+       /* TODO: do we need to detect the processor? */
+
+       /* detect chipset */
+       if (nforce2_detect_chipset()) {
+               printk(KERN_ERR "cpufreq: No nForce2 chipset.\n");
+               return -ENODEV;
+       }
+
+       return cpufreq_register_driver(&nforce2_driver);
+}
+
+/**
+ * nforce2_exit - unregisters cpufreq module
+ *
+ *   Unregisters nForce2 FSB change support.
+ */
+static void __exit nforce2_exit(void)
+{
+       cpufreq_unregister_driver(&nforce2_driver);
+}
+
+module_init(nforce2_init);
+module_exit(nforce2_exit);
+
diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep-est-common.h b/arch/i386/kernel/cpu/cpufreq/speedstep-est-common.h
new file mode 100644 (file)
index 0000000..5ce995c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Routines common for drivers handling Enhanced Speedstep Technology
+ *  Copyright (C) 2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+ *
+ *  Licensed under the terms of the GNU GPL License version 2 -- see
+ *  COPYING for details.
+ */
+
+static inline int is_const_loops_cpu(unsigned int cpu)
+{
+       struct cpuinfo_x86      *c = cpu_data + cpu;
+
+       if (c->x86_vendor != X86_VENDOR_INTEL || !cpu_has(c, X86_FEATURE_EST))
+               return 0;
+
+       /*
+        * on P-4s, the TSC runs with constant frequency independent of cpu freq
+        * when we use EST
+        */
+       if (c->x86 == 0xf)
+               return 1;
+
+       return 0;
+}
+
diff --git a/arch/i386/kernel/cpu/intel_cacheinfo.c b/arch/i386/kernel/cpu/intel_cacheinfo.c
new file mode 100644 (file)
index 0000000..2bba6df
--- /dev/null
@@ -0,0 +1,140 @@
+#include <linux/init.h>
+#include <asm/processor.h>
+
+#define LVL_1_INST     1
+#define LVL_1_DATA     2
+#define LVL_2          3
+#define LVL_3          4
+#define LVL_TRACE      5
+
+struct _cache_table
+{
+       unsigned char descriptor;
+       char cache_type;
+       short size;
+};
+
+/* all the cache descriptor types we care about (no TLB or trace cache entries) */
+static struct _cache_table cache_table[] __initdata =
+{
+       { 0x06, LVL_1_INST, 8 },        /* 4-way set assoc, 32 byte line size */
+       { 0x08, LVL_1_INST, 16 },       /* 4-way set assoc, 32 byte line size */
+       { 0x0a, LVL_1_DATA, 8 },        /* 2 way set assoc, 32 byte line size */
+       { 0x0c, LVL_1_DATA, 16 },       /* 4-way set assoc, 32 byte line size */
+       { 0x22, LVL_3,      512 },      /* 4-way set assoc, sectored cache, 64 byte line size */
+       { 0x23, LVL_3,      1024 },     /* 8-way set assoc, sectored cache, 64 byte line size */
+       { 0x25, LVL_3,      2048 },     /* 8-way set assoc, sectored cache, 64 byte line size */
+       { 0x29, LVL_3,      4096 },     /* 8-way set assoc, sectored cache, 64 byte line size */
+       { 0x2c, LVL_1_DATA, 32 },       /* 8-way set assoc, 64 byte line size */
+       { 0x30, LVL_1_INST, 32 },       /* 8-way set assoc, 64 byte line size */
+       { 0x39, LVL_2,      128 },      /* 4-way set assoc, sectored cache, 64 byte line size */
+       { 0x3b, LVL_2,      128 },      /* 2-way set assoc, sectored cache, 64 byte line size */
+       { 0x3c, LVL_2,      256 },      /* 4-way set assoc, sectored cache, 64 byte line size */
+       { 0x41, LVL_2,      128 },      /* 4-way set assoc, 32 byte line size */
+       { 0x42, LVL_2,      256 },      /* 4-way set assoc, 32 byte line size */
+       { 0x43, LVL_2,      512 },      /* 4-way set assoc, 32 byte line size */
+       { 0x44, LVL_2,      1024 },     /* 4-way set assoc, 32 byte line size */
+       { 0x45, LVL_2,      2048 },     /* 4-way set assoc, 32 byte line size */
+       { 0x60, LVL_1_DATA, 16 },       /* 8-way set assoc, sectored cache, 64 byte line size */
+       { 0x66, LVL_1_DATA, 8 },        /* 4-way set assoc, sectored cache, 64 byte line size */
+       { 0x67, LVL_1_DATA, 16 },       /* 4-way set assoc, sectored cache, 64 byte line size */
+       { 0x68, LVL_1_DATA, 32 },       /* 4-way set assoc, sectored cache, 64 byte line size */
+       { 0x70, LVL_TRACE,  12 },       /* 8-way set assoc */
+       { 0x71, LVL_TRACE,  16 },       /* 8-way set assoc */
+       { 0x72, LVL_TRACE,  32 },       /* 8-way set assoc */
+       { 0x78, LVL_2,    1024 },       /* 4-way set assoc, 64 byte line size */
+       { 0x79, LVL_2,     128 },       /* 8-way set assoc, sectored cache, 64 byte line size */
+       { 0x7a, LVL_2,     256 },       /* 8-way set assoc, sectored cache, 64 byte line size */
+       { 0x7b, LVL_2,     512 },       /* 8-way set assoc, sectored cache, 64 byte line size */
+       { 0x7c, LVL_2,    1024 },       /* 8-way set assoc, sectored cache, 64 byte line size */
+       { 0x7d, LVL_2,    2048 },       /* 8-way set assoc, 64 byte line size */
+       { 0x7f, LVL_2,     512 },       /* 2-way set assoc, 64 byte line size */
+       { 0x82, LVL_2,     256 },       /* 8-way set assoc, 32 byte line size */
+       { 0x83, LVL_2,     512 },       /* 8-way set assoc, 32 byte line size */
+       { 0x84, LVL_2,    1024 },       /* 8-way set assoc, 32 byte line size */
+       { 0x85, LVL_2,    2048 },       /* 8-way set assoc, 32 byte line size */
+       { 0x86, LVL_2,     512 },       /* 4-way set assoc, 64 byte line size */
+       { 0x87, LVL_2,    1024 },       /* 8-way set assoc, 64 byte line size */
+       { 0x00, 0, 0}
+};
+
+unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c)
+{
+       unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0; /* Cache sizes */
+
+       if (c->cpuid_level > 1) {
+               /* supports eax=2  call */
+               int i, j, n;
+               int regs[4];
+               unsigned char *dp = (unsigned char *)regs;
+
+               /* Number of times to iterate */
+               n = cpuid_eax(2) & 0xFF;
+
+               for ( i = 0 ; i < n ; i++ ) {
+                       cpuid(2, &regs[0], &regs[1], &regs[2], &regs[3]);
+
+                       /* If bit 31 is set, this is an unknown format */
+                       for ( j = 0 ; j < 3 ; j++ ) {
+                               if ( regs[j] < 0 ) regs[j] = 0;
+                       }
+
+                       /* Byte 0 is level count, not a descriptor */
+                       for ( j = 1 ; j < 16 ; j++ ) {
+                               unsigned char des = dp[j];
+                               unsigned char k = 0;
+
+                               /* look up this descriptor in the table */
+                               while (cache_table[k].descriptor != 0)
+                               {
+                                       if (cache_table[k].descriptor == des) {
+                                               switch (cache_table[k].cache_type) {
+                                               case LVL_1_INST:
+                                                       l1i += cache_table[k].size;
+                                                       break;
+                                               case LVL_1_DATA:
+                                                       l1d += cache_table[k].size;
+                                                       break;
+                                               case LVL_2:
+                                                       l2 += cache_table[k].size;
+                                                       break;
+                                               case LVL_3:
+                                                       l3 += cache_table[k].size;
+                                                       break;
+                                               case LVL_TRACE:
+                                                       trace += cache_table[k].size;
+                                                       break;
+                                               }
+
+                                               break;
+                                       }
+
+                                       k++;
+                               }
+                       }
+               }
+
+               if ( trace )
+                       printk (KERN_INFO "CPU: Trace cache: %dK uops", trace);
+               else if ( l1i )
+                       printk (KERN_INFO "CPU: L1 I cache: %dK", l1i);
+               if ( l1d )
+                       printk(", L1 D cache: %dK\n", l1d);
+               else
+                       printk("\n");
+               if ( l2 )
+                       printk(KERN_INFO "CPU: L2 cache: %dK\n", l2);
+               if ( l3 )
+                       printk(KERN_INFO "CPU: L3 cache: %dK\n", l3);
+
+               /*
+                * This assumes the L3 cache is shared; it typically lives in
+                * the northbridge.  The L1 caches are included by the L2
+                * cache, and so should not be included for the purpose of
+                * SMP switching weights.
+                */
+               c->x86_cache_size = l2 ? l2 : (l1i+l1d);
+       }
+
+       return l2;
+}
diff --git a/arch/i386/kernel/crash_dump.c b/arch/i386/kernel/crash_dump.c
new file mode 100644 (file)
index 0000000..1c9bdd2
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Architecture specific (i386) functions for kexec based crash dumps.
+ *
+ * Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2004. All rights reserved.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/irq.h>
+
+#include <asm/crash_dump.h>
+#include <asm/processor.h>
+#include <asm/hardirq.h>
+#include <asm/nmi.h>
+#include <asm/hw_irq.h>
+
+struct pt_regs crash_smp_regs[NR_CPUS];
+long crash_smp_current_task[NR_CPUS];
+
+#ifdef CONFIG_SMP
+static atomic_t waiting_for_dump_ipi;
+static int crash_dump_expect_ipi[NR_CPUS];
+extern void crash_dump_send_ipi(void);
+extern void stop_this_cpu(void *);
+
+static int crash_dump_nmi_callback(struct pt_regs *regs, int cpu)
+{
+       if (!crash_dump_expect_ipi[cpu])
+               return 0;
+
+       crash_dump_expect_ipi[cpu] = 0;
+       crash_dump_save_this_cpu(regs, cpu);
+       atomic_dec(&waiting_for_dump_ipi);
+
+       stop_this_cpu(NULL);
+
+       return 1;
+}
+
+void __crash_dump_stop_cpus(void)
+{
+       int i, cpu, other_cpus;
+
+       preempt_disable();
+       cpu = smp_processor_id();
+       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++)
+                       crash_dump_expect_ipi[i] = (i != cpu && cpu_online(i));
+
+               set_nmi_callback(crash_dump_nmi_callback);
+               /* Ensure the new callback function is set before sending
+                * out the IPI
+                */
+               wmb();
+
+               crash_dump_send_ipi();
+               while (atomic_read(&waiting_for_dump_ipi) > 0)
+                       cpu_relax();
+
+               unset_nmi_callback();
+       } else {
+               local_irq_disable();
+               disable_local_APIC();
+               local_irq_enable();
+       }
+       preempt_enable();
+}
+#else
+void __crash_dump_stop_cpus(void) {}
+#endif
+
+void crash_get_current_regs(struct pt_regs *regs)
+{
+       __asm__ __volatile__("movl %%ebx,%0" : "=m"(regs->ebx));
+       __asm__ __volatile__("movl %%ecx,%0" : "=m"(regs->ecx));
+       __asm__ __volatile__("movl %%edx,%0" : "=m"(regs->edx));
+       __asm__ __volatile__("movl %%esi,%0" : "=m"(regs->esi));
+       __asm__ __volatile__("movl %%edi,%0" : "=m"(regs->edi));
+       __asm__ __volatile__("movl %%ebp,%0" : "=m"(regs->ebp));
+       __asm__ __volatile__("movl %%eax,%0" : "=m"(regs->eax));
+       __asm__ __volatile__("movl %%esp,%0" : "=m"(regs->esp));
+       __asm__ __volatile__("movw %%ss, %%ax;" :"=a"(regs->xss));
+       __asm__ __volatile__("movw %%cs, %%ax;" :"=a"(regs->xcs));
+       __asm__ __volatile__("movw %%ds, %%ax;" :"=a"(regs->xds));
+       __asm__ __volatile__("movw %%es, %%ax;" :"=a"(regs->xes));
+       __asm__ __volatile__("pushfl; popl %0" :"=m"(regs->eflags));
+
+       regs->eip = (unsigned long)current_text_addr();
+}
+
+void crash_dump_save_this_cpu(struct pt_regs *regs, int cpu)
+{
+       crash_smp_current_task[cpu] = (long)current;
+       crash_smp_regs[cpu] = *regs;
+}
+
diff --git a/arch/i386/kernel/quirks.c b/arch/i386/kernel/quirks.c
new file mode 100644 (file)
index 0000000..847ba44
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file contains work-arounds for x86 and x86_64 platform bugs.
+ */
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/irq.h>
+
+#if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_SMP) && defined(CONFIG_PCI)
+
+void __devinit quirk_intel_irqbalance(struct pci_dev *dev)
+{
+       u8 config, rev;
+       u32 word;
+
+       /* BIOS may enable hardware IRQ balancing for
+        * E7520/E7320/E7525(revision ID 0x9 and below)
+        * based platforms.
+        * Disable SW irqbalance/affinity on those platforms.
+        */
+       pci_read_config_byte(dev, PCI_CLASS_REVISION, &rev);
+       if (rev > 0x9)
+               return;
+
+       printk(KERN_INFO "Intel E7520/7320/7525 detected.");
+
+       /* enable access to config space*/
+       pci_read_config_byte(dev, 0xf4, &config);
+       config |= 0x2;
+       pci_write_config_byte(dev, 0xf4, config);
+
+       /* read xTPR register */
+       raw_pci_ops->read(0, 0, 0x40, 0x4c, 2, &word);
+
+       if (!(word & (1 << 13))) {
+               printk(KERN_INFO "Disabling irq balancing and affinity\n");
+#ifdef CONFIG_IRQBALANCE
+               irqbalance_disable("");
+#endif
+               noirqdebug_setup("");
+               no_irq_affinity = 1;
+       }
+
+       config &= ~0x2;
+       /* disable access to config space*/
+       pci_write_config_byte(dev, 0xf4, config);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_E7320_MCH,  quirk_intel_irqbalance);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_E7525_MCH,  quirk_intel_irqbalance);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_E7520_MCH,  quirk_intel_irqbalance);
+#endif
diff --git a/arch/i386/oprofile/backtrace.c b/arch/i386/oprofile/backtrace.c
new file mode 100644 (file)
index 0000000..49a0605
--- /dev/null
@@ -0,0 +1,124 @@
+/**
+ * @file backtrace.c
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author David Smith
+ */
+
+#include <linux/oprofile.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/ptrace.h>
+
+struct frame_head {
+       struct frame_head * ebp;
+       unsigned long ret;
+} __attribute__((packed));
+
+
+static struct frame_head *
+dump_backtrace(struct frame_head * head)
+{
+       oprofile_add_trace(head->ret);
+
+       /* frame pointers should strictly progress back up the stack
+        * (towards higher addresses) */
+       if (head >= head->ebp)
+               return 0;
+
+       return head->ebp;
+}
+
+
+#ifdef CONFIG_X86_4G
+/* With a 4G kernel/user split, user pages are not directly
+ * accessible from the kernel, so don't try
+ */
+static int pages_present(struct frame_head * head)
+{
+       return 0;
+}
+#else
+/* check that the page(s) containing the frame head are present */
+static int pages_present(struct frame_head * head)
+{
+       struct mm_struct * mm = current->mm;
+
+       /* FIXME: only necessary once per page */
+       if (!check_user_page_readable(mm, (unsigned long)head))
+               return 0;
+
+       return check_user_page_readable(mm, (unsigned long)(head + 1));
+}
+#endif /* CONFIG_X86_4G */
+
+
+/*
+ * |             | /\ Higher addresses
+ * |             |
+ * --------------- stack base (address of current_thread_info)
+ * | thread info |
+ * .             .
+ * |    stack    |
+ * --------------- saved regs->ebp value if valid (frame_head address)
+ * .             .
+ * --------------- struct pt_regs stored on stack (struct pt_regs *)
+ * |             |
+ * .             .
+ * |             |
+ * --------------- %esp
+ * |             |
+ * |             | \/ Lower addresses
+ *
+ * Thus, &pt_regs <-> stack base restricts the valid(ish) ebp values
+ */
+#ifdef CONFIG_FRAME_POINTER
+static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
+{
+       unsigned long headaddr = (unsigned long)head;
+       unsigned long stack = (unsigned long)regs;
+       unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
+
+       return headaddr > stack && headaddr < stack_base;
+}
+#else
+/* without fp, it's just junk */
+static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
+{
+       return 0;
+}
+#endif
+
+
+void
+x86_backtrace(struct pt_regs * const regs, unsigned int depth)
+{
+       struct frame_head *head;
+
+#ifdef CONFIG_X86_64
+       head = (struct frame_head *)regs->rbp;
+#else
+       head = (struct frame_head *)regs->ebp;
+#endif
+
+       if (!user_mode(regs)) {
+               while (depth-- && valid_kernel_stack(head, regs))
+                       head = dump_backtrace(head);
+               return;
+       }
+
+#ifdef CONFIG_SMP
+       if (!spin_trylock(&current->mm->page_table_lock))
+               return;
+#endif
+
+       while (depth-- && head && pages_present(head))
+               head = dump_backtrace(head);
+
+#ifdef CONFIG_SMP
+       spin_unlock(&current->mm->page_table_lock);
+#endif
+}
diff --git a/arch/ia64/hp/common/hwsw_iommu.c b/arch/ia64/hp/common/hwsw_iommu.c
new file mode 100644 (file)
index 0000000..80f8ef0
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2004 Hewlett-Packard Development Company, L.P.
+ *   Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * This is a pseudo I/O MMU which dispatches to the hardware I/O MMU
+ * whenever possible.  We assume that the hardware I/O MMU requires
+ * full 32-bit addressability, as is the case, e.g., for HP zx1-based
+ * systems (there, the I/O MMU window is mapped at 3-4GB).  If a
+ * device doesn't provide full 32-bit addressability, we fall back on
+ * the sw I/O TLB.  This is good enough to let us support broken
+ * hardware such as soundcards which have a DMA engine that can
+ * address only 28 bits.
+ */
+
+#include <linux/device.h>
+
+#include <asm/machvec.h>
+
+/* swiotlb declarations & definitions: */
+extern void swiotlb_init_with_default_size (size_t size);
+extern ia64_mv_dma_alloc_coherent      swiotlb_alloc_coherent;
+extern ia64_mv_dma_free_coherent       swiotlb_free_coherent;
+extern ia64_mv_dma_map_single          swiotlb_map_single;
+extern ia64_mv_dma_unmap_single                swiotlb_unmap_single;
+extern ia64_mv_dma_map_sg              swiotlb_map_sg;
+extern ia64_mv_dma_unmap_sg            swiotlb_unmap_sg;
+extern ia64_mv_dma_supported           swiotlb_dma_supported;
+extern ia64_mv_dma_mapping_error       swiotlb_dma_mapping_error;
+
+/* hwiommu declarations & definitions: */
+
+extern ia64_mv_dma_alloc_coherent      sba_alloc_coherent;
+extern ia64_mv_dma_free_coherent       sba_free_coherent;
+extern ia64_mv_dma_map_single          sba_map_single;
+extern ia64_mv_dma_unmap_single                sba_unmap_single;
+extern ia64_mv_dma_map_sg              sba_map_sg;
+extern ia64_mv_dma_unmap_sg            sba_unmap_sg;
+extern ia64_mv_dma_supported           sba_dma_supported;
+extern ia64_mv_dma_mapping_error       sba_dma_mapping_error;
+
+#define hwiommu_alloc_coherent         sba_alloc_coherent
+#define hwiommu_free_coherent          sba_free_coherent
+#define hwiommu_map_single             sba_map_single
+#define hwiommu_unmap_single           sba_unmap_single
+#define hwiommu_map_sg                 sba_map_sg
+#define hwiommu_unmap_sg               sba_unmap_sg
+#define hwiommu_dma_supported          sba_dma_supported
+#define hwiommu_dma_mapping_error      sba_dma_mapping_error
+#define hwiommu_sync_single_for_cpu    machvec_dma_sync_single
+#define hwiommu_sync_sg_for_cpu                machvec_dma_sync_sg
+#define hwiommu_sync_single_for_device machvec_dma_sync_single
+#define hwiommu_sync_sg_for_device     machvec_dma_sync_sg
+
+
+/*
+ * Note: we need to make the determination of whether or not to use
+ * the sw I/O TLB based purely on the device structure.  Anything else
+ * would be unreliable or would be too intrusive.
+ */
+static inline int
+use_swiotlb (struct device *dev)
+{
+       return dev && dev->dma_mask && !hwiommu_dma_supported(dev, *dev->dma_mask);
+}
+
+void
+hwsw_init (void)
+{
+       /* default to a smallish 2MB sw I/O TLB */
+       swiotlb_init_with_default_size (2 * (1<<20));
+}
+
+void *
+hwsw_alloc_coherent (struct device *dev, size_t size, dma_addr_t *dma_handle, int flags)
+{
+       if (use_swiotlb(dev))
+               return swiotlb_alloc_coherent(dev, size, dma_handle, flags);
+       else
+               return hwiommu_alloc_coherent(dev, size, dma_handle, flags);
+}
+
+void
+hwsw_free_coherent (struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle)
+{
+       if (use_swiotlb(dev))
+               swiotlb_free_coherent(dev, size, vaddr, dma_handle);
+       else
+               hwiommu_free_coherent(dev, size, vaddr, dma_handle);
+}
+
+dma_addr_t
+hwsw_map_single (struct device *dev, void *addr, size_t size, int dir)
+{
+       if (use_swiotlb(dev))
+               return swiotlb_map_single(dev, addr, size, dir);
+       else
+               return hwiommu_map_single(dev, addr, size, dir);
+}
+
+void
+hwsw_unmap_single (struct device *dev, dma_addr_t iova, size_t size, int dir)
+{
+       if (use_swiotlb(dev))
+               return swiotlb_unmap_single(dev, iova, size, dir);
+       else
+               return hwiommu_unmap_single(dev, iova, size, dir);
+}
+
+
+int
+hwsw_map_sg (struct device *dev, struct scatterlist *sglist, int nents, int dir)
+{
+       if (use_swiotlb(dev))
+               return swiotlb_map_sg(dev, sglist, nents, dir);
+       else
+               return hwiommu_map_sg(dev, sglist, nents, dir);
+}
+
+void
+hwsw_unmap_sg (struct device *dev, struct scatterlist *sglist, int nents, int dir)
+{
+       if (use_swiotlb(dev))
+               return swiotlb_unmap_sg(dev, sglist, nents, dir);
+       else
+               return hwiommu_unmap_sg(dev, sglist, nents, dir);
+}
+
+void
+hwsw_sync_single_for_cpu (struct device *dev, dma_addr_t addr, size_t size, int dir)
+{
+       if (use_swiotlb(dev))
+               swiotlb_sync_single_for_cpu(dev, addr, size, dir);
+       else
+               hwiommu_sync_single_for_cpu(dev, addr, size, dir);
+}
+
+void
+hwsw_sync_sg_for_cpu (struct device *dev, struct scatterlist *sg, int nelems, int dir)
+{
+       if (use_swiotlb(dev))
+               swiotlb_sync_sg_for_cpu(dev, sg, nelems, dir);
+       else
+               hwiommu_sync_sg_for_cpu(dev, sg, nelems, dir);
+}
+
+void
+hwsw_sync_single_for_device (struct device *dev, dma_addr_t addr, size_t size, int dir)
+{
+       if (use_swiotlb(dev))
+               swiotlb_sync_single_for_device(dev, addr, size, dir);
+       else
+               hwiommu_sync_single_for_device(dev, addr, size, dir);
+}
+
+void
+hwsw_sync_sg_for_device (struct device *dev, struct scatterlist *sg, int nelems, int dir)
+{
+       if (use_swiotlb(dev))
+               swiotlb_sync_sg_for_device(dev, sg, nelems, dir);
+       else
+               hwiommu_sync_sg_for_device(dev, sg, nelems, dir);
+}
+
+int
+hwsw_dma_supported (struct device *dev, u64 mask)
+{
+       if (hwiommu_dma_supported(dev, mask))
+               return 1;
+       return swiotlb_dma_supported(dev, mask);
+}
+
+int
+hwsw_dma_mapping_error (dma_addr_t dma_addr)
+{
+       return hwiommu_dma_mapping_error (dma_addr) || swiotlb_dma_mapping_error(dma_addr);
+}
+
+EXPORT_SYMBOL(hwsw_dma_mapping_error);
+EXPORT_SYMBOL(hwsw_map_single);
+EXPORT_SYMBOL(hwsw_unmap_single);
+EXPORT_SYMBOL(hwsw_map_sg);
+EXPORT_SYMBOL(hwsw_unmap_sg);
+EXPORT_SYMBOL(hwsw_dma_supported);
+EXPORT_SYMBOL(hwsw_alloc_coherent);
+EXPORT_SYMBOL(hwsw_free_coherent);
diff --git a/arch/ia64/hp/zx1/hpzx1_swiotlb_machvec.c b/arch/ia64/hp/zx1/hpzx1_swiotlb_machvec.c
new file mode 100644 (file)
index 0000000..4392a96
--- /dev/null
@@ -0,0 +1,3 @@
+#define MACHVEC_PLATFORM_NAME          hpzx1_swiotlb
+#define MACHVEC_PLATFORM_HEADER                <asm/machvec_hpzx1_swiotlb.h>
+#include <asm/machvec_init.h>
diff --git a/arch/ia64/kernel/domain.c b/arch/ia64/kernel/domain.c
new file mode 100644 (file)
index 0000000..c655353
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * arch/ia64/kernel/domain.c
+ * Architecture specific sched-domains builder.
+ *
+ * Copyright (C) 2004 Jesse Barnes
+ * Copyright (C) 2004 Silicon Graphics, Inc.
+ */
+
+#include <linux/sched.h>
+#include <linux/percpu.h>
+#include <linux/slab.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/topology.h>
+
+#define SD_NODES_PER_DOMAIN 6
+
+#ifdef CONFIG_NUMA
+/**
+ * find_next_best_node - find the next node to include in a sched_domain
+ * @node: node whose sched_domain we're building
+ * @used_nodes: nodes already in the sched_domain
+ *
+ * Find the next node to include in a given scheduling domain.  Simply
+ * finds the closest node not already in the @used_nodes map.
+ *
+ * Should use nodemask_t.
+ */
+static int __devinit find_next_best_node(int node, unsigned long *used_nodes)
+{
+       int i, n, val, min_val, best_node = 0;
+
+       min_val = INT_MAX;
+
+       for (i = 0; i < MAX_NUMNODES; i++) {
+               /* Start at @node */
+               n = (node + i) % MAX_NUMNODES;
+
+               if (!nr_cpus_node(n))
+                       continue;
+
+               /* Skip already used nodes */
+               if (test_bit(n, used_nodes))
+                       continue;
+
+               /* Simple min distance search */
+               val = node_distance(node, n);
+
+               if (val < min_val) {
+                       min_val = val;
+                       best_node = n;
+               }
+       }
+
+       set_bit(best_node, used_nodes);
+       return best_node;
+}
+
+/**
+ * sched_domain_node_span - get a cpumask for a node's sched_domain
+ * @node: node whose cpumask we're constructing
+ * @size: number of nodes to include in this span
+ *
+ * Given a node, construct a good cpumask for its sched_domain to span.  It
+ * should be one that prevents unnecessary balancing, but also spreads tasks
+ * out optimally.
+ */
+static cpumask_t __devinit sched_domain_node_span(int node)
+{
+       int i;
+       cpumask_t span, nodemask;
+       DECLARE_BITMAP(used_nodes, MAX_NUMNODES);
+
+       cpus_clear(span);
+       bitmap_zero(used_nodes, MAX_NUMNODES);
+
+       nodemask = node_to_cpumask(node);
+       cpus_or(span, span, nodemask);
+       set_bit(node, used_nodes);
+
+       for (i = 1; i < SD_NODES_PER_DOMAIN; i++) {
+               int next_node = find_next_best_node(node, used_nodes);
+               nodemask = node_to_cpumask(next_node);
+               cpus_or(span, span, nodemask);
+       }
+
+       return span;
+}
+#endif
+
+/*
+ * At the moment, CONFIG_SCHED_SMT is never defined, but leave it in so we
+ * can switch it on easily if needed.
+ */
+#ifdef CONFIG_SCHED_SMT
+static DEFINE_PER_CPU(struct sched_domain, cpu_domains);
+static struct sched_group sched_group_cpus[NR_CPUS];
+static int __devinit cpu_to_cpu_group(int cpu)
+{
+       return cpu;
+}
+#endif
+
+static DEFINE_PER_CPU(struct sched_domain, phys_domains);
+static struct sched_group sched_group_phys[NR_CPUS];
+static int __devinit cpu_to_phys_group(int cpu)
+{
+#ifdef CONFIG_SCHED_SMT
+       return first_cpu(cpu_sibling_map[cpu]);
+#else
+       return cpu;
+#endif
+}
+
+#ifdef CONFIG_NUMA
+/*
+ * The init_sched_build_groups can't handle what we want to do with node
+ * groups, so roll our own. Now each node has its own list of groups which
+ * gets dynamically allocated.
+ */
+static DEFINE_PER_CPU(struct sched_domain, node_domains);
+static struct sched_group *sched_group_nodes[MAX_NUMNODES];
+
+static DEFINE_PER_CPU(struct sched_domain, allnodes_domains);
+static struct sched_group sched_group_allnodes[MAX_NUMNODES];
+
+static int __devinit cpu_to_allnodes_group(int cpu)
+{
+       return cpu_to_node(cpu);
+}
+#endif
+
+/*
+ * Set up scheduler domains and groups.  Callers must hold the hotplug lock.
+ */
+void __devinit arch_init_sched_domains(void)
+{
+       int i;
+       cpumask_t cpu_default_map;
+
+       /*
+        * Setup mask for cpus without special case scheduling requirements.
+        * For now this just excludes isolated cpus, but could be used to
+        * exclude other special cases in the future.
+        */
+       cpus_complement(cpu_default_map, cpu_isolated_map);
+       cpus_and(cpu_default_map, cpu_default_map, cpu_online_map);
+
+       /*
+        * Set up domains. Isolated domains just stay on the dummy domain.
+        */
+       for_each_cpu_mask(i, cpu_default_map) {
+               int node = cpu_to_node(i);
+               int group;
+               struct sched_domain *sd = NULL, *p;
+               cpumask_t nodemask = node_to_cpumask(node);
+
+               cpus_and(nodemask, nodemask, cpu_default_map);
+
+#ifdef CONFIG_NUMA
+               if (num_online_cpus()
+                               > SD_NODES_PER_DOMAIN*cpus_weight(nodemask)) {
+                       sd = &per_cpu(allnodes_domains, i);
+                       *sd = SD_ALLNODES_INIT;
+                       sd->span = cpu_default_map;
+                       group = cpu_to_allnodes_group(i);
+                       sd->groups = &sched_group_allnodes[group];
+                       p = sd;
+               } else
+                       p = NULL;
+
+               sd = &per_cpu(node_domains, i);
+               *sd = SD_NODE_INIT;
+               sd->span = sched_domain_node_span(node);
+               sd->parent = p;
+               cpus_and(sd->span, sd->span, cpu_default_map);
+#endif
+
+               p = sd;
+               sd = &per_cpu(phys_domains, i);
+               group = cpu_to_phys_group(i);
+               *sd = SD_CPU_INIT;
+               sd->span = nodemask;
+               sd->parent = p;
+               sd->groups = &sched_group_phys[group];
+
+#ifdef CONFIG_SCHED_SMT
+               p = sd;
+               sd = &per_cpu(cpu_domains, i);
+               group = cpu_to_cpu_group(i);
+               *sd = SD_SIBLING_INIT;
+               sd->span = cpu_sibling_map[i];
+               cpus_and(sd->span, sd->span, cpu_default_map);
+               sd->parent = p;
+               sd->groups = &sched_group_cpus[group];
+#endif
+       }
+
+#ifdef CONFIG_SCHED_SMT
+       /* Set up CPU (sibling) groups */
+       for_each_cpu_mask(i, cpu_default_map) {
+               cpumask_t this_sibling_map = cpu_sibling_map[i];
+               cpus_and(this_sibling_map, this_sibling_map, cpu_default_map);
+               if (i != first_cpu(this_sibling_map))
+                       continue;
+
+               init_sched_build_groups(sched_group_cpus, this_sibling_map,
+                                               &cpu_to_cpu_group);
+       }
+#endif
+
+       /* Set up physical groups */
+       for (i = 0; i < MAX_NUMNODES; i++) {
+               cpumask_t nodemask = node_to_cpumask(i);
+
+               cpus_and(nodemask, nodemask, cpu_default_map);
+               if (cpus_empty(nodemask))
+                       continue;
+
+               init_sched_build_groups(sched_group_phys, nodemask,
+                                               &cpu_to_phys_group);
+       }
+
+#ifdef CONFIG_NUMA
+       init_sched_build_groups(sched_group_allnodes, cpu_default_map,
+                               &cpu_to_allnodes_group);
+
+       for (i = 0; i < MAX_NUMNODES; i++) {
+               /* Set up node groups */
+               struct sched_group *sg, *prev;
+               cpumask_t nodemask = node_to_cpumask(i);
+               cpumask_t domainspan;
+               cpumask_t covered = CPU_MASK_NONE;
+               int j;
+
+               cpus_and(nodemask, nodemask, cpu_default_map);
+               if (cpus_empty(nodemask))
+                       continue;
+
+               domainspan = sched_domain_node_span(i);
+               cpus_and(domainspan, domainspan, cpu_default_map);
+
+               sg = kmalloc(sizeof(struct sched_group), GFP_KERNEL);
+               sched_group_nodes[i] = sg;
+               for_each_cpu_mask(j, nodemask) {
+                       struct sched_domain *sd;
+                       sd = &per_cpu(node_domains, j);
+                       sd->groups = sg;
+                       if (sd->groups == NULL) {
+                               /* Turn off balancing if we have no groups */
+                               sd->flags = 0;
+                       }
+               }
+               if (!sg) {
+                       printk(KERN_WARNING
+                       "Can not alloc domain group for node %d\n", i);
+                       continue;
+               }
+               sg->cpu_power = 0;
+               sg->cpumask = nodemask;
+               cpus_or(covered, covered, nodemask);
+               prev = sg;
+
+               for (j = 0; j < MAX_NUMNODES; j++) {
+                       cpumask_t tmp, notcovered;
+                       int n = (i + j) % MAX_NUMNODES;
+
+                       cpus_complement(notcovered, covered);
+                       cpus_and(tmp, notcovered, cpu_default_map);
+                       cpus_and(tmp, tmp, domainspan);
+                       if (cpus_empty(tmp))
+                               break;
+
+                       nodemask = node_to_cpumask(n);
+                       cpus_and(tmp, tmp, nodemask);
+                       if (cpus_empty(tmp))
+                               continue;
+
+                       sg = kmalloc(sizeof(struct sched_group), GFP_KERNEL);
+                       if (!sg) {
+                               printk(KERN_WARNING
+                               "Can not alloc domain group for node %d\n", j);
+                               break;
+                       }
+                       sg->cpu_power = 0;
+                       sg->cpumask = tmp;
+                       cpus_or(covered, covered, tmp);
+                       prev->next = sg;
+                       prev = sg;
+               }
+               prev->next = sched_group_nodes[i];
+       }
+#endif
+
+       /* Calculate CPU power for physical packages and nodes */
+       for_each_cpu_mask(i, cpu_default_map) {
+               int power;
+               struct sched_domain *sd;
+#ifdef CONFIG_SCHED_SMT
+               sd = &per_cpu(cpu_domains, i);
+               power = SCHED_LOAD_SCALE;
+               sd->groups->cpu_power = power;
+#endif
+
+               sd = &per_cpu(phys_domains, i);
+               power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE *
+                               (cpus_weight(sd->groups->cpumask)-1) / 10;
+               sd->groups->cpu_power = power;
+
+#ifdef CONFIG_NUMA
+               sd = &per_cpu(allnodes_domains, i);
+               if (sd->groups) {
+                       power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE *
+                               (cpus_weight(sd->groups->cpumask)-1) / 10;
+                       sd->groups->cpu_power = power;
+               }
+#endif
+       }
+
+#ifdef CONFIG_NUMA
+       for (i = 0; i < MAX_NUMNODES; i++) {
+               struct sched_group *sg = sched_group_nodes[i];
+               int j;
+
+               if (sg == NULL)
+                       continue;
+next_sg:
+               for_each_cpu_mask(j, sg->cpumask) {
+                       struct sched_domain *sd;
+                       int power;
+
+                       sd = &per_cpu(phys_domains, j);
+                       if (j != first_cpu(sd->groups->cpumask)) {
+                               /*
+                                * Only add "power" once for each
+                                * physical package.
+                                */
+                               continue;
+                       }
+                       power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE *
+                               (cpus_weight(sd->groups->cpumask)-1) / 10;
+
+                       sg->cpu_power += power;
+               }
+               sg = sg->next;
+               if (sg != sched_group_nodes[i])
+                       goto next_sg;
+       }
+#endif
+
+       /* Attach the domains */
+       for_each_online_cpu(i) {
+               struct sched_domain *sd;
+#ifdef CONFIG_SCHED_SMT
+               sd = &per_cpu(cpu_domains, i);
+#else
+               sd = &per_cpu(phys_domains, i);
+#endif
+               cpu_attach_domain(sd, i);
+       }
+}
+
+void __devinit arch_destroy_sched_domains(void)
+{
+#ifdef CONFIG_NUMA
+       int i;
+       for (i = 0; i < MAX_NUMNODES; i++) {
+               struct sched_group *oldsg, *sg = sched_group_nodes[i];
+               if (sg == NULL)
+                       continue;
+               sg = sg->next;
+next_sg:
+               oldsg = sg;
+               sg = sg->next;
+               kfree(oldsg);
+               if (oldsg != sched_group_nodes[i])
+                       goto next_sg;
+               sched_group_nodes[i] = NULL;
+       }
+#endif
+}
+
diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c
new file mode 100644 (file)
index 0000000..f1aafd4
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * This file contains NUMA specific variables and functions which can
+ * be split away from DISCONTIGMEM and are used on NUMA machines with
+ * contiguous memory.
+ *             2002/08/07 Erich Focht <efocht@ess.nec.de>
+ * Populate cpu entries in sysfs for non-numa systems as well
+ *     Intel Corporation - Ashok Raj
+ */
+
+#include <linux/config.h>
+#include <linux/cpu.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/node.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/nodemask.h>
+#include <asm/mmzone.h>
+#include <asm/numa.h>
+#include <asm/cpu.h>
+
+#ifdef CONFIG_NUMA
+static struct node *sysfs_nodes;
+#endif
+static struct ia64_cpu *sysfs_cpus;
+
+int arch_register_cpu(int num)
+{
+       struct node *parent = NULL;
+       
+#ifdef CONFIG_NUMA
+       parent = &sysfs_nodes[cpu_to_node(num)];
+#endif /* CONFIG_NUMA */
+
+       return register_cpu(&sysfs_cpus[num].cpu, num, parent);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+void arch_unregister_cpu(int num)
+{
+       struct node *parent = NULL;
+
+#ifdef CONFIG_NUMA
+       int node = cpu_to_node(num);
+       parent = &sysfs_nodes[node];
+#endif /* CONFIG_NUMA */
+
+       return unregister_cpu(&sysfs_cpus[num].cpu, parent);
+}
+EXPORT_SYMBOL(arch_register_cpu);
+EXPORT_SYMBOL(arch_unregister_cpu);
+#endif /*CONFIG_HOTPLUG_CPU*/
+
+
+static int __init topology_init(void)
+{
+       int i, err = 0;
+
+#ifdef CONFIG_NUMA
+       sysfs_nodes = kmalloc(sizeof(struct node) * MAX_NUMNODES, GFP_KERNEL);
+       if (!sysfs_nodes) {
+               err = -ENOMEM;
+               goto out;
+       }
+       memset(sysfs_nodes, 0, sizeof(struct node) * MAX_NUMNODES);
+
+       /* MCD - Do we want to register all ONLINE nodes, or all POSSIBLE nodes? */
+       for_each_online_node(i)
+               if ((err = register_node(&sysfs_nodes[i], i, 0)))
+                       goto out;
+#endif
+
+       sysfs_cpus = kmalloc(sizeof(struct ia64_cpu) * NR_CPUS, GFP_KERNEL);
+       if (!sysfs_cpus) {
+               err = -ENOMEM;
+               goto out;
+       }
+       memset(sysfs_cpus, 0, sizeof(struct ia64_cpu) * NR_CPUS);
+
+       for_each_present_cpu(i)
+               if((err = arch_register_cpu(i)))
+                       goto out;
+out:
+       return err;
+}
+
+__initcall(topology_init);
diff --git a/arch/ia64/oprofile/backtrace.c b/arch/ia64/oprofile/backtrace.c
new file mode 100644 (file)
index 0000000..b7dabbf
--- /dev/null
@@ -0,0 +1,150 @@
+/**
+ * @file backtrace.c
+ *
+ * @remark Copyright 2004 Silicon Graphics Inc.  All Rights Reserved.
+ * @remark Read the file COPYING
+ *
+ * @author Greg Banks <gnb@melbourne.sgi.com>
+ * @author Keith Owens <kaos@melbourne.sgi.com>
+ * Based on work done for the ia64 port of the SGI kernprof patch, which is
+ *    Copyright (c) 2003-2004 Silicon Graphics Inc.  All Rights Reserved.
+ */
+
+#include <linux/oprofile.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+
+/*
+ * For IA64 we need to perform a complex little dance to get both
+ * the struct pt_regs and a synthetic struct switch_stack in place
+ * to allow the unwind code to work.  This dance requires our unwind
+ * using code to be called from a function called from unw_init_running().
+ * There we only get a single void* data pointer, so use this struct
+ * to hold all the data we need during the unwind.
+ */
+typedef struct
+{
+       unsigned int depth;
+       struct pt_regs *regs;
+       struct unw_frame_info frame;
+       u64 *prev_pfs_loc;      /* state for WAR for old spinlock ool code */
+} ia64_backtrace_t;
+
+#if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 3)
+/*
+ * Returns non-zero if the PC is in the spinlock contention out-of-line code
+ * with non-standard calling sequence (on older compilers).
+ */
+static __inline__ int in_old_ool_spinlock_code(unsigned long pc)
+{
+       extern const char ia64_spinlock_contention_pre3_4[] __attribute__ ((weak));
+       extern const char ia64_spinlock_contention_pre3_4_end[] __attribute__ ((weak));
+       unsigned long sc_start = (unsigned long)ia64_spinlock_contention_pre3_4;
+       unsigned long sc_end = (unsigned long)ia64_spinlock_contention_pre3_4_end;
+       return (sc_start && sc_end && pc >= sc_start && pc < sc_end);
+}
+#else
+/* Newer spinlock code does a proper br.call and works fine with the unwinder */
+#define in_old_ool_spinlock_code(pc)   0
+#endif
+
+/* Returns non-zero if the PC is in the Interrupt Vector Table */
+static __inline__ int in_ivt_code(unsigned long pc)
+{
+       extern char ia64_ivt[];
+       return (pc >= (u_long)ia64_ivt && pc < (u_long)ia64_ivt+32768);
+}
+
+/*
+ * Unwind to next stack frame.
+ */
+static __inline__ int next_frame(ia64_backtrace_t *bt)
+{
+       /*
+        * Avoid unsightly console message from unw_unwind() when attempting
+        * to unwind through the Interrupt Vector Table which has no unwind
+        * information.
+        */
+       if (in_ivt_code(bt->frame.ip))
+               return 0;
+
+       /*
+        * WAR for spinlock contention from leaf functions.  ia64_spinlock_contention_pre3_4
+        * has ar.pfs == r0.  Leaf functions do not modify ar.pfs so ar.pfs remains
+        * as 0, stopping the backtrace.  Record the previous ar.pfs when the current
+        * IP is in ia64_spinlock_contention_pre3_4 then unwind, if pfs_loc has not changed
+        * after unwind then use pt_regs.ar_pfs which is where the real ar.pfs is for
+        * leaf functions.
+        */
+       if (bt->prev_pfs_loc && bt->regs && bt->frame.pfs_loc == bt->prev_pfs_loc)
+               bt->frame.pfs_loc = &bt->regs->ar_pfs;
+       bt->prev_pfs_loc = (in_old_ool_spinlock_code(bt->frame.ip) ? bt->frame.pfs_loc : NULL);
+
+       return unw_unwind(&bt->frame) == 0;
+}
+
+
+static void do_ia64_backtrace(struct unw_frame_info *info, void *vdata)
+{
+       ia64_backtrace_t *bt = vdata;
+       struct switch_stack *sw;
+       int count = 0;
+       u_long pc, sp;
+
+       sw = (struct switch_stack *)(info+1);
+       /* padding from unw_init_running */
+       sw = (struct switch_stack *)(((unsigned long)sw + 15) & ~15);
+
+       unw_init_frame_info(&bt->frame, current, sw);
+
+       /* skip over interrupt frame and oprofile calls */
+       do {
+               unw_get_sp(&bt->frame, &sp);
+               if (sp >= (u_long)bt->regs)
+                       break;
+               if (!next_frame(bt))
+                       return;
+       } while (count++ < 200);
+
+       /* finally, grab the actual sample */
+       while (bt->depth-- && next_frame(bt)) {
+               unw_get_ip(&bt->frame, &pc);
+               oprofile_add_trace(pc);
+               if (unw_is_intr_frame(&bt->frame)) {
+                       /*
+                        * Interrupt received on kernel stack; this can
+                        * happen when timer interrupt fires while processing
+                        * a softirq from the tail end of a hardware interrupt
+                        * which interrupted a system call.  Don't laugh, it
+                        * happens!  Splice the backtrace into two parts to
+                        * avoid spurious cycles in the gprof output.
+                        */
+                       /* TODO: split rather than drop the 2nd half */
+                       break;
+               }
+       }
+}
+
+void
+ia64_backtrace(struct pt_regs * const regs, unsigned int depth)
+{
+       ia64_backtrace_t bt;
+       unsigned long flags;
+
+       /*
+        * On IA64 there is little hope of getting backtraces from
+        * user space programs -- the problems of getting the unwind
+        * information from arbitrary user programs are extreme.
+        */
+       if (user_mode(regs))
+               return;
+
+       bt.depth = depth;
+       bt.regs = regs;
+       bt.prev_pfs_loc = NULL;
+       local_irq_save(flags);
+       unw_init_running(do_ia64_backtrace, &bt);
+       local_irq_restore(flags);
+}
diff --git a/arch/ia64/sn/include/ioerror.h b/arch/ia64/sn/include/ioerror.h
new file mode 100644 (file)
index 0000000..e68f2b0
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
+ */
+#ifndef _ASM_IA64_SN_IOERROR_H
+#define _ASM_IA64_SN_IOERROR_H
+
+/*
+ * IO error structure.
+ *
+ * This structure would expand to hold the information retrieved from
+ * all IO related error registers.
+ *
+ * This structure is defined to hold all system specific
+ * information related to a single error.
+ *
+ * This serves a couple of purpose.
+ *      - Error handling often involves translating one form of address to other
+ *        form. So, instead of having different data structures at each level,
+ *        we have a single structure, and the appropriate fields get filled in
+ *        at each layer.
+ *      - This provides a way to dump all error related information in any layer
+ *        of erorr handling (debugging aid).
+ *
+ * A second possibility is to allow each layer to define its own error
+ * data structure, and fill in the proper fields. This has the advantage
+ * of isolating the layers.
+ * A big concern is the potential stack usage (and overflow), if each layer
+ * defines these structures on stack (assuming we don't want to do kmalloc.
+ *
+ * Any layer wishing to pass extra information to a layer next to it in
+ * error handling hierarchy, can do so as a separate parameter.
+ */
+
+typedef struct io_error_s {
+    /* Bit fields indicating which structure fields are valid */
+    union {
+       struct {
+           unsigned                ievb_errortype:1;
+           unsigned                ievb_widgetnum:1;
+           unsigned                ievb_widgetdev:1;
+           unsigned                ievb_srccpu:1;
+           unsigned                ievb_srcnode:1;
+           unsigned                ievb_errnode:1;
+           unsigned                ievb_sysioaddr:1;
+           unsigned                ievb_xtalkaddr:1;
+           unsigned                ievb_busspace:1;
+           unsigned                ievb_busaddr:1;
+           unsigned                ievb_vaddr:1;
+           unsigned                ievb_memaddr:1;
+           unsigned                ievb_epc:1;
+           unsigned                ievb_ef:1;
+           unsigned                ievb_tnum:1;
+       } iev_b;
+       unsigned                iev_a;
+    } ie_v;
+
+    short                   ie_errortype;      /* error type: extra info about error */
+    short                   ie_widgetnum;      /* Widget number that's in error */
+    short                   ie_widgetdev;      /* Device within widget in error */
+    cpuid_t                 ie_srccpu; /* CPU on srcnode generating error */
+    cnodeid_t               ie_srcnode;                /* Node which caused the error   */
+    cnodeid_t               ie_errnode;                /* Node where error was noticed  */
+    iopaddr_t               ie_sysioaddr;      /* Sys specific IO address       */
+    iopaddr_t               ie_xtalkaddr;      /* Xtalk (48bit) addr of Error   */
+    iopaddr_t               ie_busspace;       /* Bus specific address space    */
+    iopaddr_t               ie_busaddr;                /* Bus specific address          */
+    caddr_t                 ie_vaddr;  /* Virtual address of error      */
+    iopaddr_t               ie_memaddr;                /* Physical memory address       */
+    caddr_t                ie_epc;             /* pc when error reported        */
+    caddr_t                ie_ef;              /* eframe when error reported    */
+    short                  ie_tnum;            /* Xtalk TNUM field */
+} ioerror_t;
+
+#define        IOERROR_INIT(e)         do { (e)->ie_v.iev_a = 0; } while (0)
+#define        IOERROR_SETVALUE(e,f,v) do { (e)->ie_ ## f = (v); (e)->ie_v.iev_b.ievb_ ## f = 1; } while (0)
+
+#endif /* _ASM_IA64_SN_IOERROR_H */
diff --git a/arch/ia64/sn/include/pci/pcibr_provider.h b/arch/ia64/sn/include/pci/pcibr_provider.h
new file mode 100644 (file)
index 0000000..b1f05ff
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992-1997,2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+#ifndef _ASM_IA64_SN_PCI_PCIBR_PROVIDER_H
+#define _ASM_IA64_SN_PCI_PCIBR_PROVIDER_H
+
+/* Workarounds */
+#define PV907516 (1 << 1) /* TIOCP: Don't write the write buffer flush reg */
+
+#define BUSTYPE_MASK                    0x1
+
+/* Macros given a pcibus structure */
+#define IS_PCIX(ps)     ((ps)->pbi_bridge_mode & BUSTYPE_MASK)
+#define IS_PCI_BRIDGE_ASIC(asic) (asic == PCIIO_ASIC_TYPE_PIC || \
+                asic == PCIIO_ASIC_TYPE_TIOCP)
+#define IS_PIC_SOFT(ps)     (ps->pbi_bridge_type == PCIBR_BRIDGETYPE_PIC)
+
+
+/* 
+ * The different PCI Bridge types supported on the SGI Altix platforms
+ */
+#define PCIBR_BRIDGETYPE_UNKNOWN       -1
+#define PCIBR_BRIDGETYPE_PIC            2
+#define PCIBR_BRIDGETYPE_TIOCP          3
+
+/*
+ * Bridge 64bit Direct Map Attributes
+ */
+#define PCI64_ATTR_PREF                 (1ull << 59)
+#define PCI64_ATTR_PREC                 (1ull << 58)
+#define PCI64_ATTR_VIRTUAL              (1ull << 57)
+#define PCI64_ATTR_BAR                  (1ull << 56)
+#define PCI64_ATTR_SWAP                 (1ull << 55)
+#define PCI64_ATTR_VIRTUAL1             (1ull << 54)
+
+#define PCI32_LOCAL_BASE                0
+#define PCI32_MAPPED_BASE               0x40000000
+#define PCI32_DIRECT_BASE               0x80000000
+
+#define IS_PCI32_MAPPED(x)              ((uint64_t)(x) < PCI32_DIRECT_BASE && \
+                                         (uint64_t)(x) >= PCI32_MAPPED_BASE)
+#define IS_PCI32_DIRECT(x)              ((uint64_t)(x) >= PCI32_MAPPED_BASE)
+
+
+/*
+ * Bridge PMU Address Transaltion Entry Attibutes
+ */
+#define PCI32_ATE_V                     (0x1 << 0)
+#define PCI32_ATE_CO                    (0x1 << 1)
+#define PCI32_ATE_PREC                  (0x1 << 2)
+#define PCI32_ATE_PREF                  (0x1 << 3)
+#define PCI32_ATE_BAR                   (0x1 << 4)
+#define PCI32_ATE_ADDR_SHFT             12
+
+#define MINIMAL_ATES_REQUIRED(addr, size) \
+       (IOPG(IOPGOFF(addr) + (size) - 1) == IOPG((size) - 1))
+
+#define MINIMAL_ATE_FLAG(addr, size) \
+       (MINIMAL_ATES_REQUIRED((uint64_t)addr, size) ? 1 : 0)
+
+/* bit 29 of the pci address is the SWAP bit */
+#define ATE_SWAPSHIFT                   29
+#define ATE_SWAP_ON(x)                  ((x) |= (1 << ATE_SWAPSHIFT))
+#define ATE_SWAP_OFF(x)                 ((x) &= ~(1 << ATE_SWAPSHIFT))
+
+/*
+ * I/O page size
+ */
+#if PAGE_SIZE < 16384
+#define IOPFNSHIFT                      12      /* 4K per mapped page */
+#else
+#define IOPFNSHIFT                      14      /* 16K per mapped page */
+#endif
+
+#define IOPGSIZE                        (1 << IOPFNSHIFT)
+#define IOPG(x)                         ((x) >> IOPFNSHIFT)
+#define IOPGOFF(x)                      ((x) & (IOPGSIZE-1))
+
+#define PCIBR_DEV_SWAP_DIR              (1ull << 19)
+#define PCIBR_CTRL_PAGE_SIZE            (0x1 << 21)
+
+/*
+ * PMU resources.
+ */
+struct ate_resource{
+       uint64_t *ate;
+       uint64_t num_ate;
+       uint64_t lowest_free_index;
+};
+
+struct pcibus_info {
+       struct pcibus_bussoft   pbi_buscommon;   /* common header */
+       uint32_t                pbi_moduleid;
+       short                   pbi_bridge_type;
+       short                   pbi_bridge_mode;
+
+       struct ate_resource     pbi_int_ate_resource;
+       uint64_t                pbi_int_ate_size;
+       
+       uint64_t                pbi_dir_xbase;
+       char                    pbi_hub_xid;
+
+       uint64_t                pbi_devreg[8];
+       spinlock_t              pbi_lock;
+
+       uint32_t                pbi_valid_devices;
+       uint32_t                pbi_enabled_devices;
+};
+
+/*
+ * pcibus_info structure locking macros
+ */
+inline static unsigned long
+pcibr_lock(struct pcibus_info *pcibus_info)
+{
+       unsigned long flag;
+       spin_lock_irqsave(&pcibus_info->pbi_lock, flag);
+       return(flag);
+}
+#define pcibr_unlock(pcibus_info, flag)  spin_unlock_irqrestore(&pcibus_info->pbi_lock, flag)
+
+extern void *pcibr_bus_fixup(struct pcibus_bussoft *);
+extern uint64_t pcibr_dma_map(struct pcidev_info *, unsigned long, size_t, unsigned int);
+extern void pcibr_dma_unmap(struct pcidev_info *, dma_addr_t, int);
+
+/*
+ * prototypes for the bridge asic register access routines in pcibr_reg.c
+ */
+extern void             pcireg_control_bit_clr(struct pcibus_info *, uint64_t);
+extern void             pcireg_control_bit_set(struct pcibus_info *, uint64_t);
+extern uint64_t         pcireg_tflush_get(struct pcibus_info *);
+extern uint64_t         pcireg_intr_status_get(struct pcibus_info *);
+extern void             pcireg_intr_enable_bit_clr(struct pcibus_info *, uint64_t);
+extern void             pcireg_intr_enable_bit_set(struct pcibus_info *, uint64_t);
+extern void             pcireg_intr_addr_addr_set(struct pcibus_info *, int, uint64_t);
+extern void             pcireg_force_intr_set(struct pcibus_info *, int);
+extern uint64_t         pcireg_wrb_flush_get(struct pcibus_info *, int);
+extern void             pcireg_int_ate_set(struct pcibus_info *, int, uint64_t);
+extern uint64_t *      pcireg_int_ate_addr(struct pcibus_info *, int);
+extern void            pcibr_force_interrupt(struct sn_irq_info *sn_irq_info);
+extern void            pcibr_change_devices_irq(struct sn_irq_info *sn_irq_info);
+extern int             pcibr_ate_alloc(struct pcibus_info *, int);
+extern void            pcibr_ate_free(struct pcibus_info *, int);
+extern void            ate_write(struct pcibus_info *, int, int, uint64_t);
+#endif
diff --git a/arch/ia64/sn/include/pci/pcibus_provider_defs.h b/arch/ia64/sn/include/pci/pcibus_provider_defs.h
new file mode 100644 (file)
index 0000000..0706561
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+#ifndef _ASM_IA64_SN_PCI_PCIBUS_PROVIDER_H
+#define _ASM_IA64_SN_PCI_PCIBUS_PROVIDER_H
+
+/*
+ * SN pci asic types.  Do not ever renumber these or reuse values.  The
+ * values must agree with what prom thinks they are.
+ */
+
+#define PCIIO_ASIC_TYPE_UNKNOWN        0
+#define PCIIO_ASIC_TYPE_PPB    1
+#define PCIIO_ASIC_TYPE_PIC    2
+#define PCIIO_ASIC_TYPE_TIOCP  3
+
+/*
+ * Common pciio bus provider data.  There should be one of these as the
+ * first field in any pciio based provider soft structure (e.g. pcibr_soft
+ * tioca_soft, etc).
+ */
+
+struct pcibus_bussoft {
+       uint32_t                bs_asic_type;   /* chipset type */
+       uint32_t                bs_xid;         /* xwidget id */
+       uint64_t                bs_persist_busnum; /* Persistent Bus Number */
+       uint64_t                bs_legacy_io;   /* legacy io pio addr */
+       uint64_t                bs_legacy_mem;  /* legacy mem pio addr */
+       uint64_t                bs_base;        /* widget base */
+       struct xwidget_info     *bs_xwidget_info;
+};
+
+/*
+ * DMA mapping flags
+ */
+
+#define SN_PCIDMA_CONSISTENT    0x0001
+
+#endif                         /* _ASM_IA64_SN_PCI_PCIBUS_PROVIDER_H */
diff --git a/arch/ia64/sn/include/pci/pcidev.h b/arch/ia64/sn/include/pci/pcidev.h
new file mode 100644 (file)
index 0000000..81eb95d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+#ifndef _ASM_IA64_SN_PCI_PCIDEV_H
+#define _ASM_IA64_SN_PCI_PCIDEV_H
+
+#include <linux/pci.h>
+
+extern struct sn_irq_info **sn_irq;
+
+#define SN_PCIDEV_INFO(pci_dev) \
+        ((struct pcidev_info *)(pci_dev)->sysdata)
+
+/*
+ * Given a pci_bus, return the sn pcibus_bussoft struct.  Note that
+ * this only works for root busses, not for busses represented by PPB's.
+ */
+
+#define SN_PCIBUS_BUSSOFT(pci_bus) \
+        ((struct pcibus_bussoft *)(PCI_CONTROLLER((pci_bus))->platform_data))
+
+/*
+ * Given a struct pci_dev, return the sn pcibus_bussoft struct.  Note
+ * that this is not equivalent to SN_PCIBUS_BUSSOFT(pci_dev->bus) due
+ * due to possible PPB's in the path.
+ */
+
+#define SN_PCIDEV_BUSSOFT(pci_dev) \
+       (SN_PCIDEV_INFO(pci_dev)->pdi_host_pcidev_info->pdi_pcibus_info)
+
+#define PCIIO_BUS_NONE 255      /* bus 255 reserved */
+#define PCIIO_SLOT_NONE 255
+#define PCIIO_FUNC_NONE 255
+#define PCIIO_VENDOR_ID_NONE   (-1)
+
+struct pcidev_info {
+       uint64_t                pdi_pio_mapped_addr[7]; /* 6 BARs PLUS 1 ROM */
+       uint64_t                pdi_slot_host_handle;   /* Bus and devfn Host pci_dev */
+
+       struct pcibus_bussoft   *pdi_pcibus_info;       /* Kernel common bus soft */
+       struct pcidev_info      *pdi_host_pcidev_info;  /* Kernel Host pci_dev */
+       struct pci_dev          *pdi_linux_pcidev;      /* Kernel pci_dev */
+
+       struct sn_irq_info      *pdi_sn_irq_info;
+};
+
+extern void sn_irq_fixup(struct pci_dev *pci_dev,
+                        struct sn_irq_info *sn_irq_info);
+
+#endif                         /* _ASM_IA64_SN_PCI_PCIDEV_H */
diff --git a/arch/ia64/sn/include/pci/pic.h b/arch/ia64/sn/include/pci/pic.h
new file mode 100644 (file)
index 0000000..fd18ace
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
+ */
+#ifndef _ASM_IA64_SN_PCI_PIC_H
+#define _ASM_IA64_SN_PCI_PIC_H
+
+/*
+ * PIC AS DEVICE ZERO
+ * ------------------
+ *
+ * PIC handles PCI/X busses.  PCI/X requires that the 'bridge' (i.e. PIC)
+ * be designated as 'device 0'.   That is a departure from earlier SGI
+ * PCI bridges.  Because of that we use config space 1 to access the
+ * config space of the first actual PCI device on the bus. 
+ * Here's what the PIC manual says:
+ *
+ *     The current PCI-X bus specification now defines that the parent
+ *     hosts bus bridge (PIC for example) must be device 0 on bus 0. PIC
+ *     reduced the total number of devices from 8 to 4 and removed the
+ *     device registers and windows, now only supporting devices 0,1,2, and
+ *     3. PIC did leave all 8 configuration space windows. The reason was
+ *     there was nothing to gain by removing them. Here in lies the problem.
+ *     The device numbering we do using 0 through 3 is unrelated to the device
+ *     numbering which PCI-X requires in configuration space. In the past we
+ *     correlated Configs pace and our device space 0 <-> 0, 1 <-> 1, etc.
+ *     PCI-X requires we start a 1, not 0 and currently the PX brick
+ *     does associate our:
+ * 
+ *         device 0 with configuration space window 1,
+ *         device 1 with configuration space window 2, 
+ *         device 2 with configuration space window 3,
+ *         device 3 with configuration space window 4.
+ *
+ * The net effect is that all config space access are off-by-one with 
+ * relation to other per-slot accesses on the PIC.   
+ * Here is a table that shows some of that:
+ *
+ *                               Internal Slot#
+ *           |
+ *           |     0         1        2         3
+ * ----------|---------------------------------------
+ * config    |  0x21000   0x22000  0x23000   0x24000
+ *           |
+ * even rrb  |  0[0]      n/a      1[0]      n/a       [] == implied even/odd
+ *           |
+ * odd rrb   |  n/a       0[1]     n/a       1[1]
+ *           |
+ * int dev   |  00       01        10        11
+ *           |
+ * ext slot# |  1        2         3         4
+ * ----------|---------------------------------------
+ */
+
+#define PIC_ATE_TARGETID_SHFT           8
+#define PIC_HOST_INTR_ADDR              0x0000FFFFFFFFFFFFUL
+#define PIC_PCI64_ATTR_TARG_SHFT        60
+
+
+/*****************************************************************************
+ *********************** PIC MMR structure mapping ***************************
+ *****************************************************************************/
+
+/* NOTE: PIC WAR. PV#854697.  PIC does not allow writes just to [31:0]
+ * of a 64-bit register.  When writing PIC registers, always write the 
+ * entire 64 bits.
+ */
+
+struct pic {
+
+    /* 0x000000-0x00FFFF -- Local Registers */
+
+    /* 0x000000-0x000057 -- Standard Widget Configuration */
+    uint64_t           p_wid_id;                       /* 0x000000 */
+    uint64_t           p_wid_stat;                     /* 0x000008 */
+    uint64_t           p_wid_err_upper;                /* 0x000010 */
+    uint64_t           p_wid_err_lower;                /* 0x000018 */
+    #define p_wid_err p_wid_err_lower
+    uint64_t           p_wid_control;                  /* 0x000020 */
+    uint64_t           p_wid_req_timeout;              /* 0x000028 */
+    uint64_t           p_wid_int_upper;                /* 0x000030 */
+    uint64_t           p_wid_int_lower;                /* 0x000038 */
+    #define p_wid_int p_wid_int_lower
+    uint64_t           p_wid_err_cmdword;              /* 0x000040 */
+    uint64_t           p_wid_llp;                      /* 0x000048 */
+    uint64_t           p_wid_tflush;                   /* 0x000050 */
+
+    /* 0x000058-0x00007F -- Bridge-specific Widget Configuration */
+    uint64_t           p_wid_aux_err;                  /* 0x000058 */
+    uint64_t           p_wid_resp_upper;               /* 0x000060 */
+    uint64_t           p_wid_resp_lower;               /* 0x000068 */
+    #define p_wid_resp p_wid_resp_lower
+    uint64_t           p_wid_tst_pin_ctrl;             /* 0x000070 */
+    uint64_t           p_wid_addr_lkerr;               /* 0x000078 */
+
+    /* 0x000080-0x00008F -- PMU & MAP */
+    uint64_t           p_dir_map;                      /* 0x000080 */
+    uint64_t           _pad_000088;                    /* 0x000088 */
+
+    /* 0x000090-0x00009F -- SSRAM */
+    uint64_t           p_map_fault;                    /* 0x000090 */
+    uint64_t           _pad_000098;                    /* 0x000098 */
+
+    /* 0x0000A0-0x0000AF -- Arbitration */
+    uint64_t           p_arb;                          /* 0x0000A0 */
+    uint64_t           _pad_0000A8;                    /* 0x0000A8 */
+
+    /* 0x0000B0-0x0000BF -- Number In A Can or ATE Parity Error */
+    uint64_t           p_ate_parity_err;               /* 0x0000B0 */
+    uint64_t           _pad_0000B8;                    /* 0x0000B8 */
+
+    /* 0x0000C0-0x0000FF -- PCI/GIO */
+    uint64_t           p_bus_timeout;                  /* 0x0000C0 */
+    uint64_t           p_pci_cfg;                      /* 0x0000C8 */
+    uint64_t           p_pci_err_upper;                /* 0x0000D0 */
+    uint64_t           p_pci_err_lower;                /* 0x0000D8 */
+    #define p_pci_err p_pci_err_lower
+    uint64_t           _pad_0000E0[4];                 /* 0x0000{E0..F8} */
+
+    /* 0x000100-0x0001FF -- Interrupt */
+    uint64_t           p_int_status;                   /* 0x000100 */
+    uint64_t           p_int_enable;                   /* 0x000108 */
+    uint64_t           p_int_rst_stat;                 /* 0x000110 */
+    uint64_t           p_int_mode;                     /* 0x000118 */
+    uint64_t           p_int_device;                   /* 0x000120 */
+    uint64_t           p_int_host_err;                 /* 0x000128 */
+    uint64_t           p_int_addr[8];                  /* 0x0001{30,,,68} */
+    uint64_t           p_err_int_view;                 /* 0x000170 */
+    uint64_t           p_mult_int;                     /* 0x000178 */
+    uint64_t           p_force_always[8];              /* 0x0001{80,,,B8} */
+    uint64_t           p_force_pin[8];                 /* 0x0001{C0,,,F8} */
+
+    /* 0x000200-0x000298 -- Device */
+    uint64_t           p_device[4];                    /* 0x0002{00,,,18} */
+    uint64_t           _pad_000220[4];                 /* 0x0002{20,,,38} */
+    uint64_t           p_wr_req_buf[4];                /* 0x0002{40,,,58} */
+    uint64_t           _pad_000260[4];                 /* 0x0002{60,,,78} */
+    uint64_t           p_rrb_map[2];                   /* 0x0002{80,,,88} */
+    #define p_even_resp p_rrb_map[0]                   /* 0x000280 */
+    #define p_odd_resp  p_rrb_map[1]                   /* 0x000288 */
+    uint64_t           p_resp_status;                  /* 0x000290 */
+    uint64_t           p_resp_clear;                   /* 0x000298 */
+
+    uint64_t           _pad_0002A0[12];                /* 0x0002{A0..F8} */
+
+    /* 0x000300-0x0003F8 -- Buffer Address Match Registers */
+    struct {
+       uint64_t        upper;                          /* 0x0003{00,,,F0} */
+       uint64_t        lower;                          /* 0x0003{08,,,F8} */
+    } p_buf_addr_match[16];
+
+    /* 0x000400-0x0005FF -- Performance Monitor Registers (even only) */
+    struct {
+       uint64_t        flush_w_touch;                  /* 0x000{400,,,5C0} */
+       uint64_t        flush_wo_touch;                 /* 0x000{408,,,5C8} */
+       uint64_t        inflight;                       /* 0x000{410,,,5D0} */
+       uint64_t        prefetch;                       /* 0x000{418,,,5D8} */
+       uint64_t        total_pci_retry;                /* 0x000{420,,,5E0} */
+       uint64_t        max_pci_retry;                  /* 0x000{428,,,5E8} */
+       uint64_t        max_latency;                    /* 0x000{430,,,5F0} */
+       uint64_t        clear_all;                      /* 0x000{438,,,5F8} */
+    } p_buf_count[8];
+
+    
+    /* 0x000600-0x0009FF -- PCI/X registers */
+    uint64_t           p_pcix_bus_err_addr;            /* 0x000600 */
+    uint64_t           p_pcix_bus_err_attr;            /* 0x000608 */
+    uint64_t           p_pcix_bus_err_data;            /* 0x000610 */
+    uint64_t           p_pcix_pio_split_addr;          /* 0x000618 */
+    uint64_t           p_pcix_pio_split_attr;          /* 0x000620 */
+    uint64_t           p_pcix_dma_req_err_attr;        /* 0x000628 */
+    uint64_t           p_pcix_dma_req_err_addr;        /* 0x000630 */
+    uint64_t           p_pcix_timeout;                 /* 0x000638 */
+
+    uint64_t           _pad_000640[120];               /* 0x000{640,,,9F8} */
+
+    /* 0x000A00-0x000BFF -- PCI/X Read&Write Buffer */
+    struct {
+       uint64_t        p_buf_addr;                     /* 0x000{A00,,,AF0} */
+       uint64_t        p_buf_attr;                     /* 0X000{A08,,,AF8} */
+    } p_pcix_read_buf_64[16];
+
+    struct {
+       uint64_t        p_buf_addr;                     /* 0x000{B00,,,BE0} */
+       uint64_t        p_buf_attr;                     /* 0x000{B08,,,BE8} */
+       uint64_t        p_buf_valid;                    /* 0x000{B10,,,BF0} */
+       uint64_t        __pad1;                         /* 0x000{B18,,,BF8} */
+    } p_pcix_write_buf_64[8];
+
+    /* End of Local Registers -- Start of Address Map space */
+
+    char               _pad_000c00[0x010000 - 0x000c00];
+
+    /* 0x010000-0x011fff -- Internal ATE RAM (Auto Parity Generation) */
+    uint64_t           p_int_ate_ram[1024];            /* 0x010000-0x011fff */
+
+    /* 0x012000-0x013fff -- Internal ATE RAM (Manual Parity Generation) */
+    uint64_t           p_int_ate_ram_mp[1024];         /* 0x012000-0x013fff */
+
+    char               _pad_014000[0x18000 - 0x014000];
+
+    /* 0x18000-0x197F8 -- PIC Write Request Ram */
+    uint64_t           p_wr_req_lower[256];            /* 0x18000 - 0x187F8 */
+    uint64_t           p_wr_req_upper[256];            /* 0x18800 - 0x18FF8 */
+    uint64_t           p_wr_req_parity[256];           /* 0x19000 - 0x197F8 */
+
+    char               _pad_019800[0x20000 - 0x019800];
+
+    /* 0x020000-0x027FFF -- PCI Device Configuration Spaces */
+    union {
+       uint8_t         c[0x1000 / 1];                  /* 0x02{0000,,,7FFF} */
+       uint16_t        s[0x1000 / 2];                  /* 0x02{0000,,,7FFF} */
+       uint32_t        l[0x1000 / 4];                  /* 0x02{0000,,,7FFF} */
+       uint64_t        d[0x1000 / 8];                  /* 0x02{0000,,,7FFF} */
+       union {
+           uint8_t     c[0x100 / 1];
+           uint16_t    s[0x100 / 2];
+           uint32_t    l[0x100 / 4];
+           uint64_t    d[0x100 / 8];
+       } f[8];
+    } p_type0_cfg_dev[8];                              /* 0x02{0000,,,7FFF} */
+
+    /* 0x028000-0x028FFF -- PCI Type 1 Configuration Space */
+    union {
+       uint8_t         c[0x1000 / 1];                  /* 0x028000-0x029000 */
+       uint16_t        s[0x1000 / 2];                  /* 0x028000-0x029000 */
+       uint32_t        l[0x1000 / 4];                  /* 0x028000-0x029000 */
+       uint64_t        d[0x1000 / 8];                  /* 0x028000-0x029000 */
+       union {
+           uint8_t     c[0x100 / 1];
+           uint16_t    s[0x100 / 2];
+           uint32_t    l[0x100 / 4];
+           uint64_t    d[0x100 / 8];
+       } f[8];
+    } p_type1_cfg;                                     /* 0x028000-0x029000 */
+
+    char               _pad_029000[0x030000-0x029000];
+
+    /* 0x030000-0x030007 -- PCI Interrupt Acknowledge Cycle */
+    union {
+       uint8_t         c[8 / 1];
+       uint16_t        s[8 / 2];
+       uint32_t        l[8 / 4];
+       uint64_t        d[8 / 8];
+    } p_pci_iack;                                      /* 0x030000-0x030007 */
+
+    char               _pad_030007[0x040000-0x030008];
+
+    /* 0x040000-0x030007 -- PCIX Special Cycle */
+    union {
+       uint8_t         c[8 / 1];
+       uint16_t        s[8 / 2];
+       uint32_t        l[8 / 4];
+       uint64_t        d[8 / 8];
+    } p_pcix_cycle;                                    /* 0x040000-0x040007 */
+};
+
+#endif                          /* _ASM_IA64_SN_PCI_PIC_H */
diff --git a/arch/ia64/sn/include/pci/tiocp.h b/arch/ia64/sn/include/pci/tiocp.h
new file mode 100644 (file)
index 0000000..f07c83b
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+#ifndef _ASM_IA64_SN_PCI_TIOCP_H
+#define _ASM_IA64_SN_PCI_TIOCP_H
+
+#define TIOCP_HOST_INTR_ADDR            0x003FFFFFFFFFFFFFUL
+#define TIOCP_PCI64_CMDTYPE_MEM         (0x1ull << 60)
+
+
+/*****************************************************************************
+ *********************** TIOCP MMR structure mapping ***************************
+ *****************************************************************************/
+
+struct tiocp{
+
+    /* 0x000000-0x00FFFF -- Local Registers */
+
+    /* 0x000000-0x000057 -- (Legacy Widget Space) Configuration */
+    uint64_t           cp_id;                          /* 0x000000 */
+    uint64_t           cp_stat;                        /* 0x000008 */
+    uint64_t           cp_err_upper;                   /* 0x000010 */
+    uint64_t           cp_err_lower;                   /* 0x000018 */
+    #define cp_err cp_err_lower
+    uint64_t           cp_control;                     /* 0x000020 */
+    uint64_t           cp_req_timeout;                 /* 0x000028 */
+    uint64_t           cp_intr_upper;                  /* 0x000030 */
+    uint64_t           cp_intr_lower;                  /* 0x000038 */
+    #define cp_intr cp_intr_lower
+    uint64_t           cp_err_cmdword;                 /* 0x000040 */
+    uint64_t           _pad_000048;                    /* 0x000048 */
+    uint64_t           cp_tflush;                      /* 0x000050 */
+
+    /* 0x000058-0x00007F -- Bridge-specific Configuration */
+    uint64_t           cp_aux_err;                     /* 0x000058 */
+    uint64_t           cp_resp_upper;                  /* 0x000060 */
+    uint64_t           cp_resp_lower;                  /* 0x000068 */
+    #define cp_resp cp_resp_lower
+    uint64_t           cp_tst_pin_ctrl;                /* 0x000070 */
+    uint64_t           cp_addr_lkerr;                  /* 0x000078 */
+
+    /* 0x000080-0x00008F -- PMU & MAP */
+    uint64_t           cp_dir_map;                     /* 0x000080 */
+    uint64_t           _pad_000088;                    /* 0x000088 */
+
+    /* 0x000090-0x00009F -- SSRAM */
+    uint64_t           cp_map_fault;                   /* 0x000090 */
+    uint64_t           _pad_000098;                    /* 0x000098 */
+
+    /* 0x0000A0-0x0000AF -- Arbitration */
+    uint64_t           cp_arb;                         /* 0x0000A0 */
+    uint64_t           _pad_0000A8;                    /* 0x0000A8 */
+
+    /* 0x0000B0-0x0000BF -- Number In A Can or ATE Parity Error */
+    uint64_t           cp_ate_parity_err;              /* 0x0000B0 */
+    uint64_t           _pad_0000B8;                    /* 0x0000B8 */
+
+    /* 0x0000C0-0x0000FF -- PCI/GIO */
+    uint64_t           cp_bus_timeout;                 /* 0x0000C0 */
+    uint64_t           cp_pci_cfg;                     /* 0x0000C8 */
+    uint64_t           cp_pci_err_upper;               /* 0x0000D0 */
+    uint64_t           cp_pci_err_lower;               /* 0x0000D8 */
+    #define cp_pci_err cp_pci_err_lower
+    uint64_t           _pad_0000E0[4];                 /* 0x0000{E0..F8} */
+
+    /* 0x000100-0x0001FF -- Interrupt */
+    uint64_t           cp_int_status;                  /* 0x000100 */
+    uint64_t           cp_int_enable;                  /* 0x000108 */
+    uint64_t           cp_int_rst_stat;                /* 0x000110 */
+    uint64_t           cp_int_mode;                    /* 0x000118 */
+    uint64_t           cp_int_device;                  /* 0x000120 */
+    uint64_t           cp_int_host_err;                /* 0x000128 */
+    uint64_t           cp_int_addr[8];                 /* 0x0001{30,,,68} */
+    uint64_t           cp_err_int_view;                /* 0x000170 */
+    uint64_t           cp_mult_int;                    /* 0x000178 */
+    uint64_t           cp_force_always[8];             /* 0x0001{80,,,B8} */
+    uint64_t           cp_force_pin[8];                /* 0x0001{C0,,,F8} */
+
+    /* 0x000200-0x000298 -- Device */
+    uint64_t           cp_device[4];                   /* 0x0002{00,,,18} */
+    uint64_t           _pad_000220[4];                 /* 0x0002{20,,,38} */
+    uint64_t           cp_wr_req_buf[4];               /* 0x0002{40,,,58} */
+    uint64_t           _pad_000260[4];                 /* 0x0002{60,,,78} */
+    uint64_t           cp_rrb_map[2];                  /* 0x0002{80,,,88} */
+    #define cp_even_resp cp_rrb_map[0]                 /* 0x000280 */
+    #define cp_odd_resp  cp_rrb_map[1]                 /* 0x000288 */
+    uint64_t           cp_resp_status;                 /* 0x000290 */
+    uint64_t           cp_resp_clear;                  /* 0x000298 */
+
+    uint64_t           _pad_0002A0[12];                /* 0x0002{A0..F8} */
+
+    /* 0x000300-0x0003F8 -- Buffer Address Match Registers */
+    struct {
+       uint64_t        upper;                          /* 0x0003{00,,,F0} */
+       uint64_t        lower;                          /* 0x0003{08,,,F8} */
+    } cp_buf_addr_match[16];
+
+    /* 0x000400-0x0005FF -- Performance Monitor Registers (even only) */
+    struct {
+       uint64_t        flush_w_touch;                  /* 0x000{400,,,5C0} */
+       uint64_t        flush_wo_touch;                 /* 0x000{408,,,5C8} */
+       uint64_t        inflight;                       /* 0x000{410,,,5D0} */
+       uint64_t        prefetch;                       /* 0x000{418,,,5D8} */
+       uint64_t        total_pci_retry;                /* 0x000{420,,,5E0} */
+       uint64_t        max_pci_retry;                  /* 0x000{428,,,5E8} */
+       uint64_t        max_latency;                    /* 0x000{430,,,5F0} */
+       uint64_t        clear_all;                      /* 0x000{438,,,5F8} */
+    } cp_buf_count[8];
+
+    
+    /* 0x000600-0x0009FF -- PCI/X registers */
+    uint64_t           cp_pcix_bus_err_addr;           /* 0x000600 */
+    uint64_t           cp_pcix_bus_err_attr;           /* 0x000608 */
+    uint64_t           cp_pcix_bus_err_data;           /* 0x000610 */
+    uint64_t           cp_pcix_pio_split_addr;         /* 0x000618 */
+    uint64_t           cp_pcix_pio_split_attr;         /* 0x000620 */
+    uint64_t           cp_pcix_dma_req_err_attr;       /* 0x000628 */
+    uint64_t           cp_pcix_dma_req_err_addr;       /* 0x000630 */
+    uint64_t           cp_pcix_timeout;                /* 0x000638 */
+
+    uint64_t           _pad_000640[24];                /* 0x000{640,,,6F8} */
+
+    /* 0x000700-0x000737 -- Debug Registers */
+    uint64_t           cp_ct_debug_ctl;                /* 0x000700 */
+    uint64_t           cp_br_debug_ctl;                /* 0x000708 */
+    uint64_t           cp_mux3_debug_ctl;              /* 0x000710 */
+    uint64_t           cp_mux4_debug_ctl;              /* 0x000718 */
+    uint64_t           cp_mux5_debug_ctl;              /* 0x000720 */
+    uint64_t           cp_mux6_debug_ctl;              /* 0x000728 */
+    uint64_t           cp_mux7_debug_ctl;              /* 0x000730 */
+
+    uint64_t           _pad_000738[89];                /* 0x000{738,,,9F8} */
+
+    /* 0x000A00-0x000BFF -- PCI/X Read&Write Buffer */
+    struct {
+       uint64_t        cp_buf_addr;                    /* 0x000{A00,,,AF0} */
+       uint64_t        cp_buf_attr;                    /* 0X000{A08,,,AF8} */
+    } cp_pcix_read_buf_64[16];
+
+    struct {
+       uint64_t        cp_buf_addr;                    /* 0x000{B00,,,BE0} */
+       uint64_t        cp_buf_attr;                    /* 0x000{B08,,,BE8} */
+       uint64_t        cp_buf_valid;                   /* 0x000{B10,,,BF0} */
+       uint64_t        __pad1;                         /* 0x000{B18,,,BF8} */
+    } cp_pcix_write_buf_64[8];
+
+    /* End of Local Registers -- Start of Address Map space */
+
+    char               _pad_000c00[0x010000 - 0x000c00];
+
+    /* 0x010000-0x011FF8 -- Internal ATE RAM (Auto Parity Generation) */
+    uint64_t           cp_int_ate_ram[1024];           /* 0x010000-0x011FF8 */
+
+    char               _pad_012000[0x14000 - 0x012000];
+
+    /* 0x014000-0x015FF8 -- Internal ATE RAM (Manual Parity Generation) */
+    uint64_t           cp_int_ate_ram_mp[1024];        /* 0x014000-0x015FF8 */
+
+    char               _pad_016000[0x18000 - 0x016000];
+
+    /* 0x18000-0x197F8 -- TIOCP Write Request Ram */
+    uint64_t           cp_wr_req_lower[256];           /* 0x18000 - 0x187F8 */
+    uint64_t           cp_wr_req_upper[256];           /* 0x18800 - 0x18FF8 */
+    uint64_t           cp_wr_req_parity[256];          /* 0x19000 - 0x197F8 */
+
+    char               _pad_019800[0x1C000 - 0x019800];
+
+    /* 0x1C000-0x1EFF8 -- TIOCP Read Response Ram */
+    uint64_t           cp_rd_resp_lower[512];          /* 0x1C000 - 0x1CFF8 */
+    uint64_t           cp_rd_resp_upper[512];          /* 0x1D000 - 0x1DFF8 */
+    uint64_t           cp_rd_resp_parity[512];         /* 0x1E000 - 0x1EFF8 */
+
+    char               _pad_01F000[0x20000 - 0x01F000];
+
+    /* 0x020000-0x021FFF -- Host Device (CP) Configuration Space (not used)  */
+    char               _pad_020000[0x021000 - 0x20000];
+
+    /* 0x021000-0x027FFF -- PCI Device Configuration Spaces */
+    union {
+       uint8_t         c[0x1000 / 1];                  /* 0x02{0000,,,7FFF} */
+       uint16_t        s[0x1000 / 2];                  /* 0x02{0000,,,7FFF} */
+       uint32_t        l[0x1000 / 4];                  /* 0x02{0000,,,7FFF} */
+       uint64_t        d[0x1000 / 8];                  /* 0x02{0000,,,7FFF} */
+       union {
+           uint8_t     c[0x100 / 1];
+           uint16_t    s[0x100 / 2];
+           uint32_t    l[0x100 / 4];
+           uint64_t    d[0x100 / 8];
+       } f[8];
+    } cp_type0_cfg_dev[7];                             /* 0x02{1000,,,7FFF} */
+
+    /* 0x028000-0x028FFF -- PCI Type 1 Configuration Space */
+    union {
+       uint8_t         c[0x1000 / 1];                  /* 0x028000-0x029000 */
+       uint16_t        s[0x1000 / 2];                  /* 0x028000-0x029000 */
+       uint32_t        l[0x1000 / 4];                  /* 0x028000-0x029000 */
+       uint64_t        d[0x1000 / 8];                  /* 0x028000-0x029000 */
+       union {
+           uint8_t     c[0x100 / 1];
+           uint16_t    s[0x100 / 2];
+           uint32_t    l[0x100 / 4];
+           uint64_t    d[0x100 / 8];
+       } f[8];
+    } cp_type1_cfg;                                    /* 0x028000-0x029000 */
+
+    char               _pad_029000[0x030000-0x029000];
+
+    /* 0x030000-0x030007 -- PCI Interrupt Acknowledge Cycle */
+    union {
+       uint8_t         c[8 / 1];
+       uint16_t        s[8 / 2];
+       uint32_t        l[8 / 4];
+       uint64_t        d[8 / 8];
+    } cp_pci_iack;                                     /* 0x030000-0x030007 */
+
+    char               _pad_030007[0x040000-0x030008];
+
+    /* 0x040000-0x040007 -- PCIX Special Cycle */
+    union {
+       uint8_t         c[8 / 1];
+       uint16_t        s[8 / 2];
+       uint32_t        l[8 / 4];
+       uint64_t        d[8 / 8];
+    } cp_pcix_cycle;                                   /* 0x040000-0x040007 */
+
+    char               _pad_040007[0x200000-0x040008];
+
+    /* 0x200000-0x7FFFFF -- PCI/GIO Device Spaces */
+    union {
+       uint8_t         c[0x100000 / 1];
+       uint16_t        s[0x100000 / 2];
+       uint32_t        l[0x100000 / 4];
+       uint64_t        d[0x100000 / 8];
+    } cp_devio_raw[6];                                 /* 0x200000-0x7FFFFF */
+
+    #define cp_devio(n)  cp_devio_raw[((n)<2)?(n*2):(n+2)]
+
+    char               _pad_800000[0xA00000-0x800000];
+
+    /* 0xA00000-0xBFFFFF -- PCI/GIO Device Spaces w/flush  */
+    union {
+       uint8_t         c[0x100000 / 1];
+       uint16_t        s[0x100000 / 2];
+       uint32_t        l[0x100000 / 4];
+       uint64_t        d[0x100000 / 8];
+    } cp_devio_raw_flush[6];                           /* 0xA00000-0xBFFFFF */
+
+    #define cp_devio_flush(n)  cp_devio_raw_flush[((n)<2)?(n*2):(n+2)]
+
+};
+
+#endif         /* _ASM_IA64_SN_PCI_TIOCP_H */
diff --git a/arch/ia64/sn/include/tio.h b/arch/ia64/sn/include/tio.h
new file mode 100644 (file)
index 0000000..0139124
--- /dev/null
@@ -0,0 +1,37 @@
+/* 
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#ifndef _ASM_IA64_SN_TIO_H
+#define _ASM_IA64_SN_TIO_H
+
+#define        TIO_MMR_ADDR_MOD
+
+#define TIO_NODE_ID     TIO_MMR_ADDR_MOD(0x0000000090060e80)
+
+#define TIO_ITTE_BASE   0xb0008800        /* base of translation table entries */
+#define TIO_ITTE(bigwin)        (TIO_ITTE_BASE + 8*(bigwin))
+
+#define TIO_ITTE_OFFSET_BITS    8       /* size of offset field */
+#define TIO_ITTE_OFFSET_MASK    ((1<<TIO_ITTE_OFFSET_BITS)-1)
+#define TIO_ITTE_OFFSET_SHIFT   0
+
+#define TIO_ITTE_WIDGET_BITS    2       /* size of widget field */
+#define TIO_ITTE_WIDGET_MASK    ((1<<TIO_ITTE_WIDGET_BITS)-1)
+#define TIO_ITTE_WIDGET_SHIFT   12
+#define TIO_ITTE_VALID_MASK    0x1
+#define TIO_ITTE_VALID_SHIFT   16
+
+
+#define TIO_ITTE_PUT(nasid, bigwin, widget, addr, valid) \
+        REMOTE_HUB_S((nasid), TIO_ITTE(bigwin), \
+                (((((addr) >> TIO_BWIN_SIZE_BITS) & \
+                   TIO_ITTE_OFFSET_MASK) << TIO_ITTE_OFFSET_SHIFT) | \
+                (((widget) & TIO_ITTE_WIDGET_MASK) << TIO_ITTE_WIDGET_SHIFT)) | \
+               (( (valid) & TIO_ITTE_VALID_MASK) << TIO_ITTE_VALID_SHIFT))
+
+#endif /*  _ASM_IA64_SN_TIO_H */
diff --git a/arch/ia64/sn/include/xtalk/hubdev.h b/arch/ia64/sn/include/xtalk/hubdev.h
new file mode 100644 (file)
index 0000000..868e7ec
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+#ifndef _ASM_IA64_SN_XTALK_HUBDEV_H
+#define _ASM_IA64_SN_XTALK_HUBDEV_H
+
+#define HUB_WIDGET_ID_MAX 0xf
+#define DEV_PER_WIDGET (2*2*8)
+#define IIO_ITTE_WIDGET_BITS    4       /* size of widget field */
+#define IIO_ITTE_WIDGET_MASK    ((1<<IIO_ITTE_WIDGET_BITS)-1)
+#define IIO_ITTE_WIDGET_SHIFT   8
+
+/*
+ * Use the top big window as a surrogate for the first small window
+ */
+#define SWIN0_BIGWIN            HUB_NUM_BIG_WINDOW
+#define IIO_NUM_ITTES   7
+#define HUB_NUM_BIG_WINDOW      (IIO_NUM_ITTES - 1)
+
+struct sn_flush_device_list {
+       int sfdl_bus;
+       int sfdl_slot;
+       int sfdl_pin;
+       struct bar_list {
+               unsigned long start;
+               unsigned long end;
+       } sfdl_bar_list[6];
+       unsigned long sfdl_force_int_addr;
+       unsigned long sfdl_flush_value;
+       volatile unsigned long *sfdl_flush_addr;
+       uint64_t sfdl_persistent_busnum;
+       struct pcibus_info *sfdl_pcibus_info;
+       spinlock_t sfdl_flush_lock;
+};
+
+/*
+ * **widget_p - Used as an array[wid_num][device] of sn_flush_device_list.
+ */
+struct sn_flush_nasid_entry  {
+       struct sn_flush_device_list **widget_p; /* Used as a array of wid_num */
+       uint64_t iio_itte[8];
+};
+
+struct hubdev_info {
+       geoid_t                         hdi_geoid;
+       short                           hdi_nasid;
+       short                           hdi_peer_nasid;   /* Dual Porting Peer */
+
+       struct sn_flush_nasid_entry     hdi_flush_nasid_list;
+       struct xwidget_info             hdi_xwidget_info[HUB_WIDGET_ID_MAX + 1];
+
+
+       void                            *hdi_nodepda;
+       void                            *hdi_node_vertex;
+       void                            *hdi_xtalk_vertex;
+};
+
+extern void hubdev_init_node(nodepda_t *, cnodeid_t);
+extern void hub_error_init(struct hubdev_info *);
+extern void ice_error_init(struct hubdev_info *);
+
+
+#endif /* _ASM_IA64_SN_XTALK_HUBDEV_H */
diff --git a/arch/ia64/sn/include/xtalk/xbow.h b/arch/ia64/sn/include/xtalk/xbow.h
new file mode 100644 (file)
index 0000000..ec56b34
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992-1997,2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ */
+#ifndef _ASM_IA64_SN_XTALK_XBOW_H
+#define _ASM_IA64_SN_XTALK_XBOW_H
+
+#define XBOW_PORT_8    0x8
+#define XBOW_PORT_C    0xc
+#define XBOW_PORT_F    0xf
+
+#define MAX_XBOW_PORTS 8       /* number of ports on xbow chip */
+#define BASE_XBOW_PORT XBOW_PORT_8     /* Lowest external port */
+
+#define        XBOW_CREDIT     4
+
+#define MAX_XBOW_NAME  16
+
+/* Register set for each xbow link */
+typedef volatile struct xb_linkregs_s {
+/* 
+ * we access these through synergy unswizzled space, so the address
+ * gets twiddled (i.e. references to 0x4 actually go to 0x0 and vv.)
+ * That's why we put the register first and filler second.
+ */
+    uint32_t               link_ibf;
+    uint32_t               filler0;    /* filler for proper alignment */
+    uint32_t               link_control;
+    uint32_t               filler1;
+    uint32_t               link_status;
+    uint32_t               filler2;
+    uint32_t               link_arb_upper;
+    uint32_t               filler3;
+    uint32_t               link_arb_lower;
+    uint32_t               filler4;
+    uint32_t               link_status_clr;
+    uint32_t               filler5;
+    uint32_t               link_reset;
+    uint32_t               filler6;
+    uint32_t               link_aux_status;
+    uint32_t               filler7;
+} xb_linkregs_t;
+
+typedef volatile struct xbow_s {
+    /* standard widget configuration                       0x000000-0x000057 */
+    struct widget_cfg            xb_widget;  /* 0x000000 */
+
+    /* helper fieldnames for accessing bridge widget */
+
+#define xb_wid_id                       xb_widget.w_id
+#define xb_wid_stat                     xb_widget.w_status
+#define xb_wid_err_upper                xb_widget.w_err_upper_addr
+#define xb_wid_err_lower                xb_widget.w_err_lower_addr
+#define xb_wid_control                  xb_widget.w_control
+#define xb_wid_req_timeout              xb_widget.w_req_timeout
+#define xb_wid_int_upper                xb_widget.w_intdest_upper_addr
+#define xb_wid_int_lower                xb_widget.w_intdest_lower_addr
+#define xb_wid_err_cmdword              xb_widget.w_err_cmd_word
+#define xb_wid_llp                      xb_widget.w_llp_cfg
+#define xb_wid_stat_clr                 xb_widget.w_tflush
+
+/* 
+ * we access these through synergy unswizzled space, so the address
+ * gets twiddled (i.e. references to 0x4 actually go to 0x0 and vv.)
+ * That's why we put the register first and filler second.
+ */
+    /* xbow-specific widget configuration                  0x000058-0x0000FF */
+    uint32_t               xb_wid_arb_reload;  /* 0x00005C */
+    uint32_t               _pad_000058;
+    uint32_t               xb_perf_ctr_a;      /* 0x000064 */
+    uint32_t               _pad_000060;
+    uint32_t               xb_perf_ctr_b;      /* 0x00006c */
+    uint32_t               _pad_000068;
+    uint32_t               xb_nic;     /* 0x000074 */
+    uint32_t               _pad_000070;
+
+    /* Xbridge only */
+    uint32_t               xb_w0_rst_fnc;      /* 0x00007C */
+    uint32_t               _pad_000078;
+    uint32_t               xb_l8_rst_fnc;      /* 0x000084 */
+    uint32_t               _pad_000080;
+    uint32_t               xb_l9_rst_fnc;      /* 0x00008c */
+    uint32_t               _pad_000088;
+    uint32_t               xb_la_rst_fnc;      /* 0x000094 */
+    uint32_t               _pad_000090;
+    uint32_t               xb_lb_rst_fnc;      /* 0x00009c */
+    uint32_t               _pad_000098;
+    uint32_t               xb_lc_rst_fnc;      /* 0x0000a4 */
+    uint32_t               _pad_0000a0;
+    uint32_t               xb_ld_rst_fnc;      /* 0x0000ac */
+    uint32_t               _pad_0000a8;
+    uint32_t               xb_le_rst_fnc;      /* 0x0000b4 */
+    uint32_t               _pad_0000b0;
+    uint32_t               xb_lf_rst_fnc;      /* 0x0000bc */
+    uint32_t               _pad_0000b8;
+    uint32_t               xb_lock;            /* 0x0000c4 */
+    uint32_t               _pad_0000c0;
+    uint32_t               xb_lock_clr;        /* 0x0000cc */
+    uint32_t               _pad_0000c8;
+    /* end of Xbridge only */
+    uint32_t               _pad_0000d0[12];
+
+    /* Link Specific Registers, port 8..15                 0x000100-0x000300 */
+    xb_linkregs_t           xb_link_raw[MAX_XBOW_PORTS];
+#define xb_link(p)      xb_link_raw[(p) & (MAX_XBOW_PORTS - 1)]
+
+} xbow_t;
+
+#define XB_FLAGS_EXISTS                0x1     /* device exists */
+#define XB_FLAGS_MASTER                0x2
+#define XB_FLAGS_SLAVE         0x0
+#define XB_FLAGS_GBR           0x4
+#define XB_FLAGS_16BIT         0x8
+#define XB_FLAGS_8BIT          0x0
+
+/* is widget port number valid?  (based on version 7.0 of xbow spec) */
+#define XBOW_WIDGET_IS_VALID(wid) ((wid) >= XBOW_PORT_8 && (wid) <= XBOW_PORT_F)
+
+/* whether to use upper or lower arbitration register, given source widget id */
+#define XBOW_ARB_IS_UPPER(wid)         ((wid) >= XBOW_PORT_8 && (wid) <= XBOW_PORT_B)
+#define XBOW_ARB_IS_LOWER(wid)         ((wid) >= XBOW_PORT_C && (wid) <= XBOW_PORT_F)
+
+/* offset of arbitration register, given source widget id */
+#define XBOW_ARB_OFF(wid)      (XBOW_ARB_IS_UPPER(wid) ? 0x1c : 0x24)
+
+#define        XBOW_WID_ID             WIDGET_ID
+#define        XBOW_WID_STAT           WIDGET_STATUS
+#define        XBOW_WID_ERR_UPPER      WIDGET_ERR_UPPER_ADDR
+#define        XBOW_WID_ERR_LOWER      WIDGET_ERR_LOWER_ADDR
+#define        XBOW_WID_CONTROL        WIDGET_CONTROL
+#define        XBOW_WID_REQ_TO         WIDGET_REQ_TIMEOUT
+#define        XBOW_WID_INT_UPPER      WIDGET_INTDEST_UPPER_ADDR
+#define        XBOW_WID_INT_LOWER      WIDGET_INTDEST_LOWER_ADDR
+#define        XBOW_WID_ERR_CMDWORD    WIDGET_ERR_CMD_WORD
+#define        XBOW_WID_LLP            WIDGET_LLP_CFG
+#define        XBOW_WID_STAT_CLR       WIDGET_TFLUSH
+#define XBOW_WID_ARB_RELOAD    0x5c
+#define XBOW_WID_PERF_CTR_A    0x64
+#define XBOW_WID_PERF_CTR_B    0x6c
+#define XBOW_WID_NIC           0x74
+
+/* Xbridge only */
+#define XBOW_W0_RST_FNC                0x00007C
+#define        XBOW_L8_RST_FNC         0x000084
+#define        XBOW_L9_RST_FNC         0x00008c
+#define        XBOW_LA_RST_FNC         0x000094
+#define        XBOW_LB_RST_FNC         0x00009c
+#define        XBOW_LC_RST_FNC         0x0000a4
+#define        XBOW_LD_RST_FNC         0x0000ac
+#define        XBOW_LE_RST_FNC         0x0000b4
+#define        XBOW_LF_RST_FNC         0x0000bc
+#define XBOW_RESET_FENCE(x) ((x) > 7 && (x) < 16) ? \
+                               (XBOW_W0_RST_FNC + ((x) - 7) * 8) : \
+                               ((x) == 0) ? XBOW_W0_RST_FNC : 0
+#define XBOW_LOCK              0x0000c4
+#define XBOW_LOCK_CLR          0x0000cc
+/* End of Xbridge only */
+
+/* used only in ide, but defined here within the reserved portion */
+/*              of the widget0 address space (before 0xf4) */
+#define        XBOW_WID_UNDEF          0xe4
+
+/* xbow link register set base, legal value for x is 0x8..0xf */
+#define        XB_LINK_BASE            0x100
+#define        XB_LINK_OFFSET          0x40
+#define        XB_LINK_REG_BASE(x)     (XB_LINK_BASE + ((x) & (MAX_XBOW_PORTS - 1)) * XB_LINK_OFFSET)
+
+#define        XB_LINK_IBUF_FLUSH(x)   (XB_LINK_REG_BASE(x) + 0x4)
+#define        XB_LINK_CTRL(x)         (XB_LINK_REG_BASE(x) + 0xc)
+#define        XB_LINK_STATUS(x)       (XB_LINK_REG_BASE(x) + 0x14)
+#define        XB_LINK_ARB_UPPER(x)    (XB_LINK_REG_BASE(x) + 0x1c)
+#define        XB_LINK_ARB_LOWER(x)    (XB_LINK_REG_BASE(x) + 0x24)
+#define        XB_LINK_STATUS_CLR(x)   (XB_LINK_REG_BASE(x) + 0x2c)
+#define        XB_LINK_RESET(x)        (XB_LINK_REG_BASE(x) + 0x34)
+#define        XB_LINK_AUX_STATUS(x)   (XB_LINK_REG_BASE(x) + 0x3c)
+
+/* link_control(x) */
+#define        XB_CTRL_LINKALIVE_IE            0x80000000      /* link comes alive */
+     /* reserved:                      0x40000000 */
+#define        XB_CTRL_PERF_CTR_MODE_MSK       0x30000000      /* perf counter mode */
+#define        XB_CTRL_IBUF_LEVEL_MSK          0x0e000000      /* input packet buffer level */
+#define        XB_CTRL_8BIT_MODE               0x01000000      /* force link into 8 bit mode */
+#define XB_CTRL_BAD_LLP_PKT            0x00800000      /* force bad LLP packet */
+#define XB_CTRL_WIDGET_CR_MSK          0x007c0000      /* LLP widget credit mask */
+#define XB_CTRL_WIDGET_CR_SHFT 18                      /* LLP widget credit shift */
+#define XB_CTRL_ILLEGAL_DST_IE         0x00020000      /* illegal destination */
+#define XB_CTRL_OALLOC_IBUF_IE         0x00010000      /* overallocated input buffer */
+     /* reserved:                      0x0000fe00 */
+#define XB_CTRL_BNDWDTH_ALLOC_IE       0x00000100      /* bandwidth alloc */
+#define XB_CTRL_RCV_CNT_OFLOW_IE       0x00000080      /* rcv retry overflow */
+#define XB_CTRL_XMT_CNT_OFLOW_IE       0x00000040      /* xmt retry overflow */
+#define XB_CTRL_XMT_MAX_RTRY_IE                0x00000020      /* max transmit retry */
+#define XB_CTRL_RCV_IE                 0x00000010      /* receive */
+#define XB_CTRL_XMT_RTRY_IE            0x00000008      /* transmit retry */
+     /* reserved:                      0x00000004 */
+#define        XB_CTRL_MAXREQ_TOUT_IE          0x00000002      /* maximum request timeout */
+#define        XB_CTRL_SRC_TOUT_IE             0x00000001      /* source timeout */
+
+/* link_status(x) */
+#define        XB_STAT_LINKALIVE               XB_CTRL_LINKALIVE_IE
+     /* reserved:                      0x7ff80000 */
+#define        XB_STAT_MULTI_ERR               0x00040000      /* multi error */
+#define        XB_STAT_ILLEGAL_DST_ERR         XB_CTRL_ILLEGAL_DST_IE
+#define        XB_STAT_OALLOC_IBUF_ERR         XB_CTRL_OALLOC_IBUF_IE
+#define        XB_STAT_BNDWDTH_ALLOC_ID_MSK    0x0000ff00      /* port bitmask */
+#define        XB_STAT_RCV_CNT_OFLOW_ERR       XB_CTRL_RCV_CNT_OFLOW_IE
+#define        XB_STAT_XMT_CNT_OFLOW_ERR       XB_CTRL_XMT_CNT_OFLOW_IE
+#define        XB_STAT_XMT_MAX_RTRY_ERR        XB_CTRL_XMT_MAX_RTRY_IE
+#define        XB_STAT_RCV_ERR                 XB_CTRL_RCV_IE
+#define        XB_STAT_XMT_RTRY_ERR            XB_CTRL_XMT_RTRY_IE
+     /* reserved:                      0x00000004 */
+#define        XB_STAT_MAXREQ_TOUT_ERR         XB_CTRL_MAXREQ_TOUT_IE
+#define        XB_STAT_SRC_TOUT_ERR            XB_CTRL_SRC_TOUT_IE
+
+/* link_aux_status(x) */
+#define        XB_AUX_STAT_RCV_CNT     0xff000000
+#define        XB_AUX_STAT_XMT_CNT     0x00ff0000
+#define        XB_AUX_STAT_TOUT_DST    0x0000ff00
+#define        XB_AUX_LINKFAIL_RST_BAD 0x00000040
+#define        XB_AUX_STAT_PRESENT     0x00000020
+#define        XB_AUX_STAT_PORT_WIDTH  0x00000010
+     /*        reserved:               0x0000000f */
+
+/*
+ * link_arb_upper/link_arb_lower(x), (reg) should be the link_arb_upper
+ * register if (x) is 0x8..0xb, link_arb_lower if (x) is 0xc..0xf
+ */
+#define        XB_ARB_GBR_MSK          0x1f
+#define        XB_ARB_RR_MSK           0x7
+#define        XB_ARB_GBR_SHFT(x)      (((x) & 0x3) * 8)
+#define        XB_ARB_RR_SHFT(x)       (((x) & 0x3) * 8 + 5)
+#define        XB_ARB_GBR_CNT(reg,x)   ((reg) >> XB_ARB_GBR_SHFT(x) & XB_ARB_GBR_MSK)
+#define        XB_ARB_RR_CNT(reg,x)    ((reg) >> XB_ARB_RR_SHFT(x) & XB_ARB_RR_MSK)
+
+/* XBOW_WID_STAT */
+#define        XB_WID_STAT_LINK_INTR_SHFT      (24)
+#define        XB_WID_STAT_LINK_INTR_MASK      (0xFF << XB_WID_STAT_LINK_INTR_SHFT)
+#define        XB_WID_STAT_LINK_INTR(x)        (0x1 << (((x)&7) + XB_WID_STAT_LINK_INTR_SHFT))
+#define        XB_WID_STAT_WIDGET0_INTR        0x00800000
+#define XB_WID_STAT_SRCID_MASK         0x000003c0      /* Xbridge only */
+#define        XB_WID_STAT_REG_ACC_ERR         0x00000020
+#define XB_WID_STAT_RECV_TOUT          0x00000010      /* Xbridge only */
+#define XB_WID_STAT_ARB_TOUT           0x00000008      /* Xbridge only */
+#define        XB_WID_STAT_XTALK_ERR           0x00000004
+#define XB_WID_STAT_DST_TOUT           0x00000002      /* Xbridge only */
+#define        XB_WID_STAT_MULTI_ERR           0x00000001
+
+#define XB_WID_STAT_SRCID_SHFT         6
+
+/* XBOW_WID_CONTROL */
+#define XB_WID_CTRL_REG_ACC_IE         XB_WID_STAT_REG_ACC_ERR
+#define XB_WID_CTRL_RECV_TOUT          XB_WID_STAT_RECV_TOUT
+#define XB_WID_CTRL_ARB_TOUT           XB_WID_STAT_ARB_TOUT
+#define XB_WID_CTRL_XTALK_IE           XB_WID_STAT_XTALK_ERR
+
+/* XBOW_WID_INT_UPPER */
+/* defined in xwidget.h for WIDGET_INTDEST_UPPER_ADDR */
+
+/* XBOW WIDGET part number, in the ID register */
+#define XBOW_WIDGET_PART_NUM   0x0             /* crossbow */
+#define XXBOW_WIDGET_PART_NUM  0xd000          /* Xbridge */
+#define        XBOW_WIDGET_MFGR_NUM    0x0
+#define        XXBOW_WIDGET_MFGR_NUM   0x0
+#define PXBOW_WIDGET_PART_NUM   0xd100          /* PIC */
+
+#define        XBOW_REV_1_0            0x1     /* xbow rev 1.0 is "1" */
+#define        XBOW_REV_1_1            0x2     /* xbow rev 1.1 is "2" */
+#define XBOW_REV_1_2           0x3     /* xbow rev 1.2 is "3" */
+#define XBOW_REV_1_3           0x4     /* xbow rev 1.3 is "4" */
+#define XBOW_REV_2_0           0x5     /* xbow rev 2.0 is "5" */
+
+#define XXBOW_PART_REV_1_0             (XXBOW_WIDGET_PART_NUM << 4 | 0x1 )
+#define XXBOW_PART_REV_2_0             (XXBOW_WIDGET_PART_NUM << 4 | 0x2 )
+
+/* XBOW_WID_ARB_RELOAD */
+#define        XBOW_WID_ARB_RELOAD_INT 0x3f    /* GBR reload interval */
+
+#define IS_XBRIDGE_XBOW(wid) \
+        (XWIDGET_PART_NUM(wid) == XXBOW_WIDGET_PART_NUM && \
+                        XWIDGET_MFG_NUM(wid) == XXBOW_WIDGET_MFGR_NUM)
+
+#define IS_PIC_XBOW(wid) \
+        (XWIDGET_PART_NUM(wid) == PXBOW_WIDGET_PART_NUM && \
+                        XWIDGET_MFG_NUM(wid) == XXBOW_WIDGET_MFGR_NUM)
+
+#define XBOW_WAR_ENABLED(pv, widid) ((1 << XWIDGET_REV_NUM(widid)) & pv)
+
+#endif                          /* _ASM_IA64_SN_XTALK_XBOW_H */
diff --git a/arch/ia64/sn/include/xtalk/xwidgetdev.h b/arch/ia64/sn/include/xtalk/xwidgetdev.h
new file mode 100644 (file)
index 0000000..c5f4bc5
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992-1997,2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ */
+#ifndef _ASM_IA64_SN_XTALK_XWIDGET_H
+#define _ASM_IA64_SN_XTALK_XWIDGET_H
+
+/* WIDGET_ID */
+#define WIDGET_REV_NUM                  0xf0000000
+#define WIDGET_PART_NUM                 0x0ffff000
+#define WIDGET_MFG_NUM                  0x00000ffe
+#define WIDGET_REV_NUM_SHFT             28
+#define WIDGET_PART_NUM_SHFT            12
+#define WIDGET_MFG_NUM_SHFT             1
+
+#define XWIDGET_PART_NUM(widgetid) (((widgetid) & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT)
+#define XWIDGET_REV_NUM(widgetid) (((widgetid) & WIDGET_REV_NUM) >> WIDGET_REV_NUM_SHFT)
+#define XWIDGET_MFG_NUM(widgetid) (((widgetid) & WIDGET_MFG_NUM) >> WIDGET_MFG_NUM_SHFT)
+#define XWIDGET_PART_REV_NUM(widgetid) ((XWIDGET_PART_NUM(widgetid) << 4) | \
+                                        XWIDGET_REV_NUM(widgetid))
+#define XWIDGET_PART_REV_NUM_REV(partrev) (partrev & 0xf)
+
+/* widget configuration registers */
+struct widget_cfg{
+       uint32_t        w_id;   /* 0x04 */
+       uint32_t        w_pad_0;        /* 0x00 */
+       uint32_t        w_status;       /* 0x0c */
+       uint32_t        w_pad_1;        /* 0x08 */
+       uint32_t        w_err_upper_addr;       /* 0x14 */
+       uint32_t        w_pad_2;        /* 0x10 */
+       uint32_t        w_err_lower_addr;       /* 0x1c */
+       uint32_t        w_pad_3;        /* 0x18 */
+       uint32_t        w_control;      /* 0x24 */
+       uint32_t        w_pad_4;        /* 0x20 */
+       uint32_t        w_req_timeout;  /* 0x2c */
+       uint32_t        w_pad_5;        /* 0x28 */
+       uint32_t        w_intdest_upper_addr;   /* 0x34 */
+       uint32_t        w_pad_6;        /* 0x30 */
+       uint32_t        w_intdest_lower_addr;   /* 0x3c */
+       uint32_t        w_pad_7;        /* 0x38 */
+       uint32_t        w_err_cmd_word; /* 0x44 */
+       uint32_t        w_pad_8;        /* 0x40 */
+       uint32_t        w_llp_cfg;      /* 0x4c */
+       uint32_t        w_pad_9;        /* 0x48 */
+       uint32_t        w_tflush;       /* 0x54 */
+       uint32_t        w_pad_10;       /* 0x50 */
+};
+
+/*
+ * Crosstalk Widget Hardware Identification, as defined in the Crosstalk spec.
+ */
+struct xwidget_hwid{
+       int             mfg_num;
+       int             rev_num;
+       int             part_num;
+};
+
+struct xwidget_info{
+
+       struct xwidget_hwid     xwi_hwid;       /* Widget Identification */
+       char                    xwi_masterxid;  /* Hub's Widget Port Number */
+       void                    *xwi_hubinfo;     /* Hub's provider private info */
+       uint64_t                *xwi_hub_provider; /* prom provider functions */
+       void                    *xwi_vertex;
+};
+
+#endif                          /* _ASM_IA64_SN_XTALK_XWIDGET_H */
diff --git a/arch/ia64/sn/kernel/bte_error.c b/arch/ia64/sn/kernel/bte_error.c
new file mode 100644 (file)
index 0000000..3591c2c
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+#include <linux/types.h>
+#include <asm/sn/sn_sal.h>
+#include "ioerror.h"
+#include <asm/sn/addrs.h>
+#include "shubio.h"
+#include <asm/sn/geo.h>
+#include "xtalk/xwidgetdev.h"
+#include "xtalk/hubdev.h"
+#include <asm/sn/bte.h>
+
+/*
+ * Bte error handling is done in two parts.  The first captures
+ * any crb related errors.  Since there can be multiple crbs per
+ * interface and multiple interfaces active, we need to wait until
+ * all active crbs are completed.  This is the first job of the
+ * second part error handler.  When all bte related CRBs are cleanly
+ * completed, it resets the interfaces and gets them ready for new
+ * transfers to be queued.
+ */
+
+void bte_error_handler(unsigned long);
+
+/*
+ * Wait until all BTE related CRBs are completed
+ * and then reset the interfaces.
+ */
+void bte_error_handler(unsigned long _nodepda)
+{
+       struct nodepda_s *err_nodepda = (struct nodepda_s *)_nodepda;
+       spinlock_t *recovery_lock = &err_nodepda->bte_recovery_lock;
+       struct timer_list *recovery_timer = &err_nodepda->bte_recovery_timer;
+       nasid_t nasid;
+       int i;
+       int valid_crbs;
+       unsigned long irq_flags;
+       volatile u64 *notify;
+       bte_result_t bh_error;
+       ii_imem_u_t imem;       /* II IMEM Register */
+       ii_icrb0_d_u_t icrbd;   /* II CRB Register D */
+       ii_ibcr_u_t ibcr;
+       ii_icmr_u_t icmr;
+
+       BTE_PRINTK(("bte_error_handler(%p) - %d\n", err_nodepda,
+                   smp_processor_id()));
+
+       spin_lock_irqsave(recovery_lock, irq_flags);
+
+       if ((err_nodepda->bte_if[0].bh_error == BTE_SUCCESS) &&
+           (err_nodepda->bte_if[1].bh_error == BTE_SUCCESS)) {
+               BTE_PRINTK(("eh:%p:%d Nothing to do.\n", err_nodepda,
+                           smp_processor_id()));
+               spin_unlock_irqrestore(recovery_lock, irq_flags);
+               return;
+       }
+       /*
+        * Lock all interfaces on this node to prevent new transfers
+        * from being queued.
+        */
+       for (i = 0; i < BTES_PER_NODE; i++) {
+               if (err_nodepda->bte_if[i].cleanup_active) {
+                       continue;
+               }
+               spin_lock(&err_nodepda->bte_if[i].spinlock);
+               BTE_PRINTK(("eh:%p:%d locked %d\n", err_nodepda,
+                           smp_processor_id(), i));
+               err_nodepda->bte_if[i].cleanup_active = 1;
+       }
+
+       /* Determine information about our hub */
+       nasid = cnodeid_to_nasid(err_nodepda->bte_if[0].bte_cnode);
+
+       /*
+        * A BTE transfer can use multiple CRBs.  We need to make sure
+        * that all the BTE CRBs are complete (or timed out) before
+        * attempting to clean up the error.  Resetting the BTE while
+        * there are still BTE CRBs active will hang the BTE.
+        * We should look at all the CRBs to see if they are allocated
+        * to the BTE and see if they are still active.  When none
+        * are active, we can continue with the cleanup.
+        *
+        * We also want to make sure that the local NI port is up.
+        * When a router resets the NI port can go down, while it
+        * goes through the LLP handshake, but then comes back up.
+        */
+       icmr.ii_icmr_regval = REMOTE_HUB_L(nasid, IIO_ICMR);
+       if (icmr.ii_icmr_fld_s.i_crb_mark != 0) {
+               /*
+                * There are errors which still need to be cleaned up by
+                * hubiio_crb_error_handler
+                */
+               mod_timer(recovery_timer, HZ * 5);
+               BTE_PRINTK(("eh:%p:%d Marked Giving up\n", err_nodepda,
+                           smp_processor_id()));
+               spin_unlock_irqrestore(recovery_lock, irq_flags);
+               return;
+       }
+       if (icmr.ii_icmr_fld_s.i_crb_vld != 0) {
+
+               valid_crbs = icmr.ii_icmr_fld_s.i_crb_vld;
+
+               for (i = 0; i < IIO_NUM_CRBS; i++) {
+                       if (!((1 << i) & valid_crbs)) {
+                               /* This crb was not marked as valid, ignore */
+                               continue;
+                       }
+                       icrbd.ii_icrb0_d_regval =
+                           REMOTE_HUB_L(nasid, IIO_ICRB_D(i));
+                       if (icrbd.d_bteop) {
+                               mod_timer(recovery_timer, HZ * 5);
+                               BTE_PRINTK(("eh:%p:%d Valid %d, Giving up\n",
+                                           err_nodepda, smp_processor_id(),
+                                           i));
+                               spin_unlock_irqrestore(recovery_lock,
+                                                      irq_flags);
+                               return;
+                       }
+               }
+       }
+
+       BTE_PRINTK(("eh:%p:%d Cleaning up\n", err_nodepda, smp_processor_id()));
+       /* Reenable both bte interfaces */
+       imem.ii_imem_regval = REMOTE_HUB_L(nasid, IIO_IMEM);
+       imem.ii_imem_fld_s.i_b0_esd = imem.ii_imem_fld_s.i_b1_esd = 1;
+       REMOTE_HUB_S(nasid, IIO_IMEM, imem.ii_imem_regval);
+
+       /* Reinitialize both BTE state machines. */
+       ibcr.ii_ibcr_regval = REMOTE_HUB_L(nasid, IIO_IBCR);
+       ibcr.ii_ibcr_fld_s.i_soft_reset = 1;
+       REMOTE_HUB_S(nasid, IIO_IBCR, ibcr.ii_ibcr_regval);
+
+       for (i = 0; i < BTES_PER_NODE; i++) {
+               bh_error = err_nodepda->bte_if[i].bh_error;
+               if (bh_error != BTE_SUCCESS) {
+                       /* There is an error which needs to be notified */
+                       notify = err_nodepda->bte_if[i].most_rcnt_na;
+                       BTE_PRINTK(("cnode %d bte %d error=0x%lx\n",
+                                   err_nodepda->bte_if[i].bte_cnode,
+                                   err_nodepda->bte_if[i].bte_num,
+                                   IBLS_ERROR | (u64) bh_error));
+                       *notify = IBLS_ERROR | bh_error;
+                       err_nodepda->bte_if[i].bh_error = BTE_SUCCESS;
+               }
+
+               err_nodepda->bte_if[i].cleanup_active = 0;
+               BTE_PRINTK(("eh:%p:%d Unlocked %d\n", err_nodepda,
+                           smp_processor_id(), i));
+               spin_unlock(&pda->cpu_bte_if[i]->spinlock);
+       }
+
+       del_timer(recovery_timer);
+
+       spin_unlock_irqrestore(recovery_lock, irq_flags);
+}
+
+/*
+ * First part error handler.  This is called whenever any error CRB interrupt
+ * is generated by the II.
+ */
+void
+bte_crb_error_handler(cnodeid_t cnode, int btenum,
+                      int crbnum, ioerror_t * ioe, int bteop)
+{
+       struct bteinfo_s *bte;
+
+
+       bte = &(NODEPDA(cnode)->bte_if[btenum]);
+
+       /*
+        * The caller has already figured out the error type, we save that
+        * in the bte handle structure for the thread excercising the
+        * interface to consume.
+        */
+       bte->bh_error = ioe->ie_errortype + BTEFAIL_OFFSET;
+       bte->bte_error_count++;
+
+       BTE_PRINTK(("Got an error on cnode %d bte %d: HW error type 0x%x\n",
+               bte->bte_cnode, bte->bte_num, ioe->ie_errortype));
+       bte_error_handler((unsigned long) NODEPDA(cnode));
+}
+
diff --git a/arch/ia64/sn/kernel/huberror.c b/arch/ia64/sn/kernel/huberror.c
new file mode 100644 (file)
index 0000000..1180604
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000,2002-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <asm/delay.h>
+#include <asm/sn/sn_sal.h>
+#include "ioerror.h"
+#include <asm/sn/addrs.h>
+#include "shubio.h"
+#include <asm/sn/geo.h>
+#include "xtalk/xwidgetdev.h"
+#include "xtalk/hubdev.h"
+#include <asm/sn/bte.h>
+
+void hubiio_crb_error_handler(struct hubdev_info *hubdev_info);
+extern void bte_crb_error_handler(cnodeid_t, int, int, ioerror_t *,
+                                 int);
+static irqreturn_t hub_eint_handler(int irq, void *arg, struct pt_regs *ep)
+{
+       struct hubdev_info *hubdev_info;
+       struct ia64_sal_retval ret_stuff;
+       nasid_t nasid;
+
+       ret_stuff.status = 0;
+       ret_stuff.v0 = 0;
+       hubdev_info = (struct hubdev_info *)arg;
+       nasid = hubdev_info->hdi_nasid;
+       SAL_CALL_NOLOCK(ret_stuff, SN_SAL_HUB_ERROR_INTERRUPT,
+                       (u64) nasid, 0, 0, 0, 0, 0, 0);
+
+       if ((int)ret_stuff.v0)
+               panic("hubii_eint_handler(): Fatal TIO Error");
+
+       if (!(nasid & 1)) /* Not a TIO, handle CRB errors */
+               (void)hubiio_crb_error_handler(hubdev_info);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Free the hub CRB "crbnum" which encountered an error.
+ * Assumption is, error handling was successfully done,
+ * and we now want to return the CRB back to Hub for normal usage.
+ *
+ * In order to free the CRB, all that's needed is to de-allocate it
+ *
+ * Assumption:
+ *      No other processor is mucking around with the hub control register.
+ *      So, upper layer has to single thread this.
+ */
+void hubiio_crb_free(struct hubdev_info *hubdev_info, int crbnum)
+{
+       ii_icrb0_b_u_t icrbb;
+
+       /*
+        * The hardware does NOT clear the mark bit, so it must get cleared
+        * here to be sure the error is not processed twice.
+        */
+       icrbb.ii_icrb0_b_regval = REMOTE_HUB_L(hubdev_info->hdi_nasid,
+                                              IIO_ICRB_B(crbnum));
+       icrbb.b_mark = 0;
+       REMOTE_HUB_S(hubdev_info->hdi_nasid, IIO_ICRB_B(crbnum),
+                    icrbb.ii_icrb0_b_regval);
+       /*
+        * Deallocate the register wait till hub indicates it's done.
+        */
+       REMOTE_HUB_S(hubdev_info->hdi_nasid, IIO_ICDR, (IIO_ICDR_PND | crbnum));
+       while (REMOTE_HUB_L(hubdev_info->hdi_nasid, IIO_ICDR) & IIO_ICDR_PND)
+               udelay(1);
+
+}
+
+/*
+ * hubiio_crb_error_handler
+ *
+ *     This routine gets invoked when a hub gets an error 
+ *     interrupt. So, the routine is running in interrupt context
+ *     at error interrupt level.
+ * Action:
+ *     It's responsible for identifying ALL the CRBs that are marked
+ *     with error, and process them. 
+ *     
+ *     If you find the CRB that's marked with error, map this to the
+ *     reason it caused error, and invoke appropriate error handler.
+ *
+ *     XXX Be aware of the information in the context register.
+ *
+ * NOTE:
+ *     Use REMOTE_HUB_* macro instead of LOCAL_HUB_* so that the interrupt
+ *     handler can be run on any node. (not necessarily the node 
+ *     corresponding to the hub that encountered error).
+ */
+
+void hubiio_crb_error_handler(struct hubdev_info *hubdev_info)
+{
+       nasid_t nasid;
+       ii_icrb0_a_u_t icrba;   /* II CRB Register A */
+       ii_icrb0_b_u_t icrbb;   /* II CRB Register B */
+       ii_icrb0_c_u_t icrbc;   /* II CRB Register C */
+       ii_icrb0_d_u_t icrbd;   /* II CRB Register D */
+       ii_icrb0_e_u_t icrbe;   /* II CRB Register D */
+       int i;
+       int num_errors = 0;     /* Num of errors handled */
+       ioerror_t ioerror;
+
+       nasid = hubdev_info->hdi_nasid;
+
+       /*
+        * XXX - Add locking for any recovery actions
+        */
+       /*
+        * Scan through all CRBs in the Hub, and handle the errors
+        * in any of the CRBs marked.
+        */
+       for (i = 0; i < IIO_NUM_CRBS; i++) {
+               /* Check this crb entry to see if it is in error. */
+               icrbb.ii_icrb0_b_regval = REMOTE_HUB_L(nasid, IIO_ICRB_B(i));
+
+               if (icrbb.b_mark == 0) {
+                       continue;
+               }
+
+               icrba.ii_icrb0_a_regval = REMOTE_HUB_L(nasid, IIO_ICRB_A(i));
+
+               IOERROR_INIT(&ioerror);
+
+               /* read other CRB error registers. */
+               icrbc.ii_icrb0_c_regval = REMOTE_HUB_L(nasid, IIO_ICRB_C(i));
+               icrbd.ii_icrb0_d_regval = REMOTE_HUB_L(nasid, IIO_ICRB_D(i));
+               icrbe.ii_icrb0_e_regval = REMOTE_HUB_L(nasid, IIO_ICRB_E(i));
+
+               IOERROR_SETVALUE(&ioerror, errortype, icrbb.b_ecode);
+
+               /* Check if this error is due to BTE operation,
+                * and handle it separately.
+                */
+               if (icrbd.d_bteop ||
+                   ((icrbb.b_initiator == IIO_ICRB_INIT_BTE0 ||
+                     icrbb.b_initiator == IIO_ICRB_INIT_BTE1) &&
+                    (icrbb.b_imsgtype == IIO_ICRB_IMSGT_BTE ||
+                     icrbb.b_imsgtype == IIO_ICRB_IMSGT_SN1NET))) {
+
+                       int bte_num;
+
+                       if (icrbd.d_bteop)
+                               bte_num = icrbc.c_btenum;
+                       else    /* b_initiator bit 2 gives BTE number */
+                               bte_num = (icrbb.b_initiator & 0x4) >> 2;
+
+                       hubiio_crb_free(hubdev_info, i);
+
+                       bte_crb_error_handler(nasid_to_cnodeid(nasid), bte_num,
+                                             i, &ioerror, icrbd.d_bteop);
+                       num_errors++;
+                       continue;
+               }
+       }
+}
+
+/*
+ * Function    : hub_error_init
+ * Purpose     : initialize the error handling requirements for a given hub.
+ * Parameters  : cnode, the compact nodeid.
+ * Assumptions : Called only once per hub, either by a local cpu. Or by a
+ *                     remote cpu, when this hub is headless.(cpuless)
+ * Returns     : None
+ */
+void hub_error_init(struct hubdev_info *hubdev_info)
+{
+       if (request_irq(SGI_II_ERROR, (void *)hub_eint_handler, SA_SHIRQ,
+                       "SN_hub_error", (void *)hubdev_info))
+               printk("hub_error_init: Failed to request_irq for 0x%p\n",
+                   hubdev_info);
+       return;
+}
+
+
+/*
+ * Function    : ice_error_init
+ * Purpose     : initialize the error handling requirements for a given tio.
+ * Parameters  : cnode, the compact nodeid.
+ * Assumptions : Called only once per tio.
+ * Returns     : None
+ */
+void ice_error_init(struct hubdev_info *hubdev_info)
+{
+        if (request_irq
+            (SGI_TIO_ERROR, (void *)hub_eint_handler, SA_SHIRQ, "SN_TIO_error",
+             (void *)hubdev_info))
+                printk("ice_error_init: request_irq() error hubdev_info 0x%p\n",
+                       hubdev_info);
+        return;
+}
+
diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c
new file mode 100644 (file)
index 0000000..abb0f42
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/bootmem.h>
+#include <asm/sn/types.h>
+#include <asm/sn/sn_sal.h>
+#include <asm/sn/addrs.h>
+#include "pci/pcibus_provider_defs.h"
+#include "pci/pcidev.h"
+#include "pci/pcibr_provider.h"
+#include "xtalk/xwidgetdev.h"
+#include <asm/sn/geo.h>
+#include "xtalk/hubdev.h"
+#include <asm/sn/io.h>
+#include <asm/sn/simulator.h>
+
+char master_baseio_wid;
+nasid_t master_nasid = INVALID_NASID;  /* Partition Master */
+
+struct slab_info {
+       struct hubdev_info hubdev;
+};
+
+struct brick {
+       moduleid_t id;          /* Module ID of this module        */
+       struct slab_info slab_info[MAX_SLABS + 1];
+};
+
+int sn_ioif_inited = 0;                /* SN I/O infrastructure initialized? */
+
+/*
+ * Retrieve the DMA Flush List given nasid.  This list is needed 
+ * to implement the WAR - Flush DMA data on PIO Reads.
+ */
+static inline uint64_t
+sal_get_widget_dmaflush_list(u64 nasid, u64 widget_num, u64 address)
+{
+
+       struct ia64_sal_retval ret_stuff;
+       ret_stuff.status = 0;
+       ret_stuff.v0 = 0;
+
+       SAL_CALL_NOLOCK(ret_stuff,
+                       (u64) SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST,
+                       (u64) nasid, (u64) widget_num, (u64) address, 0, 0, 0,
+                       0);
+       return ret_stuff.v0;
+
+}
+
+/*
+ * Retrieve the hub device info structure for the given nasid.
+ */
+static inline uint64_t sal_get_hubdev_info(u64 handle, u64 address)
+{
+
+       struct ia64_sal_retval ret_stuff;
+       ret_stuff.status = 0;
+       ret_stuff.v0 = 0;
+
+       SAL_CALL_NOLOCK(ret_stuff,
+                       (u64) SN_SAL_IOIF_GET_HUBDEV_INFO,
+                       (u64) handle, (u64) address, 0, 0, 0, 0, 0);
+       return ret_stuff.v0;
+}
+
+/*
+ * Retrieve the pci bus information given the bus number.
+ */
+static inline uint64_t sal_get_pcibus_info(u64 segment, u64 busnum, u64 address)
+{
+
+       struct ia64_sal_retval ret_stuff;
+       ret_stuff.status = 0;
+       ret_stuff.v0 = 0;
+
+       SAL_CALL_NOLOCK(ret_stuff,
+                       (u64) SN_SAL_IOIF_GET_PCIBUS_INFO,
+                       (u64) segment, (u64) busnum, (u64) address, 0, 0, 0, 0);
+       return ret_stuff.v0;
+}
+
+/*
+ * Retrieve the pci device information given the bus and device|function number.
+ */
+static inline uint64_t
+sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, 
+                       u64 sn_irq_info)
+{
+       struct ia64_sal_retval ret_stuff;
+       ret_stuff.status = 0;
+       ret_stuff.v0 = 0;
+
+       SAL_CALL_NOLOCK(ret_stuff,
+                       (u64) SN_SAL_IOIF_GET_PCIDEV_INFO,
+                       (u64) segment, (u64) bus_number, (u64) devfn, 
+                       (u64) pci_dev,
+                       sn_irq_info, 0, 0);
+       return ret_stuff.v0;
+}
+
+/*
+ * sn_alloc_pci_sysdata() - This routine allocates a pci controller
+ *     which is expected as the pci_dev and pci_bus sysdata by the Linux
+ *     PCI infrastructure.
+ */
+static inline struct pci_controller *sn_alloc_pci_sysdata(void)
+{
+       struct pci_controller *pci_sysdata;
+
+       pci_sysdata = kmalloc(sizeof(*pci_sysdata), GFP_KERNEL);
+       if (!pci_sysdata)
+               BUG();
+
+       memset(pci_sysdata, 0, sizeof(*pci_sysdata));
+       return pci_sysdata;
+}
+
+/*
+ * sn_fixup_ionodes() - This routine initializes the HUB data strcuture for 
+ *     each node in the system.
+ */
+static void sn_fixup_ionodes(void)
+{
+
+       struct sn_flush_device_list *sn_flush_device_list;
+       struct hubdev_info *hubdev;
+       uint64_t status;
+       uint64_t nasid;
+       int i, widget;
+
+       for (i = 0; i < numionodes; i++) {
+               hubdev = (struct hubdev_info *)(NODEPDA(i)->pdinfo);
+               nasid = cnodeid_to_nasid(i);
+               status = sal_get_hubdev_info(nasid, (uint64_t) __pa(hubdev));
+               if (status)
+                       continue;
+
+               for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++)
+                       hubdev->hdi_xwidget_info[widget].xwi_hubinfo = hubdev;
+
+               if (!hubdev->hdi_flush_nasid_list.widget_p)
+                       continue;
+
+               hubdev->hdi_flush_nasid_list.widget_p =
+                   kmalloc((HUB_WIDGET_ID_MAX + 1) *
+                           sizeof(struct sn_flush_device_list *), GFP_KERNEL);
+
+               memset(hubdev->hdi_flush_nasid_list.widget_p, 0x0,
+                      (HUB_WIDGET_ID_MAX + 1) *
+                      sizeof(struct sn_flush_device_list *));
+
+               for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) {
+                       sn_flush_device_list = kmalloc(DEV_PER_WIDGET *
+                                                      sizeof(struct
+                                                             sn_flush_device_list),
+                                                      GFP_KERNEL);
+                       memset(sn_flush_device_list, 0x0,
+                              DEV_PER_WIDGET *
+                              sizeof(struct sn_flush_device_list));
+
+                       status =
+                           sal_get_widget_dmaflush_list(nasid, widget,
+                                                        (uint64_t)
+                                                        __pa
+                                                        (sn_flush_device_list));
+                       if (status) {
+                               kfree(sn_flush_device_list);
+                               continue;
+                       }
+
+                       hubdev->hdi_flush_nasid_list.widget_p[widget] =
+                           sn_flush_device_list;
+               }
+
+               if (!(i & 1))
+                       hub_error_init(hubdev);
+               else
+                       ice_error_init(hubdev);
+       }
+
+}
+
+/*
+ * sn_pci_fixup_slot() - This routine sets up a slot's resources
+ * consistent with the Linux PCI abstraction layer.  Resources acquired
+ * from our PCI provider include PIO maps to BAR space and interrupt
+ * objects.
+ */
+static void sn_pci_fixup_slot(struct pci_dev *dev)
+{
+       int idx;
+       int segment = 0;
+       uint64_t size;
+       struct sn_irq_info *sn_irq_info;
+       struct pci_dev *host_pci_dev;
+       int status = 0;
+
+       SN_PCIDEV_INFO(dev) = kmalloc(sizeof(struct pcidev_info), GFP_KERNEL);
+       if (SN_PCIDEV_INFO(dev) <= 0)
+               BUG();          /* Cannot afford to run out of memory */
+       memset(SN_PCIDEV_INFO(dev), 0, sizeof(struct pcidev_info));
+
+       sn_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
+       if (sn_irq_info <= 0)
+               BUG();          /* Cannot afford to run out of memory */
+       memset(sn_irq_info, 0, sizeof(struct sn_irq_info));
+
+       /* Call to retrieve pci device information needed by kernel. */
+       status = sal_get_pcidev_info((u64) segment, (u64) dev->bus->number, 
+                                    dev->devfn,
+                                    (u64) __pa(SN_PCIDEV_INFO(dev)),
+                                    (u64) __pa(sn_irq_info));
+       if (status)
+               BUG();          /* Cannot get platform pci device information information */
+
+       /* Copy over PIO Mapped Addresses */
+       for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) {
+               unsigned long start, end, addr;
+
+               if (!SN_PCIDEV_INFO(dev)->pdi_pio_mapped_addr[idx])
+                       continue;
+
+               start = dev->resource[idx].start;
+               end = dev->resource[idx].end;
+               size = end - start;
+               addr = SN_PCIDEV_INFO(dev)->pdi_pio_mapped_addr[idx];
+               addr = ((addr << 4) >> 4) | __IA64_UNCACHED_OFFSET;
+               dev->resource[idx].start = addr;
+               dev->resource[idx].end = addr + size;
+               if (dev->resource[idx].flags & IORESOURCE_IO)
+                       dev->resource[idx].parent = &ioport_resource;
+               else
+                       dev->resource[idx].parent = &iomem_resource;
+       }
+
+       /* set up host bus linkages */
+       host_pci_dev =
+           pci_find_slot(SN_PCIDEV_INFO(dev)->pdi_slot_host_handle >> 32,
+                         SN_PCIDEV_INFO(dev)->
+                         pdi_slot_host_handle & 0xffffffff);
+       SN_PCIDEV_INFO(dev)->pdi_host_pcidev_info =
+           SN_PCIDEV_INFO(host_pci_dev);
+       SN_PCIDEV_INFO(dev)->pdi_linux_pcidev = dev;
+       SN_PCIDEV_INFO(dev)->pdi_pcibus_info = SN_PCIBUS_BUSSOFT(dev->bus);
+
+       /* Only set up IRQ stuff if this device has a host bus context */
+       if (SN_PCIDEV_BUSSOFT(dev) && sn_irq_info->irq_irq) {
+               SN_PCIDEV_INFO(dev)->pdi_sn_irq_info = sn_irq_info;
+               dev->irq = SN_PCIDEV_INFO(dev)->pdi_sn_irq_info->irq_irq;
+               sn_irq_fixup(dev, sn_irq_info);
+       }
+}
+
+/*
+ * sn_pci_controller_fixup() - This routine sets up a bus's resources
+ * consistent with the Linux PCI abstraction layer.
+ */
+static void sn_pci_controller_fixup(int segment, int busnum)
+{
+       int status = 0;
+       int nasid, cnode;
+       struct pci_bus *bus;
+       struct pci_controller *controller;
+       struct pcibus_bussoft *prom_bussoft_ptr;
+       struct hubdev_info *hubdev_info;
+       void *provider_soft;
+
+       status =
+           sal_get_pcibus_info((u64) segment, (u64) busnum,
+                               (u64) ia64_tpa(&prom_bussoft_ptr));
+       if (status > 0) {
+               return;         /* bus # does not exist */
+       }
+
+       prom_bussoft_ptr = __va(prom_bussoft_ptr);
+       controller = sn_alloc_pci_sysdata();
+       /* controller non-zero is BUG'd in sn_alloc_pci_sysdata */
+
+       bus = pci_scan_bus(busnum, &pci_root_ops, controller);
+       if (bus == NULL) {
+               return;         /* error, or bus already scanned */
+       }
+
+       /*
+        * Per-provider fixup.  Copies the contents from prom to local
+        * area and links SN_PCIBUS_BUSSOFT().
+        *
+        * Note:  Provider is responsible for ensuring that prom_bussoft_ptr
+        * represents an asic-type that it can handle.
+        */
+
+       if (prom_bussoft_ptr->bs_asic_type == PCIIO_ASIC_TYPE_PPB) {
+               return;         /* no further fixup necessary */
+       }
+
+       provider_soft = pcibr_bus_fixup(prom_bussoft_ptr);
+       if (provider_soft == NULL) {
+               return;         /* fixup failed or not applicable */
+       }
+
+       /*
+        * Generic bus fixup goes here.  Don't reference prom_bussoft_ptr
+        * after this point.
+        */
+
+       PCI_CONTROLLER(bus) = controller;
+       SN_PCIBUS_BUSSOFT(bus) = provider_soft;
+
+       nasid = NASID_GET(SN_PCIBUS_BUSSOFT(bus)->bs_base);
+       cnode = nasid_to_cnodeid(nasid);
+       hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo);
+       SN_PCIBUS_BUSSOFT(bus)->bs_xwidget_info =
+           &(hubdev_info->hdi_xwidget_info[SN_PCIBUS_BUSSOFT(bus)->bs_xid]);
+}
+
+/*
+ * Ugly hack to get PCI setup until we have a proper ACPI namespace.
+ */
+
+#define PCI_BUSES_TO_SCAN 256
+
+static int __init sn_pci_init(void)
+{
+       int i = 0;
+       struct pci_dev *pci_dev = NULL;
+       extern void sn_init_cpei_timer(void);
+#ifdef CONFIG_PROC_FS
+       extern void register_sn_procfs(void);
+#endif
+
+       if (!ia64_platform_is("sn2") || IS_RUNNING_ON_SIMULATOR())
+               return 0;
+
+       /*
+        * This is needed to avoid bounce limit checks in the blk layer
+        */
+       ia64_max_iommu_merge_mask = ~PAGE_MASK;
+       sn_fixup_ionodes();
+       sn_irq = kmalloc(sizeof(struct sn_irq_info *) * NR_IRQS, GFP_KERNEL);
+       if (sn_irq <= 0)
+               BUG();          /* Canno afford to run out of memory. */
+       memset(sn_irq, 0, sizeof(struct sn_irq_info *) * NR_IRQS);
+
+       sn_init_cpei_timer();
+
+#ifdef CONFIG_PROC_FS
+       register_sn_procfs();
+#endif
+
+       for (i = 0; i < PCI_BUSES_TO_SCAN; i++) {
+               sn_pci_controller_fixup(0, i);
+       }
+
+       /*
+        * Generic Linux PCI Layer has created the pci_bus and pci_dev 
+        * structures - time for us to add our SN PLatform specific 
+        * information.
+        */
+
+       while ((pci_dev =
+               pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) != NULL) {
+               sn_pci_fixup_slot(pci_dev);
+       }
+
+       sn_ioif_inited = 1;     /* sn I/O infrastructure now initialized */
+
+       return 0;
+}
+
+/*
+ * hubdev_init_node() - Creates the HUB data structure and link them to it's 
+ *     own NODE specific data area.
+ */
+void hubdev_init_node(nodepda_t * npda, cnodeid_t node)
+{
+
+       struct hubdev_info *hubdev_info;
+
+       if (node >= numnodes)   /* Headless/memless IO nodes */
+               hubdev_info =
+                   (struct hubdev_info *)alloc_bootmem_node(NODE_DATA(0),
+                                                            sizeof(struct
+                                                                   hubdev_info));
+       else
+               hubdev_info =
+                   (struct hubdev_info *)alloc_bootmem_node(NODE_DATA(node),
+                                                            sizeof(struct
+                                                                   hubdev_info));
+       npda->pdinfo = (void *)hubdev_info;
+
+}
+
+geoid_t
+cnodeid_get_geoid(cnodeid_t cnode)
+{
+
+       struct hubdev_info *hubdev;
+
+       hubdev = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo);
+       return hubdev->hdi_geoid;
+
+}
+
+subsys_initcall(sn_pci_init);
diff --git a/arch/ia64/sn/kernel/iomv.c b/arch/ia64/sn/kernel/iomv.c
new file mode 100644 (file)
index 0000000..a98d674
--- /dev/null
@@ -0,0 +1,75 @@
+/* 
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/sn/nodepda.h>
+#include <asm/sn/simulator.h>
+#include <asm/sn/pda.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/shub_mmr.h>
+
+/**
+ * sn_io_addr - convert an in/out port to an i/o address
+ * @port: port to convert
+ *
+ * Legacy in/out instructions are converted to ld/st instructions
+ * on IA64.  This routine will convert a port number into a valid 
+ * SN i/o address.  Used by sn_in*() and sn_out*().
+ */
+void *sn_io_addr(unsigned long port)
+{
+       if (!IS_RUNNING_ON_SIMULATOR()) {
+               /* On sn2, legacy I/O ports don't point at anything */
+               if (port < (64 * 1024))
+                       return NULL;
+               return ((void *)(port | __IA64_UNCACHED_OFFSET));
+       } else {
+               /* but the simulator uses them... */
+               unsigned long io_base;
+               unsigned long addr;
+
+               /*
+                * word align port, but need more than 10 bits
+                * for accessing registers in bedrock local block
+                * (so we don't do port&0xfff)
+                */
+               if ((port >= 0x1f0 && port <= 0x1f7) ||
+                   port == 0x3f6 || port == 0x3f7) {
+                       io_base = (0xc000000fcc000000UL |
+                                  ((unsigned long)get_nasid() << 38));
+                       addr = io_base | ((port >> 2) << 12) | (port & 0xfff);
+               } else {
+                       addr = __ia64_get_io_port_base() | ((port >> 2) << 2);
+               }
+               return (void *)addr;
+       }
+}
+
+EXPORT_SYMBOL(sn_io_addr);
+
+/**
+ * __sn_mmiowb - I/O space memory barrier
+ *
+ * See include/asm-ia64/io.h and Documentation/DocBook/deviceiobook.tmpl
+ * for details.
+ *
+ * On SN2, we wait for the PIO_WRITE_STATUS SHub register to clear.
+ * See PV 871084 for details about the WAR about zero value.
+ *
+ */
+void __sn_mmiowb(void)
+{
+       while ((((volatile unsigned long)(*pda->pio_write_status_addr)) &
+               SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_MASK) !=
+              SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_MASK)
+               cpu_relax();
+}
+
+EXPORT_SYMBOL(__sn_mmiowb);
diff --git a/arch/ia64/sn/kernel/klconflib.c b/arch/ia64/sn/kernel/klconflib.c
new file mode 100644 (file)
index 0000000..0f11a32
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <asm/sn/types.h>
+#include <asm/sn/module.h>
+#include <asm/sn/l1.h>
+
+char brick_types[MAX_BRICK_TYPES + 1] = "cri.xdpn%#=vo^kjbf890123456789...";
+/*
+ * Format a module id for printing.
+ *
+ * There are three possible formats:
+ *
+ *   MODULE_FORMAT_BRIEF       is the brief 6-character format, including
+ *                             the actual brick-type as recorded in the 
+ *                             moduleid_t, eg. 002c15 for a C-brick, or
+ *                             101#17 for a PX-brick.
+ *
+ *   MODULE_FORMAT_LONG                is the hwgraph format, eg. rack/002/bay/15
+ *                             of rack/101/bay/17 (note that the brick
+ *                             type does not appear in this format).
+ *
+ *   MODULE_FORMAT_LCD         is like MODULE_FORMAT_BRIEF, except that it
+ *                             ensures that the module id provided appears
+ *                             exactly as it would on the LCD display of
+ *                             the corresponding brick, eg. still 002c15
+ *                             for a C-brick, but 101p17 for a PX-brick.
+ *
+ * maule (9/13/04):  Removed top-level check for (fmt == MODULE_FORMAT_LCD)
+ * making MODULE_FORMAT_LCD equivalent to MODULE_FORMAT_BRIEF.  It was
+ * decided that all callers should assume the returned string should be what
+ * is displayed on the brick L1 LCD.
+ */
+void
+format_module_id(char *buffer, moduleid_t m, int fmt)
+{
+       int rack, position;
+       unsigned char brickchar;
+
+       rack = MODULE_GET_RACK(m);
+       brickchar = MODULE_GET_BTCHAR(m);
+
+       /* Be sure we use the same brick type character as displayed
+        * on the brick's LCD
+        */
+       switch (brickchar) 
+       {
+       case L1_BRICKTYPE_GA:
+       case L1_BRICKTYPE_OPUS_TIO:
+               brickchar = L1_BRICKTYPE_C;
+               break;
+
+       case L1_BRICKTYPE_PX:
+       case L1_BRICKTYPE_PE:
+       case L1_BRICKTYPE_PA:
+       case L1_BRICKTYPE_SA: /* we can move this to the "I's" later
+                              * if that makes more sense
+                              */
+               brickchar = L1_BRICKTYPE_P;
+               break;
+
+       case L1_BRICKTYPE_IX:
+       case L1_BRICKTYPE_IA:
+
+               brickchar = L1_BRICKTYPE_I;
+               break;
+       }
+
+       position = MODULE_GET_BPOS(m);
+
+       if ((fmt == MODULE_FORMAT_BRIEF) || (fmt == MODULE_FORMAT_LCD)) {
+           /* Brief module number format, eg. 002c15 */
+
+           /* Decompress the rack number */
+           *buffer++ = '0' + RACK_GET_CLASS(rack);
+           *buffer++ = '0' + RACK_GET_GROUP(rack);
+           *buffer++ = '0' + RACK_GET_NUM(rack);
+
+           /* Add the brick type */
+           *buffer++ = brickchar;
+       }
+       else if (fmt == MODULE_FORMAT_LONG) {
+           /* Fuller hwgraph format, eg. rack/002/bay/15 */
+
+           strcpy(buffer, "rack" "/");  buffer += strlen(buffer);
+
+           *buffer++ = '0' + RACK_GET_CLASS(rack);
+           *buffer++ = '0' + RACK_GET_GROUP(rack);
+           *buffer++ = '0' + RACK_GET_NUM(rack);
+
+           strcpy(buffer, "/" "bay" "/");  buffer += strlen(buffer);
+       }
+
+       /* Add the bay position, using at least two digits */
+       if (position < 10)
+           *buffer++ = '0';
+       sprintf(buffer, "%d", position);
+
+}
diff --git a/arch/ia64/sn/pci/Makefile b/arch/ia64/sn/pci/Makefile
new file mode 100644 (file)
index 0000000..b5dca00
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+#
+# Makefile for the sn pci general routines.
+
+obj-y := pci_dma.o pcibr/ 
diff --git a/arch/ia64/sn/pci/pci_dma.c b/arch/ia64/sn/pci/pci_dma.c
new file mode 100644 (file)
index 0000000..71f311d
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2000,2002-2004 Silicon Graphics, Inc. All rights reserved.
+ *
+ * Routines for PCI DMA mapping.  See Documentation/DMA-mapping.txt for
+ * a description of how these routines should be used.
+ */
+
+#include <linux/module.h>
+#include <asm/sn/sn_sal.h>
+#include "pci/pcibus_provider_defs.h"
+#include "pci/pcidev.h"
+#include "pci/pcibr_provider.h"
+
+void sn_pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents,
+                    int direction);
+
+/**
+ * sn_pci_alloc_consistent - allocate memory for coherent DMA
+ * @hwdev: device to allocate for
+ * @size: size of the region
+ * @dma_handle: DMA (bus) address
+ *
+ * pci_alloc_consistent() returns a pointer to a memory region suitable for
+ * coherent DMA traffic to/from a PCI device.  On SN platforms, this means
+ * that @dma_handle will have the %PCIIO_DMA_CMD flag set.
+ *
+ * This interface is usually used for "command" streams (e.g. the command
+ * queue for a SCSI controller).  See Documentation/DMA-mapping.txt for
+ * more information.
+ *
+ * Also known as platform_pci_alloc_consistent() by the IA64 machvec code.
+ */
+void *sn_pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
+                             dma_addr_t * dma_handle)
+{
+       void *cpuaddr;
+       unsigned long phys_addr;
+       struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev);
+       struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev);
+
+       if (bussoft == NULL) {
+               return NULL;
+       }
+
+       if (! IS_PCI_BRIDGE_ASIC(bussoft->bs_asic_type)) {
+               return NULL;            /* unsupported asic type */
+       }
+
+       /*
+        * Allocate the memory.
+        * FIXME: We should be doing alloc_pages_node for the node closest
+        *        to the PCI device.
+        */
+       if (!(cpuaddr = (void *)__get_free_pages(GFP_ATOMIC, get_order(size))))
+               return NULL;
+
+       memset(cpuaddr, 0x0, size);
+
+       /* physical addr. of the memory we just got */
+       phys_addr = __pa(cpuaddr);
+
+       /*
+        * 64 bit address translations should never fail.
+        * 32 bit translations can fail if there are insufficient mapping
+        *   resources.
+        */
+
+       *dma_handle = pcibr_dma_map(pcidev_info, phys_addr, size, SN_PCIDMA_CONSISTENT);
+       if (!*dma_handle) {
+               printk(KERN_ERR
+                      "sn_pci_alloc_consistent():  failed  *dma_handle = 0x%lx hwdev->dev.coherent_dma_mask = 0x%lx \n",
+                      *dma_handle, hwdev->dev.coherent_dma_mask);
+               free_pages((unsigned long)cpuaddr, get_order(size));
+               return NULL;
+       }
+
+       return cpuaddr;
+}
+
+/**
+ * sn_pci_free_consistent - free memory associated with coherent DMAable region
+ * @hwdev: device to free for
+ * @size: size to free
+ * @vaddr: kernel virtual address to free
+ * @dma_handle: DMA address associated with this region
+ *
+ * Frees the memory allocated by pci_alloc_consistent().  Also known
+ * as platform_pci_free_consistent() by the IA64 machvec code.
+ */
+void
+sn_pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr,
+                      dma_addr_t dma_handle)
+{
+       struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev);
+       struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev);
+
+       if (! bussoft) {
+               return;
+       }
+
+       pcibr_dma_unmap(pcidev_info, dma_handle, 0);
+       free_pages((unsigned long)vaddr, get_order(size));
+}
+
+/**
+ * sn_pci_map_sg - map a scatter-gather list for DMA
+ * @hwdev: device to map for
+ * @sg: scatterlist to map
+ * @nents: number of entries
+ * @direction: direction of the DMA transaction
+ *
+ * Maps each entry of @sg for DMA.  Also known as platform_pci_map_sg by the
+ * IA64 machvec code.
+ */
+int
+sn_pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents,
+             int direction)
+{
+
+       int i;
+       unsigned long phys_addr;
+       struct scatterlist *saved_sg = sg;
+       struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev);
+       struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev);
+
+       /* can't go anywhere w/o a direction in life */
+       if (direction == PCI_DMA_NONE)
+               BUG();
+
+       if (! bussoft) {
+               return 0;
+       }
+
+       /* SN cannot support DMA addresses smaller than 32 bits. */
+       if (hwdev->dma_mask < 0x7fffffff)
+               return 0;
+
+       /*
+        * Setup a DMA address for each entry in the
+        * scatterlist.
+        */
+       for (i = 0; i < nents; i++, sg++) {
+               phys_addr =
+                   __pa((unsigned long)page_address(sg->page) + sg->offset);
+               sg->dma_address = pcibr_dma_map(pcidev_info, phys_addr, sg->length, 0);
+
+               if (!sg->dma_address) {
+                       printk(KERN_ERR "sn_pci_map_sg: Unable to allocate "
+                              "anymore page map entries.\n");
+                       /*
+                        * We will need to free all previously allocated entries.
+                        */
+                       if (i > 0) {
+                               sn_pci_unmap_sg(hwdev, saved_sg, i, direction);
+                       }
+                       return (0);
+               }
+
+               sg->dma_length = sg->length;
+       }
+
+       return nents;
+
+}
+
+/**
+ * sn_pci_unmap_sg - unmap a scatter-gather list
+ * @hwdev: device to unmap
+ * @sg: scatterlist to unmap
+ * @nents: number of scatterlist entries
+ * @direction: DMA direction
+ *
+ * Unmap a set of streaming mode DMA translations.  Again, cpu read rules
+ * concerning calls here are the same as for pci_unmap_single() below.  Also
+ * known as sn_pci_unmap_sg() by the IA64 machvec code.
+ */
+void
+sn_pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents,
+               int direction)
+{
+       int i;
+       struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev);
+       struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev);
+
+       /* can't go anywhere w/o a direction in life */
+       if (direction == PCI_DMA_NONE)
+               BUG();
+
+       if (! bussoft) {
+               return;
+       }
+
+       for (i = 0; i < nents; i++, sg++) {
+               pcibr_dma_unmap(pcidev_info, sg->dma_address, direction);
+               sg->dma_address = (dma_addr_t) NULL;
+               sg->dma_length = 0;
+       }
+}
+
+/**
+ * sn_pci_map_single - map a single region for DMA
+ * @hwdev: device to map for
+ * @ptr: kernel virtual address of the region to map
+ * @size: size of the region
+ * @direction: DMA direction
+ *
+ * Map the region pointed to by @ptr for DMA and return the
+ * DMA address.   Also known as platform_pci_map_single() by
+ * the IA64 machvec code.
+ *
+ * We map this to the one step pcibr_dmamap_trans interface rather than
+ * the two step pcibr_dmamap_alloc/pcibr_dmamap_addr because we have
+ * no way of saving the dmamap handle from the alloc to later free
+ * (which is pretty much unacceptable).
+ *
+ * TODO: simplify our interface;
+ *       get rid of dev_desc and vhdl (seems redundant given a pci_dev);
+ *       figure out how to save dmamap handle so can use two step.
+ */
+dma_addr_t
+sn_pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, int direction)
+{
+       dma_addr_t dma_addr;
+       unsigned long phys_addr;
+       struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev);
+       struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev);
+
+       if (direction == PCI_DMA_NONE)
+               BUG();
+
+       if (bussoft == NULL) {
+               return 0;
+       }
+
+       if (! IS_PCI_BRIDGE_ASIC(bussoft->bs_asic_type)) {
+               return 0;               /* unsupported asic type */
+       }
+
+       /* SN cannot support DMA addresses smaller than 32 bits. */
+       if (hwdev->dma_mask < 0x7fffffff)
+               return 0;
+
+       /*
+        * Call our dmamap interface
+        */
+
+       phys_addr = __pa(ptr);
+       dma_addr = pcibr_dma_map(pcidev_info, phys_addr, size, 0);
+       if (!dma_addr) {
+               printk(KERN_ERR "pci_map_single: Unable to allocate anymore "
+                      "page map entries.\n");
+               return 0;
+       }
+       return ((dma_addr_t) dma_addr);
+}
+
+/**
+ * sn_pci_dma_sync_single_* - make sure all DMAs or CPU accesses
+ * have completed
+ * @hwdev: device to sync
+ * @dma_handle: DMA address to sync
+ * @size: size of region
+ * @direction: DMA direction
+ *
+ * This routine is supposed to sync the DMA region specified
+ * by @dma_handle into the 'coherence domain'.  We do not need to do
+ * anything on our platform.
+ */
+void
+sn_pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size,
+                   int direction)
+{
+       struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev);
+       struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(hwdev);
+
+       if (direction == PCI_DMA_NONE)
+               BUG();
+
+       if (bussoft == NULL) {
+               return;
+       }
+
+       if (! IS_PCI_BRIDGE_ASIC(bussoft->bs_asic_type)) {
+               return;         /* unsupported asic type */
+       }
+
+       pcibr_dma_unmap(pcidev_info, dma_addr, direction);
+}
+
+/**
+ * sn_dma_supported - test a DMA mask
+ * @hwdev: device to test
+ * @mask: DMA mask to test
+ *
+ * Return whether the given PCI device DMA address mask can be supported
+ * properly.  For example, if your device can only drive the low 24-bits
+ * during PCI bus mastering, then you would pass 0x00ffffff as the mask to
+ * this function.  Of course, SN only supports devices that have 32 or more
+ * address bits when using the PMU.  We could theoretically support <32 bit
+ * cards using direct mapping, but we'll worry about that later--on the off
+ * chance that someone actually wants to use such a card.
+ */
+int sn_pci_dma_supported(struct pci_dev *hwdev, u64 mask)
+{
+       if (mask < 0x7fffffff)
+               return 0;
+       return 1;
+}
+
+/*
+ * New generic DMA routines just wrap sn2 PCI routines until we
+ * support other bus types (if ever).
+ */
+
+int sn_dma_supported(struct device *dev, u64 mask)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+
+       return sn_pci_dma_supported(to_pci_dev(dev), mask);
+}
+
+EXPORT_SYMBOL(sn_dma_supported);
+
+int sn_dma_set_mask(struct device *dev, u64 dma_mask)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+
+       if (!sn_dma_supported(dev, dma_mask))
+               return 0;
+
+       *dev->dma_mask = dma_mask;
+       return 1;
+}
+
+EXPORT_SYMBOL(sn_dma_set_mask);
+
+void *sn_dma_alloc_coherent(struct device *dev, size_t size,
+                           dma_addr_t * dma_handle, int flag)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+
+       return sn_pci_alloc_consistent(to_pci_dev(dev), size, dma_handle);
+}
+
+EXPORT_SYMBOL(sn_dma_alloc_coherent);
+
+void
+sn_dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
+                    dma_addr_t dma_handle)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+
+       sn_pci_free_consistent(to_pci_dev(dev), size, cpu_addr, dma_handle);
+}
+
+EXPORT_SYMBOL(sn_dma_free_coherent);
+
+dma_addr_t
+sn_dma_map_single(struct device *dev, void *cpu_addr, size_t size,
+                 int direction)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+
+       return sn_pci_map_single(to_pci_dev(dev), cpu_addr, size,
+                                (int)direction);
+}
+
+EXPORT_SYMBOL(sn_dma_map_single);
+
+void
+sn_dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+                   int direction)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+
+       sn_pci_unmap_single(to_pci_dev(dev), dma_addr, size, (int)direction);
+}
+
+EXPORT_SYMBOL(sn_dma_unmap_single);
+
+dma_addr_t
+sn_dma_map_page(struct device *dev, struct page *page,
+               unsigned long offset, size_t size, int direction)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+
+       return pci_map_page(to_pci_dev(dev), page, offset, size,
+                           (int)direction);
+}
+
+EXPORT_SYMBOL(sn_dma_map_page);
+
+void
+sn_dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
+                 int direction)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+
+       pci_unmap_page(to_pci_dev(dev), dma_address, size, (int)direction);
+}
+
+EXPORT_SYMBOL(sn_dma_unmap_page);
+
+int
+sn_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+             int direction)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+
+       return sn_pci_map_sg(to_pci_dev(dev), sg, nents, (int)direction);
+}
+
+EXPORT_SYMBOL(sn_dma_map_sg);
+
+void
+sn_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries,
+               int direction)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+
+       sn_pci_unmap_sg(to_pci_dev(dev), sg, nhwentries, (int)direction);
+}
+
+EXPORT_SYMBOL(sn_dma_unmap_sg);
+
+void
+sn_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
+                          size_t size, int direction)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+}
+
+EXPORT_SYMBOL(sn_dma_sync_single_for_cpu);
+
+void
+sn_dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle,
+                             size_t size, int direction)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+}
+
+EXPORT_SYMBOL(sn_dma_sync_single_for_device);
+
+void
+sn_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
+                      int direction)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+}
+
+EXPORT_SYMBOL(sn_dma_sync_sg_for_cpu);
+
+void
+sn_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
+                         int nelems, int direction)
+{
+       BUG_ON(dev->bus != &pci_bus_type);
+}
+
+int sn_dma_mapping_error(dma_addr_t dma_addr)
+{
+       return 0;
+}
+
+EXPORT_SYMBOL(sn_dma_sync_sg_for_device);
+EXPORT_SYMBOL(sn_pci_unmap_single);
+EXPORT_SYMBOL(sn_pci_map_single);
+EXPORT_SYMBOL(sn_pci_map_sg);
+EXPORT_SYMBOL(sn_pci_unmap_sg);
+EXPORT_SYMBOL(sn_pci_alloc_consistent);
+EXPORT_SYMBOL(sn_pci_free_consistent);
+EXPORT_SYMBOL(sn_pci_dma_supported);
+EXPORT_SYMBOL(sn_dma_mapping_error);
diff --git a/arch/ia64/sn/pci/pcibr/Makefile b/arch/ia64/sn/pci/pcibr/Makefile
new file mode 100644 (file)
index 0000000..1850c4a
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2002-2004 Silicon Graphics, Inc.  All Rights Reserved.
+#
+# Makefile for the sn2 io routines.
+
+obj-y                          +=  pcibr_dma.o pcibr_reg.o \
+                                   pcibr_ate.o pcibr_provider.o
diff --git a/arch/ia64/sn/pci/pcibr/pcibr_ate.c b/arch/ia64/sn/pci/pcibr/pcibr_ate.c
new file mode 100644 (file)
index 0000000..9d68546
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <asm/sn/sn_sal.h>
+#include "pci/pcibus_provider_defs.h"
+#include "pci/pcidev.h"
+#include "pci/pcibr_provider.h"
+
+int pcibr_invalidate_ate = 0;  /* by default don't invalidate ATE on free */
+
+/*
+ * mark_ate: Mark the ate as either free or inuse.
+ */
+static void mark_ate(struct ate_resource *ate_resource, int start, int number,
+                    uint64_t value)
+{
+
+       uint64_t *ate = ate_resource->ate;
+       int index;
+       int length = 0;
+
+       for (index = start; length < number; index++, length++)
+               ate[index] = value;
+
+}
+
+/*
+ * find_free_ate:  Find the first free ate index starting from the given
+ *                index for the desired consequtive count.
+ */
+static int find_free_ate(struct ate_resource *ate_resource, int start,
+                        int count)
+{
+
+       uint64_t *ate = ate_resource->ate;
+       int index;
+       int start_free;
+
+       for (index = start; index < ate_resource->num_ate;) {
+               if (!ate[index]) {
+                       int i;
+                       int free;
+                       free = 0;
+                       start_free = index;     /* Found start free ate */
+                       for (i = start_free; i < ate_resource->num_ate; i++) {
+                               if (!ate[i]) {  /* This is free */
+                                       if (++free == count)
+                                               return start_free;
+                               } else {
+                                       index = i + 1;
+                                       break;
+                               }
+                       }
+               } else
+                       index++;        /* Try next ate */
+       }
+
+       return -1;
+}
+
+/*
+ * free_ate_resource:  Free the requested number of ATEs.
+ */
+static inline void free_ate_resource(struct ate_resource *ate_resource,
+                                    int start)
+{
+
+       mark_ate(ate_resource, start, ate_resource->ate[start], 0);
+       if ((ate_resource->lowest_free_index > start) ||
+           (ate_resource->lowest_free_index < 0))
+               ate_resource->lowest_free_index = start;
+
+}
+
+/*
+ * alloc_ate_resource:  Allocate the requested number of ATEs.
+ */
+static inline int alloc_ate_resource(struct ate_resource *ate_resource,
+                                    int ate_needed)
+{
+
+       int start_index;
+
+       /*
+        * Check for ate exhaustion.
+        */
+       if (ate_resource->lowest_free_index < 0)
+               return -1;
+
+       /*
+        * Find the required number of free consequtive ates.
+        */
+       start_index =
+           find_free_ate(ate_resource, ate_resource->lowest_free_index,
+                         ate_needed);
+       if (start_index >= 0)
+               mark_ate(ate_resource, start_index, ate_needed, ate_needed);
+
+       ate_resource->lowest_free_index =
+           find_free_ate(ate_resource, ate_resource->lowest_free_index, 1);
+
+       return start_index;
+}
+
+/*
+ * Allocate "count" contiguous Bridge Address Translation Entries
+ * on the specified bridge to be used for PCI to XTALK mappings.
+ * Indices in rm map range from 1..num_entries.  Indicies returned
+ * to caller range from 0..num_entries-1.
+ *
+ * Return the start index on success, -1 on failure.
+ */
+int pcibr_ate_alloc(struct pcibus_info *pcibus_info, int count)
+{
+       int status = 0;
+       uint64_t flag;
+
+       flag = pcibr_lock(pcibus_info);
+       status = alloc_ate_resource(&pcibus_info->pbi_int_ate_resource, count);
+
+       if (status < 0) {
+               /* Failed to allocate */
+               pcibr_unlock(pcibus_info, flag);
+               return -1;
+       }
+
+       pcibr_unlock(pcibus_info, flag);
+
+       return status;
+}
+
+/*
+ * Setup an Address Translation Entry as specified.  Use either the Bridge
+ * internal maps or the external map RAM, as appropriate.
+ */
+static inline uint64_t *pcibr_ate_addr(struct pcibus_info *pcibus_info,
+                                      int ate_index)
+{
+       if (ate_index < pcibus_info->pbi_int_ate_size) {
+               return pcireg_int_ate_addr(pcibus_info, ate_index);
+       }
+       panic("pcibr_ate_addr: invalid ate_index 0x%x", ate_index);
+}
+
+/*
+ * Update the ate.
+ */
+void inline
+ate_write(struct pcibus_info *pcibus_info, int ate_index, int count,
+         volatile uint64_t ate)
+{
+       while (count-- > 0) {
+               if (ate_index < pcibus_info->pbi_int_ate_size) {
+                       pcireg_int_ate_set(pcibus_info, ate_index, ate);
+               } else {
+                       panic("ate_write: invalid ate_index 0x%x", ate_index);
+               }
+               ate_index++;
+               ate += IOPGSIZE;
+       }
+
+       pcireg_tflush_get(pcibus_info); /* wait until Bridge PIO complete */
+}
+
+void pcibr_ate_free(struct pcibus_info *pcibus_info, int index)
+{
+
+       volatile uint64_t ate;
+       int count;
+       uint64_t flags;
+
+       if (pcibr_invalidate_ate) {
+               /* For debugging purposes, clear the valid bit in the ATE */
+               ate = *pcibr_ate_addr(pcibus_info, index);
+               count = pcibus_info->pbi_int_ate_resource.ate[index];
+               ate_write(pcibus_info, index, count, (ate & ~PCI32_ATE_V));
+       }
+
+       flags = pcibr_lock(pcibus_info);
+       free_ate_resource(&pcibus_info->pbi_int_ate_resource, index);
+       pcibr_unlock(pcibus_info, flags);
+}
diff --git a/arch/ia64/sn/pci/pcibr/pcibr_dma.c b/arch/ia64/sn/pci/pcibr/pcibr_dma.c
new file mode 100644 (file)
index 0000000..b1d66ac
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <asm/sn/sn_sal.h>
+#include <asm/sn/geo.h>
+#include "xtalk/xwidgetdev.h"
+#include "xtalk/hubdev.h"
+#include "pci/pcibus_provider_defs.h"
+#include "pci/pcidev.h"
+#include "pci/tiocp.h"
+#include "pci/pic.h"
+#include "pci/pcibr_provider.h"
+#include "pci/tiocp.h"
+#include "tio.h"
+#include <asm/sn/addrs.h>
+
+extern int sn_ioif_inited;
+
+/* =====================================================================
+ *    DMA MANAGEMENT
+ *
+ *      The Bridge ASIC provides three methods of doing DMA: via a "direct map"
+ *      register available in 32-bit PCI space (which selects a contiguous 2G
+ *     address space on some other widget), via "direct" addressing via 64-bit
+ *      PCI space (all destination information comes from the PCI address,
+ *      including transfer attributes), and via a "mapped" region that allows 
+ *      a bunch of different small mappings to be established with the PMU.
+ *
+ *      For efficiency, we most prefer to use the 32bit direct mapping facility,
+ *      since it requires no resource allocations. The advantage of using the
+ *      PMU over the 64-bit direct is that single-cycle PCI addressing can be
+ *      used; the advantage of using 64-bit direct over PMU addressing is that
+ *      we do not have to allocate entries in the PMU.
+ */
+
+static uint64_t
+pcibr_dmamap_ate32(struct pcidev_info *info,
+                  uint64_t paddr, size_t req_size, uint64_t flags)
+{
+
+       struct pcidev_info *pcidev_info = info->pdi_host_pcidev_info;
+       struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info->
+           pdi_pcibus_info;
+       uint8_t internal_device = (PCI_SLOT(pcidev_info->pdi_host_pcidev_info->
+                                           pdi_linux_pcidev->devfn)) - 1;
+       int ate_count;
+       int ate_index;
+       uint64_t ate_flags = flags | PCI32_ATE_V;
+       uint64_t ate;
+       uint64_t pci_addr;
+       uint64_t xio_addr;
+       uint64_t offset;
+
+       /* PIC in PCI-X mode does not supports 32bit PageMap mode */
+       if (IS_PIC_SOFT(pcibus_info) && IS_PCIX(pcibus_info)) {
+               return 0;
+       }
+
+       /* Calculate the number of ATEs needed. */
+       if (!(MINIMAL_ATE_FLAG(paddr, req_size))) {
+               ate_count = IOPG((IOPGSIZE - 1) /* worst case start offset */
+                                +req_size      /* max mapping bytes */
+                                - 1) + 1;      /* round UP */
+       } else {                /* assume requested target is page aligned */
+               ate_count = IOPG(req_size       /* max mapping bytes */
+                                - 1) + 1;      /* round UP */
+       }
+
+       /* Get the number of ATEs required. */
+       ate_index = pcibr_ate_alloc(pcibus_info, ate_count);
+       if (ate_index < 0)
+               return 0;
+
+       /* In PCI-X mode, Prefetch not supported */
+       if (IS_PCIX(pcibus_info))
+               ate_flags &= ~(PCI32_ATE_PREF);
+
+       xio_addr =
+           IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) :
+           PHYS_TO_TIODMA(paddr);
+       offset = IOPGOFF(xio_addr);
+       ate = ate_flags | (xio_addr - offset);
+
+       /* If PIC, put the targetid in the ATE */
+       if (IS_PIC_SOFT(pcibus_info)) {
+               ate |= (pcibus_info->pbi_hub_xid << PIC_ATE_TARGETID_SHFT);
+       }
+       ate_write(pcibus_info, ate_index, ate_count, ate);
+
+       /*
+        * Set up the DMA mapped Address.
+        */
+       pci_addr = PCI32_MAPPED_BASE + offset + IOPGSIZE * ate_index;
+
+       /*
+        * If swap was set in device in pcibr_endian_set()
+        * we need to turn swapping on.
+        */
+       if (pcibus_info->pbi_devreg[internal_device] & PCIBR_DEV_SWAP_DIR)
+               ATE_SWAP_ON(pci_addr);
+
+       return pci_addr;
+}
+
+static uint64_t
+pcibr_dmatrans_direct64(struct pcidev_info * info, uint64_t paddr,
+                       uint64_t dma_attributes)
+{
+       struct pcibus_info *pcibus_info = (struct pcibus_info *)
+           ((info->pdi_host_pcidev_info)->pdi_pcibus_info);
+       uint64_t pci_addr;
+
+       /* Translate to Crosstalk View of Physical Address */
+       pci_addr = (IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) :
+                   PHYS_TO_TIODMA(paddr)) | dma_attributes;
+
+       /* Handle Bus mode */
+       if (IS_PCIX(pcibus_info))
+               pci_addr &= ~PCI64_ATTR_PREF;
+
+       /* Handle Bridge Chipset differences */
+       if (IS_PIC_SOFT(pcibus_info)) {
+               pci_addr |=
+                   ((uint64_t) pcibus_info->
+                    pbi_hub_xid << PIC_PCI64_ATTR_TARG_SHFT);
+       } else
+               pci_addr |= TIOCP_PCI64_CMDTYPE_MEM;
+
+       /* If PCI mode, func zero uses VCHAN0, every other func uses VCHAN1 */
+       if (!IS_PCIX(pcibus_info) && PCI_FUNC(info->pdi_linux_pcidev->devfn))
+               pci_addr |= PCI64_ATTR_VIRTUAL;
+
+       return pci_addr;
+
+}
+
+static uint64_t
+pcibr_dmatrans_direct32(struct pcidev_info * info,
+                       uint64_t paddr, size_t req_size, uint64_t flags)
+{
+
+       struct pcidev_info *pcidev_info = info->pdi_host_pcidev_info;
+       struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info->
+           pdi_pcibus_info;
+       uint64_t xio_addr;
+
+       uint64_t xio_base;
+       uint64_t offset;
+       uint64_t endoff;
+
+       if (IS_PCIX(pcibus_info)) {
+               return 0;
+       }
+
+       xio_addr = IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) :
+           PHYS_TO_TIODMA(paddr);
+
+       xio_base = pcibus_info->pbi_dir_xbase;
+       offset = xio_addr - xio_base;
+       endoff = req_size + offset;
+       if ((req_size > (1ULL << 31)) ||        /* Too Big */
+           (xio_addr < xio_base) ||    /* Out of range for mappings */
+           (endoff > (1ULL << 31))) {  /* Too Big */
+               return 0;
+       }
+
+       return PCI32_DIRECT_BASE | offset;
+
+}
+
+/*
+ * Wrapper routine for free'ing DMA maps
+ * DMA mappings for Direct 64 and 32 do not have any DMA maps.
+ */
+void
+pcibr_dma_unmap(struct pcidev_info *pcidev_info, dma_addr_t dma_handle,
+               int direction)
+{
+       struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info->
+           pdi_pcibus_info;
+
+       if (IS_PCI32_MAPPED(dma_handle)) {
+               int ate_index;
+
+               ate_index =
+                   IOPG((ATE_SWAP_OFF(dma_handle) - PCI32_MAPPED_BASE));
+               pcibr_ate_free(pcibus_info, ate_index);
+       }
+}
+
+/*
+ * On SN systems there is a race condition between a PIO read response and 
+ * DMA's.  In rare cases, the read response may beat the DMA, causing the
+ * driver to think that data in memory is complete and meaningful.  This code
+ * eliminates that race.  This routine is called by the PIO read routines
+ * after doing the read.  For PIC this routine then forces a fake interrupt
+ * on another line, which is logically associated with the slot that the PIO
+ * is addressed to.  It then spins while watching the memory location that
+ * the interrupt is targetted to.  When the interrupt response arrives, we 
+ * are sure that the DMA has landed in memory and it is safe for the driver
+ * to proceed. For TIOCP use the Device(x) Write Request Buffer Flush 
+ * Bridge register since it ensures the data has entered the coherence domain,
+ * unlike the PIC Device(x) Write Request Buffer Flush register.
+ */
+
+void sn_dma_flush(uint64_t addr)
+{
+       nasid_t nasid;
+       int is_tio;
+       int wid_num;
+       int i, j;
+       int bwin;
+       uint64_t flags;
+       struct hubdev_info *hubinfo;
+       volatile struct sn_flush_device_list *p;
+       struct sn_flush_nasid_entry *flush_nasid_list;
+
+       if (!sn_ioif_inited)
+               return;
+
+       nasid = NASID_GET(addr);
+       if (-1 == nasid_to_cnodeid(nasid))
+               return;
+
+       hubinfo = (NODEPDA(nasid_to_cnodeid(nasid)))->pdinfo;
+
+       if (!hubinfo) {
+               BUG();
+       }
+       is_tio = (nasid & 1);
+       if (is_tio) {
+               wid_num = TIO_SWIN_WIDGETNUM(addr);
+               bwin = TIO_BWIN_WINDOWNUM(addr);
+       } else {
+               wid_num = SWIN_WIDGETNUM(addr);
+               bwin = BWIN_WINDOWNUM(addr);
+       }
+
+       flush_nasid_list = &hubinfo->hdi_flush_nasid_list;
+       if (flush_nasid_list->widget_p == NULL)
+               return;
+       if (bwin > 0) {
+               uint64_t itte = flush_nasid_list->iio_itte[bwin];
+
+               if (is_tio) {
+                       wid_num = (itte >> TIO_ITTE_WIDGET_SHIFT) &
+                           TIO_ITTE_WIDGET_MASK;
+               } else {
+                       wid_num = (itte >> IIO_ITTE_WIDGET_SHIFT) &
+                           IIO_ITTE_WIDGET_MASK;
+               }
+       }
+       if (flush_nasid_list->widget_p == NULL)
+               return;
+       if (flush_nasid_list->widget_p[wid_num] == NULL)
+               return;
+       p = &flush_nasid_list->widget_p[wid_num][0];
+
+       /* find a matching BAR */
+       for (i = 0; i < DEV_PER_WIDGET; i++) {
+               for (j = 0; j < PCI_ROM_RESOURCE; j++) {
+                       if (p->sfdl_bar_list[j].start == 0)
+                               break;
+                       if (addr >= p->sfdl_bar_list[j].start
+                           && addr <= p->sfdl_bar_list[j].end)
+                               break;
+               }
+               if (j < PCI_ROM_RESOURCE && p->sfdl_bar_list[j].start != 0)
+                       break;
+               p++;
+       }
+
+       /* if no matching BAR, return without doing anything. */
+       if (i == DEV_PER_WIDGET)
+               return;
+
+       /*
+        * For TIOCP use the Device(x) Write Request Buffer Flush Bridge
+        * register since it ensures the data has entered the coherence
+        * domain, unlike PIC
+        */
+       if (is_tio) {
+               uint32_t tio_id = REMOTE_HUB_L(nasid, TIO_NODE_ID);
+               uint32_t revnum = XWIDGET_PART_REV_NUM(tio_id);
+
+               /* TIOCP BRINGUP WAR (PV907516): Don't write buffer flush reg */
+               if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) {
+                       return;
+               } else {
+                       pcireg_wrb_flush_get(p->sfdl_pcibus_info,
+                                            (p->sfdl_slot - 1));
+               }
+       } else {
+               spin_lock_irqsave(&((struct sn_flush_device_list *)p)->
+                                 sfdl_flush_lock, flags);
+
+               p->sfdl_flush_value = 0;
+
+               /* force an interrupt. */
+               *(volatile uint32_t *)(p->sfdl_force_int_addr) = 1;
+
+               /* wait for the interrupt to come back. */
+               while (*(p->sfdl_flush_addr) != 0x10f) ;
+
+               /* okay, everything is synched up. */
+               spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock, flags);
+       }
+       return;
+}
+
+/*
+ * Wrapper DMA interface.  Called from pci_dma.c routines.
+ */
+
+uint64_t
+pcibr_dma_map(struct pcidev_info * pcidev_info, unsigned long phys_addr,
+             size_t size, unsigned int flags)
+{
+       dma_addr_t dma_handle;
+       struct pci_dev *pcidev = pcidev_info->pdi_linux_pcidev;
+
+       if (flags & SN_PCIDMA_CONSISTENT) {
+               /* sn_pci_alloc_consistent interfaces */
+               if (pcidev->dev.coherent_dma_mask == ~0UL) {
+                       dma_handle =
+                           pcibr_dmatrans_direct64(pcidev_info, phys_addr,
+                                                   PCI64_ATTR_BAR);
+               } else {
+                       dma_handle =
+                           (dma_addr_t) pcibr_dmamap_ate32(pcidev_info,
+                                                           phys_addr, size,
+                                                           PCI32_ATE_BAR);
+               }
+       } else {
+               /* map_sg/map_single interfaces */
+
+               /* SN cannot support DMA addresses smaller than 32 bits. */
+               if (pcidev->dma_mask < 0x7fffffff) {
+                       return 0;
+               }
+
+               if (pcidev->dma_mask == ~0UL) {
+                       /*
+                        * Handle the most common case: 64 bit cards.  This
+                        * call should always succeed.
+                        */
+
+                       dma_handle =
+                           pcibr_dmatrans_direct64(pcidev_info, phys_addr,
+                                                   PCI64_ATTR_PREF);
+               } else {
+                       /* Handle 32-63 bit cards via direct mapping */
+                       dma_handle =
+                           pcibr_dmatrans_direct32(pcidev_info, phys_addr,
+                                                   size, 0);
+                       if (!dma_handle) {
+                               /*
+                                * It is a 32 bit card and we cannot do direct mapping,
+                                * so we use an ATE.
+                                */
+
+                               dma_handle =
+                                   pcibr_dmamap_ate32(pcidev_info, phys_addr,
+                                                      size, PCI32_ATE_PREF);
+                       }
+               }
+       }
+
+       return dma_handle;
+}
+
+EXPORT_SYMBOL(sn_dma_flush);
diff --git a/arch/ia64/sn/pci/pcibr/pcibr_provider.c b/arch/ia64/sn/pci/pcibr/pcibr_provider.c
new file mode 100644 (file)
index 0000000..92bd278
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <asm/sn/sn_sal.h>
+#include "xtalk/xwidgetdev.h"
+#include <asm/sn/geo.h>
+#include "xtalk/hubdev.h"
+#include "pci/pcibus_provider_defs.h"
+#include "pci/pcidev.h"
+#include "pci/pcibr_provider.h"
+#include <asm/sn/addrs.h>
+
+
+static int sal_pcibr_error_interrupt(struct pcibus_info *soft)
+{
+       struct ia64_sal_retval ret_stuff;
+       uint64_t busnum;
+       int segment;
+       ret_stuff.status = 0;
+       ret_stuff.v0 = 0;
+
+       segment = 0;
+       busnum = soft->pbi_buscommon.bs_persist_busnum;
+       SAL_CALL_NOLOCK(ret_stuff,
+                       (u64) SN_SAL_IOIF_ERROR_INTERRUPT,
+                       (u64) segment, (u64) busnum, 0, 0, 0, 0, 0);
+
+       return (int)ret_stuff.v0;
+}
+
+/* 
+ * PCI Bridge Error interrupt handler.  Gets invoked whenever a PCI 
+ * bridge sends an error interrupt.
+ */
+static irqreturn_t
+pcibr_error_intr_handler(int irq, void *arg, struct pt_regs *regs)
+{
+       struct pcibus_info *soft = (struct pcibus_info *)arg;
+
+       if (sal_pcibr_error_interrupt(soft) < 0) {
+               panic("pcibr_error_intr_handler(): Fatal Bridge Error");
+       }
+       return IRQ_HANDLED;
+}
+
+void *
+pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft)
+{
+       int nasid, cnode, j;
+       struct hubdev_info *hubdev_info;
+       struct pcibus_info *soft;
+       struct sn_flush_device_list *sn_flush_device_list;
+
+       if (! IS_PCI_BRIDGE_ASIC(prom_bussoft->bs_asic_type)) {
+               return NULL;
+       }
+
+       /*
+        * Allocate kernel bus soft and copy from prom.
+        */
+
+       soft = kmalloc(sizeof(struct pcibus_info), GFP_KERNEL);
+       if (!soft) {
+               return NULL;
+       }
+
+       memcpy(soft, prom_bussoft, sizeof(struct pcibus_info));
+       soft->pbi_buscommon.bs_base =
+           (((u64) soft->pbi_buscommon.
+             bs_base << 4) >> 4) | __IA64_UNCACHED_OFFSET;
+
+       spin_lock_init(&soft->pbi_lock);
+
+       /*
+        * register the bridge's error interrupt handler
+        */
+       if (request_irq(SGI_PCIBR_ERROR, (void *)pcibr_error_intr_handler,
+                       SA_SHIRQ, "PCIBR error", (void *)(soft))) {
+               printk(KERN_WARNING
+                      "pcibr cannot allocate interrupt for error handler\n");
+       }
+
+       /* 
+        * Update the Bridge with the "kernel" pagesize 
+        */
+       if (PAGE_SIZE < 16384) {
+               pcireg_control_bit_clr(soft, PCIBR_CTRL_PAGE_SIZE);
+       } else {
+               pcireg_control_bit_set(soft, PCIBR_CTRL_PAGE_SIZE);
+       }
+
+       nasid = NASID_GET(soft->pbi_buscommon.bs_base);
+       cnode = nasid_to_cnodeid(nasid);
+       hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo);
+
+       if (hubdev_info->hdi_flush_nasid_list.widget_p) {
+               sn_flush_device_list = hubdev_info->hdi_flush_nasid_list.
+                   widget_p[(int)soft->pbi_buscommon.bs_xid];
+               if (sn_flush_device_list) {
+                       for (j = 0; j < DEV_PER_WIDGET;
+                            j++, sn_flush_device_list++) {
+                               if (sn_flush_device_list->sfdl_slot == -1)
+                                       continue;
+                               if (sn_flush_device_list->
+                                   sfdl_persistent_busnum ==
+                                   soft->pbi_buscommon.bs_persist_busnum)
+                                       sn_flush_device_list->sfdl_pcibus_info =
+                                           soft;
+                       }
+               }
+       }
+
+       /* Setup the PMU ATE map */
+       soft->pbi_int_ate_resource.lowest_free_index = 0;
+       soft->pbi_int_ate_resource.ate =
+           kmalloc(soft->pbi_int_ate_size * sizeof(uint64_t), GFP_KERNEL);
+       memset(soft->pbi_int_ate_resource.ate, 0,
+              (soft->pbi_int_ate_size * sizeof(uint64_t)));
+
+       return soft;
+}
+
+void pcibr_force_interrupt(struct sn_irq_info *sn_irq_info)
+{
+       struct pcidev_info *pcidev_info;
+       struct pcibus_info *pcibus_info;
+       int bit = sn_irq_info->irq_int_bit;
+
+       pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
+       if (pcidev_info) {
+               pcibus_info =
+                   (struct pcibus_info *)pcidev_info->pdi_host_pcidev_info->
+                   pdi_pcibus_info;
+               pcireg_force_intr_set(pcibus_info, bit);
+       }
+}
+
+void pcibr_change_devices_irq(struct sn_irq_info *sn_irq_info)
+{
+       struct pcidev_info *pcidev_info;
+       struct pcibus_info *pcibus_info;
+       int bit = sn_irq_info->irq_int_bit;
+       uint64_t xtalk_addr = sn_irq_info->irq_xtalkaddr;
+
+       pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
+       if (pcidev_info) {
+               pcibus_info =
+                   (struct pcibus_info *)pcidev_info->pdi_host_pcidev_info->
+                   pdi_pcibus_info;
+
+               /* Disable the device's IRQ   */
+               pcireg_intr_enable_bit_clr(pcibus_info, bit);
+
+               /* Change the device's IRQ    */
+               pcireg_intr_addr_addr_set(pcibus_info, bit, xtalk_addr);
+
+               /* Re-enable the device's IRQ */
+               pcireg_intr_enable_bit_set(pcibus_info, bit);
+
+               pcibr_force_interrupt(sn_irq_info);
+       }
+}
diff --git a/arch/ia64/sn/pci/pcibr/pcibr_reg.c b/arch/ia64/sn/pci/pcibr/pcibr_reg.c
new file mode 100644 (file)
index 0000000..74a74a7
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include "pci/pcibus_provider_defs.h"
+#include "pci/pcidev.h"
+#include "pci/tiocp.h"
+#include "pci/pic.h"
+#include "pci/pcibr_provider.h"
+
+union br_ptr {
+       struct tiocp tio;
+       struct pic pic;
+};
+
+/*
+ * Control Register Access -- Read/Write                            0000_0020
+ */
+void pcireg_control_bit_clr(struct pcibus_info *pcibus_info, uint64_t bits)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_control &= ~bits;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_wid_control &= ~bits;
+                       break;
+               default:
+                       panic
+                           ("pcireg_control_bit_clr: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+void pcireg_control_bit_set(struct pcibus_info *pcibus_info, uint64_t bits)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_control |= bits;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_wid_control |= bits;
+                       break;
+               default:
+                       panic
+                           ("pcireg_control_bit_set: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+/*
+ * PCI/PCIX Target Flush Register Access -- Read Only              0000_0050
+ */
+uint64_t pcireg_tflush_get(struct pcibus_info *pcibus_info)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+       uint64_t ret = 0;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ret = ptr->tio.cp_tflush;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ret = ptr->pic.p_wid_tflush;
+                       break;
+               default:
+                       panic
+                           ("pcireg_tflush_get: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+
+       /* Read of the Target Flush should always return zero */
+       if (ret != 0)
+               panic("pcireg_tflush_get:Target Flush failed\n");
+
+       return ret;
+}
+
+/*
+ * Interrupt Status Register Access -- Read Only                   0000_0100
+ */
+uint64_t pcireg_intr_status_get(struct pcibus_info * pcibus_info)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+       uint64_t ret = 0;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ret = ptr->tio.cp_int_status;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ret = ptr->pic.p_int_status;
+                       break;
+               default:
+                       panic
+                           ("pcireg_intr_status_get: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+       return ret;
+}
+
+/*
+ * Interrupt Enable Register Access -- Read/Write                   0000_0108
+ */
+void pcireg_intr_enable_bit_clr(struct pcibus_info *pcibus_info, uint64_t bits)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_int_enable &= ~bits;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_int_enable &= ~bits;
+                       break;
+               default:
+                       panic
+                           ("pcireg_intr_enable_bit_clr: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+void pcireg_intr_enable_bit_set(struct pcibus_info *pcibus_info, uint64_t bits)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_int_enable |= bits;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_int_enable |= bits;
+                       break;
+               default:
+                       panic
+                           ("pcireg_intr_enable_bit_set: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+/*
+ * Intr Host Address Register (int_addr) -- Read/Write  0000_0130 - 0000_0168
+ */
+void pcireg_intr_addr_addr_set(struct pcibus_info *pcibus_info, int int_n,
+                              uint64_t addr)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_int_addr[int_n] &= ~TIOCP_HOST_INTR_ADDR;
+                       ptr->tio.cp_int_addr[int_n] |=
+                           (addr & TIOCP_HOST_INTR_ADDR);
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_int_addr[int_n] &= ~PIC_HOST_INTR_ADDR;
+                       ptr->pic.p_int_addr[int_n] |=
+                           (addr & PIC_HOST_INTR_ADDR);
+                       break;
+               default:
+                       panic
+                           ("pcireg_intr_addr_addr_get: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+/*
+ * Force Interrupt Register Access -- Write Only       0000_01C0 - 0000_01F8
+ */
+void pcireg_force_intr_set(struct pcibus_info *pcibus_info, int int_n)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_force_pin[int_n] = 1;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_force_pin[int_n] = 1;
+                       break;
+               default:
+                       panic
+                           ("pcireg_force_intr_set: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+/*
+ * Device(x) Write Buffer Flush Reg Access -- Read Only 0000_0240 - 0000_0258
+ */
+uint64_t pcireg_wrb_flush_get(struct pcibus_info *pcibus_info, int device)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+       uint64_t ret = 0;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ret = ptr->tio.cp_wr_req_buf[device];
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ret = ptr->pic.p_wr_req_buf[device];
+                       break;
+               default:
+                     panic("pcireg_wrb_flush_get: unknown bridgetype bridge 0x%p", (void *)ptr);
+               }
+
+       }
+       /* Read of the Write Buffer Flush should always return zero */
+       return ret;
+}
+
+void pcireg_int_ate_set(struct pcibus_info *pcibus_info, int ate_index,
+                       uint64_t val)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_int_ate_ram[ate_index] = (uint64_t) val;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_int_ate_ram[ate_index] = (uint64_t) val;
+                       break;
+               default:
+                       panic
+                           ("pcireg_int_ate_set: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+uint64_t *pcireg_int_ate_addr(struct pcibus_info *pcibus_info, int ate_index)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+       uint64_t *ret = (uint64_t *) 0;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ret =
+                           (uint64_t *) & (ptr->tio.cp_int_ate_ram[ate_index]);
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ret =
+                           (uint64_t *) & (ptr->pic.p_int_ate_ram[ate_index]);
+                       break;
+               default:
+                       panic
+                           ("pcireg_int_ate_addr: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+       return ret;
+}
diff --git a/arch/m32r/Kconfig.debug b/arch/m32r/Kconfig.debug
new file mode 100644 (file)
index 0000000..36788c2
--- /dev/null
@@ -0,0 +1,34 @@
+menu "Kernel hacking"
+
+source "lib/Kconfig.debug"
+
+config DEBUG_STACKOVERFLOW
+       bool "Check for stack overflows"
+       depends on DEBUG_KERNEL
+
+config DEBUG_STACK_USAGE
+       bool "Stack utilization instrumentation"
+       depends on DEBUG_KERNEL
+       help
+         Enables the display of the minimum amount of free stack which each
+         task has ever had available in the sysrq-T and sysrq-P debug output.
+
+         This option will slow down process creation somewhat.
+
+config DEBUG_PAGEALLOC
+       bool "Page alloc debugging"
+       depends on DEBUG_KERNEL
+       help
+         Unmap pages from the kernel linear mapping after free_pages().
+         This results in a large slowdown, but helps to find certain types
+         of memory corruptions.
+
+config FRAME_POINTER
+       bool "Compile the kernel with frame pointers"
+       help
+         If you say Y here the resulting kernel image will be slightly larger
+         and slower, but it will give very useful debugging information.
+         If you don't debug the kernel, you can say N, but we may not be able
+         to solve problems without frame pointers.
+
+endmenu
diff --git a/arch/m32r/m32700ut/dot.gdbinit_400MHz_32MB b/arch/m32r/m32700ut/dot.gdbinit_400MHz_32MB
new file mode 100644 (file)
index 0000000..adc608a
--- /dev/null
@@ -0,0 +1,249 @@
+# .gdbinit file
+# $Id: dot.gdbinit_400MHz_32MB,v 1.1 2004/10/21 01:41:27 fujiwara Exp $
+#-----
+# NOTE: this file is generated by a script, "gen_gdbinit.pl".
+# (Please type "gen_gdbinit.pl --help" and check the help message).
+# $ Id: gen_gdbinit.pl,v 1.12 2004/07/26 09:56:10 takata Exp $
+#-----
+# target platform: m32700ut
+
+# setting
+set width 0d70
+set radix 0d16
+
+debug_chaos
+
+# clk xin:cpu:bif:bus=25:400:100:50
+define clock_init
+  set *(unsigned long *)0x00ef4008 = 0x00000000
+  set *(unsigned long *)0x00ef4004 = 0
+  shell sleep 0.1
+  # NOTE: Please change the master clock source from PLL-clock to Xin-clock
+  # and switch off PLL, before resetting the clock gear ratio.
+
+  set *(unsigned long *)0x00ef4024 = 3
+  set *(unsigned long *)0x00ef4020 = 2
+  set *(unsigned long *)0x00ef4010 = 0
+  set *(unsigned long *)0x00ef4014 = 0
+  set *(unsigned long *)0x00ef4004 = 7
+  shell sleep 0.1
+  set *(unsigned long *)0x00ef4008 = 0x00000200
+end
+
+# Initialize SDRAM controller
+define sdram_init
+  # SDIR0
+  set *(unsigned long *)0x00ef6008 = 0x00000182
+  # SDIR1
+  set *(unsigned long *)0x00ef600c = 0x00000001
+  # Initialize wait
+  shell sleep 0.1
+  # Ch0-MOD
+  set *(unsigned long *)0x00ef602c = 0x00000020
+  # Ch0-TR
+  set *(unsigned long *)0x00ef6028 = 0x00041302
+  # Ch0-ADR (size:32MB)
+  set *(unsigned long *)0x00ef6020 = 0x08000003
+  # AutoRef On
+  set *(unsigned long *)0x00ef6004 = 0x00010517
+  # Access enable
+  set *(unsigned long *)0x00ef6024 = 0x00000001
+end
+document sdram_init
+  SDRAM controller initialization
+  0x08000000 - 0x09ffffff (32MB)
+end
+
+# Initialize BSEL3 for UT-CFC
+define cfc_init
+  set $sfrbase = 0xa0ef0000
+# too fast
+#  set *(unsigned long *)($sfrbase + 0x5300) = 0x0b0b8000
+#  set *(unsigned long *)($sfrbase + 0x5304) = 0x00102204
+#  set *(unsigned long *)($sfrbase + 0x5300) = 0x1f1f8000
+#  set *(unsigned long *)($sfrbase + 0x5300) = 0x1f1f1fdf
+#  set *(unsigned long *)($sfrbase + 0x5304) = 0x0013220f
+#  set *(unsigned long *)($sfrbase + 0x5304) = 0x0013330f
+end
+document cfc_init
+  CF controller initialization
+end
+
+# MMU enable
+define mmu_enable
+  set $evb=0x88000000
+  set *(unsigned long *)0xffff0024=1
+end
+
+# MMU disable
+define mmu_disable
+  set $evb=0
+  set *(unsigned long *)0xffff0024=0
+end
+
+# Show TLB entries
+define show_tlb_entries
+  set $i = 0
+  set $addr = $arg0
+  set $nr_entries = $arg1
+  use_mon_code
+  while ($i < $nr_entries)
+    set $tlb_tag = *(unsigned long*)$addr
+    set $tlb_data = *(unsigned long*)($addr + 4)
+    printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data
+    set $i = $i + 1
+    set $addr = $addr + 8
+  end
+  use_debug_dma
+end
+define itlb
+  set $itlb=0xfe000000
+  show_tlb_entries $itlb 0d32
+end
+define dtlb
+  set $dtlb=0xfe000800
+  show_tlb_entries $dtlb 0d32
+end
+
+# Initialize TLB entries
+define init_tlb_entries
+  set $i = 0
+  set $addr = $arg0
+  set $nr_entries = $arg1
+  use_mon_code
+  while ($i < $nr_entries)
+    set *(unsigned long *)($addr + 0x4) = 0
+    set $i = $i + 1
+    set $addr = $addr + 8
+  end
+  use_debug_dma
+end
+define tlb_init
+  set $itlb=0xfe000000
+  init_tlb_entries $itlb 0d32
+  set $dtlb=0xfe000800
+  init_tlb_entries $dtlb 0d32
+end
+
+# Show current task structure
+define show_current
+  set $current = $spi & 0xffffe000
+  printf "$current=0x%08lX\n",$current
+  print *(struct task_struct *)$current
+end
+
+# Show user assigned task structure
+define show_task
+  set  = $arg0 & 0xffffe000
+  printf "$task=0x%08lX\n",$task
+  print *(struct task_struct *)$task
+end
+document show_task
+  Show user assigned task structure
+  arg0 : task structure address
+end
+
+# Show M32R registers
+define show_regs
+  printf " R0[0x%08lX]   R1[0x%08lX]   R2[0x%08lX]   R3[0x%08lX]\n",$r0,$r1,$r2,$r3
+  printf " R4[0x%08lX]   R5[0x%08lX]   R6[0x%08lX]   R7[0x%08lX]\n",$r4,$r5,$r6,$r7
+  printf " R8[0x%08lX]   R9[0x%08lX]  R10[0x%08lX]  R11[0x%08lX]\n",$r8,$r9,$r10,$r11
+  printf "R12[0x%08lX]   FP[0x%08lX]   LR[0x%08lX]   SP[0x%08lX]\n",$r12,$fp,$lr,$sp
+  printf "PSW[0x%08lX]  CBR[0x%08lX]  SPI[0x%08lX]  SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu
+  printf "BPC[0x%08lX]   PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch
+  printf "EVB[0x%08lX]\n",$evb
+end
+
+# Setup all
+define setup
+  use_mon_code
+  set *(unsigned int)0xfffffffc=0x60
+  shell sleep 0.1
+  clock_init
+  shell sleep 0.1
+  # SDRAM: 32MB
+  set *(unsigned long *)0x00ef6020 = 0x08000003
+  cfc_init
+  # USB
+  set *(unsigned short *)0xb0301000 = 0x100
+
+  set $evb=0x08000000
+end
+
+# Load modules
+define load_modules
+  use_debug_dma
+  load
+end
+
+# Set kernel parameters
+define set_kernel_parameters
+  set $param = (void*)0x08001000
+  # INITRD_START
+#  set *(unsigned long *)($param + 0x0010) = 0x08300000
+  # INITRD_SIZE
+#  set *(unsigned long *)($param + 0x0014) = 0x00000000
+  # M32R_CPUCLK
+  set *(unsigned long *)($param + 0x0018) = 0d400000000
+  # M32R_BUSCLK
+  set *(unsigned long *)($param + 0x001c) = 0d50000000
+
+  # M32R_TIMER_DIVIDE
+  set *(unsigned long *)($param + 0x0020) = 0d128
+
+  set {char[0x200]}($param + 0x100) = "console=ttyS0,115200n8x console=tty1 root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/rootfs,rsize=1024,wsize=1024 nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 mem=32M \0"
+end
+
+# Boot
+define boot
+  set_kernel_parameters
+  set $fp = 0
+  set $pc = 0x08002000
+# set *(unsigned char *)0xffffffff = 0x03
+  si
+  c
+end
+
+# Set breakpoints
+define set_breakpoints
+  b *0x08000030
+end
+
+# Restart
+define restart
+  sdireset
+  sdireset
+  set $pc = 0
+  b *0x04001000
+  b *0x08001000
+  b *0x08002000
+  si
+  c
+  tlb_init
+  del
+  setup
+  load_modules
+  boot
+end
+
+define si
+  stepi
+  x/i $pc
+  show_reg
+end
+
+sdireset
+sdireset
+file vmlinux
+target m32rsdi
+set $pc = 0
+b *0x04001000
+b *0x08001000
+b *0x08002000
+c
+tlb_init
+del
+setup
+load_modules
+boot
+
diff --git a/arch/m32r/mappi2/defconfig.vdec2 b/arch/m32r/mappi2/defconfig.vdec2
new file mode 100644 (file)
index 0000000..7f6125f
--- /dev/null
@@ -0,0 +1,698 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc1-bk21
+# Fri Nov 12 16:08:58 2004
+#
+CONFIG_M32R=y
+CONFIG_UID16=y
+CONFIG_GENERIC_ISA_DMA=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_HOTPLUG=y
+CONFIG_KOBJECT_UEVENT=y
+CONFIG_IKCONFIG=y
+# CONFIG_IKCONFIG_PROC is not set
+CONFIG_EMBEDDED=y
+# CONFIG_KALLSYMS is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Processor type and features
+#
+# CONFIG_PLAT_MAPPI is not set
+# CONFIG_PLAT_USRV is not set
+# CONFIG_PLAT_M32700UT is not set
+# CONFIG_PLAT_OPSPUT is not set
+# CONFIG_PLAT_OAKS32R is not set
+CONFIG_PLAT_MAPPI2=y
+# CONFIG_CHIP_M32700 is not set
+# CONFIG_CHIP_M32102 is not set
+CONFIG_CHIP_VDEC2=y
+# CONFIG_CHIP_OPSP is not set
+CONFIG_MMU=y
+CONFIG_TLB_ENTRIES=16
+CONFIG_ISA_M32R2=y
+CONFIG_BUS_CLOCK=50000000
+CONFIG_TIMER_DIVIDE=128
+# CONFIG_CPU_LITTLE_ENDIAN is not set
+CONFIG_MEMORY_START=0x08000000
+CONFIG_MEMORY_SIZE=0x01000000
+CONFIG_NOHIGHMEM=y
+# CONFIG_DISCONTIGMEM is not set
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
+CONFIG_PREEMPT=y
+# CONFIG_HAVE_DEC_LOCK is not set
+# CONFIG_SMP is not set
+
+#
+# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+#
+# CONFIG_PCI is not set
+# CONFIG_ISA is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA_DEBUG is not set
+# CONFIG_PCMCIA_OBSOLETE is not set
+CONFIG_PCMCIA=y
+
+#
+# PC-card bridges
+#
+# CONFIG_TCIC is not set
+# CONFIG_M32R_CFC is not set
+
+#
+# PCI Hotplug Support
+#
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER 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_FD is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECS=y
+CONFIG_BLK_DEV_IDECD=m
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_IDE_ARM 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=m
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_QLOGIC_1280_1040 is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# PCMCIA SCSI adapter support
+#
+# CONFIG_PCMCIA_AHA152X is not set
+# CONFIG_PCMCIA_FDOMAIN is not set
+# CONFIG_PCMCIA_NINJA_SCSI is not set
+# CONFIG_PCMCIA_QLOGIC is not set
+# CONFIG_PCMCIA_SYM53C500 is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# 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=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=y
+# CONFIG_IP_PNP_BOOTP is not set
+# 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_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+# CONFIG_NE2000 is not set
+
+#
+# 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
+
+#
+# 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 is not set
+# 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
+# CONFIG_SERIO_RAW 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_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_M32R_SIO=y
+CONFIG_SERIAL_M32R_SIO_CONSOLE=y
+# CONFIG_SERIAL_M32R_PLDSIO is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# 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
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=y
+
+#
+# Video For Linux
+#
+
+#
+# Video Adapters
+#
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_M32R_AR is not set
+
+#
+# Radio Adapters
+#
+# CONFIG_RADIO_MAESTRO 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_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# 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=m
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+CONFIG_JBD=m
+CONFIG_JBD_DEBUG=y
+CONFIG_FS_MBCACHE=y
+CONFIG_REISERFS_FS=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR 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_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR 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_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_RPCSEC_GSS_SPKM3 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 is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# 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_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# 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
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_FRAME_POINTER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
diff --git a/arch/m32r/mappi2/dot.gdbinit.vdec2 b/arch/m32r/mappi2/dot.gdbinit.vdec2
new file mode 100644 (file)
index 0000000..797a830
--- /dev/null
@@ -0,0 +1,233 @@
+# .gdbinit file
+# $Id: dot.gdbinit.vdec2,v 1.2 2004/11/11 02:03:15 takata Exp $
+
+# setting
+set width 0d70
+set radix 0d16
+use_debug_dma
+
+# Initialize SDRAM controller for Mappi
+define sdram_init
+  # SDIR0
+  set *(unsigned long *)0x00ef6008=0x00000182
+  # SDIR1
+  set *(unsigned long *)0x00ef600c=0x00000001
+  # Initialize wait
+  shell sleep 1
+  # Ch0-MOD
+  set *(unsigned long *)0x00ef602c=0x00000020
+  # Ch0-TR
+  set *(unsigned long *)0x00ef6028=0x00041302
+  # Ch0-ADR
+  set *(unsigned long *)0x00ef6020=0x08000004
+  # AutoRef On
+  set *(unsigned long *)0x00ef6004=0x00010705
+  # Access enable
+  set *(unsigned long *)0x00ef6024=0x00000001
+end
+document sdram_init
+  Mappi SDRAM controller initialization
+  0x08000000 - 0x0bffffff (64MB)
+end
+
+# Initialize SDRAM controller for Mappi
+define sdram_init2
+  # SDIR0
+  set *(unsigned long *)0x00ef6008=0x00000182
+  # Ch0-MOD
+  set *(unsigned long *)0x00ef602c=0x00000020
+  # Ch0-TR
+  set *(unsigned long *)0x00ef6028=0x00010002
+  # Ch0-ADR
+  set *(unsigned long *)0x00ef6020=0x08000004
+  # AutoRef On
+  set *(unsigned long *)0x00ef6004=0x00010107
+  # SDIR1
+  set *(unsigned long *)0x00ef600c=0x00000001
+  # Initialize wait
+  shell sleep 1
+  # Access enable
+  set *(unsigned long *)0x00ef6024=0x00000001
+  shell sleep 1
+end
+document sdram_init
+  Mappi SDRAM controller initialization
+  0x08000000 - 0x0bffffff (64MB)
+end
+
+# Initialize LAN controller for Mappi
+define lanc_init
+  # Set BSEL1 (BSEL3 for the Chaos's bselc)
+  #set *(unsigned long *)0x00ef5004 = 0x0fff330f
+  #set *(unsigned long *)0x00ef5004 = 0x01113301
+
+#  set *(unsigned long *)0x00ef5004 = 0x02011101
+#  set *(unsigned long *)0x00ef5004 = 0x04441104
+
+  # BSEL5
+#  set *(unsigned long *)0x00ef5014 = 0x0ccc310c
+#  set *(unsigned long *)0x00ef5014 = 0x0303310f
+#  set *(unsigned long *)0x00ef5014 = 0x01011102 -> NG
+#  set *(unsigned long *)0x00ef5014 = 0x03033103
+
+ set *(unsigned long *)0x00ef500c = 0x0b0b1304
+ set *(unsigned long *)0x00ef5010 = 0x03033302
+# set *(unsigned long *)0x00ef5018 = 0x02223302
+end
+
+# MMU enable
+define mmu_enable
+  set $evb=0x88000000
+  set *(unsigned long *)0xffff0024=1
+end
+
+# MMU disable
+define mmu_disable
+  set $evb=0
+  set *(unsigned long *)0xffff0024=0
+end
+
+# Show TLB entries
+define show_tlb_entries
+  set $i = 0
+  set $addr = $arg0
+  while ($i < 0d16 )
+    set $tlb_tag = *(unsigned long*)$addr
+    set $tlb_data = *(unsigned long*)($addr + 4)
+    printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data
+    set $i = $i + 1
+    set $addr = $addr + 8
+  end
+end
+define itlb
+  set $itlb=0xfe000000
+  show_tlb_entries $itlb
+end
+define dtlb
+  set $dtlb=0xfe000800
+  show_tlb_entries $dtlb
+end
+
+# Cache ON
+define set_cache_type
+  set $mctype = (void*)0xfffffff8
+# chaos
+# set *(unsigned long *)($mctype) = 0x0000c000
+# m32102 i-cache only
+  set *(unsigned long *)($mctype) = 0x00008000
+# m32102 d-cache only
+#  set *(unsigned long *)($mctype) = 0x00004000
+end
+define cache_on
+  set $param = (void*)0x08001000
+  set *(unsigned long *)($param) = 0x60ff6102
+end
+
+
+# Show current task structure
+define show_current
+  set $current = $spi & 0xffffe000
+  printf "$current=0x%08lX\n",$current
+  print *(struct task_struct *)$current
+end
+
+# Show user assigned task structure
+define show_task
+  set $task = $arg0 & 0xffffe000
+  printf "$task=0x%08lX\n",$task
+  print *(struct task_struct *)$task
+end
+document show_task
+  Show user assigned task structure
+  arg0 : task structure address
+end
+
+# Show M32R registers
+define show_regs
+  printf " R0[0x%08lX]   R1[0x%08lX]   R2[0x%08lX]   R3[0x%08lX]\n",$r0,$r1,$r2,$r3
+  printf " R4[0x%08lX]   R5[0x%08lX]   R6[0x%08lX]   R7[0x%08lX]\n",$r4,$r5,$r6,$r7
+  printf " R8[0x%08lX]   R9[0x%08lX]  R10[0x%08lX]  R11[0x%08lX]\n",$r8,$r9,$r10,$r11
+  printf "R12[0x%08lX]   FP[0x%08lX]   LR[0x%08lX]   SP[0x%08lX]\n",$r12,$fp,$lr,$sp
+  printf "PSW[0x%08lX]  CBR[0x%08lX]  SPI[0x%08lX]  SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu
+  printf "BPC[0x%08lX]   PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch
+  printf "EVB[0x%08lX]\n",$evb
+
+  set $mests = *(unsigned long *)0xffff000c
+  set $mdeva = *(unsigned long *)0xffff0010
+  printf "MESTS[0x%08lX] MDEVA[0x%08lX]\n",$mests,$mdeva
+end
+
+
+# Setup all
+define setup
+  sdram_init
+#  lanc_init
+#  dispc_init
+#  set $evb=0x08000000
+end
+
+# Load modules
+define load_modules
+  use_debug_dma
+  load
+#  load busybox.mot
+end
+
+# Set kernel parameters
+define set_kernel_parameters
+  set $param = (void*)0x08001000
+
+  ## MOUNT_ROOT_RDONLY
+  set {long}($param+0x00)=0
+  ## RAMDISK_FLAGS
+  #set {long}($param+0x04)=0
+  ## ORIG_ROOT_DEV
+  #set {long}($param+0x08)=0x00000100
+  ## LOADER_TYPE
+  #set {long}($param+0x0C)=0
+  ## INITRD_START
+  set {long}($param+0x10)=0x082a0000
+  ## INITRD_SIZE
+  set {long}($param+0x14)=0d6200000
+
+  # M32R_CPUCLK
+  set *(unsigned long *)($param + 0x0018) = 0d25000000
+  # M32R_BUSCLK
+  set *(unsigned long *)($param + 0x001c) = 0d25000000
+  # M32R_TIMER_DIVIDE
+  set *(unsigned long *)($param + 0x0020) = 0d128
+
+
+ set {char[0x200]}($param + 0x100) = "console=ttyS0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.2.6 nfsaddrs=192.168.0.102:192.168.0.1:192.168.0.1:255.255.255.0:mappi: \0"
+
+
+end
+
+# Boot
+define boot
+  set_kernel_parameters
+  debug_chaos
+  set $pc=0x08002000
+  set $fp=0
+  del b
+  si
+end
+
+# Restart
+define restart
+  sdireset
+  sdireset
+  setup
+  load_modules
+  boot
+end
+
+sdireset
+sdireset
+file vmlinux
+target m32rsdi
+
+restart
+boot
+
+
diff --git a/arch/m68k/configs/amiga_defconfig b/arch/m68k/configs/amiga_defconfig
new file mode 100644 (file)
index 0000000..a912df1
--- /dev/null
@@ -0,0 +1,964 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:21:25 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-amiga"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+# CONFIG_SUN3 is not set
+CONFIG_AMIGA=y
+# CONFIG_ATARI is not set
+# CONFIG_MAC is not set
+# CONFIG_APOLLO is not set
+# CONFIG_VME is not set
+# CONFIG_HP300 is not set
+# CONFIG_SUN3X is not set
+# CONFIG_Q40 is not set
+
+#
+# Processor type
+#
+CONFIG_M68020=y
+CONFIG_M68030=y
+CONFIG_M68040=y
+CONFIG_M68060=y
+CONFIG_MMU_MOTOROLA=y
+CONFIG_M68KFPU_EMU=y
+CONFIG_M68KFPU_EMU_EXTRAPREC=y
+# CONFIG_M68KFPU_EMU_ONLY is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+CONFIG_ZORRO=y
+CONFIG_AMIGA_PCMCIA=y
+# CONFIG_HEARTBEAT is not set
+CONFIG_PROC_HARDWARE=y
+CONFIG_ISA=y
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_ZORRO_NAMES=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+CONFIG_PARPORT=m
+# CONFIG_PARPORT_PC is not set
+CONFIG_PARPORT_AMIGA=m
+CONFIG_PARPORT_MFC3=m
+# CONFIG_PARPORT_OTHER is not set
+CONFIG_PARPORT_1284=y
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+CONFIG_AMIGA_FLOPPY=y
+CONFIG_AMIGA_Z2RAM=y
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=m
+CONFIG_BLK_DEV_IDEFLOPPY=m
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_IDE_ARM is not set
+CONFIG_BLK_DEV_GAYLE=y
+CONFIG_BLK_DEV_IDEDOUBLER=y
+CONFIG_BLK_DEV_BUDDHA=y
+# CONFIG_IDE_CHIPSETS 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=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_PPA is not set
+# CONFIG_SCSI_IMM is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_A3000_SCSI=y
+CONFIG_A2091_SCSI=y
+CONFIG_GVP11_SCSI=y
+CONFIG_CYBERSTORM_SCSI=y
+CONFIG_CYBERSTORMII_SCSI=y
+CONFIG_BLZ2060_SCSI=y
+CONFIG_BLZ1230_SCSI=y
+CONFIG_FASTLANE_SCSI=y
+CONFIG_OKTAGON_SCSI=y
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# 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 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# 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=m
+CONFIG_ARIADNE=m
+CONFIG_A2065=m
+CONFIG_HYDRA=m
+CONFIG_ZORRO8390=m
+CONFIG_APNE=m
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+CONFIG_PLIP=m
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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=m
+CONFIG_SERIO_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PARKBD is not set
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# 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_KEYBOARD_AMIGA=y
+CONFIG_INPUT_MOUSE=y
+# CONFIG_MOUSE_PS2 is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_INPORT is not set
+# CONFIG_MOUSE_LOGIBM is not set
+# CONFIG_MOUSE_PC110PAD is not set
+CONFIG_MOUSE_AMIGA=y
+# CONFIG_MOUSE_VSXXXAA is not set
+CONFIG_INPUT_JOYSTICK=y
+# CONFIG_JOYSTICK_IFORCE is not set
+# CONFIG_JOYSTICK_WARRIOR is not set
+# CONFIG_JOYSTICK_MAGELLAN is not set
+# CONFIG_JOYSTICK_SPACEORB is not set
+# CONFIG_JOYSTICK_SPACEBALL is not set
+# CONFIG_JOYSTICK_STINGER is not set
+# CONFIG_JOYSTICK_TWIDDLER is not set
+# CONFIG_JOYSTICK_DB9 is not set
+# CONFIG_JOYSTICK_GAMECON is not set
+# CONFIG_JOYSTICK_TURBOGRAFX is not set
+CONFIG_JOYSTICK_AMIGA=m
+# 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
+CONFIG_A2232=m
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+CONFIG_PRINTER=m
+# CONFIG_LP_CONSOLE is not set
+# CONFIG_PPDEV is not set
+# CONFIG_TIPAR is not set
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=m
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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=y
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+CONFIG_FB_CIRRUS=m
+CONFIG_FB_AMIGA=y
+CONFIG_FB_AMIGA_OCS=y
+CONFIG_FB_AMIGA_ECS=y
+CONFIG_FB_AMIGA_AGA=y
+CONFIG_FB_FM2=y
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_FONT_PEARL_8x8=y
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+CONFIG_DMASOUND_PAULA=m
+CONFIG_DMASOUND=m
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+CONFIG_AMIGA_BUILTIN_SERIAL=y
+# CONFIG_WHIPPET_SERIAL is not set
+CONFIG_MULTIFACE_III_TTY=m
+# CONFIG_SERIAL_CONSOLE 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=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# 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=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=y
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+CONFIG_RPCSEC_GSS_KRB5=m
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+CONFIG_NCPFS_NFS_NS=y
+CONFIG_NCPFS_OS2_NS=y
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_AMIGA_PARTITION=y
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68k/configs/apollo_defconfig b/arch/m68k/configs/apollo_defconfig
new file mode 100644 (file)
index 0000000..45ed614
--- /dev/null
@@ -0,0 +1,821 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:21:29 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-apollo"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+# CONFIG_SUN3 is not set
+# CONFIG_AMIGA is not set
+# CONFIG_ATARI is not set
+# CONFIG_MAC is not set
+CONFIG_APOLLO=y
+# CONFIG_VME is not set
+# CONFIG_HP300 is not set
+# CONFIG_SUN3X is not set
+# CONFIG_Q40 is not set
+
+#
+# Processor type
+#
+CONFIG_M68020=y
+CONFIG_M68030=y
+CONFIG_M68040=y
+CONFIG_M68060=y
+CONFIG_MMU_MOTOROLA=y
+CONFIG_M68KFPU_EMU=y
+CONFIG_M68KFPU_EMU_EXTRAPREC=y
+# CONFIG_M68KFPU_EMU_ONLY is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+CONFIG_HEARTBEAT=y
+CONFIG_PROC_HARDWARE=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# 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=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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=m
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# 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 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_APOLLO_ELPLUS=y
+
+#
+# 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=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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=m
+CONFIG_SERIO_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# 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=m
+CONFIG_MOUSE_SERIAL=m
+# 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_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=y
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+CONFIG_DN_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+
+#
+# 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=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# 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=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+CONFIG_NCPFS_NFS_NS=y
+CONFIG_NCPFS_OS2_NS=y
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68k/configs/atari_defconfig b/arch/m68k/configs/atari_defconfig
new file mode 100644 (file)
index 0000000..2e3a162
--- /dev/null
@@ -0,0 +1,876 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:21:34 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-atari"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+# CONFIG_SUN3 is not set
+# CONFIG_AMIGA is not set
+CONFIG_ATARI=y
+# CONFIG_MAC is not set
+# CONFIG_APOLLO is not set
+# CONFIG_VME is not set
+# CONFIG_HP300 is not set
+# CONFIG_SUN3X is not set
+# CONFIG_Q40 is not set
+
+#
+# Processor type
+#
+CONFIG_M68020=y
+CONFIG_M68030=y
+CONFIG_M68040=y
+CONFIG_M68060=y
+CONFIG_MMU_MOTOROLA=y
+CONFIG_M68KFPU_EMU=y
+CONFIG_M68KFPU_EMU_EXTRAPREC=y
+# CONFIG_M68KFPU_EMU_ONLY is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+CONFIG_STRAM_PROC=y
+CONFIG_HEARTBEAT=y
+CONFIG_PROC_HARDWARE=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+CONFIG_PARPORT=m
+# CONFIG_PARPORT_PC is not set
+CONFIG_PARPORT_ATARI=m
+# CONFIG_PARPORT_OTHER is not set
+CONFIG_PARPORT_1284=y
+
+#
+# Plug and Play support
+#
+
+#
+# Block devices
+#
+CONFIG_ATARI_FLOPPY=y
+# CONFIG_PARIDE is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=m
+CONFIG_BLK_DEV_IDEFLOPPY=m
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_IDE_ARM is not set
+CONFIG_BLK_DEV_FALCON_IDE=y
+# 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=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_PPA is not set
+# CONFIG_SCSI_IMM is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# 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 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_ATARILANCE=m
+
+#
+# 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_PLIP is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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_SERPORT=y
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PARKBD is not set
+# CONFIG_SERIO_RAW 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=y
+CONFIG_INPUT_M68K_BEEP=m
+CONFIG_INPUT_UINPUT=m
+
+#
+# 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=y
+CONFIG_LEGACY_PTY_COUNT=256
+CONFIG_PRINTER=m
+# CONFIG_LP_CONSOLE is not set
+# CONFIG_PPDEV is not set
+# CONFIG_TIPAR is not set
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_NVRAM=y
+CONFIG_GEN_RTC=y
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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=y
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_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=m
+CONFIG_DMASOUND_ATARI=m
+CONFIG_DMASOUND=m
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+CONFIG_ATARI_MFPSER=m
+CONFIG_ATARI_SCC=y
+CONFIG_ATARI_SCC_DMA=y
+CONFIG_ATARI_MIDI=m
+CONFIG_ATARI_DSP56K=m
+# CONFIG_SERIAL_CONSOLE 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=m
+# CONFIG_REISERFS_CHECK is not set
+CONFIG_REISERFS_PROC_INFO=y
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# 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=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V4 is not set
+CONFIG_NFSD_TCP=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=m
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+CONFIG_NCPFS_NFS_NS=y
+CONFIG_NCPFS_OS2_NS=y
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_ATARI_PARTITION=y
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68k/configs/bvme6000_defconfig b/arch/m68k/configs/bvme6000_defconfig
new file mode 100644 (file)
index 0000000..5416c54
--- /dev/null
@@ -0,0 +1,820 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:21:38 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-bvme6000"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+# CONFIG_SUN3 is not set
+# CONFIG_AMIGA is not set
+# CONFIG_ATARI is not set
+# CONFIG_MAC is not set
+# CONFIG_APOLLO is not set
+CONFIG_VME=y
+# CONFIG_MVME147 is not set
+# CONFIG_MVME16x is not set
+CONFIG_BVME6000=y
+# CONFIG_HP300 is not set
+# CONFIG_SUN3X is not set
+# CONFIG_Q40 is not set
+
+#
+# Processor type
+#
+# CONFIG_M68020 is not set
+# CONFIG_M68030 is not set
+CONFIG_M68040=y
+CONFIG_M68060=y
+CONFIG_MMU_MOTOROLA=y
+# CONFIG_M68KFPU_EMU is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+CONFIG_PROC_HARDWARE=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# 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=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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=m
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+CONFIG_LLC=m
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+CONFIG_ATALK=m
+# CONFIG_DEV_APPLETALK 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_BVME6000_NET=y
+
+#
+# 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=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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=m
+CONFIG_SERIO_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# 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=m
+CONFIG_MOUSE_SERIAL=m
+# 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_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=m
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+CONFIG_BVME6000_SCC=y
+CONFIG_SERIAL_CONSOLE=y
+
+#
+# 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=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# 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=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+CONFIG_NCPFS_NFS_NS=y
+CONFIG_NCPFS_OS2_NS=y
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=m
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68k/configs/hp300_defconfig b/arch/m68k/configs/hp300_defconfig
new file mode 100644 (file)
index 0000000..3ff6b3b
--- /dev/null
@@ -0,0 +1,820 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:21:44 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-hp300"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+# CONFIG_SUN3 is not set
+# CONFIG_AMIGA is not set
+# CONFIG_ATARI is not set
+# CONFIG_MAC is not set
+# CONFIG_APOLLO is not set
+# CONFIG_VME is not set
+CONFIG_HP300=y
+CONFIG_DIO=y
+# CONFIG_SUN3X is not set
+# CONFIG_Q40 is not set
+
+#
+# Processor type
+#
+CONFIG_M68020=y
+CONFIG_M68030=y
+CONFIG_M68040=y
+CONFIG_M68060=y
+CONFIG_MMU_MOTOROLA=y
+CONFIG_M68KFPU_EMU=y
+CONFIG_M68KFPU_EMU_EXTRAPREC=y
+# CONFIG_M68KFPU_EMU_ONLY is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+CONFIG_HEARTBEAT=y
+CONFIG_PROC_HARDWARE=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# 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=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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=m
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# 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 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_HPLANCE=y
+
+#
+# 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=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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=m
+CONFIG_SERIO_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# 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=m
+CONFIG_MOUSE_SERIAL=m
+# 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_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=y
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+
+#
+# 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=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# 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=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+CONFIG_NCPFS_NFS_NS=y
+CONFIG_NCPFS_OS2_NS=y
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68k/configs/mac_defconfig b/arch/m68k/configs/mac_defconfig
new file mode 100644 (file)
index 0000000..7d7fd05
--- /dev/null
@@ -0,0 +1,899 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:21:47 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-mac"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+# CONFIG_SUN3 is not set
+# CONFIG_AMIGA is not set
+# CONFIG_ATARI is not set
+CONFIG_MAC=y
+CONFIG_NUBUS=y
+CONFIG_M68K_L2_CACHE=y
+# CONFIG_APOLLO is not set
+# CONFIG_VME is not set
+# CONFIG_HP300 is not set
+# CONFIG_SUN3X is not set
+# CONFIG_Q40 is not set
+
+#
+# Processor type
+#
+CONFIG_M68020=y
+CONFIG_M68030=y
+CONFIG_M68040=y
+# CONFIG_M68060 is not set
+CONFIG_MMU_MOTOROLA=y
+CONFIG_M68KFPU_EMU=y
+CONFIG_M68KFPU_EMU_EXTRAPREC=y
+# CONFIG_M68KFPU_EMU_ONLY is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+# CONFIG_HEARTBEAT is not set
+CONFIG_PROC_HARDWARE=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# 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=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=m
+CONFIG_BLK_DEV_IDEFLOPPY=m
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_IDE_ARM is not set
+CONFIG_BLK_DEV_MAC_IDE=y
+# 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=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_MAC_SCSI=y
+CONFIG_SCSI_MAC_ESP=y
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Macintosh device drivers
+#
+CONFIG_ADB=y
+CONFIG_ADB_MACII=y
+CONFIG_ADB_MACIISI=y
+CONFIG_ADB_IOP=y
+CONFIG_ADB_PMU68K=y
+CONFIG_ADB_CUDA=y
+CONFIG_INPUT_ADBHID=y
+CONFIG_MAC_EMUMOUSEBTN=y
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# 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 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MII is not set
+CONFIG_MAC8390=y
+CONFIG_MAC89x0=y
+CONFIG_MACSONIC=y
+CONFIG_MACMACE=y
+# CONFIG_NET_VENDOR_SMC 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=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+# CONFIG_PPP_ASYNC is not set
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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=m
+CONFIG_SERIO_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# 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=m
+CONFIG_MOUSE_SERIAL=m
+# 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_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=m
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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=y
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+CONFIG_FB_VALKYRIE=y
+CONFIG_FB_MAC=y
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_FONT_6x11=y
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+CONFIG_LOGO_MAC_CLUT224=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+CONFIG_MAC_SCC=y
+CONFIG_MAC_HID=y
+CONFIG_MAC_ADBKEYCODES=y
+CONFIG_SERIAL_CONSOLE=y
+
+#
+# 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=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# 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=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=y
+CONFIG_HFSPLUS_FS=y
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+CONFIG_RPCSEC_GSS_KRB5=m
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+CONFIG_NCPFS_NFS_NS=y
+CONFIG_NCPFS_OS2_NS=y
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API 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=y
+CONFIG_ATARI_PARTITION=y
+CONFIG_MAC_PARTITION=y
+CONFIG_MSDOS_PARTITION=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_UNIXWARE_DISKLABEL=y
+CONFIG_LDM_PARTITION=y
+CONFIG_LDM_DEBUG=y
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+CONFIG_SUN_PARTITION=y
+# CONFIG_EFI_PARTITION is not set
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68k/configs/mvme147_defconfig b/arch/m68k/configs/mvme147_defconfig
new file mode 100644 (file)
index 0000000..84610d3
--- /dev/null
@@ -0,0 +1,839 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:21:49 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-mvme147"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+# CONFIG_SUN3 is not set
+# CONFIG_AMIGA is not set
+# CONFIG_ATARI is not set
+# CONFIG_MAC is not set
+# CONFIG_APOLLO is not set
+CONFIG_VME=y
+CONFIG_MVME147=y
+# CONFIG_MVME16x is not set
+# CONFIG_BVME6000 is not set
+# CONFIG_HP300 is not set
+# CONFIG_SUN3X is not set
+# CONFIG_Q40 is not set
+
+#
+# Processor type
+#
+# CONFIG_M68020 is not set
+CONFIG_M68030=y
+# CONFIG_M68040 is not set
+# CONFIG_M68060 is not set
+CONFIG_MMU_MOTOROLA=y
+# CONFIG_M68KFPU_EMU is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+CONFIG_PROC_HARDWARE=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# 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=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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=m
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_MVME147_SCSI=y
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# 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 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_MVME147_NET=y
+
+#
+# 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=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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=m
+CONFIG_SERIO_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# 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=m
+CONFIG_MOUSE_SERIAL=m
+# 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_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=m
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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=y
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+CONFIG_MVME147_SCC=y
+CONFIG_SERIAL_CONSOLE=y
+
+#
+# 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=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68k/configs/mvme16x_defconfig b/arch/m68k/configs/mvme16x_defconfig
new file mode 100644 (file)
index 0000000..688c9e7
--- /dev/null
@@ -0,0 +1,838 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:21:52 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-mvme16x"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+# CONFIG_SUN3 is not set
+# CONFIG_AMIGA is not set
+# CONFIG_ATARI is not set
+# CONFIG_MAC is not set
+# CONFIG_APOLLO is not set
+CONFIG_VME=y
+# CONFIG_MVME147 is not set
+CONFIG_MVME16x=y
+# CONFIG_BVME6000 is not set
+# CONFIG_HP300 is not set
+# CONFIG_SUN3X is not set
+# CONFIG_Q40 is not set
+
+#
+# Processor type
+#
+# CONFIG_M68020 is not set
+# CONFIG_M68030 is not set
+CONFIG_M68040=y
+CONFIG_M68060=y
+CONFIG_MMU_MOTOROLA=y
+# CONFIG_M68KFPU_EMU is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+CONFIG_PROC_HARDWARE=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# 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=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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=m
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# 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 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_MVME16x_NET=y
+
+#
+# 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=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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=m
+CONFIG_SERIO_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# 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=m
+CONFIG_MOUSE_SERIAL=m
+# 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_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=m
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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=y
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+CONFIG_MVME162_SCC=y
+CONFIG_SERIAL_CONSOLE=y
+
+#
+# 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=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68k/configs/q40_defconfig b/arch/m68k/configs/q40_defconfig
new file mode 100644 (file)
index 0000000..6ede4e7
--- /dev/null
@@ -0,0 +1,911 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:21:55 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-q40"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+# CONFIG_SUN3 is not set
+# CONFIG_AMIGA is not set
+# CONFIG_ATARI is not set
+# CONFIG_MAC is not set
+# CONFIG_APOLLO is not set
+# CONFIG_VME is not set
+# CONFIG_HP300 is not set
+# CONFIG_SUN3X is not set
+CONFIG_Q40=y
+
+#
+# Processor type
+#
+# CONFIG_M68020 is not set
+# CONFIG_M68030 is not set
+CONFIG_M68040=y
+CONFIG_M68060=y
+CONFIG_MMU_MOTOROLA=y
+CONFIG_M68KFPU_EMU=y
+CONFIG_M68KFPU_EMU_EXTRAPREC=y
+# CONFIG_M68KFPU_EMU_ONLY is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+# CONFIG_HEARTBEAT is not set
+CONFIG_PROC_HARDWARE=y
+CONFIG_ISA=y
+CONFIG_GENERIC_ISA_DMA=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# 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
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=m
+CONFIG_BLK_DEV_IDEFLOPPY=m
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_IDE_ARM is not set
+CONFIG_BLK_DEV_Q40IDE=y
+# CONFIG_IDE_CHIPSETS 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=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# 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 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# 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=m
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NE2000=m
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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=m
+CONFIG_SERIO_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+CONFIG_SERIO_Q40KBD=m
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# 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=m
+CONFIG_MOUSE_SERIAL=m
+# CONFIG_MOUSE_INPORT is not set
+# CONFIG_MOUSE_LOGIBM is not set
+# CONFIG_MOUSE_PC110PAD 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_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=m
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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=y
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+CONFIG_FB_Q40=y
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+CONFIG_DMASOUND_Q40=y
+CONFIG_DMASOUND=y
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+
+#
+# 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=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# 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=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+CONFIG_UFS_FS_WRITE=y
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68k/configs/sun3_defconfig b/arch/m68k/configs/sun3_defconfig
new file mode 100644 (file)
index 0000000..e4e473e
--- /dev/null
@@ -0,0 +1,826 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:21:58 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-sun3"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+CONFIG_SUN3=y
+
+#
+# Processor type
+#
+CONFIG_M68020=y
+CONFIG_MMU_SUN3=y
+CONFIG_M68KFPU_EMU=y
+CONFIG_M68KFPU_EMU_EXTRAPREC=y
+# CONFIG_M68KFPU_EMU_ONLY is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+CONFIG_PROC_HARDWARE=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# 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=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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=m
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SUN3_SCSI=y
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# 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 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_SUN3LANCE=y
+CONFIG_SUN3_82586=y
+
+#
+# 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=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_SUNKBD=y
+# 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=m
+CONFIG_MOUSE_SERIAL=m
+# 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_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=y
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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=y
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+
+#
+# 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=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+CONFIG_UFS_FS_WRITE=y
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68k/configs/sun3x_defconfig b/arch/m68k/configs/sun3x_defconfig
new file mode 100644 (file)
index 0000000..f9acd19
--- /dev/null
@@ -0,0 +1,836 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc3-m68k
+# Sun Dec  5 14:22:01 2004
+#
+CONFIG_M68K=y
+CONFIG_MMU=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION="-sun3x"
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_SYSCTL=y
+CONFIG_AUDIT=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Platform dependent setup
+#
+# CONFIG_SUN3 is not set
+# CONFIG_AMIGA is not set
+# CONFIG_ATARI is not set
+# CONFIG_MAC is not set
+# CONFIG_APOLLO is not set
+# CONFIG_VME is not set
+# CONFIG_HP300 is not set
+CONFIG_SUN3X=y
+# CONFIG_Q40 is not set
+
+#
+# Processor type
+#
+# CONFIG_M68020 is not set
+CONFIG_M68030=y
+# CONFIG_M68040 is not set
+# CONFIG_M68060 is not set
+CONFIG_MMU_MOTOROLA=y
+CONFIG_M68KFPU_EMU=y
+CONFIG_M68KFPU_EMU_EXTRAPREC=y
+# CONFIG_M68KFPU_EMU_ONLY is not set
+# CONFIG_ADVANCED is not set
+
+#
+# General setup
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+CONFIG_PROC_HARDWARE=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# 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=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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=m
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SUN3X_ESP=y
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+# CONFIG_MD_RAID10 is not set
+CONFIG_MD_RAID5=m
+CONFIG_MD_RAID6=m
+CONFIG_MD_MULTIPATH=m
+# CONFIG_MD_FAULTY is not set
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+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=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_REALM=m
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+CONFIG_IP_NF_MATCH_CONNMARK=m
+CONFIG_IP_NF_MATCH_HASHLIMIT=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=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=m
+CONFIG_IP_NF_TARGET_SAME=m
+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_NAT_TFTP=m
+CONFIG_IP_NF_NAT_AMANDA=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_CLASSIFY=m
+CONFIG_IP_NF_TARGET_CONNMARK=m
+# CONFIG_IP_NF_TARGET_CLUSTERIP is not set
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_TARGET_NOTRACK=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+# CONFIG_SCTP_HMAC_SHA1 is not set
+CONFIG_SCTP_HMAC_MD5=y
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# 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 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+CONFIG_NET_CLS_ROUTE=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_RX is not set
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=m
+# CONFIG_BONDING is not set
+CONFIG_EQUALIZER=m
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_SUN3LANCE=y
+
+#
+# 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=m
+# CONFIG_PPP_MULTILINK is not set
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+CONFIG_SLIP_MODE_SLIP6=y
+CONFIG_SHAPER=m
+CONFIG_NETCONSOLE=m
+
+#
+# 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_SERPORT=m
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_RAW is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_SUNKBD=y
+# 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=m
+CONFIG_MOUSE_SERIAL=m
+# 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_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_GEN_RTC=y
+CONFIG_GEN_RTC_X=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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=y
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB_ARCH_HAS_HCD is not set
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Character devices
+#
+
+#
+# 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=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+CONFIG_JFS_FS=m
+# CONFIG_JFS_POSIX_ACL is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+CONFIG_MINIX_FS=y
+# CONFIG_ROMFS_FS is not set
+CONFIG_QUOTA=y
+# CONFIG_QFMT_V1 is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=y
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+CONFIG_HPFS_FS=m
+# CONFIG_QNX4FS_FS is not set
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+CONFIG_UFS_FS_WRITE=y
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+# CONFIG_CIFS is not set
+CONFIG_NCP_FS=m
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+CONFIG_NCPFS_NLS=y
+# CONFIG_NCPFS_EXTRAS is not set
+CONFIG_CODA_FS=m
+# CONFIG_CODA_FS_OLD_API is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_INFO is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
diff --git a/arch/m68knommu/lib/delay.c b/arch/m68knommu/lib/delay.c
new file mode 100644 (file)
index 0000000..7bd4323
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ *     arch/m68knommu/lib/delay.c
+ *
+ *     (C) Copyright 2004, Greg Ungerer <gerg@snapgear.com>
+ *
+ * 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 <asm/param.h>
+#include <asm/delay.h>
+
+void udelay(unsigned long usecs)
+{
+       _udelay(usecs);
+}
+
diff --git a/arch/m68knommu/platform/5272/CANCam/crt0_ram.S b/arch/m68knommu/platform/5272/CANCam/crt0_ram.S
new file mode 100644 (file)
index 0000000..06ee803
--- /dev/null
@@ -0,0 +1,154 @@
+/*****************************************************************************/
+
+/*
+ *     crt0_ram.S -- startup code for Feith CANCan board.
+ *
+ *     (C) Copyright 1999-2002, Greg Ungerer (gerg@snapgear.com).
+ *     (C) Copyright 2000, Lineo (www.lineo.com).
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+/*****************************************************************************/
+
+/*
+ *     Feith ColdFire CANCam, chip select and memory setup.
+ */
+
+#define        MEM_BASE        0x00000000      /* Memory base at address 0 */
+#define        VBR_BASE        MEM_BASE        /* Vector address */
+#define MEM_SIZE  0x04000000      /* Memory size 64Mb */
+
+
+/*****************************************************************************/
+
+.global        _start
+.global _rambase
+.global _ramvec
+.global        _ramstart
+.global        _ramend
+
+/*****************************************************************************/
+
+.data
+
+/*
+ *     Set up the usable of RAM stuff. Size of RAM is determined then
+ *     an initial stack set up at the end.
+ */
+_rambase:
+.long  0
+_ramvec:
+.long  0
+_ramstart:
+.long  0
+_ramend:
+.long  0
+
+/*****************************************************************************/
+
+.text
+
+/*
+ *     This is the codes first entry point. This is where it all
+ *     begins...
+ */
+
+_start:
+       nop                                     /* Filler */
+       move.w  #0x2700, %sr                    /* No interrupts */
+
+       /*
+        * Setup VBR here, otherwise buserror remap will not work.
+        * if dBug was active before (on my SBC with dBug 1.1 of Dec 16 1996)
+        *
+        * bkr@cut.de 19990306
+        *
+        * Note: this is because dBUG points VBR to ROM, making vectors read
+        * only, so the bus trap can't be changed. (RS)
+        */
+       move.l  #VBR_BASE, %a7                  /* Note VBR can't be read */
+       movec   %a7, %VBR
+       move.l  %a7, _ramvec                    /* Set up vector addr */
+       move.l  %a7, _rambase                   /* Set up base RAM addr */
+
+
+       /*
+        *      Set memory size.
+        */
+       move.l  #MEM_SIZE, %a0
+
+       move.l  %a0, %d0                        /* Mem end addr is in a0 */
+       move.l  %d0, %sp                        /* Set up initial stack ptr */
+       move.l  %d0, _ramend                    /* Set end ram addr */
+
+       /*
+        *      Enable CPU internal cache.
+        */
+       move.l  #0x01000000, %d0                /* Invalidate cache cmd */
+       movec   %d0, %CACR                      /* Invalidate cache */
+       move.l  #0x80000100, %d0                /* Setup cache mask */
+       movec   %d0, %CACR                      /* Enable cache */
+       nop
+
+#ifdef CONFIG_ROMFS_FS
+       /*
+        *      Move ROM filesystem above bss :-)
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Set up destination  */
+       move.l  %a0, %a2                        /* Copy of bss start */
+
+       move.l  8(%a0), %d0                     /* Get size of ROMFS */
+       addq.l  #8, %d0                         /* Allow for rounding */
+       and.l   #0xfffffffc, %d0                /* Whole words */
+
+       add.l   %d0, %a0                        /* Copy from end */
+       add.l   %d0, %a1                        /* Copy from end */
+       move.l  %a1, _ramstart                  /* Set start of ram */
+
+_copy_romfs:
+       move.l  -(%a0), %d0                     /* Copy dword */
+       move.l  %d0, -(%a1)
+       cmp.l   %a0, %a2                        /* Check if at end */
+       bne     _copy_romfs
+
+#else /* CONFIG_ROMFS_FS */
+       lea.l   _ebss, %a1
+       move.l  %a1, _ramstart
+#endif /* CONFIG_ROMFS_FS */
+
+
+       /*
+        *      Zero out the bss region.
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Get end of bss */
+       clr.l   %d0                             /* Set value */
+_clear_bss:
+       move.l  %d0, (%a0)+                     /* Clear each word */
+       cmp.l   %a0, %a1                        /* Check if at end */
+       bne     _clear_bss
+
+       /*
+        *      Load the current thread pointer and stack.
+        */
+       lea     init_thread_union, %a0
+       lea     0x2000(%a0), %sp
+
+       /*
+        *      Assember start up done, start code proper.
+        */
+       jsr     start_kernel                    /* Start Linux kernel */
+
+_exit:
+       jmp     _exit                           /* Should never get here */
+
+/*****************************************************************************/
diff --git a/arch/m68knommu/platform/5272/SCALES/crt0_ram.S b/arch/m68knommu/platform/5272/SCALES/crt0_ram.S
new file mode 100644 (file)
index 0000000..fd69270
--- /dev/null
@@ -0,0 +1,154 @@
+/*****************************************************************************/
+
+/*
+ *     crt0_ram.S -- startup code for Feith SCALES board.
+ *
+ *     (C) Copyright 1999-2002, Greg Ungerer (gerg@snapgear.com).
+ *     (C) Copyright 2000, Lineo (www.lineo.com).
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+/*****************************************************************************/
+
+/*
+ *     Feith ColdFire SCALES, chip select and memory setup.
+ */
+
+#define        MEM_BASE        0x00000000      /* Memory base at address 0 */
+#define        VBR_BASE        MEM_BASE        /* Vector address */
+#define MEM_SIZE  0x02000000      /* Memory size 32Mb */
+
+
+/*****************************************************************************/
+
+.global        _start
+.global _rambase
+.global _ramvec
+.global        _ramstart
+.global        _ramend
+
+/*****************************************************************************/
+
+.data
+
+/*
+ *     Set up the usable of RAM stuff. Size of RAM is determined then
+ *     an initial stack set up at the end.
+ */
+_rambase:
+.long  0
+_ramvec:
+.long  0
+_ramstart:
+.long  0
+_ramend:
+.long  0
+
+/*****************************************************************************/
+
+.text
+
+/*
+ *     This is the codes first entry point. This is where it all
+ *     begins...
+ */
+
+_start:
+       nop                                     /* Filler */
+       move.w  #0x2700, %sr                    /* No interrupts */
+
+       /*
+        * Setup VBR here, otherwise buserror remap will not work.
+        * if dBug was active before (on my SBC with dBug 1.1 of Dec 16 1996)
+        *
+        * bkr@cut.de 19990306
+        *
+        * Note: this is because dBUG points VBR to ROM, making vectors read
+        * only, so the bus trap can't be changed. (RS)
+        */
+       move.l  #VBR_BASE, %a7                  /* Note VBR can't be read */
+       movec   %a7, %VBR
+       move.l  %a7, _ramvec                    /* Set up vector addr */
+       move.l  %a7, _rambase                   /* Set up base RAM addr */
+
+
+       /*
+        *      Set memory size.
+        */
+       move.l  #MEM_SIZE, %a0
+
+       move.l  %a0, %d0                        /* Mem end addr is in a0 */
+       move.l  %d0, %sp                        /* Set up initial stack ptr */
+       move.l  %d0, _ramend                    /* Set end ram addr */
+
+       /*
+        *      Enable CPU internal cache.
+        */
+       move.l  #0x01000000, %d0                /* Invalidate cache cmd */
+       movec   %d0, %CACR                      /* Invalidate cache */
+       move.l  #0x80000100, %d0                /* Setup cache mask */
+       movec   %d0, %CACR                      /* Enable cache */
+       nop
+
+#ifdef CONFIG_ROMFS_FS
+       /*
+        *      Move ROM filesystem above bss :-)
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Set up destination  */
+       move.l  %a0, %a2                        /* Copy of bss start */
+
+       move.l  8(%a0), %d0                     /* Get size of ROMFS */
+       addq.l  #8, %d0                         /* Allow for rounding */
+       and.l   #0xfffffffc, %d0                /* Whole words */
+
+       add.l   %d0, %a0                        /* Copy from end */
+       add.l   %d0, %a1                        /* Copy from end */
+       move.l  %a1, _ramstart                  /* Set start of ram */
+
+_copy_romfs:
+       move.l  -(%a0), %d0                     /* Copy dword */
+       move.l  %d0, -(%a1)
+       cmp.l   %a0, %a2                        /* Check if at end */
+       bne     _copy_romfs
+
+#else /* CONFIG_ROMFS_FS */
+       lea.l   _ebss, %a1
+       move.l  %a1, _ramstart
+#endif /* CONFIG_ROMFS_FS */
+
+
+       /*
+        *      Zero out the bss region.
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Get end of bss */
+       clr.l   %d0                             /* Set value */
+_clear_bss:
+       move.l  %d0, (%a0)+                     /* Clear each word */
+       cmp.l   %a0, %a1                        /* Check if at end */
+       bne     _clear_bss
+
+       /*
+        *      Load the current thread pointer and stack.
+        */
+       lea     init_thread_union, %a0
+       lea     0x2000(%a0), %sp
+
+       /*
+        *      Assember start up done, start code proper.
+        */
+       jsr     start_kernel                    /* Start Linux kernel */
+
+_exit:
+       jmp     _exit                           /* Should never get here */
+
+/*****************************************************************************/
diff --git a/arch/m68knommu/platform/527x/M5271EVB/crt0_ram.S b/arch/m68knommu/platform/527x/M5271EVB/crt0_ram.S
new file mode 100644 (file)
index 0000000..41f533f
--- /dev/null
@@ -0,0 +1,166 @@
+/*****************************************************************************/
+
+/*
+ *     crt0_ram.S -- startup code for MCF527x ColdFire based Freescale boards.
+ *
+ *     (C) Copyright 2003-2004, Greg Ungerer (gerg@snapgear.com).
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+/*****************************************************************************/
+
+/*
+ *     Freescale M5271EVB ColdFire eval board, chip select and memory setup.
+ */
+
+#define        MEM_BASE        0x00000000      /* Memory base at address 0 */
+#define        VBR_BASE        MEM_BASE        /* Vector address */
+
+#if defined(CONFIG_RAM4MB)
+#define        MEM_SIZE        0x00400000      /* Memory size 4Mb */
+#elif defined(CONFIG_RAM8MB)
+#define        MEM_SIZE        0x00800000      /* Memory size 8Mb */
+#else
+#define        MEM_SIZE        0x01000000      /* Memory size 16Mb */
+#endif
+
+/*****************************************************************************/
+
+.global        _start
+.global _rambase
+.global _ramvec
+.global        _ramstart
+.global        _ramend
+
+/*****************************************************************************/
+
+.data
+
+/*
+ *     Set up the usable of RAM stuff. Size of RAM is determined then
+ *     an initial stack set up at the end.
+ */
+_rambase:
+.long  0
+_ramvec:
+.long  0
+_ramstart:
+.long  0
+_ramend:
+.long  0
+
+/*****************************************************************************/
+
+.text
+
+/*
+ *     This is the codes first entry point. This is where it all
+ *     begins...
+ */
+
+_start:
+       nop                                     /* Filler */
+       move.w  #0x2700, %sr                    /* No interrupts */
+
+       /*
+        * Setup VBR here, otherwise buserror remap will not work.
+        * if dBug was active before (on my SBC with dBug 1.1 of Dec 16 1996)
+        *
+        * bkr@cut.de 19990306
+        *
+        * Note: this is because dBUG points VBR to ROM, making vectors read
+        * only, so the bus trap can't be changed. (RS)
+        */
+       move.l  #VBR_BASE, %a7                  /* Note VBR can't be read */
+       movec   %a7, %VBR
+       move.l  %a7, _ramvec                    /* Set up vector addr */
+       move.l  %a7, _rambase                   /* Set up base RAM addr */
+
+
+       /*
+        *      Set memory size.
+        */
+       move.l  #MEM_SIZE, %a0
+
+       move.l  %a0, %d0                        /* Mem end addr is in a0 */
+       move.l  %d0, %sp                        /* Set up initial stack ptr */
+       move.l  %d0, _ramend                    /* Set end ram addr */
+
+       /*
+        *      Enable CPU internal cache.
+        */
+       move.l  #0x01400000, %d0
+       movec   %d0, %CACR                      /* Invalidate cache */
+       nop
+
+       move.l  #0x0000c000, %d0                /* Set SDRAM cached only */
+       movec   %d0, %ACR0
+       move.l  #0x00000000, %d0                /* No other regions cached */
+       movec   %d0, %ACR1
+
+       move.l  #0x80400100, %d0                /* Configure cache */
+       movec   %d0, %CACR                      /* Enable cache */
+       nop
+
+
+#ifdef CONFIG_ROMFS_FS
+       /*
+        *      Move ROM filesystem above bss :-)
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Set up destination  */
+       move.l  %a0, %a2                        /* Copy of bss start */
+
+       move.l  8(%a0), %d0                     /* Get size of ROMFS */
+       addq.l  #8, %d0                         /* Allow for rounding */
+       and.l   #0xfffffffc, %d0                /* Whole words */
+
+       add.l   %d0, %a0                        /* Copy from end */
+       add.l   %d0, %a1                        /* Copy from end */
+       move.l  %a1, _ramstart                  /* Set start of ram */
+
+_copy_romfs:
+       move.l  -(%a0), %d0                     /* Copy dword */
+       move.l  %d0, -(%a1)
+       cmp.l   %a0, %a2                        /* Check if at end */
+       bne     _copy_romfs
+#else /* CONFIG_ROMFS_FS */
+       lea.l   _ebss, %a1
+       move.l  %a1, _ramstart
+#endif /* CONFIG_ROMFS_FS */
+
+
+       /*
+        *      Zero out the bss region.
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Get end of bss */
+       clr.l   %d0                             /* Set value */
+_clear_bss:
+       move.l  %d0, (%a0)+                     /* Clear each word */
+       cmp.l   %a0, %a1                        /* Check if at end */
+       bne     _clear_bss
+
+       /*
+        *      Load the current thread pointer and stack.
+        */
+       lea     init_thread_union, %a0
+       lea     0x2000(%a0), %sp
+
+       /*
+        *      Assember start up done, start code proper.
+        */
+       jsr     start_kernel                    /* Start Linux kernel */
+
+_exit:
+       jmp     _exit                           /* Should never get here */
+
+/*****************************************************************************/
diff --git a/arch/m68knommu/platform/527x/M5275EVB/crt0_ram.S b/arch/m68knommu/platform/527x/M5275EVB/crt0_ram.S
new file mode 100644 (file)
index 0000000..ceb9920
--- /dev/null
@@ -0,0 +1,166 @@
+/*****************************************************************************/
+
+/*
+ *     crt0_ram.S -- startup code for MCF527x ColdFire based Freescale boards.
+ *
+ *     (C) Copyright 2003-2004, Greg Ungerer (gerg@snapgear.com).
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+/*****************************************************************************/
+
+/*
+ *     Freescale M5275EVB ColdFire eval board, chip select and memory setup.
+ */
+
+#define        MEM_BASE        0x00000000      /* Memory base at address 0 */
+#define        VBR_BASE        MEM_BASE        /* Vector address */
+
+#if defined(CONFIG_RAM4MB)
+#define        MEM_SIZE        0x00400000      /* Memory size 4Mb */
+#elif defined(CONFIG_RAM8MB)
+#define        MEM_SIZE        0x00800000      /* Memory size 8Mb */
+#else
+#define        MEM_SIZE        0x01000000      /* Memory size 16Mb */
+#endif
+
+/*****************************************************************************/
+
+.global        _start
+.global _rambase
+.global _ramvec
+.global        _ramstart
+.global        _ramend
+
+/*****************************************************************************/
+
+.data
+
+/*
+ *     Set up the usable of RAM stuff. Size of RAM is determined then
+ *     an initial stack set up at the end.
+ */
+_rambase:
+.long  0
+_ramvec:
+.long  0
+_ramstart:
+.long  0
+_ramend:
+.long  0
+
+/*****************************************************************************/
+
+.text
+
+/*
+ *     This is the codes first entry point. This is where it all
+ *     begins...
+ */
+
+_start:
+       nop                                     /* Filler */
+       move.w  #0x2700, %sr                    /* No interrupts */
+
+       /*
+        * Setup VBR here, otherwise buserror remap will not work.
+        * if dBug was active before (on my SBC with dBug 1.1 of Dec 16 1996)
+        *
+        * bkr@cut.de 19990306
+        *
+        * Note: this is because dBUG points VBR to ROM, making vectors read
+        * only, so the bus trap can't be changed. (RS)
+        */
+       move.l  #VBR_BASE, %a7                  /* Note VBR can't be read */
+       movec   %a7, %VBR
+       move.l  %a7, _ramvec                    /* Set up vector addr */
+       move.l  %a7, _rambase                   /* Set up base RAM addr */
+
+
+       /*
+        *      Set memory size.
+        */
+       move.l  #MEM_SIZE, %a0
+
+       move.l  %a0, %d0                        /* Mem end addr is in a0 */
+       move.l  %d0, %sp                        /* Set up initial stack ptr */
+       move.l  %d0, _ramend                    /* Set end ram addr */
+
+       /*
+        *      Enable CPU internal cache.
+        */
+       move.l  #0x01400000, %d0
+       movec   %d0, %CACR                      /* Invalidate cache */
+       nop
+
+       move.l  #0x0000c000, %d0                /* Set SDRAM cached only */
+       movec   %d0, %ACR0
+       move.l  #0x00000000, %d0                /* No other regions cached */
+       movec   %d0, %ACR1
+
+       move.l  #0x80400100, %d0                /* Configure cache */
+       movec   %d0, %CACR                      /* Enable cache */
+       nop
+
+
+#ifdef CONFIG_ROMFS_FS
+       /*
+        *      Move ROM filesystem above bss :-)
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Set up destination  */
+       move.l  %a0, %a2                        /* Copy of bss start */
+
+       move.l  8(%a0), %d0                     /* Get size of ROMFS */
+       addq.l  #8, %d0                         /* Allow for rounding */
+       and.l   #0xfffffffc, %d0                /* Whole words */
+
+       add.l   %d0, %a0                        /* Copy from end */
+       add.l   %d0, %a1                        /* Copy from end */
+       move.l  %a1, _ramstart                  /* Set start of ram */
+
+_copy_romfs:
+       move.l  -(%a0), %d0                     /* Copy dword */
+       move.l  %d0, -(%a1)
+       cmp.l   %a0, %a2                        /* Check if at end */
+       bne     _copy_romfs
+#else /* CONFIG_ROMFS_FS */
+       lea.l   _ebss, %a1
+       move.l  %a1, _ramstart
+#endif /* CONFIG_ROMFS_FS */
+
+
+       /*
+        *      Zero out the bss region.
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Get end of bss */
+       clr.l   %d0                             /* Set value */
+_clear_bss:
+       move.l  %d0, (%a0)+                     /* Clear each word */
+       cmp.l   %a0, %a1                        /* Check if at end */
+       bne     _clear_bss
+
+       /*
+        *      Load the current thread pointer and stack.
+        */
+       lea     init_thread_union, %a0
+       lea     0x2000(%a0), %sp
+
+       /*
+        *      Assember start up done, start code proper.
+        */
+       jsr     start_kernel                    /* Start Linux kernel */
+
+_exit:
+       jmp     _exit                           /* Should never get here */
+
+/*****************************************************************************/
diff --git a/arch/m68knommu/platform/527x/Makefile b/arch/m68knommu/platform/527x/Makefile
new file mode 100644 (file)
index 0000000..e49335f
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# Makefile for the linux kernel.
+#
+
+#
+# If you want to play with the HW breakpoints then you will
+# need to add define this,  which will give you a stack backtrace
+# on the console port whenever a DBG interrupt occurs.  You have to
+# set up you HW breakpoints to trigger a DBG interrupt:
+#
+# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT
+# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT
+#
+
+ifdef CONFIG_FULLDEBUG
+AFLAGS += -DDEBUGGER_COMPATIBLE_CACHE=1
+endif
+
+obj-y := config.o
+
+extra-y := $(BOARD)/crt0_$(MODEL).o
diff --git a/arch/m68knommu/platform/527x/config.c b/arch/m68knommu/platform/527x/config.c
new file mode 100644 (file)
index 0000000..804de33
--- /dev/null
@@ -0,0 +1,82 @@
+/***************************************************************************/
+
+/*
+ *     linux/arch/m68knommu/platform/527x/config.c
+ *
+ *     Sub-architcture dependant initialization code for the Freescale
+ *     5270/5271 CPUs.
+ *
+ *     Copyright (C) 1999-2004, Greg Ungerer (gerg@snapgear.com)
+ *     Copyright (C) 2001-2004, SnapGear Inc. (www.snapgear.com)
+ */
+
+/***************************************************************************/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/param.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/dma.h>
+#include <asm/traps.h>
+#include <asm/machdep.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+#include <asm/mcfdma.h>
+
+/***************************************************************************/
+
+void coldfire_pit_tick(void);
+void coldfire_pit_init(irqreturn_t (*handler)(int, void *, struct pt_regs *));
+unsigned long coldfire_pit_offset(void);
+void coldfire_trap_init(void);
+void coldfire_reset(void);
+
+/***************************************************************************/
+
+/*
+ *     DMA channel base address table.
+ */
+unsigned int   dma_base_addr[MAX_M68K_DMA_CHANNELS] = {
+        MCF_MBAR + MCFDMA_BASE0,
+};
+
+unsigned int dma_device_address[MAX_M68K_DMA_CHANNELS];
+
+/***************************************************************************/
+
+void mcf_disableall(void)
+{
+       *((volatile unsigned long *) (MCF_IPSBAR + MCFICM_INTC0 + MCFINTC_IMRH)) = 0xffffffff;
+       *((volatile unsigned long *) (MCF_IPSBAR + MCFICM_INTC0 + MCFINTC_IMRL)) = 0xffffffff;
+}
+
+/***************************************************************************/
+
+void mcf_autovector(unsigned int vec)
+{
+       /* Everything is auto-vectored on the 5272 */
+}
+
+/***************************************************************************/
+
+void config_BSP(char *commandp, int size)
+{
+       mcf_disableall();
+
+#ifdef CONFIG_BOOTPARAM
+       strncpy(commandp, CONFIG_BOOTPARAM_STRING, size);
+       commandp[size-1] = 0;
+#else
+       memset(commandp, 0, size);
+#endif
+
+       mach_sched_init = coldfire_pit_init;
+       mach_tick = coldfire_pit_tick;
+       mach_gettimeoffset = coldfire_pit_offset;
+       mach_trap_init = coldfire_trap_init;
+       mach_reset = coldfire_reset;
+}
+
+/***************************************************************************/
diff --git a/arch/m68knommu/platform/528x/M5282EVB/crt0_ram.S b/arch/m68knommu/platform/528x/M5282EVB/crt0_ram.S
new file mode 100644 (file)
index 0000000..2b14d9a
--- /dev/null
@@ -0,0 +1,171 @@
+/*****************************************************************************/
+
+/*
+ *     crt0_ram.S -- startup code for M5282EVB ColdFire based MOTOROLA boards.
+ *
+ *     (C) Copyright 2003, Greg Ungerer (gerg@snapgear.com).
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+/*****************************************************************************/
+
+/*
+ *     Motorola M5282EVB ColdFire eval board, chip select and memory setup.
+ */
+
+#define        MEM_BASE        0x00000000      /* Memory base at address 0 */
+#define        VBR_BASE        MEM_BASE        /* Vector address */
+
+#if defined(CONFIG_RAM16MB)
+#define        MEM_SIZE        0x01000000      /* Memory size 16Mb */
+#elif defined(CONFIG_RAM8MB)
+#define        MEM_SIZE        0x00800000      /* Memory size 8Mb */
+#else
+#define        MEM_SIZE        0x00400000      /* Memory size 4Mb */
+#endif
+
+/*****************************************************************************/
+
+.global        _start
+.global _rambase
+.global _ramvec
+.global        _ramstart
+.global        _ramend
+
+/*****************************************************************************/
+
+.data
+
+/*
+ *     Set up the usable of RAM stuff. Size of RAM is determined then
+ *     an initial stack set up at the end.
+ */
+_rambase:
+.long  0
+_ramvec:
+.long  0
+_ramstart:
+.long  0
+_ramend:
+.long  0
+
+/*****************************************************************************/
+
+.text
+
+/*
+ *     This is the codes first entry point. This is where it all
+ *     begins...
+ */
+
+_start:
+       nop                                     /* Filler */
+       move.w  #0x2700, %sr                    /* No interrupts */
+
+       /*
+        * Setup VBR here, otherwise buserror remap will not work.
+        * if dBug was active before (on my SBC with dBug 1.1 of Dec 16 1996)
+        *
+        * bkr@cut.de 19990306
+        *
+        * Note: this is because dBUG points VBR to ROM, making vectors read
+        * only, so the bus trap can't be changed. (RS)
+        */
+       move.l  #VBR_BASE, %a7                  /* Note VBR can't be read */
+       movec   %a7, %VBR
+       move.l  %a7, _ramvec                    /* Set up vector addr */
+       move.l  %a7, _rambase                   /* Set up base RAM addr */
+
+
+       /*
+        *      Set memory size.
+        */
+       move.l  #MEM_SIZE, %a0
+
+       move.l  %a0, %d0                        /* Mem end addr is in a0 */
+       move.l  %d0, %sp                        /* Set up initial stack ptr */
+       move.l  %d0, _ramend                    /* Set end ram addr */
+
+       /*
+        *      Enable CPU internal cache.
+        *
+        *      Cache is totally broken in first 5282 silicon.
+        *      No point enabling it for now.
+        */
+#if 0
+       move.l  #0x01000000, %d0
+       movec   %d0, %CACR                      /* Invalidate cache */
+       nop
+
+       move.l  #0x0000c000, %d0                /* Set SDRAM cached only */
+       movec   %d0, %ACR0
+       move.l  #0x00000000, %d0                /* No other regions cached */
+       movec   %d0, %ACR1
+
+       move.l  #0x00000000, %d0                /* Setup cache mask */
+       movec   %d0, %CACR                      /* Enable cache */
+       nop
+#endif
+
+
+#ifdef CONFIG_ROMFS_FS
+       /*
+        *      Move ROM filesystem above bss :-)
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Set up destination  */
+       move.l  %a0, %a2                        /* Copy of bss start */
+
+       move.l  8(%a0), %d0                     /* Get size of ROMFS */
+       addq.l  #8, %d0                         /* Allow for rounding */
+       and.l   #0xfffffffc, %d0                /* Whole words */
+
+       add.l   %d0, %a0                        /* Copy from end */
+       add.l   %d0, %a1                        /* Copy from end */
+       move.l  %a1, _ramstart                  /* Set start of ram */
+
+_copy_romfs:
+       move.l  -(%a0), %d0                     /* Copy dword */
+       move.l  %d0, -(%a1)
+       cmp.l   %a0, %a2                        /* Check if at end */
+       bne     _copy_romfs
+#else /* CONFIG_ROMFS_FS */
+       lea.l   _ebss, %a1
+       move.l  %a1, _ramstart
+#endif /* CONFIG_ROMFS_FS */
+
+
+       /*
+        *      Zero out the bss region.
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Get end of bss */
+       clr.l   %d0                             /* Set value */
+_clear_bss:
+       move.l  %d0, (%a0)+                     /* Clear each word */
+       cmp.l   %a0, %a1                        /* Check if at end */
+       bne     _clear_bss
+
+       /*
+        *      Load the current thread pointer and stack.
+        */
+       lea     init_thread_union, %a0
+       lea     0x2000(%a0), %sp
+
+       /*
+        *      Assember start up done, start code proper.
+        */
+       jsr     start_kernel                    /* Start Linux kernel */
+
+_exit:
+       jmp     _exit                           /* Should never get here */
+
+/*****************************************************************************/
diff --git a/arch/m68knommu/platform/528x/Makefile b/arch/m68knommu/platform/528x/Makefile
new file mode 100644 (file)
index 0000000..e49335f
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# Makefile for the linux kernel.
+#
+
+#
+# If you want to play with the HW breakpoints then you will
+# need to add define this,  which will give you a stack backtrace
+# on the console port whenever a DBG interrupt occurs.  You have to
+# set up you HW breakpoints to trigger a DBG interrupt:
+#
+# EXTRA_CFLAGS += -DTRAP_DBG_INTERRUPT
+# EXTRA_AFLAGS += -DTRAP_DBG_INTERRUPT
+#
+
+ifdef CONFIG_FULLDEBUG
+AFLAGS += -DDEBUGGER_COMPATIBLE_CACHE=1
+endif
+
+obj-y := config.o
+
+extra-y := $(BOARD)/crt0_$(MODEL).o
diff --git a/arch/m68knommu/platform/528x/config.c b/arch/m68knommu/platform/528x/config.c
new file mode 100644 (file)
index 0000000..a5d2aa3
--- /dev/null
@@ -0,0 +1,82 @@
+/***************************************************************************/
+
+/*
+ *     linux/arch/m68knommu/platform/528x/config.c
+ *
+ *     Sub-architcture dependant initialization code for the Motorola
+ *     5280 and 5282 CPUs.
+ *
+ *     Copyright (C) 1999-2003, Greg Ungerer (gerg@snapgear.com)
+ *     Copyright (C) 2001-2003, SnapGear Inc. (www.snapgear.com)
+ */
+
+/***************************************************************************/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/param.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/dma.h>
+#include <asm/traps.h>
+#include <asm/machdep.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+#include <asm/mcfdma.h>
+
+/***************************************************************************/
+
+void coldfire_pit_tick(void);
+void coldfire_pit_init(irqreturn_t (*handler)(int, void *, struct pt_regs *));
+unsigned long coldfire_pit_offset(void);
+void coldfire_trap_init(void);
+void coldfire_reset(void);
+
+/***************************************************************************/
+
+/*
+ *     DMA channel base address table.
+ */
+unsigned int   dma_base_addr[MAX_M68K_DMA_CHANNELS] = {
+        MCF_MBAR + MCFDMA_BASE0,
+};
+
+unsigned int dma_device_address[MAX_M68K_DMA_CHANNELS];
+
+/***************************************************************************/
+
+void mcf_disableall(void)
+{
+       *((volatile unsigned long *) (MCF_IPSBAR + MCFICM_INTC0 + MCFINTC_IMRH)) = 0xffffffff;
+       *((volatile unsigned long *) (MCF_IPSBAR + MCFICM_INTC0 + MCFINTC_IMRL)) = 0xffffffff;
+}
+
+/***************************************************************************/
+
+void mcf_autovector(unsigned int vec)
+{
+       /* Everything is auto-vectored on the 5272 */
+}
+
+/***************************************************************************/
+
+void config_BSP(char *commandp, int size)
+{
+       mcf_disableall();
+
+#ifdef CONFIG_BOOTPARAM
+       strncpy(commandp, CONFIG_BOOTPARAM_STRING, size);
+       commandp[size-1] = 0;
+#else
+       memset(commandp, 0, size);
+#endif
+
+       mach_sched_init = coldfire_pit_init;
+       mach_tick = coldfire_pit_tick;
+       mach_gettimeoffset = coldfire_pit_offset;
+       mach_trap_init = coldfire_trap_init;
+       mach_reset = coldfire_reset;
+}
+
+/***************************************************************************/
diff --git a/arch/m68knommu/platform/528x/senTec/crt0_ram.S b/arch/m68knommu/platform/528x/senTec/crt0_ram.S
new file mode 100644 (file)
index 0000000..c2f3bbd
--- /dev/null
@@ -0,0 +1,180 @@
+/*****************************************************************************/
+
+/*
+ *     crt0_ram.S -- startup code for senTec COBRA5282 ColdFire based boards.
+ *
+ *     (C) Copyright 2003, Greg Ungerer (gerg@snapgear.com).
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/linkage.h>
+#include <asm/segment.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+/*****************************************************************************/
+
+/*
+ *     senTec COBRA5282 board, chip select and memory setup.
+ */
+
+#define        MEM_BASE        0x00000000      /* Memory base at address 0 */
+#define        VBR_BASE        MEM_BASE        /* Vector address */
+
+#if defined(CONFIG_RAM16MB)
+#define        MEM_SIZE        0x01000000      /* Memory size 16Mb */
+#elif defined(CONFIG_RAM8MB)
+#define        MEM_SIZE        0x00800000      /* Memory size 8Mb */
+#else
+#define        MEM_SIZE        0x00400000      /* Memory size 4Mb */
+#endif
+
+#define IPSBAR 0x40000000
+#define GPACR0 0x30
+/*****************************************************************************/
+
+.global        _start
+.global        _rambase
+.global        _ramvec
+.global        _ramstart
+.global        _ramend
+
+/*****************************************************************************/
+
+.data
+
+/*
+ *     Set up the usable of RAM stuff. Size of RAM is determined then
+ *     an initial stack set up at the end.
+ */
+_rambase:
+.long  0
+_ramvec:
+.long  0
+_ramstart:
+.long  0
+_ramend:
+.long  0
+
+/*****************************************************************************/
+
+.text
+
+/*
+ *     This is the codes first entry point. This is where it all
+ *     begins...
+ */
+
+_start:
+       nop                                                             /* Filler */
+       move.w  #0x2700, %sr                    /* No interrupts */
+
+       /*
+        * Setup VBR here, otherwise buserror remap will not work.
+        * if dBug was active before (on my SBC with dBug 1.1 of Dec 16 1996)
+        *
+        * bkr@cut.de 19990306
+        *
+        * Note: this is because dBUG points VBR to ROM, making vectors read
+        * only, so the bus trap can't be changed. (RS)
+        */
+       move.l  #VBR_BASE, %a7                  /* Note VBR can't be read */
+       movec   %a7, %VBR
+       move.l  %a7, _ramvec                    /* Set up vector addr */
+       move.l  %a7, _rambase                   /* Set up base RAM addr */
+
+
+       /*
+        *      Set memory size.
+        */
+       move.l  #MEM_SIZE, %a0
+
+       move.l  %a0, %d0                        /* Mem end addr is in a0 */
+       move.l  %d0, %sp                        /* Set up initial stack ptr */
+       move.l  %d0, _ramend            /* Set end ram addr */
+
+       /*
+        *      Enable CPU internal cache.
+        *
+        *      Cache is totally broken in first 5282 silicon.
+        *      No point enabling it for now.
+        */
+#if 0
+       move.l  #0x01000000, %d0
+       movec   %d0, %CACR                              /* Invalidate cache */
+       nop
+
+       move.l  #0x0000c000, %d0                /* Set SDRAM cached only */
+       movec   %d0, %ACR0
+       move.l  #0x00000000, %d0                /* No other regions cached */
+       movec   %d0, %ACR1
+
+       move.l  #0x00000000, %d0                /* Setup cache mask */
+       movec   %d0, %CACR                              /* Enable cache */
+       nop
+#endif
+
+
+#ifdef CONFIG_ROMFS_FS
+       /*
+        *      Move ROM filesystem above bss :-)
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Set up destination  */
+       move.l  %a0, %a2                        /* Copy of bss start */
+
+       move.l  8(%a0), %d0                     /* Get size of ROMFS */
+       addq.l  #8, %d0                         /* Allow for rounding */
+       and.l   #0xfffffffc, %d0        /* Whole words */
+
+       add.l   %d0, %a0                        /* Copy from end */
+       add.l   %d0, %a1                        /* Copy from end */
+       move.l  %a1, _ramstart          /* Set start of ram */
+
+_copy_romfs:
+       move.l  -(%a0), %d0                     /* Copy dword */
+       move.l  %d0, -(%a1)
+       cmp.l   %a0, %a2                        /* Check if at end */
+       bne     _copy_romfs
+#else /* CONFIG_ROMFS_FS */
+       lea.l   _ebss, %a1
+       move.l  %a1, _ramstart
+#endif /* CONFIG_ROMFS_FS */
+
+
+       /*
+        *      Zero out the bss region.
+        */
+       lea.l   _sbss, %a0                      /* Get start of bss */
+       lea.l   _ebss, %a1                      /* Get end of bss */
+       clr.l   %d0                                     /* Set value */
+_clear_bss:
+       move.l  %d0, (%a0)+                     /* Clear each word */
+       cmp.l   %a0, %a1                        /* Check if at end */
+       bne     _clear_bss
+
+       /*
+        *      Load the current thread pointer and stack.
+        */
+       lea     init_thread_union, %a0
+       lea     0x2000(%a0), %sp
+
+   /*
+    * User mode port access
+    */
+   move.l   #0x0000000c, %d0
+   move.b   %d0, (IPSBAR+GPACR0)
+
+
+       /*
+        *      Assember start up done, start code proper.
+        */
+       jsr     start_kernel                    /* Start Linux kernel */
+
+_exit:
+       jmp     _exit                           /* Should never get here */
+
+/*****************************************************************************/
diff --git a/arch/m68knommu/platform/5307/head.S b/arch/m68knommu/platform/5307/head.S
new file mode 100644 (file)
index 0000000..f3a0bdf
--- /dev/null
@@ -0,0 +1,253 @@
+/*****************************************************************************/
+
+/*
+ *     head.S -- common startup code for ColdFire CPUs.
+ *
+ *     (C) Copyright 1999-2004, Greg Ungerer (gerg@snapgear.com).
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/thread_info.h>
+#include <asm/coldfire.h>
+#include <asm/mcfcache.h>
+#include <asm/mcfsim.h>
+
+/*****************************************************************************/
+
+/*
+ *     Define fixed memory sizes. Configuration of a fixed memory size
+ *     overrides everything else. If the user defined a size we just
+ *     blindly use it (they know what they are doing right :-)
+ */
+#if defined(CONFIG_RAM32MB)
+#define MEM_SIZE       0x02000000      /* memory size 32Mb */
+#elif defined(CONFIG_RAM16MB)
+#define MEM_SIZE       0x01000000      /* memory size 16Mb */
+#elif defined(CONFIG_RAM8MB)
+#define MEM_SIZE       0x00800000      /* memory size 8Mb */
+#elif defined(CONFIG_RAM4MB)
+#define MEM_SIZE       0x00400000      /* memory size 4Mb */
+#elif defined(CONFIG_RAM1MB)
+#define MEM_SIZE       0x00100000      /* memory size 1Mb */
+#endif
+
+/*
+ *     Memory size exceptions for special cases. Some boards may be set
+ *     for auto memory sizing, but we can't do it that way for some reason.
+ *     For example the 5206eLITE board has static RAM, and auto-detecting
+ *     the SDRAM will do you no good at all.
+ */
+#ifdef CONFIG_RAMAUTO
+#if defined(CONFIG_M5206eLITE)
+#define        MEM_SIZE        0x00100000              /* 1MiB default memory */
+#endif
+#endif /* CONFIG_RAMAUTO */
+
+/*
+ *     If we don't have a fixed memory size now, then lets build in code
+ *     to auto detect the DRAM size. Obviously this is the prefered
+ *     method, and should work for most boards (it won't work for those
+ *     that do not have their RAM starting at address 0).
+ */
+#if defined(MEM_SIZE)
+.macro GET_MEM_SIZE
+       movel   #MEM_SIZE,%d0           /* hard coded memory size */
+.endm
+
+#elif defined(CONFIG_M5206) || defined(CONFIG_M5206e) || \
+      defined(CONFIG_M5249) || defined(CONFIG_M527x) || \
+      defined(CONFIG_M528x) || defined(CONFIG_M5307) || \
+      defined(CONFIG_M5407)
+/*
+ *     Not all these devices have exactly the same DRAM controller,
+ *     but the DCMR register is virtually identical - give or take
+ *     a couple of bits. The only exception is the 5272 devices, their
+ *     DRAM controller is quite different.
+ */
+.macro GET_MEM_SIZE
+       movel   MCF_MBAR+MCFSIM_DMR0,%d0 /* get mask for 1st bank */
+       btst    #0,%d0                  /* check if region enabled */
+       beq     1f
+       andl    #0xfffc0000,%d0
+       beq     1f
+       addl    #0x00040000,%d0         /* convert mask to size */
+1:
+       movel   MCF_MBAR+MCFSIM_DMR1,%d1 /* get mask for 2nd bank */
+       btst    #0,%d1                  /* check if region enabled */
+       beq     2f
+       andl    #0xfffc0000, %d1
+       beq     2f
+       addl    #0x00040000,%d1
+       addl    %d1,%d0                 /* total mem size in d0 */
+2:
+.endm
+
+#elif defined(CONFIG_M5272)
+.macro GET_MEMORY_SIZE
+       movel   MCF_MBAR+MCFSIM_CSOR7,%d0 /* get SDRAM address mask */
+       andil   #0xfffff000,%d0         /* mask out chip select options */
+       negl    %d0                     /* negate bits */
+.endm
+
+#else
+#error "ERROR: I don't know how to determine your boards memory size?"
+#endif
+
+
+/*
+ *     Most ColdFire boards have their DRAM starting at address 0.
+ *     Notable exception is the 5206eLITE board.
+ */
+#if defined(CONFIG_M5206eLITE)
+#define        MEM_BASE        0x30000000
+#endif
+
+#ifndef MEM_BASE
+#define        MEM_BASE        0x00000000      /* memory base at address 0 */
+#endif
+
+/*
+ *     The default location for the vectors is at the base of RAM.
+ *     Some boards might like to use internal SRAM or something like
+ *     that. If no board specific header defines an alternative then
+ *     use the base of RAM.
+ */
+#ifndef        VBR_BASE
+#define        VBR_BASE        MEM_BASE        /* vector address */
+#endif
+
+/*****************************************************************************/
+
+/*
+ *     Boards and platforms can do specific early hardware setup if
+ *     they need to. Most don't need this, define away if not required.
+ */
+#ifndef PLATFORM_SETUP
+#define        PLATFORM_SETUP
+#endif
+
+/*****************************************************************************/
+
+.global        _start
+.global _rambase
+.global _ramvec
+.global        _ramstart
+.global        _ramend
+
+/*****************************************************************************/
+
+.data
+
+/*
+ *     During startup we store away the RAM setup. These are not in the
+ *     bss, since their values are determined and written before the bss
+ *     has been cleared.
+ */
+_rambase:
+.long  0
+_ramvec:
+.long  0
+_ramstart:
+.long  0
+_ramend:
+.long  0
+
+/*****************************************************************************/
+
+.text
+
+/*
+ *     This is the codes first entry point. This is where it all
+ *     begins...
+ */
+
+_start:
+       nop                                     /* filler */
+       movew   #0x2700, %sr                    /* no interrupts */
+
+       /*
+        *      Do any platform or board specific setup now. Most boards
+        *      don't need anything. Those exceptions are define this in
+        *      their board specific includes.
+        */
+       PLATFORM_SETUP
+
+       /*
+        *      Create basic memory configuration. Set VBR accordingly,
+        *      and size memory.
+        */
+       movel   #VBR_BASE,%a7
+       movec   %a7,%VBR                        /* set vectors addr */
+       movel   %a7,_ramvec
+
+       movel   #MEM_BASE,%a7                   /* mark the base of RAM */
+       movel   %a7,_rambase
+
+       GET_MEM_SIZE                            /* macro code determines size */
+       movel   %d0,_ramend                     /* set end ram addr */
+
+       /*
+        *      Now that we know what the memory is, lets enable cache
+        *      and get things moving. This is Coldfire CPU specific.
+        */
+       CACHE_ENABLE                            /* enable CPU cache */
+
+
+#ifdef CONFIG_ROMFS_FS
+       /*
+        *      Move ROM filesystem above bss :-)
+        */
+       lea     _sbss,%a0                       /* get start of bss */
+       lea     _ebss,%a1                       /* set up destination  */
+       movel   %a0,%a2                         /* copy of bss start */
+
+       movel   8(%a0),%d0                      /* get size of ROMFS */
+       addql   #8,%d0                          /* allow for rounding */
+       andl    #0xfffffffc, %d0                /* whole words */
+
+       addl    %d0,%a0                         /* copy from end */
+       addl    %d0,%a1                         /* copy from end */
+       movel   %a1,_ramstart                   /* set start of ram */
+
+_copy_romfs:
+       movel   -(%a0),%d0                      /* copy dword */
+       movel   %d0,-(%a1)
+       cmpl    %a0,%a2                         /* check if at end */
+       bne     _copy_romfs
+
+#else /* CONFIG_ROMFS_FS */
+       lea     _ebss,%a1
+       movel   %a1,_ramstart
+#endif /* CONFIG_ROMFS_FS */
+
+
+       /*
+        *      Zero out the bss region.
+        */
+       lea     _sbss,%a0                       /* get start of bss */
+       lea     _ebss,%a1                       /* get end of bss */
+       clrl    %d0                             /* set value */
+_clear_bss:
+       movel   %d0,(%a0)+                      /* clear each word */
+       cmpl    %a0,%a1                         /* check if at end */
+       bne     _clear_bss
+
+       /*
+        *      Load the current task pointer and stack.
+        */
+       lea     init_thread_union,%a0
+       lea     THREAD_SIZE(%a0),%sp
+
+       /*
+        *      Assember start up done, start code proper.
+        */
+       jsr     start_kernel                    /* start Linux kernel */
+
+_exit:
+       jmp     _exit                           /* should never get here */
+
+/*****************************************************************************/
diff --git a/arch/m68knommu/platform/5307/pit.c b/arch/m68knommu/platform/5307/pit.c
new file mode 100644 (file)
index 0000000..a9b2c2e
--- /dev/null
@@ -0,0 +1,88 @@
+/***************************************************************************/
+
+/*
+ *     pit.c -- Motorola ColdFire PIT timer. Currently this type of
+ *              hardware timer only exists in the Motorola ColdFire
+ *              5270/5271 and 5282 CPUs.
+ *
+ *     Copyright (C) 1999-2004, Greg Ungerer (gerg@snapgear.com)
+ *     Copyright (C) 2001-2004, SnapGear Inc. (www.snapgear.com)
+ *
+ */
+
+/***************************************************************************/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/param.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/coldfire.h>
+#include <asm/mcfpit.h>
+#include <asm/mcfsim.h>
+
+/***************************************************************************/
+
+void coldfire_pit_tick(void)
+{
+       volatile struct mcfpit *tp;
+
+       /* Reset the ColdFire timer */
+       tp = (volatile struct mcfpit *) (MCF_IPSBAR + MCFPIT_BASE1);
+       tp->pcsr |= MCFPIT_PCSR_PIF;
+}
+
+/***************************************************************************/
+
+void coldfire_pit_init(irqreturn_t (*handler)(int, void *, struct pt_regs *))
+{
+       volatile unsigned char *icrp;
+       volatile unsigned long *imrp;
+       volatile struct mcfpit *tp;
+
+       request_irq(MCFINT_VECBASE + MCFINT_PIT1, handler, SA_INTERRUPT,
+               "ColdFire Timer", NULL);
+
+       icrp = (volatile unsigned char *) (MCF_IPSBAR + MCFICM_INTC0 +
+               MCFINTC_ICR0 + MCFINT_PIT1);
+       *icrp = 0x2b; /* PIT1 with level 5, priority 3 */
+
+       imrp = (volatile unsigned long *) (MCF_IPSBAR + MCFICM_INTC0 + MCFINTC_IMRH);
+       *imrp &= ~(1 << (MCFINT_PIT1 - 32));
+
+       /* Set up PIT timer 1 as poll clock */
+       tp = (volatile struct mcfpit *) (MCF_IPSBAR + MCFPIT_BASE1);
+       tp->pcsr = MCFPIT_PCSR_DISABLE;
+
+       tp->pmr = ((MCF_CLK / 2) / 64) / HZ;
+       tp->pcsr = MCFPIT_PCSR_EN | MCFPIT_PCSR_PIE | MCFPIT_PCSR_OVW |
+               MCFPIT_PCSR_RLD | MCFPIT_PCSR_CLK64;
+}
+
+/***************************************************************************/
+
+unsigned long coldfire_pit_offset(void)
+{
+       volatile struct mcfpit *tp;
+       volatile unsigned long *ipr;
+       unsigned long pmr, pcntr, offset;
+
+       tp = (volatile struct mcfpit *) (MCF_IPSBAR + MCFPIT_BASE1);
+       ipr = (volatile unsigned long *) (MCF_IPSBAR + MCFICM_INTC0 + MCFINTC_IPRH);
+
+       pmr = *(&tp->pmr);
+       pcntr = *(&tp->pcntr);
+
+       /*
+        * If we are still in the first half of the upcount and a
+        * timer interupt is pending, then add on a ticks worth of time.
+        */
+       offset = ((pmr - pcntr) * (1000000 / HZ)) / pmr;
+       if ((offset < (1000000 / HZ / 2)) && (*ipr & (1 << (MCFINT_PIT1 - 32))))
+               offset += 1000000 / HZ;
+       return offset;  
+}
+
+/***************************************************************************/
diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c
new file mode 100644 (file)
index 0000000..22acce0
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Platform device support for Au1x00 SoCs.
+ *
+ * Copyright 2004, Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 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 <linux/config.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/resource.h>
+
+#include <asm/mach-au1x00/au1000.h>
+
+static struct resource au1xxx_usb_ohci_resources[] = {
+       [0] = {
+               .start          = USB_OHCI_BASE,
+               .end            = USB_OHCI_BASE + USB_OHCI_LEN,
+               .flags          = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start          = AU1000_USB_HOST_INT,
+               .end            = AU1000_USB_HOST_INT,
+               .flags          = IORESOURCE_IRQ,
+       },
+};
+
+/* The dmamask must be set for OHCI to work */
+static u64 ohci_dmamask = ~(u32)0;
+
+static struct platform_device au1xxx_usb_ohci_device = {
+       .name           = "au1xxx-ohci",
+       .id             = 0,
+       .dev = {
+               .dma_mask               = &ohci_dmamask,
+               .coherent_dma_mask      = 0xffffffff,
+       },
+       .num_resources  = ARRAY_SIZE(au1xxx_usb_ohci_resources),
+       .resource       = au1xxx_usb_ohci_resources,
+};
+
+static struct platform_device *au1xxx_platform_devices[] __initdata = {
+       &au1xxx_usb_ohci_device,
+};
+
+int au1xxx_platform_init(void)
+{
+       return platform_add_devices(au1xxx_platform_devices, ARRAY_SIZE(au1xxx_platform_devices));
+}
+
+arch_initcall(au1xxx_platform_init);
diff --git a/arch/mips/configs/db1550_defconfig b/arch/mips/configs/db1550_defconfig
new file mode 100644 (file)
index 0000000..73d45f7
--- /dev/null
@@ -0,0 +1,906 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc2
+# Sun Nov 21 14:11:57 2004
+#
+CONFIG_MIPS=y
+# CONFIG_MIPS64 is not set
+# CONFIG_64BIT is not set
+CONFIG_MIPS32=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_KMOD=y
+
+#
+# Machine selection
+#
+# CONFIG_MACH_JAZZ is not set
+# CONFIG_MACH_VR41XX is not set
+# CONFIG_TOSHIBA_JMR3927 is not set
+# CONFIG_MIPS_COBALT is not set
+# CONFIG_MACH_DECSTATION is not set
+# CONFIG_MIPS_EV64120 is not set
+# CONFIG_MIPS_EV96100 is not set
+# CONFIG_MIPS_IVR is not set
+# CONFIG_LASAT is not set
+# CONFIG_MIPS_ITE8172 is not set
+# CONFIG_MIPS_ATLAS is not set
+# CONFIG_MIPS_MALTA is not set
+# CONFIG_MIPS_SEAD is not set
+# CONFIG_MOMENCO_OCELOT is not set
+# CONFIG_MOMENCO_OCELOT_G is not set
+# CONFIG_MOMENCO_OCELOT_C is not set
+# CONFIG_MOMENCO_OCELOT_3 is not set
+# CONFIG_MOMENCO_JAGUAR_ATX is not set
+# CONFIG_PMC_YOSEMITE is not set
+# CONFIG_DDB5074 is not set
+# CONFIG_DDB5476 is not set
+# CONFIG_DDB5477 is not set
+# CONFIG_NEC_OSPREY is not set
+# CONFIG_SGI_IP22 is not set
+CONFIG_SOC_AU1X00=y
+# CONFIG_SOC_AU1000 is not set
+# CONFIG_SOC_AU1100 is not set
+# CONFIG_SOC_AU1500 is not set
+CONFIG_SOC_AU1550=y
+# CONFIG_MIPS_PB1000 is not set
+# CONFIG_MIPS_PB1100 is not set
+# CONFIG_MIPS_PB1500 is not set
+# CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_DB1000 is not set
+# CONFIG_MIPS_DB1100 is not set
+# CONFIG_MIPS_DB1500 is not set
+CONFIG_MIPS_DB1550=y
+# CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_MIRAGE is not set
+# CONFIG_MIPS_XXS1500 is not set
+# CONFIG_MIPS_MTX1 is not set
+# CONFIG_SIBYTE_SB1xxx_SOC is not set
+# CONFIG_SNI_RM200_PCI is not set
+# CONFIG_TOSHIBA_RBTX4927 is not set
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_DMA_COHERENT=y
+CONFIG_MIPS_DISABLE_OBSOLETE_IDE=y
+CONFIG_CPU_LITTLE_ENDIAN=y
+CONFIG_MIPS_L1_CACHE_SHIFT=5
+# CONFIG_FB is not set
+
+#
+# CPU selection
+#
+CONFIG_CPU_MIPS32=y
+# CONFIG_CPU_MIPS64 is not set
+# CONFIG_CPU_R3000 is not set
+# CONFIG_CPU_TX39XX is not set
+# CONFIG_CPU_VR41XX is not set
+# CONFIG_CPU_R4300 is not set
+# CONFIG_CPU_R4X00 is not set
+# CONFIG_CPU_TX49XX is not set
+# CONFIG_CPU_R5000 is not set
+# CONFIG_CPU_R5432 is not set
+# CONFIG_CPU_R6000 is not set
+# CONFIG_CPU_NEVADA is not set
+# CONFIG_CPU_R8000 is not set
+# CONFIG_CPU_R10000 is not set
+# CONFIG_CPU_RM7000 is not set
+# CONFIG_CPU_RM9000 is not set
+# CONFIG_CPU_SB1 is not set
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_CPU_HAS_PREFETCH=y
+# CONFIG_VTAG_ICACHE is not set
+CONFIG_64BIT_PHYS_ADDR=y
+# CONFIG_CPU_ADVANCED is not set
+CONFIG_CPU_HAS_LLSC=y
+CONFIG_CPU_HAS_SYNC=y
+# CONFIG_PREEMPT is not set
+
+#
+# Bus options (PCI, PCMCIA, EISA, ISA, TC)
+#
+CONFIG_HW_HAS_PCI=y
+CONFIG_PCI=y
+CONFIG_PCI_LEGACY_PROC=y
+CONFIG_PCI_NAMES=y
+CONFIG_MMU=y
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+CONFIG_PCCARD=m
+# CONFIG_PCMCIA_DEBUG is not set
+# CONFIG_PCMCIA_OBSOLETE is not set
+CONFIG_PCMCIA=m
+CONFIG_CARDBUS=y
+
+#
+# PC-card bridges
+#
+# CONFIG_YENTA is not set
+# CONFIG_PD6729 is not set
+# CONFIG_I82092 is not set
+# CONFIG_TCIC is not set
+CONFIG_PCMCIA_AU1X00=m
+
+#
+# PCI Hotplug Support
+#
+# CONFIG_HOTPLUG_PCI is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+CONFIG_TRAD_SIGNALS=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER 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 is not set
+# CONFIG_MTD_CMDLINE_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_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_AMDSTD_RETRY=0
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_DB1550=y
+CONFIG_MTD_DB1550_BOOT=y
+CONFIG_MTD_DB1550_USER=y
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM 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
+CONFIG_MTD_NAND_AU1550=m
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# 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_SX8 is not set
+# CONFIG_BLK_DEV_RAM is not set
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_LBD is not set
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# 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_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECS=m
+# 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
+
+#
+# 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=y
+# CONFIG_BLK_DEV_OPTI621 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_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# 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 is not set
+# 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 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_IDE_ARM 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
+
+#
+# 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
+#
+# CONFIG_I2O is not set
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# 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_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_INET_TUNNEL=m
+CONFIG_IP_TCPDIAG=m
+# CONFIG_IP_TCPDIAG_IPV6 is not set
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+# CONFIG_IPV6 is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+# CONFIG_IP_NF_CONNTRACK is not set
+CONFIG_IP_NF_CONNTRACK_MARK=y
+# CONFIG_IP_NF_QUEUE is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
+# CONFIG_IP_NF_COMPAT_IPFWADM is not set
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+
+#
+# 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+# CONFIG_ETHERTAP is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+CONFIG_MIPS_AU1X00_ENET=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 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 is not set
+
+#
+# PCMCIA network device support
+#
+CONFIG_NET_PCMCIA=y
+CONFIG_PCMCIA_3C589=m
+CONFIG_PCMCIA_3C574=m
+CONFIG_PCMCIA_FMVJ18X=m
+CONFIG_PCMCIA_PCNET=m
+CONFIG_PCMCIA_NMCLAN=m
+CONFIG_PCMCIA_SMC91C92=m
+CONFIG_PCMCIA_XIRC2PS=m
+CONFIG_PCMCIA_AXNET=m
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+CONFIG_PPP=m
+CONFIG_PPP_MULTILINK=y
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+# CONFIG_PPP_BSDCOMP is not set
+CONFIG_PPPOE=m
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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=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=y
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PCIPS2 is not set
+CONFIG_SERIO_RAW=m
+
+#
+# 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
+# CONFIG_AU1X00_GPIO is not set
+# CONFIG_TS_AU1X00_ADS7846 is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_AU1X00=y
+CONFIG_SERIAL_AU1X00_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG 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_AGP is not set
+# CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+CONFIG_SYNCLINK_CS=m
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET 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=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+CONFIG_REISERFS_FS=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+CONFIG_REISERFS_FS_XATTR=y
+CONFIG_REISERFS_FS_POSIX_ACL=y
+CONFIG_REISERFS_FS_SECURITY=y
+# 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_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_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=y
+CONFIG_DEVPTS_FS_SECURITY=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR 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=m
+# 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=m
+# CONFIG_NFSD_V3 is not set
+# CONFIG_NFSD_TCP is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT 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 is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=m
+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_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# 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
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+CONFIG_CROSSCOMPILE=y
+CONFIG_CMDLINE=""
+
+#
+# Security options
+#
+CONFIG_KEYS=y
+CONFIG_KEYS_DEBUG_PROC_KEYS=y
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_NULL=y
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+CONFIG_CRYPTO_SHA512=y
+CONFIG_CRYPTO_WP512=m
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_SERPENT is not set
+CONFIG_CRYPTO_AES=m
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_TEA=m
+# CONFIG_CRYPTO_ARC4 is not set
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_MICHAEL_MIC=y
+CONFIG_CRYPTO_CRC32C=m
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
diff --git a/arch/mips/configs/ocelot_3_defconfig b/arch/mips/configs/ocelot_3_defconfig
new file mode 100644 (file)
index 0000000..37fbacb
--- /dev/null
@@ -0,0 +1,846 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc2
+# Sun Nov 21 14:12:03 2004
+#
+CONFIG_MIPS=y
+# CONFIG_MIPS64 is not set
+# CONFIG_64BIT is not set
+CONFIG_MIPS32=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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_KOBJECT_UEVENT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Machine selection
+#
+# CONFIG_MACH_JAZZ is not set
+# CONFIG_MACH_VR41XX is not set
+# CONFIG_TOSHIBA_JMR3927 is not set
+# CONFIG_MIPS_COBALT is not set
+# CONFIG_MACH_DECSTATION is not set
+# CONFIG_MIPS_EV64120 is not set
+# CONFIG_MIPS_EV96100 is not set
+# CONFIG_MIPS_IVR is not set
+# CONFIG_LASAT is not set
+# CONFIG_MIPS_ITE8172 is not set
+# CONFIG_MIPS_ATLAS is not set
+# CONFIG_MIPS_MALTA is not set
+# CONFIG_MIPS_SEAD is not set
+# CONFIG_MOMENCO_OCELOT is not set
+# CONFIG_MOMENCO_OCELOT_G is not set
+# CONFIG_MOMENCO_OCELOT_C is not set
+CONFIG_MOMENCO_OCELOT_3=y
+# CONFIG_MOMENCO_JAGUAR_ATX is not set
+# CONFIG_PMC_YOSEMITE is not set
+# CONFIG_DDB5074 is not set
+# CONFIG_DDB5476 is not set
+# CONFIG_DDB5477 is not set
+# CONFIG_NEC_OSPREY is not set
+# CONFIG_SGI_IP22 is not set
+# CONFIG_SOC_AU1X00 is not set
+# CONFIG_SIBYTE_SB1xxx_SOC is not set
+# CONFIG_SNI_RM200_PCI is not set
+# CONFIG_TOSHIBA_RBTX4927 is not set
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_DMA_NONCOHERENT=y
+# CONFIG_CPU_LITTLE_ENDIAN is not set
+CONFIG_IRQ_CPU=y
+CONFIG_IRQ_CPU_RM7K=y
+CONFIG_IRQ_MV64340=y
+CONFIG_PCI_MARVELL=y
+CONFIG_SWAP_IO_SPACE=y
+CONFIG_BOOT_ELF32=y
+CONFIG_MIPS_L1_CACHE_SHIFT=5
+CONFIG_FB=y
+
+#
+# CPU selection
+#
+# CONFIG_CPU_MIPS32 is not set
+# CONFIG_CPU_MIPS64 is not set
+# CONFIG_CPU_R3000 is not set
+# CONFIG_CPU_TX39XX is not set
+# CONFIG_CPU_VR41XX is not set
+# CONFIG_CPU_R4300 is not set
+# CONFIG_CPU_R4X00 is not set
+# CONFIG_CPU_TX49XX is not set
+# CONFIG_CPU_R5000 is not set
+# CONFIG_CPU_R5432 is not set
+# CONFIG_CPU_R6000 is not set
+# CONFIG_CPU_NEVADA is not set
+# CONFIG_CPU_R8000 is not set
+# CONFIG_CPU_R10000 is not set
+# CONFIG_CPU_RM7000 is not set
+CONFIG_CPU_RM9000=y
+# CONFIG_CPU_SB1 is not set
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_BOARD_SCACHE=y
+CONFIG_RM7000_CPU_SCACHE=y
+CONFIG_CPU_HAS_PREFETCH=y
+# CONFIG_64BIT_PHYS_ADDR is not set
+# CONFIG_CPU_ADVANCED is not set
+CONFIG_CPU_HAS_LLSC=y
+CONFIG_CPU_HAS_LLDSCD=y
+CONFIG_CPU_HAS_SYNC=y
+# CONFIG_HIGHMEM is not set
+# CONFIG_SMP is not set
+# CONFIG_PREEMPT is not set
+
+#
+# Bus options (PCI, PCMCIA, EISA, ISA, TC)
+#
+CONFIG_HW_HAS_PCI=y
+CONFIG_PCI=y
+CONFIG_PCI_LEGACY_PROC=y
+CONFIG_PCI_NAMES=y
+CONFIG_MMU=y
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+CONFIG_TRAD_SIGNALS=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+
+#
+# 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_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_SX8 is not set
+# CONFIG_BLK_DEV_RAM is not set
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_LBD is not set
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI=m
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+# CONFIG_BLK_DEV_SD is not set
+# 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 is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLOGIC_1280_1040 is not set
+CONFIG_SCSI_QLA2XXX=m
+# CONFIG_SCSI_QLA21XX is not set
+# CONFIG_SCSI_QLA22XX is not set
+# CONFIG_SCSI_QLA2300 is not set
+# CONFIG_SCSI_QLA2322 is not set
+# CONFIG_SCSI_QLA6312 is not set
+# CONFIG_SCSI_QLA6322 is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD 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
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK_DEV=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_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_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=m
+CONFIG_IP_TCPDIAG_IPV6=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_IPV6_TUNNEL is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+# CONFIG_IP_NF_CONNTRACK is not set
+# CONFIG_IP_NF_CONNTRACK_MARK is not set
+# CONFIG_IP_NF_QUEUE is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
+# CONFIG_IP_NF_COMPAT_IPFWADM is not set
+
+#
+# IPv6: Netfilter Configuration
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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=m
+# 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 is not set
+CONFIG_E100=y
+# CONFIG_E100_NAPI 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
+# CONFIG_LAN_SAA9730 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_VIA_VELOCITY is not set
+# CONFIG_TIGON3 is not set
+CONFIG_MV643XX_ETH=y
+CONFIG_MV643XX_ETH_0=y
+CONFIG_MV643XX_ETH_1=y
+CONFIG_MV643XX_ETH_2=y
+
+#
+# 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+# CONFIG_PPP_BSDCOMP is not set
+CONFIG_PPPOE=m
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 is not set
+# 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 is not set
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PCIPS2 is not set
+# CONFIG_SERIO_RAW 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=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+# 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
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_RTC=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON_OLD is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_E1356 is not set
+# CONFIG_FB_VIRTUAL is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# 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=m
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+CONFIG_JBD=m
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+CONFIG_REISERFS_FS=m
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_REISERFS_FS_XATTR is not set
+# CONFIG_JFS_FS is not set
+CONFIG_XFS_FS=m
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_SECURITY is not set
+# CONFIG_XFS_POSIX_ACL is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+CONFIG_AUTOFS_FS=y
+CONFIG_AUTOFS4_FS=m
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_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=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS_XATTR=y
+CONFIG_DEVPTS_FS_SECURITY=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR 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=y
+CONFIG_CRAMFS=y
+# 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=y
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V4 is not set
+# CONFIG_NFSD_TCP is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT 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 is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=m
+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_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# 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
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+CONFIG_CROSSCOMPILE=y
+CONFIG_CMDLINE="ip=any root=nfs"
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=m
+CONFIG_CRC32=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
diff --git a/arch/mips/kernel/irix5sys.S b/arch/mips/kernel/irix5sys.S
new file mode 100644 (file)
index 0000000..eeef891
--- /dev/null
@@ -0,0 +1,1041 @@
+/*
+ * 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.
+ *
+ * 32-bit IRIX5 ABI system call table derived from original file 'irix5sys.h'
+ * created by David S. Miller.
+ *
+ * Copyright (C) 1996 - 2004 David S. Miller <dm@engr.sgi.com>
+ * Copyright (C) 2004 Steven J. Hill <sjhill@realitydiluted.com>
+ */
+#include <asm/asm.h>
+
+       /*
+        * Key:
+        *         V == Valid and should work as expected for most cases.
+        *        HV == Half Valid, some things will work, some likely will not
+        *        IV == InValid, certainly will not work at all yet
+        *        ?V == ?'ably Valid, I have not done enough looking into it
+        *        DC == Don't Care, a rats ass we couldn't give
+        */
+
+       .macro  irix5syscalltable
+
+       sys     sys_syscall     0       /* 1000  sysindir()            V*/
+       sys     sys_exit        1       /* 1001  exit()                V*/
+       sys     sys_fork        0       /* 1002  fork()                V*/
+       sys     sys_read        3       /* 1003  read()                V*/
+       sys     sys_write       3       /* 1004  write()               V*/
+       sys     sys_open        3       /* 1005  open()                V*/
+       sys     sys_close       1       /* 1006  close()               V*/
+       sys     irix_unimp      0       /* 1007  (XXX IRIX 4 wait)     V*/
+       sys     sys_creat       2       /* 1008  creat()               V*/
+       sys     sys_link        2       /* 1009  link()                V*/
+       sys     sys_unlink      1       /* 1010  unlink()              V*/
+       sys     irix_exec       0       /* 1011  exec()                V*/
+       sys     sys_chdir       1       /* 1012  chdir()               V*/
+       sys     irix_gtime      0       /* 1013  time()                V*/
+       sys     irix_unimp      0       /* 1014  (XXX IRIX 4 mknod)    V*/
+       sys     sys_chmod       2       /* 1015  chmod()               V*/
+       sys     sys_chown       3       /* 1016  chown()               V*/
+       sys     irix_brk        1       /* 1017  break()               V*/
+       sys     irix_unimp      0       /* 1018  (XXX IRIX 4 stat)     V*/
+       sys     sys_lseek       3       /* 1019  lseek()     XXX64bit HV*/
+       sys     irix_getpid     0       /* 1020  getpid()              V*/
+       sys     irix_mount      6       /* 1021  mount()              IV*/
+       sys     sys_umount      1       /* 1022  umount()              V*/
+       sys     sys_setuid      1       /* 1023  setuid()              V*/
+       sys     irix_getuid     0       /* 1024  getuid()              V*/
+       sys     irix_stime      1       /* 1025  stime()               V*/
+       sys     irix_unimp      4       /* 1026  XXX ptrace()         IV*/
+       sys     irix_alarm      1       /* 1027  alarm()               V*/
+       sys     irix_unimp      0       /* 1028  (XXX IRIX 4 fstat)    V*/
+       sys     irix_pause      0       /* 1029  pause()               V*/
+       sys     sys_utime       2       /* 1030  utime()               V*/
+       sys     irix_unimp      0       /* 1031  nuthin'               V*/
+       sys     irix_unimp      0       /* 1032  nobody home man...    V*/
+       sys     sys_access      2       /* 1033  access()              V*/
+       sys     sys_nice        1       /* 1034  nice()                V*/
+       sys     irix_statfs     2       /* 1035  statfs()              V*/
+       sys     sys_sync        0       /* 1036  sync()                V*/
+       sys     sys_kill        2       /* 1037  kill()                V*/
+       sys     irix_fstatfs    2       /* 1038  fstatfs()             V*/
+       sys     irix_setpgrp    1       /* 1039  setpgrp()             V*/
+       sys     irix_syssgi     0       /* 1040  syssgi()             HV*/
+       sys     sys_dup         1       /* 1041  dup()                 V*/
+       sys     sys_pipe        0       /* 1042  pipe()                V*/
+       sys     irix_times      1       /* 1043  times()               V*/
+       sys     irix_unimp      0       /* 1044  XXX profil()         IV*/
+       sys     irix_unimp      0       /* 1045  XXX lock()           IV*/
+       sys     sys_setgid      1       /* 1046  setgid()              V*/
+       sys     irix_getgid     0       /* 1047  getgid()              V*/
+       sys     irix_unimp      0       /* 1048  (XXX IRIX 4 ssig)     V*/
+       sys     irix_msgsys     6       /* 1049  sys_msgsys            V*/
+       sys     sys_sysmips     4       /* 1050  sysmips()            HV*/
+       sys     irix_unimp      0       /* 1051  XXX sysacct()        IV*/
+       sys     irix_shmsys     5       /* 1052  sys_shmsys            V*/
+       sys     irix_semsys     0       /* 1053  sys_semsys            V*/
+       sys     irix_ioctl      3       /* 1054  ioctl()              HV*/
+       sys     irix_uadmin     0       /* 1055  XXX sys_uadmin()     HC*/
+       sys     irix_sysmp      0       /* 1056  sysmp()              HV*/
+       sys     irix_utssys     4       /* 1057  sys_utssys()         HV*/
+       sys     irix_unimp      0       /* 1058  nada enchilada        V*/
+       sys     irix_exece      0       /* 1059  exece()               V*/
+       sys     sys_umask       1       /* 1060  umask()               V*/
+       sys     sys_chroot      1       /* 1061  chroot()              V*/
+       sys     irix_fcntl      3       /* 1062  fcntl()              ?V*/
+       sys     irix_ulimit     2       /* 1063  ulimit()             HV*/
+       sys     irix_unimp      0       /* 1064  XXX AFS shit         DC*/
+       sys     irix_unimp      0       /* 1065  XXX AFS shit         DC*/
+       sys     irix_unimp      0       /* 1066  XXX AFS shit         DC*/
+       sys     irix_unimp      0       /* 1067  XXX AFS shit         DC*/
+       sys     irix_unimp      0       /* 1068  XXX AFS shit         DC*/
+       sys     irix_unimp      0       /* 1069  XXX AFS shit         DC*/
+       sys     irix_unimp      0       /* 1070  XXX AFS shit         DC*/
+       sys     irix_unimp      0       /* 1071  XXX AFS shit         DC*/
+       sys     irix_unimp      0       /* 1072  XXX AFS shit         DC*/
+       sys     irix_unimp      0       /* 1073  XXX AFS shit         DC*/
+       sys     irix_unimp      0       /* 1074  nuttin'               V*/
+       sys     irix_unimp      0       /* 1075  XXX sys_getrlimit64()IV*/
+       sys     irix_unimp      0       /* 1076  XXX sys_setrlimit64()IV*/
+       sys     sys_nanosleep   2       /* 1077  nanosleep()           V*/
+       sys     irix_lseek64    5       /* 1078  lseek64()            ?V*/
+       sys     sys_rmdir       1       /* 1079  rmdir()               V*/
+       sys     sys_mkdir       2       /* 1080  mkdir()               V*/
+       sys     sys_getdents    3       /* 1081  getdents()            V*/
+       sys     irix_sginap     1       /* 1082  sys_sginap()          V*/
+       sys     irix_sgikopt    3       /* 1083  sys_sgikopt()        DC*/
+       sys     sys_sysfs       3       /* 1084  sysfs()              ?V*/
+       sys     irix_unimp      0       /* 1085  XXX sys_getmsg()     DC*/
+       sys     irix_unimp      0       /* 1086  XXX sys_putmsg()     DC*/
+       sys     sys_poll        3       /* 1087  poll()                V*/
+       sys     irix_sigreturn  0       /* 1088  sigreturn()          ?V*/
+       sys     sys_accept      3       /* 1089  accept()              V*/
+       sys     sys_bind        3       /* 1090  bind()                V*/
+       sys     sys_connect     3       /* 1091  connect()             V*/
+       sys     irix_gethostid  0       /* 1092  sys_gethostid()      ?V*/
+       sys     sys_getpeername 3       /* 1093  getpeername()         V*/
+       sys     sys_getsockname 3       /* 1094  getsockname()         V*/
+       sys     sys_getsockopt  5       /* 1095  getsockopt()          V*/
+       sys     sys_listen      2       /* 1096  listen()              V*/
+       sys     sys_recv        4       /* 1097  recv()                V*/
+       sys     sys_recvfrom    6       /* 1098  recvfrom()            V*/
+       sys     sys_recvmsg     3       /* 1099  recvmsg()             V*/
+       sys     sys_select      5       /* 1100  select()              V*/
+       sys     sys_send        4       /* 1101  send()                V*/
+       sys     sys_sendmsg     3       /* 1102  sendmsg()             V*/
+       sys     sys_sendto      6       /* 1103  sendto()              V*/
+       sys     irix_sethostid  1       /* 1104  sys_sethostid()      ?V*/
+       sys     sys_setsockopt  5       /* 1105  setsockopt()          V*/
+       sys     sys_shutdown    2       /* 1106  shutdown()           ?V*/
+       sys     irix_socket     3       /* 1107  socket()              V*/
+       sys     sys_gethostname 2       /* 1108  sys_gethostname()    ?V*/
+       sys     sys_sethostname 2       /* 1109  sethostname()        ?V*/
+       sys     irix_getdomainname 2    /* 1110  sys_getdomainname()  ?V*/
+       sys     sys_setdomainname 2     /* 1111  setdomainname()      ?V*/
+       sys     sys_truncate    2       /* 1112  truncate()            V*/
+       sys     sys_ftruncate   2       /* 1113  ftruncate()           V*/
+       sys     sys_rename      2       /* 1114  rename()              V*/
+       sys     sys_symlink     2       /* 1115  symlink()             V*/
+       sys     sys_readlink    3       /* 1116  readlink()            V*/
+       sys     irix_unimp      0       /* 1117  XXX IRIX 4 lstat()   DC*/
+       sys     irix_unimp      0       /* 1118  nothin'               V*/
+       sys     irix_unimp      0       /* 1119  XXX nfs_svc()        DC*/
+       sys     irix_unimp      0       /* 1120  XXX nfs_getfh()      DC*/
+       sys     irix_unimp      0       /* 1121  XXX async_daemon()   DC*/
+       sys     irix_unimp      0       /* 1122  XXX exportfs()       DC*/
+       sys     sys_setregid    2       /* 1123  setregid()            V*/
+       sys     sys_setreuid    2       /* 1124  setreuid()            V*/
+       sys     sys_getitimer   2       /* 1125  getitimer()           V*/
+       sys     sys_setitimer   3       /* 1126  setitimer()           V*/
+       sys     irix_unimp      1       /* 1127  XXX adjtime()        IV*/
+       sys     irix_gettimeofday 1     /* 1128  gettimeofday()        V*/
+       sys     irix_unimp      0       /* 1129  XXX sproc()          IV*/
+       sys     irix_prctl      0       /* 1130  prctl()              HV*/
+       sys     irix_unimp      0       /* 1131  XXX procblk()        IV*/
+       sys     irix_unimp      0       /* 1132  XXX sprocsp()        IV*/
+       sys     irix_unimp      0       /* 1133  XXX sgigsc()         IV*/
+       sys     irix_mmap32     6       /* 1134  mmap()    XXXflags?  ?V*/
+       sys     sys_munmap      2       /* 1135  munmap()              V*/
+       sys     sys_mprotect    3       /* 1136  mprotect()            V*/
+       sys     sys_msync       4       /* 1137  msync()               V*/
+       sys     irix_madvise    3       /* 1138  madvise()            DC*/
+       sys     irix_pagelock   3       /* 1139  pagelock()           IV*/
+       sys     irix_getpagesize 0      /* 1140  getpagesize()         V*/
+       sys     irix_quotactl   0       /* 1141  quotactl()            V*/
+       sys     irix_unimp      0       /* 1142  nobody home man       V*/
+       sys     sys_getpgid     1       /* 1143  BSD getpgrp()         V*/
+       sys     irix_BSDsetpgrp 2       /* 1143  BSD setpgrp()         V*/
+       sys     sys_vhangup     0       /* 1144  vhangup()             V*/
+       sys     sys_fsync       1       /* 1145  fsync()               V*/
+       sys     sys_fchdir      1       /* 1146  fchdir()              V*/
+       sys     sys_getrlimit   2       /* 1147  getrlimit()          ?V*/
+       sys     sys_setrlimit   2       /* 1148  setrlimit()          ?V*/
+       sys     sys_cacheflush  3       /* 1150  cacheflush()         HV*/
+       sys     sys_cachectl    3       /* 1151  cachectl()           HV*/
+       sys     sys_fchown      3       /* 1152  fchown()             ?V*/
+       sys     sys_fchmod      2       /* 1153  fchmod()             ?V*/
+       sys     irix_unimp      0       /* 1154  XXX IRIX 4 wait3()    V*/
+       sys     sys_socketpair  4       /* 1155  socketpair()          V*/
+       sys     irix_systeminfo 3       /* 1156  systeminfo()         IV*/
+       sys     irix_uname      1       /* 1157  uname()              IV*/
+       sys     irix_xstat      3       /* 1158  xstat()               V*/
+       sys     irix_lxstat     3       /* 1159  lxstat()              V*/
+       sys     irix_fxstat     3       /* 1160  fxstat()              V*/
+       sys     irix_xmknod     0       /* 1161  xmknod()             ?V*/
+       sys     irix_sigaction  4       /* 1162  sigaction()          ?V*/
+       sys     irix_sigpending 1       /* 1163  sigpending()         ?V*/
+       sys     irix_sigprocmask 3      /* 1164  sigprocmask()        ?V*/
+       sys     irix_sigsuspend 0       /* 1165  sigsuspend()         ?V*/
+       sys     irix_sigpoll_sys 3      /* 1166  sigpoll_sys()        IV*/
+       sys     irix_swapctl    2       /* 1167  swapctl()            IV*/
+       sys     irix_getcontext 0       /* 1168  getcontext()         HV*/
+       sys     irix_setcontext 0       /* 1169  setcontext()         HV*/
+       sys     irix_waitsys    5       /* 1170  waitsys()            IV*/
+       sys     irix_sigstack   2       /* 1171  sigstack()           HV*/
+       sys     irix_sigaltstack 2      /* 1172  sigaltstack()        HV*/
+       sys     irix_sigsendset 2       /* 1173  sigsendset()         IV*/
+       sys     irix_statvfs    2       /* 1174  statvfs()             V*/
+       sys     irix_fstatvfs   2       /* 1175  fstatvfs()            V*/
+       sys     irix_unimp      0       /* 1176  XXX getpmsg()        DC*/
+       sys     irix_unimp      0       /* 1177  XXX putpmsg()        DC*/
+       sys     sys_lchown      3       /* 1178  lchown()              V*/
+       sys     irix_priocntl   0       /* 1179  priocntl()           DC*/
+       sys     irix_sigqueue   4       /* 1180  sigqueue()           IV*/
+       sys     sys_readv       3       /* 1181  readv()               V*/
+       sys     sys_writev      3       /* 1182  writev()              V*/
+       sys     irix_truncate64 4       /* 1183  truncate64() XX32bit HV*/
+       sys     irix_ftruncate64 4      /* 1184  ftruncate64()XX32bit HV*/
+       sys     irix_mmap64     0       /* 1185  mmap64()     XX32bit HV*/
+       sys     irix_dmi        0       /* 1186  dmi()                DC*/
+       sys     irix_pread      6       /* 1187  pread()              IV*/
+       sys     irix_pwrite     6       /* 1188  pwrite()             IV*/
+       sys     sys_fsync       1       /* 1189  fdatasync()  XXPOSIX HV*/
+       sys     irix_sgifastpath 7      /* 1190  sgifastpath() WHEEE  IV*/
+       sys     irix_unimp      0       /* 1191  XXX attr_get()       DC*/
+       sys     irix_unimp      0       /* 1192  XXX attr_getf()      DC*/
+       sys     irix_unimp      0       /* 1193  XXX attr_set()       DC*/
+       sys     irix_unimp      0       /* 1194  XXX attr_setf()      DC*/
+       sys     irix_unimp      0       /* 1195  XXX attr_remove()    DC*/
+       sys     irix_unimp      0       /* 1196  XXX attr_removef()   DC*/
+       sys     irix_unimp      0       /* 1197  XXX attr_list()      DC*/
+       sys     irix_unimp      0       /* 1198  XXX attr_listf()     DC*/
+       sys     irix_unimp      0       /* 1199  XXX attr_multi()     DC*/
+       sys     irix_unimp      0       /* 1200  XXX attr_multif()    DC*/
+       sys     irix_statvfs64  2       /* 1201  statvfs64()           V*/
+       sys     irix_fstatvfs64 2       /* 1202  fstatvfs64()          V*/
+       sys     irix_getmountid 2       /* 1203  getmountid()XXXfsids HV*/
+       sys     irix_nsproc     5       /* 1204  nsproc()             IV*/
+       sys     irix_getdents64 3       /* 1205  getdents64()         HV*/
+       sys     irix_unimp      0       /* 1206  XXX DFS garbage      DC*/
+       sys     irix_ngetdents  4       /* 1207  ngetdents() XXXeop   HV*/
+       sys     irix_ngetdents64 4      /* 1208  ngetdents64() XXXeop HV*/
+       sys     irix_unimp      0       /* 1209  nothin'               V*/
+       sys     irix_unimp      0       /* 1210  XXX pidsprocsp()       */
+       sys     irix_unimp      0       /* 1211  XXX rexec()            */
+       sys     irix_unimp      0       /* 1212  XXX timer_create()     */
+       sys     irix_unimp      0       /* 1213  XXX timer_delete()     */
+       sys     irix_unimp      0       /* 1214  XXX timer_settime()    */
+       sys     irix_unimp      0       /* 1215  XXX timer_gettime()    */
+       sys     irix_unimp      0       /* 1216  XXX timer_setoverrun() */
+       sys     sys_sched_rr_get_interval 2             /* 1217  sched_rr_get_interval()V*/
+       sys     sys_sched_yield 0       /* 1218  sched_yield()         V*/
+       sys     sys_sched_getscheduler 1 /* 1219  sched_getscheduler()  V*/
+       sys     sys_sched_setscheduler 3 /* 1220  sched_setscheduler()  V*/
+       sys     sys_sched_getparam 2    /* 1221  sched_getparam()      V*/
+       sys     sys_sched_setparam 2    /* 1222  sched_setparam()      V*/
+       sys     irix_unimp      0       /* 1223  XXX usync_cntl()       */
+       sys     irix_unimp      0       /* 1224  XXX psema_cntl()       */
+       sys     irix_unimp      0       /* 1225  XXX restartreturn()    */
+
+       /* Just to pad things out nicely. */
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+       sys     irix_unimp      0
+
+       .endm
+
+       /*
+        * Pre-compute the number of _instruction_ bytes needed to load
+        * or store the arguments 6-8. Negative values are ignored.
+        */
+       .macro  sys function, nargs
+       PTR     \function
+       LONG    (\nargs << 2) - (5 << 2)
+       .endm
+
+       .align  4
+EXPORT(sys_call_table_irix5)
+       irix5syscalltable
diff --git a/arch/mips/kernel/irq-msc01.c b/arch/mips/kernel/irq-msc01.c
new file mode 100644 (file)
index 0000000..43c00ac
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2004 MIPS Inc
+ * Author: chris@mips.com
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <asm/ptrace.h>
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/msc01_ic.h>
+
+static unsigned long _icctrl_msc;
+#define MSC01_IC_REG_BASE      _icctrl_msc
+
+#define MSCIC_WRITE(reg, data) do { *(volatile u32 *)(reg) = data; } while (0)
+#define MSCIC_READ(reg, data)  do { data = *(volatile u32 *)(reg); } while (0)
+
+static unsigned int irq_base;
+
+/* mask off an interrupt */
+static inline void mask_msc_irq(unsigned int irq)
+{
+       if (irq < (irq_base + 32))
+               MSCIC_WRITE(MSC01_IC_DISL, 1<<(irq - irq_base));
+       else
+               MSCIC_WRITE(MSC01_IC_DISH, 1<<(irq - irq_base - 32));
+}
+
+/* unmask an interrupt */
+static inline void unmask_msc_irq(unsigned int irq)
+{
+       if (irq < (irq_base + 32))
+               MSCIC_WRITE(MSC01_IC_ENAL, 1<<(irq - irq_base));
+       else
+               MSCIC_WRITE(MSC01_IC_ENAH, 1<<(irq - irq_base - 32));
+}
+
+/*
+ * Enables the IRQ on SOC-it
+ */
+static void enable_msc_irq(unsigned int irq)
+{
+       unmask_msc_irq(irq);
+}
+
+/*
+ * Initialize the IRQ on SOC-it
+ */
+static unsigned int startup_msc_irq(unsigned int irq)
+{
+       unmask_msc_irq(irq);
+       return 0;
+}
+
+/*
+ * Disables the IRQ on SOC-it
+ */
+static void disable_msc_irq(unsigned int irq)
+{
+       mask_msc_irq(irq);
+}
+
+/*
+ * Masks and ACKs an IRQ
+ */
+static void level_mask_and_ack_msc_irq(unsigned int irq)
+{
+       mask_msc_irq(irq);
+       if (!cpu_has_ei)
+               MSCIC_WRITE(MSC01_IC_EOI, 0);
+}
+
+/*
+ * Masks and ACKs an IRQ
+ */
+static void edge_mask_and_ack_msc_irq(unsigned int irq)
+{
+       mask_msc_irq(irq);
+       if (!cpu_has_ei)
+               MSCIC_WRITE(MSC01_IC_EOI, 0);
+       else {
+               u32 r;
+               MSCIC_READ(MSC01_IC_SUP+irq*8, r);
+               MSCIC_WRITE(MSC01_IC_SUP+irq*8, r | ~MSC01_IC_SUP_EDGE_BIT);
+               MSCIC_WRITE(MSC01_IC_SUP+irq*8, r);
+       }
+}
+
+/*
+ * End IRQ processing
+ */
+static void end_msc_irq(unsigned int irq)
+{
+       if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+               unmask_msc_irq(irq);
+}
+
+/*
+ * Interrupt handler for interrupts coming from SOC-it.
+ */
+void ll_msc_irq(struct pt_regs *regs)
+{
+       unsigned int irq;
+
+       /* read the interrupt vector register */
+       MSCIC_READ(MSC01_IC_VEC, irq);
+       if (irq < 64)
+               do_IRQ(irq + irq_base, regs);
+       else {
+               /* Ignore spurious interrupt */
+       }
+}
+
+void
+msc_bind_eic_interrupt (unsigned int irq, unsigned int set)
+{
+       MSCIC_WRITE(MSC01_IC_RAMW,
+                   (irq<<MSC01_IC_RAMW_ADDR_SHF) | (set<<MSC01_IC_RAMW_DATA_SHF));
+}
+
+#define shutdown_msc_irq       disable_msc_irq
+
+struct hw_interrupt_type msc_levelirq_type = {
+       "SOC-it-Level",
+       startup_msc_irq,
+       shutdown_msc_irq,
+       enable_msc_irq,
+       disable_msc_irq,
+       level_mask_and_ack_msc_irq,
+       end_msc_irq,
+       NULL
+};
+
+struct hw_interrupt_type msc_edgeirq_type = {
+       "SOC-it-Edge",
+       startup_msc_irq,
+       shutdown_msc_irq,
+       enable_msc_irq,
+       disable_msc_irq,
+       edge_mask_and_ack_msc_irq,
+       end_msc_irq,
+       NULL
+};
+
+
+void __init init_msc_irqs(unsigned int base, msc_irqmap_t *imp, int nirq)
+{
+       extern void (*board_bind_eic_interrupt)(unsigned int irq, unsigned int regset);
+
+       _icctrl_msc = (unsigned long) ioremap (MIPS_MSC01_IC_REG_BASE, 0x40000);
+
+       /* Reset interrupt controller - initialises all registers to 0 */
+       MSCIC_WRITE(MSC01_IC_RST, MSC01_IC_RST_RST_BIT);
+
+       board_bind_eic_interrupt = &msc_bind_eic_interrupt;
+
+       for (; nirq >= 0; nirq--, imp++) {
+               int n = imp->im_irq;
+
+               switch (imp->im_type) {
+               case MSC01_IRQ_EDGE:
+                       irq_desc[base+n].handler = &msc_edgeirq_type;
+                       if (cpu_has_ei)
+                               MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT);
+                       else
+                               MSCIC_WRITE(MSC01_IC_SUP+n*8, MSC01_IC_SUP_EDGE_BIT | imp->im_lvl);
+                       break;
+               case MSC01_IRQ_LEVEL:
+                       irq_desc[base+n].handler = &msc_levelirq_type;
+                       if (cpu_has_ei)
+                               MSCIC_WRITE(MSC01_IC_SUP+n*8, 0);
+                       else
+                               MSCIC_WRITE(MSC01_IC_SUP+n*8, imp->im_lvl);
+               }
+       }
+
+       irq_base = base;
+
+       MSCIC_WRITE(MSC01_IC_GENA, MSC01_IC_GENA_GENA_BIT);     /* Enable interrupt generation */
+
+}
diff --git a/arch/mips/kernel/irq-rm9000.c b/arch/mips/kernel/irq-rm9000.c
new file mode 100644 (file)
index 0000000..bdd1302
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2003 Ralf Baechle
+ *
+ * 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.
+ *
+ * Handler for RM9000 extended interrupts.  These are a non-standard
+ * feature so we handle them separately from standard interrupts.
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+#include <asm/system.h>
+
+static int irq_base;
+
+static inline void unmask_rm9k_irq(unsigned int irq)
+{
+       set_c0_intcontrol(0x1000 << (irq - irq_base));
+}
+
+static inline void mask_rm9k_irq(unsigned int irq)
+{
+       clear_c0_intcontrol(0x1000 << (irq - irq_base));
+}
+
+static inline void rm9k_cpu_irq_enable(unsigned int irq)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       unmask_rm9k_irq(irq);
+       local_irq_restore(flags);
+}
+
+static void rm9k_cpu_irq_disable(unsigned int irq)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       mask_rm9k_irq(irq);
+       local_irq_restore(flags);
+}
+
+static unsigned int rm9k_cpu_irq_startup(unsigned int irq)
+{
+       rm9k_cpu_irq_enable(irq);
+
+       return 0;
+}
+
+#define        rm9k_cpu_irq_shutdown   rm9k_cpu_irq_disable
+
+/*
+ * Performance counter interrupts are global on all processors.
+ */
+static void local_rm9k_perfcounter_irq_startup(void *args)
+{
+       unsigned int irq = (unsigned int) args;
+
+       rm9k_cpu_irq_enable(irq);
+}
+
+static unsigned int rm9k_perfcounter_irq_startup(unsigned int irq)
+{
+       on_each_cpu(local_rm9k_perfcounter_irq_startup, (void *) irq, 0, 1);
+
+       return 0;
+}
+
+static void local_rm9k_perfcounter_irq_shutdown(void *args)
+{
+       unsigned int irq = (unsigned int) args;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       mask_rm9k_irq(irq);
+       local_irq_restore(flags);
+}
+
+static void rm9k_perfcounter_irq_shutdown(unsigned int irq)
+{
+       on_each_cpu(local_rm9k_perfcounter_irq_shutdown, (void *) irq, 0, 1);
+}
+
+
+/*
+ * While we ack the interrupt interrupts are disabled and thus we don't need
+ * to deal with concurrency issues.  Same for rm9k_cpu_irq_end.
+ */
+static void rm9k_cpu_irq_ack(unsigned int irq)
+{
+       mask_rm9k_irq(irq);
+}
+
+static void rm9k_cpu_irq_end(unsigned int irq)
+{
+       if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+               unmask_rm9k_irq(irq);
+}
+
+static hw_irq_controller rm9k_irq_controller = {
+       "RM9000",
+       rm9k_cpu_irq_startup,
+       rm9k_cpu_irq_shutdown,
+       rm9k_cpu_irq_enable,
+       rm9k_cpu_irq_disable,
+       rm9k_cpu_irq_ack,
+       rm9k_cpu_irq_end,
+};
+
+static hw_irq_controller rm9k_perfcounter_irq = {
+       "RM9000",
+       rm9k_perfcounter_irq_startup,
+       rm9k_perfcounter_irq_shutdown,
+       rm9k_cpu_irq_enable,
+       rm9k_cpu_irq_disable,
+       rm9k_cpu_irq_ack,
+       rm9k_cpu_irq_end,
+};
+
+unsigned int rm9000_perfcount_irq;
+
+EXPORT_SYMBOL(rm9000_perfcount_irq);
+
+void __init rm9k_cpu_irq_init(int base)
+{
+       int i;
+
+       clear_c0_intcontrol(0x0000f000);                /* Mask all */
+
+       for (i = base; i < base + 4; i++) {
+               irq_desc[i].status = IRQ_DISABLED;
+               irq_desc[i].action = NULL;
+               irq_desc[i].depth = 1;
+               irq_desc[i].handler = &rm9k_irq_controller;
+       }
+
+       rm9000_perfcount_irq = base + 1;
+       irq_desc[rm9000_perfcount_irq].handler = &rm9k_perfcounter_irq;
+
+       irq_base = base;
+}
diff --git a/arch/mips/kernel/signal-common.h b/arch/mips/kernel/signal-common.h
new file mode 100644 (file)
index 0000000..f9234df
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1991, 1992  Linus Torvalds
+ * Copyright (C) 1994 - 2000  Ralf Baechle
+ * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
+ */
+
+static inline int
+setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
+{
+       int err = 0;
+
+       err |= __put_user(regs->cp0_epc, &sc->sc_pc);
+       err |= __put_user(regs->cp0_status, &sc->sc_status);
+
+#define save_gp_reg(i) do {                                            \
+       err |= __put_user(regs->regs[i], &sc->sc_regs[i]);              \
+} while(0)
+       __put_user(0, &sc->sc_regs[0]); save_gp_reg(1); save_gp_reg(2);
+       save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); save_gp_reg(6);
+       save_gp_reg(7); save_gp_reg(8); save_gp_reg(9); save_gp_reg(10);
+       save_gp_reg(11); save_gp_reg(12); save_gp_reg(13); save_gp_reg(14);
+       save_gp_reg(15); save_gp_reg(16); save_gp_reg(17); save_gp_reg(18);
+       save_gp_reg(19); save_gp_reg(20); save_gp_reg(21); save_gp_reg(22);
+       save_gp_reg(23); save_gp_reg(24); save_gp_reg(25); save_gp_reg(26);
+       save_gp_reg(27); save_gp_reg(28); save_gp_reg(29); save_gp_reg(30);
+       save_gp_reg(31);
+#undef save_gp_reg
+
+       err |= __put_user(regs->hi, &sc->sc_mdhi);
+       err |= __put_user(regs->lo, &sc->sc_mdlo);
+       err |= __put_user(regs->cp0_cause, &sc->sc_cause);
+       err |= __put_user(regs->cp0_badvaddr, &sc->sc_badvaddr);
+
+       err |= __put_user(!!used_math(), &sc->sc_used_math);
+
+       if (!used_math())
+               goto out;
+
+       /*
+        * Save FPU state to signal context.  Signal handler will "inherit"
+        * current FPU state.
+        */
+       preempt_disable();
+
+       if (!is_fpu_owner()) {
+               own_fpu();
+               restore_fp(current);
+       }
+       err |= save_fp_context(sc);
+
+       preempt_enable();
+
+out:
+       return err;
+}
+
+static inline int
+restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
+{
+       int err = 0;
+       unsigned int used_math;
+
+       /* Always make any pending restarted system calls return -EINTR */
+       current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+       err |= __get_user(regs->cp0_epc, &sc->sc_pc);
+       err |= __get_user(regs->hi, &sc->sc_mdhi);
+       err |= __get_user(regs->lo, &sc->sc_mdlo);
+
+#define restore_gp_reg(i) do {                                         \
+       err |= __get_user(regs->regs[i], &sc->sc_regs[i]);              \
+} while(0)
+       restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3);
+       restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6);
+       restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9);
+       restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12);
+       restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15);
+       restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18);
+       restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21);
+       restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24);
+       restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27);
+       restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30);
+       restore_gp_reg(31);
+#undef restore_gp_reg
+
+       err |= __get_user(used_math, &sc->sc_used_math);
+       conditional_used_math(used_math);
+
+       preempt_disable();
+
+       if (used_math()) {
+               /* restore fpu context if we have used it before */
+               own_fpu();
+               err |= restore_fp_context(sc);
+       } else {
+               /* signal handler may have used FPU.  Give it up. */
+               lose_fpu();
+       }
+
+       preempt_enable();
+
+       return err;
+}
+
+/*
+ * Determine which stack to use..
+ */
+static inline void *
+get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size)
+{
+       unsigned long sp, almask;
+
+       /* Default to using normal stack */
+       sp = regs->regs[29];
+
+       /*
+        * FPU emulator may have it's own trampoline active just
+        * above the user stack, 16-bytes before the next lowest
+        * 16 byte boundary.  Try to avoid trashing it.
+        */
+       sp -= 32;
+
+       /* This is the X/Open sanctioned signal stack switching.  */
+       if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags (sp) == 0))
+               sp = current->sas_ss_sp + current->sas_ss_size;
+
+       if (PLAT_TRAMPOLINE_STUFF_LINE)
+               almask = ~(PLAT_TRAMPOLINE_STUFF_LINE - 1);
+       else
+               almask = ALMASK;
+
+       return (void *)((sp - frame_size) & almask);
+}
diff --git a/arch/mips/lib/iomap.c b/arch/mips/lib/iomap.c
new file mode 100644 (file)
index 0000000..b5d5fa8
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *  iomap.c, Memory Mapped I/O routines for MIPS architecture.
+ *
+ *  This code is based on lib/iomap.c, by Linus Torvalds.
+ *
+ *  Copyright (C) 2004-2005  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+
+void __iomem *ioport_map(unsigned long port, unsigned int nr)
+{
+       unsigned long end;
+
+       end = port + nr - 1UL;
+       if (ioport_resource.start > port ||
+           ioport_resource.end < end || port > end)
+               return NULL;
+
+       return (void __iomem *)(mips_io_port_base + port);
+}
+
+void ioport_unmap(void __iomem *addr)
+{
+}
+EXPORT_SYMBOL(ioport_map);
+EXPORT_SYMBOL(ioport_unmap);
+
+void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
+{
+       unsigned long start, len, flags;
+
+       if (dev == NULL)
+               return NULL;
+
+       start = pci_resource_start(dev, bar);
+       len = pci_resource_len(dev, bar);
+       if (!start || !len)
+               return NULL;
+
+       if (maxlen != 0 && len > maxlen)
+               len = maxlen;
+
+       flags = pci_resource_flags(dev, bar);
+       if (flags & IORESOURCE_IO)
+               return ioport_map(start, len);
+       if (flags & IORESOURCE_MEM) {
+               if (flags & IORESOURCE_CACHEABLE)
+                       return ioremap_cacheable_cow(start, len);
+               return ioremap_nocache(start, len);
+       }
+
+       return NULL;
+}
+
+void pci_iounmap(struct pci_dev *dev, void __iomem *addr)
+{
+       iounmap(addr);
+}
+EXPORT_SYMBOL(pci_iomap);
+EXPORT_SYMBOL(pci_iounmap);
diff --git a/arch/mips/mm/dma-ip32.c b/arch/mips/mm/dma-ip32.c
new file mode 100644 (file)
index 0000000..2cbe196
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2000  Ani Joshi <ajoshi@unixbox.com>
+ * Copyright (C) 2000, 2001  Ralf Baechle <ralf@gnu.org>
+ * Copyright (C) 2005 Ilya A. Volynets-Evenbakh <ilya@total-knowledge.com>
+ * swiped from i386, and cloned for MIPS by Geert, polished by Ralf.
+ * IP32 changes by Ilya.
+ */
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <asm/ip32/crime.h>
+
+/*
+ * Warning on the terminology - Linux calls an uncached area coherent;
+ * MIPS terminology calls memory areas with hardware maintained coherency
+ * coherent.
+ */
+
+/*
+ * Few notes.
+ * 1. CPU sees memory as two chunks: 0-256M@0x0, and the rest @0x40000000+256M
+ * 2. PCI sees memory as one big chunk @0x0 (or we could use 0x40000000 for native-endian)
+ * 3. All other devices see memory as one big chunk at 0x40000000
+ * 4. Non-PCI devices will pass NULL as struct device*
+ * Thus we translate differently, depending on device.
+ */
+
+#define RAM_OFFSET_MASK        0x3fffffff
+
+void *dma_alloc_noncoherent(struct device *dev, size_t size,
+       dma_addr_t * dma_handle, int gfp)
+{
+       void *ret;
+       /* ignore region specifiers */
+       gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
+
+       if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
+               gfp |= GFP_DMA;
+       ret = (void *) __get_free_pages(gfp, get_order(size));
+
+       if (ret != NULL) {
+               unsigned long addr = virt_to_phys(ret)&RAM_OFFSET_MASK;
+               memset(ret, 0, size);
+               if(dev==NULL)
+                   addr+= CRIME_HI_MEM_BASE;
+               *dma_handle = addr;
+       }
+
+       return ret;
+}
+
+EXPORT_SYMBOL(dma_alloc_noncoherent);
+
+void *dma_alloc_coherent(struct device *dev, size_t size,
+       dma_addr_t * dma_handle, int gfp)
+{
+       void *ret;
+
+       ret = dma_alloc_noncoherent(dev, size, dma_handle, gfp);
+       if (ret) {
+               dma_cache_wback_inv((unsigned long) ret, size);
+               ret = UNCAC_ADDR(ret);
+       }
+
+       return ret;
+}
+
+EXPORT_SYMBOL(dma_alloc_coherent);
+
+void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr,
+       dma_addr_t dma_handle)
+{
+       free_pages((unsigned long) vaddr, get_order(size));
+}
+
+EXPORT_SYMBOL(dma_free_noncoherent);
+
+void dma_free_coherent(struct device *dev, size_t size, void *vaddr,
+       dma_addr_t dma_handle)
+{
+       unsigned long addr = (unsigned long) vaddr;
+
+       addr = CAC_ADDR(addr);
+       free_pages(addr, get_order(size));
+}
+
+EXPORT_SYMBOL(dma_free_coherent);
+
+static inline void __dma_sync(unsigned long addr, size_t size,
+       enum dma_data_direction direction)
+{
+       switch (direction) {
+       case DMA_TO_DEVICE:
+               dma_cache_wback(addr, size);
+               break;
+
+       case DMA_FROM_DEVICE:
+               dma_cache_inv(addr, size);
+               break;
+
+       case DMA_BIDIRECTIONAL:
+               dma_cache_wback_inv(addr, size);
+               break;
+
+       default:
+               BUG();
+       }
+}
+
+dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
+       enum dma_data_direction direction)
+{
+       unsigned long addr = (unsigned long) ptr;
+
+       switch (direction) {
+       case DMA_TO_DEVICE:
+               dma_cache_wback(addr, size);
+               break;
+
+       case DMA_FROM_DEVICE:
+               dma_cache_inv(addr, size);
+               break;
+
+       case DMA_BIDIRECTIONAL:
+               dma_cache_wback_inv(addr, size);
+               break;
+
+       default:
+               BUG();
+       }
+
+       addr = virt_to_phys(ptr)&RAM_OFFSET_MASK;;
+       if(dev == NULL)
+           addr+=CRIME_HI_MEM_BASE;
+       return (dma_addr_t)addr;
+}
+
+EXPORT_SYMBOL(dma_map_single);
+
+void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+       enum dma_data_direction direction)
+{
+       switch (direction) {
+       case DMA_TO_DEVICE:
+               break;
+
+       case DMA_FROM_DEVICE:
+               break;
+
+       case DMA_BIDIRECTIONAL:
+               break;
+
+       default:
+               BUG();
+       }
+}
+
+EXPORT_SYMBOL(dma_unmap_single);
+
+int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+       enum dma_data_direction direction)
+{
+       int i;
+
+       BUG_ON(direction == DMA_NONE);
+
+       for (i = 0; i < nents; i++, sg++) {
+               unsigned long addr;
+
+               addr = (unsigned long) page_address(sg->page)+sg->offset;
+               if (addr)
+                       __dma_sync(addr, sg->length, direction);
+               addr = __pa(addr)&RAM_OFFSET_MASK;;
+               if(dev == NULL)
+                       addr +=  CRIME_HI_MEM_BASE;
+               sg->dma_address = (dma_addr_t)addr;
+       }
+
+       return nents;
+}
+
+EXPORT_SYMBOL(dma_map_sg);
+
+dma_addr_t dma_map_page(struct device *dev, struct page *page,
+       unsigned long offset, size_t size, enum dma_data_direction direction)
+{
+       unsigned long addr;
+
+       BUG_ON(direction == DMA_NONE);
+
+       addr = (unsigned long) page_address(page) + offset;
+       dma_cache_wback_inv(addr, size);
+       addr = __pa(addr)&RAM_OFFSET_MASK;;
+       if(dev == NULL)
+               addr +=  CRIME_HI_MEM_BASE;
+
+       return (dma_addr_t)addr;
+}
+
+EXPORT_SYMBOL(dma_map_page);
+
+void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
+       enum dma_data_direction direction)
+{
+       BUG_ON(direction == DMA_NONE);
+
+       if (direction != DMA_TO_DEVICE) {
+               unsigned long addr;
+
+               dma_address&=RAM_OFFSET_MASK;
+               addr = dma_address + PAGE_OFFSET;
+               if(dma_address>=256*1024*1024)
+                       addr+=CRIME_HI_MEM_BASE;
+               dma_cache_wback_inv(addr, size);
+       }
+}
+
+EXPORT_SYMBOL(dma_unmap_page);
+
+void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries,
+       enum dma_data_direction direction)
+{
+       unsigned long addr;
+       int i;
+
+       BUG_ON(direction == DMA_NONE);
+
+       if (direction == DMA_TO_DEVICE)
+               return;
+
+       for (i = 0; i < nhwentries; i++, sg++) {
+               addr = (unsigned long) page_address(sg->page);
+               if (!addr)
+                       continue;
+               dma_cache_wback_inv(addr + sg->offset, sg->length);
+       }
+}
+
+EXPORT_SYMBOL(dma_unmap_sg);
+
+void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
+       size_t size, enum dma_data_direction direction)
+{
+       unsigned long addr;
+
+       BUG_ON(direction == DMA_NONE);
+
+       dma_handle&=RAM_OFFSET_MASK;
+       addr = dma_handle + PAGE_OFFSET;
+       if(dma_handle>=256*1024*1024)
+           addr+=CRIME_HI_MEM_BASE;
+       __dma_sync(addr, size, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_single_for_cpu);
+
+void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle,
+       size_t size, enum dma_data_direction direction)
+{
+       unsigned long addr;
+
+       BUG_ON(direction == DMA_NONE);
+
+       dma_handle&=RAM_OFFSET_MASK;
+       addr = dma_handle + PAGE_OFFSET;
+       if(dma_handle>=256*1024*1024)
+           addr+=CRIME_HI_MEM_BASE;
+       __dma_sync(addr, size, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_single_for_device);
+
+void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle,
+       unsigned long offset, size_t size, enum dma_data_direction direction)
+{
+       unsigned long addr;
+
+       BUG_ON(direction == DMA_NONE);
+
+       dma_handle&=RAM_OFFSET_MASK;
+       addr = dma_handle + offset + PAGE_OFFSET;
+       if(dma_handle>=256*1024*1024)
+           addr+=CRIME_HI_MEM_BASE;
+       __dma_sync(addr, size, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_single_range_for_cpu);
+
+void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle,
+       unsigned long offset, size_t size, enum dma_data_direction direction)
+{
+       unsigned long addr;
+
+       BUG_ON(direction == DMA_NONE);
+
+       dma_handle&=RAM_OFFSET_MASK;
+       addr = dma_handle + offset + PAGE_OFFSET;
+       if(dma_handle>=256*1024*1024)
+           addr+=CRIME_HI_MEM_BASE;
+       __dma_sync(addr, size, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_single_range_for_device);
+
+void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
+       enum dma_data_direction direction)
+{
+       int i;
+
+       BUG_ON(direction == DMA_NONE);
+
+       /* Make sure that gcc doesn't leave the empty loop body.  */
+       for (i = 0; i < nelems; i++, sg++)
+               __dma_sync((unsigned long)page_address(sg->page),
+                          sg->length, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_sg_for_cpu);
+
+void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems,
+       enum dma_data_direction direction)
+{
+       int i;
+
+       BUG_ON(direction == DMA_NONE);
+
+       /* Make sure that gcc doesn't leave the empty loop body.  */
+       for (i = 0; i < nelems; i++, sg++)
+               __dma_sync((unsigned long)page_address(sg->page),
+                          sg->length, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_sg_for_device);
+
+int dma_mapping_error(dma_addr_t dma_addr)
+{
+       return 0;
+}
+
+EXPORT_SYMBOL(dma_mapping_error);
+
+int dma_supported(struct device *dev, u64 mask)
+{
+       /*
+        * we fall back to GFP_DMA when the mask isn't all 1s,
+        * so we can't guarantee allocations that must be
+        * within a tighter range than GFP_DMA..
+        */
+       if (mask < 0x00ffffff)
+               return 0;
+
+       return 1;
+}
+
+EXPORT_SYMBOL(dma_supported);
+
+int dma_is_consistent(dma_addr_t dma_addr)
+{
+       return 1;
+}
+
+EXPORT_SYMBOL(dma_is_consistent);
+
+void dma_cache_sync(void *vaddr, size_t size, enum dma_data_direction direction)
+{
+       if (direction == DMA_NONE)
+               return;
+
+       dma_cache_wback_inv((unsigned long)vaddr, size);
+}
+
+EXPORT_SYMBOL(dma_cache_sync);
+
diff --git a/arch/mips/mm/tlbex-fault.S b/arch/mips/mm/tlbex-fault.S
new file mode 100644 (file)
index 0000000..9e7f417
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1999 Ralf Baechle
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ */
+#include <asm/mipsregs.h>
+#include <asm/page.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+       .macro tlb_do_page_fault, write
+       NESTED(tlb_do_page_fault_\write, PT_SIZE, sp)
+       SAVE_ALL
+       MFC0    a2, CP0_BADVADDR
+       KMODE
+       move    a0, sp
+       REG_S   a2, PT_BVADDR(sp)
+       li      a1, \write
+       jal     do_page_fault
+       j       ret_from_exception
+       END(tlb_do_page_fault_\write)
+       .endm
+
+       tlb_do_page_fault 0
+       tlb_do_page_fault 1
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
new file mode 100644 (file)
index 0000000..fc3f5c5
--- /dev/null
@@ -0,0 +1,1177 @@
+/*
+ * 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.
+ *
+ * Synthesize TLB refill handlers at runtime.
+ *
+ * Copyright (C) 2004 by Thiemo Seufer
+ */
+
+#include <stdarg.h>
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+#include <asm/cacheflush.h>
+#include <asm/mmu_context.h>
+#include <asm/inst.h>
+#include <asm/elf.h>
+#include <asm/smp.h>
+
+/* #define DEBUG_TLB */
+
+static __init int __attribute__((unused)) r45k_bvahwbug(void)
+{
+       /* XXX: We should probe for the presence of this bug, but we don't. */
+       return 0;
+}
+
+static __init int __attribute__((unused)) r4k_250MHZhwbug(void)
+{
+       /* XXX: We should probe for the presence of this bug, but we don't. */
+       return 0;
+}
+
+static __init int __attribute__((unused)) bcm1250_m3_war(void)
+{
+       return BCM1250_M3_WAR;
+}
+
+/*
+ * A little micro-assembler, intended for TLB refill handler
+ * synthesizing. It is intentionally kept simple, does only support
+ * a subset of instructions, and does not try to hide pipeline effects
+ * like branch delay slots.
+ */
+
+enum fields
+{
+       RS = 0x001,
+       RT = 0x002,
+       RD = 0x004,
+       RE = 0x008,
+       SIMM = 0x010,
+       UIMM = 0x020,
+       BIMM = 0x040,
+       JIMM = 0x080,
+       FUNC = 0x100,
+};
+
+#define OP_MASK                0x2f
+#define OP_SH          26
+#define RS_MASK                0x1f
+#define RS_SH          21
+#define RT_MASK                0x1f
+#define RT_SH          16
+#define RD_MASK                0x1f
+#define RD_SH          11
+#define RE_MASK                0x1f
+#define RE_SH          6
+#define IMM_MASK       0xffff
+#define IMM_SH         0
+#define JIMM_MASK      0x3ffffff
+#define JIMM_SH                0
+#define FUNC_MASK      0x2f
+#define FUNC_SH                0
+
+enum opcode {
+       insn_invalid,
+       insn_addu, insn_addiu, insn_and, insn_andi, insn_beq,
+       insn_bgez, insn_bgezl, insn_bltz, insn_bltzl, insn_bne,
+       insn_daddu, insn_daddiu, insn_dmfc0, insn_dmtc0,
+       insn_dsll, insn_dsll32, insn_dsra, insn_dsrl, insn_dsrl32,
+       insn_dsubu, insn_eret, insn_j, insn_jal, insn_jr, insn_ld,
+       insn_lui, insn_lw, insn_mfc0, insn_mtc0, insn_ori, insn_rfe,
+       insn_sd, insn_sll, insn_sra, insn_srl, insn_subu, insn_sw,
+       insn_tlbp, insn_tlbwi, insn_tlbwr, insn_xor, insn_xori
+};
+
+struct insn {
+       enum opcode opcode;
+       u32 match;
+       enum fields fields;
+};
+
+/* This macro sets the non-variable bits of an instruction. */
+#define M(a, b, c, d, e, f)                                    \
+       ((a) << OP_SH                                           \
+        | (b) << RS_SH                                         \
+        | (c) << RT_SH                                         \
+        | (d) << RD_SH                                         \
+        | (e) << RE_SH                                         \
+        | (f) << FUNC_SH)
+
+static __initdata struct insn insn_table[] = {
+       { insn_addiu, M(addiu_op,0,0,0,0,0), RS | RT | SIMM },
+       { insn_addu, M(spec_op,0,0,0,0,addu_op), RS | RT | RD },
+       { insn_and, M(spec_op,0,0,0,0,and_op), RS | RT | RD },
+       { insn_andi, M(andi_op,0,0,0,0,0), RS | RT | UIMM },
+       { insn_beq, M(beq_op,0,0,0,0,0), RS | RT | BIMM },
+       { insn_bgez, M(bcond_op,0,bgez_op,0,0,0), RS | BIMM },
+       { insn_bgezl, M(bcond_op,0,bgezl_op,0,0,0), RS | BIMM },
+       { insn_bltz, M(bcond_op,0,bltz_op,0,0,0), RS | BIMM },
+       { insn_bltzl, M(bcond_op,0,bltzl_op,0,0,0), RS | BIMM },
+       { insn_bne, M(bne_op,0,0,0,0,0), RS | RT | BIMM },
+       { insn_daddiu, M(daddiu_op,0,0,0,0,0), RS | RT | SIMM },
+       { insn_daddu, M(spec_op,0,0,0,0,daddu_op), RS | RT | RD },
+       { insn_dmfc0, M(cop0_op,dmfc_op,0,0,0,0), RT | RD },
+       { insn_dmtc0, M(cop0_op,dmtc_op,0,0,0,0), RT | RD },
+       { insn_dsll, M(spec_op,0,0,0,0,dsll_op), RT | RD | RE },
+       { insn_dsll32, M(spec_op,0,0,0,0,dsll32_op), RT | RD | RE },
+       { insn_dsra, M(spec_op,0,0,0,0,dsra_op), RT | RD | RE },
+       { insn_dsrl, M(spec_op,0,0,0,0,dsrl_op), RT | RD | RE },
+       { insn_dsrl32, M(spec_op,0,0,0,0,dsrl32_op), RT | RD | RE },
+       { insn_dsubu, M(spec_op,0,0,0,0,dsubu_op), RS | RT | RD },
+       { insn_eret, M(cop0_op,cop_op,0,0,0,eret_op), 0 },
+       { insn_j, M(j_op,0,0,0,0,0), JIMM },
+       { insn_jal, M(jal_op,0,0,0,0,0), JIMM },
+       { insn_jr, M(spec_op,0,0,0,0,jr_op), RS },
+       { insn_ld, M(ld_op,0,0,0,0,0), RS | RT | SIMM },
+       { insn_lui, M(lui_op,0,0,0,0,0), RT | SIMM },
+       { insn_lw, M(lw_op,0,0,0,0,0), RS | RT | SIMM },
+       { insn_mfc0, M(cop0_op,mfc_op,0,0,0,0), RT | RD },
+       { insn_mtc0, M(cop0_op,mtc_op,0,0,0,0), RT | RD },
+       { insn_ori, M(ori_op,0,0,0,0,0), RS | RT | UIMM },
+       { insn_rfe, M(cop0_op,cop_op,0,0,0,rfe_op), 0 },
+       { insn_sd, M(sd_op,0,0,0,0,0), RS | RT | SIMM },
+       { insn_sll, M(spec_op,0,0,0,0,sll_op), RT | RD | RE },
+       { insn_sra, M(spec_op,0,0,0,0,sra_op), RT | RD | RE },
+       { insn_srl, M(spec_op,0,0,0,0,srl_op), RT | RD | RE },
+       { insn_subu, M(spec_op,0,0,0,0,subu_op), RS | RT | RD },
+       { insn_sw, M(sw_op,0,0,0,0,0), RS | RT | SIMM },
+       { insn_tlbp, M(cop0_op,cop_op,0,0,0,tlbp_op), 0 },
+       { insn_tlbwi, M(cop0_op,cop_op,0,0,0,tlbwi_op), 0 },
+       { insn_tlbwr, M(cop0_op,cop_op,0,0,0,tlbwr_op), 0 },
+       { insn_xor, M(spec_op,0,0,0,0,xor_op), RS | RT | RD },
+       { insn_xori, M(xori_op,0,0,0,0,0), RS | RT | UIMM },
+       { insn_invalid, 0, 0 }
+};
+
+#undef M
+
+static __init u32 build_rs(u32 arg)
+{
+       if (arg & ~RS_MASK)
+               printk(KERN_WARNING "TLB synthesizer field overflow\n");
+
+       return (arg & RS_MASK) << RS_SH;
+}
+
+static __init u32 build_rt(u32 arg)
+{
+       if (arg & ~RT_MASK)
+               printk(KERN_WARNING "TLB synthesizer field overflow\n");
+
+       return (arg & RT_MASK) << RT_SH;
+}
+
+static __init u32 build_rd(u32 arg)
+{
+       if (arg & ~RD_MASK)
+               printk(KERN_WARNING "TLB synthesizer field overflow\n");
+
+       return (arg & RD_MASK) << RD_SH;
+}
+
+static __init u32 build_re(u32 arg)
+{
+       if (arg & ~RE_MASK)
+               printk(KERN_WARNING "TLB synthesizer field overflow\n");
+
+       return (arg & RE_MASK) << RE_SH;
+}
+
+static __init u32 build_simm(s32 arg)
+{
+       if (arg > 0x7fff || arg < -0x8000)
+               printk(KERN_WARNING "TLB synthesizer field overflow\n");
+
+       return arg & 0xffff;
+}
+
+static __init u32 build_uimm(u32 arg)
+{
+       if (arg & ~IMM_MASK)
+               printk(KERN_WARNING "TLB synthesizer field overflow\n");
+
+       return arg & IMM_MASK;
+}
+
+static __init u32 build_bimm(s32 arg)
+{
+       if (arg > 0x1ffff || arg < -0x20000)
+               printk(KERN_WARNING "TLB synthesizer field overflow\n");
+
+       if (arg & 0x3)
+               printk(KERN_WARNING "Invalid TLB synthesizer branch target\n");
+
+       return ((arg < 0) ? (1 << 15) : 0) | ((arg >> 2) & 0x7fff);
+}
+
+static __init u32 build_jimm(u32 arg)
+{
+       if (arg & ~((JIMM_MASK) << 2))
+               printk(KERN_WARNING "TLB synthesizer field overflow\n");
+
+       return (arg >> 2) & JIMM_MASK;
+}
+
+static __init u32 build_func(u32 arg)
+{
+       if (arg & ~FUNC_MASK)
+               printk(KERN_WARNING "TLB synthesizer field overflow\n");
+
+       return arg & FUNC_MASK;
+}
+
+/*
+ * The order of opcode arguments is implicitly left to right,
+ * starting with RS and ending with FUNC or IMM.
+ */
+static void __init build_insn(u32 **buf, enum opcode opc, ...)
+{
+       struct insn *ip = NULL;
+       unsigned int i;
+       va_list ap;
+       u32 op;
+
+       for (i = 0; insn_table[i].opcode != insn_invalid; i++)
+               if (insn_table[i].opcode == opc) {
+                       ip = &insn_table[i];
+                       break;
+               }
+
+       if (!ip)
+               panic("Unsupported TLB synthesizer instruction %d", opc);
+
+       op = ip->match;
+       va_start(ap, opc);
+       if (ip->fields & RS) op |= build_rs(va_arg(ap, u32));
+       if (ip->fields & RT) op |= build_rt(va_arg(ap, u32));
+       if (ip->fields & RD) op |= build_rd(va_arg(ap, u32));
+       if (ip->fields & RE) op |= build_re(va_arg(ap, u32));
+       if (ip->fields & SIMM) op |= build_simm(va_arg(ap, s32));
+       if (ip->fields & UIMM) op |= build_uimm(va_arg(ap, u32));
+       if (ip->fields & BIMM) op |= build_bimm(va_arg(ap, s32));
+       if (ip->fields & JIMM) op |= build_jimm(va_arg(ap, u32));
+       if (ip->fields & FUNC) op |= build_func(va_arg(ap, u32));
+       va_end(ap);
+
+       **buf = op;
+       (*buf)++;
+}
+
+#define I_u1u2u3(op)                                           \
+       static inline void i##op(u32 **buf, unsigned int a,     \
+               unsigned int b, unsigned int c)                 \
+       {                                                       \
+               build_insn(buf, insn##op, a, b, c);             \
+       }
+
+#define I_u2u1u3(op)                                           \
+       static inline void i##op(u32 **buf, unsigned int a,     \
+               unsigned int b, unsigned int c)                 \
+       {                                                       \
+               build_insn(buf, insn##op, b, a, c);             \
+       }
+
+#define I_u3u1u2(op)                                           \
+       static inline void i##op(u32 **buf, unsigned int a,     \
+               unsigned int b, unsigned int c)                 \
+       {                                                       \
+               build_insn(buf, insn##op, b, c, a);             \
+       }
+
+#define I_u1u2s3(op)                                           \
+       static inline void i##op(u32 **buf, unsigned int a,     \
+               unsigned int b, signed int c)                   \
+       {                                                       \
+               build_insn(buf, insn##op, a, b, c);             \
+       }
+
+#define I_u2s3u1(op)                                           \
+       static inline void i##op(u32 **buf, unsigned int a,     \
+               signed int b, unsigned int c)                   \
+       {                                                       \
+               build_insn(buf, insn##op, c, a, b);             \
+       }
+
+#define I_u2u1s3(op)                                           \
+       static inline void i##op(u32 **buf, unsigned int a,     \
+               unsigned int b, signed int c)                   \
+       {                                                       \
+               build_insn(buf, insn##op, b, a, c);             \
+       }
+
+#define I_u1u2(op)                                             \
+       static inline void i##op(u32 **buf, unsigned int a,     \
+               unsigned int b)                                 \
+       {                                                       \
+               build_insn(buf, insn##op, a, b);                \
+       }
+
+#define I_u1s2(op)                                             \
+       static inline void i##op(u32 **buf, unsigned int a,     \
+               signed int b)                                   \
+       {                                                       \
+               build_insn(buf, insn##op, a, b);                \
+       }
+
+#define I_u1(op)                                               \
+       static inline void i##op(u32 **buf, unsigned int a)     \
+       {                                                       \
+               build_insn(buf, insn##op, a);                   \
+       }
+
+#define I_0(op)                                                        \
+       static inline void i##op(u32 **buf)                     \
+       {                                                       \
+               build_insn(buf, insn##op);                      \
+       }
+
+I_u2u1s3(_addiu);
+I_u3u1u2(_addu);
+I_u2u1u3(_andi);
+I_u3u1u2(_and);
+I_u1u2s3(_beq);
+I_u1s2(_bgez);
+I_u1s2(_bgezl);
+I_u1s2(_bltz);
+I_u1s2(_bltzl);
+I_u1u2s3(_bne);
+I_u1u2(_dmfc0);
+I_u1u2(_dmtc0);
+I_u2u1s3(_daddiu);
+I_u3u1u2(_daddu);
+I_u2u1u3(_dsll);
+I_u2u1u3(_dsll32);
+I_u2u1u3(_dsra);
+I_u2u1u3(_dsrl);
+I_u2u1u3(_dsrl32);
+I_u3u1u2(_dsubu);
+I_0(_eret);
+I_u1(_j);
+I_u1(_jal);
+I_u1(_jr);
+I_u2s3u1(_ld);
+I_u1s2(_lui);
+I_u2s3u1(_lw);
+I_u1u2(_mfc0);
+I_u1u2(_mtc0);
+I_u2u1u3(_ori);
+I_0(_rfe);
+I_u2s3u1(_sd);
+I_u2u1u3(_sll);
+I_u2u1u3(_sra);
+I_u2u1u3(_srl);
+I_u3u1u2(_subu);
+I_u2s3u1(_sw);
+I_0(_tlbp);
+I_0(_tlbwi);
+I_0(_tlbwr);
+I_u3u1u2(_xor)
+I_u2u1u3(_xori);
+
+/*
+ * handling labels
+ */
+
+enum label_id {
+       label_invalid,
+       label_second_part,
+       label_leave,
+       label_vmalloc,
+       label_vmalloc_done,
+       label_tlbwr_hazard,
+       label_split
+};
+
+struct label {
+       u32 *addr;
+       enum label_id lab;
+};
+
+static __init void build_label(struct label **lab, u32 *addr,
+                              enum label_id l)
+{
+       (*lab)->addr = addr;
+       (*lab)->lab = l;
+       (*lab)++;
+}
+
+#define L_LA(lb)                                               \
+       static inline void l##lb(struct label **lab, u32 *addr) \
+       {                                                       \
+               build_label(lab, addr, label##lb);              \
+       }
+
+L_LA(_second_part)
+L_LA(_leave)
+L_LA(_vmalloc)
+L_LA(_vmalloc_done)
+L_LA(_tlbwr_hazard)
+L_LA(_split)
+
+/* convenience macros for instructions */
+#ifdef CONFIG_MIPS64
+# define i_LW(buf, rs, rt, off) i_ld(buf, rs, rt, off)
+# define i_SW(buf, rs, rt, off) i_sd(buf, rs, rt, off)
+# define i_SLL(buf, rs, rt, sh) i_dsll(buf, rs, rt, sh)
+# define i_SRA(buf, rs, rt, sh) i_dsra(buf, rs, rt, sh)
+# define i_SRL(buf, rs, rt, sh) i_dsrl(buf, rs, rt, sh)
+# define i_MFC0(buf, rt, rd) i_dmfc0(buf, rt, rd)
+# define i_MTC0(buf, rt, rd) i_dmtc0(buf, rt, rd)
+# define i_ADDIU(buf, rs, rt, val) i_daddiu(buf, rs, rt, val)
+# define i_ADDU(buf, rs, rt, rd) i_daddu(buf, rs, rt, rd)
+# define i_SUBU(buf, rs, rt, rd) i_dsubu(buf, rs, rt, rd)
+#else
+# define i_LW(buf, rs, rt, off) i_lw(buf, rs, rt, off)
+# define i_SW(buf, rs, rt, off) i_sw(buf, rs, rt, off)
+# define i_SLL(buf, rs, rt, sh) i_sll(buf, rs, rt, sh)
+# define i_SRA(buf, rs, rt, sh) i_sra(buf, rs, rt, sh)
+# define i_SRL(buf, rs, rt, sh) i_srl(buf, rs, rt, sh)
+# define i_MFC0(buf, rt, rd) i_mfc0(buf, rt, rd)
+# define i_MTC0(buf, rt, rd) i_mtc0(buf, rt, rd)
+# define i_ADDIU(buf, rs, rt, val) i_addiu(buf, rs, rt, val)
+# define i_ADDU(buf, rs, rt, rd) i_addu(buf, rs, rt, rd)
+# define i_SUBU(buf, rs, rt, rd) i_subu(buf, rs, rt, rd)
+#endif
+
+#define i_b(buf, off) i_beq(buf, 0, 0, off)
+#define i_bnez(buf, rs, off) i_bne(buf, rs, 0, off)
+#define i_move(buf, a, b) i_ADDU(buf, a, 0, b)
+#define i_nop(buf) i_sll(buf, 0, 0, 0)
+#define i_ssnop(buf) i_sll(buf, 0, 0, 1)
+#define i_ehb(buf) i_sll(buf, 0, 0, 3)
+
+#if CONFIG_MIPS64
+static __init int in_compat_space_p(long addr)
+{
+       /* Is this address in 32bit compat space? */
+       return (((addr) & 0xffffffff00000000) == 0xffffffff00000000);
+}
+
+static __init int rel_highest(long val)
+{
+       return ((((val + 0x800080008000L) >> 48) & 0xffff) ^ 0x8000) - 0x8000;
+}
+
+static __init int rel_higher(long val)
+{
+       return ((((val + 0x80008000L) >> 32) & 0xffff) ^ 0x8000) - 0x8000;
+}
+#endif
+
+static __init int rel_hi(long val)
+{
+       return ((((val + 0x8000L) >> 16) & 0xffff) ^ 0x8000) - 0x8000;
+}
+
+static __init int rel_lo(long val)
+{
+       return ((val & 0xffff) ^ 0x8000) - 0x8000;
+}
+
+static __init void i_LA_mostly(u32 **buf, unsigned int rs, long addr)
+{
+#if CONFIG_MIPS64
+       if (!in_compat_space_p(addr)) {
+               i_lui(buf, rs, rel_highest(addr));
+               if (rel_higher(addr))
+                       i_daddiu(buf, rs, rs, rel_higher(addr));
+               if (rel_hi(addr)) {
+                       i_dsll(buf, rs, rs, 16);
+                       i_daddiu(buf, rs, rs, rel_hi(addr));
+                       i_dsll(buf, rs, rs, 16);
+               } else
+                       i_dsll32(buf, rs, rs, 0);
+       } else
+#endif
+               i_lui(buf, rs, rel_hi(addr));
+}
+
+static __init void __attribute__((unused)) i_LA(u32 **buf, unsigned int rs,
+                                               long addr)
+{
+       i_LA_mostly(buf, rs, addr);
+       if (rel_lo(addr))
+               i_ADDIU(buf, rs, rs, rel_lo(addr));
+}
+
+/*
+ * handle relocations
+ */
+
+struct reloc {
+       u32 *addr;
+       unsigned int type;
+       enum label_id lab;
+};
+
+static __init void r_mips_pc16(struct reloc **rel, u32 *addr,
+                              enum label_id l)
+{
+       (*rel)->addr = addr;
+       (*rel)->type = R_MIPS_PC16;
+       (*rel)->lab = l;
+       (*rel)++;
+}
+
+static inline void __resolve_relocs(struct reloc *rel, struct label *lab)
+{
+       long laddr = (long)lab->addr;
+       long raddr = (long)rel->addr;
+
+       switch (rel->type) {
+       case R_MIPS_PC16:
+               *rel->addr |= build_bimm(laddr - (raddr + 4));
+               break;
+
+       default:
+               panic("Unsupported TLB synthesizer relocation %d",
+                     rel->type);
+       }
+}
+
+static __init void resolve_relocs(struct reloc *rel, struct label *lab)
+{
+       struct label *l;
+
+       for (; rel->lab != label_invalid; rel++)
+               for (l = lab; l->lab != label_invalid; l++)
+                       if (rel->lab == l->lab)
+                               __resolve_relocs(rel, l);
+}
+
+static __init void copy_handler(struct reloc *rel, struct label *lab,
+                               u32 *first, u32 *end, u32* target)
+{
+       long off = (long)(target - first);
+
+       memcpy(target, first, (end - first) * sizeof(u32));
+
+       for (; rel->lab != label_invalid; rel++)
+               if (rel->addr >= first && rel->addr < end)
+                       rel->addr += off;
+
+       for (; lab->lab != label_invalid; lab++)
+               if (lab->addr >= first && lab->addr < end)
+                       lab->addr += off;
+}
+
+static __init int __attribute__((unused)) insn_has_bdelay(struct reloc *rel,
+                                                         u32 *addr)
+{
+       for (; rel->lab != label_invalid; rel++) {
+               if (rel->addr == addr
+                   && (rel->type == R_MIPS_PC16
+                       || rel->type == R_MIPS_26))
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* convenience functions for labeled branches */
+static void __attribute__((unused)) il_bltz(u32 **p, struct reloc **r,
+                                           unsigned int reg, enum label_id l)
+{
+       r_mips_pc16(r, *p, l);
+       i_bltz(p, reg, 0);
+}
+
+static void __attribute__((unused)) il_b(u32 **p, struct reloc **r,
+                                        enum label_id l)
+{
+       r_mips_pc16(r, *p, l);
+       i_b(p, 0);
+}
+
+static void il_bnez(u32 **p, struct reloc **r, unsigned int reg,
+                   enum label_id l)
+{
+       r_mips_pc16(r, *p, l);
+       i_bnez(p, reg, 0);
+}
+
+static void il_bgezl(u32 **p, struct reloc **r, unsigned int reg,
+                    enum label_id l)
+{
+       r_mips_pc16(r, *p, l);
+       i_bgezl(p, reg, 0);
+}
+
+/* The only registers allowed in TLB handlers. */
+#define K0             26
+#define K1             27
+
+/* Some CP0 registers */
+#define C0_INDEX       0
+#define C0_ENTRYLO0    2
+#define C0_ENTRYLO1    3
+#define C0_CONTEXT     4
+#define C0_BADVADDR    8
+#define C0_ENTRYHI     10
+#define C0_EPC         14
+#define C0_XCONTEXT    20
+
+#ifdef CONFIG_MIPS64
+# define GET_CONTEXT(buf, reg) i_MFC0(buf, reg, C0_XCONTEXT)
+#else
+# define GET_CONTEXT(buf, reg) i_MFC0(buf, reg, C0_CONTEXT)
+#endif
+
+/* The worst case length of the handler is around 18 instructions for
+ * R3000-style TLBs and up to 63 instructions for R4000-style TLBs.
+ * Maximum space available is 32 instructions for R3000 and 64
+ * instructions for R4000.
+ *
+ * We deliberately chose a buffer size of 128, so we won't scribble
+ * over anything important on overflow before we panic.
+ */
+static __initdata u32 tlb_handler[128];
+
+/* simply assume worst case size for labels and relocs */
+static __initdata struct label labels[128];
+static __initdata struct reloc relocs[128];
+
+#ifdef CONFIG_MIPS32
+/*
+ * The R3000 TLB handler is simple.
+ */
+static void __init build_r3000_tlb_refill_handler(void)
+{
+       long pgdc = (long)pgd_current;
+       u32 *p;
+
+       memset(tlb_handler, 0, sizeof(tlb_handler));
+       p = tlb_handler;
+
+       i_mfc0(&p, K0, C0_BADVADDR);
+       i_lui(&p, K1, rel_hi(pgdc)); /* cp0 delay */
+       i_lw(&p, K1, rel_lo(pgdc), K1);
+       i_srl(&p, K0, K0, 22); /* load delay */
+       i_sll(&p, K0, K0, 2);
+       i_addu(&p, K1, K1, K0);
+       i_mfc0(&p, K0, C0_CONTEXT);
+       i_lw(&p, K1, 0, K1); /* cp0 delay */
+       i_andi(&p, K0, K0, 0xffc); /* load delay */
+       i_addu(&p, K1, K1, K0);
+       i_lw(&p, K0, 0, K1);
+       i_nop(&p); /* load delay */
+       i_mtc0(&p, K0, C0_ENTRYLO0);
+       i_mfc0(&p, K1, C0_EPC); /* cp0 delay */
+       i_tlbwr(&p); /* cp0 delay */
+       i_jr(&p, K1);
+       i_rfe(&p); /* branch delay */
+
+       if (p > tlb_handler + 32)
+               panic("TLB refill handler space exceeded");
+
+       printk("Synthesized TLB handler (%u instructions).\n",
+              p - tlb_handler);
+#ifdef DEBUG_TLB
+       {
+               int i;
+               for (i = 0; i < (p - tlb_handler); i++)
+                       printk("%08x\n", tlb_handler[i]);
+       }
+#endif
+
+       memcpy((void *)CAC_BASE, tlb_handler, 0x80);
+       flush_icache_range(CAC_BASE, CAC_BASE + 0x80);
+}
+#endif /* CONFIG_MIPS32 */
+
+/*
+ * The R4000 TLB handler is much more complicated. We have two
+ * consecutive handler areas with 32 instructions space each.
+ * Since they aren't used at the same time, we can overflow in the
+ * other one.To keep things simple, we first assume linear space,
+ * then we relocate it to the final handler layout as needed.
+ */
+static __initdata u32 final_handler[64];
+
+/*
+ * Hazards
+ *
+ * From the IDT errata for the QED RM5230 (Nevada), processor revision 1.0:
+ * 2. A timing hazard exists for the TLBP instruction.
+ *
+ *      stalling_instruction
+ *      TLBP
+ *
+ * The JTLB is being read for the TLBP throughout the stall generated by the
+ * previous instruction. This is not really correct as the stalling instruction
+ * can modify the address used to access the JTLB.  The failure symptom is that
+ * the TLBP instruction will use an address created for the stalling instruction
+ * and not the address held in C0_ENHI and thus report the wrong results.
+ *
+ * The software work-around is to not allow the instruction preceding the TLBP
+ * to stall - make it an NOP or some other instruction guaranteed not to stall.
+ *
+ * Errata 2 will not be fixed.  This errata is also on the R5000.
+ *
+ * As if we MIPS hackers wouldn't know how to nop pipelines happy ...
+ */
+static __init void __attribute__((unused)) build_tlb_probe_entry(u32 **p)
+{
+       switch (current_cpu_data.cputype) {
+       case CPU_R5000:
+       case CPU_R5000A:
+       case CPU_NEVADA:
+               i_nop(p);
+               i_tlbp(p);
+               break;
+
+       default:
+               i_tlbp(p);
+               break;
+       }
+}
+
+/*
+ * Write random TLB entry, and care about the hazards from the
+ * preceeding mtc0 and for the following eret.
+ */
+static __init void build_tlb_write_random_entry(u32 **p, struct label **l,
+                                               struct reloc **r)
+{
+       switch (current_cpu_data.cputype) {
+       case CPU_R4000PC:
+       case CPU_R4000SC:
+       case CPU_R4000MC:
+       case CPU_R4400PC:
+       case CPU_R4400SC:
+       case CPU_R4400MC:
+               /*
+                * This branch uses up a mtc0 hazard nop slot and saves
+                * two nops after the tlbwr.
+                */
+               il_bgezl(p, r, 0, label_tlbwr_hazard);
+               i_tlbwr(p);
+               l_tlbwr_hazard(l, *p);
+               i_nop(p);
+               break;
+
+       case CPU_R4600:
+       case CPU_R4700:
+       case CPU_R5000:
+       case CPU_R5000A:
+       case CPU_5KC:
+       case CPU_AU1000:
+       case CPU_AU1100:
+       case CPU_AU1500:
+       case CPU_AU1550:
+               i_nop(p);
+               i_tlbwr(p);
+               break;
+
+       case CPU_R10000:
+       case CPU_R12000:
+       case CPU_4KC:
+       case CPU_SB1:
+       case CPU_4KSC:
+       case CPU_20KC:
+       case CPU_25KF:
+               i_tlbwr(p);
+               break;
+
+       case CPU_NEVADA:
+               i_nop(p); /* QED specifies 2 nops hazard */
+               /*
+                * This branch uses up a mtc0 hazard nop slot and saves
+                * a nop after the tlbwr.
+                */
+               il_bgezl(p, r, 0, label_tlbwr_hazard);
+               i_tlbwr(p);
+               l_tlbwr_hazard(l, *p);
+               break;
+
+       case CPU_4KEC:
+       case CPU_24K:
+               i_ehb(p);
+               i_tlbwr(p);
+               break;
+
+       case CPU_RM9000:
+               /*
+                * When the JTLB is updated by tlbwi or tlbwr, a subsequent
+                * use of the JTLB for instructions should not occur for 4
+                * cpu cycles and use for data translations should not occur
+                * for 3 cpu cycles.
+                */
+               i_ssnop(p);
+               i_ssnop(p);
+               i_ssnop(p);
+               i_ssnop(p);
+               i_tlbwr(p);
+               i_ssnop(p);
+               i_ssnop(p);
+               i_ssnop(p);
+               i_ssnop(p);
+               break;
+
+       default:
+               panic("No TLB refill handler yet (CPU type: %d)",
+                     current_cpu_data.cputype);
+               break;
+       }
+}
+
+#if CONFIG_MIPS64
+/*
+ * TMP and PTR are scratch.
+ * TMP will be clobbered, PTR will hold the pmd entry.
+ */
+static __init void
+build_get_pmde64(u32 **p, struct label **l, struct reloc **r,
+                unsigned int tmp, unsigned int ptr)
+{
+       long pgdc = (long)pgd_current;
+
+       /*
+        * The vmalloc handling is not in the hotpath.
+        */
+       i_dmfc0(p, tmp, C0_BADVADDR);
+       il_bltz(p, r, tmp, label_vmalloc);
+       /* No i_nop needed here, since the next insn doesn't touch TMP. */
+
+# ifdef CONFIG_SMP
+       /*
+        * 64 bit SMP has the lower part of &pgd_current[smp_processor_id()]
+        * stored in CONTEXT.
+        */
+       if (in_compat_space_p(pgdc)) {
+               i_dmfc0(p, ptr, C0_CONTEXT);
+               i_dsra(p, ptr, ptr, 23);
+       } else {
+               i_dmfc0(p, ptr, C0_CONTEXT);
+               i_lui(p, tmp, rel_highest(pgdc));
+               i_dsll(p, ptr, ptr, 9);
+               i_daddiu(p, tmp, tmp, rel_higher(pgdc));
+               i_dsrl32(p, ptr, ptr, 0);
+               i_and(p, ptr, ptr, tmp);
+               i_dmfc0(p, tmp, C0_BADVADDR);
+       }
+       i_ld(p, ptr, 0, ptr);
+# else
+       i_LA_mostly(p, ptr, pgdc);
+       i_ld(p, ptr, rel_lo(pgdc), ptr);
+# endif
+
+       l_vmalloc_done(l, *p);
+       i_dsrl(p, tmp, tmp, PGDIR_SHIFT-3); /* get pgd offset in bytes */
+       i_andi(p, tmp, tmp, (PTRS_PER_PGD - 1)<<3);
+       i_daddu(p, ptr, ptr, tmp); /* add in pgd offset */
+       i_dmfc0(p, tmp, C0_BADVADDR); /* get faulting address */
+       i_ld(p, ptr, 0, ptr); /* get pmd pointer */
+       i_dsrl(p, tmp, tmp, PMD_SHIFT-3); /* get pmd offset in bytes */
+       i_andi(p, tmp, tmp, (PTRS_PER_PMD - 1)<<3);
+       i_daddu(p, ptr, ptr, tmp); /* add in pmd offset */
+}
+
+/*
+ * BVADDR is the faulting address, PTR is scratch.
+ * PTR will hold the pgd for vmalloc.
+ */
+static __init void
+build_get_pgd_vmalloc64(u32 **p, struct label **l, struct reloc **r,
+                       unsigned int bvaddr, unsigned int ptr)
+{
+       long swpd = (long)swapper_pg_dir;
+
+       l_vmalloc(l, *p);
+       i_LA(p, ptr, VMALLOC_START);
+       i_dsubu(p, bvaddr, bvaddr, ptr);
+
+       if (in_compat_space_p(swpd) && !rel_lo(swpd)) {
+               il_b(p, r, label_vmalloc_done);
+               i_lui(p, ptr, rel_hi(swpd));
+       } else {
+               i_LA_mostly(p, ptr, swpd);
+               il_b(p, r, label_vmalloc_done);
+               i_daddiu(p, ptr, ptr, rel_lo(swpd));
+       }
+}
+
+#else /* CONFIG_MIPS32 */
+
+/*
+ * TMP and PTR are scratch.
+ * TMP will be clobbered, PTR will hold the pgd entry.
+ */
+static __init void build_get_pgde32(u32 **p, unsigned int tmp, unsigned int ptr)
+{
+       long pgdc = (long)pgd_current;
+
+       /* 32 bit SMP has smp_processor_id() stored in CONTEXT. */
+#ifdef CONFIG_SMP
+       i_mfc0(p, ptr, C0_CONTEXT);
+       i_LA_mostly(p, tmp, pgdc);
+       i_srl(p, ptr, ptr, 23);
+       i_sll(p, ptr, ptr, 2);
+       i_addu(p, ptr, tmp, ptr);
+#else
+       i_LA_mostly(p, ptr, pgdc);
+#endif
+       i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */
+       i_lw(p, ptr, rel_lo(pgdc), ptr);
+       i_srl(p, tmp, tmp, PGDIR_SHIFT); /* get pgd only bits */
+       i_sll(p, tmp, tmp, PGD_T_LOG2);
+       i_addu(p, ptr, ptr, tmp); /* add in pgd offset */
+}
+#endif /* CONFIG_MIPS32 */
+
+static __init void build_adjust_context(u32 **p, unsigned int ctx)
+{
+       unsigned int shift = 0;
+       unsigned int mask = 0xff0;
+
+#if !defined(CONFIG_MIPS64) && !defined(CONFIG_64BIT_PHYS_ADDR)
+       shift++;
+       mask |= 0x008;
+#endif
+
+       switch (current_cpu_data.cputype) {
+       case CPU_VR41XX:
+       case CPU_VR4111:
+       case CPU_VR4121:
+       case CPU_VR4122:
+       case CPU_VR4131:
+       case CPU_VR4181:
+       case CPU_VR4181A:
+       case CPU_VR4133:
+               shift += 2;
+               break;
+
+       default:
+               break;
+       }
+
+       if (shift)
+               i_SRL(p, ctx, ctx, shift);
+       i_andi(p, ctx, ctx, mask);
+}
+
+static __init void build_get_ptep(u32 **p, unsigned int tmp, unsigned int ptr)
+{
+       /*
+        * Bug workaround for the Nevada. It seems as if under certain
+        * circumstances the move from cp0_context might produce a
+        * bogus result when the mfc0 instruction and its consumer are
+        * in a different cacheline or a load instruction, probably any
+        * memory reference, is between them.
+        */
+       switch (current_cpu_data.cputype) {
+       case CPU_NEVADA:
+               i_LW(p, ptr, 0, ptr);
+               GET_CONTEXT(p, tmp); /* get context reg */
+               break;
+
+       default:
+               GET_CONTEXT(p, tmp); /* get context reg */
+               i_LW(p, ptr, 0, ptr);
+               break;
+       }
+
+       build_adjust_context(p, tmp);
+       i_ADDU(p, ptr, ptr, tmp); /* add in offset */
+}
+
+static __init void build_update_entries(u32 **p, unsigned int tmp,
+                                       unsigned int ptep)
+{
+       /*
+        * 64bit address support (36bit on a 32bit CPU) in a 32bit
+        * Kernel is a special case. Only a few CPUs use it.
+        */
+#ifdef CONFIG_64BIT_PHYS_ADDR
+       if (cpu_has_64bit_gp_regs) {
+               i_ld(p, tmp, 0, ptep); /* get even pte */
+               i_ld(p, ptep, sizeof(pte_t), ptep); /* get odd pte */
+               i_dsrl(p, tmp, tmp, 6); /* convert to entrylo0 */
+               i_mtc0(p, tmp, C0_ENTRYLO0); /* load it */
+               i_dsrl(p, ptep, ptep, 6); /* convert to entrylo1 */
+               i_mtc0(p, ptep, C0_ENTRYLO1); /* load it */
+       } else {
+               int pte_off_even = sizeof(pte_t) / 2;
+               int pte_off_odd = pte_off_even + sizeof(pte_t);
+
+               /* The pte entries are pre-shifted */
+               i_lw(p, tmp, pte_off_even, ptep); /* get even pte */
+               i_mtc0(p, tmp, C0_ENTRYLO0); /* load it */
+               i_lw(p, ptep, pte_off_odd, ptep); /* get odd pte */
+               i_mtc0(p, ptep, C0_ENTRYLO1); /* load it */
+       }
+#else
+       i_LW(p, tmp, 0, ptep); /* get even pte */
+       i_LW(p, ptep, sizeof(pte_t), ptep); /* get odd pte */
+       if (r45k_bvahwbug())
+               build_tlb_probe_entry(p);
+       i_SRL(p, tmp, tmp, 6); /* convert to entrylo0 */
+       if (r4k_250MHZhwbug())
+               i_mtc0(p, 0, C0_ENTRYLO0);
+       i_mtc0(p, tmp, C0_ENTRYLO0); /* load it */
+       i_SRL(p, ptep, ptep, 6); /* convert to entrylo1 */
+       if (r45k_bvahwbug())
+               i_mfc0(p, tmp, C0_INDEX);
+       if (r4k_250MHZhwbug())
+               i_mtc0(p, 0, C0_ENTRYLO1);
+       i_mtc0(p, ptep, C0_ENTRYLO1); /* load it */
+#endif
+}
+
+static void __init build_r4000_tlb_refill_handler(void)
+{
+       u32 *p = tlb_handler;
+       struct label *l = labels;
+       struct reloc *r = relocs;
+       u32 *f;
+       unsigned int final_len;
+
+       memset(tlb_handler, 0, sizeof(tlb_handler));
+       memset(labels, 0, sizeof(labels));
+       memset(relocs, 0, sizeof(relocs));
+       memset(final_handler, 0, sizeof(final_handler));
+
+       /*
+        * create the plain linear handler
+        */
+       if (bcm1250_m3_war()) {
+               i_MFC0(&p, K0, C0_BADVADDR);
+               i_MFC0(&p, K1, C0_ENTRYHI);
+               i_xor(&p, K0, K0, K1);
+               i_SRL(&p, K0, K0, PAGE_SHIFT+1);
+               il_bnez(&p, &r, K0, label_leave);
+               /* No need for i_nop */
+       }
+
+#ifdef CONFIG_MIPS64
+       build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd ptr in K1 */
+#else
+       build_get_pgde32(&p, K0, K1); /* get pgd ptr in K1 */
+#endif
+
+       build_get_ptep(&p, K0, K1);
+       build_update_entries(&p, K0, K1);
+       build_tlb_write_random_entry(&p, &l, &r);
+       l_leave(&l, p);
+       i_eret(&p); /* return from trap */
+
+#ifdef CONFIG_MIPS64
+       build_get_pgd_vmalloc64(&p, &l, &r, K0, K1);
+#endif
+
+       /*
+        * Overflow check: For the 64bit handler, we need at least one
+        * free instruction slot for the wrap-around branch. In worst
+        * case, if the intended insertion point is a delay slot, we
+        * need three, with the the second nop'ed and the third being
+        * unused.
+        */
+#ifdef CONFIG_MIPS32
+       if ((p - tlb_handler) > 64)
+               panic("TLB refill handler space exceeded");
+#else
+       if (((p - tlb_handler) > 63)
+           || (((p - tlb_handler) > 61)
+               && insn_has_bdelay(relocs, tlb_handler + 29)))
+               panic("TLB refill handler space exceeded");
+#endif
+
+       /*
+        * Now fold the handler in the TLB refill handler space.
+        */
+#ifdef CONFIG_MIPS32
+       f = final_handler;
+       /* Simplest case, just copy the handler. */
+       copy_handler(relocs, labels, tlb_handler, p, f);
+       final_len = p - tlb_handler;
+#else /* CONFIG_MIPS64 */
+       f = final_handler + 32;
+       if ((p - tlb_handler) <= 32) {
+               /* Just copy the handler. */
+               copy_handler(relocs, labels, tlb_handler, p, f);
+               final_len = p - tlb_handler;
+       } else {
+               u32 *split = tlb_handler + 30;
+
+               /*
+                * Find the split point.
+                */
+               if (insn_has_bdelay(relocs, split - 1))
+                       split--;
+
+               /* Copy first part of the handler. */
+               copy_handler(relocs, labels, tlb_handler, split, f);
+               f += split - tlb_handler;
+
+               /* Insert branch. */
+               l_split(&l, final_handler);
+               il_b(&f, &r, label_split);
+               if (insn_has_bdelay(relocs, split))
+                       i_nop(&f);
+               else {
+                       copy_handler(relocs, labels, split, split + 1, f);
+                       f++;
+                       split++;
+               }
+
+               /* Copy the rest of the handler. */
+               copy_handler(relocs, labels, split, p, final_handler);
+               final_len = (f - (final_handler + 32)) + (p - split);
+       }
+#endif /* CONFIG_MIPS64 */
+
+       resolve_relocs(relocs, labels);
+       printk("Synthesized TLB handler (%u instructions).\n", final_len);
+
+#ifdef DEBUG_TLB
+       {
+               int i;
+
+               for (i = 0; i < 64; i++)
+                       printk("%08x\n", final_handler[i]);
+       }
+#endif
+
+       memcpy((void *)CAC_BASE, final_handler, 0x100);
+       flush_icache_range(CAC_BASE, CAC_BASE + 0x100);
+}
+
+void __init build_tlb_refill_handler(void)
+{
+       switch (current_cpu_data.cputype) {
+#ifdef CONFIG_MIPS32
+       case CPU_R2000:
+       case CPU_R3000:
+       case CPU_R3000A:
+       case CPU_R3081E:
+       case CPU_TX3912:
+       case CPU_TX3922:
+       case CPU_TX3927:
+               build_r3000_tlb_refill_handler();
+               break;
+
+       case CPU_R6000:
+       case CPU_R6000A:
+               panic("No R6000 TLB refill handler yet");
+               break;
+#endif
+
+       case CPU_R8000:
+               panic("No R8000 TLB refill handler yet");
+               break;
+
+       default:
+               build_r4000_tlb_refill_handler();
+       }
+}
diff --git a/arch/mips/momentum/ocelot_3/Makefile b/arch/mips/momentum/ocelot_3/Makefile
new file mode 100644 (file)
index 0000000..aab8fd8
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for Momentum Computer's Ocelot-3 board.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+obj-y   += int-handler.o irq.o prom.o reset.o setup.o
diff --git a/arch/mips/momentum/ocelot_3/int-handler.S b/arch/mips/momentum/ocelot_3/int-handler.S
new file mode 100644 (file)
index 0000000..4522f09
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2002 Momentum Computer Inc.
+ * Author: Matthew Dharm <mdharm@momenco.com>
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: jsun@mvista.com or jsun@junsun.net
+ *
+ * Copyright 2004 PMC-Sierra
+ * Author: Manish Lachwani (lachwani@pmc-sierra.com)
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com
+ *
+ * First-level interrupt dispatcher for Ocelot-3 board.
+ *
+ * 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 <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+/*
+ * First level interrupt dispatcher for Ocelot-3 board
+ */
+               .align  5
+               NESTED(ocelot3_handle_int, PT_SIZE, sp)
+               SAVE_ALL
+               CLI
+               .set    at
+
+               mfc0    t0, CP0_CAUSE
+               mfc0    t2, CP0_STATUS
+
+               and     t0, t2
+
+               andi    t1, t0, STATUSF_IP0     /* sw0 software interrupt (IRQ0) */
+               bnez    t1, ll_sw0_irq
+
+               andi    t1, t0, STATUSF_IP1     /* sw1 software interrupt (IRQ1) */
+               bnez    t1, ll_sw1_irq
+
+               andi    t1, t0, STATUSF_IP2     /* int0 hardware line (IRQ2) */
+               bnez    t1, ll_pci0slot1_irq
+
+               andi    t1, t0, STATUSF_IP3     /* int1 hardware line (IRQ3) */
+               bnez    t1, ll_pci0slot2_irq
+
+               andi    t1, t0, STATUSF_IP4     /* int2 hardware line (IRQ4) */
+               bnez    t1, ll_pci1slot1_irq
+
+               andi    t1, t0, STATUSF_IP5     /* int3 hardware line (IRQ5) */
+               bnez    t1, ll_pci1slot2_irq
+
+               andi    t1, t0, STATUSF_IP6     /* int4 hardware line (IRQ6) */
+               bnez    t1, ll_uart_irq
+
+               andi    t1, t0, STATUSF_IP7     /* cpu timer (IRQ7) */
+               bnez    t1, ll_cputimer_irq
+
+                /* now look at extended interrupts */
+                mfc0    t0, CP0_CAUSE
+                cfc0    t1, CP0_S1_INTCONTROL
+
+                /* shift the mask 8 bits left to line up the bits */
+                sll     t2, t1, 8
+
+                and     t0, t2
+                srl     t0, t0, 16
+
+                andi    t1, t0, STATUSF_IP8     /* int6 hardware line (IRQ9) */
+                bnez    t1, ll_mv64340_decode_irq
+
+               .set    reorder
+
+               /* wrong alarm or masked ... */
+               j       spurious_interrupt
+               nop
+               END(ocelot3_handle_int)
+
+               .align  5
+ll_sw0_irq:
+               li      a0, 0           /* IRQ 1 */
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+ll_sw1_irq:
+               li      a0, 1           /* IRQ 2 */
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+
+ll_pci0slot1_irq:
+               li      a0, 2           /* IRQ 3 */
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+
+ll_pci0slot2_irq:
+               li      a0, 3           /* IRQ 4 */
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+
+ll_pci1slot1_irq:
+               li      a0, 4           /* IRQ 5 */
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+
+ll_pci1slot2_irq:
+               li      a0, 5           /* IRQ 6 */
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+
+ll_uart_irq:
+               li      a0, 6           /* IRQ 7 */
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+
+ll_cputimer_irq:
+               li      a0, 7           /* IRQ 8 */
+               move    a1, sp
+               jal     do_IRQ
+               j       ret_from_irq
+
+ll_mv64340_decode_irq:
+               move    a0, sp
+               jal     ll_mv64340_irq
+               j       ret_from_irq
+
diff --git a/arch/mips/momentum/ocelot_3/irq.c b/arch/mips/momentum/ocelot_3/irq.c
new file mode 100644 (file)
index 0000000..42464db
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2000 RidgeRun, Inc.
+ * Author: RidgeRun, Inc.
+ *   glonnon@ridgerun.com, skranz@ridgerun.com, stevej@ridgerun.com
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
+ * Copyright (C) 2000, 2001 Ralf Baechle (ralf@gnu.org)
+ *
+ * Copyright 2004 PMC-Sierra
+ * Author: Manish Lachwani (lachwani@pmc-sierra.com)
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  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.
+ *
+ *  Copyright (C) 2004 MontaVista Software Inc.
+ *  Author: Manish Lachwani, mlachwani@mvista.com
+ *
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <asm/bitops.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mipsregs.h>
+#include <asm/system.h>
+
+extern asmlinkage void ocelot3_handle_int(void);
+
+static struct irqaction cascade_mv64340 = {
+       no_action, SA_INTERRUPT, CPU_MASK_NONE, "MV64340-Cascade", NULL, NULL
+};
+
+void __init arch_init_irq(void)
+{
+       /*
+        * Clear all of the interrupts while we change the able around a bit.
+        * int-handler is not on bootstrap
+        */
+       clear_c0_status(ST0_IM | ST0_BEV);
+
+       /* Sets the first-level interrupt dispatcher. */
+       set_except_vector(0, ocelot3_handle_int);
+       mips_cpu_irq_init(0);
+       rm7k_cpu_irq_init(8);
+
+       /* set up the cascading interrupts */
+       setup_irq(8, &cascade_mv64340);         /* unmask intControl IM8, IRQ 9 */
+       mv64340_irq_init(16);
+
+       set_c0_status(ST0_IM); /* IE in the status register */
+
+}
diff --git a/arch/mips/momentum/ocelot_3/ocelot_3_fpga.h b/arch/mips/momentum/ocelot_3/ocelot_3_fpga.h
new file mode 100644 (file)
index 0000000..227e429
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Ocelot-3 Board Register Definitions
+ *
+ * (C) 2002 Momentum Computer 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.
+ *
+ *  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.
+ *
+ *  Louis Hamilton, Red Hat, Inc.
+ *    hamilton@redhat.com  [MIPS64 modifications]
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com
+ */
+
+#ifndef __OCELOT_3_FPGA_H__
+#define __OCELOT_3_FPGA_H__
+
+#define OCELOT_3_REG_BOARDREV          0x0
+#define OCELOT_3_REG_FPGA_REV          0x1
+#define OCELOT_3_REG_FPGA_TYPE         0x2
+#define OCELOT_3_REG_RESET_STATUS      0x3
+#define OCELOT_3_REG_BOARD_STATUS      0x4
+#define OCELOT_3_REG_CPCI_ID           0x5
+#define OCELOT_3_REG_SET               0x6
+#define OCELOT_3_REG_CLR               0x7
+#define OCELOT_3_REG_EEPROM_MODE       0x9
+#define OCELOT_3_REG_INTMASK           0xa
+#define OCELOT_3_REG_INTSTAT           0xb
+#define OCELOT_3_REG_UART_INTMASK      0xc
+#define OCELOT_3_REG_UART_INTSTAT      0xd
+#define OCELOT_3_REG_INTSET            0xe
+#define OCELOT_3_REG_INTCLR            0xf
+
+extern unsigned long ocelot_fpga_base;
+
+#define OCELOT_FPGA_WRITE(x, y) writeb(x, ocelot_fpga_base + OCELOT_3_REG_##y)
+#define OCELOT_FPGA_READ(x) readb(ocelot_fpga_base + OCELOT_3_REG_##x)
+
+#endif
diff --git a/arch/mips/momentum/ocelot_3/prom.c b/arch/mips/momentum/ocelot_3/prom.c
new file mode 100644 (file)
index 0000000..89c17a0
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2002 Momentum Computer Inc.
+ * Author: Matthew Dharm <mdharm@momenco.com>
+ *
+ * Louis Hamilton, Red Hat, Inc.
+ *   hamilton@redhat.com  [MIPS64 modifications]
+ *
+ * Copyright 2004 PMC-Sierra
+ * Author: Manish Lachwani (lachwani@pmc-sierra.com)
+ *
+ * Based on Ocelot Linux port, which is
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: jsun@mvista.com or jsun@junsun.net
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com
+ *
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/mv643xx.h>
+
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+#include <asm/pmon.h>
+#include "ocelot_3_fpga.h"
+
+struct callvectors* debug_vectors;
+extern unsigned long marvell_base;
+extern unsigned long cpu_clock;
+
+#ifdef CONFIG_MV643XX_ETH
+extern unsigned char prom_mac_addr_base[6];
+#endif
+
+const char *get_system_type(void)
+{
+       return "Momentum Ocelot-3";
+}
+
+#ifdef CONFIG_MV643XX_ETH
+void burn_clocks(void)
+{
+       int i;
+
+       /* this loop should burn at least 1us -- this should be plenty */
+       for (i = 0; i < 0x10000; i++)
+               ;
+}
+
+u8 exchange_bit(u8 val, u8 cs)
+{
+       /* place the data */
+       OCELOT_FPGA_WRITE((val << 2) | cs, EEPROM_MODE);
+       burn_clocks();
+
+       /* turn the clock on */
+       OCELOT_FPGA_WRITE((val << 2) | cs | 0x2, EEPROM_MODE);
+       burn_clocks();
+
+       /* turn the clock off and read-strobe */
+       OCELOT_FPGA_WRITE((val << 2) | cs | 0x10, EEPROM_MODE);
+
+       /* return the data */
+       return ((OCELOT_FPGA_READ(EEPROM_MODE) >> 3) & 0x1);
+}
+
+void get_mac(char dest[6])
+{
+       u8 read_opcode[12] = {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+       int i,j;
+
+       for (i = 0; i < 12; i++)
+               exchange_bit(read_opcode[i], 1);
+
+       for (j = 0; j < 6; j++) {
+               dest[j] = 0;
+               for (i = 0; i < 8; i++) {
+                       dest[j] <<= 1;
+                       dest[j] |= exchange_bit(0, 1);
+               }
+       }
+
+       /* turn off CS */
+       exchange_bit(0,0);
+}
+#endif
+
+
+#ifdef CONFIG_MIPS64
+
+unsigned long signext(unsigned long addr)
+{
+       addr &= 0xffffffff;
+       return (unsigned long)((int)addr);
+}
+
+void *get_arg(unsigned long args, int arc)
+{
+       unsigned long ul;
+       unsigned char *puc, uc;
+
+       args += (arc * 4);
+       ul = (unsigned long)signext(args);
+       puc = (unsigned char *)ul;
+       if (puc == 0)
+               return (void *)0;
+
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+       uc = *puc++;
+       ul = (unsigned long)uc;
+       uc = *puc++;
+       ul |= (((unsigned long)uc) << 8);
+       uc = *puc++;
+       ul |= (((unsigned long)uc) << 16);
+       uc = *puc++;
+       ul |= (((unsigned long)uc) << 24);
+#else  /* CONFIG_CPU_LITTLE_ENDIAN */
+       uc = *puc++;
+       ul = ((unsigned long)uc) << 24;
+       uc = *puc++;
+       ul |= (((unsigned long)uc) << 16);
+       uc = *puc++;
+       ul |= (((unsigned long)uc) << 8);
+       uc = *puc++;
+       ul |= ((unsigned long)uc);
+#endif  /* CONFIG_CPU_LITTLE_ENDIAN */
+       ul = signext(ul);
+       return (void *)ul;
+}
+
+char *arg64(unsigned long addrin, int arg_index)
+{
+       unsigned long args;
+       char *p;
+
+       args = signext(addrin);
+       p = (char *)get_arg(args, arg_index);
+
+       return p;
+}
+#endif  /* CONFIG_MIPS64 */
+
+void __init prom_init(void)
+{
+       int argc = fw_arg0;
+       char **arg = (char **) fw_arg1;
+       char **env = (char **) fw_arg2;
+       struct callvectors *cv = (struct callvectors *) fw_arg3;
+       int i;
+
+#ifdef CONFIG_MIPS64
+       char *ptr;
+       printk("prom_init - MIPS64\n");
+
+       /* save the PROM vectors for debugging use */
+       debug_vectors = (struct callvectors *)signext((unsigned long)cv);
+
+       /* arg[0] is "g", the rest is boot parameters */
+       arcs_cmdline[0] = '\0';
+
+       for (i = 1; i < argc; i++) {
+               ptr = (char *)arg64((unsigned long)arg, i);
+               if ((strlen(arcs_cmdline) + strlen(ptr) + 1) >=
+                   sizeof(arcs_cmdline))
+                       break;
+               strcat(arcs_cmdline, ptr);
+               strcat(arcs_cmdline, " ");
+       }
+       i = 0;
+
+       while (1) {
+               ptr = (char *)arg64((unsigned long)env, i);
+               if (! ptr)
+                       break;
+
+               if (strncmp("gtbase", ptr, strlen("gtbase")) == 0) {
+                       marvell_base = simple_strtol(ptr + strlen("gtbase="),
+                                                       NULL, 16);
+
+                       if ((marvell_base & 0xffffffff00000000) == 0)
+                               marvell_base |= 0xffffffff00000000;
+
+                       printk("marvell_base set to 0x%016lx\n", marvell_base);
+               }
+               if (strncmp("cpuclock", ptr, strlen("cpuclock")) == 0) {
+                       cpu_clock = simple_strtol(ptr + strlen("cpuclock="),
+                                                       NULL, 10);
+                       printk("cpu_clock set to %d\n", cpu_clock);
+               }
+               i++;
+       }
+       printk("arcs_cmdline: %s\n", arcs_cmdline);
+
+#else   /* CONFIG_MIPS64 */
+
+       /* save the PROM vectors for debugging use */
+       debug_vectors = cv;
+
+       /* arg[0] is "g", the rest is boot parameters */
+       arcs_cmdline[0] = '\0';
+       for (i = 1; i < argc; i++) {
+               if (strlen(arcs_cmdline) + strlen(arg[i] + 1)
+                   >= sizeof(arcs_cmdline))
+                       break;
+               strcat(arcs_cmdline, arg[i]);
+               strcat(arcs_cmdline, " ");
+       }
+
+       while (*env) {
+               if (strncmp("gtbase", *env, strlen("gtbase")) == 0) {
+                       marvell_base = simple_strtol(*env + strlen("gtbase="),
+                                                       NULL, 16);
+               }
+               if (strncmp("cpuclock", *env, strlen("cpuclock")) == 0) {
+                       cpu_clock = simple_strtol(*env + strlen("cpuclock="),
+                                                       NULL, 10);
+               }
+               env++;
+       }
+#endif /* CONFIG_MIPS64 */
+
+       mips_machgroup = MACH_GROUP_MOMENCO;
+       mips_machtype = MACH_MOMENCO_OCELOT_3;
+
+#ifdef CONFIG_MV643XX_ETH
+       /* get the base MAC address for on-board ethernet ports */
+       get_mac(prom_mac_addr_base);
+#endif
+
+#ifndef CONFIG_MIPS64
+       debug_vectors->printf("Booting Linux kernel...\n");
+#endif
+}
+
+void __init prom_free_prom_memory(void)
+{
+}
+
+void __init prom_fixup_mem_map(unsigned long start, unsigned long end)
+{
+}
diff --git a/arch/mips/momentum/ocelot_3/reset.c b/arch/mips/momentum/ocelot_3/reset.c
new file mode 100644 (file)
index 0000000..cb7e356
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * Copyright (C) 1997, 2001 Ralf Baechle
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: jsun@mvista.com or jsun@junsun.net
+ *
+ * Copyright (C) 2002 Momentum Computer Inc.
+ * Author: Matthew Dharm <mdharm@momenco.com>
+ *
+ * Louis Hamilton, Red Hat, Inc.
+ * hamilton@redhat.com  [MIPS64 modifications]
+ *
+ * Copyright 2004 PMC-Sierra
+ * Author: Manish Lachwani (lachwani@pmc-sierra.com)
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com
+ */
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/reboot.h>
+#include <asm/system.h>
+
+void momenco_ocelot_restart(char *command)
+{
+       /* base address of timekeeper portion of part */
+       void *nvram = (void *) 0xfc807000L;
+
+       /* Ask the NVRAM/RTC/watchdog chip to assert reset in 1/16 second */
+       writeb(0x84, nvram + 0xff7);
+
+       /* wait for the watchdog to go off */
+       mdelay(100+(1000/16));
+
+       /* if the watchdog fails for some reason, let people know */
+       printk(KERN_NOTICE "Watchdog reset failed\n");
+}
+
+void momenco_ocelot_halt(void)
+{
+       printk(KERN_NOTICE "\n** You can safely turn off the power\n");
+       while (1)
+               __asm__(".set\tmips3\n\t"
+                       "wait\n\t"
+                       ".set\tmips0");
+}
+
+void momenco_ocelot_power_off(void)
+{
+       momenco_ocelot_halt();
+}
diff --git a/arch/mips/momentum/ocelot_3/setup.c b/arch/mips/momentum/ocelot_3/setup.c
new file mode 100644 (file)
index 0000000..9c73363
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * setup.c
+ *
+ * BRIEF MODULE DESCRIPTION
+ * Momentum Computer Ocelot-3 board dependent boot routines
+ *
+ * Copyright (C) 1996, 1997, 2001  Ralf Baechle
+ * Copyright (C) 2000 RidgeRun, Inc.
+ * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2002 Momentum Computer
+ *
+ * Author: Matthew Dharm, Momentum Computer
+ *   mdharm@momenco.com
+ *
+ * Louis Hamilton, Red Hat, Inc.
+ *   hamilton@redhat.com  [MIPS64 modifications]
+ *
+ * Author: RidgeRun, Inc.
+ *   glonnon@ridgerun.com, skranz@ridgerun.com, stevej@ridgerun.com
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: jsun@mvista.com or jsun@junsun.net
+ *
+ * Copyright 2004 PMC-Sierra
+ * Author: Manish Lachwani (lachwani@pmc-sierra.com)
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  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 <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mc146818rtc.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/timex.h>
+#include <linux/bootmem.h>
+#include <linux/mv643xx.h>
+#include <asm/time.h>
+#include <asm/page.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pci.h>
+#include <asm/processor.h>
+#include <asm/ptrace.h>
+#include <asm/reboot.h>
+#include <asm/mc146818rtc.h>
+#include <asm/tlbflush.h>
+#include "ocelot_3_fpga.h"
+
+/* Marvell Discovery Register Base */
+unsigned long marvell_base = (signed)0xf4000000;
+
+/* CPU clock */
+unsigned long cpu_clock;
+
+/* RTC/NVRAM */
+unsigned char* rtc_base = (unsigned char*)(signed)0xfc800000;
+
+/* FPGA Base */
+unsigned long ocelot_fpga_base = (signed)0xfc000000;
+
+/* Serial base */
+unsigned long uart_base = (signed)0xfd000000;
+
+/*
+ * Marvell Discovery SRAM. This is one place where Ethernet
+ * Tx and Rx descriptors can be placed to improve performance
+ */
+extern unsigned long mv64340_sram_base;
+
+/* These functions are used for rebooting or halting the machine*/
+extern void momenco_ocelot_restart(char *command);
+extern void momenco_ocelot_halt(void);
+extern void momenco_ocelot_power_off(void);
+
+void momenco_time_init(void);
+static char reset_reason;
+
+void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
+                    unsigned long entryhi, unsigned long pagemask);
+
+static inline unsigned long ENTRYLO(unsigned long paddr)
+{
+       return ((paddr & PAGE_MASK) |
+               (_PAGE_PRESENT | __READABLE | __WRITEABLE | _PAGE_GLOBAL |
+               _CACHE_UNCACHED)) >> 6;
+}
+
+void __init bus_error_init(void)
+{
+       /* nothing */
+}
+
+/*
+ * setup code for a handoff from a version 2 PMON 2000 PROM
+ */
+void setup_wired_tlb_entries(void)
+{
+       write_c0_wired(0);
+       local_flush_tlb_all();
+
+       /* marvell and extra space */
+       add_wired_entry(ENTRYLO(0xf4000000), ENTRYLO(0xf4010000), (signed)0xf4000000, PM_64K);
+
+       /* fpga, rtc, and uart */
+       add_wired_entry(ENTRYLO(0xfc000000), ENTRYLO(0xfd000000), (signed)0xfc000000, PM_16M);
+}
+
+#define CONV_BCD_TO_BIN(val)   (((val) & 0xf) + (((val) >> 4) * 10))
+#define CONV_BIN_TO_BCD(val)   (((val) % 10) + (((val) / 10) << 4))
+
+unsigned long m48t37y_get_time(void)
+{
+       unsigned int year, month, day, hour, min, sec;
+
+       /* stop the update */
+       rtc_base[0x7ff8] = 0x40;
+
+       year = CONV_BCD_TO_BIN(rtc_base[0x7fff]);
+       year += CONV_BCD_TO_BIN(rtc_base[0x7ff1]) * 100;
+
+       month = CONV_BCD_TO_BIN(rtc_base[0x7ffe]);
+
+       day = CONV_BCD_TO_BIN(rtc_base[0x7ffd]);
+
+       hour = CONV_BCD_TO_BIN(rtc_base[0x7ffb]);
+       min = CONV_BCD_TO_BIN(rtc_base[0x7ffa]);
+       sec = CONV_BCD_TO_BIN(rtc_base[0x7ff9]);
+
+       /* start the update */
+       rtc_base[0x7ff8] = 0x00;
+
+       return mktime(year, month, day, hour, min, sec);
+}
+
+int m48t37y_set_time(unsigned long sec)
+{
+       struct rtc_time tm;
+
+       /* convert to a more useful format -- note months count from 0 */
+       to_tm(sec, &tm);
+       tm.tm_mon += 1;
+
+       /* enable writing */
+       rtc_base[0x7ff8] = 0x80;
+
+       /* year */
+       rtc_base[0x7fff] = CONV_BIN_TO_BCD(tm.tm_year % 100);
+       rtc_base[0x7ff1] = CONV_BIN_TO_BCD(tm.tm_year / 100);
+
+       /* month */
+       rtc_base[0x7ffe] = CONV_BIN_TO_BCD(tm.tm_mon);
+
+       /* day */
+       rtc_base[0x7ffd] = CONV_BIN_TO_BCD(tm.tm_mday);
+
+       /* hour/min/sec */
+       rtc_base[0x7ffb] = CONV_BIN_TO_BCD(tm.tm_hour);
+       rtc_base[0x7ffa] = CONV_BIN_TO_BCD(tm.tm_min);
+       rtc_base[0x7ff9] = CONV_BIN_TO_BCD(tm.tm_sec);
+
+       /* day of week -- not really used, but let's keep it up-to-date */
+       rtc_base[0x7ffc] = CONV_BIN_TO_BCD(tm.tm_wday + 1);
+
+       /* disable writing */
+       rtc_base[0x7ff8] = 0x00;
+
+       return 0;
+}
+
+void momenco_timer_setup(struct irqaction *irq)
+{
+       setup_irq(7, irq);      /* Timer interrupt, unmask status IM7 */
+}
+
+void momenco_time_init(void)
+{
+       setup_wired_tlb_entries();
+
+       /*
+        * Ocelot-3 board has been built with both
+        * the Rm7900 and the Rm7065C
+        */
+       mips_hpt_frequency = cpu_clock / 2;
+       board_timer_setup = momenco_timer_setup;
+
+       rtc_get_time = m48t37y_get_time;
+       rtc_set_time = m48t37y_set_time;
+}
+
+/*
+ * PCI Support for Ocelot-3
+ */
+
+/* Bus #0 IO and MEM space */
+#define        OCELOT_3_PCI_IO_0_START         0xe0000000
+#define        OCELOT_3_PCI_IO_0_SIZE          0x08000000
+#define        OCELOT_3_PCI_MEM_0_START        0xc0000000
+#define        OCELOT_3_PCI_MEM_0_SIZE         0x10000000
+
+/* Bus #1 IO and MEM space */
+#define        OCELOT_3_PCI_IO_1_START         0xe8000000
+#define        OCELOT_3_PCI_IO_1_SIZE          0x08000000
+#define        OCELOT_3_PCI_MEM_1_START        0xd0000000
+#define        OCELOT_3_PCI_MEM_1_SIZE         0x10000000
+
+static struct resource mv_pci_io_mem0_resource = {
+       .name   = "MV64340 PCI0 IO MEM",
+       .start  = OCELOT_3_PCI_IO_0_START,
+       .end    = OCELOT_3_PCI_IO_0_START + OCELOT_3_PCI_IO_0_SIZE - 1,
+       .flags  = IORESOURCE_IO,
+};
+
+static struct resource mv_pci_io_mem1_resource = {
+       .name   = "MV64340 PCI1 IO MEM",
+       .start  = OCELOT_3_PCI_IO_1_START,
+       .end    = OCELOT_3_PCI_IO_1_START + OCELOT_3_PCI_IO_1_SIZE - 1,
+       .flags  = IORESOURCE_IO,
+};
+
+static struct resource mv_pci_mem0_resource = {
+       .name   = "MV64340 PCI0 MEM",
+       .start  = OCELOT_3_PCI_MEM_0_START,
+       .end    = OCELOT_3_PCI_MEM_0_START + OCELOT_3_PCI_MEM_0_SIZE - 1,
+       .flags  = IORESOURCE_MEM,
+};
+
+static struct resource mv_pci_mem1_resource = {
+       .name   = "MV64340 PCI1 MEM",
+       .start  = OCELOT_3_PCI_MEM_1_START,
+       .end    = OCELOT_3_PCI_MEM_1_START + OCELOT_3_PCI_MEM_1_SIZE - 1,
+       .flags  = IORESOURCE_MEM,
+};
+
+static struct mv_pci_controller mv_bus0_controller = {
+       .pcic = {
+                .pci_ops       = &mv_pci_ops,
+                .mem_resource  = &mv_pci_mem0_resource,
+                .io_resource   = &mv_pci_io_mem0_resource,
+       },
+       .config_addr    = MV64340_PCI_0_CONFIG_ADDR,
+       .config_vreg    = MV64340_PCI_0_CONFIG_DATA_VIRTUAL_REG,
+};
+
+static struct mv_pci_controller mv_bus1_controller = {
+       .pcic = {
+                .pci_ops       = &mv_pci_ops,
+                .mem_resource  = &mv_pci_mem1_resource,
+                .io_resource   = &mv_pci_io_mem1_resource,
+       },
+       .config_addr    = MV64340_PCI_1_CONFIG_ADDR,
+       .config_vreg    = MV64340_PCI_1_CONFIG_DATA_VIRTUAL_REG,
+};
+
+static __init int __init ja_pci_init(void)
+{
+       uint32_t enable;
+       extern int pci_probe_only;
+
+       /* PMON will assign PCI resources */
+       pci_probe_only = 1;
+
+       enable = ~MV_READ(MV64340_BASE_ADDR_ENABLE);
+       /*
+        * We require at least one enabled I/O or PCI memory window or we
+        * will ignore this PCI bus.  We ignore PCI windows 1, 2 and 3.
+        */
+       if (enable & (0x01 <<  9) || enable & (0x01 << 10))
+               register_pci_controller(&mv_bus0_controller.pcic);
+
+       if (enable & (0x01 << 14) || enable & (0x01 << 15))
+               register_pci_controller(&mv_bus1_controller.pcic);
+
+       ioport_resource.end = OCELOT_3_PCI_IO_0_START + OCELOT_3_PCI_IO_0_SIZE +
+                                       OCELOT_3_PCI_IO_1_SIZE - 1;
+
+       iomem_resource.end = OCELOT_3_PCI_MEM_0_START + OCELOT_3_PCI_MEM_0_SIZE +
+                                       OCELOT_3_PCI_MEM_1_SIZE - 1;
+
+       set_io_port_base(OCELOT_3_PCI_IO_0_START); /* mips_io_port_base */
+
+       return 0;
+}
+
+arch_initcall(ja_pci_init);
+
+static int __init momenco_ocelot_3_setup(void)
+{
+       unsigned int tmpword;
+
+       board_time_init = momenco_time_init;
+
+       _machine_restart = momenco_ocelot_restart;
+       _machine_halt = momenco_ocelot_halt;
+       _machine_power_off = momenco_ocelot_power_off;
+
+       /* Wired TLB entries */
+       setup_wired_tlb_entries();
+
+       /* shut down ethernet ports, just to be sure our memory doesn't get
+        * corrupted by random ethernet traffic.
+        */
+       MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(0), 0xff << 8);
+       MV_WRITE(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(1), 0xff << 8);
+       MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(0), 0xff << 8);
+       MV_WRITE(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(1), 0xff << 8);
+       do {}
+         while (MV_READ(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(0)) & 0xff);
+       do {}
+         while (MV_READ(MV64340_ETH_RECEIVE_QUEUE_COMMAND_REG(1)) & 0xff);
+       do {}
+         while (MV_READ(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(0)) & 0xff);
+       do {}
+         while (MV_READ(MV64340_ETH_TRANSMIT_QUEUE_COMMAND_REG(1)) & 0xff);
+       MV_WRITE(MV64340_ETH_PORT_SERIAL_CONTROL_REG(0),
+                MV_READ(MV64340_ETH_PORT_SERIAL_CONTROL_REG(0)) & ~1);
+       MV_WRITE(MV64340_ETH_PORT_SERIAL_CONTROL_REG(1),
+                MV_READ(MV64340_ETH_PORT_SERIAL_CONTROL_REG(1)) & ~1);
+
+       /* Turn off the Bit-Error LED */
+       OCELOT_FPGA_WRITE(0x80, CLR);
+
+       tmpword = OCELOT_FPGA_READ(BOARDREV);
+       if (tmpword < 26)
+               printk("Momenco Ocelot-3: Board Assembly Rev. %c\n",
+                       'A'+tmpword);
+       else
+               printk("Momenco Ocelot-3: Board Assembly Revision #0x%x\n",
+                       tmpword);
+
+       tmpword = OCELOT_FPGA_READ(FPGA_REV);
+       printk("FPGA Rev: %d.%d\n", tmpword>>4, tmpword&15);
+       tmpword = OCELOT_FPGA_READ(RESET_STATUS);
+       printk("Reset reason: 0x%x\n", tmpword);
+       switch (tmpword) {
+               case 0x1:
+                       printk("  - Power-up reset\n");
+                       break;
+               case 0x2:
+                       printk("  - Push-button reset\n");
+                       break;
+               case 0x4:
+                       printk("  - cPCI bus reset\n");
+                       break;
+               case 0x8:
+                       printk("  - Watchdog reset\n");
+                       break;
+               case 0x10:
+                       printk("  - Software reset\n");
+                       break;
+               default:
+                       printk("  - Unknown reset cause\n");
+       }
+       reset_reason = tmpword;
+       OCELOT_FPGA_WRITE(0xff, RESET_STATUS);
+
+       tmpword = OCELOT_FPGA_READ(CPCI_ID);
+       printk("cPCI ID register: 0x%02x\n", tmpword);
+       printk("  - Slot number: %d\n", tmpword & 0x1f);
+       printk("  - PCI bus present: %s\n", tmpword & 0x40 ? "yes" : "no");
+       printk("  - System Slot: %s\n", tmpword & 0x20 ? "yes" : "no");
+
+       tmpword = OCELOT_FPGA_READ(BOARD_STATUS);
+       printk("Board Status register: 0x%02x\n", tmpword);
+       printk("  - User jumper: %s\n", (tmpword & 0x80)?"installed":"absent");
+       printk("  - Boot flash write jumper: %s\n", (tmpword&0x40)?"installed":"absent");
+       printk("  - L3 cache size: %d MB\n", (1<<((tmpword&12) >> 2))&~1);
+
+       /* Support for 128 MB memory */
+       add_memory_region(0x0, 0x08000000, BOOT_MEM_RAM);
+
+       return 0;
+}
+
+early_initcall(momenco_ocelot_3_setup);
diff --git a/arch/mips/oprofile/Kconfig b/arch/mips/oprofile/Kconfig
new file mode 100644 (file)
index 0000000..19d3773
--- /dev/null
@@ -0,0 +1,23 @@
+
+menu "Profiling support"
+       depends on EXPERIMENTAL
+
+config PROFILING
+       bool "Profiling support (EXPERIMENTAL)"
+       help
+         Say Y here to enable the extended profiling support mechanisms used
+         by profilers such as OProfile.
+
+
+config OPROFILE
+       tristate "OProfile system profiling (EXPERIMENTAL)"
+       depends on PROFILING
+       help
+         OProfile is a profiling system capable of profiling the
+         whole system, include the kernel, kernel modules, libraries,
+         and applications.
+
+         If unsure, say N.
+
+endmenu
+
diff --git a/arch/mips/oprofile/Makefile b/arch/mips/oprofile/Makefile
new file mode 100644 (file)
index 0000000..354261d
--- /dev/null
@@ -0,0 +1,15 @@
+EXTRA_CFLAGS := -Werror
+
+obj-$(CONFIG_OPROFILE) += oprofile.o
+
+DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
+               oprof.o cpu_buffer.o buffer_sync.o \
+               event_buffer.o oprofile_files.o \
+               oprofilefs.o oprofile_stats.o \
+               timer_int.o )
+
+oprofile-y                             := $(DRIVER_OBJS) common.o
+
+oprofile-$(CONFIG_CPU_MIPS32)          += op_model_mipsxx.o
+oprofile-$(CONFIG_CPU_MIPS64)          += op_model_mipsxx.o
+oprofile-$(CONFIG_CPU_RM9000)          += op_model_rm9000.o
diff --git a/arch/mips/oprofile/common.c b/arch/mips/oprofile/common.c
new file mode 100644 (file)
index 0000000..ab65ce3
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 by Ralf Baechle
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/oprofile.h>
+#include <linux/smp.h>
+#include <asm/cpu-info.h>
+
+#include "op_impl.h"
+
+extern struct op_mips_model op_model_mipsxx __attribute__((weak));
+extern struct op_mips_model op_model_rm9000 __attribute__((weak));
+
+static struct op_mips_model *model;
+
+static struct op_counter_config ctr[20];
+
+static int op_mips_setup(void)
+{
+       /* Pre-compute the values to stuff in the hardware registers.  */
+       model->reg_setup(ctr);
+
+       /* Configure the registers on all cpus.  */
+       on_each_cpu(model->cpu_setup, 0, 0, 1);
+
+        return 0;
+}
+
+static int op_mips_create_files(struct super_block * sb, struct dentry * root)
+{
+       int i;
+
+       for (i = 0; i < model->num_counters; ++i) {
+               struct dentry *dir;
+               char buf[3];
+
+               snprintf(buf, sizeof buf, "%d", i);
+               dir = oprofilefs_mkdir(sb, root, buf);
+
+               oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
+               oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
+               oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
+               /* Dummies.  */
+               oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
+               oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
+               oprofilefs_create_ulong(sb, dir, "exl", &ctr[i].exl);
+               oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+       }
+
+       return 0;
+}
+
+static int op_mips_start(void)
+{
+       on_each_cpu(model->cpu_start, NULL, 0, 1);
+
+       return 0;
+}
+
+static void op_mips_stop(void)
+{
+       /* Disable performance monitoring for all counters.  */
+       on_each_cpu(model->cpu_stop, NULL, 0, 1);
+}
+
+void __init oprofile_arch_init(struct oprofile_operations *ops)
+{
+       struct op_mips_model *lmodel = NULL;
+
+       switch (current_cpu_data.cputype) {
+       case CPU_24K:
+               lmodel = &op_model_mipsxx;
+               break;
+
+       case CPU_RM9000:
+               lmodel = &op_model_rm9000;
+               break;
+       };
+
+       if (!lmodel)
+               return;
+
+       if (lmodel->init())
+               return;
+
+       model = lmodel;
+
+       ops->create_files = op_mips_create_files;
+       ops->setup = op_mips_setup;
+       ops->start = op_mips_start;
+       ops->stop = op_mips_stop;
+       ops->cpu_type = lmodel->cpu_type;
+
+       printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
+              lmodel->cpu_type);
+}
+
+void oprofile_arch_exit(void)
+{
+       model->exit();
+}
diff --git a/arch/mips/oprofile/op_impl.h b/arch/mips/oprofile/op_impl.h
new file mode 100644 (file)
index 0000000..9f5cdff
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * @file arch/alpha/oprofile/op_impl.h
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Richard Henderson <rth@twiddle.net>
+ */
+
+#ifndef OP_IMPL_H
+#define OP_IMPL_H 1
+
+/* Per-counter configuration as set via oprofilefs.  */
+struct op_counter_config {
+       unsigned long enabled;
+       unsigned long event;
+       unsigned long count;
+       /* Dummies because I am too lazy to hack the userspace tools.  */
+       unsigned long kernel;
+       unsigned long user;
+       unsigned long exl;
+       unsigned long unit_mask;
+};
+
+/* Per-architecture configury and hooks.  */
+struct op_mips_model {
+       void (*reg_setup) (struct op_counter_config *);
+       void (*cpu_setup) (void * dummy);
+       int (*init)(void);
+       void (*exit)(void);
+       void (*cpu_start)(void *args);
+       void (*cpu_stop)(void *args);
+       char *cpu_type;
+       unsigned char num_counters;
+};
+
+#endif
diff --git a/arch/mips/oprofile/op_model_rm9000.c b/arch/mips/oprofile/op_model_rm9000.c
new file mode 100644 (file)
index 0000000..bee4779
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 by Ralf Baechle
+ */
+#include <linux/oprofile.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+
+#include "op_impl.h"
+
+#define RM9K_COUNTER1_EVENT(event)     ((event) << 0)
+#define RM9K_COUNTER1_SUPERVISOR       (1ULL    <<  7)
+#define RM9K_COUNTER1_KERNEL           (1ULL    <<  8)
+#define RM9K_COUNTER1_USER             (1ULL    <<  9)
+#define RM9K_COUNTER1_ENABLE           (1ULL    << 10)
+#define RM9K_COUNTER1_OVERFLOW         (1ULL    << 15)
+
+#define RM9K_COUNTER2_EVENT(event)     ((event) << 16)
+#define RM9K_COUNTER2_SUPERVISOR       (1ULL    << 23)
+#define RM9K_COUNTER2_KERNEL           (1ULL    << 24)
+#define RM9K_COUNTER2_USER             (1ULL    << 25)
+#define RM9K_COUNTER2_ENABLE           (1ULL    << 26)
+#define RM9K_COUNTER2_OVERFLOW         (1ULL    << 31)
+
+extern unsigned int rm9000_perfcount_irq;
+
+static struct rm9k_register_config {
+       unsigned int control;
+       unsigned int reset_counter1;
+       unsigned int reset_counter2;
+} reg;
+
+/* Compute all of the registers in preparation for enabling profiling.  */
+
+static void rm9000_reg_setup(struct op_counter_config *ctr)
+{
+       unsigned int control = 0;
+
+       /* Compute the performance counter control word.  */
+       /* For now count kernel and user mode */
+       if (ctr[0].enabled)
+               control |= RM9K_COUNTER1_EVENT(ctr[0].event) |
+                          RM9K_COUNTER1_KERNEL |
+                          RM9K_COUNTER1_USER |
+                          RM9K_COUNTER1_ENABLE;
+       if (ctr[1].enabled)
+               control |= RM9K_COUNTER2_EVENT(ctr[1].event) |
+                          RM9K_COUNTER2_KERNEL |
+                          RM9K_COUNTER2_USER |
+                          RM9K_COUNTER2_ENABLE;
+       reg.control = control;
+
+       reg.reset_counter1 = 0x80000000 - ctr[0].count;
+       reg.reset_counter2 = 0x80000000 - ctr[1].count;
+}
+
+/* Program all of the registers in preparation for enabling profiling.  */
+
+static void rm9000_cpu_setup (void *args)
+{
+       uint64_t perfcount;
+
+       perfcount = ((uint64_t) reg.reset_counter2 << 32) | reg.reset_counter1;
+       write_c0_perfcount(perfcount);
+}
+
+static void rm9000_cpu_start(void *args)
+{
+       /* Start all counters on current CPU */
+       write_c0_perfcontrol(reg.control);
+}
+
+static void rm9000_cpu_stop(void *args)
+{
+       /* Stop all counters on current CPU */
+       write_c0_perfcontrol(0);
+}
+
+static irqreturn_t rm9000_perfcount_handler(int irq, void * dev_id,
+       struct pt_regs *regs)
+{
+       unsigned int control = read_c0_perfcontrol();
+       uint32_t counter1, counter2;
+       uint64_t counters;
+
+       /*
+        * RM9000 combines two 32-bit performance counters into a single
+        * 64-bit coprocessor zero register.  To avoid a race updating the
+        * registers we need to stop the counters while we're messing with
+        * them ...
+        */
+       write_c0_perfcontrol(0);
+
+       counters = read_c0_perfcount();
+       counter1 = counters;
+       counter2 = counters >> 32;
+
+       if (control & RM9K_COUNTER1_OVERFLOW) {
+               oprofile_add_sample(regs, 0);
+               counter1 = reg.reset_counter1;
+       }
+       if (control & RM9K_COUNTER2_OVERFLOW) {
+               oprofile_add_sample(regs, 1);
+               counter2 = reg.reset_counter2;
+       }
+
+       counters = ((uint64_t)counter2 << 32) | counter1;
+       write_c0_perfcount(counters);
+       write_c0_perfcontrol(reg.control);
+
+       return IRQ_HANDLED;
+}
+
+static int rm9000_init(void)
+{
+       return request_irq(rm9000_perfcount_irq, rm9000_perfcount_handler,
+                          0, "Perfcounter", NULL);
+}
+
+static void rm9000_exit(void)
+{
+       free_irq(rm9000_perfcount_irq, NULL);
+}
+
+struct op_mips_model op_model_rm9000 = {
+       .reg_setup      = rm9000_reg_setup,
+       .cpu_setup      = rm9000_cpu_setup,
+       .init           = rm9000_init,
+       .exit           = rm9000_exit,
+       .cpu_start      = rm9000_cpu_start,
+       .cpu_stop       = rm9000_cpu_stop,
+       .cpu_type       = "mips/rm9000",
+       .num_counters   = 2
+};
diff --git a/arch/mips/pci/fixup-ocelot3.c b/arch/mips/pci/fixup-ocelot3.c
new file mode 100644 (file)
index 0000000..ececc03
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 Montavista Software Inc.
+ * Author: Manish Lachwani (mlachwani@mvista.com)
+ *
+ * Looking at the schematics for the Ocelot-3 board, there are
+ * two PCI busses and each bus has two PCI slots.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/mipsregs.h>
+
+/*
+ * Do platform specific device initialization at
+ * pci_enable_device() time
+ */
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+       return 0;
+}
+
+int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+       int bus = dev->bus->number;
+
+       if (bus == 0 && slot == 1)
+               return 2;       /* PCI-X A */
+       if (bus == 0 && slot == 2)
+               return 3;       /* PCI-X B */
+       if (bus == 1 && slot == 1)
+               return 4;       /* PCI A */
+       if (bus == 1 && slot == 2)
+               return 5;       /* PCI B */
+
+return 0;
+       panic("Whooops in pcibios_map_irq");
+}
diff --git a/arch/mips/pci/fixup-sb1250.c b/arch/mips/pci/fixup-sb1250.c
new file mode 100644 (file)
index 0000000..13791b7
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ *     arch/mips/pci/fixup-sb1250.c
+ *
+ *     Copyright (C) 2004  MIPS Technologies, Inc.  All rights reserved.
+ *         Author:     Maciej W. Rozycki <macro@mips.com>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/pci.h>
+
+/*
+ * The BCM1250, etc. PCI/HT bridge reports as a host bridge.
+ */
+static void __init quirk_sb1250_ht(struct pci_dev *dev)
+{
+       dev->class = PCI_CLASS_BRIDGE_PCI << 8;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIBYTE, PCI_DEVICE_ID_BCM1250_HT,
+                       quirk_sb1250_ht);
diff --git a/arch/mips/pci/fixup-vr4133.c b/arch/mips/pci/fixup-vr4133.c
new file mode 100644 (file)
index 0000000..03a0ff2
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * arch/mips/vr41xx/nec-cmbvr4133/pci_fixup.c
+ *
+ * The NEC CMB-VR4133 Board specific PCI fixups.
+ *
+ * Author: Yoichi Yuasa <yyuasa@mvista.com, or source@mvista.com> and
+ *         Alex Sapkov <asapkov@ru.mvista.com>
+ *
+ * 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.
+ *
+ * Modified for support in 2.6
+ * Author: Manish Lachwani (mlachwani@mvista.com)
+ *
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+#include <asm/vr41xx/cmbvr4133.h>
+
+extern int vr4133_rockhopper;
+extern void ali_m1535plus_init(struct pci_dev *dev);
+extern void ali_m5229_init(struct pci_dev *dev);
+
+/* Do platform specific device initialization at pci_enable_device() time */
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+       /*
+        * We have to reset AMD PCnet adapter on Rockhopper since
+        * PMON leaves it enabled and generating interrupts. This leads
+        * to a lock if some PCI device driver later enables the IRQ line
+        * shared with PCnet and there is no AMD PCnet driver to catch its
+        * interrupts.
+        */
+#ifdef CONFIG_ROCKHOPPER
+       if (dev->vendor == PCI_VENDOR_ID_AMD &&
+               dev->device == PCI_DEVICE_ID_AMD_LANCE) {
+               inl(pci_resource_start(dev, 0) + 0x18);
+       }
+#endif
+
+       /*
+        * we have to open the bridges' windows down to 0 because otherwise
+        * we cannot access ISA south bridge I/O registers that get mapped from
+        * 0. for example, 8259 PIC would be unaccessible without that
+        */
+       if(dev->vendor == PCI_VENDOR_ID_INTEL && dev->device == PCI_DEVICE_ID_INTEL_S21152BB) {
+               pci_write_config_byte(dev, PCI_IO_BASE, 0);
+               if(dev->bus->number == 0) {
+                       pci_write_config_word(dev, PCI_IO_BASE_UPPER16, 0);
+               } else {
+                       pci_write_config_word(dev, PCI_IO_BASE_UPPER16, 1);
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * M1535 IRQ mapping
+ * Feel free to change this, although it shouldn't be needed
+ */
+#define M1535_IRQ_INTA  7
+#define M1535_IRQ_INTB  9
+#define M1535_IRQ_INTC  10
+#define M1535_IRQ_INTD  11
+
+#define M1535_IRQ_USB   9
+#define M1535_IRQ_IDE   14
+#define M1535_IRQ_IDE2  15
+#define M1535_IRQ_PS2   12
+#define M1535_IRQ_RTC   8
+#define M1535_IRQ_FDC   6
+#define M1535_IRQ_AUDIO 5
+#define M1535_IRQ_COM1  4
+#define M1535_IRQ_COM2  4
+#define M1535_IRQ_IRDA  3
+#define M1535_IRQ_KBD   1
+#define M1535_IRQ_TMR   0
+
+/* Rockhopper "slots" assignment; this is hard-coded ... */
+#define ROCKHOPPER_M5451_SLOT  1
+#define ROCKHOPPER_M1535_SLOT  2
+#define ROCKHOPPER_M5229_SLOT  11
+#define ROCKHOPPER_M5237_SLOT  15
+#define ROCKHOPPER_PMU_SLOT    12
+/* ... and hard-wired. */
+#define ROCKHOPPER_PCI1_SLOT   3
+#define ROCKHOPPER_PCI2_SLOT   4
+#define ROCKHOPPER_PCI3_SLOT   5
+#define ROCKHOPPER_PCI4_SLOT   6
+#define ROCKHOPPER_PCNET_SLOT  1
+
+#define M1535_IRQ_MASK(n) (1 << (n))
+
+#define M1535_IRQ_EDGE  (M1535_IRQ_MASK(M1535_IRQ_TMR)  | \
+                         M1535_IRQ_MASK(M1535_IRQ_KBD)  | \
+                         M1535_IRQ_MASK(M1535_IRQ_COM1) | \
+                         M1535_IRQ_MASK(M1535_IRQ_COM2) | \
+                         M1535_IRQ_MASK(M1535_IRQ_IRDA) | \
+                         M1535_IRQ_MASK(M1535_IRQ_RTC)  | \
+                         M1535_IRQ_MASK(M1535_IRQ_FDC)  | \
+                         M1535_IRQ_MASK(M1535_IRQ_PS2))
+
+#define M1535_IRQ_LEVEL (M1535_IRQ_MASK(M1535_IRQ_IDE)  | \
+                         M1535_IRQ_MASK(M1535_IRQ_USB)  | \
+                         M1535_IRQ_MASK(M1535_IRQ_INTA) | \
+                         M1535_IRQ_MASK(M1535_IRQ_INTB) | \
+                         M1535_IRQ_MASK(M1535_IRQ_INTC) | \
+                         M1535_IRQ_MASK(M1535_IRQ_INTD))
+
+struct irq_map_entry {
+       u16 bus;
+       u8 slot;
+       u8 irq;
+};
+static struct irq_map_entry int_map[] = {
+       {1, ROCKHOPPER_M5451_SLOT, M1535_IRQ_AUDIO},    /* Audio controller */
+       {1, ROCKHOPPER_PCI1_SLOT, M1535_IRQ_INTD},      /* PCI slot #1 */
+       {1, ROCKHOPPER_PCI2_SLOT, M1535_IRQ_INTC},      /* PCI slot #2 */
+       {1, ROCKHOPPER_M5237_SLOT, M1535_IRQ_USB},      /* USB host controller */
+       {1, ROCKHOPPER_M5229_SLOT, IDE_PRIMARY_IRQ},    /* IDE controller */
+       {2, ROCKHOPPER_PCNET_SLOT, M1535_IRQ_INTD},     /* AMD Am79c973 on-board
+                                                          ethernet */
+       {2, ROCKHOPPER_PCI3_SLOT, M1535_IRQ_INTB},      /* PCI slot #3 */
+       {2, ROCKHOPPER_PCI4_SLOT, M1535_IRQ_INTC}       /* PCI slot #4 */
+};
+
+static int pci_intlines[] =
+    { M1535_IRQ_INTA, M1535_IRQ_INTB, M1535_IRQ_INTC, M1535_IRQ_INTD };
+
+/* Determine the Rockhopper IRQ line number for the PCI device */
+int rockhopper_get_irq(struct pci_dev *dev, u8 pin, u8 slot)
+{
+       struct pci_bus *bus;
+       int i;
+
+       bus = dev->bus;
+       if (bus == NULL)
+               return -1;
+
+       for (i = 0; i < sizeof (int_map) / sizeof (int_map[0]); i++) {
+               if (int_map[i].bus == bus->number && int_map[i].slot == slot) {
+                       int line;
+                       for (line = 0; line < 4; line++)
+                               if (pci_intlines[line] == int_map[i].irq)
+                                       break;
+                       if (line < 4)
+                               return pci_intlines[(line + (pin - 1)) % 4];
+                       else
+                               return int_map[i].irq;
+               }
+       }
+       return -1;
+}
+
+#ifdef CONFIG_ROCKHOPPER
+void i8259_init(void)
+{
+       outb(0x11, 0x20);               /* Master ICW1 */
+       outb(I8259_IRQ_BASE, 0x21);     /* Master ICW2 */
+       outb(0x04, 0x21);               /* Master ICW3 */
+       outb(0x01, 0x21);               /* Master ICW4 */
+       outb(0xff, 0x21);               /* Master IMW */
+
+       outb(0x11, 0xa0);               /* Slave ICW1 */
+       outb(I8259_IRQ_BASE + 8, 0xa1); /* Slave ICW2 */
+       outb(0x02, 0xa1);               /* Slave ICW3 */
+       outb(0x01, 0xa1);               /* Slave ICW4 */
+       outb(0xff, 0xa1);               /* Slave IMW */
+
+       outb(0x00, 0x4d0);
+       outb(0x02, 0x4d1);      /* USB IRQ9 is level */
+}
+#endif
+
+int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+       extern int pci_probe_only;
+       pci_probe_only = 1;
+
+#ifdef CONFIG_ROCKHOPPER
+       if( dev->bus->number == 1 && vr4133_rockhopper )  {
+               if(slot == ROCKHOPPER_PCI1_SLOT || slot == ROCKHOPPER_PCI2_SLOT)
+                       dev->irq = CMBVR41XX_INTA_IRQ;
+               else
+                       dev->irq = rockhopper_get_irq(dev, pin, slot);
+       } else
+               dev->irq = CMBVR41XX_INTA_IRQ;
+#else
+       dev->irq = CMBVR41XX_INTA_IRQ;
+#endif
+
+       return dev->irq;
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, ali_m1535plus_init);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, ali_m5229_init);
+
+
diff --git a/arch/mips/sgi-ip27/ip27-dbgio.c b/arch/mips/sgi-ip27/ip27-dbgio.c
new file mode 100644 (file)
index 0000000..08fd88b
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *  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.
+ *
+ * Copyright 2004 Ralf Baechle <ralf@linux-mips.org>
+ */
+#include <asm/sn/addrs.h>
+#include <asm/sn/sn0/hub.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/ioc3.h>
+#include <asm/sn/sn_private.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+#define IOC3_CLK        (22000000 / 3)
+#define IOC3_FLAGS      (0)
+
+static inline struct ioc3_uartregs *console_uart(void)
+{
+       struct ioc3 *ioc3;
+
+       ioc3 = (struct ioc3 *)KL_CONFIG_CH_CONS_INFO(get_nasid())->memory_base;
+
+       return &ioc3->sregs.uarta;
+}
+
+unsigned char getDebugChar(void)
+{
+       struct ioc3_uartregs *uart = console_uart();
+
+       while ((uart->iu_lsr & UART_LSR_DR) == 0);
+       return uart->iu_rbr;
+}
+
+void putDebugChar(unsigned char c)
+{
+       struct ioc3_uartregs *uart = console_uart();
+
+       while ((uart->iu_lsr & UART_LSR_THRE) == 0);
+       uart->iu_thr = c;
+}
diff --git a/arch/mips/sgi-ip32/ip32-memory.c b/arch/mips/sgi-ip32/ip32-memory.c
new file mode 100644 (file)
index 0000000..fc76ca9
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Keith M Wesolowski
+ * Copyright (C) 2005 Ilya A. Volynets (Total Knowledge)
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include <asm/ip32/crime.h>
+#include <asm/bootinfo.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+
+extern void crime_init(void);
+
+void __init prom_meminit (void)
+{
+       u64 base, size;
+       int bank;
+
+       crime_init();
+
+       for (bank=0; bank < CRIME_MAXBANKS; bank++) {
+               u64 bankctl = crime->bank_ctrl[bank];
+               base = (bankctl & CRIME_MEM_BANK_CONTROL_ADDR) << 25;
+               if (bank != 0 && base == 0)
+                       continue;
+               size = (bankctl & CRIME_MEM_BANK_CONTROL_SDRAM_SIZE) ? 128 : 32;
+               size <<= 20;
+               if (base + size > (256 << 20))
+                       base += CRIME_HI_MEM_BASE;
+
+               printk("CRIME MC: bank %u base 0x%016lx size %luMB\n",
+                       bank, base, size);
+               add_memory_region (base, size, BOOT_MEM_RAM);
+       }
+}
+
+
+unsigned long __init prom_free_prom_memory (void)
+{
+       return 0;
+}
diff --git a/arch/mips/vr41xx/nec-cmbvr4133/Makefile b/arch/mips/vr41xx/nec-cmbvr4133/Makefile
new file mode 100644 (file)
index 0000000..5835cae
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for the NEC-CMBVR4133
+#
+
+obj-y                          := init.o setup.o
+
+obj-$(CONFIG_PCI)              += m1535plus.o
+obj-$(CONFIG_ROCKHOPPER)       += irq.o
diff --git a/arch/mips/vr41xx/nec-cmbvr4133/init.c b/arch/mips/vr41xx/nec-cmbvr4133/init.c
new file mode 100644 (file)
index 0000000..87f06b3
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * arch/mips/vr41xx/nec-cmbvr4133/init.c
+ *
+ * PROM library initialisation code for NEC CMB-VR4133 board.
+ *
+ * Author: Yoichi Yuasa <yyuasa@mvista.com, or source@mvista.com> and
+ *         Jun Sun <jsun@mvista.com, or source@mvista.com> and
+ *         Alex Sapkov <asapkov@ru.mvista.com>
+ *
+ * 2001-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.
+ *
+ * Support for NEC-CMBVR4133 in 2.6
+ * Manish Lachwani (mlachwani@mvista.com)
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include <asm/bootinfo.h>
+
+#ifdef CONFIG_ROCKHOPPER
+#include <asm/io.h>
+#include <linux/pci.h>
+
+#define PCICONFDREG    0xaf000c14
+#define PCICONFAREG    0xaf000c18
+#endif
+
+const char *get_system_type(void)
+{
+       return "NEC CMB-VR4133";
+}
+
+#ifdef CONFIG_ROCKHOPPER
+void disable_pcnet(void)
+{
+       u32 data;
+
+       /*
+        * Workaround for the bug in PMON on VR4133. PMON leaves
+        * AMD PCNet controller (on Rockhopper) initialized and running in
+        * bus master mode. We have do disable it before doing any
+        * further initialization. Or we get problems with PCI bus 2
+        * and random lockups and crashes.
+        */
+
+       writel((2 << 16)                |
+              (PCI_DEVFN(1,0) << 8)    |
+              (0 & 0xfc)               |
+               1UL,
+              PCICONFAREG);
+
+       data = readl(PCICONFDREG);
+
+       writel((2 << 16)                |
+              (PCI_DEVFN(1,0) << 8)    |
+              (4 & 0xfc)               |
+               1UL,
+              PCICONFAREG);
+
+       data = readl(PCICONFDREG);
+
+       writel((2 << 16)                |
+              (PCI_DEVFN(1,0) << 8)    |
+              (4 & 0xfc)               |
+               1UL,
+              PCICONFAREG);
+
+       data &= ~4;
+
+       writel(data, PCICONFDREG);
+}
+#endif
+
diff --git a/arch/mips/vr41xx/nec-cmbvr4133/irq.c b/arch/mips/vr41xx/nec-cmbvr4133/irq.c
new file mode 100644 (file)
index 0000000..31db6b6
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * arch/mips/vr41xx/nec-cmbvr4133/irq.c
+ *
+ * Interrupt routines for the NEC CMB-VR4133 board.
+ *
+ * Author: Yoichi Yuasa <yyuasa@mvista.com, or source@mvista.com> and
+ *         Alex Sapkov <asapkov@ru.mvista.com>
+ *
+ * 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.
+ *
+ * Support for NEC-CMBVR4133 in 2.6
+ * Manish Lachwani (mlachwani@mvista.com)
+ */
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/vr41xx/cmbvr4133.h>
+
+extern void enable_8259A_irq(unsigned int irq);
+extern void disable_8259A_irq(unsigned int irq);
+extern void mask_and_ack_8259A(unsigned int irq);
+extern void init_8259A(int hoge);
+
+extern int vr4133_rockhopper;
+
+static unsigned int startup_i8259_irq(unsigned int irq)
+{
+       enable_8259A_irq(irq - I8259_IRQ_BASE);
+       return 0;
+}
+
+static void shutdown_i8259_irq(unsigned int irq)
+{
+       disable_8259A_irq(irq - I8259_IRQ_BASE);
+}
+
+static void enable_i8259_irq(unsigned int irq)
+{
+       enable_8259A_irq(irq - I8259_IRQ_BASE);
+}
+
+static void disable_i8259_irq(unsigned int irq)
+{
+       disable_8259A_irq(irq - I8259_IRQ_BASE);
+}
+
+static void ack_i8259_irq(unsigned int irq)
+{
+       mask_and_ack_8259A(irq - I8259_IRQ_BASE);
+}
+
+static void end_i8259_irq(unsigned int irq)
+{
+       if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+               enable_8259A_irq(irq - I8259_IRQ_BASE);
+}
+
+static struct hw_interrupt_type i8259_irq_type = {
+       .typename       = "XT-PIC",
+       .startup        = startup_i8259_irq,
+       .shutdown       = shutdown_i8259_irq,
+       .enable         = enable_i8259_irq,
+       .disable        = disable_i8259_irq,
+       .ack            = ack_i8259_irq,
+       .end            = end_i8259_irq,
+};
+
+static int i8259_get_irq_number(int irq)
+{
+       unsigned long isr;
+
+       isr = inb(0x20);
+       irq = ffz(~isr);
+       if (irq == 2) {
+               isr = inb(0xa0);
+               irq = 8 + ffz(~isr);
+       }
+
+       if (irq < 0 || irq > 15)
+               return -EINVAL;
+
+       return I8259_IRQ_BASE + irq;
+}
+
+static struct irqaction i8259_slave_cascade = {
+       .handler        = &no_action,
+       .name           = "cascade",
+};
+
+void __init rockhopper_init_irq(void)
+{
+       int i;
+
+       if(!vr4133_rockhopper) {
+               printk(KERN_ERR "Not a Rockhopper Board \n");
+               return;
+       }
+
+       for (i = I8259_IRQ_BASE; i <= I8259_IRQ_LAST; i++)
+               irq_desc[i].handler = &i8259_irq_type;
+
+       setup_irq(I8259_SLAVE_IRQ, &i8259_slave_cascade);
+
+       vr41xx_set_irq_trigger(CMBVR41XX_INTC_PIN, TRIGGER_LEVEL, SIGNAL_THROUGH);
+       vr41xx_set_irq_level(CMBVR41XX_INTC_PIN, LEVEL_HIGH);
+       vr41xx_cascade_irq(CMBVR41XX_INTC_IRQ, i8259_get_irq_number);
+}
diff --git a/arch/mips/vr41xx/nec-cmbvr4133/m1535plus.c b/arch/mips/vr41xx/nec-cmbvr4133/m1535plus.c
new file mode 100644 (file)
index 0000000..1f6b24e
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * arch/mips/vr41xx/nec-cmbvr4133/m1535plus.c
+ *
+ * Initialize for ALi M1535+(included M5229 and M5237).
+ *
+ * Author: Yoichi Yuasa <yyuasa@mvista.com, or source@mvista.com> and
+ *         Alex Sapkov <asapkov@ru.mvista.com>
+ *
+ * 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.
+ *
+ * Support for NEC-CMBVR4133 in 2.6
+ * Author: Manish Lachwani (mlachwani@mvista.com)
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/serial.h>
+
+#include <asm/vr41xx/cmbvr4133.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#define CONFIG_PORT(port)      ((port) ? 0x3f0 : 0x370)
+#define DATA_PORT(port)                ((port) ? 0x3f1 : 0x371)
+#define INDEX_PORT(port)       CONFIG_PORT(port)
+
+#define ENTER_CONFIG_MODE(port)                                \
+       do {                                            \
+               outb_p(0x51, CONFIG_PORT(port));        \
+               outb_p(0x23, CONFIG_PORT(port));        \
+       } while(0)
+
+#define SELECT_LOGICAL_DEVICE(port, dev_no)            \
+       do {                                            \
+               outb_p(0x07, INDEX_PORT(port));         \
+               outb_p((dev_no), DATA_PORT(port));      \
+       } while(0)
+
+#define WRITE_CONFIG_DATA(port,index,data)             \
+       do {                                            \
+               outb_p((index), INDEX_PORT(port));      \
+               outb_p((data), DATA_PORT(port));        \
+       } while(0)
+
+#define EXIT_CONFIG_MODE(port) outb(0xbb, CONFIG_PORT(port))
+
+#define PCI_CONFIG_ADDR        KSEG1ADDR(0x0f000c18)
+#define PCI_CONFIG_DATA        KSEG1ADDR(0x0f000c14)
+
+#ifdef CONFIG_BLK_DEV_FD
+
+void __devinit ali_m1535plus_fdc_init(int port)
+{
+       ENTER_CONFIG_MODE(port);
+       SELECT_LOGICAL_DEVICE(port, 0);         /* FDC */
+       WRITE_CONFIG_DATA(port, 0x30, 0x01);    /* FDC: enable */
+       WRITE_CONFIG_DATA(port, 0x60, 0x03);    /* I/O port base: 0x3f0 */
+       WRITE_CONFIG_DATA(port, 0x61, 0xf0);
+       WRITE_CONFIG_DATA(port, 0x70, 0x06);    /* IRQ: 6 */
+       WRITE_CONFIG_DATA(port, 0x74, 0x02);    /* DMA: channel 2 */
+       WRITE_CONFIG_DATA(port, 0xf0, 0x08);
+       WRITE_CONFIG_DATA(port, 0xf1, 0x00);
+       WRITE_CONFIG_DATA(port, 0xf2, 0xff);
+       WRITE_CONFIG_DATA(port, 0xf4, 0x00);
+       EXIT_CONFIG_MODE(port);
+}
+
+#endif
+
+void __devinit ali_m1535plus_parport_init(int port)
+{
+       ENTER_CONFIG_MODE(port);
+       SELECT_LOGICAL_DEVICE(port, 3);         /* Parallel Port */
+       WRITE_CONFIG_DATA(port, 0x30, 0x01);
+       WRITE_CONFIG_DATA(port, 0x60, 0x03);    /* I/O port base: 0x378 */
+       WRITE_CONFIG_DATA(port, 0x61, 0x78);
+       WRITE_CONFIG_DATA(port, 0x70, 0x07);    /* IRQ: 7 */
+       WRITE_CONFIG_DATA(port, 0x74, 0x04);    /* DMA: None */
+       WRITE_CONFIG_DATA(port, 0xf0, 0x8c);    /* IRQ polarity: Active Low */
+       WRITE_CONFIG_DATA(port, 0xf1, 0xc5);
+       EXIT_CONFIG_MODE(port);
+}
+
+void __devinit ali_m1535plus_keyboard_init(int port)
+{
+       ENTER_CONFIG_MODE(port);
+       SELECT_LOGICAL_DEVICE(port, 7);         /* KEYBOARD */
+       WRITE_CONFIG_DATA(port, 0x30, 0x01);    /* KEYBOARD: eable */
+       WRITE_CONFIG_DATA(port, 0x70, 0x01);    /* IRQ: 1 */
+       WRITE_CONFIG_DATA(port, 0x72, 0x0c);    /* PS/2 Mouse IRQ: 12 */
+       WRITE_CONFIG_DATA(port, 0xf0, 0x00);
+       EXIT_CONFIG_MODE(port);
+}
+
+void __devinit ali_m1535plus_hotkey_init(int port)
+{
+       ENTER_CONFIG_MODE(port);
+       SELECT_LOGICAL_DEVICE(port, 0xc);       /* HOTKEY */
+       WRITE_CONFIG_DATA(port, 0x30, 0x00);
+       WRITE_CONFIG_DATA(port, 0xf0, 0x35);
+       WRITE_CONFIG_DATA(port, 0xf1, 0x14);
+       WRITE_CONFIG_DATA(port, 0xf2, 0x11);
+       WRITE_CONFIG_DATA(port, 0xf3, 0x71);
+       WRITE_CONFIG_DATA(port, 0xf5, 0x05);
+       EXIT_CONFIG_MODE(port);
+}
+
+void ali_m1535plus_init(struct pci_dev *dev)
+{
+       pci_write_config_byte(dev, 0x40, 0x18); /* PCI Interface Control */
+       pci_write_config_byte(dev, 0x41, 0xc0); /* PS2 keyb & mouse enable */
+       pci_write_config_byte(dev, 0x42, 0x41); /* ISA bus cycle control */
+       pci_write_config_byte(dev, 0x43, 0x00); /* ISA bus cycle control 2 */
+       pci_write_config_byte(dev, 0x44, 0x5d); /* IDE enable & IRQ 14 */
+       pci_write_config_byte(dev, 0x45, 0x0b); /* PCI int polling mode */
+       pci_write_config_byte(dev, 0x47, 0x00); /* BIOS chip select control */
+
+       /* IRQ routing */
+       pci_write_config_byte(dev, 0x48, 0x03); /* INTA IRQ10, INTB disable */
+       pci_write_config_byte(dev, 0x49, 0x00); /* INTC and INTD disable */
+       pci_write_config_byte(dev, 0x4a, 0x00); /* INTE and INTF disable */
+       pci_write_config_byte(dev, 0x4b, 0x90); /* Audio IRQ11, Modem disable */
+
+       pci_write_config_word(dev, 0x50, 0x4000); /* Parity check IDE enable */
+       pci_write_config_word(dev, 0x52, 0x0000); /* USB & RTC disable */
+       pci_write_config_word(dev, 0x54, 0x0002); /* ??? no info */
+       pci_write_config_word(dev, 0x56, 0x0002); /* PCS1J signal disable */
+
+       pci_write_config_byte(dev, 0x59, 0x00); /* PCSDS */
+       pci_write_config_byte(dev, 0x5a, 0x00);
+       pci_write_config_byte(dev, 0x5b, 0x00);
+       pci_write_config_word(dev, 0x5c, 0x0000);
+       pci_write_config_byte(dev, 0x5e, 0x00);
+       pci_write_config_byte(dev, 0x5f, 0x00);
+       pci_write_config_word(dev, 0x60, 0x0000);
+
+       pci_write_config_byte(dev, 0x6c, 0x00);
+       pci_write_config_byte(dev, 0x6d, 0x48); /* ROM address mapping */
+       pci_write_config_byte(dev, 0x6e, 0x00); /* ??? what for? */
+
+       pci_write_config_byte(dev, 0x70, 0x12); /* Serial IRQ control */
+       pci_write_config_byte(dev, 0x71, 0xEF); /* DMA channel select */
+       pci_write_config_byte(dev, 0x72, 0x03); /* USB IDSEL */
+       pci_write_config_byte(dev, 0x73, 0x00); /* ??? no info */
+
+       /*
+        * IRQ setup ALi M5237 USB Host Controller
+        * IRQ: 9
+        */
+       pci_write_config_byte(dev, 0x74, 0x01); /* USB IRQ9 */
+
+       pci_write_config_byte(dev, 0x75, 0x1f); /* IDE2 IRQ 15  */
+       pci_write_config_byte(dev, 0x76, 0x80); /* ACPI disable */
+       pci_write_config_byte(dev, 0x77, 0x40); /* Modem disable */
+       pci_write_config_dword(dev, 0x78, 0x20000000); /* Pin select 2 */
+       pci_write_config_byte(dev, 0x7c, 0x00); /* Pin select 3 */
+       pci_write_config_byte(dev, 0x81, 0x00); /* ID read/write control */
+       pci_write_config_byte(dev, 0x90, 0x00); /* PCI PM block control */
+       pci_write_config_word(dev, 0xa4, 0x0000); /* PMSCR */
+
+#ifdef CONFIG_BLK_DEV_FD
+       ali_m1535plus_fdc_init(1);
+#endif
+
+       ali_m1535plus_keyboard_init(1);
+       ali_m1535plus_hotkey_init(1);
+}
+
+static inline void ali_config_writeb(u8 reg, u8 val, int devfn)
+{
+       u32 data;
+       int shift;
+
+       writel((1 << 16) | (devfn << 8) | (reg & 0xfc) | 1UL, PCI_CONFIG_ADDR);
+        data = readl(PCI_CONFIG_DATA);
+
+       shift = (reg & 3) << 3;
+       data &= ~(0xff << shift);
+       data |= (((u32)val) << shift);
+
+       writel(data, PCI_CONFIG_DATA);
+}
+
+static inline u8 ali_config_readb(u8 reg, int devfn)
+{
+       u32 data;
+
+       writel((1 << 16) | (devfn << 8) | (reg & 0xfc) | 1UL, PCI_CONFIG_ADDR);
+       data = readl(PCI_CONFIG_DATA);
+
+       return (u8)(data >> ((reg & 3) << 3));
+}
+
+static inline u16 ali_config_readw(u8 reg, int devfn)
+{
+       u32 data;
+
+       writel((1 << 16) | (devfn << 8) | (reg & 0xfc) | 1UL, PCI_CONFIG_ADDR);
+       data = readl(PCI_CONFIG_DATA);
+
+       return (u16)(data >> ((reg & 2) << 3));
+}
+
+int vr4133_rockhopper = 0;
+void __init ali_m5229_preinit(void)
+{
+       if (ali_config_readw(PCI_VENDOR_ID,16) == PCI_VENDOR_ID_AL &&
+           ali_config_readw(PCI_DEVICE_ID,16) == PCI_DEVICE_ID_AL_M1533) {
+               printk(KERN_INFO "Found an NEC Rockhopper \n");
+               vr4133_rockhopper = 1;
+               /*
+                * Enable ALi M5229 IDE Controller (both channels)
+                * IDSEL: A27
+                */
+               ali_config_writeb(0x58, 0x4c, 16);
+       }
+}
+
+void __init ali_m5229_init(struct pci_dev *dev)
+{
+       /*
+        * Enable Primary/Secondary Channel Cable Detect 40-Pin
+        */
+       pci_write_config_word(dev, 0x4a, 0xc023);
+
+       /*
+        * Set only the 3rd byteis for the master IDE's cycle and
+        * enable Internal IDE Function
+        */
+       pci_write_config_byte(dev, 0x50, 0x23); /* Class code attr register */
+
+       pci_write_config_byte(dev, 0x09, 0xff); /* Set native mode & stuff */
+       pci_write_config_byte(dev, 0x52, 0x00); /* use timing registers */
+       pci_write_config_byte(dev, 0x58, 0x02); /* Primary addr setup timing */
+       pci_write_config_byte(dev, 0x59, 0x22); /* Primary cmd block timing */
+       pci_write_config_byte(dev, 0x5a, 0x22); /* Pr drv 0 R/W timing */
+       pci_write_config_byte(dev, 0x5b, 0x22); /* Pr drv 1 R/W timing */
+       pci_write_config_byte(dev, 0x5c, 0x02); /* Sec addr setup timing */
+       pci_write_config_byte(dev, 0x5d, 0x22); /* Sec cmd block timing */
+       pci_write_config_byte(dev, 0x5e, 0x22); /* Sec drv 0 R/W timing */
+       pci_write_config_byte(dev, 0x5f, 0x22); /* Sec drv 1 R/W timing */
+       pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
+       pci_write_config_word(dev, PCI_COMMAND,
+                                  PCI_COMMAND_PARITY | PCI_COMMAND_MASTER |
+                                  PCI_COMMAND_IO);
+}
+
diff --git a/arch/mips/vr41xx/nec-cmbvr4133/setup.c b/arch/mips/vr41xx/nec-cmbvr4133/setup.c
new file mode 100644 (file)
index 0000000..b2ffa1d
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * arch/mips/vr41xx/nec-cmbvr4133/setup.c
+ *
+ * Setup for the NEC CMB-VR4133.
+ *
+ * Author: Yoichi Yuasa <yyuasa@mvista.com, or source@mvista.com> and
+ *         Alex Sapkov <asapkov@ru.mvista.com>
+ *
+ * 2001-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.
+ *
+ * Support for CMBVR4133 board in 2.6
+ * Author: Manish Lachwani (mlachwani@mvista.com)
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/ide.h>
+#include <linux/ioport.h>
+
+#include <asm/reboot.h>
+#include <asm/time.h>
+#include <asm/vr41xx/cmbvr4133.h>
+#include <asm/bootinfo.h>
+
+#ifdef CONFIG_MTD
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+static struct mtd_partition cmbvr4133_mtd_parts[] = {
+       {
+               .name =         "User FS",
+               .size =         0x1be0000,
+               .offset =       0,
+               .mask_flags =   0,
+       },
+       {
+               .name =         "PMON",
+               .size =         0x140000,
+               .offset =       MTDPART_OFS_APPEND,
+               .mask_flags =   MTD_WRITEABLE,  /* force read-only */
+       },
+       {
+               .name =         "User FS2",
+               .size =         MTDPART_SIZ_FULL,
+               .offset =       MTDPART_OFS_APPEND,
+               .mask_flags =   0,
+       }
+};
+
+#define number_partitions (sizeof(cmbvr4133_mtd_parts)/sizeof(struct mtd_partition))
+#endif
+
+extern void (*late_time_init)(void);
+
+static void __init vr4133_serial_init(void)
+{
+       vr41xx_select_siu_interface(SIU_RS232C, IRDA_NONE);
+       vr41xx_siu_init();
+       vr41xx_dsiu_init();
+}
+
+extern void i8259_init(void);
+
+static int __init nec_cmbvr4133_setup(void)
+{
+#ifdef CONFIG_ROCKHOPPER
+       extern void disable_pcnet(void);
+
+       disable_pcnet();
+#endif
+       set_io_port_base(KSEG1ADDR(0x16000000));
+
+       mips_machgroup = MACH_GROUP_NEC_VR41XX;
+       mips_machtype = MACH_NEC_CMBVR4133;
+
+       late_time_init = vr4133_serial_init;
+
+#ifdef CONFIG_PCI
+#ifdef CONFIG_ROCKHOPPER
+       ali_m5229_preinit();
+#endif
+#endif
+
+#ifdef CONFIG_ROCKHOPPER
+       rockhopper_init_irq();
+#endif
+
+#ifdef CONFIG_MTD
+       /* we use generic physmap mapping driver and we use partitions */
+       physmap_configure(0x1C000000, 0x02000000, 4, NULL);
+       physmap_set_partitions(cmbvr4133_mtd_parts, number_partitions);
+#endif
+
+       /* 128 MB memory support */
+       add_memory_region(0, 0x08000000, BOOT_MEM_RAM);
+
+#ifdef CONFIG_ROCKHOPPER
+       i8259_init();
+#endif
+       return 0;
+}
+
+early_initcall(nec_cmbvr4133_setup);
diff --git a/arch/parisc/install.sh b/arch/parisc/install.sh
new file mode 100644 (file)
index 0000000..9632b3e
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# arch/parisc/install.sh, derived from arch/i386/boot/install.sh
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995 by Linus Torvalds
+#
+# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
+#
+# "make install" script for i386 architecture
+#
+# Arguments:
+#   $1 - kernel version
+#   $2 - kernel image file
+#   $3 - kernel map file
+#   $4 - default install path (blank if root directory)
+#
+
+# User may have a custom install script
+
+if [ -x ~/bin/installkernel ]; then exec ~/bin/installkernel "$@"; fi
+if [ -x /sbin/installkernel ]; then exec /sbin/installkernel "$@"; fi
+
+# Default install
+
+if [ -f $4/vmlinux ]; then
+       mv $4/vmlinux $4/vmlinux.old
+fi
+
+if [ -f $4/System.map ]; then
+       mv $4/System.map $4/System.old
+fi
+
+cat $2 > $4/vmlinux
+cp $3 $4/System.map
diff --git a/arch/parisc/kernel/topology.c b/arch/parisc/kernel/topology.c
new file mode 100644 (file)
index 0000000..ac2a406
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * arch/parisc/kernel/topology.c - Populate driverfs with topology information
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+
+static struct cpu cpu_devices[NR_CPUS];
+
+static int __init topology_init(void)
+{
+       struct node *parent = NULL;
+       int num;
+
+       for_each_present_cpu(num) {
+               register_cpu(&cpu_devices[num], num, parent);
+       }
+       return 0;
+}
+
+subsys_initcall(topology_init);
diff --git a/arch/parisc/lib/fixup.S b/arch/parisc/lib/fixup.S
new file mode 100644 (file)
index 0000000..134f0cd
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Linux/PA-RISC Project (http://www.parisc-linux.org/)
+ *
+ *  Copyright (C) 2004  Randolph Chung <tausq@debian.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, 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.
+ * 
+ * Fixup routines for kernel exception handling.
+ */
+#include <linux/config.h>
+#include <asm/offsets.h>
+#include <asm/assembly.h>
+#include <asm/errno.h>
+
+#ifdef CONFIG_SMP
+       .macro  get_fault_ip t1 t2
+       addil LT%__per_cpu_offset,%r27
+       LDREG RT%__per_cpu_offset(%r1),\t1
+       /* t2 = smp_processor_id() */
+       mfctl 30,\t2
+       ldw TI_CPU(\t2),\t2
+#ifdef __LP64__
+       extrd,u \t2,63,32,\t2
+#endif
+       /* t2 = &__per_cpu_offset[smp_processor_id()]; */
+       LDREG,s \t2(\t1),\t2 
+       addil LT%per_cpu__exception_data,%r27
+       LDREG RT%per_cpu__exception_data(%r1),\t1
+       /* t1 = &__get_cpu_var(exception_data) */
+       add,l \t1,\t2,\t1
+       /* t1 = t1->fault_ip */
+       LDREG EXCDATA_IP(\t1), \t1
+       .endm
+#else
+       .macro  get_fault_ip t1 t2
+       /* t1 = &__get_cpu_var(exception_data) */
+       addil LT%per_cpu__exception_data,%r27
+       LDREG RT%per_cpu__exception_data(%r1),\t2
+       /* t1 = t2->fault_ip */
+       LDREG EXCDATA_IP(\t2), \t1
+       .endm
+#endif
+
+       .text
+       .section .fixup, "ax"
+
+       /* get_user() fixups, store -EFAULT in r8, and 0 in r9 */
+       .export fixup_get_user_skip_1
+fixup_get_user_skip_1:
+       get_fault_ip %r1,%r8
+       ldo 4(%r1), %r1
+       ldi -EFAULT, %r8
+       bv %r0(%r1)
+       copy %r0, %r9
+
+       .export fixup_get_user_skip_2
+fixup_get_user_skip_2:
+       get_fault_ip %r1,%r8
+       ldo 8(%r1), %r1
+       ldi -EFAULT, %r8
+       bv %r0(%r1)
+       copy %r0, %r9
+
+       /* put_user() fixups, store -EFAULT in r8 */
+       .export fixup_put_user_skip_1
+fixup_put_user_skip_1:
+       get_fault_ip %r1,%r8
+       ldo 4(%r1), %r1
+       bv %r0(%r1)
+       ldi -EFAULT, %r8
+
+       .export fixup_put_user_skip_2
+fixup_put_user_skip_2:
+       get_fault_ip %r1,%r8
+       ldo 8(%r1), %r1
+       bv %r0(%r1)
+       ldi -EFAULT, %r8
diff --git a/arch/parisc/lib/iomap.c b/arch/parisc/lib/iomap.c
new file mode 100644 (file)
index 0000000..290a62e
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * iomap.c - Implement iomap interface for PA-RISC
+ * Copyright (c) 2004 Matthew Wilcox
+ */
+
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+/*
+ * The iomap space on 32-bit PA-RISC is intended to look like this:
+ * 00000000-7fffffff virtual mapped IO
+ * 80000000-8fffffff ISA/EISA port space that can't be virtually mapped
+ * 90000000-9fffffff Dino port space
+ * a0000000-afffffff Astro port space
+ * b0000000-bfffffff PAT port space
+ * c0000000-cfffffff non-swapped memory IO
+ * f0000000-ffffffff legacy IO memory pointers
+ *
+ * For the moment, here's what it looks like:
+ * 80000000-8fffffff All ISA/EISA port space
+ * f0000000-ffffffff legacy IO memory pointers
+ *
+ * On 64-bit, everything is extended, so:
+ * 8000000000000000-8fffffffffffffff All ISA/EISA port space
+ * f000000000000000-ffffffffffffffff legacy IO memory pointers
+ */
+
+/*
+ * Technically, this should be 'if (VMALLOC_START < addr < VMALLOC_END),
+ * but that's slow and we know it'll be within the first 2GB.
+ */
+#ifdef CONFIG_64BIT
+#define INDIRECT_ADDR(addr)    (((unsigned long)(addr) & 1UL<<63) != 0)
+#define ADDR_TO_REGION(addr)    (((unsigned long)addr >> 60) & 7)
+#define IOPORT_MAP_BASE                (8UL << 60)
+#else
+#define INDIRECT_ADDR(addr)     (((unsigned long)(addr) & 1UL<<31) != 0)
+#define ADDR_TO_REGION(addr)    (((unsigned long)addr >> 28) & 7)
+#define IOPORT_MAP_BASE                (8UL << 28)
+#endif
+
+struct iomap_ops {
+       unsigned int (*read8)(void __iomem *);
+       unsigned int (*read16)(void __iomem *);
+       unsigned int (*read32)(void __iomem *);
+       void (*write8)(u8, void __iomem *);
+       void (*write16)(u16, void __iomem *);
+       void (*write32)(u32, void __iomem *);
+       void (*read8r)(void __iomem *, void *, unsigned long);
+       void (*read16r)(void __iomem *, void *, unsigned long);
+       void (*read32r)(void __iomem *, void *, unsigned long);
+       void (*write8r)(void __iomem *, const void *, unsigned long);
+       void (*write16r)(void __iomem *, const void *, unsigned long);
+       void (*write32r)(void __iomem *, const void *, unsigned long);
+};
+
+/* Generic ioport ops.  To be replaced later by specific dino/elroy/wax code */
+
+#define ADDR2PORT(addr) ((unsigned long __force)(addr) & 0xffffff)
+
+static unsigned int ioport_read8(void __iomem *addr)
+{
+       return inb(ADDR2PORT(addr));
+}
+
+static unsigned int ioport_read16(void __iomem *addr)
+{
+       return inw(ADDR2PORT(addr));
+}
+
+static unsigned int ioport_read32(void __iomem *addr)
+{
+       return inl(ADDR2PORT(addr));
+}
+
+static void ioport_write8(u8 datum, void __iomem *addr)
+{
+       outb(datum, ADDR2PORT(addr));
+}
+
+static void ioport_write16(u16 datum, void __iomem *addr)
+{
+       outw(datum, ADDR2PORT(addr));
+}
+
+static void ioport_write32(u32 datum, void __iomem *addr)
+{
+       outl(datum, ADDR2PORT(addr));
+}
+
+static void ioport_read8r(void __iomem *addr, void *dst, unsigned long count)
+{
+       insb(ADDR2PORT(addr), dst, count);
+}
+
+static void ioport_read16r(void __iomem *addr, void *dst, unsigned long count)
+{
+       insw(ADDR2PORT(addr), dst, count);
+}
+
+static void ioport_read32r(void __iomem *addr, void *dst, unsigned long count)
+{
+       insl(ADDR2PORT(addr), dst, count);
+}
+
+static void ioport_write8r(void __iomem *addr, const void *s, unsigned long n)
+{
+       outsb(ADDR2PORT(addr), s, n);
+}
+
+static void ioport_write16r(void __iomem *addr, const void *s, unsigned long n)
+{
+       outsw(ADDR2PORT(addr), s, n);
+}
+
+static void ioport_write32r(void __iomem *addr, const void *s, unsigned long n)
+{
+       outsl(ADDR2PORT(addr), s, n);
+}
+
+static const struct iomap_ops ioport_ops = {
+       ioport_read8,
+       ioport_read16,
+       ioport_read32,
+       ioport_write8,
+       ioport_write16,
+       ioport_write32,
+       ioport_read8r,
+       ioport_read16r,
+       ioport_read32r,
+       ioport_write8r,
+       ioport_write16r,
+       ioport_write32r,
+};
+
+/* Legacy I/O memory ops */
+
+static unsigned int iomem_read8(void __iomem *addr)
+{
+       return readb(addr);
+}
+
+static unsigned int iomem_read16(void __iomem *addr)
+{
+       return readw(addr);
+}
+
+static unsigned int iomem_read32(void __iomem *addr)
+{
+       return readl(addr);
+}
+
+static void iomem_write8(u8 datum, void __iomem *addr)
+{
+       writeb(datum, addr);
+}
+
+static void iomem_write16(u16 datum, void __iomem *addr)
+{
+       writew(datum, addr);
+}
+
+static void iomem_write32(u32 datum, void __iomem *addr)
+{
+       writel(datum, addr);
+}
+
+static void iomem_read8r(void __iomem *addr, void *dst, unsigned long count)
+{
+       while (count--) {
+               *(u8 *)dst = __raw_readb(addr);
+               dst++;
+       }
+}
+
+static void iomem_read16r(void __iomem *addr, void *dst, unsigned long count)
+{
+       while (count--) {
+               *(u16 *)dst = __raw_readw(addr);
+               dst += 2;
+       }
+}
+
+static void iomem_read32r(void __iomem *addr, void *dst, unsigned long count)
+{
+       while (count--) {
+               *(u32 *)dst = __raw_readl(addr);
+               dst += 4;
+       }
+}
+
+static void iomem_write8r(void __iomem *addr, const void *s, unsigned long n)
+{
+       while (n--) {
+               __raw_writeb(*(u8 *)s, addr);
+               s++;
+       }
+}
+
+static void iomem_write16r(void __iomem *addr, const void *s, unsigned long n)
+{
+       while (n--) {
+               __raw_writew(*(u16 *)s, addr);
+               s += 2;
+       }
+}
+
+static void iomem_write32r(void __iomem *addr, const void *s, unsigned long n)
+{
+       while (n--) {
+               __raw_writel(*(u32 *)s, addr);
+               s += 4;
+       }
+}
+
+static const struct iomap_ops iomem_ops = {
+       iomem_read8,
+       iomem_read16,
+       iomem_read32,
+       iomem_write8,
+       iomem_write16,
+       iomem_write32,
+       iomem_read8r,
+       iomem_read16r,
+       iomem_read32r,
+       iomem_write8r,
+       iomem_write16r,
+       iomem_write32r,
+};
+
+const struct iomap_ops *iomap_ops[8] = {
+       [0] = &ioport_ops,
+#ifdef CONFIG_DEBUG_IOREMAP
+       [6] = &iomem_ops,
+#else
+       [7] = &iomem_ops
+#endif
+};
+
+
+unsigned int ioread8(void __iomem *addr)
+{
+       if (unlikely(INDIRECT_ADDR(addr)))
+               return iomap_ops[ADDR_TO_REGION(addr)]->read8(addr);
+       return *((u8 *)addr);
+}
+
+unsigned int ioread16(void __iomem *addr)
+{
+       if (unlikely(INDIRECT_ADDR(addr)))
+               return iomap_ops[ADDR_TO_REGION(addr)]->read16(addr);
+       return le16_to_cpup((u16 *)addr);
+}
+
+unsigned int ioread32(void __iomem *addr)
+{
+       if (unlikely(INDIRECT_ADDR(addr)))
+               return iomap_ops[ADDR_TO_REGION(addr)]->read32(addr);
+       return le32_to_cpup((u32 *)addr);
+}
+
+void iowrite8(u8 datum, void __iomem *addr)
+{
+       if (unlikely(INDIRECT_ADDR(addr))) {
+               iomap_ops[ADDR_TO_REGION(addr)]->write8(datum, addr);
+       } else {
+               *((u8 *)addr) = datum;
+       }
+}
+
+void iowrite16(u16 datum, void __iomem *addr)
+{
+       if (unlikely(INDIRECT_ADDR(addr))) {
+               iomap_ops[ADDR_TO_REGION(addr)]->write16(datum, addr);
+       } else {
+               *((u16 *)addr) = cpu_to_le16(datum);
+       }
+}
+
+void iowrite32(u32 datum, void __iomem *addr)
+{
+       if (unlikely(INDIRECT_ADDR(addr))) {
+               iomap_ops[ADDR_TO_REGION(addr)]->write32(datum, addr);
+       } else {
+               *((u32 *)addr) = cpu_to_le32(datum);
+       }
+}
+
+/* Repeating interfaces */
+
+void ioread8_rep(void __iomem *addr, void *dst, unsigned long count)
+{
+       if (unlikely(INDIRECT_ADDR(addr))) {
+               iomap_ops[ADDR_TO_REGION(addr)]->read8r(addr, dst, count);
+       } else {
+               while (count--) {
+                       *(u8 *)dst = *(u8 *)addr;
+                       dst++;
+               }
+       }
+}
+
+void ioread16_rep(void __iomem *addr, void *dst, unsigned long count)
+{
+       if (unlikely(INDIRECT_ADDR(addr))) {
+               iomap_ops[ADDR_TO_REGION(addr)]->read16r(addr, dst, count);
+       } else {
+               while (count--) {
+                       *(u16 *)dst = *(u16 *)addr;
+                       dst += 2;
+               }
+       }
+}
+
+void ioread32_rep(void __iomem *addr, void *dst, unsigned long count)
+{
+       if (unlikely(INDIRECT_ADDR(addr))) {
+               iomap_ops[ADDR_TO_REGION(addr)]->read32r(addr, dst, count);
+       } else {
+               while (count--) {
+                       *(u32 *)dst = *(u32 *)addr;
+                       dst += 4;
+               }
+       }
+}
+
+void iowrite8_rep(void __iomem *addr, const void *src, unsigned long count)
+{
+       if (unlikely(INDIRECT_ADDR(addr))) {
+               iomap_ops[ADDR_TO_REGION(addr)]->write8r(addr, src, count);
+       } else {
+               while (count--) {
+                       *(u8 *)addr = *(u8 *)src;
+                       src++;
+               }
+       }
+}
+
+void iowrite16_rep(void __iomem *addr, const void *src, unsigned long count)
+{
+       if (unlikely(INDIRECT_ADDR(addr))) {
+               iomap_ops[ADDR_TO_REGION(addr)]->write16r(addr, src, count);
+       } else {
+               while (count--) {
+                       *(u16 *)addr = *(u16 *)src;
+                       src += 2;
+               }
+       }
+}
+
+void iowrite32_rep(void __iomem *addr, const void *src, unsigned long count)
+{
+       if (unlikely(INDIRECT_ADDR(addr))) {
+               iomap_ops[ADDR_TO_REGION(addr)]->write32r(addr, src, count);
+       } else {
+               while (count--) {
+                       *(u32 *)addr = *(u32 *)src;
+                       src += 4;
+               }
+       }
+}
+
+/* Mapping interfaces */
+
+void __iomem *ioport_map(unsigned long port, unsigned int nr)
+{
+       return (void __iomem *)(IOPORT_MAP_BASE | port);
+}
+
+void ioport_unmap(void __iomem *addr)
+{
+       if (!INDIRECT_ADDR(addr)) {
+               iounmap(addr);
+       }
+}
+
+/* Create a virtual mapping cookie for a PCI BAR (memory or IO) */
+void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
+{
+       unsigned long start = pci_resource_start(dev, bar);
+       unsigned long len = pci_resource_len(dev, bar);
+       unsigned long flags = pci_resource_flags(dev, bar);
+
+       if (!len || !start)
+               return NULL;
+       if (maxlen && len > maxlen)
+               len = maxlen;
+       if (flags & IORESOURCE_IO)
+               return ioport_map(start, len);
+       if (flags & IORESOURCE_MEM) {
+               if (flags & IORESOURCE_CACHEABLE)
+                       return ioremap(start, len);
+               return ioremap_nocache(start, len);
+       }
+       /* What? */
+       return NULL;
+}
+
+void pci_iounmap(struct pci_dev *dev, void __iomem * addr)
+{
+       if (!INDIRECT_ADDR(addr)) {
+               iounmap(addr);
+       }
+}
+
+EXPORT_SYMBOL(ioread8);
+EXPORT_SYMBOL(ioread16);
+EXPORT_SYMBOL(ioread32);
+EXPORT_SYMBOL(iowrite8);
+EXPORT_SYMBOL(iowrite16);
+EXPORT_SYMBOL(iowrite32);
+EXPORT_SYMBOL(ioread8_rep);
+EXPORT_SYMBOL(ioread16_rep);
+EXPORT_SYMBOL(ioread32_rep);
+EXPORT_SYMBOL(iowrite8_rep);
+EXPORT_SYMBOL(iowrite16_rep);
+EXPORT_SYMBOL(iowrite32_rep);
+EXPORT_SYMBOL(ioport_map);
+EXPORT_SYMBOL(ioport_unmap);
+EXPORT_SYMBOL(pci_iomap);
+EXPORT_SYMBOL(pci_iounmap);
diff --git a/arch/parisc/lib/memcpy.c b/arch/parisc/lib/memcpy.c
new file mode 100644 (file)
index 0000000..e743ca5
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ *    Optimized memory copy routines.
+ *
+ *    Copyright (C) 2004 Randolph Chung <tausq@debian.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, 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.
+ *
+ *    Portions derived from the GNU C Library
+ *    Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc.
+ *
+ * Several strategies are tried to try to get the best performance for various
+ * conditions. In the optimal case, we copy 64-bytes in an unrolled loop using 
+ * fp regs. This is followed by loops that copy 32- or 16-bytes at a time using
+ * general registers.  Unaligned copies are handled either by aligning the 
+ * destination and then using shift-and-write method, or in a few cases by 
+ * falling back to a byte-at-a-time copy.
+ *
+ * I chose to implement this in C because it is easier to maintain and debug,
+ * and in my experiments it appears that the C code generated by gcc (3.3/3.4
+ * at the time of writing) is fairly optimal. Unfortunately some of the 
+ * semantics of the copy routine (exception handling) is difficult to express
+ * in C, so we have to play some tricks to get it to work.
+ *
+ * All the loads and stores are done via explicit asm() code in order to use
+ * the right space registers. 
+ * 
+ * Testing with various alignments and buffer sizes shows that this code is 
+ * often >10x faster than a simple byte-at-a-time copy, even for strangely
+ * aligned operands. It is interesting to note that the glibc version
+ * of memcpy (written in C) is actually quite fast already. This routine is 
+ * able to beat it by 30-40% for aligned copies because of the loop unrolling, 
+ * but in some cases the glibc version is still slightly faster. This lends 
+ * more credibility that gcc can generate very good code as long as we are 
+ * careful.
+ *
+ * TODO:
+ * - cache prefetching needs more experimentation to get optimal settings
+ * - try not to use the post-increment address modifiers; they create additional
+ *   interlocks
+ * - replace byte-copy loops with stybs sequences
+ */
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/compiler.h>
+#include <asm/uaccess.h>
+#define s_space "%%sr1"
+#define d_space "%%sr2"
+#else
+#include "memcpy.h"
+#define s_space "%%sr0"
+#define d_space "%%sr0"
+#define pa_memcpy new2_copy
+#endif
+
+DECLARE_PER_CPU(struct exception_data, exception_data);
+
+#define preserve_branch(label) do {                                    \
+       volatile int dummy;                                             \
+       /* The following branch is never taken, it's just here to  */   \
+       /* prevent gcc from optimizing away our exception code. */      \
+       if (unlikely(dummy != dummy))                                   \
+               goto label;                                             \
+} while (0)
+
+#define get_user_space() (segment_eq(get_fs(), KERNEL_DS) ? 0 : mfsp(3))
+#define get_kernel_space() (0)
+
+#define MERGE(w0, sh_1, w1, sh_2)  ({                                  \
+       unsigned int _r;                                                \
+       asm volatile (                                                  \
+       "mtsar %3\n"                                                    \
+       "shrpw %1, %2, %%sar, %0\n"                                     \
+       : "=r"(_r)                                                      \
+       : "r"(w0), "r"(w1), "r"(sh_2)                                   \
+       );                                                              \
+       _r;                                                             \
+})
+#define THRESHOLD      16
+
+#ifdef DEBUG_MEMCPY
+#define DPRINTF(fmt, args...) do { printk(KERN_DEBUG "%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__ ); printk(KERN_DEBUG fmt, ##args ); } while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+#ifndef __LP64__
+#define EXC_WORD ".word"
+#else
+#define EXC_WORD ".dword"
+#endif
+
+#define def_load_ai_insn(_insn,_sz,_tt,_s,_a,_t,_e)    \
+       __asm__ __volatile__ (                          \
+       "1:\t" #_insn ",ma " #_sz "(" _s ",%1), %0\n"   \
+       "\t.section __ex_table,\"aw\"\n"                \
+       "\t" EXC_WORD "\t1b\n"                          \
+       "\t" EXC_WORD "\t" #_e "\n"                     \
+       "\t.previous\n"                                 \
+       : _tt(_t), "+r"(_a)                             \
+       : "1"(_a)                                       \
+       : "r8")
+
+#define def_store_ai_insn(_insn,_sz,_tt,_s,_a,_t,_e)   \
+       __asm__ __volatile__ (                          \
+       "1:\t" #_insn ",ma %1, " #_sz "(" _s ",%0)\n"   \
+       "\t.section __ex_table,\"aw\"\n"                \
+       "\t" EXC_WORD "\t1b\n"                          \
+       "\t" EXC_WORD "\t" #_e "\n"                     \
+       "\t.previous\n"                                 \
+       : "+r"(_a)                                      \
+       : _tt(_t), "0"(_a)                              \
+       : "r8")
+
+#define ldbma(_s, _a, _t, _e) def_load_ai_insn(ldbs,1,"=r",_s,_a,_t,_e)
+#define stbma(_s, _t, _a, _e) def_store_ai_insn(stbs,1,"r",_s,_a,_t,_e)
+#define ldwma(_s, _a, _t, _e) def_load_ai_insn(ldw,4,"=r",_s,_a,_t,_e)
+#define stwma(_s, _t, _a, _e) def_store_ai_insn(stw,4,"r",_s,_a,_t,_e)
+#define flddma(_s, _a, _t, _e) def_load_ai_insn(fldd,8,"=f",_s,_a,_t,_e)
+#define fstdma(_s, _t, _a, _e) def_store_ai_insn(fstd,8,"f",_s,_a,_t,_e)
+
+#define def_load_insn(_insn,_tt,_s,_o,_a,_t,_e)        \
+       __asm__ __volatile__ (                          \
+       "1:\t" #_insn " " #_o "(" _s ",%1), %0\n"       \
+       "\t.section __ex_table,\"aw\"\n"                \
+       "\t" EXC_WORD "\t1b\n"                          \
+       "\t" EXC_WORD "\t" #_e "\n"                     \
+       "\t.previous\n"                                 \
+       : _tt(_t)                                       \
+       : "r"(_a)                                       \
+       : "r8")
+
+#define def_store_insn(_insn,_tt,_s,_t,_o,_a,_e)       \
+       __asm__ __volatile__ (                          \
+       "1:\t" #_insn " %0, " #_o "(" _s ",%1)\n"       \
+       "\t.section __ex_table,\"aw\"\n"                \
+       "\t" EXC_WORD "\t1b\n"                          \
+       "\t" EXC_WORD "\t" #_e "\n"                     \
+       "\t.previous\n"                                 \
+       :                                               \
+       : _tt(_t), "r"(_a)                              \
+       : "r8")
+
+#define ldw(_s,_o,_a,_t,_e)    def_load_insn(ldw,"=r",_s,_o,_a,_t,_e)
+#define stw(_s,_t,_o,_a,_e)    def_store_insn(stw,"r",_s,_t,_o,_a,_e)
+
+#ifdef  CONFIG_PREFETCH
+extern inline void prefetch_src(const void *addr)
+{
+       __asm__("ldw 0(" s_space ",%0), %%r0" : : "r" (addr));
+}
+
+extern inline void prefetch_dst(const void *addr)
+{
+       __asm__("ldd 0(" d_space ",%0), %%r0" : : "r" (addr));
+}
+#else
+#define prefetch_src(addr)
+#define prefetch_dst(addr)
+#endif
+
+/* Copy from a not-aligned src to an aligned dst, using shifts. Handles 4 words
+ * per loop.  This code is derived from glibc. 
+ */
+static inline unsigned long copy_dstaligned(unsigned long dst, unsigned long src, unsigned long len, unsigned long o_dst, unsigned long o_src, unsigned long o_len)
+{
+       /* gcc complains that a2 and a3 may be uninitialized, but actually
+        * they cannot be.  Initialize a2/a3 to shut gcc up.
+        */
+       register unsigned int a0, a1, a2 = 0, a3 = 0;
+       int sh_1, sh_2;
+       struct exception_data *d;
+
+       /* prefetch_src((const void *)src); */
+
+       /* Calculate how to shift a word read at the memory operation
+          aligned srcp to make it aligned for copy.  */
+       sh_1 = 8 * (src % sizeof(unsigned int));
+       sh_2 = 8 * sizeof(unsigned int) - sh_1;
+
+       /* Make src aligned by rounding it down.  */
+       src &= -sizeof(unsigned int);
+
+       switch (len % 4)
+       {
+               case 2:
+                       /* a1 = ((unsigned int *) src)[0];
+                          a2 = ((unsigned int *) src)[1]; */
+                       ldw(s_space, 0, src, a1, cda_ldw_exc);
+                       ldw(s_space, 4, src, a2, cda_ldw_exc);
+                       src -= 1 * sizeof(unsigned int);
+                       dst -= 3 * sizeof(unsigned int);
+                       len += 2;
+                       goto do1;
+               case 3:
+                       /* a0 = ((unsigned int *) src)[0];
+                          a1 = ((unsigned int *) src)[1]; */
+                       ldw(s_space, 0, src, a0, cda_ldw_exc);
+                       ldw(s_space, 4, src, a1, cda_ldw_exc);
+                       src -= 0 * sizeof(unsigned int);
+                       dst -= 2 * sizeof(unsigned int);
+                       len += 1;
+                       goto do2;
+               case 0:
+                       if (len == 0)
+                               return 0;
+                       /* a3 = ((unsigned int *) src)[0];
+                          a0 = ((unsigned int *) src)[1]; */
+                       ldw(s_space, 0, src, a3, cda_ldw_exc);
+                       ldw(s_space, 4, src, a0, cda_ldw_exc);
+                       src -=-1 * sizeof(unsigned int);
+                       dst -= 1 * sizeof(unsigned int);
+                       len += 0;
+                       goto do3;
+               case 1:
+                       /* a2 = ((unsigned int *) src)[0];
+                          a3 = ((unsigned int *) src)[1]; */
+                       ldw(s_space, 0, src, a2, cda_ldw_exc);
+                       ldw(s_space, 4, src, a3, cda_ldw_exc);
+                       src -=-2 * sizeof(unsigned int);
+                       dst -= 0 * sizeof(unsigned int);
+                       len -= 1;
+                       if (len == 0)
+                               goto do0;
+                       goto do4;                       /* No-op.  */
+       }
+
+       do
+       {
+               /* prefetch_src((const void *)(src + 4 * sizeof(unsigned int))); */
+do4:
+               /* a0 = ((unsigned int *) src)[0]; */
+               ldw(s_space, 0, src, a0, cda_ldw_exc);
+               /* ((unsigned int *) dst)[0] = MERGE (a2, sh_1, a3, sh_2); */
+               stw(d_space, MERGE (a2, sh_1, a3, sh_2), 0, dst, cda_stw_exc);
+do3:
+               /* a1 = ((unsigned int *) src)[1]; */
+               ldw(s_space, 4, src, a1, cda_ldw_exc);
+               /* ((unsigned int *) dst)[1] = MERGE (a3, sh_1, a0, sh_2); */
+               stw(d_space, MERGE (a3, sh_1, a0, sh_2), 4, dst, cda_stw_exc);
+do2:
+               /* a2 = ((unsigned int *) src)[2]; */
+               ldw(s_space, 8, src, a2, cda_ldw_exc);
+               /* ((unsigned int *) dst)[2] = MERGE (a0, sh_1, a1, sh_2); */
+               stw(d_space, MERGE (a0, sh_1, a1, sh_2), 8, dst, cda_stw_exc);
+do1:
+               /* a3 = ((unsigned int *) src)[3]; */
+               ldw(s_space, 12, src, a3, cda_ldw_exc);
+               /* ((unsigned int *) dst)[3] = MERGE (a1, sh_1, a2, sh_2); */
+               stw(d_space, MERGE (a1, sh_1, a2, sh_2), 12, dst, cda_stw_exc);
+
+               src += 4 * sizeof(unsigned int);
+               dst += 4 * sizeof(unsigned int);
+               len -= 4;
+       }
+       while (len != 0);
+
+do0:
+       /* ((unsigned int *) dst)[0] = MERGE (a2, sh_1, a3, sh_2); */
+       stw(d_space, MERGE (a2, sh_1, a3, sh_2), 0, dst, cda_stw_exc);
+
+       preserve_branch(handle_load_error);
+       preserve_branch(handle_store_error);
+
+       return 0;
+
+handle_load_error:
+       __asm__ __volatile__ ("cda_ldw_exc:\n");
+       d = &__get_cpu_var(exception_data);
+       DPRINTF("cda_ldw_exc: o_len=%lu fault_addr=%lu o_src=%lu ret=%lu\n",
+               o_len, d->fault_addr, o_src, o_len - d->fault_addr + o_src);
+       return o_len * 4 - d->fault_addr + o_src;
+
+handle_store_error:
+       __asm__ __volatile__ ("cda_stw_exc:\n");
+       d = &__get_cpu_var(exception_data);
+       DPRINTF("cda_stw_exc: o_len=%lu fault_addr=%lu o_dst=%lu ret=%lu\n",
+               o_len, d->fault_addr, o_dst, o_len - d->fault_addr + o_dst);
+       return o_len * 4 - d->fault_addr + o_dst;
+}
+
+
+/* Returns 0 for success, otherwise, returns number of bytes not transferred. */
+unsigned long pa_memcpy(void *dstp, const void *srcp, unsigned long len)
+{
+       register unsigned long src, dst, t1, t2, t3;
+       register char *pcs, *pcd;
+       register unsigned int *pws, *pwd;
+       register double *pds, *pdd;
+       unsigned long ret = 0;
+       unsigned long o_dst, o_src, o_len;
+       struct exception_data *d;
+
+       src = (unsigned long)srcp;
+       dst = (unsigned long)dstp;
+       pcs = (unsigned char *)srcp;
+       pcd = (unsigned char *)dstp;
+
+       o_dst = dst; o_src = src; o_len = len;
+
+       /* prefetch_src((const void *)srcp); */
+
+       if (len < THRESHOLD)
+               goto byte_copy;
+
+       /* Check alignment */
+       t1 = (src ^ dst);
+       if (unlikely(t1 & (sizeof(double)-1)))
+               goto unaligned_copy;
+
+       /* src and dst have same alignment. */
+
+       /* Copy bytes till we are double-aligned. */
+       t2 = src & (sizeof(double) - 1);
+       if (unlikely(t2 != 0)) {
+               t2 = sizeof(double) - t2;
+               while (t2 && len) {
+                       /* *pcd++ = *pcs++; */
+                       ldbma(s_space, pcs, t3, pmc_load_exc);
+                       len--;
+                       stbma(d_space, t3, pcd, pmc_store_exc);
+                       t2--;
+               }
+       }
+
+       pds = (double *)pcs;
+       pdd = (double *)pcd;
+
+       /* Copy 8 doubles at a time */
+       while (len >= 8*sizeof(double)) {
+               register double r1, r2, r3, r4, r5, r6, r7, r8;
+               /* prefetch_src((char *)pds + L1_CACHE_BYTES); */
+               flddma(s_space, pds, r1, pmc_load_exc);
+               flddma(s_space, pds, r2, pmc_load_exc);
+               flddma(s_space, pds, r3, pmc_load_exc);
+               flddma(s_space, pds, r4, pmc_load_exc);
+               fstdma(d_space, r1, pdd, pmc_store_exc);
+               fstdma(d_space, r2, pdd, pmc_store_exc);
+               fstdma(d_space, r3, pdd, pmc_store_exc);
+               fstdma(d_space, r4, pdd, pmc_store_exc);
+
+#if 0
+               if (L1_CACHE_BYTES <= 32)
+                       prefetch_src((char *)pds + L1_CACHE_BYTES);
+#endif
+               flddma(s_space, pds, r5, pmc_load_exc);
+               flddma(s_space, pds, r6, pmc_load_exc);
+               flddma(s_space, pds, r7, pmc_load_exc);
+               flddma(s_space, pds, r8, pmc_load_exc);
+               fstdma(d_space, r5, pdd, pmc_store_exc);
+               fstdma(d_space, r6, pdd, pmc_store_exc);
+               fstdma(d_space, r7, pdd, pmc_store_exc);
+               fstdma(d_space, r8, pdd, pmc_store_exc);
+               len -= 8*sizeof(double);
+       }
+
+       pws = (unsigned int *)pds;
+       pwd = (unsigned int *)pdd;
+
+word_copy:
+       while (len >= 8*sizeof(unsigned int)) {
+               register unsigned int r1,r2,r3,r4,r5,r6,r7,r8;
+               /* prefetch_src((char *)pws + L1_CACHE_BYTES); */
+               ldwma(s_space, pws, r1, pmc_load_exc);
+               ldwma(s_space, pws, r2, pmc_load_exc);
+               ldwma(s_space, pws, r3, pmc_load_exc);
+               ldwma(s_space, pws, r4, pmc_load_exc);
+               stwma(d_space, r1, pwd, pmc_store_exc);
+               stwma(d_space, r2, pwd, pmc_store_exc);
+               stwma(d_space, r3, pwd, pmc_store_exc);
+               stwma(d_space, r4, pwd, pmc_store_exc);
+
+               ldwma(s_space, pws, r5, pmc_load_exc);
+               ldwma(s_space, pws, r6, pmc_load_exc);
+               ldwma(s_space, pws, r7, pmc_load_exc);
+               ldwma(s_space, pws, r8, pmc_load_exc);
+               stwma(d_space, r5, pwd, pmc_store_exc);
+               stwma(d_space, r6, pwd, pmc_store_exc);
+               stwma(d_space, r7, pwd, pmc_store_exc);
+               stwma(d_space, r8, pwd, pmc_store_exc);
+               len -= 8*sizeof(unsigned int);
+       }
+
+       while (len >= 4*sizeof(unsigned int)) {
+               register unsigned int r1,r2,r3,r4;
+               ldwma(s_space, pws, r1, pmc_load_exc);
+               ldwma(s_space, pws, r2, pmc_load_exc);
+               ldwma(s_space, pws, r3, pmc_load_exc);
+               ldwma(s_space, pws, r4, pmc_load_exc);
+               stwma(d_space, r1, pwd, pmc_store_exc);
+               stwma(d_space, r2, pwd, pmc_store_exc);
+               stwma(d_space, r3, pwd, pmc_store_exc);
+               stwma(d_space, r4, pwd, pmc_store_exc);
+               len -= 4*sizeof(unsigned int);
+       }
+
+       pcs = (unsigned char *)pws;
+       pcd = (unsigned char *)pwd;
+
+byte_copy:
+       while (len) {
+               /* *pcd++ = *pcs++; */
+               ldbma(s_space, pcs, t3, pmc_load_exc);
+               stbma(d_space, t3, pcd, pmc_store_exc);
+               len--;
+       }
+
+       return 0;
+
+unaligned_copy:
+       /* possibly we are aligned on a word, but not on a double... */
+       if (likely(t1 & (sizeof(unsigned int)-1)) == 0) {
+               t2 = src & (sizeof(unsigned int) - 1);
+
+               if (unlikely(t2 != 0)) {
+                       t2 = sizeof(unsigned int) - t2;
+                       while (t2) {
+                               /* *pcd++ = *pcs++; */
+                               ldbma(s_space, pcs, t3, pmc_load_exc);
+                               stbma(d_space, t3, pcd, pmc_store_exc);
+                               len--;
+                               t2--;
+                       }
+               }
+
+               pws = (unsigned int *)pcs;
+               pwd = (unsigned int *)pcd;
+               goto word_copy;
+       }
+
+       /* Align the destination.  */
+       if (unlikely((dst & (sizeof(unsigned int) - 1)) != 0)) {
+               t2 = sizeof(unsigned int) - (dst & (sizeof(unsigned int) - 1));
+               while (t2) {
+                       /* *pcd++ = *pcs++; */
+                       ldbma(s_space, pcs, t3, pmc_load_exc);
+                       stbma(d_space, t3, pcd, pmc_store_exc);
+                       len--;
+                       t2--;
+               }
+               dst = (unsigned long)pcd;
+               src = (unsigned long)pcs;
+       }
+
+       ret = copy_dstaligned(dst, src, len / sizeof(unsigned int), 
+               o_dst, o_src, o_len);
+       if (ret)
+               return ret;
+
+       pcs += (len & -sizeof(unsigned int));
+       pcd += (len & -sizeof(unsigned int));
+       len %= sizeof(unsigned int);
+
+       preserve_branch(handle_load_error);
+       preserve_branch(handle_store_error);
+
+       goto byte_copy;
+
+handle_load_error:
+       __asm__ __volatile__ ("pmc_load_exc:\n");
+       d = &__get_cpu_var(exception_data);
+       DPRINTF("pmc_load_exc: o_len=%lu fault_addr=%lu o_src=%lu ret=%lu\n",
+               o_len, d->fault_addr, o_src, o_len - d->fault_addr + o_src);
+       return o_len - d->fault_addr + o_src;
+
+handle_store_error:
+       __asm__ __volatile__ ("pmc_store_exc:\n");
+       d = &__get_cpu_var(exception_data);
+       DPRINTF("pmc_store_exc: o_len=%lu fault_addr=%lu o_dst=%lu ret=%lu\n",
+               o_len, d->fault_addr, o_dst, o_len - d->fault_addr + o_dst);
+       return o_len - d->fault_addr + o_dst;
+}
+
+#ifdef __KERNEL__
+unsigned long copy_to_user(void __user *dst, const void *src, unsigned long len)
+{
+       mtsp(get_kernel_space(), 1);
+       mtsp(get_user_space(), 2);
+       return pa_memcpy((void __force *)dst, src, len);
+}
+
+unsigned long copy_from_user(void *dst, const void __user *src, unsigned long len)
+{
+       mtsp(get_user_space(), 1);
+       mtsp(get_kernel_space(), 2);
+       return pa_memcpy(dst, (void __force *)src, len);
+}
+
+unsigned long copy_in_user(void __user *dst, const void __user *src, unsigned long len)
+{
+       mtsp(get_user_space(), 1);
+       mtsp(get_user_space(), 2);
+       return pa_memcpy((void __force *)dst, (void __force *)src, len);
+}
+
+
+void * memcpy(void * dst,const void *src, size_t count)
+{
+       mtsp(get_kernel_space(), 1);
+       mtsp(get_kernel_space(), 2);
+       pa_memcpy(dst, src, count);
+       return dst;
+}
+
+void bcopy(const void * srcp, void * destp, size_t count)
+{
+       mtsp(get_kernel_space(), 1);
+       mtsp(get_kernel_space(), 2);
+       pa_memcpy(destp, srcp, count);
+}
+
+EXPORT_SYMBOL(copy_to_user);
+EXPORT_SYMBOL(copy_from_user);
+EXPORT_SYMBOL(copy_in_user);
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(bcopy);
+#endif
diff --git a/arch/ppc/boot/include/mpsc_defs.h b/arch/ppc/boot/include/mpsc_defs.h
new file mode 100644 (file)
index 0000000..2ce7bbb
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * drivers/serial/mpsc/mpsc_defs.h
+ *
+ * Register definitions for the Marvell Multi-Protocol Serial Controller (MPSC),
+ * Serial DMA Controller (SDMA), and Baud Rate Generator (BRG).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 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.
+ */
+#ifndef        _PPC_BOOT_MPSC_DEFS_H__
+#define        _PPC_BOOT_MPSC_DEFS_H__
+
+#define        MPSC_NUM_CTLRS          2
+
+/*
+ *****************************************************************************
+ *
+ *     Multi-Protocol Serial Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+/* Main Configuratino Register Offsets */
+#define        MPSC_MMCRL                      0x0000
+#define        MPSC_MMCRH                      0x0004
+#define        MPSC_MPCR                       0x0008
+#define        MPSC_CHR_1                      0x000c
+#define        MPSC_CHR_2                      0x0010
+#define        MPSC_CHR_3                      0x0014
+#define        MPSC_CHR_4                      0x0018
+#define        MPSC_CHR_5                      0x001c
+#define        MPSC_CHR_6                      0x0020
+#define        MPSC_CHR_7                      0x0024
+#define        MPSC_CHR_8                      0x0028
+#define        MPSC_CHR_9                      0x002c
+#define        MPSC_CHR_10                     0x0030
+#define        MPSC_CHR_11                     0x0034
+
+#define        MPSC_MPCR_CL_5                  0
+#define        MPSC_MPCR_CL_6                  1
+#define        MPSC_MPCR_CL_7                  2
+#define        MPSC_MPCR_CL_8                  3
+#define        MPSC_MPCR_SBL_1                 0
+#define        MPSC_MPCR_SBL_2                 3
+
+#define        MPSC_CHR_2_TEV                  (1<<1)
+#define        MPSC_CHR_2_TA                   (1<<7)
+#define        MPSC_CHR_2_TTCS                 (1<<9)
+#define        MPSC_CHR_2_REV                  (1<<17)
+#define        MPSC_CHR_2_RA                   (1<<23)
+#define        MPSC_CHR_2_CRD                  (1<<25)
+#define        MPSC_CHR_2_EH                   (1<<31)
+#define        MPSC_CHR_2_PAR_ODD              0
+#define        MPSC_CHR_2_PAR_SPACE            1
+#define        MPSC_CHR_2_PAR_EVEN             2
+#define        MPSC_CHR_2_PAR_MARK             3
+
+/* MPSC Signal Routing */
+#define        MPSC_MRR                        0x0000
+#define        MPSC_RCRR                       0x0004
+#define        MPSC_TCRR                       0x0008
+
+/*
+ *****************************************************************************
+ *
+ *     Serial DMA Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        SDMA_SDC                        0x0000
+#define        SDMA_SDCM                       0x0008
+#define        SDMA_RX_DESC                    0x0800
+#define        SDMA_RX_BUF_PTR                 0x0808
+#define        SDMA_SCRDP                      0x0810
+#define        SDMA_TX_DESC                    0x0c00
+#define        SDMA_SCTDP                      0x0c10
+#define        SDMA_SFTDP                      0x0c14
+
+#define        SDMA_DESC_CMDSTAT_PE            (1<<0)
+#define        SDMA_DESC_CMDSTAT_CDL           (1<<1)
+#define        SDMA_DESC_CMDSTAT_FR            (1<<3)
+#define        SDMA_DESC_CMDSTAT_OR            (1<<6)
+#define        SDMA_DESC_CMDSTAT_BR            (1<<9)
+#define        SDMA_DESC_CMDSTAT_MI            (1<<10)
+#define        SDMA_DESC_CMDSTAT_A             (1<<11)
+#define        SDMA_DESC_CMDSTAT_AM            (1<<12)
+#define        SDMA_DESC_CMDSTAT_CT            (1<<13)
+#define        SDMA_DESC_CMDSTAT_C             (1<<14)
+#define        SDMA_DESC_CMDSTAT_ES            (1<<15)
+#define        SDMA_DESC_CMDSTAT_L             (1<<16)
+#define        SDMA_DESC_CMDSTAT_F             (1<<17)
+#define        SDMA_DESC_CMDSTAT_P             (1<<18)
+#define        SDMA_DESC_CMDSTAT_EI            (1<<23)
+#define        SDMA_DESC_CMDSTAT_O             (1<<31)
+
+#define SDMA_DESC_DFLT                 (SDMA_DESC_CMDSTAT_O |  \
+                                       SDMA_DESC_CMDSTAT_EI)
+
+#define        SDMA_SDC_RFT                    (1<<0)
+#define        SDMA_SDC_SFM                    (1<<1)
+#define        SDMA_SDC_BLMR                   (1<<6)
+#define        SDMA_SDC_BLMT                   (1<<7)
+#define        SDMA_SDC_POVR                   (1<<8)
+#define        SDMA_SDC_RIFB                   (1<<9)
+
+#define        SDMA_SDCM_ERD                   (1<<7)
+#define        SDMA_SDCM_AR                    (1<<15)
+#define        SDMA_SDCM_STD                   (1<<16)
+#define        SDMA_SDCM_TXD                   (1<<23)
+#define        SDMA_SDCM_AT                    (1<<31)
+
+#define        SDMA_0_CAUSE_RXBUF              (1<<0)
+#define        SDMA_0_CAUSE_RXERR              (1<<1)
+#define        SDMA_0_CAUSE_TXBUF              (1<<2)
+#define        SDMA_0_CAUSE_TXEND              (1<<3)
+#define        SDMA_1_CAUSE_RXBUF              (1<<8)
+#define        SDMA_1_CAUSE_RXERR              (1<<9)
+#define        SDMA_1_CAUSE_TXBUF              (1<<10)
+#define        SDMA_1_CAUSE_TXEND              (1<<11)
+
+#define        SDMA_CAUSE_RX_MASK      (SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR | \
+       SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR)
+#define        SDMA_CAUSE_TX_MASK      (SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND | \
+       SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND)
+
+/* SDMA Interrupt registers */
+#define        SDMA_INTR_CAUSE                 0x0000
+#define        SDMA_INTR_MASK                  0x0080
+
+/*
+ *****************************************************************************
+ *
+ *     Baud Rate Generator Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        BRG_BCR                         0x0000
+#define        BRG_BTR                         0x0004
+
+#endif /*_PPC_BOOT_MPSC_DEFS_H__ */
diff --git a/arch/ppc/boot/simple/misc-chestnut.S b/arch/ppc/boot/simple/misc-chestnut.S
new file mode 100644 (file)
index 0000000..f75c06c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * arch/ppc/boot/simple/misc-chestnut.S
+ *
+ * Setup for the IBM Chestnut (ibm-750fxgx_eval)
+ *
+ * Author: <source@mvista.com>
+ *
+ * <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 <asm/ppc_asm.h>
+#include <asm/mv64x60_defs.h>
+#include <platforms/chestnut.h>
+
+       .globl  mv64x60_board_init
+mv64x60_board_init:
+       /*
+        * move UART to 0xffc00000
+        */
+
+       li      r23,16
+
+       addis   r25,0,CONFIG_MV64X60_BASE@h
+       ori     r25,r25,MV64x60_CPU2DEV_2_BASE
+       addis   r26,0,CHESTNUT_UART_BASE@h
+       srw     r26,r26,r23
+       stwbrx  r26,0,(r25)
+       sync
+
+       addis   r25,0,CONFIG_MV64X60_BASE@h
+       ori     r25,r25,MV64x60_CPU2DEV_2_SIZE
+       addis   r26,0,0x00100000@h
+       srw     r26,r26,r23
+       stwbrx  r26,0,(r25)
+       sync
+
+       blr
diff --git a/arch/ppc/boot/simple/misc-cpci690.c b/arch/ppc/boot/simple/misc-cpci690.c
new file mode 100644 (file)
index 0000000..b37d02f
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * arch/ppc/boot/simple/misc-cpci690.c
+ *
+ * Add birec data for Force CPCI690 board.
+ *
+ * Author: Mark A. Greer <source@mvista.com>
+ *
+ * 2003 (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 <linux/types.h>
+long   mv64x60_mpsc_clk_freq = 133000000;
diff --git a/arch/ppc/boot/simple/misc-katana.c b/arch/ppc/boot/simple/misc-katana.c
new file mode 100644 (file)
index 0000000..c4a90d5
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * arch/ppc/boot/simple/misc-katana.c
+ *
+ * Add birec data for Artesyn KATANA board.
+ *
+ * Author: Mark A. Greer <source@mvista.com>
+ *
+ * 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 <linux/types.h>
+long   mv64x60_mpsc_clk_freq = 133333333;
diff --git a/arch/ppc/configs/chestnut_defconfig b/arch/ppc/configs/chestnut_defconfig
new file mode 100644 (file)
index 0000000..2c34eb8
--- /dev/null
@@ -0,0 +1,739 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc2
+# Tue Dec  7 16:02:09 2004
+#
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_GENERIC_NVRAM=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Processor
+#
+CONFIG_6xx=y
+# CONFIG_40x is not set
+# CONFIG_44x is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+# CONFIG_E500 is not set
+CONFIG_ALTIVEC=y
+# CONFIG_TAU is not set
+# CONFIG_CPU_FREQ is not set
+CONFIG_PPC_GEN550=y
+CONFIG_PPC_STD_MMU=y
+CONFIG_NOT_COHERENT_CACHE=y
+
+#
+# Platform options
+#
+# CONFIG_PPC_MULTIPLATFORM is not set
+# CONFIG_APUS is not set
+# CONFIG_WILLOW is not set
+# CONFIG_PCORE is not set
+# CONFIG_POWERPMC250 is not set
+CONFIG_CHESTNUT=y
+# CONFIG_SPRUCE is not set
+# CONFIG_EV64260 is not set
+# CONFIG_LOPEC is not set
+# CONFIG_MCPN765 is not set
+# CONFIG_MVME5100 is not set
+# CONFIG_PPLUS is not set
+# CONFIG_PRPMC750 is not set
+# CONFIG_PRPMC800 is not set
+# CONFIG_SANDPOINT is not set
+# CONFIG_ADIR is not set
+# CONFIG_K2 is not set
+# CONFIG_PAL4 is not set
+# CONFIG_GEMINI is not set
+# CONFIG_EST8260 is not set
+# CONFIG_SBC82xx is not set
+# CONFIG_SBS8260 is not set
+# CONFIG_RPX8260 is not set
+# CONFIG_TQM8260 is not set
+# CONFIG_ADS8272 is not set
+# CONFIG_LITE5200 is not set
+CONFIG_MV64360=y
+CONFIG_MV64X60=y
+
+#
+# Set bridge options
+#
+CONFIG_MV64X60_BASE=0xf1000000
+CONFIG_MV64X60_NEW_BASE=0xf1000000
+# CONFIG_SMP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttyS0,115200 ip=on"
+
+#
+# Bus options
+#
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_LEGACY_PROC=y
+CONFIG_PCI_NAMES=y
+
+#
+# Advanced setup
+#
+CONFIG_ADVANCED_OPTIONS=y
+CONFIG_HIGHMEM_START=0xfe000000
+# CONFIG_LOWMEM_SIZE_BOOL is not set
+CONFIG_LOWMEM_SIZE=0x30000000
+# CONFIG_KERNEL_START_BOOL is not set
+CONFIG_KERNEL_START=0xc0000000
+# CONFIG_TASK_SIZE_BOOL is not set
+CONFIG_TASK_SIZE=0x80000000
+# CONFIG_CONSISTENT_START_BOOL is not set
+CONFIG_CONSISTENT_START=0xff100000
+# CONFIG_CONSISTENT_SIZE_BOOL is not set
+CONFIG_CONSISTENT_SIZE=0x00200000
+# CONFIG_BOOT_LOAD_BOOL is not set
+CONFIG_BOOT_LOAD=0x00800000
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+
+#
+# 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 is not set
+# CONFIG_MTD_CMDLINE_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_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+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_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_CHESTNUT=y
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM 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
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# 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_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_LBD is not set
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI 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
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP 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=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# 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=y
+# CONFIG_DE2104X is not set
+CONFIG_TULIP=y
+# CONFIG_TULIP_MWI is not set
+CONFIG_TULIP_MMIO=y
+# CONFIG_TULIP_NAPI is not set
+# CONFIG_DE4X5 is not set
+# CONFIG_WINBOND_840 is not set
+# CONFIG_DM9102 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 is not set
+CONFIG_E100=y
+# CONFIG_E100_NAPI 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_VIA_VELOCITY 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 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=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# 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
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+CONFIG_GEN_RTC=y
+# CONFIG_GEN_RTC_X 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_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# 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 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_DNOTIFY=y
+# 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_MSDOS_FS is not set
+# CONFIG_VFAT_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=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR 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_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN 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_RPCSEC_GSS_SPKM3 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 is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_SERIAL_TEXT_DEBUG is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
diff --git a/arch/ppc/configs/cpci690_defconfig b/arch/ppc/configs/cpci690_defconfig
new file mode 100644 (file)
index 0000000..5394879
--- /dev/null
@@ -0,0 +1,686 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.10-rc2
+# Fri Dec  3 15:56:10 2004
+#
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_GENERIC_NVRAM=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+# CONFIG_SWAP is not set
+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_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Processor
+#
+CONFIG_6xx=y
+# CONFIG_40x is not set
+# CONFIG_44x is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+# CONFIG_E500 is not set
+CONFIG_ALTIVEC=y
+# CONFIG_TAU is not set
+# CONFIG_CPU_FREQ is not set
+CONFIG_PPC_STD_MMU=y
+# CONFIG_NOT_COHERENT_CACHE is not set
+
+#
+# Platform options
+#
+# CONFIG_PPC_MULTIPLATFORM is not set
+# CONFIG_APUS is not set
+# CONFIG_KATANA is not set
+# CONFIG_DMV182 is not set
+# CONFIG_WILLOW is not set
+CONFIG_CPCI690=y
+# CONFIG_PCORE is not set
+# CONFIG_POWERPMC250 is not set
+# CONFIG_EV64260 is not set
+# CONFIG_DB64360 is not set
+# CONFIG_CHESTNUT is not set
+# CONFIG_SPRUCE is not set
+# CONFIG_LOPEC is not set
+# CONFIG_MCPN765 is not set
+# CONFIG_MVME5100 is not set
+# CONFIG_PPLUS is not set
+# CONFIG_PRPMC750 is not set
+# CONFIG_PRPMC800 is not set
+# CONFIG_PRPMC880 is not set
+# CONFIG_SANDPOINT is not set
+# CONFIG_ADIR is not set
+# CONFIG_K2 is not set
+# CONFIG_PAL4 is not set
+# CONFIG_GEMINI is not set
+# CONFIG_EST8260 is not set
+# CONFIG_SBC82xx is not set
+# CONFIG_SBS8260 is not set
+# CONFIG_RPX8260 is not set
+# CONFIG_TQM8260 is not set
+# CONFIG_ADS8272 is not set
+# CONFIG_LITE5200 is not set
+
+#
+# Set bridge options
+#
+CONFIG_MV64X60_BASE=0xf1000000
+CONFIG_MV64X60_NEW_BASE=0xf1000000
+CONFIG_GT64260=y
+CONFIG_MV64X60=y
+# CONFIG_SMP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttyMM0,9600 ip=on"
+
+#
+# Bus options
+#
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_LEGACY_PROC=y
+CONFIG_PCI_NAMES=y
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_BOOT_LOAD=0x00800000
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+
+#
+# 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_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_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_LBD is not set
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI 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
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP 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=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# 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=y
+# CONFIG_DE2104X is not set
+CONFIG_TULIP=y
+# CONFIG_TULIP_MWI is not set
+# CONFIG_TULIP_MMIO is not set
+# CONFIG_TULIP_NAPI is not set
+# CONFIG_DE4X5 is not set
+# CONFIG_WINBOND_840 is not set
+# CONFIG_DM9102 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_VIA_VELOCITY 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 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=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_MPSC=y
+CONFIG_SERIAL_MPSC_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+CONFIG_GEN_RTC=y
+# CONFIG_GEN_RTC_X 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_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# 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 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_DNOTIFY=y
+# 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_MSDOS_FS is not set
+# CONFIG_VFAT_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=y
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR 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_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=y
+# 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_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 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 is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_SERIAL_TEXT_DEBUG is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_TEST is not set
diff --git a/arch/ppc/configs/katana_defconfig b/arch/ppc/configs/katana_defconfig
new file mode 100644 (file)
index 0000000..0134ae1
--- /dev/null
@@ -0,0 +1,691 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11-rc2
+# Tue Jan 25 16:31:13 2005
+#
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_GENERIC_NVRAM=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Processor
+#
+CONFIG_6xx=y
+# CONFIG_40x is not set
+# CONFIG_44x is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+# CONFIG_E500 is not set
+CONFIG_ALTIVEC=y
+# CONFIG_TAU is not set
+# CONFIG_CPU_FREQ is not set
+CONFIG_PPC_STD_MMU=y
+CONFIG_NOT_COHERENT_CACHE=y
+
+#
+# Platform options
+#
+# CONFIG_PPC_MULTIPLATFORM is not set
+# CONFIG_APUS is not set
+CONFIG_KATANA=y
+# CONFIG_WILLOW is not set
+# CONFIG_CPCI690 is not set
+# CONFIG_PCORE is not set
+# CONFIG_POWERPMC250 is not set
+# CONFIG_CHESTNUT is not set
+# CONFIG_SPRUCE is not set
+# CONFIG_EV64260 is not set
+# CONFIG_LOPEC is not set
+# CONFIG_MCPN765 is not set
+# CONFIG_MVME5100 is not set
+# CONFIG_PPLUS is not set
+# CONFIG_PRPMC750 is not set
+# CONFIG_PRPMC800 is not set
+# CONFIG_SANDPOINT is not set
+# CONFIG_ADIR is not set
+# CONFIG_K2 is not set
+# CONFIG_PAL4 is not set
+# CONFIG_GEMINI is not set
+# CONFIG_EST8260 is not set
+# CONFIG_SBC82xx is not set
+# CONFIG_SBS8260 is not set
+# CONFIG_RPX8260 is not set
+# CONFIG_TQM8260 is not set
+# CONFIG_ADS8272 is not set
+# CONFIG_PQ2FADS is not set
+# CONFIG_LITE5200 is not set
+CONFIG_MV64360=y
+CONFIG_MV64X60=y
+
+#
+# Set bridge options
+#
+CONFIG_MV64X60_BASE=0xf8100000
+CONFIG_MV64X60_NEW_BASE=0xf8100000
+# CONFIG_SMP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttyMM0,9600 ip=on"
+
+#
+# Bus options
+#
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_LEGACY_PROC=y
+CONFIG_PCI_NAMES=y
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PC-card bridges
+#
+
+#
+# Advanced setup
+#
+CONFIG_ADVANCED_OPTIONS=y
+CONFIG_HIGHMEM_START=0xfe000000
+# CONFIG_LOWMEM_SIZE_BOOL is not set
+CONFIG_LOWMEM_SIZE=0x30000000
+# CONFIG_KERNEL_START_BOOL is not set
+CONFIG_KERNEL_START=0xc0000000
+# CONFIG_TASK_SIZE_BOOL is not set
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_CONSISTENT_START_BOOL=y
+CONFIG_CONSISTENT_START=0xf0000000
+# CONFIG_CONSISTENT_SIZE_BOOL is not set
+CONFIG_CONSISTENT_SIZE=0x00200000
+# CONFIG_BOOT_LOAD_BOOL is not set
+CONFIG_BOOT_LOAD=0x00800000
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER 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_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_COW_COMMON 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_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_LBD is not set
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI 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
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP 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=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# 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=y
+# CONFIG_DE2104X is not set
+CONFIG_TULIP=y
+# CONFIG_TULIP_MWI is not set
+# CONFIG_TULIP_MMIO is not set
+# CONFIG_TULIP_NAPI is not set
+# CONFIG_DE4X5 is not set
+# CONFIG_WINBOND_840 is not set
+# CONFIG_DM9102 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 is not set
+CONFIG_E100=y
+# CONFIG_E100_NAPI 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_VIA_VELOCITY 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 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=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_MPSC=y
+CONFIG_SERIAL_MPSC_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+CONFIG_GEN_RTC=y
+# CONFIG_GEN_RTC_X 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_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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_DUMMY_CONSOLE=y
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND 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_DNOTIFY=y
+# 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_MSDOS_FS is not set
+# CONFIG_VFAT_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=y
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR 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_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_RPCSEC_GSS_SPKM3 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 is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Hardware crypto devices
+#
diff --git a/arch/ppc/configs/luan_defconfig b/arch/ppc/configs/luan_defconfig
new file mode 100644 (file)
index 0000000..71d7bf1
--- /dev/null
@@ -0,0 +1,668 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11-rc2
+# Mon Jan 31 16:26:31 2005
+#
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_GENERIC_NVRAM=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Processor
+#
+# CONFIG_6xx is not set
+# CONFIG_40x is not set
+CONFIG_44x=y
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+# CONFIG_E500 is not set
+CONFIG_BOOKE=y
+CONFIG_PTE_64BIT=y
+CONFIG_PHYS_64BIT=y
+# CONFIG_MATH_EMULATION is not set
+# CONFIG_CPU_FREQ is not set
+CONFIG_4xx=y
+
+#
+# IBM 4xx options
+#
+# CONFIG_EBONY is not set
+CONFIG_LUAN=y
+# CONFIG_OCOTEA is not set
+CONFIG_440SP=y
+CONFIG_440=y
+CONFIG_IBM_OCP=y
+CONFIG_IBM_EMAC4=y
+# CONFIG_PPC4xx_DMA is not set
+CONFIG_PPC_GEN550=y
+# CONFIG_PM is not set
+CONFIG_NOT_COHERENT_CACHE=y
+
+#
+# Platform options
+#
+# CONFIG_PC_KEYBOARD is not set
+# CONFIG_SMP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="ip=on console=ttyS0,115200"
+
+#
+# Bus options
+#
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCI_LEGACY_PROC is not set
+# CONFIG_PCI_NAMES is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PC-card bridges
+#
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_CONSISTENT_START=0xff100000
+CONFIG_CONSISTENT_SIZE=0x00200000
+CONFIG_BOOT_LOAD=0x01000000
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+# CONFIG_STANDALONE is not set
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+# 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_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_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_RAM is not set
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_LBD is not set
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI 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
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP 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_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 is not set
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+# CONFIG_IPV6 is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+# CONFIG_IP_NF_CONNTRACK is not set
+# CONFIG_IP_NF_CONNTRACK_MARK is not set
+# CONFIG_IP_NF_QUEUE is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_ARPTABLES 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MII is not set
+# 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_IBM_EMAC=y
+# CONFIG_IBM_EMAC_ERRMSG is not set
+CONFIG_IBM_EMAC_RXB=128
+CONFIG_IBM_EMAC_TXB=128
+CONFIG_IBM_EMAC_FGAP=8
+CONFIG_IBM_EMAC_SKBRES=0
+# CONFIG_NET_PCI 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 is not set
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PCIPS2 is not set
+# CONFIG_SERIO_LIBPS2 is not set
+# CONFIG_SERIO_RAW 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=4
+CONFIG_SERIAL_8250_EXTENDED=y
+# CONFIG_SERIAL_8250_MANY_PORTS is not set
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+# CONFIG_SERIAL_8250_MULTIPORT is not set
+# CONFIG_SERIAL_8250_RSA 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
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM 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_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS 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_DNOTIFY=y
+# 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_MSDOS_FS is not set
+# CONFIG_VFAT_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_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_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_RPCSEC_GSS_SPKM3 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 is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_FS is not set
+# CONFIG_KGDB is not set
+# CONFIG_XMON is not set
+CONFIG_BDI_SWITCH=y
+# CONFIG_SERIAL_TEXT_DEBUG is not set
+CONFIG_PPC_OCP=y
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Hardware crypto devices
+#
diff --git a/arch/ppc/configs/mpc8540_ads_defconfig b/arch/ppc/configs/mpc8540_ads_defconfig
new file mode 100644 (file)
index 0000000..c5c8602
--- /dev/null
@@ -0,0 +1,707 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11-rc1
+# Thu Jan 20 01:23:13 2005
+#
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_GENERIC_NVRAM=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+CONFIG_EMBEDDED=y
+# CONFIG_KALLSYMS is not set
+CONFIG_FUTEX=y
+# CONFIG_EPOLL is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+# CONFIG_MODULES is not set
+
+#
+# Processor
+#
+# CONFIG_6xx is not set
+# CONFIG_40x is not set
+# CONFIG_44x is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+CONFIG_E500=y
+CONFIG_BOOKE=y
+CONFIG_FSL_BOOKE=y
+CONFIG_SPE=y
+CONFIG_MATH_EMULATION=y
+# CONFIG_CPU_FREQ is not set
+CONFIG_PPC_GEN550=y
+CONFIG_85xx=y
+CONFIG_PPC_INDIRECT_PCI_BE=y
+
+#
+# Freescale 85xx options
+#
+CONFIG_MPC8540_ADS=y
+# CONFIG_MPC8555_CDS is not set
+# CONFIG_MPC8560_ADS is not set
+# CONFIG_SBC8560 is not set
+CONFIG_MPC8540=y
+
+#
+# Platform options
+#
+# CONFIG_SMP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Bus options
+#
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCI_LEGACY_PROC is not set
+CONFIG_PCI_NAMES=y
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PC-card bridges
+#
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_BOOT_LOAD=0x00800000
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER 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_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_COW_COMMON 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_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=32768
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_LBD is not set
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI 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
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# 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
+# CONFIG_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# 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 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
+CONFIG_GIANFAR=y
+CONFIG_GFAR_NAPI=y
+
+#
+# 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 is not set
+# 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=4
+# 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
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+CONFIG_GEN_RTC=y
+# CONFIG_GEN_RTC_X 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_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 is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA 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_MPC=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
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Hardware Sensors Chip support
+#
+# CONFIG_I2C_SENSOR is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1031 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_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 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_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_SMSC47M1 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 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 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
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# 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_XFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# 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_MSDOS_FS is not set
+# CONFIG_VFAT_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=y
+# CONFIG_TMPFS_XATTR 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_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_RPCSEC_GSS_SPKM3 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 is not set
+# CONFIG_LDM_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
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_SERIAL_TEXT_DEBUG is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Hardware crypto devices
+#
diff --git a/arch/ppc/configs/mpc8555_cds_defconfig b/arch/ppc/configs/mpc8555_cds_defconfig
new file mode 100644 (file)
index 0000000..728bd9e
--- /dev/null
@@ -0,0 +1,718 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11-rc1
+# Thu Jan 20 01:25:35 2005
+#
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_GENERIC_NVRAM=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+CONFIG_EMBEDDED=y
+# CONFIG_KALLSYMS is not set
+CONFIG_FUTEX=y
+# CONFIG_EPOLL is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+# CONFIG_MODULES is not set
+
+#
+# Processor
+#
+# CONFIG_6xx is not set
+# CONFIG_40x is not set
+# CONFIG_44x is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+CONFIG_E500=y
+CONFIG_BOOKE=y
+CONFIG_FSL_BOOKE=y
+CONFIG_SPE=y
+CONFIG_MATH_EMULATION=y
+# CONFIG_CPU_FREQ is not set
+CONFIG_PPC_GEN550=y
+CONFIG_85xx=y
+CONFIG_PPC_INDIRECT_PCI_BE=y
+
+#
+# Freescale 85xx options
+#
+# CONFIG_MPC8540_ADS is not set
+CONFIG_MPC8555_CDS=y
+# CONFIG_MPC8560_ADS is not set
+# CONFIG_SBC8560 is not set
+CONFIG_MPC8555=y
+CONFIG_85xx_PCI2=y
+
+#
+# Platform options
+#
+CONFIG_CPM2=y
+# CONFIG_PC_KEYBOARD is not set
+# CONFIG_SMP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Bus options
+#
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCI_LEGACY_PROC is not set
+CONFIG_PCI_NAMES=y
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PC-card bridges
+#
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_BOOT_LOAD=0x00800000
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER 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_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_COW_COMMON 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_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=32768
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_LBD is not set
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI 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
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# 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
+# CONFIG_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# 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 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
+CONFIG_GIANFAR=y
+CONFIG_GFAR_NAPI=y
+
+#
+# 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 is not set
+# 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=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_CPM is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+CONFIG_GEN_RTC=y
+# CONFIG_GEN_RTC_X 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_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 is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA 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_MPC=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
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Hardware Sensors Chip support
+#
+# CONFIG_I2C_SENSOR is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1031 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_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 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_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_SMSC47M1 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 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 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
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# 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_XFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# 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_MSDOS_FS is not set
+# CONFIG_VFAT_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=y
+# CONFIG_TMPFS_XATTR 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_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_RPCSEC_GSS_SPKM3 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 is not set
+# CONFIG_LDM_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
+# CONFIG_SCC_ENET is not set
+# CONFIG_FEC_ENET is not set
+
+#
+# CPM2 Options
+#
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_KGDB_CONSOLE is not set
+# CONFIG_SERIAL_TEXT_DEBUG is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Hardware crypto devices
+#
diff --git a/arch/ppc/configs/mpc8560_ads_defconfig b/arch/ppc/configs/mpc8560_ads_defconfig
new file mode 100644 (file)
index 0000000..38a343c
--- /dev/null
@@ -0,0 +1,719 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11-rc1
+# Thu Jan 20 01:24:56 2005
+#
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_GENERIC_NVRAM=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+CONFIG_EMBEDDED=y
+# CONFIG_KALLSYMS is not set
+CONFIG_FUTEX=y
+# CONFIG_EPOLL is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+# CONFIG_MODULES is not set
+
+#
+# Processor
+#
+# CONFIG_6xx is not set
+# CONFIG_40x is not set
+# CONFIG_44x is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+CONFIG_E500=y
+CONFIG_BOOKE=y
+CONFIG_FSL_BOOKE=y
+CONFIG_SPE=y
+CONFIG_MATH_EMULATION=y
+# CONFIG_CPU_FREQ is not set
+CONFIG_85xx=y
+CONFIG_PPC_INDIRECT_PCI_BE=y
+
+#
+# Freescale 85xx options
+#
+# CONFIG_MPC8540_ADS is not set
+# CONFIG_MPC8555_CDS is not set
+CONFIG_MPC8560_ADS=y
+# CONFIG_SBC8560 is not set
+CONFIG_MPC8560=y
+
+#
+# Platform options
+#
+CONFIG_CPM2=y
+# CONFIG_PC_KEYBOARD is not set
+# CONFIG_SMP is not set
+# CONFIG_PREEMPT is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Bus options
+#
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCI_LEGACY_PROC is not set
+CONFIG_PCI_NAMES=y
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PC-card bridges
+#
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_BOOT_LOAD=0x00800000
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER 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_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_COW_COMMON 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_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=32768
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_LBD is not set
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI 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
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# 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
+# CONFIG_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# 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 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
+CONFIG_GIANFAR=y
+CONFIG_GFAR_NAPI=y
+
+#
+# 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 is not set
+# 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 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_CPM=y
+CONFIG_SERIAL_CPM_CONSOLE=y
+CONFIG_SERIAL_CPM_SCC1=y
+# CONFIG_SERIAL_CPM_SCC2 is not set
+# CONFIG_SERIAL_CPM_SCC3 is not set
+CONFIG_SERIAL_CPM_SCC4=y
+# CONFIG_SERIAL_CPM_SMC1 is not set
+# CONFIG_SERIAL_CPM_SMC2 is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+CONFIG_GEN_RTC=y
+# CONFIG_GEN_RTC_X 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_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 is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA 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_MPC=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
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Hardware Sensors Chip support
+#
+# CONFIG_I2C_SENSOR is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1031 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_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 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_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_SMSC47M1 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 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 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
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# 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_XFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# 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_MSDOS_FS is not set
+# CONFIG_VFAT_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=y
+# CONFIG_TMPFS_XATTR 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_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_RPCSEC_GSS_SPKM3 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 is not set
+# CONFIG_LDM_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
+# CONFIG_SCC_ENET is not set
+# CONFIG_FEC_ENET is not set
+
+#
+# CPM2 Options
+#
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_KGDB_CONSOLE is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Hardware crypto devices
+#
diff --git a/arch/ppc/configs/stx_gp3_defconfig b/arch/ppc/configs/stx_gp3_defconfig
new file mode 100644 (file)
index 0000000..66dae83
--- /dev/null
@@ -0,0 +1,972 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11-rc2
+# Wed Jan 26 14:32:58 2005
+#
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_GENERIC_NVRAM=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODULE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Processor
+#
+# CONFIG_6xx is not set
+# CONFIG_40x is not set
+# CONFIG_44x is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+CONFIG_E500=y
+CONFIG_BOOKE=y
+CONFIG_FSL_BOOKE=y
+# CONFIG_SPE is not set
+CONFIG_MATH_EMULATION=y
+# CONFIG_CPU_FREQ is not set
+CONFIG_85xx=y
+CONFIG_PPC_INDIRECT_PCI_BE=y
+
+#
+# Freescale 85xx options
+#
+# CONFIG_MPC8540_ADS is not set
+# CONFIG_MPC8555_CDS is not set
+# CONFIG_MPC8560_ADS is not set
+# CONFIG_SBC8560 is not set
+CONFIG_STX_GP3=y
+CONFIG_MPC8560=y
+
+#
+# Platform options
+#
+CONFIG_CPM2=y
+# CONFIG_PC_KEYBOARD is not set
+# CONFIG_SMP is not set
+# CONFIG_PREEMPT is not set
+CONFIG_HIGHMEM=y
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=m
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Bus options
+#
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCI_LEGACY_PROC is not set
+# CONFIG_PCI_NAMES is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PC-card bridges
+#
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_BOOT_LOAD=0x00800000
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+# CONFIG_PARPORT_PC_FIFO is not set
+# CONFIG_PARPORT_PC_SUPERIO is not set
+# CONFIG_PARPORT_OTHER is not set
+# CONFIG_PARPORT_1284 is not set
+
+#
+# Plug and Play support
+#
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=m
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_SX8 is not set
+CONFIG_BLK_DEV_RAM=m
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_LBD is not set
+# CONFIG_CDROM_PKTCDVD is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_ATA_OVER_ETH 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_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECD=m
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_IDE_ARM 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=m
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_PPA is not set
+# CONFIG_SCSI_IMM is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+CONFIG_SCSI_QLA2XXX=m
+# CONFIG_SCSI_QLA21XX is not set
+# CONFIG_SCSI_QLA22XX is not set
+# CONFIG_SCSI_QLA2300 is not set
+# CONFIG_SCSI_QLA2322 is not set
+# CONFIG_SCSI_QLA6312 is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD 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
+
+#
+# Macintosh device drivers
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP 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_INET_TUNNEL is not set
+CONFIG_IP_TCPDIAG=y
+# CONFIG_IP_TCPDIAG_IPV6 is not set
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+# CONFIG_IPV6 is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+# CONFIG_IP_NF_CT_ACCT is not set
+# CONFIG_IP_NF_CONNTRACK_MARK is not set
+# CONFIG_IP_NF_CT_PROTO_SCTP is not set
+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 is not set
+CONFIG_IP_NF_IPTABLES=m
+# CONFIG_IP_NF_MATCH_LIMIT is not set
+# CONFIG_IP_NF_MATCH_IPRANGE is not set
+# CONFIG_IP_NF_MATCH_MAC is not set
+# CONFIG_IP_NF_MATCH_PKTTYPE is not set
+# CONFIG_IP_NF_MATCH_MARK is not set
+# CONFIG_IP_NF_MATCH_MULTIPORT is not set
+# CONFIG_IP_NF_MATCH_TOS is not set
+# 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 is not set
+# CONFIG_IP_NF_MATCH_LENGTH is not set
+# CONFIG_IP_NF_MATCH_TTL is not set
+# CONFIG_IP_NF_MATCH_TCPMSS is not set
+# CONFIG_IP_NF_MATCH_HELPER is not set
+# CONFIG_IP_NF_MATCH_STATE is not set
+# CONFIG_IP_NF_MATCH_CONNTRACK is not set
+# CONFIG_IP_NF_MATCH_OWNER is not set
+# CONFIG_IP_NF_MATCH_ADDRTYPE is not set
+# CONFIG_IP_NF_MATCH_REALM is not set
+# CONFIG_IP_NF_MATCH_SCTP is not set
+# CONFIG_IP_NF_MATCH_COMMENT is not set
+# CONFIG_IP_NF_MATCH_HASHLIMIT is not set
+CONFIG_IP_NF_FILTER=m
+# CONFIG_IP_NF_TARGET_REJECT is not set
+# CONFIG_IP_NF_TARGET_LOG is not set
+# CONFIG_IP_NF_TARGET_ULOG is not set
+# CONFIG_IP_NF_TARGET_TCPMSS is not set
+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_SNMP_BASIC=m
+CONFIG_IP_NF_NAT_IRC=m
+CONFIG_IP_NF_NAT_FTP=m
+# CONFIG_IP_NF_MANGLE is not set
+# CONFIG_IP_NF_RAW is not set
+# CONFIG_IP_NF_ARPTABLES 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
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MII is not set
+# 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 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
+CONFIG_GIANFAR=y
+CONFIG_GFAR_NAPI=y
+
+#
+# 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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=1280
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=1024
+CONFIG_INPUT_JOYDEV=m
+# CONFIG_INPUT_TSDEV is not set
+CONFIG_INPUT_EVDEV=m
+# 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=y
+CONFIG_SERIO_SERPORT=y
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PARKBD is not set
+# CONFIG_SERIO_PCIPS2 is not set
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW 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 is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_CPM=y
+CONFIG_SERIAL_CPM_CONSOLE=y
+# CONFIG_SERIAL_CPM_SCC1 is not set
+CONFIG_SERIAL_CPM_SCC2=y
+# CONFIG_SERIAL_CPM_SCC3 is not set
+# CONFIG_SERIAL_CPM_SCC4 is not set
+# CONFIG_SERIAL_CPM_SMC1 is not set
+# CONFIG_SERIAL_CPM_SMC2 is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+CONFIG_PRINTER=m
+# CONFIG_LP_CONSOLE is not set
+# CONFIG_PPDEV is not set
+# CONFIG_TIPAR is not set
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM 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_AGP=m
+CONFIG_DRM=m
+# CONFIG_DRM_TDFX is not set
+# CONFIG_DRM_R128 is not set
+# CONFIG_DRM_RADEON is not set
+# CONFIG_DRM_MGA is not set
+# CONFIG_DRM_SIS is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+CONFIG_I2C=m
+CONFIG_I2C_CHARDEV=m
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=m
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA 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_MPC is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_PARPORT 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_STUB is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_VOODOO3 is not set
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Hardware Sensors Chip support
+#
+# CONFIG_I2C_SENSOR is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1031 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_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 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_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_SMSC47M1 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 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 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
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+# CONFIG_SND is not set
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+CONFIG_JBD=y
+CONFIG_JBD_DEBUG=y
+CONFIG_FS_MBCACHE=y
+# 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_DNOTIFY=y
+CONFIG_AUTOFS_FS=m
+CONFIG_AUTOFS4_FS=y
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=m
+# CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+# CONFIG_PROC_KCORE is not set
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_XATTR 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_CRAMFS=m
+# 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_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT 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 is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# 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_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# 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
+# CONFIG_SCC_ENET is not set
+# CONFIG_FEC_ENET is not set
+
+#
+# CPM2 Options
+#
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=y
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=m
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_HIGHMEM is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_KGDB_CONSOLE is not set
+# CONFIG_XMON is not set
+CONFIG_BDI_SWITCH=y
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Hardware crypto devices
+#
diff --git a/arch/ppc/kernel/machine_kexec.c b/arch/ppc/kernel/machine_kexec.c
new file mode 100644 (file)
index 0000000..9daefb3
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * machine_kexec.c - handle transition of Linux booting another kernel
+ * Copyright (C) 2002-2003 Eric Biederman  <ebiederm@xmission.com>
+ *
+ * GAMECUBE/PPC32 port Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/kexec.h>
+#include <linux/delay.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/mmu_context.h>
+#include <asm/io.h>
+#include <asm/hw_irq.h>
+#include <asm/cacheflush.h>
+
+typedef void (*relocate_new_kernel_t)(
+       unsigned long indirection_page, unsigned long reboot_code_buffer,
+       unsigned long start_address);
+
+const extern unsigned char relocate_new_kernel[];
+const extern unsigned int relocate_new_kernel_size;
+extern void use_mm(struct mm_struct *mm);
+
+static int identity_map_pages(struct page *pages, int order)
+{
+       struct mm_struct *mm;
+       struct vm_area_struct *vma;
+       int error;
+
+       mm = &init_mm;
+       vma = NULL;
+
+       down_write(&mm->mmap_sem);
+       error = -ENOMEM;
+       vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+       if (!vma) {
+               goto out;
+       }
+
+       memset(vma, 0, sizeof(*vma));
+       vma->vm_mm = mm;
+       vma->vm_start = page_to_pfn(pages) << PAGE_SHIFT;
+       vma->vm_end = vma->vm_start + (1 << (order + PAGE_SHIFT));
+       vma->vm_ops = NULL;
+       vma->vm_flags = VM_SHARED \
+               | VM_READ | VM_WRITE | VM_EXEC \
+               | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC \
+               | VM_DONTCOPY | VM_RESERVED;
+       vma->vm_page_prot = protection_map[vma->vm_flags & 0xf];
+       vma->vm_file = NULL;
+       vma->vm_private_data = NULL;
+       insert_vm_struct(mm, vma);
+
+       error = remap_page_range(vma, vma->vm_start, vma->vm_start,
+               vma->vm_end - vma->vm_start, vma->vm_page_prot);
+       if (error) {
+               goto out;
+       }
+
+       error = 0;
+ out:
+       if (error && vma) {
+               kmem_cache_free(vm_area_cachep, vma);
+               vma = NULL;
+       }
+       up_write(&mm->mmap_sem);
+
+       return error;
+}
+
+/*
+ * Do what every setup is needed on image and the
+ * reboot code buffer to allow us to avoid allocations
+ * later.
+ */
+int machine_kexec_prepare(struct kimage *image)
+{
+       unsigned int order;
+       order = get_order(KEXEC_CONTROL_CODE_SIZE);
+       return identity_map_pages(image->control_code_page, order);
+}
+
+void machine_kexec_cleanup(struct kimage *image)
+{
+       unsigned int order;
+       order = get_order(KEXEC_CONTROL_CODE_SIZE);
+       do_munmap(&init_mm,
+               page_to_pfn(image->control_code_page) << PAGE_SHIFT,
+               1 << (order + PAGE_SHIFT));
+}
+
+void machine_shutdown(void)
+{
+}
+
+/*
+ * Do not allocate memory (or fail in any way) in machine_kexec().
+ * We are past the point of no return, committed to rebooting now.
+ */
+void machine_kexec(struct kimage *image)
+{
+       unsigned long indirection_page;
+       unsigned long reboot_code_buffer;
+       relocate_new_kernel_t rnk;
+
+       /* switch to an mm where the reboot_code_buffer is identity mapped */
+       use_mm(&init_mm);
+
+       /* Interrupts aren't acceptable while we reboot */
+       local_irq_disable();
+
+       reboot_code_buffer = page_to_pfn(image->control_code_page) <<PAGE_SHIFT;
+       indirection_page = image->head & PAGE_MASK;
+
+       /* copy it out */
+       memcpy((void *)reboot_code_buffer,
+               relocate_new_kernel, relocate_new_kernel_size);
+
+       flush_icache_range(reboot_code_buffer,
+               reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE);
+       printk(KERN_INFO "Bye!\n");
+
+       /* now call it */
+       rnk = (relocate_new_kernel_t) reboot_code_buffer;
+       (*rnk)(indirection_page, reboot_code_buffer, image->start);
+}
+
diff --git a/arch/ppc/kernel/perfmon.c b/arch/ppc/kernel/perfmon.c
new file mode 100644 (file)
index 0000000..918f6b2
--- /dev/null
@@ -0,0 +1,93 @@
+/* kernel/perfmon.c
+ * PPC 32 Performance Monitor Infrastructure
+ *
+ * Author: Andy Fleming
+ * Copyright (c) 2004 Freescale Semiconductor, 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 <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/prctl.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/reg.h>
+#include <asm/xmon.h>
+
+/* A lock to regulate grabbing the interrupt */
+DEFINE_SPINLOCK(perfmon_lock);
+
+#ifdef CONFIG_FSL_BOOKE
+static void dummy_perf(struct pt_regs *regs)
+{
+       unsigned int pmgc0 = mfpmr(PMRN_PMGC0);
+
+       pmgc0 &= ~PMGC0_PMIE;
+       mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+#else
+/* Ensure exceptions are disabled */
+
+static void dummy_perf(struct pt_regs *regs)
+{
+       unsigned int mmcr0 = mfspr(SPRN_MMCR0);
+
+       mmcr0 &= ~MMCR0_PMXE;
+       mtspr(SPRN_MMCR0, mmcr0);
+}
+#endif
+
+void (*perf_irq)(struct pt_regs *) = dummy_perf;
+
+/* Grab the interrupt, if it's free.
+ * Returns 0 on success, -1 if the interrupt is taken already */
+int request_perfmon_irq(void (*handler)(struct pt_regs *))
+{
+       int err = 0;
+
+       spin_lock(&perfmon_lock);
+
+       if (perf_irq == dummy_perf)
+               perf_irq = handler;
+       else {
+               pr_info("perfmon irq already handled by %p\n", perf_irq);
+               err = -1;
+       }
+
+       spin_unlock(&perfmon_lock);
+
+       return err;
+}
+
+void free_perfmon_irq(void)
+{
+       spin_lock(&perfmon_lock);
+
+       perf_irq = dummy_perf;
+
+       spin_unlock(&perfmon_lock);
+}
+
+EXPORT_SYMBOL(perf_irq);
+EXPORT_SYMBOL(request_perfmon_irq);
+EXPORT_SYMBOL(free_perfmon_irq);
diff --git a/arch/ppc/kernel/perfmon_fsl_booke.c b/arch/ppc/kernel/perfmon_fsl_booke.c
new file mode 100644 (file)
index 0000000..03526bf
--- /dev/null
@@ -0,0 +1,222 @@
+/* kernel/perfmon_fsl_booke.c
+ * Freescale Book-E Performance Monitor code
+ *
+ * Author: Andy Fleming
+ * Copyright (c) 2004 Freescale Semiconductor, 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 <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/prctl.h>
+
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/reg.h>
+#include <asm/xmon.h>
+#include <asm/perfmon.h>
+
+static inline u32 get_pmlca(int ctr);
+static inline void set_pmlca(int ctr, u32 pmlca);
+
+static inline u32 get_pmlca(int ctr)
+{
+       u32 pmlca;
+
+       switch (ctr) {
+               case 0:
+                       pmlca = mfpmr(PMRN_PMLCA0);
+                       break;
+               case 1:
+                       pmlca = mfpmr(PMRN_PMLCA1);
+                       break;
+               case 2:
+                       pmlca = mfpmr(PMRN_PMLCA2);
+                       break;
+               case 3:
+                       pmlca = mfpmr(PMRN_PMLCA3);
+                       break;
+               default:
+                       panic("Bad ctr number\n");
+       }
+
+       return pmlca;
+}
+
+static inline void set_pmlca(int ctr, u32 pmlca)
+{
+       switch (ctr) {
+               case 0:
+                       mtpmr(PMRN_PMLCA0, pmlca);
+                       break;
+               case 1:
+                       mtpmr(PMRN_PMLCA1, pmlca);
+                       break;
+               case 2:
+                       mtpmr(PMRN_PMLCA2, pmlca);
+                       break;
+               case 3:
+                       mtpmr(PMRN_PMLCA3, pmlca);
+                       break;
+               default:
+                       panic("Bad ctr number\n");
+       }
+}
+
+void init_pmc_stop(int ctr)
+{
+       u32 pmlca = (PMLCA_FC | PMLCA_FCS | PMLCA_FCU |
+                       PMLCA_FCM1 | PMLCA_FCM0);
+       u32 pmlcb = 0;
+
+       switch (ctr) {
+               case 0:
+                       mtpmr(PMRN_PMLCA0, pmlca);
+                       mtpmr(PMRN_PMLCB0, pmlcb);
+                       break;
+               case 1:
+                       mtpmr(PMRN_PMLCA1, pmlca);
+                       mtpmr(PMRN_PMLCB1, pmlcb);
+                       break;
+               case 2:
+                       mtpmr(PMRN_PMLCA2, pmlca);
+                       mtpmr(PMRN_PMLCB2, pmlcb);
+                       break;
+               case 3:
+                       mtpmr(PMRN_PMLCA3, pmlca);
+                       mtpmr(PMRN_PMLCB3, pmlcb);
+                       break;
+               default:
+                       panic("Bad ctr number!\n");
+       }
+}
+
+void set_pmc_event(int ctr, int event)
+{
+       u32 pmlca;
+
+       pmlca = get_pmlca(ctr);
+
+       pmlca = (pmlca & ~PMLCA_EVENT_MASK) |
+               ((event << PMLCA_EVENT_SHIFT) &
+                PMLCA_EVENT_MASK);
+
+       set_pmlca(ctr, pmlca);
+}
+
+void set_pmc_user_kernel(int ctr, int user, int kernel)
+{
+       u32 pmlca;
+
+       pmlca = get_pmlca(ctr);
+
+       if(user)
+               pmlca &= ~PMLCA_FCU;
+       else
+               pmlca |= PMLCA_FCU;
+
+       if(kernel)
+               pmlca &= ~PMLCA_FCS;
+       else
+               pmlca |= PMLCA_FCS;
+
+       set_pmlca(ctr, pmlca);
+}
+
+void set_pmc_marked(int ctr, int mark0, int mark1)
+{
+       u32 pmlca = get_pmlca(ctr);
+
+       if(mark0)
+               pmlca &= ~PMLCA_FCM0;
+       else
+               pmlca |= PMLCA_FCM0;
+
+       if(mark1)
+               pmlca &= ~PMLCA_FCM1;
+       else
+               pmlca |= PMLCA_FCM1;
+
+       set_pmlca(ctr, pmlca);
+}
+
+void pmc_start_ctr(int ctr, int enable)
+{
+       u32 pmlca = get_pmlca(ctr);
+
+       pmlca &= ~PMLCA_FC;
+
+       if (enable)
+               pmlca |= PMLCA_CE;
+       else
+               pmlca &= ~PMLCA_CE;
+
+       set_pmlca(ctr, pmlca);
+}
+
+void pmc_start_ctrs(int enable)
+{
+       u32 pmgc0 = mfpmr(PMRN_PMGC0);
+
+       pmgc0 &= ~PMGC0_FAC;
+       pmgc0 |= PMGC0_FCECE;
+
+       if (enable)
+               pmgc0 |= PMGC0_PMIE;
+       else
+               pmgc0 &= ~PMGC0_PMIE;
+
+       mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+void pmc_stop_ctrs(void)
+{
+       u32 pmgc0 = mfpmr(PMRN_PMGC0);
+
+       pmgc0 |= PMGC0_FAC;
+
+       pmgc0 &= ~(PMGC0_PMIE | PMGC0_FCECE);
+
+       mtpmr(PMRN_PMGC0, pmgc0);
+}
+
+void dump_pmcs(void)
+{
+       printk("pmgc0: %x\n", mfpmr(PMRN_PMGC0));
+       printk("pmc\t\tpmlca\t\tpmlcb\n");
+       printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC0),
+                       mfpmr(PMRN_PMLCA0), mfpmr(PMRN_PMLCB0));
+       printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC1),
+                       mfpmr(PMRN_PMLCA1), mfpmr(PMRN_PMLCB1));
+       printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC2),
+                       mfpmr(PMRN_PMLCA2), mfpmr(PMRN_PMLCB2));
+       printk("%8x\t%8x\t%8x\n", mfpmr(PMRN_PMC3),
+                       mfpmr(PMRN_PMLCA3), mfpmr(PMRN_PMLCB3));
+}
+
+EXPORT_SYMBOL(init_pmc_stop);
+EXPORT_SYMBOL(set_pmc_event);
+EXPORT_SYMBOL(set_pmc_user_kernel);
+EXPORT_SYMBOL(set_pmc_marked);
+EXPORT_SYMBOL(pmc_start_ctr);
+EXPORT_SYMBOL(pmc_start_ctrs);
+EXPORT_SYMBOL(pmc_stop_ctrs);
+EXPORT_SYMBOL(dump_pmcs);
diff --git a/arch/ppc/kernel/relocate_kernel.S b/arch/ppc/kernel/relocate_kernel.S
new file mode 100644 (file)
index 0000000..837e596
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * relocate_kernel.S - put the kernel image in place to boot
+ * Copyright (C) 2002-2003 Eric Biederman  <ebiederm@xmission.com>
+ *
+ * GAMECUBE/PPC32 port Copyright (C) 2004 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#include <asm/reg.h>
+#include <asm/ppc_asm.h>
+#include <asm/processor.h>
+
+#include <asm/kexec.h>
+
+#define PAGE_SIZE      4096 /* must be same value as in <asm/page.h> */
+
+/* returns  r3 = relocated address of sym */
+/* modifies r0 */
+#define RELOC_SYM(sym) \
+       mflr    r3; \
+       bl      1f; \
+1:     mflr    r0; \
+       mtlr    r3; \
+       lis     r3, 1b@ha; \
+       ori     r3, r3, 1b@l; \
+       subf    r0, r3, r0; \
+       lis     r3, sym@ha; \
+       ori     r3, r3, sym@l; \
+       add     r3, r3, r0
+
+       /*
+        * Must be relocatable PIC code callable as a C function.
+        */
+       .globl relocate_new_kernel
+relocate_new_kernel:
+       /* r3 = indirection_page   */
+       /* r4 = reboot_code_buffer */
+       /* r5 = start_address      */
+
+       li      r0, 0
+
+       /* Set Machine Status Register to a known status */
+       mr      r8, r0
+       ori     r8, r8, MSR_RI|MSR_ME
+       mtmsr   r8
+       isync
+
+       /* from this point address translation is turned off */
+       /* and interrupts are disabled */
+
+       /* set a new stack at the bottom of our page... */
+       /* (not really needed now) */
+       addi    r1, r4, KEXEC_CONTROL_CODE_SIZE - 8 /* for LR Save+Back Chain */
+       stw     r0, 0(r1)
+
+       /* Do the copies */
+       li      r6, 0 /* checksum */
+       subi    r3, r3, 4
+
+0:     /* top, read another word for the indirection page */
+       lwzu    r0, 4(r3)
+
+       /* is it a destination page? (r8) */
+       rlwinm. r7, r0, 0, 31, 31 /* IND_DESTINATION (1<<0) */
+       beq     1f
+
+       rlwinm  r8, r0, 0, 0, 19 /* clear kexec flags, page align */
+       b       0b
+
+1:     /* is it an indirection page? (r3) */
+       rlwinm. r7, r0, 0, 30, 30 /* IND_INDIRECTION (1<<1) */
+       beq     1f
+
+       rlwinm  r3, r0, 0, 0, 19 /* clear kexec flags, page align */
+       subi    r3, r3, 4
+       b       0b
+
+1:     /* are we done? */
+       rlwinm. r7, r0, 0, 29, 29 /* IND_DONE (1<<2) */
+       beq     1f
+       b       2f
+
+1:     /* is it a source page? (r9) */
+       rlwinm. r7, r0, 0, 28, 28 /* IND_SOURCE (1<<3) */
+       beq     0b
+
+       rlwinm  r9, r0, 0, 0, 19 /* clear kexec flags, page align */
+
+       li      r7, PAGE_SIZE / 4
+       mtctr   r7
+       subi    r9, r9, 4
+       subi    r8, r8, 4
+9:
+       lwzu    r0, 4(r9)  /* do the copy */
+       xor     r6, r6, r0
+       stwu    r0, 4(r8)
+       dcbst   0, r8
+       sync
+       icbi    0, r8
+       bdnz    9b
+
+       addi    r9, r9, 4
+       addi    r8, r8, 4
+       b       0b
+
+2:
+
+       /* To be certain of avoiding problems with self-modifying code
+        * execute a serializing instruction here.
+        */
+       isync
+       sync
+
+       /* jump to the entry point, usually the setup routine */
+       mtlr    r5
+       blrl
+
+1:     b       1b
+
+relocate_new_kernel_end:
+
+       .globl relocate_new_kernel_size
+relocate_new_kernel_size:
+       .long relocate_new_kernel_end - relocate_new_kernel
+
diff --git a/arch/ppc/oprofile/common.c b/arch/ppc/oprofile/common.c
new file mode 100644 (file)
index 0000000..3169c67
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * PPC 32 oprofile support
+ * Based on PPC64 oprofile support
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Copyright (C) Freescale Semiconductor, Inc 2004
+ *
+ * Author: Andy Fleming
+ *
+ * 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 <linux/oprofile.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/errno.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/perfmon.h>
+#include <asm/cputable.h>
+
+#include "op_impl.h"
+
+static struct op_ppc32_model *model;
+
+static struct op_counter_config ctr[OP_MAX_COUNTER];
+static struct op_system_config sys;
+
+static void op_handle_interrupt(struct pt_regs *regs)
+{
+       model->handle_interrupt(regs, ctr);
+}
+
+static int op_ppc32_setup(void)
+{
+       /* Install our interrupt handler into the existing hook.  */
+       if(request_perfmon_irq(&op_handle_interrupt))
+               return -EBUSY;
+
+       mb();
+
+       /* Pre-compute the values to stuff in the hardware registers.  */
+       model->reg_setup(ctr, &sys, model->num_counters);
+
+#if 0
+       /* FIXME: Make multi-cpu work */
+       /* Configure the registers on all cpus.  */
+       on_each_cpu(model->reg_setup, NULL, 0, 1);
+#endif
+
+       return 0;
+}
+
+static void op_ppc32_shutdown(void)
+{
+       mb();
+
+       /* Remove our interrupt handler. We may be removing this module. */
+       free_perfmon_irq();
+}
+
+static void op_ppc32_cpu_start(void *dummy)
+{
+       model->start(ctr);
+}
+
+static int op_ppc32_start(void)
+{
+       on_each_cpu(op_ppc32_cpu_start, NULL, 0, 1);
+       return 0;
+}
+
+static inline void op_ppc32_cpu_stop(void *dummy)
+{
+       model->stop();
+}
+
+static void op_ppc32_stop(void)
+{
+       on_each_cpu(op_ppc32_cpu_stop, NULL, 0, 1);
+}
+
+static int op_ppc32_create_files(struct super_block *sb, struct dentry *root)
+{
+       int i;
+
+       for (i = 0; i < model->num_counters; ++i) {
+               struct dentry *dir;
+               char buf[3];
+
+               snprintf(buf, sizeof buf, "%d", i);
+               dir = oprofilefs_mkdir(sb, root, buf);
+
+               oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
+               oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
+               oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
+               oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
+               oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
+
+               /* FIXME: Not sure if this is used */
+               oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+       }
+
+       oprofilefs_create_ulong(sb, root, "enable_kernel", &sys.enable_kernel);
+       oprofilefs_create_ulong(sb, root, "enable_user", &sys.enable_user);
+
+       /* Default to tracing both kernel and user */
+       sys.enable_kernel = 1;
+       sys.enable_user = 1;
+
+       return 0;
+}
+
+static struct oprofile_operations oprof_ppc32_ops = {
+       .create_files   = op_ppc32_create_files,
+       .setup          = op_ppc32_setup,
+       .shutdown       = op_ppc32_shutdown,
+       .start          = op_ppc32_start,
+       .stop           = op_ppc32_stop,
+       .cpu_type       = NULL          /* To be filled in below. */
+};
+
+int __init oprofile_arch_init(struct oprofile_operations *ops)
+{
+       char *name;
+       int cpu_id = smp_processor_id();
+
+#ifdef CONFIG_FSL_BOOKE
+       model = &op_model_fsl_booke;
+#else
+       return -ENODEV;
+#endif
+
+       name = kmalloc(32, GFP_KERNEL);
+
+       if (NULL == name)
+               return -ENOMEM;
+
+       sprintf(name, "ppc/%s", cur_cpu_spec[cpu_id]->cpu_name);
+
+       oprof_ppc32_ops.cpu_type = name;
+
+       model->num_counters = cur_cpu_spec[cpu_id]->num_pmcs;
+
+       *ops = oprof_ppc32_ops;
+
+       printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
+              oprof_ppc32_ops.cpu_type);
+
+       return 0;
+}
+
+void oprofile_arch_exit(void)
+{
+       kfree(oprof_ppc32_ops.cpu_type);
+       oprof_ppc32_ops.cpu_type = NULL;
+}
diff --git a/arch/ppc/oprofile/op_impl.h b/arch/ppc/oprofile/op_impl.h
new file mode 100644 (file)
index 0000000..bc336dc
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Based on alpha version.
+ *
+ * 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 OP_IMPL_H
+#define OP_IMPL_H 1
+
+#define OP_MAX_COUNTER 8
+
+/* Per-counter configuration as set via oprofilefs.  */
+struct op_counter_config {
+       unsigned long enabled;
+       unsigned long event;
+       unsigned long count;
+       unsigned long kernel;
+       unsigned long user;
+       unsigned long unit_mask;
+};
+
+/* System-wide configuration as set via oprofilefs.  */
+struct op_system_config {
+       unsigned long enable_kernel;
+       unsigned long enable_user;
+};
+
+/* Per-arch configuration */
+struct op_ppc32_model {
+       void (*reg_setup) (struct op_counter_config *,
+                          struct op_system_config *,
+                          int num_counters);
+       void (*start) (struct op_counter_config *);
+       void (*stop) (void);
+       void (*handle_interrupt) (struct pt_regs *,
+                                 struct op_counter_config *);
+       int num_counters;
+};
+
+#endif /* OP_IMPL_H */
diff --git a/arch/ppc/oprofile/op_model_fsl_booke.c b/arch/ppc/oprofile/op_model_fsl_booke.c
new file mode 100644 (file)
index 0000000..fc9c859
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * oprofile/op_model_e500.c
+ *
+ * Freescale Book-E oprofile support, based on ppc64 oprofile support
+ * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM
+ *
+ * Copyright (c) 2004 Freescale Semiconductor, Inc
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala <Kumar.Gala@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/oprofile.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/cputable.h>
+#include <asm/reg_booke.h>
+#include <asm/page.h>
+#include <asm/perfmon.h>
+
+#include "op_impl.h"
+
+static unsigned long reset_value[OP_MAX_COUNTER];
+
+static int num_counters;
+static int oprofile_running;
+
+static inline unsigned int ctr_read(unsigned int i)
+{
+       switch(i) {
+               case 0:
+                       return mfpmr(PMRN_PMC0);
+               case 1:
+                       return mfpmr(PMRN_PMC1);
+               case 2:
+                       return mfpmr(PMRN_PMC2);
+               case 3:
+                       return mfpmr(PMRN_PMC3);
+               default:
+                       return 0;
+       }
+}
+
+static inline void ctr_write(unsigned int i, unsigned int val)
+{
+       switch(i) {
+               case 0:
+                       mtpmr(PMRN_PMC0, val);
+                       break;
+               case 1:
+                       mtpmr(PMRN_PMC1, val);
+                       break;
+               case 2:
+                       mtpmr(PMRN_PMC2, val);
+                       break;
+               case 3:
+                       mtpmr(PMRN_PMC3, val);
+                       break;
+               default:
+                       break;
+       }
+}
+
+
+static void fsl_booke_reg_setup(struct op_counter_config *ctr,
+                            struct op_system_config *sys,
+                            int num_ctrs)
+{
+       int i;
+
+       num_counters = num_ctrs;
+
+       /* freeze all counters */
+       pmc_stop_ctrs();
+
+       /* Our counters count up, and "count" refers to
+        * how much before the next interrupt, and we interrupt
+        * on overflow.  So we calculate the starting value
+        * which will give us "count" until overflow.
+        * Then we set the events on the enabled counters */
+       for (i = 0; i < num_counters; ++i) {
+               reset_value[i] = 0x80000000UL - ctr[i].count;
+
+               init_pmc_stop(i);
+
+               set_pmc_event(i, ctr[i].event);
+
+               set_pmc_user_kernel(i, ctr[i].user, ctr[i].kernel);
+       }
+}
+
+static void fsl_booke_start(struct op_counter_config *ctr)
+{
+       int i;
+
+       mtmsr(mfmsr() | MSR_PMM);
+
+       for (i = 0; i < num_counters; ++i) {
+               if (ctr[i].enabled) {
+                       ctr_write(i, reset_value[i]);
+                       /* Set Each enabled counterd to only
+                        * count when the Mark bit is not set */
+                       set_pmc_marked(i, 1, 0);
+                       pmc_start_ctr(i, 1);
+               } else {
+                       ctr_write(i, 0);
+
+                       /* Set the ctr to be stopped */
+                       pmc_start_ctr(i, 0);
+               }
+       }
+
+       /* Clear the freeze bit, and enable the interrupt.
+        * The counters won't actually start until the rfi clears
+        * the PMM bit */
+       pmc_start_ctrs(1);
+
+       oprofile_running = 1;
+
+       pr_debug("start on cpu %d, pmgc0 %x\n", smp_processor_id(),
+                       mfpmr(PMRN_PMGC0));
+}
+
+static void fsl_booke_stop(void)
+{
+       /* freeze counters */
+       pmc_stop_ctrs();
+
+       oprofile_running = 0;
+
+       pr_debug("stop on cpu %d, pmgc0 %x\n", smp_processor_id(),
+                       mfpmr(PMRN_PMGC0));
+
+       mb();
+}
+
+
+static void fsl_booke_handle_interrupt(struct pt_regs *regs,
+                                   struct op_counter_config *ctr)
+{
+       unsigned long pc;
+       int is_kernel;
+       int val;
+       int i;
+
+       /* set the PMM bit (see comment below) */
+       mtmsr(mfmsr() | MSR_PMM);
+
+       pc = regs->nip;
+       is_kernel = (pc >= KERNELBASE);
+
+       for (i = 0; i < num_counters; ++i) {
+               val = ctr_read(i);
+               if (val < 0) {
+                       if (oprofile_running && ctr[i].enabled) {
+                               oprofile_add_pc(pc, is_kernel, i);
+                               ctr_write(i, reset_value[i]);
+                       } else {
+                               ctr_write(i, 0);
+                       }
+               }
+       }
+
+       /* The freeze bit was set by the interrupt. */
+       /* Clear the freeze bit, and reenable the interrupt.
+        * The counters won't actually start until the rfi clears
+        * the PMM bit */
+       pmc_start_ctrs(1);
+}
+
+struct op_ppc32_model op_model_fsl_booke = {
+       .reg_setup              = fsl_booke_reg_setup,
+       .start                  = fsl_booke_start,
+       .stop                   = fsl_booke_stop,
+       .handle_interrupt       = fsl_booke_handle_interrupt,
+};
diff --git a/arch/ppc/platforms/4xx/ibm440sp.c b/arch/ppc/platforms/4xx/ibm440sp.c
new file mode 100644 (file)
index 0000000..a203efb
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * arch/ppc/platforms/4xx/ibm440sp.c
+ *
+ * PPC440SP I/O descriptions
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ * Copyright 2002-2005 MontaVista Software Inc.
+ *
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2003, 2004 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 <linux/init.h>
+#include <linux/module.h>
+#include <platforms/4xx/ibm440sp.h>
+#include <asm/ocp.h>
+
+static struct ocp_func_emac_data ibm440sp_emac0_def = {
+       .rgmii_idx      = -1,           /* No RGMII */
+       .rgmii_mux      = -1,           /* No RGMII */
+       .zmii_idx       = -1,           /* No ZMII */
+       .zmii_mux       = -1,           /* No ZMII */
+       .mal_idx        = 0,            /* MAL device index */
+       .mal_rx_chan    = 0,            /* MAL rx channel number */
+       .mal_tx_chan    = 0,            /* MAL tx channel number */
+       .wol_irq        = 61,           /* WOL interrupt number */
+       .mdio_idx       = -1,           /* No shared MDIO */
+       .tah_idx        = -1,           /* No TAH */
+       .jumbo          = 1,            /* Jumbo frames supported */
+};
+OCP_SYSFS_EMAC_DATA()
+
+static struct ocp_func_mal_data ibm440sp_mal0_def = {
+       .num_tx_chans   = 4,            /* Number of TX channels */
+       .num_rx_chans   = 4,            /* Number of RX channels */
+       .txeob_irq      = 38,           /* TX End Of Buffer IRQ  */
+       .rxeob_irq      = 39,           /* RX End Of Buffer IRQ  */
+       .txde_irq       = 34,           /* TX Descriptor Error IRQ */
+       .rxde_irq       = 35,           /* RX Descriptor Error IRQ */
+       .serr_irq       = 33,           /* MAL System Error IRQ    */
+};
+OCP_SYSFS_MAL_DATA()
+
+static struct ocp_func_iic_data ibm440sp_iic0_def = {
+       .fast_mode      = 0,            /* Use standad mode (100Khz) */
+};
+
+static struct ocp_func_iic_data ibm440sp_iic1_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        = 0x0000000140000000ULL,
+         .irq          = OCP_IRQ_NA,
+         .pm           = OCP_CPM_NA,
+       },
+       { .vendor       = OCP_VENDOR_IBM,
+         .function     = OCP_FUNC_16550,
+         .index        = 0,
+         .paddr        = PPC440SP_UART0_ADDR,
+         .irq          = UART0_INT,
+         .pm           = IBM_CPM_UART0,
+       },
+       { .vendor       = OCP_VENDOR_IBM,
+         .function     = OCP_FUNC_16550,
+         .index        = 1,
+         .paddr        = PPC440SP_UART1_ADDR,
+         .irq          = UART1_INT,
+         .pm           = IBM_CPM_UART1,
+       },
+       { .vendor       = OCP_VENDOR_IBM,
+         .function     = OCP_FUNC_16550,
+         .index        = 2,
+         .paddr        = PPC440SP_UART2_ADDR,
+         .irq          = UART2_INT,
+         .pm           = IBM_CPM_UART2,
+       },
+       { .vendor       = OCP_VENDOR_IBM,
+         .function     = OCP_FUNC_IIC,
+         .index        = 0,
+         .paddr        = 0x00000001f0000400ULL,
+         .irq          = 2,
+         .pm           = IBM_CPM_IIC0,
+         .additions    = &ibm440sp_iic0_def,
+         .show         = &ocp_show_iic_data
+       },
+       { .vendor       = OCP_VENDOR_IBM,
+         .function     = OCP_FUNC_IIC,
+         .index        = 1,
+         .paddr        = 0x00000001f0000500ULL,
+         .irq          = 3,
+         .pm           = IBM_CPM_IIC1,
+         .additions    = &ibm440sp_iic1_def,
+         .show         = &ocp_show_iic_data
+       },
+       { .vendor       = OCP_VENDOR_IBM,
+         .function     = OCP_FUNC_GPIO,
+         .index        = 0,
+         .paddr        = 0x00000001f0000700ULL,
+         .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    = &ibm440sp_mal0_def,
+         .show         = &ocp_show_mal_data,
+       },
+       { .vendor       = OCP_VENDOR_IBM,
+         .function     = OCP_FUNC_EMAC,
+         .index        = 0,
+         .paddr        = 0x00000001f0000800ULL,
+         .irq          = 60,
+         .pm           = OCP_CPM_NA,
+         .additions    = &ibm440sp_emac0_def,
+         .show         = &ocp_show_emac_data,
+       },
+       { .vendor       = OCP_VENDOR_INVALID
+       }
+};
diff --git a/arch/ppc/platforms/4xx/ibm440sp.h b/arch/ppc/platforms/4xx/ibm440sp.h
new file mode 100644 (file)
index 0000000..c71e46a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * arch/ppc/platforms/4xx/ibm440sp.h
+ *
+ * PPC440SP definitions
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Copyright 2004-2005 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.
+ */
+
+#ifdef __KERNEL__
+#ifndef __PPC_PLATFORMS_IBM440SP_H
+#define __PPC_PLATFORMS_IBM440SP_H
+
+#include <linux/config.h>
+
+#include <asm/ibm44x.h>
+
+/* UART */
+#define PPC440SP_UART0_ADDR    0x00000001f0000200ULL
+#define PPC440SP_UART1_ADDR    0x00000001f0000300ULL
+#define PPC440SP_UART2_ADDR    0x00000001f0000600ULL
+#define UART0_INT              0
+#define UART1_INT              1
+#define UART2_INT              2
+
+/* Clock and Power Management */
+#define IBM_CPM_IIC0           0x80000000      /* IIC interface */
+#define IBM_CPM_IIC1           0x40000000      /* IIC interface */
+#define IBM_CPM_PCI            0x20000000      /* PCI bridge */
+#define IBM_CPM_CPU                0x02000000  /* processor core */
+#define IBM_CPM_DMA                0x01000000  /* DMA controller */
+#define IBM_CPM_BGO                0x00800000  /* PLB to OPB bus arbiter */
+#define IBM_CPM_BGI                0x00400000  /* OPB to PLB bridge */
+#define IBM_CPM_EBC                0x00200000  /* External Bux Controller */
+#define IBM_CPM_EBM                0x00100000  /* Ext Bus Master Interface */
+#define IBM_CPM_DMC                0x00080000  /* SDRAM peripheral controller */
+#define IBM_CPM_PLB                0x00040000  /* PLB bus arbiter */
+#define IBM_CPM_SRAM           0x00020000      /* SRAM memory controller */
+#define IBM_CPM_PPM                0x00002000  /* PLB Performance Monitor */
+#define IBM_CPM_UIC1           0x00001000      /* Universal Interrupt Controller */
+#define IBM_CPM_GPIO0          0x00000800      /* General Purpose IO (??) */
+#define IBM_CPM_GPT                0x00000400  /* General Purpose Timers  */
+#define IBM_CPM_UART0          0x00000200      /* serial port 0 */
+#define IBM_CPM_UART1          0x00000100      /* serial port 1 */
+#define IBM_CPM_UART2          0x00000100      /* serial port 1 */
+#define IBM_CPM_UIC0           0x00000080      /* Universal Interrupt Controller */
+#define IBM_CPM_TMRCLK         0x00000040      /* CPU timers */
+#define IBM_CPM_EMAC0                  0x00000020      /* EMAC 0     */
+
+#define DFLT_IBM4xx_PM         ~(IBM_CPM_UIC | IBM_CPM_UIC1 | IBM_CPM_CPU \
+                               | IBM_CPM_EBC | IBM_CPM_SRAM | IBM_CPM_BGO \
+                               | IBM_CPM_EBM | IBM_CPM_PLB | IBM_CPM_OPB \
+                               | IBM_CPM_TMRCLK | IBM_CPM_DMA | IBM_CPM_PCI \
+                               | IBM_CPM_TAHOE0 | IBM_CPM_TAHOE1 \
+                               | IBM_CPM_EMAC0 | IBM_CPM_EMAC1 \
+                               | IBM_CPM_EMAC2 | IBM_CPM_EMAC3 )
+#endif /* __PPC_PLATFORMS_IBM440SP_H */
+#endif /* __KERNEL__ */
diff --git a/arch/ppc/platforms/4xx/luan.c b/arch/ppc/platforms/4xx/luan.c
new file mode 100644 (file)
index 0000000..1df2339
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * arch/ppc/platforms/4xx/luan.c
+ *
+ * Luan board specific routines
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Copyright 2004-2005 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.
+ */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+#include <linux/initrd.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/ocp.h>
+#include <asm/pci-bridge.h>
+#include <asm/time.h>
+#include <asm/todc.h>
+#include <asm/bootinfo.h>
+#include <asm/ppc4xx_pic.h>
+#include <asm/ppcboot.h>
+
+#include <syslib/ibm44x_common.h>
+#include <syslib/ibm440gx_common.h>
+#include <syslib/ibm440sp_common.h>
+
+/*
+ * This is a horrible kludge, we eventually need to abstract this
+ * generic PHY stuff, so the  standard phy mode defines can be
+ * easily used from arch code.
+ */
+#include "../../../../drivers/net/ibm_emac/ibm_emac_phy.h"
+
+bd_t __res;
+
+static struct ibm44x_clocks clocks __initdata;
+
+static void __init
+luan_calibrate_decr(void)
+{
+       unsigned int freq;
+
+       if (mfspr(SPRN_CCR1) & CCR1_TCS)
+               freq = LUAN_TMR_CLK;
+       else
+               freq = clocks.cpu;
+
+       ibm44x_calibrate_decr(freq);
+}
+
+static int
+luan_show_cpuinfo(struct seq_file *m)
+{
+       seq_printf(m, "vendor\t\t: IBM\n");
+       seq_printf(m, "machine\t\t: PPC440SP EVB (Luan)\n");
+
+       return 0;
+}
+
+static inline int
+luan_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+       struct pci_controller *hose = pci_bus_to_hose(dev->bus->number);
+
+       /* PCIX0 in adapter mode, no host interrupt routing */
+
+       /* PCIX1 */
+       if (hose->index == 0) {
+               static char pci_irq_table[][4] =
+               /*
+                *      PCI IDSEL/INTPIN->INTLINE
+                *        A   B   C   D
+                */
+               {
+                       { 49, 49, 49, 49 },     /* IDSEL 1 - PCIX1 Slot 0 */
+                       { 49, 49, 49, 49 },     /* IDSEL 2 - PCIX1 Slot 1 */
+                       { 49, 49, 49, 49 },     /* IDSEL 3 - PCIX1 Slot 2 */
+                       { 49, 49, 49, 49 },     /* IDSEL 4 - PCIX1 Slot 3 */
+               };
+               const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       /* PCIX2 */
+       } else if (hose->index == 1) {
+               static char pci_irq_table[][4] =
+               /*
+                *      PCI IDSEL/INTPIN->INTLINE
+                *        A   B   C   D
+                */
+               {
+                       { 50, 50, 50, 50 },     /* IDSEL 1 - PCIX2 Slot 0 */
+                       { 50, 50, 50, 50 },     /* IDSEL 2 - PCIX2 Slot 1 */
+                       { 50, 50, 50, 50 },     /* IDSEL 3 - PCIX2 Slot 2 */
+                       { 50, 50, 50, 50 },     /* IDSEL 4 - PCIX2 Slot 3 */
+               };
+               const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       }
+       return -1;
+}
+
+static void __init luan_set_emacdata(void)
+{
+       struct ocp_def *def;
+       struct ocp_func_emac_data *emacdata;
+
+       /* Set phy_map, phy_mode, and mac_addr for the EMAC */
+       def = ocp_get_one_device(OCP_VENDOR_IBM, OCP_FUNC_EMAC, 0);
+       emacdata = def->additions;
+       emacdata->phy_map = 0x00000001; /* Skip 0x00 */
+       emacdata->phy_mode = PHY_MODE_GMII;
+       memcpy(emacdata->mac_addr, __res.bi_enetaddr, 6);
+}
+
+#define PCIX_READW(offset) \
+       (readw((void *)((u32)pcix_reg_base+offset)))
+
+#define PCIX_WRITEW(value, offset) \
+       (writew(value, (void *)((u32)pcix_reg_base+offset)))
+
+#define PCIX_WRITEL(value, offset) \
+       (writel(value, (void *)((u32)pcix_reg_base+offset)))
+
+static void __init
+luan_setup_pcix(void)
+{
+       int i;
+       void *pcix_reg_base;
+
+       for (i=0;i<3;i++) {
+               pcix_reg_base = ioremap64(PCIX0_REG_BASE + i*PCIX_REG_OFFSET, PCIX_REG_SIZE);
+
+               /* Enable PCIX0 I/O, Mem, and Busmaster cycles */
+               PCIX_WRITEW(PCIX_READW(PCIX0_COMMAND) | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER, PCIX0_COMMAND);
+
+               /* Disable all windows */
+               PCIX_WRITEL(0, PCIX0_POM0SA);
+               PCIX_WRITEL(0, PCIX0_POM1SA);
+               PCIX_WRITEL(0, PCIX0_POM2SA);
+               PCIX_WRITEL(0, PCIX0_PIM0SA);
+               PCIX_WRITEL(0, PCIX0_PIM0SAH);
+               PCIX_WRITEL(0, PCIX0_PIM1SA);
+               PCIX_WRITEL(0, PCIX0_PIM2SA);
+               PCIX_WRITEL(0, PCIX0_PIM2SAH);
+
+               /*
+                * Setup 512MB PLB->PCI outbound mem window
+                * (a_n000_0000->0_n000_0000)
+                * */
+               PCIX_WRITEL(0x0000000a, PCIX0_POM0LAH);
+               PCIX_WRITEL(0x80000000 | i*LUAN_PCIX_MEM_SIZE, PCIX0_POM0LAL);
+               PCIX_WRITEL(0x00000000, PCIX0_POM0PCIAH);
+               PCIX_WRITEL(0x80000000 | i*LUAN_PCIX_MEM_SIZE, PCIX0_POM0PCIAL);
+               PCIX_WRITEL(0xe0000001, PCIX0_POM0SA);
+
+               /* Setup 2GB PCI->PLB inbound memory window at 0, enable MSIs */
+               PCIX_WRITEL(0x00000000, PCIX0_PIM0LAH);
+               PCIX_WRITEL(0x00000000, PCIX0_PIM0LAL);
+               PCIX_WRITEL(0xe0000007, PCIX0_PIM0SA);
+               PCIX_WRITEL(0xffffffff, PCIX0_PIM0SAH);
+
+               iounmap(pcix_reg_base);
+       }
+
+       eieio();
+}
+
+static void __init
+luan_setup_hose(struct pci_controller *hose,
+               int lower_mem,
+               int upper_mem,
+               int cfga,
+               int cfgd,
+               u64 pcix_io_base)
+{
+       char name[20];
+
+       sprintf(name, "PCIX%d host bridge", hose->index);
+
+       hose->pci_mem_offset = LUAN_PCIX_MEM_OFFSET;
+
+       pci_init_resource(&hose->io_resource,
+                       LUAN_PCIX_LOWER_IO,
+                       LUAN_PCIX_UPPER_IO,
+                       IORESOURCE_IO,
+                       name);
+
+       pci_init_resource(&hose->mem_resources[0],
+                       lower_mem,
+                       upper_mem,
+                       IORESOURCE_MEM,
+                       name);
+
+       hose->io_space.start = LUAN_PCIX_LOWER_IO;
+       hose->io_space.end = LUAN_PCIX_UPPER_IO;
+       hose->mem_space.start = lower_mem;
+       hose->mem_space.end = upper_mem;
+       isa_io_base =
+               (unsigned long)ioremap64(pcix_io_base, PCIX_IO_SIZE);
+       hose->io_base_virt = (void *)isa_io_base;
+
+       setup_indirect_pci(hose, cfga, cfgd);
+       hose->set_cfg_type = 1;
+}
+
+static void __init
+luan_setup_hoses(void)
+{
+       struct pci_controller *hose1, *hose2;
+
+       /* Configure windows on the PCI-X host bridge */
+       luan_setup_pcix();
+
+       /* Allocate hoses for PCIX1 and PCIX2 */
+       hose1 = pcibios_alloc_controller();
+       hose2 = pcibios_alloc_controller();
+       if (!hose1 || !hose2)
+               return;
+
+       /* Setup PCIX1 */
+       hose1->first_busno = 0;
+       hose1->last_busno = 0xff;
+
+       luan_setup_hose(hose1,
+                       LUAN_PCIX1_LOWER_MEM,
+                       LUAN_PCIX1_UPPER_MEM,
+                       PCIX1_CFGA,
+                       PCIX1_CFGD,
+                       PCIX1_IO_BASE);
+
+       hose1->last_busno = pciauto_bus_scan(hose1, hose1->first_busno);
+
+       /* Setup PCIX2 */
+       hose2->first_busno = hose1->last_busno + 1;
+       hose2->last_busno = 0xff;
+
+       luan_setup_hose(hose2,
+                       LUAN_PCIX2_LOWER_MEM,
+                       LUAN_PCIX2_UPPER_MEM,
+                       PCIX2_CFGA,
+                       PCIX2_CFGD,
+                       PCIX2_IO_BASE);
+
+       hose2->last_busno = pciauto_bus_scan(hose2, hose2->first_busno);
+
+       ppc_md.pci_swizzle = common_swizzle;
+       ppc_md.pci_map_irq = luan_map_irq;
+}
+
+TODC_ALLOC();
+
+static void __init
+luan_early_serial_map(void)
+{
+       struct uart_port port;
+
+       /* Setup ioremapped serial port access */
+       memset(&port, 0, sizeof(port));
+       port.membase = ioremap64(PPC440SP_UART0_ADDR, 8);
+       port.irq = UART0_INT;
+       port.uartclk = clocks.uart0;
+       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 = ioremap64(PPC440SP_UART1_ADDR, 8);
+       port.irq = UART1_INT;
+       port.uartclk = clocks.uart1;
+       port.line = 1;
+
+       if (early_serial_setup(&port) != 0) {
+               printk("Early serial init of port 1 failed\n");
+       }
+
+       port.membase = ioremap64(PPC440SP_UART2_ADDR, 8);
+       port.irq = UART2_INT;
+       port.uartclk = BASE_BAUD;
+       port.line = 2;
+
+       if (early_serial_setup(&port) != 0) {
+               printk("Early serial init of port 2 failed\n");
+       }
+}
+
+static void __init
+luan_setup_arch(void)
+{
+       luan_set_emacdata();
+
+#if !defined(CONFIG_BDI_SWITCH)
+       /*
+        * The Abatron BDI JTAG debugger does not tolerate others
+        * mucking with the debug registers.
+        */
+        mtspr(SPRN_DBCR0, (DBCR0_TDE | DBCR0_IDM));
+#endif
+
+       /*
+        * Determine various clocks.
+        * To be completely correct we should get SysClk
+        * from FPGA, because it can be changed by on-board switches
+        * --ebs
+        */
+       /* 440GX and 440SP clocking is the same -mdp */
+       ibm440gx_get_clocks(&clocks, 33333333, 6 * 1843200);
+       ocp_sys_info.opb_bus_freq = clocks.opb;
+
+       /* init to some ~sane value until calibrate_delay() runs */
+        loops_per_jiffy = 50000000/HZ;
+
+       /* Setup PCIXn host bridges */
+       luan_setup_hoses();
+
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (initrd_start)
+               ROOT_DEV = Root_RAM0;
+       else
+#endif
+#ifdef CONFIG_ROOT_NFS
+               ROOT_DEV = Root_NFS;
+#else
+               ROOT_DEV = Root_HDA1;
+#endif
+
+       luan_early_serial_map();
+
+       /* Identify the system */
+       printk("Luan port (MontaVista Software, Inc. <source@mvista.com>)\n");
+}
+
+void __init platform_init(unsigned long r3, unsigned long r4,
+               unsigned long r5, unsigned long r6, unsigned long r7)
+{
+       parse_bootinfo(find_bootinfo());
+
+       /*
+        * If we were passed in a board information, copy it into the
+        * residual data area.
+        */
+       if (r3)
+               __res = *(bd_t *)(r3 + KERNELBASE);
+
+       ibm44x_platform_init();
+
+       ppc_md.setup_arch = luan_setup_arch;
+       ppc_md.show_cpuinfo = luan_show_cpuinfo;
+       ppc_md.find_end_of_memory = ibm440sp_find_end_of_memory;
+       ppc_md.get_irq = NULL;          /* Set in ppc4xx_pic_init() */
+
+       ppc_md.calibrate_decr = luan_calibrate_decr;
+#ifdef CONFIG_KGDB
+       ppc_md.early_serial_map = luan_early_serial_map;
+#endif
+}
diff --git a/arch/ppc/platforms/4xx/luan.h b/arch/ppc/platforms/4xx/luan.h
new file mode 100644 (file)
index 0000000..09b444c
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * arch/ppc/platforms/4xx/luan.h
+ *
+ * Luan board definitions
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Copyright 2004-2005 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.
+ *
+ */
+
+#ifdef __KERNEL__
+#ifndef __ASM_LUAN_H__
+#define __ASM_LUAN_H__
+
+#include <linux/config.h>
+#include <platforms/4xx/ibm440sp.h>
+
+/* F/W TLB mapping used in bootloader glue to reset EMAC */
+#define PPC44x_EMAC0_MR0       0xa0000800
+
+/* Location of MAC addresses in PIBS image */
+#define PIBS_FLASH_BASE                0xffe00000
+#define PIBS_MAC_BASE          (PIBS_FLASH_BASE+0x1b0400)
+
+/* External timer clock frequency */
+#define LUAN_TMR_CLK           25000000
+
+/* Flash */
+#define LUAN_FPGA_REG_0                        0x0000000148300000ULL
+#define LUAN_BOOT_LARGE_FLASH(x)       (x & 0x40)
+#define LUAN_SMALL_FLASH_LOW           0x00000001ff900000ULL
+#define LUAN_SMALL_FLASH_HIGH          0x00000001ffe00000ULL
+#define LUAN_SMALL_FLASH_SIZE          0x100000
+#define LUAN_LARGE_FLASH_LOW           0x00000001ff800000ULL
+#define LUAN_LARGE_FLASH_HIGH          0x00000001ffc00000ULL
+#define LUAN_LARGE_FLASH_SIZE          0x400000
+
+/*
+ * Serial port defines
+ */
+#define RS_TABLE_SIZE  3
+
+/* PIBS defined UART mappings, used before early_serial_setup */
+#define UART0_IO_BASE  0xa0000200
+#define UART1_IO_BASE  0xa0000300
+#define UART2_IO_BASE  0xa0000600
+
+#define BASE_BAUD      11059200
+#define STD_UART_OP(num)                                       \
+       { 0, BASE_BAUD, 0, UART##num##_INT,                     \
+               (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST),        \
+               iomem_base: UART##num##_IO_BASE,                \
+               io_type: SERIAL_IO_MEM},
+
+#define SERIAL_PORT_DFNS       \
+       STD_UART_OP(0)          \
+       STD_UART_OP(1)          \
+       STD_UART_OP(2)
+
+/* PCI support */
+#define LUAN_PCIX_LOWER_IO     0x00000000
+#define LUAN_PCIX_UPPER_IO     0x0000ffff
+#define LUAN_PCIX0_LOWER_MEM   0x80000000
+#define LUAN_PCIX0_UPPER_MEM   0x9fffffff
+#define LUAN_PCIX1_LOWER_MEM   0xa0000000
+#define LUAN_PCIX1_UPPER_MEM   0xbfffffff
+#define LUAN_PCIX2_LOWER_MEM   0xc0000000
+#define LUAN_PCIX2_UPPER_MEM   0xdfffffff
+
+#define LUAN_PCIX_MEM_SIZE     0x20000000
+#define LUAN_PCIX_MEM_OFFSET   0x00000000
+
+#endif                         /* __ASM_LUAN_H__ */
+#endif                         /* __KERNEL__ */
diff --git a/arch/ppc/platforms/4xx/virtex-ii_pro.c b/arch/ppc/platforms/4xx/virtex-ii_pro.c
new file mode 100644 (file)
index 0000000..097cc9d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * arch/ppc/platforms/4xx/virtex-ii_pro.c
+ *
+ * Author: MontaVista Software, Inc.
+ *         source@mvista.com
+ *
+ * 2002-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 <linux/config.h>
+#include <linux/init.h>
+#include <asm/ocp.h>
+#include "virtex-ii_pro.h"
+
+/* Have OCP take care of the serial ports. */
+struct ocp_def core_ocp[] = {
+#ifdef XPAR_UARTNS550_0_BASEADDR
+       { .vendor       = OCP_VENDOR_XILINX,
+         .function     = OCP_FUNC_16550,
+         .index        = 0,
+         .paddr        = XPAR_UARTNS550_0_BASEADDR,
+         .irq          = XPAR_INTC_0_UARTNS550_0_VEC_ID,
+         .pm           = OCP_CPM_NA
+       },
+#ifdef XPAR_UARTNS550_1_BASEADDR
+       { .vendor       = OCP_VENDOR_XILINX,
+         .function     = OCP_FUNC_16550,
+         .index        = 1,
+         .paddr        = XPAR_UARTNS550_1_BASEADDR,
+         .irq          = XPAR_INTC_0_UARTNS550_1_VEC_ID,
+         .pm           = OCP_CPM_NA
+       },
+#ifdef XPAR_UARTNS550_2_BASEADDR
+       { .vendor       = OCP_VENDOR_XILINX,
+         .function     = OCP_FUNC_16550,
+         .index        = 2,
+         .paddr        = XPAR_UARTNS550_2_BASEADDR,
+         .irq          = XPAR_INTC_0_UARTNS550_2_VEC_ID,
+         .pm           = OCP_CPM_NA
+       },
+#ifdef XPAR_UARTNS550_3_BASEADDR
+       { .vendor       = OCP_VENDOR_XILINX,
+         .function     = OCP_FUNC_16550,
+         .index        = 3,
+         .paddr        = XPAR_UARTNS550_3_BASEADDR,
+         .irq          = XPAR_INTC_0_UARTNS550_3_VEC_ID,
+         .pm           = OCP_CPM_NA
+       },
+#ifdef XPAR_UARTNS550_4_BASEADDR
+#error Edit this file to add more devices.
+#endif                 /* 4 */
+#endif                 /* 3 */
+#endif                 /* 2 */
+#endif                 /* 1 */
+#endif                 /* 0 */
+       { .vendor       = OCP_VENDOR_INVALID
+       }
+};
diff --git a/arch/ppc/platforms/4xx/virtex-ii_pro.h b/arch/ppc/platforms/4xx/virtex-ii_pro.h
new file mode 100644 (file)
index 0000000..9014c48
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * arch/ppc/platforms/4xx/virtex-ii_pro.h
+ *
+ * Include file that defines the Xilinx Virtex-II Pro processor
+ *
+ * Author: MontaVista Software, Inc.
+ *         source@mvista.com
+ *
+ * 2002-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.
+ */
+
+#ifdef __KERNEL__
+#ifndef __ASM_VIRTEXIIPRO_H__
+#define __ASM_VIRTEXIIPRO_H__
+
+#include <linux/config.h>
+#include <asm/xparameters.h>
+
+/* serial defines */
+
+#define RS_TABLE_SIZE  4       /* change this and add more devices below
+                                  if you have more then 4 16x50 UARTs */
+
+#define BASE_BAUD              (XPAR_UARTNS550_0_CLOCK_FREQ_HZ/16)
+
+/* The serial ports in the Virtex-II Pro have each I/O byte in the
+ * LSByte of a word.  This means that iomem_reg_shift needs to be 2 to
+ * change the byte offsets into word offsets.  In addition the base
+ * addresses need to have 3 added to them to get to the LSByte.
+ */
+#define STD_UART_OP(num)                                                \
+       { 0, BASE_BAUD, 0, XPAR_INTC_0_UARTNS550_##num##_VEC_ID,         \
+               ASYNC_BOOT_AUTOCONF,                                     \
+               .iomem_base = (u8 *)XPAR_UARTNS550_##num##_BASEADDR + 3, \
+               .iomem_reg_shift = 2,                                    \
+               .io_type = SERIAL_IO_MEM},
+
+#if defined(XPAR_INTC_0_UARTNS550_0_VEC_ID)
+#define ML300_UART0 STD_UART_OP(0)
+#else
+#define ML300_UART0
+#endif
+
+#if defined(XPAR_INTC_0_UARTNS550_1_VEC_ID)
+#define ML300_UART1 STD_UART_OP(1)
+#else
+#define ML300_UART1
+#endif
+
+#if defined(XPAR_INTC_0_UARTNS550_2_VEC_ID)
+#define ML300_UART2 STD_UART_OP(2)
+#else
+#define ML300_UART2
+#endif
+
+#if defined(XPAR_INTC_0_UARTNS550_3_VEC_ID)
+#define ML300_UART3 STD_UART_OP(3)
+#else
+#define ML300_UART3
+#endif
+
+#if defined(XPAR_INTC_0_UARTNS550_4_VEC_ID)
+#error Edit this file to add more devices.
+#elif defined(XPAR_INTC_0_UARTNS550_3_VEC_ID)
+#define NR_SER_PORTS   4
+#elif defined(XPAR_INTC_0_UARTNS550_2_VEC_ID)
+#define NR_SER_PORTS   3
+#elif defined(XPAR_INTC_0_UARTNS550_1_VEC_ID)
+#define NR_SER_PORTS   2
+#elif defined(XPAR_INTC_0_UARTNS550_0_VEC_ID)
+#define NR_SER_PORTS   1
+#else
+#define NR_SER_PORTS   0
+#endif
+
+#if defined(CONFIG_UART0_TTYS0)
+#define SERIAL_PORT_DFNS       \
+       ML300_UART0             \
+       ML300_UART1             \
+       ML300_UART2             \
+       ML300_UART3
+#endif
+
+#if defined(CONFIG_UART0_TTYS1)
+#define SERIAL_PORT_DFNS       \
+       ML300_UART1             \
+       ML300_UART0             \
+       ML300_UART2             \
+       ML300_UART3
+#endif
+
+#define DCRN_CPMFR_BASE        0
+
+#include <asm/ibm405.h>
+
+#endif                         /* __ASM_VIRTEXIIPRO_H__ */
+#endif                         /* __KERNEL__ */
diff --git a/arch/ppc/platforms/4xx/xilinx_ml300.c b/arch/ppc/platforms/4xx/xilinx_ml300.c
new file mode 100644 (file)
index 0000000..d07fe02
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * arch/ppc/platforms/4xx/xilinx_ml300.c
+ *
+ * Xilinx ML300 evaluation board initialization
+ *
+ * Author: MontaVista Software, Inc.
+ *         source@mvista.com
+ *
+ * 2002-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 <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serialP.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/ocp.h>
+
+#include <platforms/4xx/virtex-ii_pro.h>       /* for NR_SER_PORTS */
+
+/*
+ * As an overview of how the following functions (platform_init,
+ * ml300_map_io, ml300_setup_arch and ml300_init_IRQ) fit into the
+ * kernel startup procedure, here's a call tree:
+ *
+ * start_here                                  arch/ppc/kernel/head_4xx.S
+ *  early_init                                 arch/ppc/kernel/setup.c
+ *  machine_init                               arch/ppc/kernel/setup.c
+ *    platform_init                            this file
+ *      ppc4xx_init                            arch/ppc/syslib/ppc4xx_setup.c
+ *        parse_bootinfo
+ *          find_bootinfo
+ *        "setup some default ppc_md pointers"
+ *  MMU_init                                   arch/ppc/mm/init.c
+ *    *ppc_md.setup_io_mappings == ml300_map_io        this file
+ *      ppc4xx_map_io                          arch/ppc/syslib/ppc4xx_setup.c
+ *  start_kernel                               init/main.c
+ *    setup_arch                               arch/ppc/kernel/setup.c
+ * #if defined(CONFIG_KGDB)
+ *      *ppc_md.kgdb_map_scc() == gen550_kgdb_map_scc
+ * #endif
+ *      *ppc_md.setup_arch == ml300_setup_arch this file
+ *        ppc4xx_setup_arch                    arch/ppc/syslib/ppc4xx_setup.c
+ *          ppc4xx_find_bridges                        arch/ppc/syslib/ppc405_pci.c
+ *    init_IRQ                                 arch/ppc/kernel/irq.c
+ *      *ppc_md.init_IRQ == ml300_init_IRQ     this file
+ *        ppc4xx_init_IRQ                      arch/ppc/syslib/ppc4xx_setup.c
+ *          ppc4xx_pic_init                    arch/ppc/syslib/xilinx_pic.c
+ */
+
+#if defined(XPAR_POWER_0_POWERDOWN_BASEADDR)
+
+static volatile unsigned *powerdown_base =
+    (volatile unsigned *) XPAR_POWER_0_POWERDOWN_BASEADDR;
+
+static void
+xilinx_power_off(void)
+{
+       local_irq_disable();
+       out_be32(powerdown_base, XPAR_POWER_0_POWERDOWN_VALUE);
+       while (1) ;
+}
+#endif
+
+void __init
+ml300_map_io(void)
+{
+       ppc4xx_map_io();
+
+#if defined(XPAR_POWER_0_POWERDOWN_BASEADDR)
+       powerdown_base = ioremap((unsigned long) powerdown_base,
+                                XPAR_POWER_0_POWERDOWN_HIGHADDR -
+                                XPAR_POWER_0_POWERDOWN_BASEADDR + 1);
+#endif
+}
+
+static void __init
+ml300_early_serial_map(void)
+{
+#ifdef CONFIG_SERIAL_8250
+       struct serial_state old_ports[] = { SERIAL_PORT_DFNS };
+       struct uart_port port;
+       int i;
+
+       /* Setup ioremapped serial port access */
+       for (i = 0; i < ARRAY_SIZE(old_ports); i++ ) {
+               memset(&port, 0, sizeof(port));
+               port.membase = ioremap((phys_addr_t)(old_ports[i].iomem_base), 16);
+               port.irq = old_ports[i].irq;
+               port.uartclk = old_ports[i].baud_base * 16;
+               port.regshift = old_ports[i].iomem_reg_shift;
+               port.iotype = SERIAL_IO_MEM;
+               port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
+               port.line = i;
+
+               if (early_serial_setup(&port) != 0) {
+                       printk("Early serial init of port %d failed\n", i);
+               }
+       }
+#endif /* CONFIG_SERIAL_8250 */
+}
+
+void __init
+ml300_setup_arch(void)
+{
+       ppc4xx_setup_arch();    /* calls ppc4xx_find_bridges() */
+
+       ml300_early_serial_map();
+
+       /* Identify the system */
+       printk(KERN_INFO "Xilinx Virtex-II Pro port\n");
+       printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n");
+}
+
+/* Called after board_setup_irq from ppc4xx_init_IRQ(). */
+void __init
+ml300_init_irq(void)
+{
+       unsigned int i;
+
+       ppc4xx_init_IRQ();
+
+       /*
+        * For PowerPC 405 cores the default value for NR_IRQS is 32.
+        * See include/asm-ppc/irq.h for details.
+        * This is just fine for ML300.
+        */
+#if (NR_IRQS != 32)
+#error NR_IRQS must be 32 for ML300
+#endif
+
+       for (i = 0; i < NR_IRQS; i++) {
+               if (XPAR_INTC_0_KIND_OF_INTR & (0x80000000 >> i))
+                       irq_desc[i].status &= ~IRQ_LEVEL;
+               else
+                       irq_desc[i].status |= IRQ_LEVEL;
+       }
+}
+
+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 = ml300_setup_arch;
+       ppc_md.setup_io_mappings = ml300_map_io;
+       ppc_md.init_IRQ = ml300_init_irq;
+
+#if defined(XPAR_POWER_0_POWERDOWN_BASEADDR)
+       ppc_md.power_off = xilinx_power_off;
+#endif
+
+#ifdef CONFIG_KGDB
+       ppc_md.early_serial_map = ml300_early_serial_map;
+#endif
+}
+
diff --git a/arch/ppc/platforms/4xx/xilinx_ml300.h b/arch/ppc/platforms/4xx/xilinx_ml300.h
new file mode 100644 (file)
index 0000000..f8c5884
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * arch/ppc/platforms/4xx/xilinx_ml300.h
+ *
+ * Include file that defines the Xilinx ML300 evaluation board
+ *
+ * Author: MontaVista Software, Inc.
+ *         source@mvista.com
+ *
+ * 2002-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.
+ */
+
+#ifdef __KERNEL__
+#ifndef __ASM_XILINX_ML300_H__
+#define __ASM_XILINX_ML300_H__
+
+/* ML300 has a Xilinx Virtex-II Pro processor */
+#include <platforms/4xx/virtex-ii_pro.h>
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+
+typedef struct board_info {
+       unsigned int     bi_memsize;            /* DRAM installed, in bytes */
+       unsigned char    bi_enetaddr[6];        /* Local 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 */
+} bd_t;
+
+/* Some 4xx parts use a different timebase frequency from the internal clock.
+*/
+#define bi_tbfreq bi_intfreq
+
+#endif /* !__ASSEMBLY__ */
+
+/* We don't need anything mapped.  Size of zero will accomplish that. */
+#define PPC4xx_ONB_IO_PADDR    0u
+#define PPC4xx_ONB_IO_VADDR    0u
+#define PPC4xx_ONB_IO_SIZE     0u
+
+#define PPC4xx_MACHINE_NAME "Xilinx ML300"
+
+#endif /* __ASM_XILINX_ML300_H__ */
+#endif /* __KERNEL__ */
diff --git a/arch/ppc/platforms/4xx/xparameters/xparameters_ml300.h b/arch/ppc/platforms/4xx/xparameters/xparameters_ml300.h
new file mode 100644 (file)
index 0000000..97e3f4d
--- /dev/null
@@ -0,0 +1,310 @@
+/*******************************************************************
+*
+*     Author: Xilinx, 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.
+*
+*
+*     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A
+*     COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
+*     ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD,
+*     XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE
+*     FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING
+*     ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.
+*     XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO
+*     THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY
+*     WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM
+*     CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND
+*     FITNESS FOR A PARTICULAR PURPOSE.
+*
+*
+*     Xilinx hardware products are not intended for use in life support
+*     appliances, devices, or systems. Use in such applications is
+*     expressly prohibited.
+*
+*
+*     (c) Copyright 2002-2004 Xilinx Inc.
+*     All rights reserved.
+*
+*
+*     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.
+*
+* Description: Driver parameters
+*
+*******************************************************************/
+
+#define XPAR_XPCI_NUM_INSTANCES 1
+#define XPAR_XPCI_CLOCK_HZ 33333333
+#define XPAR_OPB_PCI_REF_0_DEVICE_ID 0
+#define XPAR_OPB_PCI_REF_0_BASEADDR 0x20000000
+#define XPAR_OPB_PCI_REF_0_HIGHADDR 0x3FFFFFFF
+#define XPAR_OPB_PCI_REF_0_CONFIG_ADDR 0x3C000000
+#define XPAR_OPB_PCI_REF_0_CONFIG_DATA 0x3C000004
+#define XPAR_OPB_PCI_REF_0_LCONFIG_ADDR 0x3E000000
+#define XPAR_OPB_PCI_REF_0_MEM_BASEADDR 0x20000000
+#define XPAR_OPB_PCI_REF_0_MEM_HIGHADDR 0x37FFFFFF
+#define XPAR_OPB_PCI_REF_0_IO_BASEADDR 0x38000000
+#define XPAR_OPB_PCI_REF_0_IO_HIGHADDR 0x3BFFFFFF
+
+/******************************************************************/
+
+#define XPAR_XEMAC_NUM_INSTANCES 1
+#define XPAR_OPB_ETHERNET_0_BASEADDR 0x60000000
+#define XPAR_OPB_ETHERNET_0_HIGHADDR 0x60003FFF
+#define XPAR_OPB_ETHERNET_0_DEVICE_ID 0
+#define XPAR_OPB_ETHERNET_0_ERR_COUNT_EXIST 1
+#define XPAR_OPB_ETHERNET_0_DMA_PRESENT 1
+#define XPAR_OPB_ETHERNET_0_MII_EXIST 1
+
+/******************************************************************/
+
+#define XPAR_MY_OPB_GPIO_0_DEVICE_ID_0 0
+#define XPAR_MY_OPB_GPIO_0_BASEADDR_0 0x90000000
+#define XPAR_MY_OPB_GPIO_0_HIGHADDR_0 (0x90000000+0x7)
+#define XPAR_MY_OPB_GPIO_0_DEVICE_ID_1 1
+#define XPAR_MY_OPB_GPIO_0_BASEADDR_1 (0x90000000+0x8)
+#define XPAR_MY_OPB_GPIO_0_HIGHADDR_1 (0x90000000+0x1F)
+#define XPAR_XGPIO_NUM_INSTANCES 2
+
+/******************************************************************/
+
+#define XPAR_XIIC_NUM_INSTANCES 1
+#define XPAR_OPB_IIC_0_BASEADDR 0xA8000000
+#define XPAR_OPB_IIC_0_HIGHADDR 0xA80001FF
+#define XPAR_OPB_IIC_0_DEVICE_ID 0
+#define XPAR_OPB_IIC_0_TEN_BIT_ADR 0
+
+/******************************************************************/
+
+#define XPAR_XUARTNS550_NUM_INSTANCES 2
+#define XPAR_XUARTNS550_CLOCK_HZ 100000000
+#define XPAR_OPB_UART16550_0_BASEADDR 0xA0000000
+#define XPAR_OPB_UART16550_0_HIGHADDR 0xA0001FFF
+#define XPAR_OPB_UART16550_0_DEVICE_ID 0
+#define XPAR_OPB_UART16550_1_BASEADDR 0xA0010000
+#define XPAR_OPB_UART16550_1_HIGHADDR 0xA0011FFF
+#define XPAR_OPB_UART16550_1_DEVICE_ID 1
+
+/******************************************************************/
+
+#define XPAR_XSPI_NUM_INSTANCES 1
+#define XPAR_OPB_SPI_0_BASEADDR 0xA4000000
+#define XPAR_OPB_SPI_0_HIGHADDR 0xA400007F
+#define XPAR_OPB_SPI_0_DEVICE_ID 0
+#define XPAR_OPB_SPI_0_FIFO_EXIST 1
+#define XPAR_OPB_SPI_0_SPI_SLAVE_ONLY 0
+#define XPAR_OPB_SPI_0_NUM_SS_BITS 1
+
+/******************************************************************/
+
+#define XPAR_XPS2_NUM_INSTANCES 2
+#define XPAR_OPB_PS2_DUAL_REF_0_DEVICE_ID_0 0
+#define XPAR_OPB_PS2_DUAL_REF_0_BASEADDR_0 0xA9000000
+#define XPAR_OPB_PS2_DUAL_REF_0_HIGHADDR_0 (0xA9000000+0x3F)
+#define XPAR_OPB_PS2_DUAL_REF_0_DEVICE_ID_1 1
+#define XPAR_OPB_PS2_DUAL_REF_0_BASEADDR_1 (0xA9000000+0x1000)
+#define XPAR_OPB_PS2_DUAL_REF_0_HIGHADDR_1 (0xA9000000+0x103F)
+
+/******************************************************************/
+
+#define XPAR_XTOUCHSCREEN_NUM_INSTANCES 1
+#define XPAR_OPB_TSD_REF_0_BASEADDR 0xAA000000
+#define XPAR_OPB_TSD_REF_0_HIGHADDR 0xAA000007
+#define XPAR_OPB_TSD_REF_0_DEVICE_ID 0
+
+/******************************************************************/
+
+#define XPAR_OPB_AC97_CONTROLLER_REF_0_BASEADDR 0xA6000000
+#define XPAR_OPB_AC97_CONTROLLER_REF_0_HIGHADDR 0xA60000FF
+#define XPAR_OPB_PAR_PORT_REF_0_BASEADDR 0x90010000
+#define XPAR_OPB_PAR_PORT_REF_0_HIGHADDR 0x900100FF
+#define XPAR_PLB_DDR_0_BASEADDR 0x00000000
+#define XPAR_PLB_DDR_0_HIGHADDR 0x0FFFFFFF
+
+/******************************************************************/
+
+#define XPAR_XINTC_HAS_IPR 1
+#define XPAR_INTC_MAX_NUM_INTR_INPUTS 18
+#define XPAR_XINTC_USE_DCR 0
+#define XPAR_XINTC_NUM_INSTANCES 1
+#define XPAR_DCR_INTC_0_BASEADDR 0xD0000FC0
+#define XPAR_DCR_INTC_0_HIGHADDR 0xD0000FDF
+#define XPAR_DCR_INTC_0_DEVICE_ID 0
+#define XPAR_DCR_INTC_0_KIND_OF_INTR 0x00038000
+
+/******************************************************************/
+
+#define XPAR_DCR_INTC_0_MISC_LOGIC_0_PHY_MII_INT_INTR 0
+#define XPAR_DCR_INTC_0_OPB_ETHERNET_0_IP2INTC_IRPT_INTR 1
+#define XPAR_DCR_INTC_0_MISC_LOGIC_0_IIC_TEMP_CRIT_INTR 2
+#define XPAR_DCR_INTC_0_MISC_LOGIC_0_IIC_IRQ_INTR 3
+#define XPAR_DCR_INTC_0_OPB_IIC_0_IP2INTC_IRPT_INTR 4
+#define XPAR_DCR_INTC_0_OPB_SYSACE_0_SYSACE_IRQ_INTR 5
+#define XPAR_DCR_INTC_0_OPB_UART16550_0_IP2INTC_IRPT_INTR 6
+#define XPAR_DCR_INTC_0_OPB_UART16550_1_IP2INTC_IRPT_INTR 7
+#define XPAR_DCR_INTC_0_OPB_PS2_DUAL_REF_0_SYS_INTR1_INTR 8
+#define XPAR_DCR_INTC_0_OPB_PS2_DUAL_REF_0_SYS_INTR2_INTR 9
+#define XPAR_DCR_INTC_0_OPB_SPI_0_IP2INTC_IRPT_INTR 10
+#define XPAR_DCR_INTC_0_OPB_TSD_REF_0_INTR_INTR 11
+#define XPAR_DCR_INTC_0_OPB_AC97_CONTROLLER_REF_0_PLAYBACK_INTERRUPT_INTR 12
+#define XPAR_DCR_INTC_0_OPB_AC97_CONTROLLER_REF_0_RECORD_INTERRUPT_INTR 13
+#define XPAR_DCR_INTC_0_OPB_PCI_REF_0_INTR_OUT_INTR 14
+#define XPAR_DCR_INTC_0_PLB2OPB_BRIDGE_0_BUS_ERROR_DET_INTR 15
+#define XPAR_DCR_INTC_0_PLB_V34_0_BUS_ERROR_DET_INTR 16
+#define XPAR_DCR_INTC_0_OPB2PLB_BRIDGE_0_BUS_ERROR_DET_INTR 17
+
+/******************************************************************/
+
+#define XPAR_XTFT_NUM_INSTANCES 1
+#define XPAR_PLB_TFT_CNTLR_REF_0_DCR_BASEADDR 0xD0000200
+#define XPAR_PLB_TFT_CNTLR_REF_0_DCR_HIGHADDR 0xD0000207
+#define XPAR_PLB_TFT_CNTLR_REF_0_DEVICE_ID 0
+
+/******************************************************************/
+
+#define XPAR_XSYSACE_MEM_WIDTH 8
+#define XPAR_XSYSACE_NUM_INSTANCES 1
+#define XPAR_OPB_SYSACE_0_BASEADDR 0xCF000000
+#define XPAR_OPB_SYSACE_0_HIGHADDR 0xCF0001FF
+#define XPAR_OPB_SYSACE_0_DEVICE_ID 0
+#define XPAR_OPB_SYSACE_0_MEM_WIDTH 8
+
+/******************************************************************/
+
+#define XPAR_CPU_PPC405_CORE_CLOCK_FREQ_HZ 300000000
+
+/******************************************************************/
+
+/******************************************************************/
+
+/* Linux Redefines */
+
+/******************************************************************/
+
+#define XPAR_UARTNS550_0_BASEADDR (XPAR_OPB_UART16550_0_BASEADDR+0x1000)
+#define XPAR_UARTNS550_0_HIGHADDR XPAR_OPB_UART16550_0_HIGHADDR
+#define XPAR_UARTNS550_0_CLOCK_FREQ_HZ XPAR_XUARTNS550_CLOCK_HZ
+#define XPAR_UARTNS550_0_DEVICE_ID XPAR_OPB_UART16550_0_DEVICE_ID
+#define XPAR_UARTNS550_1_BASEADDR (XPAR_OPB_UART16550_1_BASEADDR+0x1000)
+#define XPAR_UARTNS550_1_HIGHADDR XPAR_OPB_UART16550_1_HIGHADDR
+#define XPAR_UARTNS550_1_CLOCK_FREQ_HZ XPAR_XUARTNS550_CLOCK_HZ
+#define XPAR_UARTNS550_1_DEVICE_ID XPAR_OPB_UART16550_1_DEVICE_ID
+
+/******************************************************************/
+
+#define XPAR_GPIO_0_BASEADDR XPAR_MY_OPB_GPIO_0_BASEADDR_0
+#define XPAR_GPIO_0_HIGHADDR XPAR_MY_OPB_GPIO_0_HIGHADDR_0
+#define XPAR_GPIO_0_DEVICE_ID XPAR_MY_OPB_GPIO_0_DEVICE_ID_0
+#define XPAR_GPIO_1_BASEADDR XPAR_MY_OPB_GPIO_0_BASEADDR_1
+#define XPAR_GPIO_1_HIGHADDR XPAR_MY_OPB_GPIO_0_HIGHADDR_1
+#define XPAR_GPIO_1_DEVICE_ID XPAR_MY_OPB_GPIO_0_DEVICE_ID_1
+
+/******************************************************************/
+
+#define XPAR_IIC_0_BASEADDR XPAR_OPB_IIC_0_BASEADDR
+#define XPAR_IIC_0_HIGHADDR XPAR_OPB_IIC_0_HIGHADDR
+#define XPAR_IIC_0_TEN_BIT_ADR XPAR_OPB_IIC_0_TEN_BIT_ADR
+#define XPAR_IIC_0_DEVICE_ID XPAR_OPB_IIC_0_DEVICE_ID
+
+/******************************************************************/
+
+#define XPAR_SYSACE_0_BASEADDR XPAR_OPB_SYSACE_0_BASEADDR
+#define XPAR_SYSACE_0_HIGHADDR XPAR_OPB_SYSACE_0_HIGHADDR
+#define XPAR_SYSACE_0_DEVICE_ID XPAR_OPB_SYSACE_0_DEVICE_ID
+
+/******************************************************************/
+
+#define XPAR_INTC_0_BASEADDR XPAR_DCR_INTC_0_BASEADDR
+#define XPAR_INTC_0_HIGHADDR XPAR_DCR_INTC_0_HIGHADDR
+#define XPAR_INTC_0_KIND_OF_INTR XPAR_DCR_INTC_0_KIND_OF_INTR
+#define XPAR_INTC_0_DEVICE_ID XPAR_DCR_INTC_0_DEVICE_ID
+
+/******************************************************************/
+
+#define XPAR_INTC_0_EMAC_0_VEC_ID XPAR_DCR_INTC_0_OPB_ETHERNET_0_IP2INTC_IRPT_INTR
+#define XPAR_INTC_0_IIC_0_VEC_ID XPAR_DCR_INTC_0_OPB_IIC_0_IP2INTC_IRPT_INTR
+#define XPAR_INTC_0_SYSACE_0_VEC_ID XPAR_DCR_INTC_0_OPB_SYSACE_0_SYSACE_IRQ_INTR
+#define XPAR_INTC_0_UARTNS550_0_VEC_ID XPAR_DCR_INTC_0_OPB_UART16550_0_IP2INTC_IRPT_INTR
+#define XPAR_INTC_0_UARTNS550_1_VEC_ID XPAR_DCR_INTC_0_OPB_UART16550_1_IP2INTC_IRPT_INTR
+#define XPAR_INTC_0_PS2_0_VEC_ID XPAR_DCR_INTC_0_OPB_PS2_DUAL_REF_0_SYS_INTR1_INTR
+#define XPAR_INTC_0_PS2_1_VEC_ID XPAR_DCR_INTC_0_OPB_PS2_DUAL_REF_0_SYS_INTR2_INTR
+#define XPAR_INTC_0_SPI_0_VEC_ID XPAR_DCR_INTC_0_OPB_SPI_0_IP2INTC_IRPT_INTR
+#define XPAR_INTC_0_TOUCHSCREEN_0_VEC_ID XPAR_DCR_INTC_0_OPB_TSD_REF_0_INTR_INTR
+#define XPAR_INTC_0_PCI_0_VEC_ID_A XPAR_DCR_INTC_0_OPB_PCI_REF_0_INTR_OUT_INTR
+#define XPAR_INTC_0_PCI_0_VEC_ID_B XPAR_DCR_INTC_0_OPB_PCI_REF_0_INTR_OUT_INTR
+#define XPAR_INTC_0_PCI_0_VEC_ID_C XPAR_DCR_INTC_0_OPB_PCI_REF_0_INTR_OUT_INTR
+#define XPAR_INTC_0_PCI_0_VEC_ID_D XPAR_DCR_INTC_0_OPB_PCI_REF_0_INTR_OUT_INTR
+
+/******************************************************************/
+
+#define XPAR_EMAC_0_BASEADDR XPAR_OPB_ETHERNET_0_BASEADDR
+#define XPAR_EMAC_0_HIGHADDR XPAR_OPB_ETHERNET_0_HIGHADDR
+#define XPAR_EMAC_0_DMA_PRESENT XPAR_OPB_ETHERNET_0_DMA_PRESENT
+#define XPAR_EMAC_0_MII_EXIST XPAR_OPB_ETHERNET_0_MII_EXIST
+#define XPAR_EMAC_0_ERR_COUNT_EXIST XPAR_OPB_ETHERNET_0_ERR_COUNT_EXIST
+#define XPAR_EMAC_0_DEVICE_ID XPAR_OPB_ETHERNET_0_DEVICE_ID
+
+/******************************************************************/
+
+#define XPAR_SPI_0_BASEADDR XPAR_OPB_SPI_0_BASEADDR
+#define XPAR_SPI_0_HIGHADDR XPAR_OPB_SPI_0_HIGHADDR
+#define XPAR_SPI_0_DEVICE_ID XPAR_OPB_SPI_0_DEVICE_ID
+
+/******************************************************************/
+
+#define XPAR_TOUCHSCREEN_0_BASEADDR XPAR_OPB_TSD_REF_0_BASEADDR
+#define XPAR_TOUCHSCREEN_0_HIGHADDR XPAR_OPB_TSD_REF_0_HIGHADDR
+#define XPAR_TOUCHSCREEN_0_DEVICE_ID XPAR_OPB_TSD_REF_0_DEVICE_ID
+
+/******************************************************************/
+
+#define XPAR_TFT_0_BASEADDR XPAR_PLB_TFT_CNTLR_REF_0_DCR_BASEADDR
+
+/******************************************************************/
+
+#define XPAR_PCI_0_BASEADDR XPAR_OPB_PCI_REF_0_BASEADDR
+#define XPAR_PCI_0_HIGHADDR XPAR_OPB_PCI_REF_0_HIGHADDR
+#define XPAR_PCI_0_CONFIG_ADDR XPAR_OPB_PCI_REF_0_CONFIG_ADDR
+#define XPAR_PCI_0_CONFIG_DATA XPAR_OPB_PCI_REF_0_CONFIG_DATA
+#define XPAR_PCI_0_LCONFIG_ADDR XPAR_OPB_PCI_REF_0_LCONFIG_ADDR
+#define XPAR_PCI_0_MEM_BASEADDR XPAR_OPB_PCI_REF_0_MEM_BASEADDR
+#define XPAR_PCI_0_MEM_HIGHADDR XPAR_OPB_PCI_REF_0_MEM_HIGHADDR
+#define XPAR_PCI_0_IO_BASEADDR XPAR_OPB_PCI_REF_0_IO_BASEADDR
+#define XPAR_PCI_0_IO_HIGHADDR XPAR_OPB_PCI_REF_0_IO_HIGHADDR
+#define XPAR_PCI_0_CLOCK_FREQ_HZ XPAR_XPCI_CLOCK_HZ
+#define XPAR_PCI_0_DEVICE_ID XPAR_OPB_PCI_REF_0_DEVICE_ID
+
+/******************************************************************/
+
+#define XPAR_PS2_0_BASEADDR XPAR_OPB_PS2_DUAL_REF_0_BASEADDR_0
+#define XPAR_PS2_0_HIGHADDR XPAR_OPB_PS2_DUAL_REF_0_HIGHADDR_0
+#define XPAR_PS2_0_DEVICE_ID XPAR_OPB_PS2_DUAL_REF_0_DEVICE_ID_0
+#define XPAR_PS2_1_BASEADDR XPAR_OPB_PS2_DUAL_REF_0_BASEADDR_1
+#define XPAR_PS2_1_HIGHADDR XPAR_OPB_PS2_DUAL_REF_0_HIGHADDR_1
+#define XPAR_PS2_1_DEVICE_ID XPAR_OPB_PS2_DUAL_REF_0_DEVICE_ID_1
+
+/******************************************************************/
+
+#define XPAR_PLB_CLOCK_FREQ_HZ 100000000
+#define XPAR_CORE_CLOCK_FREQ_HZ XPAR_CPU_PPC405_CORE_CLOCK_FREQ_HZ
+#define XPAR_DDR_0_SIZE 0x08000000
+
+/******************************************************************/
+
+#define XPAR_PERSISTENT_0_IIC_0_BASEADDR 0x00000400
+#define XPAR_PERSISTENT_0_IIC_0_HIGHADDR 0x000007FF
+#define XPAR_PERSISTENT_0_IIC_0_EEPROMADDR 0xA0
+
+/******************************************************************/
+
+#define XPAR_POWER_0_POWERDOWN_BASEADDR 0x90000004
+#define XPAR_POWER_0_POWERDOWN_HIGHADDR 0x90000007
+#define XPAR_POWER_0_POWERDOWN_VALUE 0xFF
+
+/******************************************************************/
diff --git a/arch/ppc/platforms/85xx/mpc85xx_devices.c b/arch/ppc/platforms/85xx/mpc85xx_devices.c
new file mode 100644 (file)
index 0000000..a231795
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ * arch/ppc/platforms/85xx/mpc85xx_devices.c
+ *
+ * MPC85xx Device descriptions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/serial_8250.h>
+#include <linux/fsl_devices.h>
+#include <asm/mpc85xx.h>
+#include <asm/irq.h>
+#include <asm/ppc_sys.h>
+
+/* We use offsets for IORESOURCE_MEM since we do not know at compile time
+ * what CCSRBAR is, will get fixed up by mach_mpc85xx_fixup
+ */
+
+static struct gianfar_platform_data mpc85xx_tsec1_pdata = {
+       .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
+           FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON |
+           FSL_GIANFAR_DEV_HAS_MULTI_INTR,
+       .phy_reg_addr = MPC85xx_ENET1_OFFSET,
+};
+
+static struct gianfar_platform_data mpc85xx_tsec2_pdata = {
+       .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
+           FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON |
+           FSL_GIANFAR_DEV_HAS_MULTI_INTR,
+       .phy_reg_addr = MPC85xx_ENET1_OFFSET,
+};
+
+static struct gianfar_platform_data mpc85xx_fec_pdata = {
+       .phy_reg_addr = MPC85xx_ENET1_OFFSET,
+};
+
+static struct fsl_i2c_platform_data mpc85xx_fsl_i2c_pdata = {
+       .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR,
+};
+
+static struct plat_serial8250_port serial_platform_data[] = {
+       [0] = {
+               .mapbase        = 0x4500,
+               .irq            = MPC85xx_IRQ_DUART,
+               .iotype         = UPIO_MEM,
+               .flags          = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ,
+       },
+       [1] = {
+               .mapbase        = 0x4600,
+               .irq            = MPC85xx_IRQ_DUART,
+               .iotype         = UPIO_MEM,
+               .flags          = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ,
+       },
+};
+
+struct platform_device ppc_sys_platform_devices[] = {
+       [MPC85xx_TSEC1] = {
+               .name = "fsl-gianfar",
+               .id     = 1,
+               .dev.platform_data = &mpc85xx_tsec1_pdata,
+               .num_resources   = 4,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = MPC85xx_ENET1_OFFSET,
+                               .end    = MPC85xx_ENET1_OFFSET +
+                                               MPC85xx_ENET1_SIZE - 1,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .name   = "tx",
+                               .start  = MPC85xx_IRQ_TSEC1_TX,
+                               .end    = MPC85xx_IRQ_TSEC1_TX,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+                       {
+                               .name   = "rx",
+                               .start  = MPC85xx_IRQ_TSEC1_RX,
+                               .end    = MPC85xx_IRQ_TSEC1_RX,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+                       {
+                               .name   = "error",
+                               .start  = MPC85xx_IRQ_TSEC1_ERROR,
+                               .end    = MPC85xx_IRQ_TSEC1_ERROR,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_TSEC2] = {
+               .name = "fsl-gianfar",
+               .id     = 2,
+               .dev.platform_data = &mpc85xx_tsec2_pdata,
+               .num_resources   = 4,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = MPC85xx_ENET2_OFFSET,
+                               .end    = MPC85xx_ENET2_OFFSET +
+                                               MPC85xx_ENET2_SIZE - 1,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .name   = "tx",
+                               .start  = MPC85xx_IRQ_TSEC2_TX,
+                               .end    = MPC85xx_IRQ_TSEC2_TX,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+                       {
+                               .name   = "rx",
+                               .start  = MPC85xx_IRQ_TSEC2_RX,
+                               .end    = MPC85xx_IRQ_TSEC2_RX,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+                       {
+                               .name   = "error",
+                               .start  = MPC85xx_IRQ_TSEC2_ERROR,
+                               .end    = MPC85xx_IRQ_TSEC2_ERROR,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_FEC] = {
+               .name = "fsl-gianfar",
+               .id     = 3,
+               .dev.platform_data = &mpc85xx_fec_pdata,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = MPC85xx_ENET3_OFFSET,
+                               .end    = MPC85xx_ENET3_OFFSET +
+                                               MPC85xx_ENET3_SIZE - 1,
+                               .flags  = IORESOURCE_MEM,
+
+                       },
+                       {
+                               .start  = MPC85xx_IRQ_FEC,
+                               .end    = MPC85xx_IRQ_FEC,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_IIC1] = {
+               .name = "fsl-i2c",
+               .id     = 1,
+               .dev.platform_data = &mpc85xx_fsl_i2c_pdata,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = MPC85xx_IIC1_OFFSET,
+                               .end    = MPC85xx_IIC1_OFFSET +
+                                               MPC85xx_IIC1_SIZE - 1,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC85xx_IRQ_IIC1,
+                               .end    = MPC85xx_IRQ_IIC1,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_DMA0] = {
+               .name = "fsl-dma",
+               .id     = 0,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = MPC85xx_DMA0_OFFSET,
+                               .end    = MPC85xx_DMA0_OFFSET +
+                                               MPC85xx_DMA0_SIZE - 1,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC85xx_IRQ_DMA0,
+                               .end    = MPC85xx_IRQ_DMA0,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_DMA1] = {
+               .name = "fsl-dma",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = MPC85xx_DMA1_OFFSET,
+                               .end    = MPC85xx_DMA1_OFFSET +
+                                               MPC85xx_DMA1_SIZE - 1,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC85xx_IRQ_DMA1,
+                               .end    = MPC85xx_IRQ_DMA1,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_DMA2] = {
+               .name = "fsl-dma",
+               .id     = 2,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = MPC85xx_DMA2_OFFSET,
+                               .end    = MPC85xx_DMA2_OFFSET +
+                                               MPC85xx_DMA2_SIZE - 1,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC85xx_IRQ_DMA2,
+                               .end    = MPC85xx_IRQ_DMA2,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_DMA3] = {
+               .name = "fsl-dma",
+               .id     = 3,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = MPC85xx_DMA3_OFFSET,
+                               .end    = MPC85xx_DMA3_OFFSET +
+                                               MPC85xx_DMA3_SIZE - 1,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC85xx_IRQ_DMA3,
+                               .end    = MPC85xx_IRQ_DMA3,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_DUART] = {
+               .name = "serial8250",
+               .id     = 0,
+               .dev.platform_data = serial_platform_data,
+       },
+       [MPC85xx_PERFMON] = {
+               .name = "fsl-perfmon",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = MPC85xx_PERFMON_OFFSET,
+                               .end    = MPC85xx_PERFMON_OFFSET +
+                                               MPC85xx_PERFMON_SIZE - 1,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC85xx_IRQ_PERFMON,
+                               .end    = MPC85xx_IRQ_PERFMON,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_SEC2] = {
+               .name = "fsl-sec2",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = MPC85xx_SEC2_OFFSET,
+                               .end    = MPC85xx_SEC2_OFFSET +
+                                               MPC85xx_SEC2_SIZE - 1,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC85xx_IRQ_SEC2,
+                               .end    = MPC85xx_IRQ_SEC2,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+#ifdef CONFIG_CPM2
+       [MPC85xx_CPM_FCC1] = {
+               .name = "fsl-cpm-fcc",
+               .id     = 1,
+               .num_resources   = 3,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91300,
+                               .end    = 0x9131F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = 0x91380,
+                               .end    = 0x9139F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_FCC1,
+                               .end    = SIU_INT_FCC1,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_FCC2] = {
+               .name = "fsl-cpm-fcc",
+               .id     = 2,
+               .num_resources   = 3,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91320,
+                               .end    = 0x9133F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = 0x913A0,
+                               .end    = 0x913CF,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_FCC2,
+                               .end    = SIU_INT_FCC2,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_FCC3] = {
+               .name = "fsl-cpm-fcc",
+               .id     = 3,
+               .num_resources   = 3,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91340,
+                               .end    = 0x9135F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = 0x913D0,
+                               .end    = 0x913FF,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_FCC3,
+                               .end    = SIU_INT_FCC3,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_I2C] = {
+               .name = "fsl-cpm-i2c",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91860,
+                               .end    = 0x918BF,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_I2C,
+                               .end    = SIU_INT_I2C,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_SCC1] = {
+               .name = "fsl-cpm-scc",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91A00,
+                               .end    = 0x91A1F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_SCC1,
+                               .end    = SIU_INT_SCC1,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_SCC2] = {
+               .name = "fsl-cpm-scc",
+               .id     = 2,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91A20,
+                               .end    = 0x91A3F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_SCC2,
+                               .end    = SIU_INT_SCC2,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_SCC3] = {
+               .name = "fsl-cpm-scc",
+               .id     = 3,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91A40,
+                               .end    = 0x91A5F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_SCC3,
+                               .end    = SIU_INT_SCC3,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_SCC4] = {
+               .name = "fsl-cpm-scc",
+               .id     = 4,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91A60,
+                               .end    = 0x91A7F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_SCC4,
+                               .end    = SIU_INT_SCC4,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_SPI] = {
+               .name = "fsl-cpm-spi",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91AA0,
+                               .end    = 0x91AFF,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_SPI,
+                               .end    = SIU_INT_SPI,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_MCC1] = {
+               .name = "fsl-cpm-mcc",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91B30,
+                               .end    = 0x91B3F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_MCC1,
+                               .end    = SIU_INT_MCC1,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_MCC2] = {
+               .name = "fsl-cpm-mcc",
+               .id     = 2,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91B50,
+                               .end    = 0x91B5F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_MCC2,
+                               .end    = SIU_INT_MCC2,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_SMC1] = {
+               .name = "fsl-cpm-smc",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91A80,
+                               .end    = 0x91A8F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_SMC1,
+                               .end    = SIU_INT_SMC1,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_SMC2] = {
+               .name = "fsl-cpm-smc",
+               .id     = 2,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91A90,
+                               .end    = 0x91A9F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_SMC2,
+                               .end    = SIU_INT_SMC2,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC85xx_CPM_USB] = {
+               .name = "fsl-cpm-usb",
+               .id     = 2,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x91B60,
+                               .end    = 0x91B7F,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = SIU_INT_USB,
+                               .end    = SIU_INT_USB,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+#endif /* CONFIG_CPM2 */
+};
+
+static int __init mach_mpc85xx_fixup(struct platform_device *pdev)
+{
+       ppc_sys_fixup_mem_resource(pdev, CCSRBAR);
+       return 0;
+}
+
+static int __init mach_mpc85xx_init(void)
+{
+       ppc_sys_device_fixup = mach_mpc85xx_fixup;
+       return 0;
+}
+
+postcore_initcall(mach_mpc85xx_init);
diff --git a/arch/ppc/platforms/85xx/mpc85xx_sys.c b/arch/ppc/platforms/85xx/mpc85xx_sys.c
new file mode 100644 (file)
index 0000000..9ba0255
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * arch/ppc/platforms/85xx/mpc85xx_sys.c
+ *
+ * MPC85xx System descriptions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <asm/ppc_sys.h>
+
+struct ppc_sys_spec *cur_ppc_sys_spec;
+struct ppc_sys_spec ppc_sys_specs[] = {
+       {
+               .ppc_sys_name   = "MPC8540",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80300000,
+               .num_devices    = 10,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_FEC, MPC85xx_IIC1,
+                       MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+                       MPC85xx_PERFMON, MPC85xx_DUART,
+               },
+       },
+       {
+               .ppc_sys_name   = "MPC8560",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80700000,
+               .num_devices    = 19,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
+                       MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+                       MPC85xx_PERFMON,
+                       MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
+                       MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3, MPC85xx_CPM_SCC4,
+                       MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, MPC85xx_CPM_FCC3,
+                       MPC85xx_CPM_MCC1, MPC85xx_CPM_MCC2,
+               },
+       },
+       {
+               .ppc_sys_name   = "MPC8541",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80720000,
+               .num_devices    = 13,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
+                       MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+                       MPC85xx_PERFMON, MPC85xx_DUART,
+                       MPC85xx_CPM_SPI, MPC85xx_CPM_I2C,
+                       MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
+               },
+       },
+       {
+               .ppc_sys_name   = "MPC8541E",
+               .mask           = 0xFFFF0000,
+               .value          = 0x807A0000,
+               .num_devices    = 14,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
+                       MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+                       MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2,
+                       MPC85xx_CPM_SPI, MPC85xx_CPM_I2C,
+                       MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
+               },
+       },
+       {
+               .ppc_sys_name   = "MPC8555",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80710000,
+               .num_devices    = 20,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
+                       MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+                       MPC85xx_PERFMON, MPC85xx_DUART,
+                       MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
+                       MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3,
+                       MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, MPC85xx_CPM_FCC3,
+                       MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2,
+                       MPC85xx_CPM_USB,
+               },
+       },
+       {
+               .ppc_sys_name   = "MPC8555E",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80790000,
+               .num_devices    = 21,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
+                       MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+                       MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2,
+                       MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
+                       MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3,
+                       MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, MPC85xx_CPM_FCC3,
+                       MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2,
+                       MPC85xx_CPM_USB,
+               },
+       },
+       {       /* default match */
+               .ppc_sys_name   = "",
+               .mask           = 0x00000000,
+               .value          = 0x00000000,
+       },
+};
diff --git a/arch/ppc/platforms/85xx/stx_gp3.c b/arch/ppc/platforms/85xx/stx_gp3.c
new file mode 100644 (file)
index 0000000..8b637e6
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * arch/ppc/platforms/85xx/stx_gp3.c
+ *
+ * STx GP3 board specific routines
+ *
+ * Dan Malek <dan@embeddededge.com>
+ * Copyright 2004 Embedded Edge, LLC
+ *
+ * Copied from mpc8560_ads.c
+ * Copyright 2002, 2003 Motorola Inc.
+ *
+ * Ported to 2.6, Matt Porter <mporter@kernel.crashing.org>
+ * Copyright 2004-2005 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.
+ */
+
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/root_dev.h>
+#include <linux/seq_file.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+#include <linux/fsl_devices.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/atomic.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/prom.h>
+#include <asm/open_pic.h>
+#include <asm/bootinfo.h>
+#include <asm/pci-bridge.h>
+#include <asm/mpc85xx.h>
+#include <asm/irq.h>
+#include <asm/immap_85xx.h>
+#include <asm/immap_cpm2.h>
+#include <asm/mpc85xx.h>
+#include <asm/ppc_sys.h>
+
+#include <syslib/cpm2_pic.h>
+#include <syslib/ppc85xx_common.h>
+
+extern void cpm2_reset(void);
+
+unsigned char __res[sizeof(bd_t)];
+
+#ifndef CONFIG_PCI
+unsigned long isa_io_base = 0;
+unsigned long isa_mem_base = 0;
+unsigned long pci_dram_offset = 0;
+#endif
+
+/* Internal interrupts are all Level Sensitive, and Positive Polarity */
+static u8 gp3_openpic_initsenses[] __initdata = {
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal  0: L2 Cache */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal  1: ECM */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal  2: DDR DRAM */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal  3: LBIU */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal  4: DMA 0 */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal  5: DMA 1 */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal  6: DMA 2 */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal  7: DMA 3 */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal  8: PCI/PCI-X */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal  9: RIO Inbound Port Write Error */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 10: RIO Doorbell Inbound */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 11: RIO Outbound Message */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 12: RIO Inbound Message */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 13: TSEC 0 Transmit */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 14: TSEC 0 Receive */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 15: Unused */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 16: Unused */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 17: Unused */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 18: TSEC 0 Receive/Transmit Error */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 19: TSEC 1 Transmit */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 20: TSEC 1 Receive */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 21: Unused */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 22: Unused */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 23: Unused */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 24: TSEC 1 Receive/Transmit Error */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 25: Fast Ethernet */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 26: DUART */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 27: I2C */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 28: Performance Monitor */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 29: Unused */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 30: CPM */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),      /* Internal 31: Unused */
+       0x0,                                            /* External  0: */
+#if defined(CONFIG_PCI)
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),      /* External 1: PCI slot 0 */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),      /* External 2: PCI slot 1 */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),      /* External 3: PCI slot 2 */
+       (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),      /* External 4: PCI slot 3 */
+#else
+       0x0,                            /* External  1: */
+       0x0,                            /* External  2: */
+       0x0,                            /* External  3: */
+       0x0,                            /* External  4: */
+#endif
+       0x0,                            /* External  5: */
+       0x0,                            /* External  6: */
+       0x0,                            /* External  7: */
+       0x0,                            /* External  8: */
+       0x0,                            /* External  9: */
+       0x0,                            /* External 10: */
+       0x0,                            /* External 11: */
+};
+
+/*
+ * Setup the architecture
+ */
+static void __init
+gp3_setup_arch(void)
+{
+       bd_t *binfo = (bd_t *) __res;
+       unsigned int freq;
+       struct gianfar_platform_data *pdata;
+
+       cpm2_reset();
+
+       /* get the core frequency */
+       freq = binfo->bi_intfreq;
+
+       if (ppc_md.progress)
+               ppc_md.progress("gp3_setup_arch()", 0);
+
+       /* Set loops_per_jiffy to a half-way reasonable value,
+          for use until calibrate_delay gets called. */
+       loops_per_jiffy = freq / HZ;
+
+#ifdef CONFIG_PCI
+       /* setup PCI host bridges */
+       mpc85xx_setup_hose();
+#endif
+
+       /* setup the board related information for the enet controllers */
+       pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC1);
+/*     pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; */
+       pdata->interruptPHY = MPC85xx_IRQ_EXT5;
+       pdata->phyid = 2;
+       pdata->phy_reg_addr += binfo->bi_immr_base;
+       memcpy(pdata->mac_addr, binfo->bi_enetaddr, 6);
+
+       pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC85xx_TSEC2);
+/*     pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR; */
+       pdata->interruptPHY = MPC85xx_IRQ_EXT5;
+       pdata->phyid = 4;
+       /* fixup phy address */
+       pdata->phy_reg_addr += binfo->bi_immr_base;
+       memcpy(pdata->mac_addr, binfo->bi_enet1addr, 6);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (initrd_start)
+               ROOT_DEV = Root_RAM0;
+       else
+#endif
+#ifdef CONFIG_ROOT_NFS
+               ROOT_DEV = Root_NFS;
+#else
+               ROOT_DEV = Root_HDA1;
+#endif
+
+       printk ("bi_immr_base = %8.8lx\n", binfo->bi_immr_base);
+}
+
+static irqreturn_t cpm2_cascade(int irq, void *dev_id, struct pt_regs *regs)
+{
+       while ((irq = cpm2_get_irq(regs)) >= 0)
+               __do_IRQ(irq, regs);
+
+       return IRQ_HANDLED;
+}
+
+static struct irqaction cpm2_irqaction = {
+       .handler        = cpm2_cascade,
+       .flags          = SA_INTERRUPT,
+       .mask           = CPU_MASK_NONE,
+       .name           = "cpm2_cascade",
+};
+
+static void __init
+gp3_init_IRQ(void)
+{
+       int i;
+       volatile cpm2_map_t *immap = cpm2_immr;
+       bd_t *binfo = (bd_t *) __res;
+
+       /*
+        * Setup OpenPIC
+        */
+
+       /* Determine the Physical Address of the OpenPIC regs */
+       phys_addr_t OpenPIC_PAddr =
+           binfo->bi_immr_base + MPC85xx_OPENPIC_OFFSET;
+       OpenPIC_Addr = ioremap(OpenPIC_PAddr, MPC85xx_OPENPIC_SIZE);
+       OpenPIC_InitSenses = gp3_openpic_initsenses;
+       OpenPIC_NumInitSenses = sizeof (gp3_openpic_initsenses);
+
+       /* Skip reserved space and internal sources */
+       openpic_set_sources(0, 32, OpenPIC_Addr + 0x10200);
+
+       /* Map PIC IRQs 0-11 */
+       openpic_set_sources(32, 12, OpenPIC_Addr + 0x10000);
+
+       /*
+        * Let openpic interrupts starting from an offset, to
+        * leave space for cascading interrupts underneath.
+        */
+       openpic_init(MPC85xx_OPENPIC_IRQ_OFFSET);
+
+       /*
+        * Setup CPM2 PIC
+        */
+
+       /* disable all CPM interupts */
+       immap->im_intctl.ic_simrh = 0x0;
+       immap->im_intctl.ic_simrl = 0x0;
+
+       for (i = CPM_IRQ_OFFSET; i < (NR_CPM_INTS + CPM_IRQ_OFFSET); i++)
+               irq_desc[i].handler = &cpm2_pic;
+
+       /*
+        * Initialize the default interrupt mapping priorities,
+        * in case the boot rom changed something on us.
+        */
+       immap->im_intctl.ic_sicr = 0;
+       immap->im_intctl.ic_scprrh = 0x05309770;
+       immap->im_intctl.ic_scprrl = 0x05309770;
+
+       setup_irq(MPC85xx_IRQ_CPM, &cpm2_irqaction);
+
+       return;
+}
+
+static int
+gp3_show_cpuinfo(struct seq_file *m)
+{
+       uint pvid, svid, phid1;
+       bd_t *binfo = (bd_t *) __res;
+       uint    memsize;
+       unsigned int freq;
+       extern unsigned long total_memory;      /* in mm/init */
+
+       /* get the core frequency */
+       freq = binfo->bi_intfreq;
+
+       pvid = mfspr(PVR);
+       svid = mfspr(SVR);
+
+       memsize = total_memory;
+
+       seq_printf(m, "Vendor\t\t: RPC Electronics STx \n");
+
+       switch (svid & 0xffff0000) {
+       case SVR_8540:
+               seq_printf(m, "Machine\t\t: GP3 - MPC8540\n");
+               break;
+       case SVR_8560:
+               seq_printf(m, "Machine\t\t: GP3 - MPC8560\n");
+               break;
+       default:
+               seq_printf(m, "Machine\t\t: unknown\n");
+               break;
+       }
+       seq_printf(m, "bus freq\t: %u.%.6u MHz\n", freq / 1000000,
+                  freq % 1000000);
+       seq_printf(m, "PVR\t\t: 0x%x\n", pvid);
+       seq_printf(m, "SVR\t\t: 0x%x\n", svid);
+
+       /* Display cpu Pll setting */
+       phid1 = mfspr(HID1);
+       seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f));
+
+       /* Display the amount of memory */
+       seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024));
+
+       return 0;
+}
+
+#ifdef CONFIG_PCI
+int mpc85xx_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
+            */
+       {
+               {PIRQA, PIRQB, PIRQC, PIRQD},
+               {PIRQD, PIRQA, PIRQB, PIRQC},
+               {PIRQC, PIRQD, PIRQA, PIRQB},
+               {PIRQB, PIRQC, PIRQD, PIRQA},
+       };
+
+       const long min_idsel = 12, max_idsel = 15, irqs_per_slot = 4;
+       return PCI_IRQ_TABLE_LOOKUP;
+}
+
+int mpc85xx_exclude_device(u_char bus, u_char devfn)
+{
+       if (bus == 0 && PCI_SLOT(devfn) == 0)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       else
+               return PCIBIOS_SUCCESSFUL;
+}
+#endif /* CONFIG_PCI */
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+             unsigned long r6, unsigned long r7)
+{
+       /* parse_bootinfo must always be called first */
+       parse_bootinfo(find_bootinfo());
+
+       /*
+        * If we were passed in a board information, copy it into the
+        * residual data area.
+        */
+       if (r3) {
+               memcpy((void *) __res, (void *) (r3 + KERNELBASE),
+                      sizeof (bd_t));
+
+       }
+#if defined(CONFIG_BLK_DEV_INITRD)
+       /*
+        * If the init RAM disk has been configured in, and there's a valid
+        * starting address for it, set it up.
+        */
+       if (r4) {
+               initrd_start = r4 + KERNELBASE;
+               initrd_end = r5 + KERNELBASE;
+       }
+#endif                         /* CONFIG_BLK_DEV_INITRD */
+
+       /* Copy the kernel command line arguments to a safe place. */
+
+       if (r6) {
+               *(char *) (r7 + KERNELBASE) = 0;
+               strcpy(cmd_line, (char *) (r6 + KERNELBASE));
+       }
+
+       identify_ppc_sys_by_id(mfspr(SVR));
+
+       /* setup the PowerPC module struct */
+       ppc_md.setup_arch = gp3_setup_arch;
+       ppc_md.show_cpuinfo = gp3_show_cpuinfo;
+
+       ppc_md.init_IRQ = gp3_init_IRQ;
+       ppc_md.get_irq = openpic_get_irq;
+
+       ppc_md.restart = mpc85xx_restart;
+       ppc_md.power_off = mpc85xx_power_off;
+       ppc_md.halt = mpc85xx_halt;
+
+       ppc_md.find_end_of_memory = mpc85xx_find_end_of_memory;
+
+       ppc_md.calibrate_decr = mpc85xx_calibrate_decr;
+
+       if (ppc_md.progress)
+               ppc_md.progress("platform_init(): exit", 0);
+
+       return;
+}
diff --git a/arch/ppc/platforms/85xx/stx_gp3.h b/arch/ppc/platforms/85xx/stx_gp3.h
new file mode 100644 (file)
index 0000000..7bcc6c3
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * arch/ppc/platforms/stx8560_gp3.h
+ *
+ * STx GP3 board definitions
+ *
+ * Dan Malek (dan@embeddededge.com)
+ * Copyright 2004 Embedded Edge, LLC
+ *
+ * Ported to 2.6, Matt Porter <mporter@kernel.crashing.org>
+ * Copyright 2004-2005 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.
+ *
+ */
+
+#ifndef __MACH_STX_GP3_H
+#define __MACH_STX_GP3_H
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <asm/ppcboot.h>
+
+#define BOARD_CCSRBAR          ((uint)0xe0000000)
+#define CCSRBAR_SIZE           ((uint)1024*1024)
+
+#define CPM_MAP_ADDR           (CCSRBAR + MPC85xx_CPM_OFFSET)
+
+#define BCSR_ADDR              ((uint)0xfc000000)
+#define BCSR_SIZE              ((uint)(16 * 1024))
+
+#define BCSR_TSEC1_RESET       0x00000080
+#define BCSR_TSEC2_RESET       0x00000040
+#define BCSR_LED1              0x00000008
+#define BCSR_LED2              0x00000004
+#define BCSR_LED3              0x00000002
+#define BCSR_LED4              0x00000001
+
+extern void mpc85xx_setup_hose(void) __init;
+extern void mpc85xx_restart(char *cmd);
+extern void mpc85xx_power_off(void);
+extern void mpc85xx_halt(void);
+extern int mpc85xx_show_cpuinfo(struct seq_file *m);
+extern void mpc85xx_init_IRQ(void) __init;
+extern unsigned long mpc85xx_find_end_of_memory(void) __init;
+extern void mpc85xx_calibrate_decr(void) __init;
+
+#define PCI_CFG_ADDR_OFFSET    (0x8000)
+#define PCI_CFG_DATA_OFFSET    (0x8004)
+
+/* PCI interrupt controller */
+#define PIRQA          MPC85xx_IRQ_EXT1
+#define PIRQB          MPC85xx_IRQ_EXT2
+#define PIRQC          MPC85xx_IRQ_EXT3
+#define PIRQD          MPC85xx_IRQ_EXT4
+#define PCI_MIN_IDSEL  16
+#define PCI_MAX_IDSEL  19
+#define PCI_IRQ_SLOT   4
+
+#define MPC85XX_PCI1_LOWER_IO  0x00000000
+#define MPC85XX_PCI1_UPPER_IO  0x00ffffff
+
+#define MPC85XX_PCI1_LOWER_MEM 0x80000000
+#define MPC85XX_PCI1_UPPER_MEM 0x9fffffff
+
+#define MPC85XX_PCI1_IO_BASE   0xe2000000
+#define MPC85XX_PCI1_MEM_OFFSET        0x00000000
+
+#define MPC85XX_PCI1_IO_SIZE   0x01000000
+
+#endif /* __MACH_STX_GP3_H */
diff --git a/arch/ppc/platforms/chestnut.c b/arch/ppc/platforms/chestnut.c
new file mode 100644 (file)
index 0000000..502291e
--- /dev/null
@@ -0,0 +1,623 @@
+/*
+ * arch/ppc/platforms/chestnut.c
+ *
+ * Board setup routines for IBM Chestnut
+ *
+ * Author: <source@mvista.com>
+ *
+ * <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 <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/console.h>
+#include <linux/root_dev.h>
+#include <linux/initrd.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/ide.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/time.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <linux/irq.h>
+#include <asm/hw_irq.h>
+#include <asm/machdep.h>
+#include <asm/kgdb.h>
+#include <asm/bootinfo.h>
+#include <asm/mv64x60.h>
+#include <platforms/chestnut.h>
+
+static u32 boot_base; /* Virtual addr of 8bit boot */
+static u32 cpld_base; /* Virtual addr of CPLD Regs */
+
+static mv64x60_handle_t        bh;
+
+extern void gen550_progress(char *, unsigned short);
+extern void gen550_init(int, struct uart_port *);
+extern void mv64360_pcibios_fixup(mv64x60_handle_t *bh);
+
+#define BIT(x) (1<<x)
+#define CHESTNUT_PRESERVE_MASK (BIT(MV64x60_CPU2DEV_0_WIN) | \
+                               BIT(MV64x60_CPU2DEV_1_WIN) | \
+                               BIT(MV64x60_CPU2DEV_2_WIN) | \
+                               BIT(MV64x60_CPU2DEV_3_WIN) | \
+                               BIT(MV64x60_CPU2BOOT_WIN))
+/**************************************************************************
+ * FUNCTION: chestnut_calibrate_decr
+ *
+ * DESCRIPTION: initialize decrementer interrupt frequency (used as system
+ *              timer)
+ *
+ ****/
+static void __init
+chestnut_calibrate_decr(void){
+       ulong freq;
+
+       freq = CHESTNUT_BUS_SPEED / 4;
+
+       printk("time_init: decrementer frequency = %lu.%.6lu MHz\n",
+               freq/1000000, freq%1000000);
+
+       tb_ticks_per_jiffy = freq / HZ;
+       tb_to_us = mulhwu_scale_factor(freq, 1000000);
+
+       return;
+}
+
+static int
+chestnut_show_cpuinfo(struct seq_file *m)
+{
+       seq_printf(m, "vendor\t\t: IBM\n");
+       seq_printf(m, "machine\t\t: 750FX/GX Eval Board (Chestnut/Buckeye)\n");
+
+       return 0;
+}
+
+/**************************************************************************
+ * FUNCTION: chestnut_find_end_of_memory
+ *
+ * DESCRIPTION: ppc_md memory size callback
+ *
+ ****/
+unsigned long __init
+chestnut_find_end_of_memory(void)
+{
+       static int  mem_size = 0;
+
+       if (mem_size == 0) {
+               mem_size = mv64x60_get_mem_size(CONFIG_MV64X60_NEW_BASE,
+                               MV64x60_TYPE_MV64460);
+       }
+       return(mem_size);
+}
+
+#if defined(CONFIG_SERIAL_8250)
+static void __init
+chestnut_early_serial_map(void)
+{
+       struct uart_port port;
+
+       /* Setup serial port access */
+       memset(&port, 0, sizeof(port));
+       port.uartclk = BASE_BAUD * 16;
+       port.irq = UART0_INT;
+       port.flags = STD_COM_FLAGS | UPF_IOREMAP;
+       port.iotype = SERIAL_IO_MEM;
+       port.mapbase = CHESTNUT_UART0_IO_BASE;
+       port.regshift = 0;
+
+       if (early_serial_setup(&port) != 0)
+               printk("Early serial init of port 0 failed\n");
+
+       /* Assume early_serial_setup() doesn't modify serial_req */
+       port.line = 1;
+       port.irq = UART1_INT;
+       port.mapbase = CHESTNUT_UART1_IO_BASE;
+
+       if (early_serial_setup(&port) != 0)
+               printk("Early serial init of port 1 failed\n");
+}
+#endif
+
+/**************************************************************************
+ * FUNCTION: chestnut_map_irq
+ *
+ * DESCRIPTION: 0 return since PCI IRQs not needed
+ *
+ ****/
+static int __init
+chestnut_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+       static char pci_irq_table[][4] = {
+               {CHESTNUT_PCI_SLOT0_IRQ, CHESTNUT_PCI_SLOT0_IRQ,
+                CHESTNUT_PCI_SLOT0_IRQ, CHESTNUT_PCI_SLOT0_IRQ},
+               {CHESTNUT_PCI_SLOT1_IRQ, CHESTNUT_PCI_SLOT1_IRQ,
+                CHESTNUT_PCI_SLOT1_IRQ, CHESTNUT_PCI_SLOT1_IRQ},
+               {CHESTNUT_PCI_SLOT2_IRQ, CHESTNUT_PCI_SLOT2_IRQ,
+                CHESTNUT_PCI_SLOT2_IRQ, CHESTNUT_PCI_SLOT2_IRQ},
+               {CHESTNUT_PCI_SLOT3_IRQ, CHESTNUT_PCI_SLOT3_IRQ,
+                CHESTNUT_PCI_SLOT3_IRQ, CHESTNUT_PCI_SLOT3_IRQ},
+       };
+       const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4;
+
+       return (PCI_IRQ_TABLE_LOOKUP);
+}
+
+
+/**************************************************************************
+ * FUNCTION: chestnut_setup_bridge
+ *
+ * DESCRIPTION: initalize board-specific settings on the MV64360
+ *
+ ****/
+static void __init
+chestnut_setup_bridge(void)
+{
+       struct mv64x60_setup_info       si;
+       int i;
+
+       if ( ppc_md.progress )
+               ppc_md.progress("chestnut_setup_bridge: enter", 0);
+
+       memset(&si, 0, sizeof(si));
+
+       si.phys_reg_base = CONFIG_MV64X60_NEW_BASE;
+
+       /* setup only PCI bus 0 (bus 1 not used) */
+       si.pci_0.enable_bus = 1;
+       si.pci_0.pci_io.cpu_base = CHESTNUT_PCI0_IO_PROC_ADDR;
+       si.pci_0.pci_io.pci_base_hi = 0;
+       si.pci_0.pci_io.pci_base_lo = CHESTNUT_PCI0_IO_PCI_ADDR;
+       si.pci_0.pci_io.size = CHESTNUT_PCI0_IO_SIZE;
+       si.pci_0.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE; /* no swapping */
+       si.pci_0.pci_mem[0].cpu_base = CHESTNUT_PCI0_MEM_PROC_ADDR;
+       si.pci_0.pci_mem[0].pci_base_hi = CHESTNUT_PCI0_MEM_PCI_HI_ADDR;
+       si.pci_0.pci_mem[0].pci_base_lo = CHESTNUT_PCI0_MEM_PCI_LO_ADDR;
+       si.pci_0.pci_mem[0].size = CHESTNUT_PCI0_MEM_SIZE;
+       si.pci_0.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE; /* no swapping */
+       si.pci_0.pci_cmd_bits = 0;
+       si.pci_0.latency_timer = 0x80;
+
+       si.window_preserve_mask_32_lo = CHESTNUT_PRESERVE_MASK;
+
+       for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) {
+               si.cpu_prot_options[i] = 0;
+#ifdef CONFIG_NOT_CACHE_COHERENT
+               si.cpu_snoop_options[i] = MV64360_CPU_SNOOP_NONE;
+#else
+               si.cpu_snoop_options[i] = MV64360_CPU_SNOOP_WB; /* risky */
+#endif
+               si.pci_0.acc_cntl_options[i] =
+#ifdef CONFIG_NOT_CACHE_COHERENT
+                       MV64360_PCI_ACC_CNTL_SNOOP_NONE |
+#else
+                       MV64360_PCI_ACC_CNTL_SNOOP_WB | /* risky */
+#endif
+                       MV64360_PCI_ACC_CNTL_SWAP_NONE |
+                       MV64360_PCI_ACC_CNTL_MBURST_32_BYTES |
+                       MV64360_PCI_ACC_CNTL_RDSIZE_32_BYTES;
+       }
+
+       /* Lookup host bridge - on CPU 0 - no SMP support */
+       if (mv64x60_init(&bh, &si)) {
+               printk("\n\nPCI Bridge initialization failed!\n");
+       }
+
+       pci_dram_offset = 0;
+       ppc_md.pci_swizzle = common_swizzle;
+       ppc_md.pci_map_irq = chestnut_map_irq;
+       ppc_md.pci_exclude_device = mv64x60_pci_exclude_device;
+
+       mv64x60_set_bus(&bh, 0, 0);
+       bh.hose_a->first_busno = 0;
+       bh.hose_a->last_busno = 0xff;
+       bh.hose_a->last_busno = pciauto_bus_scan(bh.hose_a, 0);
+
+}
+
+void __init
+chestnut_setup_peripherals(void)
+{
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN,
+                       CHESTNUT_BOOT_8BIT_BASE, CHESTNUT_BOOT_8BIT_SIZE, 0);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN,
+                       CHESTNUT_32BIT_BASE, CHESTNUT_32BIT_SIZE, 0);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN,
+                       CHESTNUT_CPLD_BASE, CHESTNUT_CPLD_SIZE, 0);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN,
+                       CHESTNUT_UART_BASE, CHESTNUT_UART_SIZE, 0);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN,
+                       CHESTNUT_FRAM_BASE, CHESTNUT_FRAM_SIZE, 0);
+       /* Set up window for internal sram (256KByte insize) */
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN,
+                       CHESTNUT_INTERNAL_SRAM_BASE,
+                       CHESTNUT_INTERNAL_SRAM_SIZE, 0);
+
+       boot_base = (u32)ioremap(CHESTNUT_BOOT_8BIT_BASE,
+                               CHESTNUT_BOOT_8BIT_SIZE);
+       cpld_base = (u32)ioremap(CHESTNUT_CPLD_BASE, CHESTNUT_CPLD_SIZE);
+
+       /*
+        * Configure internal SRAM -
+        * Cache coherent write back, incase
+        *      CONFIG_MV64360_SRAM_CACHE_COHERENT set
+        * Parity enabled.
+        * Parity error propagation
+        * Arbitration not parked for CPU only
+        * Other bits are reserved.
+        */
+#ifdef CONFIG_MV64360_SRAM_CACHE_COHERENT
+       mv64x60_write(&bh, MV64360_SRAM_CONFIG, 0x001600b2);
+#else
+       mv64x60_write(&bh, MV64360_SRAM_CONFIG, 0x001600b0);
+#endif
+
+       /*
+        * Setting the SRAM to 0. Note that this generates parity errors on
+        * internal data path in SRAM since it's first time accessing it
+        * while after reset it's not configured
+       */
+       memset((void *)CHESTNUT_INTERNAL_SRAM_BASE, 0, CHESTNUT_INTERNAL_SRAM_SIZE);
+       /*
+        * Configure MPP pins for PCI DMA
+        *
+        * PCI Slot     GNT pin         REQ pin
+        *      0       MPP16           MPP17
+        *      1       MPP18           MPP19
+        *      2       MPP20           MPP21
+        *      3       MPP22           MPP23
+        */
+       mv64x60_write(&bh, MV64x60_MPP_CNTL_2,
+                       (0x1 << 0)  |   /* MPPSel16 PCI0_GNT[0] */
+                       (0x1 << 4)  |   /* MPPSel17 PCI0_REQ[0] */
+                       (0x1 << 8)  |   /* MPPSel18 PCI0_GNT[1] */
+                       (0x1 << 12) |   /* MPPSel19 PCI0_REQ[1] */
+                       (0x1 << 16) |   /* MPPSel20 PCI0_GNT[2] */
+                       (0x1 << 20) |   /* MPPSel21 PCI0_REQ[2] */
+                       (0x1 << 24) |   /* MPPSel22 PCI0_GNT[3] */
+                       (0x1 << 28));   /* MPPSel23 PCI0_REQ[3] */
+       /*
+        * Set unused MPP pins for output, as per schematic note
+        *
+        * Unused Pins: MPP01, MPP02, MPP04, MPP05, MPP06
+        *              MPP09, MPP10, MPP13, MPP14, MPP15
+        */
+       mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_0,
+                       (0xf << 4)  |   /* MPPSel01 GPIO[1] */
+                       (0xf << 8)  |   /* MPPSel02 GPIO[2] */
+                       (0xf << 16) |   /* MPPSel04 GPIO[4] */
+                       (0xf << 20) |   /* MPPSel05 GPIO[5] */
+                       (0xf << 24));   /* MPPSel06 GPIO[6] */
+       mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_1,
+                       (0xf << 4)  |   /* MPPSel09 GPIO[9] */
+                       (0xf << 8)  |   /* MPPSel10 GPIO[10] */
+                       (0xf << 20) |   /* MPPSel13 GPIO[13] */
+                       (0xf << 24) |   /* MPPSel14 GPIO[14] */
+                       (0xf << 28));   /* MPPSel15 GPIO[15] */
+       mv64x60_set_bits(&bh, MV64x60_GPP_IO_CNTL,
+                       BIT(1)  | BIT(2)  | BIT(4)  | BIT(5)  | BIT(6)  |
+                       BIT(9)  | BIT(10) | BIT(13) | BIT(14) | BIT(15)); /* Output */
+
+       /*
+        * Configure the following MPP pins to indicate a level
+        * triggered interrupt
+        *
+                * MPP24 - Board Reset (just map the MPP & GPP for chestnut_reset)
+                * MPP25 - UART A  (high)
+                * MPP26 - UART B  (high)
+        * MPP28 - PCI Slot 3 (low)
+        * MPP29 - PCI Slot 2 (low)
+        * MPP30 - PCI Slot 1 (low)
+        * MPP31 - PCI Slot 0 (low)
+        */
+        mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_3,
+                        BIT(3) | BIT(2) | BIT(1) | BIT(0)       | /* MPP 24 */
+                        BIT(7) | BIT(6) | BIT(5) | BIT(4)       | /* MPP 25 */
+                        BIT(11) | BIT(10) | BIT(9) | BIT(8)     | /* MPP 26 */
+                       BIT(19) | BIT(18) | BIT(17) | BIT(16)    | /* MPP 28 */
+                       BIT(23) | BIT(22) | BIT(21) | BIT(20)    | /* MPP 29 */
+                       BIT(27) | BIT(26) | BIT(25) | BIT(24)    | /* MPP 30 */
+                       BIT(31) | BIT(30) | BIT(29) | BIT(28));    /* MPP 31 */
+
+       /*
+        * Define GPP 25 (high), 26 (high), 28 (low), 29 (low), 30 (low),
+        * 31 (low) interrupt polarity input signal and level triggered
+        */
+       mv64x60_clr_bits(&bh, MV64x60_GPP_LEVEL_CNTL, BIT(25) | BIT(26));
+       mv64x60_set_bits(&bh, MV64x60_GPP_LEVEL_CNTL,
+                       BIT(28) | BIT(29) | BIT(30) | BIT(31));
+       mv64x60_clr_bits(&bh, MV64x60_GPP_IO_CNTL,
+                       BIT(25) | BIT(26) | BIT(28) | BIT(29) | BIT(30) |
+                       BIT(31));
+
+       /* Config GPP interrupt controller to respond to level trigger */
+       mv64x60_set_bits(&bh, MV64360_COMM_ARBITER_CNTL, BIT(10));
+
+       /*
+        * Dismiss and then enable interrupt on GPP interrupt cause for CPU #0
+        */
+       mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE,
+                       ~(BIT(25) | BIT(26) | BIT(28) | BIT(29) | BIT(30) |
+                         BIT(31)));
+       mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK,
+                       BIT(25) | BIT(26) | BIT(28) | BIT(29) | BIT(30) |
+                       BIT(31));
+
+       /*
+        * Dismiss and then enable interrupt on CPU #0 high cause register
+        * BIT27 summarizes GPP interrupts 24-31
+       */
+       mv64x60_set_bits(&bh, MV64360_IC_CPU0_INTR_MASK_HI, BIT(27));
+
+       if (ppc_md.progress)
+               ppc_md.progress("chestnut_setup_bridge: exit", 0);
+}
+
+/**************************************************************************
+ * FUNCTION: chestnut_setup_arch
+ *
+ * DESCRIPTION: ppc_md machine configuration callback
+ *
+ ****/
+static void __init
+chestnut_setup_arch(void)
+{
+       if (ppc_md.progress)
+               ppc_md.progress("chestnut_setup_arch: enter", 0);
+
+       /* init to some ~sane value until calibrate_delay() runs */
+       loops_per_jiffy = 50000000 / HZ;
+
+       /* if the time base value is greater than bus freq/4 (the TB and
+       * decrementer tick rate) + signed integer rollover value, we
+       * can spend a fair amount of time waiting for the rollover to
+       * happen.  To get around this, initialize the time base register
+       * to a "safe" value.
+       */
+       set_tb(0, 0);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (initrd_start)
+               ROOT_DEV = Root_RAM0;
+       else
+#endif
+#ifdef CONFIG_ROOT_NFS
+               ROOT_DEV = Root_NFS;
+#else
+               ROOT_DEV = Root_SDA2;
+#endif
+
+       /*
+       * Set up the L2CR register.
+       */
+       _set_L2CR(_get_L2CR() | L2CR_L2E);
+
+       chestnut_setup_bridge();
+       chestnut_setup_peripherals();
+
+#ifdef CONFIG_DUMMY_CONSOLE
+       conswitchp = &dummy_con;
+#endif
+
+#if defined(CONFIG_SERIAL_8250)
+       chestnut_early_serial_map();
+#endif
+
+       /* Identify the system */
+       printk(KERN_INFO "System Identification: IBM 750FX/GX Eval Board\n");
+       printk(KERN_INFO "IBM 750FX/GX port (C) 2004 MontaVista Software, Inc. (source@mvista.com)\n");
+
+       if (ppc_md.progress)
+               ppc_md.progress("chestnut_setup_arch: exit", 0);
+
+       return;
+}
+
+/**************************************************************************
+ * FUNCTION: chestnut_restart
+ *
+ * DESCRIPTION: ppc_md machine reset callback
+ *              reset the board via the CPLD command register
+ *
+ ****/
+static void
+chestnut_restart(char *cmd)
+{
+       volatile ulong i = 10000000;
+
+       local_irq_disable();
+
+        /*
+         * Set CPLD Reg 3 bit 0 to 1 to allow MPP signals on reset to work
+         *
+         * MPP24 - board reset
+         */
+       writeb(0x1, (void __iomem *)(cpld_base+3));
+
+       /* GPP pin tied to MPP earlier */
+        mv64x60_set_bits(&bh, MV64x60_GPP_VALUE_SET, BIT(24));
+
+       while (i-- > 0);
+       panic("restart failed\n");
+}
+
+static void
+chestnut_halt(void)
+{
+       local_irq_disable();
+       for (;;);
+       /* NOTREACHED */
+}
+
+static void
+chestnut_power_off(void)
+{
+       chestnut_halt();
+       /* NOTREACHED */
+}
+
+#define SET_PCI_COMMAND_INVALIDATE
+#ifdef SET_PCI_COMMAND_INVALIDATE
+/*
+ * Dave Wilhardt found that PCI_COMMAND_INVALIDATE must
+ * be set for each device if you are using cache coherency.
+ */
+static void __init
+set_pci_command_invalidate(void)
+{
+       struct pci_dev *dev = NULL;
+       u16 val;
+
+       while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+               pci_read_config_word(dev, PCI_COMMAND, &val);
+               val |= PCI_COMMAND_INVALIDATE;
+               pci_write_config_word(dev, PCI_COMMAND, val);
+
+               pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
+                                     L1_CACHE_LINE_SIZE >> 2);
+       }
+}
+#endif
+
+static void __init
+chestnut_pci_fixups(void)
+{
+#ifdef SET_PCI_COMMAND_INVALIDATE
+       set_pci_command_invalidate();
+#endif
+}
+
+/**************************************************************************
+ * FUNCTION: chestnut_map_io
+ *
+ * DESCRIPTION: configure fixed memory-mapped IO
+ *
+ ****/
+static void __init
+chestnut_map_io(void)
+{
+#ifdef CONFIG_MV64360_SRAM_CACHEABLE
+       io_block_mapping(CHESTNUT_INTERNAL_SRAM_BASE,
+                               CHESTNUT_INTERNAL_SRAM_BASE,
+                               CHESTNUT_INTERNAL_SRAM_SIZE,
+                               _PAGE_KERNEL | _PAGE_GUARDED);
+#else
+#ifdef CONFIG_MV64360_SRAM_CACHE_COHERENT
+       io_block_mapping(CHESTNUT_INTERNAL_SRAM_BASE,
+                               CHESTNUT_INTERNAL_SRAM_BASE,
+                               CHESTNUT_INTERNAL_SRAM_SIZE,
+                               _PAGE_KERNEL | _PAGE_GUARDED | _PAGE_COHERENT);
+#else
+       io_block_mapping(CHESTNUT_INTERNAL_SRAM_BASE,
+                               CHESTNUT_INTERNAL_SRAM_BASE,
+                               CHESTNUT_INTERNAL_SRAM_SIZE,
+                               _PAGE_IO);
+#endif /* !CONFIG_MV64360_SRAM_CACHE_COHERENT */
+#endif /* !CONFIG_MV64360_SRAM_CACHEABLE */
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+       io_block_mapping(CHESTNUT_UART_BASE, CHESTNUT_UART_BASE, 0x100000, _PAGE_IO);
+#endif
+}
+
+/**************************************************************************
+ * FUNCTION: chestnut_set_bat
+ *
+ * DESCRIPTION: configures a (temporary) bat mapping for early access to
+ *              device I/O
+ *
+ ****/
+static __inline__ void
+chestnut_set_bat(void)
+{
+        mb();
+        mtspr(DBAT3U, 0xf0001ffe);
+        mtspr(DBAT3L, 0xf000002a);
+        mb();
+
+       return;
+}
+
+/**************************************************************************
+ * FUNCTION: platform_init
+ *
+ * DESCRIPTION: main entry point for configuring board-specific machine
+ *              callbacks
+ *
+ ****/
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+             unsigned long r6, unsigned long r7)
+{
+       parse_bootinfo(find_bootinfo());
+
+        /* Copy the kernel command line arguments to a safe place. */
+
+        if (r6) {
+                *(char *) (r7 + KERNELBASE) = 0;
+                strcpy(cmd_line, (char *) (r6 + KERNELBASE));
+        }
+
+       isa_mem_base = 0;
+
+       ppc_md.setup_arch = chestnut_setup_arch;
+       ppc_md.show_cpuinfo = chestnut_show_cpuinfo;
+       ppc_md.irq_canonicalize = NULL;
+       ppc_md.init_IRQ = mv64360_init_irq;
+       ppc_md.get_irq = mv64360_get_irq;
+       ppc_md.init = NULL;
+
+       ppc_md.find_end_of_memory = chestnut_find_end_of_memory;
+       ppc_md.setup_io_mappings  = chestnut_map_io;
+       ppc_md.pcibios_fixup = chestnut_pci_fixups;
+
+       ppc_md.restart = chestnut_restart;
+       ppc_md.power_off = chestnut_power_off;
+       ppc_md.halt = chestnut_halt;
+
+       ppc_md.time_init = NULL;
+       ppc_md.set_rtc_time = NULL;
+       ppc_md.get_rtc_time = NULL;
+       ppc_md.calibrate_decr = chestnut_calibrate_decr;
+
+       ppc_md.nvram_read_val = NULL;
+       ppc_md.nvram_write_val = NULL;
+
+       ppc_md.heartbeat = NULL;
+
+       ppc_md.pcibios_fixup = chestnut_pci_fixups;
+
+       bh.p_base = CONFIG_MV64X60_NEW_BASE;
+
+       chestnut_set_bat();
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG)
+       ppc_md.progress = gen550_progress;
+#endif
+#if defined(CONFIG_KGDB)
+       ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
+#endif
+
+       if (ppc_md.progress)
+                ppc_md.progress("chestnut_init(): exit", 0);
+
+        return;
+}
diff --git a/arch/ppc/platforms/chestnut.h b/arch/ppc/platforms/chestnut.h
new file mode 100644 (file)
index 0000000..560fd0d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * arch/ppc/platforms/chestnut.h
+ *
+ * Definitions for IBM 750FXGX Eval (Chestnut)
+ *
+ * Author: <source@mvista.com>
+ *
+ * Based on Artesyn Katana code done by Tim Montgomery <timm@artesyncp.com>
+ * Based on code done by Rabeeh Khoury - rabeeh@galileo.co.il
+ * Based on code done by Mark A. Greer <mgreer@mvista.com>
+ *
+ * <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.
+ */
+
+/*
+ * This is the CPU physical memory map (windows must be at least 1MB and start
+ * on a boundary that is a multiple of the window size):
+ *
+ * Seems on the IBM 750FXGX Eval board, the MV64460 Registers can be in
+ * only 2 places per switch U17 0x14000000 or 0xf1000000 easily - chose to
+ * implement at 0xf1000000 only at this time
+ *
+ *    0xfff00000-0xffffffff      - 8 Flash
+ *    0xffd00000-0xffd00004      - CPLD
+ *    0xffc00000-0xffc0000f      - UART
+ *    0xffb00000-0xffb07fff      - FRAM
+ *    0xffa00000-0xffafffff      - *** HOLE ***
+ *    0xff900000-0xff9fffff      - MV64460 Integrated SRAM
+ *    0xfe000000-0xff8fffff      - *** HOLE ***
+ *    0xfc000000-0xfdffffff      - 32bit Flash
+ *    0xf1010000-0xfbffffff      - *** HOLE ***
+ *    0xf1000000-0xf100ffff      - MV64460 Registers
+ */
+
+#ifndef __PPC_PLATFORMS_CHESTNUT_H__
+#define __PPC_PLATFORMS_CHESTNUT_H__
+
+#define CHESTNUT_BOOT_8BIT_BASE                        0xfff00000
+#define CHESTNUT_BOOT_8BIT_SIZE_ACTUAL         (1024*1024)
+#define CHESTNUT_BOOT_SRAM_BASE                        0xffe00000
+#define CHESTNUT_BOOT_SRAM_SIZE_ACTUAL         (1024*1024)
+#define CHESTNUT_CPLD_BASE                     0xffd00000
+#define CHESTNUT_CPLD_SIZE_ACTUAL              5
+#define CHESTNUT_CPLD_REG3                     (CHESTNUT_CPLD_BASE+3)
+#define CHESTNUT_UART_BASE                     0xffc00000
+#define CHESTNUT_UART_SIZE_ACTUAL              16
+#define CHESTNUT_FRAM_BASE                     0xffb00000
+#define CHESTNUT_FRAM_SIZE_ACTUAL              (32*1024)
+#define CHESTNUT_BRIDGE_REG_BASE               0xf1000000
+#define CHESTNUT_INTERNAL_SRAM_BASE            0xff900000
+#define CHESTNUT_INTERNAL_SRAM_SIZE_ACTUAL     (256*1024)
+#define CHESTNUT_32BIT_BASE                    0xfc000000
+#define CHESTNUT_32BIT_SIZE                    (32*1024*1024)
+
+#define CHESTNUT_BOOT_8BIT_SIZE                max(MV64360_WINDOW_SIZE_MIN, \
+                                       CHESTNUT_BOOT_8BIT_SIZE_ACTUAL)
+#define CHESTNUT_BOOT_SRAM_SIZE                max(MV64360_WINDOW_SIZE_MIN, \
+                                       CHESTNUT_BOOT_SRAM_SIZE_ACTUAL)
+#define CHESTNUT_CPLD_SIZE             max(MV64360_WINDOW_SIZE_MIN, \
+                                       CHESTNUT_CPLD_SIZE_ACTUAL)
+#define CHESTNUT_UART_SIZE             max(MV64360_WINDOW_SIZE_MIN, \
+                                       CHESTNUT_UART_SIZE_ACTUAL)
+#define CHESTNUT_FRAM_SIZE             max(MV64360_WINDOW_SIZE_MIN, \
+                                       CHESTNUT_FRAM_SIZE_ACTUAL)
+#define CHESTNUT_INTERNAL_SRAM_SIZE    max(MV64360_WINDOW_SIZE_MIN, \
+                                       CHESTNUT_INTERNAL_SRAM_SIZE_ACTUAL)
+
+#define CHESTNUT_BUS_SPEED             200000000
+#define CHESTNUT_PIBS_DATABASE         0xf0000 /* from PIBS src code */
+
+#define MV64360_ETH_PORT_SERIAL_CONTROL_REG_PORT0      0x243c
+#define MV64360_ETH_PORT_SERIAL_CONTROL_REG_PORT1      0x283c
+
+/*
+ * PCI windows
+ */
+
+#define CHESTNUT_PCI0_MEM_PROC_ADDR    0x80000000
+#define CHESTNUT_PCI0_MEM_PCI_HI_ADDR  0x00000000
+#define CHESTNUT_PCI0_MEM_PCI_LO_ADDR  0x80000000
+#define CHESTNUT_PCI0_MEM_SIZE         0x10000000
+#define CHESTNUT_PCI0_IO_PROC_ADDR     0xa0000000
+#define CHESTNUT_PCI0_IO_PCI_ADDR      0x00000000
+#define CHESTNUT_PCI0_IO_SIZE          0x01000000
+
+/*
+ * Board-specific IRQ info
+ */
+#define CHESTNUT_PCI_SLOT0_IRQ 64+31
+#define CHESTNUT_PCI_SLOT1_IRQ 64+30
+#define CHESTNUT_PCI_SLOT2_IRQ 64+29
+#define CHESTNUT_PCI_SLOT3_IRQ 64+28
+
+/* serial port definitions */
+#define CHESTNUT_UART0_IO_BASE  CHESTNUT_UART_BASE+8
+#define CHESTNUT_UART1_IO_BASE  CHESTNUT_UART_BASE
+
+#define UART0_INT              64+25
+#define UART1_INT              64+26
+
+#ifdef CONFIG_SERIAL_MANY_PORTS
+#define RS_TABLE_SIZE  64
+#else
+#define RS_TABLE_SIZE  2
+#endif
+
+/* Rate for the 3.6864 Mhz clock for the onboard serial chip */
+#define BASE_BAUD              ( 3686400 / 16 )
+
+#ifdef CONFIG_SERIAL_DETECT_IRQ
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ)
+#else
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST)
+#endif
+
+#define STD_UART_OP(num)                                               \
+        { 0, BASE_BAUD, 0, UART##num##_INT, STD_COM_FLAGS,             \
+                iomem_base: (u8 *)CHESTNUT_UART##num##_IO_BASE,        \
+               io_type: SERIAL_IO_MEM},
+
+#define SERIAL_PORT_DFNS        \
+        STD_UART_OP(0)          \
+        STD_UART_OP(1)
+
+#endif /* __PPC_PLATFORMS_CHESTNUT_H__ */
diff --git a/arch/ppc/platforms/cpci690.c b/arch/ppc/platforms/cpci690.c
new file mode 100644 (file)
index 0000000..0405efe
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * arch/ppc/platforms/cpci690.c
+ *
+ * Board setup routines for the Force CPCI690 board.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2003 (c) MontaVista Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This programr
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+#include <linux/irq.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/console.h>
+#include <linux/initrd.h>
+#include <linux/root_dev.h>
+#include <linux/mv643xx.h>
+#include <asm/bootinfo.h>
+#include <asm/machdep.h>
+#include <asm/todc.h>
+#include <asm/time.h>
+#include <asm/mv64x60.h>
+#include <platforms/cpci690.h>
+
+#define BOARD_VENDOR   "Force"
+#define BOARD_MACHINE  "CPCI690"
+
+/* Set IDE controllers into Native mode? */
+#define SET_PCI_IDE_NATIVE
+
+static struct mv64x60_handle   bh;
+static u32 cpci690_br_base;
+
+static const unsigned int cpu_7xx[16] = { /* 7xx & 74xx (but not 745x) */
+       18, 15, 14, 2, 4, 13, 5, 9, 6, 11, 8, 10, 16, 12, 7, 0
+};
+
+TODC_ALLOC();
+
+static int __init
+cpci690_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+       struct pci_controller   *hose = pci_bus_to_hose(dev->bus->number);
+
+       if (hose->index == 0) {
+               static char pci_irq_table[][4] =
+               /*
+                *      PCI IDSEL/INTPIN->INTLINE
+                *         A   B   C   D
+                */
+               {
+                       { 90, 91, 88, 89}, /* IDSEL 30/20 - Sentinel */
+               };
+
+               const long min_idsel = 20, max_idsel = 20, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       }
+       else {
+               static char pci_irq_table[][4] =
+               /*
+                *      PCI IDSEL/INTPIN->INTLINE
+                *         A   B   C   D
+                */
+               {
+                       { 93, 94, 95, 92}, /* IDSEL 28/18 - PMC slot 2 */
+                       {  0,  0,  0,  0}, /* IDSEL 29/19 - Not used */
+                       { 94, 95, 92, 93}, /* IDSEL 30/20 - PMC slot 1 */
+               };
+
+               const long min_idsel = 18, max_idsel = 20, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       }
+}
+
+static int
+cpci690_get_bus_speed(void)
+{
+       return 133333333;
+}
+
+static int
+cpci690_get_cpu_speed(void)
+{
+       unsigned long   hid1;
+
+       hid1 = mfspr(HID1) >> 28;
+       return cpci690_get_bus_speed() * cpu_7xx[hid1]/2;
+}
+
+#define        KB      (1024UL)
+#define        MB      (1024UL * KB)
+#define        GB      (1024UL * MB)
+
+unsigned long __init
+cpci690_find_end_of_memory(void)
+{
+       u32             mem_ctlr_size;
+       static u32      board_size;
+       static u8       first_time = 1;
+
+       if (first_time) {
+               /* Using cpci690_set_bat() mapping ==> virt addr == phys addr */
+               switch (in_8((u8 *) (cpci690_br_base +
+                       CPCI690_BR_MEM_CTLR)) & 0x07) {
+               case 0x01:
+                       board_size = 256*MB;
+                       break;
+               case 0x02:
+                       board_size = 512*MB;
+                       break;
+               case 0x03:
+                       board_size = 768*MB;
+                       break;
+               case 0x04:
+                       board_size = 1*GB;
+                       break;
+               case 0x05:
+                       board_size = 1*GB + 512*MB;
+                       break;
+               case 0x06:
+                       board_size = 2*GB;
+                       break;
+               default:
+                       board_size = 0xffffffff; /* use mem ctlr size */
+               } /* switch */
+
+               mem_ctlr_size =  mv64x60_get_mem_size(CONFIG_MV64X60_NEW_BASE,
+                       MV64x60_TYPE_GT64260A);
+
+               /* Check that mem ctlr & board reg agree.  If not, pick MIN. */
+               if (board_size != mem_ctlr_size) {
+                       printk(KERN_WARNING "Board register & memory controller"
+                               "mem size disagree (board reg: 0x%lx, "
+                               "mem ctlr: 0x%lx)\n",
+                               (ulong)board_size, (ulong)mem_ctlr_size);
+                       board_size = min(board_size, mem_ctlr_size);
+               }
+
+               first_time = 0;
+       } /* if */
+
+       return board_size;
+}
+
+static void __init
+cpci690_setup_bridge(void)
+{
+       struct mv64x60_setup_info       si;
+       int                             i;
+
+       memset(&si, 0, sizeof(si));
+
+       si.phys_reg_base = CONFIG_MV64X60_NEW_BASE;
+
+       si.pci_0.enable_bus = 1;
+       si.pci_0.pci_io.cpu_base = CPCI690_PCI0_IO_START_PROC_ADDR;
+       si.pci_0.pci_io.pci_base_hi = 0;
+       si.pci_0.pci_io.pci_base_lo = CPCI690_PCI0_IO_START_PCI_ADDR;
+       si.pci_0.pci_io.size = CPCI690_PCI0_IO_SIZE;
+       si.pci_0.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_0.pci_mem[0].cpu_base = CPCI690_PCI0_MEM_START_PROC_ADDR;
+       si.pci_0.pci_mem[0].pci_base_hi = CPCI690_PCI0_MEM_START_PCI_HI_ADDR;
+       si.pci_0.pci_mem[0].pci_base_lo = CPCI690_PCI0_MEM_START_PCI_LO_ADDR;
+       si.pci_0.pci_mem[0].size = CPCI690_PCI0_MEM_SIZE;
+       si.pci_0.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_0.pci_cmd_bits = 0;
+       si.pci_0.latency_timer = 0x80;
+
+       si.pci_1.enable_bus = 1;
+       si.pci_1.pci_io.cpu_base = CPCI690_PCI1_IO_START_PROC_ADDR;
+       si.pci_1.pci_io.pci_base_hi = 0;
+       si.pci_1.pci_io.pci_base_lo = CPCI690_PCI1_IO_START_PCI_ADDR;
+       si.pci_1.pci_io.size = CPCI690_PCI1_IO_SIZE;
+       si.pci_1.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_1.pci_mem[0].cpu_base = CPCI690_PCI1_MEM_START_PROC_ADDR;
+       si.pci_1.pci_mem[0].pci_base_hi = CPCI690_PCI1_MEM_START_PCI_HI_ADDR;
+       si.pci_1.pci_mem[0].pci_base_lo = CPCI690_PCI1_MEM_START_PCI_LO_ADDR;
+       si.pci_1.pci_mem[0].size = CPCI690_PCI1_MEM_SIZE;
+       si.pci_1.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_1.pci_cmd_bits = 0;
+       si.pci_1.latency_timer = 0x80;
+
+       for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) {
+               si.cpu_prot_options[i] = 0;
+               si.cpu_snoop_options[i] = GT64260_CPU_SNOOP_WB;
+               si.pci_0.acc_cntl_options[i] =
+                       GT64260_PCI_ACC_CNTL_DREADEN |
+                       GT64260_PCI_ACC_CNTL_RDPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDLINEPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDMULPREFETCH |
+                       GT64260_PCI_ACC_CNTL_SWAP_NONE |
+                       GT64260_PCI_ACC_CNTL_MBURST_32_BTYES;
+               si.pci_0.snoop_options[i] = GT64260_PCI_SNOOP_WB;
+               si.pci_1.acc_cntl_options[i] =
+                       GT64260_PCI_ACC_CNTL_DREADEN |
+                       GT64260_PCI_ACC_CNTL_RDPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDLINEPREFETCH |
+                       GT64260_PCI_ACC_CNTL_RDMULPREFETCH |
+                       GT64260_PCI_ACC_CNTL_SWAP_NONE |
+                       GT64260_PCI_ACC_CNTL_MBURST_32_BTYES;
+               si.pci_1.snoop_options[i] = GT64260_PCI_SNOOP_WB;
+       }
+
+        /* Lookup PCI host bridges */
+        if (mv64x60_init(&bh, &si))
+                printk(KERN_ERR "Bridge initialization failed.\n");
+
+       pci_dram_offset = 0; /* System mem at same addr on PCI & cpu bus */
+       ppc_md.pci_swizzle = common_swizzle;
+       ppc_md.pci_map_irq = cpci690_map_irq;
+       ppc_md.pci_exclude_device = mv64x60_pci_exclude_device;
+
+       mv64x60_set_bus(&bh, 0, 0);
+       bh.hose_a->first_busno = 0;
+       bh.hose_a->last_busno = 0xff;
+       bh.hose_a->last_busno = pciauto_bus_scan(bh.hose_a, 0);
+
+       bh.hose_b->first_busno = bh.hose_a->last_busno + 1;
+       mv64x60_set_bus(&bh, 1, bh.hose_b->first_busno);
+       bh.hose_b->last_busno = 0xff;
+       bh.hose_b->last_busno = pciauto_bus_scan(bh.hose_b,
+               bh.hose_b->first_busno);
+
+       return;
+}
+
+static void __init
+cpci690_setup_peripherals(void)
+{
+       /* Set up windows to CPLD, RTC/TODC, IPMI. */
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, CPCI690_BR_BASE,
+               CPCI690_BR_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_0_WIN);
+       cpci690_br_base = (u32)ioremap(CPCI690_BR_BASE, CPCI690_BR_SIZE);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, CPCI690_TODC_BASE,
+               CPCI690_TODC_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_1_WIN);
+       TODC_INIT(TODC_TYPE_MK48T35, 0, 0,
+                       ioremap(CPCI690_TODC_BASE, CPCI690_TODC_SIZE), 8);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN, CPCI690_IPMI_BASE,
+               CPCI690_IPMI_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_2_WIN);
+
+       mv64x60_set_bits(&bh, MV64x60_PCI0_ARBITER_CNTL, (1<<31));
+       mv64x60_set_bits(&bh, MV64x60_PCI1_ARBITER_CNTL, (1<<31));
+
+        mv64x60_set_bits(&bh, MV64x60_CPU_MASTER_CNTL, (1<<9)); /* Only 1 cpu */
+
+       /*
+        * Turn off timer/counters.  Not turning off watchdog timer because
+        * can't read its reg on the 64260A so don't know if we'll be enabling
+        * or disabling.
+        */
+       mv64x60_clr_bits(&bh, MV64x60_TIMR_CNTR_0_3_CNTL,
+                       ((1<<0) | (1<<8) | (1<<16) | (1<<24)));
+       mv64x60_clr_bits(&bh, GT64260_TIMR_CNTR_4_7_CNTL,
+                       ((1<<0) | (1<<8) | (1<<16) | (1<<24)));
+
+       /*
+        * Set MPSC Multiplex RMII
+        * NOTE: ethernet driver modifies bit 0 and 1
+        */
+       mv64x60_write(&bh, GT64260_MPP_SERIAL_PORTS_MULTIPLEX, 0x00001102);
+
+#define GPP_EXTERNAL_INTERRUPTS \
+               ((1<<24) | (1<<25) | (1<<26) | (1<<27) | \
+                (1<<28) | (1<<29) | (1<<30) | (1<<31))
+       /* PCI interrupts are inputs */
+       mv64x60_clr_bits(&bh, MV64x60_GPP_IO_CNTL, GPP_EXTERNAL_INTERRUPTS);
+       /* PCI interrupts are active low */
+       mv64x60_set_bits(&bh, MV64x60_GPP_LEVEL_CNTL, GPP_EXTERNAL_INTERRUPTS);
+
+       /* Clear any pending interrupts for these inputs and enable them. */
+       mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~GPP_EXTERNAL_INTERRUPTS);
+       mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, GPP_EXTERNAL_INTERRUPTS);
+
+       /* Route MPP interrupt inputs to GPP */
+       mv64x60_write(&bh, MV64x60_MPP_CNTL_2, 0x00000000);
+       mv64x60_write(&bh, MV64x60_MPP_CNTL_3, 0x00000000);
+
+       return;
+}
+
+static void __init
+cpci690_setup_arch(void)
+{
+       if (ppc_md.progress)
+               ppc_md.progress("cpci690_setup_arch: enter", 0);
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (initrd_start)
+               ROOT_DEV = Root_RAM0;
+       else
+#endif
+#ifdef   CONFIG_ROOT_NFS
+               ROOT_DEV = Root_NFS;
+#else
+               ROOT_DEV = Root_SDA2;
+#endif
+
+       if (ppc_md.progress)
+               ppc_md.progress("cpci690_setup_arch: Enabling L2 cache", 0);
+
+       /* Enable L2 and L3 caches (if 745x) */
+       _set_L2CR(_get_L2CR() | L2CR_L2E);
+       _set_L3CR(_get_L3CR() | L3CR_L3E);
+
+       if (ppc_md.progress)
+               ppc_md.progress("cpci690_setup_arch: Initializing bridge", 0);
+
+       cpci690_setup_bridge();         /* set up PCI bridge(s) */
+       cpci690_setup_peripherals();    /* set up chip selects/GPP/MPP etc */
+
+       if (ppc_md.progress)
+               ppc_md.progress("cpci690_setup_arch: bridge init complete", 0);
+
+       printk(KERN_INFO "%s %s port (C) 2003 MontaVista Software, Inc. "
+               "(source@mvista.com)\n", BOARD_VENDOR, BOARD_MACHINE);
+
+       if (ppc_md.progress)
+               ppc_md.progress("cpci690_setup_arch: exit", 0);
+
+       return;
+}
+
+/* Platform device data fixup routines. */
+#if defined(CONFIG_SERIAL_MPSC)
+static void __init
+cpci690_fixup_mpsc_pdata(struct platform_device *pdev)
+{
+       struct mpsc_pdata *pdata;
+
+       pdata = (struct mpsc_pdata *)pdev->dev.platform_data;
+
+       pdata->max_idle = 40;
+       pdata->default_baud = 9600;
+       pdata->brg_clk_src = 8;
+       pdata->brg_clk_freq = 133000000;
+
+       return;
+}
+
+static int __init
+cpci690_platform_notify(struct device *dev)
+{
+       static struct {
+               char    *bus_id;
+               void    ((*rtn)(struct platform_device *pdev));
+       } dev_map[] = {
+               { MPSC_CTLR_NAME "0", cpci690_fixup_mpsc_pdata },
+               { MPSC_CTLR_NAME "1", cpci690_fixup_mpsc_pdata },
+       };
+       struct platform_device  *pdev;
+       int     i;
+
+       if (dev && dev->bus_id)
+               for (i=0; i<ARRAY_SIZE(dev_map); i++)
+                       if (!strncmp(dev->bus_id, dev_map[i].bus_id,
+                               BUS_ID_SIZE)) {
+
+                               pdev = container_of(dev,
+                                       struct platform_device, dev);
+                               dev_map[i].rtn(pdev);
+                       }
+
+       return 0;
+}
+#endif
+
+static void
+cpci690_reset_board(void)
+{
+       u32     i = 10000;
+
+       local_irq_disable();
+       out_8((u8 *)(cpci690_br_base + CPCI690_BR_SW_RESET), 0x11);
+
+       while (i != 0) i++;
+       panic("restart failed\n");
+}
+
+static void
+cpci690_restart(char *cmd)
+{
+       cpci690_reset_board();
+}
+
+static void
+cpci690_halt(void)
+{
+       while (1);
+       /* NOTREACHED */
+}
+
+static void
+cpci690_power_off(void)
+{
+       cpci690_halt();
+       /* NOTREACHED */
+}
+
+static int
+cpci690_show_cpuinfo(struct seq_file *m)
+{
+       seq_printf(m, "vendor\t\t: " BOARD_VENDOR "\n");
+       seq_printf(m, "machine\t\t: " BOARD_MACHINE "\n");
+       seq_printf(m, "cpu MHz\t\t: %d\n", cpci690_get_cpu_speed()/1000/1000);
+       seq_printf(m, "bus MHz\t\t: %d\n", cpci690_get_bus_speed()/1000/1000);
+
+       return 0;
+}
+
+static void __init
+cpci690_calibrate_decr(void)
+{
+       ulong freq;
+
+       freq = cpci690_get_bus_speed()/4;
+
+       printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n",
+              freq/1000000, freq%1000000);
+
+       tb_ticks_per_jiffy = freq / HZ;
+       tb_to_us = mulhwu_scale_factor(freq, 1000000);
+
+       return;
+}
+
+static __inline__ void
+cpci690_set_bat(u32 addr, u32 size)
+{
+       addr &= 0xfffe0000;
+       size &= 0x1ffe0000;
+       size = ((size >> 17) - 1) << 2;
+
+       mb();
+       mtspr(DBAT1U, addr | size | 0x2); /* Vs == 1; Vp == 0 */
+       mtspr(DBAT1L, addr | 0x2a); /* WIMG bits == 0101; PP == r/w access */
+       mb();
+
+       return;
+}
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+static void __init
+cpci690_map_io(void)
+{
+       io_block_mapping(CONFIG_MV64X60_NEW_BASE, CONFIG_MV64X60_NEW_BASE,
+               128 * KB, _PAGE_IO);
+}
+#endif
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+             unsigned long r6, unsigned long r7)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+       initrd_start=initrd_end=0;
+       initrd_below_start_ok=0;
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+       parse_bootinfo(find_bootinfo());
+
+       loops_per_jiffy = cpci690_get_cpu_speed() / HZ;
+
+       isa_mem_base = 0;
+
+       ppc_md.setup_arch = cpci690_setup_arch;
+       ppc_md.show_cpuinfo = cpci690_show_cpuinfo;
+       ppc_md.init_IRQ = gt64260_init_irq;
+       ppc_md.get_irq = gt64260_get_irq;
+       ppc_md.restart = cpci690_restart;
+       ppc_md.power_off = cpci690_power_off;
+       ppc_md.halt = cpci690_halt;
+       ppc_md.find_end_of_memory = cpci690_find_end_of_memory;
+       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;
+       ppc_md.calibrate_decr = cpci690_calibrate_decr;
+
+       /*
+        * Need to map in board regs (used by cpci690_find_end_of_memory())
+        * and the bridge's regs (used by progress);
+        */
+       cpci690_set_bat(CPCI690_BR_BASE, 32 * MB);
+       cpci690_br_base = CPCI690_BR_BASE;
+
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+       ppc_md.setup_io_mappings = cpci690_map_io;
+       ppc_md.progress = mv64x60_mpsc_progress;
+       mv64x60_progress_init(CONFIG_MV64X60_NEW_BASE);
+#endif /* CONFIG_SERIAL_TEXT_DEBUG */
+#ifdef CONFIG_KGDB
+       ppc_md.setup_io_mappings = cpci690_map_io;
+       ppc_md.early_serial_map = cpci690_early_serial_map;
+#endif /* CONFIG_KGDB */
+
+#if defined(CONFIG_SERIAL_MPSC)
+       platform_notify = cpci690_platform_notify;
+#endif
+
+       return;
+}
diff --git a/arch/ppc/platforms/cpci690.h b/arch/ppc/platforms/cpci690.h
new file mode 100644 (file)
index 0000000..3144b87
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * arch/ppc/platforms/cpci690.h
+ *
+ * Definitions for Force CPCI690
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2003 (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.
+ */
+
+/*
+ * The GT64260 has 2 PCI buses each with 1 window from the CPU bus to
+ * PCI I/O space and 4 windows from the CPU bus to PCI MEM space.
+ */
+
+#ifndef __PPC_PLATFORMS_CPCI690_H
+#define __PPC_PLATFORMS_CPCI690_H
+
+/*
+ * Define bd_t to pass in the MAC addresses used by the GT64260's enet ctlrs.
+ */
+#define        CPCI690_BI_MAGIC                0xFE8765DC
+
+typedef struct board_info {
+       u32     bi_magic;
+       u8      bi_enetaddr[3][6];
+} bd_t;
+
+/* PCI bus Resource setup */
+#define CPCI690_PCI0_MEM_START_PROC_ADDR       0x80000000
+#define CPCI690_PCI0_MEM_START_PCI_HI_ADDR     0x00000000
+#define CPCI690_PCI0_MEM_START_PCI_LO_ADDR     0x80000000
+#define CPCI690_PCI0_MEM_SIZE                  0x10000000
+#define CPCI690_PCI0_IO_START_PROC_ADDR                0xa0000000
+#define CPCI690_PCI0_IO_START_PCI_ADDR         0x00000000
+#define CPCI690_PCI0_IO_SIZE                   0x01000000
+
+#define CPCI690_PCI1_MEM_START_PROC_ADDR       0x90000000
+#define CPCI690_PCI1_MEM_START_PCI_HI_ADDR     0x00000000
+#define CPCI690_PCI1_MEM_START_PCI_LO_ADDR     0x90000000
+#define CPCI690_PCI1_MEM_SIZE                  0x10000000
+#define CPCI690_PCI1_IO_START_PROC_ADDR                0xa1000000
+#define CPCI690_PCI1_IO_START_PCI_ADDR         0x01000000
+#define CPCI690_PCI1_IO_SIZE                   0x01000000
+
+/* Board Registers */
+#define        CPCI690_BR_BASE                         0xf0000000
+#define        CPCI690_BR_SIZE_ACTUAL                  0x8
+#define        CPCI690_BR_SIZE                 max(GT64260_WINDOW_SIZE_MIN,    \
+                                               CPCI690_BR_SIZE_ACTUAL)
+#define        CPCI690_BR_LED_CNTL                     0x00
+#define        CPCI690_BR_SW_RESET                     0x01
+#define        CPCI690_BR_MISC_STATUS                  0x02
+#define        CPCI690_BR_SWITCH_STATUS                0x03
+#define        CPCI690_BR_MEM_CTLR                     0x04
+#define        CPCI690_BR_LAST_RESET_1                 0x05
+#define        CPCI690_BR_LAST_RESET_2                 0x06
+
+#define        CPCI690_TODC_BASE                       0xf0100000
+#define        CPCI690_TODC_SIZE_ACTUAL                0x8000 /* Size or NVRAM + RTC */
+#define        CPCI690_TODC_SIZE               max(GT64260_WINDOW_SIZE_MIN,    \
+                                               CPCI690_TODC_SIZE_ACTUAL)
+#define        CPCI690_MAC_OFFSET                      0x7c10 /* MAC in RTC NVRAM */
+
+#define        CPCI690_IPMI_BASE                       0xf0200000
+#define        CPCI690_IPMI_SIZE_ACTUAL                0x10 /* 16 bytes of IPMI */
+#define        CPCI690_IPMI_SIZE               max(GT64260_WINDOW_SIZE_MIN,    \
+                                               CPCI690_IPMI_SIZE_ACTUAL)
+
+#endif /* __PPC_PLATFORMS_CPCI690_H */
diff --git a/arch/ppc/platforms/katana.c b/arch/ppc/platforms/katana.c
new file mode 100644 (file)
index 0000000..bad8f27
--- /dev/null
@@ -0,0 +1,684 @@
+/*
+ * arch/ppc/platforms/katana.c
+ *
+ * Board setup routines for the Artesyn Katana 750 based boards.
+ *
+ * Tim Montgomery <timm@artesyncp.com>
+ *
+ * Based on code done by Rabeeh Khoury - rabeeh@galileo.co.il
+ * Based on code done by - Mark A. Greer <mgreer@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+/*
+ * Supports the Artesyn 750i, 752i, and 3750.  The 752i is virtually identical
+ * to the 750i except that it has an mv64460 bridge.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+#include <linux/console.h>
+#include <linux/initrd.h>
+#include <linux/root_dev.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/smp.h>
+#include <linux/mv643xx.h>
+#ifdef CONFIG_BOOTIMG
+#include <linux/bootimg.h>
+#endif
+#include <asm/page.h>
+#include <asm/time.h>
+#include <asm/smp.h>
+#include <asm/todc.h>
+#include <asm/bootinfo.h>
+#include <asm/mv64x60.h>
+#include <platforms/katana.h>
+
+static struct          mv64x60_handle bh;
+static katana_id_t     katana_id;
+static u32             cpld_base;
+static u32             sram_base;
+
+/* PCI Interrupt routing */
+static int __init
+katana_irq_lookup_750i(unsigned char idsel, unsigned char pin)
+{
+       static char pci_irq_table[][4] = {
+               /*
+                * PCI IDSEL/INTPIN->INTLINE
+                *       A   B   C   D
+                */
+               /* IDSEL 4  (PMC 1) */
+               { KATANA_PCI_INTB_IRQ_750i, KATANA_PCI_INTC_IRQ_750i,
+                       KATANA_PCI_INTD_IRQ_750i, KATANA_PCI_INTA_IRQ_750i },
+               /* IDSEL 5  (PMC 2) */
+               { KATANA_PCI_INTC_IRQ_750i, KATANA_PCI_INTD_IRQ_750i,
+                       KATANA_PCI_INTA_IRQ_750i, KATANA_PCI_INTB_IRQ_750i },
+               /* IDSEL 6 (T8110) */
+               {KATANA_PCI_INTD_IRQ_750i, 0, 0, 0 },
+       };
+       const long min_idsel = 4, max_idsel = 6, irqs_per_slot = 4;
+
+       return PCI_IRQ_TABLE_LOOKUP;
+}
+
+static int __init
+katana_irq_lookup_3750(unsigned char idsel, unsigned char pin)
+{
+       static char pci_irq_table[][4] = {
+               /*
+                * PCI IDSEL/INTPIN->INTLINE
+                *       A   B   C   D
+                */
+               { KATANA_PCI_INTA_IRQ_3750, 0, 0, 0 }, /* IDSEL 3 (BCM5691) */
+               { KATANA_PCI_INTB_IRQ_3750, 0, 0, 0 }, /* IDSEL 4 (MV64360 #2)*/
+               { KATANA_PCI_INTC_IRQ_3750, 0, 0, 0 }, /* IDSEL 5 (MV64360 #3)*/
+       };
+       const long min_idsel = 3, max_idsel = 5, irqs_per_slot = 4;
+
+       return PCI_IRQ_TABLE_LOOKUP;
+}
+
+static int __init
+katana_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+       switch (katana_id) {
+       case KATANA_ID_750I:
+       case KATANA_ID_752I:
+               return katana_irq_lookup_750i(idsel, pin);
+
+       case KATANA_ID_3750:
+               return katana_irq_lookup_3750(idsel, pin);
+
+       default:
+               printk(KERN_ERR "Bogus board ID\n");
+               return 0;
+       }
+}
+
+/* Board info retrieval routines */
+void __init
+katana_get_board_id(void)
+{
+       switch (in_8((volatile char *)(cpld_base + KATANA_CPLD_PRODUCT_ID))) {
+       case KATANA_PRODUCT_ID_3750:
+               katana_id = KATANA_ID_3750;
+               break;
+
+       case KATANA_PRODUCT_ID_750i:
+               katana_id = KATANA_ID_750I;
+               break;
+
+       case KATANA_PRODUCT_ID_752i:
+               katana_id = KATANA_ID_752I;
+               break;
+
+       default:
+               printk(KERN_ERR "Unsupported board\n");
+       }
+}
+
+int __init
+katana_get_proc_num(void)
+{
+       u16             val;
+       u8              save_exclude;
+       static int      proc = -1;
+       static u8       first_time = 1;
+
+       if (first_time) {
+               if (katana_id != KATANA_ID_3750)
+                       proc = 0;
+               else {
+                       save_exclude = mv64x60_pci_exclude_bridge;
+                       mv64x60_pci_exclude_bridge = 0;
+
+                       early_read_config_word(bh.hose_a, 0,
+                               PCI_DEVFN(0,0), PCI_DEVICE_ID, &val);
+
+                       mv64x60_pci_exclude_bridge = save_exclude;
+
+                       switch(val) {
+                       case PCI_DEVICE_ID_KATANA_3750_PROC0:
+                               proc = 0;
+                               break;
+
+                       case PCI_DEVICE_ID_KATANA_3750_PROC1:
+                               proc = 1;
+                               break;
+
+                       case PCI_DEVICE_ID_KATANA_3750_PROC2:
+                               proc = 2;
+                               break;
+
+                       default:
+                               printk(KERN_ERR "Bogus Device ID\n");
+                       }
+               }
+
+               first_time = 0;
+       }
+
+       return proc;
+}
+
+static inline int
+katana_is_monarch(void)
+{
+       return in_8((volatile char *)(cpld_base + KATANA_CPLD_BD_CFG_3)) &
+               KATANA_CPLD_BD_CFG_3_MONARCH;
+}
+
+static void __init
+katana_enable_ipmi(void)
+{
+       u8 reset_out;
+
+       /* Enable access to IPMI ctlr by clearing IPMI PORTSEL bit in CPLD */
+       reset_out = in_8((volatile char *)(cpld_base + KATANA_CPLD_RESET_OUT));
+       reset_out &= ~KATANA_CPLD_RESET_OUT_PORTSEL;
+       out_8((volatile void *)(cpld_base + KATANA_CPLD_RESET_OUT), reset_out);
+       return;
+}
+
+static unsigned long
+katana_bus_freq(void)
+{
+       u8 bd_cfg_0;
+
+       bd_cfg_0 = in_8((volatile char *)(cpld_base + KATANA_CPLD_BD_CFG_0));
+
+       switch (bd_cfg_0 & KATANA_CPLD_BD_CFG_0_SYSCLK_MASK) {
+       case KATANA_CPLD_BD_CFG_0_SYSCLK_200:
+               return 200000000;
+               break;
+
+       case KATANA_CPLD_BD_CFG_0_SYSCLK_166:
+               return 166666666;
+               break;
+
+       case KATANA_CPLD_BD_CFG_0_SYSCLK_133:
+               return 133333333;
+               break;
+
+       case KATANA_CPLD_BD_CFG_0_SYSCLK_100:
+               return 100000000;
+               break;
+
+       default:
+               return 133333333;
+               break;
+       }
+}
+
+/* Bridge & platform setup routines */
+void __init
+katana_intr_setup(void)
+{
+       /* MPP 8, 9, and 10 */
+       mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_1, 0xfff);
+
+       /* MPP 14 */
+       if ((katana_id == KATANA_ID_750I) || (katana_id == KATANA_ID_752I))
+               mv64x60_clr_bits(&bh, MV64x60_MPP_CNTL_1, 0x0f000000);
+
+       /*
+        * Define GPP 8,9,and 10 interrupt polarity as active low
+        * input signal and level triggered
+        */
+       mv64x60_set_bits(&bh, MV64x60_GPP_LEVEL_CNTL, 0x700);
+       mv64x60_clr_bits(&bh, MV64x60_GPP_IO_CNTL, 0x700);
+
+       if ((katana_id == KATANA_ID_750I) || (katana_id == KATANA_ID_752I)) {
+               mv64x60_set_bits(&bh, MV64x60_GPP_LEVEL_CNTL, (1<<14));
+               mv64x60_clr_bits(&bh, MV64x60_GPP_IO_CNTL, (1<<14));
+       }
+
+       /* Config GPP intr ctlr to respond to level trigger */
+       mv64x60_set_bits(&bh, MV64x60_COMM_ARBITER_CNTL, (1<<10));
+
+       /* Erranum FEr PCI-#8 */
+       mv64x60_clr_bits(&bh, MV64x60_PCI0_CMD, (1<<5) | (1<<9));
+       mv64x60_clr_bits(&bh, MV64x60_PCI1_CMD, (1<<5) | (1<<9));
+
+       /*
+        * Dismiss and then enable interrupt on GPP interrupt cause
+        * for CPU #0
+        */
+       mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~0x700);
+       mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, 0x700);
+
+       if ((katana_id == KATANA_ID_750I) || (katana_id == KATANA_ID_752I)) {
+               mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~(1<<14));
+               mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, (1<<14));
+       }
+
+       /*
+        * Dismiss and then enable interrupt on CPU #0 high cause reg
+        * BIT25 summarizes GPP interrupts 8-15
+        */
+       mv64x60_set_bits(&bh, MV64360_IC_CPU0_INTR_MASK_HI, (1<<25));
+       return;
+}
+
+void __init
+katana_setup_peripherals(void)
+{
+       u32 base, size_0, size_1;
+
+       /* Set up windows for boot CS, soldered & socketed flash, and CPLD */
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN,
+                KATANA_BOOT_WINDOW_BASE, KATANA_BOOT_WINDOW_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN);
+
+       /* Assume firmware set up window sizes correctly for dev 0 & 1 */
+       mv64x60_get_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN, &base, &size_0);
+
+       if (size_0 > 0) {
+               mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN,
+                        KATANA_SOLDERED_FLASH_BASE, size_0, 0);
+               bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_0_WIN);
+       }
+
+       mv64x60_get_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN, &base, &size_1);
+
+       if (size_1 > 0) {
+               mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN,
+                        (KATANA_SOLDERED_FLASH_BASE + size_0), size_1, 0);
+               bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_1_WIN);
+       }
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN,
+                KATANA_SOCKET_BASE, KATANA_SOCKETED_FLASH_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_2_WIN);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN,
+                KATANA_CPLD_BASE, KATANA_CPLD_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_3_WIN);
+       cpld_base = (u32)ioremap(KATANA_CPLD_BASE, KATANA_CPLD_SIZE);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN,
+                KATANA_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN);
+       sram_base = (u32)ioremap(KATANA_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE);
+
+       /* Set up Enet->SRAM window */
+       mv64x60_set_32bit_window(&bh, MV64x60_ENET2MEM_4_WIN,
+               KATANA_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE, 0x2);
+       bh.ci->enable_window_32bit(&bh, MV64x60_ENET2MEM_4_WIN);
+
+       /* Give enet r/w access to memory region */
+       mv64x60_set_bits(&bh, MV64360_ENET2MEM_ACC_PROT_0, (0x3 << (4 << 1)));
+       mv64x60_set_bits(&bh, MV64360_ENET2MEM_ACC_PROT_1, (0x3 << (4 << 1)));
+       mv64x60_set_bits(&bh, MV64360_ENET2MEM_ACC_PROT_2, (0x3 << (4 << 1)));
+
+       mv64x60_clr_bits(&bh, MV64x60_PCI1_PCI_DECODE_CNTL, (1 << 3));
+       mv64x60_clr_bits(&bh, MV64x60_TIMR_CNTR_0_3_CNTL,
+                        ((1 << 0) | (1 << 8) | (1 << 16) | (1 << 24)));
+
+       /* Must wait until window set up before retrieving board id */
+       katana_get_board_id();
+
+       /* Enumerate pci bus (must know board id before getting proc number) */
+       if (katana_get_proc_num() == 0)
+               bh.hose_b->last_busno = pciauto_bus_scan(bh.hose_b, 0);
+
+#if defined(CONFIG_NOT_COHERENT_CACHE)
+       mv64x60_write(&bh, MV64360_SRAM_CONFIG, 0x00160000);
+#else
+       mv64x60_write(&bh, MV64360_SRAM_CONFIG, 0x001600b2);
+#endif
+
+       /*
+        * Setting the SRAM to 0. Note that this generates parity errors on
+        * internal data path in SRAM since it's first time accessing it
+        * while after reset it's not configured.
+        */
+       memset((void *)sram_base, 0, MV64360_SRAM_SIZE);
+
+       /* Only processor zero [on 3750] is an PCI interrupt controller */
+       if (katana_get_proc_num() == 0)
+               katana_intr_setup();
+
+       return;
+}
+
+static void __init
+katana_setup_bridge(void)
+{
+       struct mv64x60_setup_info si;
+       int i;
+
+       memset(&si, 0, sizeof(si));
+
+       si.phys_reg_base = KATANA_BRIDGE_REG_BASE;
+
+       si.pci_1.enable_bus = 1;
+       si.pci_1.pci_io.cpu_base = KATANA_PCI1_IO_START_PROC_ADDR;
+       si.pci_1.pci_io.pci_base_hi = 0;
+       si.pci_1.pci_io.pci_base_lo = KATANA_PCI1_IO_START_PCI_ADDR;
+       si.pci_1.pci_io.size = KATANA_PCI1_IO_SIZE;
+       si.pci_1.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_1.pci_mem[0].cpu_base = KATANA_PCI1_MEM_START_PROC_ADDR;
+       si.pci_1.pci_mem[0].pci_base_hi = KATANA_PCI1_MEM_START_PCI_HI_ADDR;
+       si.pci_1.pci_mem[0].pci_base_lo = KATANA_PCI1_MEM_START_PCI_LO_ADDR;
+       si.pci_1.pci_mem[0].size = KATANA_PCI1_MEM_SIZE;
+       si.pci_1.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_1.pci_cmd_bits = 0;
+       si.pci_1.latency_timer = 0x80;
+
+       for (i = 0; i < MV64x60_CPU2MEM_WINDOWS; i++) {
+#if defined(CONFIG_NOT_COHERENT_CACHE)
+               si.cpu_prot_options[i] = 0;
+               si.enet_options[i] = MV64360_ENET2MEM_SNOOP_NONE;
+               si.mpsc_options[i] = MV64360_MPSC2MEM_SNOOP_NONE;
+               si.idma_options[i] = MV64360_IDMA2MEM_SNOOP_NONE;
+
+               si.pci_1.acc_cntl_options[i] =
+                   MV64360_PCI_ACC_CNTL_SNOOP_NONE |
+                   MV64360_PCI_ACC_CNTL_SWAP_NONE |
+                   MV64360_PCI_ACC_CNTL_MBURST_128_BYTES |
+                   MV64360_PCI_ACC_CNTL_RDSIZE_256_BYTES;
+#else
+               si.cpu_prot_options[i] = 0;
+               si.enet_options[i] = MV64360_ENET2MEM_SNOOP_NONE; /* errata */
+               si.mpsc_options[i] = MV64360_MPSC2MEM_SNOOP_NONE; /* errata */
+               si.idma_options[i] = MV64360_IDMA2MEM_SNOOP_NONE; /* errata */
+
+               si.pci_1.acc_cntl_options[i] =
+                   MV64360_PCI_ACC_CNTL_SNOOP_WB |
+                   MV64360_PCI_ACC_CNTL_SWAP_NONE |
+                   MV64360_PCI_ACC_CNTL_MBURST_32_BYTES |
+                   MV64360_PCI_ACC_CNTL_RDSIZE_32_BYTES;
+#endif
+       }
+
+       /* Lookup PCI host bridges */
+       if (mv64x60_init(&bh, &si))
+               printk(KERN_WARNING "Bridge initialization failed.\n");
+
+       pci_dram_offset = 0; /* sys mem at same addr on PCI & cpu bus */
+       ppc_md.pci_swizzle = common_swizzle;
+       ppc_md.pci_map_irq = katana_map_irq;
+       ppc_md.pci_exclude_device = mv64x60_pci_exclude_device;
+
+       mv64x60_set_bus(&bh, 1, 0);
+       bh.hose_b->first_busno = 0;
+       bh.hose_b->last_busno = 0xff;
+
+       return;
+}
+
+static void __init
+katana_setup_arch(void)
+{
+       if (ppc_md.progress)
+               ppc_md.progress("katana_setup_arch: enter", 0);
+
+       set_tb(0, 0);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (initrd_start)
+               ROOT_DEV = Root_RAM0;
+       else
+#endif
+#ifdef   CONFIG_ROOT_NFS
+               ROOT_DEV = Root_NFS;
+#else
+               ROOT_DEV = Root_SDA2;
+#endif
+
+       /*
+        * Set up the L2CR register.
+        *
+        * 750FX has only L2E, L2PE (bits 2-8 are reserved)
+        * DD2.0 has bug that requires the L2 to be in WRT mode
+        * avoid dirty data in cache
+        */
+       if (PVR_REV(mfspr(PVR)) == 0x0200) {
+               printk(KERN_INFO "DD2.0 detected. Setting L2 cache"
+                       "to Writethrough mode\n");
+               _set_L2CR(L2CR_L2E | L2CR_L2PE | L2CR_L2WT);
+       }
+       else
+               _set_L2CR(L2CR_L2E | L2CR_L2PE);
+
+       if (ppc_md.progress)
+               ppc_md.progress("katana_setup_arch: calling setup_bridge", 0);
+
+       katana_setup_bridge();
+       katana_setup_peripherals();
+       katana_enable_ipmi();
+
+       printk(KERN_INFO "Artesyn Communication Products, LLC - Katana(TM)\n");
+       if (ppc_md.progress)
+               ppc_md.progress("katana_setup_arch: exit", 0);
+       return;
+}
+
+/* Platform device data fixup routines. */
+#if defined(CONFIG_SERIAL_MPSC)
+static void __init
+katana_fixup_mpsc_pdata(struct platform_device *pdev)
+{
+       struct mpsc_pdata *pdata;
+
+       pdata = (struct mpsc_pdata *)pdev->dev.platform_data;
+
+       pdata->max_idle = 40;
+       pdata->default_baud = KATANA_DEFAULT_BAUD;
+       pdata->brg_clk_src = KATANA_MPSC_CLK_SRC;
+       pdata->brg_clk_freq = KATANA_MPSC_CLK_FREQ;
+
+       return;
+}
+#endif
+
+#if defined(CONFIG_MV643XX_ETH)
+static void __init
+katana_fixup_eth_pdata(struct platform_device *pdev)
+{
+       struct mv64xxx_eth_platform_data *eth_pd;
+       static u16 phy_addr[] = {
+               KATANA_ETH0_PHY_ADDR,
+               KATANA_ETH1_PHY_ADDR,
+               KATANA_ETH2_PHY_ADDR,
+       };
+       int     rx_size = KATANA_ETH_RX_QUEUE_SIZE * MV64340_ETH_DESC_SIZE;
+       int     tx_size = KATANA_ETH_TX_QUEUE_SIZE * MV64340_ETH_DESC_SIZE;
+
+       eth_pd = pdev->dev.platform_data;
+       eth_pd->force_phy_addr = 1;
+       eth_pd->phy_addr = phy_addr[pdev->id];
+       eth_pd->tx_queue_size = KATANA_ETH_TX_QUEUE_SIZE;
+       eth_pd->rx_queue_size = KATANA_ETH_RX_QUEUE_SIZE;
+       eth_pd->tx_sram_addr = mv643xx_sram_alloc(tx_size);
+
+       if (eth_pd->tx_sram_addr)
+               eth_pd->tx_sram_size = tx_size;
+       else
+               printk(KERN_ERR "mv643xx_sram_alloc failed\n");
+
+       eth_pd->rx_sram_addr = mv643xx_sram_alloc(rx_size);
+       if (eth_pd->rx_sram_addr)
+               eth_pd->rx_sram_size = rx_size;
+       else
+               printk(KERN_ERR "mv643xx_sram_alloc failed\n");
+}
+#endif
+
+static int __init
+katana_platform_notify(struct device *dev)
+{
+       static struct {
+               char    *bus_id;
+               void    ((*rtn)(struct platform_device *pdev));
+       } dev_map[] = {
+#if defined(CONFIG_SERIAL_MPSC)
+               { MPSC_CTLR_NAME "0", katana_fixup_mpsc_pdata },
+               { MPSC_CTLR_NAME "1", katana_fixup_mpsc_pdata },
+#endif
+#if defined(CONFIG_MV643XX_ETH)
+               { MV64XXX_ETH_NAME "0", katana_fixup_eth_pdata },
+               { MV64XXX_ETH_NAME "1", katana_fixup_eth_pdata },
+               { MV64XXX_ETH_NAME "2", katana_fixup_eth_pdata },
+#endif
+       };
+       struct platform_device  *pdev;
+       int     i;
+
+       if (dev && dev->bus_id)
+               for (i=0; i<ARRAY_SIZE(dev_map); i++)
+                       if (!strncmp(dev->bus_id, dev_map[i].bus_id,
+                               BUS_ID_SIZE)) {
+
+                               pdev = container_of(dev,
+                                       struct platform_device, dev);
+                               dev_map[i].rtn(pdev);
+                       }
+
+       return 0;
+}
+
+static void
+katana_restart(char *cmd)
+{
+       volatile ulong i = 10000000;
+
+       /* issue hard reset to the reset command register */
+       out_8((volatile char *)(cpld_base + KATANA_CPLD_RST_CMD),
+               KATANA_CPLD_RST_CMD_HR);
+
+       while (i-- > 0) ;
+       panic("restart failed\n");
+}
+
+static void
+katana_halt(void)
+{
+       while (1) ;
+       /* NOTREACHED */
+}
+
+static void
+katana_power_off(void)
+{
+       katana_halt();
+       /* NOTREACHED */
+}
+
+static int
+katana_show_cpuinfo(struct seq_file *m)
+{
+       seq_printf(m, "vendor\t\t: Artesyn Communication Products, LLC\n");
+
+       seq_printf(m, "board\t\t: ");
+
+       switch (katana_id) {
+       case KATANA_ID_3750:
+               seq_printf(m, "Katana 3750\n");
+               break;
+
+       case KATANA_ID_750I:
+               seq_printf(m, "Katana 750i\n");
+               break;
+
+       case KATANA_ID_752I:
+               seq_printf(m, "Katana 752i\n");
+               break;
+
+       default:
+               seq_printf(m, "Unknown\n");
+               break;
+       }
+
+       seq_printf(m, "product ID\t: 0x%x\n",
+                  in_8((volatile char *)(cpld_base + KATANA_CPLD_PRODUCT_ID)));
+       seq_printf(m, "hardware rev\t: 0x%x\n",
+                  in_8((volatile char *)(cpld_base+KATANA_CPLD_HARDWARE_VER)));
+       seq_printf(m, "PLD rev\t\t: 0x%x\n",
+                  in_8((volatile char *)(cpld_base + KATANA_CPLD_PLD_VER)));
+       seq_printf(m, "PLB freq\t: %ldMhz\n", katana_bus_freq() / 1000000);
+       seq_printf(m, "PCI\t\t: %sMonarch\n", katana_is_monarch()? "" : "Non-");
+
+       return 0;
+}
+
+static void __init
+katana_calibrate_decr(void)
+{
+       ulong freq;
+
+       freq = katana_bus_freq() / 4;
+
+       printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n",
+              freq / 1000000, freq % 1000000);
+
+       tb_ticks_per_jiffy = freq / HZ;
+       tb_to_us = mulhwu_scale_factor(freq, 1000000);
+
+       return;
+}
+
+unsigned long __init
+katana_find_end_of_memory(void)
+{
+       return mv64x60_get_mem_size(KATANA_BRIDGE_REG_BASE,
+               MV64x60_TYPE_MV64360);
+}
+
+static inline void
+katana_set_bat(void)
+{
+       mb();
+       mtspr(DBAT2U, 0xf0001ffe);
+       mtspr(DBAT2L, 0xf000002a);
+       mb();
+
+       return;
+}
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) && defined(CONFIG_SERIAL_MPSC_CONSOLE)
+static void __init
+katana_map_io(void)
+{
+       io_block_mapping(0xf8100000, 0xf8100000, 0x00020000, _PAGE_IO);
+}
+#endif
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+             unsigned long r6, unsigned long r7)
+{
+       parse_bootinfo(find_bootinfo());
+
+       isa_mem_base = 0;
+
+       ppc_md.setup_arch = katana_setup_arch;
+       ppc_md.show_cpuinfo = katana_show_cpuinfo;
+       ppc_md.init_IRQ = mv64360_init_irq;
+       ppc_md.get_irq = mv64360_get_irq;
+       ppc_md.restart = katana_restart;
+       ppc_md.power_off = katana_power_off;
+       ppc_md.halt = katana_halt;
+       ppc_md.find_end_of_memory = katana_find_end_of_memory;
+       ppc_md.calibrate_decr = katana_calibrate_decr;
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) && defined(CONFIG_SERIAL_MPSC_CONSOLE)
+       ppc_md.setup_io_mappings = katana_map_io;
+       ppc_md.progress = mv64x60_mpsc_progress;
+       mv64x60_progress_init(KATANA_BRIDGE_REG_BASE);
+#endif
+
+#if defined(CONFIG_SERIAL_MPSC) || defined(CONFIG_MV643XX_ETH)
+       platform_notify = katana_platform_notify;
+#endif
+
+       katana_set_bat(); /* Need for katana_find_end_of_memory and progress */
+       return;
+}
diff --git a/arch/ppc/platforms/katana.h b/arch/ppc/platforms/katana.h
new file mode 100644 (file)
index 0000000..3b8ff9a
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * arch/ppc/platforms/katana.h
+ *
+ * Definitions for Artesyn Katana750i/3750 board.
+ *
+ * Tim Montgomery <timm@artesyncp.com>
+ *
+ * Based on code done by Rabeeh Khoury - rabeeh@galileo.co.il
+ * Based on code done by Mark A. Greer <mgreer@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+/*
+ * The MV64360 has 2 PCI buses each with 1 window from the CPU bus to
+ * PCI I/O space and 4 windows from the CPU bus to PCI MEM space.
+ * We'll only use one PCI MEM window on each PCI bus.
+ *
+ * This is the CPU physical memory map (windows must be at least 1MB and start
+ * on a boundary that is a multiple of the window size):
+ *
+ *    0xff800000-0xffffffff      - Boot window
+ *    0xf8400000-0xf85fffff      - Internal SRAM
+ *    0xf8200000-0xf823ffff      - CPLD
+ *    0xf8100000-0xf810ffff      - MV64360 Registers
+ *    0xf8000000-0xf80fffff      - PLCC socket
+ *    0xf0000000-0xf01fffff     - Consistent memory pool
+ *    0xe8000000-0xefffffff      - soldered flash
+ *    0xc0000000-0xc0ffffff      - PCI I/O
+ *    0x80000000-0xbfffffff      - PCI MEM
+ */
+
+#ifndef __PPC_PLATFORMS_KATANA_H
+#define __PPC_PLATFORMS_KATANA_H
+
+/* CPU Physical Memory Map setup. */
+#define KATANA_BOOT_WINDOW_BASE                        0xff800000
+#define KATANA_INTERNAL_SRAM_BASE              0xf8400000
+#define KATANA_CPLD_BASE                       0xf8200000
+#define KATANA_BRIDGE_REG_BASE                 0xf8100000
+#define KATANA_SOCKET_BASE                     0xf8000000
+#define KATANA_SOLDERED_FLASH_BASE             0xe8000000
+
+#define KATANA_BOOT_WINDOW_SIZE_ACTUAL         0x00800000 /* 8MB */
+#define KATANA_CPLD_SIZE_ACTUAL                        0x00020000 /* 128KB */
+#define KATANA_SOCKETED_FLASH_SIZE_ACTUAL      0x00080000 /* 512KB */
+#define KATANA_SOLDERED_FLASH_SIZE_ACTUAL      0x02000000 /* 32MB */
+
+#define KATANA_BOOT_WINDOW_SIZE                max(MV64360_WINDOW_SIZE_MIN,    \
+               KATANA_BOOT_WINDOW_SIZE_ACTUAL)
+#define KATANA_CPLD_SIZE               max(MV64360_WINDOW_SIZE_MIN,    \
+               KATANA_CPLD_SIZE_ACTUAL)
+#define KATANA_SOCKETED_FLASH_SIZE     max(MV64360_WINDOW_SIZE_MIN,    \
+               KATANA_SOCKETED_FLASH_SIZE_ACTUAL)
+#define KATANA_SOLDERED_FLASH_SIZE     max(MV64360_WINDOW_SIZE_MIN,    \
+               KATANA_SOLDERED_FLASH_SIZE_ACTUAL)
+
+#define KATANA_PCI1_MEM_START_PROC_ADDR         0x80000000
+#define KATANA_PCI1_MEM_START_PCI_HI_ADDR       0x00000000
+#define KATANA_PCI1_MEM_START_PCI_LO_ADDR       0x80000000
+#define KATANA_PCI1_MEM_SIZE                    0x40000000
+#define KATANA_PCI1_IO_START_PROC_ADDR          0xc0000000
+#define KATANA_PCI1_IO_START_PCI_ADDR           0x00000000
+#define KATANA_PCI1_IO_SIZE                     0x01000000
+
+/* Board-specific IRQ info */
+#define  KATANA_PCI_INTA_IRQ_3750              64+8
+#define  KATANA_PCI_INTB_IRQ_3750              64+9
+#define  KATANA_PCI_INTC_IRQ_3750              64+10
+
+#define  KATANA_PCI_INTA_IRQ_750i              64+8
+#define  KATANA_PCI_INTB_IRQ_750i              64+9
+#define  KATANA_PCI_INTC_IRQ_750i              64+10
+#define  KATANA_PCI_INTD_IRQ_750i              64+14
+
+#define KATANA_CPLD_RST_EVENT                  0x00000000
+#define KATANA_CPLD_RST_CMD                    0x00001000
+#define KATANA_CPLD_PCI_ERR_INT_EN             0x00002000
+#define KATANA_CPLD_PCI_ERR_INT_PEND           0x00003000
+#define KATANA_CPLD_PRODUCT_ID                 0x00004000
+#define KATANA_CPLD_EREADY                     0x00005000
+
+#define KATANA_CPLD_HARDWARE_VER               0x00007000
+#define KATANA_CPLD_PLD_VER                    0x00008000
+#define KATANA_CPLD_BD_CFG_0                   0x00009000
+#define KATANA_CPLD_BD_CFG_1                   0x0000a000
+#define KATANA_CPLD_BD_CFG_3                   0x0000c000
+#define KATANA_CPLD_LED                                0x0000d000
+#define KATANA_CPLD_RESET_OUT                  0x0000e000
+
+#define KATANA_CPLD_RST_EVENT_INITACT          0x80
+#define KATANA_CPLD_RST_EVENT_SW               0x40
+#define KATANA_CPLD_RST_EVENT_WD               0x20
+#define KATANA_CPLD_RST_EVENT_COPS             0x10
+#define KATANA_CPLD_RST_EVENT_COPH             0x08
+#define KATANA_CPLD_RST_EVENT_CPCI             0x02
+#define KATANA_CPLD_RST_EVENT_FP               0x01
+
+#define KATANA_CPLD_RST_CMD_SCL                        0x80
+#define KATANA_CPLD_RST_CMD_SDA                        0x40
+#define KATANA_CPLD_RST_CMD_I2C                        0x10
+#define KATANA_CPLD_RST_CMD_FR                 0x08
+#define KATANA_CPLD_RST_CMD_SR                 0x04
+#define KATANA_CPLD_RST_CMD_HR                 0x01
+
+#define KATANA_CPLD_BD_CFG_0_SYSCLK_MASK       0xc0
+#define KATANA_CPLD_BD_CFG_0_SYSCLK_200                0x00
+#define KATANA_CPLD_BD_CFG_0_SYSCLK_166                0x80
+#define KATANA_CPLD_BD_CFG_0_SYSCLK_133                0xc0
+#define KATANA_CPLD_BD_CFG_0_SYSCLK_100                0x40
+
+#define KATANA_CPLD_BD_CFG_1_FL_BANK_MASK      0x03
+#define KATANA_CPLD_BD_CFG_1_FL_BANK_16MB      0x00
+#define KATANA_CPLD_BD_CFG_1_FL_BANK_32MB      0x01
+#define KATANA_CPLD_BD_CFG_1_FL_BANK_64MB      0x02
+#define KATANA_CPLD_BD_CFG_1_FL_BANK_128MB     0x03
+
+#define KATANA_CPLD_BD_CFG_1_FL_NUM_BANKS_MASK 0x04
+#define KATANA_CPLD_BD_CFG_1_FL_NUM_BANKS_ONE  0x00
+#define KATANA_CPLD_BD_CFG_1_FL_NUM_BANKS_TWO  0x04
+
+#define KATANA_CPLD_BD_CFG_3_MONARCH           0x04
+
+#define KATANA_CPLD_RESET_OUT_PORTSEL          0x80
+#define KATANA_CPLD_RESET_OUT_WD               0x20
+#define KATANA_CPLD_RESET_OUT_COPH             0x08
+#define KATANA_CPLD_RESET_OUT_PCI_RST_PCI      0x02
+#define KATANA_CPLD_RESET_OUT_PCI_RST_FP       0x01
+
+#define KATANA_MBOX_RESET_REQUEST              0xC83A
+#define KATANA_MBOX_RESET_ACK                  0xE430
+#define KATANA_MBOX_RESET_DONE                 0x32E5
+
+#define HSL_PLD_BASE                           0x00010000
+#define HSL_PLD_J4SGA_REG_OFF                  0
+#define HSL_PLD_J4GA_REG_OFF                   1
+#define HSL_PLD_J2GA_REG_OFF                   2
+#define GA_MASK                                        0x1f
+#define HSL_PLD_SIZE                           0x1000
+#define K3750_GPP_GEO_ADDR_PINS                        0xf8000000
+#define K3750_GPP_GEO_ADDR_SHIFT               27
+
+#define K3750_GPP_EVENT_PROC_0                 (1 << 21)
+#define K3750_GPP_EVENT_PROC_1_2               (1 << 2)
+
+#define PCI_VENDOR_ID_ARTESYN                  0x1223
+#define PCI_DEVICE_ID_KATANA_3750_PROC0                0x0041
+#define PCI_DEVICE_ID_KATANA_3750_PROC1                0x0042
+#define PCI_DEVICE_ID_KATANA_3750_PROC2                0x0043
+
+#define COPROC_MEM_FUNCTION                    0
+#define COPROC_MEM_BAR                         0
+#define COPROC_REGS_FUNCTION                   0
+#define COPROC_REGS_BAR                                4
+#define COPROC_FLASH_FUNCTION                  2
+#define COPROC_FLASH_BAR                       4
+
+#define KATANA_IPMB_LOCAL_I2C_ADDR             0x08
+
+#define        KATANA_DEFAULT_BAUD                     9600
+#define        KATANA_MPSC_CLK_SRC                     8         /* TCLK */
+#define        KATANA_MPSC_CLK_FREQ                    133333333 /* 133.3333... MHz */
+
+#define        KATANA_ETH0_PHY_ADDR                    12
+#define        KATANA_ETH1_PHY_ADDR                    11
+#define        KATANA_ETH2_PHY_ADDR                    4
+
+#define KATANA_PRODUCT_ID_3750                 0x01
+#define KATANA_PRODUCT_ID_750i                 0x02
+#define KATANA_PRODUCT_ID_752i                 0x04
+
+#define KATANA_ETH_TX_QUEUE_SIZE               800
+#define KATANA_ETH_RX_QUEUE_SIZE               400
+
+#define        KATANA_ETH_PORT_CONFIG_VALUE                    \
+       ETH_UNICAST_NORMAL_MODE                 |       \
+       ETH_DEFAULT_RX_QUEUE_0                  |       \
+       ETH_DEFAULT_RX_ARP_QUEUE_0              |       \
+       ETH_RECEIVE_BC_IF_NOT_IP_OR_ARP         |       \
+       ETH_RECEIVE_BC_IF_IP                    |       \
+       ETH_RECEIVE_BC_IF_ARP                   |       \
+       ETH_CAPTURE_TCP_FRAMES_DIS              |       \
+       ETH_CAPTURE_UDP_FRAMES_DIS              |       \
+       ETH_DEFAULT_RX_TCP_QUEUE_0              |       \
+       ETH_DEFAULT_RX_UDP_QUEUE_0              |       \
+       ETH_DEFAULT_RX_BPDU_QUEUE_0
+
+#define        KATANA_ETH_PORT_CONFIG_EXTEND_VALUE             \
+       ETH_SPAN_BPDU_PACKETS_AS_NORMAL         |       \
+       ETH_PARTITION_DISABLE
+
+#define        GT_ETH_IPG_INT_RX(value)                        \
+       ((value & 0x3fff) << 8)
+
+#define        KATANA_ETH_PORT_SDMA_CONFIG_VALUE               \
+       ETH_RX_BURST_SIZE_4_64BIT               |       \
+       GT_ETH_IPG_INT_RX(0)                    |       \
+       ETH_TX_BURST_SIZE_4_64BIT
+
+#define        KATANA_ETH_PORT_SERIAL_CONTROL_VALUE            \
+       ETH_FORCE_LINK_PASS                     |       \
+       ETH_ENABLE_AUTO_NEG_FOR_DUPLX           |       \
+       ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL      |       \
+       ETH_ADV_SYMMETRIC_FLOW_CTRL             |       \
+       ETH_FORCE_FC_MODE_NO_PAUSE_DIS_TX       |       \
+       ETH_FORCE_BP_MODE_NO_JAM                |       \
+       BIT9                                    |       \
+       ETH_DO_NOT_FORCE_LINK_FAIL              |       \
+       ETH_RETRANSMIT_16_ATTEMPTS              |       \
+       ETH_ENABLE_AUTO_NEG_SPEED_GMII          |       \
+       ETH_DTE_ADV_0                           |       \
+       ETH_DISABLE_AUTO_NEG_BYPASS             |       \
+       ETH_AUTO_NEG_NO_CHANGE                  |       \
+       ETH_MAX_RX_PACKET_9700BYTE              |       \
+       ETH_CLR_EXT_LOOPBACK                    |       \
+       ETH_SET_FULL_DUPLEX_MODE                |       \
+       ETH_ENABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX
+
+#ifndef __ASSEMBLY__
+
+typedef enum {
+       KATANA_ID_3750,
+       KATANA_ID_750I,
+       KATANA_ID_752I,
+       KATANA_ID_MAX
+} katana_id_t;
+
+#endif
+
+#endif                         /* __PPC_PLATFORMS_KATANA_H */
diff --git a/arch/ppc/platforms/pmac_cache.S b/arch/ppc/platforms/pmac_cache.S
new file mode 100644 (file)
index 0000000..fa15663
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * This file contains low-level cache management functions
+ * used for sleep and CPU speed changes on Apple machines.
+ * (In fact the only thing that is Apple-specific is that we assume
+ * that we can read from ROM at physical address 0xfff00000.)
+ *
+ *    Copyright (C) 2004 Paul Mackerras (paulus@samba.org) and
+ *                       Benjamin 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 <linux/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+#include <asm/cputable.h>
+
+/*
+ * Flush and disable all data caches (dL1, L2, L3). This is used
+ * when going to sleep, when doing a PMU based cpufreq transition,
+ * or when "offlining" a CPU on SMP machines. This code is over
+ * paranoid, but I've had enough issues with various CPU revs and
+ * bugs that I decided it was worth beeing over cautious
+ */
+
+_GLOBAL(flush_disable_caches)
+BEGIN_FTR_SECTION
+       b       flush_disable_745x
+END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450)
+BEGIN_FTR_SECTION
+       b       flush_disable_75x
+END_FTR_SECTION_IFSET(CPU_FTR_L2CR)
+       b       __flush_disable_L1
+
+/* This is the code for G3 and 74[01]0 */
+flush_disable_75x:
+       mflr    r10
+
+       /* Turn off EE and DR in MSR */
+       mfmsr   r11
+       rlwinm  r0,r11,0,~MSR_EE
+       rlwinm  r0,r0,0,~MSR_DR
+       sync
+       mtmsr   r0
+       isync
+
+       /* Stop DST streams */
+BEGIN_FTR_SECTION
+       DSSALL
+       sync
+END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
+
+       /* Stop DPM */
+       mfspr   r8,SPRN_HID0            /* Save HID0 in r8 */
+       rlwinm  r4,r8,0,12,10           /* Turn off HID0[DPM] */
+       sync
+       mtspr   SPRN_HID0,r4            /* Disable DPM */
+       sync
+
+       /* disp-flush L1 */
+       li      r4,0x4000
+       mtctr   r4
+       lis     r4,0xfff0
+1:     lwzx    r0,r0,r4
+       addi    r4,r4,32
+       bdnz    1b
+       sync
+       isync
+
+       /* disable / invalidate / enable L1 data */
+       mfspr   r3,SPRN_HID0
+       rlwinm  r0,r0,0,~HID0_DCE
+       mtspr   SPRN_HID0,r3
+       sync
+       isync
+       ori     r3,r3,HID0_DCE|HID0_DCI
+       sync
+       isync
+       mtspr   SPRN_HID0,r3
+       xori    r3,r3,HID0_DCI
+       mtspr   SPRN_HID0,r3
+       sync
+
+       /* Get the current enable bit of the L2CR into r4 */
+       mfspr   r5,L2CR
+       /* Set to data-only (pre-745x bit) */
+       oris    r3,r5,L2CR_L2DO@h
+       b       2f
+       /* When disabling L2, code must be in L1 */
+       .balign 32
+1:     mtspr   L2CR,r3
+3:     sync
+       isync
+       b       1f
+2:     b       3f
+3:     sync
+       isync
+       b       1b
+1:     /* disp-flush L2. The interesting thing here is that the L2 can be
+        * up to 2Mb ... so using the ROM, we'll end up wrapping back to memory
+        * but that is probbaly fine. We disp-flush over 4Mb to be safe
+        */
+       lis     r4,2
+       mtctr   r4
+       lis     r4,0xfff0
+1:     lwzx    r0,r0,r4
+       addi    r4,r4,32
+       bdnz    1b
+       sync
+       isync
+       /* now disable L2 */
+       rlwinm  r5,r5,0,~L2CR_L2E
+       b       2f
+       /* When disabling L2, code must be in L1 */
+       .balign 32
+1:     mtspr   L2CR,r5
+3:     sync
+       isync
+       b       1f
+2:     b       3f
+3:     sync
+       isync
+       b       1b
+1:     sync
+       isync
+       /* Invalidate L2. This is pre-745x, we clear the L2I bit ourselves */
+       oris    r4,r5,L2CR_L2I@h
+       mtspr   L2CR,r4
+       sync
+       isync
+       xoris   r4,r4,L2CR_L2I@h
+       sync
+       mtspr   L2CR,r4
+       sync
+
+       /* now disable the L1 data cache */
+       mfspr   r0,HID0
+       rlwinm  r0,r0,0,~HID0_DCE
+       mtspr   HID0,r0
+       sync
+       isync
+
+       /* Restore HID0[DPM] to whatever it was before */
+       sync
+       mtspr   SPRN_HID0,r8
+       sync
+
+       /* restore DR and EE */
+       sync
+       mtmsr   r11
+       isync
+
+       mtlr    r10
+       blr
+
+/* This code is for 745x processors */
+flush_disable_745x:
+       /* Turn off EE and DR in MSR */
+       mfmsr   r11
+       rlwinm  r0,r11,0,~MSR_EE
+       rlwinm  r0,r0,0,~MSR_DR
+       sync
+       mtmsr   r0
+       isync
+
+       /* Stop prefetch streams */
+       DSSALL
+       sync
+
+       /* Disable L2 prefetching */
+       mfspr   r0,SPRN_MSSCR0
+       rlwinm  r0,r0,0,0,29
+       mtspr   SPRN_MSSCR0,r0
+       sync
+       isync
+       lis     r4,0
+       dcbf    0,r4
+       dcbf    0,r4
+       dcbf    0,r4
+       dcbf    0,r4
+       dcbf    0,r4
+       dcbf    0,r4
+       dcbf    0,r4
+       dcbf    0,r4
+
+       /* Due to a bug with the HW flush on some CPU revs, we occasionally
+        * experience data corruption. I'm adding a displacement flush along
+        * with a dcbf loop over a few Mb to "help". The problem isn't totally
+        * fixed by this in theory, but at least, in practice, I couldn't reproduce
+        * it even with a big hammer...
+        */
+
+        lis     r4,0x0002
+        mtctr   r4
+       li      r4,0
+1:
+        lwzx    r0,r0,r4
+        addi    r4,r4,32                /* Go to start of next cache line */
+        bdnz    1b
+        isync
+
+        /* Now, flush the first 4MB of memory */
+        lis     r4,0x0002
+        mtctr   r4
+       li      r4,0
+        sync
+1:
+        dcbf    0,r4
+        addi    r4,r4,32                /* Go to start of next cache line */
+        bdnz    1b
+
+       /* Flush and disable the L1 data cache */
+       mfspr   r6,SPRN_LDSTCR
+       lis     r3,0xfff0       /* read from ROM for displacement flush */
+       li      r4,0xfe         /* start with only way 0 unlocked */
+       li      r5,128          /* 128 lines in each way */
+1:     mtctr   r5
+       rlwimi  r6,r4,0,24,31
+       mtspr   SPRN_LDSTCR,r6
+       sync
+       isync
+2:     lwz     r0,0(r3)        /* touch each cache line */
+       addi    r3,r3,32
+       bdnz    2b
+       rlwinm  r4,r4,1,24,30   /* move on to the next way */
+       ori     r4,r4,1
+       cmpwi   r4,0xff         /* all done? */
+       bne     1b
+       /* now unlock the L1 data cache */
+       li      r4,0
+       rlwimi  r6,r4,0,24,31
+       sync
+       mtspr   SPRN_LDSTCR,r6
+       sync
+       isync
+
+       /* Flush the L2 cache using the hardware assist */
+       mfspr   r3,L2CR
+       cmpwi   r3,0            /* check if it is enabled first */
+       bge     4f
+       oris    r0,r3,(L2CR_L2IO_745x|L2CR_L2DO_745x)@h
+       b       2f
+       /* When disabling/locking L2, code must be in L1 */
+       .balign 32
+1:     mtspr   L2CR,r0         /* lock the L2 cache */
+3:     sync
+       isync
+       b       1f
+2:     b       3f
+3:     sync
+       isync
+       b       1b
+1:     sync
+       isync
+       ori     r0,r3,L2CR_L2HWF_745x
+       sync
+       mtspr   L2CR,r0         /* set the hardware flush bit */
+3:     mfspr   r0,L2CR         /* wait for it to go to 0 */
+       andi.   r0,r0,L2CR_L2HWF_745x
+       bne     3b
+       sync
+       rlwinm  r3,r3,0,~L2CR_L2E
+       b       2f
+       /* When disabling L2, code must be in L1 */
+       .balign 32
+1:     mtspr   L2CR,r3         /* disable the L2 cache */
+3:     sync
+       isync
+       b       1f
+2:     b       3f
+3:     sync
+       isync
+       b       1b
+1:     sync
+       isync
+       oris    r4,r3,L2CR_L2I@h
+       mtspr   L2CR,r4
+       sync
+       isync
+1:     mfspr   r4,L2CR
+       andis.  r0,r4,L2CR_L2I@h
+       bne     1b
+       sync
+
+BEGIN_FTR_SECTION
+       /* Flush the L3 cache using the hardware assist */
+4:     mfspr   r3,L3CR
+       cmpwi   r3,0            /* check if it is enabled */
+       bge     6f
+       oris    r0,r3,L3CR_L3IO@h
+       ori     r0,r0,L3CR_L3DO
+       sync
+       mtspr   L3CR,r0         /* lock the L3 cache */
+       sync
+       isync
+       ori     r0,r0,L3CR_L3HWF
+       sync
+       mtspr   L3CR,r0         /* set the hardware flush bit */
+5:     mfspr   r0,L3CR         /* wait for it to go to zero */
+       andi.   r0,r0,L3CR_L3HWF
+       bne     5b
+       rlwinm  r3,r3,0,~L3CR_L3E
+       sync
+       mtspr   L3CR,r3         /* disable the L3 cache */
+       sync
+       ori     r4,r3,L3CR_L3I
+       mtspr   SPRN_L3CR,r4
+1:     mfspr   r4,SPRN_L3CR
+       andi.   r0,r4,L3CR_L3I
+       bne     1b
+       sync
+END_FTR_SECTION_IFSET(CPU_FTR_L3CR)
+
+6:     mfspr   r0,HID0         /* now disable the L1 data cache */
+       rlwinm  r0,r0,0,~HID0_DCE
+       mtspr   HID0,r0
+       sync
+       isync
+       mtmsr   r11             /* restore DR and EE */
+       isync
+       blr
diff --git a/arch/ppc/syslib/gen550.h b/arch/ppc/syslib/gen550.h
new file mode 100644 (file)
index 0000000..039d249
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * arch/ppc/syslib/gen550.h
+ *
+ * gen550 prototypes
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 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.
+ */
+
+extern void gen550_progress(char *, unsigned short);
+extern void gen550_init(int, struct uart_port *);
+extern void gen550_kgdb_map_scc(void);
diff --git a/arch/ppc/syslib/ibm440sp_common.c b/arch/ppc/syslib/ibm440sp_common.c
new file mode 100644 (file)
index 0000000..417d4cf
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * arch/ppc/syslib/ibm440sp_common.c
+ *
+ * PPC440SP system library
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ * Copyright 2002-2005 MontaVista Software Inc.
+ *
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2003, 2004 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 <linux/config.h>
+#include <linux/types.h>
+#include <linux/serial.h>
+
+#include <asm/param.h>
+#include <asm/ibm44x.h>
+#include <asm/mmu.h>
+#include <asm/machdep.h>
+#include <asm/time.h>
+#include <asm/ppc4xx_pic.h>
+
+/*
+ * Read the 440SP memory controller to get size of system memory.
+ */
+unsigned long __init ibm440sp_find_end_of_memory(void)
+{
+       u32 i;
+       u32 mem_size = 0;
+
+       /* Read two bank sizes and sum */
+       for (i=0; i<2; i++)
+               switch (mfdcr(DCRN_MQ0_BS0BAS + i) & MQ0_CONFIG_SIZE_MASK) {
+                       case MQ0_CONFIG_SIZE_8M:
+                               mem_size += PPC44x_MEM_SIZE_8M;
+                               break;
+                       case MQ0_CONFIG_SIZE_16M:
+                               mem_size += PPC44x_MEM_SIZE_16M;
+                               break;
+                       case MQ0_CONFIG_SIZE_32M:
+                               mem_size += PPC44x_MEM_SIZE_32M;
+                               break;
+                       case MQ0_CONFIG_SIZE_64M:
+                               mem_size += PPC44x_MEM_SIZE_64M;
+                               break;
+                       case MQ0_CONFIG_SIZE_128M:
+                               mem_size += PPC44x_MEM_SIZE_128M;
+                               break;
+                       case MQ0_CONFIG_SIZE_256M:
+                               mem_size += PPC44x_MEM_SIZE_256M;
+                               break;
+                       case MQ0_CONFIG_SIZE_512M:
+                               mem_size += PPC44x_MEM_SIZE_512M;
+                               break;
+                       case MQ0_CONFIG_SIZE_1G:
+                               mem_size += PPC44x_MEM_SIZE_1G;
+                               break;
+                       case MQ0_CONFIG_SIZE_2G:
+                               mem_size += PPC44x_MEM_SIZE_2G;
+                               break;
+                       default:
+                               break;
+               }
+       return mem_size;
+}
diff --git a/arch/ppc/syslib/ibm440sp_common.h b/arch/ppc/syslib/ibm440sp_common.h
new file mode 100644 (file)
index 0000000..a21a990
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * arch/ppc/syslib/ibm440sp_common.h
+ *
+ * PPC440SP system library
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ * Copyright 2004-2005 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.
+ *
+ */
+#ifdef __KERNEL__
+#ifndef __PPC_SYSLIB_IBM440SP_COMMON_H
+#define __PPC_SYSLIB_IBM440SP_COMMON_H
+
+#ifndef __ASSEMBLY__
+
+extern unsigned long __init ibm440sp_find_end_of_memory(void);
+
+#endif /* __ASSEMBLY__ */
+#endif /* __PPC_SYSLIB_IBM440SP_COMMON_H */
+#endif /* __KERNEL__ */
diff --git a/arch/ppc/syslib/mv64x60_dbg.c b/arch/ppc/syslib/mv64x60_dbg.c
new file mode 100644 (file)
index 0000000..2927c7a
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * arch/ppc/syslib/mv64x60_dbg.c
+ *
+ * KGDB and progress routines for the Marvell/Galileo MV64x60 (Discovery).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2003 (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.
+ */
+
+/*
+ *****************************************************************************
+ *
+ *     Low-level MPSC/UART I/O routines
+ *
+ *****************************************************************************
+ */
+
+
+#include <linux/config.h>
+#include <linux/irq.h>
+#include <asm/delay.h>
+#include <asm/mv64x60.h>
+
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG)
+
+#define        MPSC_CHR_1      0x000c
+#define        MPSC_CHR_2      0x0010
+
+static struct mv64x60_handle   mv64x60_dbg_bh;
+
+void
+mv64x60_progress_init(u32 base)
+{
+       mv64x60_dbg_bh.v_base = base;
+       return;
+}
+
+static void
+mv64x60_polled_putc(int chan, char c)
+{
+       u32     offset;
+
+       if (chan == 0)
+               offset = 0x8000;
+       else
+               offset = 0x9000;
+
+       mv64x60_write(&mv64x60_dbg_bh, offset + MPSC_CHR_1, (u32)c);
+       mv64x60_write(&mv64x60_dbg_bh, offset + MPSC_CHR_2, 0x200);
+       udelay(2000);
+}
+
+void
+mv64x60_mpsc_progress(char *s, unsigned short hex)
+{
+       volatile char   c;
+
+       mv64x60_polled_putc(0, '\r');
+
+       while ((c = *s++) != 0)
+               mv64x60_polled_putc(0, c);
+
+       mv64x60_polled_putc(0, '\n');
+       mv64x60_polled_putc(0, '\r');
+
+       return;
+}
+#endif /* CONFIG_SERIAL_TEXT_DEBUG */
+
+
+#if defined(CONFIG_KGDB)
+
+#if defined(CONFIG_KGDB_TTYS0)
+#define KGDB_PORT 0
+#elif defined(CONFIG_KGDB_TTYS1)
+#define KGDB_PORT 1
+#else
+#error "Invalid kgdb_tty port"
+#endif
+
+void
+putDebugChar(unsigned char c)
+{
+       mv64x60_polled_putc(KGDB_PORT, (char)c);
+}
+
+int
+getDebugChar(void)
+{
+       unsigned char   c;
+
+       while (!mv64x60_polled_getc(KGDB_PORT, &c));
+       return (int)c;
+}
+
+void
+putDebugString(char* str)
+{
+       while (*str != '\0') {
+               putDebugChar(*str);
+               str++;
+       }
+       putDebugChar('\r');
+       return;
+}
+
+void
+kgdb_interruptible(int enable)
+{
+}
+
+void
+kgdb_map_scc(void)
+{
+       if (ppc_md.early_serial_map)
+               ppc_md.early_serial_map();
+}
+#endif /* CONFIG_KGDB */
diff --git a/arch/ppc/syslib/mv64x60_win.c b/arch/ppc/syslib/mv64x60_win.c
new file mode 100644 (file)
index 0000000..b6f0f5d
--- /dev/null
@@ -0,0 +1,1168 @@
+/*
+ * arch/ppc/syslib/mv64x60_win.c
+ *
+ * Tables with info on how to manipulate the 32 & 64 bit windows on the
+ * various types of Marvell bridge chips.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/bootmem.h>
+#include <linux/mv643xx.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <asm/delay.h>
+#include <asm/mv64x60.h>
+
+
+/*
+ *****************************************************************************
+ *
+ *     Tables describing how to set up windows on each type of bridge
+ *
+ *****************************************************************************
+ */
+struct mv64x60_32bit_window
+       gt64260_32bit_windows[MV64x60_32BIT_WIN_COUNT] __initdata = {
+       /* CPU->MEM Windows */
+       [MV64x60_CPU2MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2MEM_0_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2MEM_1_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2MEM_2_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2MEM_3_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->Device Windows */
+       [MV64x60_CPU2DEV_0_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_0_BASE,
+               .size_reg               = MV64x60_CPU2DEV_0_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2DEV_1_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_1_BASE,
+               .size_reg               = MV64x60_CPU2DEV_1_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2DEV_2_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_2_BASE,
+               .size_reg               = MV64x60_CPU2DEV_2_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2DEV_3_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_3_BASE,
+               .size_reg               = MV64x60_CPU2DEV_3_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->Boot Window */
+       [MV64x60_CPU2BOOT_WIN] = {
+               .base_reg               = MV64x60_CPU2BOOT_0_BASE,
+               .size_reg               = MV64x60_CPU2BOOT_0_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 0 Windows */
+       [MV64x60_CPU2PCI0_IO_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_IO_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_IO_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_0_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_1_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_2_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_3_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 1 Windows */
+       [MV64x60_CPU2PCI1_IO_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_IO_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_IO_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_0_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_1_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_2_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_3_SIZE,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->SRAM Window (64260 has no integrated SRAM) */
+       /* CPU->PCI 0 Remap I/O Window */
+       [MV64x60_CPU2PCI0_IO_REMAP_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_IO_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 1 Remap I/O Window */
+       [MV64x60_CPU2PCI1_IO_REMAP_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_IO_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU Memory Protection Windows */
+       [MV64x60_CPU_PROT_0_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_0,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_0,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_PROT_1_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_1,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_1,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_PROT_2_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_2,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_2,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_PROT_3_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_3,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_3,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU Snoop Windows */
+       [MV64x60_CPU_SNOOP_0_WIN] = {
+               .base_reg               = GT64260_CPU_SNOOP_BASE_0,
+               .size_reg               = GT64260_CPU_SNOOP_SIZE_0,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_SNOOP_1_WIN] = {
+               .base_reg               = GT64260_CPU_SNOOP_BASE_1,
+               .size_reg               = GT64260_CPU_SNOOP_SIZE_1,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_SNOOP_2_WIN] = {
+               .base_reg               = GT64260_CPU_SNOOP_BASE_2,
+               .size_reg               = GT64260_CPU_SNOOP_SIZE_2,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU_SNOOP_3_WIN] = {
+               .base_reg               = GT64260_CPU_SNOOP_BASE_3,
+               .size_reg               = GT64260_CPU_SNOOP_SIZE_3,
+               .base_bits              = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 0->System Memory Remap Windows */
+       [MV64x60_PCI02MEM_REMAP_0_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_0_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_1_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_2_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_3_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       /* PCI 1->System Memory Remap Windows */
+       [MV64x60_PCI12MEM_REMAP_0_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_0_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_1_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_2_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_3_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       /* ENET->SRAM Window (64260 doesn't have separate windows) */
+       /* MPSC->SRAM Window (64260 doesn't have separate windows) */
+       /* IDMA->SRAM Window (64260 doesn't have separate windows) */
+};
+
+struct mv64x60_64bit_window
+       gt64260_64bit_windows[MV64x60_64BIT_WIN_COUNT] __initdata = {
+       /* CPU->PCI 0 MEM Remap Windows */
+       [MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_0_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_0_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_1_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_1_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_2_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_2_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_3_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_3_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 1 MEM Remap Windows */
+       [MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_0_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_0_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_1_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_1_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_2_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_2_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_3_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_3_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 12,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 0->MEM Access Control Windows */
+       [MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_0_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_0_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_0_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_1_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_1_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_1_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_2_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_2_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_2_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_3_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_3_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_3_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 1->MEM Access Control Windows */
+       [MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_0_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_0_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_0_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_1_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_1_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_1_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_2_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_2_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_2_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_3_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_3_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_3_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 0->MEM Snoop Windows */
+       [MV64x60_PCI02MEM_SNOOP_0_WIN] = {
+               .base_hi_reg            = GT64260_PCI0_SNOOP_0_BASE_HI,
+               .base_lo_reg            = GT64260_PCI0_SNOOP_0_BASE_LO,
+               .size_reg               = GT64260_PCI0_SNOOP_0_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_SNOOP_1_WIN] = {
+               .base_hi_reg            = GT64260_PCI0_SNOOP_1_BASE_HI,
+               .base_lo_reg            = GT64260_PCI0_SNOOP_1_BASE_LO,
+               .size_reg               = GT64260_PCI0_SNOOP_1_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_SNOOP_2_WIN] = {
+               .base_hi_reg            = GT64260_PCI0_SNOOP_2_BASE_HI,
+               .base_lo_reg            = GT64260_PCI0_SNOOP_2_BASE_LO,
+               .size_reg               = GT64260_PCI0_SNOOP_2_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_SNOOP_3_WIN] = {
+               .base_hi_reg            = GT64260_PCI0_SNOOP_3_BASE_HI,
+               .base_lo_reg            = GT64260_PCI0_SNOOP_3_BASE_LO,
+               .size_reg               = GT64260_PCI0_SNOOP_3_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 1->MEM Snoop Windows */
+       [MV64x60_PCI12MEM_SNOOP_0_WIN] = {
+               .base_hi_reg            = GT64260_PCI1_SNOOP_0_BASE_HI,
+               .base_lo_reg            = GT64260_PCI1_SNOOP_0_BASE_LO,
+               .size_reg               = GT64260_PCI1_SNOOP_0_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_SNOOP_1_WIN] = {
+               .base_hi_reg            = GT64260_PCI1_SNOOP_1_BASE_HI,
+               .base_lo_reg            = GT64260_PCI1_SNOOP_1_BASE_LO,
+               .size_reg               = GT64260_PCI1_SNOOP_1_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_SNOOP_2_WIN] = {
+               .base_hi_reg            = GT64260_PCI1_SNOOP_2_BASE_HI,
+               .base_lo_reg            = GT64260_PCI1_SNOOP_2_BASE_LO,
+               .size_reg               = GT64260_PCI1_SNOOP_2_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_SNOOP_3_WIN] = {
+               .base_hi_reg            = GT64260_PCI1_SNOOP_3_BASE_HI,
+               .base_lo_reg            = GT64260_PCI1_SNOOP_3_BASE_LO,
+               .size_reg               = GT64260_PCI1_SNOOP_3_SIZE,
+               .base_lo_bits           = 12,
+               .size_bits              = 12,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+};
+
+struct mv64x60_32bit_window
+       mv64360_32bit_windows[MV64x60_32BIT_WIN_COUNT] __initdata = {
+       /* CPU->MEM Windows */
+       [MV64x60_CPU2MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2MEM_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 0 },
+       [MV64x60_CPU2MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2MEM_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 1 },
+       [MV64x60_CPU2MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2MEM_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 2 },
+       [MV64x60_CPU2MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2MEM_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 3 },
+       /* CPU->Device Windows */
+       [MV64x60_CPU2DEV_0_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_0_BASE,
+               .size_reg               = MV64x60_CPU2DEV_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 4 },
+       [MV64x60_CPU2DEV_1_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_1_BASE,
+               .size_reg               = MV64x60_CPU2DEV_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 5 },
+       [MV64x60_CPU2DEV_2_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_2_BASE,
+               .size_reg               = MV64x60_CPU2DEV_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 6 },
+       [MV64x60_CPU2DEV_3_WIN] = {
+               .base_reg               = MV64x60_CPU2DEV_3_BASE,
+               .size_reg               = MV64x60_CPU2DEV_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 7 },
+       /* CPU->Boot Window */
+       [MV64x60_CPU2BOOT_WIN] = {
+               .base_reg               = MV64x60_CPU2BOOT_0_BASE,
+               .size_reg               = MV64x60_CPU2BOOT_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 8 },
+       /* CPU->PCI 0 Windows */
+       [MV64x60_CPU2PCI0_IO_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_IO_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_IO_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 9 },
+       [MV64x60_CPU2PCI0_MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 10 },
+       [MV64x60_CPU2PCI0_MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 11 },
+       [MV64x60_CPU2PCI0_MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 12 },
+       [MV64x60_CPU2PCI0_MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2PCI0_MEM_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 13 },
+       /* CPU->PCI 1 Windows */
+       [MV64x60_CPU2PCI1_IO_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_IO_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_IO_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 14 },
+       [MV64x60_CPU2PCI1_MEM_0_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_0_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 15 },
+       [MV64x60_CPU2PCI1_MEM_1_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_1_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 16 },
+       [MV64x60_CPU2PCI1_MEM_2_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_2_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 17 },
+       [MV64x60_CPU2PCI1_MEM_3_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_MEM_3_BASE,
+               .size_reg               = MV64x60_CPU2PCI1_MEM_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 18 },
+       /* CPU->SRAM Window */
+       [MV64x60_CPU2SRAM_WIN] = {
+               .base_reg               = MV64360_CPU2SRAM_BASE,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUWIN_ENAB | 19 },
+       /* CPU->PCI 0 Remap I/O Window */
+       [MV64x60_CPU2PCI0_IO_REMAP_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI0_IO_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 1 Remap I/O Window */
+       [MV64x60_CPU2PCI1_IO_REMAP_WIN] = {
+               .base_reg               = MV64x60_CPU2PCI1_IO_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU Memory Protection Windows */
+       [MV64x60_CPU_PROT_0_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_0,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_0,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUPROT_ENAB | 31 },
+       [MV64x60_CPU_PROT_1_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_1,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_1,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUPROT_ENAB | 31 },
+       [MV64x60_CPU_PROT_2_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_2,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_2,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUPROT_ENAB | 31 },
+       [MV64x60_CPU_PROT_3_WIN] = {
+               .base_reg               = MV64x60_CPU_PROT_BASE_3,
+               .size_reg               = MV64x60_CPU_PROT_SIZE_3,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = MV64x60_EXTRA_CPUPROT_ENAB | 31 },
+       /* CPU Snoop Windows -- don't exist on 64360 */
+       /* PCI 0->System Memory Remap Windows */
+       [MV64x60_PCI02MEM_REMAP_0_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_0_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_1_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_2_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI02MEM_REMAP_3_WIN] = {
+               .base_reg               = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       /* PCI 1->System Memory Remap Windows */
+       [MV64x60_PCI12MEM_REMAP_0_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_0_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_1_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_2_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       [MV64x60_PCI12MEM_REMAP_3_WIN] = {
+               .base_reg               = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+               .size_reg               = 0,
+               .base_bits              = 20,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = 0 },
+       /* ENET->System Memory Windows */
+       [MV64x60_ENET2MEM_0_WIN] = {
+               .base_reg               = MV64360_ENET2MEM_0_BASE,
+               .size_reg               = MV64360_ENET2MEM_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_ENET_ENAB | 0 },
+       [MV64x60_ENET2MEM_1_WIN] = {
+               .base_reg               = MV64360_ENET2MEM_1_BASE,
+               .size_reg               = MV64360_ENET2MEM_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_ENET_ENAB | 1 },
+       [MV64x60_ENET2MEM_2_WIN] = {
+               .base_reg               = MV64360_ENET2MEM_2_BASE,
+               .size_reg               = MV64360_ENET2MEM_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_ENET_ENAB | 2 },
+       [MV64x60_ENET2MEM_3_WIN] = {
+               .base_reg               = MV64360_ENET2MEM_3_BASE,
+               .size_reg               = MV64360_ENET2MEM_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_ENET_ENAB | 3 },
+       [MV64x60_ENET2MEM_4_WIN] = {
+               .base_reg               = MV64360_ENET2MEM_4_BASE,
+               .size_reg               = MV64360_ENET2MEM_4_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_ENET_ENAB | 4 },
+       [MV64x60_ENET2MEM_5_WIN] = {
+               .base_reg               = MV64360_ENET2MEM_5_BASE,
+               .size_reg               = MV64360_ENET2MEM_5_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_ENET_ENAB | 5 },
+       /* MPSC->System Memory Windows */
+       [MV64x60_MPSC2MEM_0_WIN] = {
+               .base_reg               = MV64360_MPSC2MEM_0_BASE,
+               .size_reg               = MV64360_MPSC2MEM_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_MPSC_ENAB | 0 },
+       [MV64x60_MPSC2MEM_1_WIN] = {
+               .base_reg               = MV64360_MPSC2MEM_1_BASE,
+               .size_reg               = MV64360_MPSC2MEM_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_MPSC_ENAB | 1 },
+       [MV64x60_MPSC2MEM_2_WIN] = {
+               .base_reg               = MV64360_MPSC2MEM_2_BASE,
+               .size_reg               = MV64360_MPSC2MEM_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_MPSC_ENAB | 2 },
+       [MV64x60_MPSC2MEM_3_WIN] = {
+               .base_reg               = MV64360_MPSC2MEM_3_BASE,
+               .size_reg               = MV64360_MPSC2MEM_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_MPSC_ENAB | 3 },
+       /* IDMA->System Memory Windows */
+       [MV64x60_IDMA2MEM_0_WIN] = {
+               .base_reg               = MV64360_IDMA2MEM_0_BASE,
+               .size_reg               = MV64360_IDMA2MEM_0_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_IDMA_ENAB | 0 },
+       [MV64x60_IDMA2MEM_1_WIN] = {
+               .base_reg               = MV64360_IDMA2MEM_1_BASE,
+               .size_reg               = MV64360_IDMA2MEM_1_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_IDMA_ENAB | 1 },
+       [MV64x60_IDMA2MEM_2_WIN] = {
+               .base_reg               = MV64360_IDMA2MEM_2_BASE,
+               .size_reg               = MV64360_IDMA2MEM_2_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_IDMA_ENAB | 2 },
+       [MV64x60_IDMA2MEM_3_WIN] = {
+               .base_reg               = MV64360_IDMA2MEM_3_BASE,
+               .size_reg               = MV64360_IDMA2MEM_3_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_IDMA_ENAB | 3 },
+       [MV64x60_IDMA2MEM_4_WIN] = {
+               .base_reg               = MV64360_IDMA2MEM_4_BASE,
+               .size_reg               = MV64360_IDMA2MEM_4_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_IDMA_ENAB | 4 },
+       [MV64x60_IDMA2MEM_5_WIN] = {
+               .base_reg               = MV64360_IDMA2MEM_5_BASE,
+               .size_reg               = MV64360_IDMA2MEM_5_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_IDMA_ENAB | 5 },
+       [MV64x60_IDMA2MEM_6_WIN] = {
+               .base_reg               = MV64360_IDMA2MEM_6_BASE,
+               .size_reg               = MV64360_IDMA2MEM_6_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_IDMA_ENAB | 6 },
+       [MV64x60_IDMA2MEM_7_WIN] = {
+               .base_reg               = MV64360_IDMA2MEM_7_BASE,
+               .size_reg               = MV64360_IDMA2MEM_7_SIZE,
+               .base_bits              = 16,
+               .size_bits              = 16,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_IDMA_ENAB | 7 },
+};
+
+struct mv64x60_64bit_window
+       mv64360_64bit_windows[MV64x60_64BIT_WIN_COUNT] __initdata = {
+       /* CPU->PCI 0 MEM Remap Windows */
+       [MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_0_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_0_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_1_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_1_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_2_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_2_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI0_MEM_3_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI0_MEM_3_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* CPU->PCI 1 MEM Remap Windows */
+       [MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_0_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_0_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_1_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_1_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_2_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_2_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       [MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = {
+               .base_hi_reg            = MV64x60_CPU2PCI1_MEM_3_REMAP_HI,
+               .base_lo_reg            = MV64x60_CPU2PCI1_MEM_3_REMAP_LO,
+               .size_reg               = 0,
+               .base_lo_bits           = 16,
+               .size_bits              = 0,
+               .get_from_field         = mv64x60_shift_left,
+               .map_to_field           = mv64x60_shift_right,
+               .extra                  = 0 },
+       /* PCI 0->MEM Access Control Windows */
+       [MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_0_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_0_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_0_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_1_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_1_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_1_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_2_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_2_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_2_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+       [MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = {
+               .base_hi_reg            = MV64x60_PCI0_ACC_CNTL_3_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI0_ACC_CNTL_3_BASE_LO,
+               .size_reg               = MV64x60_PCI0_ACC_CNTL_3_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+       /* PCI 1->MEM Access Control Windows */
+       [MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_0_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_0_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_0_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_1_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_1_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_1_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_2_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_2_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_2_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+       [MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = {
+               .base_hi_reg            = MV64x60_PCI1_ACC_CNTL_3_BASE_HI,
+               .base_lo_reg            = MV64x60_PCI1_ACC_CNTL_3_BASE_LO,
+               .size_reg               = MV64x60_PCI1_ACC_CNTL_3_SIZE,
+               .base_lo_bits           = 20,
+               .size_bits              = 20,
+               .get_from_field         = mv64x60_mask,
+               .map_to_field           = mv64x60_mask,
+               .extra                  = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+       /* PCI 0->MEM Snoop Windows -- don't exist on 64360 */
+       /* PCI 1->MEM Snoop Windows -- don't exist on 64360 */
+};
diff --git a/arch/ppc/syslib/ppc403_pic.c b/arch/ppc/syslib/ppc403_pic.c
new file mode 100644 (file)
index 0000000..06cb0af
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *
+ *    Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
+ *
+ *    Module name: ppc403_pic.c
+ *
+ *    Description:
+ *      Interrupt controller driver for PowerPC 403-based processors.
+ */
+
+/*
+ * The PowerPC 403 cores' Asynchronous Interrupt Controller (AIC) has
+ * 32 possible interrupts, a majority of which are not implemented on
+ * all cores. There are six configurable, external interrupt pins and
+ * there are eight internal interrupts for the on-chip serial port
+ * (SPU), DMA controller, and JTAG controller.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/stddef.h>
+
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/ppc4xx_pic.h>
+
+/* Function Prototypes */
+
+static void ppc403_aic_enable(unsigned int irq);
+static void ppc403_aic_disable(unsigned int irq);
+static void ppc403_aic_disable_and_ack(unsigned int irq);
+
+static struct hw_interrupt_type ppc403_aic = {
+       "403GC AIC",
+       NULL,
+       NULL,
+       ppc403_aic_enable,
+       ppc403_aic_disable,
+       ppc403_aic_disable_and_ack,
+       0
+};
+
+int
+ppc403_pic_get_irq(struct pt_regs *regs)
+{
+       int irq;
+       unsigned long bits;
+
+       /*
+        * Only report the status of those interrupts that are actually
+        * enabled.
+        */
+
+       bits = mfdcr(DCRN_EXISR) & mfdcr(DCRN_EXIER);
+
+       /*
+        * Walk through the interrupts from highest priority to lowest, and
+        * report the first pending interrupt found.
+        * We want PPC, not C bit numbering, so just subtract the ffs()
+        * result from 32.
+        */
+       irq = 32 - ffs(bits);
+
+       if (irq == NR_AIC_IRQS)
+               irq = -1;
+
+       return (irq);
+}
+
+static void
+ppc403_aic_enable(unsigned int irq)
+{
+       int bit, word;
+
+       bit = irq & 0x1f;
+       word = irq >> 5;
+
+       ppc_cached_irq_mask[word] |= (1 << (31 - bit));
+       mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
+}
+
+static void
+ppc403_aic_disable(unsigned int irq)
+{
+       int bit, word;
+
+       bit = irq & 0x1f;
+       word = irq >> 5;
+
+       ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
+       mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
+}
+
+static void
+ppc403_aic_disable_and_ack(unsigned int irq)
+{
+       int bit, word;
+
+       bit = irq & 0x1f;
+       word = irq >> 5;
+
+       ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
+       mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
+       mtdcr(DCRN_EXISR, (1 << (31 - bit)));
+}
+
+void __init
+ppc4xx_pic_init(void)
+{
+       int i;
+
+       /*
+        * Disable all external interrupts until they are
+        * explicity requested.
+        */
+       ppc_cached_irq_mask[0] = 0;
+
+       mtdcr(DCRN_EXIER, ppc_cached_irq_mask[0]);
+
+       ppc_md.get_irq = ppc403_pic_get_irq;
+
+       for (i = 0; i < NR_IRQS; i++)
+               irq_desc[i].handler = &ppc403_aic;
+}
diff --git a/arch/ppc/syslib/ppc_sys.c b/arch/ppc/syslib/ppc_sys.c
new file mode 100644 (file)
index 0000000..8792023
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * arch/ppc/syslib/ppc_sys.c
+ *
+ * PPC System library functions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor 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 <asm/ppc_sys.h>
+
+int (*ppc_sys_device_fixup) (struct platform_device * pdev);
+
+static int ppc_sys_inited;
+
+void __init identify_ppc_sys_by_id(u32 id)
+{
+       unsigned int i = 0;
+       while (1) {
+               if ((ppc_sys_specs[i].mask & id) == ppc_sys_specs[i].value)
+                       break;
+               i++;
+       }
+
+       cur_ppc_sys_spec = &ppc_sys_specs[i];
+
+       return;
+}
+
+void __init identify_ppc_sys_by_name(char *name)
+{
+       /* TODO */
+       return;
+}
+
+/* Update all memory resources by paddr, call before platform_device_register */
+void __init
+ppc_sys_fixup_mem_resource(struct platform_device *pdev, phys_addr_t paddr)
+{
+       int i;
+       for (i = 0; i < pdev->num_resources; i++) {
+               struct resource *r = &pdev->resource[i];
+               if ((r->flags & IORESOURCE_MEM) == IORESOURCE_MEM) {
+                       r->start += paddr;
+                       r->end += paddr;
+               }
+       }
+}
+
+/* Get platform_data pointer out of platform device, call before platform_device_register */
+void *__init ppc_sys_get_pdata(enum ppc_sys_devices dev)
+{
+       return ppc_sys_platform_devices[dev].dev.platform_data;
+}
+
+void ppc_sys_device_remove(enum ppc_sys_devices dev)
+{
+       unsigned int i;
+
+       if (ppc_sys_inited) {
+               platform_device_unregister(&ppc_sys_platform_devices[dev]);
+       } else {
+               if (cur_ppc_sys_spec == NULL)
+                       return;
+               for (i = 0; i < cur_ppc_sys_spec->num_devices; i++)
+                       if (cur_ppc_sys_spec->device_list[i] == dev)
+                               cur_ppc_sys_spec->device_list[i] = -1;
+       }
+}
+
+static int __init ppc_sys_init(void)
+{
+       unsigned int i, dev_id, ret = 0;
+
+       BUG_ON(cur_ppc_sys_spec == NULL);
+
+       for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) {
+               dev_id = cur_ppc_sys_spec->device_list[i];
+               if (dev_id != -1) {
+                       if (ppc_sys_device_fixup != NULL)
+                               ppc_sys_device_fixup(&ppc_sys_platform_devices
+                                                    [dev_id]);
+                       if (platform_device_register
+                           (&ppc_sys_platform_devices[dev_id])) {
+                               ret = 1;
+                               printk(KERN_ERR
+                                      "unable to register device %d\n",
+                                      dev_id);
+                       }
+               }
+       }
+
+       ppc_sys_inited = 1;
+       return ret;
+}
+
+subsys_initcall(ppc_sys_init);
diff --git a/arch/ppc/syslib/xilinx_pic.c b/arch/ppc/syslib/xilinx_pic.c
new file mode 100644 (file)
index 0000000..89b5ac1
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * arch/ppc/syslib/xilinx_pic.c
+ *
+ * Interrupt controller driver for Xilinx Virtex-II Pro.
+ *
+ * Author: MontaVista Software, Inc.
+ *         source@mvista.com
+ *
+ * 2002-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 <linux/init.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include <asm/xparameters.h>
+#include <asm/ibm4xx.h>
+
+/* No one else should require these constants, so define them locally here. */
+#define ISR 0                  /* Interrupt Status Register */
+#define IPR 1                  /* Interrupt Pending Register */
+#define IER 2                  /* Interrupt Enable Register */
+#define IAR 3                  /* Interrupt Acknowledge Register */
+#define SIE 4                  /* Set Interrupt Enable bits */
+#define CIE 5                  /* Clear Interrupt Enable bits */
+#define IVR 6                  /* Interrupt Vector Register */
+#define MER 7                  /* Master Enable Register */
+
+#if XPAR_XINTC_USE_DCR == 0
+static volatile u32 *intc;
+#define intc_out_be32(addr, mask)     out_be32((addr), (mask))
+#define intc_in_be32(addr)            in_be32((addr))
+#else
+#define intc    XPAR_INTC_0_BASEADDR
+#define intc_out_be32(addr, mask)     mtdcr((addr), (mask))
+#define intc_in_be32(addr)            mfdcr((addr))
+#endif
+
+/* Global Variables */
+struct hw_interrupt_type *ppc4xx_pic;
+
+static void
+xilinx_intc_enable(unsigned int irq)
+{
+       unsigned long mask = (0x00000001 << (irq & 31));
+       pr_debug("enable: %d\n", irq);
+       intc_out_be32(intc + SIE, mask);
+}
+
+static void
+xilinx_intc_disable(unsigned int irq)
+{
+       unsigned long mask = (0x00000001 << (irq & 31));
+       pr_debug("disable: %d\n", irq);
+       intc_out_be32(intc + CIE, mask);
+}
+
+static void
+xilinx_intc_disable_and_ack(unsigned int irq)
+{
+       unsigned long mask = (0x00000001 << (irq & 31));
+       pr_debug("disable_and_ack: %d\n", irq);
+       intc_out_be32(intc + CIE, mask);
+       if (!(irq_desc[irq].status & IRQ_LEVEL))
+               intc_out_be32(intc + IAR, mask);        /* ack edge triggered intr */
+}
+
+static void
+xilinx_intc_end(unsigned int irq)
+{
+       unsigned long mask = (0x00000001 << (irq & 31));
+
+       pr_debug("end: %d\n", irq);
+       if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+               intc_out_be32(intc + SIE, mask);
+               /* ack level sensitive intr */
+               if (irq_desc[irq].status & IRQ_LEVEL)
+                       intc_out_be32(intc + IAR, mask);
+       }
+}
+
+static struct hw_interrupt_type xilinx_intc = {
+       "Xilinx Interrupt Controller",
+       NULL,
+       NULL,
+       xilinx_intc_enable,
+       xilinx_intc_disable,
+       xilinx_intc_disable_and_ack,
+       xilinx_intc_end,
+       0
+};
+
+int
+xilinx_pic_get_irq(struct pt_regs *regs)
+{
+       int irq;
+
+       /*
+        * NOTE: This function is the one that needs to be improved in
+        * order to handle multiple interrupt controllers.  It currently
+        * is hardcoded to check for interrupts only on the first INTC.
+        */
+
+       irq = intc_in_be32(intc + IVR);
+       if (irq != -1)
+               irq = irq;
+
+       pr_debug("get_irq: %d\n", irq);
+
+       return (irq);
+}
+
+void __init
+ppc4xx_pic_init(void)
+{
+#if XPAR_XINTC_USE_DCR == 0
+       intc = ioremap(XPAR_INTC_0_BASEADDR, 32);
+
+       printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX mapped to 0x%08lX\n",
+              (unsigned long) XPAR_INTC_0_BASEADDR, (unsigned long) intc);
+#else
+       printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX (DCR)\n",
+              (unsigned long) XPAR_INTC_0_BASEADDR);
+#endif
+
+       /*
+        * Disable all external interrupts until they are
+        * explicity requested.
+        */
+       intc_out_be32(intc + IER, 0);
+
+       /* Acknowledge any pending interrupts just in case. */
+       intc_out_be32(intc + IAR, ~(u32) 0);
+
+       /* Turn on the Master Enable. */
+       intc_out_be32(intc + MER, 0x3UL);
+
+       ppc4xx_pic = &xilinx_intc;
+       ppc_md.get_irq = xilinx_pic_get_irq;
+}
diff --git a/arch/ppc64/configs/maple_defconfig b/arch/ppc64/configs/maple_defconfig
new file mode 100644 (file)
index 0000000..65571a0
--- /dev/null
@@ -0,0 +1,921 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.9
+# Wed Oct 20 15:39:14 2004
+#
+CONFIG_64BIT=y
+CONFIG_MMU=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_HAVE_DEC_LOCK=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_COMPAT=y
+CONFIG_FRAME_POINTER=y
+CONFIG_FORCE_MAX_ZONEORDER=13
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+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=17
+# CONFIG_HOTPLUG is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_KALLSYMS_EXTRA_PASS 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
+CONFIG_SHMEM=y
+# CONFIG_TINY_SHMEM is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+CONFIG_STOP_MACHINE=y
+CONFIG_SYSVIPC_COMPAT=y
+
+#
+# Platform support
+#
+# CONFIG_PPC_ISERIES is not set
+CONFIG_PPC_MULTIPLATFORM=y
+# CONFIG_PPC_PSERIES is not set
+# CONFIG_PPC_PMAC is not set
+CONFIG_PPC_MAPLE=y
+CONFIG_PPC=y
+CONFIG_PPC64=y
+CONFIG_PPC_OF=y
+# CONFIG_ALTIVEC is not set
+CONFIG_U3_DART=y
+CONFIG_MPIC_BROKEN_U3=y
+CONFIG_BOOTX_TEXT=y
+CONFIG_POWER4_ONLY=y
+CONFIG_IOMMU_VMERGE=y
+CONFIG_SMP=y
+# CONFIG_IRQ_ALL_CPUS is not set
+CONFIG_NR_CPUS=2
+# CONFIG_SCHED_SMT is not set
+# CONFIG_PREEMPT is not set
+
+#
+# General setup
+#
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+CONFIG_PCI_LEGACY_PROC=y
+CONFIG_PCI_NAMES=y
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# 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_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 is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+# CONFIG_BLK_DEV_INITRD 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_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+CONFIG_IDE_TASK_IOCTL=y
+CONFIG_IDE_TASKFILE_IO=y
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_GENERIC=y
+# 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=y
+# CONFIG_IDEDMA_ONLYDISK is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+CONFIG_BLK_DEV_AMD74XX=y
+# CONFIG_BLK_DEV_CMD64X is not set
+# 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 is not set
+# 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 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_IDE_ARM is not set
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_IVB is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI 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
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP 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_INET_TUNNEL 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_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# 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=y
+# CONFIG_AMD8111E_NAPI 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 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_VIA_RHINE is not set
+# CONFIG_VIA_VELOCITY is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+CONFIG_E1000=y
+# CONFIG_E1000_NAPI 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 is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1600
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=1200
+# 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=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_PMACZILOG is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG 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_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
+# CONFIG_I2C_ALGOPCA 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=y
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_I810 is not set
+# CONFIG_I2C_ISA is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_PARPORT_LIGHT 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
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Hardware Sensors Chip support
+#
+# CONFIG_I2C_SENSOR is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1031 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_LM77 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_MAX1619 is not set
+# CONFIG_SENSORS_SMSC47M1 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 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 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
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_BANDWIDTH is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_SPLIT_ISO=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_UHCI_HCD=y
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_BLUETOOTH_TTY is not set
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_STORAGE is not set
+
+#
+# USB Human Interface Devices (HID)
+#
+CONFIG_USB_HID=y
+CONFIG_USB_HIDINPUT=y
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+# CONFIG_USB_AIPTEK is not set
+# CONFIG_USB_WACOM is not set
+# CONFIG_USB_KBTAB is not set
+# CONFIG_USB_POWERMATE is not set
+# CONFIG_USB_MTOUCH is not set
+# CONFIG_USB_EGALAX is not set
+# CONFIG_USB_XPAD is not set
+# CONFIG_USB_ATI_REMOTE is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+
+#
+# USB Multimedia devices
+#
+# CONFIG_USB_DABUSB is not set
+
+#
+# Video4Linux support is needed for USB Multimedia device support
+#
+
+#
+# USB Network adaptors
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+CONFIG_USB_PEGASUS=y
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+
+#
+# USB port drivers
+#
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=y
+# CONFIG_USB_SERIAL_CONSOLE is not set
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_BELKIN is not set
+# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
+# CONFIG_USB_SERIAL_EMPEG is not set
+# CONFIG_USB_SERIAL_FTDI_SIO is not set
+# CONFIG_USB_SERIAL_VISOR is not set
+# CONFIG_USB_SERIAL_IPAQ is not set
+# CONFIG_USB_SERIAL_IR is not set
+# CONFIG_USB_SERIAL_EDGEPORT is not set
+# CONFIG_USB_SERIAL_EDGEPORT_TI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
+CONFIG_USB_SERIAL_KEYSPAN=y
+CONFIG_USB_SERIAL_KEYSPAN_MPR=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19=y
+CONFIG_USB_SERIAL_KEYSPAN_USA18X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
+# CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
+# CONFIG_USB_SERIAL_MCT_U232 is not set
+# CONFIG_USB_SERIAL_PL2303 is not set
+# CONFIG_USB_SERIAL_SAFE is not set
+# CONFIG_USB_SERIAL_CYBERJACK is not set
+# CONFIG_USB_SERIAL_XIRCOM is not set
+# CONFIG_USB_SERIAL_OMNINET is not set
+CONFIG_USB_EZUSB=y
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_TIGL is not set
+# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGETSERVO is not set
+# CONFIG_USB_TEST is not set
+
+#
+# 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=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# 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=y
+# CONFIG_DEVPTS_FS_SECURITY is not set
+CONFIG_TMPFS=y
+# CONFIG_HUGETLBFS 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_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=y
+# 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_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# CONFIG_RPCSEC_GSS_SPKM3 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=y
+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_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=y
+CONFIG_NLS_DEFAULT="utf-8"
+# 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_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# 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=y
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_SLAB=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+# CONFIG_DEBUG_INFO is not set
+CONFIG_DEBUG_STACKOVERFLOW=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUGGER=y
+CONFIG_XMON=y
+CONFIG_XMON_DEFAULT=y
+# CONFIG_PPCDBG is not set
+# CONFIG_IRQSTACKS is not set
+# CONFIG_SCHEDSTATS is not set
+
+#
+# Security options
+#
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_WP512 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=y
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
diff --git a/arch/ppc64/kernel/iSeries_smp.c b/arch/ppc64/kernel/iSeries_smp.c
new file mode 100644 (file)
index 0000000..a5835eb
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * SMP support for iSeries machines.
+ *
+ * Dave Engebretsen, Peter Bergner, and
+ * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com
+ *
+ * Plus various changes from other IBM teams...
+ *
+ *      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.
+ */
+
+#undef DEBUG
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/cache.h>
+#include <linux/err.h>
+#include <linux/sysdev.h>
+#include <linux/cpu.h>
+
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/naca.h>
+#include <asm/paca.h>
+#include <asm/iSeries/LparData.h>
+#include <asm/iSeries/HvCall.h>
+#include <asm/iSeries/HvCallCfg.h>
+#include <asm/time.h>
+#include <asm/ppcdebug.h>
+#include <asm/machdep.h>
+#include <asm/cputable.h>
+#include <asm/system.h>
+
+static unsigned long iSeries_smp_message[NR_CPUS];
+
+void iSeries_smp_message_recv( struct pt_regs * regs )
+{
+       int cpu = smp_processor_id();
+       int msg;
+
+       if ( num_online_cpus() < 2 )
+               return;
+
+       for ( msg = 0; msg < 4; ++msg )
+               if ( test_and_clear_bit( msg, &iSeries_smp_message[cpu] ) )
+                       smp_message_recv( msg, regs );
+}
+
+static inline void smp_iSeries_do_message(int cpu, int msg)
+{
+       set_bit(msg, &iSeries_smp_message[cpu]);
+       HvCall_sendIPI(&(paca[cpu]));
+}
+
+static void smp_iSeries_message_pass(int target, int msg)
+{
+       int i;
+
+       if (target < NR_CPUS)
+               smp_iSeries_do_message(target, msg);
+       else {
+               for_each_online_cpu(i) {
+                       if (target == MSG_ALL_BUT_SELF
+                           && i == smp_processor_id())
+                               continue;
+                       smp_iSeries_do_message(i, msg);
+               }
+       }
+}
+
+static int smp_iSeries_numProcs(void)
+{
+       unsigned np, i;
+
+       np = 0;
+        for (i=0; i < NR_CPUS; ++i) {
+                if (paca[i].lppaca.xDynProcStatus < 2) {
+                       cpu_set(i, cpu_possible_map);
+                       cpu_set(i, cpu_present_map);
+                       cpu_set(i, cpu_sibling_map[i]);
+                        ++np;
+                }
+        }
+       return np;
+}
+
+static int smp_iSeries_probe(void)
+{
+       unsigned i;
+       unsigned np = 0;
+
+       for (i=0; i < NR_CPUS; ++i) {
+               if (paca[i].lppaca.xDynProcStatus < 2) {
+                       /*paca[i].active = 1;*/
+                       ++np;
+               }
+       }
+
+       return np;
+}
+
+static void smp_iSeries_kick_cpu(int nr)
+{
+       BUG_ON(nr < 0 || nr >= NR_CPUS);
+
+       /* Verify that our partition has a processor nr */
+       if (paca[nr].lppaca.xDynProcStatus >= 2)
+               return;
+
+       /* The processor is currently spinning, waiting
+        * for the cpu_start field to become non-zero
+        * After we set cpu_start, the processor will
+        * continue on to secondary_start in iSeries_head.S
+        */
+       paca[nr].cpu_start = 1;
+}
+
+static void __devinit smp_iSeries_setup_cpu(int nr)
+{
+}
+
+static struct smp_ops_t iSeries_smp_ops = {
+       .message_pass = smp_iSeries_message_pass,
+       .probe        = smp_iSeries_probe,
+       .kick_cpu     = smp_iSeries_kick_cpu,
+       .setup_cpu    = smp_iSeries_setup_cpu,
+};
+
+/* This is called very early. */
+void __init smp_init_iSeries(void)
+{
+       smp_ops = &iSeries_smp_ops;
+       systemcfg->processorCount       = smp_iSeries_numProcs();
+}
+
diff --git a/arch/ppc64/kernel/kprobes.c b/arch/ppc64/kernel/kprobes.c
new file mode 100644 (file)
index 0000000..522e774
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ *  Kernel Probes (KProbes)
+ *  arch/ppc64/kernel/kprobes.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ *
+ * 2002-Oct    Created by Vamsi Krishna S <vamsi_krishna@in.ibm.com> Kernel
+ *             Probes initial implementation ( includes contributions from
+ *             Rusty Russell).
+ * 2004-July   Suparna Bhattacharya <suparna@in.ibm.com> added jumper probes
+ *             interface to access function arguments.
+ * 2004-Nov    Ananth N Mavinakayanahalli <ananth@in.ibm.com> kprobes port
+ *             for PPC64
+ */
+
+#include <linux/config.h>
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+#include <linux/spinlock.h>
+#include <linux/preempt.h>
+#include <asm/kdebug.h>
+#include <asm/sstep.h>
+
+/* kprobe_status settings */
+#define KPROBE_HIT_ACTIVE      0x00000001
+#define KPROBE_HIT_SS          0x00000002
+
+static struct kprobe *current_kprobe;
+static unsigned long kprobe_status, kprobe_saved_msr;
+static struct pt_regs jprobe_saved_regs;
+
+int arch_prepare_kprobe(struct kprobe *p)
+{
+       kprobe_opcode_t insn = *p->addr;
+
+       if (IS_MTMSRD(insn) || IS_RFID(insn))
+               /* cannot put bp on RFID/MTMSRD */
+               return 1;
+       return 0;
+}
+
+void arch_copy_kprobe(struct kprobe *p)
+{
+       memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+}
+
+void arch_remove_kprobe(struct kprobe *p)
+{
+}
+
+static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
+{
+       *p->addr = p->opcode;
+       regs->nip = (unsigned long)p->addr;
+}
+
+static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+       regs->msr |= MSR_SE;
+       regs->nip = (unsigned long)&p->ainsn.insn;
+}
+
+static inline int kprobe_handler(struct pt_regs *regs)
+{
+       struct kprobe *p;
+       int ret = 0;
+       unsigned int *addr = (unsigned int *)regs->nip;
+
+       /* We're in an interrupt, but this is clear and BUG()-safe. */
+       preempt_disable();
+
+       /* Check we're not actually recursing */
+       if (kprobe_running()) {
+               /* We *are* holding lock here, so this is safe.
+                  Disarm the probe we just hit, and ignore it. */
+               p = get_kprobe(addr);
+               if (p) {
+                       disarm_kprobe(p, regs);
+                       ret = 1;
+               } else {
+                       p = current_kprobe;
+                       if (p->break_handler && p->break_handler(p, regs)) {
+                               goto ss_probe;
+                       }
+               }
+               /* If it's not ours, can't be delete race, (we hold lock). */
+               goto no_kprobe;
+       }
+
+       lock_kprobes();
+       p = get_kprobe(addr);
+       if (!p) {
+               unlock_kprobes();
+#if 0
+               if (*addr != BREAKPOINT_INSTRUCTION) {
+                       /*
+                        * The breakpoint instruction was removed right
+                        * after we hit it.  Another cpu has removed
+                        * either a probepoint or a debugger breakpoint
+                        * at this address.  In either case, no further
+                        * handling of this interrupt is appropriate.
+                        */
+                       ret = 1;
+               }
+#endif
+               /* Not one of ours: let kernel handle it */
+               goto no_kprobe;
+       }
+
+       kprobe_status = KPROBE_HIT_ACTIVE;
+       current_kprobe = p;
+       kprobe_saved_msr = regs->msr;
+       if (p->pre_handler(p, regs)) {
+               /* handler has already set things up, so skip ss setup */
+               return 1;
+       }
+
+ss_probe:
+       prepare_singlestep(p, regs);
+       kprobe_status = KPROBE_HIT_SS;
+       return 1;
+
+no_kprobe:
+       preempt_enable_no_resched();
+       return ret;
+}
+
+/*
+ * Called after single-stepping.  p->addr is the address of the
+ * instruction whose first byte has been replaced by the "breakpoint"
+ * instruction.  To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction.  The address of this
+ * copy is p->ainsn.insn.
+ */
+static void resume_execution(struct kprobe *p, struct pt_regs *regs)
+{
+       int ret;
+
+       regs->nip = (unsigned long)p->addr;
+       ret = emulate_step(regs, p->ainsn.insn[0]);
+       if (ret == 0)
+               regs->nip = (unsigned long)p->addr + 4;
+
+       regs->msr &= ~MSR_SE;
+}
+
+static inline int post_kprobe_handler(struct pt_regs *regs)
+{
+       if (!kprobe_running())
+               return 0;
+
+       if (current_kprobe->post_handler)
+               current_kprobe->post_handler(current_kprobe, regs, 0);
+
+       resume_execution(current_kprobe, regs);
+       regs->msr |= kprobe_saved_msr;
+
+       unlock_kprobes();
+       preempt_enable_no_resched();
+
+       /*
+        * if somebody else is singlestepping across a probe point, msr
+        * will have SE set, in which case, continue the remaining processing
+        * of do_debug, as if this is not a probe hit.
+        */
+       if (regs->msr & MSR_SE)
+               return 0;
+
+       return 1;
+}
+
+/* Interrupts disabled, kprobe_lock held. */
+static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+       if (current_kprobe->fault_handler
+           && current_kprobe->fault_handler(current_kprobe, regs, trapnr))
+               return 1;
+
+       if (kprobe_status & KPROBE_HIT_SS) {
+               resume_execution(current_kprobe, regs);
+               regs->msr |= kprobe_saved_msr;
+
+               unlock_kprobes();
+               preempt_enable_no_resched();
+       }
+       return 0;
+}
+
+/*
+ * Wrapper routine to for handling exceptions.
+ */
+int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val,
+                            void *data)
+{
+       struct die_args *args = (struct die_args *)data;
+       switch (val) {
+       case DIE_IABR_MATCH:
+       case DIE_DABR_MATCH:
+       case DIE_BPT:
+               if (kprobe_handler(args->regs))
+                       return NOTIFY_STOP;
+               break;
+       case DIE_SSTEP:
+               if (post_kprobe_handler(args->regs))
+                       return NOTIFY_STOP;
+               break;
+       case DIE_GPF:
+       case DIE_PAGE_FAULT:
+               if (kprobe_running() &&
+                   kprobe_fault_handler(args->regs, args->trapnr))
+                       return NOTIFY_STOP;
+               break;
+       default:
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       struct jprobe *jp = container_of(p, struct jprobe, kp);
+
+       memcpy(&jprobe_saved_regs, regs, sizeof(struct pt_regs));
+
+       /* setup return addr to the jprobe handler routine */
+       regs->nip = (unsigned long)(((func_descr_t *)jp->entry)->entry);
+       regs->gpr[2] = (unsigned long)(((func_descr_t *)jp->entry)->toc);
+
+       return 1;
+}
+
+void jprobe_return(void)
+{
+       preempt_enable_no_resched();
+       asm volatile("trap" ::: "memory");
+}
+
+void jprobe_return_end(void)
+{
+};
+
+int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       /*
+        * FIXME - we should ideally be validating that we got here 'cos
+        * of the "trap" in jprobe_return() above, before restoring the
+        * saved regs...
+        */
+       memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs));
+       return 1;
+}
diff --git a/arch/ppc64/kernel/maple_pci.c b/arch/ppc64/kernel/maple_pci.c
new file mode 100644 (file)
index 0000000..bf3a3c0
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2004 Benjamin Herrenschmuidt (benh@kernel.crashing.org),
+ *                   IBM Corp.
+ *
+ * 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 DEBUG
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+
+#include <asm/sections.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+#include <asm/iommu.h>
+
+#include "pci.h"
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+static struct pci_controller *u3_agp, *u3_ht;
+
+static int __init fixup_one_level_bus_range(struct device_node *node, int higher)
+{
+       for (; node != 0;node = node->sibling) {
+               int * bus_range;
+               unsigned int *class_code;
+               int len;
+
+               /* For PCI<->PCI bridges or CardBus bridges, we go down */
+               class_code = (unsigned int *) get_property(node, "class-code", NULL);
+               if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
+                       (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
+                       continue;
+               bus_range = (int *) get_property(node, "bus-range", &len);
+               if (bus_range != NULL && len > 2 * sizeof(int)) {
+                       if (bus_range[1] > higher)
+                               higher = bus_range[1];
+               }
+               higher = fixup_one_level_bus_range(node->child, higher);
+       }
+       return higher;
+}
+
+/* This routine fixes the "bus-range" property of all bridges in the
+ * system since they tend to have their "last" member wrong on macs
+ *
+ * Note that the bus numbers manipulated here are OF bus numbers, they
+ * are not Linux bus numbers.
+ */
+static void __init fixup_bus_range(struct device_node *bridge)
+{
+       int * bus_range;
+       int len;
+
+       /* Lookup the "bus-range" property for the hose */
+       bus_range = (int *) get_property(bridge, "bus-range", &len);
+       if (bus_range == NULL || len < 2 * sizeof(int)) {
+               printk(KERN_WARNING "Can't get bus-range for %s\n",
+                              bridge->full_name);
+               return;
+       }
+       bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]);
+}
+
+
+#define U3_AGP_CFA0(devfn, off)        \
+       ((1 << (unsigned long)PCI_SLOT(dev_fn)) \
+       | (((unsigned long)PCI_FUNC(dev_fn)) << 8) \
+       | (((unsigned long)(off)) & 0xFCUL))
+
+#define U3_AGP_CFA1(bus, devfn, off)   \
+       ((((unsigned long)(bus)) << 16) \
+       |(((unsigned long)(devfn)) << 8) \
+       |(((unsigned long)(off)) & 0xFCUL) \
+       |1UL)
+
+static unsigned long u3_agp_cfg_access(struct pci_controller* hose,
+                                      u8 bus, u8 dev_fn, u8 offset)
+{
+       unsigned int caddr;
+
+       if (bus == hose->first_busno) {
+               if (dev_fn < (11 << 3))
+                       return 0;
+               caddr = U3_AGP_CFA0(dev_fn, offset);
+       } else
+               caddr = U3_AGP_CFA1(bus, dev_fn, offset);
+
+       /* Uninorth will return garbage if we don't read back the value ! */
+       do {
+               out_le32(hose->cfg_addr, caddr);
+       } while (in_le32(hose->cfg_addr) != caddr);
+
+       offset &= 0x07;
+       return ((unsigned long)hose->cfg_data) + offset;
+}
+
+static int u3_agp_read_config(struct pci_bus *bus, unsigned int devfn,
+                             int offset, int len, u32 *val)
+{
+       struct pci_controller *hose;
+       unsigned long addr;
+
+       hose = pci_bus_to_host(bus);
+       if (hose == NULL)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       addr = u3_agp_cfg_access(hose, bus->number, devfn, offset);
+       if (!addr)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       /*
+        * Note: the caller has already checked that offset is
+        * suitably aligned and that len is 1, 2 or 4.
+        */
+       switch (len) {
+       case 1:
+               *val = in_8((u8 *)addr);
+               break;
+       case 2:
+               *val = in_le16((u16 *)addr);
+               break;
+       default:
+               *val = in_le32((u32 *)addr);
+               break;
+       }
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int u3_agp_write_config(struct pci_bus *bus, unsigned int devfn,
+                              int offset, int len, u32 val)
+{
+       struct pci_controller *hose;
+       unsigned long addr;
+
+       hose = pci_bus_to_host(bus);
+       if (hose == NULL)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       addr = u3_agp_cfg_access(hose, bus->number, devfn, offset);
+       if (!addr)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       /*
+        * Note: the caller has already checked that offset is
+        * suitably aligned and that len is 1, 2 or 4.
+        */
+       switch (len) {
+       case 1:
+               out_8((u8 *)addr, val);
+               (void) in_8((u8 *)addr);
+               break;
+       case 2:
+               out_le16((u16 *)addr, val);
+               (void) in_le16((u16 *)addr);
+               break;
+       default:
+               out_le32((u32 *)addr, val);
+               (void) in_le32((u32 *)addr);
+               break;
+       }
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops u3_agp_pci_ops =
+{
+       u3_agp_read_config,
+       u3_agp_write_config
+};
+
+
+#define U3_HT_CFA0(devfn, off)         \
+               ((((unsigned long)devfn) << 8) | offset)
+#define U3_HT_CFA1(bus, devfn, off)    \
+               (U3_HT_CFA0(devfn, off) \
+               + (((unsigned long)bus) << 16) \
+               + 0x01000000UL)
+
+static unsigned long u3_ht_cfg_access(struct pci_controller* hose,
+                                     u8 bus, u8 devfn, u8 offset)
+{
+       if (bus == hose->first_busno) {
+               if (PCI_SLOT(devfn) == 0)
+                       return 0;
+               return ((unsigned long)hose->cfg_data) + U3_HT_CFA0(devfn, offset);
+       } else
+               return ((unsigned long)hose->cfg_data) + U3_HT_CFA1(bus, devfn, offset);
+}
+
+static int u3_ht_read_config(struct pci_bus *bus, unsigned int devfn,
+                            int offset, int len, u32 *val)
+{
+       struct pci_controller *hose;
+       unsigned long addr;
+
+       hose = pci_bus_to_host(bus);
+       if (hose == NULL)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       addr = u3_ht_cfg_access(hose, bus->number, devfn, offset);
+       if (!addr)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       /*
+        * Note: the caller has already checked that offset is
+        * suitably aligned and that len is 1, 2 or 4.
+        */
+       switch (len) {
+       case 1:
+               *val = in_8((u8 *)addr);
+               break;
+       case 2:
+               *val = in_le16((u16 *)addr);
+               break;
+       default:
+               *val = in_le32((u32 *)addr);
+               break;
+       }
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int u3_ht_write_config(struct pci_bus *bus, unsigned int devfn,
+                             int offset, int len, u32 val)
+{
+       struct pci_controller *hose;
+       unsigned long addr;
+
+       hose = pci_bus_to_host(bus);
+       if (hose == NULL)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       addr = u3_ht_cfg_access(hose, bus->number, devfn, offset);
+       if (!addr)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       /*
+        * Note: the caller has already checked that offset is
+        * suitably aligned and that len is 1, 2 or 4.
+        */
+       switch (len) {
+       case 1:
+               out_8((u8 *)addr, val);
+               (void) in_8((u8 *)addr);
+               break;
+       case 2:
+               out_le16((u16 *)addr, val);
+               (void) in_le16((u16 *)addr);
+               break;
+       default:
+               out_le32((u32 *)addr, val);
+               (void) in_le32((u32 *)addr);
+               break;
+       }
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops u3_ht_pci_ops =
+{
+       u3_ht_read_config,
+       u3_ht_write_config
+};
+
+static void __init setup_u3_agp(struct pci_controller* hose)
+{
+       /* On G5, we move AGP up to high bus number so we don't need
+        * to reassign bus numbers for HT. If we ever have P2P bridges
+        * on AGP, we'll have to move pci_assign_all_busses to the
+        * pci_controller structure so we enable it for AGP and not for
+        * HT childs.
+        * We hard code the address because of the different size of
+        * the reg address cell, we shall fix that by killing struct
+        * reg_property and using some accessor functions instead
+        */
+               hose->first_busno = 0xf0;
+       hose->last_busno = 0xff;
+       hose->ops = &u3_agp_pci_ops;
+       hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000);
+       hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000);
+
+       u3_agp = hose;
+}
+
+static void __init setup_u3_ht(struct pci_controller* hose)
+{
+       hose->ops = &u3_ht_pci_ops;
+
+       /* We hard code the address because of the different size of
+        * the reg address cell, we shall fix that by killing struct
+        * reg_property and using some accessor functions instead
+        */
+       hose->cfg_data = (volatile unsigned char *)ioremap(0xf2000000, 0x02000000);
+
+       hose->first_busno = 0;
+       hose->last_busno = 0xef;
+
+       u3_ht = hose;
+}
+
+static int __init add_bridge(struct device_node *dev)
+{
+       int len;
+       struct pci_controller *hose;
+       char* disp_name;
+       int *bus_range;
+       int primary = 1;
+       struct property *of_prop;
+
+       DBG("Adding PCI host bridge %s\n", dev->full_name);
+
+               bus_range = (int *) get_property(dev, "bus-range", &len);
+               if (bus_range == NULL || len < 2 * sizeof(int)) {
+                       printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n",
+                                      dev->full_name);
+               }
+
+       hose = alloc_bootmem(sizeof(struct pci_controller));
+       if (hose == NULL)
+               return -ENOMEM;
+               pci_setup_pci_controller(hose);
+
+               hose->arch_data = dev;
+               hose->first_busno = bus_range ? bus_range[0] : 0;
+               hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+       of_prop = alloc_bootmem(sizeof(struct property) +
+                               sizeof(hose->global_number));
+       if (of_prop) {
+               memset(of_prop, 0, sizeof(struct property));
+               of_prop->name = "linux,pci-domain";
+               of_prop->length = sizeof(hose->global_number);
+               of_prop->value = (unsigned char *)&of_prop[1];
+               memcpy(of_prop->value, &hose->global_number, sizeof(hose->global_number));
+               prom_add_property(dev, of_prop);
+       }
+
+       disp_name = NULL;
+               if (device_is_compatible(dev, "u3-agp")) {
+                       setup_u3_agp(hose);
+                       disp_name = "U3-AGP";
+                       primary = 0;
+               } else if (device_is_compatible(dev, "u3-ht")) {
+                       setup_u3_ht(hose);
+                       disp_name = "U3-HT";
+                       primary = 1;
+               }
+               printk(KERN_INFO "Found %s PCI host bridge. Firmware bus number: %d->%d\n",
+                       disp_name, hose->first_busno, hose->last_busno);
+
+               /* Interpret the "ranges" property */
+               /* This also maps the I/O region and sets isa_io/mem_base */
+               pci_process_bridge_OF_ranges(hose, dev);
+       pci_setup_phb_io(hose, primary);
+
+               /* Fixup "bus-range" OF property */
+               fixup_bus_range(dev);
+
+       return 0;
+}
+
+
+void __init maple_pcibios_fixup(void)
+{
+       struct pci_dev *dev = NULL;
+
+       DBG(" -> maple_pcibios_fixup\n");
+
+       for_each_pci_dev(dev)
+               pci_read_irq_line(dev);
+
+       /* Do the mapping of the IO space */
+       phbs_remap_io();
+
+       /* Fixup the pci_bus sysdata pointers */
+       pci_fix_bus_sysdata();
+
+       /* Setup the iommu */
+       iommu_setup_u3();
+
+       DBG(" <- maple_pcibios_fixup\n");
+}
+
+static void __init maple_fixup_phb_resources(void)
+{
+       struct pci_controller *hose, *tmp;
+       
+       list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
+               unsigned long offset = (unsigned long)hose->io_base_virt - pci_io_base;
+               hose->io_resource.start += offset;
+               hose->io_resource.end += offset;
+               printk(KERN_INFO "PCI Host %d, io start: %lx; io end: %lx\n",
+                      hose->global_number,
+                      hose->io_resource.start, hose->io_resource.end);
+       }
+}
+
+void __init maple_pci_init(void)
+{
+       struct device_node *np, *root;
+       struct device_node *ht = NULL;
+
+       /* Probe root PCI hosts, that is on U3 the AGP host and the
+        * HyperTransport host. That one is actually "kept" around
+        * and actually added last as it's resource management relies
+        * on the AGP resources to have been setup first
+        */
+       root = of_find_node_by_path("/");
+       if (root == NULL) {
+               printk(KERN_CRIT "maple_find_bridges: can't find root of device tree\n");
+               return;
+       }
+       for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) {
+               if (np->name == NULL)
+                       continue;
+               if (strcmp(np->name, "pci") == 0) {
+                       if (add_bridge(np) == 0)
+                               of_node_get(np);
+               }
+               if (strcmp(np->name, "ht") == 0) {
+                       of_node_get(np);
+                       ht = np;
+               }
+       }
+       of_node_put(root);
+
+       /* Now setup the HyperTransport host if we found any
+        */
+       if (ht && add_bridge(ht) != 0)
+               of_node_put(ht);
+
+       /* Fixup the IO resources on our host bridges as the common code
+        * does it only for childs of the host bridges
+        */
+       maple_fixup_phb_resources();
+
+       /* Setup the linkage between OF nodes and PHBs */ 
+       pci_devs_phb_init();
+
+       /* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We
+        * assume there is no P2P bridge on the AGP bus, which should be a
+        * safe assumptions hopefully.
+        */
+       if (u3_agp) {
+               struct device_node *np = u3_agp->arch_data;
+               np->busno = 0xf0;
+               for (np = np->child; np; np = np->sibling)
+                       np->busno = 0xf0;
+       }
+
+       /* Tell pci.c to use the common resource allocation mecanism */
+       pci_probe_only = 0;
+       
+       /* Allow all IO */
+       io_page_mask = -1;
+}
+
+int maple_pci_get_legacy_ide_irq(struct pci_dev *pdev, int channel)
+{
+       struct device_node *np;
+       int irq = channel ? 15 : 14;
+
+       if (pdev->vendor != PCI_VENDOR_ID_AMD ||
+           pdev->device != PCI_DEVICE_ID_AMD_8111_IDE)
+               return irq;
+
+       np = pci_device_to_OF_node(pdev);
+       if (np == NULL)
+               return irq;
+       if (np->n_intrs < 2)
+               return irq;
+       return np->intrs[channel & 0x1].line;
+}
+
+/* XXX: To remove once all firmwares are ok */
+static void fixup_maple_ide(struct pci_dev* dev)
+{
+#if 0 /* Enable this to enable IDE port 0 */
+       {
+               u8 v;
+
+               pci_read_config_byte(dev, 0x40, &v);
+               v |= 2;
+               pci_write_config_byte(dev, 0x40, v);
+       }
+#endif
+#if 0 /* fix bus master base */
+       pci_write_config_dword(dev, 0x20, 0xcc01);
+       printk("old ide resource: %lx -> %lx \n",
+              dev->resource[4].start, dev->resource[4].end);
+       dev->resource[4].start = 0xcc00;
+       dev->resource[4].end = 0xcc10;
+#endif
+#if 1 /* Enable this to fixup IDE sense/polarity of irqs in IO-APICs */
+       {
+               struct pci_dev *apicdev;
+               u32 v;
+
+               apicdev = pci_get_slot (dev->bus, PCI_DEVFN(5,0));
+               if (apicdev == NULL)
+                       printk("IDE Fixup IRQ: Can't find IO-APIC !\n");
+               else {
+                       pci_write_config_byte(apicdev, 0xf2, 0x10 + 2*14);
+                       pci_read_config_dword(apicdev, 0xf4, &v);
+                       v &= ~0x00000022;
+                       pci_write_config_dword(apicdev, 0xf4, v);
+                       pci_write_config_byte(apicdev, 0xf2, 0x10 + 2*15);
+                       pci_read_config_dword(apicdev, 0xf4, &v);
+                       v &= ~0x00000022;
+                       pci_write_config_dword(apicdev, 0xf4, v);
+                       pci_dev_put(apicdev);
+               }
+       }
+#endif
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_IDE,
+                        fixup_maple_ide);
diff --git a/arch/ppc64/kernel/maple_setup.c b/arch/ppc64/kernel/maple_setup.c
new file mode 100644 (file)
index 0000000..29d5859
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ *  arch/ppc64/kernel/maple_setup.c
+ *
+ *  (c) Copyright 2004 Benjamin Herrenschmidt (benh@kernel.crashing.org),
+ *                     IBM Corp. 
+ *
+ *  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 DEBUG
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/major.h>
+#include <linux/initrd.h>
+#include <linux/vt_kern.h>
+#include <linux/console.h>
+#include <linux/ide.h>
+#include <linux/pci.h>
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/pmu.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/serial.h>
+#include <linux/smp.h>
+
+#include <asm/processor.h>
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+#include <asm/iommu.h>
+#include <asm/machdep.h>
+#include <asm/dma.h>
+#include <asm/cputable.h>
+#include <asm/time.h>
+#include <asm/of_device.h>
+#include <asm/lmb.h>
+
+#include "mpic.h"
+
+#ifdef DEBUG
+#define DBG(fmt...) udbg_printf(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+extern int maple_set_rtc_time(struct rtc_time *tm);
+extern void maple_get_rtc_time(struct rtc_time *tm);
+extern void maple_get_boot_time(struct rtc_time *tm);
+extern void maple_calibrate_decr(void);
+extern void maple_pci_init(void);
+extern void maple_pcibios_fixup(void);
+extern int maple_pci_get_legacy_ide_irq(struct pci_dev *dev, int channel);
+extern void generic_find_legacy_serial_ports(unsigned int *default_speed);
+
+
+static void maple_restart(char *cmd)
+{
+}
+
+static void maple_power_off(void)
+{
+}
+
+static void maple_halt(void)
+{
+}
+
+#ifdef CONFIG_SMP
+struct smp_ops_t maple_smp_ops = {
+       .probe          = smp_mpic_probe,
+       .message_pass   = smp_mpic_message_pass,
+       .kick_cpu       = smp_generic_kick_cpu,
+       .setup_cpu      = smp_mpic_setup_cpu,
+       .give_timebase  = smp_generic_give_timebase,
+       .take_timebase  = smp_generic_take_timebase,
+};
+#endif /* CONFIG_SMP */
+
+void __init maple_setup_arch(void)
+{
+       /* init to some ~sane value until calibrate_delay() runs */
+       loops_per_jiffy = 50000000;
+
+       /* Setup SMP callback */
+#ifdef CONFIG_SMP
+       smp_ops = &maple_smp_ops;
+#endif
+       /* Setup the PCI DMA to "direct" by default. May be overriden
+        * by iommu later on
+        */
+       pci_dma_init_direct();
+
+       /* Lookup PCI hosts */
+               maple_pci_init();
+
+#ifdef CONFIG_DUMMY_CONSOLE
+       conswitchp = &dummy_con;
+#endif
+}
+
+/* 
+ * Early initialization.
+ */
+static void __init maple_init_early(void)
+{
+       unsigned int default_speed;
+
+       DBG(" -> maple_init_early\n");
+
+       /* Initialize hash table, from now on, we can take hash faults
+        * and call ioremap
+        */
+       hpte_init_native();
+
+       /* Find the serial port */
+               generic_find_legacy_serial_ports(&default_speed);
+
+       DBG("naca->serialPortAddr: %lx\n", (long)naca->serialPortAddr);
+
+       if (naca->serialPortAddr) {
+               void *comport;
+               /* Map the uart for udbg. */
+               comport = (void *)__ioremap(naca->serialPortAddr, 16, _PAGE_NO_CACHE);
+               udbg_init_uart(comport, default_speed);
+
+               ppc_md.udbg_putc = udbg_putc;
+               ppc_md.udbg_getc = udbg_getc;
+               ppc_md.udbg_getc_poll = udbg_getc_poll;
+               DBG("Hello World !\n");
+       }
+
+       /* Setup interrupt mapping options */
+       naca->interrupt_controller = IC_OPEN_PIC;
+
+       DBG(" <- maple_init_early\n");
+}
+
+
+static __init void maple_init_IRQ(void)
+{
+       struct device_node *root;
+       unsigned int *opprop;
+       unsigned long opic_addr;
+       struct mpic *mpic;
+       unsigned char senses[128];
+       int n;
+
+       DBG(" -> maple_init_IRQ\n");
+
+       /* XXX: Non standard, replace that with a proper openpic/mpic node
+        * in the device-tree. Find the Open PIC if present */
+       root = of_find_node_by_path("/");
+       opprop = (unsigned int *) get_property(root,
+                               "platform-open-pic", NULL);
+       if (opprop == 0)
+               panic("OpenPIC not found !\n");
+
+       n = prom_n_addr_cells(root);
+       for (opic_addr = 0; n > 0; --n)
+               opic_addr = (opic_addr << 32) + *opprop++;
+       of_node_put(root);
+
+       /* Obtain sense values from device-tree */
+       prom_get_irq_senses(senses, 0, 128);
+
+       mpic = mpic_alloc(opic_addr,
+                         MPIC_PRIMARY | MPIC_BIG_ENDIAN |
+                         MPIC_BROKEN_U3 | MPIC_WANTS_RESET,
+                         0, 0, 128, 128, senses, 128, "U3-MPIC");
+       BUG_ON(mpic == NULL);
+       mpic_init(mpic);
+
+       DBG(" <- maple_init_IRQ\n");
+}
+
+static void __init maple_progress(char *s, unsigned short hex)
+{
+       printk("*** %04x : %s\n", hex, s ? s : "");
+}
+
+
+/*
+ * Called very early, MMU is off, device-tree isn't unflattened
+ */
+static int __init maple_probe(int platform)
+{
+       if (platform != PLATFORM_MAPLE)
+               return 0;
+       /*
+        * On U3, the DART (iommu) must be allocated now since it
+        * has an impact on htab_initialize (due to the large page it
+        * occupies having to be broken up so the DART itself is not
+        * part of the cacheable linar mapping
+        */
+       alloc_u3_dart_table();
+
+       return 1;
+}
+
+struct machdep_calls __initdata maple_md = {
+       .probe                  = maple_probe,
+       .setup_arch             = maple_setup_arch,
+       .init_early             = maple_init_early,
+       .init_IRQ               = maple_init_IRQ,
+       .get_irq                = mpic_get_irq,
+       .pcibios_fixup          = maple_pcibios_fixup,
+       .pci_get_legacy_ide_irq = maple_pci_get_legacy_ide_irq,
+       .restart                = maple_restart,
+       .power_off              = maple_power_off,
+       .halt                   = maple_halt,
+               .get_boot_time          = maple_get_boot_time,
+               .set_rtc_time           = maple_set_rtc_time,
+               .get_rtc_time           = maple_get_rtc_time,
+       .calibrate_decr         = maple_calibrate_decr,
+       .progress               = maple_progress,
+};
diff --git a/arch/ppc64/kernel/maple_time.c b/arch/ppc64/kernel/maple_time.c
new file mode 100644 (file)
index 0000000..07ce789
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ *  arch/ppc64/kernel/maple_time.c
+ *
+ *  (c) Copyright 2004 Benjamin Herrenschmidt (benh@kernel.crashing.org),
+ *                     IBM Corp. 
+ *
+ *  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.
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/interrupt.h>
+#include <linux/mc146818rtc.h>
+#include <linux/bcd.h>
+
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/machdep.h>
+#include <asm/time.h>
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+extern void setup_default_decr(void);
+extern void GregorianDay(struct rtc_time * tm);
+
+extern unsigned long ppc_tb_freq;
+extern unsigned long ppc_proc_freq;
+static int maple_rtc_addr;
+
+static int maple_clock_read(int addr)
+{
+       outb_p(addr, maple_rtc_addr);
+       return inb_p(maple_rtc_addr+1);
+}
+
+static void maple_clock_write(unsigned long val, int addr)
+{
+       outb_p(addr, maple_rtc_addr);
+       outb_p(val, maple_rtc_addr+1);
+}
+
+void maple_get_rtc_time(struct rtc_time *tm)
+{
+       int uip, i;
+
+       /* The Linux interpretation of the CMOS clock register contents:
+        * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
+        * RTC registers show the second which has precisely just started.
+        * Let's hope other operating systems interpret the RTC the same way.
+        */
+
+       /* Since the UIP flag is set for about 2.2 ms and the clock
+        * is typically written with a precision of 1 jiffy, trying
+        * to obtain a precision better than a few milliseconds is
+        * an illusion. Only consistency is interesting, this also
+        * allows to use the routine for /dev/rtc without a potential
+        * 1 second kernel busy loop triggered by any reader of /dev/rtc.
+        */
+
+       for (i = 0; i<1000000; i++) {
+               uip = maple_clock_read(RTC_FREQ_SELECT);
+               tm->tm_sec = maple_clock_read(RTC_SECONDS);
+               tm->tm_min = maple_clock_read(RTC_MINUTES);
+               tm->tm_hour = maple_clock_read(RTC_HOURS);
+               tm->tm_mday = maple_clock_read(RTC_DAY_OF_MONTH);
+               tm->tm_mon = maple_clock_read(RTC_MONTH);
+               tm->tm_year = maple_clock_read(RTC_YEAR);
+               uip |= maple_clock_read(RTC_FREQ_SELECT);
+               if ((uip & RTC_UIP)==0)
+                       break;
+       }
+
+       if (!(maple_clock_read(RTC_CONTROL) & RTC_DM_BINARY)
+           || RTC_ALWAYS_BCD) {
+               BCD_TO_BIN(tm->tm_sec);
+               BCD_TO_BIN(tm->tm_min);
+               BCD_TO_BIN(tm->tm_hour);
+               BCD_TO_BIN(tm->tm_mday);
+               BCD_TO_BIN(tm->tm_mon);
+               BCD_TO_BIN(tm->tm_year);
+         }
+       if ((tm->tm_year + 1900) < 1970)
+               tm->tm_year += 100;
+
+       GregorianDay(tm);
+}
+
+int maple_set_rtc_time(struct rtc_time *tm)
+{
+       unsigned char save_control, save_freq_select;
+       int sec, min, hour, mon, mday, year;
+
+       spin_lock(&rtc_lock);
+
+       save_control = maple_clock_read(RTC_CONTROL); /* tell the clock it's being set */
+
+       maple_clock_write((save_control|RTC_SET), RTC_CONTROL);
+
+       save_freq_select = maple_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */
+
+       maple_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+       sec = tm->tm_sec;
+       min = tm->tm_min;
+       hour = tm->tm_hour;
+       mon = tm->tm_mon;
+       mday = tm->tm_mday;
+       year = tm->tm_year;
+
+       if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+               BIN_TO_BCD(sec);
+               BIN_TO_BCD(min);
+               BIN_TO_BCD(hour);
+               BIN_TO_BCD(mon);
+               BIN_TO_BCD(mday);
+               BIN_TO_BCD(year);
+       }
+       maple_clock_write(sec, RTC_SECONDS);
+       maple_clock_write(min, RTC_MINUTES);
+       maple_clock_write(hour, RTC_HOURS);
+       maple_clock_write(mon, RTC_MONTH);
+       maple_clock_write(mday, RTC_DAY_OF_MONTH);
+       maple_clock_write(year, RTC_YEAR);
+
+       /* The following flags have to be released exactly in this order,
+        * otherwise the DS12887 (popular MC146818A clone with integrated
+        * battery and quartz) will not reset the oscillator and will not
+        * update precisely 500 ms later. You won't find this mentioned in
+        * the Dallas Semiconductor data sheets, but who believes data
+        * sheets anyway ...                           -- Markus Kuhn
+        */
+       maple_clock_write(save_control, RTC_CONTROL);
+       maple_clock_write(save_freq_select, RTC_FREQ_SELECT);
+
+       spin_unlock(&rtc_lock);
+
+       return 0;
+}
+
+void __init maple_get_boot_time(struct rtc_time *tm)
+{
+       struct device_node *rtcs;
+
+       rtcs = find_compatible_devices("rtc", "pnpPNP,b00");
+       if (rtcs && rtcs->addrs) {
+               maple_rtc_addr = rtcs->addrs[0].address;
+               printk(KERN_INFO "Maple: Found RTC at 0x%x\n", maple_rtc_addr);
+       } else {
+               maple_rtc_addr = RTC_PORT(0); /* legacy address */
+               printk(KERN_INFO "Maple: No device node for RTC, assuming "
+                      "legacy address (0x%x)\n", maple_rtc_addr);
+       }
+       
+       maple_get_rtc_time(tm);
+}
+
+/* XXX FIXME: Some sane defaults: 125 MHz timebase, 1GHz processor */
+#define DEFAULT_TB_FREQ                125000000UL
+#define DEFAULT_PROC_FREQ      (DEFAULT_TB_FREQ * 8)
+
+void __init maple_calibrate_decr(void)
+{
+       struct device_node *cpu;
+       struct div_result divres;
+       unsigned int *fp = NULL;
+
+       /*
+        * The cpu node should have a timebase-frequency property
+        * to tell us the rate at which the decrementer counts.
+        */
+       cpu = of_find_node_by_type(NULL, "cpu");
+
+       ppc_tb_freq = DEFAULT_TB_FREQ;
+       if (cpu != 0)
+               fp = (unsigned int *)get_property(cpu, "timebase-frequency", NULL);
+       if (fp != NULL)
+               ppc_tb_freq = *fp;
+       else
+               printk(KERN_ERR "WARNING: Estimating decrementer frequency (not found)\n");
+       fp = NULL;
+       ppc_proc_freq = DEFAULT_PROC_FREQ;
+       if (cpu != 0)
+               fp = (unsigned int *)get_property(cpu, "clock-frequency", NULL);
+       if (fp != NULL)
+               ppc_proc_freq = *fp;
+       else
+               printk(KERN_ERR "WARNING: Estimating processor frequency (not found)\n");
+
+       of_node_put(cpu);
+
+       printk(KERN_INFO "time_init: decrementer frequency = %lu.%.6lu MHz\n",
+              ppc_tb_freq/1000000, ppc_tb_freq%1000000);
+       printk(KERN_INFO "time_init: processor frequency   = %lu.%.6lu MHz\n",
+              ppc_proc_freq/1000000, ppc_proc_freq%1000000);
+
+       tb_ticks_per_jiffy = ppc_tb_freq / HZ;
+       tb_ticks_per_sec = tb_ticks_per_jiffy * HZ;
+       tb_ticks_per_usec = ppc_tb_freq / 1000000;
+       tb_to_us = mulhwu_scale_factor(ppc_tb_freq, 1000000);
+       div128_by_32(1024*1024, 0, tb_ticks_per_sec, &divres);
+       tb_to_xs = divres.result_low;
+
+       setup_default_decr();
+}
diff --git a/arch/ppc64/kernel/mpic.c b/arch/ppc64/kernel/mpic.c
new file mode 100644 (file)
index 0000000..e013e85
--- /dev/null
@@ -0,0 +1,861 @@
+/*
+ *  arch/ppc64/kernel/mpic.c
+ *
+ *  Driver for interrupt controllers following the OpenPIC standard, the
+ *  common implementation beeing IBM's MPIC. This driver also can deal
+ *  with various broken implementations of this HW.
+ *
+ *  Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp.
+ *
+ *  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.
+ */
+
+#undef DEBUG
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/bootmem.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+
+#include <asm/ptrace.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+#include <asm/machdep.h>
+
+#include "mpic.h"
+
+#ifdef DEBUG
+#define DBG(fmt...) printk(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+static struct mpic *mpics;
+static struct mpic *mpic_primary;
+static spinlock_t mpic_lock = SPIN_LOCK_UNLOCKED;
+
+
+/*
+ * Register accessor functions
+ */
+
+
+static inline u32 _mpic_read(unsigned int be, volatile u32 __iomem *base,
+                           unsigned int reg)
+{
+       if (be)
+               return in_be32(base + (reg >> 2));
+       else
+               return in_le32(base + (reg >> 2));
+}
+
+static inline void _mpic_write(unsigned int be, volatile u32 __iomem *base,
+                             unsigned int reg, u32 value)
+{
+       if (be)
+               out_be32(base + (reg >> 2), value);
+       else
+               out_le32(base + (reg >> 2), value);
+}
+
+static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
+{
+       unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0;
+       unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
+
+       if (mpic->flags & MPIC_BROKEN_IPI)
+               be = !be;
+       return _mpic_read(be, mpic->gregs, offset);
+}
+
+static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value)
+{
+       unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10);
+
+       _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, value);
+}
+
+static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg)
+{
+       unsigned int cpu = 0;
+
+       if (mpic->flags & MPIC_PRIMARY)
+               cpu = hard_smp_processor_id();
+
+       return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg);
+}
+
+static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value)
+{
+       unsigned int cpu = 0;
+
+       if (mpic->flags & MPIC_PRIMARY)
+               cpu = hard_smp_processor_id();
+
+       _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg, value);
+}
+
+static inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg)
+{
+       unsigned int    isu = src_no >> mpic->isu_shift;
+       unsigned int    idx = src_no & mpic->isu_mask;
+
+       return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
+                         reg + (idx * MPIC_IRQ_STRIDE));
+}
+
+static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no,
+                                  unsigned int reg, u32 value)
+{
+       unsigned int    isu = src_no >> mpic->isu_shift;
+       unsigned int    idx = src_no & mpic->isu_mask;
+
+       _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
+                   reg + (idx * MPIC_IRQ_STRIDE), value);
+}
+
+#define mpic_read(b,r)         _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,(b),(r))
+#define mpic_write(b,r,v)      _mpic_write(mpic->flags & MPIC_BIG_ENDIAN,(b),(r),(v))
+#define mpic_ipi_read(i)       _mpic_ipi_read(mpic,(i))
+#define mpic_ipi_write(i,v)    _mpic_ipi_write(mpic,(i),(v))
+#define mpic_cpu_read(i)       _mpic_cpu_read(mpic,(i))
+#define mpic_cpu_write(i,v)    _mpic_cpu_write(mpic,(i),(v))
+#define mpic_irq_read(s,r)     _mpic_irq_read(mpic,(s),(r))
+#define mpic_irq_write(s,r,v)  _mpic_irq_write(mpic,(s),(r),(v))
+
+
+/*
+ * Low level utility functions
+ */
+
+
+
+/* Check if we have one of those nice broken MPICs with a flipped endian on
+ * reads from IPI registers
+ */
+static void __init mpic_test_broken_ipi(struct mpic *mpic)
+{
+       u32 r;
+
+       mpic_write(mpic->gregs, MPIC_GREG_IPI_VECTOR_PRI_0, MPIC_VECPRI_MASK);
+       r = mpic_read(mpic->gregs, MPIC_GREG_IPI_VECTOR_PRI_0);
+
+       if (r == le32_to_cpu(MPIC_VECPRI_MASK)) {
+               printk(KERN_INFO "mpic: Detected reversed IPI registers\n");
+               mpic->flags |= MPIC_BROKEN_IPI;
+       }
+}
+
+#ifdef CONFIG_MPIC_BROKEN_U3
+
+/* Test if an interrupt is sourced from HyperTransport (used on broken U3s)
+ * to force the edge setting on the MPIC and do the ack workaround.
+ */
+static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source_no)
+{
+       if (source_no >= 128 || !mpic->fixups)
+               return 0;
+       return mpic->fixups[source_no].base != NULL;
+}
+
+static inline void mpic_apic_end_irq(struct mpic *mpic, unsigned int source_no)
+{
+       struct mpic_irq_fixup *fixup = &mpic->fixups[source_no];
+       u32 tmp;
+
+       spin_lock(&mpic->fixup_lock);
+       writeb(0x11 + 2 * fixup->irq, fixup->base);
+       tmp = readl(fixup->base + 2);
+       writel(tmp | 0x80000000ul, fixup->base + 2);
+       /* config writes shouldn't be posted but let's be safe ... */
+       (void)readl(fixup->base + 2);
+       spin_unlock(&mpic->fixup_lock);
+}
+
+
+static void __init mpic_amd8111_read_irq(struct mpic *mpic, u8 __iomem *devbase)
+{
+       int i, irq;
+       u32 tmp;
+
+       printk(KERN_INFO "mpic:    - Workarounds on AMD 8111 @ %p\n", devbase);
+
+       for (i=0; i < 24; i++) {
+               writeb(0x10 + 2*i, devbase + 0xf2);
+               tmp = readl(devbase + 0xf4);
+               if ((tmp & 0x1) || !(tmp & 0x20))
+                       continue;
+               irq = (tmp >> 16) & 0xff;
+               mpic->fixups[irq].irq = i;
+               mpic->fixups[irq].base = devbase + 0xf2;
+       }
+}
+static void __init mpic_amd8131_read_irq(struct mpic *mpic, u8 __iomem *devbase)
+{
+       int i, irq;
+       u32 tmp;
+
+       printk(KERN_INFO "mpic:    - Workarounds on AMD 8131 @ %p\n", devbase);
+
+       for (i=0; i < 4; i++) {
+               writeb(0x10 + 2*i, devbase + 0xba);
+               tmp = readl(devbase + 0xbc);
+               if ((tmp & 0x1) || !(tmp & 0x20))
+                       continue;
+               irq = (tmp >> 16) & 0xff;
+               mpic->fixups[irq].irq = i;
+               mpic->fixups[irq].base = devbase + 0xba;
+       }
+}
+static void __init mpic_scan_ioapics(struct mpic *mpic)
+{
+       unsigned int devfn;
+       u8 __iomem *cfgspace;
+
+       printk(KERN_INFO "mpic: Setting up IO-APICs workarounds for U3\n");
+
+       /* Allocate fixups array */
+       mpic->fixups = alloc_bootmem(128 * sizeof(struct mpic_irq_fixup));
+       BUG_ON(mpic->fixups == NULL);
+       memset(mpic->fixups, 0, 128 * sizeof(struct mpic_irq_fixup));
+
+       /* Init spinlock */
+       spin_lock_init(&mpic->fixup_lock);
+
+       /* Map u3 config space. We assume all IO-APICs are on the primary bus
+        * and slot will never be above "0xf" so we only need to map 32k
+        */
+       cfgspace = (unsigned char __iomem *)ioremap(0xf2000000, 0x8000);
+       BUG_ON(cfgspace == NULL);
+
+       /* Now we scan all slots. We do a very quick scan, we read the header type,
+        * vendor ID and device ID only, that's plenty enough
+        */
+       for (devfn = 0; devfn < PCI_DEVFN(0x10,0); devfn ++) {
+               u8 __iomem *devbase = cfgspace + (devfn << 8);
+               u8 hdr_type = readb(devbase + PCI_HEADER_TYPE);
+               u32 l = readl(devbase + PCI_VENDOR_ID);
+               u16 vendor_id, device_id;
+               int multifunc = 0;
+
+               DBG("devfn %x, l: %x\n", devfn, l);
+
+               /* If no device, skip */
+               if (l == 0xffffffff || l == 0x00000000 ||
+                   l == 0x0000ffff || l == 0xffff0000)
+                       goto next;
+
+               /* Check if it's a multifunction device (only really used
+                * to function 0 though
+                */
+               multifunc = !!(hdr_type & 0x80);
+               vendor_id = l & 0xffff;
+               device_id = (l >> 16) & 0xffff;
+
+               /* If a known device, go to fixup setup code */
+               if (vendor_id == PCI_VENDOR_ID_AMD && device_id == 0x7460)
+                       mpic_amd8111_read_irq(mpic, devbase);
+               if (vendor_id == PCI_VENDOR_ID_AMD && device_id == 0x7450)
+                       mpic_amd8131_read_irq(mpic, devbase);
+       next:
+               /* next device, if function 0 */
+               if ((PCI_FUNC(devfn) == 0) && !multifunc)
+                       devfn += 7;
+       }
+}
+
+#endif /* CONFIG_MPIC_BROKEN_U3 */
+
+
+/* Find an mpic associated with a given linux interrupt */
+static struct mpic *mpic_find(unsigned int irq, unsigned int *is_ipi)
+{
+       struct mpic *mpic = mpics;
+
+       while(mpic) {
+               /* search IPIs first since they may override the main interrupts */
+               if (irq >= mpic->ipi_offset && irq < (mpic->ipi_offset + 4)) {
+                       if (is_ipi)
+                               *is_ipi = 1;
+                       return mpic;
+               }
+               if (irq >= mpic->irq_offset &&
+                   irq < (mpic->irq_offset + mpic->irq_count)) {
+                       if (is_ipi)
+                               *is_ipi = 0;
+                       return mpic;
+               }
+               mpic = mpic -> next;
+       }
+       return NULL;
+}
+
+/* Convert a cpu mask from logical to physical cpu numbers. */
+static inline u32 mpic_physmask(u32 cpumask)
+{
+       int i;
+       u32 mask = 0;
+
+       for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
+               mask |= (cpumask & 1) << get_hard_smp_processor_id(i);
+       return mask;
+}
+
+#ifdef CONFIG_SMP
+/* Get the mpic structure from the IPI number */
+static inline struct mpic * mpic_from_ipi(unsigned int ipi)
+{
+       return container_of(irq_desc[ipi].handler, struct mpic, hc_ipi);
+}
+#endif
+
+/* Get the mpic structure from the irq number */
+static inline struct mpic * mpic_from_irq(unsigned int irq)
+{
+       return container_of(irq_desc[irq].handler, struct mpic, hc_irq);
+}
+
+/* Send an EOI */
+static inline void mpic_eoi(struct mpic *mpic)
+{
+       mpic_cpu_write(MPIC_CPU_EOI, 0);
+       (void)mpic_cpu_read(MPIC_CPU_WHOAMI);
+}
+
+#ifdef CONFIG_SMP
+static irqreturn_t mpic_ipi_action(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct mpic *mpic = dev_id;
+
+       smp_message_recv(irq - mpic->ipi_offset, regs);
+       return IRQ_HANDLED;
+}
+#endif /* CONFIG_SMP */
+
+/*
+ * Linux descriptor level callbacks
+ */
+
+
+static void mpic_enable_irq(unsigned int irq)
+{
+       unsigned int loops = 100000;
+       struct mpic *mpic = mpic_from_irq(irq);
+       unsigned int src = irq - mpic->irq_offset;
+
+       DBG("%s: enable_irq: %d (src %d)\n", mpic->name, irq, src);
+
+       mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
+                      mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & ~MPIC_VECPRI_MASK);
+
+       /* make sure mask gets to controller before we return to user */
+       do {
+               if (!loops--) {
+                       printk(KERN_ERR "mpic_enable_irq timeout\n");
+                       break;
+               }
+       } while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK);    
+}
+
+static void mpic_disable_irq(unsigned int irq)
+{
+       unsigned int loops = 100000;
+       struct mpic *mpic = mpic_from_irq(irq);
+       unsigned int src = irq - mpic->irq_offset;
+
+       DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src);
+
+       mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI,
+                      mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) | MPIC_VECPRI_MASK);
+
+       /* make sure mask gets to controller before we return to user */
+       do {
+               if (!loops--) {
+                       printk(KERN_ERR "mpic_enable_irq timeout\n");
+                       break;
+               }
+       } while(!(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK));
+}
+
+static void mpic_end_irq(unsigned int irq)
+{
+       struct mpic *mpic = mpic_from_irq(irq);
+
+       DBG("%s: end_irq: %d\n", mpic->name, irq);
+
+       /* We always EOI on end_irq() even for edge interrupts since that
+        * should only lower the priority, the MPIC should have properly
+        * latched another edge interrupt coming in anyway
+        */
+
+#ifdef CONFIG_MPIC_BROKEN_U3
+       if (mpic->flags & MPIC_BROKEN_U3) {
+               unsigned int src = irq - mpic->irq_offset;
+               if (mpic_is_ht_interrupt(mpic, src))
+                       mpic_apic_end_irq(mpic, src);
+       }
+#endif /* CONFIG_MPIC_BROKEN_U3 */
+
+       mpic_eoi(mpic);
+}
+
+#ifdef CONFIG_SMP
+
+static void mpic_enable_ipi(unsigned int irq)
+{
+       struct mpic *mpic = mpic_from_ipi(irq);
+       unsigned int src = irq - mpic->ipi_offset;
+
+       DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, irq, src);
+       mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK);
+}
+
+static void mpic_disable_ipi(unsigned int irq)
+{
+       /* NEVER disable an IPI... that's just plain wrong! */
+}
+
+static void mpic_end_ipi(unsigned int irq)
+{
+       struct mpic *mpic = mpic_from_ipi(irq);
+
+       /*
+        * IPIs are marked IRQ_PER_CPU. This has the side effect of
+        * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from
+        * applying to them. We EOI them late to avoid re-entering.
+        * We mark IPI's with SA_INTERRUPT as they must run with
+        * irqs disabled.
+        */
+       mpic_eoi(mpic);
+}
+
+#endif /* CONFIG_SMP */
+
+static void mpic_set_affinity(unsigned int irq, cpumask_t cpumask)
+{
+       struct mpic *mpic = mpic_from_irq(irq);
+
+       cpumask_t tmp;
+
+       cpus_and(tmp, cpumask, cpu_online_map);
+
+       mpic_irq_write(irq - mpic->irq_offset, MPIC_IRQ_DESTINATION,
+                      mpic_physmask(cpus_addr(tmp)[0]));       
+}
+
+
+/*
+ * Exported functions
+ */
+
+
+struct mpic * __init mpic_alloc(unsigned long phys_addr,
+                               unsigned int flags,
+                               unsigned int isu_size,
+                               unsigned int irq_offset,
+                               unsigned int irq_count,
+                               unsigned int ipi_offset,
+                               unsigned char *senses,
+                               unsigned int senses_count,
+                               const char *name)
+{
+       struct mpic     *mpic;
+       u32             reg;
+       const char      *vers;
+       int             i;
+
+       mpic = alloc_bootmem(sizeof(struct mpic));
+       if (mpic == NULL)
+               return NULL;
+       
+       memset(mpic, 0, sizeof(struct mpic));
+       mpic->name = name;
+
+       mpic->hc_irq.typename = name;
+       mpic->hc_irq.enable = mpic_enable_irq;
+       mpic->hc_irq.disable = mpic_disable_irq;
+       mpic->hc_irq.end = mpic_end_irq;
+       if (flags & MPIC_PRIMARY)
+               mpic->hc_irq.set_affinity = mpic_set_affinity;
+#ifdef CONFIG_SMP
+       mpic->hc_ipi.typename = name;
+       mpic->hc_ipi.enable = mpic_enable_ipi;
+       mpic->hc_ipi.disable = mpic_disable_ipi;
+       mpic->hc_ipi.end = mpic_end_ipi;
+#endif /* CONFIG_SMP */
+
+       mpic->flags = flags;
+       mpic->isu_size = isu_size;
+       mpic->irq_offset = irq_offset;
+       mpic->irq_count = irq_count;
+       mpic->ipi_offset = ipi_offset;
+       mpic->num_sources = 0; /* so far */
+       mpic->senses = senses;
+       mpic->senses_count = senses_count;
+
+       /* Map the global registers */
+       mpic->gregs = ioremap(phys_addr + MPIC_GREG_BASE, 0x1000);
+       mpic->tmregs = mpic->gregs + (MPIC_TIMER_BASE >> 2);
+       BUG_ON(mpic->gregs == NULL);
+
+       /* Reset */
+       if (flags & MPIC_WANTS_RESET) {
+               mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0,
+                          mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
+                          | MPIC_GREG_GCONF_RESET);
+               while( mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
+                      & MPIC_GREG_GCONF_RESET)
+                       mb();
+       }
+
+       /* Read feature register, calculate num CPUs and, for non-ISU
+        * MPICs, num sources as well. On ISU MPICs, sources are counted
+        * as ISUs are added
+        */
+       reg = mpic_read(mpic->gregs, MPIC_GREG_FEATURE_0);
+       mpic->num_cpus = ((reg & MPIC_GREG_FEATURE_LAST_CPU_MASK)
+                         >> MPIC_GREG_FEATURE_LAST_CPU_SHIFT) + 1;
+       if (isu_size == 0)
+               mpic->num_sources = ((reg & MPIC_GREG_FEATURE_LAST_SRC_MASK)
+                                    >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT) + 1;
+
+       /* Map the per-CPU registers */
+       for (i = 0; i < mpic->num_cpus; i++) {
+               mpic->cpuregs[i] = ioremap(phys_addr + MPIC_CPU_BASE +
+                                          i * MPIC_CPU_STRIDE, 0x1000);
+               BUG_ON(mpic->cpuregs[i] == NULL);
+       }
+
+       /* Initialize main ISU if none provided */
+       if (mpic->isu_size == 0) {
+               mpic->isu_size = mpic->num_sources;
+               mpic->isus[0] = ioremap(phys_addr + MPIC_IRQ_BASE,
+                                       MPIC_IRQ_STRIDE * mpic->isu_size);
+               BUG_ON(mpic->isus[0] == NULL);
+       }
+       mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);
+       mpic->isu_mask = (1 << mpic->isu_shift) - 1;
+
+       /* Display version */
+       switch (reg & MPIC_GREG_FEATURE_VERSION_MASK) {
+       case 1:
+               vers = "1.0";
+               break;
+       case 2:
+               vers = "1.2";
+               break;
+       case 3:
+               vers = "1.3";
+               break;
+       default:
+               vers = "<unknown>";
+               break;
+       }
+       printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %lx, max %d CPUs\n",
+              name, vers, phys_addr, mpic->num_cpus);
+       printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", mpic->isu_size,
+              mpic->isu_shift, mpic->isu_mask);
+
+       mpic->next = mpics;
+       mpics = mpic;
+
+       if (flags & MPIC_PRIMARY)
+               mpic_primary = mpic;
+
+       return mpic;
+}
+
+void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
+                           unsigned long phys_addr)
+{
+       unsigned int isu_first = isu_num * mpic->isu_size;
+
+       BUG_ON(isu_num >= MPIC_MAX_ISU);
+
+       mpic->isus[isu_num] = ioremap(phys_addr, MPIC_IRQ_STRIDE * mpic->isu_size);
+       if ((isu_first + mpic->isu_size) > mpic->num_sources)
+               mpic->num_sources = isu_first + mpic->isu_size;
+}
+
+void __init mpic_setup_cascade(unsigned int irq, mpic_cascade_t handler,
+                              void *data)
+{
+       struct mpic *mpic = mpic_find(irq, NULL);
+       unsigned long flags;
+
+       /* Synchronization here is a bit dodgy, so don't try to replace cascade
+        * interrupts on the fly too often ... but normally it's set up at boot.
+        */
+       spin_lock_irqsave(&mpic_lock, flags);
+       if (mpic->cascade)             
+               mpic_disable_irq(mpic->cascade_vec + mpic->irq_offset);
+       mpic->cascade = NULL;
+       wmb();
+       mpic->cascade_vec = irq - mpic->irq_offset;
+       mpic->cascade_data = data;
+       wmb();
+       mpic->cascade = handler;
+       mpic_enable_irq(irq);
+       spin_unlock_irqrestore(&mpic_lock, flags);
+}
+
+void __init mpic_init(struct mpic *mpic)
+{
+       int i;
+
+       BUG_ON(mpic->num_sources == 0);
+
+       printk(KERN_INFO "mpic: Initializing for %d sources\n", mpic->num_sources);
+
+       /* Set current processor priority to max */
+       mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0xf);
+
+       /* Initialize timers: just disable them all */
+       for (i = 0; i < 4; i++) {
+               mpic_write(mpic->tmregs,
+                          i * MPIC_TIMER_STRIDE + MPIC_TIMER_DESTINATION, 0);
+               mpic_write(mpic->tmregs,
+                          i * MPIC_TIMER_STRIDE + MPIC_TIMER_VECTOR_PRI,
+                          MPIC_VECPRI_MASK |
+                          (MPIC_VEC_TIMER_0 + i));
+       }
+
+       /* Initialize IPIs to our reserved vectors and mark them disabled for now */
+       mpic_test_broken_ipi(mpic);
+       for (i = 0; i < 4; i++) {
+               mpic_ipi_write(i,
+                              MPIC_VECPRI_MASK |
+                              (10 << MPIC_VECPRI_PRIORITY_SHIFT) |
+                              (MPIC_VEC_IPI_0 + i));
+#ifdef CONFIG_SMP
+               if (!(mpic->flags & MPIC_PRIMARY))
+                       continue;
+               irq_desc[mpic->ipi_offset+i].status |= IRQ_PER_CPU;
+               irq_desc[mpic->ipi_offset+i].handler = &mpic->hc_ipi;
+               
+#endif /* CONFIG_SMP */
+       }
+
+       /* Initialize interrupt sources */
+       if (mpic->irq_count == 0)
+               mpic->irq_count = mpic->num_sources;
+
+#ifdef CONFIG_MPIC_BROKEN_U3
+       /* Do the ioapic fixups on U3 broken mpic */
+       DBG("MPIC flags: %x\n", mpic->flags);
+       if ((mpic->flags & MPIC_BROKEN_U3) && (mpic->flags & MPIC_PRIMARY))
+               mpic_scan_ioapics(mpic);
+#endif /* CONFIG_MPIC_BROKEN_U3 */
+
+       for (i = 0; i < mpic->num_sources; i++) {
+               /* start with vector = source number, and masked */
+               u32 vecpri = MPIC_VECPRI_MASK | i | (8 << MPIC_VECPRI_PRIORITY_SHIFT);
+               int level = 0;
+               
+               /* if it's an IPI, we skip it */
+               if ((mpic->irq_offset + i) >= (mpic->ipi_offset + i) &&
+                   (mpic->irq_offset + i) <  (mpic->ipi_offset + i + 4))
+                       continue;
+
+               /* do senses munging */
+               if (mpic->senses && i < mpic->senses_count) {
+                       if (mpic->senses[i] & IRQ_SENSE_LEVEL)
+                               vecpri |= MPIC_VECPRI_SENSE_LEVEL;
+                       if (mpic->senses[i] & IRQ_POLARITY_POSITIVE)
+                               vecpri |= MPIC_VECPRI_POLARITY_POSITIVE;
+               } else
+                       vecpri |= MPIC_VECPRI_SENSE_LEVEL;
+
+               /* remember if it was a level interrupts */
+               level = (vecpri & MPIC_VECPRI_SENSE_LEVEL);
+
+               /* deal with broken U3 */
+               if (mpic->flags & MPIC_BROKEN_U3) {
+#ifdef CONFIG_MPIC_BROKEN_U3
+                       if (mpic_is_ht_interrupt(mpic, i)) {
+                               vecpri &= ~(MPIC_VECPRI_SENSE_MASK |
+                                           MPIC_VECPRI_POLARITY_MASK);
+                               vecpri |= MPIC_VECPRI_POLARITY_POSITIVE;
+                       }
+#else
+                       printk(KERN_ERR "mpic: BROKEN_U3 set, but CONFIG doesn't match\n");
+#endif
+               }
+
+               DBG("setup source %d, vecpri: %08x, level: %d\n", i, vecpri,
+                   (level != 0));
+
+               /* init hw */
+               mpic_irq_write(i, MPIC_IRQ_VECTOR_PRI, vecpri);
+               mpic_irq_write(i, MPIC_IRQ_DESTINATION,
+                              1 << get_hard_smp_processor_id(boot_cpuid));
+
+               /* init linux descriptors */
+               if (i < mpic->irq_count) {
+                       irq_desc[mpic->irq_offset+i].status = level ? IRQ_LEVEL : 0;
+                       irq_desc[mpic->irq_offset+i].handler = &mpic->hc_irq;
+               }
+       }
+       
+       /* Init spurrious vector */
+       mpic_write(mpic->gregs, MPIC_GREG_SPURIOUS, MPIC_VEC_SPURRIOUS);
+
+       /* Disable 8259 passthrough */
+       mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0,
+                  mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_0)
+                  | MPIC_GREG_GCONF_8259_PTHROU_DIS);
+
+       /* Set current processor priority to 0 */
+       mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0);
+}
+
+
+
+void mpic_irq_set_priority(unsigned int irq, unsigned int pri)
+{
+       int is_ipi;
+       struct mpic *mpic = mpic_find(irq, &is_ipi);
+       unsigned long flags;
+       u32 reg;
+
+       spin_lock_irqsave(&mpic_lock, flags);
+       if (is_ipi) {
+               reg = mpic_ipi_read(irq - mpic->ipi_offset) & MPIC_VECPRI_PRIORITY_MASK;
+               mpic_ipi_write(irq - mpic->ipi_offset,
+                              reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
+       } else {
+               reg = mpic_irq_read(irq - mpic->irq_offset, MPIC_IRQ_VECTOR_PRI)
+                       & MPIC_VECPRI_PRIORITY_MASK;
+               mpic_irq_write(irq - mpic->irq_offset, MPIC_IRQ_VECTOR_PRI,
+                              reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
+       }
+       spin_unlock_irqrestore(&mpic_lock, flags);
+}
+
+unsigned int mpic_irq_get_priority(unsigned int irq)
+{
+       int is_ipi;
+       struct mpic *mpic = mpic_find(irq, &is_ipi);
+       unsigned long flags;
+       u32 reg;
+
+       spin_lock_irqsave(&mpic_lock, flags);
+       if (is_ipi)
+               reg = mpic_ipi_read(irq - mpic->ipi_offset);
+       else
+               reg = mpic_irq_read(irq - mpic->irq_offset, MPIC_IRQ_VECTOR_PRI);
+       spin_unlock_irqrestore(&mpic_lock, flags);
+       return (reg & MPIC_VECPRI_PRIORITY_MASK) >> MPIC_VECPRI_PRIORITY_SHIFT;
+}
+
+void mpic_setup_this_cpu(void)
+{
+#ifdef CONFIG_SMP
+       struct mpic *mpic = mpic_primary;
+       unsigned long flags;
+#ifdef CONFIG_IRQ_ALL_CPUS
+       u32 msk = 1 << hard_smp_processor_id();
+       unsigned int i;
+#endif
+
+       BUG_ON(mpic == NULL);
+
+       DBG("%s: setup_this_cpu(%d)\n", mpic->name, hard_smp_processor_id());
+
+       spin_lock_irqsave(&mpic_lock, flags);
+
+#ifdef CONFIG_IRQ_ALL_CPUS
+       /* let the mpic know we want intrs. default affinity is 0xffffffff
+        * until changed via /proc. That's how it's done on x86. If we want
+        * it differently, then we should make sure we also change the default
+        * values of irq_affinity in irq.c.
+        */
+       for (i = 0; i < mpic->num_sources ; i++)
+               mpic_irq_write(i, MPIC_IRQ_DESTINATION,
+                       mpic_irq_read(i, MPIC_IRQ_DESTINATION) | msk);
+#endif /* CONFIG_IRQ_ALL_CPUS */
+
+       /* Set current processor priority to 0 */
+       mpic_cpu_write(MPIC_CPU_CURRENT_TASK_PRI, 0);
+
+       spin_unlock_irqrestore(&mpic_lock, flags);
+#endif /* CONFIG_SMP */
+}
+
+void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask)
+{
+       struct mpic *mpic = mpic_primary;
+
+       BUG_ON(mpic == NULL);
+
+       DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
+
+       mpic_cpu_write(MPIC_CPU_IPI_DISPATCH_0 + ipi_no * 0x10,
+                      mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
+}
+
+int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs)
+{
+       u32 irq;
+
+       irq = mpic_cpu_read(MPIC_CPU_INTACK) & MPIC_VECPRI_VECTOR_MASK;
+       DBG("%s: get_one_irq(): %d\n", mpic->name, irq);
+
+       if (mpic->cascade && irq == mpic->cascade_vec) {
+               DBG("%s: cascading ...\n", mpic->name);
+               irq = mpic->cascade(regs, mpic->cascade_data);
+               mpic_eoi(mpic);
+               return irq;
+       }
+       if (unlikely(irq == MPIC_VEC_SPURRIOUS))
+               return -1;
+       if (irq < MPIC_VEC_IPI_0) 
+               return irq + mpic->irq_offset;
+               DBG("%s: ipi %d !\n", mpic->name, irq - MPIC_VEC_IPI_0);
+       return irq - MPIC_VEC_IPI_0 + mpic->ipi_offset;
+}
+
+int mpic_get_irq(struct pt_regs *regs)
+{
+       struct mpic *mpic = mpic_primary;
+
+       BUG_ON(mpic == NULL);
+
+       return mpic_get_one_irq(mpic, regs);
+}
+
+
+#ifdef CONFIG_SMP
+void mpic_request_ipis(void)
+{
+       struct mpic *mpic = mpic_primary;
+
+       BUG_ON(mpic == NULL);
+       
+       printk("requesting IPIs ... \n");
+
+       /* IPIs are marked SA_INTERRUPT as they must run with irqs disabled */
+       request_irq(mpic->ipi_offset+0, mpic_ipi_action, SA_INTERRUPT,
+                   "IPI0 (call function)", mpic);
+       request_irq(mpic->ipi_offset+1, mpic_ipi_action, SA_INTERRUPT,
+                  "IPI1 (reschedule)", mpic);
+       request_irq(mpic->ipi_offset+2, mpic_ipi_action, SA_INTERRUPT,
+                  "IPI2 (unused)", mpic);
+       request_irq(mpic->ipi_offset+3, mpic_ipi_action, SA_INTERRUPT,
+                  "IPI3 (debugger break)", mpic);
+
+       printk("IPIs requested... \n");
+}
+#endif /* CONFIG_SMP */
diff --git a/arch/ppc64/kernel/mpic.h b/arch/ppc64/kernel/mpic.h
new file mode 100644 (file)
index 0000000..571b3c9
--- /dev/null
@@ -0,0 +1,267 @@
+#include <linux/irq.h>
+
+/*
+ * Global registers
+ */
+
+#define MPIC_GREG_BASE                 0x01000
+
+#define MPIC_GREG_FEATURE_0            0x00000
+#define                MPIC_GREG_FEATURE_LAST_SRC_MASK         0x07ff0000
+#define                MPIC_GREG_FEATURE_LAST_SRC_SHIFT        16
+#define                MPIC_GREG_FEATURE_LAST_CPU_MASK         0x00001f00
+#define                MPIC_GREG_FEATURE_LAST_CPU_SHIFT        8
+#define                MPIC_GREG_FEATURE_VERSION_MASK          0xff
+#define MPIC_GREG_FEATURE_1            0x00010
+#define MPIC_GREG_GLOBAL_CONF_0                0x00020
+#define                MPIC_GREG_GCONF_RESET                   0x80000000
+#define                MPIC_GREG_GCONF_8259_PTHROU_DIS         0x20000000
+#define                MPIC_GREG_GCONF_BASE_MASK               0x000fffff
+#define MPIC_GREG_GLOBAL_CONF_1                0x00030
+#define MPIC_GREG_VENDOR_0             0x00040
+#define MPIC_GREG_VENDOR_1             0x00050
+#define MPIC_GREG_VENDOR_2             0x00060
+#define MPIC_GREG_VENDOR_3             0x00070
+#define MPIC_GREG_VENDOR_ID            0x00080
+#define        MPIC_GREG_VENDOR_ID_STEPPING_MASK       0x00ff0000
+#define        MPIC_GREG_VENDOR_ID_STEPPING_SHIFT      16
+#define        MPIC_GREG_VENDOR_ID_DEVICE_ID_MASK      0x0000ff00
+#define        MPIC_GREG_VENDOR_ID_DEVICE_ID_SHIFT     8
+#define        MPIC_GREG_VENDOR_ID_VENDOR_ID_MASK      0x000000ff
+#define MPIC_GREG_PROCESSOR_INIT       0x00090
+#define MPIC_GREG_IPI_VECTOR_PRI_0     0x000a0
+#define MPIC_GREG_IPI_VECTOR_PRI_1     0x000b0
+#define MPIC_GREG_IPI_VECTOR_PRI_2     0x000c0
+#define MPIC_GREG_IPI_VECTOR_PRI_3     0x000d0
+#define MPIC_GREG_SPURIOUS             0x000e0
+#define MPIC_GREG_TIMER_FREQ           0x000f0
+
+/*
+ *
+ * Timer registers
+ */
+#define MPIC_TIMER_BASE                        0x01100
+#define MPIC_TIMER_STRIDE              0x40
+
+#define MPIC_TIMER_CURRENT_CNT         0x00000
+#define MPIC_TIMER_BASE_CNT            0x00010
+#define MPIC_TIMER_VECTOR_PRI          0x00020
+#define MPIC_TIMER_DESTINATION         0x00030
+
+/*
+ * Per-Processor registers
+ */
+
+#define MPIC_CPU_THISBASE              0x00000
+#define MPIC_CPU_BASE                  0x20000
+#define MPIC_CPU_STRIDE                        0x01000
+
+#define MPIC_CPU_IPI_DISPATCH_0                0x00040
+#define MPIC_CPU_IPI_DISPATCH_1                0x00050
+#define MPIC_CPU_IPI_DISPATCH_2                0x00060
+#define MPIC_CPU_IPI_DISPATCH_3                0x00070
+#define MPIC_CPU_CURRENT_TASK_PRI      0x00080
+#define        MPIC_CPU_TASKPRI_MASK                   0x0000000f
+#define MPIC_CPU_WHOAMI                        0x00090
+#define        MPIC_CPU_WHOAMI_MASK                    0x0000001f
+#define MPIC_CPU_INTACK                        0x000a0
+#define MPIC_CPU_EOI                   0x000b0
+
+/*
+ * Per-source registers
+ */
+
+#define MPIC_IRQ_BASE                  0x10000
+#define MPIC_IRQ_STRIDE                        0x00020
+#define MPIC_IRQ_VECTOR_PRI            0x00000
+#define        MPIC_VECPRI_MASK                        0x80000000
+#define        MPIC_VECPRI_ACTIVITY                    0x40000000      /* Read Only */
+#define        MPIC_VECPRI_PRIORITY_MASK               0x000f0000
+#define        MPIC_VECPRI_PRIORITY_SHIFT              16
+#define        MPIC_VECPRI_VECTOR_MASK                 0x000007ff
+#define        MPIC_VECPRI_POLARITY_POSITIVE           0x00800000
+#define        MPIC_VECPRI_POLARITY_NEGATIVE           0x00000000
+#define        MPIC_VECPRI_POLARITY_MASK               0x00800000
+#define        MPIC_VECPRI_SENSE_LEVEL                 0x00400000
+#define        MPIC_VECPRI_SENSE_EDGE                  0x00000000
+#define        MPIC_VECPRI_SENSE_MASK                  0x00400000
+#define MPIC_IRQ_DESTINATION           0x00010
+
+#define MPIC_MAX_IRQ_SOURCES   2048
+#define MPIC_MAX_CPUS          32
+#define MPIC_MAX_ISU           32
+
+/*
+ * Special vector numbers (internal use only)
+ */
+#define MPIC_VEC_SPURRIOUS     255
+#define MPIC_VEC_IPI_3         254
+#define MPIC_VEC_IPI_2         253
+#define MPIC_VEC_IPI_1         252
+#define MPIC_VEC_IPI_0         251
+
+/* unused */
+#define MPIC_VEC_TIMER_3       250
+#define MPIC_VEC_TIMER_2       249
+#define MPIC_VEC_TIMER_1       248
+#define MPIC_VEC_TIMER_0       247
+
+/* Type definition of the cascade handler */
+typedef int (*mpic_cascade_t)(struct pt_regs *regs, void *data);
+
+#ifdef CONFIG_MPIC_BROKEN_U3
+/* Fixup table entry */
+struct mpic_irq_fixup
+{
+       u8 __iomem      *base;
+       unsigned int   irq;
+};
+#endif /* CONFIG_MPIC_BROKEN_U3 */
+
+
+/* The instance data of a given MPIC */
+struct mpic
+{
+       /* The "linux" controller struct */
+       hw_irq_controller       hc_irq;
+#ifdef CONFIG_SMP
+       hw_irq_controller       hc_ipi;
+#endif
+       const char              *name;
+       /* Flags */
+       unsigned int            flags;
+       /* How many irq sources in a given ISU */
+       unsigned int            isu_size;
+       unsigned int            isu_shift;
+       unsigned int            isu_mask;
+       /* Offset of irq vector numbers */
+       unsigned int            irq_offset;     
+       unsigned int            irq_count;
+       /* Offset of ipi vector numbers */
+       unsigned int            ipi_offset;
+       /* Number of sources */
+       unsigned int            num_sources;
+       /* Number of CPUs */
+       unsigned int            num_cpus;
+       /* cascade handler */
+       mpic_cascade_t          cascade;
+       void                    *cascade_data;
+       unsigned int            cascade_vec;
+       /* senses array */
+       unsigned char           *senses;
+       unsigned int            senses_count;
+
+#ifdef CONFIG_MPIC_BROKEN_U3
+       /* The fixup table */
+       struct mpic_irq_fixup   *fixups;
+       spinlock_t              fixup_lock;
+#endif
+
+       /* The various ioremap'ed bases */
+       volatile u32 __iomem    *gregs;
+       volatile u32 __iomem    *tmregs;
+       volatile u32 __iomem    *cpuregs[MPIC_MAX_CPUS];
+       volatile u32 __iomem    *isus[MPIC_MAX_ISU];
+
+       /* link */
+       struct mpic             *next;
+};
+
+/* This is the primary controller, only that one has IPIs and
+ * has afinity control. A non-primary MPIC always uses CPU0
+ * registers only
+ */
+#define MPIC_PRIMARY                   0x00000001
+/* Set this for a big-endian MPIC */
+#define MPIC_BIG_ENDIAN                        0x00000002
+/* Broken U3 MPIC */
+#define MPIC_BROKEN_U3                 0x00000004
+/* Broken IPI registers (autodetected) */
+#define MPIC_BROKEN_IPI                        0x00000008
+/* MPIC wants a reset */
+#define MPIC_WANTS_RESET               0x00000010
+
+/* Allocate the controller structure and setup the linux irq descs
+ * for the range if interrupts passed in. No HW initialization is
+ * actually performed.
+ * 
+ * @phys_addr: physial base address of the MPIC
+ * @flags:     flags, see constants above
+ * @isu_size:  number of interrupts in an ISU. Use 0 to use a
+ *              standard ISU-less setup (aka powermac)
+ * @irq_offset: first irq number to assign to this mpic
+ * @irq_count:  number of irqs to use with this mpic IRQ sources. Pass 0
+ *             to match the number of sources
+ * @ipi_offset: first irq number to assign to this mpic IPI sources,
+ *             used only on primary mpic
+ * @senses:    array of sense values
+ * @senses_num: number of entries in the array
+ *
+ * Note about the sense array. If none is passed, all interrupts are
+ * setup to be level negative unless MPIC_BROKEN_U3 is set in which
+ * case they are edge positive (and the array is ignored anyway).
+ * The values in the array start at the first source of the MPIC,
+ * that is senses[0] correspond to linux irq "irq_offset".
+ */
+extern struct mpic *mpic_alloc(unsigned long phys_addr,
+                              unsigned int flags,
+                              unsigned int isu_size,
+                              unsigned int irq_offset,
+                              unsigned int irq_count,
+                              unsigned int ipi_offset,
+                              unsigned char *senses,
+                              unsigned int senses_num,
+                              const char *name);
+
+/* Assign ISUs, to call before mpic_init()
+ *
+ * @mpic:      controller structure as returned by mpic_alloc()
+ * @isu_num:   ISU number
+ * @phys_addr: physical address of the ISU
+ */
+extern void mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
+                           unsigned long phys_addr);
+
+/* Initialize the controller. After this has been called, none of the above
+ * should be called again for this mpic
+ */
+extern void mpic_init(struct mpic *mpic);
+
+/* Setup a cascade. Currently, only one cascade is supported this
+ * way, though you can always do a normal request_irq() and add
+ * other cascades this way. You should call this _after_ having
+ * added all the ISUs
+ *
+ * @irq_no:    "linux" irq number of the cascade (that is offset'ed vector)
+ * @handler:   cascade handler function
+ */
+extern void mpic_setup_cascade(unsigned int irq_no, mpic_cascade_t hanlder,
+                              void *data);
+
+/*
+ * All of the following functions must only be used after the
+ * ISUs have been assigned and the controller fully initialized
+ * with mpic_init()
+ */
+
+
+/* Change/Read the priority of an interrupt. Default is 8 for irqs and
+ * 10 for IPIs. You can call this on both IPIs and IRQ numbers, but the
+ * IPI number is then the offset'ed (linux irq number mapped to the IPI)
+ */
+extern void mpic_irq_set_priority(unsigned int irq, unsigned int pri);
+extern unsigned int mpic_irq_get_priority(unsigned int irq);
+
+/* Setup a non-boot CPU */
+extern void mpic_setup_this_cpu(void);
+
+/* Request IPIs on primary mpic */
+extern void mpic_request_ipis(void);
+
+/* Send an IPI (non offseted number 0..3) */
+extern void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask);
+
+/* Fetch interrupt from a given mpic */
+extern int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs);
+/* This one gets to the primary mpic */
+extern int mpic_get_irq(struct pt_regs *regs);
diff --git a/arch/ppc64/kernel/pSeries_smp.c b/arch/ppc64/kernel/pSeries_smp.c
new file mode 100644 (file)
index 0000000..6eba926
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * SMP support for pSeries machines.
+ *
+ * Dave Engebretsen, Peter Bergner, and
+ * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com
+ *
+ * Plus various changes from other IBM teams...
+ *
+ *      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.
+ */
+
+#undef DEBUG
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/cache.h>
+#include <linux/err.h>
+#include <linux/sysdev.h>
+#include <linux/cpu.h>
+
+#include <asm/ptrace.h>
+#include <asm/atomic.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/smp.h>
+#include <asm/naca.h>
+#include <asm/paca.h>
+#include <asm/time.h>
+#include <asm/ppcdebug.h>
+#include <asm/machdep.h>
+#include <asm/xics.h>
+#include <asm/cputable.h>
+#include <asm/system.h>
+#include <asm/rtas.h>
+#include <asm/plpar_wrappers.h>
+
+#include "mpic.h"
+
+#ifdef DEBUG
+#define DBG(fmt...) udbg_printf(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+extern void pseries_secondary_smp_init(unsigned long); 
+
+/* Get state of physical CPU.
+ * Return codes:
+ *     0       - The processor is in the RTAS stopped state
+ *     1       - stop-self is in progress
+ *     2       - The processor is not in the RTAS stopped state
+ *     -1      - Hardware Error
+ *     -2      - Hardware Busy, Try again later.
+ */
+static int query_cpu_stopped(unsigned int pcpu)
+{
+       int cpu_status;
+       int status, qcss_tok;
+
+       qcss_tok = rtas_token("query-cpu-stopped-state");
+       if (qcss_tok == RTAS_UNKNOWN_SERVICE)
+               return -1;
+       status = rtas_call(qcss_tok, 1, 2, &cpu_status, pcpu);
+       if (status != 0) {
+               printk(KERN_ERR
+                      "RTAS query-cpu-stopped-state failed: %i\n", status);
+               return status;
+       }
+
+       return cpu_status;
+}
+
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+int __cpu_disable(void)
+{
+       /* FIXME: go put this in a header somewhere */
+       extern void xics_migrate_irqs_away(void);
+
+       systemcfg->processorCount--;
+
+       /*fix boot_cpuid here*/
+       if (smp_processor_id() == boot_cpuid)
+               boot_cpuid = any_online_cpu(cpu_online_map);
+
+       /* FIXME: abstract this to not be platform specific later on */
+       xics_migrate_irqs_away();
+       return 0;
+}
+
+void __cpu_die(unsigned int cpu)
+{
+       int tries;
+       int cpu_status;
+       unsigned int pcpu = get_hard_smp_processor_id(cpu);
+
+       for (tries = 0; tries < 25; tries++) {
+               cpu_status = query_cpu_stopped(pcpu);
+               if (cpu_status == 0 || cpu_status == -1)
+                       break;
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(HZ/5);
+       }
+       if (cpu_status != 0) {
+               printk("Querying DEAD? cpu %i (%i) shows %i\n",
+                      cpu, pcpu, cpu_status);
+       }
+
+       /* Isolation and deallocation are definatly done by
+        * drslot_chrp_cpu.  If they were not they would be
+        * done here.  Change isolate state to Isolate and
+        * change allocation-state to Unusable.
+        */
+       paca[cpu].cpu_start = 0;
+}
+
+/* Search all cpu device nodes for an offline logical cpu.  If a
+ * device node has a "ibm,my-drc-index" property (meaning this is an
+ * LPAR), paranoid-check whether we own the cpu.  For each "thread"
+ * of a cpu, if it is offline and has the same hw index as before,
+ * grab that in preference.
+ */
+static unsigned int find_physical_cpu_to_start(unsigned int old_hwindex)
+{
+       struct device_node *np = NULL;
+       unsigned int best = -1U;
+
+       while ((np = of_find_node_by_type(np, "cpu"))) {
+               int nr_threads, len;
+               u32 *index = (u32 *)get_property(np, "ibm,my-drc-index", NULL);
+               u32 *tid = (u32 *)
+                       get_property(np, "ibm,ppc-interrupt-server#s", &len);
+
+               if (!tid)
+                       tid = (u32 *)get_property(np, "reg", &len);
+
+               if (!tid)
+                       continue;
+
+               /* If there is a drc-index, make sure that we own
+                * the cpu.
+                */
+               if (index) {
+                       int state;
+                       int rc = rtas_get_sensor(9003, *index, &state);
+                       if (rc != 0 || state != 1)
+                               continue;
+               }
+
+               nr_threads = len / sizeof(u32);
+
+               while (nr_threads--) {
+                       if (0 == query_cpu_stopped(tid[nr_threads])) {
+                               best = tid[nr_threads];
+                               if (best == old_hwindex)
+                                       goto out;
+                       }
+               }
+       }
+out:
+       of_node_put(np);
+       return best;
+}
+
+/**
+ * smp_startup_cpu() - start the given cpu
+ *
+ * At boot time, there is nothing to do.  At run-time, call RTAS with
+ * the appropriate start location, if the cpu is in the RTAS stopped
+ * state.
+ *
+ * Returns:
+ *     0       - failure
+ *     1       - success
+ */
+static inline int __devinit smp_startup_cpu(unsigned int lcpu)
+{
+       int status;
+       unsigned long start_here = __pa((u32)*((unsigned long *)
+                                              pseries_secondary_smp_init));
+       unsigned int pcpu;
+
+       /* At boot time the cpus are already spinning in hold
+        * loops, so nothing to do. */
+       if (system_state < SYSTEM_RUNNING)
+               return 1;
+
+       pcpu = find_physical_cpu_to_start(get_hard_smp_processor_id(lcpu));
+       if (pcpu == -1U) {
+               printk(KERN_INFO "No more cpus available, failing\n");
+               return 0;
+       }
+
+       /* Fixup atomic count: it exited inside IRQ handler. */
+       paca[lcpu].__current->thread_info->preempt_count        = 0;
+
+       /* At boot this is done in prom.c. */
+       paca[lcpu].hw_cpu_id = pcpu;
+
+       status = rtas_call(rtas_token("start-cpu"), 3, 1, NULL,
+                          pcpu, start_here, lcpu);
+       if (status != 0) {
+               printk(KERN_ERR "start-cpu failed: %i\n", status);
+               return 0;
+       }
+       return 1;
+}
+#else /* ... CONFIG_HOTPLUG_CPU */
+static inline int __devinit smp_startup_cpu(unsigned int lcpu)
+{
+       return 1;
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+static inline void smp_xics_do_message(int cpu, int msg)
+{
+       set_bit(msg, &xics_ipi_message[cpu].value);
+       mb();
+       xics_cause_IPI(cpu);
+}
+
+static void smp_xics_message_pass(int target, int msg)
+{
+       unsigned int i;
+
+       if (target < NR_CPUS) {
+               smp_xics_do_message(target, msg);
+       } else {
+               for_each_online_cpu(i) {
+                       if (target == MSG_ALL_BUT_SELF
+                           && i == smp_processor_id())
+                               continue;
+                       smp_xics_do_message(i, msg);
+               }
+       }
+}
+
+extern void xics_request_IPIs(void);
+
+static int __init smp_xics_probe(void)
+{
+       xics_request_IPIs();
+
+       return cpus_weight(cpu_possible_map);
+}
+
+static void __devinit smp_xics_setup_cpu(int cpu)
+{
+       if (cpu != boot_cpuid)
+               xics_setup_cpu();
+}
+
+static spinlock_t timebase_lock = SPIN_LOCK_UNLOCKED;
+static unsigned long timebase = 0;
+
+static void __devinit pSeries_give_timebase(void)
+{
+       spin_lock(&timebase_lock);
+       rtas_call(rtas_token("freeze-time-base"), 0, 1, NULL);
+       timebase = get_tb();
+       spin_unlock(&timebase_lock);
+
+       while (timebase)
+               barrier();
+       rtas_call(rtas_token("thaw-time-base"), 0, 1, NULL);
+}
+
+static void __devinit pSeries_take_timebase(void)
+{
+       while (!timebase)
+               barrier();
+       spin_lock(&timebase_lock);
+       set_tb(timebase >> 32, timebase & 0xffffffff);
+       timebase = 0;
+       spin_unlock(&timebase_lock);
+}
+
+static void __devinit pSeries_late_setup_cpu(int cpu)
+{
+       extern unsigned int default_distrib_server;
+
+       if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) {
+               vpa_init(cpu); 
+       }
+
+#ifdef CONFIG_IRQ_ALL_CPUS
+       /* Put the calling processor into the GIQ.  This is really only
+        * necessary from a secondary thread as the OF start-cpu interface
+        * performs this function for us on primary threads.
+        */
+       /* TODO: 9005 is #defined in rtas-proc.c -- move to a header */
+       rtas_set_indicator(9005, default_distrib_server, 1);
+#endif
+}
+
+
+void __devinit smp_pSeries_kick_cpu(int nr)
+{
+       BUG_ON(nr < 0 || nr >= NR_CPUS);
+
+       if (!smp_startup_cpu(nr))
+               return;
+
+       /*
+        * The processor is currently spinning, waiting for the
+        * cpu_start field to become non-zero After we set cpu_start,
+        * the processor will continue on to secondary_start
+        */
+       paca[nr].cpu_start = 1;
+}
+
+static struct smp_ops_t pSeries_mpic_smp_ops = {
+       .message_pass   = smp_mpic_message_pass,
+       .probe          = smp_mpic_probe,
+       .kick_cpu       = smp_pSeries_kick_cpu,
+       .setup_cpu      = smp_mpic_setup_cpu,
+       .late_setup_cpu = pSeries_late_setup_cpu,
+};
+
+static struct smp_ops_t pSeries_xics_smp_ops = {
+       .message_pass   = smp_xics_message_pass,
+       .probe          = smp_xics_probe,
+       .kick_cpu       = smp_pSeries_kick_cpu,
+       .setup_cpu      = smp_xics_setup_cpu,
+       .late_setup_cpu = pSeries_late_setup_cpu,
+};
+
+/* This is called very early */
+void __init smp_init_pSeries(void)
+{
+       int ret, i;
+
+       DBG(" -> smp_init_pSeries()\n");
+
+       if (naca->interrupt_controller == IC_OPEN_PIC)
+               smp_ops = &pSeries_mpic_smp_ops;
+       else
+               smp_ops = &pSeries_xics_smp_ops;
+
+       /* Start secondary threads on SMT systems; primary threads
+        * are already in the running state.
+        */
+       for_each_present_cpu(i) {
+               if (query_cpu_stopped(get_hard_smp_processor_id(i)) == 0) {
+                       printk("%16.16x : starting thread\n", i);
+                       DBG("%16.16x : starting thread\n", i);
+                       rtas_call(rtas_token("start-cpu"), 3, 1, &ret,
+                                 get_hard_smp_processor_id(i),
+                                 __pa((u32)*((unsigned long *)
+                                             pseries_secondary_smp_init)),
+                                 i);
+               }
+       }
+
+       if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR)
+               vpa_init(boot_cpuid);
+
+       /* Non-lpar has additional take/give timebase */
+       if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) {
+               smp_ops->give_timebase = pSeries_give_timebase;
+               smp_ops->take_timebase = pSeries_take_timebase;
+       }
+
+       DBG(" <- smp_init_pSeries()\n");
+}
+
diff --git a/arch/ppc64/kernel/pci_direct_iommu.c b/arch/ppc64/kernel/pci_direct_iommu.c
new file mode 100644 (file)
index 0000000..8faabca
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Support for DMA from PCI devices to main memory on
+ * machines without an iommu or with directly addressable
+ * RAM (typically a pmac with 2Gb of RAM or less)
+ *
+ * Copyright (C) 2003 Benjamin 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 <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/sections.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/abs_addr.h>
+
+#include "pci.h"
+
+static void *pci_direct_alloc_consistent(struct pci_dev *hwdev, size_t size,
+                                  dma_addr_t *dma_handle)
+{
+       void *ret;
+
+       ret = (void *)__get_free_pages(GFP_ATOMIC, get_order(size));
+       if (ret != NULL) {
+               memset(ret, 0, size);
+               *dma_handle = virt_to_abs(ret);
+       }
+       return ret;
+}
+
+static void pci_direct_free_consistent(struct pci_dev *hwdev, size_t size,
+                                void *vaddr, dma_addr_t dma_handle)
+{
+       free_pages((unsigned long)vaddr, get_order(size));
+}
+
+static dma_addr_t pci_direct_map_single(struct pci_dev *hwdev, void *ptr,
+               size_t size, enum dma_data_direction direction)
+{
+       return virt_to_abs(ptr);
+}
+
+static void pci_direct_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
+               size_t size, enum dma_data_direction direction)
+{
+}
+
+static int pci_direct_map_sg(struct pci_dev *hwdev, struct scatterlist *sg,
+               int nents, enum dma_data_direction direction)
+{
+       int i;
+
+       for (i = 0; i < nents; i++, sg++) {
+               sg->dma_address = page_to_phys(sg->page) + sg->offset;
+               sg->dma_length = sg->length;
+       }
+
+       return nents;
+}
+
+static void pci_direct_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg,
+               int nents, enum dma_data_direction direction)
+{
+}
+
+void __init pci_direct_iommu_init(void)
+{
+       pci_dma_ops.pci_alloc_consistent = pci_direct_alloc_consistent;
+       pci_dma_ops.pci_free_consistent = pci_direct_free_consistent;
+       pci_dma_ops.pci_map_single = pci_direct_map_single;
+       pci_dma_ops.pci_unmap_single = pci_direct_unmap_single;
+       pci_dma_ops.pci_map_sg = pci_direct_map_sg;
+       pci_dma_ops.pci_unmap_sg = pci_direct_unmap_sg;
+}
diff --git a/arch/ppc64/lib/sstep.c b/arch/ppc64/lib/sstep.c
new file mode 100644 (file)
index 0000000..ce9ecc6
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Single-step support.
+ *
+ * Copyright (C) 2004 Paul Mackerras <paulus@au.ibm.com>, IBM
+ *
+ * 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 <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <asm/sstep.h>
+#include <asm/processor.h>
+
+extern char SystemCall_common[];
+
+/* Bits in SRR1 that are copied from MSR */
+#define MSR_MASK       0xffffffff87c0ffff
+
+/*
+ * Determine whether a conditional branch instruction would branch.
+ */
+static int branch_taken(unsigned int instr, struct pt_regs *regs)
+{
+       unsigned int bo = (instr >> 21) & 0x1f;
+       unsigned int bi;
+
+       if ((bo & 4) == 0) {
+               /* decrement counter */
+               --regs->ctr;
+               if (((bo >> 1) & 1) ^ (regs->ctr == 0))
+                       return 0;
+       }
+       if ((bo & 0x10) == 0) {
+               /* check bit from CR */
+               bi = (instr >> 16) & 0x1f;
+               if (((regs->ccr >> (31 - bi)) & 1) != ((bo >> 3) & 1))
+                       return 0;
+       }
+       return 1;
+}
+
+/*
+ * Emulate instructions that cause a transfer of control.
+ * Returns 1 if the step was emulated, 0 if not,
+ * or -1 if the instruction is one that should not be stepped,
+ * such as an rfid, or a mtmsrd that would clear MSR_RI.
+ */
+int emulate_step(struct pt_regs *regs, unsigned int instr)
+{
+       unsigned int opcode, rd;
+       unsigned long int imm;
+
+       opcode = instr >> 26;
+       switch (opcode) {
+       case 16:        /* bc */
+               imm = (signed short)(instr & 0xfffc);
+               if ((instr & 2) == 0)
+                       imm += regs->nip;
+               regs->nip += 4;
+               if ((regs->msr & MSR_SF) == 0)
+                       regs->nip &= 0xffffffffUL;
+               if (instr & 1)
+                       regs->link = regs->nip;
+               if (branch_taken(instr, regs))
+                       regs->nip = imm;
+               return 1;
+       case 17:        /* sc */
+               /*
+                * N.B. this uses knowledge about how the syscall
+                * entry code works.  If that is changed, this will
+                * need to be changed also.
+                */
+               regs->gpr[9] = regs->gpr[13];
+               regs->gpr[11] = regs->nip + 4;
+               regs->gpr[12] = regs->msr & MSR_MASK;
+               regs->gpr[13] = (unsigned long) get_paca();
+               regs->nip = (unsigned long) &SystemCall_common;
+               regs->msr = MSR_KERNEL;
+               return 1;
+       case 18:        /* b */
+               imm = instr & 0x03fffffc;
+               if (imm & 0x02000000)
+                       imm -= 0x04000000;
+               if ((instr & 2) == 0)
+                       imm += regs->nip;
+               if (instr & 1) {
+                       regs->link = regs->nip + 4;
+                       if ((regs->msr & MSR_SF) == 0)
+                               regs->link &= 0xffffffffUL;
+               }
+               if ((regs->msr & MSR_SF) == 0)
+                       imm &= 0xffffffffUL;
+               regs->nip = imm;
+               return 1;
+       case 19:
+               switch (instr & 0x7fe) {
+               case 0x20:      /* bclr */
+               case 0x420:     /* bcctr */
+                       imm = (instr & 0x400)? regs->ctr: regs->link;
+                       regs->nip += 4;
+                       if ((regs->msr & MSR_SF) == 0) {
+                               regs->nip &= 0xffffffffUL;
+                               imm &= 0xffffffffUL;
+                       }
+                       if (instr & 1)
+                               regs->link = regs->nip;
+                       if (branch_taken(instr, regs))
+                               regs->nip = imm;
+                       return 1;
+               case 0x24:      /* rfid, scary */
+                       return -1;
+               }
+       case 31:
+               rd = (instr >> 21) & 0x1f;
+               switch (instr & 0x7fe) {
+               case 0xa6:      /* mfmsr */
+                       regs->gpr[rd] = regs->msr & MSR_MASK;
+                       regs->nip += 4;
+                       if ((regs->msr & MSR_SF) == 0)
+                               regs->nip &= 0xffffffffUL;
+                       return 1;
+               case 0x164:     /* mtmsrd */
+                       /* only MSR_EE and MSR_RI get changed if bit 15 set */
+                       /* mtmsrd doesn't change MSR_HV and MSR_ME */
+                       imm = (instr & 0x10000)? 0x8002: 0xefffffffffffefffUL;
+                       imm = (regs->msr & MSR_MASK & ~imm)
+                               | (regs->gpr[rd] & imm);
+                       if ((imm & MSR_RI) == 0)
+                               /* can't step mtmsrd that would clear MSR_RI */
+                               return -1;
+                       regs->msr = imm;
+                       regs->nip += 4;
+                       if ((imm & MSR_SF) == 0)
+                               regs->nip &= 0xffffffffUL;
+                       return 1;
+               }
+       }
+       return 0;
+}
diff --git a/arch/ppc64/xmon/setjmp.S b/arch/ppc64/xmon/setjmp.S
new file mode 100644 (file)
index 0000000..30ee643
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ * NOTE: assert(sizeof(buf) > 184)
+ */
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+
+_GLOBAL(xmon_setjmp)
+       mflr    r0
+       std     r0,0(r3)
+       std     r1,8(r3)
+       std     r2,16(r3)
+       mfcr    r0
+       std     r0,24(r3)
+       std     r13,32(r3)
+       std     r14,40(r3)
+       std     r15,48(r3)
+       std     r16,56(r3)
+       std     r17,64(r3)
+       std     r18,72(r3)
+       std     r19,80(r3)
+       std     r20,88(r3)
+       std     r21,96(r3)
+       std     r22,104(r3)
+       std     r23,112(r3)
+       std     r24,120(r3)
+       std     r25,128(r3)
+       std     r26,136(r3)
+       std     r27,144(r3)
+       std     r28,152(r3)
+       std     r29,160(r3)
+       std     r30,168(r3)
+       std     r31,176(r3)
+       li      r3,0
+       blr
+
+_GLOBAL(xmon_longjmp)
+       cmpdi   r4,0
+       bne     1f
+       li      r4,1
+1:     ld      r13,32(r3)
+       ld      r14,40(r3)
+       ld      r15,48(r3)
+       ld      r16,56(r3)
+       ld      r17,64(r3)
+       ld      r18,72(r3)
+       ld      r19,80(r3)
+       ld      r20,88(r3)
+       ld      r21,96(r3)
+       ld      r22,104(r3)
+       ld      r23,112(r3)
+       ld      r24,120(r3)
+       ld      r25,128(r3)
+       ld      r26,136(r3)
+       ld      r27,144(r3)
+       ld      r28,152(r3)
+       ld      r29,160(r3)
+       ld      r30,168(r3)
+       ld      r31,176(r3)
+       ld      r0,24(r3)
+       mtcrf   56,r0
+       ld      r0,0(r3)
+       ld      r1,8(r3)
+       ld      r2,16(r3)
+       mtlr    r0
+       mr      r3,r4
+       blr
diff --git a/arch/sh/boards/renesas/edosk7705/Makefile b/arch/sh/boards/renesas/edosk7705/Makefile
new file mode 100644 (file)
index 0000000..7fccbf2
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Makefile for the EDOSK7705 specific parts of the kernel
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+
+obj-y   := setup.o io.o
+
diff --git a/arch/sh/boards/renesas/edosk7705/io.c b/arch/sh/boards/renesas/edosk7705/io.c
new file mode 100644 (file)
index 0000000..541cea2
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * arch/sh/boards/renesas/edosk7705/io.c
+ *
+ * Copyright (C) 2001  Ian da Silva, Jeremy Siegel
+ * Based largely on io_se.c.
+ *
+ * I/O routines for Hitachi EDOSK7705 board.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/edosk7705/io.h>
+#include <asm/addrspace.h>
+
+#define SMC_IOADDR     0xA2000000
+
+#define maybebadio(name,port) \
+  printk("bad PC-like io %s for port 0x%lx at 0x%08x\n", \
+        #name, (port), (__u32) __builtin_return_address(0))
+
+/* Map the Ethernet addresses as if it is at 0x300 - 0x320 */
+unsigned long sh_edosk7705_isa_port2addr(unsigned long port)
+{
+     if (port >= 0x300 && port < 0x320) {
+         /* SMC91C96 registers are 4 byte aligned rather than the
+          * usual 2 byte!
+          */
+         return SMC_IOADDR + ( (port - 0x300) * 2);
+     }
+
+     maybebadio(sh_edosk7705_isa_port2addr, port);
+     return port;
+}
+
+/* Trying to read / write bytes on odd-byte boundaries to the Ethernet
+ * registers causes problems. So we bit-shift the value and read / write
+ * in 2 byte chunks. Setting the low byte to 0 does not cause problems
+ * now as odd byte writes are only made on the bit mask / interrupt
+ * register. This may not be the case in future Mar-2003 SJD
+ */
+unsigned char sh_edosk7705_inb(unsigned long port)
+{
+       if (port >= 0x300 && port < 0x320 && port & 0x01) {
+               return (volatile unsigned char)(generic_inw(port -1) >> 8);
+       }
+       return *(volatile unsigned char *)sh_edosk7705_isa_port2addr(port);
+}
+
+unsigned int sh_edosk7705_inl(unsigned long port)
+{
+       return *(volatile unsigned long *)port;
+}
+
+void sh_edosk7705_outb(unsigned char value, unsigned long port)
+{
+       if (port >= 0x300 && port < 0x320 && port & 0x01) {
+               generic_outw(((unsigned short)value << 8), port -1);
+               return;
+       }
+       *(volatile unsigned char *)sh_edosk7705_isa_port2addr(port) = value;
+}
+
+void sh_edosk7705_outl(unsigned int value, unsigned long port)
+{
+       *(volatile unsigned long *)port = value;
+}
+
+void sh_edosk7705_insb(unsigned long port, void *addr, unsigned long count)
+{
+       unsigned char *p = addr;
+       while (count--) *p++ = sh_edosk7705_inb(port);
+}
+
+void sh_edosk7705_insl(unsigned long port, void *addr, unsigned long count)
+{
+       unsigned long *p = (unsigned long*)addr;
+       while (count--)
+               *p++ = *(volatile unsigned long *)port;
+}
+
+void sh_edosk7705_outsb(unsigned long port, const void *addr, unsigned long count)
+{
+       unsigned char *p = (unsigned char*)addr;
+       while (count--) sh_edosk7705_outb(*p++, port);
+}
+
+void sh_edosk7705_outsl(unsigned long port, const void *addr, unsigned long count)
+{
+       unsigned long *p = (unsigned long*)addr;
+       while (count--) sh_edosk7705_outl(*p++, port);
+}
+
diff --git a/arch/sh/boards/renesas/edosk7705/setup.c b/arch/sh/boards/renesas/edosk7705/setup.c
new file mode 100644 (file)
index 0000000..8b6f0c2
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * arch/sh/boards/renesas/edosk7705/setup.c
+ *
+ * Copyright (C) 2000  Kazumoto Kojima
+ *
+ * Hitachi SolutionEngine Support.
+ *
+ * Modified for edosk7705 development
+ * board by S. Dunn, 2003.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/machvec.h>
+#include <asm/machvec_init.h>
+#include <asm/edosk7705/io.h>
+
+static void init_edosk7705(void);
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_edosk7705 __initmv = {
+       .mv_nr_irqs             = 80,
+
+       .mv_inb                 = sh_edosk7705_inb,
+       .mv_inl                 = sh_edosk7705_inl,
+       .mv_outb                = sh_edosk7705_outb,
+       .mv_outl                = sh_edosk7705_outl,
+
+       .mv_inl_p               = sh_edosk7705_inl,
+       .mv_outl_p              = sh_edosk7705_outl,
+
+       .mv_insb                = sh_edosk7705_insb,
+       .mv_insl                = sh_edosk7705_insl,
+       .mv_outsb               = sh_edosk7705_outsb,
+       .mv_outsl               = sh_edosk7705_outsl,
+
+       .mv_isa_port2addr       = sh_edosk7705_isa_port2addr,
+       .mv_init_irq            = init_edosk7705,
+};
+ALIAS_MV(edosk7705)
+
+static void __init init_edosk7705(void)
+{
+       /* This is the Ethernet interrupt */
+       make_imask_irq(0x09);
+}
+
+const char *get_system_type(void)
+{
+       return "EDOSK7705";
+}
+
+void __init platform_setup(void)
+{
+       /* Nothing .. */
+}
+
diff --git a/arch/sh/boards/se/73180/Makefile b/arch/sh/boards/se/73180/Makefile
new file mode 100644 (file)
index 0000000..8f63886
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the 73180 SolutionEngine specific parts of the kernel
+#
+
+obj-y   := setup.o io.o irq.o
+
+obj-$(CONFIG_HEARTBEAT) += led.o
diff --git a/arch/sh/boards/se/73180/io.c b/arch/sh/boards/se/73180/io.c
new file mode 100644 (file)
index 0000000..73648cb
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * arch/sh/boards/se/73180/io.c
+ *
+ * Copyright (C) 2003 YOSHII Takashi <yoshii-takashi@hitachi-ul.co.jp>
+ * Based on arch/sh/boards/se/7300/io.c
+ *
+ * I/O routine for SH-Mobile3 73180 SolutionEngine.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <asm/mach/se73180.h>
+#include <asm/io.h>
+
+#define badio(fn, a) panic("bad i/o operation %s for %08lx.", #fn, a)
+
+struct iop {
+       unsigned long start, end;
+       unsigned long base;
+       struct iop *(*check) (struct iop * p, unsigned long port);
+       unsigned char (*inb) (struct iop * p, unsigned long port);
+       unsigned short (*inw) (struct iop * p, unsigned long port);
+       void (*outb) (struct iop * p, unsigned char value, unsigned long port);
+       void (*outw) (struct iop * p, unsigned short value, unsigned long port);
+};
+
+struct iop *
+simple_check(struct iop *p, unsigned long port)
+{
+       if ((p->start <= port) && (port <= p->end))
+               return p;
+       else
+               badio(check, port);
+}
+
+struct iop *
+ide_check(struct iop *p, unsigned long port)
+{
+       if (((0x1f0 <= port) && (port <= 0x1f7)) || (port == 0x3f7))
+               return p;
+       return NULL;
+}
+
+unsigned char
+simple_inb(struct iop *p, unsigned long port)
+{
+       return *(unsigned char *) (p->base + port);
+}
+
+unsigned short
+simple_inw(struct iop *p, unsigned long port)
+{
+       return *(unsigned short *) (p->base + port);
+}
+
+void
+simple_outb(struct iop *p, unsigned char value, unsigned long port)
+{
+       *(unsigned char *) (p->base + port) = value;
+}
+
+void
+simple_outw(struct iop *p, unsigned short value, unsigned long port)
+{
+       *(unsigned short *) (p->base + port) = value;
+}
+
+unsigned char
+pcc_inb(struct iop *p, unsigned long port)
+{
+       unsigned long addr = p->base + port + 0x40000;
+       unsigned long v;
+
+       if (port & 1)
+               addr += 0x00400000;
+       v = *(volatile unsigned char *) addr;
+       return v;
+}
+
+void
+pcc_outb(struct iop *p, unsigned char value, unsigned long port)
+{
+       unsigned long addr = p->base + port + 0x40000;
+
+       if (port & 1)
+               addr += 0x00400000;
+       *(volatile unsigned char *) addr = value;
+}
+
+unsigned char
+bad_inb(struct iop *p, unsigned long port)
+{
+       badio(inb, port);
+}
+
+void
+bad_outb(struct iop *p, unsigned char value, unsigned long port)
+{
+       badio(inw, port);
+}
+
+/* MSTLANEX01 LAN at 0xb400:0000 */
+static struct iop laniop = {
+       .start = 0x300,
+       .end = 0x30f,
+       .base = 0xb4000000,
+       .check = simple_check,
+       .inb = simple_inb,
+       .inw = simple_inw,
+       .outb = simple_outb,
+       .outw = simple_outw,
+};
+
+/* NE2000 pc card NIC */
+static struct iop neiop = {
+       .start = 0x280,
+       .end = 0x29f,
+       .base = 0xb0600000 + 0x80,      /* soft 0x280 -> hard 0x300 */
+       .check = simple_check,
+       .inb = pcc_inb,
+       .inw = simple_inw,
+       .outb = pcc_outb,
+       .outw = simple_outw,
+};
+
+/* CF in CF slot */
+static struct iop cfiop = {
+       .base = 0xb0600000,
+       .check = ide_check,
+       .inb = pcc_inb,
+       .inw = simple_inw,
+       .outb = pcc_outb,
+       .outw = simple_outw,
+};
+
+static __inline__ struct iop *
+port2iop(unsigned long port)
+{
+       if (0) ;
+#if defined(CONFIG_SMC91111)
+       else if (laniop.check(&laniop, port))
+               return &laniop;
+#endif
+#if defined(CONFIG_NE2000)
+       else if (neiop.check(&neiop, port))
+               return &neiop;
+#endif
+#if defined(CONFIG_IDE)
+       else if (cfiop.check(&cfiop, port))
+               return &cfiop;
+#endif
+       else
+               return &neiop;  /* fallback */
+}
+
+static inline void
+delay(void)
+{
+       ctrl_inw(0xac000000);
+       ctrl_inw(0xac000000);
+}
+
+unsigned char
+sh73180se_inb(unsigned long port)
+{
+       struct iop *p = port2iop(port);
+       return (p->inb) (p, port);
+}
+
+unsigned char
+sh73180se_inb_p(unsigned long port)
+{
+       unsigned char v = sh73180se_inb(port);
+       delay();
+       return v;
+}
+
+unsigned short
+sh73180se_inw(unsigned long port)
+{
+       struct iop *p = port2iop(port);
+       return (p->inw) (p, port);
+}
+
+unsigned int
+sh73180se_inl(unsigned long port)
+{
+       badio(inl, port);
+}
+
+void
+sh73180se_outb(unsigned char value, unsigned long port)
+{
+       struct iop *p = port2iop(port);
+       (p->outb) (p, value, port);
+}
+
+void
+sh73180se_outb_p(unsigned char value, unsigned long port)
+{
+       sh73180se_outb(value, port);
+       delay();
+}
+
+void
+sh73180se_outw(unsigned short value, unsigned long port)
+{
+       struct iop *p = port2iop(port);
+       (p->outw) (p, value, port);
+}
+
+void
+sh73180se_outl(unsigned int value, unsigned long port)
+{
+       badio(outl, port);
+}
+
+void
+sh73180se_insb(unsigned long port, void *addr, unsigned long count)
+{
+       unsigned char *a = addr;
+       struct iop *p = port2iop(port);
+       while (count--)
+               *a++ = (p->inb) (p, port);
+}
+
+void
+sh73180se_insw(unsigned long port, void *addr, unsigned long count)
+{
+       unsigned short *a = addr;
+       struct iop *p = port2iop(port);
+       while (count--)
+               *a++ = (p->inw) (p, port);
+}
+
+void
+sh73180se_insl(unsigned long port, void *addr, unsigned long count)
+{
+       badio(insl, port);
+}
+
+void
+sh73180se_outsb(unsigned long port, const void *addr, unsigned long count)
+{
+       unsigned char *a = (unsigned char *) addr;
+       struct iop *p = port2iop(port);
+       while (count--)
+               (p->outb) (p, *a++, port);
+}
+
+void
+sh73180se_outsw(unsigned long port, const void *addr, unsigned long count)
+{
+       unsigned short *a = (unsigned short *) addr;
+       struct iop *p = port2iop(port);
+       while (count--)
+               (p->outw) (p, *a++, port);
+}
+
+void
+sh73180se_outsl(unsigned long port, const void *addr, unsigned long count)
+{
+       badio(outsw, port);
+}
diff --git a/arch/sh/boards/se/73180/irq.c b/arch/sh/boards/se/73180/irq.c
new file mode 100644 (file)
index 0000000..70f04ca
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * arch/sh/boards/se/73180/irq.c
+ *
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ * Based on arch/sh/boards/se/7300/irq.c
+ *
+ * Modified for SH-Mobile SolutionEngine 73180 Support
+ *              by YOSHII Takashi <yoshii-takashi@hitachi-ul.co.jp>
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/mach/se73180.h>
+
+static int
+intreq2irq(int i)
+{
+       if (i == 5)
+               return 10;
+       return 32 + 7 - i;
+}
+
+static int
+irq2intreq(int irq)
+{
+       if (irq == 10)
+               return 5;
+       return 7 - (irq - 32);
+}
+
+static void
+disable_intreq_irq(unsigned int irq)
+{
+       ctrl_outb(1 << (7 - irq2intreq(irq)), INTMSK0);
+}
+
+static void
+enable_intreq_irq(unsigned int irq)
+{
+       ctrl_outb(1 << (7 - irq2intreq(irq)), INTMSKCLR0);
+}
+
+static void
+mask_and_ack_intreq_irq(unsigned int irq)
+{
+       disable_intreq_irq(irq);
+}
+
+static unsigned int
+startup_intreq_irq(unsigned int irq)
+{
+       enable_intreq_irq(irq);
+       return 0;
+}
+
+static void
+shutdown_intreq_irq(unsigned int irq)
+{
+       disable_intreq_irq(irq);
+}
+
+static void
+end_intreq_irq(unsigned int irq)
+{
+       if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+               enable_intreq_irq(irq);
+}
+
+static struct hw_interrupt_type intreq_irq_type = {
+       .typename = "intreq",
+       .startup = startup_intreq_irq,
+       .shutdown = shutdown_intreq_irq,
+       .enable = enable_intreq_irq,
+       .disable = disable_intreq_irq,
+       .ack = mask_and_ack_intreq_irq,
+       .end = end_intreq_irq
+};
+
+void
+make_intreq_irq(unsigned int irq)
+{
+       disable_irq_nosync(irq);
+       irq_desc[irq].handler = &intreq_irq_type;
+       disable_intreq_irq(irq);
+}
+
+int
+shmse_irq_demux(int irq)
+{
+       if (irq == IRQ5_IRQ)
+               return 10;
+       return irq;
+}
+
+/*
+ * Initialize IRQ setting
+ */
+void __init
+init_73180se_IRQ(void)
+{
+       make_ipr_irq(SIOF0_IRQ, SIOF0_IPR_ADDR, SIOF0_IPR_POS, SIOF0_PRIORITY);
+
+       ctrl_outw(0x2000, 0xb03fffec);  /* mrshpc irq enable */
+       ctrl_outw(0x2000, 0xb07fffec);  /* mrshpc irq enable */
+       ctrl_outl(3 << ((7 - 5) * 4), INTC_INTPRI0);    /* irq5 pri=3 */
+       ctrl_outw(2 << ((7 - 5) * 2), INTC_ICR1);       /* low-level irq */
+       make_intreq_irq(10);
+
+       make_ipr_irq(VPU_IRQ, VPU_IPR_ADDR, VPU_IPR_POS, 8);
+
+       ctrl_outb(0x0f, INTC_IMCR5);    /* enable SCIF IRQ */
+
+       make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY);
+       make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY);
+       make_ipr_irq(DMTE4_IRQ, DMA2_IPR_ADDR, DMA2_IPR_POS, DMA2_PRIORITY);
+       make_ipr_irq(IIC0_ALI_IRQ, IIC0_IPR_ADDR, IIC0_IPR_POS, IIC0_PRIORITY);
+       make_ipr_irq(IIC0_TACKI_IRQ, IIC0_IPR_ADDR, IIC0_IPR_POS,
+                    IIC0_PRIORITY);
+       make_ipr_irq(IIC0_WAITI_IRQ, IIC0_IPR_ADDR, IIC0_IPR_POS,
+                    IIC0_PRIORITY);
+       make_ipr_irq(IIC0_DTEI_IRQ, IIC0_IPR_ADDR, IIC0_IPR_POS, IIC0_PRIORITY);
+       make_ipr_irq(SIOF0_IRQ, SIOF0_IPR_ADDR, SIOF0_IPR_POS, SIOF0_PRIORITY);
+       make_ipr_irq(SIU_IRQ, SIU_IPR_ADDR, SIU_IPR_POS, SIU_PRIORITY);
+
+       /* VIO interrupt */
+       make_ipr_irq(CEU_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY);
+       make_ipr_irq(BEU_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY);
+       make_ipr_irq(VEU_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY);
+
+       make_ipr_irq(LCDC_IRQ, LCDC_IPR_ADDR, LCDC_IPR_POS, LCDC_PRIORITY);
+       ctrl_outw(0x2000, PA_MRSHPC + 0x0c);    /* mrshpc irq enable */
+}
diff --git a/arch/sh/boards/se/73180/led.c b/arch/sh/boards/se/73180/led.c
new file mode 100644 (file)
index 0000000..1e8f1cf
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * arch/sh/boards/se/73180/led.c
+ *
+ * Derived from arch/sh/boards/se/770x/led.c
+ *
+ * Copyright (C) 2000 Stuart Menefy <stuart.menefy@st.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * This file contains Solution Engine specific LED code.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <asm/mach/se73180.h>
+
+static void
+mach_led(int position, int value)
+{
+       volatile unsigned short *p = (volatile unsigned short *) PA_LED;
+
+       if (value) {
+               *p |= (1 << LED_SHIFT);
+       } else {
+               *p &= ~(1 << LED_SHIFT);
+       }
+}
+
+/* Cycle the LED's in the clasic Knightrider/Sun pattern */
+void
+heartbeat_73180se(void)
+{
+       static unsigned int cnt = 0, period = 0;
+       volatile unsigned short *p = (volatile unsigned short *) PA_LED;
+       static unsigned bit = 0, up = 1;
+
+       cnt += 1;
+       if (cnt < period) {
+               return;
+       }
+
+       cnt = 0;
+
+       /* Go through the points (roughly!):
+        * f(0)=10, f(1)=16, f(2)=20, f(5)=35,f(inf)->110
+        */
+       period = 110 - ((300 << FSHIFT) / ((avenrun[0] / 5) + (3 << FSHIFT)));
+
+       if (up) {
+               if (bit == 7) {
+                       bit--;
+                       up = 0;
+               } else {
+                       bit++;
+               }
+       } else {
+               if (bit == 0) {
+                       bit++;
+                       up = 1;
+               } else {
+                       bit--;
+               }
+       }
+       *p = 1 << (bit + LED_SHIFT);
+
+}
diff --git a/arch/sh/boards/se/73180/setup.c b/arch/sh/boards/se/73180/setup.c
new file mode 100644 (file)
index 0000000..07fa90c
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * arch/sh/boards/se/73180/setup.c
+ *
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ * Based on arch/sh/setup_shmse.c
+ *
+ * Modified for 73180 SolutionEngine
+ *           by YOSHII Takashi <yoshii-takashi@hitachi-ul.co.jp>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/machvec.h>
+#include <asm/machvec_init.h>
+#include <asm/mach/io.h>
+
+void heartbeat_73180se(void);
+void init_73180se_IRQ(void);
+
+const char *
+get_system_type(void)
+{
+       return "SolutionEngine 73180";
+}
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_73180se __initmv = {
+       .mv_nr_irqs = 108,
+       .mv_inb = sh73180se_inb,
+       .mv_inw = sh73180se_inw,
+       .mv_inl = sh73180se_inl,
+       .mv_outb = sh73180se_outb,
+       .mv_outw = sh73180se_outw,
+       .mv_outl = sh73180se_outl,
+
+       .mv_inb_p = sh73180se_inb_p,
+       .mv_inw_p = sh73180se_inw,
+       .mv_inl_p = sh73180se_inl,
+       .mv_outb_p = sh73180se_outb_p,
+       .mv_outw_p = sh73180se_outw,
+       .mv_outl_p = sh73180se_outl,
+
+       .mv_insb = sh73180se_insb,
+       .mv_insw = sh73180se_insw,
+       .mv_insl = sh73180se_insl,
+       .mv_outsb = sh73180se_outsb,
+       .mv_outsw = sh73180se_outsw,
+       .mv_outsl = sh73180se_outsl,
+
+       .mv_init_irq = init_73180se_IRQ,
+#ifdef CONFIG_HEARTBEAT
+       .mv_heartbeat = heartbeat_73180se,
+#endif
+};
+
+ALIAS_MV(73180se)
+/*
+ * Initialize the board
+ */
+void __init
+platform_setup(void)
+{
+
+}
diff --git a/arch/sh/boards/sh03/Makefile b/arch/sh/boards/sh03/Makefile
new file mode 100644 (file)
index 0000000..321be50
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for the Interface (CTP/PCI-SH03) specific parts of the kernel
+#
+
+obj-y   := setup.o rtc.o
+obj-$(CONFIG_HEARTBEAT) += led.o
diff --git a/arch/sh/boards/sh03/led.c b/arch/sh/boards/sh03/led.c
new file mode 100644 (file)
index 0000000..c851b0b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * linux/arch/sh/boards/sh03/led.c
+ *
+ * Copyright (C) 2004  Saito.K Interface Corporation.
+ *
+ * This file contains Interface CTP/PCI-SH03 specific LED code.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+
+/* Cycle the LED's in the clasic Knightrider/Sun pattern */
+void heartbeat_sh03(void)
+{
+       static unsigned int cnt = 0, period = 0;
+       volatile unsigned char* p = (volatile unsigned char*)0xa0800000;
+       static unsigned bit = 0, up = 1;
+
+       cnt += 1;
+       if (cnt < period) {
+               return;
+       }
+
+       cnt = 0;
+
+       /* Go through the points (roughly!):
+        * f(0)=10, f(1)=16, f(2)=20, f(5)=35,f(inf)->110
+        */
+       period = 110 - ( (300<<FSHIFT)/
+                        ((avenrun[0]/5) + (3<<FSHIFT)) );
+
+       if (up) {
+               if (bit == 7) {
+                       bit--;
+                       up=0;
+               } else {
+                       bit ++;
+               }
+       } else {
+               if (bit == 0) {
+                       bit++;
+                       up=1;
+               } else {
+                       bit--;
+               }
+       }
+       *p = 1<<bit;
+
+}
diff --git a/arch/sh/boards/sh03/rtc.c b/arch/sh/boards/sh03/rtc.c
new file mode 100644 (file)
index 0000000..e5881b1
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * linux/arch/sh/boards/sh03/rtc.c -- CTP/PCI-SH03 on-chip RTC support
+ *
+ *  Copyright (C) 2004  Saito.K & Jeanne(ksaito@interface.co.jp)
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <asm/io.h>
+#include <linux/rtc.h>
+#include <linux/spinlock.h>
+
+#define RTC_BASE       0xb0000000
+#define RTC_SEC1       (RTC_BASE + 0)
+#define RTC_SEC10      (RTC_BASE + 1)
+#define RTC_MIN1       (RTC_BASE + 2)
+#define RTC_MIN10      (RTC_BASE + 3)
+#define RTC_HOU1       (RTC_BASE + 4)
+#define RTC_HOU10      (RTC_BASE + 5)
+#define RTC_WEE1       (RTC_BASE + 6)
+#define RTC_DAY1       (RTC_BASE + 7)
+#define RTC_DAY10      (RTC_BASE + 8)
+#define RTC_MON1       (RTC_BASE + 9)
+#define RTC_MON10      (RTC_BASE + 10)
+#define RTC_YEA1       (RTC_BASE + 11)
+#define RTC_YEA10      (RTC_BASE + 12)
+#define RTC_YEA100     (RTC_BASE + 13)
+#define RTC_YEA1000    (RTC_BASE + 14)
+#define RTC_CTL                (RTC_BASE + 15)
+#define RTC_BUSY       1
+#define RTC_STOP       2
+
+#ifndef BCD_TO_BIN
+#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
+#endif
+
+#ifndef BIN_TO_BCD
+#define BIN_TO_BCD(val)        ((val)=(((val)/10)<<4) + (val)%10)
+#endif
+
+extern void (*rtc_get_time)(struct timespec *);
+extern int (*rtc_set_time)(const time_t);
+extern spinlock_t rtc_lock;
+
+unsigned long get_cmos_time(void)
+{
+       unsigned int year, mon, day, hour, min, sec;
+       int i;
+
+       spin_lock(&rtc_lock);
+ again:
+       for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
+               if (!(ctrl_inb(RTC_CTL) & RTC_BUSY))
+                       break;
+       do {
+               sec  = (ctrl_inb(RTC_SEC1) & 0xf) + (ctrl_inb(RTC_SEC10) & 0x7) * 10;
+               min  = (ctrl_inb(RTC_MIN1) & 0xf) + (ctrl_inb(RTC_MIN10) & 0xf) * 10;
+               hour = (ctrl_inb(RTC_HOU1) & 0xf) + (ctrl_inb(RTC_HOU10) & 0xf) * 10;
+               day  = (ctrl_inb(RTC_DAY1) & 0xf) + (ctrl_inb(RTC_DAY10) & 0xf) * 10;
+               mon  = (ctrl_inb(RTC_MON1) & 0xf) + (ctrl_inb(RTC_MON10) & 0xf) * 10;
+               year = (ctrl_inb(RTC_YEA1) & 0xf) + (ctrl_inb(RTC_YEA10) & 0xf) * 10
+                    + (ctrl_inb(RTC_YEA100 ) & 0xf) * 100
+                    + (ctrl_inb(RTC_YEA1000) & 0xf) * 1000;
+       } while (sec != (ctrl_inb(RTC_SEC1) & 0xf) + (ctrl_inb(RTC_SEC10) & 0x7) * 10);
+       if (year == 0 || mon < 1 || mon > 12 || day > 31 || day < 1 ||
+           hour > 23 || min > 59 || sec > 59) {
+               printk(KERN_ERR
+                      "SH-03 RTC: invalid value, resetting to 1 Jan 2000\n");
+               printk("year=%d, mon=%d, day=%d, hour=%d, min=%d, sec=%d\n",
+                      year, mon, day, hour, min, sec);
+
+               ctrl_outb(0, RTC_SEC1); ctrl_outb(0, RTC_SEC10);
+               ctrl_outb(0, RTC_MIN1); ctrl_outb(0, RTC_MIN10);
+               ctrl_outb(0, RTC_HOU1); ctrl_outb(0, RTC_HOU10);
+               ctrl_outb(6, RTC_WEE1);
+               ctrl_outb(1, RTC_DAY1); ctrl_outb(0, RTC_DAY10);
+               ctrl_outb(1, RTC_MON1); ctrl_outb(0, RTC_MON10);
+               ctrl_outb(0, RTC_YEA1); ctrl_outb(0, RTC_YEA10);
+               ctrl_outb(0, RTC_YEA100);
+               ctrl_outb(2, RTC_YEA1000);
+               ctrl_outb(0, RTC_CTL);
+               goto again;
+       }
+
+       spin_unlock(&rtc_lock);
+       return mktime(year, mon, day, hour, min, sec);
+}
+
+void sh03_rtc_gettimeofday(struct timespec *tv)
+{
+
+       tv->tv_sec = get_cmos_time();
+       tv->tv_nsec = 0;
+}
+
+static int set_rtc_mmss(unsigned long nowtime)
+{
+       int retval = 0;
+       int real_seconds, real_minutes, cmos_minutes;
+       int i;
+
+       /* gets recalled with irq locally disabled */
+       spin_lock(&rtc_lock);
+       for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
+               if (!(ctrl_inb(RTC_CTL) & RTC_BUSY))
+                       break;
+       cmos_minutes = (ctrl_inb(RTC_MIN1) & 0xf) + (ctrl_inb(RTC_MIN10) & 0xf) * 10;
+       real_seconds = nowtime % 60;
+       real_minutes = nowtime / 60;
+       if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1)
+               real_minutes += 30;             /* correct for half hour time zone */
+       real_minutes %= 60;
+
+       if (abs(real_minutes - cmos_minutes) < 30) {
+               BIN_TO_BCD(real_seconds);
+               BIN_TO_BCD(real_minutes);
+               ctrl_outb(real_seconds % 10, RTC_SEC1);
+               ctrl_outb(real_seconds / 10, RTC_SEC10);
+               ctrl_outb(real_minutes % 10, RTC_MIN1);
+               ctrl_outb(real_minutes / 10, RTC_MIN10);
+       } else {
+               printk(KERN_WARNING
+                      "set_rtc_mmss: can't update from %d to %d\n",
+                      cmos_minutes, real_minutes);
+               retval = -1;
+       }
+       spin_unlock(&rtc_lock);
+
+       return retval;
+}
+
+int sh03_rtc_settimeofday(const time_t secs)
+{
+       unsigned long nowtime = secs;
+
+       return set_rtc_mmss(nowtime);
+}
+
+void sh03_time_init(void)
+{
+       rtc_get_time = sh03_rtc_gettimeofday;
+       rtc_set_time = sh03_rtc_settimeofday;
+}
diff --git a/arch/sh/boards/sh03/setup.c b/arch/sh/boards/sh03/setup.c
new file mode 100644 (file)
index 0000000..d2a08ca
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * linux/arch/sh/boards/sh03/setup.c
+ *
+ * Copyright (C) 2004  Interface Co.,Ltd. Saito.K
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <asm/io.h>
+#include <asm/sh03/io.h>
+#include <asm/sh03/sh03.h>
+#include <asm/addrspace.h>
+#include "../../drivers/pci/pci-sh7751.h"
+
+extern void (*board_time_init)(void);
+
+const char *get_system_type(void)
+{
+       return "Interface CTP/PCI-SH03)";
+}
+
+void init_sh03_IRQ(void)
+{
+       ctrl_outw(ctrl_inw(INTC_ICR) | INTC_ICR_IRLM, INTC_ICR);
+
+       make_ipr_irq(IRL0_IRQ, IRL0_IPR_ADDR, IRL0_IPR_POS, IRL0_PRIORITY);
+       make_ipr_irq(IRL1_IRQ, IRL1_IPR_ADDR, IRL1_IPR_POS, IRL1_PRIORITY);
+       make_ipr_irq(IRL2_IRQ, IRL2_IPR_ADDR, IRL2_IPR_POS, IRL2_PRIORITY);
+       make_ipr_irq(IRL3_IRQ, IRL3_IPR_ADDR, IRL3_IPR_POS, IRL3_PRIORITY);
+}
+
+extern void *cf_io_base;
+
+unsigned long sh03_isa_port2addr(unsigned long port)
+{
+       if (PXSEG(port))
+               return port;
+       /* CompactFlash (IDE) */
+       if (((port >= 0x1f0) && (port <= 0x1f7)) || (port == 0x3f6)) {
+               return (unsigned long)cf_io_base + port;
+       }
+        return port + SH7751_PCI_IO_BASE;
+}
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_sh03 __initmv = {
+       .mv_nr_irqs             = 48,
+       .mv_isa_port2addr       = sh03_isa_port2addr,
+       .mv_init_irq            = init_sh03_IRQ,
+
+#ifdef CONFIG_HEARTBEAT
+       .mv_heartbeat           = heartbeat_sh03,
+#endif
+};
+
+ALIAS_MV(sh03)
+
+/* arch/sh/boards/sh03/rtc.c */
+void sh03_time_init(void);
+
+int __init platform_setup(void)
+{
+       board_time_init = sh03_time_init;
+       return 0;
+}
diff --git a/arch/sh/boards/superh/microdev/Makefile b/arch/sh/boards/superh/microdev/Makefile
new file mode 100644 (file)
index 0000000..1387dd6
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for the SuperH MicroDev specific parts of the kernel
+#
+
+obj-y   := setup.o irq.o io.o
+
+obj-$(CONFIG_HEARTBEAT)        += led.o
+
diff --git a/arch/sh/boards/superh/microdev/io.c b/arch/sh/boards/superh/microdev/io.c
new file mode 100644 (file)
index 0000000..fe83b2c
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * linux/arch/sh/kernel/io_microdev.c
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ * Copyright (C) 2003, 2004 SuperH, Inc.
+ * Copyright (C) 2004 Paul Mundt
+ *
+ * SuperH SH4-202 MicroDev board support.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include <asm/mach/io.h>
+
+       /*
+        *      we need to have a 'safe' address to re-direct all I/O requests
+        *      that we do not explicitly wish to handle. This safe address
+        *      must have the following properies:
+        *
+        *              * writes are ignored (no exception)
+        *              * reads are benign (no side-effects)
+        *              * accesses of width 1, 2 and 4-bytes are all valid.
+        *
+        *      The Processor Version Register (PVR) has these properties.
+        */
+#define        PVR     0xff000030      /* Processor Version Register */
+
+
+#define        IO_IDE2_BASE            0x170ul /* I/O base for SMSC FDC37C93xAPM IDE #2 */
+#define        IO_IDE1_BASE            0x1f0ul /* I/O base for SMSC FDC37C93xAPM IDE #1 */
+#define IO_ISP1161_BASE                0x290ul /* I/O port for Philips ISP1161x USB chip */
+#define IO_SERIAL2_BASE                0x2f8ul /* I/O base for SMSC FDC37C93xAPM Serial #2 */
+#define        IO_LAN91C111_BASE       0x300ul /* I/O base for SMSC LAN91C111 Ethernet chip */
+#define        IO_IDE2_MISC            0x376ul /* I/O misc for SMSC FDC37C93xAPM IDE #2 */
+#define IO_SUPERIO_BASE                0x3f0ul /* I/O base for SMSC FDC37C93xAPM SuperIO chip */
+#define        IO_IDE1_MISC            0x3f6ul /* I/O misc for SMSC FDC37C93xAPM IDE #1 */
+#define IO_SERIAL1_BASE                0x3f8ul /* I/O base for SMSC FDC37C93xAPM Serial #1 */
+
+#define        IO_ISP1161_EXTENT       0x04ul  /* I/O extent for Philips ISP1161x USB chip */
+#define        IO_LAN91C111_EXTENT     0x10ul  /* I/O extent for SMSC LAN91C111 Ethernet chip */
+#define        IO_SUPERIO_EXTENT       0x02ul  /* I/O extent for SMSC FDC37C93xAPM SuperIO chip */
+#define        IO_IDE_EXTENT           0x08ul  /* I/O extent for IDE Task Register set */
+#define IO_SERIAL_EXTENT       0x10ul
+
+#define        IO_LAN91C111_PHYS       0xa7500000ul    /* Physical address of SMSC LAN91C111 Ethernet chip */
+#define        IO_ISP1161_PHYS         0xa7700000ul    /* Physical address of Philips ISP1161x USB chip */
+#define        IO_SUPERIO_PHYS         0xa7800000ul    /* Physical address of SMSC FDC37C93xAPM SuperIO chip */
+
+#define PORT2ADDR(x) (microdev_isa_port2addr(x))
+
+
+static inline void delay(void)
+{
+#if defined(CONFIG_PCI)
+       /* System board present, just make a dummy SRAM access.  (CS0 will be
+          mapped to PCI memory, probably good to avoid it.) */
+       ctrl_inw(0xa6800000);
+#else
+       /* CS0 will be mapped to flash, ROM etc so safe to access it. */
+       ctrl_inw(0xa0000000);
+#endif
+}
+
+unsigned char microdev_inb(unsigned long port)
+{
+#ifdef CONFIG_PCI
+       if (port >= PCIBIOS_MIN_IO)
+               return microdev_pci_inb(port);
+#endif
+       return *(volatile unsigned char*)PORT2ADDR(port);
+}
+
+unsigned short microdev_inw(unsigned long port)
+{
+#ifdef CONFIG_PCI
+       if (port >= PCIBIOS_MIN_IO)
+               return microdev_pci_inw(port);
+#endif
+       return *(volatile unsigned short*)PORT2ADDR(port);
+}
+
+unsigned int microdev_inl(unsigned long port)
+{
+#ifdef CONFIG_PCI
+       if (port >= PCIBIOS_MIN_IO)
+               return microdev_pci_inl(port);
+#endif
+       return *(volatile unsigned int*)PORT2ADDR(port);
+}
+
+void microdev_outb(unsigned char b, unsigned long port)
+{
+#ifdef CONFIG_PCI
+       if (port >= PCIBIOS_MIN_IO) {
+               microdev_pci_outb(b, port);
+               return;
+       }
+#endif
+
+       /*
+        *      There is a board feature with the current SH4-202 MicroDev in
+        *      that the 2 byte enables (nBE0 and nBE1) are tied together (and
+        *      to the Chip Select Line (Ethernet_CS)). Due to this conectivity,
+        *      it is not possible to safely perform 8-bit writes to the
+        *      Ethernet registers, as 16-bits will be consumed from the Data
+        *      lines (corrupting the other byte).  Hence, this function is
+        *      written to impliment 16-bit read/modify/write for all byte-wide
+        *      acceses.
+        *
+        *      Note: there is no problem with byte READS (even or odd).
+        *
+        *                      Sean McGoogan - 16th June 2003.
+        */
+       if ((port >= IO_LAN91C111_BASE) &&
+           (port <  IO_LAN91C111_BASE + IO_LAN91C111_EXTENT)) {
+                       /*
+                        * Then are trying to perform a byte-write to the
+                        * LAN91C111.  This needs special care.
+                        */
+               if (port % 2 == 1) {    /* is the port odd ? */
+                       /* unset bit-0, i.e. make even */
+                       const unsigned long evenPort = port-1;
+                       unsigned short word;
+
+                       /*
+                        * do a 16-bit read/write to write to 'port',
+                        * preserving even byte.
+                        *
+                        *      Even addresses are bits 0-7
+                        *      Odd  addresses are bits 8-15
+                        */
+                       word = microdev_inw(evenPort);
+                       word = (word & 0xffu) | (b << 8);
+                       microdev_outw(word, evenPort);
+               } else {
+                       /* else, we are trying to do an even byte write */
+                       unsigned short word;
+
+                       /*
+                        * do a 16-bit read/write to write to 'port',
+                        * preserving odd byte.
+                        *
+                        *      Even addresses are bits 0-7
+                        *      Odd  addresses are bits 8-15
+                        */
+                       word = microdev_inw(port);
+                       word = (word & 0xff00u) | (b);
+                       microdev_outw(word, port);
+               }
+       } else {
+               *(volatile unsigned char*)PORT2ADDR(port) = b;
+       }
+}
+
+void microdev_outw(unsigned short b, unsigned long port)
+{
+#ifdef CONFIG_PCI
+       if (port >= PCIBIOS_MIN_IO) {
+               microdev_pci_outw(b, port);
+               return;
+       }
+#endif
+       *(volatile unsigned short*)PORT2ADDR(port) = b;
+}
+
+void microdev_outl(unsigned int b, unsigned long port)
+{
+#ifdef CONFIG_PCI
+       if (port >= PCIBIOS_MIN_IO) {
+               microdev_pci_outl(b, port);
+               return;
+       }
+#endif
+       *(volatile unsigned int*)PORT2ADDR(port) = b;
+}
+
+unsigned char microdev_inb_p(unsigned long port)
+{
+       unsigned char v = microdev_inb(port);
+       delay();
+       return v;
+}
+
+unsigned short microdev_inw_p(unsigned long port)
+{
+       unsigned short v = microdev_inw(port);
+       delay();
+       return v;
+}
+
+unsigned int microdev_inl_p(unsigned long port)
+{
+       unsigned int v = microdev_inl(port);
+       delay();
+       return v;
+}
+
+void microdev_outb_p(unsigned char b, unsigned long port)
+{
+       microdev_outb(b, port);
+       delay();
+}
+
+void microdev_outw_p(unsigned short b, unsigned long port)
+{
+       microdev_outw(b, port);
+       delay();
+}
+
+void microdev_outl_p(unsigned int b, unsigned long port)
+{
+       microdev_outl(b, port);
+       delay();
+}
+
+void microdev_insb(unsigned long port, void *buffer, unsigned long count)
+{
+       volatile unsigned char *port_addr;
+       unsigned char *buf = buffer;
+
+       port_addr = (volatile unsigned char *)PORT2ADDR(port);
+
+       while (count--)
+               *buf++ = *port_addr;
+}
+
+void microdev_insw(unsigned long port, void *buffer, unsigned long count)
+{
+       volatile unsigned short *port_addr;
+       unsigned short *buf = buffer;
+
+       port_addr = (volatile unsigned short *)PORT2ADDR(port);
+
+       while (count--)
+               *buf++ = *port_addr;
+}
+
+void microdev_insl(unsigned long port, void *buffer, unsigned long count)
+{
+       volatile unsigned long *port_addr;
+       unsigned int *buf = buffer;
+
+       port_addr = (volatile unsigned long *)PORT2ADDR(port);
+
+       while (count--)
+               *buf++ = *port_addr;
+}
+
+void microdev_outsb(unsigned long port, const void *buffer, unsigned long count)
+{
+       volatile unsigned char *port_addr;
+       const unsigned char *buf = buffer;
+
+       port_addr = (volatile unsigned char *)PORT2ADDR(port);
+
+       while (count--)
+               *port_addr = *buf++;
+}
+
+void microdev_outsw(unsigned long port, const void *buffer, unsigned long count)
+{
+       volatile unsigned short *port_addr;
+       const unsigned short *buf = buffer;
+
+       port_addr = (volatile unsigned short *)PORT2ADDR(port);
+
+       while (count--)
+               *port_addr = *buf++;
+}
+
+void microdev_outsl(unsigned long port, const void *buffer, unsigned long count)
+{
+       volatile unsigned long *port_addr;
+       const unsigned int *buf = buffer;
+
+       port_addr = (volatile unsigned long *)PORT2ADDR(port);
+
+       while (count--)
+               *port_addr = *buf++;
+}
+
+/*
+ * map I/O ports to memory-mapped addresses
+ */
+unsigned long microdev_isa_port2addr(unsigned long offset)
+{
+       unsigned long result;
+
+       if ((offset >= IO_LAN91C111_BASE) &&
+           (offset <  IO_LAN91C111_BASE + IO_LAN91C111_EXTENT)) {
+                       /*
+                        *      SMSC LAN91C111 Ethernet chip
+                        */
+               result = IO_LAN91C111_PHYS + offset - IO_LAN91C111_BASE;
+       } else if ((offset >= IO_SUPERIO_BASE) &&
+                  (offset <  IO_SUPERIO_BASE + IO_SUPERIO_EXTENT)) {
+                       /*
+                        *      SMSC FDC37C93xAPM SuperIO chip
+                        *
+                        *      Configuration Registers
+                        */
+               result = IO_SUPERIO_PHYS + (offset << 1);
+#if 0
+       } else if (offset == KBD_DATA_REG || offset == KBD_CNTL_REG ||
+                  offset == KBD_STATUS_REG) {
+                       /*
+                        *      SMSC FDC37C93xAPM SuperIO chip
+                        *
+                        *      PS/2 Keyboard + Mouse (ports 0x60 and 0x64).
+                        */
+               result = IO_SUPERIO_PHYS + (offset << 1);
+#endif
+       } else if (((offset >= IO_IDE1_BASE) &&
+                   (offset <  IO_IDE1_BASE + IO_IDE_EXTENT)) ||
+                   (offset == IO_IDE1_MISC)) {
+                       /*
+                        *      SMSC FDC37C93xAPM SuperIO chip
+                        *
+                        *      IDE #1
+                        */
+               result = IO_SUPERIO_PHYS + (offset << 1);
+       } else if (((offset >= IO_IDE2_BASE) &&
+                   (offset <  IO_IDE2_BASE + IO_IDE_EXTENT)) ||
+                   (offset == IO_IDE2_MISC)) {
+                       /*
+                        *      SMSC FDC37C93xAPM SuperIO chip
+                        *
+                        *      IDE #2
+                        */
+               result = IO_SUPERIO_PHYS + (offset << 1);
+       } else if ((offset >= IO_SERIAL1_BASE) &&
+                  (offset <  IO_SERIAL1_BASE + IO_SERIAL_EXTENT)) {
+                       /*
+                        *      SMSC FDC37C93xAPM SuperIO chip
+                        *
+                        *      Serial #1
+                        */
+               result = IO_SUPERIO_PHYS + (offset << 1);
+       } else if ((offset >= IO_SERIAL2_BASE) &&
+                  (offset <  IO_SERIAL2_BASE + IO_SERIAL_EXTENT)) {
+                       /*
+                        *      SMSC FDC37C93xAPM SuperIO chip
+                        *
+                        *      Serial #2
+                        */
+               result = IO_SUPERIO_PHYS + (offset << 1);
+       } else if ((offset >= IO_ISP1161_BASE) &&
+                  (offset < IO_ISP1161_BASE + IO_ISP1161_EXTENT)) {
+                       /*
+                        *      Philips USB ISP1161x chip
+                        */
+               result = IO_ISP1161_PHYS + offset - IO_ISP1161_BASE;
+       } else {
+                       /*
+                        *      safe default.
+                        */
+               printk("Warning: unexpected port in %s( offset = 0x%lx )\n",
+                      __FUNCTION__, offset);
+               result = PVR;
+       }
+
+       return result;
+}
+
diff --git a/arch/sh/boards/superh/microdev/irq.c b/arch/sh/boards/superh/microdev/irq.c
new file mode 100644 (file)
index 0000000..1298883
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * arch/sh/boards/superh/microdev/irq.c
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ *
+ * SuperH SH4-202 MicroDev board support.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/mach/irq.h>
+
+#define NUM_EXTERNAL_IRQS 16   /* IRL0 .. IRL15 */
+
+
+static const struct {
+       unsigned char fpgaIrq;
+       unsigned char mapped;
+       const char *name;
+} fpgaIrqTable[NUM_EXTERNAL_IRQS] = {
+       { 0,                            0,      "unused"   },           /* IRQ #0       IRL=15  0x200  */
+       { MICRODEV_FPGA_IRQ_KEYBOARD,   1,      "keyboard" },           /* IRQ #1       IRL=14  0x220  */
+       { MICRODEV_FPGA_IRQ_SERIAL1,    1,      "Serial #1"},           /* IRQ #2       IRL=13  0x240  */
+       { MICRODEV_FPGA_IRQ_ETHERNET,   1,      "Ethernet" },           /* IRQ #3       IRL=12  0x260  */
+       { MICRODEV_FPGA_IRQ_SERIAL2,    0,      "Serial #2"},           /* IRQ #4       IRL=11  0x280  */
+       { 0,                            0,      "unused"   },           /* IRQ #5       IRL=10  0x2a0  */
+       { 0,                            0,      "unused"   },           /* IRQ #6       IRL=9   0x2c0  */
+       { MICRODEV_FPGA_IRQ_USB_HC,     1,      "USB"      },           /* IRQ #7       IRL=8   0x2e0  */
+       { MICRODEV_IRQ_PCI_INTA,        1,      "PCI INTA" },           /* IRQ #8       IRL=7   0x300  */
+       { MICRODEV_IRQ_PCI_INTB,        1,      "PCI INTB" },           /* IRQ #9       IRL=6   0x320  */
+       { MICRODEV_IRQ_PCI_INTC,        1,      "PCI INTC" },           /* IRQ #10      IRL=5   0x340  */
+       { MICRODEV_IRQ_PCI_INTD,        1,      "PCI INTD" },           /* IRQ #11      IRL=4   0x360  */
+       { MICRODEV_FPGA_IRQ_MOUSE,      1,      "mouse"    },           /* IRQ #12      IRL=3   0x380  */
+       { MICRODEV_FPGA_IRQ_IDE2,       1,      "IDE #2"   },           /* IRQ #13      IRL=2   0x3a0  */
+       { MICRODEV_FPGA_IRQ_IDE1,       1,      "IDE #1"   },           /* IRQ #14      IRL=1   0x3c0  */
+       { 0,                            0,      "unused"   },           /* IRQ #15      IRL=0   0x3e0  */
+};
+
+#if (MICRODEV_LINUX_IRQ_KEYBOARD != 1)
+#  error Inconsistancy in defining the IRQ# for Keyboard!
+#endif
+
+#if (MICRODEV_LINUX_IRQ_ETHERNET != 3)
+#  error Inconsistancy in defining the IRQ# for Ethernet!
+#endif
+
+#if (MICRODEV_LINUX_IRQ_USB_HC != 7)
+#  error Inconsistancy in defining the IRQ# for USB!
+#endif
+
+#if (MICRODEV_LINUX_IRQ_MOUSE != 12)
+#  error Inconsistancy in defining the IRQ# for PS/2 Mouse!
+#endif
+
+#if (MICRODEV_LINUX_IRQ_IDE2 != 13)
+#  error Inconsistancy in defining the IRQ# for secondary IDE!
+#endif
+
+#if (MICRODEV_LINUX_IRQ_IDE1 != 14)
+#  error Inconsistancy in defining the IRQ# for primary IDE!
+#endif
+
+static void enable_microdev_irq(unsigned int irq);
+static void disable_microdev_irq(unsigned int irq);
+
+       /* shutdown is same as "disable" */
+#define shutdown_microdev_irq disable_microdev_irq
+
+static void mask_and_ack_microdev(unsigned int);
+static void end_microdev_irq(unsigned int irq);
+
+static unsigned int startup_microdev_irq(unsigned int irq)
+{
+       enable_microdev_irq(irq);
+       return 0;               /* never anything pending */
+}
+
+static struct hw_interrupt_type microdev_irq_type = {
+       "MicroDev-IRQ",
+       startup_microdev_irq,
+       shutdown_microdev_irq,
+       enable_microdev_irq,
+       disable_microdev_irq,
+       mask_and_ack_microdev,
+       end_microdev_irq
+};
+
+static void disable_microdev_irq(unsigned int irq)
+{
+       unsigned int flags;
+       unsigned int fpgaIrq;
+
+       if (irq >= NUM_EXTERNAL_IRQS) return;
+       if (!fpgaIrqTable[irq].mapped) return;
+
+       fpgaIrq = fpgaIrqTable[irq].fpgaIrq;
+
+               /* disable interrupts */
+       local_irq_save(flags);
+
+               /* disable interupts on the FPGA INTC register */
+       ctrl_outl(MICRODEV_FPGA_INTC_MASK(fpgaIrq), MICRODEV_FPGA_INTDSB_REG);
+
+               /* restore interrupts */
+       local_irq_restore(flags);
+}
+
+static void enable_microdev_irq(unsigned int irq)
+{
+       unsigned long priorityReg, priorities, pri;
+       unsigned int flags;
+       unsigned int fpgaIrq;
+
+
+       if (irq >= NUM_EXTERNAL_IRQS) return;
+       if (!fpgaIrqTable[irq].mapped) return;
+
+       pri = 15 - irq;
+
+       fpgaIrq = fpgaIrqTable[irq].fpgaIrq;
+       priorityReg = MICRODEV_FPGA_INTPRI_REG(fpgaIrq);
+
+               /* disable interrupts */
+       local_irq_save(flags);
+
+               /* set priority for the interrupt */
+       priorities = ctrl_inl(priorityReg);
+       priorities &= ~MICRODEV_FPGA_INTPRI_MASK(fpgaIrq);
+       priorities |= MICRODEV_FPGA_INTPRI_LEVEL(fpgaIrq, pri);
+       ctrl_outl(priorities, priorityReg);
+
+               /* enable interupts on the FPGA INTC register */
+       ctrl_outl(MICRODEV_FPGA_INTC_MASK(fpgaIrq), MICRODEV_FPGA_INTENB_REG);
+
+               /* restore interrupts */
+       local_irq_restore(flags);
+}
+
+       /* This functions sets the desired irq handler to be a MicroDev type */
+static void __init make_microdev_irq(unsigned int irq)
+{
+       disable_irq_nosync(irq);
+       irq_desc[irq].handler = &microdev_irq_type;
+       disable_microdev_irq(irq);
+}
+
+static void mask_and_ack_microdev(unsigned int irq)
+{
+       disable_microdev_irq(irq);
+}
+
+static void end_microdev_irq(unsigned int irq)
+{
+       if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+       {
+               enable_microdev_irq(irq);
+       }
+}
+
+extern void __init init_microdev_irq(void)
+{
+       int i;
+
+               /* disable interupts on the FPGA INTC register */
+       ctrl_outl(~0ul, MICRODEV_FPGA_INTDSB_REG);
+
+       for (i = 0; i < NUM_EXTERNAL_IRQS; i++)
+       {
+               make_microdev_irq(i);
+       }
+}
+
+extern void microdev_print_fpga_intc_status(void)
+{
+       volatile unsigned int * const intenb = (unsigned int*)MICRODEV_FPGA_INTENB_REG;
+       volatile unsigned int * const intdsb = (unsigned int*)MICRODEV_FPGA_INTDSB_REG;
+       volatile unsigned int * const intpria = (unsigned int*)MICRODEV_FPGA_INTPRI_REG(0);
+       volatile unsigned int * const intprib = (unsigned int*)MICRODEV_FPGA_INTPRI_REG(8);
+       volatile unsigned int * const intpric = (unsigned int*)MICRODEV_FPGA_INTPRI_REG(16);
+       volatile unsigned int * const intprid = (unsigned int*)MICRODEV_FPGA_INTPRI_REG(24);
+       volatile unsigned int * const intsrc = (unsigned int*)MICRODEV_FPGA_INTSRC_REG;
+       volatile unsigned int * const intreq = (unsigned int*)MICRODEV_FPGA_INTREQ_REG;
+
+       printk("-------------------------- microdev_print_fpga_intc_status() ------------------\n");
+       printk("FPGA_INTENB = 0x%08x\n", *intenb);
+       printk("FPGA_INTDSB = 0x%08x\n", *intdsb);
+       printk("FPGA_INTSRC = 0x%08x\n", *intsrc);
+       printk("FPGA_INTREQ = 0x%08x\n", *intreq);
+       printk("FPGA_INTPRI[3..0] = %08x:%08x:%08x:%08x\n", *intprid, *intpric, *intprib, *intpria);
+       printk("-------------------------------------------------------------------------------\n");
+}
+
+
diff --git a/arch/sh/boards/superh/microdev/led.c b/arch/sh/boards/superh/microdev/led.c
new file mode 100644 (file)
index 0000000..52a98e6
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * linux/arch/sh/kernel/led_microdev.c
+ *
+ * Copyright (C) 2002 Stuart Menefy <stuart.menefy@st.com>
+ * Copyright (C) 2003 Richard Curnow (Richard.Curnow@superh.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ */
+
+#include <linux/config.h>
+#include <asm/io.h>
+
+#define LED_REGISTER 0xa6104d20
+
+static void mach_led_d9(int value)
+{
+       unsigned long reg;
+       reg = ctrl_inl(LED_REGISTER);
+       reg &= ~1;
+       reg |= (value & 1);
+       ctrl_outl(reg, LED_REGISTER);
+       return;
+}
+
+static void mach_led_d10(int value)
+{
+       unsigned long reg;
+       reg = ctrl_inl(LED_REGISTER);
+       reg &= ~2;
+       reg |= ((value & 1) << 1);
+       ctrl_outl(reg, LED_REGISTER);
+       return;
+}
+
+
+#ifdef CONFIG_HEARTBEAT
+#include <linux/sched.h>
+
+static unsigned char banner_table[] = {
+       0x11, 0x01, 0x11, 0x01, 0x11, 0x03,
+       0x11, 0x01, 0x11, 0x01, 0x13, 0x03,
+       0x11, 0x01, 0x13, 0x01, 0x13, 0x01, 0x11, 0x03,
+       0x11, 0x03,
+       0x11, 0x01, 0x13, 0x01, 0x11, 0x03,
+       0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x11, 0x07,
+       0x13, 0x01, 0x13, 0x03,
+       0x11, 0x01, 0x11, 0x03,
+       0x13, 0x01, 0x11, 0x01, 0x13, 0x01, 0x11, 0x03,
+       0x11, 0x01, 0x13, 0x01, 0x11, 0x03,
+       0x13, 0x01, 0x13, 0x01, 0x13, 0x03,
+       0x13, 0x01, 0x11, 0x01, 0x11, 0x03,
+       0x11, 0x03,
+       0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x13, 0x07,
+       0xff
+};
+
+static void banner(void)
+{
+       static int pos = 0;
+       static int count = 0;
+
+       if (count) {
+               count--;
+       } else {
+               int val = banner_table[pos];
+               if (val == 0xff) {
+                       pos = 0;
+                       val = banner_table[pos];
+               }
+               pos++;
+               mach_led_d10((val >> 4) & 1);
+               count = 10 * (val & 0xf);
+       }
+}
+
+/* From heartbeat_harp in the stboards directory */
+/* acts like an actual heart beat -- ie thump-thump-pause... */
+void microdev_heartbeat(void)
+{
+       static unsigned cnt = 0, period = 0, dist = 0;
+
+       if (cnt == 0 || cnt == dist)
+               mach_led_d9(1);
+       else if (cnt == 7 || cnt == dist+7)
+               mach_led_d9(0);
+
+       if (++cnt > period) {
+               cnt = 0;
+               /* The hyperbolic function below modifies the heartbeat period
+                * length in dependency of the current (5min) load. It goes
+                * through the points f(0)=126, f(1)=86, f(5)=51,
+                * f(inf)->30. */
+               period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30;
+               dist = period / 4;
+       }
+
+       banner();
+}
+
+#endif
diff --git a/arch/sh/boards/superh/microdev/setup.c b/arch/sh/boards/superh/microdev/setup.c
new file mode 100644 (file)
index 0000000..c189199
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * arch/sh/boards/superh/microdev/setup.c
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ * Copyright (C) 2003, 2004 SuperH, Inc.
+ * Copyright (C) 2004 Paul Mundt
+ *
+ * SuperH SH4-202 MicroDev board support.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/io.h>
+#include <asm/machvec.h>
+#include <asm/machvec_init.h>
+
+extern void microdev_heartbeat(void);
+
+/*
+ * The Machine Vector
+ */
+
+struct sh_machine_vector mv_sh4202_microdev __initmv = {
+       .mv_nr_irqs             = 72,           /* QQQ need to check this - use the MACRO */
+
+       .mv_inb                 = microdev_inb,
+       .mv_inw                 = microdev_inw,
+       .mv_inl                 = microdev_inl,
+       .mv_outb                = microdev_outb,
+       .mv_outw                = microdev_outw,
+       .mv_outl                = microdev_outl,
+
+       .mv_inb_p               = microdev_inb_p,
+       .mv_inw_p               = microdev_inw_p,
+       .mv_inl_p               = microdev_inl_p,
+       .mv_outb_p              = microdev_outb_p,
+       .mv_outw_p              = microdev_outw_p,
+       .mv_outl_p              = microdev_outl_p,
+
+       .mv_insb                = microdev_insb,
+       .mv_insw                = microdev_insw,
+       .mv_insl                = microdev_insl,
+       .mv_outsb               = microdev_outsb,
+       .mv_outsw               = microdev_outsw,
+       .mv_outsl               = microdev_outsl,
+
+       .mv_isa_port2addr       = microdev_isa_port2addr,
+
+       .mv_init_irq            = init_microdev_irq,
+
+#ifdef CONFIG_HEARTBEAT
+       .mv_heartbeat           = microdev_heartbeat,
+#endif
+};
+ALIAS_MV(sh4202_microdev)
+
+/****************************************************************************/
+
+
+       /*
+        * Setup for the SMSC FDC37C93xAPM
+        */
+#define SMSC_CONFIG_PORT_ADDR   (0x3F0)
+#define SMSC_INDEX_PORT_ADDR    SMSC_CONFIG_PORT_ADDR
+#define SMSC_DATA_PORT_ADDR     (SMSC_INDEX_PORT_ADDR + 1)
+
+#define SMSC_ENTER_CONFIG_KEY   0x55
+#define SMSC_EXIT_CONFIG_KEY    0xaa
+
+#define SMCS_LOGICAL_DEV_INDEX          0x07   /* Logical Device Number */
+#define SMSC_DEVICE_ID_INDEX    0x20   /* Device ID */
+#define SMSC_DEVICE_REV_INDEX   0x21   /* Device Revision */
+#define SMSC_ACTIVATE_INDEX     0x30   /* Activate */
+#define SMSC_PRIMARY_BASE_INDEX         0x60   /* Primary Base Address */
+#define SMSC_SECONDARY_BASE_INDEX 0x62 /* Secondary Base Address */
+#define SMSC_PRIMARY_INT_INDEX  0x70   /* Primary Interrupt Select */
+#define SMSC_SECONDARY_INT_INDEX 0x72  /* Secondary Interrupt Select */
+#define SMSC_HDCS0_INDEX        0xf0   /* HDCS0 Address Decoder */
+#define SMSC_HDCS1_INDEX        0xf1   /* HDCS1 Address Decoder */
+
+#define SMSC_IDE1_DEVICE       1       /* IDE #1 logical device */
+#define SMSC_IDE2_DEVICE       2       /* IDE #2 logical device */
+#define SMSC_PARALLEL_DEVICE   3       /* Parallel Port logical device */
+#define SMSC_SERIAL1_DEVICE    4       /* Serial #1 logical device */
+#define SMSC_SERIAL2_DEVICE    5       /* Serial #2 logical device */
+#define SMSC_KEYBOARD_DEVICE   7       /* Keyboard logical device */
+#define SMSC_CONFIG_REGISTERS  8       /* Configuration Registers (Aux I/O) */
+
+#define SMSC_READ_INDEXED(index) ({ \
+       outb((index), SMSC_INDEX_PORT_ADDR); \
+       inb(SMSC_DATA_PORT_ADDR); })
+#define SMSC_WRITE_INDEXED(val, index) ({ \
+       outb((index), SMSC_INDEX_PORT_ADDR); \
+       outb((val),   SMSC_DATA_PORT_ADDR); })
+
+#define        IDE1_PRIMARY_BASE       0x01f0  /* Task File Registe base for IDE #1 */
+#define        IDE1_SECONDARY_BASE     0x03f6  /* Miscellaneous AT registers for IDE #1 */
+#define        IDE2_PRIMARY_BASE       0x0170  /* Task File Registe base for IDE #2 */
+#define        IDE2_SECONDARY_BASE     0x0376  /* Miscellaneous AT registers for IDE #2 */
+
+#define SERIAL1_PRIMARY_BASE   0x03f8
+#define SERIAL2_PRIMARY_BASE   0x02f8
+
+#define        MSB(x)          ( (x) >> 8 )
+#define        LSB(x)          ( (x) & 0xff )
+
+       /* General-Purpose base address on CPU-board FPGA */
+#define        MICRODEV_FPGA_GP_BASE           0xa6100000ul
+
+       /* assume a Keyboard Controller is present */
+int microdev_kbd_controller_present = 1;
+
+const char *get_system_type(void)
+{
+       return "SH4-202 MicroDev";
+}
+
+static struct resource smc91x_resources[] = {
+       [0] = {
+               .start          = 0x300,
+               .end            = 0x300 + 0x0001000 - 1,
+               .flags          = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start          = MICRODEV_LINUX_IRQ_ETHERNET,
+               .end            = MICRODEV_LINUX_IRQ_ETHERNET,
+               .flags          = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device smc91x_device = {
+       .name           = "smc91x",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(smc91x_resources),
+       .resource       = smc91x_resources,
+};
+
+static int __init smc91x_setup(void)
+{
+       return platform_device_register(&smc91x_device);
+}
+
+__initcall(smc91x_setup);
+
+       /*
+        * Initialize the board
+        */
+void __init platform_setup(void)
+{
+       int * const fpgaRevisionRegister = (int*)(MICRODEV_FPGA_GP_BASE + 0x8ul);
+       const int fpgaRevision = *fpgaRevisionRegister;
+       int * const CacheControlRegister = (int*)CCR;
+
+       printk("SuperH %s board (FPGA rev: 0x%0x, CCR: 0x%0x)\n",
+               get_system_type(), fpgaRevision, *CacheControlRegister);
+}
+
+
+/****************************************************************************/
+
+
+       /*
+        * Setup for the SMSC FDC37C93xAPM
+        */
+static int __init smsc_superio_setup(void)
+{
+
+       unsigned char devid, devrev;
+
+               /* Initially the chip is in run state */
+               /* Put it into configuration state */
+       outb(SMSC_ENTER_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+
+               /* Read device ID info */
+       devid  = SMSC_READ_INDEXED(SMSC_DEVICE_ID_INDEX);
+       devrev = SMSC_READ_INDEXED(SMSC_DEVICE_REV_INDEX);
+       if ( (devid==0x30) && (devrev==0x01) )
+       {
+               printk("SMSC FDC37C93xAPM SuperIO device detected\n");
+       }
+       else
+       {               /* not the device identity we expected */
+               printk("Not detected a SMSC FDC37C93xAPM SuperIO device (devid=0x%02x, rev=0x%02x)\n",
+                       devid, devrev);
+                       /* inform the keyboard driver that we have no keyboard controller */
+               microdev_kbd_controller_present = 0;
+                       /* little point in doing anything else in this functon */
+               return 0;
+       }
+
+               /* Select the keyboard device */
+       SMSC_WRITE_INDEXED(SMSC_KEYBOARD_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+               /* enable it */
+       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+               /* enable the interrupts */
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_KEYBOARD, SMSC_PRIMARY_INT_INDEX);
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_MOUSE, SMSC_SECONDARY_INT_INDEX);
+
+               /* Select the Serial #1 device */
+       SMSC_WRITE_INDEXED(SMSC_SERIAL1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+               /* enable it */
+       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+               /* program with port addresses */
+       SMSC_WRITE_INDEXED(MSB(SERIAL1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(SERIAL1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+       SMSC_WRITE_INDEXED(0x00, SMSC_HDCS0_INDEX);
+               /* enable the interrupts */
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_SERIAL1, SMSC_PRIMARY_INT_INDEX);
+
+               /* Select the Serial #2 device */
+       SMSC_WRITE_INDEXED(SMSC_SERIAL2_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+               /* enable it */
+       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+               /* program with port addresses */
+       SMSC_WRITE_INDEXED(MSB(SERIAL2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(SERIAL2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+       SMSC_WRITE_INDEXED(0x00, SMSC_HDCS0_INDEX);
+               /* enable the interrupts */
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_SERIAL2, SMSC_PRIMARY_INT_INDEX);
+
+               /* Select the IDE#1 device */
+       SMSC_WRITE_INDEXED(SMSC_IDE1_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+               /* enable it */
+       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+               /* program with port addresses */
+       SMSC_WRITE_INDEXED(MSB(IDE1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(IDE1_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+       SMSC_WRITE_INDEXED(MSB(IDE1_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(IDE1_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+1);
+       SMSC_WRITE_INDEXED(0x0c, SMSC_HDCS0_INDEX);
+       SMSC_WRITE_INDEXED(0x00, SMSC_HDCS1_INDEX);
+               /* select the interrupt */
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_IDE1, SMSC_PRIMARY_INT_INDEX);
+
+               /* Select the IDE#2 device */
+       SMSC_WRITE_INDEXED(SMSC_IDE2_DEVICE, SMCS_LOGICAL_DEV_INDEX);
+               /* enable it */
+       SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX);
+               /* program with port addresses */
+       SMSC_WRITE_INDEXED(MSB(IDE2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(IDE2_PRIMARY_BASE), SMSC_PRIMARY_BASE_INDEX+1);
+       SMSC_WRITE_INDEXED(MSB(IDE2_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+0);
+       SMSC_WRITE_INDEXED(LSB(IDE2_SECONDARY_BASE), SMSC_SECONDARY_BASE_INDEX+1);
+               /* select the interrupt */
+       SMSC_WRITE_INDEXED(MICRODEV_FPGA_IRQ_IDE2, SMSC_PRIMARY_INT_INDEX);
+
+               /* Select the configuration registers */
+       SMSC_WRITE_INDEXED(SMSC_CONFIG_REGISTERS, SMCS_LOGICAL_DEV_INDEX);
+               /* enable the appropriate GPIO pins for IDE functionality:
+                * bit[0]   In/Out              1==input;  0==output
+                * bit[1]   Polarity            1==invert; 0==no invert
+                * bit[2]   Int Enb #1          1==Enable Combined IRQ #1; 0==disable
+                * bit[3:4] Function Select     00==original; 01==Alternate Function #1
+                */
+       SMSC_WRITE_INDEXED(0x00, 0xc2); /* GP42 = nIDE1_OE */
+       SMSC_WRITE_INDEXED(0x01, 0xc5); /* GP45 = IDE1_IRQ */
+       SMSC_WRITE_INDEXED(0x00, 0xc6); /* GP46 = nIOROP */
+       SMSC_WRITE_INDEXED(0x00, 0xc7); /* GP47 = nIOWOP */
+       SMSC_WRITE_INDEXED(0x08, 0xe8); /* GP20 = nIDE2_OE */
+
+               /* Exit the configuraton state */
+       outb(SMSC_EXIT_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR);
+
+       return 0;
+}
+
+
+/* This is grotty, but, because kernel is always referenced on the link line
+ * before any devices, this is safe.
+ */
+__initcall(smsc_superio_setup);
diff --git a/arch/sh/configs/microdev_defconfig b/arch/sh/configs/microdev_defconfig
new file mode 100644 (file)
index 0000000..e76a93a
--- /dev/null
@@ -0,0 +1,680 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_SUPERH=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_SWAP=y
+# CONFIG_SYSVIPC is not set
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 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=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS 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 is not set
+
+#
+# System type
+#
+# CONFIG_SH_SOLUTION_ENGINE is not set
+# CONFIG_SH_7751_SOLUTION_ENGINE is not set
+# CONFIG_SH_7300_SOLUTION_ENGINE is not set
+# CONFIG_SH_73180_SOLUTION_ENGINE is not set
+# CONFIG_SH_7751_SYSTEMH is not set
+# CONFIG_SH_STB1_HARP is not set
+# CONFIG_SH_STB1_OVERDRIVE is not set
+# CONFIG_SH_HP620 is not set
+# CONFIG_SH_HP680 is not set
+# CONFIG_SH_HP690 is not set
+# CONFIG_SH_CQREEK is not set
+# CONFIG_SH_DMIDA is not set
+# CONFIG_SH_EC3104 is not set
+# CONFIG_SH_SATURN is not set
+# CONFIG_SH_DREAMCAST is not set
+# CONFIG_SH_CAT68701 is not set
+# CONFIG_SH_BIGSUR is not set
+# CONFIG_SH_SH2000 is not set
+# CONFIG_SH_ADX is not set
+# CONFIG_SH_MPC1211 is not set
+# CONFIG_SH_SH03 is not set
+# CONFIG_SH_SECUREEDGE5410 is not set
+# CONFIG_SH_HS7751RVOIP is not set
+# CONFIG_SH_RTS7751R2D is not set
+# CONFIG_SH_EDOSK7705 is not set
+CONFIG_SH_SH4202_MICRODEV=y
+# CONFIG_SH_UNKNOWN is not set
+# CONFIG_CPU_SH2 is not set
+# CONFIG_CPU_SH3 is not set
+CONFIG_CPU_SH4=y
+# CONFIG_CPU_SUBTYPE_SH7604 is not set
+# CONFIG_CPU_SUBTYPE_SH7300 is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH73180 is not set
+# CONFIG_CPU_SUBTYPE_ST40STB1 is not set
+# CONFIG_CPU_SUBTYPE_ST40GX1 is not set
+CONFIG_CPU_SUBTYPE_SH4_202=y
+CONFIG_MMU=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC0,115200"
+CONFIG_MEMORY_START=0x08000000
+CONFIG_MEMORY_SIZE=0x04000000
+CONFIG_MEMORY_SET=y
+# CONFIG_MEMORY_OVERRIDE is not set
+CONFIG_SH_RTC=y
+CONFIG_SH_FPU=y
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CPU_LITTLE_ENDIAN=y
+CONFIG_PREEMPT=y
+# CONFIG_UBC_WAKEUP is not set
+# CONFIG_SH_WRITETHROUGH is not set
+# CONFIG_SH_OCRAM is not set
+# CONFIG_SH_STORE_QUEUES is not set
+# CONFIG_SMP is not set
+CONFIG_SH_PCLK_CALC=y
+CONFIG_SH_PCLK_FREQ=65986048
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+CONFIG_SH_DMA=y
+CONFIG_NR_ONCHIP_DMA_CHANNELS=4
+# CONFIG_NR_DMA_CHANNELS_BOOL is not set
+
+#
+# Companion Chips
+#
+# CONFIG_HD6446X_SERIES is not set
+CONFIG_HEARTBEAT=y
+
+#
+# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+#
+CONFIG_ISA=y
+# CONFIG_PCI is not set
+
+#
+# PCMCIA/CardBus support
+#
+# CONFIG_PCMCIA is not set
+CONFIG_PCMCIA_PROBE=y
+
+#
+# PCI Hotplug Support
+#
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_FLAT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# SH initrd options
+#
+# CONFIG_EMBEDDED_RAMDISK is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_LBD is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=1
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECD=y
+# 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_IDE_SH=y
+# CONFIG_IDE_ARM is not set
+# CONFIG_IDE_CHIPSETS 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
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# I2O device support
+#
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_PACKET 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 is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# 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_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_STNIC is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+CONFIG_SMC91X=y
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# 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
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+# CONFIG_INPUT is not set
+
+#
+# Userland interfaces
+#
+
+#
+# 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
+#
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_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_RTC=y
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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
+
+#
+# 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=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# 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_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 is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+CONFIG_DEVFS_FS=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS_XATTR=y
+# CONFIG_DEVPTS_FS_SECURITY is not set
+CONFIG_TMPFS=y
+# CONFIG_HUGETLBFS 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_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=y
+# 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_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# 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 is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# 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_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# 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
+
+#
+# Kernel hacking
+#
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_KGDB is not set
+# CONFIG_FRAME_POINTER is not set
+
+#
+# Security options
+#
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
diff --git a/arch/sh/configs/se73180_defconfig b/arch/sh/configs/se73180_defconfig
new file mode 100644 (file)
index 0000000..1d5884d
--- /dev/null
@@ -0,0 +1,430 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_SUPERH=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 is not set
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_SYSCTL is not set
+# 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 is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+
+#
+# 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_SH_SOLUTION_ENGINE is not set
+# CONFIG_SH_7751_SOLUTION_ENGINE is not set
+# CONFIG_SH_7300_SOLUTION_ENGINE is not set
+CONFIG_SH_73180_SOLUTION_ENGINE=y
+# CONFIG_SH_7751_SYSTEMH is not set
+# CONFIG_SH_STB1_HARP is not set
+# CONFIG_SH_STB1_OVERDRIVE is not set
+# CONFIG_SH_HP620 is not set
+# CONFIG_SH_HP680 is not set
+# CONFIG_SH_HP690 is not set
+# CONFIG_SH_CQREEK is not set
+# CONFIG_SH_DMIDA is not set
+# CONFIG_SH_EC3104 is not set
+# CONFIG_SH_SATURN is not set
+# CONFIG_SH_DREAMCAST is not set
+# CONFIG_SH_CAT68701 is not set
+# CONFIG_SH_BIGSUR is not set
+# CONFIG_SH_SH2000 is not set
+# CONFIG_SH_ADX is not set
+# CONFIG_SH_MPC1211 is not set
+# CONFIG_SH_SECUREEDGE5410 is not set
+# CONFIG_SH_HS7751RVOIP is not set
+# CONFIG_SH_RTS7751R2D is not set
+# CONFIG_SH_UNKNOWN is not set
+# CONFIG_CPU_SH2 is not set
+# CONFIG_CPU_SH3 is not set
+CONFIG_CPU_SH4=y
+# CONFIG_CPU_SUBTYPE_SH7604 is not set
+# CONFIG_CPU_SUBTYPE_SH7300 is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+CONFIG_CPU_SUBTYPE_SH73180=y
+# CONFIG_CPU_SUBTYPE_ST40STB1 is not set
+# CONFIG_CPU_SUBTYPE_ST40GX1 is not set
+CONFIG_MMU=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC0,38400 root=/dev/ram"
+CONFIG_MEMORY_START=0x0c000000
+CONFIG_MEMORY_SIZE=0x02000000
+# CONFIG_MEMORY_OVERRIDE is not set
+# CONFIG_SH_FPU is not set
+CONFIG_ZERO_PAGE_OFFSET=0x00010000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_PREEMPT is not set
+# CONFIG_UBC_WAKEUP is not set
+# CONFIG_SH_WRITETHROUGH is not set
+# CONFIG_SH_OCRAM is not set
+# CONFIG_SH_STORE_QUEUES is not set
+# CONFIG_SMP is not set
+# CONFIG_SH_PCLK_CALC is not set
+CONFIG_SH_PCLK_FREQ=27000000
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+# CONFIG_HD6446X_SERIES is not set
+CONFIG_HEARTBEAT=y
+
+#
+# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+#
+# CONFIG_PCI is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_FLAT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# SH initrd options
+#
+CONFIG_EMBEDDED_RAMDISK=y
+CONFIG_EMBEDDED_RAMDISK_IMAGE="ramdisk.gz"
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+
+#
+# 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_FD is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_LBD is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI 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 is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+
+#
+# ISDN subsystem
+#
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+# CONFIG_INPUT is not set
+
+#
+# Userland interfaces
+#
+
+#
+# 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
+#
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_UNIX98_PTYS is not set
+# CONFIG_LEGACY_PTYS is not set
+# 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_SH_WDT 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
+
+#
+# Misc devices
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+
+#
+# Graphics support
+#
+# CONFIG_FB is not set
+
+#
+# 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 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 is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+# CONFIG_SYSFS is not set
+CONFIG_DEVFS_FS=y
+CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_TMPFS=y
+# CONFIG_HUGETLBFS 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_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
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_INFO is not set
+CONFIG_SH_STANDARD_BIOS=y
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_EARLY_PRINTK is not set
+# CONFIG_KGDB is not set
+# CONFIG_FRAME_POINTER 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
diff --git a/arch/sh/configs/se7705_defconfig b/arch/sh/configs/se7705_defconfig
new file mode 100644 (file)
index 0000000..bf48af3
--- /dev/null
@@ -0,0 +1,645 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_SUPERH=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+# CONFIG_SWAP is not set
+# CONFIG_SYSVIPC is not set
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_SYSCTL is not set
+# 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 is not set
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_IOSCHED_NOOP is not set
+CONFIG_IOSCHED_AS=y
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODULE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# System type
+#
+CONFIG_SH_SOLUTION_ENGINE=y
+# CONFIG_SH_7751_SOLUTION_ENGINE is not set
+# CONFIG_SH_7300_SOLUTION_ENGINE is not set
+# CONFIG_SH_73180_SOLUTION_ENGINE is not set
+# CONFIG_SH_7751_SYSTEMH is not set
+# CONFIG_SH_STB1_HARP is not set
+# CONFIG_SH_STB1_OVERDRIVE is not set
+# CONFIG_SH_HP620 is not set
+# CONFIG_SH_HP680 is not set
+# CONFIG_SH_HP690 is not set
+# CONFIG_SH_CQREEK is not set
+# CONFIG_SH_DMIDA is not set
+# CONFIG_SH_EC3104 is not set
+# CONFIG_SH_SATURN is not set
+# CONFIG_SH_DREAMCAST is not set
+# CONFIG_SH_CAT68701 is not set
+# CONFIG_SH_BIGSUR is not set
+# CONFIG_SH_SH2000 is not set
+# CONFIG_SH_ADX is not set
+# CONFIG_SH_MPC1211 is not set
+# CONFIG_SH_SECUREEDGE5410 is not set
+# CONFIG_SH_HS7751RVOIP is not set
+# CONFIG_SH_RTS7751R2D is not set
+# CONFIG_SH_EDOSK7705 is not set
+# CONFIG_SH_SH4202_MICRODEV is not set
+# CONFIG_SH_UNKNOWN is not set
+# CONFIG_CPU_SH2 is not set
+CONFIG_CPU_SH3=y
+# CONFIG_CPU_SH4 is not set
+# CONFIG_CPU_SUBTYPE_SH7604 is not set
+# CONFIG_CPU_SUBTYPE_SH7300 is not set
+CONFIG_CPU_SUBTYPE_SH7705=y
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH73180 is not set
+# CONFIG_CPU_SUBTYPE_ST40STB1 is not set
+# CONFIG_CPU_SUBTYPE_ST40GX1 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+CONFIG_SH7705_CACHE_32KB=y
+CONFIG_MMU=y
+# CONFIG_CMDLINE_BOOL is not set
+CONFIG_MEMORY_START=0x0c000000
+CONFIG_MEMORY_SIZE=0x02000000
+CONFIG_MEMORY_SET=y
+# CONFIG_MEMORY_OVERRIDE is not set
+# CONFIG_CF_ENABLER is not set
+CONFIG_SH_RTC=y
+# CONFIG_SH_DSP is not set
+# CONFIG_SH_ADC is not set
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CPU_LITTLE_ENDIAN=y
+CONFIG_PREEMPT=y
+# CONFIG_UBC_WAKEUP is not set
+# CONFIG_SH_WRITETHROUGH is not set
+# CONFIG_SH_OCRAM is not set
+# CONFIG_SMP is not set
+CONFIG_SH_PCLK_CALC=y
+CONFIG_SH_PCLK_FREQ=33333333
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+# CONFIG_HD6446X_SERIES is not set
+CONFIG_HEARTBEAT=y
+
+#
+# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+#
+# CONFIG_PCI is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_FLAT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# SH initrd options
+#
+# CONFIG_EMBEDDED_RAMDISK is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+
+#
+# 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 is not set
+# CONFIG_MTD_CMDLINE_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_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_AMDSTD_RETRY=0
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_SOLUTIONENGINE=y
+CONFIG_MTD_SUPERH_RESERVE=0x300000
+# CONFIG_MTD_MPC1211 is not set
+# CONFIG_MTD_RTS7751R2D is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM 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
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT 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=8192
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_LBD is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_SCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+
+#
+# IEEE 1394 (FireWire) support
+#
+
+#
+# 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=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=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# 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_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+CONFIG_STNIC=y
+
+#
+# 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=y
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=y
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=y
+# CONFIG_PPP_BSDCOMP is not set
+# CONFIG_PPPOE is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 is not set
+# 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 is not set
+# 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 is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+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_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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
+
+#
+# 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 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_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+# CONFIG_SYSFS is not set
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVPTS_FS_XATTR is not set
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLBFS 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_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN 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_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_KGDB is not set
+# CONFIG_FRAME_POINTER is not set
+
+#
+# Security options
+#
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=y
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
diff --git a/arch/sh/configs/sh03_defconfig b/arch/sh/configs/sh03_defconfig
new file mode 100644 (file)
index 0000000..100d08f
--- /dev/null
@@ -0,0 +1,936 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_SUPERH=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+# CONFIG_CLEAN_COMPILE is not set
+CONFIG_BROKEN=y
+CONFIG_BROKEN_ON_SMP=y
+
+#
+# General setup
+#
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 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_KALLSYMS_EXTRA_PASS 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
+
+#
+# System type
+#
+# CONFIG_SH_SOLUTION_ENGINE is not set
+# CONFIG_SH_7751_SOLUTION_ENGINE is not set
+# CONFIG_SH_7300_SOLUTION_ENGINE is not set
+# CONFIG_SH_73180_SOLUTION_ENGINE is not set
+# CONFIG_SH_7751_SYSTEMH is not set
+# CONFIG_SH_STB1_HARP is not set
+# CONFIG_SH_STB1_OVERDRIVE is not set
+# CONFIG_SH_HP620 is not set
+# CONFIG_SH_HP680 is not set
+# CONFIG_SH_HP690 is not set
+# CONFIG_SH_CQREEK is not set
+# CONFIG_SH_DMIDA is not set
+# CONFIG_SH_EC3104 is not set
+# CONFIG_SH_SATURN is not set
+# CONFIG_SH_DREAMCAST is not set
+# CONFIG_SH_CAT68701 is not set
+# CONFIG_SH_BIGSUR is not set
+# CONFIG_SH_SH2000 is not set
+# CONFIG_SH_ADX is not set
+# CONFIG_SH_MPC1211 is not set
+CONFIG_SH_SH03=y
+# CONFIG_SH_SECUREEDGE5410 is not set
+# CONFIG_SH_HS7751RVOIP is not set
+# CONFIG_SH_RTS7751R2D is not set
+# CONFIG_SH_EDOSK7705 is not set
+# CONFIG_SH_SH4202_MICRODEV is not set
+# CONFIG_SH_UNKNOWN is not set
+# CONFIG_CPU_SH2 is not set
+# CONFIG_CPU_SH3 is not set
+CONFIG_CPU_SH4=y
+# CONFIG_CPU_SUBTYPE_SH7604 is not set
+# CONFIG_CPU_SUBTYPE_SH7300 is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+CONFIG_CPU_SUBTYPE_SH7751=y
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH73180 is not set
+# CONFIG_CPU_SUBTYPE_ST40STB1 is not set
+# CONFIG_CPU_SUBTYPE_ST40GX1 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+CONFIG_MMU=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC1,115200 mem=64M root=/dev/nfs"
+CONFIG_MEMORY_START=0x08000000
+CONFIG_MEMORY_SIZE=0x08000000
+CONFIG_MEMORY_SET=y
+# CONFIG_MEMORY_OVERRIDE is not set
+CONFIG_CF_ENABLER=y
+CONFIG_CF_AREA5=y
+# CONFIG_CF_AREA6 is not set
+CONFIG_CF_BASE_ADDR=0xb4000000
+CONFIG_SH_RTC=y
+CONFIG_SH_FPU=y
+CONFIG_ZERO_PAGE_OFFSET=0x00004000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CPU_LITTLE_ENDIAN=y
+CONFIG_PREEMPT=y
+# CONFIG_UBC_WAKEUP is not set
+# CONFIG_SH_WRITETHROUGH is not set
+# CONFIG_SH_OCRAM is not set
+# CONFIG_SH_STORE_QUEUES is not set
+# CONFIG_SMP is not set
+CONFIG_SH_PCLK_CALC=y
+CONFIG_SH_PCLK_FREQ=49876504
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+# CONFIG_HD6446X_SERIES is not set
+CONFIG_HEARTBEAT=y
+
+#
+# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+#
+CONFIG_ISA=y
+CONFIG_PCI=y
+CONFIG_SH_PCIDMA_NONCOHERENT=y
+CONFIG_PCI_AUTO=y
+CONFIG_PCI_AUTO_UPDATE_RESOURCES=y
+CONFIG_PCI_LEGACY_PROC=y
+CONFIG_PCI_NAMES=y
+
+#
+# PCMCIA/CardBus support
+#
+CONFIG_PCMCIA=m
+# CONFIG_PCMCIA_DEBUG is not set
+# CONFIG_YENTA is not set
+# CONFIG_PD6729 is not set
+# CONFIG_I82092 is not set
+# CONFIG_I82365 is not set
+# CONFIG_TCIC is not set
+CONFIG_PCMCIA_PROBE=y
+
+#
+# PCI Hotplug Support
+#
+CONFIG_HOTPLUG_PCI=m
+# CONFIG_HOTPLUG_PCI_FAKE is not set
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+# CONFIG_HOTPLUG_PCI_PCIE is not set
+# CONFIG_HOTPLUG_PCI_SHPC is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_FLAT is not set
+CONFIG_BINFMT_MISC=y
+
+#
+# SH initrd options
+#
+# CONFIG_EMBEDDED_RAMDISK is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_FW_LOADER is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+# CONFIG_PNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD 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=y
+# CONFIG_BLK_DEV_SX8 is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_LBD is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=4
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+CONFIG_BLK_DEV_IDECS=m
+CONFIG_BLK_DEV_IDECD=m
+CONFIG_BLK_DEV_IDETAPE=m
+CONFIG_BLK_DEV_IDEFLOPPY=m
+# CONFIG_BLK_DEV_IDESCSI 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 is not set
+CONFIG_IDE_SH=y
+# CONFIG_IDE_ARM is not set
+# CONFIG_IDE_CHIPSETS 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=m
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=m
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CPQFCTS is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+CONFIG_SCSI_QLA2XXX=m
+# CONFIG_SCSI_QLA21XX is not set
+# CONFIG_SCSI_QLA22XX is not set
+# CONFIG_SCSI_QLA2300 is not set
+# CONFIG_SCSI_QLA2322 is not set
+# CONFIG_SCSI_QLA6312 is not set
+# CONFIG_SCSI_QLA6322 is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# PCMCIA SCSI adapter support
+#
+# CONFIG_PCMCIA_AHA152X is not set
+# CONFIG_PCMCIA_FDOMAIN is not set
+# CONFIG_PCMCIA_NINJA_SCSI is not set
+# CONFIG_PCMCIA_QLOGIC is not set
+# CONFIG_PCMCIA_SYM53C500 is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD 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
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# 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
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER 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_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE 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
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_STNIC is not set
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+
+#
+# Tulip family network device support
+#
+# CONFIG_NET_TULIP is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_PCI=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_B44 is not set
+# CONFIG_FORCEDETH is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_DGRS is not set
+# CONFIG_EEPRO100 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=y
+# CONFIG_8139TOO is not set
+# CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_VELOCITY is not set
+# CONFIG_NET_POCKET 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 is not set
+
+#
+# PCMCIA network device support
+#
+# CONFIG_NET_PCMCIA is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE 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 is not set
+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=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=m
+# CONFIG_SERIAL_8250_CS is not set
+CONFIG_SERIAL_8250_NR_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_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=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_SH_WDT=m
+
+#
+# ISA-based Watchdog Cards
+#
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_MIXCOMWD is not set
+# CONFIG_WDT is not set
+
+#
+# PCI-based Watchdog Cards
+#
+# CONFIG_PCIPCWATCHDOG is not set
+# CONFIG_WDTPCI is not set
+# CONFIG_RTC is not set
+CONFIG_SH03_RTC=y
+# 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_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
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 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
+#
+# CONFIG_USB is not set
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+# CONFIG_EXT2_FS_POSIX_ACL is not set
+# 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=y
+CONFIG_AUTOFS4_FS=y
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_ZISOFS_FS=m
+CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# 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=y
+# CONFIG_HUGETLBFS 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_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=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=y
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V4 is not set
+CONFIG_NFSD_TCP=y
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=y
+CONFIG_SUNRPC=y
+CONFIG_SUNRPC_GSS=y
+CONFIG_RPCSEC_GSS_KRB5=y
+# 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_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=m
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+
+#
+# Profiling support
+#
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_SH_STANDARD_BIOS=y
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_EARLY_PRINTK is not set
+# CONFIG_KGDB is not set
+# CONFIG_FRAME_POINTER is not set
+
+#
+# Security options
+#
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Library routines
+#
+CONFIG_CRC_CCITT=y
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
diff --git a/arch/sh/drivers/pci/fixups-sh03.c b/arch/sh/drivers/pci/fixups-sh03.c
new file mode 100644 (file)
index 0000000..5db5cb9
--- /dev/null
@@ -0,0 +1,61 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+
+/*
+ *     IRQ functions
+ */
+
+int __init pcibios_map_platform_irq(u8 slot, u8 pin, struct pci_dev *dev)
+{
+       int irq;
+
+       if (dev->bus->number == 0) {
+               switch (slot) {
+               case 4: return 5;       /* eth0       */
+               case 8: return 5;       /* eth1       */
+               case 6: return 2;       /* PCI bridge */
+               default:
+                       printk("PCI: Bad IRQ mapping request for slot %d\n", slot);
+                       return 2;
+               }
+       } else {
+               switch (pin) {
+               case 0:   irq =  2; break;
+               case 1:   irq =  2; break;
+               case 2:   irq =  2; break;
+               case 3:   irq =  2; break;
+               case 4:   irq =  2; break;
+               default:  irq = -1; break;
+               }
+       }
+       return irq;
+}
+
+static u8 __init sh03_no_swizzle(struct pci_dev *dev, u8 *pin)
+{
+       /* no swizzling */
+       return PCI_SLOT(dev->devfn);
+}
+
+static int sh03_pci_lookup_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+       int irq = -1;
+
+       /* now lookup the actual IRQ on a platform specific basis (pci-'platform'.c) */
+       irq = pcibios_map_platform_irq(slot, pin, dev);
+       if( irq < 0 ) {
+               pr_debug("PCI: Error mapping IRQ on device %s\n", dev->slot_name);
+               return irq;
+       }
+
+       pr_debug("Setting IRQ for slot %s to %d\n", dev->slot_name, irq);
+
+       return irq;
+}
+
+void __init pcibios_fixup_irqs(void)
+{
+       pci_fixup_irqs(sh03_no_swizzle, sh03_pci_lookup_irq);
+}
diff --git a/arch/sh/drivers/pci/ops-sh03.c b/arch/sh/drivers/pci/ops-sh03.c
new file mode 100644 (file)
index 0000000..df21997
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * linux/arch/sh/drivers/pci/ops-sh03.c
+ *
+ * PCI initialization for the Interface CTP/PCI-SH03 board
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include "pci-sh7751.h"
+
+/*
+ * Description:  This function sets up and initializes the pcic, sets
+ * up the BARS, maps the DRAM into the address space etc, etc.
+ */
+int __init pcibios_init_platform(void)
+{
+   return 1;
+}
+
+static struct resource sh7751_io_resource = {
+       .name   = "SH03 IO",
+       .start  = SH7751_PCI_IO_BASE,
+       .end    = SH7751_PCI_IO_BASE + SH7751_PCI_IO_SIZE - 1,
+       .flags  = IORESOURCE_IO
+};
+
+static struct resource sh7751_mem_resource = {
+       .name   = "SH03 mem",
+       .start  = SH7751_PCI_MEMORY_BASE,
+       .end    = SH7751_PCI_MEMORY_BASE + SH7751_PCI_MEM_SIZE - 1,
+       .flags  = IORESOURCE_MEM
+};
+
+extern struct pci_ops sh7751_pci_ops;
+
+struct pci_channel board_pci_channels[] = {
+       { &sh7751_pci_ops, &sh7751_io_resource, &sh7751_mem_resource, 0, 0xff },
+       { NULL, NULL, NULL, 0, 0 },
+};
+
diff --git a/arch/sh/kernel/asm-offsets.c b/arch/sh/kernel/asm-offsets.c
new file mode 100644 (file)
index 0000000..dc6725c
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * This program is used to generate definitions needed by
+ * assembly language modules.
+ *
+ * We use the technique used in the OSF Mach kernel code:
+ * generate asm statements containing #defines,
+ * compile this file to assembler, and then extract the
+ * #defines from the assembly-language output.
+ */
+
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <asm/thread_info.h>
+
+#define DEFINE(sym, val) \
+        asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+#define BLANK() asm volatile("\n->" : : )
+
+int main(void)
+{
+       /* offsets into the thread_info struct */
+       DEFINE(TI_TASK,         offsetof(struct thread_info, task));
+       DEFINE(TI_EXEC_DOMAIN,  offsetof(struct thread_info, exec_domain));
+       DEFINE(TI_FLAGS,        offsetof(struct thread_info, flags));
+       DEFINE(TI_CPU,          offsetof(struct thread_info, cpu));
+       DEFINE(TI_PRE_COUNT,    offsetof(struct thread_info, preempt_count));
+       DEFINE(TI_RESTART_BLOCK,offsetof(struct thread_info, restart_block));
+
+       return 0;
+}
diff --git a/arch/sh/kernel/cpu/sh2/probe.c b/arch/sh/kernel/cpu/sh2/probe.c
new file mode 100644 (file)
index 0000000..f17a2a0
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * arch/sh/kernel/cpu/sh2/probe.c
+ *
+ * CPU Subtype Probing for SH-2.
+ *
+ * Copyright (C) 2002 Paul Mundt
+ *
+ * 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 <linux/init.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+
+int __init detect_cpu_and_cache_system(void)
+{
+       /*
+        * For now, assume SH7604 .. fix this later.
+        */
+       cpu_data->type                  = CPU_SH7604;
+       cpu_data->dcache.ways           = 4;
+       cpu_data->dcache.way_shift      = 6;
+       cpu_data->dcache.sets           = 64;
+       cpu_data->dcache.entry_shift    = 4;
+       cpu_data->dcache.linesz         = L1_CACHE_BYTES;
+       cpu_data->dcache.flags          = 0;
+
+       /*
+        * SH-2 doesn't have separate caches
+        */
+       cpu_data->dcache.flags |= SH_CACHE_COMBINED;
+       cpu_data->icache = cpu_data->dcache;
+
+       return 0;
+}
+
diff --git a/arch/sh/kernel/cpu/sh3/probe.c b/arch/sh/kernel/cpu/sh3/probe.c
new file mode 100644 (file)
index 0000000..5cdc886
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * arch/sh/kernel/cpu/sh3/probe.c
+ *
+ * CPU Subtype Probing for SH-3.
+ *
+ * Copyright (C) 1999, 2000  Niibe Yutaka
+ * Copyright (C) 2002  Paul Mundt
+ *
+ * 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 <linux/init.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+
+int __init detect_cpu_and_cache_system(void)
+{
+       unsigned long addr0, addr1, data0, data1, data2, data3;
+
+       jump_to_P2();
+       /*
+        * Check if the entry shadows or not.
+        * When shadowed, it's 128-entry system.
+        * Otherwise, it's 256-entry system.
+        */
+       addr0 = CACHE_OC_ADDRESS_ARRAY + (3 << 12);
+       addr1 = CACHE_OC_ADDRESS_ARRAY + (1 << 12);
+
+       /* First, write back & invalidate */
+       data0  = ctrl_inl(addr0);
+       ctrl_outl(data0&~(SH_CACHE_VALID|SH_CACHE_UPDATED), addr0);
+       data1  = ctrl_inl(addr1);
+       ctrl_outl(data1&~(SH_CACHE_VALID|SH_CACHE_UPDATED), addr1);
+
+       /* Next, check if there's shadow or not */
+       data0 = ctrl_inl(addr0);
+       data0 ^= SH_CACHE_VALID;
+       ctrl_outl(data0, addr0);
+       data1 = ctrl_inl(addr1);
+       data2 = data1 ^ SH_CACHE_VALID;
+       ctrl_outl(data2, addr1);
+       data3 = ctrl_inl(addr0);
+
+       /* Lastly, invaliate them. */
+       ctrl_outl(data0&~SH_CACHE_VALID, addr0);
+       ctrl_outl(data2&~SH_CACHE_VALID, addr1);
+
+       back_to_P1();
+
+       cpu_data->dcache.ways           = 4;
+       cpu_data->dcache.entry_shift    = 4;
+       cpu_data->dcache.linesz         = L1_CACHE_BYTES;
+       cpu_data->dcache.flags          = 0;
+
+       /*
+        * 7709A/7729 has 16K cache (256-entry), while 7702 has only
+        * 2K(direct) 7702 is not supported (yet)
+        */
+       if (data0 == data1 && data2 == data3) { /* Shadow */
+               cpu_data->dcache.way_incr       = (1 << 11);
+               cpu_data->dcache.entry_mask     = 0x7f0;
+               cpu_data->dcache.sets           = 128;
+               cpu_data->type = CPU_SH7708;
+
+               cpu_data->flags |= CPU_HAS_MMU_PAGE_ASSOC;
+       } else {                                /* 7709A or 7729  */
+               cpu_data->dcache.way_incr       = (1 << 12);
+               cpu_data->dcache.entry_mask     = 0xff0;
+               cpu_data->dcache.sets           = 256;
+               cpu_data->type = CPU_SH7729;
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7705)
+               cpu_data->type = CPU_SH7705;
+
+#if defined(CONFIG_SH7705_CACHE_32KB)
+               cpu_data->dcache.way_incr       = (1 << 13);
+               cpu_data->dcache.entry_mask     = 0x1ff0;
+               cpu_data->dcache.sets           = 512;
+               ctrl_outl(CCR_CACHE_32KB, CCR3);
+#else
+               ctrl_outl(CCR_CACHE_16KB, CCR3);
+#endif
+#endif
+       }
+
+       /*
+        * SH-3 doesn't have separate caches
+        */
+       cpu_data->dcache.flags |= SH_CACHE_COMBINED;
+       cpu_data->icache = cpu_data->dcache;
+
+       return 0;
+}
+
diff --git a/arch/sh/kernel/cpu/sh4/probe.c b/arch/sh/kernel/cpu/sh4/probe.c
new file mode 100644 (file)
index 0000000..42427b7
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * arch/sh/kernel/cpu/sh4/probe.c
+ *
+ * CPU Subtype Probing for SH-4.
+ *
+ * Copyright (C) 2001, 2002, 2003, 2004  Paul Mundt
+ * Copyright (C) 2003  Richard Curnow
+ *
+ * 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 <linux/init.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+
+int __init detect_cpu_and_cache_system(void)
+{
+       unsigned long pvr, prr, cvr;
+       unsigned long size;
+
+       static unsigned long sizes[16] = {
+               [1] = (1 << 12),
+               [2] = (1 << 13),
+               [4] = (1 << 14),
+               [8] = (1 << 15),
+               [9] = (1 << 16)
+       };
+
+       pvr = (ctrl_inl(CCN_PVR) >> 8) & 0xffff;
+       prr = (ctrl_inl(CCN_PRR) >> 4) & 0xff;
+       cvr = (ctrl_inl(CCN_CVR));
+
+       /*
+        * Setup some sane SH-4 defaults for the icache
+        */
+       cpu_data->icache.way_incr       = (1 << 13);
+       cpu_data->icache.entry_shift    = 5;
+       cpu_data->icache.entry_mask     = 0x1fe0;
+       cpu_data->icache.sets           = 256;
+       cpu_data->icache.ways           = 1;
+       cpu_data->icache.linesz         = L1_CACHE_BYTES;
+
+       /*
+        * And again for the dcache ..
+        */
+       cpu_data->dcache.way_incr       = (1 << 14);
+       cpu_data->dcache.entry_shift    = 5;
+       cpu_data->dcache.entry_mask     = 0x3fe0;
+       cpu_data->dcache.sets           = 512;
+       cpu_data->dcache.ways           = 1;
+       cpu_data->dcache.linesz         = L1_CACHE_BYTES;
+
+       /* Set the FPU flag, virtually all SH-4's have one */
+       cpu_data->flags |= CPU_HAS_FPU;
+
+       /*
+        * Probe the underlying processor version/revision and
+        * adjust cpu_data setup accordingly.
+        */
+       switch (pvr) {
+       case 0x205:
+               cpu_data->type = CPU_SH7750;
+               cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_PERF_COUNTER;
+               break;
+       case 0x206:
+               cpu_data->type = CPU_SH7750S;
+               cpu_data->flags |= CPU_HAS_P2_FLUSH_BUG | CPU_HAS_PERF_COUNTER;
+               break;
+       case 0x1100:
+               cpu_data->type = CPU_SH7751;
+               break;
+       case 0x2000:
+               cpu_data->type = CPU_SH73180;
+               cpu_data->icache.ways = 4;
+               cpu_data->dcache.ways = 4;
+               cpu_data->flags &= ~CPU_HAS_FPU;
+               break;
+       case 0x8000:
+               cpu_data->type = CPU_ST40RA;
+               break;
+       case 0x8100:
+               cpu_data->type = CPU_ST40GX1;
+               break;
+       case 0x700:
+               cpu_data->type = CPU_SH4_501;
+               cpu_data->icache.ways = 2;
+               cpu_data->dcache.ways = 2;
+
+               /* No FPU on the SH4-500 series.. */
+               cpu_data->flags &= ~CPU_HAS_FPU;
+               break;
+       case 0x600:
+               cpu_data->type = CPU_SH4_202;
+               cpu_data->icache.ways = 2;
+               cpu_data->dcache.ways = 2;
+               break;
+       case 0x500 ... 0x501:
+               switch (prr) {
+                   case 0x10: cpu_data->type = CPU_SH7750R; break;
+                   case 0x11: cpu_data->type = CPU_SH7751R; break;
+                   case 0x50: cpu_data->type = CPU_SH7760;  break;
+               }
+
+               cpu_data->icache.ways = 2;
+               cpu_data->dcache.ways = 2;
+
+               break;
+       default:
+               cpu_data->type = CPU_SH_NONE;
+               break;
+       }
+
+       /*
+        * On anything that's not a direct-mapped cache, look to the CVR
+        * for I/D-cache specifics.
+        */
+       if (cpu_data->icache.ways > 1) {
+               size = sizes[(cvr >> 20) & 0xf];
+               cpu_data->icache.way_incr       = (size >> 1);
+               cpu_data->icache.sets           = (size >> 6);
+               cpu_data->icache.entry_mask     =
+                       (cpu_data->icache.way_incr - (1 << 5));
+       }
+
+       if (cpu_data->dcache.ways > 1) {
+               size = sizes[(cvr >> 16) & 0xf];
+               cpu_data->dcache.way_incr       = (size >> 1);
+               cpu_data->dcache.sets           = (size >> 6);
+               cpu_data->dcache.entry_mask     =
+                       (cpu_data->dcache.way_incr - (1 << 5));
+       }
+
+       return 0;
+}
+
diff --git a/arch/sh/lib/memcpy-sh4.S b/arch/sh/lib/memcpy-sh4.S
new file mode 100644 (file)
index 0000000..55f2274
--- /dev/null
@@ -0,0 +1,800 @@
+/*
+ * "memcpy" implementation of SuperH
+ *
+ * Copyright (C) 1999  Niibe Yutaka
+ * Copyright (c) 2002  STMicroelectronics Ltd
+ *   Modified from memcpy.S and micro-optimised for SH4
+ *   Stuart Menefy (stuart.menefy@st.com)
+ *
+ */
+#include <linux/linkage.h>
+#include <linux/config.h>
+
+/*
+ * void *memcpy(void *dst, const void *src, size_t n);
+ *
+ * It is assumed that there is no overlap between src and dst.
+ * If there is an overlap, then the results are undefined.
+ */
+
+       !
+       !       GHIJ KLMN OPQR -->  ...G HIJK LMNO PQR.
+       !
+
+       ! Size is 16 or greater, and may have trailing bytes
+
+       .balign 32
+.Lcase1:
+       ! Read a long word and write a long word at once
+       ! At the start of each iteration, r7 contains last long load
+       add     #-1,r5          !  79 EX
+       mov     r4,r2           !   5 MT (0 cycles latency)
+
+       mov.l   @(r0,r5),r7     !  21 LS (2 cycles latency)
+       add     #-4,r5          !  50 EX
+
+       add     #7,r2           !  79 EX
+       !
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+       ! 6 cycles, 4 bytes per iteration
+3:     mov.l   @(r0,r5),r1     !  21 LS (latency=2)    ! NMLK
+       mov     r7, r3          !   5 MT (latency=0)    ! RQPO
+
+       cmp/hi  r2,r0           !  57 MT
+       shll16  r3              ! 103 EX
+
+       mov     r1,r6           !   5 MT (latency=0)
+       shll8   r3              ! 102 EX                ! Oxxx
+
+       shlr8   r6              ! 106 EX                ! xNML
+       mov     r1, r7          !   5 MT (latency=0)
+
+       or      r6,r3           !  82 EX                ! ONML
+       bt/s    3b              ! 109 BR
+
+        mov.l  r3,@-r0         !  30 LS
+#else
+3:     mov.l   @(r0,r5),r1     !  21 LS (latency=2)    ! KLMN
+       mov     r7,r3           !   5 MT (latency=0)    ! OPQR
+
+       cmp/hi  r2,r0           !  57 MT
+       shlr16  r3              ! 107 EX
+
+       shlr8   r3              ! 106 EX                ! xxxO
+       mov     r1,r6           !   5 MT (latency=0)
+
+       shll8   r6              ! 102 EX                ! LMNx
+       mov     r1,r7           !   5 MT (latency=0)
+
+       or      r6,r3           !  82 EX                ! LMNO
+       bt/s    3b              ! 109 BR
+
+        mov.l  r3,@-r0         !  30 LS
+#endif
+       ! Finally, copy a byte at once, if necessary
+
+       add     #4,r5           !  50 EX
+       cmp/eq  r4,r0           !  54 MT
+
+       add     #-6,r2          !  50 EX
+       bt      9f              ! 109 BR
+
+8:     cmp/hi  r2,r0           !  57 MT
+       mov.b   @(r0,r5),r1     !  20 LS (latency=2)
+
+       bt/s    8b              ! 109 BR
+
+        mov.b  r1,@-r0         !  29 LS
+
+9:     rts
+        nop
+
+
+       !
+       !       GHIJ KLMN OPQR -->  .GHI JKLM NOPQ R...
+       !
+
+       ! Size is 16 or greater, and may have trailing bytes
+
+       .balign 32
+.Lcase3:
+       ! Read a long word and write a long word at once
+       ! At the start of each iteration, r7 contains last long load
+       add     #-3,r5          ! 79 EX
+       mov     r4,r2           !  5 MT (0 cycles latency)
+
+       mov.l   @(r0,r5),r7     ! 21 LS (2 cycles latency)
+       add     #-4,r5          ! 50 EX
+
+       add     #7,r2           !  79 EX
+       !
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+       ! 6 cycles, 4 bytes per iteration
+3:     mov.l   @(r0,r5),r1     !  21 LS (latency=2)    ! NMLK
+       mov     r7, r3          !   5 MT (latency=0)    ! RQPO
+
+       cmp/hi  r2,r0           !  57 MT
+       shll8   r3              ! 102 EX                ! QPOx
+
+       mov     r1,r6           !   5 MT (latency=0)
+       shlr16  r6              ! 107 EX
+
+       shlr8   r6              ! 106 EX                ! xxxN
+       mov     r1, r7          !   5 MT (latency=0)
+
+       or      r6,r3           !  82 EX                ! QPON
+       bt/s    3b              ! 109 BR
+
+        mov.l  r3,@-r0         !  30 LS
+#else
+3:     mov     r1,r3           ! OPQR
+       shlr8   r3              ! xOPQ
+       mov.l   @(r0,r5),r1     ! KLMN
+       mov     r1,r6
+       shll16  r6
+       shll8   r6              ! Nxxx
+       or      r6,r3           ! NOPQ
+       cmp/hi  r2,r0
+       bt/s    3b
+        mov.l  r3,@-r0
+#endif
+
+       ! Finally, copy a byte at once, if necessary
+
+       add     #6,r5           !  50 EX
+       cmp/eq  r4,r0           !  54 MT
+
+       add     #-6,r2          !  50 EX
+       bt      9f              ! 109 BR
+
+8:     cmp/hi  r2,r0           !  57 MT
+       mov.b   @(r0,r5),r1     !  20 LS (latency=2)
+
+       bt/s    8b              ! 109 BR
+
+        mov.b  r1,@-r0         !  29 LS
+
+9:     rts
+        nop
+
+ENTRY(memcpy)
+
+       ! Calculate the invariants which will be used in the remainder
+       ! of the code:
+       !
+       !      r4   -->  [ ...  ] DST             [ ...  ] SRC
+       !                [ ...  ]                 [ ...  ]
+       !                  :                        :
+       !      r0   -->  [ ...  ]       r0+r5 --> [ ...  ]
+       !
+       !
+
+       ! Short circuit the common case of src, dst and len being 32 bit aligned
+       ! and test for zero length move
+
+       mov     r6, r0          !   5 MT (0 cycle latency)
+       or      r4, r0          !  82 EX
+
+       or      r5, r0          !  82 EX
+       tst     r6, r6          !  86 MT
+
+       bt/s    99f             ! 111 BR                (zero len)
+        tst    #3, r0          !  87 MT
+
+       mov     r4, r0          !   5 MT (0 cycle latency)
+       add     r6, r0          !  49 EX
+
+       mov     #16, r1         !   6 EX
+       bt/s    .Lcase00        ! 111 BR                (aligned)
+
+        sub    r4, r5          !  75 EX
+
+       ! Arguments are not nicely long word aligned or zero len.
+       ! Check for small copies, and if so do a simple byte at a time copy.
+       !
+       ! Deciding on an exact value of 'small' is not easy, as the point at which
+       ! using the optimised routines become worthwhile varies (these are the
+       ! cycle counts for differnet sizes using byte-at-a-time vs. optimised):
+       !       size    byte-at-time    long    word    byte
+       !       16      42              39-40   46-50   50-55
+       !       24      58              43-44   54-58   62-67
+       !       36      82              49-50   66-70   80-85
+       ! However the penalty for getting it 'wrong' is much higher for long word
+       ! aligned data (and this is more common), so use a value of 16.
+
+       cmp/gt  r6,r1           !  56 MT
+
+       add     #-1,r5          !  50 EX
+       bf/s    6f              ! 108 BR                (not small)
+
+        mov    r5, r3          !   5 MT (latency=0)
+       shlr    r6              ! 104 EX
+
+       mov.b   @(r0,r5),r1     !  20 LS (latency=2)
+       bf/s    4f              ! 111 BR
+
+        add    #-1,r3          !  50 EX
+       tst     r6, r6          !  86 MT
+
+       bt/s    98f             ! 110 BR
+        mov.b  r1,@-r0         !  29 LS
+
+       ! 4 cycles, 2 bytes per iteration
+3:     mov.b   @(r0,r5),r1     !  20 LS (latency=2)
+
+4:     mov.b   @(r0,r3),r2     !  20 LS (latency=2)
+       dt      r6              !  67 EX
+
+       mov.b   r1,@-r0         !  29 LS
+       bf/s    3b              ! 111 BR
+
+        mov.b  r2,@-r0         !  29 LS
+98:
+       rts
+        nop
+
+99:    rts
+        mov    r4, r0
+
+       ! Size is not small, so its worthwhile looking for optimisations.
+       ! First align destination to a long word boundary.
+       !
+       ! r5 = normal value -1
+
+6:     tst     #3, r0          !  87 MT
+        mov    #3, r3          !   6 EX
+
+       bt/s    2f              ! 111 BR
+        and    r0,r3           !  78 EX
+
+       ! 3 cycles, 1 byte per iteration
+1:     dt      r3              !  67 EX
+       mov.b   @(r0,r5),r1     !  19 LS (latency=2)
+
+       add     #-1, r6         !  79 EX
+       bf/s    1b              ! 109 BR
+
+        mov.b  r1,@-r0         !  28 LS
+
+2:     add     #1, r5          !  79 EX
+
+       ! Now select the appropriate bulk transfer code based on relative
+       ! alignment of src and dst.
+
+       mov     r0, r3          !   5 MT (latency=0)
+
+       mov     r5, r0          !   5 MT (latency=0)
+       tst     #1, r0          !  87 MT
+
+       bf/s    1f              ! 111 BR
+        mov    #64, r7         !   6 EX
+
+       ! bit 0 clear
+
+       cmp/ge  r7, r6          !  55 MT
+
+       bt/s    2f              ! 111 BR
+        tst    #2, r0          !  87 MT
+
+       ! small
+       bt/s    .Lcase0
+        mov    r3, r0
+
+       bra     .Lcase2
+        nop
+
+       ! big
+2:     bt/s    .Lcase0b
+        mov    r3, r0
+
+       bra     .Lcase2b
+        nop
+
+       ! bit 0 set
+1:     tst     #2, r0          ! 87 MT
+
+       bt/s    .Lcase1
+        mov    r3, r0
+
+       bra     .Lcase3
+        nop
+
+
+       !
+       !       GHIJ KLMN OPQR -->  GHIJ KLMN OPQR
+       !
+
+       ! src, dst and size are all long word aligned
+       ! size is non-zero
+
+       .balign 32
+.Lcase00:
+       mov     #64, r1         !   6 EX
+       mov     r5, r3          !   5 MT (latency=0)
+
+       cmp/gt  r6, r1          !  56 MT
+       add     #-4, r5         !  50 EX
+
+       bf      .Lcase00b       ! 108 BR                (big loop)
+       shlr2   r6              ! 105 EX
+
+       shlr    r6              ! 104 EX
+       mov.l   @(r0, r5), r1   !  21 LS (latency=2)
+
+       bf/s    4f              ! 111 BR
+        add    #-8, r3         !  50 EX
+
+       tst     r6, r6          !  86 MT
+       bt/s    5f              ! 110 BR
+
+        mov.l  r1,@-r0         !  30 LS
+
+       ! 4 cycles, 2 long words per iteration
+3:     mov.l   @(r0, r5), r1   !  21 LS (latency=2)
+
+4:     mov.l   @(r0, r3), r2   !  21 LS (latency=2)
+       dt      r6              !  67 EX
+
+       mov.l   r1, @-r0        !  30 LS
+       bf/s    3b              ! 109 BR
+
+        mov.l  r2, @-r0        !  30 LS
+
+5:     rts
+        nop
+
+
+       ! Size is 16 or greater and less than 64, but may have trailing bytes
+
+       .balign 32
+.Lcase0:
+       add     #-4, r5         !  50 EX
+       mov     r4, r7          !   5 MT (latency=0)
+
+       mov.l   @(r0, r5), r1   !  21 LS (latency=2)
+       mov     #4, r2          !   6 EX
+
+       add     #11, r7         !  50 EX
+       tst     r2, r6          !  86 MT
+
+       mov     r5, r3          !   5 MT (latency=0)
+       bt/s    4f              ! 111 BR
+
+        add    #-4, r3         !  50 EX
+       mov.l   r1,@-r0         !  30 LS
+
+       ! 4 cycles, 2 long words per iteration
+3:     mov.l   @(r0, r5), r1   !  21 LS (latency=2)
+
+4:     mov.l   @(r0, r3), r2   !  21 LS (latency=2)
+       cmp/hi  r7, r0
+
+       mov.l   r1, @-r0        !  30 LS
+       bt/s    3b              ! 109 BR
+
+        mov.l  r2, @-r0        !  30 LS
+
+       ! Copy the final 0-3 bytes
+
+       add     #3,r5           !  50 EX
+
+       cmp/eq  r0, r4          !  54 MT
+       add     #-10, r7        !  50 EX
+
+       bt      9f              ! 110 BR
+
+       ! 3 cycles, 1 byte per iteration
+1:     mov.b   @(r0,r5),r1     !  19 LS
+       cmp/hi  r7,r0           !  57 MT
+
+       bt/s    1b              ! 111 BR
+        mov.b  r1,@-r0         !  28 LS
+
+9:     rts
+        nop
+
+       ! Size is at least 64 bytes, so will be going round the big loop at least once.
+       !
+       !   r2 = rounded up r4
+       !   r3 = rounded down r0
+
+       .balign 32
+.Lcase0b:
+       add     #-4, r5         !  50 EX
+
+.Lcase00b:
+       mov     r0, r3          !   5 MT (latency=0)
+       mov     #(~0x1f), r1    !   6 EX
+
+       and     r1, r3          !  78 EX
+       mov     r4, r2          !   5 MT (latency=0)
+
+       cmp/eq  r3, r0          !  54 MT
+       add     #0x1f, r2       !  50 EX
+
+       bt/s    1f              ! 110 BR
+        and    r1, r2          !  78 EX
+
+       ! copy initial words until cache line aligned
+
+       mov.l   @(r0, r5), r1   !  21 LS (latency=2)
+       tst     #4, r0          !  87 MT
+
+       mov     r5, r6          !   5 MT (latency=0)
+       add     #-4, r6         !  50 EX
+
+       bt/s    4f              ! 111 BR
+        add    #8, r3          !  50 EX
+
+       tst     #0x18, r0       !  87 MT
+
+       bt/s    1f              ! 109 BR
+        mov.l  r1,@-r0         !  30 LS
+
+       ! 4 cycles, 2 long words per iteration
+3:     mov.l   @(r0, r5), r1   !  21 LS (latency=2)
+
+4:     mov.l   @(r0, r6), r7   !  21 LS (latency=2)
+       cmp/eq  r3, r0          !  54 MT
+
+       mov.l   r1, @-r0        !  30 LS
+       bf/s    3b              ! 109 BR
+
+        mov.l  r7, @-r0        !  30 LS
+
+       ! Copy the cache line aligned blocks
+       !
+       ! In use: r0, r2, r4, r5
+       ! Scratch: r1, r3, r6, r7
+       !
+       ! We could do this with the four scratch registers, but if src
+       ! and dest hit the same cache line, this will thrash, so make
+       ! use of additional registers.
+       !
+       ! We also need r0 as a temporary (for movca), so 'undo' the invariant:
+       !   r5:  src (was r0+r5)
+       !   r1:  dest (was r0)
+       ! this can be reversed at the end, so we don't need to save any extra
+       ! state.
+       !
+1:     mov.l   r8, @-r15       !  30 LS
+       add     r0, r5          !  49 EX
+
+       mov.l   r9, @-r15       !  30 LS
+       mov     r0, r1          !   5 MT (latency=0)
+
+       mov.l   r10, @-r15      !  30 LS
+       add     #-0x1c, r5      !  50 EX
+
+       mov.l   r11, @-r15      !  30 LS
+
+       ! 16 cycles, 32 bytes per iteration
+2:     mov.l   @(0x00,r5),r0   ! 18 LS (latency=2)
+       add     #-0x20, r1      ! 50 EX
+       mov.l   @(0x04,r5),r3   ! 18 LS (latency=2)
+       mov.l   @(0x08,r5),r6   ! 18 LS (latency=2)
+       mov.l   @(0x0c,r5),r7   ! 18 LS (latency=2)
+       mov.l   @(0x10,r5),r8   ! 18 LS (latency=2)
+       mov.l   @(0x14,r5),r9   ! 18 LS (latency=2)
+       mov.l   @(0x18,r5),r10  ! 18 LS (latency=2)
+       mov.l   @(0x1c,r5),r11  ! 18 LS (latency=2)
+       movca.l r0,@r1          ! 40 LS (latency=3-7)
+       mov.l   r3,@(0x04,r1)   ! 33 LS
+       mov.l   r6,@(0x08,r1)   ! 33 LS
+       mov.l   r7,@(0x0c,r1)   ! 33 LS
+
+       mov.l   r8,@(0x10,r1)   ! 33 LS
+       add     #-0x20, r5      ! 50 EX
+
+       mov.l   r9,@(0x14,r1)   ! 33 LS
+       cmp/eq  r2,r1           ! 54 MT
+
+       mov.l   r10,@(0x18,r1)  !  33 LS
+       bf/s    2b              ! 109 BR
+
+        mov.l  r11,@(0x1c,r1)  !  33 LS
+
+       mov     r1, r0          !   5 MT (latency=0)
+
+       mov.l   @r15+, r11      !  15 LS
+       sub     r1, r5          !  75 EX
+
+       mov.l   @r15+, r10      !  15 LS
+       cmp/eq  r4, r0          !  54 MT
+
+       bf/s    1f              ! 109 BR
+        mov.l   @r15+, r9      !  15 LS
+
+       rts
+1:      mov.l  @r15+, r8       !  15 LS
+       sub     r4, r1          !  75 EX                (len remaining)
+
+       ! number of trailing bytes is non-zero
+       !
+       ! invariants restored (r5 already decremented by 4)
+       ! also r1=num bytes remaining
+
+       mov     #4, r2          !   6 EX
+       mov     r4, r7          !   5 MT (latency=0)
+
+       add     #0x1c, r5       !  50 EX                (back to -4)
+       cmp/hs  r2, r1          !  58 MT
+
+       bf/s    5f              ! 108 BR
+        add     #11, r7        !  50 EX
+
+       mov.l   @(r0, r5), r6   !  21 LS (latency=2)
+       tst     r2, r1          !  86 MT
+
+       mov     r5, r3          !   5 MT (latency=0)
+       bt/s    4f              ! 111 BR
+
+        add    #-4, r3         !  50 EX
+       cmp/hs  r2, r1          !  58 MT
+
+       bt/s    5f              ! 111 BR
+        mov.l  r6,@-r0         !  30 LS
+
+       ! 4 cycles, 2 long words per iteration
+3:     mov.l   @(r0, r5), r6   !  21 LS (latency=2)
+
+4:     mov.l   @(r0, r3), r2   !  21 LS (latency=2)
+       cmp/hi  r7, r0
+
+       mov.l   r6, @-r0        !  30 LS
+       bt/s    3b              ! 109 BR
+
+        mov.l  r2, @-r0        !  30 LS
+
+       ! Copy the final 0-3 bytes
+
+5:     cmp/eq  r0, r4          !  54 MT
+       add     #-10, r7        !  50 EX
+
+       bt      9f              ! 110 BR
+       add     #3,r5           !  50 EX
+
+       ! 3 cycles, 1 byte per iteration
+1:     mov.b   @(r0,r5),r1     !  19 LS
+       cmp/hi  r7,r0           !  57 MT
+
+       bt/s    1b              ! 111 BR
+        mov.b  r1,@-r0         !  28 LS
+
+9:     rts
+        nop
+
+       !
+       !       GHIJ KLMN OPQR -->  ..GH IJKL MNOP QR..
+       !
+
+       .balign 32
+.Lcase2:
+       ! Size is 16 or greater and less then 64, but may have trailing bytes
+
+2:     mov     r5, r6          !   5 MT (latency=0)
+       add     #-2,r5          !  50 EX
+
+       mov     r4,r2           !   5 MT (latency=0)
+       add     #-4,r6          !  50 EX
+
+       add     #7,r2           !  50 EX
+3:     mov.w   @(r0,r5),r1     !  20 LS (latency=2)
+
+       mov.w   @(r0,r6),r3     !  20 LS (latency=2)
+       cmp/hi  r2,r0           !  57 MT
+
+       mov.w   r1,@-r0         !  29 LS
+       bt/s    3b              ! 111 BR
+
+        mov.w  r3,@-r0         !  29 LS
+
+       bra     10f
+        nop
+
+
+       .balign 32
+.Lcase2b:
+       ! Size is at least 64 bytes, so will be going round the big loop at least once.
+       !
+       !   r2 = rounded up r4
+       !   r3 = rounded down r0
+
+       mov     r0, r3          !   5 MT (latency=0)
+       mov     #(~0x1f), r1    !   6 EX
+
+       and     r1, r3          !  78 EX
+       mov     r4, r2          !   5 MT (latency=0)
+
+       cmp/eq  r3, r0          !  54 MT
+       add     #0x1f, r2       !  50 EX
+
+       add     #-2, r5         !  50 EX
+       bt/s    1f              ! 110 BR
+        and    r1, r2          !  78 EX
+
+       ! Copy a short word one at a time until we are cache line aligned
+       !   Normal values: r0, r2, r3, r4
+       !   Unused: r1, r6, r7
+       !   Mod: r5 (=r5-2)
+       !
+       add     #2, r3          !  50 EX
+
+2:     mov.w   @(r0,r5),r1     !  20 LS (latency=2)
+       cmp/eq  r3,r0           !  54 MT
+
+       bf/s    2b              ! 111 BR
+
+        mov.w  r1,@-r0         !  29 LS
+
+       ! Copy the cache line aligned blocks
+       !
+       ! In use: r0, r2, r4, r5 (=r5-2)
+       ! Scratch: r1, r3, r6, r7
+       !
+       ! We could do this with the four scratch registers, but if src
+       ! and dest hit the same cache line, this will thrash, so make
+       ! use of additional registers.
+       !
+       ! We also need r0 as a temporary (for movca), so 'undo' the invariant:
+       !   r5:  src (was r0+r5)
+       !   r1:  dest (was r0)
+       ! this can be reversed at the end, so we don't need to save any extra
+       ! state.
+       !
+1:     mov.l   r8, @-r15       !  30 LS
+       add     r0, r5          !  49 EX
+
+       mov.l   r9, @-r15       !  30 LS
+       mov     r0, r1          !   5 MT (latency=0)
+
+       mov.l   r10, @-r15      !  30 LS
+       add     #-0x1e, r5      !  50 EX
+
+       mov.l   r11, @-r15      !  30 LS
+
+       mov.l   r12, @-r15      !  30 LS
+
+       ! 17 cycles, 32 bytes per iteration
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+2:     mov.w   @r5+, r0        !  14 LS (latency=2)            ..JI
+       add     #-0x20, r1      !  50 EX
+
+       mov.l   @r5+, r3        !  15 LS (latency=2)            NMLK
+
+       mov.l   @r5+, r6        !  15 LS (latency=2)            RQPO
+       shll16  r0              ! 103 EX                        JI..
+
+       mov.l   @r5+, r7        !  15 LS (latency=2)
+       xtrct   r3, r0          !  48 EX                        LKJI
+
+       mov.l   @r5+, r8        !  15 LS (latency=2)
+       xtrct   r6, r3          !  48 EX                        PONM
+
+       mov.l   @r5+, r9        !  15 LS (latency=2)
+       xtrct   r7, r6          !  48 EX
+
+       mov.l   @r5+, r10       !  15 LS (latency=2)
+       xtrct   r8, r7          !  48 EX
+
+       mov.l   @r5+, r11       !  15 LS (latency=2)
+       xtrct   r9, r8          !  48 EX
+
+       mov.w   @r5+, r12       !  15 LS (latency=2)
+       xtrct   r10, r9         !  48 EX
+
+       movca.l r0,@r1          !  40 LS (latency=3-7)
+       xtrct   r11, r10        !  48 EX
+
+       mov.l   r3, @(0x04,r1)  !  33 LS
+       xtrct   r12, r11        !  48 EX
+
+       mov.l   r6, @(0x08,r1)  !  33 LS
+
+       mov.l   r7, @(0x0c,r1)  !  33 LS
+
+       mov.l   r8, @(0x10,r1)  !  33 LS
+       add     #-0x40, r5      !  50 EX
+
+       mov.l   r9, @(0x14,r1)  !  33 LS
+       cmp/eq  r2,r1           !  54 MT
+
+       mov.l   r10, @(0x18,r1) !  33 LS
+       bf/s    2b              ! 109 BR
+
+        mov.l  r11, @(0x1c,r1) !  33 LS
+#else
+2:     mov.w   @(0x1e,r5), r0  !  17 LS (latency=2)
+       add     #-2, r5         !  50 EX
+
+       mov.l   @(0x1c,r5), r3  !  18 LS (latency=2)
+       add     #-4, r1         !  50 EX
+
+       mov.l   @(0x18,r5), r6  !  18 LS (latency=2)
+       shll16  r0              ! 103 EX
+
+       mov.l   @(0x14,r5), r7  !  18 LS (latency=2)
+       xtrct   r3, r0          !  48 EX
+
+       mov.l   @(0x10,r5), r8  !  18 LS (latency=2)
+       xtrct   r6, r3          !  48 EX
+
+       mov.l   @(0x0c,r5), r9  !  18 LS (latency=2)
+       xtrct   r7, r6          !  48 EX
+
+       mov.l   @(0x08,r5), r10 !  18 LS (latency=2)
+       xtrct   r8, r7          !  48 EX
+
+       mov.l   @(0x04,r5), r11 !  18 LS (latency=2)
+       xtrct   r9, r8          !  48 EX
+
+       mov.w   @(0x02,r5), r12 !  18 LS (latency=2)
+       xtrct   r10, r9         !  48 EX
+
+       movca.l r0,@r1          !  40 LS (latency=3-7)
+       add     #-0x1c, r1      !  50 EX
+
+       mov.l   r3, @(0x1c,r1)  !  33 LS
+       xtrct   r11, r10        !  48 EX
+
+       mov.l   r6, @(0x18,r1)  !  33 LS
+       xtrct   r12, r11        !  48 EX
+
+       mov.l   r7, @(0x14,r1)  !  33 LS
+
+       mov.l   r8, @(0x10,r1)  !  33 LS
+       add     #-0x3e, r5      !  50 EX
+
+       mov.l   r9, @(0x0c,r1)  !  33 LS
+       cmp/eq  r2,r1           !  54 MT
+
+       mov.l   r10, @(0x08,r1) !  33 LS
+       bf/s    2b              ! 109 BR
+
+        mov.l  r11, @(0x04,r1) !  33 LS
+#endif
+
+       mov.l   @r15+, r12
+       mov     r1, r0          !   5 MT (latency=0)
+
+       mov.l   @r15+, r11      !  15 LS
+       sub     r1, r5          !  75 EX
+
+       mov.l   @r15+, r10      !  15 LS
+       cmp/eq  r4, r0          !  54 MT
+
+       bf/s    1f              ! 109 BR
+        mov.l   @r15+, r9      !  15 LS
+
+       rts
+1:      mov.l  @r15+, r8       !  15 LS
+
+       add     #0x1e, r5       !  50 EX
+
+       ! Finish off a short word at a time
+       ! r5 must be invariant - 2
+10:    mov     r4,r2           !   5 MT (latency=0)
+       add     #1,r2           !  50 EX
+
+       cmp/hi  r2, r0          !  57 MT
+       bf/s    1f              ! 109 BR
+
+        add    #2, r2          !  50 EX
+
+3:     mov.w   @(r0,r5),r1     !  20 LS
+       cmp/hi  r2,r0           !  57 MT
+
+       bt/s    3b              ! 109 BR
+
+        mov.w  r1,@-r0         !  29 LS
+1:
+
+       !
+       ! Finally, copy the last byte if necessary
+       cmp/eq  r4,r0           !  54 MT
+       bt/s    9b
+        add    #1,r5
+       mov.b   @(r0,r5),r1
+       rts
+        mov.b  r1,@-r0
+
diff --git a/arch/sh/mm/cache-sh7705.c b/arch/sh/mm/cache-sh7705.c
new file mode 100644 (file)
index 0000000..3a0508b
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * arch/sh/mm/cache-sh7705.c
+ *
+ * Copyright (C) 1999, 2000  Niibe Yutaka
+ * Copyright (C) 2004  Alex Song
+ *
+ * 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 <linux/init.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/threads.h>
+#include <asm/addrspace.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+#include <asm/mmu_context.h>
+#include <asm/cacheflush.h>
+
+/* The 32KB cache on the SH7705 suffers from the same synonym problem
+ * as SH4 CPUs */
+
+#define __pte_offset(address) \
+               ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
+#define pte_offset(dir, address) ((pte_t *) pmd_page_kernel(*(dir)) + \
+               __pte_offset(address))
+
+static inline void cache_wback_all(void)
+{
+       unsigned long ways, waysize, addrstart;
+
+       ways = cpu_data->dcache.ways;
+       waysize = cpu_data->dcache.sets;
+       waysize <<= cpu_data->dcache.entry_shift;
+
+       addrstart = CACHE_OC_ADDRESS_ARRAY;
+
+       do {
+               unsigned long addr;
+
+               for (addr = addrstart;
+                    addr < addrstart + waysize;
+                    addr += cpu_data->dcache.linesz) {
+                       unsigned long data;
+                       int v = SH_CACHE_UPDATED | SH_CACHE_VALID;
+
+                       data = ctrl_inl(addr);
+
+                       if ((data & v) == v)
+                               ctrl_outl(data & ~v, addr);
+
+               }
+
+               addrstart += cpu_data->dcache.way_incr;
+       } while (--ways);
+}
+
+/*
+ * Write back the range of D-cache, and purge the I-cache.
+ *
+ * Called from kernel/module.c:sys_init_module and routine for a.out format.
+ */
+void flush_icache_range(unsigned long start, unsigned long end)
+{
+       __flush_wback_region((void *)start, end - start);
+}
+
+
+/*
+ * Writeback&Invalidate the D-cache of the page
+ */
+static void __flush_dcache_page(unsigned long phys)
+{
+       unsigned long ways, waysize, addrstart;
+       unsigned long flags;
+
+       phys |= SH_CACHE_VALID;
+
+       /*
+        * Here, phys is the physical address of the page. We check all the
+        * tags in the cache for those with the same page number as this page
+        * (by masking off the lowest 2 bits of the 19-bit tag; these bits are
+        * derived from the offset within in the 4k page). Matching valid
+        * entries are invalidated.
+        *
+        * Since 2 bits of the cache index are derived from the virtual page
+        * number, knowing this would reduce the number of cache entries to be
+        * searched by a factor of 4. However this function exists to deal with
+        * potential cache aliasing, therefore the optimisation is probably not
+        * possible.
+        */
+       local_irq_save(flags);
+       jump_to_P2();
+
+       ways = cpu_data->dcache.ways;
+       waysize = cpu_data->dcache.sets;
+       waysize <<= cpu_data->dcache.entry_shift;
+
+       addrstart = CACHE_OC_ADDRESS_ARRAY;
+
+       do {
+               unsigned long addr;
+
+               for (addr = addrstart;
+                    addr < addrstart + waysize;
+                    addr += cpu_data->dcache.linesz) {
+                       unsigned long data;
+
+                       data = ctrl_inl(addr) & (0x1ffffC00 | SH_CACHE_VALID);
+                       if (data == phys) {
+                               data &= ~(SH_CACHE_VALID | SH_CACHE_UPDATED);
+                               ctrl_outl(data, addr);
+                       }
+               }
+
+               addrstart += cpu_data->dcache.way_incr;
+       } while (--ways);
+
+       back_to_P1();
+       local_irq_restore(flags);
+}
+
+
+/*
+ * Write back & invalidate the D-cache of the page.
+ * (To avoid "alias" issues)
+ */
+void flush_dcache_page(struct page *page)
+{
+       if (test_bit(PG_mapped, &page->flags))
+               __flush_dcache_page(PHYSADDR(page_address(page)));
+}
+
+void flush_cache_all(void)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       jump_to_P2();
+
+       cache_wback_all();
+       back_to_P1();
+       local_irq_restore(flags);
+}
+
+void flush_cache_mm(struct mm_struct *mm)
+{
+       /* Is there any good way? */
+       /* XXX: possibly call flush_cache_range for each vm area */
+       flush_cache_all();
+}
+
+/*
+ * Write back and invalidate D-caches.
+ *
+ * START, END: Virtual Address (U0 address)
+ *
+ * NOTE: We need to flush the _physical_ page entry.
+ * Flushing the cache lines for U0 only isn't enough.
+ * We need to flush for P1 too, which may contain aliases.
+ */
+void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
+                      unsigned long end)
+{
+
+       /*
+        * We could call flush_cache_page for the pages of these range,
+        * but it's not efficient (scan the caches all the time...).
+        *
+        * We can't use A-bit magic, as there's the case we don't have
+        * valid entry on TLB.
+        */
+       flush_cache_all();
+}
+
+/*
+ * Write back and invalidate I/D-caches for the page.
+ *
+ * ADDRESS: Virtual Address (U0 address)
+ */
+void flush_cache_page(struct vm_area_struct *vma, unsigned long address)
+{
+       pgd_t *dir;
+       pmd_t *pmd;
+       pte_t *pte;
+       pte_t entry;
+       unsigned long phys;
+
+       dir = pgd_offset(vma->vm_mm, address);
+       pmd = pmd_offset(dir, address);
+       if (pmd_none(*pmd) || pmd_bad(*pmd))
+               return;
+       pte = pte_offset(pmd, address);
+       entry = *pte;
+       if (pte_none(entry) || !pte_present(entry))
+               return;
+
+       phys = pte_val(entry)&PTE_PHYS_MASK;
+       __flush_dcache_page(phys);
+}
+
+/*
+ * This is called when a page-cache page is about to be mapped into a
+ * user process' address space.  It offers an opportunity for a
+ * port to ensure d-cache/i-cache coherency if necessary.
+ *
+ * Not entirely sure why this is necessary on SH3 with 32K cache but
+ * without it we get occasional "Memory fault" when loading a program.
+ */
+void flush_icache_page(struct vm_area_struct *vma, struct page *page)
+{
+       __flush_purge_region(page_address(page), PAGE_SIZE);
+}
+
diff --git a/arch/sh/mm/pg-sh7705.c b/arch/sh/mm/pg-sh7705.c
new file mode 100644 (file)
index 0000000..4a6eea8
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * arch/sh/mm/pg-sh7705.c
+ *
+ * Copyright (C) 1999, 2000  Niibe Yutaka
+ * Copyright (C) 2004  Alex Song
+ *
+ * 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 <linux/init.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/threads.h>
+#include <asm/addrspace.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+#include <asm/mmu_context.h>
+#include <asm/cacheflush.h>
+
+static inline void __flush_purge_virtual_region(void *p1, void *virt, int size)
+{
+       unsigned long v;
+       unsigned long begin, end;
+       unsigned long p1_begin;
+
+
+       begin = L1_CACHE_ALIGN((unsigned long)virt);
+       end = L1_CACHE_ALIGN((unsigned long)virt + size);
+
+       p1_begin = (unsigned long)p1 & ~(L1_CACHE_BYTES - 1);
+
+       /* do this the slow way as we may not have TLB entries
+        * for virt yet. */
+       for (v = begin; v < end; v += L1_CACHE_BYTES) {
+               unsigned long p;
+               unsigned long ways, addr;
+
+               p = __pa(p1_begin);
+
+               ways = cpu_data->dcache.ways;
+               addr = CACHE_OC_ADDRESS_ARRAY;
+
+               do {
+                       unsigned long data;
+
+                       addr |= (v & cpu_data->dcache.entry_mask);
+
+                       data = ctrl_inl(addr);
+                       if ((data & CACHE_PHYSADDR_MASK) ==
+                              (p & CACHE_PHYSADDR_MASK)) {
+                               data &= ~(SH_CACHE_UPDATED|SH_CACHE_VALID);
+                               ctrl_outl(data, addr);
+                       }
+
+                       addr += cpu_data->dcache.way_incr;
+               } while (--ways);
+
+               p1_begin += L1_CACHE_BYTES;
+       }
+}
+
+/*
+ * clear_user_page
+ * @to: P1 address
+ * @address: U0 address to be mapped
+ */
+void clear_user_page(void *to, unsigned long address, struct page *pg)
+{
+       struct page *page = virt_to_page(to);
+
+       __set_bit(PG_mapped, &page->flags);
+       if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) {
+               clear_page(to);
+               __flush_wback_region(to, PAGE_SIZE);
+       } else {
+               __flush_purge_virtual_region(to,
+                                            (void *)(address & 0xfffff000),
+                                            PAGE_SIZE);
+               clear_page(to);
+               __flush_wback_region(to, PAGE_SIZE);
+       }
+}
+
+/*
+ * copy_user_page
+ * @to: P1 address
+ * @from: P1 address
+ * @address: U0 address to be mapped
+ */
+void copy_user_page(void *to, void *from, unsigned long address, struct page *pg)
+{
+       struct page *page = virt_to_page(to);
+
+
+       __set_bit(PG_mapped, &page->flags);
+       if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0) {
+               copy_page(to, from);
+               __flush_wback_region(to, PAGE_SIZE);
+       } else {
+               __flush_purge_virtual_region(to,
+                                            (void *)(address & 0xfffff000),
+                                            PAGE_SIZE);
+               copy_page(to, from);
+               __flush_wback_region(to, PAGE_SIZE);
+       }
+}
+
+/*
+ * For SH7705, we have our own implementation for ptep_get_and_clear
+ * Copied from pg-sh4.c
+ */
+inline pte_t ptep_get_and_clear(pte_t *ptep)
+{
+       pte_t pte = *ptep;
+
+       pte_clear(ptep);
+       if (!pte_not_present(pte)) {
+               unsigned long pfn = pte_pfn(pte);
+               if (pfn_valid(pfn)) {
+                       struct page *page = pfn_to_page(pfn);
+                       struct address_space *mapping = page_mapping(page);
+                       if (!mapping || !mapping_writably_mapped(mapping))
+                               __clear_bit(PG_mapped, &page->flags);
+               }
+       }
+
+       return pte;
+}
+
diff --git a/arch/sh/oprofile/op_model_sh7750.c b/arch/sh/oprofile/op_model_sh7750.c
new file mode 100644 (file)
index 0000000..de09ba7
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * arch/sh/oprofile/op_model_sh7750.c
+ *
+ * OProfile support for SH7750/SH7750S Performance Counters
+ *
+ * Copyright (C) 2003, 2004  Paul Mundt
+ *
+ * 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 <linux/kernel.h>
+#include <linux/oprofile.h>
+#include <linux/profile.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/notifier.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#define PM_CR_BASE     0xff000084      /* 16-bit */
+#define PM_CTR_BASE    0xff100004      /* 32-bit */
+
+#define PMCR1          (PM_CR_BASE  + 0x00)
+#define PMCR2          (PM_CR_BASE  + 0x04)
+#define PMCTR1H                (PM_CTR_BASE + 0x00)
+#define PMCTR1L                (PM_CTR_BASE + 0x04)
+#define PMCTR2H                (PM_CTR_BASE + 0x08)
+#define PMCTR2L                (PM_CTR_BASE + 0x0c)
+
+#define PMCR_PMM_MASK  0x0000003f
+
+#define PMCR_CLKF      0x00000100
+#define PMCR_PMCLR     0x00002000
+#define PMCR_PMST      0x00004000
+#define PMCR_PMEN      0x00008000
+
+#define PMCR_ENABLE    (PMCR_PMST | PMCR_PMEN)
+
+/*
+ * SH7750/SH7750S have 2 perf counters
+ */
+#define NR_CNTRS       2
+
+extern const char *get_cpu_subtype(void);
+
+struct op_counter_config {
+       unsigned long enabled;
+       unsigned long event;
+       unsigned long count;
+
+       /* Dummy values for userspace tool compliance */
+       unsigned long kernel;
+       unsigned long user;
+       unsigned long unit_mask;
+};
+
+static struct op_counter_config ctr[NR_CNTRS];
+
+/*
+ * There are a number of events supported by each counter (33 in total).
+ * Since we have 2 counters, each counter will take the event code as it
+ * corresponds to the PMCR PMM setting. Each counter can be configured
+ * independently.
+ *
+ *     Event Code      Description
+ *     ----------      -----------
+ *
+ *     0x01            Operand read access
+ *     0x02            Operand write access
+ *     0x03            UTLB miss
+ *     0x04            Operand cache read miss
+ *     0x05            Operand cache write miss
+ *     0x06            Instruction fetch (w/ cache)
+ *     0x07            Instruction TLB miss
+ *     0x08            Instruction cache miss
+ *     0x09            All operand accesses
+ *     0x0a            All instruction accesses
+ *     0x0b            OC RAM operand access
+ *     0x0d            On-chip I/O space access
+ *     0x0e            Operand access (r/w)
+ *     0x0f            Operand cache miss (r/w)
+ *     0x10            Branch instruction
+ *     0x11            Branch taken
+ *     0x12            BSR/BSRF/JSR
+ *     0x13            Instruction execution
+ *     0x14            Instruction execution in parallel
+ *     0x15            FPU Instruction execution
+ *     0x16            Interrupt
+ *     0x17            NMI
+ *     0x18            trapa instruction execution
+ *     0x19            UBCA match
+ *     0x1a            UBCB match
+ *     0x21            Instruction cache fill
+ *     0x22            Operand cache fill
+ *     0x23            Elapsed time
+ *     0x24            Pipeline freeze by I-cache miss
+ *     0x25            Pipeline freeze by D-cache miss
+ *     0x27            Pipeline freeze by branch instruction
+ *     0x28            Pipeline freeze by CPU register
+ *     0x29            Pipeline freeze by FPU
+ *
+ * Unfortunately we don't have a native exception or interrupt for counter
+ * overflow (although since these counters can run for 16.3 days without
+ * overflowing, it's not really necessary).
+ *
+ * OProfile on the other hand likes to have samples taken periodically, so
+ * for now we just piggyback the timer interrupt to get the expected
+ * behavior.
+ */
+
+static int sh7750_timer_notify(struct notifier_block *self,
+                              unsigned long val, void *data)
+{
+       struct pt_regs *regs = data;
+       unsigned long pc;
+
+       pc = instruction_pointer(regs);
+       oprofile_add_sample(pc, !user_mode(regs), 0, smp_processor_id());
+
+       return 0;
+}
+
+static struct notifier_block sh7750_timer_notifier = {
+       .notifier_call          = sh7750_timer_notify,
+};
+
+static u64 sh7750_read_counter(int counter)
+{
+       u32 hi, lo;
+
+       hi = (counter == 0) ? ctrl_inl(PMCTR1H) : ctrl_inl(PMCTR2H);
+       lo = (counter == 0) ? ctrl_inl(PMCTR1L) : ctrl_inl(PMCTR2L);
+
+       return (u64)((u64)(hi & 0xffff) << 32) | lo;
+}
+
+/*
+ * Files will be in a path like:
+ *
+ *  /<oprofilefs mount point>/<counter number>/<file>
+ *
+ * So when dealing with <file>, we look to the parent dentry for the counter
+ * number.
+ */
+static inline int to_counter(struct file *file)
+{
+       const unsigned char *name = file->f_dentry->d_parent->d_name.name;
+
+       return (int)simple_strtol(name, NULL, 10);
+}
+
+/*
+ * XXX: We have 48-bit counters, so we're probably going to want something
+ * more along the lines of oprofilefs_ullong_to_user().. Truncating to
+ * unsigned long works fine for now though, as long as we don't attempt to
+ * profile for too horribly long.
+ */
+static ssize_t sh7750_read_count(struct file *file, char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+       int counter = to_counter(file);
+       u64 val = sh7750_read_counter(counter);
+
+       return oprofilefs_ulong_to_user((unsigned long)val, buf, count, ppos);
+}
+
+static ssize_t sh7750_write_count(struct file *file, const char __user *buf,
+                                 size_t count, loff_t *ppos)
+{
+       int counter = to_counter(file);
+       unsigned long val;
+
+       if (oprofilefs_ulong_from_user(&val, buf, count))
+               return -EFAULT;
+
+       /*
+        * Any write will clear the counter, although only 0 should be
+        * written for this purpose, as we do not support setting the
+        * counter to an arbitrary value.
+        */
+       WARN_ON(val != 0);
+
+       if (counter == 0) {
+               ctrl_outw(ctrl_inw(PMCR1) | PMCR_PMCLR, PMCR1);
+       } else {
+               ctrl_outw(ctrl_inw(PMCR2) | PMCR_PMCLR, PMCR2);
+       }
+
+       return count;
+}
+
+static struct file_operations count_fops = {
+       .read           = sh7750_read_count,
+       .write          = sh7750_write_count,
+};
+
+static int sh7750_perf_counter_create_files(struct super_block *sb, struct dentry *root)
+{
+       int i;
+
+       for (i = 0; i < NR_CNTRS; i++) {
+               struct dentry *dir;
+               char buf[3];
+
+               snprintf(buf, sizeof(buf), "%d", i);
+               dir = oprofilefs_mkdir(sb, root, buf);
+
+               oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
+               oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
+               oprofilefs_create_file(sb, dir, "count", &count_fops);
+
+               /* Dummy entries */
+               oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
+               oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
+               oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
+       }
+
+       return 0;
+}
+
+static int sh7750_perf_counter_start(void)
+{
+       u16 pmcr;
+
+       /* Enable counter 1 */
+       if (ctr[0].enabled) {
+               pmcr = ctrl_inw(PMCR1);
+               WARN_ON(pmcr & PMCR_PMEN);
+
+               pmcr &= ~PMCR_PMM_MASK;
+               pmcr |= ctr[0].event;
+               ctrl_outw(pmcr | PMCR_ENABLE, PMCR1);
+       }
+
+       /* Enable counter 2 */
+       if (ctr[1].enabled) {
+               pmcr = ctrl_inw(PMCR2);
+               WARN_ON(pmcr & PMCR_PMEN);
+
+               pmcr &= ~PMCR_PMM_MASK;
+               pmcr |= ctr[1].event;
+               ctrl_outw(pmcr | PMCR_ENABLE, PMCR2);
+       }
+
+       return register_profile_notifier(&sh7750_timer_notifier);
+}
+
+static void sh7750_perf_counter_stop(void)
+{
+       ctrl_outw(ctrl_inw(PMCR1) & ~PMCR_PMEN, PMCR1);
+       ctrl_outw(ctrl_inw(PMCR2) & ~PMCR_PMEN, PMCR2);
+
+       unregister_profile_notifier(&sh7750_timer_notifier);
+}
+
+static struct oprofile_operations sh7750_perf_counter_ops = {
+       .create_files   = sh7750_perf_counter_create_files,
+       .start          = sh7750_perf_counter_start,
+       .stop           = sh7750_perf_counter_stop,
+};
+
+int __init oprofile_arch_init(struct oprofile_operations **ops)
+{
+       if (!(cpu_data->flags & CPU_HAS_PERF_COUNTER))
+               return -ENODEV;
+
+       sh7750_perf_counter_ops.cpu_type = (char *)get_cpu_subtype();
+       *ops = &sh7750_perf_counter_ops;
+
+       printk(KERN_INFO "oprofile: using SH-4 (%s) performance monitoring.\n",
+              sh7750_perf_counter_ops.cpu_type);
+
+       /* Clear the counters */
+       ctrl_outw(ctrl_inw(PMCR1) | PMCR_PMCLR, PMCR1);
+       ctrl_outw(ctrl_inw(PMCR2) | PMCR_PMCLR, PMCR2);
+
+       return 0;
+}
+
+void oprofile_arch_exit(void)
+{
+}
+
diff --git a/arch/sh/tools/gen-mach-types b/arch/sh/tools/gen-mach-types
new file mode 100644 (file)
index 0000000..bb2b822
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/awk
+#
+# Awk script to generate include/asm-sh/machtypes.h
+# Heavily based on arch/arm/tools/gen-mach-types
+#
+BEGIN  { nr = 0 }
+/^#/   { next }
+/^[    ]*$/ { next }
+
+NF == 2 {
+         mach[nr] = $1;
+         config[nr] = "CONFIG_"$2;
+         nr++;
+       }
+
+END    {
+         printf("/*\n");
+         printf(" * Automagically generated, don't touch.\n");
+         printf(" */\n");
+         printf("#ifndef __ASM_SH_MACHTYPES_H\n");
+         printf("#define __ASM_SH_MACHTYPES_H\n");
+         printf("\n");
+         printf("#include <linux/config.h>\n");
+         printf("\n");
+         printf("/*\n");
+         printf(" * We'll use the following MACH_xxx defs for placeholders for the time\n");
+         printf(" * being .. these will all go away once sh_machtype is assigned per-board.\n");
+         printf(" *\n");
+         printf(" * For now we leave things the way they are for backwards compatibility.\n");
+         printf(" */\n");
+         printf("\n");
+         printf("/* Mach types */\n");
+
+         for (i = 0; i < nr; i++) {
+             printf("#ifdef %s\n", config[i]);
+             printf("  #define MACH_%s\t\t1\n", mach[i]);
+             printf("#else\n");
+             printf("  #define MACH_%s\t\t0\n", mach[i]);
+             printf("#endif\n");
+           }
+
+         printf("\n");
+         printf("/* Machtype checks */\n");
+         for (i = 0; i < nr; i++)
+             printf("#define mach_is_%s()\t\t\t(MACH_%s)\n",
+                tolower(mach[i]), mach[i]);
+         printf("\n");
+         printf("#endif /* __ASM_SH_MACHTYPES_H */\n");
+       }
diff --git a/arch/sparc64/prom/cif.S b/arch/sparc64/prom/cif.S
new file mode 100644 (file)
index 0000000..29d0ae7
--- /dev/null
@@ -0,0 +1,225 @@
+/* cif.S: PROM entry/exit assembler trampolines.
+ *
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 2005 David S. Miller <davem@davemloft.net>
+ */
+
+#include <asm/pstate.h>
+
+       .text
+       .globl  prom_cif_interface
+prom_cif_interface:
+       sethi   %hi(p1275buf), %o0
+       or      %o0, %lo(p1275buf), %o0
+       ldx     [%o0 + 0x010], %o1      ! prom_cif_stack
+       save    %o1, -0x190, %sp
+       ldx     [%i0 + 0x008], %l2      ! prom_cif_handler
+       rdpr    %pstate, %l4
+       wrpr    %g0, 0x15, %pstate      ! save alternate globals
+       stx     %g1, [%sp + 2047 + 0x0b0]
+       stx     %g2, [%sp + 2047 + 0x0b8]
+       stx     %g3, [%sp + 2047 + 0x0c0]
+       stx     %g4, [%sp + 2047 + 0x0c8]
+       stx     %g5, [%sp + 2047 + 0x0d0]
+       stx     %g6, [%sp + 2047 + 0x0d8]
+       stx     %g7, [%sp + 2047 + 0x0e0]
+       wrpr    %g0, 0x814, %pstate     ! save interrupt globals
+       stx     %g1, [%sp + 2047 + 0x0e8]
+       stx     %g2, [%sp + 2047 + 0x0f0]
+       stx     %g3, [%sp + 2047 + 0x0f8]
+       stx     %g4, [%sp + 2047 + 0x100]
+       stx     %g5, [%sp + 2047 + 0x108]
+       stx     %g6, [%sp + 2047 + 0x110]
+       stx     %g7, [%sp + 2047 + 0x118]
+       wrpr    %g0, 0x14, %pstate      ! save normal globals
+       stx     %g1, [%sp + 2047 + 0x120]
+       stx     %g2, [%sp + 2047 + 0x128]
+       stx     %g3, [%sp + 2047 + 0x130]
+       stx     %g4, [%sp + 2047 + 0x138]
+       stx     %g5, [%sp + 2047 + 0x140]
+       stx     %g6, [%sp + 2047 + 0x148]
+       stx     %g7, [%sp + 2047 + 0x150]
+       wrpr    %g0, 0x414, %pstate     ! save mmu globals
+       stx     %g1, [%sp + 2047 + 0x158]
+       stx     %g2, [%sp + 2047 + 0x160]
+       stx     %g3, [%sp + 2047 + 0x168]
+       stx     %g4, [%sp + 2047 + 0x170]
+       stx     %g5, [%sp + 2047 + 0x178]
+       stx     %g6, [%sp + 2047 + 0x180]
+       stx     %g7, [%sp + 2047 + 0x188]
+       mov     %g1, %l0                ! also save to locals, so we can handle
+       mov     %g2, %l1                ! tlb faults later on, when accessing
+       mov     %g3, %l3                ! the stack.
+       mov     %g7, %l5
+       wrpr    %l4, PSTATE_IE, %pstate ! turn off interrupts
+       call    %l2
+        add    %i0, 0x018, %o0         ! prom_args
+       wrpr    %g0, 0x414, %pstate     ! restore mmu globals
+       mov     %l0, %g1
+       mov     %l1, %g2
+       mov     %l3, %g3
+       mov     %l5, %g7
+       wrpr    %g0, 0x14, %pstate      ! restore normal globals
+       ldx     [%sp + 2047 + 0x120], %g1
+       ldx     [%sp + 2047 + 0x128], %g2
+       ldx     [%sp + 2047 + 0x130], %g3
+       ldx     [%sp + 2047 + 0x138], %g4
+       ldx     [%sp + 2047 + 0x140], %g5
+       ldx     [%sp + 2047 + 0x148], %g6
+       ldx     [%sp + 2047 + 0x150], %g7
+       wrpr    %g0, 0x814, %pstate     ! restore interrupt globals
+       ldx     [%sp + 2047 + 0x0e8], %g1
+       ldx     [%sp + 2047 + 0x0f0], %g2
+       ldx     [%sp + 2047 + 0x0f8], %g3
+       ldx     [%sp + 2047 + 0x100], %g4
+       ldx     [%sp + 2047 + 0x108], %g5
+       ldx     [%sp + 2047 + 0x110], %g6
+       ldx     [%sp + 2047 + 0x118], %g7
+       wrpr    %g0, 0x15, %pstate      ! restore alternate globals
+       ldx     [%sp + 2047 + 0x0b0], %g1
+       ldx     [%sp + 2047 + 0x0b8], %g2
+       ldx     [%sp + 2047 + 0x0c0], %g3
+       ldx     [%sp + 2047 + 0x0c8], %g4
+       ldx     [%sp + 2047 + 0x0d0], %g5
+       ldx     [%sp + 2047 + 0x0d8], %g6
+       ldx     [%sp + 2047 + 0x0e0], %g7
+       wrpr    %l4, 0, %pstate ! restore original pstate
+       ret
+        restore
+
+       .globl  prom_cif_callback
+prom_cif_callback:
+       sethi   %hi(p1275buf), %o1
+       or      %o1, %lo(p1275buf), %o1
+       save    %sp, -0x270, %sp
+       rdpr    %pstate, %l4
+       wrpr    %g0, 0x15, %pstate      ! save PROM alternate globals
+       stx     %g1, [%sp + 2047 + 0x0b0]
+       stx     %g2, [%sp + 2047 + 0x0b8]
+       stx     %g3, [%sp + 2047 + 0x0c0]
+       stx     %g4, [%sp + 2047 + 0x0c8]
+       stx     %g5, [%sp + 2047 + 0x0d0]
+       stx     %g6, [%sp + 2047 + 0x0d8]
+       stx     %g7, [%sp + 2047 + 0x0e0]
+                                       ! restore Linux alternate globals
+       ldx     [%sp + 2047 + 0x190], %g1
+       ldx     [%sp + 2047 + 0x198], %g2
+       ldx     [%sp + 2047 + 0x1a0], %g3
+       ldx     [%sp + 2047 + 0x1a8], %g4
+       ldx     [%sp + 2047 + 0x1b0], %g5
+       ldx     [%sp + 2047 + 0x1b8], %g6
+       ldx     [%sp + 2047 + 0x1c0], %g7
+       wrpr    %g0, 0x814, %pstate     ! save PROM interrupt globals
+       stx     %g1, [%sp + 2047 + 0x0e8]
+       stx     %g2, [%sp + 2047 + 0x0f0]
+       stx     %g3, [%sp + 2047 + 0x0f8]
+       stx     %g4, [%sp + 2047 + 0x100]
+       stx     %g5, [%sp + 2047 + 0x108]
+       stx     %g6, [%sp + 2047 + 0x110]
+       stx     %g7, [%sp + 2047 + 0x118]
+                                       ! restore Linux interrupt globals
+       ldx     [%sp + 2047 + 0x1c8], %g1
+       ldx     [%sp + 2047 + 0x1d0], %g2
+       ldx     [%sp + 2047 + 0x1d8], %g3
+       ldx     [%sp + 2047 + 0x1e0], %g4
+       ldx     [%sp + 2047 + 0x1e8], %g5
+       ldx     [%sp + 2047 + 0x1f0], %g6
+       ldx     [%sp + 2047 + 0x1f8], %g7
+       wrpr    %g0, 0x14, %pstate      ! save PROM normal globals
+       stx     %g1, [%sp + 2047 + 0x120]
+       stx     %g2, [%sp + 2047 + 0x128]
+       stx     %g3, [%sp + 2047 + 0x130]
+       stx     %g4, [%sp + 2047 + 0x138]
+       stx     %g5, [%sp + 2047 + 0x140]
+       stx     %g6, [%sp + 2047 + 0x148]
+       stx     %g7, [%sp + 2047 + 0x150]
+                                       ! restore Linux normal globals
+       ldx     [%sp + 2047 + 0x200], %g1
+       ldx     [%sp + 2047 + 0x208], %g2
+       ldx     [%sp + 2047 + 0x210], %g3
+       ldx     [%sp + 2047 + 0x218], %g4
+       ldx     [%sp + 2047 + 0x220], %g5
+       ldx     [%sp + 2047 + 0x228], %g6
+       ldx     [%sp + 2047 + 0x230], %g7
+       wrpr    %g0, 0x414, %pstate     ! save PROM mmu globals
+       stx     %g1, [%sp + 2047 + 0x158]
+       stx     %g2, [%sp + 2047 + 0x160]
+       stx     %g3, [%sp + 2047 + 0x168]
+       stx     %g4, [%sp + 2047 + 0x170]
+       stx     %g5, [%sp + 2047 + 0x178]
+       stx     %g6, [%sp + 2047 + 0x180]
+       stx     %g7, [%sp + 2047 + 0x188]
+                                       ! restore Linux mmu globals
+       ldx     [%sp + 2047 + 0x238], %o0
+       ldx     [%sp + 2047 + 0x240], %o1
+       ldx     [%sp + 2047 + 0x248], %l2
+       ldx     [%sp + 2047 + 0x250], %l3
+       ldx     [%sp + 2047 + 0x258], %l5
+       ldx     [%sp + 2047 + 0x260], %l6
+       ldx     [%sp + 2047 + 0x268], %l7
+                                       ! switch to Linux tba
+       sethi   %hi(sparc64_ttable_tl0), %l1
+       rdpr    %tba, %l0               ! save PROM tba
+       mov     %o0, %g1
+       mov     %o1, %g2
+       mov     %l2, %g3
+       mov     %l3, %g4
+       mov     %l5, %g5
+       mov     %l6, %g6
+       mov     %l7, %g7
+       wrpr    %l1, %tba               ! install Linux tba
+       wrpr    %l4, 0, %pstate         ! restore PSTATE
+       call    prom_world
+        mov    %g0, %o0
+       ldx     [%i1 + 0x000], %l2
+       call    %l2
+        mov    %i0, %o0
+       mov     %o0, %l1
+       call    prom_world
+        or     %g0, 1, %o0
+       wrpr    %g0, 0x14, %pstate      ! interrupts off
+                                       ! restore PROM mmu globals
+       ldx     [%sp + 2047 + 0x158], %o0
+       ldx     [%sp + 2047 + 0x160], %o1
+       ldx     [%sp + 2047 + 0x168], %l2
+       ldx     [%sp + 2047 + 0x170], %l3
+       ldx     [%sp + 2047 + 0x178], %l5
+       ldx     [%sp + 2047 + 0x180], %l6
+       ldx     [%sp + 2047 + 0x188], %l7
+       wrpr    %g0, 0x414, %pstate     ! restore PROM mmu globals
+       mov     %o0, %g1
+       mov     %o1, %g2
+       mov     %l2, %g3
+       mov     %l3, %g4
+       mov     %l5, %g5
+       mov     %l6, %g6
+       mov     %l7, %g7
+       wrpr    %l0, %tba               ! restore PROM tba
+       wrpr    %g0, 0x14, %pstate      ! restore PROM normal globals
+       ldx     [%sp + 2047 + 0x120], %g1
+       ldx     [%sp + 2047 + 0x128], %g2
+       ldx     [%sp + 2047 + 0x130], %g3
+       ldx     [%sp + 2047 + 0x138], %g4
+       ldx     [%sp + 2047 + 0x140], %g5
+       ldx     [%sp + 2047 + 0x148], %g6
+       ldx     [%sp + 2047 + 0x150], %g7
+       wrpr    %g0, 0x814, %pstate     ! restore PROM interrupt globals
+       ldx     [%sp + 2047 + 0x0e8], %g1
+       ldx     [%sp + 2047 + 0x0f0], %g2
+       ldx     [%sp + 2047 + 0x0f8], %g3
+       ldx     [%sp + 2047 + 0x100], %g4
+       ldx     [%sp + 2047 + 0x108], %g5
+       ldx     [%sp + 2047 + 0x110], %g6
+       ldx     [%sp + 2047 + 0x118], %g7
+       wrpr    %g0, 0x15, %pstate      ! restore PROM alternate globals
+       ldx     [%sp + 2047 + 0x0b0], %g1
+       ldx     [%sp + 2047 + 0x0b8], %g2
+       ldx     [%sp + 2047 + 0x0c0], %g3
+       ldx     [%sp + 2047 + 0x0c8], %g4
+       ldx     [%sp + 2047 + 0x0d0], %g5
+       ldx     [%sp + 2047 + 0x0d8], %g6
+       ldx     [%sp + 2047 + 0x0e0], %g7
+       wrpr    %l4, 0, %pstate
+       ret
+        restore %l1, 0, %o0
+
diff --git a/arch/um/Kconfig_i386 b/arch/um/Kconfig_i386
new file mode 100644 (file)
index 0000000..203c242
--- /dev/null
@@ -0,0 +1,24 @@
+config 64_BIT
+       bool
+       default n
+
+config TOP_ADDR
+       hex
+       default 0xc0000000 if !HOST_2G_2G
+       default 0x80000000 if HOST_2G_2G
+
+config 3_LEVEL_PGTABLES
+       bool "Three-level pagetables"
+       default n
+       help
+       Three-level pagetables will let UML have more than 4G of physical
+       memory.  All the memory that can't be mapped directly will be treated
+       as high memory.
+
+config ARCH_HAS_SC_SIGNALS
+       bool
+       default y
+
+config ARCH_REUSE_HOST_VSYSCALL_AREA
+       bool
+       default y
diff --git a/arch/um/Kconfig_x86_64 b/arch/um/Kconfig_x86_64
new file mode 100644 (file)
index 0000000..768dc66
--- /dev/null
@@ -0,0 +1,15 @@
+config 64_BIT
+       bool
+       default y
+
+config 3_LEVEL_PGTABLES
+       bool
+       default y
+
+config ARCH_HAS_SC_SIGNALS
+       bool
+       default n
+
+config ARCH_REUSE_HOST_VSYSCALL_AREA
+       bool
+       default n
diff --git a/arch/um/Makefile-x86_64 b/arch/um/Makefile-x86_64
new file mode 100644 (file)
index 0000000..705df73
--- /dev/null
@@ -0,0 +1 @@
+ARCH_USER_CFLAGS := -D__x86_64__
diff --git a/arch/um/drivers/stderr_console.c b/arch/um/drivers/stderr_console.c
new file mode 100644 (file)
index 0000000..98565b5
--- /dev/null
@@ -0,0 +1,45 @@
+#include <linux/init.h>
+#include <linux/console.h>
+
+#include "chan_user.h"
+
+/* ----------------------------------------------------------------------------- */
+/* trivial console driver -- simply dump everything to stderr                    */
+
+/*
+ * Don't register by default -- as this registeres very early in the
+ * boot process it becomes the default console.  And as this isn't a
+ * real tty driver init isn't able to open /dev/console then.
+ *
+ * In most cases this isn't what you want ...
+ */
+static int use_stderr_console = 0;
+
+static void stderr_console_write(struct console *console, const char *string,
+                                unsigned len)
+{
+       generic_write(2 /* stderr */, string, len, NULL);
+}
+
+static struct console stderr_console = {
+       .name           "stderr",
+       .write          stderr_console_write,
+       .flags          CON_PRINTBUFFER,
+};
+
+static int __init stderr_console_init(void)
+{
+       if (use_stderr_console)
+               register_console(&stderr_console);
+       return 0;
+}
+console_initcall(stderr_console_init);
+
+static int stderr_setup(char *str)
+{
+       if (!str)
+               return 0;
+       use_stderr_console = simple_strtoul(str,&str,0);
+       return 1;
+}
+__setup("stderr=", stderr_setup);
diff --git a/arch/um/include/elf_user.h b/arch/um/include/elf_user.h
new file mode 100644 (file)
index 0000000..53516b6
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2004 Fujitsu Siemens Computers GmbH
+ * Author: Bodo Stroesser <bstroesser@fujitsu-siemens.com>
+ * Licensed under the GPL
+ */
+
+#ifndef __ELF_USER_H__
+#define __ELF_USER_H__
+
+/* For compilation on a host that doesn't support AT_SYSINFO (Linux 2.4)  */
+
+#ifndef AT_SYSINFO
+#define AT_SYSINFO 32
+#endif
+#ifndef AT_SYSINFO_EHDR
+#define AT_SYSINFO_EHDR 33
+#endif
+
+#endif
diff --git a/arch/um/include/registers.h b/arch/um/include/registers.h
new file mode 100644 (file)
index 0000000..87899df
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2004 PathScale, Inc
+ * Licensed under the GPL
+ */
+
+#ifndef __REGISTERS_H
+#define __REGISTERS_H
+
+#include "sysdep/ptrace.h"
+
+extern void init_thread_registers(union uml_pt_regs *to);
+extern void save_registers(int pid, union uml_pt_regs *regs);
+extern void restore_registers(int pid, union uml_pt_regs *regs);
+extern void init_registers(int pid);
+
+#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/sysdep-i386/signal.h b/arch/um/include/sysdep-i386/signal.h
new file mode 100644 (file)
index 0000000..b1e1f7a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2004 PathScale, Inc
+ * Licensed under the GPL
+ */
+
+#ifndef __I386_SIGNAL_H_
+#define __I386_SIGNAL_H_
+
+#include <signal.h>
+
+#define ARCH_GET_SIGCONTEXT(sc, sig) \
+       do sc = (struct sigcontext *) (&sig + 1); while(0)
+
+#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/sysdep-x86_64/checksum.h b/arch/um/include/sysdep-x86_64/checksum.h
new file mode 100644 (file)
index 0000000..d57aa7f
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_SYSDEP_CHECKSUM_H
+#define __UM_SYSDEP_CHECKSUM_H
+
+#include "linux/string.h"
+#include "linux/in6.h"
+#include "asm/uaccess.h"
+
+extern unsigned int csum_partial_copy_from(const char *src, char *dst, int len,
+                                          int sum, int *err_ptr);
+extern unsigned csum_partial(const unsigned char *buff, unsigned len,
+                             unsigned sum);
+
+/*
+ *     Note: when you get a NULL pointer exception here this means someone
+ *     passed in an incorrect kernel address to one of these functions.
+ *
+ *     If you use these functions directly please don't forget the
+ *     verify_area().
+ */
+
+static __inline__
+unsigned int csum_partial_copy_nocheck(const char *src, char *dst,
+                                      int len, int sum)
+{
+       memcpy(dst, src, len);
+       return(csum_partial(dst, len, sum));
+}
+
+static __inline__
+unsigned int csum_partial_copy_from_user(const char *src, char *dst,
+                                        int len, int sum, int *err_ptr)
+{
+       return csum_partial_copy_from(src, dst, len, sum, err_ptr);
+}
+
+/**
+ * csum_fold - Fold and invert a 32bit checksum.
+ * sum: 32bit unfolded sum
+ *
+ * Fold a 32bit running checksum to 16bit and invert it. This is usually
+ * the last step before putting a checksum into a packet.
+ * Make sure not to mix with 64bit checksums.
+ */
+static inline unsigned int csum_fold(unsigned int sum)
+{
+       __asm__(
+               "  addl %1,%0\n"
+               "  adcl $0xffff,%0"
+               : "=r" (sum)
+               : "r" (sum << 16), "0" (sum & 0xffff0000)
+       );
+       return (~sum) >> 16;
+}
+
+/**
+ * csum_tcpup_nofold - Compute an IPv4 pseudo header checksum.
+ * @saddr: source address
+ * @daddr: destination address
+ * @len: length of packet
+ * @proto: ip protocol of packet
+ * @sum: initial sum to be added in (32bit unfolded)
+ *
+ * Returns the pseudo header checksum the input data. Result is
+ * 32bit unfolded.
+ */
+static inline unsigned long
+csum_tcpudp_nofold(unsigned saddr, unsigned daddr, unsigned short len,
+                  unsigned short proto, unsigned int sum)
+{
+       asm("  addl %1, %0\n"
+           "  adcl %2, %0\n"
+           "  adcl %3, %0\n"
+           "  adcl $0, %0\n"
+               : "=r" (sum)
+           : "g" (daddr), "g" (saddr), "g" ((ntohs(len)<<16)+proto*256), "0" (sum));
+    return sum;
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+static inline unsigned short int csum_tcpudp_magic(unsigned long saddr,
+                                                  unsigned long daddr,
+                                                  unsigned short len,
+                                                  unsigned short proto,
+                                                  unsigned int sum)
+{
+       return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
+}
+
+/**
+ * ip_fast_csum - Compute the IPv4 header checksum efficiently.
+ * iph: ipv4 header
+ * ihl: length of header / 4
+ */
+static inline unsigned short ip_fast_csum(unsigned char *iph, unsigned int ihl)
+{
+       unsigned int sum;
+
+       asm(    "  movl (%1), %0\n"
+               "  subl $4, %2\n"
+               "  jbe 2f\n"
+               "  addl 4(%1), %0\n"
+               "  adcl 8(%1), %0\n"
+               "  adcl 12(%1), %0\n"
+               "1: adcl 16(%1), %0\n"
+               "  lea 4(%1), %1\n"
+               "  decl %2\n"
+               "  jne  1b\n"
+               "  adcl $0, %0\n"
+               "  movl %0, %2\n"
+               "  shrl $16, %0\n"
+               "  addw %w2, %w0\n"
+               "  adcl $0, %0\n"
+               "  notl %0\n"
+               "2:"
+       /* Since the input registers which are loaded with iph and ipl
+          are modified, we must also specify them as outputs, or gcc
+          will assume they contain their original values. */
+       : "=r" (sum), "=r" (iph), "=r" (ihl)
+       : "1" (iph), "2" (ihl)
+       : "memory");
+       return(sum);
+}
+
+static inline unsigned add32_with_carry(unsigned a, unsigned b)
+{
+        asm("addl %2,%0\n\t"
+            "adcl $0,%0"
+            : "=r" (a)
+            : "0" (a), "r" (b));
+        return a;
+}
+
+#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/sysdep-x86_64/ptrace.h b/arch/um/include/sysdep-x86_64/ptrace.h
new file mode 100644 (file)
index 0000000..64b1cc1
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __SYSDEP_X86_64_PTRACE_H
+#define __SYSDEP_X86_64_PTRACE_H
+
+#include "uml-config.h"
+
+#ifdef UML_CONFIG_MODE_TT
+#include "sysdep/sc.h"
+#endif
+
+#ifdef UML_CONFIG_MODE_SKAS
+#include "skas_ptregs.h"
+
+#define REGS_IP(r) ((r)[HOST_IP])
+#define REGS_SP(r) ((r)[HOST_SP])
+
+#define REGS_RBX(r) ((r)[HOST_RBX])
+#define REGS_RCX(r) ((r)[HOST_RCX])
+#define REGS_RDX(r) ((r)[HOST_RDX])
+#define REGS_RSI(r) ((r)[HOST_RSI])
+#define REGS_RDI(r) ((r)[HOST_RDI])
+#define REGS_RBP(r) ((r)[HOST_RBP])
+#define REGS_RAX(r) ((r)[HOST_RAX])
+#define REGS_R8(r) ((r)[HOST_R8])
+#define REGS_R9(r) ((r)[HOST_R9])
+#define REGS_R10(r) ((r)[HOST_R10])
+#define REGS_R11(r) ((r)[HOST_R11])
+#define REGS_R12(r) ((r)[HOST_R12])
+#define REGS_R13(r) ((r)[HOST_R13])
+#define REGS_R14(r) ((r)[HOST_R14])
+#define REGS_R15(r) ((r)[HOST_R15])
+#define REGS_CS(r) ((r)[HOST_CS])
+#define REGS_EFLAGS(r) ((r)[HOST_EFLAGS])
+#define REGS_SS(r) ((r)[HOST_SS])
+
+#define HOST_FS_BASE 21
+#define HOST_GS_BASE 22
+#define HOST_DS 23
+#define HOST_ES 24
+#define HOST_FS 25
+#define HOST_GS 26
+
+#define REGS_FS_BASE(r) ((r)[HOST_FS_BASE])
+#define REGS_GS_BASE(r) ((r)[HOST_GS_BASE])
+#define REGS_DS(r) ((r)[HOST_DS])
+#define REGS_ES(r) ((r)[HOST_ES])
+#define REGS_FS(r) ((r)[HOST_FS])
+#define REGS_GS(r) ((r)[HOST_GS])
+
+#define REGS_ORIG_RAX(r) ((r)[HOST_ORIG_RAX])
+
+#define REGS_SET_SYSCALL_RETURN(r, res) REGS_RAX(r) = (res)
+
+#define REGS_RESTART_SYSCALL(r) IP_RESTART_SYSCALL(REGS_IP(r))
+
+#define REGS_SEGV_IS_FIXABLE(r) SEGV_IS_FIXABLE((r)->trap_type)
+
+#define REGS_FAULT_ADDR(r) ((r)->fault_addr)
+
+#define REGS_FAULT_WRITE(r) FAULT_WRITE((r)->fault_type)
+
+#define REGS_TRAP(r) ((r)->trap_type)
+
+#define REGS_ERR(r) ((r)->fault_type)
+
+#endif
+
+#include "choose-mode.h"
+
+/* XXX */
+union uml_pt_regs {
+#ifdef UML_CONFIG_MODE_TT
+       struct tt_regs {
+               long syscall;
+               unsigned long orig_rax;
+               void *sc;
+       } tt;
+#endif
+#ifdef UML_CONFIG_MODE_SKAS
+       struct skas_regs {
+               /* XXX */
+               unsigned long regs[27];
+               unsigned long fp[65];
+               unsigned long fault_addr;
+               unsigned long fault_type;
+               unsigned long trap_type;
+               long syscall;
+               int is_user;
+       } skas;
+#endif
+};
+
+#define EMPTY_UML_PT_REGS { }
+
+/* XXX */
+extern int mode_tt;
+
+#define UPT_RBX(r) CHOOSE_MODE(SC_RBX(UPT_SC(r)), REGS_RBX((r)->skas.regs))
+#define UPT_RCX(r) CHOOSE_MODE(SC_RCX(UPT_SC(r)), REGS_RCX((r)->skas.regs))
+#define UPT_RDX(r) CHOOSE_MODE(SC_RDX(UPT_SC(r)), REGS_RDX((r)->skas.regs))
+#define UPT_RSI(r) CHOOSE_MODE(SC_RSI(UPT_SC(r)), REGS_RSI((r)->skas.regs))
+#define UPT_RDI(r) CHOOSE_MODE(SC_RDI(UPT_SC(r)), REGS_RDI((r)->skas.regs))
+#define UPT_RBP(r) CHOOSE_MODE(SC_RBP(UPT_SC(r)), REGS_RBP((r)->skas.regs))
+#define UPT_RAX(r) CHOOSE_MODE(SC_RAX(UPT_SC(r)), REGS_RAX((r)->skas.regs))
+#define UPT_R8(r) CHOOSE_MODE(SC_R8(UPT_SC(r)), REGS_R8((r)->skas.regs))
+#define UPT_R9(r) CHOOSE_MODE(SC_R9(UPT_SC(r)), REGS_R9((r)->skas.regs))
+#define UPT_R10(r) CHOOSE_MODE(SC_R10(UPT_SC(r)), REGS_R10((r)->skas.regs))
+#define UPT_R11(r) CHOOSE_MODE(SC_R11(UPT_SC(r)), REGS_R11((r)->skas.regs))
+#define UPT_R12(r) CHOOSE_MODE(SC_R12(UPT_SC(r)), REGS_R12((r)->skas.regs))
+#define UPT_R13(r) CHOOSE_MODE(SC_R13(UPT_SC(r)), REGS_R13((r)->skas.regs))
+#define UPT_R14(r) CHOOSE_MODE(SC_R14(UPT_SC(r)), REGS_R14((r)->skas.regs))
+#define UPT_R15(r) CHOOSE_MODE(SC_R15(UPT_SC(r)), REGS_R15((r)->skas.regs))
+#define UPT_CS(r) CHOOSE_MODE(SC_CS(UPT_SC(r)), REGS_CS((r)->skas.regs))
+#define UPT_FS(r) CHOOSE_MODE(SC_FS(UPT_SC(r)), REGS_FS((r)->skas.regs))
+#define UPT_GS(r) CHOOSE_MODE(SC_GS(UPT_SC(r)), REGS_GS((r)->skas.regs))
+#define UPT_DS(r) CHOOSE_MODE(SC_DS(UPT_SC(r)), REGS_DS((r)->skas.regs))
+#define UPT_ES(r) CHOOSE_MODE(SC_ES(UPT_SC(r)), REGS_ES((r)->skas.regs))
+#define UPT_CS(r) CHOOSE_MODE(SC_CS(UPT_SC(r)), REGS_CS((r)->skas.regs))
+#define UPT_ORIG_RAX(r) \
+       CHOOSE_MODE((r)->tt.orig_rax, REGS_ORIG_RAX((r)->skas.regs))
+
+#define UPT_IP(r) CHOOSE_MODE(SC_IP(UPT_SC(r)), REGS_IP((r)->skas.regs))
+#define UPT_SP(r) CHOOSE_MODE(SC_SP(UPT_SC(r)), REGS_SP((r)->skas.regs))
+
+#define UPT_EFLAGS(r) \
+       CHOOSE_MODE(SC_EFLAGS(UPT_SC(r)), REGS_EFLAGS((r)->skas.regs))
+#define UPT_SC(r) ((r)->tt.sc)
+#define UPT_SYSCALL_NR(r) CHOOSE_MODE((r)->tt.syscall, (r)->skas.syscall)
+
+extern int user_context(unsigned long sp);
+
+#define UPT_IS_USER(r) \
+       CHOOSE_MODE(user_context(UPT_SP(r)), (r)->skas.is_user)
+
+#define UPT_SYSCALL_ARG1(r) UPT_RDI(r)
+#define UPT_SYSCALL_ARG2(r) UPT_RSI(r)
+#define UPT_SYSCALL_ARG3(r) UPT_RDX(r)
+#define UPT_SYSCALL_ARG4(r) UPT_R10(r)
+#define UPT_SYSCALL_ARG5(r) UPT_R8(r)
+#define UPT_SYSCALL_ARG6(r) UPT_R9(r)
+
+struct syscall_args {
+       unsigned long args[6];
+};
+
+#define SYSCALL_ARGS(r) ((struct syscall_args) \
+                        { .args = { UPT_SYSCALL_ARG1(r), \
+                                    UPT_SYSCALL_ARG2(r), \
+                                   UPT_SYSCALL_ARG3(r), \
+                                    UPT_SYSCALL_ARG4(r), \
+                                   UPT_SYSCALL_ARG5(r), \
+                                    UPT_SYSCALL_ARG6(r) } } )
+
+#define UPT_REG(regs, reg) \
+        ({      unsigned long val; \
+                switch(reg){ \
+               case R8: val = UPT_R8(regs); break; \
+               case R9: val = UPT_R9(regs); break; \
+               case R10: val = UPT_R10(regs); break; \
+               case R11: val = UPT_R11(regs); break; \
+               case R12: val = UPT_R12(regs); break; \
+               case R13: val = UPT_R13(regs); break; \
+               case R14: val = UPT_R14(regs); break; \
+               case R15: val = UPT_R15(regs); break; \
+                case RIP: val = UPT_IP(regs); break; \
+                case RSP: val = UPT_SP(regs); break; \
+                case RAX: val = UPT_RAX(regs); break; \
+                case RBX: val = UPT_RBX(regs); break; \
+                case RCX: val = UPT_RCX(regs); break; \
+                case RDX: val = UPT_RDX(regs); break; \
+                case RSI: val = UPT_RSI(regs); break; \
+                case RDI: val = UPT_RDI(regs); break; \
+                case RBP: val = UPT_RBP(regs); break; \
+                case ORIG_RAX: val = UPT_ORIG_RAX(regs); break; \
+                case CS: val = UPT_CS(regs); break; \
+                case DS: val = UPT_DS(regs); break; \
+                case ES: val = UPT_ES(regs); break; \
+                case FS: val = UPT_FS(regs); break; \
+                case GS: val = UPT_GS(regs); break; \
+                case EFLAGS: val = UPT_EFLAGS(regs); break; \
+                default :  \
+                        panic("Bad register in UPT_REG : %d\n", reg);  \
+                        val = -1; \
+                } \
+                val; \
+        })
+
+
+#define UPT_SET(regs, reg, val) \
+        ({      unsigned long val; \
+                switch(reg){ \
+               case R8: UPT_R8(regs) = val; break; \
+               case R9: UPT_R9(regs) = val; break; \
+               case R10: UPT_R10(regs) = val; break; \
+               case R11: UPT_R11(regs) = val; break; \
+               case R12: UPT_R12(regs) = val; break; \
+               case R13: UPT_R13(regs) = val; break; \
+               case R14: UPT_R14(regs) = val; break; \
+               case R15: UPT_R15(regs) = val; break; \
+                case RIP: UPT_IP(regs) = val; break; \
+                case RSP: UPT_SP(regs) = val; break; \
+                case RAX: UPT_RAX(regs) = val; break; \
+                case RBX: UPT_RBX(regs) = val; break; \
+                case RCX: UPT_RCX(regs) = val; break; \
+                case RDX: UPT_RDX(regs) = val; break; \
+                case RSI: UPT_RSI(regs) = val; break; \
+                case RDI: UPT_RDI(regs) = val; break; \
+                case RBP: UPT_RBP(regs) = val; break; \
+                case ORIG_RAX: UPT_ORIG_RAX(regs) = val; break; \
+                case CS: UPT_CS(regs) = val; break; \
+                case DS: UPT_DS(regs) = val; break; \
+                case ES: UPT_ES(regs) = val; break; \
+                case FS: UPT_FS(regs) = val; break; \
+                case GS: UPT_GS(regs) = val; break; \
+                case EFLAGS: UPT_EFLAGS(regs) = val; break; \
+                default :  \
+                        panic("Bad register in UPT_SET : %d\n", reg);  \
+                       break; \
+                } \
+                val; \
+        })
+
+#define UPT_SET_SYSCALL_RETURN(r, res) \
+       CHOOSE_MODE(SC_SET_SYSCALL_RETURN(UPT_SC(r), (res)), \
+                    REGS_SET_SYSCALL_RETURN((r)->skas.regs, (res)))
+
+#define UPT_RESTART_SYSCALL(r) \
+       CHOOSE_MODE(SC_RESTART_SYSCALL(UPT_SC(r)), \
+                   REGS_RESTART_SYSCALL((r)->skas.regs))
+
+#define UPT_SEGV_IS_FIXABLE(r) \
+       CHOOSE_MODE(SC_SEGV_IS_FIXABLE(UPT_SC(r)), \
+                    REGS_SEGV_IS_FIXABLE(&r->skas))
+
+#define UPT_FAULT_ADDR(r) \
+       CHOOSE_MODE(SC_FAULT_ADDR(UPT_SC(r)), REGS_FAULT_ADDR(&r->skas))
+
+#define UPT_FAULT_WRITE(r) \
+       CHOOSE_MODE(SC_FAULT_WRITE(UPT_SC(r)), REGS_FAULT_WRITE(&r->skas))
+
+#define UPT_TRAP(r) CHOOSE_MODE(SC_TRAP_TYPE(UPT_SC(r)), REGS_TRAP(&r->skas))
+#define UPT_ERR(r) CHOOSE_MODE(SC_FAULT_TYPE(UPT_SC(r)), REGS_ERR(&r->skas))
+
+#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/sysdep-x86_64/ptrace_user.h b/arch/um/include/sysdep-x86_64/ptrace_user.h
new file mode 100644 (file)
index 0000000..24cb4ee
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __SYSDEP_X86_64_PTRACE_USER_H__
+#define __SYSDEP_X86_64_PTRACE_USER_H__
+
+#define __FRAME_OFFSETS
+#include <asm/ptrace.h>
+#undef __FRAME_OFFSETS
+
+#define PT_INDEX(off) ((off) / sizeof(unsigned long))
+
+#define PT_SYSCALL_NR(regs) ((regs)[PT_INDEX(ORIG_RAX)])
+#define PT_SYSCALL_NR_OFFSET (ORIG_RAX)
+
+#define PT_SYSCALL_ARG1(regs) (((unsigned long *) (regs))[PT_INDEX(RDI)])
+#define PT_SYSCALL_ARG1_OFFSET (RDI)
+
+#define PT_SYSCALL_ARG2(regs) (((unsigned long *) (regs))[PT_INDEX(RSI)])
+#define PT_SYSCALL_ARG2_OFFSET (RSI)
+
+#define PT_SYSCALL_ARG3(regs) (((unsigned long *) (regs))[PT_INDEX(RDX)])
+#define PT_SYSCALL_ARG3_OFFSET (RDX)
+
+#define PT_SYSCALL_ARG4(regs) (((unsigned long *) (regs))[PT_INDEX(RCX)])
+#define PT_SYSCALL_ARG4_OFFSET (RCX)
+
+#define PT_SYSCALL_ARG5(regs) (((unsigned long *) (regs))[PT_INDEX(R8)])
+#define PT_SYSCALL_ARG5_OFFSET (R8)
+
+#define PT_SYSCALL_ARG6(regs) (((unsigned long *) (regs))[PT_INDEX(R9)])
+#define PT_SYSCALL_ARG6_OFFSET (R9)
+
+#define PT_SYSCALL_RET_OFFSET (RAX)
+
+#define PT_IP_OFFSET (RIP)
+#define PT_IP(regs) ((regs)[PT_INDEX(RIP)])
+
+#define PT_SP_OFFSET (RSP)
+#define PT_SP(regs) ((regs)[PT_INDEX(RSP)])
+
+#define PT_ORIG_RAX_OFFSET (ORIG_RAX)
+#define PT_ORIG_RAX(regs) ((regs)[PT_INDEX(ORIG_RAX)])
+
+#define MAX_REG_OFFSET (FRAME_SIZE)
+#define MAX_REG_NR ((MAX_REG_OFFSET) / sizeof(unsigned long))
+
+/* x86_64 FC3 doesn't define this in /usr/include/linux/ptrace.h even though
+ * it's defined in the kernel's include/linux/ptrace.h. Additionally, use the
+ * 2.4 name and value for 2.4 host compatibility.
+ */
+#ifndef PTRACE_OLDSETOPTIONS
+#define PTRACE_OLDSETOPTIONS 21
+#endif
+
+#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/sysdep-x86_64/sigcontext.h b/arch/um/include/sysdep-x86_64/sigcontext.h
new file mode 100644 (file)
index 0000000..6a0c346
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __SYSDEP_X86_64_SIGCONTEXT_H
+#define __SYSDEP_X86_64_SIGCONTEXT_H
+
+#include "sc.h"
+
+#define IP_RESTART_SYSCALL(ip) ((ip) -= 2)
+
+#define SC_RESTART_SYSCALL(sc) IP_RESTART_SYSCALL(SC_IP(sc))
+#define SC_SET_SYSCALL_RETURN(sc, result) SC_RAX(sc) = (result)
+
+#define SC_FAULT_ADDR(sc) SC_CR2(sc)
+#define SC_FAULT_TYPE(sc) SC_ERR(sc)
+
+#define FAULT_WRITE(err) ((err) & 2)
+
+#define SC_FAULT_WRITE(sc) FAULT_WRITE(SC_FAULT_TYPE(sc))
+
+#define SC_TRAP_TYPE(sc) SC_TRAPNO(sc)
+
+/* ptrace expects that, at the start of a system call, %eax contains
+ * -ENOSYS, so this makes it so.
+ */
+
+#define SC_START_SYSCALL(sc) do SC_RAX(sc) = -ENOSYS; while(0)
+
+#define SEGV_IS_FIXABLE(trap) ((trap) == 14)
+#define SC_SEGV_IS_FIXABLE(sc) SEGV_IS_FIXABLE(SC_TRAP_TYPE(sc))
+
+extern unsigned long *sc_sigmask(void *sc_ptr);
+
+#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/sysdep-x86_64/signal.h b/arch/um/include/sysdep-x86_64/signal.h
new file mode 100644 (file)
index 0000000..e5e5275
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2004 PathScale, Inc
+ * Licensed under the GPL
+ */
+
+#ifndef __X86_64_SIGNAL_H_
+#define __X86_64_SIGNAL_H_
+
+#define ARCH_GET_SIGCONTEXT(sc, sig_addr) \
+       do { \
+               struct ucontext *__uc; \
+               asm("movq %%rdx, %0" : "=r" (__uc)); \
+               sc = (struct sigcontext *) &__uc->uc_mcontext; \
+       } while(0)
+
+#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/sysdep-x86_64/syscalls.h b/arch/um/include/sysdep-x86_64/syscalls.h
new file mode 100644 (file)
index 0000000..b187a41
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __SYSDEP_X86_64_SYSCALLS_H__
+#define __SYSDEP_X86_64_SYSCALLS_H__
+
+#include <linux/msg.h>
+#include <linux/shm.h>
+
+typedef long syscall_handler_t(void);
+
+extern syscall_handler_t *ia32_sys_call_table[];
+
+#define EXECUTE_SYSCALL(syscall, regs) \
+       (((long (*)(long, long, long, long, long, long)) \
+         (*sys_call_table[syscall]))(UPT_SYSCALL_ARG1(&regs->regs), \
+                                     UPT_SYSCALL_ARG2(&regs->regs), \
+                                     UPT_SYSCALL_ARG3(&regs->regs), \
+                                     UPT_SYSCALL_ARG4(&regs->regs), \
+                                     UPT_SYSCALL_ARG5(&regs->regs), \
+                                     UPT_SYSCALL_ARG6(&regs->regs)))
+
+extern long old_mmap(unsigned long addr, unsigned long len,
+                    unsigned long prot, unsigned long flags,
+                    unsigned long fd, unsigned long pgoff);
+extern syscall_handler_t wrap_sys_shmat;
+extern syscall_handler_t sys_modify_ldt;
+extern syscall_handler_t sys_arch_prctl;
+
+#define ARCH_SYSCALLS \
+       [ __NR_mmap ] = (syscall_handler_t *) old_mmap, \
+       [ __NR_select ] = (syscall_handler_t *) sys_select, \
+       [ __NR_mincore ] = (syscall_handler_t *) sys_mincore, \
+       [ __NR_madvise ] = (syscall_handler_t *) sys_madvise, \
+       [ __NR_shmget ] = (syscall_handler_t *) sys_shmget, \
+       [ __NR_shmat ] = (syscall_handler_t *) wrap_sys_shmat, \
+       [ __NR_shmctl ] = (syscall_handler_t *) sys_shmctl, \
+       [ __NR_semop ] = (syscall_handler_t *) sys_semop, \
+       [ __NR_semget ] = (syscall_handler_t *) sys_semget, \
+       [ __NR_semctl ] = (syscall_handler_t *) sys_semctl, \
+       [ __NR_shmdt ] = (syscall_handler_t *) sys_shmdt, \
+       [ __NR_msgget ] = (syscall_handler_t *) sys_msgget, \
+       [ __NR_msgsnd ] = (syscall_handler_t *) sys_msgsnd, \
+       [ __NR_msgrcv ] = (syscall_handler_t *) sys_msgrcv, \
+       [ __NR_msgctl ] = (syscall_handler_t *) sys_msgctl, \
+       [ __NR_pivot_root ] = (syscall_handler_t *) sys_pivot_root, \
+       [ __NR_tuxcall ] = (syscall_handler_t *) sys_ni_syscall, \
+       [ __NR_security ] = (syscall_handler_t *) sys_ni_syscall, \
+       [ __NR_epoll_ctl_old ] = (syscall_handler_t *) sys_ni_syscall, \
+       [ __NR_epoll_wait_old ] = (syscall_handler_t *) sys_ni_syscall, \
+       [ __NR_modify_ldt ] = (syscall_handler_t *) sys_modify_ldt, \
+       [ __NR_arch_prctl ] = (syscall_handler_t *) sys_arch_prctl, \
+       [ __NR_socket ] = (syscall_handler_t *) sys_socket, \
+       [ __NR_connect ] = (syscall_handler_t *) sys_connect, \
+       [ __NR_accept ] = (syscall_handler_t *) sys_accept, \
+       [ __NR_recvfrom ] = (syscall_handler_t *) sys_recvfrom, \
+       [ __NR_recvmsg ] = (syscall_handler_t *) sys_recvmsg, \
+       [ __NR_sendmsg ] = (syscall_handler_t *) sys_sendmsg, \
+       [ __NR_bind ] = (syscall_handler_t *) sys_bind, \
+       [ __NR_listen ] = (syscall_handler_t *) sys_listen, \
+       [ __NR_getsockname ] = (syscall_handler_t *) sys_getsockname, \
+       [ __NR_getpeername ] = (syscall_handler_t *) sys_getpeername, \
+       [ __NR_socketpair ] = (syscall_handler_t *) sys_socketpair, \
+       [ __NR_sendto ] = (syscall_handler_t *) sys_sendto, \
+       [ __NR_shutdown ] = (syscall_handler_t *) sys_shutdown, \
+       [ __NR_setsockopt ] = (syscall_handler_t *) sys_setsockopt, \
+       [ __NR_getsockopt ] = (syscall_handler_t *) sys_getsockopt, \
+       [ __NR_iopl ] = (syscall_handler_t *) sys_ni_syscall, \
+       [ __NR_set_thread_area ] = (syscall_handler_t *) sys_ni_syscall, \
+       [ __NR_get_thread_area ] = (syscall_handler_t *) sys_ni_syscall, \
+       [ __NR_semtimedop ] = (syscall_handler_t *) sys_semtimedop, \
+       [ 251 ] = (syscall_handler_t *) sys_ni_syscall,
+
+#define LAST_ARCH_SYSCALL 251
+#define NR_syscalls 1024
+
+#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/skas/include/mmu-skas.h b/arch/um/kernel/skas/include/mmu-skas.h
new file mode 100644 (file)
index 0000000..4cd60d7
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __SKAS_MMU_H
+#define __SKAS_MMU_H
+
+struct mmu_context_skas {
+       int mm_fd;
+};
+
+#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/skas/include/mode-skas.h b/arch/um/kernel/skas/include/mode-skas.h
new file mode 100644 (file)
index 0000000..f198388
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __MODE_SKAS_H__
+#define __MODE_SKAS_H__
+
+#include <sysdep/ptrace.h>
+
+extern unsigned long exec_regs[];
+extern unsigned long exec_fp_regs[];
+extern unsigned long exec_fpx_regs[];
+extern int have_fpx_regs;
+
+extern void user_time_init_skas(void);
+extern int copy_sc_from_user_skas(int pid, union uml_pt_regs *regs,
+                                 void *from_ptr);
+extern int copy_sc_to_user_skas(int pid, void *to_ptr, void *fp,
+                               union uml_pt_regs *regs,
+                               unsigned long fault_addr, int fault_type);
+extern void sig_handler_common_skas(int sig, void *sc_ptr);
+extern void halt_skas(void);
+extern void reboot_skas(void);
+extern void kill_off_processes_skas(void);
+extern int is_skas_winch(int pid, int fd, void *data);
+
+#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/skas/include/mode_kern-skas.h b/arch/um/kernel/skas/include/mode_kern-skas.h
new file mode 100644 (file)
index 0000000..94c5649
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __SKAS_MODE_KERN_H__
+#define __SKAS_MODE_KERN_H__
+
+#include "linux/sched.h"
+#include "asm/page.h"
+#include "asm/ptrace.h"
+
+extern void flush_thread_skas(void);
+extern void *switch_to_skas(void *prev, void *next);
+extern void start_thread_skas(struct pt_regs *regs, unsigned long eip,
+                             unsigned long esp);
+extern int copy_thread_skas(int nr, unsigned long clone_flags,
+                           unsigned long sp, unsigned long stack_top,
+                           struct task_struct *p, struct pt_regs *regs);
+extern void release_thread_skas(struct task_struct *task);
+extern void exit_thread_skas(void);
+extern void initial_thread_cb_skas(void (*proc)(void *), void *arg);
+extern void init_idle_skas(void);
+extern void flush_tlb_kernel_range_skas(unsigned long start,
+                                       unsigned long end);
+extern void flush_tlb_kernel_vm_skas(void);
+extern void __flush_tlb_one_skas(unsigned long addr);
+extern void flush_tlb_range_skas(struct vm_area_struct *vma,
+                                unsigned long start, unsigned long end);
+extern void flush_tlb_mm_skas(struct mm_struct *mm);
+extern void force_flush_all_skas(void);
+extern long execute_syscall_skas(void *r);
+extern void before_mem_skas(unsigned long unused);
+extern unsigned long set_task_sizes_skas(int arg, unsigned long *host_size_out,
+                                        unsigned long *task_size_out);
+extern int start_uml_skas(void);
+extern int external_pid_skas(struct task_struct *task);
+extern int thread_pid_skas(struct task_struct *task);
+
+#define kmem_end_skas (host_task_size - 1024 * 1024)
+
+#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/skas/include/uaccess-skas.h b/arch/um/kernel/skas/include/uaccess-skas.h
new file mode 100644 (file)
index 0000000..da9c52c
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __SKAS_UACCESS_H
+#define __SKAS_UACCESS_H
+
+#include "asm/errno.h"
+
+#define access_ok_skas(type, addr, size) \
+       ((segment_eq(get_fs(), KERNEL_DS)) || \
+        (((unsigned long) (addr) < TASK_SIZE) && \
+         ((unsigned long) (addr) + (size) <= TASK_SIZE)))
+
+static inline int verify_area_skas(int type, const void * addr,
+                                  unsigned long size)
+{
+       return(access_ok_skas(type, addr, size) ? 0 : -EFAULT);
+}
+
+extern int copy_from_user_skas(void *to, const void *from, int n);
+extern int copy_to_user_skas(void *to, const void *from, int n);
+extern int strncpy_from_user_skas(char *dst, const char *src, int count);
+extern int __clear_user_skas(void *mem, int len);
+extern int clear_user_skas(void *mem, int len);
+extern int strnlen_user_skas(const void *str, int len);
+
+#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/skas/util/mk_ptregs-i386.c b/arch/um/kernel/skas/util/mk_ptregs-i386.c
new file mode 100644 (file)
index 0000000..0788dd0
--- /dev/null
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <asm/ptrace.h>
+#include <asm/user.h>
+
+#define PRINT_REG(name, val) printf("#define HOST_%s %d\n", (name), (val))
+
+int main(int argc, char **argv)
+{
+       printf("/* Automatically generated by "
+              "arch/um/kernel/skas/util/mk_ptregs */\n");
+       printf("\n");
+       printf("#ifndef __SKAS_PT_REGS_\n");
+       printf("#define __SKAS_PT_REGS_\n");
+       printf("\n");
+       printf("#define HOST_FRAME_SIZE %d\n", FRAME_SIZE);
+       printf("#define HOST_FP_SIZE %d\n",
+              sizeof(struct user_i387_struct) / sizeof(unsigned long));
+       printf("#define HOST_XFP_SIZE %d\n",
+              sizeof(struct user_fxsr_struct) / sizeof(unsigned long));
+
+       PRINT_REG("IP", EIP);
+       PRINT_REG("SP", UESP);
+       PRINT_REG("EFLAGS", EFL);
+       PRINT_REG("EAX", EAX);
+       PRINT_REG("EBX", EBX);
+       PRINT_REG("ECX", ECX);
+       PRINT_REG("EDX", EDX);
+       PRINT_REG("ESI", ESI);
+       PRINT_REG("EDI", EDI);
+       PRINT_REG("EBP", EBP);
+       PRINT_REG("CS", CS);
+       PRINT_REG("SS", SS);
+       PRINT_REG("DS", DS);
+       PRINT_REG("FS", FS);
+       PRINT_REG("ES", ES);
+       PRINT_REG("GS", GS);
+       printf("\n");
+       printf("#endif\n");
+       return(0);
+}
+
+/*
+ * 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/util/mk_ptregs-x86_64.c b/arch/um/kernel/skas/util/mk_ptregs-x86_64.c
new file mode 100644 (file)
index 0000000..67aee92
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#define __FRAME_OFFSETS
+#include <asm/ptrace.h>
+
+#define PRINT_REG(name, val) \
+       printf("#define HOST_%s (%d / sizeof(unsigned long))\n", (name), (val))
+
+int main(int argc, char **argv)
+{
+       printf("/* Automatically generated by "
+              "arch/um/kernel/skas/util/mk_ptregs */\n");
+       printf("\n");
+       printf("#ifndef __SKAS_PT_REGS_\n");
+       printf("#define __SKAS_PT_REGS_\n");
+       printf("#define HOST_FRAME_SIZE (%d / sizeof(unsigned long))\n",
+              FRAME_SIZE);
+       PRINT_REG("RBX", RBX);
+       PRINT_REG("RCX", RCX);
+       PRINT_REG("RDI", RDI);
+       PRINT_REG("RSI", RSI);
+       PRINT_REG("RDX", RDX);
+       PRINT_REG("RBP", RBP);
+       PRINT_REG("RAX", RAX);
+       PRINT_REG("R8", R8);
+       PRINT_REG("R9", R9);
+       PRINT_REG("R10", R10);
+       PRINT_REG("R11", R11);
+       PRINT_REG("R12", R12);
+       PRINT_REG("R13", R13);
+       PRINT_REG("R14", R14);
+       PRINT_REG("R15", R15);
+       PRINT_REG("ORIG_RAX", ORIG_RAX);
+       PRINT_REG("CS", CS);
+       PRINT_REG("SS", SS);
+       PRINT_REG("EFLAGS", EFLAGS);
+#if 0
+       PRINT_REG("FS", FS);
+       PRINT_REG("GS", GS);
+       PRINT_REG("DS", DS);
+       PRINT_REG("ES", ES);
+#endif
+
+       PRINT_REG("IP", RIP);
+       PRINT_REG("SP", RSP);
+       printf("#define HOST_FP_SIZE 0\n");
+       printf("#define HOST_XFP_SIZE 0\n");
+       printf("\n");
+       printf("\n");
+       printf("#endif\n");
+       return(0);
+}
+
+/*
+ * 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/include/mmu-tt.h b/arch/um/kernel/tt/include/mmu-tt.h
new file mode 100644 (file)
index 0000000..0440510
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __TT_MMU_H
+#define __TT_MMU_H
+
+struct mmu_context_tt {
+};
+
+#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/tt/include/mode-tt.h b/arch/um/kernel/tt/include/mode-tt.h
new file mode 100644 (file)
index 0000000..fb5cfd3
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __MODE_TT_H__
+#define __MODE_TT_H__
+
+#include "sysdep/ptrace.h"
+
+enum { OP_NONE, OP_EXEC, OP_FORK, OP_TRACE_ON, OP_REBOOT, OP_HALT, OP_CB };
+
+extern int tracing_pid;
+
+extern int tracer(int (*init_proc)(void *), void *sp);
+extern void user_time_init_tt(void);
+extern int copy_sc_from_user_tt(void *to_ptr, void *from_ptr, void *data);
+extern int copy_sc_to_user_tt(void *to_ptr, void *fp, void *from_ptr,
+                             void *data);
+extern void sig_handler_common_tt(int sig, void *sc);
+extern void syscall_handler_tt(int sig, union uml_pt_regs *regs);
+extern void reboot_tt(void);
+extern void halt_tt(void);
+extern int is_tracer_winch(int pid, int fd, void *data);
+extern void kill_off_processes_tt(void);
+
+#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/tt/include/mode_kern-tt.h b/arch/um/kernel/tt/include/mode_kern-tt.h
new file mode 100644 (file)
index 0000000..28aaab3
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __TT_MODE_KERN_H__
+#define __TT_MODE_KERN_H__
+
+#include "linux/sched.h"
+#include "asm/page.h"
+#include "asm/ptrace.h"
+#include "asm/uaccess.h"
+
+extern void *switch_to_tt(void *prev, void *next);
+extern void flush_thread_tt(void);
+extern void start_thread_tt(struct pt_regs *regs, unsigned long eip,
+                          unsigned long esp);
+extern int copy_thread_tt(int nr, unsigned long clone_flags, unsigned long sp,
+                         unsigned long stack_top, struct task_struct *p,
+                         struct pt_regs *regs);
+extern void release_thread_tt(struct task_struct *task);
+extern void exit_thread_tt(void);
+extern void initial_thread_cb_tt(void (*proc)(void *), void *arg);
+extern void init_idle_tt(void);
+extern void flush_tlb_kernel_range_tt(unsigned long start, unsigned long end);
+extern void flush_tlb_kernel_vm_tt(void);
+extern void __flush_tlb_one_tt(unsigned long addr);
+extern void flush_tlb_range_tt(struct vm_area_struct *vma,
+                              unsigned long start, unsigned long end);
+extern void flush_tlb_mm_tt(struct mm_struct *mm);
+extern void force_flush_all_tt(void);
+extern long execute_syscall_tt(void *r);
+extern void before_mem_tt(unsigned long brk_start);
+extern unsigned long set_task_sizes_tt(int arg, unsigned long *host_size_out,
+                                      unsigned long *task_size_out);
+extern int start_uml_tt(void);
+extern int external_pid_tt(struct task_struct *task);
+extern int thread_pid_tt(struct task_struct *task);
+
+#define kmem_end_tt (host_task_size - ABOVE_KMEM)
+
+#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/tt/include/uaccess-tt.h b/arch/um/kernel/tt/include/uaccess-tt.h
new file mode 100644 (file)
index 0000000..f0bad01
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __TT_UACCESS_H
+#define __TT_UACCESS_H
+
+#include "linux/string.h"
+#include "linux/sched.h"
+#include "asm/processor.h"
+#include "asm/errno.h"
+#include "asm/current.h"
+#include "asm/a.out.h"
+#include "uml_uaccess.h"
+
+#define ABOVE_KMEM (16 * 1024 * 1024)
+
+extern unsigned long end_vm;
+extern unsigned long uml_physmem;
+
+#define under_task_size(addr, size) \
+       (((unsigned long) (addr) < TASK_SIZE) && \
+         (((unsigned long) (addr) + (size)) < TASK_SIZE))
+
+#define is_stack(addr, size) \
+       (((unsigned long) (addr) < STACK_TOP) && \
+        ((unsigned long) (addr) >= STACK_TOP - ABOVE_KMEM) && \
+        (((unsigned long) (addr) + (size)) <= STACK_TOP))
+
+#define access_ok_tt(type, addr, size) \
+       ((type == VERIFY_READ) || (segment_eq(get_fs(), KERNEL_DS)) || \
+         (((unsigned long) (addr) <= ((unsigned long) (addr) + (size))) && \
+          (under_task_size(addr, size) || is_stack(addr, size))))
+
+static inline int verify_area_tt(int type, const void * addr,
+                                unsigned long size)
+{
+       return(access_ok_tt(type, addr, size) ? 0 : -EFAULT);
+}
+
+extern unsigned long get_fault_addr(void);
+
+extern int __do_copy_from_user(void *to, const void *from, int n,
+                              void **fault_addr, void **fault_catcher);
+extern int __do_strncpy_from_user(char *dst, const char *src, size_t n,
+                                 void **fault_addr, void **fault_catcher);
+extern int __do_clear_user(void *mem, size_t len, void **fault_addr,
+                          void **fault_catcher);
+extern int __do_strnlen_user(const char *str, unsigned long n,
+                            void **fault_addr, void **fault_catcher);
+
+extern int copy_from_user_tt(void *to, const void *from, int n);
+extern int copy_to_user_tt(void *to, const void *from, int n);
+extern int strncpy_from_user_tt(char *dst, const char *src, int count);
+extern int __clear_user_tt(void *mem, int len);
+extern int clear_user_tt(void *mem, int len);
+extern int strnlen_user_tt(const void *str, int len);
+
+#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/os-Linux/elf_aux.c b/arch/um/os-Linux/elf_aux.c
new file mode 100644 (file)
index 0000000..9aee0b6
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *  arch/um/kernel/elf_aux.c
+ *
+ *  Scan the Elf auxiliary vector provided by the host to extract
+ *  information about vsyscall-page, etc.
+ *
+ *  Copyright (C) 2004 Fujitsu Siemens Computers GmbH
+ *  Author: Bodo Stroesser (bodo.stroesser@fujitsu-siemens.com)
+ */
+#include <elf.h>
+#include <stddef.h>
+#include "init.h"
+#include "elf_user.h"
+
+#if ELF_CLASS == ELFCLASS32
+typedef Elf32_auxv_t elf_auxv_t;
+#else
+typedef Elf64_auxv_t elf_auxv_t;
+#endif
+
+char * elf_aux_platform;
+long elf_aux_hwcap;
+
+unsigned long vsyscall_ehdr;
+unsigned long vsyscall_end;
+
+unsigned long __kernel_vsyscall;
+
+__init void scan_elf_aux( char **envp)
+{
+       long page_size = 0;
+       elf_auxv_t * auxv;
+
+       while ( *envp++ != NULL) ;
+
+       for ( auxv = (elf_auxv_t *)envp; auxv->a_type != AT_NULL; auxv++) {
+               switch ( auxv->a_type ) {
+                       case AT_SYSINFO:
+                               __kernel_vsyscall = auxv->a_un.a_val;
+                               break;
+                       case AT_SYSINFO_EHDR:
+                               vsyscall_ehdr = auxv->a_un.a_val;
+                               break;
+                       case AT_HWCAP:
+                               elf_aux_hwcap = auxv->a_un.a_val;
+                               break;
+                       case AT_PLATFORM:
+                               elf_aux_platform = auxv->a_un.a_ptr;
+                               break;
+                       case AT_PAGESZ:
+                               page_size = auxv->a_un.a_val;
+                               break;
+               }
+       }
+       if ( ! __kernel_vsyscall || ! vsyscall_ehdr ||
+            ! elf_aux_hwcap || ! elf_aux_platform ||
+            ! page_size || (vsyscall_ehdr % page_size) ) {
+               __kernel_vsyscall = 0;
+               vsyscall_ehdr = 0;
+               elf_aux_hwcap = 0;
+               elf_aux_platform = "i586";
+       }
+       else {
+               vsyscall_end = vsyscall_ehdr + page_size;
+       }
+}
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c
new file mode 100644 (file)
index 0000000..7eac1ba
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2004 PathScale, Inc
+ * Licensed under the GPL
+ */
+
+#include <signal.h>
+#include "time_user.h"
+#include "mode.h"
+#include "sysdep/signal.h"
+
+void sig_handler(int sig)
+{
+       struct sigcontext *sc;
+
+       ARCH_GET_SIGCONTEXT(sc, sig);
+       CHOOSE_MODE_PROC(sig_handler_common_tt, sig_handler_common_skas,
+                        sig, sc);
+}
+
+extern int timer_irq_inited;
+
+void alarm_handler(int sig)
+{
+       struct sigcontext *sc;
+
+       ARCH_GET_SIGCONTEXT(sc, sig);
+       if(!timer_irq_inited) return;
+
+       if(sig == SIGALRM)
+               switch_timers(0);
+
+       CHOOSE_MODE_PROC(sig_handler_common_tt, sig_handler_common_skas,
+                        sig, sc);
+
+       if(sig == SIGALRM)
+               switch_timers(1);
+}
+
+/*
+ * 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/sys-i386/Makefile b/arch/um/os-Linux/sys-i386/Makefile
new file mode 100644 (file)
index 0000000..bdfa841
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
+# Licensed under the GPL
+#
+
+obj-$(CONFIG_MODE_SKAS) = registers.o
+
+USER_OBJS := $(foreach file,$(obj-y),$(obj)/$(file))
+
+$(USER_OBJS) : %.o: %.c
+       $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $<
diff --git a/arch/um/os-Linux/sys-i386/registers.c b/arch/um/os-Linux/sys-i386/registers.c
new file mode 100644 (file)
index 0000000..cbfbaf2
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2004 PathScale, Inc
+ * Licensed under the GPL
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include "sysdep/ptrace.h"
+#include "uml-config.h"
+#include "skas_ptregs.h"
+#include "registers.h"
+#include "user.h"
+
+/* These are set once at boot time and not changed thereafter */
+
+static unsigned long exec_regs[HOST_FRAME_SIZE];
+static unsigned long exec_fp_regs[HOST_FP_SIZE];
+static unsigned long exec_fpx_regs[HOST_XFP_SIZE];
+static int have_fpx_regs = 1;
+
+void init_thread_registers(union uml_pt_regs *to)
+{
+       memcpy(to->skas.regs, exec_regs, sizeof(to->skas.regs));
+       memcpy(to->skas.fp, exec_fp_regs, sizeof(to->skas.fp));
+       if(have_fpx_regs)
+               memcpy(to->skas.xfp, exec_fpx_regs, sizeof(to->skas.xfp));
+}
+
+static int move_registers(int pid, int int_op, union uml_pt_regs *regs,
+                         int fp_op, unsigned long *fp_regs)
+{
+       if(ptrace(int_op, pid, 0, regs->skas.regs) < 0)
+               return(-errno);
+
+       if(ptrace(fp_op, pid, 0, fp_regs) < 0)
+               return(-errno);
+
+       return(0);
+}
+
+void save_registers(int pid, union uml_pt_regs *regs)
+{
+       unsigned long *fp_regs;
+       int err, fp_op;
+
+       if(have_fpx_regs){
+               fp_op = PTRACE_GETFPXREGS;
+               fp_regs = regs->skas.xfp;
+       }
+       else {
+               fp_op = PTRACE_GETFPREGS;
+               fp_regs = regs->skas.fp;
+       }
+
+       err = move_registers(pid, PTRACE_GETREGS, regs, fp_op, fp_regs);
+       if(err)
+               panic("save_registers - saving registers failed, errno = %d\n",
+                     -err);
+}
+
+void restore_registers(int pid, union uml_pt_regs *regs)
+{
+       unsigned long *fp_regs;
+       int err, fp_op;
+
+       if(have_fpx_regs){
+               fp_op = PTRACE_SETFPXREGS;
+               fp_regs = regs->skas.xfp;
+       }
+       else {
+               fp_op = PTRACE_SETFPREGS;
+               fp_regs = regs->skas.fp;
+       }
+
+       err = move_registers(pid, PTRACE_SETREGS, regs, fp_op, fp_regs);
+       if(err)
+               panic("restore_registers - saving registers failed, "
+                     "errno = %d\n", -err);
+}
+
+void init_registers(int pid)
+{
+       int err;
+
+       err = ptrace(PTRACE_GETREGS, pid, 0, exec_regs);
+       if(err)
+               panic("check_ptrace : PTRACE_GETREGS failed, errno = %d",
+                     err);
+
+       err = ptrace(PTRACE_GETFPXREGS, pid, 0, exec_fpx_regs);
+       if(!err)
+               return;
+
+       have_fpx_regs = 0;
+       if(err != EIO)
+               panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d",
+                     err);
+
+       err = ptrace(PTRACE_GETFPREGS, pid, 0, exec_fp_regs);
+       if(err)
+               panic("check_ptrace : PTRACE_GETFPREGS failed, errno = %d",
+                     err);
+}
+
+/*
+ * 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/sys-x86_64/Makefile b/arch/um/os-Linux/sys-x86_64/Makefile
new file mode 100644 (file)
index 0000000..bdfa841
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
+# Licensed under the GPL
+#
+
+obj-$(CONFIG_MODE_SKAS) = registers.o
+
+USER_OBJS := $(foreach file,$(obj-y),$(obj)/$(file))
+
+$(USER_OBJS) : %.o: %.c
+       $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $<
diff --git a/arch/um/os-Linux/sys-x86_64/registers.c b/arch/um/os-Linux/sys-x86_64/registers.c
new file mode 100644 (file)
index 0000000..31b1f4f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2004 PathScale, Inc
+ * Licensed under the GPL
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include "sysdep/ptrace.h"
+#include "uml-config.h"
+#include "skas_ptregs.h"
+#include "registers.h"
+#include "user.h"
+
+/* These are set once at boot time and not changed thereafter */
+
+static unsigned long exec_regs[HOST_FRAME_SIZE];
+static unsigned long exec_fp_regs[HOST_FP_SIZE];
+
+void init_thread_registers(union uml_pt_regs *to)
+{
+       memcpy(to->skas.regs, exec_regs, sizeof(to->skas.regs));
+       memcpy(to->skas.fp, exec_fp_regs, sizeof(to->skas.fp));
+}
+
+static int move_registers(int pid, int int_op, int fp_op,
+                         union uml_pt_regs *regs)
+{
+       if(ptrace(int_op, pid, 0, regs->skas.regs) < 0)
+               return(-errno);
+
+       if(ptrace(fp_op, pid, 0, regs->skas.fp) < 0)
+               return(-errno);
+
+       return(0);
+}
+
+void save_registers(int pid, union uml_pt_regs *regs)
+{
+       int err;
+
+       err = move_registers(pid, PTRACE_GETREGS, PTRACE_GETFPREGS, regs);
+       if(err)
+               panic("save_registers - saving registers failed, errno = %d\n",
+                     -err);
+}
+
+void restore_registers(int pid, union uml_pt_regs *regs)
+{
+       int err;
+
+       err = move_registers(pid, PTRACE_SETREGS, PTRACE_SETFPREGS, regs);
+       if(err)
+               panic("restore_registers - saving registers failed, "
+                     "errno = %d\n", -err);
+}
+
+void init_registers(int pid)
+{
+       int err;
+
+       err = ptrace(PTRACE_GETREGS, pid, 0, exec_regs);
+       if(err)
+               panic("check_ptrace : PTRACE_GETREGS failed, errno = %d",
+                     err);
+
+       err = ptrace(PTRACE_GETFPREGS, pid, 0, exec_fp_regs);
+       if(err)
+               panic("check_ptrace : PTRACE_GETFPREGS failed, errno = %d",
+                     err);
+}
+
+/*
+ * 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/sys-i386/delay.c b/arch/um/sys-i386/delay.c
new file mode 100644 (file)
index 0000000..20d37db
--- /dev/null
@@ -0,0 +1,14 @@
+void __delay(unsigned long time)
+{
+       /* Stolen from the i386 __loop_delay */
+       int d0;
+       __asm__ __volatile__(
+               "\tjmp 1f\n"
+               ".align 16\n"
+               "1:\tjmp 2f\n"
+               ".align 16\n"
+               "2:\tdecl %0\n\tjns 2b"
+               :"=&a" (d0)
+               :"0" (time));
+}
+
diff --git a/arch/um/sys-i386/signal.c b/arch/um/sys-i386/signal.c
new file mode 100644 (file)
index 0000000..0cb4c86
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#include "linux/signal.h"
+#include "linux/ptrace.h"
+#include "asm/current.h"
+#include "asm/ucontext.h"
+#include "asm/uaccess.h"
+#include "asm/unistd.h"
+#include "frame_kern.h"
+#include "signal_user.h"
+#include "ptrace_user.h"
+#include "sigcontext.h"
+#include "mode.h"
+
+#ifdef CONFIG_MODE_SKAS
+
+#include "skas.h"
+
+static int copy_sc_from_user_skas(struct pt_regs *regs,
+                                 struct sigcontext *from)
+{
+       struct sigcontext sc;
+       unsigned long fpregs[HOST_FP_SIZE];
+       int err;
+
+       err = copy_from_user(&sc, from, sizeof(sc));
+       err |= copy_from_user(fpregs, sc.fpstate, sizeof(fpregs));
+       if(err)
+               return(err);
+
+       REGS_GS(regs->regs.skas.regs) = sc.gs;
+       REGS_FS(regs->regs.skas.regs) = sc.fs;
+       REGS_ES(regs->regs.skas.regs) = sc.es;
+       REGS_DS(regs->regs.skas.regs) = sc.ds;
+       REGS_EDI(regs->regs.skas.regs) = sc.edi;
+       REGS_ESI(regs->regs.skas.regs) = sc.esi;
+       REGS_EBP(regs->regs.skas.regs) = sc.ebp;
+       REGS_SP(regs->regs.skas.regs) = sc.esp;
+       REGS_EBX(regs->regs.skas.regs) = sc.ebx;
+       REGS_EDX(regs->regs.skas.regs) = sc.edx;
+       REGS_ECX(regs->regs.skas.regs) = sc.ecx;
+       REGS_EAX(regs->regs.skas.regs) = sc.eax;
+       REGS_IP(regs->regs.skas.regs) = sc.eip;
+       REGS_CS(regs->regs.skas.regs) = sc.cs;
+       REGS_EFLAGS(regs->regs.skas.regs) = sc.eflags;
+       REGS_SS(regs->regs.skas.regs) = sc.ss;
+       regs->regs.skas.fault_addr = sc.cr2;
+       regs->regs.skas.fault_type = FAULT_WRITE(sc.err);
+       regs->regs.skas.trap_type = sc.trapno;
+
+       err = ptrace_setfpregs(userspace_pid[0], fpregs);
+       if(err < 0){
+               printk("copy_sc_from_user_skas - PTRACE_SETFPREGS failed, "
+                      "errno = %d\n", err);
+               return(1);
+       }
+
+       return(0);
+}
+
+int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp,
+                        struct pt_regs *regs, unsigned long fault_addr,
+                        int fault_type)
+{
+       struct sigcontext sc;
+       unsigned long fpregs[HOST_FP_SIZE];
+       int err;
+
+       sc.gs = REGS_GS(regs->regs.skas.regs);
+       sc.fs = REGS_FS(regs->regs.skas.regs);
+       sc.es = REGS_ES(regs->regs.skas.regs);
+       sc.ds = REGS_DS(regs->regs.skas.regs);
+       sc.edi = REGS_EDI(regs->regs.skas.regs);
+       sc.esi = REGS_ESI(regs->regs.skas.regs);
+       sc.ebp = REGS_EBP(regs->regs.skas.regs);
+       sc.esp = REGS_SP(regs->regs.skas.regs);
+       sc.ebx = REGS_EBX(regs->regs.skas.regs);
+       sc.edx = REGS_EDX(regs->regs.skas.regs);
+       sc.ecx = REGS_ECX(regs->regs.skas.regs);
+       sc.eax = REGS_EAX(regs->regs.skas.regs);
+       sc.eip = REGS_IP(regs->regs.skas.regs);
+       sc.cs = REGS_CS(regs->regs.skas.regs);
+       sc.eflags = REGS_EFLAGS(regs->regs.skas.regs);
+       sc.esp_at_signal = regs->regs.skas.regs[UESP];
+       sc.ss = regs->regs.skas.regs[SS];
+       sc.cr2 = fault_addr;
+       sc.err = TO_SC_ERR(fault_type);
+       sc.trapno = regs->regs.skas.trap_type;
+
+       err = ptrace_getfpregs(userspace_pid[0], fpregs);
+       if(err < 0){
+               printk("copy_sc_to_user_skas - PTRACE_GETFPREGS failed, "
+                      "errno = %d\n", err);
+               return(1);
+       }
+       to_fp = (to_fp ? to_fp : (struct _fpstate *) (to + 1));
+       sc.fpstate = to_fp;
+
+       if(err)
+               return(err);
+
+       return(copy_to_user(to, &sc, sizeof(sc)) ||
+              copy_to_user(to_fp, fpregs, sizeof(fpregs)));
+}
+#endif
+
+#ifdef CONFIG_MODE_TT
+int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from,
+                        int fpsize)
+{
+       struct _fpstate *to_fp, *from_fp;
+       unsigned long sigs;
+       int err;
+
+       to_fp = to->fpstate;
+       from_fp = from->fpstate;
+       sigs = to->oldmask;
+       err = copy_from_user(to, from, sizeof(*to));
+       to->oldmask = sigs;
+       if(to_fp != NULL){
+               err |= copy_from_user(&to->fpstate, &to_fp,
+                                     sizeof(to->fpstate));
+               err |= copy_from_user(to_fp, from_fp, fpsize);
+       }
+       return(err);
+}
+
+int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp,
+                      struct sigcontext *from, int fpsize)
+{
+       struct _fpstate *to_fp, *from_fp;
+       int err;
+
+       to_fp = (fp ? fp : (struct _fpstate *) (to + 1));
+       from_fp = from->fpstate;
+       err = copy_to_user(to, from, sizeof(*to));
+       if(from_fp != NULL){
+               err |= copy_to_user(&to->fpstate, &to_fp,
+                                        sizeof(to->fpstate));
+               err |= copy_to_user(to_fp, from_fp, fpsize);
+       }
+       return(err);
+}
+#endif
+
+static int copy_sc_from_user(struct pt_regs *to, void __user *from)
+{
+       int ret;
+
+       ret = CHOOSE_MODE(copy_sc_from_user_tt(UPT_SC(&to->regs), from,
+                                              sizeof(struct _fpstate)),
+                         copy_sc_from_user_skas(to, from));
+       return(ret);
+}
+
+static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp,
+                          struct pt_regs *from)
+{
+       return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs),
+                                             sizeof(*fp)),
+                          copy_sc_to_user_skas(to, fp, from,
+                                               current->thread.cr2,
+                                               current->thread.err)));
+}
+
+static int copy_ucontext_to_user(struct ucontext *uc, struct _fpstate *fp,
+                                sigset_t *set, unsigned long sp)
+{
+       int err = 0;
+
+       err |= put_user(current->sas_ss_sp, &uc->uc_stack.ss_sp);
+       err |= put_user(sas_ss_flags(sp), &uc->uc_stack.ss_flags);
+       err |= put_user(current->sas_ss_size, &uc->uc_stack.ss_size);
+       err |= copy_sc_to_user(&uc->uc_mcontext, fp, &current->thread.regs);
+       err |= copy_to_user(&uc->uc_sigmask, set, sizeof(*set));
+       return(err);
+}
+
+struct sigframe
+{
+       char *pretcode;
+       int sig;
+       struct sigcontext sc;
+       struct _fpstate fpstate;
+       unsigned long extramask[_NSIG_WORDS-1];
+       char retcode[8];
+};
+
+struct rt_sigframe
+{
+       char *pretcode;
+       int sig;
+       struct siginfo *pinfo;
+       void *puc;
+       struct siginfo info;
+       struct ucontext uc;
+       struct _fpstate fpstate;
+       char retcode[8];
+};
+
+int setup_signal_stack_sc(unsigned long stack_top, int sig,
+                         struct k_sigaction *ka, struct pt_regs *regs,
+                         sigset_t *mask)
+{
+       struct sigframe __user *frame;
+       void *restorer;
+       int err = 0;
+
+       stack_top &= -8UL;
+       frame = (struct sigframe *) stack_top - 1;
+       if(verify_area(VERIFY_WRITE, frame, sizeof(*frame)))
+               return(1);
+
+       restorer = (void *) frame->retcode;
+       if(ka->sa.sa_flags & SA_RESTORER)
+               restorer = ka->sa.sa_restorer;
+
+       err |= __put_user(restorer, &frame->pretcode);
+       err |= __put_user(sig, &frame->sig);
+       err |= copy_sc_to_user(&frame->sc, NULL, regs);
+       err |= __put_user(mask->sig[0], &frame->sc.oldmask);
+       if (_NSIG_WORDS > 1)
+               err |= __copy_to_user(&frame->extramask, &mask->sig[1],
+                                     sizeof(frame->extramask));
+
+       /*
+        * This is popl %eax ; movl $,%eax ; int $0x80
+        *
+        * WE DO NOT USE IT ANY MORE! It's only left here for historical
+        * reasons and because gdb uses it as a signature to notice
+        * signal handler stack frames.
+        */
+       err |= __put_user(0xb858, (short __user *)(frame->retcode+0));
+       err |= __put_user(__NR_sigreturn, (int __user *)(frame->retcode+2));
+       err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));
+
+       if(err)
+               return(err);
+
+       PT_REGS_SP(regs) = (unsigned long) frame;
+       PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler;
+       PT_REGS_EAX(regs) = (unsigned long) sig;
+       PT_REGS_EDX(regs) = (unsigned long) 0;
+       PT_REGS_ECX(regs) = (unsigned long) 0;
+
+       if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
+               ptrace_notify(SIGTRAP);
+       return(0);
+}
+
+int setup_signal_stack_si(unsigned long stack_top, int sig,
+                         struct k_sigaction *ka, struct pt_regs *regs,
+                         siginfo_t *info, sigset_t *mask)
+{
+       struct rt_sigframe __user *frame;
+       void *restorer;
+       int err = 0;
+
+       stack_top &= -8UL;
+       frame = (struct rt_sigframe *) stack_top - 1;
+       if(verify_area(VERIFY_WRITE, frame, sizeof(*frame)))
+               return(1);
+
+       restorer = (void *) frame->retcode;
+       if(ka->sa.sa_flags & SA_RESTORER)
+               restorer = ka->sa.sa_restorer;
+
+       err |= __put_user(restorer, &frame->pretcode);
+       err |= __put_user(sig, &frame->sig);
+       err |= __put_user(&frame->info, &frame->pinfo);
+       err |= __put_user(&frame->uc, &frame->puc);
+       err |= copy_siginfo_to_user(&frame->info, info);
+       err |= copy_ucontext_to_user(&frame->uc, &frame->fpstate, mask,
+                                    PT_REGS_SP(regs));
+
+       /*
+        * This is movl $,%eax ; int $0x80
+        *
+        * WE DO NOT USE IT ANY MORE! It's only left here for historical
+        * reasons and because gdb uses it as a signature to notice
+        * signal handler stack frames.
+        */
+       err |= __put_user(0xb8, (char __user *)(frame->retcode+0));
+       err |= __put_user(__NR_rt_sigreturn, (int __user *)(frame->retcode+1));
+       err |= __put_user(0x80cd, (short __user *)(frame->retcode+5));
+
+       if(err)
+               return(err);
+
+       PT_REGS_SP(regs) = (unsigned long) frame;
+       PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler;
+       PT_REGS_EAX(regs) = (unsigned long) sig;
+       PT_REGS_EDX(regs) = (unsigned long) &frame->info;
+       PT_REGS_ECX(regs) = (unsigned long) &frame->uc;
+
+       if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
+               ptrace_notify(SIGTRAP);
+       return(0);
+}
+
+long sys_sigreturn(struct pt_regs regs)
+{
+       unsigned long __user sp = PT_REGS_SP(&current->thread.regs);
+       struct sigframe __user *frame = (struct sigframe *)(sp - 8);
+       sigset_t set;
+       struct sigcontext __user *sc = &frame->sc;
+       unsigned long __user *oldmask = &sc->oldmask;
+       unsigned long __user *extramask = frame->extramask;
+       int sig_size = (_NSIG_WORDS - 1) * sizeof(unsigned long);
+
+       if(copy_from_user(&set.sig[0], oldmask, sizeof(&set.sig[0])) ||
+          copy_from_user(&set.sig[1], extramask, sig_size))
+               goto segfault;
+
+       sigdelsetmask(&set, ~_BLOCKABLE);
+
+       spin_lock_irq(&current->sighand->siglock);
+       current->blocked = set;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       if(copy_sc_from_user(&current->thread.regs, sc))
+               goto segfault;
+
+       /* Avoid ERESTART handling */
+       PT_REGS_SYSCALL_NR(&current->thread.regs) = -1;
+       return(PT_REGS_SYSCALL_RET(&current->thread.regs));
+
+ segfault:
+       force_sig(SIGSEGV, current);
+       return 0;
+}
+
+long sys_rt_sigreturn(struct pt_regs regs)
+{
+       unsigned long __user sp = PT_REGS_SP(&current->thread.regs);
+       struct rt_sigframe __user *frame = (struct rt_sigframe *) (sp - 4);
+       sigset_t set;
+       struct ucontext __user *uc = &frame->uc;
+       int sig_size = _NSIG_WORDS * sizeof(unsigned long);
+
+       if(copy_from_user(&set, &uc->uc_sigmask, sig_size))
+               goto segfault;
+
+       sigdelsetmask(&set, ~_BLOCKABLE);
+
+       spin_lock_irq(&current->sighand->siglock);
+       current->blocked = set;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       if(copy_sc_from_user(&current->thread.regs, &uc->uc_mcontext))
+               goto segfault;
+
+       /* Avoid ERESTART handling */
+       PT_REGS_SYSCALL_NR(&current->thread.regs) = -1;
+       return(PT_REGS_SYSCALL_RET(&current->thread.regs));
+
+ segfault:
+       force_sig(SIGSEGV, current);
+       return 0;
+}
+
+/*
+ * 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/sys-x86_64/Makefile b/arch/um/sys-x86_64/Makefile
new file mode 100644 (file)
index 0000000..1ec504b
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# Copyright 2003 PathScale, Inc.
+#
+# Licensed under the GPL
+#
+
+lib-y = bitops.o bugs.o csum-partial.o delay.o fault.o mem.o memcpy.o \
+       ptrace.o ptrace_user.o semaphore.o sigcontext.o signal.o \
+       syscalls.o sysrq.o thunk.o
+
+USER_OBJS := ptrace_user.o sigcontext.o
+USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file))
+
+SYMLINKS = bitops.c csum-copy.S csum-partial.c csum-wrappers.c memcpy.S \
+       semaphore.c thunk.S
+SYMLINKS := $(foreach f,$(SYMLINKS),$(src)/$f)
+
+clean-files := $(SYMLINKS)
+
+bitops.c-dir = lib
+csum-copy.S-dir = lib
+csum-partial.c-dir = lib
+csum-wrappers.c-dir = lib
+memcpy.S-dir = lib
+semaphore.c-dir = kernel
+thunk.S-dir = lib
+
+define make_link
+       -rm -f $1
+       ln -sf $(TOPDIR)/arch/x86_64/$($(notdir $1)-dir)/$(notdir $1) $1
+endef
+
+$(SYMLINKS):
+       $(call make_link,$@)
+
+$(USER_OBJS) : %.o: %.c
+       $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $<
+
+CFLAGS_csum-partial.o := -Dcsum_partial=arch_csum_partial
diff --git a/arch/um/sys-x86_64/bugs.c b/arch/um/sys-x86_64/bugs.c
new file mode 100644 (file)
index 0000000..fdce7ea
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#include "linux/sched.h"
+#include "linux/errno.h"
+#include "asm/system.h"
+#include "asm/pda.h"
+#include "sysdep/ptrace.h"
+#include "os.h"
+
+void arch_init_thread(void)
+{
+}
+
+void arch_check_bugs(void)
+{
+}
+
+int arch_handle_signal(int sig, union uml_pt_regs *regs)
+{
+       return(0);
+}
+
+#define MAXTOKEN 64
+
+/* Set during early boot */
+int host_has_cmov = 1;
+int host_has_xmm = 0;
+
+static char token(int fd, char *buf, int len, char stop)
+{
+       int n;
+       char *ptr, *end, c;
+
+       ptr = buf;
+       end = &buf[len];
+       do {
+               n = os_read_file(fd, ptr, sizeof(*ptr));
+               c = *ptr++;
+               if(n != sizeof(*ptr)){
+                       if(n == 0) return(0);
+                       printk("Reading /proc/cpuinfo failed, err = %d\n", -n);
+                       if(n < 0)
+                               return(n);
+                       else
+                               return(-EIO);
+               }
+       } while((c != '\n') && (c != stop) && (ptr < end));
+
+       if(ptr == end){
+               printk("Failed to find '%c' in /proc/cpuinfo\n", stop);
+               return(-1);
+       }
+       *(ptr - 1) = '\0';
+       return(c);
+}
+
+static int find_cpuinfo_line(int fd, char *key, char *scratch, int len)
+{
+       int n;
+       char c;
+
+       scratch[len - 1] = '\0';
+       while(1){
+               c = token(fd, scratch, len - 1, ':');
+               if(c <= 0)
+                       return(0);
+               else if(c != ':'){
+                       printk("Failed to find ':' in /proc/cpuinfo\n");
+                       return(0);
+               }
+
+               if(!strncmp(scratch, key, strlen(key)))
+                       return(1);
+
+               do {
+                       n = os_read_file(fd, &c, sizeof(c));
+                       if(n != sizeof(c)){
+                               printk("Failed to find newline in "
+                                      "/proc/cpuinfo, err = %d\n", -n);
+                               return(0);
+                       }
+               } while(c != '\n');
+       }
+       return(0);
+}
+
+int cpu_feature(char *what, char *buf, int len)
+{
+       int fd, ret = 0;
+
+       fd = os_open_file("/proc/cpuinfo", of_read(OPENFLAGS()), 0);
+       if(fd < 0){
+               printk("Couldn't open /proc/cpuinfo, err = %d\n", -fd);
+               return(0);
+       }
+
+       if(!find_cpuinfo_line(fd, what, buf, len)){
+               printk("Couldn't find '%s' line in /proc/cpuinfo\n", what);
+               goto out_close;
+       }
+
+       token(fd, buf, len, '\n');
+       ret = 1;
+
+ out_close:
+       os_close_file(fd);
+       return(ret);
+}
+
+/* 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/sys-x86_64/delay.c b/arch/um/sys-x86_64/delay.c
new file mode 100644 (file)
index 0000000..f3b5187
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ * Copied from arch/x86_64
+ *
+ * Licensed under the GPL
+ */
+
+#include "asm/processor.h"
+
+void __delay(unsigned long loops)
+{
+       unsigned long i;
+
+       for(i = 0; i < loops; i++) ;
+}
+
+/*
+ * 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/sys-x86_64/fault.c b/arch/um/sys-x86_64/fault.c
new file mode 100644 (file)
index 0000000..cee1513
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#include "user.h"
+
+int arch_fixup(unsigned long address, void *sc_ptr)
+{
+       /* XXX search_exception_tables() */
+       return(0);
+}
+
+/* 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/sys-x86_64/mem.c b/arch/um/sys-x86_64/mem.c
new file mode 100644 (file)
index 0000000..3f59a0a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#include "linux/mm.h"
+#include "asm/page.h"
+#include "asm/mman.h"
+
+unsigned long vm_stack_flags = __VM_STACK_FLAGS;
+unsigned long vm_stack_flags32 = __VM_STACK_FLAGS;
+unsigned long vm_data_default_flags = __VM_DATA_DEFAULT_FLAGS;
+unsigned long vm_data_default_flags32 = __VM_DATA_DEFAULT_FLAGS;
+unsigned long vm_force_exec32 = PROT_EXEC;
+
+/* 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/sys-x86_64/ptrace.c b/arch/um/sys-x86_64/ptrace.c
new file mode 100644 (file)
index 0000000..8c146b2
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#define __FRAME_OFFSETS
+#include "asm/ptrace.h"
+#include "linux/sched.h"
+#include "linux/errno.h"
+#include "asm/elf.h"
+
+/* XXX x86_64 */
+unsigned long not_ss;
+unsigned long not_ds;
+unsigned long not_es;
+
+#define SC_SS(r) (not_ss)
+#define SC_DS(r) (not_ds)
+#define SC_ES(r) (not_es)
+
+/* determines which flags the user has access to. */
+/* 1 = access 0 = no access */
+#define FLAG_MASK 0x44dd5UL
+
+int putreg(struct task_struct *child, int regno, unsigned long value)
+{
+       unsigned long tmp;
+
+#ifdef TIF_IA32
+       /* Some code in the 64bit emulation may not be 64bit clean.
+          Don't take any chances. */
+       if (test_tsk_thread_flag(child, TIF_IA32))
+               value &= 0xffffffff;
+#endif
+       switch (regno){
+       case FS:
+       case GS:
+       case DS:
+       case ES:
+       case SS:
+       case CS:
+               if (value && (value & 3) != 3)
+                       return -EIO;
+               value &= 0xffff;
+               break;
+
+       case FS_BASE:
+       case GS_BASE:
+               if (!((value >> 48) == 0 || (value >> 48) == 0xffff))
+                       return -EIO;
+               break;
+
+       case EFLAGS:
+               value &= FLAG_MASK;
+               tmp = PT_REGS_EFLAGS(&child->thread.regs) & ~FLAG_MASK;
+               value |= tmp;
+               break;
+       }
+
+       PT_REGS_SET(&child->thread.regs, regno, value);
+       return 0;
+}
+
+unsigned long getreg(struct task_struct *child, int regno)
+{
+       unsigned long retval = ~0UL;
+       switch (regno) {
+       case FS:
+       case GS:
+       case DS:
+       case ES:
+       case SS:
+       case CS:
+               retval = 0xffff;
+               /* fall through */
+       default:
+               retval &= PT_REG(&child->thread.regs, regno);
+#ifdef TIF_IA32
+               if (test_tsk_thread_flag(child, TIF_IA32))
+                       retval &= 0xffffffff;
+#endif
+       }
+       return retval;
+}
+
+void arch_switch(void)
+{
+/* XXX
+       printk("arch_switch\n");
+*/
+}
+
+int is_syscall(unsigned long addr)
+{
+       panic("is_syscall");
+}
+
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu )
+{
+       panic("dump_fpu");
+       return(1);
+}
+
+int get_fpregs(unsigned long buf, struct task_struct *child)
+{
+       panic("get_fpregs");
+       return(0);
+}
+
+int set_fpregs(unsigned long buf, struct task_struct *child)
+{
+       panic("set_fpregs");
+       return(0);
+}
+
+int get_fpxregs(unsigned long buf, struct task_struct *tsk)
+{
+       panic("get_fpxregs");
+       return(0);
+}
+
+int set_fpxregs(unsigned long buf, struct task_struct *tsk)
+{
+       panic("set_fxpregs");
+       return(0);
+}
+
+/*
+ * 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/sys-x86_64/ptrace_user.c b/arch/um/sys-x86_64/ptrace_user.c
new file mode 100644 (file)
index 0000000..e1f8bac
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#include <stddef.h>
+#include <errno.h>
+#define __FRAME_OFFSETS
+#include <sys/ptrace.h>
+#include <asm/ptrace.h>
+#include "user.h"
+#include "kern_constants.h"
+
+int ptrace_getregs(long pid, unsigned long *regs_out)
+{
+       if(ptrace(PTRACE_GETREGS, pid, 0, regs_out) < 0)
+               return(-errno);
+       return(0);
+}
+
+int ptrace_setregs(long pid, unsigned long *regs)
+{
+       if(ptrace(PTRACE_SETREGS, pid, 0, regs) < 0)
+               return(-errno);
+       return(0);
+}
+
+void ptrace_pokeuser(unsigned long addr, unsigned long data)
+{
+       panic("ptrace_pokeuser");
+}
+
+#define DS 184
+#define ES 192
+#define __USER_DS     0x2b
+
+void arch_enter_kernel(void *task, int pid)
+{
+}
+
+void arch_leave_kernel(void *task, int pid)
+{
+#ifdef UM_USER_CS
+       if(ptrace(PTRACE_POKEUSER, pid, CS, UM_USER_CS) < 0)
+               tracer_panic("POKEUSER CS failed");
+#endif
+
+       if(ptrace(PTRACE_POKEUSER, pid, DS, __USER_DS) < 0)
+               tracer_panic("POKEUSER DS failed");
+       if(ptrace(PTRACE_POKEUSER, pid, ES, __USER_DS) < 0)
+               tracer_panic("POKEUSER ES failed");
+}
+
+/*
+ * 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/sys-x86_64/sigcontext.c b/arch/um/sys-x86_64/sigcontext.c
new file mode 100644 (file)
index 0000000..c88e64d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include "user.h"
+
+void sc_to_sc(void *to_ptr, void *from_ptr)
+{
+        struct sigcontext *to = to_ptr, *from = from_ptr;
+        int size = sizeof(*to); /* + sizeof(struct _fpstate); */
+
+        memcpy(to, from, size);
+        if(from->fpstate != NULL)
+               to->fpstate = (struct _fpstate *) (to + 1);
+
+       to->fpstate = NULL;
+}
+
+unsigned long *sc_sigmask(void *sc_ptr)
+{
+       struct sigcontext *sc = sc_ptr;
+
+       return(&sc->oldmask);
+}
+
+/* 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/sys-x86_64/signal.c b/arch/um/sys-x86_64/signal.c
new file mode 100644 (file)
index 0000000..a5682f1
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2003 PathScale, Inc.
+ * Licensed under the GPL
+ */
+
+#include "linux/stddef.h"
+#include "linux/errno.h"
+#include "linux/personality.h"
+#include "linux/ptrace.h"
+#include "asm/current.h"
+#include "asm/uaccess.h"
+#include "asm/sigcontext.h"
+#include "asm/ptrace.h"
+#include "asm/arch/ucontext.h"
+#include "choose-mode.h"
+#include "sysdep/ptrace.h"
+#include "frame_kern.h"
+
+#ifdef CONFIG_MODE_SKAS
+
+#include "skas.h"
+
+static int copy_sc_from_user_skas(struct pt_regs *regs,
+                                 struct sigcontext *from)
+{
+       int err = 0;
+
+#define GETREG(regs, regno, sc, regname) \
+       __get_user((regs)->regs.skas.regs[(regno) / sizeof(unsigned long)], \
+                  &(sc)->regname)
+
+       err |= GETREG(regs, R8, from, r8);
+       err |= GETREG(regs, R9, from, r9);
+       err |= GETREG(regs, R10, from, r10);
+       err |= GETREG(regs, R11, from, r11);
+       err |= GETREG(regs, R12, from, r12);
+       err |= GETREG(regs, R13, from, r13);
+       err |= GETREG(regs, R14, from, r14);
+       err |= GETREG(regs, R15, from, r15);
+       err |= GETREG(regs, RDI, from, rdi);
+       err |= GETREG(regs, RSI, from, rsi);
+       err |= GETREG(regs, RBP, from, rbp);
+       err |= GETREG(regs, RBX, from, rbx);
+       err |= GETREG(regs, RDX, from, rdx);
+       err |= GETREG(regs, RAX, from, rax);
+       err |= GETREG(regs, RCX, from, rcx);
+       err |= GETREG(regs, RSP, from, rsp);
+       err |= GETREG(regs, RIP, from, rip);
+       err |= GETREG(regs, EFLAGS, from, eflags);
+       err |= GETREG(regs, CS, from, cs);
+
+#undef GETREG
+
+       return(err);
+}
+
+int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp,
+                        struct pt_regs *regs, unsigned long mask)
+{
+       unsigned long eflags;
+       int err = 0;
+
+       err |= __put_user(0, &to->gs);
+       err |= __put_user(0, &to->fs);
+
+#define PUTREG(regs, regno, sc, regname) \
+       __put_user((regs)->regs.skas.regs[(regno) / sizeof(unsigned long)], \
+                  &(sc)->regname)
+
+       err |= PUTREG(regs, RDI, to, rdi);
+       err |= PUTREG(regs, RSI, to, rsi);
+       err |= PUTREG(regs, RBP, to, rbp);
+       err |= PUTREG(regs, RSP, to, rsp);
+       err |= PUTREG(regs, RBX, to, rbx);
+       err |= PUTREG(regs, RDX, to, rdx);
+       err |= PUTREG(regs, RCX, to, rcx);
+       err |= PUTREG(regs, RAX, to, rax);
+       err |= PUTREG(regs, R8, to, r8);
+       err |= PUTREG(regs, R9, to, r9);
+       err |= PUTREG(regs, R10, to, r10);
+       err |= PUTREG(regs, R11, to, r11);
+       err |= PUTREG(regs, R12, to, r12);
+       err |= PUTREG(regs, R13, to, r13);
+       err |= PUTREG(regs, R14, to, r14);
+       err |= PUTREG(regs, R15, to, r15);
+       err |= PUTREG(regs, CS, to, cs); /* XXX x86_64 doesn't do this */
+       err |= __put_user(current->thread.err, &to->err);
+       err |= __put_user(current->thread.trap_no, &to->trapno);
+       err |= PUTREG(regs, RIP, to, rip);
+       err |= PUTREG(regs, EFLAGS, to, eflags);
+#undef PUTREG
+
+       err |= __put_user(mask, &to->oldmask);
+       err |= __put_user(current->thread.cr2, &to->cr2);
+
+       return(err);
+}
+
+#endif
+
+#ifdef CONFIG_MODE_TT
+int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from,
+                        int fpsize)
+{
+       struct _fpstate *to_fp, *from_fp;
+       unsigned long sigs;
+       int err;
+
+       to_fp = to->fpstate;
+       from_fp = from->fpstate;
+       sigs = to->oldmask;
+       err = copy_from_user(to, from, sizeof(*to));
+       to->oldmask = sigs;
+       return(err);
+}
+
+int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp,
+                      struct sigcontext *from, int fpsize)
+{
+       struct _fpstate *to_fp, *from_fp;
+       int err;
+
+       to_fp = (fp ? fp : (struct _fpstate *) (to + 1));
+       from_fp = from->fpstate;
+       err = copy_to_user(to, from, sizeof(*to));
+       return(err);
+}
+
+#endif
+
+static int copy_sc_from_user(struct pt_regs *to, void __user *from)
+{
+       int ret;
+
+       ret = CHOOSE_MODE(copy_sc_from_user_tt(UPT_SC(&to->regs), from,
+                                              sizeof(struct _fpstate)),
+                         copy_sc_from_user_skas(to, from));
+       return(ret);
+}
+
+static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp,
+                          struct pt_regs *from, unsigned long mask)
+{
+       return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs),
+                                             sizeof(*fp)),
+                          copy_sc_to_user_skas(to, fp, from, mask)));
+}
+
+struct rt_sigframe
+{
+       char *pretcode;
+       struct ucontext uc;
+       struct siginfo info;
+};
+
+#define round_down(m, n) (((m) / (n)) * (n))
+
+int setup_signal_stack_si(unsigned long stack_top, int sig,
+                         struct k_sigaction *ka, struct pt_regs * regs,
+                         siginfo_t *info, sigset_t *set)
+{
+       struct rt_sigframe __user *frame;
+       struct _fpstate __user *fp = NULL;
+       int err = 0;
+       struct task_struct *me = current;
+
+       frame = (struct rt_sigframe __user *)
+               round_down(stack_top - sizeof(struct rt_sigframe), 16) - 8;
+       frame -= 128;
+
+       if (!access_ok(VERIFY_WRITE, fp, sizeof(struct _fpstate)))
+               goto out;
+
+#if 0 /* XXX */
+       if (save_i387(fp) < 0)
+               err |= -1;
+#endif
+       if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+               goto out;
+
+       if (ka->sa.sa_flags & SA_SIGINFO) {
+               err |= copy_siginfo_to_user(&frame->info, info);
+               if (err)
+                       goto out;
+       }
+
+       /* Create the ucontext.  */
+       err |= __put_user(0, &frame->uc.uc_flags);
+       err |= __put_user(0, &frame->uc.uc_link);
+       err |= __put_user(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
+       err |= __put_user(sas_ss_flags(PT_REGS_SP(regs)),
+                         &frame->uc.uc_stack.ss_flags);
+       err |= __put_user(me->sas_ss_size, &frame->uc.uc_stack.ss_size);
+       err |= copy_sc_to_user(&frame->uc.uc_mcontext, fp, regs, set->sig[0]);
+       err |= __put_user(fp, &frame->uc.uc_mcontext.fpstate);
+       if (sizeof(*set) == 16) {
+               __put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]);
+               __put_user(set->sig[1], &frame->uc.uc_sigmask.sig[1]);
+       }
+       else
+               err |= __copy_to_user(&frame->uc.uc_sigmask, set,
+                                     sizeof(*set));
+
+       /* Set up to return from userspace.  If provided, use a stub
+          already in userspace.  */
+       /* x86-64 should always use SA_RESTORER. */
+       if (ka->sa.sa_flags & SA_RESTORER)
+               err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
+       else
+               /* could use a vstub here */
+               goto out;
+
+       if (err)
+               goto out;
+
+       /* Set up registers for signal handler */
+       {
+               struct exec_domain *ed = current_thread_info()->exec_domain;
+               if (unlikely(ed && ed->signal_invmap && sig < 32))
+                       sig = ed->signal_invmap[sig];
+       }
+
+       PT_REGS_RDI(regs) = sig;
+       /* In case the signal handler was declared without prototypes */
+       PT_REGS_RAX(regs) = 0;
+
+       /* This also works for non SA_SIGINFO handlers because they expect the
+          next argument after the signal number on the stack. */
+       PT_REGS_RSI(regs) = (unsigned long) &frame->info;
+       PT_REGS_RDX(regs) = (unsigned long) &frame->uc;
+       PT_REGS_RIP(regs) = (unsigned long) ka->sa.sa_handler;
+
+       PT_REGS_RSP(regs) = (unsigned long) frame;
+ out:
+       return(err);
+}
+
+long sys_rt_sigreturn(struct pt_regs *regs)
+{
+       unsigned long __user sp = PT_REGS_SP(&current->thread.regs);
+       struct rt_sigframe __user *frame =
+               (struct rt_sigframe __user *)(sp - 8);
+       struct ucontext __user *uc = &frame->uc;
+       sigset_t set;
+
+       if(copy_from_user(&set, &uc->uc_sigmask, sizeof(set)))
+               goto segfault;
+
+       sigdelsetmask(&set, ~_BLOCKABLE);
+
+       spin_lock_irq(&current->sighand->siglock);
+       current->blocked = set;
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+
+       if(copy_sc_from_user(&current->thread.regs, &uc->uc_mcontext))
+               goto segfault;
+
+       /* Avoid ERESTART handling */
+       PT_REGS_SYSCALL_NR(&current->thread.regs) = -1;
+       return(PT_REGS_SYSCALL_RET(&current->thread.regs));
+
+ segfault:
+       force_sig(SIGSEGV, current);
+       return 0;
+}
+/*
+ * 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/sys-x86_64/syscalls.c b/arch/um/sys-x86_64/syscalls.c
new file mode 100644 (file)
index 0000000..4c61dcc
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#include "linux/linkage.h"
+#include "linux/slab.h"
+#include "linux/shm.h"
+#include "asm/uaccess.h"
+#define __FRAME_OFFSETS
+#include "asm/ptrace.h"
+#include "asm/unistd.h"
+#include "asm/prctl.h" /* XXX This should get the constants from libc */
+#include "choose-mode.h"
+
+asmlinkage long wrap_sys_shmat(int shmid, char __user *shmaddr, int shmflg)
+{
+       unsigned long raddr;
+
+       return do_shmat(shmid, shmaddr, shmflg, &raddr) ?: (long) raddr;
+}
+
+#ifdef CONFIG_MODE_TT
+extern int modify_ldt(int func, void *ptr, unsigned long bytecount);
+
+long sys_modify_ldt_tt(int func, void *ptr, unsigned long bytecount)
+{
+       /* XXX This should check VERIFY_WRITE depending on func, check this
+        * in i386 as well.
+        */
+       if(verify_area(VERIFY_READ, ptr, bytecount))
+               return(-EFAULT);
+       return(modify_ldt(func, ptr, bytecount));
+}
+#endif
+
+#ifdef CONFIG_MODE_SKAS
+extern int userspace_pid;
+
+#ifndef __NR_mm_indirect
+#define __NR_mm_indirect 241
+#endif
+
+long sys_modify_ldt_skas(int func, void *ptr, unsigned long bytecount)
+{
+       unsigned long args[6];
+        void *buf;
+        int res, n;
+
+        buf = kmalloc(bytecount, GFP_KERNEL);
+        if(buf == NULL)
+                return(-ENOMEM);
+
+        res = 0;
+
+        switch(func){
+        case 1:
+        case 0x11:
+                res = copy_from_user(buf, ptr, bytecount);
+                break;
+        }
+
+        if(res != 0){
+                res = -EFAULT;
+                goto out;
+        }
+
+       args[0] = func;
+       args[1] = (unsigned long) buf;
+       args[2] = bytecount;
+       res = syscall(__NR_mm_indirect, &current->mm->context.u,
+                     __NR_modify_ldt, args);
+
+        if(res < 0)
+                goto out;
+
+        switch(func){
+        case 0:
+        case 2:
+                n = res;
+                res = copy_to_user(ptr, buf, n);
+                if(res != 0)
+                        res = -EFAULT;
+                else
+                        res = n;
+                break;
+        }
+
+ out:
+        kfree(buf);
+        return(res);
+}
+#endif
+
+long sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
+{
+        return(CHOOSE_MODE_PROC(sys_modify_ldt_tt, sys_modify_ldt_skas, func,
+                                ptr, bytecount));
+}
+
+#ifdef CONFIG_MODE_TT
+extern long arch_prctl(int code, unsigned long addr);
+
+static long arch_prctl_tt(int code, unsigned long addr)
+{
+       unsigned long tmp;
+       long ret;
+
+       switch(code){
+       case ARCH_SET_GS:
+       case ARCH_SET_FS:
+               ret = arch_prctl(code, addr);
+               break;
+       case ARCH_GET_FS:
+       case ARCH_GET_GS:
+               ret = arch_prctl(code, (unsigned long) &tmp);
+               if(!ret)
+                       ret = put_user(tmp, &addr);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return(ret);
+}
+#endif
+
+#ifdef CONFIG_MODE_SKAS
+
+static long arch_prctl_skas(int code, unsigned long addr)
+{
+       long ret = 0;
+
+       switch(code){
+       case ARCH_SET_GS:
+               current->thread.regs.regs.skas.regs[GS_BASE / sizeof(unsigned long)] = addr;
+               break;
+       case ARCH_SET_FS:
+               current->thread.regs.regs.skas.regs[FS_BASE / sizeof(unsigned long)] = addr;
+               break;
+       case ARCH_GET_FS:
+               ret = put_user(current->thread.regs.regs.skas.regs[GS / sizeof(unsigned long)], &addr);
+               break;
+       case ARCH_GET_GS:
+               ret = put_user(current->thread.regs.regs.skas.regs[FS / sizeof(unsigned \
+long)], &addr);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return(ret);
+}
+#endif
+
+long sys_arch_prctl(int code, unsigned long addr)
+{
+       return(CHOOSE_MODE_PROC(arch_prctl_tt, arch_prctl_skas, code, addr));
+}
+
+long sys_clone(unsigned long clone_flags, unsigned long newsp,
+              void __user *parent_tid, void __user *child_tid)
+{
+       long ret;
+
+       /* XXX: normal arch do here this pass, and also pass the regs to
+        * do_fork, instead of NULL. Currently the arch-independent code
+        * ignores these values, while the UML code (actually it's
+        * copy_thread) does the right thing. But this should change,
+        probably. */
+       /*if (!newsp)
+               newsp = UPT_SP(current->thread.regs);*/
+       current->thread.forking = 1;
+       ret = do_fork(clone_flags, newsp, NULL, 0, parent_tid, child_tid);
+       current->thread.forking = 0;
+       return(ret);
+}
+
+/*
+ * 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/sys-x86_64/sysrq.c b/arch/um/sys-x86_64/sysrq.c
new file mode 100644 (file)
index 0000000..ddf7469
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#include "linux/kernel.h"
+#include "linux/utsname.h"
+#include "linux/module.h"
+#include "asm/current.h"
+#include "asm/ptrace.h"
+#include "sysrq.h"
+
+void __show_regs(struct pt_regs * regs)
+{
+       printk("\n");
+       print_modules();
+       printk("Pid: %d, comm: %.20s %s %s\n",
+              current->pid, current->comm, print_tainted(), system_utsname.release);
+       printk("RIP: %04lx:[<%016lx>] ", PT_REGS_CS(regs) & 0xffff,
+              PT_REGS_RIP(regs));
+       printk("\nRSP: %016lx  EFLAGS: %08lx\n", PT_REGS_RSP(regs),
+              PT_REGS_EFLAGS(regs));
+       printk("RAX: %016lx RBX: %016lx RCX: %016lx\n",
+              PT_REGS_RAX(regs), PT_REGS_RBX(regs), PT_REGS_RCX(regs));
+       printk("RDX: %016lx RSI: %016lx RDI: %016lx\n",
+              PT_REGS_RDX(regs), PT_REGS_RSI(regs), PT_REGS_RDI(regs));
+       printk("RBP: %016lx R08: %016lx R09: %016lx\n",
+              PT_REGS_RBP(regs), PT_REGS_R8(regs), PT_REGS_R9(regs));
+       printk("R10: %016lx R11: %016lx R12: %016lx\n",
+              PT_REGS_R10(regs), PT_REGS_R11(regs), PT_REGS_R12(regs));
+       printk("R13: %016lx R14: %016lx R15: %016lx\n",
+              PT_REGS_R13(regs), PT_REGS_R14(regs), PT_REGS_R15(regs));
+}
+
+void show_regs(struct pt_regs *regs)
+{
+       __show_regs(regs);
+       show_trace((unsigned long *) &regs);
+}
+
+/* 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/sys-x86_64/util/Makefile b/arch/um/sys-x86_64/util/Makefile
new file mode 100644 (file)
index 0000000..0026079
--- /dev/null
@@ -0,0 +1,10 @@
+# Copyright 2003 - 2004 Pathscale, Inc
+# Released under the GPL
+
+hostprogs-y    := mk_sc mk_thread
+always         := $(hostprogs-y)
+
+mk_thread-objs := mk_thread_kern.o mk_thread_user.o
+
+HOSTCFLAGS_mk_thread_kern.o    := $(CFLAGS) $(CPPFLAGS)
+HOSTCFLAGS_mk_thread_user.o    := $(USER_CFLAGS)
diff --git a/arch/um/sys-x86_64/util/mk_sc.c b/arch/um/sys-x86_64/util/mk_sc.c
new file mode 100644 (file)
index 0000000..c236e21
--- /dev/null
@@ -0,0 +1,58 @@
+/* Copyright (C) 2003 - 2004 PathScale, Inc
+ * Released under the GPL
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <linux/stddef.h>
+
+#define SC_OFFSET(name, field) \
+  printf("#define " name \
+        "(sc) *((unsigned long *) &(((char *) (sc))[%ld]))\n",\
+        offsetof(struct sigcontext, field))
+
+#define SC_FP_OFFSET(name, field) \
+  printf("#define " name \
+        "(sc) *((unsigned long *) &(((char *) (SC_FPSTATE(sc)))[%ld]))\n",\
+        offsetof(struct _fpstate, field))
+
+#define SC_FP_OFFSET_PTR(name, field, type) \
+  printf("#define " name \
+        "(sc) ((" type " *) &(((char *) (SC_FPSTATE(sc)))[%d]))\n",\
+        offsetof(struct _fpstate, field))
+
+int main(int argc, char **argv)
+{
+  SC_OFFSET("SC_RBX", rbx);
+  SC_OFFSET("SC_RCX", rcx);
+  SC_OFFSET("SC_RDX", rdx);
+  SC_OFFSET("SC_RSI", rsi);
+  SC_OFFSET("SC_RDI", rdi);
+  SC_OFFSET("SC_RBP", rbp);
+  SC_OFFSET("SC_RAX", rax);
+  SC_OFFSET("SC_R8", r8);
+  SC_OFFSET("SC_R9", r9);
+  SC_OFFSET("SC_R10", r10);
+  SC_OFFSET("SC_R11", r11);
+  SC_OFFSET("SC_R12", r12);
+  SC_OFFSET("SC_R13", r13);
+  SC_OFFSET("SC_R14", r14);
+  SC_OFFSET("SC_R15", r15);
+  SC_OFFSET("SC_IP", rip);
+  SC_OFFSET("SC_SP", rsp);
+  SC_OFFSET("SC_CR2", cr2);
+  SC_OFFSET("SC_ERR", err);
+  SC_OFFSET("SC_TRAPNO", trapno);
+  SC_OFFSET("SC_CS", cs);
+  SC_OFFSET("SC_FS", fs);
+  SC_OFFSET("SC_GS", gs);
+  SC_OFFSET("SC_EFLAGS", eflags);
+  SC_OFFSET("SC_SIGMASK", oldmask);
+#if 0
+  SC_OFFSET("SC_ORIG_RAX", orig_rax);
+  SC_OFFSET("SC_DS", ds);
+  SC_OFFSET("SC_ES", es);
+  SC_OFFSET("SC_SS", ss);
+#endif
+  return(0);
+}
diff --git a/arch/um/sys-x86_64/util/mk_thread_kern.c b/arch/um/sys-x86_64/util/mk_thread_kern.c
new file mode 100644 (file)
index 0000000..a281673
--- /dev/null
@@ -0,0 +1,21 @@
+#include "linux/config.h"
+#include "linux/stddef.h"
+#include "linux/sched.h"
+
+extern void print_head(void);
+extern void print_constant_ptr(char *name, int value);
+extern void print_constant(char *name, char *type, int value);
+extern void print_tail(void);
+
+#define THREAD_OFFSET(field) offsetof(struct task_struct, thread.field)
+
+int main(int argc, char **argv)
+{
+  print_head();
+#ifdef CONFIG_MODE_TT
+  print_constant("TASK_EXTERN_PID", "int", THREAD_OFFSET(mode.tt.extern_pid));
+#endif
+  print_tail();
+  return(0);
+}
+
diff --git a/arch/um/sys-x86_64/util/mk_thread_user.c b/arch/um/sys-x86_64/util/mk_thread_user.c
new file mode 100644 (file)
index 0000000..7989725
--- /dev/null
@@ -0,0 +1,30 @@
+#include <stdio.h>
+
+void print_head(void)
+{
+  printf("/*\n");
+  printf(" * Generated by mk_thread\n");
+  printf(" */\n");
+  printf("\n");
+  printf("#ifndef __UM_THREAD_H\n");
+  printf("#define __UM_THREAD_H\n");
+  printf("\n");
+}
+
+void print_constant_ptr(char *name, int value)
+{
+  printf("#define %s(task) ((unsigned long *) "
+        "&(((char *) (task))[%d]))\n", name, value);
+}
+
+void print_constant(char *name, char *type, int value)
+{
+  printf("#define %s(task) *((%s *) &(((char *) (task))[%d]))\n", name, type,
+        value);
+}
+
+void print_tail(void)
+{
+  printf("\n");
+  printf("#endif\n");
+}
diff --git a/arch/x86_64/kernel/genapic.c b/arch/x86_64/kernel/genapic.c
new file mode 100644 (file)
index 0000000..9aa5cb6
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2004 James Cleverdon, IBM.
+ * Subject to the GNU Public License, v.2
+ *
+ * Generic APIC sub-arch probe layer.
+ *
+ * Hacked for x86-64 by James Cleverdon from i386 architecture code by
+ * Martin Bligh, Andi Kleen, James Bottomley, John Stultz, and
+ * James Cleverdon.
+ */
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <asm/smp.h>
+#include <asm/ipi.h>
+
+/* which logical CPU number maps to which CPU (physical APIC ID) */
+u8 x86_cpu_to_apicid[NR_CPUS] = { [0 ... NR_CPUS-1] = BAD_APICID };
+EXPORT_SYMBOL(x86_cpu_to_apicid);
+u8 x86_cpu_to_log_apicid[NR_CPUS] = { [0 ... NR_CPUS-1] = BAD_APICID };
+
+extern struct genapic apic_cluster;
+extern struct genapic apic_flat;
+
+struct genapic *genapic = &apic_flat;
+
+
+/*
+ * Check the APIC IDs in bios_cpu_apicid and choose the APIC mode.
+ */
+void __init clustered_apic_check(void)
+{
+       long i;
+       u8 clusters, max_cluster;
+       u8 id;
+       u8 cluster_cnt[NUM_APIC_CLUSTERS];
+
+       memset(cluster_cnt, 0, sizeof(cluster_cnt));
+
+       for (i = 0; i < NR_CPUS; i++) {
+               id = bios_cpu_apicid[i];
+               if (id != BAD_APICID)
+                       cluster_cnt[APIC_CLUSTERID(id)]++;
+       }
+
+       clusters = 0;
+       max_cluster = 0;
+       for (i = 0; i < NUM_APIC_CLUSTERS; i++) {
+               if (cluster_cnt[i] > 0) {
+                       ++clusters;
+                       if (cluster_cnt[i] > max_cluster)
+                               max_cluster = cluster_cnt[i];
+               }
+       }
+
+       /*
+        * If we have clusters <= 1 and CPUs <= 8 in cluster 0, then flat mode,
+        * else if max_cluster <= 4 and cluster_cnt[15] == 0, clustered logical
+        * else physical mode.
+        * (We don't use lowest priority delivery + HW APIC IRQ steering, so
+        * can ignore the clustered logical case and go straight to physical.)
+        */
+       if (clusters <= 1 && max_cluster <= 8 && cluster_cnt[0] == max_cluster)
+               genapic = &apic_flat;
+       else
+               genapic = &apic_cluster;
+
+       printk(KERN_INFO "Setting APIC routing to %s\n", genapic->name);
+}
+
+/* Same for both flat and clustered. */
+
+void send_IPI_self(int vector)
+{
+       __send_IPI_shortcut(APIC_DEST_SELF, vector, APIC_DEST_PHYSICAL);
+}
diff --git a/arch/x86_64/kernel/genapic_cluster.c b/arch/x86_64/kernel/genapic_cluster.c
new file mode 100644 (file)
index 0000000..9703da7
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2004 James Cleverdon, IBM.
+ * Subject to the GNU Public License, v.2
+ *
+ * Clustered APIC subarch code.  Up to 255 CPUs, physical delivery.
+ * (A more realistic maximum is around 230 CPUs.)
+ *
+ * Hacked for x86-64 by James Cleverdon from i386 architecture code by
+ * Martin Bligh, Andi Kleen, James Bottomley, John Stultz, and
+ * James Cleverdon.
+ */
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <asm/smp.h>
+#include <asm/ipi.h>
+
+
+/*
+ * Set up the logical destination ID.
+ *
+ * Intel recommends to set DFR, LDR and TPR before enabling
+ * an APIC.  See e.g. "AP-388 82489DX User's Manual" (Intel
+ * document number 292116).  So here it goes...
+ */
+static void cluster_init_apic_ldr(void)
+{
+       unsigned long val, id;
+       long i, count;
+       u8 lid;
+       u8 my_id = hard_smp_processor_id();
+       u8 my_cluster = APIC_CLUSTER(my_id);
+
+       /* Create logical APIC IDs by counting CPUs already in cluster. */
+       for (count = 0, i = NR_CPUS; --i >= 0; ) {
+               lid = x86_cpu_to_log_apicid[i];
+               if (lid != BAD_APICID && APIC_CLUSTER(lid) == my_cluster)
+                       ++count;
+       }
+       /*
+        * We only have a 4 wide bitmap in cluster mode.  There's no way
+        * to get above 60 CPUs and still give each one it's own bit.
+        * But, we're using physical IRQ delivery, so we don't care.
+        * Use bit 3 for the 4th through Nth CPU in each cluster.
+        */
+       if (count >= XAPIC_DEST_CPUS_SHIFT)
+               count = 3;
+       id = my_cluster | (1UL << count);
+       x86_cpu_to_log_apicid[smp_processor_id()] = id;
+       apic_write_around(APIC_DFR, APIC_DFR_CLUSTER);
+       val = apic_read(APIC_LDR) & ~APIC_LDR_MASK;
+       val |= SET_APIC_LOGICAL_ID(id);
+       apic_write_around(APIC_LDR, val);
+}
+
+/* Start with all IRQs pointing to boot CPU.  IRQ balancing will shift them. */
+
+static cpumask_t cluster_target_cpus(void)
+{
+       return cpumask_of_cpu(0);
+}
+
+static void cluster_send_IPI_mask(cpumask_t mask, int vector)
+{
+       send_IPI_mask_sequence(mask, vector);
+}
+
+static void cluster_send_IPI_allbutself(int vector)
+{
+       cpumask_t mask = cpu_online_map;
+       cpu_clear(smp_processor_id(), mask);
+
+       if (!cpus_empty(mask))
+               cluster_send_IPI_mask(mask, vector);
+}
+
+static void cluster_send_IPI_all(int vector)
+{
+       cluster_send_IPI_mask(cpu_online_map, vector);
+}
+
+static int cluster_apic_id_registered(void)
+{
+       return 1;
+}
+
+static unsigned int cluster_cpu_mask_to_apicid(cpumask_t cpumask)
+{
+       int cpu;
+
+       /*
+        * We're using fixed IRQ delivery, can only return one phys APIC ID.
+        * May as well be the first.
+        */
+       cpu = first_cpu(cpumask);
+       if ((unsigned)cpu < NR_CPUS)
+               return x86_cpu_to_apicid[cpu];
+       else
+               return BAD_APICID;
+}
+
+/* cpuid returns the value latched in the HW at reset, not the APIC ID
+ * register's value.  For any box whose BIOS changes APIC IDs, like
+ * clustered APIC systems, we must use hard_smp_processor_id.
+ *
+ * See Intel's IA-32 SW Dev's Manual Vol2 under CPUID.
+ */
+static unsigned int phys_pkg_id(int index_msb)
+{
+       return hard_smp_processor_id() >> index_msb;
+}
+
+struct genapic apic_cluster = {
+       .name = "clustered",
+       .int_delivery_mode = dest_Fixed,
+       .int_dest_mode = (APIC_DEST_PHYSICAL != 0),
+       .int_delivery_dest = APIC_DEST_PHYSICAL | APIC_DM_FIXED,
+       .target_cpus = cluster_target_cpus,
+       .apic_id_registered = cluster_apic_id_registered,
+       .init_apic_ldr = cluster_init_apic_ldr,
+       .send_IPI_all = cluster_send_IPI_all,
+       .send_IPI_allbutself = cluster_send_IPI_allbutself,
+       .send_IPI_mask = cluster_send_IPI_mask,
+       .cpu_mask_to_apicid = cluster_cpu_mask_to_apicid,
+       .phys_pkg_id = phys_pkg_id,
+};
diff --git a/arch/x86_64/kernel/genapic_flat.c b/arch/x86_64/kernel/genapic_flat.c
new file mode 100644 (file)
index 0000000..b4cbbad
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2004 James Cleverdon, IBM.
+ * Subject to the GNU Public License, v.2
+ *
+ * Flat APIC subarch code.  Maximum 8 CPUs, logical delivery.
+ *
+ * Hacked for x86-64 by James Cleverdon from i386 architecture code by
+ * Martin Bligh, Andi Kleen, James Bottomley, John Stultz, and
+ * James Cleverdon.
+ */
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <asm/smp.h>
+#include <asm/ipi.h>
+
+
+static cpumask_t flat_target_cpus(void)
+{
+       return cpu_online_map;
+}
+
+/*
+ * Set up the logical destination ID.
+ *
+ * Intel recommends to set DFR, LDR and TPR before enabling
+ * an APIC.  See e.g. "AP-388 82489DX User's Manual" (Intel
+ * document number 292116).  So here it goes...
+ */
+static void flat_init_apic_ldr(void)
+{
+       unsigned long val;
+       unsigned long num, id;
+
+       num = smp_processor_id();
+       id = 1UL << num;
+       x86_cpu_to_log_apicid[num] = id;
+       apic_write_around(APIC_DFR, APIC_DFR_FLAT);
+       val = apic_read(APIC_LDR) & ~APIC_LDR_MASK;
+       val |= SET_APIC_LOGICAL_ID(id);
+       apic_write_around(APIC_LDR, val);
+}
+
+static void flat_send_IPI_allbutself(int vector)
+{
+       /*
+        * if there are no other CPUs in the system then
+        * we get an APIC send error if we try to broadcast.
+        * thus we have to avoid sending IPIs in this case.
+        */
+       if (num_online_cpus() > 1)
+               __send_IPI_shortcut(APIC_DEST_ALLBUT, vector, APIC_DEST_LOGICAL);
+}
+
+static void flat_send_IPI_all(int vector)
+{
+       __send_IPI_shortcut(APIC_DEST_ALLINC, vector, APIC_DEST_LOGICAL);
+}
+
+static void flat_send_IPI_mask(cpumask_t cpumask, int vector)
+{
+       unsigned long mask = cpus_addr(cpumask)[0];
+       unsigned long cfg;
+       unsigned long flags;
+
+       local_save_flags(flags);
+       local_irq_disable();
+
+       /*
+        * Wait for idle.
+        */
+       apic_wait_icr_idle();
+
+       /*
+        * prepare target chip field
+        */
+       cfg = __prepare_ICR2(mask);
+       apic_write_around(APIC_ICR2, cfg);
+
+       /*
+        * program the ICR
+        */
+       cfg = __prepare_ICR(0, vector, APIC_DEST_LOGICAL);
+
+       /*
+        * Send the IPI. The write to APIC_ICR fires this off.
+        */
+       apic_write_around(APIC_ICR, cfg);
+       local_irq_restore(flags);
+}
+
+static int flat_apic_id_registered(void)
+{
+       return physid_isset(GET_APIC_ID(apic_read(APIC_ID)), phys_cpu_present_map);
+}
+
+static unsigned int flat_cpu_mask_to_apicid(cpumask_t cpumask)
+{
+       return cpus_addr(cpumask)[0] & APIC_ALL_CPUS;
+}
+
+static unsigned int phys_pkg_id(int index_msb)
+{
+       u32 ebx;
+
+       ebx = cpuid_ebx(1);
+       return ((ebx >> 24) & 0xFF) >> index_msb;
+}
+
+struct genapic apic_flat =  {
+       .name = "flat",
+       .int_delivery_mode = dest_LowestPrio,
+       .int_dest_mode = (APIC_DEST_LOGICAL != 0),
+       .int_delivery_dest = APIC_DEST_LOGICAL | APIC_DM_LOWEST,
+       .target_cpus = flat_target_cpus,
+       .apic_id_registered = flat_apic_id_registered,
+       .init_apic_ldr = flat_init_apic_ldr,
+       .send_IPI_all = flat_send_IPI_all,
+       .send_IPI_allbutself = flat_send_IPI_allbutself,
+       .send_IPI_mask = flat_send_IPI_mask,
+       .cpu_mask_to_apicid = flat_cpu_mask_to_apicid,
+       .phys_pkg_id = phys_pkg_id,
+};
diff --git a/arch/x86_64/kernel/kprobes.c b/arch/x86_64/kernel/kprobes.c
new file mode 100644 (file)
index 0000000..dfef312
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ *  Kernel Probes (KProbes)
+ *  arch/x86_64/kernel/kprobes.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ *
+ * 2002-Oct    Created by Vamsi Krishna S <vamsi_krishna@in.ibm.com> Kernel
+ *             Probes initial implementation ( includes contributions from
+ *             Rusty Russell).
+ * 2004-July   Suparna Bhattacharya <suparna@in.ibm.com> added jumper probes
+ *             interface to access function arguments.
+ * 2004-Oct    Jim Keniston <kenistoj@us.ibm.com> and Prasanna S Panchamukhi
+ *             <prasanna@in.ibm.com> adapted for x86_64
+ */
+
+#include <linux/config.h>
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/preempt.h>
+#include <linux/vmalloc.h>
+
+#include <asm/pgtable.h>
+#include <asm/kdebug.h>
+
+/* kprobe_status settings */
+#define KPROBE_HIT_ACTIVE      0x00000001
+#define KPROBE_HIT_SS          0x00000002
+
+static struct kprobe *current_kprobe;
+static unsigned long kprobe_status, kprobe_old_rflags, kprobe_saved_rflags;
+static struct pt_regs jprobe_saved_regs;
+static long *jprobe_saved_rsp;
+static kprobe_opcode_t *get_insn_slot(void);
+static void free_insn_slot(kprobe_opcode_t *slot);
+void jprobe_return_end(void);
+
+/* copy of the kernel stack at the probe fire time */
+static kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE];
+
+/*
+ * returns non-zero if opcode modifies the interrupt flag.
+ */
+static inline int is_IF_modifier(kprobe_opcode_t *insn)
+{
+       switch (*insn) {
+       case 0xfa:              /* cli */
+       case 0xfb:              /* sti */
+       case 0xcf:              /* iret/iretd */
+       case 0x9d:              /* popf/popfd */
+               return 1;
+       }
+
+       if (*insn  >= 0x40 && *insn <= 0x4f && *++insn == 0xcf)
+               return 1;
+       return 0;
+}
+
+int arch_prepare_kprobe(struct kprobe *p)
+{
+       /* insn: must be on special executable page on x86_64. */
+       p->ainsn.insn = get_insn_slot();
+       if (!p->ainsn.insn) {
+               return -ENOMEM;
+       }
+       memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE);
+       return 0;
+}
+
+void arch_remove_kprobe(struct kprobe *p)
+{
+       free_insn_slot(p->ainsn.insn);
+}
+
+static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs)
+{
+       *p->addr = p->opcode;
+       regs->rip = (unsigned long)p->addr;
+}
+
+static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+       regs->eflags |= TF_MASK;
+       regs->eflags &= ~IF_MASK;
+
+       regs->rip = (unsigned long)p->ainsn.insn;
+}
+
+/*
+ * Interrupts are disabled on entry as trap3 is an interrupt gate and they
+ * remain disabled thorough out this function.
+ */
+int kprobe_handler(struct pt_regs *regs)
+{
+       struct kprobe *p;
+       int ret = 0;
+       kprobe_opcode_t *addr = (kprobe_opcode_t *)(regs->rip - sizeof(kprobe_opcode_t));
+
+       /* We're in an interrupt, but this is clear and BUG()-safe. */
+       preempt_disable();
+
+       /* Check we're not actually recursing */
+       if (kprobe_running()) {
+               /* We *are* holding lock here, so this is safe.
+                  Disarm the probe we just hit, and ignore it. */
+               p = get_kprobe(addr);
+               if (p) {
+                       disarm_kprobe(p, regs);
+                       ret = 1;
+               } else {
+                       p = current_kprobe;
+                       if (p->break_handler && p->break_handler(p, regs)) {
+                               goto ss_probe;
+                       }
+               }
+               /* If it's not ours, can't be delete race, (we hold lock). */
+               goto no_kprobe;
+       }
+
+       lock_kprobes();
+       p = get_kprobe(addr);
+       if (!p) {
+               unlock_kprobes();
+               if (*addr != BREAKPOINT_INSTRUCTION) {
+                       /*
+                        * The breakpoint instruction was removed right
+                        * after we hit it.  Another cpu has removed
+                        * either a probepoint or a debugger breakpoint
+                        * at this address.  In either case, no further
+                        * handling of this interrupt is appropriate.
+                        */
+                       ret = 1;
+               }
+               /* Not one of ours: let kernel handle it */
+               goto no_kprobe;
+       }
+
+       kprobe_status = KPROBE_HIT_ACTIVE;
+       current_kprobe = p;
+       kprobe_saved_rflags = kprobe_old_rflags
+           = (regs->eflags & (TF_MASK | IF_MASK));
+       if (is_IF_modifier(p->ainsn.insn))
+               kprobe_saved_rflags &= ~IF_MASK;
+
+       if (p->pre_handler(p, regs)) {
+               /* handler has already set things up, so skip ss setup */
+               return 1;
+       }
+
+      ss_probe:
+       prepare_singlestep(p, regs);
+       kprobe_status = KPROBE_HIT_SS;
+       return 1;
+
+      no_kprobe:
+       preempt_enable_no_resched();
+       return ret;
+}
+
+/*
+ * Called after single-stepping.  p->addr is the address of the
+ * instruction whose first byte has been replaced by the "int 3"
+ * instruction.  To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction.  The address of this
+ * copy is p->ainsn.insn.
+ *
+ * This function prepares to return from the post-single-step
+ * interrupt.  We have to fix up the stack as follows:
+ *
+ * 0) Except in the case of absolute or indirect jump or call instructions,
+ * the new rip is relative to the copied instruction.  We need to make
+ * it relative to the original instruction.
+ *
+ * 1) If the single-stepped instruction was pushfl, then the TF and IF
+ * flags are set in the just-pushed eflags, and may need to be cleared.
+ *
+ * 2) If the single-stepped instruction was a call, the return address
+ * that is atop the stack is the address following the copied instruction.
+ * We need to make it the address following the original instruction.
+ */
+static void resume_execution(struct kprobe *p, struct pt_regs *regs)
+{
+       unsigned long *tos = (unsigned long *)regs->rsp;
+       unsigned long next_rip = 0;
+       unsigned long copy_rip = (unsigned long)p->ainsn.insn;
+       unsigned long orig_rip = (unsigned long)p->addr;
+       kprobe_opcode_t *insn = p->ainsn.insn;
+
+       /*skip the REX prefix*/
+       if (*insn >= 0x40 && *insn <= 0x4f)
+               insn++;
+
+       switch (*insn) {
+       case 0x9c:              /* pushfl */
+               *tos &= ~(TF_MASK | IF_MASK);
+               *tos |= kprobe_old_rflags;
+               break;
+       case 0xe8:              /* call relative - Fix return addr */
+               *tos = orig_rip + (*tos - copy_rip);
+               break;
+       case 0xff:
+               if ((*insn & 0x30) == 0x10) {
+                       /* call absolute, indirect */
+                       /* Fix return addr; rip is correct. */
+                       next_rip = regs->rip;
+                       *tos = orig_rip + (*tos - copy_rip);
+               } else if (((*insn & 0x31) == 0x20) ||  /* jmp near, absolute indirect */
+                          ((*insn & 0x31) == 0x21)) {  /* jmp far, absolute indirect */
+                       /* rip is correct. */
+                       next_rip = regs->rip;
+               }
+               break;
+       case 0xea:              /* jmp absolute -- rip is correct */
+               next_rip = regs->rip;
+               break;
+       default:
+               break;
+       }
+
+       regs->eflags &= ~TF_MASK;
+       if (next_rip) {
+               regs->rip = next_rip;
+       } else {
+               regs->rip = orig_rip + (regs->rip - copy_rip);
+       }
+}
+
+/*
+ * Interrupts are disabled on entry as trap1 is an interrupt gate and they
+ * remain disabled thoroughout this function.  And we hold kprobe lock.
+ */
+int post_kprobe_handler(struct pt_regs *regs)
+{
+       if (!kprobe_running())
+               return 0;
+
+       if (current_kprobe->post_handler)
+               current_kprobe->post_handler(current_kprobe, regs, 0);
+
+       resume_execution(current_kprobe, regs);
+       regs->eflags |= kprobe_saved_rflags;
+
+       unlock_kprobes();
+       preempt_enable_no_resched();
+
+       /*
+        * if somebody else is singlestepping across a probe point, eflags
+        * will have TF set, in which case, continue the remaining processing
+        * of do_debug, as if this is not a probe hit.
+        */
+       if (regs->eflags & TF_MASK)
+               return 0;
+
+       return 1;
+}
+
+/* Interrupts disabled, kprobe_lock held. */
+int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+       if (current_kprobe->fault_handler
+           && current_kprobe->fault_handler(current_kprobe, regs, trapnr))
+               return 1;
+
+       if (kprobe_status & KPROBE_HIT_SS) {
+               resume_execution(current_kprobe, regs);
+               regs->eflags |= kprobe_old_rflags;
+
+               unlock_kprobes();
+               preempt_enable_no_resched();
+       }
+       return 0;
+}
+
+/*
+ * Wrapper routine for handling exceptions.
+ */
+int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val,
+                            void *data)
+{
+       struct die_args *args = (struct die_args *)data;
+       switch (val) {
+       case DIE_INT3:
+               if (kprobe_handler(args->regs))
+                       return NOTIFY_STOP;
+               break;
+       case DIE_DEBUG:
+               if (post_kprobe_handler(args->regs))
+                       return NOTIFY_STOP;
+               break;
+       case DIE_GPF:
+               if (kprobe_running() &&
+                   kprobe_fault_handler(args->regs, args->trapnr))
+                       return NOTIFY_STOP;
+               break;
+       case DIE_PAGE_FAULT:
+               if (kprobe_running() &&
+                   kprobe_fault_handler(args->regs, args->trapnr))
+                       return NOTIFY_STOP;
+               break;
+       default:
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       struct jprobe *jp = container_of(p, struct jprobe, kp);
+       unsigned long addr;
+
+       jprobe_saved_regs = *regs;
+       jprobe_saved_rsp = (long *) regs->rsp;
+       addr = (unsigned long)jprobe_saved_rsp;
+       /*
+        * As Linus pointed out, gcc assumes that the callee
+        * owns the argument space and could overwrite it, e.g.
+        * tailcall optimization. So, to be absolutely safe
+        * we also save and restore enough stack bytes to cover
+        * the argument area.
+        */
+       memcpy(jprobes_stack, (kprobe_opcode_t *) addr, MIN_STACK_SIZE(addr));
+       regs->eflags &= ~IF_MASK;
+       regs->rip = (unsigned long)(jp->entry);
+       return 1;
+}
+
+void jprobe_return(void)
+{
+       preempt_enable_no_resched();
+       asm volatile ("       xchg   %%rbx,%%rsp     \n"
+                     "       int3                      \n"
+                     "       .globl jprobe_return_end  \n"
+                     "       jprobe_return_end:        \n"
+                     "       nop                       \n"::"b"
+                     (jprobe_saved_rsp):"memory");
+}
+
+int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       u8 *addr = (u8 *) (regs->rip - 1);
+       unsigned long stack_addr = (unsigned long)jprobe_saved_rsp;
+       struct jprobe *jp = container_of(p, struct jprobe, kp);
+
+       if ((addr > (u8 *) jprobe_return) && (addr < (u8 *) jprobe_return_end)) {
+               if ((long *)regs->rsp != jprobe_saved_rsp) {
+                       struct pt_regs *saved_regs =
+                           container_of(jprobe_saved_rsp, struct pt_regs, rsp);
+                       printk("current rsp %p does not match saved rsp %p\n",
+                              (long *)regs->rsp, jprobe_saved_rsp);
+                       printk("Saved registers for jprobe %p\n", jp);
+                       show_registers(saved_regs);
+                       printk("Current registers\n");
+                       show_registers(regs);
+                       BUG();
+               }
+               *regs = jprobe_saved_regs;
+               memcpy((kprobe_opcode_t *) stack_addr, jprobes_stack,
+                      MIN_STACK_SIZE(stack_addr));
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * kprobe->ainsn.insn points to the copy of the instruction to be single-stepped.
+ * By default on x86_64, pages we get from kmalloc or vmalloc are not
+ * executable.  Single-stepping an instruction on such a page yields an
+ * oops.  So instead of storing the instruction copies in their respective
+ * kprobe objects, we allocate a page, map it executable, and store all the
+ * instruction copies there.  (We can allocate additional pages if somebody
+ * inserts a huge number of probes.)  Each page can hold up to INSNS_PER_PAGE
+ * instruction slots, each of which is MAX_INSN_SIZE*sizeof(kprobe_opcode_t)
+ * bytes.
+ */
+#define INSNS_PER_PAGE (PAGE_SIZE/(MAX_INSN_SIZE*sizeof(kprobe_opcode_t)))
+struct kprobe_insn_page {
+       struct hlist_node hlist;
+       kprobe_opcode_t *insns;         /* page of instruction slots */
+       char slot_used[INSNS_PER_PAGE];
+       int nused;
+};
+
+static struct hlist_head kprobe_insn_pages;
+
+/**
+ * get_insn_slot() - Find a slot on an executable page for an instruction.
+ * We allocate an executable page if there's no room on existing ones.
+ */
+static kprobe_opcode_t *get_insn_slot(void)
+{
+       struct kprobe_insn_page *kip;
+       struct hlist_node *pos;
+
+       hlist_for_each(pos, &kprobe_insn_pages) {
+               kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
+               if (kip->nused < INSNS_PER_PAGE) {
+                       int i;
+                       for (i = 0; i < INSNS_PER_PAGE; i++) {
+                               if (!kip->slot_used[i]) {
+                                       kip->slot_used[i] = 1;
+                                       kip->nused++;
+                                       return kip->insns + (i*MAX_INSN_SIZE);
+                               }
+                       }
+                       /* Surprise!  No unused slots.  Fix kip->nused. */
+                       kip->nused = INSNS_PER_PAGE;
+               }
+       }
+
+       /* All out of space.  Need to allocate a new page. Use slot 0.*/
+       kip = kmalloc(sizeof(struct kprobe_insn_page), GFP_ATOMIC);
+       if (!kip) {
+               return NULL;
+       }
+       kip->insns = (kprobe_opcode_t*) __vmalloc(PAGE_SIZE,
+               GFP_ATOMIC|__GFP_HIGHMEM, __pgprot(__PAGE_KERNEL_EXEC));
+       if (!kip->insns) {
+               kfree(kip);
+               return NULL;
+       }
+       INIT_HLIST_NODE(&kip->hlist);
+       hlist_add_head(&kip->hlist, &kprobe_insn_pages);
+       memset(kip->slot_used, 0, INSNS_PER_PAGE);
+       kip->slot_used[0] = 1;
+       kip->nused = 1;
+       return kip->insns;
+}
+
+/**
+ * free_insn_slot() - Free instruction slot obtained from get_insn_slot().
+ */
+static void free_insn_slot(kprobe_opcode_t *slot)
+{
+       struct kprobe_insn_page *kip;
+       struct hlist_node *pos;
+
+       hlist_for_each(pos, &kprobe_insn_pages) {
+               kip = hlist_entry(pos, struct kprobe_insn_page, hlist);
+               if (kip->insns <= slot
+                   && slot < kip->insns+(INSNS_PER_PAGE*MAX_INSN_SIZE)) {
+                       int i = (slot - kip->insns) / MAX_INSN_SIZE;
+                       kip->slot_used[i] = 0;
+                       kip->nused--;
+                       if (kip->nused == 0) {
+                               /*
+                                * Page is no longer in use.  Free it unless
+                                * it's the last one.  We keep the last one
+                                * so as not to have to set it up again the
+                                * next time somebody inserts a probe.
+                                */
+                               hlist_del(&kip->hlist);
+                               if (hlist_empty(&kprobe_insn_pages)) {
+                                       INIT_HLIST_NODE(&kip->hlist);
+                                       hlist_add_head(&kip->hlist,
+                                               &kprobe_insn_pages);
+                               } else {
+                                       vfree(kip->insns);
+                                       kfree(kip);
+                               }
+                       }
+                       return;
+               }
+       }
+}
diff --git a/arch/x86_64/kernel/machine_kexec.c b/arch/x86_64/kernel/machine_kexec.c
new file mode 100644 (file)
index 0000000..3473878
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * machine_kexec.c - handle transition of Linux booting another kernel
+ * Copyright (C) 2002-2004 Eric Biederman  <ebiederm@xmission.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/kexec.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/reboot.h>
+#include <asm/pda.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+#include <asm/mmu_context.h>
+#include <asm/io.h>
+#include <asm/apic.h>
+#include <asm/cpufeature.h>
+#include <asm/hw_irq.h>
+
+#define LEVEL0_SIZE (1UL << 12UL)
+#define LEVEL1_SIZE (1UL << 21UL)
+#define LEVEL2_SIZE (1UL << 30UL)
+#define LEVEL3_SIZE (1UL << 39UL)
+#define LEVEL4_SIZE (1UL << 48UL)
+
+#define L0_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
+#define L1_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_PSE)
+#define L2_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
+#define L3_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
+
+static void init_level2_page(
+       uint64_t *level2p, unsigned long addr)
+{
+       unsigned long end_addr;
+       addr &= PAGE_MASK;
+       end_addr = addr + LEVEL2_SIZE;
+       while(addr < end_addr) {
+               *(level2p++) = addr | L1_ATTR;
+               addr += LEVEL1_SIZE;
+       }
+}
+
+static int init_level3_page(struct kimage *image,
+       uint64_t *level3p, unsigned long addr, unsigned long last_addr)
+{
+       unsigned long end_addr;
+       int result;
+       result = 0;
+       addr &= PAGE_MASK;
+       end_addr = addr + LEVEL3_SIZE;
+       while((addr < last_addr) && (addr < end_addr)) {
+               struct page *page;
+               uint64_t *level2p;
+               page = kimage_alloc_control_pages(image, 0);
+               if (!page) {
+                       result = -ENOMEM;
+                       goto out;
+               }
+               level2p = (uint64_t *)page_address(page);
+               init_level2_page(level2p, addr);
+               *(level3p++) = __pa(level2p) | L2_ATTR;
+               addr += LEVEL2_SIZE;
+       }
+       /* clear the unused entries */
+       while(addr < end_addr) {
+               *(level3p++) = 0;
+               addr += LEVEL2_SIZE;
+       }
+out:
+       return result;
+}
+
+
+static int init_level4_page(struct kimage *image,
+       uint64_t *level4p, unsigned long addr, unsigned long last_addr)
+{
+       unsigned long end_addr;
+       int result;
+       result = 0;
+       addr &= PAGE_MASK;
+       end_addr = addr + LEVEL4_SIZE;
+       while((addr < last_addr) && (addr < end_addr)) {
+               struct page *page;
+               uint64_t *level3p;
+               page = kimage_alloc_control_pages(image, 0);
+               if (!page) {
+                       result = -ENOMEM;
+                       goto out;
+               }
+               level3p = (uint64_t *)page_address(page);
+               result = init_level3_page(image, level3p, addr, last_addr);
+               if (result) {
+                       goto out;
+               }
+               *(level4p++) = __pa(level3p) | L3_ATTR;
+               addr += LEVEL3_SIZE;
+       }
+       /* clear the unused entries */
+       while(addr < end_addr) {
+               *(level4p++) = 0;
+               addr += LEVEL3_SIZE;
+       }
+ out:
+       return result;
+}
+
+
+static int init_pgtable(struct kimage *image, unsigned long start_pgtable)
+{
+       uint64_t *level4p;
+       level4p = (uint64_t *)__va(start_pgtable);
+       return init_level4_page(image, level4p, 0, end_pfn << PAGE_SHIFT);
+}
+
+static void set_idt(void *newidt, __u16 limit)
+{
+       unsigned char curidt[10];
+
+       /* x86-64 supports unaliged loads & stores */
+       (*(__u16 *)(curidt)) = limit;
+       (*(__u64 *)(curidt +2)) = (unsigned long)(newidt);
+
+       __asm__ __volatile__ (
+               "lidt %0\n"
+               : "=m" (curidt)
+               );
+};
+
+
+static void set_gdt(void *newgdt, __u16 limit)
+{
+       unsigned char curgdt[10];
+
+       /* x86-64 supports unaligned loads & stores */
+       (*(__u16 *)(curgdt)) = limit;
+       (*(__u64 *)(curgdt +2)) = (unsigned long)(newgdt);
+
+       __asm__ __volatile__ (
+               "lgdt %0\n"
+               : "=m" (curgdt)
+               );
+};
+
+static void load_segments(void)
+{
+       __asm__ __volatile__ (
+               "\tmovl $"STR(__KERNEL_DS)",%eax\n"
+               "\tmovl %eax,%ds\n"
+               "\tmovl %eax,%es\n"
+               "\tmovl %eax,%ss\n"
+               "\tmovl %eax,%fs\n"
+               "\tmovl %eax,%gs\n"
+               );
+#undef STR
+#undef __STR
+}
+
+typedef void (*relocate_new_kernel_t)(
+       unsigned long indirection_page, unsigned long control_code_buffer,
+       unsigned long start_address, unsigned long pgtable);
+
+const extern unsigned char relocate_new_kernel[];
+extern void relocate_new_kernel_end(void);
+const extern unsigned long relocate_new_kernel_size;
+
+int machine_kexec_prepare(struct kimage *image)
+{
+       unsigned long start_pgtable, control_code_buffer;
+       int result;
+
+       /* Calculate the offsets */
+       start_pgtable       = page_to_pfn(image->control_code_page) << PAGE_SHIFT;
+       control_code_buffer = start_pgtable + 4096UL;
+
+       /* Setup the identity mapped 64bit page table */
+       result = init_pgtable(image, start_pgtable);
+       if (result) {
+               return result;
+       }
+
+       /* Place the code in the reboot code buffer */
+       memcpy(__va(control_code_buffer), relocate_new_kernel, relocate_new_kernel_size);
+
+       return 0;
+}
+
+void machine_kexec_cleanup(struct kimage *image)
+{
+       return;
+}
+
+/*
+ * Do not allocate memory (or fail in any way) in machine_kexec().
+ * We are past the point of no return, committed to rebooting now.
+ */
+void machine_kexec(struct kimage *image)
+{
+       unsigned long indirection_page;
+       unsigned long control_code_buffer;
+       unsigned long start_pgtable;
+       relocate_new_kernel_t rnk;
+
+       /* Interrupts aren't acceptable while we reboot */
+       local_irq_disable();
+
+       /* Calculate the offsets */
+       indirection_page    = image->head & PAGE_MASK;
+       start_pgtable       = page_to_pfn(image->control_code_page) << PAGE_SHIFT;
+       control_code_buffer = start_pgtable + 4096UL;
+
+       /* Set the low half of the page table to my identity mapped
+        * page table for kexec.  Leave the high half pointing at the
+        * kernel pages.   Don't bother to flush the global pages
+        * as that will happen when I fully switch to my identity mapped
+        * page table anyway.
+        */
+       memcpy((void *)read_pda(level4_pgt), __va(start_pgtable), PAGE_SIZE/2);
+       __flush_tlb();
+
+
+       /* The segment registers are funny things, they are
+        * automatically loaded from a table, in memory wherever you
+        * set them to a specific selector, but this table is never
+        * accessed again unless you set the segment to a different selector.
+        *
+        * The more common model are caches where the behide
+        * the scenes work is done, but is also dropped at arbitrary
+        * times.
+        *
+        * I take advantage of this here by force loading the
+        * segments, before I zap the gdt with an invalid value.
+        */
+       load_segments();
+       /* The gdt & idt are now invalid.
+        * If you want to load them you must set up your own idt & gdt.
+        */
+       set_gdt(phys_to_virt(0),0);
+       set_idt(phys_to_virt(0),0);
+       /* now call it */
+       rnk = (relocate_new_kernel_t) control_code_buffer;
+       (*rnk)(indirection_page, control_code_buffer, image->start, start_pgtable);
+}
diff --git a/arch/x86_64/kernel/mce_intel.c b/arch/x86_64/kernel/mce_intel.c
new file mode 100644 (file)
index 0000000..4db9a64
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Intel specific MCE features.
+ * Copyright 2004 Zwane Mwaikambo <zwane@linuxpower.ca>
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/percpu.h>
+#include <asm/processor.h>
+#include <asm/msr.h>
+#include <asm/mce.h>
+#include <asm/hw_irq.h>
+
+static DEFINE_PER_CPU(unsigned long, next_check);
+
+asmlinkage void smp_thermal_interrupt(void)
+{
+       struct mce m;
+
+       ack_APIC_irq();
+
+       irq_enter();
+       if (time_before(jiffies, __get_cpu_var(next_check)))
+               goto done;
+
+       __get_cpu_var(next_check) = jiffies + HZ*300;
+       memset(&m, 0, sizeof(m));
+       m.cpu = smp_processor_id();
+       m.bank = MCE_THERMAL_BANK;
+       rdtscll(m.tsc);
+       rdmsrl(MSR_IA32_THERM_STATUS, m.status);
+       if (m.status & 0x1) {
+               printk(KERN_EMERG
+                       "CPU%d: Temperature above threshold, cpu clock throttled\n", m.cpu);
+               add_taint(TAINT_MACHINE_CHECK);
+       } else {
+               printk(KERN_EMERG "CPU%d: Temperature/speed normal\n", m.cpu);
+       }
+
+       mce_log(&m);
+done:
+       irq_exit();
+}
+
+static void __init intel_init_thermal(struct cpuinfo_x86 *c)
+{
+       u32 l, h;
+       int tm2 = 0;
+       unsigned int cpu = smp_processor_id();
+
+       if (!cpu_has(c, X86_FEATURE_ACPI))
+               return;
+
+       if (!cpu_has(c, X86_FEATURE_ACC))
+               return;
+
+       /* first check if TM1 is already enabled by the BIOS, in which
+        * case there might be some SMM goo which handles it, so we can't even
+        * put a handler since it might be delivered via SMI already.
+        */
+       rdmsr(MSR_IA32_MISC_ENABLE, l, h);
+       h = apic_read(APIC_LVTTHMR);
+       if ((l & (1 << 3)) && (h & APIC_DM_SMI)) {
+               printk(KERN_DEBUG
+                      "CPU%d: Thermal monitoring handled by SMI\n", cpu);
+               return;
+       }
+
+       if (cpu_has(c, X86_FEATURE_TM2) && (l & (1 << 13)))
+               tm2 = 1;
+
+       if (h & APIC_VECTOR_MASK) {
+               printk(KERN_DEBUG
+                      "CPU%d: Thermal LVT vector (%#x) already "
+                      "installed\n", cpu, (h & APIC_VECTOR_MASK));
+               return;
+       }
+
+       h = THERMAL_APIC_VECTOR;
+       h |= (APIC_DM_FIXED | APIC_LVT_MASKED);
+       apic_write_around(APIC_LVTTHMR, h);
+
+       rdmsr(MSR_IA32_THERM_INTERRUPT, l, h);
+       wrmsr(MSR_IA32_THERM_INTERRUPT, l | 0x03, h);
+
+       rdmsr(MSR_IA32_MISC_ENABLE, l, h);
+       wrmsr(MSR_IA32_MISC_ENABLE, l | (1 << 3), h);
+
+       l = apic_read(APIC_LVTTHMR);
+       apic_write_around(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
+       printk(KERN_INFO "CPU%d: Thermal monitoring enabled (%s)\n",
+               cpu, tm2 ? "TM2" : "TM1");
+       return;
+}
+
+void __init mce_intel_feature_init(struct cpuinfo_x86 *c)
+{
+       intel_init_thermal(c);
+}
diff --git a/arch/x86_64/kernel/relocate_kernel.S b/arch/x86_64/kernel/relocate_kernel.S
new file mode 100644 (file)
index 0000000..c944e59
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * relocate_kernel.S - put the kernel image in place to boot
+ * Copyright (C) 2002-2004 Eric Biederman  <ebiederm@xmission.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#include <linux/linkage.h>
+
+       /*
+        * Must be relocatable PIC code callable as a C function, that once
+        * it starts can not use the previous processes stack.
+        */
+       .globl relocate_new_kernel
+       .code64
+relocate_new_kernel:
+       /* %rdi indirection_page
+        * %rsi reboot_code_buffer
+        * %rdx start address
+        * %rcx page_table
+        * %r8  arg5
+        * %r9  arg6
+        */
+
+       /* zero out flags, and disable interrupts */
+       pushq $0
+       popfq
+
+       /* set a new stack at the bottom of our page... */
+       lea   4096(%rsi), %rsp
+
+       /* store the parameters back on the stack */
+       pushq   %rdx /* store the start address */
+
+       /* Set cr0 to a known state:
+        * 31 1 == Paging enabled
+        * 18 0 == Alignment check disabled
+        * 16 0 == Write protect disabled
+        * 3  0 == No task switch
+        * 2  0 == Don't do FP software emulation.
+        * 0  1 == Proctected mode enabled
+        */
+       movq    %cr0, %rax
+       andq    $~((1<<18)|(1<<16)|(1<<3)|(1<<2)), %rax
+       orl     $((1<<31)|(1<<0)), %eax
+       movq    %rax, %cr0
+
+       /* Set cr4 to a known state:
+        * 10 0 == xmm exceptions disabled
+        * 9  0 == xmm registers instructions disabled
+        * 8  0 == performance monitoring counter disabled
+        * 7  0 == page global disabled
+        * 6  0 == machine check exceptions disabled
+        * 5  1 == physical address extension enabled
+        * 4  0 == page size extensions disabled
+        * 3  0 == Debug extensions disabled
+        * 2  0 == Time stamp disable (disabled)
+        * 1  0 == Protected mode virtual interrupts disabled
+        * 0  0 == VME disabled
+        */
+
+       movq    $((1<<5)), %rax
+       movq    %rax, %cr4
+
+       jmp 1f
+1:
+
+       /* Switch to the identity mapped page tables,
+        * and flush the TLB.
+       */
+       movq    %rcx, %cr3
+
+       /* Do the copies */
+       movq    %rdi, %rbx      /* Put the indirection page in %rbx */
+       xorq    %rdi, %rdi
+       xorq    %rsi, %rsi
+
+0:     /* top, read another word for the indirection page */
+
+       movq    (%rbx), %rcx
+       addq    $8,     %rbx
+       testq   $0x1,   %rcx  /* is it a destination page? */
+       jz      1f
+       movq    %rcx,   %rdi
+       andq    $0xfffffffffffff000, %rdi
+       jmp     0b
+1:
+       testq   $0x2,   %rcx  /* is it an indirection page? */
+       jz      1f
+       movq    %rcx,   %rbx
+       andq    $0xfffffffffffff000, %rbx
+       jmp     0b
+1:
+       testq   $0x4,   %rcx  /* is it the done indicator? */
+       jz      1f
+       jmp     2f
+1:
+       testq   $0x8,   %rcx  /* is it the source indicator? */
+       jz      0b            /* Ignore it otherwise */
+       movq    %rcx,   %rsi  /* For ever source page do a copy */
+       andq    $0xfffffffffffff000, %rsi
+
+       movq    $512,   %rcx
+       rep ; movsq
+       jmp     0b
+2:
+
+       /* To be certain of avoiding problems with self-modifying code
+        * I need to execute a serializing instruction here.
+        * So I flush the TLB by reloading %cr3 here, it's handy,
+        * and not processor dependent.
+        */
+       movq    %cr3, %rax
+       movq    %rax, %cr3
+
+       /* set all of the registers to known values */
+       /* leave %rsp alone */
+
+       xorq    %rax, %rax
+       xorq    %rbx, %rbx
+       xorq    %rcx, %rcx
+       xorq    %rdx, %rdx
+       xorq    %rsi, %rsi
+       xorq    %rdi, %rdi
+       xorq    %rbp, %rbp
+       xorq    %r8,  %r8
+       xorq    %r9,  %r9
+       xorq    %r10, %r9
+       xorq    %r11, %r11
+       xorq    %r12, %r12
+       xorq    %r13, %r13
+       xorq    %r14, %r14
+       xorq    %r15, %r15
+
+       ret
+relocate_new_kernel_end:
+
+       .globl relocate_new_kernel_size
+relocate_new_kernel_size:
+       .quad relocate_new_kernel_end - relocate_new_kernel
diff --git a/arch/x86_64/mm/srat.c b/arch/x86_64/mm/srat.c
new file mode 100644 (file)
index 0000000..5d01b31
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * ACPI 3.0 based NUMA setup
+ * Copyright 2004 Andi Kleen, SuSE Labs.
+ *
+ * Reads the ACPI SRAT table to figure out what memory belongs to which CPUs.
+ *
+ * Called from acpi_numa_init while reading the SRAT and SLIT tables.
+ * Assumes all memory regions belonging to a single proximity domain
+ * are in one chunk. Holes between them will be included in the node.
+ */
+
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/mmzone.h>
+#include <linux/bitmap.h>
+#include <linux/module.h>
+#include <linux/topology.h>
+#include <asm/proto.h>
+#include <asm/numa.h>
+
+static struct acpi_table_slit *acpi_slit;
+
+static nodemask_t nodes_parsed __initdata;
+static nodemask_t nodes_found __initdata;
+static struct node nodes[MAX_NUMNODES] __initdata;
+static __u8  pxm2node[256] = { [0 ... 255] = 0xff };
+
+static __init int setup_node(int pxm)
+{
+       unsigned node = pxm2node[pxm];
+       if (node == 0xff) {
+               if (nodes_weight(nodes_found) >= MAX_NUMNODES)
+                       return -1;
+               node = first_unset_node(nodes_found); 
+               node_set(node, nodes_found);
+               pxm2node[pxm] = node;
+       }
+       return pxm2node[pxm];
+}
+
+static __init int conflicting_nodes(unsigned long start, unsigned long end)
+{
+       int i;
+       for_each_online_node(i) {
+               struct node *nd = &nodes[i];
+               if (nd->start == nd->end)
+                       continue;
+               if (nd->end > start && nd->start < end)
+                       return 1;
+               if (nd->end == end && nd->start == start)
+                       return 1;
+       }
+       return -1;
+}
+
+static __init void cutoff_node(int i, unsigned long start, unsigned long end)
+{
+       struct node *nd = &nodes[i];
+       if (nd->start < start) {
+               nd->start = start;
+               if (nd->end < nd->start)
+                       nd->start = nd->end;
+       }
+       if (nd->end > end) {
+               if (!(end & 0xfff))
+                       end--;
+               nd->end = end;
+               if (nd->start > nd->end)
+                       nd->start = nd->end;
+       }
+}
+
+static __init void bad_srat(void)
+{
+       printk(KERN_ERR "SRAT: SRAT not used.\n");
+       acpi_numa = -1;
+}
+
+static __init inline int srat_disabled(void)
+{
+       return numa_off || acpi_numa < 0;
+}
+
+/* Callback for SLIT parsing */
+void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
+{
+       acpi_slit = slit;
+}
+
+/* Callback for Proximity Domain -> LAPIC mapping */
+void __init
+acpi_numa_processor_affinity_init(struct acpi_table_processor_affinity *pa)
+{
+       int pxm, node;
+       if (srat_disabled() || pa->flags.enabled == 0)
+               return;
+       pxm = pa->proximity_domain;
+       node = setup_node(pxm);
+       if (node < 0) {
+               printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm);
+               bad_srat();
+               return;
+       }
+       if (pa->apic_id >= NR_CPUS) {
+               printk(KERN_ERR "SRAT: lapic %u too large.\n",
+                      pa->apic_id);
+               bad_srat();
+               return;
+       }
+       cpu_to_node[pa->apic_id] = node;
+       acpi_numa = 1;
+       printk(KERN_INFO "SRAT: PXM %u -> APIC %u -> Node %u\n",
+              pxm, pa->apic_id, node);
+}
+
+/* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
+void __init
+acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma)
+{
+       struct node *nd;
+       unsigned long start, end;
+       int node, pxm;
+       int i;
+
+       if (srat_disabled() || ma->flags.enabled == 0)
+               return;
+       /* hotplug bit is ignored for now */
+       pxm = ma->proximity_domain;
+       node = setup_node(pxm);
+       if (node < 0) {
+               printk(KERN_ERR "SRAT: Too many proximity domains.\n");
+               bad_srat();
+               return;
+       }
+       start = ma->base_addr_lo | ((u64)ma->base_addr_hi << 32);
+       end = start + (ma->length_lo | ((u64)ma->length_hi << 32));
+       i = conflicting_nodes(start, end);
+       if (i >= 0) {
+               printk(KERN_ERR
+                      "SRAT: pxm %d overlap %lx-%lx with node %d(%Lx-%Lx)\n",
+                      pxm, start, end, i, nodes[i].start, nodes[i].end);
+               bad_srat();
+               return;
+       }
+       nd = &nodes[node];
+       if (!node_test_and_set(node, nodes_parsed)) {
+               nd->start = start;
+               nd->end = end;
+       } else {
+               if (start < nd->start)
+                       nd->start = start;
+               if (nd->end < end)
+                       nd->end = end;
+       }
+       if (!(nd->end & 0xfff))
+               nd->end--;
+       printk(KERN_INFO "SRAT: Node %u PXM %u %Lx-%Lx\n", node, pxm,
+              nd->start, nd->end);
+}
+
+void __init acpi_numa_arch_fixup(void) {}
+
+/* Use the information discovered above to actually set up the nodes. */
+int __init acpi_scan_nodes(unsigned long start, unsigned long end)
+{
+       int i;
+       if (acpi_numa <= 0)
+               return -1;
+       memnode_shift = compute_hash_shift(nodes, nodes_weight(nodes_parsed));
+       if (memnode_shift < 0) {
+               printk(KERN_ERR
+                    "SRAT: No NUMA node hash function found. Contact maintainer\n");
+               bad_srat();
+               return -1;
+       }
+       for (i = 0; i < MAX_NUMNODES; i++) {
+               if (!node_isset(i, nodes_parsed))
+                       continue;
+               cutoff_node(i, start, end);
+               if (nodes[i].start == nodes[i].end) { 
+                       node_clear(i, nodes_parsed);
+                       continue;
+               }
+               setup_node_bootmem(i, nodes[i].start, nodes[i].end);
+       }
+       for (i = 0; i < NR_CPUS; i++) { 
+               if (cpu_to_node[i] == NUMA_NO_NODE)
+                       continue;
+               if (!node_isset(cpu_to_node[i], nodes_parsed))
+                       cpu_to_node[i] = NUMA_NO_NODE; 
+       }
+       numa_init_array();
+       return 0;
+}
+
+int node_to_pxm(int n)
+{
+       int i;
+       if (pxm2node[n] == n)
+               return n;
+       for (i = 0; i < 256; i++)
+               if (pxm2node[i] == n)
+                       return i;
+       return 0;
+}
+
+int __node_distance(int a, int b)
+{
+       int index;
+
+       if (!acpi_slit)
+               return a == b ? 10 : 20;
+       index = acpi_slit->localities * node_to_pxm(a);
+       return acpi_slit->entry[index + node_to_pxm(b)];
+}
+
+EXPORT_SYMBOL(__node_distance);
diff --git a/crypto/anubis.c b/crypto/anubis.c
new file mode 100644 (file)
index 0000000..3925eb0
--- /dev/null
@@ -0,0 +1,719 @@
+/*
+ * Cryptographic API.
+ *
+ * Anubis Algorithm
+ *
+ * The Anubis algorithm was developed by Paulo S. L. M. Barreto and
+ * Vincent Rijmen.
+ *
+ * See
+ *
+ *     P.S.L.M. Barreto, V. Rijmen,
+ *     ``The Anubis block cipher,''
+ *     NESSIE submission, 2000.
+ *
+ * This software implements the "tweaked" version of Anubis.
+ * Only the S-box and (consequently) the rounds constants have been
+ * changed.
+ *
+ * The original authors have disclaimed all copyright interest in this
+ * code and thus put it in the public domain. The subsequent authors
+ * have put this under the GNU General Public License.
+ *
+ * By Aaron Grothe ajgrothe@yahoo.com, October 28, 2004
+ *
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <asm/scatterlist.h>
+#include <linux/crypto.h>
+
+#define ANUBIS_MIN_KEY_SIZE    16
+#define ANUBIS_MAX_KEY_SIZE    40
+#define ANUBIS_BLOCK_SIZE      16
+#define ANUBIS_MAX_N           10
+#define ANUBIS_MAX_ROUNDS      (8 + ANUBIS_MAX_N)
+
+struct anubis_ctx {
+       int key_len; // in bits
+       int R;
+       u32 E[ANUBIS_MAX_ROUNDS + 1][4];
+       u32 D[ANUBIS_MAX_ROUNDS + 1][4];
+};
+
+static const u32 T0[256] = {
+       0xba69d2bbU, 0x54a84de5U, 0x2f5ebce2U, 0x74e8cd25U,
+       0x53a651f7U, 0xd3bb6bd0U, 0xd2b96fd6U, 0x4d9a29b3U,
+       0x50a05dfdU, 0xac458acfU, 0x8d070e09U, 0xbf63c6a5U,
+       0x70e0dd3dU, 0x52a455f1U, 0x9a29527bU, 0x4c982db5U,
+       0xeac98f46U, 0xd5b773c4U, 0x97336655U, 0xd1bf63dcU,
+       0x3366ccaaU, 0x51a259fbU, 0x5bb671c7U, 0xa651a2f3U,
+       0xdea15ffeU, 0x48903dadU, 0xa84d9ad7U, 0x992f5e71U,
+       0xdbab4be0U, 0x3264c8acU, 0xb773e695U, 0xfce5d732U,
+       0xe3dbab70U, 0x9e214263U, 0x913f7e41U, 0x9b2b567dU,
+       0xe2d9af76U, 0xbb6bd6bdU, 0x4182199bU, 0x6edca579U,
+       0xa557aef9U, 0xcb8b0b80U, 0x6bd6b167U, 0x95376e59U,
+       0xa15fbee1U, 0xf3fbeb10U, 0xb17ffe81U, 0x0204080cU,
+       0xcc851792U, 0xc49537a2U, 0x1d3a744eU, 0x14285078U,
+       0xc39b2bb0U, 0x63c69157U, 0xdaa94fe6U, 0x5dba69d3U,
+       0x5fbe61dfU, 0xdca557f2U, 0x7dfae913U, 0xcd871394U,
+       0x7ffee11fU, 0x5ab475c1U, 0x6cd8ad75U, 0x5cb86dd5U,
+       0xf7f3fb08U, 0x264c98d4U, 0xffe3db38U, 0xedc79354U,
+       0xe8cd874aU, 0x9d274e69U, 0x6fdea17fU, 0x8e010203U,
+       0x19326456U, 0xa05dbae7U, 0xf0fde71aU, 0x890f1e11U,
+       0x0f1e3c22U, 0x070e1c12U, 0xaf4386c5U, 0xfbebcb20U,
+       0x08102030U, 0x152a547eU, 0x0d1a342eU, 0x04081018U,
+       0x01020406U, 0x64c88d45U, 0xdfa35bf8U, 0x76ecc529U,
+       0x79f2f90bU, 0xdda753f4U, 0x3d7af48eU, 0x162c5874U,
+       0x3f7efc82U, 0x376edcb2U, 0x6ddaa973U, 0x3870e090U,
+       0xb96fdeb1U, 0x73e6d137U, 0xe9cf834cU, 0x356ad4beU,
+       0x55aa49e3U, 0x71e2d93bU, 0x7bf6f107U, 0x8c050a0fU,
+       0x72e4d531U, 0x880d1a17U, 0xf6f1ff0eU, 0x2a54a8fcU,
+       0x3e7cf884U, 0x5ebc65d9U, 0x274e9cd2U, 0x468c0589U,
+       0x0c183028U, 0x65ca8943U, 0x68d0bd6dU, 0x61c2995bU,
+       0x03060c0aU, 0xc19f23bcU, 0x57ae41efU, 0xd6b17fceU,
+       0xd9af43ecU, 0x58b07dcdU, 0xd8ad47eaU, 0x66cc8549U,
+       0xd7b37bc8U, 0x3a74e89cU, 0xc88d078aU, 0x3c78f088U,
+       0xfae9cf26U, 0x96316253U, 0xa753a6f5U, 0x982d5a77U,
+       0xecc59752U, 0xb86ddab7U, 0xc7933ba8U, 0xae4182c3U,
+       0x69d2b96bU, 0x4b9631a7U, 0xab4b96ddU, 0xa94f9ed1U,
+       0x67ce814fU, 0x0a14283cU, 0x478e018fU, 0xf2f9ef16U,
+       0xb577ee99U, 0x224488ccU, 0xe5d7b364U, 0xeec19f5eU,
+       0xbe61c2a3U, 0x2b56acfaU, 0x811f3e21U, 0x1224486cU,
+       0x831b362dU, 0x1b366c5aU, 0x0e1c3824U, 0x23468ccaU,
+       0xf5f7f304U, 0x458a0983U, 0x214284c6U, 0xce811f9eU,
+       0x499239abU, 0x2c58b0e8U, 0xf9efc32cU, 0xe6d1bf6eU,
+       0xb671e293U, 0x2850a0f0U, 0x172e5c72U, 0x8219322bU,
+       0x1a34685cU, 0x8b0b161dU, 0xfee1df3eU, 0x8a09121bU,
+       0x09122436U, 0xc98f038cU, 0x87132635U, 0x4e9c25b9U,
+       0xe1dfa37cU, 0x2e5cb8e4U, 0xe4d5b762U, 0xe0dda77aU,
+       0xebcb8b40U, 0x903d7a47U, 0xa455aaffU, 0x1e3c7844U,
+       0x85172e39U, 0x60c09d5dU, 0x00000000U, 0x254a94deU,
+       0xf4f5f702U, 0xf1ffe31cU, 0x94356a5fU, 0x0b162c3aU,
+       0xe7d3bb68U, 0x75eac923U, 0xefc39b58U, 0x3468d0b8U,
+       0x3162c4a6U, 0xd4b577c2U, 0xd0bd67daU, 0x86112233U,
+       0x7efce519U, 0xad478ec9U, 0xfde7d334U, 0x2952a4f6U,
+       0x3060c0a0U, 0x3b76ec9aU, 0x9f234665U, 0xf8edc72aU,
+       0xc6913faeU, 0x13264c6aU, 0x060c1814U, 0x050a141eU,
+       0xc59733a4U, 0x11224466U, 0x77eec12fU, 0x7cf8ed15U,
+       0x7af4f501U, 0x78f0fd0dU, 0x366cd8b4U, 0x1c387048U,
+       0x3972e496U, 0x59b279cbU, 0x18306050U, 0x56ac45e9U,
+       0xb37bf68dU, 0xb07dfa87U, 0x244890d8U, 0x204080c0U,
+       0xb279f28bU, 0x9239724bU, 0xa35bb6edU, 0xc09d27baU,
+       0x44880d85U, 0x62c49551U, 0x10204060U, 0xb475ea9fU,
+       0x84152a3fU, 0x43861197U, 0x933b764dU, 0xc2992fb6U,
+       0x4a9435a1U, 0xbd67cea9U, 0x8f030605U, 0x2d5ab4eeU,
+       0xbc65caafU, 0x9c254a6fU, 0x6ad4b561U, 0x40801d9dU,
+       0xcf831b98U, 0xa259b2ebU, 0x801d3a27U, 0x4f9e21bfU,
+       0x1f3e7c42U, 0xca890f86U, 0xaa4992dbU, 0x42841591U,
+};
+
+static const u32 T1[256] = {
+       0x69babbd2U, 0xa854e54dU, 0x5e2fe2bcU, 0xe87425cdU,
+       0xa653f751U, 0xbbd3d06bU, 0xb9d2d66fU, 0x9a4db329U,
+       0xa050fd5dU, 0x45accf8aU, 0x078d090eU, 0x63bfa5c6U,
+       0xe0703dddU, 0xa452f155U, 0x299a7b52U, 0x984cb52dU,
+       0xc9ea468fU, 0xb7d5c473U, 0x33975566U, 0xbfd1dc63U,
+       0x6633aaccU, 0xa251fb59U, 0xb65bc771U, 0x51a6f3a2U,
+       0xa1defe5fU, 0x9048ad3dU, 0x4da8d79aU, 0x2f99715eU,
+       0xabdbe04bU, 0x6432acc8U, 0x73b795e6U, 0xe5fc32d7U,
+       0xdbe370abU, 0x219e6342U, 0x3f91417eU, 0x2b9b7d56U,
+       0xd9e276afU, 0x6bbbbdd6U, 0x82419b19U, 0xdc6e79a5U,
+       0x57a5f9aeU, 0x8bcb800bU, 0xd66b67b1U, 0x3795596eU,
+       0x5fa1e1beU, 0xfbf310ebU, 0x7fb181feU, 0x04020c08U,
+       0x85cc9217U, 0x95c4a237U, 0x3a1d4e74U, 0x28147850U,
+       0x9bc3b02bU, 0xc6635791U, 0xa9dae64fU, 0xba5dd369U,
+       0xbe5fdf61U, 0xa5dcf257U, 0xfa7d13e9U, 0x87cd9413U,
+       0xfe7f1fe1U, 0xb45ac175U, 0xd86c75adU, 0xb85cd56dU,
+       0xf3f708fbU, 0x4c26d498U, 0xe3ff38dbU, 0xc7ed5493U,
+       0xcde84a87U, 0x279d694eU, 0xde6f7fa1U, 0x018e0302U,
+       0x32195664U, 0x5da0e7baU, 0xfdf01ae7U, 0x0f89111eU,
+       0x1e0f223cU, 0x0e07121cU, 0x43afc586U, 0xebfb20cbU,
+       0x10083020U, 0x2a157e54U, 0x1a0d2e34U, 0x08041810U,
+       0x02010604U, 0xc864458dU, 0xa3dff85bU, 0xec7629c5U,
+       0xf2790bf9U, 0xa7ddf453U, 0x7a3d8ef4U, 0x2c167458U,
+       0x7e3f82fcU, 0x6e37b2dcU, 0xda6d73a9U, 0x703890e0U,
+       0x6fb9b1deU, 0xe67337d1U, 0xcfe94c83U, 0x6a35bed4U,
+       0xaa55e349U, 0xe2713bd9U, 0xf67b07f1U, 0x058c0f0aU,
+       0xe47231d5U, 0x0d88171aU, 0xf1f60effU, 0x542afca8U,
+       0x7c3e84f8U, 0xbc5ed965U, 0x4e27d29cU, 0x8c468905U,
+       0x180c2830U, 0xca654389U, 0xd0686dbdU, 0xc2615b99U,
+       0x06030a0cU, 0x9fc1bc23U, 0xae57ef41U, 0xb1d6ce7fU,
+       0xafd9ec43U, 0xb058cd7dU, 0xadd8ea47U, 0xcc664985U,
+       0xb3d7c87bU, 0x743a9ce8U, 0x8dc88a07U, 0x783c88f0U,
+       0xe9fa26cfU, 0x31965362U, 0x53a7f5a6U, 0x2d98775aU,
+       0xc5ec5297U, 0x6db8b7daU, 0x93c7a83bU, 0x41aec382U,
+       0xd2696bb9U, 0x964ba731U, 0x4babdd96U, 0x4fa9d19eU,
+       0xce674f81U, 0x140a3c28U, 0x8e478f01U, 0xf9f216efU,
+       0x77b599eeU, 0x4422cc88U, 0xd7e564b3U, 0xc1ee5e9fU,
+       0x61bea3c2U, 0x562bfaacU, 0x1f81213eU, 0x24126c48U,
+       0x1b832d36U, 0x361b5a6cU, 0x1c0e2438U, 0x4623ca8cU,
+       0xf7f504f3U, 0x8a458309U, 0x4221c684U, 0x81ce9e1fU,
+       0x9249ab39U, 0x582ce8b0U, 0xeff92cc3U, 0xd1e66ebfU,
+       0x71b693e2U, 0x5028f0a0U, 0x2e17725cU, 0x19822b32U,
+       0x341a5c68U, 0x0b8b1d16U, 0xe1fe3edfU, 0x098a1b12U,
+       0x12093624U, 0x8fc98c03U, 0x13873526U, 0x9c4eb925U,
+       0xdfe17ca3U, 0x5c2ee4b8U, 0xd5e462b7U, 0xdde07aa7U,
+       0xcbeb408bU, 0x3d90477aU, 0x55a4ffaaU, 0x3c1e4478U,
+       0x1785392eU, 0xc0605d9dU, 0x00000000U, 0x4a25de94U,
+       0xf5f402f7U, 0xfff11ce3U, 0x35945f6aU, 0x160b3a2cU,
+       0xd3e768bbU, 0xea7523c9U, 0xc3ef589bU, 0x6834b8d0U,
+       0x6231a6c4U, 0xb5d4c277U, 0xbdd0da67U, 0x11863322U,
+       0xfc7e19e5U, 0x47adc98eU, 0xe7fd34d3U, 0x5229f6a4U,
+       0x6030a0c0U, 0x763b9aecU, 0x239f6546U, 0xedf82ac7U,
+       0x91c6ae3fU, 0x26136a4cU, 0x0c061418U, 0x0a051e14U,
+       0x97c5a433U, 0x22116644U, 0xee772fc1U, 0xf87c15edU,
+       0xf47a01f5U, 0xf0780dfdU, 0x6c36b4d8U, 0x381c4870U,
+       0x723996e4U, 0xb259cb79U, 0x30185060U, 0xac56e945U,
+       0x7bb38df6U, 0x7db087faU, 0x4824d890U, 0x4020c080U,
+       0x79b28bf2U, 0x39924b72U, 0x5ba3edb6U, 0x9dc0ba27U,
+       0x8844850dU, 0xc4625195U, 0x20106040U, 0x75b49feaU,
+       0x15843f2aU, 0x86439711U, 0x3b934d76U, 0x99c2b62fU,
+       0x944aa135U, 0x67bda9ceU, 0x038f0506U, 0x5a2deeb4U,
+       0x65bcafcaU, 0x259c6f4aU, 0xd46a61b5U, 0x80409d1dU,
+       0x83cf981bU, 0x59a2ebb2U, 0x1d80273aU, 0x9e4fbf21U,
+       0x3e1f427cU, 0x89ca860fU, 0x49aadb92U, 0x84429115U,
+};
+
+static const u32 T2[256] = {
+       0xd2bbba69U, 0x4de554a8U, 0xbce22f5eU, 0xcd2574e8U,
+       0x51f753a6U, 0x6bd0d3bbU, 0x6fd6d2b9U, 0x29b34d9aU,
+       0x5dfd50a0U, 0x8acfac45U, 0x0e098d07U, 0xc6a5bf63U,
+       0xdd3d70e0U, 0x55f152a4U, 0x527b9a29U, 0x2db54c98U,
+       0x8f46eac9U, 0x73c4d5b7U, 0x66559733U, 0x63dcd1bfU,
+       0xccaa3366U, 0x59fb51a2U, 0x71c75bb6U, 0xa2f3a651U,
+       0x5ffedea1U, 0x3dad4890U, 0x9ad7a84dU, 0x5e71992fU,
+       0x4be0dbabU, 0xc8ac3264U, 0xe695b773U, 0xd732fce5U,
+       0xab70e3dbU, 0x42639e21U, 0x7e41913fU, 0x567d9b2bU,
+       0xaf76e2d9U, 0xd6bdbb6bU, 0x199b4182U, 0xa5796edcU,
+       0xaef9a557U, 0x0b80cb8bU, 0xb1676bd6U, 0x6e599537U,
+       0xbee1a15fU, 0xeb10f3fbU, 0xfe81b17fU, 0x080c0204U,
+       0x1792cc85U, 0x37a2c495U, 0x744e1d3aU, 0x50781428U,
+       0x2bb0c39bU, 0x915763c6U, 0x4fe6daa9U, 0x69d35dbaU,
+       0x61df5fbeU, 0x57f2dca5U, 0xe9137dfaU, 0x1394cd87U,
+       0xe11f7ffeU, 0x75c15ab4U, 0xad756cd8U, 0x6dd55cb8U,
+       0xfb08f7f3U, 0x98d4264cU, 0xdb38ffe3U, 0x9354edc7U,
+       0x874ae8cdU, 0x4e699d27U, 0xa17f6fdeU, 0x02038e01U,
+       0x64561932U, 0xbae7a05dU, 0xe71af0fdU, 0x1e11890fU,
+       0x3c220f1eU, 0x1c12070eU, 0x86c5af43U, 0xcb20fbebU,
+       0x20300810U, 0x547e152aU, 0x342e0d1aU, 0x10180408U,
+       0x04060102U, 0x8d4564c8U, 0x5bf8dfa3U, 0xc52976ecU,
+       0xf90b79f2U, 0x53f4dda7U, 0xf48e3d7aU, 0x5874162cU,
+       0xfc823f7eU, 0xdcb2376eU, 0xa9736ddaU, 0xe0903870U,
+       0xdeb1b96fU, 0xd13773e6U, 0x834ce9cfU, 0xd4be356aU,
+       0x49e355aaU, 0xd93b71e2U, 0xf1077bf6U, 0x0a0f8c05U,
+       0xd53172e4U, 0x1a17880dU, 0xff0ef6f1U, 0xa8fc2a54U,
+       0xf8843e7cU, 0x65d95ebcU, 0x9cd2274eU, 0x0589468cU,
+       0x30280c18U, 0x894365caU, 0xbd6d68d0U, 0x995b61c2U,
+       0x0c0a0306U, 0x23bcc19fU, 0x41ef57aeU, 0x7fced6b1U,
+       0x43ecd9afU, 0x7dcd58b0U, 0x47ead8adU, 0x854966ccU,
+       0x7bc8d7b3U, 0xe89c3a74U, 0x078ac88dU, 0xf0883c78U,
+       0xcf26fae9U, 0x62539631U, 0xa6f5a753U, 0x5a77982dU,
+       0x9752ecc5U, 0xdab7b86dU, 0x3ba8c793U, 0x82c3ae41U,
+       0xb96b69d2U, 0x31a74b96U, 0x96ddab4bU, 0x9ed1a94fU,
+       0x814f67ceU, 0x283c0a14U, 0x018f478eU, 0xef16f2f9U,
+       0xee99b577U, 0x88cc2244U, 0xb364e5d7U, 0x9f5eeec1U,
+       0xc2a3be61U, 0xacfa2b56U, 0x3e21811fU, 0x486c1224U,
+       0x362d831bU, 0x6c5a1b36U, 0x38240e1cU, 0x8cca2346U,
+       0xf304f5f7U, 0x0983458aU, 0x84c62142U, 0x1f9ece81U,
+       0x39ab4992U, 0xb0e82c58U, 0xc32cf9efU, 0xbf6ee6d1U,
+       0xe293b671U, 0xa0f02850U, 0x5c72172eU, 0x322b8219U,
+       0x685c1a34U, 0x161d8b0bU, 0xdf3efee1U, 0x121b8a09U,
+       0x24360912U, 0x038cc98fU, 0x26358713U, 0x25b94e9cU,
+       0xa37ce1dfU, 0xb8e42e5cU, 0xb762e4d5U, 0xa77ae0ddU,
+       0x8b40ebcbU, 0x7a47903dU, 0xaaffa455U, 0x78441e3cU,
+       0x2e398517U, 0x9d5d60c0U, 0x00000000U, 0x94de254aU,
+       0xf702f4f5U, 0xe31cf1ffU, 0x6a5f9435U, 0x2c3a0b16U,
+       0xbb68e7d3U, 0xc92375eaU, 0x9b58efc3U, 0xd0b83468U,
+       0xc4a63162U, 0x77c2d4b5U, 0x67dad0bdU, 0x22338611U,
+       0xe5197efcU, 0x8ec9ad47U, 0xd334fde7U, 0xa4f62952U,
+       0xc0a03060U, 0xec9a3b76U, 0x46659f23U, 0xc72af8edU,
+       0x3faec691U, 0x4c6a1326U, 0x1814060cU, 0x141e050aU,
+       0x33a4c597U, 0x44661122U, 0xc12f77eeU, 0xed157cf8U,
+       0xf5017af4U, 0xfd0d78f0U, 0xd8b4366cU, 0x70481c38U,
+       0xe4963972U, 0x79cb59b2U, 0x60501830U, 0x45e956acU,
+       0xf68db37bU, 0xfa87b07dU, 0x90d82448U, 0x80c02040U,
+       0xf28bb279U, 0x724b9239U, 0xb6eda35bU, 0x27bac09dU,
+       0x0d854488U, 0x955162c4U, 0x40601020U, 0xea9fb475U,
+       0x2a3f8415U, 0x11974386U, 0x764d933bU, 0x2fb6c299U,
+       0x35a14a94U, 0xcea9bd67U, 0x06058f03U, 0xb4ee2d5aU,
+       0xcaafbc65U, 0x4a6f9c25U, 0xb5616ad4U, 0x1d9d4080U,
+       0x1b98cf83U, 0xb2eba259U, 0x3a27801dU, 0x21bf4f9eU,
+       0x7c421f3eU, 0x0f86ca89U, 0x92dbaa49U, 0x15914284U,
+};
+
+static const u32 T3[256] = {
+       0xbbd269baU, 0xe54da854U, 0xe2bc5e2fU, 0x25cde874U,
+       0xf751a653U, 0xd06bbbd3U, 0xd66fb9d2U, 0xb3299a4dU,
+       0xfd5da050U, 0xcf8a45acU, 0x090e078dU, 0xa5c663bfU,
+       0x3ddde070U, 0xf155a452U, 0x7b52299aU, 0xb52d984cU,
+       0x468fc9eaU, 0xc473b7d5U, 0x55663397U, 0xdc63bfd1U,
+       0xaacc6633U, 0xfb59a251U, 0xc771b65bU, 0xf3a251a6U,
+       0xfe5fa1deU, 0xad3d9048U, 0xd79a4da8U, 0x715e2f99U,
+       0xe04babdbU, 0xacc86432U, 0x95e673b7U, 0x32d7e5fcU,
+       0x70abdbe3U, 0x6342219eU, 0x417e3f91U, 0x7d562b9bU,
+       0x76afd9e2U, 0xbdd66bbbU, 0x9b198241U, 0x79a5dc6eU,
+       0xf9ae57a5U, 0x800b8bcbU, 0x67b1d66bU, 0x596e3795U,
+       0xe1be5fa1U, 0x10ebfbf3U, 0x81fe7fb1U, 0x0c080402U,
+       0x921785ccU, 0xa23795c4U, 0x4e743a1dU, 0x78502814U,
+       0xb02b9bc3U, 0x5791c663U, 0xe64fa9daU, 0xd369ba5dU,
+       0xdf61be5fU, 0xf257a5dcU, 0x13e9fa7dU, 0x941387cdU,
+       0x1fe1fe7fU, 0xc175b45aU, 0x75add86cU, 0xd56db85cU,
+       0x08fbf3f7U, 0xd4984c26U, 0x38dbe3ffU, 0x5493c7edU,
+       0x4a87cde8U, 0x694e279dU, 0x7fa1de6fU, 0x0302018eU,
+       0x56643219U, 0xe7ba5da0U, 0x1ae7fdf0U, 0x111e0f89U,
+       0x223c1e0fU, 0x121c0e07U, 0xc58643afU, 0x20cbebfbU,
+       0x30201008U, 0x7e542a15U, 0x2e341a0dU, 0x18100804U,
+       0x06040201U, 0x458dc864U, 0xf85ba3dfU, 0x29c5ec76U,
+       0x0bf9f279U, 0xf453a7ddU, 0x8ef47a3dU, 0x74582c16U,
+       0x82fc7e3fU, 0xb2dc6e37U, 0x73a9da6dU, 0x90e07038U,
+       0xb1de6fb9U, 0x37d1e673U, 0x4c83cfe9U, 0xbed46a35U,
+       0xe349aa55U, 0x3bd9e271U, 0x07f1f67bU, 0x0f0a058cU,
+       0x31d5e472U, 0x171a0d88U, 0x0efff1f6U, 0xfca8542aU,
+       0x84f87c3eU, 0xd965bc5eU, 0xd29c4e27U, 0x89058c46U,
+       0x2830180cU, 0x4389ca65U, 0x6dbdd068U, 0x5b99c261U,
+       0x0a0c0603U, 0xbc239fc1U, 0xef41ae57U, 0xce7fb1d6U,
+       0xec43afd9U, 0xcd7db058U, 0xea47add8U, 0x4985cc66U,
+       0xc87bb3d7U, 0x9ce8743aU, 0x8a078dc8U, 0x88f0783cU,
+       0x26cfe9faU, 0x53623196U, 0xf5a653a7U, 0x775a2d98U,
+       0x5297c5ecU, 0xb7da6db8U, 0xa83b93c7U, 0xc38241aeU,
+       0x6bb9d269U, 0xa731964bU, 0xdd964babU, 0xd19e4fa9U,
+       0x4f81ce67U, 0x3c28140aU, 0x8f018e47U, 0x16eff9f2U,
+       0x99ee77b5U, 0xcc884422U, 0x64b3d7e5U, 0x5e9fc1eeU,
+       0xa3c261beU, 0xfaac562bU, 0x213e1f81U, 0x6c482412U,
+       0x2d361b83U, 0x5a6c361bU, 0x24381c0eU, 0xca8c4623U,
+       0x04f3f7f5U, 0x83098a45U, 0xc6844221U, 0x9e1f81ceU,
+       0xab399249U, 0xe8b0582cU, 0x2cc3eff9U, 0x6ebfd1e6U,
+       0x93e271b6U, 0xf0a05028U, 0x725c2e17U, 0x2b321982U,
+       0x5c68341aU, 0x1d160b8bU, 0x3edfe1feU, 0x1b12098aU,
+       0x36241209U, 0x8c038fc9U, 0x35261387U, 0xb9259c4eU,
+       0x7ca3dfe1U, 0xe4b85c2eU, 0x62b7d5e4U, 0x7aa7dde0U,
+       0x408bcbebU, 0x477a3d90U, 0xffaa55a4U, 0x44783c1eU,
+       0x392e1785U, 0x5d9dc060U, 0x00000000U, 0xde944a25U,
+       0x02f7f5f4U, 0x1ce3fff1U, 0x5f6a3594U, 0x3a2c160bU,
+       0x68bbd3e7U, 0x23c9ea75U, 0x589bc3efU, 0xb8d06834U,
+       0xa6c46231U, 0xc277b5d4U, 0xda67bdd0U, 0x33221186U,
+       0x19e5fc7eU, 0xc98e47adU, 0x34d3e7fdU, 0xf6a45229U,
+       0xa0c06030U, 0x9aec763bU, 0x6546239fU, 0x2ac7edf8U,
+       0xae3f91c6U, 0x6a4c2613U, 0x14180c06U, 0x1e140a05U,
+       0xa43397c5U, 0x66442211U, 0x2fc1ee77U, 0x15edf87cU,
+       0x01f5f47aU, 0x0dfdf078U, 0xb4d86c36U, 0x4870381cU,
+       0x96e47239U, 0xcb79b259U, 0x50603018U, 0xe945ac56U,
+       0x8df67bb3U, 0x87fa7db0U, 0xd8904824U, 0xc0804020U,
+       0x8bf279b2U, 0x4b723992U, 0xedb65ba3U, 0xba279dc0U,
+       0x850d8844U, 0x5195c462U, 0x60402010U, 0x9fea75b4U,
+       0x3f2a1584U, 0x97118643U, 0x4d763b93U, 0xb62f99c2U,
+       0xa135944aU, 0xa9ce67bdU, 0x0506038fU, 0xeeb45a2dU,
+       0xafca65bcU, 0x6f4a259cU, 0x61b5d46aU, 0x9d1d8040U,
+       0x981b83cfU, 0xebb259a2U, 0x273a1d80U, 0xbf219e4fU,
+       0x427c3e1fU, 0x860f89caU, 0xdb9249aaU, 0x91158442U,
+};
+
+static const u32 T4[256] = {
+       0xbabababaU, 0x54545454U, 0x2f2f2f2fU, 0x74747474U,
+       0x53535353U, 0xd3d3d3d3U, 0xd2d2d2d2U, 0x4d4d4d4dU,
+       0x50505050U, 0xacacacacU, 0x8d8d8d8dU, 0xbfbfbfbfU,
+       0x70707070U, 0x52525252U, 0x9a9a9a9aU, 0x4c4c4c4cU,
+       0xeaeaeaeaU, 0xd5d5d5d5U, 0x97979797U, 0xd1d1d1d1U,
+       0x33333333U, 0x51515151U, 0x5b5b5b5bU, 0xa6a6a6a6U,
+       0xdedededeU, 0x48484848U, 0xa8a8a8a8U, 0x99999999U,
+       0xdbdbdbdbU, 0x32323232U, 0xb7b7b7b7U, 0xfcfcfcfcU,
+       0xe3e3e3e3U, 0x9e9e9e9eU, 0x91919191U, 0x9b9b9b9bU,
+       0xe2e2e2e2U, 0xbbbbbbbbU, 0x41414141U, 0x6e6e6e6eU,
+       0xa5a5a5a5U, 0xcbcbcbcbU, 0x6b6b6b6bU, 0x95959595U,
+       0xa1a1a1a1U, 0xf3f3f3f3U, 0xb1b1b1b1U, 0x02020202U,
+       0xccccccccU, 0xc4c4c4c4U, 0x1d1d1d1dU, 0x14141414U,
+       0xc3c3c3c3U, 0x63636363U, 0xdadadadaU, 0x5d5d5d5dU,
+       0x5f5f5f5fU, 0xdcdcdcdcU, 0x7d7d7d7dU, 0xcdcdcdcdU,
+       0x7f7f7f7fU, 0x5a5a5a5aU, 0x6c6c6c6cU, 0x5c5c5c5cU,
+       0xf7f7f7f7U, 0x26262626U, 0xffffffffU, 0xededededU,
+       0xe8e8e8e8U, 0x9d9d9d9dU, 0x6f6f6f6fU, 0x8e8e8e8eU,
+       0x19191919U, 0xa0a0a0a0U, 0xf0f0f0f0U, 0x89898989U,
+       0x0f0f0f0fU, 0x07070707U, 0xafafafafU, 0xfbfbfbfbU,
+       0x08080808U, 0x15151515U, 0x0d0d0d0dU, 0x04040404U,
+       0x01010101U, 0x64646464U, 0xdfdfdfdfU, 0x76767676U,
+       0x79797979U, 0xddddddddU, 0x3d3d3d3dU, 0x16161616U,
+       0x3f3f3f3fU, 0x37373737U, 0x6d6d6d6dU, 0x38383838U,
+       0xb9b9b9b9U, 0x73737373U, 0xe9e9e9e9U, 0x35353535U,
+       0x55555555U, 0x71717171U, 0x7b7b7b7bU, 0x8c8c8c8cU,
+       0x72727272U, 0x88888888U, 0xf6f6f6f6U, 0x2a2a2a2aU,
+       0x3e3e3e3eU, 0x5e5e5e5eU, 0x27272727U, 0x46464646U,
+       0x0c0c0c0cU, 0x65656565U, 0x68686868U, 0x61616161U,
+       0x03030303U, 0xc1c1c1c1U, 0x57575757U, 0xd6d6d6d6U,
+       0xd9d9d9d9U, 0x58585858U, 0xd8d8d8d8U, 0x66666666U,
+       0xd7d7d7d7U, 0x3a3a3a3aU, 0xc8c8c8c8U, 0x3c3c3c3cU,
+       0xfafafafaU, 0x96969696U, 0xa7a7a7a7U, 0x98989898U,
+       0xececececU, 0xb8b8b8b8U, 0xc7c7c7c7U, 0xaeaeaeaeU,
+       0x69696969U, 0x4b4b4b4bU, 0xababababU, 0xa9a9a9a9U,
+       0x67676767U, 0x0a0a0a0aU, 0x47474747U, 0xf2f2f2f2U,
+       0xb5b5b5b5U, 0x22222222U, 0xe5e5e5e5U, 0xeeeeeeeeU,
+       0xbebebebeU, 0x2b2b2b2bU, 0x81818181U, 0x12121212U,
+       0x83838383U, 0x1b1b1b1bU, 0x0e0e0e0eU, 0x23232323U,
+       0xf5f5f5f5U, 0x45454545U, 0x21212121U, 0xcecececeU,
+       0x49494949U, 0x2c2c2c2cU, 0xf9f9f9f9U, 0xe6e6e6e6U,
+       0xb6b6b6b6U, 0x28282828U, 0x17171717U, 0x82828282U,
+       0x1a1a1a1aU, 0x8b8b8b8bU, 0xfefefefeU, 0x8a8a8a8aU,
+       0x09090909U, 0xc9c9c9c9U, 0x87878787U, 0x4e4e4e4eU,
+       0xe1e1e1e1U, 0x2e2e2e2eU, 0xe4e4e4e4U, 0xe0e0e0e0U,
+       0xebebebebU, 0x90909090U, 0xa4a4a4a4U, 0x1e1e1e1eU,
+       0x85858585U, 0x60606060U, 0x00000000U, 0x25252525U,
+       0xf4f4f4f4U, 0xf1f1f1f1U, 0x94949494U, 0x0b0b0b0bU,
+       0xe7e7e7e7U, 0x75757575U, 0xefefefefU, 0x34343434U,
+       0x31313131U, 0xd4d4d4d4U, 0xd0d0d0d0U, 0x86868686U,
+       0x7e7e7e7eU, 0xadadadadU, 0xfdfdfdfdU, 0x29292929U,
+       0x30303030U, 0x3b3b3b3bU, 0x9f9f9f9fU, 0xf8f8f8f8U,
+       0xc6c6c6c6U, 0x13131313U, 0x06060606U, 0x05050505U,
+       0xc5c5c5c5U, 0x11111111U, 0x77777777U, 0x7c7c7c7cU,
+       0x7a7a7a7aU, 0x78787878U, 0x36363636U, 0x1c1c1c1cU,
+       0x39393939U, 0x59595959U, 0x18181818U, 0x56565656U,
+       0xb3b3b3b3U, 0xb0b0b0b0U, 0x24242424U, 0x20202020U,
+       0xb2b2b2b2U, 0x92929292U, 0xa3a3a3a3U, 0xc0c0c0c0U,
+       0x44444444U, 0x62626262U, 0x10101010U, 0xb4b4b4b4U,
+       0x84848484U, 0x43434343U, 0x93939393U, 0xc2c2c2c2U,
+       0x4a4a4a4aU, 0xbdbdbdbdU, 0x8f8f8f8fU, 0x2d2d2d2dU,
+       0xbcbcbcbcU, 0x9c9c9c9cU, 0x6a6a6a6aU, 0x40404040U,
+       0xcfcfcfcfU, 0xa2a2a2a2U, 0x80808080U, 0x4f4f4f4fU,
+       0x1f1f1f1fU, 0xcacacacaU, 0xaaaaaaaaU, 0x42424242U,
+};
+
+static const u32 T5[256] = {
+       0x00000000U, 0x01020608U, 0x02040c10U, 0x03060a18U,
+       0x04081820U, 0x050a1e28U, 0x060c1430U, 0x070e1238U,
+       0x08103040U, 0x09123648U, 0x0a143c50U, 0x0b163a58U,
+       0x0c182860U, 0x0d1a2e68U, 0x0e1c2470U, 0x0f1e2278U,
+       0x10206080U, 0x11226688U, 0x12246c90U, 0x13266a98U,
+       0x142878a0U, 0x152a7ea8U, 0x162c74b0U, 0x172e72b8U,
+       0x183050c0U, 0x193256c8U, 0x1a345cd0U, 0x1b365ad8U,
+       0x1c3848e0U, 0x1d3a4ee8U, 0x1e3c44f0U, 0x1f3e42f8U,
+       0x2040c01dU, 0x2142c615U, 0x2244cc0dU, 0x2346ca05U,
+       0x2448d83dU, 0x254ade35U, 0x264cd42dU, 0x274ed225U,
+       0x2850f05dU, 0x2952f655U, 0x2a54fc4dU, 0x2b56fa45U,
+       0x2c58e87dU, 0x2d5aee75U, 0x2e5ce46dU, 0x2f5ee265U,
+       0x3060a09dU, 0x3162a695U, 0x3264ac8dU, 0x3366aa85U,
+       0x3468b8bdU, 0x356abeb5U, 0x366cb4adU, 0x376eb2a5U,
+       0x387090ddU, 0x397296d5U, 0x3a749ccdU, 0x3b769ac5U,
+       0x3c7888fdU, 0x3d7a8ef5U, 0x3e7c84edU, 0x3f7e82e5U,
+       0x40809d3aU, 0x41829b32U, 0x4284912aU, 0x43869722U,
+       0x4488851aU, 0x458a8312U, 0x468c890aU, 0x478e8f02U,
+       0x4890ad7aU, 0x4992ab72U, 0x4a94a16aU, 0x4b96a762U,
+       0x4c98b55aU, 0x4d9ab352U, 0x4e9cb94aU, 0x4f9ebf42U,
+       0x50a0fdbaU, 0x51a2fbb2U, 0x52a4f1aaU, 0x53a6f7a2U,
+       0x54a8e59aU, 0x55aae392U, 0x56ace98aU, 0x57aeef82U,
+       0x58b0cdfaU, 0x59b2cbf2U, 0x5ab4c1eaU, 0x5bb6c7e2U,
+       0x5cb8d5daU, 0x5dbad3d2U, 0x5ebcd9caU, 0x5fbedfc2U,
+       0x60c05d27U, 0x61c25b2fU, 0x62c45137U, 0x63c6573fU,
+       0x64c84507U, 0x65ca430fU, 0x66cc4917U, 0x67ce4f1fU,
+       0x68d06d67U, 0x69d26b6fU, 0x6ad46177U, 0x6bd6677fU,
+       0x6cd87547U, 0x6dda734fU, 0x6edc7957U, 0x6fde7f5fU,
+       0x70e03da7U, 0x71e23bafU, 0x72e431b7U, 0x73e637bfU,
+       0x74e82587U, 0x75ea238fU, 0x76ec2997U, 0x77ee2f9fU,
+       0x78f00de7U, 0x79f20befU, 0x7af401f7U, 0x7bf607ffU,
+       0x7cf815c7U, 0x7dfa13cfU, 0x7efc19d7U, 0x7ffe1fdfU,
+       0x801d2774U, 0x811f217cU, 0x82192b64U, 0x831b2d6cU,
+       0x84153f54U, 0x8517395cU, 0x86113344U, 0x8713354cU,
+       0x880d1734U, 0x890f113cU, 0x8a091b24U, 0x8b0b1d2cU,
+       0x8c050f14U, 0x8d07091cU, 0x8e010304U, 0x8f03050cU,
+       0x903d47f4U, 0x913f41fcU, 0x92394be4U, 0x933b4decU,
+       0x94355fd4U, 0x953759dcU, 0x963153c4U, 0x973355ccU,
+       0x982d77b4U, 0x992f71bcU, 0x9a297ba4U, 0x9b2b7dacU,
+       0x9c256f94U, 0x9d27699cU, 0x9e216384U, 0x9f23658cU,
+       0xa05de769U, 0xa15fe161U, 0xa259eb79U, 0xa35bed71U,
+       0xa455ff49U, 0xa557f941U, 0xa651f359U, 0xa753f551U,
+       0xa84dd729U, 0xa94fd121U, 0xaa49db39U, 0xab4bdd31U,
+       0xac45cf09U, 0xad47c901U, 0xae41c319U, 0xaf43c511U,
+       0xb07d87e9U, 0xb17f81e1U, 0xb2798bf9U, 0xb37b8df1U,
+       0xb4759fc9U, 0xb57799c1U, 0xb67193d9U, 0xb77395d1U,
+       0xb86db7a9U, 0xb96fb1a1U, 0xba69bbb9U, 0xbb6bbdb1U,
+       0xbc65af89U, 0xbd67a981U, 0xbe61a399U, 0xbf63a591U,
+       0xc09dba4eU, 0xc19fbc46U, 0xc299b65eU, 0xc39bb056U,
+       0xc495a26eU, 0xc597a466U, 0xc691ae7eU, 0xc793a876U,
+       0xc88d8a0eU, 0xc98f8c06U, 0xca89861eU, 0xcb8b8016U,
+       0xcc85922eU, 0xcd879426U, 0xce819e3eU, 0xcf839836U,
+       0xd0bddaceU, 0xd1bfdcc6U, 0xd2b9d6deU, 0xd3bbd0d6U,
+       0xd4b5c2eeU, 0xd5b7c4e6U, 0xd6b1cefeU, 0xd7b3c8f6U,
+       0xd8adea8eU, 0xd9afec86U, 0xdaa9e69eU, 0xdbabe096U,
+       0xdca5f2aeU, 0xdda7f4a6U, 0xdea1febeU, 0xdfa3f8b6U,
+       0xe0dd7a53U, 0xe1df7c5bU, 0xe2d97643U, 0xe3db704bU,
+       0xe4d56273U, 0xe5d7647bU, 0xe6d16e63U, 0xe7d3686bU,
+       0xe8cd4a13U, 0xe9cf4c1bU, 0xeac94603U, 0xebcb400bU,
+       0xecc55233U, 0xedc7543bU, 0xeec15e23U, 0xefc3582bU,
+       0xf0fd1ad3U, 0xf1ff1cdbU, 0xf2f916c3U, 0xf3fb10cbU,
+       0xf4f502f3U, 0xf5f704fbU, 0xf6f10ee3U, 0xf7f308ebU,
+       0xf8ed2a93U, 0xf9ef2c9bU, 0xfae92683U, 0xfbeb208bU,
+       0xfce532b3U, 0xfde734bbU, 0xfee13ea3U, 0xffe338abU,
+};
+
+static const u32 rc[] = {
+       0xba542f74U, 0x53d3d24dU, 0x50ac8dbfU, 0x70529a4cU,
+       0xead597d1U, 0x33515ba6U, 0xde48a899U, 0xdb32b7fcU,
+       0xe39e919bU, 0xe2bb416eU, 0xa5cb6b95U, 0xa1f3b102U,
+       0xccc41d14U, 0xc363da5dU, 0x5fdc7dcdU, 0x7f5a6c5cU,
+       0xf726ffedU, 0xe89d6f8eU, 0x19a0f089U,
+};
+
+static int anubis_setkey(void *ctx_arg, const u8 *in_key,
+                        unsigned int key_len, u32 *flags)
+{
+
+       int N, R, i, pos, r;
+       u32 kappa[ANUBIS_MAX_N];
+       u32 inter[ANUBIS_MAX_N];
+
+       struct anubis_ctx *ctx = ctx_arg;
+
+       switch (key_len)
+       {
+               case 16: case 20: case 24: case 28:
+               case 32: case 36: case 40:
+                       break;
+               default:
+                       *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+                       return - EINVAL;
+       }
+
+       ctx->key_len = key_len * 8;
+       N = ctx->key_len >> 5;
+       ctx->R = R = 8 + N;
+
+       /* * map cipher key to initial key state (mu): */
+               for (i = 0, pos = 0; i < N; i++, pos += 4) {
+               kappa[i] =
+                       (in_key[pos    ] << 24) ^
+                       (in_key[pos + 1] << 16) ^
+                       (in_key[pos + 2] <<  8) ^
+                       (in_key[pos + 3]      );
+       }
+
+       /*
+        * generate R + 1 round keys:
+        */
+       for (r = 0; r <= R; r++) {
+               u32 K0, K1, K2, K3;
+               /*
+                * generate r-th round key K^r:
+                */
+               K0 = T4[(kappa[N - 1] >> 24)       ];
+               K1 = T4[(kappa[N - 1] >> 16) & 0xff];
+               K2 = T4[(kappa[N - 1] >>  8) & 0xff];
+               K3 = T4[(kappa[N - 1]      ) & 0xff];
+               for (i = N - 2; i >= 0; i--) {
+                       K0 = T4[(kappa[i] >> 24)       ] ^
+                               (T5[(K0 >> 24)       ] & 0xff000000U) ^
+                               (T5[(K0 >> 16) & 0xff] & 0x00ff0000U) ^
+                               (T5[(K0 >>  8) & 0xff] & 0x0000ff00U) ^
+                               (T5[(K0      ) & 0xff] & 0x000000ffU);
+                       K1 = T4[(kappa[i] >> 16) & 0xff] ^
+                               (T5[(K1 >> 24)       ] & 0xff000000U) ^
+                               (T5[(K1 >> 16) & 0xff] & 0x00ff0000U) ^
+                               (T5[(K1 >>  8) & 0xff] & 0x0000ff00U) ^
+                               (T5[(K1      ) & 0xff] & 0x000000ffU);
+                       K2 = T4[(kappa[i] >>  8) & 0xff] ^
+                               (T5[(K2 >> 24)       ] & 0xff000000U) ^
+                               (T5[(K2 >> 16) & 0xff] & 0x00ff0000U) ^
+                               (T5[(K2 >>  8) & 0xff] & 0x0000ff00U) ^
+                               (T5[(K2      ) & 0xff] & 0x000000ffU);
+                       K3 = T4[(kappa[i]      ) & 0xff] ^
+                               (T5[(K3 >> 24)       ] & 0xff000000U) ^
+                               (T5[(K3 >> 16) & 0xff] & 0x00ff0000U) ^
+                               (T5[(K3 >>  8) & 0xff] & 0x0000ff00U) ^
+                               (T5[(K3      ) & 0xff] & 0x000000ffU);
+               }
+
+               ctx->E[r][0] = K0;
+               ctx->E[r][1] = K1;
+               ctx->E[r][2] = K2;
+               ctx->E[r][3] = K3;
+
+               /*
+                * compute kappa^{r+1} from kappa^r:
+                */
+               if (r == R) {
+                       break;
+               }
+               for (i = 0; i < N; i++) {
+                       int j = i;
+                       inter[i]  = T0[(kappa[j--] >> 24)       ];
+                       if (j < 0) j = N - 1;
+                       inter[i] ^= T1[(kappa[j--] >> 16) & 0xff];
+                       if (j < 0) j = N - 1;
+                       inter[i] ^= T2[(kappa[j--] >>  8) & 0xff];
+                       if (j < 0) j = N - 1;
+                       inter[i] ^= T3[(kappa[j  ]      ) & 0xff];
+               }
+               kappa[0] = inter[0] ^ rc[r];
+               for (i = 1; i < N; i++) {
+                       kappa[i] = inter[i];
+               }
+       }
+
+       /*
+        * generate inverse key schedule: K'^0 = K^R, K'^R =
+        *                                K^0, K'^r = theta(K^{R-r}):
+        */
+       for (i = 0; i < 4; i++) {
+               ctx->D[0][i] = ctx->E[R][i];
+               ctx->D[R][i] = ctx->E[0][i];
+       }
+       for (r = 1; r < R; r++) {
+               for (i = 0; i < 4; i++) {
+                       u32 v = ctx->E[R - r][i];
+                       ctx->D[r][i] =
+                               T0[T4[(v >> 24)       ] & 0xff] ^
+                               T1[T4[(v >> 16) & 0xff] & 0xff] ^
+                               T2[T4[(v >>  8) & 0xff] & 0xff] ^
+                               T3[T4[(v      ) & 0xff] & 0xff];
+               }
+       }
+
+       return 0;
+}
+
+static void anubis_crypt(u32 roundKey[ANUBIS_MAX_ROUNDS + 1][4],
+               u8 *ciphertext, const u8 *plaintext, const int R)
+{
+       int i, pos, r;
+       u32 state[4];
+       u32 inter[4];
+
+       /*
+        * map plaintext block to cipher state (mu)
+        * and add initial round key (sigma[K^0]):
+        */
+       for (i = 0, pos = 0; i < 4; i++, pos += 4) {
+               state[i] =
+                       (plaintext[pos    ] << 24) ^
+                       (plaintext[pos + 1] << 16) ^
+                       (plaintext[pos + 2] <<  8) ^
+                       (plaintext[pos + 3]      ) ^
+                       roundKey[0][i];
+       }
+
+       /*
+        * R - 1 full rounds:
+        */
+
+       for (r = 1; r < R; r++) {
+               inter[0] =
+                       T0[(state[0] >> 24)       ] ^
+                       T1[(state[1] >> 24)       ] ^
+                       T2[(state[2] >> 24)       ] ^
+                       T3[(state[3] >> 24)       ] ^
+                       roundKey[r][0];
+               inter[1] =
+                       T0[(state[0] >> 16) & 0xff] ^
+                       T1[(state[1] >> 16) & 0xff] ^
+                       T2[(state[2] >> 16) & 0xff] ^
+                       T3[(state[3] >> 16) & 0xff] ^
+                       roundKey[r][1];
+               inter[2] =
+                       T0[(state[0] >>  8) & 0xff] ^
+                       T1[(state[1] >>  8) & 0xff] ^
+                       T2[(state[2] >>  8) & 0xff] ^
+                       T3[(state[3] >>  8) & 0xff] ^
+                       roundKey[r][2];
+               inter[3] =
+                       T0[(state[0]      ) & 0xff] ^
+                       T1[(state[1]      ) & 0xff] ^
+                       T2[(state[2]      ) & 0xff] ^
+                       T3[(state[3]      ) & 0xff] ^
+                       roundKey[r][3];
+               state[0] = inter[0];
+               state[1] = inter[1];
+               state[2] = inter[2];
+               state[3] = inter[3];
+       }
+
+       /*
+        * last round:
+        */
+
+       inter[0] =
+               (T0[(state[0] >> 24)       ] & 0xff000000U) ^
+               (T1[(state[1] >> 24)       ] & 0x00ff0000U) ^
+               (T2[(state[2] >> 24)       ] & 0x0000ff00U) ^
+               (T3[(state[3] >> 24)       ] & 0x000000ffU) ^
+               roundKey[R][0];
+       inter[1] =
+               (T0[(state[0] >> 16) & 0xff] & 0xff000000U) ^
+               (T1[(state[1] >> 16) & 0xff] & 0x00ff0000U) ^
+               (T2[(state[2] >> 16) & 0xff] & 0x0000ff00U) ^
+               (T3[(state[3] >> 16) & 0xff] & 0x000000ffU) ^
+               roundKey[R][1];
+       inter[2] =
+               (T0[(state[0] >>  8) & 0xff] & 0xff000000U) ^
+               (T1[(state[1] >>  8) & 0xff] & 0x00ff0000U) ^
+               (T2[(state[2] >>  8) & 0xff] & 0x0000ff00U) ^
+               (T3[(state[3] >>  8) & 0xff] & 0x000000ffU) ^
+               roundKey[R][2];
+       inter[3] =
+               (T0[(state[0]      ) & 0xff] & 0xff000000U) ^
+               (T1[(state[1]      ) & 0xff] & 0x00ff0000U) ^
+               (T2[(state[2]      ) & 0xff] & 0x0000ff00U) ^
+               (T3[(state[3]      ) & 0xff] & 0x000000ffU) ^
+               roundKey[R][3];
+
+       /*
+        * map cipher state to ciphertext block (mu^{-1}):
+        */
+
+       for (i = 0, pos = 0; i < 4; i++, pos += 4) {
+               u32 w = inter[i];
+               ciphertext[pos    ] = (u8)(w >> 24);
+               ciphertext[pos + 1] = (u8)(w >> 16);
+               ciphertext[pos + 2] = (u8)(w >>  8);
+               ciphertext[pos + 3] = (u8)(w      );
+       }
+}
+
+static void anubis_encrypt(void *ctx_arg, u8 *dst, const u8 *src)
+{
+       struct anubis_ctx *ctx = ctx_arg;
+       anubis_crypt(ctx->E, dst, src, ctx->R);
+}
+
+static void anubis_decrypt(void *ctx_arg, u8 *dst, const u8 *src)
+{
+       struct anubis_ctx *ctx = ctx_arg;
+       anubis_crypt(ctx->D, dst, src, ctx->R);
+}
+
+static struct crypto_alg anubis_alg = {
+       .cra_name               =       "anubis",
+       .cra_flags              =       CRYPTO_ALG_TYPE_CIPHER,
+       .cra_blocksize          =       ANUBIS_BLOCK_SIZE,
+       .cra_ctxsize            =       sizeof (struct anubis_ctx),
+       .cra_module             =       THIS_MODULE,
+       .cra_list               =       LIST_HEAD_INIT(anubis_alg.cra_list),
+       .cra_u                  =       { .cipher = {
+       .cia_min_keysize        =       ANUBIS_MIN_KEY_SIZE,
+       .cia_max_keysize        =       ANUBIS_MAX_KEY_SIZE,
+       .cia_setkey             =       anubis_setkey,
+       .cia_encrypt            =       anubis_encrypt,
+       .cia_decrypt            =       anubis_decrypt } }
+};
+
+static int __init init(void)
+{
+       int ret = 0;
+       
+       ret = crypto_register_alg(&anubis_alg);
+       return ret;
+}
+
+static void __exit fini(void)
+{
+       crypto_unregister_alg(&anubis_alg);
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Anubis Cryptographic Algorithm");
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c
new file mode 100644 (file)
index 0000000..060251b
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * acpi_container.c  - ACPI Generic Container Driver
+ * ($Revision: )
+ *
+ * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com)
+ * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com)
+ * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com)
+ * Copyright (C) 2004 Intel Corp.
+ * Copyright (C) 2004 FUJITSU LIMITED
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/container.h>
+
+#define ACPI_CONTAINER_DRIVER_NAME     "ACPI container driver"
+#define ACPI_CONTAINER_DEVICE_NAME     "ACPI container device"
+#define ACPI_CONTAINER_CLASS           "container"
+
+#define INSTALL_NOTIFY_HANDLER         1
+#define UNINSTALL_NOTIFY_HANDLER       2
+
+#define ACPI_CONTAINER_COMPONENT       0x01000000
+#define _COMPONENT                     ACPI_CONTAINER_COMPONENT
+ACPI_MODULE_NAME                       ("acpi_container")
+
+MODULE_AUTHOR("Anil S Keshavamurthy");
+MODULE_DESCRIPTION(ACPI_CONTAINER_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+#define ACPI_STA_PRESENT               (0x00000001)
+
+static int acpi_container_add(struct acpi_device *device);
+static int acpi_container_remove(struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_container_driver = {
+       .name =         ACPI_CONTAINER_DRIVER_NAME,
+       .class =        ACPI_CONTAINER_CLASS,
+       .ids =          "ACPI0004,PNP0A05,PNP0A06",
+       .ops =          {
+                               .add =          acpi_container_add,
+                               .remove =       acpi_container_remove,
+                       },
+};
+
+
+/*******************************************************************/
+
+static int
+is_device_present(acpi_handle handle)
+{
+       acpi_handle             temp;
+       acpi_status             status;
+       unsigned long   sta;
+
+       ACPI_FUNCTION_TRACE("is_device_present");
+
+       status = acpi_get_handle(handle, "_STA", &temp);
+       if (ACPI_FAILURE(status))
+               return_VALUE(1); /* _STA not found, assmue device present */
+
+       status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+       if (ACPI_FAILURE(status))
+               return_VALUE(0); /* Firmware error */
+
+       return_VALUE((sta & ACPI_STA_PRESENT) == ACPI_STA_PRESENT);
+}
+
+/*******************************************************************/
+static int
+acpi_container_add(struct acpi_device *device)
+{
+       struct acpi_container *container;
+
+       ACPI_FUNCTION_TRACE("acpi_container_add");
+
+       if (!device) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "device is NULL\n"));
+               return_VALUE(-EINVAL);
+       }
+
+       container = kmalloc(sizeof(struct acpi_container), GFP_KERNEL);
+       if(!container)
+               return_VALUE(-ENOMEM);
+       
+       memset(container, 0, sizeof(struct acpi_container));
+       container->handle = device->handle;
+       strcpy(acpi_device_name(device), ACPI_CONTAINER_DEVICE_NAME);
+       strcpy(acpi_device_class(device), ACPI_CONTAINER_CLASS);
+       acpi_driver_data(device) = container;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device <%s> bid <%s>\n",       \
+               acpi_device_name(device), acpi_device_bid(device)));
+
+
+       return_VALUE(0);
+}
+
+static int
+acpi_container_remove(struct acpi_device *device, int type)
+{
+       acpi_status             status = AE_OK;
+       struct acpi_container   *pc = NULL;
+       pc = (struct acpi_container*) acpi_driver_data(device);
+
+       if (pc)
+               kfree(pc);
+
+       return status;
+}
+
+
+static int
+container_device_add(struct acpi_device **device, acpi_handle handle)
+{
+       acpi_handle phandle;
+       struct acpi_device *pdev;
+       int result;
+
+       ACPI_FUNCTION_TRACE("container_device_add");
+
+       if (acpi_get_parent(handle, &phandle)) {
+               return_VALUE(-ENODEV);
+       }
+
+       if (acpi_bus_get_device(phandle, &pdev)) {
+               return_VALUE(-ENODEV);
+       }
+
+       if (acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_DEVICE)) {
+               return_VALUE(-ENODEV);
+       }
+
+       result = acpi_bus_scan(*device);
+
+       return_VALUE(result);
+}
+
+static void
+container_notify_cb(acpi_handle handle, u32 type, void *context)
+{
+       struct acpi_device              *device = NULL;
+       int result;
+       int present;
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE("container_notify_cb");
+
+       present = is_device_present(handle);
+       
+       switch (type) {
+       case ACPI_NOTIFY_BUS_CHECK:
+               /* Fall through */
+       case ACPI_NOTIFY_DEVICE_CHECK:
+               printk("Container driver received %s event\n",
+                       (type == ACPI_NOTIFY_BUS_CHECK)?
+                       "ACPI_NOTIFY_BUS_CHECK":"ACPI_NOTIFY_DEVICE_CHECK");
+               if (present) {
+                       status = acpi_bus_get_device(handle, &device);
+                       if (ACPI_FAILURE(status) || !device) {
+                               result = container_device_add(&device, handle);
+                               if (!result)
+                                       kobject_hotplug(&device->kobj, KOBJ_ONLINE);
+                       } else {
+                               /* device exist and this is a remove request */
+                               kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+                       }
+               }
+               break;
+       case ACPI_NOTIFY_EJECT_REQUEST:
+               if (!acpi_bus_get_device(handle, &device) && device) {
+                       kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+               }
+               break;
+       default:
+               break;
+       }
+       return_VOID;
+}
+
+static acpi_status
+container_walk_namespace_cb(acpi_handle handle,
+       u32 lvl,
+       void *context,
+       void **rv)
+{
+       char                            *hid = NULL;
+       struct acpi_buffer              buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       struct acpi_device_info         *info;
+       acpi_status                     status;
+       int                             *action = context;
+
+       ACPI_FUNCTION_TRACE("container_walk_namespace_cb");
+
+       status = acpi_get_object_info(handle, &buffer);
+       if (ACPI_FAILURE(status) || !buffer.pointer) {
+               return_ACPI_STATUS(AE_OK);
+       }
+
+       info = buffer.pointer;
+       if (info->valid & ACPI_VALID_HID)
+               hid = info->hardware_id.value;
+
+       if (hid == NULL) {
+               goto end;
+       }
+
+       if (strcmp(hid, "ACPI0004") && strcmp(hid, "PNP0A05") &&
+                       strcmp(hid, "PNP0A06")) {
+               goto end;
+       }
+
+       switch(*action) {
+       case INSTALL_NOTIFY_HANDLER:
+               acpi_install_notify_handler(handle,
+                       ACPI_SYSTEM_NOTIFY,
+                       container_notify_cb,
+                       NULL);
+               break;
+       case UNINSTALL_NOTIFY_HANDLER:
+               acpi_remove_notify_handler(handle,
+                       ACPI_SYSTEM_NOTIFY,
+                       container_notify_cb);
+               break;
+       default:
+               break;
+       }
+
+end:
+       acpi_os_free(buffer.pointer);
+
+       return_ACPI_STATUS(AE_OK);
+}
+
+
+int __init
+acpi_container_init(void)
+{
+       int     result = 0;
+       int     action = INSTALL_NOTIFY_HANDLER;
+
+       result = acpi_bus_register_driver(&acpi_container_driver);
+       if (result < 0) {
+               return(result);
+       }
+
+       /* register notify handler to every container device */
+       acpi_walk_namespace(ACPI_TYPE_DEVICE,
+                                    ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX,
+                                    container_walk_namespace_cb,
+                                    &action, NULL);
+
+       return(0);
+}
+
+void __exit
+acpi_container_exit(void)
+{
+       int                     action = UNINSTALL_NOTIFY_HANDLER;
+
+       ACPI_FUNCTION_TRACE("acpi_container_exit");
+
+       acpi_walk_namespace(ACPI_TYPE_DEVICE,
+                                    ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX,
+                                    container_walk_namespace_cb,
+                                    &action, NULL);
+
+       acpi_bus_unregister_driver(&acpi_container_driver);
+
+       return_VOID;
+}
+
+module_init(acpi_container_init);
+module_exit(acpi_container_exit);
diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c
new file mode 100644 (file)
index 0000000..b435b3e
--- /dev/null
@@ -0,0 +1,1242 @@
+/*
+ *  ibm_acpi.c - IBM ThinkPad ACPI Extras
+ *
+ *
+ *  Copyright (C) 2004 Borislav Deianov
+ *
+ *  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
+ *
+ *  Changelog:
+ *
+ *  2004-08-09 0.1     initial release, support for X series
+ *  2004-08-14 0.2     support for T series, X20
+ *                     bluetooth enable/disable
+ *                     hotkey events disabled by default
+ *                     removed fan control, currently useless
+ *  2004-08-17 0.3     support for R40
+ *                     lcd off, brightness control
+ *                     thinklight on/off
+ *  2004-09-16 0.4     support for module parameters
+ *                     hotkey mask can be prefixed by 0x
+ *                     video output switching
+ *                     video expansion control
+ *                     ultrabay eject support
+ *                     removed lcd brightness/on/off control, didn't work
+ *  2004-10-18 0.5     thinklight support on A21e, G40, R32, T20, T21, X20
+ *                     proc file format changed
+ *                     video_switch command
+ *                     experimental cmos control
+ *                     experimental led control
+ *                     experimental acpi sounds
+ *  2004-10-19 0.6     use acpi_bus_register_driver() to claim HKEY device
+ *  2004-10-23 0.7     fix module loading on A21e, A22p, T20, T21, X20
+ *                     fix LED control on A21e
+ *  2004-11-08 0.8     fix init error case, don't return from a macro
+ *                             thanks to Chris Wright <chrisw@osdl.org>
+ */
+
+#define IBM_VERSION "0.8"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+
+#include <acpi/acpi_drivers.h>
+#include <acpi/acnamesp.h>
+
+#define IBM_NAME "ibm"
+#define IBM_DESC "IBM ThinkPad ACPI Extras"
+#define IBM_FILE "ibm_acpi"
+#define IBM_URL "http://ibm-acpi.sf.net/"
+
+#define IBM_DIR IBM_NAME
+
+#define IBM_LOG IBM_FILE ": "
+#define IBM_ERR           KERN_ERR    IBM_LOG
+#define IBM_NOTICE KERN_NOTICE IBM_LOG
+#define IBM_INFO   KERN_INFO   IBM_LOG
+#define IBM_DEBUG  KERN_DEBUG  IBM_LOG
+
+#define IBM_MAX_ACPI_ARGS 3
+
+#define __unused __attribute__ ((unused))
+
+static int experimental;
+module_param(experimental, int, 0);
+
+static acpi_handle root_handle = NULL;
+
+#define IBM_HANDLE(object, parent, paths...)                   \
+       static acpi_handle  object##_handle;                    \
+       static acpi_handle *object##_parent = &parent##_handle; \
+       static char        *object##_paths[] = { paths }
+
+IBM_HANDLE(ec, root,
+          "\\_SB.PCI0.ISA.EC",    /* A21e, A22p, T20, T21, X20 */
+          "\\_SB.PCI0.LPC.EC",    /* all others */
+);
+
+IBM_HANDLE(vid, root, 
+          "\\_SB.PCI0.VID",       /* A21e, G40, X30, X40 */
+          "\\_SB.PCI0.AGP.VID",   /* all others */
+);
+
+IBM_HANDLE(cmos, root,
+          "\\UCMS",               /* R50, R50p, R51, T4x, X31, X40 */
+          "\\CMOS",               /* A3x, G40, R32, T23, T30, X22, X24, X30 */
+          "\\CMS",                /* R40, R40e */
+);                                 /* A21e, A22p, T20, T21, X20 */
+
+IBM_HANDLE(dock, root,
+          "\\_SB.GDCK",           /* X30, X31, X40 */
+          "\\_SB.PCI0.DOCK",      /* A22p, T20, T21, X20 */
+          "\\_SB.PCI0.PCI1.DOCK", /* all others */
+);                                 /* A21e, G40, R32, R40, R40e */
+
+IBM_HANDLE(bay, root,
+          "\\_SB.PCI0.IDE0.SCND.MSTR");      /* all except A21e */
+IBM_HANDLE(bayej, root,
+          "\\_SB.PCI0.IDE0.SCND.MSTR._EJ0"); /* all except A2x, A3x */
+
+IBM_HANDLE(lght, root, "\\LGHT");  /* A21e, A22p, T20, T21, X20 */
+IBM_HANDLE(hkey, ec,   "HKEY");    /* all */
+IBM_HANDLE(led,  ec,   "LED");     /* all except A21e, A22p, T20, T21, X20 */
+IBM_HANDLE(sysl, ec,   "SYSL");    /* A21e, A22p, T20, T21, X20 */
+IBM_HANDLE(bled, ec,   "BLED");    /* A22p, T20, T21, X20 */
+IBM_HANDLE(beep, ec,   "BEEP");    /* all models */
+
+struct ibm_struct {
+       char *name;
+
+       char *hid;
+       struct acpi_driver *driver;
+       
+       int  (*init)   (struct ibm_struct *);
+       int  (*read)   (struct ibm_struct *, char *);
+       int  (*write)  (struct ibm_struct *, char *);
+       void (*exit)   (struct ibm_struct *);
+
+       void (*notify) (struct ibm_struct *, u32);      
+       acpi_handle *handle;
+       int type;
+       struct acpi_device *device;
+
+       int driver_registered;
+       int proc_created;
+       int init_called;
+       int notify_installed;
+
+       int supported;
+       union {
+               struct {
+                       int status;
+                       int mask;
+               } hotkey;
+               struct {
+                       int autoswitch;
+               } video;
+       } state;
+
+       int experimental;
+};
+
+struct proc_dir_entry *proc_dir = NULL;
+
+#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
+#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
+#define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
+
+static int acpi_evalf(acpi_handle handle,
+                     void *res, char *method, char *fmt, ...)
+{
+       char *fmt0 = fmt;
+        struct acpi_object_list        params;
+        union acpi_object      in_objs[IBM_MAX_ACPI_ARGS];
+        struct acpi_buffer     result;
+        union acpi_object      out_obj;
+        acpi_status            status;
+       va_list                 ap;
+       char                    res_type;
+       int                     success;
+       int                     quiet;
+
+       if (!*fmt) {
+               printk(IBM_ERR "acpi_evalf() called with empty format\n");
+               return 0;
+       }
+
+       if (*fmt == 'q') {
+               quiet = 1;
+               fmt++;
+       } else
+               quiet = 0;
+
+       res_type = *(fmt++);
+
+       params.count = 0;
+       params.pointer = &in_objs[0];
+
+       va_start(ap, fmt);
+       while (*fmt) {
+               char c = *(fmt++);
+               switch (c) {
+               case 'd':       /* int */
+                       in_objs[params.count].integer.value = va_arg(ap, int);
+                       in_objs[params.count++].type = ACPI_TYPE_INTEGER;
+                       break;
+               /* add more types as needed */
+               default:
+                       printk(IBM_ERR "acpi_evalf() called "
+                              "with invalid format character '%c'\n", c);
+                       return 0;
+               }
+       }
+       va_end(ap);
+
+       result.length = sizeof(out_obj);
+       result.pointer = &out_obj;
+
+       status = acpi_evaluate_object(handle, method, &params, &result);
+
+       switch (res_type) {
+       case 'd':       /* int */
+               if (res)
+                       *(int *)res = out_obj.integer.value;
+               success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
+               break;
+       case 'v':       /* void */
+               success = status == AE_OK;
+               break;
+       /* add more types as needed */
+       default:
+               printk(IBM_ERR "acpi_evalf() called "
+                      "with invalid format character '%c'\n", res_type);
+               return 0;
+       }
+
+       if (!success && !quiet)
+               printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
+                      method, fmt0, status);
+
+       return success;
+}
+
+static void __unused acpi_print_int(acpi_handle handle, char *method)
+{
+       int i;
+
+       if (acpi_evalf(handle, &i, method, "d"))
+               printk(IBM_INFO "%s = 0x%x\n", method, i);
+       else
+               printk(IBM_ERR "error calling %s\n", method);
+}
+
+static char *next_cmd(char **cmds)
+{
+       char *start = *cmds;
+       char *end;
+
+       while ((end = strchr(start, ',')) && end == start)
+               start = end + 1;
+
+       if (!end)
+               return NULL;
+
+       *end = 0;
+       *cmds = end + 1;
+       return start;
+}
+
+static int driver_init(struct ibm_struct *ibm)
+{
+       printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
+       printk(IBM_INFO "%s\n", IBM_URL);
+
+       return 0;
+}
+
+static int driver_read(struct ibm_struct *ibm, char *p)
+{
+       int len = 0;
+
+       len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
+       len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
+
+       return len;
+}
+
+static int hotkey_get(struct ibm_struct *ibm, int *status, int *mask)
+{
+       if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
+               return -EIO;
+       if (ibm->supported) {
+               if (!acpi_evalf(hkey_handle, mask, "DHKN", "qd"))
+                       return -EIO;
+       } else {
+               *mask = ibm->state.hotkey.mask;
+       }
+       return 0;
+}
+
+static int hotkey_set(struct ibm_struct *ibm, int status, int mask)
+{
+       int i;
+
+       if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
+               return -EIO;
+
+       if (!ibm->supported)
+               return 0;
+
+       for (i=0; i<32; i++) {
+               int bit = ((1 << i) & mask) != 0;
+               if (!acpi_evalf(hkey_handle, NULL, "MHKM", "vdd", i+1, bit))
+                       return -EIO;
+       }
+
+       return 0;
+}
+
+static int hotkey_init(struct ibm_struct *ibm)
+{
+       int ret;
+
+       ibm->supported = 1;
+       ret = hotkey_get(ibm,
+                        &ibm->state.hotkey.status,
+                        &ibm->state.hotkey.mask);
+       if (ret < 0) {
+               /* mask not supported on A21e, A22p, T20, T21, X20, X22, X24 */
+               ibm->supported = 0;
+               ret = hotkey_get(ibm,
+                                &ibm->state.hotkey.status,
+                                &ibm->state.hotkey.mask);
+       }
+
+       return ret;
+}      
+
+static int hotkey_read(struct ibm_struct *ibm, char *p)
+{
+       int status, mask;
+       int len = 0;
+
+       if (hotkey_get(ibm, &status, &mask) < 0)
+               return -EIO;
+
+       len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
+       if (ibm->supported) {
+               len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
+               len += sprintf(p + len,
+                              "commands:\tenable, disable, reset, <mask>\n");
+       } else {
+               len += sprintf(p + len, "mask:\t\tnot supported\n");
+               len += sprintf(p + len, "commands:\tenable, disable, reset\n");
+       }
+
+       return len;
+}
+
+static int hotkey_write(struct ibm_struct *ibm, char *buf)
+{
+       int status, mask;
+       char *cmd;
+       int do_cmd = 0;
+
+       if (hotkey_get(ibm, &status, &mask) < 0)
+               return -ENODEV;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "enable") == 0) {
+                       status = 1;
+               } else if (strlencmp(cmd, "disable") == 0) {
+                       status = 0;
+               } else if (strlencmp(cmd, "reset") == 0) {
+                       status = ibm->state.hotkey.status;
+                       mask   = ibm->state.hotkey.mask;
+               } else if (sscanf(cmd, "0x%x", &mask) == 1) {
+                       /* mask set */
+               } else if (sscanf(cmd, "%x", &mask) == 1) {
+                       /* mask set */
+               } else
+                       return -EINVAL;
+               do_cmd = 1;
+       }
+
+       if (do_cmd && hotkey_set(ibm, status, mask) < 0)
+               return -EIO;
+
+       return 0;
+}      
+
+static void hotkey_exit(struct ibm_struct *ibm)
+{
+       hotkey_set(ibm, ibm->state.hotkey.status, ibm->state.hotkey.mask);
+}
+
+static void hotkey_notify(struct ibm_struct *ibm, u32 event)
+{
+       int hkey;
+
+       if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
+               acpi_bus_generate_event(ibm->device, event, hkey);
+       else {
+               printk(IBM_ERR "unknown hotkey event %d\n", event);
+               acpi_bus_generate_event(ibm->device, event, 0);
+       }       
+}
+
+static int bluetooth_init(struct ibm_struct *ibm)
+{
+       /* bluetooth not supported on A21e, G40, T20, T21, X20 */
+       ibm->supported = acpi_evalf(hkey_handle, NULL, "GBDC", "qv");
+
+       return 0;
+}
+
+static int bluetooth_status(struct ibm_struct *ibm)
+{
+       int status;
+
+       if (!ibm->supported || !acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+               status = 0;
+
+       return status;
+}
+
+static int bluetooth_read(struct ibm_struct *ibm, char *p)
+{
+       int len = 0;
+       int status = bluetooth_status(ibm);
+
+       if (!ibm->supported)
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       else if (!(status & 1))
+               len += sprintf(p + len, "status:\t\tnot installed\n");
+       else {
+               len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
+               len += sprintf(p + len, "commands:\tenable, disable\n");
+       }
+
+       return len;
+}
+
+static int bluetooth_write(struct ibm_struct *ibm, char *buf)
+{
+       int status = bluetooth_status(ibm);
+       char *cmd;
+       int do_cmd = 0;
+
+       if (!ibm->supported)
+               return -EINVAL;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "enable") == 0) {
+                       status |= 2;
+               } else if (strlencmp(cmd, "disable") == 0) {
+                       status &= ~2;
+               } else
+                       return -EINVAL;
+               do_cmd = 1;
+       }
+
+       if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
+           return -EIO;
+
+       return 0;
+}
+
+static int video_init(struct ibm_struct *ibm)
+{
+       if (!acpi_evalf(vid_handle,
+                       &ibm->state.video.autoswitch, "^VDEE", "d"))
+               return -ENODEV;
+
+       return 0;
+}
+
+static int video_status(struct ibm_struct *ibm)
+{
+       int status = 0;
+       int i;
+
+       acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1);
+       if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
+               status |= 0x02 * i;
+
+       acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0);
+       if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
+               status |= 0x01 * i;
+       if (acpi_evalf(NULL, &i, "\\VCDD", "d"))
+               status |= 0x08 * i;
+
+       if (acpi_evalf(vid_handle, &i, "^VDEE", "d"))
+               status |= 0x10 * (i & 1);
+
+       return status;
+}
+
+static int video_read(struct ibm_struct *ibm, char *p)
+{
+       int status = video_status(ibm);
+       int len = 0;
+
+       len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
+       len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
+       len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
+       len += sprintf(p + len, "auto:\t\t%s\n", enabled(status, 4));
+       len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable, "
+                      "crt_enable, crt_disable\n");
+       len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable, "
+                      "auto_enable, auto_disable\n");
+       len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
+
+       return len;
+}
+
+static int video_write(struct ibm_struct *ibm, char *buf)
+{
+       char *cmd;
+       int enable, disable, status;
+
+       enable = disable = 0;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "lcd_enable") == 0) {
+                       enable |= 0x01;
+               } else if (strlencmp(cmd, "lcd_disable") == 0) {
+                       disable |= 0x01;
+               } else if (strlencmp(cmd, "crt_enable") == 0) {
+                       enable |= 0x02;
+               } else if (strlencmp(cmd, "crt_disable") == 0) {
+                       disable |= 0x02;
+               } else if (strlencmp(cmd, "dvi_enable") == 0) {
+                       enable |= 0x08;
+               } else if (strlencmp(cmd, "dvi_disable") == 0) {
+                       disable |= 0x08;
+               } else if (strlencmp(cmd, "auto_enable") == 0) {
+                       if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
+                               return -EIO;
+               } else if (strlencmp(cmd, "auto_disable") == 0) {
+                       if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0))
+                               return -EIO;
+               } else if (strlencmp(cmd, "video_switch") == 0) {
+                       int autoswitch;
+                       if (!acpi_evalf(vid_handle, &autoswitch, "^VDEE", "d"))
+                               return -EIO;
+                       if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
+                               return -EIO;
+                       if (!acpi_evalf(vid_handle, NULL, "VSWT", "v"))
+                               return -EIO;
+                       if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd",
+                                       autoswitch))
+                               return -EIO;
+               } else if (strlencmp(cmd, "expand_toggle") == 0) {
+                       if (!acpi_evalf(NULL, NULL, "\\VEXP", "v"))
+                               return -EIO;
+               } else
+                       return -EINVAL;
+       }
+
+       if (enable || disable) {
+               status = (video_status(ibm) & 0x0f & ~disable) | enable;
+               if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80))
+                       return -EIO;
+               if (!acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1))
+                       return -EIO;
+       }
+
+       return 0;
+}
+
+static void video_exit(struct ibm_struct *ibm)
+{
+       acpi_evalf(vid_handle, NULL, "_DOS", "vd",
+                  ibm->state.video.autoswitch);
+}
+
+static int light_init(struct ibm_struct *ibm)
+{
+       /* kblt not supported on G40, R32, X20 */
+       ibm->supported = acpi_evalf(ec_handle, NULL, "KBLT", "qv");
+
+       return 0;
+}
+
+static int light_read(struct ibm_struct *ibm, char *p)
+{
+       int len = 0;
+       int status = 0;
+
+       if (ibm->supported) {
+               if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
+                       return -EIO;
+               len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
+       } else
+               len += sprintf(p + len, "status:\t\tunknown\n");
+
+       len += sprintf(p + len, "commands:\ton, off\n");
+
+       return len;
+}
+
+static int light_write(struct ibm_struct *ibm, char *buf)
+{
+       int cmos_cmd, lght_cmd;
+       char *cmd;
+       int success;
+       
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "on") == 0) {
+                       cmos_cmd = 0x0c;
+                       lght_cmd = 1;
+               } else if (strlencmp(cmd, "off") == 0) {
+                       cmos_cmd = 0x0d;
+                       lght_cmd = 0;
+               } else
+                       return -EINVAL;
+               
+               success = cmos_handle ?
+                       acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
+                       acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
+               if (!success)
+                       return -EIO;
+       }
+
+       return 0;
+}
+
+static int _sta(acpi_handle handle)
+{
+       int status;
+
+       if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
+               status = 0;
+
+       return status;
+}
+
+#define dock_docked() (_sta(dock_handle) & 1)
+
+static int dock_read(struct ibm_struct *ibm, char *p)
+{
+       int len = 0;
+       int docked = dock_docked();
+
+       if (!dock_handle)
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       else if (!docked)
+               len += sprintf(p + len, "status:\t\tundocked\n");
+       else {
+               len += sprintf(p + len, "status:\t\tdocked\n");
+               len += sprintf(p + len, "commands:\tdock, undock\n");
+       }
+
+       return len;
+}
+
+static int dock_write(struct ibm_struct *ibm, char *buf)
+{
+       char *cmd;
+
+       if (!dock_docked())
+               return -EINVAL;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "undock") == 0) {
+                       if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0))
+                               return -EIO;
+                       if (!acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
+                               return -EIO;
+               } else if (strlencmp(cmd, "dock") == 0) {
+                       if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
+                               return -EIO;
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}      
+
+static void dock_notify(struct ibm_struct *ibm, u32 event)
+{
+       int docked = dock_docked();
+
+       if (event == 3 && docked)
+               acpi_bus_generate_event(ibm->device, event, 1); /* button */
+       else if (event == 3 && !docked)
+               acpi_bus_generate_event(ibm->device, event, 2); /* undock */
+       else if (event == 0 && docked)
+               acpi_bus_generate_event(ibm->device, event, 3); /* dock */
+       else {
+               printk(IBM_ERR "unknown dock event %d, status %d\n",
+                      event, _sta(dock_handle));
+               acpi_bus_generate_event(ibm->device, event, 0); /* unknown */
+       }
+}
+
+#define bay_occupied() (_sta(bay_handle) & 1)
+
+static int bay_init(struct ibm_struct *ibm)
+{
+       /* bay not supported on A21e, A22p, A31, A31p, G40, R32, R40e */
+       ibm->supported = bay_handle && bayej_handle &&
+               acpi_evalf(bay_handle, NULL, "_STA", "qv");
+
+       return 0;
+}
+
+static int bay_read(struct ibm_struct *ibm, char *p)
+{
+       int len = 0;
+       int occupied = bay_occupied();
+       
+       if (!ibm->supported)
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       else if (!occupied)
+               len += sprintf(p + len, "status:\t\tunoccupied\n");
+       else {
+               len += sprintf(p + len, "status:\t\toccupied\n");
+               len += sprintf(p + len, "commands:\teject\n");
+       }
+
+       return len;
+}
+
+static int bay_write(struct ibm_struct *ibm, char *buf)
+{
+       char *cmd;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "eject") == 0) {
+                       if (!ibm->supported ||
+                           !acpi_evalf(bay_handle, NULL, "_EJ0", "vd", 1))
+                               return -EIO;
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}      
+
+static void bay_notify(struct ibm_struct *ibm, u32 event)
+{
+       acpi_bus_generate_event(ibm->device, event, 0);
+}
+
+static int cmos_read(struct ibm_struct *ibm, char *p)
+{
+       int len = 0;
+
+       /* cmos not supported on A21e, A22p, T20, T21, X20 */
+       if (!cmos_handle)
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       else {
+               len += sprintf(p + len, "status:\t\tsupported\n");
+               len += sprintf(p + len, "commands:\t<int>\n");
+       }
+
+       return len;
+}
+
+static int cmos_write(struct ibm_struct *ibm, char *buf)
+{
+       char *cmd;
+       int cmos_cmd;
+
+       if (!cmos_handle)
+               return -EINVAL;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (sscanf(cmd, "%u", &cmos_cmd) == 1) {
+                       /* cmos_cmd set */
+               } else
+                       return -EINVAL;
+
+               if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd))
+                       return -EIO;
+       }
+
+       return 0;
+}      
+               
+static int led_read(struct ibm_struct *ibm, char *p)
+{
+       int len = 0;
+
+       len += sprintf(p + len, "commands:\t"
+                      "<int> on, <int> off, <int> blink\n");
+
+       return len;
+}
+
+static int led_write(struct ibm_struct *ibm, char *buf)
+{
+       char *cmd;
+       unsigned int led;
+       int led_cmd, sysl_cmd, bled_a, bled_b;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (sscanf(cmd, "%u", &led) != 1)
+                       return -EINVAL;
+
+               if (strstr(cmd, "blink")) {
+                       led_cmd = 0xc0;
+                       sysl_cmd = 2;
+                       bled_a = 2;
+                       bled_b = 1;
+               } else if (strstr(cmd, "on")) {
+                       led_cmd = 0x80;
+                       sysl_cmd = 1;
+                       bled_a = 2;
+                       bled_b = 0;
+               } else if (strstr(cmd, "off")) {
+                       led_cmd = sysl_cmd = bled_a = bled_b = 0;
+               } else
+                       return -EINVAL;
+               
+               if (led_handle) {
+                       if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+                                       led, led_cmd))
+                               return -EIO;
+               } else if (led < 2) {
+                       if (acpi_evalf(sysl_handle, NULL, NULL, "vdd",
+                                      led, sysl_cmd))
+                               return -EIO;
+               } else if (led == 2 && bled_handle) {
+                       if (acpi_evalf(bled_handle, NULL, NULL, "vdd",
+                                      bled_a, bled_b))
+                               return -EIO;
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}      
+               
+static int beep_read(struct ibm_struct *ibm, char *p)
+{
+       int len = 0;
+
+       len += sprintf(p + len, "commands:\t<int>\n");
+
+       return len;
+}
+
+static int beep_write(struct ibm_struct *ibm, char *buf)
+{
+       char *cmd;
+       int beep_cmd;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (sscanf(cmd, "%u", &beep_cmd) == 1) {
+                       /* beep_cmd set */
+               } else
+                       return -EINVAL;
+
+               if (!acpi_evalf(beep_handle, NULL, NULL, "vd", beep_cmd))
+                       return -EIO;
+       }
+
+       return 0;
+}      
+               
+struct ibm_struct ibms[] = {
+       {
+               .name   = "driver",
+               .init   = driver_init,
+               .read   = driver_read,
+       },
+       {
+               .name   = "hotkey",
+               .hid    = "IBM0068",
+               .init   = hotkey_init,
+               .read   = hotkey_read,
+               .write  = hotkey_write,
+               .exit   = hotkey_exit,
+               .notify = hotkey_notify,
+               .handle = &hkey_handle,
+               .type   = ACPI_DEVICE_NOTIFY,
+       },
+       {
+               .name   = "bluetooth",
+               .init   = bluetooth_init,
+               .read   = bluetooth_read,
+               .write  = bluetooth_write,
+       },
+       {
+               .name   = "video",
+               .init   = video_init,
+               .read   = video_read,
+               .write  = video_write,
+               .exit   = video_exit,
+       },
+       {
+               .name   = "light",
+               .init   = light_init,
+               .read   = light_read,
+               .write  = light_write,
+       },
+       {
+               .name   = "dock",
+               .read   = dock_read,
+               .write  = dock_write,
+               .notify = dock_notify,
+               .handle = &dock_handle,
+               .type   = ACPI_SYSTEM_NOTIFY,
+       },
+       {
+               .name   = "bay",
+               .init   = bay_init,
+               .read   = bay_read,
+               .write  = bay_write,
+               .notify = bay_notify,
+               .handle = &bay_handle,
+               .type   = ACPI_SYSTEM_NOTIFY,
+       },
+       {
+               .name   = "cmos",
+               .read   = cmos_read,
+               .write  = cmos_write,
+               .experimental = 1,
+       },
+       {
+               .name   = "led",
+               .read   = led_read,
+               .write  = led_write,
+               .experimental = 1,
+       },
+       {
+               .name   = "beep",
+               .read   = beep_read,
+               .write  = beep_write,
+               .experimental = 1,
+       },
+};
+#define NUM_IBMS (sizeof(ibms)/sizeof(ibms[0]))
+
+static int dispatch_read(char *page, char **start, off_t off, int count,
+                        int *eof, void *data)
+{
+       struct ibm_struct *ibm = (struct ibm_struct *)data;
+       int len;
+       
+       if (!ibm || !ibm->read)
+               return -EINVAL;
+
+       len = ibm->read(ibm, page);
+       if (len < 0)
+               return len;
+
+       if (len <= off + count)
+               *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len > count)
+               len = count;
+       if (len < 0)
+               len = 0;
+
+       return len;
+}
+
+static int dispatch_write(struct file *file, const char __user *userbuf,
+                         unsigned long count, void *data)
+{
+       struct ibm_struct *ibm = (struct ibm_struct *)data;
+       char *kernbuf;
+       int ret;
+
+       if (!ibm || !ibm->write)
+               return -EINVAL;
+
+       kernbuf = kmalloc(count + 2, GFP_KERNEL);
+       if (!kernbuf)
+               return -ENOMEM;
+
+        if (copy_from_user(kernbuf, userbuf, count)) {
+               kfree(kernbuf);
+                return -EFAULT;
+       }
+
+       kernbuf[count] = 0;
+       strcat(kernbuf, ",");
+       ret = ibm->write(ibm, kernbuf);
+       if (ret == 0)
+               ret = count;
+
+       kfree(kernbuf);
+
+        return ret;
+}
+
+static void dispatch_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct ibm_struct *ibm = (struct ibm_struct *)data;
+
+       if (!ibm || !ibm->notify)
+               return;
+
+       ibm->notify(ibm, event);
+}
+
+static int setup_notify(struct ibm_struct *ibm)
+{
+       acpi_status status;
+       int ret;
+
+       if (!*ibm->handle)
+               return 0;
+
+       ret = acpi_bus_get_device(*ibm->handle, &ibm->device);
+       if (ret < 0) {
+               printk(IBM_ERR "%s device not present\n", ibm->name);
+               return 0;
+       }
+
+       acpi_driver_data(ibm->device) = ibm;
+       sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name);
+
+       status = acpi_install_notify_handler(*ibm->handle, ibm->type,
+                                            dispatch_notify, ibm);
+       if (ACPI_FAILURE(status)) {
+               printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n",
+                      ibm->name, status);
+               return -ENODEV;
+       }
+
+       ibm->notify_installed = 1;
+
+       return 0;
+}
+
+static int device_add(struct acpi_device *device)
+{
+       return 0;
+}
+
+static int register_driver(struct ibm_struct *ibm)
+{
+       int ret;
+
+       ibm->driver = kmalloc(sizeof(struct acpi_driver), GFP_KERNEL);
+       if (!ibm->driver) {
+               printk(IBM_ERR "kmalloc(ibm->driver) failed\n");
+               return -1;
+       }
+
+       memset(ibm->driver, 0, sizeof(struct acpi_driver));
+       sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name);
+       ibm->driver->ids = ibm->hid;
+       ibm->driver->ops.add = &device_add;
+
+       ret = acpi_bus_register_driver(ibm->driver);
+       if (ret < 0) {
+               printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
+                      ibm->hid, ret);
+               kfree(ibm->driver);
+       }
+
+       return ret;
+}
+
+static int ibm_init(struct ibm_struct *ibm)
+{
+       int ret;
+       struct proc_dir_entry *entry;
+
+       if (ibm->experimental && !experimental)
+               return 0;
+
+       if (ibm->hid) {
+               ret = register_driver(ibm);
+               if (ret < 0)
+                       return ret;
+               ibm->driver_registered = 1;
+       }
+
+       if (ibm->init) {
+               ret = ibm->init(ibm);
+               if (ret != 0)
+                       return ret;
+               ibm->init_called = 1;
+       }
+
+       entry = create_proc_entry(ibm->name, S_IFREG | S_IRUGO | S_IWUSR,
+                                 proc_dir);
+       if (!entry) {
+               printk(IBM_ERR "unable to create proc entry %s\n", ibm->name);
+               return -ENODEV;
+       }
+       entry->owner = THIS_MODULE;
+       ibm->proc_created = 1;
+       
+       entry->data = ibm;
+       if (ibm->read)
+               entry->read_proc = &dispatch_read;
+       if (ibm->write)
+               entry->write_proc = &dispatch_write;
+
+       if (ibm->notify) {
+               ret = setup_notify(ibm);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void ibm_exit(struct ibm_struct *ibm)
+{
+       if (ibm->notify_installed)
+               acpi_remove_notify_handler(*ibm->handle, ibm->type,
+                                          dispatch_notify);
+
+       if (ibm->proc_created)
+               remove_proc_entry(ibm->name, proc_dir);
+
+       if (ibm->init_called && ibm->exit)
+               ibm->exit(ibm);
+
+       if (ibm->driver_registered) {
+               acpi_bus_unregister_driver(ibm->driver);
+               kfree(ibm->driver);
+       }
+}
+
+static int ibm_handle_init(char *name,
+                          acpi_handle *handle, acpi_handle parent,
+                          char **paths, int num_paths, int required)
+{
+       int i;
+       acpi_status status;
+
+       for (i=0; i<num_paths; i++) {
+               status = acpi_get_handle(parent, paths[i], handle);
+               if (ACPI_SUCCESS(status))
+                       return 0;
+       }
+       
+       *handle = NULL;
+
+       if (required) {
+               printk(IBM_ERR "%s object not found\n", name);
+               return -1;
+       }
+
+       return 0;
+}
+
+#define IBM_HANDLE_INIT(object, required)                              \
+       ibm_handle_init(#object, &object##_handle, *object##_parent,    \
+               object##_paths, sizeof(object##_paths)/sizeof(char*), required)
+
+
+static int set_ibm_param(const char *val, struct kernel_param *kp)
+{
+       unsigned int i;
+       char arg_with_comma[32];
+
+       if (strlen(val) > 30)
+               return -ENOSPC;
+
+       strcpy(arg_with_comma, val);
+       strcat(arg_with_comma, ",");
+
+       for (i=0; i<NUM_IBMS; i++)
+               if (strcmp(ibms[i].name, kp->name) == 0)
+                       return ibms[i].write(&ibms[i], arg_with_comma);
+       BUG();
+       return -EINVAL;
+}
+
+#define IBM_PARAM(feature) \
+       module_param_call(feature, set_ibm_param, NULL, NULL, 0)
+
+static void __exit acpi_ibm_exit(void)
+{
+       int i;
+
+       for (i=NUM_IBMS-1; i>=0; i--)
+               ibm_exit(&ibms[i]);
+
+       remove_proc_entry(IBM_DIR, acpi_root_dir);
+}
+
+static int __init acpi_ibm_init(void)
+{
+       int ret, i;
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       /* these handles are required */
+       if (IBM_HANDLE_INIT(ec,   1) < 0 ||
+           IBM_HANDLE_INIT(hkey, 1) < 0 ||
+           IBM_HANDLE_INIT(vid,  1) < 0 ||
+           IBM_HANDLE_INIT(beep, 1) < 0)
+               return -ENODEV;
+
+       /* these handles have alternatives */
+       IBM_HANDLE_INIT(lght, 0);
+       if (IBM_HANDLE_INIT(cmos, !lght_handle) < 0)
+               return -ENODEV;
+       IBM_HANDLE_INIT(sysl, 0);
+       if (IBM_HANDLE_INIT(led, !sysl_handle) < 0)
+               return -ENODEV;
+
+       /* these handles are not required */
+       IBM_HANDLE_INIT(dock,  0);
+       IBM_HANDLE_INIT(bay,   0);
+       IBM_HANDLE_INIT(bayej, 0);
+       IBM_HANDLE_INIT(bled,  0);
+
+       proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir);
+       if (!proc_dir) {
+               printk(IBM_ERR "unable to create proc dir %s", IBM_DIR);
+               return -ENODEV;
+       }
+       proc_dir->owner = THIS_MODULE;
+       
+       for (i=0; i<NUM_IBMS; i++) {
+               ret = ibm_init(&ibms[i]);
+               if (ret < 0) {
+                       acpi_ibm_exit();
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+module_init(acpi_ibm_init);
+module_exit(acpi_ibm_exit);
+
+MODULE_AUTHOR("Borislav Deianov");
+MODULE_DESCRIPTION(IBM_DESC);
+MODULE_LICENSE("GPL");
+
+IBM_PARAM(hotkey);
+IBM_PARAM(bluetooth);
+IBM_PARAM(video);
+IBM_PARAM(light);
+IBM_PARAM(dock);
+IBM_PARAM(bay);
+IBM_PARAM(cmos);
+IBM_PARAM(led);
+IBM_PARAM(beep);
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
new file mode 100644 (file)
index 0000000..e517a11
--- /dev/null
@@ -0,0 +1,989 @@
+/*
+ * acpi_processor.c - ACPI Processor Driver ($Revision: 71 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
+ *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ *                     - Added processor hotplug support
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *  TBD:
+ *     1. Make # power states dynamic.
+ *     2. Support duty_cycle values that span bit 4.
+ *     3. Optimize by having scheduler determine business instead of
+ *        having us try to calculate it here.
+ *     4. Need C1 timing -- must modify kernel (IRQ handler) to get this.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+#include <linux/cpufreq.h>
+#include <linux/cpu.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/dmi.h>
+#include <linux/moduleparam.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/cpu.h>
+#include <asm/delay.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+#include <asm/smp.h>
+#include <asm/acpi.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/processor.h>
+
+
+#define ACPI_PROCESSOR_COMPONENT       0x01000000
+#define ACPI_PROCESSOR_CLASS           "processor"
+#define ACPI_PROCESSOR_DRIVER_NAME     "ACPI Processor Driver"
+#define ACPI_PROCESSOR_DEVICE_NAME     "Processor"
+#define ACPI_PROCESSOR_FILE_INFO       "info"
+#define ACPI_PROCESSOR_FILE_THROTTLING "throttling"
+#define ACPI_PROCESSOR_FILE_LIMIT      "limit"
+#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
+#define ACPI_PROCESSOR_NOTIFY_POWER    0x81
+
+#define ACPI_PROCESSOR_LIMIT_USER      0
+#define ACPI_PROCESSOR_LIMIT_THERMAL   1
+
+#define ACPI_STA_PRESENT 0x00000001
+
+#define _COMPONENT             ACPI_PROCESSOR_COMPONENT
+ACPI_MODULE_NAME               ("acpi_processor")
+
+MODULE_AUTHOR("Paul Diefenbaugh");
+MODULE_DESCRIPTION(ACPI_PROCESSOR_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+
+static int acpi_processor_add (struct acpi_device *device);
+static int acpi_processor_start (struct acpi_device *device);
+static int acpi_processor_remove (struct acpi_device *device, int type);
+static int acpi_processor_info_open_fs(struct inode *inode, struct file *file);
+static void acpi_processor_notify ( acpi_handle        handle, u32 event, void *data);
+static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu);
+static int acpi_processor_handle_eject(struct acpi_processor *pr);
+
+static struct acpi_driver acpi_processor_driver = {
+       .name =         ACPI_PROCESSOR_DRIVER_NAME,
+       .class =        ACPI_PROCESSOR_CLASS,
+       .ids =          ACPI_PROCESSOR_HID,
+       .ops =          {
+                               .add =          acpi_processor_add,
+                               .remove =       acpi_processor_remove,
+                               .start  =       acpi_processor_start,
+                       },
+};
+
+#define INSTALL_NOTIFY_HANDLER         1
+#define UNINSTALL_NOTIFY_HANDLER       2
+
+
+struct file_operations acpi_processor_info_fops = {
+       .open           = acpi_processor_info_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+
+struct acpi_processor  *processors[NR_CPUS];
+struct acpi_processor_errata errata;
+
+
+/* --------------------------------------------------------------------------
+                                Errata Handling
+   -------------------------------------------------------------------------- */
+
+int
+acpi_processor_errata_piix4 (
+       struct pci_dev          *dev)
+{
+       u8                      rev = 0;
+       u8                      value1 = 0;
+       u8                      value2 = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_errata_piix4");
+
+       if (!dev)
+               return_VALUE(-EINVAL);
+
+       /*
+        * Note that 'dev' references the PIIX4 ACPI Controller.
+        */
+
+       pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
+
+       switch (rev) {
+       case 0:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 A-step\n"));
+               break;
+       case 1:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 B-step\n"));
+               break;
+       case 2:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4E\n"));
+               break;
+       case 3:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4M\n"));
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unknown PIIX4\n"));
+               break;
+       }
+
+       switch (rev) {
+
+       case 0:         /* PIIX4 A-step */
+       case 1:         /* PIIX4 B-step */
+               /*
+                * See specification changes #13 ("Manual Throttle Duty Cycle")
+                * and #14 ("Enabling and Disabling Manual Throttle"), plus
+                * erratum #5 ("STPCLK# Deassertion Time") from the January
+                * 2002 PIIX4 specification update.  Applies to only older
+                * PIIX4 models.
+                */
+               errata.piix4.throttle = 1;
+
+       case 2:         /* PIIX4E */
+       case 3:         /* PIIX4M */
+               /*
+                * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA
+                * Livelock") from the January 2002 PIIX4 specification update.
+                * Applies to all PIIX4 models.
+                */
+
+               /*
+                * BM-IDE
+                * ------
+                * Find the PIIX4 IDE Controller and get the Bus Master IDE
+                * Status register address.  We'll use this later to read
+                * each IDE controller's DMA status to make sure we catch all
+                * DMA activity.
+                */
+               dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+                          PCI_DEVICE_ID_INTEL_82371AB,
+                           PCI_ANY_ID, PCI_ANY_ID, NULL);
+               if (dev) {
+                       errata.piix4.bmisx = pci_resource_start(dev, 4);
+                       pci_dev_put(dev);
+               }
+
+               /*
+                * Type-F DMA
+                * ----------
+                * Find the PIIX4 ISA Controller and read the Motherboard
+                * DMA controller's status to see if Type-F (Fast) DMA mode
+                * is enabled (bit 7) on either channel.  Note that we'll
+                * disable C3 support if this is enabled, as some legacy
+                * devices won't operate well if fast DMA is disabled.
+                */
+               dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+                       PCI_DEVICE_ID_INTEL_82371AB_0,
+                       PCI_ANY_ID, PCI_ANY_ID, NULL);
+               if (dev) {
+                       pci_read_config_byte(dev, 0x76, &value1);
+                       pci_read_config_byte(dev, 0x77, &value2);
+                       if ((value1 & 0x80) || (value2 & 0x80))
+                               errata.piix4.fdma = 1;
+                       pci_dev_put(dev);
+               }
+
+               break;
+       }
+
+       if (errata.piix4.bmisx)
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Bus master activity detection (BM-IDE) erratum enabled\n"));
+       if (errata.piix4.fdma)
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Type-F DMA livelock erratum (C3 disabled)\n"));
+
+       return_VALUE(0);
+}
+
+
+int
+acpi_processor_errata (
+       struct acpi_processor   *pr)
+{
+       int                     result = 0;
+       struct pci_dev          *dev = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_errata");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       /*
+        * PIIX4
+        */
+       dev = pci_get_subsys(PCI_VENDOR_ID_INTEL,
+               PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, PCI_ANY_ID, NULL);
+       if (dev) {
+               result = acpi_processor_errata_piix4(dev);
+               pci_dev_put(dev);
+       }
+
+       return_VALUE(result);
+}
+
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+struct proc_dir_entry          *acpi_processor_dir = NULL;
+
+static int acpi_processor_info_seq_show(struct seq_file *seq, void *offset)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *)seq->private;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_info_seq_show");
+
+       if (!pr)
+               goto end;
+
+       seq_printf(seq, "processor id:            %d\n"
+                       "acpi id:                 %d\n"
+                       "bus mastering control:   %s\n"
+                       "power management:        %s\n"
+                       "throttling control:      %s\n"
+                       "limit interface:         %s\n",
+                       pr->id,
+                       pr->acpi_id,
+                       pr->flags.bm_control ? "yes" : "no",
+                       pr->flags.power ? "yes" : "no",
+                       pr->flags.throttling ? "yes" : "no",
+                       pr->flags.limit ? "yes" : "no");
+
+end:
+       return_VALUE(0);
+}
+
+static int acpi_processor_info_open_fs(struct inode *inode, struct file *file)
+{
+       return single_open(file, acpi_processor_info_seq_show,
+                                               PDE(inode)->data);
+}
+
+
+static int
+acpi_processor_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_add_fs");
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                       acpi_processor_dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+       }
+       acpi_device_dir(device)->owner = THIS_MODULE;
+
+       /* 'info' [R] */
+       entry = create_proc_entry(ACPI_PROCESSOR_FILE_INFO,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_PROCESSOR_FILE_INFO));
+       else {
+               entry->proc_fops = &acpi_processor_info_fops;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       /* 'throttling' [R/W] */
+       entry = create_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_PROCESSOR_FILE_THROTTLING));
+       else {
+               entry->proc_fops = &acpi_processor_throttling_fops;
+               entry->proc_fops->write = acpi_processor_write_throttling;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       /* 'limit' [R/W] */
+       entry = create_proc_entry(ACPI_PROCESSOR_FILE_LIMIT,
+               S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_PROCESSOR_FILE_LIMIT));
+       else {
+               entry->proc_fops = &acpi_processor_limit_fops;
+               entry->proc_fops->write = acpi_processor_write_limit;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_processor_remove_fs (
+       struct acpi_device      *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_remove_fs");
+
+       if (acpi_device_dir(device)) {
+               remove_proc_entry(ACPI_PROCESSOR_FILE_INFO,acpi_device_dir(device));
+               remove_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING,
+                       acpi_device_dir(device));
+               remove_proc_entry(ACPI_PROCESSOR_FILE_LIMIT,acpi_device_dir(device));
+               remove_proc_entry(acpi_device_bid(device), acpi_processor_dir);
+               acpi_device_dir(device) = NULL;
+       }
+
+       return_VALUE(0);
+}
+
+/* Use the acpiid in MADT to map cpus in case of SMP */
+#ifndef CONFIG_SMP
+#define convert_acpiid_to_cpu(acpi_id) (0xff)
+#else
+
+#ifdef CONFIG_IA64
+#define arch_acpiid_to_apicid  ia64_acpiid_to_sapicid
+#define arch_cpu_to_apicid     ia64_cpu_to_sapicid
+#define ARCH_BAD_APICID                (0xffff)
+#else
+#define arch_acpiid_to_apicid  x86_acpiid_to_apicid
+#define arch_cpu_to_apicid     x86_cpu_to_apicid
+#define ARCH_BAD_APICID                (0xff)
+#endif
+
+static u8 convert_acpiid_to_cpu(u8 acpi_id)
+{
+       u16 apic_id;
+       int i;
+
+       apic_id = arch_acpiid_to_apicid[acpi_id];
+       if (apic_id == ARCH_BAD_APICID)
+               return -1;
+
+       for (i = 0; i < NR_CPUS; i++) {
+               if (arch_cpu_to_apicid[i] == apic_id)
+                       return i;
+       }
+       return -1;
+}
+#endif
+
+/* --------------------------------------------------------------------------
+                                 Driver Interface
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_processor_get_info (
+       struct acpi_processor   *pr)
+{
+       acpi_status             status = 0;
+       union acpi_object       object = {0};
+       struct acpi_buffer      buffer = {sizeof(union acpi_object), &object};
+       u8                      cpu_index;
+       static int              cpu0_initialized;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_info");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if (num_online_cpus() > 1)
+               errata.smp = TRUE;
+
+       acpi_processor_errata(pr);
+
+       /*
+        * Check to see if we have bus mastering arbitration control.  This
+        * is required for proper C3 usage (to maintain cache coherency).
+        */
+       if (acpi_fadt.V1_pm2_cnt_blk && acpi_fadt.pm2_cnt_len) {
+               pr->flags.bm_control = 1;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Bus mastering arbitration control present\n"));
+       }
+       else
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "No bus mastering arbitration control\n"));
+
+       /*
+        * Evalute the processor object.  Note that it is common on SMP to
+        * have the first (boot) processor with a valid PBLK address while
+        * all others have a NULL address.
+        */
+       status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error evaluating processor object\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       /*
+        * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
+        *      >>> 'acpi_get_processor_id(acpi_id, &id)' in arch/xxx/acpi.c
+        */
+       pr->acpi_id = object.processor.proc_id;
+
+       cpu_index = convert_acpiid_to_cpu(pr->acpi_id);
+
+       /* Handle UP system running SMP kernel, with no LAPIC in MADT */
+       if ( !cpu0_initialized && (cpu_index == 0xff) &&
+                       (num_online_cpus() == 1)) {
+               cpu_index = 0;
+       }
+
+       cpu0_initialized = 1;
+
+       pr->id = cpu_index;
+
+       /*
+        *  Extra Processor objects may be enumerated on MP systems with
+        *  less than the max # of CPUs. They should be ignored _iff
+        *  they are physically not present.
+        */
+       if (cpu_index >=  NR_CPUS) {
+               if (ACPI_FAILURE(acpi_processor_hotadd_init(pr->handle, &pr->id))) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                               "Error getting cpuindex for acpiid 0x%x\n",
+                               pr->acpi_id));
+                       return_VALUE(-ENODEV);
+               }
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id,
+               pr->acpi_id));
+
+       if (!object.processor.pblk_address)
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No PBLK (NULL address)\n"));
+       else if (object.processor.pblk_length != 6)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid PBLK length [%d]\n",
+                       object.processor.pblk_length));
+       else {
+               pr->throttling.address = object.processor.pblk_address;
+               pr->throttling.duty_offset = acpi_fadt.duty_offset;
+               pr->throttling.duty_width = acpi_fadt.duty_width;
+
+               pr->pblk = object.processor.pblk_address;
+
+               /*
+                * We don't care about error returns - we just try to mark
+                * these reserved so that nobody else is confused into thinking
+                * that this region might be unused..
+                *
+                * (In particular, allocating the IO range for Cardbus)
+                */
+               request_region(pr->throttling.address, 6, "ACPI CPU throttle");
+       }
+
+#ifdef CONFIG_CPU_FREQ
+       acpi_processor_ppc_has_changed(pr);
+#endif
+       acpi_processor_get_throttling_info(pr);
+       acpi_processor_get_limit_info(pr);
+
+       return_VALUE(0);
+}
+
+static int
+acpi_processor_start(
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_processor   *pr;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_start");
+
+       pr = acpi_driver_data(device);
+
+       result = acpi_processor_get_info(pr);
+       if (result) {
+               /* Processor is physically not present */
+               return_VALUE(0);
+       }
+
+       BUG_ON((pr->id >= NR_CPUS) || (pr->id < 0));
+
+       processors[pr->id] = pr;
+
+       result = acpi_processor_add_fs(device);
+       if (result)
+               goto end;
+
+       status = acpi_install_notify_handler(pr->handle, ACPI_DEVICE_NOTIFY,
+               acpi_processor_notify, pr);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error installing device notify handler\n"));
+       }
+
+       acpi_processor_power_init(pr, device);
+
+       if (pr->flags.throttling) {
+               printk(KERN_INFO PREFIX "%s [%s] (supports",
+                       acpi_device_name(device), acpi_device_bid(device));
+               printk(" %d throttling states", pr->throttling.state_count);
+               printk(")\n");
+       }
+
+end:
+
+       return_VALUE(result);
+}
+
+
+
+static void
+acpi_processor_notify (
+       acpi_handle             handle,
+       u32                     event,
+       void                    *data)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *) data;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_notify");
+
+       if (!pr)
+               return_VOID;
+
+       if (acpi_bus_get_device(pr->handle, &device))
+               return_VOID;
+
+       switch (event) {
+       case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
+               acpi_processor_ppc_has_changed(pr);
+               acpi_bus_generate_event(device, event,
+                       pr->performance_platform_limit);
+               break;
+       case ACPI_PROCESSOR_NOTIFY_POWER:
+               acpi_processor_cst_has_changed(pr);
+               acpi_bus_generate_event(device, event, 0);
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Unsupported event [0x%x]\n", event));
+               break;
+       }
+
+       return_VOID;
+}
+
+
+static int
+acpi_processor_add (
+       struct acpi_device      *device)
+{
+       struct acpi_processor   *pr = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       pr = kmalloc(sizeof(struct acpi_processor), GFP_KERNEL);
+       if (!pr)
+               return_VALUE(-ENOMEM);
+       memset(pr, 0, sizeof(struct acpi_processor));
+
+       pr->handle = device->handle;
+       strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME);
+       strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS);
+       acpi_driver_data(device) = pr;
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_processor_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       acpi_status             status = AE_OK;
+       struct acpi_processor   *pr = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       pr = (struct acpi_processor *) acpi_driver_data(device);
+
+       if (pr->id >= NR_CPUS) {
+               kfree(pr);
+               return_VALUE(0);
+       }
+
+       if (type == ACPI_BUS_REMOVAL_EJECT) {
+               if (acpi_processor_handle_eject(pr))
+                       return_VALUE(-EINVAL);
+       }
+
+       acpi_processor_power_exit(pr, device);
+
+       status = acpi_remove_notify_handler(pr->handle, ACPI_DEVICE_NOTIFY,
+               acpi_processor_notify);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error removing notify handler\n"));
+       }
+
+       acpi_processor_remove_fs(device);
+
+       processors[pr->id] = NULL;
+
+       kfree(pr);
+
+       return_VALUE(0);
+}
+
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+/****************************************************************************
+ *     Acpi processor hotplug support                                      *
+ ****************************************************************************/
+
+static int is_processor_present(acpi_handle handle);
+
+static int
+is_processor_present(
+       acpi_handle handle)
+{
+       acpi_status             status;
+       unsigned long           sta = 0;
+
+       ACPI_FUNCTION_TRACE("is_processor_present");
+
+       status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+       if (ACPI_FAILURE(status) || !(sta & ACPI_STA_PRESENT)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Processor Device is not present\n"));
+               return_VALUE(0);
+       }
+       return_VALUE(1);
+}
+
+
+static
+int acpi_processor_device_add(
+       acpi_handle     handle,
+       struct acpi_device **device)
+{
+       acpi_handle             phandle;
+       struct acpi_device      *pdev;
+       struct acpi_processor   *pr;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_device_add");
+
+       if (acpi_get_parent(handle, &phandle)) {
+               return_VALUE(-ENODEV);
+       }
+
+       if (acpi_bus_get_device(phandle, &pdev)) {
+               return_VALUE(-ENODEV);
+       }
+
+       if (acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_PROCESSOR)) {
+               return_VALUE(-ENODEV);
+       }
+
+       acpi_bus_scan(*device);
+
+       pr = acpi_driver_data(*device);
+       if (!pr)
+               return_VALUE(-ENODEV);
+
+       if ((pr->id >=0) && (pr->id < NR_CPUS)) {
+               kobject_hotplug(&(*device)->kobj, KOBJ_ONLINE);
+       }
+       return_VALUE(0);
+}
+
+
+static void
+acpi_processor_hotplug_notify (
+       acpi_handle             handle,
+       u32                     event,
+       void                    *data)
+{
+       struct acpi_processor   *pr;
+       struct acpi_device      *device = NULL;
+       int result;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_hotplug_notify");
+
+       switch (event) {
+       case ACPI_NOTIFY_BUS_CHECK:
+       case ACPI_NOTIFY_DEVICE_CHECK:
+               printk("Processor driver received %s event\n",
+                       (event==ACPI_NOTIFY_BUS_CHECK)?
+                       "ACPI_NOTIFY_BUS_CHECK":"ACPI_NOTIFY_DEVICE_CHECK");
+
+               if (!is_processor_present(handle))
+                       break;
+
+               if (acpi_bus_get_device(handle, &device)) {
+                       result = acpi_processor_device_add(handle, &device);
+                       if (result)
+                               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                       "Unable to add the device\n"));
+                       break;
+               }
+
+               pr = acpi_driver_data(device);
+               if (!pr) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                               "Driver data is NULL\n"));
+                       break;
+               }
+
+               if (pr->id >= 0 && (pr->id < NR_CPUS)) {
+                       kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+                       break;
+               }
+
+               result = acpi_processor_start(device);
+               if ((!result) && ((pr->id >=0) && (pr->id < NR_CPUS))) {
+                       kobject_hotplug(&device->kobj, KOBJ_ONLINE);
+               } else {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                               "Device [%s] failed to start\n",
+                               acpi_device_bid(device)));
+               }
+       break;
+       case ACPI_NOTIFY_EJECT_REQUEST:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,"received ACPI_NOTIFY_EJECT_REQUEST\n"));
+
+               if (acpi_bus_get_device(handle, &device)) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"Device don't exist, dropping EJECT\n"));
+                       break;
+               }
+               pr = acpi_driver_data(device);
+               if (!pr) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"Driver data is NULL, dropping EJECT\n"));
+                       return_VOID;
+               }
+
+               if ((pr->id < NR_CPUS) && (cpu_present(pr->id)))
+                       kobject_hotplug(&device->kobj, KOBJ_OFFLINE);
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Unsupported event [0x%x]\n", event));
+               break;
+       }
+
+       return_VOID;
+}
+
+static acpi_status
+processor_walk_namespace_cb(acpi_handle handle,
+       u32 lvl,
+       void *context,
+       void **rv)
+{
+       acpi_status                     status;
+       int *action = context;
+       acpi_object_type        type = 0;
+
+       status = acpi_get_type(handle, &type);
+       if (ACPI_FAILURE(status))
+               return(AE_OK);
+
+       if (type != ACPI_TYPE_PROCESSOR)
+               return(AE_OK);
+
+       switch(*action) {
+       case INSTALL_NOTIFY_HANDLER:
+               acpi_install_notify_handler(handle,
+                       ACPI_SYSTEM_NOTIFY,
+                       acpi_processor_hotplug_notify,
+                       NULL);
+               break;
+       case UNINSTALL_NOTIFY_HANDLER:
+               acpi_remove_notify_handler(handle,
+                       ACPI_SYSTEM_NOTIFY,
+                       acpi_processor_hotplug_notify);
+               break;
+       default:
+               break;
+       }
+
+       return(AE_OK);
+}
+
+
+static acpi_status
+acpi_processor_hotadd_init(
+       acpi_handle             handle,
+       int                     *p_cpu)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_hotadd_init");
+
+       if (!is_processor_present(handle)) {
+               return_VALUE(AE_ERROR);
+       }
+
+       if (acpi_map_lsapic(handle, p_cpu))
+               return_VALUE(AE_ERROR);
+
+       if (arch_register_cpu(*p_cpu)) {
+               acpi_unmap_lsapic(*p_cpu);
+               return_VALUE(AE_ERROR);
+       }
+
+       return_VALUE(AE_OK);
+}
+
+
+static int
+acpi_processor_handle_eject(struct acpi_processor *pr)
+{
+       if (cpu_online(pr->id)) {
+               return(-EINVAL);
+       }
+       arch_unregister_cpu(pr->id);
+       acpi_unmap_lsapic(pr->id);
+       return(0);
+}
+#else
+static acpi_status
+acpi_processor_hotadd_init(
+       acpi_handle             handle,
+       int                     *p_cpu)
+{
+       return AE_ERROR;
+}
+static int
+acpi_processor_handle_eject(struct acpi_processor *pr)
+{
+       return(-EINVAL);
+}
+#endif
+
+
+static
+void acpi_processor_install_hotplug_notify(void)
+{
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+       int action = INSTALL_NOTIFY_HANDLER;
+       acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
+                                    ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX,
+                                    processor_walk_namespace_cb,
+                                    &action, NULL);
+#endif
+}
+
+
+static
+void acpi_processor_uninstall_hotplug_notify(void)
+{
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+       int action = UNINSTALL_NOTIFY_HANDLER;
+       acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
+                                    ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX,
+                                    processor_walk_namespace_cb,
+                                    &action, NULL);
+#endif
+}
+
+/*
+ * We keep the driver loaded even when ACPI is not running.
+ * This is needed for the powernow-k8 driver, that works even without
+ * ACPI, but needs symbols from this driver
+ */
+
+static int __init
+acpi_processor_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_init");
+
+       memset(&processors, 0, sizeof(processors));
+       memset(&errata, 0, sizeof(errata));
+
+       acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir);
+       if (!acpi_processor_dir)
+               return_VALUE(0);
+       acpi_processor_dir->owner = THIS_MODULE;
+
+       result = acpi_bus_register_driver(&acpi_processor_driver);
+       if (result < 0) {
+               remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir);
+               return_VALUE(0);
+       }
+
+       acpi_processor_install_hotplug_notify();
+
+       acpi_thermal_cpufreq_init();
+
+       acpi_processor_ppc_init();
+
+       return_VALUE(0);
+}
+
+
+static void __exit
+acpi_processor_exit (void)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_exit");
+
+       acpi_processor_ppc_exit();
+
+       acpi_thermal_cpufreq_exit();
+
+       acpi_processor_uninstall_hotplug_notify();
+
+       acpi_bus_unregister_driver(&acpi_processor_driver);
+
+       remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir);
+
+       return_VOID;
+}
+
+
+module_init(acpi_processor_init);
+module_exit(acpi_processor_exit);
+
+EXPORT_SYMBOL(acpi_processor_set_thermal_limit);
+
+MODULE_ALIAS("processor");
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
new file mode 100644 (file)
index 0000000..05a1781
--- /dev/null
@@ -0,0 +1,1017 @@
+/*
+ * processor_idle - idle state submodule to the ACPI processor driver
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
+ *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ *                     - Added processor hotplug support
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/moduleparam.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/processor.h>
+
+#define ACPI_PROCESSOR_COMPONENT        0x01000000
+#define ACPI_PROCESSOR_CLASS            "processor"
+#define ACPI_PROCESSOR_DRIVER_NAME      "ACPI Processor Driver"
+#define _COMPONENT              ACPI_PROCESSOR_COMPONENT
+ACPI_MODULE_NAME                ("acpi_processor")
+
+#define ACPI_PROCESSOR_FILE_POWER      "power"
+
+#define US_TO_PM_TIMER_TICKS(t)                ((t * (PM_TIMER_FREQUENCY/1000)) / 1000)
+#define C2_OVERHEAD                    4       /* 1us (3.579 ticks per us) */
+#define C3_OVERHEAD                    4       /* 1us (3.579 ticks per us) */
+
+static void (*pm_idle_save)(void);
+module_param(max_cstate, uint, 0644);
+
+static unsigned int nocst = 0;
+module_param(nocst, uint, 0000);
+
+/*
+ * bm_history -- bit-mask with a bit per jiffy of bus-master activity
+ * 1000 HZ: 0xFFFFFFFF: 32 jiffies = 32ms
+ * 800 HZ: 0xFFFFFFFF: 32 jiffies = 40ms
+ * 100 HZ: 0x0000000F: 4 jiffies = 40ms
+ * reduce history for more aggressive entry into C3
+ */
+static unsigned int bm_history = (HZ >= 800 ? 0xFFFFFFFF : ((1U << (HZ / 25)) - 1));
+module_param(bm_history, uint, 0644);
+/* --------------------------------------------------------------------------
+                                Power Management
+   -------------------------------------------------------------------------- */
+
+/*
+ * IBM ThinkPad R40e crashes mysteriously when going into C2 or C3.
+ * For now disable this. Probably a bug somewhere else.
+ *
+ * To skip this limit, boot/load with a large max_cstate limit.
+ */
+static int no_c2c3(struct dmi_system_id *id)
+{
+       if (max_cstate > ACPI_PROCESSOR_MAX_POWER)
+               return 0;
+
+       printk(KERN_NOTICE PREFIX "%s detected - C2,C3 disabled."
+               " Override with \"processor.max_cstate=%d\"\n", id->ident,
+              ACPI_PROCESSOR_MAX_POWER + 1);
+
+       max_cstate = 1;
+
+       return 0;
+}
+
+
+
+
+static struct dmi_system_id __initdata processor_power_dmi_table[] = {
+       { no_c2c3, "IBM ThinkPad R40e", {
+         DMI_MATCH(DMI_BIOS_VENDOR,"IBM"),
+         DMI_MATCH(DMI_BIOS_VERSION,"1SET60WW") }},
+       { no_c2c3, "Medion 41700", {
+         DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
+         DMI_MATCH(DMI_BIOS_VERSION,"R01-A1J") }},
+       {},
+};
+
+
+static inline u32
+ticks_elapsed (
+       u32                     t1,
+       u32                     t2)
+{
+       if (t2 >= t1)
+               return (t2 - t1);
+       else if (!acpi_fadt.tmr_val_ext)
+               return (((0x00FFFFFF - t1) + t2) & 0x00FFFFFF);
+       else
+               return ((0xFFFFFFFF - t1) + t2);
+}
+
+
+static void
+acpi_processor_power_activate (
+       struct acpi_processor   *pr,
+       struct acpi_processor_cx  *new)
+{
+       struct acpi_processor_cx  *old;
+
+       if (!pr || !new)
+               return;
+
+       old = pr->power.state;
+
+       if (old)
+               old->promotion.count = 0;
+       new->demotion.count = 0;
+
+       /* Cleanup from old state. */
+       if (old) {
+               switch (old->type) {
+               case ACPI_STATE_C3:
+                       /* Disable bus master reload */
+                       if (new->type != ACPI_STATE_C3)
+                               acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0, ACPI_MTX_DO_NOT_LOCK);
+                       break;
+               }
+       }
+
+       /* Prepare to use new state. */
+       switch (new->type) {
+       case ACPI_STATE_C3:
+               /* Enable bus master reload */
+               if (old->type != ACPI_STATE_C3)
+                       acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 1, ACPI_MTX_DO_NOT_LOCK);
+               break;
+       }
+
+       pr->power.state = new;
+
+       return;
+}
+
+
+static void acpi_processor_idle (void)
+{
+       struct acpi_processor   *pr = NULL;
+       struct acpi_processor_cx *cx = NULL;
+       struct acpi_processor_cx *next_state = NULL;
+       int                     sleep_ticks = 0;
+       u32                     t1, t2 = 0;
+
+       pr = processors[_smp_processor_id()];
+       if (!pr)
+               return;
+
+       /*
+        * Interrupts must be disabled during bus mastering calculations and
+        * for C2/C3 transitions.
+        */
+       local_irq_disable();
+
+       /*
+        * Check whether we truly need to go idle, or should
+        * reschedule:
+        */
+       if (unlikely(need_resched())) {
+               local_irq_enable();
+               return;
+       }
+
+       cx = pr->power.state;
+       if (!cx)
+               goto easy_out;
+
+       /*
+        * Check BM Activity
+        * -----------------
+        * Check for bus mastering activity (if required), record, and check
+        * for demotion.
+        */
+       if (pr->flags.bm_check) {
+               u32             bm_status = 0;
+               unsigned long   diff = jiffies - pr->power.bm_check_timestamp;
+
+               if (diff > 32)
+                       diff = 32;
+
+               while (diff) {
+                       /* if we didn't get called, assume there was busmaster activity */
+                       diff--;
+                       if (diff)
+                               pr->power.bm_activity |= 0x1;
+                       pr->power.bm_activity <<= 1;
+               }
+
+               acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS,
+                       &bm_status, ACPI_MTX_DO_NOT_LOCK);
+               if (bm_status) {
+                       pr->power.bm_activity++;
+                       acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS,
+                               1, ACPI_MTX_DO_NOT_LOCK);
+               }
+               /*
+                * PIIX4 Erratum #18: Note that BM_STS doesn't always reflect
+                * the true state of bus mastering activity; forcing us to
+                * manually check the BMIDEA bit of each IDE channel.
+                */
+               else if (errata.piix4.bmisx) {
+                       if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01)
+                               || (inb_p(errata.piix4.bmisx + 0x0A) & 0x01))
+                               pr->power.bm_activity++;
+               }
+
+               pr->power.bm_check_timestamp = jiffies;
+
+               /*
+                * Apply bus mastering demotion policy.  Automatically demote
+                * to avoid a faulty transition.  Note that the processor
+                * won't enter a low-power state during this call (to this
+                * funciton) but should upon the next.
+                *
+                * TBD: A better policy might be to fallback to the demotion
+                *      state (use it for this quantum only) istead of
+                *      demoting -- and rely on duration as our sole demotion
+                *      qualification.  This may, however, introduce DMA
+                *      issues (e.g. floppy DMA transfer overrun/underrun).
+                */
+               if (pr->power.bm_activity & cx->demotion.threshold.bm) {
+                       local_irq_enable();
+                       next_state = cx->demotion.state;
+                       goto end;
+               }
+       }
+
+       cx->usage++;
+
+       /*
+        * Sleep:
+        * ------
+        * Invoke the current Cx state to put the processor to sleep.
+        */
+       switch (cx->type) {
+
+       case ACPI_STATE_C1:
+               /*
+                * Invoke C1.
+                * Use the appropriate idle routine, the one that would
+                * be used without acpi C-states.
+                */
+               if (pm_idle_save)
+                       pm_idle_save();
+               else
+                       safe_halt();
+               /*
+                 * TBD: Can't get time duration while in C1, as resumes
+                *      go to an ISR rather than here.  Need to instrument
+                *      base interrupt handler.
+                */
+               sleep_ticks = 0xFFFFFFFF;
+               break;
+
+       case ACPI_STATE_C2:
+               /* Get start time (ticks) */
+               t1 = inl(acpi_fadt.xpm_tmr_blk.address);
+               /* Invoke C2 */
+               inb(cx->address);
+               /* Dummy op - must do something useless after P_LVL2 read */
+               t2 = inl(acpi_fadt.xpm_tmr_blk.address);
+               /* Get end time (ticks) */
+               t2 = inl(acpi_fadt.xpm_tmr_blk.address);
+               /* Re-enable interrupts */
+               local_irq_enable();
+               /* Compute time (ticks) that we were actually asleep */
+               sleep_ticks = ticks_elapsed(t1, t2) - cx->latency_ticks - C2_OVERHEAD;
+               break;
+
+       case ACPI_STATE_C3:
+               /* Disable bus master arbitration */
+               acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1, ACPI_MTX_DO_NOT_LOCK);
+               /* Get start time (ticks) */
+               t1 = inl(acpi_fadt.xpm_tmr_blk.address);
+               /* Invoke C3 */
+               inb(cx->address);
+               /* Dummy op - must do something useless after P_LVL3 read */
+               t2 = inl(acpi_fadt.xpm_tmr_blk.address);
+               /* Get end time (ticks) */
+               t2 = inl(acpi_fadt.xpm_tmr_blk.address);
+               /* Enable bus master arbitration */
+               acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0, ACPI_MTX_DO_NOT_LOCK);
+               /* Re-enable interrupts */
+               local_irq_enable();
+               /* Compute time (ticks) that we were actually asleep */
+               sleep_ticks = ticks_elapsed(t1, t2) - cx->latency_ticks - C3_OVERHEAD;
+               break;
+
+       default:
+               local_irq_enable();
+               return;
+       }
+
+       next_state = pr->power.state;
+
+       /*
+        * Promotion?
+        * ----------
+        * Track the number of longs (time asleep is greater than threshold)
+        * and promote when the count threshold is reached.  Note that bus
+        * mastering activity may prevent promotions.
+        * Do not promote above max_cstate.
+        */
+       if (cx->promotion.state &&
+           ((cx->promotion.state - pr->power.states) <= max_cstate)) {
+               if (sleep_ticks > cx->promotion.threshold.ticks) {
+                       cx->promotion.count++;
+                       cx->demotion.count = 0;
+                       if (cx->promotion.count >= cx->promotion.threshold.count) {
+                               if (pr->flags.bm_check) {
+                                       if (!(pr->power.bm_activity & cx->promotion.threshold.bm)) {
+                                               next_state = cx->promotion.state;
+                                               goto end;
+                                       }
+                               }
+                               else {
+                                       next_state = cx->promotion.state;
+                                       goto end;
+                               }
+                       }
+               }
+       }
+
+       /*
+        * Demotion?
+        * ---------
+        * Track the number of shorts (time asleep is less than time threshold)
+        * and demote when the usage threshold is reached.
+        */
+       if (cx->demotion.state) {
+               if (sleep_ticks < cx->demotion.threshold.ticks) {
+                       cx->demotion.count++;
+                       cx->promotion.count = 0;
+                       if (cx->demotion.count >= cx->demotion.threshold.count) {
+                               next_state = cx->demotion.state;
+                               goto end;
+                       }
+               }
+       }
+
+end:
+       /*
+        * Demote if current state exceeds max_cstate
+        */
+       if ((pr->power.state - pr->power.states) > max_cstate) {
+               if (cx->demotion.state)
+                       next_state = cx->demotion.state;
+       }
+
+       /*
+        * New Cx State?
+        * -------------
+        * If we're going to start using a new Cx state we must clean up
+        * from the previous and prepare to use the new.
+        */
+       if (next_state != pr->power.state)
+               acpi_processor_power_activate(pr, next_state);
+
+       return;
+
+ easy_out:
+       /* do C1 instead of busy loop */
+       if (pm_idle_save)
+               pm_idle_save();
+       else
+               safe_halt();
+       return;
+}
+
+
+static int
+acpi_processor_set_power_policy (
+       struct acpi_processor   *pr)
+{
+       unsigned int i;
+       unsigned int state_is_set = 0;
+       struct acpi_processor_cx *lower = NULL;
+       struct acpi_processor_cx *higher = NULL;
+       struct acpi_processor_cx *cx;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_set_power_policy");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       /*
+        * This function sets the default Cx state policy (OS idle handler).
+        * Our scheme is to promote quickly to C2 but more conservatively
+        * to C3.  We're favoring C2  for its characteristics of low latency
+        * (quick response), good power savings, and ability to allow bus
+        * mastering activity.  Note that the Cx state policy is completely
+        * customizable and can be altered dynamically.
+        */
+
+       /* startup state */
+       for (i=1; i < ACPI_PROCESSOR_MAX_POWER; i++) {
+               cx = &pr->power.states[i];
+               if (!cx->valid)
+                       continue;
+
+               if (!state_is_set)
+                       pr->power.state = cx;
+               state_is_set++;
+               break;
+       }
+
+       if (!state_is_set)
+               return_VALUE(-ENODEV);
+
+       /* demotion */
+       for (i=1; i < ACPI_PROCESSOR_MAX_POWER; i++) {
+               cx = &pr->power.states[i];
+               if (!cx->valid)
+                       continue;
+
+               if (lower) {
+                       cx->demotion.state = lower;
+                       cx->demotion.threshold.ticks = cx->latency_ticks;
+                       cx->demotion.threshold.count = 1;
+                       if (cx->type == ACPI_STATE_C3)
+                               cx->demotion.threshold.bm = bm_history;
+               }
+
+               lower = cx;
+       }
+
+       /* promotion */
+       for (i = (ACPI_PROCESSOR_MAX_POWER - 1); i > 0; i--) {
+               cx = &pr->power.states[i];
+               if (!cx->valid)
+                       continue;
+
+               if (higher) {
+                       cx->promotion.state  = higher;
+                       cx->promotion.threshold.ticks = cx->latency_ticks;
+                       if (cx->type >= ACPI_STATE_C2)
+                               cx->promotion.threshold.count = 4;
+                       else
+                               cx->promotion.threshold.count = 10;
+                       if (higher->type == ACPI_STATE_C3)
+                               cx->promotion.threshold.bm = bm_history;
+               }
+
+               higher = cx;
+       }
+
+       return_VALUE(0);
+}
+
+
+static int acpi_processor_get_power_info_fadt (struct acpi_processor *pr)
+{
+       int i;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_power_info_fadt");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if (!pr->pblk)
+               return_VALUE(-ENODEV);
+
+       for (i = 0; i < ACPI_PROCESSOR_MAX_POWER; i++)
+               memset(pr->power.states, 0, sizeof(struct acpi_processor_cx));
+
+       /* if info is obtained from pblk/fadt, type equals state */
+       pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1;
+       pr->power.states[ACPI_STATE_C2].type = ACPI_STATE_C2;
+       pr->power.states[ACPI_STATE_C3].type = ACPI_STATE_C3;
+
+       /* the C0 state only exists as a filler in our array,
+        * and all processors need to support C1 */
+       pr->power.states[ACPI_STATE_C0].valid = 1;
+       pr->power.states[ACPI_STATE_C1].valid = 1;
+
+       /* determine C2 and C3 address from pblk */
+       pr->power.states[ACPI_STATE_C2].address = pr->pblk + 4;
+       pr->power.states[ACPI_STATE_C3].address = pr->pblk + 5;
+
+       /* determine latencies from FADT */
+       pr->power.states[ACPI_STATE_C2].latency = acpi_fadt.plvl2_lat;
+       pr->power.states[ACPI_STATE_C3].latency = acpi_fadt.plvl3_lat;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                         "lvl2[0x%08x] lvl3[0x%08x]\n",
+                         pr->power.states[ACPI_STATE_C2].address,
+                         pr->power.states[ACPI_STATE_C3].address));
+
+       return_VALUE(0);
+}
+
+
+static int acpi_processor_get_power_info_cst (struct acpi_processor *pr)
+{
+       acpi_status             status = 0;
+       acpi_integer            count;
+       int                     i;
+       struct acpi_buffer      buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object       *cst;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_power_info_cst");
+
+       if (errata.smp)
+               return_VALUE(-ENODEV);
+
+       if (nocst)
+               return_VALUE(-ENODEV);
+
+       pr->power.count = 0;
+       for (i = 0; i < ACPI_PROCESSOR_MAX_POWER; i++)
+               memset(pr->power.states, 0, sizeof(struct acpi_processor_cx));
+
+       status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No _CST, giving up\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       cst = (union acpi_object *) buffer.pointer;
+
+       /* There must be at least 2 elements */
+       if (!cst || (cst->type != ACPI_TYPE_PACKAGE) || cst->package.count < 2) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "not enough elements in _CST\n"));
+               status = -EFAULT;
+               goto end;
+       }
+
+       count = cst->package.elements[0].integer.value;
+
+       /* Validate number of power states. */
+       if (count < 1 || count != cst->package.count - 1) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "count given by _CST is not valid\n"));
+               status = -EFAULT;
+               goto end;
+       }
+
+       /* We support up to ACPI_PROCESSOR_MAX_POWER. */
+       if (count > ACPI_PROCESSOR_MAX_POWER) {
+               printk(KERN_WARNING "Limiting number of power states to max (%d)\n", ACPI_PROCESSOR_MAX_POWER);
+               printk(KERN_WARNING "Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n");
+               count = ACPI_PROCESSOR_MAX_POWER;
+       }
+
+       /* Tell driver that at least _CST is supported. */
+       pr->flags.has_cst = 1;
+
+       for (i = 1; i <= count; i++) {
+               union acpi_object *element;
+               union acpi_object *obj;
+               struct acpi_power_register *reg;
+               struct acpi_processor_cx cx;
+
+               memset(&cx, 0, sizeof(cx));
+
+               element = (union acpi_object *) &(cst->package.elements[i]);
+               if (element->type != ACPI_TYPE_PACKAGE)
+                       continue;
+
+               if (element->package.count != 4)
+                       continue;
+
+               obj = (union acpi_object *) &(element->package.elements[0]);
+
+               if (obj->type != ACPI_TYPE_BUFFER)
+                       continue;
+
+               reg = (struct acpi_power_register *) obj->buffer.pointer;
+
+               if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO &&
+                       (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE))
+                       continue;
+
+               cx.address = (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) ?
+                       0 : reg->address;
+
+               /* There should be an easy way to extract an integer... */
+               obj = (union acpi_object *) &(element->package.elements[1]);
+               if (obj->type != ACPI_TYPE_INTEGER)
+                       continue;
+
+               cx.type = obj->integer.value;
+
+               if ((cx.type != ACPI_STATE_C1) &&
+                   (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO))
+                       continue;
+
+               if ((cx.type < ACPI_STATE_C1) ||
+                   (cx.type > ACPI_STATE_C3))
+                       continue;
+
+               obj = (union acpi_object *) &(element->package.elements[2]);
+               if (obj->type != ACPI_TYPE_INTEGER)
+                       continue;
+
+               cx.latency = obj->integer.value;
+
+               obj = (union acpi_object *) &(element->package.elements[3]);
+               if (obj->type != ACPI_TYPE_INTEGER)
+                       continue;
+
+               cx.power = obj->integer.value;
+
+               (pr->power.count)++;
+               memcpy(&(pr->power.states[pr->power.count]), &cx, sizeof(cx));
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d power states\n", pr->power.count));
+
+       /* Validate number of power states discovered */
+       if (pr->power.count < 2)
+               status = -ENODEV;
+
+end:
+       acpi_os_free(buffer.pointer);
+
+       return_VALUE(status);
+}
+
+
+static void acpi_processor_power_verify_c2(struct acpi_processor_cx *cx)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_get_power_verify_c2");
+
+       if (!cx->address)
+               return_VOID;
+
+       /*
+        * C2 latency must be less than or equal to 100
+        * microseconds.
+        */
+       else if (cx->latency > ACPI_PROCESSOR_MAX_C2_LATENCY) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                 "latency too large [%d]\n",
+                                 cx->latency));
+               return_VOID;
+       }
+
+       /* We're (currently) only supporting C2 on UP */
+       else if (errata.smp) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                 "C2 not supported in SMP mode\n"));
+               return_VOID;
+       }
+
+       /*
+        * Otherwise we've met all of our C2 requirements.
+        * Normalize the C2 latency to expidite policy
+        */
+       cx->valid = 1;
+       cx->latency_ticks = US_TO_PM_TIMER_TICKS(cx->latency);
+
+       return_VOID;
+}
+
+
+static void acpi_processor_power_verify_c3(
+       struct acpi_processor *pr,
+       struct acpi_processor_cx *cx)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_get_power_verify_c3");
+
+       if (!cx->address)
+               return_VOID;
+
+       /*
+        * C3 latency must be less than or equal to 1000
+        * microseconds.
+        */
+       else if (cx->latency > ACPI_PROCESSOR_MAX_C3_LATENCY) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                 "latency too large [%d]\n",
+                                 cx->latency));
+               return_VOID;
+       }
+
+       /* bus mastering control is necessary */
+       else if (!pr->flags.bm_control) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                 "C3 support requires bus mastering control\n"));
+               return_VOID;
+       }
+
+       /* We're (currently) only supporting C2 on UP */
+       else if (errata.smp) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                 "C3 not supported in SMP mode\n"));
+               return_VOID;
+       }
+
+       /*
+        * PIIX4 Erratum #18: We don't support C3 when Type-F (fast)
+        * DMA transfers are used by any ISA device to avoid livelock.
+        * Note that we could disable Type-F DMA (as recommended by
+        * the erratum), but this is known to disrupt certain ISA
+        * devices thus we take the conservative approach.
+        */
+       else if (errata.piix4.fdma) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "C3 not supported on PIIX4 with Type-F DMA\n"));
+               return_VOID;
+       }
+
+       /*
+        * Otherwise we've met all of our C3 requirements.
+        * Normalize the C3 latency to expidite policy.  Enable
+        * checking of bus mastering status (bm_check) so we can
+        * use this in our C3 policy
+        */
+       cx->valid = 1;
+       cx->latency_ticks = US_TO_PM_TIMER_TICKS(cx->latency);
+       pr->flags.bm_check = 1;
+
+       return_VOID;
+}
+
+
+static int acpi_processor_power_verify(struct acpi_processor *pr)
+{
+       unsigned int i;
+       unsigned int working = 0;
+
+       for (i=1; i < ACPI_PROCESSOR_MAX_POWER; i++) {
+               struct acpi_processor_cx *cx = &pr->power.states[i];
+
+               switch (cx->type) {
+               case ACPI_STATE_C1:
+                       cx->valid = 1;
+                       break;
+
+               case ACPI_STATE_C2:
+                       acpi_processor_power_verify_c2(cx);
+                       break;
+
+               case ACPI_STATE_C3:
+                       acpi_processor_power_verify_c3(pr, cx);
+                       break;
+               }
+
+               if (cx->valid)
+                       working++;
+       }
+
+       return (working);
+}
+
+static int acpi_processor_get_power_info (
+       struct acpi_processor   *pr)
+{
+       unsigned int i;
+       int result;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_power_info");
+
+       /* NOTE: the idle thread may not be running while calling
+        * this function */
+
+       result = acpi_processor_get_power_info_cst(pr);
+       if ((result) || (acpi_processor_power_verify(pr) < 2)) {
+               result = acpi_processor_get_power_info_fadt(pr);
+               if (result)
+                       return_VALUE(result);
+
+               if (acpi_processor_power_verify(pr) < 2)
+                       return_VALUE(-ENODEV);
+       }
+
+       /*
+        * Set Default Policy
+        * ------------------
+        * Now that we know which states are supported, set the default
+        * policy.  Note that this policy can be changed dynamically
+        * (e.g. encourage deeper sleeps to conserve battery life when
+        * not on AC).
+        */
+       result = acpi_processor_set_power_policy(pr);
+       if (result)
+               return_VALUE(result);
+
+       /*
+        * if one state of type C2 or C3 is available, mark this
+        * CPU as being "idle manageable"
+        */
+       for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) {
+               if (pr->power.states[i].valid)
+                       pr->power.count = i;
+               if ((pr->power.states[i].valid) &&
+                   (pr->power.states[i].type >= ACPI_STATE_C2))
+                       pr->flags.power = 1;
+       }
+
+       return_VALUE(0);
+}
+
+int acpi_processor_cst_has_changed (struct acpi_processor *pr)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_cst_has_changed");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if (errata.smp || nocst) {
+               return_VALUE(-ENODEV);
+       }
+
+       if (!pr->flags.power_setup_done)
+               return_VALUE(-ENODEV);
+
+       /* Fall back to the default idle loop */
+       pm_idle = pm_idle_save;
+       synchronize_kernel();
+
+       pr->flags.power = 0;
+       result = acpi_processor_get_power_info(pr);
+       if ((pr->flags.power == 1) && (pr->flags.power_setup_done))
+               pm_idle = acpi_processor_idle;
+
+       return_VALUE(result);
+}
+
+/* proc interface */
+
+static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *)seq->private;
+       unsigned int            i;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_power_seq_show");
+
+       if (!pr)
+               goto end;
+
+       seq_printf(seq, "active state:            C%zd\n"
+                       "max_cstate:              C%d\n"
+                       "bus master activity:     %08x\n",
+                       pr->power.state ? pr->power.state - pr->power.states : 0,
+                       max_cstate,
+                       (unsigned)pr->power.bm_activity);
+
+       seq_puts(seq, "states:\n");
+
+       for (i = 1; i <= pr->power.count; i++) {
+               seq_printf(seq, "   %cC%d:                  ",
+                       (&pr->power.states[i] == pr->power.state?'*':' '), i);
+
+               if (!pr->power.states[i].valid) {
+                       seq_puts(seq, "<not supported>\n");
+                       continue;
+               }
+
+               switch (pr->power.states[i].type) {
+               case ACPI_STATE_C1:
+                       seq_printf(seq, "type[C1] ");
+                       break;
+               case ACPI_STATE_C2:
+                       seq_printf(seq, "type[C2] ");
+                       break;
+               case ACPI_STATE_C3:
+                       seq_printf(seq, "type[C3] ");
+                       break;
+               default:
+                       seq_printf(seq, "type[--] ");
+                       break;
+               }
+
+               if (pr->power.states[i].promotion.state)
+                       seq_printf(seq, "promotion[C%zd] ",
+                               (pr->power.states[i].promotion.state -
+                                pr->power.states));
+               else
+                       seq_puts(seq, "promotion[--] ");
+
+               if (pr->power.states[i].demotion.state)
+                       seq_printf(seq, "demotion[C%zd] ",
+                               (pr->power.states[i].demotion.state -
+                                pr->power.states));
+               else
+                       seq_puts(seq, "demotion[--] ");
+
+               seq_printf(seq, "latency[%03d] usage[%08d]\n",
+                       pr->power.states[i].latency,
+                       pr->power.states[i].usage);
+       }
+
+end:
+       return_VALUE(0);
+}
+
+static int acpi_processor_power_open_fs(struct inode *inode, struct file *file)
+{
+       return single_open(file, acpi_processor_power_seq_show,
+                                               PDE(inode)->data);
+}
+
+static struct file_operations acpi_processor_power_fops = {
+       .open           = acpi_processor_power_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+
+int acpi_processor_power_init(struct acpi_processor *pr, struct acpi_device *device)
+{
+       acpi_status             status = 0;
+       static int              first_run = 0;
+       struct proc_dir_entry   *entry = NULL;
+       unsigned int i;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_power_init");
+
+       if (!first_run) {
+               dmi_check_system(processor_power_dmi_table);
+               if (max_cstate < ACPI_C_STATES_MAX)
+                       printk(KERN_NOTICE "ACPI: processor limited to max C-state %d\n", max_cstate);
+               first_run++;
+       }
+
+       if (!errata.smp && (pr->id == 0) && acpi_fadt.cst_cnt && !nocst) {
+               status = acpi_os_write_port(acpi_fadt.smi_cmd, acpi_fadt.cst_cnt, 8);
+               if (ACPI_FAILURE(status)) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                         "Notifying BIOS of _CST ability failed\n"));
+               }
+       }
+
+       acpi_processor_get_power_info(pr);
+
+       /*
+        * Install the idle handler if processor power management is supported.
+        * Note that we use previously set idle handler will be used on
+        * platforms that only support C1.
+        */
+       if ((pr->flags.power) && (!boot_option_idle_override)) {
+               printk(KERN_INFO PREFIX "CPU%d (power states:", pr->id);
+               for (i = 1; i <= pr->power.count; i++)
+                       if (pr->power.states[i].valid)
+                               printk(" C%d[C%d]", i, pr->power.states[i].type);
+               printk(")\n");
+
+               if (pr->id == 0) {
+                       pm_idle_save = pm_idle;
+                       pm_idle = acpi_processor_idle;
+               }
+       }
+
+       /* 'power' [R] */
+       entry = create_proc_entry(ACPI_PROCESSOR_FILE_POWER,
+               S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_PROCESSOR_FILE_POWER));
+       else {
+               entry->proc_fops = &acpi_processor_power_fops;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       pr->flags.power_setup_done = 1;
+
+       return_VALUE(0);
+}
+
+int acpi_processor_power_exit(struct acpi_processor *pr, struct acpi_device *device)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_power_exit");
+
+       pr->flags.power_setup_done = 0;
+
+       if (acpi_device_dir(device))
+               remove_proc_entry(ACPI_PROCESSOR_FILE_POWER,acpi_device_dir(device));
+
+       /* Unregister the idle handler when processor #0 is removed. */
+       if (pr->id == 0) {
+               pm_idle = pm_idle_save;
+
+               /*
+                * We are about to unload the current idle thread pm callback
+                * (pm_idle), Wait for all processors to update cached/local
+                * copies of pm_idle before proceeding.
+                */
+               cpu_idle_wait();
+       }
+
+       return_VALUE(0);
+}
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
new file mode 100644 (file)
index 0000000..a9a1a8f
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ * processor_perflib.c - ACPI Processor P-States Library ($Revision: 71 $)
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
+ *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ *                     - Added processor hotplug support
+ *
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+
+#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/uaccess.h>
+#endif
+
+#include <acpi/acpi_bus.h>
+#include <acpi/processor.h>
+
+
+#define ACPI_PROCESSOR_COMPONENT       0x01000000
+#define ACPI_PROCESSOR_CLASS           "processor"
+#define ACPI_PROCESSOR_DRIVER_NAME     "ACPI Processor Driver"
+#define ACPI_PROCESSOR_FILE_PERFORMANCE        "performance"
+#define _COMPONENT             ACPI_PROCESSOR_COMPONENT
+ACPI_MODULE_NAME               ("acpi_processor")
+
+
+static DECLARE_MUTEX(performance_sem);
+
+/*
+ * _PPC support is implemented as a CPUfreq policy notifier:
+ * This means each time a CPUfreq driver registered also with
+ * the ACPI core is asked to change the speed policy, the maximum
+ * value is adjusted so that it is within the platform limit.
+ *
+ * Also, when a new platform limit value is detected, the CPUfreq
+ * policy is adjusted accordingly.
+ */
+
+#define PPC_REGISTERED   1
+#define PPC_IN_USE       2
+
+static int acpi_processor_ppc_status = 0;
+
+static int acpi_processor_ppc_notifier(struct notifier_block *nb,
+       unsigned long event,
+       void *data)
+{
+       struct cpufreq_policy *policy = data;
+       struct acpi_processor *pr;
+       unsigned int ppc = 0;
+
+       down(&performance_sem);
+
+       if (event != CPUFREQ_INCOMPATIBLE)
+               goto out;
+
+       pr = processors[policy->cpu];
+       if (!pr || !pr->performance)
+               goto out;
+
+       ppc = (unsigned int) pr->performance_platform_limit;
+       if (!ppc)
+               goto out;
+
+       if (ppc > pr->performance->state_count)
+               goto out;
+
+       cpufreq_verify_within_limits(policy, 0,
+               pr->performance->states[ppc].core_frequency * 1000);
+
+ out:
+       up(&performance_sem);
+
+       return 0;
+}
+
+
+static struct notifier_block acpi_ppc_notifier_block = {
+       .notifier_call = acpi_processor_ppc_notifier,
+};
+
+
+static int
+acpi_processor_get_platform_limit (
+       struct acpi_processor*  pr)
+{
+       acpi_status             status = 0;
+       unsigned long           ppc = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_platform_limit");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       /*
+        * _PPC indicates the maximum state currently supported by the platform
+        * (e.g. 0 = states 0..n; 1 = states 1..n; etc.
+        */
+       status = acpi_evaluate_integer(pr->handle, "_PPC", NULL, &ppc);
+
+       if (status != AE_NOT_FOUND)
+               acpi_processor_ppc_status |= PPC_IN_USE;
+
+       if(ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PPC\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       pr->performance_platform_limit = (int) ppc;
+
+       return_VALUE(0);
+}
+
+
+int acpi_processor_ppc_has_changed(
+       struct acpi_processor *pr)
+{
+       int ret = acpi_processor_get_platform_limit(pr);
+       if (ret < 0)
+               return (ret);
+       else
+               return cpufreq_update_policy(pr->id);
+}
+
+
+void acpi_processor_ppc_init(void) {
+       if (!cpufreq_register_notifier(&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER))
+               acpi_processor_ppc_status |= PPC_REGISTERED;
+       else
+               printk(KERN_DEBUG "Warning: Processor Platform Limit not supported.\n");
+}
+
+
+void acpi_processor_ppc_exit(void) {
+       if (acpi_processor_ppc_status & PPC_REGISTERED)
+               cpufreq_unregister_notifier(&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER);
+
+       acpi_processor_ppc_status &= ~PPC_REGISTERED;
+}
+
+/*
+ * when registering a cpufreq driver with this ACPI processor driver, the
+ * _PCT and _PSS structures are read out and written into struct
+ * acpi_processor_performance.
+ */
+static int acpi_processor_set_pdc (struct acpi_processor *pr)
+{
+       acpi_status             status = AE_OK;
+       u32                     arg0_buf[3];
+       union acpi_object       arg0 = {ACPI_TYPE_BUFFER};
+       struct acpi_object_list no_object = {1, &arg0};
+       struct acpi_object_list *pdc;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_set_pdc");
+
+       arg0.buffer.length = 12;
+       arg0.buffer.pointer = (u8 *) arg0_buf;
+       arg0_buf[0] = ACPI_PDC_REVISION_ID;
+       arg0_buf[1] = 0;
+       arg0_buf[2] = 0;
+
+       pdc = (pr->performance->pdc) ? pr->performance->pdc : &no_object;
+
+       status = acpi_evaluate_object(pr->handle, "_PDC", pdc, NULL);
+
+       if ((ACPI_FAILURE(status)) && (pr->performance->pdc))
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Error evaluating _PDC, using legacy perf. control...\n"));
+
+       return_VALUE(status);
+}
+
+
+static int
+acpi_processor_get_performance_control (
+       struct acpi_processor *pr)
+{
+       int                     result = 0;
+       acpi_status             status = 0;
+       struct acpi_buffer      buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object       *pct = NULL;
+       union acpi_object       obj = {0};
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_performance_control");
+
+       status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
+       if(ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PCT\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       pct = (union acpi_object *) buffer.pointer;
+       if (!pct || (pct->type != ACPI_TYPE_PACKAGE)
+               || (pct->package.count != 2)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PCT data\n"));
+               result = -EFAULT;
+               goto end;
+       }
+
+       /*
+        * control_register
+        */
+
+       obj = pct->package.elements[0];
+
+       if ((obj.type != ACPI_TYPE_BUFFER)
+               || (obj.buffer.length < sizeof(struct acpi_pct_register))
+               || (obj.buffer.pointer == NULL)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Invalid _PCT data (control_register)\n"));
+               result = -EFAULT;
+               goto end;
+       }
+       memcpy(&pr->performance->control_register, obj.buffer.pointer, sizeof(struct acpi_pct_register));
+
+
+       /*
+        * status_register
+        */
+
+       obj = pct->package.elements[1];
+
+       if ((obj.type != ACPI_TYPE_BUFFER)
+               || (obj.buffer.length < sizeof(struct acpi_pct_register))
+               || (obj.buffer.pointer == NULL)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Invalid _PCT data (status_register)\n"));
+               result = -EFAULT;
+               goto end;
+       }
+
+       memcpy(&pr->performance->status_register, obj.buffer.pointer, sizeof(struct acpi_pct_register));
+
+end:
+       acpi_os_free(buffer.pointer);
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_processor_get_performance_states (
+       struct acpi_processor   *pr)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       struct acpi_buffer      buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       struct acpi_buffer      format = {sizeof("NNNNNN"), "NNNNNN"};
+       struct acpi_buffer      state = {0, NULL};
+       union acpi_object       *pss = NULL;
+       int                     i;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_performance_states");
+
+       status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
+       if(ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PSS\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       pss = (union acpi_object *) buffer.pointer;
+       if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data\n"));
+               result = -EFAULT;
+               goto end;
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n",
+               pss->package.count));
+
+       pr->performance->state_count = pss->package.count;
+       pr->performance->states = kmalloc(sizeof(struct acpi_processor_px) * pss->package.count, GFP_KERNEL);
+       if (!pr->performance->states) {
+               result = -ENOMEM;
+               goto end;
+       }
+
+       for (i = 0; i < pr->performance->state_count; i++) {
+
+               struct acpi_processor_px *px = &(pr->performance->states[i]);
+
+               state.length = sizeof(struct acpi_processor_px);
+               state.pointer = px;
+
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i));
+
+               status = acpi_extract_package(&(pss->package.elements[i]),
+                       &format, &state);
+               if (ACPI_FAILURE(status)) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data\n"));
+                       result = -EFAULT;
+                       kfree(pr->performance->states);
+                       goto end;
+               }
+
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n",
+                       i,
+                       (u32) px->core_frequency,
+                       (u32) px->power,
+                       (u32) px->transition_latency,
+                       (u32) px->bus_master_latency,
+                       (u32) px->control,
+                       (u32) px->status));
+
+               if (!px->core_frequency) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data: freq is zero\n"));
+                       result = -EFAULT;
+                       kfree(pr->performance->states);
+                       goto end;
+               }
+       }
+
+end:
+       acpi_os_free(buffer.pointer);
+
+       return_VALUE(result);
+}
+
+
+static int
+acpi_processor_get_performance_info (
+       struct acpi_processor   *pr)
+{
+       int                     result = 0;
+       acpi_status             status = AE_OK;
+       acpi_handle             handle = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_performance_info");
+
+       if (!pr || !pr->performance || !pr->handle)
+               return_VALUE(-EINVAL);
+
+       acpi_processor_set_pdc(pr);
+
+       status = acpi_get_handle(pr->handle, "_PCT", &handle);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "ACPI-based processor performance control unavailable\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       result = acpi_processor_get_performance_control(pr);
+       if (result)
+               return_VALUE(result);
+
+       result = acpi_processor_get_performance_states(pr);
+       if (result)
+               return_VALUE(result);
+
+       result = acpi_processor_get_platform_limit(pr);
+       if (result)
+               return_VALUE(result);
+
+       return_VALUE(0);
+}
+
+
+int acpi_processor_notify_smm(struct module *calling_module) {
+       acpi_status             status;
+       static int              is_done = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_notify_smm");
+
+       if (!(acpi_processor_ppc_status & PPC_REGISTERED))
+               return_VALUE(-EBUSY);
+
+       if (!try_module_get(calling_module))
+               return_VALUE(-EINVAL);
+
+       /* is_done is set to negative if an error occured,
+        * and to postitive if _no_ error occured, but SMM
+        * was already notified. This avoids double notification
+        * which might lead to unexpected results...
+        */
+       if (is_done > 0) {
+               module_put(calling_module);
+               return_VALUE(0);
+       }
+       else if (is_done < 0) {
+               module_put(calling_module);
+               return_VALUE(is_done);
+       }
+
+       is_done = -EIO;
+
+       /* Can't write pstate_cnt to smi_cmd if either value is zero */
+       if ((!acpi_fadt.smi_cmd) ||
+           (!acpi_fadt.pstate_cnt)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "No SMI port or pstate_cnt\n"));
+               module_put(calling_module);
+               return_VALUE(0);
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Writing pstate_cnt [0x%x] to smi_cmd [0x%x]\n", acpi_fadt.pstate_cnt, acpi_fadt.smi_cmd));
+
+       /* FADT v1 doesn't support pstate_cnt, many BIOS vendors use
+        * it anyway, so we need to support it... */
+       if (acpi_fadt_is_v1) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Using v1.0 FADT reserved value for pstate_cnt\n"));
+       }
+
+       status = acpi_os_write_port (acpi_fadt.smi_cmd,
+                                    (u32) acpi_fadt.pstate_cnt, 8);
+       if (ACPI_FAILURE (status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                 "Failed to write pstate_cnt [0x%x] to "
+                                 "smi_cmd [0x%x]\n", acpi_fadt.pstate_cnt, acpi_fadt.smi_cmd));
+               module_put(calling_module);
+               return_VALUE(status);
+       }
+
+       /* Success. If there's no _PPC, we need to fear nothing, so
+        * we can allow the cpufreq driver to be rmmod'ed. */
+       is_done = 1;
+
+       if (!(acpi_processor_ppc_status & PPC_IN_USE))
+               module_put(calling_module);
+
+       return_VALUE(0);
+}
+EXPORT_SYMBOL(acpi_processor_notify_smm);
+
+
+#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF
+/* /proc/acpi/processor/../performance interface (DEPRECATED) */
+
+static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file);
+static struct file_operations acpi_processor_perf_fops = {
+       .open           = acpi_processor_perf_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *)seq->private;
+       int                     i;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_perf_seq_show");
+
+       if (!pr)
+               goto end;
+
+       if (!pr->performance) {
+               seq_puts(seq, "<not supported>\n");
+               goto end;
+       }
+
+       seq_printf(seq, "state count:             %d\n"
+                       "active state:            P%d\n",
+                       pr->performance->state_count,
+                       pr->performance->state);
+
+       seq_puts(seq, "states:\n");
+       for (i = 0; i < pr->performance->state_count; i++)
+               seq_printf(seq, "   %cP%d:                  %d MHz, %d mW, %d uS\n",
+                       (i == pr->performance->state?'*':' '), i,
+                       (u32) pr->performance->states[i].core_frequency,
+                       (u32) pr->performance->states[i].power,
+                       (u32) pr->performance->states[i].transition_latency);
+
+end:
+       return_VALUE(0);
+}
+
+static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file)
+{
+       return single_open(file, acpi_processor_perf_seq_show,
+                                               PDE(inode)->data);
+}
+
+static ssize_t
+acpi_processor_write_performance (
+        struct file            *file,
+        const char             __user *buffer,
+        size_t                 count,
+        loff_t                 *data)
+{
+       int                     result = 0;
+       struct seq_file         *m = (struct seq_file *) file->private_data;
+       struct acpi_processor   *pr = (struct acpi_processor *) m->private;
+       struct acpi_processor_performance *perf;
+       char                    state_string[12] = {'\0'};
+       unsigned int            new_state = 0;
+       struct cpufreq_policy   policy;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_write_performance");
+
+       if (!pr || (count > sizeof(state_string) - 1))
+               return_VALUE(-EINVAL);
+
+       perf = pr->performance;
+       if (!perf)
+               return_VALUE(-EINVAL);
+
+       if (copy_from_user(state_string, buffer, count))
+               return_VALUE(-EFAULT);
+
+       state_string[count] = '\0';
+       new_state = simple_strtoul(state_string, NULL, 0);
+
+       if (new_state >= perf->state_count)
+               return_VALUE(-EINVAL);
+
+       cpufreq_get_policy(&policy, pr->id);
+
+       policy.cpu = pr->id;
+       policy.min = perf->states[new_state].core_frequency * 1000;
+       policy.max = perf->states[new_state].core_frequency * 1000;
+
+       result = cpufreq_set_policy(&policy);
+       if (result)
+               return_VALUE(result);
+
+       return_VALUE(count);
+}
+
+static void
+acpi_cpufreq_add_file (
+       struct acpi_processor *pr)
+{
+       struct proc_dir_entry   *entry = NULL;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile");
+
+       if (acpi_bus_get_device(pr->handle, &device))
+               return_VOID;
+
+       /* add file 'performance' [R/W] */
+       entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
+                 S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create '%s' fs entry\n",
+                       ACPI_PROCESSOR_FILE_PERFORMANCE));
+       else {
+               entry->proc_fops = &acpi_processor_perf_fops;
+               entry->proc_fops->write = acpi_processor_write_performance;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+       return_VOID;
+}
+
+static void
+acpi_cpufreq_remove_file (
+       struct acpi_processor *pr)
+{
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile");
+
+       if (acpi_bus_get_device(pr->handle, &device))
+               return_VOID;
+
+       /* remove file 'performance' */
+       remove_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE,
+                 acpi_device_dir(device));
+
+       return_VOID;
+}
+
+#else
+static void acpi_cpufreq_add_file (struct acpi_processor *pr) { return; }
+static void acpi_cpufreq_remove_file (struct acpi_processor *pr) { return; }
+#endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */
+
+
+int
+acpi_processor_register_performance (
+       struct acpi_processor_performance * performance,
+       unsigned int cpu)
+{
+       struct acpi_processor *pr;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_register_performance");
+
+       if (!(acpi_processor_ppc_status & PPC_REGISTERED))
+               return_VALUE(-EINVAL);
+
+       down(&performance_sem);
+
+       pr = processors[cpu];
+       if (!pr) {
+               up(&performance_sem);
+               return_VALUE(-ENODEV);
+       }
+
+       if (pr->performance) {
+               up(&performance_sem);
+               return_VALUE(-EBUSY);
+       }
+
+       pr->performance = performance;
+
+       if (acpi_processor_get_performance_info(pr)) {
+               pr->performance = NULL;
+               up(&performance_sem);
+               return_VALUE(-EIO);
+       }
+
+       acpi_cpufreq_add_file(pr);
+
+       up(&performance_sem);
+       return_VALUE(0);
+}
+EXPORT_SYMBOL(acpi_processor_register_performance);
+
+
+void
+acpi_processor_unregister_performance (
+       struct acpi_processor_performance * performance,
+       unsigned int cpu)
+{
+       struct acpi_processor *pr;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_unregister_performance");
+
+       down(&performance_sem);
+
+       pr = processors[cpu];
+       if (!pr) {
+               up(&performance_sem);
+               return_VOID;
+       }
+
+       kfree(pr->performance->states);
+       pr->performance = NULL;
+
+       acpi_cpufreq_remove_file(pr);
+
+       up(&performance_sem);
+
+       return_VOID;
+}
+EXPORT_SYMBOL(acpi_processor_unregister_performance);
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c
new file mode 100644 (file)
index 0000000..8711236
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * processor_thermal.c - Passive cooling submodule of the ACPI processor driver
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
+ *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ *                     - Added processor hotplug support
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/uaccess.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/processor.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_PROCESSOR_COMPONENT        0x01000000
+#define ACPI_PROCESSOR_CLASS            "processor"
+#define ACPI_PROCESSOR_DRIVER_NAME      "ACPI Processor Driver"
+#define _COMPONENT              ACPI_PROCESSOR_COMPONENT
+ACPI_MODULE_NAME                ("acpi_processor")
+
+
+/* --------------------------------------------------------------------------
+                                 Limit Interface
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_processor_apply_limit (
+       struct acpi_processor*  pr)
+{
+       int                     result = 0;
+       u16                     px = 0;
+       u16                     tx = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_apply_limit");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if (!pr->flags.limit)
+               return_VALUE(-ENODEV);
+
+       if (pr->flags.throttling) {
+               if (pr->limit.user.tx > tx)
+                       tx = pr->limit.user.tx;
+               if (pr->limit.thermal.tx > tx)
+                       tx = pr->limit.thermal.tx;
+
+               result = acpi_processor_set_throttling(pr, tx);
+               if (result)
+                       goto end;
+       }
+
+       pr->limit.state.px = px;
+       pr->limit.state.tx = tx;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d] limit set to (P%d:T%d)\n",
+               pr->id,
+               pr->limit.state.px,
+               pr->limit.state.tx));
+
+end:
+       if (result)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to set limit\n"));
+
+       return_VALUE(result);
+}
+
+
+#ifdef CONFIG_CPU_FREQ
+
+/* If a passive cooling situation is detected, primarily CPUfreq is used, as it
+ * offers (in most cases) voltage scaling in addition to frequency scaling, and
+ * thus a cubic (instead of linear) reduction of energy. Also, we allow for
+ * _any_ cpufreq driver and not only the acpi-cpufreq driver.
+ */
+
+static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS];
+static unsigned int acpi_thermal_cpufreq_is_init = 0;
+
+
+static int cpu_has_cpufreq(unsigned int cpu)
+{
+       struct cpufreq_policy policy;
+       if (!acpi_thermal_cpufreq_is_init)
+               return -ENODEV;
+       if (!cpufreq_get_policy(&policy, cpu))
+               return -ENODEV;
+       return 0;
+}
+
+
+static int acpi_thermal_cpufreq_increase(unsigned int cpu)
+{
+       if (!cpu_has_cpufreq(cpu))
+               return -ENODEV;
+
+       if (cpufreq_thermal_reduction_pctg[cpu] < 60) {
+               cpufreq_thermal_reduction_pctg[cpu] += 20;
+               cpufreq_update_policy(cpu);
+               return 0;
+       }
+
+       return -ERANGE;
+}
+
+
+static int acpi_thermal_cpufreq_decrease(unsigned int cpu)
+{
+       if (!cpu_has_cpufreq(cpu))
+               return -ENODEV;
+
+       if (cpufreq_thermal_reduction_pctg[cpu] >= 20) {
+               cpufreq_thermal_reduction_pctg[cpu] -= 20;
+               cpufreq_update_policy(cpu);
+               return 0;
+       }
+
+       return -ERANGE;
+}
+
+
+static int acpi_thermal_cpufreq_notifier(
+       struct notifier_block *nb,
+       unsigned long event,
+       void *data)
+{
+       struct cpufreq_policy *policy = data;
+       unsigned long max_freq = 0;
+
+       if (event != CPUFREQ_ADJUST)
+               goto out;
+
+       max_freq = (policy->cpuinfo.max_freq * (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100;
+
+       cpufreq_verify_within_limits(policy, 0, max_freq);
+
+ out:
+       return 0;
+}
+
+
+static struct notifier_block acpi_thermal_cpufreq_notifier_block = {
+       .notifier_call = acpi_thermal_cpufreq_notifier,
+};
+
+
+void acpi_thermal_cpufreq_init(void) {
+       int i;
+
+       for (i=0; i<NR_CPUS; i++)
+               cpufreq_thermal_reduction_pctg[i] = 0;
+
+       i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER);
+       if (!i)
+               acpi_thermal_cpufreq_is_init = 1;
+}
+
+void acpi_thermal_cpufreq_exit(void) {
+       if (acpi_thermal_cpufreq_is_init)
+               cpufreq_unregister_notifier(&acpi_thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER);
+
+       acpi_thermal_cpufreq_is_init = 0;
+}
+
+#else /* ! CONFIG_CPU_FREQ */
+
+static int acpi_thermal_cpufreq_increase(unsigned int cpu) { return -ENODEV; }
+static int acpi_thermal_cpufreq_decrease(unsigned int cpu) { return -ENODEV; }
+
+
+#endif
+
+
+int
+acpi_processor_set_thermal_limit (
+       acpi_handle             handle,
+       int                     type)
+{
+       int                     result = 0;
+       struct acpi_processor   *pr = NULL;
+       struct acpi_device      *device = NULL;
+       int                     tx = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_set_thermal_limit");
+
+       if ((type < ACPI_PROCESSOR_LIMIT_NONE)
+               || (type > ACPI_PROCESSOR_LIMIT_DECREMENT))
+               return_VALUE(-EINVAL);
+
+       result = acpi_bus_get_device(handle, &device);
+       if (result)
+               return_VALUE(result);
+
+       pr = (struct acpi_processor *) acpi_driver_data(device);
+       if (!pr)
+               return_VALUE(-ENODEV);
+
+       /* Thermal limits are always relative to the current Px/Tx state. */
+       if (pr->flags.throttling)
+               pr->limit.thermal.tx = pr->throttling.state;
+
+       /*
+        * Our default policy is to only use throttling at the lowest
+        * performance state.
+        */
+
+       tx = pr->limit.thermal.tx;
+
+       switch (type) {
+
+       case ACPI_PROCESSOR_LIMIT_NONE:
+               do {
+                       result = acpi_thermal_cpufreq_decrease(pr->id);
+               } while (!result);
+               tx = 0;
+               break;
+
+       case ACPI_PROCESSOR_LIMIT_INCREMENT:
+               /* if going up: P-states first, T-states later */
+
+               result = acpi_thermal_cpufreq_increase(pr->id);
+               if (!result)
+                       goto end;
+               else if (result == -ERANGE)
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                       "At maximum performance state\n"));
+
+               if (pr->flags.throttling) {
+                       if (tx == (pr->throttling.state_count - 1))
+                               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                       "At maximum throttling state\n"));
+                       else
+                               tx++;
+               }
+               break;
+
+       case ACPI_PROCESSOR_LIMIT_DECREMENT:
+               /* if going down: T-states first, P-states later */
+
+               if (pr->flags.throttling) {
+                       if (tx == 0)
+                               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                       "At minimum throttling state\n"));
+                       else {
+                               tx--;
+                               goto end;
+                       }
+               }
+
+               result = acpi_thermal_cpufreq_decrease(pr->id);
+               if (result == -ERANGE)
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                                       "At minimum performance state\n"));
+
+               break;
+       }
+
+end:
+       if (pr->flags.throttling) {
+               pr->limit.thermal.px = 0;
+               pr->limit.thermal.tx = tx;
+
+               result = acpi_processor_apply_limit(pr);
+               if (result)
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                         "Unable to set thermal limit\n"));
+
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Thermal limit now (P%d:T%d)\n",
+                                 pr->limit.thermal.px,
+                                 pr->limit.thermal.tx));
+       } else
+               result = 0;
+
+       return_VALUE(result);
+}
+
+
+int
+acpi_processor_get_limit_info (
+       struct acpi_processor   *pr)
+{
+       ACPI_FUNCTION_TRACE("acpi_processor_get_limit_info");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if (pr->flags.throttling)
+               pr->flags.limit = 1;
+
+       return_VALUE(0);
+}
+
+
+/* /proc interface */
+
+static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *)seq->private;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_limit_seq_show");
+
+       if (!pr)
+               goto end;
+
+       if (!pr->flags.limit) {
+               seq_puts(seq, "<not supported>\n");
+               goto end;
+       }
+
+       seq_printf(seq, "active limit:            P%d:T%d\n"
+                       "user limit:              P%d:T%d\n"
+                       "thermal limit:           P%d:T%d\n",
+                       pr->limit.state.px, pr->limit.state.tx,
+                       pr->limit.user.px, pr->limit.user.tx,
+                       pr->limit.thermal.px, pr->limit.thermal.tx);
+
+end:
+       return_VALUE(0);
+}
+
+int acpi_processor_limit_open_fs(struct inode *inode, struct file *file)
+{
+       return single_open(file, acpi_processor_limit_seq_show,
+                                               PDE(inode)->data);
+}
+
+ssize_t acpi_processor_write_limit (
+       struct file             *file,
+       const char              __user *buffer,
+       size_t                  count,
+       loff_t                  *data)
+{
+       int                     result = 0;
+        struct seq_file        *m = (struct seq_file *)file->private_data;
+       struct acpi_processor   *pr = (struct acpi_processor *)m->private;
+       char                    limit_string[25] = {'\0'};
+       int                     px = 0;
+       int                     tx = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_write_limit");
+
+       if (!pr || (count > sizeof(limit_string) - 1)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument\n"));
+               return_VALUE(-EINVAL);
+       }
+
+       if (copy_from_user(limit_string, buffer, count)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data\n"));
+               return_VALUE(-EFAULT);
+       }
+
+       limit_string[count] = '\0';
+
+       if (sscanf(limit_string, "%d:%d", &px, &tx) != 2) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format\n"));
+               return_VALUE(-EINVAL);
+       }
+
+       if (pr->flags.throttling) {
+               if ((tx < 0) || (tx > (pr->throttling.state_count - 1))) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid tx\n"));
+                       return_VALUE(-EINVAL);
+               }
+               pr->limit.user.tx = tx;
+       }
+
+       result = acpi_processor_apply_limit(pr);
+
+       return_VALUE(count);
+}
+
+
+struct file_operations acpi_processor_limit_fops = {
+       .open           = acpi_processor_limit_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
new file mode 100644 (file)
index 0000000..db0b31d
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * processor_throttling.c - Throttling submodule of the ACPI processor driver
+ *
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
+ *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
+ *                     - Added processor hotplug support
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/processor.h>
+
+#define ACPI_PROCESSOR_COMPONENT        0x01000000
+#define ACPI_PROCESSOR_CLASS            "processor"
+#define ACPI_PROCESSOR_DRIVER_NAME      "ACPI Processor Driver"
+#define _COMPONENT              ACPI_PROCESSOR_COMPONENT
+ACPI_MODULE_NAME                ("acpi_processor")
+
+
+/* --------------------------------------------------------------------------
+                              Throttling Control
+   -------------------------------------------------------------------------- */
+
+static int
+acpi_processor_get_throttling (
+       struct acpi_processor   *pr)
+{
+       int                     state = 0;
+       u32                     value = 0;
+       u32                     duty_mask = 0;
+       u32                     duty_value = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_throttling");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if (!pr->flags.throttling)
+               return_VALUE(-ENODEV);
+
+       pr->throttling.state = 0;
+
+       duty_mask = pr->throttling.state_count - 1;
+
+       duty_mask <<= pr->throttling.duty_offset;
+
+       local_irq_disable();
+
+       value = inl(pr->throttling.address);
+
+       /*
+        * Compute the current throttling state when throttling is enabled
+        * (bit 4 is on).
+        */
+       if (value & 0x10) {
+               duty_value = value & duty_mask;
+               duty_value >>= pr->throttling.duty_offset;
+
+               if (duty_value)
+                       state = pr->throttling.state_count-duty_value;
+       }
+
+       pr->throttling.state = state;
+
+       local_irq_enable();
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+               "Throttling state is T%d (%d%% throttling applied)\n",
+               state, pr->throttling.states[state].performance));
+
+       return_VALUE(0);
+}
+
+
+int acpi_processor_set_throttling (
+       struct acpi_processor   *pr,
+       int                     state)
+{
+       u32                     value = 0;
+       u32                     duty_mask = 0;
+       u32                     duty_value = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_set_throttling");
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       if ((state < 0) || (state > (pr->throttling.state_count - 1)))
+               return_VALUE(-EINVAL);
+
+       if (!pr->flags.throttling)
+               return_VALUE(-ENODEV);
+
+       if (state == pr->throttling.state)
+               return_VALUE(0);
+
+       /*
+        * Calculate the duty_value and duty_mask.
+        */
+       if (state) {
+               duty_value = pr->throttling.state_count - state;
+
+               duty_value <<= pr->throttling.duty_offset;
+
+               /* Used to clear all duty_value bits */
+               duty_mask = pr->throttling.state_count - 1;
+
+               duty_mask <<= acpi_fadt.duty_offset;
+               duty_mask = ~duty_mask;
+       }
+
+       local_irq_disable();
+
+       /*
+        * Disable throttling by writing a 0 to bit 4.  Note that we must
+        * turn it off before you can change the duty_value.
+        */
+       value = inl(pr->throttling.address);
+       if (value & 0x10) {
+               value &= 0xFFFFFFEF;
+               outl(value, pr->throttling.address);
+       }
+
+       /*
+        * Write the new duty_value and then enable throttling.  Note
+        * that a state value of 0 leaves throttling disabled.
+        */
+       if (state) {
+               value &= duty_mask;
+               value |= duty_value;
+               outl(value, pr->throttling.address);
+
+               value |= 0x00000010;
+               outl(value, pr->throttling.address);
+       }
+
+       pr->throttling.state = state;
+
+       local_irq_enable();
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+               "Throttling state set to T%d (%d%%)\n", state,
+               (pr->throttling.states[state].performance?pr->throttling.states[state].performance/10:0)));
+
+       return_VALUE(0);
+}
+
+
+int
+acpi_processor_get_throttling_info (
+       struct acpi_processor   *pr)
+{
+       int                     result = 0;
+       int                     step = 0;
+       int                     i = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_get_throttling_info");
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+               "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n",
+               pr->throttling.address,
+               pr->throttling.duty_offset,
+               pr->throttling.duty_width));
+
+       if (!pr)
+               return_VALUE(-EINVAL);
+
+       /* TBD: Support ACPI 2.0 objects */
+
+       if (!pr->throttling.address) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling register\n"));
+               return_VALUE(0);
+       }
+       else if (!pr->throttling.duty_width) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling states\n"));
+               return_VALUE(0);
+       }
+       /* TBD: Support duty_cycle values that span bit 4. */
+       else if ((pr->throttling.duty_offset
+               + pr->throttling.duty_width) > 4) {
+               ACPI_DEBUG_PRINT((ACPI_DB_WARN, "duty_cycle spans bit 4\n"));
+               return_VALUE(0);
+       }
+
+       /*
+        * PIIX4 Errata: We don't support throttling on the original PIIX4.
+        * This shouldn't be an issue as few (if any) mobile systems ever
+        * used this part.
+        */
+       if (errata.piix4.throttle) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Throttling not supported on PIIX4 A- or B-step\n"));
+               return_VALUE(0);
+       }
+
+       pr->throttling.state_count = 1 << acpi_fadt.duty_width;
+
+       /*
+        * Compute state values. Note that throttling displays a linear power/
+        * performance relationship (at 50% performance the CPU will consume
+        * 50% power).  Values are in 1/10th of a percent to preserve accuracy.
+        */
+
+       step = (1000 / pr->throttling.state_count);
+
+       for (i=0; i<pr->throttling.state_count; i++) {
+               pr->throttling.states[i].performance = step * i;
+               pr->throttling.states[i].power = step * i;
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n",
+               pr->throttling.state_count));
+
+       pr->flags.throttling = 1;
+
+       /*
+        * Disable throttling (if enabled).  We'll let subsequent policy (e.g.
+        * thermal) decide to lower performance if it so chooses, but for now
+        * we'll crank up the speed.
+        */
+
+       result = acpi_processor_get_throttling(pr);
+       if (result)
+               goto end;
+
+       if (pr->throttling.state) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Disabling throttling (was T%d)\n",
+                       pr->throttling.state));
+               result = acpi_processor_set_throttling(pr, 0);
+               if (result)
+                       goto end;
+       }
+
+end:
+       if (result)
+               pr->flags.throttling = 0;
+
+       return_VALUE(result);
+}
+
+
+/* proc interface */
+
+static int acpi_processor_throttling_seq_show(struct seq_file *seq, void *offset)
+{
+       struct acpi_processor   *pr = (struct acpi_processor *)seq->private;
+       int                     i = 0;
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_processor_throttling_seq_show");
+
+       if (!pr)
+               goto end;
+
+       if (!(pr->throttling.state_count > 0)) {
+               seq_puts(seq, "<not supported>\n");
+               goto end;
+       }
+
+       result = acpi_processor_get_throttling(pr);
+
+       if (result) {
+               seq_puts(seq, "Could not determine current throttling state.\n");
+               goto end;
+       }
+
+       seq_printf(seq, "state count:             %d\n"
+                       "active state:            T%d\n",
+                       pr->throttling.state_count,
+                       pr->throttling.state);
+
+       seq_puts(seq, "states:\n");
+       for (i = 0; i < pr->throttling.state_count; i++)
+               seq_printf(seq, "   %cT%d:                  %02d%%\n",
+                       (i == pr->throttling.state?'*':' '), i,
+                       (pr->throttling.states[i].performance?pr->throttling.states[i].performance/10:0));
+
+end:
+       return_VALUE(0);
+}
+
+int acpi_processor_throttling_open_fs(struct inode *inode, struct file *file)
+{
+       return single_open(file, acpi_processor_throttling_seq_show,
+                                               PDE(inode)->data);
+}
+
+ssize_t acpi_processor_write_throttling (
+        struct file            *file,
+        const char             __user *buffer,
+        size_t                 count,
+        loff_t                 *data)
+{
+       int                     result = 0;
+        struct seq_file        *m = (struct seq_file *)file->private_data;
+       struct acpi_processor   *pr = (struct acpi_processor *)m->private;
+       char                    state_string[12] = {'\0'};
+
+       ACPI_FUNCTION_TRACE("acpi_processor_write_throttling");
+
+       if (!pr || (count > sizeof(state_string) - 1))
+               return_VALUE(-EINVAL);
+
+       if (copy_from_user(state_string, buffer, count))
+               return_VALUE(-EFAULT);
+
+       state_string[count] = '\0';
+
+       result = acpi_processor_set_throttling(pr,
+               simple_strtoul(state_string, NULL, 0));
+       if (result)
+               return_VALUE(result);
+
+       return_VALUE(count);
+}
+
+struct file_operations acpi_processor_throttling_fops = {
+       .open           = acpi_processor_throttling_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
new file mode 100644 (file)
index 0000000..5618a23
--- /dev/null
@@ -0,0 +1,1988 @@
+/*
+ *  video.c - ACPI Video Driver ($Revision:$)
+ *
+ *  Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
+ *  Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.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.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <asm/uaccess.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_VIDEO_COMPONENT           0x08000000
+#define ACPI_VIDEO_CLASS               "video"
+#define ACPI_VIDEO_DRIVER_NAME         "ACPI Video Driver"
+#define ACPI_VIDEO_BUS_NAME            "Video Bus"
+#define ACPI_VIDEO_DEVICE_NAME         "Video Device"
+#define ACPI_VIDEO_NOTIFY_SWITCH       0x80
+#define ACPI_VIDEO_NOTIFY_PROBE                0x81
+#define ACPI_VIDEO_NOTIFY_CYCLE                0x82
+#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT  0x83
+#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT  0x84
+
+#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS     0x82
+#define        ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS        0x83
+#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS       0x84
+#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS      0x85
+#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF          0x86
+
+
+#define ACPI_VIDEO_HEAD_INVALID                (~0u - 1)
+#define ACPI_VIDEO_HEAD_END            (~0u)
+
+
+#define _COMPONENT             ACPI_VIDEO_COMPONENT
+ACPI_MODULE_NAME               ("acpi_video")
+
+MODULE_AUTHOR("Bruno Ducrot");
+MODULE_DESCRIPTION(ACPI_VIDEO_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+static int acpi_video_bus_add (struct acpi_device *device);
+static int acpi_video_bus_remove (struct acpi_device *device, int type);
+static int acpi_video_bus_match (struct acpi_device *device, struct acpi_driver *driver);
+
+static struct acpi_driver acpi_video_bus = {
+       .name = ACPI_VIDEO_DRIVER_NAME,
+       .class = ACPI_VIDEO_CLASS,
+       .ops = {
+               .add = acpi_video_bus_add,
+               .remove = acpi_video_bus_remove,
+               .match = acpi_video_bus_match,
+       },
+};
+
+struct acpi_video_bus_flags {
+       u8      multihead:1;    /* can switch video heads */
+       u8      rom:1;          /* can retrieve a video rom */
+       u8      post:1;         /* can configure the head to */
+       u8      reserved:5;
+};
+
+struct acpi_video_bus_cap {
+       u8      _DOS:1; /*Enable/Disable output switching*/
+       u8      _DOD:1; /*Enumerate all devices attached to display adapter*/
+       u8      _ROM:1; /*Get ROM Data*/
+       u8      _GPD:1; /*Get POST Device*/
+       u8      _SPD:1; /*Set POST Device*/
+       u8      _VPO:1; /*Video POST Options*/
+       u8      reserved:2;
+};
+
+struct acpi_video_device_attrib{
+       u32     display_index:4; /* A zero-based instance of the Display*/
+       u32     display_port_attachment:4; /*This field differenates displays type*/
+       u32     display_type:4; /*Describe the specific type in use*/
+       u32     vendor_specific:4; /*Chipset Vendor Specifi*/ 
+       u32     bios_can_detect:1; /*BIOS can detect the device*/
+       u32     depend_on_vga:1; /*Non-VGA output device whose power is related to 
+                                  the VGA device.*/
+       u32     pipe_id:3; /*For VGA multiple-head devices.*/
+       u32     reserved:10; /*Must be 0*/
+       u32     device_id_scheme:1; /*Device ID Scheme*/
+};
+
+struct acpi_video_enumerated_device {
+       union {
+               u32 int_val;
+               struct acpi_video_device_attrib attrib;
+       } value;
+       struct acpi_video_device *bind_info;
+};
+
+struct acpi_video_bus {
+       acpi_handle     handle;
+       u8      dos_setting;
+       struct acpi_video_enumerated_device *attached_array;
+       u8                      attached_count;
+       struct acpi_video_bus_cap       cap;
+       struct acpi_video_bus_flags flags;
+       struct semaphore        sem;
+       struct list_head        video_device_list;
+       struct proc_dir_entry   *dir;
+};
+
+struct acpi_video_device_flags {
+       u8      crt:1;
+       u8      lcd:1;
+       u8      tvout:1;
+       u8      bios:1;
+       u8      unknown:1;
+       u8      reserved:3;
+};
+
+struct acpi_video_device_cap {
+       u8      _ADR:1; /*Return the unique ID */
+       u8      _BCL:1; /*Query list of brightness control levels supported*/
+       u8      _BCM:1; /*Set the brightness level*/
+       u8      _DDC:1; /*Return the EDID for this device*/
+       u8      _DCS:1; /*Return status of output device*/
+       u8      _DGS:1; /*Query graphics state*/
+       u8      _DSS:1; /*Device state set*/
+       u8      _reserved:1;
+};
+
+struct acpi_video_device_brightness {
+       int     curr;
+       int     count;
+       int     *levels;
+};
+
+struct acpi_video_device {
+       acpi_handle             handle;
+       unsigned long           device_id;
+       struct acpi_video_device_flags  flags;
+       struct acpi_video_device_cap    cap;
+       struct list_head        entry;
+       struct acpi_video_bus   *video;
+       struct acpi_device      *dev;
+       struct acpi_video_device_brightness *brightness;
+};
+
+
+/* bus */
+static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
+static struct file_operations acpi_video_bus_info_fops = {
+       .open           = acpi_video_bus_info_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
+static struct file_operations acpi_video_bus_ROM_fops = {
+       .open           = acpi_video_bus_ROM_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file);
+static struct file_operations acpi_video_bus_POST_info_fops = {
+       .open           = acpi_video_bus_POST_info_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
+static struct file_operations acpi_video_bus_POST_fops = {
+       .open           = acpi_video_bus_POST_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+
+static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
+static struct file_operations acpi_video_bus_DOS_fops = {
+       .open           = acpi_video_bus_DOS_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/* device */
+static int acpi_video_device_info_open_fs(struct inode *inode, struct file *file);
+static struct file_operations acpi_video_device_info_fops = {
+       .open           = acpi_video_device_info_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int acpi_video_device_state_open_fs(struct inode *inode, struct file *file);
+static struct file_operations acpi_video_device_state_fops = {
+       .open           = acpi_video_device_state_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file);
+static struct file_operations acpi_video_device_brightness_fops = {
+       .open           = acpi_video_device_brightness_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file);
+static struct file_operations acpi_video_device_EDID_fops = {
+       .open           = acpi_video_device_EDID_open_fs,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void acpi_video_device_notify ( acpi_handle handle, u32 event, void *data);
+static void acpi_video_device_rebind( struct acpi_video_bus *video);
+static void acpi_video_device_bind( struct acpi_video_bus *video, struct acpi_video_device *device);
+static int acpi_video_device_enumerate(struct acpi_video_bus *video);
+static int acpi_video_switch_output( struct acpi_video_bus *video, int event);
+static int acpi_video_get_next_level( struct acpi_video_device *device, u32 level_current,u32 event);
+static void acpi_video_switch_brightness ( struct acpi_video_device *device, int event);
+
+
+/* --------------------------------------------------------------------------
+                               Video Management
+   -------------------------------------------------------------------------- */
+
+/* device */
+
+static int
+acpi_video_device_query (
+       struct acpi_video_device        *device,
+       unsigned long                   *state)
+{
+       int                     status;
+       ACPI_FUNCTION_TRACE("acpi_video_device_query");
+       status = acpi_evaluate_integer(device->handle, "_DGS", NULL, state);
+
+       return_VALUE(status);
+}
+
+static int
+acpi_video_device_get_state (
+       struct acpi_video_device        *device,
+       unsigned long           *state)
+{
+       int                     status;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_get_state");
+
+       status = acpi_evaluate_integer(device->handle, "_DCS", NULL, state);
+
+       return_VALUE(status);
+}
+
+static int
+acpi_video_device_set_state (
+       struct acpi_video_device        *device,
+       int                     state)
+{
+       int                     status;
+       union acpi_object       arg0 = {ACPI_TYPE_INTEGER};
+       struct acpi_object_list args = {1, &arg0};
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_set_state");
+
+       arg0.integer.value = state;
+       status = acpi_evaluate_integer(device->handle, "_DSS", &args, NULL);
+
+       return_VALUE(status);
+}
+
+static int
+acpi_video_device_lcd_query_levels (
+       struct acpi_video_device        *device,
+       union acpi_object               **levels)
+{
+       int                     status;
+       struct acpi_buffer      buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object       *obj;
+
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_lcd_query_levels");
+
+       *levels = NULL;
+
+       status = acpi_evaluate_object(device->handle, "_BCL", NULL, &buffer);
+       if (!ACPI_SUCCESS(status))
+               return_VALUE(status);
+       obj = (union acpi_object *) buffer.pointer;
+       if (!obj && (obj->type != ACPI_TYPE_PACKAGE)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _BCL data\n"));
+               status = -EFAULT;
+               goto err;
+       }
+
+       *levels = obj;
+
+       return_VALUE(0);
+
+err:
+       if (buffer.pointer)
+               kfree(buffer.pointer);
+
+       return_VALUE(status);
+}
+
+static int
+acpi_video_device_lcd_set_level (
+       struct acpi_video_device        *device,
+       int                             level)
+{
+       int                     status;
+       union acpi_object       arg0 = {ACPI_TYPE_INTEGER};
+       struct acpi_object_list args = {1, &arg0};
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_lcd_set_level");
+
+       arg0.integer.value = level;
+       status = acpi_evaluate_object(device->handle, "_BCM", &args, NULL);
+
+       printk(KERN_DEBUG "set_level status: %x\n", status);
+       return_VALUE(status);
+}
+
+static int
+acpi_video_device_lcd_get_level_current (
+       struct acpi_video_device        *device,
+       unsigned long   *level)
+{
+       int                     status;
+       ACPI_FUNCTION_TRACE("acpi_video_device_lcd_get_level_current");
+
+       status = acpi_evaluate_integer(device->handle, "_BQC", NULL, level);
+
+       return_VALUE(status);
+}
+
+static int
+acpi_video_device_EDID (
+       struct acpi_video_device        *device,
+       union acpi_object               **edid,
+       ssize_t                         length)
+{
+       int                     status;
+       struct acpi_buffer      buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object       *obj;
+       union acpi_object       arg0 = {ACPI_TYPE_INTEGER};
+       struct acpi_object_list args = {1, &arg0};
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_get_EDID");
+
+       *edid = NULL;
+
+       if (!device)
+               return_VALUE(-ENODEV);
+       if (length == 128)
+               arg0.integer.value = 1;
+       else if (length == 256)
+               arg0.integer.value = 2;
+       else
+               return_VALUE(-EINVAL);
+
+       status = acpi_evaluate_object(device->handle, "_DDC", &args, &buffer);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       obj = (union acpi_object *) buffer.pointer;
+
+       if (obj && obj->type == ACPI_TYPE_BUFFER)
+               *edid = obj;
+       else {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _DDC data\n"));
+               status = -EFAULT;
+               kfree(obj);
+       }
+
+       return_VALUE(status);
+}
+
+
+/* bus */
+
+static int
+acpi_video_bus_set_POST (
+       struct acpi_video_bus   *video,
+       unsigned long           option)
+{
+       int                     status;
+       unsigned long           tmp;
+       union acpi_object       arg0 = {ACPI_TYPE_INTEGER};
+       struct acpi_object_list args = {1, &arg0};
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_set_POST");
+
+       arg0.integer.value = option;
+
+       status = acpi_evaluate_integer(video->handle, "_SPD", &args, &tmp);
+       if (ACPI_SUCCESS(status))
+               status = tmp ? (-EINVAL):(AE_OK);
+
+       return_VALUE(status);
+}
+
+static int
+acpi_video_bus_get_POST (
+       struct acpi_video_bus   *video,
+       unsigned long           *id)
+{
+       int status;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_get_POST");
+
+       status = acpi_evaluate_integer(video->handle, "_GPD", NULL, id);
+
+       return_VALUE(status);
+}
+
+static int
+acpi_video_bus_POST_options (
+       struct acpi_video_bus   *video,
+       unsigned long           *options)
+{
+       int                     status;
+       ACPI_FUNCTION_TRACE("acpi_video_bus_POST_options");
+
+       status = acpi_evaluate_integer(video->handle, "_VPO", NULL, options);
+       *options &= 3;
+
+       return_VALUE(status);
+}
+
+/*
+ *  Arg:
+ *     video           : video bus device pointer
+ *     bios_flag       : 
+ *             0.      The system BIOS should NOT automatically switch(toggle)
+ *                     the active display output.
+ *             1.      The system BIOS should automatically switch (toggle) the
+ *                     active display output. No swich event.
+ *             2.      The _DGS value should be locked.
+ *             3.      The system BIOS should not automatically switch (toggle) the
+ *                     active display output, but instead generate the display switch
+ *                     event notify code.
+ *     lcd_flag        :
+ *             0.      The system BIOS should automatically control the brightness level
+ *                     of the LCD, when the power changes from AC to DC
+ *             1.      The system BIOS should NOT automatically control the brightness 
+ *                     level of the LCD, when the power changes from AC to DC.
+ * Return Value:
+ *             -1      wrong arg.
+ */
+
+static int
+acpi_video_bus_DOS(
+       struct acpi_video_bus   *video,
+       int                     bios_flag,
+       int                     lcd_flag)
+{
+       acpi_integer            status = 0;
+       union acpi_object       arg0 = {ACPI_TYPE_INTEGER};
+       struct acpi_object_list args = {1, &arg0};
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_DOS");
+
+       if (bios_flag < 0 || bios_flag >3 || lcd_flag < 0 || lcd_flag > 1){
+               status = -1;
+               goto Failed;
+       }
+       arg0.integer.value = (lcd_flag << 2) | bios_flag;
+       video->dos_setting = arg0.integer.value;
+       acpi_evaluate_object(video->handle, "_DOS", &args, NULL);
+
+Failed:
+       return_VALUE(status);
+}
+
+/*
+ *  Arg:       
+ *     device  : video output device (LCD, CRT, ..)
+ *
+ *  Return Value:
+ *     None
+ *
+ *  Find out all required AML method defined under the output
+ *  device.
+ */
+
+static void
+acpi_video_device_find_cap (struct acpi_video_device *device)
+{
+       acpi_integer            status;
+       acpi_handle h_dummy1;
+       int i;
+       union acpi_object *obj = NULL;
+       struct acpi_video_device_brightness *br = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_find_cap");
+
+       memset( &device->cap, 0, 4);
+
+       if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_ADR", &h_dummy1))) {
+               device->cap._ADR = 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_BCL", &h_dummy1))) {
+               device->cap._BCL= 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_BCM", &h_dummy1))) {
+               device->cap._BCM= 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DDC", &h_dummy1))) {
+               device->cap._DDC= 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DCS", &h_dummy1))) {
+               device->cap._DCS = 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DGS", &h_dummy1))) {
+               device->cap._DGS = 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DSS", &h_dummy1))) {
+               device->cap._DSS = 1;
+       }
+
+       status = acpi_video_device_lcd_query_levels(device, &obj);
+
+       if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count >= 2) {
+               int count = 0;
+               union acpi_object *o;
+               
+               br = kmalloc(sizeof &br, GFP_KERNEL);
+               if (!br) {
+                       printk(KERN_ERR "can't allocate memory\n");
+               } else {
+                       memset(br, 0, sizeof &br);
+                       br->levels = kmalloc(obj->package.count * sizeof &br->levels, GFP_KERNEL);
+                       if (!br->levels)
+                               goto out;
+
+                       for (i = 0; i < obj->package.count; i++) {
+                               o = (union acpi_object *) &obj->package.elements[i];
+                               if (o->type != ACPI_TYPE_INTEGER) {
+                                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data\n"));
+                                       continue;
+                               }
+                               br->levels[count] = (u32) o->integer.value;
+                               count++;
+                       }
+out:
+                       if (count < 2) {
+                               if (br->levels)
+                                       kfree(br->levels);
+                               kfree(br);
+                       } else {
+                               br->count = count;
+                               device->brightness = br;
+                               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "found %d brightness levels\n", count));
+                       }
+               }
+       }
+
+       if (obj)
+               kfree(obj);
+
+       return_VOID;
+}
+
+/*
+ *  Arg:       
+ *     device  : video output device (VGA)
+ *
+ *  Return Value:
+ *     None
+ *
+ *  Find out all required AML method defined under the video bus device.
+ */
+
+static void 
+acpi_video_bus_find_cap (struct acpi_video_bus *video)
+{
+       acpi_handle     h_dummy1;
+
+       memset(&video->cap ,0, 4);
+       if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_DOS", &h_dummy1))) {
+               video->cap._DOS = 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_DOD", &h_dummy1))) {
+               video->cap._DOD = 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_ROM", &h_dummy1))) {
+               video->cap._ROM = 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_GPD", &h_dummy1))) {
+               video->cap._GPD = 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_SPD", &h_dummy1))) {
+               video->cap._SPD = 1;
+       }
+       if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_VPO", &h_dummy1))) {
+               video->cap._VPO = 1;
+       }
+}
+
+/*
+ * Check whether the video bus device has required AML method to
+ * support the desired features
+ */
+
+static int
+acpi_video_bus_check (
+       struct acpi_video_bus   *video)
+{
+       acpi_status             status = -ENOENT;
+
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_check");
+
+       if (!video)
+               return_VALUE(-EINVAL);
+
+       /* Since there is no HID, CID and so on for VGA driver, we have
+        * to check well known required nodes.
+        */
+
+       /* Does this device able to support video switching ? */
+       if(video->cap._DOS){
+               video->flags.multihead = 1;
+               status = 0;
+       }
+
+       /* Does this device able to retrieve a retrieve a video ROM ? */
+       if(video->cap._ROM){
+               video->flags.rom = 1;
+               status = 0;
+       }
+
+       /* Does this device able to configure which video device to POST ? */
+       if(video->cap._GPD && video->cap._SPD && video->cap._VPO){
+               video->flags.post = 1;
+               status = 0;
+       }
+
+       return_VALUE(status);
+}
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+struct proc_dir_entry          *acpi_video_dir;
+
+/* video devices */
+
+static int
+acpi_video_device_info_seq_show (
+       struct seq_file         *seq,
+       void                    *offset)
+{
+       struct acpi_video_device        *dev = (struct acpi_video_device *) seq->private;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_info_seq_show");
+
+       if (!dev)
+               goto end;
+
+       seq_printf(seq, "device_id:    0x%04x\n", (u32) dev->device_id);
+       seq_printf(seq, "type:         ");
+       if (dev->flags.crt)
+               seq_printf(seq, "CRT\n");
+       else if (dev->flags.lcd)
+               seq_printf(seq, "LCD\n");
+       else if (dev->flags.tvout)
+               seq_printf(seq, "TVOUT\n");
+       else
+               seq_printf(seq, "UNKNOWN\n");
+
+       seq_printf(seq,"known by bios: %s\n",
+                  dev->flags.bios ? "yes":"no");
+
+end:
+       return_VALUE(0);
+}
+
+static int
+acpi_video_device_info_open_fs (
+       struct inode            *inode,
+       struct file             *file)
+{
+       return single_open(file, acpi_video_device_info_seq_show,
+                          PDE(inode)->data);
+}
+
+static int  
+acpi_video_device_state_seq_show (
+       struct seq_file         *seq,
+       void                    *offset)
+{
+       int                     status;
+       struct acpi_video_device        *dev = (struct acpi_video_device *) seq->private;
+       unsigned long   state;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_state_seq_show");
+
+       if (!dev)
+               goto end;
+
+       status = acpi_video_device_get_state(dev, &state);
+       seq_printf(seq, "state:     ");
+       if (ACPI_SUCCESS(status))
+               seq_printf(seq, "0x%02lx\n", state);
+       else
+               seq_printf(seq, "<not supported>\n");
+
+       status = acpi_video_device_query(dev, &state);
+       seq_printf(seq, "query:     ");
+       if (ACPI_SUCCESS(status))
+               seq_printf(seq, "0x%02lx\n", state);
+       else
+               seq_printf(seq, "<not supported>\n");
+
+end:
+       return_VALUE(0);
+}
+
+static int
+acpi_video_device_state_open_fs (
+       struct inode            *inode,
+       struct file             *file)
+{
+       return single_open(file, acpi_video_device_state_seq_show,
+                          PDE(inode)->data);
+}
+
+static ssize_t
+acpi_video_device_write_state (
+       struct file             *file,
+       const char              __user *buffer,
+       size_t                  count,
+       loff_t                  *data)
+{
+       int                     status;
+       struct seq_file         *m = (struct seq_file *) file->private_data;
+       struct acpi_video_device        *dev = (struct acpi_video_device *) m->private;
+       char                    str[12] = {0};
+       u32                     state = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_write_state");
+
+       if (!dev || count + 1 > sizeof str)
+               return_VALUE(-EINVAL);
+
+       if (copy_from_user(str, buffer, count))
+               return_VALUE(-EFAULT);
+
+       str[count] = 0;
+       state = simple_strtoul(str, NULL, 0);
+       state &= ((1ul<<31) | (1ul<<30) | (1ul<<0));
+
+       status = acpi_video_device_set_state(dev, state);
+
+       if (status)
+               return_VALUE(-EFAULT);
+
+       return_VALUE(count);
+}
+
+static int
+acpi_video_device_brightness_seq_show (
+       struct seq_file         *seq,
+       void                    *offset)
+{
+       struct acpi_video_device        *dev = (struct acpi_video_device *) seq->private;
+       int                     i;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_brightness_seq_show");
+
+       if (!dev || !dev->brightness) {
+               seq_printf(seq, "<not supported>\n");
+               return_VALUE(0);
+       }
+
+       seq_printf(seq, "levels: ");
+       for (i = 0; i < dev->brightness->count; i++)
+               seq_printf(seq, " %d", dev->brightness->levels[i]);
+       seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr);
+
+       return_VALUE(0);
+}
+
+static int
+acpi_video_device_brightness_open_fs (
+       struct inode            *inode,
+       struct file             *file)
+{
+       return single_open(file, acpi_video_device_brightness_seq_show,
+                          PDE(inode)->data);
+}
+
+static ssize_t
+acpi_video_device_write_brightness (
+       struct file             *file,
+       const char              __user *buffer,
+       size_t                  count,
+       loff_t                  *data)
+{
+       struct seq_file         *m = (struct seq_file *) file->private_data;
+       struct acpi_video_device        *dev = (struct acpi_video_device *) m->private;
+       char                    str[4] = {0};
+       unsigned int            level = 0;
+       int                     i;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_write_brightness");
+
+       if (!dev || count + 1 > sizeof str)
+               return_VALUE(-EINVAL);
+
+       if (copy_from_user(str, buffer, count))
+               return_VALUE(-EFAULT);
+
+       str[count] = 0;
+       level = simple_strtoul(str, NULL, 0);
+       
+       if (level > 100)
+               return_VALUE(-EFAULT);
+
+       /* validate though the list of available levels */
+       for (i = 0; i < dev->brightness->count; i++)
+               if (level == dev->brightness->levels[i]) {
+                       if (ACPI_SUCCESS(acpi_video_device_lcd_set_level(dev, level)))
+                               dev->brightness->curr = level;
+                       break;
+               }
+
+       return_VALUE(count);
+}
+
+static int
+acpi_video_device_EDID_seq_show (
+       struct seq_file         *seq,
+       void                    *offset)
+{
+       struct acpi_video_device        *dev = (struct acpi_video_device *) seq->private;
+       int                     status;
+       int                     i;
+       union acpi_object       *edid = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_EDID_seq_show");
+
+       if (!dev)
+               goto out;
+
+       status = acpi_video_device_EDID (dev, &edid, 128);
+       if (ACPI_FAILURE(status)) {
+               status = acpi_video_device_EDID (dev, &edid, 256);
+       }
+
+       if (ACPI_FAILURE(status)) {
+               goto out;
+       }
+
+       if (edid && edid->type == ACPI_TYPE_BUFFER) {
+               for (i = 0; i < edid->buffer.length; i++)
+                       seq_putc(seq, edid->buffer.pointer[i]);
+       }
+
+out:
+       if (!edid)
+               seq_printf(seq, "<not supported>\n");
+       else
+               kfree(edid);
+
+       return_VALUE(0);
+}
+
+static int
+acpi_video_device_EDID_open_fs (
+       struct inode            *inode,
+       struct file             *file)
+{
+       return single_open(file, acpi_video_device_EDID_seq_show,
+                          PDE(inode)->data);
+}
+
+
+static int
+acpi_video_device_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+       struct acpi_video_device *vid_dev;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_add_fs");
+
+       if (!device)
+               return_VALUE(-ENODEV);
+
+       vid_dev = (struct acpi_video_device *) acpi_driver_data(device);
+       if (!vid_dev)
+               return_VALUE(-ENODEV);
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                               vid_dev->video->dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+               acpi_device_dir(device)->owner = THIS_MODULE;
+       }
+
+       /* 'info' [R] */
+       entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create 'info' fs entry\n"));
+       else {
+               entry->proc_fops = &acpi_video_device_info_fops;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       /* 'state' [R/W] */
+       entry = create_proc_entry("state", S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create 'state' fs entry\n"));
+       else {
+               entry->proc_fops = &acpi_video_device_state_fops;
+               entry->proc_fops->write = acpi_video_device_write_state;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       /* 'brightness' [R/W] */
+       entry = create_proc_entry("brightness", S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create 'brightness' fs entry\n"));
+       else {
+               entry->proc_fops = &acpi_video_device_brightness_fops;
+               entry->proc_fops->write = acpi_video_device_write_brightness;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       /* 'EDID' [R] */
+       entry = create_proc_entry("EDID", S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Unable to create 'brightness' fs entry\n"));
+       else {
+               entry->proc_fops = &acpi_video_device_EDID_fops;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       return_VALUE(0);
+}
+
+static int
+acpi_video_device_remove_fs (
+       struct acpi_device      *device)
+{
+       struct acpi_video_device *vid_dev;
+       ACPI_FUNCTION_TRACE("acpi_video_device_remove_fs");
+
+       vid_dev = (struct acpi_video_device *) acpi_driver_data(device);
+       if (!vid_dev || !vid_dev->video || !vid_dev->video->dir)
+               return_VALUE(-ENODEV);
+
+       if (acpi_device_dir(device)) {
+               remove_proc_entry("info", acpi_device_dir(device));
+               remove_proc_entry("state", acpi_device_dir(device));
+               remove_proc_entry("brightness", acpi_device_dir(device));
+               remove_proc_entry("EDID", acpi_device_dir(device));
+               remove_proc_entry(acpi_device_bid(device),
+                                vid_dev->video->dir);
+               acpi_device_dir(device) = NULL;
+       }
+
+       return_VALUE(0);
+}
+
+
+/* video bus */
+static int
+acpi_video_bus_info_seq_show (
+       struct seq_file         *seq,
+       void                    *offset)
+{
+       struct acpi_video_bus   *video = (struct acpi_video_bus *) seq->private;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_info_seq_show");
+
+       if (!video)
+               goto end;
+
+       seq_printf(seq, "Switching heads:              %s\n",
+                       video->flags.multihead ? "yes":"no");
+       seq_printf(seq, "Video ROM:                    %s\n",
+                       video->flags.rom ? "yes":"no");
+       seq_printf(seq, "Device to be POSTed on boot:  %s\n",
+                       video->flags.post ? "yes":"no");
+
+end:
+       return_VALUE(0);
+}
+
+static int
+acpi_video_bus_info_open_fs (
+       struct inode            *inode,
+       struct file             *file)
+{
+       return single_open(file, acpi_video_bus_info_seq_show, PDE(inode)->data);
+}
+
+static int
+acpi_video_bus_ROM_seq_show (
+       struct seq_file         *seq,
+       void                    *offset)
+{
+       struct acpi_video_bus   *video = (struct acpi_video_bus *) seq->private;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_ROM_seq_show");
+
+       if (!video)
+               goto end;
+
+       printk(KERN_INFO PREFIX "Please implement %s\n", __FUNCTION__);
+       seq_printf(seq, "<TODO>\n");
+
+end:
+       return_VALUE(0);
+}
+
+static int
+acpi_video_bus_ROM_open_fs (
+       struct inode            *inode,
+       struct file             *file)
+{
+       return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data);
+}
+
+static int
+acpi_video_bus_POST_info_seq_show (
+       struct seq_file         *seq,
+       void                    *offset)
+{
+       struct acpi_video_bus   *video = (struct acpi_video_bus *) seq->private;
+       unsigned long           options;
+       int                     status;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_POST_info_seq_show");
+
+       if (!video)
+               goto end;
+
+       status = acpi_video_bus_POST_options(video, &options);
+       if (ACPI_SUCCESS(status)) {
+               if (!(options & 1)) {
+                       printk(KERN_WARNING PREFIX "The motherboard VGA device is not listed as a possible POST device.\n");
+                       printk(KERN_WARNING PREFIX "This indicate a BIOS bug.  Please contact the manufacturer.\n");
+               }
+               printk("%lx\n", options);
+               seq_printf(seq, "can POST: <intgrated video>");
+               if (options & 2)
+                       seq_printf(seq, " <PCI video>");
+               if (options & 4)
+                       seq_printf(seq, " <AGP video>");
+               seq_putc(seq, '\n');
+       } else
+               seq_printf(seq, "<not supported>\n");
+end:
+       return_VALUE(0);
+}
+
+static int
+acpi_video_bus_POST_info_open_fs (
+       struct inode            *inode,
+       struct file             *file)
+{
+       return single_open(file, acpi_video_bus_POST_info_seq_show, PDE(inode)->data);
+}
+
+static int
+acpi_video_bus_POST_seq_show (
+       struct seq_file         *seq,
+       void                    *offset)
+{
+       struct acpi_video_bus   *video = (struct acpi_video_bus *) seq->private;
+       int                     status;
+       unsigned long           id;
+       char                    device_decode[][30] = {
+                                       "motherboard VGA device",
+                                       "PCI VGA device",
+                                       "AGP VGA device",
+                                       "UNKNOWN",
+       };
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_POST_seq_show");
+
+       if (!video)
+               goto end;
+
+       status = acpi_video_bus_get_POST (video, &id);
+       if (!ACPI_SUCCESS(status)) {
+               seq_printf(seq, "<not supported>\n");
+               goto end;
+       }
+       seq_printf(seq, "device posted is <%s>\n",  device_decode[id & 3]);
+
+end:
+       return_VALUE(0);
+}
+
+static int
+acpi_video_bus_DOS_seq_show (
+       struct seq_file         *seq,
+       void                    *offset)
+{
+       struct acpi_video_bus   *video = (struct acpi_video_bus *) seq->private;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_DOS_seq_show");
+
+       seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting );
+
+       return_VALUE(0);
+}
+
+static int
+acpi_video_bus_POST_open_fs (
+       struct inode            *inode,
+       struct file             *file)
+{
+       return single_open(file, acpi_video_bus_POST_seq_show, PDE(inode)->data);
+}
+
+static int
+acpi_video_bus_DOS_open_fs (
+       struct inode            *inode,
+       struct file             *file)
+{
+       return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data);
+}
+
+static ssize_t
+acpi_video_bus_write_POST (
+       struct file             *file,
+       const char              __user *buffer,
+       size_t                  count,
+       loff_t                  *data)
+{
+       int                     status;
+       struct seq_file         *m = (struct seq_file *) file->private_data;
+       struct acpi_video_bus   *video = (struct acpi_video_bus *) m->private;
+       char                    str[12] = {0};
+       unsigned long           opt, options;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_write_POST");
+
+
+       if (!video || count + 1 > sizeof str)
+               return_VALUE(-EINVAL);
+
+       status = acpi_video_bus_POST_options(video, &options);
+       if (!ACPI_SUCCESS(status))
+               return_VALUE(-EINVAL);
+
+       if (copy_from_user(str, buffer, count))
+               return_VALUE(-EFAULT);
+
+       str[count] = 0;
+       opt = strtoul(str, NULL, 0);
+       if (opt > 3)
+               return_VALUE(-EFAULT);
+
+       /* just in case an OEM 'forget' the motherboard... */
+       options |= 1;
+
+       if (options & (1ul << opt)) {
+               status = acpi_video_bus_set_POST (video, opt);
+               if (!ACPI_SUCCESS(status))
+                       return_VALUE(-EFAULT);
+
+       }
+
+
+       return_VALUE(count);
+}
+
+static ssize_t
+acpi_video_bus_write_DOS (
+       struct file             *file,
+       const char              __user *buffer,
+       size_t                  count,
+       loff_t                  *data)
+{
+       int                     status;
+       struct seq_file         *m = (struct seq_file *) file->private_data;
+       struct acpi_video_bus   *video = (struct acpi_video_bus *) m->private;
+       char                    str[12] = {0};
+       unsigned long           opt;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_write_DOS");
+
+
+       if (!video || count + 1 > sizeof str)
+               return_VALUE(-EINVAL);
+
+       if (copy_from_user(str, buffer, count))
+               return_VALUE(-EFAULT);
+
+       str[count] = 0;
+       opt = strtoul(str, NULL, 0);
+       if (opt > 7)
+               return_VALUE(-EFAULT);
+
+       status = acpi_video_bus_DOS (video, opt & 0x3, (opt & 0x4)>>2);
+
+       if (!ACPI_SUCCESS(status))
+               return_VALUE(-EFAULT);
+
+       return_VALUE(count);
+}
+
+static int
+acpi_video_bus_add_fs (
+       struct acpi_device      *device)
+{
+       struct proc_dir_entry   *entry = NULL;
+       struct acpi_video_bus   *video;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_add_fs");
+
+       video = (struct acpi_video_bus *) acpi_driver_data(device);
+
+       if (!acpi_device_dir(device)) {
+               acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+                               acpi_video_dir);
+               if (!acpi_device_dir(device))
+                       return_VALUE(-ENODEV);
+               video->dir = acpi_device_dir(device);
+               acpi_device_dir(device)->owner = THIS_MODULE;
+       }
+
+       /* 'info' [R] */
+       entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'info' fs entry\n"));
+       else {
+               entry->proc_fops = &acpi_video_bus_info_fops;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       /* 'ROM' [R] */
+       entry = create_proc_entry("ROM", S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'ROM' fs entry\n"));
+       else {
+               entry->proc_fops = &acpi_video_bus_ROM_fops;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       /* 'POST_info' [R] */
+       entry = create_proc_entry("POST_info", S_IRUGO, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'POST_info' fs entry\n"));
+       else {
+               entry->proc_fops = &acpi_video_bus_POST_info_fops;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       /* 'POST' [R/W] */
+       entry = create_proc_entry("POST", S_IFREG|S_IRUGO|S_IRUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'POST' fs entry\n"));
+       else {
+               entry->proc_fops = &acpi_video_bus_POST_fops;
+               entry->proc_fops->write = acpi_video_bus_write_POST;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       /* 'DOS' [R/W] */
+       entry = create_proc_entry("DOS", S_IFREG|S_IRUGO|S_IRUSR, acpi_device_dir(device));
+       if (!entry)
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'DOS' fs entry\n"));
+       else {
+               entry->proc_fops = &acpi_video_bus_DOS_fops;
+               entry->proc_fops->write = acpi_video_bus_write_DOS;
+               entry->data = acpi_driver_data(device);
+               entry->owner = THIS_MODULE;
+       }
+
+       return_VALUE(0);
+}
+
+static int
+acpi_video_bus_remove_fs (
+       struct acpi_device      *device)
+{
+       struct acpi_video_bus   *video;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_remove_fs");
+
+       video = (struct acpi_video_bus *) acpi_driver_data(device);
+
+       if (acpi_device_dir(device)) {
+               remove_proc_entry("info", acpi_device_dir(device));
+               remove_proc_entry("ROM", acpi_device_dir(device));
+               remove_proc_entry("POST_info", acpi_device_dir(device));
+               remove_proc_entry("POST", acpi_device_dir(device));
+               remove_proc_entry("DOS", acpi_device_dir(device));
+               remove_proc_entry(acpi_device_bid(device),
+                               acpi_video_dir); 
+               acpi_device_dir(device) = NULL;
+       }
+
+       return_VALUE(0);
+}
+
+/* --------------------------------------------------------------------------
+                                 Driver Interface
+   -------------------------------------------------------------------------- */
+
+/* device interface */
+
+static int
+acpi_video_bus_get_one_device (
+       struct acpi_device      *device,
+       struct acpi_video_bus   *video)
+{
+       unsigned long           device_id;
+       int                     status, result;
+       struct acpi_video_device        *data;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_get_one_device");
+
+       if (!device || !video)
+               return_VALUE(-EINVAL);
+
+       status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
+       if (ACPI_SUCCESS(status)) {
+
+               data = kmalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
+               if (!data)
+                       return_VALUE(-ENOMEM);
+
+               memset(data, 0, sizeof(struct acpi_video_device));
+
+               data->handle = device->handle;
+               strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
+               strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
+               acpi_driver_data(device) = data;
+
+               data->device_id = device_id;
+               data->video = video;
+               data->dev = device;
+
+               switch (device_id & 0xffff) {
+               case 0x0100:
+                       data->flags.crt = 1;
+                       break;
+               case 0x0400:
+                       data->flags.lcd = 1;
+                       break;
+               case 0x0200:
+                       data->flags.tvout = 1;
+                       break;
+               default:
+                       data->flags.unknown = 1;
+                       break;
+               }
+               
+               acpi_video_device_bind(video, data);
+               acpi_video_device_find_cap(data);
+
+               status = acpi_install_notify_handler(data->handle,
+                       ACPI_DEVICE_NOTIFY, acpi_video_device_notify, data);
+               if (ACPI_FAILURE(status)) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                               "Error installing notify handler\n"));
+                       result = -ENODEV;
+                       goto end;
+               }
+
+               down(&video->sem);
+               list_add_tail(&data->entry, &video->video_device_list);
+               up(&video->sem);
+
+               acpi_video_device_add_fs(device);
+
+               return_VALUE(0);
+       }
+
+end:
+       return_VALUE(-ENOENT);
+}
+
+/*
+ *  Arg:
+ *     video   : video bus device 
+ *
+ *  Return:
+ *     none
+ *  
+ *  Enumerate the video device list of the video bus, 
+ *  bind the ids with the corresponding video devices
+ *  under the video bus.
+ */  
+
+static void
+acpi_video_device_rebind( struct acpi_video_bus *video)
+{
+       struct list_head * node, * next;
+       list_for_each_safe(node, next, &video->video_device_list) {
+               struct acpi_video_device * dev = container_of(node, struct acpi_video_device, entry);
+               acpi_video_device_bind( video, dev);
+       }
+}
+
+/*
+ *  Arg:
+ *     video   : video bus device 
+ *     device  : video output device under the video 
+ *             bus
+ *
+ *  Return:
+ *     none
+ *  
+ *  Bind the ids with the corresponding video devices
+ *  under the video bus.
+ */  
+
+static void
+acpi_video_device_bind( struct acpi_video_bus *video,
+                       struct acpi_video_device *device)
+{
+       int     i;
+       ACPI_FUNCTION_TRACE("acpi_video_device_bind");
+
+#define IDS_VAL(i) video->attached_array[i].value.int_val
+#define IDS_BIND(i) video->attached_array[i].bind_info
+       
+       for (i = 0; IDS_VAL(i) != ACPI_VIDEO_HEAD_INVALID && 
+               i < video->attached_count; i++) {
+               if (device->device_id == (IDS_VAL(i)& 0xffff)) {
+                       IDS_BIND(i) = device;
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i));
+               }
+       }
+#undef IDS_VAL
+#undef IDS_BIND
+}
+
+/*
+ *  Arg:
+ *     video   : video bus device 
+ *
+ *  Return:
+ *     < 0     : error
+ *  
+ *  Call _DOD to enumerate all devices attached to display adapter
+ *
+ */  
+
+static int acpi_video_device_enumerate(struct acpi_video_bus *video)
+{
+       int                     status;
+       int                     count;
+       int                     i;
+       struct acpi_video_enumerated_device *active_device_list;
+       struct acpi_buffer      buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object       *dod = NULL;
+       union acpi_object       *obj;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_enumerate");
+
+       status = acpi_evaluate_object(video->handle, "_DOD", NULL, &buffer);
+       if (!ACPI_SUCCESS(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _DOD\n"));
+               return_VALUE(status);
+       }
+
+       dod = (union acpi_object *) buffer.pointer;
+       if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _DOD data\n"));
+               status = -EFAULT;
+               goto out;
+       }
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n",
+               dod->package.count));
+
+       active_device_list= kmalloc(
+               dod->package.count*sizeof(struct acpi_video_enumerated_device),
+               GFP_KERNEL);
+
+       if (!active_device_list) {
+               status = -ENOMEM;
+               goto out;
+       }
+
+       count = 0;
+       for (i = 0; i < dod->package.count; i++) {
+               obj = (union acpi_object *) &dod->package.elements[i];
+
+               if (obj->type != ACPI_TYPE_INTEGER) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _DOD data\n"));
+                       active_device_list[i].value.int_val = ACPI_VIDEO_HEAD_INVALID;
+               }
+               active_device_list[i].value.int_val = obj->integer.value;
+               active_device_list[i].bind_info = NULL;
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i, (int) obj->integer.value));
+               count++;
+       }
+       active_device_list[count].value.int_val = ACPI_VIDEO_HEAD_END;
+
+       if(video->attached_array)
+               kfree(video->attached_array);
+       
+       video->attached_array = active_device_list;
+       video->attached_count = count;
+out:
+       acpi_os_free(buffer.pointer);
+       return_VALUE(status);
+}
+
+/*
+ *  Arg:
+ *     video   : video bus device 
+ *     event   : Nontify Event
+ *
+ *  Return:
+ *     < 0     : error
+ *  
+ *     1. Find out the current active output device.
+ *     2. Identify the next output device to switch
+ *     3. call _DSS to do actual switch.
+ */  
+
+static int 
+acpi_video_switch_output(
+       struct acpi_video_bus *video, 
+       int     event)
+{
+       struct list_head * node, * next;
+       struct acpi_video_device *dev=NULL;
+               struct acpi_video_device *dev_next=NULL;
+       struct acpi_video_device *dev_prev=NULL;
+       unsigned long state;
+       int status = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_video_switch_output");
+
+       list_for_each_safe(node, next, &video->video_device_list) {
+               struct acpi_video_device * dev = container_of(node, struct acpi_video_device, entry);
+               status = acpi_video_device_get_state(dev, &state);
+               if (state & 0x2){
+                       dev_next = container_of(node->next, struct acpi_video_device, entry);
+                       dev_prev = container_of(node->prev, struct acpi_video_device, entry);
+                       goto out;
+               }
+       }
+       dev_next = container_of(node->next, struct acpi_video_device, entry);
+       dev_prev = container_of(node->prev, struct acpi_video_device, entry);
+out:   
+       switch (event) {
+       case ACPI_VIDEO_NOTIFY_CYCLE:
+       case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:
+               acpi_video_device_set_state(dev, 0);
+               acpi_video_device_set_state(dev_next, 0x80000001);
+               break;
+       case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:
+               acpi_video_device_set_state(dev, 0);
+               acpi_video_device_set_state(dev_prev, 0x80000001);
+       default:
+               break;
+       }
+
+       return_VALUE(status);
+}
+
+static int 
+acpi_video_get_next_level(
+       struct acpi_video_device *device,
+       u32     level_current,
+       u32     event)
+{
+       /*Fix me*/
+       return level_current;
+}
+
+
+static void
+acpi_video_switch_brightness (
+       struct acpi_video_device *device, 
+       int     event)
+{
+       unsigned long level_current, level_next;
+       acpi_video_device_lcd_get_level_current(device, &level_current);
+       level_next = acpi_video_get_next_level(device, level_current, event);
+       acpi_video_device_lcd_set_level(device, level_next);
+}
+
+static int
+acpi_video_bus_get_devices (
+       struct acpi_video_bus   *video,
+       struct acpi_device      *device)
+{
+       int                     status = 0;
+       struct list_head        *node, *next;
+
+       ACPI_FUNCTION_TRACE("acpi_video_get_devices");
+
+       acpi_video_device_enumerate(video);
+
+       list_for_each_safe(node, next, &device->children) {
+               struct acpi_device *dev = list_entry(node, struct acpi_device, node);
+
+               if (!dev)
+                       continue;
+
+               status = acpi_video_bus_get_one_device(dev, video);
+               if (ACPI_FAILURE(status)) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Cant attach device\n"));
+                       continue;
+               }
+
+       }
+       return_VALUE(status);
+}
+
+static int
+acpi_video_bus_put_one_device(
+       struct acpi_video_device        *device)
+{
+       struct acpi_video_bus *video;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_put_one_device");
+
+       if (!device || !device->video)
+               return_VALUE(-ENOENT);
+
+       video = device->video;
+
+       down(&video->sem);
+       list_del(&device->entry);
+       up(&video->sem);
+       acpi_video_device_remove_fs(device->dev);
+
+       return_VALUE(0);
+}
+
+static int
+acpi_video_bus_put_devices (
+       struct acpi_video_bus   *video)
+{
+       int                     status;
+       struct list_head        *node, *next;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_put_devices");
+
+       list_for_each_safe(node, next, &video->video_device_list) {
+               struct acpi_video_device *data = list_entry(node, struct acpi_video_device, entry);
+               if (!data)
+                       continue;
+
+               status = acpi_video_bus_put_one_device(data);
+               if(ACPI_FAILURE(status))
+                       printk(KERN_WARNING PREFIX "hhuuhhuu bug in acpi video driver.\n");
+
+               if (data->brightness)
+                       kfree(data->brightness);
+
+               kfree(data);
+       }
+
+       return_VALUE(0);
+}
+
+/* acpi_video interface */
+
+static int
+acpi_video_bus_start_devices(
+       struct acpi_video_bus   *video)
+{
+       return acpi_video_bus_DOS(video, 1, 0);
+}
+
+static int
+acpi_video_bus_stop_devices(
+       struct acpi_video_bus   *video)
+{
+       return acpi_video_bus_DOS(video, 0, 1);
+}
+
+static void
+acpi_video_bus_notify (
+       acpi_handle             handle,
+       u32                     event,
+       void                    *data)
+{
+       struct acpi_video_bus   *video = (struct acpi_video_bus *) data;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_notify");
+       printk("video bus notify\n");
+
+       if (!video)
+               return_VOID;
+
+       if (acpi_bus_get_device(handle, &device))
+               return_VOID;
+
+       switch (event) {
+       case ACPI_VIDEO_NOTIFY_SWITCH:  /* User request that a switch occur,
+                                        * most likely via hotkey. */
+               acpi_bus_generate_event(device, event, 0);
+               break;
+
+       case ACPI_VIDEO_NOTIFY_PROBE:   /* User plug or remove a video
+                                        * connector. */
+               acpi_video_device_enumerate(video);
+               acpi_video_device_rebind(video);
+               acpi_video_switch_output(video, event);
+               acpi_bus_generate_event(device, event, 0);
+               break;
+
+       case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed.*/
+       case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */
+       case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */
+               acpi_video_switch_output(video, event);
+               acpi_bus_generate_event(device, event, 0);
+               break;
+
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Unsupported event [0x%x]\n", event));
+               break;
+       }
+
+       return_VOID;
+}
+
+static void
+acpi_video_device_notify (
+       acpi_handle             handle,
+       u32                     event,
+       void                    *data)
+{
+       struct acpi_video_device        *video_device = (struct acpi_video_device *) data;
+       struct acpi_device      *device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_video_device_notify");
+
+       printk("video device notify\n");
+       if (!video_device)
+               return_VOID;
+
+       if (acpi_bus_get_device(handle, &device))
+               return_VOID;
+
+       switch (event) {
+       case ACPI_VIDEO_NOTIFY_SWITCH: /* change in status (cycle output device) */
+       case ACPI_VIDEO_NOTIFY_PROBE: /* change in status (output device status) */
+               acpi_bus_generate_event(device, event, 0);
+               break;
+       case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */
+       case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */
+       case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */
+       case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */
+       case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */
+               acpi_video_switch_brightness (video_device, event);
+               acpi_bus_generate_event(device, event, 0);
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Unsupported event [0x%x]\n", event));
+               break;
+       }
+       return_VOID;
+}
+
+static int
+acpi_video_bus_add (
+       struct acpi_device      *device)
+{
+       int                     result = 0;
+       acpi_status             status = 0;
+       struct acpi_video_bus   *video = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_add");
+       
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       video = kmalloc(sizeof(struct acpi_video_bus), GFP_KERNEL);
+       if (!video)
+               return_VALUE(-ENOMEM);
+       memset(video, 0, sizeof(struct acpi_video_bus));
+
+       video->handle = device->handle;
+       strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
+       strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
+       acpi_driver_data(device) = video;
+
+       acpi_video_bus_find_cap(video);
+       result = acpi_video_bus_check(video);
+       if (result)
+               goto end;
+
+       result = acpi_video_bus_add_fs(device);
+       if (result)
+               goto end;
+
+       init_MUTEX(&video->sem);
+       INIT_LIST_HEAD(&video->video_device_list);
+
+       acpi_video_bus_get_devices(video, device);
+       acpi_video_bus_start_devices(video);
+
+       status = acpi_install_notify_handler(video->handle,
+               ACPI_DEVICE_NOTIFY, acpi_video_bus_notify, video);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error installing notify handler\n"));
+               result = -ENODEV;
+               goto end;
+       }
+
+       printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s  rom: %s  post: %s)\n",
+               ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
+               video->flags.multihead ? "yes":"no",
+               video->flags.rom ? "yes":"no",
+               video->flags.post ? "yes":"no");
+
+end:
+       if (result) {
+               acpi_video_bus_remove_fs(device);
+               kfree(video);
+       }
+
+       return_VALUE(result);
+}
+
+static int
+acpi_video_bus_remove (
+       struct acpi_device      *device,
+       int                     type)
+{
+       acpi_status             status = 0;
+       struct acpi_video_bus   *video = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       video = (struct acpi_video_bus *) acpi_driver_data(device);
+
+       acpi_video_bus_stop_devices(video);
+
+       status = acpi_remove_notify_handler(video->handle,
+               ACPI_DEVICE_NOTIFY, acpi_video_bus_notify);
+       if (ACPI_FAILURE(status))
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error removing notify handler\n"));
+
+       acpi_video_bus_put_devices(video);
+       acpi_video_bus_remove_fs(device);
+
+       if (video->attached_array)
+               kfree(video->attached_array);
+       kfree(video);
+
+       return_VALUE(0);
+}
+
+
+static int
+acpi_video_bus_match (
+       struct acpi_device      *device,
+       struct acpi_driver      *driver)
+{
+       acpi_handle             h_dummy1;
+       acpi_handle             h_dummy2;
+       acpi_handle             h_dummy3;
+
+       ACPI_FUNCTION_TRACE("acpi_video_bus_match");
+
+       if (!device || !driver)
+               return_VALUE(-EINVAL);
+
+       /* Since there is no HID, CID for ACPI Video drivers, we have
+        * to check well known required nodes for each feature we support.
+        */
+
+       /* Does this device able to support video switching ? */
+       if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy1)) &&
+           ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy2)))
+               return_VALUE(0);
+
+       /* Does this device able to retrieve a video ROM ? */
+       if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy1)))
+               return_VALUE(0);
+
+       /* Does this device able to configure which video head to be POSTed ? */
+       if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy1)) &&
+           ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy2)) &&
+           ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy3)))
+               return_VALUE(0);
+
+
+       return_VALUE(-ENODEV);
+}
+
+
+static int __init
+acpi_video_init (void)
+{
+       int                     result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_video_init");
+
+       /*
+       acpi_dbg_level = 0xFFFFFFFF;
+       acpi_dbg_layer = 0x08000000;
+       */
+
+       acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
+       if (!acpi_video_dir)
+               return_VALUE(-ENODEV);
+       acpi_video_dir->owner = THIS_MODULE;
+
+       result = acpi_bus_register_driver(&acpi_video_bus);
+       if (result < 0) {
+               remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
+               return_VALUE(-ENODEV);
+       }
+
+       return_VALUE(0);
+}
+
+static void __exit
+acpi_video_exit (void)
+{
+       ACPI_FUNCTION_TRACE("acpi_video_exit");
+
+       acpi_bus_unregister_driver(&acpi_video_bus);
+
+       remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
+
+       return_VOID;
+}
+
+module_init(acpi_video_init);
+module_exit(acpi_video_exit);
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
new file mode 100644 (file)
index 0000000..ec615d8
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * attribute_container.c - implementation of a simple container for classes
+ *
+ * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
+ *
+ * This file is licensed under GPLv2
+ *
+ * The basic idea here is to enable a device to be attached to an
+ * aritrary numer of classes without having to allocate storage for them.
+ * Instead, the contained classes select the devices they need to attach
+ * to via a matching function.
+ */
+
+#include <linux/attribute_container.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+/* This is a private structure used to tie the classdev and the
+ * container .. it should never be visible outside this file */
+struct internal_container {
+       struct list_head node;
+       struct attribute_container *cont;
+       struct class_device classdev;
+};
+
+/**
+ * attribute_container_classdev_to_container - given a classdev, return the container
+ *
+ * @classdev: the class device created by attribute_container_add_device.
+ *
+ * Returns the container associated with this classdev.
+ */
+struct attribute_container *
+attribute_container_classdev_to_container(struct class_device *classdev)
+{
+       struct internal_container *ic =
+               container_of(classdev, struct internal_container, classdev);
+       return ic->cont;
+}
+EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container);
+
+static struct list_head attribute_container_list;
+
+static DECLARE_MUTEX(attribute_container_mutex);
+
+/**
+ * attribute_container_register - register an attribute container
+ *
+ * @cont: The container to register.  This must be allocated by the
+ *        callee and should also be zeroed by it.
+ */
+int
+attribute_container_register(struct attribute_container *cont)
+{
+       INIT_LIST_HEAD(&cont->node);
+       INIT_LIST_HEAD(&cont->containers);
+               
+       down(&attribute_container_mutex);
+       list_add_tail(&cont->node, &attribute_container_list);
+       up(&attribute_container_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(attribute_container_register);
+
+/**
+ * attribute_container_unregister - remove a container registration
+ *
+ * @cont: previously registered container to remove
+ */
+int
+attribute_container_unregister(struct attribute_container *cont)
+{
+       int retval = -EBUSY;
+       down(&attribute_container_mutex);
+       if (!list_empty(&cont->containers))
+               goto out;
+       retval = 0;
+       list_del(&cont->node);
+ out:
+       up(&attribute_container_mutex);
+       return retval;
+               
+}
+EXPORT_SYMBOL_GPL(attribute_container_unregister);
+
+/* private function used as class release */
+static void attribute_container_release(struct class_device *classdev)
+{
+       struct internal_container *ic 
+               = container_of(classdev, struct internal_container, classdev);
+       struct device *dev = classdev->dev;
+
+       kfree(ic);
+       put_device(dev);
+}
+
+/**
+ * attribute_container_add_device - see if any container is interested in dev
+ *
+ * @dev: device to add attributes to
+ * @fn:         function to trigger addition of class device.
+ *
+ * This function allocates storage for the class device(s) to be
+ * attached to dev (one for each matching attribute_container).  If no
+ * fn is provided, the code will simply register the class device via
+ * class_device_add.  If a function is provided, it is expected to add
+ * the class device at the appropriate time.  One of the things that
+ * might be necessary is to allocate and initialise the classdev and
+ * then add it a later time.  To do this, call this routine for
+ * allocation and initialisation and then use
+ * attribute_container_device_trigger() to call class_device_add() on
+ * it.  Note: after this, the class device contains a reference to dev
+ * which is not relinquished until the release of the classdev.
+ */
+void
+attribute_container_add_device(struct device *dev,
+                              int (*fn)(struct attribute_container *,
+                                        struct device *,
+                                        struct class_device *))
+{
+       struct attribute_container *cont;
+
+       down(&attribute_container_mutex);
+       list_for_each_entry(cont, &attribute_container_list, node) {
+               struct internal_container *ic;
+
+               if (attribute_container_no_classdevs(cont))
+                       continue;
+
+               if (!cont->match(cont, dev))
+                       continue;
+               ic = kmalloc(sizeof(struct internal_container), GFP_KERNEL);
+               if (!ic) {
+                       dev_printk(KERN_ERR, dev, "failed to allocate class container\n");
+                       continue;
+               }
+               memset(ic, 0, sizeof(struct internal_container));
+               INIT_LIST_HEAD(&ic->node);
+               ic->cont = cont;
+               class_device_initialize(&ic->classdev);
+               ic->classdev.dev = get_device(dev);
+               ic->classdev.class = cont->class;
+               cont->class->release = attribute_container_release;
+               strcpy(ic->classdev.class_id, dev->bus_id);
+               if (fn)
+                       fn(cont, dev, &ic->classdev);
+               else
+                       attribute_container_add_class_device(&ic->classdev);
+               list_add_tail(&ic->node, &cont->containers);
+       }
+       up(&attribute_container_mutex);
+}
+
+/**
+ * attribute_container_remove_device - make device eligible for removal.
+ *
+ * @dev:  The generic device
+ * @fn:          A function to call to remove the device
+ *
+ * This routine triggers device removal.  If fn is NULL, then it is
+ * simply done via class_device_unregister (note that if something
+ * still has a reference to the classdev, then the memory occupied
+ * will not be freed until the classdev is released).  If you want a
+ * two phase release: remove from visibility and then delete the
+ * device, then you should use this routine with a fn that calls
+ * class_device_del() and then use
+ * attribute_container_device_trigger() to do the final put on the
+ * classdev.
+ */
+void
+attribute_container_remove_device(struct device *dev,
+                                 void (*fn)(struct attribute_container *,
+                                            struct device *,
+                                            struct class_device *))
+{
+       struct attribute_container *cont;
+
+       down(&attribute_container_mutex);
+       list_for_each_entry(cont, &attribute_container_list, node) {
+               struct internal_container *ic, *tmp;
+
+               if (attribute_container_no_classdevs(cont))
+                       continue;
+
+               if (!cont->match(cont, dev))
+                       continue;
+               list_for_each_entry_safe(ic, tmp, &cont->containers, node) {
+                       if (dev != ic->classdev.dev)
+                               continue;
+                       list_del(&ic->node);
+                       if (fn)
+                               fn(cont, dev, &ic->classdev);
+                       else {
+                               attribute_container_remove_attrs(&ic->classdev);
+                               class_device_unregister(&ic->classdev);
+                       }
+               }
+       }
+       up(&attribute_container_mutex);
+}
+EXPORT_SYMBOL_GPL(attribute_container_remove_device);
+
+/**
+ * attribute_container_device_trigger - execute a trigger for each matching classdev
+ *
+ * @dev:  The generic device to run the trigger for
+ * @fn   the function to execute for each classdev.
+ *
+ * This funcion is for executing a trigger when you need to know both
+ * the container and the classdev.  If you only care about the
+ * container, then use attribute_container_trigger() instead.
+ */
+void
+attribute_container_device_trigger(struct device *dev, 
+                                  int (*fn)(struct attribute_container *,
+                                            struct device *,
+                                            struct class_device *))
+{
+       struct attribute_container *cont;
+
+       down(&attribute_container_mutex);
+       list_for_each_entry(cont, &attribute_container_list, node) {
+               struct internal_container *ic, *tmp;
+
+               if (!cont->match(cont, dev))
+                       continue;
+
+               list_for_each_entry_safe(ic, tmp, &cont->containers, node) {
+                       if (dev == ic->classdev.dev)
+                               fn(cont, dev, &ic->classdev);
+               }
+       }
+       up(&attribute_container_mutex);
+}
+EXPORT_SYMBOL_GPL(attribute_container_device_trigger);
+
+/**
+ * attribute_container_trigger - trigger a function for each matching container
+ *
+ * @dev:  The generic device to activate the trigger for
+ * @fn:          the function to trigger
+ *
+ * This routine triggers a function that only needs to know the
+ * matching containers (not the classdev) associated with a device.
+ * It is more lightweight than attribute_container_device_trigger, so
+ * should be used in preference unless the triggering function
+ * actually needs to know the classdev.
+ */
+void
+attribute_container_trigger(struct device *dev,
+                           int (*fn)(struct attribute_container *,
+                                     struct device *))
+{
+       struct attribute_container *cont;
+
+       down(&attribute_container_mutex);
+       list_for_each_entry(cont, &attribute_container_list, node) {
+               if (cont->match(cont, dev))
+                       fn(cont, dev);
+       }
+       up(&attribute_container_mutex);
+}
+EXPORT_SYMBOL_GPL(attribute_container_trigger);
+
+/**
+ * attribute_container_add_attrs - add attributes
+ *
+ * @classdev: The class device
+ *
+ * This simply creates all the class device sysfs files from the
+ * attributes listed in the container
+ */
+int
+attribute_container_add_attrs(struct class_device *classdev)
+{
+       struct attribute_container *cont =
+               attribute_container_classdev_to_container(classdev);
+       struct class_device_attribute **attrs = cont->attrs;
+       int i, error;
+
+       if (!attrs)
+               return 0;
+
+       for (i = 0; attrs[i]; i++) {
+               error = class_device_create_file(classdev, attrs[i]);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(attribute_container_add_attrs);
+
+/**
+ * attribute_container_add_class_device - same function as class_device_add
+ *
+ * @classdev:  the class device to add
+ *
+ * This performs essentially the same function as class_device_add except for
+ * attribute containers, namely add the classdev to the system and then
+ * create the attribute files
+ */
+int
+attribute_container_add_class_device(struct class_device *classdev)
+{
+       int error = class_device_add(classdev);
+       if (error)
+               return error;
+       return attribute_container_add_attrs(classdev);
+}
+EXPORT_SYMBOL_GPL(attribute_container_add_class_device);
+
+/**
+ * attribute_container_add_class_device_adapter - simple adapter for triggers
+ *
+ * This function is identical to attribute_container_add_class_device except
+ * that it is designed to be called from the triggers
+ */
+int
+attribute_container_add_class_device_adapter(struct attribute_container *cont,
+                                            struct device *dev,
+                                            struct class_device *classdev)
+{
+       return attribute_container_add_class_device(classdev);
+}
+EXPORT_SYMBOL_GPL(attribute_container_add_class_device_adapter);
+
+/**
+ * attribute_container_remove_attrs - remove any attribute files
+ *
+ * @classdev: The class device to remove the files from
+ *
+ */
+void
+attribute_container_remove_attrs(struct class_device *classdev)
+{
+       struct attribute_container *cont =
+               attribute_container_classdev_to_container(classdev);
+       struct class_device_attribute **attrs = cont->attrs;
+       int i;
+
+       if (!attrs)
+               return;
+
+       for (i = 0; attrs[i]; i++)
+               class_device_remove_file(classdev, attrs[i]);
+}
+EXPORT_SYMBOL_GPL(attribute_container_remove_attrs);
+
+/**
+ * attribute_container_class_device_del - equivalent of class_device_del
+ *
+ * @classdev: the class device
+ *
+ * This function simply removes all the attribute files and then calls
+ * class_device_del.
+ */
+void
+attribute_container_class_device_del(struct class_device *classdev)
+{
+       attribute_container_remove_attrs(classdev);
+       class_device_del(classdev);
+}
+EXPORT_SYMBOL_GPL(attribute_container_class_device_del);
+
+int __init
+attribute_container_init(void)
+{
+       INIT_LIST_HEAD(&attribute_container_list);
+       return 0;
+}
diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
new file mode 100644 (file)
index 0000000..3bae11c
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * transport_class.c - implementation of generic transport classes
+ *                     using attribute_containers
+ *
+ * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
+ *
+ * This file is licensed under GPLv2
+ *
+ * The basic idea here is to allow any "device controller" (which
+ * would most often be a Host Bus Adapter" to use the services of one
+ * or more tranport classes for performing transport specific
+ * services.  Transport specific services are things that the generic
+ * command layer doesn't want to know about (speed settings, line
+ * condidtioning, etc), but which the user might be interested in.
+ * Thus, the HBA's use the routines exported by the transport classes
+ * to perform these functions.  The transport classes export certain
+ * values to the user via sysfs using attribute containers.
+ *
+ * Note: because not every HBA will care about every transport
+ * attribute, there's a many to one relationship that goes like this:
+ *
+ * transport class<-----attribute container<----class device
+ *
+ * Usually the attribute container is per-HBA, but the design doesn't
+ * mandate that.  Although most of the services will be specific to
+ * the actual external storage connection used by the HBA, the generic
+ * transport class is framed entirely in terms of generic devices to
+ * allow it to be used by any physical HBA in the system.
+ */
+#include <linux/attribute_container.h>
+#include <linux/transport_class.h>
+
+/**
+ * transport_class_register - register an initial transport class
+ *
+ * @tclass:    a pointer to the transport class structure to be initialised
+ *
+ * The transport class contains an embedded class which is used to
+ * identify it.  The caller should initialise this structure with
+ * zeros and then generic class must have been initialised with the
+ * actual transport class unique name.  There's a macro
+ * DECLARE_TRANSPORT_CLASS() to do this (declared classes still must
+ * be registered).
+ *
+ * Returns 0 on success or error on failure.
+ */
+int transport_class_register(struct transport_class *tclass)
+{
+       return class_register(&tclass->class);
+}
+EXPORT_SYMBOL_GPL(transport_class_register);
+
+/**
+ * transport_class_unregister - unregister a previously registered class
+ *
+ * @tclass: The transport class to unregister
+ *
+ * Must be called prior to deallocating the memory for the transport
+ * class.
+ */
+void transport_class_unregister(struct transport_class *tclass)
+{
+       class_unregister(&tclass->class);
+}
+EXPORT_SYMBOL_GPL(transport_class_unregister);
+
+static int anon_transport_dummy_function(struct device *dev)
+{
+       /* do nothing */
+       return 0;
+}
+
+/**
+ * anon_transport_class_register - register an anonymous class
+ *
+ * @atc: The anon transport class to register
+ *
+ * The anonymous transport class contains both a transport class and a
+ * container.  The idea of an anonymous class is that it never
+ * actually has any device attributes associated with it (and thus
+ * saves on container storage).  So it can only be used for triggering
+ * events.  Use prezero and then use DECLARE_ANON_TRANSPORT_CLASS() to
+ * initialise the anon transport class storage.
+ */
+int anon_transport_class_register(struct anon_transport_class *atc)
+{
+       int error;
+       atc->container.class = &atc->tclass.class;
+       attribute_container_set_no_classdevs(&atc->container);
+       error = attribute_container_register(&atc->container);
+       if (error)
+               return error;
+       atc->tclass.setup = anon_transport_dummy_function;
+       atc->tclass.remove = anon_transport_dummy_function;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(anon_transport_class_register);
+
+/**
+ * anon_transport_class_unregister - unregister an anon class
+ *
+ * @atc: Pointer to the anon transport class to unregister
+ *
+ * Must be called prior to deallocating the memory for the anon
+ * transport class.
+ */
+void anon_transport_class_unregister(struct anon_transport_class *atc)
+{
+       attribute_container_unregister(&atc->container);
+}
+EXPORT_SYMBOL_GPL(anon_transport_class_unregister);
+
+static int transport_setup_classdev(struct attribute_container *cont,
+                                   struct device *dev,
+                                   struct class_device *classdev)
+{
+       struct transport_class *tclass = class_to_transport_class(cont->class);
+
+       if (tclass->setup)
+               tclass->setup(dev);
+
+       return 0;
+}
+
+/**
+ * transport_setup_device - declare a new dev for transport class association
+ *                         but don't make it visible yet.
+ *
+ * @dev: the generic device representing the entity being added
+ *
+ * Usually, dev represents some component in the HBA system (either
+ * the HBA itself or a device remote across the HBA bus).  This
+ * routine is simply a trigger point to see if any set of transport
+ * classes wishes to associate with the added device.  This allocates
+ * storage for the class device and initialises it, but does not yet
+ * add it to the system or add attributes to it (you do this with
+ * transport_add_device).  If you have no need for a separate setup
+ * and add operations, use transport_register_device (see
+ * transport_class.h).
+ */
+
+void transport_setup_device(struct device *dev)
+{
+       attribute_container_add_device(dev, transport_setup_classdev);
+}
+EXPORT_SYMBOL_GPL(transport_setup_device);
+
+
+/**
+ * transport_add_device - declare a new dev for transport class association
+ *
+ * @dev: the generic device representing the entity being added
+ *
+ * Usually, dev represents some component in the HBA system (either
+ * the HBA itself or a device remote across the HBA bus).  This
+ * routine is simply a trigger point used to add the device to the
+ * system and register attributes for it.
+ */
+
+void transport_add_device(struct device *dev)
+{
+       attribute_container_device_trigger(dev,
+                          attribute_container_add_class_device_adapter);
+}
+EXPORT_SYMBOL_GPL(transport_add_device);
+
+static int transport_configure(struct attribute_container *cont,
+                              struct device *dev)
+{
+       struct transport_class *tclass = class_to_transport_class(cont->class);
+
+       if (tclass->configure)
+               tclass->configure(dev);
+
+       return 0;
+}
+
+/**
+ * transport_configure_device - configure an already set up device
+ *
+ * @dev: generic device representing device to be configured
+ *
+ * The idea of configure is simply to provide a point within the setup
+ * process to allow the transport class to extract information from a
+ * device after it has been setup.  This is used in SCSI because we
+ * have to have a setup device to begin using the HBA, but after we
+ * send the initial inquiry, we use configure to extract the device
+ * parameters.  The device need not have been added to be configured.
+ */
+void transport_configure_device(struct device *dev)
+{
+       attribute_container_trigger(dev, transport_configure);
+}
+EXPORT_SYMBOL_GPL(transport_configure_device);
+
+static int transport_remove_classdev(struct attribute_container *cont,
+                                    struct device *dev,
+                                    struct class_device *classdev)
+{
+       struct transport_class *tclass = class_to_transport_class(cont->class);
+
+       if (tclass->remove)
+               tclass->remove(dev);
+
+       if (tclass->remove != anon_transport_dummy_function)
+               attribute_container_class_device_del(classdev);
+
+       return 0;
+}
+
+
+/**
+ * transport_remove_device - remove the visibility of a device
+ *
+ * @dev: generic device to remove
+ *
+ * This call removes the visibility of the device (to the user from
+ * sysfs), but does not destroy it.  To eliminate a device entirely
+ * you must also call transport_destroy_device.  If you don't need to
+ * do remove and destroy as separate operations, use
+ * transport_unregister_device() (see transport_class.h) which will
+ * perform both calls for you.
+ */
+void transport_remove_device(struct device *dev)
+{
+       attribute_container_device_trigger(dev, transport_remove_classdev);
+}
+EXPORT_SYMBOL_GPL(transport_remove_device);
+
+static void transport_destroy_classdev(struct attribute_container *cont,
+                                     struct device *dev,
+                                     struct class_device *classdev)
+{
+       struct transport_class *tclass = class_to_transport_class(cont->class);
+
+       if (tclass->remove != anon_transport_dummy_function)
+               class_device_put(classdev);
+}
+
+
+/**
+ * transport_destroy_device - destroy a removed device
+ *
+ * @dev: device to eliminate from the transport class.
+ *
+ * This call triggers the elimination of storage associated with the
+ * transport classdev.  Note: all it really does is relinquish a
+ * reference to the classdev.  The memory will not be freed until the
+ * last reference goes to zero.  Note also that the classdev retains a
+ * reference count on dev, so dev too will remain for as long as the
+ * transport class device remains around.
+ */
+void transport_destroy_device(struct device *dev)
+{
+       attribute_container_remove_device(dev, transport_destroy_classdev);
+}
+EXPORT_SYMBOL_GPL(transport_destroy_device);
diff --git a/drivers/block/aoe/Makefile b/drivers/block/aoe/Makefile
new file mode 100644 (file)
index 0000000..e76d997
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for ATA over Ethernet
+#
+
+obj-$(CONFIG_ATA_OVER_ETH)     += aoe.o
+aoe-objs := aoeblk.o aoechr.o aoecmd.o aoedev.o aoemain.o aoenet.o
diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h
new file mode 100644 (file)
index 0000000..2f4eafa
--- /dev/null
@@ -0,0 +1,166 @@
+/* Copyright (c) 2004 Coraid, Inc.  See COPYING for GPL terms. */
+#define VERSION "5"
+#define AOE_MAJOR 152
+#define DEVICE_NAME "aoe"
+#ifndef AOE_PARTITIONS
+#define AOE_PARTITIONS 16
+#endif
+#define SYSMINOR(aoemajor, aoeminor) ((aoemajor) * 10 + (aoeminor))
+#define AOEMAJOR(sysminor) ((sysminor) / 10)
+#define AOEMINOR(sysminor) ((sysminor) % 10)
+#define WHITESPACE " \t\v\f\n"
+
+enum {
+       AOECMD_ATA,
+       AOECMD_CFG,
+
+       AOEFL_RSP = (1<<3),
+       AOEFL_ERR = (1<<2),
+
+       AOEAFL_EXT = (1<<6),
+       AOEAFL_DEV = (1<<4),
+       AOEAFL_ASYNC = (1<<1),
+       AOEAFL_WRITE = (1<<0),
+
+       AOECCMD_READ = 0,
+       AOECCMD_TEST,
+       AOECCMD_PTEST,
+       AOECCMD_SET,
+       AOECCMD_FSET,
+
+       AOE_HVER = 0x10,
+};
+
+struct aoe_hdr {
+       unsigned char dst[6];
+       unsigned char src[6];
+       unsigned char type[2];
+       unsigned char verfl;
+       unsigned char err;
+       unsigned char major[2];
+       unsigned char minor;
+       unsigned char cmd;
+       unsigned char tag[4];
+};
+
+struct aoe_atahdr {
+       unsigned char aflags;
+       unsigned char errfeat;
+       unsigned char scnt;
+       unsigned char cmdstat;
+       unsigned char lba0;
+       unsigned char lba1;
+       unsigned char lba2;
+       unsigned char lba3;
+       unsigned char lba4;
+       unsigned char lba5;
+       unsigned char res[2];
+};
+
+struct aoe_cfghdr {
+       unsigned char bufcnt[2];
+       unsigned char fwver[2];
+       unsigned char res;
+       unsigned char aoeccmd;
+       unsigned char cslen[2];
+};
+
+enum {
+       DEVFL_UP = 1,   /* device is installed in system and ready for AoE->ATA commands */
+       DEVFL_TKILL = (1<<1),   /* flag for timer to know when to kill self */
+       DEVFL_EXT = (1<<2),     /* device accepts lba48 commands */
+       DEVFL_CLOSEWAIT = (1<<3), /* device is waiting for all closes to revalidate */
+       DEVFL_WC_UPDATE = (1<<4), /* this device needs to update write cache status */
+       DEVFL_WORKON = (1<<4),
+
+       BUFFL_FAIL = 1,
+};
+
+enum {
+       MAXATADATA = 1024,
+       NPERSHELF = 10,
+       FREETAG = -1,
+       MIN_BUFS = 8,
+};
+
+struct buf {
+       struct list_head bufs;
+       ulong flags;
+       ulong nframesout;
+       char *bufaddr;
+       ulong resid;
+       ulong bv_resid;
+       sector_t sector;
+       struct bio *bio;
+       struct bio_vec *bv;
+};
+
+struct frame {
+       int tag;
+       ulong waited;
+       struct buf *buf;
+       char *bufaddr;
+       int writedatalen;
+       int ndata;
+
+       /* largest possible */
+       unsigned char data[sizeof(struct aoe_hdr) + sizeof(struct aoe_atahdr)];
+};
+
+struct aoedev {
+       struct aoedev *next;
+       unsigned char addr[6];  /* remote mac addr */
+       ushort flags;
+       ulong sysminor;
+       ulong aoemajor;
+       ulong aoeminor;
+       ulong nopen;            /* (bd_openers isn't available without sleeping) */
+       ulong rttavg;           /* round trip average of requests/responses */
+       u16 fw_ver;             /* version of blade's firmware */
+       struct work_struct work;/* disk create work struct */
+       struct gendisk *gd;
+       request_queue_t blkq;
+       struct hd_geometry geo; 
+       sector_t ssize;
+       struct timer_list timer;
+       spinlock_t lock;
+       struct net_device *ifp; /* interface ed is attached to */
+       struct sk_buff *skblist;/* packets needing to be sent */
+       mempool_t *bufpool;     /* for deadlock-free Buf allocation */
+       struct list_head bufq;  /* queue of bios to work on */
+       struct buf *inprocess;  /* the one we're currently working on */
+       ulong lasttag;          /* last tag sent */
+       ulong nframes;          /* number of frames below */
+       struct frame *frames;
+};
+
+
+int aoeblk_init(void);
+void aoeblk_exit(void);
+void aoeblk_gdalloc(void *);
+void aoedisk_rm_sysfs(struct aoedev *d);
+
+int aoechr_init(void);
+void aoechr_exit(void);
+void aoechr_error(char *);
+void aoechr_hdump(char *, int len);
+
+void aoecmd_work(struct aoedev *d);
+void aoecmd_cfg(ushort, unsigned char);
+void aoecmd_ata_rsp(struct sk_buff *);
+void aoecmd_cfg_rsp(struct sk_buff *);
+
+int aoedev_init(void);
+void aoedev_exit(void);
+struct aoedev *aoedev_bymac(unsigned char *);
+void aoedev_downdev(struct aoedev *d);
+struct aoedev *aoedev_set(ulong, unsigned char *, struct net_device *, ulong);
+int aoedev_busy(void);
+
+int aoenet_init(void);
+void aoenet_exit(void);
+void aoenet_xmit(struct sk_buff *);
+int is_aoe_netif(struct net_device *ifp);
+int set_aoe_iflist(const char __user *str, size_t size);
+
+u64 mac_addr(char addr[6]);
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
new file mode 100644 (file)
index 0000000..7736a44
--- /dev/null
@@ -0,0 +1,265 @@
+/* Copyright (c) 2004 Coraid, Inc.  See COPYING for GPL terms. */
+/*
+ * aoeblk.c
+ * block device routines
+ */
+
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/genhd.h>
+#include <linux/netdevice.h>
+#include "aoe.h"
+
+static kmem_cache_t *buf_pool_cache;
+
+/* add attributes for our block devices in sysfs */
+static ssize_t aoedisk_show_state(struct gendisk * disk, char *page)
+{
+       struct aoedev *d = disk->private_data;
+
+       return snprintf(page, PAGE_SIZE,
+                       "%s%s\n",
+                       (d->flags & DEVFL_UP) ? "up" : "down",
+                       (d->flags & DEVFL_CLOSEWAIT) ? ",closewait" : "");
+}
+static ssize_t aoedisk_show_mac(struct gendisk * disk, char *page)
+{
+       struct aoedev *d = disk->private_data;
+
+       return snprintf(page, PAGE_SIZE, "%012llx\n", mac_addr(d->addr));
+}
+static ssize_t aoedisk_show_netif(struct gendisk * disk, char *page)
+{
+       struct aoedev *d = disk->private_data;
+
+       return snprintf(page, PAGE_SIZE, "%s\n", d->ifp->name);
+}
+
+static struct disk_attribute disk_attr_state = {
+       .attr = {.name = "state", .mode = S_IRUGO },
+       .show = aoedisk_show_state
+};
+static struct disk_attribute disk_attr_mac = {
+       .attr = {.name = "mac", .mode = S_IRUGO },
+       .show = aoedisk_show_mac
+};
+static struct disk_attribute disk_attr_netif = {
+       .attr = {.name = "netif", .mode = S_IRUGO },
+       .show = aoedisk_show_netif
+};
+
+static void
+aoedisk_add_sysfs(struct aoedev *d)
+{
+       sysfs_create_file(&d->gd->kobj, &disk_attr_state.attr);
+       sysfs_create_file(&d->gd->kobj, &disk_attr_mac.attr);
+       sysfs_create_file(&d->gd->kobj, &disk_attr_netif.attr);
+}
+void
+aoedisk_rm_sysfs(struct aoedev *d)
+{
+       sysfs_remove_link(&d->gd->kobj, "state");
+       sysfs_remove_link(&d->gd->kobj, "mac");
+       sysfs_remove_link(&d->gd->kobj, "netif");
+}
+
+static int
+aoeblk_open(struct inode *inode, struct file *filp)
+{
+       struct aoedev *d;
+       ulong flags;
+
+       d = inode->i_bdev->bd_disk->private_data;
+
+       spin_lock_irqsave(&d->lock, flags);
+       if (d->flags & DEVFL_UP) {
+               d->nopen++;
+               spin_unlock_irqrestore(&d->lock, flags);
+               return 0;
+       }
+       spin_unlock_irqrestore(&d->lock, flags);
+       return -ENODEV;
+}
+
+static int
+aoeblk_release(struct inode *inode, struct file *filp)
+{
+       struct aoedev *d;
+       ulong flags;
+
+       d = inode->i_bdev->bd_disk->private_data;
+
+       spin_lock_irqsave(&d->lock, flags);
+
+       if (--d->nopen == 0 && (d->flags & DEVFL_CLOSEWAIT)) {
+               d->flags &= ~DEVFL_CLOSEWAIT;
+               spin_unlock_irqrestore(&d->lock, flags);
+               aoecmd_cfg(d->aoemajor, d->aoeminor);
+               return 0;
+       }
+       spin_unlock_irqrestore(&d->lock, flags);
+
+       return 0;
+}
+
+static int
+aoeblk_make_request(request_queue_t *q, struct bio *bio)
+{
+       struct aoedev *d;
+       struct buf *buf;
+       struct sk_buff *sl;
+       ulong flags;
+
+       blk_queue_bounce(q, &bio);
+
+       d = bio->bi_bdev->bd_disk->private_data;
+       buf = mempool_alloc(d->bufpool, GFP_NOIO);
+       if (buf == NULL) {
+               printk(KERN_INFO "aoe: aoeblk_make_request: buf allocation "
+                       "failure\n");
+               bio_endio(bio, bio->bi_size, -ENOMEM);
+               return 0;
+       }
+       memset(buf, 0, sizeof(*buf));
+       INIT_LIST_HEAD(&buf->bufs);
+       buf->bio = bio;
+       buf->resid = bio->bi_size;
+       buf->sector = bio->bi_sector;
+       buf->bv = buf->bio->bi_io_vec;
+       buf->bv_resid = buf->bv->bv_len;
+       buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
+
+       spin_lock_irqsave(&d->lock, flags);
+
+       if ((d->flags & DEVFL_UP) == 0) {
+               printk(KERN_INFO "aoe: aoeblk_make_request: device %ld.%ld is not up\n",
+                       d->aoemajor, d->aoeminor);
+               spin_unlock_irqrestore(&d->lock, flags);
+               mempool_free(buf, d->bufpool);
+               bio_endio(bio, bio->bi_size, -ENXIO);
+               return 0;
+       }
+
+       list_add_tail(&buf->bufs, &d->bufq);
+       aoecmd_work(d);
+
+       sl = d->skblist;
+       d->skblist = NULL;
+
+       spin_unlock_irqrestore(&d->lock, flags);
+
+       aoenet_xmit(sl);
+       return 0;
+}
+
+/* This ioctl implementation expects userland to have the device node
+ * permissions set so that only priviledged users can open an aoe
+ * block device directly.
+ */
+static int
+aoeblk_ioctl(struct inode *inode, struct file *filp, uint cmd, ulong arg)
+{
+       struct aoedev *d;
+
+       if (!arg)
+               return -EINVAL;
+
+       d = inode->i_bdev->bd_disk->private_data;
+       if ((d->flags & DEVFL_UP) == 0) {
+               printk(KERN_ERR "aoe: aoeblk_ioctl: disk not up\n");
+               return -ENODEV;
+       }
+
+       if (cmd == HDIO_GETGEO) {
+               d->geo.start = get_start_sect(inode->i_bdev);
+               if (!copy_to_user((void __user *) arg, &d->geo, sizeof d->geo))
+                       return 0;
+               return -EFAULT;
+       }
+       printk(KERN_INFO "aoe: aoeblk_ioctl: unknown ioctl %d\n", cmd);
+       return -EINVAL;
+}
+
+static struct block_device_operations aoe_bdops = {
+       .open = aoeblk_open,
+       .release = aoeblk_release,
+       .ioctl = aoeblk_ioctl,
+       .owner = THIS_MODULE,
+};
+
+/* alloc_disk and add_disk can sleep */
+void
+aoeblk_gdalloc(void *vp)
+{
+       struct aoedev *d = vp;
+       struct gendisk *gd;
+       ulong flags;
+
+       gd = alloc_disk(AOE_PARTITIONS);
+       if (gd == NULL) {
+               printk(KERN_ERR "aoe: aoeblk_gdalloc: cannot allocate disk "
+                       "structure for %ld.%ld\n", d->aoemajor, d->aoeminor);
+               spin_lock_irqsave(&d->lock, flags);
+               d->flags &= ~DEVFL_WORKON;
+               spin_unlock_irqrestore(&d->lock, flags);
+               return;
+       }
+
+       d->bufpool = mempool_create(MIN_BUFS,
+                                   mempool_alloc_slab, mempool_free_slab,
+                                   buf_pool_cache);
+       if (d->bufpool == NULL) {
+               printk(KERN_ERR "aoe: aoeblk_gdalloc: cannot allocate bufpool "
+                       "for %ld.%ld\n", d->aoemajor, d->aoeminor);
+               put_disk(gd);
+               spin_lock_irqsave(&d->lock, flags);
+               d->flags &= ~DEVFL_WORKON;
+               spin_unlock_irqrestore(&d->lock, flags);
+               return;
+       }
+
+       spin_lock_irqsave(&d->lock, flags);
+       blk_queue_make_request(&d->blkq, aoeblk_make_request);
+       gd->major = AOE_MAJOR;
+       gd->first_minor = d->sysminor * AOE_PARTITIONS;
+       gd->fops = &aoe_bdops;
+       gd->private_data = d;
+       gd->capacity = d->ssize;
+       snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%ld",
+               d->aoemajor, d->aoeminor);
+
+       gd->queue = &d->blkq;
+       d->gd = gd;
+       d->flags &= ~DEVFL_WORKON;
+       d->flags |= DEVFL_UP;
+
+       spin_unlock_irqrestore(&d->lock, flags);
+
+       add_disk(gd);
+       aoedisk_add_sysfs(d);
+       
+       printk(KERN_INFO "aoe: %012llx e%lu.%lu v%04x has %llu "
+               "sectors\n", mac_addr(d->addr), d->aoemajor, d->aoeminor,
+               d->fw_ver, (long long)d->ssize);
+}
+
+void
+aoeblk_exit(void)
+{
+       kmem_cache_destroy(buf_pool_cache);
+}
+
+int __init
+aoeblk_init(void)
+{
+       buf_pool_cache = kmem_cache_create("aoe_bufs", 
+                                          sizeof(struct buf),
+                                          0, 0, NULL, NULL);
+       if (buf_pool_cache == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+
diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c
new file mode 100644 (file)
index 0000000..8155a4a
--- /dev/null
@@ -0,0 +1,279 @@
+/* Copyright (c) 2004 Coraid, Inc.  See COPYING for GPL terms. */
+/*
+ * aoechr.c
+ * AoE character device driver
+ */
+
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+#include "aoe.h"
+
+enum {
+       //MINOR_STAT = 1, (moved to sysfs)
+       MINOR_ERR = 2,
+       MINOR_DISCOVER,
+       MINOR_INTERFACES,
+       MSGSZ = 2048,
+       NARGS = 10,
+       NMSG = 100,             /* message backlog to retain */
+};
+
+struct aoe_chardev {
+       ulong minor;
+       char name[32];
+};
+
+enum { EMFL_VALID = 1 };
+
+struct ErrMsg {
+       short flags;
+       short len;
+       char *msg;
+};
+
+static struct ErrMsg emsgs[NMSG];
+static int emsgs_head_idx, emsgs_tail_idx;
+static struct semaphore emsgs_sema;
+static spinlock_t emsgs_lock;
+static int nblocked_emsgs_readers;
+static struct class_simple *aoe_class;
+static struct aoe_chardev chardevs[] = {
+       { MINOR_ERR, "err" },
+       { MINOR_DISCOVER, "discover" },
+       { MINOR_INTERFACES, "interfaces" },
+};
+
+static int
+discover(void)
+{
+       aoecmd_cfg(0xffff, 0xff);
+       return 0;
+}
+
+static int
+interfaces(const char __user *str, size_t size)
+{
+       if (set_aoe_iflist(str, size)) {
+               printk(KERN_CRIT
+                      "%s: could not set interface list: %s\n",
+                      __FUNCTION__, "too many interfaces");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+void
+aoechr_error(char *msg)
+{
+       struct ErrMsg *em;
+       char *mp;
+       ulong flags, n;
+
+       n = strlen(msg);
+
+       spin_lock_irqsave(&emsgs_lock, flags);
+
+       em = emsgs + emsgs_tail_idx;
+       if ((em->flags & EMFL_VALID)) {
+bail:          spin_unlock_irqrestore(&emsgs_lock, flags);
+               return;
+       }
+
+       mp = kmalloc(n, GFP_ATOMIC);
+       if (mp == NULL) {
+               printk(KERN_CRIT "aoe: aoechr_error: allocation failure, len=%ld\n", n);
+               goto bail;
+       }
+
+       memcpy(mp, msg, n);
+       em->msg = mp;
+       em->flags |= EMFL_VALID;
+       em->len = n;
+
+       emsgs_tail_idx++;
+       emsgs_tail_idx %= ARRAY_SIZE(emsgs);
+
+       spin_unlock_irqrestore(&emsgs_lock, flags);
+
+       if (nblocked_emsgs_readers)
+               up(&emsgs_sema);
+}
+
+#define PERLINE 16
+void
+aoechr_hdump(char *buf, int n)
+{
+       int bufsiz;
+       char *fbuf;
+       int linelen;
+       char *p, *e, *fp;
+
+       bufsiz = n * 3;                 /* 2 hex digits and a space */
+       bufsiz += n / PERLINE + 1;      /* the newline characters */
+       bufsiz += 1;                    /* the final '\0' */
+
+       fbuf = kmalloc(bufsiz, GFP_ATOMIC);
+       if (!fbuf) {
+               printk(KERN_INFO
+                      "%s: cannot allocate memory\n",
+                      __FUNCTION__);
+               return;
+       }
+       
+       for (p = buf; n <= 0;) {
+               linelen = n > PERLINE ? PERLINE : n;
+               n -= linelen;
+
+               fp = fbuf;
+               for (e=p+linelen; p<e; p++)
+                       fp += sprintf(fp, "%2.2X ", *p & 255);
+               sprintf(fp, "\n");
+               aoechr_error(fbuf);
+       }
+
+       kfree(fbuf);
+}
+
+static ssize_t
+aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp)
+{
+       int ret = -EINVAL;
+
+       switch ((unsigned long) filp->private_data) {
+       default:
+               printk(KERN_INFO "aoe: aoechr_write: can't write to that file.\n");
+               break;
+       case MINOR_DISCOVER:
+               ret = discover();
+               break;
+       case MINOR_INTERFACES:
+               ret = interfaces(buf, cnt);
+               break;
+       }
+       if (ret == 0)
+               ret = cnt;
+       return ret;
+}
+
+static int
+aoechr_open(struct inode *inode, struct file *filp)
+{
+       int n, i;
+
+       n = MINOR(inode->i_rdev);
+       filp->private_data = (void *) (unsigned long) n;
+
+       for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
+               if (chardevs[i].minor == n)
+                       return 0;
+       return -EINVAL;
+}
+
+static int
+aoechr_rel(struct inode *inode, struct file *filp)
+{
+       return 0;
+}
+
+static ssize_t
+aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
+{
+       int n;
+       char *mp;
+       struct ErrMsg *em;
+       ssize_t len;
+       ulong flags;
+
+       n = (int) filp->private_data;
+       switch (n) {
+       case MINOR_ERR:
+               spin_lock_irqsave(&emsgs_lock, flags);
+loop:
+               em = emsgs + emsgs_head_idx;
+               if ((em->flags & EMFL_VALID) == 0) {
+                       if (filp->f_flags & O_NDELAY) {
+                               spin_unlock_irqrestore(&emsgs_lock, flags);
+                               return -EAGAIN;
+                       }
+                       nblocked_emsgs_readers++;
+
+                       spin_unlock_irqrestore(&emsgs_lock, flags);
+
+                       n = down_interruptible(&emsgs_sema);
+
+                       spin_lock_irqsave(&emsgs_lock, flags);
+
+                       nblocked_emsgs_readers--;
+
+                       if (n) {
+                               spin_unlock_irqrestore(&emsgs_lock, flags);
+                               return -ERESTARTSYS;
+                       }
+                       goto loop;
+               }
+               if (em->len > cnt) {
+                       spin_unlock_irqrestore(&emsgs_lock, flags);
+                       return -EAGAIN;
+               }
+               mp = em->msg;
+               len = em->len;
+               em->msg = NULL;
+               em->flags &= ~EMFL_VALID;
+
+               emsgs_head_idx++;
+               emsgs_head_idx %= ARRAY_SIZE(emsgs);
+
+               spin_unlock_irqrestore(&emsgs_lock, flags);
+
+               n = copy_to_user(buf, mp, len);
+               kfree(mp);
+               return n == 0 ? len : -EFAULT;
+       default:
+               return -EFAULT;
+       }
+}
+
+struct file_operations aoe_fops = {
+       .write = aoechr_write,
+       .read = aoechr_read,
+       .open = aoechr_open,
+       .release = aoechr_rel,
+       .owner = THIS_MODULE,
+};
+
+int __init
+aoechr_init(void)
+{
+       int n, i;
+
+       n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
+       if (n < 0) { 
+               printk(KERN_ERR "aoe: aoechr_init: can't register char device\n");
+               return n;
+       }
+       sema_init(&emsgs_sema, 0);
+       spin_lock_init(&emsgs_lock);
+       aoe_class = class_simple_create(THIS_MODULE, "aoe");
+       if (IS_ERR(aoe_class)) {
+               unregister_chrdev(AOE_MAJOR, "aoechr");
+               return PTR_ERR(aoe_class);
+       }
+       for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
+               class_simple_device_add(aoe_class,
+                                       MKDEV(AOE_MAJOR, chardevs[i].minor),
+                                       NULL, chardevs[i].name);
+
+       return 0;
+}
+
+void
+aoechr_exit(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
+               class_simple_device_remove(MKDEV(AOE_MAJOR, chardevs[i].minor));
+       class_simple_destroy(aoe_class);
+       unregister_chrdev(AOE_MAJOR, "aoechr");
+}
+
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
new file mode 100644 (file)
index 0000000..9ac1a58
--- /dev/null
@@ -0,0 +1,627 @@
+/* Copyright (c) 2004 Coraid, Inc.  See COPYING for GPL terms. */
+/*
+ * aoecmd.c
+ * Filesystem request handling methods
+ */
+
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include "aoe.h"
+
+#define TIMERTICK (HZ / 10)
+#define MINTIMER (2 * TIMERTICK)
+#define MAXTIMER (HZ << 1)
+#define MAXWAIT (60 * 3)       /* After MAXWAIT seconds, give up and fail dev */
+
+static struct sk_buff *
+new_skb(struct net_device *if_dev, ulong len)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(len, GFP_ATOMIC);
+       if (skb) {
+               skb->nh.raw = skb->mac.raw = skb->data;
+               skb->dev = if_dev;
+               skb->protocol = __constant_htons(ETH_P_AOE);
+               skb->priority = 0;
+               skb_put(skb, len);
+               skb->next = skb->prev = NULL;
+
+               /* tell the network layer not to perform IP checksums
+                * or to get the NIC to do it
+                */
+               skb->ip_summed = CHECKSUM_NONE;
+       }
+       return skb;
+}
+
+static struct sk_buff *
+skb_prepare(struct aoedev *d, struct frame *f)
+{
+       struct sk_buff *skb;
+       char *p;
+
+       skb = new_skb(d->ifp, f->ndata + f->writedatalen);
+       if (!skb) {
+               printk(KERN_INFO "aoe: skb_prepare: failure to allocate skb\n");
+               return NULL;
+       }
+
+       p = skb->mac.raw;
+       memcpy(p, f->data, f->ndata);
+
+       if (f->writedatalen) {
+               p += sizeof(struct aoe_hdr) + sizeof(struct aoe_atahdr);
+               memcpy(p, f->bufaddr, f->writedatalen);
+       }
+
+       return skb;
+}
+
+static struct frame *
+getframe(struct aoedev *d, int tag)
+{
+       struct frame *f, *e;
+
+       f = d->frames;
+       e = f + d->nframes;
+       for (; f<e; f++)
+               if (f->tag == tag)
+                       return f;
+       return NULL;
+}
+
+/*
+ * Leave the top bit clear so we have tagspace for userland.
+ * The bottom 16 bits are the xmit tick for rexmit/rttavg processing.
+ * This driver reserves tag -1 to mean "unused frame."
+ */
+static int
+newtag(struct aoedev *d)
+{
+       register ulong n;
+
+       n = jiffies & 0xffff;
+       return n |= (++d->lasttag & 0x7fff) << 16;
+}
+
+static int
+aoehdr_atainit(struct aoedev *d, struct aoe_hdr *h)
+{
+       u16 type = __constant_cpu_to_be16(ETH_P_AOE);
+       u16 aoemajor = __cpu_to_be16(d->aoemajor);
+       u32 host_tag = newtag(d);
+       u32 tag = __cpu_to_be32(host_tag);
+
+       memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
+       memcpy(h->dst, d->addr, sizeof h->dst);
+       memcpy(h->type, &type, sizeof type);
+       h->verfl = AOE_HVER;
+       memcpy(h->major, &aoemajor, sizeof aoemajor);
+       h->minor = d->aoeminor;
+       h->cmd = AOECMD_ATA;
+       memcpy(h->tag, &tag, sizeof tag);
+
+       return host_tag;
+}
+
+static void
+aoecmd_ata_rw(struct aoedev *d, struct frame *f)
+{
+       struct aoe_hdr *h;
+       struct aoe_atahdr *ah;
+       struct buf *buf;
+       struct sk_buff *skb;
+       ulong bcnt;
+       register sector_t sector;
+       char writebit, extbit;
+
+       writebit = 0x10;
+       extbit = 0x4;
+
+       buf = d->inprocess;
+
+       sector = buf->sector;
+       bcnt = buf->bv_resid;
+       if (bcnt > MAXATADATA)
+               bcnt = MAXATADATA;
+
+       /* initialize the headers & frame */
+       h = (struct aoe_hdr *) f->data;
+       ah = (struct aoe_atahdr *) (h+1);
+       f->ndata = sizeof *h + sizeof *ah;
+       memset(h, 0, f->ndata);
+       f->tag = aoehdr_atainit(d, h);
+       f->waited = 0;
+       f->buf = buf;
+       f->bufaddr = buf->bufaddr;
+
+       /* set up ata header */
+       ah->scnt = bcnt >> 9;
+       ah->lba0 = sector;
+       ah->lba1 = sector >>= 8;
+       ah->lba2 = sector >>= 8;
+       ah->lba3 = sector >>= 8;
+       if (d->flags & DEVFL_EXT) {
+               ah->aflags |= AOEAFL_EXT;
+               ah->lba4 = sector >>= 8;
+               ah->lba5 = sector >>= 8;
+       } else {
+               extbit = 0;
+               ah->lba3 &= 0x0f;
+               ah->lba3 |= 0xe0;       /* LBA bit + obsolete 0xa0 */
+       }
+
+       if (bio_data_dir(buf->bio) == WRITE) {
+               ah->aflags |= AOEAFL_WRITE;
+               f->writedatalen = bcnt;
+       } else {
+               writebit = 0;
+               f->writedatalen = 0;
+       }
+
+       ah->cmdstat = WIN_READ | writebit | extbit;
+
+       /* mark all tracking fields and load out */
+       buf->nframesout += 1;
+       buf->bufaddr += bcnt;
+       buf->bv_resid -= bcnt;
+/* printk(KERN_INFO "aoe: bv_resid=%ld\n", buf->bv_resid); */
+       buf->resid -= bcnt;
+       buf->sector += bcnt >> 9;
+       if (buf->resid == 0) {
+               d->inprocess = NULL;
+       } else if (buf->bv_resid == 0) {
+               buf->bv++;
+               buf->bv_resid = buf->bv->bv_len;
+               buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
+       }
+
+       skb = skb_prepare(d, f);
+       if (skb) {
+               skb->next = d->skblist;
+               d->skblist = skb;
+       }
+}
+
+/* enters with d->lock held */
+void
+aoecmd_work(struct aoedev *d)
+{
+       struct frame *f;
+       struct buf *buf;
+loop:
+       f = getframe(d, FREETAG);
+       if (f == NULL)
+               return;
+       if (d->inprocess == NULL) {
+               if (list_empty(&d->bufq))
+                       return;
+               buf = container_of(d->bufq.next, struct buf, bufs);
+               list_del(d->bufq.next);
+/*printk(KERN_INFO "aoecmd_work: bi_size=%ld\n", buf->bio->bi_size); */
+               d->inprocess = buf;
+       }
+       aoecmd_ata_rw(d, f);
+       goto loop;
+}
+
+static void
+rexmit(struct aoedev *d, struct frame *f)
+{
+       struct sk_buff *skb;
+       struct aoe_hdr *h;
+       char buf[128];
+       u32 n;
+       u32 net_tag;
+
+       n = newtag(d);
+
+       snprintf(buf, sizeof buf,
+               "%15s e%ld.%ld oldtag=%08x@%08lx newtag=%08x\n",
+               "retransmit",
+               d->aoemajor, d->aoeminor, f->tag, jiffies, n);
+       aoechr_error(buf);
+
+       h = (struct aoe_hdr *) f->data;
+       f->tag = n;
+       net_tag = __cpu_to_be32(n);
+       memcpy(h->tag, &net_tag, sizeof net_tag);
+
+       skb = skb_prepare(d, f);
+       if (skb) {
+               skb->next = d->skblist;
+               d->skblist = skb;
+       }
+}
+
+static int
+tsince(int tag)
+{
+       int n;
+
+       n = jiffies & 0xffff;
+       n -= tag & 0xffff;
+       if (n < 0)
+               n += 1<<16;
+       return n;
+}
+
+static void
+rexmit_timer(ulong vp)
+{
+       struct aoedev *d;
+       struct frame *f, *e;
+       struct sk_buff *sl;
+       register long timeout;
+       ulong flags, n;
+
+       d = (struct aoedev *) vp;
+       sl = NULL;
+
+       /* timeout is always ~150% of the moving average */
+       timeout = d->rttavg;
+       timeout += timeout >> 1;
+
+       spin_lock_irqsave(&d->lock, flags);
+
+       if (d->flags & DEVFL_TKILL) {
+tdie:          spin_unlock_irqrestore(&d->lock, flags);
+               return;
+       }
+       f = d->frames;
+       e = f + d->nframes;
+       for (; f<e; f++) {
+               if (f->tag != FREETAG && tsince(f->tag) >= timeout) {
+                       n = f->waited += timeout;
+                       n /= HZ;
+                       if (n > MAXWAIT) { /* waited too long.  device failure. */
+                               aoedev_downdev(d);
+                               goto tdie;
+                       }
+                       rexmit(d, f);
+               }
+       }
+
+       sl = d->skblist;
+       d->skblist = NULL;
+       if (sl) {
+               n = d->rttavg <<= 1;
+               if (n > MAXTIMER)
+                       d->rttavg = MAXTIMER;
+       }
+
+       d->timer.expires = jiffies + TIMERTICK;
+       add_timer(&d->timer);
+
+       spin_unlock_irqrestore(&d->lock, flags);
+
+       aoenet_xmit(sl);
+}
+
+static void
+ataid_complete(struct aoedev *d, unsigned char *id)
+{
+       u64 ssize;
+       u16 n;
+
+       /* word 83: command set supported */
+       n = __le16_to_cpu(*((u16 *) &id[83<<1]));
+
+       /* word 86: command set/feature enabled */
+       n |= __le16_to_cpu(*((u16 *) &id[86<<1]));
+
+       if (n & (1<<10)) {      /* bit 10: LBA 48 */
+               d->flags |= DEVFL_EXT;
+
+               /* word 100: number lba48 sectors */
+               ssize = __le64_to_cpu(*((u64 *) &id[100<<1]));
+
+               /* set as in ide-disk.c:init_idedisk_capacity */
+               d->geo.cylinders = ssize;
+               d->geo.cylinders /= (255 * 63);
+               d->geo.heads = 255;
+               d->geo.sectors = 63;
+       } else {
+               d->flags &= ~DEVFL_EXT;
+
+               /* number lba28 sectors */
+               ssize = __le32_to_cpu(*((u32 *) &id[60<<1]));
+
+               /* NOTE: obsolete in ATA 6 */
+               d->geo.cylinders = __le16_to_cpu(*((u16 *) &id[54<<1]));
+               d->geo.heads = __le16_to_cpu(*((u16 *) &id[55<<1]));
+               d->geo.sectors = __le16_to_cpu(*((u16 *) &id[56<<1]));
+       }
+       d->ssize = ssize;
+       d->geo.start = 0;
+       if (d->gd != NULL) {
+               d->gd->capacity = ssize;
+               d->flags |= DEVFL_UP;
+               return;
+       }
+       if (d->flags & DEVFL_WORKON) {
+               printk(KERN_INFO "aoe: ataid_complete: can't schedule work, it's already on!  "
+                       "(This really shouldn't happen).\n");
+               return;
+       }
+       INIT_WORK(&d->work, aoeblk_gdalloc, d);
+       schedule_work(&d->work);
+       d->flags |= DEVFL_WORKON;
+}
+
+static void
+calc_rttavg(struct aoedev *d, int rtt)
+{
+       register long n;
+
+       n = rtt;
+       if (n < MINTIMER)
+               n = MINTIMER;
+       else if (n > MAXTIMER)
+               n = MAXTIMER;
+
+       /* g == .25; cf. Congestion Avoidance and Control, Jacobson & Karels; 1988 */
+       n -= d->rttavg;
+       d->rttavg += n >> 2;
+}
+
+void
+aoecmd_ata_rsp(struct sk_buff *skb)
+{
+       struct aoedev *d;
+       struct aoe_hdr *hin;
+       struct aoe_atahdr *ahin, *ahout;
+       struct frame *f;
+       struct buf *buf;
+       struct sk_buff *sl;
+       register long n;
+       ulong flags;
+       char ebuf[128];
+       
+       hin = (struct aoe_hdr *) skb->mac.raw;
+       d = aoedev_bymac(hin->src);
+       if (d == NULL) {
+               snprintf(ebuf, sizeof ebuf, "aoecmd_ata_rsp: ata response "
+                       "for unknown device %d.%d\n",
+                        __be16_to_cpu(*((u16 *) hin->major)),
+                       hin->minor);
+               aoechr_error(ebuf);
+               return;
+       }
+
+       spin_lock_irqsave(&d->lock, flags);
+
+       f = getframe(d, __be32_to_cpu(*((u32 *) hin->tag)));
+       if (f == NULL) {
+               spin_unlock_irqrestore(&d->lock, flags);
+               snprintf(ebuf, sizeof ebuf,
+                       "%15s e%d.%d    tag=%08x@%08lx\n",
+                       "unexpected rsp",
+                       __be16_to_cpu(*((u16 *) hin->major)),
+                       hin->minor,
+                       __be32_to_cpu(*((u32 *) hin->tag)),
+                       jiffies);
+               aoechr_error(ebuf);
+               return;
+       }
+
+       calc_rttavg(d, tsince(f->tag));
+
+       ahin = (struct aoe_atahdr *) (hin+1);
+       ahout = (struct aoe_atahdr *) (f->data + sizeof(struct aoe_hdr));
+       buf = f->buf;
+
+       if (ahin->cmdstat & 0xa9) {     /* these bits cleared on success */
+               printk(KERN_CRIT "aoe: aoecmd_ata_rsp: ata error cmd=%2.2Xh "
+                       "stat=%2.2Xh\n", ahout->cmdstat, ahin->cmdstat);
+               if (buf)
+                       buf->flags |= BUFFL_FAIL;
+       } else {
+               switch (ahout->cmdstat) {
+               case WIN_READ:
+               case WIN_READ_EXT:
+                       n = ahout->scnt << 9;
+                       if (skb->len - sizeof *hin - sizeof *ahin < n) {
+                               printk(KERN_CRIT "aoe: aoecmd_ata_rsp: runt "
+                                       "ata data size in read.  skb->len=%d\n",
+                                       skb->len);
+                               /* fail frame f?  just returning will rexmit. */
+                               spin_unlock_irqrestore(&d->lock, flags);
+                               return;
+                       }
+                       memcpy(f->bufaddr, ahin+1, n);
+               case WIN_WRITE:
+               case WIN_WRITE_EXT:
+                       break;
+               case WIN_IDENTIFY:
+                       if (skb->len - sizeof *hin - sizeof *ahin < 512) {
+                               printk(KERN_INFO "aoe: aoecmd_ata_rsp: runt data size "
+                                       "in ataid.  skb->len=%d\n", skb->len);
+                               spin_unlock_irqrestore(&d->lock, flags);
+                               return;
+                       }
+                       ataid_complete(d, (char *) (ahin+1));
+                       /* d->flags |= DEVFL_WC_UPDATE; */
+                       break;
+               default:
+                       printk(KERN_INFO "aoe: aoecmd_ata_rsp: unrecognized "
+                              "outbound ata command %2.2Xh for %d.%d\n", 
+                              ahout->cmdstat,
+                              __be16_to_cpu(*((u16 *) hin->major)),
+                              hin->minor);
+               }
+       }
+
+       if (buf) {
+               buf->nframesout -= 1;
+               if (buf->nframesout == 0 && buf->resid == 0) {
+                       n = !(buf->flags & BUFFL_FAIL);
+                       bio_endio(buf->bio, buf->bio->bi_size, 0);
+                       mempool_free(buf, d->bufpool);
+               }
+       }
+
+       f->buf = NULL;
+       f->tag = FREETAG;
+
+       aoecmd_work(d);
+
+       sl = d->skblist;
+       d->skblist = NULL;
+
+       spin_unlock_irqrestore(&d->lock, flags);
+
+       aoenet_xmit(sl);
+}
+
+void
+aoecmd_cfg(ushort aoemajor, unsigned char aoeminor)
+{
+       struct aoe_hdr *h;
+       struct aoe_cfghdr *ch;
+       struct sk_buff *skb, *sl;
+       struct net_device *ifp;
+       u16 aoe_type = __constant_cpu_to_be16(ETH_P_AOE);
+       u16 net_aoemajor = __cpu_to_be16(aoemajor);
+
+       sl = NULL;
+
+       read_lock(&dev_base_lock);
+       for (ifp = dev_base; ifp; dev_put(ifp), ifp = ifp->next) {
+               dev_hold(ifp);
+               if (!is_aoe_netif(ifp))
+                       continue;
+
+               skb = new_skb(ifp, sizeof *h + sizeof *ch);
+               if (skb == NULL) {
+                       printk(KERN_INFO "aoe: aoecmd_cfg: skb alloc failure\n");
+                       continue;
+               }
+               h = (struct aoe_hdr *) skb->mac.raw;
+               memset(h, 0, sizeof *h + sizeof *ch);
+
+               memset(h->dst, 0xff, sizeof h->dst);
+               memcpy(h->src, ifp->dev_addr, sizeof h->src);
+               memcpy(h->type, &aoe_type, sizeof aoe_type);
+               h->verfl = AOE_HVER;
+               memcpy(h->major, &net_aoemajor, sizeof net_aoemajor);
+               h->minor = aoeminor;
+               h->cmd = AOECMD_CFG;
+
+               skb->next = sl;
+               sl = skb;
+       }
+       read_unlock(&dev_base_lock);
+
+       aoenet_xmit(sl);
+}
+/*
+ * Since we only call this in one place (and it only prepares one frame)
+ * we just return the skb.  Usually we'd chain it up to the d->skblist.
+ */
+static struct sk_buff *
+aoecmd_ata_id(struct aoedev *d)
+{
+       struct aoe_hdr *h;
+       struct aoe_atahdr *ah;
+       struct frame *f;
+       struct sk_buff *skb;
+
+       f = getframe(d, FREETAG);
+       if (f == NULL) {
+               printk(KERN_CRIT "aoe: aoecmd_ata_id: can't get a frame.  "
+                       "This shouldn't happen.\n");
+               return NULL;
+       }
+
+       /* initialize the headers & frame */
+       h = (struct aoe_hdr *) f->data;
+       ah = (struct aoe_atahdr *) (h+1);
+       f->ndata = sizeof *h + sizeof *ah;
+       memset(h, 0, f->ndata);
+       f->tag = aoehdr_atainit(d, h);
+       f->waited = 0;
+       f->writedatalen = 0;
+
+       /* this message initializes the device, so we reset the rttavg */
+       d->rttavg = MAXTIMER;
+
+       /* set up ata header */
+       ah->scnt = 1;
+       ah->cmdstat = WIN_IDENTIFY;
+       ah->lba3 = 0xa0;
+
+       skb = skb_prepare(d, f);
+
+       /* we now want to start the rexmit tracking */
+       d->flags &= ~DEVFL_TKILL;
+       d->timer.data = (ulong) d;
+       d->timer.function = rexmit_timer;
+       d->timer.expires = jiffies + TIMERTICK;
+       add_timer(&d->timer);
+
+       return skb;
+}
+void
+aoecmd_cfg_rsp(struct sk_buff *skb)
+{
+       struct aoedev *d;
+       struct aoe_hdr *h;
+       struct aoe_cfghdr *ch;
+       ulong flags, bufcnt, sysminor, aoemajor;
+       struct sk_buff *sl;
+       enum { MAXFRAMES = 8, MAXSYSMINOR = 255 };
+
+       h = (struct aoe_hdr *) skb->mac.raw;
+       ch = (struct aoe_cfghdr *) (h+1);
+
+       /*
+        * Enough people have their dip switches set backwards to
+        * warrant a loud message for this special case.
+        */
+       aoemajor = __be16_to_cpu(*((u16 *) h->major));
+       if (aoemajor == 0xfff) {
+               printk(KERN_CRIT "aoe: aoecmd_cfg_rsp: Warning: shelf "
+                       "address is all ones.  Check shelf dip switches\n");
+               return;
+       }
+
+       sysminor = SYSMINOR(aoemajor, h->minor);
+       if (sysminor > MAXSYSMINOR) {
+               printk(KERN_INFO "aoe: aoecmd_cfg_rsp: sysminor %ld too "
+                       "large\n", sysminor);
+               return;
+       }
+
+       bufcnt = __be16_to_cpu(*((u16 *) ch->bufcnt));
+       if (bufcnt > MAXFRAMES) /* keep it reasonable */
+               bufcnt = MAXFRAMES;
+
+       d = aoedev_set(sysminor, h->src, skb->dev, bufcnt);
+       if (d == NULL) {
+               printk(KERN_INFO "aoe: aoecmd_cfg_rsp: device set failure\n");
+               return;
+       }
+
+       spin_lock_irqsave(&d->lock, flags);
+
+       if (d->flags & (DEVFL_UP | DEVFL_CLOSEWAIT)) {
+               spin_unlock_irqrestore(&d->lock, flags);
+               return;
+       }
+
+       d->fw_ver = __be16_to_cpu(*((u16 *) ch->fwver));
+
+       /* we get here only if the device is new */
+       sl = aoecmd_ata_id(d);
+
+       spin_unlock_irqrestore(&d->lock, flags);
+
+       aoenet_xmit(sl);
+}
+
diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c
new file mode 100644 (file)
index 0000000..240abae
--- /dev/null
@@ -0,0 +1,180 @@
+/* Copyright (c) 2004 Coraid, Inc.  See COPYING for GPL terms. */
+/*
+ * aoedev.c
+ * AoE device utility functions; maintains device list.
+ */
+
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+#include <linux/netdevice.h>
+#include "aoe.h"
+
+static struct aoedev *devlist;
+static spinlock_t devlist_lock;
+
+struct aoedev *
+aoedev_bymac(unsigned char *macaddr)
+{
+       struct aoedev *d;
+       ulong flags;
+
+       spin_lock_irqsave(&devlist_lock, flags);
+
+       for (d=devlist; d; d=d->next)
+               if (!memcmp(d->addr, macaddr, 6))
+                       break;
+
+       spin_unlock_irqrestore(&devlist_lock, flags);
+       return d;
+}
+
+/* called with devlist lock held */
+static struct aoedev *
+aoedev_newdev(ulong nframes)
+{
+       struct aoedev *d;
+       struct frame *f, *e;
+
+       d = kcalloc(1, sizeof *d, GFP_ATOMIC);
+       if (d == NULL)
+               return NULL;
+       f = kcalloc(nframes, sizeof *f, GFP_ATOMIC);
+       if (f == NULL) {
+               kfree(d);
+               return NULL;
+       }
+
+       d->nframes = nframes;
+       d->frames = f;
+       e = f + nframes;
+       for (; f<e; f++)
+               f->tag = FREETAG;
+
+       spin_lock_init(&d->lock);
+       init_timer(&d->timer);
+       d->bufpool = NULL;      /* defer to aoeblk_gdalloc */
+       INIT_LIST_HEAD(&d->bufq);
+       d->next = devlist;
+       devlist = d;
+
+       return d;
+}
+
+void
+aoedev_downdev(struct aoedev *d)
+{
+       struct frame *f, *e;
+       struct buf *buf;
+       struct bio *bio;
+
+       d->flags |= DEVFL_TKILL;
+       del_timer(&d->timer);
+
+       f = d->frames;
+       e = f + d->nframes;
+       for (; f<e; f->tag = FREETAG, f->buf = NULL, f++) {
+               if (f->tag == FREETAG || f->buf == NULL)
+                       continue;
+               buf = f->buf;
+               bio = buf->bio;
+               if (--buf->nframesout == 0) {
+                       mempool_free(buf, d->bufpool);
+                       bio_endio(bio, bio->bi_size, -EIO);
+               }
+       }
+       d->inprocess = NULL;
+
+       while (!list_empty(&d->bufq)) {
+               buf = container_of(d->bufq.next, struct buf, bufs);
+               list_del(d->bufq.next);
+               bio = buf->bio;
+               mempool_free(buf, d->bufpool);
+               bio_endio(bio, bio->bi_size, -EIO);
+       }
+
+       if (d->nopen)
+               d->flags |= DEVFL_CLOSEWAIT;
+       if (d->gd)
+               d->gd->capacity = 0;
+
+       d->flags &= ~DEVFL_UP;
+}
+
+struct aoedev *
+aoedev_set(ulong sysminor, unsigned char *addr, struct net_device *ifp, ulong bufcnt)
+{
+       struct aoedev *d;
+       ulong flags;
+
+       spin_lock_irqsave(&devlist_lock, flags);
+
+       for (d=devlist; d; d=d->next)
+               if (d->sysminor == sysminor
+               || memcmp(d->addr, addr, sizeof d->addr) == 0)
+                       break;
+
+       if (d == NULL && (d = aoedev_newdev(bufcnt)) == NULL) {
+               spin_unlock_irqrestore(&devlist_lock, flags);
+               printk(KERN_INFO "aoe: aoedev_set: aoedev_newdev failure.\n");
+               return NULL;
+       }
+
+       spin_unlock_irqrestore(&devlist_lock, flags);
+       spin_lock_irqsave(&d->lock, flags);
+
+       d->ifp = ifp;
+
+       if (d->sysminor != sysminor
+       || memcmp(d->addr, addr, sizeof d->addr)
+       || (d->flags & DEVFL_UP) == 0) {
+               aoedev_downdev(d); /* flushes outstanding frames */
+               memcpy(d->addr, addr, sizeof d->addr);
+               d->sysminor = sysminor;
+               d->aoemajor = AOEMAJOR(sysminor);
+               d->aoeminor = AOEMINOR(sysminor);
+       }
+
+       spin_unlock_irqrestore(&d->lock, flags);
+       return d;
+}
+
+static void
+aoedev_freedev(struct aoedev *d)
+{
+       if (d->gd) {
+               aoedisk_rm_sysfs(d);
+               del_gendisk(d->gd);
+               put_disk(d->gd);
+       }
+       kfree(d->frames);
+       mempool_destroy(d->bufpool);
+       kfree(d);
+}
+
+void
+aoedev_exit(void)
+{
+       struct aoedev *d;
+       ulong flags;
+
+       flush_scheduled_work();
+
+       while ((d = devlist)) {
+               devlist = d->next;
+
+               spin_lock_irqsave(&d->lock, flags);
+               aoedev_downdev(d);
+               spin_unlock_irqrestore(&d->lock, flags);
+
+               del_timer_sync(&d->timer);
+               aoedev_freedev(d);
+       }
+}
+
+int __init
+aoedev_init(void)
+{
+       spin_lock_init(&devlist_lock);
+       return 0;
+}
+
diff --git a/drivers/block/aoe/aoemain.c b/drivers/block/aoe/aoemain.c
new file mode 100644 (file)
index 0000000..387588a
--- /dev/null
@@ -0,0 +1,112 @@
+/* Copyright (c) 2004 Coraid, Inc.  See COPYING for GPL terms. */
+/*
+ * aoemain.c
+ * Module initialization routines, discover timer
+ */
+
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+#include <linux/module.h>
+#include "aoe.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sam Hopkins <sah@coraid.com>");
+MODULE_DESCRIPTION("AoE block/char driver for 2.6.[0-9]+");
+MODULE_VERSION(VERSION);
+
+enum { TINIT, TRUN, TKILL };
+
+static void
+discover_timer(ulong vp)
+{
+       static struct timer_list t;
+       static volatile ulong die;
+       static spinlock_t lock;
+       ulong flags;
+       enum { DTIMERTICK = HZ * 60 }; /* one minute */
+
+       switch (vp) {
+       case TINIT:
+               init_timer(&t);
+               spin_lock_init(&lock);
+               t.data = TRUN;
+               t.function = discover_timer;
+               die = 0;
+       case TRUN:
+               spin_lock_irqsave(&lock, flags);
+               if (!die) {
+                       t.expires = jiffies + DTIMERTICK;
+                       add_timer(&t);
+               }
+               spin_unlock_irqrestore(&lock, flags);
+
+               aoecmd_cfg(0xffff, 0xff);
+               return;
+       case TKILL:
+               spin_lock_irqsave(&lock, flags);
+               die = 1;
+               spin_unlock_irqrestore(&lock, flags);
+
+               del_timer_sync(&t);
+       default:
+               return;
+       }
+}
+
+static void
+aoe_exit(void)
+{
+       discover_timer(TKILL);
+
+       aoenet_exit();
+       unregister_blkdev(AOE_MAJOR, DEVICE_NAME);
+       aoechr_exit();
+       aoedev_exit();
+       aoeblk_exit();          /* free cache after de-allocating bufs */
+}
+
+static int __init
+aoe_init(void)
+{
+       int ret;
+
+       ret = aoedev_init();
+       if (ret)
+               return ret;
+       ret = aoechr_init();
+       if (ret)
+               goto chr_fail;
+       ret = aoeblk_init();
+       if (ret)
+               goto blk_fail;
+       ret = aoenet_init();
+       if (ret)
+               goto net_fail;
+       ret = register_blkdev(AOE_MAJOR, DEVICE_NAME);
+       if (ret < 0) {
+               printk(KERN_ERR "aoe: aoeblk_init: can't register major\n");
+               goto blkreg_fail;
+       }
+
+       printk(KERN_INFO
+              "aoe: aoe_init: AoE v2.6-%s initialised.\n",
+              VERSION);
+       discover_timer(TINIT);
+       return 0;
+
+ blkreg_fail:
+       aoenet_exit();
+ net_fail:
+       aoeblk_exit();
+ blk_fail:
+       aoechr_exit();
+ chr_fail:
+       aoedev_exit();
+       
+       printk(KERN_INFO "aoe: aoe_init: initialisation failure.\n");
+       return ret;
+}
+
+module_init(aoe_init);
+module_exit(aoe_exit);
+
diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c
new file mode 100644 (file)
index 0000000..cc1945b
--- /dev/null
@@ -0,0 +1,172 @@
+/* Copyright (c) 2004 Coraid, Inc.  See COPYING for GPL terms. */
+/*
+ * aoenet.c
+ * Ethernet portion of AoE driver
+ */
+
+#include <linux/hdreg.h>
+#include <linux/blkdev.h>
+#include <linux/netdevice.h>
+#include "aoe.h"
+
+#define NECODES 5
+
+static char *aoe_errlist[] =
+{
+       "no such error",
+       "unrecognized command code",
+       "bad argument parameter",
+       "device unavailable",
+       "config string present",
+       "unsupported version"
+};
+
+enum {
+       IFLISTSZ = 1024,
+};
+
+static char aoe_iflist[IFLISTSZ];
+
+int
+is_aoe_netif(struct net_device *ifp)
+{
+       register char *p, *q;
+       register int len;
+
+       if (aoe_iflist[0] == '\0')
+               return 1;
+
+       for (p = aoe_iflist; *p; p = q + strspn(q, WHITESPACE)) {
+               q = p + strcspn(p, WHITESPACE);
+               if (q != p)
+                       len = q - p;
+               else
+                       len = strlen(p); /* last token in aoe_iflist */
+
+               if (strlen(ifp->name) == len && !strncmp(ifp->name, p, len))
+                       return 1;
+               if (q == p)
+                       break;
+       }
+
+       return 0;
+}
+
+int
+set_aoe_iflist(const char __user *user_str, size_t size)
+{
+       if (size >= IFLISTSZ)
+               return -EINVAL;
+
+       if (copy_from_user(aoe_iflist, user_str, size)) {
+               printk(KERN_INFO "aoe: %s: copy from user failed\n", __FUNCTION__);
+               return -EFAULT;
+       }
+       aoe_iflist[size] = 0x00;
+       return 0;
+}
+
+u64
+mac_addr(char addr[6])
+{
+       u64 n = 0;
+       char *p = (char *) &n;
+
+       memcpy(p + 2, addr, 6); /* (sizeof addr != 6) */
+
+       return __be64_to_cpu(n);
+}
+
+static struct sk_buff *
+skb_check(struct sk_buff *skb)
+{
+       if (skb_is_nonlinear(skb))
+       if ((skb = skb_share_check(skb, GFP_ATOMIC)))
+       if (skb_linearize(skb, GFP_ATOMIC) < 0) {
+               dev_kfree_skb(skb);
+               return NULL;
+       }
+       return skb;
+}
+
+void
+aoenet_xmit(struct sk_buff *sl)
+{
+       struct sk_buff *skb;
+
+       while ((skb = sl)) {
+               sl = sl->next;
+               skb->next = skb->prev = NULL;
+               dev_queue_xmit(skb);
+       }
+}
+
+/* 
+ * (1) len doesn't include the header by default.  I want this. 
+ */
+static int
+aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt)
+{
+       struct aoe_hdr *h;
+       ulong n;
+
+       skb = skb_check(skb);
+       if (!skb)
+               return 0;
+
+       if (!is_aoe_netif(ifp))
+               goto exit;
+
+       //skb->len += ETH_HLEN; /* (1) */
+       skb_push(skb, ETH_HLEN);        /* (1) */
+
+       h = (struct aoe_hdr *) skb->mac.raw;
+       n = __be32_to_cpu(*((u32 *) h->tag));
+       if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31))
+               goto exit;
+
+       if (h->verfl & AOEFL_ERR) {
+               n = h->err;
+               if (n > NECODES)
+                       n = 0;
+               if (net_ratelimit())
+                       printk(KERN_ERR "aoe: aoenet_rcv: error packet from %d.%d; "
+                              "ecode=%d '%s'\n",
+                              __be16_to_cpu(*((u16 *) h->major)), h->minor, 
+                              h->err, aoe_errlist[n]);
+               goto exit;
+       }
+
+       switch (h->cmd) {
+       case AOECMD_ATA:
+               aoecmd_ata_rsp(skb);
+               break;
+       case AOECMD_CFG:
+               aoecmd_cfg_rsp(skb);
+               break;
+       default:
+               printk(KERN_INFO "aoe: aoenet_rcv: unknown cmd %d\n", h->cmd);
+       }
+exit:
+       dev_kfree_skb(skb);
+       return 0;
+}
+
+static struct packet_type aoe_pt = {
+       .type = __constant_htons(ETH_P_AOE),
+       .func = aoenet_rcv,
+};
+
+int __init
+aoenet_init(void)
+{
+       dev_add_pack(&aoe_pt);
+       return 0;
+}
+
+void
+aoenet_exit(void)
+{
+       dev_remove_pack(&aoe_pt);
+}
+
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
new file mode 100644 (file)
index 0000000..03a9388
--- /dev/null
@@ -0,0 +1,2679 @@
+/*
+ * Copyright (C) 2000 Jens Axboe <axboe@suse.de>
+ * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and
+ * DVD-RW devices (aka an exercise in block layer masturbation)
+ *
+ *
+ * TODO: (circa order of when I will fix it)
+ * - Only able to write on CD-RW media right now.
+ * - check host application code on media and set it in write page
+ * - interface for UDF <-> packet to negotiate a new location when a write
+ *   fails.
+ * - handle OPC, especially for -RW media
+ *
+ * Theory of operation:
+ *
+ * We use a custom make_request_fn function that forwards reads directly to
+ * the underlying CD device. Write requests are either attached directly to
+ * a live packet_data object, or simply stored sequentially in a list for
+ * later processing by the kcdrwd kernel thread. This driver doesn't use
+ * any elevator functionally as defined by the elevator_s struct, but the
+ * underlying CD device uses a standard elevator.
+ *
+ * This strategy makes it possible to do very late merging of IO requests.
+ * A new bio sent to pkt_make_request can be merged with a live packet_data
+ * object even if the object is in the data gathering state.
+ *
+ *************************************************************************/
+
+#define VERSION_CODE   "v0.2.0a 2004-07-14 Jens Axboe (axboe@suse.de) and petero2@telia.com"
+
+#include <linux/pktcdvd.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/file.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/miscdevice.h>
+#include <linux/suspend.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_ioctl.h>
+
+#include <asm/uaccess.h>
+
+#if PACKET_DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#if PACKET_DEBUG > 1
+#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
+#else
+#define VPRINTK(fmt, args...)
+#endif
+
+#define MAX_SPEED 0xffff
+
+#define ZONE(sector, pd) (((sector) + (pd)->offset) & ~((pd)->settings.size - 1))
+
+static struct pktcdvd_device *pkt_devs[MAX_WRITERS];
+static struct proc_dir_entry *pkt_proc;
+static int pkt_major;
+static struct semaphore ctl_mutex;     /* Serialize open/close/setup/teardown */
+static mempool_t *psd_pool;
+
+
+static void pkt_bio_finished(struct pktcdvd_device *pd)
+{
+       BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0);
+       if (atomic_dec_and_test(&pd->cdrw.pending_bios)) {
+               VPRINTK("pktcdvd: queue empty\n");
+               atomic_set(&pd->iosched.attention, 1);
+               wake_up(&pd->wqueue);
+       }
+}
+
+static void pkt_bio_destructor(struct bio *bio)
+{
+       kfree(bio->bi_io_vec);
+       kfree(bio);
+}
+
+static struct bio *pkt_bio_alloc(int nr_iovecs)
+{
+       struct bio_vec *bvl = NULL;
+       struct bio *bio;
+
+       bio = kmalloc(sizeof(struct bio), GFP_KERNEL);
+       if (!bio)
+               goto no_bio;
+       bio_init(bio);
+
+       bvl = kmalloc(nr_iovecs * sizeof(struct bio_vec), GFP_KERNEL);
+       if (!bvl)
+               goto no_bvl;
+       memset(bvl, 0, nr_iovecs * sizeof(struct bio_vec));
+
+       bio->bi_max_vecs = nr_iovecs;
+       bio->bi_io_vec = bvl;
+       bio->bi_destructor = pkt_bio_destructor;
+
+       return bio;
+
+ no_bvl:
+       kfree(bio);
+ no_bio:
+       return NULL;
+}
+
+/*
+ * Allocate a packet_data struct
+ */
+static struct packet_data *pkt_alloc_packet_data(void)
+{
+       int i;
+       struct packet_data *pkt;
+
+       pkt = kmalloc(sizeof(struct packet_data), GFP_KERNEL);
+       if (!pkt)
+               goto no_pkt;
+       memset(pkt, 0, sizeof(struct packet_data));
+
+       pkt->w_bio = pkt_bio_alloc(PACKET_MAX_SIZE);
+       if (!pkt->w_bio)
+               goto no_bio;
+
+       for (i = 0; i < PAGES_PER_PACKET; i++) {
+               pkt->pages[i] = alloc_page(GFP_KERNEL);
+               if (!pkt->pages[i])
+                       goto no_page;
+       }
+       for (i = 0; i < PAGES_PER_PACKET; i++)
+               clear_page(page_address(pkt->pages[i]));
+
+       spin_lock_init(&pkt->lock);
+
+       for (i = 0; i < PACKET_MAX_SIZE; i++) {
+               struct bio *bio = pkt_bio_alloc(1);
+               if (!bio)
+                       goto no_rd_bio;
+               pkt->r_bios[i] = bio;
+       }
+
+       return pkt;
+
+no_rd_bio:
+       for (i = 0; i < PACKET_MAX_SIZE; i++) {
+               struct bio *bio = pkt->r_bios[i];
+               if (bio)
+                       bio_put(bio);
+       }
+
+no_page:
+       for (i = 0; i < PAGES_PER_PACKET; i++)
+               if (pkt->pages[i])
+                       __free_page(pkt->pages[i]);
+       bio_put(pkt->w_bio);
+no_bio:
+       kfree(pkt);
+no_pkt:
+       return NULL;
+}
+
+/*
+ * Free a packet_data struct
+ */
+static void pkt_free_packet_data(struct packet_data *pkt)
+{
+       int i;
+
+       for (i = 0; i < PACKET_MAX_SIZE; i++) {
+               struct bio *bio = pkt->r_bios[i];
+               if (bio)
+                       bio_put(bio);
+       }
+       for (i = 0; i < PAGES_PER_PACKET; i++)
+               __free_page(pkt->pages[i]);
+       bio_put(pkt->w_bio);
+       kfree(pkt);
+}
+
+static void pkt_shrink_pktlist(struct pktcdvd_device *pd)
+{
+       struct packet_data *pkt, *next;
+
+       BUG_ON(!list_empty(&pd->cdrw.pkt_active_list));
+
+       list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) {
+               pkt_free_packet_data(pkt);
+       }
+}
+
+static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets)
+{
+       struct packet_data *pkt;
+
+       INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
+       INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
+       spin_lock_init(&pd->cdrw.active_list_lock);
+       while (nr_packets > 0) {
+               pkt = pkt_alloc_packet_data();
+               if (!pkt) {
+                       pkt_shrink_pktlist(pd);
+                       return 0;
+               }
+               pkt->id = nr_packets;
+               pkt->pd = pd;
+               list_add(&pkt->list, &pd->cdrw.pkt_free_list);
+               nr_packets--;
+       }
+       return 1;
+}
+
+static void *pkt_rb_alloc(int gfp_mask, void *data)
+{
+       return kmalloc(sizeof(struct pkt_rb_node), gfp_mask);
+}
+
+static void pkt_rb_free(void *ptr, void *data)
+{
+       kfree(ptr);
+}
+
+static inline struct pkt_rb_node *pkt_rbtree_next(struct pkt_rb_node *node)
+{
+       struct rb_node *n = rb_next(&node->rb_node);
+       if (!n)
+               return NULL;
+       return rb_entry(n, struct pkt_rb_node, rb_node);
+}
+
+static inline void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node)
+{
+       rb_erase(&node->rb_node, &pd->bio_queue);
+       mempool_free(node, pd->rb_pool);
+       pd->bio_queue_size--;
+       BUG_ON(pd->bio_queue_size < 0);
+}
+
+/*
+ * Find the first node in the pd->bio_queue rb tree with a starting sector >= s.
+ */
+static struct pkt_rb_node *pkt_rbtree_find(struct pktcdvd_device *pd, sector_t s)
+{
+       struct rb_node *n = pd->bio_queue.rb_node;
+       struct rb_node *next;
+       struct pkt_rb_node *tmp;
+
+       if (!n) {
+               BUG_ON(pd->bio_queue_size > 0);
+               return NULL;
+       }
+
+       for (;;) {
+               tmp = rb_entry(n, struct pkt_rb_node, rb_node);
+               if (s <= tmp->bio->bi_sector)
+                       next = n->rb_left;
+               else
+                       next = n->rb_right;
+               if (!next)
+                       break;
+               n = next;
+       }
+
+       if (s > tmp->bio->bi_sector) {
+               tmp = pkt_rbtree_next(tmp);
+               if (!tmp)
+                       return NULL;
+       }
+       BUG_ON(s > tmp->bio->bi_sector);
+       return tmp;
+}
+
+/*
+ * Insert a node into the pd->bio_queue rb tree.
+ */
+static void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *node)
+{
+       struct rb_node **p = &pd->bio_queue.rb_node;
+       struct rb_node *parent = NULL;
+       sector_t s = node->bio->bi_sector;
+       struct pkt_rb_node *tmp;
+
+       while (*p) {
+               parent = *p;
+               tmp = rb_entry(parent, struct pkt_rb_node, rb_node);
+               if (s < tmp->bio->bi_sector)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
+       }
+       rb_link_node(&node->rb_node, parent, p);
+       rb_insert_color(&node->rb_node, &pd->bio_queue);
+       pd->bio_queue_size++;
+}
+
+/*
+ * Add a bio to a single linked list defined by its head and tail pointers.
+ */
+static inline void pkt_add_list_last(struct bio *bio, struct bio **list_head, struct bio **list_tail)
+{
+       bio->bi_next = NULL;
+       if (*list_tail) {
+               BUG_ON((*list_head) == NULL);
+               (*list_tail)->bi_next = bio;
+               (*list_tail) = bio;
+       } else {
+               BUG_ON((*list_head) != NULL);
+               (*list_head) = bio;
+               (*list_tail) = bio;
+       }
+}
+
+/*
+ * Remove and return the first bio from a single linked list defined by its
+ * head and tail pointers.
+ */
+static inline struct bio *pkt_get_list_first(struct bio **list_head, struct bio **list_tail)
+{
+       struct bio *bio;
+
+       if (*list_head == NULL)
+               return NULL;
+
+       bio = *list_head;
+       *list_head = bio->bi_next;
+       if (*list_head == NULL)
+               *list_tail = NULL;
+
+       bio->bi_next = NULL;
+       return bio;
+}
+
+/*
+ * Send a packet_command to the underlying block device and
+ * wait for completion.
+ */
+static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *cgc)
+{
+       char sense[SCSI_SENSE_BUFFERSIZE];
+       request_queue_t *q;
+       struct request *rq;
+       DECLARE_COMPLETION(wait);
+       int err = 0;
+
+       q = bdev_get_queue(pd->bdev);
+
+       rq = blk_get_request(q, (cgc->data_direction == CGC_DATA_WRITE) ? WRITE : READ,
+                            __GFP_WAIT);
+       rq->errors = 0;
+       rq->rq_disk = pd->bdev->bd_disk;
+       rq->bio = NULL;
+       rq->buffer = NULL;
+       rq->timeout = 60*HZ;
+       rq->data = cgc->buffer;
+       rq->data_len = cgc->buflen;
+       rq->sense = sense;
+       memset(sense, 0, sizeof(sense));
+       rq->sense_len = 0;
+       rq->flags |= REQ_BLOCK_PC | REQ_HARDBARRIER;
+       if (cgc->quiet)
+               rq->flags |= REQ_QUIET;
+       memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE);
+       if (sizeof(rq->cmd) > CDROM_PACKET_SIZE)
+               memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE);
+
+       rq->ref_count++;
+       rq->flags |= REQ_NOMERGE;
+       rq->waiting = &wait;
+       elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1);
+       generic_unplug_device(q);
+       wait_for_completion(&wait);
+
+       if (rq->errors)
+               err = -EIO;
+
+       blk_put_request(rq);
+       return err;
+}
+
+/*
+ * A generic sense dump / resolve mechanism should be implemented across
+ * all ATAPI + SCSI devices.
+ */
+static void pkt_dump_sense(struct packet_command *cgc)
+{
+       static char *info[9] = { "No sense", "Recovered error", "Not ready",
+                                "Medium error", "Hardware error", "Illegal request",
+                                "Unit attention", "Data protect", "Blank check" };
+       int i;
+       struct request_sense *sense = cgc->sense;
+
+       printk("pktcdvd:");
+       for (i = 0; i < CDROM_PACKET_SIZE; i++)
+               printk(" %02x", cgc->cmd[i]);
+       printk(" - ");
+
+       if (sense == NULL) {
+               printk("no sense\n");
+               return;
+       }
+
+       printk("sense %02x.%02x.%02x", sense->sense_key, sense->asc, sense->ascq);
+
+       if (sense->sense_key > 8) {
+               printk(" (INVALID)\n");
+               return;
+       }
+
+       printk(" (%s)\n", info[sense->sense_key]);
+}
+
+/*
+ * flush the drive cache to media
+ */
+static int pkt_flush_cache(struct pktcdvd_device *pd)
+{
+       struct packet_command cgc;
+
+       init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+       cgc.cmd[0] = GPCMD_FLUSH_CACHE;
+       cgc.quiet = 1;
+
+       /*
+        * the IMMED bit -- we default to not setting it, although that
+        * would allow a much faster close, this is safer
+        */
+#if 0
+       cgc.cmd[1] = 1 << 1;
+#endif
+       return pkt_generic_packet(pd, &cgc);
+}
+
+/*
+ * speed is given as the normal factor, e.g. 4 for 4x
+ */
+static int pkt_set_speed(struct pktcdvd_device *pd, unsigned write_speed, unsigned read_speed)
+{
+       struct packet_command cgc;
+       struct request_sense sense;
+       int ret;
+
+       init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+       cgc.sense = &sense;
+       cgc.cmd[0] = GPCMD_SET_SPEED;
+       cgc.cmd[2] = (read_speed >> 8) & 0xff;
+       cgc.cmd[3] = read_speed & 0xff;
+       cgc.cmd[4] = (write_speed >> 8) & 0xff;
+       cgc.cmd[5] = write_speed & 0xff;
+
+       if ((ret = pkt_generic_packet(pd, &cgc)))
+               pkt_dump_sense(&cgc);
+
+       return ret;
+}
+
+/*
+ * Queue a bio for processing by the low-level CD device. Must be called
+ * from process context.
+ */
+static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio, int high_prio_read)
+{
+       spin_lock(&pd->iosched.lock);
+       if (bio_data_dir(bio) == READ) {
+               pkt_add_list_last(bio, &pd->iosched.read_queue,
+                                 &pd->iosched.read_queue_tail);
+               if (high_prio_read)
+                       pd->iosched.high_prio_read = 1;
+       } else {
+               pkt_add_list_last(bio, &pd->iosched.write_queue,
+                                 &pd->iosched.write_queue_tail);
+       }
+       spin_unlock(&pd->iosched.lock);
+
+       atomic_set(&pd->iosched.attention, 1);
+       wake_up(&pd->wqueue);
+}
+
+/*
+ * Process the queued read/write requests. This function handles special
+ * requirements for CDRW drives:
+ * - A cache flush command must be inserted before a read request if the
+ *   previous request was a write.
+ * - Switching between reading and writing is slow, so don't it more often
+ *   than necessary.
+ * - Set the read speed according to current usage pattern. When only reading
+ *   from the device, it's best to use the highest possible read speed, but
+ *   when switching often between reading and writing, it's better to have the
+ *   same read and write speeds.
+ * - Reads originating from user space should have higher priority than reads
+ *   originating from pkt_gather_data, because some process is usually waiting
+ *   on reads of the first kind.
+ */
+static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
+{
+       request_queue_t *q;
+
+       if (atomic_read(&pd->iosched.attention) == 0)
+               return;
+       atomic_set(&pd->iosched.attention, 0);
+
+       q = bdev_get_queue(pd->bdev);
+
+       for (;;) {
+               struct bio *bio;
+               int reads_queued, writes_queued, high_prio_read;
+
+               spin_lock(&pd->iosched.lock);
+               reads_queued = (pd->iosched.read_queue != NULL);
+               writes_queued = (pd->iosched.write_queue != NULL);
+               if (!reads_queued)
+                       pd->iosched.high_prio_read = 0;
+               high_prio_read = pd->iosched.high_prio_read;
+               spin_unlock(&pd->iosched.lock);
+
+               if (!reads_queued && !writes_queued)
+                       break;
+
+               if (pd->iosched.writing) {
+                       if (high_prio_read || (!writes_queued && reads_queued)) {
+                               if (atomic_read(&pd->cdrw.pending_bios) > 0) {
+                                       VPRINTK("pktcdvd: write, waiting\n");
+                                       break;
+                               }
+                               pkt_flush_cache(pd);
+                               pd->iosched.writing = 0;
+                       }
+               } else {
+                       if (!reads_queued && writes_queued) {
+                               if (atomic_read(&pd->cdrw.pending_bios) > 0) {
+                                       VPRINTK("pktcdvd: read, waiting\n");
+                                       break;
+                               }
+                               pd->iosched.writing = 1;
+                       }
+               }
+
+               spin_lock(&pd->iosched.lock);
+               if (pd->iosched.writing) {
+                       bio = pkt_get_list_first(&pd->iosched.write_queue,
+                                                &pd->iosched.write_queue_tail);
+               } else {
+                       bio = pkt_get_list_first(&pd->iosched.read_queue,
+                                                &pd->iosched.read_queue_tail);
+               }
+               spin_unlock(&pd->iosched.lock);
+
+               if (!bio)
+                       continue;
+
+               if (bio_data_dir(bio) == READ)
+                       pd->iosched.successive_reads += bio->bi_size >> 10;
+               else
+                       pd->iosched.successive_reads = 0;
+               if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) {
+                       if (pd->read_speed == pd->write_speed) {
+                               pd->read_speed = MAX_SPEED;
+                               pkt_set_speed(pd, pd->write_speed, pd->read_speed);
+                       }
+               } else {
+                       if (pd->read_speed != pd->write_speed) {
+                               pd->read_speed = pd->write_speed;
+                               pkt_set_speed(pd, pd->write_speed, pd->read_speed);
+                       }
+               }
+
+               atomic_inc(&pd->cdrw.pending_bios);
+               generic_make_request(bio);
+       }
+}
+
+/*
+ * Special care is needed if the underlying block device has a small
+ * max_phys_segments value.
+ */
+static int pkt_set_segment_merging(struct pktcdvd_device *pd, request_queue_t *q)
+{
+       if ((pd->settings.size << 9) / CD_FRAMESIZE <= q->max_phys_segments) {
+               /*
+                * The cdrom device can handle one segment/frame
+                */
+               clear_bit(PACKET_MERGE_SEGS, &pd->flags);
+               return 0;
+       } else if ((pd->settings.size << 9) / PAGE_SIZE <= q->max_phys_segments) {
+               /*
+                * We can handle this case at the expense of some extra memory
+                * copies during write operations
+                */
+               set_bit(PACKET_MERGE_SEGS, &pd->flags);
+               return 0;
+       } else {
+               printk("pktcdvd: cdrom max_phys_segments too small\n");
+               return -EIO;
+       }
+}
+
+/*
+ * Copy CD_FRAMESIZE bytes from src_bio into a destination page
+ */
+static void pkt_copy_bio_data(struct bio *src_bio, int seg, int offs,
+                             struct page *dst_page, int dst_offs)
+{
+       unsigned int copy_size = CD_FRAMESIZE;
+
+       while (copy_size > 0) {
+               struct bio_vec *src_bvl = bio_iovec_idx(src_bio, seg);
+               void *vfrom = kmap_atomic(src_bvl->bv_page, KM_USER0) +
+                       src_bvl->bv_offset + offs;
+               void *vto = page_address(dst_page) + dst_offs;
+               int len = min_t(int, copy_size, src_bvl->bv_len - offs);
+
+               BUG_ON(len < 0);
+               memcpy(vto, vfrom, len);
+               kunmap_atomic(vfrom, KM_USER0);
+
+               seg++;
+               offs = 0;
+               dst_offs += len;
+               copy_size -= len;
+       }
+}
+
+/*
+ * Copy all data for this packet to pkt->pages[], so that
+ * a) The number of required segments for the write bio is minimized, which
+ *    is necessary for some scsi controllers.
+ * b) The data can be used as cache to avoid read requests if we receive a
+ *    new write request for the same zone.
+ */
+static void pkt_make_local_copy(struct packet_data *pkt, struct page **pages, int *offsets)
+{
+       int f, p, offs;
+
+       /* Copy all data to pkt->pages[] */
+       p = 0;
+       offs = 0;
+       for (f = 0; f < pkt->frames; f++) {
+               if (pages[f] != pkt->pages[p]) {
+                       void *vfrom = kmap_atomic(pages[f], KM_USER0) + offsets[f];
+                       void *vto = page_address(pkt->pages[p]) + offs;
+                       memcpy(vto, vfrom, CD_FRAMESIZE);
+                       kunmap_atomic(vfrom, KM_USER0);
+                       pages[f] = pkt->pages[p];
+                       offsets[f] = offs;
+               } else {
+                       BUG_ON(offsets[f] != offs);
+               }
+               offs += CD_FRAMESIZE;
+               if (offs >= PAGE_SIZE) {
+                       BUG_ON(offs > PAGE_SIZE);
+                       offs = 0;
+                       p++;
+               }
+       }
+}
+
+static int pkt_end_io_read(struct bio *bio, unsigned int bytes_done, int err)
+{
+       struct packet_data *pkt = bio->bi_private;
+       struct pktcdvd_device *pd = pkt->pd;
+       BUG_ON(!pd);
+
+       if (bio->bi_size)
+               return 1;
+
+       VPRINTK("pkt_end_io_read: bio=%p sec0=%llx sec=%llx err=%d\n", bio,
+               (unsigned long long)pkt->sector, (unsigned long long)bio->bi_sector, err);
+
+       if (err)
+               atomic_inc(&pkt->io_errors);
+       if (atomic_dec_and_test(&pkt->io_wait)) {
+               atomic_inc(&pkt->run_sm);
+               wake_up(&pd->wqueue);
+       }
+       pkt_bio_finished(pd);
+
+       return 0;
+}
+
+static int pkt_end_io_packet_write(struct bio *bio, unsigned int bytes_done, int err)
+{
+       struct packet_data *pkt = bio->bi_private;
+       struct pktcdvd_device *pd = pkt->pd;
+       BUG_ON(!pd);
+
+       if (bio->bi_size)
+               return 1;
+
+       VPRINTK("pkt_end_io_packet_write: id=%d, err=%d\n", pkt->id, err);
+
+       pd->stats.pkt_ended++;
+
+       pkt_bio_finished(pd);
+       atomic_dec(&pkt->io_wait);
+       atomic_inc(&pkt->run_sm);
+       wake_up(&pd->wqueue);
+       return 0;
+}
+
+/*
+ * Schedule reads for the holes in a packet
+ */
+static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+       int frames_read = 0;
+       struct bio *bio;
+       int f;
+       char written[PACKET_MAX_SIZE];
+
+       BUG_ON(!pkt->orig_bios);
+
+       atomic_set(&pkt->io_wait, 0);
+       atomic_set(&pkt->io_errors, 0);
+
+       if (pkt->cache_valid) {
+               VPRINTK("pkt_gather_data: zone %llx cached\n",
+                       (unsigned long long)pkt->sector);
+               goto out_account;
+       }
+
+       /*
+        * Figure out which frames we need to read before we can write.
+        */
+       memset(written, 0, sizeof(written));
+       spin_lock(&pkt->lock);
+       for (bio = pkt->orig_bios; bio; bio = bio->bi_next) {
+               int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);
+               int num_frames = bio->bi_size / CD_FRAMESIZE;
+               BUG_ON(first_frame < 0);
+               BUG_ON(first_frame + num_frames > pkt->frames);
+               for (f = first_frame; f < first_frame + num_frames; f++)
+                       written[f] = 1;
+       }
+       spin_unlock(&pkt->lock);
+
+       /*
+        * Schedule reads for missing parts of the packet.
+        */
+       for (f = 0; f < pkt->frames; f++) {
+               int p, offset;
+               if (written[f])
+                       continue;
+               bio = pkt->r_bios[f];
+               bio_init(bio);
+               bio->bi_max_vecs = 1;
+               bio->bi_sector = pkt->sector + f * (CD_FRAMESIZE >> 9);
+               bio->bi_bdev = pd->bdev;
+               bio->bi_end_io = pkt_end_io_read;
+               bio->bi_private = pkt;
+
+               p = (f * CD_FRAMESIZE) / PAGE_SIZE;
+               offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
+               VPRINTK("pkt_gather_data: Adding frame %d, page:%p offs:%d\n",
+                       f, pkt->pages[p], offset);
+               if (!bio_add_page(bio, pkt->pages[p], CD_FRAMESIZE, offset))
+                       BUG();
+
+               atomic_inc(&pkt->io_wait);
+               bio->bi_rw = READ;
+               pkt_queue_bio(pd, bio, 0);
+               frames_read++;
+       }
+
+out_account:
+       VPRINTK("pkt_gather_data: need %d frames for zone %llx\n",
+               frames_read, (unsigned long long)pkt->sector);
+       pd->stats.pkt_started++;
+       pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9);
+       pd->stats.secs_w += pd->settings.size;
+}
+
+/*
+ * Find a packet matching zone, or the least recently used packet if
+ * there is no match.
+ */
+static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zone)
+{
+       struct packet_data *pkt;
+
+       list_for_each_entry(pkt, &pd->cdrw.pkt_free_list, list) {
+               if (pkt->sector == zone || pkt->list.next == &pd->cdrw.pkt_free_list) {
+                       list_del_init(&pkt->list);
+                       if (pkt->sector != zone)
+                               pkt->cache_valid = 0;
+                       break;
+               }
+       }
+       return pkt;
+}
+
+static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+       if (pkt->cache_valid) {
+               list_add(&pkt->list, &pd->cdrw.pkt_free_list);
+       } else {
+               list_add_tail(&pkt->list, &pd->cdrw.pkt_free_list);
+       }
+}
+
+/*
+ * recover a failed write, query for relocation if possible
+ *
+ * returns 1 if recovery is possible, or 0 if not
+ *
+ */
+static int pkt_start_recovery(struct packet_data *pkt)
+{
+       /*
+        * FIXME. We need help from the file system to implement
+        * recovery handling.
+        */
+       return 0;
+#if 0
+       struct request *rq = pkt->rq;
+       struct pktcdvd_device *pd = rq->rq_disk->private_data;
+       struct block_device *pkt_bdev;
+       struct super_block *sb = NULL;
+       unsigned long old_block, new_block;
+       sector_t new_sector;
+
+       pkt_bdev = bdget(kdev_t_to_nr(pd->pkt_dev));
+       if (pkt_bdev) {
+               sb = get_super(pkt_bdev);
+               bdput(pkt_bdev);
+       }
+
+       if (!sb)
+               return 0;
+
+       if (!sb->s_op || !sb->s_op->relocate_blocks)
+               goto out;
+
+       old_block = pkt->sector / (CD_FRAMESIZE >> 9);
+       if (sb->s_op->relocate_blocks(sb, old_block, &new_block))
+               goto out;
+
+       new_sector = new_block * (CD_FRAMESIZE >> 9);
+       pkt->sector = new_sector;
+
+       pkt->bio->bi_sector = new_sector;
+       pkt->bio->bi_next = NULL;
+       pkt->bio->bi_flags = 1 << BIO_UPTODATE;
+       pkt->bio->bi_idx = 0;
+
+       BUG_ON(pkt->bio->bi_rw != (1 << BIO_RW));
+       BUG_ON(pkt->bio->bi_vcnt != pkt->frames);
+       BUG_ON(pkt->bio->bi_size != pkt->frames * CD_FRAMESIZE);
+       BUG_ON(pkt->bio->bi_end_io != pkt_end_io_packet_write);
+       BUG_ON(pkt->bio->bi_private != pkt);
+
+       drop_super(sb);
+       return 1;
+
+out:
+       drop_super(sb);
+       return 0;
+#endif
+}
+
+static inline void pkt_set_state(struct packet_data *pkt, enum packet_data_state state)
+{
+#if PACKET_DEBUG > 1
+       static const char *state_name[] = {
+               "IDLE", "WAITING", "READ_WAIT", "WRITE_WAIT", "RECOVERY", "FINISHED"
+       };
+       enum packet_data_state old_state = pkt->state;
+       VPRINTK("pkt %2d : s=%6llx %s -> %s\n", pkt->id, (unsigned long long)pkt->sector,
+               state_name[old_state], state_name[state]);
+#endif
+       pkt->state = state;
+}
+
+/*
+ * Scan the work queue to see if we can start a new packet.
+ * returns non-zero if any work was done.
+ */
+static int pkt_handle_queue(struct pktcdvd_device *pd)
+{
+       struct packet_data *pkt, *p;
+       struct bio *bio = NULL;
+       sector_t zone = 0; /* Suppress gcc warning */
+       struct pkt_rb_node *node, *first_node;
+       struct rb_node *n;
+
+       VPRINTK("handle_queue\n");
+
+       atomic_set(&pd->scan_queue, 0);
+
+       if (list_empty(&pd->cdrw.pkt_free_list)) {
+               VPRINTK("handle_queue: no pkt\n");
+               return 0;
+       }
+
+       /*
+        * Try to find a zone we are not already working on.
+        */
+       spin_lock(&pd->lock);
+       first_node = pkt_rbtree_find(pd, pd->current_sector);
+       if (!first_node) {
+               n = rb_first(&pd->bio_queue);
+               if (n)
+                       first_node = rb_entry(n, struct pkt_rb_node, rb_node);
+       }
+       node = first_node;
+       while (node) {
+               bio = node->bio;
+               zone = ZONE(bio->bi_sector, pd);
+               list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) {
+                       if (p->sector == zone)
+                               goto try_next_bio;
+               }
+               break;
+try_next_bio:
+               node = pkt_rbtree_next(node);
+               if (!node) {
+                       n = rb_first(&pd->bio_queue);
+                       if (n)
+                               node = rb_entry(n, struct pkt_rb_node, rb_node);
+               }
+               if (node == first_node)
+                       node = NULL;
+       }
+       spin_unlock(&pd->lock);
+       if (!bio) {
+               VPRINTK("handle_queue: no bio\n");
+               return 0;
+       }
+
+       pkt = pkt_get_packet_data(pd, zone);
+       BUG_ON(!pkt);
+
+       pd->current_sector = zone + pd->settings.size;
+       pkt->sector = zone;
+       pkt->frames = pd->settings.size >> 2;
+       BUG_ON(pkt->frames > PACKET_MAX_SIZE);
+       pkt->write_size = 0;
+
+       /*
+        * Scan work queue for bios in the same zone and link them
+        * to this packet.
+        */
+       spin_lock(&pd->lock);
+       VPRINTK("pkt_handle_queue: looking for zone %llx\n", (unsigned long long)zone);
+       while ((node = pkt_rbtree_find(pd, zone)) != NULL) {
+               bio = node->bio;
+               VPRINTK("pkt_handle_queue: found zone=%llx\n",
+                       (unsigned long long)ZONE(bio->bi_sector, pd));
+               if (ZONE(bio->bi_sector, pd) != zone)
+                       break;
+               pkt_rbtree_erase(pd, node);
+               spin_lock(&pkt->lock);
+               pkt_add_list_last(bio, &pkt->orig_bios, &pkt->orig_bios_tail);
+               pkt->write_size += bio->bi_size / CD_FRAMESIZE;
+               spin_unlock(&pkt->lock);
+       }
+       spin_unlock(&pd->lock);
+
+       pkt->sleep_time = max(PACKET_WAIT_TIME, 1);
+       pkt_set_state(pkt, PACKET_WAITING_STATE);
+       atomic_set(&pkt->run_sm, 1);
+
+       spin_lock(&pd->cdrw.active_list_lock);
+       list_add(&pkt->list, &pd->cdrw.pkt_active_list);
+       spin_unlock(&pd->cdrw.active_list_lock);
+
+       return 1;
+}
+
+/*
+ * Assemble a bio to write one packet and queue the bio for processing
+ * by the underlying block device.
+ */
+static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+       struct bio *bio;
+       struct page *pages[PACKET_MAX_SIZE];
+       int offsets[PACKET_MAX_SIZE];
+       int f;
+       int frames_write;
+
+       for (f = 0; f < pkt->frames; f++) {
+               pages[f] = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE];
+               offsets[f] = (f * CD_FRAMESIZE) % PAGE_SIZE;
+       }
+
+       /*
+        * Fill-in pages[] and offsets[] with data from orig_bios.
+        */
+       frames_write = 0;
+       spin_lock(&pkt->lock);
+       for (bio = pkt->orig_bios; bio; bio = bio->bi_next) {
+               int segment = bio->bi_idx;
+               int src_offs = 0;
+               int first_frame = (bio->bi_sector - pkt->sector) / (CD_FRAMESIZE >> 9);
+               int num_frames = bio->bi_size / CD_FRAMESIZE;
+               BUG_ON(first_frame < 0);
+               BUG_ON(first_frame + num_frames > pkt->frames);
+               for (f = first_frame; f < first_frame + num_frames; f++) {
+                       struct bio_vec *src_bvl = bio_iovec_idx(bio, segment);
+
+                       while (src_offs >= src_bvl->bv_len) {
+                               src_offs -= src_bvl->bv_len;
+                               segment++;
+                               BUG_ON(segment >= bio->bi_vcnt);
+                               src_bvl = bio_iovec_idx(bio, segment);
+                       }
+
+                       if (src_bvl->bv_len - src_offs >= CD_FRAMESIZE) {
+                               pages[f] = src_bvl->bv_page;
+                               offsets[f] = src_bvl->bv_offset + src_offs;
+                       } else {
+                               pkt_copy_bio_data(bio, segment, src_offs,
+                                                 pages[f], offsets[f]);
+                       }
+                       src_offs += CD_FRAMESIZE;
+                       frames_write++;
+               }
+       }
+       pkt_set_state(pkt, PACKET_WRITE_WAIT_STATE);
+       spin_unlock(&pkt->lock);
+
+       VPRINTK("pkt_start_write: Writing %d frames for zone %llx\n",
+               frames_write, (unsigned long long)pkt->sector);
+       BUG_ON(frames_write != pkt->write_size);
+
+       if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames)) {
+               pkt_make_local_copy(pkt, pages, offsets);
+               pkt->cache_valid = 1;
+       } else {
+               pkt->cache_valid = 0;
+       }
+
+       /* Start the write request */
+       bio_init(pkt->w_bio);
+       pkt->w_bio->bi_max_vecs = PACKET_MAX_SIZE;
+       pkt->w_bio->bi_sector = pkt->sector;
+       pkt->w_bio->bi_bdev = pd->bdev;
+       pkt->w_bio->bi_end_io = pkt_end_io_packet_write;
+       pkt->w_bio->bi_private = pkt;
+       for (f = 0; f < pkt->frames; f++) {
+               if ((f + 1 < pkt->frames) && (pages[f + 1] == pages[f]) &&
+                   (offsets[f + 1] = offsets[f] + CD_FRAMESIZE)) {
+                       if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE * 2, offsets[f]))
+                               BUG();
+                       f++;
+               } else {
+                       if (!bio_add_page(pkt->w_bio, pages[f], CD_FRAMESIZE, offsets[f]))
+                               BUG();
+               }
+       }
+       VPRINTK("pktcdvd: vcnt=%d\n", pkt->w_bio->bi_vcnt);
+
+       atomic_set(&pkt->io_wait, 1);
+       pkt->w_bio->bi_rw = WRITE;
+       pkt_queue_bio(pd, pkt->w_bio, 0);
+}
+
+static void pkt_finish_packet(struct packet_data *pkt, int uptodate)
+{
+       struct bio *bio, *next;
+
+       if (!uptodate)
+               pkt->cache_valid = 0;
+
+       /* Finish all bios corresponding to this packet */
+       bio = pkt->orig_bios;
+       while (bio) {
+               next = bio->bi_next;
+               bio->bi_next = NULL;
+               bio_endio(bio, bio->bi_size, uptodate ? 0 : -EIO);
+               bio = next;
+       }
+       pkt->orig_bios = pkt->orig_bios_tail = NULL;
+}
+
+static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+       int uptodate;
+
+       VPRINTK("run_state_machine: pkt %d\n", pkt->id);
+
+       for (;;) {
+               switch (pkt->state) {
+               case PACKET_WAITING_STATE:
+                       if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0))
+                               return;
+
+                       pkt->sleep_time = 0;
+                       pkt_gather_data(pd, pkt);
+                       pkt_set_state(pkt, PACKET_READ_WAIT_STATE);
+                       break;
+
+               case PACKET_READ_WAIT_STATE:
+                       if (atomic_read(&pkt->io_wait) > 0)
+                               return;
+
+                       if (atomic_read(&pkt->io_errors) > 0) {
+                               pkt_set_state(pkt, PACKET_RECOVERY_STATE);
+                       } else {
+                               pkt_start_write(pd, pkt);
+                       }
+                       break;
+
+               case PACKET_WRITE_WAIT_STATE:
+                       if (atomic_read(&pkt->io_wait) > 0)
+                               return;
+
+                       if (test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags)) {
+                               pkt_set_state(pkt, PACKET_FINISHED_STATE);
+                       } else {
+                               pkt_set_state(pkt, PACKET_RECOVERY_STATE);
+                       }
+                       break;
+
+               case PACKET_RECOVERY_STATE:
+                       if (pkt_start_recovery(pkt)) {
+                               pkt_start_write(pd, pkt);
+                       } else {
+                               VPRINTK("No recovery possible\n");
+                               pkt_set_state(pkt, PACKET_FINISHED_STATE);
+                       }
+                       break;
+
+               case PACKET_FINISHED_STATE:
+                       uptodate = test_bit(BIO_UPTODATE, &pkt->w_bio->bi_flags);
+                       pkt_finish_packet(pkt, uptodate);
+                       return;
+
+               default:
+                       BUG();
+                       break;
+               }
+       }
+}
+
+static void pkt_handle_packets(struct pktcdvd_device *pd)
+{
+       struct packet_data *pkt, *next;
+
+       VPRINTK("pkt_handle_packets\n");
+
+       /*
+        * Run state machine for active packets
+        */
+       list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+               if (atomic_read(&pkt->run_sm) > 0) {
+                       atomic_set(&pkt->run_sm, 0);
+                       pkt_run_state_machine(pd, pkt);
+               }
+       }
+
+       /*
+        * Move no longer active packets to the free list
+        */
+       spin_lock(&pd->cdrw.active_list_lock);
+       list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_active_list, list) {
+               if (pkt->state == PACKET_FINISHED_STATE) {
+                       list_del(&pkt->list);
+                       pkt_put_packet_data(pd, pkt);
+                       pkt_set_state(pkt, PACKET_IDLE_STATE);
+                       atomic_set(&pd->scan_queue, 1);
+               }
+       }
+       spin_unlock(&pd->cdrw.active_list_lock);
+}
+
+static void pkt_count_states(struct pktcdvd_device *pd, int *states)
+{
+       struct packet_data *pkt;
+       int i;
+
+       for (i = 0; i <= PACKET_NUM_STATES; i++)
+               states[i] = 0;
+
+       spin_lock(&pd->cdrw.active_list_lock);
+       list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+               states[pkt->state]++;
+       }
+       spin_unlock(&pd->cdrw.active_list_lock);
+}
+
+/*
+ * kcdrwd is woken up when writes have been queued for one of our
+ * registered devices
+ */
+static int kcdrwd(void *foobar)
+{
+       struct pktcdvd_device *pd = foobar;
+       struct packet_data *pkt;
+       long min_sleep_time, residue;
+
+       set_user_nice(current, -20);
+
+       for (;;) {
+               DECLARE_WAITQUEUE(wait, current);
+
+               /*
+                * Wait until there is something to do
+                */
+               add_wait_queue(&pd->wqueue, &wait);
+               for (;;) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+
+                       /* Check if we need to run pkt_handle_queue */
+                       if (atomic_read(&pd->scan_queue) > 0)
+                               goto work_to_do;
+
+                       /* Check if we need to run the state machine for some packet */
+                       list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+                               if (atomic_read(&pkt->run_sm) > 0)
+                                       goto work_to_do;
+                       }
+
+                       /* Check if we need to process the iosched queues */
+                       if (atomic_read(&pd->iosched.attention) != 0)
+                               goto work_to_do;
+
+                       /* Otherwise, go to sleep */
+                       if (PACKET_DEBUG > 1) {
+                               int states[PACKET_NUM_STATES];
+                               pkt_count_states(pd, states);
+                               VPRINTK("kcdrwd: i:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
+                                       states[0], states[1], states[2], states[3],
+                                       states[4], states[5]);
+                       }
+
+                       min_sleep_time = MAX_SCHEDULE_TIMEOUT;
+                       list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+                               if (pkt->sleep_time && pkt->sleep_time < min_sleep_time)
+                                       min_sleep_time = pkt->sleep_time;
+                       }
+
+                       generic_unplug_device(bdev_get_queue(pd->bdev));
+
+                       VPRINTK("kcdrwd: sleeping\n");
+                       residue = schedule_timeout(min_sleep_time);
+                       VPRINTK("kcdrwd: wake up\n");
+
+                       /* make swsusp happy with our thread */
+                       if (current->flags & PF_FREEZE)
+                               refrigerator(PF_FREEZE);
+
+                       list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+                               if (!pkt->sleep_time)
+                                       continue;
+                               pkt->sleep_time -= min_sleep_time - residue;
+                               if (pkt->sleep_time <= 0) {
+                                       pkt->sleep_time = 0;
+                                       atomic_inc(&pkt->run_sm);
+                               }
+                       }
+
+                       if (signal_pending(current)) {
+                               flush_signals(current);
+                       }
+                       if (kthread_should_stop())
+                               break;
+               }
+work_to_do:
+               set_current_state(TASK_RUNNING);
+               remove_wait_queue(&pd->wqueue, &wait);
+
+               if (kthread_should_stop())
+                       break;
+
+               /*
+                * if pkt_handle_queue returns true, we can queue
+                * another request.
+                */
+               while (pkt_handle_queue(pd))
+                       ;
+
+               /*
+                * Handle packet state machine
+                */
+               pkt_handle_packets(pd);
+
+               /*
+                * Handle iosched queues
+                */
+               pkt_iosched_process_queue(pd);
+       }
+
+       return 0;
+}
+
+static void pkt_print_settings(struct pktcdvd_device *pd)
+{
+       printk("pktcdvd: %s packets, ", pd->settings.fp ? "Fixed" : "Variable");
+       printk("%u blocks, ", pd->settings.size >> 2);
+       printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2');
+}
+
+static int pkt_mode_sense(struct pktcdvd_device *pd, struct packet_command *cgc,
+                         int page_code, int page_control)
+{
+       memset(cgc->cmd, 0, sizeof(cgc->cmd));
+
+       cgc->cmd[0] = GPCMD_MODE_SENSE_10;
+       cgc->cmd[2] = page_code | (page_control << 6);
+       cgc->cmd[7] = cgc->buflen >> 8;
+       cgc->cmd[8] = cgc->buflen & 0xff;
+       cgc->data_direction = CGC_DATA_READ;
+       return pkt_generic_packet(pd, cgc);
+}
+
+static int pkt_mode_select(struct pktcdvd_device *pd, struct packet_command *cgc)
+{
+       memset(cgc->cmd, 0, sizeof(cgc->cmd));
+       memset(cgc->buffer, 0, 2);
+       cgc->cmd[0] = GPCMD_MODE_SELECT_10;
+       cgc->cmd[1] = 0x10;             /* PF */
+       cgc->cmd[7] = cgc->buflen >> 8;
+       cgc->cmd[8] = cgc->buflen & 0xff;
+       cgc->data_direction = CGC_DATA_WRITE;
+       return pkt_generic_packet(pd, cgc);
+}
+
+static int pkt_get_disc_info(struct pktcdvd_device *pd, disc_information *di)
+{
+       struct packet_command cgc;
+       int ret;
+
+       /* set up command and get the disc info */
+       init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);
+       cgc.cmd[0] = GPCMD_READ_DISC_INFO;
+       cgc.cmd[8] = cgc.buflen = 2;
+       cgc.quiet = 1;
+
+       if ((ret = pkt_generic_packet(pd, &cgc)))
+               return ret;
+
+       /* not all drives have the same disc_info length, so requeue
+        * packet with the length the drive tells us it can supply
+        */
+       cgc.buflen = be16_to_cpu(di->disc_information_length) +
+                    sizeof(di->disc_information_length);
+
+       if (cgc.buflen > sizeof(disc_information))
+               cgc.buflen = sizeof(disc_information);
+
+       cgc.cmd[8] = cgc.buflen;
+       return pkt_generic_packet(pd, &cgc);
+}
+
+static int pkt_get_track_info(struct pktcdvd_device *pd, __u16 track, __u8 type, track_information *ti)
+{
+       struct packet_command cgc;
+       int ret;
+
+       init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);
+       cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
+       cgc.cmd[1] = type & 3;
+       cgc.cmd[4] = (track & 0xff00) >> 8;
+       cgc.cmd[5] = track & 0xff;
+       cgc.cmd[8] = 8;
+       cgc.quiet = 1;
+
+       if ((ret = pkt_generic_packet(pd, &cgc)))
+               return ret;
+
+       cgc.buflen = be16_to_cpu(ti->track_information_length) +
+                    sizeof(ti->track_information_length);
+
+       if (cgc.buflen > sizeof(track_information))
+               cgc.buflen = sizeof(track_information);
+
+       cgc.cmd[8] = cgc.buflen;
+       return pkt_generic_packet(pd, &cgc);
+}
+
+static int pkt_get_last_written(struct pktcdvd_device *pd, long *last_written)
+{
+       disc_information di;
+       track_information ti;
+       __u32 last_track;
+       int ret = -1;
+
+       if ((ret = pkt_get_disc_info(pd, &di)))
+               return ret;
+
+       last_track = (di.last_track_msb << 8) | di.last_track_lsb;
+       if ((ret = pkt_get_track_info(pd, last_track, 1, &ti)))
+               return ret;
+
+       /* if this track is blank, try the previous. */
+       if (ti.blank) {
+               last_track--;
+               if ((ret = pkt_get_track_info(pd, last_track, 1, &ti)))
+                       return ret;
+       }
+
+       /* if last recorded field is valid, return it. */
+       if (ti.lra_v) {
+               *last_written = be32_to_cpu(ti.last_rec_address);
+       } else {
+               /* make it up instead */
+               *last_written = be32_to_cpu(ti.track_start) +
+                               be32_to_cpu(ti.track_size);
+               if (ti.free_blocks)
+                       *last_written -= (be32_to_cpu(ti.free_blocks) + 7);
+       }
+       return 0;
+}
+
+/*
+ * write mode select package based on pd->settings
+ */
+static int pkt_set_write_settings(struct pktcdvd_device *pd)
+{
+       struct packet_command cgc;
+       struct request_sense sense;
+       write_param_page *wp;
+       char buffer[128];
+       int ret, size;
+
+       /* doesn't apply to DVD+RW */
+       if (pd->mmc3_profile == 0x1a)
+               return 0;
+
+       memset(buffer, 0, sizeof(buffer));
+       init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ);
+       cgc.sense = &sense;
+       if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) {
+               pkt_dump_sense(&cgc);
+               return ret;
+       }
+
+       size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff));
+       pd->mode_offset = (buffer[6] << 8) | (buffer[7] & 0xff);
+       if (size > sizeof(buffer))
+               size = sizeof(buffer);
+
+       /*
+        * now get it all
+        */
+       init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ);
+       cgc.sense = &sense;
+       if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) {
+               pkt_dump_sense(&cgc);
+               return ret;
+       }
+
+       /*
+        * write page is offset header + block descriptor length
+        */
+       wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset];
+
+       wp->fp = pd->settings.fp;
+       wp->track_mode = pd->settings.track_mode;
+       wp->write_type = pd->settings.write_type;
+       wp->data_block_type = pd->settings.block_mode;
+
+       wp->multi_session = 0;
+
+#ifdef PACKET_USE_LS
+       wp->link_size = 7;
+       wp->ls_v = 1;
+#endif
+
+       if (wp->data_block_type == PACKET_BLOCK_MODE1) {
+               wp->session_format = 0;
+               wp->subhdr2 = 0x20;
+       } else if (wp->data_block_type == PACKET_BLOCK_MODE2) {
+               wp->session_format = 0x20;
+               wp->subhdr2 = 8;
+#if 0
+               wp->mcn[0] = 0x80;
+               memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1);
+#endif
+       } else {
+               /*
+                * paranoia
+                */
+               printk("pktcdvd: write mode wrong %d\n", wp->data_block_type);
+               return 1;
+       }
+       wp->packet_size = cpu_to_be32(pd->settings.size >> 2);
+
+       cgc.buflen = cgc.cmd[8] = size;
+       if ((ret = pkt_mode_select(pd, &cgc))) {
+               pkt_dump_sense(&cgc);
+               return ret;
+       }
+
+       pkt_print_settings(pd);
+       return 0;
+}
+
+/*
+ * 0 -- we can write to this track, 1 -- we can't
+ */
+static int pkt_good_track(track_information *ti)
+{
+       /*
+        * only good for CD-RW at the moment, not DVD-RW
+        */
+
+       /*
+        * FIXME: only for FP
+        */
+       if (ti->fp == 0)
+               return 0;
+
+       /*
+        * "good" settings as per Mt Fuji.
+        */
+       if (ti->rt == 0 && ti->blank == 0 && ti->packet == 1)
+               return 0;
+
+       if (ti->rt == 0 && ti->blank == 1 && ti->packet == 1)
+               return 0;
+
+       if (ti->rt == 1 && ti->blank == 0 && ti->packet == 1)
+               return 0;
+
+       printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet);
+       return 1;
+}
+
+/*
+ * 0 -- we can write to this disc, 1 -- we can't
+ */
+static int pkt_good_disc(struct pktcdvd_device *pd, disc_information *di)
+{
+       switch (pd->mmc3_profile) {
+               case 0x0a: /* CD-RW */
+               case 0xffff: /* MMC3 not supported */
+                       break;
+               case 0x1a: /* DVD+RW */
+               case 0x13: /* DVD-RW */
+                       return 0;
+               default:
+                       printk("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile);
+                       return 1;
+       }
+
+       /*
+        * for disc type 0xff we should probably reserve a new track.
+        * but i'm not sure, should we leave this to user apps? probably.
+        */
+       if (di->disc_type == 0xff) {
+               printk("pktcdvd: Unknown disc. No track?\n");
+               return 1;
+       }
+
+       if (di->disc_type != 0x20 && di->disc_type != 0) {
+               printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type);
+               return 1;
+       }
+
+       if (di->erasable == 0) {
+               printk("pktcdvd: Disc not erasable\n");
+               return 1;
+       }
+
+       if (di->border_status == PACKET_SESSION_RESERVED) {
+               printk("pktcdvd: Can't write to last track (reserved)\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+static int pkt_probe_settings(struct pktcdvd_device *pd)
+{
+       struct packet_command cgc;
+       unsigned char buf[12];
+       disc_information di;
+       track_information ti;
+       int ret, track;
+
+       init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
+       cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
+       cgc.cmd[8] = 8;
+       ret = pkt_generic_packet(pd, &cgc);
+       pd->mmc3_profile = ret ? 0xffff : buf[6] << 8 | buf[7];
+
+       memset(&di, 0, sizeof(disc_information));
+       memset(&ti, 0, sizeof(track_information));
+
+       if ((ret = pkt_get_disc_info(pd, &di))) {
+               printk("failed get_disc\n");
+               return ret;
+       }
+
+       if (pkt_good_disc(pd, &di))
+               return -ENXIO;
+
+       switch (pd->mmc3_profile) {
+               case 0x1a: /* DVD+RW */
+                       printk("pktcdvd: inserted media is DVD+RW\n");
+                       break;
+               case 0x13: /* DVD-RW */
+                       printk("pktcdvd: inserted media is DVD-RW\n");
+                       break;
+               default:
+                       printk("pktcdvd: inserted media is CD-R%s\n", di.erasable ? "W" : "");
+                       break;
+       }
+       pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR;
+
+       track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */
+       if ((ret = pkt_get_track_info(pd, track, 1, &ti))) {
+               printk("pktcdvd: failed get_track\n");
+               return ret;
+       }
+
+       if (pkt_good_track(&ti)) {
+               printk("pktcdvd: can't write to this track\n");
+               return -ENXIO;
+       }
+
+       /*
+        * we keep packet size in 512 byte units, makes it easier to
+        * deal with request calculations.
+        */
+       pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2;
+       if (pd->settings.size == 0) {
+               printk("pktcdvd: detected zero packet size!\n");
+               pd->settings.size = 128;
+       }
+       pd->settings.fp = ti.fp;
+       pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1);
+
+       if (ti.nwa_v) {
+               pd->nwa = be32_to_cpu(ti.next_writable);
+               set_bit(PACKET_NWA_VALID, &pd->flags);
+       }
+
+       /*
+        * in theory we could use lra on -RW media as well and just zero
+        * blocks that haven't been written yet, but in practice that
+        * is just a no-go. we'll use that for -R, naturally.
+        */
+       if (ti.lra_v) {
+               pd->lra = be32_to_cpu(ti.last_rec_address);
+               set_bit(PACKET_LRA_VALID, &pd->flags);
+       } else {
+               pd->lra = 0xffffffff;
+               set_bit(PACKET_LRA_VALID, &pd->flags);
+       }
+
+       /*
+        * fine for now
+        */
+       pd->settings.link_loss = 7;
+       pd->settings.write_type = 0;    /* packet */
+       pd->settings.track_mode = ti.track_mode;
+
+       /*
+        * mode1 or mode2 disc
+        */
+       switch (ti.data_mode) {
+               case PACKET_MODE1:
+                       pd->settings.block_mode = PACKET_BLOCK_MODE1;
+                       break;
+               case PACKET_MODE2:
+                       pd->settings.block_mode = PACKET_BLOCK_MODE2;
+                       break;
+               default:
+                       printk("pktcdvd: unknown data mode\n");
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * enable/disable write caching on drive
+ */
+static int pkt_write_caching(struct pktcdvd_device *pd, int set)
+{
+       struct packet_command cgc;
+       struct request_sense sense;
+       unsigned char buf[64];
+       int ret;
+
+       memset(buf, 0, sizeof(buf));
+       init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
+       cgc.sense = &sense;
+       cgc.buflen = pd->mode_offset + 12;
+
+       /*
+        * caching mode page might not be there, so quiet this command
+        */
+       cgc.quiet = 1;
+
+       if ((ret = pkt_mode_sense(pd, &cgc, GPMODE_WCACHING_PAGE, 0)))
+               return ret;
+
+       buf[pd->mode_offset + 10] |= (!!set << 2);
+
+       cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff));
+       ret = pkt_mode_select(pd, &cgc);
+       if (ret) {
+               printk("pktcdvd: write caching control failed\n");
+               pkt_dump_sense(&cgc);
+       } else if (!ret && set)
+               printk("pktcdvd: enabled write caching on %s\n", pd->name);
+       return ret;
+}
+
+static int pkt_lock_door(struct pktcdvd_device *pd, int lockflag)
+{
+       struct packet_command cgc;
+
+       init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+       cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+       cgc.cmd[4] = lockflag ? 1 : 0;
+       return pkt_generic_packet(pd, &cgc);
+}
+
+/*
+ * Returns drive maximum write speed
+ */
+static int pkt_get_max_speed(struct pktcdvd_device *pd, unsigned *write_speed)
+{
+       struct packet_command cgc;
+       struct request_sense sense;
+       unsigned char buf[256+18];
+       unsigned char *cap_buf;
+       int ret, offset;
+
+       memset(buf, 0, sizeof(buf));
+       cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset];
+       init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN);
+       cgc.sense = &sense;
+
+       ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+       if (ret) {
+               cgc.buflen = pd->mode_offset + cap_buf[1] + 2 +
+                            sizeof(struct mode_page_header);
+               ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+               if (ret) {
+                       pkt_dump_sense(&cgc);
+                       return ret;
+               }
+       }
+
+       offset = 20;                        /* Obsoleted field, used by older drives */
+       if (cap_buf[1] >= 28)
+               offset = 28;                /* Current write speed selected */
+       if (cap_buf[1] >= 30) {
+               /* If the drive reports at least one "Logical Unit Write
+                * Speed Performance Descriptor Block", use the information
+                * in the first block. (contains the highest speed)
+                */
+               int num_spdb = (cap_buf[30] << 8) + cap_buf[31];
+               if (num_spdb > 0)
+                       offset = 34;
+       }
+
+       *write_speed = (cap_buf[offset] << 8) | cap_buf[offset + 1];
+       return 0;
+}
+
+/* These tables from cdrecord - I don't have orange book */
+/* standard speed CD-RW (1-4x) */
+static char clv_to_speed[16] = {
+       /* 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 */
+          0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+/* high speed CD-RW (-10x) */
+static char hs_clv_to_speed[16] = {
+       /* 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 */
+          0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+/* ultra high speed CD-RW */
+static char us_clv_to_speed[16] = {
+       /* 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 */
+          0, 2, 4, 8, 0, 0,16, 0,24,32,40,48, 0, 0, 0, 0
+};
+
+/*
+ * reads the maximum media speed from ATIP
+ */
+static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed)
+{
+       struct packet_command cgc;
+       struct request_sense sense;
+       unsigned char buf[64];
+       unsigned int size, st, sp;
+       int ret;
+
+       init_cdrom_command(&cgc, buf, 2, CGC_DATA_READ);
+       cgc.sense = &sense;
+       cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+       cgc.cmd[1] = 2;
+       cgc.cmd[2] = 4; /* READ ATIP */
+       cgc.cmd[8] = 2;
+       ret = pkt_generic_packet(pd, &cgc);
+       if (ret) {
+               pkt_dump_sense(&cgc);
+               return ret;
+       }
+       size = ((unsigned int) buf[0]<<8) + buf[1] + 2;
+       if (size > sizeof(buf))
+               size = sizeof(buf);
+
+       init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
+       cgc.sense = &sense;
+       cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+       cgc.cmd[1] = 2;
+       cgc.cmd[2] = 4;
+       cgc.cmd[8] = size;
+       ret = pkt_generic_packet(pd, &cgc);
+       if (ret) {
+               pkt_dump_sense(&cgc);
+               return ret;
+       }
+
+       if (!buf[6] & 0x40) {
+               printk("pktcdvd: Disc type is not CD-RW\n");
+               return 1;
+       }
+       if (!buf[6] & 0x4) {
+               printk("pktcdvd: A1 values on media are not valid, maybe not CDRW?\n");
+               return 1;
+       }
+
+       st = (buf[6] >> 3) & 0x7; /* disc sub-type */
+
+       sp = buf[16] & 0xf; /* max speed from ATIP A1 field */
+
+       /* Info from cdrecord */
+       switch (st) {
+               case 0: /* standard speed */
+                       *speed = clv_to_speed[sp];
+                       break;
+               case 1: /* high speed */
+                       *speed = hs_clv_to_speed[sp];
+                       break;
+               case 2: /* ultra high speed */
+                       *speed = us_clv_to_speed[sp];
+                       break;
+               default:
+                       printk("pktcdvd: Unknown disc sub-type %d\n",st);
+                       return 1;
+       }
+       if (*speed) {
+               printk("pktcdvd: Max. media speed: %d\n",*speed);
+               return 0;
+       } else {
+               printk("pktcdvd: Unknown speed %d for sub-type %d\n",sp,st);
+               return 1;
+       }
+}
+
+static int pkt_perform_opc(struct pktcdvd_device *pd)
+{
+       struct packet_command cgc;
+       struct request_sense sense;
+       int ret;
+
+       VPRINTK("pktcdvd: Performing OPC\n");
+
+       init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+       cgc.sense = &sense;
+       cgc.timeout = 60*HZ;
+       cgc.cmd[0] = GPCMD_SEND_OPC;
+       cgc.cmd[1] = 1;
+       if ((ret = pkt_generic_packet(pd, &cgc)))
+               pkt_dump_sense(&cgc);
+       return ret;
+}
+
+static int pkt_open_write(struct pktcdvd_device *pd)
+{
+       int ret;
+       unsigned int write_speed, media_write_speed, read_speed;
+
+       if ((ret = pkt_probe_settings(pd))) {
+               DPRINTK("pktcdvd: %s failed probe\n", pd->name);
+               return -EIO;
+       }
+
+       if ((ret = pkt_set_write_settings(pd))) {
+               DPRINTK("pktcdvd: %s failed saving write settings\n", pd->name);
+               return -EIO;
+       }
+
+       pkt_write_caching(pd, USE_WCACHING);
+
+       if ((ret = pkt_get_max_speed(pd, &write_speed)))
+               write_speed = 16 * 177;
+       switch (pd->mmc3_profile) {
+               case 0x13: /* DVD-RW */
+               case 0x1a: /* DVD+RW */
+                       DPRINTK("pktcdvd: write speed %ukB/s\n", write_speed);
+                       break;
+               default:
+                       if ((ret = pkt_media_speed(pd, &media_write_speed)))
+                               media_write_speed = 16;
+                       write_speed = min(write_speed, media_write_speed * 177);
+                       DPRINTK("pktcdvd: write speed %ux\n", write_speed / 176);
+                       break;
+       }
+       read_speed = write_speed;
+
+       if ((ret = pkt_set_speed(pd, write_speed, read_speed))) {
+               DPRINTK("pktcdvd: %s couldn't set write speed\n", pd->name);
+               return -EIO;
+       }
+       pd->write_speed = write_speed;
+       pd->read_speed = read_speed;
+
+       if ((ret = pkt_perform_opc(pd))) {
+               DPRINTK("pktcdvd: %s Optimum Power Calibration failed\n", pd->name);
+       }
+
+       return 0;
+}
+
+/*
+ * called at open time.
+ */
+static int pkt_open_dev(struct pktcdvd_device *pd, int write)
+{
+       int ret;
+       long lba;
+       request_queue_t *q;
+
+       /*
+        * We need to re-open the cdrom device without O_NONBLOCK to be able
+        * to read/write from/to it. It is already opened in O_NONBLOCK mode
+        * so bdget() can't fail.
+        */
+       bdget(pd->bdev->bd_dev);
+       if ((ret = blkdev_get(pd->bdev, FMODE_READ, O_RDONLY)))
+               goto out;
+
+       if ((ret = pkt_get_last_written(pd, &lba))) {
+               printk("pktcdvd: pkt_get_last_written failed\n");
+               goto out_putdev;
+       }
+
+       set_capacity(pd->disk, lba << 2);
+       set_capacity(pd->bdev->bd_disk, lba << 2);
+       bd_set_size(pd->bdev, (loff_t)lba << 11);
+
+       q = bdev_get_queue(pd->bdev);
+       if (write) {
+               if ((ret = pkt_open_write(pd)))
+                       goto out_putdev;
+               /*
+                * Some CDRW drives can not handle writes larger than one packet,
+                * even if the size is a multiple of the packet size.
+                */
+               spin_lock_irq(q->queue_lock);
+               blk_queue_max_sectors(q, pd->settings.size);
+               spin_unlock_irq(q->queue_lock);
+               set_bit(PACKET_WRITABLE, &pd->flags);
+       } else {
+               pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
+               clear_bit(PACKET_WRITABLE, &pd->flags);
+       }
+
+       if ((ret = pkt_set_segment_merging(pd, q)))
+               goto out_putdev;
+
+       if (write)
+               printk("pktcdvd: %lukB available on disc\n", lba << 1);
+
+       return 0;
+
+out_putdev:
+       blkdev_put(pd->bdev);
+out:
+       return ret;
+}
+
+/*
+ * called when the device is closed. makes sure that the device flushes
+ * the internal cache before we close.
+ */
+static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
+{
+       if (flush && pkt_flush_cache(pd))
+               DPRINTK("pktcdvd: %s not flushing cache\n", pd->name);
+
+       pkt_lock_door(pd, 0);
+
+       pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
+       blkdev_put(pd->bdev);
+}
+
+static struct pktcdvd_device *pkt_find_dev_from_minor(int dev_minor)
+{
+       if (dev_minor >= MAX_WRITERS)
+               return NULL;
+       return pkt_devs[dev_minor];
+}
+
+static int pkt_open(struct inode *inode, struct file *file)
+{
+       struct pktcdvd_device *pd = NULL;
+       int ret;
+
+       VPRINTK("pktcdvd: entering open\n");
+
+       down(&ctl_mutex);
+       pd = pkt_find_dev_from_minor(iminor(inode));
+       if (!pd) {
+               ret = -ENODEV;
+               goto out;
+       }
+       BUG_ON(pd->refcnt < 0);
+
+       pd->refcnt++;
+       if (pd->refcnt == 1) {
+               if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) {
+                       ret = -EIO;
+                       goto out_dec;
+               }
+               /*
+                * needed here as well, since ext2 (among others) may change
+                * the blocksize at mount time
+                */
+               set_blocksize(inode->i_bdev, CD_FRAMESIZE);
+       }
+
+       up(&ctl_mutex);
+       return 0;
+
+out_dec:
+       pd->refcnt--;
+out:
+       VPRINTK("pktcdvd: failed open (%d)\n", ret);
+       up(&ctl_mutex);
+       return ret;
+}
+
+static int pkt_close(struct inode *inode, struct file *file)
+{
+       struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data;
+       int ret = 0;
+
+       down(&ctl_mutex);
+       pd->refcnt--;
+       BUG_ON(pd->refcnt < 0);
+       if (pd->refcnt == 0) {
+               int flush = test_bit(PACKET_WRITABLE, &pd->flags);
+               pkt_release_dev(pd, flush);
+       }
+       up(&ctl_mutex);
+       return ret;
+}
+
+
+static void *psd_pool_alloc(int gfp_mask, void *data)
+{
+       return kmalloc(sizeof(struct packet_stacked_data), gfp_mask);
+}
+
+static void psd_pool_free(void *ptr, void *data)
+{
+       kfree(ptr);
+}
+
+static int pkt_end_io_read_cloned(struct bio *bio, unsigned int bytes_done, int err)
+{
+       struct packet_stacked_data *psd = bio->bi_private;
+       struct pktcdvd_device *pd = psd->pd;
+
+       if (bio->bi_size)
+               return 1;
+
+       bio_put(bio);
+       bio_endio(psd->bio, psd->bio->bi_size, err);
+       mempool_free(psd, psd_pool);
+       pkt_bio_finished(pd);
+       return 0;
+}
+
+static int pkt_make_request(request_queue_t *q, struct bio *bio)
+{
+       struct pktcdvd_device *pd;
+       char b[BDEVNAME_SIZE];
+       sector_t zone;
+       struct packet_data *pkt;
+       int was_empty, blocked_bio;
+       struct pkt_rb_node *node;
+
+       pd = q->queuedata;
+       if (!pd) {
+               printk("pktcdvd: %s incorrect request queue\n", bdevname(bio->bi_bdev, b));
+               goto end_io;
+       }
+
+       /*
+        * Clone READ bios so we can have our own bi_end_io callback.
+        */
+       if (bio_data_dir(bio) == READ) {
+               struct bio *cloned_bio = bio_clone(bio, GFP_NOIO);
+               struct packet_stacked_data *psd = mempool_alloc(psd_pool, GFP_NOIO);
+
+               psd->pd = pd;
+               psd->bio = bio;
+               cloned_bio->bi_bdev = pd->bdev;
+               cloned_bio->bi_private = psd;
+               cloned_bio->bi_end_io = pkt_end_io_read_cloned;
+               pd->stats.secs_r += bio->bi_size >> 9;
+               pkt_queue_bio(pd, cloned_bio, 1);
+               return 0;
+       }
+
+       if (!test_bit(PACKET_WRITABLE, &pd->flags)) {
+               printk("pktcdvd: WRITE for ro device %s (%llu)\n",
+                       pd->name, (unsigned long long)bio->bi_sector);
+               goto end_io;
+       }
+
+       if (!bio->bi_size || (bio->bi_size % CD_FRAMESIZE)) {
+               printk("pktcdvd: wrong bio size\n");
+               goto end_io;
+       }
+
+       blk_queue_bounce(q, &bio);
+
+       zone = ZONE(bio->bi_sector, pd);
+       VPRINTK("pkt_make_request: start = %6llx stop = %6llx\n",
+               (unsigned long long)bio->bi_sector,
+               (unsigned long long)(bio->bi_sector + bio_sectors(bio)));
+
+       /* Check if we have to split the bio */
+       {
+               struct bio_pair *bp;
+               sector_t last_zone;
+               int first_sectors;
+
+               last_zone = ZONE(bio->bi_sector + bio_sectors(bio) - 1, pd);
+               if (last_zone != zone) {
+                       BUG_ON(last_zone != zone + pd->settings.size);
+                       first_sectors = last_zone - bio->bi_sector;
+                       bp = bio_split(bio, bio_split_pool, first_sectors);
+                       BUG_ON(!bp);
+                       pkt_make_request(q, &bp->bio1);
+                       pkt_make_request(q, &bp->bio2);
+                       bio_pair_release(bp);
+                       return 0;
+               }
+       }
+
+       /*
+        * If we find a matching packet in state WAITING or READ_WAIT, we can
+        * just append this bio to that packet.
+        */
+       spin_lock(&pd->cdrw.active_list_lock);
+       blocked_bio = 0;
+       list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+               if (pkt->sector == zone) {
+                       spin_lock(&pkt->lock);
+                       if ((pkt->state == PACKET_WAITING_STATE) ||
+                           (pkt->state == PACKET_READ_WAIT_STATE)) {
+                               pkt_add_list_last(bio, &pkt->orig_bios,
+                                                 &pkt->orig_bios_tail);
+                               pkt->write_size += bio->bi_size / CD_FRAMESIZE;
+                               if ((pkt->write_size >= pkt->frames) &&
+                                   (pkt->state == PACKET_WAITING_STATE)) {
+                                       atomic_inc(&pkt->run_sm);
+                                       wake_up(&pd->wqueue);
+                               }
+                               spin_unlock(&pkt->lock);
+                               spin_unlock(&pd->cdrw.active_list_lock);
+                               return 0;
+                       } else {
+                               blocked_bio = 1;
+                       }
+                       spin_unlock(&pkt->lock);
+               }
+       }
+       spin_unlock(&pd->cdrw.active_list_lock);
+
+       /*
+        * No matching packet found. Store the bio in the work queue.
+        */
+       node = mempool_alloc(pd->rb_pool, GFP_NOIO);
+       BUG_ON(!node);
+       node->bio = bio;
+       spin_lock(&pd->lock);
+       BUG_ON(pd->bio_queue_size < 0);
+       was_empty = (pd->bio_queue_size == 0);
+       pkt_rbtree_insert(pd, node);
+       spin_unlock(&pd->lock);
+
+       /*
+        * Wake up the worker thread.
+        */
+       atomic_set(&pd->scan_queue, 1);
+       if (was_empty) {
+               /* This wake_up is required for correct operation */
+               wake_up(&pd->wqueue);
+       } else if (!list_empty(&pd->cdrw.pkt_free_list) && !blocked_bio) {
+               /*
+                * This wake up is not required for correct operation,
+                * but improves performance in some cases.
+                */
+               wake_up(&pd->wqueue);
+       }
+       return 0;
+end_io:
+       bio_io_error(bio, bio->bi_size);
+       return 0;
+}
+
+
+
+static int pkt_merge_bvec(request_queue_t *q, struct bio *bio, struct bio_vec *bvec)
+{
+       struct pktcdvd_device *pd = q->queuedata;
+       sector_t zone = ZONE(bio->bi_sector, pd);
+       int used = ((bio->bi_sector - zone) << 9) + bio->bi_size;
+       int remaining = (pd->settings.size << 9) - used;
+       int remaining2;
+
+       /*
+        * A bio <= PAGE_SIZE must be allowed. If it crosses a packet
+        * boundary, pkt_make_request() will split the bio.
+        */
+       remaining2 = PAGE_SIZE - bio->bi_size;
+       remaining = max(remaining, remaining2);
+
+       BUG_ON(remaining < 0);
+       return remaining;
+}
+
+static void pkt_init_queue(struct pktcdvd_device *pd)
+{
+       request_queue_t *q = pd->disk->queue;
+
+       blk_queue_make_request(q, pkt_make_request);
+       blk_queue_hardsect_size(q, CD_FRAMESIZE);
+       blk_queue_max_sectors(q, PACKET_MAX_SECTORS);
+       blk_queue_merge_bvec(q, pkt_merge_bvec);
+       q->queuedata = pd;
+}
+
+static int pkt_seq_show(struct seq_file *m, void *p)
+{
+       struct pktcdvd_device *pd = m->private;
+       char *msg;
+       char bdev_buf[BDEVNAME_SIZE];
+       int states[PACKET_NUM_STATES];
+
+       seq_printf(m, "Writer %s mapped to %s:\n", pd->name,
+                  bdevname(pd->bdev, bdev_buf));
+
+       seq_printf(m, "\nSettings:\n");
+       seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2);
+
+       if (pd->settings.write_type == 0)
+               msg = "Packet";
+       else
+               msg = "Unknown";
+       seq_printf(m, "\twrite type:\t\t%s\n", msg);
+
+       seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable");
+       seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss);
+
+       seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode);
+
+       if (pd->settings.block_mode == PACKET_BLOCK_MODE1)
+               msg = "Mode 1";
+       else if (pd->settings.block_mode == PACKET_BLOCK_MODE2)
+               msg = "Mode 2";
+       else
+               msg = "Unknown";
+       seq_printf(m, "\tblock mode:\t\t%s\n", msg);
+
+       seq_printf(m, "\nStatistics:\n");
+       seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started);
+       seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended);
+       seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1);
+       seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1);
+       seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);
+
+       seq_printf(m, "\nMisc:\n");
+       seq_printf(m, "\treference count:\t%d\n", pd->refcnt);
+       seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags);
+       seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed);
+       seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed);
+       seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset);
+       seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset);
+
+       seq_printf(m, "\nQueue state:\n");
+       seq_printf(m, "\tbios queued:\t\t%d\n", pd->bio_queue_size);
+       seq_printf(m, "\tbios pending:\t\t%d\n", atomic_read(&pd->cdrw.pending_bios));
+       seq_printf(m, "\tcurrent sector:\t\t0x%llx\n", (unsigned long long)pd->current_sector);
+
+       pkt_count_states(pd, states);
+       seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
+                  states[0], states[1], states[2], states[3], states[4], states[5]);
+
+       return 0;
+}
+
+static int pkt_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, pkt_seq_show, PDE(inode)->data);
+}
+
+static struct file_operations pkt_proc_fops = {
+       .open   = pkt_seq_open,
+       .read   = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release
+};
+
+static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
+{
+       int i;
+       int ret = 0;
+       char b[BDEVNAME_SIZE];
+       struct proc_dir_entry *proc;
+       struct block_device *bdev;
+
+       if (pd->pkt_dev == dev) {
+               printk("pktcdvd: Recursive setup not allowed\n");
+               return -EBUSY;
+       }
+       for (i = 0; i < MAX_WRITERS; i++) {
+               struct pktcdvd_device *pd2 = pkt_devs[i];
+               if (!pd2)
+                       continue;
+               if (pd2->bdev->bd_dev == dev) {
+                       printk("pktcdvd: %s already setup\n", bdevname(pd2->bdev, b));
+                       return -EBUSY;
+               }
+               if (pd2->pkt_dev == dev) {
+                       printk("pktcdvd: Can't chain pktcdvd devices\n");
+                       return -EBUSY;
+               }
+       }
+
+       bdev = bdget(dev);
+       if (!bdev)
+               return -ENOMEM;
+       ret = blkdev_get(bdev, FMODE_READ, O_RDONLY | O_NONBLOCK);
+       if (ret)
+               return ret;
+
+       /* This is safe, since we have a reference from open(). */
+       __module_get(THIS_MODULE);
+
+       if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
+               printk("pktcdvd: not enough memory for buffers\n");
+               ret = -ENOMEM;
+               goto out_mem;
+       }
+
+       pd->bdev = bdev;
+       set_blocksize(bdev, CD_FRAMESIZE);
+
+       pkt_init_queue(pd);
+
+       atomic_set(&pd->cdrw.pending_bios, 0);
+       pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name);
+       if (IS_ERR(pd->cdrw.thread)) {
+               printk("pktcdvd: can't start kernel thread\n");
+               ret = -ENOMEM;
+               goto out_thread;
+       }
+
+       proc = create_proc_entry(pd->name, 0, pkt_proc);
+       if (proc) {
+               proc->data = pd;
+               proc->proc_fops = &pkt_proc_fops;
+       }
+       DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b));
+       return 0;
+
+out_thread:
+       pkt_shrink_pktlist(pd);
+out_mem:
+       blkdev_put(bdev);
+       /* This is safe: open() is still holding a reference. */
+       module_put(THIS_MODULE);
+       return ret;
+}
+
+static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data;
+
+       VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode));
+       BUG_ON(!pd);
+
+       switch (cmd) {
+       /*
+        * forward selected CDROM ioctls to CD-ROM, for UDF
+        */
+       case CDROMMULTISESSION:
+       case CDROMREADTOCENTRY:
+       case CDROM_LAST_WRITTEN:
+       case CDROM_SEND_PACKET:
+       case SCSI_IOCTL_SEND_COMMAND:
+               return ioctl_by_bdev(pd->bdev, cmd, arg);
+
+       case CDROMEJECT:
+               /*
+                * The door gets locked when the device is opened, so we
+                * have to unlock it or else the eject command fails.
+                */
+               pkt_lock_door(pd, 0);
+               return ioctl_by_bdev(pd->bdev, cmd, arg);
+
+       default:
+               printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd);
+               return -ENOTTY;
+       }
+
+       return 0;
+}
+
+static int pkt_media_changed(struct gendisk *disk)
+{
+       struct pktcdvd_device *pd = disk->private_data;
+       struct gendisk *attached_disk;
+
+       if (!pd)
+               return 0;
+       if (!pd->bdev)
+               return 0;
+       attached_disk = pd->bdev->bd_disk;
+       if (!attached_disk)
+               return 0;
+       return attached_disk->fops->media_changed(attached_disk);
+}
+
+static struct block_device_operations pktcdvd_ops = {
+       .owner =                THIS_MODULE,
+       .open =                 pkt_open,
+       .release =              pkt_close,
+       .ioctl =                pkt_ioctl,
+       .media_changed =        pkt_media_changed,
+};
+
+/*
+ * Set up mapping from pktcdvd device to CD-ROM device.
+ */
+static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd)
+{
+       int idx;
+       int ret = -ENOMEM;
+       struct pktcdvd_device *pd;
+       struct gendisk *disk;
+       dev_t dev = new_decode_dev(ctrl_cmd->dev);
+
+       for (idx = 0; idx < MAX_WRITERS; idx++)
+               if (!pkt_devs[idx])
+                       break;
+       if (idx == MAX_WRITERS) {
+               printk("pktcdvd: max %d writers supported\n", MAX_WRITERS);
+               return -EBUSY;
+       }
+
+       pd = kmalloc(sizeof(struct pktcdvd_device), GFP_KERNEL);
+       if (!pd)
+               return ret;
+       memset(pd, 0, sizeof(struct pktcdvd_device));
+
+       pd->rb_pool = mempool_create(PKT_RB_POOL_SIZE, pkt_rb_alloc, pkt_rb_free, NULL);
+       if (!pd->rb_pool)
+               goto out_mem;
+
+       disk = alloc_disk(1);
+       if (!disk)
+               goto out_mem;
+       pd->disk = disk;
+
+       spin_lock_init(&pd->lock);
+       spin_lock_init(&pd->iosched.lock);
+       sprintf(pd->name, "pktcdvd%d", idx);
+       init_waitqueue_head(&pd->wqueue);
+       pd->bio_queue = RB_ROOT;
+
+       disk->major = pkt_major;
+       disk->first_minor = idx;
+       disk->fops = &pktcdvd_ops;
+       disk->flags = GENHD_FL_REMOVABLE;
+       sprintf(disk->disk_name, "pktcdvd%d", idx);
+       disk->private_data = pd;
+       disk->queue = blk_alloc_queue(GFP_KERNEL);
+       if (!disk->queue)
+               goto out_mem2;
+
+       pd->pkt_dev = MKDEV(disk->major, disk->first_minor);
+       ret = pkt_new_dev(pd, dev);
+       if (ret)
+               goto out_new_dev;
+
+       add_disk(disk);
+       pkt_devs[idx] = pd;
+       ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
+       return 0;
+
+out_new_dev:
+       blk_put_queue(disk->queue);
+out_mem2:
+       put_disk(disk);
+out_mem:
+       if (pd->rb_pool)
+               mempool_destroy(pd->rb_pool);
+       kfree(pd);
+       return ret;
+}
+
+/*
+ * Tear down mapping from pktcdvd device to CD-ROM device.
+ */
+static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd)
+{
+       struct pktcdvd_device *pd;
+       int idx;
+       dev_t pkt_dev = new_decode_dev(ctrl_cmd->pkt_dev);
+
+       for (idx = 0; idx < MAX_WRITERS; idx++) {
+               pd = pkt_devs[idx];
+               if (pd && (pd->pkt_dev == pkt_dev))
+                       break;
+       }
+       if (idx == MAX_WRITERS) {
+               DPRINTK("pktcdvd: dev not setup\n");
+               return -ENXIO;
+       }
+
+       if (pd->refcnt > 0)
+               return -EBUSY;
+
+       if (!IS_ERR(pd->cdrw.thread))
+               kthread_stop(pd->cdrw.thread);
+
+       blkdev_put(pd->bdev);
+
+       pkt_shrink_pktlist(pd);
+
+       remove_proc_entry(pd->name, pkt_proc);
+       DPRINTK("pktcdvd: writer %s unmapped\n", pd->name);
+
+       del_gendisk(pd->disk);
+       blk_put_queue(pd->disk->queue);
+       put_disk(pd->disk);
+
+       pkt_devs[idx] = NULL;
+       mempool_destroy(pd->rb_pool);
+       kfree(pd);
+
+       /* This is safe: open() is still holding a reference. */
+       module_put(THIS_MODULE);
+       return 0;
+}
+
+static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd)
+{
+       struct pktcdvd_device *pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index);
+       if (pd) {
+               ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev);
+               ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
+       } else {
+               ctrl_cmd->dev = 0;
+               ctrl_cmd->pkt_dev = 0;
+       }
+       ctrl_cmd->num_devices = MAX_WRITERS;
+}
+
+static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       struct pkt_ctrl_command ctrl_cmd;
+       int ret = 0;
+
+       if (cmd != PACKET_CTRL_CMD)
+               return -ENOTTY;
+
+       if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command)))
+               return -EFAULT;
+
+       switch (ctrl_cmd.command) {
+       case PKT_CTRL_CMD_SETUP:
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+               down(&ctl_mutex);
+               ret = pkt_setup_dev(&ctrl_cmd);
+               up(&ctl_mutex);
+               break;
+       case PKT_CTRL_CMD_TEARDOWN:
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+               down(&ctl_mutex);
+               ret = pkt_remove_dev(&ctrl_cmd);
+               up(&ctl_mutex);
+               break;
+       case PKT_CTRL_CMD_STATUS:
+               down(&ctl_mutex);
+               pkt_get_status(&ctrl_cmd);
+               up(&ctl_mutex);
+               break;
+       default:
+               return -ENOTTY;
+       }
+
+       if (copy_to_user(argp, &ctrl_cmd, sizeof(struct pkt_ctrl_command)))
+               return -EFAULT;
+       return ret;
+}
+
+
+static struct file_operations pkt_ctl_fops = {
+       .ioctl   = pkt_ctl_ioctl,
+       .owner   = THIS_MODULE,
+};
+
+static struct miscdevice pkt_misc = {
+       .minor          = MISC_DYNAMIC_MINOR,
+       .name           = "pktcdvd",
+       .devfs_name     = "pktcdvd/control",
+       .fops           = &pkt_ctl_fops
+};
+
+int pkt_init(void)
+{
+       int ret;
+
+       psd_pool = mempool_create(PSD_POOL_SIZE, psd_pool_alloc, psd_pool_free, NULL);
+       if (!psd_pool)
+               return -ENOMEM;
+
+       ret = register_blkdev(pkt_major, "pktcdvd");
+       if (ret < 0) {
+               printk("pktcdvd: Unable to register block device\n");
+               goto out2;
+       }
+       if (!pkt_major)
+               pkt_major = ret;
+
+       ret = misc_register(&pkt_misc);
+       if (ret) {
+               printk("pktcdvd: Unable to register misc device\n");
+               goto out;
+       }
+
+       init_MUTEX(&ctl_mutex);
+
+       pkt_proc = proc_mkdir("pktcdvd", proc_root_driver);
+
+       DPRINTK("pktcdvd: %s\n", VERSION_CODE);
+       return 0;
+
+out:
+       unregister_blkdev(pkt_major, "pktcdvd");
+out2:
+       mempool_destroy(psd_pool);
+       return ret;
+}
+
+void pkt_exit(void)
+{
+       remove_proc_entry("pktcdvd", proc_root_driver);
+       misc_deregister(&pkt_misc);
+       unregister_blkdev(pkt_major, "pktcdvd");
+       mempool_destroy(psd_pool);
+}
+
+MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives");
+MODULE_AUTHOR("Jens Axboe <axboe@suse.de>");
+MODULE_LICENSE("GPL");
+
+module_init(pkt_init);
+module_exit(pkt_exit);
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
new file mode 100644 (file)
index 0000000..2771c86
--- /dev/null
@@ -0,0 +1,657 @@
+/*
+ *
+ *  Digianswer Bluetooth USB driver
+ *
+ *  Copyright (C) 2004-2005  Marcel Holtmann <marcel@holtmann.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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef CONFIG_BT_HCIBPA10X_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "0.8"
+
+static int ignore = 0;
+
+static struct usb_device_id bpa10x_table[] = {
+       /* Tektronix BPA 100/105 (Digianswer) */
+       { USB_DEVICE(0x08fd, 0x0002) },
+
+       { }     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bpa10x_table);
+
+#define BPA10X_CMD_EP          0x00
+#define BPA10X_EVT_EP          0x81
+#define BPA10X_TX_EP           0x02
+#define BPA10X_RX_EP           0x82
+
+#define BPA10X_CMD_BUF_SIZE    252
+#define BPA10X_EVT_BUF_SIZE    16
+#define BPA10X_TX_BUF_SIZE     384
+#define BPA10X_RX_BUF_SIZE     384
+
+struct bpa10x_data {
+       struct hci_dev          *hdev;
+       struct usb_device       *udev;
+
+       rwlock_t                lock;
+
+       struct sk_buff_head     cmd_queue;
+       struct urb              *cmd_urb;
+       struct urb              *evt_urb;
+       struct sk_buff          *evt_skb;
+       unsigned int            evt_len;
+
+       struct sk_buff_head     tx_queue;
+       struct urb              *tx_urb;
+       struct urb              *rx_urb;
+};
+
+#define HCI_VENDOR_HDR_SIZE    5
+
+struct hci_vendor_hdr {
+       __u8    type;
+       __u16   snum;
+       __u16   dlen;
+} __attribute__ ((packed));
+
+static void bpa10x_recv_bulk(struct bpa10x_data *data, unsigned char *buf, int count)
+{
+       struct hci_acl_hdr *ah;
+       struct hci_sco_hdr *sh;
+       struct hci_vendor_hdr *vh;
+       struct sk_buff *skb;
+       int len;
+
+       while (count) {
+               switch (*buf++) {
+               case HCI_ACLDATA_PKT:
+                       ah = (struct hci_acl_hdr *) buf;
+                       len = HCI_ACL_HDR_SIZE + __le16_to_cpu(ah->dlen);
+                       skb = bt_skb_alloc(len, GFP_ATOMIC);
+                       if (skb) {
+                               memcpy(skb_put(skb, len), buf, len);
+                               skb->dev = (void *) data->hdev;
+                               skb->pkt_type = HCI_ACLDATA_PKT;
+                               hci_recv_frame(skb);
+                       }
+                       break;
+
+               case HCI_SCODATA_PKT:
+                       sh = (struct hci_sco_hdr *) buf;
+                       len = HCI_SCO_HDR_SIZE + sh->dlen;
+                       skb = bt_skb_alloc(len, GFP_ATOMIC);
+                       if (skb) {
+                               memcpy(skb_put(skb, len), buf, len);
+                               skb->dev = (void *) data->hdev;
+                               skb->pkt_type = HCI_SCODATA_PKT;
+                               hci_recv_frame(skb);
+                       }
+                       break;
+
+               case HCI_VENDOR_PKT:
+                       vh = (struct hci_vendor_hdr *) buf;
+                       len = HCI_VENDOR_HDR_SIZE + __le16_to_cpu(vh->dlen);
+                       skb = bt_skb_alloc(len, GFP_ATOMIC);
+                       if (skb) {
+                               memcpy(skb_put(skb, len), buf, len);
+                               skb->dev = (void *) data->hdev;
+                               skb->pkt_type = HCI_VENDOR_PKT;
+                               hci_recv_frame(skb);
+                       }
+                       break;
+
+               default:
+                       len = count - 1;
+                       break;
+               }
+
+               buf   += len;
+               count -= (len + 1);
+       }
+}
+
+static int bpa10x_recv_event(struct bpa10x_data *data, unsigned char *buf, int size)
+{
+       BT_DBG("data %p buf %p size %d", data, buf, size);
+
+       if (data->evt_skb) {
+               struct sk_buff *skb = data->evt_skb;
+
+               memcpy(skb_put(skb, size), buf, size);
+
+               if (skb->len == data->evt_len) {
+                       data->evt_skb = NULL;
+                       data->evt_len = 0;
+                       hci_recv_frame(skb);
+               }
+       } else {
+               struct sk_buff *skb;
+               struct hci_event_hdr *hdr;
+               unsigned char pkt_type;
+               int pkt_len = 0;
+
+               if (size < HCI_EVENT_HDR_SIZE + 1) {
+                       BT_ERR("%s event packet block with size %d is too short",
+                                                       data->hdev->name, size);
+                       return -EILSEQ;
+               }
+
+               pkt_type = *buf++;
+               size--;
+
+               if (pkt_type != HCI_EVENT_PKT) {
+                       BT_ERR("%s unexpected event packet start byte 0x%02x",
+                                                       data->hdev->name, pkt_type);
+                       return -EPROTO;
+               }
+
+               hdr = (struct hci_event_hdr *) buf;
+               pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+
+               skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
+               if (!skb) {
+                       BT_ERR("%s no memory for new event packet",
+                                                       data->hdev->name);
+                       return -ENOMEM;
+               }
+
+               skb->dev = (void *) data->hdev;
+               skb->pkt_type = pkt_type;
+
+               memcpy(skb_put(skb, size), buf, size);
+
+               if (pkt_len == size) {
+                       hci_recv_frame(skb);
+               } else {
+                       data->evt_skb = skb;
+                       data->evt_len = pkt_len;
+               }
+       }
+
+       return 0;
+}
+
+static void bpa10x_wakeup(struct bpa10x_data *data)
+{
+       struct urb *urb;
+       struct sk_buff *skb;
+       int err;
+
+       BT_DBG("data %p", data);
+
+       urb = data->cmd_urb;
+       if (urb->status == -EINPROGRESS)
+               skb = NULL;
+       else
+               skb = skb_dequeue(&data->cmd_queue);
+
+       if (skb) {
+               struct usb_ctrlrequest *cr;
+
+               if (skb->len > BPA10X_CMD_BUF_SIZE) {
+                       BT_ERR("%s command packet with size %d is too big",
+                                                       data->hdev->name, skb->len);
+                       kfree_skb(skb);
+                       return;
+               }
+
+               cr = (struct usb_ctrlrequest *) urb->setup_packet;
+               cr->wLength = __cpu_to_le16(skb->len);
+
+               memcpy(urb->transfer_buffer, skb->data, skb->len);
+               urb->transfer_buffer_length = skb->len;
+
+               err = usb_submit_urb(urb, GFP_ATOMIC);
+               if (err < 0 && err != -ENODEV) {
+                       BT_ERR("%s submit failed for command urb %p with error %d",
+                                                       data->hdev->name, urb, err);
+                       skb_queue_head(&data->cmd_queue, skb);
+               } else
+                       kfree_skb(skb);
+       }
+
+       urb = data->tx_urb;
+       if (urb->status == -EINPROGRESS)
+               skb = NULL;
+       else
+               skb = skb_dequeue(&data->tx_queue);
+
+       if (skb) {
+               memcpy(urb->transfer_buffer, skb->data, skb->len);
+               urb->transfer_buffer_length = skb->len;
+
+               err = usb_submit_urb(urb, GFP_ATOMIC);
+               if (err < 0 && err != -ENODEV) {
+                       BT_ERR("%s submit failed for command urb %p with error %d",
+                                                       data->hdev->name, urb, err);
+                       skb_queue_head(&data->tx_queue, skb);
+               } else
+                       kfree_skb(skb);
+       }
+}
+
+static void bpa10x_complete(struct urb *urb, struct pt_regs *regs)
+{
+       struct bpa10x_data *data = urb->context;
+       unsigned char *buf = urb->transfer_buffer;
+       int err, count = urb->actual_length;
+
+       BT_DBG("data %p urb %p buf %p count %d", data, urb, buf, count);
+
+       read_lock(&data->lock);
+
+       if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+               goto unlock;
+
+       if (urb->status < 0 || !count)
+               goto resubmit;
+
+       if (usb_pipein(urb->pipe)) {
+               data->hdev->stat.byte_rx += count;
+
+               if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)
+                       bpa10x_recv_event(data, buf, count);
+
+               if (usb_pipetype(urb->pipe) == PIPE_BULK)
+                       bpa10x_recv_bulk(data, buf, count);
+       } else {
+               data->hdev->stat.byte_tx += count;
+
+               bpa10x_wakeup(data);
+       }
+
+resubmit:
+       if (usb_pipein(urb->pipe)) {
+               err = usb_submit_urb(urb, GFP_ATOMIC);
+               if (err < 0 && err != -ENODEV) {
+                       BT_ERR("%s urb %p type %d resubmit status %d",
+                               data->hdev->name, urb, usb_pipetype(urb->pipe), err);
+               }
+       }
+
+unlock:
+       read_unlock(&data->lock);
+}
+
+static inline struct urb *bpa10x_alloc_urb(struct usb_device *udev, unsigned int pipe, size_t size, int flags, void *data)
+{
+       struct urb *urb;
+       struct usb_ctrlrequest *cr;
+       unsigned char *buf;
+
+       BT_DBG("udev %p data %p", udev, data);
+
+       urb = usb_alloc_urb(0, flags);
+       if (!urb)
+               return NULL;
+
+       buf = kmalloc(size, flags);
+       if (!buf) {
+               usb_free_urb(urb);
+               return NULL;
+       }
+
+       switch (usb_pipetype(pipe)) {
+       case PIPE_CONTROL:
+               cr = kmalloc(sizeof(*cr), flags);
+               if (!cr) {
+                       kfree(buf);
+                       usb_free_urb(urb);
+                       return NULL;
+               }
+
+               cr->bRequestType = USB_TYPE_VENDOR;
+               cr->bRequest     = 0;
+               cr->wIndex       = 0;
+               cr->wValue       = 0;
+               cr->wLength      = __cpu_to_le16(0);
+
+               usb_fill_control_urb(urb, udev, pipe, (void *) cr, buf, 0, bpa10x_complete, data);
+               break;
+
+       case PIPE_INTERRUPT:
+               usb_fill_int_urb(urb, udev, pipe, buf, size, bpa10x_complete, data, 1);
+               break;
+
+       case PIPE_BULK:
+               usb_fill_bulk_urb(urb, udev, pipe, buf, size, bpa10x_complete, data);
+               break;
+
+       default:
+               kfree(buf);
+               usb_free_urb(urb);
+               return NULL;
+       }
+
+       return urb;
+}
+
+static inline void bpa10x_free_urb(struct urb *urb)
+{
+       BT_DBG("urb %p", urb);
+
+       if (!urb)
+               return;
+
+       if (urb->setup_packet)
+               kfree(urb->setup_packet);
+
+       if (urb->transfer_buffer)
+               kfree(urb->transfer_buffer);
+
+       usb_free_urb(urb);
+}
+
+static int bpa10x_open(struct hci_dev *hdev)
+{
+       struct bpa10x_data *data = hdev->driver_data;
+       struct usb_device *udev = data->udev;
+       unsigned long flags;
+       int err;
+
+       BT_DBG("hdev %p data %p", hdev, data);
+
+       if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+               return 0;
+
+       data->cmd_urb = bpa10x_alloc_urb(udev, usb_sndctrlpipe(udev, BPA10X_CMD_EP),
+                                       BPA10X_CMD_BUF_SIZE, GFP_KERNEL, data);
+       if (!data->cmd_urb) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       data->evt_urb = bpa10x_alloc_urb(udev, usb_rcvintpipe(udev, BPA10X_EVT_EP),
+                                       BPA10X_EVT_BUF_SIZE, GFP_KERNEL, data);
+       if (!data->evt_urb) {
+               bpa10x_free_urb(data->cmd_urb);
+               err = -ENOMEM;
+               goto done;
+       }
+
+       data->rx_urb = bpa10x_alloc_urb(udev, usb_rcvbulkpipe(udev, BPA10X_RX_EP),
+                                       BPA10X_RX_BUF_SIZE, GFP_KERNEL, data);
+       if (!data->rx_urb) {
+               bpa10x_free_urb(data->evt_urb);
+               bpa10x_free_urb(data->cmd_urb);
+               err = -ENOMEM;
+               goto done;
+       }
+
+       data->tx_urb = bpa10x_alloc_urb(udev, usb_sndbulkpipe(udev, BPA10X_TX_EP),
+                                       BPA10X_TX_BUF_SIZE, GFP_KERNEL, data);
+       if (!data->rx_urb) {
+               bpa10x_free_urb(data->rx_urb);
+               bpa10x_free_urb(data->evt_urb);
+               bpa10x_free_urb(data->cmd_urb);
+               err = -ENOMEM;
+               goto done;
+       }
+
+       write_lock_irqsave(&data->lock, flags);
+
+       err = usb_submit_urb(data->evt_urb, GFP_ATOMIC);
+       if (err < 0) {
+               BT_ERR("%s submit failed for event urb %p with error %d",
+                                       data->hdev->name, data->evt_urb, err);
+       } else {
+               err = usb_submit_urb(data->rx_urb, GFP_ATOMIC);
+               if (err < 0) {
+                       BT_ERR("%s submit failed for rx urb %p with error %d",
+                                       data->hdev->name, data->evt_urb, err);
+                       usb_kill_urb(data->evt_urb);
+               }
+       }
+
+       write_unlock_irqrestore(&data->lock, flags);
+
+done:
+       if (err < 0)
+               clear_bit(HCI_RUNNING, &hdev->flags);
+
+       return err;
+}
+
+static int bpa10x_close(struct hci_dev *hdev)
+{
+       struct bpa10x_data *data = hdev->driver_data;
+       unsigned long flags;
+
+       BT_DBG("hdev %p data %p", hdev, data);
+
+       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+               return 0;
+
+       write_lock_irqsave(&data->lock, flags);
+
+       skb_queue_purge(&data->cmd_queue);
+       usb_kill_urb(data->cmd_urb);
+       usb_kill_urb(data->evt_urb);
+       usb_kill_urb(data->rx_urb);
+       usb_kill_urb(data->tx_urb);
+
+       write_unlock_irqrestore(&data->lock, flags);
+
+       bpa10x_free_urb(data->cmd_urb);
+       bpa10x_free_urb(data->evt_urb);
+       bpa10x_free_urb(data->rx_urb);
+       bpa10x_free_urb(data->tx_urb);
+
+       return 0;
+}
+
+static int bpa10x_flush(struct hci_dev *hdev)
+{
+       struct bpa10x_data *data = hdev->driver_data;
+
+       BT_DBG("hdev %p data %p", hdev, data);
+
+       skb_queue_purge(&data->cmd_queue);
+
+       return 0;
+}
+
+static int bpa10x_send_frame(struct sk_buff *skb)
+{
+       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+       struct bpa10x_data *data;
+
+       BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len);
+
+       if (!hdev) {
+               BT_ERR("Frame for unknown HCI device");
+               return -ENODEV;
+       }
+
+       if (!test_bit(HCI_RUNNING, &hdev->flags))
+               return -EBUSY;
+
+       data = hdev->driver_data;
+
+       /* Prepend skb with frame type */
+       memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
+
+       switch (skb->pkt_type) {
+       case HCI_COMMAND_PKT:
+               hdev->stat.cmd_tx++;
+               skb_queue_tail(&data->cmd_queue, skb);
+               break;
+
+       case HCI_ACLDATA_PKT:
+               hdev->stat.acl_tx++;
+               skb_queue_tail(&data->tx_queue, skb);
+               break;
+
+       case HCI_SCODATA_PKT:
+               hdev->stat.sco_tx++;
+               skb_queue_tail(&data->tx_queue, skb);
+               break;
+       };
+
+       read_lock(&data->lock);
+
+       bpa10x_wakeup(data);
+
+       read_unlock(&data->lock);
+
+       return 0;
+}
+
+static void bpa10x_destruct(struct hci_dev *hdev)
+{
+       struct bpa10x_data *data = hdev->driver_data;
+
+       BT_DBG("hdev %p data %p", hdev, data);
+
+       kfree(data);
+}
+
+static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct hci_dev *hdev;
+       struct bpa10x_data *data;
+       int err;
+
+       BT_DBG("intf %p id %p", intf, id);
+
+       if (ignore)
+               return -ENODEV;
+
+       data = kmalloc(sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               BT_ERR("Can't allocate data structure");
+               return -ENOMEM;
+       }
+
+       memset(data, 0, sizeof(*data));
+
+       data->udev = udev;
+
+       rwlock_init(&data->lock);
+
+       skb_queue_head_init(&data->cmd_queue);
+       skb_queue_head_init(&data->tx_queue);
+
+       hdev = hci_alloc_dev();
+       if (!hdev) {
+               BT_ERR("Can't allocate HCI device");
+               kfree(data);
+               return -ENOMEM;
+       }
+
+       data->hdev = hdev;
+
+       hdev->type = HCI_USB;
+       hdev->driver_data = data;
+       SET_HCIDEV_DEV(hdev, &intf->dev);
+
+       hdev->open      = bpa10x_open;
+       hdev->close     = bpa10x_close;
+       hdev->flush     = bpa10x_flush;
+       hdev->send      = bpa10x_send_frame;
+       hdev->destruct  = bpa10x_destruct;
+
+       hdev->owner = THIS_MODULE;
+
+       err = hci_register_dev(hdev);
+       if (err < 0) {
+               BT_ERR("Can't register HCI device");
+               kfree(data);
+               hci_free_dev(hdev);
+               return err;
+       }
+
+       usb_set_intfdata(intf, data);
+
+       return 0;
+}
+
+static void bpa10x_disconnect(struct usb_interface *intf)
+{
+       struct bpa10x_data *data = usb_get_intfdata(intf);
+       struct hci_dev *hdev = data->hdev;
+
+       BT_DBG("intf %p", intf);
+
+       if (!hdev)
+               return;
+
+       usb_set_intfdata(intf, NULL);
+
+       if (hci_unregister_dev(hdev) < 0)
+               BT_ERR("Can't unregister HCI device %s", hdev->name);
+
+       hci_free_dev(hdev);
+}
+
+static struct usb_driver bpa10x_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "bpa10x",
+       .probe          = bpa10x_probe,
+       .disconnect     = bpa10x_disconnect,
+       .id_table       = bpa10x_table,
+};
+
+static int __init bpa10x_init(void)
+{
+       int err;
+
+       BT_INFO("Digianswer Bluetooth USB driver ver %s", VERSION);
+
+       err = usb_register(&bpa10x_driver);
+       if (err < 0)
+               BT_ERR("Failed to register USB driver");
+
+       return err;
+}
+
+static void __exit bpa10x_exit(void)
+{
+       usb_deregister(&bpa10x_driver);
+}
+
+module_init(bpa10x_init);
+module_exit(bpa10x_exit);
+
+module_param(ignore, bool, 0644);
+MODULE_PARM_DESC(ignore, "Ignore devices from the matching table");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/drm/ati_pcigart.c b/drivers/char/drm/ati_pcigart.c
new file mode 100644 (file)
index 0000000..fdca187
--- /dev/null
@@ -0,0 +1,208 @@
+/**
+ * \file ati_pcigart.h 
+ * ATI PCI GART support
+ *
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com
+ *
+ * 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
+ * PRECISION INSIGHT 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"
+
+#if PAGE_SIZE == 65536
+# define ATI_PCIGART_TABLE_ORDER       0
+# define ATI_PCIGART_TABLE_PAGES       (1 << 0)
+#elif PAGE_SIZE == 16384
+# define ATI_PCIGART_TABLE_ORDER       1
+# define ATI_PCIGART_TABLE_PAGES       (1 << 1)
+#elif PAGE_SIZE == 8192
+# define ATI_PCIGART_TABLE_ORDER       2
+# define ATI_PCIGART_TABLE_PAGES       (1 << 2)
+#elif PAGE_SIZE == 4096
+# define ATI_PCIGART_TABLE_ORDER       3
+# define ATI_PCIGART_TABLE_PAGES       (1 << 3)
+#else
+# error - PAGE_SIZE not 64K, 16K, 8K or 4K
+#endif
+
+# define ATI_MAX_PCIGART_PAGES         8192    /**< 32 MB aperture, 4K pages */
+# define ATI_PCIGART_PAGE_SIZE         4096    /**< PCI GART page size */
+
+unsigned long drm_ati_alloc_pcigart_table( void )
+{
+       unsigned long address;
+       struct page *page;
+       int i;
+       DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+       address = __get_free_pages( GFP_KERNEL, ATI_PCIGART_TABLE_ORDER );
+       if ( address == 0UL ) {
+               return 0;
+       }
+
+       page = virt_to_page( address );
+
+       for ( i = 0 ; i < ATI_PCIGART_TABLE_PAGES ; i++, page++ ) {
+               get_page(page);
+               SetPageReserved( page );
+       }
+
+       DRM_DEBUG( "%s: returning 0x%08lx\n", __FUNCTION__, address );
+       return address;
+}
+
+static void drm_ati_free_pcigart_table( unsigned long address )
+{
+       struct page *page;
+       int i;
+       DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+       page = virt_to_page( address );
+
+       for ( i = 0 ; i < ATI_PCIGART_TABLE_PAGES ; i++, page++ ) {
+               __put_page(page);
+               ClearPageReserved( page );
+       }
+
+       free_pages( address, ATI_PCIGART_TABLE_ORDER );
+}
+
+int drm_ati_pcigart_cleanup( drm_device_t *dev,
+                             unsigned long addr,
+                             dma_addr_t bus_addr)
+{
+       drm_sg_mem_t *entry = dev->sg;
+       unsigned long pages;
+       int i;
+
+       /* we need to support large memory configurations */
+       if ( !entry ) {
+               DRM_ERROR( "no scatter/gather memory!\n" );
+               return 0;
+       }
+
+       if ( bus_addr ) {
+               pci_unmap_single(dev->pdev, bus_addr,
+                                ATI_PCIGART_TABLE_PAGES * PAGE_SIZE,
+                                PCI_DMA_TODEVICE);
+
+               pages = ( entry->pages <= ATI_MAX_PCIGART_PAGES )
+                       ? entry->pages : ATI_MAX_PCIGART_PAGES;
+
+               for ( i = 0 ; i < pages ; i++ ) {
+                       if ( !entry->busaddr[i] ) break;
+                       pci_unmap_single(dev->pdev, entry->busaddr[i],
+                                        PAGE_SIZE, PCI_DMA_TODEVICE);
+               }
+       }
+
+       if ( addr ) {
+               drm_ati_free_pcigart_table( addr );
+       }
+
+       return 1;
+}
+EXPORT_SYMBOL(drm_ati_pcigart_cleanup);
+
+int drm_ati_pcigart_init( drm_device_t *dev,
+                          unsigned long *addr,
+                          dma_addr_t *bus_addr)
+{
+       drm_sg_mem_t *entry = dev->sg;
+       unsigned long address = 0;
+       unsigned long pages;
+       u32 *pci_gart, page_base, bus_address = 0;
+       int i, j, ret = 0;
+
+       if ( !entry ) {
+               DRM_ERROR( "no scatter/gather memory!\n" );
+               goto done;
+       }
+
+       address = drm_ati_alloc_pcigart_table();
+       if ( !address ) {
+               DRM_ERROR( "cannot allocate PCI GART page!\n" );
+               goto done;
+       }
+
+       if ( !dev->pdev ) {
+               DRM_ERROR( "PCI device unknown!\n" );
+               goto done;
+       }
+
+       bus_address = pci_map_single(dev->pdev, (void *)address,
+                                 ATI_PCIGART_TABLE_PAGES * PAGE_SIZE,
+                                 PCI_DMA_TODEVICE);
+       if (bus_address == 0) {
+               DRM_ERROR( "unable to map PCIGART pages!\n" );
+               drm_ati_free_pcigart_table( address );
+               address = 0;
+               goto done;
+       }
+
+       pci_gart = (u32 *)address;
+
+       pages = ( entry->pages <= ATI_MAX_PCIGART_PAGES )
+               ? entry->pages : ATI_MAX_PCIGART_PAGES;
+
+       memset( pci_gart, 0, ATI_MAX_PCIGART_PAGES * sizeof(u32) );
+
+       for ( i = 0 ; i < pages ; i++ ) {
+               /* we need to support large memory configurations */
+               entry->busaddr[i] = pci_map_single(dev->pdev,
+                                          page_address( entry->pagelist[i] ),
+                                          PAGE_SIZE,
+                                          PCI_DMA_TODEVICE);
+               if (entry->busaddr[i] == 0) {
+                       DRM_ERROR( "unable to map PCIGART pages!\n" );
+                       drm_ati_pcigart_cleanup( dev, address, bus_address );
+                       address = 0;
+                       bus_address = 0;
+                       goto done;
+               }
+               page_base = (u32) entry->busaddr[i];
+
+               for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) {
+                       *pci_gart++ = cpu_to_le32( page_base );
+                       page_base += ATI_PCIGART_PAGE_SIZE;
+               }
+       }
+
+       ret = 1;
+
+#if defined(__i386__) || defined(__x86_64__)
+       wbinvd();
+#else
+       mb();
+#endif
+
+done:
+       *addr = address;
+       *bus_addr = bus_address;
+       return ret;
+}
+EXPORT_SYMBOL(drm_ati_pcigart_init);
diff --git a/drivers/char/drm/drm_agpsupport.c b/drivers/char/drm/drm_agpsupport.c
new file mode 100644 (file)
index 0000000..ea3ed21
--- /dev/null
@@ -0,0 +1,439 @@
+/**
+ * \file drm_agpsupport.h 
+ * DRM support for AGP/GART backend
+ *    
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Copyright 1999 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 <linux/module.h>
+
+#if __OS_HAS_AGP
+
+/**
+ * AGP information ioctl.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a (output) drm_agp_info structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Verifies the AGP device has been initialized and acquired and fills in the
+ * drm_agp_info structure with the information in drm_agp_head::agp_info.
+ */
+int drm_agp_info(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_AGP_KERN     *kern;
+       drm_agp_info_t   info;
+
+       if (!dev->agp || !dev->agp->acquired)
+               return -EINVAL;
+
+       kern                   = &dev->agp->agp_info;
+       info.agp_version_major = kern->version.major;
+       info.agp_version_minor = kern->version.minor;
+       info.mode              = kern->mode;
+       info.aperture_base     = kern->aper_base;
+       info.aperture_size     = kern->aper_size * 1024 * 1024;
+       info.memory_allowed    = kern->max_memory << PAGE_SHIFT;
+       info.memory_used       = kern->current_memory << PAGE_SHIFT;
+       info.id_vendor         = kern->device->vendor;
+       info.id_device         = kern->device->device;
+
+       if (copy_to_user((drm_agp_info_t __user *)arg, &info, sizeof(info)))
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * Acquire the AGP device (ioctl).
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument.
+ * \return zero on success or a negative number on failure. 
+ *
+ * Verifies the AGP device hasn't been acquired before and calls
+ * agp_acquire().
+ */
+int drm_agp_acquire(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;
+       int              retcode;
+
+       if (!dev->agp)
+               return -ENODEV;
+       if (dev->agp->acquired)
+               return -EBUSY;
+       if ((retcode = agp_backend_acquire()))
+               return retcode;
+       dev->agp->acquired = 1;
+       return 0;
+}
+
+/**
+ * Release the AGP device (ioctl).
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument.
+ * \return zero on success or a negative number on failure.
+ *
+ * Verifies the AGP device has been acquired and calls agp_backend_release().
+ */
+int drm_agp_release(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;
+
+       if (!dev->agp || !dev->agp->acquired)
+               return -EINVAL;
+       agp_backend_release();
+       dev->agp->acquired = 0;
+       return 0;
+
+}
+
+/**
+ * Release the AGP device.
+ *
+ * Calls agp_backend_release().
+ */
+void drm_agp_do_release(void)
+{
+  agp_backend_release();
+}
+
+/**
+ * Enable the AGP bus.
+ * 
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_agp_mode structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Verifies the AGP device has been acquired but not enabled, and calls
+ * agp_enable().
+ */
+int drm_agp_enable(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_agp_mode_t   mode;
+
+       if (!dev->agp || !dev->agp->acquired)
+               return -EINVAL;
+
+       if (copy_from_user(&mode, (drm_agp_mode_t __user *)arg, sizeof(mode)))
+               return -EFAULT;
+
+       dev->agp->mode    = mode.mode;
+       agp_enable(mode.mode);
+       dev->agp->base    = dev->agp->agp_info.aper_base;
+       dev->agp->enabled = 1;
+       return 0;
+}
+
+/**
+ * Allocate AGP memory.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_agp_buffer structure.
+ * \return zero on success or a negative number on failure.
+ * 
+ * Verifies the AGP device is present and has been acquired, allocates the
+ * memory via alloc_agp() and creates a drm_agp_mem entry for it.
+ */
+int drm_agp_alloc(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_agp_buffer_t request;
+       drm_agp_mem_t    *entry;
+       DRM_AGP_MEM      *memory;
+       unsigned long    pages;
+       u32              type;
+       drm_agp_buffer_t __user *argp = (void __user *)arg;
+
+       if (!dev->agp || !dev->agp->acquired)
+               return -EINVAL;
+       if (copy_from_user(&request, argp, sizeof(request)))
+               return -EFAULT;
+       if (!(entry = drm_alloc(sizeof(*entry), DRM_MEM_AGPLISTS)))
+               return -ENOMEM;
+
+       memset(entry, 0, sizeof(*entry));
+
+       pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE;
+       type = (u32) request.type;
+
+       if (!(memory = drm_alloc_agp(pages, type))) {
+               drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+               return -ENOMEM;
+       }
+
+       entry->handle    = (unsigned long)memory->key + 1;
+       entry->memory    = memory;
+       entry->bound     = 0;
+       entry->pages     = pages;
+       entry->prev      = NULL;
+       entry->next      = dev->agp->memory;
+       if (dev->agp->memory)
+               dev->agp->memory->prev = entry;
+       dev->agp->memory = entry;
+
+       request.handle   = entry->handle;
+       request.physical = memory->physical;
+
+       if (copy_to_user(argp, &request, sizeof(request))) {
+               dev->agp->memory       = entry->next;
+               dev->agp->memory->prev = NULL;
+               drm_free_agp(memory, pages);
+               drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+               return -EFAULT;
+       }
+       return 0;
+}
+
+/**
+ * Search for the AGP memory entry associated with a handle.
+ *
+ * \param dev DRM device structure.
+ * \param handle AGP memory handle.
+ * \return pointer to the drm_agp_mem structure associated with \p handle.
+ * 
+ * Walks through drm_agp_head::memory until finding a matching handle.
+ */
+static drm_agp_mem_t *drm_agp_lookup_entry(drm_device_t *dev,
+                                           unsigned long handle)
+{
+       drm_agp_mem_t *entry;
+
+       for (entry = dev->agp->memory; entry; entry = entry->next) {
+               if (entry->handle == handle)
+                       return entry;
+       }
+       return NULL;
+}
+
+/**
+ * Unbind AGP memory from the GATT (ioctl).
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_agp_binding structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Verifies the AGP device is present and acquired, looks-up the AGP memory
+ * entry and passes it to the unbind_agp() function.
+ */
+int drm_agp_unbind(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_agp_binding_t request;
+       drm_agp_mem_t     *entry;
+       int ret;
+
+       if (!dev->agp || !dev->agp->acquired)
+               return -EINVAL;
+       if (copy_from_user(&request, (drm_agp_binding_t __user *)arg, sizeof(request)))
+               return -EFAULT;
+       if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+               return -EINVAL;
+       if (!entry->bound)
+               return -EINVAL;
+       ret = drm_unbind_agp(entry->memory);
+       if (ret == 0)
+           entry->bound = 0;
+       return ret;
+}
+
+/**
+ * Bind AGP memory into the GATT (ioctl)
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_agp_binding structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Verifies the AGP device is present and has been acquired and that no memory
+ * is currently bound into the GATT. Looks-up the AGP memory entry and passes
+ * it to bind_agp() function.
+ */
+int drm_agp_bind(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_agp_binding_t request;
+       drm_agp_mem_t     *entry;
+       int               retcode;
+       int               page;
+
+       if (!dev->agp || !dev->agp->acquired)
+               return -EINVAL;
+       if (copy_from_user(&request, (drm_agp_binding_t __user *)arg, sizeof(request)))
+               return -EFAULT;
+       if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+               return -EINVAL;
+       if (entry->bound)
+               return -EINVAL;
+       page = (request.offset + PAGE_SIZE - 1) / PAGE_SIZE;
+       if ((retcode = drm_bind_agp(entry->memory, page)))
+               return retcode;
+       entry->bound = dev->agp->base + (page << PAGE_SHIFT);
+       DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n",
+                 dev->agp->base, entry->bound);
+       return 0;
+}
+
+/**
+ * Free AGP memory (ioctl).
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_agp_buffer structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Verifies the AGP device is present and has been acquired and looks up the
+ * AGP memory entry. If the memory it's currently bound, unbind it via
+ * unbind_agp(). Frees it via free_agp() as well as the entry itself
+ * and unlinks from the doubly linked list it's inserted in.
+ */
+int drm_agp_free(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_agp_buffer_t request;
+       drm_agp_mem_t    *entry;
+
+       if (!dev->agp || !dev->agp->acquired)
+               return -EINVAL;
+       if (copy_from_user(&request, (drm_agp_buffer_t __user *)arg, sizeof(request)))
+               return -EFAULT;
+       if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+               return -EINVAL;
+       if (entry->bound)
+               drm_unbind_agp(entry->memory);
+
+       if (entry->prev)
+               entry->prev->next = entry->next;
+       else
+               dev->agp->memory = entry->next;
+
+       if (entry->next)
+               entry->next->prev = entry->prev;
+
+       drm_free_agp(entry->memory, entry->pages);
+       drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+       return 0;
+}
+
+/**
+ * Initialize the AGP resources.
+ *
+ * \return pointer to a drm_agp_head structure.
+ *
+ */
+drm_agp_head_t *drm_agp_init(void)
+{
+       drm_agp_head_t *head         = NULL;
+
+       if (!(head = drm_alloc(sizeof(*head), DRM_MEM_AGPLISTS)))
+               return NULL;
+       memset((void *)head, 0, sizeof(*head));
+       agp_copy_info(&head->agp_info);
+       if (head->agp_info.chipset == NOT_SUPPORTED) {
+               drm_free(head, sizeof(*head), DRM_MEM_AGPLISTS);
+               return NULL;
+       }
+       head->memory = NULL;
+#if LINUX_VERSION_CODE <= 0x020408
+       head->cant_use_aperture = 0;
+       head->page_mask = ~(0xfff);
+#else
+       head->cant_use_aperture = head->agp_info.cant_use_aperture;
+       head->page_mask = head->agp_info.page_mask;
+#endif
+
+       return head;
+}
+
+/** Calls agp_allocate_memory() */
+DRM_AGP_MEM *drm_agp_allocate_memory(size_t pages, u32 type)
+{
+       return agp_allocate_memory(pages, type);
+}
+
+/** Calls agp_free_memory() */
+int drm_agp_free_memory(DRM_AGP_MEM *handle)
+{
+       if (!handle)
+               return 0;
+       agp_free_memory(handle);
+       return 1;
+}
+
+/** Calls agp_bind_memory() */
+int drm_agp_bind_memory(DRM_AGP_MEM *handle, off_t start)
+{
+       if (!handle)
+               return -EINVAL;
+       return agp_bind_memory(handle, start);
+}
+
+/** Calls agp_unbind_memory() */
+int drm_agp_unbind_memory(DRM_AGP_MEM *handle)
+{
+       if (!handle)
+               return -EINVAL;
+       return agp_unbind_memory(handle);
+}
+
+#endif /* __OS_HAS_AGP */
diff --git a/drivers/char/drm/drm_auth.c b/drivers/char/drm/drm_auth.c
new file mode 100644 (file)
index 0000000..777c631
--- /dev/null
@@ -0,0 +1,230 @@
+/**
+ * \file drm_auth.h 
+ * IOCTLs for authentication
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
+ *
+ * Copyright 1999 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"
+
+/**
+ * Generate a hash key from a magic.
+ *
+ * \param magic magic.
+ * \return hash key.
+ *
+ * The key is the modulus of the hash table size, #DRM_HASH_SIZE, which must be
+ * a power of 2.
+ */
+static int drm_hash_magic(drm_magic_t magic)
+{
+       return magic & (DRM_HASH_SIZE-1);
+}
+
+/**
+ * Find the file with the given magic number.
+ *
+ * \param dev DRM device.
+ * \param magic magic number.
+ *
+ * Searches in drm_device::magiclist within all files with the same hash key
+ * the one with matching magic number, while holding the drm_device::struct_sem
+ * lock.
+ */
+static drm_file_t *drm_find_file(drm_device_t *dev, drm_magic_t magic)
+{
+       drm_file_t        *retval = NULL;
+       drm_magic_entry_t *pt;
+       int               hash    = drm_hash_magic(magic);
+
+       down(&dev->struct_sem);
+       for (pt = dev->magiclist[hash].head; pt; pt = pt->next) {
+               if (pt->magic == magic) {
+                       retval = pt->priv;
+                       break;
+               }
+       }
+       up(&dev->struct_sem);
+       return retval;
+}
+
+/**
+ * Adds a magic number.
+ * 
+ * \param dev DRM device.
+ * \param priv file private data.
+ * \param magic magic number.
+ *
+ * Creates a drm_magic_entry structure and appends to the linked list
+ * associated the magic number hash key in drm_device::magiclist, while holding
+ * the drm_device::struct_sem lock.
+ */
+int drm_add_magic(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic)
+{
+       int               hash;
+       drm_magic_entry_t *entry;
+
+       DRM_DEBUG("%d\n", magic);
+
+       hash         = drm_hash_magic(magic);
+       entry        = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
+       if (!entry) return -ENOMEM;
+       memset(entry, 0, sizeof(*entry));
+       entry->magic = magic;
+       entry->priv  = priv;
+       entry->next  = NULL;
+
+       down(&dev->struct_sem);
+       if (dev->magiclist[hash].tail) {
+               dev->magiclist[hash].tail->next = entry;
+               dev->magiclist[hash].tail       = entry;
+       } else {
+               dev->magiclist[hash].head       = entry;
+               dev->magiclist[hash].tail       = entry;
+       }
+       up(&dev->struct_sem);
+
+       return 0;
+}
+
+/**
+ * Remove a magic number.
+ * 
+ * \param dev DRM device.
+ * \param magic magic number.
+ *
+ * Searches and unlinks the entry in drm_device::magiclist with the magic
+ * number hash key, while holding the drm_device::struct_sem lock.
+ */
+int drm_remove_magic(drm_device_t *dev, drm_magic_t magic)
+{
+       drm_magic_entry_t *prev = NULL;
+       drm_magic_entry_t *pt;
+       int               hash;
+
+
+       DRM_DEBUG("%d\n", magic);
+       hash = drm_hash_magic(magic);
+
+       down(&dev->struct_sem);
+       for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) {
+               if (pt->magic == magic) {
+                       if (dev->magiclist[hash].head == pt) {
+                               dev->magiclist[hash].head = pt->next;
+                       }
+                       if (dev->magiclist[hash].tail == pt) {
+                               dev->magiclist[hash].tail = prev;
+                       }
+                       if (prev) {
+                               prev->next = pt->next;
+                       }
+                       up(&dev->struct_sem);
+                       return 0;
+               }
+       }
+       up(&dev->struct_sem);
+
+       drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+
+       return -EINVAL;
+}
+
+/**
+ * Get a unique magic number (ioctl).
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a resulting drm_auth structure.
+ * \return zero on success, or a negative number on failure.
+ *
+ * If there is a magic number in drm_file::magic then use it, otherwise
+ * searches an unique non-zero magic number and add it associating it with \p
+ * filp.
+ */
+int drm_getmagic(struct inode *inode, struct file *filp,
+                 unsigned int cmd, unsigned long arg)
+{
+       static drm_magic_t sequence = 0;
+       static DEFINE_SPINLOCK(lock);
+       drm_file_t         *priv    = filp->private_data;
+       drm_device_t       *dev     = priv->dev;
+       drm_auth_t         auth;
+
+                               /* Find unique magic */
+       if (priv->magic) {
+               auth.magic = priv->magic;
+       } else {
+               do {
+                       spin_lock(&lock);
+                       if (!sequence) ++sequence; /* reserve 0 */
+                       auth.magic = sequence++;
+                       spin_unlock(&lock);
+               } while (drm_find_file(dev, auth.magic));
+               priv->magic = auth.magic;
+               drm_add_magic(dev, priv, auth.magic);
+       }
+
+       DRM_DEBUG("%u\n", auth.magic);
+       if (copy_to_user((drm_auth_t __user *)arg, &auth, sizeof(auth)))
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * Authenticate with a magic.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_auth structure.
+ * \return zero if authentication successed, or a negative number otherwise.
+ *
+ * Checks if \p filp is associated with the magic number passed in \arg.
+ */
+int drm_authmagic(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_auth_t         auth;
+       drm_file_t         *file;
+
+       if (copy_from_user(&auth, (drm_auth_t __user *)arg, sizeof(auth)))
+               return -EFAULT;
+       DRM_DEBUG("%u\n", auth.magic);
+       if ((file = drm_find_file(dev, auth.magic))) {
+               file->authenticated = 1;
+               drm_remove_magic(dev, auth.magic);
+               return 0;
+       }
+       return -EINVAL;
+}
diff --git a/drivers/char/drm/drm_bufs.c b/drivers/char/drm/drm_bufs.c
new file mode 100644 (file)
index 0000000..78320ac
--- /dev/null
@@ -0,0 +1,1270 @@
+/**
+ * \file drm_bufs.h 
+ * Generic buffer template
+ * 
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Thu Nov 23 03:10:50 2000 by gareth@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 <linux/vmalloc.h>
+#include "drmP.h"
+
+/**
+ * Compute size order.  Returns the exponent of the smaller power of two which
+ * is greater or equal to given number.
+ * 
+ * \param size size.
+ * \return order.
+ *
+ * \todo Can be made faster.
+ */
+int drm_order( unsigned long size )
+{
+       int order;
+       unsigned long tmp;
+
+       for (order = 0, tmp = size >> 1; tmp; tmp >>= 1, order++)
+               ;
+
+       if (size & (size - 1))
+               ++order;
+
+       return order;
+}
+EXPORT_SYMBOL(drm_order);
+
+/**
+ * Ioctl to specify a range of memory that is available for mapping by a non-root process.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_map structure.
+ * \return zero on success or a negative value on error.
+ *
+ * Adjusts the memory offset to its absolute value according to the mapping
+ * type.  Adds the map to the map list drm_device::maplist. Adds MTRR's where
+ * applicable and if supported by the kernel.
+ */
+int drm_addmap( 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_map_t *map;
+       drm_map_t __user *argp = (void __user *)arg;
+       drm_map_list_t *list;
+
+       if ( !(filp->f_mode & 3) ) return -EACCES; /* Require read/write */
+
+       map = drm_alloc( sizeof(*map), DRM_MEM_MAPS );
+       if ( !map )
+               return -ENOMEM;
+
+       if ( copy_from_user( map, argp, sizeof(*map) ) ) {
+               drm_free( map, sizeof(*map), DRM_MEM_MAPS );
+               return -EFAULT;
+       }
+
+       /* Only allow shared memory to be removable since we only keep enough
+        * book keeping information about shared memory to allow for removal
+        * when processes fork.
+        */
+       if ( (map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM ) {
+               drm_free( map, sizeof(*map), DRM_MEM_MAPS );
+               return -EINVAL;
+       }
+       DRM_DEBUG( "offset = 0x%08lx, size = 0x%08lx, type = %d\n",
+                  map->offset, map->size, map->type );
+       if ( (map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK)) ) {
+               drm_free( map, sizeof(*map), DRM_MEM_MAPS );
+               return -EINVAL;
+       }
+       map->mtrr   = -1;
+       map->handle = NULL;
+
+       switch ( map->type ) {
+       case _DRM_REGISTERS:
+       case _DRM_FRAME_BUFFER:
+#if !defined(__sparc__) && !defined(__alpha__) && !defined(__ia64__)
+               if ( map->offset + map->size < map->offset ||
+                    map->offset < virt_to_phys(high_memory) ) {
+                       drm_free( map, sizeof(*map), DRM_MEM_MAPS );
+                       return -EINVAL;
+               }
+#endif
+#ifdef __alpha__
+               map->offset += dev->hose->mem_space->start;
+#endif
+               if (drm_core_has_MTRR(dev)) {
+                       if ( map->type == _DRM_FRAME_BUFFER ||
+                            (map->flags & _DRM_WRITE_COMBINING) ) {
+                               map->mtrr = mtrr_add( map->offset, map->size,
+                                                     MTRR_TYPE_WRCOMB, 1 );
+                       }
+               }
+               if (map->type == _DRM_REGISTERS)
+                       map->handle = drm_ioremap( map->offset, map->size,
+                                                   dev );
+               break;
+
+       case _DRM_SHM:
+               map->handle = vmalloc_32(map->size);
+               DRM_DEBUG( "%lu %d %p\n",
+                          map->size, drm_order( map->size ), map->handle );
+               if ( !map->handle ) {
+                       drm_free( map, sizeof(*map), DRM_MEM_MAPS );
+                       return -ENOMEM;
+               }
+               map->offset = (unsigned long)map->handle;
+               if ( map->flags & _DRM_CONTAINS_LOCK ) {
+                       /* Prevent a 2nd X Server from creating a 2nd lock */
+                       if (dev->lock.hw_lock != NULL) {
+                               vfree( map->handle );
+                               drm_free( map, sizeof(*map), DRM_MEM_MAPS );
+                               return -EBUSY;
+                       }
+                       dev->sigdata.lock =
+                       dev->lock.hw_lock = map->handle; /* Pointer to lock */
+               }
+               break;
+       case _DRM_AGP:
+               if (drm_core_has_AGP(dev)) {
+#ifdef __alpha__
+                       map->offset += dev->hose->mem_space->start;
+#endif
+                       map->offset += dev->agp->base;
+                       map->mtrr   = dev->agp->agp_mtrr; /* for getmap */
+               }
+               break;
+       case _DRM_SCATTER_GATHER:
+               if (!dev->sg) {
+                       drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+                       return -EINVAL;
+               }
+               map->offset += dev->sg->handle;
+               break;
+
+       default:
+               drm_free( map, sizeof(*map), DRM_MEM_MAPS );
+               return -EINVAL;
+       }
+
+       list = drm_alloc(sizeof(*list), DRM_MEM_MAPS);
+       if(!list) {
+               drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+               return -EINVAL;
+       }
+       memset(list, 0, sizeof(*list));
+       list->map = map;
+
+       down(&dev->struct_sem);
+       list_add(&list->head, &dev->maplist->head);
+       up(&dev->struct_sem);
+
+       if ( copy_to_user( argp, map, sizeof(*map) ) )
+               return -EFAULT;
+       if ( map->type != _DRM_SHM ) {
+               if ( copy_to_user( &argp->handle,
+                                  &map->offset,
+                                  sizeof(map->offset) ) )
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+
+/**
+ * Remove a map private from list and deallocate resources if the mapping
+ * isn't in use.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_map_t structure.
+ * \return zero on success or a negative value on error.
+ *
+ * Searches the map on drm_device::maplist, removes it from the list, see if
+ * its being used, and free any associate resource (such as MTRR's) if it's not
+ * being on use.
+ *
+ * \sa addmap().
+ */
+int drm_rmmap(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;
+       struct list_head *list;
+       drm_map_list_t *r_list = NULL;
+       drm_vma_entry_t *pt, *prev;
+       drm_map_t *map;
+       drm_map_t request;
+       int found_maps = 0;
+
+       if (copy_from_user(&request, (drm_map_t __user *)arg,
+                          sizeof(request))) {
+               return -EFAULT;
+       }
+
+       down(&dev->struct_sem);
+       list = &dev->maplist->head;
+       list_for_each(list, &dev->maplist->head) {
+               r_list = list_entry(list, drm_map_list_t, head);
+
+               if(r_list->map &&
+                  r_list->map->handle == request.handle &&
+                  r_list->map->flags & _DRM_REMOVABLE) break;
+       }
+
+       /* List has wrapped around to the head pointer, or its empty we didn't
+        * find anything.
+        */
+       if(list == (&dev->maplist->head)) {
+               up(&dev->struct_sem);
+               return -EINVAL;
+       }
+       map = r_list->map;
+       list_del(list);
+       drm_free(list, sizeof(*list), DRM_MEM_MAPS);
+
+       for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) {
+               if (pt->vma->vm_private_data == map) found_maps++;
+       }
+
+       if(!found_maps) {
+               switch (map->type) {
+               case _DRM_REGISTERS:
+               case _DRM_FRAME_BUFFER:
+                 if (drm_core_has_MTRR(dev)) {
+                               if (map->mtrr >= 0) {
+                                       int retcode;
+                                       retcode = mtrr_del(map->mtrr,
+                                                          map->offset,
+                                                          map->size);
+                                       DRM_DEBUG("mtrr_del = %d\n", retcode);
+                               }
+                       }
+                       drm_ioremapfree(map->handle, map->size, dev);
+                       break;
+               case _DRM_SHM:
+                       vfree(map->handle);
+                       break;
+               case _DRM_AGP:
+               case _DRM_SCATTER_GATHER:
+                       break;
+               }
+               drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+       }
+       up(&dev->struct_sem);
+       return 0;
+}
+
+/**
+ * Cleanup after an error on one of the addbufs() functions.
+ *
+ * \param entry buffer entry where the error occurred.
+ *
+ * Frees any pages and buffers associated with the given entry.
+ */
+static void drm_cleanup_buf_error(drm_device_t *dev, drm_buf_entry_t *entry)
+{
+       int i;
+
+       if (entry->seg_count) {
+               for (i = 0; i < entry->seg_count; i++) {
+                       if (entry->seglist[i]) {
+                               drm_free_pages(entry->seglist[i],
+                                               entry->page_order,
+                                               DRM_MEM_DMA);
+                       }
+               }
+               drm_free(entry->seglist,
+                         entry->seg_count *
+                         sizeof(*entry->seglist),
+                         DRM_MEM_SEGS);
+
+               entry->seg_count = 0;
+       }
+
+       if (entry->buf_count) {
+               for (i = 0; i < entry->buf_count; i++) {
+                       if (entry->buflist[i].dev_private) {
+                               drm_free(entry->buflist[i].dev_private,
+                                         entry->buflist[i].dev_priv_size,
+                                         DRM_MEM_BUFS);
+                       }
+               }
+               drm_free(entry->buflist,
+                         entry->buf_count *
+                         sizeof(*entry->buflist),
+                         DRM_MEM_BUFS);
+
+               entry->buf_count = 0;
+       }
+}
+
+#if __OS_HAS_AGP
+/**
+ * Add AGP buffers for DMA transfers (ioctl).
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_buf_desc_t request.
+ * \return zero on success or a negative number on failure.
+ * 
+ * After some sanity checks creates a drm_buf structure for each buffer and
+ * reallocates the buffer list of the same size order to accommodate the new
+ * buffers.
+ */
+int drm_addbufs_agp( 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_device_dma_t *dma = dev->dma;
+       drm_buf_desc_t request;
+       drm_buf_entry_t *entry;
+       drm_buf_t *buf;
+       unsigned long offset;
+       unsigned long agp_offset;
+       int count;
+       int order;
+       int size;
+       int alignment;
+       int page_order;
+       int total;
+       int byte_count;
+       int i;
+       drm_buf_t **temp_buflist;
+       drm_buf_desc_t __user *argp = (void __user *)arg;
+
+       if ( !dma ) return -EINVAL;
+
+       if ( copy_from_user( &request, argp,
+                            sizeof(request) ) )
+               return -EFAULT;
+
+       count = request.count;
+       order = drm_order( request.size );
+       size = 1 << order;
+
+       alignment  = (request.flags & _DRM_PAGE_ALIGN)
+               ? PAGE_ALIGN(size) : size;
+       page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+       total = PAGE_SIZE << page_order;
+
+       byte_count = 0;
+       agp_offset = dev->agp->base + request.agp_start;
+
+       DRM_DEBUG( "count:      %d\n",  count );
+       DRM_DEBUG( "order:      %d\n",  order );
+       DRM_DEBUG( "size:       %d\n",  size );
+       DRM_DEBUG( "agp_offset: %lu\n", agp_offset );
+       DRM_DEBUG( "alignment:  %d\n",  alignment );
+       DRM_DEBUG( "page_order: %d\n",  page_order );
+       DRM_DEBUG( "total:      %d\n",  total );
+
+       if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL;
+       if ( dev->queue_count ) return -EBUSY; /* Not while in use */
+
+       spin_lock( &dev->count_lock );
+       if ( dev->buf_use ) {
+               spin_unlock( &dev->count_lock );
+               return -EBUSY;
+       }
+       atomic_inc( &dev->buf_alloc );
+       spin_unlock( &dev->count_lock );
+
+       down( &dev->struct_sem );
+       entry = &dma->bufs[order];
+       if ( entry->buf_count ) {
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM; /* May only call once for each order */
+       }
+
+       if (count < 0 || count > 4096) {
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -EINVAL;
+       }
+
+       entry->buflist = drm_alloc( count * sizeof(*entry->buflist),
+                                   DRM_MEM_BUFS );
+       if ( !entry->buflist ) {
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM;
+       }
+       memset( entry->buflist, 0, count * sizeof(*entry->buflist) );
+
+       entry->buf_size = size;
+       entry->page_order = page_order;
+
+       offset = 0;
+
+       while ( entry->buf_count < count ) {
+               buf          = &entry->buflist[entry->buf_count];
+               buf->idx     = dma->buf_count + entry->buf_count;
+               buf->total   = alignment;
+               buf->order   = order;
+               buf->used    = 0;
+
+               buf->offset  = (dma->byte_count + offset);
+               buf->bus_address = agp_offset + offset;
+               buf->address = (void *)(agp_offset + offset);
+               buf->next    = NULL;
+               buf->waiting = 0;
+               buf->pending = 0;
+               init_waitqueue_head( &buf->dma_wait );
+               buf->filp    = NULL;
+
+               buf->dev_priv_size = dev->driver->dev_priv_size;
+               buf->dev_private = drm_alloc( buf->dev_priv_size,
+                                              DRM_MEM_BUFS );
+               if(!buf->dev_private) {
+                       /* Set count correctly so we free the proper amount. */
+                       entry->buf_count = count;
+                       drm_cleanup_buf_error(dev,entry);
+                       up( &dev->struct_sem );
+                       atomic_dec( &dev->buf_alloc );
+                       return -ENOMEM;
+               }
+               memset( buf->dev_private, 0, buf->dev_priv_size );
+
+               DRM_DEBUG( "buffer %d @ %p\n",
+                          entry->buf_count, buf->address );
+
+               offset += alignment;
+               entry->buf_count++;
+               byte_count += PAGE_SIZE << page_order;
+       }
+
+       DRM_DEBUG( "byte_count: %d\n", byte_count );
+
+       temp_buflist = drm_realloc( dma->buflist,
+                                    dma->buf_count * sizeof(*dma->buflist),
+                                    (dma->buf_count + entry->buf_count)
+                                    * sizeof(*dma->buflist),
+                                    DRM_MEM_BUFS );
+       if(!temp_buflist) {
+               /* Free the entry because it isn't valid */
+               drm_cleanup_buf_error(dev,entry);
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM;
+       }
+       dma->buflist = temp_buflist;
+
+       for ( i = 0 ; i < entry->buf_count ; i++ ) {
+               dma->buflist[i + dma->buf_count] = &entry->buflist[i];
+       }
+
+       dma->buf_count += entry->buf_count;
+       dma->byte_count += byte_count;
+
+       DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count );
+       DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count );
+
+       up( &dev->struct_sem );
+
+       request.count = entry->buf_count;
+       request.size = size;
+
+       if ( copy_to_user( argp, &request, sizeof(request) ) )
+               return -EFAULT;
+
+       dma->flags = _DRM_DMA_USE_AGP;
+
+       atomic_dec( &dev->buf_alloc );
+       return 0;
+}
+#endif /* __OS_HAS_AGP */
+
+int drm_addbufs_pci( 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_device_dma_t *dma = dev->dma;
+       drm_buf_desc_t request;
+       int count;
+       int order;
+       int size;
+       int total;
+       int page_order;
+       drm_buf_entry_t *entry;
+       unsigned long page;
+       drm_buf_t *buf;
+       int alignment;
+       unsigned long offset;
+       int i;
+       int byte_count;
+       int page_count;
+       unsigned long *temp_pagelist;
+       drm_buf_t **temp_buflist;
+       drm_buf_desc_t __user *argp = (void __user *)arg;
+
+       if (!drm_core_check_feature(dev, DRIVER_PCI_DMA)) return -EINVAL;
+       if ( !dma ) return -EINVAL;
+
+       if ( copy_from_user( &request, argp, sizeof(request) ) )
+               return -EFAULT;
+
+       count = request.count;
+       order = drm_order( request.size );
+       size = 1 << order;
+
+       DRM_DEBUG( "count=%d, size=%d (%d), order=%d, queue_count=%d\n",
+                  request.count, request.size, size,
+                  order, dev->queue_count );
+
+       if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL;
+       if ( dev->queue_count ) return -EBUSY; /* Not while in use */
+
+       alignment = (request.flags & _DRM_PAGE_ALIGN)
+               ? PAGE_ALIGN(size) : size;
+       page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+       total = PAGE_SIZE << page_order;
+
+       spin_lock( &dev->count_lock );
+       if ( dev->buf_use ) {
+               spin_unlock( &dev->count_lock );
+               return -EBUSY;
+       }
+       atomic_inc( &dev->buf_alloc );
+       spin_unlock( &dev->count_lock );
+
+       down( &dev->struct_sem );
+       entry = &dma->bufs[order];
+       if ( entry->buf_count ) {
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM; /* May only call once for each order */
+       }
+
+       if (count < 0 || count > 4096) {
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -EINVAL;
+       }
+
+       entry->buflist = drm_alloc( count * sizeof(*entry->buflist),
+                                   DRM_MEM_BUFS );
+       if ( !entry->buflist ) {
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM;
+       }
+       memset( entry->buflist, 0, count * sizeof(*entry->buflist) );
+
+       entry->seglist = drm_alloc( count * sizeof(*entry->seglist),
+                                   DRM_MEM_SEGS );
+       if ( !entry->seglist ) {
+               drm_free( entry->buflist,
+                         count * sizeof(*entry->buflist),
+                         DRM_MEM_BUFS );
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM;
+       }
+       memset( entry->seglist, 0, count * sizeof(*entry->seglist) );
+
+       /* Keep the original pagelist until we know all the allocations
+        * have succeeded
+        */
+       temp_pagelist = drm_alloc( (dma->page_count + (count << page_order))
+                                   * sizeof(*dma->pagelist),
+                                   DRM_MEM_PAGES );
+       if (!temp_pagelist) {
+               drm_free( entry->buflist,
+                          count * sizeof(*entry->buflist),
+                          DRM_MEM_BUFS );
+               drm_free( entry->seglist,
+                          count * sizeof(*entry->seglist),
+                          DRM_MEM_SEGS );
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM;
+       }
+       memcpy(temp_pagelist,
+              dma->pagelist,
+              dma->page_count * sizeof(*dma->pagelist));
+       DRM_DEBUG( "pagelist: %d entries\n",
+                  dma->page_count + (count << page_order) );
+
+       entry->buf_size = size;
+       entry->page_order = page_order;
+       byte_count = 0;
+       page_count = 0;
+
+       while ( entry->buf_count < count ) {
+               page = drm_alloc_pages( page_order, DRM_MEM_DMA );
+               if ( !page ) {
+                       /* Set count correctly so we free the proper amount. */
+                       entry->buf_count = count;
+                       entry->seg_count = count;
+                       drm_cleanup_buf_error(dev, entry);
+                       drm_free( temp_pagelist,
+                                  (dma->page_count + (count << page_order))
+                                  * sizeof(*dma->pagelist),
+                                  DRM_MEM_PAGES );
+                       up( &dev->struct_sem );
+                       atomic_dec( &dev->buf_alloc );
+                       return -ENOMEM;
+               }
+               entry->seglist[entry->seg_count++] = page;
+               for ( i = 0 ; i < (1 << page_order) ; i++ ) {
+                       DRM_DEBUG( "page %d @ 0x%08lx\n",
+                                  dma->page_count + page_count,
+                                  page + PAGE_SIZE * i );
+                       temp_pagelist[dma->page_count + page_count++]
+                               = page + PAGE_SIZE * i;
+               }
+               for ( offset = 0 ;
+                     offset + size <= total && entry->buf_count < count ;
+                     offset += alignment, ++entry->buf_count ) {
+                       buf          = &entry->buflist[entry->buf_count];
+                       buf->idx     = dma->buf_count + entry->buf_count;
+                       buf->total   = alignment;
+                       buf->order   = order;
+                       buf->used    = 0;
+                       buf->offset  = (dma->byte_count + byte_count + offset);
+                       buf->address = (void *)(page + offset);
+                       buf->next    = NULL;
+                       buf->waiting = 0;
+                       buf->pending = 0;
+                       init_waitqueue_head( &buf->dma_wait );
+                       buf->filp    = NULL;
+
+                       buf->dev_priv_size = dev->driver->dev_priv_size;
+                       buf->dev_private = drm_alloc( buf->dev_priv_size,
+                                                      DRM_MEM_BUFS );
+                       if(!buf->dev_private) {
+                               /* Set count correctly so we free the proper amount. */
+                               entry->buf_count = count;
+                               entry->seg_count = count;
+                               drm_cleanup_buf_error(dev,entry);
+                               drm_free( temp_pagelist,
+                                          (dma->page_count + (count << page_order))
+                                          * sizeof(*dma->pagelist),
+                                          DRM_MEM_PAGES );
+                               up( &dev->struct_sem );
+                               atomic_dec( &dev->buf_alloc );
+                               return -ENOMEM;
+                       }
+                       memset( buf->dev_private, 0, buf->dev_priv_size );
+
+                       DRM_DEBUG( "buffer %d @ %p\n",
+                                  entry->buf_count, buf->address );
+               }
+               byte_count += PAGE_SIZE << page_order;
+       }
+
+       temp_buflist = drm_realloc( dma->buflist,
+                                    dma->buf_count * sizeof(*dma->buflist),
+                                    (dma->buf_count + entry->buf_count)
+                                    * sizeof(*dma->buflist),
+                                    DRM_MEM_BUFS );
+       if (!temp_buflist) {
+               /* Free the entry because it isn't valid */
+               drm_cleanup_buf_error(dev,entry);
+               drm_free( temp_pagelist,
+                          (dma->page_count + (count << page_order))
+                          * sizeof(*dma->pagelist),
+                          DRM_MEM_PAGES );
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM;
+       }
+       dma->buflist = temp_buflist;
+
+       for ( i = 0 ; i < entry->buf_count ; i++ ) {
+               dma->buflist[i + dma->buf_count] = &entry->buflist[i];
+       }
+
+       /* No allocations failed, so now we can replace the orginal pagelist
+        * with the new one.
+        */
+       if (dma->page_count) {
+               drm_free(dma->pagelist,
+                         dma->page_count * sizeof(*dma->pagelist),
+                         DRM_MEM_PAGES);
+       }
+       dma->pagelist = temp_pagelist;
+
+       dma->buf_count += entry->buf_count;
+       dma->seg_count += entry->seg_count;
+       dma->page_count += entry->seg_count << page_order;
+       dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
+
+       up( &dev->struct_sem );
+
+       request.count = entry->buf_count;
+       request.size = size;
+
+       if ( copy_to_user( argp, &request, sizeof(request) ) )
+               return -EFAULT;
+
+       atomic_dec( &dev->buf_alloc );
+       return 0;
+
+}
+
+int drm_addbufs_sg( 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_device_dma_t *dma = dev->dma;
+       drm_buf_desc_t __user *argp = (void __user *)arg;
+       drm_buf_desc_t request;
+       drm_buf_entry_t *entry;
+       drm_buf_t *buf;
+       unsigned long offset;
+       unsigned long agp_offset;
+       int count;
+       int order;
+       int size;
+       int alignment;
+       int page_order;
+       int total;
+       int byte_count;
+       int i;
+       drm_buf_t **temp_buflist;
+
+       if (!drm_core_check_feature(dev, DRIVER_SG)) return -EINVAL;
+       
+       if ( !dma ) return -EINVAL;
+
+       if ( copy_from_user( &request, argp, sizeof(request) ) )
+               return -EFAULT;
+
+       count = request.count;
+       order = drm_order( request.size );
+       size = 1 << order;
+
+       alignment  = (request.flags & _DRM_PAGE_ALIGN)
+                       ? PAGE_ALIGN(size) : size;
+       page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+       total = PAGE_SIZE << page_order;
+
+       byte_count = 0;
+       agp_offset = request.agp_start;
+
+       DRM_DEBUG( "count:      %d\n",  count );
+       DRM_DEBUG( "order:      %d\n",  order );
+       DRM_DEBUG( "size:       %d\n",  size );
+       DRM_DEBUG( "agp_offset: %lu\n", agp_offset );
+       DRM_DEBUG( "alignment:  %d\n",  alignment );
+       DRM_DEBUG( "page_order: %d\n",  page_order );
+       DRM_DEBUG( "total:      %d\n",  total );
+
+       if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL;
+       if ( dev->queue_count ) return -EBUSY; /* Not while in use */
+
+       spin_lock( &dev->count_lock );
+       if ( dev->buf_use ) {
+               spin_unlock( &dev->count_lock );
+               return -EBUSY;
+       }
+       atomic_inc( &dev->buf_alloc );
+       spin_unlock( &dev->count_lock );
+
+       down( &dev->struct_sem );
+       entry = &dma->bufs[order];
+       if ( entry->buf_count ) {
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM; /* May only call once for each order */
+       }
+
+       if (count < 0 || count > 4096) {
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -EINVAL;
+       }
+
+       entry->buflist = drm_alloc( count * sizeof(*entry->buflist),
+                                    DRM_MEM_BUFS );
+       if ( !entry->buflist ) {
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM;
+       }
+       memset( entry->buflist, 0, count * sizeof(*entry->buflist) );
+
+       entry->buf_size = size;
+       entry->page_order = page_order;
+
+       offset = 0;
+
+       while ( entry->buf_count < count ) {
+               buf          = &entry->buflist[entry->buf_count];
+               buf->idx     = dma->buf_count + entry->buf_count;
+               buf->total   = alignment;
+               buf->order   = order;
+               buf->used    = 0;
+
+               buf->offset  = (dma->byte_count + offset);
+               buf->bus_address = agp_offset + offset;
+               buf->address = (void *)(agp_offset + offset + dev->sg->handle);
+               buf->next    = NULL;
+               buf->waiting = 0;
+               buf->pending = 0;
+               init_waitqueue_head( &buf->dma_wait );
+               buf->filp    = NULL;
+
+               buf->dev_priv_size = dev->driver->dev_priv_size;
+               buf->dev_private = drm_alloc( buf->dev_priv_size,
+                                              DRM_MEM_BUFS );
+               if(!buf->dev_private) {
+                       /* Set count correctly so we free the proper amount. */
+                       entry->buf_count = count;
+                       drm_cleanup_buf_error(dev,entry);
+                       up( &dev->struct_sem );
+                       atomic_dec( &dev->buf_alloc );
+                       return -ENOMEM;
+               }
+
+               memset( buf->dev_private, 0, buf->dev_priv_size );
+
+               DRM_DEBUG( "buffer %d @ %p\n",
+                          entry->buf_count, buf->address );
+
+               offset += alignment;
+               entry->buf_count++;
+               byte_count += PAGE_SIZE << page_order;
+       }
+
+       DRM_DEBUG( "byte_count: %d\n", byte_count );
+
+       temp_buflist = drm_realloc( dma->buflist,
+                                    dma->buf_count * sizeof(*dma->buflist),
+                                    (dma->buf_count + entry->buf_count)
+                                    * sizeof(*dma->buflist),
+                                    DRM_MEM_BUFS );
+       if(!temp_buflist) {
+               /* Free the entry because it isn't valid */
+               drm_cleanup_buf_error(dev,entry);
+               up( &dev->struct_sem );
+               atomic_dec( &dev->buf_alloc );
+               return -ENOMEM;
+       }
+       dma->buflist = temp_buflist;
+
+       for ( i = 0 ; i < entry->buf_count ; i++ ) {
+               dma->buflist[i + dma->buf_count] = &entry->buflist[i];
+       }
+
+       dma->buf_count += entry->buf_count;
+       dma->byte_count += byte_count;
+
+       DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count );
+       DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count );
+
+       up( &dev->struct_sem );
+
+       request.count = entry->buf_count;
+       request.size = size;
+
+       if ( copy_to_user( argp, &request, sizeof(request) ) )
+               return -EFAULT;
+
+       dma->flags = _DRM_DMA_USE_SG;
+
+       atomic_dec( &dev->buf_alloc );
+       return 0;
+}
+
+/**
+ * Add buffers for DMA transfers (ioctl).
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_buf_desc_t request.
+ * \return zero on success or a negative number on failure.
+ *
+ * According with the memory type specified in drm_buf_desc::flags and the
+ * build options, it dispatches the call either to addbufs_agp(),
+ * addbufs_sg() or addbufs_pci() for AGP, scatter-gather or consistent
+ * PCI memory respectively.
+ */
+int drm_addbufs( struct inode *inode, struct file *filp,
+                 unsigned int cmd, unsigned long arg )
+{
+       drm_buf_desc_t request;
+       drm_file_t *priv = filp->private_data;
+       drm_device_t *dev = priv->dev;
+       
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+               return -EINVAL;
+
+       if ( copy_from_user( &request, (drm_buf_desc_t __user *)arg,
+                            sizeof(request) ) )
+               return -EFAULT;
+
+#if __OS_HAS_AGP
+       if ( request.flags & _DRM_AGP_BUFFER )
+               return drm_addbufs_agp( inode, filp, cmd, arg );
+       else
+#endif
+       if ( request.flags & _DRM_SG_BUFFER )
+               return drm_addbufs_sg( inode, filp, cmd, arg );
+       else
+               return drm_addbufs_pci( inode, filp, cmd, arg );
+}
+
+
+/**
+ * Get information about the buffer mappings.
+ *
+ * This was originally mean for debugging purposes, or by a sophisticated
+ * client library to determine how best to use the available buffers (e.g.,
+ * large buffers can be used for image transfer).
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_buf_info structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Increments drm_device::buf_use while holding the drm_device::count_lock
+ * lock, preventing of allocating more buffers after this call. Information
+ * about each requested buffer is then copied into user space.
+ */
+int drm_infobufs( 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_device_dma_t *dma = dev->dma;
+       drm_buf_info_t request;
+       drm_buf_info_t __user *argp = (void __user *)arg;
+       int i;
+       int count;
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+               return -EINVAL;
+
+       if ( !dma ) return -EINVAL;
+
+       spin_lock( &dev->count_lock );
+       if ( atomic_read( &dev->buf_alloc ) ) {
+               spin_unlock( &dev->count_lock );
+               return -EBUSY;
+       }
+       ++dev->buf_use;         /* Can't allocate more after this call */
+       spin_unlock( &dev->count_lock );
+
+       if ( copy_from_user( &request, argp, sizeof(request) ) )
+               return -EFAULT;
+
+       for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) {
+               if ( dma->bufs[i].buf_count ) ++count;
+       }
+
+       DRM_DEBUG( "count = %d\n", count );
+
+       if ( request.count >= count ) {
+               for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) {
+                       if ( dma->bufs[i].buf_count ) {
+                               drm_buf_desc_t __user *to = &request.list[count];
+                               drm_buf_entry_t *from = &dma->bufs[i];
+                               drm_freelist_t *list = &dma->bufs[i].freelist;
+                               if ( copy_to_user( &to->count,
+                                                  &from->buf_count,
+                                                  sizeof(from->buf_count) ) ||
+                                    copy_to_user( &to->size,
+                                                  &from->buf_size,
+                                                  sizeof(from->buf_size) ) ||
+                                    copy_to_user( &to->low_mark,
+                                                  &list->low_mark,
+                                                  sizeof(list->low_mark) ) ||
+                                    copy_to_user( &to->high_mark,
+                                                  &list->high_mark,
+                                                  sizeof(list->high_mark) ) )
+                                       return -EFAULT;
+
+                               DRM_DEBUG( "%d %d %d %d %d\n",
+                                          i,
+                                          dma->bufs[i].buf_count,
+                                          dma->bufs[i].buf_size,
+                                          dma->bufs[i].freelist.low_mark,
+                                          dma->bufs[i].freelist.high_mark );
+                               ++count;
+                       }
+               }
+       }
+       request.count = count;
+
+       if ( copy_to_user( argp, &request, sizeof(request) ) )
+               return -EFAULT;
+
+       return 0;
+}
+
+/**
+ * Specifies a low and high water mark for buffer allocation
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg a pointer to a drm_buf_desc structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Verifies that the size order is bounded between the admissible orders and
+ * updates the respective drm_device_dma::bufs entry low and high water mark.
+ *
+ * \note This ioctl is deprecated and mostly never used.
+ */
+int drm_markbufs( 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_device_dma_t *dma = dev->dma;
+       drm_buf_desc_t request;
+       int order;
+       drm_buf_entry_t *entry;
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+               return -EINVAL;
+
+       if ( !dma ) return -EINVAL;
+
+       if ( copy_from_user( &request,
+                            (drm_buf_desc_t __user *)arg,
+                            sizeof(request) ) )
+               return -EFAULT;
+
+       DRM_DEBUG( "%d, %d, %d\n",
+                  request.size, request.low_mark, request.high_mark );
+       order = drm_order( request.size );
+       if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL;
+       entry = &dma->bufs[order];
+
+       if ( request.low_mark < 0 || request.low_mark > entry->buf_count )
+               return -EINVAL;
+       if ( request.high_mark < 0 || request.high_mark > entry->buf_count )
+               return -EINVAL;
+
+       entry->freelist.low_mark  = request.low_mark;
+       entry->freelist.high_mark = request.high_mark;
+
+       return 0;
+}
+
+/**
+ * Unreserve the buffers in list, previously reserved using drmDMA. 
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_buf_free structure.
+ * \return zero on success or a negative number on failure.
+ * 
+ * Calls free_buffer() for each used buffer.
+ * This function is primarily used for debugging.
+ */
+int drm_freebufs( 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_device_dma_t *dma = dev->dma;
+       drm_buf_free_t request;
+       int i;
+       int idx;
+       drm_buf_t *buf;
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+               return -EINVAL;
+
+       if ( !dma ) return -EINVAL;
+
+       if ( copy_from_user( &request,
+                            (drm_buf_free_t __user *)arg,
+                            sizeof(request) ) )
+               return -EFAULT;
+
+       DRM_DEBUG( "%d\n", request.count );
+       for ( i = 0 ; i < request.count ; i++ ) {
+               if ( copy_from_user( &idx,
+                                    &request.list[i],
+                                    sizeof(idx) ) )
+                       return -EFAULT;
+               if ( idx < 0 || idx >= dma->buf_count ) {
+                       DRM_ERROR( "Index %d (of %d max)\n",
+                                  idx, dma->buf_count - 1 );
+                       return -EINVAL;
+               }
+               buf = dma->buflist[idx];
+               if ( buf->filp != filp ) {
+                       DRM_ERROR( "Process %d freeing buffer not owned\n",
+                                  current->pid );
+                       return -EINVAL;
+               }
+               drm_free_buffer( dev, buf );
+       }
+
+       return 0;
+}
+
+/**
+ * Maps all of the DMA buffers into client-virtual space (ioctl).
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg pointer to a drm_buf_map structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Maps the AGP or SG buffer region with do_mmap(), and copies information
+ * about each buffer into user space. The PCI buffers are already mapped on the
+ * addbufs_pci() call.
+ */
+int drm_mapbufs( 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_device_dma_t *dma = dev->dma;
+       drm_buf_map_t __user *argp = (void __user *)arg;
+       int retcode = 0;
+       const int zero = 0;
+       unsigned long virtual;
+       unsigned long address;
+       drm_buf_map_t request;
+       int i;
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+               return -EINVAL;
+
+       if ( !dma ) return -EINVAL;
+
+       spin_lock( &dev->count_lock );
+       if ( atomic_read( &dev->buf_alloc ) ) {
+               spin_unlock( &dev->count_lock );
+               return -EBUSY;
+       }
+       dev->buf_use++;         /* Can't allocate more after this call */
+       spin_unlock( &dev->count_lock );
+
+       if ( copy_from_user( &request, argp, sizeof(request) ) )
+               return -EFAULT;
+
+       if ( request.count >= dma->buf_count ) {
+               if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP)) ||
+                   (drm_core_check_feature(dev, DRIVER_SG) && (dma->flags & _DRM_DMA_USE_SG)) ) {
+                       drm_map_t *map = dev->agp_buffer_map;
+
+                       if ( !map ) {
+                               retcode = -EINVAL;
+                               goto done;
+                       }
+
+#if LINUX_VERSION_CODE <= 0x020402
+                       down( &current->mm->mmap_sem );
+#else
+                       down_write( &current->mm->mmap_sem );
+#endif
+                       virtual = do_mmap( filp, 0, map->size,
+                                          PROT_READ | PROT_WRITE,
+                                          MAP_SHARED,
+                                          (unsigned long)map->offset );
+#if LINUX_VERSION_CODE <= 0x020402
+                       up( &current->mm->mmap_sem );
+#else
+                       up_write( &current->mm->mmap_sem );
+#endif
+               } else {
+#if LINUX_VERSION_CODE <= 0x020402
+                       down( &current->mm->mmap_sem );
+#else
+                       down_write( &current->mm->mmap_sem );
+#endif
+                       virtual = do_mmap( filp, 0, dma->byte_count,
+                                          PROT_READ | PROT_WRITE,
+                                          MAP_SHARED, 0 );
+#if LINUX_VERSION_CODE <= 0x020402
+                       up( &current->mm->mmap_sem );
+#else
+                       up_write( &current->mm->mmap_sem );
+#endif
+               }
+               if ( virtual > -1024UL ) {
+                       /* Real error */
+                       retcode = (signed long)virtual;
+                       goto done;
+               }
+               request.virtual = (void __user *)virtual;
+
+               for ( i = 0 ; i < dma->buf_count ; i++ ) {
+                       if ( copy_to_user( &request.list[i].idx,
+                                          &dma->buflist[i]->idx,
+                                          sizeof(request.list[0].idx) ) ) {
+                               retcode = -EFAULT;
+                               goto done;
+                       }
+                       if ( copy_to_user( &request.list[i].total,
+                                          &dma->buflist[i]->total,
+                                          sizeof(request.list[0].total) ) ) {
+                               retcode = -EFAULT;
+                               goto done;
+                       }
+                       if ( copy_to_user( &request.list[i].used,
+                                          &zero,
+                                          sizeof(zero) ) ) {
+                               retcode = -EFAULT;
+                               goto done;
+                       }
+                       address = virtual + dma->buflist[i]->offset; /* *** */
+                       if ( copy_to_user( &request.list[i].address,
+                                          &address,
+                                          sizeof(address) ) ) {
+                               retcode = -EFAULT;
+                               goto done;
+                       }
+               }
+       }
+ done:
+       request.count = dma->buf_count;
+       DRM_DEBUG( "%d buffers, retcode = %d\n", request.count, retcode );
+
+       if ( copy_to_user( argp, &request, sizeof(request) ) )
+               return -EFAULT;
+
+       return retcode;
+}
+
diff --git a/drivers/char/drm/drm_context.c b/drivers/char/drm/drm_context.c
new file mode 100644 (file)
index 0000000..3a7637c
--- /dev/null
@@ -0,0 +1,578 @@
+/**
+ * \file drm_context.h 
+ * IOCTLs for generic contexts
+ * 
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Fri Nov 24 18:31:37 2000 by gareth@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.
+ */
+
+/*
+ * ChangeLog:
+ *  2001-11-16 Torsten Duwe <duwe@caldera.de>
+ *             added context constructor/destructor hooks,
+ *             needed by SiS driver's memory management.
+ */
+
+#include "drmP.h"
+
+/******************************************************************/
+/** \name Context bitmap support */
+/*@{*/
+
+/**
+ * Free a handle from the context bitmap.
+ *
+ * \param dev DRM device.
+ * \param ctx_handle context handle.
+ *
+ * Clears the bit specified by \p ctx_handle in drm_device::ctx_bitmap and the entry
+ * in drm_device::context_sareas, while holding the drm_device::struct_sem
+ * lock.
+ */
+void drm_ctxbitmap_free( drm_device_t *dev, int ctx_handle )
+{
+       if ( ctx_handle < 0 ) goto failed;
+       if ( !dev->ctx_bitmap ) goto failed;
+
+       if ( ctx_handle < DRM_MAX_CTXBITMAP ) {
+               down(&dev->struct_sem);
+               clear_bit( ctx_handle, dev->ctx_bitmap );
+               dev->context_sareas[ctx_handle] = NULL;
+               up(&dev->struct_sem);
+               return;
+       }
+failed:
+               DRM_ERROR( "Attempt to free invalid context handle: %d\n",
+                  ctx_handle );
+               return;
+}
+
+/** 
+ * Context bitmap allocation.
+ *
+ * \param dev DRM device.
+ * \return (non-negative) context handle on success or a negative number on failure.
+ *
+ * Find the first zero bit in drm_device::ctx_bitmap and (re)allocates
+ * drm_device::context_sareas to accommodate the new entry while holding the
+ * drm_device::struct_sem lock.
+ */
+int drm_ctxbitmap_next( drm_device_t *dev )
+{
+       int bit;
+
+       if(!dev->ctx_bitmap) return -1;
+
+       down(&dev->struct_sem);
+       bit = find_first_zero_bit( dev->ctx_bitmap, DRM_MAX_CTXBITMAP );
+       if ( bit < DRM_MAX_CTXBITMAP ) {
+               set_bit( bit, dev->ctx_bitmap );
+               DRM_DEBUG( "drm_ctxbitmap_next bit : %d\n", bit );
+               if((bit+1) > dev->max_context) {
+                       dev->max_context = (bit+1);
+                       if(dev->context_sareas) {
+                               drm_map_t **ctx_sareas;
+
+                               ctx_sareas = drm_realloc(dev->context_sareas,
+                                               (dev->max_context - 1) * 
+                                               sizeof(*dev->context_sareas),
+                                               dev->max_context * 
+                                               sizeof(*dev->context_sareas),
+                                               DRM_MEM_MAPS);
+                               if(!ctx_sareas) {
+                                       clear_bit(bit, dev->ctx_bitmap);
+                                       up(&dev->struct_sem);
+                                       return -1;
+                               }
+                               dev->context_sareas = ctx_sareas;
+                               dev->context_sareas[bit] = NULL;
+                       } else {
+                               /* max_context == 1 at this point */
+                               dev->context_sareas = drm_alloc(
+                                               dev->max_context * 
+                                               sizeof(*dev->context_sareas),
+                                               DRM_MEM_MAPS);
+                               if(!dev->context_sareas) {
+                                       clear_bit(bit, dev->ctx_bitmap);
+                                       up(&dev->struct_sem);
+                                       return -1;
+                               }
+                               dev->context_sareas[bit] = NULL;
+                       }
+               }
+               up(&dev->struct_sem);
+               return bit;
+       }
+       up(&dev->struct_sem);
+       return -1;
+}
+
+/**
+ * Context bitmap initialization.
+ *
+ * \param dev DRM device.
+ *
+ * Allocates and initialize drm_device::ctx_bitmap and drm_device::context_sareas, while holding
+ * the drm_device::struct_sem lock.
+ */
+int drm_ctxbitmap_init( drm_device_t *dev )
+{
+       int i;
+       int temp;
+
+       down(&dev->struct_sem);
+       dev->ctx_bitmap = (unsigned long *) drm_alloc( PAGE_SIZE,
+                                                       DRM_MEM_CTXBITMAP );
+       if ( dev->ctx_bitmap == NULL ) {
+               up(&dev->struct_sem);
+               return -ENOMEM;
+       }
+       memset( (void *)dev->ctx_bitmap, 0, PAGE_SIZE );
+       dev->context_sareas = NULL;
+       dev->max_context = -1;
+       up(&dev->struct_sem);
+
+       for ( i = 0 ; i < DRM_RESERVED_CONTEXTS ; i++ ) {
+               temp = drm_ctxbitmap_next( dev );
+               DRM_DEBUG( "drm_ctxbitmap_init : %d\n", temp );
+       }
+
+       return 0;
+}
+
+/**
+ * Context bitmap cleanup.
+ *
+ * \param dev DRM device.
+ *
+ * Frees drm_device::ctx_bitmap and drm_device::context_sareas, while holding
+ * the drm_device::struct_sem lock.
+ */
+void drm_ctxbitmap_cleanup( drm_device_t *dev )
+{
+       down(&dev->struct_sem);
+       if( dev->context_sareas ) drm_free( dev->context_sareas,
+                                            sizeof(*dev->context_sareas) * 
+                                            dev->max_context,
+                                            DRM_MEM_MAPS );
+       drm_free( (void *)dev->ctx_bitmap, PAGE_SIZE, DRM_MEM_CTXBITMAP );
+       up(&dev->struct_sem);
+}
+
+/*@}*/
+
+/******************************************************************/
+/** \name Per Context SAREA Support */
+/*@{*/
+
+/**
+ * Get per-context SAREA.
+ * 
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument pointing to a drm_ctx_priv_map structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Gets the map from drm_device::context_sareas with the handle specified and
+ * returns its handle.
+ */
+int drm_getsareactx(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_ctx_priv_map_t __user *argp = (void __user *)arg;
+       drm_ctx_priv_map_t request;
+       drm_map_t *map;
+
+       if (copy_from_user(&request, argp, sizeof(request)))
+               return -EFAULT;
+
+       down(&dev->struct_sem);
+       if (dev->max_context < 0 || request.ctx_id >= (unsigned) dev->max_context) {
+               up(&dev->struct_sem);
+               return -EINVAL;
+       }
+
+       map = dev->context_sareas[request.ctx_id];
+       up(&dev->struct_sem);
+
+       request.handle = map->handle;
+       if (copy_to_user(argp, &request, sizeof(request)))
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * Set per-context SAREA.
+ * 
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument pointing to a drm_ctx_priv_map structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Searches the mapping specified in \p arg and update the entry in
+ * drm_device::context_sareas with it.
+ */
+int drm_setsareactx(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_ctx_priv_map_t request;
+       drm_map_t *map = NULL;
+       drm_map_list_t *r_list = NULL;
+       struct list_head *list;
+
+       if (copy_from_user(&request,
+                          (drm_ctx_priv_map_t __user *)arg,
+                          sizeof(request)))
+               return -EFAULT;
+
+       down(&dev->struct_sem);
+       list_for_each(list, &dev->maplist->head) {
+               r_list = list_entry(list, drm_map_list_t, head);
+               if(r_list->map &&
+                  r_list->map->handle == request.handle)
+                       goto found;
+       }
+bad:
+       up(&dev->struct_sem);
+       return -EINVAL;
+
+found:
+       map = r_list->map;
+       if (!map) goto bad;
+       if (dev->max_context < 0)
+               goto bad;
+       if (request.ctx_id >= (unsigned) dev->max_context)
+               goto bad;
+       dev->context_sareas[request.ctx_id] = map;
+       up(&dev->struct_sem);
+       return 0;
+}
+
+/*@}*/
+
+/******************************************************************/
+/** \name The actual DRM context handling routines */
+/*@{*/
+
+/**
+ * Switch context.
+ *
+ * \param dev DRM device.
+ * \param old old context handle.
+ * \param new new context handle.
+ * \return zero on success or a negative number on failure.
+ *
+ * Attempt to set drm_device::context_flag.
+ */
+int drm_context_switch( drm_device_t *dev, int old, int new )
+{
+        if ( test_and_set_bit( 0, &dev->context_flag ) ) {
+                DRM_ERROR( "Reentering -- FIXME\n" );
+                return -EBUSY;
+        }
+
+
+        DRM_DEBUG( "Context switch from %d to %d\n", old, new );
+
+        if ( new == dev->last_context ) {
+                clear_bit( 0, &dev->context_flag );
+                return 0;
+        }
+
+        return 0;
+}
+
+/**
+ * Complete context switch.
+ *
+ * \param dev DRM device.
+ * \param new new context handle.
+ * \return zero on success or a negative number on failure.
+ *
+ * Updates drm_device::last_context and drm_device::last_switch. Verifies the
+ * hardware lock is held, clears the drm_device::context_flag and wakes up
+ * drm_device::context_wait.
+ */
+int drm_context_switch_complete( drm_device_t *dev, int new )
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+
+        if ( !_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ) {
+                DRM_ERROR( "Lock isn't held after context switch\n" );
+        }
+
+                               /* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+        clear_bit( 0, &dev->context_flag );
+        wake_up( &dev->context_wait );
+
+        return 0;
+}
+
+/**
+ * Reserve contexts.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument pointing to a drm_ctx_res structure.
+ * \return zero on success or a negative number on failure.
+ */
+int drm_resctx( struct inode *inode, struct file *filp,
+                unsigned int cmd, unsigned long arg )
+{
+       drm_ctx_res_t res;
+       drm_ctx_t __user *argp = (void __user *)arg;
+       drm_ctx_t ctx;
+       int i;
+
+       if ( copy_from_user( &res, argp, sizeof(res) ) )
+               return -EFAULT;
+
+       if ( res.count >= DRM_RESERVED_CONTEXTS ) {
+               memset( &ctx, 0, sizeof(ctx) );
+               for ( i = 0 ; i < DRM_RESERVED_CONTEXTS ; i++ ) {
+                       ctx.handle = i;
+                       if ( copy_to_user( &res.contexts[i],
+                                          &i, sizeof(i) ) )
+                               return -EFAULT;
+               }
+       }
+       res.count = DRM_RESERVED_CONTEXTS;
+
+       if ( copy_to_user( argp, &res, sizeof(res) ) )
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * Add context.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument pointing to a drm_ctx structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Get a new handle for the context and copy to userspace.
+ */
+int drm_addctx( 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_ctx_list_t * ctx_entry;
+       drm_ctx_t __user *argp = (void __user *)arg;
+       drm_ctx_t ctx;
+
+       if ( copy_from_user( &ctx, argp, sizeof(ctx) ) )
+               return -EFAULT;
+
+       ctx.handle = drm_ctxbitmap_next( dev );
+       if ( ctx.handle == DRM_KERNEL_CONTEXT ) {
+                               /* Skip kernel's context and get a new one. */
+               ctx.handle = drm_ctxbitmap_next( dev );
+       }
+       DRM_DEBUG( "%d\n", ctx.handle );
+       if ( ctx.handle == -1 ) {
+               DRM_DEBUG( "Not enough free contexts.\n" );
+                               /* Should this return -EBUSY instead? */
+               return -ENOMEM;
+       }
+
+       if ( ctx.handle != DRM_KERNEL_CONTEXT )
+       {
+               if (dev->driver->context_ctor)
+                       dev->driver->context_ctor(dev, ctx.handle);
+       }
+
+       ctx_entry = drm_alloc( sizeof(*ctx_entry), DRM_MEM_CTXLIST );
+       if ( !ctx_entry ) {
+               DRM_DEBUG("out of memory\n");
+               return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD( &ctx_entry->head );
+       ctx_entry->handle = ctx.handle;
+       ctx_entry->tag = priv;
+
+       down( &dev->ctxlist_sem );
+       list_add( &ctx_entry->head, &dev->ctxlist->head );
+       ++dev->ctx_count;
+       up( &dev->ctxlist_sem );
+
+       if ( copy_to_user( argp, &ctx, sizeof(ctx) ) )
+               return -EFAULT;
+       return 0;
+}
+
+int drm_modctx( struct inode *inode, struct file *filp,
+                unsigned int cmd, unsigned long arg )
+{
+       /* This does nothing */
+       return 0;
+}
+
+/**
+ * Get context.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument pointing to a drm_ctx structure.
+ * \return zero on success or a negative number on failure.
+ */
+int drm_getctx( struct inode *inode, struct file *filp,
+                unsigned int cmd, unsigned long arg )
+{
+       drm_ctx_t __user *argp = (void __user *)arg;
+       drm_ctx_t ctx;
+
+       if ( copy_from_user( &ctx, argp, sizeof(ctx) ) )
+               return -EFAULT;
+
+       /* This is 0, because we don't handle any context flags */
+       ctx.flags = 0;
+
+       if ( copy_to_user( argp, &ctx, sizeof(ctx) ) )
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * Switch context.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument pointing to a drm_ctx structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Calls context_switch().
+ */
+int drm_switchctx( 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_ctx_t ctx;
+
+       if ( copy_from_user( &ctx, (drm_ctx_t __user *)arg, sizeof(ctx) ) )
+               return -EFAULT;
+
+       DRM_DEBUG( "%d\n", ctx.handle );
+       return drm_context_switch( dev, dev->last_context, ctx.handle );
+}
+
+/**
+ * New context.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument pointing to a drm_ctx structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Calls context_switch_complete().
+ */
+int drm_newctx( 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_ctx_t ctx;
+
+       if ( copy_from_user( &ctx, (drm_ctx_t __user *)arg, sizeof(ctx) ) )
+               return -EFAULT;
+
+       DRM_DEBUG( "%d\n", ctx.handle );
+       drm_context_switch_complete( dev, ctx.handle );
+
+       return 0;
+}
+
+/**
+ * Remove context.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument pointing to a drm_ctx structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * If not the special kernel context, calls ctxbitmap_free() to free the specified context.
+ */
+int drm_rmctx( 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_ctx_t ctx;
+
+       if ( copy_from_user( &ctx, (drm_ctx_t __user *)arg, sizeof(ctx) ) )
+               return -EFAULT;
+
+       DRM_DEBUG( "%d\n", ctx.handle );
+       if ( ctx.handle == DRM_KERNEL_CONTEXT + 1 ) {
+               priv->remove_auth_on_close = 1;
+       }
+       if ( ctx.handle != DRM_KERNEL_CONTEXT ) {
+               if (dev->driver->context_dtor)
+                       dev->driver->context_dtor(dev, ctx.handle);
+               drm_ctxbitmap_free( dev, ctx.handle );
+       }
+
+       down( &dev->ctxlist_sem );
+       if ( !list_empty( &dev->ctxlist->head ) ) {
+               drm_ctx_list_t *pos, *n;
+
+               list_for_each_entry_safe( pos, n, &dev->ctxlist->head, head ) {
+                       if ( pos->handle == ctx.handle ) {
+                               list_del( &pos->head );
+                               drm_free( pos, sizeof(*pos), DRM_MEM_CTXLIST );
+                               --dev->ctx_count;
+                       }
+               }
+       }
+       up( &dev->ctxlist_sem );
+
+       return 0;
+}
+
+/*@}*/
+
diff --git a/drivers/char/drm/drm_dma.c b/drivers/char/drm/drm_dma.c
new file mode 100644 (file)
index 0000000..4a28c05
--- /dev/null
@@ -0,0 +1,180 @@
+/**
+ * \file drm_dma.h 
+ * DMA IOCTL and function support
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * 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"
+
+/**
+ * Initialize the DMA data.
+ * 
+ * \param dev DRM device.
+ * \return zero on success or a negative value on failure.
+ *
+ * Allocate and initialize a drm_device_dma structure.
+ */
+int drm_dma_setup( drm_device_t *dev )
+{
+       int i;
+
+       dev->dma = drm_alloc( sizeof(*dev->dma), DRM_MEM_DRIVER );
+       if ( !dev->dma )
+               return -ENOMEM;
+
+       memset( dev->dma, 0, sizeof(*dev->dma) );
+
+       for ( i = 0 ; i <= DRM_MAX_ORDER ; i++ )
+               memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0]));
+
+       return 0;
+}
+
+/**
+ * Cleanup the DMA resources.
+ *
+ * \param dev DRM device.
+ *
+ * Free all pages associated with DMA buffers, the buffers and pages lists, and
+ * finally the the drm_device::dma structure itself.
+ */
+void drm_dma_takedown(drm_device_t *dev)
+{
+       drm_device_dma_t  *dma = dev->dma;
+       int               i, j;
+
+       if (!dma) return;
+
+                               /* Clear dma buffers */
+       for (i = 0; i <= DRM_MAX_ORDER; i++) {
+               if (dma->bufs[i].seg_count) {
+                       DRM_DEBUG("order %d: buf_count = %d,"
+                                 " seg_count = %d\n",
+                                 i,
+                                 dma->bufs[i].buf_count,
+                                 dma->bufs[i].seg_count);
+                       for (j = 0; j < dma->bufs[i].seg_count; j++) {
+                               if (dma->bufs[i].seglist[j]) {
+                                       drm_free_pages(dma->bufs[i].seglist[j],
+                                                       dma->bufs[i].page_order,
+                                                       DRM_MEM_DMA);
+                               }
+                       }
+                       drm_free(dma->bufs[i].seglist,
+                                 dma->bufs[i].seg_count
+                                 * sizeof(*dma->bufs[0].seglist),
+                                 DRM_MEM_SEGS);
+               }
+               if (dma->bufs[i].buf_count) {
+                       for (j = 0; j < dma->bufs[i].buf_count; j++) {
+                               if (dma->bufs[i].buflist[j].dev_private) {
+                                       drm_free(dma->bufs[i].buflist[j].dev_private,
+                                                 dma->bufs[i].buflist[j].dev_priv_size,
+                                                 DRM_MEM_BUFS);
+                               }
+                       }
+                       drm_free(dma->bufs[i].buflist,
+                                 dma->bufs[i].buf_count *
+                                 sizeof(*dma->bufs[0].buflist),
+                                 DRM_MEM_BUFS);
+               }
+       }
+
+       if (dma->buflist) {
+               drm_free(dma->buflist,
+                         dma->buf_count * sizeof(*dma->buflist),
+                         DRM_MEM_BUFS);
+       }
+
+       if (dma->pagelist) {
+               drm_free(dma->pagelist,
+                         dma->page_count * sizeof(*dma->pagelist),
+                         DRM_MEM_PAGES);
+       }
+       drm_free(dev->dma, sizeof(*dev->dma), DRM_MEM_DRIVER);
+       dev->dma = NULL;
+}
+
+
+/**
+ * Free a buffer.
+ *
+ * \param dev DRM device.
+ * \param buf buffer to free.
+ * 
+ * Resets the fields of \p buf.
+ */
+void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf)
+{
+       if (!buf) return;
+
+       buf->waiting  = 0;
+       buf->pending  = 0;
+       buf->filp     = NULL;
+       buf->used     = 0;
+
+       if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) && waitqueue_active(&buf->dma_wait)) {
+               wake_up_interruptible(&buf->dma_wait);
+       }
+}
+
+/**
+ * Reclaim the buffers.
+ *
+ * \param filp file pointer.
+ *
+ * Frees each buffer associated with \p filp not already on the hardware.
+ */
+void drm_core_reclaim_buffers(drm_device_t *dev, struct file *filp)
+{
+       drm_device_dma_t *dma = dev->dma;
+       int              i;
+
+       if (!dma) return;
+       for (i = 0; i < dma->buf_count; i++) {
+               if (dma->buflist[i]->filp == filp) {
+                       switch (dma->buflist[i]->list) {
+                       case DRM_LIST_NONE:
+                               drm_free_buffer(dev, dma->buflist[i]);
+                               break;
+                       case DRM_LIST_WAIT:
+                               dma->buflist[i]->list = DRM_LIST_RECLAIM;
+                               break;
+                       default:
+                               /* Buffer already on hardware. */
+                               break;
+                       }
+               }
+       }
+}
+EXPORT_SYMBOL(drm_core_reclaim_buffers);
+
diff --git a/drivers/char/drm/drm_drawable.c b/drivers/char/drm/drm_drawable.c
new file mode 100644 (file)
index 0000000..e8e8e42
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * \file drm_drawable.h 
+ * IOCTLs for drawables
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
+ *
+ * Copyright 1999 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"
+
+/** No-op. */
+int drm_adddraw(struct inode *inode, struct file *filp,
+                unsigned int cmd, unsigned long arg)
+{
+       drm_draw_t draw;
+
+       draw.handle = 0;        /* NOOP */
+       DRM_DEBUG("%d\n", draw.handle);
+       if (copy_to_user((drm_draw_t __user *)arg, &draw, sizeof(draw)))
+               return -EFAULT;
+       return 0;
+}
+
+/** No-op. */
+int drm_rmdraw(struct inode *inode, struct file *filp,
+               unsigned int cmd, unsigned long arg)
+{
+       return 0;               /* NOOP */
+}
diff --git a/drivers/char/drm/drm_drv.c b/drivers/char/drm/drm_drv.c
new file mode 100644 (file)
index 0000000..5845d98
--- /dev/null
@@ -0,0 +1,545 @@
+/**
+ * \file drm_drv.h 
+ * Generic driver template
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ *
+ * To use this template, you must at least define the following (samples
+ * given for the MGA driver):
+ *
+ * \code
+ * #define DRIVER_AUTHOR       "VA Linux Systems, Inc."
+ *
+ * #define DRIVER_NAME         "mga"
+ * #define DRIVER_DESC         "Matrox G200/G400"
+ * #define DRIVER_DATE         "20001127"
+ *
+ * #define DRIVER_MAJOR                2
+ * #define DRIVER_MINOR                0
+ * #define DRIVER_PATCHLEVEL   2
+ *
+ * #define DRIVER_IOCTL_COUNT  DRM_ARRAY_SIZE( mga_ioctls )
+ *
+ * #define drm_x               mga_##x
+ * \endcode
+ */
+
+/*
+ * Created: Thu Nov 23 03:10:50 2000 by gareth@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 "drm_core.h"
+
+/** Ioctl table */
+drm_ioctl_desc_t                 drm_ioctls[] = {
+       [DRM_IOCTL_NR(DRM_IOCTL_VERSION)]       = { drm_version,     0, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]    = { drm_getunique,   0, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]     = { drm_getmagic,    0, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]     = { drm_irq_by_busid, 0, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_GET_MAP)]       = { drm_getmap,      0, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_GET_CLIENT)]    = { drm_getclient,   0, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_GET_STATS)]     = { drm_getstats,    0, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_SET_VERSION)]   = { drm_setversion,  0, 1 },
+
+       [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]    = { drm_setunique,   1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]         = { drm_noop,        1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]       = { drm_noop,        1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]    = { drm_authmagic,   1, 1 },
+
+       [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]       = { drm_addmap,      1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_RM_MAP)]        = { drm_rmmap,       1, 0 },
+
+       [DRM_IOCTL_NR(DRM_IOCTL_SET_SAREA_CTX)] = { drm_setsareactx, 1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_GET_SAREA_CTX)] = { drm_getsareactx, 1, 0 },
+
+       [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]       = { drm_addctx,      1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]        = { drm_rmctx,       1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]       = { drm_modctx,      1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]       = { drm_getctx,      1, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]    = { drm_switchctx,   1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]       = { drm_newctx,      1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]       = { drm_resctx,      1, 0 },
+
+       [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]      = { drm_adddraw,     1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]       = { drm_rmdraw,      1, 1 },
+
+       [DRM_IOCTL_NR(DRM_IOCTL_LOCK)]          = { drm_lock,        1, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]        = { drm_unlock,      1, 0 },
+
+       [DRM_IOCTL_NR(DRM_IOCTL_FINISH)]        = { drm_noop,      1, 0 },
+
+       [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]      = { drm_addbufs,     1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]     = { drm_markbufs,    1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]     = { drm_infobufs,    1, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]      = { drm_mapbufs,     1, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]     = { drm_freebufs,    1, 0 },
+       /* The DRM_IOCTL_DMA ioctl should be defined by the driver. */
+
+       [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]       = { drm_control,     1, 1 },
+
+#if __OS_HAS_AGP
+       [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)]   = { drm_agp_acquire, 1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)]   = { drm_agp_release, 1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]    = { drm_agp_enable,  1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]      = { drm_agp_info,    1, 0 },
+       [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]     = { drm_agp_alloc,   1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]      = { drm_agp_free,    1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]      = { drm_agp_bind,    1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]    = { drm_agp_unbind,  1, 1 },
+#endif
+
+       [DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC)]      = { drm_sg_alloc,    1, 1 },
+       [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)]       = { drm_sg_free,     1, 1 },
+
+       [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)]   = { drm_wait_vblank, 0, 0 },
+};
+
+#define DRIVER_IOCTL_COUNT     DRM_ARRAY_SIZE( drm_ioctls )
+
+/**
+ * Take down the DRM device.
+ *
+ * \param dev DRM device structure.
+ *
+ * Frees every resource in \p dev.
+ *
+ * \sa drm_device and setup().
+ */
+int drm_takedown( drm_device_t *dev )
+{
+       drm_magic_entry_t *pt, *next;
+       drm_map_t *map;
+       drm_map_list_t *r_list;
+       struct list_head *list, *list_next;
+       drm_vma_entry_t *vma, *vma_next;
+       int i;
+
+       DRM_DEBUG( "\n" );
+
+       if (dev->driver->pretakedown)
+         dev->driver->pretakedown(dev);
+
+       if ( dev->irq_enabled ) drm_irq_uninstall( dev );
+
+       down( &dev->struct_sem );
+       del_timer( &dev->timer );
+
+       if ( dev->devname ) {
+               drm_free( dev->devname, strlen( dev->devname ) + 1,
+                          DRM_MEM_DRIVER );
+               dev->devname = NULL;
+       }
+
+       if ( dev->unique ) {
+               drm_free( dev->unique, strlen( dev->unique ) + 1,
+                          DRM_MEM_DRIVER );
+               dev->unique = NULL;
+               dev->unique_len = 0;
+       }
+                               /* Clear pid list */
+       for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) {
+               for ( pt = dev->magiclist[i].head ; pt ; pt = next ) {
+                       next = pt->next;
+                       drm_free( pt, sizeof(*pt), DRM_MEM_MAGIC );
+               }
+               dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+       }
+
+                               /* Clear AGP information */
+       if (drm_core_has_AGP(dev) && dev->agp) {
+               drm_agp_mem_t *entry;
+               drm_agp_mem_t *nexte;
+
+                               /* Remove AGP resources, but leave dev->agp
+                                   intact until drv_cleanup is called. */
+               for ( entry = dev->agp->memory ; entry ; entry = nexte ) {
+                       nexte = entry->next;
+                       if ( entry->bound ) drm_unbind_agp( entry->memory );
+                       drm_free_agp( entry->memory, entry->pages );
+                       drm_free( entry, sizeof(*entry), DRM_MEM_AGPLISTS );
+               }
+               dev->agp->memory = NULL;
+
+               if ( dev->agp->acquired ) drm_agp_do_release();
+
+               dev->agp->acquired = 0;
+               dev->agp->enabled  = 0;
+       }
+
+                               /* Clear vma list (only built for debugging) */
+       if ( dev->vmalist ) {
+               for ( vma = dev->vmalist ; vma ; vma = vma_next ) {
+                       vma_next = vma->next;
+                       drm_free( vma, sizeof(*vma), DRM_MEM_VMAS );
+               }
+               dev->vmalist = NULL;
+       }
+
+       if( dev->maplist ) {
+               list_for_each_safe( list, list_next, &dev->maplist->head ) {
+                       r_list = (drm_map_list_t *)list;
+
+                       if ( ( map = r_list->map ) ) {
+                               switch ( map->type ) {
+                               case _DRM_REGISTERS:
+                               case _DRM_FRAME_BUFFER:
+                                       if (drm_core_has_MTRR(dev)) {
+                                               if ( map->mtrr >= 0 ) {
+                                                       int retcode;
+                                                       retcode = mtrr_del( map->mtrr,
+                                                                           map->offset,
+                                                                           map->size );
+                                                       DRM_DEBUG( "mtrr_del=%d\n", retcode );
+                                               }
+                                       }
+                                       drm_ioremapfree( map->handle, map->size, dev );
+                                       break;
+                               case _DRM_SHM:
+                                       vfree(map->handle);
+                                       break;
+
+                               case _DRM_AGP:
+                                       /* Do nothing here, because this is all
+                                        * handled in the AGP/GART driver.
+                                        */
+                                       break;
+                               case _DRM_SCATTER_GATHER:
+                                       /* Handle it */
+                                       if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg) {
+                                               drm_sg_cleanup(dev->sg);
+                                               dev->sg = NULL;
+                                       }
+                                       break;
+                               }
+                               drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+                       }
+                       list_del( list );
+                       drm_free(r_list, sizeof(*r_list), DRM_MEM_MAPS);
+               }
+               drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
+               dev->maplist = NULL;
+       }
+
+       if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) && dev->queuelist ) {
+               for ( i = 0 ; i < dev->queue_count ; i++ ) {
+                       if ( dev->queuelist[i] ) {
+                               drm_free( dev->queuelist[i],
+                                         sizeof(*dev->queuelist[0]),
+                                         DRM_MEM_QUEUES );
+                               dev->queuelist[i] = NULL;
+                       }
+               }
+               drm_free( dev->queuelist,
+                         dev->queue_slots * sizeof(*dev->queuelist),
+                         DRM_MEM_QUEUES );
+               dev->queuelist = NULL;
+       }
+       dev->queue_count = 0;
+
+       if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+               drm_dma_takedown( dev );
+
+       if ( dev->lock.hw_lock ) {
+               dev->sigdata.lock = dev->lock.hw_lock = NULL; /* SHM removed */
+               dev->lock.filp = NULL;
+               wake_up_interruptible( &dev->lock.lock_queue );
+       }
+       up( &dev->struct_sem );
+
+       return 0;
+}
+
+
+
+/**
+ * Module initialization. Called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported).
+ *
+ * \return zero on success or a negative number on failure.
+ *
+ * Initializes an array of drm_device structures, and attempts to
+ * initialize all available devices, using consecutive minors, registering the
+ * stubs and initializing the AGP device.
+ * 
+ * Expands the \c DRIVER_PREINIT and \c DRIVER_POST_INIT macros before and
+ * after the initialization for driver customization.
+ */
+int drm_init( struct drm_driver *driver )
+{
+       struct pci_dev *pdev = NULL;
+       struct pci_device_id *pid;
+       int i;
+
+       DRM_DEBUG( "\n" );
+
+       drm_mem_init();
+
+       for (i=0; driver->pci_driver.id_table[i].vendor != 0; i++) {
+               pid = (struct pci_device_id *)&driver->pci_driver.id_table[i];
+               
+               pdev=NULL;
+               /* pass back in pdev to account for multiple identical cards */         
+               while ((pdev = pci_get_subsys(pid->vendor, pid->device, pid->subvendor, pid->subdevice, pdev)) != NULL) {
+                       /* stealth mode requires a manual probe */
+                       pci_dev_get(pdev);
+                       drm_probe(pdev, pid, driver);
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL(drm_init);
+
+/**
+ * Called via cleanup_module() at module unload time.
+ *
+ * Cleans up all DRM device, calling takedown().
+ * 
+ * \sa drm_init().
+ */
+static void drm_cleanup( drm_device_t *dev )
+{
+       DRM_DEBUG( "\n" );
+
+       if (!dev) {
+               DRM_ERROR("cleanup called no dev\n");
+               return;
+       }
+
+       drm_takedown( dev );    
+
+       drm_ctxbitmap_cleanup( dev );
+       
+       if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) &&
+           dev->agp && dev->agp->agp_mtrr >= 0) {
+               int retval;
+               retval = mtrr_del( dev->agp->agp_mtrr,
+                                  dev->agp->agp_info.aper_base,
+                                  dev->agp->agp_info.aper_size*1024*1024 );
+               DRM_DEBUG( "mtrr_del=%d\n", retval );
+       }
+       
+       if (drm_core_has_AGP(dev) && dev->agp ) {
+               drm_free( dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS );
+               dev->agp = NULL;
+       }
+
+       if (dev->driver->postcleanup)
+               dev->driver->postcleanup(dev);
+       
+       if ( drm_put_minor(dev) )
+               DRM_ERROR( "Cannot unload module\n" );
+}
+
+void drm_exit (struct drm_driver *driver)
+{
+       int i;
+       drm_device_t *dev = NULL;
+       drm_minor_t *minor;
+       
+       DRM_DEBUG( "\n" );
+
+       for (i = 0; i < drm_cards_limit; i++) {
+               minor = &drm_minors[i];
+               if (!minor->dev)
+                       continue;
+               if (minor->dev->driver!=driver)
+                       continue;
+
+               dev = minor->dev;
+               
+       }
+       if (dev) {
+               /* release the pci driver */
+               if (dev->pdev)
+                       pci_dev_put(dev->pdev);
+               drm_cleanup(dev);
+       }
+       
+       DRM_INFO( "Module unloaded\n" );
+}
+EXPORT_SYMBOL(drm_exit);
+
+/** File operations structure */
+static struct file_operations drm_stub_fops = {
+       .owner = THIS_MODULE,
+       .open  = drm_stub_open
+};
+
+static int __init drm_core_init(void)
+{
+       int ret = -ENOMEM;
+       
+       drm_cards_limit = (drm_cards_limit < DRM_MAX_MINOR + 1 ? drm_cards_limit : DRM_MAX_MINOR + 1);
+       drm_minors = drm_calloc(drm_cards_limit,
+                               sizeof(*drm_minors), DRM_MEM_STUB);
+       if(!drm_minors) 
+               goto err_p1;
+       
+       if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops))
+               goto err_p1;
+       
+       drm_class = drm_sysfs_create(THIS_MODULE, "drm");
+       if (IS_ERR(drm_class)) {
+               printk (KERN_ERR "DRM: Error creating drm class.\n");
+               ret = PTR_ERR(drm_class);
+               goto err_p2;
+       }
+
+       drm_proc_root = create_proc_entry("dri", S_IFDIR, NULL);
+       if (!drm_proc_root) {
+               DRM_ERROR("Cannot create /proc/dri\n");
+               ret = -1;
+               goto err_p3;
+       }
+               
+       DRM_INFO( "Initialized %s %d.%d.%d %s\n",
+               DRIVER_NAME,
+               DRIVER_MAJOR,
+               DRIVER_MINOR,
+               DRIVER_PATCHLEVEL,
+               DRIVER_DATE
+               );
+       return 0;
+err_p3:
+       drm_sysfs_destroy(drm_class);
+err_p2:
+       unregister_chrdev(DRM_MAJOR, "drm");
+       drm_free(drm_minors, sizeof(*drm_minors) * drm_cards_limit, DRM_MEM_STUB);
+err_p1:        
+       return ret;
+}
+
+static void __exit drm_core_exit (void)
+{
+       remove_proc_entry("dri", NULL);
+       drm_sysfs_destroy(drm_class);
+
+       unregister_chrdev(DRM_MAJOR, "drm");
+
+       drm_free(drm_minors, sizeof(*drm_minors) *
+                               drm_cards_limit, DRM_MEM_STUB);
+}
+
+
+module_init( drm_core_init );
+module_exit( drm_core_exit );
+
+
+/**
+ * Get version information
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_version structure.
+ * \return zero on success or negative number on failure.
+ *
+ * Fills in the version information in \p arg.
+ */
+int drm_version( 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_version_t __user *argp = (void __user *)arg;
+       drm_version_t version;
+       int ret;
+
+       if ( copy_from_user( &version, argp, sizeof(version) ) )
+               return -EFAULT;
+
+       /* version is a required function to return the personality module version */
+       if ((ret = dev->driver->version(&version)))
+               return ret;
+               
+       if ( copy_to_user( argp, &version, sizeof(version) ) )
+               return -EFAULT;
+       return 0;
+}
+
+
+
+/** 
+ * Called whenever a process performs an ioctl on /dev/drm.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument.
+ * \return zero on success or negative number on failure.
+ *
+ * Looks up the ioctl function in the ::ioctls table, checking for root
+ * previleges if so required, and dispatches to the respective function.
+ */
+int drm_ioctl( 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_ioctl_desc_t *ioctl;
+       drm_ioctl_t *func;
+       unsigned int nr = DRM_IOCTL_NR(cmd);
+       int retcode = -EINVAL;
+
+       atomic_inc( &dev->ioctl_count );
+       atomic_inc( &dev->counts[_DRM_STAT_IOCTLS] );
+       ++priv->ioctl_count;
+
+       DRM_DEBUG( "pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%lx, auth=%d\n",
+                  current->pid, cmd, nr, (long)old_encode_dev(dev->device), 
+                  priv->authenticated );
+       
+       if (nr < DRIVER_IOCTL_COUNT)
+               ioctl = &drm_ioctls[nr];
+       else if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls))
+               ioctl = &dev->driver->ioctls[nr - DRM_COMMAND_BASE];
+       else
+               goto err_i1;
+       
+       func = ioctl->func;
+       /* is there a local override? */
+       if ((nr == DRM_IOCTL_NR(DRM_IOCTL_DMA)) && dev->driver->dma_ioctl)
+               func = dev->driver->dma_ioctl;
+       
+       if ( !func ) {
+               DRM_DEBUG( "no function\n" );
+               retcode = -EINVAL;
+       } else if ( ( ioctl->root_only && !capable( CAP_SYS_ADMIN ) )||
+                   ( ioctl->auth_needed && !priv->authenticated ) ) {
+               retcode = -EACCES;
+       } else {
+               retcode = func( inode, filp, cmd, arg );
+       }
+       
+err_i1:
+       atomic_dec( &dev->ioctl_count );
+       if (retcode) DRM_DEBUG( "ret = %x\n", retcode);
+       return retcode;
+}
+EXPORT_SYMBOL(drm_ioctl);
+
diff --git a/drivers/char/drm/drm_fops.c b/drivers/char/drm/drm_fops.c
new file mode 100644 (file)
index 0000000..e967efb
--- /dev/null
@@ -0,0 +1,449 @@
+/**
+ * \file drm_fops.h 
+ * File operations for DRM
+ * 
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Daryll Strauss <daryll@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Mon Jan  4 08:58:31 1999 by faith@valinux.com
+ *
+ * Copyright 1999 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 <linux/poll.h>
+
+static int drm_setup( drm_device_t *dev )
+{
+       int i;
+       int ret;
+
+       if (dev->driver->presetup)
+       {
+               ret=dev->driver->presetup(dev);
+               if (ret!=0) 
+                       return ret;
+       }
+
+       atomic_set( &dev->ioctl_count, 0 );
+       atomic_set( &dev->vma_count, 0 );
+       dev->buf_use = 0;
+       atomic_set( &dev->buf_alloc, 0 );
+
+       if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+       {
+               i = drm_dma_setup( dev );
+               if ( i < 0 )
+                       return i;
+       }
+
+       for ( i = 0 ; i < DRM_ARRAY_SIZE(dev->counts) ; i++ )
+               atomic_set( &dev->counts[i], 0 );
+
+       for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) {
+               dev->magiclist[i].head = NULL;
+               dev->magiclist[i].tail = NULL;
+       }
+
+       dev->maplist = drm_alloc(sizeof(*dev->maplist),
+                                 DRM_MEM_MAPS);
+       if(dev->maplist == NULL) return -ENOMEM;
+       memset(dev->maplist, 0, sizeof(*dev->maplist));
+       INIT_LIST_HEAD(&dev->maplist->head);
+
+       dev->ctxlist = drm_alloc(sizeof(*dev->ctxlist),
+                                 DRM_MEM_CTXLIST);
+       if(dev->ctxlist == NULL) return -ENOMEM;
+       memset(dev->ctxlist, 0, sizeof(*dev->ctxlist));
+       INIT_LIST_HEAD(&dev->ctxlist->head);
+
+       dev->vmalist = NULL;
+       dev->sigdata.lock = dev->lock.hw_lock = NULL;
+       init_waitqueue_head( &dev->lock.lock_queue );
+       dev->queue_count = 0;
+       dev->queue_reserved = 0;
+       dev->queue_slots = 0;
+       dev->queuelist = NULL;
+       dev->irq_enabled = 0;
+       dev->context_flag = 0;
+       dev->interrupt_flag = 0;
+       dev->dma_flag = 0;
+       dev->last_context = 0;
+       dev->last_switch = 0;
+       dev->last_checked = 0;
+       init_waitqueue_head( &dev->context_wait );
+       dev->if_version = 0;
+
+       dev->ctx_start = 0;
+       dev->lck_start = 0;
+
+       dev->buf_rp = dev->buf;
+       dev->buf_wp = dev->buf;
+       dev->buf_end = dev->buf + DRM_BSZ;
+       dev->buf_async = NULL;
+       init_waitqueue_head( &dev->buf_readers );
+       init_waitqueue_head( &dev->buf_writers );
+
+       DRM_DEBUG( "\n" );
+
+       /*
+        * The kernel's context could be created here, but is now created
+        * in drm_dma_enqueue.  This is more resource-efficient for
+        * hardware that does not do DMA, but may mean that
+        * drm_select_queue fails between the time the interrupt is
+        * initialized and the time the queues are initialized.
+        */
+       if (dev->driver->postsetup)
+               dev->driver->postsetup(dev);
+
+       return 0;
+}
+
+/**
+ * Open file.
+ * 
+ * \param inode device inode
+ * \param filp file pointer.
+ * \return zero on success or a negative number on failure.
+ *
+ * Searches the DRM device with the same minor number, calls open_helper(), and
+ * increments the device open count. If the open count was previous at zero,
+ * i.e., it's the first that the device is open, then calls setup().
+ */
+int drm_open( struct inode *inode, struct file *filp )
+{
+       drm_device_t *dev = NULL;
+       int minor = iminor(inode);
+       int retcode = 0;
+
+       if (!((minor >= 0) && (minor < drm_cards_limit)))
+               return -ENODEV;
+               
+       dev = drm_minors[minor].dev;
+       if (!dev)
+               return -ENODEV;
+       
+       retcode = drm_open_helper( inode, filp, dev );
+       if ( !retcode ) {
+               atomic_inc( &dev->counts[_DRM_STAT_OPENS] );
+               spin_lock( &dev->count_lock );
+               if ( !dev->open_count++ ) {
+                       spin_unlock( &dev->count_lock );
+                       return drm_setup( dev );
+               }
+               spin_unlock( &dev->count_lock );
+       }
+
+       return retcode;
+}
+EXPORT_SYMBOL(drm_open);
+
+/**
+ * Release file.
+ *
+ * \param inode device inode
+ * \param filp file pointer.
+ * \return zero on success or a negative number on failure.
+ *
+ * If the hardware lock is held then free it, and take it again for the kernel
+ * context since it's necessary to reclaim buffers. Unlink the file private
+ * data from its list and free it. Decreases the open count and if it reaches
+ * zero calls takedown().
+ */
+int drm_release( struct inode *inode, struct file *filp )
+{
+       drm_file_t *priv = filp->private_data;
+       drm_device_t *dev;
+       int retcode = 0;
+
+       lock_kernel();
+       dev = priv->dev;
+
+       DRM_DEBUG( "open_count = %d\n", dev->open_count );
+
+       if (dev->driver->prerelease)
+               dev->driver->prerelease(dev, filp);
+
+       /* ========================================================
+        * Begin inline drm_release
+        */
+
+       DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n",
+                  current->pid, (long)old_encode_dev(dev->device), dev->open_count );
+
+       if ( priv->lock_count && dev->lock.hw_lock &&
+            _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
+            dev->lock.filp == filp ) {
+               DRM_DEBUG( "File %p released, freeing lock for context %d\n",
+                       filp,
+                       _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) );
+               
+               if (dev->driver->release)
+                       dev->driver->release(dev, filp);
+
+               drm_lock_free( dev, &dev->lock.hw_lock->lock,
+                               _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) );
+
+                               /* FIXME: may require heavy-handed reset of
+                                   hardware at this point, possibly
+                                   processed via a callback to the X
+                                   server. */
+       }
+       else if ( dev->driver->release && priv->lock_count && dev->lock.hw_lock ) {
+               /* The lock is required to reclaim buffers */
+               DECLARE_WAITQUEUE( entry, current );
+
+               add_wait_queue( &dev->lock.lock_queue, &entry );
+               for (;;) {
+                       __set_current_state(TASK_INTERRUPTIBLE);
+                       if ( !dev->lock.hw_lock ) {
+                               /* Device has been unregistered */
+                               retcode = -EINTR;
+                               break;
+                       }
+                       if ( drm_lock_take( &dev->lock.hw_lock->lock,
+                                            DRM_KERNEL_CONTEXT ) ) {
+                               dev->lock.filp      = filp;
+                               dev->lock.lock_time = jiffies;
+                                atomic_inc( &dev->counts[_DRM_STAT_LOCKS] );
+                               break;  /* Got lock */
+                       }
+                               /* Contention */
+                       schedule();
+                       if ( signal_pending( current ) ) {
+                               retcode = -ERESTARTSYS;
+                               break;
+                       }
+               }
+               __set_current_state(TASK_RUNNING);
+               remove_wait_queue( &dev->lock.lock_queue, &entry );
+               if( !retcode ) {
+                       if (dev->driver->release)
+                               dev->driver->release(dev, filp);
+                       drm_lock_free( dev, &dev->lock.hw_lock->lock,
+                                       DRM_KERNEL_CONTEXT );
+               }
+       }
+       
+       if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
+       {
+               dev->driver->reclaim_buffers(dev, filp);
+       }
+
+       drm_fasync( -1, filp, 0 );
+
+       down( &dev->ctxlist_sem );
+       if ( !list_empty( &dev->ctxlist->head ) ) {
+               drm_ctx_list_t *pos, *n;
+
+               list_for_each_entry_safe( pos, n, &dev->ctxlist->head, head ) {
+                       if ( pos->tag == priv &&
+                            pos->handle != DRM_KERNEL_CONTEXT ) {
+                               if (dev->driver->context_dtor)
+                                       dev->driver->context_dtor(dev, pos->handle);
+
+                               drm_ctxbitmap_free( dev, pos->handle );
+
+                               list_del( &pos->head );
+                               drm_free( pos, sizeof(*pos), DRM_MEM_CTXLIST );
+                               --dev->ctx_count;
+                       }
+               }
+       }
+       up( &dev->ctxlist_sem );
+
+       down( &dev->struct_sem );
+       if ( priv->remove_auth_on_close == 1 ) {
+               drm_file_t *temp = dev->file_first;
+               while ( temp ) {
+                       temp->authenticated = 0;
+                       temp = temp->next;
+               }
+       }
+       if ( priv->prev ) {
+               priv->prev->next = priv->next;
+       } else {
+               dev->file_first  = priv->next;
+       }
+       if ( priv->next ) {
+               priv->next->prev = priv->prev;
+       } else {
+               dev->file_last   = priv->prev;
+       }
+       up( &dev->struct_sem );
+       
+       if (dev->driver->free_filp_priv)
+               dev->driver->free_filp_priv(dev, priv);
+
+       drm_free( priv, sizeof(*priv), DRM_MEM_FILES );
+
+       /* ========================================================
+        * End inline drm_release
+        */
+
+       atomic_inc( &dev->counts[_DRM_STAT_CLOSES] );
+       spin_lock( &dev->count_lock );
+       if ( !--dev->open_count ) {
+               if ( atomic_read( &dev->ioctl_count ) || dev->blocked ) {
+                       DRM_ERROR( "Device busy: %d %d\n",
+                                  atomic_read( &dev->ioctl_count ),
+                                  dev->blocked );
+                       spin_unlock( &dev->count_lock );
+                       unlock_kernel();
+                       return -EBUSY;
+               }
+               spin_unlock( &dev->count_lock );
+               unlock_kernel();
+               return drm_takedown( dev );
+       }
+       spin_unlock( &dev->count_lock );
+
+       unlock_kernel();
+
+       return retcode;
+}
+EXPORT_SYMBOL(drm_release);
+
+/**
+ * Called whenever a process opens /dev/drm. 
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param dev device.
+ * \return zero on success or a negative number on failure.
+ * 
+ * Creates and initializes a drm_file structure for the file private data in \p
+ * filp and add it into the double linked list in \p dev.
+ */
+int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev)
+{
+       int          minor = iminor(inode);
+       drm_file_t   *priv;
+       int ret;
+
+       if (filp->f_flags & O_EXCL)   return -EBUSY; /* No exclusive opens */
+       if (!drm_cpu_valid())        return -EINVAL;
+
+       DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor);
+
+       priv                = drm_alloc(sizeof(*priv), DRM_MEM_FILES);
+       if(!priv) return -ENOMEM;
+
+       memset(priv, 0, sizeof(*priv));
+       filp->private_data  = priv;
+       priv->uid           = current->euid;
+       priv->pid           = current->pid;
+       priv->minor         = minor;
+       priv->dev           = dev;
+       priv->ioctl_count   = 0;
+       priv->authenticated = capable(CAP_SYS_ADMIN);
+       priv->lock_count    = 0;
+
+       if (dev->driver->open_helper) {
+               ret=dev->driver->open_helper(dev, priv);
+               if (ret < 0)
+                       goto out_free;
+       }
+
+       down(&dev->struct_sem);
+       if (!dev->file_last) {
+               priv->next      = NULL;
+               priv->prev      = NULL;
+               dev->file_first = priv;
+               dev->file_last  = priv;
+       } else {
+               priv->next           = NULL;
+               priv->prev           = dev->file_last;
+               dev->file_last->next = priv;
+               dev->file_last       = priv;
+       }
+       up(&dev->struct_sem);
+
+#ifdef __alpha__
+       /*
+        * Default the hose
+        */
+       if (!dev->hose) {
+               struct pci_dev *pci_dev;
+               pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL);
+               if (pci_dev) {
+                       dev->hose = pci_dev->sysdata;
+                       pci_dev_put(pci_dev);
+               }
+               if (!dev->hose) {
+                       struct pci_bus *b = pci_bus_b(pci_root_buses.next);
+                       if (b) dev->hose = b->sysdata;
+               }
+       }
+#endif
+
+       return 0;
+out_free:
+       drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+       filp->private_data=NULL;
+       return ret;
+}
+
+/** No-op. */
+int drm_flush(struct file *filp)
+{
+       drm_file_t    *priv   = filp->private_data;
+       drm_device_t  *dev    = priv->dev;
+
+       DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
+                 current->pid, (long)old_encode_dev(dev->device), dev->open_count);
+       return 0;
+}
+EXPORT_SYMBOL(drm_flush);
+
+/** No-op. */
+int drm_fasync(int fd, struct file *filp, int on)
+{
+       drm_file_t    *priv   = filp->private_data;
+       drm_device_t  *dev    = priv->dev;
+       int           retcode;
+
+       DRM_DEBUG("fd = %d, device = 0x%lx\n", fd, (long)old_encode_dev(dev->device));
+       retcode = fasync_helper(fd, filp, on, &dev->buf_async);
+       if (retcode < 0) return retcode;
+       return 0;
+}
+EXPORT_SYMBOL(drm_fasync);
+
+/** No-op. */
+unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
+{
+       return 0;
+}
+EXPORT_SYMBOL(drm_poll);
+
+
+/** No-op. */
+ssize_t drm_read(struct file *filp, char __user *buf, size_t count, loff_t *off)
+{
+       return 0;
+}
diff --git a/drivers/char/drm/drm_init.c b/drivers/char/drm/drm_init.c
new file mode 100644 (file)
index 0000000..62883b7
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * \file drm_init.h 
+ * Setup/Cleanup for DRM
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Mon Jan  4 08:58:31 1999 by faith@valinux.com
+ *
+ * Copyright 1999 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"
+
+/**
+ * Check whether DRI will run on this CPU.
+ *
+ * \return non-zero if the DRI will run on this CPU, or zero otherwise.
+ */
+int drm_cpu_valid(void)
+{
+#if defined(__i386__)
+       if (boot_cpu_data.x86 == 3) return 0; /* No cmpxchg on a 386 */
+#endif
+#if defined(__sparc__) && !defined(__sparc_v9__)
+       return 0; /* No cmpxchg before v9 sparc. */
+#endif
+       return 1;
+}
diff --git a/drivers/char/drm/drm_ioctl.c b/drivers/char/drm/drm_ioctl.c
new file mode 100644 (file)
index 0000000..7621b35
--- /dev/null
@@ -0,0 +1,370 @@
+/**
+ * \file drm_ioctl.h 
+ * IOCTL processing for DRM
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Fri Jan  8 09:01:26 1999 by faith@valinux.com
+ *
+ * Copyright 1999 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 "drm_core.h"
+
+#include "linux/pci.h"
+
+/**
+ * Get the bus id.
+ * 
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_unique structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Copies the bus id from drm_device::unique into user space.
+ */
+int drm_getunique(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_unique_t     __user *argp = (void __user *)arg;
+       drm_unique_t     u;
+
+       if (copy_from_user(&u, argp, sizeof(u)))
+               return -EFAULT;
+       if (u.unique_len >= dev->unique_len) {
+               if (copy_to_user(u.unique, dev->unique, dev->unique_len))
+                       return -EFAULT;
+       }
+       u.unique_len = dev->unique_len;
+       if (copy_to_user(argp, &u, sizeof(u)))
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * Set the bus id.
+ * 
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_unique structure.
+ * \return zero on success or a negative number on failure.
+ *
+ * Copies the bus id from userspace into drm_device::unique, and verifies that
+ * it matches the device this DRM is attached to (EINVAL otherwise).  Deprecated
+ * in interface version 1.1 and will return EBUSY when setversion has requested
+ * version 1.1 or greater.
+ */
+int drm_setunique(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_unique_t     u;
+       int              domain, bus, slot, func, ret;
+
+       if (dev->unique_len || dev->unique) return -EBUSY;
+
+       if (copy_from_user(&u, (drm_unique_t __user *)arg, sizeof(u)))
+               return -EFAULT;
+
+       if (!u.unique_len || u.unique_len > 1024) return -EINVAL;
+
+       dev->unique_len = u.unique_len;
+       dev->unique     = drm_alloc(u.unique_len + 1, DRM_MEM_DRIVER);
+       if(!dev->unique) return -ENOMEM;
+       if (copy_from_user(dev->unique, u.unique, dev->unique_len))
+               return -EFAULT;
+
+       dev->unique[dev->unique_len] = '\0';
+
+       dev->devname = drm_alloc(strlen(dev->driver->pci_driver.name) + strlen(dev->unique) + 2,
+                                 DRM_MEM_DRIVER);
+       if (!dev->devname)
+               return -ENOMEM;
+
+       sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, dev->unique);
+
+       /* Return error if the busid submitted doesn't match the device's actual
+        * busid.
+        */
+       ret = sscanf(dev->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
+       if (ret != 3)
+               return DRM_ERR(EINVAL);
+       domain = bus >> 8;
+       bus &= 0xff;
+       
+       if ((domain != dev->pci_domain) ||
+           (bus != dev->pci_bus) ||
+           (slot != dev->pci_slot) ||
+           (func != dev->pci_func))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int
+drm_set_busid(drm_device_t *dev)
+{
+       if (dev->unique != NULL)
+               return EBUSY;
+
+       dev->unique_len = 20;
+       dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER);
+       if (dev->unique == NULL)
+               return ENOMEM;
+
+       snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%d",
+               dev->pci_domain, dev->pci_bus, dev->pci_slot, dev->pci_func);
+
+       dev->devname = drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len + 2,
+                               DRM_MEM_DRIVER);
+       if (dev->devname == NULL)
+               return ENOMEM;
+
+       sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name, dev->unique);
+
+       return 0;
+}
+
+
+/**
+ * Get a mapping information.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_map structure.
+ * 
+ * \return zero on success or a negative number on failure.
+ *
+ * Searches for the mapping with the specified offset and copies its information
+ * into userspace
+ */
+int drm_getmap( 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_map_t    __user *argp = (void __user *)arg;
+       drm_map_t    map;
+       drm_map_list_t *r_list = NULL;
+       struct list_head *list;
+       int          idx;
+       int          i;
+
+       if (copy_from_user(&map, argp, sizeof(map)))
+               return -EFAULT;
+       idx = map.offset;
+
+       down(&dev->struct_sem);
+       if (idx < 0) {
+               up(&dev->struct_sem);
+               return -EINVAL;
+       }
+
+       i = 0;
+       list_for_each(list, &dev->maplist->head) {
+               if(i == idx) {
+                       r_list = list_entry(list, drm_map_list_t, head);
+                       break;
+               }
+               i++;
+       }
+       if(!r_list || !r_list->map) {
+               up(&dev->struct_sem);
+               return -EINVAL;
+       }
+
+       map.offset = r_list->map->offset;
+       map.size   = r_list->map->size;
+       map.type   = r_list->map->type;
+       map.flags  = r_list->map->flags;
+       map.handle = r_list->map->handle;
+       map.mtrr   = r_list->map->mtrr;
+       up(&dev->struct_sem);
+
+       if (copy_to_user(argp, &map, sizeof(map))) return -EFAULT;
+       return 0;
+}
+
+/**
+ * Get client information.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_client structure.
+ * 
+ * \return zero on success or a negative number on failure.
+ *
+ * Searches for the client with the specified index and copies its information
+ * into userspace
+ */
+int drm_getclient( 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_client_t __user *argp = (void __user *)arg;
+       drm_client_t client;
+       drm_file_t   *pt;
+       int          idx;
+       int          i;
+
+       if (copy_from_user(&client, argp, sizeof(client)))
+               return -EFAULT;
+       idx = client.idx;
+       down(&dev->struct_sem);
+       for (i = 0, pt = dev->file_first; i < idx && pt; i++, pt = pt->next)
+               ;
+
+       if (!pt) {
+               up(&dev->struct_sem);
+               return -EINVAL;
+       }
+       client.auth  = pt->authenticated;
+       client.pid   = pt->pid;
+       client.uid   = pt->uid;
+       client.magic = pt->magic;
+       client.iocs  = pt->ioctl_count;
+       up(&dev->struct_sem);
+
+       if (copy_to_user((drm_client_t __user *)arg, &client, sizeof(client)))
+               return -EFAULT;
+       return 0;
+}
+
+/** 
+ * Get statistics information. 
+ * 
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_stats structure.
+ * 
+ * \return zero on success or a negative number on failure.
+ */
+int drm_getstats( 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_stats_t  stats;
+       int          i;
+
+       memset(&stats, 0, sizeof(stats));
+       
+       down(&dev->struct_sem);
+
+       for (i = 0; i < dev->counters; i++) {
+               if (dev->types[i] == _DRM_STAT_LOCK)
+                       stats.data[i].value
+                               = (dev->lock.hw_lock
+                                  ? dev->lock.hw_lock->lock : 0);
+               else 
+                       stats.data[i].value = atomic_read(&dev->counts[i]);
+               stats.data[i].type  = dev->types[i];
+       }
+       
+       stats.count = dev->counters;
+
+       up(&dev->struct_sem);
+
+       if (copy_to_user((drm_stats_t __user *)arg, &stats, sizeof(stats)))
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * Setversion ioctl.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_lock structure.
+ * \return zero on success or negative number on failure.
+ *
+ * Sets the requested interface version
+ */
+int drm_setversion(DRM_IOCTL_ARGS)
+{
+       DRM_DEVICE;
+       drm_set_version_t sv;
+       drm_set_version_t retv;
+       int if_version;
+       drm_set_version_t __user *argp = (void __user *)data;
+       drm_version_t version;
+
+       DRM_COPY_FROM_USER_IOCTL(sv, argp, sizeof(sv));
+
+       memset(&version, 0, sizeof(version));
+
+       dev->driver->version(&version);
+       retv.drm_di_major = DRM_IF_MAJOR;
+       retv.drm_di_minor = DRM_IF_MINOR;
+       retv.drm_dd_major = version.version_major;
+       retv.drm_dd_minor = version.version_minor;
+
+       DRM_COPY_TO_USER_IOCTL(argp, retv, sizeof(sv));
+
+       if (sv.drm_di_major != -1) {
+               if (sv.drm_di_major != DRM_IF_MAJOR ||
+                   sv.drm_di_minor < 0 || sv.drm_di_minor > DRM_IF_MINOR)
+                       return EINVAL;
+               if_version = DRM_IF_VERSION(sv.drm_di_major, sv.drm_dd_minor);
+               dev->if_version = DRM_MAX(if_version, dev->if_version);
+               if (sv.drm_di_minor >= 1) {
+                       /*
+                        * Version 1.1 includes tying of DRM to specific device
+                        */
+                       drm_set_busid(dev);
+               }
+       }
+
+       if (sv.drm_dd_major != -1) {
+               if (sv.drm_dd_major != version.version_major ||
+                   sv.drm_dd_minor < 0 || sv.drm_dd_minor > version.version_minor)
+                       return EINVAL;
+
+               if (dev->driver->set_version)
+                       dev->driver->set_version(dev, &sv);
+       }
+       return 0;
+}
+
+/** No-op ioctl. */
+int drm_noop(struct inode *inode, struct file *filp, unsigned int cmd,
+              unsigned long arg)
+{
+       DRM_DEBUG("\n");
+       return 0;
+}
diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c
new file mode 100644 (file)
index 0000000..0547a90
--- /dev/null
@@ -0,0 +1,370 @@
+/**
+ * \file drm_irq.h 
+ * IRQ support
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * 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 <linux/interrupt.h>   /* For task queue support */
+
+/**
+ * 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 __user *argp = (void __user *)arg;
+       drm_irq_busid_t p;
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+               return -EINVAL;
+
+       if (copy_from_user(&p, argp, 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(argp, &p, sizeof(p)))
+               return -EFAULT;
+       return 0;
+}
+
+/**
+ * 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;
+       unsigned long sh_flags=0;
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+               return -EINVAL;
+
+       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 (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) {
+               init_waitqueue_head(&dev->vbl_queue);
+               
+               spin_lock_init( &dev->vbl_lock );
+               
+               INIT_LIST_HEAD( &dev->vbl_sigs.head );
+               
+               dev->vbl_pending = 0;
+       }
+
+                               /* Before installing handler */
+       dev->driver->irq_preinstall(dev);
+
+                               /* Install handler */
+       if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
+               sh_flags = SA_SHIRQ;
+       
+       ret = request_irq( dev->irq, dev->driver->irq_handler,
+                          sh_flags, dev->devname, dev );
+       if ( ret < 0 ) {
+               down( &dev->struct_sem );
+               dev->irq_enabled = 0;
+               up( &dev->struct_sem );
+               return ret;
+       }
+
+                               /* After installing handler */
+       dev->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;
+
+       if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+               return -EINVAL;
+
+       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 );
+
+       dev->driver->irq_uninstall(dev);
+
+       free_irq( dev->irq, dev );
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_irq_uninstall);
+
+/**
+ * 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 we haven't irq we fallback for compatibility reasons - this used to be a separate function in drm_dma.h */
+
+       if ( copy_from_user( &ctl, (drm_control_t __user *)arg, sizeof(ctl) ) )
+               return -EFAULT;
+
+       switch ( ctl.func ) {
+       case DRM_INST_HANDLER:
+               if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+                       return 0;
+               if (dev->if_version < DRM_IF_VERSION(1, 2) &&
+                   ctl.irq != dev->irq)
+                       return -EINVAL;
+               return drm_irq_install( dev );
+       case DRM_UNINST_HANDLER:
+               if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
+                       return 0;
+               return drm_irq_uninstall( dev );
+       default:
+               return -EINVAL;
+       }
+}
+
+/**
+ * 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 __user *argp = (void __user *)data;
+       drm_wait_vblank_t vblwait;
+       struct timeval now;
+       int ret = 0;
+       unsigned int flags;
+
+       if (!drm_core_check_feature(dev, DRIVER_IRQ_VBL))
+               return -EINVAL;
+
+       if (!dev->irq)
+               return -EINVAL;
+
+       DRM_COPY_FROM_USER_IOCTL( vblwait, argp, 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_alloc( sizeof( drm_vbl_sig_t ), DRM_MEM_DRIVER ) ) ) {
+                       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 {
+               if (dev->driver->vblank_wait)
+                       ret = dev->driver->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( argp, 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), DRM_MEM_DRIVER );
+
+                       dev->vbl_pending--;
+               }
+       }
+
+       spin_unlock_irqrestore( &dev->vbl_lock, flags );
+}
+EXPORT_SYMBOL(drm_vbl_send_signals);
+
+
diff --git a/drivers/char/drm/drm_lock.c b/drivers/char/drm/drm_lock.c
new file mode 100644 (file)
index 0000000..e511464
--- /dev/null
@@ -0,0 +1,303 @@
+/**
+ * \file drm_lock.h 
+ * IOCTLs for locking
+ * 
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
+ *
+ * Copyright 1999 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"
+
+/** 
+ * Lock ioctl.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_lock structure.
+ * \return zero on success or negative number on failure.
+ *
+ * Add the current task to the lock wait queue, and attempt to take to lock.
+ */
+int drm_lock( 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;
+        DECLARE_WAITQUEUE( entry, current );
+        drm_lock_t lock;
+        int ret = 0;
+
+       ++priv->lock_count;
+
+        if ( copy_from_user( &lock, (drm_lock_t __user *)arg, sizeof(lock) ) )
+               return -EFAULT;
+
+        if ( lock.context == DRM_KERNEL_CONTEXT ) {
+                DRM_ERROR( "Process %d using kernel context %d\n",
+                          current->pid, lock.context );
+                return -EINVAL;
+        }
+
+        DRM_DEBUG( "%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid,
+                  dev->lock.hw_lock->lock, lock.flags );
+
+       if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE))
+               if ( lock.context < 0 )
+                       return -EINVAL;
+
+       add_wait_queue( &dev->lock.lock_queue, &entry );
+       for (;;) {
+               __set_current_state(TASK_INTERRUPTIBLE);
+               if ( !dev->lock.hw_lock ) {
+                       /* Device has been unregistered */
+                       ret = -EINTR;
+                       break;
+               }
+               if ( drm_lock_take( &dev->lock.hw_lock->lock,
+                                    lock.context ) ) {
+                       dev->lock.filp      = filp;
+                       dev->lock.lock_time = jiffies;
+                       atomic_inc( &dev->counts[_DRM_STAT_LOCKS] );
+                       break;  /* Got lock */
+               }
+               
+               /* Contention */
+               schedule();
+               if ( signal_pending( current ) ) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+       }
+       __set_current_state(TASK_RUNNING);
+       remove_wait_queue( &dev->lock.lock_queue, &entry );
+
+       sigemptyset( &dev->sigmask );
+       sigaddset( &dev->sigmask, SIGSTOP );
+       sigaddset( &dev->sigmask, SIGTSTP );
+       sigaddset( &dev->sigmask, SIGTTIN );
+       sigaddset( &dev->sigmask, SIGTTOU );
+       dev->sigdata.context = lock.context;
+       dev->sigdata.lock    = dev->lock.hw_lock;
+       block_all_signals( drm_notifier,
+                          &dev->sigdata, &dev->sigmask );
+       
+       if (dev->driver->dma_ready && (lock.flags & _DRM_LOCK_READY))
+               dev->driver->dma_ready(dev);
+       
+       if ( dev->driver->dma_quiescent && (lock.flags & _DRM_LOCK_QUIESCENT ))
+               return dev->driver->dma_quiescent(dev);
+       
+       /* dev->driver->kernel_context_switch isn't used by any of the x86 
+        *  drivers but is used by the Sparc driver.
+        */
+       
+       if (dev->driver->kernel_context_switch && 
+           dev->last_context != lock.context) {
+         dev->driver->kernel_context_switch(dev, dev->last_context, 
+                                           lock.context);
+       }
+        DRM_DEBUG( "%d %s\n", lock.context, ret ? "interrupted" : "has lock" );
+
+        return ret;
+}
+
+/** 
+ * Unlock ioctl.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument, pointing to a drm_lock structure.
+ * \return zero on success or negative number on failure.
+ *
+ * Transfer and free the lock.
+ */
+int drm_unlock( 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_lock_t lock;
+
+       if ( copy_from_user( &lock, (drm_lock_t __user *)arg, sizeof(lock) ) )
+               return -EFAULT;
+
+       if ( lock.context == DRM_KERNEL_CONTEXT ) {
+               DRM_ERROR( "Process %d using kernel context %d\n",
+                          current->pid, lock.context );
+               return -EINVAL;
+       }
+
+       atomic_inc( &dev->counts[_DRM_STAT_UNLOCKS] );
+
+       /* kernel_context_switch isn't used by any of the x86 drm
+        * modules but is required by the Sparc driver.
+        */
+       if (dev->driver->kernel_context_switch_unlock)
+               dev->driver->kernel_context_switch_unlock(dev, &lock);
+       else {
+               drm_lock_transfer( dev, &dev->lock.hw_lock->lock, 
+                                   DRM_KERNEL_CONTEXT );
+               
+               if ( drm_lock_free( dev, &dev->lock.hw_lock->lock,
+                                    DRM_KERNEL_CONTEXT ) ) {
+                       DRM_ERROR( "\n" );
+               }
+       }
+
+       unblock_all_signals();
+       return 0;
+}
+
+/**
+ * Take the heavyweight lock.
+ *
+ * \param lock lock pointer.
+ * \param context locking context.
+ * \return one if the lock is held, or zero otherwise.
+ *
+ * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
+ */
+int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
+{
+       unsigned int old, new, prev;
+
+       do {
+               old = *lock;
+               if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT;
+               else                      new = context | _DRM_LOCK_HELD;
+               prev = cmpxchg(lock, old, new);
+       } while (prev != old);
+       if (_DRM_LOCKING_CONTEXT(old) == context) {
+               if (old & _DRM_LOCK_HELD) {
+                       if (context != DRM_KERNEL_CONTEXT) {
+                               DRM_ERROR("%d holds heavyweight lock\n",
+                                         context);
+                       }
+                       return 0;
+               }
+       }
+       if (new == (context | _DRM_LOCK_HELD)) {
+                               /* Have lock */
+               return 1;
+       }
+       return 0;
+}
+
+/**
+ * This takes a lock forcibly and hands it to context. Should ONLY be used
+ * inside *_unlock to give lock to kernel before calling *_dma_schedule. 
+ * 
+ * \param dev DRM device.
+ * \param lock lock pointer.
+ * \param context locking context.
+ * \return always one.
+ *
+ * Resets the lock file pointer.
+ * Marks the lock as held by the given context, via the \p cmpxchg instruction.
+ */
+int drm_lock_transfer(drm_device_t *dev,
+                      __volatile__ unsigned int *lock, unsigned int context)
+{
+       unsigned int old, new, prev;
+
+       dev->lock.filp = NULL;
+       do {
+               old  = *lock;
+               new  = context | _DRM_LOCK_HELD;
+               prev = cmpxchg(lock, old, new);
+       } while (prev != old);
+       return 1;
+}
+
+/**
+ * Free lock.
+ * 
+ * \param dev DRM device.
+ * \param lock lock.
+ * \param context context.
+ * 
+ * Resets the lock file pointer.
+ * Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task
+ * waiting on the lock queue.
+ */
+int drm_lock_free(drm_device_t *dev,
+                  __volatile__ unsigned int *lock, unsigned int context)
+{
+       unsigned int old, new, prev;
+
+       dev->lock.filp = NULL;
+       do {
+               old  = *lock;
+               new  = 0;
+               prev = cmpxchg(lock, old, new);
+       } while (prev != old);
+       if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
+               DRM_ERROR("%d freed heavyweight lock held by %d\n",
+                         context,
+                         _DRM_LOCKING_CONTEXT(old));
+               return 1;
+       }
+       wake_up_interruptible(&dev->lock.lock_queue);
+       return 0;
+}
+
+/**
+ * If we get here, it means that the process has called DRM_IOCTL_LOCK
+ * without calling DRM_IOCTL_UNLOCK.
+ *
+ * If the lock is not held, then let the signal proceed as usual.  If the lock
+ * is held, then set the contended flag and keep the signal blocked.
+ *
+ * \param priv pointer to a drm_sigdata structure.
+ * \return one if the signal should be delivered normally, or zero if the
+ * signal should be blocked.
+ */
+int drm_notifier(void *priv)
+{
+       drm_sigdata_t *s = (drm_sigdata_t *)priv;
+       unsigned int  old, new, prev;
+
+
+                               /* Allow signal delivery if lock isn't held */
+       if (!s->lock || !_DRM_LOCK_IS_HELD(s->lock->lock)
+           || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) return 1;
+
+                               /* Otherwise, set flag to force call to
+                                   drmUnlock */
+       do {
+               old  = s->lock->lock;
+               new  = old | _DRM_LOCK_CONT;
+               prev = cmpxchg(&s->lock->lock, old, new);
+       } while (prev != old);
+       return 0;
+}
diff --git a/drivers/char/drm/drm_memory.c b/drivers/char/drm/drm_memory.c
new file mode 100644 (file)
index 0000000..81f109b
--- /dev/null
@@ -0,0 +1,181 @@
+/** 
+ * \file drm_memory.h 
+ * Memory management wrappers for DRM
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/* 
+ * Created: Thu Feb  4 14:00:34 1999 by faith@valinux.com
+ *
+ * Copyright 1999 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 <linux/config.h>
+#include <linux/highmem.h>
+#include "drmP.h"
+
+#ifdef DEBUG_MEMORY
+#include "drm_memory_debug.h"
+#else
+
+/** No-op. */
+void drm_mem_init(void)
+{
+}
+
+/**
+ * Called when "/proc/dri/%dev%/mem" is read.
+ * 
+ * \param buf output buffer.
+ * \param start start of output data.
+ * \param offset requested start offset.
+ * \param len requested number of bytes.
+ * \param eof whether there is no more data to return.
+ * \param data private data.
+ * \return number of written bytes.
+ *
+ * No-op. 
+ */
+int drm_mem_info(char *buf, char **start, off_t offset,
+                 int len, int *eof, void *data)
+{
+       return 0;
+}
+
+/** Wrapper around kmalloc() */
+void *drm_calloc(size_t nmemb, size_t size, int area)
+{
+       void *addr;
+
+       addr = kmalloc(size * nmemb, GFP_KERNEL);
+       if (addr != NULL)
+               memset((void *)addr, 0, size * nmemb);
+
+       return addr;
+}
+EXPORT_SYMBOL(drm_calloc);
+
+/** Wrapper around kmalloc() and kfree() */
+void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area)
+{
+       void *pt;
+
+       if (!(pt = kmalloc(size, GFP_KERNEL))) return NULL;
+       if (oldpt && oldsize) {
+               memcpy(pt, oldpt, oldsize);
+               kfree(oldpt);
+       }
+       return pt;
+}
+
+/**
+ * Allocate pages.
+ *
+ * \param order size order.
+ * \param area memory area. (Not used.)
+ * \return page address on success, or zero on failure.
+ *
+ * Allocate and reserve free pages.
+ */
+unsigned long drm_alloc_pages(int order, int area)
+{
+       unsigned long address;
+       unsigned long bytes       = PAGE_SIZE << order;
+       unsigned long addr;
+       unsigned int  sz;
+
+       address = __get_free_pages(GFP_KERNEL, order);
+       if (!address) 
+               return 0;
+
+                               /* Zero */
+       memset((void *)address, 0, bytes);
+
+                               /* Reserve */
+       for (addr = address, sz = bytes;
+            sz > 0;
+            addr += PAGE_SIZE, sz -= PAGE_SIZE) {
+               SetPageReserved(virt_to_page(addr));
+       }
+
+       return address;
+}
+
+/**
+ * Free pages.
+ * 
+ * \param address address of the pages to free.
+ * \param order size order.
+ * \param area memory area. (Not used.)
+ *
+ * Unreserve and free pages allocated by alloc_pages().
+ */
+void drm_free_pages(unsigned long address, int order, int area)
+{
+       unsigned long bytes = PAGE_SIZE << order;
+       unsigned long addr;
+       unsigned int  sz;
+
+       if (!address) 
+               return;
+
+       /* Unreserve */
+       for (addr = address, sz = bytes;
+            sz > 0;
+            addr += PAGE_SIZE, sz -= PAGE_SIZE) {
+               ClearPageReserved(virt_to_page(addr));
+       }
+
+       free_pages(address, order);
+}
+
+
+#if __OS_HAS_AGP
+/** Wrapper around agp_allocate_memory() */
+DRM_AGP_MEM *drm_alloc_agp(int pages, u32 type)
+{
+       return drm_agp_allocate_memory(pages, type);
+}
+
+/** Wrapper around agp_free_memory() */
+int drm_free_agp(DRM_AGP_MEM *handle, int pages)
+{
+       return drm_agp_free_memory(handle) ? 0 : -EINVAL;
+}
+
+/** Wrapper around agp_bind_memory() */
+int drm_bind_agp(DRM_AGP_MEM *handle, unsigned int start)
+{
+       return drm_agp_bind_memory(handle, start);
+}
+
+/** Wrapper around agp_unbind_memory() */
+int drm_unbind_agp(DRM_AGP_MEM *handle)
+{
+       return drm_agp_unbind_memory(handle);
+}
+#endif /* agp */
+#endif /* debug_memory */
diff --git a/drivers/char/drm/drm_pci.c b/drivers/char/drm/drm_pci.c
new file mode 100644 (file)
index 0000000..192e876
--- /dev/null
@@ -0,0 +1,140 @@
+/* drm_pci.h -- PCI DMA memory management wrappers for DRM -*- linux-c -*- */
+/**
+ * \file drm_pci.c
+ * \brief Functions and ioctls to manage PCI memory
+ *
+ * \warning These interfaces aren't stable yet.
+ *
+ * \todo Implement the remaining ioctl's for the PCI pools.
+ * \todo The wrappers here are so thin that they would be better off inlined..
+ *
+ * \author Jose Fonseca <jrfonseca@tungstengraphics.com>
+ * \author Leif Delgass <ldelgass@retinalburn.net>
+ */
+
+/*
+ * Copyright 2003 Jos�Fonseca.
+ * Copyright 2003 Leif Delgass.
+ * 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 THE
+ * AUTHORS 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 <linux/pci.h>
+#include "drmP.h"
+
+/**********************************************************************/
+/** \name PCI memory */
+/*@{*/
+
+/**
+ * \brief Allocate a PCI consistent memory block, for DMA.
+ */
+void *drm_pci_alloc(drm_device_t * dev, size_t size, size_t align,
+                   dma_addr_t maxaddr, dma_addr_t * busaddr)
+{
+       void *address;
+#if DRM_DEBUG_MEMORY
+       int area = DRM_MEM_DMA;
+
+       spin_lock(&drm_mem_lock);
+       if ((drm_ram_used >> PAGE_SHIFT)
+           > (DRM_RAM_PERCENT * drm_ram_available) / 100) {
+               spin_unlock(&drm_mem_lock);
+               return 0;
+       }
+       spin_unlock(&drm_mem_lock);
+#endif
+
+       /* pci_alloc_consistent only guarantees alignment to the smallest
+        * PAGE_SIZE order which is greater than or equal to the requested size.
+        * Return NULL here for now to make sure nobody tries for larger alignment
+        */
+       if (align > size)
+               return NULL;
+
+       if (pci_set_dma_mask(dev->pdev, maxaddr) != 0) {
+               DRM_ERROR("Setting pci dma mask failed\n");
+               return NULL;
+       }
+
+       address = pci_alloc_consistent(dev->pdev, size, busaddr);
+
+#if DRM_DEBUG_MEMORY
+       if (address == NULL) {
+               spin_lock(&drm_mem_lock);
+               ++drm_mem_stats[area].fail_count;
+               spin_unlock(&drm_mem_lock);
+               return NULL;
+       }
+
+       spin_lock(&drm_mem_lock);
+       ++drm_mem_stats[area].succeed_count;
+       drm_mem_stats[area].bytes_allocated += size;
+       drm_ram_used += size;
+       spin_unlock(&drm_mem_lock);
+#else
+       if (address == NULL)
+               return NULL;
+#endif
+
+       memset(address, 0, size);
+
+       return address;
+}
+EXPORT_SYMBOL(drm_pci_alloc);
+
+/**
+ * \brief Free a PCI consistent memory block.
+ */
+void
+drm_pci_free(drm_device_t * dev, size_t size, void *vaddr, dma_addr_t busaddr)
+{
+#if DRM_DEBUG_MEMORY
+       int area = DRM_MEM_DMA;
+       int alloc_count;
+       int free_count;
+#endif
+
+       if (!vaddr) {
+#if DRM_DEBUG_MEMORY
+               DRM_MEM_ERROR(area, "Attempt to free address 0\n");
+#endif
+       } else {
+               pci_free_consistent(dev->pdev, size, vaddr, busaddr);
+       }
+
+#if DRM_DEBUG_MEMORY
+       spin_lock(&drm_mem_lock);
+       free_count = ++drm_mem_stats[area].free_count;
+       alloc_count = drm_mem_stats[area].succeed_count;
+       drm_mem_stats[area].bytes_freed += size;
+       drm_ram_used -= size;
+       spin_unlock(&drm_mem_lock);
+       if (free_count > alloc_count) {
+               DRM_MEM_ERROR(area,
+                             "Excess frees: %d frees, %d allocs\n",
+                             free_count, alloc_count);
+       }
+#endif
+
+}
+EXPORT_SYMBOL(drm_pci_free);
+
+/*@}*/
diff --git a/drivers/char/drm/drm_proc.c b/drivers/char/drm/drm_proc.c
new file mode 100644 (file)
index 0000000..6906f70
--- /dev/null
@@ -0,0 +1,539 @@
+/**
+ * \file drm_proc.h 
+ * /proc support for DRM
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ *
+ * \par Acknowledgements:
+ *    Matthew J Sottek <matthew.j.sottek@intel.com> sent in a patch to fix
+ *    the problem with the proc files not outputting all their information.
+ */
+
+/*
+ * Created: Mon Jan 11 09:48:47 1999 by faith@valinux.com
+ *
+ * Copyright 1999 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"
+
+static int        drm_name_info(char *buf, char **start, off_t offset,
+                                 int request, int *eof, void *data);
+static int        drm_vm_info(char *buf, char **start, off_t offset,
+                               int request, int *eof, void *data);
+static int        drm_clients_info(char *buf, char **start, off_t offset,
+                                    int request, int *eof, void *data);
+static int        drm_queues_info(char *buf, char **start, off_t offset,
+                                   int request, int *eof, void *data);
+static int        drm_bufs_info(char *buf, char **start, off_t offset,
+                                 int request, int *eof, void *data);
+#if DRM_DEBUG_CODE
+static int        drm_vma_info(char *buf, char **start, off_t offset,
+                                int request, int *eof, void *data);
+#endif
+
+/**
+ * Proc file list.
+ */
+struct drm_proc_list {
+       const char *name;       /**< file name */
+       int        (*f)(char *, char **, off_t, int, int *, void *);    /**< proc callback*/
+} drm_proc_list[] = {
+       { "name",    drm_name_info    },
+       { "mem",     drm_mem_info     },
+       { "vm",      drm_vm_info      },
+       { "clients", drm_clients_info },
+       { "queues",  drm_queues_info  },
+       { "bufs",    drm_bufs_info    },
+#if DRM_DEBUG_CODE
+       { "vma",     drm_vma_info     },
+#endif
+};
+#define DRM_PROC_ENTRIES (sizeof(drm_proc_list)/sizeof(drm_proc_list[0]))
+
+/**
+ * Initialize the DRI proc filesystem for a device.
+ *
+ * \param dev DRM device.
+ * \param minor device minor number.
+ * \param root DRI proc dir entry.
+ * \param dev_root resulting DRI device proc dir entry.
+ * \return root entry pointer on success, or NULL on failure.
+ * 
+ * Create the DRI proc root entry "/proc/dri", the device proc root entry
+ * "/proc/dri/%minor%/", and each entry in proc_list as
+ * "/proc/dri/%minor%/%name%".
+ */
+int drm_proc_init(drm_device_t *dev, int minor,
+                   struct proc_dir_entry *root,
+                   struct proc_dir_entry **dev_root)
+{
+       struct proc_dir_entry *ent;
+       int                   i, j;
+       char                  name[64];
+
+       sprintf(name, "%d", minor);
+       *dev_root = create_proc_entry(name, S_IFDIR, root);
+       if (!*dev_root) {
+               DRM_ERROR("Cannot create /proc/dri/%s\n", name);
+               return -1;
+       }
+
+       for (i = 0; i < DRM_PROC_ENTRIES; i++) {
+               ent = create_proc_entry(drm_proc_list[i].name,
+                                       S_IFREG|S_IRUGO, *dev_root);
+               if (!ent) {
+                       DRM_ERROR("Cannot create /proc/dri/%s/%s\n",
+                                 name, drm_proc_list[i].name);
+                       for (j = 0; j < i; j++)
+                               remove_proc_entry(drm_proc_list[i].name,
+                                                 *dev_root);
+                       remove_proc_entry(name, root);
+                       return -1;
+               }
+               ent->read_proc = drm_proc_list[i].f;
+               ent->data      = dev;
+       }
+
+       return 0;
+}
+
+
+/**
+ * Cleanup the proc filesystem resources.
+ *
+ * \param minor device minor number.
+ * \param root DRI proc dir entry.
+ * \param dev_root DRI device proc dir entry.
+ * \return always zero.
+ *
+ * Remove all proc entries created by proc_init().
+ */
+int drm_proc_cleanup(int minor, struct proc_dir_entry *root,
+                     struct proc_dir_entry *dev_root)
+{
+       int  i;
+       char name[64];
+
+       if (!root || !dev_root) return 0;
+
+       for (i = 0; i < DRM_PROC_ENTRIES; i++)
+               remove_proc_entry(drm_proc_list[i].name, dev_root);
+       sprintf(name, "%d", minor);
+       remove_proc_entry(name, root);
+
+       return 0;
+}
+
+/**
+ * Called when "/proc/dri/.../name" is read.
+ * 
+ * \param buf output buffer.
+ * \param start start of output data.
+ * \param offset requested start offset.
+ * \param request requested number of bytes.
+ * \param eof whether there is no more data to return.
+ * \param data private data.
+ * \return number of written bytes.
+ * 
+ * Prints the device name together with the bus id if available.
+ */
+static int drm_name_info(char *buf, char **start, off_t offset, int request,
+                         int *eof, void *data)
+{
+       drm_device_t *dev = (drm_device_t *)data;
+       int          len  = 0;
+
+       if (offset > DRM_PROC_LIMIT) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = &buf[offset];
+       *eof   = 0;
+
+       if (dev->unique) {
+               DRM_PROC_PRINT("%s 0x%lx %s\n",
+                              dev->driver->pci_driver.name, (long)old_encode_dev(dev->device), dev->unique);
+       } else {
+               DRM_PROC_PRINT("%s 0x%lx\n", dev->driver->pci_driver.name, (long)old_encode_dev(dev->device));
+       }
+
+       if (len > request + offset) return request;
+       *eof = 1;
+       return len - offset;
+}
+
+/**
+ * Called when "/proc/dri/.../vm" is read.
+ * 
+ * \param buf output buffer.
+ * \param start start of output data.
+ * \param offset requested start offset.
+ * \param request requested number of bytes.
+ * \param eof whether there is no more data to return.
+ * \param data private data.
+ * \return number of written bytes.
+ * 
+ * Prints information about all mappings in drm_device::maplist.
+ */
+static int drm__vm_info(char *buf, char **start, off_t offset, int request,
+                        int *eof, void *data)
+{
+       drm_device_t *dev = (drm_device_t *)data;
+       int          len  = 0;
+       drm_map_t    *map;
+       drm_map_list_t *r_list;
+       struct list_head *list;
+
+                               /* Hardcoded from _DRM_FRAME_BUFFER,
+                                   _DRM_REGISTERS, _DRM_SHM, _DRM_AGP, and
+                                   _DRM_SCATTER_GATHER. */
+       const char   *types[] = { "FB", "REG", "SHM", "AGP", "SG" };
+       const char   *type;
+       int          i;
+
+       if (offset > DRM_PROC_LIMIT) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = &buf[offset];
+       *eof   = 0;
+
+       DRM_PROC_PRINT("slot     offset       size type flags    "
+                      "address mtrr\n\n");
+       i = 0;
+       if (dev->maplist != NULL) list_for_each(list, &dev->maplist->head) {
+               r_list = list_entry(list, drm_map_list_t, head);
+               map = r_list->map;
+               if(!map) continue;
+               if (map->type < 0 || map->type > 4) type = "??";
+               else                                type = types[map->type];
+               DRM_PROC_PRINT("%4d 0x%08lx 0x%08lx %4.4s  0x%02x 0x%08lx ",
+                              i,
+                              map->offset,
+                              map->size,
+                              type,
+                              map->flags,
+                              (unsigned long)map->handle);
+               if (map->mtrr < 0) {
+                       DRM_PROC_PRINT("none\n");
+               } else {
+                       DRM_PROC_PRINT("%4d\n", map->mtrr);
+               }
+               i++;
+       }
+
+       if (len > request + offset) return request;
+       *eof = 1;
+       return len - offset;
+}
+
+/**
+ * Simply calls _vm_info() while holding the drm_device::struct_sem lock.
+ */
+static int drm_vm_info(char *buf, char **start, off_t offset, int request,
+                       int *eof, void *data)
+{
+       drm_device_t *dev = (drm_device_t *)data;
+       int          ret;
+
+       down(&dev->struct_sem);
+       ret = drm__vm_info(buf, start, offset, request, eof, data);
+       up(&dev->struct_sem);
+       return ret;
+}
+
+/**
+ * Called when "/proc/dri/.../queues" is read.
+ * 
+ * \param buf output buffer.
+ * \param start start of output data.
+ * \param offset requested start offset.
+ * \param request requested number of bytes.
+ * \param eof whether there is no more data to return.
+ * \param data private data.
+ * \return number of written bytes.
+ */
+static int drm__queues_info(char *buf, char **start, off_t offset,
+                            int request, int *eof, void *data)
+{
+       drm_device_t *dev = (drm_device_t *)data;
+       int          len  = 0;
+       int          i;
+       drm_queue_t  *q;
+
+       if (offset > DRM_PROC_LIMIT) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = &buf[offset];
+       *eof   = 0;
+
+       DRM_PROC_PRINT("  ctx/flags   use   fin"
+                      "   blk/rw/rwf  wait    flushed     queued"
+                      "      locks\n\n");
+       for (i = 0; i < dev->queue_count; i++) {
+               q = dev->queuelist[i];
+               atomic_inc(&q->use_count);
+               DRM_PROC_PRINT_RET(atomic_dec(&q->use_count),
+                                  "%5d/0x%03x %5d %5d"
+                                  " %5d/%c%c/%c%c%c %5Zd\n",
+                                  i,
+                                  q->flags,
+                                  atomic_read(&q->use_count),
+                                  atomic_read(&q->finalization),
+                                  atomic_read(&q->block_count),
+                                  atomic_read(&q->block_read) ? 'r' : '-',
+                                  atomic_read(&q->block_write) ? 'w' : '-',
+                                  waitqueue_active(&q->read_queue) ? 'r':'-',
+                                  waitqueue_active(&q->write_queue) ? 'w':'-',
+                                  waitqueue_active(&q->flush_queue) ? 'f':'-',
+                                  DRM_BUFCOUNT(&q->waitlist));
+               atomic_dec(&q->use_count);
+       }
+
+       if (len > request + offset) return request;
+       *eof = 1;
+       return len - offset;
+}
+
+/**
+ * Simply calls _queues_info() while holding the drm_device::struct_sem lock.
+ */
+static int drm_queues_info(char *buf, char **start, off_t offset, int request,
+                           int *eof, void *data)
+{
+       drm_device_t *dev = (drm_device_t *)data;
+       int          ret;
+
+       down(&dev->struct_sem);
+       ret = drm__queues_info(buf, start, offset, request, eof, data);
+       up(&dev->struct_sem);
+       return ret;
+}
+
+/**
+ * Called when "/proc/dri/.../bufs" is read.
+ * 
+ * \param buf output buffer.
+ * \param start start of output data.
+ * \param offset requested start offset.
+ * \param request requested number of bytes.
+ * \param eof whether there is no more data to return.
+ * \param data private data.
+ * \return number of written bytes.
+ */
+static int drm__bufs_info(char *buf, char **start, off_t offset, int request,
+                          int *eof, void *data)
+{
+       drm_device_t     *dev = (drm_device_t *)data;
+       int              len  = 0;
+       drm_device_dma_t *dma = dev->dma;
+       int              i;
+
+       if (!dma || offset > DRM_PROC_LIMIT) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = &buf[offset];
+       *eof   = 0;
+
+       DRM_PROC_PRINT(" o     size count  free  segs pages    kB\n\n");
+       for (i = 0; i <= DRM_MAX_ORDER; i++) {
+               if (dma->bufs[i].buf_count)
+                       DRM_PROC_PRINT("%2d %8d %5d %5d %5d %5d %5ld\n",
+                                      i,
+                                      dma->bufs[i].buf_size,
+                                      dma->bufs[i].buf_count,
+                                      atomic_read(&dma->bufs[i]
+                                                  .freelist.count),
+                                      dma->bufs[i].seg_count,
+                                      dma->bufs[i].seg_count
+                                      *(1 << dma->bufs[i].page_order),
+                                      (dma->bufs[i].seg_count
+                                       * (1 << dma->bufs[i].page_order))
+                                      * PAGE_SIZE / 1024);
+       }
+       DRM_PROC_PRINT("\n");
+       for (i = 0; i < dma->buf_count; i++) {
+               if (i && !(i%32)) DRM_PROC_PRINT("\n");
+               DRM_PROC_PRINT(" %d", dma->buflist[i]->list);
+       }
+       DRM_PROC_PRINT("\n");
+
+       if (len > request + offset) return request;
+       *eof = 1;
+       return len - offset;
+}
+
+/**
+ * Simply calls _bufs_info() while holding the drm_device::struct_sem lock.
+ */
+static int drm_bufs_info(char *buf, char **start, off_t offset, int request,
+                         int *eof, void *data)
+{
+       drm_device_t *dev = (drm_device_t *)data;
+       int          ret;
+
+       down(&dev->struct_sem);
+       ret = drm__bufs_info(buf, start, offset, request, eof, data);
+       up(&dev->struct_sem);
+       return ret;
+}
+
+/**
+ * Called when "/proc/dri/.../clients" is read.
+ * 
+ * \param buf output buffer.
+ * \param start start of output data.
+ * \param offset requested start offset.
+ * \param request requested number of bytes.
+ * \param eof whether there is no more data to return.
+ * \param data private data.
+ * \return number of written bytes.
+ */
+static int drm__clients_info(char *buf, char **start, off_t offset,
+                             int request, int *eof, void *data)
+{
+       drm_device_t *dev = (drm_device_t *)data;
+       int          len  = 0;
+       drm_file_t   *priv;
+
+       if (offset > DRM_PROC_LIMIT) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = &buf[offset];
+       *eof   = 0;
+
+       DRM_PROC_PRINT("a dev   pid    uid      magic     ioctls\n\n");
+       for (priv = dev->file_first; priv; priv = priv->next) {
+               DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n",
+                              priv->authenticated ? 'y' : 'n',
+                              priv->minor,
+                              priv->pid,
+                              priv->uid,
+                              priv->magic,
+                              priv->ioctl_count);
+       }
+
+       if (len > request + offset) return request;
+       *eof = 1;
+       return len - offset;
+}
+
+/**
+ * Simply calls _clients_info() while holding the drm_device::struct_sem lock.
+ */
+static int drm_clients_info(char *buf, char **start, off_t offset,
+                            int request, int *eof, void *data)
+{
+       drm_device_t *dev = (drm_device_t *)data;
+       int          ret;
+
+       down(&dev->struct_sem);
+       ret = drm__clients_info(buf, start, offset, request, eof, data);
+       up(&dev->struct_sem);
+       return ret;
+}
+
+#if DRM_DEBUG_CODE
+
+static int drm__vma_info(char *buf, char **start, off_t offset, int request,
+                         int *eof, void *data)
+{
+       drm_device_t          *dev = (drm_device_t *)data;
+       int                   len  = 0;
+       drm_vma_entry_t       *pt;
+       struct vm_area_struct *vma;
+#if defined(__i386__)
+       unsigned int          pgprot;
+#endif
+
+       if (offset > DRM_PROC_LIMIT) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = &buf[offset];
+       *eof   = 0;
+
+       DRM_PROC_PRINT("vma use count: %d, high_memory = %p, 0x%08lx\n",
+                      atomic_read(&dev->vma_count),
+                      high_memory, virt_to_phys(high_memory));
+       for (pt = dev->vmalist; pt; pt = pt->next) {
+               if (!(vma = pt->vma)) continue;
+               DRM_PROC_PRINT("\n%5d 0x%08lx-0x%08lx %c%c%c%c%c%c 0x%08lx",
+                              pt->pid,
+                              vma->vm_start,
+                              vma->vm_end,
+                              vma->vm_flags & VM_READ     ? 'r' : '-',
+                              vma->vm_flags & VM_WRITE    ? 'w' : '-',
+                              vma->vm_flags & VM_EXEC     ? 'x' : '-',
+                              vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
+                              vma->vm_flags & VM_LOCKED   ? 'l' : '-',
+                              vma->vm_flags & VM_IO       ? 'i' : '-',
+                              VM_OFFSET(vma));
+
+#if defined(__i386__)
+               pgprot = pgprot_val(vma->vm_page_prot);
+               DRM_PROC_PRINT(" %c%c%c%c%c%c%c%c%c",
+                              pgprot & _PAGE_PRESENT  ? 'p' : '-',
+                              pgprot & _PAGE_RW       ? 'w' : 'r',
+                              pgprot & _PAGE_USER     ? 'u' : 's',
+                              pgprot & _PAGE_PWT      ? 't' : 'b',
+                              pgprot & _PAGE_PCD      ? 'u' : 'c',
+                              pgprot & _PAGE_ACCESSED ? 'a' : '-',
+                              pgprot & _PAGE_DIRTY    ? 'd' : '-',
+                              pgprot & _PAGE_PSE      ? 'm' : 'k',
+                              pgprot & _PAGE_GLOBAL   ? 'g' : 'l' );
+#endif
+               DRM_PROC_PRINT("\n");
+       }
+
+       if (len > request + offset) return request;
+       *eof = 1;
+       return len - offset;
+}
+
+static int drm_vma_info(char *buf, char **start, off_t offset, int request,
+                        int *eof, void *data)
+{
+       drm_device_t *dev = (drm_device_t *)data;
+       int          ret;
+
+       down(&dev->struct_sem);
+       ret = drm__vma_info(buf, start, offset, request, eof, data);
+       up(&dev->struct_sem);
+       return ret;
+}
+#endif
+
+
diff --git a/drivers/char/drm/drm_scatter.c b/drivers/char/drm/drm_scatter.c
new file mode 100644 (file)
index 0000000..5611bad
--- /dev/null
@@ -0,0 +1,231 @@
+/**
+ * \file drm_scatter.h 
+ * IOCTLs to manage scatter/gather memory
+ *
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com
+ *
+ * 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
+ * PRECISION INSIGHT 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 <linux/config.h>
+#include <linux/vmalloc.h>
+#include "drmP.h"
+
+#define DEBUG_SCATTER 0
+
+void drm_sg_cleanup( drm_sg_mem_t *entry )
+{
+       struct page *page;
+       int i;
+
+       for ( i = 0 ; i < entry->pages ; i++ ) {
+               page = entry->pagelist[i];
+               if ( page )
+                       ClearPageReserved( page );
+       }
+
+       vfree( entry->virtual );
+
+       drm_free( entry->busaddr,
+                  entry->pages * sizeof(*entry->busaddr),
+                  DRM_MEM_PAGES );
+       drm_free( entry->pagelist,
+                  entry->pages * sizeof(*entry->pagelist),
+                  DRM_MEM_PAGES );
+       drm_free( entry,
+                  sizeof(*entry),
+                  DRM_MEM_SGLISTS );
+}
+
+int drm_sg_alloc( 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_scatter_gather_t __user *argp = (void __user *)arg;
+       drm_scatter_gather_t request;
+       drm_sg_mem_t *entry;
+       unsigned long pages, i, j;
+
+       DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+       if (!drm_core_check_feature(dev, DRIVER_SG))
+               return -EINVAL;
+
+       if ( dev->sg )
+               return -EINVAL;
+
+       if ( copy_from_user( &request, argp, sizeof(request) ) )
+               return -EFAULT;
+
+       entry = drm_alloc( sizeof(*entry), DRM_MEM_SGLISTS );
+       if ( !entry )
+               return -ENOMEM;
+
+       memset( entry, 0, sizeof(*entry) );
+
+       pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE;
+       DRM_DEBUG( "sg size=%ld pages=%ld\n", request.size, pages );
+
+       entry->pages = pages;
+       entry->pagelist = drm_alloc( pages * sizeof(*entry->pagelist),
+                                    DRM_MEM_PAGES );
+       if ( !entry->pagelist ) {
+               drm_free( entry, sizeof(*entry), DRM_MEM_SGLISTS );
+               return -ENOMEM;
+       }
+
+       memset(entry->pagelist, 0, pages * sizeof(*entry->pagelist));
+
+       entry->busaddr = drm_alloc( pages * sizeof(*entry->busaddr),
+                                    DRM_MEM_PAGES );
+       if ( !entry->busaddr ) {
+               drm_free( entry->pagelist,
+                          entry->pages * sizeof(*entry->pagelist),
+                          DRM_MEM_PAGES );
+               drm_free( entry,
+                          sizeof(*entry),
+                          DRM_MEM_SGLISTS );
+               return -ENOMEM;
+       }
+       memset( (void *)entry->busaddr, 0, pages * sizeof(*entry->busaddr) );
+
+       entry->virtual = vmalloc_32( pages << PAGE_SHIFT );
+       if ( !entry->virtual ) {
+               drm_free( entry->busaddr,
+                          entry->pages * sizeof(*entry->busaddr),
+                          DRM_MEM_PAGES );
+               drm_free( entry->pagelist,
+                          entry->pages * sizeof(*entry->pagelist),
+                          DRM_MEM_PAGES );
+               drm_free( entry,
+                          sizeof(*entry),
+                          DRM_MEM_SGLISTS );
+               return -ENOMEM;
+       }
+
+       /* This also forces the mapping of COW pages, so our page list
+        * will be valid.  Please don't remove it...
+        */
+       memset( entry->virtual, 0, pages << PAGE_SHIFT );
+
+       entry->handle = (unsigned long)entry->virtual;
+
+       DRM_DEBUG( "sg alloc handle  = %08lx\n", entry->handle );
+       DRM_DEBUG( "sg alloc virtual = %p\n", entry->virtual );
+
+       for ( i = entry->handle, j = 0 ; j < pages ; i += PAGE_SIZE, j++ ) {
+               entry->pagelist[j] = vmalloc_to_page((void *)i);
+               if (!entry->pagelist[j])
+                       goto failed;
+               SetPageReserved(entry->pagelist[j]);
+       }
+
+       request.handle = entry->handle;
+
+       if ( copy_to_user( argp, &request, sizeof(request) ) ) {
+               drm_sg_cleanup( entry );
+               return -EFAULT;
+       }
+
+       dev->sg = entry;
+
+#if DEBUG_SCATTER
+       /* Verify that each page points to its virtual address, and vice
+        * versa.
+        */
+       {
+       int error = 0;
+
+       for ( i = 0 ; i < pages ; i++ ) {
+               unsigned long *tmp;
+
+               tmp = page_address( entry->pagelist[i] );
+               for ( j = 0 ;
+                     j < PAGE_SIZE / sizeof(unsigned long) ;
+                     j++, tmp++ ) {
+                       *tmp = 0xcafebabe;
+               }
+               tmp = (unsigned long *)((u8 *)entry->virtual +
+                                       (PAGE_SIZE * i));
+               for( j = 0 ;
+                    j < PAGE_SIZE / sizeof(unsigned long) ;
+                    j++, tmp++ ) {
+                       if ( *tmp != 0xcafebabe && error == 0 ) {
+                               error = 1;
+                               DRM_ERROR( "Scatter allocation error, "
+                                          "pagelist does not match "
+                                          "virtual mapping\n" );
+                       }
+               }
+               tmp = page_address( entry->pagelist[i] );
+               for(j = 0 ;
+                   j < PAGE_SIZE / sizeof(unsigned long) ;
+                   j++, tmp++) {
+                       *tmp = 0;
+               }
+       }
+       if (error == 0)
+               DRM_ERROR( "Scatter allocation matches pagelist\n" );
+       }
+#endif
+
+       return 0;
+
+ failed:
+       drm_sg_cleanup( entry );
+       return -ENOMEM;
+}
+
+int drm_sg_free( 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_scatter_gather_t request;
+       drm_sg_mem_t *entry;
+
+       if (!drm_core_check_feature(dev, DRIVER_SG))
+               return -EINVAL;
+
+       if ( copy_from_user( &request,
+                            (drm_scatter_gather_t __user *)arg,
+                            sizeof(request) ) )
+               return -EFAULT;
+
+       entry = dev->sg;
+       dev->sg = NULL;
+
+       if ( !entry || entry->handle != request.handle )
+               return -EINVAL;
+
+       DRM_DEBUG( "sg free virtual  = %p\n", entry->virtual );
+
+       drm_sg_cleanup( entry );
+
+       return 0;
+}
diff --git a/drivers/char/drm/drm_stub.c b/drivers/char/drm/drm_stub.c
new file mode 100644 (file)
index 0000000..1ff4213
--- /dev/null
@@ -0,0 +1,259 @@
+/**
+ * \file drm_stub.h
+ * Stub support
+ *
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ */
+
+/*
+ * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
+ *
+ * Copyright 2001 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
+ * PRECISION INSIGHT 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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include "drmP.h"
+#include "drm_core.h"
+
+unsigned int drm_cards_limit = 16;     /* Enough for one machine */
+unsigned int drm_debug = 0;            /* 1 to enable debug output */
+EXPORT_SYMBOL(drm_debug);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM_DESC(drm_cards_limit, "Maximum number of graphics cards");
+MODULE_PARM_DESC(drm_debug, "Enable debug output");
+
+module_param_named(cards_limit, drm_cards_limit, int, 0444);
+module_param_named(debug, drm_debug, int, 0666);
+
+drm_minor_t *drm_minors;
+struct drm_sysfs_class *drm_class;
+struct proc_dir_entry *drm_proc_root;
+
+static int drm_fill_in_dev(drm_device_t *dev, struct pci_dev *pdev, const struct pci_device_id *ent, struct drm_driver *driver)
+{
+       int retcode;
+
+       spin_lock_init(&dev->count_lock);
+       init_timer( &dev->timer );
+       sema_init( &dev->struct_sem, 1 );
+       sema_init( &dev->ctxlist_sem, 1 );
+
+       dev->pdev   = pdev;
+
+#ifdef __alpha__
+       dev->hose   = pdev->sysdata;
+       dev->pci_domain = dev->hose->bus->number;
+#else
+       dev->pci_domain = 0;
+#endif
+       dev->pci_bus = pdev->bus->number;
+       dev->pci_slot = PCI_SLOT(pdev->devfn);
+       dev->pci_func = PCI_FUNC(pdev->devfn);
+       dev->irq = pdev->irq;
+
+       /* the DRM has 6 basic counters */
+       dev->counters = 6;
+       dev->types[0]  = _DRM_STAT_LOCK;
+       dev->types[1]  = _DRM_STAT_OPENS;
+       dev->types[2]  = _DRM_STAT_CLOSES;
+       dev->types[3]  = _DRM_STAT_IOCTLS;
+       dev->types[4]  = _DRM_STAT_LOCKS;
+       dev->types[5]  = _DRM_STAT_UNLOCKS;
+
+       dev->driver = driver;
+       
+       if (dev->driver->preinit)
+               if ((retcode = dev->driver->preinit(dev, ent->driver_data)))
+                       goto error_out_unreg;
+
+       if (drm_core_has_AGP(dev)) {
+               dev->agp = drm_agp_init();
+               if (drm_core_check_feature(dev, DRIVER_REQUIRE_AGP) && (dev->agp == NULL)) {
+                       DRM_ERROR( "Cannot initialize the agpgart module.\n" );
+                       retcode = -EINVAL;
+                       goto error_out_unreg;
+               }
+               if (drm_core_has_MTRR(dev)) {
+                       if (dev->agp)
+                               dev->agp->agp_mtrr = mtrr_add( dev->agp->agp_info.aper_base,
+                                                              dev->agp->agp_info.aper_size*1024*1024,
+                                                              MTRR_TYPE_WRCOMB,
+                                                              1 );
+               }
+       }
+
+       retcode = drm_ctxbitmap_init( dev );
+       if( retcode ) {
+               DRM_ERROR( "Cannot allocate memory for context bitmap.\n" );
+               goto error_out_unreg;
+       }
+
+       dev->device = MKDEV(DRM_MAJOR, dev->minor );
+
+       /* postinit is a required function to display the signon banner */
+       if ((retcode = dev->driver->postinit(dev, ent->driver_data)))
+               goto error_out_unreg;
+
+       return 0;
+       
+error_out_unreg:
+       drm_takedown(dev);
+       return retcode;
+}
+
+/**
+ * File \c open operation.
+ *
+ * \param inode device inode.
+ * \param filp file pointer.
+ *
+ * Puts the dev->fops corresponding to the device minor number into
+ * \p filp, call the \c open method, and restore the file operations.
+ */
+int drm_stub_open(struct inode *inode, struct file *filp)
+{
+       drm_device_t *dev = NULL;
+       int minor = iminor(inode);
+       int err = -ENODEV;
+       struct file_operations *old_fops;
+       
+       DRM_DEBUG("\n");
+
+       if (!((minor >= 0) && (minor < drm_cards_limit)))
+               return -ENODEV;
+
+       dev = drm_minors[minor].dev;
+       if (!dev)
+               return -ENODEV;
+
+       old_fops = filp->f_op;
+       filp->f_op = fops_get(&dev->driver->fops);
+       if (filp->f_op->open && (err = filp->f_op->open(inode, filp))) {
+               fops_put(filp->f_op);
+               filp->f_op = fops_get(old_fops);
+       }
+       fops_put(old_fops);
+
+       return err;
+}
+
+/**
+ * Get a device minor number.
+ *
+ * \param pdev PCI device structure
+ * \param ent entry from the PCI ID table with device type flags
+ * \return zero on success or a negative number on failure.
+ *
+ * Attempt to gets inter module "drm" information. If we are first
+ * then register the character device and inter module information.
+ * Try and register, if we fail to register, backout previous work.
+ */
+int drm_probe(struct pci_dev *pdev, const struct pci_device_id *ent, struct drm_driver *driver)
+{
+       struct class_device *dev_class;
+       drm_device_t *dev;
+       int ret;
+       int minor;
+       drm_minor_t *minors = &drm_minors[0];
+
+       DRM_DEBUG("\n");
+
+       for (minor = 0; minor < drm_cards_limit; minor++, minors++) {
+               if (minors->type == DRM_MINOR_FREE) {
+
+                       DRM_DEBUG("assigning minor %d\n", minor);
+                       dev = drm_calloc(1, sizeof(*dev), DRM_MEM_STUB);
+                       if (!dev)
+                               return -ENOMEM;
+
+                       *minors = (drm_minor_t){.dev = dev, .type=DRM_MINOR_PRIMARY};
+                       dev->minor = minor;
+
+                       pci_enable_device(pdev);
+
+                       if ((ret=drm_fill_in_dev(dev, pdev, ent, driver))) {
+                               printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
+                               goto err_g1;
+                       }
+                       if ((ret = drm_proc_init(dev, minor, drm_proc_root, &minors->dev_root))) {
+                               printk (KERN_ERR "DRM: Failed to initialize /proc/dri.\n");
+                               goto err_g1;
+                       }
+
+                       
+                       dev_class = drm_sysfs_device_add(drm_class,
+                                                        MKDEV(DRM_MAJOR,
+                                                              minor),
+                                                        &pdev->dev,
+                                                        "card%d", minor);
+                       if (IS_ERR(dev_class)) {
+                               printk(KERN_ERR "DRM: Error sysfs_device_add.\n");
+                               ret = PTR_ERR(dev_class);
+                               goto err_g2;
+                       }
+                       
+                       DRM_DEBUG("new minor assigned %d\n", minor);
+                       return 0;
+               }
+       }
+       DRM_ERROR("out of minors\n");
+       return -ENOMEM;
+err_g2:
+       drm_proc_cleanup(minor, drm_proc_root, minors->dev_root);
+err_g1:
+       *minors = (drm_minor_t){.dev = NULL, .type = DRM_MINOR_FREE};
+       drm_free(dev, sizeof(*dev), DRM_MEM_STUB);
+       return ret;
+}
+EXPORT_SYMBOL(drm_probe);
+               
+
+/**
+ * Put a device minor number.
+ *
+ * \param minor minor number.
+ * \return always zero.
+ *
+ * Cleans up the proc resources. If a minor is zero then release the foreign
+ * "drm" data, otherwise unregisters the "drm" data, frees the stub list and
+ * unregisters the character device. 
+ */
+int drm_put_minor(drm_device_t *dev)
+{
+       drm_minor_t *minors = &drm_minors[dev->minor];
+       
+       DRM_DEBUG("release minor %d\n", dev->minor);
+       
+       drm_proc_cleanup(dev->minor, drm_proc_root, minors->dev_root);
+       drm_sysfs_device_remove(MKDEV(DRM_MAJOR, dev->minor));
+       
+       *minors = (drm_minor_t){.dev = NULL, .type = DRM_MINOR_FREE};
+       drm_free(dev, sizeof(*dev), DRM_MEM_STUB);
+       
+       return 0;
+}
+
diff --git a/drivers/char/drm/drm_sysfs.c b/drivers/char/drm/drm_sysfs.c
new file mode 100644 (file)
index 0000000..6c9a830
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * drm_sysfs.c - Modifications to drm_sysfs_class.c to support
+ *               extra sysfs attribute from DRM. Normal drm_sysfs_class
+ *               does not allow adding attributes.
+ *
+ * Copyright (c) 2004 Jon Smirl <jonsmirl@gmail.com>
+ * Copyright (c) 2003-2004 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (c) 2003-2004 IBM Corp.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/device.h>
+#include <linux/kdev_t.h>
+#include <linux/err.h>
+
+#include "drm_core.h"
+
+struct drm_sysfs_class {
+       struct class_device_attribute attr;
+       struct class class;
+};
+#define to_drm_sysfs_class(d) container_of(d, struct drm_sysfs_class, class)
+
+struct simple_dev {
+       struct list_head node;
+       dev_t dev;
+       struct class_device class_dev;
+};
+#define to_simple_dev(d) container_of(d, struct simple_dev, class_dev)
+
+static LIST_HEAD(simple_dev_list);
+static DEFINE_SPINLOCK(simple_dev_list_lock);
+
+static void release_simple_dev(struct class_device *class_dev)
+{
+       struct simple_dev *s_dev = to_simple_dev(class_dev);
+       kfree(s_dev);
+}
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+       struct simple_dev *s_dev = to_simple_dev(class_dev);
+       return print_dev_t(buf, s_dev->dev);
+}
+
+static void drm_sysfs_class_release(struct class *class)
+{
+       struct drm_sysfs_class *cs = to_drm_sysfs_class(class);
+       kfree(cs);
+}
+
+/* Display the version of drm_core. This doesn't work right in current design */
+static ssize_t version_show(struct class *dev, char *buf)
+{
+       return sprintf(buf, "%s %d.%d.%d %s\n", DRIVER_NAME, DRIVER_MAJOR,
+                      DRIVER_MINOR, DRIVER_PATCHLEVEL, DRIVER_DATE);
+}
+
+static CLASS_ATTR(version, S_IRUGO, version_show, NULL);
+
+/**
+ * drm_sysfs_create - create a struct drm_sysfs_class structure
+ * @owner: pointer to the module that is to "own" this struct drm_sysfs_class
+ * @name: pointer to a string for the name of this class.
+ *
+ * This is used to create a struct drm_sysfs_class pointer that can then be used
+ * in calls to drm_sysfs_device_add().
+ *
+ * Note, the pointer created here is to be destroyed when finished by making a
+ * call to drm_sysfs_destroy().
+ */
+struct drm_sysfs_class *drm_sysfs_create(struct module *owner, char *name)
+{
+       struct drm_sysfs_class *cs;
+       int retval;
+
+       cs = kmalloc(sizeof(*cs), GFP_KERNEL);
+       if (!cs) {
+               retval = -ENOMEM;
+               goto error;
+       }
+       memset(cs, 0x00, sizeof(*cs));
+
+       cs->class.name = name;
+       cs->class.class_release = drm_sysfs_class_release;
+       cs->class.release = release_simple_dev;
+
+       cs->attr.attr.name = "dev";
+       cs->attr.attr.mode = S_IRUGO;
+       cs->attr.attr.owner = owner;
+       cs->attr.show = show_dev;
+       cs->attr.store = NULL;
+
+       retval = class_register(&cs->class);
+       if (retval)
+               goto error;
+       class_create_file(&cs->class, &class_attr_version);
+
+       return cs;
+
+      error:
+       kfree(cs);
+       return ERR_PTR(retval);
+}
+
+/**
+ * drm_sysfs_destroy - destroys a struct drm_sysfs_class structure
+ * @cs: pointer to the struct drm_sysfs_class that is to be destroyed
+ *
+ * Note, the pointer to be destroyed must have been created with a call to
+ * drm_sysfs_create().
+ */
+void drm_sysfs_destroy(struct drm_sysfs_class *cs)
+{
+       if ((cs == NULL) || (IS_ERR(cs)))
+               return;
+
+       class_unregister(&cs->class);
+}
+
+/**
+ * drm_sysfs_device_add - adds a class device to sysfs for a character driver
+ * @cs: pointer to the struct drm_sysfs_class that this device should be registered to.
+ * @dev: the dev_t for the device to be added.
+ * @device: a pointer to a struct device that is assiociated with this class device.
+ * @fmt: string for the class device's name
+ *
+ * A struct class_device will be created in sysfs, registered to the specified
+ * class.  A "dev" file will be created, showing the dev_t for the device.  The
+ * pointer to the struct class_device will be returned from the call.  Any further
+ * sysfs files that might be required can be created using this pointer.
+ * Note: the struct drm_sysfs_class passed to this function must have previously been
+ * created with a call to drm_sysfs_create().
+ */
+struct class_device *drm_sysfs_device_add(struct drm_sysfs_class *cs, dev_t dev,
+                                         struct device *device,
+                                         const char *fmt, ...)
+{
+       va_list args;
+       struct simple_dev *s_dev = NULL;
+       int retval;
+
+       if ((cs == NULL) || (IS_ERR(cs))) {
+               retval = -ENODEV;
+               goto error;
+       }
+
+       s_dev = kmalloc(sizeof(*s_dev), GFP_KERNEL);
+       if (!s_dev) {
+               retval = -ENOMEM;
+               goto error;
+       }
+       memset(s_dev, 0x00, sizeof(*s_dev));
+
+       s_dev->dev = dev;
+       s_dev->class_dev.dev = device;
+       s_dev->class_dev.class = &cs->class;
+
+       va_start(args, fmt);
+       vsnprintf(s_dev->class_dev.class_id, BUS_ID_SIZE, fmt, args);
+       va_end(args);
+       retval = class_device_register(&s_dev->class_dev);
+       if (retval)
+               goto error;
+
+       class_device_create_file(&s_dev->class_dev, &cs->attr);
+
+       spin_lock(&simple_dev_list_lock);
+       list_add(&s_dev->node, &simple_dev_list);
+       spin_unlock(&simple_dev_list_lock);
+
+       return &s_dev->class_dev;
+
+      error:
+       kfree(s_dev);
+       return ERR_PTR(retval);
+}
+
+/**
+ * drm_sysfs_device_remove - removes a class device that was created with drm_sysfs_device_add()
+ * @dev: the dev_t of the device that was previously registered.
+ *
+ * This call unregisters and cleans up a class device that was created with a
+ * call to drm_sysfs_device_add()
+ */
+void drm_sysfs_device_remove(dev_t dev)
+{
+       struct simple_dev *s_dev = NULL;
+       int found = 0;
+
+       spin_lock(&simple_dev_list_lock);
+       list_for_each_entry(s_dev, &simple_dev_list, node) {
+               if (s_dev->dev == dev) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (found) {
+               list_del(&s_dev->node);
+               spin_unlock(&simple_dev_list_lock);
+               class_device_unregister(&s_dev->class_dev);
+       } else {
+               spin_unlock(&simple_dev_list_lock);
+       }
+}
diff --git a/drivers/char/drm/drm_vm.c b/drivers/char/drm/drm_vm.c
new file mode 100644 (file)
index 0000000..f10ad6c
--- /dev/null
@@ -0,0 +1,678 @@
+/**
+ * \file drm_vm.h
+ * Memory mapping for DRM
+ * 
+ * \author Rickard E. (Rik) Faith <faith@valinux.com>
+ * \author Gareth Hughes <gareth@valinux.com>
+ */
+
+/*
+ * Created: Mon Jan  4 08:58:31 1999 by faith@valinux.com
+ *
+ * Copyright 1999 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"
+#if defined(__ia64__)
+#include <linux/efi.h>
+#endif
+
+
+/**
+ * \c nopage method for AGP virtual memory.
+ *
+ * \param vma virtual memory area.
+ * \param address access address.
+ * \return pointer to the page structure.
+ * 
+ * Find the right map and if it's AGP memory find the real physical page to
+ * map, get the page, increment the use count and return it.
+ */
+#if __OS_HAS_AGP
+static __inline__ struct page *drm_do_vm_nopage(struct vm_area_struct *vma,
+                                                unsigned long address)
+{
+       drm_file_t *priv  = vma->vm_file->private_data;
+       drm_device_t *dev = priv->dev;
+       drm_map_t *map    = NULL;
+       drm_map_list_t  *r_list;
+       struct list_head *list;
+
+       /*
+         * Find the right map
+         */
+       if (!drm_core_has_AGP(dev))
+               goto vm_nopage_error;
+
+       if(!dev->agp || !dev->agp->cant_use_aperture) goto vm_nopage_error;
+
+       list_for_each(list, &dev->maplist->head) {
+               r_list = list_entry(list, drm_map_list_t, head);
+               map = r_list->map;
+               if (!map) continue;
+               if (map->offset == VM_OFFSET(vma)) break;
+       }
+
+       if (map && map->type == _DRM_AGP) {
+               unsigned long offset = address - vma->vm_start;
+               unsigned long baddr = VM_OFFSET(vma) + offset;
+               struct drm_agp_mem *agpmem;
+               struct page *page;
+
+#ifdef __alpha__
+               /*
+                 * Adjust to a bus-relative address
+                 */
+               baddr -= dev->hose->mem_space->start;
+#endif
+
+               /*
+                 * It's AGP memory - find the real physical page to map
+                 */
+               for(agpmem = dev->agp->memory; agpmem; agpmem = agpmem->next) {
+                       if (agpmem->bound <= baddr &&
+                           agpmem->bound + agpmem->pages * PAGE_SIZE > baddr) 
+                               break;
+               }
+
+               if (!agpmem) goto vm_nopage_error;
+
+               /*
+                 * Get the page, inc the use count, and return it
+                 */
+               offset = (baddr - agpmem->bound) >> PAGE_SHIFT;
+               page = virt_to_page(__va(agpmem->memory->memory[offset]));
+               get_page(page);
+
+               DRM_DEBUG("baddr = 0x%lx page = 0x%p, offset = 0x%lx, count=%d\n",
+                         baddr, __va(agpmem->memory->memory[offset]), offset,
+                         page_count(page));
+
+               return page;
+        }
+vm_nopage_error:
+       return NOPAGE_SIGBUS;           /* Disallow mremap */
+}
+#else /* __OS_HAS_AGP */
+static __inline__ struct page *drm_do_vm_nopage(struct vm_area_struct *vma,
+                                                unsigned long address)
+{
+       return NOPAGE_SIGBUS;
+}
+#endif /* __OS_HAS_AGP */
+
+/**
+ * \c nopage method for shared virtual memory.
+ *
+ * \param vma virtual memory area.
+ * \param address access address.
+ * \return pointer to the page structure.
+ * 
+ * Get the the mapping, find the real physical page to map, get the page, and
+ * return it.
+ */
+static __inline__ struct page *drm_do_vm_shm_nopage(struct vm_area_struct *vma,
+                                                    unsigned long address)
+{
+       drm_map_t        *map    = (drm_map_t *)vma->vm_private_data;
+       unsigned long    offset;
+       unsigned long    i;
+       struct page      *page;
+
+       if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+       if (!map)                  return NOPAGE_OOM;  /* Nothing allocated */
+
+       offset   = address - vma->vm_start;
+       i = (unsigned long)map->handle + offset;
+       page = vmalloc_to_page((void *)i);
+       if (!page)
+               return NOPAGE_OOM;
+       get_page(page);
+
+       DRM_DEBUG("shm_nopage 0x%lx\n", address);
+       return page;
+}
+
+
+/**
+ * \c close method for shared virtual memory.
+ * 
+ * \param vma virtual memory area.
+ * 
+ * Deletes map information if we are the last
+ * person to close a mapping and it's not in the global maplist.
+ */
+void drm_vm_shm_close(struct vm_area_struct *vma)
+{
+       drm_file_t      *priv   = vma->vm_file->private_data;
+       drm_device_t    *dev    = priv->dev;
+       drm_vma_entry_t *pt, *prev, *next;
+       drm_map_t *map;
+       drm_map_list_t *r_list;
+       struct list_head *list;
+       int found_maps = 0;
+
+       DRM_DEBUG("0x%08lx,0x%08lx\n",
+                 vma->vm_start, vma->vm_end - vma->vm_start);
+       atomic_dec(&dev->vma_count);
+
+       map = vma->vm_private_data;
+
+       down(&dev->struct_sem);
+       for (pt = dev->vmalist, prev = NULL; pt; pt = next) {
+               next = pt->next;
+               if (pt->vma->vm_private_data == map) found_maps++;
+               if (pt->vma == vma) {
+                       if (prev) {
+                               prev->next = pt->next;
+                       } else {
+                               dev->vmalist = pt->next;
+                       }
+                       drm_free(pt, sizeof(*pt), DRM_MEM_VMAS);
+               } else {
+                       prev = pt;
+               }
+       }
+       /* We were the only map that was found */
+       if(found_maps == 1 &&
+          map->flags & _DRM_REMOVABLE) {
+               /* Check to see if we are in the maplist, if we are not, then
+                * we delete this mappings information.
+                */
+               found_maps = 0;
+               list = &dev->maplist->head;
+               list_for_each(list, &dev->maplist->head) {
+                       r_list = list_entry(list, drm_map_list_t, head);
+                       if (r_list->map == map) found_maps++;
+               }
+
+               if(!found_maps) {
+                       switch (map->type) {
+                       case _DRM_REGISTERS:
+                       case _DRM_FRAME_BUFFER:
+                               if (drm_core_has_MTRR(dev) && map->mtrr >= 0) {
+                                       int retcode;
+                                       retcode = mtrr_del(map->mtrr,
+                                                          map->offset,
+                                                          map->size);
+                                       DRM_DEBUG("mtrr_del = %d\n", retcode);
+                               }
+                               drm_ioremapfree(map->handle, map->size, dev);
+                               break;
+                       case _DRM_SHM:
+                               vfree(map->handle);
+                               break;
+                       case _DRM_AGP:
+                       case _DRM_SCATTER_GATHER:
+                               break;
+                       }
+                       drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+               }
+       }
+       up(&dev->struct_sem);
+}
+
+/**
+ * \c nopage method for DMA virtual memory.
+ *
+ * \param vma virtual memory area.
+ * \param address access address.
+ * \return pointer to the page structure.
+ * 
+ * Determine the page number from the page offset and get it from drm_device_dma::pagelist.
+ */
+static __inline__ struct page *drm_do_vm_dma_nopage(struct vm_area_struct *vma,
+                                                    unsigned long address)
+{
+       drm_file_t       *priv   = vma->vm_file->private_data;
+       drm_device_t     *dev    = priv->dev;
+       drm_device_dma_t *dma    = dev->dma;
+       unsigned long    offset;
+       unsigned long    page_nr;
+       struct page      *page;
+
+       if (!dma)                  return NOPAGE_SIGBUS; /* Error */
+       if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+       if (!dma->pagelist)        return NOPAGE_OOM ; /* Nothing allocated */
+
+       offset   = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */
+       page_nr  = offset >> PAGE_SHIFT;
+       page = virt_to_page((dma->pagelist[page_nr] + 
+                            (offset & (~PAGE_MASK))));
+
+       get_page(page);
+
+       DRM_DEBUG("dma_nopage 0x%lx (page %lu)\n", address, page_nr);
+       return page;
+}
+
+/**
+ * \c nopage method for scatter-gather virtual memory.
+ *
+ * \param vma virtual memory area.
+ * \param address access address.
+ * \return pointer to the page structure.
+ * 
+ * Determine the map offset from the page offset and get it from drm_sg_mem::pagelist.
+ */
+static __inline__ struct page *drm_do_vm_sg_nopage(struct vm_area_struct *vma,
+                                                   unsigned long address)
+{
+       drm_map_t        *map    = (drm_map_t *)vma->vm_private_data;
+       drm_file_t *priv = vma->vm_file->private_data;
+       drm_device_t *dev = priv->dev;
+       drm_sg_mem_t *entry = dev->sg;
+       unsigned long offset;
+       unsigned long map_offset;
+       unsigned long page_offset;
+       struct page *page;
+
+       if (!entry)                return NOPAGE_SIGBUS; /* Error */
+       if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+       if (!entry->pagelist)      return NOPAGE_OOM ;  /* Nothing allocated */
+
+
+       offset = address - vma->vm_start;
+       map_offset = map->offset - dev->sg->handle;
+       page_offset = (offset >> PAGE_SHIFT) + (map_offset >> PAGE_SHIFT);
+       page = entry->pagelist[page_offset];
+       get_page(page);
+
+       return page;
+}
+
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+
+static struct page *drm_vm_nopage(struct vm_area_struct *vma,
+                                  unsigned long address,
+                                  int *type) {
+       if (type) *type = VM_FAULT_MINOR;
+       return drm_do_vm_nopage(vma, address);
+}
+
+static struct page *drm_vm_shm_nopage(struct vm_area_struct *vma,
+                                      unsigned long address,
+                                      int *type) {
+       if (type) *type = VM_FAULT_MINOR;
+       return drm_do_vm_shm_nopage(vma, address);
+}
+
+static struct page *drm_vm_dma_nopage(struct vm_area_struct *vma,
+                                      unsigned long address,
+                                      int *type) {
+       if (type) *type = VM_FAULT_MINOR;
+       return drm_do_vm_dma_nopage(vma, address);
+}
+
+static struct page *drm_vm_sg_nopage(struct vm_area_struct *vma,
+                                     unsigned long address,
+                                     int *type) {
+       if (type) *type = VM_FAULT_MINOR;
+       return drm_do_vm_sg_nopage(vma, address);
+}
+
+#else  /* LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,0) */
+
+static struct page *drm_vm_nopage(struct vm_area_struct *vma,
+                                  unsigned long address,
+                                  int unused) {
+       return drm_do_vm_nopage(vma, address);
+}
+
+static struct page *drm_vm_shm_nopage(struct vm_area_struct *vma,
+                                      unsigned long address,
+                                      int unused) {
+       return drm_do_vm_shm_nopage(vma, address);
+}
+
+static struct page *drm_vm_dma_nopage(struct vm_area_struct *vma,
+                                      unsigned long address,
+                                      int unused) {
+       return drm_do_vm_dma_nopage(vma, address);
+}
+
+static struct page *drm_vm_sg_nopage(struct vm_area_struct *vma,
+                                     unsigned long address,
+                                     int unused) {
+       return drm_do_vm_sg_nopage(vma, address);
+}
+
+#endif
+
+
+/** AGP virtual memory operations */
+static struct vm_operations_struct   drm_vm_ops = {
+       .nopage = drm_vm_nopage,
+       .open   = drm_vm_open,
+       .close  = drm_vm_close,
+};
+
+/** Shared virtual memory operations */
+static struct vm_operations_struct   drm_vm_shm_ops = {
+       .nopage = drm_vm_shm_nopage,
+       .open   = drm_vm_open,
+       .close  = drm_vm_shm_close,
+};
+
+/** DMA virtual memory operations */
+static struct vm_operations_struct   drm_vm_dma_ops = {
+       .nopage = drm_vm_dma_nopage,
+       .open   = drm_vm_open,
+       .close  = drm_vm_close,
+};
+
+/** Scatter-gather virtual memory operations */
+static struct vm_operations_struct   drm_vm_sg_ops = {
+       .nopage = drm_vm_sg_nopage,
+       .open   = drm_vm_open,
+       .close  = drm_vm_close,
+};
+
+
+/**
+ * \c open method for shared virtual memory.
+ * 
+ * \param vma virtual memory area.
+ * 
+ * Create a new drm_vma_entry structure as the \p vma private data entry and
+ * add it to drm_device::vmalist.
+ */
+void drm_vm_open(struct vm_area_struct *vma)
+{
+       drm_file_t      *priv   = vma->vm_file->private_data;
+       drm_device_t    *dev    = priv->dev;
+       drm_vma_entry_t *vma_entry;
+
+       DRM_DEBUG("0x%08lx,0x%08lx\n",
+                 vma->vm_start, vma->vm_end - vma->vm_start);
+       atomic_inc(&dev->vma_count);
+
+       vma_entry = drm_alloc(sizeof(*vma_entry), DRM_MEM_VMAS);
+       if (vma_entry) {
+               down(&dev->struct_sem);
+               vma_entry->vma  = vma;
+               vma_entry->next = dev->vmalist;
+               vma_entry->pid  = current->pid;
+               dev->vmalist    = vma_entry;
+               up(&dev->struct_sem);
+       }
+}
+
+/**
+ * \c close method for all virtual memory types.
+ * 
+ * \param vma virtual memory area.
+ * 
+ * Search the \p vma private data entry in drm_device::vmalist, unlink it, and
+ * free it.
+ */
+void drm_vm_close(struct vm_area_struct *vma)
+{
+       drm_file_t      *priv   = vma->vm_file->private_data;
+       drm_device_t    *dev    = priv->dev;
+       drm_vma_entry_t *pt, *prev;
+
+       DRM_DEBUG("0x%08lx,0x%08lx\n",
+                 vma->vm_start, vma->vm_end - vma->vm_start);
+       atomic_dec(&dev->vma_count);
+
+       down(&dev->struct_sem);
+       for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) {
+               if (pt->vma == vma) {
+                       if (prev) {
+                               prev->next = pt->next;
+                       } else {
+                               dev->vmalist = pt->next;
+                       }
+                       drm_free(pt, sizeof(*pt), DRM_MEM_VMAS);
+                       break;
+               }
+       }
+       up(&dev->struct_sem);
+}
+
+/**
+ * mmap DMA memory.
+ *
+ * \param filp file pointer.
+ * \param vma virtual memory area.
+ * \return zero on success or a negative number on failure.
+ * 
+ * Sets the virtual memory area operations structure to vm_dma_ops, the file
+ * pointer, and calls vm_open().
+ */
+int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
+{
+       drm_file_t       *priv   = filp->private_data;
+       drm_device_t     *dev;
+       drm_device_dma_t *dma;
+       unsigned long    length  = vma->vm_end - vma->vm_start;
+
+       lock_kernel();
+       dev      = priv->dev;
+       dma      = dev->dma;
+       DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+                 vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+                               /* Length must match exact page count */
+       if (!dma || (length >> PAGE_SHIFT) != dma->page_count) {
+               unlock_kernel();
+               return -EINVAL;
+       }
+       unlock_kernel();
+
+       vma->vm_ops   = &drm_vm_dma_ops;
+
+#if LINUX_VERSION_CODE <= 0x02040e /* KERNEL_VERSION(2,4,14) */
+       vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+#else
+       vma->vm_flags |= VM_RESERVED; /* Don't swap */
+#endif
+
+       vma->vm_file  =  filp;  /* Needed for drm_vm_open() */
+       drm_vm_open(vma);
+       return 0;
+}
+
+unsigned long drm_core_get_map_ofs(drm_map_t *map)
+{
+       return map->offset;
+}
+EXPORT_SYMBOL(drm_core_get_map_ofs);
+
+unsigned long drm_core_get_reg_ofs(struct drm_device *dev)
+{
+#ifdef __alpha__
+       return dev->hose->dense_mem_base - dev->hose->mem_space->start;
+#else
+       return 0;
+#endif
+}
+EXPORT_SYMBOL(drm_core_get_reg_ofs);
+
+/**
+ * mmap DMA memory.
+ *
+ * \param filp file pointer.
+ * \param vma virtual memory area.
+ * \return zero on success or a negative number on failure.
+ * 
+ * If the virtual memory area has no offset associated with it then it's a DMA
+ * area, so calls mmap_dma(). Otherwise searches the map in drm_device::maplist,
+ * checks that the restricted flag is not set, sets the virtual memory operations
+ * according to the mapping type and remaps the pages. Finally sets the file
+ * pointer and calls vm_open().
+ */
+int drm_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       drm_file_t      *priv   = filp->private_data;
+       drm_device_t    *dev    = priv->dev;
+       drm_map_t       *map    = NULL;
+       drm_map_list_t  *r_list;
+       unsigned long   offset  = 0;
+       struct list_head *list;
+
+       DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+                 vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+       if ( !priv->authenticated ) return -EACCES;
+
+       /* We check for "dma". On Apple's UniNorth, it's valid to have
+        * the AGP mapped at physical address 0
+        * --BenH.
+        */
+       if (!VM_OFFSET(vma)
+#if __OS_HAS_AGP
+           && (!dev->agp || dev->agp->agp_info.device->vendor != PCI_VENDOR_ID_APPLE)
+#endif
+           )
+               return drm_mmap_dma(filp, vma);
+
+                               /* A sequential search of a linked list is
+                                  fine here because: 1) there will only be
+                                  about 5-10 entries in the list and, 2) a
+                                  DRI client only has to do this mapping
+                                  once, so it doesn't have to be optimized
+                                  for performance, even if the list was a
+                                  bit longer. */
+       list_for_each(list, &dev->maplist->head) {
+               unsigned long off;
+
+               r_list = list_entry(list, drm_map_list_t, head);
+               map = r_list->map;
+               if (!map) continue;
+               off = dev->driver->get_map_ofs(map);
+               if (off == VM_OFFSET(vma)) break;
+       }
+
+       if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN)))
+               return -EPERM;
+
+                               /* Check for valid size. */
+       if (map->size != vma->vm_end - vma->vm_start) return -EINVAL;
+
+       if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) {
+               vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE);
+#if defined(__i386__) || defined(__x86_64__)
+               pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
+#else
+                               /* Ye gads this is ugly.  With more thought
+                                   we could move this up higher and use
+                                   `protection_map' instead.  */
+               vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
+                       __pte(pgprot_val(vma->vm_page_prot)))));
+#endif
+       }
+
+       switch (map->type) {
+        case _DRM_AGP:
+         if (drm_core_has_AGP(dev) && dev->agp->cant_use_aperture) {
+                /*
+                 * On some platforms we can't talk to bus dma address from the CPU, so for
+                 * memory of type DRM_AGP, we'll deal with sorting out the real physical
+                 * pages and mappings in nopage()
+                 */
+#if defined(__powerpc__)
+               pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
+#endif
+                vma->vm_ops = &drm_vm_ops;
+                break;
+         }
+                /* fall through to _DRM_FRAME_BUFFER... */        
+       case _DRM_FRAME_BUFFER:
+       case _DRM_REGISTERS:
+               if (VM_OFFSET(vma) >= __pa(high_memory)) {
+#if defined(__i386__) || defined(__x86_64__)
+                       if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) {
+                               pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
+                               pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT;
+                       }
+#elif defined(__powerpc__)
+                       pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE | _PAGE_GUARDED;
+#endif
+                       vma->vm_flags |= VM_IO; /* not in core dump */
+               }
+#if defined(__ia64__)
+               if (efi_range_is_wc(vma->vm_start, vma->vm_end -
+                                   vma->vm_start))
+                       vma->vm_page_prot =
+                               pgprot_writecombine(vma->vm_page_prot);
+               else
+                       vma->vm_page_prot =
+                               pgprot_noncached(vma->vm_page_prot);
+#endif
+               offset = dev->driver->get_reg_ofs(dev);
+#ifdef __sparc__
+               if (io_remap_page_range(DRM_RPR_ARG(vma) vma->vm_start,
+                                       VM_OFFSET(vma) + offset,
+                                       vma->vm_end - vma->vm_start,
+                                       vma->vm_page_prot, 0))
+#else
+               if (remap_pfn_range(DRM_RPR_ARG(vma) vma->vm_start,
+                                    (VM_OFFSET(vma) + offset) >> PAGE_SHIFT,
+                                    vma->vm_end - vma->vm_start,
+                                    vma->vm_page_prot))
+#endif
+                               return -EAGAIN;
+               DRM_DEBUG("   Type = %d; start = 0x%lx, end = 0x%lx,"
+                         " offset = 0x%lx\n",
+                         map->type,
+                         vma->vm_start, vma->vm_end, VM_OFFSET(vma) + offset);
+               vma->vm_ops = &drm_vm_ops;
+               break;
+       case _DRM_SHM:
+               vma->vm_ops = &drm_vm_shm_ops;
+               vma->vm_private_data = (void *)map;
+                               /* Don't let this area swap.  Change when
+                                  DRM_KERNEL advisory is supported. */
+#if LINUX_VERSION_CODE <= 0x02040e /* KERNEL_VERSION(2,4,14) */
+               vma->vm_flags |= VM_LOCKED;
+#else
+               vma->vm_flags |= VM_RESERVED;
+#endif
+               break;
+       case _DRM_SCATTER_GATHER:
+               vma->vm_ops = &drm_vm_sg_ops;
+               vma->vm_private_data = (void *)map;
+#if LINUX_VERSION_CODE <= 0x02040e /* KERNEL_VERSION(2,4,14) */
+               vma->vm_flags |= VM_LOCKED;
+#else
+               vma->vm_flags |= VM_RESERVED;
+#endif
+                break;
+       default:
+               return -EINVAL; /* This should never happen. */
+       }
+#if LINUX_VERSION_CODE <= 0x02040e /* KERNEL_VERSION(2,4,14) */
+       vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+#else
+       vma->vm_flags |= VM_RESERVED; /* Don't swap */
+#endif
+
+       vma->vm_file  =  filp;  /* Needed for drm_vm_open() */
+       drm_vm_open(vma);
+       return 0;
+}
+EXPORT_SYMBOL(drm_mmap);
diff --git a/drivers/char/drm/tdfx_drv.h b/drivers/char/drm/tdfx_drv.h
new file mode 100644 (file)
index 0000000..a582a3d
--- /dev/null
@@ -0,0 +1,50 @@
+/* tdfx.h -- 3dfx DRM template customization -*- linux-c -*-
+ * Created: Wed Feb 14 12:32:32 2001 by gareth@valinux.com
+ *
+ * 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.
+ *
+ * Authors:
+ *    Gareth Hughes <gareth@valinux.com>
+ */
+
+#ifndef __TDFX_H__
+#define __TDFX_H__
+
+/* This remains constant for all DRM template files.
+ */
+#define DRM(x) tdfx_##x
+
+/* General customization:
+ */
+
+#define DRIVER_AUTHOR          "VA Linux Systems Inc."
+
+#define DRIVER_NAME            "tdfx"
+#define DRIVER_DESC            "3dfx Banshee/Voodoo3+"
+#define DRIVER_DATE            "20010216"
+
+#define DRIVER_MAJOR           1
+#define DRIVER_MINOR           0
+#define DRIVER_PATCHLEVEL      0
+
+#endif
diff --git a/drivers/char/ds1302.c b/drivers/char/ds1302.c
new file mode 100644 (file)
index 0000000..a75e860
--- /dev/null
@@ -0,0 +1,354 @@
+/*!***************************************************************************
+*!
+*! FILE NAME  : ds1302.c
+*!
+*! DESCRIPTION: Implements an interface for the DS1302 RTC
+*!
+*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status
+*!
+*! ---------------------------------------------------------------------------
+*!
+*! (C) Copyright 1999, 2000, 2001  Axis Communications AB, LUND, SWEDEN
+*!
+*!***************************************************************************/
+
+#include <linux/config.h>
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/bcd.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/rtc.h>
+#if defined(CONFIG_M32R)
+#include <asm/m32r.h>
+#endif
+
+#define RTC_MAJOR_NR 121 /* local major, change later */
+
+static const char ds1302_name[] = "ds1302";
+
+/* Send 8 bits. */
+static void
+out_byte_rtc(unsigned int reg_addr, unsigned char x)
+{
+       //RST H
+       outw(0x0001,(unsigned long)PLD_RTCRSTODT);
+       //write data
+       outw(((x<<8)|(reg_addr&0xff)),(unsigned long)PLD_RTCWRDATA);
+       //WE
+       outw(0x0002,(unsigned long)PLD_RTCCR);
+       //wait
+       while(inw((unsigned long)PLD_RTCCR));
+
+       //RST L
+       outw(0x0000,(unsigned long)PLD_RTCRSTODT);
+
+}
+
+static unsigned char
+in_byte_rtc(unsigned int reg_addr)
+{
+       unsigned char retval;
+
+       //RST H
+       outw(0x0001,(unsigned long)PLD_RTCRSTODT);
+       //write data
+       outw((reg_addr&0xff),(unsigned long)PLD_RTCRDDATA);
+       //RE
+       outw(0x0001,(unsigned long)PLD_RTCCR);
+       //wait
+       while(inw((unsigned long)PLD_RTCCR));
+
+       //read data
+       retval=(inw((unsigned long)PLD_RTCRDDATA) & 0xff00)>>8;
+
+       //RST L
+       outw(0x0000,(unsigned long)PLD_RTCRSTODT);
+
+       return retval;
+}
+
+/* Enable writing. */
+
+static void
+ds1302_wenable(void)
+{
+       out_byte_rtc(0x8e,0x00);
+}
+
+/* Disable writing. */
+
+static void
+ds1302_wdisable(void)
+{
+       out_byte_rtc(0x8e,0x80);
+}
+
+
+
+/* Read a byte from the selected register in the DS1302. */
+
+unsigned char
+ds1302_readreg(int reg)
+{
+       unsigned char x;
+
+       x=in_byte_rtc((0x81 | (reg << 1))); /* read register */
+
+       return x;
+}
+
+/* Write a byte to the selected register. */
+
+void
+ds1302_writereg(int reg, unsigned char val)
+{
+       ds1302_wenable();
+       out_byte_rtc((0x80 | (reg << 1)),val);
+       ds1302_wdisable();
+}
+
+void
+get_rtc_time(struct rtc_time *rtc_tm)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       local_irq_disable();
+
+       rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
+       rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
+       rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
+       rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
+       rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
+       rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
+
+       local_irq_restore(flags);
+
+       BCD_TO_BIN(rtc_tm->tm_sec);
+       BCD_TO_BIN(rtc_tm->tm_min);
+       BCD_TO_BIN(rtc_tm->tm_hour);
+       BCD_TO_BIN(rtc_tm->tm_mday);
+       BCD_TO_BIN(rtc_tm->tm_mon);
+       BCD_TO_BIN(rtc_tm->tm_year);
+
+       /*
+        * Account for differences between how the RTC uses the values
+        * and how they are defined in a struct rtc_time;
+        */
+
+       if (rtc_tm->tm_year <= 69)
+               rtc_tm->tm_year += 100;
+
+       rtc_tm->tm_mon--;
+}
+
+static unsigned char days_in_mo[] =
+    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/* ioctl that supports RTC_RD_TIME and RTC_SET_TIME (read and set time/date). */
+
+static int
+rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+         unsigned long arg)
+{
+       unsigned long flags;
+
+       switch(cmd) {
+               case RTC_RD_TIME:       /* read the time/date from RTC  */
+               {
+                       struct rtc_time rtc_tm;
+
+                       memset(&rtc_tm, 0, sizeof (struct rtc_time));
+                       get_rtc_time(&rtc_tm);
+                       if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time)))
+                               return -EFAULT;
+                       return 0;
+               }
+
+               case RTC_SET_TIME:      /* set the RTC */
+               {
+                       struct rtc_time rtc_tm;
+                       unsigned char mon, day, hrs, min, sec, leap_yr;
+                       unsigned int yrs;
+
+                       if (!capable(CAP_SYS_TIME))
+                               return -EPERM;
+
+                       if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)))
+                               return -EFAULT;
+
+                       yrs = rtc_tm.tm_year + 1900;
+                       mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
+                       day = rtc_tm.tm_mday;
+                       hrs = rtc_tm.tm_hour;
+                       min = rtc_tm.tm_min;
+                       sec = rtc_tm.tm_sec;
+
+
+                       if ((yrs < 1970) || (yrs > 2069))
+                               return -EINVAL;
+
+                       leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+
+                       if ((mon > 12) || (day == 0))
+                               return -EINVAL;
+
+                       if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+                               return -EINVAL;
+
+                       if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+                               return -EINVAL;
+
+                       if (yrs >= 2000)
+                               yrs -= 2000;    /* RTC (0, 1, ... 69) */
+                       else
+                               yrs -= 1900;    /* RTC (70, 71, ... 99) */
+
+                       BIN_TO_BCD(sec);
+                       BIN_TO_BCD(min);
+                       BIN_TO_BCD(hrs);
+                       BIN_TO_BCD(day);
+                       BIN_TO_BCD(mon);
+                       BIN_TO_BCD(yrs);
+
+                       local_irq_save(flags);
+                       local_irq_disable();
+                       CMOS_WRITE(yrs, RTC_YEAR);
+                       CMOS_WRITE(mon, RTC_MONTH);
+                       CMOS_WRITE(day, RTC_DAY_OF_MONTH);
+                       CMOS_WRITE(hrs, RTC_HOURS);
+                       CMOS_WRITE(min, RTC_MINUTES);
+                       CMOS_WRITE(sec, RTC_SECONDS);
+                       local_irq_restore(flags);
+
+                       /* Notice that at this point, the RTC is updated but
+                        * the kernel is still running with the old time.
+                        * You need to set that separately with settimeofday
+                        * or adjtimex.
+                        */
+                       return 0;
+               }
+
+               case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */
+               {
+                       int tcs_val;
+
+                       if (!capable(CAP_SYS_TIME))
+                               return -EPERM;
+
+                       if(copy_from_user(&tcs_val, (int*)arg, sizeof(int)))
+                               return -EFAULT;
+
+                       tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F);
+                       ds1302_writereg(RTC_TRICKLECHARGER, tcs_val);
+                       return 0;
+               }
+               default:
+                       return -EINVAL;
+       }
+}
+
+int
+get_rtc_status(char *buf)
+{
+       char *p;
+       struct rtc_time tm;
+
+       p = buf;
+
+       get_rtc_time(&tm);
+
+       /*
+        * There is no way to tell if the luser has the RTC set for local
+        * time or for Universal Standard Time (GMT). Probably local though.
+        */
+
+       p += sprintf(p,
+               "rtc_time\t: %02d:%02d:%02d\n"
+               "rtc_date\t: %04d-%02d-%02d\n",
+               tm.tm_hour, tm.tm_min, tm.tm_sec,
+               tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+
+       return  p - buf;
+}
+
+
+/* The various file operations we support. */
+
+static struct file_operations rtc_fops = {
+       .owner          = THIS_MODULE,
+       .ioctl          = rtc_ioctl,
+};
+
+/* Probe for the chip by writing something to its RAM and try reading it back. */
+
+#define MAGIC_PATTERN 0x42
+
+static int __init
+ds1302_probe(void)
+{
+       int retval, res, baur;
+
+       baur=(boot_cpu_data.bus_clock/(2*1000*1000));
+
+       printk("%s: Set PLD_RTCBAUR = %d\n", ds1302_name,baur);
+
+       outw(0x0000,(unsigned long)PLD_RTCCR);
+       outw(0x0000,(unsigned long)PLD_RTCRSTODT);
+       outw(baur,(unsigned long)PLD_RTCBAUR);
+
+       /* Try to talk to timekeeper. */
+
+       ds1302_wenable();
+       /* write RAM byte 0 */
+       /* write something magic */
+       out_byte_rtc(0xc0,MAGIC_PATTERN);
+
+       /* read RAM byte 0 */
+       if((res = in_byte_rtc(0xc1)) == MAGIC_PATTERN) {
+               char buf[100];
+               ds1302_wdisable();
+               printk("%s: RTC found.\n", ds1302_name);
+               get_rtc_status(buf);
+               printk(buf);
+               retval = 1;
+       } else {
+               printk("%s: RTC not found.\n", ds1302_name);
+               retval = 0;
+       }
+
+       return retval;
+}
+
+
+/* Just probe for the RTC and register the device to handle the ioctl needed. */
+
+int __init
+ds1302_init(void)
+{
+       if (!ds1302_probe()) {
+               return -1;
+       }
+       return 0;
+}
+
+static int __init ds1302_register(void)
+{
+       ds1302_init();
+       if (register_chrdev(RTC_MAJOR_NR, ds1302_name, &rtc_fops)) {
+               printk(KERN_INFO "%s: unable to get major %d for rtc\n",
+                      ds1302_name, RTC_MAJOR_NR);
+               return -1;
+       }
+       return 0;
+}
+
+module_init(ds1302_register);
diff --git a/drivers/char/mxser.h b/drivers/char/mxser.h
new file mode 100644 (file)
index 0000000..e7fd0b0
--- /dev/null
@@ -0,0 +1,450 @@
+#ifndef _MXSER_H
+#define _MXSER_H
+
+/*
+ *     Semi-public control interfaces
+ */
+/*
+ *     MOXA ioctls
+ */
+
+#define MOXA                   0x400
+#define MOXA_GETDATACOUNT      (MOXA + 23)
+#define        MOXA_GET_CONF           (MOXA + 35)
+#define MOXA_DIAGNOSE          (MOXA + 50)
+#define MOXA_CHKPORTENABLE     (MOXA + 60)
+#define MOXA_HighSpeedOn       (MOXA + 61)
+#define MOXA_GET_MAJOR         (MOXA + 63)
+#define MOXA_GET_CUMAJOR       (MOXA + 64)
+#define MOXA_GETMSTATUS                (MOXA + 65)
+#define MOXA_SET_OP_MODE       (MOXA + 66)
+#define MOXA_GET_OP_MODE       (MOXA + 67)
+
+#define RS232_MODE             0
+#define RS485_2WIRE_MODE       1
+#define RS422_MODE             2
+#define RS485_4WIRE_MODE       3
+#define OP_MODE_MASK           3
+// above add by Victor Yu. 01-05-2004
+
+#define TTY_THRESHOLD_THROTTLE  128
+
+#define LO_WATER               (TTY_FLIPBUF_SIZE)
+#define HI_WATER               (TTY_FLIPBUF_SIZE*2*3/4)
+
+// added by James. 03-11-2004.
+#define MOXA_SDS_GETICOUNTER   (MOXA + 68)
+#define MOXA_SDS_RSTICOUNTER   (MOXA + 69)
+// (above) added by James.
+
+#define MOXA_ASPP_OQUEUE       (MOXA + 70)
+#define MOXA_ASPP_SETBAUD      (MOXA + 71)
+#define MOXA_ASPP_GETBAUD      (MOXA + 72)
+#define MOXA_ASPP_MON          (MOXA + 73)
+#define MOXA_ASPP_LSTATUS      (MOXA + 74)
+#define MOXA_ASPP_MON_EXT      (MOXA + 75)
+#define MOXA_SET_BAUD_METHOD   (MOXA + 76)
+
+
+/* --------------------------------------------------- */
+
+#define NPPI_NOTIFY_PARITY     0x01
+#define NPPI_NOTIFY_FRAMING    0x02
+#define NPPI_NOTIFY_HW_OVERRUN 0x04
+#define NPPI_NOTIFY_SW_OVERRUN 0x08
+#define NPPI_NOTIFY_BREAK      0x10
+
+#define NPPI_NOTIFY_CTSHOLD         0x01       // Tx hold by CTS low
+#define NPPI_NOTIFY_DSRHOLD         0x02       // Tx hold by DSR low
+#define NPPI_NOTIFY_XOFFHOLD        0x08       // Tx hold by Xoff received
+#define NPPI_NOTIFY_XOFFXENT        0x10       // Xoff Sent
+
+//CheckIsMoxaMust return value
+#define MOXA_OTHER_UART                        0x00
+#define MOXA_MUST_MU150_HWID           0x01
+#define MOXA_MUST_MU860_HWID           0x02
+
+// follow just for Moxa Must chip define.
+//
+// when LCR register (offset 0x03) write following value,
+// the Must chip will enter enchance mode. And write value
+// on EFR (offset 0x02) bit 6,7 to change bank.
+#define MOXA_MUST_ENTER_ENCHANCE       0xBF
+
+// when enhance mode enable, access on general bank register
+#define MOXA_MUST_GDL_REGISTER         0x07
+#define MOXA_MUST_GDL_MASK             0x7F
+#define MOXA_MUST_GDL_HAS_BAD_DATA     0x80
+
+#define MOXA_MUST_LSR_RERR             0x80    // error in receive FIFO
+// enchance register bank select and enchance mode setting register
+// when LCR register equal to 0xBF
+#define MOXA_MUST_EFR_REGISTER         0x02
+// enchance mode enable
+#define MOXA_MUST_EFR_EFRB_ENABLE      0x10
+// enchance reister bank set 0, 1, 2
+#define MOXA_MUST_EFR_BANK0            0x00
+#define MOXA_MUST_EFR_BANK1            0x40
+#define MOXA_MUST_EFR_BANK2            0x80
+#define MOXA_MUST_EFR_BANK3            0xC0
+#define MOXA_MUST_EFR_BANK_MASK                0xC0
+
+// set XON1 value register, when LCR=0xBF and change to bank0
+#define MOXA_MUST_XON1_REGISTER                0x04
+
+// set XON2 value register, when LCR=0xBF and change to bank0
+#define MOXA_MUST_XON2_REGISTER                0x05
+
+// set XOFF1 value register, when LCR=0xBF and change to bank0
+#define MOXA_MUST_XOFF1_REGISTER       0x06
+
+// set XOFF2 value register, when LCR=0xBF and change to bank0
+#define MOXA_MUST_XOFF2_REGISTER       0x07
+
+#define MOXA_MUST_RBRTL_REGISTER       0x04
+#define MOXA_MUST_RBRTH_REGISTER       0x05
+#define MOXA_MUST_RBRTI_REGISTER       0x06
+#define MOXA_MUST_THRTL_REGISTER       0x07
+#define MOXA_MUST_ENUM_REGISTER                0x04
+#define MOXA_MUST_HWID_REGISTER                0x05
+#define MOXA_MUST_ECR_REGISTER         0x06
+#define MOXA_MUST_CSR_REGISTER         0x07
+
+// good data mode enable
+#define MOXA_MUST_FCR_GDA_MODE_ENABLE  0x20
+// only good data put into RxFIFO
+#define MOXA_MUST_FCR_GDA_ONLY_ENABLE  0x10
+
+// enable CTS interrupt
+#define MOXA_MUST_IER_ECTSI            0x80
+// eanble RTS interrupt
+#define MOXA_MUST_IER_ERTSI            0x40
+// enable Xon/Xoff interrupt
+#define MOXA_MUST_IER_XINT             0x20
+// enable GDA interrupt
+#define MOXA_MUST_IER_EGDAI            0x10
+
+#define MOXA_MUST_RECV_ISR             (UART_IER_RDI | MOXA_MUST_IER_EGDAI)
+
+// GDA interrupt pending
+#define MOXA_MUST_IIR_GDA              0x1C
+#define MOXA_MUST_IIR_RDA              0x04
+#define MOXA_MUST_IIR_RTO              0x0C
+#define MOXA_MUST_IIR_LSR              0x06
+
+// recieved Xon/Xoff or specical interrupt pending
+#define MOXA_MUST_IIR_XSC              0x10
+
+// RTS/CTS change state interrupt pending
+#define MOXA_MUST_IIR_RTSCTS           0x20
+#define MOXA_MUST_IIR_MASK             0x3E
+
+#define MOXA_MUST_MCR_XON_FLAG         0x40
+#define MOXA_MUST_MCR_XON_ANY          0x80
+#define MOXA_MUST_MCR_TX_XON           0x08
+
+
+// software flow control on chip mask value
+#define MOXA_MUST_EFR_SF_MASK          0x0F
+// send Xon1/Xoff1
+#define MOXA_MUST_EFR_SF_TX1           0x08
+// send Xon2/Xoff2
+#define MOXA_MUST_EFR_SF_TX2           0x04
+// send Xon1,Xon2/Xoff1,Xoff2
+#define MOXA_MUST_EFR_SF_TX12          0x0C
+// don't send Xon/Xoff
+#define MOXA_MUST_EFR_SF_TX_NO         0x00
+// Tx software flow control mask
+#define MOXA_MUST_EFR_SF_TX_MASK       0x0C
+// don't receive Xon/Xoff
+#define MOXA_MUST_EFR_SF_RX_NO         0x00
+// receive Xon1/Xoff1
+#define MOXA_MUST_EFR_SF_RX1           0x02
+// receive Xon2/Xoff2
+#define MOXA_MUST_EFR_SF_RX2           0x01
+// receive Xon1,Xon2/Xoff1,Xoff2
+#define MOXA_MUST_EFR_SF_RX12          0x03
+// Rx software flow control mask
+#define MOXA_MUST_EFR_SF_RX_MASK       0x03
+
+//#define MOXA_MUST_MIN_XOFFLIMIT               66
+//#define MOXA_MUST_MIN_XONLIMIT                20
+//#define ID1_RX_TRIG                   120
+
+
+#define CHECK_MOXA_MUST_XOFFLIMIT(info) {      \
+       if ( (info)->IsMoxaMustChipFlag &&      \
+        (info)->HandFlow.XoffLimit < MOXA_MUST_MIN_XOFFLIMIT ) {       \
+               (info)->HandFlow.XoffLimit = MOXA_MUST_MIN_XOFFLIMIT;   \
+               (info)->HandFlow.XonLimit = MOXA_MUST_MIN_XONLIMIT;     \
+       }       \
+}
+
+#define ENABLE_MOXA_MUST_ENCHANCE_MODE(baseio) { \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr |= MOXA_MUST_EFR_EFRB_ENABLE;     \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define DISABLE_MOXA_MUST_ENCHANCE_MODE(baseio) {      \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_EFRB_ENABLE;    \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_XON1_VALUE(baseio, Value) {      \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK0;   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb((u8)(Value), (baseio)+MOXA_MUST_XON1_REGISTER);    \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_XON2_VALUE(baseio, Value) {      \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK0;   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb((u8)(Value), (baseio)+MOXA_MUST_XON2_REGISTER);    \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_XOFF1_VALUE(baseio, Value) {     \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK0;   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb((u8)(Value), (baseio)+MOXA_MUST_XOFF1_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_XOFF2_VALUE(baseio, Value) {     \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK0;   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb((u8)(Value), (baseio)+MOXA_MUST_XOFF2_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_RBRTL_VALUE(baseio, Value) {     \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK1;   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb((u8)(Value), (baseio)+MOXA_MUST_RBRTL_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_RBRTH_VALUE(baseio, Value) {     \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK1;   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb((u8)(Value), (baseio)+MOXA_MUST_RBRTH_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_RBRTI_VALUE(baseio, Value) {     \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK1;   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb((u8)(Value), (baseio)+MOXA_MUST_RBRTI_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_THRTL_VALUE(baseio, Value) {     \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK1;   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb((u8)(Value), (baseio)+MOXA_MUST_THRTL_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+//#define MOXA_MUST_RBRL_VALUE  4
+#define SET_MOXA_MUST_FIFO_VALUE(info) {       \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((info)->base+UART_LCR);  \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (info)->base+UART_LCR);  \
+       __efr = inb((info)->base+MOXA_MUST_EFR_REGISTER);       \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK1;   \
+       outb(__efr, (info)->base+MOXA_MUST_EFR_REGISTER);       \
+       outb((u8)((info)->rx_high_water), (info)->base+MOXA_MUST_RBRTH_REGISTER);       \
+       outb((u8)((info)->rx_trigger), (info)->base+MOXA_MUST_RBRTI_REGISTER);  \
+       outb((u8)((info)->rx_low_water), (info)->base+MOXA_MUST_RBRTL_REGISTER);        \
+       outb(__oldlcr, (info)->base+UART_LCR);  \
+}
+
+
+
+#define SET_MOXA_MUST_ENUM_VALUE(baseio, Value) {      \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK2;   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb((u8)(Value), (baseio)+MOXA_MUST_ENUM_REGISTER);    \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define GET_MOXA_MUST_HARDWARE_ID(baseio, pId) {       \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_BANK_MASK;      \
+       __efr |= MOXA_MUST_EFR_BANK2;   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       *pId = inb((baseio)+MOXA_MUST_HWID_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(baseio) {       \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_SF_MASK;        \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_JUST_TX_SOFTWARE_FLOW_CONTROL(baseio) {  \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_SF_MASK;        \
+       __efr |= MOXA_MUST_EFR_SF_TX1;  \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(baseio) {    \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_SF_TX_MASK;     \
+       __efr |= MOXA_MUST_EFR_SF_TX1;  \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(baseio) {   \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_SF_TX_MASK;     \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define SET_MOXA_MUST_JUST_RX_SOFTWARE_FLOW_CONTROL(baseio) {  \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_SF_MASK;        \
+       __efr |= MOXA_MUST_EFR_SF_RX1;  \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(baseio) {    \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_SF_RX_MASK;     \
+       __efr |= MOXA_MUST_EFR_SF_RX1;  \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(baseio) {   \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_SF_RX_MASK;     \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define ENABLE_MOXA_MUST_TX_RX_SOFTWARE_FLOW_CONTROL(baseio) { \
+       u8      __oldlcr, __efr;        \
+       __oldlcr = inb((baseio)+UART_LCR);      \
+       outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR);      \
+       __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER);   \
+       __efr &= ~MOXA_MUST_EFR_SF_MASK;        \
+       __efr |= (MOXA_MUST_EFR_SF_RX1|MOXA_MUST_EFR_SF_TX1);   \
+       outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER);   \
+       outb(__oldlcr, (baseio)+UART_LCR);      \
+}
+
+#define ENABLE_MOXA_MUST_XON_ANY_FLOW_CONTROL(baseio) {        \
+       u8      __oldmcr;       \
+       __oldmcr = inb((baseio)+UART_MCR);      \
+       __oldmcr |= MOXA_MUST_MCR_XON_ANY;      \
+       outb(__oldmcr, (baseio)+UART_MCR);      \
+}
+
+#define DISABLE_MOXA_MUST_XON_ANY_FLOW_CONTROL(baseio) {       \
+       u8      __oldmcr;       \
+       __oldmcr = inb((baseio)+UART_MCR);      \
+       __oldmcr &= ~MOXA_MUST_MCR_XON_ANY;     \
+       outb(__oldmcr, (baseio)+UART_MCR);      \
+}
+
+#define READ_MOXA_MUST_GDL(baseio)     inb((baseio)+MOXA_MUST_GDL_REGISTER)
+
+
+#ifndef INIT_WORK
+#define INIT_WORK(_work, _func, _data){        \
+       _data->tqueue.routine = _func;\
+       _data->tqueue.data = _data;\
+       }
+#endif
+
+#endif
diff --git a/drivers/char/s3c2410-rtc.c b/drivers/char/s3c2410-rtc.c
new file mode 100644 (file)
index 0000000..687d9fa
--- /dev/null
@@ -0,0 +1,587 @@
+/* drivers/char/s3c2410_rtc.c
+ *
+ * Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk>
+ *                   http://www.simtec.co.uk/products/SWLINUX/
+ *
+ * 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.
+ *
+ * S3C2410 Internal RTC Driver
+ *
+ *  Changelog:
+ *     08-Nov-2004     BJD     Initial creation
+ *     12-Nov-2004     BJD     Added periodic IRQ and PM code
+ *     22-Nov-2004     BJD     Sign-test on alarm code to check for <0
+*/
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+#include <asm/hardware.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/rtc.h>
+
+#include <asm/mach/time.h>
+
+#include <asm/hardware/clock.h>
+#include <asm/arch/regs-rtc.h>
+
+/* need this for the RTC_AF definitions */
+#include <linux/mc146818rtc.h>
+
+#undef S3C2410_VA_RTC
+#define S3C2410_VA_RTC s3c2410_rtc_base
+
+static struct resource *s3c2410_rtc_mem;
+
+static void __iomem *s3c2410_rtc_base;
+static int s3c2410_rtc_alarmno = NO_IRQ;
+static int s3c2410_rtc_tickno  = NO_IRQ;
+static int s3c2410_rtc_freq    = 1;
+
+static spinlock_t s3c2410_rtc_pie_lock = SPIN_LOCK_UNLOCKED;
+
+/* IRQ Handlers */
+
+static irqreturn_t s3c2410_rtc_alarmirq(int irq, void *id, struct pt_regs *r)
+{
+       rtc_update(1, RTC_AF | RTC_IRQF);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t s3c2410_rtc_tickirq(int irq, void *id, struct pt_regs *r)
+{
+       rtc_update(1, RTC_PF | RTC_IRQF);
+       return IRQ_HANDLED;
+}
+
+/* Update control registers */
+static void s3c2410_rtc_setaie(int to)
+{
+       unsigned int tmp;
+
+       pr_debug("%s: aie=%d\n", __FUNCTION__, to);
+
+       tmp = readb(S3C2410_RTCALM);
+
+       if (to)
+               tmp |= S3C2410_RTCALM_ALMEN;
+       else
+               tmp &= ~S3C2410_RTCALM_ALMEN;
+
+
+       writeb(tmp, S3C2410_RTCALM);
+}
+
+static void s3c2410_rtc_setpie(int to)
+{
+       unsigned int tmp;
+
+       pr_debug("%s: pie=%d\n", __FUNCTION__, to);
+
+       spin_lock_irq(&s3c2410_rtc_pie_lock);
+       tmp = readb(S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;
+
+       if (to)
+               tmp |= S3C2410_TICNT_ENABLE;
+
+       writeb(tmp, S3C2410_TICNT);
+       spin_unlock_irq(&s3c2410_rtc_pie_lock);
+}
+
+static void s3c2410_rtc_setfreq(int freq)
+{
+       unsigned int tmp;
+
+       spin_lock_irq(&s3c2410_rtc_pie_lock);
+       tmp = readb(S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
+
+       s3c2410_rtc_freq = freq;
+
+       tmp |= (128 / freq)-1;
+
+       writeb(tmp, S3C2410_TICNT);
+       spin_unlock_irq(&s3c2410_rtc_pie_lock);
+}
+
+/* Time read/write */
+
+static void s3c2410_rtc_gettime(struct rtc_time *rtc_tm)
+{
+       unsigned int have_retried = 0;
+
+ retry_get_time:
+       rtc_tm->tm_min  = readb(S3C2410_RTCMIN);
+       rtc_tm->tm_hour = readb(S3C2410_RTCHOUR);
+       rtc_tm->tm_mday = readb(S3C2410_RTCDATE);
+       rtc_tm->tm_mon  = readb(S3C2410_RTCMON);
+       rtc_tm->tm_year = readb(S3C2410_RTCYEAR);
+       rtc_tm->tm_sec  = readb(S3C2410_RTCSEC);
+
+       /* the only way to work out wether the system was mid-update
+        * when we read it is to check the second counter, and if it
+        * is zero, then we re-try the entire read
+        */
+
+       if (rtc_tm->tm_sec == 0 && !have_retried) {
+               have_retried = 1;
+               goto retry_get_time;
+       }
+
+       pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
+                rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
+                rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
+
+       BCD_TO_BIN(rtc_tm->tm_sec);
+       BCD_TO_BIN(rtc_tm->tm_min);
+       BCD_TO_BIN(rtc_tm->tm_hour);
+       BCD_TO_BIN(rtc_tm->tm_mday);
+       BCD_TO_BIN(rtc_tm->tm_mon);
+       BCD_TO_BIN(rtc_tm->tm_year);
+
+       rtc_tm->tm_year += 100;
+       rtc_tm->tm_mon -= 1;
+}
+
+
+static int s3c2410_rtc_settime(struct rtc_time *tm)
+{
+       /* the rtc gets round the y2k problem by just not supporting it */
+
+       if (tm->tm_year < 100)
+               return -EINVAL;
+
+       writeb(BIN2BCD(tm->tm_sec),  S3C2410_RTCSEC);
+       writeb(BIN2BCD(tm->tm_min),  S3C2410_RTCMIN);
+       writeb(BIN2BCD(tm->tm_hour), S3C2410_RTCHOUR);
+       writeb(BIN2BCD(tm->tm_mday), S3C2410_RTCDATE);
+       writeb(BIN2BCD(tm->tm_mon + 1), S3C2410_RTCMON);
+       writeb(BIN2BCD(tm->tm_year - 100), S3C2410_RTCYEAR);
+
+       return 0;
+}
+
+static void s3c2410_rtc_getalarm(struct rtc_wkalrm *alrm)
+{
+       struct rtc_time *alm_tm = &alrm->time;
+       unsigned int alm_en;
+
+       alm_tm->tm_sec  = readb(S3C2410_ALMSEC);
+       alm_tm->tm_min  = readb(S3C2410_ALMMIN);
+       alm_tm->tm_hour = readb(S3C2410_ALMHOUR);
+       alm_tm->tm_mon  = readb(S3C2410_ALMMON);
+       alm_tm->tm_mday = readb(S3C2410_ALMDATE);
+       alm_tm->tm_year = readb(S3C2410_ALMYEAR);
+
+       alm_en = readb(S3C2410_RTCALM);
+
+       pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
+                alm_en,
+                alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
+                alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
+
+
+       /* decode the alarm enable field */
+
+       if (alm_en & S3C2410_RTCALM_SECEN) {
+               BCD_TO_BIN(alm_tm->tm_sec);
+       } else {
+               alm_tm->tm_sec = 0xff;
+       }
+
+       if (alm_en & S3C2410_RTCALM_MINEN) {
+               BCD_TO_BIN(alm_tm->tm_min);
+       } else {
+               alm_tm->tm_min = 0xff;
+       }
+
+       if (alm_en & S3C2410_RTCALM_HOUREN) {
+               BCD_TO_BIN(alm_tm->tm_hour);
+       } else {
+               alm_tm->tm_hour = 0xff;
+       }
+
+       if (alm_en & S3C2410_RTCALM_DAYEN) {
+               BCD_TO_BIN(alm_tm->tm_mday);
+       } else {
+               alm_tm->tm_mday = 0xff;
+       }
+
+       if (alm_en & S3C2410_RTCALM_MONEN) {
+               BCD_TO_BIN(alm_tm->tm_mon);
+               alm_tm->tm_mon -= 1;
+       } else {
+               alm_tm->tm_mon = 0xff;
+       }
+
+       if (alm_en & S3C2410_RTCALM_YEAREN) {
+               BCD_TO_BIN(alm_tm->tm_year);
+       } else {
+               alm_tm->tm_year = 0xffff;
+       }
+
+       /* todo - set alrm->enabled ? */
+}
+
+static int s3c2410_rtc_setalarm(struct rtc_wkalrm *alrm)
+{
+       struct rtc_time *tm = &alrm->time;
+       unsigned int alrm_en;
+
+       pr_debug("s3c2410_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
+                alrm->enabled,
+                tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
+                tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
+
+       if (alrm->enabled || 1) {
+               alrm_en = readb(S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
+               writeb(0x00, S3C2410_RTCALM);
+
+               if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
+                       alrm_en |= S3C2410_RTCALM_SECEN;
+                       writeb(BIN2BCD(tm->tm_sec), S3C2410_ALMSEC);
+               }
+
+               if (tm->tm_min < 60 && tm->tm_min >= 0) {
+                       alrm_en |= S3C2410_RTCALM_MINEN;
+                       writeb(BIN2BCD(tm->tm_min), S3C2410_ALMMIN);
+               }
+
+               if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
+                       alrm_en |= S3C2410_RTCALM_HOUREN;
+                       writeb(BIN2BCD(tm->tm_hour), S3C2410_ALMHOUR);
+               }
+
+               pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en);
+
+               writeb(alrm_en, S3C2410_RTCALM);
+               enable_irq_wake(s3c2410_rtc_alarmno);
+       } else {
+               alrm_en = readb(S3C2410_RTCALM);
+               alrm_en &= ~S3C2410_RTCALM_ALMEN;
+               writeb(alrm_en, S3C2410_RTCALM);
+               disable_irq_wake(s3c2410_rtc_alarmno);
+       }
+
+       return 0;
+}
+
+static int s3c2410_rtc_ioctl(unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case RTC_AIE_OFF:
+       case RTC_AIE_ON:
+               s3c2410_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0);
+               return 0;
+
+       case RTC_PIE_OFF:
+       case RTC_PIE_ON:
+               s3c2410_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0);
+               return 0;
+
+       case RTC_IRQP_READ:
+               return put_user(s3c2410_rtc_freq, (unsigned long __user *)arg);
+
+       case RTC_IRQP_SET:
+               if (arg < 1 || arg > 64)
+                       return -EINVAL;
+
+               if (!capable(CAP_SYS_RESOURCE))
+                       return -EACCES;
+
+               /* check for power of 2 */
+
+               if ((arg & (arg-1)) != 0)
+                       return -EINVAL;
+
+               pr_debug("s3c2410_rtc: setting frequency %ld\n", arg);
+
+               s3c2410_rtc_setfreq(arg);
+               return 0;
+
+       case RTC_UIE_ON:
+       case RTC_UIE_OFF:
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static int s3c2410_rtc_proc(char *buf)
+{
+       unsigned int rtcalm = readb(S3C2410_RTCALM);
+       unsigned int ticnt = readb (S3C2410_TICNT);
+       char *p = buf;
+
+       p += sprintf(p, "alarm_IRQ\t: %s\n",
+                    (rtcalm & S3C2410_RTCALM_ALMEN) ? "yes" : "no" );
+       p += sprintf(p, "periodic_IRQ\t: %s\n",
+                    (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
+       p += sprintf(p, "periodic_freq\t: %d\n", s3c2410_rtc_freq);
+
+       return p - buf;
+}
+
+static int s3c2410_rtc_open(void)
+{
+       int ret;
+
+       ret = request_irq(s3c2410_rtc_alarmno, s3c2410_rtc_alarmirq,
+                         SA_INTERRUPT,  "s3c2410-rtc alarm", NULL);
+
+       if (ret)
+               printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_alarmno);
+
+       ret = request_irq(s3c2410_rtc_tickno, s3c2410_rtc_tickirq,
+                         SA_INTERRUPT,  "s3c2410-rtc tick", NULL);
+
+       if (ret) {
+               printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_tickno);
+               goto tick_err;
+       }
+
+       return ret;
+
+ tick_err:
+       free_irq(s3c2410_rtc_alarmno, NULL);
+       return ret;
+}
+
+static void s3c2410_rtc_release(void)
+{
+       /* do not clear AIE here, it may be needed for wake */
+
+       s3c2410_rtc_setpie(0);
+       free_irq(s3c2410_rtc_alarmno, NULL);
+       free_irq(s3c2410_rtc_tickno, NULL);
+}
+
+static struct rtc_ops s3c2410_rtcops = {
+       .owner          = THIS_MODULE,
+       .open           = s3c2410_rtc_open,
+       .release        = s3c2410_rtc_release,
+       .ioctl          = s3c2410_rtc_ioctl,
+       .read_time      = s3c2410_rtc_gettime,
+       .set_time       = s3c2410_rtc_settime,
+       .read_alarm     = s3c2410_rtc_getalarm,
+       .set_alarm      = s3c2410_rtc_setalarm,
+       .proc           = s3c2410_rtc_proc,
+};
+
+static void s3c2410_rtc_enable(struct device *dev, int en)
+{
+       unsigned int tmp;
+
+       if (s3c2410_rtc_base == NULL)
+               return;
+
+       if (!en) {
+               tmp = readb(S3C2410_RTCCON);
+               writeb(tmp & ~S3C2410_RTCCON_RTCEN, S3C2410_RTCCON);
+
+               tmp = readb(S3C2410_TICNT);
+               writeb(tmp & ~S3C2410_TICNT_ENABLE, S3C2410_TICNT);
+       } else {
+               /* re-enable the device, and check it is ok */
+
+               if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
+                       dev_info(dev, "rtc disabled, re-enabling\n");
+
+                       tmp = readb(S3C2410_RTCCON);
+                       writeb(tmp | S3C2410_RTCCON_RTCEN , S3C2410_RTCCON);
+               }
+
+               if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
+                       dev_info(dev, "removing S3C2410_RTCCON_CNTSEL\n");
+
+                       tmp = readb(S3C2410_RTCCON);
+                       writeb(tmp& ~S3C2410_RTCCON_CNTSEL , S3C2410_RTCCON);
+               }
+
+               if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
+                       dev_info(dev, "removing S3C2410_RTCCON_CLKRST\n");
+
+                       tmp = readb(S3C2410_RTCCON);
+                       writeb(tmp & ~S3C2410_RTCCON_CLKRST, S3C2410_RTCCON);
+               }
+       }
+}
+
+static int s3c2410_rtc_remove(struct device *dev)
+{
+       unregister_rtc(&s3c2410_rtcops);
+
+       s3c2410_rtc_setpie(0);
+       s3c2410_rtc_setaie(0);
+
+       if (s3c2410_rtc_mem != NULL) {
+               pr_debug("s3c2410_rtc: releasing s3c2410_rtc_mem\n");
+               iounmap(s3c2410_rtc_base);
+               release_resource(s3c2410_rtc_mem);
+               kfree(s3c2410_rtc_mem);
+       }
+
+       return 0;
+}
+
+static int s3c2410_rtc_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct resource *res;
+       int ret;
+
+       pr_debug("%s: probe=%p, device=%p\n", __FUNCTION__, pdev, dev);
+
+       /* find the IRQs */
+
+       s3c2410_rtc_tickno = platform_get_irq(pdev, 1);
+       if (s3c2410_rtc_tickno <= 0) {
+               dev_err(dev, "no irq for rtc tick\n");
+               return -ENOENT;
+       }
+
+       s3c2410_rtc_alarmno = platform_get_irq(pdev, 0);
+       if (s3c2410_rtc_alarmno <= 0) {
+               dev_err(dev, "no irq for alarm\n");
+               return -ENOENT;
+       }
+
+       pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
+                s3c2410_rtc_tickno, s3c2410_rtc_alarmno);
+
+       /* get the memory region */
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(dev, "failed to get memory region resource\n");
+               return -ENOENT;
+       }
+
+       s3c2410_rtc_mem = request_mem_region(res->start, res->end-res->start+1,
+                                    pdev->name);
+
+       if (s3c2410_rtc_mem == NULL) {
+               dev_err(dev, "failed to reserve memory region\n");
+               ret = -ENOENT;
+               goto exit_err;
+       }
+
+       s3c2410_rtc_base = ioremap(res->start, res->end - res->start + 1);
+       if (s3c2410_rtc_base == NULL) {
+               dev_err(dev, "failed ioremap()\n");
+               ret = -EINVAL;
+               goto exit_err;
+       }
+
+       s3c2410_rtc_mem = res;
+       pr_debug("s3c2410_rtc_base=%p\n", s3c2410_rtc_base);
+
+       pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
+
+       /* check to see if everything is setup correctly */
+
+       s3c2410_rtc_enable(dev, 1);
+
+       pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
+
+       s3c2410_rtc_setfreq(s3c2410_rtc_freq);
+
+       /* register RTC and exit */
+
+       register_rtc(&s3c2410_rtcops);
+       return 0;
+
+ exit_err:
+       dev_err(dev, "error %d during initialisation\n", ret);
+
+       return ret;
+}
+
+#ifdef CONFIG_PM
+
+/* S3C2410 RTC Power management control */
+
+static struct timespec s3c2410_rtc_delta;
+
+static int ticnt_save;
+
+static int s3c2410_rtc_suspend(struct device *dev, u32 state, u32 level)
+{
+       struct rtc_time tm;
+       struct timespec time;
+
+       time.tv_nsec = 0;
+
+       if (level == SUSPEND_POWER_DOWN) {
+               /* save TICNT for anyone using periodic interrupts */
+
+               ticnt_save = readb(S3C2410_TICNT);
+
+               /* calculate time delta for suspend */
+
+               s3c2410_rtc_gettime(&tm);
+               rtc_tm_to_time(&tm, &time.tv_sec);
+               save_time_delta(&s3c2410_rtc_delta, &time);
+               s3c2410_rtc_enable(dev, 0);
+       }
+
+       return 0;
+}
+
+static int s3c2410_rtc_resume(struct device *dev, u32 level)
+{
+       struct rtc_time tm;
+       struct timespec time;
+
+       time.tv_nsec = 0;
+
+       s3c2410_rtc_enable(dev, 1);
+       s3c2410_rtc_gettime(&tm);
+       rtc_tm_to_time(&tm, &time.tv_sec);
+       restore_time_delta(&s3c2410_rtc_delta, &time);
+
+       writeb(ticnt_save, S3C2410_TICNT);
+       return 0;
+}
+#else
+#define s3c2410_rtc_suspend NULL
+#define s3c2410_rtc_resume  NULL
+#endif
+
+static struct device_driver s3c2410_rtcdrv = {
+       .name           = "s3c2410-rtc",
+       .bus            = &platform_bus_type,
+       .probe          = s3c2410_rtc_probe,
+       .remove         = s3c2410_rtc_remove,
+       .suspend        = s3c2410_rtc_suspend,
+       .resume         = s3c2410_rtc_resume,
+};
+
+static char __initdata banner[] = "S3C2410 RTC, (c) 2004 Simtec Electronics\n";
+
+static int __init s3c2410_rtc_init(void)
+{
+       printk(banner);
+       return driver_register(&s3c2410_rtcdrv);
+}
+
+static void __exit s3c2410_rtc_exit(void)
+{
+       driver_unregister(&s3c2410_rtcdrv);
+}
+
+module_init(s3c2410_rtc_init);
+module_exit(s3c2410_rtc_exit);
+
+MODULE_DESCRIPTION("S3C24XX RTC Driver");
+MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/watchdog/s3c2410_wdt.c b/drivers/char/watchdog/s3c2410_wdt.c
new file mode 100644 (file)
index 0000000..47480f8
--- /dev/null
@@ -0,0 +1,516 @@
+/* linux/drivers/char/watchdog/s3c2410_wdt.c
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 Watchdog Timer Support
+ *
+ * Based on, softdog.c by Alan Cox,
+ *     (c) Copyright 1996 Alan Cox <alan@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Changelog:
+ *     05-Oct-2004     BJD     Added semaphore init to stop crashes on open
+ *                             Fixed tmr_count / wdt_count confusion
+ *                             Added configurable debug
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <asm/arch/map.h>
+#include <asm/hardware/clock.h>
+
+#undef S3C2410_VA_WATCHDOG
+#define S3C2410_VA_WATCHDOG (0)
+
+#include <asm/arch/regs-watchdog.h>
+
+#define PFX "s3c2410-wdt: "
+
+#define CONFIG_S3C2410_WATCHDOG_ATBOOT         (0)
+#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME   (15)
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+static int nowayout = 1;
+#else
+static int nowayout = 0;
+#endif
+
+static int tmr_margin  = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
+static int tmr_atboot  = CONFIG_S3C2410_WATCHDOG_ATBOOT;
+static int soft_noboot = 0;
+static int debug       = 0;
+
+module_param(tmr_margin,  int, 0);
+module_param(tmr_atboot,  int, 0);
+module_param(nowayout,    int, 0);
+module_param(soft_noboot, int, 0);
+module_param(debug,      int, 0);
+
+MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. default=" __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")");
+
+MODULE_PARM_DESC(tmr_atboot, "Watchdog is started at boot time if set to 1, default=" __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));
+
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
+
+MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default depends on ONLY_TESTING)");
+
+MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)");
+
+
+typedef enum close_state {
+       CLOSE_STATE_NOT,
+       CLOSE_STATE_ALLOW=0x4021
+} close_state_t;
+
+static DECLARE_MUTEX(open_lock);
+
+static struct resource *wdt_mem;
+static struct resource *wdt_irq;
+static struct clk      *wdt_clock;
+static void __iomem    *wdt_base;
+static unsigned int     wdt_count;
+static close_state_t    allow_close;
+
+/* watchdog control routines */
+
+#define DBG(msg...) do { \
+       if (debug) \
+               printk(KERN_INFO msg); \
+       } while(0)
+
+/* functions */
+
+static int s3c2410wdt_keepalive(void)
+{
+       writel(wdt_count, wdt_base + S3C2410_WTCNT);
+       return 0;
+}
+
+static int s3c2410wdt_stop(void)
+{
+       unsigned long wtcon;
+
+       wtcon = readl(wdt_base + S3C2410_WTCON);
+       wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
+       writel(wtcon, wdt_base + S3C2410_WTCON);
+
+       return 0;
+}
+
+static int s3c2410wdt_start(void)
+{
+       unsigned long wtcon;
+
+       s3c2410wdt_stop();
+
+       wtcon = readl(wdt_base + S3C2410_WTCON);
+       wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
+
+       if (soft_noboot) {
+               wtcon |= S3C2410_WTCON_INTEN;
+               wtcon &= ~S3C2410_WTCON_RSTEN;
+       } else {
+               wtcon &= ~S3C2410_WTCON_INTEN;
+               wtcon |= S3C2410_WTCON_RSTEN;
+       }
+
+       DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",
+           __FUNCTION__, wdt_count, wtcon);
+
+       writel(wdt_count, wdt_base + S3C2410_WTDAT);
+       writel(wdt_count, wdt_base + S3C2410_WTCNT);
+       writel(wtcon, wdt_base + S3C2410_WTCON);
+
+       return 0;
+}
+
+static int s3c2410wdt_set_heartbeat(int timeout)
+{
+       unsigned int freq = clk_get_rate(wdt_clock);
+       unsigned int count;
+       unsigned int divisor = 1;
+       unsigned long wtcon;
+
+       if (timeout < 1)
+               return -EINVAL;
+
+       /* I think someone must have missed a divide-by-2 in the 2410,
+        * as a divisor of 128 gives half the calculated delay...
+        */
+
+       freq /= 128/2;
+       count = timeout * freq;
+
+       DBG("%s: count=%d, timeout=%d, freq=%d\n",
+           __FUNCTION__, count, timeout, freq);
+
+       /* if the count is bigger than the watchdog register,
+          then work out what we need to do (and if) we can
+          actually make this value
+       */
+
+       if (count >= 0x10000) {
+               for (divisor = 1; divisor <= 0x100; divisor++) {
+                       if ((count / divisor) < 0x10000)
+                               break;
+               }
+
+               if ((count / divisor) >= 0x10000) {
+                       printk(KERN_ERR PFX "timeout %d too big\n", timeout);
+                       return -EINVAL;
+               }
+       }
+
+       tmr_margin = timeout;
+
+       DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
+           __FUNCTION__, timeout, divisor, count, count/divisor);
+
+       count /= divisor;
+       wdt_count = count;
+
+       /* update the pre-scaler */
+       wtcon = readl(wdt_base + S3C2410_WTCON);
+       wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
+       wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
+
+       writel(count, wdt_base + S3C2410_WTDAT);
+       writel(wtcon, wdt_base + S3C2410_WTCON);
+
+       return 0;
+}
+
+/*
+ *     /dev/watchdog handling
+ */
+
+static int s3c2410wdt_open(struct inode *inode, struct file *file)
+{
+       if(down_trylock(&open_lock))
+               return -EBUSY;
+
+       if (nowayout) {
+               __module_get(THIS_MODULE);
+       } else {
+               allow_close = CLOSE_STATE_ALLOW;
+       }
+
+       /* start the timer */
+       s3c2410wdt_start();
+       return nonseekable_open(inode, file);
+}
+
+static int s3c2410wdt_release(struct inode *inode, struct file *file)
+{
+       /*
+        *      Shut off the timer.
+        *      Lock it in if it's a module and we set nowayout
+        */
+       if (allow_close == CLOSE_STATE_ALLOW) {
+               s3c2410wdt_stop();
+       } else {
+               printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
+               s3c2410wdt_keepalive();
+       }
+
+       allow_close = CLOSE_STATE_NOT;
+       up(&open_lock);
+       return 0;
+}
+
+static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,
+                               size_t len, loff_t *ppos)
+{
+       /*
+        *      Refresh the timer.
+        */
+       if(len) {
+               if (!nowayout) {
+                       size_t i;
+
+                       /* In case it was set long ago */
+                       allow_close = CLOSE_STATE_NOT;
+
+                       for (i = 0; i != len; i++) {
+                               char c;
+
+                               if (get_user(c, data + i))
+                                       return -EFAULT;
+                               if (c == 'V')
+                                       allow_close = CLOSE_STATE_ALLOW;
+                       }
+               }
+
+               s3c2410wdt_keepalive();
+       }
+       return len;
+}
+
+#define OPTIONS WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE
+
+static struct watchdog_info s3c2410_wdt_ident = {
+       .options          =     OPTIONS,
+       .firmware_version =     0,
+       .identity         =     "S3C2410 Watchdog",
+};
+
+
+static int s3c2410wdt_ioctl(struct inode *inode, struct file *file,
+       unsigned int cmd, unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       int __user *p = argp;
+       int new_margin;
+
+       switch (cmd) {
+               default:
+                       return -ENOIOCTLCMD;
+
+               case WDIOC_GETSUPPORT:
+                       return copy_to_user(argp, &s3c2410_wdt_ident,
+                               sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
+
+               case WDIOC_GETSTATUS:
+               case WDIOC_GETBOOTSTATUS:
+                       return put_user(0, p);
+
+               case WDIOC_KEEPALIVE:
+                       s3c2410wdt_keepalive();
+                       return 0;
+
+               case WDIOC_SETTIMEOUT:
+                       if (get_user(new_margin, p))
+                               return -EFAULT;
+
+                       if (s3c2410wdt_set_heartbeat(new_margin))
+                               return -EINVAL;
+
+                       s3c2410wdt_keepalive();
+                       return put_user(tmr_margin, p);
+
+               case WDIOC_GETTIMEOUT:
+                       return put_user(tmr_margin, p);
+       }
+}
+
+/*
+ *     Notifier for system down
+ */
+
+static int s3c2410wdt_notify_sys(struct notifier_block *this, unsigned long code,
+                             void *unused)
+{
+       if(code==SYS_DOWN || code==SYS_HALT) {
+               /* Turn the WDT off */
+               s3c2410wdt_stop();
+       }
+       return NOTIFY_DONE;
+}
+
+/* kernel interface */
+
+static struct file_operations s3c2410wdt_fops = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .write          = s3c2410wdt_write,
+       .ioctl          = s3c2410wdt_ioctl,
+       .open           = s3c2410wdt_open,
+       .release        = s3c2410wdt_release,
+};
+
+static struct miscdevice s3c2410wdt_miscdev = {
+       .minor          = WATCHDOG_MINOR,
+       .name           = "watchdog",
+       .fops           = &s3c2410wdt_fops,
+};
+
+static struct notifier_block s3c2410wdt_notifier = {
+       .notifier_call  = s3c2410wdt_notify_sys,
+};
+
+/* interrupt handler code */
+
+static irqreturn_t s3c2410wdt_irq(int irqno, void *param,
+                                 struct pt_regs *regs)
+{
+       printk(KERN_INFO PFX "Watchdog timer expired!\n");
+
+       s3c2410wdt_keepalive();
+       return IRQ_HANDLED;
+}
+/* device interface */
+
+static int s3c2410wdt_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct resource *res;
+       int started = 0;
+       int ret;
+       int size;
+
+       DBG("%s: probe=%p, device=%p\n", __FUNCTION__, pdev, dev);
+
+       /* get the memory region for the watchdog timer */
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               printk(KERN_INFO PFX "failed to get memory region resouce\n");
+               return -ENOENT;
+       }
+
+       size = (res->end-res->start)+1;
+       wdt_mem = request_mem_region(res->start, size, pdev->name);
+       if (wdt_mem == NULL) {
+               printk(KERN_INFO PFX "failed to get memory region\n");
+               return -ENOENT;
+       }
+
+       wdt_base = ioremap(res->start, size);
+       if (wdt_base == 0) {
+               printk(KERN_INFO PFX "failed to ioremap() region\n");
+               return -EINVAL;
+       }
+
+       DBG("probe: mapped wdt_base=%p\n", wdt_base);
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (res == NULL) {
+               printk(KERN_INFO PFX "failed to get irq resource\n");
+               return -ENOENT;
+       }
+
+       ret = request_irq(res->start, s3c2410wdt_irq, 0, pdev->name, dev);
+       if (ret != 0) {
+               printk(KERN_INFO PFX "failed to install irq (%d)\n", ret);
+               return ret;
+       }
+
+       wdt_clock = clk_get(dev, "watchdog");
+       if (wdt_clock == NULL) {
+               printk(KERN_INFO PFX "failed to find watchdog clock source\n");
+               return -ENOENT;
+       }
+
+       clk_use(wdt_clock);
+       clk_enable(wdt_clock);
+
+       /* see if we can actually set the requested timer margin, and if
+        * not, try the default value */
+
+       if (s3c2410wdt_set_heartbeat(tmr_margin)) {
+               started = s3c2410wdt_set_heartbeat(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
+
+               if (started == 0) {
+                       printk(KERN_INFO PFX "tmr_margin value out of range, default %d used\n",
+                              CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
+               } else {
+                       printk(KERN_INFO PFX "default timer value is out of range, cannot start\n");
+               }
+       }
+
+       ret = register_reboot_notifier(&s3c2410wdt_notifier);
+       if (ret) {
+               printk (KERN_ERR PFX "cannot register reboot notifier (%d)\n",
+                       ret);
+               return ret;
+       }
+
+       ret = misc_register(&s3c2410wdt_miscdev);
+       if (ret) {
+               printk (KERN_ERR PFX "cannot register miscdev on minor=%d (%d)\n",
+                       WATCHDOG_MINOR, ret);
+               unregister_reboot_notifier(&s3c2410wdt_notifier);
+               return ret;
+       }
+
+       if (tmr_atboot && started == 0) {
+               printk(KERN_INFO PFX "Starting Watchdog Timer\n");
+               s3c2410wdt_start();
+       }
+
+       return 0;
+}
+
+static int s3c2410wdt_remove(struct device *dev)
+{
+       if (wdt_mem != NULL) {
+               release_resource(wdt_mem);
+               kfree(wdt_mem);
+               wdt_mem = NULL;
+       }
+
+       if (wdt_irq != NULL) {
+               free_irq(wdt_irq->start, dev);
+               wdt_irq = NULL;
+       }
+
+       if (wdt_clock != NULL) {
+               clk_disable(wdt_clock);
+               clk_unuse(wdt_clock);
+               clk_put(wdt_clock);
+               wdt_clock = NULL;
+       }
+
+       misc_deregister(&s3c2410wdt_miscdev);
+       return 0;
+}
+
+static struct device_driver s3c2410wdt_driver = {
+       .name           = "s3c2410-wdt",
+       .bus            = &platform_bus_type,
+       .probe          = s3c2410wdt_probe,
+       .remove         = s3c2410wdt_remove,
+};
+
+
+
+static char banner[] __initdata = KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";
+
+static int __init watchdog_init(void)
+{
+       printk(banner);
+       return driver_register(&s3c2410wdt_driver);
+}
+
+static void __exit watchdog_exit(void)
+{
+       driver_unregister(&s3c2410wdt_driver);
+       unregister_reboot_notifier(&s3c2410wdt_notifier);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
new file mode 100644 (file)
index 0000000..2084593
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ *  drivers/cpufreq/cpufreq_stats.c
+ *
+ *  Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
+ *           (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
+ *
+ * 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 <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sysdev.h>
+#include <linux/cpu.h>
+#include <linux/sysfs.h>
+#include <linux/cpufreq.h>
+#include <linux/jiffies.h>
+#include <linux/percpu.h>
+#include <linux/kobject.h>
+#include <linux/spinlock.h>
+
+static spinlock_t cpufreq_stats_lock;
+
+#define CPUFREQ_STATDEVICE_ATTR(_name,_mode,_show) \
+static struct freq_attr _attr_##_name = {\
+       .attr = {.name = __stringify(_name), .owner = THIS_MODULE, \
+               .mode = _mode, }, \
+       .show = _show,\
+};
+
+static unsigned long
+delta_time(unsigned long old, unsigned long new)
+{
+       return (old > new) ? (old - new): (new + ~old + 1);
+}
+
+struct cpufreq_stats {
+       unsigned int cpu;
+       unsigned int total_trans;
+       unsigned long long last_time;
+       unsigned int max_state;
+       unsigned int state_num;
+       unsigned int last_index;
+       unsigned long long *time_in_state;
+       unsigned int *freq_table;
+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
+       unsigned int *trans_table;
+#endif
+};
+
+static struct cpufreq_stats *cpufreq_stats_table[NR_CPUS];
+
+struct cpufreq_stats_attribute {
+       struct attribute attr;
+       ssize_t(*show) (struct cpufreq_stats *, char *);
+};
+
+static int
+cpufreq_stats_update (unsigned int cpu)
+{
+       struct cpufreq_stats *stat;
+       spin_lock(&cpufreq_stats_lock);
+       stat = cpufreq_stats_table[cpu];
+       if (stat->time_in_state)
+               stat->time_in_state[stat->last_index] +=
+                       delta_time(stat->last_time, jiffies);
+       stat->last_time = jiffies;
+       spin_unlock(&cpufreq_stats_lock);
+       return 0;
+}
+
+static ssize_t
+show_total_trans(struct cpufreq_policy *policy, char *buf)
+{
+       struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu];
+       if(!stat)
+               return 0;
+       return sprintf(buf, "%d\n",
+                       cpufreq_stats_table[stat->cpu]->total_trans);
+}
+
+static ssize_t
+show_time_in_state(struct cpufreq_policy *policy, char *buf)
+{
+       ssize_t len = 0;
+       int i;
+       struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu];
+       if(!stat)
+               return 0;
+       cpufreq_stats_update(stat->cpu);
+       for (i = 0; i < stat->state_num; i++) {
+               len += sprintf(buf + len, "%u %llu\n",
+                       stat->freq_table[i], stat->time_in_state[i]);
+       }
+       return len;
+}
+
+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
+static ssize_t
+show_trans_table(struct cpufreq_policy *policy, char *buf)
+{
+       ssize_t len = 0;
+       int i, j;
+
+       struct cpufreq_stats *stat = cpufreq_stats_table[policy->cpu];
+       if(!stat)
+               return 0;
+       cpufreq_stats_update(stat->cpu);
+       for (i = 0; i < stat->state_num; i++) {
+               if (len >= PAGE_SIZE)
+                       break;
+               len += snprintf(buf + len, PAGE_SIZE - len, "%9u:\t",
+                               stat->freq_table[i]);
+
+               for (j = 0; j < stat->state_num; j++)   {
+                       if (len >= PAGE_SIZE)
+                               break;
+                       len += snprintf(buf + len, PAGE_SIZE - len, "%u\t",
+                                       stat->trans_table[i*stat->max_state+j]);
+               }
+               len += snprintf(buf + len, PAGE_SIZE - len, "\n");
+       }
+       return len;
+}
+CPUFREQ_STATDEVICE_ATTR(trans_table,0444,show_trans_table);
+#endif
+
+CPUFREQ_STATDEVICE_ATTR(total_trans,0444,show_total_trans);
+CPUFREQ_STATDEVICE_ATTR(time_in_state,0444,show_time_in_state);
+
+static struct attribute *default_attrs[] = {
+       &_attr_total_trans.attr,
+       &_attr_time_in_state.attr,
+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
+       &_attr_trans_table.attr,
+#endif
+       NULL
+};
+static struct attribute_group stats_attr_group = {
+       .attrs = default_attrs,
+       .name = "stats"
+};
+
+static int
+freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
+{
+       int index;
+       for (index = 0; index < stat->max_state; index++)
+               if (stat->freq_table[index] == freq)
+                       return index;
+       return -1;
+}
+
+static void
+cpufreq_stats_free_table (unsigned int cpu)
+{
+       struct cpufreq_stats *stat = cpufreq_stats_table[cpu];
+       struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+       if (policy && policy->cpu == cpu)       
+               sysfs_remove_group(&policy->kobj, &stats_attr_group);
+       if (stat) {
+               kfree(stat->time_in_state);
+               kfree(stat);
+       }
+       cpufreq_stats_table[cpu] = NULL;
+       if (policy)
+               cpufreq_cpu_put(policy);
+}
+
+static int
+cpufreq_stats_create_table (struct cpufreq_policy *policy,
+               struct cpufreq_frequency_table *table)
+{
+       unsigned int i, j, count = 0, ret = 0;
+       struct cpufreq_stats *stat;
+       struct cpufreq_policy *data;
+       unsigned int alloc_size;
+       unsigned int cpu = policy->cpu;
+       if (cpufreq_stats_table[cpu])
+               return -EBUSY;
+       if ((stat = kmalloc(sizeof(struct cpufreq_stats), GFP_KERNEL)) == NULL)
+               return -ENOMEM;
+       memset(stat, 0, sizeof (struct cpufreq_stats));
+
+       data = cpufreq_cpu_get(cpu);
+       if ((ret = sysfs_create_group(&data->kobj, &stats_attr_group)))
+               goto error_out;
+
+       stat->cpu = cpu;
+       cpufreq_stats_table[cpu] = stat;
+
+       for (i=0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
+               unsigned int freq = table[i].frequency;
+               if (freq == CPUFREQ_ENTRY_INVALID)
+                       continue;
+               count++;
+       }
+
+       alloc_size = count * sizeof(int) + count * sizeof(long long);
+
+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
+       alloc_size += count * count * sizeof(int);
+#endif
+       stat->max_state = count;
+       stat->time_in_state = kmalloc(alloc_size, GFP_KERNEL);
+       if (!stat->time_in_state) {
+               ret = -ENOMEM;
+               goto error_out;
+       }
+       memset(stat->time_in_state, 0, alloc_size);
+       stat->freq_table = (unsigned int *)(stat->time_in_state + count);
+
+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
+       stat->trans_table = stat->freq_table + count;
+#endif
+       j = 0;
+       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
+               unsigned int freq = table[i].frequency;
+               if (freq == CPUFREQ_ENTRY_INVALID)
+                       continue;
+               if (freq_table_get_index(stat, freq) == -1)
+                       stat->freq_table[j++] = freq;
+       }
+       stat->state_num = j;
+       spin_lock(&cpufreq_stats_lock);
+       stat->last_time = jiffies;
+       stat->last_index = freq_table_get_index(stat, policy->cur);
+       spin_unlock(&cpufreq_stats_lock);
+       cpufreq_cpu_put(data);
+       return 0;
+error_out:
+       cpufreq_cpu_put(data);
+       kfree(stat);
+       cpufreq_stats_table[cpu] = NULL;
+       return ret;
+}
+
+static int
+cpufreq_stat_notifier_policy (struct notifier_block *nb, unsigned long val,
+               void *data)
+{
+       int ret;
+       struct cpufreq_policy *policy = data;
+       struct cpufreq_frequency_table *table;
+       unsigned int cpu = policy->cpu;
+       if (val != CPUFREQ_NOTIFY)
+               return 0;
+       table = cpufreq_frequency_get_table(cpu);
+       if (!table)
+               return 0;
+       if ((ret = cpufreq_stats_create_table(policy, table)))
+               return ret;
+       return 0;
+}
+
+static int
+cpufreq_stat_notifier_trans (struct notifier_block *nb, unsigned long val,
+               void *data)
+{
+       struct cpufreq_freqs *freq = data;
+       struct cpufreq_stats *stat;
+       int old_index, new_index;
+
+       if (val != CPUFREQ_POSTCHANGE)
+               return 0;
+
+       stat = cpufreq_stats_table[freq->cpu];
+       if (!stat)
+               return 0;
+       old_index = freq_table_get_index(stat, freq->old);
+       new_index = freq_table_get_index(stat, freq->new);
+
+       cpufreq_stats_update(freq->cpu);
+       if (old_index == new_index)
+               return 0;
+
+       spin_lock(&cpufreq_stats_lock);
+       stat->last_index = new_index;
+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
+       stat->trans_table[old_index * stat->max_state + new_index]++;
+#endif
+       stat->total_trans++;
+       spin_unlock(&cpufreq_stats_lock);
+       return 0;
+}
+
+static struct notifier_block notifier_policy_block = {
+       .notifier_call = cpufreq_stat_notifier_policy
+};
+
+static struct notifier_block notifier_trans_block = {
+       .notifier_call = cpufreq_stat_notifier_trans
+};
+
+static int
+__init cpufreq_stats_init(void)
+{
+       int ret;
+       unsigned int cpu;
+       spin_lock_init(&cpufreq_stats_lock);
+       if ((ret = cpufreq_register_notifier(&notifier_policy_block,
+                               CPUFREQ_POLICY_NOTIFIER)))
+               return ret;
+
+       if ((ret = cpufreq_register_notifier(&notifier_trans_block,
+                               CPUFREQ_TRANSITION_NOTIFIER))) {
+               cpufreq_unregister_notifier(&notifier_policy_block,
+                               CPUFREQ_POLICY_NOTIFIER);
+               return ret;
+       }
+
+       for_each_cpu(cpu)
+               cpufreq_update_policy(cpu);
+       return 0;
+}
+static void
+__exit cpufreq_stats_exit(void)
+{
+       unsigned int cpu;
+       cpufreq_unregister_notifier(&notifier_policy_block,
+                       CPUFREQ_POLICY_NOTIFIER);
+       cpufreq_unregister_notifier(&notifier_trans_block,
+                       CPUFREQ_TRANSITION_NOTIFIER);
+       for_each_cpu(cpu)
+               cpufreq_stats_free_table(cpu);
+}
+
+MODULE_AUTHOR ("Zou Nan hai <nanhai.zou@intel.com>");
+MODULE_DESCRIPTION ("'cpufreq_stats' - A driver to export cpufreq stats through sysfs filesystem");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_stats_init);
+module_exit(cpufreq_stats_exit);
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
new file mode 100644 (file)
index 0000000..094835c
--- /dev/null
@@ -0,0 +1,23 @@
+menu "Hardware crypto devices"
+
+config CRYPTO_DEV_PADLOCK
+       tristate "Support for VIA PadLock ACE"
+       depends on CRYPTO && X86 && !X86_64
+       help
+         Some VIA processors come with an integrated crypto engine
+         (so called VIA PadLock ACE, Advanced Cryptography Engine)
+         that provides instructions for very fast {en,de}cryption 
+         with some algorithms.
+         
+         The instructions are used only when the CPU supports them.
+         Otherwise software encryption is used. If you are unsure,
+         say Y.
+
+config CRYPTO_DEV_PADLOCK_AES
+       bool "Support for AES in VIA PadLock"
+       depends on CRYPTO_DEV_PADLOCK
+       default y
+       help
+         Use VIA PadLock for AES algorithm.
+
+endmenu
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
new file mode 100644 (file)
index 0000000..45426ca
--- /dev/null
@@ -0,0 +1,7 @@
+
+obj-$(CONFIG_CRYPTO_DEV_PADLOCK) += padlock.o
+
+padlock-objs-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o
+
+padlock-objs := padlock-generic.o $(padlock-objs-y)
+
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
new file mode 100644 (file)
index 0000000..ed708b4
--- /dev/null
@@ -0,0 +1,468 @@
+/* 
+ * Cryptographic API.
+ *
+ * Support for VIA PadLock hardware crypto engine.
+ *
+ * Copyright (c) 2004  Michal Ludvig <michal@logix.cz>
+ *
+ * Key expansion routine taken from crypto/aes.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.
+ *
+ * ---------------------------------------------------------------------------
+ * Copyright (c) 2002, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ * All rights reserved.
+ *
+ * LICENSE TERMS
+ *
+ * The free distribution and use of this software in both source and binary
+ * form is allowed (with or without changes) provided that:
+ *
+ *   1. distributions of this source code include the above copyright
+ *      notice, this list of conditions and the following disclaimer;
+ *
+ *   2. distributions in binary form include the above copyright
+ *      notice, this list of conditions and the following disclaimer
+ *      in the documentation and/or other associated materials;
+ *
+ *   3. the copyright holder's name is not used to endorse products
+ *      built using this software without specific written permission.
+ *
+ * ALTERNATIVELY, provided that this notice is retained in full, this product
+ * may be distributed under the terms of the GNU General Public License (GPL),
+ * in which case the provisions of the GPL apply INSTEAD OF those given above.
+ *
+ * DISCLAIMER
+ *
+ * This software is provided 'as is' with no explicit or implied warranties
+ * in respect of its properties, including, but not limited to, correctness
+ * and/or fitness for purpose.
+ * ---------------------------------------------------------------------------
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/crypto.h>
+#include <linux/interrupt.h>
+#include <asm/byteorder.h>
+#include "padlock.h"
+
+#define AES_MIN_KEY_SIZE       16      /* in uint8_t units */
+#define AES_MAX_KEY_SIZE       32      /* ditto */
+#define AES_BLOCK_SIZE         16      /* ditto */
+#define AES_EXTENDED_KEY_SIZE  64      /* in uint32_t units */
+#define AES_EXTENDED_KEY_SIZE_B        (AES_EXTENDED_KEY_SIZE * sizeof(uint32_t))
+
+struct aes_ctx {
+       uint32_t e_data[AES_EXTENDED_KEY_SIZE+4];
+       uint32_t d_data[AES_EXTENDED_KEY_SIZE+4];
+       uint32_t *E;
+       uint32_t *D;
+       int key_length;
+};
+
+/* ====== Key management routines ====== */
+
+static inline uint32_t
+generic_rotr32 (const uint32_t x, const unsigned bits)
+{
+       const unsigned n = bits % 32;
+       return (x >> n) | (x << (32 - n));
+}
+
+static inline uint32_t
+generic_rotl32 (const uint32_t x, const unsigned bits)
+{
+       const unsigned n = bits % 32;
+       return (x << n) | (x >> (32 - n));
+}
+
+#define rotl generic_rotl32
+#define rotr generic_rotr32
+
+/*
+ * #define byte(x, nr) ((unsigned char)((x) >> (nr*8))) 
+ */
+static inline uint8_t
+byte(const uint32_t x, const unsigned n)
+{
+       return x >> (n << 3);
+}
+
+#define uint32_t_in(x) le32_to_cpu(*(const uint32_t *)(x))
+#define uint32_t_out(to, from) (*(uint32_t *)(to) = cpu_to_le32(from))
+
+#define E_KEY ctx->E
+#define D_KEY ctx->D
+
+static uint8_t pow_tab[256];
+static uint8_t log_tab[256];
+static uint8_t sbx_tab[256];
+static uint8_t isb_tab[256];
+static uint32_t rco_tab[10];
+static uint32_t ft_tab[4][256];
+static uint32_t it_tab[4][256];
+
+static uint32_t fl_tab[4][256];
+static uint32_t il_tab[4][256];
+
+static inline uint8_t
+f_mult (uint8_t a, uint8_t b)
+{
+       uint8_t aa = log_tab[a], cc = aa + log_tab[b];
+
+       return pow_tab[cc + (cc < aa ? 1 : 0)];
+}
+
+#define ff_mult(a,b)    (a && b ? f_mult(a, b) : 0)
+
+#define f_rn(bo, bi, n, k)                                     \
+    bo[n] =  ft_tab[0][byte(bi[n],0)] ^                                \
+             ft_tab[1][byte(bi[(n + 1) & 3],1)] ^              \
+             ft_tab[2][byte(bi[(n + 2) & 3],2)] ^              \
+             ft_tab[3][byte(bi[(n + 3) & 3],3)] ^ *(k + n)
+
+#define i_rn(bo, bi, n, k)                                     \
+    bo[n] =  it_tab[0][byte(bi[n],0)] ^                                \
+             it_tab[1][byte(bi[(n + 3) & 3],1)] ^              \
+             it_tab[2][byte(bi[(n + 2) & 3],2)] ^              \
+             it_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n)
+
+#define ls_box(x)                              \
+    ( fl_tab[0][byte(x, 0)] ^                  \
+      fl_tab[1][byte(x, 1)] ^                  \
+      fl_tab[2][byte(x, 2)] ^                  \
+      fl_tab[3][byte(x, 3)] )
+
+#define f_rl(bo, bi, n, k)                                     \
+    bo[n] =  fl_tab[0][byte(bi[n],0)] ^                                \
+             fl_tab[1][byte(bi[(n + 1) & 3],1)] ^              \
+             fl_tab[2][byte(bi[(n + 2) & 3],2)] ^              \
+             fl_tab[3][byte(bi[(n + 3) & 3],3)] ^ *(k + n)
+
+#define i_rl(bo, bi, n, k)                                     \
+    bo[n] =  il_tab[0][byte(bi[n],0)] ^                                \
+             il_tab[1][byte(bi[(n + 3) & 3],1)] ^              \
+             il_tab[2][byte(bi[(n + 2) & 3],2)] ^              \
+             il_tab[3][byte(bi[(n + 1) & 3],3)] ^ *(k + n)
+
+static void
+gen_tabs (void)
+{
+       uint32_t i, t;
+       uint8_t p, q;
+
+       /* log and power tables for GF(2**8) finite field with
+          0x011b as modular polynomial - the simplest prmitive
+          root is 0x03, used here to generate the tables */
+
+       for (i = 0, p = 1; i < 256; ++i) {
+               pow_tab[i] = (uint8_t) p;
+               log_tab[p] = (uint8_t) i;
+
+               p ^= (p << 1) ^ (p & 0x80 ? 0x01b : 0);
+       }
+
+       log_tab[1] = 0;
+
+       for (i = 0, p = 1; i < 10; ++i) {
+               rco_tab[i] = p;
+
+               p = (p << 1) ^ (p & 0x80 ? 0x01b : 0);
+       }
+
+       for (i = 0; i < 256; ++i) {
+               p = (i ? pow_tab[255 - log_tab[i]] : 0);
+               q = ((p >> 7) | (p << 1)) ^ ((p >> 6) | (p << 2));
+               p ^= 0x63 ^ q ^ ((q >> 6) | (q << 2));
+               sbx_tab[i] = p;
+               isb_tab[p] = (uint8_t) i;
+       }
+
+       for (i = 0; i < 256; ++i) {
+               p = sbx_tab[i];
+
+               t = p;
+               fl_tab[0][i] = t;
+               fl_tab[1][i] = rotl (t, 8);
+               fl_tab[2][i] = rotl (t, 16);
+               fl_tab[3][i] = rotl (t, 24);
+
+               t = ((uint32_t) ff_mult (2, p)) |
+                   ((uint32_t) p << 8) |
+                   ((uint32_t) p << 16) | ((uint32_t) ff_mult (3, p) << 24);
+
+               ft_tab[0][i] = t;
+               ft_tab[1][i] = rotl (t, 8);
+               ft_tab[2][i] = rotl (t, 16);
+               ft_tab[3][i] = rotl (t, 24);
+
+               p = isb_tab[i];
+
+               t = p;
+               il_tab[0][i] = t;
+               il_tab[1][i] = rotl (t, 8);
+               il_tab[2][i] = rotl (t, 16);
+               il_tab[3][i] = rotl (t, 24);
+
+               t = ((uint32_t) ff_mult (14, p)) |
+                   ((uint32_t) ff_mult (9, p) << 8) |
+                   ((uint32_t) ff_mult (13, p) << 16) |
+                   ((uint32_t) ff_mult (11, p) << 24);
+
+               it_tab[0][i] = t;
+               it_tab[1][i] = rotl (t, 8);
+               it_tab[2][i] = rotl (t, 16);
+               it_tab[3][i] = rotl (t, 24);
+       }
+}
+
+#define star_x(x) (((x) & 0x7f7f7f7f) << 1) ^ ((((x) & 0x80808080) >> 7) * 0x1b)
+
+#define imix_col(y,x)       \
+    u   = star_x(x);        \
+    v   = star_x(u);        \
+    w   = star_x(v);        \
+    t   = w ^ (x);          \
+   (y)  = u ^ v ^ w;        \
+   (y) ^= rotr(u ^ t,  8) ^ \
+          rotr(v ^ t, 16) ^ \
+          rotr(t,24)
+
+/* initialise the key schedule from the user supplied key */
+
+#define loop4(i)                                    \
+{   t = rotr(t,  8); t = ls_box(t) ^ rco_tab[i];    \
+    t ^= E_KEY[4 * i];     E_KEY[4 * i + 4] = t;    \
+    t ^= E_KEY[4 * i + 1]; E_KEY[4 * i + 5] = t;    \
+    t ^= E_KEY[4 * i + 2]; E_KEY[4 * i + 6] = t;    \
+    t ^= E_KEY[4 * i + 3]; E_KEY[4 * i + 7] = t;    \
+}
+
+#define loop6(i)                                    \
+{   t = rotr(t,  8); t = ls_box(t) ^ rco_tab[i];    \
+    t ^= E_KEY[6 * i];     E_KEY[6 * i + 6] = t;    \
+    t ^= E_KEY[6 * i + 1]; E_KEY[6 * i + 7] = t;    \
+    t ^= E_KEY[6 * i + 2]; E_KEY[6 * i + 8] = t;    \
+    t ^= E_KEY[6 * i + 3]; E_KEY[6 * i + 9] = t;    \
+    t ^= E_KEY[6 * i + 4]; E_KEY[6 * i + 10] = t;   \
+    t ^= E_KEY[6 * i + 5]; E_KEY[6 * i + 11] = t;   \
+}
+
+#define loop8(i)                                    \
+{   t = rotr(t,  8); ; t = ls_box(t) ^ rco_tab[i];  \
+    t ^= E_KEY[8 * i];     E_KEY[8 * i + 8] = t;    \
+    t ^= E_KEY[8 * i + 1]; E_KEY[8 * i + 9] = t;    \
+    t ^= E_KEY[8 * i + 2]; E_KEY[8 * i + 10] = t;   \
+    t ^= E_KEY[8 * i + 3]; E_KEY[8 * i + 11] = t;   \
+    t  = E_KEY[8 * i + 4] ^ ls_box(t);    \
+    E_KEY[8 * i + 12] = t;                \
+    t ^= E_KEY[8 * i + 5]; E_KEY[8 * i + 13] = t;   \
+    t ^= E_KEY[8 * i + 6]; E_KEY[8 * i + 14] = t;   \
+    t ^= E_KEY[8 * i + 7]; E_KEY[8 * i + 15] = t;   \
+}
+
+/* Tells whether the ACE is capable to generate
+   the extended key for a given key_len. */
+static inline int
+aes_hw_extkey_available(uint8_t key_len)
+{
+       /* TODO: We should check the actual CPU model/stepping
+                as it's possible that the capability will be
+                added in the next CPU revisions. */
+       if (key_len == 16)
+               return 1;
+       return 0;
+}
+
+static int
+aes_set_key(void *ctx_arg, const uint8_t *in_key, unsigned int key_len, uint32_t *flags)
+{
+       struct aes_ctx *ctx = ctx_arg;
+       uint32_t i, t, u, v, w;
+       uint32_t P[AES_EXTENDED_KEY_SIZE];
+       uint32_t rounds;
+
+       if (key_len != 16 && key_len != 24 && key_len != 32) {
+               *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+               return -EINVAL;
+       }
+
+       ctx->key_length = key_len;
+
+       ctx->E = ctx->e_data;
+       ctx->D = ctx->d_data;
+
+       /* Ensure 16-Bytes alignmentation of keys for VIA PadLock. */
+       if ((int)(ctx->e_data) & 0x0F)
+               ctx->E += 4 - (((int)(ctx->e_data) & 0x0F) / sizeof (ctx->e_data[0]));
+
+       if ((int)(ctx->d_data) & 0x0F)
+               ctx->D += 4 - (((int)(ctx->d_data) & 0x0F) / sizeof (ctx->d_data[0]));
+
+       E_KEY[0] = uint32_t_in (in_key);
+       E_KEY[1] = uint32_t_in (in_key + 4);
+       E_KEY[2] = uint32_t_in (in_key + 8);
+       E_KEY[3] = uint32_t_in (in_key + 12);
+
+       /* Don't generate extended keys if the hardware can do it. */
+       if (aes_hw_extkey_available(key_len))
+               return 0;
+
+       switch (key_len) {
+       case 16:
+               t = E_KEY[3];
+               for (i = 0; i < 10; ++i)
+                       loop4 (i);
+               break;
+
+       case 24:
+               E_KEY[4] = uint32_t_in (in_key + 16);
+               t = E_KEY[5] = uint32_t_in (in_key + 20);
+               for (i = 0; i < 8; ++i)
+                       loop6 (i);
+               break;
+
+       case 32:
+               E_KEY[4] = uint32_t_in (in_key + 16);
+               E_KEY[5] = uint32_t_in (in_key + 20);
+               E_KEY[6] = uint32_t_in (in_key + 24);
+               t = E_KEY[7] = uint32_t_in (in_key + 28);
+               for (i = 0; i < 7; ++i)
+                       loop8 (i);
+               break;
+       }
+
+       D_KEY[0] = E_KEY[0];
+       D_KEY[1] = E_KEY[1];
+       D_KEY[2] = E_KEY[2];
+       D_KEY[3] = E_KEY[3];
+
+       for (i = 4; i < key_len + 24; ++i) {
+               imix_col (D_KEY[i], E_KEY[i]);
+       }
+
+       /* PadLock needs a different format of the decryption key. */
+       rounds = 10 + (key_len - 16) / 4;
+
+       for (i = 0; i < rounds; i++) {
+               P[((i + 1) * 4) + 0] = D_KEY[((rounds - i - 1) * 4) + 0];
+               P[((i + 1) * 4) + 1] = D_KEY[((rounds - i - 1) * 4) + 1];
+               P[((i + 1) * 4) + 2] = D_KEY[((rounds - i - 1) * 4) + 2];
+               P[((i + 1) * 4) + 3] = D_KEY[((rounds - i - 1) * 4) + 3];
+       }
+
+       P[0] = E_KEY[(rounds * 4) + 0];
+       P[1] = E_KEY[(rounds * 4) + 1];
+       P[2] = E_KEY[(rounds * 4) + 2];
+       P[3] = E_KEY[(rounds * 4) + 3];
+
+       memcpy(D_KEY, P, AES_EXTENDED_KEY_SIZE_B);
+
+       return 0;
+}
+
+/* ====== Encryption/decryption routines ====== */
+
+/* This is the real call to PadLock. */
+static inline void
+padlock_xcrypt_ecb(uint8_t *input, uint8_t *output, uint8_t *key,
+                  void *control_word, uint32_t count)
+{
+       asm volatile ("pushfl; popfl");         /* enforce key reload. */
+       asm volatile (".byte 0xf3,0x0f,0xa7,0xc8"       /* rep xcryptecb */
+                     : "+S"(input), "+D"(output)
+                     : "d"(control_word), "b"(key), "c"(count));
+}
+
+static void
+aes_padlock(void *ctx_arg, uint8_t *out_arg, const uint8_t *in_arg, int encdec)
+{
+       /* Don't blindly modify this structure - the items must 
+          fit on 16-Bytes boundaries! */
+       struct padlock_xcrypt_data {
+               uint8_t buf[AES_BLOCK_SIZE];
+               union cword cword;
+       };
+
+       struct aes_ctx *ctx = ctx_arg;
+       char bigbuf[sizeof(struct padlock_xcrypt_data) + 16];
+       struct padlock_xcrypt_data *data;
+       void *key;
+
+       /* Place 'data' at the first 16-Bytes aligned address in 'bigbuf'. */
+       if (((long)bigbuf) & 0x0F)
+               data = (void*)(bigbuf + 16 - ((long)bigbuf & 0x0F));
+       else
+               data = (void*)bigbuf;
+
+       /* Prepare Control word. */
+       memset (data, 0, sizeof(struct padlock_xcrypt_data));
+       data->cword.b.encdec = !encdec; /* in the rest of cryptoapi ENC=1/DEC=0 */
+       data->cword.b.rounds = 10 + (ctx->key_length - 16) / 4;
+       data->cword.b.ksize = (ctx->key_length - 16) / 8;
+
+       /* Is the hardware capable to generate the extended key? */
+       if (!aes_hw_extkey_available(ctx->key_length))
+               data->cword.b.keygen = 1;
+
+       /* ctx->E starts with a plain key - if the hardware is capable
+          to generate the extended key itself we must supply
+          the plain key for both Encryption and Decryption. */
+       if (encdec == CRYPTO_DIR_ENCRYPT || data->cword.b.keygen == 0)
+               key = ctx->E;
+       else
+               key = ctx->D;
+       
+       memcpy(data->buf, in_arg, AES_BLOCK_SIZE);
+       padlock_xcrypt_ecb(data->buf, data->buf, key, &data->cword, 1);
+       memcpy(out_arg, data->buf, AES_BLOCK_SIZE);
+}
+
+static void
+aes_encrypt(void *ctx_arg, uint8_t *out, const uint8_t *in)
+{
+       aes_padlock(ctx_arg, out, in, CRYPTO_DIR_ENCRYPT);
+}
+
+static void
+aes_decrypt(void *ctx_arg, uint8_t *out, const uint8_t *in)
+{
+       aes_padlock(ctx_arg, out, in, CRYPTO_DIR_DECRYPT);
+}
+
+static struct crypto_alg aes_alg = {
+       .cra_name               =       "aes",
+       .cra_flags              =       CRYPTO_ALG_TYPE_CIPHER,
+       .cra_blocksize          =       AES_BLOCK_SIZE,
+       .cra_ctxsize            =       sizeof(struct aes_ctx),
+       .cra_module             =       THIS_MODULE,
+       .cra_list               =       LIST_HEAD_INIT(aes_alg.cra_list),
+       .cra_u                  =       {
+               .cipher = {
+                       .cia_min_keysize        =       AES_MIN_KEY_SIZE,
+                       .cia_max_keysize        =       AES_MAX_KEY_SIZE,
+                       .cia_setkey             =       aes_set_key,
+                       .cia_encrypt            =       aes_encrypt,
+                       .cia_decrypt            =       aes_decrypt
+               }
+       }
+};
+
+int __init padlock_init_aes(void)
+{
+       printk(KERN_NOTICE PFX "Using VIA PadLock ACE for AES algorithm.\n");
+
+       gen_tabs();
+       return crypto_register_alg(&aes_alg);
+}
+
+void __exit padlock_fini_aes(void)
+{
+       crypto_unregister_alg(&aes_alg);
+}
diff --git a/drivers/crypto/padlock-generic.c b/drivers/crypto/padlock-generic.c
new file mode 100644 (file)
index 0000000..18cf0e8
--- /dev/null
@@ -0,0 +1,63 @@
+/* 
+ * Cryptographic API.
+ *
+ * Support for VIA PadLock hardware crypto engine.
+ *
+ * Copyright (c) 2004  Michal Ludvig <michal@logix.cz>
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/crypto.h>
+#include <asm/byteorder.h>
+#include "padlock.h"
+
+static int __init
+padlock_init(void)
+{
+       int ret = -ENOSYS;
+       
+       if (!cpu_has_xcrypt) {
+               printk(KERN_ERR PFX "VIA PadLock not detected.\n");
+               return -ENODEV;
+       }
+
+       if (!cpu_has_xcrypt_enabled) {
+               printk(KERN_ERR PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n");
+               return -ENODEV;
+       }
+
+#ifdef CONFIG_CRYPTO_DEV_PADLOCK_AES
+       if ((ret = padlock_init_aes())) {
+               printk(KERN_ERR PFX "VIA PadLock AES initialization failed.\n");
+               return ret;
+       }
+#endif
+
+       if (ret == -ENOSYS)
+               printk(KERN_ERR PFX "Hmm, VIA PadLock was compiled without any algorithm.\n");
+
+       return ret;
+}
+
+static void __exit
+padlock_fini(void)
+{
+#ifdef CONFIG_CRYPTO_DEV_PADLOCK_AES
+       padlock_fini_aes();
+#endif
+}
+
+module_init(padlock_init);
+module_exit(padlock_fini);
+
+MODULE_DESCRIPTION("VIA PadLock crypto engine support.");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Michal Ludvig");
diff --git a/drivers/crypto/padlock.h b/drivers/crypto/padlock.h
new file mode 100644 (file)
index 0000000..7a50060
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Driver for VIA PadLock
+ *
+ * Copyright (c) 2004 Michal Ludvig <michal@logix.cz>
+ *
+ * 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 _CRYPTO_PADLOCK_H
+#define _CRYPTO_PADLOCK_H
+
+/* Control word. */
+union cword {
+       uint32_t cword[4];
+       struct {
+               int rounds:4;
+               int algo:3;
+               int keygen:1;
+               int interm:1;
+               int encdec:1;
+               int ksize:2;
+       } b;
+};
+
+#define PFX    "padlock: "
+
+#ifdef CONFIG_CRYPTO_DEV_PADLOCK_AES
+int padlock_init_aes(void);
+void padlock_fini_aes(void);
+#endif
+
+#endif /* _CRYPTO_PADLOCK_H */
diff --git a/drivers/dio/dio-driver.c b/drivers/dio/dio-driver.c
new file mode 100644 (file)
index 0000000..ffe6f44
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ *  DIO Driver Services
+ *
+ *  Copyright (C) 2004 Jochen Friedrich
+ *
+ *  Loosely based on drivers/pci/pci-driver.c and drivers/zorro/zorro-driver.c
+ *
+ *  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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/dio.h>
+
+
+       /**
+        *  dio_match_device - Tell if a DIO device structure has a matching
+        *                     DIO device id structure
+        *  @ids: array of DIO device id structures to search in
+        *  @dev: the DIO device structure to match against
+        *
+        *  Used by a driver to check whether a DIO device present in the
+        *  system is in its list of supported devices. Returns the matching
+        *  dio_device_id structure or %NULL if there is no match.
+        */
+
+const struct dio_device_id *
+dio_match_device(const struct dio_device_id *ids,
+                  const struct dio_dev *d)
+{
+       while (ids->id) {
+               if (ids->id == DIO_WILDCARD)
+                       return ids;
+               if (DIO_NEEDSSECID(ids->id & 0xff)) {
+                       if (ids->id == d->id)
+                               return ids;
+               } else {
+                       if ((ids->id & 0xff) == (d->id & 0xff))
+                               return ids;
+               }
+               ids++;
+       }
+       return NULL;
+}
+
+static int dio_device_probe(struct device *dev)
+{
+       int error = 0;
+       struct dio_driver *drv = to_dio_driver(dev->driver);
+       struct dio_dev *d = to_dio_dev(dev);
+
+       if (!d->driver && drv->probe) {
+               const struct dio_device_id *id;
+
+               id = dio_match_device(drv->id_table, d);
+               if (id)
+                       error = drv->probe(d, id);
+               if (error >= 0) {
+                       d->driver = drv;
+                       error = 0;
+               }
+       }
+       return error;
+}
+
+
+       /**
+        *  dio_register_driver - register a new DIO driver
+        *  @drv: the driver structure to register
+        *
+        *  Adds the driver structure to the list of registered drivers
+        *  Returns the number of DIO devices which were claimed by the driver
+        *  during registration.  The driver remains registered even if the
+        *  return value is zero.
+        */
+
+int dio_register_driver(struct dio_driver *drv)
+{
+       int count = 0;
+
+       /* initialize common driver fields */
+       drv->driver.name = drv->name;
+       drv->driver.bus = &dio_bus_type;
+       drv->driver.probe = dio_device_probe;
+
+       /* register with core */
+       count = driver_register(&drv->driver);
+       return count ? count : 1;
+}
+
+
+       /**
+        *  dio_unregister_driver - unregister a DIO driver
+        *  @drv: the driver structure to unregister
+        *
+        *  Deletes the driver structure from the list of registered DIO 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 dio_unregister_driver(struct dio_driver *drv)
+{
+       driver_unregister(&drv->driver);
+}
+
+
+       /**
+        *  dio_bus_match - Tell if a DIO device structure has a matching DIO
+        *                  device id structure
+        *  @ids: array of DIO device id structures to search in
+        *  @dev: the DIO device structure to match against
+        *
+        *  Used by a driver to check whether a DIO device present in the
+        *  system is in its list of supported devices. Returns the matching
+        *  dio_device_id structure or %NULL if there is no match.
+        */
+
+static int dio_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct dio_dev *d = to_dio_dev(dev);
+       struct dio_driver *dio_drv = to_dio_driver(drv);
+       const struct dio_device_id *ids = dio_drv->id_table;
+
+       if (!ids)
+               return 0;
+
+       while (ids->id) {
+               if (ids->id == DIO_WILDCARD)
+                       return 1;
+               if (DIO_NEEDSSECID(ids->id & 0xff)) {
+                       if (ids->id == d->id)
+                               return 1;
+               } else {
+                       if ((ids->id & 0xff) == (d->id & 0xff))
+                               return 1;
+               }
+               ids++;
+       }
+       return 0;
+}
+
+
+struct bus_type dio_bus_type = {
+       .name   = "dio",
+       .match  = dio_bus_match
+};
+
+
+static int __init dio_driver_init(void)
+{
+       return bus_register(&dio_bus_type);
+}
+
+postcore_initcall(dio_driver_init);
+
+EXPORT_SYMBOL(dio_match_device);
+EXPORT_SYMBOL(dio_register_driver);
+EXPORT_SYMBOL(dio_unregister_driver);
+EXPORT_SYMBOL(dio_dev_driver);
+EXPORT_SYMBOL(dio_bus_type);
diff --git a/drivers/dio/dio-sysfs.c b/drivers/dio/dio-sysfs.c
new file mode 100644 (file)
index 0000000..d30591f
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *  File Attributes for DIO Devices
+ *
+ *  Copyright (C) 2004 Jochen Friedrich
+ *
+ *  Loosely based on drivers/pci/pci-sysfs.c and drivers/zorro/zorro-sysfs.c
+ *
+ *  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 <linux/kernel.h>
+#include <linux/dio.h>
+#include <linux/stat.h>
+
+/* show configuration fields */
+
+static ssize_t dio_show_id(struct device *dev, char *buf)
+{
+       struct dio_dev *d;
+
+       d = to_dio_dev(dev);
+       return sprintf(buf, "0x%02x\n", (d->id & 0xff));
+}
+static DEVICE_ATTR(id, S_IRUGO, dio_show_id, NULL);
+
+static ssize_t dio_show_ipl(struct device *dev, char *buf)
+{
+       struct dio_dev *d;
+
+       d = to_dio_dev(dev);
+       return sprintf(buf, "0x%02x\n", d->ipl);
+}
+static DEVICE_ATTR(ipl, S_IRUGO, dio_show_ipl, NULL);
+
+static ssize_t dio_show_secid(struct device *dev, char *buf)
+{
+       struct dio_dev *d;
+
+       d = to_dio_dev(dev);
+       return sprintf(buf, "0x%02x\n", ((d->id >> 8)& 0xff));
+}
+static DEVICE_ATTR(secid, S_IRUGO, dio_show_secid, NULL);
+
+static ssize_t dio_show_name(struct device *dev, char *buf)
+{
+       struct dio_dev *d;
+
+       d = to_dio_dev(dev);
+       return sprintf(buf, "%s\n", d->name);
+}
+static DEVICE_ATTR(name, S_IRUGO, dio_show_name, NULL);
+
+static ssize_t dio_show_resource(struct device *dev, char *buf)
+{
+       struct dio_dev *d = to_dio_dev(dev);
+
+       return sprintf(buf, "0x%08lx 0x%08lx 0x%08lx\n",
+                      dio_resource_start(d), dio_resource_end(d),
+                      dio_resource_flags(d));
+}
+static DEVICE_ATTR(resource, S_IRUGO, dio_show_resource, NULL);
+
+void dio_create_sysfs_dev_files(struct dio_dev *d)
+{
+       struct device *dev = &d->dev;
+
+       /* current configuration's attributes */
+       device_create_file(dev, &dev_attr_id);
+       device_create_file(dev, &dev_attr_ipl);
+       device_create_file(dev, &dev_attr_secid);
+       device_create_file(dev, &dev_attr_name);
+       device_create_file(dev, &dev_attr_resource);
+}
+
diff --git a/drivers/i2c/algos/i2c-algo-sgi.c b/drivers/i2c/algos/i2c-algo-sgi.c
new file mode 100644 (file)
index 0000000..b4e0a06
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * i2c-algo-sgi.c: i2c driver algorithms for SGI adapters.
+ * 
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-sgi.h>
+
+
+#define SGI_I2C_FORCE_IDLE     (0 << 0)
+#define SGI_I2C_NOT_IDLE       (1 << 0)
+#define SGI_I2C_WRITE          (0 << 1)
+#define SGI_I2C_READ           (1 << 1)
+#define SGI_I2C_RELEASE_BUS    (0 << 2)
+#define SGI_I2C_HOLD_BUS       (1 << 2)
+#define SGI_I2C_XFER_DONE      (0 << 4)
+#define SGI_I2C_XFER_BUSY      (1 << 4)
+#define SGI_I2C_ACK            (0 << 5)
+#define SGI_I2C_NACK           (1 << 5)
+#define SGI_I2C_BUS_OK         (0 << 7)
+#define SGI_I2C_BUS_ERR                (1 << 7)
+
+#define get_control()          adap->getctrl(adap->data)
+#define set_control(val)       adap->setctrl(adap->data, val)
+#define read_data()            adap->rdata(adap->data)
+#define write_data(val)                adap->wdata(adap->data, val)
+
+
+static int wait_xfer_done(struct i2c_algo_sgi_data *adap)
+{
+       int i;
+
+       for (i = 0; i < adap->xfer_timeout; i++) {
+               if ((get_control() & SGI_I2C_XFER_BUSY) == 0)
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int wait_ack(struct i2c_algo_sgi_data *adap)
+{
+       int i;
+
+       if (wait_xfer_done(adap))
+               return -ETIMEDOUT;
+       for (i = 0; i < adap->ack_timeout; i++) {
+               if ((get_control() & SGI_I2C_NACK) == 0)
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int force_idle(struct i2c_algo_sgi_data *adap)
+{
+       int i;
+
+       set_control(SGI_I2C_FORCE_IDLE);
+       for (i = 0; i < adap->xfer_timeout; i++) {
+               if ((get_control() & SGI_I2C_NOT_IDLE) == 0)
+                       goto out;
+               udelay(1);
+       }
+       return -ETIMEDOUT;
+out:
+       if (get_control() & SGI_I2C_BUS_ERR)
+               return -EIO;
+       return 0;
+}
+
+static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr,
+                     int rd)
+{
+       if (rd)
+               set_control(SGI_I2C_NOT_IDLE);
+       /* Check if bus is idle, eventually force it to do so */
+       if (get_control() & SGI_I2C_NOT_IDLE)
+               if (force_idle(adap))
+                       return -EIO;
+       /* Write out the i2c chip address and specify operation */
+       set_control(SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE);
+       if (rd)
+               addr |= 1;
+       write_data(addr);
+       if (wait_ack(adap))
+               return -EIO;
+       return 0;
+}
+
+static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf,
+                   unsigned int len)
+{
+       int i;
+
+       set_control(SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE);
+       for (i = 0; i < len; i++) {
+               if (wait_xfer_done(adap))
+                       return -EIO;
+               buf[i] = read_data();
+       }
+       set_control(SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE);
+
+       return 0;
+
+}
+
+static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf,
+                    unsigned int len)
+{
+       int i;
+
+       /* We are already in write state */
+       for (i = 0; i < len; i++) {
+               write_data(buf[i]);
+               if (wait_ack(adap))
+                       return -EIO;
+       }
+       return 0;
+}
+
+static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[],
+                   int num)
+{
+       struct i2c_algo_sgi_data *adap = i2c_adap->algo_data;
+       struct i2c_msg *p;
+       int i, err = 0;
+
+       for (i = 0; !err && i < num; i++) {
+               p = &msgs[i];
+               err = do_address(adap, p->addr, p->flags & I2C_M_RD);
+               if (err || !p->len)
+                       continue;
+               if (p->flags & I2C_M_RD)
+                       err = i2c_read(adap, p->buf, p->len);
+               else
+                       err = i2c_write(adap, p->buf, p->len);
+       }
+
+       return err;
+}
+
+static u32 sgi_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm sgi_algo = {
+       .name           = "SGI algorithm",
+       .id             = I2C_ALGO_SGI,
+       .master_xfer    = sgi_xfer,
+       .functionality  = sgi_func,
+};
+
+/* 
+ * registering functions to load algorithms at runtime 
+ */
+int i2c_sgi_add_bus(struct i2c_adapter *adap)
+{
+       adap->id |= sgi_algo.id;
+       adap->algo = &sgi_algo;
+
+       return i2c_add_adapter(adap);
+}
+
+
+int i2c_sgi_del_bus(struct i2c_adapter *adap)
+{
+       return i2c_del_adapter(adap);
+}
+
+EXPORT_SYMBOL(i2c_sgi_add_bus);
+EXPORT_SYMBOL(i2c_sgi_del_bus);
+
+MODULE_AUTHOR("Ladislav Michl <ladis@linux-mips.org>");
+MODULE_DESCRIPTION("I2C-Bus SGI algorithm");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/algos/i2c-algo-sibyte.c b/drivers/i2c/algos/i2c-algo-sibyte.c
new file mode 100644 (file)
index 0000000..ea1a904
--- /dev/null
@@ -0,0 +1,225 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-algo-sibyte.c i2c driver algorithms for bit-shift adapters                   */
+/* ------------------------------------------------------------------------- */
+/*   Copyright (C) 2001,2002,2003 Broadcom Corporation
+     Copyright (C) 1995-2000 Simon G. Vogl
+
+    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.               */
+/* ------------------------------------------------------------------------- */
+
+/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
+   Frodo Looijaard <frodol@dds.nl>.  */
+
+/* Ported for SiByte SOCs by Broadcom Corporation.  */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_smbus.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-sibyte.h>
+
+/* ----- global defines ----------------------------------------------- */
+#define SMB_CSR(a,r) ((long)(a->reg_base + r))
+
+/* ----- global variables ---------------------------------------------        */
+
+/* module parameters:
+ */
+static int bit_scan=0; /* have a look at what's hanging 'round         */
+
+
+static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr, 
+                      unsigned short flags, char read_write,
+                      u8 command, int size, union i2c_smbus_data * data)
+{
+       struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+        int data_bytes = 0;
+        int error;
+
+        while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
+                ;
+
+        switch (size) {
+        case I2C_SMBUS_QUICK:
+                csr_out32((V_SMB_ADDR(addr) | (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
+                          V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START));
+                break;
+        case I2C_SMBUS_BYTE:
+                if (read_write == I2C_SMBUS_READ) {
+                        csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE),
+                                 SMB_CSR(adap, R_SMB_START));
+                        data_bytes = 1;
+                } else {
+                        csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+                        csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE),
+                                 SMB_CSR(adap, R_SMB_START));
+                }
+                break;
+        case I2C_SMBUS_BYTE_DATA:
+                csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+                if (read_write == I2C_SMBUS_READ) {
+                        csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE),
+                                 SMB_CSR(adap, R_SMB_START));
+                        data_bytes = 1;
+                } else {
+                        csr_out32(V_SMB_LB(data->byte), SMB_CSR(adap, R_SMB_DATA));
+                        csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
+                                 SMB_CSR(adap, R_SMB_START));
+                }
+                break;
+        case I2C_SMBUS_WORD_DATA:
+                csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+                if (read_write == I2C_SMBUS_READ) {
+                        csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE),
+                                 SMB_CSR(adap, R_SMB_START));
+                        data_bytes = 2;
+                } else {
+                        csr_out32(V_SMB_LB(data->word & 0xff), SMB_CSR(adap, R_SMB_DATA));
+                        csr_out32(V_SMB_MB(data->word >> 8), SMB_CSR(adap, R_SMB_DATA));
+                        csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
+                                 SMB_CSR(adap, R_SMB_START));
+                }
+                break;
+        default:
+                return -1;      /* XXXKW better error code? */
+        }
+
+        while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
+                ;
+
+        error = csr_in32(SMB_CSR(adap, R_SMB_STATUS));
+        if (error & M_SMB_ERROR) {
+                /* Clear error bit by writing a 1 */
+                csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS));
+                return -1;      /* XXXKW better error code? */
+        }
+
+        if (data_bytes == 1)
+                data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff;
+        if (data_bytes == 2)
+                data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff;
+
+        return 0;
+}
+
+static int algo_control(struct i2c_adapter *adapter, 
+       unsigned int cmd, unsigned long arg)
+{
+       return 0;
+}
+
+static u32 bit_func(struct i2c_adapter *adap)
+{
+       return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+                I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
+}
+
+
+/* -----exported algorithm data: ------------------------------------- */
+
+static struct i2c_algorithm i2c_sibyte_algo = {
+       "SiByte algorithm",
+       I2C_ALGO_SIBYTE,
+       NULL,                           /* master_xfer          */
+       smbus_xfer,                     /* smbus_xfer           */
+       NULL,                           /* slave_xmit           */
+       NULL,                           /* slave_recv           */
+       algo_control,                   /* ioctl                */
+       bit_func,                       /* functionality        */
+};
+
+/* 
+ * registering functions to load algorithms at runtime 
+ */
+int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed)
+{
+       int i;
+       struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+
+       /* register new adapter to i2c module... */
+
+       i2c_adap->id |= i2c_sibyte_algo.id;
+       i2c_adap->algo = &i2c_sibyte_algo;
+        
+        /* Set the frequency to 100 kHz */
+        csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ));
+        csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL));
+
+       /* scan bus */
+       if (bit_scan) {
+                union i2c_smbus_data data;
+                int rc;
+               printk(KERN_INFO " i2c-algo-sibyte.o: scanning bus %s.\n",
+                      i2c_adap->name);
+               for (i = 0x00; i < 0x7f; i++) {
+                        /* XXXKW is this a realistic probe? */
+                        rc = smbus_xfer(i2c_adap, i, 0, I2C_SMBUS_READ, 0,
+                                        I2C_SMBUS_BYTE_DATA, &data);
+                       if (!rc) {
+                               printk("(%02x)",i); 
+                       } else 
+                               printk("."); 
+               }
+               printk("\n");
+       }
+
+       i2c_add_adapter(i2c_adap);
+
+       return 0;
+}
+
+
+int i2c_sibyte_del_bus(struct i2c_adapter *adap)
+{
+       int res;
+
+       if ((res = i2c_del_adapter(adap)) < 0)
+               return res;
+
+       return 0;
+}
+
+int __init i2c_algo_sibyte_init (void)
+{
+       printk("i2c-algo-sibyte.o: i2c SiByte algorithm module\n");
+       return 0;
+}
+
+
+EXPORT_SYMBOL(i2c_sibyte_add_bus);
+EXPORT_SYMBOL(i2c_sibyte_del_bus);
+
+#ifdef MODULE
+MODULE_AUTHOR("Kip Walker, Broadcom Corp.");
+MODULE_DESCRIPTION("SiByte I2C-Bus algorithm");
+MODULE_PARM(bit_scan, "i");
+MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus");
+MODULE_LICENSE("GPL");
+
+int init_module(void) 
+{
+       return i2c_algo_sibyte_init();
+}
+
+void cleanup_module(void) 
+{
+}
+#endif
diff --git a/drivers/i2c/busses/i2c-amd756-s4882.c b/drivers/i2c/busses/i2c-amd756-s4882.c
new file mode 100644 (file)
index 0000000..4e553e8
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard
+ *
+ * Copyright (C) 2004 Jean Delvare <khali@linux-fr.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.
+ *
+ * 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.
+ */
+/*
+ * We select the channels by sending commands to the Philips
+ * PCA9556 chip at I2C address 0x18. The main adapter is used for
+ * the non-multiplexed part of the bus, and 4 virtual adapters
+ * are defined for the multiplexed addresses: 0x50-0x53 (memory
+ * module EEPROM) located on channels 1-4, and 0x4c (LM63)
+ * located on multiplexed channels 0 and 5-7. We define one
+ * virtual adapter per CPU, which corresponds to two multiplexed
+ * channels:
+ *   CPU0: virtual adapter 1, channels 1 and 0
+ *   CPU1: virtual adapter 2, channels 2 and 5
+ *   CPU2: virtual adapter 3, channels 3 and 6
+ *   CPU3: virtual adapter 4, channels 4 and 7
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+
+extern struct i2c_adapter amd756_smbus;
+
+static struct i2c_adapter *s4882_adapter;
+static struct i2c_algorithm *s4882_algo;
+
+/* Wrapper access functions for multiplexed SMBus */
+static struct semaphore amd756_lock;
+
+static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
+                              unsigned short flags, char read_write,
+                              u8 command, int size,
+                              union i2c_smbus_data * data)
+{
+       int error;
+
+       /* We exclude the multiplexed addresses */
+       if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
+        || addr == 0x18)
+               return -1;
+
+       down(&amd756_lock);
+
+       error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
+                                             command, size, data);
+
+       up(&amd756_lock);
+
+       return error;
+}
+
+/* We remember the last used channels combination so as to only switch
+   channels when it is really needed. This greatly reduces the SMBus
+   overhead, but also assumes that nobody will be writing to the PCA9556
+   in our back. */
+static u8 last_channels;
+
+static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
+                                       unsigned short flags, char read_write,
+                                       u8 command, int size,
+                                       union i2c_smbus_data * data,
+                                       u8 channels)
+{
+       int error;
+
+       /* We exclude the non-multiplexed addresses */
+       if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
+               return -1;
+
+       down(&amd756_lock);
+
+       if (last_channels != channels) {
+               union i2c_smbus_data mplxdata;
+               mplxdata.byte = channels;
+
+               error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0,
+                                                     I2C_SMBUS_WRITE, 0x01,
+                                                     I2C_SMBUS_BYTE_DATA,
+                                                     &mplxdata);
+               if (error)
+                       goto UNLOCK;
+               last_channels = channels;
+       }
+       error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
+                                             command, size, data);
+
+UNLOCK:
+       up(&amd756_lock);
+       return error;
+}
+
+static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr,
+                              unsigned short flags, char read_write,
+                              u8 command, int size,
+                              union i2c_smbus_data * data)
+{
+       /* CPU0: channels 1 and 0 enabled */
+       return amd756_access_channel(adap, addr, flags, read_write, command,
+                                    size, data, 0x03);
+}
+
+static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr,
+                              unsigned short flags, char read_write,
+                              u8 command, int size,
+                              union i2c_smbus_data * data)
+{
+       /* CPU1: channels 2 and 5 enabled */
+       return amd756_access_channel(adap, addr, flags, read_write, command,
+                                    size, data, 0x24);
+}
+
+static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr,
+                              unsigned short flags, char read_write,
+                              u8 command, int size,
+                              union i2c_smbus_data * data)
+{
+       /* CPU2: channels 3 and 6 enabled */
+       return amd756_access_channel(adap, addr, flags, read_write, command,
+                                    size, data, 0x48);
+}
+
+static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr,
+                              unsigned short flags, char read_write,
+                              u8 command, int size,
+                              union i2c_smbus_data * data)
+{
+       /* CPU3: channels 4 and 7 enabled */
+       return amd756_access_channel(adap, addr, flags, read_write, command,
+                                    size, data, 0x90);
+}
+
+static int __init amd756_s4882_init(void)
+{
+       int i, error;
+       union i2c_smbus_data ioconfig;
+
+       /* Unregister physical bus */
+       error = i2c_del_adapter(&amd756_smbus);
+       if (error) {
+               if (error == -EINVAL)
+                       error = -ENODEV;
+               else
+                       dev_err(&amd756_smbus.dev, "Physical bus removal "
+                               "failed\n");
+               goto ERROR0;
+       }
+
+       printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
+       init_MUTEX(&amd756_lock);
+
+       /* Define the 5 virtual adapters and algorithms structures */
+       if (!(s4882_adapter = kmalloc(5 * sizeof(struct i2c_adapter),
+                                     GFP_KERNEL))) {
+               error = -ENOMEM;
+               goto ERROR1;
+       }
+       if (!(s4882_algo = kmalloc(5 * sizeof(struct i2c_algorithm),
+                                  GFP_KERNEL))) {
+               error = -ENOMEM;
+               goto ERROR2;
+       }
+
+       /* Fill in the new structures */
+       s4882_algo[0] = *(amd756_smbus.algo);
+       s4882_algo[0].smbus_xfer = amd756_access_virt0;
+       s4882_adapter[0] = amd756_smbus;
+       s4882_adapter[0].algo = s4882_algo;
+       for (i = 1; i < 5; i++) {
+               s4882_algo[i] = *(amd756_smbus.algo);
+               s4882_adapter[i] = amd756_smbus;
+               sprintf(s4882_adapter[i].name,
+                       "SMBus 8111 adapter (CPU%d)", i-1);
+               s4882_adapter[i].algo = s4882_algo+i;
+       }
+       s4882_algo[1].smbus_xfer = amd756_access_virt1;
+       s4882_algo[2].smbus_xfer = amd756_access_virt2;
+       s4882_algo[3].smbus_xfer = amd756_access_virt3;
+       s4882_algo[4].smbus_xfer = amd756_access_virt4;
+
+       /* Configure the PCA9556 multiplexer */
+       ioconfig.byte = 0x00; /* All I/O to output mode */
+       error = amd756_smbus.algo->smbus_xfer(&amd756_smbus, 0x18, 0,
+                                             I2C_SMBUS_WRITE, 0x03,
+                                             I2C_SMBUS_BYTE_DATA, &ioconfig);
+       if (error) {
+               dev_err(&amd756_smbus.dev, "PCA9556 configuration failed\n");
+               error = -EIO;
+               goto ERROR3;
+       }
+
+       /* Register virtual adapters */
+       for (i = 0; i < 5; i++) {
+               error = i2c_add_adapter(s4882_adapter+i);
+               if (error) {
+                       dev_err(&amd756_smbus.dev,
+                              "Virtual adapter %d registration "
+                              "failed, module not inserted\n", i);
+                       for (i--; i >= 0; i--)
+                               i2c_del_adapter(s4882_adapter+i);
+                       goto ERROR3;
+               }
+       }
+
+       return 0;
+
+ERROR3:
+       kfree(s4882_algo);
+       s4882_algo = NULL;
+ERROR2:
+       kfree(s4882_adapter);
+       s4882_adapter = NULL;
+ERROR1:
+       i2c_del_adapter(&amd756_smbus);
+ERROR0:
+       return error;
+}
+
+static void __exit amd756_s4882_exit(void)
+{
+       if (s4882_adapter) {
+               int i;
+
+               for (i = 0; i < 5; i++)
+                       i2c_del_adapter(s4882_adapter+i);
+               kfree(s4882_adapter);
+               s4882_adapter = NULL;
+       }
+       if (s4882_algo) {
+               kfree(s4882_algo);
+               s4882_algo = NULL;
+       }
+
+       /* Restore physical bus */
+       if (i2c_add_adapter(&amd756_smbus))
+               dev_err(&amd756_smbus.dev, "Physical bus restoration "
+                       "failed\n");
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("S4882 SMBus multiplexing");
+MODULE_LICENSE("GPL");
+
+module_init(amd756_s4882_init);
+module_exit(amd756_s4882_exit);
diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c
new file mode 100644 (file)
index 0000000..c43353a
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * i2c-au1550.c: SMBus (i2c) adapter for Alchemy PSC interface
+ * Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com>
+ *
+ * 2.6 port by Matt Porter <mporter@kernel.crashing.org>
+ *
+ * The documentation describes this as an SMBus controller, but it doesn't
+ * understand any of the SMBus protocol in hardware.  It's really an I2C
+ * controller that could emulate most of the SMBus in software.
+ *
+ * This is just a skeleton adapter to use with the Au1550 PSC
+ * algorithm.  It was developed for the Pb1550, but will work with
+ * any Au1550 board that has a similar PSC configuration.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-pb1x00/pb1550.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+
+#include "i2c-au1550.h"
+
+static int
+wait_xfer_done(struct i2c_au1550_data *adap)
+{
+       u32     stat;
+       int     i;
+       volatile psc_smb_t      *sp;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       /* Wait for Tx FIFO Underflow.
+       */
+       for (i = 0; i < adap->xfer_timeout; i++) {
+               stat = sp->psc_smbevnt;
+               au_sync();
+               if ((stat & PSC_SMBEVNT_TU) != 0) {
+                       /* Clear it.  */
+                       sp->psc_smbevnt = PSC_SMBEVNT_TU;
+                       au_sync();
+                       return 0;
+               }
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int
+wait_ack(struct i2c_au1550_data *adap)
+{
+       u32     stat;
+       volatile psc_smb_t      *sp;
+
+       if (wait_xfer_done(adap))
+               return -ETIMEDOUT;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       stat = sp->psc_smbevnt;
+       au_sync();
+
+       if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int
+wait_master_done(struct i2c_au1550_data *adap)
+{
+       u32     stat;
+       int     i;
+       volatile psc_smb_t      *sp;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       /* Wait for Master Done.
+       */
+       for (i = 0; i < adap->xfer_timeout; i++) {
+               stat = sp->psc_smbevnt;
+               au_sync();
+               if ((stat & PSC_SMBEVNT_MD) != 0)
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int
+do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd)
+{
+       volatile psc_smb_t      *sp;
+       u32                     stat;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       /* Reset the FIFOs, clear events.
+       */
+       sp->psc_smbpcr = PSC_SMBPCR_DC;
+       sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
+       au_sync();
+       do {
+               stat = sp->psc_smbpcr;
+               au_sync();
+       } while ((stat & PSC_SMBPCR_DC) != 0);
+
+       /* Write out the i2c chip address and specify operation
+       */
+       addr <<= 1;
+       if (rd)
+               addr |= 1;
+
+       /* Put byte into fifo, start up master.
+       */
+       sp->psc_smbtxrx = addr;
+       au_sync();
+       sp->psc_smbpcr = PSC_SMBPCR_MS;
+       au_sync();
+       if (wait_ack(adap))
+               return -EIO;
+       return 0;
+}
+
+static u32
+wait_for_rx_byte(struct i2c_au1550_data *adap, u32 *ret_data)
+{
+       int     j;
+       u32     data, stat;
+       volatile psc_smb_t      *sp;
+
+       if (wait_xfer_done(adap))
+               return -EIO;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       j =  adap->xfer_timeout * 100;
+       do {
+               j--;
+               if (j <= 0)
+                       return -EIO;
+
+               stat = sp->psc_smbstat;
+               au_sync();
+               if ((stat & PSC_SMBSTAT_RE) == 0)
+                       j = 0;
+               else
+                       udelay(1);
+       } while (j > 0);
+       data = sp->psc_smbtxrx;
+       au_sync();
+       *ret_data = data;
+
+       return 0;
+}
+
+static int
+i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
+                   unsigned int len)
+{
+       int     i;
+       u32     data;
+       volatile psc_smb_t      *sp;
+
+       if (len == 0)
+               return 0;
+
+       /* A read is performed by stuffing the transmit fifo with
+        * zero bytes for timing, waiting for bytes to appear in the
+        * receive fifo, then reading the bytes.
+        */
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       i = 0;
+       while (i < (len-1)) {
+               sp->psc_smbtxrx = 0;
+               au_sync();
+               if (wait_for_rx_byte(adap, &data))
+                       return -EIO;
+
+               buf[i] = data;
+               i++;
+       }
+
+       /* The last byte has to indicate transfer done.
+       */
+       sp->psc_smbtxrx = PSC_SMBTXRX_STP;
+       au_sync();
+       if (wait_master_done(adap))
+               return -EIO;
+
+       data = sp->psc_smbtxrx;
+       au_sync();
+       buf[i] = data;
+       return 0;
+}
+
+static int
+i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
+                    unsigned int len)
+{
+       int     i;
+       u32     data;
+       volatile psc_smb_t      *sp;
+
+       if (len == 0)
+               return 0;
+
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+
+       i = 0;
+       while (i < (len-1)) {
+               data = buf[i];
+               sp->psc_smbtxrx = data;
+               au_sync();
+               if (wait_ack(adap))
+                       return -EIO;
+               i++;
+       }
+
+       /* The last byte has to indicate transfer done.
+       */
+       data = buf[i];
+       data |= PSC_SMBTXRX_STP;
+       sp->psc_smbtxrx = data;
+       au_sync();
+       if (wait_master_done(adap))
+               return -EIO;
+       return 0;
+}
+
+static int
+au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+       struct i2c_au1550_data *adap = i2c_adap->algo_data;
+       struct i2c_msg *p;
+       int i, err = 0;
+
+       for (i = 0; !err && i < num; i++) {
+               p = &msgs[i];
+               err = do_address(adap, p->addr, p->flags & I2C_M_RD);
+               if (err || !p->len)
+                       continue;
+               if (p->flags & I2C_M_RD)
+                       err = i2c_read(adap, p->buf, p->len);
+               else
+                       err = i2c_write(adap, p->buf, p->len);
+       }
+
+       /* Return the number of messages processed, or the error code.
+       */
+       if (err == 0)
+               err = num;
+       return err;
+}
+
+static u32
+au1550_func(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm au1550_algo = {
+       .name           = "Au1550 algorithm",
+       .id             = I2C_ALGO_AU1550,
+       .master_xfer    = au1550_xfer,
+       .functionality  = au1550_func,
+};
+
+/*
+ * registering functions to load algorithms at runtime
+ * Prior to calling us, the 50MHz clock frequency and routing
+ * must have been set up for the PSC indicated by the adapter.
+ */
+int
+i2c_au1550_add_bus(struct i2c_adapter *i2c_adap)
+{
+       struct i2c_au1550_data *adap = i2c_adap->algo_data;
+       volatile psc_smb_t      *sp;
+       u32     stat;
+
+       i2c_adap->algo = &au1550_algo;
+
+       /* Now, set up the PSC for SMBus PIO mode.
+       */
+       sp = (volatile psc_smb_t *)(adap->psc_base);
+       sp->psc_ctrl = PSC_CTRL_DISABLE;
+       au_sync();
+       sp->psc_sel = PSC_SEL_PS_SMBUSMODE;
+       sp->psc_smbcfg = 0;
+       au_sync();
+       sp->psc_ctrl = PSC_CTRL_ENABLE;
+       au_sync();
+       do {
+               stat = sp->psc_smbstat;
+               au_sync();
+       } while ((stat & PSC_SMBSTAT_SR) == 0);
+
+       sp->psc_smbcfg = (PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 |
+                               PSC_SMBCFG_DD_DISABLE);
+
+       /* Divide by 8 to get a 6.25 MHz clock.  The later protocol
+        * timings are based on this clock.
+        */
+       sp->psc_smbcfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8);
+       sp->psc_smbmsk = PSC_SMBMSK_ALLMASK;
+       au_sync();
+
+       /* Set the protocol timer values.  See Table 71 in the
+        * Au1550 Data Book for standard timing values.
+        */
+       sp->psc_smbtmr = PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(15) | \
+               PSC_SMBTMR_SET_PU(15) | PSC_SMBTMR_SET_SH(15) | \
+               PSC_SMBTMR_SET_SU(15) | PSC_SMBTMR_SET_CL(15) | \
+               PSC_SMBTMR_SET_CH(15);
+       au_sync();
+
+       sp->psc_smbcfg |= PSC_SMBCFG_DE_ENABLE;
+       do {
+               stat = sp->psc_smbstat;
+               au_sync();
+       } while ((stat & PSC_SMBSTAT_DR) == 0);
+
+       return i2c_add_adapter(i2c_adap);
+}
+
+
+int
+i2c_au1550_del_bus(struct i2c_adapter *adap)
+{
+       return i2c_del_adapter(adap);
+}
+
+static int
+pb1550_reg(struct i2c_client *client)
+{
+       return 0;
+}
+
+static int
+pb1550_unreg(struct i2c_client *client)
+{
+       return 0;
+}
+
+static struct i2c_au1550_data pb1550_i2c_info = {
+       SMBUS_PSC_BASE, 200, 200
+};
+
+static struct i2c_adapter pb1550_board_adapter = {
+       name:              "pb1550 adapter",
+       id:                I2C_HW_AU1550_PSC,
+       algo:              NULL,
+       algo_data:         &pb1550_i2c_info,
+       client_register:   pb1550_reg,
+       client_unregister: pb1550_unreg,
+};
+
+/* BIG hack to support the control interface on the Wolfson WM8731
+ * audio codec on the Pb1550 board.  We get an address and two data
+ * bytes to write, create an i2c message, and send it across the
+ * i2c transfer function.  We do this here because we have access to
+ * the i2c adapter structure.
+ */
+static struct i2c_msg wm_i2c_msg;  /* We don't want this stuff on the stack */
+static u8 i2cbuf[2];
+
+int
+pb1550_wm_codec_write(u8 addr, u8 reg, u8 val)
+{
+       wm_i2c_msg.addr = addr;
+       wm_i2c_msg.flags = 0;
+       wm_i2c_msg.buf = i2cbuf;
+       wm_i2c_msg.len = 2;
+       i2cbuf[0] = reg;
+       i2cbuf[1] = val;
+
+       return pb1550_board_adapter.algo->master_xfer(&pb1550_board_adapter, &wm_i2c_msg, 1);
+}
+
+static int __init
+i2c_au1550_init(void)
+{
+       printk(KERN_INFO "Au1550 I2C: ");
+
+       /* This is where we would set up a 50MHz clock source
+        * and routing.  On the Pb1550, the SMBus is PSC2, which
+        * uses a shared clock with USB.  This has been already
+        * configured by Yamon as a 48MHz clock, close enough
+        * for our work.
+        */
+        if (i2c_au1550_add_bus(&pb1550_board_adapter) < 0) {
+               printk("failed to initialize.\n");
+                return -ENODEV;
+       }
+
+       printk("initialized.\n");
+       return 0;
+}
+
+static void __exit
+i2c_au1550_exit(void)
+{
+       i2c_au1550_del_bus(&pb1550_board_adapter);
+}
+
+MODULE_AUTHOR("Dan Malek, Embedded Edge, LLC.");
+MODULE_DESCRIPTION("SMBus adapter Alchemy pb1550");
+MODULE_LICENSE("GPL");
+
+module_init (i2c_au1550_init);
+module_exit (i2c_au1550_exit);
diff --git a/drivers/i2c/busses/i2c-au1550.h b/drivers/i2c/busses/i2c-au1550.h
new file mode 100644 (file)
index 0000000..fce15d1
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com>
+ * 2.6 port by Matt Porter <mporter@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.
+ *
+ *  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 I2C_AU1550_H
+#define I2C_AU1550_H
+
+struct i2c_au1550_data {
+       u32     psc_base;
+       int     xfer_timeout;
+       int     ack_timeout;
+};
+
+int i2c_au1550_add_bus(struct i2c_adapter *);
+int i2c_au1550_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_AU1550_H */
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
new file mode 100644 (file)
index 0000000..9cb6974
--- /dev/null
@@ -0,0 +1,926 @@
+/* linux/drivers/i2c/busses/i2c-s3c2410.c
+ *
+ * Copyright (C) 2004 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 I2C Controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <asm/hardware/clock.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-iic.h>
+#include <asm/arch/iic.h>
+
+/* i2c controller state */
+
+enum s3c24xx_i2c_state {
+       STATE_IDLE,
+       STATE_START,
+       STATE_READ,
+       STATE_WRITE,
+       STATE_STOP
+};
+
+struct s3c24xx_i2c {
+       spinlock_t              lock;
+       wait_queue_head_t       wait;
+
+       struct i2c_msg          *msg;
+       unsigned int            msg_num;
+       unsigned int            msg_idx;
+       unsigned int            msg_ptr;
+
+       enum s3c24xx_i2c_state  state;
+
+       void __iomem            *regs;
+       struct clk              *clk;
+       struct device           *dev;
+       struct resource         *irq;
+       struct resource         *ioarea;
+       struct i2c_adapter      adap;
+};
+
+/* default platform data to use if not supplied in the platform_device
+*/
+
+static struct s3c2410_platform_i2c s3c24xx_i2c_default_platform = {
+       .flags          = 0,
+       .slave_addr     = 0x10,
+       .bus_freq       = 100*1000,
+       .max_freq       = 400*1000,
+       .sda_delay      = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
+};
+
+/* s3c24xx_i2c_is2440()
+ *
+ * return true is this is an s3c2440
+*/
+
+static inline int s3c24xx_i2c_is2440(struct s3c24xx_i2c *i2c)
+{
+       struct platform_device *pdev = to_platform_device(i2c->dev);
+
+       return !strcmp(pdev->name, "s3c2440-i2c");
+}
+
+
+/* s3c24xx_i2c_get_platformdata
+ *
+ * get the platform data associated with the given device, or return
+ * the default if there is none
+*/
+
+static inline struct s3c2410_platform_i2c *s3c24xx_i2c_get_platformdata(struct device *dev)
+{
+       if (dev->platform_data != NULL)
+               return (struct s3c2410_platform_i2c *)dev->platform_data;
+
+       return &s3c24xx_i2c_default_platform;
+}
+
+/* s3c24xx_i2c_master_complete
+ *
+ * complete the message and wake up the caller, using the given return code,
+ * or zero to mean ok.
+*/
+
+static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret)
+{
+       dev_dbg(i2c->dev, "master_complete %d\n", ret);
+
+       i2c->msg_ptr = 0;
+       i2c->msg = NULL;
+       i2c->msg_idx ++;
+       i2c->msg_num = 0;
+       if (ret)
+               i2c->msg_idx = ret;
+
+       wake_up(&i2c->wait);
+}
+
+static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c)
+{
+       unsigned long tmp;
+       
+       tmp = readl(i2c->regs + S3C2410_IICCON);
+       writel(tmp & ~S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);
+
+}
+
+static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c)
+{
+       unsigned long tmp;
+       
+       tmp = readl(i2c->regs + S3C2410_IICCON);
+       writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);
+
+}
+
+/* irq enable/disable functions */
+
+static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c)
+{
+       unsigned long tmp;
+       
+       tmp = readl(i2c->regs + S3C2410_IICCON);
+       writel(tmp & ~S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
+}
+
+static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)
+{
+       unsigned long tmp;
+       
+       tmp = readl(i2c->regs + S3C2410_IICCON);
+       writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
+}
+
+
+/* s3c24xx_i2c_message_start
+ *
+ * put the start of a message onto the bus 
+*/
+
+static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, 
+                                     struct i2c_msg *msg)
+{
+       unsigned int addr = (msg->addr & 0x7f) << 1;
+       unsigned long stat;
+       unsigned long iiccon;
+
+       stat = 0;
+       stat |=  S3C2410_IICSTAT_TXRXEN;
+
+       if (msg->flags & I2C_M_RD) {
+               stat |= S3C2410_IICSTAT_MASTER_RX;
+               addr |= 1;
+       } else
+               stat |= S3C2410_IICSTAT_MASTER_TX;
+
+       // todo - check for wether ack wanted or not
+       s3c24xx_i2c_enable_ack(i2c);
+
+       iiccon = readl(i2c->regs + S3C2410_IICCON);
+       writel(stat, i2c->regs + S3C2410_IICSTAT);
+       
+       dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
+       writeb(addr, i2c->regs + S3C2410_IICDS);
+       
+       // delay a bit and reset iiccon before setting start (per samsung)
+       udelay(1);
+       dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
+       writel(iiccon, i2c->regs + S3C2410_IICCON);
+       
+       stat |=  S3C2410_IICSTAT_START;
+       writel(stat, i2c->regs + S3C2410_IICSTAT);
+}
+
+static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
+{
+       unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);
+
+       dev_dbg(i2c->dev, "STOP\n");
+
+       /* stop the transfer */
+       iicstat &= ~ S3C2410_IICSTAT_START;
+       writel(iicstat, i2c->regs + S3C2410_IICSTAT);
+       
+       i2c->state = STATE_STOP;
+       
+       s3c24xx_i2c_master_complete(i2c, ret);
+       s3c24xx_i2c_disable_irq(i2c);
+}
+
+/* helper functions to determine the current state in the set of
+ * messages we are sending */
+
+/* is_lastmsg()
+ *
+ * returns TRUE if the current message is the last in the set 
+*/
+
+static inline int is_lastmsg(struct s3c24xx_i2c *i2c)
+{
+       return i2c->msg_idx >= (i2c->msg_num - 1);
+}
+
+/* is_msglast
+ *
+ * returns TRUE if we this is the last byte in the current message
+*/
+
+static inline int is_msglast(struct s3c24xx_i2c *i2c)
+{
+       return i2c->msg_ptr == i2c->msg->len-1;
+}
+
+/* is_msgend
+ *
+ * returns TRUE if we reached the end of the current message
+*/
+
+static inline int is_msgend(struct s3c24xx_i2c *i2c)
+{
+       return i2c->msg_ptr >= i2c->msg->len;
+}
+
+/* i2s_s3c_irq_nextbyte
+ *
+ * process an interrupt and work out what to do
+ */
+
+static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
+{
+       unsigned long tmp;
+       unsigned char byte;
+       int ret = 0;
+
+       switch (i2c->state) {
+
+       case STATE_IDLE:
+               dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __FUNCTION__);
+               goto out;
+               break;
+
+       case STATE_STOP:
+               dev_err(i2c->dev, "%s: called in STATE_STOP\n", __FUNCTION__);
+               s3c24xx_i2c_disable_irq(i2c);           
+               goto out_ack;
+
+       case STATE_START:
+               /* last thing we did was send a start condition on the
+                * bus, or started a new i2c message
+                */
+               
+               if (iicstat  & S3C2410_IICSTAT_LASTBIT &&
+                   !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
+                       /* ack was not received... */
+
+                       dev_err(i2c->dev, "ack was not received\n" );
+                       s3c24xx_i2c_stop(i2c, -EREMOTEIO);
+                       goto out_ack;
+               }
+
+               if (i2c->msg->flags & I2C_M_RD)
+                       i2c->state = STATE_READ;
+               else
+                       i2c->state = STATE_WRITE;
+
+               /* terminate the transfer if there is nothing to do
+                * (used by the i2c probe to find devices */
+
+               if (is_lastmsg(i2c) && i2c->msg->len == 0) {
+                       s3c24xx_i2c_stop(i2c, 0);
+                       goto out_ack;
+               }
+
+               if (i2c->state == STATE_READ)
+                       goto prepare_read;
+
+               /* fall through to the write state, as we will need to 
+                * send a byte as well */
+
+       case STATE_WRITE:
+               /* we are writing data to the device... check for the
+                * end of the message, and if so, work out what to do
+                */
+
+       retry_write:
+               if (!is_msgend(i2c)) {
+                       byte = i2c->msg->buf[i2c->msg_ptr++];
+                       writeb(byte, i2c->regs + S3C2410_IICDS);
+                       
+               } else if (!is_lastmsg(i2c)) {
+                       /* we need to go to the next i2c message */
+
+                       dev_dbg(i2c->dev, "WRITE: Next Message\n");
+
+                       i2c->msg_ptr = 0;
+                       i2c->msg_idx ++;
+                       i2c->msg++;
+                       
+                       /* check to see if we need to do another message */
+                       if (i2c->msg->flags & I2C_M_NOSTART) {
+
+                               if (i2c->msg->flags & I2C_M_RD) {
+                                       /* cannot do this, the controller
+                                        * forces us to send a new START
+                                        * when we change direction */
+
+                                       s3c24xx_i2c_stop(i2c, -EINVAL);
+                               }
+
+                               goto retry_write;
+                       } else {
+                       
+                               /* send the new start */
+                               s3c24xx_i2c_message_start(i2c, i2c->msg);
+                               i2c->state = STATE_START;
+                       }
+
+               } else {
+                       /* send stop */
+
+                       s3c24xx_i2c_stop(i2c, 0);
+               }
+               break;
+
+       case STATE_READ:
+               /* we have a byte of data in the data register, do 
+                * something with it, and then work out wether we are
+                * going to do any more read/write
+                */
+
+               if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) &&
+                   !(is_msglast(i2c) && is_lastmsg(i2c))) {
+
+                       if (iicstat & S3C2410_IICSTAT_LASTBIT) {
+                               dev_dbg(i2c->dev, "READ: No Ack\n");
+
+                               s3c24xx_i2c_stop(i2c, -ECONNREFUSED);
+                               goto out_ack;
+                       }
+               }
+
+               byte = readb(i2c->regs + S3C2410_IICDS);
+               i2c->msg->buf[i2c->msg_ptr++] = byte;
+
+       prepare_read:
+               if (is_msglast(i2c)) {
+                       /* last byte of buffer */
+
+                       if (is_lastmsg(i2c))
+                               s3c24xx_i2c_disable_ack(i2c);
+                       
+               } else if (is_msgend(i2c)) {
+                       /* ok, we've read the entire buffer, see if there
+                        * is anything else we need to do */
+
+                       if (is_lastmsg(i2c)) {
+                               /* last message, send stop and complete */
+                               dev_dbg(i2c->dev, "READ: Send Stop\n");
+
+                               s3c24xx_i2c_stop(i2c, 0);
+                       } else {
+                               /* go to the next transfer */
+                               dev_dbg(i2c->dev, "READ: Next Transfer\n");
+
+                               i2c->msg_ptr = 0;
+                               i2c->msg_idx++;
+                               i2c->msg++;
+                       }
+               }
+
+               break;
+       }
+
+       /* acknowlegde the IRQ and get back on with the work */
+
+ out_ack:
+       tmp = readl(i2c->regs + S3C2410_IICCON);        
+       tmp &= ~S3C2410_IICCON_IRQPEND;
+       writel(tmp, i2c->regs + S3C2410_IICCON);
+ out:
+       return ret;
+}
+
+/* s3c24xx_i2c_irq
+ *
+ * top level IRQ servicing routine
+*/
+
+static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id,
+                                  struct pt_regs *regs)
+{
+       struct s3c24xx_i2c *i2c = dev_id;
+       unsigned long status;
+       unsigned long tmp;
+
+       status = readl(i2c->regs + S3C2410_IICSTAT);
+
+       if (status & S3C2410_IICSTAT_ARBITR) {
+               // deal with arbitration loss
+               dev_err(i2c->dev, "deal with arbitration loss\n");
+       }
+
+       if (i2c->state == STATE_IDLE) {
+               dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");
+
+               tmp = readl(i2c->regs + S3C2410_IICCON);        
+               tmp &= ~S3C2410_IICCON_IRQPEND;
+               writel(tmp, i2c->regs +  S3C2410_IICCON);
+               goto out;
+       }
+       
+       /* pretty much this leaves us with the fact that we've
+        * transmitted or received whatever byte we last sent */
+
+       i2s_s3c_irq_nextbyte(i2c, status);
+
+ out:
+       return IRQ_HANDLED;
+}
+
+
+/* s3c24xx_i2c_set_master
+ *
+ * get the i2c bus for a master transaction
+*/
+
+static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
+{
+       unsigned long iicstat;
+       int timeout = 400;
+
+       while (timeout-- > 0) {
+               iicstat = readl(i2c->regs + S3C2410_IICSTAT);
+               
+               if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))
+                       return 0;
+
+               msleep(1);
+       }
+
+       dev_dbg(i2c->dev, "timeout: GPEDAT is %08x\n",
+               __raw_readl(S3C2410_GPEDAT));
+
+       return -ETIMEDOUT;
+}
+
+/* s3c24xx_i2c_doxfer
+ *
+ * this starts an i2c transfer
+*/
+
+static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg msgs[], int num)
+{
+       unsigned long timeout;
+       int ret;
+
+       ret = s3c24xx_i2c_set_master(i2c);
+       if (ret != 0) {
+               dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
+               ret = -EAGAIN;
+               goto out;
+       }
+
+       spin_lock_irq(&i2c->lock);
+
+       i2c->msg     = msgs;
+       i2c->msg_num = num;
+       i2c->msg_ptr = 0;
+       i2c->msg_idx = 0;
+       i2c->state   = STATE_START;
+
+       s3c24xx_i2c_enable_irq(i2c);
+       s3c24xx_i2c_message_start(i2c, msgs);
+       spin_unlock_irq(&i2c->lock);
+       
+       timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
+
+       ret = i2c->msg_idx;
+
+       /* having these next two as dev_err() makes life very 
+        * noisy when doing an i2cdetect */
+
+       if (timeout == 0)
+               dev_dbg(i2c->dev, "timeout\n");
+       else if (ret != num)
+               dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
+
+       /* ensure the stop has been through the bus */
+
+       msleep(1);
+
+ out:
+       return ret;
+}
+
+/* s3c24xx_i2c_xfer
+ *
+ * first port of call from the i2c bus code when an message needs
+ * transfering across the i2c bus.
+*/
+
+static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
+                       struct i2c_msg msgs[], int num)
+{
+       struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
+       int retry;
+       int ret;
+
+       for (retry = 0; retry < adap->retries; retry++) {
+
+               ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
+
+               if (ret != -EAGAIN)
+                       return ret;
+
+               dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
+
+               udelay(100);
+       }
+
+       return -EREMOTEIO;
+}
+
+/* i2c bus registration info */
+
+static struct i2c_algorithm s3c24xx_i2c_algorithm = {
+       .name                   = "S3C2410-I2C-Algorithm",
+       .master_xfer            = s3c24xx_i2c_xfer,
+};
+
+static struct s3c24xx_i2c s3c24xx_i2c = {
+       .lock   = SPIN_LOCK_UNLOCKED,
+       .wait   = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
+       .adap   = {
+               .name                   = "s3c2410-i2c",
+               .algo                   = &s3c24xx_i2c_algorithm,
+               .retries                = 2,
+       },
+};
+
+/* s3c24xx_i2c_calcdivisor
+ *
+ * return the divisor settings for a given frequency
+*/
+
+static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
+                                  unsigned int *div1, unsigned int *divs)
+{
+       unsigned int calc_divs = clkin / wanted;
+       unsigned int calc_div1;
+
+       if (calc_divs > (16*16))
+               calc_div1 = 512;
+       else
+               calc_div1 = 16;
+
+       calc_divs += calc_div1-1;
+       calc_divs /= calc_div1;
+
+       if (calc_divs == 0)
+               calc_divs = 1;
+       if (calc_divs > 17)
+               calc_divs = 17;
+
+       *divs = calc_divs;
+       *div1 = calc_div1;
+
+       return clkin / (calc_divs * calc_div1);
+}
+
+/* freq_acceptable
+ *
+ * test wether a frequency is within the acceptable range of error
+*/
+
+static inline int freq_acceptable(unsigned int freq, unsigned int wanted)
+{
+       int diff = freq - wanted;
+
+       return (diff >= -2 && diff <= 2);
+}
+
+/* s3c24xx_i2c_getdivisor
+ *
+ * work out a divisor for the user requested frequency setting,
+ * either by the requested frequency, or scanning the acceptable
+ * range of frequencies until something is found
+*/
+
+static int s3c24xx_i2c_getdivisor(struct s3c24xx_i2c *i2c,
+                                 struct s3c2410_platform_i2c *pdata,
+                                 unsigned long *iicon,
+                                 unsigned int *got)
+{
+       unsigned long clkin = clk_get_rate(i2c->clk);
+       
+       unsigned int divs, div1;
+       int freq;
+       int start, end;
+
+       clkin /= 1000;          /* clkin now in KHz */
+     
+       dev_dbg(i2c->dev,  "pdata %p, freq %lu %lu..%lu\n",
+                pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq);
+
+       if (pdata->bus_freq != 0) {
+               freq = s3c24xx_i2c_calcdivisor(clkin, pdata->bus_freq/1000,
+                                              &div1, &divs);
+               if (freq_acceptable(freq, pdata->bus_freq/1000))
+                       goto found;
+       }
+
+       /* ok, we may have to search for something suitable... */
+
+       start = (pdata->max_freq == 0) ? pdata->bus_freq : pdata->max_freq;
+       end = pdata->min_freq;
+
+       start /= 1000;
+       end /= 1000;
+
+       /* search loop... */
+
+       for (; start > end; start--) {
+               freq = s3c24xx_i2c_calcdivisor(clkin, start, &div1, &divs);
+               if (freq_acceptable(freq, start))
+                       goto found;
+       }
+
+       /* cannot find frequency spec */
+
+       return -EINVAL;
+
+ found:
+       *got = freq;
+       *iicon |= (divs-1);
+       *iicon |= (div1 == 512) ? S3C2410_IICCON_TXDIV_512 : 0;
+       return 0;
+}
+
+/* s3c24xx_i2c_init
+ *
+ * initialise the controller, set the IO lines and frequency 
+*/
+
+static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
+{
+       unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
+       struct s3c2410_platform_i2c *pdata;
+       unsigned int freq;
+
+       /* get the plafrom data */
+
+       pdata = s3c24xx_i2c_get_platformdata(i2c->adap.dev.parent);
+
+       /* inititalise the gpio */
+
+       s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA);
+       s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL);
+
+       /* write slave address */
+       
+       writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
+
+       dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
+
+       /* we need to work out the divisors for the clock... */
+
+       if (s3c24xx_i2c_getdivisor(i2c, pdata, &iicon, &freq) != 0) {
+               dev_err(i2c->dev, "cannot meet bus frequency required\n");
+               return -EINVAL;
+       }
+
+       /* todo - check that the i2c lines aren't being dragged anywhere */
+
+       dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
+       dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
+       
+       writel(iicon, i2c->regs + S3C2410_IICCON);
+
+       /* check for s3c2440 i2c controller  */
+
+       if (s3c24xx_i2c_is2440(i2c)) {
+               dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);
+
+               writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);
+       }
+
+       return 0;
+}
+
+static void s3c24xx_i2c_free(struct s3c24xx_i2c *i2c)
+{
+       if (i2c->clk != NULL && !IS_ERR(i2c->clk)) {
+               clk_disable(i2c->clk);
+               clk_unuse(i2c->clk);
+               clk_put(i2c->clk);
+               i2c->clk = NULL;
+       }
+
+       if (i2c->regs != NULL) {
+               iounmap(i2c->regs);
+               i2c->regs = NULL;
+       }
+
+       if (i2c->ioarea != NULL) {
+               release_resource(i2c->ioarea);
+               kfree(i2c->ioarea);
+               i2c->ioarea = NULL;
+       }
+}
+
+/* s3c24xx_i2c_probe
+ *
+ * called by the bus driver when a suitable device is found
+*/
+
+static int s3c24xx_i2c_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
+       struct resource *res;
+       int ret;
+
+       /* find the clock and enable it */
+
+       i2c->dev = dev;
+       i2c->clk = clk_get(dev, "i2c");
+       if (IS_ERR(i2c->clk)) {
+               dev_err(dev, "cannot get clock\n");
+               ret = -ENOENT;
+               goto out;
+       }
+
+       dev_dbg(dev, "clock source %p\n", i2c->clk);
+
+       clk_use(i2c->clk);
+       clk_enable(i2c->clk);
+
+       /* map the registers */
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(dev, "cannot find IO resource\n");
+               ret = -ENOENT;
+               goto out;
+       }
+
+       i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
+                                        pdev->name);
+
+       if (i2c->ioarea == NULL) {
+               dev_err(dev, "cannot request IO\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       i2c->regs = ioremap(res->start, (res->end-res->start)+1);
+
+       if (i2c->regs == NULL) {
+               dev_err(dev, "cannot map IO\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       dev_dbg(dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res);
+
+       /* setup info block for the i2c core */
+
+       i2c->adap.algo_data = i2c;
+       i2c->adap.dev.parent = dev;
+
+       /* initialise the i2c controller */
+
+       ret = s3c24xx_i2c_init(i2c);
+       if (ret != 0)
+               goto out;
+
+       /* find the IRQ for this unit (note, this relies on the init call to
+        * ensure no current IRQs pending 
+        */
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (res == NULL) {
+               dev_err(dev, "cannot find IRQ\n");
+               ret = -ENOENT;
+               goto out;
+       }
+
+       ret = request_irq(res->start, s3c24xx_i2c_irq, SA_INTERRUPT,
+                         pdev->name, i2c);
+
+       if (ret != 0) {
+               dev_err(dev, "cannot claim IRQ\n");
+               goto out;
+       }
+
+       i2c->irq = res;
+               
+       dev_dbg(dev, "irq resource %p (%ld)\n", res, res->start);
+
+       ret = i2c_add_adapter(&i2c->adap);
+       if (ret < 0) {
+               dev_err(dev, "failed to add bus to i2c core\n");
+               goto out;
+       }
+
+       dev_set_drvdata(dev, i2c);
+
+       dev_info(dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id);
+
+ out:
+       if (ret < 0)
+               s3c24xx_i2c_free(i2c);
+
+       return ret;
+}
+
+/* s3c24xx_i2c_remove
+ *
+ * called when device is removed from the bus
+*/
+
+static int s3c24xx_i2c_remove(struct device *dev)
+{
+       struct s3c24xx_i2c *i2c = dev_get_drvdata(dev);
+       
+       if (i2c != NULL) {
+               s3c24xx_i2c_free(i2c);
+               dev_set_drvdata(dev, NULL);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c24xx_i2c_resume(struct device *dev, u32 level)
+{
+       struct s3c24xx_i2c *i2c = dev_get_drvdata(dev);
+       
+       if (i2c != NULL && level == RESUME_ENABLE) {
+               dev_dbg(dev, "resume: level %d\n", level);
+               s3c24xx_i2c_init(i2c);
+       }
+
+       return 0;
+}
+
+#else
+#define s3c24xx_i2c_resume NULL
+#endif
+
+/* device driver for platform bus bits */
+
+static struct device_driver s3c2410_i2c_driver = {
+       .name           = "s3c2410-i2c",
+       .bus            = &platform_bus_type,
+       .probe          = s3c24xx_i2c_probe,
+       .remove         = s3c24xx_i2c_remove,
+       .resume         = s3c24xx_i2c_resume,
+};
+
+static struct device_driver s3c2440_i2c_driver = {
+       .name           = "s3c2440-i2c",
+       .bus            = &platform_bus_type,
+       .probe          = s3c24xx_i2c_probe,
+       .remove         = s3c24xx_i2c_remove,
+       .resume         = s3c24xx_i2c_resume,
+};
+
+static int __init i2c_adap_s3c_init(void)
+{
+       int ret;
+
+       ret = driver_register(&s3c2410_i2c_driver);
+       if (ret == 0)
+               ret = driver_register(&s3c2440_i2c_driver); 
+
+       return ret;
+}
+
+static void __exit i2c_adap_s3c_exit(void)
+{
+       driver_unregister(&s3c2410_i2c_driver);
+       driver_unregister(&s3c2440_i2c_driver);
+}
+
+module_init(i2c_adap_s3c_init);
+module_exit(i2c_adap_s3c_exit);
+
+MODULE_DESCRIPTION("S3C24XX I2C Bus driver");
+MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c
new file mode 100644 (file)
index 0000000..e5dd90b
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2004 Steven J. Hill
+ * Copyright (C) 2001,2002,2003 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/i2c-algo-sibyte.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_smbus.h>
+
+static struct i2c_algo_sibyte_data sibyte_board_data[2] = {
+       { NULL, 0, (void *) (KSEG1+A_SMB_BASE(0)) },
+       { NULL, 1, (void *) (KSEG1+A_SMB_BASE(1)) }
+};
+
+static struct i2c_adapter sibyte_board_adapter[2] = {
+       {
+               .owner          = THIS_MODULE,
+               .id             = I2C_HW_SIBYTE,
+               .class          = I2C_CLASS_HWMON,
+               .algo           = NULL,
+               .algo_data      = &sibyte_board_data[0],
+               .name           = "SiByte SMBus 0",
+       },
+       {
+               .owner          = THIS_MODULE,
+               .id             = I2C_HW_SIBYTE,
+               .class          = I2C_CLASS_HWMON,
+               .algo           = NULL,
+               .algo_data      = &sibyte_board_data[1],
+               .name           = "SiByte SMBus 1",
+       },
+};
+
+static int __init i2c_sibyte_init(void)
+{
+       printk("i2c-swarm.o: i2c SMBus adapter module for SiByte board\n");
+       if (i2c_sibyte_add_bus(&sibyte_board_adapter[0], K_SMB_FREQ_100KHZ) < 0)
+               return -ENODEV;
+       if (i2c_sibyte_add_bus(&sibyte_board_adapter[1], K_SMB_FREQ_400KHZ) < 0)
+               return -ENODEV;
+       return 0;
+}
+
+static void __exit i2c_sibyte_exit(void)
+{
+       i2c_sibyte_del_bus(&sibyte_board_adapter[0]);
+       i2c_sibyte_del_bus(&sibyte_board_adapter[1]);
+}
+
+module_init(i2c_sibyte_init);
+module_exit(i2c_sibyte_exit);
+
+MODULE_AUTHOR("Kip Walker <kwalker@broadcom.com>, Steven J. Hill <sjhill@realitydiluted.com>");
+MODULE_DESCRIPTION("SMBus adapter routines for SiByte boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c
new file mode 100644 (file)
index 0000000..fec0bff
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+    i2c-stub.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+
+    Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define DEBUG 1
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+
+static u8  stub_bytes[256];
+static u16 stub_words[256];
+
+/* Return -1 on error. */
+static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
+       char read_write, u8 command, int size, union i2c_smbus_data * data)
+{
+       s32 ret;
+
+       switch (size) {
+
+       case I2C_SMBUS_QUICK:
+               dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr);
+               ret = 0;
+               break;
+
+       case I2C_SMBUS_BYTE_DATA:
+               if (read_write == I2C_SMBUS_WRITE) {
+                       stub_bytes[command] = data->byte;
+                       dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, "
+                                       "wrote 0x%02x at 0x%02x.\n",
+                                       addr, data->byte, command);
+               } else {
+                       data->byte = stub_bytes[command];
+                       dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, "
+                                       "read  0x%02x at 0x%02x.\n",
+                                       addr, data->byte, command);
+               }
+
+               ret = 0;
+               break;
+
+       case I2C_SMBUS_WORD_DATA:
+               if (read_write == I2C_SMBUS_WRITE) {
+                       stub_words[command] = data->word;
+                       dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, "
+                                       "wrote 0x%04x at 0x%02x.\n",
+                                       addr, data->word, command);
+               } else {
+                       data->word = stub_words[command];
+                       dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, "
+                                       "read  0x%04x at 0x%02x.\n",
+                                       addr, data->word, command);
+               }
+
+               ret = 0;
+               break;
+
+       default:
+               dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n");
+               ret = -1;
+               break;
+       } /* switch (size) */
+
+       return ret;
+}
+
+static u32 stub_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE_DATA |
+               I2C_FUNC_SMBUS_WORD_DATA;
+}
+
+static struct i2c_algorithm smbus_algorithm = {
+       .name           = "Non-I2C SMBus adapter",
+       .id             = I2C_ALGO_SMBUS,
+       .functionality  = stub_func,
+       .smbus_xfer     = stub_xfer,
+};
+
+static struct i2c_adapter stub_adapter = {
+       .owner          = THIS_MODULE,
+       .class          = I2C_CLASS_HWMON,
+       .algo           = &smbus_algorithm,
+       .name           = "SMBus stub driver",
+};
+
+static int __init i2c_stub_init(void)
+{
+       printk(KERN_INFO "i2c-stub loaded\n");
+       return i2c_add_adapter(&stub_adapter);
+}
+
+static void __exit i2c_stub_exit(void)
+{
+       i2c_del_adapter(&stub_adapter);
+}
+
+MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
+MODULE_DESCRIPTION("I2C stub driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_stub_init);
+module_exit(i2c_stub_exit);
+
diff --git a/drivers/i2c/chips/adm1026.c b/drivers/i2c/chips/adm1026.c
new file mode 100644 (file)
index 0000000..e2d13fa
--- /dev/null
@@ -0,0 +1,1779 @@
+/*
+    adm1026.c - Part of lm_sensors, Linux kernel modules for hardware
+            monitoring
+    Copyright (C) 2002, 2003  Philip Pokorny <ppokorny@penguincomputing.com>
+    Copyright (C) 2004 Justin Thiessen <jthiessen@penguincomputing.com>
+
+    Chip details at:
+
+    <http://www.analog.com/UploadedFiles/Data_Sheets/779263102ADM1026_a.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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/i2c-vid.h>
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(adm1026);
+
+static int gpio_input[17]  = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+                               -1, -1, -1, -1, -1, -1, -1, -1 }; 
+static int gpio_output[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+                               -1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_inverted[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+                               -1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_normal[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
+                               -1, -1, -1, -1, -1, -1, -1, -1 };
+static int gpio_fan[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+module_param_array(gpio_input,int,NULL,0);
+MODULE_PARM_DESC(gpio_input,"List of GPIO pins (0-16) to program as inputs");
+module_param_array(gpio_output,int,NULL,0);
+MODULE_PARM_DESC(gpio_output,"List of GPIO pins (0-16) to program as "
+       "outputs");
+module_param_array(gpio_inverted,int,NULL,0);
+MODULE_PARM_DESC(gpio_inverted,"List of GPIO pins (0-16) to program as "
+       "inverted");
+module_param_array(gpio_normal,int,NULL,0);
+MODULE_PARM_DESC(gpio_normal,"List of GPIO pins (0-16) to program as "
+       "normal/non-inverted");
+module_param_array(gpio_fan,int,NULL,0);
+MODULE_PARM_DESC(gpio_fan,"List of GPIO pins (0-7) to program as fan tachs");
+
+/* Many ADM1026 constants specified below */
+
+/* The ADM1026 registers */
+#define ADM1026_REG_CONFIG1  0x00
+#define CFG1_MONITOR     0x01
+#define CFG1_INT_ENABLE  0x02
+#define CFG1_INT_CLEAR   0x04
+#define CFG1_AIN8_9      0x08
+#define CFG1_THERM_HOT   0x10
+#define CFG1_DAC_AFC     0x20
+#define CFG1_PWM_AFC     0x40
+#define CFG1_RESET       0x80
+#define ADM1026_REG_CONFIG2  0x01
+/* CONFIG2 controls FAN0/GPIO0 through FAN7/GPIO7 */
+#define ADM1026_REG_CONFIG3  0x07
+#define CFG3_GPIO16_ENABLE  0x01
+#define CFG3_CI_CLEAR  0x02
+#define CFG3_VREF_250  0x04
+#define CFG3_GPIO16_DIR  0x40
+#define CFG3_GPIO16_POL  0x80
+#define ADM1026_REG_E2CONFIG  0x13
+#define E2CFG_READ  0x01
+#define E2CFG_WRITE  0x02
+#define E2CFG_ERASE  0x04
+#define E2CFG_ROM  0x08
+#define E2CFG_CLK_EXT  0x80
+
+/* There are 10 general analog inputs and 7 dedicated inputs
+ * They are:
+ *    0 - 9  =  AIN0 - AIN9
+ *       10  =  Vbat
+ *       11  =  3.3V Standby
+ *       12  =  3.3V Main
+ *       13  =  +5V
+ *       14  =  Vccp (CPU core voltage)
+ *       15  =  +12V
+ *       16  =  -12V
+ */
+static u16 ADM1026_REG_IN[] = {
+               0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+               0x36, 0x37, 0x27, 0x29, 0x26, 0x2a,
+               0x2b, 0x2c, 0x2d, 0x2e, 0x2f
+       };
+static u16 ADM1026_REG_IN_MIN[] = {
+               0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d,
+               0x5e, 0x5f, 0x6d, 0x49, 0x6b, 0x4a,
+               0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+       };
+static u16 ADM1026_REG_IN_MAX[] = {
+               0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
+               0x56, 0x57, 0x6c, 0x41, 0x6a, 0x42,
+               0x43, 0x44, 0x45, 0x46, 0x47
+       };
+
+/* Temperatures are:
+ *    0 - Internal
+ *    1 - External 1
+ *    2 - External 2
+ */
+static u16 ADM1026_REG_TEMP[] = { 0x1f, 0x28, 0x29 };
+static u16 ADM1026_REG_TEMP_MIN[] = { 0x69, 0x48, 0x49 };
+static u16 ADM1026_REG_TEMP_MAX[] = { 0x68, 0x40, 0x41 };
+static u16 ADM1026_REG_TEMP_TMIN[] = { 0x10, 0x11, 0x12 };
+static u16 ADM1026_REG_TEMP_THERM[] = { 0x0d, 0x0e, 0x0f };
+static u16 ADM1026_REG_TEMP_OFFSET[] = { 0x1e, 0x6e, 0x6f };
+
+#define ADM1026_REG_FAN(nr) (0x38 + (nr))
+#define ADM1026_REG_FAN_MIN(nr) (0x60 + (nr))
+#define ADM1026_REG_FAN_DIV_0_3 0x02
+#define ADM1026_REG_FAN_DIV_4_7 0x03
+
+#define ADM1026_REG_DAC  0x04
+#define ADM1026_REG_PWM  0x05
+
+#define ADM1026_REG_GPIO_CFG_0_3 0x08
+#define ADM1026_REG_GPIO_CFG_4_7 0x09
+#define ADM1026_REG_GPIO_CFG_8_11 0x0a
+#define ADM1026_REG_GPIO_CFG_12_15 0x0b
+/* CFG_16 in REG_CFG3 */
+#define ADM1026_REG_GPIO_STATUS_0_7 0x24
+#define ADM1026_REG_GPIO_STATUS_8_15 0x25
+/* STATUS_16 in REG_STATUS4 */
+#define ADM1026_REG_GPIO_MASK_0_7 0x1c
+#define ADM1026_REG_GPIO_MASK_8_15 0x1d
+/* MASK_16 in REG_MASK4 */
+
+#define ADM1026_REG_COMPANY 0x16
+#define ADM1026_REG_VERSTEP 0x17
+/* These are the recognized values for the above regs */
+#define ADM1026_COMPANY_ANALOG_DEV 0x41
+#define ADM1026_VERSTEP_GENERIC 0x40
+#define ADM1026_VERSTEP_ADM1026 0x44
+
+#define ADM1026_REG_MASK1 0x18
+#define ADM1026_REG_MASK2 0x19
+#define ADM1026_REG_MASK3 0x1a
+#define ADM1026_REG_MASK4 0x1b
+
+#define ADM1026_REG_STATUS1 0x20
+#define ADM1026_REG_STATUS2 0x21
+#define ADM1026_REG_STATUS3 0x22
+#define ADM1026_REG_STATUS4 0x23
+
+#define ADM1026_FAN_ACTIVATION_TEMP_HYST -6
+#define ADM1026_FAN_CONTROL_TEMP_RANGE 20
+#define ADM1026_PWM_MAX 255
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG 
+ * variants. Note that you should be a bit careful with which arguments
+ * these macros are called: arguments may be evaluated more than once.
+ */
+
+/* IN are scaled acording to built-in resistors.  These are the
+ *   voltages corresponding to 3/4 of full scale (192 or 0xc0)
+ *   NOTE: The -12V input needs an additional factor to account
+ *      for the Vref pullup resistor.
+ *      NEG12_OFFSET = SCALE * Vref / V-192 - Vref
+ *                   = 13875 * 2.50 / 1.875 - 2500
+ *                   = 16000
+ *
+ * The values in this table are based on Table II, page 15 of the
+ *    datasheet.
+ */
+static int adm1026_scaling[] = {  /* .001 Volts */
+               2250, 2250, 2250, 2250, 2250, 2250, 
+               1875, 1875, 1875, 1875, 3000, 3330, 
+               3330, 4995, 2250, 12000, 13875
+       };
+#define NEG12_OFFSET  16000
+#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from))
+#define INS_TO_REG(n,val)  (SENSORS_LIMIT(SCALE(val,adm1026_scaling[n],192),\
+       0,255))
+#define INS_FROM_REG(n,val) (SCALE(val,192,adm1026_scaling[n]))
+
+/* FAN speed is measured using 22.5kHz clock and counts for 2 pulses
+ *   and we assume a 2 pulse-per-rev fan tach signal
+ *      22500 kHz * 60 (sec/min) * 2 (pulse) / 2 (pulse/rev) == 1350000
+ */
+#define FAN_TO_REG(val,div)  ((val)<=0 ? 0xff : SENSORS_LIMIT(1350000/((val)*\
+       (div)),1,254)) 
+#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==0xff ? 0 : 1350000/((val)*\
+       (div)))
+#define DIV_FROM_REG(val) (1<<(val))
+#define DIV_TO_REG(val) ((val)>=8 ? 3 : (val)>=4 ? 2 : (val)>=2 ? 1 : 0)
+
+/* Temperature is reported in 1 degC increments */
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)+((val)<0 ? -500 : 500))/1000,\
+       -127,127))
+#define TEMP_FROM_REG(val) ((val) * 1000)
+#define OFFSET_TO_REG(val) (SENSORS_LIMIT(((val)+((val)<0 ? -500 : 500))/1000,\
+       -127,127))
+#define OFFSET_FROM_REG(val) ((val) * 1000)
+
+#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255))
+#define PWM_FROM_REG(val) (val)
+
+#define PWM_MIN_TO_REG(val) ((val) & 0xf0)
+#define PWM_MIN_FROM_REG(val) (((val) & 0xf0) + ((val) >> 4))
+
+/* Analog output is a voltage, and scaled to millivolts.  The datasheet 
+ *   indicates that the DAC could be used to drive the fans, but in our 
+ *   example board (Arima HDAMA) it isn't connected to the fans at all.
+ */
+#define DAC_TO_REG(val) (SENSORS_LIMIT(((((val)*255)+500)/2500),0,255)) 
+#define DAC_FROM_REG(val) (((val)*2500)/255)
+
+/* Typically used with systems using a v9.1 VRM spec ? */
+#define ADM1026_INIT_VRM  91
+
+/* Chip sampling rates
+ *
+ * Some sensors are not updated more frequently than once per second
+ *    so it doesn't make sense to read them more often than that.
+ *    We cache the results and return the saved data if the driver
+ *    is called again before a second has elapsed.
+ *
+ * Also, there is significant configuration data for this chip
+ *    So, we keep the config data up to date in the cache
+ *    when it is written and only sample it once every 5 *minutes*
+ */
+#define ADM1026_DATA_INTERVAL  (1 * HZ)
+#define ADM1026_CONFIG_INTERVAL  (5 * 60 * HZ)
+
+/* We allow for multiple chips in a single system.
+ *
+ * For each registered ADM1026, we need to keep state information
+ * at client->data. The adm1026_data structure is dynamically
+ * allocated, when a new client structure is allocated. */
+
+struct pwm_data {
+       u8 pwm;
+       u8 enable;
+       u8 auto_pwm_min;
+};
+
+struct adm1026_data {
+       struct i2c_client client;
+       struct semaphore lock;
+       enum chips type;
+
+       struct semaphore update_lock;
+       int valid;              /* !=0 if following fields are valid */
+       unsigned long last_reading;     /* In jiffies */
+       unsigned long last_config;      /* In jiffies */
+
+       u8 in[17];              /* Register value */
+       u8 in_max[17];          /* Register value */
+       u8 in_min[17];          /* Register value */
+       s8 temp[3];             /* Register value */
+       s8 temp_min[3];         /* Register value */
+       s8 temp_max[3];         /* Register value */
+       s8 temp_tmin[3];        /* Register value */
+       s8 temp_crit[3];        /* Register value */
+       s8 temp_offset[3];      /* Register value */
+       u8 fan[8];              /* Register value */
+       u8 fan_min[8];          /* Register value */
+       u8 fan_div[8];          /* Decoded value */
+       struct pwm_data pwm1;   /* Pwm control values */
+       int vid;                /* Decoded value */
+       u8 vrm;                 /* VRM version */
+       u8 analog_out;          /* Register value (DAC) */
+       long alarms;            /* Register encoding, combined */
+       long alarm_mask;        /* Register encoding, combined */
+       long gpio;              /* Register encoding, combined */
+       long gpio_mask;         /* Register encoding, combined */
+       u8 gpio_config[17];     /* Decoded value */
+       u8 config1;             /* Register value */
+       u8 config2;             /* Register value */
+       u8 config3;             /* Register value */
+};
+
+static int adm1026_attach_adapter(struct i2c_adapter *adapter);
+static int adm1026_detect(struct i2c_adapter *adapter, int address,
+       int kind);
+static int adm1026_detach_client(struct i2c_client *client);
+static int adm1026_read_value(struct i2c_client *client, u8 register);
+static int adm1026_write_value(struct i2c_client *client, u8 register,
+       int value); 
+static void adm1026_print_gpio(struct i2c_client *client);
+static void adm1026_fixup_gpio(struct i2c_client *client); 
+static struct adm1026_data *adm1026_update_device(struct device *dev);
+static void adm1026_init_client(struct i2c_client *client);
+
+
+static struct i2c_driver adm1026_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "adm1026",
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = adm1026_attach_adapter,
+       .detach_client  = adm1026_detach_client,
+};
+
+static int adm1026_id;
+
+int adm1026_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON)) {
+               return 0;
+       }
+       return i2c_detect(adapter, &addr_data, adm1026_detect);
+}
+
+int adm1026_detach_client(struct i2c_client *client)
+{
+       i2c_detach_client(client);
+       kfree(client);
+       return 0;
+}
+
+int adm1026_read_value(struct i2c_client *client, u8 reg)
+{
+       int res;
+
+       if (reg < 0x80) {
+               /* "RAM" locations */
+               res = i2c_smbus_read_byte_data(client, reg) & 0xff;
+       } else {
+               /* EEPROM, do nothing */
+               res = 0;
+       }
+       return res;
+}
+
+int adm1026_write_value(struct i2c_client *client, u8 reg, int value)
+{
+       int res;
+
+       if (reg < 0x80) {
+               /* "RAM" locations */
+               res = i2c_smbus_write_byte_data(client, reg, value);
+       } else {
+               /* EEPROM, do nothing */
+               res = 0;
+       }
+       return res;
+}
+
+void adm1026_init_client(struct i2c_client *client)
+{
+       int value, i;
+       struct adm1026_data *data = i2c_get_clientdata(client);
+
+        dev_dbg(&client->dev,"(%d): Initializing device\n", client->id);
+       /* Read chip config */
+       data->config1 = adm1026_read_value(client, ADM1026_REG_CONFIG1);
+       data->config2 = adm1026_read_value(client, ADM1026_REG_CONFIG2);
+       data->config3 = adm1026_read_value(client, ADM1026_REG_CONFIG3);
+
+       /* Inform user of chip config */
+       dev_dbg(&client->dev, "(%d): ADM1026_REG_CONFIG1 is: 0x%02x\n",
+               client->id, data->config1);
+       if ((data->config1 & CFG1_MONITOR) == 0) {
+               dev_dbg(&client->dev, "(%d): Monitoring not currently "
+                       "enabled.\n", client->id);
+       }
+       if (data->config1 & CFG1_INT_ENABLE) {
+               dev_dbg(&client->dev, "(%d): SMBALERT interrupts are "
+                       "enabled.\n", client->id);
+       }
+       if (data->config1 & CFG1_AIN8_9) {
+               dev_dbg(&client->dev, "(%d): in8 and in9 enabled. "
+                       "temp3 disabled.\n", client->id);
+       } else {
+               dev_dbg(&client->dev, "(%d): temp3 enabled.  in8 and "
+                       "in9 disabled.\n", client->id);
+       }
+       if (data->config1 & CFG1_THERM_HOT) {
+               dev_dbg(&client->dev, "(%d): Automatic THERM, PWM, "
+                       "and temp limits enabled.\n", client->id);
+       }
+
+       value = data->config3;
+       if (data->config3 & CFG3_GPIO16_ENABLE) {
+               dev_dbg(&client->dev, "(%d): GPIO16 enabled.  THERM"
+                       "pin disabled.\n", client->id);
+       } else {
+               dev_dbg(&client->dev, "(%d): THERM pin enabled.  "
+                       "GPIO16 disabled.\n", client->id);
+       }
+       if (data->config3 & CFG3_VREF_250) {
+               dev_dbg(&client->dev, "(%d): Vref is 2.50 Volts.\n",
+                       client->id);
+       } else {
+               dev_dbg(&client->dev, "(%d): Vref is 1.82 Volts.\n",
+                       client->id);
+       }
+       /* Read and pick apart the existing GPIO configuration */
+       value = 0;
+       for (i = 0;i <= 15;++i) {
+               if ((i & 0x03) == 0) {
+                       value = adm1026_read_value(client,
+                                       ADM1026_REG_GPIO_CFG_0_3 + i/4);
+               }
+               data->gpio_config[i] = value & 0x03;
+               value >>= 2;
+       }
+       data->gpio_config[16] = (data->config3 >> 6) & 0x03;
+
+       /* ... and then print it */
+       adm1026_print_gpio(client);
+
+       /* If the user asks us to reprogram the GPIO config, then
+        *   do it now.  But only if this is the first ADM1026.
+        */
+       if (client->id == 0
+           && (gpio_input[0] != -1 || gpio_output[0] != -1
+               || gpio_inverted[0] != -1 || gpio_normal[0] != -1
+               || gpio_fan[0] != -1)) {
+               adm1026_fixup_gpio(client);
+       }
+
+       /* WE INTENTIONALLY make no changes to the limits,
+        *   offsets, pwms, fans and zones.  If they were
+        *   configured, we don't want to mess with them.
+        *   If they weren't, the default is 100% PWM, no
+        *   control and will suffice until 'sensors -s'
+        *   can be run by the user.  We DO set the default 
+        *   value for pwm1.auto_pwm_min to its maximum
+        *   so that enabling automatic pwm fan control
+        *   without first setting a value for pwm1.auto_pwm_min 
+        *   will not result in potentially dangerous fan speed decrease.
+        */
+       data->pwm1.auto_pwm_min=255;
+       /* Start monitoring */
+       value = adm1026_read_value(client, ADM1026_REG_CONFIG1);
+       /* Set MONITOR, clear interrupt acknowledge and s/w reset */
+       value = (value | CFG1_MONITOR) & (~CFG1_INT_CLEAR & ~CFG1_RESET);
+       dev_dbg(&client->dev, "(%d): Setting CONFIG to: 0x%02x\n",
+               client->id, value);
+       data->config1 = value;
+       adm1026_write_value(client, ADM1026_REG_CONFIG1, value);
+}
+
+void adm1026_print_gpio(struct i2c_client *client)
+{
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int  i;
+
+       dev_dbg(&client->dev, "(%d): GPIO config is:",
+                           client->id);
+       for (i = 0;i <= 7;++i) {
+               if (data->config2 & (1 << i)) {
+                       dev_dbg(&client->dev, "\t(%d): %sGP%s%d\n", client->id,
+                               data->gpio_config[i] & 0x02 ? "" : "!",
+                               data->gpio_config[i] & 0x01 ? "OUT" : "IN",
+                               i);
+               } else {
+                       dev_dbg(&client->dev, "\t(%d): FAN%d\n",
+                               client->id, i);
+               }
+       }
+       for (i = 8;i <= 15;++i) {
+               dev_dbg(&client->dev, "\t(%d): %sGP%s%d\n", client->id,
+                       data->gpio_config[i] & 0x02 ? "" : "!",
+                       data->gpio_config[i] & 0x01 ? "OUT" : "IN",
+                       i);
+       }
+       if (data->config3 & CFG3_GPIO16_ENABLE) {
+               dev_dbg(&client->dev, "\t(%d): %sGP%s16\n", client->id,
+                       data->gpio_config[16] & 0x02 ? "" : "!",
+                       data->gpio_config[16] & 0x01 ? "OUT" : "IN");
+       } else {
+               /* GPIO16 is THERM  */
+               dev_dbg(&client->dev, "\t(%d): THERM\n", client->id);
+       }
+}
+
+void adm1026_fixup_gpio(struct i2c_client *client)
+{
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int  i;
+       int  value;
+
+       /* Make the changes requested. */
+       /* We may need to unlock/stop monitoring or soft-reset the
+        *    chip before we can make changes.  This hasn't been
+        *    tested much.  FIXME
+        */
+
+       /* Make outputs */
+       for (i = 0;i <= 16;++i) {
+               if (gpio_output[i] >= 0 && gpio_output[i] <= 16) {
+                       data->gpio_config[gpio_output[i]] |= 0x01;
+               }
+               /* if GPIO0-7 is output, it isn't a FAN tach */
+               if (gpio_output[i] >= 0 && gpio_output[i] <= 7) {
+                       data->config2 |= 1 << gpio_output[i];
+               }
+       }
+
+       /* Input overrides output */
+       for (i = 0;i <= 16;++i) {
+               if (gpio_input[i] >= 0 && gpio_input[i] <= 16) {
+                       data->gpio_config[gpio_input[i]] &= ~ 0x01;
+               }
+               /* if GPIO0-7 is input, it isn't a FAN tach */
+               if (gpio_input[i] >= 0 && gpio_input[i] <= 7) {
+                       data->config2 |= 1 << gpio_input[i];
+               }
+       }
+
+       /* Inverted  */
+       for (i = 0;i <= 16;++i) {
+               if (gpio_inverted[i] >= 0 && gpio_inverted[i] <= 16) {
+                       data->gpio_config[gpio_inverted[i]] &= ~ 0x02;
+               }
+       }
+
+       /* Normal overrides inverted  */
+       for (i = 0;i <= 16;++i) {
+               if (gpio_normal[i] >= 0 && gpio_normal[i] <= 16) {
+                       data->gpio_config[gpio_normal[i]] |= 0x02;
+               }
+       }
+
+       /* Fan overrides input and output */
+       for (i = 0;i <= 7;++i) {
+               if (gpio_fan[i] >= 0 && gpio_fan[i] <= 7) {
+                       data->config2 &= ~(1 << gpio_fan[i]);
+               }
+       }
+
+       /* Write new configs to registers */
+       adm1026_write_value(client, ADM1026_REG_CONFIG2, data->config2);
+       data->config3 = (data->config3 & 0x3f)
+                       | ((data->gpio_config[16] & 0x03) << 6);
+       adm1026_write_value(client, ADM1026_REG_CONFIG3, data->config3);
+       for (i = 15, value = 0;i >= 0;--i) {
+               value <<= 2;
+               value |= data->gpio_config[i] & 0x03;
+               if ((i & 0x03) == 0) {
+                       adm1026_write_value(client,
+                                       ADM1026_REG_GPIO_CFG_0_3 + i/4,
+                                       value);
+                       value = 0;
+               }
+       }
+
+       /* Print the new config */
+       adm1026_print_gpio(client);
+}
+
+
+static struct adm1026_data *adm1026_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int i;
+       long value, alarms, gpio;
+
+       down(&data->update_lock);
+       if (!data->valid
+           || (jiffies - data->last_reading > ADM1026_DATA_INTERVAL)) {
+               /* Things that change quickly */
+               dev_dbg(&client->dev,"(%d): Reading sensor values\n", 
+                       client->id);
+               for (i = 0;i <= 16;++i) {
+                       data->in[i] =
+                           adm1026_read_value(client, ADM1026_REG_IN[i]);
+               }
+
+               for (i = 0;i <= 7;++i) {
+                       data->fan[i] =
+                           adm1026_read_value(client, ADM1026_REG_FAN(i));
+               }
+
+               for (i = 0;i <= 2;++i) {
+                       /* NOTE: temp[] is s8 and we assume 2's complement
+                        *   "conversion" in the assignment   */
+                       data->temp[i] =
+                           adm1026_read_value(client, ADM1026_REG_TEMP[i]);
+               }
+
+               data->pwm1.pwm = adm1026_read_value(client, 
+                       ADM1026_REG_PWM);
+               data->analog_out = adm1026_read_value(client, 
+                       ADM1026_REG_DAC);
+               /* GPIO16 is MSbit of alarms, move it to gpio */
+               alarms = adm1026_read_value(client, ADM1026_REG_STATUS4);
+               gpio = alarms & 0x80 ? 0x0100 : 0;  /* GPIO16 */
+               alarms &= 0x7f;
+               alarms <<= 8;
+               alarms |= adm1026_read_value(client, ADM1026_REG_STATUS3);
+               alarms <<= 8;
+               alarms |= adm1026_read_value(client, ADM1026_REG_STATUS2);
+               alarms <<= 8;
+               alarms |= adm1026_read_value(client, ADM1026_REG_STATUS1);
+               data->alarms = alarms;
+
+               /* Read the GPIO values */
+               gpio |= adm1026_read_value(client, 
+                       ADM1026_REG_GPIO_STATUS_8_15);
+               gpio <<= 8;
+               gpio |= adm1026_read_value(client, 
+                       ADM1026_REG_GPIO_STATUS_0_7);
+               data->gpio = gpio;
+
+               data->last_reading = jiffies;
+       };  /* last_reading */
+
+       if (!data->valid || (jiffies - data->last_config > 
+               ADM1026_CONFIG_INTERVAL)) {
+               /* Things that don't change often */
+               dev_dbg(&client->dev, "(%d): Reading config values\n", 
+                       client->id);
+               for (i = 0;i <= 16;++i) {
+                       data->in_min[i] = adm1026_read_value(client, 
+                               ADM1026_REG_IN_MIN[i]);
+                       data->in_max[i] = adm1026_read_value(client, 
+                               ADM1026_REG_IN_MAX[i]);
+               }
+
+               value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3)
+                       | (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7)
+                       << 8);
+               for (i = 0;i <= 7;++i) {
+                       data->fan_min[i] = adm1026_read_value(client, 
+                               ADM1026_REG_FAN_MIN(i));
+                       data->fan_div[i] = DIV_FROM_REG(value & 0x03);
+                       value >>= 2;
+               }
+
+               for (i = 0; i <= 2; ++i) {
+                       /* NOTE: temp_xxx[] are s8 and we assume 2's 
+                        *    complement "conversion" in the assignment
+                        */
+                       data->temp_min[i] = adm1026_read_value(client, 
+                               ADM1026_REG_TEMP_MIN[i]);
+                       data->temp_max[i] = adm1026_read_value(client, 
+                               ADM1026_REG_TEMP_MAX[i]);
+                       data->temp_tmin[i] = adm1026_read_value(client, 
+                               ADM1026_REG_TEMP_TMIN[i]);
+                       data->temp_crit[i] = adm1026_read_value(client, 
+                               ADM1026_REG_TEMP_THERM[i]);
+                       data->temp_offset[i] = adm1026_read_value(client, 
+                               ADM1026_REG_TEMP_OFFSET[i]);
+               }
+
+               /* Read the STATUS/alarm masks */
+               alarms  = adm1026_read_value(client, ADM1026_REG_MASK4);
+               gpio    = alarms & 0x80 ? 0x0100 : 0;  /* GPIO16 */
+               alarms  = (alarms & 0x7f) << 8;
+               alarms |= adm1026_read_value(client, ADM1026_REG_MASK3);
+               alarms <<= 8;
+               alarms |= adm1026_read_value(client, ADM1026_REG_MASK2);
+               alarms <<= 8;
+               alarms |= adm1026_read_value(client, ADM1026_REG_MASK1);
+               data->alarm_mask = alarms;
+
+               /* Read the GPIO values */
+               gpio |= adm1026_read_value(client, 
+                       ADM1026_REG_GPIO_MASK_8_15);
+               gpio <<= 8;
+               gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_MASK_0_7);
+               data->gpio_mask = gpio;
+
+               /* Read various values from CONFIG1 */
+               data->config1 = adm1026_read_value(client, 
+                       ADM1026_REG_CONFIG1);
+               if (data->config1 & CFG1_PWM_AFC) {
+                       data->pwm1.enable = 2;
+                       data->pwm1.auto_pwm_min = 
+                               PWM_MIN_FROM_REG(data->pwm1.pwm);
+               }
+               /* Read the GPIO config */
+               data->config2 = adm1026_read_value(client, 
+                       ADM1026_REG_CONFIG2);
+               data->config3 = adm1026_read_value(client, 
+                       ADM1026_REG_CONFIG3);
+               data->gpio_config[16] = (data->config3 >> 6) & 0x03;
+
+               value = 0;
+               for (i = 0;i <= 15;++i) {
+                       if ((i & 0x03) == 0) {
+                               value = adm1026_read_value(client,
+                                           ADM1026_REG_GPIO_CFG_0_3 + i/4);
+                       }
+                       data->gpio_config[i] = value & 0x03;
+                       value >>= 2;
+               }
+
+               data->last_config = jiffies;
+       };  /* last_config */
+
+       dev_dbg(&client->dev, "(%d): Setting VID from GPIO11-15.\n", 
+               client->id);
+       data->vid = (data->gpio >> 11) & 0x1f;
+       data->valid = 1;
+       up(&data->update_lock);
+       return data;
+}
+
+static ssize_t show_in(struct device *dev, char *buf, int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in[nr]));
+}
+static ssize_t show_in_min(struct device *dev, char *buf, int nr) 
+{
+       struct adm1026_data *data = adm1026_update_device(dev); 
+       return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_min[nr]));
+}
+static ssize_t set_in_min(struct device *dev, const char *buf, 
+               size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->in_min[nr] = INS_TO_REG(nr, val);
+       adm1026_write_value(client, ADM1026_REG_IN_MIN[nr], data->in_min[nr]);
+       up(&data->update_lock);
+       return count; 
+}
+static ssize_t show_in_max(struct device *dev, char *buf, int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_max[nr]));
+}
+static ssize_t set_in_max(struct device *dev, const char *buf,
+               size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->in_max[nr] = INS_TO_REG(nr, val);
+       adm1026_write_value(client, ADM1026_REG_IN_MAX[nr], data->in_max[nr]);
+       up(&data->update_lock);
+       return count;
+}
+
+#define in_reg(offset)                                                    \
+static ssize_t show_in##offset (struct device *dev, char *buf)            \
+{                                                                         \
+       return show_in(dev, buf, offset);                                 \
+}                                                                         \
+static ssize_t show_in##offset##_min (struct device *dev, char *buf)      \
+{                                                                         \
+       return show_in_min(dev, buf, offset);                             \
+}                                                                         \
+static ssize_t set_in##offset##_min (struct device *dev,                  \
+       const char *buf, size_t count)                                    \
+{                                                                         \
+       return set_in_min(dev, buf, count, offset);                       \
+}                                                                         \
+static ssize_t show_in##offset##_max (struct device *dev, char *buf)      \
+{                                                                         \
+       return show_in_max(dev, buf, offset);                             \
+}                                                                         \
+static ssize_t set_in##offset##_max (struct device *dev,                  \
+       const char *buf, size_t count)                                    \
+{                                                                         \
+       return set_in_max(dev, buf, count, offset);                       \
+}                                                                         \
+static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);   \
+static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR,                   \
+               show_in##offset##_min, set_in##offset##_min);             \
+static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR,                   \
+               show_in##offset##_max, set_in##offset##_max);
+
+
+in_reg(0);
+in_reg(1);
+in_reg(2);
+in_reg(3);
+in_reg(4);
+in_reg(5);
+in_reg(6);
+in_reg(7);
+in_reg(8);
+in_reg(9);
+in_reg(10);
+in_reg(11);
+in_reg(12);
+in_reg(13);
+in_reg(14);
+in_reg(15);
+
+static ssize_t show_in16(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", INS_FROM_REG(16, data->in[16]) -
+               NEG12_OFFSET);
+}
+static ssize_t show_in16_min(struct device *dev, char *buf) 
+{
+       struct adm1026_data *data = adm1026_update_device(dev); 
+       return sprintf(buf,"%d\n", INS_FROM_REG(16, data->in_min[16])
+               - NEG12_OFFSET);
+}
+static ssize_t set_in16_min(struct device *dev, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->in_min[16] = INS_TO_REG(16, val + NEG12_OFFSET);
+       adm1026_write_value(client, ADM1026_REG_IN_MIN[16], data->in_min[16]);
+       up(&data->update_lock);
+       return count; 
+}
+static ssize_t show_in16_max(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", INS_FROM_REG(16, data->in_max[16])
+                       - NEG12_OFFSET);
+}
+static ssize_t set_in16_max(struct device *dev, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->in_max[16] = INS_TO_REG(16, val+NEG12_OFFSET);
+       adm1026_write_value(client, ADM1026_REG_IN_MAX[16], data->in_max[16]);
+       up(&data->update_lock);
+       return count;
+}
+
+static DEVICE_ATTR(in16_input, S_IRUGO, show_in16, NULL);
+static DEVICE_ATTR(in16_min, S_IRUGO | S_IWUSR, show_in16_min, set_in16_min);
+static DEVICE_ATTR(in16_max, S_IRUGO | S_IWUSR, show_in16_max, set_in16_max);
+
+
+
+
+/* Now add fan read/write functions */
+
+static ssize_t show_fan(struct device *dev, char *buf, int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr],
+               data->fan_div[nr]));
+}
+static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
+               data->fan_div[nr]));
+}
+static ssize_t set_fan_min(struct device *dev, const char *buf,
+               size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->fan_min[nr] = FAN_TO_REG(val, data->fan_div[nr]);
+       adm1026_write_value(client, ADM1026_REG_FAN_MIN(nr),
+               data->fan_min[nr]);
+       up(&data->update_lock);
+       return count;
+}
+
+#define fan_offset(offset)                                                  \
+static ssize_t show_fan_##offset (struct device *dev, char *buf)            \
+{                                                                           \
+       return show_fan(dev, buf, offset - 1);                              \
+}                                                                           \
+static ssize_t show_fan_##offset##_min (struct device *dev, char *buf)      \
+{                                                                           \
+       return show_fan_min(dev, buf, offset - 1);                          \
+}                                                                           \
+static ssize_t set_fan_##offset##_min (struct device *dev,                  \
+       const char *buf, size_t count)                                      \
+{                                                                           \
+       return set_fan_min(dev, buf, count, offset - 1);                    \
+}                                                                           \
+static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);  \
+static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR,                    \
+               show_fan_##offset##_min, set_fan_##offset##_min);
+
+fan_offset(1);
+fan_offset(2);
+fan_offset(3);
+fan_offset(4);
+fan_offset(5);
+fan_offset(6);
+fan_offset(7);
+fan_offset(8);
+
+/* Adjust fan_min to account for new fan divisor */
+static void fixup_fan_min(struct device *dev, int fan, int old_div)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int    new_min;
+       int    new_div = data->fan_div[fan];
+
+       /* 0 and 0xff are special.  Don't adjust them */
+       if (data->fan_min[fan] == 0 || data->fan_min[fan] == 0xff) {
+               return;
+       }
+
+       new_min = data->fan_min[fan] * old_div / new_div;
+       new_min = SENSORS_LIMIT(new_min, 1, 254);
+       data->fan_min[fan] = new_min;
+       adm1026_write_value(client, ADM1026_REG_FAN_MIN(fan), new_min);
+}
+
+/* Now add fan_div read/write functions */
+static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", data->fan_div[nr]);
+}
+static ssize_t set_fan_div(struct device *dev, const char *buf,
+               size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int    val,orig_div,new_div,shift;
+
+       val = simple_strtol(buf, NULL, 10);
+       new_div = DIV_TO_REG(val); 
+       if (new_div == 0) {
+               return -EINVAL;
+       }
+       down(&data->update_lock);
+       orig_div = data->fan_div[nr];
+       data->fan_div[nr] = DIV_FROM_REG(new_div);
+
+       if (nr < 4) { /* 0 <= nr < 4 */
+               shift = 2 * nr;
+               adm1026_write_value(client, ADM1026_REG_FAN_DIV_0_3,
+                       ((DIV_TO_REG(orig_div) & (~(0x03 << shift))) |
+                       (new_div << shift)));
+       } else { /* 3 < nr < 8 */
+               shift = 2 * (nr - 4);
+               adm1026_write_value(client, ADM1026_REG_FAN_DIV_4_7,
+                       ((DIV_TO_REG(orig_div) & (~(0x03 << (2 * shift)))) |
+                       (new_div << shift)));
+       }
+
+       if (data->fan_div[nr] != orig_div) {
+               fixup_fan_min(dev,nr,orig_div);
+       }
+       up(&data->update_lock);
+       return count;
+}
+
+#define fan_offset_div(offset)                                          \
+static ssize_t show_fan_##offset##_div (struct device *dev, char *buf)  \
+{                                                                       \
+       return show_fan_div(dev, buf, offset - 1);                      \
+}                                                                       \
+static ssize_t set_fan_##offset##_div (struct device *dev,              \
+       const char *buf, size_t count)                                  \
+{                                                                       \
+       return set_fan_div(dev, buf, count, offset - 1);                \
+}                                                                       \
+static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR,                \
+               show_fan_##offset##_div, set_fan_##offset##_div);
+
+fan_offset_div(1);
+fan_offset_div(2);
+fan_offset_div(3);
+fan_offset_div(4);
+fan_offset_div(5);
+fan_offset_div(6);
+fan_offset_div(7);
+fan_offset_div(8);
+
+/* Temps */
+static ssize_t show_temp(struct device *dev, char *buf, int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp[nr]));
+}
+static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_min[nr]));
+}
+static ssize_t set_temp_min(struct device *dev, const char *buf,
+               size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->temp_min[nr] = TEMP_TO_REG(val);
+       adm1026_write_value(client, ADM1026_REG_TEMP_MIN[nr],
+               data->temp_min[nr]);
+       up(&data->update_lock);
+       return count;
+}
+static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_max[nr]));
+}
+static ssize_t set_temp_max(struct device *dev, const char *buf,
+               size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->temp_max[nr] = TEMP_TO_REG(val);
+       adm1026_write_value(client, ADM1026_REG_TEMP_MAX[nr],
+               data->temp_max[nr]);
+       up(&data->update_lock);
+       return count;
+}
+#define temp_reg(offset)                                                      \
+static ssize_t show_temp_##offset (struct device *dev, char *buf)             \
+{                                                                             \
+       return show_temp(dev, buf, offset - 1);                               \
+}                                                                             \
+static ssize_t show_temp_##offset##_min (struct device *dev, char *buf)       \
+{                                                                             \
+       return show_temp_min(dev, buf, offset - 1);                           \
+}                                                                             \
+static ssize_t show_temp_##offset##_max (struct device *dev, char *buf)       \
+{                                                                             \
+       return show_temp_max(dev, buf, offset - 1);                           \
+}                                                                             \
+static ssize_t set_temp_##offset##_min (struct device *dev,                   \
+       const char *buf, size_t count)                                        \
+{                                                                             \
+       return set_temp_min(dev, buf, count, offset - 1);                     \
+}                                                                             \
+static ssize_t set_temp_##offset##_max (struct device *dev,                   \
+       const char *buf, size_t count)                                        \
+{                                                                             \
+       return set_temp_max(dev, buf, count, offset - 1);                     \
+}                                                                             \
+static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL);  \
+static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR,                     \
+               show_temp_##offset##_min, set_temp_##offset##_min);           \
+static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR,                     \
+               show_temp_##offset##_max, set_temp_##offset##_max);
+
+
+temp_reg(1);
+temp_reg(2);
+temp_reg(3);
+
+static ssize_t show_temp_offset(struct device *dev, char *buf, int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_offset[nr]));
+}
+static ssize_t set_temp_offset(struct device *dev, const char *buf,
+               size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->temp_offset[nr] = TEMP_TO_REG(val);
+       adm1026_write_value(client, ADM1026_REG_TEMP_OFFSET[nr],
+               data->temp_offset[nr]);
+       up(&data->update_lock);
+       return count;
+}
+
+#define temp_offset_reg(offset)                                             \
+static ssize_t show_temp_##offset##_offset (struct device *dev, char *buf)  \
+{                                                                           \
+       return show_temp_offset(dev, buf, offset - 1);                      \
+}                                                                           \
+static ssize_t set_temp_##offset##_offset (struct device *dev,              \
+       const char *buf, size_t count)                                      \
+{                                                                           \
+       return set_temp_offset(dev, buf, count, offset - 1);                \
+}                                                                           \
+static DEVICE_ATTR(temp##offset##_offset, S_IRUGO | S_IWUSR,                \
+               show_temp_##offset##_offset, set_temp_##offset##_offset);
+
+temp_offset_reg(1);
+temp_offset_reg(2);
+temp_offset_reg(3);
+
+static ssize_t show_temp_auto_point1_temp_hyst(struct device *dev, char *buf,
+               int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", TEMP_FROM_REG(
+               ADM1026_FAN_ACTIVATION_TEMP_HYST + data->temp_tmin[nr]));
+}
+static ssize_t show_temp_auto_point2_temp(struct device *dev, char *buf,
+               int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_tmin[nr] +
+               ADM1026_FAN_CONTROL_TEMP_RANGE));
+}
+static ssize_t show_temp_auto_point1_temp(struct device *dev, char *buf,
+               int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_tmin[nr]));
+}
+static ssize_t set_temp_auto_point1_temp(struct device *dev, const char *buf,
+               size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->temp_tmin[nr] = TEMP_TO_REG(val);
+       adm1026_write_value(client, ADM1026_REG_TEMP_TMIN[nr],
+               data->temp_tmin[nr]);
+       up(&data->update_lock);
+       return count;
+}
+
+#define temp_auto_point(offset)                                             \
+static ssize_t show_temp##offset##_auto_point1_temp (struct device *dev,    \
+       char *buf)                                                          \
+{                                                                           \
+       return show_temp_auto_point1_temp(dev, buf, offset - 1);            \
+}                                                                           \
+static ssize_t set_temp##offset##_auto_point1_temp (struct device *dev,     \
+       const char *buf, size_t count)                                      \
+{                                                                           \
+       return set_temp_auto_point1_temp(dev, buf, count, offset - 1);      \
+}                                                                           \
+static ssize_t show_temp##offset##_auto_point1_temp_hyst (struct device     \
+       *dev, char *buf)                                                    \
+{                                                                           \
+       return show_temp_auto_point1_temp_hyst(dev, buf, offset - 1);       \
+}                                                                           \
+static ssize_t show_temp##offset##_auto_point2_temp (struct device *dev,    \
+        char *buf)                                                         \
+{                                                                           \
+       return show_temp_auto_point2_temp(dev, buf, offset - 1);            \
+}                                                                           \
+static DEVICE_ATTR(temp##offset##_auto_point1_temp, S_IRUGO | S_IWUSR,      \
+               show_temp##offset##_auto_point1_temp,                       \
+               set_temp##offset##_auto_point1_temp);                       \
+static DEVICE_ATTR(temp##offset##_auto_point1_temp_hyst, S_IRUGO,           \
+               show_temp##offset##_auto_point1_temp_hyst, NULL);           \
+static DEVICE_ATTR(temp##offset##_auto_point2_temp, S_IRUGO,                \
+               show_temp##offset##_auto_point2_temp, NULL);
+
+temp_auto_point(1);
+temp_auto_point(2);
+temp_auto_point(3);
+
+static ssize_t show_temp_crit_enable(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", (data->config1 & CFG1_THERM_HOT) >> 4);
+}
+static ssize_t set_temp_crit_enable(struct device *dev, const char *buf,
+               size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       val = simple_strtol(buf, NULL, 10);
+       if ((val == 1) || (val==0)) {
+               down(&data->update_lock);
+               data->config1 = (data->config1 & ~CFG1_THERM_HOT) | (val << 4);
+               adm1026_write_value(client, ADM1026_REG_CONFIG1, 
+                       data->config1);
+               up(&data->update_lock);
+       }
+       return count;
+}
+
+static DEVICE_ATTR(temp1_crit_enable, S_IRUGO | S_IWUSR, 
+       show_temp_crit_enable, set_temp_crit_enable);
+
+static DEVICE_ATTR(temp2_crit_enable, S_IRUGO | S_IWUSR, 
+       show_temp_crit_enable, set_temp_crit_enable);
+
+static DEVICE_ATTR(temp3_crit_enable, S_IRUGO | S_IWUSR, 
+       show_temp_crit_enable, set_temp_crit_enable);
+
+
+static ssize_t show_temp_crit(struct device *dev, char *buf, int nr)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_crit[nr]));
+}
+static ssize_t set_temp_crit(struct device *dev, const char *buf,
+               size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->temp_crit[nr] = TEMP_TO_REG(val);
+       adm1026_write_value(client, ADM1026_REG_TEMP_THERM[nr],
+               data->temp_crit[nr]);
+       up(&data->update_lock);
+       return count;
+}
+
+#define temp_crit_reg(offset)                                             \
+static ssize_t show_temp_##offset##_crit (struct device *dev, char *buf)  \
+{                                                                         \
+       return show_temp_crit(dev, buf, offset - 1);                      \
+}                                                                         \
+static ssize_t set_temp_##offset##_crit (struct device *dev,              \
+       const char *buf, size_t count)                                    \
+{                                                                         \
+       return set_temp_crit(dev, buf, count, offset - 1);                \
+}                                                                         \
+static DEVICE_ATTR(temp##offset##_crit, S_IRUGO | S_IWUSR,                \
+               show_temp_##offset##_crit, set_temp_##offset##_crit);
+
+temp_crit_reg(1);
+temp_crit_reg(2);
+temp_crit_reg(3);
+
+static ssize_t show_analog_out_reg(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", DAC_FROM_REG(data->analog_out));
+}
+static ssize_t set_analog_out_reg(struct device *dev, const char *buf,
+               size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->analog_out = DAC_TO_REG(val);
+       adm1026_write_value(client, ADM1026_REG_DAC, data->analog_out);
+       up(&data->update_lock);
+       return count;
+}
+
+static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg, 
+       set_analog_out_reg);
+
+static ssize_t show_vid_reg(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", vid_from_reg(data->vid & 0x3f, data->vrm));
+}
+
+static DEVICE_ATTR(vid, S_IRUGO, show_vid_reg, NULL);
+
+static ssize_t show_vrm_reg(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", data->vrm);
+}
+static ssize_t store_vrm_reg(struct device *dev, const char *buf,
+               size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+
+       data->vrm = simple_strtol(buf, NULL, 10);
+       return count;
+}
+
+static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
+
+static ssize_t show_alarms_reg(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf, "%ld\n", (long) (data->alarms));
+}
+
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+
+static ssize_t show_alarm_mask(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%ld\n", data->alarm_mask);
+}
+static ssize_t set_alarm_mask(struct device *dev, const char *buf,
+               size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+       unsigned long mask;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->alarm_mask = val & 0x7fffffff;
+       mask = data->alarm_mask
+               | (data->gpio_mask & 0x10000 ? 0x80000000 : 0);
+       adm1026_write_value(client, ADM1026_REG_MASK1,
+               mask & 0xff);
+       mask >>= 8;
+       adm1026_write_value(client, ADM1026_REG_MASK2,
+               mask & 0xff);
+       mask >>= 8;
+       adm1026_write_value(client, ADM1026_REG_MASK3,
+               mask & 0xff);
+       mask >>= 8;
+       adm1026_write_value(client, ADM1026_REG_MASK4,
+               mask & 0xff);
+       up(&data->update_lock);
+       return count;
+}
+
+static DEVICE_ATTR(alarm_mask, S_IRUGO | S_IWUSR, show_alarm_mask,
+       set_alarm_mask);
+
+
+static ssize_t show_gpio(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%ld\n", data->gpio);
+}
+static ssize_t set_gpio(struct device *dev, const char *buf,
+               size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+       long   gpio;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->gpio = val & 0x1ffff;
+       gpio = data->gpio;
+       adm1026_write_value(client, ADM1026_REG_GPIO_STATUS_0_7,gpio & 0xff);
+       gpio >>= 8;
+       adm1026_write_value(client, ADM1026_REG_GPIO_STATUS_8_15,gpio & 0xff);
+       gpio = ((gpio >> 1) & 0x80) | (data->alarms >> 24 & 0x7f);
+       adm1026_write_value(client, ADM1026_REG_STATUS4,gpio & 0xff);
+       up(&data->update_lock);
+       return count;
+}
+
+static DEVICE_ATTR(gpio, S_IRUGO | S_IWUSR, show_gpio, set_gpio);
+
+
+static ssize_t show_gpio_mask(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%ld\n", data->gpio_mask);
+}
+static ssize_t set_gpio_mask(struct device *dev, const char *buf,
+               size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+       long   mask;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->gpio_mask = val & 0x1ffff;
+       mask = data->gpio_mask;
+       adm1026_write_value(client, ADM1026_REG_GPIO_MASK_0_7,mask & 0xff);
+       mask >>= 8;
+       adm1026_write_value(client, ADM1026_REG_GPIO_MASK_8_15,mask & 0xff);
+       mask = ((mask >> 1) & 0x80) | (data->alarm_mask >> 24 & 0x7f);
+       adm1026_write_value(client, ADM1026_REG_MASK1,mask & 0xff);
+       up(&data->update_lock);
+       return count;
+}
+
+static DEVICE_ATTR(gpio_mask, S_IRUGO | S_IWUSR, show_gpio_mask, set_gpio_mask);
+
+static ssize_t show_pwm_reg(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", PWM_FROM_REG(data->pwm1.pwm));
+}
+static ssize_t set_pwm_reg(struct device *dev, const char *buf,
+               size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       if (data->pwm1.enable == 1) {
+               down(&data->update_lock);
+               val = simple_strtol(buf, NULL, 10);
+               data->pwm1.pwm = PWM_TO_REG(val);
+               adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm);
+               up(&data->update_lock);
+       }
+       return count;
+}
+static ssize_t show_auto_pwm_min(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", data->pwm1.auto_pwm_min);
+}
+static ssize_t set_auto_pwm_min(struct device *dev, const char *buf,
+               size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+
+       down(&data->update_lock);
+       val = simple_strtol(buf, NULL, 10);
+       data->pwm1.auto_pwm_min = SENSORS_LIMIT(val,0,255);
+       if (data->pwm1.enable == 2) { /* apply immediately */
+               data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) |
+                       PWM_MIN_TO_REG(data->pwm1.auto_pwm_min)); 
+               adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm);
+       }
+       up(&data->update_lock);
+       return count;
+}
+static ssize_t show_auto_pwm_max(struct device *dev, char *buf)
+{
+       return sprintf(buf,"%d\n", ADM1026_PWM_MAX);
+}
+static ssize_t show_pwm_enable(struct device *dev, char *buf)
+{
+       struct adm1026_data *data = adm1026_update_device(dev);
+       return sprintf(buf,"%d\n", data->pwm1.enable);
+}
+static ssize_t set_pwm_enable(struct device *dev, const char *buf,
+               size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adm1026_data *data = i2c_get_clientdata(client);
+       int     val;
+       int     old_enable;
+
+       val = simple_strtol(buf, NULL, 10);
+       if ((val >= 0) && (val < 3)) {
+               down(&data->update_lock);
+               old_enable = data->pwm1.enable;
+               data->pwm1.enable = val;
+               data->config1 = (data->config1 & ~CFG1_PWM_AFC)
+                               | ((val == 2) ? CFG1_PWM_AFC : 0);
+               adm1026_write_value(client, ADM1026_REG_CONFIG1,
+                       data->config1);
+               if (val == 2) {  /* apply pwm1_auto_pwm_min to pwm1 */
+                       data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) |
+                               PWM_MIN_TO_REG(data->pwm1.auto_pwm_min)); 
+                       adm1026_write_value(client, ADM1026_REG_PWM, 
+                               data->pwm1.pwm);
+               } else if (!((old_enable == 1) && (val == 1))) {
+                       /* set pwm to safe value */
+                       data->pwm1.pwm = 255;
+                       adm1026_write_value(client, ADM1026_REG_PWM, 
+                               data->pwm1.pwm);
+               }
+               up(&data->update_lock);
+       }
+       return count;
+}
+
+/* enable PWM fan control */
+static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg); 
+static DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg); 
+static DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg); 
+static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, 
+       set_pwm_enable);
+static DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, show_pwm_enable, 
+       set_pwm_enable);
+static DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, show_pwm_enable, 
+       set_pwm_enable);
+static DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO | S_IWUSR, 
+       show_auto_pwm_min, set_auto_pwm_min);
+static DEVICE_ATTR(temp2_auto_point1_pwm, S_IRUGO | S_IWUSR, 
+       show_auto_pwm_min, set_auto_pwm_min);
+static DEVICE_ATTR(temp3_auto_point1_pwm, S_IRUGO | S_IWUSR, 
+       show_auto_pwm_min, set_auto_pwm_min);
+
+static DEVICE_ATTR(temp1_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
+static DEVICE_ATTR(temp2_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
+static DEVICE_ATTR(temp3_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
+
+int adm1026_detect(struct i2c_adapter *adapter, int address,
+               int kind)
+{
+       int company, verstep;
+       struct i2c_client *new_client;
+       struct adm1026_data *data;
+       int err = 0;
+       const char *type_name = "";
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+               /* We need to be able to do byte I/O */
+               goto exit;
+       };
+
+       /* OK. For now, we presume we have a valid client. We now create the
+          client structure, even though we cannot fill it completely yet.
+          But it allows us to access adm1026_{read,write}_value. */
+
+       if (!(data = kmalloc(sizeof(struct adm1026_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       memset(data, 0, sizeof(struct adm1026_data));
+
+       new_client = &data->client;
+       i2c_set_clientdata(new_client, data);
+       new_client->addr = address;
+       new_client->adapter = adapter;
+       new_client->driver = &adm1026_driver;
+       new_client->flags = 0;
+
+       /* Now, we do the remaining detection. */
+
+       company = adm1026_read_value(new_client, ADM1026_REG_COMPANY);
+       verstep = adm1026_read_value(new_client, ADM1026_REG_VERSTEP);
+
+       dev_dbg(&new_client->dev, "Detecting device at %d,0x%02x with"
+               " COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+               i2c_adapter_id(new_client->adapter), new_client->addr,
+               company, verstep);
+
+       /* If auto-detecting, Determine the chip type. */
+       if (kind <= 0) {
+               dev_dbg(&new_client->dev, "Autodetecting device at %d,0x%02x "
+                       "...\n", i2c_adapter_id(adapter), address);
+               if (company == ADM1026_COMPANY_ANALOG_DEV
+                   && verstep == ADM1026_VERSTEP_ADM1026) {
+                       kind = adm1026;
+               } else if (company == ADM1026_COMPANY_ANALOG_DEV
+                       && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
+                       dev_err(&adapter->dev, ": Unrecognized stepping "
+                               "0x%02x. Defaulting to ADM1026.\n", verstep);
+                       kind = adm1026;
+               } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
+                       dev_err(&adapter->dev, ": Found version/stepping "
+                               "0x%02x. Assuming generic ADM1026.\n",
+                               verstep);
+                       kind = any_chip;
+               } else {
+                       dev_dbg(&new_client->dev, ": Autodetection "
+                               "failed\n");
+                       /* Not an ADM1026 ... */
+                       if (kind == 0)  { /* User used force=x,y */
+                               dev_err(&adapter->dev, "Generic ADM1026 not "
+                                       "found at %d,0x%02x.  Try "
+                                       "force_adm1026.\n",
+                                       i2c_adapter_id(adapter), address);
+                       }
+                       err = 0;
+                       goto exitfree;
+               }
+       }
+
+       /* Fill in the chip specific driver values */
+       switch (kind) {
+       case any_chip :
+               type_name = "adm1026";
+               break;
+       case adm1026 :
+               type_name = "adm1026";
+               break;
+       default :
+               dev_err(&adapter->dev, ": Internal error, invalid "
+                       "kind (%d)!", kind);
+               err = -EFAULT;
+               goto exitfree;
+       }
+       strlcpy(new_client->name, type_name, I2C_NAME_SIZE);
+
+       /* Fill in the remaining client fields */
+       new_client->id = adm1026_id++;
+       data->type = kind;
+       data->valid = 0;
+       init_MUTEX(&data->update_lock);
+
+       dev_dbg(&new_client->dev, "(%d): Assigning ID %d to %s at %d,0x%02x\n",
+               new_client->id, new_client->id, new_client->name,
+               i2c_adapter_id(new_client->adapter),
+               new_client->addr);
+
+       /* Tell the I2C layer a new client has arrived */
+       if ((err = i2c_attach_client(new_client)))
+               goto exitfree;
+
+       /* Set the VRM version */
+       data->vrm = i2c_which_vrm();
+
+       /* Initialize the ADM1026 chip */
+       adm1026_init_client(new_client);
+
+       /* Register sysfs hooks */
+       device_create_file(&new_client->dev, &dev_attr_in0_input);
+       device_create_file(&new_client->dev, &dev_attr_in0_max);
+       device_create_file(&new_client->dev, &dev_attr_in0_min);
+       device_create_file(&new_client->dev, &dev_attr_in1_input);
+       device_create_file(&new_client->dev, &dev_attr_in1_max);
+       device_create_file(&new_client->dev, &dev_attr_in1_min);
+       device_create_file(&new_client->dev, &dev_attr_in2_input);
+       device_create_file(&new_client->dev, &dev_attr_in2_max);
+       device_create_file(&new_client->dev, &dev_attr_in2_min);
+       device_create_file(&new_client->dev, &dev_attr_in3_input);
+       device_create_file(&new_client->dev, &dev_attr_in3_max);
+       device_create_file(&new_client->dev, &dev_attr_in3_min);
+       device_create_file(&new_client->dev, &dev_attr_in4_input);
+       device_create_file(&new_client->dev, &dev_attr_in4_max);
+       device_create_file(&new_client->dev, &dev_attr_in4_min);
+       device_create_file(&new_client->dev, &dev_attr_in5_input);
+       device_create_file(&new_client->dev, &dev_attr_in5_max);
+       device_create_file(&new_client->dev, &dev_attr_in5_min);
+       device_create_file(&new_client->dev, &dev_attr_in6_input);
+       device_create_file(&new_client->dev, &dev_attr_in6_max);
+       device_create_file(&new_client->dev, &dev_attr_in6_min);
+       device_create_file(&new_client->dev, &dev_attr_in7_input);
+       device_create_file(&new_client->dev, &dev_attr_in7_max);
+       device_create_file(&new_client->dev, &dev_attr_in7_min);
+       device_create_file(&new_client->dev, &dev_attr_in8_input);
+       device_create_file(&new_client->dev, &dev_attr_in8_max);
+       device_create_file(&new_client->dev, &dev_attr_in8_min);
+       device_create_file(&new_client->dev, &dev_attr_in9_input);
+       device_create_file(&new_client->dev, &dev_attr_in9_max);
+       device_create_file(&new_client->dev, &dev_attr_in9_min);
+       device_create_file(&new_client->dev, &dev_attr_in10_input);
+       device_create_file(&new_client->dev, &dev_attr_in10_max);
+       device_create_file(&new_client->dev, &dev_attr_in10_min);
+       device_create_file(&new_client->dev, &dev_attr_in11_input);
+       device_create_file(&new_client->dev, &dev_attr_in11_max);
+       device_create_file(&new_client->dev, &dev_attr_in11_min);
+       device_create_file(&new_client->dev, &dev_attr_in12_input);
+       device_create_file(&new_client->dev, &dev_attr_in12_max);
+       device_create_file(&new_client->dev, &dev_attr_in12_min);
+       device_create_file(&new_client->dev, &dev_attr_in13_input);
+       device_create_file(&new_client->dev, &dev_attr_in13_max);
+       device_create_file(&new_client->dev, &dev_attr_in13_min);
+       device_create_file(&new_client->dev, &dev_attr_in14_input);
+       device_create_file(&new_client->dev, &dev_attr_in14_max);
+       device_create_file(&new_client->dev, &dev_attr_in14_min);
+       device_create_file(&new_client->dev, &dev_attr_in15_input);
+       device_create_file(&new_client->dev, &dev_attr_in15_max);
+       device_create_file(&new_client->dev, &dev_attr_in15_min);
+       device_create_file(&new_client->dev, &dev_attr_in16_input);
+       device_create_file(&new_client->dev, &dev_attr_in16_max);
+       device_create_file(&new_client->dev, &dev_attr_in16_min);
+       device_create_file(&new_client->dev, &dev_attr_fan1_input);
+       device_create_file(&new_client->dev, &dev_attr_fan1_div);
+       device_create_file(&new_client->dev, &dev_attr_fan1_min);
+       device_create_file(&new_client->dev, &dev_attr_fan2_input);
+       device_create_file(&new_client->dev, &dev_attr_fan2_div);
+       device_create_file(&new_client->dev, &dev_attr_fan2_min);
+       device_create_file(&new_client->dev, &dev_attr_fan3_input);
+       device_create_file(&new_client->dev, &dev_attr_fan3_div);
+       device_create_file(&new_client->dev, &dev_attr_fan3_min);
+       device_create_file(&new_client->dev, &dev_attr_fan4_input);
+       device_create_file(&new_client->dev, &dev_attr_fan4_div);
+       device_create_file(&new_client->dev, &dev_attr_fan4_min);
+       device_create_file(&new_client->dev, &dev_attr_fan5_input);
+       device_create_file(&new_client->dev, &dev_attr_fan5_div);
+       device_create_file(&new_client->dev, &dev_attr_fan5_min);
+       device_create_file(&new_client->dev, &dev_attr_fan6_input);
+       device_create_file(&new_client->dev, &dev_attr_fan6_div);
+       device_create_file(&new_client->dev, &dev_attr_fan6_min);
+       device_create_file(&new_client->dev, &dev_attr_fan7_input);
+       device_create_file(&new_client->dev, &dev_attr_fan7_div);
+       device_create_file(&new_client->dev, &dev_attr_fan7_min);
+       device_create_file(&new_client->dev, &dev_attr_fan8_input);
+       device_create_file(&new_client->dev, &dev_attr_fan8_div);
+       device_create_file(&new_client->dev, &dev_attr_fan8_min);
+       device_create_file(&new_client->dev, &dev_attr_temp1_input);
+       device_create_file(&new_client->dev, &dev_attr_temp1_max);
+       device_create_file(&new_client->dev, &dev_attr_temp1_min);
+       device_create_file(&new_client->dev, &dev_attr_temp2_input);
+       device_create_file(&new_client->dev, &dev_attr_temp2_max);
+       device_create_file(&new_client->dev, &dev_attr_temp2_min);
+       device_create_file(&new_client->dev, &dev_attr_temp3_input);
+       device_create_file(&new_client->dev, &dev_attr_temp3_max);
+       device_create_file(&new_client->dev, &dev_attr_temp3_min);
+       device_create_file(&new_client->dev, &dev_attr_temp1_offset);
+       device_create_file(&new_client->dev, &dev_attr_temp2_offset);
+       device_create_file(&new_client->dev, &dev_attr_temp3_offset);
+       device_create_file(&new_client->dev, 
+               &dev_attr_temp1_auto_point1_temp);
+       device_create_file(&new_client->dev, 
+               &dev_attr_temp2_auto_point1_temp);
+       device_create_file(&new_client->dev, 
+               &dev_attr_temp3_auto_point1_temp);
+       device_create_file(&new_client->dev,
+               &dev_attr_temp1_auto_point1_temp_hyst);
+       device_create_file(&new_client->dev,
+               &dev_attr_temp2_auto_point1_temp_hyst);
+       device_create_file(&new_client->dev,
+               &dev_attr_temp3_auto_point1_temp_hyst);
+       device_create_file(&new_client->dev, 
+               &dev_attr_temp1_auto_point2_temp);
+       device_create_file(&new_client->dev, 
+               &dev_attr_temp2_auto_point2_temp);
+       device_create_file(&new_client->dev, 
+               &dev_attr_temp3_auto_point2_temp);
+       device_create_file(&new_client->dev, &dev_attr_temp1_crit);
+       device_create_file(&new_client->dev, &dev_attr_temp2_crit);
+       device_create_file(&new_client->dev, &dev_attr_temp3_crit);
+       device_create_file(&new_client->dev, &dev_attr_temp1_crit_enable);
+       device_create_file(&new_client->dev, &dev_attr_temp2_crit_enable);
+       device_create_file(&new_client->dev, &dev_attr_temp3_crit_enable);
+       device_create_file(&new_client->dev, &dev_attr_vid);
+       device_create_file(&new_client->dev, &dev_attr_vrm);
+       device_create_file(&new_client->dev, &dev_attr_alarms);
+       device_create_file(&new_client->dev, &dev_attr_alarm_mask);
+       device_create_file(&new_client->dev, &dev_attr_gpio);
+       device_create_file(&new_client->dev, &dev_attr_gpio_mask);
+       device_create_file(&new_client->dev, &dev_attr_pwm1);
+       device_create_file(&new_client->dev, &dev_attr_pwm2);
+       device_create_file(&new_client->dev, &dev_attr_pwm3);
+       device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
+       device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
+       device_create_file(&new_client->dev, &dev_attr_pwm3_enable);
+       device_create_file(&new_client->dev, &dev_attr_temp1_auto_point1_pwm);
+       device_create_file(&new_client->dev, &dev_attr_temp2_auto_point1_pwm);
+       device_create_file(&new_client->dev, &dev_attr_temp3_auto_point1_pwm);
+       device_create_file(&new_client->dev, &dev_attr_temp1_auto_point2_pwm);
+       device_create_file(&new_client->dev, &dev_attr_temp2_auto_point2_pwm);
+       device_create_file(&new_client->dev, &dev_attr_temp3_auto_point2_pwm);
+       device_create_file(&new_client->dev, &dev_attr_analog_out);
+       return 0;
+
+       /* Error out and cleanup code */
+exitfree:
+       kfree(new_client);
+exit:
+       return err;
+}
+static int __init sm_adm1026_init(void)
+{
+       return i2c_add_driver(&adm1026_driver);
+}
+
+static void  __exit sm_adm1026_exit(void)
+{
+       i2c_del_driver(&adm1026_driver);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Philip Pokorny <ppokorny@penguincomputing.com>, "
+              "Justin Thiessen <jthiessen@penguincomputing.com>");
+MODULE_DESCRIPTION("ADM1026 driver");
+
+module_init(sm_adm1026_init);
+module_exit(sm_adm1026_exit);
diff --git a/drivers/i2c/chips/lm63.c b/drivers/i2c/chips/lm63.c
new file mode 100644 (file)
index 0000000..73b8e83
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+ * lm63.c - driver for the National Semiconductor LM63 temperature sensor
+ *          with integrated fan control
+ * Copyright (C) 2004  Jean Delvare <khali@linux-fr.org>
+ * Based on the lm90 driver.
+ *
+ * The LM63 is a sensor chip made by National Semiconductor. It measures
+ * two temperatures (its own and one external one) and the speed of one
+ * fan, those speed it can additionally control. Complete datasheet can be
+ * obtained from National's website at:
+ *   http://www.national.com/pf/LM/LM63.html
+ *
+ * The LM63 is basically an LM86 with fan speed monitoring and control
+ * capabilities added. It misses some of the LM86 features though:
+ *  - No low limit for local temperature.
+ *  - No critical limit for local temperature.
+ *  - Critical limit for remote temperature can be changed only once. We
+ *    will consider that the critical limit is read-only.
+ *
+ * The datasheet isn't very clear about what the tachometer reading is.
+ * I had a explanation from National Semiconductor though. The two lower
+ * bits of the read value have to be masked out. The value is still 16 bit
+ * in width.
+ *
+ * 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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+
+/*
+ * Addresses to scan
+ * Address is fully defined internally and cannot be changed.
+ */
+
+static unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_1(lm63);
+
+/*
+ * The LM63 registers
+ */
+
+#define LM63_REG_CONFIG1               0x03
+#define LM63_REG_CONFIG2               0xBF
+#define LM63_REG_CONFIG_FAN            0x4A
+
+#define LM63_REG_TACH_COUNT_MSB                0x47
+#define LM63_REG_TACH_COUNT_LSB                0x46
+#define LM63_REG_TACH_LIMIT_MSB                0x49
+#define LM63_REG_TACH_LIMIT_LSB                0x48
+
+#define LM63_REG_PWM_VALUE             0x4C
+#define LM63_REG_PWM_FREQ              0x4D
+
+#define LM63_REG_LOCAL_TEMP            0x00
+#define LM63_REG_LOCAL_HIGH            0x05
+
+#define LM63_REG_REMOTE_TEMP_MSB       0x01
+#define LM63_REG_REMOTE_TEMP_LSB       0x10
+#define LM63_REG_REMOTE_OFFSET_MSB     0x11
+#define LM63_REG_REMOTE_OFFSET_LSB     0x12
+#define LM63_REG_REMOTE_HIGH_MSB       0x07
+#define LM63_REG_REMOTE_HIGH_LSB       0x13
+#define LM63_REG_REMOTE_LOW_MSB                0x08
+#define LM63_REG_REMOTE_LOW_LSB                0x14
+#define LM63_REG_REMOTE_TCRIT          0x19
+#define LM63_REG_REMOTE_TCRIT_HYST     0x21
+
+#define LM63_REG_ALERT_STATUS          0x02
+#define LM63_REG_ALERT_MASK            0x16
+
+#define LM63_REG_MAN_ID                        0xFE
+#define LM63_REG_CHIP_ID               0xFF
+
+/*
+ * Conversions and various macros
+ * For tachometer counts, the LM63 uses 16-bit values.
+ * For local temperature and high limit, remote critical limit and hysteresis
+ * value, it uses signed 8-bit values with LSB = 1 degree Celcius.
+ * For remote temperature, low and high limits, it uses signed 11-bit values
+ * with LSB = 0.125 degree Celcius, left-justified in 16-bit registers.
+ */
+
+#define FAN_FROM_REG(reg)      ((reg) == 0xFFFC || (reg) == 0 ? 0 : \
+                                5400000 / (reg))
+#define FAN_TO_REG(val)                ((val) <= 82 ? 0xFFFC : \
+                                (5400000 / (val)) & 0xFFFC)
+#define TEMP8_FROM_REG(reg)    ((reg) * 1000)
+#define TEMP8_TO_REG(val)      ((val) <= -128000 ? -128 : \
+                                (val) >= 127000 ? 127 : \
+                                (val) < 0 ? ((val) - 500) / 1000 : \
+                                ((val) + 500) / 1000)
+#define TEMP11_FROM_REG(reg)   ((reg) / 32 * 125)
+#define TEMP11_TO_REG(val)     ((val) <= -128000 ? 0x8000 : \
+                                (val) >= 127875 ? 0x7FE0 : \
+                                (val) < 0 ? ((val) - 62) / 125 * 32 : \
+                                ((val) + 62) / 125 * 32)
+#define HYST_TO_REG(val)       ((val) <= 0 ? 0 : \
+                                (val) >= 127000 ? 127 : \
+                                ((val) + 500) / 1000)
+
+/*
+ * Functions declaration
+ */
+
+static int lm63_attach_adapter(struct i2c_adapter *adapter);
+static int lm63_detach_client(struct i2c_client *client);
+
+static struct lm63_data *lm63_update_device(struct device *dev);
+
+static int lm63_detect(struct i2c_adapter *adapter, int address, int kind);
+static void lm63_init_client(struct i2c_client *client);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver lm63_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "lm63",
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = lm63_attach_adapter,
+       .detach_client  = lm63_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm63_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 config, config_fan;
+       u16 fan1_input;
+       u16 fan1_low;
+       u8 pwm1_freq;
+       u8 pwm1_value;
+       s8 temp1_input;
+       s8 temp1_high;
+       s16 temp2_input;
+       s16 temp2_high;
+       s16 temp2_low;
+       s8 temp2_crit;
+       u8 temp2_crit_hyst;
+       u8 alarms;
+};
+
+/*
+ * Sysfs callback functions and files
+ */
+
+#define show_fan(value) \
+static ssize_t show_##value(struct device *dev, char *buf) \
+{ \
+       struct lm63_data *data = lm63_update_device(dev); \
+       return sprintf(buf, "%d\n", FAN_FROM_REG(data->value)); \
+}
+show_fan(fan1_input);
+show_fan(fan1_low);
+
+static ssize_t set_fan1_low(struct device *dev, const char *buf,
+       size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm63_data *data = i2c_get_clientdata(client);
+       unsigned long val = simple_strtoul(buf, NULL, 10);
+       data->fan1_low = FAN_TO_REG(val);
+       i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_LSB,
+                                 data->fan1_low & 0xFF);
+       i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_MSB,
+                                 data->fan1_low >> 8);
+       return count;
+}
+
+static ssize_t show_pwm1(struct device *dev, char *buf)
+{
+       struct lm63_data *data = lm63_update_device(dev);
+       return sprintf(buf, "%d\n", data->pwm1_value >= 2 * data->pwm1_freq ?
+                      255 : (data->pwm1_value * 255 + data->pwm1_freq) /
+                      (2 * data->pwm1_freq));
+}
+
+static ssize_t set_pwm1(struct device *dev, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm63_data *data = i2c_get_clientdata(client);
+       unsigned long val;
+       
+       if (!(data->config_fan & 0x20)) /* register is read-only */
+               return -EPERM;
+
+       val = simple_strtoul(buf, NULL, 10);
+       data->pwm1_value = val <= 0 ? 0 :
+                          val >= 255 ? 2 * data->pwm1_freq :
+                          (val * data->pwm1_freq * 2 + 127) / 255;
+       i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value);
+       return count;
+}
+
+static ssize_t show_pwm1_enable(struct device *dev, char *buf)
+{
+       struct lm63_data *data = lm63_update_device(dev);
+       return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2);
+}
+
+#define show_temp8(value) \
+static ssize_t show_##value(struct device *dev, char *buf) \
+{ \
+       struct lm63_data *data = lm63_update_device(dev); \
+       return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->value)); \
+}
+#define show_temp11(value) \
+static ssize_t show_##value(struct device *dev, char *buf) \
+{ \
+       struct lm63_data *data = lm63_update_device(dev); \
+       return sprintf(buf, "%d\n", TEMP11_FROM_REG(data->value)); \
+}
+show_temp8(temp1_input);
+show_temp8(temp1_high);
+show_temp11(temp2_input);
+show_temp11(temp2_high);
+show_temp11(temp2_low);
+show_temp8(temp2_crit);
+
+#define set_temp8(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 lm63_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->value = TEMP8_TO_REG(val); \
+       i2c_smbus_write_byte_data(client, reg, data->value); \
+       return count; \
+}
+#define set_temp11(value, reg_msb, reg_lsb) \
+static ssize_t set_##value(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct lm63_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->value = TEMP11_TO_REG(val); \
+       i2c_smbus_write_byte_data(client, reg_msb, data->value >> 8); \
+       i2c_smbus_write_byte_data(client, reg_lsb, data->value & 0xff); \
+       return count; \
+}
+set_temp8(temp1_high, LM63_REG_LOCAL_HIGH);
+set_temp11(temp2_high, LM63_REG_REMOTE_HIGH_MSB, LM63_REG_REMOTE_HIGH_LSB);
+set_temp11(temp2_low, LM63_REG_REMOTE_LOW_MSB, LM63_REG_REMOTE_LOW_LSB);
+
+/* Hysteresis register holds a relative value, while we want to present
+   an absolute to user-space */
+static ssize_t show_temp2_crit_hyst(struct device *dev, char *buf)
+{
+       struct lm63_data *data = lm63_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp2_crit)
+                      - TEMP8_FROM_REG(data->temp2_crit_hyst));
+}
+
+/* And now the other way around, user-space provides an absolute
+   hysteresis value and we have to store a relative one */
+static ssize_t set_temp2_crit_hyst(struct device *dev, const char *buf,
+       size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm63_data *data = i2c_get_clientdata(client);
+       int hyst = TEMP8_FROM_REG(data->temp2_crit) -
+                  simple_strtol(buf, NULL, 10);
+       i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST,
+                                 HYST_TO_REG(hyst));
+       return count;
+}
+
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+       struct lm63_data *data = lm63_update_device(dev);
+       return sprintf(buf, "%u\n", data->alarms);
+}
+
+static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan1_input, NULL);
+static DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan1_low,
+       set_fan1_low);
+
+static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1);
+static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL);
+
+static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL);
+static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp1_high,
+       set_temp1_high);
+
+static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp2_input, NULL);
+static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp2_low,
+       set_temp2_low);
+static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp2_high,
+       set_temp2_high);
+static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp2_crit, NULL);
+static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst,
+       set_temp2_crit_hyst);
+
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+/*
+ * Real code
+ */
+
+static int lm63_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_detect(adapter, &addr_data, lm63_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int lm63_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *new_client;
+       struct lm63_data *data;
+       int err = 0;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               goto exit;
+
+       if (!(data = kmalloc(sizeof(struct lm63_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+       memset(data, 0, sizeof(struct lm63_data));
+
+       /* The common I2C client data is placed right before the
+          LM63-specific data. */
+       new_client = &data->client;
+       i2c_set_clientdata(new_client, data);
+       new_client->addr = address;
+       new_client->adapter = adapter;
+       new_client->driver = &lm63_driver;
+       new_client->flags = 0;
+
+       /* Default to an LM63 if forced */
+       if (kind == 0)
+               kind = lm63;
+
+       if (kind < 0) { /* must identify */
+               u8 man_id, chip_id, reg_config1, reg_config2;
+               u8 reg_alert_status, reg_alert_mask;
+
+               man_id = i2c_smbus_read_byte_data(new_client,
+                        LM63_REG_MAN_ID);
+               chip_id = i2c_smbus_read_byte_data(new_client,
+                         LM63_REG_CHIP_ID);
+               reg_config1 = i2c_smbus_read_byte_data(new_client,
+                             LM63_REG_CONFIG1);
+               reg_config2 = i2c_smbus_read_byte_data(new_client,
+                             LM63_REG_CONFIG2);
+               reg_alert_status = i2c_smbus_read_byte_data(new_client,
+                                  LM63_REG_ALERT_STATUS);
+               reg_alert_mask = i2c_smbus_read_byte_data(new_client,
+                                LM63_REG_ALERT_MASK);
+
+               if (man_id == 0x01 /* National Semiconductor */
+                && chip_id == 0x41 /* LM63 */
+                && (reg_config1 & 0x18) == 0x00
+                && (reg_config2 & 0xF8) == 0x00
+                && (reg_alert_status & 0x20) == 0x00
+                && (reg_alert_mask & 0xA4) == 0xA4) {
+                       kind = lm63;
+               } else { /* failed */
+                       dev_dbg(&adapter->dev, "Unsupported chip "
+                               "(man_id=0x%02X, chip_id=0x%02X).\n",
+                               man_id, chip_id);
+                       goto exit_free;
+               }
+       }
+
+       strlcpy(new_client->name, "lm63", I2C_NAME_SIZE);
+       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 LM63 chip */
+       lm63_init_client(new_client);
+
+       /* Register sysfs hooks */
+       if (data->config & 0x04) { /* tachometer enabled */
+               device_create_file(&new_client->dev, &dev_attr_fan1_input);
+               device_create_file(&new_client->dev, &dev_attr_fan1_min);
+       }
+       device_create_file(&new_client->dev, &dev_attr_pwm1);
+       device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
+       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_temp1_max);
+       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;
+}
+
+/* Idealy we shouldn't have to initialize anything, since the BIOS
+   should have taken care of everything */
+static void lm63_init_client(struct i2c_client *client)
+{
+       struct lm63_data *data = i2c_get_clientdata(client);
+
+       data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
+       data->config_fan = i2c_smbus_read_byte_data(client,
+                                                   LM63_REG_CONFIG_FAN);
+
+       /* Start converting if needed */
+       if (data->config & 0x40) { /* standby */
+               dev_dbg(&client->dev, "Switching to operational mode");
+               data->config &= 0xA7;
+               i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
+                                         data->config);
+       }
+
+       /* We may need pwm1_freq before ever updating the client data */
+       data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ);
+       if (data->pwm1_freq == 0)
+               data->pwm1_freq = 1;
+
+       /* Show some debug info about the LM63 configuration */
+       dev_dbg(&client->dev, "Alert/tach pin configured for %s\n",
+               (data->config & 0x04) ? "tachometer input" :
+               "alert output");
+       dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n",
+               (data->config_fan & 0x04) ? "1.4" : "360",
+               ((data->config_fan & 0x04) ? 700 : 180000) / data->pwm1_freq);
+       dev_dbg(&client->dev, "PWM output active %s, %s mode\n",
+               (data->config_fan & 0x10) ? "low" : "high",
+               (data->config_fan & 0x20) ? "manual" : "auto");
+}
+
+static int lm63_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 lm63_data *lm63_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm63_data *data = i2c_get_clientdata(client);
+
+       down(&data->update_lock);
+
+       if ((jiffies - data->last_updated > HZ) ||
+           (jiffies < data->last_updated) ||
+           !data->valid) {
+               if (data->config & 0x04) { /* tachometer enabled  */
+                       /* order matters for fan1_input */
+                       data->fan1_input = i2c_smbus_read_byte_data(client,
+                                          LM63_REG_TACH_COUNT_LSB) & 0xFC;
+                       data->fan1_input |= i2c_smbus_read_byte_data(client,
+                                           LM63_REG_TACH_COUNT_MSB) << 8;
+                       data->fan1_low = (i2c_smbus_read_byte_data(client,
+                                         LM63_REG_TACH_LIMIT_LSB) & 0xFC)
+                                      | (i2c_smbus_read_byte_data(client,
+                                         LM63_REG_TACH_LIMIT_MSB) << 8);
+               }
+
+               data->pwm1_freq = i2c_smbus_read_byte_data(client,
+                                 LM63_REG_PWM_FREQ);
+               if (data->pwm1_freq == 0)
+                       data->pwm1_freq = 1;
+               data->pwm1_value = i2c_smbus_read_byte_data(client,
+                                  LM63_REG_PWM_VALUE);
+
+               data->temp1_input = i2c_smbus_read_byte_data(client,
+                                   LM63_REG_LOCAL_TEMP);
+               data->temp1_high = i2c_smbus_read_byte_data(client,
+                                  LM63_REG_LOCAL_HIGH);
+
+               /* order matters for temp2_input */
+               data->temp2_input = i2c_smbus_read_byte_data(client,
+                                   LM63_REG_REMOTE_TEMP_MSB) << 8;
+               data->temp2_input |= i2c_smbus_read_byte_data(client,
+                                    LM63_REG_REMOTE_TEMP_LSB);
+               data->temp2_high = (i2c_smbus_read_byte_data(client,
+                                  LM63_REG_REMOTE_HIGH_MSB) << 8)
+                                | i2c_smbus_read_byte_data(client,
+                                  LM63_REG_REMOTE_HIGH_LSB);
+               data->temp2_low = (i2c_smbus_read_byte_data(client,
+                                 LM63_REG_REMOTE_LOW_MSB) << 8)
+                               | i2c_smbus_read_byte_data(client,
+                                 LM63_REG_REMOTE_LOW_LSB);
+               data->temp2_crit = i2c_smbus_read_byte_data(client,
+                                  LM63_REG_REMOTE_TCRIT);
+               data->temp2_crit_hyst = i2c_smbus_read_byte_data(client,
+                                       LM63_REG_REMOTE_TCRIT_HYST);
+
+               data->alarms = i2c_smbus_read_byte_data(client,
+                              LM63_REG_ALERT_STATUS) & 0x7F;
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       up(&data->update_lock);
+
+       return data;
+}
+
+static int __init sensors_lm63_init(void)
+{
+       return i2c_add_driver(&lm63_driver);
+}
+
+static void __exit sensors_lm63_exit(void)
+{
+       i2c_del_driver(&lm63_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("LM63 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm63_init);
+module_exit(sensors_lm63_exit);
diff --git a/drivers/i2c/chips/lm87.c b/drivers/i2c/chips/lm87.c
new file mode 100644 (file)
index 0000000..7da39fe
--- /dev/null
@@ -0,0 +1,812 @@
+/*
+ * lm87.c
+ *
+ * Copyright (C) 2000       Frodo Looijaard <frodol@dds.nl>
+ *                          Philip Edelbrock <phil@netroedge.com>
+ *                          Stephen Rousset <stephen.rousset@rocketlogix.com>
+ *                          Dan Eaton <dan.eaton@rocketlogix.com>
+ * Copyright (C) 2004       Jean Delvare <khali@linux-fr.org>
+ *
+ * Original port to Linux 2.6 by Jeff Oliver.
+ *
+ * The LM87 is a sensor chip made by National Semiconductor. It monitors up
+ * to 8 voltages (including its own power source), up to three temperatures
+ * (its own plus up to two external ones) and up to two fans. The default
+ * configuration is 6 voltages, two temperatures and two fans (see below).
+ * Voltages are scaled internally with ratios such that the nominal value of
+ * each voltage correspond to a register value of 192 (which means a
+ * resolution of about 0.5% of the nominal value). Temperature values are
+ * reported with a 1 deg resolution and a 3-4 deg accuracy. Complete
+ * datasheet can be obtained from National's website at:
+ *   http://www.national.com/pf/LM/LM87.html
+ *
+ * Some functions share pins, so not all functions are available at the same
+ * time. Which are depends on the hardware setup. This driver assumes that
+ * the BIOS configured the chip correctly. In that respect, it  differs from
+ * the original driver (from lm_sensors for Linux 2.4), which would force the
+ * LM87 to an arbitrary, compile-time chosen mode, regardless of the actual
+ * chipset wiring.
+ * For reference, here is the list of exclusive functions:
+ *  - in0+in5 (default) or temp3
+ *  - fan1 (default) or in6
+ *  - fan2 (default) or in7
+ *  - VID lines (default) or IRQ lines (not handled by this driver)
+ *
+ * The LM87 additionally features an analog output, supposedly usable to
+ * control the speed of a fan. All new chips use pulse width modulation
+ * instead. The LM87 is the only hardware monitoring chipset I know of
+ * which uses amplitude modulation. Be careful when using this feature.
+ *
+ * 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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/i2c-vid.h>
+
+/*
+ * Addresses to scan
+ * LM87 has three possible addresses: 0x2c, 0x2d and 0x2e.
+ */
+
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_1(lm87);
+
+/*
+ * The LM87 registers
+ */
+
+/* nr in 0..5 */
+#define LM87_REG_IN(nr)                        (0x20 + (nr))
+#define LM87_REG_IN_MAX(nr)            (0x2B + (nr) * 2)
+#define LM87_REG_IN_MIN(nr)            (0x2C + (nr) * 2)
+/* nr in 0..1 */
+#define LM87_REG_AIN(nr)               (0x28 + (nr))
+#define LM87_REG_AIN_MIN(nr)           (0x1A + (nr))
+#define LM87_REG_AIN_MAX(nr)           (0x3B + (nr))
+
+static u8 LM87_REG_TEMP[3] = { 0x27, 0x26, 0x20 };
+static u8 LM87_REG_TEMP_HIGH[3] = { 0x39, 0x37, 0x2B };
+static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C };
+
+#define LM87_REG_TEMP_HW_INT_LOCK      0x13
+#define LM87_REG_TEMP_HW_EXT_LOCK      0x14
+#define LM87_REG_TEMP_HW_INT           0x17
+#define LM87_REG_TEMP_HW_EXT           0x18
+
+/* nr in 0..1 */
+#define LM87_REG_FAN(nr)               (0x28 + (nr))
+#define LM87_REG_FAN_MIN(nr)           (0x3B + (nr))
+#define LM87_REG_AOUT                  0x19
+
+#define LM87_REG_CONFIG                        0x40
+#define LM87_REG_CHANNEL_MODE          0x16
+#define LM87_REG_VID_FAN_DIV           0x47
+#define LM87_REG_VID4                  0x49
+
+#define LM87_REG_ALARMS1               0x41
+#define LM87_REG_ALARMS2               0x42
+
+#define LM87_REG_COMPANY_ID            0x3E
+#define LM87_REG_REVISION              0x3F
+
+/*
+ * Conversions and various macros
+ * The LM87 uses signed 8-bit values for temperatures.
+ */
+
+#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192)
+#define IN_TO_REG(val,scale)   ((val) <= 0 ? 0 : \
+                                (val) * 192 >= (scale) * 255 ? 255 : \
+                                ((val) * 192 + (scale)/2) / (scale))
+
+#define TEMP_FROM_REG(reg)     ((reg) * 1000)
+#define TEMP_TO_REG(val)       ((val) <= -127500 ? -128 : \
+                                (val) >= 126500 ? 127 : \
+                                (((val) < 0 ? (val)-500 : (val)+500) / 1000))
+
+#define FAN_FROM_REG(reg,div)  ((reg) == 255 || (reg) == 0 ? 0 : \
+                                1350000 + (reg)*(div) / 2) / ((reg)*(div))
+#define FAN_TO_REG(val,div)    ((val)*(div) * 255 <= 1350000 ? 255 : \
+                                (1350000 + (val)*(div) / 2) / ((val)*(div)))
+
+#define FAN_DIV_FROM_REG(reg)  (1 << (reg))
+
+/* analog out is 9.80mV/LSB */
+#define AOUT_FROM_REG(reg)     (((reg) * 98 + 5) / 10)
+#define AOUT_TO_REG(val)       ((val) <= 0 ? 0 : \
+                                (val) >= 2500 ? 255 : \
+                                ((val) * 10 + 49) / 98)
+
+/* nr in 0..1 */
+#define CHAN_NO_FAN(nr)                (1 << (nr))
+#define CHAN_TEMP3             (1 << 2)
+#define CHAN_VCC_5V            (1 << 3)
+#define CHAN_NO_VID            (1 << 8)
+
+/*
+ * Functions declaration
+ */
+
+static int lm87_attach_adapter(struct i2c_adapter *adapter);
+static int lm87_detect(struct i2c_adapter *adapter, int address, int kind);
+static void lm87_init_client(struct i2c_client *client);
+static int lm87_detach_client(struct i2c_client *client);
+static struct lm87_data *lm87_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver lm87_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "lm87",
+       .id             = I2C_DRIVERID_LM87,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = lm87_attach_adapter,
+       .detach_client  = lm87_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm87_data {
+       struct i2c_client client;
+       struct semaphore update_lock;
+       char valid; /* zero until following fields are valid */
+       unsigned long last_updated; /* In jiffies */
+
+       u8 channel;             /* register value */
+
+       u8 in[8];               /* register value */
+       u8 in_max[8];           /* register value */
+       u8 in_min[8];           /* register value */
+       u16 in_scale[8];
+
+       s8 temp[3];             /* register value */
+       s8 temp_high[3];        /* register value */
+       s8 temp_low[3];         /* register value */
+       s8 temp_crit_int;       /* min of two register values */
+       s8 temp_crit_ext;       /* min of two register values */
+
+       u8 fan[2];              /* register value */
+       u8 fan_min[2];          /* register value */
+       u8 fan_div[2];          /* register value, shifted right */
+       u8 aout;                /* register value */
+
+       u16 alarms;             /* register values, combined */
+       u8 vid;                 /* register values, combined */
+       u8 vrm;
+};
+
+/*
+ * Internal variables
+ */
+
+static int lm87_id;
+
+/*
+ * Sysfs stuff
+ */
+
+static inline int lm87_read_value(struct i2c_client *client, u8 reg)
+{
+       return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+       return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+#define show_in(offset) \
+static ssize_t show_in##offset##_input(struct device *dev, char *buf) \
+{ \
+       struct lm87_data *data = lm87_update_device(dev); \
+       return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \
+                      data->in_scale[offset])); \
+} \
+static ssize_t show_in##offset##_min(struct device *dev, char *buf) \
+{ \
+       struct lm87_data *data = lm87_update_device(dev); \
+       return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \
+                      data->in_scale[offset])); \
+} \
+static ssize_t show_in##offset##_max(struct device *dev, char *buf) \
+{ \
+       struct lm87_data *data = lm87_update_device(dev); \
+       return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \
+                      data->in_scale[offset])); \
+} \
+static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
+               show_in##offset##_input, NULL);
+show_in(0);
+show_in(1);
+show_in(2);
+show_in(3);
+show_in(4);
+show_in(5);
+show_in(6);
+show_in(7);
+
+static void set_in_min(struct device *dev, const char *buf, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm87_data *data = i2c_get_clientdata(client);
+       long val = simple_strtol(buf, NULL, 10);
+       data->in_min[nr] = IN_TO_REG(val, data->in_scale[nr]);
+       lm87_write_value(client, nr<6 ? LM87_REG_IN_MIN(nr) :
+                        LM87_REG_AIN_MIN(nr-6), data->in_min[nr]);
+}
+
+static void set_in_max(struct device *dev, const char *buf, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm87_data *data = i2c_get_clientdata(client);
+       long val = simple_strtol(buf, NULL, 10);
+       data->in_max[nr] = IN_TO_REG(val, data->in_scale[nr]);
+       lm87_write_value(client, nr<6 ? LM87_REG_IN_MAX(nr) :
+                        LM87_REG_AIN_MAX(nr-6), data->in_max[nr]);
+}
+
+#define set_in(offset) \
+static ssize_t set_in##offset##_min(struct device *dev, \
+               const char *buf, size_t count) \
+{ \
+       set_in_min(dev, buf, offset); \
+       return count; \
+} \
+static ssize_t set_in##offset##_max(struct device *dev, \
+               const char *buf, size_t count) \
+{ \
+       set_in_max(dev, buf, offset); \
+       return count; \
+} \
+static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
+               show_in##offset##_min, set_in##offset##_min); \
+static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
+               show_in##offset##_max, set_in##offset##_max);
+set_in(0);
+set_in(1);
+set_in(2);
+set_in(3);
+set_in(4);
+set_in(5);
+set_in(6);
+set_in(7);
+
+#define show_temp(offset) \
+static ssize_t show_temp##offset##_input(struct device *dev, char *buf) \
+{ \
+       struct lm87_data *data = lm87_update_device(dev); \
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \
+} \
+static ssize_t show_temp##offset##_low(struct device *dev, char *buf) \
+{ \
+       struct lm87_data *data = lm87_update_device(dev); \
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[offset-1])); \
+} \
+static ssize_t show_temp##offset##_high(struct device *dev, char *buf) \
+{ \
+       struct lm87_data *data = lm87_update_device(dev); \
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[offset-1])); \
+}\
+static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
+               show_temp##offset##_input, NULL);
+show_temp(1);
+show_temp(2);
+show_temp(3);
+
+static void set_temp_low(struct device *dev, const char *buf, int nr)
+{
+    struct i2c_client *client = to_i2c_client(dev);
+    struct lm87_data *data = i2c_get_clientdata(client);
+    long val = simple_strtol(buf, NULL, 10);
+    data->temp_low[nr] = TEMP_TO_REG(val);
+    lm87_write_value(client, LM87_REG_TEMP_LOW[nr], data->temp_low[nr]);
+}
+
+static void set_temp_high(struct device *dev, const char *buf, int nr)
+{
+    struct i2c_client *client = to_i2c_client(dev);
+    struct lm87_data *data = i2c_get_clientdata(client);
+    long val = simple_strtol(buf, NULL, 10);
+    data->temp_high[nr] = TEMP_TO_REG(val);
+    lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], data->temp_high[nr]);
+}
+
+#define set_temp(offset) \
+static ssize_t set_temp##offset##_low(struct device *dev, \
+               const char *buf, size_t count) \
+{ \
+       set_temp_low(dev, buf, offset-1); \
+       return count; \
+} \
+static ssize_t set_temp##offset##_high(struct device *dev, \
+               const char *buf, size_t count) \
+{ \
+       set_temp_high(dev, buf, offset-1); \
+       return count; \
+} \
+static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
+               show_temp##offset##_high, set_temp##offset##_high); \
+static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \
+               show_temp##offset##_low, set_temp##offset##_low);
+set_temp(1);
+set_temp(2);
+set_temp(3);
+
+static ssize_t show_temp_crit_int(struct device *dev, char *buf)
+{
+       struct lm87_data *data = lm87_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int));
+}
+
+static ssize_t show_temp_crit_ext(struct device *dev, char *buf)
+{
+       struct lm87_data *data = lm87_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext));
+}
+
+static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit_int, NULL);
+static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit_ext, NULL);
+static DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit_ext, NULL);
+
+#define show_fan(offset) \
+static ssize_t show_fan##offset##_input(struct device *dev, char *buf) \
+{ \
+       struct lm87_data *data = lm87_update_device(dev); \
+       return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[offset-1], \
+                      FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \
+} \
+static ssize_t show_fan##offset##_min(struct device *dev, char *buf) \
+{ \
+       struct lm87_data *data = lm87_update_device(dev); \
+       return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[offset-1], \
+                      FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \
+} \
+static ssize_t show_fan##offset##_div(struct device *dev, char *buf) \
+{ \
+       struct lm87_data *data = lm87_update_device(dev); \
+       return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[offset-1])); \
+} \
+static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
+               show_fan##offset##_input, NULL);
+show_fan(1);
+show_fan(2);
+
+static void set_fan_min(struct device *dev, const char *buf, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm87_data *data = i2c_get_clientdata(client);
+       long val = simple_strtol(buf, NULL, 10);
+       data->fan_min[nr] = FAN_TO_REG(val,
+                           FAN_DIV_FROM_REG(data->fan_div[nr]));
+       lm87_write_value(client, LM87_REG_FAN_MIN(nr), data->fan_min[nr]);
+}
+
+/* Note: we save and restore the fan minimum here, because its value is
+   determined in part by the fan clock divider.  This follows the principle
+   of least suprise; the user doesn't expect the fan minimum to change just
+   because the divider changed. */
+static ssize_t set_fan_div(struct device *dev, const char *buf,
+               size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm87_data *data = i2c_get_clientdata(client);
+       long val = simple_strtol(buf, NULL, 10);
+       unsigned long min = FAN_FROM_REG(data->fan_min[nr],
+                           FAN_DIV_FROM_REG(data->fan_div[nr]));
+       u8 reg;
+
+       switch (val) {
+       case 1: data->fan_div[nr] = 0; break;
+       case 2: data->fan_div[nr] = 1; break;
+       case 4: data->fan_div[nr] = 2; break;
+       case 8: data->fan_div[nr] = 3; break;
+       default: return -EINVAL;
+       }
+
+       reg = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
+       switch (nr) {
+       case 0:
+           reg = (reg & 0xCF) | (data->fan_div[0] << 4);
+           break;
+       case 1:
+           reg = (reg & 0x3F) | (data->fan_div[1] << 6);
+           break;
+       }
+       lm87_write_value(client, LM87_REG_VID_FAN_DIV, reg);
+
+       data->fan_min[nr] = FAN_TO_REG(min, val);
+       lm87_write_value(client, LM87_REG_FAN_MIN(nr),
+                        data->fan_min[nr]);
+       return count;
+}
+
+#define set_fan(offset) \
+static ssize_t set_fan##offset##_min(struct device *dev, const char *buf, \
+               size_t count) \
+{ \
+       set_fan_min(dev, buf, offset-1); \
+       return count; \
+} \
+static ssize_t set_fan##offset##_div(struct device *dev, const char *buf, \
+               size_t count) \
+{ \
+       return set_fan_div(dev, buf, count, offset-1); \
+} \
+static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
+               show_fan##offset##_min, set_fan##offset##_min); \
+static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
+               show_fan##offset##_div, set_fan##offset##_div);
+set_fan(1);
+set_fan(2);
+
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+       struct lm87_data *data = lm87_update_device(dev);
+       return sprintf(buf, "%d\n", data->alarms);
+}
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+static ssize_t show_vid(struct device *dev, char *buf)
+{
+       struct lm87_data *data = lm87_update_device(dev);
+       return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+
+static ssize_t show_vrm(struct device *dev, char *buf)
+{
+       struct lm87_data *data = lm87_update_device(dev);
+       return sprintf(buf, "%d\n", data->vrm);
+}
+static ssize_t set_vrm(struct device *dev, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm87_data *data = i2c_get_clientdata(client);
+       data->vrm = simple_strtoul(buf, NULL, 10);
+       return count;
+}
+static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+
+static ssize_t show_aout(struct device *dev, char *buf)
+{
+       struct lm87_data *data = lm87_update_device(dev);
+       return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout));
+}
+static ssize_t set_aout(struct device *dev, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm87_data *data = i2c_get_clientdata(client);
+       long val = simple_strtol(buf, NULL, 10);
+       data->aout = AOUT_TO_REG(val);
+       lm87_write_value(client, LM87_REG_AOUT, data->aout);
+       return count;
+}
+static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout);
+
+/*
+ * Real code
+ */
+
+static int lm87_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_detect(adapter, &addr_data, lm87_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int lm87_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *new_client;
+       struct lm87_data *data;
+       int err = 0;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               goto exit;
+
+       if (!(data = kmalloc(sizeof(struct lm87_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+       memset(data, 0, sizeof(struct lm87_data));
+
+       /* The common I2C client data is placed right before the
+          LM87-specific data. */
+       new_client = &data->client;
+       i2c_set_clientdata(new_client, data);
+       new_client->addr = address;
+       new_client->adapter = adapter;
+       new_client->driver = &lm87_driver;
+       new_client->flags = 0;
+
+       /* Default to an LM87 if forced */
+       if (kind == 0)
+               kind = lm87;
+
+       /* Now, we do the remaining detection. */
+       if (kind < 0) {
+               u8 rev = lm87_read_value(new_client, LM87_REG_REVISION);
+
+               if (rev < 0x01 || rev > 0x08
+                || (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)
+                || lm87_read_value(new_client, LM87_REG_COMPANY_ID) != 0x02) {
+                       dev_dbg(&adapter->dev,
+                               "LM87 detection failed at 0x%02x.\n",
+                               address);
+                       goto exit_free;
+               }
+       }
+
+       /* We can fill in the remaining client fields */
+       strlcpy(new_client->name, "lm87", I2C_NAME_SIZE);
+       new_client->id = lm87_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 LM87 chip */
+       lm87_init_client(new_client);
+
+       data->in_scale[0] = 2500;
+       data->in_scale[1] = 2700;
+       data->in_scale[2] = (data->channel & CHAN_VCC_5V) ? 5000 : 3300;
+       data->in_scale[3] = 5000;
+       data->in_scale[4] = 12000;
+       data->in_scale[5] = 2700;
+       data->in_scale[6] = 1875;
+       data->in_scale[7] = 1875;
+
+       /* Register sysfs hooks */
+       device_create_file(&new_client->dev, &dev_attr_in1_input);
+       device_create_file(&new_client->dev, &dev_attr_in1_min);
+       device_create_file(&new_client->dev, &dev_attr_in1_max);
+       device_create_file(&new_client->dev, &dev_attr_in2_input);
+       device_create_file(&new_client->dev, &dev_attr_in2_min);
+       device_create_file(&new_client->dev, &dev_attr_in2_max);
+       device_create_file(&new_client->dev, &dev_attr_in3_input);
+       device_create_file(&new_client->dev, &dev_attr_in3_min);
+       device_create_file(&new_client->dev, &dev_attr_in3_max);
+       device_create_file(&new_client->dev, &dev_attr_in4_input);
+       device_create_file(&new_client->dev, &dev_attr_in4_min);
+       device_create_file(&new_client->dev, &dev_attr_in4_max);
+
+       if (data->channel & CHAN_NO_FAN(0)) {
+               device_create_file(&new_client->dev, &dev_attr_in6_input);
+               device_create_file(&new_client->dev, &dev_attr_in6_min);
+               device_create_file(&new_client->dev, &dev_attr_in6_max);
+       } else {
+               device_create_file(&new_client->dev, &dev_attr_fan1_input);
+               device_create_file(&new_client->dev, &dev_attr_fan1_min);
+               device_create_file(&new_client->dev, &dev_attr_fan1_div);
+       }
+       if (data->channel & CHAN_NO_FAN(1)) {
+               device_create_file(&new_client->dev, &dev_attr_in7_input);
+               device_create_file(&new_client->dev, &dev_attr_in7_min);
+               device_create_file(&new_client->dev, &dev_attr_in7_max);
+       } else {
+               device_create_file(&new_client->dev, &dev_attr_fan2_input);
+               device_create_file(&new_client->dev, &dev_attr_fan2_min);
+               device_create_file(&new_client->dev, &dev_attr_fan2_div);
+       }
+
+       device_create_file(&new_client->dev, &dev_attr_temp1_input);
+       device_create_file(&new_client->dev, &dev_attr_temp1_max);
+       device_create_file(&new_client->dev, &dev_attr_temp1_min);
+       device_create_file(&new_client->dev, &dev_attr_temp1_crit);
+       device_create_file(&new_client->dev, &dev_attr_temp2_input);
+       device_create_file(&new_client->dev, &dev_attr_temp2_max);
+       device_create_file(&new_client->dev, &dev_attr_temp2_min);
+       device_create_file(&new_client->dev, &dev_attr_temp2_crit);
+
+       if (data->channel & CHAN_TEMP3) {
+               device_create_file(&new_client->dev, &dev_attr_temp3_input);
+               device_create_file(&new_client->dev, &dev_attr_temp3_max);
+               device_create_file(&new_client->dev, &dev_attr_temp3_min);
+               device_create_file(&new_client->dev, &dev_attr_temp3_crit);
+       } else {
+               device_create_file(&new_client->dev, &dev_attr_in0_input);
+               device_create_file(&new_client->dev, &dev_attr_in0_min);
+               device_create_file(&new_client->dev, &dev_attr_in0_max);
+               device_create_file(&new_client->dev, &dev_attr_in5_input);
+               device_create_file(&new_client->dev, &dev_attr_in5_min);
+               device_create_file(&new_client->dev, &dev_attr_in5_max);
+       }
+
+       if (!(data->channel & CHAN_NO_VID)) {
+               device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
+               device_create_file(&new_client->dev, &dev_attr_vrm);
+       }
+
+       device_create_file(&new_client->dev, &dev_attr_alarms);
+       device_create_file(&new_client->dev, &dev_attr_aout_output);
+
+       return 0;
+
+exit_free:
+       kfree(data);
+exit:
+       return err;
+}
+
+static void lm87_init_client(struct i2c_client *client)
+{
+       struct lm87_data *data = i2c_get_clientdata(client);
+       u8 config;
+
+       data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
+       data->vrm = i2c_which_vrm();
+
+       config = lm87_read_value(client, LM87_REG_CONFIG);
+       if (!(config & 0x01)) {
+               int i;
+
+               /* Limits are left uninitialized after power-up */
+               for (i = 1; i < 6; i++) {
+                       lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00);
+                       lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF);
+               }
+               for (i = 0; i < 2; i++) {
+                       lm87_write_value(client, LM87_REG_TEMP_HIGH[i], 0x7F);
+                       lm87_write_value(client, LM87_REG_TEMP_LOW[i], 0x00);
+                       lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00);
+                       lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF);
+               }
+               if (data->channel & CHAN_TEMP3) {
+                       lm87_write_value(client, LM87_REG_TEMP_HIGH[2], 0x7F);
+                       lm87_write_value(client, LM87_REG_TEMP_LOW[2], 0x00);
+               } else {
+                       lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00);
+                       lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF);
+               }
+       }
+       if ((config & 0x81) != 0x01) {
+               /* Start monitoring */
+               lm87_write_value(client, LM87_REG_CONFIG,
+                                (config & 0xF7) | 0x01);
+       }
+}
+
+static int lm87_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 lm87_data *lm87_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm87_data *data = i2c_get_clientdata(client);
+
+       down(&data->update_lock);
+
+       if (jiffies - data->last_updated > HZ
+         || jiffies < data->last_updated
+         || !data->valid) {
+               int i, j;
+
+               dev_dbg(&client->dev, "Updating data.\n");
+
+               i = (data->channel & CHAN_TEMP3) ? 1 : 0;
+               j = (data->channel & CHAN_TEMP3) ? 5 : 6;
+               for (; i < j; i++) {
+                       data->in[i] = lm87_read_value(client,
+                                     LM87_REG_IN(i));
+                       data->in_min[i] = lm87_read_value(client,
+                                         LM87_REG_IN_MIN(i));
+                       data->in_max[i] = lm87_read_value(client,
+                                         LM87_REG_IN_MAX(i));
+               }
+
+               for (i = 0; i < 2; i++) {
+                       if (data->channel & CHAN_NO_FAN(i)) {
+                               data->in[6+i] = lm87_read_value(client,
+                                               LM87_REG_AIN(i));
+                               data->in_max[6+i] = lm87_read_value(client,
+                                                   LM87_REG_AIN_MAX(i));
+                               data->in_min[6+i] = lm87_read_value(client,
+                                                   LM87_REG_AIN_MIN(i));
+
+                       } else {
+                               data->fan[i] = lm87_read_value(client,
+                                              LM87_REG_FAN(i));
+                               data->fan_min[i] = lm87_read_value(client,
+                                                  LM87_REG_FAN_MIN(i));
+                       }
+               }
+
+               j = (data->channel & CHAN_TEMP3) ? 3 : 2;
+               for (i = 0 ; i < j; i++) {
+                       data->temp[i] = lm87_read_value(client,
+                                       LM87_REG_TEMP[i]);
+                       data->temp_high[i] = lm87_read_value(client,
+                                            LM87_REG_TEMP_HIGH[i]);
+                       data->temp_low[i] = lm87_read_value(client,
+                                           LM87_REG_TEMP_LOW[i]);
+               }
+
+               i = lm87_read_value(client, LM87_REG_TEMP_HW_INT_LOCK);
+               j = lm87_read_value(client, LM87_REG_TEMP_HW_INT);
+               data->temp_crit_int = min(i, j);
+
+               i = lm87_read_value(client, LM87_REG_TEMP_HW_EXT_LOCK);
+               j = lm87_read_value(client, LM87_REG_TEMP_HW_EXT);
+               data->temp_crit_ext = min(i, j);
+
+               i = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
+               data->fan_div[0] = (i >> 4) & 0x03;
+               data->fan_div[1] = (i >> 6) & 0x03;
+               data->vid = (i & 0x0F)
+                         | (lm87_read_value(client, LM87_REG_VID4) & 0x01)
+                            << 4;
+
+               data->alarms = lm87_read_value(client, LM87_REG_ALARMS1)
+                            | (lm87_read_value(client, LM87_REG_ALARMS2)
+                               << 8);
+               data->aout = lm87_read_value(client, LM87_REG_AOUT);
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       up(&data->update_lock);
+
+       return data;
+}
+
+static int __init sensors_lm87_init(void)
+{
+       return i2c_add_driver(&lm87_driver);
+}
+
+static void __exit sensors_lm87_exit(void)
+{
+       i2c_del_driver(&lm87_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org> and others");
+MODULE_DESCRIPTION("LM87 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm87_init);
+module_exit(sensors_lm87_exit);
diff --git a/drivers/i2c/chips/pc87360.c b/drivers/i2c/chips/pc87360.c
new file mode 100644 (file)
index 0000000..3b1c020
--- /dev/null
@@ -0,0 +1,1298 @@
+/*
+ *  pc87360.c - Part of lm_sensors, Linux kernel modules
+ *              for hardware monitoring
+ *  Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
+ *
+ *  Copied from smsc47m1.c:
+ *  Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Supports the following chips:
+ *
+ *  Chip        #vin    #fan    #pwm    #temp   devid
+ *  PC87360     -       2       2       -       0xE1
+ *  PC87363     -       2       2       -       0xE8
+ *  PC87364     -       3       3       -       0xE4
+ *  PC87365     11      3       3       2       0xE5
+ *  PC87366     11      3       3       3-4     0xE9
+ *
+ *  This driver assumes that no more than one chip is present, and one of
+ *  the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F).
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/i2c-vid.h>
+#include <asm/io.h>
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END };
+static struct i2c_force_data forces[] = {{ NULL }};
+static u8 devid;
+static unsigned int extra_isa[3];
+static u8 confreg[4];
+
+enum chips { any_chip, pc87360, pc87363, pc87364, pc87365, pc87366 };
+static struct i2c_address_data addr_data = {
+       .normal_i2c             = normal_i2c,
+       .normal_isa             = normal_isa,
+       .forces                 = forces,
+};
+
+static int init = 1;
+module_param(init, int, 0);
+MODULE_PARM_DESC(init,
+ "Chip initialization level:\n"
+ " 0: None\n"
+ "*1: Forcibly enable internal voltage and temperature channels, except in9\n"
+ " 2: Forcibly enable all voltage and temperature channels, except in9\n"
+ " 3: Forcibly enable all voltage and temperature channels, including in9");
+
+/*
+ * Super-I/O registers and operations
+ */
+
+#define DEV    0x07    /* Register: Logical device select */
+#define DEVID  0x20    /* Register: Device ID */
+#define ACT    0x30    /* Register: Device activation */
+#define BASE   0x60    /* Register: Base address */
+
+#define FSCM   0x09    /* Logical device: fans */
+#define VLM    0x0d    /* Logical device: voltages */
+#define TMS    0x0e    /* Logical device: temperatures */
+static const u8 logdev[3] = { FSCM, VLM, TMS };
+
+#define LD_FAN         0
+#define LD_IN          1
+#define LD_TEMP                2
+
+static inline void superio_outb(int sioaddr, int reg, int val)
+{
+       outb(reg, sioaddr);
+       outb(val, sioaddr+1);
+}
+
+static inline int superio_inb(int sioaddr, int reg)
+{
+       outb(reg, sioaddr);
+       return inb(sioaddr+1);
+}
+
+static inline void superio_exit(int sioaddr)
+{
+       outb(0x02, sioaddr);
+       outb(0x02, sioaddr+1);
+}
+
+/*
+ * Logical devices
+ */
+
+#define PC87360_EXTENT         0x10
+#define PC87365_REG_BANK       0x09
+#define NO_BANK                        0xff
+
+/*
+ * Fan registers and conversions
+ */
+
+/* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */
+#define PC87360_REG_PRESCALE(nr)       (0x00 + 2 * (nr))
+#define PC87360_REG_PWM(nr)            (0x01 + 2 * (nr))
+#define PC87360_REG_FAN_MIN(nr)                (0x06 + 3 * (nr))
+#define PC87360_REG_FAN(nr)            (0x07 + 3 * (nr))
+#define PC87360_REG_FAN_STATUS(nr)     (0x08 + 3 * (nr))
+
+#define FAN_FROM_REG(val,div)          ((val) == 0 ? 0: \
+                                        480000 / ((val)*(div)))
+#define FAN_TO_REG(val,div)            ((val) <= 100 ? 0 : \
+                                        480000 / ((val)*(div)))
+#define FAN_DIV_FROM_REG(val)          (1 << ((val >> 5) & 0x03))
+#define FAN_STATUS_FROM_REG(val)       ((val) & 0x07)
+
+#define FAN_CONFIG_MONITOR(val,nr)     (((val) >> (2 + nr * 3)) & 1)
+#define FAN_CONFIG_CONTROL(val,nr)     (((val) >> (3 + nr * 3)) & 1)
+#define FAN_CONFIG_INVERT(val,nr)      (((val) >> (4 + nr * 3)) & 1)
+
+#define PWM_FROM_REG(val,inv)          ((inv) ? 255 - (val) : (val))
+static inline u8 PWM_TO_REG(int val, int inv)
+{
+       if (inv)
+               val = 255 - val;
+       if (val < 0)
+               return 0;
+       if (val > 255)
+               return 255;
+       return val;
+}
+
+/*
+ * Voltage registers and conversions
+ */
+
+#define PC87365_REG_IN_CONVRATE                0x07
+#define PC87365_REG_IN_CONFIG          0x08
+#define PC87365_REG_IN                 0x0B
+#define PC87365_REG_IN_MIN             0x0D
+#define PC87365_REG_IN_MAX             0x0C
+#define PC87365_REG_IN_STATUS          0x0A
+#define PC87365_REG_IN_ALARMS1         0x00
+#define PC87365_REG_IN_ALARMS2         0x01
+#define PC87365_REG_VID                        0x06
+
+#define IN_FROM_REG(val,ref)           (((val) * (ref) + 128) / 256)
+#define IN_TO_REG(val,ref)             ((val) < 0 ? 0 : \
+                                        (val)*256 >= (ref)*255 ? 255: \
+                                        ((val) * 256 + (ref)/2) / (ref))
+
+/*
+ * Temperature registers and conversions
+ */
+
+#define PC87365_REG_TEMP_CONFIG                0x08
+#define PC87365_REG_TEMP               0x0B
+#define PC87365_REG_TEMP_MIN           0x0D
+#define PC87365_REG_TEMP_MAX           0x0C
+#define PC87365_REG_TEMP_CRIT          0x0E
+#define PC87365_REG_TEMP_STATUS                0x0A
+#define PC87365_REG_TEMP_ALARMS                0x00
+
+#define TEMP_FROM_REG(val)             ((val) * 1000)
+#define TEMP_TO_REG(val)               ((val) < -55000 ? -55 : \
+                                        (val) > 127000 ? 127 : \
+                                        (val) < 0 ? ((val) - 500) / 1000 : \
+                                        ((val) + 500) / 1000)
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct pc87360_data {
+       struct i2c_client client;
+       struct semaphore lock;
+       struct semaphore update_lock;
+       char valid;             /* !=0 if following fields are valid */
+       unsigned long last_updated;     /* In jiffies */
+
+       int address[3];
+
+       u8 fannr, innr, tempnr;
+
+       u8 fan[3];              /* Register value */
+       u8 fan_min[3];          /* Register value */
+       u8 fan_status[3];       /* Register value */
+       u8 pwm[3];              /* Register value */
+       u16 fan_conf;           /* Configuration register values, combined */
+
+       u16 in_vref;            /* 1 mV/bit */
+       u8 in[14];              /* Register value */
+       u8 in_min[14];          /* Register value */
+       u8 in_max[14];          /* Register value */
+       u8 in_crit[3];          /* Register value */
+       u8 in_status[14];       /* Register value */
+       u16 in_alarms;          /* Register values, combined, masked */
+       u8 vid_conf;            /* Configuration register value */
+       u8 vrm;
+       u8 vid;                 /* Register value */
+
+       s8 temp[3];             /* Register value */
+       s8 temp_min[3];         /* Register value */
+       s8 temp_max[3];         /* Register value */
+       s8 temp_crit[3];        /* Register value */
+       u8 temp_status[3];      /* Register value */
+       u8 temp_alarms;         /* Register value, masked */
+};
+
+/*
+ * Functions declaration
+ */
+
+static int pc87360_attach_adapter(struct i2c_adapter *adapter);
+static int pc87360_detect(struct i2c_adapter *adapter, int address, int kind);
+static int pc87360_detach_client(struct i2c_client *client);
+
+static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank,
+                             u8 reg);
+static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank,
+                               u8 reg, u8 value);
+static void pc87360_init_client(struct i2c_client *client, int use_thermistors);
+static struct pc87360_data *pc87360_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static struct i2c_driver pc87360_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "pc87360",
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = pc87360_attach_adapter,
+       .detach_client  = pc87360_detach_client,
+};
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t set_fan_min(struct device *dev, const char *buf,
+       size_t count, int nr)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct pc87360_data *data = i2c_get_clientdata(client);
+       long fan_min = simple_strtol(buf, NULL, 10);
+
+       fan_min = FAN_TO_REG(fan_min, FAN_DIV_FROM_REG(data->fan_status[nr]));
+
+       /* If it wouldn't fit, change clock divisor */
+       while (fan_min > 255
+           && (data->fan_status[nr] & 0x60) != 0x60) {
+               fan_min >>= 1;
+               data->fan[nr] >>= 1;
+               data->fan_status[nr] += 0x20;
+       }
+       data->fan_min[nr] = fan_min > 255 ? 255 : fan_min;
+       pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_MIN(nr),
+                           data->fan_min[nr]);
+
+       /* Write new divider, preserve alarm bits */
+       pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_STATUS(nr),
+                           data->fan_status[nr] & 0xF9);
+
+       return count;
+}
+
+#define show_and_set_fan(offset) \
+static ssize_t show_fan##offset##_input(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[offset-1], \
+                      FAN_DIV_FROM_REG(data->fan_status[offset-1]))); \
+} \
+static ssize_t show_fan##offset##_min(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[offset-1], \
+                      FAN_DIV_FROM_REG(data->fan_status[offset-1]))); \
+} \
+static ssize_t show_fan##offset##_div(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", \
+                      FAN_DIV_FROM_REG(data->fan_status[offset-1])); \
+} \
+static ssize_t show_fan##offset##_status(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", \
+                      FAN_STATUS_FROM_REG(data->fan_status[offset-1])); \
+} \
+static ssize_t set_fan##offset##_min(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       return set_fan_min(dev, buf, count, offset-1); \
+} \
+static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
+       show_fan##offset##_input, NULL); \
+static DEVICE_ATTR(fan##offset##_min, S_IWUSR | S_IRUGO, \
+       show_fan##offset##_min, set_fan##offset##_min); \
+static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \
+       show_fan##offset##_div, NULL); \
+static DEVICE_ATTR(fan##offset##_status, S_IRUGO, \
+       show_fan##offset##_status, NULL);
+show_and_set_fan(1)
+show_and_set_fan(2)
+show_and_set_fan(3)
+
+#define show_and_set_pwm(offset) \
+static ssize_t show_pwm##offset(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", \
+                      PWM_FROM_REG(data->pwm[offset-1], \
+                                   FAN_CONFIG_INVERT(data->fan_conf, \
+                                                     offset-1))); \
+} \
+static ssize_t set_pwm##offset(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct pc87360_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->pwm[offset-1] = PWM_TO_REG(val, \
+                             FAN_CONFIG_INVERT(data->fan_conf, offset-1)); \
+       pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(offset-1), \
+                           data->pwm[offset-1]); \
+       return count; \
+} \
+static DEVICE_ATTR(pwm##offset, S_IWUSR | S_IRUGO, \
+       show_pwm##offset, set_pwm##offset);
+show_and_set_pwm(1)
+show_and_set_pwm(2)
+show_and_set_pwm(3)
+
+#define show_and_set_in(offset) \
+static ssize_t show_in##offset##_input(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \
+                      data->in_vref)); \
+} \
+static ssize_t show_in##offset##_min(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \
+                      data->in_vref)); \
+} \
+static ssize_t show_in##offset##_max(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \
+                      data->in_vref)); \
+} \
+static ssize_t show_in##offset##_status(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", data->in_status[offset]); \
+} \
+static ssize_t set_in##offset##_min(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct pc87360_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->in_min[offset] = IN_TO_REG(val, data->in_vref); \
+       pc87360_write_value(data, LD_IN, offset, PC87365_REG_IN_MIN, \
+                           data->in_min[offset]); \
+       return count; \
+} \
+static ssize_t set_in##offset##_max(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct pc87360_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->in_max[offset] = IN_TO_REG(val, \
+                              data->in_vref); \
+       pc87360_write_value(data, LD_IN, offset, PC87365_REG_IN_MAX, \
+                           data->in_max[offset]); \
+       return count; \
+} \
+static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
+       show_in##offset##_input, NULL); \
+static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \
+       show_in##offset##_min, set_in##offset##_min); \
+static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \
+       show_in##offset##_max, set_in##offset##_max); \
+static DEVICE_ATTR(in##offset##_status, S_IRUGO, \
+       show_in##offset##_status, NULL);
+show_and_set_in(0)
+show_and_set_in(1)
+show_and_set_in(2)
+show_and_set_in(3)
+show_and_set_in(4)
+show_and_set_in(5)
+show_and_set_in(6)
+show_and_set_in(7)
+show_and_set_in(8)
+show_and_set_in(9)
+show_and_set_in(10)
+
+#define show_and_set_therm(offset) \
+static ssize_t show_temp##offset##_input(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset+7], \
+                      data->in_vref)); \
+} \
+static ssize_t show_temp##offset##_min(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset+7], \
+                      data->in_vref)); \
+} \
+static ssize_t show_temp##offset##_max(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset+7], \
+                      data->in_vref)); \
+} \
+static ssize_t show_temp##offset##_crit(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[offset-4], \
+                      data->in_vref)); \
+} \
+static ssize_t show_temp##offset##_status(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%u\n", data->in_status[offset+7]); \
+} \
+static ssize_t set_temp##offset##_min(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct pc87360_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->in_min[offset+7] = IN_TO_REG(val, data->in_vref); \
+       pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_MIN, \
+                           data->in_min[offset+7]); \
+       return count; \
+} \
+static ssize_t set_temp##offset##_max(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct pc87360_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->in_max[offset+7] = IN_TO_REG(val, data->in_vref); \
+       pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_MAX, \
+                           data->in_max[offset+7]); \
+       return count; \
+} \
+static ssize_t set_temp##offset##_crit(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct pc87360_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->in_crit[offset-4] = IN_TO_REG(val, data->in_vref); \
+       pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_CRIT, \
+                           data->in_crit[offset-4]); \
+       return count; \
+} \
+static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
+       show_temp##offset##_input, NULL); \
+static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \
+       show_temp##offset##_min, set_temp##offset##_min); \
+static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \
+       show_temp##offset##_max, set_temp##offset##_max); \
+static DEVICE_ATTR(temp##offset##_crit, S_IWUSR | S_IRUGO, \
+       show_temp##offset##_crit, set_temp##offset##_crit); \
+static DEVICE_ATTR(temp##offset##_status, S_IRUGO, \
+       show_temp##offset##_status, NULL);
+show_and_set_therm(4)
+show_and_set_therm(5)
+show_and_set_therm(6)
+
+static ssize_t show_vid(struct device *dev, char *buf)
+{
+       struct pc87360_data *data = pc87360_update_device(dev);
+       return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
+}
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+
+static ssize_t show_vrm(struct device *dev, char *buf)
+{
+       struct pc87360_data *data = pc87360_update_device(dev);
+       return sprintf(buf, "%u\n", data->vrm);
+}
+static ssize_t set_vrm(struct device *dev, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct pc87360_data *data = i2c_get_clientdata(client);
+       data->vrm = simple_strtoul(buf, NULL, 10);
+       return count;
+}
+static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+
+static ssize_t show_in_alarms(struct device *dev, char *buf)
+{
+       struct pc87360_data *data = pc87360_update_device(dev);
+       return sprintf(buf, "%u\n", data->in_alarms);
+}
+static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL);
+
+#define show_and_set_temp(offset) \
+static ssize_t show_temp##offset##_input(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \
+} \
+static ssize_t show_temp##offset##_min(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[offset-1])); \
+} \
+static ssize_t show_temp##offset##_max(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \
+}\
+static ssize_t show_temp##offset##_crit(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[offset-1])); \
+}\
+static ssize_t show_temp##offset##_status(struct device *dev, char *buf) \
+{ \
+       struct pc87360_data *data = pc87360_update_device(dev); \
+       return sprintf(buf, "%d\n", data->temp_status[offset-1]); \
+}\
+static ssize_t set_temp##offset##_min(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct pc87360_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->temp_min[offset-1] = TEMP_TO_REG(val); \
+       pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_MIN, \
+                           data->temp_min[offset-1]); \
+       return count; \
+} \
+static ssize_t set_temp##offset##_max(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct pc87360_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->temp_max[offset-1] = TEMP_TO_REG(val); \
+       pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_MAX, \
+                           data->temp_max[offset-1]); \
+       return count; \
+} \
+static ssize_t set_temp##offset##_crit(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct pc87360_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+       data->temp_crit[offset-1] = TEMP_TO_REG(val); \
+       pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_CRIT, \
+                           data->temp_crit[offset-1]); \
+       return count; \
+} \
+static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
+       show_temp##offset##_input, NULL); \
+static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \
+       show_temp##offset##_min, set_temp##offset##_min); \
+static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \
+       show_temp##offset##_max, set_temp##offset##_max); \
+static DEVICE_ATTR(temp##offset##_crit, S_IWUSR | S_IRUGO, \
+       show_temp##offset##_crit, set_temp##offset##_crit); \
+static DEVICE_ATTR(temp##offset##_status, S_IRUGO, \
+       show_temp##offset##_status, NULL);
+show_and_set_temp(1)
+show_and_set_temp(2)
+show_and_set_temp(3)
+
+static ssize_t show_temp_alarms(struct device *dev, char *buf)
+{
+       struct pc87360_data *data = pc87360_update_device(dev);
+       return sprintf(buf, "%u\n", data->temp_alarms);
+}
+static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL);
+
+/*
+ * Device detection, registration and update
+ */
+
+static int pc87360_attach_adapter(struct i2c_adapter *adapter)
+{
+       return i2c_detect(adapter, &addr_data, pc87360_detect);
+}
+
+static int pc87360_find(int sioaddr, u8 *devid, int *address)
+{
+       u16 val;
+       int i;
+       int nrdev; /* logical device count */
+
+       /* No superio_enter */
+
+       /* Identify device */
+       val = superio_inb(sioaddr, DEVID);
+       switch (val) {
+       case 0xE1: /* PC87360 */
+       case 0xE8: /* PC87363 */
+       case 0xE4: /* PC87364 */
+               nrdev = 1;
+               break;
+       case 0xE5: /* PC87365 */
+       case 0xE9: /* PC87366 */
+               nrdev = 3;
+               break;
+       default:
+               superio_exit(sioaddr);
+               return -ENODEV;
+       }
+       /* Remember the device id */
+       *devid = val;
+
+       for (i = 0; i < nrdev; i++) {
+               /* select logical device */
+               superio_outb(sioaddr, DEV, logdev[i]);
+
+               val = superio_inb(sioaddr, ACT);
+               if (!(val & 0x01)) {
+                       printk(KERN_INFO "pc87360: Device 0x%02x not "
+                              "activated\n", logdev[i]);
+                       continue;
+               }
+
+               val = (superio_inb(sioaddr, BASE) << 8)
+                   | superio_inb(sioaddr, BASE + 1);
+               if (!val) {
+                       printk(KERN_INFO "pc87360: Base address not set for "
+                              "device 0x%02x\n", logdev[i]);
+                       continue;
+               }
+
+               address[i] = val;
+
+               if (i==0) { /* Fans */
+                       confreg[0] = superio_inb(sioaddr, 0xF0);
+                       confreg[1] = superio_inb(sioaddr, 0xF1);
+
+#ifdef DEBUG
+                       printk(KERN_DEBUG "pc87360: Fan 1: mon=%d "
+                              "ctrl=%d inv=%d\n", (confreg[0]>>2)&1,
+                              (confreg[0]>>3)&1, (confreg[0]>>4)&1);
+                       printk(KERN_DEBUG "pc87360: Fan 2: mon=%d "
+                              "ctrl=%d inv=%d\n", (confreg[0]>>5)&1,
+                              (confreg[0]>>6)&1, (confreg[0]>>7)&1);
+                       printk(KERN_DEBUG "pc87360: Fan 3: mon=%d "
+                              "ctrl=%d inv=%d\n", confreg[1]&1,
+                              (confreg[1]>>1)&1, (confreg[1]>>2)&1);
+#endif
+               } else if (i==1) { /* Voltages */
+                       /* Are we using thermistors? */
+                       if (*devid == 0xE9) { /* PC87366 */
+                               /* These registers are not logical-device
+                                  specific, just that we won't need them if
+                                  we don't use the VLM device */
+                               confreg[2] = superio_inb(sioaddr, 0x2B);
+                               confreg[3] = superio_inb(sioaddr, 0x25);
+
+                               if (confreg[2] & 0x40) {
+                                       printk(KERN_INFO "pc87360: Using "
+                                              "thermistors for temperature "
+                                              "monitoring\n");
+                               }
+                               if (confreg[3] & 0xE0) {
+                                       printk(KERN_INFO "pc87360: VID "
+                                              "inputs routed (mode %u)\n",
+                                              confreg[3] >> 5);
+                               }
+                       }
+               }
+       }
+
+       superio_exit(sioaddr);
+       return 0;
+}
+
+/* We don't really care about the address.
+   Read from extra_isa instead. */
+int pc87360_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       int i;
+       struct i2c_client *new_client;
+       struct pc87360_data *data;
+       int err = 0;
+       const char *name = "pc87360";
+       int use_thermistors = 0;
+
+       if (!i2c_is_isa_adapter(adapter))
+               return -ENODEV;
+
+       if (!(data = kmalloc(sizeof(struct pc87360_data), GFP_KERNEL)))
+               return -ENOMEM;
+       memset(data, 0x00, sizeof(struct pc87360_data));
+
+       new_client = &data->client;
+       i2c_set_clientdata(new_client, data);
+       new_client->addr = address;
+       init_MUTEX(&data->lock);
+       new_client->adapter = adapter;
+       new_client->driver = &pc87360_driver;
+       new_client->flags = 0;
+
+       data->fannr = 2;
+       data->innr = 0;
+       data->tempnr = 0;
+
+       switch (devid) {
+       case 0xe8:
+               name = "pc87363";
+               break;
+       case 0xe4:
+               name = "pc87364";
+               data->fannr = 3;
+               break;
+       case 0xe5:
+               name = "pc87365";
+               data->fannr = extra_isa[0] ? 3 : 0;
+               data->innr = extra_isa[1] ? 11 : 0;
+               data->tempnr = extra_isa[2] ? 2 : 0;
+               break;
+       case 0xe9:
+               name = "pc87366";
+               data->fannr = extra_isa[0] ? 3 : 0;
+               data->innr = extra_isa[1] ? 14 : 0;
+               data->tempnr = extra_isa[2] ? 3 : 0;
+               break;
+       }
+
+       strcpy(new_client->name, name);
+       data->valid = 0;
+       init_MUTEX(&data->update_lock);
+
+       for (i = 0; i < 3; i++) {
+               if (((data->address[i] = extra_isa[i]))
+                && !request_region(extra_isa[i], PC87360_EXTENT, "pc87360")) {
+                       dev_err(&new_client->dev, "Region 0x%x-0x%x already "
+                               "in use!\n", extra_isa[i],
+                               extra_isa[i]+PC87360_EXTENT-1);
+                       for (i--; i >= 0; i--)
+                               release_region(extra_isa[i], PC87360_EXTENT);
+                       err = -EBUSY;
+                       goto ERROR1;
+               }
+       }
+
+       /* Retrieve the fans configuration from Super-I/O space */
+       if (data->fannr)
+               data->fan_conf = confreg[0] | (confreg[1] << 8);
+
+       if ((err = i2c_attach_client(new_client)))
+               goto ERROR2;
+
+       /* Use the correct reference voltage
+          Unless both the VLM and the TMS logical devices agree to
+          use an external Vref, the internal one is used. */
+       if (data->innr) {
+               i = pc87360_read_value(data, LD_IN, NO_BANK,
+                                      PC87365_REG_IN_CONFIG);
+               if (data->tempnr) {
+                       i &= pc87360_read_value(data, LD_TEMP, NO_BANK,
+                                               PC87365_REG_TEMP_CONFIG);
+               }
+               data->in_vref = (i&0x02) ? 3025 : 2966;
+               dev_dbg(&new_client->dev, "Using %s reference voltage\n",
+                       (i&0x02) ? "external" : "internal");
+
+               data->vid_conf = confreg[3];
+               data->vrm = 90;
+       }
+
+       /* Fan clock dividers may be needed before any data is read */
+       for (i = 0; i < data->fannr; i++) {
+               data->fan_status[i] = pc87360_read_value(data, LD_FAN,
+                                     NO_BANK, PC87360_REG_FAN_STATUS(i));
+       }
+
+       if (init > 0) {
+               if (devid == 0xe9 && data->address[1]) /* PC87366 */
+                       use_thermistors = confreg[2] & 0x40;
+
+               pc87360_init_client(new_client, use_thermistors);
+       }
+
+       /* Register sysfs hooks */
+       if (data->innr) {
+               device_create_file(&new_client->dev, &dev_attr_in0_input);
+               device_create_file(&new_client->dev, &dev_attr_in1_input);
+               device_create_file(&new_client->dev, &dev_attr_in2_input);
+               device_create_file(&new_client->dev, &dev_attr_in3_input);
+               device_create_file(&new_client->dev, &dev_attr_in4_input);
+               device_create_file(&new_client->dev, &dev_attr_in5_input);
+               device_create_file(&new_client->dev, &dev_attr_in6_input);
+               device_create_file(&new_client->dev, &dev_attr_in7_input);
+               device_create_file(&new_client->dev, &dev_attr_in8_input);
+               device_create_file(&new_client->dev, &dev_attr_in9_input);
+               device_create_file(&new_client->dev, &dev_attr_in10_input);
+               device_create_file(&new_client->dev, &dev_attr_in0_min);
+               device_create_file(&new_client->dev, &dev_attr_in1_min);
+               device_create_file(&new_client->dev, &dev_attr_in2_min);
+               device_create_file(&new_client->dev, &dev_attr_in3_min);
+               device_create_file(&new_client->dev, &dev_attr_in4_min);
+               device_create_file(&new_client->dev, &dev_attr_in5_min);
+               device_create_file(&new_client->dev, &dev_attr_in6_min);
+               device_create_file(&new_client->dev, &dev_attr_in7_min);
+               device_create_file(&new_client->dev, &dev_attr_in8_min);
+               device_create_file(&new_client->dev, &dev_attr_in9_min);
+               device_create_file(&new_client->dev, &dev_attr_in10_min);
+               device_create_file(&new_client->dev, &dev_attr_in0_max);
+               device_create_file(&new_client->dev, &dev_attr_in1_max);
+               device_create_file(&new_client->dev, &dev_attr_in2_max);
+               device_create_file(&new_client->dev, &dev_attr_in3_max);
+               device_create_file(&new_client->dev, &dev_attr_in4_max);
+               device_create_file(&new_client->dev, &dev_attr_in5_max);
+               device_create_file(&new_client->dev, &dev_attr_in6_max);
+               device_create_file(&new_client->dev, &dev_attr_in7_max);
+               device_create_file(&new_client->dev, &dev_attr_in8_max);
+               device_create_file(&new_client->dev, &dev_attr_in9_max);
+               device_create_file(&new_client->dev, &dev_attr_in10_max);
+               device_create_file(&new_client->dev, &dev_attr_in0_status);
+               device_create_file(&new_client->dev, &dev_attr_in1_status);
+               device_create_file(&new_client->dev, &dev_attr_in2_status);
+               device_create_file(&new_client->dev, &dev_attr_in3_status);
+               device_create_file(&new_client->dev, &dev_attr_in4_status);
+               device_create_file(&new_client->dev, &dev_attr_in5_status);
+               device_create_file(&new_client->dev, &dev_attr_in6_status);
+               device_create_file(&new_client->dev, &dev_attr_in7_status);
+               device_create_file(&new_client->dev, &dev_attr_in8_status);
+               device_create_file(&new_client->dev, &dev_attr_in9_status);
+               device_create_file(&new_client->dev, &dev_attr_in10_status);
+
+               device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
+               device_create_file(&new_client->dev, &dev_attr_vrm);
+               device_create_file(&new_client->dev, &dev_attr_alarms_in);
+       }
+
+       if (data->tempnr) {
+               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_temp1_min);
+               device_create_file(&new_client->dev, &dev_attr_temp2_min);
+               device_create_file(&new_client->dev, &dev_attr_temp1_max);
+               device_create_file(&new_client->dev, &dev_attr_temp2_max);
+               device_create_file(&new_client->dev, &dev_attr_temp1_crit);
+               device_create_file(&new_client->dev, &dev_attr_temp2_crit);
+               device_create_file(&new_client->dev, &dev_attr_temp1_status);
+               device_create_file(&new_client->dev, &dev_attr_temp2_status);
+
+               device_create_file(&new_client->dev, &dev_attr_alarms_temp);
+       }
+       if (data->tempnr == 3) {
+               device_create_file(&new_client->dev, &dev_attr_temp3_input);
+               device_create_file(&new_client->dev, &dev_attr_temp3_min);
+               device_create_file(&new_client->dev, &dev_attr_temp3_max);
+               device_create_file(&new_client->dev, &dev_attr_temp3_crit);
+               device_create_file(&new_client->dev, &dev_attr_temp3_status);
+       }
+       if (data->innr == 14) {
+               device_create_file(&new_client->dev, &dev_attr_temp4_input);
+               device_create_file(&new_client->dev, &dev_attr_temp5_input);
+               device_create_file(&new_client->dev, &dev_attr_temp6_input);
+               device_create_file(&new_client->dev, &dev_attr_temp4_min);
+               device_create_file(&new_client->dev, &dev_attr_temp5_min);
+               device_create_file(&new_client->dev, &dev_attr_temp6_min);
+               device_create_file(&new_client->dev, &dev_attr_temp4_max);
+               device_create_file(&new_client->dev, &dev_attr_temp5_max);
+               device_create_file(&new_client->dev, &dev_attr_temp6_max);
+               device_create_file(&new_client->dev, &dev_attr_temp4_crit);
+               device_create_file(&new_client->dev, &dev_attr_temp5_crit);
+               device_create_file(&new_client->dev, &dev_attr_temp6_crit);
+               device_create_file(&new_client->dev, &dev_attr_temp4_status);
+               device_create_file(&new_client->dev, &dev_attr_temp5_status);
+               device_create_file(&new_client->dev, &dev_attr_temp6_status);
+       }
+
+       if (data->fannr) {
+               device_create_file(&new_client->dev, &dev_attr_fan1_input);
+               device_create_file(&new_client->dev, &dev_attr_fan2_input);
+               device_create_file(&new_client->dev, &dev_attr_fan1_min);
+               device_create_file(&new_client->dev, &dev_attr_fan2_min);
+               device_create_file(&new_client->dev, &dev_attr_fan1_div);
+               device_create_file(&new_client->dev, &dev_attr_fan2_div);
+               device_create_file(&new_client->dev, &dev_attr_fan1_status);
+               device_create_file(&new_client->dev, &dev_attr_fan2_status);
+
+               if (FAN_CONFIG_CONTROL(data->fan_conf, 0))
+                       device_create_file(&new_client->dev, &dev_attr_pwm1);
+               if (FAN_CONFIG_CONTROL(data->fan_conf, 1))
+                       device_create_file(&new_client->dev, &dev_attr_pwm2);
+       }
+       if (data->fannr == 3) {
+               device_create_file(&new_client->dev, &dev_attr_fan3_input);
+               device_create_file(&new_client->dev, &dev_attr_fan3_min);
+               device_create_file(&new_client->dev, &dev_attr_fan3_div);
+               device_create_file(&new_client->dev, &dev_attr_fan3_status);
+
+               if (FAN_CONFIG_CONTROL(data->fan_conf, 2))
+                       device_create_file(&new_client->dev, &dev_attr_pwm3);
+       }
+
+       return 0;
+
+ERROR2:
+       for (i = 0; i < 3; i++) {
+               if (data->address[i]) {
+                       release_region(data->address[i], PC87360_EXTENT);
+               }
+       }
+ERROR1:
+       kfree(data);
+       return err;
+}
+
+static int pc87360_detach_client(struct i2c_client *client)
+{
+       struct pc87360_data *data = i2c_get_clientdata(client);
+       int i;
+
+       if ((i = i2c_detach_client(client))) {
+               dev_err(&client->dev, "Client deregistration failed, "
+                       "client not detached.\n");
+               return i;
+       }
+
+       for (i = 0; i < 3; i++) {
+               if (data->address[i]) {
+                       release_region(data->address[i], PC87360_EXTENT);
+               }
+       }
+       kfree(data);
+
+       return 0;
+}
+
+/* ldi is the logical device index
+   bank is for voltages and temperatures only */
+static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank,
+                             u8 reg)
+{
+       int res;
+
+       down(&(data->lock));
+       if (bank != NO_BANK)
+               outb_p(bank, data->address[ldi] + PC87365_REG_BANK);
+       res = inb_p(data->address[ldi] + reg);
+       up(&(data->lock));
+
+       return res;
+}
+
+static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank,
+                               u8 reg, u8 value)
+{
+       down(&(data->lock));
+       if (bank != NO_BANK)
+               outb_p(bank, data->address[ldi] + PC87365_REG_BANK);
+       outb_p(value, data->address[ldi] + reg);
+       up(&(data->lock));
+}
+
+static void pc87360_init_client(struct i2c_client *client, int use_thermistors)
+{
+       struct pc87360_data *data = i2c_get_clientdata(client);
+       int i, nr;
+       const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 };
+       const u8 init_temp[3] = { 2, 2, 1 };
+       u8 reg;
+
+       if (init >= 2 && data->innr) {
+               reg = pc87360_read_value(data, LD_IN, NO_BANK,
+                                        PC87365_REG_IN_CONVRATE);
+               dev_info(&client->dev, "VLM conversion set to"
+                        "1s period, 160us delay\n");
+               pc87360_write_value(data, LD_IN, NO_BANK,
+                                   PC87365_REG_IN_CONVRATE,
+                                   (reg & 0xC0) | 0x11);
+       }
+
+       nr = data->innr < 11 ? data->innr : 11;
+       for (i=0; i<nr; i++) {
+               if (init >= init_in[i]) {
+                       /* Forcibly enable voltage channel */
+                       reg = pc87360_read_value(data, LD_IN, i,
+                                                PC87365_REG_IN_STATUS);
+                       if (!(reg & 0x01)) {
+                               dev_dbg(&client->dev, "Forcibly "
+                                       "enabling in%d\n", i);
+                               pc87360_write_value(data, LD_IN, i,
+                                                   PC87365_REG_IN_STATUS,
+                                                   (reg & 0x68) | 0x87);
+                       }
+               }
+       }
+
+       /* We can't blindly trust the Super-I/O space configuration bit,
+          most BIOS won't set it properly */
+       for (i=11; i<data->innr; i++) {
+               reg = pc87360_read_value(data, LD_IN, i,
+                                        PC87365_REG_TEMP_STATUS);
+               use_thermistors = use_thermistors || (reg & 0x01);
+       }
+
+       i = use_thermistors ? 2 : 0;
+       for (; i<data->tempnr; i++) {
+               if (init >= init_temp[i]) {
+                       /* Forcibly enable temperature channel */
+                       reg = pc87360_read_value(data, LD_TEMP, i,
+                                                PC87365_REG_TEMP_STATUS);
+                       if (!(reg & 0x01)) {
+                               dev_dbg(&client->dev, "Forcibly "
+                                       "enabling temp%d\n", i+1);
+                               pc87360_write_value(data, LD_TEMP, i,
+                                                   PC87365_REG_TEMP_STATUS,
+                                                   0xCF);
+                       }
+               }
+       }
+
+       if (use_thermistors) {
+               for (i=11; i<data->innr; i++) {
+                       if (init >= init_in[i]) {
+                               /* The pin may already be used by thermal
+                                  diodes */
+                               reg = pc87360_read_value(data, LD_TEMP,
+                                     (i-11)/2, PC87365_REG_TEMP_STATUS);
+                               if (reg & 0x01) {
+                                       dev_dbg(&client->dev, "Skipping "
+                                               "temp%d, pin already in use "
+                                               "by temp%d\n", i-7, (i-11)/2);
+                                       continue;
+                               }
+
+                               /* Forcibly enable thermistor channel */
+                               reg = pc87360_read_value(data, LD_IN, i,
+                                                        PC87365_REG_IN_STATUS);
+                               if (!(reg & 0x01)) {
+                                       dev_dbg(&client->dev, "Forcibly "
+                                               "enabling temp%d\n", i-7);
+                                       pc87360_write_value(data, LD_IN, i,
+                                               PC87365_REG_TEMP_STATUS,
+                                               (reg & 0x60) | 0x8F);
+                               }
+                       }
+               }
+       }
+
+       if (data->innr) {
+               reg = pc87360_read_value(data, LD_IN, NO_BANK,
+                                        PC87365_REG_IN_CONFIG);
+               if (reg & 0x01) {
+                       dev_dbg(&client->dev, "Forcibly "
+                               "enabling monitoring (VLM)\n");
+                       pc87360_write_value(data, LD_IN, NO_BANK,
+                                           PC87365_REG_IN_CONFIG,
+                                           reg & 0xFE);
+               }
+       }
+
+       if (data->tempnr) {
+               reg = pc87360_read_value(data, LD_TEMP, NO_BANK,
+                                        PC87365_REG_TEMP_CONFIG);
+               if (reg & 0x01) {
+                       dev_dbg(&client->dev, "Forcibly enabling "
+                               "monitoring (TMS)\n");
+                       pc87360_write_value(data, LD_TEMP, NO_BANK,
+                                           PC87365_REG_TEMP_CONFIG,
+                                           reg & 0xFE);
+               }
+
+               if (init >= 2) {
+                       /* Chip config as documented by National Semi. */
+                       pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08);
+                       /* We voluntarily omit the bank here, in case the
+                          sequence itself matters. It shouldn't be a problem,
+                          since nobody else is supposed to access the
+                          device at that point. */
+                       pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04);
+                       pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35);
+                       pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05);
+                       pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05);
+               }
+       }
+}
+
+static void pc87360_autodiv(struct i2c_client *client, int nr)
+{
+       struct pc87360_data *data = i2c_get_clientdata(client);
+       u8 old_min = data->fan_min[nr];
+
+       /* Increase clock divider if needed and possible */
+       if ((data->fan_status[nr] & 0x04) /* overflow flag */
+        || (data->fan[nr] >= 224)) { /* next to overflow */
+               if ((data->fan_status[nr] & 0x60) != 0x60) {
+                       data->fan_status[nr] += 0x20;
+                       data->fan_min[nr] >>= 1;
+                       data->fan[nr] >>= 1;
+                       dev_dbg(&client->dev, "Increasing "
+                               "clock divider to %d for fan %d\n",
+                               FAN_DIV_FROM_REG(data->fan_status[nr]), nr+1);
+               }
+       } else {
+               /* Decrease clock divider if possible */
+               while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */
+                && data->fan[nr] < 85 /* bad accuracy */
+                && (data->fan_status[nr] & 0x60) != 0x00) {
+                       data->fan_status[nr] -= 0x20;
+                       data->fan_min[nr] <<= 1;
+                       data->fan[nr] <<= 1;
+                       dev_dbg(&client->dev, "Decreasing "
+                               "clock divider to %d for fan %d\n",
+                               FAN_DIV_FROM_REG(data->fan_status[nr]),
+                               nr+1);
+               }
+       }
+
+       /* Write new fan min if it changed */
+       if (old_min != data->fan_min[nr]) {
+               pc87360_write_value(data, LD_FAN, NO_BANK,
+                                   PC87360_REG_FAN_MIN(nr),
+                                   data->fan_min[nr]);
+       }
+}
+
+static struct pc87360_data *pc87360_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct pc87360_data *data = i2c_get_clientdata(client);
+       u8 i;
+
+       down(&data->update_lock);
+
+       if ((jiffies - data->last_updated > HZ * 2)
+        || (jiffies < data->last_updated) || !data->valid) {
+               dev_dbg(&client->dev, "Data update\n");
+
+               /* Fans */
+               for (i = 0; i < data->fannr; i++) {
+                       if (FAN_CONFIG_MONITOR(data->fan_conf, i)) {
+                               data->fan_status[i] =
+                                       pc87360_read_value(data, LD_FAN,
+                                       NO_BANK, PC87360_REG_FAN_STATUS(i));
+                               data->fan[i] = pc87360_read_value(data, LD_FAN,
+                                              NO_BANK, PC87360_REG_FAN(i));
+                               data->fan_min[i] = pc87360_read_value(data,
+                                                  LD_FAN, NO_BANK,
+                                                  PC87360_REG_FAN_MIN(i));
+                               /* Change clock divider if needed */
+                               pc87360_autodiv(client, i);
+                               /* Clear bits and write new divider */
+                               pc87360_write_value(data, LD_FAN, NO_BANK,
+                                                   PC87360_REG_FAN_STATUS(i),
+                                                   data->fan_status[i]);
+                       }
+                       if (FAN_CONFIG_CONTROL(data->fan_conf, i))
+                               data->pwm[i] = pc87360_read_value(data, LD_FAN,
+                                              NO_BANK, PC87360_REG_PWM(i));
+               }
+
+               /* Voltages */
+               for (i = 0; i < data->innr; i++) {
+                       data->in_status[i] = pc87360_read_value(data, LD_IN, i,
+                                            PC87365_REG_IN_STATUS);
+                       /* Clear bits */
+                       pc87360_write_value(data, LD_IN, i,
+                                           PC87365_REG_IN_STATUS,
+                                           data->in_status[i]);
+                       if ((data->in_status[i] & 0x81) == 0x81) {
+                               data->in[i] = pc87360_read_value(data, LD_IN,
+                                             i, PC87365_REG_IN);
+                       }
+                       if (data->in_status[i] & 0x01) {
+                               data->in_min[i] = pc87360_read_value(data,
+                                                 LD_IN, i,
+                                                 PC87365_REG_IN_MIN);
+                               data->in_max[i] = pc87360_read_value(data,
+                                                 LD_IN, i,
+                                                 PC87365_REG_IN_MAX);
+                               if (i >= 11)
+                                       data->in_crit[i-11] =
+                                               pc87360_read_value(data, LD_IN,
+                                               i, PC87365_REG_TEMP_CRIT);
+                       }
+               }
+               if (data->innr) {
+                       data->in_alarms = pc87360_read_value(data, LD_IN,
+                                         NO_BANK, PC87365_REG_IN_ALARMS1)
+                                       | ((pc87360_read_value(data, LD_IN,
+                                           NO_BANK, PC87365_REG_IN_ALARMS2)
+                                           & 0x07) << 8);
+                       data->vid = (data->vid_conf & 0xE0) ?
+                                   pc87360_read_value(data, LD_IN,
+                                   NO_BANK, PC87365_REG_VID) : 0x1F;
+               }
+
+               /* Temperatures */
+               for (i = 0; i < data->tempnr; i++) {
+                       data->temp_status[i] = pc87360_read_value(data,
+                                              LD_TEMP, i,
+                                              PC87365_REG_TEMP_STATUS);
+                       /* Clear bits */
+                       pc87360_write_value(data, LD_TEMP, i,
+                                           PC87365_REG_TEMP_STATUS,
+                                           data->temp_status[i]);
+                       if ((data->temp_status[i] & 0x81) == 0x81) {
+                               data->temp[i] = pc87360_read_value(data,
+                                               LD_TEMP, i,
+                                               PC87365_REG_TEMP);
+                       }
+                       if (data->temp_status[i] & 0x01) {
+                               data->temp_min[i] = pc87360_read_value(data,
+                                                   LD_TEMP, i,
+                                                   PC87365_REG_TEMP_MIN);
+                               data->temp_max[i] = pc87360_read_value(data,
+                                                   LD_TEMP, i,
+                                                   PC87365_REG_TEMP_MAX);
+                               data->temp_crit[i] = pc87360_read_value(data,
+                                                    LD_TEMP, i,
+                                                    PC87365_REG_TEMP_CRIT);
+                       }
+               }
+               if (data->tempnr) {
+                       data->temp_alarms = pc87360_read_value(data, LD_TEMP,
+                                           NO_BANK, PC87365_REG_TEMP_ALARMS)
+                                           & 0x3F;
+               }
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       up(&data->update_lock);
+
+       return data;
+}
+
+static int __init pc87360_init(void)
+{
+       int i;
+
+       if (pc87360_find(0x2e, &devid, extra_isa)
+        && pc87360_find(0x4e, &devid, extra_isa)) {
+               printk(KERN_WARNING "pc87360: PC8736x not detected, "
+                      "module not inserted.\n");
+               return -ENODEV;
+       }
+
+       /* Arbitrarily pick one of the addresses */
+       for (i = 0; i < 3; i++) {
+               if (extra_isa[i] != 0x0000) {
+                       normal_isa[0] = extra_isa[i];
+                       break;
+               }
+       }
+
+       if (normal_isa[0] == 0x0000) {
+               printk(KERN_WARNING "pc87360: No active logical device, "
+                      "module not inserted.\n");
+               return -ENODEV;
+       }
+
+       return i2c_add_driver(&pc87360_driver);
+}
+
+static void __exit pc87360_exit(void)
+{
+       i2c_del_driver(&pc87360_driver);
+}
+
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("PC8736x hardware monitor");
+MODULE_LICENSE("GPL");
+
+module_init(pc87360_init);
+module_exit(pc87360_exit);
diff --git a/drivers/i2c/chips/smsc47b397.c b/drivers/i2c/chips/smsc47b397.c
new file mode 100644 (file)
index 0000000..aab9f24
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+    smsc47b397.c - Part of lm_sensors, Linux kernel modules
+                       for hardware monitoring
+
+    Supports the SMSC LPC47B397-NC Super-I/O chip.
+
+    Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com>
+       Copyright (C) 2004 Utilitek Systems, Inc.
+
+    derived in part from smsc47m1.c:
+       Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+       Copyright (C) 2004 Jean Delvare <khali@linux-fr.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.
+
+    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 <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+/* Address is autodetected, there is no default value */
+static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
+static struct i2c_force_data forces[] = {{NULL}};
+
+enum chips { any_chip, smsc47b397 };
+static struct i2c_address_data addr_data = {
+       .normal_i2c             = normal_i2c,
+       .normal_isa             = normal_isa,
+       .probe                  = normal_i2c,           /* cheat */
+       .ignore                 = normal_i2c,           /* cheat */
+       .forces                 = forces,
+};
+
+/* Super-I/0 registers and commands */
+
+#define        REG     0x2e    /* The register to read/write */
+#define        VAL     0x2f    /* The value to read/write */
+
+static inline void superio_outb(int reg, int val)
+{
+       outb(reg, REG);
+       outb(val, VAL);
+}
+
+static inline int superio_inb(int reg)
+{
+       outb(reg, REG);
+       return inb(VAL);
+}
+
+/* select superio logical device */
+static inline void superio_select(int ld)
+{
+       superio_outb(0x07, ld);
+}
+
+static inline void superio_enter(void)
+{
+       outb(0x55, REG);
+}
+
+static inline void superio_exit(void)
+{
+       outb(0xAA, REG);
+}
+
+#define SUPERIO_REG_DEVID      0x20
+#define SUPERIO_REG_DEVREV     0x21
+#define SUPERIO_REG_BASE_MSB   0x60
+#define SUPERIO_REG_BASE_LSB   0x61
+#define SUPERIO_REG_LD8                0x08
+
+#define SMSC_EXTENT            0x02
+
+/* 0 <= nr <= 3 */
+static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80};
+#define SMSC47B397_REG_TEMP(nr)        (smsc47b397_reg_temp[(nr)])
+
+/* 0 <= nr <= 3 */
+#define SMSC47B397_REG_FAN_LSB(nr) (0x28 + 2 * (nr))
+#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr))
+
+struct smsc47b397_data {
+       struct i2c_client client;
+       struct semaphore lock;
+
+       struct semaphore update_lock;
+       unsigned long last_updated; /* in jiffies */
+       int valid;
+
+       /* register values */
+       u16 fan[4];
+       u8 temp[4];
+};
+
+static int smsc47b397_read_value(struct i2c_client *client, u8 reg)
+{
+       struct smsc47b397_data *data = i2c_get_clientdata(client);
+       int res;
+
+       down(&data->lock);
+       outb(reg, client->addr);
+       res = inb_p(client->addr + 1);
+       up(&data->lock);
+       return res;
+}
+
+static struct smsc47b397_data *smsc47b397_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct smsc47b397_data *data = i2c_get_clientdata(client);
+       int i;
+
+       down(&data->update_lock);
+
+       if (time_after(jiffies - data->last_updated, (unsigned long)HZ)
+               || time_before(jiffies, data->last_updated) || !data->valid) {
+
+               dev_dbg(&client->dev, "starting device update...\n");
+
+               /* 4 temperature inputs, 4 fan inputs */
+               for (i = 0; i < 4; i++) {
+                       data->temp[i] = smsc47b397_read_value(client,
+                                       SMSC47B397_REG_TEMP(i));
+
+                       /* must read LSB first */
+                       data->fan[i]  = smsc47b397_read_value(client,
+                                       SMSC47B397_REG_FAN_LSB(i));
+                       data->fan[i] |= smsc47b397_read_value(client,
+                                       SMSC47B397_REG_FAN_MSB(i)) << 8;
+               }
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+
+               dev_dbg(&client->dev, "... device update complete\n");
+       }
+
+       up(&data->update_lock);
+
+       return data;
+}
+
+/* TEMP: 0.001C/bit (-128C to +127C)
+   REG: 1C/bit, two's complement */
+static int temp_from_reg(u8 reg)
+{
+       return (s8)reg * 1000;
+}
+
+/* 0 <= nr <= 3 */
+static ssize_t show_temp(struct device *dev, char *buf, int nr)
+{
+       struct smsc47b397_data *data = smsc47b397_update_device(dev);
+       return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr]));
+}
+
+#define sysfs_temp(num) \
+static ssize_t show_temp##num(struct device *dev, char *buf) \
+{ \
+       return show_temp(dev, buf, num-1); \
+} \
+static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL)
+
+sysfs_temp(1);
+sysfs_temp(2);
+sysfs_temp(3);
+sysfs_temp(4);
+
+#define device_create_file_temp(client, num) \
+       device_create_file(&client->dev, &dev_attr_temp##num##_input)
+
+/* FAN: 1 RPM/bit
+   REG: count of 90kHz pulses / revolution */
+static int fan_from_reg(u16 reg)
+{
+       return 90000 * 60 / reg;
+}
+
+/* 0 <= nr <= 3 */
+static ssize_t show_fan(struct device *dev, char *buf, int nr)
+{
+        struct smsc47b397_data *data = smsc47b397_update_device(dev);
+        return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr]));
+}
+
+#define sysfs_fan(num) \
+static ssize_t show_fan##num(struct device *dev, char *buf) \
+{ \
+       return show_fan(dev, buf, num-1); \
+} \
+static DEVICE_ATTR(fan##num##_input, S_IRUGO, show_fan##num, NULL)
+
+sysfs_fan(1);
+sysfs_fan(2);
+sysfs_fan(3);
+sysfs_fan(4);
+
+#define device_create_file_fan(client, num) \
+       device_create_file(&client->dev, &dev_attr_fan##num##_input)
+
+static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind);
+
+static int smsc47b397_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_detect(adapter, &addr_data, smsc47b397_detect);
+}
+
+static int smsc47b397_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;
+       }
+
+       release_region(client->addr, SMSC_EXTENT);
+       kfree(i2c_get_clientdata(client));
+
+       return 0;
+}
+
+static struct i2c_driver smsc47b397_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "smsc47b397",
+       .id             = I2C_DRIVERID_SMSC47B397,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = smsc47b397_attach_adapter,
+       .detach_client  = smsc47b397_detach_client,
+};
+
+static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind)
+{
+       struct i2c_client *new_client;
+       struct smsc47b397_data *data;
+       int err = 0;
+
+       if (!i2c_is_isa_adapter(adapter)) {
+               return 0;
+       }
+
+       if (!request_region(addr, SMSC_EXTENT, smsc47b397_driver.name)) {
+               dev_err(&adapter->dev, "Region 0x%x already in use!\n", addr);
+               return -EBUSY;
+       }
+
+       if (!(data = kmalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto error_release;
+       }
+       memset(data, 0x00, sizeof(struct smsc47b397_data));
+
+       new_client = &data->client;
+       i2c_set_clientdata(new_client, data);
+       new_client->addr = addr;
+       init_MUTEX(&data->lock);
+       new_client->adapter = adapter;
+       new_client->driver = &smsc47b397_driver;
+       new_client->flags = 0;
+
+       strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE);
+
+       init_MUTEX(&data->update_lock);
+
+       if ((err = i2c_attach_client(new_client)))
+               goto error_free;
+
+       device_create_file_temp(new_client, 1);
+       device_create_file_temp(new_client, 2);
+       device_create_file_temp(new_client, 3);
+       device_create_file_temp(new_client, 4);
+
+       device_create_file_fan(new_client, 1);
+       device_create_file_fan(new_client, 2);
+       device_create_file_fan(new_client, 3);
+       device_create_file_fan(new_client, 4);
+
+       return 0;
+
+error_free:
+       kfree(new_client);
+error_release:
+       release_region(addr, SMSC_EXTENT);
+       return err;
+}
+
+static int __init smsc47b397_find(unsigned int *addr)
+{
+       u8 id, rev;
+
+       superio_enter();
+       id = superio_inb(SUPERIO_REG_DEVID);
+
+       if (id != 0x6f) {
+               superio_exit();
+               return -ENODEV;
+       }
+
+       rev = superio_inb(SUPERIO_REG_DEVREV);
+
+       superio_select(SUPERIO_REG_LD8);
+       *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8)
+                |  superio_inb(SUPERIO_REG_BASE_LSB);
+
+       printk(KERN_INFO "smsc47b397: found SMSC LPC47B397-NC "
+               "(base address 0x%04x, revision %u)\n", *addr, rev);
+
+       superio_exit();
+       return 0;
+}
+
+static int __init smsc47b397_init(void)
+{
+       int ret;
+
+       if ((ret = smsc47b397_find(normal_isa)))
+               return ret;
+
+       return i2c_add_driver(&smsc47b397_driver);
+}
+
+static void __exit smsc47b397_exit(void)
+{
+       i2c_del_driver(&smsc47b397_driver);
+}
+
+MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
+MODULE_DESCRIPTION("SMSC LPC47B397 driver");
+MODULE_LICENSE("GPL");
+
+module_init(smsc47b397_init);
+module_exit(smsc47b397_exit);
diff --git a/drivers/ide/cris/Makefile b/drivers/ide/cris/Makefile
new file mode 100644 (file)
index 0000000..fdc2943
--- /dev/null
@@ -0,0 +1,3 @@
+EXTRA_CFLAGS                           += -Idrivers/ide
+
+obj-$(CONFIG_ETRAX_ARCH_V10)           += ide-v10.o
diff --git a/drivers/ide/cris/ide-v10.c b/drivers/ide/cris/ide-v10.c
new file mode 100644 (file)
index 0000000..4631b4a
--- /dev/null
@@ -0,0 +1,859 @@
+/* $Id: ide.c,v 1.4 2004/10/12 07:55:48 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 <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+#include <linux/scatterlist.h>
+
+#include <asm/io.h>
+#include <asm/arch/svinto.h>
+#include <asm/dma.h>
+
+/* 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
+
+static int e100_read_command = 0;
+
+#define LOWDB(x)
+#define D(x)
+
+static int e100_ide_build_dmatable (ide_drive_t *drive);
+static ide_startstop_t etrax_dma_intr (ide_drive_t *drive);
+
+void
+etrax100_ide_outw(unsigned short data, unsigned long 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, unsigned long 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(unsigned long 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(unsigned long 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 void e100_dma_start(ide_drive_t *drive);
+static int e100_dma_end (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);
+
+
+/*
+ * 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;
+       }
+}
+
+static int e100_dma_setup(ide_drive_t *drive)
+{
+       struct request *rq = drive->hwif->hwgroup->rq;
+
+       if (rq_data_dir(rq)) {
+               e100_read_command = 0;
+
+               RESET_DMA(ATA_TX_DMA_NBR); /* sometimes the DMA channel get stuck so we need to do this */
+               WAIT_DMA(ATA_TX_DMA_NBR);
+       } else {
+               e100_read_command = 1;
+
+               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)) {
+               ide_map_sg(drive, rq);
+               return 1;
+       }
+
+       return 0;
+}
+
+static void e100_dma_exec_cmd(ide_drive_t *drive, u8 command)
+{
+       /* 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 */
+       etrax100_ide_outb(command, IDE_COMMAND_REG);
+}
+
+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->dma_setup = &e100_dma_setup;
+               hwif->dma_exec_cmd = &e100_dma_exec_cmd;
+               hwif->dma_start = &e100_dma_start;
+               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;
+       }
+
+       /* 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 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)
+{
+       unsigned long 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)
+{
+       unsigned long 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;
+
+       ide_map_sg(drive, rq);
+
+       i = hwif->sg_nents;
+
+       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.
+ *
+ * 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 void e100_dma_start(ide_drive_t *drive)
+{
+       if (e100_read_command) {
+               /* 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 */
+               /* 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));
+       }
+}
diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c
new file mode 100644 (file)
index 0000000..41d8acb
--- /dev/null
@@ -0,0 +1,812 @@
+
+/*
+ * linux/drivers/ide/pci/it821x.c              Version 0.09    December 2004
+ *
+ * Copyright (C) 2004          Red Hat <alan@redhat.com>
+ *
+ *  May be copied or modified under the terms of the GNU General Public License
+ *  Based in part on the ITE vendor provided SCSI driver.
+ *
+ *  Documentation available from
+ *     http://www.ite.com.tw/pc/IT8212F_V04.pdf
+ *  Some other documents are NDA.
+ *
+ *  The ITE8212 isn't exactly a standard IDE controller. It has two
+ *  modes. In pass through mode then it is an IDE controller. In its smart
+ *  mode its actually quite a capable hardware raid controller disguised
+ *  as an IDE controller. Smart mode only understands DMA read/write and
+ *  identify, none of the fancier commands apply. The IT8211 is identical
+ *  in other respects but lacks the raid mode.
+ *
+ *  Errata:
+ *  o  Rev 0x10 also requires master/slave hold the same DMA timings and
+ *     cannot do ATAPI MWDMA. 
+ *  o  The identify data for raid volumes lacks CHS info (technically ok)
+ *     but also fails to set the LBA28 and other bits. We fix these in
+ *     the IDE probe quirk code.
+ *  o  If you write LBA48 sized I/O's (ie > 256 sector) in smart mode
+ *     raid then the controller firmware dies
+ *  o  Smart mode without RAID doesn't clear all the necessary identify
+ *     bits to reduce the command set to the one used
+ *
+ *  This has a few impacts on the driver
+ *  - In pass through mode we do all the work you would expect
+ *  - In smart mode the clocking set up is done by the controller generally
+ *    but we must watch the other limits and filter.
+ *  - There are a few extra vendor commands that actually talk to the
+ *    controller but only work PIO with no IRQ.
+ *
+ *  Vendor areas of the identify block in smart mode are used for the
+ *  timing and policy set up. Each HDD in raid mode also has a serial
+ *  block on the disk. The hardware extra commands are get/set chip status,
+ *  rebuild, get rebuild status.
+ *
+ *  In Linux the driver supports pass through mode as if the device was
+ *  just another IDE controller. If the smart mode is running then
+ *  volumes are managed by the controller firmware and each IDE "disk"
+ *  is a raid volume. Even more cute - the controller can do automated
+ *  hotplug and rebuild.
+ *
+ *  The pass through controller itself is a little demented. It has a
+ *  flaw that it has a single set of PIO/MWDMA timings per channel so
+ *  non UDMA devices restrict each others performance. It also has a
+ *  single clock source per channel so mixed UDMA100/133 performance
+ *  isn't perfect and we have to pick a clock. Thankfully none of this
+ *  matters in smart mode. ATAPI DMA is not currently supported.
+ *
+ *  It seems the smart mode is a win for RAID1/RAID10 but otherwise not.
+ *
+ *  TODO
+ *     -       ATAPI UDMA is ok but not MWDMA it seems
+ *     -       RAID configuration ioctls
+ *     -       Move to libata once it grows up
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+struct it821x_dev
+{
+       unsigned int smart:1,           /* Are we in smart raid mode */
+               timing10:1;             /* Rev 0x10 */
+       u8      clock_mode;             /* 0, ATA_50 or ATA_66 */
+       u8      want[2][2];             /* Mode/Pri log for master slave */
+       /* We need these for switching the clock when DMA goes on/off
+          The high byte is the 66Mhz timing */
+       u16     pio[2];                 /* Cached PIO values */
+       u16     mwdma[2];               /* Cached MWDMA values */
+       u16     udma[2];                /* Cached UDMA values (per drive) */
+};
+
+#define ATA_66         0
+#define ATA_50         1
+#define ATA_ANY                2
+
+#define UDMA_OFF       0
+#define MWDMA_OFF      0
+
+/*
+ *     We allow users to force the card into non raid mode without
+ *     flashing the alternative BIOS. This is also neccessary right now
+ *     for embedded platforms that cannot run a PC BIOS but are using this
+ *     device.
+ */
+static int it8212_noraid;
+
+/**
+ *     it821x_program  -       program the PIO/MWDMA registers
+ *     @drive: drive to tune
+ *
+ *     Program the PIO/MWDMA timing for this channel according to the
+ *     current clock.
+ */
+
+static void it821x_program(ide_drive_t *drive, u16 timing)
+{
+       ide_hwif_t *hwif        = drive->hwif;
+       struct it821x_dev *itdev = ide_get_hwifdata(hwif);
+       int channel = hwif->channel;
+       u8 conf;
+
+       /* Program PIO/MWDMA timing bits */
+       if(itdev->clock_mode == ATA_66)
+               conf = timing >> 8;
+       else    
+               conf = timing & 0xFF;
+       pci_write_config_byte(hwif->pci_dev, 0x54 + 4 * channel, conf);
+}
+
+/**
+ *     it821x_program_udma     -       program the UDMA registers
+ *     @drive: drive to tune
+ *
+ *     Program the UDMA timing for this drive according to the
+ *     current clock.
+ */
+
+static void it821x_program_udma(ide_drive_t *drive, u16 timing)
+{
+       ide_hwif_t *hwif        = drive->hwif;
+       struct it821x_dev *itdev = ide_get_hwifdata(hwif);
+       int channel = hwif->channel;
+       int unit = drive->select.b.unit;
+       u8 conf;
+       
+       /* Program UDMA timing bits */
+       if(itdev->clock_mode == ATA_66)
+               conf = timing >> 8;
+       else
+               conf = timing & 0xFF;
+       if(itdev->timing10 == 0)
+               pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + unit, conf);
+       else {
+               pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel, conf);
+               pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + 1, conf);
+       }
+}
+
+
+/**
+ *     it821x_clock_strategy
+ *     @hwif: hardware interface
+ *
+ *     Select between the 50 and 66Mhz base clocks to get the best
+ *     results for this interface.
+ */
+
+static void it821x_clock_strategy(ide_drive_t *drive)
+{
+       ide_hwif_t *hwif = drive->hwif;
+       struct it821x_dev *itdev = ide_get_hwifdata(hwif);
+
+       u8 unit = drive->select.b.unit;
+       ide_drive_t *pair = &hwif->drives[1-unit];
+
+       int clock, altclock;
+       u8 v;
+       int sel = 0;
+
+       if(itdev->want[0][0] > itdev->want[1][0]) {
+               clock = itdev->want[0][1];
+               altclock = itdev->want[1][1];
+       } else {
+               clock = itdev->want[1][1];
+               altclock = itdev->want[0][1];
+       }
+
+       /* Master doesn't care does the slave ? */
+       if(clock == ATA_ANY)
+               clock = altclock;
+               
+       /* Nobody cares - keep the same clock */
+       if(clock == ATA_ANY)
+               return;
+       /* No change */
+       if(clock == itdev->clock_mode)
+               return;
+               
+       /* Load this into the controller ? */
+       if(clock == ATA_66)
+               itdev->clock_mode = ATA_66;
+       else {
+               itdev->clock_mode = ATA_50;
+               sel = 1;
+       }
+       pci_read_config_byte(hwif->pci_dev, 0x50, &v);
+       v &= ~(1 << (1 + hwif->channel));
+       v |= sel << (1 + hwif->channel);
+       pci_write_config_byte(hwif->pci_dev, 0x50, v);
+       
+       /*
+        *      Reprogram the UDMA/PIO of the pair drive for the switch
+        *      MWDMA will be dealt with by the dma switcher
+        */
+       if(pair && itdev->udma[1-unit] != UDMA_OFF) {
+               it821x_program_udma(pair, itdev->udma[1-unit]);
+               it821x_program(pair, itdev->pio[1-unit]);
+       }
+       /*
+        *      Reprogram the UDMA/PIO of our drive for the switch.
+        *      MWDMA will be dealt with by the dma switcher
+        */
+       if(itdev->udma[unit] != UDMA_OFF) {
+               it821x_program_udma(drive, itdev->udma[unit]);
+               it821x_program(drive, itdev->pio[unit]);
+       }
+}
+
+/**
+ *     it821x_ratemask -       Compute available modes
+ *     @drive: IDE drive
+ *
+ *     Compute the available speeds for the devices on the interface. This
+ *     is all modes to ATA133 clipped by drive cable setup.
+ */
+
+static u8 it821x_ratemask (ide_drive_t *drive)
+{
+       u8 mode = 4;
+       if (!eighty_ninty_three(drive))
+               mode = min(mode, (u8)1);
+       return mode;
+}
+
+/**
+ *     it821x_tuneproc -       tune a drive
+ *     @drive: drive to tune
+ *     @mode_wanted: the target operating mode
+ *
+ *     Load the timing settings for this device mode into the
+ *     controller. By the time we are called the mode has been
+ *     modified as neccessary to handle the absence of seperate
+ *     master/slave timers for MWDMA/PIO.
+ *
+ *     This code is only used in pass through mode.
+ */
+
+static void it821x_tuneproc (ide_drive_t *drive, byte mode_wanted)
+{
+       ide_hwif_t *hwif        = drive->hwif;
+       struct it821x_dev *itdev = ide_get_hwifdata(hwif);
+       int unit = drive->select.b.unit;
+       
+       /* Spec says 89 ref driver uses 88 */
+       static u16 pio[]        = { 0xAA88, 0xA382, 0xA181, 0x3332, 0x3121 };
+       static u8 pio_want[]    = { ATA_66, ATA_66, ATA_66, ATA_66, ATA_ANY };
+       
+       if(itdev->smart)
+               return;
+
+       /* We prefer 66Mhz clock for PIO 0-3, don't care for PIO4 */
+       itdev->want[unit][1] = pio_want[mode_wanted];
+       itdev->want[unit][0] = 1;       /* PIO is lowest priority */
+       itdev->pio[unit] = pio[mode_wanted];
+       it821x_clock_strategy(drive);
+       it821x_program(drive, itdev->pio[unit]);
+}
+
+/**
+ *     it821x_tune_mwdma       -       tune a channel for MWDMA
+ *     @drive: drive to set up
+ *     @mode_wanted: the target operating mode
+ *
+ *     Load the timing settings for this device mode into the
+ *     controller when doing MWDMA in pass through mode. The caller
+ *     must manage the whole lack of per device MWDMA/PIO timings and
+ *     the shared MWDMA/PIO timing register.
+ */
+
+static void it821x_tune_mwdma (ide_drive_t *drive, byte mode_wanted)
+{
+       ide_hwif_t *hwif        = drive->hwif;
+       struct it821x_dev *itdev = (void *)ide_get_hwifdata(hwif);
+       int unit = drive->select.b.unit;
+       int channel = hwif->channel;
+       u8 conf;
+       
+       static u16 dma[]        = { 0x8866, 0x3222, 0x3121 };
+       static u8 mwdma_want[]  = { ATA_ANY, ATA_66, ATA_ANY };
+       
+       itdev->want[unit][1] = mwdma_want[mode_wanted];
+       itdev->want[unit][0] = 2;       /* MWDMA is low priority */
+       itdev->mwdma[unit] = dma[mode_wanted];
+       itdev->udma[unit] = UDMA_OFF;
+       
+       /* UDMA bits off - Revision 0x10 do them in pairs */
+       pci_read_config_byte(hwif->pci_dev, 0x50, &conf);
+       if(itdev->timing10)
+               conf |= channel ? 0x60: 0x18;
+       else
+               conf |= 1 << (3 + 2 * channel + unit);
+       pci_write_config_byte(hwif->pci_dev, 0x50, conf);
+
+       it821x_clock_strategy(drive);
+       /* FIXME: do we need to program this ? */
+       /* it821x_program(drive, itdev->mwdma[unit]); */
+}
+
+/**
+ *     it821x_tune_udma        -       tune a channel for UDMA
+ *     @drive: drive to set up
+ *     @mode_wanted: the target operating mode
+ *
+ *     Load the timing settings for this device mode into the
+ *     controller when doing UDMA modes in pass through.
+ */
+
+static void it821x_tune_udma (ide_drive_t *drive, byte mode_wanted)
+{
+       ide_hwif_t *hwif        = drive->hwif;
+       struct it821x_dev *itdev = ide_get_hwifdata(hwif);
+       int unit = drive->select.b.unit;
+       int channel = hwif->channel;
+       u8 conf;
+
+       static u16 udma[]       = { 0x4433, 0x4231, 0x3121, 0x2121, 0x1111, 0x2211, 0x1111 };
+       static u8 udma_want[]   = { ATA_ANY, ATA_50, ATA_ANY, ATA_66, ATA_66, ATA_50, ATA_66 };
+       
+       itdev->want[unit][1] = udma_want[mode_wanted];
+       itdev->want[unit][0] = 3;       /* UDMA is high priority */
+       itdev->mwdma[unit] = MWDMA_OFF;
+       itdev->udma[unit] = udma[mode_wanted];
+       if(mode_wanted >= 5)
+               itdev->udma[unit] |= 0x8080;    /* UDMA 5/6 select on */
+               
+       /* UDMA on. Again revision 0x10 must do the pair */
+       pci_read_config_byte(hwif->pci_dev, 0x50, &conf);
+       if(itdev->timing10)
+               conf &= channel ? 0x9F: 0xE7;
+       else
+               conf &= ~ (1 << (3 + 2 * channel + unit));
+       pci_write_config_byte(hwif->pci_dev, 0x50, conf);
+
+       it821x_clock_strategy(drive);
+       it821x_program_udma(drive, itdev->udma[unit]);
+       
+}
+
+/**
+ *     config_it821x_chipset_for_pio   -       set drive timings
+ *     @drive: drive to tune
+ *     @speed we want
+ *
+ *     Compute the best pio mode we can for a given device. We must
+ *     pick a speed that does not cause problems with the other device
+ *     on the cable.
+ */
+
+static void config_it821x_chipset_for_pio (ide_drive_t *drive, byte set_speed)
+{
+       u8 unit = drive->select.b.unit;
+       ide_hwif_t *hwif = drive->hwif;
+       ide_drive_t *pair = &hwif->drives[1-unit];
+       u8 speed = 0, set_pio   = ide_get_best_pio_mode(drive, 255, 5, NULL);
+       u8 pair_pio;
+       
+       /* We have to deal with this mess in pairs */
+       if(pair != NULL) {
+               pair_pio = ide_get_best_pio_mode(pair, 255, 5, NULL);
+               /* Trim PIO to the slowest of the master/slave */
+               if(pair_pio < set_pio)
+                       set_pio = pair_pio;
+       }
+       it821x_tuneproc(drive, set_pio);
+       speed = XFER_PIO_0 + set_pio;
+       /* XXX - We trim to the lowest of the pair so the other drive
+          will always be fine at this point until we do hotplug passthru */
+
+       if (set_speed)
+               (void) ide_config_drive_speed(drive, speed);
+}
+
+/**
+ *     it821x_dma_read -       DMA hook
+ *     @drive: drive for DMA
+ *
+ *     The IT821x has a single timing register for MWDMA and for PIO
+ *     operations. As we flip back and forth we have to reload the
+ *     clock. In addition the rev 0x10 device only works if the same
+ *     timing value is loaded into the master and slave UDMA clock
+ *     so we must also reload that.
+ *
+ *     FIXME: we could figure out in advance if we need to do reloads
+ */
+
+static void it821x_dma_start(ide_drive_t *drive)
+{
+       ide_hwif_t *hwif = drive->hwif;
+       struct it821x_dev *itdev = ide_get_hwifdata(hwif);
+       int unit = drive->select.b.unit;
+       if(itdev->mwdma[unit] != MWDMA_OFF)
+               it821x_program(drive, itdev->mwdma[unit]);
+       else if(itdev->udma[unit] != UDMA_OFF && itdev->timing10)
+               it821x_program_udma(drive, itdev->udma[unit]);
+       ide_dma_start(drive);
+}
+
+/**
+ *     it821x_dma_write        -       DMA hook
+ *     @drive: drive for DMA stop
+ *
+ *     The IT821x has a single timing register for MWDMA and for PIO
+ *     operations. As we flip back and forth we have to reload the
+ *     clock.
+ */
+
+static int it821x_dma_end(ide_drive_t *drive)
+{
+       ide_hwif_t *hwif = drive->hwif;
+       int unit = drive->select.b.unit;
+       struct it821x_dev *itdev = ide_get_hwifdata(hwif);
+       int ret = __ide_dma_end(drive);
+       if(itdev->mwdma[unit] != MWDMA_OFF)
+               it821x_program(drive, itdev->pio[unit]);
+       return ret;
+}
+
+       
+/**
+ *     it821x_tune_chipset     -       set controller timings
+ *     @drive: Drive to set up
+ *     @xferspeed: speed we want to achieve
+ *
+ *     Tune the ITE chipset for the desired mode. If we can't achieve
+ *     the desired mode then tune for a lower one, but ultimately
+ *     make the thing work.
+ */
+
+static int it821x_tune_chipset (ide_drive_t *drive, byte xferspeed)
+{
+
+       ide_hwif_t *hwif        = drive->hwif;
+       struct it821x_dev *itdev = ide_get_hwifdata(hwif);
+       u8 speed                = ide_rate_filter(it821x_ratemask(drive), xferspeed);
+
+       if(!itdev->smart) {
+               switch(speed) {
+                       case XFER_PIO_4:
+                       case XFER_PIO_3:
+                       case XFER_PIO_2:
+                       case XFER_PIO_1:
+                       case XFER_PIO_0:
+                               it821x_tuneproc(drive, (speed - XFER_PIO_0));
+                               break;
+                       /* MWDMA tuning is really hard because our MWDMA and PIO
+                          timings are kept in the same place. We can switch in the
+                          host dma on/off callbacks */
+                       case XFER_MW_DMA_2:
+                       case XFER_MW_DMA_1:
+                       case XFER_MW_DMA_0:
+                               it821x_tune_mwdma(drive, (speed - XFER_MW_DMA_0));
+                               break;
+                       case XFER_UDMA_6:
+                       case XFER_UDMA_5:
+                       case XFER_UDMA_4:
+                       case XFER_UDMA_3:
+                       case XFER_UDMA_2:
+                       case XFER_UDMA_1:
+                       case XFER_UDMA_0:
+                               it821x_tune_udma(drive, (speed - XFER_UDMA_0));
+                               break;
+                       default:
+                               return 1;
+               }
+       }
+       /*
+        *      In smart mode the clocking is done by the host controller
+        *      snooping the mode we picked. The rest of it is not our problem
+        */
+       return ide_config_drive_speed(drive, speed);
+}
+
+/**
+ *     config_chipset_for_dma  -       configure for DMA
+ *     @drive: drive to configure
+ *
+ *     Called by the IDE layer when it wants the timings set up.
+ */
+
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+       u8 speed        = ide_dma_speed(drive, it821x_ratemask(drive));
+
+       config_it821x_chipset_for_pio(drive, !speed);
+       it821x_tune_chipset(drive, speed);
+       return ide_dma_enable(drive);
+}
+
+/**
+ *     it821x_configure_drive_for_dma  -       set up for DMA transfers
+ *     @drive: drive we are going to set up
+ *
+ *     Set up the drive for DMA, tune the controller and drive as
+ *     required. If the drive isn't suitable for DMA or we hit
+ *     other problems then we will drop down to PIO and set up
+ *     PIO appropriately
+ */
+
+static int it821x_config_drive_for_dma (ide_drive_t *drive)
+{
+       ide_hwif_t *hwif        = drive->hwif;
+
+       if (ide_use_dma(drive)) {
+               if (config_chipset_for_dma(drive))
+                       return hwif->ide_dma_on(drive);
+       }
+       config_it821x_chipset_for_pio(drive, 1);
+       return hwif->ide_dma_off_quietly(drive);
+}
+
+/**
+ *     ata66_it821x    -       check for 80 pin cable
+ *     @hwif: interface to check
+ *
+ *     Check for the presence of an ATA66 capable cable on the
+ *     interface. Problematic as it seems some cards don't have
+ *     the needed logic onboard.
+ */
+
+static unsigned int __devinit ata66_it821x(ide_hwif_t *hwif)
+{
+       /* The reference driver also only does disk side */
+       return 1;
+}
+
+/**
+ *     it821x_fixup    -       post init callback
+ *     @hwif: interface
+ *
+ *     This callback is run after the drives have been probed but
+ *     before anything gets attached. It allows drivers to do any 
+ *     final tuning that is needed, or fixups to work around bugs.
+ */
+
+static void __devinit it821x_fixups(ide_hwif_t *hwif)
+{
+       struct it821x_dev *itdev = ide_get_hwifdata(hwif);
+       int i;
+
+       if(!itdev->smart) {
+               /*
+                *      If we are in pass through mode then not much
+                *      needs to be done, but we do bother to clear the
+                *      IRQ mask as we may well be in PIO (eg rev 0x10)
+                *      for now and we know unmasking is safe on this chipset.
+                */
+               for (i = 0; i < 2; i++) {
+                       ide_drive_t *drive = &hwif->drives[i];
+                       if(drive->present)
+                               drive->unmask = 1;
+               }
+               return;
+       }
+       /*
+        *      Perform fixups on smart mode. We need to "lose" some
+        *      capabilities the firmware lacks but does not filter, and
+        *      also patch up some capability bits that it forgets to set
+        *      in RAID mode.
+        */
+       
+       for(i = 0; i < 2; i++) {
+               ide_drive_t *drive = &hwif->drives[i];
+               struct hd_driveid *id;
+               u16 *idbits;
+               
+               if(!drive->present)
+                       continue;
+               id = drive->id;
+               idbits = (u16 *)drive->id;
+               
+               /* Check for RAID v native */
+               if(strstr(id->model, "Integrated Technology Express")) {
+                       /* In raid mode the ident block is slightly buggy
+                          We need to set the bits so that the IDE layer knows
+                          LBA28. LBA48 and DMA ar valid */
+                       id->capability |= 3;            /* LBA28, DMA */
+                       id->command_set_2 |= 0x0400;    /* LBA48 valid */
+                       id->cfs_enable_2 |= 0x0400;     /* LBA48 on */
+                       /* Reporting logic */
+                       printk(KERN_INFO "%s: IT8212 %sRAID %d volume",
+                               drive->name,
+                               idbits[147] ? "Bootable ":"",
+                               idbits[129]);
+                               if(idbits[129] != 1)
+                                       printk("(%dK stripe)", idbits[146]);
+                               printk(".\n");
+                       /* Now the core code will have wrongly decided no DMA 
+                          so we need to fix this */
+                       hwif->ide_dma_off_quietly(drive);
+#ifdef CONFIG_IDEDMA_ONLYDISK
+                       if (drive->media == ide_disk)
+#endif
+                               hwif->ide_dma_check(drive);
+               } else {
+                       /* Non RAID volume. Fixups to stop the core code 
+                          doing unsupported things */
+                       id->field_valid &= 1;
+                       id->queue_depth = 0;
+                       id->command_set_1 = 0;
+                       id->command_set_2 &= 0xC400;
+                       id->cfsse &= 0xC000;
+                       id->cfs_enable_1 = 0;
+                       id->cfs_enable_2 &= 0xC400;
+                       id->csf_default &= 0xC000;
+                       id->word127 = 0;
+                       id->dlf = 0;
+                       id->csfo = 0;
+                       id->cfa_power = 0;
+                       printk(KERN_INFO "%s: Performing identify fixups.\n",
+                               drive->name);
+               }
+       }
+       
+}
+
+/**
+ *     init_hwif_it821x        -       set up hwif structs
+ *     @hwif: interface to set up
+ *
+ *     We do the basic set up of the interface structure. The IT8212
+ *     requires several custom handlers so we override the default
+ *     ide DMA handlers appropriately
+ */
+
+static void __devinit init_hwif_it821x(ide_hwif_t *hwif)
+{
+       struct it821x_dev *idev = kmalloc(sizeof(struct it821x_dev), GFP_KERNEL);
+       u8 conf;
+
+       if(idev == NULL) {
+               printk(KERN_ERR "it821x: out of memory, falling back to legacy behaviour.\n");
+               goto fallback;
+       }
+       memset(idev, 0, sizeof(struct it821x_dev));
+       ide_set_hwifdata(hwif, idev);
+
+       pci_read_config_byte(hwif->pci_dev, 0x50, &conf);
+       if(conf & 1) {
+               idev->smart = 1;
+               hwif->atapi_dma = 0;
+               /* Long I/O's although allowed in LBA48 space cause the
+                  onboard firmware to enter the twighlight zone */
+               hwif->rqsize = 256;
+       }
+
+       /* Pull the current clocks from 0x50 also */
+       if (conf & (1 << (1 + hwif->channel)))
+               idev->clock_mode = ATA_50;
+       else
+               idev->clock_mode = ATA_66;
+               
+       idev->want[0][1] = ATA_ANY;
+       idev->want[1][1] = ATA_ANY;
+               
+       /*
+        *      Not in the docs but according to the reference driver
+        *      this is neccessary. 
+        */
+
+       pci_read_config_byte(hwif->pci_dev, 0x08, &conf);
+       if(conf == 0x10) {
+               idev->timing10 = 1;
+               hwif->atapi_dma = 0;
+               if(!idev->smart)
+                       printk(KERN_WARNING "it821x: Revision 0x10, workarounds activated.\n");
+       }
+               
+       hwif->speedproc = &it821x_tune_chipset;
+       hwif->tuneproc  = &it821x_tuneproc;
+       
+       /* MWDMA/PIO clock switching for pass through mode */
+       if(!idev->smart) {
+               hwif->ide_dma_start = &it821x_dma_start;
+               hwif->ide_dma_end = &it821x_dma_end;
+       }
+
+       hwif->drives[0].autotune = 1;
+       hwif->drives[1].autotune = 1;
+
+       if (!hwif->dma_base)
+               goto fallback;
+
+       hwif->ultra_mask = 0x7f;
+       hwif->mwdma_mask = 0x07;
+       hwif->swdma_mask = 0x07;
+
+       hwif->ide_dma_check = &it821x_config_drive_for_dma;
+       if (!(hwif->udma_four))
+               hwif->udma_four = ata66_it821x(hwif);
+
+       /*
+        *      The BIOS often doesn't set up DMA on this controller
+        *      so we always do it.
+        */
+
+       hwif->autodma = 1;
+       hwif->drives[0].autodma = hwif->autodma;
+       hwif->drives[1].autodma = hwif->autodma;
+       return;
+fallback:
+       hwif->autodma = 0;
+       return;
+}
+
+static void __devinit it8212_disable_raid(struct pci_dev *dev)
+{
+       /* Reset local CPU, and set BIOS not ready */
+       pci_write_config_byte(dev, 0x5E, 0x01);
+
+       /* Set to bypass mode, and reset PCI bus */
+       pci_write_config_byte(dev, 0x50, 0x00);
+       pci_write_config_word(dev, PCI_COMMAND,
+                             PCI_COMMAND_PARITY | PCI_COMMAND_IO |
+                             PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+       pci_write_config_word(dev, 0x40, 0xA0F3);
+
+       pci_write_config_dword(dev,0x4C, 0x02040204);
+       pci_write_config_byte(dev, 0x42, 0x36);
+       pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0);
+}
+
+static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev, const char *name)
+{
+       u8 conf;
+       static char *mode[2] = { "pass through", "smart" };
+
+       /* Force the card into bypass mode if so requested */
+       if (it8212_noraid) {
+               printk(KERN_INFO "it8212: forcing bypass mode.\n");
+               it8212_disable_raid(dev);
+       }
+       pci_read_config_byte(dev, 0x50, &conf);
+       printk(KERN_INFO "it821x: controller in %s mode.\n", mode[conf & 1]);
+       return 0;
+}
+
+
+#define DECLARE_ITE_DEV(name_str)                      \
+       {                                               \
+               .name           = name_str,             \
+               .init_chipset   = init_chipset_it821x,  \
+               .init_hwif      = init_hwif_it821x,     \
+               .channels       = 2,                    \
+               .autodma        = AUTODMA,              \
+               .bootable       = ON_BOARD,             \
+               .fixup          = it821x_fixups         \
+       }
+
+static ide_pci_device_t it821x_chipsets[] __devinitdata = {
+       /* 0 */ DECLARE_ITE_DEV("IT8212"),
+};
+
+/**
+ *     it821x_init_one -       pci layer discovery entry
+ *     @dev: PCI device
+ *     @id: ident table entry
+ *
+ *     Called by the PCI code when it finds an ITE821x controller.
+ *     We then use the IDE PCI generic helper to do most of the work.
+ */
+
+static int __devinit it821x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       ide_setup_pci_device(dev, &it821x_chipsets[id->driver_data]);
+       return 0;
+}
+
+static struct pci_device_id it821x_pci_tbl[] = {
+       { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8211,  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8212,  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, it821x_pci_tbl);
+
+static struct pci_driver driver = {
+       .name           = "ITE821x IDE",
+       .id_table       = it821x_pci_tbl,
+       .probe          = it821x_init_one,
+};
+
+static int __init it821x_ide_init(void)
+{
+       return ide_pci_register_driver(&driver);
+}
+
+module_init(it821x_ide_init);
+
+module_param_named(noraid, it8212_noraid, int, S_IRUGO);
+MODULE_PARM_DESC(it8212_noraid, "Force card into bypass mode");
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("PCI driver module for the ITE 821x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
new file mode 100644 (file)
index 0000000..3cc3ff0
--- /dev/null
@@ -0,0 +1,14 @@
+menu "InfiniBand support"
+
+config INFINIBAND
+       tristate "InfiniBand support"
+       ---help---
+         Core support for InfiniBand (IB).  Make sure to also select
+         any protocols you wish to use as well as drivers for your
+         InfiniBand hardware.
+
+source "drivers/infiniband/hw/mthca/Kconfig"
+
+source "drivers/infiniband/ulp/ipoib/Kconfig"
+
+endmenu
diff --git a/drivers/infiniband/Makefile b/drivers/infiniband/Makefile
new file mode 100644 (file)
index 0000000..d256cf7
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_INFINIBAND)               += core/
+obj-$(CONFIG_INFINIBAND_MTHCA)         += hw/mthca/
+obj-$(CONFIG_INFINIBAND_IPOIB)         += ulp/ipoib/
diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile
new file mode 100644 (file)
index 0000000..d2dbfb5
--- /dev/null
@@ -0,0 +1,12 @@
+EXTRA_CFLAGS += -Idrivers/infiniband/include
+
+obj-$(CONFIG_INFINIBAND) +=    ib_core.o ib_mad.o ib_sa.o ib_umad.o
+
+ib_core-y :=                   packer.o ud_header.o verbs.o sysfs.o \
+                               device.o fmr_pool.o cache.o
+
+ib_mad-y :=                    mad.o smi.o agent.o
+
+ib_sa-y :=                     sa_query.o
+
+ib_umad-y :=                   user_mad.o
diff --git a/drivers/infiniband/core/agent.c b/drivers/infiniband/core/agent.c
new file mode 100644 (file)
index 0000000..49f4f9f
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
+ * Copyright (c) 2004 Infinicon Corporation.  All rights reserved.
+ * Copyright (c) 2004 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ * Copyright (c) 2004 Voltaire Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: agent.c 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#include <linux/dma-mapping.h>
+
+#include <asm/bug.h>
+
+#include <ib_smi.h>
+
+#include "smi.h"
+#include "agent_priv.h"
+#include "mad_priv.h"
+
+
+spinlock_t ib_agent_port_list_lock;
+static LIST_HEAD(ib_agent_port_list);
+
+extern kmem_cache_t *ib_mad_cache;
+
+
+/*
+ * Caller must hold ib_agent_port_list_lock
+ */
+static inline struct ib_agent_port_private *
+__ib_get_agent_port(struct ib_device *device, int port_num,
+                   struct ib_mad_agent *mad_agent)
+{
+       struct ib_agent_port_private *entry;
+
+       BUG_ON(!(!!device ^ !!mad_agent));  /* Exactly one MUST be (!NULL) */
+
+       if (device) {
+               list_for_each_entry(entry, &ib_agent_port_list, port_list) {
+                       if (entry->dr_smp_agent->device == device &&
+                           entry->port_num == port_num)
+                               return entry;
+               }
+       } else {
+               list_for_each_entry(entry, &ib_agent_port_list, port_list) {
+                       if ((entry->dr_smp_agent == mad_agent) ||
+                           (entry->lr_smp_agent == mad_agent) ||
+                           (entry->perf_mgmt_agent == mad_agent))
+                               return entry;
+               }
+       }
+       return NULL;
+}
+
+static inline struct ib_agent_port_private *
+ib_get_agent_port(struct ib_device *device, int port_num,
+                 struct ib_mad_agent *mad_agent)
+{
+       struct ib_agent_port_private *entry;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ib_agent_port_list_lock, flags);
+       entry = __ib_get_agent_port(device, port_num, mad_agent);
+       spin_unlock_irqrestore(&ib_agent_port_list_lock, flags);
+
+       return entry;
+}
+
+int smi_check_local_dr_smp(struct ib_smp *smp,
+                          struct ib_device *device,
+                          int port_num)
+{
+       struct ib_agent_port_private *port_priv;
+
+       if (smp->mgmt_class != IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+               return 1;
+       port_priv = ib_get_agent_port(device, port_num, NULL);
+       if (!port_priv) {
+               printk(KERN_DEBUG SPFX "smi_check_local_dr_smp %s port %d "
+                      "not open\n",
+                      device->name, port_num);
+               return 1;
+       }
+
+       return smi_check_local_smp(port_priv->dr_smp_agent, smp);
+}
+
+static int agent_mad_send(struct ib_mad_agent *mad_agent,
+                         struct ib_agent_port_private *port_priv,
+                         struct ib_mad_private *mad_priv,
+                         struct ib_grh *grh,
+                         struct ib_wc *wc)
+{
+       struct ib_agent_send_wr *agent_send_wr;
+       struct ib_sge gather_list;
+       struct ib_send_wr send_wr;
+       struct ib_send_wr *bad_send_wr;
+       struct ib_ah_attr ah_attr;
+       unsigned long flags;
+       int ret = 1;
+
+       agent_send_wr = kmalloc(sizeof(*agent_send_wr), GFP_KERNEL);
+       if (!agent_send_wr)
+               goto out;
+       agent_send_wr->mad = mad_priv;
+
+       /* PCI mapping */
+       gather_list.addr = dma_map_single(mad_agent->device->dma_device,
+                                         &mad_priv->mad,
+                                         sizeof(mad_priv->mad),
+                                         DMA_TO_DEVICE);
+       gather_list.length = sizeof(mad_priv->mad);
+       gather_list.lkey = (*port_priv->mr).lkey;
+
+       send_wr.next = NULL;
+       send_wr.opcode = IB_WR_SEND;
+       send_wr.sg_list = &gather_list;
+       send_wr.num_sge = 1;
+       send_wr.wr.ud.remote_qpn = wc->src_qp; /* DQPN */
+       send_wr.wr.ud.timeout_ms = 0;
+       send_wr.send_flags = IB_SEND_SIGNALED | IB_SEND_SOLICITED;
+
+       ah_attr.dlid = wc->slid;
+       ah_attr.port_num = mad_agent->port_num;
+       ah_attr.src_path_bits = wc->dlid_path_bits;
+       ah_attr.sl = wc->sl;
+       ah_attr.static_rate = 0;
+       ah_attr.ah_flags = 0; /* No GRH */
+       if (mad_priv->mad.mad.mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT) {
+               if (wc->wc_flags & IB_WC_GRH) {
+                       ah_attr.ah_flags = IB_AH_GRH;
+                       /* Should sgid be looked up ? */
+                       ah_attr.grh.sgid_index = 0;
+                       ah_attr.grh.hop_limit = grh->hop_limit;
+                       ah_attr.grh.flow_label = be32_to_cpup(
+                               &grh->version_tclass_flow)  & 0xfffff;
+                       ah_attr.grh.traffic_class = (be32_to_cpup(
+                               &grh->version_tclass_flow) >> 20) & 0xff;
+                       memcpy(ah_attr.grh.dgid.raw,
+                              grh->sgid.raw,
+                              sizeof(ah_attr.grh.dgid));
+               }
+       }
+
+       agent_send_wr->ah = ib_create_ah(mad_agent->qp->pd, &ah_attr);
+       if (IS_ERR(agent_send_wr->ah)) {
+               printk(KERN_ERR SPFX "No memory for address handle\n");
+               kfree(agent_send_wr);
+               goto out;
+       }
+
+       send_wr.wr.ud.ah = agent_send_wr->ah;
+       if (mad_priv->mad.mad.mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT) {
+               send_wr.wr.ud.pkey_index = wc->pkey_index;
+               send_wr.wr.ud.remote_qkey = IB_QP1_QKEY;
+       } else {        /* for SMPs */
+               send_wr.wr.ud.pkey_index = 0;
+               send_wr.wr.ud.remote_qkey = 0;
+       }
+       send_wr.wr.ud.mad_hdr = &mad_priv->mad.mad.mad_hdr;
+       send_wr.wr_id = (unsigned long)agent_send_wr;
+
+       pci_unmap_addr_set(agent_send_wr, mapping, gather_list.addr);
+
+       /* Send */
+       spin_lock_irqsave(&port_priv->send_list_lock, flags);
+       if (ib_post_send_mad(mad_agent, &send_wr, &bad_send_wr)) {
+               spin_unlock_irqrestore(&port_priv->send_list_lock, flags);
+               dma_unmap_single(mad_agent->device->dma_device,
+                                pci_unmap_addr(agent_send_wr, mapping),
+                                sizeof(mad_priv->mad),
+                                DMA_TO_DEVICE);
+               ib_destroy_ah(agent_send_wr->ah);
+               kfree(agent_send_wr);
+       } else {
+               list_add_tail(&agent_send_wr->send_list,
+                             &port_priv->send_posted_list);
+               spin_unlock_irqrestore(&port_priv->send_list_lock, flags);
+               ret = 0;
+       }
+
+out:
+       return ret;
+}
+
+int agent_send(struct ib_mad_private *mad,
+              struct ib_grh *grh,
+              struct ib_wc *wc,
+              struct ib_device *device,
+              int port_num)
+{
+       struct ib_agent_port_private *port_priv;
+       struct ib_mad_agent *mad_agent;
+
+       port_priv = ib_get_agent_port(device, port_num, NULL);
+       if (!port_priv) {
+               printk(KERN_DEBUG SPFX "agent_send %s port %d not open\n",
+                      device->name, port_num);
+               return 1;
+       }
+
+       /* Get mad agent based on mgmt_class in MAD */
+       switch (mad->mad.mad.mad_hdr.mgmt_class) {
+               case IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE:
+                       mad_agent = port_priv->dr_smp_agent;
+                       break;
+               case IB_MGMT_CLASS_SUBN_LID_ROUTED:
+                       mad_agent = port_priv->lr_smp_agent;
+                       break;
+               case IB_MGMT_CLASS_PERF_MGMT:
+                       mad_agent = port_priv->perf_mgmt_agent;
+                       break;
+               default:
+                       return 1;
+       }
+
+       return agent_mad_send(mad_agent, port_priv, mad, grh, wc);
+}
+
+static void agent_send_handler(struct ib_mad_agent *mad_agent,
+                              struct ib_mad_send_wc *mad_send_wc)
+{
+       struct ib_agent_port_private    *port_priv;
+       struct ib_agent_send_wr         *agent_send_wr;
+       unsigned long                   flags;
+
+       /* Find matching MAD agent */
+       port_priv = ib_get_agent_port(NULL, 0, mad_agent);
+       if (!port_priv) {
+               printk(KERN_ERR SPFX "agent_send_handler: no matching MAD "
+                      "agent %p\n", mad_agent);
+               return;
+       }
+
+       agent_send_wr = (struct ib_agent_send_wr *)(unsigned long)mad_send_wc->wr_id;
+       spin_lock_irqsave(&port_priv->send_list_lock, flags);
+       /* Remove completed send from posted send MAD list */
+       list_del(&agent_send_wr->send_list);
+       spin_unlock_irqrestore(&port_priv->send_list_lock, flags);
+
+       /* Unmap PCI */
+       dma_unmap_single(mad_agent->device->dma_device,
+                        pci_unmap_addr(agent_send_wr, mapping),
+                        sizeof(agent_send_wr->mad->mad),
+                        DMA_TO_DEVICE);
+
+       ib_destroy_ah(agent_send_wr->ah);
+
+       /* Release allocated memory */
+       kmem_cache_free(ib_mad_cache, agent_send_wr->mad);
+       kfree(agent_send_wr);
+}
+
+int ib_agent_port_open(struct ib_device *device, int port_num)
+{
+       int ret;
+       struct ib_agent_port_private *port_priv;
+       struct ib_mad_reg_req reg_req;
+       unsigned long flags;
+
+       /* First, check if port already open for SMI */
+       port_priv = ib_get_agent_port(device, port_num, NULL);
+       if (port_priv) {
+               printk(KERN_DEBUG SPFX "%s port %d already open\n",
+                      device->name, port_num);
+               return 0;
+       }
+
+       /* Create new device info */
+       port_priv = kmalloc(sizeof *port_priv, GFP_KERNEL);
+       if (!port_priv) {
+               printk(KERN_ERR SPFX "No memory for ib_agent_port_private\n");
+               ret = -ENOMEM;
+               goto error1;
+       }
+
+       memset(port_priv, 0, sizeof *port_priv);
+       port_priv->port_num = port_num;
+       spin_lock_init(&port_priv->send_list_lock);
+       INIT_LIST_HEAD(&port_priv->send_posted_list);
+
+       /* Obtain MAD agent for directed route SM class */
+       reg_req.mgmt_class = IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE;
+       reg_req.mgmt_class_version = 1;
+
+       port_priv->dr_smp_agent = ib_register_mad_agent(device, port_num,
+                                                       IB_QPT_SMI,
+                                                       NULL, 0,
+                                                      &agent_send_handler,
+                                                       NULL, NULL);
+
+       if (IS_ERR(port_priv->dr_smp_agent)) {
+               ret = PTR_ERR(port_priv->dr_smp_agent);
+               goto error2;
+       }
+
+       /* Obtain MAD agent for LID routed SM class */
+       reg_req.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+       port_priv->lr_smp_agent = ib_register_mad_agent(device, port_num,
+                                                       IB_QPT_SMI,
+                                                       NULL, 0,
+                                                      &agent_send_handler,
+                                                       NULL, NULL);
+       if (IS_ERR(port_priv->lr_smp_agent)) {
+               ret = PTR_ERR(port_priv->lr_smp_agent);
+               goto error3;
+       }
+
+       /* Obtain MAD agent for PerfMgmt class */
+       reg_req.mgmt_class = IB_MGMT_CLASS_PERF_MGMT;
+       port_priv->perf_mgmt_agent = ib_register_mad_agent(device, port_num,
+                                                          IB_QPT_GSI,
+                                                          NULL, 0,
+                                                         &agent_send_handler,
+                                                          NULL, NULL);
+       if (IS_ERR(port_priv->perf_mgmt_agent)) {
+               ret = PTR_ERR(port_priv->perf_mgmt_agent);
+               goto error4;
+       }
+
+       port_priv->mr = ib_get_dma_mr(port_priv->dr_smp_agent->qp->pd,
+                                     IB_ACCESS_LOCAL_WRITE);
+       if (IS_ERR(port_priv->mr)) {
+               printk(KERN_ERR SPFX "Couldn't get DMA MR\n");
+               ret = PTR_ERR(port_priv->mr);
+               goto error5;
+       }
+
+       spin_lock_irqsave(&ib_agent_port_list_lock, flags);
+       list_add_tail(&port_priv->port_list, &ib_agent_port_list);
+       spin_unlock_irqrestore(&ib_agent_port_list_lock, flags);
+
+       return 0;
+
+error5:
+       ib_unregister_mad_agent(port_priv->perf_mgmt_agent);
+error4:
+       ib_unregister_mad_agent(port_priv->lr_smp_agent);
+error3:
+       ib_unregister_mad_agent(port_priv->dr_smp_agent);
+error2:
+       kfree(port_priv);
+error1:
+       return ret;
+}
+
+int ib_agent_port_close(struct ib_device *device, int port_num)
+{
+       struct ib_agent_port_private *port_priv;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ib_agent_port_list_lock, flags);
+       port_priv = __ib_get_agent_port(device, port_num, NULL);
+       if (port_priv == NULL) {
+               spin_unlock_irqrestore(&ib_agent_port_list_lock, flags);
+               printk(KERN_ERR SPFX "Port %d not found\n", port_num);
+               return -ENODEV;
+       }
+       list_del(&port_priv->port_list);
+       spin_unlock_irqrestore(&ib_agent_port_list_lock, flags);
+
+       ib_dereg_mr(port_priv->mr);
+
+       ib_unregister_mad_agent(port_priv->perf_mgmt_agent);
+       ib_unregister_mad_agent(port_priv->lr_smp_agent);
+       ib_unregister_mad_agent(port_priv->dr_smp_agent);
+       kfree(port_priv);
+
+       return 0;
+}
diff --git a/drivers/infiniband/core/agent.h b/drivers/infiniband/core/agent.h
new file mode 100644 (file)
index 0000000..d942684
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
+ * Copyright (c) 2004 Infinicon Corporation.  All rights reserved.
+ * Copyright (c) 2004 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ * Copyright (c) 2004 Voltaire Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: agent.h 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#ifndef __AGENT_H_
+#define __AGENT_H_
+
+extern spinlock_t ib_agent_port_list_lock;
+
+extern int ib_agent_port_open(struct ib_device *device,
+                             int port_num);
+
+extern int ib_agent_port_close(struct ib_device *device, int port_num);
+
+extern int agent_send(struct ib_mad_private *mad,
+                     struct ib_grh *grh,
+                     struct ib_wc *wc,
+                     struct ib_device *device,
+                     int port_num);
+
+#endif /* __AGENT_H_ */
diff --git a/drivers/infiniband/core/agent_priv.h b/drivers/infiniband/core/agent_priv.h
new file mode 100644 (file)
index 0000000..3b63338
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
+ * Copyright (c) 2004 Infinicon Corporation.  All rights reserved.
+ * Copyright (c) 2004 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ * Copyright (c) 2004 Voltaire Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: agent_priv.h 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#ifndef __IB_AGENT_PRIV_H__
+#define __IB_AGENT_PRIV_H__
+
+#include <linux/pci.h>
+
+#define SPFX "ib_agent: "
+
+struct ib_agent_send_wr {
+       struct list_head send_list;
+       struct ib_ah *ah;
+       struct ib_mad_private *mad;
+       DECLARE_PCI_UNMAP_ADDR(mapping)
+};
+
+struct ib_agent_port_private {
+       struct list_head port_list;
+       struct list_head send_posted_list;
+       spinlock_t send_list_lock;
+       int port_num;
+       struct ib_mad_agent *dr_smp_agent;    /* DR SM class */
+       struct ib_mad_agent *lr_smp_agent;    /* LR SM class */
+       struct ib_mad_agent *perf_mgmt_agent; /* PerfMgmt class */
+       struct ib_mr *mr;
+};
+
+#endif /* __IB_AGENT_PRIV_H__ */
diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c
new file mode 100644 (file)
index 0000000..f70fbfa
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: cache.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include "core_priv.h"
+
+struct ib_pkey_cache {
+       int             table_len;
+       u16             table[0];
+};
+
+struct ib_gid_cache {
+       int             table_len;
+       union ib_gid    table[0];
+};
+
+struct ib_update_work {
+       struct work_struct work;
+       struct ib_device  *device;
+       u8                 port_num;
+};
+
+static inline int start_port(struct ib_device *device)
+{
+       return device->node_type == IB_NODE_SWITCH ? 0 : 1;
+}
+
+static inline int end_port(struct ib_device *device)
+{
+       return device->node_type == IB_NODE_SWITCH ? 0 : device->phys_port_cnt;
+}
+
+int ib_get_cached_gid(struct ib_device *device,
+                     u8                port_num,
+                     int               index,
+                     union ib_gid     *gid)
+{
+       struct ib_gid_cache *cache;
+       unsigned long flags;
+       int ret = 0;
+
+       if (port_num < start_port(device) || port_num > end_port(device))
+               return -EINVAL;
+
+       read_lock_irqsave(&device->cache.lock, flags);
+
+       cache = device->cache.gid_cache[port_num - start_port(device)];
+
+       if (index < 0 || index >= cache->table_len)
+               ret = -EINVAL;
+       else
+               *gid = cache->table[index];
+
+       read_unlock_irqrestore(&device->cache.lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_get_cached_gid);
+
+int ib_find_cached_gid(struct ib_device *device,
+                      union ib_gid     *gid,
+                      u8               *port_num,
+                      u16              *index)
+{
+       struct ib_gid_cache *cache;
+       unsigned long flags;
+       int p, i;
+       int ret = -ENOENT;
+
+       *port_num = -1;
+       if (index)
+               *index = -1;
+
+       read_lock_irqsave(&device->cache.lock, flags);
+
+       for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+               cache = device->cache.gid_cache[p];
+               for (i = 0; i < cache->table_len; ++i) {
+                       if (!memcmp(gid, &cache->table[i], sizeof *gid)) {
+                               *port_num = p;
+                               if (index)
+                                       *index = i;
+                               ret = 0;
+                               goto found;
+                       }
+               }
+       }
+found:
+       read_unlock_irqrestore(&device->cache.lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_find_cached_gid);
+
+int ib_get_cached_pkey(struct ib_device *device,
+                      u8                port_num,
+                      int               index,
+                      u16              *pkey)
+{
+       struct ib_pkey_cache *cache;
+       unsigned long flags;
+       int ret = 0;
+
+       if (port_num < start_port(device) || port_num > end_port(device))
+               return -EINVAL;
+
+       read_lock_irqsave(&device->cache.lock, flags);
+
+       cache = device->cache.pkey_cache[port_num - start_port(device)];
+
+       if (index < 0 || index >= cache->table_len)
+               ret = -EINVAL;
+       else
+               *pkey = cache->table[index];
+
+       read_unlock_irqrestore(&device->cache.lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_get_cached_pkey);
+
+int ib_find_cached_pkey(struct ib_device *device,
+                       u8                port_num,
+                       u16               pkey,
+                       u16              *index)
+{
+       struct ib_pkey_cache *cache;
+       unsigned long flags;
+       int i;
+       int ret = -ENOENT;
+
+       if (port_num < start_port(device) || port_num > end_port(device))
+               return -EINVAL;
+
+       read_lock_irqsave(&device->cache.lock, flags);
+
+       cache = device->cache.pkey_cache[port_num - start_port(device)];
+
+       *index = -1;
+
+       for (i = 0; i < cache->table_len; ++i)
+               if ((cache->table[i] & 0x7fff) == (pkey & 0x7fff)) {
+                       *index = i;
+                       ret = 0;
+                       break;
+               }
+
+       read_unlock_irqrestore(&device->cache.lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_find_cached_pkey);
+
+static void ib_cache_update(struct ib_device *device,
+                           u8                port)
+{
+       struct ib_port_attr       *tprops = NULL;
+       struct ib_pkey_cache      *pkey_cache = NULL, *old_pkey_cache;
+       struct ib_gid_cache       *gid_cache = NULL, *old_gid_cache;
+       int                        i;
+       int                        ret;
+
+       tprops = kmalloc(sizeof *tprops, GFP_KERNEL);
+       if (!tprops)
+               return;
+
+       ret = ib_query_port(device, port, tprops);
+       if (ret) {
+               printk(KERN_WARNING "ib_query_port failed (%d) for %s\n",
+                      ret, device->name);
+               goto err;
+       }
+
+       pkey_cache = kmalloc(sizeof *pkey_cache + tprops->pkey_tbl_len *
+                            sizeof *pkey_cache->table, GFP_KERNEL);
+       if (!pkey_cache)
+               goto err;
+
+       pkey_cache->table_len = tprops->pkey_tbl_len;
+
+       gid_cache = kmalloc(sizeof *gid_cache + tprops->gid_tbl_len *
+                           sizeof *gid_cache->table, GFP_KERNEL);
+       if (!gid_cache)
+               goto err;
+
+       gid_cache->table_len = tprops->gid_tbl_len;
+
+       for (i = 0; i < pkey_cache->table_len; ++i) {
+               ret = ib_query_pkey(device, port, i, pkey_cache->table + i);
+               if (ret) {
+                       printk(KERN_WARNING "ib_query_pkey failed (%d) for %s (index %d)\n",
+                              ret, device->name, i);
+                       goto err;
+               }
+       }
+
+       for (i = 0; i < gid_cache->table_len; ++i) {
+               ret = ib_query_gid(device, port, i, gid_cache->table + i);
+               if (ret) {
+                       printk(KERN_WARNING "ib_query_gid failed (%d) for %s (index %d)\n",
+                              ret, device->name, i);
+                       goto err;
+               }
+       }
+
+       write_lock_irq(&device->cache.lock);
+
+       old_pkey_cache = device->cache.pkey_cache[port - start_port(device)];
+       old_gid_cache  = device->cache.gid_cache [port - start_port(device)];
+
+       device->cache.pkey_cache[port - start_port(device)] = pkey_cache;
+       device->cache.gid_cache [port - start_port(device)] = gid_cache;
+
+       write_unlock_irq(&device->cache.lock);
+
+       kfree(old_pkey_cache);
+       kfree(old_gid_cache);
+       kfree(tprops);
+       return;
+
+err:
+       kfree(pkey_cache);
+       kfree(gid_cache);
+       kfree(tprops);
+}
+
+static void ib_cache_task(void *work_ptr)
+{
+       struct ib_update_work *work = work_ptr;
+
+       ib_cache_update(work->device, work->port_num);
+       kfree(work);
+}
+
+static void ib_cache_event(struct ib_event_handler *handler,
+                          struct ib_event *event)
+{
+       struct ib_update_work *work;
+
+       if (event->event == IB_EVENT_PORT_ERR    ||
+           event->event == IB_EVENT_PORT_ACTIVE ||
+           event->event == IB_EVENT_LID_CHANGE  ||
+           event->event == IB_EVENT_PKEY_CHANGE ||
+           event->event == IB_EVENT_SM_CHANGE) {
+               work = kmalloc(sizeof *work, GFP_ATOMIC);
+               if (work) {
+                       INIT_WORK(&work->work, ib_cache_task, work);
+                       work->device   = event->device;
+                       work->port_num = event->element.port_num;
+                       schedule_work(&work->work);
+               }
+       }
+}
+
+static void ib_cache_setup_one(struct ib_device *device)
+{
+       int p;
+
+       rwlock_init(&device->cache.lock);
+
+       device->cache.pkey_cache =
+               kmalloc(sizeof *device->cache.pkey_cache *
+                       (end_port(device) - start_port(device) + 1), GFP_KERNEL);
+       device->cache.gid_cache =
+               kmalloc(sizeof *device->cache.pkey_cache *
+                       (end_port(device) - start_port(device) + 1), GFP_KERNEL);
+
+       if (!device->cache.pkey_cache || !device->cache.gid_cache) {
+               printk(KERN_WARNING "Couldn't allocate cache "
+                      "for %s\n", device->name);
+               goto err;
+       }
+
+       for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+               device->cache.pkey_cache[p] = NULL;
+               device->cache.gid_cache [p] = NULL;
+               ib_cache_update(device, p + start_port(device));
+       }
+
+       INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
+                             device, ib_cache_event);
+       if (ib_register_event_handler(&device->cache.event_handler))
+               goto err_cache;
+
+       return;
+
+err_cache:
+       for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+               kfree(device->cache.pkey_cache[p]);
+               kfree(device->cache.gid_cache[p]);
+       }
+
+err:
+       kfree(device->cache.pkey_cache);
+       kfree(device->cache.gid_cache);
+}
+
+static void ib_cache_cleanup_one(struct ib_device *device)
+{
+       int p;
+
+       ib_unregister_event_handler(&device->cache.event_handler);
+       flush_scheduled_work();
+
+       for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+               kfree(device->cache.pkey_cache[p]);
+               kfree(device->cache.gid_cache[p]);
+       }
+
+       kfree(device->cache.pkey_cache);
+       kfree(device->cache.gid_cache);
+}
+
+static struct ib_client cache_client = {
+       .name   = "cache",
+       .add    = ib_cache_setup_one,
+       .remove = ib_cache_cleanup_one
+};
+
+int __init ib_cache_setup(void)
+{
+       return ib_register_client(&cache_client);
+}
+
+void __exit ib_cache_cleanup(void)
+{
+       ib_unregister_client(&cache_client);
+}
diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h
new file mode 100644 (file)
index 0000000..7970496
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: core_priv.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#ifndef _CORE_PRIV_H
+#define _CORE_PRIV_H
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+#include <ib_verbs.h>
+
+int  ib_device_register_sysfs(struct ib_device *device);
+void ib_device_unregister_sysfs(struct ib_device *device);
+
+int  ib_sysfs_setup(void);
+void ib_sysfs_cleanup(void);
+
+int  ib_cache_setup(void);
+void ib_cache_cleanup(void);
+
+#endif /* _CORE_PRIV_H */
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
new file mode 100644 (file)
index 0000000..9197e92
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: device.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <asm/semaphore.h>
+
+#include "core_priv.h"
+
+MODULE_AUTHOR("Roland Dreier");
+MODULE_DESCRIPTION("core kernel InfiniBand API");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct ib_client_data {
+       struct list_head  list;
+       struct ib_client *client;
+       void *            data;
+};
+
+static LIST_HEAD(device_list);
+static LIST_HEAD(client_list);
+
+/*
+ * device_sem protects access to both device_list and client_list.
+ * There's no real point to using multiple locks or something fancier
+ * like an rwsem: we always access both lists, and we're always
+ * modifying one list or the other list.  In any case this is not a
+ * hot path so there's no point in trying to optimize.
+ */
+static DECLARE_MUTEX(device_sem);
+
+static int ib_device_check_mandatory(struct ib_device *device)
+{
+#define IB_MANDATORY_FUNC(x) { offsetof(struct ib_device, x), #x }
+       static const struct {
+               size_t offset;
+               char  *name;
+       } mandatory_table[] = {
+               IB_MANDATORY_FUNC(query_device),
+               IB_MANDATORY_FUNC(query_port),
+               IB_MANDATORY_FUNC(query_pkey),
+               IB_MANDATORY_FUNC(query_gid),
+               IB_MANDATORY_FUNC(alloc_pd),
+               IB_MANDATORY_FUNC(dealloc_pd),
+               IB_MANDATORY_FUNC(create_ah),
+               IB_MANDATORY_FUNC(destroy_ah),
+               IB_MANDATORY_FUNC(create_qp),
+               IB_MANDATORY_FUNC(modify_qp),
+               IB_MANDATORY_FUNC(destroy_qp),
+               IB_MANDATORY_FUNC(post_send),
+               IB_MANDATORY_FUNC(post_recv),
+               IB_MANDATORY_FUNC(create_cq),
+               IB_MANDATORY_FUNC(destroy_cq),
+               IB_MANDATORY_FUNC(poll_cq),
+               IB_MANDATORY_FUNC(req_notify_cq),
+               IB_MANDATORY_FUNC(get_dma_mr),
+               IB_MANDATORY_FUNC(dereg_mr)
+       };
+       int i;
+
+       for (i = 0; i < sizeof mandatory_table / sizeof mandatory_table[0]; ++i) {
+               if (!*(void **) ((void *) device + mandatory_table[i].offset)) {
+                       printk(KERN_WARNING "Device %s is missing mandatory function %s\n",
+                              device->name, mandatory_table[i].name);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static struct ib_device *__ib_device_get_by_name(const char *name)
+{
+       struct ib_device *device;
+
+       list_for_each_entry(device, &device_list, core_list)
+               if (!strncmp(name, device->name, IB_DEVICE_NAME_MAX))
+                       return device;
+
+       return NULL;
+}
+
+
+static int alloc_name(char *name)
+{
+       long *inuse;
+       char buf[IB_DEVICE_NAME_MAX];
+       struct ib_device *device;
+       int i;
+
+       inuse = (long *) get_zeroed_page(GFP_KERNEL);
+       if (!inuse)
+               return -ENOMEM;
+
+       list_for_each_entry(device, &device_list, core_list) {
+               if (!sscanf(device->name, name, &i))
+                       continue;
+               if (i < 0 || i >= PAGE_SIZE * 8)
+                       continue;
+               snprintf(buf, sizeof buf, name, i);
+               if (!strncmp(buf, device->name, IB_DEVICE_NAME_MAX))
+                       set_bit(i, inuse);
+       }
+
+       i = find_first_zero_bit(inuse, PAGE_SIZE * 8);
+       free_page((unsigned long) inuse);
+       snprintf(buf, sizeof buf, name, i);
+
+       if (__ib_device_get_by_name(buf))
+               return -ENFILE;
+
+       strlcpy(name, buf, IB_DEVICE_NAME_MAX);
+       return 0;
+}
+
+/**
+ * ib_alloc_device - allocate an IB device struct
+ * @size:size of structure to allocate
+ *
+ * Low-level drivers should use ib_alloc_device() to allocate &struct
+ * ib_device.  @size is the size of the structure to be allocated,
+ * including any private data used by the low-level driver.
+ * ib_dealloc_device() must be used to free structures allocated with
+ * ib_alloc_device().
+ */
+struct ib_device *ib_alloc_device(size_t size)
+{
+       void *dev;
+
+       BUG_ON(size < sizeof (struct ib_device));
+
+       dev = kmalloc(size, GFP_KERNEL);
+       if (!dev)
+               return NULL;
+
+       memset(dev, 0, size);
+
+       return dev;
+}
+EXPORT_SYMBOL(ib_alloc_device);
+
+/**
+ * ib_dealloc_device - free an IB device struct
+ * @device:structure to free
+ *
+ * Free a structure allocated with ib_alloc_device().
+ */
+void ib_dealloc_device(struct ib_device *device)
+{
+       if (device->reg_state == IB_DEV_UNINITIALIZED) {
+               kfree(device);
+               return;
+       }
+
+       BUG_ON(device->reg_state != IB_DEV_UNREGISTERED);
+
+       ib_device_unregister_sysfs(device);
+}
+EXPORT_SYMBOL(ib_dealloc_device);
+
+static int add_client_context(struct ib_device *device, struct ib_client *client)
+{
+       struct ib_client_data *context;
+       unsigned long flags;
+
+       context = kmalloc(sizeof *context, GFP_KERNEL);
+       if (!context) {
+               printk(KERN_WARNING "Couldn't allocate client context for %s/%s\n",
+                      device->name, client->name);
+               return -ENOMEM;
+       }
+
+       context->client = client;
+       context->data   = NULL;
+
+       spin_lock_irqsave(&device->client_data_lock, flags);
+       list_add(&context->list, &device->client_data_list);
+       spin_unlock_irqrestore(&device->client_data_lock, flags);
+
+       return 0;
+}
+
+/**
+ * ib_register_device - Register an IB device with IB core
+ * @device:Device to register
+ *
+ * Low-level drivers use ib_register_device() to register their
+ * devices with the IB core.  All registered clients will receive a
+ * callback for each device that is added. @device must be allocated
+ * with ib_alloc_device().
+ */
+int ib_register_device(struct ib_device *device)
+{
+       int ret;
+
+       down(&device_sem);
+
+       if (strchr(device->name, '%')) {
+               ret = alloc_name(device->name);
+               if (ret)
+                       goto out;
+       }
+
+       if (ib_device_check_mandatory(device)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       INIT_LIST_HEAD(&device->event_handler_list);
+       INIT_LIST_HEAD(&device->client_data_list);
+       spin_lock_init(&device->event_handler_lock);
+       spin_lock_init(&device->client_data_lock);
+
+       ret = ib_device_register_sysfs(device);
+       if (ret) {
+               printk(KERN_WARNING "Couldn't register device %s with driver model\n",
+                      device->name);
+               goto out;
+       }
+
+       list_add_tail(&device->core_list, &device_list);
+
+       device->reg_state = IB_DEV_REGISTERED;
+
+       {
+               struct ib_client *client;
+
+               list_for_each_entry(client, &client_list, list)
+                       if (client->add && !add_client_context(device, client))
+                               client->add(device);
+       }
+
+ out:
+       up(&device_sem);
+       return ret;
+}
+EXPORT_SYMBOL(ib_register_device);
+
+/**
+ * ib_unregister_device - Unregister an IB device
+ * @device:Device to unregister
+ *
+ * Unregister an IB device.  All clients will receive a remove callback.
+ */
+void ib_unregister_device(struct ib_device *device)
+{
+       struct ib_client *client;
+       struct ib_client_data *context, *tmp;
+       unsigned long flags;
+
+       down(&device_sem);
+
+       list_for_each_entry_reverse(client, &client_list, list)
+               if (client->remove)
+                       client->remove(device);
+
+       list_del(&device->core_list);
+
+       up(&device_sem);
+
+       spin_lock_irqsave(&device->client_data_lock, flags);
+       list_for_each_entry_safe(context, tmp, &device->client_data_list, list)
+               kfree(context);
+       spin_unlock_irqrestore(&device->client_data_lock, flags);
+
+       device->reg_state = IB_DEV_UNREGISTERED;
+}
+EXPORT_SYMBOL(ib_unregister_device);
+
+/**
+ * ib_register_client - Register an IB client
+ * @client:Client to register
+ *
+ * Upper level users of the IB drivers can use ib_register_client() to
+ * register callbacks for IB device addition and removal.  When an IB
+ * device is added, each registered client's add method will be called
+ * (in the order the clients were registered), and when a device is
+ * removed, each client's remove method will be called (in the reverse
+ * order that clients were registered).  In addition, when
+ * ib_register_client() is called, the client will receive an add
+ * callback for all devices already registered.
+ */
+int ib_register_client(struct ib_client *client)
+{
+       struct ib_device *device;
+
+       down(&device_sem);
+
+       list_add_tail(&client->list, &client_list);
+       list_for_each_entry(device, &device_list, core_list)
+               if (client->add && !add_client_context(device, client))
+                       client->add(device);
+
+       up(&device_sem);
+
+       return 0;
+}
+EXPORT_SYMBOL(ib_register_client);
+
+/**
+ * ib_unregister_client - Unregister an IB client
+ * @client:Client to unregister
+ *
+ * Upper level users use ib_unregister_client() to remove their client
+ * registration.  When ib_unregister_client() is called, the client
+ * will receive a remove callback for each IB device still registered.
+ */
+void ib_unregister_client(struct ib_client *client)
+{
+       struct ib_client_data *context, *tmp;
+       struct ib_device *device;
+       unsigned long flags;
+
+       down(&device_sem);
+
+       list_for_each_entry(device, &device_list, core_list) {
+               if (client->remove)
+                       client->remove(device);
+
+               spin_lock_irqsave(&device->client_data_lock, flags);
+               list_for_each_entry_safe(context, tmp, &device->client_data_list, list)
+                       if (context->client == client) {
+                               list_del(&context->list);
+                               kfree(context);
+                       }
+               spin_unlock_irqrestore(&device->client_data_lock, flags);
+       }
+       list_del(&client->list);
+
+       up(&device_sem);
+}
+EXPORT_SYMBOL(ib_unregister_client);
+
+/**
+ * ib_get_client_data - Get IB client context
+ * @device:Device to get context for
+ * @client:Client to get context for
+ *
+ * ib_get_client_data() returns client context set with
+ * ib_set_client_data().
+ */
+void *ib_get_client_data(struct ib_device *device, struct ib_client *client)
+{
+       struct ib_client_data *context;
+       void *ret = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&device->client_data_lock, flags);
+       list_for_each_entry(context, &device->client_data_list, list)
+               if (context->client == client) {
+                       ret = context->data;
+                       break;
+               }
+       spin_unlock_irqrestore(&device->client_data_lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_get_client_data);
+
+/**
+ * ib_set_client_data - Get IB client context
+ * @device:Device to set context for
+ * @client:Client to set context for
+ * @data:Context to set
+ *
+ * ib_set_client_data() sets client context that can be retrieved with
+ * ib_get_client_data().
+ */
+void ib_set_client_data(struct ib_device *device, struct ib_client *client,
+                       void *data)
+{
+       struct ib_client_data *context;
+       unsigned long flags;
+
+       spin_lock_irqsave(&device->client_data_lock, flags);
+       list_for_each_entry(context, &device->client_data_list, list)
+               if (context->client == client) {
+                       context->data = data;
+                       goto out;
+               }
+
+       printk(KERN_WARNING "No client context found for %s/%s\n",
+              device->name, client->name);
+
+out:
+       spin_unlock_irqrestore(&device->client_data_lock, flags);
+}
+EXPORT_SYMBOL(ib_set_client_data);
+
+/**
+ * ib_register_event_handler - Register an IB event handler
+ * @event_handler:Handler to register
+ *
+ * ib_register_event_handler() registers an event handler that will be
+ * called back when asynchronous IB events occur (as defined in
+ * chapter 11 of the InfiniBand Architecture Specification).  This
+ * callback may occur in interrupt context.
+ */
+int ib_register_event_handler  (struct ib_event_handler *event_handler)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&event_handler->device->event_handler_lock, flags);
+       list_add_tail(&event_handler->list,
+                     &event_handler->device->event_handler_list);
+       spin_unlock_irqrestore(&event_handler->device->event_handler_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ib_register_event_handler);
+
+/**
+ * ib_unregister_event_handler - Unregister an event handler
+ * @event_handler:Handler to unregister
+ *
+ * Unregister an event handler registered with
+ * ib_register_event_handler().
+ */
+int ib_unregister_event_handler(struct ib_event_handler *event_handler)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&event_handler->device->event_handler_lock, flags);
+       list_del(&event_handler->list);
+       spin_unlock_irqrestore(&event_handler->device->event_handler_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ib_unregister_event_handler);
+
+/**
+ * ib_dispatch_event - Dispatch an asynchronous event
+ * @event:Event to dispatch
+ *
+ * Low-level drivers must call ib_dispatch_event() to dispatch the
+ * event to all registered event handlers when an asynchronous event
+ * occurs.
+ */
+void ib_dispatch_event(struct ib_event *event)
+{
+       unsigned long flags;
+       struct ib_event_handler *handler;
+
+       spin_lock_irqsave(&event->device->event_handler_lock, flags);
+
+       list_for_each_entry(handler, &event->device->event_handler_list, list)
+               handler->handler(handler, event);
+
+       spin_unlock_irqrestore(&event->device->event_handler_lock, flags);
+}
+EXPORT_SYMBOL(ib_dispatch_event);
+
+/**
+ * ib_query_device - Query IB device attributes
+ * @device:Device to query
+ * @device_attr:Device attributes
+ *
+ * ib_query_device() returns the attributes of a device through the
+ * @device_attr pointer.
+ */
+int ib_query_device(struct ib_device *device,
+                   struct ib_device_attr *device_attr)
+{
+       return device->query_device(device, device_attr);
+}
+EXPORT_SYMBOL(ib_query_device);
+
+/**
+ * ib_query_port - Query IB port attributes
+ * @device:Device to query
+ * @port_num:Port number to query
+ * @port_attr:Port attributes
+ *
+ * ib_query_port() returns the attributes of a port through the
+ * @port_attr pointer.
+ */
+int ib_query_port(struct ib_device *device,
+                 u8 port_num,
+                 struct ib_port_attr *port_attr)
+{
+       return device->query_port(device, port_num, port_attr);
+}
+EXPORT_SYMBOL(ib_query_port);
+
+/**
+ * ib_query_gid - Get GID table entry
+ * @device:Device to query
+ * @port_num:Port number to query
+ * @index:GID table index to query
+ * @gid:Returned GID
+ *
+ * ib_query_gid() fetches the specified GID table entry.
+ */
+int ib_query_gid(struct ib_device *device,
+                u8 port_num, int index, union ib_gid *gid)
+{
+       return device->query_gid(device, port_num, index, gid);
+}
+EXPORT_SYMBOL(ib_query_gid);
+
+/**
+ * ib_query_pkey - Get P_Key table entry
+ * @device:Device to query
+ * @port_num:Port number to query
+ * @index:P_Key table index to query
+ * @pkey:Returned P_Key
+ *
+ * ib_query_pkey() fetches the specified P_Key table entry.
+ */
+int ib_query_pkey(struct ib_device *device,
+                 u8 port_num, u16 index, u16 *pkey)
+{
+       return device->query_pkey(device, port_num, index, pkey);
+}
+EXPORT_SYMBOL(ib_query_pkey);
+
+/**
+ * ib_modify_device - Change IB device attributes
+ * @device:Device to modify
+ * @device_modify_mask:Mask of attributes to change
+ * @device_modify:New attribute values
+ *
+ * ib_modify_device() changes a device's attributes as specified by
+ * the @device_modify_mask and @device_modify structure.
+ */
+int ib_modify_device(struct ib_device *device,
+                    int device_modify_mask,
+                    struct ib_device_modify *device_modify)
+{
+       return device->modify_device(device, device_modify_mask,
+                                    device_modify);
+}
+EXPORT_SYMBOL(ib_modify_device);
+
+/**
+ * ib_modify_port - Modifies the attributes for the specified port.
+ * @device: The device to modify.
+ * @port_num: The number of the port to modify.
+ * @port_modify_mask: Mask used to specify which attributes of the port
+ *   to change.
+ * @port_modify: New attribute values for the port.
+ *
+ * ib_modify_port() changes a port's attributes as specified by the
+ * @port_modify_mask and @port_modify structure.
+ */
+int ib_modify_port(struct ib_device *device,
+                  u8 port_num, int port_modify_mask,
+                  struct ib_port_modify *port_modify)
+{
+       return device->modify_port(device, port_num, port_modify_mask,
+                                  port_modify);
+}
+EXPORT_SYMBOL(ib_modify_port);
+
+static int __init ib_core_init(void)
+{
+       int ret;
+
+       ret = ib_sysfs_setup();
+       if (ret)
+               printk(KERN_WARNING "Couldn't create InfiniBand device class\n");
+
+       ret = ib_cache_setup();
+       if (ret) {
+               printk(KERN_WARNING "Couldn't set up InfiniBand P_Key/GID cache\n");
+               ib_sysfs_cleanup();
+       }
+
+       return ret;
+}
+
+static void __exit ib_core_cleanup(void)
+{
+       ib_cache_cleanup();
+       ib_sysfs_cleanup();
+}
+
+module_init(ib_core_init);
+module_exit(ib_core_cleanup);
diff --git a/drivers/infiniband/core/fmr_pool.c b/drivers/infiniband/core/fmr_pool.c
new file mode 100644 (file)
index 0000000..2e9469f
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: fmr_pool.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/jhash.h>
+#include <linux/kthread.h>
+
+#include <ib_fmr_pool.h>
+
+#include "core_priv.h"
+
+enum {
+       IB_FMR_MAX_REMAPS = 32,
+
+       IB_FMR_HASH_BITS  = 8,
+       IB_FMR_HASH_SIZE  = 1 << IB_FMR_HASH_BITS,
+       IB_FMR_HASH_MASK  = IB_FMR_HASH_SIZE - 1
+};
+
+/*
+ * If an FMR is not in use, then the list member will point to either
+ * its pool's free_list (if the FMR can be mapped again; that is,
+ * remap_count < IB_FMR_MAX_REMAPS) or its pool's dirty_list (if the
+ * FMR needs to be unmapped before being remapped).  In either of
+ * these cases it is a bug if the ref_count is not 0.  In other words,
+ * if ref_count is > 0, then the list member must not be linked into
+ * either free_list or dirty_list.
+ *
+ * The cache_node member is used to link the FMR into a cache bucket
+ * (if caching is enabled).  This is independent of the reference
+ * count of the FMR.  When a valid FMR is released, its ref_count is
+ * decremented, and if ref_count reaches 0, the FMR is placed in
+ * either free_list or dirty_list as appropriate.  However, it is not
+ * removed from the cache and may be "revived" if a call to
+ * ib_fmr_register_physical() occurs before the FMR is remapped.  In
+ * this case we just increment the ref_count and remove the FMR from
+ * free_list/dirty_list.
+ *
+ * Before we remap an FMR from free_list, we remove it from the cache
+ * (to prevent another user from obtaining a stale FMR).  When an FMR
+ * is released, we add it to the tail of the free list, so that our
+ * cache eviction policy is "least recently used."
+ *
+ * All manipulation of ref_count, list and cache_node is protected by
+ * pool_lock to maintain consistency.
+ */
+
+struct ib_fmr_pool {
+       spinlock_t                pool_lock;
+
+       int                       pool_size;
+       int                       max_pages;
+       int                       dirty_watermark;
+       int                       dirty_len;
+       struct list_head          free_list;
+       struct list_head          dirty_list;
+       struct hlist_head        *cache_bucket;
+
+       void                     (*flush_function)(struct ib_fmr_pool *pool,
+                                                  void *              arg);
+       void                     *flush_arg;
+
+       struct task_struct       *thread;
+
+       atomic_t                  req_ser;
+       atomic_t                  flush_ser;
+
+       wait_queue_head_t         force_wait;
+};
+
+static inline u32 ib_fmr_hash(u64 first_page)
+{
+       return jhash_2words((u32) first_page,
+                           (u32) (first_page >> 32),
+                           0);
+}
+
+/* Caller must hold pool_lock */
+static inline struct ib_pool_fmr *ib_fmr_cache_lookup(struct ib_fmr_pool *pool,
+                                                     u64 *page_list,
+                                                     int  page_list_len,
+                                                     u64  io_virtual_address)
+{
+       struct hlist_head *bucket;
+       struct ib_pool_fmr *fmr;
+       struct hlist_node *pos;
+
+       if (!pool->cache_bucket)
+               return NULL;
+
+       bucket = pool->cache_bucket + ib_fmr_hash(*page_list);
+
+       hlist_for_each_entry(fmr, pos, bucket, cache_node)
+               if (io_virtual_address == fmr->io_virtual_address &&
+                   page_list_len      == fmr->page_list_len      &&
+                   !memcmp(page_list, fmr->page_list,
+                           page_list_len * sizeof *page_list))
+                       return fmr;
+
+       return NULL;
+}
+
+static void ib_fmr_batch_release(struct ib_fmr_pool *pool)
+{
+       int                 ret;
+       struct ib_pool_fmr *fmr;
+       LIST_HEAD(unmap_list);
+       LIST_HEAD(fmr_list);
+
+       spin_lock_irq(&pool->pool_lock);
+
+       list_for_each_entry(fmr, &pool->dirty_list, list) {
+               hlist_del_init(&fmr->cache_node);
+               fmr->remap_count = 0;
+               list_add_tail(&fmr->fmr->list, &fmr_list);
+
+#ifdef DEBUG
+               if (fmr->ref_count !=0) {
+                       printk(KERN_WARNING "Unmapping FMR 0x%08x with ref count %d",
+                              fmr, fmr->ref_count);
+               }
+#endif
+       }
+
+       list_splice(&pool->dirty_list, &unmap_list);
+       INIT_LIST_HEAD(&pool->dirty_list);
+       pool->dirty_len = 0;
+
+       spin_unlock_irq(&pool->pool_lock);
+
+       if (list_empty(&unmap_list)) {
+               return;
+       }
+
+       ret = ib_unmap_fmr(&fmr_list);
+       if (ret)
+               printk(KERN_WARNING "ib_unmap_fmr returned %d", ret);
+
+       spin_lock_irq(&pool->pool_lock);
+       list_splice(&unmap_list, &pool->free_list);
+       spin_unlock_irq(&pool->pool_lock);
+}
+
+static int ib_fmr_cleanup_thread(void *pool_ptr)
+{
+       struct ib_fmr_pool *pool = pool_ptr;
+
+       do {
+               if (pool->dirty_len >= pool->dirty_watermark ||
+                   atomic_read(&pool->flush_ser) - atomic_read(&pool->req_ser) < 0) {
+                       ib_fmr_batch_release(pool);
+
+                       atomic_inc(&pool->flush_ser);
+                       wake_up_interruptible(&pool->force_wait);
+
+                       if (pool->flush_function)
+                               pool->flush_function(pool, pool->flush_arg);
+               }
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (pool->dirty_len < pool->dirty_watermark &&
+                   atomic_read(&pool->flush_ser) - atomic_read(&pool->req_ser) >= 0 &&
+                   !kthread_should_stop())
+                       schedule();
+               __set_current_state(TASK_RUNNING);
+       } while (!kthread_should_stop());
+
+       return 0;
+}
+
+/**
+ * ib_create_fmr_pool - Create an FMR pool
+ * @pd:Protection domain for FMRs
+ * @params:FMR pool parameters
+ *
+ * Create a pool of FMRs.  Return value is pointer to new pool or
+ * error code if creation failed.
+ */
+struct ib_fmr_pool *ib_create_fmr_pool(struct ib_pd             *pd,
+                                      struct ib_fmr_pool_param *params)
+{
+       struct ib_device   *device;
+       struct ib_fmr_pool *pool;
+       int i;
+       int ret;
+
+       if (!params)
+               return ERR_PTR(-EINVAL);
+
+       device = pd->device;
+       if (!device->alloc_fmr    || !device->dealloc_fmr  ||
+           !device->map_phys_fmr || !device->unmap_fmr) {
+               printk(KERN_WARNING "Device %s does not support fast memory regions",
+                      device->name);
+               return ERR_PTR(-ENOSYS);
+       }
+
+       pool = kmalloc(sizeof *pool, GFP_KERNEL);
+       if (!pool) {
+               printk(KERN_WARNING "couldn't allocate pool struct");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       pool->cache_bucket   = NULL;
+
+       pool->flush_function = params->flush_function;
+       pool->flush_arg      = params->flush_arg;
+
+       INIT_LIST_HEAD(&pool->free_list);
+       INIT_LIST_HEAD(&pool->dirty_list);
+
+       if (params->cache) {
+               pool->cache_bucket =
+                       kmalloc(IB_FMR_HASH_SIZE * sizeof *pool->cache_bucket,
+                               GFP_KERNEL);
+               if (!pool->cache_bucket) {
+                       printk(KERN_WARNING "Failed to allocate cache in pool");
+                       ret = -ENOMEM;
+                       goto out_free_pool;
+               }
+
+               for (i = 0; i < IB_FMR_HASH_SIZE; ++i)
+                       INIT_HLIST_HEAD(pool->cache_bucket + i);
+       }
+
+       pool->pool_size       = 0;
+       pool->max_pages       = params->max_pages_per_fmr;
+       pool->dirty_watermark = params->dirty_watermark;
+       pool->dirty_len       = 0;
+       spin_lock_init(&pool->pool_lock);
+       atomic_set(&pool->req_ser,   0);
+       atomic_set(&pool->flush_ser, 0);
+       init_waitqueue_head(&pool->force_wait);
+
+       pool->thread = kthread_create(ib_fmr_cleanup_thread,
+                                     pool,
+                                     "ib_fmr(%s)",
+                                     device->name);
+       if (IS_ERR(pool->thread)) {
+               printk(KERN_WARNING "couldn't start cleanup thread");
+               ret = PTR_ERR(pool->thread);
+               goto out_free_pool;
+       }
+
+       {
+               struct ib_pool_fmr *fmr;
+               struct ib_fmr_attr attr = {
+                       .max_pages = params->max_pages_per_fmr,
+                       .max_maps  = IB_FMR_MAX_REMAPS,
+                       .page_size = PAGE_SHIFT
+               };
+
+               for (i = 0; i < params->pool_size; ++i) {
+                       fmr = kmalloc(sizeof *fmr + params->max_pages_per_fmr * sizeof (u64),
+                                     GFP_KERNEL);
+                       if (!fmr) {
+                               printk(KERN_WARNING "failed to allocate fmr struct "
+                                      "for FMR %d", i);
+                               goto out_fail;
+                       }
+
+                       fmr->pool             = pool;
+                       fmr->remap_count      = 0;
+                       fmr->ref_count        = 0;
+                       INIT_HLIST_NODE(&fmr->cache_node);
+
+                       fmr->fmr = ib_alloc_fmr(pd, params->access, &attr);
+                       if (IS_ERR(fmr->fmr)) {
+                               printk(KERN_WARNING "fmr_create failed for FMR %d", i);
+                               kfree(fmr);
+                               goto out_fail;
+                       }
+
+                       list_add_tail(&fmr->list, &pool->free_list);
+                       ++pool->pool_size;
+               }
+       }
+
+       return pool;
+
+ out_free_pool:
+       kfree(pool->cache_bucket);
+       kfree(pool);
+
+       return ERR_PTR(ret);
+
+ out_fail:
+       ib_destroy_fmr_pool(pool);
+
+       return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL(ib_create_fmr_pool);
+
+/**
+ * ib_destroy_fmr_pool - Free FMR pool
+ * @pool:FMR pool to free
+ *
+ * Destroy an FMR pool and free all associated resources.
+ */
+int ib_destroy_fmr_pool(struct ib_fmr_pool *pool)
+{
+       struct ib_pool_fmr *fmr;
+       struct ib_pool_fmr *tmp;
+       int                 i;
+
+       kthread_stop(pool->thread);
+       ib_fmr_batch_release(pool);
+
+       i = 0;
+       list_for_each_entry_safe(fmr, tmp, &pool->free_list, list) {
+               ib_dealloc_fmr(fmr->fmr);
+               list_del(&fmr->list);
+               kfree(fmr);
+               ++i;
+       }
+
+       if (i < pool->pool_size)
+               printk(KERN_WARNING "pool still has %d regions registered",
+                      pool->pool_size - i);
+
+       kfree(pool->cache_bucket);
+       kfree(pool);
+
+       return 0;
+}
+EXPORT_SYMBOL(ib_destroy_fmr_pool);
+
+/**
+ * ib_flush_fmr_pool - Invalidate all unmapped FMRs
+ * @pool:FMR pool to flush
+ *
+ * Ensure that all unmapped FMRs are fully invalidated.
+ */
+int ib_flush_fmr_pool(struct ib_fmr_pool *pool)
+{
+       int serial;
+
+       atomic_inc(&pool->req_ser);
+       /*
+        * It's OK if someone else bumps req_ser again here -- we'll
+        * just wait a little longer.
+        */
+       serial = atomic_read(&pool->req_ser);
+
+       wake_up_process(pool->thread);
+
+       if (wait_event_interruptible(pool->force_wait,
+                                    atomic_read(&pool->flush_ser) -
+                                    atomic_read(&pool->req_ser) >= 0))
+               return -EINTR;
+
+       return 0;
+}
+EXPORT_SYMBOL(ib_flush_fmr_pool);
+
+/**
+ * ib_fmr_pool_map_phys -
+ * @pool:FMR pool to allocate FMR from
+ * @page_list:List of pages to map
+ * @list_len:Number of pages in @page_list
+ * @io_virtual_address:I/O virtual address for new FMR
+ *
+ * Map an FMR from an FMR pool.
+ */
+struct ib_pool_fmr *ib_fmr_pool_map_phys(struct ib_fmr_pool *pool_handle,
+                                        u64                *page_list,
+                                        int                 list_len,
+                                        u64                *io_virtual_address)
+{
+       struct ib_fmr_pool *pool = pool_handle;
+       struct ib_pool_fmr *fmr;
+       unsigned long       flags;
+       int                 result;
+
+       if (list_len < 1 || list_len > pool->max_pages)
+               return ERR_PTR(-EINVAL);
+
+       spin_lock_irqsave(&pool->pool_lock, flags);
+       fmr = ib_fmr_cache_lookup(pool,
+                                 page_list,
+                                 list_len,
+                                 *io_virtual_address);
+       if (fmr) {
+               /* found in cache */
+               ++fmr->ref_count;
+               if (fmr->ref_count == 1) {
+                       list_del(&fmr->list);
+               }
+
+               spin_unlock_irqrestore(&pool->pool_lock, flags);
+
+               return fmr;
+       }
+
+       if (list_empty(&pool->free_list)) {
+               spin_unlock_irqrestore(&pool->pool_lock, flags);
+               return ERR_PTR(-EAGAIN);
+       }
+
+       fmr = list_entry(pool->free_list.next, struct ib_pool_fmr, list);
+       list_del(&fmr->list);
+       hlist_del_init(&fmr->cache_node);
+       spin_unlock_irqrestore(&pool->pool_lock, flags);
+
+       result = ib_map_phys_fmr(fmr->fmr, page_list, list_len,
+                                *io_virtual_address);
+
+       if (result) {
+               spin_lock_irqsave(&pool->pool_lock, flags);
+               list_add(&fmr->list, &pool->free_list);
+               spin_unlock_irqrestore(&pool->pool_lock, flags);
+
+               printk(KERN_WARNING "fmr_map returns %d",
+                      result);
+
+               return ERR_PTR(result);
+       }
+
+       ++fmr->remap_count;
+       fmr->ref_count = 1;
+
+       if (pool->cache_bucket) {
+               fmr->io_virtual_address = *io_virtual_address;
+               fmr->page_list_len      = list_len;
+               memcpy(fmr->page_list, page_list, list_len * sizeof(*page_list));
+
+               spin_lock_irqsave(&pool->pool_lock, flags);
+               hlist_add_head(&fmr->cache_node,
+                              pool->cache_bucket + ib_fmr_hash(fmr->page_list[0]));
+               spin_unlock_irqrestore(&pool->pool_lock, flags);
+       }
+
+       return fmr;
+}
+EXPORT_SYMBOL(ib_fmr_pool_map_phys);
+
+/**
+ * ib_fmr_pool_unmap - Unmap FMR
+ * @fmr:FMR to unmap
+ *
+ * Unmap an FMR.  The FMR mapping may remain valid until the FMR is
+ * reused (or until ib_flush_fmr_pool() is called).
+ */
+int ib_fmr_pool_unmap(struct ib_pool_fmr *fmr)
+{
+       struct ib_fmr_pool *pool;
+       unsigned long flags;
+
+       pool = fmr->pool;
+
+       spin_lock_irqsave(&pool->pool_lock, flags);
+
+       --fmr->ref_count;
+       if (!fmr->ref_count) {
+               if (fmr->remap_count < IB_FMR_MAX_REMAPS) {
+                       list_add_tail(&fmr->list, &pool->free_list);
+               } else {
+                       list_add_tail(&fmr->list, &pool->dirty_list);
+                       ++pool->dirty_len;
+                       wake_up_process(pool->thread);
+               }
+       }
+
+#ifdef DEBUG
+       if (fmr->ref_count < 0)
+               printk(KERN_WARNING "FMR %p has ref count %d < 0",
+                      fmr, fmr->ref_count);
+#endif
+
+       spin_unlock_irqrestore(&pool->pool_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(ib_fmr_pool_unmap);
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
new file mode 100644 (file)
index 0000000..754a2c5
--- /dev/null
@@ -0,0 +1,2689 @@
+/*
+ * Copyright (c) 2004, 2005 Voltaire, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mad.c 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+
+#include <ib_mad.h>
+
+#include "mad_priv.h"
+#include "smi.h"
+#include "agent.h"
+
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("kernel IB MAD API");
+MODULE_AUTHOR("Hal Rosenstock");
+MODULE_AUTHOR("Sean Hefty");
+
+
+kmem_cache_t *ib_mad_cache;
+static struct list_head ib_mad_port_list;
+static u32 ib_mad_client_id = 0;
+
+/* Port list lock */
+static spinlock_t ib_mad_port_list_lock;
+
+
+/* Forward declarations */
+static int method_in_use(struct ib_mad_mgmt_method_table **method,
+                        struct ib_mad_reg_req *mad_reg_req);
+static void remove_mad_reg_req(struct ib_mad_agent_private *priv);
+static struct ib_mad_agent_private *find_mad_agent(
+                                       struct ib_mad_port_private *port_priv,
+                                       struct ib_mad *mad, int solicited);
+static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
+                                   struct ib_mad_private *mad);
+static void cancel_mads(struct ib_mad_agent_private *mad_agent_priv);
+static void ib_mad_complete_send_wr(struct ib_mad_send_wr_private *mad_send_wr,
+                                   struct ib_mad_send_wc *mad_send_wc);
+static void timeout_sends(void *data);
+static void local_completions(void *data);
+static int solicited_mad(struct ib_mad *mad);
+static int add_nonoui_reg_req(struct ib_mad_reg_req *mad_reg_req,
+                             struct ib_mad_agent_private *agent_priv,
+                             u8 mgmt_class);
+static int add_oui_reg_req(struct ib_mad_reg_req *mad_reg_req,
+                          struct ib_mad_agent_private *agent_priv);
+
+/*
+ * Returns a ib_mad_port_private structure or NULL for a device/port
+ * Assumes ib_mad_port_list_lock is being held
+ */
+static inline struct ib_mad_port_private *
+__ib_get_mad_port(struct ib_device *device, int port_num)
+{
+       struct ib_mad_port_private *entry;
+
+       list_for_each_entry(entry, &ib_mad_port_list, port_list) {
+               if (entry->device == device && entry->port_num == port_num)
+                       return entry;
+       }
+       return NULL;
+}
+
+/*
+ * Wrapper function to return a ib_mad_port_private structure or NULL
+ * for a device/port
+ */
+static inline struct ib_mad_port_private *
+ib_get_mad_port(struct ib_device *device, int port_num)
+{
+       struct ib_mad_port_private *entry;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ib_mad_port_list_lock, flags);
+       entry = __ib_get_mad_port(device, port_num);
+       spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
+
+       return entry;
+}
+
+static inline u8 convert_mgmt_class(u8 mgmt_class)
+{
+       /* Alias IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE to 0 */
+       return mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ?
+               0 : mgmt_class;
+}
+
+static int get_spl_qp_index(enum ib_qp_type qp_type)
+{
+       switch (qp_type)
+       {
+       case IB_QPT_SMI:
+               return 0;
+       case IB_QPT_GSI:
+               return 1;
+       default:
+               return -1;
+       }
+}
+
+static int vendor_class_index(u8 mgmt_class)
+{
+       return mgmt_class - IB_MGMT_CLASS_VENDOR_RANGE2_START;
+}
+
+static int is_vendor_class(u8 mgmt_class)
+{
+       if ((mgmt_class < IB_MGMT_CLASS_VENDOR_RANGE2_START) ||
+           (mgmt_class > IB_MGMT_CLASS_VENDOR_RANGE2_END))
+               return 0;
+       return 1;
+}
+
+static int is_vendor_oui(char *oui)
+{
+       if (oui[0] || oui[1] || oui[2])
+               return 1;
+       return 0;
+}
+
+static int is_vendor_method_in_use(
+               struct ib_mad_mgmt_vendor_class *vendor_class,
+               struct ib_mad_reg_req *mad_reg_req)
+{
+       struct ib_mad_mgmt_method_table *method;
+       int i;
+
+       for (i = 0; i < MAX_MGMT_OUI; i++) {
+               if (!memcmp(vendor_class->oui[i], mad_reg_req->oui, 3)) {
+                       method = vendor_class->method_table[i];
+                       if (method) {
+                               if (method_in_use(&method, mad_reg_req))
+                                       return 1;
+                               else
+                                       break;
+                       }
+               }
+       }
+       return 0;
+}
+
+/*
+ * ib_register_mad_agent - Register to send/receive MADs
+ */
+struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
+                                          u8 port_num,
+                                          enum ib_qp_type qp_type,
+                                          struct ib_mad_reg_req *mad_reg_req,
+                                          u8 rmpp_version,
+                                          ib_mad_send_handler send_handler,
+                                          ib_mad_recv_handler recv_handler,
+                                          void *context)
+{
+       struct ib_mad_port_private *port_priv;
+       struct ib_mad_agent *ret = ERR_PTR(-EINVAL);
+       struct ib_mad_agent_private *mad_agent_priv;
+       struct ib_mad_reg_req *reg_req = NULL;
+       struct ib_mad_mgmt_class_table *class;
+       struct ib_mad_mgmt_vendor_class_table *vendor;
+       struct ib_mad_mgmt_vendor_class *vendor_class;
+       struct ib_mad_mgmt_method_table *method;
+       int ret2, qpn;
+       unsigned long flags;
+       u8 mgmt_class, vclass;
+
+       /* Validate parameters */
+       qpn = get_spl_qp_index(qp_type);
+       if (qpn == -1)
+               goto error1;
+
+       if (rmpp_version)
+               goto error1;    /* XXX: until RMPP implemented */
+
+       /* Validate MAD registration request if supplied */
+       if (mad_reg_req) {
+               if (mad_reg_req->mgmt_class_version >= MAX_MGMT_VERSION)
+                       goto error1;
+               if (!recv_handler)
+                       goto error1;
+               if (mad_reg_req->mgmt_class >= MAX_MGMT_CLASS) {
+                       /*
+                        * IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE is the only
+                        * one in this range currently allowed
+                        */
+                       if (mad_reg_req->mgmt_class !=
+                           IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+                               goto error1;
+               } else if (mad_reg_req->mgmt_class == 0) {
+                       /*
+                        * Class 0 is reserved in IBA and is used for
+                        * aliasing of IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE
+                        */
+                       goto error1;
+               } else if (is_vendor_class(mad_reg_req->mgmt_class)) {
+                       /*
+                        * If class is in "new" vendor range,
+                        * ensure supplied OUI is not zero
+                        */
+                       if (!is_vendor_oui(mad_reg_req->oui))
+                               goto error1;
+               }
+               /* Make sure class supplied is consistent with QP type */
+               if (qp_type == IB_QPT_SMI) {
+                       if ((mad_reg_req->mgmt_class !=
+                                       IB_MGMT_CLASS_SUBN_LID_ROUTED) &&
+                           (mad_reg_req->mgmt_class !=
+                                       IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE))
+                               goto error1;
+               } else {
+                       if ((mad_reg_req->mgmt_class ==
+                                       IB_MGMT_CLASS_SUBN_LID_ROUTED) ||
+                           (mad_reg_req->mgmt_class ==
+                                       IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE))
+                               goto error1;
+               }
+       } else {
+               /* No registration request supplied */
+               if (!send_handler)
+                       goto error1;
+       }
+
+       /* Validate device and port */
+       port_priv = ib_get_mad_port(device, port_num);
+       if (!port_priv) {
+               ret = ERR_PTR(-ENODEV);
+               goto error1;
+       }
+
+       /* Allocate structures */
+       mad_agent_priv = kmalloc(sizeof *mad_agent_priv, GFP_KERNEL);
+       if (!mad_agent_priv) {
+               ret = ERR_PTR(-ENOMEM);
+               goto error1;
+       }
+
+       if (mad_reg_req) {
+               reg_req = kmalloc(sizeof *reg_req, GFP_KERNEL);
+               if (!reg_req) {
+                       ret = ERR_PTR(-ENOMEM);
+                       goto error2;
+               }
+               /* Make a copy of the MAD registration request */
+               memcpy(reg_req, mad_reg_req, sizeof *reg_req);
+       }
+
+       /* Now, fill in the various structures */
+       memset(mad_agent_priv, 0, sizeof *mad_agent_priv);
+       mad_agent_priv->qp_info = &port_priv->qp_info[qpn];
+       mad_agent_priv->reg_req = reg_req;
+       mad_agent_priv->rmpp_version = rmpp_version;
+       mad_agent_priv->agent.device = device;
+       mad_agent_priv->agent.recv_handler = recv_handler;
+       mad_agent_priv->agent.send_handler = send_handler;
+       mad_agent_priv->agent.context = context;
+       mad_agent_priv->agent.qp = port_priv->qp_info[qpn].qp;
+       mad_agent_priv->agent.port_num = port_num;
+
+       spin_lock_irqsave(&port_priv->reg_lock, flags);
+       mad_agent_priv->agent.hi_tid = ++ib_mad_client_id;
+
+       /*
+        * Make sure MAD registration (if supplied)
+        * is non overlapping with any existing ones
+        */
+       if (mad_reg_req) {
+               mgmt_class = convert_mgmt_class(mad_reg_req->mgmt_class);
+               if (!is_vendor_class(mgmt_class)) {
+                       class = port_priv->version[mad_reg_req->
+                                                  mgmt_class_version].class;
+                       if (class) {
+                               method = class->method_table[mgmt_class];
+                               if (method) {
+                                       if (method_in_use(&method,
+                                                          mad_reg_req))
+                                               goto error3;
+                               }
+                       }
+                       ret2 = add_nonoui_reg_req(mad_reg_req, mad_agent_priv,
+                                                 mgmt_class);
+               } else {
+                       /* "New" vendor class range */
+                       vendor = port_priv->version[mad_reg_req->
+                                                   mgmt_class_version].vendor;
+                       if (vendor) {
+                               vclass = vendor_class_index(mgmt_class);
+                               vendor_class = vendor->vendor_class[vclass];
+                               if (vendor_class) {
+                                       if (is_vendor_method_in_use(
+                                                       vendor_class,
+                                                       mad_reg_req))
+                                               goto error3;
+                               }
+                       }
+                       ret2 = add_oui_reg_req(mad_reg_req, mad_agent_priv);
+               }
+               if (ret2) {
+                       ret = ERR_PTR(ret2);
+                       goto error3;
+               }
+       }
+
+       /* Add mad agent into port's agent list */
+       list_add_tail(&mad_agent_priv->agent_list, &port_priv->agent_list);
+       spin_unlock_irqrestore(&port_priv->reg_lock, flags);
+
+       spin_lock_init(&mad_agent_priv->lock);
+       INIT_LIST_HEAD(&mad_agent_priv->send_list);
+       INIT_LIST_HEAD(&mad_agent_priv->wait_list);
+       INIT_WORK(&mad_agent_priv->timed_work, timeout_sends, mad_agent_priv);
+       INIT_LIST_HEAD(&mad_agent_priv->local_list);
+       INIT_WORK(&mad_agent_priv->local_work, local_completions,
+                  mad_agent_priv);
+       atomic_set(&mad_agent_priv->refcount, 1);
+       init_waitqueue_head(&mad_agent_priv->wait);
+
+       return &mad_agent_priv->agent;
+
+error3:
+       spin_unlock_irqrestore(&port_priv->reg_lock, flags);
+       kfree(reg_req);
+error2:
+       kfree(mad_agent_priv);
+error1:
+       return ret;
+}
+EXPORT_SYMBOL(ib_register_mad_agent);
+
+static inline int is_snooping_sends(int mad_snoop_flags)
+{
+       return (mad_snoop_flags &
+               (/*IB_MAD_SNOOP_POSTED_SENDS |
+                IB_MAD_SNOOP_RMPP_SENDS |*/
+                IB_MAD_SNOOP_SEND_COMPLETIONS /*|
+                IB_MAD_SNOOP_RMPP_SEND_COMPLETIONS*/));
+}
+
+static inline int is_snooping_recvs(int mad_snoop_flags)
+{
+       return (mad_snoop_flags &
+               (IB_MAD_SNOOP_RECVS /*|
+                IB_MAD_SNOOP_RMPP_RECVS*/));
+}
+
+static int register_snoop_agent(struct ib_mad_qp_info *qp_info,
+                               struct ib_mad_snoop_private *mad_snoop_priv)
+{
+       struct ib_mad_snoop_private **new_snoop_table;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&qp_info->snoop_lock, flags);
+       /* Check for empty slot in array. */
+       for (i = 0; i < qp_info->snoop_table_size; i++)
+               if (!qp_info->snoop_table[i])
+                       break;
+
+       if (i == qp_info->snoop_table_size) {
+               /* Grow table. */
+               new_snoop_table = kmalloc(sizeof mad_snoop_priv *
+                                         qp_info->snoop_table_size + 1,
+                                         GFP_ATOMIC);
+               if (!new_snoop_table) {
+                       i = -ENOMEM;
+                       goto out;
+               }
+               if (qp_info->snoop_table) {
+                       memcpy(new_snoop_table, qp_info->snoop_table,
+                              sizeof mad_snoop_priv *
+                              qp_info->snoop_table_size);
+                       kfree(qp_info->snoop_table);
+               }
+               qp_info->snoop_table = new_snoop_table;
+               qp_info->snoop_table_size++;
+       }
+       qp_info->snoop_table[i] = mad_snoop_priv;
+       atomic_inc(&qp_info->snoop_count);
+out:
+       spin_unlock_irqrestore(&qp_info->snoop_lock, flags);
+       return i;
+}
+
+struct ib_mad_agent *ib_register_mad_snoop(struct ib_device *device,
+                                          u8 port_num,
+                                          enum ib_qp_type qp_type,
+                                          int mad_snoop_flags,
+                                          ib_mad_snoop_handler snoop_handler,
+                                          ib_mad_recv_handler recv_handler,
+                                          void *context)
+{
+       struct ib_mad_port_private *port_priv;
+       struct ib_mad_agent *ret;
+       struct ib_mad_snoop_private *mad_snoop_priv;
+       int qpn;
+
+       /* Validate parameters */
+       if ((is_snooping_sends(mad_snoop_flags) && !snoop_handler) ||
+           (is_snooping_recvs(mad_snoop_flags) && !recv_handler)) {
+               ret = ERR_PTR(-EINVAL);
+               goto error1;
+       }
+       qpn = get_spl_qp_index(qp_type);
+       if (qpn == -1) {
+               ret = ERR_PTR(-EINVAL);
+               goto error1;
+       }
+       port_priv = ib_get_mad_port(device, port_num);
+       if (!port_priv) {
+               ret = ERR_PTR(-ENODEV);
+               goto error1;
+       }
+       /* Allocate structures */
+       mad_snoop_priv = kmalloc(sizeof *mad_snoop_priv, GFP_KERNEL);
+       if (!mad_snoop_priv) {
+               ret = ERR_PTR(-ENOMEM);
+               goto error1;
+       }
+
+       /* Now, fill in the various structures */
+       memset(mad_snoop_priv, 0, sizeof *mad_snoop_priv);
+       mad_snoop_priv->qp_info = &port_priv->qp_info[qpn];
+       mad_snoop_priv->agent.device = device;
+       mad_snoop_priv->agent.recv_handler = recv_handler;
+       mad_snoop_priv->agent.snoop_handler = snoop_handler;
+       mad_snoop_priv->agent.context = context;
+       mad_snoop_priv->agent.qp = port_priv->qp_info[qpn].qp;
+       mad_snoop_priv->agent.port_num = port_num;
+       mad_snoop_priv->mad_snoop_flags = mad_snoop_flags;
+       init_waitqueue_head(&mad_snoop_priv->wait);
+       mad_snoop_priv->snoop_index = register_snoop_agent(
+                                               &port_priv->qp_info[qpn],
+                                               mad_snoop_priv);
+       if (mad_snoop_priv->snoop_index < 0) {
+               ret = ERR_PTR(mad_snoop_priv->snoop_index);
+               goto error2;
+       }
+
+       atomic_set(&mad_snoop_priv->refcount, 1);
+       return &mad_snoop_priv->agent;
+
+error2:
+       kfree(mad_snoop_priv);
+error1:
+       return ret;
+}
+EXPORT_SYMBOL(ib_register_mad_snoop);
+
+static void unregister_mad_agent(struct ib_mad_agent_private *mad_agent_priv)
+{
+       struct ib_mad_port_private *port_priv;
+       unsigned long flags;
+
+       /* Note that we could still be handling received MADs */
+
+       /*
+        * Canceling all sends results in dropping received response
+        * MADs, preventing us from queuing additional work
+        */
+       cancel_mads(mad_agent_priv);
+
+       port_priv = mad_agent_priv->qp_info->port_priv;
+       cancel_delayed_work(&mad_agent_priv->timed_work);
+       flush_workqueue(port_priv->wq);
+
+       spin_lock_irqsave(&port_priv->reg_lock, flags);
+       remove_mad_reg_req(mad_agent_priv);
+       list_del(&mad_agent_priv->agent_list);
+       spin_unlock_irqrestore(&port_priv->reg_lock, flags);
+
+       /* XXX: Cleanup pending RMPP receives for this agent */
+
+       atomic_dec(&mad_agent_priv->refcount);
+       wait_event(mad_agent_priv->wait,
+                  !atomic_read(&mad_agent_priv->refcount));
+
+       if (mad_agent_priv->reg_req)
+               kfree(mad_agent_priv->reg_req);
+       kfree(mad_agent_priv);
+}
+
+static void unregister_mad_snoop(struct ib_mad_snoop_private *mad_snoop_priv)
+{
+       struct ib_mad_qp_info *qp_info;
+       unsigned long flags;
+
+       qp_info = mad_snoop_priv->qp_info;
+       spin_lock_irqsave(&qp_info->snoop_lock, flags);
+       qp_info->snoop_table[mad_snoop_priv->snoop_index] = NULL;
+       atomic_dec(&qp_info->snoop_count);
+       spin_unlock_irqrestore(&qp_info->snoop_lock, flags);
+
+       atomic_dec(&mad_snoop_priv->refcount);
+       wait_event(mad_snoop_priv->wait,
+                  !atomic_read(&mad_snoop_priv->refcount));
+
+       kfree(mad_snoop_priv);
+}
+
+/*
+ * ib_unregister_mad_agent - Unregisters a client from using MAD services
+ */
+int ib_unregister_mad_agent(struct ib_mad_agent *mad_agent)
+{
+       struct ib_mad_agent_private *mad_agent_priv;
+       struct ib_mad_snoop_private *mad_snoop_priv;
+
+       /* If the TID is zero, the agent can only snoop. */
+       if (mad_agent->hi_tid) {
+               mad_agent_priv = container_of(mad_agent,
+                                             struct ib_mad_agent_private,
+                                             agent);
+               unregister_mad_agent(mad_agent_priv);
+       } else {
+               mad_snoop_priv = container_of(mad_agent,
+                                             struct ib_mad_snoop_private,
+                                             agent);
+               unregister_mad_snoop(mad_snoop_priv);
+       }
+       return 0;
+}
+EXPORT_SYMBOL(ib_unregister_mad_agent);
+
+static void dequeue_mad(struct ib_mad_list_head *mad_list)
+{
+       struct ib_mad_queue *mad_queue;
+       unsigned long flags;
+
+       BUG_ON(!mad_list->mad_queue);
+       mad_queue = mad_list->mad_queue;
+       spin_lock_irqsave(&mad_queue->lock, flags);
+       list_del(&mad_list->list);
+       mad_queue->count--;
+       spin_unlock_irqrestore(&mad_queue->lock, flags);
+}
+
+static void snoop_send(struct ib_mad_qp_info *qp_info,
+                      struct ib_send_wr *send_wr,
+                      struct ib_mad_send_wc *mad_send_wc,
+                      int mad_snoop_flags)
+{
+       struct ib_mad_snoop_private *mad_snoop_priv;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&qp_info->snoop_lock, flags);
+       for (i = 0; i < qp_info->snoop_table_size; i++) {
+               mad_snoop_priv = qp_info->snoop_table[i];
+               if (!mad_snoop_priv ||
+                   !(mad_snoop_priv->mad_snoop_flags & mad_snoop_flags))
+                       continue;
+
+               atomic_inc(&mad_snoop_priv->refcount);
+               spin_unlock_irqrestore(&qp_info->snoop_lock, flags);
+               mad_snoop_priv->agent.snoop_handler(&mad_snoop_priv->agent,
+                                                   send_wr, mad_send_wc);
+               if (atomic_dec_and_test(&mad_snoop_priv->refcount))
+                       wake_up(&mad_snoop_priv->wait);
+               spin_lock_irqsave(&qp_info->snoop_lock, flags);
+       }
+       spin_unlock_irqrestore(&qp_info->snoop_lock, flags);
+}
+
+static void snoop_recv(struct ib_mad_qp_info *qp_info,
+                      struct ib_mad_recv_wc *mad_recv_wc,
+                      int mad_snoop_flags)
+{
+       struct ib_mad_snoop_private *mad_snoop_priv;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&qp_info->snoop_lock, flags);
+       for (i = 0; i < qp_info->snoop_table_size; i++) {
+               mad_snoop_priv = qp_info->snoop_table[i];
+               if (!mad_snoop_priv ||
+                   !(mad_snoop_priv->mad_snoop_flags & mad_snoop_flags))
+                       continue;
+
+               atomic_inc(&mad_snoop_priv->refcount);
+               spin_unlock_irqrestore(&qp_info->snoop_lock, flags);
+               mad_snoop_priv->agent.recv_handler(&mad_snoop_priv->agent,
+                                                  mad_recv_wc);
+               if (atomic_dec_and_test(&mad_snoop_priv->refcount))
+                       wake_up(&mad_snoop_priv->wait);
+               spin_lock_irqsave(&qp_info->snoop_lock, flags);
+       }
+       spin_unlock_irqrestore(&qp_info->snoop_lock, flags);
+}
+
+static void build_smp_wc(u64 wr_id, u16 slid, u16 pkey_index, u8 port_num,
+                        struct ib_wc *wc)
+{
+       memset(wc, 0, sizeof *wc);
+       wc->wr_id = wr_id;
+       wc->status = IB_WC_SUCCESS;
+       wc->opcode = IB_WC_RECV;
+       wc->pkey_index = pkey_index;
+       wc->byte_len = sizeof(struct ib_mad) + sizeof(struct ib_grh);
+       wc->src_qp = IB_QP0;
+       wc->qp_num = IB_QP0;
+       wc->slid = slid;
+       wc->sl = 0;
+       wc->dlid_path_bits = 0;
+       wc->port_num = port_num;
+}
+
+/*
+ * Return 0 if SMP is to be sent
+ * Return 1 if SMP was consumed locally (whether or not solicited)
+ * Return < 0 if error
+ */
+static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
+                                 struct ib_smp *smp,
+                                 struct ib_send_wr *send_wr)
+{
+       int ret, alloc_flags, solicited;
+       unsigned long flags;
+       struct ib_mad_local_private *local;
+       struct ib_mad_private *mad_priv;
+       struct ib_mad_port_private *port_priv;
+       struct ib_mad_agent_private *recv_mad_agent = NULL;
+       struct ib_device *device = mad_agent_priv->agent.device;
+       u8 port_num = mad_agent_priv->agent.port_num;
+       struct ib_wc mad_wc;
+
+       if (!smi_handle_dr_smp_send(smp, device->node_type, port_num)) {
+               ret = -EINVAL;
+               printk(KERN_ERR PFX "Invalid directed route\n");
+               goto out;
+       }
+       /* Check to post send on QP or process locally */
+       ret = smi_check_local_dr_smp(smp, device, port_num);
+       if (!ret || !device->process_mad)
+               goto out;
+
+       if (in_atomic() || irqs_disabled())
+               alloc_flags = GFP_ATOMIC;
+       else
+               alloc_flags = GFP_KERNEL;
+       local = kmalloc(sizeof *local, alloc_flags);
+       if (!local) {
+               ret = -ENOMEM;
+               printk(KERN_ERR PFX "No memory for ib_mad_local_private\n");
+               goto out;
+       }
+       local->mad_priv = NULL;
+       local->recv_mad_agent = NULL;
+       mad_priv = kmem_cache_alloc(ib_mad_cache, alloc_flags);
+       if (!mad_priv) {
+               ret = -ENOMEM;
+               printk(KERN_ERR PFX "No memory for local response MAD\n");
+               kfree(local);
+               goto out;
+       }
+
+       build_smp_wc(send_wr->wr_id, smp->dr_slid, send_wr->wr.ud.pkey_index,
+                    send_wr->wr.ud.port_num, &mad_wc);
+
+       /* No GRH for DR SMP */
+       ret = device->process_mad(device, 0, port_num, &mad_wc, NULL,
+                                 (struct ib_mad *)smp,
+                                 (struct ib_mad *)&mad_priv->mad);
+       switch (ret)
+       {
+       case IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY:
+               /*
+                * See if response is solicited and
+                * there is a recv handler
+                */
+               if (solicited_mad(&mad_priv->mad.mad) &&
+                   mad_agent_priv->agent.recv_handler) {
+                       local->mad_priv = mad_priv;
+                       local->recv_mad_agent = mad_agent_priv;
+                       /*
+                        * Reference MAD agent until receive
+                        * side of local completion handled
+                        */
+                       atomic_inc(&mad_agent_priv->refcount);
+               } else
+                       kmem_cache_free(ib_mad_cache, mad_priv);
+               break;
+       case IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED:
+               kmem_cache_free(ib_mad_cache, mad_priv);
+               break;
+       case IB_MAD_RESULT_SUCCESS:
+               /* Treat like an incoming receive MAD */
+               solicited = solicited_mad(&mad_priv->mad.mad);
+               port_priv = ib_get_mad_port(mad_agent_priv->agent.device,
+                                           mad_agent_priv->agent.port_num);
+               if (port_priv) {
+                       mad_priv->mad.mad.mad_hdr.tid =
+                               ((struct ib_mad *)smp)->mad_hdr.tid;
+                       recv_mad_agent = find_mad_agent(port_priv,
+                                                      &mad_priv->mad.mad,
+                                                       solicited);
+               }
+               if (!port_priv || !recv_mad_agent) {
+                       kmem_cache_free(ib_mad_cache, mad_priv);
+                       kfree(local);
+                       ret = 0;
+                       goto out;
+               }
+               local->mad_priv = mad_priv;
+               local->recv_mad_agent = recv_mad_agent;
+               break;
+       default:
+               kmem_cache_free(ib_mad_cache, mad_priv);
+               kfree(local);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       local->send_wr = *send_wr;
+       local->send_wr.sg_list = local->sg_list;
+       memcpy(local->sg_list, send_wr->sg_list,
+              sizeof *send_wr->sg_list * send_wr->num_sge);
+       local->send_wr.next = NULL;
+       local->tid = send_wr->wr.ud.mad_hdr->tid;
+       local->wr_id = send_wr->wr_id;
+       /* Reference MAD agent until send side of local completion handled */
+       atomic_inc(&mad_agent_priv->refcount);
+       /* Queue local completion to local list */
+       spin_lock_irqsave(&mad_agent_priv->lock, flags);
+       list_add_tail(&local->completion_list, &mad_agent_priv->local_list);
+       spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+       queue_work(mad_agent_priv->qp_info->port_priv->wq,
+                 &mad_agent_priv->local_work);
+       ret = 1;
+out:
+       return ret;
+}
+
+static int ib_send_mad(struct ib_mad_agent_private *mad_agent_priv,
+                      struct ib_mad_send_wr_private *mad_send_wr)
+{
+       struct ib_mad_qp_info *qp_info;
+       struct ib_send_wr *bad_send_wr;
+       unsigned long flags;
+       int ret;
+
+       /* Replace user's WR ID with our own to find WR upon completion */
+       qp_info = mad_agent_priv->qp_info;
+       mad_send_wr->wr_id = mad_send_wr->send_wr.wr_id;
+       mad_send_wr->send_wr.wr_id = (unsigned long)&mad_send_wr->mad_list;
+       mad_send_wr->mad_list.mad_queue = &qp_info->send_queue;
+
+       spin_lock_irqsave(&qp_info->send_queue.lock, flags);
+       if (qp_info->send_queue.count++ < qp_info->send_queue.max_active) {
+               list_add_tail(&mad_send_wr->mad_list.list,
+                             &qp_info->send_queue.list);
+               spin_unlock_irqrestore(&qp_info->send_queue.lock, flags);
+               ret = ib_post_send(mad_agent_priv->agent.qp,
+                                  &mad_send_wr->send_wr, &bad_send_wr);
+               if (ret) {
+                       printk(KERN_ERR PFX "ib_post_send failed: %d\n", ret);
+                       dequeue_mad(&mad_send_wr->mad_list);
+               }
+       } else {
+               list_add_tail(&mad_send_wr->mad_list.list,
+                             &qp_info->overflow_list);
+               spin_unlock_irqrestore(&qp_info->send_queue.lock, flags);
+               ret = 0;
+       }
+       return ret;
+}
+
+/*
+ * ib_post_send_mad - Posts MAD(s) to the send queue of the QP associated
+ *  with the registered client
+ */
+int ib_post_send_mad(struct ib_mad_agent *mad_agent,
+                    struct ib_send_wr *send_wr,
+                    struct ib_send_wr **bad_send_wr)
+{
+       int ret = -EINVAL;
+       struct ib_mad_agent_private *mad_agent_priv;
+
+       /* Validate supplied parameters */
+       if (!bad_send_wr)
+               goto error1;
+
+       if (!mad_agent || !send_wr)
+               goto error2;
+
+       if (!mad_agent->send_handler)
+               goto error2;
+
+       mad_agent_priv = container_of(mad_agent,
+                                     struct ib_mad_agent_private,
+                                     agent);
+
+       /* Walk list of send WRs and post each on send list */
+       while (send_wr) {
+               unsigned long                   flags;
+               struct ib_send_wr               *next_send_wr;
+               struct ib_mad_send_wr_private   *mad_send_wr;
+               struct ib_smp                   *smp;
+
+               /* Validate more parameters */
+               if (send_wr->num_sge > IB_MAD_SEND_REQ_MAX_SG)
+                       goto error2;
+
+               if (send_wr->wr.ud.timeout_ms && !mad_agent->recv_handler)
+                       goto error2;
+
+               if (!send_wr->wr.ud.mad_hdr) {
+                       printk(KERN_ERR PFX "MAD header must be supplied "
+                              "in WR %p\n", send_wr);
+                       goto error2;
+               }
+
+               /*
+                * Save pointer to next work request to post in case the
+                * current one completes, and the user modifies the work
+                * request associated with the completion
+                */
+               next_send_wr = (struct ib_send_wr *)send_wr->next;
+
+               smp = (struct ib_smp *)send_wr->wr.ud.mad_hdr;
+               if (smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
+                       ret = handle_outgoing_dr_smp(mad_agent_priv, smp,
+                                                    send_wr);
+                       if (ret < 0)            /* error */
+                               goto error2;
+                       else if (ret == 1)      /* locally consumed */
+                               goto next;
+               }
+
+               /* Allocate MAD send WR tracking structure */
+               mad_send_wr = kmalloc(sizeof *mad_send_wr,
+                                     (in_atomic() || irqs_disabled()) ?
+                                     GFP_ATOMIC : GFP_KERNEL);
+               if (!mad_send_wr) {
+                       printk(KERN_ERR PFX "No memory for "
+                              "ib_mad_send_wr_private\n");
+                       ret = -ENOMEM;
+                       goto error2;
+               }
+
+               mad_send_wr->send_wr = *send_wr;
+               mad_send_wr->send_wr.sg_list = mad_send_wr->sg_list;
+               memcpy(mad_send_wr->sg_list, send_wr->sg_list,
+                      sizeof *send_wr->sg_list * send_wr->num_sge);
+               mad_send_wr->send_wr.next = NULL;
+               mad_send_wr->tid = send_wr->wr.ud.mad_hdr->tid;
+               mad_send_wr->agent = mad_agent;
+               /* Timeout will be updated after send completes */
+               mad_send_wr->timeout = msecs_to_jiffies(send_wr->wr.
+                                                       ud.timeout_ms);
+               mad_send_wr->retry = 0;
+               /* One reference for each work request to QP + response */
+               mad_send_wr->refcount = 1 + (mad_send_wr->timeout > 0);
+               mad_send_wr->status = IB_WC_SUCCESS;
+
+               /* Reference MAD agent until send completes */
+               atomic_inc(&mad_agent_priv->refcount);
+               spin_lock_irqsave(&mad_agent_priv->lock, flags);
+               list_add_tail(&mad_send_wr->agent_list,
+                             &mad_agent_priv->send_list);
+               spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+
+               ret = ib_send_mad(mad_agent_priv, mad_send_wr);
+               if (ret) {
+                       /* Fail send request */
+                       spin_lock_irqsave(&mad_agent_priv->lock, flags);
+                       list_del(&mad_send_wr->agent_list);
+                       spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+                       atomic_dec(&mad_agent_priv->refcount);
+                       goto error2;
+               }
+next:
+               send_wr = next_send_wr;
+       }
+       return 0;
+
+error2:
+       *bad_send_wr = send_wr;
+error1:
+       return ret;
+}
+EXPORT_SYMBOL(ib_post_send_mad);
+
+/*
+ * ib_free_recv_mad - Returns data buffers used to receive
+ *  a MAD to the access layer
+ */
+void ib_free_recv_mad(struct ib_mad_recv_wc *mad_recv_wc)
+{
+       struct ib_mad_recv_buf *entry;
+       struct ib_mad_private_header *mad_priv_hdr;
+       struct ib_mad_private *priv;
+
+       mad_priv_hdr = container_of(mad_recv_wc,
+                                   struct ib_mad_private_header,
+                                   recv_wc);
+       priv = container_of(mad_priv_hdr, struct ib_mad_private, header);
+
+       /*
+        * Walk receive buffer list associated with this WC
+        * No need to remove them from list of receive buffers
+        */
+       list_for_each_entry(entry, &mad_recv_wc->recv_buf.list, list) {
+               /* Free previous receive buffer */
+               kmem_cache_free(ib_mad_cache, priv);
+               mad_priv_hdr = container_of(mad_recv_wc,
+                                           struct ib_mad_private_header,
+                                           recv_wc);
+               priv = container_of(mad_priv_hdr, struct ib_mad_private,
+                                   header);
+       }
+
+       /* Free last buffer */
+       kmem_cache_free(ib_mad_cache, priv);
+}
+EXPORT_SYMBOL(ib_free_recv_mad);
+
+void ib_coalesce_recv_mad(struct ib_mad_recv_wc *mad_recv_wc,
+                         void *buf)
+{
+       printk(KERN_ERR PFX "ib_coalesce_recv_mad() not implemented yet\n");
+}
+EXPORT_SYMBOL(ib_coalesce_recv_mad);
+
+struct ib_mad_agent *ib_redirect_mad_qp(struct ib_qp *qp,
+                                       u8 rmpp_version,
+                                       ib_mad_send_handler send_handler,
+                                       ib_mad_recv_handler recv_handler,
+                                       void *context)
+{
+       return ERR_PTR(-EINVAL);        /* XXX: for now */
+}
+EXPORT_SYMBOL(ib_redirect_mad_qp);
+
+int ib_process_mad_wc(struct ib_mad_agent *mad_agent,
+                     struct ib_wc *wc)
+{
+       printk(KERN_ERR PFX "ib_process_mad_wc() not implemented yet\n");
+       return 0;
+}
+EXPORT_SYMBOL(ib_process_mad_wc);
+
+static int method_in_use(struct ib_mad_mgmt_method_table **method,
+                        struct ib_mad_reg_req *mad_reg_req)
+{
+       int i;
+
+       for (i = find_first_bit(mad_reg_req->method_mask, IB_MGMT_MAX_METHODS);
+            i < IB_MGMT_MAX_METHODS;
+            i = find_next_bit(mad_reg_req->method_mask, IB_MGMT_MAX_METHODS,
+                              1+i)) {
+               if ((*method)->agent[i]) {
+                       printk(KERN_ERR PFX "Method %d already in use\n", i);
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+static int allocate_method_table(struct ib_mad_mgmt_method_table **method)
+{
+       /* Allocate management method table */
+       *method = kmalloc(sizeof **method, GFP_ATOMIC);
+       if (!*method) {
+               printk(KERN_ERR PFX "No memory for "
+                      "ib_mad_mgmt_method_table\n");
+               return -ENOMEM;
+       }
+       /* Clear management method table */
+       memset(*method, 0, sizeof **method);
+
+       return 0;
+}
+
+/*
+ * Check to see if there are any methods still in use
+ */
+static int check_method_table(struct ib_mad_mgmt_method_table *method)
+{
+       int i;
+
+       for (i = 0; i < IB_MGMT_MAX_METHODS; i++)
+               if (method->agent[i])
+                       return 1;
+       return 0;
+}
+
+/*
+ * Check to see if there are any method tables for this class still in use
+ */
+static int check_class_table(struct ib_mad_mgmt_class_table *class)
+{
+       int i;
+
+       for (i = 0; i < MAX_MGMT_CLASS; i++)
+               if (class->method_table[i])
+                       return 1;
+       return 0;
+}
+
+static int check_vendor_class(struct ib_mad_mgmt_vendor_class *vendor_class)
+{
+       int i;
+
+       for (i = 0; i < MAX_MGMT_OUI; i++)
+               if (vendor_class->method_table[i])
+                       return 1;
+       return 0;
+}
+
+static int find_vendor_oui(struct ib_mad_mgmt_vendor_class *vendor_class,
+                          char *oui)
+{
+       int i;
+
+       for (i = 0; i < MAX_MGMT_OUI; i++)
+                /* Is there matching OUI for this vendor class ? */
+                if (!memcmp(vendor_class->oui[i], oui, 3))
+                       return i;
+
+       return -1;
+}
+
+static int check_vendor_table(struct ib_mad_mgmt_vendor_class_table *vendor)
+{
+       int i;
+
+       for (i = 0; i < MAX_MGMT_VENDOR_RANGE2; i++)
+               if (vendor->vendor_class[i])
+                       return 1;
+
+       return 0;
+}
+
+static void remove_methods_mad_agent(struct ib_mad_mgmt_method_table *method,
+                                    struct ib_mad_agent_private *agent)
+{
+       int i;
+
+       /* Remove any methods for this mad agent */
+       for (i = 0; i < IB_MGMT_MAX_METHODS; i++) {
+               if (method->agent[i] == agent) {
+                       method->agent[i] = NULL;
+               }
+       }
+}
+
+static int add_nonoui_reg_req(struct ib_mad_reg_req *mad_reg_req,
+                             struct ib_mad_agent_private *agent_priv,
+                             u8 mgmt_class)
+{
+       struct ib_mad_port_private *port_priv;
+       struct ib_mad_mgmt_class_table **class;
+       struct ib_mad_mgmt_method_table **method;
+       int i, ret;
+
+       port_priv = agent_priv->qp_info->port_priv;
+       class = &port_priv->version[mad_reg_req->mgmt_class_version].class;
+       if (!*class) {
+               /* Allocate management class table for "new" class version */
+               *class = kmalloc(sizeof **class, GFP_ATOMIC);
+               if (!*class) {
+                       printk(KERN_ERR PFX "No memory for "
+                              "ib_mad_mgmt_class_table\n");
+                       ret = -ENOMEM;
+                       goto error1;
+               }
+               /* Clear management class table */
+               memset(*class, 0, sizeof(**class));
+               /* Allocate method table for this management class */
+               method = &(*class)->method_table[mgmt_class];
+               if ((ret = allocate_method_table(method)))
+                       goto error2;
+       } else {
+               method = &(*class)->method_table[mgmt_class];
+               if (!*method) {
+                       /* Allocate method table for this management class */
+                       if ((ret = allocate_method_table(method)))
+                               goto error1;
+               }
+       }
+
+       /* Now, make sure methods are not already in use */
+       if (method_in_use(method, mad_reg_req))
+               goto error3;
+
+       /* Finally, add in methods being registered */
+       for (i = find_first_bit(mad_reg_req->method_mask,
+                               IB_MGMT_MAX_METHODS);
+            i < IB_MGMT_MAX_METHODS;
+            i = find_next_bit(mad_reg_req->method_mask, IB_MGMT_MAX_METHODS,
+                              1+i)) {
+               (*method)->agent[i] = agent_priv;
+       }
+       return 0;
+
+error3:
+       /* Remove any methods for this mad agent */
+       remove_methods_mad_agent(*method, agent_priv);
+       /* Now, check to see if there are any methods in use */
+       if (!check_method_table(*method)) {
+               /* If not, release management method table */
+               kfree(*method);
+               *method = NULL;
+       }
+       ret = -EINVAL;
+       goto error1;
+error2:
+       kfree(*class);
+       *class = NULL;
+error1:
+       return ret;
+}
+
+static int add_oui_reg_req(struct ib_mad_reg_req *mad_reg_req,
+                          struct ib_mad_agent_private *agent_priv)
+{
+       struct ib_mad_port_private *port_priv;
+       struct ib_mad_mgmt_vendor_class_table **vendor_table;
+       struct ib_mad_mgmt_vendor_class_table *vendor = NULL;
+       struct ib_mad_mgmt_vendor_class *vendor_class = NULL;
+       struct ib_mad_mgmt_method_table **method;
+       int i, ret = -ENOMEM;
+       u8 vclass;
+
+       /* "New" vendor (with OUI) class */
+       vclass = vendor_class_index(mad_reg_req->mgmt_class);
+       port_priv = agent_priv->qp_info->port_priv;
+       vendor_table = &port_priv->version[
+                               mad_reg_req->mgmt_class_version].vendor;
+       if (!*vendor_table) {
+               /* Allocate mgmt vendor class table for "new" class version */
+               vendor = kmalloc(sizeof *vendor, GFP_ATOMIC);
+               if (!vendor) {
+                       printk(KERN_ERR PFX "No memory for "
+                              "ib_mad_mgmt_vendor_class_table\n");
+                       goto error1;
+               }
+               /* Clear management vendor class table */
+               memset(vendor, 0, sizeof(*vendor));
+               *vendor_table = vendor;
+       }
+       if (!(*vendor_table)->vendor_class[vclass]) {
+               /* Allocate table for this management vendor class */
+               vendor_class = kmalloc(sizeof *vendor_class, GFP_ATOMIC);
+               if (!vendor_class) {
+                       printk(KERN_ERR PFX "No memory for "
+                              "ib_mad_mgmt_vendor_class\n");
+                       goto error2;
+               }
+               memset(vendor_class, 0, sizeof(*vendor_class));
+               (*vendor_table)->vendor_class[vclass] = vendor_class;
+       }
+       for (i = 0; i < MAX_MGMT_OUI; i++) {
+               /* Is there matching OUI for this vendor class ? */
+               if (!memcmp((*vendor_table)->vendor_class[vclass]->oui[i],
+                           mad_reg_req->oui, 3)) {
+                       method = &(*vendor_table)->vendor_class[
+                                               vclass]->method_table[i];
+                       BUG_ON(!*method);
+                       goto check_in_use;
+               }
+       }
+       for (i = 0; i < MAX_MGMT_OUI; i++) {
+               /* OUI slot available ? */
+               if (!is_vendor_oui((*vendor_table)->vendor_class[
+                               vclass]->oui[i])) {
+                       method = &(*vendor_table)->vendor_class[
+                               vclass]->method_table[i];
+                       BUG_ON(*method);
+                       /* Allocate method table for this OUI */
+                       if ((ret = allocate_method_table(method)))
+                               goto error3;
+                       memcpy((*vendor_table)->vendor_class[vclass]->oui[i],
+                              mad_reg_req->oui, 3);
+                       goto check_in_use;
+               }
+       }
+       printk(KERN_ERR PFX "All OUI slots in use\n");
+       goto error3;
+
+check_in_use:
+       /* Now, make sure methods are not already in use */
+       if (method_in_use(method, mad_reg_req))
+               goto error4;
+
+       /* Finally, add in methods being registered */
+       for (i = find_first_bit(mad_reg_req->method_mask,
+                               IB_MGMT_MAX_METHODS);
+            i < IB_MGMT_MAX_METHODS;
+            i = find_next_bit(mad_reg_req->method_mask, IB_MGMT_MAX_METHODS,
+                              1+i)) {
+               (*method)->agent[i] = agent_priv;
+       }
+       return 0;
+
+error4:
+       /* Remove any methods for this mad agent */
+       remove_methods_mad_agent(*method, agent_priv);
+       /* Now, check to see if there are any methods in use */
+       if (!check_method_table(*method)) {
+               /* If not, release management method table */
+               kfree(*method);
+               *method = NULL;
+       }
+       ret = -EINVAL;
+error3:
+       if (vendor_class) {
+               (*vendor_table)->vendor_class[vclass] = NULL;
+               kfree(vendor_class);
+       }
+error2:
+       if (vendor) {
+               *vendor_table = NULL;
+               kfree(vendor);
+       }
+error1:
+       return ret;
+}
+
+static void remove_mad_reg_req(struct ib_mad_agent_private *agent_priv)
+{
+       struct ib_mad_port_private *port_priv;
+       struct ib_mad_mgmt_class_table *class;
+       struct ib_mad_mgmt_method_table *method;
+       struct ib_mad_mgmt_vendor_class_table *vendor;
+       struct ib_mad_mgmt_vendor_class *vendor_class;
+       int index;
+       u8 mgmt_class;
+
+       /*
+        * Was MAD registration request supplied
+        * with original registration ?
+        */
+       if (!agent_priv->reg_req) {
+               goto out;
+       }
+
+       port_priv = agent_priv->qp_info->port_priv;
+       class = port_priv->version[
+                       agent_priv->reg_req->mgmt_class_version].class;
+       if (!class)
+               goto vendor_check;
+
+       mgmt_class = convert_mgmt_class(agent_priv->reg_req->mgmt_class);
+       method = class->method_table[mgmt_class];
+       if (method) {
+               /* Remove any methods for this mad agent */
+               remove_methods_mad_agent(method, agent_priv);
+               /* Now, check to see if there are any methods still in use */
+               if (!check_method_table(method)) {
+                       /* If not, release management method table */
+                        kfree(method);
+                        class->method_table[mgmt_class] = NULL;
+                        /* Any management classes left ? */
+                       if (!check_class_table(class)) {
+                               /* If not, release management class table */
+                               kfree(class);
+                               port_priv->version[
+                                       agent_priv->reg_req->
+                                       mgmt_class_version].class = NULL;
+                       }
+               }
+       }
+
+vendor_check:
+       vendor = port_priv->version[
+                       agent_priv->reg_req->mgmt_class_version].vendor;
+       if (!vendor)
+               goto out;
+
+       mgmt_class = vendor_class_index(agent_priv->reg_req->mgmt_class);
+       vendor_class = vendor->vendor_class[mgmt_class];
+       if (vendor_class) {
+               index = find_vendor_oui(vendor_class, agent_priv->reg_req->oui);
+               if (index == -1)
+                       goto out;
+               method = vendor_class->method_table[index];
+               if (method) {
+                       /* Remove any methods for this mad agent */
+                       remove_methods_mad_agent(method, agent_priv);
+                       /*
+                        * Now, check to see if there are
+                        * any methods still in use
+                        */
+                       if (!check_method_table(method)) {
+                               /* If not, release management method table */
+                               kfree(method);
+                               vendor_class->method_table[index] = NULL;
+                               memset(vendor_class->oui[index], 0, 3);
+                               /* Any OUIs left ? */
+                               if (!check_vendor_class(vendor_class)) {
+                                       /* If not, release vendor class table */
+                                       kfree(vendor_class);
+                                       vendor->vendor_class[mgmt_class] = NULL;
+                                       /* Any other vendor classes left ? */
+                                       if (!check_vendor_table(vendor)) {
+                                               kfree(vendor);
+                                               port_priv->version[
+                                                       agent_priv->reg_req->
+                                                       mgmt_class_version].
+                                                       vendor = NULL;
+                                       }
+                               }
+                       }
+               }
+       }
+
+out:
+       return;
+}
+
+static int response_mad(struct ib_mad *mad)
+{
+       /* Trap represses are responses although response bit is reset */
+       return ((mad->mad_hdr.method == IB_MGMT_METHOD_TRAP_REPRESS) ||
+               (mad->mad_hdr.method & IB_MGMT_METHOD_RESP));
+}
+
+static int solicited_mad(struct ib_mad *mad)
+{
+       /* CM MADs are never solicited */
+       if (mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_CM) {
+               return 0;
+       }
+
+       /* XXX: Determine whether MAD is using RMPP */
+
+       /* Not using RMPP */
+       /* Is this MAD a response to a previous MAD ? */
+       return response_mad(mad);
+}
+
+static struct ib_mad_agent_private *
+find_mad_agent(struct ib_mad_port_private *port_priv,
+              struct ib_mad *mad,
+              int solicited)
+{
+       struct ib_mad_agent_private *mad_agent = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&port_priv->reg_lock, flags);
+
+       /*
+        * Whether MAD was solicited determines type of routing to
+        * MAD client.
+        */
+       if (solicited) {
+               u32 hi_tid;
+               struct ib_mad_agent_private *entry;
+
+               /*
+                * Routing is based on high 32 bits of transaction ID
+                * of MAD.
+                */
+               hi_tid = be64_to_cpu(mad->mad_hdr.tid) >> 32;
+               list_for_each_entry(entry, &port_priv->agent_list,
+                                   agent_list) {
+                       if (entry->agent.hi_tid == hi_tid) {
+                               mad_agent = entry;
+                               break;
+                       }
+               }
+       } else {
+               struct ib_mad_mgmt_class_table *class;
+               struct ib_mad_mgmt_method_table *method;
+               struct ib_mad_mgmt_vendor_class_table *vendor;
+               struct ib_mad_mgmt_vendor_class *vendor_class;
+               struct ib_vendor_mad *vendor_mad;
+               int index;
+
+               /*
+                * Routing is based on version, class, and method
+                * For "newer" vendor MADs, also based on OUI
+                */
+               if (mad->mad_hdr.class_version >= MAX_MGMT_VERSION)
+                       goto out;
+               if (!is_vendor_class(mad->mad_hdr.mgmt_class)) {
+                       class = port_priv->version[
+                                       mad->mad_hdr.class_version].class;
+                       if (!class)
+                               goto out;
+                       method = class->method_table[convert_mgmt_class(
+                                                       mad->mad_hdr.mgmt_class)];
+                       if (method)
+                               mad_agent = method->agent[mad->mad_hdr.method &
+                                                         ~IB_MGMT_METHOD_RESP];
+               } else {
+                       vendor = port_priv->version[
+                                       mad->mad_hdr.class_version].vendor;
+                       if (!vendor)
+                               goto out;
+                       vendor_class = vendor->vendor_class[vendor_class_index(
+                                               mad->mad_hdr.mgmt_class)];
+                       if (!vendor_class)
+                               goto out;
+                       /* Find matching OUI */
+                       vendor_mad = (struct ib_vendor_mad *)mad;
+                       index = find_vendor_oui(vendor_class, vendor_mad->oui);
+                       if (index == -1)
+                               goto out;
+                       method = vendor_class->method_table[index];
+                       if (method) {
+                               mad_agent = method->agent[mad->mad_hdr.method &
+                                                         ~IB_MGMT_METHOD_RESP];
+                       }
+               }
+       }
+
+       if (mad_agent) {
+               if (mad_agent->agent.recv_handler)
+                       atomic_inc(&mad_agent->refcount);
+               else {
+                       printk(KERN_NOTICE PFX "No receive handler for client "
+                              "%p on port %d\n",
+                              &mad_agent->agent, port_priv->port_num);
+                       mad_agent = NULL;
+               }
+       }
+out:
+       spin_unlock_irqrestore(&port_priv->reg_lock, flags);
+
+       return mad_agent;
+}
+
+static int validate_mad(struct ib_mad *mad, u32 qp_num)
+{
+       int valid = 0;
+
+       /* Make sure MAD base version is understood */
+       if (mad->mad_hdr.base_version != IB_MGMT_BASE_VERSION) {
+               printk(KERN_ERR PFX "MAD received with unsupported base "
+                      "version %d\n", mad->mad_hdr.base_version);
+               goto out;
+       }
+
+       /* Filter SMI packets sent to other than QP0 */
+       if ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED) ||
+           (mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)) {
+               if (qp_num == 0)
+                       valid = 1;
+       } else {
+               /* Filter GSI packets sent to QP0 */
+               if (qp_num != 0)
+                       valid = 1;
+       }
+
+out:
+       return valid;
+}
+
+/*
+ * Return start of fully reassembled MAD, or NULL, if MAD isn't assembled yet
+ */
+static struct ib_mad_private *
+reassemble_recv(struct ib_mad_agent_private *mad_agent_priv,
+               struct ib_mad_private *recv)
+{
+       /* Until we have RMPP, all receives are reassembled!... */
+       INIT_LIST_HEAD(&recv->header.recv_wc.recv_buf.list);
+       return recv;
+}
+
+static struct ib_mad_send_wr_private*
+find_send_req(struct ib_mad_agent_private *mad_agent_priv,
+             u64 tid)
+{
+       struct ib_mad_send_wr_private *mad_send_wr;
+
+       list_for_each_entry(mad_send_wr, &mad_agent_priv->wait_list,
+                           agent_list) {
+               if (mad_send_wr->tid == tid)
+                       return mad_send_wr;
+       }
+
+       /*
+        * It's possible to receive the response before we've
+        * been notified that the send has completed
+        */
+       list_for_each_entry(mad_send_wr, &mad_agent_priv->send_list,
+                           agent_list) {
+               if (mad_send_wr->tid == tid && mad_send_wr->timeout) {
+                       /* Verify request has not been canceled */
+                       return (mad_send_wr->status == IB_WC_SUCCESS) ?
+                               mad_send_wr : NULL;
+               }
+       }
+       return NULL;
+}
+
+static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
+                                struct ib_mad_private *recv,
+                                int solicited)
+{
+       struct ib_mad_send_wr_private *mad_send_wr;
+       struct ib_mad_send_wc mad_send_wc;
+       unsigned long flags;
+
+       /* Fully reassemble receive before processing */
+       recv = reassemble_recv(mad_agent_priv, recv);
+       if (!recv) {
+               if (atomic_dec_and_test(&mad_agent_priv->refcount))
+                       wake_up(&mad_agent_priv->wait);
+               return;
+       }
+
+       /* Complete corresponding request */
+       if (solicited) {
+               spin_lock_irqsave(&mad_agent_priv->lock, flags);
+               mad_send_wr = find_send_req(mad_agent_priv,
+                                           recv->mad.mad.mad_hdr.tid);
+               if (!mad_send_wr) {
+                       spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+                       ib_free_recv_mad(&recv->header.recv_wc);
+                       if (atomic_dec_and_test(&mad_agent_priv->refcount))
+                               wake_up(&mad_agent_priv->wait);
+                       return;
+               }
+               /* Timeout = 0 means that we won't wait for a response */
+               mad_send_wr->timeout = 0;
+               spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+
+               /* Defined behavior is to complete response before request */
+               recv->header.recv_wc.wc->wr_id = mad_send_wr->wr_id;
+               mad_agent_priv->agent.recv_handler(
+                                               &mad_agent_priv->agent,
+                                               &recv->header.recv_wc);
+               atomic_dec(&mad_agent_priv->refcount);
+
+               mad_send_wc.status = IB_WC_SUCCESS;
+               mad_send_wc.vendor_err = 0;
+               mad_send_wc.wr_id = mad_send_wr->wr_id;
+               ib_mad_complete_send_wr(mad_send_wr, &mad_send_wc);
+       } else {
+               mad_agent_priv->agent.recv_handler(
+                                               &mad_agent_priv->agent,
+                                               &recv->header.recv_wc);
+               if (atomic_dec_and_test(&mad_agent_priv->refcount))
+                       wake_up(&mad_agent_priv->wait);
+       }
+}
+
+static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
+                                    struct ib_wc *wc)
+{
+       struct ib_mad_qp_info *qp_info;
+       struct ib_mad_private_header *mad_priv_hdr;
+       struct ib_mad_private *recv, *response;
+       struct ib_mad_list_head *mad_list;
+       struct ib_mad_agent_private *mad_agent;
+       int solicited;
+
+       response = kmem_cache_alloc(ib_mad_cache, GFP_KERNEL);
+       if (!response)
+               printk(KERN_ERR PFX "ib_mad_recv_done_handler no memory "
+                      "for response buffer\n");
+
+       mad_list = (struct ib_mad_list_head *)(unsigned long)wc->wr_id;
+       qp_info = mad_list->mad_queue->qp_info;
+       dequeue_mad(mad_list);
+
+       mad_priv_hdr = container_of(mad_list, struct ib_mad_private_header,
+                                   mad_list);
+       recv = container_of(mad_priv_hdr, struct ib_mad_private, header);
+       dma_unmap_single(port_priv->device->dma_device,
+                        pci_unmap_addr(&recv->header, mapping),
+                        sizeof(struct ib_mad_private) -
+                        sizeof(struct ib_mad_private_header),
+                        DMA_FROM_DEVICE);
+
+       /* Setup MAD receive work completion from "normal" work completion */
+       recv->header.recv_wc.wc = wc;
+       recv->header.recv_wc.mad_len = sizeof(struct ib_mad);
+       recv->header.recv_wc.recv_buf.mad = &recv->mad.mad;
+       recv->header.recv_wc.recv_buf.grh = &recv->grh;
+
+       if (atomic_read(&qp_info->snoop_count))
+               snoop_recv(qp_info, &recv->header.recv_wc, IB_MAD_SNOOP_RECVS);
+
+       /* Validate MAD */
+       if (!validate_mad(&recv->mad.mad, qp_info->qp->qp_num))
+               goto out;
+
+       if (recv->mad.mad.mad_hdr.mgmt_class ==
+           IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
+               if (!smi_handle_dr_smp_recv(&recv->mad.smp,
+                                           port_priv->device->node_type,
+                                           port_priv->port_num,
+                                           port_priv->device->phys_port_cnt))
+                       goto out;
+               if (!smi_check_forward_dr_smp(&recv->mad.smp))
+                       goto local;
+               if (!smi_handle_dr_smp_send(&recv->mad.smp,
+                                           port_priv->device->node_type,
+                                           port_priv->port_num))
+                       goto out;
+               if (!smi_check_local_dr_smp(&recv->mad.smp,
+                                           port_priv->device,
+                                           port_priv->port_num))
+                       goto out;
+       }
+
+local:
+       /* Give driver "right of first refusal" on incoming MAD */
+       if (port_priv->device->process_mad) {
+               int ret;
+
+               if (!response) {
+                       printk(KERN_ERR PFX "No memory for response MAD\n");
+                       /*
+                        * Is it better to assume that
+                        * it wouldn't be processed ?
+                        */
+                       goto out;
+               }
+
+               ret = port_priv->device->process_mad(port_priv->device, 0,
+                                                    port_priv->port_num,
+                                                    wc, &recv->grh,
+                                                    &recv->mad.mad,
+                                                    &response->mad.mad);
+               if (ret & IB_MAD_RESULT_SUCCESS) {
+                       if (ret & IB_MAD_RESULT_CONSUMED)
+                               goto out;
+                       if (ret & IB_MAD_RESULT_REPLY) {
+                               /* Send response */
+                               if (!agent_send(response, &recv->grh, wc,
+                                               port_priv->device,
+                                               port_priv->port_num))
+                                       response = NULL;
+                               goto out;
+                       }
+               }
+       }
+
+       /* Determine corresponding MAD agent for incoming receive MAD */
+       solicited = solicited_mad(&recv->mad.mad);
+       mad_agent = find_mad_agent(port_priv, &recv->mad.mad, solicited);
+       if (mad_agent) {
+               ib_mad_complete_recv(mad_agent, recv, solicited);
+               /*
+                * recv is freed up in error cases in ib_mad_complete_recv
+                * or via recv_handler in ib_mad_complete_recv()
+                */
+               recv = NULL;
+       }
+
+out:
+       /* Post another receive request for this QP */
+       if (response) {
+               ib_mad_post_receive_mads(qp_info, response);
+               if (recv)
+                       kmem_cache_free(ib_mad_cache, recv);
+       } else
+               ib_mad_post_receive_mads(qp_info, recv);
+}
+
+static void adjust_timeout(struct ib_mad_agent_private *mad_agent_priv)
+{
+       struct ib_mad_send_wr_private *mad_send_wr;
+       unsigned long delay;
+
+       if (list_empty(&mad_agent_priv->wait_list)) {
+               cancel_delayed_work(&mad_agent_priv->timed_work);
+       } else {
+               mad_send_wr = list_entry(mad_agent_priv->wait_list.next,
+                                        struct ib_mad_send_wr_private,
+                                        agent_list);
+
+               if (time_after(mad_agent_priv->timeout,
+                              mad_send_wr->timeout)) {
+                       mad_agent_priv->timeout = mad_send_wr->timeout;
+                       cancel_delayed_work(&mad_agent_priv->timed_work);
+                       delay = mad_send_wr->timeout - jiffies;
+                       if ((long)delay <= 0)
+                               delay = 1;
+                       queue_delayed_work(mad_agent_priv->qp_info->
+                                          port_priv->wq,
+                                          &mad_agent_priv->timed_work, delay);
+               }
+       }
+}
+
+static void wait_for_response(struct ib_mad_agent_private *mad_agent_priv,
+                             struct ib_mad_send_wr_private *mad_send_wr )
+{
+       struct ib_mad_send_wr_private *temp_mad_send_wr;
+       struct list_head *list_item;
+       unsigned long delay;
+
+       list_del(&mad_send_wr->agent_list);
+
+       delay = mad_send_wr->timeout;
+       mad_send_wr->timeout += jiffies;
+
+       list_for_each_prev(list_item, &mad_agent_priv->wait_list) {
+               temp_mad_send_wr = list_entry(list_item,
+                                             struct ib_mad_send_wr_private,
+                                             agent_list);
+               if (time_after(mad_send_wr->timeout,
+                              temp_mad_send_wr->timeout))
+                       break;
+       }
+       list_add(&mad_send_wr->agent_list, list_item);
+
+       /* Reschedule a work item if we have a shorter timeout */
+       if (mad_agent_priv->wait_list.next == &mad_send_wr->agent_list) {
+               cancel_delayed_work(&mad_agent_priv->timed_work);
+               queue_delayed_work(mad_agent_priv->qp_info->port_priv->wq,
+                                  &mad_agent_priv->timed_work, delay);
+       }
+}
+
+/*
+ * Process a send work completion
+ */
+static void ib_mad_complete_send_wr(struct ib_mad_send_wr_private *mad_send_wr,
+                                   struct ib_mad_send_wc *mad_send_wc)
+{
+       struct ib_mad_agent_private     *mad_agent_priv;
+       unsigned long                   flags;
+
+       mad_agent_priv = container_of(mad_send_wr->agent,
+                                     struct ib_mad_agent_private, agent);
+
+       spin_lock_irqsave(&mad_agent_priv->lock, flags);
+       if (mad_send_wc->status != IB_WC_SUCCESS &&
+           mad_send_wr->status == IB_WC_SUCCESS) {
+               mad_send_wr->status = mad_send_wc->status;
+               mad_send_wr->refcount -= (mad_send_wr->timeout > 0);
+       }
+
+       if (--mad_send_wr->refcount > 0) {
+               if (mad_send_wr->refcount == 1 && mad_send_wr->timeout &&
+                   mad_send_wr->status == IB_WC_SUCCESS) {
+                       wait_for_response(mad_agent_priv, mad_send_wr);
+               }
+               spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+               return;
+       }
+
+       /* Remove send from MAD agent and notify client of completion */
+       list_del(&mad_send_wr->agent_list);
+       adjust_timeout(mad_agent_priv);
+       spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+
+       if (mad_send_wr->status != IB_WC_SUCCESS )
+               mad_send_wc->status = mad_send_wr->status;
+       mad_agent_priv->agent.send_handler(&mad_agent_priv->agent,
+                                           mad_send_wc);
+
+       /* Release reference on agent taken when sending */
+       if (atomic_dec_and_test(&mad_agent_priv->refcount))
+               wake_up(&mad_agent_priv->wait);
+
+       kfree(mad_send_wr);
+}
+
+static void ib_mad_send_done_handler(struct ib_mad_port_private *port_priv,
+                                    struct ib_wc *wc)
+{
+       struct ib_mad_send_wr_private   *mad_send_wr, *queued_send_wr;
+       struct ib_mad_list_head         *mad_list;
+       struct ib_mad_qp_info           *qp_info;
+       struct ib_mad_queue             *send_queue;
+       struct ib_send_wr               *bad_send_wr;
+       unsigned long flags;
+       int ret;
+
+       mad_list = (struct ib_mad_list_head *)(unsigned long)wc->wr_id;
+       mad_send_wr = container_of(mad_list, struct ib_mad_send_wr_private,
+                                  mad_list);
+       send_queue = mad_list->mad_queue;
+       qp_info = send_queue->qp_info;
+
+retry:
+       queued_send_wr = NULL;
+       spin_lock_irqsave(&send_queue->lock, flags);
+       list_del(&mad_list->list);
+
+       /* Move queued send to the send queue */
+       if (send_queue->count-- > send_queue->max_active) {
+               mad_list = container_of(qp_info->overflow_list.next,
+                                       struct ib_mad_list_head, list);
+               queued_send_wr = container_of(mad_list,
+                                       struct ib_mad_send_wr_private,
+                                       mad_list);
+               list_del(&mad_list->list);
+               list_add_tail(&mad_list->list, &send_queue->list);
+       }
+       spin_unlock_irqrestore(&send_queue->lock, flags);
+
+       /* Restore client wr_id in WC and complete send */
+       wc->wr_id = mad_send_wr->wr_id;
+       if (atomic_read(&qp_info->snoop_count))
+               snoop_send(qp_info, &mad_send_wr->send_wr,
+                          (struct ib_mad_send_wc *)wc,
+                          IB_MAD_SNOOP_SEND_COMPLETIONS);
+       ib_mad_complete_send_wr(mad_send_wr, (struct ib_mad_send_wc *)wc);
+
+       if (queued_send_wr) {
+               ret = ib_post_send(qp_info->qp, &queued_send_wr->send_wr,
+                               &bad_send_wr);
+               if (ret) {
+                       printk(KERN_ERR PFX "ib_post_send failed: %d\n", ret);
+                       mad_send_wr = queued_send_wr;
+                       wc->status = IB_WC_LOC_QP_OP_ERR;
+                       goto retry;
+               }
+       }
+}
+
+static void mark_sends_for_retry(struct ib_mad_qp_info *qp_info)
+{
+       struct ib_mad_send_wr_private *mad_send_wr;
+       struct ib_mad_list_head *mad_list;
+       unsigned long flags;
+
+       spin_lock_irqsave(&qp_info->send_queue.lock, flags);
+       list_for_each_entry(mad_list, &qp_info->send_queue.list, list) {
+               mad_send_wr = container_of(mad_list,
+                                          struct ib_mad_send_wr_private,
+                                          mad_list);
+               mad_send_wr->retry = 1;
+       }
+       spin_unlock_irqrestore(&qp_info->send_queue.lock, flags);
+}
+
+static void mad_error_handler(struct ib_mad_port_private *port_priv,
+                             struct ib_wc *wc)
+{
+       struct ib_mad_list_head *mad_list;
+       struct ib_mad_qp_info *qp_info;
+       struct ib_mad_send_wr_private *mad_send_wr;
+       int ret;
+
+       /* Determine if failure was a send or receive */
+       mad_list = (struct ib_mad_list_head *)(unsigned long)wc->wr_id;
+       qp_info = mad_list->mad_queue->qp_info;
+       if (mad_list->mad_queue == &qp_info->recv_queue)
+               /*
+                * Receive errors indicate that the QP has entered the error
+                * state - error handling/shutdown code will cleanup
+                */
+               return;
+
+       /*
+        * Send errors will transition the QP to SQE - move
+        * QP to RTS and repost flushed work requests
+        */
+       mad_send_wr = container_of(mad_list, struct ib_mad_send_wr_private,
+                                  mad_list);
+       if (wc->status == IB_WC_WR_FLUSH_ERR) {
+               if (mad_send_wr->retry) {
+                       /* Repost send */
+                       struct ib_send_wr *bad_send_wr;
+
+                       mad_send_wr->retry = 0;
+                       ret = ib_post_send(qp_info->qp, &mad_send_wr->send_wr,
+                                       &bad_send_wr);
+                       if (ret)
+                               ib_mad_send_done_handler(port_priv, wc);
+               } else
+                       ib_mad_send_done_handler(port_priv, wc);
+       } else {
+               struct ib_qp_attr *attr;
+
+               /* Transition QP to RTS and fail offending send */
+               attr = kmalloc(sizeof *attr, GFP_KERNEL);
+               if (attr) {
+                       attr->qp_state = IB_QPS_RTS;
+                       attr->cur_qp_state = IB_QPS_SQE;
+                       ret = ib_modify_qp(qp_info->qp, attr,
+                                          IB_QP_STATE | IB_QP_CUR_STATE);
+                       kfree(attr);
+                       if (ret)
+                               printk(KERN_ERR PFX "mad_error_handler - "
+                                      "ib_modify_qp to RTS : %d\n", ret);
+                       else
+                               mark_sends_for_retry(qp_info);
+               }
+               ib_mad_send_done_handler(port_priv, wc);
+       }
+}
+
+/*
+ * IB MAD completion callback
+ */
+static void ib_mad_completion_handler(void *data)
+{
+       struct ib_mad_port_private *port_priv;
+       struct ib_wc wc;
+
+       port_priv = (struct ib_mad_port_private *)data;
+       ib_req_notify_cq(port_priv->cq, IB_CQ_NEXT_COMP);
+
+       while (ib_poll_cq(port_priv->cq, 1, &wc) == 1) {
+               if (wc.status == IB_WC_SUCCESS) {
+                       switch (wc.opcode) {
+                       case IB_WC_SEND:
+                               ib_mad_send_done_handler(port_priv, &wc);
+                               break;
+                       case IB_WC_RECV:
+                               ib_mad_recv_done_handler(port_priv, &wc);
+                               break;
+                       default:
+                               BUG_ON(1);
+                               break;
+                       }
+               } else
+                       mad_error_handler(port_priv, &wc);
+       }
+}
+
+static void cancel_mads(struct ib_mad_agent_private *mad_agent_priv)
+{
+       unsigned long flags;
+       struct ib_mad_send_wr_private *mad_send_wr, *temp_mad_send_wr;
+       struct ib_mad_send_wc mad_send_wc;
+       struct list_head cancel_list;
+
+       INIT_LIST_HEAD(&cancel_list);
+
+       spin_lock_irqsave(&mad_agent_priv->lock, flags);
+       list_for_each_entry_safe(mad_send_wr, temp_mad_send_wr,
+                                &mad_agent_priv->send_list, agent_list) {
+               if (mad_send_wr->status == IB_WC_SUCCESS) {
+                       mad_send_wr->status = IB_WC_WR_FLUSH_ERR;
+                       mad_send_wr->refcount -= (mad_send_wr->timeout > 0);
+               }
+       }
+
+       /* Empty wait list to prevent receives from finding a request */
+       list_splice_init(&mad_agent_priv->wait_list, &cancel_list);
+       spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+
+       /* Report all cancelled requests */
+       mad_send_wc.status = IB_WC_WR_FLUSH_ERR;
+       mad_send_wc.vendor_err = 0;
+
+       list_for_each_entry_safe(mad_send_wr, temp_mad_send_wr,
+                                &cancel_list, agent_list) {
+               mad_send_wc.wr_id = mad_send_wr->wr_id;
+               mad_agent_priv->agent.send_handler(&mad_agent_priv->agent,
+                                                  &mad_send_wc);
+
+               list_del(&mad_send_wr->agent_list);
+               kfree(mad_send_wr);
+               atomic_dec(&mad_agent_priv->refcount);
+       }
+}
+
+static struct ib_mad_send_wr_private*
+find_send_by_wr_id(struct ib_mad_agent_private *mad_agent_priv,
+                  u64 wr_id)
+{
+       struct ib_mad_send_wr_private *mad_send_wr;
+
+       list_for_each_entry(mad_send_wr, &mad_agent_priv->wait_list,
+                           agent_list) {
+               if (mad_send_wr->wr_id == wr_id)
+                       return mad_send_wr;
+       }
+
+       list_for_each_entry(mad_send_wr, &mad_agent_priv->send_list,
+                           agent_list) {
+               if (mad_send_wr->wr_id == wr_id)
+                       return mad_send_wr;
+       }
+       return NULL;
+}
+
+void ib_cancel_mad(struct ib_mad_agent *mad_agent,
+                 u64 wr_id)
+{
+       struct ib_mad_agent_private *mad_agent_priv;
+       struct ib_mad_send_wr_private *mad_send_wr;
+       struct ib_mad_send_wc mad_send_wc;
+       unsigned long flags;
+
+       mad_agent_priv = container_of(mad_agent, struct ib_mad_agent_private,
+                                     agent);
+       spin_lock_irqsave(&mad_agent_priv->lock, flags);
+       mad_send_wr = find_send_by_wr_id(mad_agent_priv, wr_id);
+       if (!mad_send_wr) {
+               spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+               goto out;
+       }
+
+       if (mad_send_wr->status == IB_WC_SUCCESS)
+               mad_send_wr->refcount -= (mad_send_wr->timeout > 0);
+
+       if (mad_send_wr->refcount != 0) {
+               mad_send_wr->status = IB_WC_WR_FLUSH_ERR;
+               spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+               goto out;
+       }
+
+       list_del(&mad_send_wr->agent_list);
+       adjust_timeout(mad_agent_priv);
+       spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+
+       mad_send_wc.status = IB_WC_WR_FLUSH_ERR;
+       mad_send_wc.vendor_err = 0;
+       mad_send_wc.wr_id = mad_send_wr->wr_id;
+       mad_agent_priv->agent.send_handler(&mad_agent_priv->agent,
+                                          &mad_send_wc);
+
+       kfree(mad_send_wr);
+       if (atomic_dec_and_test(&mad_agent_priv->refcount))
+               wake_up(&mad_agent_priv->wait);
+
+out:
+       return;
+}
+EXPORT_SYMBOL(ib_cancel_mad);
+
+static void local_completions(void *data)
+{
+       struct ib_mad_agent_private *mad_agent_priv;
+       struct ib_mad_local_private *local;
+       struct ib_mad_agent_private *recv_mad_agent;
+       unsigned long flags;
+       struct ib_wc wc;
+       struct ib_mad_send_wc mad_send_wc;
+
+       mad_agent_priv = (struct ib_mad_agent_private *)data;
+
+       spin_lock_irqsave(&mad_agent_priv->lock, flags);
+       while (!list_empty(&mad_agent_priv->local_list)) {
+               local = list_entry(mad_agent_priv->local_list.next,
+                                  struct ib_mad_local_private,
+                                  completion_list);
+               spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+               if (local->mad_priv) {
+                       recv_mad_agent = local->recv_mad_agent;
+                       if (!recv_mad_agent) {
+                               printk(KERN_ERR PFX "No receive MAD agent for local completion\n");
+                               kmem_cache_free(ib_mad_cache, local->mad_priv);
+                               goto local_send_completion;
+                       }
+
+                       /*
+                        * Defined behavior is to complete response
+                        * before request
+                        */
+                       build_smp_wc(local->wr_id, IB_LID_PERMISSIVE,
+                                    0 /* pkey index */,
+                                    recv_mad_agent->agent.port_num, &wc);
+
+                       local->mad_priv->header.recv_wc.wc = &wc;
+                       local->mad_priv->header.recv_wc.mad_len =
+                                               sizeof(struct ib_mad);
+                       INIT_LIST_HEAD(&local->mad_priv->header.recv_wc.recv_buf.list);
+                       local->mad_priv->header.recv_wc.recv_buf.grh = NULL;
+                       local->mad_priv->header.recv_wc.recv_buf.mad =
+                                               &local->mad_priv->mad.mad;
+                       if (atomic_read(&recv_mad_agent->qp_info->snoop_count))
+                               snoop_recv(recv_mad_agent->qp_info,
+                                         &local->mad_priv->header.recv_wc,
+                                          IB_MAD_SNOOP_RECVS);
+                       recv_mad_agent->agent.recv_handler(
+                                               &recv_mad_agent->agent,
+                                               &local->mad_priv->header.recv_wc);
+                       spin_lock_irqsave(&recv_mad_agent->lock, flags);
+                       atomic_dec(&recv_mad_agent->refcount);
+                       spin_unlock_irqrestore(&recv_mad_agent->lock, flags);
+               }
+
+local_send_completion:
+               /* Complete send */
+               mad_send_wc.status = IB_WC_SUCCESS;
+               mad_send_wc.vendor_err = 0;
+               mad_send_wc.wr_id = local->wr_id;
+               if (atomic_read(&mad_agent_priv->qp_info->snoop_count))
+                       snoop_send(mad_agent_priv->qp_info, &local->send_wr,
+                                 &mad_send_wc,
+                                  IB_MAD_SNOOP_SEND_COMPLETIONS);
+               mad_agent_priv->agent.send_handler(&mad_agent_priv->agent,
+                                                  &mad_send_wc);
+
+               spin_lock_irqsave(&mad_agent_priv->lock, flags);
+               list_del(&local->completion_list);
+               atomic_dec(&mad_agent_priv->refcount);
+               kfree(local);
+       }
+       spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+}
+
+static void timeout_sends(void *data)
+{
+       struct ib_mad_agent_private *mad_agent_priv;
+       struct ib_mad_send_wr_private *mad_send_wr;
+       struct ib_mad_send_wc mad_send_wc;
+       unsigned long flags, delay;
+
+       mad_agent_priv = (struct ib_mad_agent_private *)data;
+
+       mad_send_wc.status = IB_WC_RESP_TIMEOUT_ERR;
+       mad_send_wc.vendor_err = 0;
+
+       spin_lock_irqsave(&mad_agent_priv->lock, flags);
+       while (!list_empty(&mad_agent_priv->wait_list)) {
+               mad_send_wr = list_entry(mad_agent_priv->wait_list.next,
+                                        struct ib_mad_send_wr_private,
+                                        agent_list);
+
+               if (time_after(mad_send_wr->timeout, jiffies)) {
+                       delay = mad_send_wr->timeout - jiffies;
+                       if ((long)delay <= 0)
+                               delay = 1;
+                       queue_delayed_work(mad_agent_priv->qp_info->
+                                          port_priv->wq,
+                                          &mad_agent_priv->timed_work, delay);
+                       break;
+               }
+
+               list_del(&mad_send_wr->agent_list);
+               spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+
+               mad_send_wc.wr_id = mad_send_wr->wr_id;
+               mad_agent_priv->agent.send_handler(&mad_agent_priv->agent,
+                                                  &mad_send_wc);
+
+               kfree(mad_send_wr);
+               atomic_dec(&mad_agent_priv->refcount);
+               spin_lock_irqsave(&mad_agent_priv->lock, flags);
+       }
+       spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
+}
+
+static void ib_mad_thread_completion_handler(struct ib_cq *cq)
+{
+       struct ib_mad_port_private *port_priv = cq->cq_context;
+
+       queue_work(port_priv->wq, &port_priv->work);
+}
+
+/*
+ * Allocate receive MADs and post receive WRs for them
+ */
+static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
+                                   struct ib_mad_private *mad)
+{
+       unsigned long flags;
+       int post, ret;
+       struct ib_mad_private *mad_priv;
+       struct ib_sge sg_list;
+       struct ib_recv_wr recv_wr, *bad_recv_wr;
+       struct ib_mad_queue *recv_queue = &qp_info->recv_queue;
+
+       /* Initialize common scatter list fields */
+       sg_list.length = sizeof *mad_priv - sizeof mad_priv->header;
+       sg_list.lkey = (*qp_info->port_priv->mr).lkey;
+
+       /* Initialize common receive WR fields */
+       recv_wr.next = NULL;
+       recv_wr.sg_list = &sg_list;
+       recv_wr.num_sge = 1;
+       recv_wr.recv_flags = IB_RECV_SIGNALED;
+
+       do {
+               /* Allocate and map receive buffer */
+               if (mad) {
+                       mad_priv = mad;
+                       mad = NULL;
+               } else {
+                       mad_priv = kmem_cache_alloc(ib_mad_cache, GFP_KERNEL);
+                       if (!mad_priv) {
+                               printk(KERN_ERR PFX "No memory for receive buffer\n");
+                               ret = -ENOMEM;
+                               break;
+                       }
+               }
+               sg_list.addr = dma_map_single(qp_info->port_priv->
+                                               device->dma_device,
+                                       &mad_priv->grh,
+                                       sizeof *mad_priv -
+                                               sizeof mad_priv->header,
+                                       DMA_FROM_DEVICE);
+               pci_unmap_addr_set(&mad_priv->header, mapping, sg_list.addr);
+               recv_wr.wr_id = (unsigned long)&mad_priv->header.mad_list;
+               mad_priv->header.mad_list.mad_queue = recv_queue;
+
+               /* Post receive WR */
+               spin_lock_irqsave(&recv_queue->lock, flags);
+               post = (++recv_queue->count < recv_queue->max_active);
+               list_add_tail(&mad_priv->header.mad_list.list, &recv_queue->list);
+               spin_unlock_irqrestore(&recv_queue->lock, flags);
+               ret = ib_post_recv(qp_info->qp, &recv_wr, &bad_recv_wr);
+               if (ret) {
+                       spin_lock_irqsave(&recv_queue->lock, flags);
+                       list_del(&mad_priv->header.mad_list.list);
+                       recv_queue->count--;
+                       spin_unlock_irqrestore(&recv_queue->lock, flags);
+                       dma_unmap_single(qp_info->port_priv->device->dma_device,
+                                        pci_unmap_addr(&mad_priv->header,
+                                                       mapping),
+                                        sizeof *mad_priv -
+                                          sizeof mad_priv->header,
+                                        DMA_FROM_DEVICE);
+                       kmem_cache_free(ib_mad_cache, mad_priv);
+                       printk(KERN_ERR PFX "ib_post_recv failed: %d\n", ret);
+                       break;
+               }
+       } while (post);
+
+       return ret;
+}
+
+/*
+ * Return all the posted receive MADs
+ */
+static void cleanup_recv_queue(struct ib_mad_qp_info *qp_info)
+{
+       struct ib_mad_private_header *mad_priv_hdr;
+       struct ib_mad_private *recv;
+       struct ib_mad_list_head *mad_list;
+
+       while (!list_empty(&qp_info->recv_queue.list)) {
+
+               mad_list = list_entry(qp_info->recv_queue.list.next,
+                                     struct ib_mad_list_head, list);
+               mad_priv_hdr = container_of(mad_list,
+                                           struct ib_mad_private_header,
+                                           mad_list);
+               recv = container_of(mad_priv_hdr, struct ib_mad_private,
+                                   header);
+
+               /* Remove from posted receive MAD list */
+               list_del(&mad_list->list);
+
+               /* Undo PCI mapping */
+               dma_unmap_single(qp_info->port_priv->device->dma_device,
+                                pci_unmap_addr(&recv->header, mapping),
+                                sizeof(struct ib_mad_private) -
+                                sizeof(struct ib_mad_private_header),
+                                DMA_FROM_DEVICE);
+               kmem_cache_free(ib_mad_cache, recv);
+       }
+
+       qp_info->recv_queue.count = 0;
+}
+
+/*
+ * Start the port
+ */
+static int ib_mad_port_start(struct ib_mad_port_private *port_priv)
+{
+       int ret, i;
+       struct ib_qp_attr *attr;
+       struct ib_qp *qp;
+
+       attr = kmalloc(sizeof *attr, GFP_KERNEL);
+       if (!attr) {
+               printk(KERN_ERR PFX "Couldn't kmalloc ib_qp_attr\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < IB_MAD_QPS_CORE; i++) {
+               qp = port_priv->qp_info[i].qp;
+               /*
+                * PKey index for QP1 is irrelevant but
+                * one is needed for the Reset to Init transition
+                */
+               attr->qp_state = IB_QPS_INIT;
+               attr->pkey_index = 0;
+               attr->qkey = (qp->qp_num == 0) ? 0 : IB_QP1_QKEY;
+               ret = ib_modify_qp(qp, attr, IB_QP_STATE |
+                                            IB_QP_PKEY_INDEX | IB_QP_QKEY);
+               if (ret) {
+                       printk(KERN_ERR PFX "Couldn't change QP%d state to "
+                              "INIT: %d\n", i, ret);
+                       goto out;
+               }
+
+               attr->qp_state = IB_QPS_RTR;
+               ret = ib_modify_qp(qp, attr, IB_QP_STATE);
+               if (ret) {
+                       printk(KERN_ERR PFX "Couldn't change QP%d state to "
+                              "RTR: %d\n", i, ret);
+                       goto out;
+               }
+
+               attr->qp_state = IB_QPS_RTS;
+               attr->sq_psn = IB_MAD_SEND_Q_PSN;
+               ret = ib_modify_qp(qp, attr, IB_QP_STATE | IB_QP_SQ_PSN);
+               if (ret) {
+                       printk(KERN_ERR PFX "Couldn't change QP%d state to "
+                              "RTS: %d\n", i, ret);
+                       goto out;
+               }
+       }
+
+       ret = ib_req_notify_cq(port_priv->cq, IB_CQ_NEXT_COMP);
+       if (ret) {
+               printk(KERN_ERR PFX "Failed to request completion "
+                      "notification: %d\n", ret);
+               goto out;
+       }
+
+       for (i = 0; i < IB_MAD_QPS_CORE; i++) {
+               ret = ib_mad_post_receive_mads(&port_priv->qp_info[i], NULL);
+               if (ret) {
+                       printk(KERN_ERR PFX "Couldn't post receive WRs\n");
+                       goto out;
+               }
+       }
+out:
+       kfree(attr);
+       return ret;
+}
+
+static void qp_event_handler(struct ib_event *event, void *qp_context)
+{
+       struct ib_mad_qp_info   *qp_info = qp_context;
+
+       /* It's worse than that! He's dead, Jim! */
+       printk(KERN_ERR PFX "Fatal error (%d) on MAD QP (%d)\n",
+               event->event, qp_info->qp->qp_num);
+}
+
+static void init_mad_queue(struct ib_mad_qp_info *qp_info,
+                          struct ib_mad_queue *mad_queue)
+{
+       mad_queue->qp_info = qp_info;
+       mad_queue->count = 0;
+       spin_lock_init(&mad_queue->lock);
+       INIT_LIST_HEAD(&mad_queue->list);
+}
+
+static void init_mad_qp(struct ib_mad_port_private *port_priv,
+                       struct ib_mad_qp_info *qp_info)
+{
+       qp_info->port_priv = port_priv;
+       init_mad_queue(qp_info, &qp_info->send_queue);
+       init_mad_queue(qp_info, &qp_info->recv_queue);
+       INIT_LIST_HEAD(&qp_info->overflow_list);
+       spin_lock_init(&qp_info->snoop_lock);
+       qp_info->snoop_table = NULL;
+       qp_info->snoop_table_size = 0;
+       atomic_set(&qp_info->snoop_count, 0);
+}
+
+static int create_mad_qp(struct ib_mad_qp_info *qp_info,
+                        enum ib_qp_type qp_type)
+{
+       struct ib_qp_init_attr  qp_init_attr;
+       int ret;
+
+       memset(&qp_init_attr, 0, sizeof qp_init_attr);
+       qp_init_attr.send_cq = qp_info->port_priv->cq;
+       qp_init_attr.recv_cq = qp_info->port_priv->cq;
+       qp_init_attr.sq_sig_type = IB_SIGNAL_ALL_WR;
+       qp_init_attr.rq_sig_type = IB_SIGNAL_ALL_WR;
+       qp_init_attr.cap.max_send_wr = IB_MAD_QP_SEND_SIZE;
+       qp_init_attr.cap.max_recv_wr = IB_MAD_QP_RECV_SIZE;
+       qp_init_attr.cap.max_send_sge = IB_MAD_SEND_REQ_MAX_SG;
+       qp_init_attr.cap.max_recv_sge = IB_MAD_RECV_REQ_MAX_SG;
+       qp_init_attr.qp_type = qp_type;
+       qp_init_attr.port_num = qp_info->port_priv->port_num;
+       qp_init_attr.qp_context = qp_info;
+       qp_init_attr.event_handler = qp_event_handler;
+       qp_info->qp = ib_create_qp(qp_info->port_priv->pd, &qp_init_attr);
+       if (IS_ERR(qp_info->qp)) {
+               printk(KERN_ERR PFX "Couldn't create ib_mad QP%d\n",
+                      get_spl_qp_index(qp_type));
+               ret = PTR_ERR(qp_info->qp);
+               goto error;
+       }
+       /* Use minimum queue sizes unless the CQ is resized */
+       qp_info->send_queue.max_active = IB_MAD_QP_SEND_SIZE;
+       qp_info->recv_queue.max_active = IB_MAD_QP_RECV_SIZE;
+       return 0;
+
+error:
+       return ret;
+}
+
+static void destroy_mad_qp(struct ib_mad_qp_info *qp_info)
+{
+       ib_destroy_qp(qp_info->qp);
+       if (qp_info->snoop_table)
+               kfree(qp_info->snoop_table);
+}
+
+/*
+ * Open the port
+ * Create the QP, PD, MR, and CQ if needed
+ */
+static int ib_mad_port_open(struct ib_device *device,
+                           int port_num)
+{
+       int ret, cq_size;
+       struct ib_mad_port_private *port_priv;
+       unsigned long flags;
+       char name[sizeof "ib_mad123"];
+
+       /* First, check if port already open at MAD layer */
+       port_priv = ib_get_mad_port(device, port_num);
+       if (port_priv) {
+               printk(KERN_DEBUG PFX "%s port %d already open\n",
+                      device->name, port_num);
+               return 0;
+       }
+
+       /* Create new device info */
+       port_priv = kmalloc(sizeof *port_priv, GFP_KERNEL);
+       if (!port_priv) {
+               printk(KERN_ERR PFX "No memory for ib_mad_port_private\n");
+               return -ENOMEM;
+       }
+       memset(port_priv, 0, sizeof *port_priv);
+       port_priv->device = device;
+       port_priv->port_num = port_num;
+       spin_lock_init(&port_priv->reg_lock);
+       INIT_LIST_HEAD(&port_priv->agent_list);
+       init_mad_qp(port_priv, &port_priv->qp_info[0]);
+       init_mad_qp(port_priv, &port_priv->qp_info[1]);
+
+       cq_size = (IB_MAD_QP_SEND_SIZE + IB_MAD_QP_RECV_SIZE) * 2;
+       port_priv->cq = ib_create_cq(port_priv->device,
+                                    (ib_comp_handler)
+                                       ib_mad_thread_completion_handler,
+                                    NULL, port_priv, cq_size);
+       if (IS_ERR(port_priv->cq)) {
+               printk(KERN_ERR PFX "Couldn't create ib_mad CQ\n");
+               ret = PTR_ERR(port_priv->cq);
+               goto error3;
+       }
+
+       port_priv->pd = ib_alloc_pd(device);
+       if (IS_ERR(port_priv->pd)) {
+               printk(KERN_ERR PFX "Couldn't create ib_mad PD\n");
+               ret = PTR_ERR(port_priv->pd);
+               goto error4;
+       }
+
+       port_priv->mr = ib_get_dma_mr(port_priv->pd, IB_ACCESS_LOCAL_WRITE);
+       if (IS_ERR(port_priv->mr)) {
+               printk(KERN_ERR PFX "Couldn't get ib_mad DMA MR\n");
+               ret = PTR_ERR(port_priv->mr);
+               goto error5;
+       }
+
+       ret = create_mad_qp(&port_priv->qp_info[0], IB_QPT_SMI);
+       if (ret)
+               goto error6;
+       ret = create_mad_qp(&port_priv->qp_info[1], IB_QPT_GSI);
+       if (ret)
+               goto error7;
+
+       snprintf(name, sizeof name, "ib_mad%d", port_num);
+       port_priv->wq = create_singlethread_workqueue(name);
+       if (!port_priv->wq) {
+               ret = -ENOMEM;
+               goto error8;
+       }
+       INIT_WORK(&port_priv->work, ib_mad_completion_handler, port_priv);
+
+       ret = ib_mad_port_start(port_priv);
+       if (ret) {
+               printk(KERN_ERR PFX "Couldn't start port\n");
+               goto error9;
+       }
+
+       spin_lock_irqsave(&ib_mad_port_list_lock, flags);
+       list_add_tail(&port_priv->port_list, &ib_mad_port_list);
+       spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
+       return 0;
+
+error9:
+       destroy_workqueue(port_priv->wq);
+error8:
+       destroy_mad_qp(&port_priv->qp_info[1]);
+error7:
+       destroy_mad_qp(&port_priv->qp_info[0]);
+error6:
+       ib_dereg_mr(port_priv->mr);
+error5:
+       ib_dealloc_pd(port_priv->pd);
+error4:
+       ib_destroy_cq(port_priv->cq);
+       cleanup_recv_queue(&port_priv->qp_info[1]);
+       cleanup_recv_queue(&port_priv->qp_info[0]);
+error3:
+       kfree(port_priv);
+
+       return ret;
+}
+
+/*
+ * Close the port
+ * If there are no classes using the port, free the port
+ * resources (CQ, MR, PD, QP) and remove the port's info structure
+ */
+static int ib_mad_port_close(struct ib_device *device, int port_num)
+{
+       struct ib_mad_port_private *port_priv;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ib_mad_port_list_lock, flags);
+       port_priv = __ib_get_mad_port(device, port_num);
+       if (port_priv == NULL) {
+               spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
+               printk(KERN_ERR PFX "Port %d not found\n", port_num);
+               return -ENODEV;
+       }
+       list_del(&port_priv->port_list);
+       spin_unlock_irqrestore(&ib_mad_port_list_lock, flags);
+
+       /* Stop processing completions. */
+       flush_workqueue(port_priv->wq);
+       destroy_workqueue(port_priv->wq);
+       destroy_mad_qp(&port_priv->qp_info[1]);
+       destroy_mad_qp(&port_priv->qp_info[0]);
+       ib_dereg_mr(port_priv->mr);
+       ib_dealloc_pd(port_priv->pd);
+       ib_destroy_cq(port_priv->cq);
+       cleanup_recv_queue(&port_priv->qp_info[1]);
+       cleanup_recv_queue(&port_priv->qp_info[0]);
+       /* XXX: Handle deallocation of MAD registration tables */
+
+       kfree(port_priv);
+
+       return 0;
+}
+
+static void ib_mad_init_device(struct ib_device *device)
+{
+       int ret, num_ports, cur_port, i, ret2;
+
+       if (device->node_type == IB_NODE_SWITCH) {
+               num_ports = 1;
+               cur_port = 0;
+       } else {
+               num_ports = device->phys_port_cnt;
+               cur_port = 1;
+       }
+       for (i = 0; i < num_ports; i++, cur_port++) {
+               ret = ib_mad_port_open(device, cur_port);
+               if (ret) {
+                       printk(KERN_ERR PFX "Couldn't open %s port %d\n",
+                              device->name, cur_port);
+                       goto error_device_open;
+               }
+               ret = ib_agent_port_open(device, cur_port);
+               if (ret) {
+                       printk(KERN_ERR PFX "Couldn't open %s port %d "
+                              "for agents\n",
+                              device->name, cur_port);
+                       goto error_device_open;
+               }
+       }
+
+       goto error_device_query;
+
+error_device_open:
+       while (i > 0) {
+               cur_port--;
+               ret2 = ib_agent_port_close(device, cur_port);
+               if (ret2) {
+                       printk(KERN_ERR PFX "Couldn't close %s port %d "
+                              "for agents\n",
+                              device->name, cur_port);
+               }
+               ret2 = ib_mad_port_close(device, cur_port);
+               if (ret2) {
+                       printk(KERN_ERR PFX "Couldn't close %s port %d\n",
+                              device->name, cur_port);
+               }
+               i--;
+       }
+
+error_device_query:
+       return;
+}
+
+static void ib_mad_remove_device(struct ib_device *device)
+{
+       int ret = 0, i, num_ports, cur_port, ret2;
+
+       if (device->node_type == IB_NODE_SWITCH) {
+               num_ports = 1;
+               cur_port = 0;
+       } else {
+               num_ports = device->phys_port_cnt;
+               cur_port = 1;
+       }
+       for (i = 0; i < num_ports; i++, cur_port++) {
+               ret2 = ib_agent_port_close(device, cur_port);
+               if (ret2) {
+                       printk(KERN_ERR PFX "Couldn't close %s port %d "
+                              "for agents\n",
+                              device->name, cur_port);
+                       if (!ret)
+                               ret = ret2;
+               }
+               ret2 = ib_mad_port_close(device, cur_port);
+               if (ret2) {
+                       printk(KERN_ERR PFX "Couldn't close %s port %d\n",
+                              device->name, cur_port);
+                       if (!ret)
+                               ret = ret2;
+               }
+       }
+}
+
+static struct ib_client mad_client = {
+       .name   = "mad",
+       .add = ib_mad_init_device,
+       .remove = ib_mad_remove_device
+};
+
+static int __init ib_mad_init_module(void)
+{
+       int ret;
+
+       spin_lock_init(&ib_mad_port_list_lock);
+       spin_lock_init(&ib_agent_port_list_lock);
+
+       ib_mad_cache = kmem_cache_create("ib_mad",
+                                        sizeof(struct ib_mad_private),
+                                        0,
+                                        SLAB_HWCACHE_ALIGN,
+                                        NULL,
+                                        NULL);
+       if (!ib_mad_cache) {
+               printk(KERN_ERR PFX "Couldn't create ib_mad cache\n");
+               ret = -ENOMEM;
+               goto error1;
+       }
+
+       INIT_LIST_HEAD(&ib_mad_port_list);
+
+       if (ib_register_client(&mad_client)) {
+               printk(KERN_ERR PFX "Couldn't register ib_mad client\n");
+               ret = -EINVAL;
+               goto error2;
+       }
+
+       return 0;
+
+error2:
+       kmem_cache_destroy(ib_mad_cache);
+error1:
+       return ret;
+}
+
+static void __exit ib_mad_cleanup_module(void)
+{
+       ib_unregister_client(&mad_client);
+
+       if (kmem_cache_destroy(ib_mad_cache)) {
+               printk(KERN_DEBUG PFX "Failed to destroy ib_mad cache\n");
+       }
+}
+
+module_init(ib_mad_init_module);
+module_exit(ib_mad_cleanup_module);
diff --git a/drivers/infiniband/core/mad_priv.h b/drivers/infiniband/core/mad_priv.h
new file mode 100644 (file)
index 0000000..6110320
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2004, 2005, Voltaire, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mad_priv.h 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#ifndef __IB_MAD_PRIV_H__
+#define __IB_MAD_PRIV_H__
+
+#include <linux/pci.h>
+#include <linux/kthread.h>
+#include <linux/workqueue.h>
+#include <ib_mad.h>
+#include <ib_smi.h>
+
+
+#define PFX "ib_mad: "
+
+#define IB_MAD_QPS_CORE                2 /* Always QP0 and QP1 as a minimum */
+
+/* QP and CQ parameters */
+#define IB_MAD_QP_SEND_SIZE    128
+#define IB_MAD_QP_RECV_SIZE    512
+#define IB_MAD_SEND_REQ_MAX_SG 2
+#define IB_MAD_RECV_REQ_MAX_SG 1
+
+#define IB_MAD_SEND_Q_PSN      0
+
+/* Registration table sizes */
+#define MAX_MGMT_CLASS         80
+#define MAX_MGMT_VERSION       8
+#define MAX_MGMT_OUI           8
+#define MAX_MGMT_VENDOR_RANGE2 IB_MGMT_CLASS_VENDOR_RANGE2_END - \
+                               IB_MGMT_CLASS_VENDOR_RANGE2_START + 1
+
+struct ib_mad_list_head {
+       struct list_head list;
+       struct ib_mad_queue *mad_queue;
+};
+
+struct ib_mad_private_header {
+       struct ib_mad_list_head mad_list;
+       struct ib_mad_recv_wc recv_wc;
+       DECLARE_PCI_UNMAP_ADDR(mapping)
+} __attribute__ ((packed));
+
+struct ib_mad_private {
+       struct ib_mad_private_header header;
+       struct ib_grh grh;
+       union {
+               struct ib_mad mad;
+               struct ib_rmpp_mad rmpp_mad;
+               struct ib_smp smp;
+       } mad;
+} __attribute__ ((packed));
+
+struct ib_mad_agent_private {
+       struct list_head agent_list;
+       struct ib_mad_agent agent;
+       struct ib_mad_reg_req *reg_req;
+       struct ib_mad_qp_info *qp_info;
+
+       spinlock_t lock;
+       struct list_head send_list;
+       struct list_head wait_list;
+       struct work_struct timed_work;
+       unsigned long timeout;
+       struct list_head local_list;
+       struct work_struct local_work;
+
+       atomic_t refcount;
+       wait_queue_head_t wait;
+       u8 rmpp_version;
+};
+
+struct ib_mad_snoop_private {
+       struct ib_mad_agent agent;
+       struct ib_mad_qp_info *qp_info;
+       int snoop_index;
+       int mad_snoop_flags;
+       atomic_t refcount;
+       wait_queue_head_t wait;
+};
+
+struct ib_mad_send_wr_private {
+       struct ib_mad_list_head mad_list;
+       struct list_head agent_list;
+       struct ib_mad_agent *agent;
+       struct ib_send_wr send_wr;
+       struct ib_sge sg_list[IB_MAD_SEND_REQ_MAX_SG];
+       u64 wr_id;                      /* client WR ID */
+       u64 tid;
+       unsigned long timeout;
+       int retry;
+       int refcount;
+       enum ib_wc_status status;
+};
+
+struct ib_mad_local_private {
+       struct list_head completion_list;
+       struct ib_mad_private *mad_priv;
+       struct ib_mad_agent_private *recv_mad_agent;
+       struct ib_send_wr send_wr;
+       struct ib_sge sg_list[IB_MAD_SEND_REQ_MAX_SG];
+       u64 wr_id;                      /* client WR ID */
+       u64 tid;
+};
+
+struct ib_mad_mgmt_method_table {
+       struct ib_mad_agent_private *agent[IB_MGMT_MAX_METHODS];
+};
+
+struct ib_mad_mgmt_class_table {
+       struct ib_mad_mgmt_method_table *method_table[MAX_MGMT_CLASS];
+};
+
+struct ib_mad_mgmt_vendor_class {
+       u8      oui[MAX_MGMT_OUI][3];
+       struct ib_mad_mgmt_method_table *method_table[MAX_MGMT_OUI];
+};
+
+struct ib_mad_mgmt_vendor_class_table {
+       struct ib_mad_mgmt_vendor_class *vendor_class[MAX_MGMT_VENDOR_RANGE2];
+};
+
+struct ib_mad_mgmt_version_table {
+       struct ib_mad_mgmt_class_table *class;
+       struct ib_mad_mgmt_vendor_class_table *vendor;
+};
+
+struct ib_mad_queue {
+       spinlock_t lock;
+       struct list_head list;
+       int count;
+       int max_active;
+       struct ib_mad_qp_info *qp_info;
+};
+
+struct ib_mad_qp_info {
+       struct ib_mad_port_private *port_priv;
+       struct ib_qp *qp;
+       struct ib_mad_queue send_queue;
+       struct ib_mad_queue recv_queue;
+       struct list_head overflow_list;
+       spinlock_t snoop_lock;
+       struct ib_mad_snoop_private **snoop_table;
+       int snoop_table_size;
+       atomic_t snoop_count;
+};
+
+struct ib_mad_port_private {
+       struct list_head port_list;
+       struct ib_device *device;
+       int port_num;
+       struct ib_cq *cq;
+       struct ib_pd *pd;
+       struct ib_mr *mr;
+
+       spinlock_t reg_lock;
+       struct ib_mad_mgmt_version_table version[MAX_MGMT_VERSION];
+       struct list_head agent_list;
+       struct workqueue_struct *wq;
+       struct work_struct work;
+       struct ib_mad_qp_info qp_info[IB_MAD_QPS_CORE];
+};
+
+#endif /* __IB_MAD_PRIV_H__ */
diff --git a/drivers/infiniband/core/packer.c b/drivers/infiniband/core/packer.c
new file mode 100644 (file)
index 0000000..5f15fef
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: packer.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <ib_pack.h>
+
+static u64 value_read(int offset, int size, void *structure)
+{
+       switch (size) {
+       case 1: return                *(u8  *) (structure + offset);
+       case 2: return be16_to_cpup((__be16 *) (structure + offset));
+       case 4: return be32_to_cpup((__be32 *) (structure + offset));
+       case 8: return be64_to_cpup((__be64 *) (structure + offset));
+       default:
+               printk(KERN_WARNING "Field size %d bits not handled\n", size * 8);
+               return 0;
+       }
+}
+
+/**
+ * ib_pack - Pack a structure into a buffer
+ * @desc:Array of structure field descriptions
+ * @desc_len:Number of entries in @desc
+ * @structure:Structure to pack from
+ * @buf:Buffer to pack into
+ *
+ * ib_pack() packs a list of structure fields into a buffer,
+ * controlled by the array of fields in @desc.
+ */
+void ib_pack(const struct ib_field        *desc,
+            int                           desc_len,
+            void                         *structure,
+            void                         *buf)
+{
+       int i;
+
+       for (i = 0; i < desc_len; ++i) {
+               if (desc[i].size_bits <= 32) {
+                       int shift;
+                       u32 val;
+                       __be32 mask;
+                       __be32 *addr;
+
+                       shift = 32 - desc[i].offset_bits - desc[i].size_bits;
+                       if (desc[i].struct_size_bytes)
+                               val = value_read(desc[i].struct_offset_bytes,
+                                                desc[i].struct_size_bytes,
+                                                structure) << shift;
+                       else
+                               val = 0;
+
+                       mask = cpu_to_be32(((1ull << desc[i].size_bits) - 1) << shift);
+                       addr = (__be32 *) buf + desc[i].offset_words;
+                       *addr = (*addr & ~mask) | (cpu_to_be32(val) & mask);
+               } else if (desc[i].size_bits <= 64) {
+                       int shift;
+                       u64 val;
+                       __be64 mask;
+                       __be64 *addr;
+
+                       shift = 64 - desc[i].offset_bits - desc[i].size_bits;
+                       if (desc[i].struct_size_bytes)
+                               val = value_read(desc[i].struct_offset_bytes,
+                                                desc[i].struct_size_bytes,
+                                                structure) << shift;
+                       else
+                               val = 0;
+
+                       mask = cpu_to_be64(((1ull << desc[i].size_bits) - 1) << shift);
+                       addr = (__be64 *) ((__be32 *) buf + desc[i].offset_words);
+                       *addr = (*addr & ~mask) | (cpu_to_be64(val) & mask);
+               } else {
+                       if (desc[i].offset_bits % 8 ||
+                           desc[i].size_bits   % 8) {
+                               printk(KERN_WARNING "Structure field %s of size %d "
+                                      "bits is not byte-aligned\n",
+                                      desc[i].field_name, desc[i].size_bits);
+                       }
+
+                       if (desc[i].struct_size_bytes)
+                               memcpy(buf + desc[i].offset_words * 4 +
+                                      desc[i].offset_bits / 8,
+                                      structure + desc[i].struct_offset_bytes,
+                                      desc[i].size_bits / 8);
+                       else
+                               memset(buf + desc[i].offset_words * 4 +
+                                      desc[i].offset_bits / 8,
+                                      0,
+                                      desc[i].size_bits / 8);
+               }
+       }
+}
+EXPORT_SYMBOL(ib_pack);
+
+static void value_write(int offset, int size, u64 val, void *structure)
+{
+       switch (size * 8) {
+       case 8:  *(    u8 *) (structure + offset) = val; break;
+       case 16: *(__be16 *) (structure + offset) = cpu_to_be16(val); break;
+       case 32: *(__be32 *) (structure + offset) = cpu_to_be32(val); break;
+       case 64: *(__be64 *) (structure + offset) = cpu_to_be64(val); break;
+       default:
+               printk(KERN_WARNING "Field size %d bits not handled\n", size * 8);
+       }
+}
+
+/**
+ * ib_unpack - Unpack a buffer into a structure
+ * @desc:Array of structure field descriptions
+ * @desc_len:Number of entries in @desc
+ * @buf:Buffer to unpack from
+ * @structure:Structure to unpack into
+ *
+ * ib_pack() unpacks a list of structure fields from a buffer,
+ * controlled by the array of fields in @desc.
+ */
+void ib_unpack(const struct ib_field        *desc,
+              int                           desc_len,
+              void                         *buf,
+              void                         *structure)
+{
+       int i;
+
+       for (i = 0; i < desc_len; ++i) {
+               if (!desc[i].struct_size_bytes)
+                       continue;
+
+               if (desc[i].size_bits <= 32) {
+                       int shift;
+                       u32  val;
+                       u32  mask;
+                       __be32 *addr;
+
+                       shift = 32 - desc[i].offset_bits - desc[i].size_bits;
+                       mask = ((1ull << desc[i].size_bits) - 1) << shift;
+                       addr = (__be32 *) buf + desc[i].offset_words;
+                       val = (be32_to_cpup(addr) & mask) >> shift;
+                       value_write(desc[i].struct_offset_bytes,
+                                   desc[i].struct_size_bytes,
+                                   val,
+                                   structure);
+               } else if (desc[i].size_bits <= 64) {
+                       int shift;
+                       u64  val;
+                       u64  mask;
+                       __be64 *addr;
+
+                       shift = 64 - desc[i].offset_bits - desc[i].size_bits;
+                       mask = ((1ull << desc[i].size_bits) - 1) << shift;
+                       addr = (__be64 *) buf + desc[i].offset_words;
+                       val = (be64_to_cpup(addr) & mask) >> shift;
+                       value_write(desc[i].struct_offset_bytes,
+                                   desc[i].struct_size_bytes,
+                                   val,
+                                   structure);
+               } else {
+                       if (desc[i].offset_bits % 8 ||
+                           desc[i].size_bits   % 8) {
+                               printk(KERN_WARNING "Structure field %s of size %d "
+                                      "bits is not byte-aligned\n",
+                                      desc[i].field_name, desc[i].size_bits);
+                       }
+
+                       memcpy(structure + desc[i].struct_offset_bytes,
+                              buf + desc[i].offset_words * 4 +
+                              desc[i].offset_bits / 8,
+                              desc[i].size_bits / 8);
+               }
+       }
+}
+EXPORT_SYMBOL(ib_unpack);
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
new file mode 100644 (file)
index 0000000..d4233ee
--- /dev/null
@@ -0,0 +1,866 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: sa_query.c 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kref.h>
+#include <linux/idr.h>
+
+#include <ib_pack.h>
+#include <ib_sa.h>
+
+MODULE_AUTHOR("Roland Dreier");
+MODULE_DESCRIPTION("InfiniBand subnet administration query support");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * These two structures must be packed because they have 64-bit fields
+ * that are only 32-bit aligned.  64-bit architectures will lay them
+ * out wrong otherwise.  (And unfortunately they are sent on the wire
+ * so we can't change the layout)
+ */
+struct ib_sa_hdr {
+       u64                     sm_key;
+       u16                     attr_offset;
+       u16                     reserved;
+       ib_sa_comp_mask         comp_mask;
+} __attribute__ ((packed));
+
+struct ib_sa_mad {
+       struct ib_mad_hdr       mad_hdr;
+       struct ib_rmpp_hdr      rmpp_hdr;
+       struct ib_sa_hdr        sa_hdr;
+       u8                      data[200];
+} __attribute__ ((packed));
+
+struct ib_sa_sm_ah {
+       struct ib_ah        *ah;
+       struct kref          ref;
+};
+
+struct ib_sa_port {
+       struct ib_mad_agent *agent;
+       struct ib_mr        *mr;
+       struct ib_sa_sm_ah  *sm_ah;
+       struct work_struct   update_task;
+       spinlock_t           ah_lock;
+       u8                   port_num;
+};
+
+struct ib_sa_device {
+       int                     start_port, end_port;
+       struct ib_event_handler event_handler;
+       struct ib_sa_port port[0];
+};
+
+struct ib_sa_query {
+       void (*callback)(struct ib_sa_query *, int, struct ib_sa_mad *);
+       void (*release)(struct ib_sa_query *);
+       struct ib_sa_port  *port;
+       struct ib_sa_mad   *mad;
+       struct ib_sa_sm_ah *sm_ah;
+       DECLARE_PCI_UNMAP_ADDR(mapping)
+       int                 id;
+};
+
+struct ib_sa_path_query {
+       void (*callback)(int, struct ib_sa_path_rec *, void *);
+       void *context;
+       struct ib_sa_query sa_query;
+};
+
+struct ib_sa_mcmember_query {
+       void (*callback)(int, struct ib_sa_mcmember_rec *, void *);
+       void *context;
+       struct ib_sa_query sa_query;
+};
+
+static void ib_sa_add_one(struct ib_device *device);
+static void ib_sa_remove_one(struct ib_device *device);
+
+static struct ib_client sa_client = {
+       .name   = "sa",
+       .add    = ib_sa_add_one,
+       .remove = ib_sa_remove_one
+};
+
+static spinlock_t idr_lock;
+static DEFINE_IDR(query_idr);
+
+static spinlock_t tid_lock;
+static u32 tid;
+
+enum {
+       IB_SA_ATTR_CLASS_PORTINFO    = 0x01,
+       IB_SA_ATTR_NOTICE            = 0x02,
+       IB_SA_ATTR_INFORM_INFO       = 0x03,
+       IB_SA_ATTR_NODE_REC          = 0x11,
+       IB_SA_ATTR_PORT_INFO_REC     = 0x12,
+       IB_SA_ATTR_SL2VL_REC         = 0x13,
+       IB_SA_ATTR_SWITCH_REC        = 0x14,
+       IB_SA_ATTR_LINEAR_FDB_REC    = 0x15,
+       IB_SA_ATTR_RANDOM_FDB_REC    = 0x16,
+       IB_SA_ATTR_MCAST_FDB_REC     = 0x17,
+       IB_SA_ATTR_SM_INFO_REC       = 0x18,
+       IB_SA_ATTR_LINK_REC          = 0x20,
+       IB_SA_ATTR_GUID_INFO_REC     = 0x30,
+       IB_SA_ATTR_SERVICE_REC       = 0x31,
+       IB_SA_ATTR_PARTITION_REC     = 0x33,
+       IB_SA_ATTR_RANGE_REC         = 0x34,
+       IB_SA_ATTR_PATH_REC          = 0x35,
+       IB_SA_ATTR_VL_ARB_REC        = 0x36,
+       IB_SA_ATTR_MC_GROUP_REC      = 0x37,
+       IB_SA_ATTR_MC_MEMBER_REC     = 0x38,
+       IB_SA_ATTR_TRACE_REC         = 0x39,
+       IB_SA_ATTR_MULTI_PATH_REC    = 0x3a,
+       IB_SA_ATTR_SERVICE_ASSOC_REC = 0x3b
+};
+
+#define PATH_REC_FIELD(field) \
+       .struct_offset_bytes = offsetof(struct ib_sa_path_rec, field),          \
+       .struct_size_bytes   = sizeof ((struct ib_sa_path_rec *) 0)->field,     \
+       .field_name          = "sa_path_rec:" #field
+
+static const struct ib_field path_rec_table[] = {
+       { RESERVED,
+         .offset_words = 0,
+         .offset_bits  = 0,
+         .size_bits    = 32 },
+       { RESERVED,
+         .offset_words = 1,
+         .offset_bits  = 0,
+         .size_bits    = 32 },
+       { PATH_REC_FIELD(dgid),
+         .offset_words = 2,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+       { PATH_REC_FIELD(sgid),
+         .offset_words = 6,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+       { PATH_REC_FIELD(dlid),
+         .offset_words = 10,
+         .offset_bits  = 0,
+         .size_bits    = 16 },
+       { PATH_REC_FIELD(slid),
+         .offset_words = 10,
+         .offset_bits  = 16,
+         .size_bits    = 16 },
+       { PATH_REC_FIELD(raw_traffic),
+         .offset_words = 11,
+         .offset_bits  = 0,
+         .size_bits    = 1 },
+       { RESERVED,
+         .offset_words = 11,
+         .offset_bits  = 1,
+         .size_bits    = 3 },
+       { PATH_REC_FIELD(flow_label),
+         .offset_words = 11,
+         .offset_bits  = 4,
+         .size_bits    = 20 },
+       { PATH_REC_FIELD(hop_limit),
+         .offset_words = 11,
+         .offset_bits  = 24,
+         .size_bits    = 8 },
+       { PATH_REC_FIELD(traffic_class),
+         .offset_words = 12,
+         .offset_bits  = 0,
+         .size_bits    = 8 },
+       { PATH_REC_FIELD(reversible),
+         .offset_words = 12,
+         .offset_bits  = 8,
+         .size_bits    = 1 },
+       { PATH_REC_FIELD(numb_path),
+         .offset_words = 12,
+         .offset_bits  = 9,
+         .size_bits    = 7 },
+       { PATH_REC_FIELD(pkey),
+         .offset_words = 12,
+         .offset_bits  = 16,
+         .size_bits    = 16 },
+       { RESERVED,
+         .offset_words = 13,
+         .offset_bits  = 0,
+         .size_bits    = 12 },
+       { PATH_REC_FIELD(sl),
+         .offset_words = 13,
+         .offset_bits  = 12,
+         .size_bits    = 4 },
+       { PATH_REC_FIELD(mtu_selector),
+         .offset_words = 13,
+         .offset_bits  = 16,
+         .size_bits    = 2 },
+       { PATH_REC_FIELD(mtu),
+         .offset_words = 13,
+         .offset_bits  = 18,
+         .size_bits    = 6 },
+       { PATH_REC_FIELD(rate_selector),
+         .offset_words = 13,
+         .offset_bits  = 24,
+         .size_bits    = 2 },
+       { PATH_REC_FIELD(rate),
+         .offset_words = 13,
+         .offset_bits  = 26,
+         .size_bits    = 6 },
+       { PATH_REC_FIELD(packet_life_time_selector),
+         .offset_words = 14,
+         .offset_bits  = 0,
+         .size_bits    = 2 },
+       { PATH_REC_FIELD(packet_life_time),
+         .offset_words = 14,
+         .offset_bits  = 2,
+         .size_bits    = 6 },
+       { PATH_REC_FIELD(preference),
+         .offset_words = 14,
+         .offset_bits  = 8,
+         .size_bits    = 8 },
+       { RESERVED,
+         .offset_words = 14,
+         .offset_bits  = 16,
+         .size_bits    = 48 },
+};
+
+#define MCMEMBER_REC_FIELD(field) \
+       .struct_offset_bytes = offsetof(struct ib_sa_mcmember_rec, field),      \
+       .struct_size_bytes   = sizeof ((struct ib_sa_mcmember_rec *) 0)->field, \
+       .field_name          = "sa_mcmember_rec:" #field
+
+static const struct ib_field mcmember_rec_table[] = {
+       { MCMEMBER_REC_FIELD(mgid),
+         .offset_words = 0,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+       { MCMEMBER_REC_FIELD(port_gid),
+         .offset_words = 4,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+       { MCMEMBER_REC_FIELD(qkey),
+         .offset_words = 8,
+         .offset_bits  = 0,
+         .size_bits    = 32 },
+       { MCMEMBER_REC_FIELD(mlid),
+         .offset_words = 9,
+         .offset_bits  = 0,
+         .size_bits    = 16 },
+       { MCMEMBER_REC_FIELD(mtu_selector),
+         .offset_words = 9,
+         .offset_bits  = 16,
+         .size_bits    = 2 },
+       { MCMEMBER_REC_FIELD(mtu),
+         .offset_words = 9,
+         .offset_bits  = 18,
+         .size_bits    = 6 },
+       { MCMEMBER_REC_FIELD(traffic_class),
+         .offset_words = 9,
+         .offset_bits  = 24,
+         .size_bits    = 8 },
+       { MCMEMBER_REC_FIELD(pkey),
+         .offset_words = 10,
+         .offset_bits  = 0,
+         .size_bits    = 16 },
+       { MCMEMBER_REC_FIELD(rate_selector),
+         .offset_words = 10,
+         .offset_bits  = 16,
+         .size_bits    = 2 },
+       { MCMEMBER_REC_FIELD(rate),
+         .offset_words = 10,
+         .offset_bits  = 18,
+         .size_bits    = 6 },
+       { MCMEMBER_REC_FIELD(packet_life_time_selector),
+         .offset_words = 10,
+         .offset_bits  = 24,
+         .size_bits    = 2 },
+       { MCMEMBER_REC_FIELD(packet_life_time),
+         .offset_words = 10,
+         .offset_bits  = 26,
+         .size_bits    = 6 },
+       { MCMEMBER_REC_FIELD(sl),
+         .offset_words = 11,
+         .offset_bits  = 0,
+         .size_bits    = 4 },
+       { MCMEMBER_REC_FIELD(flow_label),
+         .offset_words = 11,
+         .offset_bits  = 4,
+         .size_bits    = 20 },
+       { MCMEMBER_REC_FIELD(hop_limit),
+         .offset_words = 11,
+         .offset_bits  = 24,
+         .size_bits    = 8 },
+       { MCMEMBER_REC_FIELD(scope),
+         .offset_words = 12,
+         .offset_bits  = 0,
+         .size_bits    = 4 },
+       { MCMEMBER_REC_FIELD(join_state),
+         .offset_words = 12,
+         .offset_bits  = 4,
+         .size_bits    = 4 },
+       { MCMEMBER_REC_FIELD(proxy_join),
+         .offset_words = 12,
+         .offset_bits  = 8,
+         .size_bits    = 1 },
+       { RESERVED,
+         .offset_words = 12,
+         .offset_bits  = 9,
+         .size_bits    = 23 },
+};
+
+static void free_sm_ah(struct kref *kref)
+{
+       struct ib_sa_sm_ah *sm_ah = container_of(kref, struct ib_sa_sm_ah, ref);
+
+       ib_destroy_ah(sm_ah->ah);
+       kfree(sm_ah);
+}
+
+static void update_sm_ah(void *port_ptr)
+{
+       struct ib_sa_port *port = port_ptr;
+       struct ib_sa_sm_ah *new_ah, *old_ah;
+       struct ib_port_attr port_attr;
+       struct ib_ah_attr   ah_attr;
+
+       if (ib_query_port(port->agent->device, port->port_num, &port_attr)) {
+               printk(KERN_WARNING "Couldn't query port\n");
+               return;
+       }
+
+       new_ah = kmalloc(sizeof *new_ah, GFP_KERNEL);
+       if (!new_ah) {
+               printk(KERN_WARNING "Couldn't allocate new SM AH\n");
+               return;
+       }
+
+       kref_init(&new_ah->ref);
+
+       memset(&ah_attr, 0, sizeof ah_attr);
+       ah_attr.dlid     = port_attr.sm_lid;
+       ah_attr.sl       = port_attr.sm_sl;
+       ah_attr.port_num = port->port_num;
+
+       new_ah->ah = ib_create_ah(port->agent->qp->pd, &ah_attr);
+       if (IS_ERR(new_ah->ah)) {
+               printk(KERN_WARNING "Couldn't create new SM AH\n");
+               kfree(new_ah);
+               return;
+       }
+
+       spin_lock_irq(&port->ah_lock);
+       old_ah = port->sm_ah;
+       port->sm_ah = new_ah;
+       spin_unlock_irq(&port->ah_lock);
+
+       if (old_ah)
+               kref_put(&old_ah->ref, free_sm_ah);
+}
+
+static void ib_sa_event(struct ib_event_handler *handler, struct ib_event *event)
+{
+       if (event->event == IB_EVENT_PORT_ERR    ||
+           event->event == IB_EVENT_PORT_ACTIVE ||
+           event->event == IB_EVENT_LID_CHANGE  ||
+           event->event == IB_EVENT_PKEY_CHANGE ||
+           event->event == IB_EVENT_SM_CHANGE) {
+               struct ib_sa_device *sa_dev =
+                       ib_get_client_data(event->device, &sa_client);
+
+               schedule_work(&sa_dev->port[event->element.port_num -
+                                           sa_dev->start_port].update_task);
+       }
+}
+
+/**
+ * ib_sa_cancel_query - try to cancel an SA query
+ * @id:ID of query to cancel
+ * @query:query pointer to cancel
+ *
+ * Try to cancel an SA query.  If the id and query don't match up or
+ * the query has already completed, nothing is done.  Otherwise the
+ * query is canceled and will complete with a status of -EINTR.
+ */
+void ib_sa_cancel_query(int id, struct ib_sa_query *query)
+{
+       unsigned long flags;
+       struct ib_mad_agent *agent;
+
+       spin_lock_irqsave(&idr_lock, flags);
+       if (idr_find(&query_idr, id) != query) {
+               spin_unlock_irqrestore(&idr_lock, flags);
+               return;
+       }
+       agent = query->port->agent;
+       spin_unlock_irqrestore(&idr_lock, flags);
+
+       ib_cancel_mad(agent, id);
+}
+EXPORT_SYMBOL(ib_sa_cancel_query);
+
+static void init_mad(struct ib_sa_mad *mad, struct ib_mad_agent *agent)
+{
+       unsigned long flags;
+
+       memset(mad, 0, sizeof *mad);
+
+       mad->mad_hdr.base_version  = IB_MGMT_BASE_VERSION;
+       mad->mad_hdr.mgmt_class    = IB_MGMT_CLASS_SUBN_ADM;
+       mad->mad_hdr.class_version = IB_SA_CLASS_VERSION;
+
+       spin_lock_irqsave(&tid_lock, flags);
+       mad->mad_hdr.tid           =
+               cpu_to_be64(((u64) agent->hi_tid) << 32 | tid++);
+       spin_unlock_irqrestore(&tid_lock, flags);
+}
+
+static int send_mad(struct ib_sa_query *query, int timeout_ms)
+{
+       struct ib_sa_port *port = query->port;
+       unsigned long flags;
+       int ret;
+       struct ib_sge      gather_list;
+       struct ib_send_wr *bad_wr, wr = {
+               .opcode      = IB_WR_SEND,
+               .sg_list     = &gather_list,
+               .num_sge     = 1,
+               .send_flags  = IB_SEND_SIGNALED,
+               .wr          = {
+                        .ud = {
+                                .mad_hdr     = &query->mad->mad_hdr,
+                                .remote_qpn  = 1,
+                                .remote_qkey = IB_QP1_QKEY,
+                                .timeout_ms  = timeout_ms
+                        }
+                }
+       };
+
+retry:
+       if (!idr_pre_get(&query_idr, GFP_ATOMIC))
+               return -ENOMEM;
+       spin_lock_irqsave(&idr_lock, flags);
+       ret = idr_get_new(&query_idr, query, &query->id);
+       spin_unlock_irqrestore(&idr_lock, flags);
+       if (ret == -EAGAIN)
+               goto retry;
+       if (ret)
+               return ret;
+
+       wr.wr_id = query->id;
+
+       spin_lock_irqsave(&port->ah_lock, flags);
+       kref_get(&port->sm_ah->ref);
+       query->sm_ah = port->sm_ah;
+       wr.wr.ud.ah  = port->sm_ah->ah;
+       spin_unlock_irqrestore(&port->ah_lock, flags);
+
+       gather_list.addr   = dma_map_single(port->agent->device->dma_device,
+                                           query->mad,
+                                           sizeof (struct ib_sa_mad),
+                                           DMA_TO_DEVICE);
+       gather_list.length = sizeof (struct ib_sa_mad);
+       gather_list.lkey   = port->mr->lkey;
+       pci_unmap_addr_set(query, mapping, gather_list.addr);
+
+       ret = ib_post_send_mad(port->agent, &wr, &bad_wr);
+       if (ret) {
+               dma_unmap_single(port->agent->device->dma_device,
+                                pci_unmap_addr(query, mapping),
+                                sizeof (struct ib_sa_mad),
+                                DMA_TO_DEVICE);
+               kref_put(&query->sm_ah->ref, free_sm_ah);
+               spin_lock_irqsave(&idr_lock, flags);
+               idr_remove(&query_idr, query->id);
+               spin_unlock_irqrestore(&idr_lock, flags);
+       }
+
+       return ret;
+}
+
+static void ib_sa_path_rec_callback(struct ib_sa_query *sa_query,
+                                   int status,
+                                   struct ib_sa_mad *mad)
+{
+       struct ib_sa_path_query *query =
+               container_of(sa_query, struct ib_sa_path_query, sa_query);
+
+       if (mad) {
+               struct ib_sa_path_rec rec;
+
+               ib_unpack(path_rec_table, ARRAY_SIZE(path_rec_table),
+                         mad->data, &rec);
+               query->callback(status, &rec, query->context);
+       } else
+               query->callback(status, NULL, query->context);
+}
+
+static void ib_sa_path_rec_release(struct ib_sa_query *sa_query)
+{
+       kfree(sa_query->mad);
+       kfree(container_of(sa_query, struct ib_sa_path_query, sa_query));
+}
+
+/**
+ * ib_sa_path_rec_get - Start a Path get query
+ * @device:device to send query on
+ * @port_num: port number to send query on
+ * @rec:Path Record to send in query
+ * @comp_mask:component mask to send in query
+ * @timeout_ms:time to wait for response
+ * @gfp_mask:GFP mask to use for internal allocations
+ * @callback:function called when query completes, times out or is
+ * canceled
+ * @context:opaque user context passed to callback
+ * @sa_query:query context, used to cancel query
+ *
+ * Send a Path Record Get query to the SA to look up a path.  The
+ * callback function will be called when the query completes (or
+ * fails); status is 0 for a successful response, -EINTR if the query
+ * is canceled, -ETIMEDOUT is the query timed out, or -EIO if an error
+ * occurred sending the query.  The resp parameter of the callback is
+ * only valid if status is 0.
+ *
+ * If the return value of ib_sa_path_rec_get() is negative, it is an
+ * error code.  Otherwise it is a query ID that can be used to cancel
+ * the query.
+ */
+int ib_sa_path_rec_get(struct ib_device *device, u8 port_num,
+                      struct ib_sa_path_rec *rec,
+                      ib_sa_comp_mask comp_mask,
+                      int timeout_ms, int gfp_mask,
+                      void (*callback)(int status,
+                                       struct ib_sa_path_rec *resp,
+                                       void *context),
+                      void *context,
+                      struct ib_sa_query **sa_query)
+{
+       struct ib_sa_path_query *query;
+       struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
+       struct ib_sa_port   *port   = &sa_dev->port[port_num - sa_dev->start_port];
+       struct ib_mad_agent *agent  = port->agent;
+       int ret;
+
+       query = kmalloc(sizeof *query, gfp_mask);
+       if (!query)
+               return -ENOMEM;
+       query->sa_query.mad = kmalloc(sizeof *query->sa_query.mad, gfp_mask);
+       if (!query->sa_query.mad) {
+               kfree(query);
+               return -ENOMEM;
+       }
+
+       query->callback = callback;
+       query->context  = context;
+
+       init_mad(query->sa_query.mad, agent);
+
+       query->sa_query.callback              = ib_sa_path_rec_callback;
+       query->sa_query.release               = ib_sa_path_rec_release;
+       query->sa_query.port                  = port;
+       query->sa_query.mad->mad_hdr.method   = IB_MGMT_METHOD_GET;
+       query->sa_query.mad->mad_hdr.attr_id  = cpu_to_be16(IB_SA_ATTR_PATH_REC);
+       query->sa_query.mad->sa_hdr.comp_mask = comp_mask;
+
+       ib_pack(path_rec_table, ARRAY_SIZE(path_rec_table),
+               rec, query->sa_query.mad->data);
+
+       *sa_query = &query->sa_query;
+       ret = send_mad(&query->sa_query, timeout_ms);
+       if (ret) {
+               *sa_query = NULL;
+               kfree(query->sa_query.mad);
+               kfree(query);
+       }
+
+       return ret ? ret : query->sa_query.id;
+}
+EXPORT_SYMBOL(ib_sa_path_rec_get);
+
+static void ib_sa_mcmember_rec_callback(struct ib_sa_query *sa_query,
+                                       int status,
+                                       struct ib_sa_mad *mad)
+{
+       struct ib_sa_mcmember_query *query =
+               container_of(sa_query, struct ib_sa_mcmember_query, sa_query);
+
+       if (mad) {
+               struct ib_sa_mcmember_rec rec;
+
+               ib_unpack(mcmember_rec_table, ARRAY_SIZE(mcmember_rec_table),
+                         mad->data, &rec);
+               query->callback(status, &rec, query->context);
+       } else
+               query->callback(status, NULL, query->context);
+}
+
+static void ib_sa_mcmember_rec_release(struct ib_sa_query *sa_query)
+{
+       kfree(sa_query->mad);
+       kfree(container_of(sa_query, struct ib_sa_mcmember_query, sa_query));
+}
+
+int ib_sa_mcmember_rec_query(struct ib_device *device, u8 port_num,
+                            u8 method,
+                            struct ib_sa_mcmember_rec *rec,
+                            ib_sa_comp_mask comp_mask,
+                            int timeout_ms, int gfp_mask,
+                            void (*callback)(int status,
+                                             struct ib_sa_mcmember_rec *resp,
+                                             void *context),
+                            void *context,
+                            struct ib_sa_query **sa_query)
+{
+       struct ib_sa_mcmember_query *query;
+       struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
+       struct ib_sa_port   *port   = &sa_dev->port[port_num - sa_dev->start_port];
+       struct ib_mad_agent *agent  = port->agent;
+       int ret;
+
+       query = kmalloc(sizeof *query, gfp_mask);
+       if (!query)
+               return -ENOMEM;
+       query->sa_query.mad = kmalloc(sizeof *query->sa_query.mad, gfp_mask);
+       if (!query->sa_query.mad) {
+               kfree(query);
+               return -ENOMEM;
+       }
+
+       query->callback = callback;
+       query->context  = context;
+
+       init_mad(query->sa_query.mad, agent);
+
+       query->sa_query.callback              = ib_sa_mcmember_rec_callback;
+       query->sa_query.release               = ib_sa_mcmember_rec_release;
+       query->sa_query.port                  = port;
+       query->sa_query.mad->mad_hdr.method   = method;
+       query->sa_query.mad->mad_hdr.attr_id  = cpu_to_be16(IB_SA_ATTR_MC_MEMBER_REC);
+       query->sa_query.mad->sa_hdr.comp_mask = comp_mask;
+
+       ib_pack(mcmember_rec_table, ARRAY_SIZE(mcmember_rec_table),
+               rec, query->sa_query.mad->data);
+
+       *sa_query = &query->sa_query;
+       ret = send_mad(&query->sa_query, timeout_ms);
+       if (ret) {
+               *sa_query = NULL;
+               kfree(query->sa_query.mad);
+               kfree(query);
+       }
+
+       return ret ? ret : query->sa_query.id;
+}
+EXPORT_SYMBOL(ib_sa_mcmember_rec_query);
+
+static void send_handler(struct ib_mad_agent *agent,
+                        struct ib_mad_send_wc *mad_send_wc)
+{
+       struct ib_sa_query *query;
+       unsigned long flags;
+
+       spin_lock_irqsave(&idr_lock, flags);
+       query = idr_find(&query_idr, mad_send_wc->wr_id);
+       spin_unlock_irqrestore(&idr_lock, flags);
+
+       if (!query)
+               return;
+
+       switch (mad_send_wc->status) {
+       case IB_WC_SUCCESS:
+               /* No callback -- already got recv */
+               break;
+       case IB_WC_RESP_TIMEOUT_ERR:
+               query->callback(query, -ETIMEDOUT, NULL);
+               break;
+       case IB_WC_WR_FLUSH_ERR:
+               query->callback(query, -EINTR, NULL);
+               break;
+       default:
+               query->callback(query, -EIO, NULL);
+               break;
+       }
+
+       dma_unmap_single(agent->device->dma_device,
+                        pci_unmap_addr(query, mapping),
+                        sizeof (struct ib_sa_mad),
+                        DMA_TO_DEVICE);
+       kref_put(&query->sm_ah->ref, free_sm_ah);
+
+       query->release(query);
+
+       spin_lock_irqsave(&idr_lock, flags);
+       idr_remove(&query_idr, mad_send_wc->wr_id);
+       spin_unlock_irqrestore(&idr_lock, flags);
+}
+
+static void recv_handler(struct ib_mad_agent *mad_agent,
+                        struct ib_mad_recv_wc *mad_recv_wc)
+{
+       struct ib_sa_query *query;
+       unsigned long flags;
+
+       spin_lock_irqsave(&idr_lock, flags);
+       query = idr_find(&query_idr, mad_recv_wc->wc->wr_id);
+       spin_unlock_irqrestore(&idr_lock, flags);
+
+       if (query) {
+               if (mad_recv_wc->wc->status == IB_WC_SUCCESS)
+                       query->callback(query,
+                                       mad_recv_wc->recv_buf.mad->mad_hdr.status ?
+                                       -EINVAL : 0,
+                                       (struct ib_sa_mad *) mad_recv_wc->recv_buf.mad);
+               else
+                       query->callback(query, -EIO, NULL);
+       }
+
+       ib_free_recv_mad(mad_recv_wc);
+}
+
+static void ib_sa_add_one(struct ib_device *device)
+{
+       struct ib_sa_device *sa_dev;
+       int s, e, i;
+
+       if (device->node_type == IB_NODE_SWITCH)
+               s = e = 0;
+       else {
+               s = 1;
+               e = device->phys_port_cnt;
+       }
+
+       sa_dev = kmalloc(sizeof *sa_dev +
+                        (e - s + 1) * sizeof (struct ib_sa_port),
+                        GFP_KERNEL);
+       if (!sa_dev)
+               return;
+
+       sa_dev->start_port = s;
+       sa_dev->end_port   = e;
+
+       for (i = 0; i <= e - s; ++i) {
+               sa_dev->port[i].mr       = NULL;
+               sa_dev->port[i].sm_ah    = NULL;
+               sa_dev->port[i].port_num = i + s;
+               spin_lock_init(&sa_dev->port[i].ah_lock);
+
+               sa_dev->port[i].agent =
+                       ib_register_mad_agent(device, i + s, IB_QPT_GSI,
+                                             NULL, 0, send_handler,
+                                             recv_handler, sa_dev);
+               if (IS_ERR(sa_dev->port[i].agent))
+                       goto err;
+
+               sa_dev->port[i].mr = ib_get_dma_mr(sa_dev->port[i].agent->qp->pd,
+                                                  IB_ACCESS_LOCAL_WRITE);
+               if (IS_ERR(sa_dev->port[i].mr)) {
+                       ib_unregister_mad_agent(sa_dev->port[i].agent);
+                       goto err;
+               }
+
+               INIT_WORK(&sa_dev->port[i].update_task,
+                         update_sm_ah, &sa_dev->port[i]);
+       }
+
+       ib_set_client_data(device, &sa_client, sa_dev);
+
+       /*
+        * We register our event handler after everything is set up,
+        * and then update our cached info after the event handler is
+        * registered to avoid any problems if a port changes state
+        * during our initialization.
+        */
+
+       INIT_IB_EVENT_HANDLER(&sa_dev->event_handler, device, ib_sa_event);
+       if (ib_register_event_handler(&sa_dev->event_handler))
+               goto err;
+
+       for (i = 0; i <= e - s; ++i)
+               update_sm_ah(&sa_dev->port[i]);
+
+       return;
+
+err:
+       while (--i >= 0) {
+               ib_dereg_mr(sa_dev->port[i].mr);
+               ib_unregister_mad_agent(sa_dev->port[i].agent);
+       }
+
+       kfree(sa_dev);
+
+       return;
+}
+
+static void ib_sa_remove_one(struct ib_device *device)
+{
+       struct ib_sa_device *sa_dev = ib_get_client_data(device, &sa_client);
+       int i;
+
+       if (!sa_dev)
+               return;
+
+       ib_unregister_event_handler(&sa_dev->event_handler);
+
+       for (i = 0; i <= sa_dev->end_port - sa_dev->start_port; ++i) {
+               ib_unregister_mad_agent(sa_dev->port[i].agent);
+               kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah);
+       }
+
+       kfree(sa_dev);
+}
+
+static int __init ib_sa_init(void)
+{
+       int ret;
+
+       spin_lock_init(&idr_lock);
+       spin_lock_init(&tid_lock);
+
+       get_random_bytes(&tid, sizeof tid);
+
+       ret = ib_register_client(&sa_client);
+       if (ret)
+               printk(KERN_ERR "Couldn't register ib_sa client\n");
+
+       return ret;
+}
+
+static void __exit ib_sa_cleanup(void)
+{
+       ib_unregister_client(&sa_client);
+}
+
+module_init(ib_sa_init);
+module_exit(ib_sa_cleanup);
diff --git a/drivers/infiniband/core/smi.c b/drivers/infiniband/core/smi.c
new file mode 100644 (file)
index 0000000..6adf653
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
+ * Copyright (c) 2004 Infinicon Corporation.  All rights reserved.
+ * Copyright (c) 2004 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ * Copyright (c) 2004 Voltaire Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: smi.c 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#include <ib_smi.h>
+
+
+/*
+ * Fixup a directed route SMP for sending
+ * Return 0 if the SMP should be discarded
+ */
+int smi_handle_dr_smp_send(struct ib_smp *smp,
+                          u8 node_type,
+                          int port_num)
+{
+       u8 hop_ptr, hop_cnt;
+
+       hop_ptr = smp->hop_ptr;
+       hop_cnt = smp->hop_cnt;
+
+       /* See section 14.2.2.2, Vol 1 IB spec */
+       if (!ib_get_smp_direction(smp)) {
+               /* C14-9:1 */
+               if (hop_cnt && hop_ptr == 0) {
+                       smp->hop_ptr++;
+                       return (smp->initial_path[smp->hop_ptr] ==
+                               port_num);
+               }
+
+               /* C14-9:2 */
+               if (hop_ptr && hop_ptr < hop_cnt) {
+                       if (node_type != IB_NODE_SWITCH)
+                               return 0;
+
+                       /* smp->return_path set when received */
+                       smp->hop_ptr++;
+                       return (smp->initial_path[smp->hop_ptr] ==
+                               port_num);
+               }
+
+               /* C14-9:3 -- We're at the end of the DR segment of path */
+               if (hop_ptr == hop_cnt) {
+                       /* smp->return_path set when received */
+                       smp->hop_ptr++;
+                       return (node_type == IB_NODE_SWITCH ||
+                               smp->dr_dlid == IB_LID_PERMISSIVE);
+               }
+
+               /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
+               /* C14-9:5 -- Fail unreasonable hop pointer */
+               return (hop_ptr == hop_cnt + 1);
+
+       } else {
+               /* C14-13:1 */
+               if (hop_cnt && hop_ptr == hop_cnt + 1) {
+                       smp->hop_ptr--;
+                       return (smp->return_path[smp->hop_ptr] ==
+                               port_num);
+               }
+
+               /* C14-13:2 */
+               if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
+                       if (node_type != IB_NODE_SWITCH)
+                               return 0;
+
+                       smp->hop_ptr--;
+                       return (smp->return_path[smp->hop_ptr] ==
+                               port_num);
+               }
+
+               /* C14-13:3 -- at the end of the DR segment of path */
+               if (hop_ptr == 1) {
+                       smp->hop_ptr--;
+                       /* C14-13:3 -- SMPs destined for SM shouldn't be here */
+                       return (node_type == IB_NODE_SWITCH ||
+                               smp->dr_slid == IB_LID_PERMISSIVE);
+               }
+
+               /* C14-13:4 -- hop_ptr = 0 -> should have gone to SM */
+               if (hop_ptr == 0)
+                       return 1;
+
+               /* C14-13:5 -- Check for unreasonable hop pointer */
+               return 0;
+       }
+}
+
+/*
+ * Adjust information for a received SMP
+ * Return 0 if the SMP should be dropped
+ */
+int smi_handle_dr_smp_recv(struct ib_smp *smp,
+                          u8 node_type,
+                          int port_num,
+                          int phys_port_cnt)
+{
+       u8 hop_ptr, hop_cnt;
+
+       hop_ptr = smp->hop_ptr;
+       hop_cnt = smp->hop_cnt;
+
+       /* See section 14.2.2.2, Vol 1 IB spec */
+       if (!ib_get_smp_direction(smp)) {
+               /* C14-9:1 -- sender should have incremented hop_ptr */
+               if (hop_cnt && hop_ptr == 0)
+                       return 0;
+
+               /* C14-9:2 -- intermediate hop */
+               if (hop_ptr && hop_ptr < hop_cnt) {
+                       if (node_type != IB_NODE_SWITCH)
+                               return 0;
+
+                       smp->return_path[hop_ptr] = port_num;
+                       /* smp->hop_ptr updated when sending */
+                       return (smp->initial_path[hop_ptr+1] <= phys_port_cnt);
+               }
+
+               /* C14-9:3 -- We're at the end of the DR segment of path */
+               if (hop_ptr == hop_cnt) {
+                       if (hop_cnt)
+                               smp->return_path[hop_ptr] = port_num;
+                       /* smp->hop_ptr updated when sending */
+
+                       return (node_type == IB_NODE_SWITCH ||
+                               smp->dr_dlid == IB_LID_PERMISSIVE);
+               }
+
+               /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
+               /* C14-9:5 -- fail unreasonable hop pointer */
+               return (hop_ptr == hop_cnt + 1);
+
+       } else {
+
+               /* C14-13:1 */
+               if (hop_cnt && hop_ptr == hop_cnt + 1) {
+                       smp->hop_ptr--;
+                       return (smp->return_path[smp->hop_ptr] ==
+                               port_num);
+               }
+
+               /* C14-13:2 */
+               if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
+                       if (node_type != IB_NODE_SWITCH)
+                               return 0;
+
+                       /* smp->hop_ptr updated when sending */
+                       return (smp->return_path[hop_ptr-1] <= phys_port_cnt);
+               }
+
+               /* C14-13:3 -- We're at the end of the DR segment of path */
+               if (hop_ptr == 1) {
+                       if (smp->dr_slid == IB_LID_PERMISSIVE) {
+                               /* giving SMP to SM - update hop_ptr */
+                               smp->hop_ptr--;
+                               return 1;
+                       }
+                       /* smp->hop_ptr updated when sending */
+                       return (node_type == IB_NODE_SWITCH);
+               }
+
+               /* C14-13:4 -- hop_ptr = 0 -> give to SM */
+               /* C14-13:5 -- Check for unreasonable hop pointer */
+               return (hop_ptr == 0);
+       }
+}
+
+/*
+ * Return 1 if the received DR SMP should be forwarded to the send queue
+ * Return 0 if the SMP should be completed up the stack
+ */
+int smi_check_forward_dr_smp(struct ib_smp *smp)
+{
+       u8 hop_ptr, hop_cnt;
+
+       hop_ptr = smp->hop_ptr;
+       hop_cnt = smp->hop_cnt;
+
+       if (!ib_get_smp_direction(smp)) {
+               /* C14-9:2 -- intermediate hop */
+               if (hop_ptr && hop_ptr < hop_cnt)
+                       return 1;
+
+               /* C14-9:3 -- at the end of the DR segment of path */
+               if (hop_ptr == hop_cnt)
+                       return (smp->dr_dlid == IB_LID_PERMISSIVE);
+
+               /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
+               if (hop_ptr == hop_cnt + 1)
+                       return 1;
+       } else {
+               /* C14-13:2 */
+               if (2 <= hop_ptr && hop_ptr <= hop_cnt)
+                       return 1;
+
+               /* C14-13:3 -- at the end of the DR segment of path */
+               if (hop_ptr == 1)
+                       return (smp->dr_slid != IB_LID_PERMISSIVE);
+       }
+       return 0;
+}
diff --git a/drivers/infiniband/core/smi.h b/drivers/infiniband/core/smi.h
new file mode 100644 (file)
index 0000000..db25503
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
+ * Copyright (c) 2004 Infinicon Corporation.  All rights reserved.
+ * Copyright (c) 2004 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ * Copyright (c) 2004 Voltaire Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: smi.h 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#ifndef __SMI_H_
+#define __SMI_H_
+
+int smi_handle_dr_smp_recv(struct ib_smp *smp,
+                          u8 node_type,
+                          int port_num,
+                          int phys_port_cnt);
+extern int smi_check_forward_dr_smp(struct ib_smp *smp);
+extern int smi_handle_dr_smp_send(struct ib_smp *smp,
+                                 u8 node_type,
+                                 int port_num);
+extern int smi_check_local_dr_smp(struct ib_smp *smp,
+                                 struct ib_device *device,
+                                 int port_num);
+
+/*
+ * Return 1 if the SMP should be handled by the local SMA/SM via process_mad
+ */
+static inline int smi_check_local_smp(struct ib_mad_agent *mad_agent,
+                                     struct ib_smp *smp)
+{
+       /* C14-9:3 -- We're at the end of the DR segment of path */
+       /* C14-9:4 -- Hop Pointer = Hop Count + 1 -> give to SMA/SM */
+       return ((mad_agent->device->process_mad &&
+               !ib_get_smp_direction(smp) &&
+               (smp->hop_ptr == smp->hop_cnt + 1)));
+}
+
+#endif /* __SMI_H_ */
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
new file mode 100644 (file)
index 0000000..3a413f7
--- /dev/null
@@ -0,0 +1,762 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: sysfs.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include "core_priv.h"
+
+#include <ib_mad.h>
+
+struct ib_port {
+       struct kobject         kobj;
+       struct ib_device      *ibdev;
+       struct attribute_group gid_group;
+       struct attribute     **gid_attr;
+       struct attribute_group pkey_group;
+       struct attribute     **pkey_attr;
+       u8                     port_num;
+};
+
+struct port_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct ib_port *, struct port_attribute *, char *buf);
+       ssize_t (*store)(struct ib_port *, struct port_attribute *,
+                        const char *buf, size_t count);
+};
+
+#define PORT_ATTR(_name, _mode, _show, _store) \
+struct port_attribute port_attr_##_name = __ATTR(_name, _mode, _show, _store)
+
+#define PORT_ATTR_RO(_name) \
+struct port_attribute port_attr_##_name = __ATTR_RO(_name)
+
+struct port_table_attribute {
+       struct port_attribute attr;
+       int                   index;
+};
+
+static ssize_t port_attr_show(struct kobject *kobj,
+                             struct attribute *attr, char *buf)
+{
+       struct port_attribute *port_attr =
+               container_of(attr, struct port_attribute, attr);
+       struct ib_port *p = container_of(kobj, struct ib_port, kobj);
+
+       if (!port_attr->show)
+               return 0;
+
+       return port_attr->show(p, port_attr, buf);
+}
+
+static struct sysfs_ops port_sysfs_ops = {
+       .show = port_attr_show
+};
+
+static ssize_t state_show(struct ib_port *p, struct port_attribute *unused,
+                         char *buf)
+{
+       struct ib_port_attr attr;
+       ssize_t ret;
+
+       static const char *state_name[] = {
+               [IB_PORT_NOP]           = "NOP",
+               [IB_PORT_DOWN]          = "DOWN",
+               [IB_PORT_INIT]          = "INIT",
+               [IB_PORT_ARMED]         = "ARMED",
+               [IB_PORT_ACTIVE]        = "ACTIVE",
+               [IB_PORT_ACTIVE_DEFER]  = "ACTIVE_DEFER"
+       };
+
+       ret = ib_query_port(p->ibdev, p->port_num, &attr);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%d: %s\n", attr.state,
+                      attr.state >= 0 && attr.state <= ARRAY_SIZE(state_name) ?
+                      state_name[attr.state] : "UNKNOWN");
+}
+
+static ssize_t lid_show(struct ib_port *p, struct port_attribute *unused,
+                       char *buf)
+{
+       struct ib_port_attr attr;
+       ssize_t ret;
+
+       ret = ib_query_port(p->ibdev, p->port_num, &attr);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "0x%x\n", attr.lid);
+}
+
+static ssize_t lid_mask_count_show(struct ib_port *p,
+                                  struct port_attribute *unused,
+                                  char *buf)
+{
+       struct ib_port_attr attr;
+       ssize_t ret;
+
+       ret = ib_query_port(p->ibdev, p->port_num, &attr);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%d\n", attr.lmc);
+}
+
+static ssize_t sm_lid_show(struct ib_port *p, struct port_attribute *unused,
+                          char *buf)
+{
+       struct ib_port_attr attr;
+       ssize_t ret;
+
+       ret = ib_query_port(p->ibdev, p->port_num, &attr);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "0x%x\n", attr.sm_lid);
+}
+
+static ssize_t sm_sl_show(struct ib_port *p, struct port_attribute *unused,
+                         char *buf)
+{
+       struct ib_port_attr attr;
+       ssize_t ret;
+
+       ret = ib_query_port(p->ibdev, p->port_num, &attr);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%d\n", attr.sm_sl);
+}
+
+static ssize_t cap_mask_show(struct ib_port *p, struct port_attribute *unused,
+                            char *buf)
+{
+       struct ib_port_attr attr;
+       ssize_t ret;
+
+       ret = ib_query_port(p->ibdev, p->port_num, &attr);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "0x%08x\n", attr.port_cap_flags);
+}
+
+static ssize_t rate_show(struct ib_port *p, struct port_attribute *unused,
+                        char *buf)
+{
+       struct ib_port_attr attr;
+       char *speed = "";
+       int rate;
+       ssize_t ret;
+
+       ret = ib_query_port(p->ibdev, p->port_num, &attr);
+       if (ret)
+               return ret;
+
+       switch (attr.active_speed) {
+       case 2: speed = " DDR"; break;
+       case 4: speed = " QDR"; break;
+       }
+
+       rate = 25 * ib_width_enum_to_int(attr.active_width) * attr.active_speed;
+       if (rate < 0)
+               return -EINVAL;
+
+       return sprintf(buf, "%d%s Gb/sec (%dX%s)\n",
+                      rate / 10, rate % 10 ? ".5" : "",
+                      ib_width_enum_to_int(attr.active_width), speed);
+}
+
+static ssize_t phys_state_show(struct ib_port *p, struct port_attribute *unused,
+                              char *buf)
+{
+       struct ib_port_attr attr;
+
+       ssize_t ret;
+
+       ret = ib_query_port(p->ibdev, p->port_num, &attr);
+       if (ret)
+               return ret;
+
+       switch (attr.phys_state) {
+       case 1:  return sprintf(buf, "1: Sleep\n");
+       case 2:  return sprintf(buf, "2: Polling\n");
+       case 3:  return sprintf(buf, "3: Disabled\n");
+       case 4:  return sprintf(buf, "4: PortConfigurationTraining\n");
+       case 5:  return sprintf(buf, "5: LinkUp\n");
+       case 6:  return sprintf(buf, "6: LinkErrorRecovery\n");
+       case 7:  return sprintf(buf, "7: Phy Test\n");
+       default: return sprintf(buf, "%d: <unknown>\n", attr.phys_state);
+       }
+}
+
+static PORT_ATTR_RO(state);
+static PORT_ATTR_RO(lid);
+static PORT_ATTR_RO(lid_mask_count);
+static PORT_ATTR_RO(sm_lid);
+static PORT_ATTR_RO(sm_sl);
+static PORT_ATTR_RO(cap_mask);
+static PORT_ATTR_RO(rate);
+static PORT_ATTR_RO(phys_state);
+
+static struct attribute *port_default_attrs[] = {
+       &port_attr_state.attr,
+       &port_attr_lid.attr,
+       &port_attr_lid_mask_count.attr,
+       &port_attr_sm_lid.attr,
+       &port_attr_sm_sl.attr,
+       &port_attr_cap_mask.attr,
+       &port_attr_rate.attr,
+       &port_attr_phys_state.attr,
+       NULL
+};
+
+static ssize_t show_port_gid(struct ib_port *p, struct port_attribute *attr,
+                            char *buf)
+{
+       struct port_table_attribute *tab_attr =
+               container_of(attr, struct port_table_attribute, attr);
+       union ib_gid gid;
+       ssize_t ret;
+
+       ret = ib_query_gid(p->ibdev, p->port_num, tab_attr->index, &gid);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+                      be16_to_cpu(((u16 *) gid.raw)[0]),
+                      be16_to_cpu(((u16 *) gid.raw)[1]),
+                      be16_to_cpu(((u16 *) gid.raw)[2]),
+                      be16_to_cpu(((u16 *) gid.raw)[3]),
+                      be16_to_cpu(((u16 *) gid.raw)[4]),
+                      be16_to_cpu(((u16 *) gid.raw)[5]),
+                      be16_to_cpu(((u16 *) gid.raw)[6]),
+                      be16_to_cpu(((u16 *) gid.raw)[7]));
+}
+
+static ssize_t show_port_pkey(struct ib_port *p, struct port_attribute *attr,
+                             char *buf)
+{
+       struct port_table_attribute *tab_attr =
+               container_of(attr, struct port_table_attribute, attr);
+       u16 pkey;
+       ssize_t ret;
+
+       ret = ib_query_pkey(p->ibdev, p->port_num, tab_attr->index, &pkey);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "0x%04x\n", pkey);
+}
+
+#define PORT_PMA_ATTR(_name, _counter, _width, _offset)                        \
+struct port_table_attribute port_pma_attr_##_name = {                  \
+       .attr  = __ATTR(_name, S_IRUGO, show_pma_counter, NULL),        \
+       .index = (_offset) | ((_width) << 16) | ((_counter) << 24)      \
+}
+
+static ssize_t show_pma_counter(struct ib_port *p, struct port_attribute *attr,
+                               char *buf)
+{
+       struct port_table_attribute *tab_attr =
+               container_of(attr, struct port_table_attribute, attr);
+       int offset = tab_attr->index & 0xffff;
+       int width  = (tab_attr->index >> 16) & 0xff;
+       struct ib_mad *in_mad  = NULL;
+       struct ib_mad *out_mad = NULL;
+       ssize_t ret;
+
+       if (!p->ibdev->process_mad)
+               return sprintf(buf, "N/A (no PMA)\n");
+
+       in_mad  = kmalloc(sizeof *in_mad, GFP_KERNEL);
+       out_mad = kmalloc(sizeof *in_mad, GFP_KERNEL);
+       if (!in_mad || !out_mad) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       memset(in_mad, 0, sizeof *in_mad);
+       in_mad->mad_hdr.base_version  = 1;
+       in_mad->mad_hdr.mgmt_class    = IB_MGMT_CLASS_PERF_MGMT;
+       in_mad->mad_hdr.class_version = 1;
+       in_mad->mad_hdr.method        = IB_MGMT_METHOD_GET;
+       in_mad->mad_hdr.attr_id       = cpu_to_be16(0x12); /* PortCounters */
+
+       in_mad->data[41] = p->port_num; /* PortSelect field */
+
+       if ((p->ibdev->process_mad(p->ibdev, IB_MAD_IGNORE_MKEY,
+                p->port_num, NULL, NULL, in_mad, out_mad) &
+            (IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY)) !=
+           (IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       switch (width) {
+       case 4:
+               ret = sprintf(buf, "%u\n", (out_mad->data[40 + offset / 8] >>
+                                           (offset % 4)) & 0xf);
+               break;
+       case 8:
+               ret = sprintf(buf, "%u\n", out_mad->data[40 + offset / 8]);
+               break;
+       case 16:
+               ret = sprintf(buf, "%u\n",
+                             be16_to_cpup((u16 *)(out_mad->data + 40 + offset / 8)));
+               break;
+       case 32:
+               ret = sprintf(buf, "%u\n",
+                             be32_to_cpup((u32 *)(out_mad->data + 40 + offset / 8)));
+               break;
+       default:
+               ret = 0;
+       }
+
+out:
+       kfree(in_mad);
+       kfree(out_mad);
+
+       return ret;
+}
+
+static PORT_PMA_ATTR(symbol_error                  ,  0, 16,  32);
+static PORT_PMA_ATTR(link_error_recovery           ,  1,  8,  48);
+static PORT_PMA_ATTR(link_downed                   ,  2,  8,  56);
+static PORT_PMA_ATTR(port_rcv_errors               ,  3, 16,  64);
+static PORT_PMA_ATTR(port_rcv_remote_physical_errors,  4, 16,  80);
+static PORT_PMA_ATTR(port_rcv_switch_relay_errors   ,  5, 16,  96);
+static PORT_PMA_ATTR(port_xmit_discards                    ,  6, 16, 112);
+static PORT_PMA_ATTR(port_xmit_constraint_errors    ,  7,  8, 128);
+static PORT_PMA_ATTR(port_rcv_constraint_errors            ,  8,  8, 136);
+static PORT_PMA_ATTR(local_link_integrity_errors    ,  9,  4, 152);
+static PORT_PMA_ATTR(excessive_buffer_overrun_errors, 10,  4, 156);
+static PORT_PMA_ATTR(VL15_dropped                  , 11, 16, 176);
+static PORT_PMA_ATTR(port_xmit_data                , 12, 32, 192);
+static PORT_PMA_ATTR(port_rcv_data                 , 13, 32, 224);
+static PORT_PMA_ATTR(port_xmit_packets             , 14, 32, 256);
+static PORT_PMA_ATTR(port_rcv_packets              , 15, 32, 288);
+
+static struct attribute *pma_attrs[] = {
+       &port_pma_attr_symbol_error.attr.attr,
+       &port_pma_attr_link_error_recovery.attr.attr,
+       &port_pma_attr_link_downed.attr.attr,
+       &port_pma_attr_port_rcv_errors.attr.attr,
+       &port_pma_attr_port_rcv_remote_physical_errors.attr.attr,
+       &port_pma_attr_port_rcv_switch_relay_errors.attr.attr,
+       &port_pma_attr_port_xmit_discards.attr.attr,
+       &port_pma_attr_port_xmit_constraint_errors.attr.attr,
+       &port_pma_attr_port_rcv_constraint_errors.attr.attr,
+       &port_pma_attr_local_link_integrity_errors.attr.attr,
+       &port_pma_attr_excessive_buffer_overrun_errors.attr.attr,
+       &port_pma_attr_VL15_dropped.attr.attr,
+       &port_pma_attr_port_xmit_data.attr.attr,
+       &port_pma_attr_port_rcv_data.attr.attr,
+       &port_pma_attr_port_xmit_packets.attr.attr,
+       &port_pma_attr_port_rcv_packets.attr.attr,
+       NULL
+};
+
+static struct attribute_group pma_group = {
+       .name  = "counters",
+       .attrs  = pma_attrs
+};
+
+static void ib_port_release(struct kobject *kobj)
+{
+       struct ib_port *p = container_of(kobj, struct ib_port, kobj);
+       struct attribute *a;
+       int i;
+
+       for (i = 0; (a = p->gid_attr[i]); ++i) {
+               kfree(a->name);
+               kfree(a);
+       }
+
+       for (i = 0; (a = p->pkey_attr[i]); ++i) {
+               kfree(a->name);
+               kfree(a);
+       }
+
+       kfree(p->gid_attr);
+       kfree(p);
+}
+
+static struct kobj_type port_type = {
+       .release       = ib_port_release,
+       .sysfs_ops     = &port_sysfs_ops,
+       .default_attrs = port_default_attrs
+};
+
+static void ib_device_release(struct class_device *cdev)
+{
+       struct ib_device *dev = container_of(cdev, struct ib_device, class_dev);
+
+       kfree(dev);
+}
+
+static int ib_device_hotplug(struct class_device *cdev, char **envp,
+                            int num_envp, char *buf, int size)
+{
+       struct ib_device *dev = container_of(cdev, struct ib_device, class_dev);
+       int i = 0, len = 0;
+
+       if (add_hotplug_env_var(envp, num_envp, &i, buf, size, &len,
+                               "NAME=%s", dev->name))
+               return -ENOMEM;
+
+       /*
+        * It might be nice to pass the node GUID to hotplug, but
+        * right now the only way to get it is to query the device
+        * provider, and this can crash during device removal because
+        * we are will be running after driver removal has started.
+        * We could add a node_guid field to struct ib_device, or we
+        * could just let the hotplug script read the node GUID from
+        * sysfs when devices are added.
+        */
+
+       envp[i] = NULL;
+       return 0;
+}
+
+static int alloc_group(struct attribute ***attr,
+                      ssize_t (*show)(struct ib_port *,
+                                      struct port_attribute *, char *buf),
+                      int len)
+{
+       struct port_table_attribute ***tab_attr =
+               (struct port_table_attribute ***) attr;
+       int i;
+       int ret;
+
+       *tab_attr = kmalloc((1 + len) * sizeof *tab_attr, GFP_KERNEL);
+       if (!*tab_attr)
+               return -ENOMEM;
+
+       memset(*tab_attr, 0, (1 + len) * sizeof *tab_attr);
+
+       for (i = 0; i < len; ++i) {
+               (*tab_attr)[i] = kmalloc(sizeof *(*tab_attr)[i], GFP_KERNEL);
+               if (!(*tab_attr)[i]) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+               memset((*tab_attr)[i], 0, sizeof *(*tab_attr)[i]);
+               (*tab_attr)[i]->attr.attr.name = kmalloc(8, GFP_KERNEL);
+               if (!(*tab_attr)[i]->attr.attr.name) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+
+               if (snprintf((*tab_attr)[i]->attr.attr.name, 8, "%d", i) >= 8) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+
+               (*tab_attr)[i]->attr.attr.mode  = S_IRUGO;
+               (*tab_attr)[i]->attr.attr.owner = THIS_MODULE;
+               (*tab_attr)[i]->attr.show       = show;
+               (*tab_attr)[i]->index           = i;
+       }
+
+       return 0;
+
+err:
+       for (i = 0; i < len; ++i) {
+               if ((*tab_attr)[i])
+                       kfree((*tab_attr)[i]->attr.attr.name);
+               kfree((*tab_attr)[i]);
+       }
+
+       kfree(*tab_attr);
+
+       return ret;
+}
+
+static int add_port(struct ib_device *device, int port_num)
+{
+       struct ib_port *p;
+       struct ib_port_attr attr;
+       int i;
+       int ret;
+
+       ret = ib_query_port(device, port_num, &attr);
+       if (ret)
+               return ret;
+
+       p = kmalloc(sizeof *p, GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+       memset(p, 0, sizeof *p);
+
+       p->ibdev      = device;
+       p->port_num   = port_num;
+       p->kobj.ktype = &port_type;
+
+       p->kobj.parent = kobject_get(&device->ports_parent);
+       if (!p->kobj.parent) {
+               ret = -EBUSY;
+               goto err;
+       }
+
+       ret = kobject_set_name(&p->kobj, "%d", port_num);
+       if (ret)
+               goto err_put;
+
+       ret = kobject_register(&p->kobj);
+       if (ret)
+               goto err_put;
+
+       ret = sysfs_create_group(&p->kobj, &pma_group);
+       if (ret)
+               goto err_put;
+
+       ret = alloc_group(&p->gid_attr, show_port_gid, attr.gid_tbl_len);
+       if (ret)
+               goto err_remove_pma;
+
+       p->gid_group.name  = "gids";
+       p->gid_group.attrs = p->gid_attr;
+
+       ret = sysfs_create_group(&p->kobj, &p->gid_group);
+       if (ret)
+               goto err_free_gid;
+
+       ret = alloc_group(&p->pkey_attr, show_port_pkey, attr.pkey_tbl_len);
+       if (ret)
+               goto err_remove_gid;
+
+       p->pkey_group.name  = "pkeys";
+       p->pkey_group.attrs = p->pkey_attr;
+
+       ret = sysfs_create_group(&p->kobj, &p->pkey_group);
+       if (ret)
+               goto err_free_pkey;
+
+       list_add_tail(&p->kobj.entry, &device->port_list);
+
+       return 0;
+
+err_free_pkey:
+       for (i = 0; i < attr.pkey_tbl_len; ++i) {
+               kfree(p->pkey_attr[i]->name);
+               kfree(p->pkey_attr[i]);
+       }
+
+       kfree(p->pkey_attr);
+
+err_remove_gid:
+       sysfs_remove_group(&p->kobj, &p->gid_group);
+
+err_free_gid:
+       for (i = 0; i < attr.gid_tbl_len; ++i) {
+               kfree(p->gid_attr[i]->name);
+               kfree(p->gid_attr[i]);
+       }
+
+       kfree(p->gid_attr);
+
+err_remove_pma:
+       sysfs_remove_group(&p->kobj, &pma_group);
+
+err_put:
+       kobject_put(&device->ports_parent);
+
+err:
+       kfree(p);
+       return ret;
+}
+
+static ssize_t show_node_type(struct class_device *cdev, char *buf)
+{
+       struct ib_device *dev = container_of(cdev, struct ib_device, class_dev);
+
+       switch (dev->node_type) {
+       case IB_NODE_CA:     return sprintf(buf, "%d: CA\n", dev->node_type);
+       case IB_NODE_SWITCH: return sprintf(buf, "%d: switch\n", dev->node_type);
+       case IB_NODE_ROUTER: return sprintf(buf, "%d: router\n", dev->node_type);
+       default:             return sprintf(buf, "%d: <unknown>\n", dev->node_type);
+       }
+}
+
+static ssize_t show_sys_image_guid(struct class_device *cdev, char *buf)
+{
+       struct ib_device *dev = container_of(cdev, struct ib_device, class_dev);
+       struct ib_device_attr attr;
+       ssize_t ret;
+
+       ret = ib_query_device(dev, &attr);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%04x:%04x:%04x:%04x\n",
+                      be16_to_cpu(((u16 *) &attr.sys_image_guid)[0]),
+                      be16_to_cpu(((u16 *) &attr.sys_image_guid)[1]),
+                      be16_to_cpu(((u16 *) &attr.sys_image_guid)[2]),
+                      be16_to_cpu(((u16 *) &attr.sys_image_guid)[3]));
+}
+
+static ssize_t show_node_guid(struct class_device *cdev, char *buf)
+{
+       struct ib_device *dev = container_of(cdev, struct ib_device, class_dev);
+       struct ib_device_attr attr;
+       ssize_t ret;
+
+       ret = ib_query_device(dev, &attr);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%04x:%04x:%04x:%04x\n",
+                      be16_to_cpu(((u16 *) &attr.node_guid)[0]),
+                      be16_to_cpu(((u16 *) &attr.node_guid)[1]),
+                      be16_to_cpu(((u16 *) &attr.node_guid)[2]),
+                      be16_to_cpu(((u16 *) &attr.node_guid)[3]));
+}
+
+static CLASS_DEVICE_ATTR(node_type, S_IRUGO, show_node_type, NULL);
+static CLASS_DEVICE_ATTR(sys_image_guid, S_IRUGO, show_sys_image_guid, NULL);
+static CLASS_DEVICE_ATTR(node_guid, S_IRUGO, show_node_guid, NULL);
+
+static struct class_device_attribute *ib_class_attributes[] = {
+       &class_device_attr_node_type,
+       &class_device_attr_sys_image_guid,
+       &class_device_attr_node_guid
+};
+
+static struct class ib_class = {
+       .name    = "infiniband",
+       .release = ib_device_release,
+       .hotplug = ib_device_hotplug,
+};
+
+int ib_device_register_sysfs(struct ib_device *device)
+{
+       struct class_device *class_dev = &device->class_dev;
+       int ret;
+       int i;
+
+       class_dev->class      = &ib_class;
+       class_dev->class_data = device;
+       strlcpy(class_dev->class_id, device->name, BUS_ID_SIZE);
+
+       INIT_LIST_HEAD(&device->port_list);
+
+       ret = class_device_register(class_dev);
+       if (ret)
+               goto err;
+
+       for (i = 0; i < ARRAY_SIZE(ib_class_attributes); ++i) {
+               ret = class_device_create_file(class_dev, ib_class_attributes[i]);
+               if (ret)
+                       goto err_unregister;
+       }
+
+       device->ports_parent.parent = kobject_get(&class_dev->kobj);
+       if (!device->ports_parent.parent) {
+               ret = -EBUSY;
+               goto err_unregister;
+       }
+       ret = kobject_set_name(&device->ports_parent, "ports");
+       if (ret)
+               goto err_put;
+       ret = kobject_register(&device->ports_parent);
+       if (ret)
+               goto err_put;
+
+       if (device->node_type == IB_NODE_SWITCH) {
+               ret = add_port(device, 0);
+               if (ret)
+                       goto err_put;
+       } else {
+               int i;
+
+               for (i = 1; i <= device->phys_port_cnt; ++i) {
+                       ret = add_port(device, i);
+                       if (ret)
+                               goto err_put;
+               }
+       }
+
+       return 0;
+
+err_put:
+       {
+               struct kobject *p, *t;
+               struct ib_port *port;
+
+               list_for_each_entry_safe(p, t, &device->port_list, entry) {
+                       list_del(&p->entry);
+                       port = container_of(p, struct ib_port, kobj);
+                       sysfs_remove_group(p, &pma_group);
+                       sysfs_remove_group(p, &port->pkey_group);
+                       sysfs_remove_group(p, &port->gid_group);
+                       kobject_unregister(p);
+               }
+       }
+
+       kobject_put(&class_dev->kobj);
+
+err_unregister:
+       class_device_unregister(class_dev);
+
+err:
+       return ret;
+}
+
+void ib_device_unregister_sysfs(struct ib_device *device)
+{
+       struct kobject *p, *t;
+       struct ib_port *port;
+
+       list_for_each_entry_safe(p, t, &device->port_list, entry) {
+               list_del(&p->entry);
+               port = container_of(p, struct ib_port, kobj);
+               sysfs_remove_group(p, &pma_group);
+               sysfs_remove_group(p, &port->pkey_group);
+               sysfs_remove_group(p, &port->gid_group);
+               kobject_unregister(p);
+       }
+
+       kobject_unregister(&device->ports_parent);
+       class_device_unregister(&device->class_dev);
+}
+
+int ib_sysfs_setup(void)
+{
+       return class_register(&ib_class);
+}
+
+void ib_sysfs_cleanup(void)
+{
+       class_unregister(&ib_class);
+}
diff --git a/drivers/infiniband/core/ud_header.c b/drivers/infiniband/core/ud_header.c
new file mode 100644 (file)
index 0000000..dc4eb1d
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ud_header.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/errno.h>
+
+#include <ib_pack.h>
+
+#define STRUCT_FIELD(header, field) \
+       .struct_offset_bytes = offsetof(struct ib_unpacked_ ## header, field),      \
+       .struct_size_bytes   = sizeof ((struct ib_unpacked_ ## header *) 0)->field, \
+       .field_name          = #header ":" #field
+
+static const struct ib_field lrh_table[]  = {
+       { STRUCT_FIELD(lrh, virtual_lane),
+         .offset_words = 0,
+         .offset_bits  = 0,
+         .size_bits    = 4 },
+       { STRUCT_FIELD(lrh, link_version),
+         .offset_words = 0,
+         .offset_bits  = 4,
+         .size_bits    = 4 },
+       { STRUCT_FIELD(lrh, service_level),
+         .offset_words = 0,
+         .offset_bits  = 8,
+         .size_bits    = 4 },
+       { RESERVED,
+         .offset_words = 0,
+         .offset_bits  = 12,
+         .size_bits    = 2 },
+       { STRUCT_FIELD(lrh, link_next_header),
+         .offset_words = 0,
+         .offset_bits  = 14,
+         .size_bits    = 2 },
+       { STRUCT_FIELD(lrh, destination_lid),
+         .offset_words = 0,
+         .offset_bits  = 16,
+         .size_bits    = 16 },
+       { RESERVED,
+         .offset_words = 1,
+         .offset_bits  = 0,
+         .size_bits    = 5 },
+       { STRUCT_FIELD(lrh, packet_length),
+         .offset_words = 1,
+         .offset_bits  = 5,
+         .size_bits    = 11 },
+       { STRUCT_FIELD(lrh, source_lid),
+         .offset_words = 1,
+         .offset_bits  = 16,
+         .size_bits    = 16 }
+};
+
+static const struct ib_field grh_table[]  = {
+       { STRUCT_FIELD(grh, ip_version),
+         .offset_words = 0,
+         .offset_bits  = 0,
+         .size_bits    = 4 },
+       { STRUCT_FIELD(grh, traffic_class),
+         .offset_words = 0,
+         .offset_bits  = 4,
+         .size_bits    = 8 },
+       { STRUCT_FIELD(grh, flow_label),
+         .offset_words = 0,
+         .offset_bits  = 12,
+         .size_bits    = 20 },
+       { STRUCT_FIELD(grh, payload_length),
+         .offset_words = 1,
+         .offset_bits  = 0,
+         .size_bits    = 16 },
+       { STRUCT_FIELD(grh, next_header),
+         .offset_words = 1,
+         .offset_bits  = 16,
+         .size_bits    = 8 },
+       { STRUCT_FIELD(grh, hop_limit),
+         .offset_words = 1,
+         .offset_bits  = 24,
+         .size_bits    = 8 },
+       { STRUCT_FIELD(grh, source_gid),
+         .offset_words = 2,
+         .offset_bits  = 0,
+         .size_bits    = 128 },
+       { STRUCT_FIELD(grh, destination_gid),
+         .offset_words = 6,
+         .offset_bits  = 0,
+         .size_bits    = 128 }
+};
+
+static const struct ib_field bth_table[]  = {
+       { STRUCT_FIELD(bth, opcode),
+         .offset_words = 0,
+         .offset_bits  = 0,
+         .size_bits    = 8 },
+       { STRUCT_FIELD(bth, solicited_event),
+         .offset_words = 0,
+         .offset_bits  = 8,
+         .size_bits    = 1 },
+       { STRUCT_FIELD(bth, mig_req),
+         .offset_words = 0,
+         .offset_bits  = 9,
+         .size_bits    = 1 },
+       { STRUCT_FIELD(bth, pad_count),
+         .offset_words = 0,
+         .offset_bits  = 10,
+         .size_bits    = 2 },
+       { STRUCT_FIELD(bth, transport_header_version),
+         .offset_words = 0,
+         .offset_bits  = 12,
+         .size_bits    = 4 },
+       { STRUCT_FIELD(bth, pkey),
+         .offset_words = 0,
+         .offset_bits  = 16,
+         .size_bits    = 16 },
+       { RESERVED,
+         .offset_words = 1,
+         .offset_bits  = 0,
+         .size_bits    = 8 },
+       { STRUCT_FIELD(bth, destination_qpn),
+         .offset_words = 1,
+         .offset_bits  = 8,
+         .size_bits    = 24 },
+       { STRUCT_FIELD(bth, ack_req),
+         .offset_words = 2,
+         .offset_bits  = 0,
+         .size_bits    = 1 },
+       { RESERVED,
+         .offset_words = 2,
+         .offset_bits  = 1,
+         .size_bits    = 7 },
+       { STRUCT_FIELD(bth, psn),
+         .offset_words = 2,
+         .offset_bits  = 8,
+         .size_bits    = 24 }
+};
+
+static const struct ib_field deth_table[] = {
+       { STRUCT_FIELD(deth, qkey),
+         .offset_words = 0,
+         .offset_bits  = 0,
+         .size_bits    = 32 },
+       { RESERVED,
+         .offset_words = 1,
+         .offset_bits  = 0,
+         .size_bits    = 8 },
+       { STRUCT_FIELD(deth, source_qpn),
+         .offset_words = 1,
+         .offset_bits  = 8,
+         .size_bits    = 24 }
+};
+
+/**
+ * ib_ud_header_init - Initialize UD header structure
+ * @payload_bytes:Length of packet payload
+ * @grh_present:GRH flag (if non-zero, GRH will be included)
+ * @header:Structure to initialize
+ *
+ * ib_ud_header_init() initializes the lrh.link_version, lrh.link_next_header,
+ * lrh.packet_length, grh.ip_version, grh.payload_length,
+ * grh.next_header, bth.opcode, bth.pad_count and
+ * bth.transport_header_version fields of a &struct ib_ud_header given
+ * the payload length and whether a GRH will be included.
+ */
+void ib_ud_header_init(int                         payload_bytes,
+                      int                  grh_present,
+                      struct ib_ud_header *header)
+{
+       int header_len;
+
+       memset(header, 0, sizeof *header);
+
+       header_len =
+               IB_LRH_BYTES  +
+               IB_BTH_BYTES  +
+               IB_DETH_BYTES;
+       if (grh_present) {
+               header_len += IB_GRH_BYTES;
+       }
+
+       header->lrh.link_version     = 0;
+       header->lrh.link_next_header =
+               grh_present ? IB_LNH_IBA_GLOBAL : IB_LNH_IBA_LOCAL;
+       header->lrh.packet_length    = (IB_LRH_BYTES     +
+                                       IB_BTH_BYTES     +
+                                       IB_DETH_BYTES    +
+                                       payload_bytes    +
+                                       4                + /* ICRC     */
+                                       3) / 4;            /* round up */
+
+       header->grh_present          = grh_present;
+       if (grh_present) {
+               header->lrh.packet_length  += IB_GRH_BYTES / 4;
+
+               header->grh.ip_version      = 6;
+               header->grh.payload_length  =
+                       cpu_to_be16((IB_BTH_BYTES     +
+                                    IB_DETH_BYTES    +
+                                    payload_bytes    +
+                                    4                + /* ICRC     */
+                                    3) & ~3);          /* round up */
+               header->grh.next_header     = 0x1b;
+       }
+
+       cpu_to_be16s(&header->lrh.packet_length);
+
+       if (header->immediate_present)
+               header->bth.opcode           = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE;
+       else
+               header->bth.opcode           = IB_OPCODE_UD_SEND_ONLY;
+       header->bth.pad_count                = (4 - payload_bytes) & 3;
+       header->bth.transport_header_version = 0;
+}
+EXPORT_SYMBOL(ib_ud_header_init);
+
+/**
+ * ib_ud_header_pack - Pack UD header struct into wire format
+ * @header:UD header struct
+ * @buf:Buffer to pack into
+ *
+ * ib_ud_header_pack() packs the UD header structure @header into wire
+ * format in the buffer @buf.
+ */
+int ib_ud_header_pack(struct ib_ud_header *header,
+                     void                *buf)
+{
+       int len = 0;
+
+       ib_pack(lrh_table, ARRAY_SIZE(lrh_table),
+               &header->lrh, buf);
+       len += IB_LRH_BYTES;
+
+       if (header->grh_present) {
+               ib_pack(grh_table, ARRAY_SIZE(grh_table),
+                       &header->grh, buf + len);
+               len += IB_GRH_BYTES;
+       }
+
+       ib_pack(bth_table, ARRAY_SIZE(bth_table),
+               &header->bth, buf + len);
+       len += IB_BTH_BYTES;
+
+       ib_pack(deth_table, ARRAY_SIZE(deth_table),
+               &header->deth, buf + len);
+       len += IB_DETH_BYTES;
+
+       if (header->immediate_present) {
+               memcpy(buf + len, &header->immediate_data, sizeof header->immediate_data);
+               len += sizeof header->immediate_data;
+       }
+
+       return len;
+}
+EXPORT_SYMBOL(ib_ud_header_pack);
+
+/**
+ * ib_ud_header_unpack - Unpack UD header struct from wire format
+ * @header:UD header struct
+ * @buf:Buffer to pack into
+ *
+ * ib_ud_header_pack() unpacks the UD header structure @header from wire
+ * format in the buffer @buf.
+ */
+int ib_ud_header_unpack(void                *buf,
+                       struct ib_ud_header *header)
+{
+       ib_unpack(lrh_table, ARRAY_SIZE(lrh_table),
+                 buf, &header->lrh);
+       buf += IB_LRH_BYTES;
+
+       if (header->lrh.link_version != 0) {
+               printk(KERN_WARNING "Invalid LRH.link_version %d\n",
+                      header->lrh.link_version);
+               return -EINVAL;
+       }
+
+       switch (header->lrh.link_next_header) {
+       case IB_LNH_IBA_LOCAL:
+               header->grh_present = 0;
+               break;
+
+       case IB_LNH_IBA_GLOBAL:
+               header->grh_present = 1;
+               ib_unpack(grh_table, ARRAY_SIZE(grh_table),
+                         buf, &header->grh);
+               buf += IB_GRH_BYTES;
+
+               if (header->grh.ip_version != 6) {
+                       printk(KERN_WARNING "Invalid GRH.ip_version %d\n",
+                              header->grh.ip_version);
+                       return -EINVAL;
+               }
+               if (header->grh.next_header != 0x1b) {
+                       printk(KERN_WARNING "Invalid GRH.next_header 0x%02x\n",
+                              header->grh.next_header);
+                       return -EINVAL;
+               }
+               break;
+
+       default:
+               printk(KERN_WARNING "Invalid LRH.link_next_header %d\n",
+                      header->lrh.link_next_header);
+               return -EINVAL;
+       }
+
+       ib_unpack(bth_table, ARRAY_SIZE(bth_table),
+                 buf, &header->bth);
+       buf += IB_BTH_BYTES;
+
+       switch (header->bth.opcode) {
+       case IB_OPCODE_UD_SEND_ONLY:
+               header->immediate_present = 0;
+               break;
+       case IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE:
+               header->immediate_present = 1;
+               break;
+       default:
+               printk(KERN_WARNING "Invalid BTH.opcode 0x%02x\n",
+                      header->bth.opcode);
+               return -EINVAL;
+       }
+
+       if (header->bth.transport_header_version != 0) {
+               printk(KERN_WARNING "Invalid BTH.transport_header_version %d\n",
+                      header->bth.transport_header_version);
+               return -EINVAL;
+       }
+
+       ib_unpack(deth_table, ARRAY_SIZE(deth_table),
+                 buf, &header->deth);
+       buf += IB_DETH_BYTES;
+
+       if (header->immediate_present)
+               memcpy(&header->immediate_data, buf, sizeof header->immediate_data);
+
+       return 0;
+}
+EXPORT_SYMBOL(ib_ud_header_unpack);
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
new file mode 100644 (file)
index 0000000..54b4f33
--- /dev/null
@@ -0,0 +1,840 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: user_mad.c 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/poll.h>
+#include <linux/rwsem.h>
+#include <linux/kref.h>
+
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+#include <ib_mad.h>
+#include <ib_user_mad.h>
+
+MODULE_AUTHOR("Roland Dreier");
+MODULE_DESCRIPTION("InfiniBand userspace MAD packet access");
+MODULE_LICENSE("Dual BSD/GPL");
+
+enum {
+       IB_UMAD_MAX_PORTS  = 64,
+       IB_UMAD_MAX_AGENTS = 32,
+
+       IB_UMAD_MAJOR      = 231,
+       IB_UMAD_MINOR_BASE = 0
+};
+
+struct ib_umad_port {
+       int                    devnum;
+       struct cdev            dev;
+       struct class_device    class_dev;
+
+       int                    sm_devnum;
+       struct cdev            sm_dev;
+       struct class_device    sm_class_dev;
+       struct semaphore       sm_sem;
+
+       struct ib_device      *ib_dev;
+       struct ib_umad_device *umad_dev;
+       u8                     port_num;
+};
+
+struct ib_umad_device {
+       int                  start_port, end_port;
+       struct kref          ref;
+       struct ib_umad_port  port[0];
+};
+
+struct ib_umad_file {
+       struct ib_umad_port *port;
+       spinlock_t           recv_lock;
+       struct list_head     recv_list;
+       wait_queue_head_t    recv_wait;
+       struct rw_semaphore  agent_mutex;
+       struct ib_mad_agent *agent[IB_UMAD_MAX_AGENTS];
+       struct ib_mr        *mr[IB_UMAD_MAX_AGENTS];
+};
+
+struct ib_umad_packet {
+       struct ib_user_mad mad;
+       struct ib_ah      *ah;
+       struct list_head   list;
+       DECLARE_PCI_UNMAP_ADDR(mapping)
+};
+
+static const dev_t base_dev = MKDEV(IB_UMAD_MAJOR, IB_UMAD_MINOR_BASE);
+static spinlock_t map_lock;
+static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS * 2);
+
+static void ib_umad_add_one(struct ib_device *device);
+static void ib_umad_remove_one(struct ib_device *device);
+
+static int queue_packet(struct ib_umad_file *file,
+                       struct ib_mad_agent *agent,
+                       struct ib_umad_packet *packet)
+{
+       int ret = 1;
+
+       down_read(&file->agent_mutex);
+       for (packet->mad.id = 0;
+            packet->mad.id < IB_UMAD_MAX_AGENTS;
+            packet->mad.id++)
+               if (agent == file->agent[packet->mad.id]) {
+                       spin_lock_irq(&file->recv_lock);
+                       list_add_tail(&packet->list, &file->recv_list);
+                       spin_unlock_irq(&file->recv_lock);
+                       wake_up_interruptible(&file->recv_wait);
+                       ret = 0;
+                       break;
+               }
+
+       up_read(&file->agent_mutex);
+
+       return ret;
+}
+
+static void send_handler(struct ib_mad_agent *agent,
+                        struct ib_mad_send_wc *send_wc)
+{
+       struct ib_umad_file *file = agent->context;
+       struct ib_umad_packet *packet =
+               (void *) (unsigned long) send_wc->wr_id;
+
+       dma_unmap_single(agent->device->dma_device,
+                        pci_unmap_addr(packet, mapping),
+                        sizeof packet->mad.data,
+                        DMA_TO_DEVICE);
+       ib_destroy_ah(packet->ah);
+
+       if (send_wc->status == IB_WC_RESP_TIMEOUT_ERR) {
+               packet->mad.status = ETIMEDOUT;
+
+               if (!queue_packet(file, agent, packet))
+                       return;
+       }
+
+       kfree(packet);
+}
+
+static void recv_handler(struct ib_mad_agent *agent,
+                        struct ib_mad_recv_wc *mad_recv_wc)
+{
+       struct ib_umad_file *file = agent->context;
+       struct ib_umad_packet *packet;
+
+       if (mad_recv_wc->wc->status != IB_WC_SUCCESS)
+               goto out;
+
+       packet = kmalloc(sizeof *packet, GFP_KERNEL);
+       if (!packet)
+               goto out;
+
+       memset(packet, 0, sizeof *packet);
+
+       memcpy(packet->mad.data, mad_recv_wc->recv_buf.mad, sizeof packet->mad.data);
+       packet->mad.status        = 0;
+       packet->mad.qpn           = cpu_to_be32(mad_recv_wc->wc->src_qp);
+       packet->mad.lid           = cpu_to_be16(mad_recv_wc->wc->slid);
+       packet->mad.sl            = mad_recv_wc->wc->sl;
+       packet->mad.path_bits     = mad_recv_wc->wc->dlid_path_bits;
+       packet->mad.grh_present   = !!(mad_recv_wc->wc->wc_flags & IB_WC_GRH);
+       if (packet->mad.grh_present) {
+               /* XXX parse GRH */
+               packet->mad.gid_index     = 0;
+               packet->mad.hop_limit     = 0;
+               packet->mad.traffic_class = 0;
+               memset(packet->mad.gid, 0, 16);
+               packet->mad.flow_label    = 0;
+       }
+
+       if (queue_packet(file, agent, packet))
+               kfree(packet);
+
+out:
+       ib_free_recv_mad(mad_recv_wc);
+}
+
+static ssize_t ib_umad_read(struct file *filp, char __user *buf,
+                           size_t count, loff_t *pos)
+{
+       struct ib_umad_file *file = filp->private_data;
+       struct ib_umad_packet *packet;
+       ssize_t ret;
+
+       if (count < sizeof (struct ib_user_mad))
+               return -EINVAL;
+
+       spin_lock_irq(&file->recv_lock);
+
+       while (list_empty(&file->recv_list)) {
+               spin_unlock_irq(&file->recv_lock);
+
+               if (filp->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+
+               if (wait_event_interruptible(file->recv_wait,
+                                            !list_empty(&file->recv_list)))
+                       return -ERESTARTSYS;
+
+               spin_lock_irq(&file->recv_lock);
+       }
+
+       packet = list_entry(file->recv_list.next, struct ib_umad_packet, list);
+       list_del(&packet->list);
+
+       spin_unlock_irq(&file->recv_lock);
+
+       if (copy_to_user(buf, &packet->mad, sizeof packet->mad))
+               ret = -EFAULT;
+       else
+               ret = sizeof packet->mad;
+
+       kfree(packet);
+       return ret;
+}
+
+static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
+                            size_t count, loff_t *pos)
+{
+       struct ib_umad_file *file = filp->private_data;
+       struct ib_umad_packet *packet;
+       struct ib_mad_agent *agent;
+       struct ib_ah_attr ah_attr;
+       struct ib_sge      gather_list;
+       struct ib_send_wr *bad_wr, wr = {
+               .opcode      = IB_WR_SEND,
+               .sg_list     = &gather_list,
+               .num_sge     = 1,
+               .send_flags  = IB_SEND_SIGNALED,
+       };
+       u8 method;
+       u64 *tid;
+       int ret;
+
+       if (count < sizeof (struct ib_user_mad))
+               return -EINVAL;
+
+       packet = kmalloc(sizeof *packet, GFP_KERNEL);
+       if (!packet)
+               return -ENOMEM;
+
+       if (copy_from_user(&packet->mad, buf, sizeof packet->mad)) {
+               kfree(packet);
+               return -EFAULT;
+       }
+
+       if (packet->mad.id < 0 || packet->mad.id >= IB_UMAD_MAX_AGENTS) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       down_read(&file->agent_mutex);
+
+       agent = file->agent[packet->mad.id];
+       if (!agent) {
+               ret = -EINVAL;
+               goto err_up;
+       }
+
+       /*
+        * If userspace is generating a request that will generate a
+        * response, we need to make sure the high-order part of the
+        * transaction ID matches the agent being used to send the
+        * MAD.
+        */
+       method = ((struct ib_mad_hdr *) packet->mad.data)->method;
+
+       if (!(method & IB_MGMT_METHOD_RESP)       &&
+           method != IB_MGMT_METHOD_TRAP_REPRESS &&
+           method != IB_MGMT_METHOD_SEND) {
+               tid = &((struct ib_mad_hdr *) packet->mad.data)->tid;
+               *tid = cpu_to_be64(((u64) agent->hi_tid) << 32 |
+                                  (be64_to_cpup(tid) & 0xffffffff));
+       }
+
+       memset(&ah_attr, 0, sizeof ah_attr);
+       ah_attr.dlid          = be16_to_cpu(packet->mad.lid);
+       ah_attr.sl            = packet->mad.sl;
+       ah_attr.src_path_bits = packet->mad.path_bits;
+       ah_attr.port_num      = file->port->port_num;
+       if (packet->mad.grh_present) {
+               ah_attr.ah_flags = IB_AH_GRH;
+               memcpy(ah_attr.grh.dgid.raw, packet->mad.gid, 16);
+               ah_attr.grh.flow_label     = packet->mad.flow_label;
+               ah_attr.grh.hop_limit      = packet->mad.hop_limit;
+               ah_attr.grh.traffic_class  = packet->mad.traffic_class;
+       }
+
+       packet->ah = ib_create_ah(agent->qp->pd, &ah_attr);
+       if (IS_ERR(packet->ah)) {
+               ret = PTR_ERR(packet->ah);
+               goto err_up;
+       }
+
+       gather_list.addr = dma_map_single(agent->device->dma_device,
+                                         packet->mad.data,
+                                         sizeof packet->mad.data,
+                                         DMA_TO_DEVICE);
+       gather_list.length = sizeof packet->mad.data;
+       gather_list.lkey   = file->mr[packet->mad.id]->lkey;
+       pci_unmap_addr_set(packet, mapping, gather_list.addr);
+
+       wr.wr.ud.mad_hdr     = (struct ib_mad_hdr *) packet->mad.data;
+       wr.wr.ud.ah          = packet->ah;
+       wr.wr.ud.remote_qpn  = be32_to_cpu(packet->mad.qpn);
+       wr.wr.ud.remote_qkey = be32_to_cpu(packet->mad.qkey);
+       wr.wr.ud.timeout_ms  = packet->mad.timeout_ms;
+
+       wr.wr_id            = (unsigned long) packet;
+
+       ret = ib_post_send_mad(agent, &wr, &bad_wr);
+       if (ret) {
+               dma_unmap_single(agent->device->dma_device,
+                                pci_unmap_addr(packet, mapping),
+                                sizeof packet->mad.data,
+                                DMA_TO_DEVICE);
+               goto err_up;
+       }
+
+       up_read(&file->agent_mutex);
+
+       return sizeof packet->mad;
+
+err_up:
+       up_read(&file->agent_mutex);
+
+err:
+       kfree(packet);
+       return ret;
+}
+
+static unsigned int ib_umad_poll(struct file *filp, struct poll_table_struct *wait)
+{
+       struct ib_umad_file *file = filp->private_data;
+
+       /* we will always be able to post a MAD send */
+       unsigned int mask = POLLOUT | POLLWRNORM;
+
+       poll_wait(filp, &file->recv_wait, wait);
+
+       if (!list_empty(&file->recv_list))
+               mask |= POLLIN | POLLRDNORM;
+
+       return mask;
+}
+
+static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg)
+{
+       struct ib_user_mad_reg_req ureq;
+       struct ib_mad_reg_req req;
+       struct ib_mad_agent *agent;
+       int agent_id;
+       int ret;
+
+       down_write(&file->agent_mutex);
+
+       if (copy_from_user(&ureq, (void __user *) arg, sizeof ureq)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       if (ureq.qpn != 0 && ureq.qpn != 1) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       for (agent_id = 0; agent_id < IB_UMAD_MAX_AGENTS; ++agent_id)
+               if (!file->agent[agent_id])
+                       goto found;
+
+       ret = -ENOMEM;
+       goto out;
+
+found:
+       req.mgmt_class         = ureq.mgmt_class;
+       req.mgmt_class_version = ureq.mgmt_class_version;
+       memcpy(req.method_mask, ureq.method_mask, sizeof req.method_mask);
+       memcpy(req.oui,         ureq.oui,         sizeof req.oui);
+
+       agent = ib_register_mad_agent(file->port->ib_dev, file->port->port_num,
+                                     ureq.qpn ? IB_QPT_GSI : IB_QPT_SMI,
+                                     &req, 0, send_handler, recv_handler,
+                                     file);
+       if (IS_ERR(agent)) {
+               ret = PTR_ERR(agent);
+               goto out;
+       }
+
+       file->agent[agent_id] = agent;
+
+       file->mr[agent_id] = ib_get_dma_mr(agent->qp->pd, IB_ACCESS_LOCAL_WRITE);
+       if (IS_ERR(file->mr[agent_id])) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       if (put_user(agent_id,
+                    (u32 __user *) (arg + offsetof(struct ib_user_mad_reg_req, id)))) {
+               ret = -EFAULT;
+               goto err_mr;
+       }
+
+       ret = 0;
+       goto out;
+
+err_mr:
+       ib_dereg_mr(file->mr[agent_id]);
+
+err:
+       file->agent[agent_id] = NULL;
+       ib_unregister_mad_agent(agent);
+
+out:
+       up_write(&file->agent_mutex);
+       return ret;
+}
+
+static int ib_umad_unreg_agent(struct ib_umad_file *file, unsigned long arg)
+{
+       u32 id;
+       int ret = 0;
+
+       down_write(&file->agent_mutex);
+
+       if (get_user(id, (u32 __user *) arg)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       if (id < 0 || id >= IB_UMAD_MAX_AGENTS || !file->agent[id]) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ib_dereg_mr(file->mr[id]);
+       ib_unregister_mad_agent(file->agent[id]);
+       file->agent[id] = NULL;
+
+out:
+       up_write(&file->agent_mutex);
+       return ret;
+}
+
+static long ib_umad_ioctl(struct file *filp,
+                        unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case IB_USER_MAD_REGISTER_AGENT:
+               return ib_umad_reg_agent(filp->private_data, arg);
+       case IB_USER_MAD_UNREGISTER_AGENT:
+               return ib_umad_unreg_agent(filp->private_data, arg);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+static int ib_umad_open(struct inode *inode, struct file *filp)
+{
+       struct ib_umad_port *port =
+               container_of(inode->i_cdev, struct ib_umad_port, dev);
+       struct ib_umad_file *file;
+
+       file = kmalloc(sizeof *file, GFP_KERNEL);
+       if (!file)
+               return -ENOMEM;
+
+       memset(file, 0, sizeof *file);
+
+       spin_lock_init(&file->recv_lock);
+       init_rwsem(&file->agent_mutex);
+       INIT_LIST_HEAD(&file->recv_list);
+       init_waitqueue_head(&file->recv_wait);
+
+       file->port = port;
+       filp->private_data = file;
+
+       return 0;
+}
+
+static int ib_umad_close(struct inode *inode, struct file *filp)
+{
+       struct ib_umad_file *file = filp->private_data;
+       int i;
+
+       for (i = 0; i < IB_UMAD_MAX_AGENTS; ++i)
+               if (file->agent[i]) {
+                       ib_dereg_mr(file->mr[i]);
+                       ib_unregister_mad_agent(file->agent[i]);
+               }
+
+       kfree(file);
+
+       return 0;
+}
+
+static struct file_operations umad_fops = {
+       .owner          = THIS_MODULE,
+       .read           = ib_umad_read,
+       .write          = ib_umad_write,
+       .poll           = ib_umad_poll,
+       .unlocked_ioctl = ib_umad_ioctl,
+       .compat_ioctl   = ib_umad_ioctl,
+       .open           = ib_umad_open,
+       .release        = ib_umad_close
+};
+
+static int ib_umad_sm_open(struct inode *inode, struct file *filp)
+{
+       struct ib_umad_port *port =
+               container_of(inode->i_cdev, struct ib_umad_port, sm_dev);
+       struct ib_port_modify props = {
+               .set_port_cap_mask = IB_PORT_SM
+       };
+       int ret;
+
+       if (filp->f_flags & O_NONBLOCK) {
+               if (down_trylock(&port->sm_sem))
+                       return -EAGAIN;
+       } else {
+               if (down_interruptible(&port->sm_sem))
+                       return -ERESTARTSYS;
+       }
+
+       ret = ib_modify_port(port->ib_dev, port->port_num, 0, &props);
+       if (ret) {
+               up(&port->sm_sem);
+               return ret;
+       }
+
+       filp->private_data = port;
+
+       return 0;
+}
+
+static int ib_umad_sm_close(struct inode *inode, struct file *filp)
+{
+       struct ib_umad_port *port = filp->private_data;
+       struct ib_port_modify props = {
+               .clr_port_cap_mask = IB_PORT_SM
+       };
+       int ret;
+
+       ret = ib_modify_port(port->ib_dev, port->port_num, 0, &props);
+       up(&port->sm_sem);
+
+       return ret;
+}
+
+static struct file_operations umad_sm_fops = {
+       .owner   = THIS_MODULE,
+       .open    = ib_umad_sm_open,
+       .release = ib_umad_sm_close
+};
+
+static struct ib_client umad_client = {
+       .name   = "umad",
+       .add    = ib_umad_add_one,
+       .remove = ib_umad_remove_one
+};
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+       struct ib_umad_port *port = class_get_devdata(class_dev);
+
+       if (class_dev == &port->class_dev)
+               return print_dev_t(buf, port->dev.dev);
+       else
+               return print_dev_t(buf, port->sm_dev.dev);
+}
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
+static ssize_t show_ibdev(struct class_device *class_dev, char *buf)
+{
+       struct ib_umad_port *port = class_get_devdata(class_dev);
+
+       return sprintf(buf, "%s\n", port->ib_dev->name);
+}
+static CLASS_DEVICE_ATTR(ibdev, S_IRUGO, show_ibdev, NULL);
+
+static ssize_t show_port(struct class_device *class_dev, char *buf)
+{
+       struct ib_umad_port *port = class_get_devdata(class_dev);
+
+       return sprintf(buf, "%d\n", port->port_num);
+}
+static CLASS_DEVICE_ATTR(port, S_IRUGO, show_port, NULL);
+
+static void ib_umad_release_dev(struct kref *ref)
+{
+       struct ib_umad_device *dev =
+               container_of(ref, struct ib_umad_device, ref);
+
+       kfree(dev);
+}
+
+static void ib_umad_release_port(struct class_device *class_dev)
+{
+       struct ib_umad_port *port = class_get_devdata(class_dev);
+
+       if (class_dev == &port->class_dev) {
+               cdev_del(&port->dev);
+               clear_bit(port->devnum, dev_map);
+       } else {
+               cdev_del(&port->sm_dev);
+               clear_bit(port->sm_devnum, dev_map);
+       }
+
+       kref_put(&port->umad_dev->ref, ib_umad_release_dev);
+}
+
+static struct class umad_class = {
+       .name    = "infiniband_mad",
+       .release = ib_umad_release_port
+};
+
+static ssize_t show_abi_version(struct class *class, char *buf)
+{
+       return sprintf(buf, "%d\n", IB_USER_MAD_ABI_VERSION);
+}
+static CLASS_ATTR(abi_version, S_IRUGO, show_abi_version, NULL);
+
+static int ib_umad_init_port(struct ib_device *device, int port_num,
+                            struct ib_umad_port *port)
+{
+       spin_lock(&map_lock);
+       port->devnum = find_first_zero_bit(dev_map, IB_UMAD_MAX_PORTS);
+       if (port->devnum >= IB_UMAD_MAX_PORTS) {
+               spin_unlock(&map_lock);
+               return -1;
+       }
+       port->sm_devnum = find_next_zero_bit(dev_map, IB_UMAD_MAX_PORTS * 2, IB_UMAD_MAX_PORTS);
+       if (port->sm_devnum >= IB_UMAD_MAX_PORTS * 2) {
+               spin_unlock(&map_lock);
+               return -1;
+       }
+       set_bit(port->devnum, dev_map);
+       set_bit(port->sm_devnum, dev_map);
+       spin_unlock(&map_lock);
+
+       port->ib_dev   = device;
+       port->port_num = port_num;
+       init_MUTEX(&port->sm_sem);
+
+       cdev_init(&port->dev, &umad_fops);
+       port->dev.owner = THIS_MODULE;
+       kobject_set_name(&port->dev.kobj, "umad%d", port->devnum);
+       if (cdev_add(&port->dev, base_dev + port->devnum, 1))
+               return -1;
+
+       port->class_dev.class = &umad_class;
+       port->class_dev.dev   = device->dma_device;
+
+       snprintf(port->class_dev.class_id, BUS_ID_SIZE, "umad%d", port->devnum);
+
+       if (class_device_register(&port->class_dev))
+               goto err_cdev;
+
+       class_set_devdata(&port->class_dev, port);
+       kref_get(&port->umad_dev->ref);
+
+       if (class_device_create_file(&port->class_dev, &class_device_attr_dev))
+               goto err_class;
+       if (class_device_create_file(&port->class_dev, &class_device_attr_ibdev))
+               goto err_class;
+       if (class_device_create_file(&port->class_dev, &class_device_attr_port))
+               goto err_class;
+
+       cdev_init(&port->sm_dev, &umad_sm_fops);
+       port->sm_dev.owner = THIS_MODULE;
+       kobject_set_name(&port->dev.kobj, "issm%d", port->sm_devnum - IB_UMAD_MAX_PORTS);
+       if (cdev_add(&port->sm_dev, base_dev + port->sm_devnum, 1))
+               return -1;
+
+       port->sm_class_dev.class = &umad_class;
+       port->sm_class_dev.dev   = device->dma_device;
+
+       snprintf(port->sm_class_dev.class_id, BUS_ID_SIZE, "issm%d", port->sm_devnum - IB_UMAD_MAX_PORTS);
+
+       if (class_device_register(&port->sm_class_dev))
+               goto err_sm_cdev;
+
+       class_set_devdata(&port->sm_class_dev, port);
+       kref_get(&port->umad_dev->ref);
+
+       if (class_device_create_file(&port->sm_class_dev, &class_device_attr_dev))
+               goto err_sm_class;
+       if (class_device_create_file(&port->sm_class_dev, &class_device_attr_ibdev))
+               goto err_sm_class;
+       if (class_device_create_file(&port->sm_class_dev, &class_device_attr_port))
+               goto err_sm_class;
+
+       return 0;
+
+err_sm_class:
+       class_device_unregister(&port->sm_class_dev);
+
+err_sm_cdev:
+       cdev_del(&port->sm_dev);
+
+err_class:
+       class_device_unregister(&port->class_dev);
+
+err_cdev:
+       cdev_del(&port->dev);
+       clear_bit(port->devnum, dev_map);
+
+       return -1;
+}
+
+static void ib_umad_add_one(struct ib_device *device)
+{
+       struct ib_umad_device *umad_dev;
+       int s, e, i;
+
+       if (device->node_type == IB_NODE_SWITCH)
+               s = e = 0;
+       else {
+               s = 1;
+               e = device->phys_port_cnt;
+       }
+
+       umad_dev = kmalloc(sizeof *umad_dev +
+                          (e - s + 1) * sizeof (struct ib_umad_port),
+                          GFP_KERNEL);
+       if (!umad_dev)
+               return;
+
+       memset(umad_dev, 0, sizeof *umad_dev +
+              (e - s + 1) * sizeof (struct ib_umad_port));
+
+       kref_init(&umad_dev->ref);
+
+       umad_dev->start_port = s;
+       umad_dev->end_port   = e;
+
+       for (i = s; i <= e; ++i) {
+               umad_dev->port[i - s].umad_dev = umad_dev;
+
+               if (ib_umad_init_port(device, i, &umad_dev->port[i - s]))
+                       goto err;
+       }
+
+       ib_set_client_data(device, &umad_client, umad_dev);
+
+       return;
+
+err:
+       while (--i >= s) {
+               class_device_unregister(&umad_dev->port[i - s].class_dev);
+               class_device_unregister(&umad_dev->port[i - s].sm_class_dev);
+       }
+
+       kref_put(&umad_dev->ref, ib_umad_release_dev);
+}
+
+static void ib_umad_remove_one(struct ib_device *device)
+{
+       struct ib_umad_device *umad_dev = ib_get_client_data(device, &umad_client);
+       int i;
+
+       if (!umad_dev)
+               return;
+
+       for (i = 0; i <= umad_dev->end_port - umad_dev->start_port; ++i) {
+               class_device_unregister(&umad_dev->port[i].class_dev);
+               class_device_unregister(&umad_dev->port[i].sm_class_dev);
+       }
+
+       kref_put(&umad_dev->ref, ib_umad_release_dev);
+}
+
+static int __init ib_umad_init(void)
+{
+       int ret;
+
+       spin_lock_init(&map_lock);
+
+       ret = register_chrdev_region(base_dev, IB_UMAD_MAX_PORTS * 2,
+                                    "infiniband_mad");
+       if (ret) {
+               printk(KERN_ERR "user_mad: couldn't register device number\n");
+               goto out;
+       }
+
+       ret = class_register(&umad_class);
+       if (ret) {
+               printk(KERN_ERR "user_mad: couldn't create class infiniband_mad\n");
+               goto out_chrdev;
+       }
+
+       ret = class_create_file(&umad_class, &class_attr_abi_version);
+       if (ret) {
+               printk(KERN_ERR "user_mad: couldn't create abi_version attribute\n");
+               goto out_class;
+       }
+
+       ret = ib_register_client(&umad_client);
+       if (ret) {
+               printk(KERN_ERR "user_mad: couldn't register ib_umad client\n");
+               goto out_class;
+       }
+
+       return 0;
+
+out_class:
+       class_unregister(&umad_class);
+
+out_chrdev:
+       unregister_chrdev_region(base_dev, IB_UMAD_MAX_PORTS * 2);
+
+out:
+       return ret;
+}
+
+static void __exit ib_umad_cleanup(void)
+{
+       ib_unregister_client(&umad_client);
+       class_unregister(&umad_class);
+       unregister_chrdev_region(base_dev, IB_UMAD_MAX_PORTS * 2);
+}
+
+module_init(ib_umad_init);
+module_exit(ib_umad_cleanup);
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
new file mode 100644 (file)
index 0000000..7c08ed0
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
+ * Copyright (c) 2004 Infinicon Corporation.  All rights reserved.
+ * Copyright (c) 2004 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ * Copyright (c) 2004 Voltaire Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: verbs.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/errno.h>
+#include <linux/err.h>
+
+#include <ib_verbs.h>
+
+/* Protection domains */
+
+struct ib_pd *ib_alloc_pd(struct ib_device *device)
+{
+       struct ib_pd *pd;
+
+       pd = device->alloc_pd(device);
+
+       if (!IS_ERR(pd)) {
+               pd->device = device;
+               atomic_set(&pd->usecnt, 0);
+       }
+
+       return pd;
+}
+EXPORT_SYMBOL(ib_alloc_pd);
+
+int ib_dealloc_pd(struct ib_pd *pd)
+{
+       if (atomic_read(&pd->usecnt))
+               return -EBUSY;
+
+       return pd->device->dealloc_pd(pd);
+}
+EXPORT_SYMBOL(ib_dealloc_pd);
+
+/* Address handles */
+
+struct ib_ah *ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
+{
+       struct ib_ah *ah;
+
+       ah = pd->device->create_ah(pd, ah_attr);
+
+       if (!IS_ERR(ah)) {
+               ah->device = pd->device;
+               ah->pd     = pd;
+               atomic_inc(&pd->usecnt);
+       }
+
+       return ah;
+}
+EXPORT_SYMBOL(ib_create_ah);
+
+int ib_modify_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr)
+{
+       return ah->device->modify_ah ?
+               ah->device->modify_ah(ah, ah_attr) :
+               -ENOSYS;
+}
+EXPORT_SYMBOL(ib_modify_ah);
+
+int ib_query_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr)
+{
+       return ah->device->query_ah ?
+               ah->device->query_ah(ah, ah_attr) :
+               -ENOSYS;
+}
+EXPORT_SYMBOL(ib_query_ah);
+
+int ib_destroy_ah(struct ib_ah *ah)
+{
+       struct ib_pd *pd;
+       int ret;
+
+       pd = ah->pd;
+       ret = ah->device->destroy_ah(ah);
+       if (!ret)
+               atomic_dec(&pd->usecnt);
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_destroy_ah);
+
+/* Queue pairs */
+
+struct ib_qp *ib_create_qp(struct ib_pd *pd,
+                          struct ib_qp_init_attr *qp_init_attr)
+{
+       struct ib_qp *qp;
+
+       qp = pd->device->create_qp(pd, qp_init_attr);
+
+       if (!IS_ERR(qp)) {
+               qp->device        = pd->device;
+               qp->pd            = pd;
+               qp->send_cq       = qp_init_attr->send_cq;
+               qp->recv_cq       = qp_init_attr->recv_cq;
+               qp->srq           = qp_init_attr->srq;
+               qp->event_handler = qp_init_attr->event_handler;
+               qp->qp_context    = qp_init_attr->qp_context;
+               qp->qp_type       = qp_init_attr->qp_type;
+               atomic_inc(&pd->usecnt);
+               atomic_inc(&qp_init_attr->send_cq->usecnt);
+               atomic_inc(&qp_init_attr->recv_cq->usecnt);
+               if (qp_init_attr->srq)
+                       atomic_inc(&qp_init_attr->srq->usecnt);
+       }
+
+       return qp;
+}
+EXPORT_SYMBOL(ib_create_qp);
+
+int ib_modify_qp(struct ib_qp *qp,
+                struct ib_qp_attr *qp_attr,
+                int qp_attr_mask)
+{
+       return qp->device->modify_qp(qp, qp_attr, qp_attr_mask);
+}
+EXPORT_SYMBOL(ib_modify_qp);
+
+int ib_query_qp(struct ib_qp *qp,
+               struct ib_qp_attr *qp_attr,
+               int qp_attr_mask,
+               struct ib_qp_init_attr *qp_init_attr)
+{
+       return qp->device->query_qp ?
+               qp->device->query_qp(qp, qp_attr, qp_attr_mask, qp_init_attr) :
+               -ENOSYS;
+}
+EXPORT_SYMBOL(ib_query_qp);
+
+int ib_destroy_qp(struct ib_qp *qp)
+{
+       struct ib_pd *pd;
+       struct ib_cq *scq, *rcq;
+       struct ib_srq *srq;
+       int ret;
+
+       pd  = qp->pd;
+       scq = qp->send_cq;
+       rcq = qp->recv_cq;
+       srq = qp->srq;
+
+       ret = qp->device->destroy_qp(qp);
+       if (!ret) {
+               atomic_dec(&pd->usecnt);
+               atomic_dec(&scq->usecnt);
+               atomic_dec(&rcq->usecnt);
+               if (srq)
+                       atomic_dec(&srq->usecnt);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_destroy_qp);
+
+/* Completion queues */
+
+struct ib_cq *ib_create_cq(struct ib_device *device,
+                          ib_comp_handler comp_handler,
+                          void (*event_handler)(struct ib_event *, void *),
+                          void *cq_context, int cqe)
+{
+       struct ib_cq *cq;
+
+       cq = device->create_cq(device, cqe);
+
+       if (!IS_ERR(cq)) {
+               cq->device        = device;
+               cq->comp_handler  = comp_handler;
+               cq->event_handler = event_handler;
+               cq->cq_context    = cq_context;
+               atomic_set(&cq->usecnt, 0);
+       }
+
+       return cq;
+}
+EXPORT_SYMBOL(ib_create_cq);
+
+int ib_destroy_cq(struct ib_cq *cq)
+{
+       if (atomic_read(&cq->usecnt))
+               return -EBUSY;
+
+       return cq->device->destroy_cq(cq);
+}
+EXPORT_SYMBOL(ib_destroy_cq);
+
+int ib_resize_cq(struct ib_cq *cq,
+                 int           cqe)
+{
+       int ret;
+
+       if (!cq->device->resize_cq)
+               return -ENOSYS;
+
+       ret = cq->device->resize_cq(cq, &cqe);
+       if (!ret)
+               cq->cqe = cqe;
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_resize_cq);
+
+/* Memory regions */
+
+struct ib_mr *ib_get_dma_mr(struct ib_pd *pd, int mr_access_flags)
+{
+       struct ib_mr *mr;
+
+       mr = pd->device->get_dma_mr(pd, mr_access_flags);
+
+       if (!IS_ERR(mr)) {
+               mr->device = pd->device;
+               mr->pd     = pd;
+               atomic_inc(&pd->usecnt);
+               atomic_set(&mr->usecnt, 0);
+       }
+
+       return mr;
+}
+EXPORT_SYMBOL(ib_get_dma_mr);
+
+struct ib_mr *ib_reg_phys_mr(struct ib_pd *pd,
+                            struct ib_phys_buf *phys_buf_array,
+                            int num_phys_buf,
+                            int mr_access_flags,
+                            u64 *iova_start)
+{
+       struct ib_mr *mr;
+
+       mr = pd->device->reg_phys_mr(pd, phys_buf_array, num_phys_buf,
+                                    mr_access_flags, iova_start);
+
+       if (!IS_ERR(mr)) {
+               mr->device = pd->device;
+               mr->pd     = pd;
+               atomic_inc(&pd->usecnt);
+               atomic_set(&mr->usecnt, 0);
+       }
+
+       return mr;
+}
+EXPORT_SYMBOL(ib_reg_phys_mr);
+
+int ib_rereg_phys_mr(struct ib_mr *mr,
+                    int mr_rereg_mask,
+                    struct ib_pd *pd,
+                    struct ib_phys_buf *phys_buf_array,
+                    int num_phys_buf,
+                    int mr_access_flags,
+                    u64 *iova_start)
+{
+       struct ib_pd *old_pd;
+       int ret;
+
+       if (!mr->device->rereg_phys_mr)
+               return -ENOSYS;
+
+       if (atomic_read(&mr->usecnt))
+               return -EBUSY;
+
+       old_pd = mr->pd;
+
+       ret = mr->device->rereg_phys_mr(mr, mr_rereg_mask, pd,
+                                       phys_buf_array, num_phys_buf,
+                                       mr_access_flags, iova_start);
+
+       if (!ret && (mr_rereg_mask & IB_MR_REREG_PD)) {
+               atomic_dec(&old_pd->usecnt);
+               atomic_inc(&pd->usecnt);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_rereg_phys_mr);
+
+int ib_query_mr(struct ib_mr *mr, struct ib_mr_attr *mr_attr)
+{
+       return mr->device->query_mr ?
+               mr->device->query_mr(mr, mr_attr) : -ENOSYS;
+}
+EXPORT_SYMBOL(ib_query_mr);
+
+int ib_dereg_mr(struct ib_mr *mr)
+{
+       struct ib_pd *pd;
+       int ret;
+
+       if (atomic_read(&mr->usecnt))
+               return -EBUSY;
+
+       pd = mr->pd;
+       ret = mr->device->dereg_mr(mr);
+       if (!ret)
+               atomic_dec(&pd->usecnt);
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_dereg_mr);
+
+/* Memory windows */
+
+struct ib_mw *ib_alloc_mw(struct ib_pd *pd)
+{
+       struct ib_mw *mw;
+
+       if (!pd->device->alloc_mw)
+               return ERR_PTR(-ENOSYS);
+
+       mw = pd->device->alloc_mw(pd);
+       if (!IS_ERR(mw)) {
+               mw->device = pd->device;
+               mw->pd     = pd;
+               atomic_inc(&pd->usecnt);
+       }
+
+       return mw;
+}
+EXPORT_SYMBOL(ib_alloc_mw);
+
+int ib_dealloc_mw(struct ib_mw *mw)
+{
+       struct ib_pd *pd;
+       int ret;
+
+       pd = mw->pd;
+       ret = mw->device->dealloc_mw(mw);
+       if (!ret)
+               atomic_dec(&pd->usecnt);
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_dealloc_mw);
+
+/* "Fast" memory regions */
+
+struct ib_fmr *ib_alloc_fmr(struct ib_pd *pd,
+                           int mr_access_flags,
+                           struct ib_fmr_attr *fmr_attr)
+{
+       struct ib_fmr *fmr;
+
+       if (!pd->device->alloc_fmr)
+               return ERR_PTR(-ENOSYS);
+
+       fmr = pd->device->alloc_fmr(pd, mr_access_flags, fmr_attr);
+       if (!IS_ERR(fmr)) {
+               fmr->device = pd->device;
+               fmr->pd     = pd;
+               atomic_inc(&pd->usecnt);
+       }
+
+       return fmr;
+}
+EXPORT_SYMBOL(ib_alloc_fmr);
+
+int ib_unmap_fmr(struct list_head *fmr_list)
+{
+       struct ib_fmr *fmr;
+
+       if (list_empty(fmr_list))
+               return 0;
+
+       fmr = list_entry(fmr_list->next, struct ib_fmr, list);
+       return fmr->device->unmap_fmr(fmr_list);
+}
+EXPORT_SYMBOL(ib_unmap_fmr);
+
+int ib_dealloc_fmr(struct ib_fmr *fmr)
+{
+       struct ib_pd *pd;
+       int ret;
+
+       pd = fmr->pd;
+       ret = fmr->device->dealloc_fmr(fmr);
+       if (!ret)
+               atomic_dec(&pd->usecnt);
+
+       return ret;
+}
+EXPORT_SYMBOL(ib_dealloc_fmr);
+
+/* Multicast groups */
+
+int ib_attach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid)
+{
+       return qp->device->attach_mcast ?
+               qp->device->attach_mcast(qp, gid, lid) :
+               -ENOSYS;
+}
+EXPORT_SYMBOL(ib_attach_mcast);
+
+int ib_detach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid)
+{
+       return qp->device->detach_mcast ?
+               qp->device->detach_mcast(qp, gid, lid) :
+               -ENOSYS;
+}
+EXPORT_SYMBOL(ib_detach_mcast);
diff --git a/drivers/infiniband/hw/mthca/Kconfig b/drivers/infiniband/hw/mthca/Kconfig
new file mode 100644 (file)
index 0000000..e88be85
--- /dev/null
@@ -0,0 +1,16 @@
+config INFINIBAND_MTHCA
+       tristate "Mellanox HCA support"
+       depends on PCI && INFINIBAND
+       ---help---
+         This is a low-level driver for Mellanox InfiniHost host
+         channel adapters (HCAs), including the MT23108 PCI-X HCA
+         ("Tavor") and the MT25208 PCI Express HCA ("Arbel").
+
+config INFINIBAND_MTHCA_DEBUG
+       bool "Verbose debugging output"
+       depends on INFINIBAND_MTHCA
+       default n
+       ---help---
+         This option causes the mthca driver produce a bunch of debug
+         messages.  Select this is you are developing the driver or
+         trying to diagnose a problem.
diff --git a/drivers/infiniband/hw/mthca/Makefile b/drivers/infiniband/hw/mthca/Makefile
new file mode 100644 (file)
index 0000000..e453ebc
--- /dev/null
@@ -0,0 +1,12 @@
+EXTRA_CFLAGS += -Idrivers/infiniband/include
+
+ifdef CONFIG_INFINIBAND_MTHCA_DEBUG
+EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_INFINIBAND_MTHCA) += ib_mthca.o
+
+ib_mthca-y :=  mthca_main.o mthca_cmd.o mthca_profile.o mthca_reset.o \
+               mthca_allocator.o mthca_eq.o mthca_pd.o mthca_cq.o \
+               mthca_mr.o mthca_qp.o mthca_av.o mthca_mcg.o mthca_mad.o \
+               mthca_provider.o mthca_memfree.o
diff --git a/drivers/infiniband/hw/mthca/mthca_allocator.c b/drivers/infiniband/hw/mthca/mthca_allocator.c
new file mode 100644 (file)
index 0000000..b1db48d
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_allocator.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/bitmap.h>
+
+#include "mthca_dev.h"
+
+/* Trivial bitmap-based allocator */
+u32 mthca_alloc(struct mthca_alloc *alloc)
+{
+       u32 obj;
+
+       spin_lock(&alloc->lock);
+       obj = find_next_zero_bit(alloc->table, alloc->max, alloc->last);
+       if (obj >= alloc->max) {
+               alloc->top = (alloc->top + alloc->max) & alloc->mask;
+               obj = find_first_zero_bit(alloc->table, alloc->max);
+       }
+
+       if (obj < alloc->max) {
+               set_bit(obj, alloc->table);
+               obj |= alloc->top;
+       } else
+               obj = -1;
+
+       spin_unlock(&alloc->lock);
+
+       return obj;
+}
+
+void mthca_free(struct mthca_alloc *alloc, u32 obj)
+{
+       obj &= alloc->max - 1;
+       spin_lock(&alloc->lock);
+       clear_bit(obj, alloc->table);
+       alloc->last = min(alloc->last, obj);
+       alloc->top = (alloc->top + alloc->max) & alloc->mask;
+       spin_unlock(&alloc->lock);
+}
+
+int mthca_alloc_init(struct mthca_alloc *alloc, u32 num, u32 mask,
+                    u32 reserved)
+{
+       int i;
+
+       /* num must be a power of 2 */
+       if (num != 1 << (ffs(num) - 1))
+               return -EINVAL;
+
+       alloc->last = 0;
+       alloc->top  = 0;
+       alloc->max  = num;
+       alloc->mask = mask;
+       spin_lock_init(&alloc->lock);
+       alloc->table = kmalloc(BITS_TO_LONGS(num) * sizeof (long),
+                              GFP_KERNEL);
+       if (!alloc->table)
+               return -ENOMEM;
+
+       bitmap_zero(alloc->table, num);
+       for (i = 0; i < reserved; ++i)
+               set_bit(i, alloc->table);
+
+       return 0;
+}
+
+void mthca_alloc_cleanup(struct mthca_alloc *alloc)
+{
+       kfree(alloc->table);
+}
+
+/*
+ * Array of pointers with lazy allocation of leaf pages.  Callers of
+ * _get, _set and _clear methods must use a lock or otherwise
+ * serialize access to the array.
+ */
+
+void *mthca_array_get(struct mthca_array *array, int index)
+{
+       int p = (index * sizeof (void *)) >> PAGE_SHIFT;
+
+       if (array->page_list[p].page) {
+               int i = index & (PAGE_SIZE / sizeof (void *) - 1);
+               return array->page_list[p].page[i];
+       } else
+               return NULL;
+}
+
+int mthca_array_set(struct mthca_array *array, int index, void *value)
+{
+       int p = (index * sizeof (void *)) >> PAGE_SHIFT;
+
+       /* Allocate with GFP_ATOMIC because we'll be called with locks held. */
+       if (!array->page_list[p].page)
+               array->page_list[p].page = (void **) get_zeroed_page(GFP_ATOMIC);
+
+       if (!array->page_list[p].page)
+               return -ENOMEM;
+
+       array->page_list[p].page[index & (PAGE_SIZE / sizeof (void *) - 1)] =
+               value;
+       ++array->page_list[p].used;
+
+       return 0;
+}
+
+void mthca_array_clear(struct mthca_array *array, int index)
+{
+       int p = (index * sizeof (void *)) >> PAGE_SHIFT;
+
+       if (--array->page_list[p].used == 0) {
+               free_page((unsigned long) array->page_list[p].page);
+               array->page_list[p].page = NULL;
+       }
+
+       if (array->page_list[p].used < 0)
+               pr_debug("Array %p index %d page %d with ref count %d < 0\n",
+                        array, index, p, array->page_list[p].used);
+}
+
+int mthca_array_init(struct mthca_array *array, int nent)
+{
+       int npage = (nent * sizeof (void *) + PAGE_SIZE - 1) / PAGE_SIZE;
+       int i;
+
+       array->page_list = kmalloc(npage * sizeof *array->page_list, GFP_KERNEL);
+       if (!array->page_list)
+               return -ENOMEM;
+
+       for (i = 0; i < npage; ++i) {
+               array->page_list[i].page = NULL;
+               array->page_list[i].used = 0;
+       }
+
+       return 0;
+}
+
+void mthca_array_cleanup(struct mthca_array *array, int nent)
+{
+       int i;
+
+       for (i = 0; i < (nent * sizeof (void *) + PAGE_SIZE - 1) / PAGE_SIZE; ++i)
+               free_page((unsigned long) array->page_list[i].page);
+
+       kfree(array->page_list);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_av.c b/drivers/infiniband/hw/mthca/mthca_av.c
new file mode 100644 (file)
index 0000000..ee7f1dc
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_av.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/init.h>
+
+#include <ib_verbs.h>
+#include <ib_cache.h>
+
+#include "mthca_dev.h"
+
+struct mthca_av {
+       u32 port_pd;
+       u8  reserved1;
+       u8  g_slid;
+       u16 dlid;
+       u8  reserved2;
+       u8  gid_index;
+       u8  msg_sr;
+       u8  hop_limit;
+       u32 sl_tclass_flowlabel;
+       u32 dgid[4];
+};
+
+int mthca_create_ah(struct mthca_dev *dev,
+                   struct mthca_pd *pd,
+                   struct ib_ah_attr *ah_attr,
+                   struct mthca_ah *ah)
+{
+       u32 index = -1;
+       struct mthca_av *av = NULL;
+
+       ah->on_hca = 0;
+
+       if (!atomic_read(&pd->sqp_count) &&
+           !(dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) {
+               index = mthca_alloc(&dev->av_table.alloc);
+
+               /* fall back to allocate in host memory */
+               if (index == -1)
+                       goto host_alloc;
+
+               av = kmalloc(sizeof *av, GFP_KERNEL);
+               if (!av)
+                       goto host_alloc;
+
+               ah->on_hca = 1;
+               ah->avdma  = dev->av_table.ddr_av_base +
+                       index * MTHCA_AV_SIZE;
+       }
+
+ host_alloc:
+       if (!ah->on_hca) {
+               ah->av = pci_pool_alloc(dev->av_table.pool,
+                                       SLAB_KERNEL, &ah->avdma);
+               if (!ah->av)
+                       return -ENOMEM;
+
+               av = ah->av;
+       }
+
+       ah->key = pd->ntmr.ibmr.lkey;
+
+       memset(av, 0, MTHCA_AV_SIZE);
+
+       av->port_pd = cpu_to_be32(pd->pd_num | (ah_attr->port_num << 24));
+       av->g_slid  = ah_attr->src_path_bits;
+       av->dlid    = cpu_to_be16(ah_attr->dlid);
+       av->msg_sr  = (3 << 4) | /* 2K message */
+               ah_attr->static_rate;
+       av->sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
+       if (ah_attr->ah_flags & IB_AH_GRH) {
+               av->g_slid |= 0x80;
+               av->gid_index = (ah_attr->port_num - 1) * dev->limits.gid_table_len +
+                       ah_attr->grh.sgid_index;
+               av->hop_limit = ah_attr->grh.hop_limit;
+               av->sl_tclass_flowlabel |=
+                       cpu_to_be32((ah_attr->grh.traffic_class << 20) |
+                                   ah_attr->grh.flow_label);
+               memcpy(av->dgid, ah_attr->grh.dgid.raw, 16);
+       } else {
+               /* Arbel workaround -- low byte of GID must be 2 */
+               av->dgid[3] = cpu_to_be32(2);
+       }
+
+       if (0) {
+               int j;
+
+               mthca_dbg(dev, "Created UDAV at %p/%08lx:\n",
+                         av, (unsigned long) ah->avdma);
+               for (j = 0; j < 8; ++j)
+                       printk(KERN_DEBUG "  [%2x] %08x\n",
+                              j * 4, be32_to_cpu(((u32 *) av)[j]));
+       }
+
+       if (ah->on_hca) {
+               memcpy_toio(dev->av_table.av_map + index * MTHCA_AV_SIZE,
+                           av, MTHCA_AV_SIZE);
+               kfree(av);
+       }
+
+       return 0;
+}
+
+int mthca_destroy_ah(struct mthca_dev *dev, struct mthca_ah *ah)
+{
+       if (ah->on_hca)
+               mthca_free(&dev->av_table.alloc,
+                          (ah->avdma - dev->av_table.ddr_av_base) /
+                          MTHCA_AV_SIZE);
+       else
+               pci_pool_free(dev->av_table.pool, ah->av, ah->avdma);
+
+       return 0;
+}
+
+int mthca_read_ah(struct mthca_dev *dev, struct mthca_ah *ah,
+                 struct ib_ud_header *header)
+{
+       if (ah->on_hca)
+               return -EINVAL;
+
+       header->lrh.service_level   = be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 28;
+       header->lrh.destination_lid = ah->av->dlid;
+       header->lrh.source_lid      = ah->av->g_slid & 0x7f;
+       if (ah->av->g_slid & 0x80) {
+               header->grh_present = 1;
+               header->grh.traffic_class =
+                       (be32_to_cpu(ah->av->sl_tclass_flowlabel) >> 20) & 0xff;
+               header->grh.flow_label    =
+                       ah->av->sl_tclass_flowlabel & cpu_to_be32(0xfffff);
+               ib_get_cached_gid(&dev->ib_dev,
+                                 be32_to_cpu(ah->av->port_pd) >> 24,
+                                 ah->av->gid_index,
+                                 &header->grh.source_gid);
+               memcpy(header->grh.destination_gid.raw,
+                      ah->av->dgid, 16);
+       } else {
+               header->grh_present = 0;
+       }
+
+       return 0;
+}
+
+int __devinit mthca_init_av_table(struct mthca_dev *dev)
+{
+       int err;
+
+       err = mthca_alloc_init(&dev->av_table.alloc,
+                              dev->av_table.num_ddr_avs,
+                              dev->av_table.num_ddr_avs - 1,
+                              0);
+       if (err)
+               return err;
+
+       dev->av_table.pool = pci_pool_create("mthca_av", dev->pdev,
+                                            MTHCA_AV_SIZE,
+                                            MTHCA_AV_SIZE, 0);
+       if (!dev->av_table.pool)
+               goto out_free_alloc;
+
+       if (!(dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) {
+               dev->av_table.av_map = ioremap(pci_resource_start(dev->pdev, 4) +
+                                              dev->av_table.ddr_av_base -
+                                              dev->ddr_start,
+                                              dev->av_table.num_ddr_avs *
+                                              MTHCA_AV_SIZE);
+               if (!dev->av_table.av_map)
+                       goto out_free_pool;
+       } else
+               dev->av_table.av_map = NULL;
+
+       return 0;
+
+ out_free_pool:
+       pci_pool_destroy(dev->av_table.pool);
+
+ out_free_alloc:
+       mthca_alloc_cleanup(&dev->av_table.alloc);
+       return -ENOMEM;
+}
+
+void __devexit mthca_cleanup_av_table(struct mthca_dev *dev)
+{
+       if (dev->av_table.av_map)
+               iounmap(dev->av_table.av_map);
+       pci_pool_destroy(dev->av_table.pool);
+       mthca_alloc_cleanup(&dev->av_table.alloc);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.c b/drivers/infiniband/hw/mthca/mthca_cmd.c
new file mode 100644 (file)
index 0000000..a82ca9d
--- /dev/null
@@ -0,0 +1,1764 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_cmd.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <ib_mad.h>
+
+#include "mthca_dev.h"
+#include "mthca_config_reg.h"
+#include "mthca_cmd.h"
+#include "mthca_memfree.h"
+
+#define CMD_POLL_TOKEN 0xffff
+
+enum {
+       HCR_IN_PARAM_OFFSET    = 0x00,
+       HCR_IN_MODIFIER_OFFSET = 0x08,
+       HCR_OUT_PARAM_OFFSET   = 0x0c,
+       HCR_TOKEN_OFFSET       = 0x14,
+       HCR_STATUS_OFFSET      = 0x18,
+
+       HCR_OPMOD_SHIFT        = 12,
+       HCA_E_BIT              = 22,
+       HCR_GO_BIT             = 23
+};
+
+enum {
+       /* initialization and general commands */
+       CMD_SYS_EN          = 0x1,
+       CMD_SYS_DIS         = 0x2,
+       CMD_MAP_FA          = 0xfff,
+       CMD_UNMAP_FA        = 0xffe,
+       CMD_RUN_FW          = 0xff6,
+       CMD_MOD_STAT_CFG    = 0x34,
+       CMD_QUERY_DEV_LIM   = 0x3,
+       CMD_QUERY_FW        = 0x4,
+       CMD_ENABLE_LAM      = 0xff8,
+       CMD_DISABLE_LAM     = 0xff7,
+       CMD_QUERY_DDR       = 0x5,
+       CMD_QUERY_ADAPTER   = 0x6,
+       CMD_INIT_HCA        = 0x7,
+       CMD_CLOSE_HCA       = 0x8,
+       CMD_INIT_IB         = 0x9,
+       CMD_CLOSE_IB        = 0xa,
+       CMD_QUERY_HCA       = 0xb,
+       CMD_SET_IB          = 0xc,
+       CMD_ACCESS_DDR      = 0x2e,
+       CMD_MAP_ICM         = 0xffa,
+       CMD_UNMAP_ICM       = 0xff9,
+       CMD_MAP_ICM_AUX     = 0xffc,
+       CMD_UNMAP_ICM_AUX   = 0xffb,
+       CMD_SET_ICM_SIZE    = 0xffd,
+
+       /* TPT commands */
+       CMD_SW2HW_MPT       = 0xd,
+       CMD_QUERY_MPT       = 0xe,
+       CMD_HW2SW_MPT       = 0xf,
+       CMD_READ_MTT        = 0x10,
+       CMD_WRITE_MTT       = 0x11,
+       CMD_SYNC_TPT        = 0x2f,
+
+       /* EQ commands */
+       CMD_MAP_EQ          = 0x12,
+       CMD_SW2HW_EQ        = 0x13,
+       CMD_HW2SW_EQ        = 0x14,
+       CMD_QUERY_EQ        = 0x15,
+
+       /* CQ commands */
+       CMD_SW2HW_CQ        = 0x16,
+       CMD_HW2SW_CQ        = 0x17,
+       CMD_QUERY_CQ        = 0x18,
+       CMD_RESIZE_CQ       = 0x2c,
+
+       /* SRQ commands */
+       CMD_SW2HW_SRQ       = 0x35,
+       CMD_HW2SW_SRQ       = 0x36,
+       CMD_QUERY_SRQ       = 0x37,
+
+       /* QP/EE commands */
+       CMD_RST2INIT_QPEE   = 0x19,
+       CMD_INIT2RTR_QPEE   = 0x1a,
+       CMD_RTR2RTS_QPEE    = 0x1b,
+       CMD_RTS2RTS_QPEE    = 0x1c,
+       CMD_SQERR2RTS_QPEE  = 0x1d,
+       CMD_2ERR_QPEE       = 0x1e,
+       CMD_RTS2SQD_QPEE    = 0x1f,
+       CMD_SQD2SQD_QPEE    = 0x38,
+       CMD_SQD2RTS_QPEE    = 0x20,
+       CMD_ERR2RST_QPEE    = 0x21,
+       CMD_QUERY_QPEE      = 0x22,
+       CMD_INIT2INIT_QPEE  = 0x2d,
+       CMD_SUSPEND_QPEE    = 0x32,
+       CMD_UNSUSPEND_QPEE  = 0x33,
+       /* special QPs and management commands */
+       CMD_CONF_SPECIAL_QP = 0x23,
+       CMD_MAD_IFC         = 0x24,
+
+       /* multicast commands */
+       CMD_READ_MGM        = 0x25,
+       CMD_WRITE_MGM       = 0x26,
+       CMD_MGID_HASH       = 0x27,
+
+       /* miscellaneous commands */
+       CMD_DIAG_RPRT       = 0x30,
+       CMD_NOP             = 0x31,
+
+       /* debug commands */
+       CMD_QUERY_DEBUG_MSG = 0x2a,
+       CMD_SET_DEBUG_MSG   = 0x2b,
+};
+
+/*
+ * According to Mellanox code, FW may be starved and never complete
+ * commands.  So we can't use strict timeouts described in PRM -- we
+ * just arbitrarily select 60 seconds for now.
+ */
+#if 0
+/*
+ * Round up and add 1 to make sure we get the full wait time (since we
+ * will be starting in the middle of a jiffy)
+ */
+enum {
+       CMD_TIME_CLASS_A = (HZ + 999) / 1000 + 1,
+       CMD_TIME_CLASS_B = (HZ +  99) /  100 + 1,
+       CMD_TIME_CLASS_C = (HZ +   9) /   10 + 1
+};
+#else
+enum {
+       CMD_TIME_CLASS_A = 60 * HZ,
+       CMD_TIME_CLASS_B = 60 * HZ,
+       CMD_TIME_CLASS_C = 60 * HZ
+};
+#endif
+
+enum {
+       GO_BIT_TIMEOUT = HZ * 10
+};
+
+struct mthca_cmd_context {
+       struct completion done;
+       struct timer_list timer;
+       int               result;
+       int               next;
+       u64               out_param;
+       u16               token;
+       u8                status;
+};
+
+static inline int go_bit(struct mthca_dev *dev)
+{
+       return readl(dev->hcr + HCR_STATUS_OFFSET) &
+               swab32(1 << HCR_GO_BIT);
+}
+
+static int mthca_cmd_post(struct mthca_dev *dev,
+                         u64 in_param,
+                         u64 out_param,
+                         u32 in_modifier,
+                         u8 op_modifier,
+                         u16 op,
+                         u16 token,
+                         int event)
+{
+       int err = 0;
+
+       if (down_interruptible(&dev->cmd.hcr_sem))
+               return -EINTR;
+
+       if (event) {
+               unsigned long end = jiffies + GO_BIT_TIMEOUT;
+
+               while (go_bit(dev) && time_before(jiffies, end)) {
+                       set_current_state(TASK_RUNNING);
+                       schedule();
+               }
+       }
+
+       if (go_bit(dev)) {
+               err = -EAGAIN;
+               goto out;
+       }
+
+       /*
+        * We use writel (instead of something like memcpy_toio)
+        * because writes of less than 32 bits to the HCR don't work
+        * (and some architectures such as ia64 implement memcpy_toio
+        * in terms of writeb).
+        */
+       __raw_writel(cpu_to_be32(in_param >> 32),           dev->hcr + 0 * 4);
+       __raw_writel(cpu_to_be32(in_param & 0xfffffffful),  dev->hcr + 1 * 4);
+       __raw_writel(cpu_to_be32(in_modifier),              dev->hcr + 2 * 4);
+       __raw_writel(cpu_to_be32(out_param >> 32),          dev->hcr + 3 * 4);
+       __raw_writel(cpu_to_be32(out_param & 0xfffffffful), dev->hcr + 4 * 4);
+       __raw_writel(cpu_to_be32(token << 16),              dev->hcr + 5 * 4);
+
+       /* __raw_writel may not order writes. */
+       wmb();
+
+       __raw_writel(cpu_to_be32((1 << HCR_GO_BIT)                |
+                                (event ? (1 << HCA_E_BIT) : 0)   |
+                                (op_modifier << HCR_OPMOD_SHIFT) |
+                                op),                       dev->hcr + 6 * 4);
+
+out:
+       up(&dev->cmd.hcr_sem);
+       return err;
+}
+
+static int mthca_cmd_poll(struct mthca_dev *dev,
+                         u64 in_param,
+                         u64 *out_param,
+                         int out_is_imm,
+                         u32 in_modifier,
+                         u8 op_modifier,
+                         u16 op,
+                         unsigned long timeout,
+                         u8 *status)
+{
+       int err = 0;
+       unsigned long end;
+
+       if (down_interruptible(&dev->cmd.poll_sem))
+               return -EINTR;
+
+       err = mthca_cmd_post(dev, in_param,
+                            out_param ? *out_param : 0,
+                            in_modifier, op_modifier,
+                            op, CMD_POLL_TOKEN, 0);
+       if (err)
+               goto out;
+
+       end = timeout + jiffies;
+       while (go_bit(dev) && time_before(jiffies, end)) {
+               set_current_state(TASK_RUNNING);
+               schedule();
+       }
+
+       if (go_bit(dev)) {
+               err = -EBUSY;
+               goto out;
+       }
+
+       if (out_is_imm) {
+               memcpy_fromio(out_param, dev->hcr + HCR_OUT_PARAM_OFFSET, sizeof (u64));
+               be64_to_cpus(out_param);
+       }
+
+       *status = be32_to_cpu(__raw_readl(dev->hcr + HCR_STATUS_OFFSET)) >> 24;
+
+out:
+       up(&dev->cmd.poll_sem);
+       return err;
+}
+
+void mthca_cmd_event(struct mthca_dev *dev,
+                    u16 token,
+                    u8  status,
+                    u64 out_param)
+{
+       struct mthca_cmd_context *context =
+               &dev->cmd.context[token & dev->cmd.token_mask];
+
+       /* previously timed out command completing at long last */
+       if (token != context->token)
+               return;
+
+       context->result    = 0;
+       context->status    = status;
+       context->out_param = out_param;
+
+       context->token += dev->cmd.token_mask + 1;
+
+       complete(&context->done);
+}
+
+static void event_timeout(unsigned long context_ptr)
+{
+       struct mthca_cmd_context *context =
+               (struct mthca_cmd_context *) context_ptr;
+
+       context->result = -EBUSY;
+       complete(&context->done);
+}
+
+static int mthca_cmd_wait(struct mthca_dev *dev,
+                         u64 in_param,
+                         u64 *out_param,
+                         int out_is_imm,
+                         u32 in_modifier,
+                         u8 op_modifier,
+                         u16 op,
+                         unsigned long timeout,
+                         u8 *status)
+{
+       int err = 0;
+       struct mthca_cmd_context *context;
+
+       if (down_interruptible(&dev->cmd.event_sem))
+               return -EINTR;
+
+       spin_lock(&dev->cmd.context_lock);
+       BUG_ON(dev->cmd.free_head < 0);
+       context = &dev->cmd.context[dev->cmd.free_head];
+       dev->cmd.free_head = context->next;
+       spin_unlock(&dev->cmd.context_lock);
+
+       init_completion(&context->done);
+
+       err = mthca_cmd_post(dev, in_param,
+                            out_param ? *out_param : 0,
+                            in_modifier, op_modifier,
+                            op, context->token, 1);
+       if (err)
+               goto out;
+
+       context->timer.expires  = jiffies + timeout;
+       add_timer(&context->timer);
+
+       wait_for_completion(&context->done);
+       del_timer_sync(&context->timer);
+
+       err = context->result;
+       if (err)
+               goto out;
+
+       *status = context->status;
+       if (*status)
+               mthca_dbg(dev, "Command %02x completed with status %02x\n",
+                         op, *status);
+
+       if (out_is_imm)
+               *out_param = context->out_param;
+
+out:
+       spin_lock(&dev->cmd.context_lock);
+       context->next = dev->cmd.free_head;
+       dev->cmd.free_head = context - dev->cmd.context;
+       spin_unlock(&dev->cmd.context_lock);
+
+       up(&dev->cmd.event_sem);
+       return err;
+}
+
+/* Invoke a command with an output mailbox */
+static int mthca_cmd_box(struct mthca_dev *dev,
+                        u64 in_param,
+                        u64 out_param,
+                        u32 in_modifier,
+                        u8 op_modifier,
+                        u16 op,
+                        unsigned long timeout,
+                        u8 *status)
+{
+       if (dev->cmd.use_events)
+               return mthca_cmd_wait(dev, in_param, &out_param, 0,
+                                     in_modifier, op_modifier, op,
+                                     timeout, status);
+       else
+               return mthca_cmd_poll(dev, in_param, &out_param, 0,
+                                     in_modifier, op_modifier, op,
+                                     timeout, status);
+}
+
+/* Invoke a command with no output parameter */
+static int mthca_cmd(struct mthca_dev *dev,
+                    u64 in_param,
+                    u32 in_modifier,
+                    u8 op_modifier,
+                    u16 op,
+                    unsigned long timeout,
+                    u8 *status)
+{
+       return mthca_cmd_box(dev, in_param, 0, in_modifier,
+                            op_modifier, op, timeout, status);
+}
+
+/*
+ * Invoke a command with an immediate output parameter (and copy the
+ * output into the caller's out_param pointer after the command
+ * executes).
+ */
+static int mthca_cmd_imm(struct mthca_dev *dev,
+                        u64 in_param,
+                        u64 *out_param,
+                        u32 in_modifier,
+                        u8 op_modifier,
+                        u16 op,
+                        unsigned long timeout,
+                        u8 *status)
+{
+       if (dev->cmd.use_events)
+               return mthca_cmd_wait(dev, in_param, out_param, 1,
+                                     in_modifier, op_modifier, op,
+                                     timeout, status);
+       else
+               return mthca_cmd_poll(dev, in_param, out_param, 1,
+                                     in_modifier, op_modifier, op,
+                                     timeout, status);
+}
+
+/*
+ * Switch to using events to issue FW commands (should be called after
+ * event queue to command events has been initialized).
+ */
+int mthca_cmd_use_events(struct mthca_dev *dev)
+{
+       int i;
+
+       dev->cmd.context = kmalloc(dev->cmd.max_cmds *
+                                  sizeof (struct mthca_cmd_context),
+                                  GFP_KERNEL);
+       if (!dev->cmd.context)
+               return -ENOMEM;
+
+       for (i = 0; i < dev->cmd.max_cmds; ++i) {
+               dev->cmd.context[i].token = i;
+               dev->cmd.context[i].next = i + 1;
+               init_timer(&dev->cmd.context[i].timer);
+               dev->cmd.context[i].timer.data     =
+                       (unsigned long) &dev->cmd.context[i];
+               dev->cmd.context[i].timer.function = event_timeout;
+       }
+
+       dev->cmd.context[dev->cmd.max_cmds - 1].next = -1;
+       dev->cmd.free_head = 0;
+
+       sema_init(&dev->cmd.event_sem, dev->cmd.max_cmds);
+       spin_lock_init(&dev->cmd.context_lock);
+
+       for (dev->cmd.token_mask = 1;
+            dev->cmd.token_mask < dev->cmd.max_cmds;
+            dev->cmd.token_mask <<= 1)
+               ; /* nothing */
+       --dev->cmd.token_mask;
+
+       dev->cmd.use_events = 1;
+       down(&dev->cmd.poll_sem);
+
+       return 0;
+}
+
+/*
+ * Switch back to polling (used when shutting down the device)
+ */
+void mthca_cmd_use_polling(struct mthca_dev *dev)
+{
+       int i;
+
+       dev->cmd.use_events = 0;
+
+       for (i = 0; i < dev->cmd.max_cmds; ++i)
+               down(&dev->cmd.event_sem);
+
+       kfree(dev->cmd.context);
+
+       up(&dev->cmd.poll_sem);
+}
+
+int mthca_SYS_EN(struct mthca_dev *dev, u8 *status)
+{
+       u64 out;
+       int ret;
+
+       ret = mthca_cmd_imm(dev, 0, &out, 0, 0, CMD_SYS_EN, HZ, status);
+
+       if (*status == MTHCA_CMD_STAT_DDR_MEM_ERR)
+               mthca_warn(dev, "SYS_EN DDR error: syn=%x, sock=%d, "
+                          "sladdr=%d, SPD source=%s\n",
+                          (int) (out >> 6) & 0xf, (int) (out >> 4) & 3,
+                          (int) (out >> 1) & 7, (int) out & 1 ? "NVMEM" : "DIMM");
+
+       return ret;
+}
+
+int mthca_SYS_DIS(struct mthca_dev *dev, u8 *status)
+{
+       return mthca_cmd(dev, 0, 0, 0, CMD_SYS_DIS, HZ, status);
+}
+
+static int mthca_map_cmd(struct mthca_dev *dev, u16 op, struct mthca_icm *icm,
+                        u64 virt, u8 *status)
+{
+       u32 *inbox;
+       dma_addr_t indma;
+       struct mthca_icm_iter iter;
+       int lg;
+       int nent = 0;
+       int i;
+       int err = 0;
+       int ts = 0, tc = 0;
+
+       inbox = pci_alloc_consistent(dev->pdev, PAGE_SIZE, &indma);
+       if (!inbox)
+               return -ENOMEM;
+
+       memset(inbox, 0, PAGE_SIZE);
+
+       for (mthca_icm_first(icm, &iter);
+            !mthca_icm_last(&iter);
+            mthca_icm_next(&iter)) {
+               /*
+                * We have to pass pages that are aligned to their
+                * size, so find the least significant 1 in the
+                * address or size and use that as our log2 size.
+                */
+               lg = ffs(mthca_icm_addr(&iter) | mthca_icm_size(&iter)) - 1;
+               if (lg < 12) {
+                       mthca_warn(dev, "Got FW area not aligned to 4K (%llx/%lx).\n",
+                                  (unsigned long long) mthca_icm_addr(&iter),
+                                  mthca_icm_size(&iter));
+                       err = -EINVAL;
+                       goto out;
+               }
+               for (i = 0; i < mthca_icm_size(&iter) / (1 << lg); ++i, ++nent) {
+                       if (virt != -1) {
+                               *((__be64 *) (inbox + nent * 4)) =
+                                       cpu_to_be64(virt);
+                               virt += 1 << lg;
+                       }
+
+                       *((__be64 *) (inbox + nent * 4 + 2)) =
+                               cpu_to_be64((mthca_icm_addr(&iter) +
+                                            (i << lg)) | (lg - 12));
+                       ts += 1 << (lg - 10);
+                       ++tc;
+
+                       if (nent == PAGE_SIZE / 16) {
+                               err = mthca_cmd(dev, indma, nent, 0, op,
+                                               CMD_TIME_CLASS_B, status);
+                               if (err || *status)
+                                       goto out;
+                               nent = 0;
+                       }
+               }
+       }
+
+       if (nent)
+               err = mthca_cmd(dev, indma, nent, 0, op,
+                               CMD_TIME_CLASS_B, status);
+
+       switch (op) {
+       case CMD_MAP_FA:
+               mthca_dbg(dev, "Mapped %d chunks/%d KB for FW.\n", tc, ts);
+               break;
+       case CMD_MAP_ICM_AUX:
+               mthca_dbg(dev, "Mapped %d chunks/%d KB for ICM aux.\n", tc, ts);
+               break;
+       case CMD_MAP_ICM:
+               mthca_dbg(dev, "Mapped %d chunks/%d KB at %llx for ICM.\n",
+                         tc, ts, (unsigned long long) virt - (ts << 10));
+               break;
+       }
+
+out:
+       pci_free_consistent(dev->pdev, PAGE_SIZE, inbox, indma);
+       return err;
+}
+
+int mthca_MAP_FA(struct mthca_dev *dev, struct mthca_icm *icm, u8 *status)
+{
+       return mthca_map_cmd(dev, CMD_MAP_FA, icm, -1, status);
+}
+
+int mthca_UNMAP_FA(struct mthca_dev *dev, u8 *status)
+{
+       return mthca_cmd(dev, 0, 0, 0, CMD_UNMAP_FA, CMD_TIME_CLASS_B, status);
+}
+
+int mthca_RUN_FW(struct mthca_dev *dev, u8 *status)
+{
+       return mthca_cmd(dev, 0, 0, 0, CMD_RUN_FW, CMD_TIME_CLASS_A, status);
+}
+
+int mthca_QUERY_FW(struct mthca_dev *dev, u8 *status)
+{
+       u32 *outbox;
+       dma_addr_t outdma;
+       int err = 0;
+       u8 lg;
+
+#define QUERY_FW_OUT_SIZE             0x100
+#define QUERY_FW_VER_OFFSET            0x00
+#define QUERY_FW_MAX_CMD_OFFSET        0x0f
+#define QUERY_FW_ERR_START_OFFSET      0x30
+#define QUERY_FW_ERR_SIZE_OFFSET       0x38
+
+#define QUERY_FW_START_OFFSET          0x20
+#define QUERY_FW_END_OFFSET            0x28
+
+#define QUERY_FW_SIZE_OFFSET           0x00
+#define QUERY_FW_CLR_INT_BASE_OFFSET   0x20
+#define QUERY_FW_EQ_ARM_BASE_OFFSET    0x40
+#define QUERY_FW_EQ_SET_CI_BASE_OFFSET 0x48
+
+       outbox = pci_alloc_consistent(dev->pdev, QUERY_FW_OUT_SIZE, &outdma);
+       if (!outbox) {
+               return -ENOMEM;
+       }
+
+       err = mthca_cmd_box(dev, 0, outdma, 0, 0, CMD_QUERY_FW,
+                           CMD_TIME_CLASS_A, status);
+
+       if (err)
+               goto out;
+
+       MTHCA_GET(dev->fw_ver,   outbox, QUERY_FW_VER_OFFSET);
+       /*
+        * FW subminor version is at more signifant bits than minor
+        * version, so swap here.
+        */
+       dev->fw_ver = (dev->fw_ver & 0xffff00000000ull) |
+               ((dev->fw_ver & 0xffff0000ull) >> 16) |
+               ((dev->fw_ver & 0x0000ffffull) << 16);
+
+       MTHCA_GET(lg, outbox, QUERY_FW_MAX_CMD_OFFSET);
+       dev->cmd.max_cmds = 1 << lg;
+
+       mthca_dbg(dev, "FW version %012llx, max commands %d\n",
+                 (unsigned long long) dev->fw_ver, dev->cmd.max_cmds);
+
+       if (dev->hca_type == ARBEL_NATIVE) {
+               MTHCA_GET(dev->fw.arbel.fw_pages,       outbox, QUERY_FW_SIZE_OFFSET);
+               MTHCA_GET(dev->fw.arbel.clr_int_base,   outbox, QUERY_FW_CLR_INT_BASE_OFFSET);
+               MTHCA_GET(dev->fw.arbel.eq_arm_base,    outbox, QUERY_FW_EQ_ARM_BASE_OFFSET);
+               MTHCA_GET(dev->fw.arbel.eq_set_ci_base, outbox, QUERY_FW_EQ_SET_CI_BASE_OFFSET);
+               mthca_dbg(dev, "FW size %d KB\n", dev->fw.arbel.fw_pages << 2);
+
+               /*
+                * Arbel page size is always 4 KB; round up number of
+                * system pages needed.
+                */
+               dev->fw.arbel.fw_pages =
+                       (dev->fw.arbel.fw_pages + (1 << (PAGE_SHIFT - 12)) - 1) >>
+                       (PAGE_SHIFT - 12);
+
+               mthca_dbg(dev, "Clear int @ %llx, EQ arm @ %llx, EQ set CI @ %llx\n",
+                         (unsigned long long) dev->fw.arbel.clr_int_base,
+                         (unsigned long long) dev->fw.arbel.eq_arm_base,
+                         (unsigned long long) dev->fw.arbel.eq_set_ci_base);
+       } else {
+               MTHCA_GET(dev->fw.tavor.fw_start, outbox, QUERY_FW_START_OFFSET);
+               MTHCA_GET(dev->fw.tavor.fw_end,   outbox, QUERY_FW_END_OFFSET);
+
+               mthca_dbg(dev, "FW size %d KB (start %llx, end %llx)\n",
+                         (int) ((dev->fw.tavor.fw_end - dev->fw.tavor.fw_start) >> 10),
+                         (unsigned long long) dev->fw.tavor.fw_start,
+                         (unsigned long long) dev->fw.tavor.fw_end);
+       }
+
+out:
+       pci_free_consistent(dev->pdev, QUERY_FW_OUT_SIZE, outbox, outdma);
+       return err;
+}
+
+int mthca_ENABLE_LAM(struct mthca_dev *dev, u8 *status)
+{
+       u8 info;
+       u32 *outbox;
+       dma_addr_t outdma;
+       int err = 0;
+
+#define ENABLE_LAM_OUT_SIZE         0x100
+#define ENABLE_LAM_START_OFFSET     0x00
+#define ENABLE_LAM_END_OFFSET       0x08
+#define ENABLE_LAM_INFO_OFFSET      0x13
+
+#define ENABLE_LAM_INFO_HIDDEN_FLAG (1 << 4)
+#define ENABLE_LAM_INFO_ECC_MASK    0x3
+
+       outbox = pci_alloc_consistent(dev->pdev, ENABLE_LAM_OUT_SIZE, &outdma);
+       if (!outbox)
+               return -ENOMEM;
+
+       err = mthca_cmd_box(dev, 0, outdma, 0, 0, CMD_ENABLE_LAM,
+                           CMD_TIME_CLASS_C, status);
+
+       if (err)
+               goto out;
+
+       if (*status == MTHCA_CMD_STAT_LAM_NOT_PRE)
+               goto out;
+
+       MTHCA_GET(dev->ddr_start, outbox, ENABLE_LAM_START_OFFSET);
+       MTHCA_GET(dev->ddr_end,   outbox, ENABLE_LAM_END_OFFSET);
+       MTHCA_GET(info,           outbox, ENABLE_LAM_INFO_OFFSET);
+
+       if (!!(info & ENABLE_LAM_INFO_HIDDEN_FLAG) !=
+           !!(dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) {
+               mthca_info(dev, "FW reports that HCA-attached memory "
+                          "is %s hidden; does not match PCI config\n",
+                          (info & ENABLE_LAM_INFO_HIDDEN_FLAG) ?
+                          "" : "not");
+       }
+       if (info & ENABLE_LAM_INFO_HIDDEN_FLAG)
+               mthca_dbg(dev, "HCA-attached memory is hidden.\n");
+
+       mthca_dbg(dev, "HCA memory size %d KB (start %llx, end %llx)\n",
+                 (int) ((dev->ddr_end - dev->ddr_start) >> 10),
+                 (unsigned long long) dev->ddr_start,
+                 (unsigned long long) dev->ddr_end);
+
+out:
+       pci_free_consistent(dev->pdev, ENABLE_LAM_OUT_SIZE, outbox, outdma);
+       return err;
+}
+
+int mthca_DISABLE_LAM(struct mthca_dev *dev, u8 *status)
+{
+       return mthca_cmd(dev, 0, 0, 0, CMD_SYS_DIS, CMD_TIME_CLASS_C, status);
+}
+
+int mthca_QUERY_DDR(struct mthca_dev *dev, u8 *status)
+{
+       u8 info;
+       u32 *outbox;
+       dma_addr_t outdma;
+       int err = 0;
+
+#define QUERY_DDR_OUT_SIZE         0x100
+#define QUERY_DDR_START_OFFSET     0x00
+#define QUERY_DDR_END_OFFSET       0x08
+#define QUERY_DDR_INFO_OFFSET      0x13
+
+#define QUERY_DDR_INFO_HIDDEN_FLAG (1 << 4)
+#define QUERY_DDR_INFO_ECC_MASK    0x3
+
+       outbox = pci_alloc_consistent(dev->pdev, QUERY_DDR_OUT_SIZE, &outdma);
+       if (!outbox)
+               return -ENOMEM;
+
+       err = mthca_cmd_box(dev, 0, outdma, 0, 0, CMD_QUERY_DDR,
+                           CMD_TIME_CLASS_A, status);
+
+       if (err)
+               goto out;
+
+       MTHCA_GET(dev->ddr_start, outbox, QUERY_DDR_START_OFFSET);
+       MTHCA_GET(dev->ddr_end,   outbox, QUERY_DDR_END_OFFSET);
+       MTHCA_GET(info,           outbox, QUERY_DDR_INFO_OFFSET);
+
+       if (!!(info & QUERY_DDR_INFO_HIDDEN_FLAG) !=
+           !!(dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) {
+               mthca_info(dev, "FW reports that HCA-attached memory "
+                          "is %s hidden; does not match PCI config\n",
+                          (info & QUERY_DDR_INFO_HIDDEN_FLAG) ?
+                          "" : "not");
+       }
+       if (info & QUERY_DDR_INFO_HIDDEN_FLAG)
+               mthca_dbg(dev, "HCA-attached memory is hidden.\n");
+
+       mthca_dbg(dev, "HCA memory size %d KB (start %llx, end %llx)\n",
+                 (int) ((dev->ddr_end - dev->ddr_start) >> 10),
+                 (unsigned long long) dev->ddr_start,
+                 (unsigned long long) dev->ddr_end);
+
+out:
+       pci_free_consistent(dev->pdev, QUERY_DDR_OUT_SIZE, outbox, outdma);
+       return err;
+}
+
+int mthca_QUERY_DEV_LIM(struct mthca_dev *dev,
+                       struct mthca_dev_lim *dev_lim, u8 *status)
+{
+       u32 *outbox;
+       dma_addr_t outdma;
+       u8 field;
+       u16 size;
+       int err;
+
+#define QUERY_DEV_LIM_OUT_SIZE             0x100
+#define QUERY_DEV_LIM_MAX_SRQ_SZ_OFFSET     0x10
+#define QUERY_DEV_LIM_MAX_QP_SZ_OFFSET      0x11
+#define QUERY_DEV_LIM_RSVD_QP_OFFSET        0x12
+#define QUERY_DEV_LIM_MAX_QP_OFFSET         0x13
+#define QUERY_DEV_LIM_RSVD_SRQ_OFFSET       0x14
+#define QUERY_DEV_LIM_MAX_SRQ_OFFSET        0x15
+#define QUERY_DEV_LIM_RSVD_EEC_OFFSET       0x16
+#define QUERY_DEV_LIM_MAX_EEC_OFFSET        0x17
+#define QUERY_DEV_LIM_MAX_CQ_SZ_OFFSET      0x19
+#define QUERY_DEV_LIM_RSVD_CQ_OFFSET        0x1a
+#define QUERY_DEV_LIM_MAX_CQ_OFFSET         0x1b
+#define QUERY_DEV_LIM_MAX_MPT_OFFSET        0x1d
+#define QUERY_DEV_LIM_RSVD_EQ_OFFSET        0x1e
+#define QUERY_DEV_LIM_MAX_EQ_OFFSET         0x1f
+#define QUERY_DEV_LIM_RSVD_MTT_OFFSET       0x20
+#define QUERY_DEV_LIM_MAX_MRW_SZ_OFFSET     0x21
+#define QUERY_DEV_LIM_RSVD_MRW_OFFSET       0x22
+#define QUERY_DEV_LIM_MAX_MTT_SEG_OFFSET    0x23
+#define QUERY_DEV_LIM_MAX_AV_OFFSET         0x27
+#define QUERY_DEV_LIM_MAX_REQ_QP_OFFSET     0x29
+#define QUERY_DEV_LIM_MAX_RES_QP_OFFSET     0x2b
+#define QUERY_DEV_LIM_MAX_RDMA_OFFSET       0x2f
+#define QUERY_DEV_LIM_RSZ_SRQ_OFFSET        0x33
+#define QUERY_DEV_LIM_ACK_DELAY_OFFSET      0x35
+#define QUERY_DEV_LIM_MTU_WIDTH_OFFSET      0x36
+#define QUERY_DEV_LIM_VL_PORT_OFFSET        0x37
+#define QUERY_DEV_LIM_MAX_GID_OFFSET        0x3b
+#define QUERY_DEV_LIM_MAX_PKEY_OFFSET       0x3f
+#define QUERY_DEV_LIM_FLAGS_OFFSET          0x44
+#define QUERY_DEV_LIM_RSVD_UAR_OFFSET       0x48
+#define QUERY_DEV_LIM_UAR_SZ_OFFSET         0x49
+#define QUERY_DEV_LIM_PAGE_SZ_OFFSET        0x4b
+#define QUERY_DEV_LIM_MAX_SG_OFFSET         0x51
+#define QUERY_DEV_LIM_MAX_DESC_SZ_OFFSET    0x52
+#define QUERY_DEV_LIM_MAX_SG_RQ_OFFSET      0x55
+#define QUERY_DEV_LIM_MAX_DESC_SZ_RQ_OFFSET 0x56
+#define QUERY_DEV_LIM_MAX_QP_MCG_OFFSET     0x61
+#define QUERY_DEV_LIM_RSVD_MCG_OFFSET       0x62
+#define QUERY_DEV_LIM_MAX_MCG_OFFSET        0x63
+#define QUERY_DEV_LIM_RSVD_PD_OFFSET        0x64
+#define QUERY_DEV_LIM_MAX_PD_OFFSET         0x65
+#define QUERY_DEV_LIM_RSVD_RDD_OFFSET       0x66
+#define QUERY_DEV_LIM_MAX_RDD_OFFSET        0x67
+#define QUERY_DEV_LIM_EEC_ENTRY_SZ_OFFSET   0x80
+#define QUERY_DEV_LIM_QPC_ENTRY_SZ_OFFSET   0x82
+#define QUERY_DEV_LIM_EEEC_ENTRY_SZ_OFFSET  0x84
+#define QUERY_DEV_LIM_EQPC_ENTRY_SZ_OFFSET  0x86
+#define QUERY_DEV_LIM_EQC_ENTRY_SZ_OFFSET   0x88
+#define QUERY_DEV_LIM_CQC_ENTRY_SZ_OFFSET   0x8a
+#define QUERY_DEV_LIM_SRQ_ENTRY_SZ_OFFSET   0x8c
+#define QUERY_DEV_LIM_UAR_ENTRY_SZ_OFFSET   0x8e
+#define QUERY_DEV_LIM_MTT_ENTRY_SZ_OFFSET   0x90
+#define QUERY_DEV_LIM_MPT_ENTRY_SZ_OFFSET   0x92
+#define QUERY_DEV_LIM_PBL_SZ_OFFSET         0x96
+#define QUERY_DEV_LIM_BMME_FLAGS_OFFSET     0x97
+#define QUERY_DEV_LIM_RSVD_LKEY_OFFSET      0x98
+#define QUERY_DEV_LIM_LAMR_OFFSET           0x9f
+#define QUERY_DEV_LIM_MAX_ICM_SZ_OFFSET     0xa0
+
+       outbox = pci_alloc_consistent(dev->pdev, QUERY_DEV_LIM_OUT_SIZE, &outdma);
+       if (!outbox)
+               return -ENOMEM;
+
+       err = mthca_cmd_box(dev, 0, outdma, 0, 0, CMD_QUERY_DEV_LIM,
+                           CMD_TIME_CLASS_A, status);
+
+       if (err)
+               goto out;
+
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_SRQ_SZ_OFFSET);
+       dev_lim->max_srq_sz = 1 << field;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_QP_SZ_OFFSET);
+       dev_lim->max_qp_sz = 1 << field;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_QP_OFFSET);
+       dev_lim->reserved_qps = 1 << (field & 0xf);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_QP_OFFSET);
+       dev_lim->max_qps = 1 << (field & 0x1f);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_SRQ_OFFSET);
+       dev_lim->reserved_srqs = 1 << (field >> 4);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_SRQ_OFFSET);
+       dev_lim->max_srqs = 1 << (field & 0x1f);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_EEC_OFFSET);
+       dev_lim->reserved_eecs = 1 << (field & 0xf);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_EEC_OFFSET);
+       dev_lim->max_eecs = 1 << (field & 0x1f);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_CQ_SZ_OFFSET);
+       dev_lim->max_cq_sz = 1 << field;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_CQ_OFFSET);
+       dev_lim->reserved_cqs = 1 << (field & 0xf);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_CQ_OFFSET);
+       dev_lim->max_cqs = 1 << (field & 0x1f);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_MPT_OFFSET);
+       dev_lim->max_mpts = 1 << (field & 0x3f);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_EQ_OFFSET);
+       dev_lim->reserved_eqs = 1 << (field & 0xf);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_EQ_OFFSET);
+       dev_lim->max_eqs = 1 << (field & 0x7);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_MTT_OFFSET);
+       dev_lim->reserved_mtts = 1 << (field >> 4);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_MRW_SZ_OFFSET);
+       dev_lim->max_mrw_sz = 1 << field;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_MRW_OFFSET);
+       dev_lim->reserved_mrws = 1 << (field & 0xf);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_MTT_SEG_OFFSET);
+       dev_lim->max_mtt_seg = 1 << (field & 0x3f);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_REQ_QP_OFFSET);
+       dev_lim->max_requester_per_qp = 1 << (field & 0x3f);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_RES_QP_OFFSET);
+       dev_lim->max_responder_per_qp = 1 << (field & 0x3f);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_RDMA_OFFSET);
+       dev_lim->max_rdma_global = 1 << (field & 0x3f);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_ACK_DELAY_OFFSET);
+       dev_lim->local_ca_ack_delay = field & 0x1f;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MTU_WIDTH_OFFSET);
+       dev_lim->max_mtu        = field >> 4;
+       dev_lim->max_port_width = field & 0xf;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_VL_PORT_OFFSET);
+       dev_lim->max_vl    = field >> 4;
+       dev_lim->num_ports = field & 0xf;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_GID_OFFSET);
+       dev_lim->max_gids = 1 << (field & 0xf);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_PKEY_OFFSET);
+       dev_lim->max_pkeys = 1 << (field & 0xf);
+       MTHCA_GET(dev_lim->flags, outbox, QUERY_DEV_LIM_FLAGS_OFFSET);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_UAR_OFFSET);
+       dev_lim->reserved_uars = field >> 4;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_UAR_SZ_OFFSET);
+       dev_lim->uar_size = 1 << ((field & 0x3f) + 20);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_PAGE_SZ_OFFSET);
+       dev_lim->min_page_sz = 1 << field;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_SG_OFFSET);
+       dev_lim->max_sg = field;
+
+       MTHCA_GET(size, outbox, QUERY_DEV_LIM_MAX_DESC_SZ_OFFSET);
+       dev_lim->max_desc_sz = size;
+
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_QP_MCG_OFFSET);
+       dev_lim->max_qp_per_mcg = 1 << field;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_MCG_OFFSET);
+       dev_lim->reserved_mgms = field & 0xf;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_MCG_OFFSET);
+       dev_lim->max_mcgs = 1 << field;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_PD_OFFSET);
+       dev_lim->reserved_pds = field >> 4;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_PD_OFFSET);
+       dev_lim->max_pds = 1 << (field & 0x3f);
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSVD_RDD_OFFSET);
+       dev_lim->reserved_rdds = field >> 4;
+       MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_RDD_OFFSET);
+       dev_lim->max_rdds = 1 << (field & 0x3f);
+
+       MTHCA_GET(size, outbox, QUERY_DEV_LIM_EEC_ENTRY_SZ_OFFSET);
+       dev_lim->eec_entry_sz = size;
+       MTHCA_GET(size, outbox, QUERY_DEV_LIM_QPC_ENTRY_SZ_OFFSET);
+       dev_lim->qpc_entry_sz = size;
+       MTHCA_GET(size, outbox, QUERY_DEV_LIM_EEEC_ENTRY_SZ_OFFSET);
+       dev_lim->eeec_entry_sz = size;
+       MTHCA_GET(size, outbox, QUERY_DEV_LIM_EQPC_ENTRY_SZ_OFFSET);
+       dev_lim->eqpc_entry_sz = size;
+       MTHCA_GET(size, outbox, QUERY_DEV_LIM_EQC_ENTRY_SZ_OFFSET);
+       dev_lim->eqc_entry_sz = size;
+       MTHCA_GET(size, outbox, QUERY_DEV_LIM_CQC_ENTRY_SZ_OFFSET);
+       dev_lim->cqc_entry_sz = size;
+       MTHCA_GET(size, outbox, QUERY_DEV_LIM_SRQ_ENTRY_SZ_OFFSET);
+       dev_lim->srq_entry_sz = size;
+       MTHCA_GET(size, outbox, QUERY_DEV_LIM_UAR_ENTRY_SZ_OFFSET);
+       dev_lim->uar_scratch_entry_sz = size;
+
+       mthca_dbg(dev, "Max QPs: %d, reserved QPs: %d, entry size: %d\n",
+                 dev_lim->max_qps, dev_lim->reserved_qps, dev_lim->qpc_entry_sz);
+       mthca_dbg(dev, "Max CQs: %d, reserved CQs: %d, entry size: %d\n",
+                 dev_lim->max_cqs, dev_lim->reserved_cqs, dev_lim->cqc_entry_sz);
+       mthca_dbg(dev, "Max EQs: %d, reserved EQs: %d, entry size: %d\n",
+                 dev_lim->max_eqs, dev_lim->reserved_eqs, dev_lim->eqc_entry_sz);
+       mthca_dbg(dev, "reserved MPTs: %d, reserved MTTs: %d\n",
+                 dev_lim->reserved_mrws, dev_lim->reserved_mtts);
+       mthca_dbg(dev, "Max PDs: %d, reserved PDs: %d, reserved UARs: %d\n",
+                 dev_lim->max_pds, dev_lim->reserved_pds, dev_lim->reserved_uars);
+       mthca_dbg(dev, "Max QP/MCG: %d, reserved MGMs: %d\n",
+                 dev_lim->max_pds, dev_lim->reserved_mgms);
+
+       mthca_dbg(dev, "Flags: %08x\n", dev_lim->flags);
+
+       if (dev->hca_type == ARBEL_NATIVE) {
+               MTHCA_GET(field, outbox, QUERY_DEV_LIM_RSZ_SRQ_OFFSET);
+               dev_lim->hca.arbel.resize_srq = field & 1;
+               MTHCA_GET(size, outbox, QUERY_DEV_LIM_MTT_ENTRY_SZ_OFFSET);
+               dev_lim->mtt_seg_sz = size;
+               MTHCA_GET(size, outbox, QUERY_DEV_LIM_MPT_ENTRY_SZ_OFFSET);
+               dev_lim->mpt_entry_sz = size;
+               MTHCA_GET(field, outbox, QUERY_DEV_LIM_PBL_SZ_OFFSET);
+               dev_lim->hca.arbel.max_pbl_sz = 1 << (field & 0x3f);
+               MTHCA_GET(dev_lim->hca.arbel.bmme_flags, outbox,
+                         QUERY_DEV_LIM_BMME_FLAGS_OFFSET);
+               MTHCA_GET(dev_lim->hca.arbel.reserved_lkey, outbox,
+                         QUERY_DEV_LIM_RSVD_LKEY_OFFSET);
+               MTHCA_GET(field, outbox, QUERY_DEV_LIM_LAMR_OFFSET);
+               dev_lim->hca.arbel.lam_required = field & 1;
+               MTHCA_GET(dev_lim->hca.arbel.max_icm_sz, outbox,
+                         QUERY_DEV_LIM_MAX_ICM_SZ_OFFSET);
+
+               if (dev_lim->hca.arbel.bmme_flags & 1)
+                       mthca_dbg(dev, "Base MM extensions: yes "
+                                 "(flags %d, max PBL %d, rsvd L_Key %08x)\n",
+                                 dev_lim->hca.arbel.bmme_flags,
+                                 dev_lim->hca.arbel.max_pbl_sz,
+                                 dev_lim->hca.arbel.reserved_lkey);
+               else
+                       mthca_dbg(dev, "Base MM extensions: no\n");
+
+               mthca_dbg(dev, "Max ICM size %lld MB\n",
+                         (unsigned long long) dev_lim->hca.arbel.max_icm_sz >> 20);
+       } else {
+               MTHCA_GET(field, outbox, QUERY_DEV_LIM_MAX_AV_OFFSET);
+               dev_lim->hca.tavor.max_avs = 1 << (field & 0x3f);
+               dev_lim->mtt_seg_sz   = MTHCA_MTT_SEG_SIZE;
+               dev_lim->mpt_entry_sz = MTHCA_MPT_ENTRY_SIZE;
+       }
+
+out:
+       pci_free_consistent(dev->pdev, QUERY_DEV_LIM_OUT_SIZE, outbox, outdma);
+       return err;
+}
+
+int mthca_QUERY_ADAPTER(struct mthca_dev *dev,
+                       struct mthca_adapter *adapter, u8 *status)
+{
+       u32 *outbox;
+       dma_addr_t outdma;
+       int err;
+
+#define QUERY_ADAPTER_OUT_SIZE             0x100
+#define QUERY_ADAPTER_VENDOR_ID_OFFSET     0x00
+#define QUERY_ADAPTER_DEVICE_ID_OFFSET     0x04
+#define QUERY_ADAPTER_REVISION_ID_OFFSET   0x08
+#define QUERY_ADAPTER_INTA_PIN_OFFSET      0x10
+
+       outbox = pci_alloc_consistent(dev->pdev, QUERY_ADAPTER_OUT_SIZE, &outdma);
+       if (!outbox)
+               return -ENOMEM;
+
+       err = mthca_cmd_box(dev, 0, outdma, 0, 0, CMD_QUERY_ADAPTER,
+                           CMD_TIME_CLASS_A, status);
+
+       if (err)
+               goto out;
+
+       MTHCA_GET(adapter->vendor_id, outbox, QUERY_ADAPTER_VENDOR_ID_OFFSET);
+       MTHCA_GET(adapter->device_id, outbox, QUERY_ADAPTER_DEVICE_ID_OFFSET);
+       MTHCA_GET(adapter->revision_id, outbox, QUERY_ADAPTER_REVISION_ID_OFFSET);
+       MTHCA_GET(adapter->inta_pin, outbox, QUERY_ADAPTER_INTA_PIN_OFFSET);
+
+out:
+       pci_free_consistent(dev->pdev, QUERY_DEV_LIM_OUT_SIZE, outbox, outdma);
+       return err;
+}
+
+int mthca_INIT_HCA(struct mthca_dev *dev,
+                  struct mthca_init_hca_param *param,
+                  u8 *status)
+{
+       u32 *inbox;
+       dma_addr_t indma;
+       int err;
+
+#define INIT_HCA_IN_SIZE                0x200
+#define INIT_HCA_FLAGS_OFFSET           0x014
+#define INIT_HCA_QPC_OFFSET             0x020
+#define  INIT_HCA_QPC_BASE_OFFSET       (INIT_HCA_QPC_OFFSET + 0x10)
+#define  INIT_HCA_LOG_QP_OFFSET         (INIT_HCA_QPC_OFFSET + 0x17)
+#define  INIT_HCA_EEC_BASE_OFFSET       (INIT_HCA_QPC_OFFSET + 0x20)
+#define  INIT_HCA_LOG_EEC_OFFSET        (INIT_HCA_QPC_OFFSET + 0x27)
+#define  INIT_HCA_SRQC_BASE_OFFSET      (INIT_HCA_QPC_OFFSET + 0x28)
+#define  INIT_HCA_LOG_SRQ_OFFSET        (INIT_HCA_QPC_OFFSET + 0x2f)
+#define  INIT_HCA_CQC_BASE_OFFSET       (INIT_HCA_QPC_OFFSET + 0x30)
+#define  INIT_HCA_LOG_CQ_OFFSET         (INIT_HCA_QPC_OFFSET + 0x37)
+#define  INIT_HCA_EQPC_BASE_OFFSET      (INIT_HCA_QPC_OFFSET + 0x40)
+#define  INIT_HCA_EEEC_BASE_OFFSET      (INIT_HCA_QPC_OFFSET + 0x50)
+#define  INIT_HCA_EQC_BASE_OFFSET       (INIT_HCA_QPC_OFFSET + 0x60)
+#define  INIT_HCA_LOG_EQ_OFFSET         (INIT_HCA_QPC_OFFSET + 0x67)
+#define  INIT_HCA_RDB_BASE_OFFSET       (INIT_HCA_QPC_OFFSET + 0x70)
+#define INIT_HCA_UDAV_OFFSET            0x0b0
+#define  INIT_HCA_UDAV_LKEY_OFFSET      (INIT_HCA_UDAV_OFFSET + 0x0)
+#define  INIT_HCA_UDAV_PD_OFFSET        (INIT_HCA_UDAV_OFFSET + 0x4)
+#define INIT_HCA_MCAST_OFFSET           0x0c0
+#define  INIT_HCA_MC_BASE_OFFSET         (INIT_HCA_MCAST_OFFSET + 0x00)
+#define  INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x12)
+#define  INIT_HCA_MC_HASH_SZ_OFFSET      (INIT_HCA_MCAST_OFFSET + 0x16)
+#define  INIT_HCA_LOG_MC_TABLE_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x1b)
+#define INIT_HCA_TPT_OFFSET              0x0f0
+#define  INIT_HCA_MPT_BASE_OFFSET        (INIT_HCA_TPT_OFFSET + 0x00)
+#define  INIT_HCA_MTT_SEG_SZ_OFFSET      (INIT_HCA_TPT_OFFSET + 0x09)
+#define  INIT_HCA_LOG_MPT_SZ_OFFSET      (INIT_HCA_TPT_OFFSET + 0x0b)
+#define  INIT_HCA_MTT_BASE_OFFSET        (INIT_HCA_TPT_OFFSET + 0x10)
+#define INIT_HCA_UAR_OFFSET              0x120
+#define  INIT_HCA_UAR_BASE_OFFSET        (INIT_HCA_UAR_OFFSET + 0x00)
+#define  INIT_HCA_UARC_SZ_OFFSET         (INIT_HCA_UAR_OFFSET + 0x09)
+#define  INIT_HCA_LOG_UAR_SZ_OFFSET      (INIT_HCA_UAR_OFFSET + 0x0a)
+#define  INIT_HCA_UAR_PAGE_SZ_OFFSET     (INIT_HCA_UAR_OFFSET + 0x0b)
+#define  INIT_HCA_UAR_SCATCH_BASE_OFFSET (INIT_HCA_UAR_OFFSET + 0x10)
+#define  INIT_HCA_UAR_CTX_BASE_OFFSET    (INIT_HCA_UAR_OFFSET + 0x18)
+
+       inbox = pci_alloc_consistent(dev->pdev, INIT_HCA_IN_SIZE, &indma);
+       if (!inbox)
+               return -ENOMEM;
+
+       memset(inbox, 0, INIT_HCA_IN_SIZE);
+
+#if defined(__LITTLE_ENDIAN)
+       *(inbox + INIT_HCA_FLAGS_OFFSET / 4) &= ~cpu_to_be32(1 << 1);
+#elif defined(__BIG_ENDIAN)
+       *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1 << 1);
+#else
+#error Host endianness not defined
+#endif
+       /* Check port for UD address vector: */
+       *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= cpu_to_be32(1);
+
+       /* We leave wqe_quota, responder_exu, etc as 0 (default) */
+
+       /* QPC/EEC/CQC/EQC/RDB attributes */
+
+       MTHCA_PUT(inbox, param->qpc_base,     INIT_HCA_QPC_BASE_OFFSET);
+       MTHCA_PUT(inbox, param->log_num_qps,  INIT_HCA_LOG_QP_OFFSET);
+       MTHCA_PUT(inbox, param->eec_base,     INIT_HCA_EEC_BASE_OFFSET);
+       MTHCA_PUT(inbox, param->log_num_eecs, INIT_HCA_LOG_EEC_OFFSET);
+       MTHCA_PUT(inbox, param->srqc_base,    INIT_HCA_SRQC_BASE_OFFSET);
+       MTHCA_PUT(inbox, param->log_num_srqs, INIT_HCA_LOG_SRQ_OFFSET);
+       MTHCA_PUT(inbox, param->cqc_base,     INIT_HCA_CQC_BASE_OFFSET);
+       MTHCA_PUT(inbox, param->log_num_cqs,  INIT_HCA_LOG_CQ_OFFSET);
+       MTHCA_PUT(inbox, param->eqpc_base,    INIT_HCA_EQPC_BASE_OFFSET);
+       MTHCA_PUT(inbox, param->eeec_base,    INIT_HCA_EEEC_BASE_OFFSET);
+       MTHCA_PUT(inbox, param->eqc_base,     INIT_HCA_EQC_BASE_OFFSET);
+       MTHCA_PUT(inbox, param->log_num_eqs,  INIT_HCA_LOG_EQ_OFFSET);
+       MTHCA_PUT(inbox, param->rdb_base,     INIT_HCA_RDB_BASE_OFFSET);
+
+       /* UD AV attributes */
+
+       /* multicast attributes */
+
+       MTHCA_PUT(inbox, param->mc_base,         INIT_HCA_MC_BASE_OFFSET);
+       MTHCA_PUT(inbox, param->log_mc_entry_sz, INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET);
+       MTHCA_PUT(inbox, param->mc_hash_sz,      INIT_HCA_MC_HASH_SZ_OFFSET);
+       MTHCA_PUT(inbox, param->log_mc_table_sz, INIT_HCA_LOG_MC_TABLE_SZ_OFFSET);
+
+       /* TPT attributes */
+
+       MTHCA_PUT(inbox, param->mpt_base,   INIT_HCA_MPT_BASE_OFFSET);
+       if (dev->hca_type != ARBEL_NATIVE)
+               MTHCA_PUT(inbox, param->mtt_seg_sz, INIT_HCA_MTT_SEG_SZ_OFFSET);
+       MTHCA_PUT(inbox, param->log_mpt_sz, INIT_HCA_LOG_MPT_SZ_OFFSET);
+       MTHCA_PUT(inbox, param->mtt_base,   INIT_HCA_MTT_BASE_OFFSET);
+
+       /* UAR attributes */
+       {
+               u8 uar_page_sz = PAGE_SHIFT - 12;
+               MTHCA_PUT(inbox, uar_page_sz, INIT_HCA_UAR_PAGE_SZ_OFFSET);
+       }
+
+       MTHCA_PUT(inbox, param->uar_scratch_base, INIT_HCA_UAR_SCATCH_BASE_OFFSET);
+
+       if (dev->hca_type == ARBEL_NATIVE) {
+               MTHCA_PUT(inbox, param->log_uarc_sz, INIT_HCA_UARC_SZ_OFFSET);
+               MTHCA_PUT(inbox, param->log_uar_sz,  INIT_HCA_LOG_UAR_SZ_OFFSET);
+               MTHCA_PUT(inbox, param->uarc_base,   INIT_HCA_UAR_CTX_BASE_OFFSET);
+       }
+
+       err = mthca_cmd(dev, indma, 0, 0, CMD_INIT_HCA,
+                       HZ, status);
+
+       pci_free_consistent(dev->pdev, INIT_HCA_IN_SIZE, inbox, indma);
+       return err;
+}
+
+int mthca_INIT_IB(struct mthca_dev *dev,
+                 struct mthca_init_ib_param *param,
+                 int port, u8 *status)
+{
+       u32 *inbox;
+       dma_addr_t indma;
+       int err;
+       u32 flags;
+
+#define INIT_IB_IN_SIZE          56
+#define INIT_IB_FLAGS_OFFSET     0x00
+#define INIT_IB_FLAG_SIG         (1 << 18)
+#define INIT_IB_FLAG_NG          (1 << 17)
+#define INIT_IB_FLAG_G0          (1 << 16)
+#define INIT_IB_FLAG_1X          (1 << 8)
+#define INIT_IB_FLAG_4X          (1 << 9)
+#define INIT_IB_FLAG_12X         (1 << 11)
+#define INIT_IB_VL_SHIFT         4
+#define INIT_IB_MTU_SHIFT        12
+#define INIT_IB_MAX_GID_OFFSET   0x06
+#define INIT_IB_MAX_PKEY_OFFSET  0x0a
+#define INIT_IB_GUID0_OFFSET     0x10
+#define INIT_IB_NODE_GUID_OFFSET 0x18
+#define INIT_IB_SI_GUID_OFFSET   0x20
+
+       inbox = pci_alloc_consistent(dev->pdev, INIT_IB_IN_SIZE, &indma);
+       if (!inbox)
+               return -ENOMEM;
+
+       memset(inbox, 0, INIT_IB_IN_SIZE);
+
+       flags = 0;
+       flags |= param->enable_1x     ? INIT_IB_FLAG_1X  : 0;
+       flags |= param->enable_4x     ? INIT_IB_FLAG_4X  : 0;
+       flags |= param->set_guid0     ? INIT_IB_FLAG_G0  : 0;
+       flags |= param->set_node_guid ? INIT_IB_FLAG_NG  : 0;
+       flags |= param->set_si_guid   ? INIT_IB_FLAG_SIG : 0;
+       flags |= param->vl_cap << INIT_IB_VL_SHIFT;
+       flags |= param->mtu_cap << INIT_IB_MTU_SHIFT;
+       MTHCA_PUT(inbox, flags, INIT_IB_FLAGS_OFFSET);
+
+       MTHCA_PUT(inbox, param->gid_cap,   INIT_IB_MAX_GID_OFFSET);
+       MTHCA_PUT(inbox, param->pkey_cap,  INIT_IB_MAX_PKEY_OFFSET);
+       MTHCA_PUT(inbox, param->guid0,     INIT_IB_GUID0_OFFSET);
+       MTHCA_PUT(inbox, param->node_guid, INIT_IB_NODE_GUID_OFFSET);
+       MTHCA_PUT(inbox, param->si_guid,   INIT_IB_SI_GUID_OFFSET);
+
+       err = mthca_cmd(dev, indma, port, 0, CMD_INIT_IB,
+                       CMD_TIME_CLASS_A, status);
+
+       pci_free_consistent(dev->pdev, INIT_HCA_IN_SIZE, inbox, indma);
+       return err;
+}
+
+int mthca_CLOSE_IB(struct mthca_dev *dev, int port, u8 *status)
+{
+       return mthca_cmd(dev, 0, port, 0, CMD_CLOSE_IB, HZ, status);
+}
+
+int mthca_CLOSE_HCA(struct mthca_dev *dev, int panic, u8 *status)
+{
+       return mthca_cmd(dev, 0, 0, panic, CMD_CLOSE_HCA, HZ, status);
+}
+
+int mthca_SET_IB(struct mthca_dev *dev, struct mthca_set_ib_param *param,
+                int port, u8 *status)
+{
+       u32 *inbox;
+       dma_addr_t indma;
+       int err;
+       u32 flags = 0;
+
+#define SET_IB_IN_SIZE         0x40
+#define SET_IB_FLAGS_OFFSET    0x00
+#define SET_IB_FLAG_SIG        (1 << 18)
+#define SET_IB_FLAG_RQK        (1 <<  0)
+#define SET_IB_CAP_MASK_OFFSET 0x04
+#define SET_IB_SI_GUID_OFFSET  0x08
+
+       inbox = pci_alloc_consistent(dev->pdev, SET_IB_IN_SIZE, &indma);
+       if (!inbox)
+               return -ENOMEM;
+
+       memset(inbox, 0, SET_IB_IN_SIZE);
+
+       flags |= param->set_si_guid     ? SET_IB_FLAG_SIG : 0;
+       flags |= param->reset_qkey_viol ? SET_IB_FLAG_RQK : 0;
+       MTHCA_PUT(inbox, flags, SET_IB_FLAGS_OFFSET);
+
+       MTHCA_PUT(inbox, param->cap_mask, SET_IB_CAP_MASK_OFFSET);
+       MTHCA_PUT(inbox, param->si_guid,  SET_IB_SI_GUID_OFFSET);
+
+       err = mthca_cmd(dev, indma, port, 0, CMD_SET_IB,
+                       CMD_TIME_CLASS_B, status);
+
+       pci_free_consistent(dev->pdev, INIT_HCA_IN_SIZE, inbox, indma);
+       return err;
+}
+
+int mthca_MAP_ICM(struct mthca_dev *dev, struct mthca_icm *icm, u64 virt, u8 *status)
+{
+       return mthca_map_cmd(dev, CMD_MAP_ICM, icm, virt, status);
+}
+
+int mthca_MAP_ICM_page(struct mthca_dev *dev, u64 dma_addr, u64 virt, u8 *status)
+{
+       u64 *inbox;
+       dma_addr_t indma;
+       int err;
+
+       inbox = pci_alloc_consistent(dev->pdev, 16, &indma);
+       if (!inbox)
+               return -ENOMEM;
+
+       inbox[0] = cpu_to_be64(virt);
+       inbox[1] = cpu_to_be64(dma_addr | (PAGE_SHIFT - 12));
+
+       err = mthca_cmd(dev, indma, 1, 0, CMD_MAP_ICM, CMD_TIME_CLASS_B, status);
+
+       pci_free_consistent(dev->pdev, 16, inbox, indma);
+
+       if (!err)
+               mthca_dbg(dev, "Mapped page at %llx for ICM.\n",
+                         (unsigned long long) virt);
+
+       return err;
+}
+
+int mthca_UNMAP_ICM(struct mthca_dev *dev, u64 virt, u32 page_count, u8 *status)
+{
+       return mthca_cmd(dev, virt, page_count, 0, CMD_UNMAP_ICM, CMD_TIME_CLASS_B, status);
+}
+
+int mthca_MAP_ICM_AUX(struct mthca_dev *dev, struct mthca_icm *icm, u8 *status)
+{
+       return mthca_map_cmd(dev, CMD_MAP_ICM_AUX, icm, -1, status);
+}
+
+int mthca_UNMAP_ICM_AUX(struct mthca_dev *dev, u8 *status)
+{
+       return mthca_cmd(dev, 0, 0, 0, CMD_UNMAP_ICM_AUX, CMD_TIME_CLASS_B, status);
+}
+
+int mthca_SET_ICM_SIZE(struct mthca_dev *dev, u64 icm_size, u64 *aux_pages,
+                      u8 *status)
+{
+       int ret = mthca_cmd_imm(dev, icm_size, aux_pages, 0, 0, CMD_SET_ICM_SIZE,
+                               CMD_TIME_CLASS_A, status);
+
+       if (ret || status)
+               return ret;
+
+       /*
+        * Arbel page size is always 4 KB; round up number of system
+        * pages needed.
+        */
+       *aux_pages = (*aux_pages + (1 << (PAGE_SHIFT - 12)) - 1) >> (PAGE_SHIFT - 12);
+
+       return 0;
+}
+
+int mthca_SW2HW_MPT(struct mthca_dev *dev, void *mpt_entry,
+                   int mpt_index, u8 *status)
+{
+       dma_addr_t indma;
+       int err;
+
+       indma = pci_map_single(dev->pdev, mpt_entry,
+                              MTHCA_MPT_ENTRY_SIZE,
+                              PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(indma))
+               return -ENOMEM;
+
+       err = mthca_cmd(dev, indma, mpt_index, 0, CMD_SW2HW_MPT,
+                       CMD_TIME_CLASS_B, status);
+
+       pci_unmap_single(dev->pdev, indma,
+                        MTHCA_MPT_ENTRY_SIZE, PCI_DMA_TODEVICE);
+       return err;
+}
+
+int mthca_HW2SW_MPT(struct mthca_dev *dev, void *mpt_entry,
+                   int mpt_index, u8 *status)
+{
+       dma_addr_t outdma = 0;
+       int err;
+
+       if (mpt_entry) {
+               outdma = pci_map_single(dev->pdev, mpt_entry,
+                                       MTHCA_MPT_ENTRY_SIZE,
+                                       PCI_DMA_FROMDEVICE);
+               if (pci_dma_mapping_error(outdma))
+                       return -ENOMEM;
+       }
+
+       err = mthca_cmd_box(dev, 0, outdma, mpt_index, !mpt_entry,
+                           CMD_HW2SW_MPT,
+                           CMD_TIME_CLASS_B, status);
+
+       if (mpt_entry)
+               pci_unmap_single(dev->pdev, outdma,
+                                MTHCA_MPT_ENTRY_SIZE,
+                                PCI_DMA_FROMDEVICE);
+       return err;
+}
+
+int mthca_WRITE_MTT(struct mthca_dev *dev, u64 *mtt_entry,
+                   int num_mtt, u8 *status)
+{
+       dma_addr_t indma;
+       int err;
+
+       indma = pci_map_single(dev->pdev, mtt_entry,
+                              (num_mtt + 2) * 8,
+                              PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(indma))
+               return -ENOMEM;
+
+       err = mthca_cmd(dev, indma, num_mtt, 0, CMD_WRITE_MTT,
+                       CMD_TIME_CLASS_B, status);
+
+       pci_unmap_single(dev->pdev, indma,
+                        (num_mtt + 2) * 8, PCI_DMA_TODEVICE);
+       return err;
+}
+
+int mthca_MAP_EQ(struct mthca_dev *dev, u64 event_mask, int unmap,
+                int eq_num, u8 *status)
+{
+       mthca_dbg(dev, "%s mask %016llx for eqn %d\n",
+                 unmap ? "Clearing" : "Setting",
+                 (unsigned long long) event_mask, eq_num);
+       return mthca_cmd(dev, event_mask, (unmap << 31) | eq_num,
+                        0, CMD_MAP_EQ, CMD_TIME_CLASS_B, status);
+}
+
+int mthca_SW2HW_EQ(struct mthca_dev *dev, void *eq_context,
+                  int eq_num, u8 *status)
+{
+       dma_addr_t indma;
+       int err;
+
+       indma = pci_map_single(dev->pdev, eq_context,
+                              MTHCA_EQ_CONTEXT_SIZE,
+                              PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(indma))
+               return -ENOMEM;
+
+       err = mthca_cmd(dev, indma, eq_num, 0, CMD_SW2HW_EQ,
+                       CMD_TIME_CLASS_A, status);
+
+       pci_unmap_single(dev->pdev, indma,
+                        MTHCA_EQ_CONTEXT_SIZE, PCI_DMA_TODEVICE);
+       return err;
+}
+
+int mthca_HW2SW_EQ(struct mthca_dev *dev, void *eq_context,
+                  int eq_num, u8 *status)
+{
+       dma_addr_t outdma = 0;
+       int err;
+
+       outdma = pci_map_single(dev->pdev, eq_context,
+                               MTHCA_EQ_CONTEXT_SIZE,
+                               PCI_DMA_FROMDEVICE);
+       if (pci_dma_mapping_error(outdma))
+               return -ENOMEM;
+
+       err = mthca_cmd_box(dev, 0, outdma, eq_num, 0,
+                           CMD_HW2SW_EQ,
+                           CMD_TIME_CLASS_A, status);
+
+       pci_unmap_single(dev->pdev, outdma,
+                        MTHCA_EQ_CONTEXT_SIZE,
+                        PCI_DMA_FROMDEVICE);
+       return err;
+}
+
+int mthca_SW2HW_CQ(struct mthca_dev *dev, void *cq_context,
+                  int cq_num, u8 *status)
+{
+       dma_addr_t indma;
+       int err;
+
+       indma = pci_map_single(dev->pdev, cq_context,
+                              MTHCA_CQ_CONTEXT_SIZE,
+                              PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(indma))
+               return -ENOMEM;
+
+       err = mthca_cmd(dev, indma, cq_num, 0, CMD_SW2HW_CQ,
+                       CMD_TIME_CLASS_A, status);
+
+       pci_unmap_single(dev->pdev, indma,
+                        MTHCA_CQ_CONTEXT_SIZE, PCI_DMA_TODEVICE);
+       return err;
+}
+
+int mthca_HW2SW_CQ(struct mthca_dev *dev, void *cq_context,
+                  int cq_num, u8 *status)
+{
+       dma_addr_t outdma = 0;
+       int err;
+
+       outdma = pci_map_single(dev->pdev, cq_context,
+                               MTHCA_CQ_CONTEXT_SIZE,
+                               PCI_DMA_FROMDEVICE);
+       if (pci_dma_mapping_error(outdma))
+               return -ENOMEM;
+
+       err = mthca_cmd_box(dev, 0, outdma, cq_num, 0,
+                           CMD_HW2SW_CQ,
+                           CMD_TIME_CLASS_A, status);
+
+       pci_unmap_single(dev->pdev, outdma,
+                        MTHCA_CQ_CONTEXT_SIZE,
+                        PCI_DMA_FROMDEVICE);
+       return err;
+}
+
+int mthca_MODIFY_QP(struct mthca_dev *dev, int trans, u32 num,
+                   int is_ee, void *qp_context, u32 optmask,
+                   u8 *status)
+{
+       static const u16 op[] = {
+               [MTHCA_TRANS_RST2INIT]  = CMD_RST2INIT_QPEE,
+               [MTHCA_TRANS_INIT2INIT] = CMD_INIT2INIT_QPEE,
+               [MTHCA_TRANS_INIT2RTR]  = CMD_INIT2RTR_QPEE,
+               [MTHCA_TRANS_RTR2RTS]   = CMD_RTR2RTS_QPEE,
+               [MTHCA_TRANS_RTS2RTS]   = CMD_RTS2RTS_QPEE,
+               [MTHCA_TRANS_SQERR2RTS] = CMD_SQERR2RTS_QPEE,
+               [MTHCA_TRANS_ANY2ERR]   = CMD_2ERR_QPEE,
+               [MTHCA_TRANS_RTS2SQD]   = CMD_RTS2SQD_QPEE,
+               [MTHCA_TRANS_SQD2SQD]   = CMD_SQD2SQD_QPEE,
+               [MTHCA_TRANS_SQD2RTS]   = CMD_SQD2RTS_QPEE,
+               [MTHCA_TRANS_ANY2RST]   = CMD_ERR2RST_QPEE
+       };
+       u8 op_mod = 0;
+
+       dma_addr_t indma;
+       int err;
+
+       if (trans < 0 || trans >= ARRAY_SIZE(op))
+               return -EINVAL;
+
+       if (trans == MTHCA_TRANS_ANY2RST) {
+               indma  = 0;
+               op_mod = 3;     /* don't write outbox, any->reset */
+
+               /* For debugging */
+               qp_context = pci_alloc_consistent(dev->pdev, MTHCA_QP_CONTEXT_SIZE,
+                                                 &indma);
+               op_mod = 2;     /* write outbox, any->reset */
+       } else {
+               indma = pci_map_single(dev->pdev, qp_context,
+                                      MTHCA_QP_CONTEXT_SIZE,
+                                      PCI_DMA_TODEVICE);
+               if (pci_dma_mapping_error(indma))
+                       return -ENOMEM;
+
+               if (0) {
+                       int i;
+                       mthca_dbg(dev, "Dumping QP context:\n");
+                       printk(" %08x\n", be32_to_cpup(qp_context));
+                       for (i = 0; i < 0x100 / 4; ++i) {
+                               if (i % 8 == 0)
+                                       printk("[%02x] ", i * 4);
+                               printk(" %08x", be32_to_cpu(((u32 *) qp_context)[i + 2]));
+                               if ((i + 1) % 8 == 0)
+                                       printk("\n");
+                       }
+               }
+       }
+
+       if (trans == MTHCA_TRANS_ANY2RST) {
+               err = mthca_cmd_box(dev, 0, indma, (!!is_ee << 24) | num,
+                                   op_mod, op[trans], CMD_TIME_CLASS_C, status);
+
+               if (0) {
+                       int i;
+                       mthca_dbg(dev, "Dumping QP context:\n");
+                       printk(" %08x\n", be32_to_cpup(qp_context));
+                       for (i = 0; i < 0x100 / 4; ++i) {
+                               if (i % 8 == 0)
+                                       printk("[%02x] ", i * 4);
+                               printk(" %08x", be32_to_cpu(((u32 *) qp_context)[i + 2]));
+                               if ((i + 1) % 8 == 0)
+                                       printk("\n");
+                       }
+               }
+
+       } else
+               err = mthca_cmd(dev, indma, (!!is_ee << 24) | num,
+                               op_mod, op[trans], CMD_TIME_CLASS_C, status);
+
+       if (trans != MTHCA_TRANS_ANY2RST)
+               pci_unmap_single(dev->pdev, indma,
+                                MTHCA_QP_CONTEXT_SIZE, PCI_DMA_TODEVICE);
+       else
+               pci_free_consistent(dev->pdev, MTHCA_QP_CONTEXT_SIZE,
+                                   qp_context, indma);
+       return err;
+}
+
+int mthca_QUERY_QP(struct mthca_dev *dev, u32 num, int is_ee,
+                  void *qp_context, u8 *status)
+{
+       dma_addr_t outdma = 0;
+       int err;
+
+       outdma = pci_map_single(dev->pdev, qp_context,
+                               MTHCA_QP_CONTEXT_SIZE,
+                               PCI_DMA_FROMDEVICE);
+       if (pci_dma_mapping_error(outdma))
+               return -ENOMEM;
+
+       err = mthca_cmd_box(dev, 0, outdma, (!!is_ee << 24) | num, 0,
+                           CMD_QUERY_QPEE,
+                           CMD_TIME_CLASS_A, status);
+
+       pci_unmap_single(dev->pdev, outdma,
+                        MTHCA_QP_CONTEXT_SIZE,
+                        PCI_DMA_FROMDEVICE);
+       return err;
+}
+
+int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn,
+                         u8 *status)
+{
+       u8 op_mod;
+
+       switch (type) {
+       case IB_QPT_SMI:
+               op_mod = 0;
+               break;
+       case IB_QPT_GSI:
+               op_mod = 1;
+               break;
+       case IB_QPT_RAW_IPV6:
+               op_mod = 2;
+               break;
+       case IB_QPT_RAW_ETY:
+               op_mod = 3;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return mthca_cmd(dev, 0, qpn, op_mod, CMD_CONF_SPECIAL_QP,
+                        CMD_TIME_CLASS_B, status);
+}
+
+int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey,
+                 int port, struct ib_wc* in_wc, struct ib_grh* in_grh,
+                 void *in_mad, void *response_mad, u8 *status)
+{
+       void *box;
+       dma_addr_t dma;
+       int err;
+       u32 in_modifier = port;
+       u8 op_modifier = 0;
+
+#define MAD_IFC_BOX_SIZE      0x400
+#define MAD_IFC_MY_QPN_OFFSET 0x100
+#define MAD_IFC_RQPN_OFFSET   0x104
+#define MAD_IFC_SL_OFFSET     0x108
+#define MAD_IFC_G_PATH_OFFSET 0x109
+#define MAD_IFC_RLID_OFFSET   0x10a
+#define MAD_IFC_PKEY_OFFSET   0x10e
+#define MAD_IFC_GRH_OFFSET    0x140
+
+       box = pci_alloc_consistent(dev->pdev, MAD_IFC_BOX_SIZE, &dma);
+       if (!box)
+               return -ENOMEM;
+
+       memcpy(box, in_mad, 256);
+
+       /*
+        * Key check traps can't be generated unless we have in_wc to
+        * tell us where to send the trap.
+        */
+       if (ignore_mkey || !in_wc)
+               op_modifier |= 0x1;
+       if (ignore_bkey || !in_wc)
+               op_modifier |= 0x2;
+
+       if (in_wc) {
+               u8 val;
+
+               memset(box + 256, 0, 256);
+
+               MTHCA_PUT(box, in_wc->qp_num,     MAD_IFC_MY_QPN_OFFSET);
+               MTHCA_PUT(box, in_wc->src_qp,     MAD_IFC_RQPN_OFFSET);
+
+               val = in_wc->sl << 4;
+               MTHCA_PUT(box, val,               MAD_IFC_SL_OFFSET);
+
+               val = in_wc->dlid_path_bits |
+                       (in_wc->wc_flags & IB_WC_GRH ? 0x80 : 0);
+               MTHCA_PUT(box, val,               MAD_IFC_GRH_OFFSET);
+
+               MTHCA_PUT(box, in_wc->slid,       MAD_IFC_RLID_OFFSET);
+               MTHCA_PUT(box, in_wc->pkey_index, MAD_IFC_PKEY_OFFSET);
+
+               if (in_grh)
+                       memcpy((u8 *) box + MAD_IFC_GRH_OFFSET, in_grh, 40);
+
+               op_modifier |= 0x10;
+
+               in_modifier |= in_wc->slid << 16;
+       }
+
+       err = mthca_cmd_box(dev, dma, dma + 512, in_modifier, op_modifier,
+                           CMD_MAD_IFC, CMD_TIME_CLASS_C, status);
+
+       if (!err && !*status)
+               memcpy(response_mad, box + 512, 256);
+
+       pci_free_consistent(dev->pdev, MAD_IFC_BOX_SIZE, box, dma);
+       return err;
+}
+
+int mthca_READ_MGM(struct mthca_dev *dev, int index, void *mgm,
+                  u8 *status)
+{
+       dma_addr_t outdma = 0;
+       int err;
+
+       outdma = pci_map_single(dev->pdev, mgm,
+                               MTHCA_MGM_ENTRY_SIZE,
+                               PCI_DMA_FROMDEVICE);
+       if (pci_dma_mapping_error(outdma))
+               return -ENOMEM;
+
+       err = mthca_cmd_box(dev, 0, outdma, index, 0,
+                           CMD_READ_MGM,
+                           CMD_TIME_CLASS_A, status);
+
+       pci_unmap_single(dev->pdev, outdma,
+                        MTHCA_MGM_ENTRY_SIZE,
+                        PCI_DMA_FROMDEVICE);
+       return err;
+}
+
+int mthca_WRITE_MGM(struct mthca_dev *dev, int index, void *mgm,
+                   u8 *status)
+{
+       dma_addr_t indma;
+       int err;
+
+       indma = pci_map_single(dev->pdev, mgm,
+                              MTHCA_MGM_ENTRY_SIZE,
+                              PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(indma))
+               return -ENOMEM;
+
+       err = mthca_cmd(dev, indma, index, 0, CMD_WRITE_MGM,
+                       CMD_TIME_CLASS_A, status);
+
+       pci_unmap_single(dev->pdev, indma,
+                        MTHCA_MGM_ENTRY_SIZE, PCI_DMA_TODEVICE);
+       return err;
+}
+
+int mthca_MGID_HASH(struct mthca_dev *dev, void *gid, u16 *hash,
+                   u8 *status)
+{
+       dma_addr_t indma;
+       u64 imm;
+       int err;
+
+       indma = pci_map_single(dev->pdev, gid, 16, PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(indma))
+               return -ENOMEM;
+
+       err = mthca_cmd_imm(dev, indma, &imm, 0, 0, CMD_MGID_HASH,
+                           CMD_TIME_CLASS_A, status);
+       *hash = imm;
+
+       pci_unmap_single(dev->pdev, indma, 16, PCI_DMA_TODEVICE);
+       return err;
+}
+
+int mthca_NOP(struct mthca_dev *dev, u8 *status)
+{
+       return mthca_cmd(dev, 0, 0x1f, 0, CMD_NOP, msecs_to_jiffies(100), status);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.h b/drivers/infiniband/hw/mthca/mthca_cmd.h
new file mode 100644 (file)
index 0000000..3b2f969
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_cmd.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#ifndef MTHCA_CMD_H
+#define MTHCA_CMD_H
+
+#include <ib_verbs.h>
+
+#define MTHCA_CMD_MAILBOX_ALIGN 16UL
+#define MTHCA_CMD_MAILBOX_EXTRA (MTHCA_CMD_MAILBOX_ALIGN - 1)
+
+enum {
+       /* command completed successfully: */
+       MTHCA_CMD_STAT_OK             = 0x00,
+       /* Internal error (such as a bus error) occurred while processing command: */
+       MTHCA_CMD_STAT_INTERNAL_ERR   = 0x01,
+       /* Operation/command not supported or opcode modifier not supported: */
+       MTHCA_CMD_STAT_BAD_OP         = 0x02,
+       /* Parameter not supported or parameter out of range: */
+       MTHCA_CMD_STAT_BAD_PARAM      = 0x03,
+       /* System not enabled or bad system state: */
+       MTHCA_CMD_STAT_BAD_SYS_STATE  = 0x04,
+       /* Attempt to access reserved or unallocaterd resource: */
+       MTHCA_CMD_STAT_BAD_RESOURCE   = 0x05,
+       /* Requested resource is currently executing a command, or is otherwise busy: */
+       MTHCA_CMD_STAT_RESOURCE_BUSY  = 0x06,
+       /* memory error: */
+       MTHCA_CMD_STAT_DDR_MEM_ERR    = 0x07,
+       /* Required capability exceeds device limits: */
+       MTHCA_CMD_STAT_EXCEED_LIM     = 0x08,
+       /* Resource is not in the appropriate state or ownership: */
+       MTHCA_CMD_STAT_BAD_RES_STATE  = 0x09,
+       /* Index out of range: */
+       MTHCA_CMD_STAT_BAD_INDEX      = 0x0a,
+       /* FW image corrupted: */
+       MTHCA_CMD_STAT_BAD_NVMEM      = 0x0b,
+       /* Attempt to modify a QP/EE which is not in the presumed state: */
+       MTHCA_CMD_STAT_BAD_QPEE_STATE = 0x10,
+       /* Bad segment parameters (Address/Size): */
+       MTHCA_CMD_STAT_BAD_SEG_PARAM  = 0x20,
+       /* Memory Region has Memory Windows bound to: */
+       MTHCA_CMD_STAT_REG_BOUND      = 0x21,
+       /* HCA local attached memory not present: */
+       MTHCA_CMD_STAT_LAM_NOT_PRE    = 0x22,
+        /* Bad management packet (silently discarded): */
+       MTHCA_CMD_STAT_BAD_PKT        = 0x30,
+        /* More outstanding CQEs in CQ than new CQ size: */
+       MTHCA_CMD_STAT_BAD_SIZE       = 0x40
+};
+
+enum {
+       MTHCA_TRANS_INVALID = 0,
+       MTHCA_TRANS_RST2INIT,
+       MTHCA_TRANS_INIT2INIT,
+       MTHCA_TRANS_INIT2RTR,
+       MTHCA_TRANS_RTR2RTS,
+       MTHCA_TRANS_RTS2RTS,
+       MTHCA_TRANS_SQERR2RTS,
+       MTHCA_TRANS_ANY2ERR,
+       MTHCA_TRANS_RTS2SQD,
+       MTHCA_TRANS_SQD2SQD,
+       MTHCA_TRANS_SQD2RTS,
+       MTHCA_TRANS_ANY2RST,
+};
+
+enum {
+       DEV_LIM_FLAG_SRQ = 1 << 6
+};
+
+struct mthca_dev_lim {
+       int max_srq_sz;
+       int max_qp_sz;
+       int reserved_qps;
+       int max_qps;
+       int reserved_srqs;
+       int max_srqs;
+       int reserved_eecs;
+       int max_eecs;
+       int max_cq_sz;
+       int reserved_cqs;
+       int max_cqs;
+       int max_mpts;
+       int reserved_eqs;
+       int max_eqs;
+       int reserved_mtts;
+       int max_mrw_sz;
+       int reserved_mrws;
+       int max_mtt_seg;
+       int max_requester_per_qp;
+       int max_responder_per_qp;
+       int max_rdma_global;
+       int local_ca_ack_delay;
+       int max_mtu;
+       int max_port_width;
+       int max_vl;
+       int num_ports;
+       int max_gids;
+       int max_pkeys;
+       u32 flags;
+       int reserved_uars;
+       int uar_size;
+       int min_page_sz;
+       int max_sg;
+       int max_desc_sz;
+       int max_qp_per_mcg;
+       int reserved_mgms;
+       int max_mcgs;
+       int reserved_pds;
+       int max_pds;
+       int reserved_rdds;
+       int max_rdds;
+       int eec_entry_sz;
+       int qpc_entry_sz;
+       int eeec_entry_sz;
+       int eqpc_entry_sz;
+       int eqc_entry_sz;
+       int cqc_entry_sz;
+       int srq_entry_sz;
+       int uar_scratch_entry_sz;
+       int mtt_seg_sz;
+       int mpt_entry_sz;
+       union {
+               struct {
+                       int max_avs;
+               } tavor;
+               struct {
+                       int resize_srq;
+                       int max_pbl_sz;
+                       u8  bmme_flags;
+                       u32 reserved_lkey;
+                       int lam_required;
+                       u64 max_icm_sz;
+               } arbel;
+       } hca;
+};
+
+struct mthca_adapter {
+       u32 vendor_id;
+       u32 device_id;
+       u32 revision_id;
+       u8  inta_pin;
+};
+
+struct mthca_init_hca_param {
+       u64 qpc_base;
+       u64 eec_base;
+       u64 srqc_base;
+       u64 cqc_base;
+       u64 eqpc_base;
+       u64 eeec_base;
+       u64 eqc_base;
+       u64 rdb_base;
+       u64 mc_base;
+       u64 mpt_base;
+       u64 mtt_base;
+       u64 uar_scratch_base;
+       u64 uarc_base;
+       u16 log_mc_entry_sz;
+       u16 mc_hash_sz;
+       u8  log_num_qps;
+       u8  log_num_eecs;
+       u8  log_num_srqs;
+       u8  log_num_cqs;
+       u8  log_num_eqs;
+       u8  log_mc_table_sz;
+       u8  mtt_seg_sz;
+       u8  log_mpt_sz;
+       u8  log_uar_sz;
+       u8  log_uarc_sz;
+};
+
+struct mthca_init_ib_param {
+       int enable_1x;
+       int enable_4x;
+       int vl_cap;
+       int mtu_cap;
+       u16 gid_cap;
+       u16 pkey_cap;
+       int set_guid0;
+       u64 guid0;
+       int set_node_guid;
+       u64 node_guid;
+       int set_si_guid;
+       u64 si_guid;
+};
+
+struct mthca_set_ib_param {
+       int set_si_guid;
+       int reset_qkey_viol;
+       u64 si_guid;
+       u32 cap_mask;
+};
+
+int mthca_cmd_use_events(struct mthca_dev *dev);
+void mthca_cmd_use_polling(struct mthca_dev *dev);
+void mthca_cmd_event(struct mthca_dev *dev, u16 token,
+                    u8  status, u64 out_param);
+
+int mthca_SYS_EN(struct mthca_dev *dev, u8 *status);
+int mthca_SYS_DIS(struct mthca_dev *dev, u8 *status);
+int mthca_MAP_FA(struct mthca_dev *dev, struct mthca_icm *icm, u8 *status);
+int mthca_UNMAP_FA(struct mthca_dev *dev, u8 *status);
+int mthca_RUN_FW(struct mthca_dev *dev, u8 *status);
+int mthca_QUERY_FW(struct mthca_dev *dev, u8 *status);
+int mthca_ENABLE_LAM(struct mthca_dev *dev, u8 *status);
+int mthca_DISABLE_LAM(struct mthca_dev *dev, u8 *status);
+int mthca_QUERY_DDR(struct mthca_dev *dev, u8 *status);
+int mthca_QUERY_DEV_LIM(struct mthca_dev *dev,
+                       struct mthca_dev_lim *dev_lim, u8 *status);
+int mthca_QUERY_ADAPTER(struct mthca_dev *dev,
+                       struct mthca_adapter *adapter, u8 *status);
+int mthca_INIT_HCA(struct mthca_dev *dev,
+                  struct mthca_init_hca_param *param,
+                  u8 *status);
+int mthca_INIT_IB(struct mthca_dev *dev,
+                 struct mthca_init_ib_param *param,
+                 int port, u8 *status);
+int mthca_CLOSE_IB(struct mthca_dev *dev, int port, u8 *status);
+int mthca_CLOSE_HCA(struct mthca_dev *dev, int panic, u8 *status);
+int mthca_SET_IB(struct mthca_dev *dev, struct mthca_set_ib_param *param,
+                int port, u8 *status);
+int mthca_MAP_ICM(struct mthca_dev *dev, struct mthca_icm *icm, u64 virt, u8 *status);
+int mthca_MAP_ICM_page(struct mthca_dev *dev, u64 dma_addr, u64 virt, u8 *status);
+int mthca_UNMAP_ICM(struct mthca_dev *dev, u64 virt, u32 page_count, u8 *status);
+int mthca_MAP_ICM_AUX(struct mthca_dev *dev, struct mthca_icm *icm, u8 *status);
+int mthca_UNMAP_ICM_AUX(struct mthca_dev *dev, u8 *status);
+int mthca_SET_ICM_SIZE(struct mthca_dev *dev, u64 icm_size, u64 *aux_pages,
+                      u8 *status);
+int mthca_SW2HW_MPT(struct mthca_dev *dev, void *mpt_entry,
+                   int mpt_index, u8 *status);
+int mthca_HW2SW_MPT(struct mthca_dev *dev, void *mpt_entry,
+                   int mpt_index, u8 *status);
+int mthca_WRITE_MTT(struct mthca_dev *dev, u64 *mtt_entry,
+                   int num_mtt, u8 *status);
+int mthca_MAP_EQ(struct mthca_dev *dev, u64 event_mask, int unmap,
+                int eq_num, u8 *status);
+int mthca_SW2HW_EQ(struct mthca_dev *dev, void *eq_context,
+                  int eq_num, u8 *status);
+int mthca_HW2SW_EQ(struct mthca_dev *dev, void *eq_context,
+                  int eq_num, u8 *status);
+int mthca_SW2HW_CQ(struct mthca_dev *dev, void *cq_context,
+                  int cq_num, u8 *status);
+int mthca_HW2SW_CQ(struct mthca_dev *dev, void *cq_context,
+                  int cq_num, u8 *status);
+int mthca_MODIFY_QP(struct mthca_dev *dev, int trans, u32 num,
+                   int is_ee, void *qp_context, u32 optmask,
+                   u8 *status);
+int mthca_QUERY_QP(struct mthca_dev *dev, u32 num, int is_ee,
+                  void *qp_context, u8 *status);
+int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn,
+                         u8 *status);
+int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey,
+                 int port, struct ib_wc* in_wc, struct ib_grh* in_grh,
+                 void *in_mad, void *response_mad, u8 *status);
+int mthca_READ_MGM(struct mthca_dev *dev, int index, void *mgm,
+                  u8 *status);
+int mthca_WRITE_MGM(struct mthca_dev *dev, int index, void *mgm,
+                   u8 *status);
+int mthca_MGID_HASH(struct mthca_dev *dev, void *gid, u16 *hash,
+                   u8 *status);
+int mthca_NOP(struct mthca_dev *dev, u8 *status);
+
+#define MAILBOX_ALIGN(x) ((void *) ALIGN((unsigned long) (x), MTHCA_CMD_MAILBOX_ALIGN))
+
+#endif /* MTHCA_CMD_H */
diff --git a/drivers/infiniband/hw/mthca/mthca_config_reg.h b/drivers/infiniband/hw/mthca/mthca_config_reg.h
new file mode 100644 (file)
index 0000000..acb94ae
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_config_reg.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#ifndef MTHCA_CONFIG_REG_H
+#define MTHCA_CONFIG_REG_H
+
+#include <asm/page.h>
+
+#define MTHCA_HCR_BASE         0x80680
+#define MTHCA_HCR_SIZE         0x0001c
+#define MTHCA_ECR_BASE         0x80700
+#define MTHCA_ECR_SIZE         0x00008
+#define MTHCA_ECR_CLR_BASE     0x80708
+#define MTHCA_ECR_CLR_SIZE     0x00008
+#define MTHCA_MAP_ECR_SIZE     (MTHCA_ECR_SIZE + MTHCA_ECR_CLR_SIZE)
+#define MTHCA_CLR_INT_BASE     0xf00d8
+#define MTHCA_CLR_INT_SIZE     0x00008
+
+#endif /* MTHCA_CONFIG_REG_H */
diff --git a/drivers/infiniband/hw/mthca/mthca_cq.c b/drivers/infiniband/hw/mthca/mthca_cq.c
new file mode 100644 (file)
index 0000000..38f6d14
--- /dev/null
@@ -0,0 +1,841 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_cq.c 1369 2004-12-20 16:17:07Z roland $
+ */
+
+#include <linux/init.h>
+
+#include <ib_pack.h>
+
+#include "mthca_dev.h"
+#include "mthca_cmd.h"
+
+enum {
+       MTHCA_MAX_DIRECT_CQ_SIZE = 4 * PAGE_SIZE
+};
+
+enum {
+       MTHCA_CQ_ENTRY_SIZE = 0x20
+};
+
+/*
+ * Must be packed because start is 64 bits but only aligned to 32 bits.
+ */
+struct mthca_cq_context {
+       u32 flags;
+       u64 start;
+       u32 logsize_usrpage;
+       u32 error_eqn;
+       u32 comp_eqn;
+       u32 pd;
+       u32 lkey;
+       u32 last_notified_index;
+       u32 solicit_producer_index;
+       u32 consumer_index;
+       u32 producer_index;
+       u32 cqn;
+       u32 reserved[3];
+} __attribute__((packed));
+
+#define MTHCA_CQ_STATUS_OK          ( 0 << 28)
+#define MTHCA_CQ_STATUS_OVERFLOW    ( 9 << 28)
+#define MTHCA_CQ_STATUS_WRITE_FAIL  (10 << 28)
+#define MTHCA_CQ_FLAG_TR            ( 1 << 18)
+#define MTHCA_CQ_FLAG_OI            ( 1 << 17)
+#define MTHCA_CQ_STATE_DISARMED     ( 0 <<  8)
+#define MTHCA_CQ_STATE_ARMED        ( 1 <<  8)
+#define MTHCA_CQ_STATE_ARMED_SOL    ( 4 <<  8)
+#define MTHCA_EQ_STATE_FIRED        (10 <<  8)
+
+enum {
+       MTHCA_ERROR_CQE_OPCODE_MASK = 0xfe
+};
+
+enum {
+       SYNDROME_LOCAL_LENGTH_ERR        = 0x01,
+       SYNDROME_LOCAL_QP_OP_ERR         = 0x02,
+       SYNDROME_LOCAL_EEC_OP_ERR        = 0x03,
+       SYNDROME_LOCAL_PROT_ERR          = 0x04,
+       SYNDROME_WR_FLUSH_ERR            = 0x05,
+       SYNDROME_MW_BIND_ERR             = 0x06,
+       SYNDROME_BAD_RESP_ERR            = 0x10,
+       SYNDROME_LOCAL_ACCESS_ERR        = 0x11,
+       SYNDROME_REMOTE_INVAL_REQ_ERR    = 0x12,
+       SYNDROME_REMOTE_ACCESS_ERR       = 0x13,
+       SYNDROME_REMOTE_OP_ERR           = 0x14,
+       SYNDROME_RETRY_EXC_ERR           = 0x15,
+       SYNDROME_RNR_RETRY_EXC_ERR       = 0x16,
+       SYNDROME_LOCAL_RDD_VIOL_ERR      = 0x20,
+       SYNDROME_REMOTE_INVAL_RD_REQ_ERR = 0x21,
+       SYNDROME_REMOTE_ABORTED_ERR      = 0x22,
+       SYNDROME_INVAL_EECN_ERR          = 0x23,
+       SYNDROME_INVAL_EEC_STATE_ERR     = 0x24
+};
+
+struct mthca_cqe {
+       u32 my_qpn;
+       u32 my_ee;
+       u32 rqpn;
+       u16 sl_g_mlpath;
+       u16 rlid;
+       u32 imm_etype_pkey_eec;
+       u32 byte_cnt;
+       u32 wqe;
+       u8  opcode;
+       u8  is_send;
+       u8  reserved;
+       u8  owner;
+};
+
+struct mthca_err_cqe {
+       u32 my_qpn;
+       u32 reserved1[3];
+       u8  syndrome;
+       u8  reserved2;
+       u16 db_cnt;
+       u32 reserved3;
+       u32 wqe;
+       u8  opcode;
+       u8  reserved4[2];
+       u8  owner;
+};
+
+#define MTHCA_CQ_ENTRY_OWNER_SW      (0 << 7)
+#define MTHCA_CQ_ENTRY_OWNER_HW      (1 << 7)
+
+#define MTHCA_CQ_DB_INC_CI       (1 << 24)
+#define MTHCA_CQ_DB_REQ_NOT      (2 << 24)
+#define MTHCA_CQ_DB_REQ_NOT_SOL  (3 << 24)
+#define MTHCA_CQ_DB_SET_CI       (4 << 24)
+#define MTHCA_CQ_DB_REQ_NOT_MULT (5 << 24)
+
+static inline struct mthca_cqe *get_cqe(struct mthca_cq *cq, int entry)
+{
+       if (cq->is_direct)
+               return cq->queue.direct.buf + (entry * MTHCA_CQ_ENTRY_SIZE);
+       else
+               return cq->queue.page_list[entry * MTHCA_CQ_ENTRY_SIZE / PAGE_SIZE].buf
+                       + (entry * MTHCA_CQ_ENTRY_SIZE) % PAGE_SIZE;
+}
+
+static inline int cqe_sw(struct mthca_cq *cq, int i)
+{
+       return !(MTHCA_CQ_ENTRY_OWNER_HW &
+                get_cqe(cq, i)->owner);
+}
+
+static inline int next_cqe_sw(struct mthca_cq *cq)
+{
+       return cqe_sw(cq, cq->cons_index);
+}
+
+static inline void set_cqe_hw(struct mthca_cq *cq, int entry)
+{
+       get_cqe(cq, entry)->owner = MTHCA_CQ_ENTRY_OWNER_HW;
+}
+
+static inline void inc_cons_index(struct mthca_dev *dev, struct mthca_cq *cq,
+                                 int nent)
+{
+       u32 doorbell[2];
+
+       doorbell[0] = cpu_to_be32(MTHCA_CQ_DB_INC_CI | cq->cqn);
+       doorbell[1] = cpu_to_be32(nent - 1);
+
+       mthca_write64(doorbell,
+                     dev->kar + MTHCA_CQ_DOORBELL,
+                     MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock));
+}
+
+void mthca_cq_event(struct mthca_dev *dev, u32 cqn)
+{
+       struct mthca_cq *cq;
+
+       spin_lock(&dev->cq_table.lock);
+       cq = mthca_array_get(&dev->cq_table.cq, cqn & (dev->limits.num_cqs - 1));
+       if (cq)
+               atomic_inc(&cq->refcount);
+       spin_unlock(&dev->cq_table.lock);
+
+       if (!cq) {
+               mthca_warn(dev, "Completion event for bogus CQ %08x\n", cqn);
+               return;
+       }
+
+       cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);
+
+       if (atomic_dec_and_test(&cq->refcount))
+               wake_up(&cq->wait);
+}
+
+void mthca_cq_clean(struct mthca_dev *dev, u32 cqn, u32 qpn)
+{
+       struct mthca_cq *cq;
+       struct mthca_cqe *cqe;
+       int prod_index;
+       int nfreed = 0;
+
+       spin_lock_irq(&dev->cq_table.lock);
+       cq = mthca_array_get(&dev->cq_table.cq, cqn & (dev->limits.num_cqs - 1));
+       if (cq)
+               atomic_inc(&cq->refcount);
+       spin_unlock_irq(&dev->cq_table.lock);
+
+       if (!cq)
+               return;
+
+       spin_lock_irq(&cq->lock);
+
+       /*
+        * First we need to find the current producer index, so we
+        * know where to start cleaning from.  It doesn't matter if HW
+        * adds new entries after this loop -- the QP we're worried
+        * about is already in RESET, so the new entries won't come
+        * from our QP and therefore don't need to be checked.
+        */
+       for (prod_index = cq->cons_index;
+            cqe_sw(cq, prod_index & cq->ibcq.cqe);
+            ++prod_index)
+               if (prod_index == cq->cons_index + cq->ibcq.cqe)
+                       break;
+
+       if (0)
+               mthca_dbg(dev, "Cleaning QPN %06x from CQN %06x; ci %d, pi %d\n",
+                         qpn, cqn, cq->cons_index, prod_index);
+
+       /*
+        * Now sweep backwards through the CQ, removing CQ entries
+        * that match our QP by copying older entries on top of them.
+        */
+       while (prod_index > cq->cons_index) {
+               cqe = get_cqe(cq, (prod_index - 1) & cq->ibcq.cqe);
+               if (cqe->my_qpn == cpu_to_be32(qpn))
+                       ++nfreed;
+               else if (nfreed)
+                       memcpy(get_cqe(cq, (prod_index - 1 + nfreed) &
+                                      cq->ibcq.cqe),
+                              cqe,
+                              MTHCA_CQ_ENTRY_SIZE);
+               --prod_index;
+       }
+
+       if (nfreed) {
+               wmb();
+               inc_cons_index(dev, cq, nfreed);
+               cq->cons_index = (cq->cons_index + nfreed) & cq->ibcq.cqe;
+       }
+
+       spin_unlock_irq(&cq->lock);
+       if (atomic_dec_and_test(&cq->refcount))
+               wake_up(&cq->wait);
+}
+
+static int handle_error_cqe(struct mthca_dev *dev, struct mthca_cq *cq,
+                           struct mthca_qp *qp, int wqe_index, int is_send,
+                           struct mthca_err_cqe *cqe,
+                           struct ib_wc *entry, int *free_cqe)
+{
+       int err;
+       int dbd;
+       u32 new_wqe;
+
+       if (1 && cqe->syndrome != SYNDROME_WR_FLUSH_ERR) {
+               int j;
+
+               mthca_dbg(dev, "%x/%d: error CQE -> QPN %06x, WQE @ %08x\n",
+                         cq->cqn, cq->cons_index, be32_to_cpu(cqe->my_qpn),
+                         be32_to_cpu(cqe->wqe));
+
+               for (j = 0; j < 8; ++j)
+                       printk(KERN_DEBUG "  [%2x] %08x\n",
+                              j * 4, be32_to_cpu(((u32 *) cqe)[j]));
+       }
+
+       /*
+        * For completions in error, only work request ID, status (and
+        * freed resource count for RD) have to be set.
+        */
+       switch (cqe->syndrome) {
+       case SYNDROME_LOCAL_LENGTH_ERR:
+               entry->status = IB_WC_LOC_LEN_ERR;
+               break;
+       case SYNDROME_LOCAL_QP_OP_ERR:
+               entry->status = IB_WC_LOC_QP_OP_ERR;
+               break;
+       case SYNDROME_LOCAL_EEC_OP_ERR:
+               entry->status = IB_WC_LOC_EEC_OP_ERR;
+               break;
+       case SYNDROME_LOCAL_PROT_ERR:
+               entry->status = IB_WC_LOC_PROT_ERR;
+               break;
+       case SYNDROME_WR_FLUSH_ERR:
+               entry->status = IB_WC_WR_FLUSH_ERR;
+               break;
+       case SYNDROME_MW_BIND_ERR:
+               entry->status = IB_WC_MW_BIND_ERR;
+               break;
+       case SYNDROME_BAD_RESP_ERR:
+               entry->status = IB_WC_BAD_RESP_ERR;
+               break;
+       case SYNDROME_LOCAL_ACCESS_ERR:
+               entry->status = IB_WC_LOC_ACCESS_ERR;
+               break;
+       case SYNDROME_REMOTE_INVAL_REQ_ERR:
+               entry->status = IB_WC_REM_INV_REQ_ERR;
+               break;
+       case SYNDROME_REMOTE_ACCESS_ERR:
+               entry->status = IB_WC_REM_ACCESS_ERR;
+               break;
+       case SYNDROME_REMOTE_OP_ERR:
+               entry->status = IB_WC_REM_OP_ERR;
+               break;
+       case SYNDROME_RETRY_EXC_ERR:
+               entry->status = IB_WC_RETRY_EXC_ERR;
+               break;
+       case SYNDROME_RNR_RETRY_EXC_ERR:
+               entry->status = IB_WC_RNR_RETRY_EXC_ERR;
+               break;
+       case SYNDROME_LOCAL_RDD_VIOL_ERR:
+               entry->status = IB_WC_LOC_RDD_VIOL_ERR;
+               break;
+       case SYNDROME_REMOTE_INVAL_RD_REQ_ERR:
+               entry->status = IB_WC_REM_INV_RD_REQ_ERR;
+               break;
+       case SYNDROME_REMOTE_ABORTED_ERR:
+               entry->status = IB_WC_REM_ABORT_ERR;
+               break;
+       case SYNDROME_INVAL_EECN_ERR:
+               entry->status = IB_WC_INV_EECN_ERR;
+               break;
+       case SYNDROME_INVAL_EEC_STATE_ERR:
+               entry->status = IB_WC_INV_EEC_STATE_ERR;
+               break;
+       default:
+               entry->status = IB_WC_GENERAL_ERR;
+               break;
+       }
+
+       err = mthca_free_err_wqe(qp, is_send, wqe_index, &dbd, &new_wqe);
+       if (err)
+               return err;
+
+       /*
+        * If we're at the end of the WQE chain, or we've used up our
+        * doorbell count, free the CQE.  Otherwise just update it for
+        * the next poll operation.
+        */
+       if (!(new_wqe & cpu_to_be32(0x3f)) || (!cqe->db_cnt && dbd))
+               return 0;
+
+       cqe->db_cnt   = cpu_to_be16(be16_to_cpu(cqe->db_cnt) - dbd);
+       cqe->wqe      = new_wqe;
+       cqe->syndrome = SYNDROME_WR_FLUSH_ERR;
+
+       *free_cqe = 0;
+
+       return 0;
+}
+
+static void dump_cqe(struct mthca_cqe *cqe)
+{
+       int j;
+
+       for (j = 0; j < 8; ++j)
+               printk(KERN_DEBUG "  [%2x] %08x\n",
+                      j * 4, be32_to_cpu(((u32 *) cqe)[j]));
+}
+
+static inline int mthca_poll_one(struct mthca_dev *dev,
+                                struct mthca_cq *cq,
+                                struct mthca_qp **cur_qp,
+                                int *freed,
+                                struct ib_wc *entry)
+{
+       struct mthca_wq *wq;
+       struct mthca_cqe *cqe;
+       int wqe_index;
+       int is_error = 0;
+       int is_send;
+       int free_cqe = 1;
+       int err = 0;
+
+       if (!next_cqe_sw(cq))
+               return -EAGAIN;
+
+       /*
+        * Make sure we read CQ entry contents after we've checked the
+        * ownership bit.
+        */
+       rmb();
+
+       cqe = get_cqe(cq, cq->cons_index);
+
+       if (0) {
+               mthca_dbg(dev, "%x/%d: CQE -> QPN %06x, WQE @ %08x\n",
+                         cq->cqn, cq->cons_index, be32_to_cpu(cqe->my_qpn),
+                         be32_to_cpu(cqe->wqe));
+
+               dump_cqe(cqe);
+       }
+
+       if ((cqe->opcode & MTHCA_ERROR_CQE_OPCODE_MASK) ==
+           MTHCA_ERROR_CQE_OPCODE_MASK) {
+               is_error = 1;
+               is_send = cqe->opcode & 1;
+       } else
+               is_send = cqe->is_send & 0x80;
+
+       if (!*cur_qp || be32_to_cpu(cqe->my_qpn) != (*cur_qp)->qpn) {
+               if (*cur_qp) {
+                       if (*freed) {
+                               wmb();
+                               inc_cons_index(dev, cq, *freed);
+                               *freed = 0;
+                       }
+                       spin_unlock(&(*cur_qp)->lock);
+               }
+
+               spin_lock(&dev->qp_table.lock);
+               *cur_qp = mthca_array_get(&dev->qp_table.qp,
+                                         be32_to_cpu(cqe->my_qpn) &
+                                         (dev->limits.num_qps - 1));
+               if (*cur_qp)
+                       atomic_inc(&(*cur_qp)->refcount);
+               spin_unlock(&dev->qp_table.lock);
+
+               if (!*cur_qp) {
+                       mthca_warn(dev, "CQ entry for unknown QP %06x\n",
+                                  be32_to_cpu(cqe->my_qpn) & 0xffffff);
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               spin_lock(&(*cur_qp)->lock);
+       }
+
+       entry->qp_num = (*cur_qp)->qpn;
+
+       if (is_send) {
+               wq = &(*cur_qp)->sq;
+               wqe_index = ((be32_to_cpu(cqe->wqe) - (*cur_qp)->send_wqe_offset)
+                            >> wq->wqe_shift);
+               entry->wr_id = (*cur_qp)->wrid[wqe_index +
+                                              (*cur_qp)->rq.max];
+       } else {
+               wq = &(*cur_qp)->rq;
+               wqe_index = be32_to_cpu(cqe->wqe) >> wq->wqe_shift;
+               entry->wr_id = (*cur_qp)->wrid[wqe_index];
+       }
+
+       if (wq->last_comp < wqe_index)
+               wq->cur -= wqe_index - wq->last_comp;
+       else
+               wq->cur -= wq->max - wq->last_comp + wqe_index;
+
+       wq->last_comp = wqe_index;
+
+       if (0)
+               mthca_dbg(dev, "%s completion for QP %06x, index %d (nr %d)\n",
+                         is_send ? "Send" : "Receive",
+                         (*cur_qp)->qpn, wqe_index, wq->max);
+
+       if (is_error) {
+               err = handle_error_cqe(dev, cq, *cur_qp, wqe_index, is_send,
+                                      (struct mthca_err_cqe *) cqe,
+                                      entry, &free_cqe);
+               goto out;
+       }
+
+       if (is_send) {
+               entry->opcode = IB_WC_SEND; /* XXX */
+       } else {
+               entry->byte_len = be32_to_cpu(cqe->byte_cnt);
+               switch (cqe->opcode & 0x1f) {
+               case IB_OPCODE_SEND_LAST_WITH_IMMEDIATE:
+               case IB_OPCODE_SEND_ONLY_WITH_IMMEDIATE:
+                       entry->wc_flags = IB_WC_WITH_IMM;
+                       entry->imm_data = cqe->imm_etype_pkey_eec;
+                       entry->opcode = IB_WC_RECV;
+                       break;
+               case IB_OPCODE_RDMA_WRITE_LAST_WITH_IMMEDIATE:
+               case IB_OPCODE_RDMA_WRITE_ONLY_WITH_IMMEDIATE:
+                       entry->wc_flags = IB_WC_WITH_IMM;
+                       entry->imm_data = cqe->imm_etype_pkey_eec;
+                       entry->opcode = IB_WC_RECV_RDMA_WITH_IMM;
+                       break;
+               default:
+                       entry->wc_flags = 0;
+                       entry->opcode = IB_WC_RECV;
+                       break;
+               }
+               entry->slid        = be16_to_cpu(cqe->rlid);
+               entry->sl          = be16_to_cpu(cqe->sl_g_mlpath) >> 12;
+               entry->src_qp      = be32_to_cpu(cqe->rqpn) & 0xffffff;
+               entry->dlid_path_bits = be16_to_cpu(cqe->sl_g_mlpath) & 0x7f;
+               entry->pkey_index  = be32_to_cpu(cqe->imm_etype_pkey_eec) >> 16;
+               entry->wc_flags   |= be16_to_cpu(cqe->sl_g_mlpath) & 0x80 ?
+                                       IB_WC_GRH : 0;
+       }
+
+       entry->status = IB_WC_SUCCESS;
+
+ out:
+       if (free_cqe) {
+               set_cqe_hw(cq, cq->cons_index);
+               ++(*freed);
+               cq->cons_index = (cq->cons_index + 1) & cq->ibcq.cqe;
+       }
+
+       return err;
+}
+
+int mthca_poll_cq(struct ib_cq *ibcq, int num_entries,
+                 struct ib_wc *entry)
+{
+       struct mthca_dev *dev = to_mdev(ibcq->device);
+       struct mthca_cq *cq = to_mcq(ibcq);
+       struct mthca_qp *qp = NULL;
+       unsigned long flags;
+       int err = 0;
+       int freed = 0;
+       int npolled;
+
+       spin_lock_irqsave(&cq->lock, flags);
+
+       for (npolled = 0; npolled < num_entries; ++npolled) {
+               err = mthca_poll_one(dev, cq, &qp,
+                                    &freed, entry + npolled);
+               if (err)
+                       break;
+       }
+
+       if (freed) {
+               wmb();
+               inc_cons_index(dev, cq, freed);
+       }
+
+       if (qp) {
+               spin_unlock(&qp->lock);
+               if (atomic_dec_and_test(&qp->refcount))
+                       wake_up(&qp->wait);
+       }
+
+
+       spin_unlock_irqrestore(&cq->lock, flags);
+
+       return err == 0 || err == -EAGAIN ? npolled : err;
+}
+
+void mthca_arm_cq(struct mthca_dev *dev, struct mthca_cq *cq,
+                 int solicited)
+{
+       u32 doorbell[2];
+
+       doorbell[0] =  cpu_to_be32((solicited ?
+                                   MTHCA_CQ_DB_REQ_NOT_SOL :
+                                   MTHCA_CQ_DB_REQ_NOT)      |
+                                  cq->cqn);
+       doorbell[1] = 0xffffffff;
+
+       mthca_write64(doorbell,
+                     dev->kar + MTHCA_CQ_DOORBELL,
+                     MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock));
+}
+
+int mthca_init_cq(struct mthca_dev *dev, int nent,
+                 struct mthca_cq *cq)
+{
+       int size = nent * MTHCA_CQ_ENTRY_SIZE;
+       dma_addr_t t;
+       void *mailbox = NULL;
+       int npages, shift;
+       u64 *dma_list = NULL;
+       struct mthca_cq_context *cq_context;
+       int err = -ENOMEM;
+       u8 status;
+       int i;
+
+       might_sleep();
+
+       mailbox = kmalloc(sizeof (struct mthca_cq_context) + MTHCA_CMD_MAILBOX_EXTRA,
+                         GFP_KERNEL);
+       if (!mailbox)
+               goto err_out;
+
+       cq_context = MAILBOX_ALIGN(mailbox);
+
+       if (size <= MTHCA_MAX_DIRECT_CQ_SIZE) {
+               if (0)
+                       mthca_dbg(dev, "Creating direct CQ of size %d\n", size);
+
+               cq->is_direct = 1;
+               npages        = 1;
+               shift         = get_order(size) + PAGE_SHIFT;
+
+               cq->queue.direct.buf = pci_alloc_consistent(dev->pdev,
+                                                           size, &t);
+               if (!cq->queue.direct.buf)
+                       goto err_out;
+
+               pci_unmap_addr_set(&cq->queue.direct, mapping, t);
+
+               memset(cq->queue.direct.buf, 0, size);
+
+               while (t & ((1 << shift) - 1)) {
+                       --shift;
+                       npages *= 2;
+               }
+
+               dma_list = kmalloc(npages * sizeof *dma_list, GFP_KERNEL);
+               if (!dma_list)
+                       goto err_out_free;
+
+               for (i = 0; i < npages; ++i)
+                       dma_list[i] = t + i * (1 << shift);
+       } else {
+               cq->is_direct = 0;
+               npages        = (size + PAGE_SIZE - 1) / PAGE_SIZE;
+               shift         = PAGE_SHIFT;
+
+               if (0)
+                       mthca_dbg(dev, "Creating indirect CQ with %d pages\n", npages);
+
+               dma_list = kmalloc(npages * sizeof *dma_list, GFP_KERNEL);
+               if (!dma_list)
+                       goto err_out;
+
+               cq->queue.page_list = kmalloc(npages * sizeof *cq->queue.page_list,
+                                             GFP_KERNEL);
+               if (!cq->queue.page_list)
+                       goto err_out;
+
+               for (i = 0; i < npages; ++i)
+                       cq->queue.page_list[i].buf = NULL;
+
+               for (i = 0; i < npages; ++i) {
+                       cq->queue.page_list[i].buf =
+                               pci_alloc_consistent(dev->pdev, PAGE_SIZE, &t);
+                       if (!cq->queue.page_list[i].buf)
+                               goto err_out_free;
+
+                       dma_list[i] = t;
+                       pci_unmap_addr_set(&cq->queue.page_list[i], mapping, t);
+
+                       memset(cq->queue.page_list[i].buf, 0, PAGE_SIZE);
+               }
+       }
+
+       for (i = 0; i < nent; ++i)
+               set_cqe_hw(cq, i);
+
+       cq->cqn = mthca_alloc(&dev->cq_table.alloc);
+       if (cq->cqn == -1)
+               goto err_out_free;
+
+       err = mthca_mr_alloc_phys(dev, dev->driver_pd.pd_num,
+                                 dma_list, shift, npages,
+                                 0, size,
+                                 MTHCA_MPT_FLAG_LOCAL_WRITE |
+                                 MTHCA_MPT_FLAG_LOCAL_READ,
+                                 &cq->mr);
+       if (err)
+               goto err_out_free_cq;
+
+       spin_lock_init(&cq->lock);
+       atomic_set(&cq->refcount, 1);
+       init_waitqueue_head(&cq->wait);
+
+       memset(cq_context, 0, sizeof *cq_context);
+       cq_context->flags           = cpu_to_be32(MTHCA_CQ_STATUS_OK      |
+                                                 MTHCA_CQ_STATE_DISARMED |
+                                                 MTHCA_CQ_FLAG_TR);
+       cq_context->start           = cpu_to_be64(0);
+       cq_context->logsize_usrpage = cpu_to_be32((ffs(nent) - 1) << 24 |
+                                                 MTHCA_KAR_PAGE);
+       cq_context->error_eqn       = cpu_to_be32(dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn);
+       cq_context->comp_eqn        = cpu_to_be32(dev->eq_table.eq[MTHCA_EQ_COMP].eqn);
+       cq_context->pd              = cpu_to_be32(dev->driver_pd.pd_num);
+       cq_context->lkey            = cpu_to_be32(cq->mr.ibmr.lkey);
+       cq_context->cqn             = cpu_to_be32(cq->cqn);
+
+       err = mthca_SW2HW_CQ(dev, cq_context, cq->cqn, &status);
+       if (err) {
+               mthca_warn(dev, "SW2HW_CQ failed (%d)\n", err);
+               goto err_out_free_mr;
+       }
+
+       if (status) {
+               mthca_warn(dev, "SW2HW_CQ returned status 0x%02x\n",
+                          status);
+               err = -EINVAL;
+               goto err_out_free_mr;
+       }
+
+       spin_lock_irq(&dev->cq_table.lock);
+       if (mthca_array_set(&dev->cq_table.cq,
+                           cq->cqn & (dev->limits.num_cqs - 1),
+                           cq)) {
+               spin_unlock_irq(&dev->cq_table.lock);
+               goto err_out_free_mr;
+       }
+       spin_unlock_irq(&dev->cq_table.lock);
+
+       cq->cons_index = 0;
+
+       kfree(dma_list);
+       kfree(mailbox);
+
+       return 0;
+
+ err_out_free_mr:
+       mthca_free_mr(dev, &cq->mr);
+
+ err_out_free_cq:
+       mthca_free(&dev->cq_table.alloc, cq->cqn);
+
+ err_out_free:
+       if (cq->is_direct)
+               pci_free_consistent(dev->pdev, size,
+                                   cq->queue.direct.buf,
+                                   pci_unmap_addr(&cq->queue.direct, mapping));
+       else {
+               for (i = 0; i < npages; ++i)
+                       if (cq->queue.page_list[i].buf)
+                               pci_free_consistent(dev->pdev, PAGE_SIZE,
+                                                   cq->queue.page_list[i].buf,
+                                                   pci_unmap_addr(&cq->queue.page_list[i],
+                                                                  mapping));
+
+               kfree(cq->queue.page_list);
+       }
+
+ err_out:
+       kfree(dma_list);
+       kfree(mailbox);
+
+       return err;
+}
+
+void mthca_free_cq(struct mthca_dev *dev,
+                  struct mthca_cq *cq)
+{
+       void *mailbox;
+       int err;
+       u8 status;
+
+       might_sleep();
+
+       mailbox = kmalloc(sizeof (struct mthca_cq_context) + MTHCA_CMD_MAILBOX_EXTRA,
+                         GFP_KERNEL);
+       if (!mailbox) {
+               mthca_warn(dev, "No memory for mailbox to free CQ.\n");
+               return;
+       }
+
+       err = mthca_HW2SW_CQ(dev, MAILBOX_ALIGN(mailbox), cq->cqn, &status);
+       if (err)
+               mthca_warn(dev, "HW2SW_CQ failed (%d)\n", err);
+       else if (status)
+               mthca_warn(dev, "HW2SW_CQ returned status 0x%02x\n",
+                          status);
+
+       if (0) {
+               u32 *ctx = MAILBOX_ALIGN(mailbox);
+               int j;
+
+               printk(KERN_ERR "context for CQN %x (cons index %x, next sw %d)\n",
+                      cq->cqn, cq->cons_index, next_cqe_sw(cq));
+               for (j = 0; j < 16; ++j)
+                       printk(KERN_ERR "[%2x] %08x\n", j * 4, be32_to_cpu(ctx[j]));
+       }
+
+       spin_lock_irq(&dev->cq_table.lock);
+       mthca_array_clear(&dev->cq_table.cq,
+                         cq->cqn & (dev->limits.num_cqs - 1));
+       spin_unlock_irq(&dev->cq_table.lock);
+
+       atomic_dec(&cq->refcount);
+       wait_event(cq->wait, !atomic_read(&cq->refcount));
+
+       mthca_free_mr(dev, &cq->mr);
+
+       if (cq->is_direct)
+               pci_free_consistent(dev->pdev,
+                                   (cq->ibcq.cqe + 1) * MTHCA_CQ_ENTRY_SIZE,
+                                   cq->queue.direct.buf,
+                                   pci_unmap_addr(&cq->queue.direct,
+                                                  mapping));
+       else {
+               int i;
+
+               for (i = 0;
+                    i < ((cq->ibcq.cqe + 1) * MTHCA_CQ_ENTRY_SIZE + PAGE_SIZE - 1) /
+                            PAGE_SIZE;
+                    ++i)
+                       pci_free_consistent(dev->pdev, PAGE_SIZE,
+                                           cq->queue.page_list[i].buf,
+                                           pci_unmap_addr(&cq->queue.page_list[i],
+                                                          mapping));
+
+               kfree(cq->queue.page_list);
+       }
+
+       mthca_free(&dev->cq_table.alloc, cq->cqn);
+       kfree(mailbox);
+}
+
+int __devinit mthca_init_cq_table(struct mthca_dev *dev)
+{
+       int err;
+
+       spin_lock_init(&dev->cq_table.lock);
+
+       err = mthca_alloc_init(&dev->cq_table.alloc,
+                              dev->limits.num_cqs,
+                              (1 << 24) - 1,
+                              dev->limits.reserved_cqs);
+       if (err)
+               return err;
+
+       err = mthca_array_init(&dev->cq_table.cq,
+                              dev->limits.num_cqs);
+       if (err)
+               mthca_alloc_cleanup(&dev->cq_table.alloc);
+
+       return err;
+}
+
+void __devexit mthca_cleanup_cq_table(struct mthca_dev *dev)
+{
+       mthca_array_cleanup(&dev->cq_table.cq, dev->limits.num_cqs);
+       mthca_alloc_cleanup(&dev->cq_table.alloc);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_dev.h b/drivers/infiniband/hw/mthca/mthca_dev.h
new file mode 100644 (file)
index 0000000..87634ca
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_dev.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#ifndef MTHCA_DEV_H
+#define MTHCA_DEV_H
+
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <asm/semaphore.h>
+
+#include "mthca_provider.h"
+#include "mthca_doorbell.h"
+
+#define DRV_NAME       "ib_mthca"
+#define PFX            DRV_NAME ": "
+#define DRV_VERSION    "0.06-pre"
+#define DRV_RELDATE    "November 8, 2004"
+
+/* Types of supported HCA */
+enum {
+       TAVOR,                  /* MT23108                        */
+       ARBEL_COMPAT,           /* MT25208 in Tavor compat mode   */
+       ARBEL_NATIVE            /* MT25208 with extended features */
+};
+
+enum {
+       MTHCA_FLAG_DDR_HIDDEN = 1 << 1,
+       MTHCA_FLAG_SRQ        = 1 << 2,
+       MTHCA_FLAG_MSI        = 1 << 3,
+       MTHCA_FLAG_MSI_X      = 1 << 4,
+       MTHCA_FLAG_NO_LAM     = 1 << 5
+};
+
+enum {
+       MTHCA_KAR_PAGE  = 1,
+       MTHCA_MAX_PORTS = 2
+};
+
+enum {
+       MTHCA_EQ_CONTEXT_SIZE =  0x40,
+       MTHCA_CQ_CONTEXT_SIZE =  0x40,
+       MTHCA_QP_CONTEXT_SIZE = 0x200,
+       MTHCA_RDB_ENTRY_SIZE  =  0x20,
+       MTHCA_AV_SIZE         =  0x20,
+       MTHCA_MGM_ENTRY_SIZE  =  0x40,
+
+       /* Arbel FW gives us these, but we need them for Tavor */
+       MTHCA_MPT_ENTRY_SIZE  =  0x40,
+       MTHCA_MTT_SEG_SIZE    =  0x40,
+};
+
+enum {
+       MTHCA_EQ_CMD,
+       MTHCA_EQ_ASYNC,
+       MTHCA_EQ_COMP,
+       MTHCA_NUM_EQ
+};
+
+struct mthca_cmd {
+       int                       use_events;
+       struct semaphore          hcr_sem;
+       struct semaphore          poll_sem;
+       struct semaphore          event_sem;
+       int                       max_cmds;
+       spinlock_t                context_lock;
+       int                       free_head;
+       struct mthca_cmd_context *context;
+       u16                       token_mask;
+};
+
+struct mthca_limits {
+       int      num_ports;
+       int      vl_cap;
+       int      mtu_cap;
+       int      gid_table_len;
+       int      pkey_table_len;
+       int      local_ca_ack_delay;
+       int      max_sg;
+       int      num_qps;
+       int      reserved_qps;
+       int      num_srqs;
+       int      reserved_srqs;
+       int      num_eecs;
+       int      reserved_eecs;
+       int      num_cqs;
+       int      reserved_cqs;
+       int      num_eqs;
+       int      reserved_eqs;
+       int      num_mpts;
+       int      num_mtt_segs;
+       int      mtt_seg_size;
+       int      reserved_mtts;
+       int      reserved_mrws;
+       int      reserved_uars;
+       int      num_mgms;
+       int      num_amgms;
+       int      reserved_mcgs;
+       int      num_pds;
+       int      reserved_pds;
+};
+
+struct mthca_alloc {
+       u32            last;
+       u32            top;
+       u32            max;
+       u32            mask;
+       spinlock_t     lock;
+       unsigned long *table;
+};
+
+struct mthca_array {
+       struct {
+               void    **page;
+               int       used;
+       } *page_list;
+};
+
+struct mthca_pd_table {
+       struct mthca_alloc alloc;
+};
+
+struct mthca_mr_table {
+       struct mthca_alloc      mpt_alloc;
+       int                     max_mtt_order;
+       unsigned long         **mtt_buddy;
+       u64                     mtt_base;
+       struct mthca_icm_table *mtt_table;
+       struct mthca_icm_table *mpt_table;
+};
+
+struct mthca_eq_table {
+       struct mthca_alloc alloc;
+       void __iomem      *clr_int;
+       u32                clr_mask;
+       struct mthca_eq    eq[MTHCA_NUM_EQ];
+       u64                icm_virt;
+       struct page       *icm_page;
+       dma_addr_t         icm_dma;
+       int                have_irq;
+       u8                 inta_pin;
+};
+
+struct mthca_cq_table {
+       struct mthca_alloc      alloc;
+       spinlock_t              lock;
+       struct mthca_array      cq;
+       struct mthca_icm_table *table;
+};
+
+struct mthca_qp_table {
+       struct mthca_alloc      alloc;
+       u32                     rdb_base;
+       int                     rdb_shift;
+       int                     sqp_start;
+       spinlock_t              lock;
+       struct mthca_array      qp;
+       struct mthca_icm_table *qp_table;
+       struct mthca_icm_table *eqp_table;
+};
+
+struct mthca_av_table {
+       struct pci_pool   *pool;
+       int                num_ddr_avs;
+       u64                ddr_av_base;
+       void __iomem      *av_map;
+       struct mthca_alloc alloc;
+};
+
+struct mthca_mcg_table {
+       struct semaphore   sem;
+       struct mthca_alloc alloc;
+};
+
+struct mthca_dev {
+       struct ib_device  ib_dev;
+       struct pci_dev   *pdev;
+
+       int              hca_type;
+       unsigned long    mthca_flags;
+
+       u32              rev_id;
+
+       /* firmware info */
+       u64              fw_ver;
+       union {
+               struct {
+                       u64 fw_start;
+                       u64 fw_end;
+               }        tavor;
+               struct {
+                       u64 clr_int_base;
+                       u64 eq_arm_base;
+                       u64 eq_set_ci_base;
+                       struct mthca_icm *fw_icm;
+                       struct mthca_icm *aux_icm;
+                       u16 fw_pages;
+               }        arbel;
+       }                fw;
+
+       u64              ddr_start;
+       u64              ddr_end;
+
+       MTHCA_DECLARE_DOORBELL_LOCK(doorbell_lock)
+       struct semaphore cap_mask_mutex;
+
+       void __iomem    *hcr;
+       void __iomem    *ecr_base;
+       void __iomem    *clr_base;
+       void __iomem    *kar;
+
+       struct mthca_cmd    cmd;
+       struct mthca_limits limits;
+
+       struct mthca_pd_table  pd_table;
+       struct mthca_mr_table  mr_table;
+       struct mthca_eq_table  eq_table;
+       struct mthca_cq_table  cq_table;
+       struct mthca_qp_table  qp_table;
+       struct mthca_av_table  av_table;
+       struct mthca_mcg_table mcg_table;
+
+       struct mthca_pd       driver_pd;
+       struct mthca_mr       driver_mr;
+
+       struct ib_mad_agent  *send_agent[MTHCA_MAX_PORTS][2];
+       struct ib_ah         *sm_ah[MTHCA_MAX_PORTS];
+       spinlock_t            sm_lock;
+};
+
+#define mthca_dbg(mdev, format, arg...) \
+       dev_dbg(&mdev->pdev->dev, format, ## arg)
+#define mthca_err(mdev, format, arg...) \
+       dev_err(&mdev->pdev->dev, format, ## arg)
+#define mthca_info(mdev, format, arg...) \
+       dev_info(&mdev->pdev->dev, format, ## arg)
+#define mthca_warn(mdev, format, arg...) \
+       dev_warn(&mdev->pdev->dev, format, ## arg)
+
+extern void __buggy_use_of_MTHCA_GET(void);
+extern void __buggy_use_of_MTHCA_PUT(void);
+
+#define MTHCA_GET(dest, source, offset)                               \
+       do {                                                          \
+               void *__p = (char *) (source) + (offset);             \
+               switch (sizeof (dest)) {                              \
+                       case 1: (dest) = *(u8 *) __p;       break;    \
+                       case 2: (dest) = be16_to_cpup(__p); break;    \
+                       case 4: (dest) = be32_to_cpup(__p); break;    \
+                       case 8: (dest) = be64_to_cpup(__p); break;    \
+                       default: __buggy_use_of_MTHCA_GET();          \
+               }                                                     \
+       } while (0)
+
+#define MTHCA_PUT(dest, source, offset)                               \
+       do {                                                          \
+               __typeof__(source) *__p =                             \
+                       (__typeof__(source) *) ((char *) (dest) + (offset)); \
+               switch (sizeof(source)) {                             \
+                       case 1: *__p = (source);            break;    \
+                       case 2: *__p = cpu_to_be16(source); break;    \
+                       case 4: *__p = cpu_to_be32(source); break;    \
+                       case 8: *__p = cpu_to_be64(source); break;    \
+                       default: __buggy_use_of_MTHCA_PUT();          \
+               }                                                     \
+       } while (0)
+
+int mthca_reset(struct mthca_dev *mdev);
+
+u32 mthca_alloc(struct mthca_alloc *alloc);
+void mthca_free(struct mthca_alloc *alloc, u32 obj);
+int mthca_alloc_init(struct mthca_alloc *alloc, u32 num, u32 mask,
+                    u32 reserved);
+void mthca_alloc_cleanup(struct mthca_alloc *alloc);
+void *mthca_array_get(struct mthca_array *array, int index);
+int mthca_array_set(struct mthca_array *array, int index, void *value);
+void mthca_array_clear(struct mthca_array *array, int index);
+int mthca_array_init(struct mthca_array *array, int nent);
+void mthca_array_cleanup(struct mthca_array *array, int nent);
+
+int mthca_init_pd_table(struct mthca_dev *dev);
+int mthca_init_mr_table(struct mthca_dev *dev);
+int mthca_init_eq_table(struct mthca_dev *dev);
+int mthca_init_cq_table(struct mthca_dev *dev);
+int mthca_init_qp_table(struct mthca_dev *dev);
+int mthca_init_av_table(struct mthca_dev *dev);
+int mthca_init_mcg_table(struct mthca_dev *dev);
+
+void mthca_cleanup_pd_table(struct mthca_dev *dev);
+void mthca_cleanup_mr_table(struct mthca_dev *dev);
+void mthca_cleanup_eq_table(struct mthca_dev *dev);
+void mthca_cleanup_cq_table(struct mthca_dev *dev);
+void mthca_cleanup_qp_table(struct mthca_dev *dev);
+void mthca_cleanup_av_table(struct mthca_dev *dev);
+void mthca_cleanup_mcg_table(struct mthca_dev *dev);
+
+int mthca_register_device(struct mthca_dev *dev);
+void mthca_unregister_device(struct mthca_dev *dev);
+
+int mthca_pd_alloc(struct mthca_dev *dev, struct mthca_pd *pd);
+void mthca_pd_free(struct mthca_dev *dev, struct mthca_pd *pd);
+
+int mthca_mr_alloc_notrans(struct mthca_dev *dev, u32 pd,
+                          u32 access, struct mthca_mr *mr);
+int mthca_mr_alloc_phys(struct mthca_dev *dev, u32 pd,
+                       u64 *buffer_list, int buffer_size_shift,
+                       int list_len, u64 iova, u64 total_size,
+                       u32 access, struct mthca_mr *mr);
+void mthca_free_mr(struct mthca_dev *dev, struct mthca_mr *mr);
+
+int mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt);
+void mthca_unmap_eq_icm(struct mthca_dev *dev);
+
+int mthca_poll_cq(struct ib_cq *ibcq, int num_entries,
+                 struct ib_wc *entry);
+void mthca_arm_cq(struct mthca_dev *dev, struct mthca_cq *cq,
+                 int solicited);
+int mthca_init_cq(struct mthca_dev *dev, int nent,
+                 struct mthca_cq *cq);
+void mthca_free_cq(struct mthca_dev *dev,
+                  struct mthca_cq *cq);
+void mthca_cq_event(struct mthca_dev *dev, u32 cqn);
+void mthca_cq_clean(struct mthca_dev *dev, u32 cqn, u32 qpn);
+
+void mthca_qp_event(struct mthca_dev *dev, u32 qpn,
+                   enum ib_event_type event_type);
+int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask);
+int mthca_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                   struct ib_send_wr **bad_wr);
+int mthca_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+                      struct ib_recv_wr **bad_wr);
+int mthca_free_err_wqe(struct mthca_qp *qp, int is_send,
+                      int index, int *dbd, u32 *new_wqe);
+int mthca_alloc_qp(struct mthca_dev *dev,
+                  struct mthca_pd *pd,
+                  struct mthca_cq *send_cq,
+                  struct mthca_cq *recv_cq,
+                  enum ib_qp_type type,
+                  enum ib_sig_type send_policy,
+                  enum ib_sig_type recv_policy,
+                  struct mthca_qp *qp);
+int mthca_alloc_sqp(struct mthca_dev *dev,
+                   struct mthca_pd *pd,
+                   struct mthca_cq *send_cq,
+                   struct mthca_cq *recv_cq,
+                   enum ib_sig_type send_policy,
+                   enum ib_sig_type recv_policy,
+                   int qpn,
+                   int port,
+                   struct mthca_sqp *sqp);
+void mthca_free_qp(struct mthca_dev *dev, struct mthca_qp *qp);
+int mthca_create_ah(struct mthca_dev *dev,
+                   struct mthca_pd *pd,
+                   struct ib_ah_attr *ah_attr,
+                   struct mthca_ah *ah);
+int mthca_destroy_ah(struct mthca_dev *dev, struct mthca_ah *ah);
+int mthca_read_ah(struct mthca_dev *dev, struct mthca_ah *ah,
+                 struct ib_ud_header *header);
+
+int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid);
+int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid);
+
+int mthca_process_mad(struct ib_device *ibdev,
+                     int mad_flags,
+                     u8 port_num,
+                     struct ib_wc *in_wc,
+                     struct ib_grh *in_grh,
+                     struct ib_mad *in_mad,
+                     struct ib_mad *out_mad);
+int mthca_create_agents(struct mthca_dev *dev);
+void mthca_free_agents(struct mthca_dev *dev);
+
+static inline struct mthca_dev *to_mdev(struct ib_device *ibdev)
+{
+       return container_of(ibdev, struct mthca_dev, ib_dev);
+}
+
+#endif /* MTHCA_DEV_H */
diff --git a/drivers/infiniband/hw/mthca/mthca_doorbell.h b/drivers/infiniband/hw/mthca/mthca_doorbell.h
new file mode 100644 (file)
index 0000000..cc8ad11
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_doorbell.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/types.h>
+
+#define MTHCA_RD_DOORBELL      0x00
+#define MTHCA_SEND_DOORBELL    0x10
+#define MTHCA_RECEIVE_DOORBELL 0x18
+#define MTHCA_CQ_DOORBELL      0x20
+#define MTHCA_EQ_DOORBELL      0x28
+
+#if BITS_PER_LONG == 64
+/*
+ * Assume that we can just write a 64-bit doorbell atomically.  s390
+ * actually doesn't have writeq() but S/390 systems don't even have
+ * PCI so we won't worry about it.
+ */
+
+#define MTHCA_DECLARE_DOORBELL_LOCK(name)
+#define MTHCA_INIT_DOORBELL_LOCK(ptr)    do { } while (0)
+#define MTHCA_GET_DOORBELL_LOCK(ptr)      (NULL)
+
+static inline void mthca_write64(u32 val[2], void __iomem *dest,
+                                spinlock_t *doorbell_lock)
+{
+       __raw_writeq(*(u64 *) val, dest);
+}
+
+#else
+
+/*
+ * Just fall back to a spinlock to protect the doorbell if
+ * BITS_PER_LONG is 32 -- there's no portable way to do atomic 64-bit
+ * MMIO writes.
+ */
+
+#define MTHCA_DECLARE_DOORBELL_LOCK(name) spinlock_t name;
+#define MTHCA_INIT_DOORBELL_LOCK(ptr)     spin_lock_init(ptr)
+#define MTHCA_GET_DOORBELL_LOCK(ptr)      (ptr)
+
+static inline void mthca_write64(u32 val[2], void __iomem *dest,
+                                spinlock_t *doorbell_lock)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(doorbell_lock, flags);
+       __raw_writel(val[0], dest);
+       __raw_writel(val[1], dest + 4);
+       spin_unlock_irqrestore(doorbell_lock, flags);
+}
+
+#endif
diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c
new file mode 100644 (file)
index 0000000..9b37f70
--- /dev/null
@@ -0,0 +1,744 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_eq.c 1382 2004-12-24 02:21:02Z roland $
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+
+#include "mthca_dev.h"
+#include "mthca_cmd.h"
+#include "mthca_config_reg.h"
+
+enum {
+       MTHCA_NUM_ASYNC_EQE = 0x80,
+       MTHCA_NUM_CMD_EQE   = 0x80,
+       MTHCA_EQ_ENTRY_SIZE = 0x20
+};
+
+/*
+ * Must be packed because start is 64 bits but only aligned to 32 bits.
+ */
+struct mthca_eq_context {
+       u32 flags;
+       u64 start;
+       u32 logsize_usrpage;
+       u32 pd;
+       u8  reserved1[3];
+       u8  intr;
+       u32 lost_count;
+       u32 lkey;
+       u32 reserved2[2];
+       u32 consumer_index;
+       u32 producer_index;
+       u32 reserved3[4];
+} __attribute__((packed));
+
+#define MTHCA_EQ_STATUS_OK          ( 0 << 28)
+#define MTHCA_EQ_STATUS_OVERFLOW    ( 9 << 28)
+#define MTHCA_EQ_STATUS_WRITE_FAIL  (10 << 28)
+#define MTHCA_EQ_OWNER_SW           ( 0 << 24)
+#define MTHCA_EQ_OWNER_HW           ( 1 << 24)
+#define MTHCA_EQ_FLAG_TR            ( 1 << 18)
+#define MTHCA_EQ_FLAG_OI            ( 1 << 17)
+#define MTHCA_EQ_STATE_ARMED        ( 1 <<  8)
+#define MTHCA_EQ_STATE_FIRED        ( 2 <<  8)
+#define MTHCA_EQ_STATE_ALWAYS_ARMED ( 3 <<  8)
+
+enum {
+       MTHCA_EVENT_TYPE_COMP               = 0x00,
+       MTHCA_EVENT_TYPE_PATH_MIG           = 0x01,
+       MTHCA_EVENT_TYPE_COMM_EST           = 0x02,
+       MTHCA_EVENT_TYPE_SQ_DRAINED         = 0x03,
+       MTHCA_EVENT_TYPE_SRQ_LAST_WQE       = 0x13,
+       MTHCA_EVENT_TYPE_CQ_ERROR           = 0x04,
+       MTHCA_EVENT_TYPE_WQ_CATAS_ERROR     = 0x05,
+       MTHCA_EVENT_TYPE_EEC_CATAS_ERROR    = 0x06,
+       MTHCA_EVENT_TYPE_PATH_MIG_FAILED    = 0x07,
+       MTHCA_EVENT_TYPE_WQ_INVAL_REQ_ERROR = 0x10,
+       MTHCA_EVENT_TYPE_WQ_ACCESS_ERROR    = 0x11,
+       MTHCA_EVENT_TYPE_SRQ_CATAS_ERROR    = 0x12,
+       MTHCA_EVENT_TYPE_LOCAL_CATAS_ERROR  = 0x08,
+       MTHCA_EVENT_TYPE_PORT_CHANGE        = 0x09,
+       MTHCA_EVENT_TYPE_EQ_OVERFLOW        = 0x0f,
+       MTHCA_EVENT_TYPE_ECC_DETECT         = 0x0e,
+       MTHCA_EVENT_TYPE_CMD                = 0x0a
+};
+
+#define MTHCA_ASYNC_EVENT_MASK ((1ULL << MTHCA_EVENT_TYPE_PATH_MIG)           | \
+                               (1ULL << MTHCA_EVENT_TYPE_COMM_EST)           | \
+                               (1ULL << MTHCA_EVENT_TYPE_SQ_DRAINED)         | \
+                               (1ULL << MTHCA_EVENT_TYPE_CQ_ERROR)           | \
+                               (1ULL << MTHCA_EVENT_TYPE_WQ_CATAS_ERROR)     | \
+                               (1ULL << MTHCA_EVENT_TYPE_EEC_CATAS_ERROR)    | \
+                               (1ULL << MTHCA_EVENT_TYPE_PATH_MIG_FAILED)    | \
+                               (1ULL << MTHCA_EVENT_TYPE_WQ_INVAL_REQ_ERROR) | \
+                               (1ULL << MTHCA_EVENT_TYPE_WQ_ACCESS_ERROR)    | \
+                               (1ULL << MTHCA_EVENT_TYPE_LOCAL_CATAS_ERROR)  | \
+                               (1ULL << MTHCA_EVENT_TYPE_PORT_CHANGE)        | \
+                               (1ULL << MTHCA_EVENT_TYPE_ECC_DETECT))
+#define MTHCA_SRQ_EVENT_MASK    (1ULL << MTHCA_EVENT_TYPE_SRQ_CATAS_ERROR)    | \
+                               (1ULL << MTHCA_EVENT_TYPE_SRQ_LAST_WQE)
+#define MTHCA_CMD_EVENT_MASK    (1ULL << MTHCA_EVENT_TYPE_CMD)
+
+#define MTHCA_EQ_DB_INC_CI     (1 << 24)
+#define MTHCA_EQ_DB_REQ_NOT    (2 << 24)
+#define MTHCA_EQ_DB_DISARM_CQ  (3 << 24)
+#define MTHCA_EQ_DB_SET_CI     (4 << 24)
+#define MTHCA_EQ_DB_ALWAYS_ARM (5 << 24)
+
+struct mthca_eqe {
+       u8 reserved1;
+       u8 type;
+       u8 reserved2;
+       u8 subtype;
+       union {
+               u32 raw[6];
+               struct {
+                       u32 cqn;
+               } __attribute__((packed)) comp;
+               struct {
+                       u16 reserved1;
+                       u16 token;
+                       u32 reserved2;
+                       u8  reserved3[3];
+                       u8  status;
+                       u64 out_param;
+               } __attribute__((packed)) cmd;
+               struct {
+                       u32 qpn;
+               } __attribute__((packed)) qp;
+               struct {
+                       u32 cqn;
+                       u32 reserved1;
+                       u8  reserved2[3];
+                       u8  syndrome;
+               } __attribute__((packed)) cq_err;
+               struct {
+                       u32 reserved1[2];
+                       u32 port;
+               } __attribute__((packed)) port_change;
+       } event;
+       u8 reserved3[3];
+       u8 owner;
+} __attribute__((packed));
+
+#define  MTHCA_EQ_ENTRY_OWNER_SW      (0 << 7)
+#define  MTHCA_EQ_ENTRY_OWNER_HW      (1 << 7)
+
+static inline u64 async_mask(struct mthca_dev *dev)
+{
+       return dev->mthca_flags & MTHCA_FLAG_SRQ ?
+               MTHCA_ASYNC_EVENT_MASK | MTHCA_SRQ_EVENT_MASK :
+               MTHCA_ASYNC_EVENT_MASK;
+}
+
+static inline void set_eq_ci(struct mthca_dev *dev, struct mthca_eq *eq, u32 ci)
+{
+       u32 doorbell[2];
+
+       doorbell[0] = cpu_to_be32(MTHCA_EQ_DB_SET_CI | eq->eqn);
+       doorbell[1] = cpu_to_be32(ci & (eq->nent - 1));
+
+       mthca_write64(doorbell,
+                     dev->kar + MTHCA_EQ_DOORBELL,
+                     MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock));
+}
+
+static inline void eq_req_not(struct mthca_dev *dev, int eqn)
+{
+       u32 doorbell[2];
+
+       doorbell[0] = cpu_to_be32(MTHCA_EQ_DB_REQ_NOT | eqn);
+       doorbell[1] = 0;
+
+       mthca_write64(doorbell,
+                     dev->kar + MTHCA_EQ_DOORBELL,
+                     MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock));
+}
+
+static inline void disarm_cq(struct mthca_dev *dev, int eqn, int cqn)
+{
+       u32 doorbell[2];
+
+       doorbell[0] = cpu_to_be32(MTHCA_EQ_DB_DISARM_CQ | eqn);
+       doorbell[1] = cpu_to_be32(cqn);
+
+       mthca_write64(doorbell,
+                     dev->kar + MTHCA_EQ_DOORBELL,
+                     MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock));
+}
+
+static inline struct mthca_eqe *get_eqe(struct mthca_eq *eq, u32 entry)
+{
+       unsigned long off = (entry & (eq->nent - 1)) * MTHCA_EQ_ENTRY_SIZE;
+       return eq->page_list[off / PAGE_SIZE].buf + off % PAGE_SIZE;
+}
+
+static inline struct mthca_eqe* next_eqe_sw(struct mthca_eq *eq)
+{
+       struct mthca_eqe* eqe;
+       eqe = get_eqe(eq, eq->cons_index);
+       return (MTHCA_EQ_ENTRY_OWNER_HW & eqe->owner) ? NULL : eqe;
+}
+
+static inline void set_eqe_hw(struct mthca_eqe *eqe)
+{
+       eqe->owner =  MTHCA_EQ_ENTRY_OWNER_HW;
+}
+
+static void port_change(struct mthca_dev *dev, int port, int active)
+{
+       struct ib_event record;
+
+       mthca_dbg(dev, "Port change to %s for port %d\n",
+                 active ? "active" : "down", port);
+
+       record.device = &dev->ib_dev;
+       record.event  = active ? IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR;
+       record.element.port_num = port;
+
+       ib_dispatch_event(&record);
+}
+
+static void mthca_eq_int(struct mthca_dev *dev, struct mthca_eq *eq)
+{
+       struct mthca_eqe *eqe;
+       int disarm_cqn;
+       int  eqes_found = 0;
+
+       while ((eqe = next_eqe_sw(eq))) {
+               int set_ci = 0;
+
+               /*
+                * Make sure we read EQ entry contents after we've
+                * checked the ownership bit.
+                */
+               rmb();
+
+               switch (eqe->type) {
+               case MTHCA_EVENT_TYPE_COMP:
+                       disarm_cqn = be32_to_cpu(eqe->event.comp.cqn) & 0xffffff;
+                       disarm_cq(dev, eq->eqn, disarm_cqn);
+                       mthca_cq_event(dev, disarm_cqn);
+                       break;
+
+               case MTHCA_EVENT_TYPE_PATH_MIG:
+                       mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff,
+                                      IB_EVENT_PATH_MIG);
+                       break;
+
+               case MTHCA_EVENT_TYPE_COMM_EST:
+                       mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff,
+                                      IB_EVENT_COMM_EST);
+                       break;
+
+               case MTHCA_EVENT_TYPE_SQ_DRAINED:
+                       mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff,
+                                      IB_EVENT_SQ_DRAINED);
+                       break;
+
+               case MTHCA_EVENT_TYPE_WQ_CATAS_ERROR:
+                       mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff,
+                                      IB_EVENT_QP_FATAL);
+                       break;
+
+               case MTHCA_EVENT_TYPE_PATH_MIG_FAILED:
+                       mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff,
+                                      IB_EVENT_PATH_MIG_ERR);
+                       break;
+
+               case MTHCA_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
+                       mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff,
+                                      IB_EVENT_QP_REQ_ERR);
+                       break;
+
+               case MTHCA_EVENT_TYPE_WQ_ACCESS_ERROR:
+                       mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff,
+                                      IB_EVENT_QP_ACCESS_ERR);
+                       break;
+
+               case MTHCA_EVENT_TYPE_CMD:
+                       mthca_cmd_event(dev,
+                                       be16_to_cpu(eqe->event.cmd.token),
+                                       eqe->event.cmd.status,
+                                       be64_to_cpu(eqe->event.cmd.out_param));
+                       /*
+                        * cmd_event() may add more commands.
+                        * The card will think the queue has overflowed if
+                        * we don't tell it we've been processing events.
+                        */
+                       set_ci = 1;
+                       break;
+
+               case MTHCA_EVENT_TYPE_PORT_CHANGE:
+                       port_change(dev,
+                                   (be32_to_cpu(eqe->event.port_change.port) >> 28) & 3,
+                                   eqe->subtype == 0x4);
+                       break;
+
+               case MTHCA_EVENT_TYPE_CQ_ERROR:
+                       mthca_warn(dev, "CQ %s on CQN %08x\n",
+                                  eqe->event.cq_err.syndrome == 1 ?
+                                  "overrun" : "access violation",
+                                  be32_to_cpu(eqe->event.cq_err.cqn));
+                       break;
+
+               case MTHCA_EVENT_TYPE_EQ_OVERFLOW:
+                       mthca_warn(dev, "EQ overrun on EQN %d\n", eq->eqn);
+                       break;
+
+               case MTHCA_EVENT_TYPE_EEC_CATAS_ERROR:
+               case MTHCA_EVENT_TYPE_SRQ_CATAS_ERROR:
+               case MTHCA_EVENT_TYPE_LOCAL_CATAS_ERROR:
+               case MTHCA_EVENT_TYPE_ECC_DETECT:
+               default:
+                       mthca_warn(dev, "Unhandled event %02x(%02x) on EQ %d\n",
+                                  eqe->type, eqe->subtype, eq->eqn);
+                       break;
+               };
+
+               set_eqe_hw(eqe);
+               ++eq->cons_index;
+               eqes_found = 1;
+
+               if (set_ci) {
+                       wmb(); /* see comment below */
+                       set_eq_ci(dev, eq, eq->cons_index);
+                       set_ci = 0;
+               }
+       }
+
+       /*
+        * This barrier makes sure that all updates to
+        * ownership bits done by set_eqe_hw() hit memory
+        * before the consumer index is updated.  set_eq_ci()
+        * allows the HCA to possibly write more EQ entries,
+        * and we want to avoid the exceedingly unlikely
+        * possibility of the HCA writing an entry and then
+        * having set_eqe_hw() overwrite the owner field.
+        */
+       if (likely(eqes_found)) {
+               wmb();
+               set_eq_ci(dev, eq, eq->cons_index);
+       }
+       eq_req_not(dev, eq->eqn);
+}
+
+static irqreturn_t mthca_interrupt(int irq, void *dev_ptr, struct pt_regs *regs)
+{
+       struct mthca_dev *dev = dev_ptr;
+       u32 ecr;
+       int work = 0;
+       int i;
+
+       if (dev->eq_table.clr_mask)
+               writel(dev->eq_table.clr_mask, dev->eq_table.clr_int);
+
+       if ((ecr = readl(dev->ecr_base + 4)) != 0) {
+               work = 1;
+
+               writel(ecr, dev->ecr_base +
+                      MTHCA_ECR_CLR_BASE - MTHCA_ECR_BASE + 4);
+
+               for (i = 0; i < MTHCA_NUM_EQ; ++i)
+                       if (ecr & dev->eq_table.eq[i].ecr_mask)
+                               mthca_eq_int(dev, &dev->eq_table.eq[i]);
+       }
+
+       return IRQ_RETVAL(work);
+}
+
+static irqreturn_t mthca_msi_x_interrupt(int irq, void *eq_ptr,
+                                        struct pt_regs *regs)
+{
+       struct mthca_eq  *eq  = eq_ptr;
+       struct mthca_dev *dev = eq->dev;
+
+       mthca_eq_int(dev, eq);
+
+       /* MSI-X vectors always belong to us */
+       return IRQ_HANDLED;
+}
+
+static int __devinit mthca_create_eq(struct mthca_dev *dev,
+                                    int nent,
+                                    u8 intr,
+                                    struct mthca_eq *eq)
+{
+       int npages = (nent * MTHCA_EQ_ENTRY_SIZE + PAGE_SIZE - 1) /
+               PAGE_SIZE;
+       u64 *dma_list = NULL;
+       dma_addr_t t;
+       void *mailbox = NULL;
+       struct mthca_eq_context *eq_context;
+       int err = -ENOMEM;
+       int i;
+       u8 status;
+
+       /* Make sure EQ size is aligned to a power of 2 size. */
+       for (i = 1; i < nent; i <<= 1)
+               ; /* nothing */
+       nent = i;
+
+       eq->dev = dev;
+
+       eq->page_list = kmalloc(npages * sizeof *eq->page_list,
+                               GFP_KERNEL);
+       if (!eq->page_list)
+               goto err_out;
+
+       for (i = 0; i < npages; ++i)
+               eq->page_list[i].buf = NULL;
+
+       dma_list = kmalloc(npages * sizeof *dma_list, GFP_KERNEL);
+       if (!dma_list)
+               goto err_out_free;
+
+       mailbox = kmalloc(sizeof *eq_context + MTHCA_CMD_MAILBOX_EXTRA,
+                         GFP_KERNEL);
+       if (!mailbox)
+               goto err_out_free;
+       eq_context = MAILBOX_ALIGN(mailbox);
+
+       for (i = 0; i < npages; ++i) {
+               eq->page_list[i].buf = pci_alloc_consistent(dev->pdev,
+                                                           PAGE_SIZE, &t);
+               if (!eq->page_list[i].buf)
+                       goto err_out_free;
+
+               dma_list[i] = t;
+               pci_unmap_addr_set(&eq->page_list[i], mapping, t);
+
+               memset(eq->page_list[i].buf, 0, PAGE_SIZE);
+       }
+
+       for (i = 0; i < nent; ++i)
+               set_eqe_hw(get_eqe(eq, i));
+
+       eq->eqn = mthca_alloc(&dev->eq_table.alloc);
+       if (eq->eqn == -1)
+               goto err_out_free;
+
+       err = mthca_mr_alloc_phys(dev, dev->driver_pd.pd_num,
+                                 dma_list, PAGE_SHIFT, npages,
+                                 0, npages * PAGE_SIZE,
+                                 MTHCA_MPT_FLAG_LOCAL_WRITE |
+                                 MTHCA_MPT_FLAG_LOCAL_READ,
+                                 &eq->mr);
+       if (err)
+               goto err_out_free_eq;
+
+       eq->nent = nent;
+
+       memset(eq_context, 0, sizeof *eq_context);
+       eq_context->flags           = cpu_to_be32(MTHCA_EQ_STATUS_OK   |
+                                                 MTHCA_EQ_OWNER_HW    |
+                                                 MTHCA_EQ_STATE_ARMED |
+                                                 MTHCA_EQ_FLAG_TR);
+       eq_context->start           = cpu_to_be64(0);
+       eq_context->logsize_usrpage = cpu_to_be32((ffs(nent) - 1) << 24 |
+                                                 MTHCA_KAR_PAGE);
+       eq_context->pd              = cpu_to_be32(dev->driver_pd.pd_num);
+       eq_context->intr            = intr;
+       eq_context->lkey            = cpu_to_be32(eq->mr.ibmr.lkey);
+
+       err = mthca_SW2HW_EQ(dev, eq_context, eq->eqn, &status);
+       if (err) {
+               mthca_warn(dev, "SW2HW_EQ failed (%d)\n", err);
+               goto err_out_free_mr;
+       }
+       if (status) {
+               mthca_warn(dev, "SW2HW_EQ returned status 0x%02x\n",
+                          status);
+               err = -EINVAL;
+               goto err_out_free_mr;
+       }
+
+       kfree(dma_list);
+       kfree(mailbox);
+
+       eq->ecr_mask   = swab32(1 << eq->eqn);
+       eq->cons_index = 0;
+
+       eq_req_not(dev, eq->eqn);
+
+       mthca_dbg(dev, "Allocated EQ %d with %d entries\n",
+                 eq->eqn, nent);
+
+       return err;
+
+ err_out_free_mr:
+       mthca_free_mr(dev, &eq->mr);
+
+ err_out_free_eq:
+       mthca_free(&dev->eq_table.alloc, eq->eqn);
+
+ err_out_free:
+       for (i = 0; i < npages; ++i)
+               if (eq->page_list[i].buf)
+                       pci_free_consistent(dev->pdev, PAGE_SIZE,
+                                           eq->page_list[i].buf,
+                                           pci_unmap_addr(&eq->page_list[i],
+                                                          mapping));
+
+       kfree(eq->page_list);
+       kfree(dma_list);
+       kfree(mailbox);
+
+ err_out:
+       return err;
+}
+
+static void mthca_free_eq(struct mthca_dev *dev,
+                         struct mthca_eq *eq)
+{
+       void *mailbox = NULL;
+       int err;
+       u8 status;
+       int npages = (eq->nent * MTHCA_EQ_ENTRY_SIZE + PAGE_SIZE - 1) /
+               PAGE_SIZE;
+       int i;
+
+       mailbox = kmalloc(sizeof (struct mthca_eq_context) + MTHCA_CMD_MAILBOX_EXTRA,
+                         GFP_KERNEL);
+       if (!mailbox)
+               return;
+
+       err = mthca_HW2SW_EQ(dev, MAILBOX_ALIGN(mailbox),
+                            eq->eqn, &status);
+       if (err)
+               mthca_warn(dev, "HW2SW_EQ failed (%d)\n", err);
+       if (status)
+               mthca_warn(dev, "HW2SW_EQ returned status 0x%02x\n",
+                          status);
+
+       if (0) {
+               mthca_dbg(dev, "Dumping EQ context %02x:\n", eq->eqn);
+               for (i = 0; i < sizeof (struct mthca_eq_context) / 4; ++i) {
+                       if (i % 4 == 0)
+                               printk("[%02x] ", i * 4);
+                       printk(" %08x", be32_to_cpup(MAILBOX_ALIGN(mailbox) + i * 4));
+                       if ((i + 1) % 4 == 0)
+                               printk("\n");
+               }
+       }
+
+
+       mthca_free_mr(dev, &eq->mr);
+       for (i = 0; i < npages; ++i)
+               pci_free_consistent(dev->pdev, PAGE_SIZE,
+                                   eq->page_list[i].buf,
+                                   pci_unmap_addr(&eq->page_list[i], mapping));
+
+       kfree(eq->page_list);
+       kfree(mailbox);
+}
+
+static void mthca_free_irqs(struct mthca_dev *dev)
+{
+       int i;
+
+       if (dev->eq_table.have_irq)
+               free_irq(dev->pdev->irq, dev);
+       for (i = 0; i < MTHCA_NUM_EQ; ++i)
+               if (dev->eq_table.eq[i].have_irq)
+                       free_irq(dev->eq_table.eq[i].msi_x_vector,
+                                dev->eq_table.eq + i);
+}
+
+int __devinit mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt)
+{
+       int ret;
+       u8 status;
+
+       /*
+        * We assume that mapping one page is enough for the whole EQ
+        * context table.  This is fine with all current HCAs, because
+        * we only use 32 EQs and each EQ uses 32 bytes of context
+        * memory, or 1 KB total.
+        */
+       dev->eq_table.icm_virt = icm_virt;
+       dev->eq_table.icm_page = alloc_page(GFP_HIGHUSER);
+       if (!dev->eq_table.icm_page)
+               return -ENOMEM;
+       dev->eq_table.icm_dma  = pci_map_page(dev->pdev, dev->eq_table.icm_page, 0,
+                                             PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+       if (pci_dma_mapping_error(dev->eq_table.icm_dma)) {
+               __free_page(dev->eq_table.icm_page);
+               return -ENOMEM;
+       }
+
+       ret = mthca_MAP_ICM_page(dev, dev->eq_table.icm_dma, icm_virt, &status);
+       if (!ret && status)
+               ret = -EINVAL;
+       if (ret) {
+               pci_unmap_page(dev->pdev, dev->eq_table.icm_dma, PAGE_SIZE,
+                              PCI_DMA_BIDIRECTIONAL);
+               __free_page(dev->eq_table.icm_page);
+       }
+
+       return ret;
+}
+
+void __devexit mthca_unmap_eq_icm(struct mthca_dev *dev)
+{
+       u8 status;
+
+       mthca_UNMAP_ICM(dev, dev->eq_table.icm_virt, PAGE_SIZE / 4096, &status);
+       pci_unmap_page(dev->pdev, dev->eq_table.icm_dma, PAGE_SIZE,
+                      PCI_DMA_BIDIRECTIONAL);
+       __free_page(dev->eq_table.icm_page);
+}
+
+int __devinit mthca_init_eq_table(struct mthca_dev *dev)
+{
+       int err;
+       u8 status;
+       u8 intr;
+       int i;
+
+       err = mthca_alloc_init(&dev->eq_table.alloc,
+                              dev->limits.num_eqs,
+                              dev->limits.num_eqs - 1,
+                              dev->limits.reserved_eqs);
+       if (err)
+               return err;
+
+       if (dev->mthca_flags & MTHCA_FLAG_MSI ||
+           dev->mthca_flags & MTHCA_FLAG_MSI_X) {
+               dev->eq_table.clr_mask = 0;
+       } else {
+               dev->eq_table.clr_mask =
+                       swab32(1 << (dev->eq_table.inta_pin & 31));
+               dev->eq_table.clr_int  = dev->clr_base +
+                       (dev->eq_table.inta_pin < 31 ? 4 : 0);
+       }
+
+       intr = (dev->mthca_flags & MTHCA_FLAG_MSI) ?
+               128 : dev->eq_table.inta_pin;
+
+       err = mthca_create_eq(dev, dev->limits.num_cqs,
+                             (dev->mthca_flags & MTHCA_FLAG_MSI_X) ? 128 : intr,
+                             &dev->eq_table.eq[MTHCA_EQ_COMP]);
+       if (err)
+               goto err_out_free;
+
+       err = mthca_create_eq(dev, MTHCA_NUM_ASYNC_EQE,
+                             (dev->mthca_flags & MTHCA_FLAG_MSI_X) ? 129 : intr,
+                             &dev->eq_table.eq[MTHCA_EQ_ASYNC]);
+       if (err)
+               goto err_out_comp;
+
+       err = mthca_create_eq(dev, MTHCA_NUM_CMD_EQE,
+                             (dev->mthca_flags & MTHCA_FLAG_MSI_X) ? 130 : intr,
+                             &dev->eq_table.eq[MTHCA_EQ_CMD]);
+       if (err)
+               goto err_out_async;
+
+       if (dev->mthca_flags & MTHCA_FLAG_MSI_X) {
+               static const char *eq_name[] = {
+                       [MTHCA_EQ_COMP]  = DRV_NAME " (comp)",
+                       [MTHCA_EQ_ASYNC] = DRV_NAME " (async)",
+                       [MTHCA_EQ_CMD]   = DRV_NAME " (cmd)"
+               };
+
+               for (i = 0; i < MTHCA_NUM_EQ; ++i) {
+                       err = request_irq(dev->eq_table.eq[i].msi_x_vector,
+                                         mthca_msi_x_interrupt, 0,
+                                         eq_name[i], dev->eq_table.eq + i);
+                       if (err)
+                               goto err_out_cmd;
+                       dev->eq_table.eq[i].have_irq = 1;
+               }
+       } else {
+               err = request_irq(dev->pdev->irq, mthca_interrupt, SA_SHIRQ,
+                                 DRV_NAME, dev);
+               if (err)
+                       goto err_out_cmd;
+               dev->eq_table.have_irq = 1;
+       }
+
+       err = mthca_MAP_EQ(dev, async_mask(dev),
+                          0, dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn, &status);
+       if (err)
+               mthca_warn(dev, "MAP_EQ for async EQ %d failed (%d)\n",
+                          dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn, err);
+       if (status)
+               mthca_warn(dev, "MAP_EQ for async EQ %d returned status 0x%02x\n",
+                          dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn, status);
+
+       err = mthca_MAP_EQ(dev, MTHCA_CMD_EVENT_MASK,
+                          0, dev->eq_table.eq[MTHCA_EQ_CMD].eqn, &status);
+       if (err)
+               mthca_warn(dev, "MAP_EQ for cmd EQ %d failed (%d)\n",
+                          dev->eq_table.eq[MTHCA_EQ_CMD].eqn, err);
+       if (status)
+               mthca_warn(dev, "MAP_EQ for cmd EQ %d returned status 0x%02x\n",
+                          dev->eq_table.eq[MTHCA_EQ_CMD].eqn, status);
+
+       return 0;
+
+err_out_cmd:
+       mthca_free_irqs(dev);
+       mthca_free_eq(dev, &dev->eq_table.eq[MTHCA_EQ_CMD]);
+
+err_out_async:
+       mthca_free_eq(dev, &dev->eq_table.eq[MTHCA_EQ_ASYNC]);
+
+err_out_comp:
+       mthca_free_eq(dev, &dev->eq_table.eq[MTHCA_EQ_COMP]);
+
+err_out_free:
+       mthca_alloc_cleanup(&dev->eq_table.alloc);
+       return err;
+}
+
+void __devexit mthca_cleanup_eq_table(struct mthca_dev *dev)
+{
+       u8 status;
+       int i;
+
+       mthca_free_irqs(dev);
+
+       mthca_MAP_EQ(dev, async_mask(dev),
+                    1, dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn, &status);
+       mthca_MAP_EQ(dev, MTHCA_CMD_EVENT_MASK,
+                    1, dev->eq_table.eq[MTHCA_EQ_CMD].eqn, &status);
+
+       for (i = 0; i < MTHCA_NUM_EQ; ++i)
+               mthca_free_eq(dev, &dev->eq_table.eq[i]);
+
+       mthca_alloc_cleanup(&dev->eq_table.alloc);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_mad.c b/drivers/infiniband/hw/mthca/mthca_mad.c
new file mode 100644 (file)
index 0000000..7df2236
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_mad.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <ib_verbs.h>
+#include <ib_mad.h>
+#include <ib_smi.h>
+
+#include "mthca_dev.h"
+#include "mthca_cmd.h"
+
+enum {
+       MTHCA_VENDOR_CLASS1 = 0x9,
+       MTHCA_VENDOR_CLASS2 = 0xa
+};
+
+struct mthca_trap_mad {
+       struct ib_mad *mad;
+       DECLARE_PCI_UNMAP_ADDR(mapping)
+};
+
+static void update_sm_ah(struct mthca_dev *dev,
+                        u8 port_num, u16 lid, u8 sl)
+{
+       struct ib_ah *new_ah;
+       struct ib_ah_attr ah_attr;
+       unsigned long flags;
+
+       if (!dev->send_agent[port_num - 1][0])
+               return;
+
+       memset(&ah_attr, 0, sizeof ah_attr);
+       ah_attr.dlid     = lid;
+       ah_attr.sl       = sl;
+       ah_attr.port_num = port_num;
+
+       new_ah = ib_create_ah(dev->send_agent[port_num - 1][0]->qp->pd,
+                             &ah_attr);
+       if (IS_ERR(new_ah))
+               return;
+
+       spin_lock_irqsave(&dev->sm_lock, flags);
+       if (dev->sm_ah[port_num - 1])
+               ib_destroy_ah(dev->sm_ah[port_num - 1]);
+       dev->sm_ah[port_num - 1] = new_ah;
+       spin_unlock_irqrestore(&dev->sm_lock, flags);
+}
+
+/*
+ * Snoop SM MADs for port info and P_Key table sets, so we can
+ * synthesize LID change and P_Key change events.
+ */
+static void smp_snoop(struct ib_device *ibdev,
+                     u8 port_num,
+                     struct ib_mad *mad)
+{
+       struct ib_event event;
+
+       if ((mad->mad_hdr.mgmt_class  == IB_MGMT_CLASS_SUBN_LID_ROUTED ||
+            mad->mad_hdr.mgmt_class  == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) &&
+           mad->mad_hdr.method     == IB_MGMT_METHOD_SET) {
+               if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PORT_INFO) {
+                       update_sm_ah(to_mdev(ibdev), port_num,
+                                    be16_to_cpup((__be16 *) (mad->data + 58)),
+                                    (*(u8 *) (mad->data + 76)) & 0xf);
+
+                       event.device           = ibdev;
+                       event.event            = IB_EVENT_LID_CHANGE;
+                       event.element.port_num = port_num;
+                       ib_dispatch_event(&event);
+               }
+
+               if (mad->mad_hdr.attr_id == IB_SMP_ATTR_PKEY_TABLE) {
+                       event.device           = ibdev;
+                       event.event            = IB_EVENT_PKEY_CHANGE;
+                       event.element.port_num = port_num;
+                       ib_dispatch_event(&event);
+               }
+       }
+}
+
+static void forward_trap(struct mthca_dev *dev,
+                        u8 port_num,
+                        struct ib_mad *mad)
+{
+       int qpn = mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED;
+       struct mthca_trap_mad *tmad;
+       struct ib_sge      gather_list;
+       struct ib_send_wr *bad_wr, wr = {
+               .opcode      = IB_WR_SEND,
+               .sg_list     = &gather_list,
+               .num_sge     = 1,
+               .send_flags  = IB_SEND_SIGNALED,
+               .wr          = {
+                        .ud = {
+                                .remote_qpn  = qpn,
+                                .remote_qkey = qpn ? IB_QP1_QKEY : 0,
+                                .timeout_ms  = 0
+                        }
+                }
+       };
+       struct ib_mad_agent *agent = dev->send_agent[port_num - 1][qpn];
+       int ret;
+       unsigned long flags;
+
+       if (agent) {
+               tmad = kmalloc(sizeof *tmad, GFP_KERNEL);
+               if (!tmad)
+                       return;
+
+               tmad->mad = kmalloc(sizeof *tmad->mad, GFP_KERNEL);
+               if (!tmad->mad) {
+                       kfree(tmad);
+                       return;
+               }
+
+               memcpy(tmad->mad, mad, sizeof *mad);
+
+               wr.wr.ud.mad_hdr = &tmad->mad->mad_hdr;
+               wr.wr_id         = (unsigned long) tmad;
+
+               gather_list.addr   = dma_map_single(agent->device->dma_device,
+                                                   tmad->mad,
+                                                   sizeof *tmad->mad,
+                                                   DMA_TO_DEVICE);
+               gather_list.length = sizeof *tmad->mad;
+               gather_list.lkey   = to_mpd(agent->qp->pd)->ntmr.ibmr.lkey;
+               pci_unmap_addr_set(tmad, mapping, gather_list.addr);
+
+               /*
+                * We rely here on the fact that MLX QPs don't use the
+                * address handle after the send is posted (this is
+                * wrong following the IB spec strictly, but we know
+                * it's OK for our devices).
+                */
+               spin_lock_irqsave(&dev->sm_lock, flags);
+               wr.wr.ud.ah      = dev->sm_ah[port_num - 1];
+               if (wr.wr.ud.ah)
+                       ret = ib_post_send_mad(agent, &wr, &bad_wr);
+               else
+                       ret = -EINVAL;
+               spin_unlock_irqrestore(&dev->sm_lock, flags);
+
+               if (ret) {
+                       dma_unmap_single(agent->device->dma_device,
+                                        pci_unmap_addr(tmad, mapping),
+                                        sizeof *tmad->mad,
+                                        DMA_TO_DEVICE);
+                       kfree(tmad->mad);
+                       kfree(tmad);
+               }
+       }
+}
+
+int mthca_process_mad(struct ib_device *ibdev,
+                     int mad_flags,
+                     u8 port_num,
+                     struct ib_wc *in_wc,
+                     struct ib_grh *in_grh,
+                     struct ib_mad *in_mad,
+                     struct ib_mad *out_mad)
+{
+       int err;
+       u8 status;
+       u16 slid = in_wc ? in_wc->slid : IB_LID_PERMISSIVE;
+
+       /* Forward locally generated traps to the SM */
+       if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP &&
+           slid == 0) {
+               forward_trap(to_mdev(ibdev), port_num, in_mad);
+               return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
+       }
+
+       /*
+        * Only handle SM gets, sets and trap represses for SM class
+        *
+        * Only handle PMA and Mellanox vendor-specific class gets and
+        * sets for other classes.
+        */
+       if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED ||
+           in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
+               if (in_mad->mad_hdr.method   != IB_MGMT_METHOD_GET &&
+                   in_mad->mad_hdr.method   != IB_MGMT_METHOD_SET &&
+                   in_mad->mad_hdr.method   != IB_MGMT_METHOD_TRAP_REPRESS)
+                       return IB_MAD_RESULT_SUCCESS;
+
+               /*
+                * Don't process SMInfo queries or vendor-specific
+                * MADs -- the SMA can't handle them.
+                */
+               if (in_mad->mad_hdr.attr_id == IB_SMP_ATTR_SM_INFO ||
+                   ((in_mad->mad_hdr.attr_id & IB_SMP_ATTR_VENDOR_MASK) ==
+                    IB_SMP_ATTR_VENDOR_MASK))
+                       return IB_MAD_RESULT_SUCCESS;
+       } else if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_PERF_MGMT ||
+                  in_mad->mad_hdr.mgmt_class == MTHCA_VENDOR_CLASS1     ||
+                  in_mad->mad_hdr.mgmt_class == MTHCA_VENDOR_CLASS2) {
+               if (in_mad->mad_hdr.method  != IB_MGMT_METHOD_GET &&
+                   in_mad->mad_hdr.method  != IB_MGMT_METHOD_SET)
+                       return IB_MAD_RESULT_SUCCESS;
+       } else
+               return IB_MAD_RESULT_SUCCESS;
+
+       err = mthca_MAD_IFC(to_mdev(ibdev),
+                           mad_flags & IB_MAD_IGNORE_MKEY,
+                           mad_flags & IB_MAD_IGNORE_BKEY,
+                           port_num, in_wc, in_grh, in_mad, out_mad,
+                           &status);
+       if (err) {
+               mthca_err(to_mdev(ibdev), "MAD_IFC failed\n");
+               return IB_MAD_RESULT_FAILURE;
+       }
+       if (status == MTHCA_CMD_STAT_BAD_PKT)
+               return IB_MAD_RESULT_SUCCESS;
+       if (status) {
+               mthca_err(to_mdev(ibdev), "MAD_IFC returned status %02x\n",
+                         status);
+               return IB_MAD_RESULT_FAILURE;
+       }
+
+       if (!out_mad->mad_hdr.status)
+               smp_snoop(ibdev, port_num, in_mad);
+
+       /* set return bit in status of directed route responses */
+       if (in_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+               out_mad->mad_hdr.status |= cpu_to_be16(1 << 15);
+
+       if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP_REPRESS)
+               /* no response for trap repress */
+               return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED;
+
+       return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY;
+}
+
+static void send_handler(struct ib_mad_agent *agent,
+                        struct ib_mad_send_wc *mad_send_wc)
+{
+       struct mthca_trap_mad *tmad =
+               (void *) (unsigned long) mad_send_wc->wr_id;
+
+       dma_unmap_single(agent->device->dma_device,
+                        pci_unmap_addr(tmad, mapping),
+                        sizeof *tmad->mad,
+                        DMA_TO_DEVICE);
+       kfree(tmad->mad);
+       kfree(tmad);
+}
+
+int mthca_create_agents(struct mthca_dev *dev)
+{
+       struct ib_mad_agent *agent;
+       int p, q;
+
+       spin_lock_init(&dev->sm_lock);
+
+       for (p = 0; p < dev->limits.num_ports; ++p)
+               for (q = 0; q <= 1; ++q) {
+                       agent = ib_register_mad_agent(&dev->ib_dev, p + 1,
+                                                     q ? IB_QPT_GSI : IB_QPT_SMI,
+                                                     NULL, 0, send_handler,
+                                                     NULL, NULL);
+                       if (IS_ERR(agent))
+                               goto err;
+                       dev->send_agent[p][q] = agent;
+               }
+
+       return 0;
+
+err:
+       for (p = 0; p < dev->limits.num_ports; ++p)
+               for (q = 0; q <= 1; ++q)
+                       if (dev->send_agent[p][q])
+                               ib_unregister_mad_agent(dev->send_agent[p][q]);
+
+       return PTR_ERR(agent);
+}
+
+void mthca_free_agents(struct mthca_dev *dev)
+{
+       struct ib_mad_agent *agent;
+       int p, q;
+
+       for (p = 0; p < dev->limits.num_ports; ++p) {
+               for (q = 0; q <= 1; ++q) {
+                       agent = dev->send_agent[p][q];
+                       dev->send_agent[p][q] = NULL;
+                       ib_unregister_mad_agent(agent);
+               }
+
+               if (dev->sm_ah[p])
+                       ib_destroy_ah(dev->sm_ah[p]);
+       }
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c
new file mode 100644 (file)
index 0000000..4d71ae8
--- /dev/null
@@ -0,0 +1,1126 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_main.c 1396 2004-12-28 04:10:27Z roland $
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include "mthca_dev.h"
+#include "mthca_config_reg.h"
+#include "mthca_cmd.h"
+#include "mthca_profile.h"
+#include "mthca_memfree.h"
+
+MODULE_AUTHOR("Roland Dreier");
+MODULE_DESCRIPTION("Mellanox InfiniBand HCA low-level driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(DRV_VERSION);
+
+#ifdef CONFIG_PCI_MSI
+
+static int msi_x = 0;
+module_param(msi_x, int, 0444);
+MODULE_PARM_DESC(msi_x, "attempt to use MSI-X if nonzero");
+
+static int msi = 0;
+module_param(msi, int, 0444);
+MODULE_PARM_DESC(msi, "attempt to use MSI if nonzero");
+
+#else /* CONFIG_PCI_MSI */
+
+#define msi_x (0)
+#define msi   (0)
+
+#endif /* CONFIG_PCI_MSI */
+
+static const char mthca_version[] __devinitdata =
+       "ib_mthca: Mellanox InfiniBand HCA driver v"
+       DRV_VERSION " (" DRV_RELDATE ")\n";
+
+static struct mthca_profile default_profile = {
+       .num_qp     = 1 << 16,
+       .rdb_per_qp = 4,
+       .num_cq     = 1 << 16,
+       .num_mcg    = 1 << 13,
+       .num_mpt    = 1 << 17,
+       .num_mtt    = 1 << 20,
+       .num_udav   = 1 << 15,  /* Tavor only */
+       .uarc_size  = 1 << 18,  /* Arbel only */
+};
+
+static int __devinit mthca_tune_pci(struct mthca_dev *mdev)
+{
+       int cap;
+       u16 val;
+
+       /* First try to max out Read Byte Count */
+       cap = pci_find_capability(mdev->pdev, PCI_CAP_ID_PCIX);
+       if (cap) {
+               if (pci_read_config_word(mdev->pdev, cap + PCI_X_CMD, &val)) {
+                       mthca_err(mdev, "Couldn't read PCI-X command register, "
+                                 "aborting.\n");
+                       return -ENODEV;
+               }
+               val = (val & ~PCI_X_CMD_MAX_READ) | (3 << 2);
+               if (pci_write_config_word(mdev->pdev, cap + PCI_X_CMD, val)) {
+                       mthca_err(mdev, "Couldn't write PCI-X command register, "
+                                 "aborting.\n");
+                       return -ENODEV;
+               }
+       } else if (mdev->hca_type == TAVOR)
+               mthca_info(mdev, "No PCI-X capability, not setting RBC.\n");
+
+       cap = pci_find_capability(mdev->pdev, PCI_CAP_ID_EXP);
+       if (cap) {
+               if (pci_read_config_word(mdev->pdev, cap + PCI_EXP_DEVCTL, &val)) {
+                       mthca_err(mdev, "Couldn't read PCI Express device control "
+                                 "register, aborting.\n");
+                       return -ENODEV;
+               }
+               val = (val & ~PCI_EXP_DEVCTL_READRQ) | (5 << 12);
+               if (pci_write_config_word(mdev->pdev, cap + PCI_EXP_DEVCTL, val)) {
+                       mthca_err(mdev, "Couldn't write PCI Express device control "
+                                 "register, aborting.\n");
+                       return -ENODEV;
+               }
+       } else if (mdev->hca_type == ARBEL_NATIVE ||
+                  mdev->hca_type == ARBEL_COMPAT)
+               mthca_info(mdev, "No PCI Express capability, "
+                          "not setting Max Read Request Size.\n");
+
+       return 0;
+}
+
+static int __devinit mthca_dev_lim(struct mthca_dev *mdev, struct mthca_dev_lim *dev_lim)
+{
+       int err;
+       u8 status;
+
+       err = mthca_QUERY_DEV_LIM(mdev, dev_lim, &status);
+       if (err) {
+               mthca_err(mdev, "QUERY_DEV_LIM command failed, aborting.\n");
+               return err;
+       }
+       if (status) {
+               mthca_err(mdev, "QUERY_DEV_LIM returned status 0x%02x, "
+                         "aborting.\n", status);
+               return -EINVAL;
+       }
+       if (dev_lim->min_page_sz > PAGE_SIZE) {
+               mthca_err(mdev, "HCA minimum page size of %d bigger than "
+                         "kernel PAGE_SIZE of %ld, aborting.\n",
+                         dev_lim->min_page_sz, PAGE_SIZE);
+               return -ENODEV;
+       }
+       if (dev_lim->num_ports > MTHCA_MAX_PORTS) {
+               mthca_err(mdev, "HCA has %d ports, but we only support %d, "
+                         "aborting.\n",
+                         dev_lim->num_ports, MTHCA_MAX_PORTS);
+               return -ENODEV;
+       }
+
+       mdev->limits.num_ports          = dev_lim->num_ports;
+       mdev->limits.vl_cap             = dev_lim->max_vl;
+       mdev->limits.mtu_cap            = dev_lim->max_mtu;
+       mdev->limits.gid_table_len      = dev_lim->max_gids;
+       mdev->limits.pkey_table_len     = dev_lim->max_pkeys;
+       mdev->limits.local_ca_ack_delay = dev_lim->local_ca_ack_delay;
+       mdev->limits.max_sg             = dev_lim->max_sg;
+       mdev->limits.reserved_qps       = dev_lim->reserved_qps;
+       mdev->limits.reserved_srqs      = dev_lim->reserved_srqs;
+       mdev->limits.reserved_eecs      = dev_lim->reserved_eecs;
+       mdev->limits.reserved_cqs       = dev_lim->reserved_cqs;
+       mdev->limits.reserved_eqs       = dev_lim->reserved_eqs;
+       mdev->limits.reserved_mtts      = dev_lim->reserved_mtts;
+       mdev->limits.reserved_mrws      = dev_lim->reserved_mrws;
+       mdev->limits.reserved_uars      = dev_lim->reserved_uars;
+       mdev->limits.reserved_pds       = dev_lim->reserved_pds;
+
+       if (dev_lim->flags & DEV_LIM_FLAG_SRQ)
+               mdev->mthca_flags |= MTHCA_FLAG_SRQ;
+
+       return 0;
+}
+
+static int __devinit mthca_init_tavor(struct mthca_dev *mdev)
+{
+       u8 status;
+       int err;
+       struct mthca_dev_lim        dev_lim;
+       struct mthca_profile        profile;
+       struct mthca_init_hca_param init_hca;
+       struct mthca_adapter        adapter;
+
+       err = mthca_SYS_EN(mdev, &status);
+       if (err) {
+               mthca_err(mdev, "SYS_EN command failed, aborting.\n");
+               return err;
+       }
+       if (status) {
+               mthca_err(mdev, "SYS_EN returned status 0x%02x, "
+                         "aborting.\n", status);
+               return -EINVAL;
+       }
+
+       err = mthca_QUERY_FW(mdev, &status);
+       if (err) {
+               mthca_err(mdev, "QUERY_FW command failed, aborting.\n");
+               goto err_disable;
+       }
+       if (status) {
+               mthca_err(mdev, "QUERY_FW returned status 0x%02x, "
+                         "aborting.\n", status);
+               err = -EINVAL;
+               goto err_disable;
+       }
+       err = mthca_QUERY_DDR(mdev, &status);
+       if (err) {
+               mthca_err(mdev, "QUERY_DDR command failed, aborting.\n");
+               goto err_disable;
+       }
+       if (status) {
+               mthca_err(mdev, "QUERY_DDR returned status 0x%02x, "
+                         "aborting.\n", status);
+               err = -EINVAL;
+               goto err_disable;
+       }
+
+       err = mthca_dev_lim(mdev, &dev_lim);
+
+       profile = default_profile;
+       profile.num_uar   = dev_lim.uar_size / PAGE_SIZE;
+       profile.uarc_size = 0;
+
+       err = mthca_make_profile(mdev, &profile, &dev_lim, &init_hca);
+       if (err < 0)
+               goto err_disable;
+
+       err = mthca_INIT_HCA(mdev, &init_hca, &status);
+       if (err) {
+               mthca_err(mdev, "INIT_HCA command failed, aborting.\n");
+               goto err_disable;
+       }
+       if (status) {
+               mthca_err(mdev, "INIT_HCA returned status 0x%02x, "
+                         "aborting.\n", status);
+               err = -EINVAL;
+               goto err_disable;
+       }
+
+       err = mthca_QUERY_ADAPTER(mdev, &adapter, &status);
+       if (err) {
+               mthca_err(mdev, "QUERY_ADAPTER command failed, aborting.\n");
+               goto err_close;
+       }
+       if (status) {
+               mthca_err(mdev, "QUERY_ADAPTER returned status 0x%02x, "
+                         "aborting.\n", status);
+               err = -EINVAL;
+               goto err_close;
+       }
+
+       mdev->eq_table.inta_pin = adapter.inta_pin;
+       mdev->rev_id            = adapter.revision_id;
+
+       return 0;
+
+err_close:
+       mthca_CLOSE_HCA(mdev, 0, &status);
+
+err_disable:
+       mthca_SYS_DIS(mdev, &status);
+
+       return err;
+}
+
+static int __devinit mthca_load_fw(struct mthca_dev *mdev)
+{
+       u8 status;
+       int err;
+
+       /* FIXME: use HCA-attached memory for FW if present */
+
+       mdev->fw.arbel.fw_icm =
+               mthca_alloc_icm(mdev, mdev->fw.arbel.fw_pages,
+                               GFP_HIGHUSER | __GFP_NOWARN);
+       if (!mdev->fw.arbel.fw_icm) {
+               mthca_err(mdev, "Couldn't allocate FW area, aborting.\n");
+               return -ENOMEM;
+       }
+
+       err = mthca_MAP_FA(mdev, mdev->fw.arbel.fw_icm, &status);
+       if (err) {
+               mthca_err(mdev, "MAP_FA command failed, aborting.\n");
+               goto err_free;
+       }
+       if (status) {
+               mthca_err(mdev, "MAP_FA returned status 0x%02x, aborting.\n", status);
+               err = -EINVAL;
+               goto err_free;
+       }
+       err = mthca_RUN_FW(mdev, &status);
+       if (err) {
+               mthca_err(mdev, "RUN_FW command failed, aborting.\n");
+               goto err_unmap_fa;
+       }
+       if (status) {
+               mthca_err(mdev, "RUN_FW returned status 0x%02x, aborting.\n", status);
+               err = -EINVAL;
+               goto err_unmap_fa;
+       }
+
+       return 0;
+
+err_unmap_fa:
+       mthca_UNMAP_FA(mdev, &status);
+
+err_free:
+       mthca_free_icm(mdev, mdev->fw.arbel.fw_icm);
+       return err;
+}
+
+static int __devinit mthca_init_icm(struct mthca_dev *mdev,
+                                   struct mthca_dev_lim *dev_lim,
+                                   struct mthca_init_hca_param *init_hca,
+                                   u64 icm_size)
+{
+       u64 aux_pages;
+       u8 status;
+       int err;
+
+       err = mthca_SET_ICM_SIZE(mdev, icm_size, &aux_pages, &status);
+       if (err) {
+               mthca_err(mdev, "SET_ICM_SIZE command failed, aborting.\n");
+               return err;
+       }
+       if (status) {
+               mthca_err(mdev, "SET_ICM_SIZE returned status 0x%02x, "
+                         "aborting.\n", status);
+               return -EINVAL;
+       }
+
+       mthca_dbg(mdev, "%lld KB of HCA context requires %lld KB aux memory.\n",
+                 (unsigned long long) icm_size >> 10,
+                 (unsigned long long) aux_pages << 2);
+
+       mdev->fw.arbel.aux_icm = mthca_alloc_icm(mdev, aux_pages,
+                                                GFP_HIGHUSER | __GFP_NOWARN);
+       if (!mdev->fw.arbel.aux_icm) {
+               mthca_err(mdev, "Couldn't allocate aux memory, aborting.\n");
+               return -ENOMEM;
+       }
+
+       err = mthca_MAP_ICM_AUX(mdev, mdev->fw.arbel.aux_icm, &status);
+       if (err) {
+               mthca_err(mdev, "MAP_ICM_AUX command failed, aborting.\n");
+               goto err_free_aux;
+       }
+       if (status) {
+               mthca_err(mdev, "MAP_ICM_AUX returned status 0x%02x, aborting.\n", status);
+               err = -EINVAL;
+               goto err_free_aux;
+       }
+
+       err = mthca_map_eq_icm(mdev, init_hca->eqc_base);
+       if (err) {
+               mthca_err(mdev, "Failed to map EQ context memory, aborting.\n");
+               goto err_unmap_aux;
+       }
+
+       mdev->mr_table.mtt_table = mthca_alloc_icm_table(mdev, init_hca->mtt_base,
+                                                        mdev->limits.num_mtt_segs *
+                                                        init_hca->mtt_seg_sz,
+                                                        mdev->limits.reserved_mtts *
+                                                        init_hca->mtt_seg_sz, 1);
+       if (!mdev->mr_table.mtt_table) {
+               mthca_err(mdev, "Failed to map MTT context memory, aborting.\n");
+               err = -ENOMEM;
+               goto err_unmap_eq;
+       }
+
+       mdev->mr_table.mpt_table = mthca_alloc_icm_table(mdev, init_hca->mpt_base,
+                                                        mdev->limits.num_mpts *
+                                                        dev_lim->mpt_entry_sz,
+                                                        mdev->limits.reserved_mrws *
+                                                        dev_lim->mpt_entry_sz, 1);
+       if (!mdev->mr_table.mpt_table) {
+               mthca_err(mdev, "Failed to map MPT context memory, aborting.\n");
+               err = -ENOMEM;
+               goto err_unmap_mtt;
+       }
+
+       mdev->qp_table.qp_table = mthca_alloc_icm_table(mdev, init_hca->qpc_base,
+                                                       mdev->limits.num_qps *
+                                                       dev_lim->qpc_entry_sz,
+                                                       mdev->limits.reserved_qps *
+                                                       dev_lim->qpc_entry_sz, 1);
+       if (!mdev->qp_table.qp_table) {
+               mthca_err(mdev, "Failed to map QP context memory, aborting.\n");
+               err = -ENOMEM;
+               goto err_unmap_mpt;
+       }
+
+       mdev->qp_table.eqp_table = mthca_alloc_icm_table(mdev, init_hca->eqpc_base,
+                                                        mdev->limits.num_qps *
+                                                        dev_lim->eqpc_entry_sz,
+                                                        mdev->limits.reserved_qps *
+                                                        dev_lim->eqpc_entry_sz, 1);
+       if (!mdev->qp_table.eqp_table) {
+               mthca_err(mdev, "Failed to map EQP context memory, aborting.\n");
+               err = -ENOMEM;
+               goto err_unmap_qp;
+       }
+
+       mdev->cq_table.table = mthca_alloc_icm_table(mdev, init_hca->cqc_base,
+                                                    mdev->limits.num_cqs *
+                                                    dev_lim->cqc_entry_sz,
+                                                    mdev->limits.reserved_cqs *
+                                                    dev_lim->cqc_entry_sz, 1);
+       if (!mdev->cq_table.table) {
+               mthca_err(mdev, "Failed to map CQ context memory, aborting.\n");
+               err = -ENOMEM;
+               goto err_unmap_eqp;
+       }
+
+       return 0;
+
+err_unmap_eqp:
+       mthca_free_icm_table(mdev, mdev->qp_table.eqp_table);
+
+err_unmap_qp:
+       mthca_free_icm_table(mdev, mdev->qp_table.qp_table);
+
+err_unmap_mpt:
+       mthca_free_icm_table(mdev, mdev->mr_table.mpt_table);
+
+err_unmap_mtt:
+       mthca_free_icm_table(mdev, mdev->mr_table.mtt_table);
+
+err_unmap_eq:
+       mthca_unmap_eq_icm(mdev);
+
+err_unmap_aux:
+       mthca_UNMAP_ICM_AUX(mdev, &status);
+
+err_free_aux:
+       mthca_free_icm(mdev, mdev->fw.arbel.aux_icm);
+
+       return err;
+}
+
+static int __devinit mthca_init_arbel(struct mthca_dev *mdev)
+{
+       struct mthca_dev_lim        dev_lim;
+       struct mthca_profile        profile;
+       struct mthca_init_hca_param init_hca;
+       struct mthca_adapter        adapter;
+       u64 icm_size;
+       u8 status;
+       int err;
+
+       err = mthca_QUERY_FW(mdev, &status);
+       if (err) {
+               mthca_err(mdev, "QUERY_FW command failed, aborting.\n");
+               return err;
+       }
+       if (status) {
+               mthca_err(mdev, "QUERY_FW returned status 0x%02x, "
+                         "aborting.\n", status);
+               return -EINVAL;
+       }
+
+       err = mthca_ENABLE_LAM(mdev, &status);
+       if (err) {
+               mthca_err(mdev, "ENABLE_LAM command failed, aborting.\n");
+               return err;
+       }
+       if (status == MTHCA_CMD_STAT_LAM_NOT_PRE) {
+               mthca_dbg(mdev, "No HCA-attached memory (running in MemFree mode)\n");
+               mdev->mthca_flags |= MTHCA_FLAG_NO_LAM;
+       } else if (status) {
+               mthca_err(mdev, "ENABLE_LAM returned status 0x%02x, "
+                         "aborting.\n", status);
+               return -EINVAL;
+       }
+
+       err = mthca_load_fw(mdev);
+       if (err) {
+               mthca_err(mdev, "Failed to start FW, aborting.\n");
+               goto err_disable;
+       }
+
+       err = mthca_dev_lim(mdev, &dev_lim);
+       if (err) {
+               mthca_err(mdev, "QUERY_DEV_LIM command failed, aborting.\n");
+               goto err_stop_fw;
+       }
+
+       profile = default_profile;
+       profile.num_uar  = dev_lim.uar_size / PAGE_SIZE;
+       profile.num_udav = 0;
+
+       icm_size = mthca_make_profile(mdev, &profile, &dev_lim, &init_hca);
+       if ((int) icm_size < 0) {
+               err = icm_size;
+               goto err_stop_fw;
+       }
+
+       err = mthca_init_icm(mdev, &dev_lim, &init_hca, icm_size);
+       if (err)
+               goto err_stop_fw;
+
+       err = mthca_INIT_HCA(mdev, &init_hca, &status);
+       if (err) {
+               mthca_err(mdev, "INIT_HCA command failed, aborting.\n");
+               goto err_free_icm;
+       }
+       if (status) {
+               mthca_err(mdev, "INIT_HCA returned status 0x%02x, "
+                         "aborting.\n", status);
+               err = -EINVAL;
+               goto err_free_icm;
+       }
+
+       err = mthca_QUERY_ADAPTER(mdev, &adapter, &status);
+       if (err) {
+               mthca_err(mdev, "QUERY_ADAPTER command failed, aborting.\n");
+               goto err_free_icm;
+       }
+       if (status) {
+               mthca_err(mdev, "QUERY_ADAPTER returned status 0x%02x, "
+                         "aborting.\n", status);
+               err = -EINVAL;
+               goto err_free_icm;
+       }
+
+       mdev->eq_table.inta_pin = adapter.inta_pin;
+       mdev->rev_id            = adapter.revision_id;
+
+       return 0;
+
+err_free_icm:
+       mthca_free_icm_table(mdev, mdev->cq_table.table);
+       mthca_free_icm_table(mdev, mdev->qp_table.eqp_table);
+       mthca_free_icm_table(mdev, mdev->qp_table.qp_table);
+       mthca_free_icm_table(mdev, mdev->mr_table.mpt_table);
+       mthca_free_icm_table(mdev, mdev->mr_table.mtt_table);
+       mthca_unmap_eq_icm(mdev);
+
+       mthca_UNMAP_ICM_AUX(mdev, &status);
+       mthca_free_icm(mdev, mdev->fw.arbel.aux_icm);
+
+err_stop_fw:
+       mthca_UNMAP_FA(mdev, &status);
+       mthca_free_icm(mdev, mdev->fw.arbel.fw_icm);
+
+err_disable:
+       if (!(mdev->mthca_flags & MTHCA_FLAG_NO_LAM))
+               mthca_DISABLE_LAM(mdev, &status);
+
+       return err;
+}
+
+static int __devinit mthca_init_hca(struct mthca_dev *mdev)
+{
+       if (mdev->hca_type == ARBEL_NATIVE)
+               return mthca_init_arbel(mdev);
+       else
+               return mthca_init_tavor(mdev);
+}
+
+static int __devinit mthca_setup_hca(struct mthca_dev *dev)
+{
+       int err;
+       u8 status;
+
+       MTHCA_INIT_DOORBELL_LOCK(&dev->doorbell_lock);
+
+       err = mthca_init_pd_table(dev);
+       if (err) {
+               mthca_err(dev, "Failed to initialize "
+                         "protection domain table, aborting.\n");
+               return err;
+       }
+
+       err = mthca_init_mr_table(dev);
+       if (err) {
+               mthca_err(dev, "Failed to initialize "
+                         "memory region table, aborting.\n");
+               goto err_pd_table_free;
+       }
+
+       err = mthca_pd_alloc(dev, &dev->driver_pd);
+       if (err) {
+               mthca_err(dev, "Failed to create driver PD, "
+                         "aborting.\n");
+               goto err_mr_table_free;
+       }
+
+       if (dev->hca_type == ARBEL_NATIVE) {
+               mthca_warn(dev, "Sorry, native MT25208 mode support is not done, "
+                          "aborting.\n");
+               err = -ENODEV;
+               goto err_pd_free;
+       }
+
+       err = mthca_init_eq_table(dev);
+       if (err) {
+               mthca_err(dev, "Failed to initialize "
+                         "event queue table, aborting.\n");
+               goto err_pd_free;
+       }
+
+       err = mthca_cmd_use_events(dev);
+       if (err) {
+               mthca_err(dev, "Failed to switch to event-driven "
+                         "firmware commands, aborting.\n");
+               goto err_eq_table_free;
+       }
+
+       err = mthca_NOP(dev, &status);
+       if (err || status) {
+               mthca_err(dev, "NOP command failed to generate interrupt, aborting.\n");
+               if (dev->mthca_flags & (MTHCA_FLAG_MSI | MTHCA_FLAG_MSI_X))
+                       mthca_err(dev, "Try again with MSI/MSI-X disabled.\n");
+               else
+                       mthca_err(dev, "BIOS or ACPI interrupt routing problem?\n");
+
+               goto err_cmd_poll;
+       } else
+               mthca_dbg(dev, "NOP command IRQ test passed\n");
+
+       err = mthca_init_cq_table(dev);
+       if (err) {
+               mthca_err(dev, "Failed to initialize "
+                         "completion queue table, aborting.\n");
+               goto err_cmd_poll;
+       }
+
+       err = mthca_init_qp_table(dev);
+       if (err) {
+               mthca_err(dev, "Failed to initialize "
+                         "queue pair table, aborting.\n");
+               goto err_cq_table_free;
+       }
+
+       err = mthca_init_av_table(dev);
+       if (err) {
+               mthca_err(dev, "Failed to initialize "
+                         "address vector table, aborting.\n");
+               goto err_qp_table_free;
+       }
+
+       err = mthca_init_mcg_table(dev);
+       if (err) {
+               mthca_err(dev, "Failed to initialize "
+                         "multicast group table, aborting.\n");
+               goto err_av_table_free;
+       }
+
+       return 0;
+
+err_av_table_free:
+       mthca_cleanup_av_table(dev);
+
+err_qp_table_free:
+       mthca_cleanup_qp_table(dev);
+
+err_cq_table_free:
+       mthca_cleanup_cq_table(dev);
+
+err_cmd_poll:
+       mthca_cmd_use_polling(dev);
+
+err_eq_table_free:
+       mthca_cleanup_eq_table(dev);
+
+err_pd_free:
+       mthca_pd_free(dev, &dev->driver_pd);
+
+err_mr_table_free:
+       mthca_cleanup_mr_table(dev);
+
+err_pd_table_free:
+       mthca_cleanup_pd_table(dev);
+       return err;
+}
+
+static int __devinit mthca_request_regions(struct pci_dev *pdev,
+                                          int ddr_hidden)
+{
+       int err;
+
+       /*
+        * We request our first BAR in two chunks, since the MSI-X
+        * vector table is right in the middle.
+        *
+        * This is why we can't just use pci_request_regions() -- if
+        * we did then setting up MSI-X would fail, since the PCI core
+        * wants to do request_mem_region on the MSI-X vector table.
+        */
+       if (!request_mem_region(pci_resource_start(pdev, 0) +
+                               MTHCA_HCR_BASE,
+                               MTHCA_HCR_SIZE,
+                               DRV_NAME)) {
+               err = -EBUSY;
+               goto err_hcr_failed;
+       }
+
+       if (!request_mem_region(pci_resource_start(pdev, 0) +
+                               MTHCA_ECR_BASE,
+                               MTHCA_MAP_ECR_SIZE,
+                               DRV_NAME)) {
+               err = -EBUSY;
+               goto err_ecr_failed;
+       }
+
+       if (!request_mem_region(pci_resource_start(pdev, 0) +
+                               MTHCA_CLR_INT_BASE,
+                               MTHCA_CLR_INT_SIZE,
+                               DRV_NAME)) {
+               err = -EBUSY;
+               goto err_int_failed;
+       }
+
+
+       err = pci_request_region(pdev, 2, DRV_NAME);
+       if (err)
+               goto err_bar2_failed;
+
+       if (!ddr_hidden) {
+               err = pci_request_region(pdev, 4, DRV_NAME);
+               if (err)
+                       goto err_bar4_failed;
+       }
+
+       return 0;
+
+err_bar4_failed:
+
+       pci_release_region(pdev, 2);
+err_bar2_failed:
+
+       release_mem_region(pci_resource_start(pdev, 0) +
+                          MTHCA_CLR_INT_BASE,
+                          MTHCA_CLR_INT_SIZE);
+err_int_failed:
+
+       release_mem_region(pci_resource_start(pdev, 0) +
+                          MTHCA_ECR_BASE,
+                          MTHCA_MAP_ECR_SIZE);
+err_ecr_failed:
+
+       release_mem_region(pci_resource_start(pdev, 0) +
+                          MTHCA_HCR_BASE,
+                          MTHCA_HCR_SIZE);
+err_hcr_failed:
+
+       return err;
+}
+
+static void mthca_release_regions(struct pci_dev *pdev,
+                                 int ddr_hidden)
+{
+       if (!ddr_hidden)
+               pci_release_region(pdev, 4);
+
+       pci_release_region(pdev, 2);
+
+       release_mem_region(pci_resource_start(pdev, 0) +
+                          MTHCA_CLR_INT_BASE,
+                          MTHCA_CLR_INT_SIZE);
+
+       release_mem_region(pci_resource_start(pdev, 0) +
+                          MTHCA_ECR_BASE,
+                          MTHCA_MAP_ECR_SIZE);
+
+       release_mem_region(pci_resource_start(pdev, 0) +
+                          MTHCA_HCR_BASE,
+                          MTHCA_HCR_SIZE);
+}
+
+static int __devinit mthca_enable_msi_x(struct mthca_dev *mdev)
+{
+       struct msix_entry entries[3];
+       int err;
+
+       entries[0].entry = 0;
+       entries[1].entry = 1;
+       entries[2].entry = 2;
+
+       err = pci_enable_msix(mdev->pdev, entries, ARRAY_SIZE(entries));
+       if (err) {
+               if (err > 0)
+                       mthca_info(mdev, "Only %d MSI-X vectors available, "
+                                  "not using MSI-X\n", err);
+               return err;
+       }
+
+       mdev->eq_table.eq[MTHCA_EQ_COMP ].msi_x_vector = entries[0].vector;
+       mdev->eq_table.eq[MTHCA_EQ_ASYNC].msi_x_vector = entries[1].vector;
+       mdev->eq_table.eq[MTHCA_EQ_CMD  ].msi_x_vector = entries[2].vector;
+
+       return 0;
+}
+
+static void mthca_close_hca(struct mthca_dev *mdev)
+{
+       u8 status;
+
+       mthca_CLOSE_HCA(mdev, 0, &status);
+
+       if (mdev->hca_type == ARBEL_NATIVE) {
+               mthca_free_icm_table(mdev, mdev->cq_table.table);
+               mthca_free_icm_table(mdev, mdev->qp_table.eqp_table);
+               mthca_free_icm_table(mdev, mdev->qp_table.qp_table);
+               mthca_free_icm_table(mdev, mdev->mr_table.mpt_table);
+               mthca_free_icm_table(mdev, mdev->mr_table.mtt_table);
+               mthca_unmap_eq_icm(mdev);
+
+               mthca_UNMAP_ICM_AUX(mdev, &status);
+               mthca_free_icm(mdev, mdev->fw.arbel.aux_icm);
+
+               mthca_UNMAP_FA(mdev, &status);
+               mthca_free_icm(mdev, mdev->fw.arbel.fw_icm);
+
+               if (!(mdev->mthca_flags & MTHCA_FLAG_NO_LAM))
+                       mthca_DISABLE_LAM(mdev, &status);
+       } else
+               mthca_SYS_DIS(mdev, &status);
+}
+
+static int __devinit mthca_init_one(struct pci_dev *pdev,
+                                   const struct pci_device_id *id)
+{
+       static int mthca_version_printed = 0;
+       int ddr_hidden = 0;
+       int err;
+       unsigned long mthca_base;
+       struct mthca_dev *mdev;
+
+       if (!mthca_version_printed) {
+               printk(KERN_INFO "%s", mthca_version);
+               ++mthca_version_printed;
+       }
+
+       printk(KERN_INFO PFX "Initializing %s (%s)\n",
+              pci_pretty_name(pdev), pci_name(pdev));
+
+       err = pci_enable_device(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "Cannot enable PCI device, "
+                       "aborting.\n");
+               return err;
+       }
+
+       /*
+        * Check for BARs.  We expect 0: 1MB, 2: 8MB, 4: DDR (may not
+        * be present)
+        */
+       if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) ||
+           pci_resource_len(pdev, 0) != 1 << 20) {
+               dev_err(&pdev->dev, "Missing DCS, aborting.");
+               err = -ENODEV;
+               goto err_disable_pdev;
+       }
+       if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM) ||
+           pci_resource_len(pdev, 2) != 1 << 23) {
+               dev_err(&pdev->dev, "Missing UAR, aborting.");
+               err = -ENODEV;
+               goto err_disable_pdev;
+       }
+       if (!(pci_resource_flags(pdev, 4) & IORESOURCE_MEM))
+               ddr_hidden = 1;
+
+       err = mthca_request_regions(pdev, ddr_hidden);
+       if (err) {
+               dev_err(&pdev->dev, "Cannot obtain PCI resources, "
+                       "aborting.\n");
+               goto err_disable_pdev;
+       }
+
+       pci_set_master(pdev);
+
+       err = pci_set_dma_mask(pdev, DMA_64BIT_MASK);
+       if (err) {
+               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask.\n");
+               err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+               if (err) {
+                       dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting.\n");
+                       goto err_free_res;
+               }
+       }
+       err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK);
+       if (err) {
+               dev_warn(&pdev->dev, "Warning: couldn't set 64-bit "
+                        "consistent PCI DMA mask.\n");
+               err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+               if (err) {
+                       dev_err(&pdev->dev, "Can't set consistent PCI DMA mask, "
+                               "aborting.\n");
+                       goto err_free_res;
+               }
+       }
+
+       mdev = (struct mthca_dev *) ib_alloc_device(sizeof *mdev);
+       if (!mdev) {
+               dev_err(&pdev->dev, "Device struct alloc failed, "
+                       "aborting.\n");
+               err = -ENOMEM;
+               goto err_free_res;
+       }
+
+       mdev->pdev     = pdev;
+       mdev->hca_type = id->driver_data;
+
+       if (ddr_hidden)
+               mdev->mthca_flags |= MTHCA_FLAG_DDR_HIDDEN;
+
+       /*
+        * Now reset the HCA before we touch the PCI capabilities or
+        * attempt a firmware command, since a boot ROM may have left
+        * the HCA in an undefined state.
+        */
+       err = mthca_reset(mdev);
+       if (err) {
+               mthca_err(mdev, "Failed to reset HCA, aborting.\n");
+               goto err_free_dev;
+       }
+
+       if (msi_x && !mthca_enable_msi_x(mdev))
+               mdev->mthca_flags |= MTHCA_FLAG_MSI_X;
+       if (msi && !(mdev->mthca_flags & MTHCA_FLAG_MSI_X) &&
+           !pci_enable_msi(pdev))
+               mdev->mthca_flags |= MTHCA_FLAG_MSI;
+
+       sema_init(&mdev->cmd.hcr_sem, 1);
+       sema_init(&mdev->cmd.poll_sem, 1);
+       mdev->cmd.use_events = 0;
+
+       mthca_base = pci_resource_start(pdev, 0);
+       mdev->hcr = ioremap(mthca_base + MTHCA_HCR_BASE, MTHCA_HCR_SIZE);
+       if (!mdev->hcr) {
+               mthca_err(mdev, "Couldn't map command register, "
+                         "aborting.\n");
+               err = -ENOMEM;
+               goto err_free_dev;
+       }
+
+       mdev->clr_base = ioremap(mthca_base + MTHCA_CLR_INT_BASE,
+                                MTHCA_CLR_INT_SIZE);
+       if (!mdev->clr_base) {
+               mthca_err(mdev, "Couldn't map interrupt clear register, "
+                         "aborting.\n");
+               err = -ENOMEM;
+               goto err_iounmap;
+       }
+
+       mdev->ecr_base = ioremap(mthca_base + MTHCA_ECR_BASE,
+                                MTHCA_ECR_SIZE + MTHCA_ECR_CLR_SIZE);
+       if (!mdev->ecr_base) {
+               mthca_err(mdev, "Couldn't map ecr register, "
+                         "aborting.\n");
+               err = -ENOMEM;
+               goto err_iounmap_clr;
+       }
+
+       mthca_base = pci_resource_start(pdev, 2);
+       mdev->kar = ioremap(mthca_base + PAGE_SIZE * MTHCA_KAR_PAGE, PAGE_SIZE);
+       if (!mdev->kar) {
+               mthca_err(mdev, "Couldn't map kernel access region, "
+                         "aborting.\n");
+               err = -ENOMEM;
+               goto err_iounmap_ecr;
+       }
+
+       err = mthca_tune_pci(mdev);
+       if (err)
+               goto err_iounmap_kar;
+
+       err = mthca_init_hca(mdev);
+       if (err)
+               goto err_iounmap_kar;
+
+       err = mthca_setup_hca(mdev);
+       if (err)
+               goto err_close;
+
+       err = mthca_register_device(mdev);
+       if (err)
+               goto err_cleanup;
+
+       err = mthca_create_agents(mdev);
+       if (err)
+               goto err_unregister;
+
+       pci_set_drvdata(pdev, mdev);
+
+       return 0;
+
+err_unregister:
+       mthca_unregister_device(mdev);
+
+err_cleanup:
+       mthca_cleanup_mcg_table(mdev);
+       mthca_cleanup_av_table(mdev);
+       mthca_cleanup_qp_table(mdev);
+       mthca_cleanup_cq_table(mdev);
+       mthca_cmd_use_polling(mdev);
+       mthca_cleanup_eq_table(mdev);
+
+       mthca_pd_free(mdev, &mdev->driver_pd);
+
+       mthca_cleanup_mr_table(mdev);
+       mthca_cleanup_pd_table(mdev);
+
+err_close:
+       mthca_close_hca(mdev);
+
+err_iounmap_kar:
+       iounmap(mdev->kar);
+
+err_iounmap_ecr:
+       iounmap(mdev->ecr_base);
+
+err_iounmap_clr:
+       iounmap(mdev->clr_base);
+
+err_iounmap:
+       iounmap(mdev->hcr);
+
+err_free_dev:
+       if (mdev->mthca_flags & MTHCA_FLAG_MSI_X)
+               pci_disable_msix(pdev);
+       if (mdev->mthca_flags & MTHCA_FLAG_MSI)
+               pci_disable_msi(pdev);
+
+       ib_dealloc_device(&mdev->ib_dev);
+
+err_free_res:
+       mthca_release_regions(pdev, ddr_hidden);
+
+err_disable_pdev:
+       pci_disable_device(pdev);
+       pci_set_drvdata(pdev, NULL);
+       return err;
+}
+
+static void __devexit mthca_remove_one(struct pci_dev *pdev)
+{
+       struct mthca_dev *mdev = pci_get_drvdata(pdev);
+       u8 status;
+       int p;
+
+       if (mdev) {
+               mthca_free_agents(mdev);
+               mthca_unregister_device(mdev);
+
+               for (p = 1; p <= mdev->limits.num_ports; ++p)
+                       mthca_CLOSE_IB(mdev, p, &status);
+
+               mthca_cleanup_mcg_table(mdev);
+               mthca_cleanup_av_table(mdev);
+               mthca_cleanup_qp_table(mdev);
+               mthca_cleanup_cq_table(mdev);
+               mthca_cmd_use_polling(mdev);
+               mthca_cleanup_eq_table(mdev);
+
+               mthca_pd_free(mdev, &mdev->driver_pd);
+
+               mthca_cleanup_mr_table(mdev);
+               mthca_cleanup_pd_table(mdev);
+
+               mthca_close_hca(mdev);
+
+               iounmap(mdev->hcr);
+               iounmap(mdev->ecr_base);
+               iounmap(mdev->clr_base);
+
+               if (mdev->mthca_flags & MTHCA_FLAG_MSI_X)
+                       pci_disable_msix(pdev);
+               if (mdev->mthca_flags & MTHCA_FLAG_MSI)
+                       pci_disable_msi(pdev);
+
+               ib_dealloc_device(&mdev->ib_dev);
+               mthca_release_regions(pdev, mdev->mthca_flags &
+                                     MTHCA_FLAG_DDR_HIDDEN);
+               pci_disable_device(pdev);
+               pci_set_drvdata(pdev, NULL);
+       }
+}
+
+static struct pci_device_id mthca_pci_table[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_TAVOR),
+         .driver_data = TAVOR },
+       { PCI_DEVICE(PCI_VENDOR_ID_TOPSPIN, PCI_DEVICE_ID_MELLANOX_TAVOR),
+         .driver_data = TAVOR },
+       { PCI_DEVICE(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT),
+         .driver_data = ARBEL_COMPAT },
+       { PCI_DEVICE(PCI_VENDOR_ID_TOPSPIN, PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT),
+         .driver_data = ARBEL_COMPAT },
+       { PCI_DEVICE(PCI_VENDOR_ID_MELLANOX, PCI_DEVICE_ID_MELLANOX_ARBEL),
+         .driver_data = ARBEL_NATIVE },
+       { PCI_DEVICE(PCI_VENDOR_ID_TOPSPIN, PCI_DEVICE_ID_MELLANOX_ARBEL),
+         .driver_data = ARBEL_NATIVE },
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, mthca_pci_table);
+
+static struct pci_driver mthca_driver = {
+       .name           = "ib_mthca",
+       .id_table       = mthca_pci_table,
+       .probe          = mthca_init_one,
+       .remove         = __devexit_p(mthca_remove_one)
+};
+
+static int __init mthca_init(void)
+{
+       int ret;
+
+       ret = pci_register_driver(&mthca_driver);
+       return ret < 0 ? ret : 0;
+}
+
+static void __exit mthca_cleanup(void)
+{
+       pci_unregister_driver(&mthca_driver);
+}
+
+module_init(mthca_init);
+module_exit(mthca_cleanup);
diff --git a/drivers/infiniband/hw/mthca/mthca_mcg.c b/drivers/infiniband/hw/mthca/mthca_mcg.c
new file mode 100644 (file)
index 0000000..70a6553
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_mcg.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/init.h>
+
+#include "mthca_dev.h"
+#include "mthca_cmd.h"
+
+enum {
+       MTHCA_QP_PER_MGM = 4 * (MTHCA_MGM_ENTRY_SIZE / 16 - 2)
+};
+
+struct mthca_mgm {
+       u32 next_gid_index;
+       u32 reserved[3];
+       u8  gid[16];
+       u32 qp[MTHCA_QP_PER_MGM];
+};
+
+static const u8 zero_gid[16];  /* automatically initialized to 0 */
+
+/*
+ * Caller must hold MCG table semaphore.  gid and mgm parameters must
+ * be properly aligned for command interface.
+ *
+ *  Returns 0 unless a firmware command error occurs.
+ *
+ * If GID is found in MGM or MGM is empty, *index = *hash, *prev = -1
+ * and *mgm holds MGM entry.
+ *
+ * if GID is found in AMGM, *index = index in AMGM, *prev = index of
+ * previous entry in hash chain and *mgm holds AMGM entry.
+ *
+ * If no AMGM exists for given gid, *index = -1, *prev = index of last
+ * entry in hash chain and *mgm holds end of hash chain.
+ */
+static int find_mgm(struct mthca_dev *dev,
+                   u8 *gid, struct mthca_mgm *mgm,
+                   u16 *hash, int *prev, int *index)
+{
+       void *mailbox;
+       u8 *mgid;
+       int err;
+       u8 status;
+
+       mailbox = kmalloc(16 + MTHCA_CMD_MAILBOX_EXTRA, GFP_KERNEL);
+       if (!mailbox)
+               return -ENOMEM;
+       mgid = MAILBOX_ALIGN(mailbox);
+
+       memcpy(mgid, gid, 16);
+
+       err = mthca_MGID_HASH(dev, mgid, hash, &status);
+       if (err)
+               goto out;
+       if (status) {
+               mthca_err(dev, "MGID_HASH returned status %02x\n", status);
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (0)
+               mthca_dbg(dev, "Hash for %04x:%04x:%04x:%04x:"
+                         "%04x:%04x:%04x:%04x is %04x\n",
+                         be16_to_cpu(((u16 *) gid)[0]), be16_to_cpu(((u16 *) gid)[1]),
+                         be16_to_cpu(((u16 *) gid)[2]), be16_to_cpu(((u16 *) gid)[3]),
+                         be16_to_cpu(((u16 *) gid)[4]), be16_to_cpu(((u16 *) gid)[5]),
+                         be16_to_cpu(((u16 *) gid)[6]), be16_to_cpu(((u16 *) gid)[7]),
+                         *hash);
+
+       *index = *hash;
+       *prev  = -1;
+
+       do {
+               err = mthca_READ_MGM(dev, *index, mgm, &status);
+               if (err)
+                       goto out;
+               if (status) {
+                       mthca_err(dev, "READ_MGM returned status %02x\n", status);
+                       return -EINVAL;
+               }
+
+               if (!memcmp(mgm->gid, zero_gid, 16)) {
+                       if (*index != *hash) {
+                               mthca_err(dev, "Found zero MGID in AMGM.\n");
+                               err = -EINVAL;
+                       }
+                       goto out;
+               }
+
+               if (!memcmp(mgm->gid, gid, 16))
+                       goto out;
+
+               *prev = *index;
+               *index = be32_to_cpu(mgm->next_gid_index) >> 5;
+       } while (*index);
+
+       *index = -1;
+
+ out:
+       kfree(mailbox);
+       return err;
+}
+
+int mthca_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+       struct mthca_dev *dev = to_mdev(ibqp->device);
+       void *mailbox;
+       struct mthca_mgm *mgm;
+       u16 hash;
+       int index, prev;
+       int link = 0;
+       int i;
+       int err;
+       u8 status;
+
+       mailbox = kmalloc(sizeof *mgm + MTHCA_CMD_MAILBOX_EXTRA, GFP_KERNEL);
+       if (!mailbox)
+               return -ENOMEM;
+       mgm = MAILBOX_ALIGN(mailbox);
+
+       if (down_interruptible(&dev->mcg_table.sem))
+               return -EINTR;
+
+       err = find_mgm(dev, gid->raw, mgm, &hash, &prev, &index);
+       if (err)
+               goto out;
+
+       if (index != -1) {
+               if (!memcmp(mgm->gid, zero_gid, 16))
+                       memcpy(mgm->gid, gid->raw, 16);
+       } else {
+               link = 1;
+
+               index = mthca_alloc(&dev->mcg_table.alloc);
+               if (index == -1) {
+                       mthca_err(dev, "No AMGM entries left\n");
+                       err = -ENOMEM;
+                       goto out;
+               }
+
+               err = mthca_READ_MGM(dev, index, mgm, &status);
+               if (err)
+                       goto out;
+               if (status) {
+                       mthca_err(dev, "READ_MGM returned status %02x\n", status);
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               memcpy(mgm->gid, gid->raw, 16);
+               mgm->next_gid_index = 0;
+       }
+
+       for (i = 0; i < MTHCA_QP_PER_MGM; ++i)
+               if (!(mgm->qp[i] & cpu_to_be32(1 << 31))) {
+                       mgm->qp[i] = cpu_to_be32(ibqp->qp_num | (1 << 31));
+                       break;
+               }
+
+       if (i == MTHCA_QP_PER_MGM) {
+               mthca_err(dev, "MGM at index %x is full.\n", index);
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = mthca_WRITE_MGM(dev, index, mgm, &status);
+       if (err)
+               goto out;
+       if (status) {
+               mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
+               err = -EINVAL;
+       }
+
+       if (!link)
+               goto out;
+
+       err = mthca_READ_MGM(dev, prev, mgm, &status);
+       if (err)
+               goto out;
+       if (status) {
+               mthca_err(dev, "READ_MGM returned status %02x\n", status);
+               err = -EINVAL;
+               goto out;
+       }
+
+       mgm->next_gid_index = cpu_to_be32(index << 5);
+
+       err = mthca_WRITE_MGM(dev, prev, mgm, &status);
+       if (err)
+               goto out;
+       if (status) {
+               mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
+               err = -EINVAL;
+       }
+
+ out:
+       up(&dev->mcg_table.sem);
+       kfree(mailbox);
+       return err;
+}
+
+int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
+{
+       struct mthca_dev *dev = to_mdev(ibqp->device);
+       void *mailbox;
+       struct mthca_mgm *mgm;
+       u16 hash;
+       int prev, index;
+       int i, loc;
+       int err;
+       u8 status;
+
+       mailbox = kmalloc(sizeof *mgm + MTHCA_CMD_MAILBOX_EXTRA, GFP_KERNEL);
+       if (!mailbox)
+               return -ENOMEM;
+       mgm = MAILBOX_ALIGN(mailbox);
+
+       if (down_interruptible(&dev->mcg_table.sem))
+               return -EINTR;
+
+       err = find_mgm(dev, gid->raw, mgm, &hash, &prev, &index);
+       if (err)
+               goto out;
+
+       if (index == -1) {
+               mthca_err(dev, "MGID %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
+                         "not found\n",
+                         be16_to_cpu(((u16 *) gid->raw)[0]),
+                         be16_to_cpu(((u16 *) gid->raw)[1]),
+                         be16_to_cpu(((u16 *) gid->raw)[2]),
+                         be16_to_cpu(((u16 *) gid->raw)[3]),
+                         be16_to_cpu(((u16 *) gid->raw)[4]),
+                         be16_to_cpu(((u16 *) gid->raw)[5]),
+                         be16_to_cpu(((u16 *) gid->raw)[6]),
+                         be16_to_cpu(((u16 *) gid->raw)[7]));
+               err = -EINVAL;
+               goto out;
+       }
+
+       for (loc = -1, i = 0; i < MTHCA_QP_PER_MGM; ++i) {
+               if (mgm->qp[i] == cpu_to_be32(ibqp->qp_num | (1 << 31)))
+                       loc = i;
+               if (!(mgm->qp[i] & cpu_to_be32(1 << 31)))
+                       break;
+       }
+
+       if (loc == -1) {
+               mthca_err(dev, "QP %06x not found in MGM\n", ibqp->qp_num);
+               err = -EINVAL;
+               goto out;
+       }
+
+       mgm->qp[loc]   = mgm->qp[i - 1];
+       mgm->qp[i - 1] = 0;
+
+       err = mthca_WRITE_MGM(dev, index, mgm, &status);
+       if (err)
+               goto out;
+       if (status) {
+               mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (i != 1)
+               goto out;
+
+       goto out;
+
+       if (prev == -1) {
+               /* Remove entry from MGM */
+               if (be32_to_cpu(mgm->next_gid_index) >> 5) {
+                       err = mthca_READ_MGM(dev,
+                                            be32_to_cpu(mgm->next_gid_index) >> 5,
+                                            mgm, &status);
+                       if (err)
+                               goto out;
+                       if (status) {
+                               mthca_err(dev, "READ_MGM returned status %02x\n",
+                                         status);
+                               err = -EINVAL;
+                               goto out;
+                       }
+               } else
+                       memset(mgm->gid, 0, 16);
+
+               err = mthca_WRITE_MGM(dev, index, mgm, &status);
+               if (err)
+                       goto out;
+               if (status) {
+                       mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
+                       err = -EINVAL;
+                       goto out;
+               }
+       } else {
+               /* Remove entry from AMGM */
+               index = be32_to_cpu(mgm->next_gid_index) >> 5;
+               err = mthca_READ_MGM(dev, prev, mgm, &status);
+               if (err)
+                       goto out;
+               if (status) {
+                       mthca_err(dev, "READ_MGM returned status %02x\n", status);
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               mgm->next_gid_index = cpu_to_be32(index << 5);
+
+               err = mthca_WRITE_MGM(dev, prev, mgm, &status);
+               if (err)
+                       goto out;
+               if (status) {
+                       mthca_err(dev, "WRITE_MGM returned status %02x\n", status);
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+ out:
+       up(&dev->mcg_table.sem);
+       kfree(mailbox);
+       return err;
+}
+
+int __devinit mthca_init_mcg_table(struct mthca_dev *dev)
+{
+       int err;
+
+       err = mthca_alloc_init(&dev->mcg_table.alloc,
+                              dev->limits.num_amgms,
+                              dev->limits.num_amgms - 1,
+                              0);
+       if (err)
+               return err;
+
+       init_MUTEX(&dev->mcg_table.sem);
+
+       return 0;
+}
+
+void __devexit mthca_cleanup_mcg_table(struct mthca_dev *dev)
+{
+       mthca_alloc_cleanup(&dev->mcg_table.alloc);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.c b/drivers/infiniband/hw/mthca/mthca_memfree.c
new file mode 100644 (file)
index 0000000..7071361
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id$
+ */
+
+#include "mthca_memfree.h"
+#include "mthca_dev.h"
+#include "mthca_cmd.h"
+
+/*
+ * We allocate in as big chunks as we can, up to a maximum of 256 KB
+ * per chunk.
+ */
+enum {
+       MTHCA_ICM_ALLOC_SIZE   = 1 << 18,
+       MTHCA_TABLE_CHUNK_SIZE = 1 << 18
+};
+
+void mthca_free_icm(struct mthca_dev *dev, struct mthca_icm *icm)
+{
+       struct mthca_icm_chunk *chunk, *tmp;
+       int i;
+
+       if (!icm)
+               return;
+
+       list_for_each_entry_safe(chunk, tmp, &icm->chunk_list, list) {
+               if (chunk->nsg > 0)
+                       pci_unmap_sg(dev->pdev, chunk->mem, chunk->npages,
+                                    PCI_DMA_BIDIRECTIONAL);
+
+               for (i = 0; i < chunk->npages; ++i)
+                       __free_pages(chunk->mem[i].page,
+                                    get_order(chunk->mem[i].length));
+
+               kfree(chunk);
+       }
+
+       kfree(icm);
+}
+
+struct mthca_icm *mthca_alloc_icm(struct mthca_dev *dev, int npages,
+                                 unsigned int gfp_mask)
+{
+       struct mthca_icm *icm;
+       struct mthca_icm_chunk *chunk = NULL;
+       int cur_order;
+
+       icm = kmalloc(sizeof *icm, gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
+       if (!icm)
+               return icm;
+
+       INIT_LIST_HEAD(&icm->chunk_list);
+
+       cur_order = get_order(MTHCA_ICM_ALLOC_SIZE);
+
+       while (npages > 0) {
+               if (!chunk) {
+                       chunk = kmalloc(sizeof *chunk,
+                                       gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
+                       if (!chunk)
+                               goto fail;
+
+                       chunk->npages = 0;
+                       chunk->nsg    = 0;
+                       list_add_tail(&chunk->list, &icm->chunk_list);
+               }
+
+               while (1 << cur_order > npages)
+                       --cur_order;
+
+               chunk->mem[chunk->npages].page = alloc_pages(gfp_mask, cur_order);
+               if (chunk->mem[chunk->npages].page) {
+                       chunk->mem[chunk->npages].length = PAGE_SIZE << cur_order;
+                       chunk->mem[chunk->npages].offset = 0;
+
+                       if (++chunk->npages == MTHCA_ICM_CHUNK_LEN) {
+                               chunk->nsg = pci_map_sg(dev->pdev, chunk->mem,
+                                                       chunk->npages,
+                                                       PCI_DMA_BIDIRECTIONAL);
+
+                               if (chunk->nsg <= 0)
+                                       goto fail;
+
+                               chunk = NULL;
+                       }
+
+                       npages -= 1 << cur_order;
+               } else {
+                       --cur_order;
+                       if (cur_order < 0)
+                               goto fail;
+               }
+       }
+
+       if (chunk) {
+               chunk->nsg = pci_map_sg(dev->pdev, chunk->mem,
+                                       chunk->npages,
+                                       PCI_DMA_BIDIRECTIONAL);
+
+               if (chunk->nsg <= 0)
+                       goto fail;
+       }
+
+       return icm;
+
+fail:
+       mthca_free_icm(dev, icm);
+       return NULL;
+}
+
+struct mthca_icm_table *mthca_alloc_icm_table(struct mthca_dev *dev,
+                                             u64 virt, unsigned size,
+                                             unsigned reserved,
+                                             int use_lowmem)
+{
+       struct mthca_icm_table *table;
+       int num_icm;
+       int i;
+       u8 status;
+
+       num_icm = size / MTHCA_TABLE_CHUNK_SIZE;
+
+       table = kmalloc(sizeof *table + num_icm * sizeof *table->icm, GFP_KERNEL);
+       if (!table)
+               return NULL;
+
+       table->virt    = virt;
+       table->num_icm = num_icm;
+       init_MUTEX(&table->sem);
+
+       for (i = 0; i < num_icm; ++i)
+               table->icm[i] = NULL;
+
+       for (i = 0; i < (reserved + MTHCA_TABLE_CHUNK_SIZE - 1) / MTHCA_TABLE_CHUNK_SIZE; ++i) {
+               table->icm[i] = mthca_alloc_icm(dev, MTHCA_TABLE_CHUNK_SIZE >> PAGE_SHIFT,
+                                               (use_lowmem ? GFP_KERNEL : GFP_HIGHUSER) |
+                                               __GFP_NOWARN);
+               if (!table->icm[i])
+                       goto err;
+               if (mthca_MAP_ICM(dev, table->icm[i], virt + i * MTHCA_TABLE_CHUNK_SIZE,
+                                 &status) || status) {
+                       mthca_free_icm(dev, table->icm[i]);
+                       table->icm[i] = NULL;
+                       goto err;
+               }
+       }
+
+       return table;
+
+err:
+       for (i = 0; i < num_icm; ++i)
+               if (table->icm[i]) {
+                       mthca_UNMAP_ICM(dev, virt + i * MTHCA_TABLE_CHUNK_SIZE,
+                                       MTHCA_TABLE_CHUNK_SIZE >> 12, &status);
+                       mthca_free_icm(dev, table->icm[i]);
+               }
+
+       kfree(table);
+
+       return NULL;
+}
+
+void mthca_free_icm_table(struct mthca_dev *dev, struct mthca_icm_table *table)
+{
+       int i;
+       u8 status;
+
+       for (i = 0; i < table->num_icm; ++i)
+               if (table->icm[i]) {
+                       mthca_UNMAP_ICM(dev, table->virt + i * MTHCA_TABLE_CHUNK_SIZE,
+                                       MTHCA_TABLE_CHUNK_SIZE >> 12, &status);
+                       mthca_free_icm(dev, table->icm[i]);
+               }
+
+       kfree(table);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.h b/drivers/infiniband/hw/mthca/mthca_memfree.h
new file mode 100644 (file)
index 0000000..b63ae66
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef MTHCA_MEMFREE_H
+#define MTHCA_MEMFREE_H
+
+#include <linux/list.h>
+#include <linux/pci.h>
+
+#include <asm/semaphore.h>
+
+#define MTHCA_ICM_CHUNK_LEN \
+       ((256 - sizeof (struct list_head) - 2 * sizeof (int)) /         \
+        (sizeof (struct scatterlist)))
+
+struct mthca_icm_chunk {
+       struct list_head   list;
+       int                npages;
+       int                nsg;
+       struct scatterlist mem[MTHCA_ICM_CHUNK_LEN];
+};
+
+struct mthca_icm {
+       struct list_head chunk_list;
+};
+
+struct mthca_icm_table {
+       u64               virt;
+       int               num_icm;
+       struct semaphore  sem;
+       struct mthca_icm *icm[0];
+};
+
+struct mthca_icm_iter {
+       struct mthca_icm       *icm;
+       struct mthca_icm_chunk *chunk;
+       int                     page_idx;
+};
+
+struct mthca_dev;
+
+struct mthca_icm *mthca_alloc_icm(struct mthca_dev *dev, int npages,
+                                 unsigned int gfp_mask);
+void mthca_free_icm(struct mthca_dev *dev, struct mthca_icm *icm);
+
+struct mthca_icm_table *mthca_alloc_icm_table(struct mthca_dev *dev,
+                                             u64 virt, unsigned size,
+                                             unsigned reserved,
+                                             int use_lowmem);
+void mthca_free_icm_table(struct mthca_dev *dev, struct mthca_icm_table *table);
+
+static inline void mthca_icm_first(struct mthca_icm *icm,
+                                  struct mthca_icm_iter *iter)
+{
+       iter->icm      = icm;
+       iter->chunk    = list_empty(&icm->chunk_list) ?
+               NULL : list_entry(icm->chunk_list.next,
+                                 struct mthca_icm_chunk, list);
+       iter->page_idx = 0;
+}
+
+static inline int mthca_icm_last(struct mthca_icm_iter *iter)
+{
+       return !iter->chunk;
+}
+
+static inline void mthca_icm_next(struct mthca_icm_iter *iter)
+{
+       if (++iter->page_idx >= iter->chunk->nsg) {
+               if (iter->chunk->list.next == &iter->icm->chunk_list) {
+                       iter->chunk = NULL;
+                       return;
+               }
+
+               iter->chunk = list_entry(iter->chunk->list.next,
+                                        struct mthca_icm_chunk, list);
+               iter->page_idx = 0;
+       }
+}
+
+static inline dma_addr_t mthca_icm_addr(struct mthca_icm_iter *iter)
+{
+       return sg_dma_address(&iter->chunk->mem[iter->page_idx]);
+}
+
+static inline unsigned long mthca_icm_size(struct mthca_icm_iter *iter)
+{
+       return sg_dma_len(&iter->chunk->mem[iter->page_idx]);
+}
+
+#endif /* MTHCA_MEMFREE_H */
diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c
new file mode 100644 (file)
index 0000000..b7cbd24
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_mr.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+
+#include "mthca_dev.h"
+#include "mthca_cmd.h"
+
+/*
+ * Must be packed because mtt_seg is 64 bits but only aligned to 32 bits.
+ */
+struct mthca_mpt_entry {
+       u32 flags;
+       u32 page_size;
+       u32 key;
+       u32 pd;
+       u64 start;
+       u64 length;
+       u32 lkey;
+       u32 window_count;
+       u32 window_count_limit;
+       u64 mtt_seg;
+       u32 reserved[3];
+} __attribute__((packed));
+
+#define MTHCA_MPT_FLAG_SW_OWNS       (0xfUL << 28)
+#define MTHCA_MPT_FLAG_MIO           (1 << 17)
+#define MTHCA_MPT_FLAG_BIND_ENABLE   (1 << 15)
+#define MTHCA_MPT_FLAG_PHYSICAL      (1 <<  9)
+#define MTHCA_MPT_FLAG_REGION        (1 <<  8)
+
+#define MTHCA_MTT_FLAG_PRESENT       1
+
+/*
+ * Buddy allocator for MTT segments (currently not very efficient
+ * since it doesn't keep a free list and just searches linearly
+ * through the bitmaps)
+ */
+
+static u32 mthca_alloc_mtt(struct mthca_dev *dev, int order)
+{
+       int o;
+       int m;
+       u32 seg;
+
+       spin_lock(&dev->mr_table.mpt_alloc.lock);
+
+       for (o = order; o <= dev->mr_table.max_mtt_order; ++o) {
+               m = 1 << (dev->mr_table.max_mtt_order - o);
+               seg = find_first_bit(dev->mr_table.mtt_buddy[o], m);
+               if (seg < m)
+                       goto found;
+       }
+
+       spin_unlock(&dev->mr_table.mpt_alloc.lock);
+       return -1;
+
+ found:
+       clear_bit(seg, dev->mr_table.mtt_buddy[o]);
+
+       while (o > order) {
+               --o;
+               seg <<= 1;
+               set_bit(seg ^ 1, dev->mr_table.mtt_buddy[o]);
+       }
+
+       spin_unlock(&dev->mr_table.mpt_alloc.lock);
+
+       seg <<= order;
+
+       return seg;
+}
+
+static void mthca_free_mtt(struct mthca_dev *dev, u32 seg, int order)
+{
+       seg >>= order;
+
+       spin_lock(&dev->mr_table.mpt_alloc.lock);
+
+       while (test_bit(seg ^ 1, dev->mr_table.mtt_buddy[order])) {
+               clear_bit(seg ^ 1, dev->mr_table.mtt_buddy[order]);
+               seg >>= 1;
+               ++order;
+       }
+
+       set_bit(seg, dev->mr_table.mtt_buddy[order]);
+
+       spin_unlock(&dev->mr_table.mpt_alloc.lock);
+}
+
+int mthca_mr_alloc_notrans(struct mthca_dev *dev, u32 pd,
+                          u32 access, struct mthca_mr *mr)
+{
+       void *mailbox;
+       struct mthca_mpt_entry *mpt_entry;
+       int err;
+       u8 status;
+
+       might_sleep();
+
+       mr->order = -1;
+       mr->ibmr.lkey = mthca_alloc(&dev->mr_table.mpt_alloc);
+       if (mr->ibmr.lkey == -1)
+               return -ENOMEM;
+       mr->ibmr.rkey = mr->ibmr.lkey;
+
+       mailbox = kmalloc(sizeof *mpt_entry + MTHCA_CMD_MAILBOX_EXTRA,
+                         GFP_KERNEL);
+       if (!mailbox) {
+               mthca_free(&dev->mr_table.mpt_alloc, mr->ibmr.lkey);
+               return -ENOMEM;
+       }
+       mpt_entry = MAILBOX_ALIGN(mailbox);
+
+       mpt_entry->flags = cpu_to_be32(MTHCA_MPT_FLAG_SW_OWNS     |
+                                      MTHCA_MPT_FLAG_MIO         |
+                                      MTHCA_MPT_FLAG_PHYSICAL    |
+                                      MTHCA_MPT_FLAG_REGION      |
+                                      access);
+       mpt_entry->page_size = 0;
+       mpt_entry->key       = cpu_to_be32(mr->ibmr.lkey);
+       mpt_entry->pd        = cpu_to_be32(pd);
+       mpt_entry->start     = 0;
+       mpt_entry->length    = ~0ULL;
+
+       memset(&mpt_entry->lkey, 0,
+              sizeof *mpt_entry - offsetof(struct mthca_mpt_entry, lkey));
+
+       err = mthca_SW2HW_MPT(dev, mpt_entry,
+                             mr->ibmr.lkey & (dev->limits.num_mpts - 1),
+                             &status);
+       if (err)
+               mthca_warn(dev, "SW2HW_MPT failed (%d)\n", err);
+       else if (status) {
+               mthca_warn(dev, "SW2HW_MPT returned status 0x%02x\n",
+                          status);
+               err = -EINVAL;
+       }
+
+       kfree(mailbox);
+       return err;
+}
+
+int mthca_mr_alloc_phys(struct mthca_dev *dev, u32 pd,
+                       u64 *buffer_list, int buffer_size_shift,
+                       int list_len, u64 iova, u64 total_size,
+                       u32 access, struct mthca_mr *mr)
+{
+       void *mailbox;
+       u64 *mtt_entry;
+       struct mthca_mpt_entry *mpt_entry;
+       int err = -ENOMEM;
+       u8 status;
+       int i;
+
+       might_sleep();
+       WARN_ON(buffer_size_shift >= 32);
+
+       mr->ibmr.lkey = mthca_alloc(&dev->mr_table.mpt_alloc);
+       if (mr->ibmr.lkey == -1)
+               return -ENOMEM;
+       mr->ibmr.rkey = mr->ibmr.lkey;
+
+       for (i = dev->limits.mtt_seg_size / 8, mr->order = 0;
+            i < list_len;
+            i <<= 1, ++mr->order)
+               ; /* nothing */
+
+       mr->first_seg = mthca_alloc_mtt(dev, mr->order);
+       if (mr->first_seg == -1)
+               goto err_out_mpt_free;
+
+       /*
+        * If list_len is odd, we add one more dummy entry for
+        * firmware efficiency.
+        */
+       mailbox = kmalloc(max(sizeof *mpt_entry,
+                             (size_t) 8 * (list_len + (list_len & 1) + 2)) +
+                         MTHCA_CMD_MAILBOX_EXTRA,
+                         GFP_KERNEL);
+       if (!mailbox)
+               goto err_out_free_mtt;
+
+       mtt_entry = MAILBOX_ALIGN(mailbox);
+
+       mtt_entry[0] = cpu_to_be64(dev->mr_table.mtt_base +
+                                  mr->first_seg * dev->limits.mtt_seg_size);
+       mtt_entry[1] = 0;
+       for (i = 0; i < list_len; ++i)
+               mtt_entry[i + 2] = cpu_to_be64(buffer_list[i] |
+                                              MTHCA_MTT_FLAG_PRESENT);
+       if (list_len & 1) {
+               mtt_entry[i + 2] = 0;
+               ++list_len;
+       }
+
+       if (0) {
+               mthca_dbg(dev, "Dumping MPT entry\n");
+               for (i = 0; i < list_len + 2; ++i)
+                       printk(KERN_ERR "[%2d] %016llx\n",
+                              i, (unsigned long long) be64_to_cpu(mtt_entry[i]));
+       }
+
+       err = mthca_WRITE_MTT(dev, mtt_entry, list_len, &status);
+       if (err) {
+               mthca_warn(dev, "WRITE_MTT failed (%d)\n", err);
+               goto err_out_mailbox_free;
+       }
+       if (status) {
+               mthca_warn(dev, "WRITE_MTT returned status 0x%02x\n",
+                          status);
+               err = -EINVAL;
+               goto err_out_mailbox_free;
+       }
+
+       mpt_entry = MAILBOX_ALIGN(mailbox);
+
+       mpt_entry->flags = cpu_to_be32(MTHCA_MPT_FLAG_SW_OWNS     |
+                                      MTHCA_MPT_FLAG_MIO         |
+                                      MTHCA_MPT_FLAG_REGION      |
+                                      access);
+
+       mpt_entry->page_size = cpu_to_be32(buffer_size_shift - 12);
+       mpt_entry->key       = cpu_to_be32(mr->ibmr.lkey);
+       mpt_entry->pd        = cpu_to_be32(pd);
+       mpt_entry->start     = cpu_to_be64(iova);
+       mpt_entry->length    = cpu_to_be64(total_size);
+       memset(&mpt_entry->lkey, 0,
+              sizeof *mpt_entry - offsetof(struct mthca_mpt_entry, lkey));
+       mpt_entry->mtt_seg   = cpu_to_be64(dev->mr_table.mtt_base +
+                                          mr->first_seg * dev->limits.mtt_seg_size);
+
+       if (0) {
+               mthca_dbg(dev, "Dumping MPT entry %08x:\n", mr->ibmr.lkey);
+               for (i = 0; i < sizeof (struct mthca_mpt_entry) / 4; ++i) {
+                       if (i % 4 == 0)
+                               printk("[%02x] ", i * 4);
+                       printk(" %08x", be32_to_cpu(((u32 *) mpt_entry)[i]));
+                       if ((i + 1) % 4 == 0)
+                               printk("\n");
+               }
+       }
+
+       err = mthca_SW2HW_MPT(dev, mpt_entry,
+                             mr->ibmr.lkey & (dev->limits.num_mpts - 1),
+                             &status);
+       if (err)
+               mthca_warn(dev, "SW2HW_MPT failed (%d)\n", err);
+       else if (status) {
+               mthca_warn(dev, "SW2HW_MPT returned status 0x%02x\n",
+                          status);
+               err = -EINVAL;
+       }
+
+       kfree(mailbox);
+       return err;
+
+ err_out_mailbox_free:
+       kfree(mailbox);
+
+ err_out_free_mtt:
+       mthca_free_mtt(dev, mr->first_seg, mr->order);
+
+ err_out_mpt_free:
+       mthca_free(&dev->mr_table.mpt_alloc, mr->ibmr.lkey);
+       return err;
+}
+
+void mthca_free_mr(struct mthca_dev *dev, struct mthca_mr *mr)
+{
+       int err;
+       u8 status;
+
+       might_sleep();
+
+       err = mthca_HW2SW_MPT(dev, NULL,
+                             mr->ibmr.lkey & (dev->limits.num_mpts - 1),
+                             &status);
+       if (err)
+               mthca_warn(dev, "HW2SW_MPT failed (%d)\n", err);
+       else if (status)
+               mthca_warn(dev, "HW2SW_MPT returned status 0x%02x\n",
+                          status);
+
+       if (mr->order >= 0)
+               mthca_free_mtt(dev, mr->first_seg, mr->order);
+
+       mthca_free(&dev->mr_table.mpt_alloc, mr->ibmr.lkey);
+}
+
+int __devinit mthca_init_mr_table(struct mthca_dev *dev)
+{
+       int err;
+       int i, s;
+
+       err = mthca_alloc_init(&dev->mr_table.mpt_alloc,
+                              dev->limits.num_mpts,
+                              ~0, dev->limits.reserved_mrws);
+       if (err)
+               return err;
+
+       err = -ENOMEM;
+
+       for (i = 1, dev->mr_table.max_mtt_order = 0;
+            i < dev->limits.num_mtt_segs;
+            i <<= 1, ++dev->mr_table.max_mtt_order)
+               ; /* nothing */
+
+       dev->mr_table.mtt_buddy = kmalloc((dev->mr_table.max_mtt_order + 1) *
+                                         sizeof (long *),
+                                         GFP_KERNEL);
+       if (!dev->mr_table.mtt_buddy)
+               goto err_out;
+
+       for (i = 0; i <= dev->mr_table.max_mtt_order; ++i)
+               dev->mr_table.mtt_buddy[i] = NULL;
+
+       for (i = 0; i <= dev->mr_table.max_mtt_order; ++i) {
+               s = BITS_TO_LONGS(1 << (dev->mr_table.max_mtt_order - i));
+               dev->mr_table.mtt_buddy[i] = kmalloc(s * sizeof (long),
+                                                    GFP_KERNEL);
+               if (!dev->mr_table.mtt_buddy[i])
+                       goto err_out_free;
+               bitmap_zero(dev->mr_table.mtt_buddy[i],
+                           1 << (dev->mr_table.max_mtt_order - i));
+       }
+
+       set_bit(0, dev->mr_table.mtt_buddy[dev->mr_table.max_mtt_order]);
+
+       for (i = 0; i < dev->mr_table.max_mtt_order; ++i)
+               if (1 << i >= dev->limits.reserved_mtts)
+                       break;
+
+       if (i == dev->mr_table.max_mtt_order) {
+               mthca_err(dev, "MTT table of order %d is "
+                         "too small.\n", i);
+               goto err_out_free;
+       }
+
+       (void) mthca_alloc_mtt(dev, i);
+
+       return 0;
+
+ err_out_free:
+       for (i = 0; i <= dev->mr_table.max_mtt_order; ++i)
+               kfree(dev->mr_table.mtt_buddy[i]);
+
+ err_out:
+       mthca_alloc_cleanup(&dev->mr_table.mpt_alloc);
+
+       return err;
+}
+
+void __devexit mthca_cleanup_mr_table(struct mthca_dev *dev)
+{
+       int i;
+
+       /* XXX check if any MRs are still allocated? */
+       for (i = 0; i <= dev->mr_table.max_mtt_order; ++i)
+               kfree(dev->mr_table.mtt_buddy[i]);
+       kfree(dev->mr_table.mtt_buddy);
+       mthca_alloc_cleanup(&dev->mr_table.mpt_alloc);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_pd.c b/drivers/infiniband/hw/mthca/mthca_pd.c
new file mode 100644 (file)
index 0000000..ea66847
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_pd.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+
+#include "mthca_dev.h"
+
+int mthca_pd_alloc(struct mthca_dev *dev, struct mthca_pd *pd)
+{
+       int err;
+
+       might_sleep();
+
+       atomic_set(&pd->sqp_count, 0);
+       pd->pd_num = mthca_alloc(&dev->pd_table.alloc);
+       if (pd->pd_num == -1)
+               return -ENOMEM;
+
+       err = mthca_mr_alloc_notrans(dev, pd->pd_num,
+                                    MTHCA_MPT_FLAG_LOCAL_READ |
+                                    MTHCA_MPT_FLAG_LOCAL_WRITE,
+                                    &pd->ntmr);
+       if (err)
+               mthca_free(&dev->pd_table.alloc, pd->pd_num);
+
+       return err;
+}
+
+void mthca_pd_free(struct mthca_dev *dev, struct mthca_pd *pd)
+{
+       might_sleep();
+       mthca_free_mr(dev, &pd->ntmr);
+       mthca_free(&dev->pd_table.alloc, pd->pd_num);
+}
+
+int __devinit mthca_init_pd_table(struct mthca_dev *dev)
+{
+       return mthca_alloc_init(&dev->pd_table.alloc,
+                               dev->limits.num_pds,
+                               (1 << 24) - 1,
+                               dev->limits.reserved_pds);
+}
+
+void __devexit mthca_cleanup_pd_table(struct mthca_dev *dev)
+{
+       /* XXX check if any PDs are still allocated? */
+       mthca_alloc_cleanup(&dev->pd_table.alloc);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_profile.c b/drivers/infiniband/hw/mthca/mthca_profile.c
new file mode 100644 (file)
index 0000000..9b32ca8
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_profile.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include "mthca_profile.h"
+
+enum {
+       MTHCA_RES_QP,
+       MTHCA_RES_EEC,
+       MTHCA_RES_SRQ,
+       MTHCA_RES_CQ,
+       MTHCA_RES_EQP,
+       MTHCA_RES_EEEC,
+       MTHCA_RES_EQ,
+       MTHCA_RES_RDB,
+       MTHCA_RES_MCG,
+       MTHCA_RES_MPT,
+       MTHCA_RES_MTT,
+       MTHCA_RES_UAR,
+       MTHCA_RES_UDAV,
+       MTHCA_RES_UARC,
+       MTHCA_RES_NUM
+};
+
+enum {
+       MTHCA_NUM_EQS = 32,
+       MTHCA_NUM_PDS = 1 << 15
+};
+
+u64 mthca_make_profile(struct mthca_dev *dev,
+                      struct mthca_profile *request,
+                      struct mthca_dev_lim *dev_lim,
+                      struct mthca_init_hca_param *init_hca)
+{
+       struct mthca_resource {
+               u64 size;
+               u64 start;
+               int type;
+               int num;
+               int log_num;
+       };
+
+       u64 mem_base, mem_avail;
+       u64 total_size = 0;
+       struct mthca_resource *profile;
+       struct mthca_resource tmp;
+       int i, j;
+
+       profile = kmalloc(MTHCA_RES_NUM * sizeof *profile, GFP_KERNEL);
+       if (!profile)
+               return -ENOMEM;
+
+       memset(profile, 0, MTHCA_RES_NUM * sizeof *profile);
+
+       profile[MTHCA_RES_QP].size   = dev_lim->qpc_entry_sz;
+       profile[MTHCA_RES_EEC].size  = dev_lim->eec_entry_sz;
+       profile[MTHCA_RES_SRQ].size  = dev_lim->srq_entry_sz;
+       profile[MTHCA_RES_CQ].size   = dev_lim->cqc_entry_sz;
+       profile[MTHCA_RES_EQP].size  = dev_lim->eqpc_entry_sz;
+       profile[MTHCA_RES_EEEC].size = dev_lim->eeec_entry_sz;
+       profile[MTHCA_RES_EQ].size   = dev_lim->eqc_entry_sz;
+       profile[MTHCA_RES_RDB].size  = MTHCA_RDB_ENTRY_SIZE;
+       profile[MTHCA_RES_MCG].size  = MTHCA_MGM_ENTRY_SIZE;
+       profile[MTHCA_RES_MPT].size  = dev_lim->mpt_entry_sz;
+       profile[MTHCA_RES_MTT].size  = dev_lim->mtt_seg_sz;
+       profile[MTHCA_RES_UAR].size  = dev_lim->uar_scratch_entry_sz;
+       profile[MTHCA_RES_UDAV].size = MTHCA_AV_SIZE;
+       profile[MTHCA_RES_UARC].size = request->uarc_size;
+
+       profile[MTHCA_RES_QP].num    = request->num_qp;
+       profile[MTHCA_RES_EQP].num   = request->num_qp;
+       profile[MTHCA_RES_RDB].num   = request->num_qp * request->rdb_per_qp;
+       profile[MTHCA_RES_CQ].num    = request->num_cq;
+       profile[MTHCA_RES_EQ].num    = MTHCA_NUM_EQS;
+       profile[MTHCA_RES_MCG].num   = request->num_mcg;
+       profile[MTHCA_RES_MPT].num   = request->num_mpt;
+       profile[MTHCA_RES_MTT].num   = request->num_mtt;
+       profile[MTHCA_RES_UAR].num   = request->num_uar;
+       profile[MTHCA_RES_UARC].num  = request->num_uar;
+       profile[MTHCA_RES_UDAV].num  = request->num_udav;
+
+       for (i = 0; i < MTHCA_RES_NUM; ++i) {
+               profile[i].type     = i;
+               profile[i].log_num  = max(ffs(profile[i].num) - 1, 0);
+               profile[i].size    *= profile[i].num;
+               if (dev->hca_type == ARBEL_NATIVE)
+                       profile[i].size = max(profile[i].size, (u64) PAGE_SIZE);
+       }
+
+       if (dev->hca_type == ARBEL_NATIVE) {
+               mem_base  = 0;
+               mem_avail = dev_lim->hca.arbel.max_icm_sz;
+       } else {
+               mem_base  = dev->ddr_start;
+               mem_avail = dev->fw.tavor.fw_start - dev->ddr_start;
+       }
+
+       /*
+        * Sort the resources in decreasing order of size.  Since they
+        * all have sizes that are powers of 2, we'll be able to keep
+        * resources aligned to their size and pack them without gaps
+        * using the sorted order.
+        */
+       for (i = MTHCA_RES_NUM; i > 0; --i)
+               for (j = 1; j < i; ++j) {
+                       if (profile[j].size > profile[j - 1].size) {
+                               tmp            = profile[j];
+                               profile[j]     = profile[j - 1];
+                               profile[j - 1] = tmp;
+                       }
+               }
+
+       for (i = 0; i < MTHCA_RES_NUM; ++i) {
+               if (profile[i].size) {
+                       profile[i].start = mem_base + total_size;
+                       total_size      += profile[i].size;
+               }
+               if (total_size > mem_avail) {
+                       mthca_err(dev, "Profile requires 0x%llx bytes; "
+                                 "won't in 0x%llx bytes of context memory.\n",
+                                 (unsigned long long) total_size,
+                                 (unsigned long long) mem_avail);
+                       kfree(profile);
+                       return -ENOMEM;
+               }
+
+               if (profile[i].size)
+                       mthca_dbg(dev, "profile[%2d]--%2d/%2d @ 0x%16llx "
+                                 "(size 0x%8llx)\n",
+                                 i, profile[i].type, profile[i].log_num,
+                                 (unsigned long long) profile[i].start,
+                                 (unsigned long long) profile[i].size);
+       }
+
+       if (dev->hca_type == ARBEL_NATIVE)
+               mthca_dbg(dev, "HCA context memory: reserving %d KB\n",
+                         (int) (total_size >> 10));
+       else
+               mthca_dbg(dev, "HCA memory: allocated %d KB/%d KB (%d KB free)\n",
+                         (int) (total_size >> 10), (int) (mem_avail >> 10),
+                         (int) ((mem_avail - total_size) >> 10));
+
+       for (i = 0; i < MTHCA_RES_NUM; ++i) {
+               switch (profile[i].type) {
+               case MTHCA_RES_QP:
+                       dev->limits.num_qps   = profile[i].num;
+                       init_hca->qpc_base    = profile[i].start;
+                       init_hca->log_num_qps = profile[i].log_num;
+                       break;
+               case MTHCA_RES_EEC:
+                       dev->limits.num_eecs   = profile[i].num;
+                       init_hca->eec_base     = profile[i].start;
+                       init_hca->log_num_eecs = profile[i].log_num;
+                       break;
+               case MTHCA_RES_SRQ:
+                       dev->limits.num_srqs   = profile[i].num;
+                       init_hca->srqc_base    = profile[i].start;
+                       init_hca->log_num_srqs = profile[i].log_num;
+                       break;
+               case MTHCA_RES_CQ:
+                       dev->limits.num_cqs   = profile[i].num;
+                       init_hca->cqc_base    = profile[i].start;
+                       init_hca->log_num_cqs = profile[i].log_num;
+                       break;
+               case MTHCA_RES_EQP:
+                       init_hca->eqpc_base = profile[i].start;
+                       break;
+               case MTHCA_RES_EEEC:
+                       init_hca->eeec_base = profile[i].start;
+                       break;
+               case MTHCA_RES_EQ:
+                       dev->limits.num_eqs   = profile[i].num;
+                       init_hca->eqc_base    = profile[i].start;
+                       init_hca->log_num_eqs = profile[i].log_num;
+                       break;
+               case MTHCA_RES_RDB:
+                       for (dev->qp_table.rdb_shift = 0;
+                            profile[MTHCA_RES_QP].num << dev->qp_table.rdb_shift <
+                                    profile[i].num;
+                            ++dev->qp_table.rdb_shift)
+                               ; /* nothing */
+                       dev->qp_table.rdb_base    = (u32) profile[i].start;
+                       init_hca->rdb_base        = profile[i].start;
+                       break;
+               case MTHCA_RES_MCG:
+                       dev->limits.num_mgms      = profile[i].num >> 1;
+                       dev->limits.num_amgms     = profile[i].num >> 1;
+                       init_hca->mc_base         = profile[i].start;
+                       init_hca->log_mc_entry_sz = ffs(MTHCA_MGM_ENTRY_SIZE) - 1;
+                       init_hca->log_mc_table_sz = profile[i].log_num;
+                       init_hca->mc_hash_sz      = 1 << (profile[i].log_num - 1);
+                       break;
+               case MTHCA_RES_MPT:
+                       dev->limits.num_mpts = profile[i].num;
+                       init_hca->mpt_base   = profile[i].start;
+                       init_hca->log_mpt_sz = profile[i].log_num;
+                       break;
+               case MTHCA_RES_MTT:
+                       dev->limits.num_mtt_segs = profile[i].num;
+                       dev->limits.mtt_seg_size = dev_lim->mtt_seg_sz;
+                       dev->mr_table.mtt_base   = profile[i].start;
+                       init_hca->mtt_base       = profile[i].start;
+                       init_hca->mtt_seg_sz     = ffs(dev_lim->mtt_seg_sz) - 7;
+                       break;
+               case MTHCA_RES_UAR:
+                       init_hca->uar_scratch_base = profile[i].start;
+                       break;
+               case MTHCA_RES_UDAV:
+                       dev->av_table.ddr_av_base = profile[i].start;
+                       dev->av_table.num_ddr_avs = profile[i].num;
+               case MTHCA_RES_UARC:
+                       init_hca->uarc_base   = profile[i].start;
+                       init_hca->log_uarc_sz = ffs(request->uarc_size) - 13;
+                       init_hca->log_uar_sz  = ffs(request->num_uar) - 1;
+               default:
+                       break;
+               }
+       }
+
+       /*
+        * PDs don't take any HCA memory, but we assign them as part
+        * of the HCA profile anyway.
+        */
+       dev->limits.num_pds = MTHCA_NUM_PDS;
+
+       kfree(profile);
+       return total_size;
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_profile.h b/drivers/infiniband/hw/mthca/mthca_profile.h
new file mode 100644 (file)
index 0000000..daaf799
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_profile.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#ifndef MTHCA_PROFILE_H
+#define MTHCA_PROFILE_H
+
+#include "mthca_dev.h"
+#include "mthca_cmd.h"
+
+struct mthca_profile {
+       int num_qp;
+       int rdb_per_qp;
+       int num_cq;
+       int num_mcg;
+       int num_mpt;
+       int num_mtt;
+       int num_udav;
+       int num_uar;
+       int uarc_size;
+};
+
+u64 mthca_make_profile(struct mthca_dev *mdev,
+                      struct mthca_profile *request,
+                      struct mthca_dev_lim *dev_lim,
+                      struct mthca_init_hca_param *init_hca);
+
+#endif /* MTHCA_PROFILE_H */
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c
new file mode 100644 (file)
index 0000000..5444087
--- /dev/null
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_provider.c 1397 2004-12-28 05:09:00Z roland $
+ */
+
+#include <ib_smi.h>
+
+#include "mthca_dev.h"
+#include "mthca_cmd.h"
+
+static int mthca_query_device(struct ib_device *ibdev,
+                             struct ib_device_attr *props)
+{
+       struct ib_smp *in_mad  = NULL;
+       struct ib_smp *out_mad = NULL;
+       int err = -ENOMEM;
+       u8 status;
+
+       in_mad  = kmalloc(sizeof *in_mad, GFP_KERNEL);
+       out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
+       if (!in_mad || !out_mad)
+               goto out;
+
+       props->fw_ver        = to_mdev(ibdev)->fw_ver;
+
+       memset(in_mad, 0, sizeof *in_mad);
+       in_mad->base_version       = 1;
+       in_mad->mgmt_class         = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+       in_mad->class_version      = 1;
+       in_mad->method             = IB_MGMT_METHOD_GET;
+       in_mad->attr_id            = IB_SMP_ATTR_NODE_INFO;
+
+       err = mthca_MAD_IFC(to_mdev(ibdev), 1, 1,
+                           1, NULL, NULL, in_mad, out_mad,
+                           &status);
+       if (err)
+               goto out;
+       if (status) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       props->vendor_id      = be32_to_cpup((u32 *) (out_mad->data + 36)) &
+               0xffffff;
+       props->vendor_part_id = be16_to_cpup((u16 *) (out_mad->data + 30));
+       props->hw_ver         = be16_to_cpup((u16 *) (out_mad->data + 32));
+       memcpy(&props->sys_image_guid, out_mad->data +  4, 8);
+       memcpy(&props->node_guid,      out_mad->data + 12, 8);
+
+       err = 0;
+ out:
+       kfree(in_mad);
+       kfree(out_mad);
+       return err;
+}
+
+static int mthca_query_port(struct ib_device *ibdev,
+                           u8 port, struct ib_port_attr *props)
+{
+       struct ib_smp *in_mad  = NULL;
+       struct ib_smp *out_mad = NULL;
+       int err = -ENOMEM;
+       u8 status;
+
+       in_mad  = kmalloc(sizeof *in_mad, GFP_KERNEL);
+       out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
+       if (!in_mad || !out_mad)
+               goto out;
+
+       memset(in_mad, 0, sizeof *in_mad);
+       in_mad->base_version       = 1;
+       in_mad->mgmt_class         = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+       in_mad->class_version      = 1;
+       in_mad->method             = IB_MGMT_METHOD_GET;
+       in_mad->attr_id            = IB_SMP_ATTR_PORT_INFO;
+       in_mad->attr_mod           = cpu_to_be32(port);
+
+       err = mthca_MAD_IFC(to_mdev(ibdev), 1, 1,
+                           port, NULL, NULL, in_mad, out_mad,
+                           &status);
+       if (err)
+               goto out;
+       if (status) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       props->lid               = be16_to_cpup((u16 *) (out_mad->data + 16));
+       props->lmc               = out_mad->data[34] & 0x7;
+       props->sm_lid            = be16_to_cpup((u16 *) (out_mad->data + 18));
+       props->sm_sl             = out_mad->data[36] & 0xf;
+       props->state             = out_mad->data[32] & 0xf;
+       props->phys_state        = out_mad->data[33] >> 4;
+       props->port_cap_flags    = be32_to_cpup((u32 *) (out_mad->data + 20));
+       props->gid_tbl_len       = to_mdev(ibdev)->limits.gid_table_len;
+       props->pkey_tbl_len      = to_mdev(ibdev)->limits.pkey_table_len;
+       props->qkey_viol_cntr    = be16_to_cpup((u16 *) (out_mad->data + 48));
+       props->active_width      = out_mad->data[31] & 0xf;
+       props->active_speed      = out_mad->data[35] >> 4;
+
+ out:
+       kfree(in_mad);
+       kfree(out_mad);
+       return err;
+}
+
+static int mthca_modify_port(struct ib_device *ibdev,
+                            u8 port, int port_modify_mask,
+                            struct ib_port_modify *props)
+{
+       struct mthca_set_ib_param set_ib;
+       struct ib_port_attr attr;
+       int err;
+       u8 status;
+
+       if (down_interruptible(&to_mdev(ibdev)->cap_mask_mutex))
+               return -ERESTARTSYS;
+
+       err = mthca_query_port(ibdev, port, &attr);
+       if (err)
+               goto out;
+
+       set_ib.set_si_guid     = 0;
+       set_ib.reset_qkey_viol = !!(port_modify_mask & IB_PORT_RESET_QKEY_CNTR);
+
+       set_ib.cap_mask = (attr.port_cap_flags | props->set_port_cap_mask) &
+               ~props->clr_port_cap_mask;
+
+       err = mthca_SET_IB(to_mdev(ibdev), &set_ib, port, &status);
+       if (err)
+               goto out;
+       if (status) {
+               err = -EINVAL;
+               goto out;
+       }
+
+out:
+       up(&to_mdev(ibdev)->cap_mask_mutex);
+       return err;
+}
+
+static int mthca_query_pkey(struct ib_device *ibdev,
+                           u8 port, u16 index, u16 *pkey)
+{
+       struct ib_smp *in_mad  = NULL;
+       struct ib_smp *out_mad = NULL;
+       int err = -ENOMEM;
+       u8 status;
+
+       in_mad  = kmalloc(sizeof *in_mad, GFP_KERNEL);
+       out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
+       if (!in_mad || !out_mad)
+               goto out;
+
+       memset(in_mad, 0, sizeof *in_mad);
+       in_mad->base_version       = 1;
+       in_mad->mgmt_class         = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+       in_mad->class_version      = 1;
+       in_mad->method             = IB_MGMT_METHOD_GET;
+       in_mad->attr_id            = IB_SMP_ATTR_PKEY_TABLE;
+       in_mad->attr_mod           = cpu_to_be32(index / 32);
+
+       err = mthca_MAD_IFC(to_mdev(ibdev), 1, 1,
+                           port, NULL, NULL, in_mad, out_mad,
+                           &status);
+       if (err)
+               goto out;
+       if (status) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       *pkey = be16_to_cpu(((u16 *) out_mad->data)[index % 32]);
+
+ out:
+       kfree(in_mad);
+       kfree(out_mad);
+       return err;
+}
+
+static int mthca_query_gid(struct ib_device *ibdev, u8 port,
+                          int index, union ib_gid *gid)
+{
+       struct ib_smp *in_mad  = NULL;
+       struct ib_smp *out_mad = NULL;
+       int err = -ENOMEM;
+       u8 status;
+
+       in_mad  = kmalloc(sizeof *in_mad, GFP_KERNEL);
+       out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
+       if (!in_mad || !out_mad)
+               goto out;
+
+       memset(in_mad, 0, sizeof *in_mad);
+       in_mad->base_version       = 1;
+       in_mad->mgmt_class         = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+       in_mad->class_version      = 1;
+       in_mad->method             = IB_MGMT_METHOD_GET;
+       in_mad->attr_id            = IB_SMP_ATTR_PORT_INFO;
+       in_mad->attr_mod           = cpu_to_be32(port);
+
+       err = mthca_MAD_IFC(to_mdev(ibdev), 1, 1,
+                           port, NULL, NULL, in_mad, out_mad,
+                           &status);
+       if (err)
+               goto out;
+       if (status) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       memcpy(gid->raw, out_mad->data + 8, 8);
+
+       memset(in_mad, 0, sizeof *in_mad);
+       in_mad->base_version       = 1;
+       in_mad->mgmt_class         = IB_MGMT_CLASS_SUBN_LID_ROUTED;
+       in_mad->class_version      = 1;
+       in_mad->method             = IB_MGMT_METHOD_GET;
+       in_mad->attr_id            = IB_SMP_ATTR_GUID_INFO;
+       in_mad->attr_mod           = cpu_to_be32(index / 8);
+
+       err = mthca_MAD_IFC(to_mdev(ibdev), 1, 1,
+                           port, NULL, NULL, in_mad, out_mad,
+                           &status);
+       if (err)
+               goto out;
+       if (status) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       memcpy(gid->raw + 8, out_mad->data + (index % 8) * 16, 8);
+
+ out:
+       kfree(in_mad);
+       kfree(out_mad);
+       return err;
+}
+
+static struct ib_pd *mthca_alloc_pd(struct ib_device *ibdev)
+{
+       struct mthca_pd *pd;
+       int err;
+
+       pd = kmalloc(sizeof *pd, GFP_KERNEL);
+       if (!pd)
+               return ERR_PTR(-ENOMEM);
+
+       err = mthca_pd_alloc(to_mdev(ibdev), pd);
+       if (err) {
+               kfree(pd);
+               return ERR_PTR(err);
+       }
+
+       return &pd->ibpd;
+}
+
+static int mthca_dealloc_pd(struct ib_pd *pd)
+{
+       mthca_pd_free(to_mdev(pd->device), to_mpd(pd));
+       kfree(pd);
+
+       return 0;
+}
+
+static struct ib_ah *mthca_ah_create(struct ib_pd *pd,
+                                    struct ib_ah_attr *ah_attr)
+{
+       int err;
+       struct mthca_ah *ah;
+
+       ah = kmalloc(sizeof *ah, GFP_KERNEL);
+       if (!ah)
+               return ERR_PTR(-ENOMEM);
+
+       err = mthca_create_ah(to_mdev(pd->device), to_mpd(pd), ah_attr, ah);
+       if (err) {
+               kfree(ah);
+               return ERR_PTR(err);
+       }
+
+       return &ah->ibah;
+}
+
+static int mthca_ah_destroy(struct ib_ah *ah)
+{
+       mthca_destroy_ah(to_mdev(ah->device), to_mah(ah));
+       kfree(ah);
+
+       return 0;
+}
+
+static struct ib_qp *mthca_create_qp(struct ib_pd *pd,
+                                    struct ib_qp_init_attr *init_attr)
+{
+       struct mthca_qp *qp;
+       int err;
+
+       switch (init_attr->qp_type) {
+       case IB_QPT_RC:
+       case IB_QPT_UC:
+       case IB_QPT_UD:
+       {
+               qp = kmalloc(sizeof *qp, GFP_KERNEL);
+               if (!qp)
+                       return ERR_PTR(-ENOMEM);
+
+               qp->sq.max    = init_attr->cap.max_send_wr;
+               qp->rq.max    = init_attr->cap.max_recv_wr;
+               qp->sq.max_gs = init_attr->cap.max_send_sge;
+               qp->rq.max_gs = init_attr->cap.max_recv_sge;
+
+               err = mthca_alloc_qp(to_mdev(pd->device), to_mpd(pd),
+                                    to_mcq(init_attr->send_cq),
+                                    to_mcq(init_attr->recv_cq),
+                                    init_attr->qp_type, init_attr->sq_sig_type,
+                                    init_attr->rq_sig_type, qp);
+               qp->ibqp.qp_num = qp->qpn;
+               break;
+       }
+       case IB_QPT_SMI:
+       case IB_QPT_GSI:
+       {
+               qp = kmalloc(sizeof (struct mthca_sqp), GFP_KERNEL);
+               if (!qp)
+                       return ERR_PTR(-ENOMEM);
+
+               qp->sq.max    = init_attr->cap.max_send_wr;
+               qp->rq.max    = init_attr->cap.max_recv_wr;
+               qp->sq.max_gs = init_attr->cap.max_send_sge;
+               qp->rq.max_gs = init_attr->cap.max_recv_sge;
+
+               qp->ibqp.qp_num = init_attr->qp_type == IB_QPT_SMI ? 0 : 1;
+
+               err = mthca_alloc_sqp(to_mdev(pd->device), to_mpd(pd),
+                                     to_mcq(init_attr->send_cq),
+                                     to_mcq(init_attr->recv_cq),
+                                     init_attr->sq_sig_type, init_attr->rq_sig_type,
+                                     qp->ibqp.qp_num, init_attr->port_num,
+                                     to_msqp(qp));
+               break;
+       }
+       default:
+               /* Don't support raw QPs */
+               return ERR_PTR(-ENOSYS);
+       }
+
+       if (err) {
+               kfree(qp);
+               return ERR_PTR(err);
+       }
+
+        init_attr->cap.max_inline_data = 0;
+
+       return &qp->ibqp;
+}
+
+static int mthca_destroy_qp(struct ib_qp *qp)
+{
+       mthca_free_qp(to_mdev(qp->device), to_mqp(qp));
+       kfree(qp);
+       return 0;
+}
+
+static struct ib_cq *mthca_create_cq(struct ib_device *ibdev, int entries)
+{
+       struct mthca_cq *cq;
+       int nent;
+       int err;
+
+       cq = kmalloc(sizeof *cq, GFP_KERNEL);
+       if (!cq)
+               return ERR_PTR(-ENOMEM);
+
+       for (nent = 1; nent <= entries; nent <<= 1)
+               ; /* nothing */
+
+       err = mthca_init_cq(to_mdev(ibdev), nent, cq);
+       if (err) {
+               kfree(cq);
+               cq = ERR_PTR(err);
+       } else
+               cq->ibcq.cqe = nent - 1;
+
+       return &cq->ibcq;
+}
+
+static int mthca_destroy_cq(struct ib_cq *cq)
+{
+       mthca_free_cq(to_mdev(cq->device), to_mcq(cq));
+       kfree(cq);
+
+       return 0;
+}
+
+static int mthca_req_notify_cq(struct ib_cq *cq, enum ib_cq_notify notify)
+{
+       mthca_arm_cq(to_mdev(cq->device), to_mcq(cq),
+                    notify == IB_CQ_SOLICITED);
+       return 0;
+}
+
+static inline u32 convert_access(int acc)
+{
+       return (acc & IB_ACCESS_REMOTE_ATOMIC ? MTHCA_MPT_FLAG_ATOMIC       : 0) |
+              (acc & IB_ACCESS_REMOTE_WRITE  ? MTHCA_MPT_FLAG_REMOTE_WRITE : 0) |
+              (acc & IB_ACCESS_REMOTE_READ   ? MTHCA_MPT_FLAG_REMOTE_READ  : 0) |
+              (acc & IB_ACCESS_LOCAL_WRITE   ? MTHCA_MPT_FLAG_LOCAL_WRITE  : 0) |
+              MTHCA_MPT_FLAG_LOCAL_READ;
+}
+
+static struct ib_mr *mthca_get_dma_mr(struct ib_pd *pd, int acc)
+{
+       struct mthca_mr *mr;
+       int err;
+
+       mr = kmalloc(sizeof *mr, GFP_KERNEL);
+       if (!mr)
+               return ERR_PTR(-ENOMEM);
+
+       err = mthca_mr_alloc_notrans(to_mdev(pd->device),
+                                    to_mpd(pd)->pd_num,
+                                    convert_access(acc), mr);
+
+       if (err) {
+               kfree(mr);
+               return ERR_PTR(err);
+       }
+
+       return &mr->ibmr;
+}
+
+static struct ib_mr *mthca_reg_phys_mr(struct ib_pd       *pd,
+                                      struct ib_phys_buf *buffer_list,
+                                      int                 num_phys_buf,
+                                      int                 acc,
+                                      u64                *iova_start)
+{
+       struct mthca_mr *mr;
+       u64 *page_list;
+       u64 total_size;
+       u64 mask;
+       int shift;
+       int npages;
+       int err;
+       int i, j, n;
+
+       /* First check that we have enough alignment */
+       if ((*iova_start & ~PAGE_MASK) != (buffer_list[0].addr & ~PAGE_MASK))
+               return ERR_PTR(-EINVAL);
+
+       if (num_phys_buf > 1 &&
+           ((buffer_list[0].addr + buffer_list[0].size) & ~PAGE_MASK))
+               return ERR_PTR(-EINVAL);
+
+       mask = 0;
+       total_size = 0;
+       for (i = 0; i < num_phys_buf; ++i) {
+               if (buffer_list[i].addr & ~PAGE_MASK)
+                       return ERR_PTR(-EINVAL);
+               if (i != 0 && i != num_phys_buf - 1 &&
+                   (buffer_list[i].size & ~PAGE_MASK))
+                       return ERR_PTR(-EINVAL);
+
+               total_size += buffer_list[i].size;
+               if (i > 0)
+                       mask |= buffer_list[i].addr;
+       }
+
+       /* Find largest page shift we can use to cover buffers */
+       for (shift = PAGE_SHIFT; shift < 31; ++shift)
+               if (num_phys_buf > 1) {
+                       if ((1ULL << shift) & mask)
+                               break;
+               } else {
+                       if (1ULL << shift >=
+                           buffer_list[0].size +
+                           (buffer_list[0].addr & ((1ULL << shift) - 1)))
+                               break;
+               }
+
+       buffer_list[0].size += buffer_list[0].addr & ((1ULL << shift) - 1);
+       buffer_list[0].addr &= ~0ull << shift;
+
+       mr = kmalloc(sizeof *mr, GFP_KERNEL);
+       if (!mr)
+               return ERR_PTR(-ENOMEM);
+
+       npages = 0;
+       for (i = 0; i < num_phys_buf; ++i)
+               npages += (buffer_list[i].size + (1ULL << shift) - 1) >> shift;
+
+       if (!npages)
+               return &mr->ibmr;
+
+       page_list = kmalloc(npages * sizeof *page_list, GFP_KERNEL);
+       if (!page_list) {
+               kfree(mr);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       n = 0;
+       for (i = 0; i < num_phys_buf; ++i)
+               for (j = 0;
+                    j < (buffer_list[i].size + (1ULL << shift) - 1) >> shift;
+                    ++j)
+                       page_list[n++] = buffer_list[i].addr + ((u64) j << shift);
+
+       mthca_dbg(to_mdev(pd->device), "Registering memory at %llx (iova %llx) "
+                 "in PD %x; shift %d, npages %d.\n",
+                 (unsigned long long) buffer_list[0].addr,
+                 (unsigned long long) *iova_start,
+                 to_mpd(pd)->pd_num,
+                 shift, npages);
+
+       err = mthca_mr_alloc_phys(to_mdev(pd->device),
+                                 to_mpd(pd)->pd_num,
+                                 page_list, shift, npages,
+                                 *iova_start, total_size,
+                                 convert_access(acc), mr);
+
+       if (err) {
+               kfree(mr);
+               return ERR_PTR(err);
+       }
+
+       kfree(page_list);
+       return &mr->ibmr;
+}
+
+static int mthca_dereg_mr(struct ib_mr *mr)
+{
+       mthca_free_mr(to_mdev(mr->device), to_mmr(mr));
+       kfree(mr);
+       return 0;
+}
+
+static ssize_t show_rev(struct class_device *cdev, char *buf)
+{
+       struct mthca_dev *dev = container_of(cdev, struct mthca_dev, ib_dev.class_dev);
+       return sprintf(buf, "%x\n", dev->rev_id);
+}
+
+static ssize_t show_fw_ver(struct class_device *cdev, char *buf)
+{
+       struct mthca_dev *dev = container_of(cdev, struct mthca_dev, ib_dev.class_dev);
+       return sprintf(buf, "%x.%x.%x\n", (int) (dev->fw_ver >> 32),
+                      (int) (dev->fw_ver >> 16) & 0xffff,
+                      (int) dev->fw_ver & 0xffff);
+}
+
+static ssize_t show_hca(struct class_device *cdev, char *buf)
+{
+       struct mthca_dev *dev = container_of(cdev, struct mthca_dev, ib_dev.class_dev);
+       switch (dev->hca_type) {
+       case TAVOR:        return sprintf(buf, "MT23108\n");
+       case ARBEL_COMPAT: return sprintf(buf, "MT25208 (MT23108 compat mode)\n");
+       case ARBEL_NATIVE: return sprintf(buf, "MT25208\n");
+       default:           return sprintf(buf, "unknown\n");
+       }
+}
+
+static CLASS_DEVICE_ATTR(hw_rev,   S_IRUGO, show_rev,    NULL);
+static CLASS_DEVICE_ATTR(fw_ver,   S_IRUGO, show_fw_ver, NULL);
+static CLASS_DEVICE_ATTR(hca_type, S_IRUGO, show_hca,    NULL);
+
+static struct class_device_attribute *mthca_class_attributes[] = {
+       &class_device_attr_hw_rev,
+       &class_device_attr_fw_ver,
+       &class_device_attr_hca_type
+};
+
+int mthca_register_device(struct mthca_dev *dev)
+{
+       int ret;
+       int i;
+
+       strlcpy(dev->ib_dev.name, "mthca%d", IB_DEVICE_NAME_MAX);
+       dev->ib_dev.node_type            = IB_NODE_CA;
+       dev->ib_dev.phys_port_cnt        = dev->limits.num_ports;
+       dev->ib_dev.dma_device           = &dev->pdev->dev;
+       dev->ib_dev.class_dev.dev        = &dev->pdev->dev;
+       dev->ib_dev.query_device         = mthca_query_device;
+       dev->ib_dev.query_port           = mthca_query_port;
+       dev->ib_dev.modify_port          = mthca_modify_port;
+       dev->ib_dev.query_pkey           = mthca_query_pkey;
+       dev->ib_dev.query_gid            = mthca_query_gid;
+       dev->ib_dev.alloc_pd             = mthca_alloc_pd;
+       dev->ib_dev.dealloc_pd           = mthca_dealloc_pd;
+       dev->ib_dev.create_ah            = mthca_ah_create;
+       dev->ib_dev.destroy_ah           = mthca_ah_destroy;
+       dev->ib_dev.create_qp            = mthca_create_qp;
+       dev->ib_dev.modify_qp            = mthca_modify_qp;
+       dev->ib_dev.destroy_qp           = mthca_destroy_qp;
+       dev->ib_dev.post_send            = mthca_post_send;
+       dev->ib_dev.post_recv            = mthca_post_receive;
+       dev->ib_dev.create_cq            = mthca_create_cq;
+       dev->ib_dev.destroy_cq           = mthca_destroy_cq;
+       dev->ib_dev.poll_cq              = mthca_poll_cq;
+       dev->ib_dev.req_notify_cq        = mthca_req_notify_cq;
+       dev->ib_dev.get_dma_mr           = mthca_get_dma_mr;
+       dev->ib_dev.reg_phys_mr          = mthca_reg_phys_mr;
+       dev->ib_dev.dereg_mr             = mthca_dereg_mr;
+       dev->ib_dev.attach_mcast         = mthca_multicast_attach;
+       dev->ib_dev.detach_mcast         = mthca_multicast_detach;
+       dev->ib_dev.process_mad          = mthca_process_mad;
+
+       init_MUTEX(&dev->cap_mask_mutex);
+
+       ret = ib_register_device(&dev->ib_dev);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(mthca_class_attributes); ++i) {
+               ret = class_device_create_file(&dev->ib_dev.class_dev,
+                                              mthca_class_attributes[i]);
+               if (ret) {
+                       ib_unregister_device(&dev->ib_dev);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+void mthca_unregister_device(struct mthca_dev *dev)
+{
+       ib_unregister_device(&dev->ib_dev);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.h b/drivers/infiniband/hw/mthca/mthca_provider.h
new file mode 100644 (file)
index 0000000..5c94df3
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_provider.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#ifndef MTHCA_PROVIDER_H
+#define MTHCA_PROVIDER_H
+
+#include <ib_verbs.h>
+#include <ib_pack.h>
+
+#define MTHCA_MPT_FLAG_ATOMIC        (1 << 14)
+#define MTHCA_MPT_FLAG_REMOTE_WRITE  (1 << 13)
+#define MTHCA_MPT_FLAG_REMOTE_READ   (1 << 12)
+#define MTHCA_MPT_FLAG_LOCAL_WRITE   (1 << 11)
+#define MTHCA_MPT_FLAG_LOCAL_READ    (1 << 10)
+
+struct mthca_buf_list {
+       void *buf;
+       DECLARE_PCI_UNMAP_ADDR(mapping)
+};
+
+struct mthca_mr {
+       struct ib_mr ibmr;
+       int order;
+       u32 first_seg;
+};
+
+struct mthca_pd {
+       struct ib_pd    ibpd;
+       u32             pd_num;
+       atomic_t        sqp_count;
+       struct mthca_mr ntmr;
+};
+
+struct mthca_eq {
+       struct mthca_dev      *dev;
+       int                    eqn;
+       u32                    ecr_mask;
+       u32                    cons_index;
+       u16                    msi_x_vector;
+       u16                    msi_x_entry;
+       int                    have_irq;
+       int                    nent;
+       struct mthca_buf_list *page_list;
+       struct mthca_mr        mr;
+};
+
+struct mthca_av;
+
+struct mthca_ah {
+       struct ib_ah     ibah;
+       int              on_hca;
+       u32              key;
+       struct mthca_av *av;
+       dma_addr_t       avdma;
+};
+
+/*
+ * Quick description of our CQ/QP locking scheme:
+ *
+ * We have one global lock that protects dev->cq/qp_table.  Each
+ * struct mthca_cq/qp also has its own lock.  An individual qp lock
+ * may be taken inside of an individual cq lock.  Both cqs attached to
+ * a qp may be locked, with the send cq locked first.  No other
+ * nesting should be done.
+ *
+ * Each struct mthca_cq/qp also has an atomic_t ref count.  The
+ * pointer from the cq/qp_table to the struct counts as one reference.
+ * This reference also is good for access through the consumer API, so
+ * modifying the CQ/QP etc doesn't need to take another reference.
+ * Access because of a completion being polled does need a reference.
+ *
+ * Finally, each struct mthca_cq/qp has a wait_queue_head_t for the
+ * destroy function to sleep on.
+ *
+ * This means that access from the consumer API requires nothing but
+ * taking the struct's lock.
+ *
+ * Access because of a completion event should go as follows:
+ * - lock cq/qp_table and look up struct
+ * - increment ref count in struct
+ * - drop cq/qp_table lock
+ * - lock struct, do your thing, and unlock struct
+ * - decrement ref count; if zero, wake up waiters
+ *
+ * To destroy a CQ/QP, we can do the following:
+ * - lock cq/qp_table, remove pointer, unlock cq/qp_table lock
+ * - decrement ref count
+ * - wait_event until ref count is zero
+ *
+ * It is the consumer's responsibilty to make sure that no QP
+ * operations (WQE posting or state modification) are pending when the
+ * QP is destroyed.  Also, the consumer must make sure that calls to
+ * qp_modify are serialized.
+ *
+ * Possible optimizations (wait for profile data to see if/where we
+ * have locks bouncing between CPUs):
+ * - split cq/qp table lock into n separate (cache-aligned) locks,
+ *   indexed (say) by the page in the table
+ * - split QP struct lock into three (one for common info, one for the
+ *   send queue and one for the receive queue)
+ */
+
+struct mthca_cq {
+       struct ib_cq           ibcq;
+       spinlock_t             lock;
+       atomic_t               refcount;
+       int                    cqn;
+       int                    cons_index;
+       int                    is_direct;
+       union {
+               struct mthca_buf_list direct;
+               struct mthca_buf_list *page_list;
+       }                      queue;
+       struct mthca_mr        mr;
+       wait_queue_head_t      wait;
+};
+
+struct mthca_wq {
+       int   max;
+       int   cur;
+       int   next;
+       int   last_comp;
+       void *last;
+       int   max_gs;
+       int   wqe_shift;
+       enum ib_sig_type policy;
+};
+
+struct mthca_qp {
+       struct ib_qp           ibqp;
+       spinlock_t             lock;
+       atomic_t               refcount;
+       u32                    qpn;
+       int                    is_direct;
+       u8                     transport;
+       u8                     state;
+       u8                     atomic_rd_en;
+       u8                     resp_depth;
+
+       struct mthca_mr        mr;
+
+       struct mthca_wq        rq;
+       struct mthca_wq        sq;
+       int                    send_wqe_offset;
+
+       u64                   *wrid;
+       union {
+               struct mthca_buf_list direct;
+               struct mthca_buf_list *page_list;
+       }                      queue;
+
+       wait_queue_head_t      wait;
+};
+
+struct mthca_sqp {
+       struct mthca_qp qp;
+       int             port;
+       int             pkey_index;
+       u32             qkey;
+       u32             send_psn;
+       struct ib_ud_header ud_header;
+       int             header_buf_size;
+       void           *header_buf;
+       dma_addr_t      header_dma;
+};
+
+static inline struct mthca_mr *to_mmr(struct ib_mr *ibmr)
+{
+       return container_of(ibmr, struct mthca_mr, ibmr);
+}
+
+static inline struct mthca_pd *to_mpd(struct ib_pd *ibpd)
+{
+       return container_of(ibpd, struct mthca_pd, ibpd);
+}
+
+static inline struct mthca_ah *to_mah(struct ib_ah *ibah)
+{
+       return container_of(ibah, struct mthca_ah, ibah);
+}
+
+static inline struct mthca_cq *to_mcq(struct ib_cq *ibcq)
+{
+       return container_of(ibcq, struct mthca_cq, ibcq);
+}
+
+static inline struct mthca_qp *to_mqp(struct ib_qp *ibqp)
+{
+       return container_of(ibqp, struct mthca_qp, ibqp);
+}
+
+static inline struct mthca_sqp *to_msqp(struct mthca_qp *qp)
+{
+       return container_of(qp, struct mthca_sqp, qp);
+}
+
+#endif /* MTHCA_PROVIDER_H */
diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c
new file mode 100644 (file)
index 0000000..e407ea9
--- /dev/null
@@ -0,0 +1,1615 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_qp.c 1355 2004-12-17 15:23:43Z roland $
+ */
+
+#include <linux/init.h>
+
+#include <ib_verbs.h>
+#include <ib_cache.h>
+#include <ib_pack.h>
+
+#include "mthca_dev.h"
+#include "mthca_cmd.h"
+
+enum {
+       MTHCA_MAX_DIRECT_QP_SIZE = 4 * PAGE_SIZE,
+       MTHCA_ACK_REQ_FREQ       = 10,
+       MTHCA_FLIGHT_LIMIT       = 9,
+       MTHCA_UD_HEADER_SIZE     = 72 /* largest UD header possible */
+};
+
+enum {
+       MTHCA_QP_STATE_RST  = 0,
+       MTHCA_QP_STATE_INIT = 1,
+       MTHCA_QP_STATE_RTR  = 2,
+       MTHCA_QP_STATE_RTS  = 3,
+       MTHCA_QP_STATE_SQE  = 4,
+       MTHCA_QP_STATE_SQD  = 5,
+       MTHCA_QP_STATE_ERR  = 6,
+       MTHCA_QP_STATE_DRAINING = 7
+};
+
+enum {
+       MTHCA_QP_ST_RC  = 0x0,
+       MTHCA_QP_ST_UC  = 0x1,
+       MTHCA_QP_ST_RD  = 0x2,
+       MTHCA_QP_ST_UD  = 0x3,
+       MTHCA_QP_ST_MLX = 0x7
+};
+
+enum {
+       MTHCA_QP_PM_MIGRATED = 0x3,
+       MTHCA_QP_PM_ARMED    = 0x0,
+       MTHCA_QP_PM_REARM    = 0x1
+};
+
+enum {
+       /* qp_context flags */
+       MTHCA_QP_BIT_DE  = 1 <<  8,
+       /* params1 */
+       MTHCA_QP_BIT_SRE = 1 << 15,
+       MTHCA_QP_BIT_SWE = 1 << 14,
+       MTHCA_QP_BIT_SAE = 1 << 13,
+       MTHCA_QP_BIT_SIC = 1 <<  4,
+       MTHCA_QP_BIT_SSC = 1 <<  3,
+       /* params2 */
+       MTHCA_QP_BIT_RRE = 1 << 15,
+       MTHCA_QP_BIT_RWE = 1 << 14,
+       MTHCA_QP_BIT_RAE = 1 << 13,
+       MTHCA_QP_BIT_RIC = 1 <<  4,
+       MTHCA_QP_BIT_RSC = 1 <<  3
+};
+
+struct mthca_qp_path {
+       u32 port_pkey;
+       u8  rnr_retry;
+       u8  g_mylmc;
+       u16 rlid;
+       u8  ackto;
+       u8  mgid_index;
+       u8  static_rate;
+       u8  hop_limit;
+       u32 sl_tclass_flowlabel;
+       u8  rgid[16];
+} __attribute__((packed));
+
+struct mthca_qp_context {
+       u32 flags;
+       u32 sched_queue;
+       u32 mtu_msgmax;
+       u32 usr_page;
+       u32 local_qpn;
+       u32 remote_qpn;
+       u32 reserved1[2];
+       struct mthca_qp_path pri_path;
+       struct mthca_qp_path alt_path;
+       u32 rdd;
+       u32 pd;
+       u32 wqe_base;
+       u32 wqe_lkey;
+       u32 params1;
+       u32 reserved2;
+       u32 next_send_psn;
+       u32 cqn_snd;
+       u32 next_snd_wqe[2];
+       u32 last_acked_psn;
+       u32 ssn;
+       u32 params2;
+       u32 rnr_nextrecvpsn;
+       u32 ra_buff_indx;
+       u32 cqn_rcv;
+       u32 next_rcv_wqe[2];
+       u32 qkey;
+       u32 srqn;
+       u32 rmsn;
+       u32 reserved3[19];
+} __attribute__((packed));
+
+struct mthca_qp_param {
+       u32 opt_param_mask;
+       u32 reserved1;
+       struct mthca_qp_context context;
+       u32 reserved2[62];
+} __attribute__((packed));
+
+enum {
+       MTHCA_QP_OPTPAR_ALT_ADDR_PATH     = 1 << 0,
+       MTHCA_QP_OPTPAR_RRE               = 1 << 1,
+       MTHCA_QP_OPTPAR_RAE               = 1 << 2,
+       MTHCA_QP_OPTPAR_RWE               = 1 << 3,
+       MTHCA_QP_OPTPAR_PKEY_INDEX        = 1 << 4,
+       MTHCA_QP_OPTPAR_Q_KEY             = 1 << 5,
+       MTHCA_QP_OPTPAR_RNR_TIMEOUT       = 1 << 6,
+       MTHCA_QP_OPTPAR_PRIMARY_ADDR_PATH = 1 << 7,
+       MTHCA_QP_OPTPAR_SRA_MAX           = 1 << 8,
+       MTHCA_QP_OPTPAR_RRA_MAX           = 1 << 9,
+       MTHCA_QP_OPTPAR_PM_STATE          = 1 << 10,
+       MTHCA_QP_OPTPAR_PORT_NUM          = 1 << 11,
+       MTHCA_QP_OPTPAR_RETRY_COUNT       = 1 << 12,
+       MTHCA_QP_OPTPAR_ALT_RNR_RETRY     = 1 << 13,
+       MTHCA_QP_OPTPAR_ACK_TIMEOUT       = 1 << 14,
+       MTHCA_QP_OPTPAR_RNR_RETRY         = 1 << 15,
+       MTHCA_QP_OPTPAR_SCHED_QUEUE       = 1 << 16
+};
+
+enum {
+       MTHCA_OPCODE_NOP            = 0x00,
+       MTHCA_OPCODE_RDMA_WRITE     = 0x08,
+       MTHCA_OPCODE_RDMA_WRITE_IMM = 0x09,
+       MTHCA_OPCODE_SEND           = 0x0a,
+       MTHCA_OPCODE_SEND_IMM       = 0x0b,
+       MTHCA_OPCODE_RDMA_READ      = 0x10,
+       MTHCA_OPCODE_ATOMIC_CS      = 0x11,
+       MTHCA_OPCODE_ATOMIC_FA      = 0x12,
+       MTHCA_OPCODE_BIND_MW        = 0x18,
+       MTHCA_OPCODE_INVALID        = 0xff
+};
+
+enum {
+       MTHCA_NEXT_DBD       = 1 << 7,
+       MTHCA_NEXT_FENCE     = 1 << 6,
+       MTHCA_NEXT_CQ_UPDATE = 1 << 3,
+       MTHCA_NEXT_EVENT_GEN = 1 << 2,
+       MTHCA_NEXT_SOLICIT   = 1 << 1,
+
+       MTHCA_MLX_VL15       = 1 << 17,
+       MTHCA_MLX_SLR        = 1 << 16
+};
+
+struct mthca_next_seg {
+       u32 nda_op;             /* [31:6] next WQE [4:0] next opcode */
+       u32 ee_nds;             /* [31:8] next EE  [7] DBD [6] F [5:0] next WQE size */
+       u32 flags;              /* [3] CQ [2] Event [1] Solicit */
+       u32 imm;                /* immediate data */
+};
+
+struct mthca_ud_seg {
+       u32 reserved1;
+       u32 lkey;
+       u64 av_addr;
+       u32 reserved2[4];
+       u32 dqpn;
+       u32 qkey;
+       u32 reserved3[2];
+};
+
+struct mthca_bind_seg {
+       u32 flags;              /* [31] Atomic [30] rem write [29] rem read */
+       u32 reserved;
+       u32 new_rkey;
+       u32 lkey;
+       u64 addr;
+       u64 length;
+};
+
+struct mthca_raddr_seg {
+       u64 raddr;
+       u32 rkey;
+       u32 reserved;
+};
+
+struct mthca_atomic_seg {
+       u64 swap_add;
+       u64 compare;
+};
+
+struct mthca_data_seg {
+       u32 byte_count;
+       u32 lkey;
+       u64 addr;
+};
+
+struct mthca_mlx_seg {
+       u32 nda_op;
+       u32 nds;
+       u32 flags;              /* [17] VL15 [16] SLR [14:12] static rate
+                                  [11:8] SL [3] C [2] E */
+       u16 rlid;
+       u16 vcrc;
+};
+
+static int is_sqp(struct mthca_dev *dev, struct mthca_qp *qp)
+{
+       return qp->qpn >= dev->qp_table.sqp_start &&
+               qp->qpn <= dev->qp_table.sqp_start + 3;
+}
+
+static int is_qp0(struct mthca_dev *dev, struct mthca_qp *qp)
+{
+       return qp->qpn >= dev->qp_table.sqp_start &&
+               qp->qpn <= dev->qp_table.sqp_start + 1;
+}
+
+static void *get_recv_wqe(struct mthca_qp *qp, int n)
+{
+       if (qp->is_direct)
+               return qp->queue.direct.buf + (n << qp->rq.wqe_shift);
+       else
+               return qp->queue.page_list[(n << qp->rq.wqe_shift) >> PAGE_SHIFT].buf +
+                       ((n << qp->rq.wqe_shift) & (PAGE_SIZE - 1));
+}
+
+static void *get_send_wqe(struct mthca_qp *qp, int n)
+{
+       if (qp->is_direct)
+               return qp->queue.direct.buf + qp->send_wqe_offset +
+                       (n << qp->sq.wqe_shift);
+       else
+               return qp->queue.page_list[(qp->send_wqe_offset +
+                                           (n << qp->sq.wqe_shift)) >>
+                                          PAGE_SHIFT].buf +
+                       ((qp->send_wqe_offset + (n << qp->sq.wqe_shift)) &
+                        (PAGE_SIZE - 1));
+}
+
+void mthca_qp_event(struct mthca_dev *dev, u32 qpn,
+                   enum ib_event_type event_type)
+{
+       struct mthca_qp *qp;
+       struct ib_event event;
+
+       spin_lock(&dev->qp_table.lock);
+       qp = mthca_array_get(&dev->qp_table.qp, qpn & (dev->limits.num_qps - 1));
+       if (qp)
+               atomic_inc(&qp->refcount);
+       spin_unlock(&dev->qp_table.lock);
+
+       if (!qp) {
+               mthca_warn(dev, "Async event for bogus QP %08x\n", qpn);
+               return;
+       }
+
+       event.device      = &dev->ib_dev;
+       event.event       = event_type;
+       event.element.qp  = &qp->ibqp;
+       if (qp->ibqp.event_handler)
+               qp->ibqp.event_handler(&event, qp->ibqp.qp_context);
+
+       if (atomic_dec_and_test(&qp->refcount))
+               wake_up(&qp->wait);
+}
+
+static int to_mthca_state(enum ib_qp_state ib_state)
+{
+       switch (ib_state) {
+       case IB_QPS_RESET: return MTHCA_QP_STATE_RST;
+       case IB_QPS_INIT:  return MTHCA_QP_STATE_INIT;
+       case IB_QPS_RTR:   return MTHCA_QP_STATE_RTR;
+       case IB_QPS_RTS:   return MTHCA_QP_STATE_RTS;
+       case IB_QPS_SQD:   return MTHCA_QP_STATE_SQD;
+       case IB_QPS_SQE:   return MTHCA_QP_STATE_SQE;
+       case IB_QPS_ERR:   return MTHCA_QP_STATE_ERR;
+       default:                return -1;
+       }
+}
+
+enum { RC, UC, UD, RD, RDEE, MLX, NUM_TRANS };
+
+static int to_mthca_st(int transport)
+{
+       switch (transport) {
+       case RC:  return MTHCA_QP_ST_RC;
+       case UC:  return MTHCA_QP_ST_UC;
+       case UD:  return MTHCA_QP_ST_UD;
+       case RD:  return MTHCA_QP_ST_RD;
+       case MLX: return MTHCA_QP_ST_MLX;
+       default:  return -1;
+       }
+}
+
+static const struct {
+       int trans;
+       u32 req_param[NUM_TRANS];
+       u32 opt_param[NUM_TRANS];
+} state_table[IB_QPS_ERR + 1][IB_QPS_ERR + 1] = {
+       [IB_QPS_RESET] = {
+               [IB_QPS_RESET] = { .trans = MTHCA_TRANS_ANY2RST },
+               [IB_QPS_ERR] = { .trans = MTHCA_TRANS_ANY2ERR },
+               [IB_QPS_INIT]  = {
+                       .trans = MTHCA_TRANS_RST2INIT,
+                       .req_param = {
+                               [UD]  = (IB_QP_PKEY_INDEX |
+                                        IB_QP_PORT       |
+                                        IB_QP_QKEY),
+                               [RC]  = (IB_QP_PKEY_INDEX |
+                                        IB_QP_PORT       |
+                                        IB_QP_ACCESS_FLAGS),
+                               [MLX] = (IB_QP_PKEY_INDEX |
+                                        IB_QP_QKEY),
+                       },
+                       /* bug-for-bug compatibility with VAPI: */
+                       .opt_param = {
+                               [MLX] = IB_QP_PORT
+                       }
+               },
+       },
+       [IB_QPS_INIT]  = {
+               [IB_QPS_RESET] = { .trans = MTHCA_TRANS_ANY2RST },
+               [IB_QPS_ERR] = { .trans = MTHCA_TRANS_ANY2ERR },
+               [IB_QPS_INIT]  = {
+                       .trans = MTHCA_TRANS_INIT2INIT,
+                       .opt_param = {
+                               [UD]  = (IB_QP_PKEY_INDEX |
+                                        IB_QP_PORT       |
+                                        IB_QP_QKEY),
+                               [RC]  = (IB_QP_PKEY_INDEX |
+                                        IB_QP_PORT       |
+                                        IB_QP_ACCESS_FLAGS),
+                               [MLX] = (IB_QP_PKEY_INDEX |
+                                        IB_QP_QKEY),
+                       }
+               },
+               [IB_QPS_RTR]   = {
+                       .trans = MTHCA_TRANS_INIT2RTR,
+                       .req_param = {
+                               [RC]  = (IB_QP_AV                  |
+                                        IB_QP_PATH_MTU            |
+                                        IB_QP_DEST_QPN            |
+                                        IB_QP_RQ_PSN              |
+                                        IB_QP_MAX_DEST_RD_ATOMIC  |
+                                        IB_QP_MIN_RNR_TIMER),
+                       },
+                       .opt_param = {
+                               [UD]  = (IB_QP_PKEY_INDEX |
+                                        IB_QP_QKEY),
+                               [RC]  = (IB_QP_ALT_PATH     |
+                                        IB_QP_ACCESS_FLAGS |
+                                        IB_QP_PKEY_INDEX),
+                               [MLX] = (IB_QP_PKEY_INDEX |
+                                        IB_QP_QKEY),
+                       }
+               }
+       },
+       [IB_QPS_RTR]   = {
+               [IB_QPS_RESET] = { .trans = MTHCA_TRANS_ANY2RST },
+               [IB_QPS_ERR] = { .trans = MTHCA_TRANS_ANY2ERR },
+               [IB_QPS_RTS]   = {
+                       .trans = MTHCA_TRANS_RTR2RTS,
+                       .req_param = {
+                               [UD]  = IB_QP_SQ_PSN,
+                               [RC]  = (IB_QP_TIMEOUT           |
+                                        IB_QP_RETRY_CNT         |
+                                        IB_QP_RNR_RETRY         |
+                                        IB_QP_SQ_PSN            |
+                                        IB_QP_MAX_QP_RD_ATOMIC),
+                               [MLX] = IB_QP_SQ_PSN,
+                       },
+                       .opt_param = {
+                               [UD]  = (IB_QP_CUR_STATE             |
+                                        IB_QP_QKEY),
+                               [RC]  = (IB_QP_CUR_STATE             |
+                                        IB_QP_ALT_PATH              |
+                                        IB_QP_ACCESS_FLAGS          |
+                                        IB_QP_PKEY_INDEX            |
+                                        IB_QP_MIN_RNR_TIMER         |
+                                        IB_QP_PATH_MIG_STATE),
+                               [MLX] = (IB_QP_CUR_STATE             |
+                                        IB_QP_QKEY),
+                       }
+               }
+       },
+       [IB_QPS_RTS]   = {
+               [IB_QPS_RESET] = { .trans = MTHCA_TRANS_ANY2RST },
+               [IB_QPS_ERR] = { .trans = MTHCA_TRANS_ANY2ERR },
+               [IB_QPS_RTS]   = {
+                       .trans = MTHCA_TRANS_RTS2RTS,
+                       .opt_param = {
+                               [UD]  = (IB_QP_CUR_STATE             |
+                                        IB_QP_QKEY),
+                               [RC]  = (IB_QP_ACCESS_FLAGS          |
+                                        IB_QP_ALT_PATH              |
+                                        IB_QP_PATH_MIG_STATE        |
+                                        IB_QP_MIN_RNR_TIMER),
+                               [MLX] = (IB_QP_CUR_STATE             |
+                                        IB_QP_QKEY),
+                       }
+               },
+               [IB_QPS_SQD]   = {
+                       .trans = MTHCA_TRANS_RTS2SQD,
+               },
+       },
+       [IB_QPS_SQD]   = {
+               [IB_QPS_RESET] = { .trans = MTHCA_TRANS_ANY2RST },
+               [IB_QPS_ERR] = { .trans = MTHCA_TRANS_ANY2ERR },
+               [IB_QPS_RTS]   = {
+                       .trans = MTHCA_TRANS_SQD2RTS,
+                       .opt_param = {
+                               [UD]  = (IB_QP_CUR_STATE             |
+                                        IB_QP_QKEY),
+                               [RC]  = (IB_QP_CUR_STATE             |
+                                        IB_QP_ALT_PATH              |
+                                        IB_QP_ACCESS_FLAGS          |
+                                        IB_QP_MIN_RNR_TIMER         |
+                                        IB_QP_PATH_MIG_STATE),
+                               [MLX] = (IB_QP_CUR_STATE             |
+                                        IB_QP_QKEY),
+                       }
+               },
+               [IB_QPS_SQD]   = {
+                       .trans = MTHCA_TRANS_SQD2SQD,
+                       .opt_param = {
+                               [UD]  = (IB_QP_PKEY_INDEX            |
+                                        IB_QP_QKEY),
+                               [RC]  = (IB_QP_AV                    |
+                                        IB_QP_TIMEOUT               |
+                                        IB_QP_RETRY_CNT             |
+                                        IB_QP_RNR_RETRY             |
+                                        IB_QP_MAX_QP_RD_ATOMIC      |
+                                        IB_QP_MAX_DEST_RD_ATOMIC    |
+                                        IB_QP_CUR_STATE             |
+                                        IB_QP_ALT_PATH              |
+                                        IB_QP_ACCESS_FLAGS          |
+                                        IB_QP_PKEY_INDEX            |
+                                        IB_QP_MIN_RNR_TIMER         |
+                                        IB_QP_PATH_MIG_STATE),
+                               [MLX] = (IB_QP_PKEY_INDEX            |
+                                        IB_QP_QKEY),
+                       }
+               }
+       },
+       [IB_QPS_SQE]   = {
+               [IB_QPS_RESET] = { .trans = MTHCA_TRANS_ANY2RST },
+               [IB_QPS_ERR] = { .trans = MTHCA_TRANS_ANY2ERR },
+               [IB_QPS_RTS]   = {
+                       .trans = MTHCA_TRANS_SQERR2RTS,
+                       .opt_param = {
+                               [UD]  = (IB_QP_CUR_STATE             |
+                                        IB_QP_QKEY),
+                               [RC]  = (IB_QP_CUR_STATE             |
+                                        IB_QP_MIN_RNR_TIMER),
+                               [MLX] = (IB_QP_CUR_STATE             |
+                                        IB_QP_QKEY),
+                       }
+               }
+       },
+       [IB_QPS_ERR] = {
+               [IB_QPS_RESET] = { .trans = MTHCA_TRANS_ANY2RST },
+               [IB_QPS_ERR] = { .trans = MTHCA_TRANS_ANY2ERR }
+       }
+};
+
+static void store_attrs(struct mthca_sqp *sqp, struct ib_qp_attr *attr,
+                       int attr_mask)
+{
+       if (attr_mask & IB_QP_PKEY_INDEX)
+               sqp->pkey_index = attr->pkey_index;
+       if (attr_mask & IB_QP_QKEY)
+               sqp->qkey = attr->qkey;
+       if (attr_mask & IB_QP_SQ_PSN)
+               sqp->send_psn = attr->sq_psn;
+}
+
+static void init_port(struct mthca_dev *dev, int port)
+{
+       int err;
+       u8 status;
+       struct mthca_init_ib_param param;
+
+       memset(&param, 0, sizeof param);
+
+       param.enable_1x = 1;
+       param.enable_4x = 1;
+       param.vl_cap    = dev->limits.vl_cap;
+       param.mtu_cap   = dev->limits.mtu_cap;
+       param.gid_cap   = dev->limits.gid_table_len;
+       param.pkey_cap  = dev->limits.pkey_table_len;
+
+       err = mthca_INIT_IB(dev, &param, port, &status);
+       if (err)
+               mthca_warn(dev, "INIT_IB failed, return code %d.\n", err);
+       if (status)
+               mthca_warn(dev, "INIT_IB returned status %02x.\n", status);
+}
+
+int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask)
+{
+       struct mthca_dev *dev = to_mdev(ibqp->device);
+       struct mthca_qp *qp = to_mqp(ibqp);
+       enum ib_qp_state cur_state, new_state;
+       void *mailbox = NULL;
+       struct mthca_qp_param *qp_param;
+       struct mthca_qp_context *qp_context;
+       u32 req_param, opt_param;
+       u8 status;
+       int err;
+
+       if (attr_mask & IB_QP_CUR_STATE) {
+               if (attr->cur_qp_state != IB_QPS_RTR &&
+                   attr->cur_qp_state != IB_QPS_RTS &&
+                   attr->cur_qp_state != IB_QPS_SQD &&
+                   attr->cur_qp_state != IB_QPS_SQE)
+                       return -EINVAL;
+               else
+                       cur_state = attr->cur_qp_state;
+       } else {
+               spin_lock_irq(&qp->lock);
+               cur_state = qp->state;
+               spin_unlock_irq(&qp->lock);
+       }
+
+       if (attr_mask & IB_QP_STATE) {
+               if (attr->qp_state < 0 || attr->qp_state > IB_QPS_ERR)
+                       return -EINVAL;
+               new_state = attr->qp_state;
+       } else
+               new_state = cur_state;
+
+       if (state_table[cur_state][new_state].trans == MTHCA_TRANS_INVALID) {
+               mthca_dbg(dev, "Illegal QP transition "
+                         "%d->%d\n", cur_state, new_state);
+               return -EINVAL;
+       }
+
+       req_param = state_table[cur_state][new_state].req_param[qp->transport];
+       opt_param = state_table[cur_state][new_state].opt_param[qp->transport];
+
+       if ((req_param & attr_mask) != req_param) {
+               mthca_dbg(dev, "QP transition "
+                         "%d->%d missing req attr 0x%08x\n",
+                         cur_state, new_state,
+                         req_param & ~attr_mask);
+               return -EINVAL;
+       }
+
+       if (attr_mask & ~(req_param | opt_param | IB_QP_STATE)) {
+               mthca_dbg(dev, "QP transition (transport %d) "
+                         "%d->%d has extra attr 0x%08x\n",
+                         qp->transport,
+                         cur_state, new_state,
+                         attr_mask & ~(req_param | opt_param |
+                                                IB_QP_STATE));
+               return -EINVAL;
+       }
+
+       mailbox = kmalloc(sizeof (*qp_param) + MTHCA_CMD_MAILBOX_EXTRA, GFP_KERNEL);
+       if (!mailbox)
+               return -ENOMEM;
+       qp_param = MAILBOX_ALIGN(mailbox);
+       qp_context = &qp_param->context;
+       memset(qp_param, 0, sizeof *qp_param);
+
+       qp_context->flags      = cpu_to_be32((to_mthca_state(new_state) << 28) |
+                                            (to_mthca_st(qp->transport) << 16));
+       qp_context->flags     |= cpu_to_be32(MTHCA_QP_BIT_DE);
+       if (!(attr_mask & IB_QP_PATH_MIG_STATE))
+               qp_context->flags |= cpu_to_be32(MTHCA_QP_PM_MIGRATED << 11);
+       else {
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_PM_STATE);
+               switch (attr->path_mig_state) {
+               case IB_MIG_MIGRATED:
+                       qp_context->flags |= cpu_to_be32(MTHCA_QP_PM_MIGRATED << 11);
+                       break;
+               case IB_MIG_REARM:
+                       qp_context->flags |= cpu_to_be32(MTHCA_QP_PM_REARM << 11);
+                       break;
+               case IB_MIG_ARMED:
+                       qp_context->flags |= cpu_to_be32(MTHCA_QP_PM_ARMED << 11);
+                       break;
+               }
+       }
+       /* leave sched_queue as 0 */
+       if (qp->transport == MLX || qp->transport == UD)
+               qp_context->mtu_msgmax = cpu_to_be32((IB_MTU_2048 << 29) |
+                                                    (11 << 24));
+       else if (attr_mask & IB_QP_PATH_MTU) {
+               qp_context->mtu_msgmax = cpu_to_be32((attr->path_mtu << 29) |
+                                                    (31 << 24));
+       }
+       qp_context->usr_page   = cpu_to_be32(MTHCA_KAR_PAGE);
+       qp_context->local_qpn  = cpu_to_be32(qp->qpn);
+       if (attr_mask & IB_QP_DEST_QPN) {
+               qp_context->remote_qpn = cpu_to_be32(attr->dest_qp_num);
+       }
+
+       if (qp->transport == MLX)
+               qp_context->pri_path.port_pkey |=
+                       cpu_to_be32(to_msqp(qp)->port << 24);
+       else {
+               if (attr_mask & IB_QP_PORT) {
+                       qp_context->pri_path.port_pkey |=
+                               cpu_to_be32(attr->port_num << 24);
+                       qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_PORT_NUM);
+               }
+       }
+
+       if (attr_mask & IB_QP_PKEY_INDEX) {
+               qp_context->pri_path.port_pkey |=
+                       cpu_to_be32(attr->pkey_index);
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_PKEY_INDEX);
+       }
+
+       if (attr_mask & IB_QP_RNR_RETRY) {
+               qp_context->pri_path.rnr_retry = attr->rnr_retry << 5;
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_RNR_RETRY);
+       }
+
+       if (attr_mask & IB_QP_AV) {
+               qp_context->pri_path.g_mylmc     = attr->ah_attr.src_path_bits & 0x7f;
+               qp_context->pri_path.rlid        = cpu_to_be16(attr->ah_attr.dlid);
+               qp_context->pri_path.static_rate = (!!attr->ah_attr.static_rate) << 3;
+               if (attr->ah_attr.ah_flags & IB_AH_GRH) {
+                       qp_context->pri_path.g_mylmc |= 1 << 7;
+                       qp_context->pri_path.mgid_index = attr->ah_attr.grh.sgid_index;
+                       qp_context->pri_path.hop_limit = attr->ah_attr.grh.hop_limit;
+                       qp_context->pri_path.sl_tclass_flowlabel =
+                               cpu_to_be32((attr->ah_attr.sl << 28)                |
+                                           (attr->ah_attr.grh.traffic_class << 20) |
+                                           (attr->ah_attr.grh.flow_label));
+                       memcpy(qp_context->pri_path.rgid,
+                              attr->ah_attr.grh.dgid.raw, 16);
+               } else {
+                       qp_context->pri_path.sl_tclass_flowlabel =
+                               cpu_to_be32(attr->ah_attr.sl << 28);
+               }
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_PRIMARY_ADDR_PATH);
+       }
+
+       if (attr_mask & IB_QP_TIMEOUT) {
+               qp_context->pri_path.ackto = attr->timeout;
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_ACK_TIMEOUT);
+       }
+
+       /* XXX alt_path */
+
+       /* leave rdd as 0 */
+       qp_context->pd         = cpu_to_be32(to_mpd(ibqp->pd)->pd_num);
+       /* leave wqe_base as 0 (we always create an MR based at 0 for WQs) */
+       qp_context->wqe_lkey   = cpu_to_be32(qp->mr.ibmr.lkey);
+       qp_context->params1    = cpu_to_be32((MTHCA_ACK_REQ_FREQ << 28) |
+                                            (MTHCA_FLIGHT_LIMIT << 24) |
+                                            MTHCA_QP_BIT_SRE           |
+                                            MTHCA_QP_BIT_SWE           |
+                                            MTHCA_QP_BIT_SAE);
+       if (qp->sq.policy == IB_SIGNAL_ALL_WR)
+               qp_context->params1 |= cpu_to_be32(MTHCA_QP_BIT_SSC);
+       if (attr_mask & IB_QP_RETRY_CNT) {
+               qp_context->params1 |= cpu_to_be32(attr->retry_cnt << 16);
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_RETRY_COUNT);
+       }
+
+       if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) {
+               qp_context->params1 |= cpu_to_be32(min(attr->max_dest_rd_atomic ?
+                                                      ffs(attr->max_dest_rd_atomic) - 1 : 0,
+                                                      7) << 21);
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_SRA_MAX);
+       }
+
+       if (attr_mask & IB_QP_SQ_PSN)
+               qp_context->next_send_psn = cpu_to_be32(attr->sq_psn);
+       qp_context->cqn_snd = cpu_to_be32(to_mcq(ibqp->send_cq)->cqn);
+
+       if (attr_mask & IB_QP_ACCESS_FLAGS) {
+               /*
+                * Only enable RDMA/atomics if we have responder
+                * resources set to a non-zero value.
+                */
+               if (qp->resp_depth) {
+                       qp_context->params2 |=
+                               cpu_to_be32(attr->qp_access_flags & IB_ACCESS_REMOTE_WRITE ?
+                                           MTHCA_QP_BIT_RWE : 0);
+                       qp_context->params2 |=
+                               cpu_to_be32(attr->qp_access_flags & IB_ACCESS_REMOTE_READ ?
+                                           MTHCA_QP_BIT_RRE : 0);
+                       qp_context->params2 |=
+                               cpu_to_be32(attr->qp_access_flags & IB_ACCESS_REMOTE_ATOMIC ?
+                                           MTHCA_QP_BIT_RAE : 0);
+               }
+
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_RWE |
+                                                       MTHCA_QP_OPTPAR_RRE |
+                                                       MTHCA_QP_OPTPAR_RAE);
+
+               qp->atomic_rd_en = attr->qp_access_flags;
+       }
+
+       if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) {
+               u8 rra_max;
+
+               if (qp->resp_depth && !attr->max_rd_atomic) {
+                       /*
+                        * Lowering our responder resources to zero.
+                        * Turn off RDMA/atomics as responder.
+                        * (RWE/RRE/RAE in params2 already zero)
+                        */
+                       qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_RWE |
+                                                               MTHCA_QP_OPTPAR_RRE |
+                                                               MTHCA_QP_OPTPAR_RAE);
+               }
+
+               if (!qp->resp_depth && attr->max_rd_atomic) {
+                       /*
+                        * Increasing our responder resources from
+                        * zero.  Turn on RDMA/atomics as appropriate.
+                        */
+                       qp_context->params2 |=
+                               cpu_to_be32(qp->atomic_rd_en & IB_ACCESS_REMOTE_WRITE ?
+                                           MTHCA_QP_BIT_RWE : 0);
+                       qp_context->params2 |=
+                               cpu_to_be32(qp->atomic_rd_en & IB_ACCESS_REMOTE_READ ?
+                                           MTHCA_QP_BIT_RRE : 0);
+                       qp_context->params2 |=
+                               cpu_to_be32(qp->atomic_rd_en & IB_ACCESS_REMOTE_ATOMIC ?
+                                           MTHCA_QP_BIT_RAE : 0);
+
+                       qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_RWE |
+                                                               MTHCA_QP_OPTPAR_RRE |
+                                                               MTHCA_QP_OPTPAR_RAE);
+               }
+
+               for (rra_max = 0;
+                    1 << rra_max < attr->max_rd_atomic &&
+                            rra_max < dev->qp_table.rdb_shift;
+                    ++rra_max)
+                       ; /* nothing */
+
+               qp_context->params2      |= cpu_to_be32(rra_max << 21);
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_RRA_MAX);
+
+               qp->resp_depth = attr->max_rd_atomic;
+       }
+
+       if (qp->rq.policy == IB_SIGNAL_ALL_WR)
+               qp_context->params2 |= cpu_to_be32(MTHCA_QP_BIT_RSC);
+       if (attr_mask & IB_QP_MIN_RNR_TIMER) {
+               qp_context->rnr_nextrecvpsn |= cpu_to_be32(attr->min_rnr_timer << 24);
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_RNR_TIMEOUT);
+       }
+       if (attr_mask & IB_QP_RQ_PSN)
+               qp_context->rnr_nextrecvpsn |= cpu_to_be32(attr->rq_psn);
+
+       qp_context->ra_buff_indx = dev->qp_table.rdb_base +
+               ((qp->qpn & (dev->limits.num_qps - 1)) * MTHCA_RDB_ENTRY_SIZE <<
+                dev->qp_table.rdb_shift);
+
+       qp_context->cqn_rcv = cpu_to_be32(to_mcq(ibqp->recv_cq)->cqn);
+
+       if (attr_mask & IB_QP_QKEY) {
+               qp_context->qkey = cpu_to_be32(attr->qkey);
+               qp_param->opt_param_mask |= cpu_to_be32(MTHCA_QP_OPTPAR_Q_KEY);
+       }
+
+       err = mthca_MODIFY_QP(dev, state_table[cur_state][new_state].trans,
+                             qp->qpn, 0, qp_param, 0, &status);
+       if (status) {
+               mthca_warn(dev, "modify QP %d returned status %02x.\n",
+                          state_table[cur_state][new_state].trans, status);
+               err = -EINVAL;
+       }
+
+       if (!err)
+               qp->state = new_state;
+
+       kfree(mailbox);
+
+       if (is_sqp(dev, qp))
+               store_attrs(to_msqp(qp), attr, attr_mask);
+
+       /*
+        * If we are moving QP0 to RTR, bring the IB link up; if we
+        * are moving QP0 to RESET or ERROR, bring the link back down.
+        */
+       if (is_qp0(dev, qp)) {
+               if (cur_state != IB_QPS_RTR &&
+                   new_state == IB_QPS_RTR)
+                       init_port(dev, to_msqp(qp)->port);
+
+               if (cur_state != IB_QPS_RESET &&
+                   cur_state != IB_QPS_ERR &&
+                   (new_state == IB_QPS_RESET ||
+                    new_state == IB_QPS_ERR))
+                       mthca_CLOSE_IB(dev, to_msqp(qp)->port, &status);
+       }
+
+       return err;
+}
+
+/*
+ * Allocate and register buffer for WQEs.  qp->rq.max, sq.max,
+ * rq.max_gs and sq.max_gs must all be assigned.
+ * mthca_alloc_wqe_buf will calculate rq.wqe_shift and
+ * sq.wqe_shift (as well as send_wqe_offset, is_direct, and
+ * queue)
+ */
+static int mthca_alloc_wqe_buf(struct mthca_dev *dev,
+                              struct mthca_pd *pd,
+                              struct mthca_qp *qp)
+{
+       int size;
+       int i;
+       int npages, shift;
+       dma_addr_t t;
+       u64 *dma_list = NULL;
+       int err = -ENOMEM;
+
+       size = sizeof (struct mthca_next_seg) +
+               qp->rq.max_gs * sizeof (struct mthca_data_seg);
+
+       for (qp->rq.wqe_shift = 6; 1 << qp->rq.wqe_shift < size;
+            qp->rq.wqe_shift++)
+               ; /* nothing */
+
+       size = sizeof (struct mthca_next_seg) +
+               qp->sq.max_gs * sizeof (struct mthca_data_seg);
+       if (qp->transport == MLX)
+               size += 2 * sizeof (struct mthca_data_seg);
+       else if (qp->transport == UD)
+               size += sizeof (struct mthca_ud_seg);
+       else /* bind seg is as big as atomic + raddr segs */
+               size += sizeof (struct mthca_bind_seg);
+
+       for (qp->sq.wqe_shift = 6; 1 << qp->sq.wqe_shift < size;
+            qp->sq.wqe_shift++)
+               ; /* nothing */
+
+       qp->send_wqe_offset = ALIGN(qp->rq.max << qp->rq.wqe_shift,
+                                   1 << qp->sq.wqe_shift);
+       size = PAGE_ALIGN(qp->send_wqe_offset +
+                         (qp->sq.max << qp->sq.wqe_shift));
+
+       qp->wrid = kmalloc((qp->rq.max + qp->sq.max) * sizeof (u64),
+                          GFP_KERNEL);
+       if (!qp->wrid)
+               goto err_out;
+
+       if (size <= MTHCA_MAX_DIRECT_QP_SIZE) {
+               qp->is_direct = 1;
+               npages = 1;
+               shift = get_order(size) + PAGE_SHIFT;
+
+               if (0)
+                       mthca_dbg(dev, "Creating direct QP of size %d (shift %d)\n",
+                                 size, shift);
+
+               qp->queue.direct.buf = pci_alloc_consistent(dev->pdev, size, &t);
+               if (!qp->queue.direct.buf)
+                       goto err_out;
+
+               pci_unmap_addr_set(&qp->queue.direct, mapping, t);
+
+               memset(qp->queue.direct.buf, 0, size);
+
+               while (t & ((1 << shift) - 1)) {
+                       --shift;
+                       npages *= 2;
+               }
+
+               dma_list = kmalloc(npages * sizeof *dma_list, GFP_KERNEL);
+               if (!dma_list)
+                       goto err_out_free;
+
+               for (i = 0; i < npages; ++i)
+                       dma_list[i] = t + i * (1 << shift);
+       } else {
+               qp->is_direct = 0;
+               npages = size / PAGE_SIZE;
+               shift = PAGE_SHIFT;
+
+               if (0)
+                       mthca_dbg(dev, "Creating indirect QP with %d pages\n", npages);
+
+               dma_list = kmalloc(npages * sizeof *dma_list, GFP_KERNEL);
+               if (!dma_list)
+                       goto err_out;
+
+               qp->queue.page_list = kmalloc(npages *
+                                             sizeof *qp->queue.page_list,
+                                             GFP_KERNEL);
+               if (!qp->queue.page_list)
+                       goto err_out;
+
+               for (i = 0; i < npages; ++i) {
+                       qp->queue.page_list[i].buf =
+                               pci_alloc_consistent(dev->pdev, PAGE_SIZE, &t);
+                       if (!qp->queue.page_list[i].buf)
+                               goto err_out_free;
+
+                       memset(qp->queue.page_list[i].buf, 0, PAGE_SIZE);
+
+                       pci_unmap_addr_set(&qp->queue.page_list[i], mapping, t);
+                       dma_list[i] = t;
+               }
+       }
+
+       err = mthca_mr_alloc_phys(dev, pd->pd_num, dma_list, shift,
+                                 npages, 0, size,
+                                 MTHCA_MPT_FLAG_LOCAL_WRITE |
+                                 MTHCA_MPT_FLAG_LOCAL_READ,
+                                 &qp->mr);
+       if (err)
+               goto err_out_free;
+
+       kfree(dma_list);
+       return 0;
+
+ err_out_free:
+       if (qp->is_direct) {
+               pci_free_consistent(dev->pdev, size,
+                                   qp->queue.direct.buf,
+                                   pci_unmap_addr(&qp->queue.direct, mapping));
+       } else
+               for (i = 0; i < npages; ++i) {
+                       if (qp->queue.page_list[i].buf)
+                               pci_free_consistent(dev->pdev, PAGE_SIZE,
+                                                   qp->queue.page_list[i].buf,
+                                                   pci_unmap_addr(&qp->queue.page_list[i],
+                                                                  mapping));
+
+               }
+
+ err_out:
+       kfree(qp->wrid);
+       kfree(dma_list);
+       return err;
+}
+
+static int mthca_alloc_qp_common(struct mthca_dev *dev,
+                                struct mthca_pd *pd,
+                                struct mthca_cq *send_cq,
+                                struct mthca_cq *recv_cq,
+                                enum ib_sig_type send_policy,
+                                enum ib_sig_type recv_policy,
+                                struct mthca_qp *qp)
+{
+       int err;
+
+       spin_lock_init(&qp->lock);
+       atomic_set(&qp->refcount, 1);
+       qp->state        = IB_QPS_RESET;
+       qp->atomic_rd_en = 0;
+       qp->resp_depth   = 0;
+       qp->sq.policy    = send_policy;
+       qp->rq.policy    = recv_policy;
+       qp->rq.cur       = 0;
+       qp->sq.cur       = 0;
+       qp->rq.next      = 0;
+       qp->sq.next      = 0;
+       qp->rq.last_comp = qp->rq.max - 1;
+       qp->sq.last_comp = qp->sq.max - 1;
+       qp->rq.last      = NULL;
+       qp->sq.last      = NULL;
+
+       err = mthca_alloc_wqe_buf(dev, pd, qp);
+       return err;
+}
+
+int mthca_alloc_qp(struct mthca_dev *dev,
+                  struct mthca_pd *pd,
+                  struct mthca_cq *send_cq,
+                  struct mthca_cq *recv_cq,
+                  enum ib_qp_type type,
+                  enum ib_sig_type send_policy,
+                  enum ib_sig_type recv_policy,
+                  struct mthca_qp *qp)
+{
+       int err;
+
+       switch (type) {
+       case IB_QPT_RC: qp->transport = RC; break;
+       case IB_QPT_UC: qp->transport = UC; break;
+       case IB_QPT_UD: qp->transport = UD; break;
+       default: return -EINVAL;
+       }
+
+       qp->qpn = mthca_alloc(&dev->qp_table.alloc);
+       if (qp->qpn == -1)
+               return -ENOMEM;
+
+       err = mthca_alloc_qp_common(dev, pd, send_cq, recv_cq,
+                                   send_policy, recv_policy, qp);
+       if (err) {
+               mthca_free(&dev->qp_table.alloc, qp->qpn);
+               return err;
+       }
+
+       spin_lock_irq(&dev->qp_table.lock);
+       mthca_array_set(&dev->qp_table.qp,
+                       qp->qpn & (dev->limits.num_qps - 1), qp);
+       spin_unlock_irq(&dev->qp_table.lock);
+
+       return 0;
+}
+
+int mthca_alloc_sqp(struct mthca_dev *dev,
+                   struct mthca_pd *pd,
+                   struct mthca_cq *send_cq,
+                   struct mthca_cq *recv_cq,
+                   enum ib_sig_type send_policy,
+                   enum ib_sig_type recv_policy,
+                   int qpn,
+                   int port,
+                   struct mthca_sqp *sqp)
+{
+       int err = 0;
+       u32 mqpn = qpn * 2 + dev->qp_table.sqp_start + port - 1;
+
+       sqp->header_buf_size = sqp->qp.sq.max * MTHCA_UD_HEADER_SIZE;
+       sqp->header_buf = dma_alloc_coherent(&dev->pdev->dev, sqp->header_buf_size,
+                                            &sqp->header_dma, GFP_KERNEL);
+       if (!sqp->header_buf)
+               return -ENOMEM;
+
+       spin_lock_irq(&dev->qp_table.lock);
+       if (mthca_array_get(&dev->qp_table.qp, mqpn))
+               err = -EBUSY;
+       else
+               mthca_array_set(&dev->qp_table.qp, mqpn, sqp);
+       spin_unlock_irq(&dev->qp_table.lock);
+
+       if (err)
+               goto err_out;
+
+       sqp->port = port;
+       sqp->qp.qpn       = mqpn;
+       sqp->qp.transport = MLX;
+
+       err = mthca_alloc_qp_common(dev, pd, send_cq, recv_cq,
+                                   send_policy, recv_policy,
+                                   &sqp->qp);
+       if (err)
+               goto err_out_free;
+
+       atomic_inc(&pd->sqp_count);
+
+       return 0;
+
+ err_out_free:
+       spin_lock_irq(&dev->qp_table.lock);
+       mthca_array_clear(&dev->qp_table.qp, mqpn);
+       spin_unlock_irq(&dev->qp_table.lock);
+
+ err_out:
+       dma_free_coherent(&dev->pdev->dev, sqp->header_buf_size,
+                         sqp->header_buf, sqp->header_dma);
+
+       return err;
+}
+
+void mthca_free_qp(struct mthca_dev *dev,
+                  struct mthca_qp *qp)
+{
+       u8 status;
+       int size;
+       int i;
+
+       spin_lock_irq(&dev->qp_table.lock);
+       mthca_array_clear(&dev->qp_table.qp,
+                         qp->qpn & (dev->limits.num_qps - 1));
+       spin_unlock_irq(&dev->qp_table.lock);
+
+       atomic_dec(&qp->refcount);
+       wait_event(qp->wait, !atomic_read(&qp->refcount));
+
+       if (qp->state != IB_QPS_RESET)
+               mthca_MODIFY_QP(dev, MTHCA_TRANS_ANY2RST, qp->qpn, 0, NULL, 0, &status);
+
+       mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq)->cqn, qp->qpn);
+       if (qp->ibqp.send_cq != qp->ibqp.recv_cq)
+               mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq)->cqn, qp->qpn);
+
+       mthca_free_mr(dev, &qp->mr);
+
+       size = PAGE_ALIGN(qp->send_wqe_offset +
+                         (qp->sq.max << qp->sq.wqe_shift));
+
+       if (qp->is_direct) {
+               pci_free_consistent(dev->pdev, size,
+                                   qp->queue.direct.buf,
+                                   pci_unmap_addr(&qp->queue.direct, mapping));
+       } else {
+               for (i = 0; i < size / PAGE_SIZE; ++i) {
+                       pci_free_consistent(dev->pdev, PAGE_SIZE,
+                                           qp->queue.page_list[i].buf,
+                                           pci_unmap_addr(&qp->queue.page_list[i],
+                                                          mapping));
+               }
+       }
+
+       kfree(qp->wrid);
+
+       if (is_sqp(dev, qp)) {
+               atomic_dec(&(to_mpd(qp->ibqp.pd)->sqp_count));
+               dma_free_coherent(&dev->pdev->dev,
+                                 to_msqp(qp)->header_buf_size,
+                                 to_msqp(qp)->header_buf,
+                                 to_msqp(qp)->header_dma);
+       }
+       else
+               mthca_free(&dev->qp_table.alloc, qp->qpn);
+}
+
+/* Create UD header for an MLX send and build a data segment for it */
+static int build_mlx_header(struct mthca_dev *dev, struct mthca_sqp *sqp,
+                           int ind, struct ib_send_wr *wr,
+                           struct mthca_mlx_seg *mlx,
+                           struct mthca_data_seg *data)
+{
+       int header_size;
+       int err;
+
+       ib_ud_header_init(256, /* assume a MAD */
+                         sqp->ud_header.grh_present,
+                         &sqp->ud_header);
+
+       err = mthca_read_ah(dev, to_mah(wr->wr.ud.ah), &sqp->ud_header);
+       if (err)
+               return err;
+       mlx->flags &= ~cpu_to_be32(MTHCA_NEXT_SOLICIT | 1);
+       mlx->flags |= cpu_to_be32((!sqp->qp.ibqp.qp_num ? MTHCA_MLX_VL15 : 0) |
+                                 (sqp->ud_header.lrh.destination_lid == 0xffff ?
+                                  MTHCA_MLX_SLR : 0) |
+                                 (sqp->ud_header.lrh.service_level << 8));
+       mlx->rlid = sqp->ud_header.lrh.destination_lid;
+       mlx->vcrc = 0;
+
+       switch (wr->opcode) {
+       case IB_WR_SEND:
+               sqp->ud_header.bth.opcode = IB_OPCODE_UD_SEND_ONLY;
+               sqp->ud_header.immediate_present = 0;
+               break;
+       case IB_WR_SEND_WITH_IMM:
+               sqp->ud_header.bth.opcode = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE;
+               sqp->ud_header.immediate_present = 1;
+               sqp->ud_header.immediate_data = wr->imm_data;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       sqp->ud_header.lrh.virtual_lane    = !sqp->qp.ibqp.qp_num ? 15 : 0;
+       if (sqp->ud_header.lrh.destination_lid == 0xffff)
+               sqp->ud_header.lrh.source_lid = 0xffff;
+       sqp->ud_header.bth.solicited_event = !!(wr->send_flags & IB_SEND_SOLICITED);
+       if (!sqp->qp.ibqp.qp_num)
+               ib_get_cached_pkey(&dev->ib_dev, sqp->port,
+                                  sqp->pkey_index,
+                                  &sqp->ud_header.bth.pkey);
+       else
+               ib_get_cached_pkey(&dev->ib_dev, sqp->port,
+                                  wr->wr.ud.pkey_index,
+                                  &sqp->ud_header.bth.pkey);
+       cpu_to_be16s(&sqp->ud_header.bth.pkey);
+       sqp->ud_header.bth.destination_qpn = cpu_to_be32(wr->wr.ud.remote_qpn);
+       sqp->ud_header.bth.psn = cpu_to_be32((sqp->send_psn++) & ((1 << 24) - 1));
+       sqp->ud_header.deth.qkey = cpu_to_be32(wr->wr.ud.remote_qkey & 0x80000000 ?
+                                              sqp->qkey : wr->wr.ud.remote_qkey);
+       sqp->ud_header.deth.source_qpn = cpu_to_be32(sqp->qp.ibqp.qp_num);
+
+       header_size = ib_ud_header_pack(&sqp->ud_header,
+                                       sqp->header_buf +
+                                       ind * MTHCA_UD_HEADER_SIZE);
+
+       data->byte_count = cpu_to_be32(header_size);
+       data->lkey       = cpu_to_be32(to_mpd(sqp->qp.ibqp.pd)->ntmr.ibmr.lkey);
+       data->addr       = cpu_to_be64(sqp->header_dma +
+                                      ind * MTHCA_UD_HEADER_SIZE);
+
+       return 0;
+}
+
+int mthca_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                   struct ib_send_wr **bad_wr)
+{
+       struct mthca_dev *dev = to_mdev(ibqp->device);
+       struct mthca_qp *qp = to_mqp(ibqp);
+       void *wqe;
+       void *prev_wqe;
+       unsigned long flags;
+       int err = 0;
+       int nreq;
+       int i;
+       int size;
+       int size0 = 0;
+       u32 f0 = 0;
+       int ind;
+       u8 op0 = 0;
+
+       static const u8 opcode[] = {
+               [IB_WR_SEND]                 = MTHCA_OPCODE_SEND,
+               [IB_WR_SEND_WITH_IMM]        = MTHCA_OPCODE_SEND_IMM,
+               [IB_WR_RDMA_WRITE]           = MTHCA_OPCODE_RDMA_WRITE,
+               [IB_WR_RDMA_WRITE_WITH_IMM]  = MTHCA_OPCODE_RDMA_WRITE_IMM,
+               [IB_WR_RDMA_READ]            = MTHCA_OPCODE_RDMA_READ,
+               [IB_WR_ATOMIC_CMP_AND_SWP]   = MTHCA_OPCODE_ATOMIC_CS,
+               [IB_WR_ATOMIC_FETCH_AND_ADD] = MTHCA_OPCODE_ATOMIC_FA,
+       };
+
+       spin_lock_irqsave(&qp->lock, flags);
+
+       /* XXX check that state is OK to post send */
+
+       ind = qp->sq.next;
+
+       for (nreq = 0; wr; ++nreq, wr = wr->next) {
+               if (qp->sq.cur + nreq >= qp->sq.max) {
+                       mthca_err(dev, "SQ full (%d posted, %d max, %d nreq)\n",
+                                 qp->sq.cur, qp->sq.max, nreq);
+                       err = -ENOMEM;
+                       *bad_wr = wr;
+                       goto out;
+               }
+
+               wqe = get_send_wqe(qp, ind);
+               prev_wqe = qp->sq.last;
+               qp->sq.last = wqe;
+
+               ((struct mthca_next_seg *) wqe)->nda_op = 0;
+               ((struct mthca_next_seg *) wqe)->ee_nds = 0;
+               ((struct mthca_next_seg *) wqe)->flags =
+                       ((wr->send_flags & IB_SEND_SIGNALED) ?
+                        cpu_to_be32(MTHCA_NEXT_CQ_UPDATE) : 0) |
+                       ((wr->send_flags & IB_SEND_SOLICITED) ?
+                        cpu_to_be32(MTHCA_NEXT_SOLICIT) : 0)   |
+                       cpu_to_be32(1);
+               if (wr->opcode == IB_WR_SEND_WITH_IMM ||
+                   wr->opcode == IB_WR_RDMA_WRITE_WITH_IMM)
+                       ((struct mthca_next_seg *) wqe)->flags = wr->imm_data;
+
+               wqe += sizeof (struct mthca_next_seg);
+               size = sizeof (struct mthca_next_seg) / 16;
+
+               switch (qp->transport) {
+               case RC:
+                       switch (wr->opcode) {
+                       case IB_WR_ATOMIC_CMP_AND_SWP:
+                       case IB_WR_ATOMIC_FETCH_AND_ADD:
+                               ((struct mthca_raddr_seg *) wqe)->raddr =
+                                       cpu_to_be64(wr->wr.atomic.remote_addr);
+                               ((struct mthca_raddr_seg *) wqe)->rkey =
+                                       cpu_to_be32(wr->wr.atomic.rkey);
+                               ((struct mthca_raddr_seg *) wqe)->reserved = 0;
+
+                               wqe += sizeof (struct mthca_raddr_seg);
+
+                               if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) {
+                                       ((struct mthca_atomic_seg *) wqe)->swap_add =
+                                               cpu_to_be64(wr->wr.atomic.swap);
+                                       ((struct mthca_atomic_seg *) wqe)->compare =
+                                               cpu_to_be64(wr->wr.atomic.compare_add);
+                               } else {
+                                       ((struct mthca_atomic_seg *) wqe)->swap_add =
+                                               cpu_to_be64(wr->wr.atomic.compare_add);
+                                       ((struct mthca_atomic_seg *) wqe)->compare = 0;
+                               }
+
+                               wqe += sizeof (struct mthca_atomic_seg);
+                               size += sizeof (struct mthca_raddr_seg) / 16 +
+                                       sizeof (struct mthca_atomic_seg);
+                               break;
+
+                       case IB_WR_RDMA_WRITE:
+                       case IB_WR_RDMA_WRITE_WITH_IMM:
+                       case IB_WR_RDMA_READ:
+                               ((struct mthca_raddr_seg *) wqe)->raddr =
+                                       cpu_to_be64(wr->wr.rdma.remote_addr);
+                               ((struct mthca_raddr_seg *) wqe)->rkey =
+                                       cpu_to_be32(wr->wr.rdma.rkey);
+                               ((struct mthca_raddr_seg *) wqe)->reserved = 0;
+                               wqe += sizeof (struct mthca_raddr_seg);
+                               size += sizeof (struct mthca_raddr_seg) / 16;
+                               break;
+
+                       default:
+                               /* No extra segments required for sends */
+                               break;
+                       }
+
+                       break;
+
+               case UD:
+                       ((struct mthca_ud_seg *) wqe)->lkey =
+                               cpu_to_be32(to_mah(wr->wr.ud.ah)->key);
+                       ((struct mthca_ud_seg *) wqe)->av_addr =
+                               cpu_to_be64(to_mah(wr->wr.ud.ah)->avdma);
+                       ((struct mthca_ud_seg *) wqe)->dqpn =
+                               cpu_to_be32(wr->wr.ud.remote_qpn);
+                       ((struct mthca_ud_seg *) wqe)->qkey =
+                               cpu_to_be32(wr->wr.ud.remote_qkey);
+
+                       wqe += sizeof (struct mthca_ud_seg);
+                       size += sizeof (struct mthca_ud_seg) / 16;
+                       break;
+
+               case MLX:
+                       err = build_mlx_header(dev, to_msqp(qp), ind, wr,
+                                              wqe - sizeof (struct mthca_next_seg),
+                                              wqe);
+                       if (err) {
+                               *bad_wr = wr;
+                               goto out;
+                       }
+                       wqe += sizeof (struct mthca_data_seg);
+                       size += sizeof (struct mthca_data_seg) / 16;
+                       break;
+               }
+
+               if (wr->num_sge > qp->sq.max_gs) {
+                       mthca_err(dev, "too many gathers\n");
+                       err = -EINVAL;
+                       *bad_wr = wr;
+                       goto out;
+               }
+
+               for (i = 0; i < wr->num_sge; ++i) {
+                       ((struct mthca_data_seg *) wqe)->byte_count =
+                               cpu_to_be32(wr->sg_list[i].length);
+                       ((struct mthca_data_seg *) wqe)->lkey =
+                               cpu_to_be32(wr->sg_list[i].lkey);
+                       ((struct mthca_data_seg *) wqe)->addr =
+                               cpu_to_be64(wr->sg_list[i].addr);
+                       wqe += sizeof (struct mthca_data_seg);
+                       size += sizeof (struct mthca_data_seg) / 16;
+               }
+
+               /* Add one more inline data segment for ICRC */
+               if (qp->transport == MLX) {
+                       ((struct mthca_data_seg *) wqe)->byte_count =
+                               cpu_to_be32((1 << 31) | 4);
+                       ((u32 *) wqe)[1] = 0;
+                       wqe += sizeof (struct mthca_data_seg);
+                       size += sizeof (struct mthca_data_seg) / 16;
+               }
+
+               qp->wrid[ind + qp->rq.max] = wr->wr_id;
+
+               if (wr->opcode >= ARRAY_SIZE(opcode)) {
+                       mthca_err(dev, "opcode invalid\n");
+                       err = -EINVAL;
+                       *bad_wr = wr;
+                       goto out;
+               }
+
+               if (prev_wqe) {
+                       ((struct mthca_next_seg *) prev_wqe)->nda_op =
+                               cpu_to_be32(((ind << qp->sq.wqe_shift) +
+                                            qp->send_wqe_offset) |
+                                           opcode[wr->opcode]);
+                       smp_wmb();
+                       ((struct mthca_next_seg *) prev_wqe)->ee_nds =
+                               cpu_to_be32((size0 ? 0 : MTHCA_NEXT_DBD) | size);
+               }
+
+               if (!size0) {
+                       size0 = size;
+                       op0   = opcode[wr->opcode];
+               }
+
+               ++ind;
+               if (unlikely(ind >= qp->sq.max))
+                       ind -= qp->sq.max;
+       }
+
+out:
+       if (nreq) {
+               u32 doorbell[2];
+
+               doorbell[0] = cpu_to_be32(((qp->sq.next << qp->sq.wqe_shift) +
+                                          qp->send_wqe_offset) | f0 | op0);
+               doorbell[1] = cpu_to_be32((qp->qpn << 8) | size0);
+
+               wmb();
+
+               mthca_write64(doorbell,
+                             dev->kar + MTHCA_SEND_DOORBELL,
+                             MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock));
+       }
+
+       qp->sq.cur += nreq;
+       qp->sq.next = ind;
+
+       spin_unlock_irqrestore(&qp->lock, flags);
+       return err;
+}
+
+int mthca_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+                      struct ib_recv_wr **bad_wr)
+{
+       struct mthca_dev *dev = to_mdev(ibqp->device);
+       struct mthca_qp *qp = to_mqp(ibqp);
+       unsigned long flags;
+       int err = 0;
+       int nreq;
+       int i;
+       int size;
+       int size0 = 0;
+       int ind;
+       void *wqe;
+       void *prev_wqe;
+
+       spin_lock_irqsave(&qp->lock, flags);
+
+       /* XXX check that state is OK to post receive */
+
+       ind = qp->rq.next;
+
+       for (nreq = 0; wr; ++nreq, wr = wr->next) {
+               if (qp->rq.cur + nreq >= qp->rq.max) {
+                       mthca_err(dev, "RQ %06x full\n", qp->qpn);
+                       err = -ENOMEM;
+                       *bad_wr = wr;
+                       goto out;
+               }
+
+               wqe = get_recv_wqe(qp, ind);
+               prev_wqe = qp->rq.last;
+               qp->rq.last = wqe;
+
+               ((struct mthca_next_seg *) wqe)->nda_op = 0;
+               ((struct mthca_next_seg *) wqe)->ee_nds =
+                       cpu_to_be32(MTHCA_NEXT_DBD);
+               ((struct mthca_next_seg *) wqe)->flags =
+                       (wr->recv_flags & IB_RECV_SIGNALED) ?
+                       cpu_to_be32(MTHCA_NEXT_CQ_UPDATE) : 0;
+
+               wqe += sizeof (struct mthca_next_seg);
+               size = sizeof (struct mthca_next_seg) / 16;
+
+               if (wr->num_sge > qp->rq.max_gs) {
+                       err = -EINVAL;
+                       *bad_wr = wr;
+                       goto out;
+               }
+
+               for (i = 0; i < wr->num_sge; ++i) {
+                       ((struct mthca_data_seg *) wqe)->byte_count =
+                               cpu_to_be32(wr->sg_list[i].length);
+                       ((struct mthca_data_seg *) wqe)->lkey =
+                               cpu_to_be32(wr->sg_list[i].lkey);
+                       ((struct mthca_data_seg *) wqe)->addr =
+                               cpu_to_be64(wr->sg_list[i].addr);
+                       wqe += sizeof (struct mthca_data_seg);
+                       size += sizeof (struct mthca_data_seg) / 16;
+               }
+
+               qp->wrid[ind] = wr->wr_id;
+
+               if (prev_wqe) {
+                       ((struct mthca_next_seg *) prev_wqe)->nda_op =
+                               cpu_to_be32((ind << qp->rq.wqe_shift) | 1);
+                       smp_wmb();
+                       ((struct mthca_next_seg *) prev_wqe)->ee_nds =
+                               cpu_to_be32(MTHCA_NEXT_DBD | size);
+               }
+
+               if (!size0)
+                       size0 = size;
+
+               ++ind;
+               if (unlikely(ind >= qp->rq.max))
+                       ind -= qp->rq.max;
+       }
+
+out:
+       if (nreq) {
+               u32 doorbell[2];
+
+               doorbell[0] = cpu_to_be32((qp->rq.next << qp->rq.wqe_shift) | size0);
+               doorbell[1] = cpu_to_be32((qp->qpn << 8) | nreq);
+
+               wmb();
+
+               mthca_write64(doorbell,
+                             dev->kar + MTHCA_RECEIVE_DOORBELL,
+                             MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock));
+       }
+
+       qp->rq.cur += nreq;
+       qp->rq.next = ind;
+
+       spin_unlock_irqrestore(&qp->lock, flags);
+       return err;
+}
+
+int mthca_free_err_wqe(struct mthca_qp *qp, int is_send,
+                      int index, int *dbd, u32 *new_wqe)
+{
+       struct mthca_next_seg *next;
+
+       if (is_send)
+               next = get_send_wqe(qp, index);
+       else
+               next = get_recv_wqe(qp, index);
+
+       *dbd = !!(next->ee_nds & cpu_to_be32(MTHCA_NEXT_DBD));
+       if (next->ee_nds & cpu_to_be32(0x3f))
+               *new_wqe = (next->nda_op & cpu_to_be32(~0x3f)) |
+                       (next->ee_nds & cpu_to_be32(0x3f));
+       else
+               *new_wqe = 0;
+
+       return 0;
+}
+
+int __devinit mthca_init_qp_table(struct mthca_dev *dev)
+{
+       int err;
+       u8 status;
+       int i;
+
+       spin_lock_init(&dev->qp_table.lock);
+
+       /*
+        * We reserve 2 extra QPs per port for the special QPs.  The
+        * special QP for port 1 has to be even, so round up.
+        */
+       dev->qp_table.sqp_start = (dev->limits.reserved_qps + 1) & ~1UL;
+       err = mthca_alloc_init(&dev->qp_table.alloc,
+                              dev->limits.num_qps,
+                              (1 << 24) - 1,
+                              dev->qp_table.sqp_start +
+                              MTHCA_MAX_PORTS * 2);
+       if (err)
+               return err;
+
+       err = mthca_array_init(&dev->qp_table.qp,
+                              dev->limits.num_qps);
+       if (err) {
+               mthca_alloc_cleanup(&dev->qp_table.alloc);
+               return err;
+       }
+
+       for (i = 0; i < 2; ++i) {
+               err = mthca_CONF_SPECIAL_QP(dev, i ? IB_QPT_GSI : IB_QPT_SMI,
+                                           dev->qp_table.sqp_start + i * 2,
+                                           &status);
+               if (err)
+                       goto err_out;
+               if (status) {
+                       mthca_warn(dev, "CONF_SPECIAL_QP returned "
+                                  "status %02x, aborting.\n",
+                                  status);
+                       err = -EINVAL;
+                       goto err_out;
+               }
+       }
+       return 0;
+
+ err_out:
+       for (i = 0; i < 2; ++i)
+               mthca_CONF_SPECIAL_QP(dev, i, 0, &status);
+
+       mthca_array_cleanup(&dev->qp_table.qp, dev->limits.num_qps);
+       mthca_alloc_cleanup(&dev->qp_table.alloc);
+
+       return err;
+}
+
+void __devexit mthca_cleanup_qp_table(struct mthca_dev *dev)
+{
+       int i;
+       u8 status;
+
+       for (i = 0; i < 2; ++i)
+               mthca_CONF_SPECIAL_QP(dev, i, 0, &status);
+
+       mthca_alloc_cleanup(&dev->qp_table.alloc);
+}
diff --git a/drivers/infiniband/hw/mthca/mthca_reset.c b/drivers/infiniband/hw/mthca/mthca_reset.c
new file mode 100644 (file)
index 0000000..35df81c
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: mthca_reset.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "mthca_dev.h"
+#include "mthca_cmd.h"
+
+int mthca_reset(struct mthca_dev *mdev)
+{
+       int i;
+       int err = 0;
+       u32 *hca_header    = NULL;
+       u32 *bridge_header = NULL;
+       struct pci_dev *bridge = NULL;
+
+#define MTHCA_RESET_OFFSET 0xf0010
+#define MTHCA_RESET_VALUE  cpu_to_be32(1)
+
+       /*
+        * Reset the chip.  This is somewhat ugly because we have to
+        * save off the PCI header before reset and then restore it
+        * after the chip reboots.  We skip config space offsets 22
+        * and 23 since those have a special meaning.
+        *
+        * To make matters worse, for Tavor (PCI-X HCA) we have to
+        * find the associated bridge device and save off its PCI
+        * header as well.
+        */
+
+       if (mdev->hca_type == TAVOR) {
+               /* Look for the bridge -- its device ID will be 2 more
+                  than HCA's device ID. */
+               while ((bridge = pci_get_device(mdev->pdev->vendor,
+                                               mdev->pdev->device + 2,
+                                               bridge)) != NULL) {
+                       if (bridge->hdr_type    == PCI_HEADER_TYPE_BRIDGE &&
+                           bridge->subordinate == mdev->pdev->bus) {
+                               mthca_dbg(mdev, "Found bridge: %s (%s)\n",
+                                         pci_pretty_name(bridge), pci_name(bridge));
+                               break;
+                       }
+               }
+
+               if (!bridge) {
+                       /*
+                        * Didn't find a bridge for a Tavor device --
+                        * assume we're in no-bridge mode and hope for
+                        * the best.
+                        */
+                       mthca_warn(mdev, "No bridge found for %s (%s)\n",
+                                 pci_pretty_name(mdev->pdev), pci_name(mdev->pdev));
+               }
+
+       }
+
+       /* For Arbel do we need to save off the full 4K PCI Express header?? */
+       hca_header = kmalloc(256, GFP_KERNEL);
+       if (!hca_header) {
+               err = -ENOMEM;
+               mthca_err(mdev, "Couldn't allocate memory to save HCA "
+                         "PCI header, aborting.\n");
+               goto out;
+       }
+
+       for (i = 0; i < 64; ++i) {
+               if (i == 22 || i == 23)
+                       continue;
+               if (pci_read_config_dword(mdev->pdev, i * 4, hca_header + i)) {
+                       err = -ENODEV;
+                       mthca_err(mdev, "Couldn't save HCA "
+                                 "PCI header, aborting.\n");
+                       goto out;
+               }
+       }
+
+       if (bridge) {
+               bridge_header = kmalloc(256, GFP_KERNEL);
+               if (!bridge_header) {
+                       err = -ENOMEM;
+                       mthca_err(mdev, "Couldn't allocate memory to save HCA "
+                                 "bridge PCI header, aborting.\n");
+                       goto out;
+               }
+
+               for (i = 0; i < 64; ++i) {
+                       if (i == 22 || i == 23)
+                               continue;
+                       if (pci_read_config_dword(bridge, i * 4, bridge_header + i)) {
+                               err = -ENODEV;
+                               mthca_err(mdev, "Couldn't save HCA bridge "
+                                         "PCI header, aborting.\n");
+                               goto out;
+                       }
+               }
+       }
+
+       /* actually hit reset */
+       {
+               void __iomem *reset = ioremap(pci_resource_start(mdev->pdev, 0) +
+                                             MTHCA_RESET_OFFSET, 4);
+
+               if (!reset) {
+                       err = -ENOMEM;
+                       mthca_err(mdev, "Couldn't map HCA reset register, "
+                                 "aborting.\n");
+                       goto out;
+               }
+
+               writel(MTHCA_RESET_VALUE, reset);
+               iounmap(reset);
+       }
+
+       /* Docs say to wait one second before accessing device */
+       msleep(1000);
+
+       /* Now wait for PCI device to start responding again */
+       {
+               u32 v;
+               int c = 0;
+
+               for (c = 0; c < 100; ++c) {
+                       if (pci_read_config_dword(bridge ? bridge : mdev->pdev, 0, &v)) {
+                               err = -ENODEV;
+                               mthca_err(mdev, "Couldn't access HCA after reset, "
+                                         "aborting.\n");
+                               goto out;
+                       }
+
+                       if (v != 0xffffffff)
+                               goto good;
+
+                       msleep(100);
+               }
+
+               err = -ENODEV;
+               mthca_err(mdev, "PCI device did not come back after reset, "
+                         "aborting.\n");
+               goto out;
+       }
+
+good:
+       /* Now restore the PCI headers */
+       if (bridge) {
+               /*
+                * Bridge control register is at 0x3e, so we'll
+                * naturally restore it last in this loop.
+                */
+               for (i = 0; i < 16; ++i) {
+                       if (i * 4 == PCI_COMMAND)
+                               continue;
+
+                       if (pci_write_config_dword(bridge, i * 4, bridge_header[i])) {
+                               err = -ENODEV;
+                               mthca_err(mdev, "Couldn't restore HCA bridge reg %x, "
+                                         "aborting.\n", i);
+                               goto out;
+                       }
+               }
+
+               if (pci_write_config_dword(bridge, PCI_COMMAND,
+                                          bridge_header[PCI_COMMAND / 4])) {
+                       err = -ENODEV;
+                       mthca_err(mdev, "Couldn't restore HCA bridge COMMAND, "
+                                 "aborting.\n");
+                       goto out;
+               }
+       }
+
+       for (i = 0; i < 16; ++i) {
+               if (i * 4 == PCI_COMMAND)
+                       continue;
+
+               if (pci_write_config_dword(mdev->pdev, i * 4, hca_header[i])) {
+                       err = -ENODEV;
+                       mthca_err(mdev, "Couldn't restore HCA reg %x, "
+                                 "aborting.\n", i);
+                       goto out;
+               }
+       }
+
+       if (pci_write_config_dword(mdev->pdev, PCI_COMMAND,
+                                  hca_header[PCI_COMMAND / 4])) {
+               err = -ENODEV;
+               mthca_err(mdev, "Couldn't restore HCA COMMAND, "
+                         "aborting.\n");
+               goto out;
+       }
+
+out:
+       if (bridge)
+               pci_dev_put(bridge);
+       kfree(bridge_header);
+       kfree(hca_header);
+
+       return err;
+}
diff --git a/drivers/infiniband/include/ib_cache.h b/drivers/infiniband/include/ib_cache.h
new file mode 100644 (file)
index 0000000..44ef6bb
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ib_cache.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#ifndef _IB_CACHE_H
+#define _IB_CACHE_H
+
+#include <ib_verbs.h>
+
+/**
+ * ib_get_cached_gid - Returns a cached GID table entry
+ * @device: The device to query.
+ * @port_num: The port number of the device to query.
+ * @index: The index into the cached GID table to query.
+ * @gid: The GID value found at the specified index.
+ *
+ * ib_get_cached_gid() fetches the specified GID table entry stored in
+ * the local software cache.
+ */
+int ib_get_cached_gid(struct ib_device    *device,
+                     u8                   port_num,
+                     int                  index,
+                     union ib_gid        *gid);
+
+/**
+ * ib_find_cached_gid - Returns the port number and GID table index where
+ *   a specified GID value occurs.
+ * @device: The device to query.
+ * @gid: The GID value to search for.
+ * @port_num: The port number of the device where the GID value was found.
+ * @index: The index into the cached GID table where the GID was found.  This
+ *   parameter may be NULL.
+ *
+ * ib_find_cached_gid() searches for the specified GID value in
+ * the local software cache.
+ */
+int ib_find_cached_gid(struct ib_device *device,
+                      union ib_gid     *gid,
+                      u8               *port_num,
+                      u16              *index);
+
+/**
+ * ib_get_cached_pkey - Returns a cached PKey table entry
+ * @device: The device to query.
+ * @port_num: The port number of the device to query.
+ * @index: The index into the cached PKey table to query.
+ * @pkey: The PKey value found at the specified index.
+ *
+ * ib_get_cached_pkey() fetches the specified PKey table entry stored in
+ * the local software cache.
+ */
+int ib_get_cached_pkey(struct ib_device    *device_handle,
+                      u8                   port_num,
+                      int                  index,
+                      u16                 *pkey);
+
+/**
+ * ib_find_cached_pkey - Returns the PKey table index where a specified
+ *   PKey value occurs.
+ * @device: The device to query.
+ * @port_num: The port number of the device to search for the PKey.
+ * @pkey: The PKey value to search for.
+ * @index: The index into the cached PKey table where the PKey was found.
+ *
+ * ib_find_cached_pkey() searches the specified PKey table in
+ * the local software cache.
+ */
+int ib_find_cached_pkey(struct ib_device    *device,
+                       u8                   port_num,
+                       u16                  pkey,
+                       u16                 *index);
+
+#endif /* _IB_CACHE_H */
diff --git a/drivers/infiniband/include/ib_fmr_pool.h b/drivers/infiniband/include/ib_fmr_pool.h
new file mode 100644 (file)
index 0000000..e876965
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ib_fmr_pool.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#if !defined(IB_FMR_POOL_H)
+#define IB_FMR_POOL_H
+
+#include <ib_verbs.h>
+
+struct ib_fmr_pool;
+
+/**
+ * struct ib_fmr_pool_param - Parameters for creating FMR pool
+ * @max_pages_per_fmr:Maximum number of pages per map request.
+ * @access:Access flags for FMRs in pool.
+ * @pool_size:Number of FMRs to allocate for pool.
+ * @dirty_watermark:Flush is triggered when @dirty_watermark dirty
+ *     FMRs are present.
+ * @flush_function:Callback called when unmapped FMRs are flushed and
+ *     more FMRs are possibly available for mapping
+ * @flush_arg:Context passed to user's flush function.
+ * @cache:If set, FMRs may be reused after unmapping for identical map
+ *     requests.
+ */
+struct ib_fmr_pool_param {
+       int                     max_pages_per_fmr;
+       enum ib_access_flags    access;
+       int                     pool_size;
+       int                     dirty_watermark;
+       void                  (*flush_function)(struct ib_fmr_pool *pool,
+                                               void *              arg);
+       void                   *flush_arg;
+       unsigned                cache:1;
+};
+
+struct ib_pool_fmr {
+       struct ib_fmr      *fmr;
+       struct ib_fmr_pool *pool;
+       struct list_head    list;
+       struct hlist_node   cache_node;
+       int                 ref_count;
+       int                 remap_count;
+       u64                 io_virtual_address;
+       int                 page_list_len;
+       u64                 page_list[0];
+};
+
+struct ib_fmr_pool *ib_create_fmr_pool(struct ib_pd             *pd,
+                                      struct ib_fmr_pool_param *params);
+
+int ib_destroy_fmr_pool(struct ib_fmr_pool *pool);
+
+int ib_flush_fmr_pool(struct ib_fmr_pool *pool);
+
+struct ib_pool_fmr *ib_fmr_pool_map_phys(struct ib_fmr_pool *pool_handle,
+                                        u64                *page_list,
+                                        int                 list_len,
+                                        u64                *io_virtual_address);
+
+int ib_fmr_pool_unmap(struct ib_pool_fmr *fmr);
+
+#endif /* IB_FMR_POOL_H */
diff --git a/drivers/infiniband/include/ib_mad.h b/drivers/infiniband/include/ib_mad.h
new file mode 100644 (file)
index 0000000..4a6bf67
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
+ * Copyright (c) 2004 Infinicon Corporation.  All rights reserved.
+ * Copyright (c) 2004 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ * Copyright (c) 2004 Voltaire Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ib_mad.h 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#if !defined( IB_MAD_H )
+#define IB_MAD_H
+
+#include <ib_verbs.h>
+
+/* Management base version */
+#define IB_MGMT_BASE_VERSION                   1
+
+/* Management classes */
+#define IB_MGMT_CLASS_SUBN_LID_ROUTED          0x01
+#define IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE      0x81
+#define IB_MGMT_CLASS_SUBN_ADM                 0x03
+#define IB_MGMT_CLASS_PERF_MGMT                        0x04
+#define IB_MGMT_CLASS_BM                       0x05
+#define IB_MGMT_CLASS_DEVICE_MGMT              0x06
+#define IB_MGMT_CLASS_CM                       0x07
+#define IB_MGMT_CLASS_SNMP                     0x08
+#define IB_MGMT_CLASS_VENDOR_RANGE2_START      0x30
+#define IB_MGMT_CLASS_VENDOR_RANGE2_END                0x4F
+
+/* Management methods */
+#define IB_MGMT_METHOD_GET                     0x01
+#define IB_MGMT_METHOD_SET                     0x02
+#define IB_MGMT_METHOD_GET_RESP                        0x81
+#define IB_MGMT_METHOD_SEND                    0x03
+#define IB_MGMT_METHOD_TRAP                    0x05
+#define IB_MGMT_METHOD_REPORT                  0x06
+#define IB_MGMT_METHOD_REPORT_RESP             0x86
+#define IB_MGMT_METHOD_TRAP_REPRESS            0x07
+
+#define IB_MGMT_METHOD_RESP                    0x80
+
+#define IB_MGMT_MAX_METHODS                    128
+
+#define IB_QP0         0
+#define IB_QP1         __constant_htonl(1)
+#define IB_QP1_QKEY    0x80010000
+
+struct ib_grh {
+       u32             version_tclass_flow;
+       u16             paylen;
+       u8              next_hdr;
+       u8              hop_limit;
+       union ib_gid    sgid;
+       union ib_gid    dgid;
+} __attribute__ ((packed));
+
+struct ib_mad_hdr {
+       u8      base_version;
+       u8      mgmt_class;
+       u8      class_version;
+       u8      method;
+       u16     status;
+       u16     class_specific;
+       u64     tid;
+       u16     attr_id;
+       u16     resv;
+       u32     attr_mod;
+} __attribute__ ((packed));
+
+struct ib_rmpp_hdr {
+       u8      rmpp_version;
+       u8      rmpp_type;
+       u8      rmpp_rtime_flags;
+       u8      rmpp_status;
+       u32     seg_num;
+       u32     paylen_newwin;
+} __attribute__ ((packed));
+
+struct ib_mad {
+       struct ib_mad_hdr       mad_hdr;
+       u8                      data[232];
+} __attribute__ ((packed));
+
+struct ib_rmpp_mad {
+       struct ib_mad_hdr       mad_hdr;
+       struct ib_rmpp_hdr      rmpp_hdr;
+       u8                      data[220];
+} __attribute__ ((packed));
+
+struct ib_vendor_mad {
+       struct ib_mad_hdr       mad_hdr;
+       struct ib_rmpp_hdr      rmpp_hdr;
+       u8                      reserved;
+       u8                      oui[3];
+       u8                      data[216];
+} __attribute__ ((packed));
+
+struct ib_mad_agent;
+struct ib_mad_send_wc;
+struct ib_mad_recv_wc;
+
+/**
+ * ib_mad_send_handler - callback handler for a sent MAD.
+ * @mad_agent: MAD agent that sent the MAD.
+ * @mad_send_wc: Send work completion information on the sent MAD.
+ */
+typedef void (*ib_mad_send_handler)(struct ib_mad_agent *mad_agent,
+                                   struct ib_mad_send_wc *mad_send_wc);
+
+/**
+ * ib_mad_snoop_handler - Callback handler for snooping sent MADs.
+ * @mad_agent: MAD agent that snooped the MAD.
+ * @send_wr: Work request information on the sent MAD.
+ * @mad_send_wc: Work completion information on the sent MAD.  Valid
+ *   only for snooping that occurs on a send completion.
+ *
+ * Clients snooping MADs should not modify data referenced by the @send_wr
+ * or @mad_send_wc.
+ */
+typedef void (*ib_mad_snoop_handler)(struct ib_mad_agent *mad_agent,
+                                    struct ib_send_wr *send_wr,
+                                    struct ib_mad_send_wc *mad_send_wc);
+
+/**
+ * ib_mad_recv_handler - callback handler for a received MAD.
+ * @mad_agent: MAD agent requesting the received MAD.
+ * @mad_recv_wc: Received work completion information on the received MAD.
+ *
+ * MADs received in response to a send request operation will be handed to
+ * the user after the send operation completes.  All data buffers given
+ * to registered agents through this routine are owned by the receiving
+ * client, except for snooping agents.  Clients snooping MADs should not
+ * modify the data referenced by @mad_recv_wc.
+ */
+typedef void (*ib_mad_recv_handler)(struct ib_mad_agent *mad_agent,
+                                   struct ib_mad_recv_wc *mad_recv_wc);
+
+/**
+ * ib_mad_agent - Used to track MAD registration with the access layer.
+ * @device: Reference to device registration is on.
+ * @qp: Reference to QP used for sending and receiving MADs.
+ * @recv_handler: Callback handler for a received MAD.
+ * @send_handler: Callback handler for a sent MAD.
+ * @snoop_handler: Callback handler for snooped sent MADs.
+ * @context: User-specified context associated with this registration.
+ * @hi_tid: Access layer assigned transaction ID for this client.
+ *   Unsolicited MADs sent by this client will have the upper 32-bits
+ *   of their TID set to this value.
+ * @port_num: Port number on which QP is registered
+ */
+struct ib_mad_agent {
+       struct ib_device        *device;
+       struct ib_qp            *qp;
+       ib_mad_recv_handler     recv_handler;
+       ib_mad_send_handler     send_handler;
+       ib_mad_snoop_handler    snoop_handler;
+       void                    *context;
+       u32                     hi_tid;
+       u8                      port_num;
+};
+
+/**
+ * ib_mad_send_wc - MAD send completion information.
+ * @wr_id: Work request identifier associated with the send MAD request.
+ * @status: Completion status.
+ * @vendor_err: Optional vendor error information returned with a failed
+ *   request.
+ */
+struct ib_mad_send_wc {
+       u64                     wr_id;
+       enum ib_wc_status       status;
+       u32                     vendor_err;
+};
+
+/**
+ * ib_mad_recv_buf - received MAD buffer information.
+ * @list: Reference to next data buffer for a received RMPP MAD.
+ * @grh: References a data buffer containing the global route header.
+ *   The data refereced by this buffer is only valid if the GRH is
+ *   valid.
+ * @mad: References the start of the received MAD.
+ */
+struct ib_mad_recv_buf {
+       struct list_head        list;
+       struct ib_grh           *grh;
+       struct ib_mad           *mad;
+};
+
+/**
+ * ib_mad_recv_wc - received MAD information.
+ * @wc: Completion information for the received data.
+ * @recv_buf: Specifies the location of the received data buffer(s).
+ * @mad_len: The length of the received MAD, without duplicated headers.
+ *
+ * For received response, the wr_id field of the wc is set to the wr_id
+ *   for the corresponding send request.
+ */
+struct ib_mad_recv_wc {
+       struct ib_wc            *wc;
+       struct ib_mad_recv_buf  recv_buf;
+       int                     mad_len;
+};
+
+/**
+ * ib_mad_reg_req - MAD registration request
+ * @mgmt_class: Indicates which management class of MADs should be receive
+ *   by the caller.  This field is only required if the user wishes to
+ *   receive unsolicited MADs, otherwise it should be 0.
+ * @mgmt_class_version: Indicates which version of MADs for the given
+ *   management class to receive.
+ * @oui: Indicates IEEE OUI when mgmt_class is a vendor class
+ *   in the range from 0x30 to 0x4f. Otherwise not used.
+ * @method_mask: The caller will receive unsolicited MADs for any method
+ *   where @method_mask = 1.
+ */
+struct ib_mad_reg_req {
+       u8      mgmt_class;
+       u8      mgmt_class_version;
+       u8      oui[3];
+       DECLARE_BITMAP(method_mask, IB_MGMT_MAX_METHODS);
+};
+
+/**
+ * ib_register_mad_agent - Register to send/receive MADs.
+ * @device: The device to register with.
+ * @port_num: The port on the specified device to use.
+ * @qp_type: Specifies which QP to access.  Must be either
+ *   IB_QPT_SMI or IB_QPT_GSI.
+ * @mad_reg_req: Specifies which unsolicited MADs should be received
+ *   by the caller.  This parameter may be NULL if the caller only
+ *   wishes to receive solicited responses.
+ * @rmpp_version: If set, indicates that the client will send
+ *   and receive MADs that contain the RMPP header for the given version.
+ *   If set to 0, indicates that RMPP is not used by this client.
+ * @send_handler: The completion callback routine invoked after a send
+ *   request has completed.
+ * @recv_handler: The completion callback routine invoked for a received
+ *   MAD.
+ * @context: User specified context associated with the registration.
+ */
+struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
+                                          u8 port_num,
+                                          enum ib_qp_type qp_type,
+                                          struct ib_mad_reg_req *mad_reg_req,
+                                          u8 rmpp_version,
+                                          ib_mad_send_handler send_handler,
+                                          ib_mad_recv_handler recv_handler,
+                                          void *context);
+
+enum ib_mad_snoop_flags {
+       /*IB_MAD_SNOOP_POSTED_SENDS        = 1,*/
+       /*IB_MAD_SNOOP_RMPP_SENDS          = (1<<1),*/
+       IB_MAD_SNOOP_SEND_COMPLETIONS      = (1<<2),
+       /*IB_MAD_SNOOP_RMPP_SEND_COMPLETIONS = (1<<3),*/
+       IB_MAD_SNOOP_RECVS                 = (1<<4)
+       /*IB_MAD_SNOOP_RMPP_RECVS          = (1<<5),*/
+       /*IB_MAD_SNOOP_REDIRECTED_QPS      = (1<<6)*/
+};
+
+/**
+ * ib_register_mad_snoop - Register to snoop sent and received MADs.
+ * @device: The device to register with.
+ * @port_num: The port on the specified device to use.
+ * @qp_type: Specifies which QP traffic to snoop.  Must be either
+ *   IB_QPT_SMI or IB_QPT_GSI.
+ * @mad_snoop_flags: Specifies information where snooping occurs.
+ * @send_handler: The callback routine invoked for a snooped send.
+ * @recv_handler: The callback routine invoked for a snooped receive.
+ * @context: User specified context associated with the registration.
+ */
+struct ib_mad_agent *ib_register_mad_snoop(struct ib_device *device,
+                                          u8 port_num,
+                                          enum ib_qp_type qp_type,
+                                          int mad_snoop_flags,
+                                          ib_mad_snoop_handler snoop_handler,
+                                          ib_mad_recv_handler recv_handler,
+                                          void *context);
+
+/**
+ * ib_unregister_mad_agent - Unregisters a client from using MAD services.
+ * @mad_agent: Corresponding MAD registration request to deregister.
+ *
+ * After invoking this routine, MAD services are no longer usable by the
+ * client on the associated QP.
+ */
+int ib_unregister_mad_agent(struct ib_mad_agent *mad_agent);
+
+/**
+ * ib_post_send_mad - Posts MAD(s) to the send queue of the QP associated
+ *   with the registered client.
+ * @mad_agent: Specifies the associated registration to post the send to.
+ * @send_wr: Specifies the information needed to send the MAD(s).
+ * @bad_send_wr: Specifies the MAD on which an error was encountered.
+ *
+ * Sent MADs are not guaranteed to complete in the order that they were posted.
+ */
+int ib_post_send_mad(struct ib_mad_agent *mad_agent,
+                    struct ib_send_wr *send_wr,
+                    struct ib_send_wr **bad_send_wr);
+
+/**
+ * ib_coalesce_recv_mad - Coalesces received MAD data into a single buffer.
+ * @mad_recv_wc: Work completion information for a received MAD.
+ * @buf: User-provided data buffer to receive the coalesced buffers.  The
+ *   referenced buffer should be at least the size of the mad_len specified
+ *   by @mad_recv_wc.
+ *
+ * This call copies a chain of received RMPP MADs into a single data buffer,
+ * removing duplicated headers.
+ */
+void ib_coalesce_recv_mad(struct ib_mad_recv_wc *mad_recv_wc,
+                         void *buf);
+
+/**
+ * ib_free_recv_mad - Returns data buffers used to receive a MAD to the
+ *   access layer.
+ * @mad_recv_wc: Work completion information for a received MAD.
+ *
+ * Clients receiving MADs through their ib_mad_recv_handler must call this
+ * routine to return the work completion buffers to the access layer.
+ */
+void ib_free_recv_mad(struct ib_mad_recv_wc *mad_recv_wc);
+
+/**
+ * ib_cancel_mad - Cancels an outstanding send MAD operation.
+ * @mad_agent: Specifies the registration associated with sent MAD.
+ * @wr_id: Indicates the work request identifier of the MAD to cancel.
+ *
+ * MADs will be returned to the user through the corresponding
+ * ib_mad_send_handler.
+ */
+void ib_cancel_mad(struct ib_mad_agent *mad_agent,
+                  u64 wr_id);
+
+/**
+ * ib_redirect_mad_qp - Registers a QP for MAD services.
+ * @qp: Reference to a QP that requires MAD services.
+ * @rmpp_version: If set, indicates that the client will send
+ *   and receive MADs that contain the RMPP header for the given version.
+ *   If set to 0, indicates that RMPP is not used by this client.
+ * @send_handler: The completion callback routine invoked after a send
+ *   request has completed.
+ * @recv_handler: The completion callback routine invoked for a received
+ *   MAD.
+ * @context: User specified context associated with the registration.
+ *
+ * Use of this call allows clients to use MAD services, such as RMPP,
+ * on user-owned QPs.  After calling this routine, users may send
+ * MADs on the specified QP by calling ib_mad_post_send.
+ */
+struct ib_mad_agent *ib_redirect_mad_qp(struct ib_qp *qp,
+                                       u8 rmpp_version,
+                                       ib_mad_send_handler send_handler,
+                                       ib_mad_recv_handler recv_handler,
+                                       void *context);
+
+/**
+ * ib_process_mad_wc - Processes a work completion associated with a
+ *   MAD sent or received on a redirected QP.
+ * @mad_agent: Specifies the registered MAD service using the redirected QP.
+ * @wc: References a work completion associated with a sent or received
+ *   MAD segment.
+ *
+ * This routine is used to complete or continue processing on a MAD request.
+ * If the work completion is associated with a send operation, calling
+ * this routine is required to continue an RMPP transfer or to wait for a
+ * corresponding response, if it is a request.  If the work completion is
+ * associated with a receive operation, calling this routine is required to
+ * process an inbound or outbound RMPP transfer, or to match a response MAD
+ * with its corresponding request.
+ */
+int ib_process_mad_wc(struct ib_mad_agent *mad_agent,
+                     struct ib_wc *wc);
+
+#endif /* IB_MAD_H */
diff --git a/drivers/infiniband/include/ib_pack.h b/drivers/infiniband/include/ib_pack.h
new file mode 100644 (file)
index 0000000..fe480f3
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ib_pack.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#ifndef IB_PACK_H
+#define IB_PACK_H
+
+#include <ib_verbs.h>
+
+enum {
+       IB_LRH_BYTES  = 8,
+       IB_GRH_BYTES  = 40,
+       IB_BTH_BYTES  = 12,
+       IB_DETH_BYTES = 8
+};
+
+struct ib_field {
+       size_t struct_offset_bytes;
+       size_t struct_size_bytes;
+       int    offset_words;
+       int    offset_bits;
+       int    size_bits;
+       char  *field_name;
+};
+
+#define RESERVED \
+       .field_name          = "reserved"
+
+/*
+ * This macro cleans up the definitions of constants for BTH opcodes.
+ * It is used to define constants such as IB_OPCODE_UD_SEND_ONLY,
+ * which becomes IB_OPCODE_UD + IB_OPCODE_SEND_ONLY, and this gives
+ * the correct value.
+ *
+ * In short, user code should use the constants defined using the
+ * macro rather than worrying about adding together other constants.
+*/
+#define IB_OPCODE(transport, op) \
+       IB_OPCODE_ ## transport ## _ ## op = \
+               IB_OPCODE_ ## transport + IB_OPCODE_ ## op
+
+enum {
+       /* transport types -- just used to define real constants */
+       IB_OPCODE_RC                                = 0x00,
+       IB_OPCODE_UC                                = 0x20,
+       IB_OPCODE_RD                                = 0x40,
+       IB_OPCODE_UD                                = 0x60,
+
+       /* operations -- just used to define real constants */
+       IB_OPCODE_SEND_FIRST                        = 0x00,
+       IB_OPCODE_SEND_MIDDLE                       = 0x01,
+       IB_OPCODE_SEND_LAST                         = 0x02,
+       IB_OPCODE_SEND_LAST_WITH_IMMEDIATE          = 0x03,
+       IB_OPCODE_SEND_ONLY                         = 0x04,
+       IB_OPCODE_SEND_ONLY_WITH_IMMEDIATE          = 0x05,
+       IB_OPCODE_RDMA_WRITE_FIRST                  = 0x06,
+       IB_OPCODE_RDMA_WRITE_MIDDLE                 = 0x07,
+       IB_OPCODE_RDMA_WRITE_LAST                   = 0x08,
+       IB_OPCODE_RDMA_WRITE_LAST_WITH_IMMEDIATE    = 0x09,
+       IB_OPCODE_RDMA_WRITE_ONLY                   = 0x0a,
+       IB_OPCODE_RDMA_WRITE_ONLY_WITH_IMMEDIATE    = 0x0b,
+       IB_OPCODE_RDMA_READ_REQUEST                 = 0x0c,
+       IB_OPCODE_RDMA_READ_RESPONSE_FIRST          = 0x0d,
+       IB_OPCODE_RDMA_READ_RESPONSE_MIDDLE         = 0x0e,
+       IB_OPCODE_RDMA_READ_RESPONSE_LAST           = 0x0f,
+       IB_OPCODE_RDMA_READ_RESPONSE_ONLY           = 0x10,
+       IB_OPCODE_ACKNOWLEDGE                       = 0x11,
+       IB_OPCODE_ATOMIC_ACKNOWLEDGE                = 0x12,
+       IB_OPCODE_COMPARE_SWAP                      = 0x13,
+       IB_OPCODE_FETCH_ADD                         = 0x14,
+
+       /* real constants follow -- see comment about above IB_OPCODE()
+          macro for more details */
+
+       /* RC */
+       IB_OPCODE(RC, SEND_FIRST),
+       IB_OPCODE(RC, SEND_MIDDLE),
+       IB_OPCODE(RC, SEND_LAST),
+       IB_OPCODE(RC, SEND_LAST_WITH_IMMEDIATE),
+       IB_OPCODE(RC, SEND_ONLY),
+       IB_OPCODE(RC, SEND_ONLY_WITH_IMMEDIATE),
+       IB_OPCODE(RC, RDMA_WRITE_FIRST),
+       IB_OPCODE(RC, RDMA_WRITE_MIDDLE),
+       IB_OPCODE(RC, RDMA_WRITE_LAST),
+       IB_OPCODE(RC, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+       IB_OPCODE(RC, RDMA_WRITE_ONLY),
+       IB_OPCODE(RC, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+       IB_OPCODE(RC, RDMA_READ_REQUEST),
+       IB_OPCODE(RC, RDMA_READ_RESPONSE_FIRST),
+       IB_OPCODE(RC, RDMA_READ_RESPONSE_MIDDLE),
+       IB_OPCODE(RC, RDMA_READ_RESPONSE_LAST),
+       IB_OPCODE(RC, RDMA_READ_RESPONSE_ONLY),
+       IB_OPCODE(RC, ACKNOWLEDGE),
+       IB_OPCODE(RC, ATOMIC_ACKNOWLEDGE),
+       IB_OPCODE(RC, COMPARE_SWAP),
+       IB_OPCODE(RC, FETCH_ADD),
+
+       /* UC */
+       IB_OPCODE(UC, SEND_FIRST),
+       IB_OPCODE(UC, SEND_MIDDLE),
+       IB_OPCODE(UC, SEND_LAST),
+       IB_OPCODE(UC, SEND_LAST_WITH_IMMEDIATE),
+       IB_OPCODE(UC, SEND_ONLY),
+       IB_OPCODE(UC, SEND_ONLY_WITH_IMMEDIATE),
+       IB_OPCODE(UC, RDMA_WRITE_FIRST),
+       IB_OPCODE(UC, RDMA_WRITE_MIDDLE),
+       IB_OPCODE(UC, RDMA_WRITE_LAST),
+       IB_OPCODE(UC, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+       IB_OPCODE(UC, RDMA_WRITE_ONLY),
+       IB_OPCODE(UC, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+
+       /* RD */
+       IB_OPCODE(RD, SEND_FIRST),
+       IB_OPCODE(RD, SEND_MIDDLE),
+       IB_OPCODE(RD, SEND_LAST),
+       IB_OPCODE(RD, SEND_LAST_WITH_IMMEDIATE),
+       IB_OPCODE(RD, SEND_ONLY),
+       IB_OPCODE(RD, SEND_ONLY_WITH_IMMEDIATE),
+       IB_OPCODE(RD, RDMA_WRITE_FIRST),
+       IB_OPCODE(RD, RDMA_WRITE_MIDDLE),
+       IB_OPCODE(RD, RDMA_WRITE_LAST),
+       IB_OPCODE(RD, RDMA_WRITE_LAST_WITH_IMMEDIATE),
+       IB_OPCODE(RD, RDMA_WRITE_ONLY),
+       IB_OPCODE(RD, RDMA_WRITE_ONLY_WITH_IMMEDIATE),
+       IB_OPCODE(RD, RDMA_READ_REQUEST),
+       IB_OPCODE(RD, RDMA_READ_RESPONSE_FIRST),
+       IB_OPCODE(RD, RDMA_READ_RESPONSE_MIDDLE),
+       IB_OPCODE(RD, RDMA_READ_RESPONSE_LAST),
+       IB_OPCODE(RD, RDMA_READ_RESPONSE_ONLY),
+       IB_OPCODE(RD, ACKNOWLEDGE),
+       IB_OPCODE(RD, ATOMIC_ACKNOWLEDGE),
+       IB_OPCODE(RD, COMPARE_SWAP),
+       IB_OPCODE(RD, FETCH_ADD),
+
+       /* UD */
+       IB_OPCODE(UD, SEND_ONLY),
+       IB_OPCODE(UD, SEND_ONLY_WITH_IMMEDIATE)
+};
+
+enum {
+       IB_LNH_RAW        = 0,
+       IB_LNH_IP         = 1,
+       IB_LNH_IBA_LOCAL  = 2,
+       IB_LNH_IBA_GLOBAL = 3
+};
+
+struct ib_unpacked_lrh {
+       u8        virtual_lane;
+       u8        link_version;
+       u8        service_level;
+       u8        link_next_header;
+       __be16    destination_lid;
+       __be16    packet_length;
+       __be16    source_lid;
+};
+
+struct ib_unpacked_grh {
+       u8           ip_version;
+       u8           traffic_class;
+       __be32       flow_label;
+       __be16       payload_length;
+       u8           next_header;
+       u8           hop_limit;
+       union ib_gid source_gid;
+       union ib_gid destination_gid;
+};
+
+struct ib_unpacked_bth {
+       u8           opcode;
+       u8           solicited_event;
+       u8           mig_req;
+       u8           pad_count;
+       u8           transport_header_version;
+       __be16       pkey;
+       __be32       destination_qpn;
+       u8           ack_req;
+       __be32       psn;
+};
+
+struct ib_unpacked_deth {
+       __be32       qkey;
+       __be32       source_qpn;
+};
+
+struct ib_ud_header {
+       struct ib_unpacked_lrh  lrh;
+       int                     grh_present;
+       struct ib_unpacked_grh  grh;
+       struct ib_unpacked_bth  bth;
+       struct ib_unpacked_deth deth;
+       int                     immediate_present;
+       __be32                  immediate_data;
+};
+
+void ib_pack(const struct ib_field        *desc,
+            int                           desc_len,
+            void                         *structure,
+            void                         *buf);
+
+void ib_unpack(const struct ib_field        *desc,
+              int                           desc_len,
+              void                         *buf,
+              void                         *structure);
+
+void ib_ud_header_init(int                        payload_bytes,
+                      int                 grh_present,
+                      struct ib_ud_header *header);
+
+int ib_ud_header_pack(struct ib_ud_header *header,
+                     void                *buf);
+
+int ib_ud_header_unpack(void                *buf,
+                       struct ib_ud_header *header);
+
+#endif /* IB_PACK_H */
diff --git a/drivers/infiniband/include/ib_sa.h b/drivers/infiniband/include/ib_sa.h
new file mode 100644 (file)
index 0000000..f4f7477
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ib_sa.h 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#ifndef IB_SA_H
+#define IB_SA_H
+
+#include <linux/compiler.h>
+
+#include <ib_verbs.h>
+#include <ib_mad.h>
+
+enum {
+       IB_SA_CLASS_VERSION     = 2,    /* IB spec version 1.1/1.2 */
+
+       IB_SA_METHOD_DELETE     = 0x15
+};
+
+enum ib_sa_selector {
+       IB_SA_GTE  = 0,
+       IB_SA_LTE  = 1,
+       IB_SA_EQ   = 2,
+       /*
+        * The meaning of "best" depends on the attribute: for
+        * example, for MTU best will return the largest available
+        * MTU, while for packet life time, best will return the
+        * smallest available life time.
+        */
+       IB_SA_BEST = 3
+};
+
+enum ib_sa_rate {
+       IB_SA_RATE_2_5_GBPS = 2,
+       IB_SA_RATE_5_GBPS   = 5,
+       IB_SA_RATE_10_GBPS  = 3,
+       IB_SA_RATE_20_GBPS  = 6,
+       IB_SA_RATE_30_GBPS  = 4,
+       IB_SA_RATE_40_GBPS  = 7,
+       IB_SA_RATE_60_GBPS  = 8,
+       IB_SA_RATE_80_GBPS  = 9,
+       IB_SA_RATE_120_GBPS = 10
+};
+
+static inline int ib_sa_rate_enum_to_int(enum ib_sa_rate rate)
+{
+       switch (rate) {
+       case IB_SA_RATE_2_5_GBPS: return  1;
+       case IB_SA_RATE_5_GBPS:   return  2;
+       case IB_SA_RATE_10_GBPS:  return  4;
+       case IB_SA_RATE_20_GBPS:  return  8;
+       case IB_SA_RATE_30_GBPS:  return 12;
+       case IB_SA_RATE_40_GBPS:  return 16;
+       case IB_SA_RATE_60_GBPS:  return 24;
+       case IB_SA_RATE_80_GBPS:  return 32;
+       case IB_SA_RATE_120_GBPS: return 48;
+       default:                  return -1;
+       }
+}
+
+typedef u64 __bitwise ib_sa_comp_mask;
+
+#define IB_SA_COMP_MASK(n)     ((__force ib_sa_comp_mask) cpu_to_be64(1ull << n))
+
+/*
+ * Structures for SA records are named "struct ib_sa_xxx_rec."  No
+ * attempt is made to pack structures to match the physical layout of
+ * SA records in SA MADs; all packing and unpacking is handled by the
+ * SA query code.
+ *
+ * For a record with structure ib_sa_xxx_rec, the naming convention
+ * for the component mask value for field yyy is IB_SA_XXX_REC_YYY (we
+ * never use different abbreviations or otherwise change the spelling
+ * of xxx/yyy between ib_sa_xxx_rec.yyy and IB_SA_XXX_REC_YYY).
+ *
+ * Reserved rows are indicated with comments to help maintainability.
+ */
+
+/* reserved:                                                            0 */
+/* reserved:                                                            1 */
+#define IB_SA_PATH_REC_DGID                            IB_SA_COMP_MASK( 2)
+#define IB_SA_PATH_REC_SGID                            IB_SA_COMP_MASK( 3)
+#define IB_SA_PATH_REC_DLID                            IB_SA_COMP_MASK( 4)
+#define IB_SA_PATH_REC_SLID                            IB_SA_COMP_MASK( 5)
+#define IB_SA_PATH_REC_RAW_TRAFFIC                     IB_SA_COMP_MASK( 6)
+/* reserved:                                                            7 */
+#define IB_SA_PATH_REC_FLOW_LABEL                      IB_SA_COMP_MASK( 8)
+#define IB_SA_PATH_REC_HOP_LIMIT                       IB_SA_COMP_MASK( 9)
+#define IB_SA_PATH_REC_TRAFFIC_CLASS                   IB_SA_COMP_MASK(10)
+#define IB_SA_PATH_REC_REVERSIBLE                      IB_SA_COMP_MASK(11)
+#define IB_SA_PATH_REC_NUMB_PATH                       IB_SA_COMP_MASK(12)
+#define IB_SA_PATH_REC_PKEY                            IB_SA_COMP_MASK(13)
+/* reserved:                                                           14 */
+#define IB_SA_PATH_REC_SL                              IB_SA_COMP_MASK(15)
+#define IB_SA_PATH_REC_MTU_SELECTOR                    IB_SA_COMP_MASK(16)
+#define IB_SA_PATH_REC_MTU                             IB_SA_COMP_MASK(17)
+#define IB_SA_PATH_REC_RATE_SELECTOR                   IB_SA_COMP_MASK(18)
+#define IB_SA_PATH_REC_RATE                            IB_SA_COMP_MASK(19)
+#define IB_SA_PATH_REC_PACKET_LIFE_TIME_SELECTOR       IB_SA_COMP_MASK(20)
+#define IB_SA_PATH_REC_PACKET_LIFE_TIME                        IB_SA_COMP_MASK(21)
+#define IB_SA_PATH_REC_PREFERENCE                      IB_SA_COMP_MASK(22)
+
+struct ib_sa_path_rec {
+       /* reserved */
+       /* reserved */
+       union ib_gid dgid;
+       union ib_gid sgid;
+       u16          dlid;
+       u16          slid;
+       int          raw_traffic;
+       /* reserved */
+       u32          flow_label;
+       u8           hop_limit;
+       u8           traffic_class;
+       int          reversible;
+       u8           numb_path;
+       u16          pkey;
+       /* reserved */
+       u8           sl;
+       u8           mtu_selector;
+       enum ib_mtu  mtu;
+       u8           rate_selector;
+       u8           rate;
+       u8           packet_life_time_selector;
+       u8           packet_life_time;
+       u8           preference;
+};
+
+#define IB_SA_MCMEMBER_REC_MGID                                IB_SA_COMP_MASK( 0)
+#define IB_SA_MCMEMBER_REC_PORT_GID                    IB_SA_COMP_MASK( 1)
+#define IB_SA_MCMEMBER_REC_QKEY                                IB_SA_COMP_MASK( 2)
+#define IB_SA_MCMEMBER_REC_MLID                                IB_SA_COMP_MASK( 3)
+#define IB_SA_MCMEMBER_REC_MTU_SELECTOR                        IB_SA_COMP_MASK( 4)
+#define IB_SA_MCMEMBER_REC_MTU                         IB_SA_COMP_MASK( 5)
+#define IB_SA_MCMEMBER_REC_TRAFFIC_CLASS               IB_SA_COMP_MASK( 6)
+#define IB_SA_MCMEMBER_REC_PKEY                                IB_SA_COMP_MASK( 7)
+#define IB_SA_MCMEMBER_REC_RATE_SELECTOR               IB_SA_COMP_MASK( 8)
+#define IB_SA_MCMEMBER_REC_RATE                                IB_SA_COMP_MASK( 9)
+#define IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME_SELECTOR   IB_SA_COMP_MASK(10)
+#define IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME            IB_SA_COMP_MASK(11)
+#define IB_SA_MCMEMBER_REC_SL                          IB_SA_COMP_MASK(12)
+#define IB_SA_MCMEMBER_REC_FLOW_LABEL                  IB_SA_COMP_MASK(13)
+#define IB_SA_MCMEMBER_REC_HOP_LIMIT                   IB_SA_COMP_MASK(14)
+#define IB_SA_MCMEMBER_REC_SCOPE                       IB_SA_COMP_MASK(15)
+#define IB_SA_MCMEMBER_REC_JOIN_STATE                  IB_SA_COMP_MASK(16)
+#define IB_SA_MCMEMBER_REC_PROXY_JOIN                  IB_SA_COMP_MASK(17)
+
+struct ib_sa_mcmember_rec {
+       union ib_gid mgid;
+       union ib_gid port_gid;
+       u32          qkey;
+       u16          mlid;
+       u8           mtu_selector;
+       enum         ib_mtu mtu;
+       u8           traffic_class;
+       u16          pkey;
+       u8           rate_selector;
+       u8           rate;
+       u8           packet_life_time_selector;
+       u8           packet_life_time;
+       u8           sl;
+       u32          flow_label;
+       u8           hop_limit;
+       u8           scope;
+       u8           join_state;
+       int          proxy_join;
+};
+
+struct ib_sa_query;
+
+void ib_sa_cancel_query(int id, struct ib_sa_query *query);
+
+int ib_sa_path_rec_get(struct ib_device *device, u8 port_num,
+                      struct ib_sa_path_rec *rec,
+                      ib_sa_comp_mask comp_mask,
+                      int timeout_ms, int gfp_mask,
+                      void (*callback)(int status,
+                                       struct ib_sa_path_rec *resp,
+                                       void *context),
+                      void *context,
+                      struct ib_sa_query **query);
+
+int ib_sa_mcmember_rec_query(struct ib_device *device, u8 port_num,
+                            u8 method,
+                            struct ib_sa_mcmember_rec *rec,
+                            ib_sa_comp_mask comp_mask,
+                            int timeout_ms, int gfp_mask,
+                            void (*callback)(int status,
+                                             struct ib_sa_mcmember_rec *resp,
+                                             void *context),
+                            void *context,
+                            struct ib_sa_query **query);
+
+/**
+ * ib_sa_mcmember_rec_set - Start an MCMember set query
+ * @device:device to send query on
+ * @port_num: port number to send query on
+ * @rec:MCMember Record to send in query
+ * @comp_mask:component mask to send in query
+ * @timeout_ms:time to wait for response
+ * @gfp_mask:GFP mask to use for internal allocations
+ * @callback:function called when query completes, times out or is
+ * canceled
+ * @context:opaque user context passed to callback
+ * @sa_query:query context, used to cancel query
+ *
+ * Send an MCMember Set query to the SA (eg to join a multicast
+ * group).  The callback function will be called when the query
+ * completes (or fails); status is 0 for a successful response, -EINTR
+ * if the query is canceled, -ETIMEDOUT is the query timed out, or
+ * -EIO if an error occurred sending the query.  The resp parameter of
+ * the callback is only valid if status is 0.
+ *
+ * If the return value of ib_sa_mcmember_rec_set() is negative, it is
+ * an error code.  Otherwise it is a query ID that can be used to
+ * cancel the query.
+ */
+static inline int
+ib_sa_mcmember_rec_set(struct ib_device *device, u8 port_num,
+                      struct ib_sa_mcmember_rec *rec,
+                      ib_sa_comp_mask comp_mask,
+                      int timeout_ms, int gfp_mask,
+                      void (*callback)(int status,
+                                       struct ib_sa_mcmember_rec *resp,
+                                       void *context),
+                      void *context,
+                      struct ib_sa_query **query)
+{
+       return ib_sa_mcmember_rec_query(device, port_num,
+                                       IB_MGMT_METHOD_SET,
+                                       rec, comp_mask,
+                                       timeout_ms, gfp_mask, callback,
+                                       context, query);
+}
+
+/**
+ * ib_sa_mcmember_rec_delete - Start an MCMember delete query
+ * @device:device to send query on
+ * @port_num: port number to send query on
+ * @rec:MCMember Record to send in query
+ * @comp_mask:component mask to send in query
+ * @timeout_ms:time to wait for response
+ * @gfp_mask:GFP mask to use for internal allocations
+ * @callback:function called when query completes, times out or is
+ * canceled
+ * @context:opaque user context passed to callback
+ * @sa_query:query context, used to cancel query
+ *
+ * Send an MCMember Delete query to the SA (eg to leave a multicast
+ * group).  The callback function will be called when the query
+ * completes (or fails); status is 0 for a successful response, -EINTR
+ * if the query is canceled, -ETIMEDOUT is the query timed out, or
+ * -EIO if an error occurred sending the query.  The resp parameter of
+ * the callback is only valid if status is 0.
+ *
+ * If the return value of ib_sa_mcmember_rec_delete() is negative, it
+ * is an error code.  Otherwise it is a query ID that can be used to
+ * cancel the query.
+ */
+static inline int
+ib_sa_mcmember_rec_delete(struct ib_device *device, u8 port_num,
+                         struct ib_sa_mcmember_rec *rec,
+                         ib_sa_comp_mask comp_mask,
+                         int timeout_ms, int gfp_mask,
+                         void (*callback)(int status,
+                                          struct ib_sa_mcmember_rec *resp,
+                                          void *context),
+                         void *context,
+                         struct ib_sa_query **query)
+{
+       return ib_sa_mcmember_rec_query(device, port_num,
+                                       IB_SA_METHOD_DELETE,
+                                       rec, comp_mask,
+                                       timeout_ms, gfp_mask, callback,
+                                       context, query);
+}
+
+
+#endif /* IB_SA_H */
diff --git a/drivers/infiniband/include/ib_smi.h b/drivers/infiniband/include/ib_smi.h
new file mode 100644 (file)
index 0000000..ca82165
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
+ * Copyright (c) 2004 Infinicon Corporation.  All rights reserved.
+ * Copyright (c) 2004 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ * Copyright (c) 2004 Voltaire Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ib_smi.h 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#if !defined( IB_SMI_H )
+#define IB_SMI_H
+
+#include <ib_mad.h>
+
+#define IB_LID_PERMISSIVE                      0xFFFF
+
+#define IB_SMP_DATA_SIZE                       64
+#define IB_SMP_MAX_PATH_HOPS                   64
+
+struct ib_smp {
+       u8      base_version;
+       u8      mgmt_class;
+       u8      class_version;
+       u8      method;
+       u16     status;
+       u8      hop_ptr;
+       u8      hop_cnt;
+       u64     tid;
+       u16     attr_id;
+       u16     resv;
+       u32     attr_mod;
+       u64     mkey;
+       u16     dr_slid;
+       u16     dr_dlid;
+       u8      reserved[28];
+       u8      data[IB_SMP_DATA_SIZE];
+       u8      initial_path[IB_SMP_MAX_PATH_HOPS];
+       u8      return_path[IB_SMP_MAX_PATH_HOPS];
+} __attribute__ ((packed));
+
+#define IB_SMP_DIRECTION                       __constant_htons(0x8000)
+
+/* Subnet management attributes */
+#define IB_SMP_ATTR_NOTICE                     __constant_htons(0x0002)
+#define IB_SMP_ATTR_NODE_DESC                  __constant_htons(0x0010)
+#define IB_SMP_ATTR_NODE_INFO                  __constant_htons(0x0011)
+#define IB_SMP_ATTR_SWITCH_INFO                        __constant_htons(0x0012)
+#define IB_SMP_ATTR_GUID_INFO                  __constant_htons(0x0014)
+#define IB_SMP_ATTR_PORT_INFO                  __constant_htons(0x0015)
+#define IB_SMP_ATTR_PKEY_TABLE                 __constant_htons(0x0016)
+#define IB_SMP_ATTR_SL_TO_VL_TABLE             __constant_htons(0x0017)
+#define IB_SMP_ATTR_VL_ARB_TABLE               __constant_htons(0x0018)
+#define IB_SMP_ATTR_LINEAR_FORWARD_TABLE       __constant_htons(0x0019)
+#define IB_SMP_ATTR_RANDOM_FORWARD_TABLE       __constant_htons(0x001A)
+#define IB_SMP_ATTR_MCAST_FORWARD_TABLE                __constant_htons(0x001B)
+#define IB_SMP_ATTR_SM_INFO                    __constant_htons(0x0020)
+#define IB_SMP_ATTR_VENDOR_DIAG                        __constant_htons(0x0030)
+#define IB_SMP_ATTR_LED_INFO                   __constant_htons(0x0031)
+#define IB_SMP_ATTR_VENDOR_MASK                        __constant_htons(0xFF00)
+
+static inline u8
+ib_get_smp_direction(struct ib_smp *smp)
+{
+       return ((smp->status & IB_SMP_DIRECTION) == IB_SMP_DIRECTION);
+}
+
+#endif /* IB_SMI_H */
diff --git a/drivers/infiniband/include/ib_user_mad.h b/drivers/infiniband/include/ib_user_mad.h
new file mode 100644 (file)
index 0000000..06ad4a6
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ib_user_mad.h 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#ifndef IB_USER_MAD_H
+#define IB_USER_MAD_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/*
+ * Increment this value if any changes that break userspace ABI
+ * compatibility are made.
+ */
+#define IB_USER_MAD_ABI_VERSION        2
+
+/*
+ * Make sure that all structs defined in this file remain laid out so
+ * that they pack the same way on 32-bit and 64-bit architectures (to
+ * avoid incompatibility between 32-bit userspace and 64-bit kernels).
+ */
+
+/**
+ * ib_user_mad - MAD packet
+ * @data - Contents of MAD
+ * @id - ID of agent MAD received with/to be sent with
+ * @status - 0 on successful receive, ETIMEDOUT if no response
+ *   received (transaction ID in data[] will be set to TID of original
+ *   request) (ignored on send)
+ * @timeout_ms - Milliseconds to wait for response (unset on receive)
+ * @qpn - Remote QP number received from/to be sent to
+ * @qkey - Remote Q_Key to be sent with (unset on receive)
+ * @lid - Remote lid received from/to be sent to
+ * @sl - Service level received with/to be sent with
+ * @path_bits - Local path bits received with/to be sent with
+ * @grh_present - If set, GRH was received/should be sent
+ * @gid_index - Local GID index to send with (unset on receive)
+ * @hop_limit - Hop limit in GRH
+ * @traffic_class - Traffic class in GRH
+ * @gid - Remote GID in GRH
+ * @flow_label - Flow label in GRH
+ *
+ * All multi-byte quantities are stored in network (big endian) byte order.
+ */
+struct ib_user_mad {
+       __u8    data[256];
+       __u32   id;
+       __u32   status;
+       __u32   timeout_ms;
+       __u32   qpn;
+       __u32   qkey;
+       __u16   lid;
+       __u8    sl;
+       __u8    path_bits;
+       __u8    grh_present;
+       __u8    gid_index;
+       __u8    hop_limit;
+       __u8    traffic_class;
+       __u8    gid[16];
+       __u32   flow_label;
+};
+
+/**
+ * ib_user_mad_reg_req - MAD registration request
+ * @id - Set by the kernel; used to identify agent in future requests.
+ * @qpn - Queue pair number; must be 0 or 1.
+ * @method_mask - The caller will receive unsolicited MADs for any method
+ *   where @method_mask = 1.
+ * @mgmt_class - Indicates which management class of MADs should be receive
+ *   by the caller.  This field is only required if the user wishes to
+ *   receive unsolicited MADs, otherwise it should be 0.
+ * @mgmt_class_version - Indicates which version of MADs for the given
+ *   management class to receive.
+ * @oui: Indicates IEEE OUI when mgmt_class is a vendor class
+ *   in the range from 0x30 to 0x4f. Otherwise not used.
+ */
+struct ib_user_mad_reg_req {
+       __u32   id;
+       __u32   method_mask[4];
+       __u8    qpn;
+       __u8    mgmt_class;
+       __u8    mgmt_class_version;
+       __u8    oui[3];
+};
+
+#define IB_IOCTL_MAGIC         0x1b
+
+#define IB_USER_MAD_REGISTER_AGENT     _IOWR(IB_IOCTL_MAGIC, 1, \
+                                             struct ib_user_mad_reg_req)
+
+#define IB_USER_MAD_UNREGISTER_AGENT   _IOW(IB_IOCTL_MAGIC, 2, __u32)
+
+#endif /* IB_USER_MAD_H */
diff --git a/drivers/infiniband/include/ib_verbs.h b/drivers/infiniband/include/ib_verbs.h
new file mode 100644 (file)
index 0000000..cd1f01e
--- /dev/null
@@ -0,0 +1,1259 @@
+/*
+ * Copyright (c) 2004 Mellanox Technologies Ltd.  All rights reserved.
+ * Copyright (c) 2004 Infinicon Corporation.  All rights reserved.
+ * Copyright (c) 2004 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
+ * Copyright (c) 2004 Voltaire Corporation.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ib_verbs.h 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#if !defined(IB_VERBS_H)
+#define IB_VERBS_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <asm/atomic.h>
+
+union ib_gid {
+       u8      raw[16];
+       struct {
+               u64     subnet_prefix;
+               u64     interface_id;
+       } global;
+};
+
+enum ib_node_type {
+       IB_NODE_CA      = 1,
+       IB_NODE_SWITCH,
+       IB_NODE_ROUTER
+};
+
+enum ib_device_cap_flags {
+       IB_DEVICE_RESIZE_MAX_WR         = 1,
+       IB_DEVICE_BAD_PKEY_CNTR         = (1<<1),
+       IB_DEVICE_BAD_QKEY_CNTR         = (1<<2),
+       IB_DEVICE_RAW_MULTI             = (1<<3),
+       IB_DEVICE_AUTO_PATH_MIG         = (1<<4),
+       IB_DEVICE_CHANGE_PHY_PORT       = (1<<5),
+       IB_DEVICE_UD_AV_PORT_ENFORCE    = (1<<6),
+       IB_DEVICE_CURR_QP_STATE_MOD     = (1<<7),
+       IB_DEVICE_SHUTDOWN_PORT         = (1<<8),
+       IB_DEVICE_INIT_TYPE             = (1<<9),
+       IB_DEVICE_PORT_ACTIVE_EVENT     = (1<<10),
+       IB_DEVICE_SYS_IMAGE_GUID        = (1<<11),
+       IB_DEVICE_RC_RNR_NAK_GEN        = (1<<12),
+       IB_DEVICE_SRQ_RESIZE            = (1<<13),
+       IB_DEVICE_N_NOTIFY_CQ           = (1<<14),
+       IB_DEVICE_RQ_SIG_TYPE           = (1<<15)
+};
+
+enum ib_atomic_cap {
+       IB_ATOMIC_NONE,
+       IB_ATOMIC_HCA,
+       IB_ATOMIC_GLOB
+};
+
+struct ib_device_attr {
+       u64                     fw_ver;
+       u64                     node_guid;
+       u64                     sys_image_guid;
+       u64                     max_mr_size;
+       u64                     page_size_cap;
+       u32                     vendor_id;
+       u32                     vendor_part_id;
+       u32                     hw_ver;
+       int                     max_qp;
+       int                     max_qp_wr;
+       int                     device_cap_flags;
+       int                     max_sge;
+       int                     max_sge_rd;
+       int                     max_cq;
+       int                     max_cqe;
+       int                     max_mr;
+       int                     max_pd;
+       int                     max_qp_rd_atom;
+       int                     max_ee_rd_atom;
+       int                     max_res_rd_atom;
+       int                     max_qp_init_rd_atom;
+       int                     max_ee_init_rd_atom;
+       enum ib_atomic_cap      atomic_cap;
+       int                     max_ee;
+       int                     max_rdd;
+       int                     max_mw;
+       int                     max_raw_ipv6_qp;
+       int                     max_raw_ethy_qp;
+       int                     max_mcast_grp;
+       int                     max_mcast_qp_attach;
+       int                     max_total_mcast_qp_attach;
+       int                     max_ah;
+       int                     max_fmr;
+       int                     max_map_per_fmr;
+       int                     max_srq;
+       int                     max_srq_wr;
+       int                     max_srq_sge;
+       u16                     max_pkeys;
+       u8                      local_ca_ack_delay;
+};
+
+enum ib_mtu {
+       IB_MTU_256  = 1,
+       IB_MTU_512  = 2,
+       IB_MTU_1024 = 3,
+       IB_MTU_2048 = 4,
+       IB_MTU_4096 = 5
+};
+
+static inline int ib_mtu_enum_to_int(enum ib_mtu mtu)
+{
+       switch (mtu) {
+       case IB_MTU_256:  return  256;
+       case IB_MTU_512:  return  512;
+       case IB_MTU_1024: return 1024;
+       case IB_MTU_2048: return 2048;
+       case IB_MTU_4096: return 4096;
+       default:          return -1;
+       }
+}
+
+enum ib_port_state {
+       IB_PORT_NOP             = 0,
+       IB_PORT_DOWN            = 1,
+       IB_PORT_INIT            = 2,
+       IB_PORT_ARMED           = 3,
+       IB_PORT_ACTIVE          = 4,
+       IB_PORT_ACTIVE_DEFER    = 5
+};
+
+enum ib_port_cap_flags {
+       IB_PORT_SM                              = 1 <<  1,
+       IB_PORT_NOTICE_SUP                      = 1 <<  2,
+       IB_PORT_TRAP_SUP                        = 1 <<  3,
+       IB_PORT_OPT_IPD_SUP                     = 1 <<  4,
+       IB_PORT_AUTO_MIGR_SUP                   = 1 <<  5,
+       IB_PORT_SL_MAP_SUP                      = 1 <<  6,
+       IB_PORT_MKEY_NVRAM                      = 1 <<  7,
+       IB_PORT_PKEY_NVRAM                      = 1 <<  8,
+       IB_PORT_LED_INFO_SUP                    = 1 <<  9,
+       IB_PORT_SM_DISABLED                     = 1 << 10,
+       IB_PORT_SYS_IMAGE_GUID_SUP              = 1 << 11,
+       IB_PORT_PKEY_SW_EXT_PORT_TRAP_SUP       = 1 << 12,
+       IB_PORT_CM_SUP                          = 1 << 16,
+       IB_PORT_SNMP_TUNNEL_SUP                 = 1 << 17,
+       IB_PORT_REINIT_SUP                      = 1 << 18,
+       IB_PORT_DEVICE_MGMT_SUP                 = 1 << 19,
+       IB_PORT_VENDOR_CLASS_SUP                = 1 << 20,
+       IB_PORT_DR_NOTICE_SUP                   = 1 << 21,
+       IB_PORT_CAP_MASK_NOTICE_SUP             = 1 << 22,
+       IB_PORT_BOOT_MGMT_SUP                   = 1 << 23,
+       IB_PORT_LINK_LATENCY_SUP                = 1 << 24,
+       IB_PORT_CLIENT_REG_SUP                  = 1 << 25
+};
+
+enum ib_port_width {
+       IB_WIDTH_1X     = 1,
+       IB_WIDTH_4X     = 2,
+       IB_WIDTH_8X     = 4,
+       IB_WIDTH_12X    = 8
+};
+
+static inline int ib_width_enum_to_int(enum ib_port_width width)
+{
+       switch (width) {
+       case IB_WIDTH_1X:  return  1;
+       case IB_WIDTH_4X:  return  4;
+       case IB_WIDTH_8X:  return  8;
+       case IB_WIDTH_12X: return 12;
+       default:          return -1;
+       }
+}
+
+struct ib_port_attr {
+       enum ib_port_state      state;
+       enum ib_mtu             max_mtu;
+       enum ib_mtu             active_mtu;
+       int                     gid_tbl_len;
+       u32                     port_cap_flags;
+       u32                     max_msg_sz;
+       u32                     bad_pkey_cntr;
+       u32                     qkey_viol_cntr;
+       u16                     pkey_tbl_len;
+       u16                     lid;
+       u16                     sm_lid;
+       u8                      lmc;
+       u8                      max_vl_num;
+       u8                      sm_sl;
+       u8                      subnet_timeout;
+       u8                      init_type_reply;
+       u8                      active_width;
+       u8                      active_speed;
+       u8                      phys_state;
+};
+
+enum ib_device_modify_flags {
+       IB_DEVICE_MODIFY_SYS_IMAGE_GUID = 1
+};
+
+struct ib_device_modify {
+       u64     sys_image_guid;
+};
+
+enum ib_port_modify_flags {
+       IB_PORT_SHUTDOWN                = 1,
+       IB_PORT_INIT_TYPE               = (1<<2),
+       IB_PORT_RESET_QKEY_CNTR         = (1<<3)
+};
+
+struct ib_port_modify {
+       u32     set_port_cap_mask;
+       u32     clr_port_cap_mask;
+       u8      init_type;
+};
+
+enum ib_event_type {
+       IB_EVENT_CQ_ERR,
+       IB_EVENT_QP_FATAL,
+       IB_EVENT_QP_REQ_ERR,
+       IB_EVENT_QP_ACCESS_ERR,
+       IB_EVENT_COMM_EST,
+       IB_EVENT_SQ_DRAINED,
+       IB_EVENT_PATH_MIG,
+       IB_EVENT_PATH_MIG_ERR,
+       IB_EVENT_DEVICE_FATAL,
+       IB_EVENT_PORT_ACTIVE,
+       IB_EVENT_PORT_ERR,
+       IB_EVENT_LID_CHANGE,
+       IB_EVENT_PKEY_CHANGE,
+       IB_EVENT_SM_CHANGE
+};
+
+struct ib_event {
+       struct ib_device        *device;
+       union {
+               struct ib_cq    *cq;
+               struct ib_qp    *qp;
+               u8              port_num;
+       } element;
+       enum ib_event_type      event;
+};
+
+struct ib_event_handler {
+       struct ib_device *device;
+       void            (*handler)(struct ib_event_handler *, struct ib_event *);
+       struct list_head  list;
+};
+
+#define INIT_IB_EVENT_HANDLER(_ptr, _device, _handler)         \
+       do {                                                    \
+               (_ptr)->device  = _device;                      \
+               (_ptr)->handler = _handler;                     \
+               INIT_LIST_HEAD(&(_ptr)->list);                  \
+       } while (0)
+
+struct ib_global_route {
+       union ib_gid    dgid;
+       u32             flow_label;
+       u8              sgid_index;
+       u8              hop_limit;
+       u8              traffic_class;
+};
+
+enum {
+       IB_MULTICAST_QPN = 0xffffff
+};
+
+enum ib_ah_flags {
+       IB_AH_GRH       = 1
+};
+
+struct ib_ah_attr {
+       struct ib_global_route  grh;
+       u16                     dlid;
+       u8                      sl;
+       u8                      src_path_bits;
+       u8                      static_rate;
+       u8                      ah_flags;
+       u8                      port_num;
+};
+
+enum ib_wc_status {
+       IB_WC_SUCCESS,
+       IB_WC_LOC_LEN_ERR,
+       IB_WC_LOC_QP_OP_ERR,
+       IB_WC_LOC_EEC_OP_ERR,
+       IB_WC_LOC_PROT_ERR,
+       IB_WC_WR_FLUSH_ERR,
+       IB_WC_MW_BIND_ERR,
+       IB_WC_BAD_RESP_ERR,
+       IB_WC_LOC_ACCESS_ERR,
+       IB_WC_REM_INV_REQ_ERR,
+       IB_WC_REM_ACCESS_ERR,
+       IB_WC_REM_OP_ERR,
+       IB_WC_RETRY_EXC_ERR,
+       IB_WC_RNR_RETRY_EXC_ERR,
+       IB_WC_LOC_RDD_VIOL_ERR,
+       IB_WC_REM_INV_RD_REQ_ERR,
+       IB_WC_REM_ABORT_ERR,
+       IB_WC_INV_EECN_ERR,
+       IB_WC_INV_EEC_STATE_ERR,
+       IB_WC_FATAL_ERR,
+       IB_WC_RESP_TIMEOUT_ERR,
+       IB_WC_GENERAL_ERR
+};
+
+enum ib_wc_opcode {
+       IB_WC_SEND,
+       IB_WC_RDMA_WRITE,
+       IB_WC_RDMA_READ,
+       IB_WC_COMP_SWAP,
+       IB_WC_FETCH_ADD,
+       IB_WC_BIND_MW,
+/*
+ * Set value of IB_WC_RECV so consumers can test if a completion is a
+ * receive by testing (opcode & IB_WC_RECV).
+ */
+       IB_WC_RECV                      = 1 << 7,
+       IB_WC_RECV_RDMA_WITH_IMM
+};
+
+enum ib_wc_flags {
+       IB_WC_GRH               = 1,
+       IB_WC_WITH_IMM          = (1<<1)
+};
+
+struct ib_wc {
+       u64                     wr_id;
+       enum ib_wc_status       status;
+       enum ib_wc_opcode       opcode;
+       u32                     vendor_err;
+       u32                     byte_len;
+       __be32                  imm_data;
+       u32                     qp_num;
+       u32                     src_qp;
+       int                     wc_flags;
+       u16                     pkey_index;
+       u16                     slid;
+       u8                      sl;
+       u8                      dlid_path_bits;
+       u8                      port_num;       /* valid only for DR SMPs on switches */
+};
+
+enum ib_cq_notify {
+       IB_CQ_SOLICITED,
+       IB_CQ_NEXT_COMP
+};
+
+struct ib_qp_cap {
+       u32     max_send_wr;
+       u32     max_recv_wr;
+       u32     max_send_sge;
+       u32     max_recv_sge;
+       u32     max_inline_data;
+};
+
+enum ib_sig_type {
+       IB_SIGNAL_ALL_WR,
+       IB_SIGNAL_REQ_WR
+};
+
+enum ib_qp_type {
+       /*
+        * IB_QPT_SMI and IB_QPT_GSI have to be the first two entries
+        * here (and in that order) since the MAD layer uses them as
+        * indices into a 2-entry table.
+        */
+       IB_QPT_SMI,
+       IB_QPT_GSI,
+
+       IB_QPT_RC,
+       IB_QPT_UC,
+       IB_QPT_UD,
+       IB_QPT_RAW_IPV6,
+       IB_QPT_RAW_ETY
+};
+
+struct ib_qp_init_attr {
+       void                  (*event_handler)(struct ib_event *, void *);
+       void                   *qp_context;
+       struct ib_cq           *send_cq;
+       struct ib_cq           *recv_cq;
+       struct ib_srq          *srq;
+       struct ib_qp_cap        cap;
+       enum ib_sig_type        sq_sig_type;
+       enum ib_sig_type        rq_sig_type;
+       enum ib_qp_type         qp_type;
+       u8                      port_num; /* special QP types only */
+};
+
+enum ib_rnr_timeout {
+       IB_RNR_TIMER_655_36 =  0,
+       IB_RNR_TIMER_000_01 =  1,
+       IB_RNR_TIMER_000_02 =  2,
+       IB_RNR_TIMER_000_03 =  3,
+       IB_RNR_TIMER_000_04 =  4,
+       IB_RNR_TIMER_000_06 =  5,
+       IB_RNR_TIMER_000_08 =  6,
+       IB_RNR_TIMER_000_12 =  7,
+       IB_RNR_TIMER_000_16 =  8,
+       IB_RNR_TIMER_000_24 =  9,
+       IB_RNR_TIMER_000_32 = 10,
+       IB_RNR_TIMER_000_48 = 11,
+       IB_RNR_TIMER_000_64 = 12,
+       IB_RNR_TIMER_000_96 = 13,
+       IB_RNR_TIMER_001_28 = 14,
+       IB_RNR_TIMER_001_92 = 15,
+       IB_RNR_TIMER_002_56 = 16,
+       IB_RNR_TIMER_003_84 = 17,
+       IB_RNR_TIMER_005_12 = 18,
+       IB_RNR_TIMER_007_68 = 19,
+       IB_RNR_TIMER_010_24 = 20,
+       IB_RNR_TIMER_015_36 = 21,
+       IB_RNR_TIMER_020_48 = 22,
+       IB_RNR_TIMER_030_72 = 23,
+       IB_RNR_TIMER_040_96 = 24,
+       IB_RNR_TIMER_061_44 = 25,
+       IB_RNR_TIMER_081_92 = 26,
+       IB_RNR_TIMER_122_88 = 27,
+       IB_RNR_TIMER_163_84 = 28,
+       IB_RNR_TIMER_245_76 = 29,
+       IB_RNR_TIMER_327_68 = 30,
+       IB_RNR_TIMER_491_52 = 31
+};
+
+enum ib_qp_attr_mask {
+       IB_QP_STATE                     = 1,
+       IB_QP_CUR_STATE                 = (1<<1),
+       IB_QP_EN_SQD_ASYNC_NOTIFY       = (1<<2),
+       IB_QP_ACCESS_FLAGS              = (1<<3),
+       IB_QP_PKEY_INDEX                = (1<<4),
+       IB_QP_PORT                      = (1<<5),
+       IB_QP_QKEY                      = (1<<6),
+       IB_QP_AV                        = (1<<7),
+       IB_QP_PATH_MTU                  = (1<<8),
+       IB_QP_TIMEOUT                   = (1<<9),
+       IB_QP_RETRY_CNT                 = (1<<10),
+       IB_QP_RNR_RETRY                 = (1<<11),
+       IB_QP_RQ_PSN                    = (1<<12),
+       IB_QP_MAX_QP_RD_ATOMIC          = (1<<13),
+       IB_QP_ALT_PATH                  = (1<<14),
+       IB_QP_MIN_RNR_TIMER             = (1<<15),
+       IB_QP_SQ_PSN                    = (1<<16),
+       IB_QP_MAX_DEST_RD_ATOMIC        = (1<<17),
+       IB_QP_PATH_MIG_STATE            = (1<<18),
+       IB_QP_CAP                       = (1<<19),
+       IB_QP_DEST_QPN                  = (1<<20)
+};
+
+enum ib_qp_state {
+       IB_QPS_RESET,
+       IB_QPS_INIT,
+       IB_QPS_RTR,
+       IB_QPS_RTS,
+       IB_QPS_SQD,
+       IB_QPS_SQE,
+       IB_QPS_ERR
+};
+
+enum ib_mig_state {
+       IB_MIG_MIGRATED,
+       IB_MIG_REARM,
+       IB_MIG_ARMED
+};
+
+struct ib_qp_attr {
+       enum ib_qp_state        qp_state;
+       enum ib_qp_state        cur_qp_state;
+       enum ib_mtu             path_mtu;
+       enum ib_mig_state       path_mig_state;
+       u32                     qkey;
+       u32                     rq_psn;
+       u32                     sq_psn;
+       u32                     dest_qp_num;
+       int                     qp_access_flags;
+       struct ib_qp_cap        cap;
+       struct ib_ah_attr       ah_attr;
+       struct ib_ah_attr       alt_ah_attr;
+       u16                     pkey_index;
+       u16                     alt_pkey_index;
+       u8                      en_sqd_async_notify;
+       u8                      sq_draining;
+       u8                      max_rd_atomic;
+       u8                      max_dest_rd_atomic;
+       u8                      min_rnr_timer;
+       u8                      port_num;
+       u8                      timeout;
+       u8                      retry_cnt;
+       u8                      rnr_retry;
+       u8                      alt_port_num;
+       u8                      alt_timeout;
+};
+
+enum ib_wr_opcode {
+       IB_WR_RDMA_WRITE,
+       IB_WR_RDMA_WRITE_WITH_IMM,
+       IB_WR_SEND,
+       IB_WR_SEND_WITH_IMM,
+       IB_WR_RDMA_READ,
+       IB_WR_ATOMIC_CMP_AND_SWP,
+       IB_WR_ATOMIC_FETCH_AND_ADD
+};
+
+enum ib_send_flags {
+       IB_SEND_FENCE           = 1,
+       IB_SEND_SIGNALED        = (1<<1),
+       IB_SEND_SOLICITED       = (1<<2),
+       IB_SEND_INLINE          = (1<<3)
+};
+
+enum ib_recv_flags {
+       IB_RECV_SIGNALED        = 1
+};
+
+struct ib_sge {
+       u64     addr;
+       u32     length;
+       u32     lkey;
+};
+
+struct ib_send_wr {
+       struct ib_send_wr      *next;
+       u64                     wr_id;
+       struct ib_sge          *sg_list;
+       int                     num_sge;
+       enum ib_wr_opcode       opcode;
+       int                     send_flags;
+       u32                     imm_data;
+       union {
+               struct {
+                       u64     remote_addr;
+                       u32     rkey;
+               } rdma;
+               struct {
+                       u64     remote_addr;
+                       u64     compare_add;
+                       u64     swap;
+                       u32     rkey;
+               } atomic;
+               struct {
+                       struct ib_ah *ah;
+                       struct ib_mad_hdr *mad_hdr;
+                       u32     remote_qpn;
+                       u32     remote_qkey;
+                       int     timeout_ms; /* valid for MADs only */
+                       u16     pkey_index; /* valid for GSI only */
+                       u8      port_num;   /* valid for DR SMPs on switch only */
+               } ud;
+       } wr;
+};
+
+struct ib_recv_wr {
+       struct ib_recv_wr      *next;
+       u64                     wr_id;
+       struct ib_sge          *sg_list;
+       int                     num_sge;
+       int                     recv_flags;
+};
+
+enum ib_access_flags {
+       IB_ACCESS_LOCAL_WRITE   = 1,
+       IB_ACCESS_REMOTE_WRITE  = (1<<1),
+       IB_ACCESS_REMOTE_READ   = (1<<2),
+       IB_ACCESS_REMOTE_ATOMIC = (1<<3),
+       IB_ACCESS_MW_BIND       = (1<<4)
+};
+
+struct ib_phys_buf {
+       u64      addr;
+       u64      size;
+};
+
+struct ib_mr_attr {
+       struct ib_pd    *pd;
+       u64             device_virt_addr;
+       u64             size;
+       int             mr_access_flags;
+       u32             lkey;
+       u32             rkey;
+};
+
+enum ib_mr_rereg_flags {
+       IB_MR_REREG_TRANS       = 1,
+       IB_MR_REREG_PD          = (1<<1),
+       IB_MR_REREG_ACCESS      = (1<<2)
+};
+
+struct ib_mw_bind {
+       struct ib_mr   *mr;
+       u64             wr_id;
+       u64             addr;
+       u32             length;
+       int             send_flags;
+       int             mw_access_flags;
+};
+
+struct ib_fmr_attr {
+       int     max_pages;
+       int     max_maps;
+       u8      page_size;
+};
+
+struct ib_pd {
+       struct ib_device *device;
+       atomic_t          usecnt; /* count all resources */
+};
+
+struct ib_ah {
+       struct ib_device        *device;
+       struct ib_pd            *pd;
+};
+
+typedef void (*ib_comp_handler)(struct ib_cq *cq, void *cq_context);
+
+struct ib_cq {
+       struct ib_device *device;
+       ib_comp_handler   comp_handler;
+       void             (*event_handler)(struct ib_event *, void *);
+       void *            cq_context;
+       int               cqe;
+       atomic_t          usecnt; /* count number of work queues */
+};
+
+struct ib_srq {
+       struct ib_device        *device;
+       struct ib_pd            *pd;
+       void                    *srq_context;
+       atomic_t                usecnt;
+};
+
+struct ib_qp {
+       struct ib_device       *device;
+       struct ib_pd           *pd;
+       struct ib_cq           *send_cq;
+       struct ib_cq           *recv_cq;
+       struct ib_srq          *srq;
+       void                  (*event_handler)(struct ib_event *, void *);
+       void                   *qp_context;
+       u32                     qp_num;
+       enum ib_qp_type         qp_type;
+};
+
+struct ib_mr {
+       struct ib_device *device;
+       struct ib_pd     *pd;
+       u32               lkey;
+       u32               rkey;
+       atomic_t          usecnt; /* count number of MWs */
+};
+
+struct ib_mw {
+       struct ib_device        *device;
+       struct ib_pd            *pd;
+       u32                     rkey;
+};
+
+struct ib_fmr {
+       struct ib_device        *device;
+       struct ib_pd            *pd;
+       struct list_head        list;
+       u32                     lkey;
+       u32                     rkey;
+};
+
+struct ib_mad;
+struct ib_grh;
+
+enum ib_process_mad_flags {
+       IB_MAD_IGNORE_MKEY      = 1,
+       IB_MAD_IGNORE_BKEY      = 2,
+       IB_MAD_IGNORE_ALL       = IB_MAD_IGNORE_MKEY | IB_MAD_IGNORE_BKEY
+};
+
+enum ib_mad_result {
+       IB_MAD_RESULT_FAILURE  = 0,      /* (!SUCCESS is the important flag) */
+       IB_MAD_RESULT_SUCCESS  = 1 << 0, /* MAD was successfully processed   */
+       IB_MAD_RESULT_REPLY    = 1 << 1, /* Reply packet needs to be sent    */
+       IB_MAD_RESULT_CONSUMED = 1 << 2  /* Packet consumed: stop processing */
+};
+
+#define IB_DEVICE_NAME_MAX 64
+
+struct ib_cache {
+       rwlock_t                lock;
+       struct ib_event_handler event_handler;
+       struct ib_pkey_cache  **pkey_cache;
+       struct ib_gid_cache   **gid_cache;
+};
+
+struct ib_device {
+       struct device                *dma_device;
+
+       char                          name[IB_DEVICE_NAME_MAX];
+
+       struct list_head              event_handler_list;
+       spinlock_t                    event_handler_lock;
+
+       struct list_head              core_list;
+       struct list_head              client_data_list;
+       spinlock_t                    client_data_lock;
+
+       struct ib_cache               cache;
+
+       u32                           flags;
+
+       int                        (*query_device)(struct ib_device *device,
+                                                  struct ib_device_attr *device_attr);
+       int                        (*query_port)(struct ib_device *device,
+                                                u8 port_num,
+                                                struct ib_port_attr *port_attr);
+       int                        (*query_gid)(struct ib_device *device,
+                                               u8 port_num, int index,
+                                               union ib_gid *gid);
+       int                        (*query_pkey)(struct ib_device *device,
+                                                u8 port_num, u16 index, u16 *pkey);
+       int                        (*modify_device)(struct ib_device *device,
+                                                   int device_modify_mask,
+                                                   struct ib_device_modify *device_modify);
+       int                        (*modify_port)(struct ib_device *device,
+                                                 u8 port_num, int port_modify_mask,
+                                                 struct ib_port_modify *port_modify);
+       struct ib_pd *             (*alloc_pd)(struct ib_device *device);
+       int                        (*dealloc_pd)(struct ib_pd *pd);
+       struct ib_ah *             (*create_ah)(struct ib_pd *pd,
+                                               struct ib_ah_attr *ah_attr);
+       int                        (*modify_ah)(struct ib_ah *ah,
+                                               struct ib_ah_attr *ah_attr);
+       int                        (*query_ah)(struct ib_ah *ah,
+                                              struct ib_ah_attr *ah_attr);
+       int                        (*destroy_ah)(struct ib_ah *ah);
+       struct ib_qp *             (*create_qp)(struct ib_pd *pd,
+                                               struct ib_qp_init_attr *qp_init_attr);
+       int                        (*modify_qp)(struct ib_qp *qp,
+                                               struct ib_qp_attr *qp_attr,
+                                               int qp_attr_mask);
+       int                        (*query_qp)(struct ib_qp *qp,
+                                              struct ib_qp_attr *qp_attr,
+                                              int qp_attr_mask,
+                                              struct ib_qp_init_attr *qp_init_attr);
+       int                        (*destroy_qp)(struct ib_qp *qp);
+       int                        (*post_send)(struct ib_qp *qp,
+                                               struct ib_send_wr *send_wr,
+                                               struct ib_send_wr **bad_send_wr);
+       int                        (*post_recv)(struct ib_qp *qp,
+                                               struct ib_recv_wr *recv_wr,
+                                               struct ib_recv_wr **bad_recv_wr);
+       struct ib_cq *             (*create_cq)(struct ib_device *device,
+                                               int cqe);
+       int                        (*destroy_cq)(struct ib_cq *cq);
+       int                        (*resize_cq)(struct ib_cq *cq, int *cqe);
+       int                        (*poll_cq)(struct ib_cq *cq, int num_entries,
+                                             struct ib_wc *wc);
+       int                        (*peek_cq)(struct ib_cq *cq, int wc_cnt);
+       int                        (*req_notify_cq)(struct ib_cq *cq,
+                                                   enum ib_cq_notify cq_notify);
+       int                        (*req_ncomp_notif)(struct ib_cq *cq,
+                                                     int wc_cnt);
+       struct ib_mr *             (*get_dma_mr)(struct ib_pd *pd,
+                                                int mr_access_flags);
+       struct ib_mr *             (*reg_phys_mr)(struct ib_pd *pd,
+                                                 struct ib_phys_buf *phys_buf_array,
+                                                 int num_phys_buf,
+                                                 int mr_access_flags,
+                                                 u64 *iova_start);
+       int                        (*query_mr)(struct ib_mr *mr,
+                                              struct ib_mr_attr *mr_attr);
+       int                        (*dereg_mr)(struct ib_mr *mr);
+       int                        (*rereg_phys_mr)(struct ib_mr *mr,
+                                                   int mr_rereg_mask,
+                                                   struct ib_pd *pd,
+                                                   struct ib_phys_buf *phys_buf_array,
+                                                   int num_phys_buf,
+                                                   int mr_access_flags,
+                                                   u64 *iova_start);
+       struct ib_mw *             (*alloc_mw)(struct ib_pd *pd);
+       int                        (*bind_mw)(struct ib_qp *qp,
+                                             struct ib_mw *mw,
+                                             struct ib_mw_bind *mw_bind);
+       int                        (*dealloc_mw)(struct ib_mw *mw);
+       struct ib_fmr *            (*alloc_fmr)(struct ib_pd *pd,
+                                               int mr_access_flags,
+                                               struct ib_fmr_attr *fmr_attr);
+       int                        (*map_phys_fmr)(struct ib_fmr *fmr,
+                                                  u64 *page_list, int list_len,
+                                                  u64 iova);
+       int                        (*unmap_fmr)(struct list_head *fmr_list);
+       int                        (*dealloc_fmr)(struct ib_fmr *fmr);
+       int                        (*attach_mcast)(struct ib_qp *qp,
+                                                  union ib_gid *gid,
+                                                  u16 lid);
+       int                        (*detach_mcast)(struct ib_qp *qp,
+                                                  union ib_gid *gid,
+                                                  u16 lid);
+       int                        (*process_mad)(struct ib_device *device,
+                                                 int process_mad_flags,
+                                                 u8 port_num,
+                                                 struct ib_wc *in_wc,
+                                                 struct ib_grh *in_grh,
+                                                 struct ib_mad *in_mad,
+                                                 struct ib_mad *out_mad);
+
+       struct class_device          class_dev;
+       struct kobject               ports_parent;
+       struct list_head             port_list;
+
+       enum {
+               IB_DEV_UNINITIALIZED,
+               IB_DEV_REGISTERED,
+               IB_DEV_UNREGISTERED
+       }                            reg_state;
+
+       u8                           node_type;
+       u8                           phys_port_cnt;
+};
+
+struct ib_client {
+       char  *name;
+       void (*add)   (struct ib_device *);
+       void (*remove)(struct ib_device *);
+
+       struct list_head list;
+};
+
+struct ib_device *ib_alloc_device(size_t size);
+void ib_dealloc_device(struct ib_device *device);
+
+int ib_register_device   (struct ib_device *device);
+void ib_unregister_device(struct ib_device *device);
+
+int ib_register_client   (struct ib_client *client);
+void ib_unregister_client(struct ib_client *client);
+
+void *ib_get_client_data(struct ib_device *device, struct ib_client *client);
+void  ib_set_client_data(struct ib_device *device, struct ib_client *client,
+                        void *data);
+
+int ib_register_event_handler  (struct ib_event_handler *event_handler);
+int ib_unregister_event_handler(struct ib_event_handler *event_handler);
+void ib_dispatch_event(struct ib_event *event);
+
+int ib_query_device(struct ib_device *device,
+                   struct ib_device_attr *device_attr);
+
+int ib_query_port(struct ib_device *device,
+                 u8 port_num, struct ib_port_attr *port_attr);
+
+int ib_query_gid(struct ib_device *device,
+                u8 port_num, int index, union ib_gid *gid);
+
+int ib_query_pkey(struct ib_device *device,
+                 u8 port_num, u16 index, u16 *pkey);
+
+int ib_modify_device(struct ib_device *device,
+                    int device_modify_mask,
+                    struct ib_device_modify *device_modify);
+
+int ib_modify_port(struct ib_device *device,
+                  u8 port_num, int port_modify_mask,
+                  struct ib_port_modify *port_modify);
+
+/**
+ * ib_alloc_pd - Allocates an unused protection domain.
+ * @device: The device on which to allocate the protection domain.
+ *
+ * A protection domain object provides an association between QPs, shared
+ * receive queues, address handles, memory regions, and memory windows.
+ */
+struct ib_pd *ib_alloc_pd(struct ib_device *device);
+
+/**
+ * ib_dealloc_pd - Deallocates a protection domain.
+ * @pd: The protection domain to deallocate.
+ */
+int ib_dealloc_pd(struct ib_pd *pd);
+
+/**
+ * ib_create_ah - Creates an address handle for the given address vector.
+ * @pd: The protection domain associated with the address handle.
+ * @ah_attr: The attributes of the address vector.
+ *
+ * The address handle is used to reference a local or global destination
+ * in all UD QP post sends.
+ */
+struct ib_ah *ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr);
+
+/**
+ * ib_modify_ah - Modifies the address vector associated with an address
+ *   handle.
+ * @ah: The address handle to modify.
+ * @ah_attr: The new address vector attributes to associate with the
+ *   address handle.
+ */
+int ib_modify_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr);
+
+/**
+ * ib_query_ah - Queries the address vector associated with an address
+ *   handle.
+ * @ah: The address handle to query.
+ * @ah_attr: The address vector attributes associated with the address
+ *   handle.
+ */
+int ib_query_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr);
+
+/**
+ * ib_destroy_ah - Destroys an address handle.
+ * @ah: The address handle to destroy.
+ */
+int ib_destroy_ah(struct ib_ah *ah);
+
+/**
+ * ib_create_qp - Creates a QP associated with the specified protection
+ *   domain.
+ * @pd: The protection domain associated with the QP.
+ * @qp_init_attr: A list of initial attributes required to create the QP.
+ */
+struct ib_qp *ib_create_qp(struct ib_pd *pd,
+                          struct ib_qp_init_attr *qp_init_attr);
+
+/**
+ * ib_modify_qp - Modifies the attributes for the specified QP and then
+ *   transitions the QP to the given state.
+ * @qp: The QP to modify.
+ * @qp_attr: On input, specifies the QP attributes to modify.  On output,
+ *   the current values of selected QP attributes are returned.
+ * @qp_attr_mask: A bit-mask used to specify which attributes of the QP
+ *   are being modified.
+ */
+int ib_modify_qp(struct ib_qp *qp,
+                struct ib_qp_attr *qp_attr,
+                int qp_attr_mask);
+
+/**
+ * ib_query_qp - Returns the attribute list and current values for the
+ *   specified QP.
+ * @qp: The QP to query.
+ * @qp_attr: The attributes of the specified QP.
+ * @qp_attr_mask: A bit-mask used to select specific attributes to query.
+ * @qp_init_attr: Additional attributes of the selected QP.
+ *
+ * The qp_attr_mask may be used to limit the query to gathering only the
+ * selected attributes.
+ */
+int ib_query_qp(struct ib_qp *qp,
+               struct ib_qp_attr *qp_attr,
+               int qp_attr_mask,
+               struct ib_qp_init_attr *qp_init_attr);
+
+/**
+ * ib_destroy_qp - Destroys the specified QP.
+ * @qp: The QP to destroy.
+ */
+int ib_destroy_qp(struct ib_qp *qp);
+
+/**
+ * ib_post_send - Posts a list of work requests to the send queue of
+ *   the specified QP.
+ * @qp: The QP to post the work request on.
+ * @send_wr: A list of work requests to post on the send queue.
+ * @bad_send_wr: On an immediate failure, this parameter will reference
+ *   the work request that failed to be posted on the QP.
+ */
+static inline int ib_post_send(struct ib_qp *qp,
+                              struct ib_send_wr *send_wr,
+                              struct ib_send_wr **bad_send_wr)
+{
+       return qp->device->post_send(qp, send_wr, bad_send_wr);
+}
+
+/**
+ * ib_post_recv - Posts a list of work requests to the receive queue of
+ *   the specified QP.
+ * @qp: The QP to post the work request on.
+ * @recv_wr: A list of work requests to post on the receive queue.
+ * @bad_recv_wr: On an immediate failure, this parameter will reference
+ *   the work request that failed to be posted on the QP.
+ */
+static inline int ib_post_recv(struct ib_qp *qp,
+                              struct ib_recv_wr *recv_wr,
+                              struct ib_recv_wr **bad_recv_wr)
+{
+       return qp->device->post_recv(qp, recv_wr, bad_recv_wr);
+}
+
+/**
+ * ib_create_cq - Creates a CQ on the specified device.
+ * @device: The device on which to create the CQ.
+ * @comp_handler: A user-specified callback that is invoked when a
+ *   completion event occurs on the CQ.
+ * @event_handler: A user-specified callback that is invoked when an
+ *   asynchronous event not associated with a completion occurs on the CQ.
+ * @cq_context: Context associated with the CQ returned to the user via
+ *   the associated completion and event handlers.
+ * @cqe: The minimum size of the CQ.
+ *
+ * Users can examine the cq structure to determine the actual CQ size.
+ */
+struct ib_cq *ib_create_cq(struct ib_device *device,
+                          ib_comp_handler comp_handler,
+                          void (*event_handler)(struct ib_event *, void *),
+                          void *cq_context, int cqe);
+
+/**
+ * ib_resize_cq - Modifies the capacity of the CQ.
+ * @cq: The CQ to resize.
+ * @cqe: The minimum size of the CQ.
+ *
+ * Users can examine the cq structure to determine the actual CQ size.
+ */
+int ib_resize_cq(struct ib_cq *cq, int cqe);
+
+/**
+ * ib_destroy_cq - Destroys the specified CQ.
+ * @cq: The CQ to destroy.
+ */
+int ib_destroy_cq(struct ib_cq *cq);
+
+/**
+ * ib_poll_cq - poll a CQ for completion(s)
+ * @cq:the CQ being polled
+ * @num_entries:maximum number of completions to return
+ * @wc:array of at least @num_entries &struct ib_wc where completions
+ *   will be returned
+ *
+ * Poll a CQ for (possibly multiple) completions.  If the return value
+ * is < 0, an error occurred.  If the return value is >= 0, it is the
+ * number of completions returned.  If the return value is
+ * non-negative and < num_entries, then the CQ was emptied.
+ */
+static inline int ib_poll_cq(struct ib_cq *cq, int num_entries,
+                            struct ib_wc *wc)
+{
+       return cq->device->poll_cq(cq, num_entries, wc);
+}
+
+/**
+ * ib_peek_cq - Returns the number of unreaped completions currently
+ *   on the specified CQ.
+ * @cq: The CQ to peek.
+ * @wc_cnt: A minimum number of unreaped completions to check for.
+ *
+ * If the number of unreaped completions is greater than or equal to wc_cnt,
+ * this function returns wc_cnt, otherwise, it returns the actual number of
+ * unreaped completions.
+ */
+int ib_peek_cq(struct ib_cq *cq, int wc_cnt);
+
+/**
+ * ib_req_notify_cq - Request completion notification on a CQ.
+ * @cq: The CQ to generate an event for.
+ * @cq_notify: If set to %IB_CQ_SOLICITED, completion notification will
+ *   occur on the next solicited event. If set to %IB_CQ_NEXT_COMP,
+ *   notification will occur on the next completion.
+ */
+static inline int ib_req_notify_cq(struct ib_cq *cq,
+                                  enum ib_cq_notify cq_notify)
+{
+       return cq->device->req_notify_cq(cq, cq_notify);
+}
+
+/**
+ * ib_req_ncomp_notif - Request completion notification when there are
+ *   at least the specified number of unreaped completions on the CQ.
+ * @cq: The CQ to generate an event for.
+ * @wc_cnt: The number of unreaped completions that should be on the
+ *   CQ before an event is generated.
+ */
+static inline int ib_req_ncomp_notif(struct ib_cq *cq, int wc_cnt)
+{
+       return cq->device->req_ncomp_notif ?
+               cq->device->req_ncomp_notif(cq, wc_cnt) :
+               -ENOSYS;
+}
+
+/**
+ * ib_get_dma_mr - Returns a memory region for system memory that is
+ *   usable for DMA.
+ * @pd: The protection domain associated with the memory region.
+ * @mr_access_flags: Specifies the memory access rights.
+ */
+struct ib_mr *ib_get_dma_mr(struct ib_pd *pd, int mr_access_flags);
+
+/**
+ * ib_reg_phys_mr - Prepares a virtually addressed memory region for use
+ *   by an HCA.
+ * @pd: The protection domain associated assigned to the registered region.
+ * @phys_buf_array: Specifies a list of physical buffers to use in the
+ *   memory region.
+ * @num_phys_buf: Specifies the size of the phys_buf_array.
+ * @mr_access_flags: Specifies the memory access rights.
+ * @iova_start: The offset of the region's starting I/O virtual address.
+ */
+struct ib_mr *ib_reg_phys_mr(struct ib_pd *pd,
+                            struct ib_phys_buf *phys_buf_array,
+                            int num_phys_buf,
+                            int mr_access_flags,
+                            u64 *iova_start);
+
+/**
+ * ib_rereg_phys_mr - Modifies the attributes of an existing memory region.
+ *   Conceptually, this call performs the functions deregister memory region
+ *   followed by register physical memory region.  Where possible,
+ *   resources are reused instead of deallocated and reallocated.
+ * @mr: The memory region to modify.
+ * @mr_rereg_mask: A bit-mask used to indicate which of the following
+ *   properties of the memory region are being modified.
+ * @pd: If %IB_MR_REREG_PD is set in mr_rereg_mask, this field specifies
+ *   the new protection domain to associated with the memory region,
+ *   otherwise, this parameter is ignored.
+ * @phys_buf_array: If %IB_MR_REREG_TRANS is set in mr_rereg_mask, this
+ *   field specifies a list of physical buffers to use in the new
+ *   translation, otherwise, this parameter is ignored.
+ * @num_phys_buf: If %IB_MR_REREG_TRANS is set in mr_rereg_mask, this
+ *   field specifies the size of the phys_buf_array, otherwise, this
+ *   parameter is ignored.
+ * @mr_access_flags: If %IB_MR_REREG_ACCESS is set in mr_rereg_mask, this
+ *   field specifies the new memory access rights, otherwise, this
+ *   parameter is ignored.
+ * @iova_start: The offset of the region's starting I/O virtual address.
+ */
+int ib_rereg_phys_mr(struct ib_mr *mr,
+                    int mr_rereg_mask,
+                    struct ib_pd *pd,
+                    struct ib_phys_buf *phys_buf_array,
+                    int num_phys_buf,
+                    int mr_access_flags,
+                    u64 *iova_start);
+
+/**
+ * ib_query_mr - Retrieves information about a specific memory region.
+ * @mr: The memory region to retrieve information about.
+ * @mr_attr: The attributes of the specified memory region.
+ */
+int ib_query_mr(struct ib_mr *mr, struct ib_mr_attr *mr_attr);
+
+/**
+ * ib_dereg_mr - Deregisters a memory region and removes it from the
+ *   HCA translation table.
+ * @mr: The memory region to deregister.
+ */
+int ib_dereg_mr(struct ib_mr *mr);
+
+/**
+ * ib_alloc_mw - Allocates a memory window.
+ * @pd: The protection domain associated with the memory window.
+ */
+struct ib_mw *ib_alloc_mw(struct ib_pd *pd);
+
+/**
+ * ib_bind_mw - Posts a work request to the send queue of the specified
+ *   QP, which binds the memory window to the given address range and
+ *   remote access attributes.
+ * @qp: QP to post the bind work request on.
+ * @mw: The memory window to bind.
+ * @mw_bind: Specifies information about the memory window, including
+ *   its address range, remote access rights, and associated memory region.
+ */
+static inline int ib_bind_mw(struct ib_qp *qp,
+                            struct ib_mw *mw,
+                            struct ib_mw_bind *mw_bind)
+{
+       /* XXX reference counting in corresponding MR? */
+       return mw->device->bind_mw ?
+               mw->device->bind_mw(qp, mw, mw_bind) :
+               -ENOSYS;
+}
+
+/**
+ * ib_dealloc_mw - Deallocates a memory window.
+ * @mw: The memory window to deallocate.
+ */
+int ib_dealloc_mw(struct ib_mw *mw);
+
+/**
+ * ib_alloc_fmr - Allocates a unmapped fast memory region.
+ * @pd: The protection domain associated with the unmapped region.
+ * @mr_access_flags: Specifies the memory access rights.
+ * @fmr_attr: Attributes of the unmapped region.
+ *
+ * A fast memory region must be mapped before it can be used as part of
+ * a work request.
+ */
+struct ib_fmr *ib_alloc_fmr(struct ib_pd *pd,
+                           int mr_access_flags,
+                           struct ib_fmr_attr *fmr_attr);
+
+/**
+ * ib_map_phys_fmr - Maps a list of physical pages to a fast memory region.
+ * @fmr: The fast memory region to associate with the pages.
+ * @page_list: An array of physical pages to map to the fast memory region.
+ * @list_len: The number of pages in page_list.
+ * @iova: The I/O virtual address to use with the mapped region.
+ */
+static inline int ib_map_phys_fmr(struct ib_fmr *fmr,
+                                 u64 *page_list, int list_len,
+                                 u64 iova)
+{
+       return fmr->device->map_phys_fmr(fmr, page_list, list_len, iova);
+}
+
+/**
+ * ib_unmap_fmr - Removes the mapping from a list of fast memory regions.
+ * @fmr_list: A linked list of fast memory regions to unmap.
+ */
+int ib_unmap_fmr(struct list_head *fmr_list);
+
+/**
+ * ib_dealloc_fmr - Deallocates a fast memory region.
+ * @fmr: The fast memory region to deallocate.
+ */
+int ib_dealloc_fmr(struct ib_fmr *fmr);
+
+/**
+ * ib_attach_mcast - Attaches the specified QP to a multicast group.
+ * @qp: QP to attach to the multicast group.  The QP must be type
+ *   IB_QPT_UD.
+ * @gid: Multicast group GID.
+ * @lid: Multicast group LID in host byte order.
+ *
+ * In order to send and receive multicast packets, subnet
+ * administration must have created the multicast group and configured
+ * the fabric appropriately.  The port associated with the specified
+ * QP must also be a member of the multicast group.
+ */
+int ib_attach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid);
+
+/**
+ * ib_detach_mcast - Detaches the specified QP from a multicast group.
+ * @qp: QP to detach from the multicast group.
+ * @gid: Multicast group GID.
+ * @lid: Multicast group LID in host byte order.
+ */
+int ib_detach_mcast(struct ib_qp *qp, union ib_gid *gid, u16 lid);
+
+#endif /* IB_VERBS_H */
diff --git a/drivers/infiniband/ulp/ipoib/Kconfig b/drivers/infiniband/ulp/ipoib/Kconfig
new file mode 100644 (file)
index 0000000..8d2e04c
--- /dev/null
@@ -0,0 +1,33 @@
+config INFINIBAND_IPOIB
+       tristate "IP-over-InfiniBand"
+       depends on INFINIBAND && NETDEVICES && INET
+       ---help---
+         Support for the IP-over-InfiniBand protocol (IPoIB). This
+         transports IP packets over InfiniBand so you can use your IB
+         device as a fancy NIC.
+
+         The IPoIB protocol is defined by the IETF ipoib working
+         group: <http://www.ietf.org/html.charters/ipoib-charter.html>.
+
+config INFINIBAND_IPOIB_DEBUG
+       bool "IP-over-InfiniBand debugging"
+       depends on INFINIBAND_IPOIB
+       ---help---
+         This option causes debugging code to be compiled into the
+         IPoIB driver.  The output can be turned on via the
+         debug_level and mcast_debug_level module parameters (which
+         can also be set after the driver is loaded through sysfs).
+
+         This option also creates an "ipoib_debugfs," which can be
+         mounted to expose debugging information about IB multicast
+         groups used by the IPoIB driver.
+
+config INFINIBAND_IPOIB_DEBUG_DATA
+       bool "IP-over-InfiniBand data path debugging"
+       depends on INFINIBAND_IPOIB_DEBUG
+       ---help---
+         This option compiles debugging code into the the data path
+         of the IPoIB driver.  The output can be turned on via the
+         data_debug_level module parameter; however, even with output
+         turned off, this debugging code will have some performance
+         impact.
diff --git a/drivers/infiniband/ulp/ipoib/Makefile b/drivers/infiniband/ulp/ipoib/Makefile
new file mode 100644 (file)
index 0000000..394bc08
--- /dev/null
@@ -0,0 +1,11 @@
+EXTRA_CFLAGS += -Idrivers/infiniband/include
+
+obj-$(CONFIG_INFINIBAND_IPOIB)                 += ib_ipoib.o
+
+ib_ipoib-y                                     := ipoib_main.o \
+                                                  ipoib_ib.o \
+                                                  ipoib_multicast.o \
+                                                  ipoib_verbs.o \
+                                                  ipoib_vlan.o
+ib_ipoib-$(CONFIG_INFINIBAND_IPOIB_DEBUG)      += ipoib_fs.o
+
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
new file mode 100644 (file)
index 0000000..074394d
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ipoib.h 1358 2004-12-17 22:00:11Z roland $
+ */
+
+#ifndef _IPOIB_H
+#define _IPOIB_H
+
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/config.h>
+#include <linux/kref.h>
+#include <linux/if_infiniband.h>
+
+#include <net/neighbour.h>
+
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+
+#include <ib_verbs.h>
+#include <ib_pack.h>
+#include <ib_sa.h>
+
+/* constants */
+
+enum {
+       IPOIB_PACKET_SIZE         = 2048,
+       IPOIB_BUF_SIZE            = IPOIB_PACKET_SIZE + IB_GRH_BYTES,
+
+       IPOIB_ENCAP_LEN           = 4,
+
+       IPOIB_RX_RING_SIZE        = 128,
+       IPOIB_TX_RING_SIZE        = 64,
+
+       IPOIB_NUM_WC              = 4,
+
+       IPOIB_MAX_PATH_REC_QUEUE  = 3,
+       IPOIB_MAX_MCAST_QUEUE     = 3,
+
+       IPOIB_FLAG_OPER_UP        = 0,
+       IPOIB_FLAG_ADMIN_UP       = 1,
+       IPOIB_PKEY_ASSIGNED       = 2,
+       IPOIB_PKEY_STOP           = 3,
+       IPOIB_FLAG_SUBINTERFACE   = 4,
+       IPOIB_MCAST_RUN           = 5,
+       IPOIB_STOP_REAPER         = 6,
+
+       IPOIB_MAX_BACKOFF_SECONDS = 16,
+
+       IPOIB_MCAST_FLAG_FOUND    = 0,  /* used in set_multicast_list */
+       IPOIB_MCAST_FLAG_SENDONLY = 1,
+       IPOIB_MCAST_FLAG_BUSY     = 2,  /* joining or already joined */
+       IPOIB_MCAST_FLAG_ATTACHED = 3,
+};
+
+/* structs */
+
+struct ipoib_header {
+       u16 proto;
+       u16 reserved;
+};
+
+struct ipoib_pseudoheader {
+       u8  hwaddr[INFINIBAND_ALEN];
+};
+
+struct ipoib_mcast;
+
+struct ipoib_buf {
+       struct sk_buff *skb;
+       DECLARE_PCI_UNMAP_ADDR(mapping)
+};
+
+/*
+ * Device private locking: tx_lock protects members used in TX fast
+ * path (and we use LLTX so upper layers don't do extra locking).
+ * lock protects everything else.  lock nests inside of tx_lock (ie
+ * tx_lock must be acquired first if needed).
+ */
+struct ipoib_dev_priv {
+       spinlock_t lock;
+
+       struct net_device *dev;
+
+       unsigned long flags;
+
+       struct semaphore mcast_mutex;
+       struct semaphore vlan_mutex;
+
+       struct rb_root  path_tree;
+       struct list_head path_list;
+
+       struct ipoib_mcast *broadcast;
+       struct list_head multicast_list;
+       struct rb_root multicast_tree;
+
+       struct work_struct pkey_task;
+       struct work_struct mcast_task;
+       struct work_struct flush_task;
+       struct work_struct restart_task;
+       struct work_struct ah_reap_task;
+
+       struct ib_device *ca;
+       u8                port;
+       u16               pkey;
+       struct ib_pd     *pd;
+       struct ib_mr     *mr;
+       struct ib_cq     *cq;
+       struct ib_qp     *qp;
+       u32               qkey;
+
+       union ib_gid local_gid;
+       u16          local_lid;
+       u8           local_rate;
+
+       unsigned int admin_mtu;
+       unsigned int mcast_mtu;
+
+       struct ipoib_buf *rx_ring;
+
+       spinlock_t        tx_lock;
+       struct ipoib_buf *tx_ring;
+       unsigned          tx_head;
+       unsigned          tx_tail;
+       struct ib_sge     tx_sge;
+       struct ib_send_wr tx_wr;
+
+       struct ib_wc ibwc[IPOIB_NUM_WC];
+
+       struct list_head dead_ahs;
+
+       struct ib_event_handler event_handler;
+
+       struct net_device_stats stats;
+
+       struct net_device *parent;
+       struct list_head child_intfs;
+       struct list_head list;
+
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
+       struct list_head fs_list;
+       struct dentry *mcg_dentry;
+#endif
+};
+
+struct ipoib_ah {
+       struct net_device *dev;
+       struct ib_ah      *ah;
+       struct list_head   list;
+       struct kref        ref;
+       unsigned           last_send;
+};
+
+struct ipoib_path {
+       struct net_device    *dev;
+       struct ib_sa_path_rec pathrec;
+       struct ipoib_ah      *ah;
+       struct sk_buff_head   queue;
+
+       struct list_head      neigh_list;
+
+       int                   query_id;
+       struct ib_sa_query   *query;
+       struct completion     done;
+
+       struct rb_node        rb_node;
+       struct list_head      list;
+};
+
+struct ipoib_neigh {
+       struct ipoib_ah    *ah;
+       struct sk_buff_head queue;
+
+       struct neighbour   *neighbour;
+
+       struct list_head    list;
+};
+
+static inline struct ipoib_neigh **to_ipoib_neigh(struct neighbour *neigh)
+{
+       return (struct ipoib_neigh **) (neigh->ha + 24 -
+                                       (offsetof(struct neighbour, ha) & 4));
+}
+
+extern struct workqueue_struct *ipoib_workqueue;
+
+/* functions */
+
+void ipoib_ib_completion(struct ib_cq *cq, void *dev_ptr);
+
+struct ipoib_ah *ipoib_create_ah(struct net_device *dev,
+                                struct ib_pd *pd, struct ib_ah_attr *attr);
+void ipoib_free_ah(struct kref *kref);
+static inline void ipoib_put_ah(struct ipoib_ah *ah)
+{
+       kref_put(&ah->ref, ipoib_free_ah);
+}
+
+int ipoib_add_pkey_attr(struct net_device *dev);
+
+void ipoib_send(struct net_device *dev, struct sk_buff *skb,
+               struct ipoib_ah *address, u32 qpn);
+void ipoib_reap_ah(void *dev_ptr);
+
+void ipoib_flush_paths(struct net_device *dev);
+struct ipoib_dev_priv *ipoib_intf_alloc(const char *format);
+
+int ipoib_ib_dev_init(struct net_device *dev, struct ib_device *ca, int port);
+void ipoib_ib_dev_flush(void *dev);
+void ipoib_ib_dev_cleanup(struct net_device *dev);
+
+int ipoib_ib_dev_open(struct net_device *dev);
+int ipoib_ib_dev_up(struct net_device *dev);
+int ipoib_ib_dev_down(struct net_device *dev);
+int ipoib_ib_dev_stop(struct net_device *dev);
+
+int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port);
+void ipoib_dev_cleanup(struct net_device *dev);
+
+void ipoib_mcast_join_task(void *dev_ptr);
+void ipoib_mcast_send(struct net_device *dev, union ib_gid *mgid,
+                     struct sk_buff *skb);
+
+void ipoib_mcast_restart_task(void *dev_ptr);
+int ipoib_mcast_start_thread(struct net_device *dev);
+int ipoib_mcast_stop_thread(struct net_device *dev);
+
+void ipoib_mcast_dev_down(struct net_device *dev);
+void ipoib_mcast_dev_flush(struct net_device *dev);
+
+struct ipoib_mcast_iter *ipoib_mcast_iter_init(struct net_device *dev);
+void ipoib_mcast_iter_free(struct ipoib_mcast_iter *iter);
+int ipoib_mcast_iter_next(struct ipoib_mcast_iter *iter);
+void ipoib_mcast_iter_read(struct ipoib_mcast_iter *iter,
+                                 union ib_gid *gid,
+                                 unsigned long *created,
+                                 unsigned int *queuelen,
+                                 unsigned int *complete,
+                                 unsigned int *send_only);
+
+int ipoib_mcast_attach(struct net_device *dev, u16 mlid,
+                      union ib_gid *mgid);
+int ipoib_mcast_detach(struct net_device *dev, u16 mlid,
+                      union ib_gid *mgid);
+
+int ipoib_qp_create(struct net_device *dev);
+int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca);
+void ipoib_transport_dev_cleanup(struct net_device *dev);
+
+void ipoib_event(struct ib_event_handler *handler,
+                struct ib_event *record);
+
+int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey);
+int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey);
+
+void ipoib_pkey_poll(void *dev);
+int ipoib_pkey_dev_delay_open(struct net_device *dev);
+
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
+int ipoib_create_debug_file(struct net_device *dev);
+void ipoib_delete_debug_file(struct net_device *dev);
+int ipoib_register_debugfs(void);
+void ipoib_unregister_debugfs(void);
+#else
+static inline int ipoib_create_debug_file(struct net_device *dev) { return 0; }
+static inline void ipoib_delete_debug_file(struct net_device *dev) { }
+static inline int ipoib_register_debugfs(void) { return 0; }
+static inline void ipoib_unregister_debugfs(void) { }
+#endif
+
+
+#define ipoib_printk(level, priv, format, arg...)      \
+       printk(level "%s: " format, ((struct ipoib_dev_priv *) priv)->dev->name , ## arg)
+#define ipoib_warn(priv, format, arg...)               \
+       ipoib_printk(KERN_WARNING, priv, format , ## arg)
+
+
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
+extern int debug_level;
+
+#define ipoib_dbg(priv, format, arg...)                        \
+       do {                                            \
+               if (debug_level > 0)                    \
+                       ipoib_printk(KERN_DEBUG, priv, format , ## arg); \
+       } while (0)
+#define ipoib_dbg_mcast(priv, format, arg...)          \
+       do {                                            \
+               if (mcast_debug_level > 0)              \
+                       ipoib_printk(KERN_DEBUG, priv, format , ## arg); \
+       } while (0)
+#else /* CONFIG_INFINIBAND_IPOIB_DEBUG */
+#define ipoib_dbg(priv, format, arg...)                        \
+       do { (void) (priv); } while (0)
+#define ipoib_dbg_mcast(priv, format, arg...)          \
+       do { (void) (priv); } while (0)
+#endif /* CONFIG_INFINIBAND_IPOIB_DEBUG */
+
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG_DATA
+#define ipoib_dbg_data(priv, format, arg...)           \
+       do {                                            \
+               if (data_debug_level > 0)               \
+                       ipoib_printk(KERN_DEBUG, priv, format , ## arg); \
+       } while (0)
+#else /* CONFIG_INFINIBAND_IPOIB_DEBUG_DATA */
+#define ipoib_dbg_data(priv, format, arg...)           \
+       do { (void) (priv); } while (0)
+#endif /* CONFIG_INFINIBAND_IPOIB_DEBUG_DATA */
+
+
+#define IPOIB_GID_FMT          "%x:%x:%x:%x:%x:%x:%x:%x"
+
+#define IPOIB_GID_ARG(gid)     be16_to_cpup((__be16 *) ((gid).raw +  0)), \
+                               be16_to_cpup((__be16 *) ((gid).raw +  2)), \
+                               be16_to_cpup((__be16 *) ((gid).raw +  4)), \
+                               be16_to_cpup((__be16 *) ((gid).raw +  6)), \
+                               be16_to_cpup((__be16 *) ((gid).raw +  8)), \
+                               be16_to_cpup((__be16 *) ((gid).raw + 10)), \
+                               be16_to_cpup((__be16 *) ((gid).raw + 12)), \
+                               be16_to_cpup((__be16 *) ((gid).raw + 14))
+
+#endif /* _IPOIB_H */
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
new file mode 100644 (file)
index 0000000..044f2c7
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ipoib_fs.c 1389 2004-12-27 22:56:47Z roland $
+ */
+
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+
+#include "ipoib.h"
+
+enum {
+       IPOIB_MAGIC = 0x49504942 /* "IPIB" */
+};
+
+static DECLARE_MUTEX(ipoib_fs_mutex);
+static struct dentry *ipoib_root;
+static struct super_block *ipoib_sb;
+static LIST_HEAD(ipoib_device_list);
+
+static void *ipoib_mcg_seq_start(struct seq_file *file, loff_t *pos)
+{
+       struct ipoib_mcast_iter *iter;
+       loff_t n = *pos;
+
+       iter = ipoib_mcast_iter_init(file->private);
+       if (!iter)
+               return NULL;
+
+       while (n--) {
+               if (ipoib_mcast_iter_next(iter)) {
+                       ipoib_mcast_iter_free(iter);
+                       return NULL;
+               }
+       }
+
+       return iter;
+}
+
+static void *ipoib_mcg_seq_next(struct seq_file *file, void *iter_ptr,
+                                  loff_t *pos)
+{
+       struct ipoib_mcast_iter *iter = iter_ptr;
+
+       (*pos)++;
+
+       if (ipoib_mcast_iter_next(iter)) {
+               ipoib_mcast_iter_free(iter);
+               return NULL;
+       }
+
+       return iter;
+}
+
+static void ipoib_mcg_seq_stop(struct seq_file *file, void *iter_ptr)
+{
+       /* nothing for now */
+}
+
+static int ipoib_mcg_seq_show(struct seq_file *file, void *iter_ptr)
+{
+       struct ipoib_mcast_iter *iter = iter_ptr;
+       char gid_buf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
+       union ib_gid mgid;
+       int i, n;
+       unsigned long created;
+       unsigned int queuelen, complete, send_only;
+
+       if (iter) {
+               ipoib_mcast_iter_read(iter, &mgid, &created, &queuelen,
+                                     &complete, &send_only);
+
+               for (n = 0, i = 0; i < sizeof mgid / 2; ++i) {
+                       n += sprintf(gid_buf + n, "%x",
+                                    be16_to_cpu(((u16 *)mgid.raw)[i]));
+                       if (i < sizeof mgid / 2 - 1)
+                               gid_buf[n++] = ':';
+               }
+       }
+
+       seq_printf(file, "GID: %*s", -(1 + (int) sizeof gid_buf), gid_buf);
+
+       seq_printf(file,
+                  " created: %10ld queuelen: %4d complete: %d send_only: %d\n",
+                  created, queuelen, complete, send_only);
+
+       return 0;
+}
+
+static struct seq_operations ipoib_seq_ops = {
+       .start = ipoib_mcg_seq_start,
+       .next  = ipoib_mcg_seq_next,
+       .stop  = ipoib_mcg_seq_stop,
+       .show  = ipoib_mcg_seq_show,
+};
+
+static int ipoib_mcg_open(struct inode *inode, struct file *file)
+{
+       struct seq_file *seq;
+       int ret;
+
+       ret = seq_open(file, &ipoib_seq_ops);
+       if (ret)
+               return ret;
+
+       seq = file->private_data;
+       seq->private = inode->u.generic_ip;
+
+       return 0;
+}
+
+static struct file_operations ipoib_fops = {
+       .owner   = THIS_MODULE,
+       .open    = ipoib_mcg_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = seq_release
+};
+
+static struct inode *ipoib_get_inode(void)
+{
+       struct inode *inode = new_inode(ipoib_sb);
+
+       if (inode) {
+               inode->i_mode    = S_IFREG | S_IRUGO;
+               inode->i_uid     = 0;
+               inode->i_gid     = 0;
+               inode->i_blksize = PAGE_CACHE_SIZE;
+               inode->i_blocks  = 0;
+               inode->i_atime   = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+               inode->i_fop     = &ipoib_fops;
+       }
+
+       return inode;
+}
+
+static int __ipoib_create_debug_file(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct dentry *dentry;
+       struct inode *inode;
+       char name[IFNAMSIZ + sizeof "_mcg"];
+
+       snprintf(name, sizeof name, "%s_mcg", dev->name);
+
+       dentry = d_alloc_name(ipoib_root, name);
+       if (!dentry)
+               return -ENOMEM;
+
+       inode = ipoib_get_inode();
+       if (!inode) {
+               dput(dentry);
+               return -ENOMEM;
+       }
+
+       inode->u.generic_ip = dev;
+       priv->mcg_dentry = dentry;
+
+       d_add(dentry, inode);
+
+       return 0;
+}
+
+int ipoib_create_debug_file(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       down(&ipoib_fs_mutex);
+
+       list_add_tail(&priv->fs_list, &ipoib_device_list);
+
+       if (!ipoib_sb) {
+               up(&ipoib_fs_mutex);
+               return 0;
+       }
+
+       up(&ipoib_fs_mutex);
+
+       return __ipoib_create_debug_file(dev);
+}
+
+void ipoib_delete_debug_file(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       down(&ipoib_fs_mutex);
+       list_del(&priv->fs_list);
+       if (!ipoib_sb) {
+               up(&ipoib_fs_mutex);
+               return;
+       }
+       up(&ipoib_fs_mutex);
+
+       if (priv->mcg_dentry) {
+               d_drop(priv->mcg_dentry);
+               simple_unlink(ipoib_root->d_inode, priv->mcg_dentry);
+       }
+}
+
+static int ipoib_fill_super(struct super_block *sb, void *data, int silent)
+{
+       static struct tree_descr ipoib_files[] = {
+               { "" }
+       };
+       struct ipoib_dev_priv *priv;
+       int ret;
+
+       ret = simple_fill_super(sb, IPOIB_MAGIC, ipoib_files);
+       if (ret)
+               return ret;
+
+       ipoib_root = sb->s_root;
+
+       down(&ipoib_fs_mutex);
+
+       ipoib_sb = sb;
+
+       list_for_each_entry(priv, &ipoib_device_list, fs_list) {
+               ret = __ipoib_create_debug_file(priv->dev);
+               if (ret)
+                       break;
+       }
+
+       up(&ipoib_fs_mutex);
+
+       return ret;
+}
+
+static struct super_block *ipoib_get_sb(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *data)
+{
+       return get_sb_single(fs_type, flags, data, ipoib_fill_super);
+}
+
+static void ipoib_kill_sb(struct super_block *sb)
+{
+       down(&ipoib_fs_mutex);
+       ipoib_sb = NULL;
+       up(&ipoib_fs_mutex);
+
+       kill_litter_super(sb);
+}
+
+static struct file_system_type ipoib_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "ipoib_debugfs",
+       .get_sb         = ipoib_get_sb,
+       .kill_sb        = ipoib_kill_sb,
+};
+
+int ipoib_register_debugfs(void)
+{
+       return register_filesystem(&ipoib_fs_type);
+}
+
+void ipoib_unregister_debugfs(void)
+{
+       unregister_filesystem(&ipoib_fs_type);
+}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
new file mode 100644 (file)
index 0000000..f89ac46
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ipoib_ib.c 1386 2004-12-27 16:23:17Z roland $
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+
+#include <ib_cache.h>
+
+#include "ipoib.h"
+
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG_DATA
+int data_debug_level;
+
+module_param(data_debug_level, int, 0644);
+MODULE_PARM_DESC(data_debug_level,
+                "Enable data path debug tracing if > 0");
+#endif
+
+#define        IPOIB_OP_RECV   (1ul << 31)
+
+static DECLARE_MUTEX(pkey_sem);
+
+struct ipoib_ah *ipoib_create_ah(struct net_device *dev,
+                                struct ib_pd *pd, struct ib_ah_attr *attr)
+{
+       struct ipoib_ah *ah;
+
+       ah = kmalloc(sizeof *ah, GFP_KERNEL);
+       if (!ah)
+               return NULL;
+
+       ah->dev       = dev;
+       ah->last_send = 0;
+       kref_init(&ah->ref);
+
+       ah->ah = ib_create_ah(pd, attr);
+       if (IS_ERR(ah->ah)) {
+               kfree(ah);
+               ah = NULL;
+       } else
+               ipoib_dbg(netdev_priv(dev), "Created ah %p\n", ah->ah);
+
+       return ah;
+}
+
+void ipoib_free_ah(struct kref *kref)
+{
+       struct ipoib_ah *ah = container_of(kref, struct ipoib_ah, ref);
+       struct ipoib_dev_priv *priv = netdev_priv(ah->dev);
+
+       unsigned long flags;
+
+       if (ah->last_send <= priv->tx_tail) {
+               ipoib_dbg(priv, "Freeing ah %p\n", ah->ah);
+               ib_destroy_ah(ah->ah);
+               kfree(ah);
+       } else {
+               spin_lock_irqsave(&priv->lock, flags);
+               list_add_tail(&ah->list, &priv->dead_ahs);
+               spin_unlock_irqrestore(&priv->lock, flags);
+       }
+}
+
+static inline int ipoib_ib_receive(struct ipoib_dev_priv *priv,
+                                  unsigned int wr_id,
+                                  dma_addr_t addr)
+{
+       struct ib_sge list = {
+               .addr    = addr,
+               .length  = IPOIB_BUF_SIZE,
+               .lkey    = priv->mr->lkey,
+       };
+       struct ib_recv_wr param = {
+               .wr_id      = wr_id | IPOIB_OP_RECV,
+               .sg_list    = &list,
+               .num_sge    = 1,
+               .recv_flags = IB_RECV_SIGNALED
+       };
+       struct ib_recv_wr *bad_wr;
+
+       return ib_post_recv(priv->qp, &param, &bad_wr);
+}
+
+static int ipoib_ib_post_receive(struct net_device *dev, int id)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct sk_buff *skb;
+       dma_addr_t addr;
+       int ret;
+
+       skb = dev_alloc_skb(IPOIB_BUF_SIZE + 4);
+       if (!skb) {
+               ipoib_warn(priv, "failed to allocate receive buffer\n");
+
+               priv->rx_ring[id].skb = NULL;
+               return -ENOMEM;
+       }
+       skb_reserve(skb, 4);    /* 16 byte align IP header */
+       priv->rx_ring[id].skb = skb;
+       addr = dma_map_single(priv->ca->dma_device,
+                             skb->data, IPOIB_BUF_SIZE,
+                             DMA_FROM_DEVICE);
+       pci_unmap_addr_set(&priv->rx_ring[id], mapping, addr);
+
+       ret = ipoib_ib_receive(priv, id, addr);
+       if (ret) {
+               ipoib_warn(priv, "ipoib_ib_receive failed for buf %d (%d)\n",
+                          id, ret);
+               priv->rx_ring[id].skb = NULL;
+       }
+
+       return ret;
+}
+
+static int ipoib_ib_post_receives(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       int i;
+
+       for (i = 0; i < IPOIB_RX_RING_SIZE; ++i) {
+               if (ipoib_ib_post_receive(dev, i)) {
+                       ipoib_warn(priv, "ipoib_ib_post_receive failed for buf %d\n", i);
+                       return -EIO;
+               }
+       }
+
+       return 0;
+}
+
+static void ipoib_ib_handle_wc(struct net_device *dev,
+                              struct ib_wc *wc)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       unsigned int wr_id = wc->wr_id;
+
+       ipoib_dbg_data(priv, "called: id %d, op %d, status: %d\n",
+                      wr_id, wc->opcode, wc->status);
+
+       if (wr_id & IPOIB_OP_RECV) {
+               wr_id &= ~IPOIB_OP_RECV;
+
+               if (wr_id < IPOIB_RX_RING_SIZE) {
+                       struct sk_buff *skb = priv->rx_ring[wr_id].skb;
+
+                       priv->rx_ring[wr_id].skb = NULL;
+
+                       dma_unmap_single(priv->ca->dma_device,
+                                        pci_unmap_addr(&priv->rx_ring[wr_id],
+                                                       mapping),
+                                        IPOIB_BUF_SIZE,
+                                        DMA_FROM_DEVICE);
+
+                       if (wc->status != IB_WC_SUCCESS) {
+                               if (wc->status != IB_WC_WR_FLUSH_ERR)
+                                       ipoib_warn(priv, "failed recv event "
+                                                  "(status=%d, wrid=%d vend_err %x)\n",
+                                                  wc->status, wr_id, wc->vendor_err);
+                               dev_kfree_skb_any(skb);
+                               return;
+                       }
+
+                       ipoib_dbg_data(priv, "received %d bytes, SLID 0x%04x\n",
+                                      wc->byte_len, wc->slid);
+
+                       skb_put(skb, wc->byte_len);
+                       skb_pull(skb, IB_GRH_BYTES);
+
+                       if (wc->slid != priv->local_lid ||
+                           wc->src_qp != priv->qp->qp_num) {
+                               skb->protocol = ((struct ipoib_header *) skb->data)->proto;
+
+                               skb_pull(skb, IPOIB_ENCAP_LEN);
+
+                               dev->last_rx = jiffies;
+                               ++priv->stats.rx_packets;
+                               priv->stats.rx_bytes += skb->len;
+
+                               skb->dev = dev;
+                               /* XXX get correct PACKET_ type here */
+                               skb->pkt_type = PACKET_HOST;
+                               netif_rx_ni(skb);
+                       } else {
+                               ipoib_dbg_data(priv, "dropping loopback packet\n");
+                               dev_kfree_skb_any(skb);
+                       }
+
+                       /* repost receive */
+                       if (ipoib_ib_post_receive(dev, wr_id))
+                               ipoib_warn(priv, "ipoib_ib_post_receive failed "
+                                          "for buf %d\n", wr_id);
+               } else
+                       ipoib_warn(priv, "completion event with wrid %d\n",
+                                  wr_id);
+
+       } else {
+               struct ipoib_buf *tx_req;
+               unsigned long flags;
+
+               if (wr_id >= IPOIB_TX_RING_SIZE) {
+                       ipoib_warn(priv, "completion event with wrid %d (> %d)\n",
+                                  wr_id, IPOIB_TX_RING_SIZE);
+                       return;
+               }
+
+               ipoib_dbg_data(priv, "send complete, wrid %d\n", wr_id);
+
+               tx_req = &priv->tx_ring[wr_id];
+
+               dma_unmap_single(priv->ca->dma_device,
+                                pci_unmap_addr(tx_req, mapping),
+                                tx_req->skb->len,
+                                DMA_TO_DEVICE);
+
+               ++priv->stats.tx_packets;
+               priv->stats.tx_bytes += tx_req->skb->len;
+
+               dev_kfree_skb_any(tx_req->skb);
+
+               spin_lock_irqsave(&priv->tx_lock, flags);
+               ++priv->tx_tail;
+               if (netif_queue_stopped(dev) &&
+                   priv->tx_head - priv->tx_tail <= IPOIB_TX_RING_SIZE / 2)
+                       netif_wake_queue(dev);
+               spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+               if (wc->status != IB_WC_SUCCESS &&
+                   wc->status != IB_WC_WR_FLUSH_ERR)
+                       ipoib_warn(priv, "failed send event "
+                                  "(status=%d, wrid=%d vend_err %x)\n",
+                                  wc->status, wr_id, wc->vendor_err);
+       }
+}
+
+void ipoib_ib_completion(struct ib_cq *cq, void *dev_ptr)
+{
+       struct net_device *dev = (struct net_device *) dev_ptr;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       int n, i;
+
+       ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
+       do {
+               n = ib_poll_cq(cq, IPOIB_NUM_WC, priv->ibwc);
+               for (i = 0; i < n; ++i)
+                       ipoib_ib_handle_wc(dev, priv->ibwc + i);
+       } while (n == IPOIB_NUM_WC);
+}
+
+static inline int post_send(struct ipoib_dev_priv *priv,
+                           unsigned int wr_id,
+                           struct ib_ah *address, u32 qpn,
+                           dma_addr_t addr, int len)
+{
+       struct ib_send_wr *bad_wr;
+
+       priv->tx_sge.addr             = addr;
+       priv->tx_sge.length           = len;
+
+       priv->tx_wr.wr_id             = wr_id;
+       priv->tx_wr.wr.ud.remote_qpn  = qpn;
+       priv->tx_wr.wr.ud.ah          = address;
+
+       return ib_post_send(priv->qp, &priv->tx_wr, &bad_wr);
+}
+
+void ipoib_send(struct net_device *dev, struct sk_buff *skb,
+               struct ipoib_ah *address, u32 qpn)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_buf *tx_req;
+       dma_addr_t addr;
+
+       if (skb->len > dev->mtu + INFINIBAND_ALEN) {
+               ipoib_warn(priv, "packet len %d (> %d) too long to send, dropping\n",
+                          skb->len, dev->mtu + INFINIBAND_ALEN);
+               ++priv->stats.tx_dropped;
+               ++priv->stats.tx_errors;
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       ipoib_dbg_data(priv, "sending packet, length=%d address=%p qpn=0x%06x\n",
+                      skb->len, address, qpn);
+
+       /*
+        * We put the skb into the tx_ring _before_ we call post_send()
+        * because it's entirely possible that the completion handler will
+        * run before we execute anything after the post_send().  That
+        * means we have to make sure everything is properly recorded and
+        * our state is consistent before we call post_send().
+        */
+       tx_req = &priv->tx_ring[priv->tx_head & (IPOIB_TX_RING_SIZE - 1)];
+       tx_req->skb = skb;
+       addr = dma_map_single(priv->ca->dma_device, skb->data, skb->len,
+                             DMA_TO_DEVICE);
+       pci_unmap_addr_set(tx_req, mapping, addr);
+
+       if (unlikely(post_send(priv, priv->tx_head & (IPOIB_TX_RING_SIZE - 1),
+                              address->ah, qpn, addr, skb->len))) {
+               ipoib_warn(priv, "post_send failed\n");
+               ++priv->stats.tx_errors;
+               dma_unmap_single(priv->ca->dma_device, addr, skb->len,
+                                DMA_TO_DEVICE);
+               dev_kfree_skb_any(skb);
+       } else {
+               dev->trans_start = jiffies;
+
+               address->last_send = priv->tx_head;
+               ++priv->tx_head;
+
+               if (priv->tx_head - priv->tx_tail == IPOIB_TX_RING_SIZE) {
+                       ipoib_dbg(priv, "TX ring full, stopping kernel net queue\n");
+                       netif_stop_queue(dev);
+               }
+       }
+}
+
+static void __ipoib_reap_ah(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_ah *ah, *tah;
+       LIST_HEAD(remove_list);
+
+       spin_lock_irq(&priv->lock);
+       list_for_each_entry_safe(ah, tah, &priv->dead_ahs, list)
+               if (ah->last_send <= priv->tx_tail) {
+                       list_del(&ah->list);
+                       list_add_tail(&ah->list, &remove_list);
+               }
+       spin_unlock_irq(&priv->lock);
+
+       list_for_each_entry_safe(ah, tah, &remove_list, list) {
+               ipoib_dbg(priv, "Reaping ah %p\n", ah->ah);
+               ib_destroy_ah(ah->ah);
+               kfree(ah);
+       }
+}
+
+void ipoib_reap_ah(void *dev_ptr)
+{
+       struct net_device *dev = dev_ptr;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       __ipoib_reap_ah(dev);
+
+       if (!test_bit(IPOIB_STOP_REAPER, &priv->flags))
+               queue_delayed_work(ipoib_workqueue, &priv->ah_reap_task, HZ);
+}
+
+int ipoib_ib_dev_open(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       int ret;
+
+       ret = ipoib_qp_create(dev);
+       if (ret) {
+               ipoib_warn(priv, "ipoib_qp_create returned %d\n", ret);
+               return -1;
+       }
+
+       ret = ipoib_ib_post_receives(dev);
+       if (ret) {
+               ipoib_warn(priv, "ipoib_ib_post_receives returned %d\n", ret);
+               return -1;
+       }
+
+       clear_bit(IPOIB_STOP_REAPER, &priv->flags);
+       queue_delayed_work(ipoib_workqueue, &priv->ah_reap_task, HZ);
+
+       return 0;
+}
+
+int ipoib_ib_dev_up(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       set_bit(IPOIB_FLAG_OPER_UP, &priv->flags);
+
+       return ipoib_mcast_start_thread(dev);
+}
+
+int ipoib_ib_dev_down(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       ipoib_dbg(priv, "downing ib_dev\n");
+
+       clear_bit(IPOIB_FLAG_OPER_UP, &priv->flags);
+       netif_carrier_off(dev);
+
+       /* Shutdown the P_Key thread if still active */
+       if (!test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags)) {
+               down(&pkey_sem);
+               set_bit(IPOIB_PKEY_STOP, &priv->flags);
+               cancel_delayed_work(&priv->pkey_task);
+               up(&pkey_sem);
+               flush_workqueue(ipoib_workqueue);
+       }
+
+       ipoib_mcast_stop_thread(dev);
+
+       /*
+        * Flush the multicast groups first so we stop any multicast joins. The
+        * completion thread may have already died and we may deadlock waiting
+        * for the completion thread to finish some multicast joins.
+        */
+       ipoib_mcast_dev_flush(dev);
+
+       /* Delete broadcast and local addresses since they will be recreated */
+       ipoib_mcast_dev_down(dev);
+
+       ipoib_flush_paths(dev);
+
+       return 0;
+}
+
+static int recvs_pending(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       int pending = 0;
+       int i;
+
+       for (i = 0; i < IPOIB_RX_RING_SIZE; ++i)
+               if (priv->rx_ring[i].skb)
+                       ++pending;
+
+       return pending;
+}
+
+int ipoib_ib_dev_stop(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ib_qp_attr qp_attr;
+       int attr_mask;
+       unsigned long begin;
+       struct ipoib_buf *tx_req;
+       int i;
+
+       /* Kill the existing QP and allocate a new one */
+       qp_attr.qp_state = IB_QPS_ERR;
+       attr_mask        = IB_QP_STATE;
+       if (ib_modify_qp(priv->qp, &qp_attr, attr_mask))
+               ipoib_warn(priv, "Failed to modify QP to ERROR state\n");
+
+       /* Wait for all sends and receives to complete */
+       begin = jiffies;
+
+       while (priv->tx_head != priv->tx_tail || recvs_pending(dev)) {
+               if (time_after(jiffies, begin + 5 * HZ)) {
+                       ipoib_warn(priv, "timing out; %d sends %d receives not completed\n",
+                                  priv->tx_head - priv->tx_tail, recvs_pending(dev));
+
+                       /*
+                        * assume the HW is wedged and just free up
+                        * all our pending work requests.
+                        */
+                       while (priv->tx_tail < priv->tx_head) {
+                               tx_req = &priv->tx_ring[priv->tx_tail &
+                                                       (IPOIB_TX_RING_SIZE - 1)];
+                               dma_unmap_single(priv->ca->dma_device,
+                                                pci_unmap_addr(tx_req, mapping),
+                                                tx_req->skb->len,
+                                                DMA_TO_DEVICE);
+                               dev_kfree_skb_any(tx_req->skb);
+                               ++priv->tx_tail;
+                       }
+
+                       for (i = 0; i < IPOIB_RX_RING_SIZE; ++i)
+                               if (priv->rx_ring[i].skb) {
+                                       dma_unmap_single(priv->ca->dma_device,
+                                                        pci_unmap_addr(&priv->rx_ring[i],
+                                                                       mapping),
+                                                        IPOIB_BUF_SIZE,
+                                                        DMA_FROM_DEVICE);
+                                       dev_kfree_skb_any(priv->rx_ring[i].skb);
+                                       priv->rx_ring[i].skb = NULL;
+                               }
+
+                       goto timeout;
+               }
+
+               msleep(1);
+       }
+
+       ipoib_dbg(priv, "All sends and receives done.\n");
+
+timeout:
+       qp_attr.qp_state = IB_QPS_RESET;
+       attr_mask        = IB_QP_STATE;
+       if (ib_modify_qp(priv->qp, &qp_attr, attr_mask))
+               ipoib_warn(priv, "Failed to modify QP to RESET state\n");
+
+       /* Wait for all AHs to be reaped */
+       set_bit(IPOIB_STOP_REAPER, &priv->flags);
+       cancel_delayed_work(&priv->ah_reap_task);
+       flush_workqueue(ipoib_workqueue);
+
+       begin = jiffies;
+
+       while (!list_empty(&priv->dead_ahs)) {
+               __ipoib_reap_ah(dev);
+
+               if (time_after(jiffies, begin + HZ)) {
+                       ipoib_warn(priv, "timing out; will leak address handles\n");
+                       break;
+               }
+
+               msleep(1);
+       }
+
+       return 0;
+}
+
+int ipoib_ib_dev_init(struct net_device *dev, struct ib_device *ca, int port)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       priv->ca = ca;
+       priv->port = port;
+       priv->qp = NULL;
+
+       if (ipoib_transport_dev_init(dev, ca)) {
+               printk(KERN_WARNING "%s: ipoib_transport_dev_init failed\n", ca->name);
+               return -ENODEV;
+       }
+
+       if (dev->flags & IFF_UP) {
+               if (ipoib_ib_dev_open(dev)) {
+                       ipoib_transport_dev_cleanup(dev);
+                       return -ENODEV;
+               }
+       }
+
+       return 0;
+}
+
+void ipoib_ib_dev_flush(void *_dev)
+{
+       struct net_device *dev = (struct net_device *)_dev;
+       struct ipoib_dev_priv *priv = netdev_priv(dev), *cpriv;
+
+       if (!test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags))
+               return;
+
+       ipoib_dbg(priv, "flushing\n");
+
+       ipoib_ib_dev_down(dev);
+
+       /*
+        * The device could have been brought down between the start and when
+        * we get here, don't bring it back up if it's not configured up
+        */
+       if (test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags))
+               ipoib_ib_dev_up(dev);
+
+       /* Flush any child interfaces too */
+       list_for_each_entry(cpriv, &priv->child_intfs, list)
+               ipoib_ib_dev_flush(&cpriv->dev);
+}
+
+void ipoib_ib_dev_cleanup(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       ipoib_dbg(priv, "cleaning up ib_dev\n");
+
+       ipoib_mcast_stop_thread(dev);
+
+       /* Delete the broadcast address and the local address */
+       ipoib_mcast_dev_down(dev);
+
+       ipoib_transport_dev_cleanup(dev);
+}
+
+/*
+ * Delayed P_Key Assigment Interim Support
+ *
+ * The following is initial implementation of delayed P_Key assigment
+ * mechanism. It is using the same approach implemented for the multicast
+ * group join. The single goal of this implementation is to quickly address
+ * Bug #2507. This implementation will probably be removed when the P_Key
+ * change async notification is available.
+ */
+int ipoib_open(struct net_device *dev);
+
+static void ipoib_pkey_dev_check_presence(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       u16 pkey_index = 0;
+
+       if (ib_find_cached_pkey(priv->ca, priv->port, priv->pkey, &pkey_index))
+               clear_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
+       else
+               set_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
+}
+
+void ipoib_pkey_poll(void *dev_ptr)
+{
+       struct net_device *dev = dev_ptr;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       ipoib_pkey_dev_check_presence(dev);
+
+       if (test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags))
+               ipoib_open(dev);
+       else {
+               down(&pkey_sem);
+               if (!test_bit(IPOIB_PKEY_STOP, &priv->flags))
+                       queue_delayed_work(ipoib_workqueue,
+                                          &priv->pkey_task,
+                                          HZ);
+               up(&pkey_sem);
+       }
+}
+
+int ipoib_pkey_dev_delay_open(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       /* Look for the interface pkey value in the IB Port P_Key table and */
+       /* set the interface pkey assigment flag                            */
+       ipoib_pkey_dev_check_presence(dev);
+
+       /* P_Key value not assigned yet - start polling */
+       if (!test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags)) {
+               down(&pkey_sem);
+               clear_bit(IPOIB_PKEY_STOP, &priv->flags);
+               queue_delayed_work(ipoib_workqueue,
+                                  &priv->pkey_task,
+                                  HZ);
+               up(&pkey_sem);
+               return 1;
+       }
+
+       return 0;
+}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
new file mode 100644 (file)
index 0000000..63c8168
--- /dev/null
@@ -0,0 +1,1079 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ipoib_main.c 1377 2004-12-23 19:57:12Z roland $
+ */
+
+#include "ipoib.h"
+
+#include <linux/version.h>
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <linux/if_arp.h>      /* For ARPHRD_xxx */
+
+#include <linux/ip.h>
+#include <linux/in.h>
+
+MODULE_AUTHOR("Roland Dreier");
+MODULE_DESCRIPTION("IP-over-InfiniBand net driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
+int debug_level;
+
+module_param(debug_level, int, 0644);
+MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0");
+#endif
+
+static const u8 ipv4_bcast_addr[] = {
+       0x00, 0xff, 0xff, 0xff,
+       0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
+};
+
+struct workqueue_struct *ipoib_workqueue;
+
+static void ipoib_add_one(struct ib_device *device);
+static void ipoib_remove_one(struct ib_device *device);
+
+static struct ib_client ipoib_client = {
+       .name   = "ipoib",
+       .add    = ipoib_add_one,
+       .remove = ipoib_remove_one
+};
+
+int ipoib_open(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       ipoib_dbg(priv, "bringing up interface\n");
+
+       set_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags);
+
+       if (ipoib_pkey_dev_delay_open(dev))
+               return 0;
+
+       if (ipoib_ib_dev_open(dev))
+               return -EINVAL;
+
+       if (ipoib_ib_dev_up(dev))
+               return -EINVAL;
+
+       if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) {
+               struct ipoib_dev_priv *cpriv;
+
+               /* Bring up any child interfaces too */
+               down(&priv->vlan_mutex);
+               list_for_each_entry(cpriv, &priv->child_intfs, list) {
+                       int flags;
+
+                       flags = cpriv->dev->flags;
+                       if (flags & IFF_UP)
+                               continue;
+
+                       dev_change_flags(cpriv->dev, flags | IFF_UP);
+               }
+               up(&priv->vlan_mutex);
+       }
+
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+static int ipoib_stop(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       ipoib_dbg(priv, "stopping interface\n");
+
+       clear_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags);
+
+       netif_stop_queue(dev);
+
+       ipoib_ib_dev_down(dev);
+       ipoib_ib_dev_stop(dev);
+
+       if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) {
+               struct ipoib_dev_priv *cpriv;
+
+               /* Bring down any child interfaces too */
+               down(&priv->vlan_mutex);
+               list_for_each_entry(cpriv, &priv->child_intfs, list) {
+                       int flags;
+
+                       flags = cpriv->dev->flags;
+                       if (!(flags & IFF_UP))
+                               continue;
+
+                       dev_change_flags(cpriv->dev, flags & ~IFF_UP);
+               }
+               up(&priv->vlan_mutex);
+       }
+
+       return 0;
+}
+
+static int ipoib_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       if (new_mtu > IPOIB_PACKET_SIZE - IPOIB_ENCAP_LEN)
+               return -EINVAL;
+
+       priv->admin_mtu = new_mtu;
+
+       dev->mtu = min(priv->mcast_mtu, priv->admin_mtu);
+
+       return 0;
+}
+
+static struct ipoib_path *__path_find(struct net_device *dev,
+                                     union ib_gid *gid)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct rb_node *n = priv->path_tree.rb_node;
+       struct ipoib_path *path;
+       int ret;
+
+       while (n) {
+               path = rb_entry(n, struct ipoib_path, rb_node);
+
+               ret = memcmp(gid->raw, path->pathrec.dgid.raw,
+                            sizeof (union ib_gid));
+
+               if (ret < 0)
+                       n = n->rb_left;
+               else if (ret > 0)
+                       n = n->rb_right;
+               else
+                       return path;
+       }
+
+       return NULL;
+}
+
+static int __path_add(struct net_device *dev, struct ipoib_path *path)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct rb_node **n = &priv->path_tree.rb_node;
+       struct rb_node *pn = NULL;
+       struct ipoib_path *tpath;
+       int ret;
+
+       while (*n) {
+               pn = *n;
+               tpath = rb_entry(pn, struct ipoib_path, rb_node);
+
+               ret = memcmp(path->pathrec.dgid.raw, tpath->pathrec.dgid.raw,
+                            sizeof (union ib_gid));
+               if (ret < 0)
+                       n = &pn->rb_left;
+               else if (ret > 0)
+                       n = &pn->rb_right;
+               else
+                       return -EEXIST;
+       }
+
+       rb_link_node(&path->rb_node, pn, n);
+       rb_insert_color(&path->rb_node, &priv->path_tree);
+
+       list_add_tail(&path->list, &priv->path_list);
+
+       return 0;
+}
+
+static void __path_free(struct net_device *dev, struct ipoib_path *path)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_neigh *neigh, *tn;
+       struct sk_buff *skb;
+
+       while ((skb = __skb_dequeue(&path->queue)))
+               dev_kfree_skb_irq(skb);
+
+       list_for_each_entry_safe(neigh, tn, &path->neigh_list, list) {
+               if (neigh->ah)
+                       ipoib_put_ah(neigh->ah);
+               *to_ipoib_neigh(neigh->neighbour) = NULL;
+               neigh->neighbour->ops->destructor = NULL;
+               kfree(neigh);
+       }
+
+       if (path->ah)
+               ipoib_put_ah(path->ah);
+
+       rb_erase(&path->rb_node, &priv->path_tree);
+       list_del(&path->list);
+       kfree(path);
+}
+
+void ipoib_flush_paths(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_path *path, *tp;
+       LIST_HEAD(remove_list);
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       list_splice(&priv->path_list, &remove_list);
+       INIT_LIST_HEAD(&priv->path_list);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       list_for_each_entry_safe(path, tp, &remove_list, list) {
+               if (path->query)
+                       ib_sa_cancel_query(path->query_id, path->query);
+               wait_for_completion(&path->done);
+               __path_free(dev, path);
+       }
+}
+
+static void path_rec_completion(int status,
+                               struct ib_sa_path_rec *pathrec,
+                               void *path_ptr)
+{
+       struct ipoib_path *path = path_ptr;
+       struct net_device *dev = path->dev;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_ah *ah = NULL;
+       struct ipoib_neigh *neigh;
+       struct sk_buff_head skqueue;
+       struct sk_buff *skb;
+       unsigned long flags;
+
+       if (pathrec)
+               ipoib_dbg(priv, "PathRec LID 0x%04x for GID " IPOIB_GID_FMT "\n",
+                         be16_to_cpu(pathrec->dlid), IPOIB_GID_ARG(pathrec->dgid));
+       else
+               ipoib_dbg(priv, "PathRec status %d for GID " IPOIB_GID_FMT "\n",
+                         status, IPOIB_GID_ARG(path->pathrec.dgid));
+
+       skb_queue_head_init(&skqueue);
+
+       if (!status) {
+               struct ib_ah_attr av = {
+                       .dlid          = be16_to_cpu(pathrec->dlid),
+                       .sl            = pathrec->sl,
+                       .port_num      = priv->port
+               };
+
+               if (ib_sa_rate_enum_to_int(pathrec->rate) > 0)
+                       av.static_rate = (2 * priv->local_rate -
+                                         ib_sa_rate_enum_to_int(pathrec->rate) - 1) /
+                               (priv->local_rate ? priv->local_rate : 1);
+
+               ipoib_dbg(priv, "static_rate %d for local port %dX, path %dX\n",
+                         av.static_rate, priv->local_rate,
+                         ib_sa_rate_enum_to_int(pathrec->rate));
+
+               ah = ipoib_create_ah(dev, priv->pd, &av);
+       }
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       path->ah = ah;
+
+       if (ah) {
+               path->pathrec = *pathrec;
+
+               ipoib_dbg(priv, "created address handle %p for LID 0x%04x, SL %d\n",
+                         ah, be16_to_cpu(pathrec->dlid), pathrec->sl);
+
+               while ((skb = __skb_dequeue(&path->queue)))
+                       __skb_queue_tail(&skqueue, skb);
+
+               list_for_each_entry(neigh, &path->neigh_list, list) {
+                       kref_get(&path->ah->ref);
+                       neigh->ah = path->ah;
+
+                       while ((skb = __skb_dequeue(&neigh->queue)))
+                               __skb_queue_tail(&skqueue, skb);
+               }
+       } else
+               path->query = NULL;
+
+       complete(&path->done);
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       while ((skb = __skb_dequeue(&skqueue))) {
+               skb->dev = dev;
+               if (dev_queue_xmit(skb))
+                       ipoib_warn(priv, "dev_queue_xmit failed "
+                                  "to requeue packet\n");
+       }
+}
+
+static struct ipoib_path *path_rec_create(struct net_device *dev,
+                                         union ib_gid *gid)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_path *path;
+
+       path = kmalloc(sizeof *path, GFP_ATOMIC);
+       if (!path)
+               return NULL;
+
+       path->dev = dev;
+       path->pathrec.dlid = 0;
+
+       skb_queue_head_init(&path->queue);
+
+       INIT_LIST_HEAD(&path->neigh_list);
+       path->query = NULL;
+       init_completion(&path->done);
+
+       memcpy(path->pathrec.dgid.raw, gid->raw, sizeof (union ib_gid));
+       path->pathrec.sgid      = priv->local_gid;
+       path->pathrec.pkey      = cpu_to_be16(priv->pkey);
+       path->pathrec.numb_path = 1;
+
+       __path_add(dev, path);
+
+       return path;
+}
+
+static int path_rec_start(struct net_device *dev,
+                         struct ipoib_path *path)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       ipoib_dbg(priv, "Start path record lookup for " IPOIB_GID_FMT "\n",
+                 IPOIB_GID_ARG(path->pathrec.dgid));
+
+       path->query_id =
+               ib_sa_path_rec_get(priv->ca, priv->port,
+                                  &path->pathrec,
+                                  IB_SA_PATH_REC_DGID          |
+                                  IB_SA_PATH_REC_SGID          |
+                                  IB_SA_PATH_REC_NUMB_PATH     |
+                                  IB_SA_PATH_REC_PKEY,
+                                  1000, GFP_ATOMIC,
+                                  path_rec_completion,
+                                  path, &path->query);
+       if (path->query_id < 0) {
+               ipoib_warn(priv, "ib_sa_path_rec_get failed\n");
+               path->query = NULL;
+               return path->query_id;
+       }
+
+       return 0;
+}
+
+static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_path *path;
+       struct ipoib_neigh *neigh;
+
+       neigh = kmalloc(sizeof *neigh, GFP_ATOMIC);
+       if (!neigh) {
+               ++priv->stats.tx_dropped;
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       skb_queue_head_init(&neigh->queue);
+       neigh->neighbour = skb->dst->neighbour;
+       *to_ipoib_neigh(skb->dst->neighbour) = neigh;
+
+       /*
+        * We can only be called from ipoib_start_xmit, so we're
+        * inside tx_lock -- no need to save/restore flags.
+        */
+       spin_lock(&priv->lock);
+
+       path = __path_find(dev, (union ib_gid *) (skb->dst->neighbour->ha + 4));
+       if (!path) {
+               path = path_rec_create(dev,
+                                      (union ib_gid *) (skb->dst->neighbour->ha + 4));
+               if (!path)
+                       goto err;
+       }
+
+       list_add_tail(&neigh->list, &path->neigh_list);
+
+       if (path->pathrec.dlid) {
+               kref_get(&path->ah->ref);
+               neigh->ah = path->ah;
+
+               ipoib_send(dev, skb, path->ah,
+                          be32_to_cpup((__be32 *) skb->dst->neighbour->ha));
+       } else {
+               neigh->ah  = NULL;
+               if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+                       __skb_queue_tail(&neigh->queue, skb);
+               } else {
+                       ++priv->stats.tx_dropped;
+                       dev_kfree_skb_any(skb);
+               }
+
+               if (!path->query && path_rec_start(dev, path))
+                       goto err;
+       }
+
+       spin_unlock(&priv->lock);
+       return;
+
+err:
+       *to_ipoib_neigh(skb->dst->neighbour) = NULL;
+       list_del(&neigh->list);
+       kfree(neigh);
+       neigh->neighbour->ops->destructor = NULL;
+
+       ++priv->stats.tx_dropped;
+       dev_kfree_skb_any(skb);
+
+       spin_unlock(&priv->lock);
+}
+
+static void path_lookup(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(skb->dev);
+
+       /* Look up path record for unicasts */
+       if (skb->dst->neighbour->ha[4] != 0xff) {
+               neigh_add_path(skb, dev);
+               return;
+       }
+
+       /* Add in the P_Key for multicasts */
+       skb->dst->neighbour->ha[8] = (priv->pkey >> 8) & 0xff;
+       skb->dst->neighbour->ha[9] = priv->pkey & 0xff;
+       ipoib_mcast_send(dev, (union ib_gid *) (skb->dst->neighbour->ha + 4), skb);
+}
+
+static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
+                            struct ipoib_pseudoheader *phdr)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_path *path;
+
+       /*
+        * We can only be called from ipoib_start_xmit, so we're
+        * inside tx_lock -- no need to save/restore flags.
+        */
+       spin_lock(&priv->lock);
+
+       path = __path_find(dev, (union ib_gid *) (phdr->hwaddr + 4));
+       if (!path) {
+               path = path_rec_create(dev,
+                                      (union ib_gid *) (phdr->hwaddr + 4));
+               if (path) {
+                       /* put pseudoheader back on for next time */
+                       skb_push(skb, sizeof *phdr);
+                       __skb_queue_tail(&path->queue, skb);
+
+                       if (path_rec_start(dev, path))
+                               __path_free(dev, path);
+               } else {
+                       ++priv->stats.tx_dropped;
+                       dev_kfree_skb_any(skb);
+               }
+
+               spin_unlock(&priv->lock);
+               return;
+       }
+
+       if (path->pathrec.dlid) {
+               ipoib_dbg(priv, "Send unicast ARP to %04x\n",
+                         be16_to_cpu(path->pathrec.dlid));
+
+               ipoib_send(dev, skb, path->ah,
+                          be32_to_cpup((__be32 *) phdr->hwaddr));
+       } else if ((path->query || !path_rec_start(dev, path)) &&
+                  skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+               /* put pseudoheader back on for next time */
+               skb_push(skb, sizeof *phdr);
+               __skb_queue_tail(&path->queue, skb);
+       } else {
+               ++priv->stats.tx_dropped;
+               dev_kfree_skb_any(skb);
+       }
+
+       spin_unlock(&priv->lock);
+}
+
+static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_neigh *neigh;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       if (!spin_trylock(&priv->tx_lock)) {
+               local_irq_restore(flags);
+               return NETDEV_TX_LOCKED;
+       }
+
+       /*
+        * Check if our queue is stopped.  Since we have the LLTX bit
+        * set, we can't rely on netif_stop_queue() preventing our
+        * xmit function from being called with a full queue.
+        */
+       if (unlikely(netif_queue_stopped(dev))) {
+               spin_unlock_irqrestore(&priv->tx_lock, flags);
+               return NETDEV_TX_BUSY;
+       }
+
+       if (skb->dst && skb->dst->neighbour) {
+               if (unlikely(!*to_ipoib_neigh(skb->dst->neighbour))) {
+                       path_lookup(skb, dev);
+                       goto out;
+               }
+
+               neigh = *to_ipoib_neigh(skb->dst->neighbour);
+
+               if (likely(neigh->ah)) {
+                       ipoib_send(dev, skb, neigh->ah,
+                                  be32_to_cpup((__be32 *) skb->dst->neighbour->ha));
+                       goto out;
+               }
+
+               if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+                       spin_lock(&priv->lock);
+                       __skb_queue_tail(&neigh->queue, skb);
+                       spin_unlock(&priv->lock);
+               } else {
+                       ++priv->stats.tx_dropped;
+                       dev_kfree_skb_any(skb);
+               }
+       } else {
+               struct ipoib_pseudoheader *phdr =
+                       (struct ipoib_pseudoheader *) skb->data;
+               skb_pull(skb, sizeof *phdr);
+
+               if (phdr->hwaddr[4] == 0xff) {
+                       /* Add in the P_Key for multicast*/
+                       phdr->hwaddr[8] = (priv->pkey >> 8) & 0xff;
+                       phdr->hwaddr[9] = priv->pkey & 0xff;
+
+                       ipoib_mcast_send(dev, (union ib_gid *) (phdr->hwaddr + 4), skb);
+               } else {
+                       /* unicast GID -- should be ARP reply */
+
+                       if (be16_to_cpup((u16 *) skb->data) != ETH_P_ARP) {
+                               ipoib_warn(priv, "Unicast, no %s: type %04x, QPN %06x "
+                                          IPOIB_GID_FMT "\n",
+                                          skb->dst ? "neigh" : "dst",
+                                          be16_to_cpup((u16 *) skb->data),
+                                          be32_to_cpup((u32 *) phdr->hwaddr),
+                                          IPOIB_GID_ARG(*(union ib_gid *) (phdr->hwaddr + 4)));
+                               dev_kfree_skb_any(skb);
+                               ++priv->stats.tx_dropped;
+                               goto out;
+                       }
+
+                       unicast_arp_send(skb, dev, phdr);
+               }
+       }
+
+out:
+       spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+       return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *ipoib_get_stats(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       return &priv->stats;
+}
+
+static void ipoib_timeout(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       ipoib_warn(priv, "transmit timeout: latency %ld\n",
+                  jiffies - dev->trans_start);
+       /* XXX reset QP, etc. */
+}
+
+static int ipoib_hard_header(struct sk_buff *skb,
+                            struct net_device *dev,
+                            unsigned short type,
+                            void *daddr, void *saddr, unsigned len)
+{
+       struct ipoib_header *header;
+
+       header = (struct ipoib_header *) skb_push(skb, sizeof *header);
+
+       header->proto = htons(type);
+       header->reserved = 0;
+
+       /*
+        * If we don't have a neighbour structure, stuff the
+        * destination address onto the front of the skb so we can
+        * figure out where to send the packet later.
+        */
+       if (!skb->dst || !skb->dst->neighbour) {
+               struct ipoib_pseudoheader *phdr =
+                       (struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr);
+               memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN);
+       }
+
+       return 0;
+}
+
+static void ipoib_set_mcast_list(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       schedule_work(&priv->restart_task);
+}
+
+static void ipoib_neigh_destructor(struct neighbour *n)
+{
+       struct ipoib_neigh *neigh = *to_ipoib_neigh(n);
+       struct ipoib_dev_priv *priv = netdev_priv(n->dev);
+       unsigned long flags;
+
+       ipoib_dbg(priv,
+                 "neigh_destructor for %06x " IPOIB_GID_FMT "\n",
+                 be32_to_cpup((__be32 *) n->ha),
+                 IPOIB_GID_ARG(*((union ib_gid *) (n->ha + 4))));
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       if (neigh) {
+               if (neigh->ah)
+                       ipoib_put_ah(neigh->ah);
+               list_del(&neigh->list);
+               *to_ipoib_neigh(n) = NULL;
+               kfree(neigh);
+       }
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int ipoib_neigh_setup(struct neighbour *neigh)
+{
+       /*
+        * Is this kosher?  I can't find anybody in the kernel that
+        * sets neigh->destructor, so we should be able to set it here
+        * without trouble.
+        */
+       neigh->ops->destructor = ipoib_neigh_destructor;
+
+       return 0;
+}
+
+static int ipoib_neigh_setup_dev(struct net_device *dev, struct neigh_parms *parms)
+{
+       parms->neigh_setup = ipoib_neigh_setup;
+
+       return 0;
+}
+
+int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       /* Allocate RX/TX "rings" to hold queued skbs */
+
+       priv->rx_ring = kmalloc(IPOIB_RX_RING_SIZE * sizeof (struct ipoib_buf),
+                               GFP_KERNEL);
+       if (!priv->rx_ring) {
+               printk(KERN_WARNING "%s: failed to allocate RX ring (%d entries)\n",
+                      ca->name, IPOIB_RX_RING_SIZE);
+               goto out;
+       }
+       memset(priv->rx_ring, 0,
+              IPOIB_RX_RING_SIZE * sizeof (struct ipoib_buf));
+
+       priv->tx_ring = kmalloc(IPOIB_TX_RING_SIZE * sizeof (struct ipoib_buf),
+                               GFP_KERNEL);
+       if (!priv->tx_ring) {
+               printk(KERN_WARNING "%s: failed to allocate TX ring (%d entries)\n",
+                      ca->name, IPOIB_TX_RING_SIZE);
+               goto out_rx_ring_cleanup;
+       }
+       memset(priv->tx_ring, 0,
+              IPOIB_TX_RING_SIZE * sizeof (struct ipoib_buf));
+
+       /* priv->tx_head & tx_tail are already 0 */
+
+       if (ipoib_ib_dev_init(dev, ca, port))
+               goto out_tx_ring_cleanup;
+
+       return 0;
+
+out_tx_ring_cleanup:
+       kfree(priv->tx_ring);
+
+out_rx_ring_cleanup:
+       kfree(priv->rx_ring);
+
+out:
+       return -ENOMEM;
+}
+
+void ipoib_dev_cleanup(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev), *cpriv, *tcpriv;
+
+       ipoib_delete_debug_file(dev);
+
+       /* Delete any child interfaces first */
+       list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) {
+               unregister_netdev(cpriv->dev);
+               ipoib_dev_cleanup(cpriv->dev);
+               free_netdev(cpriv->dev);
+       }
+
+       ipoib_ib_dev_cleanup(dev);
+
+       if (priv->rx_ring) {
+               kfree(priv->rx_ring);
+               priv->rx_ring = NULL;
+       }
+
+       if (priv->tx_ring) {
+               kfree(priv->tx_ring);
+               priv->tx_ring = NULL;
+       }
+}
+
+static void ipoib_setup(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       dev->open                = ipoib_open;
+       dev->stop                = ipoib_stop;
+       dev->change_mtu          = ipoib_change_mtu;
+       dev->hard_start_xmit     = ipoib_start_xmit;
+       dev->get_stats           = ipoib_get_stats;
+       dev->tx_timeout          = ipoib_timeout;
+       dev->hard_header         = ipoib_hard_header;
+       dev->set_multicast_list  = ipoib_set_mcast_list;
+       dev->neigh_setup         = ipoib_neigh_setup_dev;
+
+       dev->watchdog_timeo      = HZ;
+
+       dev->rebuild_header      = NULL;
+       dev->set_mac_address     = NULL;
+       dev->header_cache_update = NULL;
+
+       dev->flags              |= IFF_BROADCAST | IFF_MULTICAST;
+
+       /*
+        * We add in INFINIBAND_ALEN to allow for the destination
+        * address "pseudoheader" for skbs without neighbour struct.
+        */
+       dev->hard_header_len     = IPOIB_ENCAP_LEN + INFINIBAND_ALEN;
+       dev->addr_len            = INFINIBAND_ALEN;
+       dev->type                = ARPHRD_INFINIBAND;
+       dev->tx_queue_len        = IPOIB_TX_RING_SIZE * 2;
+       dev->features            = NETIF_F_VLAN_CHALLENGED | NETIF_F_LLTX;
+
+       /* MTU will be reset when mcast join happens */
+       dev->mtu                 = IPOIB_PACKET_SIZE - IPOIB_ENCAP_LEN;
+       priv->mcast_mtu          = priv->admin_mtu = dev->mtu;
+
+       memcpy(dev->broadcast, ipv4_bcast_addr, INFINIBAND_ALEN);
+
+       netif_carrier_off(dev);
+
+       SET_MODULE_OWNER(dev);
+
+       priv->dev = dev;
+
+       spin_lock_init(&priv->lock);
+       spin_lock_init(&priv->tx_lock);
+
+       init_MUTEX(&priv->mcast_mutex);
+       init_MUTEX(&priv->vlan_mutex);
+
+       INIT_LIST_HEAD(&priv->path_list);
+       INIT_LIST_HEAD(&priv->child_intfs);
+       INIT_LIST_HEAD(&priv->dead_ahs);
+       INIT_LIST_HEAD(&priv->multicast_list);
+
+       INIT_WORK(&priv->pkey_task,    ipoib_pkey_poll,          priv->dev);
+       INIT_WORK(&priv->mcast_task,   ipoib_mcast_join_task,    priv->dev);
+       INIT_WORK(&priv->flush_task,   ipoib_ib_dev_flush,       priv->dev);
+       INIT_WORK(&priv->restart_task, ipoib_mcast_restart_task, priv->dev);
+       INIT_WORK(&priv->ah_reap_task, ipoib_reap_ah,            priv->dev);
+}
+
+struct ipoib_dev_priv *ipoib_intf_alloc(const char *name)
+{
+       struct net_device *dev;
+
+       dev = alloc_netdev((int) sizeof (struct ipoib_dev_priv), name,
+                          ipoib_setup);
+       if (!dev)
+               return NULL;
+
+       return netdev_priv(dev);
+}
+
+static ssize_t show_pkey(struct class_device *cdev, char *buf)
+{
+       struct ipoib_dev_priv *priv =
+               netdev_priv(container_of(cdev, struct net_device, class_dev));
+
+       return sprintf(buf, "0x%04x\n", priv->pkey);
+}
+static CLASS_DEVICE_ATTR(pkey, S_IRUGO, show_pkey, NULL);
+
+static ssize_t create_child(struct class_device *cdev,
+                           const char *buf, size_t count)
+{
+       int pkey;
+       int ret;
+
+       if (sscanf(buf, "%i", &pkey) != 1)
+               return -EINVAL;
+
+       if (pkey < 0 || pkey > 0xffff)
+               return -EINVAL;
+
+       ret = ipoib_vlan_add(container_of(cdev, struct net_device, class_dev),
+                            pkey);
+
+       return ret ? ret : count;
+}
+static CLASS_DEVICE_ATTR(create_child, S_IWUGO, NULL, create_child);
+
+static ssize_t delete_child(struct class_device *cdev,
+                           const char *buf, size_t count)
+{
+       int pkey;
+       int ret;
+
+       if (sscanf(buf, "%i", &pkey) != 1)
+               return -EINVAL;
+
+       if (pkey < 0 || pkey > 0xffff)
+               return -EINVAL;
+
+       ret = ipoib_vlan_delete(container_of(cdev, struct net_device, class_dev),
+                               pkey);
+
+       return ret ? ret : count;
+
+}
+static CLASS_DEVICE_ATTR(delete_child, S_IWUGO, NULL, delete_child);
+
+int ipoib_add_pkey_attr(struct net_device *dev)
+{
+       return class_device_create_file(&dev->class_dev,
+                                       &class_device_attr_pkey);
+}
+
+static struct net_device *ipoib_add_port(const char *format,
+                                        struct ib_device *hca, u8 port)
+{
+       struct ipoib_dev_priv *priv;
+       int result = -ENOMEM;
+
+       priv = ipoib_intf_alloc(format);
+       if (!priv)
+               goto alloc_mem_failed;
+
+       SET_NETDEV_DEV(priv->dev, hca->dma_device);
+
+       result = ib_query_pkey(hca, port, 0, &priv->pkey);
+       if (result) {
+               printk(KERN_WARNING "%s: ib_query_pkey port %d failed (ret = %d)\n",
+                      hca->name, port, result);
+               goto alloc_mem_failed;
+       }
+
+       priv->dev->broadcast[8] = priv->pkey >> 8;
+       priv->dev->broadcast[9] = priv->pkey & 0xff;
+
+       result = ib_query_gid(hca, port, 0, &priv->local_gid);
+       if (result) {
+               printk(KERN_WARNING "%s: ib_query_gid port %d failed (ret = %d)\n",
+                      hca->name, port, result);
+               goto alloc_mem_failed;
+       } else
+               memcpy(priv->dev->dev_addr + 4, priv->local_gid.raw, sizeof (union ib_gid));
+
+
+       result = ipoib_dev_init(priv->dev, hca, port);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: failed to initialize port %d (ret = %d)\n",
+                      hca->name, port, result);
+               goto device_init_failed;
+       }
+
+       INIT_IB_EVENT_HANDLER(&priv->event_handler,
+                             priv->ca, ipoib_event);
+       result = ib_register_event_handler(&priv->event_handler);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: ib_register_event_handler failed for "
+                      "port %d (ret = %d)\n",
+                      hca->name, port, result);
+               goto event_failed;
+       }
+
+       result = register_netdev(priv->dev);
+       if (result) {
+               printk(KERN_WARNING "%s: couldn't register ipoib port %d; error %d\n",
+                      hca->name, port, result);
+               goto register_failed;
+       }
+
+       if (ipoib_create_debug_file(priv->dev))
+               goto debug_failed;
+
+       if (ipoib_add_pkey_attr(priv->dev))
+               goto sysfs_failed;
+       if (class_device_create_file(&priv->dev->class_dev,
+                                    &class_device_attr_create_child))
+               goto sysfs_failed;
+       if (class_device_create_file(&priv->dev->class_dev,
+                                    &class_device_attr_delete_child))
+               goto sysfs_failed;
+
+       return priv->dev;
+
+sysfs_failed:
+       ipoib_delete_debug_file(priv->dev);
+
+debug_failed:
+       unregister_netdev(priv->dev);
+
+register_failed:
+       ib_unregister_event_handler(&priv->event_handler);
+
+event_failed:
+       ipoib_dev_cleanup(priv->dev);
+
+device_init_failed:
+       free_netdev(priv->dev);
+
+alloc_mem_failed:
+       return ERR_PTR(result);
+}
+
+static void ipoib_add_one(struct ib_device *device)
+{
+       struct list_head *dev_list;
+       struct net_device *dev;
+       struct ipoib_dev_priv *priv;
+       int s, e, p;
+
+       dev_list = kmalloc(sizeof *dev_list, GFP_KERNEL);
+       if (!dev_list)
+               return;
+
+       INIT_LIST_HEAD(dev_list);
+
+       if (device->node_type == IB_NODE_SWITCH) {
+               s = 0;
+               e = 0;
+       } else {
+               s = 1;
+               e = device->phys_port_cnt;
+       }
+
+       for (p = s; p <= e; ++p) {
+               dev = ipoib_add_port("ib%d", device, p);
+               if (!IS_ERR(dev)) {
+                       priv = netdev_priv(dev);
+                       list_add_tail(&priv->list, dev_list);
+               }
+       }
+
+       ib_set_client_data(device, &ipoib_client, dev_list);
+}
+
+static void ipoib_remove_one(struct ib_device *device)
+{
+       struct ipoib_dev_priv *priv, *tmp;
+       struct list_head *dev_list;
+
+       dev_list = ib_get_client_data(device, &ipoib_client);
+
+       list_for_each_entry_safe(priv, tmp, dev_list, list) {
+               ib_unregister_event_handler(&priv->event_handler);
+
+               unregister_netdev(priv->dev);
+               ipoib_dev_cleanup(priv->dev);
+               free_netdev(priv->dev);
+       }
+}
+
+static int __init ipoib_init_module(void)
+{
+       int ret;
+
+       ret = ipoib_register_debugfs();
+       if (ret)
+               return ret;
+
+       /*
+        * We create our own workqueue mainly because we want to be
+        * able to flush it when devices are being removed.  We can't
+        * use schedule_work()/flush_scheduled_work() because both
+        * unregister_netdev() and linkwatch_event take the rtnl lock,
+        * so flush_scheduled_work() can deadlock during device
+        * removal.
+        */
+       ipoib_workqueue = create_singlethread_workqueue("ipoib");
+       if (!ipoib_workqueue) {
+               ret = -ENOMEM;
+               goto err_fs;
+       }
+
+       ret = ib_register_client(&ipoib_client);
+       if (ret)
+               goto err_wq;
+
+       return 0;
+
+err_fs:
+       ipoib_unregister_debugfs();
+
+err_wq:
+       destroy_workqueue(ipoib_workqueue);
+
+       return ret;
+}
+
+static void __exit ipoib_cleanup_module(void)
+{
+       ipoib_unregister_debugfs();
+       ib_unregister_client(&ipoib_client);
+       destroy_workqueue(ipoib_workqueue);
+}
+
+module_init(ipoib_init_module);
+module_exit(ipoib_cleanup_module);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
new file mode 100644 (file)
index 0000000..6896962
--- /dev/null
@@ -0,0 +1,985 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ipoib_multicast.c 1362 2004-12-18 15:56:29Z roland $
+ */
+
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/igmp.h>
+#include <linux/inetdevice.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+
+#include "ipoib.h"
+
+#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG
+static int mcast_debug_level;
+
+module_param(mcast_debug_level, int, 0644);
+MODULE_PARM_DESC(mcast_debug_level,
+                "Enable multicast debug tracing if > 0");
+#endif
+
+static DECLARE_MUTEX(mcast_mutex);
+
+/* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */
+struct ipoib_mcast {
+       struct ib_sa_mcmember_rec mcmember;
+       struct ipoib_ah          *ah;
+
+       struct rb_node    rb_node;
+       struct list_head  list;
+       struct completion done;
+
+       int                 query_id;
+       struct ib_sa_query *query;
+
+       unsigned long created;
+       unsigned long backoff;
+
+       unsigned long flags;
+       unsigned char logcount;
+
+       struct list_head  neigh_list;
+
+       struct sk_buff_head pkt_queue;
+
+       struct net_device *dev;
+};
+
+struct ipoib_mcast_iter {
+       struct net_device *dev;
+       union ib_gid       mgid;
+       unsigned long      created;
+       unsigned int       queuelen;
+       unsigned int       complete;
+       unsigned int       send_only;
+};
+
+static void ipoib_mcast_free(struct ipoib_mcast *mcast)
+{
+       struct net_device *dev = mcast->dev;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_neigh *neigh, *tmp;
+       unsigned long flags;
+
+       ipoib_dbg_mcast(netdev_priv(dev),
+                       "deleting multicast group " IPOIB_GID_FMT "\n",
+                       IPOIB_GID_ARG(mcast->mcmember.mgid));
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       list_for_each_entry_safe(neigh, tmp, &mcast->neigh_list, list) {
+               ipoib_put_ah(neigh->ah);
+               *to_ipoib_neigh(neigh->neighbour) = NULL;
+               neigh->neighbour->ops->destructor = NULL;
+               kfree(neigh);
+       }
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       if (mcast->ah)
+               ipoib_put_ah(mcast->ah);
+
+       while (!skb_queue_empty(&mcast->pkt_queue)) {
+               struct sk_buff *skb = skb_dequeue(&mcast->pkt_queue);
+
+               skb->dev = dev;
+               dev_kfree_skb_any(skb);
+       }
+
+       kfree(mcast);
+}
+
+static struct ipoib_mcast *ipoib_mcast_alloc(struct net_device *dev,
+                                            int can_sleep)
+{
+       struct ipoib_mcast *mcast;
+
+       mcast = kmalloc(sizeof (*mcast), can_sleep ? GFP_KERNEL : GFP_ATOMIC);
+       if (!mcast)
+               return NULL;
+
+       memset(mcast, 0, sizeof (*mcast));
+
+       init_completion(&mcast->done);
+
+       mcast->dev = dev;
+       mcast->created = jiffies;
+       mcast->backoff = HZ;
+       mcast->logcount = 0;
+
+       INIT_LIST_HEAD(&mcast->list);
+       INIT_LIST_HEAD(&mcast->neigh_list);
+       skb_queue_head_init(&mcast->pkt_queue);
+
+       mcast->ah    = NULL;
+       mcast->query = NULL;
+
+       return mcast;
+}
+
+static struct ipoib_mcast *__ipoib_mcast_find(struct net_device *dev, union ib_gid *mgid)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct rb_node *n = priv->multicast_tree.rb_node;
+
+       while (n) {
+               struct ipoib_mcast *mcast;
+               int ret;
+
+               mcast = rb_entry(n, struct ipoib_mcast, rb_node);
+
+               ret = memcmp(mgid->raw, mcast->mcmember.mgid.raw,
+                            sizeof (union ib_gid));
+               if (ret < 0)
+                       n = n->rb_left;
+               else if (ret > 0)
+                       n = n->rb_right;
+               else
+                       return mcast;
+       }
+
+       return NULL;
+}
+
+static int __ipoib_mcast_add(struct net_device *dev, struct ipoib_mcast *mcast)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct rb_node **n = &priv->multicast_tree.rb_node, *pn = NULL;
+
+       while (*n) {
+               struct ipoib_mcast *tmcast;
+               int ret;
+
+               pn = *n;
+               tmcast = rb_entry(pn, struct ipoib_mcast, rb_node);
+
+               ret = memcmp(mcast->mcmember.mgid.raw, tmcast->mcmember.mgid.raw,
+                            sizeof (union ib_gid));
+               if (ret < 0)
+                       n = &pn->rb_left;
+               else if (ret > 0)
+                       n = &pn->rb_right;
+               else
+                       return -EEXIST;
+       }
+
+       rb_link_node(&mcast->rb_node, pn, n);
+       rb_insert_color(&mcast->rb_node, &priv->multicast_tree);
+
+       return 0;
+}
+
+static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,
+                                  struct ib_sa_mcmember_rec *mcmember)
+{
+       struct net_device *dev = mcast->dev;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       int ret;
+
+       mcast->mcmember = *mcmember;
+
+       /* Set the cached Q_Key before we attach if it's the broadcast group */
+       if (!memcmp(mcast->mcmember.mgid.raw, priv->dev->broadcast + 4,
+                   sizeof (union ib_gid))) {
+               priv->qkey = be32_to_cpu(priv->broadcast->mcmember.qkey);
+               priv->tx_wr.wr.ud.remote_qkey = priv->qkey;
+       }
+
+       if (!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
+               if (test_and_set_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) {
+                       ipoib_warn(priv, "multicast group " IPOIB_GID_FMT
+                                  " already attached\n",
+                                  IPOIB_GID_ARG(mcast->mcmember.mgid));
+
+                       return 0;
+               }
+
+               ret = ipoib_mcast_attach(dev, be16_to_cpu(mcast->mcmember.mlid),
+                                        &mcast->mcmember.mgid);
+               if (ret < 0) {
+                       ipoib_warn(priv, "couldn't attach QP to multicast group "
+                                  IPOIB_GID_FMT "\n",
+                                  IPOIB_GID_ARG(mcast->mcmember.mgid));
+
+                       clear_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags);
+                       return ret;
+               }
+       }
+
+       {
+               struct ib_ah_attr av = {
+                       .dlid          = be16_to_cpu(mcast->mcmember.mlid),
+                       .port_num      = priv->port,
+                       .sl            = mcast->mcmember.sl,
+                       .ah_flags      = IB_AH_GRH,
+                       .grh           = {
+                               .flow_label    = be32_to_cpu(mcast->mcmember.flow_label),
+                               .hop_limit     = mcast->mcmember.hop_limit,
+                               .sgid_index    = 0,
+                               .traffic_class = mcast->mcmember.traffic_class
+                       }
+               };
+
+               av.grh.dgid = mcast->mcmember.mgid;
+
+               if (ib_sa_rate_enum_to_int(mcast->mcmember.rate) > 0)
+                       av.static_rate = (2 * priv->local_rate -
+                                         ib_sa_rate_enum_to_int(mcast->mcmember.rate) - 1) /
+                               (priv->local_rate ? priv->local_rate : 1);
+
+               ipoib_dbg_mcast(priv, "static_rate %d for local port %dX, mcmember %dX\n",
+                               av.static_rate, priv->local_rate,
+                               ib_sa_rate_enum_to_int(mcast->mcmember.rate));
+
+               mcast->ah = ipoib_create_ah(dev, priv->pd, &av);
+               if (!mcast->ah) {
+                       ipoib_warn(priv, "ib_address_create failed\n");
+               } else {
+                       ipoib_dbg_mcast(priv, "MGID " IPOIB_GID_FMT
+                                       " AV %p, LID 0x%04x, SL %d\n",
+                                       IPOIB_GID_ARG(mcast->mcmember.mgid),
+                                       mcast->ah->ah,
+                                       be16_to_cpu(mcast->mcmember.mlid),
+                                       mcast->mcmember.sl);
+               }
+       }
+
+       /* actually send any queued packets */
+       while (!skb_queue_empty(&mcast->pkt_queue)) {
+               struct sk_buff *skb = skb_dequeue(&mcast->pkt_queue);
+
+               skb->dev = dev;
+
+               if (!skb->dst || !skb->dst->neighbour) {
+                       /* put pseudoheader back on for next time */
+                       skb_push(skb, sizeof (struct ipoib_pseudoheader));
+               }
+
+               if (dev_queue_xmit(skb))
+                       ipoib_warn(priv, "dev_queue_xmit failed to requeue packet\n");
+       }
+
+       return 0;
+}
+
+static void
+ipoib_mcast_sendonly_join_complete(int status,
+                                  struct ib_sa_mcmember_rec *mcmember,
+                                  void *mcast_ptr)
+{
+       struct ipoib_mcast *mcast = mcast_ptr;
+       struct net_device *dev = mcast->dev;
+
+       if (!status)
+               ipoib_mcast_join_finish(mcast, mcmember);
+       else {
+               if (mcast->logcount++ < 20)
+                       ipoib_dbg_mcast(netdev_priv(dev), "multicast join failed for "
+                                       IPOIB_GID_FMT ", status %d\n",
+                                       IPOIB_GID_ARG(mcast->mcmember.mgid), status);
+
+               /* Flush out any queued packets */
+               while (!skb_queue_empty(&mcast->pkt_queue)) {
+                       struct sk_buff *skb = skb_dequeue(&mcast->pkt_queue);
+
+                       skb->dev = dev;
+
+                       dev_kfree_skb_any(skb);
+               }
+
+               /* Clear the busy flag so we try again */
+               clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
+       }
+
+       complete(&mcast->done);
+}
+
+static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast)
+{
+       struct net_device *dev = mcast->dev;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ib_sa_mcmember_rec rec = {
+#if 0                          /* Some SMs don't support send-only yet */
+               .join_state = 4
+#else
+               .join_state = 1
+#endif
+       };
+       int ret = 0;
+
+       if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) {
+               ipoib_dbg_mcast(priv, "device shutting down, no multicast joins\n");
+               return -ENODEV;
+       }
+
+       if (test_and_set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) {
+               ipoib_dbg_mcast(priv, "multicast entry busy, skipping\n");
+               return -EBUSY;
+       }
+
+       rec.mgid     = mcast->mcmember.mgid;
+       rec.port_gid = priv->local_gid;
+       rec.pkey     = be16_to_cpu(priv->pkey);
+
+       ret = ib_sa_mcmember_rec_set(priv->ca, priv->port, &rec,
+                                    IB_SA_MCMEMBER_REC_MGID            |
+                                    IB_SA_MCMEMBER_REC_PORT_GID        |
+                                    IB_SA_MCMEMBER_REC_PKEY            |
+                                    IB_SA_MCMEMBER_REC_JOIN_STATE,
+                                    1000, GFP_ATOMIC,
+                                    ipoib_mcast_sendonly_join_complete,
+                                    mcast, &mcast->query);
+       if (ret < 0) {
+               ipoib_warn(priv, "ib_sa_mcmember_rec_set failed (ret = %d)\n",
+                          ret);
+       } else {
+               ipoib_dbg_mcast(priv, "no multicast record for " IPOIB_GID_FMT
+                               ", starting join\n",
+                               IPOIB_GID_ARG(mcast->mcmember.mgid));
+
+               mcast->query_id = ret;
+       }
+
+       return ret;
+}
+
+static void ipoib_mcast_join_complete(int status,
+                                     struct ib_sa_mcmember_rec *mcmember,
+                                     void *mcast_ptr)
+{
+       struct ipoib_mcast *mcast = mcast_ptr;
+       struct net_device *dev = mcast->dev;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       ipoib_dbg_mcast(priv, "join completion for " IPOIB_GID_FMT
+                       " (status %d)\n",
+                       IPOIB_GID_ARG(mcast->mcmember.mgid), status);
+
+       if (!status && !ipoib_mcast_join_finish(mcast, mcmember)) {
+               mcast->backoff = HZ;
+               down(&mcast_mutex);
+               if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
+                       queue_work(ipoib_workqueue, &priv->mcast_task);
+               up(&mcast_mutex);
+               complete(&mcast->done);
+               return;
+       }
+
+       if (status == -EINTR) {
+               complete(&mcast->done);
+               return;
+       }
+
+       if (status && mcast->logcount++ < 20) {
+               if (status == -ETIMEDOUT || status == -EINTR) {
+                       ipoib_dbg_mcast(priv, "multicast join failed for " IPOIB_GID_FMT
+                                       ", status %d\n",
+                                       IPOIB_GID_ARG(mcast->mcmember.mgid),
+                                       status);
+               } else {
+                       ipoib_warn(priv, "multicast join failed for "
+                                  IPOIB_GID_FMT ", status %d\n",
+                                  IPOIB_GID_ARG(mcast->mcmember.mgid),
+                                  status);
+               }
+       }
+
+       mcast->backoff *= 2;
+       if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS)
+               mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS;
+
+       mcast->query = NULL;
+
+       down(&mcast_mutex);
+       if (test_bit(IPOIB_MCAST_RUN, &priv->flags)) {
+               if (status == -ETIMEDOUT)
+                       queue_work(ipoib_workqueue, &priv->mcast_task);
+               else
+                       queue_delayed_work(ipoib_workqueue, &priv->mcast_task,
+                                          mcast->backoff * HZ);
+       } else
+               complete(&mcast->done);
+       up(&mcast_mutex);
+
+       return;
+}
+
+static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast,
+                            int create)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ib_sa_mcmember_rec rec = {
+               .join_state = 1
+       };
+       ib_sa_comp_mask comp_mask;
+       int ret = 0;
+
+       ipoib_dbg_mcast(priv, "joining MGID " IPOIB_GID_FMT "\n",
+                       IPOIB_GID_ARG(mcast->mcmember.mgid));
+
+       rec.mgid     = mcast->mcmember.mgid;
+       rec.port_gid = priv->local_gid;
+       rec.pkey     = be16_to_cpu(priv->pkey);
+
+       comp_mask =
+               IB_SA_MCMEMBER_REC_MGID         |
+               IB_SA_MCMEMBER_REC_PORT_GID     |
+               IB_SA_MCMEMBER_REC_PKEY         |
+               IB_SA_MCMEMBER_REC_JOIN_STATE;
+
+       if (create) {
+               comp_mask |=
+                       IB_SA_MCMEMBER_REC_QKEY         |
+                       IB_SA_MCMEMBER_REC_SL           |
+                       IB_SA_MCMEMBER_REC_FLOW_LABEL   |
+                       IB_SA_MCMEMBER_REC_TRAFFIC_CLASS;
+
+               rec.qkey          = priv->broadcast->mcmember.qkey;
+               rec.sl            = priv->broadcast->mcmember.sl;
+               rec.flow_label    = priv->broadcast->mcmember.flow_label;
+               rec.traffic_class = priv->broadcast->mcmember.traffic_class;
+       }
+
+       ret = ib_sa_mcmember_rec_set(priv->ca, priv->port, &rec, comp_mask,
+                                    mcast->backoff * 1000, GFP_ATOMIC,
+                                    ipoib_mcast_join_complete,
+                                    mcast, &mcast->query);
+
+       if (ret < 0) {
+               ipoib_warn(priv, "ib_sa_mcmember_rec_set failed, status %d\n", ret);
+
+               mcast->backoff *= 2;
+               if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS)
+                       mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS;
+
+               down(&mcast_mutex);
+               if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
+                       queue_delayed_work(ipoib_workqueue,
+                                          &priv->mcast_task,
+                                          mcast->backoff);
+               up(&mcast_mutex);
+       } else
+               mcast->query_id = ret;
+}
+
+void ipoib_mcast_join_task(void *dev_ptr)
+{
+       struct net_device *dev = dev_ptr;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       if (!test_bit(IPOIB_MCAST_RUN, &priv->flags))
+               return;
+
+       if (ib_query_gid(priv->ca, priv->port, 0, &priv->local_gid))
+               ipoib_warn(priv, "ib_gid_entry_get() failed\n");
+       else
+               memcpy(priv->dev->dev_addr + 4, priv->local_gid.raw, sizeof (union ib_gid));
+
+       {
+               struct ib_port_attr attr;
+
+               if (!ib_query_port(priv->ca, priv->port, &attr)) {
+                       priv->local_lid  = attr.lid;
+                       priv->local_rate = attr.active_speed *
+                               ib_width_enum_to_int(attr.active_width);
+               } else
+                       ipoib_warn(priv, "ib_query_port failed\n");
+       }
+
+       if (!priv->broadcast) {
+               priv->broadcast = ipoib_mcast_alloc(dev, 1);
+               if (!priv->broadcast) {
+                       ipoib_warn(priv, "failed to allocate broadcast group\n");
+                       down(&mcast_mutex);
+                       if (test_bit(IPOIB_MCAST_RUN, &priv->flags))
+                               queue_delayed_work(ipoib_workqueue,
+                                                  &priv->mcast_task, HZ);
+                       up(&mcast_mutex);
+                       return;
+               }
+
+               memcpy(priv->broadcast->mcmember.mgid.raw, priv->dev->broadcast + 4,
+                      sizeof (union ib_gid));
+
+               spin_lock_irq(&priv->lock);
+               __ipoib_mcast_add(dev, priv->broadcast);
+               spin_unlock_irq(&priv->lock);
+       }
+
+       if (!test_bit(IPOIB_MCAST_FLAG_ATTACHED, &priv->broadcast->flags)) {
+               ipoib_mcast_join(dev, priv->broadcast, 0);
+               return;
+       }
+
+       while (1) {
+               struct ipoib_mcast *mcast = NULL;
+
+               spin_lock_irq(&priv->lock);
+               list_for_each_entry(mcast, &priv->multicast_list, list) {
+                       if (!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)
+                           && !test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)
+                           && !test_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) {
+                               /* Found the next unjoined group */
+                               break;
+                       }
+               }
+               spin_unlock_irq(&priv->lock);
+
+               if (&mcast->list == &priv->multicast_list) {
+                       /* All done */
+                       break;
+               }
+
+               ipoib_mcast_join(dev, mcast, 1);
+               return;
+       }
+
+       priv->mcast_mtu = ib_mtu_enum_to_int(priv->broadcast->mcmember.mtu) -
+               IPOIB_ENCAP_LEN;
+       dev->mtu = min(priv->mcast_mtu, priv->admin_mtu);
+
+       ipoib_dbg_mcast(priv, "successfully joined all multicast groups\n");
+
+       clear_bit(IPOIB_MCAST_RUN, &priv->flags);
+       netif_carrier_on(dev);
+}
+
+int ipoib_mcast_start_thread(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       ipoib_dbg_mcast(priv, "starting multicast thread\n");
+
+       down(&mcast_mutex);
+       if (!test_and_set_bit(IPOIB_MCAST_RUN, &priv->flags))
+               queue_work(ipoib_workqueue, &priv->mcast_task);
+       up(&mcast_mutex);
+
+       return 0;
+}
+
+int ipoib_mcast_stop_thread(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_mcast *mcast;
+
+       ipoib_dbg_mcast(priv, "stopping multicast thread\n");
+
+       down(&mcast_mutex);
+       clear_bit(IPOIB_MCAST_RUN, &priv->flags);
+       cancel_delayed_work(&priv->mcast_task);
+       up(&mcast_mutex);
+
+       flush_workqueue(ipoib_workqueue);
+
+       if (priv->broadcast && priv->broadcast->query) {
+               ib_sa_cancel_query(priv->broadcast->query_id, priv->broadcast->query);
+               priv->broadcast->query = NULL;
+               ipoib_dbg_mcast(priv, "waiting for bcast\n");
+               wait_for_completion(&priv->broadcast->done);
+       }
+
+       list_for_each_entry(mcast, &priv->multicast_list, list) {
+               if (mcast->query) {
+                       ib_sa_cancel_query(mcast->query_id, mcast->query);
+                       mcast->query = NULL;
+                       ipoib_dbg_mcast(priv, "waiting for MGID " IPOIB_GID_FMT "\n",
+                                       IPOIB_GID_ARG(mcast->mcmember.mgid));
+                       wait_for_completion(&mcast->done);
+               }
+       }
+
+       return 0;
+}
+
+static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ib_sa_mcmember_rec rec = {
+               .join_state = 1
+       };
+       int ret = 0;
+
+       if (!test_and_clear_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags))
+               return 0;
+
+       ipoib_dbg_mcast(priv, "leaving MGID " IPOIB_GID_FMT "\n",
+                       IPOIB_GID_ARG(mcast->mcmember.mgid));
+
+       rec.mgid     = mcast->mcmember.mgid;
+       rec.port_gid = priv->local_gid;
+       rec.pkey     = be16_to_cpu(priv->pkey);
+
+       /* Remove ourselves from the multicast group */
+       ret = ipoib_mcast_detach(dev, be16_to_cpu(mcast->mcmember.mlid),
+                                &mcast->mcmember.mgid);
+       if (ret)
+               ipoib_warn(priv, "ipoib_mcast_detach failed (result = %d)\n", ret);
+
+       /*
+        * Just make one shot at leaving and don't wait for a reply;
+        * if we fail, too bad.
+        */
+       ret = ib_sa_mcmember_rec_delete(priv->ca, priv->port, &rec,
+                                       IB_SA_MCMEMBER_REC_MGID         |
+                                       IB_SA_MCMEMBER_REC_PORT_GID     |
+                                       IB_SA_MCMEMBER_REC_PKEY         |
+                                       IB_SA_MCMEMBER_REC_JOIN_STATE,
+                                       0, GFP_ATOMIC, NULL,
+                                       mcast, &mcast->query);
+       if (ret < 0)
+               ipoib_warn(priv, "ib_sa_mcmember_rec_delete failed "
+                          "for leave (result = %d)\n", ret);
+
+       return 0;
+}
+
+void ipoib_mcast_send(struct net_device *dev, union ib_gid *mgid,
+                     struct sk_buff *skb)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ipoib_mcast *mcast;
+
+       /*
+        * We can only be called from ipoib_start_xmit, so we're
+        * inside tx_lock -- no need to save/restore flags.
+        */
+       spin_lock(&priv->lock);
+
+       mcast = __ipoib_mcast_find(dev, mgid);
+       if (!mcast) {
+               /* Let's create a new send only group now */
+               ipoib_dbg_mcast(priv, "setting up send only multicast group for "
+                               IPOIB_GID_FMT "\n", IPOIB_GID_ARG(*mgid));
+
+               mcast = ipoib_mcast_alloc(dev, 0);
+               if (!mcast) {
+                       ipoib_warn(priv, "unable to allocate memory for "
+                                  "multicast structure\n");
+                       dev_kfree_skb_any(skb);
+                       goto out;
+               }
+
+               set_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags);
+               mcast->mcmember.mgid = *mgid;
+               __ipoib_mcast_add(dev, mcast);
+               list_add_tail(&mcast->list, &priv->multicast_list);
+       }
+
+       if (!mcast->ah) {
+               if (skb_queue_len(&mcast->pkt_queue) < IPOIB_MAX_MCAST_QUEUE)
+                       skb_queue_tail(&mcast->pkt_queue, skb);
+               else
+                       dev_kfree_skb_any(skb);
+
+               if (mcast->query)
+                       ipoib_dbg_mcast(priv, "no address vector, "
+                                       "but multicast join already started\n");
+               else if (test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags))
+                       ipoib_mcast_sendonly_join(mcast);
+
+               /*
+                * If lookup completes between here and out:, don't
+                * want to send packet twice.
+                */
+               mcast = NULL;
+       }
+
+out:
+       if (mcast && mcast->ah) {
+               if (skb->dst            &&
+                   skb->dst->neighbour &&
+                   !*to_ipoib_neigh(skb->dst->neighbour)) {
+                       struct ipoib_neigh *neigh = kmalloc(sizeof *neigh, GFP_ATOMIC);
+
+                       if (neigh) {
+                               kref_get(&mcast->ah->ref);
+                               neigh->ah       = mcast->ah;
+                               neigh->neighbour = skb->dst->neighbour;
+                               *to_ipoib_neigh(skb->dst->neighbour) = neigh;
+                               list_add_tail(&neigh->list, &mcast->neigh_list);
+                       }
+               }
+
+               ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN);
+       }
+
+       spin_unlock(&priv->lock);
+}
+
+void ipoib_mcast_dev_flush(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       LIST_HEAD(remove_list);
+       struct ipoib_mcast *mcast, *tmcast, *nmcast;
+       unsigned long flags;
+
+       ipoib_dbg_mcast(priv, "flushing multicast list\n");
+
+       spin_lock_irqsave(&priv->lock, flags);
+       list_for_each_entry_safe(mcast, tmcast, &priv->multicast_list, list) {
+               nmcast = ipoib_mcast_alloc(dev, 0);
+               if (nmcast) {
+                       nmcast->flags =
+                               mcast->flags & (1 << IPOIB_MCAST_FLAG_SENDONLY);
+
+                       nmcast->mcmember.mgid = mcast->mcmember.mgid;
+
+                       /* Add the new group in before the to-be-destroyed group */
+                       list_add_tail(&nmcast->list, &mcast->list);
+                       list_del_init(&mcast->list);
+
+                       rb_replace_node(&mcast->rb_node, &nmcast->rb_node,
+                                       &priv->multicast_tree);
+
+                       list_add_tail(&mcast->list, &remove_list);
+               } else {
+                       ipoib_warn(priv, "could not reallocate multicast group "
+                                  IPOIB_GID_FMT "\n",
+                                  IPOIB_GID_ARG(mcast->mcmember.mgid));
+               }
+       }
+
+       if (priv->broadcast) {
+               nmcast = ipoib_mcast_alloc(dev, 0);
+               if (nmcast) {
+                       nmcast->mcmember.mgid = priv->broadcast->mcmember.mgid;
+
+                       rb_replace_node(&priv->broadcast->rb_node,
+                                       &nmcast->rb_node,
+                                       &priv->multicast_tree);
+
+                       list_add_tail(&priv->broadcast->list, &remove_list);
+               }
+
+               priv->broadcast = nmcast;
+       }
+
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       list_for_each_entry(mcast, &remove_list, list) {
+               ipoib_mcast_leave(dev, mcast);
+               ipoib_mcast_free(mcast);
+       }
+}
+
+void ipoib_mcast_dev_down(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       unsigned long flags;
+
+       /* Delete broadcast since it will be recreated */
+       if (priv->broadcast) {
+               ipoib_dbg_mcast(priv, "deleting broadcast group\n");
+
+               spin_lock_irqsave(&priv->lock, flags);
+               rb_erase(&priv->broadcast->rb_node, &priv->multicast_tree);
+               spin_unlock_irqrestore(&priv->lock, flags);
+               ipoib_mcast_leave(dev, priv->broadcast);
+               ipoib_mcast_free(priv->broadcast);
+               priv->broadcast = NULL;
+       }
+}
+
+void ipoib_mcast_restart_task(void *dev_ptr)
+{
+       struct net_device *dev = dev_ptr;
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct dev_mc_list *mclist;
+       struct ipoib_mcast *mcast, *tmcast;
+       LIST_HEAD(remove_list);
+       unsigned long flags;
+
+       ipoib_dbg_mcast(priv, "restarting multicast task\n");
+
+       ipoib_mcast_stop_thread(dev);
+
+       spin_lock_irqsave(&priv->lock, flags);
+
+       /*
+        * Unfortunately, the networking core only gives us a list of all of
+        * the multicast hardware addresses. We need to figure out which ones
+        * are new and which ones have been removed
+        */
+
+       /* Clear out the found flag */
+       list_for_each_entry(mcast, &priv->multicast_list, list)
+               clear_bit(IPOIB_MCAST_FLAG_FOUND, &mcast->flags);
+
+       /* Mark all of the entries that are found or don't exist */
+       for (mclist = dev->mc_list; mclist; mclist = mclist->next) {
+               union ib_gid mgid;
+
+               memcpy(mgid.raw, mclist->dmi_addr + 4, sizeof mgid);
+
+               /* Add in the P_Key */
+               mgid.raw[4] = (priv->pkey >> 8) & 0xff;
+               mgid.raw[5] = priv->pkey & 0xff;
+
+               mcast = __ipoib_mcast_find(dev, &mgid);
+               if (!mcast || test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
+                       struct ipoib_mcast *nmcast;
+
+                       /* Not found or send-only group, let's add a new entry */
+                       ipoib_dbg_mcast(priv, "adding multicast entry for mgid "
+                                       IPOIB_GID_FMT "\n", IPOIB_GID_ARG(mgid));
+
+                       nmcast = ipoib_mcast_alloc(dev, 0);
+                       if (!nmcast) {
+                               ipoib_warn(priv, "unable to allocate memory for multicast structure\n");
+                               continue;
+                       }
+
+                       set_bit(IPOIB_MCAST_FLAG_FOUND, &nmcast->flags);
+
+                       nmcast->mcmember.mgid = mgid;
+
+                       if (mcast) {
+                               /* Destroy the send only entry */
+                               list_del(&mcast->list);
+                               list_add_tail(&mcast->list, &remove_list);
+
+                               rb_replace_node(&mcast->rb_node,
+                                               &nmcast->rb_node,
+                                               &priv->multicast_tree);
+                       } else
+                               __ipoib_mcast_add(dev, nmcast);
+
+                       list_add_tail(&nmcast->list, &priv->multicast_list);
+               }
+
+               if (mcast)
+                       set_bit(IPOIB_MCAST_FLAG_FOUND, &mcast->flags);
+       }
+
+       /* Remove all of the entries don't exist anymore */
+       list_for_each_entry_safe(mcast, tmcast, &priv->multicast_list, list) {
+               if (!test_bit(IPOIB_MCAST_FLAG_FOUND, &mcast->flags) &&
+                   !test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) {
+                       ipoib_dbg_mcast(priv, "deleting multicast group " IPOIB_GID_FMT "\n",
+                                       IPOIB_GID_ARG(mcast->mcmember.mgid));
+
+                       rb_erase(&mcast->rb_node, &priv->multicast_tree);
+
+                       /* Move to the remove list */
+                       list_del(&mcast->list);
+                       list_add_tail(&mcast->list, &remove_list);
+               }
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* We have to cancel outside of the spinlock */
+       list_for_each_entry(mcast, &remove_list, list) {
+               ipoib_mcast_leave(mcast->dev, mcast);
+               ipoib_mcast_free(mcast);
+       }
+
+       if (test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags))
+               ipoib_mcast_start_thread(dev);
+}
+
+struct ipoib_mcast_iter *ipoib_mcast_iter_init(struct net_device *dev)
+{
+       struct ipoib_mcast_iter *iter;
+
+       iter = kmalloc(sizeof *iter, GFP_KERNEL);
+       if (!iter)
+               return NULL;
+
+       iter->dev = dev;
+       memset(iter->mgid.raw, 0, sizeof iter->mgid);
+
+       if (ipoib_mcast_iter_next(iter)) {
+               ipoib_mcast_iter_free(iter);
+               return NULL;
+       }
+
+       return iter;
+}
+
+void ipoib_mcast_iter_free(struct ipoib_mcast_iter *iter)
+{
+       kfree(iter);
+}
+
+int ipoib_mcast_iter_next(struct ipoib_mcast_iter *iter)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(iter->dev);
+       struct rb_node *n;
+       struct ipoib_mcast *mcast;
+       int ret = 1;
+
+       spin_lock_irq(&priv->lock);
+
+       n = rb_first(&priv->multicast_tree);
+
+       while (n) {
+               mcast = rb_entry(n, struct ipoib_mcast, rb_node);
+
+               if (memcmp(iter->mgid.raw, mcast->mcmember.mgid.raw,
+                          sizeof (union ib_gid)) < 0) {
+                       iter->mgid      = mcast->mcmember.mgid;
+                       iter->created   = mcast->created;
+                       iter->queuelen  = skb_queue_len(&mcast->pkt_queue);
+                       iter->complete  = !!mcast->ah;
+                       iter->send_only = !!(mcast->flags & (1 << IPOIB_MCAST_FLAG_SENDONLY));
+
+                       ret = 0;
+
+                       break;
+               }
+
+               n = rb_next(n);
+       }
+
+       spin_unlock_irq(&priv->lock);
+
+       return ret;
+}
+
+void ipoib_mcast_iter_read(struct ipoib_mcast_iter *iter,
+                          union ib_gid *mgid,
+                          unsigned long *created,
+                          unsigned int *queuelen,
+                          unsigned int *complete,
+                          unsigned int *send_only)
+{
+       *mgid      = iter->mgid;
+       *created   = iter->created;
+       *queuelen  = iter->queuelen;
+       *complete  = iter->complete;
+       *send_only = iter->send_only;
+}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
new file mode 100644 (file)
index 0000000..368162c
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2004, 2005 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ipoib_verbs.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <ib_cache.h>
+
+#include "ipoib.h"
+
+int ipoib_mcast_attach(struct net_device *dev, u16 mlid, union ib_gid *mgid)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ib_qp_attr *qp_attr;
+       int attr_mask;
+       int ret;
+       u16 pkey_index;
+
+       ret = -ENOMEM;
+       qp_attr = kmalloc(sizeof *qp_attr, GFP_KERNEL);
+       if (!qp_attr)
+               goto out;
+
+       if (ib_find_cached_pkey(priv->ca, priv->port, priv->pkey, &pkey_index)) {
+               clear_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
+               ret = -ENXIO;
+               goto out;
+       }
+       set_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
+
+       /* set correct QKey for QP */
+       qp_attr->qkey = priv->qkey;
+       attr_mask = IB_QP_QKEY;
+       ret = ib_modify_qp(priv->qp, qp_attr, attr_mask);
+       if (ret) {
+               ipoib_warn(priv, "failed to modify QP, ret = %d\n", ret);
+               goto out;
+       }
+
+       /* attach QP to multicast group */
+       down(&priv->mcast_mutex);
+       ret = ib_attach_mcast(priv->qp, mgid, mlid);
+       up(&priv->mcast_mutex);
+       if (ret)
+               ipoib_warn(priv, "failed to attach to multicast group, ret = %d\n", ret);
+
+out:
+       kfree(qp_attr);
+       return ret;
+}
+
+int ipoib_mcast_detach(struct net_device *dev, u16 mlid, union ib_gid *mgid)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       int ret;
+
+       down(&priv->mcast_mutex);
+       ret = ib_detach_mcast(priv->qp, mgid, mlid);
+       up(&priv->mcast_mutex);
+       if (ret)
+               ipoib_warn(priv, "ib_detach_mcast failed (result = %d)\n", ret);
+
+       return ret;
+}
+
+int ipoib_qp_create(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       int ret;
+       u16 pkey_index;
+       struct ib_qp_attr qp_attr;
+       int attr_mask;
+
+       /*
+        * Search through the port P_Key table for the requested pkey value.
+        * The port has to be assigned to the respective IB partition in
+        * advance.
+        */
+       ret = ib_find_cached_pkey(priv->ca, priv->port, priv->pkey, &pkey_index);
+       if (ret) {
+               clear_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
+               return ret;
+       }
+       set_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
+
+       qp_attr.qp_state = IB_QPS_INIT;
+       qp_attr.qkey = 0;
+       qp_attr.port_num = priv->port;
+       qp_attr.pkey_index = pkey_index;
+       attr_mask =
+           IB_QP_QKEY |
+           IB_QP_PORT |
+           IB_QP_PKEY_INDEX |
+           IB_QP_STATE;
+       ret = ib_modify_qp(priv->qp, &qp_attr, attr_mask);
+       if (ret) {
+               ipoib_warn(priv, "failed to modify QP to init, ret = %d\n", ret);
+               goto out_fail;
+       }
+
+       qp_attr.qp_state = IB_QPS_RTR;
+       /* Can't set this in a INIT->RTR transition */
+       attr_mask &= ~IB_QP_PORT;
+       ret = ib_modify_qp(priv->qp, &qp_attr, attr_mask);
+       if (ret) {
+               ipoib_warn(priv, "failed to modify QP to RTR, ret = %d\n", ret);
+               goto out_fail;
+       }
+
+       qp_attr.qp_state = IB_QPS_RTS;
+       qp_attr.sq_psn = 0;
+       attr_mask |= IB_QP_SQ_PSN;
+       attr_mask &= ~IB_QP_PKEY_INDEX;
+       ret = ib_modify_qp(priv->qp, &qp_attr, attr_mask);
+       if (ret) {
+               ipoib_warn(priv, "failed to modify QP to RTS, ret = %d\n", ret);
+               goto out_fail;
+       }
+
+       return 0;
+
+out_fail:
+       ib_destroy_qp(priv->qp);
+       priv->qp = NULL;
+
+       return -EINVAL;
+}
+
+int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+       struct ib_qp_init_attr init_attr = {
+               .cap = {
+                       .max_send_wr  = IPOIB_TX_RING_SIZE,
+                       .max_recv_wr  = IPOIB_RX_RING_SIZE,
+                       .max_send_sge = 1,
+                       .max_recv_sge = 1
+               },
+               .sq_sig_type = IB_SIGNAL_ALL_WR,
+               .rq_sig_type = IB_SIGNAL_ALL_WR,
+               .qp_type     = IB_QPT_UD
+       };
+
+       priv->pd = ib_alloc_pd(priv->ca);
+       if (IS_ERR(priv->pd)) {
+               printk(KERN_WARNING "%s: failed to allocate PD\n", ca->name);
+               return -ENODEV;
+       }
+
+       priv->cq = ib_create_cq(priv->ca, ipoib_ib_completion, NULL, dev,
+                               IPOIB_TX_RING_SIZE + IPOIB_RX_RING_SIZE + 1);
+       if (IS_ERR(priv->cq)) {
+               printk(KERN_WARNING "%s: failed to create CQ\n", ca->name);
+               goto out_free_pd;
+       }
+
+       if (ib_req_notify_cq(priv->cq, IB_CQ_NEXT_COMP))
+               goto out_free_cq;
+
+       priv->mr = ib_get_dma_mr(priv->pd, IB_ACCESS_LOCAL_WRITE);
+       if (IS_ERR(priv->mr)) {
+               printk(KERN_WARNING "%s: ib_get_dma_mr failed\n", ca->name);
+               goto out_free_cq;
+       }
+
+       init_attr.send_cq = priv->cq;
+       init_attr.recv_cq = priv->cq,
+
+       priv->qp = ib_create_qp(priv->pd, &init_attr);
+       if (IS_ERR(priv->qp)) {
+               printk(KERN_WARNING "%s: failed to create QP\n", ca->name);
+               goto out_free_mr;
+       }
+
+       priv->dev->dev_addr[1] = (priv->qp->qp_num >> 16) & 0xff;
+       priv->dev->dev_addr[2] = (priv->qp->qp_num >>  8) & 0xff;
+       priv->dev->dev_addr[3] = (priv->qp->qp_num      ) & 0xff;
+
+       priv->tx_sge.lkey       = priv->mr->lkey;
+
+       priv->tx_wr.opcode      = IB_WR_SEND;
+       priv->tx_wr.sg_list     = &priv->tx_sge;
+       priv->tx_wr.num_sge     = 1;
+       priv->tx_wr.send_flags  = IB_SEND_SIGNALED;
+
+       return 0;
+
+out_free_mr:
+       ib_dereg_mr(priv->mr);
+
+out_free_cq:
+       ib_destroy_cq(priv->cq);
+
+out_free_pd:
+       ib_dealloc_pd(priv->pd);
+       return -ENODEV;
+}
+
+void ipoib_transport_dev_cleanup(struct net_device *dev)
+{
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       if (priv->qp) {
+               if (ib_destroy_qp(priv->qp))
+                       ipoib_warn(priv, "ib_qp_destroy failed\n");
+
+               priv->qp = NULL;
+               clear_bit(IPOIB_PKEY_ASSIGNED, &priv->flags);
+       }
+
+       if (ib_dereg_mr(priv->mr))
+               ipoib_warn(priv, "ib_dereg_mr failed\n");
+
+       if (ib_destroy_cq(priv->cq))
+               ipoib_warn(priv, "ib_cq_destroy failed\n");
+
+       if (ib_dealloc_pd(priv->pd))
+               ipoib_warn(priv, "ib_dealloc_pd failed\n");
+}
+
+void ipoib_event(struct ib_event_handler *handler,
+                struct ib_event *record)
+{
+       struct ipoib_dev_priv *priv =
+               container_of(handler, struct ipoib_dev_priv, event_handler);
+
+       if (record->event == IB_EVENT_PORT_ACTIVE ||
+           record->event == IB_EVENT_LID_CHANGE  ||
+           record->event == IB_EVENT_SM_CHANGE) {
+               ipoib_dbg(priv, "Port active event\n");
+               schedule_work(&priv->flush_task);
+       }
+}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
new file mode 100644 (file)
index 0000000..94b8ea8
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: ipoib_vlan.c 1349 2004-12-16 21:09:43Z roland $
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+
+#include <asm/uaccess.h>
+
+#include "ipoib.h"
+
+static ssize_t show_parent(struct class_device *class_dev, char *buf)
+{
+       struct net_device *dev =
+               container_of(class_dev, struct net_device, class_dev);
+       struct ipoib_dev_priv *priv = netdev_priv(dev);
+
+       return sprintf(buf, "%s\n", priv->parent->name);
+}
+static CLASS_DEVICE_ATTR(parent, S_IRUGO, show_parent, NULL);
+
+int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
+{
+       struct ipoib_dev_priv *ppriv, *priv;
+       char intf_name[IFNAMSIZ];
+       int result;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       ppriv = netdev_priv(pdev);
+
+       down(&ppriv->vlan_mutex);
+
+       /*
+        * First ensure this isn't a duplicate. We check the parent device and
+        * then all of the child interfaces to make sure the Pkey doesn't match.
+        */
+       if (ppriv->pkey == pkey) {
+               result = -ENOTUNIQ;
+               goto err;
+       }
+
+       list_for_each_entry(priv, &ppriv->child_intfs, list) {
+               if (priv->pkey == pkey) {
+                       result = -ENOTUNIQ;
+                       goto err;
+               }
+       }
+
+       snprintf(intf_name, sizeof intf_name, "%s.%04x",
+                ppriv->dev->name, pkey);
+       priv = ipoib_intf_alloc(intf_name);
+       if (!priv) {
+               result = -ENOMEM;
+               goto err;
+       }
+
+       set_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags);
+
+       priv->pkey = pkey;
+
+       memcpy(priv->dev->dev_addr, ppriv->dev->dev_addr, INFINIBAND_ALEN);
+       priv->dev->broadcast[8] = pkey >> 8;
+       priv->dev->broadcast[9] = pkey & 0xff;
+
+       result = ipoib_dev_init(priv->dev, ppriv->ca, ppriv->port);
+       if (result < 0) {
+               ipoib_warn(ppriv, "failed to initialize subinterface: "
+                          "device %s, port %d",
+                          ppriv->ca->name, ppriv->port);
+               goto device_init_failed;
+       }
+
+       result = register_netdev(priv->dev);
+       if (result) {
+               ipoib_warn(priv, "failed to initialize; error %i", result);
+               goto register_failed;
+       }
+
+       priv->parent = ppriv->dev;
+
+       if (ipoib_create_debug_file(priv->dev))
+               goto debug_failed;
+
+       if (ipoib_add_pkey_attr(priv->dev))
+               goto sysfs_failed;
+
+       if (class_device_create_file(&priv->dev->class_dev,
+                                    &class_device_attr_parent))
+               goto sysfs_failed;
+
+       list_add_tail(&priv->list, &ppriv->child_intfs);
+
+       up(&ppriv->vlan_mutex);
+
+       return 0;
+
+sysfs_failed:
+       ipoib_delete_debug_file(priv->dev);
+
+debug_failed:
+       unregister_netdev(priv->dev);
+
+register_failed:
+       ipoib_dev_cleanup(priv->dev);
+
+device_init_failed:
+       free_netdev(priv->dev);
+
+err:
+       up(&ppriv->vlan_mutex);
+       return result;
+}
+
+int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
+{
+       struct ipoib_dev_priv *ppriv, *priv, *tpriv;
+       int ret = -ENOENT;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       ppriv = netdev_priv(pdev);
+
+       down(&ppriv->vlan_mutex);
+       list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {
+               if (priv->pkey == pkey) {
+                       unregister_netdev(priv->dev);
+                       ipoib_dev_cleanup(priv->dev);
+
+                       list_del(&priv->list);
+
+                       kfree(priv);
+
+                       ret = 0;
+                       break;
+               }
+       }
+       up(&ppriv->vlan_mutex);
+
+       return ret;
+}
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
new file mode 100644 (file)
index 0000000..e8c0b61
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * ALPS touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
+ * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
+ *
+ * ALPS detection, tap switching and status querying info is taken from
+ * tpconfig utility (by C. Scott Ananian and Bruce Kall).
+ *
+ * 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 <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+
+#include "psmouse.h"
+#include "alps.h"
+
+#undef DEBUG
+#ifdef DEBUG
+#define dbg(format, arg...) printk(KERN_INFO "alps.c: " format "\n", ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+#define ALPS_MODEL_GLIDEPOINT  1
+#define ALPS_MODEL_DUALPOINT   2
+
+struct alps_model_info {
+       unsigned char signature[3];
+       unsigned char model;
+} alps_model_data[] = {
+/*     { { 0x33, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },        */
+       { { 0x53, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },
+       { { 0x53, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT },
+       { { 0x63, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },
+       { { 0x63, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT },
+       { { 0x73, 0x02, 0x0a }, ALPS_MODEL_GLIDEPOINT },
+       { { 0x73, 0x02, 0x14 }, ALPS_MODEL_GLIDEPOINT },
+       { { 0x63, 0x02, 0x28 }, ALPS_MODEL_GLIDEPOINT },
+/*     { { 0x63, 0x02, 0x3c }, ALPS_MODEL_GLIDEPOINT },        */
+/*     { { 0x63, 0x02, 0x50 }, ALPS_MODEL_GLIDEPOINT },        */
+       { { 0x63, 0x02, 0x64 }, ALPS_MODEL_GLIDEPOINT },
+       { { 0x20, 0x02, 0x0e }, ALPS_MODEL_DUALPOINT },
+       { { 0x22, 0x02, 0x0a }, ALPS_MODEL_DUALPOINT },
+       { { 0x22, 0x02, 0x14 }, ALPS_MODEL_DUALPOINT },
+       { { 0x63, 0x03, 0xc8 }, ALPS_MODEL_DUALPOINT },
+};
+
+/*
+ * ALPS abolute Mode
+ * byte 0:  1    1    1    1    1  mid0 rig0 lef0
+ * byte 1:  0   x6   x5   x4   x3   x2   x1   x0
+ * byte 2:  0   x10  x9   x8   x7  up1  fin  ges
+ * byte 3:  0   y9   y8   y7    1  mid1 rig1 lef1
+ * byte 4:  0   y6   y5   y4   y3   y2   y1   y0
+ * byte 5:  0   z6   z5   z4   z3   z2   z1   z0
+ *
+ * On a dualpoint, {mid,rig,lef}0 are the stick, 1 are the pad.
+ * We just 'or' them together for now.
+ *
+ * We used to send 'ges'tures as BTN_TOUCH but this made it impossible
+ * to disable tap events in the synaptics driver since the driver
+ * was unable to distinguish a gesture tap from an actual button click.
+ * A tap gesture now creates an emulated touch that the synaptics
+ * driver can interpret as a tap event, if MaxTapTime=0 and
+ * MaxTapMove=0 then the driver will ignore taps.
+ *
+ * The touchpad on an 'Acer Aspire' has 4 buttons:
+ *   left,right,up,down.
+ * This device always sets {mid,rig,lef}0 to 1 and
+ * reflects left,right,down,up in lef1,rig1,mid1,up1.
+ */
+
+static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs)
+{
+       unsigned char *packet = psmouse->packet;
+       struct input_dev *dev = &psmouse->dev;
+       int x, y, z;
+       int left = 0, right = 0, middle = 0;
+
+       input_regs(dev, regs);
+
+       if ((packet[0] & 0xc8) == 0x08) {   /* 3-byte PS/2 packet */
+               x = packet[1];
+               if (packet[0] & 0x10)
+                       x = x - 256;
+               y = packet[2];
+               if (packet[0] & 0x20)
+                       y = y - 256;
+               left  = (packet[0]     ) & 1;
+               right = (packet[0] >> 1) & 1;
+
+               input_report_rel(dev, REL_X, x);
+               input_report_rel(dev, REL_Y, -y);
+               input_report_key(dev, BTN_A, left);
+               input_report_key(dev, BTN_B, right);
+               input_sync(dev);
+               return;
+       }
+
+       x = (packet[1] & 0x7f) | ((packet[2] & 0x78)<<(7-3));
+       y = (packet[4] & 0x7f) | ((packet[3] & 0x70)<<(7-4));
+       z = packet[5];
+
+       if (z == 127) { /* DualPoint stick is relative, not absolute */
+               if (x > 383)
+                       x = x - 768;
+               if (y > 255)
+                       y = y - 512;
+               left  = packet[3] & 1;
+               right = (packet[3] >> 1) & 1;
+
+               input_report_rel(dev, REL_X, x);
+               input_report_rel(dev, REL_Y, -y);
+               input_report_key(dev, BTN_LEFT, left);
+               input_report_key(dev, BTN_RIGHT, right);
+               input_sync(dev);
+               return;
+       }
+
+       if (z > 30) input_report_key(dev, BTN_TOUCH, 1);
+       if (z < 25) input_report_key(dev, BTN_TOUCH, 0);
+
+       if (z > 0) {
+               input_report_abs(dev, ABS_X, x);
+               input_report_abs(dev, ABS_Y, y);
+       }
+       input_report_abs(dev, ABS_PRESSURE, z);
+       input_report_key(dev, BTN_TOOL_FINGER, z > 0);
+
+       left  |= (packet[2]     ) & 1;
+       left  |= (packet[3]     ) & 1;
+       right |= (packet[3] >> 1) & 1;
+       if (packet[0] == 0xff) {
+               int back    = (packet[3] >> 2) & 1;
+               int forward = (packet[2] >> 2) & 1;
+               if (back && forward) {
+                       middle = 1;
+                       back = 0;
+                       forward = 0;
+               }
+               input_report_key(dev, BTN_BACK,    back);
+               input_report_key(dev, BTN_FORWARD, forward);
+       } else {
+               left   |= (packet[0]     ) & 1;
+               right  |= (packet[0] >> 1) & 1;
+               middle |= (packet[0] >> 2) & 1;
+               middle |= (packet[3] >> 2) & 1;
+       }
+
+       input_report_key(dev, BTN_LEFT, left);
+       input_report_key(dev, BTN_RIGHT, right);
+       input_report_key(dev, BTN_MIDDLE, middle);
+
+       input_sync(dev);
+}
+
+static psmouse_ret_t alps_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
+{
+       if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
+               if (psmouse->pktcnt == 3) {
+                       alps_process_packet(psmouse, regs);
+                       return PSMOUSE_FULL_PACKET;
+               }
+               return PSMOUSE_GOOD_DATA;
+       }
+
+       /* ALPS absolute mode packets start with 0b11111mrl */
+       if ((psmouse->packet[0] & 0xf8) != 0xf8)
+               return PSMOUSE_BAD_DATA;
+
+       /* Bytes 2 - 6 should have 0 in the highest bit */
+       if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 &&
+           (psmouse->packet[psmouse->pktcnt-1] & 0x80))
+               return PSMOUSE_BAD_DATA;
+
+       if (psmouse->pktcnt == 6) {
+               alps_process_packet(psmouse, regs);
+               return PSMOUSE_FULL_PACKET;
+       }
+
+       return PSMOUSE_GOOD_DATA;
+}
+
+int alps_get_model(struct psmouse *psmouse)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       unsigned char param[4];
+       int i;
+
+       /*
+        * First try "E6 report".
+        * ALPS should return 0x00,0x00,0x0a or 0x00,0x00,0x64
+        */
+       param[0] = 0;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
+           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
+           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
+           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11))
+               return -1;
+
+       param[0] = param[1] = param[2] = 0xff;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+               return -1;
+
+       dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+
+       if (param[0] != 0x00 || param[1] != 0x00 || (param[2] != 0x0a && param[2] != 0x64))
+               return -1;
+
+       /* Now try "E7 report". ALPS should return 0x33 in byte 1 */
+       param[0] = 0;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
+           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
+           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
+           ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21))
+               return -1;
+
+       param[0] = param[1] = param[2] = 0xff;
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+               return -1;
+
+       dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+
+       for (i = 0; i < ARRAY_SIZE(alps_model_data); i++)
+               if (!memcmp(param, alps_model_data[i].signature, sizeof(alps_model_data[i].signature)))
+                       return alps_model_data[i].model;
+
+       return -1;
+}
+
+/*
+ * For DualPoint devices select the device that should respond to
+ * subsequent commands. It looks like glidepad is behind stickpointer,
+ * I'd thought it would be other way around...
+ */
+static int alps_passthrough_mode(struct psmouse *psmouse, int enable)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       unsigned char param[3];
+       int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
+
+       if (ps2_command(ps2dev, NULL, cmd) ||
+           ps2_command(ps2dev, NULL, cmd) ||
+           ps2_command(ps2dev, NULL, cmd) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
+               return -1;
+
+       /* we may get 3 more bytes, just ignore them */
+       ps2_command(ps2dev, param, 0x0300);
+
+       return 0;
+}
+
+static int alps_absolute_mode(struct psmouse *psmouse)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+       /* Try ALPS magic knock - 4 disable before enable */
+       if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
+               return -1;
+
+       /*
+        * Switch mouse to poll (remote) mode so motion data will not
+        * get in our way
+        */
+       return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
+}
+
+static int alps_get_status(struct psmouse *psmouse, char *param)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+       /* Get status: 0xF5 0xF5 0xF5 0xE9 */
+       if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+           ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+               return -1;
+
+       dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+
+       return 0;
+}
+
+/*
+ * Turn touchpad tapping on or off. The sequences are:
+ * 0xE9 0xF5 0xF5 0xF3 0x0A to enable,
+ * 0xE9 0xF5 0xF5 0xE8 0x00 to disable.
+ * My guess that 0xE9 (GetInfo) is here as a sync point.
+ * For models that also have stickpointer (DualPoints) its tapping
+ * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but
+ * we don't fiddle with it.
+ */
+static int alps_tap_mode(struct psmouse *psmouse, int enable)
+{
+       struct ps2dev *ps2dev = &psmouse->ps2dev;
+       int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES;
+       unsigned char tap_arg = enable ? 0x0A : 0x00;
+       unsigned char param[4];
+
+       if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+           ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+           ps2_command(ps2dev, &tap_arg, cmd))
+               return -1;
+
+       if (alps_get_status(psmouse, param))
+               return -1;
+
+       return 0;
+}
+
+static int alps_reconnect(struct psmouse *psmouse)
+{
+       int model;
+       unsigned char param[4];
+
+       if ((model = alps_get_model(psmouse)) < 0)
+               return -1;
+
+       if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1))
+               return -1;
+
+       if (alps_get_status(psmouse, param))
+               return -1;
+
+       if (param[0] & 0x04)
+               alps_tap_mode(psmouse, 0);
+
+       if (alps_absolute_mode(psmouse)) {
+               printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
+               return -1;
+       }
+
+       if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0))
+               return -1;
+
+       return 0;
+}
+
+static void alps_disconnect(struct psmouse *psmouse)
+{
+       psmouse_reset(psmouse);
+}
+
+int alps_init(struct psmouse *psmouse)
+{
+       unsigned char param[4];
+       int model;
+
+       if ((model = alps_get_model(psmouse)) < 0)
+               return -1;
+
+       printk(KERN_INFO "ALPS Touchpad (%s) detected\n",
+               model == ALPS_MODEL_GLIDEPOINT ? "Glidepoint" : "Dualpoint");
+
+       if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 1))
+               return -1;
+
+       if (alps_get_status(psmouse, param)) {
+               printk(KERN_ERR "alps.c: touchpad status report request failed\n");
+               return -1;
+       }
+
+       if (param[0] & 0x04) {
+               printk(KERN_INFO "  Disabling hardware tapping\n");
+               if (alps_tap_mode(psmouse, 0))
+                       printk(KERN_WARNING "alps.c: Failed to disable hardware tapping\n");
+       }
+
+       if (alps_absolute_mode(psmouse)) {
+               printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
+               return -1;
+       }
+
+       if (model == ALPS_MODEL_DUALPOINT && alps_passthrough_mode(psmouse, 0))
+               return -1;
+
+       psmouse->dev.evbit[LONG(EV_REL)] |= BIT(EV_REL);
+       psmouse->dev.relbit[LONG(REL_X)] |= BIT(REL_X);
+       psmouse->dev.relbit[LONG(REL_Y)] |= BIT(REL_Y);
+       psmouse->dev.keybit[LONG(BTN_A)] |= BIT(BTN_A);
+       psmouse->dev.keybit[LONG(BTN_B)] |= BIT(BTN_B);
+
+       psmouse->dev.evbit[LONG(EV_ABS)] |= BIT(EV_ABS);
+       input_set_abs_params(&psmouse->dev, ABS_X, 0, 1023, 0, 0);
+       input_set_abs_params(&psmouse->dev, ABS_Y, 0, 1023, 0, 0);
+       input_set_abs_params(&psmouse->dev, ABS_PRESSURE, 0, 127, 0, 0);
+
+       psmouse->dev.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
+       psmouse->dev.keybit[LONG(BTN_TOOL_FINGER)] |= BIT(BTN_TOOL_FINGER);
+       psmouse->dev.keybit[LONG(BTN_FORWARD)] |= BIT(BTN_FORWARD);
+       psmouse->dev.keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK);
+
+       psmouse->protocol_handler = alps_process_byte;
+       psmouse->disconnect = alps_disconnect;
+       psmouse->reconnect = alps_reconnect;
+       psmouse->pktsize = 6;
+
+       return 0;
+}
+
+int alps_detect(struct psmouse *psmouse, int set_properties)
+{
+       if (alps_get_model(psmouse) < 0)
+               return -1;
+
+       if (set_properties) {
+               psmouse->vendor = "ALPS";
+               psmouse->name = "TouchPad";
+       }
+       return 0;
+}
+
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
new file mode 100644 (file)
index 0000000..8efcec5
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * ALPS touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
+ *
+ * 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.
+ */
+
+#ifndef _ALPS_H
+#define _ALPS_H
+
+int alps_detect(struct psmouse *psmouse, int set_properties);
+int alps_init(struct psmouse *psmouse);
+
+#endif
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
new file mode 100644 (file)
index 0000000..c0a1974
--- /dev/null
@@ -0,0 +1,308 @@
+#ifndef _I8042_X86IA64IO_H
+#define _I8042_X86IA64IO_H
+
+/*
+ * 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.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "isa0060/serio0"
+#define I8042_AUX_PHYS_DESC "isa0060/serio1"
+#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#if defined(__ia64__)
+# define I8042_MAP_IRQ(x)      isa_irq_to_vector((x))
+#else
+# define I8042_MAP_IRQ(x)      (x)
+#endif
+
+#define I8042_KBD_IRQ  i8042_kbd_irq
+#define I8042_AUX_IRQ  i8042_aux_irq
+
+static int i8042_kbd_irq;
+static int i8042_aux_irq;
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG      i8042_command_reg
+#define I8042_STATUS_REG       i8042_command_reg
+#define I8042_DATA_REG         i8042_data_reg
+
+static int i8042_command_reg = 0x64;
+static int i8042_data_reg = 0x60;
+
+
+static inline int i8042_read_data(void)
+{
+       return inb(I8042_DATA_REG);
+}
+
+static inline int i8042_read_status(void)
+{
+       return inb(I8042_STATUS_REG);
+}
+
+static inline void i8042_write_data(int val)
+{
+       outb(val, I8042_DATA_REG);
+}
+
+static inline void i8042_write_command(int val)
+{
+       outb(val, I8042_COMMAND_REG);
+}
+
+#if defined(__i386__)
+
+#include <linux/dmi.h>
+
+static struct dmi_system_id __initdata i8042_dmi_table[] = {
+       {
+               .ident = "Compaq Proliant 8500",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
+                       DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "8500"),
+               },
+       },
+       {
+               .ident = "Compaq Proliant DL760",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
+                       DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"),
+               },
+       },
+       { }
+};
+#endif
+
+#if defined(__ia64__) && defined(CONFIG_ACPI)
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+
+struct i8042_acpi_resources {
+       unsigned int port1;
+       unsigned int port2;
+       unsigned int irq;
+};
+
+static int i8042_acpi_kbd_registered;
+static int i8042_acpi_aux_registered;
+
+static acpi_status i8042_acpi_parse_resource(struct acpi_resource *res, void *data)
+{
+       struct i8042_acpi_resources *i8042_res = data;
+       struct acpi_resource_io *io;
+       struct acpi_resource_fixed_io *fixed_io;
+       struct acpi_resource_irq *irq;
+       struct acpi_resource_ext_irq *ext_irq;
+
+       switch (res->id) {
+               case ACPI_RSTYPE_IO:
+                       io = &res->data.io;
+                       if (io->range_length) {
+                               if (!i8042_res->port1)
+                                       i8042_res->port1 = io->min_base_address;
+                               else
+                                       i8042_res->port2 = io->min_base_address;
+                       }
+                       break;
+
+               case ACPI_RSTYPE_FIXED_IO:
+                       fixed_io = &res->data.fixed_io;
+                       if (fixed_io->range_length) {
+                               if (!i8042_res->port1)
+                                       i8042_res->port1 = fixed_io->base_address;
+                               else
+                                       i8042_res->port2 = fixed_io->base_address;
+                       }
+                       break;
+
+               case ACPI_RSTYPE_IRQ:
+                       irq = &res->data.irq;
+                       if (irq->number_of_interrupts > 0)
+                               i8042_res->irq =
+                                       acpi_register_gsi(irq->interrupts[0],
+                                                         irq->edge_level,
+                                                         irq->active_high_low);
+                       break;
+
+               case ACPI_RSTYPE_EXT_IRQ:
+                       ext_irq = &res->data.extended_irq;
+                       if (ext_irq->number_of_interrupts > 0)
+                               i8042_res->irq =
+                                       acpi_register_gsi(ext_irq->interrupts[0],
+                                                         ext_irq->edge_level,
+                                                         ext_irq->active_high_low);
+                       break;
+       }
+       return AE_OK;
+}
+
+static int i8042_acpi_kbd_add(struct acpi_device *device)
+{
+       struct i8042_acpi_resources kbd_res;
+       acpi_status status;
+
+       memset(&kbd_res, 0, sizeof(kbd_res));
+       status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
+                                    i8042_acpi_parse_resource, &kbd_res);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       if (kbd_res.port1)
+               i8042_data_reg = kbd_res.port1;
+       else
+               printk(KERN_WARNING "ACPI: [%s] has no data port; default is 0x%x\n",
+                       acpi_device_bid(device), i8042_data_reg);
+
+       if (kbd_res.port2)
+               i8042_command_reg = kbd_res.port2;
+       else
+               printk(KERN_WARNING "ACPI: [%s] has no command port; default is 0x%x\n",
+                       acpi_device_bid(device), i8042_command_reg);
+
+       if (kbd_res.irq)
+               i8042_kbd_irq = kbd_res.irq;
+       else
+               printk(KERN_WARNING "ACPI: [%s] has no IRQ; default is %d\n",
+                       acpi_device_bid(device), i8042_kbd_irq);
+
+       strncpy(acpi_device_name(device), "PS/2 Keyboard Controller",
+               sizeof(acpi_device_name(device)));
+       printk("ACPI: %s [%s] at I/O 0x%x, 0x%x, irq %d\n",
+               acpi_device_name(device), acpi_device_bid(device),
+               i8042_data_reg, i8042_command_reg, i8042_kbd_irq);
+
+       return 0;
+}
+
+static int i8042_acpi_aux_add(struct acpi_device *device)
+{
+       struct i8042_acpi_resources aux_res;
+       acpi_status status;
+
+       memset(&aux_res, 0, sizeof(aux_res));
+       status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
+                                    i8042_acpi_parse_resource, &aux_res);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       if (aux_res.irq)
+               i8042_aux_irq = aux_res.irq;
+       else
+               printk(KERN_WARNING "ACPI: [%s] has no IRQ; default is %d\n",
+                       acpi_device_bid(device), i8042_aux_irq);
+
+       strncpy(acpi_device_name(device), "PS/2 Mouse Controller",
+               sizeof(acpi_device_name(device)));
+       printk("ACPI: %s [%s] at irq %d\n",
+               acpi_device_name(device), acpi_device_bid(device), i8042_aux_irq);
+
+       return 0;
+}
+
+static struct acpi_driver i8042_acpi_kbd_driver = {
+       .name           = "i8042",
+       .ids            = "PNP0303,PNP030B",
+       .ops            = {
+               .add            = i8042_acpi_kbd_add,
+       },
+};
+
+static struct acpi_driver i8042_acpi_aux_driver = {
+       .name           = "i8042",
+       .ids            = "PNP0F03,PNP0F0B,PNP0F0E,PNP0F12,PNP0F13,SYN0801",
+       .ops            = {
+               .add            = i8042_acpi_aux_add,
+       },
+};
+
+static int i8042_acpi_init(void)
+{
+       int result;
+
+       if (acpi_disabled || i8042_noacpi) {
+               printk("i8042: ACPI detection disabled\n");
+               return 0;
+       }
+
+       result = acpi_bus_register_driver(&i8042_acpi_kbd_driver);
+       if (result < 0)
+               return result;
+
+       if (result == 0) {
+               acpi_bus_unregister_driver(&i8042_acpi_kbd_driver);
+               return -ENODEV;
+       }
+       i8042_acpi_kbd_registered = 1;
+
+       result = acpi_bus_register_driver(&i8042_acpi_aux_driver);
+       if (result >= 0)
+               i8042_acpi_aux_registered = 1;
+       if (result == 0)
+               i8042_noaux = 1;
+
+       return 0;
+}
+
+static void i8042_acpi_exit(void)
+{
+       if (i8042_acpi_kbd_registered)
+               acpi_bus_unregister_driver(&i8042_acpi_kbd_driver);
+
+       if (i8042_acpi_aux_registered)
+               acpi_bus_unregister_driver(&i8042_acpi_aux_driver);
+}
+#endif
+
+static inline int i8042_platform_init(void)
+{
+/*
+ * On ix86 platforms touching the i8042 data register region can do really
+ * bad things. Because of this the region is always reserved on ix86 boxes.
+ *
+ *     if (!request_region(I8042_DATA_REG, 16, "i8042"))
+ *             return -1;
+ */
+
+       i8042_kbd_irq = I8042_MAP_IRQ(1);
+       i8042_aux_irq = I8042_MAP_IRQ(12);
+
+#if defined(__ia64__) && defined(CONFIG_ACPI)
+       if (i8042_acpi_init())
+               return -1;
+#endif
+
+#if defined(__ia64__)
+        i8042_reset = 1;
+#endif
+
+#if defined(__i386__)
+       if (dmi_check_system(i8042_dmi_table))
+               i8042_noloop = 1;
+#endif
+
+       return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+#if defined(__ia64__) && defined(CONFIG_ACPI)
+       i8042_acpi_exit();
+#endif
+}
+
+#endif /* _I8042_X86IA64IO_H */
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
new file mode 100644 (file)
index 0000000..c978657
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * PS/2 driver library
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2004 Dmitry Torokhov
+ */
+
+/*
+ * 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 <linux/delay.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/libps2.h>
+
+#define DRIVER_DESC    "PS/2 driver library"
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION("PS/2 driver library");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ps2_init);
+EXPORT_SYMBOL(ps2_sendbyte);
+EXPORT_SYMBOL(ps2_command);
+EXPORT_SYMBOL(ps2_schedule_command);
+EXPORT_SYMBOL(ps2_handle_ack);
+EXPORT_SYMBOL(ps2_handle_response);
+EXPORT_SYMBOL(ps2_cmd_aborted);
+
+/* Work structure to schedule execution of a command */
+struct ps2work {
+       struct work_struct work;
+       struct ps2dev *ps2dev;
+       int command;
+       unsigned char param[0];
+};
+
+
+/*
+ * ps2_sendbyte() sends a byte to the mouse, and waits for acknowledge.
+ * It doesn't handle retransmission, though it could - because when there would
+ * be need for retransmissions, the mouse has to be replaced anyway.
+ *
+ * ps2_sendbyte() can only be called from a process context
+ */
+
+int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
+{
+       serio_pause_rx(ps2dev->serio);
+       ps2dev->nak = 1;
+       ps2dev->flags |= PS2_FLAG_ACK;
+       serio_continue_rx(ps2dev->serio);
+
+       if (serio_write(ps2dev->serio, byte) == 0)
+               wait_event_timeout(ps2dev->wait,
+                                  !(ps2dev->flags & PS2_FLAG_ACK),
+                                  msecs_to_jiffies(timeout));
+
+       serio_pause_rx(ps2dev->serio);
+       ps2dev->flags &= ~PS2_FLAG_ACK;
+       serio_continue_rx(ps2dev->serio);
+
+       return -ps2dev->nak;
+}
+
+/*
+ * ps2_command() sends a command and its parameters to the mouse,
+ * then waits for the response and puts it in the param array.
+ *
+ * ps2_command() can only be called from a process context
+ */
+
+int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+{
+       int timeout;
+       int send = (command >> 12) & 0xf;
+       int receive = (command >> 8) & 0xf;
+       int rc = -1;
+       int i;
+
+       down(&ps2dev->cmd_sem);
+
+       serio_pause_rx(ps2dev->serio);
+       ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
+       ps2dev->cmdcnt = receive;
+       if (receive && param)
+               for (i = 0; i < receive; i++)
+                       ps2dev->cmdbuf[(receive - 1) - i] = param[i];
+       serio_continue_rx(ps2dev->serio);
+
+       /*
+        * Some devices (Synaptics) peform the reset before
+        * ACKing the reset command, and so it can take a long
+        * time before the ACK arrrives.
+        */
+       if (command & 0xff)
+               if (ps2_sendbyte(ps2dev, command & 0xff,
+                       command == PS2_CMD_RESET_BAT ? 1000 : 200))
+                       goto out;
+
+       for (i = 0; i < send; i++)
+               if (ps2_sendbyte(ps2dev, param[i], 200))
+                       goto out;
+
+       /*
+        * The reset command takes a long time to execute.
+        */
+       timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500);
+
+       timeout = wait_event_timeout(ps2dev->wait,
+                                    !(ps2dev->flags & PS2_FLAG_CMD1), timeout);
+
+       if (ps2dev->cmdcnt && timeout > 0) {
+
+               if (command == PS2_CMD_RESET_BAT && timeout > msecs_to_jiffies(100)) {
+                       /*
+                        * Device has sent the first response byte
+                        * after a reset command, reset is thus done,
+                        * shorten the timeout. The next byte will come
+                        * soon (keyboard) or not at all (mouse).
+                        */
+                       timeout = msecs_to_jiffies(100);
+               }
+
+               if (command == PS2_CMD_GETID &&
+                   ps2dev->cmdbuf[receive - 1] != 0xab && /* Regular keyboards */
+                   ps2dev->cmdbuf[receive - 1] != 0xac && /* NCD Sun keyboard */
+                   ps2dev->cmdbuf[receive - 1] != 0x2b && /* Trust keyboard, translated */
+                   ps2dev->cmdbuf[receive - 1] != 0x5d && /* Trust keyboard */
+                   ps2dev->cmdbuf[receive - 1] != 0x60 && /* NMB SGI keyboard, translated */
+                   ps2dev->cmdbuf[receive - 1] != 0x47) { /* NMB SGI keyboard */
+                       /*
+                        * Device behind the port is not a keyboard
+                        * so we don't need to wait for the 2nd byte
+                        * of ID response.
+                        */
+                       serio_pause_rx(ps2dev->serio);
+                       ps2dev->flags = ps2dev->cmdcnt = 0;
+                       serio_continue_rx(ps2dev->serio);
+               }
+
+               wait_event_timeout(ps2dev->wait,
+                                  !(ps2dev->flags & PS2_FLAG_CMD), timeout);
+       }
+
+       if (param)
+               for (i = 0; i < receive; i++)
+                       param[i] = ps2dev->cmdbuf[(receive - 1) - i];
+
+       if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1))
+               goto out;
+
+       rc = 0;
+
+out:
+       serio_pause_rx(ps2dev->serio);
+       ps2dev->flags = 0;
+       serio_continue_rx(ps2dev->serio);
+
+       up(&ps2dev->cmd_sem);
+       return rc;
+}
+
+/*
+ * ps2_execute_scheduled_command() sends a command, previously scheduled by
+ * ps2_schedule_command(), to a PS/2 device (keyboard, mouse, etc.)
+ */
+
+static void ps2_execute_scheduled_command(void *data)
+{
+       struct ps2work *ps2work = data;
+
+       ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command);
+       kfree(ps2work);
+}
+
+/*
+ * ps2_schedule_command() allows to schedule delayed execution of a PS/2
+ * command and can be used to issue a command from an interrupt or softirq
+ * context.
+ */
+
+int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+{
+       struct ps2work *ps2work;
+       int send = (command >> 12) & 0xf;
+       int receive = (command >> 8) & 0xf;
+
+       if (!(ps2work = kmalloc(sizeof(struct ps2work) + max(send, receive), GFP_ATOMIC)))
+               return -1;
+
+       memset(ps2work, 0, sizeof(struct ps2work));
+       ps2work->ps2dev = ps2dev;
+       ps2work->command = command;
+       memcpy(ps2work->param, param, send);
+       INIT_WORK(&ps2work->work, ps2_execute_scheduled_command, ps2work);
+
+       if (!schedule_work(&ps2work->work)) {
+               kfree(ps2work);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * ps2_init() initializes ps2dev structure
+ */
+
+void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
+{
+       init_MUTEX(&ps2dev->cmd_sem);
+       init_waitqueue_head(&ps2dev->wait);
+       ps2dev->serio = serio;
+}
+
+/*
+ * ps2_handle_ack() is supposed to be used in interrupt handler
+ * to properly process ACK/NAK of a command from a PS/2 device.
+ */
+
+int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
+{
+       switch (data) {
+               case PS2_RET_ACK:
+                       ps2dev->nak = 0;
+                       break;
+
+               case PS2_RET_NAK:
+                       ps2dev->nak = 1;
+                       break;
+
+               /*
+                * Workaround for mice which don't ACK the Get ID command.
+                * These are valid mouse IDs that we recognize.
+                */
+               case 0x00:
+               case 0x03:
+               case 0x04:
+                       if (ps2dev->flags & PS2_FLAG_WAITID) {
+                               ps2dev->nak = 0;
+                               break;
+                       }
+                       /* Fall through */
+               default:
+                       return 0;
+       }
+
+
+       if (!ps2dev->nak && ps2dev->cmdcnt)
+               ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
+
+       ps2dev->flags &= ~PS2_FLAG_ACK;
+       wake_up(&ps2dev->wait);
+
+       if (data != PS2_RET_ACK)
+               ps2_handle_response(ps2dev, data);
+
+       return 1;
+}
+
+/*
+ * ps2_handle_response() is supposed to be used in interrupt handler
+ * to properly store device's response to a command and notify process
+ * waiting for completion of the command.
+ */
+
+int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
+{
+       if (ps2dev->cmdcnt)
+               ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;
+
+       if (ps2dev->flags & PS2_FLAG_CMD1) {
+               ps2dev->flags &= ~PS2_FLAG_CMD1;
+               if (ps2dev->cmdcnt)
+                       wake_up(&ps2dev->wait);
+       }
+
+       if (!ps2dev->cmdcnt) {
+               ps2dev->flags &= ~PS2_FLAG_CMD;
+               wake_up(&ps2dev->wait);
+       }
+
+       return 1;
+}
+
+void ps2_cmd_aborted(struct ps2dev *ps2dev)
+{
+       if (ps2dev->flags & PS2_FLAG_ACK)
+               ps2dev->nak = 1;
+
+       if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
+               wake_up(&ps2dev->wait);
+
+       ps2dev->flags = 0;
+}
+
diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c
new file mode 100644 (file)
index 0000000..0248f8e
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * faulty.c : Multiple Devices driver for Linux
+ *
+ * Copyright (C) 2004 Neil Brown
+ *
+ * fautly-device-simulator personality for md
+ *
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example /usr/src/linux/COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+/*
+ * The "faulty" personality causes some requests to fail.
+ *
+ * Possible failure modes are:
+ *   reads fail "randomly" but succeed on retry
+ *   writes fail "randomly" but succeed on retry
+ *   reads for some address fail and then persist until a write
+ *   reads for some address fail and then persist irrespective of write
+ *   writes for some address fail and persist
+ *   all writes fail
+ *
+ * Different modes can be active at a time, but only
+ * one can be set at array creation.  Others can be added later.
+ * A mode can be one-shot or recurrent with the recurrance being
+ * once in every N requests.
+ * The bottom 5 bits of the "layout" indicate the mode.  The
+ * remainder indicate a period, or 0 for one-shot.
+ *
+ * There is an implementation limit on the number of concurrently
+ * persisting-faulty blocks. When a new fault is requested that would
+ * exceed the limit, it is ignored.
+ * All current faults can be clear using a layout of "0".
+ *
+ * Requests are always sent to the device.  If they are to fail,
+ * we clone the bio and insert a new b_end_io into the chain.
+ */
+
+#define        WriteTransient  0
+#define        ReadTransient   1
+#define        WritePersistent 2
+#define        ReadPersistent  3
+#define        WriteAll        4 /* doesn't go to device */
+#define        ReadFixable     5
+#define        Modes   6
+
+#define        ClearErrors     31
+#define        ClearFaults     30
+
+#define AllPersist     100 /* internal use only */
+#define        NoPersist       101
+
+#define        ModeMask        0x1f
+#define        ModeShift       5
+
+#define MaxFault       50
+#include <linux/raid/md.h>
+
+
+static int faulty_fail(struct bio *bio, unsigned int bytes_done, int error)
+{
+       struct bio *b = bio->bi_private;
+
+       b->bi_size = bio->bi_size;
+       b->bi_sector = bio->bi_sector;
+
+       if (bio->bi_size == 0)
+               bio_put(bio);
+
+       clear_bit(BIO_UPTODATE, &b->bi_flags);
+       return (b->bi_end_io)(b, bytes_done, -EIO);
+}
+
+typedef struct faulty_conf {
+       int period[Modes];
+       atomic_t counters[Modes];
+       sector_t faults[MaxFault];
+       int     modes[MaxFault];
+       int nfaults;
+       mdk_rdev_t *rdev;
+} conf_t;
+
+static int check_mode(conf_t *conf, int mode)
+{
+       if (conf->period[mode] == 0 &&
+           atomic_read(&conf->counters[mode]) <= 0)
+               return 0; /* no failure, no decrement */
+
+
+       if (atomic_dec_and_test(&conf->counters[mode])) {
+               if (conf->period[mode])
+                       atomic_set(&conf->counters[mode], conf->period[mode]);
+               return 1;
+       }
+       return 0;
+}
+
+static int check_sector(conf_t *conf, sector_t start, sector_t end, int dir)
+{
+       /* If we find a ReadFixable sector, we fix it ... */
+       int i;
+       for (i=0; i<conf->nfaults; i++)
+               if (conf->faults[i] >= start &&
+                   conf->faults[i] < end) {
+                       /* found it ... */
+                       switch (conf->modes[i] * 2 + dir) {
+                       case WritePersistent*2+WRITE: return 1;
+                       case ReadPersistent*2+READ: return 1;
+                       case ReadFixable*2+READ: return 1;
+                       case ReadFixable*2+WRITE:
+                               conf->modes[i] = NoPersist;
+                               return 0;
+                       case AllPersist*2+READ:
+                       case AllPersist*2+WRITE: return 1;
+                       default:
+                               return 0;
+                       }
+               }
+       return 0;
+}
+
+static void add_sector(conf_t *conf, sector_t start, int mode)
+{
+       int i;
+       int n = conf->nfaults;
+       for (i=0; i<conf->nfaults; i++)
+               if (conf->faults[i] == start) {
+                       switch(mode) {
+                       case NoPersist: conf->modes[i] = mode; return;
+                       case WritePersistent:
+                               if (conf->modes[i] == ReadPersistent ||
+                                   conf->modes[i] == ReadFixable)
+                                       conf->modes[i] = AllPersist;
+                               else
+                                       conf->modes[i] = WritePersistent;
+                               return;
+                       case ReadPersistent:
+                               if (conf->modes[i] == WritePersistent)
+                                       conf->modes[i] = AllPersist;
+                               else
+                                       conf->modes[i] = ReadPersistent;
+                               return;
+                       case ReadFixable:
+                               if (conf->modes[i] == WritePersistent ||
+                                   conf->modes[i] == ReadPersistent)
+                                       conf->modes[i] = AllPersist;
+                               else
+                                       conf->modes[i] = ReadFixable;
+                               return;
+                       }
+               } else if (conf->modes[i] == NoPersist)
+                       n = i;
+
+       if (n >= MaxFault)
+               return;
+       conf->faults[n] = start;
+       conf->modes[n] = mode;
+       if (conf->nfaults == n)
+               conf->nfaults = n+1;
+}
+
+static int make_request(request_queue_t *q, struct bio *bio)
+{
+       mddev_t *mddev = q->queuedata;
+       conf_t *conf = (conf_t*)mddev->private;
+       int failit = 0;
+
+       if (bio->bi_rw & 1) {
+               /* write request */
+               if (atomic_read(&conf->counters[WriteAll])) {
+                       /* special case - don't decrement, don't generic_make_request,
+                        * just fail immediately
+                        */
+                       bio_endio(bio, bio->bi_size, -EIO);
+                       return 0;
+               }
+
+               if (check_sector(conf, bio->bi_sector, bio->bi_sector+(bio->bi_size>>9),
+                                WRITE))
+                       failit = 1;
+               if (check_mode(conf, WritePersistent)) {
+                       add_sector(conf, bio->bi_sector, WritePersistent);
+                       failit = 1;
+               }
+               if (check_mode(conf, WriteTransient))
+                       failit = 1;
+       } else {
+               /* read request */
+               if (check_sector(conf, bio->bi_sector, bio->bi_sector + (bio->bi_size>>9),
+                                READ))
+                       failit = 1;
+               if (check_mode(conf, ReadTransient))
+                       failit = 1;
+               if (check_mode(conf, ReadPersistent)) {
+                       add_sector(conf, bio->bi_sector, ReadPersistent);
+                       failit = 1;
+               }
+               if (check_mode(conf, ReadFixable)) {
+                       add_sector(conf, bio->bi_sector, ReadFixable);
+                       failit = 1;
+               }
+       }
+       if (failit) {
+               struct bio *b = bio_clone(bio, GFP_NOIO);
+               b->bi_bdev = conf->rdev->bdev;
+               b->bi_private = bio;
+               b->bi_end_io = faulty_fail;
+               generic_make_request(b);
+               return 0;
+       } else {
+               bio->bi_bdev = conf->rdev->bdev;
+               return 1;
+       }
+}
+
+static void status(struct seq_file *seq, mddev_t *mddev)
+{
+       conf_t *conf = (conf_t*)mddev->private;
+       int n;
+
+       if ((n=atomic_read(&conf->counters[WriteTransient])) != 0)
+               seq_printf(seq, " WriteTransient=%d(%d)",
+                          n, conf->period[WriteTransient]);
+
+       if ((n=atomic_read(&conf->counters[ReadTransient])) != 0)
+               seq_printf(seq, " ReadTransient=%d(%d)",
+                          n, conf->period[ReadTransient]);
+
+       if ((n=atomic_read(&conf->counters[WritePersistent])) != 0)
+               seq_printf(seq, " WritePersistent=%d(%d)",
+                          n, conf->period[WritePersistent]);
+
+       if ((n=atomic_read(&conf->counters[ReadPersistent])) != 0)
+               seq_printf(seq, " ReadPersistent=%d(%d)",
+                          n, conf->period[ReadPersistent]);
+
+
+       if ((n=atomic_read(&conf->counters[ReadFixable])) != 0)
+               seq_printf(seq, " ReadFixable=%d(%d)",
+                          n, conf->period[ReadFixable]);
+
+       if ((n=atomic_read(&conf->counters[WriteAll])) != 0)
+               seq_printf(seq, " WriteAll");
+
+       seq_printf(seq, " nfaults=%d", conf->nfaults);
+}
+
+
+static int reconfig(mddev_t *mddev, int layout, int chunk_size)
+{
+       int mode = layout & ModeMask;
+       int count = layout >> ModeShift;
+       conf_t *conf = mddev->private;
+
+       if (chunk_size != -1)
+               return -EINVAL;
+
+       /* new layout */
+       if (mode == ClearFaults)
+               conf->nfaults = 0;
+       else if (mode == ClearErrors) {
+               int i;
+               for (i=0 ; i < Modes ; i++) {
+                       conf->period[i] = 0;
+                       atomic_set(&conf->counters[i], 0);
+               }
+       } else if (mode < Modes) {
+               conf->period[mode] = count;
+               if (!count) count++;
+               atomic_set(&conf->counters[mode], count);
+       } else
+               return -EINVAL;
+       mddev->layout = -1; /* makes sure further changes come through */
+       return 0;
+}
+
+static int run(mddev_t *mddev)
+{
+       mdk_rdev_t *rdev;
+       struct list_head *tmp;
+       int i;
+
+       conf_t *conf = kmalloc(sizeof(*conf), GFP_KERNEL);
+
+       for (i=0; i<Modes; i++) {
+               atomic_set(&conf->counters[i], 0);
+               conf->period[i] = 0;
+       }
+       conf->nfaults = 0;
+
+       ITERATE_RDEV(mddev, rdev, tmp)
+               conf->rdev = rdev;
+
+       mddev->array_size = mddev->size;
+       mddev->private = conf;
+
+       reconfig(mddev, mddev->layout, -1);
+
+       return 0;
+}
+
+static int stop(mddev_t *mddev)
+{
+       conf_t *conf = (conf_t *)mddev->private;
+
+       kfree(conf);
+       mddev->private = NULL;
+       return 0;
+}
+
+static mdk_personality_t faulty_personality =
+{
+       .name           = "faulty",
+       .owner          = THIS_MODULE,
+       .make_request   = make_request,
+       .run            = run,
+       .stop           = stop,
+       .status         = status,
+       .reconfig       = reconfig,
+};
+
+static int __init raid_init(void)
+{
+       return register_md_personality(FAULTY, &faulty_personality);
+}
+
+static void raid_exit(void)
+{
+       unregister_md_personality(FAULTY);
+}
+
+module_init(raid_init);
+module_exit(raid_exit);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("md-personality-10"); /* faulty */
diff --git a/drivers/md/raid6altivec.uc b/drivers/md/raid6altivec.uc
new file mode 100644 (file)
index 0000000..f4ff644
--- /dev/null
@@ -0,0 +1,126 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ *   Copyright 2002-2004 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330,
+ *   Bostom MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * raid6altivec$#.c
+ *
+ * $#-way unrolled portable integer math RAID-6 instruction set
+ *
+ * This file is postprocessed using unroll.pl
+ *
+ * <benh> hpa: in process,
+ * you can just "steal" the vec unit with enable_kernel_altivec() (but
+ * bracked this with preempt_disable/enable or in a lock)
+ */
+
+#include "raid6.h"
+
+#ifdef CONFIG_ALTIVEC
+
+#include <altivec.h>
+#include <asm/system.h>
+#include <asm/cputable.h>
+
+/*
+ * This is the C data type to use
+ */
+
+typedef vector unsigned char unative_t;
+
+#define NBYTES(x) ((vector unsigned char) {x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x})
+#define NSIZE  sizeof(unative_t)
+
+/*
+ * The SHLBYTE() operation shifts each byte left by 1, *not*
+ * rolling over into the next byte
+ */
+static inline __attribute_const__ unative_t SHLBYTE(unative_t v)
+{
+       return vec_add(v,v);
+}
+
+/*
+ * The MASK() operation returns 0xFF in any byte for which the high
+ * bit is 1, 0x00 for any byte for which the high bit is 0.
+ */
+static inline __attribute_const__ unative_t MASK(unative_t v)
+{
+       unative_t zv = NBYTES(0);
+
+       /* vec_cmpgt returns a vector bool char; thus the need for the cast */
+       return (unative_t)vec_cmpgt(zv, v);
+}
+
+
+/* This is noinline to make damned sure that gcc doesn't move any of the
+   Altivec code around the enable/disable code */
+static void noinline
+raid6_altivec$#_gen_syndrome_real(int disks, size_t bytes, void **ptrs)
+{
+       u8 **dptr = (u8 **)ptrs;
+       u8 *p, *q;
+       int d, z, z0;
+
+       unative_t wd$$, wq$$, wp$$, w1$$, w2$$;
+       unative_t x1d = NBYTES(0x1d);
+
+       z0 = disks - 3;         /* Highest data disk */
+       p = dptr[z0+1];         /* XOR parity */
+       q = dptr[z0+2];         /* RS syndrome */
+
+       for ( d = 0 ; d < bytes ; d += NSIZE*$# ) {
+               wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE];
+               for ( z = z0-1 ; z >= 0 ; z-- ) {
+                       wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE];
+                       wp$$ = vec_xor(wp$$, wd$$);
+                       w2$$ = MASK(wq$$);
+                       w1$$ = SHLBYTE(wq$$);
+                       w2$$ = vec_and(w2$$, x1d);
+                       w1$$ = vec_xor(w1$$, w2$$);
+                       wq$$ = vec_xor(w1$$, wd$$);
+               }
+               *(unative_t *)&p[d+NSIZE*$$] = wp$$;
+               *(unative_t *)&q[d+NSIZE*$$] = wq$$;
+       }
+}
+
+static void raid6_altivec$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+       preempt_disable();
+       enable_kernel_altivec();
+
+       raid6_altivec$#_gen_syndrome_real(disks, bytes, ptrs);
+
+       preempt_enable();
+}
+
+int raid6_have_altivec(void);
+#if $# == 1
+int raid6_have_altivec(void)
+{
+       /* This assumes either all CPUs have Altivec or none does */
+#ifdef CONFIG_PPC64
+       return cur_cpu_spec->cpu_features & CPU_FTR_ALTIVEC;
+#else
+       return cur_cpu_spec[0]->cpu_features & CPU_FTR_ALTIVEC;
+#endif
+}
+#endif
+
+const struct raid6_calls raid6_altivec$# = {
+       raid6_altivec$#_gen_syndrome,
+       raid6_have_altivec,
+       "altivecx$#",
+       0
+};
+
+#endif /* CONFIG_ALTIVEC */
diff --git a/drivers/media/dvb/b2c2/b2c2-common.c b/drivers/media/dvb/b2c2/b2c2-common.c
new file mode 100644 (file)
index 0000000..cb42d44
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * b2c2-common.c - common methods for the B2C2/Technisat SkyStar2 PCI DVB card and
+ *                 for the B2C2/Technisat Sky/Cable/AirStar USB devices
+ *                 based on the FlexCopII/FlexCopIII by B2C2, Inc.
+ *
+ * Copyright (C) 2003  Vadim Catana, skystar@moldova.cc
+ *
+ * FIX: DISEQC Tone Burst in flexcop_diseqc_ioctl()
+ * FIX: FULL soft DiSEqC for skystar2 (FlexCopII rev 130) VP310 equipped
+ *     Vincenzo Di Massa, hawk.it at tiscalinet.it
+ *
+ * Converted to Linux coding style
+ * Misc reorganization, polishing, restyling
+ *     Roberto Ragusa, r.ragusa at libero.it
+ *
+ * Added hardware filtering support,
+ *     Niklas Peinecke, peinecke at gdv.uni-hannover.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+#include "stv0299.h"
+#include "mt352.h"
+#include "mt312.h"
+
+static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
+{
+       u8 aclk = 0;
+       u8 bclk = 0;
+
+       if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
+       else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
+       else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
+       else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
+       else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
+       else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
+
+       stv0299_writereg (fe, 0x13, aclk);
+       stv0299_writereg (fe, 0x14, bclk);
+       stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
+       stv0299_writereg (fe, 0x20, (ratio >>  8) & 0xff);
+       stv0299_writereg (fe, 0x21, (ratio      ) & 0xf0);
+
+       return 0;
+}
+
+static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+       u8 buf[4];
+       u32 div;
+       struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+//     struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+       div = params->frequency / 125;
+
+       buf[0] = (div >> 8) & 0x7f;
+       buf[1] = div & 0xff;
+       buf[2] = 0x84;  // 0xC4
+       buf[3] = 0x08;
+
+       if (params->frequency < 1500000) buf[3] |= 0x10;
+
+//     if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
+       return 0;
+}
+
+static u8 samsung_tbmu24112_inittab[] = {
+            0x01, 0x15,
+            0x02, 0x30,
+            0x03, 0x00,
+            0x04, 0x7D,
+            0x05, 0x35,
+            0x06, 0x02,
+            0x07, 0x00,
+            0x08, 0xC3,
+            0x0C, 0x00,
+            0x0D, 0x81,
+            0x0E, 0x23,
+            0x0F, 0x12,
+            0x10, 0x7E,
+            0x11, 0x84,
+            0x12, 0xB9,
+            0x13, 0x88,
+            0x14, 0x89,
+            0x15, 0xC9,
+            0x16, 0x00,
+            0x17, 0x5C,
+            0x18, 0x00,
+            0x19, 0x00,
+            0x1A, 0x00,
+            0x1C, 0x00,
+            0x1D, 0x00,
+            0x1E, 0x00,
+            0x1F, 0x3A,
+            0x20, 0x2E,
+            0x21, 0x80,
+            0x22, 0xFF,
+            0x23, 0xC1,
+            0x28, 0x00,
+            0x29, 0x1E,
+            0x2A, 0x14,
+            0x2B, 0x0F,
+            0x2C, 0x09,
+            0x2D, 0x05,
+            0x31, 0x1F,
+            0x32, 0x19,
+            0x33, 0xFE,
+            0x34, 0x93,
+            0xff, 0xff,
+};
+
+static struct stv0299_config samsung_tbmu24112_config = {
+       .demod_address = 0x68,
+       .inittab = samsung_tbmu24112_inittab,
+       .mclk = 88000000UL,
+       .invert = 0,
+       .enhanced_tuning = 0,
+       .skip_reinit = 0,
+       .lock_output = STV0229_LOCKOUTPUT_LK,
+       .volt13_op0_op1 = STV0299_VOLT13_OP1,
+       .min_delay_ms = 100,
+       .set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
+       .pll_set = samsung_tbmu24112_pll_set,
+};
+
+
+
+
+
+static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
+{
+       static u8 mt352_clock_config [] = { 0x89, 0x10, 0x2d };
+       static u8 mt352_reset [] = { 0x50, 0x80 };
+       static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+       static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
+       static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+       mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+       udelay(2000);
+       mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+       mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+
+       mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
+       mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+       return 0;
+}
+
+int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
+{
+       u32 div;
+       unsigned char bs = 0;
+
+       #define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
+       div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+       if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
+       if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
+       if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
+
+       pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address
+       pllbuf[1] = div >> 8;
+       pllbuf[2] = div & 0xff;
+       pllbuf[3] = 0xcc;
+       pllbuf[4] = bs;
+
+       return 0;
+}
+
+static struct mt352_config samsung_tdtc9251dh0_config = {
+
+       .demod_address = 0x0f,
+       .demod_init = samsung_tdtc9251dh0_demod_init,
+       .pll_set = samsung_tdtc9251dh0_pll_set,
+};
+
+static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+       u8 buf[4];
+       u32 div;
+       struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+//     struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+       div = (params->frequency + (125/2)) / 125;
+
+       buf[0] = (div >> 8) & 0x7f;
+       buf[1] = (div >> 0) & 0xff;
+       buf[2] = 0x84 | ((div >> 10) & 0x60);
+       buf[3] = 0x80;
+
+       if (params->frequency < 1550000)
+               buf[3] |= 0x02;
+
+       //if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
+       return 0;
+}
+
+static struct mt312_config skystar23_samsung_tbdu18132_config = {
+
+       .demod_address = 0x0e,
+       .pll_set = skystar23_samsung_tbdu18132_pll_set,
+};
diff --git a/drivers/media/dvb/b2c2/b2c2-usb-core.c b/drivers/media/dvb/b2c2/b2c2-usb-core.c
new file mode 100644 (file)
index 0000000..d46c8c0
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2004 Patrick Boettcher <patrick.boettcher@desy.de>,
+ *                    Luca Bertagnolio <>,
+ *
+ * based on information provided by John Jurrius from BBTI, 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, version 2.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_filter.h"
+#include "dvb_net.h"
+#include "dvb_frontend.h"
+
+/* debug */
+#define dprintk(level,args...) \
+           do { if ((debug & level)) { printk(args); } } while (0)
+#define debug_dump(b,l) if (debug) {\
+       int i; deb_xfer("%s: %d > ",__FUNCTION__,l); \
+       for (i = 0; i < l; i++) deb_xfer("%02x ", b[i]); \
+       deb_xfer("\n");\
+}
+
+static int debug;
+module_param(debug, int, 0x644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4 (or-able)).");
+
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_ts(args...)   dprintk(0x02,args)
+#define deb_ctrl(args...) dprintk(0x04,args)
+
+/* Version information */
+#define DRIVER_VERSION "0.0"
+#define DRIVER_DESC "Driver for B2C2/Technisat Air/Cable/Sky-2-PC USB devices"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+
+/* transfer parameters */
+#define B2C2_USB_FRAMES_PER_ISO                4
+#define B2C2_USB_NUM_ISO_URB           4    /* TODO check out a good value */
+
+#define B2C2_USB_CTRL_PIPE_IN          usb_rcvctrlpipe(b2c2->udev,0)
+#define B2C2_USB_CTRL_PIPE_OUT         usb_sndctrlpipe(b2c2->udev,0)
+#define B2C2_USB_DATA_PIPE                     usb_rcvisocpipe(b2c2->udev,0x81)
+
+struct usb_b2c2_usb {
+       struct usb_device *udev;
+       struct usb_interface *uintf;
+
+       u8 *iso_buffer;
+       int buffer_size;
+       dma_addr_t iso_dma_handle;
+       struct urb *iso_urb[B2C2_USB_NUM_ISO_URB];
+};
+
+
+/*
+ * USB
+ * 10 90 34 12 78 56 04 00
+ * usb_control_msg(udev, usb_sndctrlpipe(udev,0),
+ * 0x90,
+ * 0x10,
+ * 0x1234,
+ * 0x5678,
+ * buf,
+ * 4,
+ * 5*HZ);
+ *
+ * extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
+ * __u8 request,
+ * __u8 requesttype,
+ * __u16 value,
+ * __u16 index,
+ * void *data,
+ * __u16 size,
+ * int timeout);
+ *
+ */
+
+/* request types */
+typedef enum {
+       RTYPE_READ_DW         = (1 << 6),
+       RTYPE_WRITE_DW_1      = (3 << 6),
+       RTYPE_READ_V8_MEMORY  = (6 << 6),
+       RTYPE_WRITE_V8_MEMORY = (7 << 6),
+       RTYPE_WRITE_V8_FLASH  = (8 << 6),
+       RTYPE_GENERIC         = (9 << 6),
+} b2c2_usb_request_type_t;
+
+/* request */
+typedef enum {
+       B2C2_USB_WRITE_V8_MEM = 0x04,
+       B2C2_USB_READ_V8_MEM  = 0x05,
+       B2C2_USB_READ_REG     = 0x08,
+       B2C2_USB_WRITE_REG    = 0x0A,
+/*     B2C2_USB_WRITEREGLO   = 0x0A, */
+       B2C2_USB_WRITEREGHI   = 0x0B,
+       B2C2_USB_FLASH_BLOCK  = 0x10,
+       B2C2_USB_I2C_REQUEST  = 0x11,
+       B2C2_USB_UTILITY      = 0x12,
+} b2c2_usb_request_t;
+
+/* function definition for I2C_REQUEST */
+typedef enum {
+       USB_FUNC_I2C_WRITE       = 0x01,
+       USB_FUNC_I2C_MULTIWRITE  = 0x02,
+       USB_FUNC_I2C_READ        = 0x03,
+       USB_FUNC_I2C_REPEATWRITE = 0x04,
+       USB_FUNC_GET_DESCRIPTOR  = 0x05,
+       USB_FUNC_I2C_REPEATREAD  = 0x06,
+/* DKT 020208 - add this to support special case of DiSEqC */
+       USB_FUNC_I2C_CHECKWRITE  = 0x07,
+       USB_FUNC_I2C_CHECKRESULT = 0x08,
+} b2c2_usb_i2c_function_t;
+
+/*
+ * function definition for UTILITY request 0x12
+ * DKT 020304 - new utility function
+ */
+typedef enum {
+       UTILITY_SET_FILTER          = 0x01,
+       UTILITY_DATA_ENABLE         = 0x02,
+       UTILITY_FLEX_MULTIWRITE     = 0x03,
+       UTILITY_SET_BUFFER_SIZE     = 0x04,
+       UTILITY_FLEX_OPERATOR       = 0x05,
+       UTILITY_FLEX_RESET300_START = 0x06,
+       UTILITY_FLEX_RESET300_STOP  = 0x07,
+       UTILITY_FLEX_RESET300       = 0x08,
+       UTILITY_SET_ISO_SIZE        = 0x09,
+       UTILITY_DATA_RESET          = 0x0A,
+       UTILITY_GET_DATA_STATUS     = 0x10,
+       UTILITY_GET_V8_REG          = 0x11,
+/* DKT 020326 - add function for v1.14 */
+       UTILITY_SRAM_WRITE          = 0x12,
+       UTILITY_SRAM_READ           = 0x13,
+       UTILITY_SRAM_TESTFILL       = 0x14,
+       UTILITY_SRAM_TESTSET        = 0x15,
+       UTILITY_SRAM_TESTVERIFY     = 0x16,
+} b2c2_usb_utility_function_t;
+
+#define B2C2_WAIT_FOR_OPERATION_RW  1  // 1 s
+#define B2C2_WAIT_FOR_OPERATION_RDW 3  // 3 s
+#define B2C2_WAIT_FOR_OPERATION_WDW 1  // 1 s
+
+#define B2C2_WAIT_FOR_OPERATION_V8READ   3  // 3 s
+#define B2C2_WAIT_FOR_OPERATION_V8WRITE  3  // 3 s
+#define B2C2_WAIT_FOR_OPERATION_V8FLASH  3  // 3 s
+
+/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
+ * in the IBI address, to make the V8 code simpler.
+ * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (these are the six bits used)
+ *                  in general: 0000 0HHH 000L LL00
+ * IBI ADDRESS FORMAT:                    RHHH BLLL
+ *
+ * where R is the read(1)/write(0) bit, B is the busy bit
+ * and HHH and LLL are the two sets of three bits from the PCI address.
+ */
+#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
+#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
+
+/*
+ * DKT 020228 - forget about this VENDOR_BUFFER_SIZE, read and write register
+ * deal with DWORD or 4 bytes, that should be should from now on
+ */
+static u32 b2c2_usb_read_dw(struct usb_b2c2_usb *b2c2, u16 wRegOffsPCI)
+{
+       u32 val;
+       u16 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | 0x0080;
+       int len = usb_control_msg(b2c2->udev,
+                       B2C2_USB_CTRL_PIPE_IN,
+                       B2C2_USB_READ_REG,
+                       RTYPE_READ_DW,
+                       wAddress,
+                       0,
+                       &val,
+                       sizeof(u32),
+                       B2C2_WAIT_FOR_OPERATION_RDW * HZ);
+
+       if (len != sizeof(u32)) {
+               err("error while reading dword from %d (%d).",wAddress,wRegOffsPCI);
+               return -EIO;
+       } else
+               return val;
+}
+
+/*
+ * DKT 020228 - from now on, we don't support anything older than firm 1.00
+ * I eliminated the write register as a 2 trip of writing hi word and lo word
+ * and force this to write only 4 bytes at a time.
+ * NOTE: this should work with all the firmware from 1.00 and newer
+ */
+static int b2c2_usb_write_dw(struct usb_b2c2_usb *b2c2, u16 wRegOffsPCI, u32 val)
+{
+       u16 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI);
+       int len = usb_control_msg(b2c2->udev,
+                       B2C2_USB_CTRL_PIPE_OUT,
+                       B2C2_USB_WRITE_REG,
+                       RTYPE_WRITE_DW_1,
+                       wAddress,
+                       0,
+                       &val,
+                       sizeof(u32),
+                       B2C2_WAIT_FOR_OPERATION_RDW * HZ);
+
+       if (len != sizeof(u32)) {
+               err("error while reading dword from %d (%d).",wAddress,wRegOffsPCI);
+               return -EIO;
+       } else
+               return 0;
+}
+
+/*
+ * DKT 010817 - add support for V8 memory read/write and flash update
+ */
+static int b2c2_usb_v8_memory_req(struct usb_b2c2_usb *b2c2,
+               b2c2_usb_request_t req, u8 page, u16 wAddress,
+               u16 buflen, u8 *pbBuffer)
+{
+       u8 dwRequestType;
+       u16 wIndex;
+       int nWaitTime,pipe,len;
+
+       wIndex = page << 8;
+
+       switch (req) {
+               case B2C2_USB_READ_V8_MEM:
+                       nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
+                       dwRequestType = (u8) RTYPE_READ_V8_MEMORY;
+                       pipe = B2C2_USB_CTRL_PIPE_IN;
+               break;
+               case B2C2_USB_WRITE_V8_MEM:
+                       wIndex |= pbBuffer[0];
+                       nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
+                       dwRequestType = (u8) RTYPE_WRITE_V8_MEMORY;
+                       pipe = B2C2_USB_CTRL_PIPE_OUT;
+               break;
+               case B2C2_USB_FLASH_BLOCK:
+                       nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
+                       dwRequestType = (u8) RTYPE_WRITE_V8_FLASH;
+                       pipe = B2C2_USB_CTRL_PIPE_OUT;
+               break;
+               default:
+                       deb_info("unsupported request for v8_mem_req %x.\n",req);
+               return -EINVAL;
+       }
+       len = usb_control_msg(b2c2->udev,pipe,
+                       req,
+                       dwRequestType,
+                       wAddress,
+                       wIndex,
+                       pbBuffer,
+                       buflen,
+                       nWaitTime * HZ);
+       return len == buflen ? 0 : -EIO;
+}
+
+static int b2c2_usb_i2c_req(struct usb_b2c2_usb *b2c2,
+               b2c2_usb_request_t req, b2c2_usb_i2c_function_t func,
+               u8 port, u8 chipaddr, u8 addr, u8 buflen, u8 *buf)
+{
+       u16 wValue, wIndex;
+       int nWaitTime,pipe,len;
+       u8 dwRequestType;
+
+       switch (func) {
+               case USB_FUNC_I2C_WRITE:
+               case USB_FUNC_I2C_MULTIWRITE:
+               case USB_FUNC_I2C_REPEATWRITE:
+               /* DKT 020208 - add this to support special case of DiSEqC */
+               case USB_FUNC_I2C_CHECKWRITE:
+                       pipe = B2C2_USB_CTRL_PIPE_OUT;
+                       nWaitTime = 2;
+                       dwRequestType = (u8) RTYPE_GENERIC;
+               break;
+               case USB_FUNC_I2C_READ:
+               case USB_FUNC_I2C_REPEATREAD:
+                       pipe = B2C2_USB_CTRL_PIPE_IN;
+                       nWaitTime = 2;
+                       dwRequestType = (u8) RTYPE_GENERIC;
+               break;
+               default:
+                       deb_info("unsupported function for i2c_req %x\n",func);
+                       return -EINVAL;
+       }
+       wValue = (func << 8 ) | port;
+       wIndex = (chipaddr << 8 ) | addr;
+
+       len = usb_control_msg(b2c2->udev,pipe,
+                       req,
+                       dwRequestType,
+                       addr,
+                       wIndex,
+                       buf,
+                       buflen,
+                       nWaitTime * HZ);
+       return len == buflen ? 0 : -EIO;
+}
+
+int static b2c2_usb_utility_req(struct usb_b2c2_usb *b2c2, int set,
+               b2c2_usb_utility_function_t func, u8 extra, u16 wIndex,
+               u16 buflen, u8 *pvBuffer)
+{
+       u16 wValue;
+       int nWaitTime = 2,
+               pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN,
+               len;
+
+       wValue = (func << 8) | extra;
+
+       len = usb_control_msg(b2c2->udev,pipe,
+                       B2C2_USB_UTILITY,
+                       (u8) RTYPE_GENERIC,
+                       wValue,
+                       wIndex,
+                       pvBuffer,
+                       buflen,
+                       nWaitTime * HZ);
+       return len == buflen ? 0 : -EIO;
+}
+
+
+
+static void b2c2_dumpfourreg(struct usb_b2c2_usb *b2c2, u16 offs)
+{
+       u32 r0,r1,r2,r3;
+       r0 = r1 = r2 = r3 = 0;
+       r0 = b2c2_usb_read_dw(b2c2,offs);
+       r1 = b2c2_usb_read_dw(b2c2,offs + 0x04);
+       r2 = b2c2_usb_read_dw(b2c2,offs + 0x08);
+       r3 = b2c2_usb_read_dw(b2c2,offs + 0x0c);
+       deb_ctrl("dump: offset: %03x, %08x, %08x, %08x, %08x\n",offs,r0,r1,r2,r3);
+}
+
+static void b2c2_urb_complete(struct urb *urb, struct pt_regs *ptregs)
+{
+       struct usb_b2c2_usb *b2c2 = urb->context;
+       deb_ts("urb completed, bufsize: %d\n",urb->transfer_buffer_length);
+
+//     urb_submit_urb(urb,GFP_ATOMIC); enable for real action
+}
+
+static void b2c2_exit_usb(struct usb_b2c2_usb *b2c2)
+{
+       int i;
+       for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
+               if (b2c2->iso_urb[i] != NULL) { /* not sure about unlink_urb and iso-urbs TODO */
+                       deb_info("unlinking/killing urb no. %d\n",i);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,7)
+                       usb_unlink_urb(b2c2->iso_urb[i]);
+#else
+                       usb_kill_urb(b2c2->iso_urb[i]);
+#endif
+                       usb_free_urb(b2c2->iso_urb[i]);
+               }
+
+       if (b2c2->iso_buffer != NULL)
+               pci_free_consistent(NULL,b2c2->buffer_size, b2c2->iso_buffer, b2c2->iso_dma_handle);
+
+}
+
+static int b2c2_init_usb(struct usb_b2c2_usb *b2c2)
+{
+       u16 frame_size = b2c2->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize;
+       int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size,i,j,ret;
+       int buffer_offset = 0;
+
+       deb_info("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
+                       B2C2_USB_NUM_ISO_URB, B2C2_USB_FRAMES_PER_ISO, frame_size,bufsize);
+
+       b2c2->iso_buffer = pci_alloc_consistent(NULL,bufsize,&b2c2->iso_dma_handle);
+       if (b2c2->iso_buffer == NULL)
+               return -ENOMEM;
+       memset(b2c2->iso_buffer, 0, bufsize);
+       b2c2->buffer_size = bufsize;
+
+       /* creating iso urbs */
+       for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
+               if (!(b2c2->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,GFP_ATOMIC))) {
+                       ret = -ENOMEM;
+                       goto urb_error;
+               }
+       /* initialising and submitting iso urbs */
+       for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
+               deb_info("initializing and submitting urb no. %d (buf_offset: %d).\n",i,buffer_offset);
+               int frame_offset = 0;
+               struct urb *urb = b2c2->iso_urb[i];
+
+               urb->dev = b2c2->udev;
+               urb->context = b2c2;
+               urb->complete = b2c2_urb_complete;
+               urb->pipe = B2C2_USB_DATA_PIPE;
+               urb->transfer_flags = URB_ISO_ASAP;
+               urb->interval = 1;
+               urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
+               urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
+               urb->transfer_buffer = b2c2->iso_buffer + buffer_offset;
+
+               buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
+               for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
+                       deb_info("urb no: %d, frame: %d, frame_offset: %d\n",i,j,frame_offset);
+                       urb->iso_frame_desc[j].offset = frame_offset;
+                       urb->iso_frame_desc[j].length = frame_size;
+                       frame_offset += frame_size;
+               }
+
+               if ((ret = usb_submit_urb(b2c2->iso_urb[i],GFP_ATOMIC))) {
+                       err("submitting urb %d failed with %d.",i,ret);
+                       goto urb_error;
+               }
+               deb_info("submitted urb no. %d.\n",i);
+       }
+
+       ret = 0;
+       goto success;
+urb_error:
+       b2c2_exit_usb(b2c2);
+success:
+       return ret;
+}
+
+static int b2c2_usb_probe(struct usb_interface *intf,
+               const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct usb_b2c2_usb *b2c2 = NULL;
+       int ret;
+
+       b2c2 = kmalloc(sizeof(struct usb_b2c2_usb),GFP_KERNEL);
+       if (b2c2 == NULL) {
+               err("no memory");
+               return -ENOMEM;
+       }
+       b2c2->udev = udev;
+       b2c2->uintf = intf;
+
+       /* use the alternate setting with the larges buffer */
+       usb_set_interface(udev,0,1);
+
+       if ((ret = b2c2_init_usb(b2c2)))
+               goto usb_init_error;
+
+       usb_set_intfdata(intf,b2c2);
+
+       switch (udev->speed) {
+               case USB_SPEED_LOW:
+                       err("cannot handle USB speed because it is to sLOW.");
+                       break;
+               case USB_SPEED_FULL:
+                       info("running at FULL speed.");
+                       break;
+               case USB_SPEED_HIGH:
+                       info("running at HIGH speed.");
+                       break;
+               case USB_SPEED_UNKNOWN: /* fall through */
+               default:
+                       err("cannot handle USB speed because it is unkown.");
+               break;
+       }
+
+       b2c2_dumpfourreg(b2c2,0x200);
+       b2c2_dumpfourreg(b2c2,0x300);
+       b2c2_dumpfourreg(b2c2,0x400);
+       b2c2_dumpfourreg(b2c2,0x700);
+
+
+       if (ret == 0)
+               info("%s successfully initialized and connected.",DRIVER_DESC);
+       else
+               info("%s error while loading driver (%d)",DRIVER_DESC,ret);
+
+       ret = 0;
+       goto success;
+
+usb_init_error:
+       kfree(b2c2);
+success:
+       return ret;
+}
+
+static void b2c2_usb_disconnect(struct usb_interface *intf)
+{
+       struct usb_b2c2_usb *b2c2 = usb_get_intfdata(intf);
+       usb_set_intfdata(intf,NULL);
+       if (b2c2 != NULL) {
+               b2c2_exit_usb(b2c2);
+               kfree(b2c2);
+       }
+       info("%s successfully deinitialized and disconnected.",DRIVER_DESC);
+
+}
+
+static struct usb_device_id b2c2_usb_table [] = {
+           { USB_DEVICE(0x0af7, 0x0101) }
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver b2c2_usb_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "dvb_b2c2_usb",
+       .probe          = b2c2_usb_probe,
+       .disconnect = b2c2_usb_disconnect,
+       .id_table       = b2c2_usb_table,
+};
+
+/* module stuff */
+static int __init b2c2_usb_init(void)
+{
+       int result;
+       if ((result = usb_register(&b2c2_usb_driver))) {
+               err("usb_register failed. Error number %d",result);
+               return result;
+       }
+
+       return 0;
+}
+
+static void __exit b2c2_usb_exit(void)
+{
+       /* deregister this driver from the USB subsystem */
+       usb_deregister(&b2c2_usb_driver);
+}
+
+module_init (b2c2_usb_init);
+module_exit (b2c2_usb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c
new file mode 100644 (file)
index 0000000..62830d1
--- /dev/null
@@ -0,0 +1,1089 @@
+/*
+    Frontend-driver for TwinHan DST Frontend
+
+    Copyright (C) 2003 Jamie Honan
+
+    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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "dst_priv.h"
+#include "dst.h"
+
+struct dst_state {
+
+       struct i2c_adapter* i2c;
+
+       struct bt878* bt;
+
+       struct dvb_frontend_ops ops;
+
+       /* configuration settings */
+       const struct dst_config* config;
+
+       struct dvb_frontend frontend;
+
+       /* private demodulator data */
+       u8 tx_tuna[10];
+       u8 rx_tuna[10];
+       u8 rxbuffer[10];
+       u8 diseq_flags;
+       u8 dst_type;
+       u32 type_flags;
+       u32 frequency;          /* intermediate frequency in kHz for QPSK */
+       fe_spectral_inversion_t inversion;
+       u32 symbol_rate;        /* symbol rate in Symbols per second */
+       fe_code_rate_t fec;
+       fe_sec_voltage_t voltage;
+       fe_sec_tone_mode_t tone;
+       u32 decode_freq;
+       u8 decode_lock;
+       u16 decode_strength;
+       u16 decode_snr;
+       unsigned long cur_jiff;
+       u8 k22;
+       fe_bandwidth_t bandwidth;
+};
+
+static unsigned int dst_verbose = 0;
+module_param(dst_verbose, int, 0644);
+MODULE_PARM_DESC(dst_verbose, "verbose startup messages, default is 1 (yes)");
+static unsigned int dst_debug = 0;
+module_param(dst_debug, int, 0644);
+MODULE_PARM_DESC(dst_debug, "debug messages, default is 0 (no)");
+
+#define dprintk        if (dst_debug) printk
+
+#define DST_TYPE_IS_SAT                0
+#define DST_TYPE_IS_TERR       1
+#define DST_TYPE_IS_CABLE      2
+
+#define DST_TYPE_HAS_NEWTUNE   1
+#define DST_TYPE_HAS_TS204     2
+#define DST_TYPE_HAS_SYMDIV    4
+
+#define HAS_LOCK       1
+#define ATTEMPT_TUNE   2
+#define HAS_POWER      4
+
+static void dst_packsize(struct dst_state* state, int psize)
+{
+       union dst_gpio_packet bits;
+
+       bits.psize = psize;
+       bt878_device_control(state->bt, DST_IG_TS, &bits);
+}
+
+static int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh)
+{
+       union dst_gpio_packet enb;
+       union dst_gpio_packet bits;
+       int err;
+
+       enb.enb.mask = mask;
+       enb.enb.enable = enbb;
+       if ((err = bt878_device_control(state->bt, DST_IG_ENABLE, &enb)) < 0) {
+               dprintk("%s: dst_gpio_enb error (err == %i, mask == 0x%02x, enb == 0x%02x)\n", __FUNCTION__, err, mask, enbb);
+               return -EREMOTEIO;
+       }
+
+       /* because complete disabling means no output, no need to do output packet */
+       if (enbb == 0)
+               return 0;
+
+       bits.outp.mask = enbb;
+       bits.outp.highvals = outhigh;
+
+       if ((err = bt878_device_control(state->bt, DST_IG_WRITE, &bits)) < 0) {
+               dprintk("%s: dst_gpio_outb error (err == %i, enbb == 0x%02x, outhigh == 0x%02x)\n", __FUNCTION__, err, enbb, outhigh);
+               return -EREMOTEIO;
+       }
+       return 0;
+}
+
+static int dst_gpio_inb(struct dst_state *state, u8 * result)
+{
+       union dst_gpio_packet rd_packet;
+       int err;
+
+       *result = 0;
+
+       if ((err = bt878_device_control(state->bt, DST_IG_READ, &rd_packet)) < 0) {
+               dprintk("%s: dst_gpio_inb error (err == %i)\n", __FUNCTION__, err);
+               return -EREMOTEIO;
+       }
+
+       *result = (u8) rd_packet.rd.value;
+       return 0;
+}
+
+#define DST_I2C_ENABLE 1
+#define DST_8820       2
+
+static int dst_reset8820(struct dst_state *state)
+{
+       int retval;
+       /* pull 8820 gpio pin low, wait, high, wait, then low */
+       // dprintk ("%s: reset 8820\n", __FUNCTION__);
+       retval = dst_gpio_outb(state, DST_8820, DST_8820, 0);
+       if (retval < 0)
+               return retval;
+       msleep(10);
+       retval = dst_gpio_outb(state, DST_8820, DST_8820, DST_8820);
+       if (retval < 0)
+               return retval;
+       /* wait for more feedback on what works here *
+          msleep(10);
+          retval = dst_gpio_outb(dst, DST_8820, DST_8820, 0);
+          if (retval < 0)
+          return retval;
+        */
+       return 0;
+}
+
+static int dst_i2c_enable(struct dst_state *state)
+{
+       int retval;
+       /* pull I2C enable gpio pin low, wait */
+       // dprintk ("%s: i2c enable\n", __FUNCTION__);
+       retval = dst_gpio_outb(state, ~0, DST_I2C_ENABLE, 0);
+       if (retval < 0)
+               return retval;
+       // dprintk ("%s: i2c enable delay\n", __FUNCTION__);
+       msleep(33);
+       return 0;
+}
+
+static int dst_i2c_disable(struct dst_state *state)
+{
+       int retval;
+       /* release I2C enable gpio pin, wait */
+       // dprintk ("%s: i2c disable\n", __FUNCTION__);
+       retval = dst_gpio_outb(state, ~0, 0, 0);
+       if (retval < 0)
+               return retval;
+       // dprintk ("%s: i2c disable delay\n", __FUNCTION__);
+       msleep(33);
+       return 0;
+}
+
+static int dst_wait_dst_ready(struct dst_state *state)
+{
+       u8 reply;
+       int retval;
+       int i;
+       for (i = 0; i < 200; i++) {
+               retval = dst_gpio_inb(state, &reply);
+               if (retval < 0)
+                       return retval;
+               if ((reply & DST_I2C_ENABLE) == 0) {
+                       dprintk("%s: dst wait ready after %d\n", __FUNCTION__, i);
+                       return 1;
+               }
+               msleep(10);
+       }
+       dprintk("%s: dst wait NOT ready after %d\n", __FUNCTION__, i);
+       return 0;
+}
+
+static int write_dst(struct dst_state *state, u8 * data, u8 len)
+{
+       struct i2c_msg msg = {
+               .addr = state->config->demod_address,.flags = 0,.buf = data,.len = len
+       };
+       int err;
+       int cnt;
+
+       if (dst_debug && dst_verbose) {
+               u8 i;
+               dprintk("%s writing", __FUNCTION__);
+               for (i = 0; i < len; i++) {
+                       dprintk(" 0x%02x", data[i]);
+               }
+               dprintk("\n");
+       }
+       msleep(30);
+       for (cnt = 0; cnt < 4; cnt++) {
+               if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) {
+                       dprintk("%s: write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)\n", __FUNCTION__, err, len, data[0]);
+                       dst_i2c_disable(state);
+                       msleep(500);
+                       dst_i2c_enable(state);
+                       msleep(500);
+                       continue;
+               } else
+                       break;
+       }
+       if (cnt >= 4)
+               return -EREMOTEIO;
+       return 0;
+}
+
+static int read_dst(struct dst_state *state, u8 * ret, u8 len)
+{
+       struct i2c_msg msg = {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = ret,.len = len };
+       int err;
+       int cnt;
+
+       for (cnt = 0; cnt < 4; cnt++) {
+               if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) {
+                       dprintk("%s: read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)\n", __FUNCTION__, err, len, ret[0]);
+                       dst_i2c_disable(state);
+                       dst_i2c_enable(state);
+                       continue;
+               } else
+                       break;
+       }
+       if (cnt >= 4)
+               return -EREMOTEIO;
+       dprintk("%s reply is 0x%x\n", __FUNCTION__, ret[0]);
+       if (dst_debug && dst_verbose) {
+               for (err = 1; err < len; err++)
+                       dprintk(" 0x%x", ret[err]);
+               if (err > 1)
+                       dprintk("\n");
+       }
+       return 0;
+}
+
+static int dst_set_freq(struct dst_state *state, u32 freq)
+{
+       u8 *val;
+
+       state->frequency = freq;
+
+       // dprintk("%s: set frequency %u\n", __FUNCTION__, freq);
+       if (state->dst_type == DST_TYPE_IS_SAT) {
+               freq = freq / 1000;
+               if (freq < 950 || freq > 2150)
+                       return -EINVAL;
+               val = &state->tx_tuna[0];
+               val[2] = (freq >> 8) & 0x7f;
+               val[3] = (u8) freq;
+               val[4] = 1;
+               val[8] &= ~4;
+               if (freq < 1531)
+                       val[8] |= 4;
+       } else if (state->dst_type == DST_TYPE_IS_TERR) {
+               freq = freq / 1000;
+               if (freq < 137000 || freq > 858000)
+                       return -EINVAL;
+               val = &state->tx_tuna[0];
+               val[2] = (freq >> 16) & 0xff;
+               val[3] = (freq >> 8) & 0xff;
+               val[4] = (u8) freq;
+               val[5] = 0;
+               switch (state->bandwidth) {
+               case BANDWIDTH_6_MHZ:
+                       val[6] = 6;
+                       break;
+
+               case BANDWIDTH_7_MHZ:
+               case BANDWIDTH_AUTO:
+                       val[6] = 7;
+                       break;
+
+               case BANDWIDTH_8_MHZ:
+                       val[6] = 8;
+                       break;
+               }
+
+               val[7] = 0;
+               val[8] = 0;
+       } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+               /* guess till will get one */
+               freq = freq / 1000;
+               val = &state->tx_tuna[0];
+               val[2] = (freq >> 16) & 0xff;
+               val[3] = (freq >> 8) & 0xff;
+               val[4] = (u8) freq;
+       } else
+               return -EINVAL;
+       return 0;
+}
+
+static int dst_set_bandwidth(struct dst_state* state, fe_bandwidth_t bandwidth)
+{
+       u8 *val;
+
+       state->bandwidth = bandwidth;
+
+       if (state->dst_type != DST_TYPE_IS_TERR)
+               return 0;
+
+       val = &state->tx_tuna[0];
+       switch (bandwidth) {
+       case BANDWIDTH_6_MHZ:
+               val[6] = 6;
+               break;
+
+       case BANDWIDTH_7_MHZ:
+               val[6] = 7;
+               break;
+
+       case BANDWIDTH_8_MHZ:
+               val[6] = 8;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int dst_set_inversion(struct dst_state* state, fe_spectral_inversion_t inversion)
+{
+       u8 *val;
+
+       state->inversion = inversion;
+
+       val = &state->tx_tuna[0];
+
+       val[8] &= ~0x80;
+
+       switch (inversion) {
+       case INVERSION_OFF:
+               break;
+       case INVERSION_ON:
+               val[8] |= 0x80;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int dst_set_fec(struct dst_state* state, fe_code_rate_t fec)
+{
+       state->fec = fec;
+       return 0;
+}
+
+static fe_code_rate_t dst_get_fec(struct dst_state* state)
+{
+       return state->fec;
+}
+
+static int dst_set_symbolrate(struct dst_state* state, u32 srate)
+{
+       u8 *val;
+       u32 symcalc;
+       u64 sval;
+
+       state->symbol_rate = srate;
+
+       if (state->dst_type == DST_TYPE_IS_TERR) {
+               return 0;
+       }
+       // dprintk("%s: set srate %u\n", __FUNCTION__, srate);
+       srate /= 1000;
+       val = &state->tx_tuna[0];
+
+       if (state->type_flags & DST_TYPE_HAS_SYMDIV) {
+               sval = srate;
+               sval <<= 20;
+               do_div(sval, 88000);
+               symcalc = (u32) sval;
+               // dprintk("%s: set symcalc %u\n", __FUNCTION__, symcalc);
+               val[5] = (u8) (symcalc >> 12);
+               val[6] = (u8) (symcalc >> 4);
+               val[7] = (u8) (symcalc << 4);
+       } else {
+               val[5] = (u8) (srate >> 16) & 0x7f;
+               val[6] = (u8) (srate >> 8);
+               val[7] = (u8) srate;
+       }
+       val[8] &= ~0x20;
+       if (srate > 8000)
+               val[8] |= 0x20;
+       return 0;
+}
+
+static u8 dst_check_sum(u8 * buf, u32 len)
+{
+       u32 i;
+       u8 val = 0;
+       if (!len)
+               return 0;
+       for (i = 0; i < len; i++) {
+               val += buf[i];
+       }
+       return ((~val) + 1);
+}
+
+struct dst_types {
+       char *mstr;
+       int offs;
+       u8 dst_type;
+       u32 type_flags;
+};
+
+static struct dst_types dst_tlist[] = {
+       {"DST-020", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV},
+       {"DST-030", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_TS204 | DST_TYPE_HAS_NEWTUNE},
+       {"DST-03T", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204},
+       {"DST-MOT", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV},
+       {"DST-CI",  1, DST_TYPE_IS_SAT, DST_TYPE_HAS_TS204 | DST_TYPE_HAS_NEWTUNE},
+       {"DSTMCI",  1, DST_TYPE_IS_SAT, DST_TYPE_HAS_NEWTUNE},
+       {"DSTFCI",  1, DST_TYPE_IS_SAT, DST_TYPE_HAS_NEWTUNE},
+       {"DCTNEW",  1, DST_TYPE_IS_CABLE, DST_TYPE_HAS_NEWTUNE},
+       {"DCT-CI",  1, DST_TYPE_IS_CABLE, DST_TYPE_HAS_NEWTUNE | DST_TYPE_HAS_TS204},
+       {"DTTDIG",  1, DST_TYPE_IS_TERR, 0}
+};
+
+/* DCTNEW and DCT-CI are guesses */
+
+static void dst_type_flags_print(u32 type_flags)
+{
+       printk("DST type flags :");
+       if (type_flags & DST_TYPE_HAS_NEWTUNE)
+               printk(" 0x%x newtuner", DST_TYPE_HAS_NEWTUNE);
+       if (type_flags & DST_TYPE_HAS_TS204)
+               printk(" 0x%x ts204", DST_TYPE_HAS_TS204);
+       if (type_flags & DST_TYPE_HAS_SYMDIV)
+               printk(" 0x%x symdiv", DST_TYPE_HAS_SYMDIV);
+       printk("\n");
+}
+
+static int dst_type_print(u8 type)
+{
+       char *otype;
+       switch (type) {
+       case DST_TYPE_IS_SAT:
+               otype = "satellite";
+               break;
+       case DST_TYPE_IS_TERR:
+               otype = "terrestrial";
+               break;
+       case DST_TYPE_IS_CABLE:
+               otype = "cable";
+               break;
+       default:
+               printk("%s: invalid dst type %d\n", __FUNCTION__, type);
+               return -EINVAL;
+       }
+       printk("DST type : %s\n", otype);
+       return 0;
+}
+
+static int dst_check_ci(struct dst_state *state)
+{
+       u8 txbuf[8];
+       u8 rxbuf[8];
+       int retval;
+       int i;
+       struct dst_types *dsp;
+       u8 use_dst_type;
+       u32 use_type_flags;
+
+       memset(txbuf, 0, sizeof(txbuf));
+       txbuf[1] = 6;
+       txbuf[7] = dst_check_sum(txbuf, 7);
+
+       dst_i2c_enable(state);
+       dst_reset8820(state);
+       retval = write_dst(state, txbuf, 8);
+       if (retval < 0) {
+               dst_i2c_disable(state);
+               dprintk("%s: write not successful, maybe no card?\n", __FUNCTION__);
+               return retval;
+       }
+       msleep(3);
+       retval = read_dst(state, rxbuf, 1);
+       dst_i2c_disable(state);
+       if (retval < 0) {
+               dprintk("%s: read not successful, maybe no card?\n", __FUNCTION__);
+               return retval;
+       }
+       if (rxbuf[0] != 0xff) {
+               dprintk("%s: write reply not 0xff, not ci (%02x)\n", __FUNCTION__, rxbuf[0]);
+               return retval;
+       }
+       if (!dst_wait_dst_ready(state))
+               return 0;
+       // dst_i2c_enable(i2c); Dimitri
+       retval = read_dst(state, rxbuf, 8);
+       dst_i2c_disable(state);
+       if (retval < 0) {
+               dprintk("%s: read not successful\n", __FUNCTION__);
+               return retval;
+       }
+       if (rxbuf[7] != dst_check_sum(rxbuf, 7)) {
+               dprintk("%s: checksum failure\n", __FUNCTION__);
+               return retval;
+       }
+       rxbuf[7] = '\0';
+       for (i = 0, dsp = &dst_tlist[0]; i < sizeof(dst_tlist) / sizeof(dst_tlist[0]); i++, dsp++) {
+               if (!strncmp(&rxbuf[dsp->offs], dsp->mstr, strlen(dsp->mstr))) {
+                       use_type_flags = dsp->type_flags;
+                       use_dst_type = dsp->dst_type;
+                       printk("%s: recognize %s\n", __FUNCTION__, dsp->mstr);
+                       break;
+               }
+       }
+       if (i >= sizeof(dst_tlist) / sizeof(dst_tlist[0])) {
+               printk("%s: unable to recognize %s or %s\n", __FUNCTION__, &rxbuf[0], &rxbuf[1]);
+               printk("%s please email linux-dvb@linuxtv.org with this type in\n", __FUNCTION__);
+               use_dst_type = DST_TYPE_IS_SAT;
+               use_type_flags = DST_TYPE_HAS_SYMDIV;
+       }
+       dst_type_print(use_dst_type);
+
+       state->type_flags = use_type_flags;
+       state->dst_type = use_dst_type;
+       dst_type_flags_print(state->type_flags);
+
+       if (state->type_flags & DST_TYPE_HAS_TS204) {
+               dst_packsize(state, 204);
+       }
+       return 0;
+}
+
+static int dst_command(struct dst_state* state, u8 * data, u8 len)
+{
+       int retval;
+       u8 reply;
+
+       dst_i2c_enable(state);
+       dst_reset8820(state);
+       retval = write_dst(state, data, len);
+       if (retval < 0) {
+               dst_i2c_disable(state);
+               dprintk("%s: write not successful\n", __FUNCTION__);
+               return retval;
+       }
+       msleep(33);
+       retval = read_dst(state, &reply, 1);
+       dst_i2c_disable(state);
+       if (retval < 0) {
+               dprintk("%s: read verify  not successful\n", __FUNCTION__);
+               return retval;
+       }
+       if (reply != 0xff) {
+               dprintk("%s: write reply not 0xff 0x%02x \n", __FUNCTION__, reply);
+               return 0;
+       }
+       if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3))
+               return 0;
+       if (!dst_wait_dst_ready(state))
+               return 0;
+       // dst_i2c_enable(i2c); Per dimitri
+       retval = read_dst(state, state->rxbuffer, 8);
+       dst_i2c_disable(state);
+       if (retval < 0) {
+               dprintk("%s: read not successful\n", __FUNCTION__);
+               return 0;
+       }
+       if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) {
+               dprintk("%s: checksum failure\n", __FUNCTION__);
+               return 0;
+       }
+       return 0;
+}
+
+static int dst_get_signal(struct dst_state* state)
+{
+       int retval;
+       u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb };
+
+       if ((state->diseq_flags & ATTEMPT_TUNE) == 0) {
+               state->decode_lock = state->decode_strength = state->decode_snr = 0;
+               return 0;
+       }
+       if (0 == (state->diseq_flags & HAS_LOCK)) {
+               state->decode_lock = state->decode_strength = state->decode_snr = 0;
+               return 0;
+       }
+       if (time_after_eq(jiffies, state->cur_jiff + (HZ / 5))) {
+               retval = dst_command(state, get_signal, 8);
+               if (retval < 0)
+                       return retval;
+               if (state->dst_type == DST_TYPE_IS_SAT) {
+                       state->decode_lock = ((state->rxbuffer[6] & 0x10) == 0) ? 1 : 0;
+                       state->decode_strength = state->rxbuffer[5] << 8;
+                       state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3];
+               } else if ((state->dst_type == DST_TYPE_IS_TERR) || (state->dst_type == DST_TYPE_IS_CABLE)) {
+                       state->decode_lock = (state->rxbuffer[1]) ? 1 : 0;
+                       state->decode_strength = state->rxbuffer[4] << 8;
+                       state->decode_snr = state->rxbuffer[3] << 8;
+               }
+               state->cur_jiff = jiffies;
+       }
+       return 0;
+}
+
+static int dst_tone_power_cmd(struct dst_state* state)
+{
+       u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 };
+
+       if (state->dst_type == DST_TYPE_IS_TERR)
+               return 0;
+
+       if (state->voltage == SEC_VOLTAGE_OFF)
+               paket[4] = 0;
+       else
+               paket[4] = 1;
+       if (state->tone == SEC_TONE_ON)
+               paket[2] = state->k22;
+       else
+               paket[2] = 0;
+       paket[7] = dst_check_sum(&paket[0], 7);
+       dst_command(state, paket, 8);
+       return 0;
+}
+
+static int dst_get_tuna(struct dst_state* state)
+{
+       int retval;
+       if ((state->diseq_flags & ATTEMPT_TUNE) == 0)
+               return 0;
+       state->diseq_flags &= ~(HAS_LOCK);
+       if (!dst_wait_dst_ready(state))
+               return 0;
+       if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+               /* how to get variable length reply ???? */
+               retval = read_dst(state, state->rx_tuna, 10);
+       } else {
+               retval = read_dst(state, &state->rx_tuna[2], 8);
+       }
+       if (retval < 0) {
+               dprintk("%s: read not successful\n", __FUNCTION__);
+               return 0;
+       }
+       if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+               if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) {
+                       dprintk("%s: checksum failure?\n", __FUNCTION__);
+                       return 0;
+               }
+       } else {
+               if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[2], 7)) {
+                       dprintk("%s: checksum failure?\n", __FUNCTION__);
+                       return 0;
+               }
+       }
+       if (state->rx_tuna[2] == 0 && state->rx_tuna[3] == 0)
+               return 0;
+       state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 8) + state->rx_tuna[3];
+
+       state->decode_lock = 1;
+       /*
+          dst->decode_n1 = (dst->rx_tuna[4] << 8) +
+          (dst->rx_tuna[5]);
+
+          dst->decode_n2 = (dst->rx_tuna[8] << 8) +
+          (dst->rx_tuna[7]);
+        */
+       state->diseq_flags |= HAS_LOCK;
+       /* dst->cur_jiff = jiffies; */
+       return 1;
+}
+
+static int dst_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
+
+static int dst_write_tuna(struct dvb_frontend* fe)
+{
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+       int retval;
+       u8 reply;
+
+       dprintk("%s: type_flags 0x%x \n", __FUNCTION__, state->type_flags);
+       state->decode_freq = 0;
+       state->decode_lock = state->decode_strength = state->decode_snr = 0;
+       if (state->dst_type == DST_TYPE_IS_SAT) {
+               if (!(state->diseq_flags & HAS_POWER))
+                       dst_set_voltage(fe, SEC_VOLTAGE_13);
+       }
+       state->diseq_flags &= ~(HAS_LOCK | ATTEMPT_TUNE);
+       dst_i2c_enable(state);
+       if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+               dst_reset8820(state);
+               state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[0], 9);
+               retval = write_dst(state, &state->tx_tuna[0], 10);
+       } else {
+               state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[2], 7);
+               retval = write_dst(state, &state->tx_tuna[2], 8);
+       }
+       if (retval < 0) {
+               dst_i2c_disable(state);
+               dprintk("%s: write not successful\n", __FUNCTION__);
+               return retval;
+       }
+       msleep(3);
+       retval = read_dst(state, &reply, 1);
+       dst_i2c_disable(state);
+       if (retval < 0) {
+               dprintk("%s: read verify  not successful\n", __FUNCTION__);
+               return retval;
+       }
+       if (reply != 0xff) {
+               dprintk("%s: write reply not 0xff 0x%02x \n", __FUNCTION__, reply);
+               return 0;
+       }
+       state->diseq_flags |= ATTEMPT_TUNE;
+       return dst_get_tuna(state);
+}
+
+/*
+ * line22k0    0x00, 0x09, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00
+ * line22k1    0x00, 0x09, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00
+ * line22k2    0x00, 0x09, 0x02, 0xff, 0x01, 0x00, 0x00, 0x00
+ * tone        0x00, 0x09, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00
+ * data        0x00, 0x09, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00
+ * power_off   0x00, 0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
+ * power_on    0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00
+ * Diseqc 1    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec
+ * Diseqc 2    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf4, 0xe8
+ * Diseqc 3    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf8, 0xe4
+ * Diseqc 4    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xfc, 0xe0
+ */
+
+static int dst_set_diseqc(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+       u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec };
+
+       if (state->dst_type == DST_TYPE_IS_TERR)
+               return 0;
+
+       if (cmd->msg_len == 0 || cmd->msg_len > 4)
+               return -EINVAL;
+       memcpy(&paket[3], cmd->msg, cmd->msg_len);
+       paket[7] = dst_check_sum(&paket[0], 7);
+       dst_command(state, paket, 8);
+       return 0;
+}
+
+static int dst_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+       u8 *val;
+       int need_cmd;
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+       state->voltage = voltage;
+
+       if (state->dst_type == DST_TYPE_IS_TERR)
+               return 0;
+
+       need_cmd = 0;
+       val = &state->tx_tuna[0];
+       val[8] &= ~0x40;
+       switch (voltage) {
+       case SEC_VOLTAGE_13:
+               if ((state->diseq_flags & HAS_POWER) == 0)
+                       need_cmd = 1;
+               state->diseq_flags |= HAS_POWER;
+               break;
+       case SEC_VOLTAGE_18:
+               if ((state->diseq_flags & HAS_POWER) == 0)
+                       need_cmd = 1;
+               state->diseq_flags |= HAS_POWER;
+               val[8] |= 0x40;
+               break;
+       case SEC_VOLTAGE_OFF:
+               need_cmd = 1;
+               state->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE);
+               break;
+       default:
+               return -EINVAL;
+       }
+       if (need_cmd) {
+               dst_tone_power_cmd(state);
+       }
+       return 0;
+}
+
+static int dst_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+       u8 *val;
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+       state->tone = tone;
+
+       if (state->dst_type == DST_TYPE_IS_TERR)
+               return 0;
+
+       val = &state->tx_tuna[0];
+
+       val[8] &= ~0x1;
+
+       switch (tone) {
+       case SEC_TONE_OFF:
+               break;
+       case SEC_TONE_ON:
+               val[8] |= 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+       dst_tone_power_cmd(state);
+       return 0;
+}
+
+static int dst_init(struct dvb_frontend* fe)
+{
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+       static u8 ini_satci_tuna[] = { 9, 0, 3, 0xb6, 1, 0, 0x73, 0x21, 0, 0 };
+       static u8 ini_satfta_tuna[] = { 0, 0, 3, 0xb6, 1, 0x55, 0xbd, 0x50, 0, 0 };
+       static u8 ini_tvfta_tuna[] = { 0, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+       static u8 ini_tvci_tuna[] = { 9, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+       static u8 ini_cabfta_tuna[] = { 0, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+       static u8 ini_cabci_tuna[] = { 9, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+       state->inversion = INVERSION_ON;
+       state->voltage = SEC_VOLTAGE_13;
+       state->tone = SEC_TONE_OFF;
+       state->symbol_rate = 29473000;
+       state->fec = FEC_AUTO;
+       state->diseq_flags = 0;
+       state->k22 = 0x02;
+       state->bandwidth = BANDWIDTH_7_MHZ;
+       state->cur_jiff = jiffies;
+       if (state->dst_type == DST_TYPE_IS_SAT) {
+               state->frequency = 950000;
+               memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_satci_tuna : ini_satfta_tuna), sizeof(ini_satfta_tuna));
+       } else if (state->dst_type == DST_TYPE_IS_TERR) {
+               state->frequency = 137000000;
+               memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_tvci_tuna : ini_tvfta_tuna), sizeof(ini_tvfta_tuna));
+       } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+               state->frequency = 51000000;
+               memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_cabci_tuna : ini_cabfta_tuna), sizeof(ini_cabfta_tuna));
+       }
+
+       return 0;
+}
+
+static int dst_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+       *status = 0;
+       if (state->diseq_flags & HAS_LOCK) {
+               dst_get_signal(state);
+               if (state->decode_lock)
+                       *status |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | FE_HAS_VITERBI;
+       }
+
+       return 0;
+}
+
+static int dst_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+       dst_get_signal(state);
+       *strength = state->decode_strength;
+
+       return 0;
+}
+
+static int dst_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+       dst_get_signal(state);
+       *snr = state->decode_snr;
+
+       return 0;
+}
+
+static int dst_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+       dst_set_freq(state, p->frequency);
+       dst_set_inversion(state, p->inversion);
+       if (state->dst_type == DST_TYPE_IS_SAT) {
+               dst_set_fec(state, p->u.qpsk.fec_inner);
+               dst_set_symbolrate(state, p->u.qpsk.symbol_rate);
+       } else if (state->dst_type == DST_TYPE_IS_TERR) {
+               dst_set_bandwidth(state, p->u.ofdm.bandwidth);
+       } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+               dst_set_fec(state, p->u.qam.fec_inner);
+               dst_set_symbolrate(state, p->u.qam.symbol_rate);
+       }
+       dst_write_tuna(fe);
+
+       return 0;
+}
+
+static int dst_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+       p->frequency = state->decode_freq;
+       p->inversion = state->inversion;
+       if (state->dst_type == DST_TYPE_IS_SAT) {
+               p->u.qpsk.symbol_rate = state->symbol_rate;
+               p->u.qpsk.fec_inner = dst_get_fec(state);
+       } else if (state->dst_type == DST_TYPE_IS_TERR) {
+               p->u.ofdm.bandwidth = state->bandwidth;
+       } else if (state->dst_type == DST_TYPE_IS_CABLE) {
+               p->u.qam.symbol_rate = state->symbol_rate;
+               p->u.qam.fec_inner = dst_get_fec(state);
+               p->u.qam.modulation = QAM_AUTO;
+       }
+
+       return 0;
+}
+
+static void dst_release(struct dvb_frontend* fe)
+{
+       struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+       kfree(state);
+}
+
+static struct dvb_frontend_ops dst_dvbt_ops;
+static struct dvb_frontend_ops dst_dvbs_ops;
+static struct dvb_frontend_ops dst_dvbc_ops;
+
+struct dvb_frontend* dst_attach(const struct dst_config* config,
+                               struct i2c_adapter* i2c,
+                               struct bt878 *bt)
+{
+       struct dst_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct dst_state*) kmalloc(sizeof(struct dst_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       state->bt = bt;
+
+       /* check if the demod is there */
+       if (dst_check_ci(state) < 0) goto error;
+
+       /* determine settings based on type */
+       switch (state->dst_type) {
+       case DST_TYPE_IS_TERR:
+               memcpy(&state->ops, &dst_dvbt_ops, sizeof(struct dvb_frontend_ops));
+               break;
+       case DST_TYPE_IS_CABLE:
+               memcpy(&state->ops, &dst_dvbc_ops, sizeof(struct dvb_frontend_ops));
+               break;
+       case DST_TYPE_IS_SAT:
+               memcpy(&state->ops, &dst_dvbs_ops, sizeof(struct dvb_frontend_ops));
+               break;
+       default:
+               printk("dst: unknown frontend type. please report to the LinuxTV.org DVB mailinglist.\n");
+               goto error;
+       }
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops dst_dvbt_ops = {
+
+       .info = {
+               .name = "DST DVB-T",
+               .type = FE_OFDM,
+               .frequency_min = 137000000,
+               .frequency_max = 858000000,
+               .frequency_stepsize = 166667,
+               .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
+       },
+
+       .release = dst_release,
+
+       .init = dst_init,
+
+       .set_frontend = dst_set_frontend,
+       .get_frontend = dst_get_frontend,
+
+       .read_status = dst_read_status,
+       .read_signal_strength = dst_read_signal_strength,
+       .read_snr = dst_read_snr,
+};
+
+static struct dvb_frontend_ops dst_dvbs_ops = {
+
+       .info = {
+               .name = "DST DVB-S",
+               .type = FE_QPSK,
+               .frequency_min = 950000,
+               .frequency_max = 2150000,
+               .frequency_stepsize = 1000,     /* kHz for QPSK frontends */
+               .frequency_tolerance = 29500,
+               .symbol_rate_min = 1000000,
+               .symbol_rate_max = 45000000,
+       /*     . symbol_rate_tolerance  =       ???,*/
+               .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK
+       },
+
+       .release = dst_release,
+
+       .init = dst_init,
+
+       .set_frontend = dst_set_frontend,
+       .get_frontend = dst_get_frontend,
+
+       .read_status = dst_read_status,
+       .read_signal_strength = dst_read_signal_strength,
+       .read_snr = dst_read_snr,
+
+       .diseqc_send_master_cmd = dst_set_diseqc,
+       .set_voltage = dst_set_voltage,
+       .set_tone = dst_set_tone,
+};
+
+static struct dvb_frontend_ops dst_dvbc_ops = {
+
+       .info = {
+               .name = "DST DVB-C",
+               .type = FE_QAM,
+               .frequency_stepsize = 62500,
+               .frequency_min = 51000000,
+               .frequency_max = 858000000,
+               .symbol_rate_min = 1000000,
+               .symbol_rate_max = 45000000,
+       /*     . symbol_rate_tolerance  =       ???,*/
+               .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO
+       },
+
+       .release = dst_release,
+
+       .init = dst_init,
+
+       .set_frontend = dst_set_frontend,
+       .get_frontend = dst_get_frontend,
+
+       .read_status = dst_read_status,
+       .read_signal_strength = dst_read_signal_strength,
+       .read_snr = dst_read_snr,
+};
+
+MODULE_DESCRIPTION("DST DVB-S/T/C Combo Frontend driver");
+MODULE_AUTHOR("Jamie Honan");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dst_attach);
diff --git a/drivers/media/dvb/bt8xx/dst.h b/drivers/media/dvb/bt8xx/dst.h
new file mode 100644 (file)
index 0000000..bcb418c
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+    Frontend-driver for TwinHan DST Frontend
+
+    Copyright (C) 2003 Jamie Honan
+
+    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 DST_H
+#define DST_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/device.h>
+#include "bt878.h"
+
+struct dst_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+};
+
+extern struct dvb_frontend* dst_attach(const struct dst_config* config,
+                                      struct i2c_adapter* i2c,
+                                      struct bt878 *bt);
+
+#endif // DST_H
diff --git a/drivers/media/dvb/bt8xx/dst_priv.h b/drivers/media/dvb/bt8xx/dst_priv.h
new file mode 100644 (file)
index 0000000..b3d5e6f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * dst-bt878.h: part of the DST driver for the TwinHan DST Frontend
+ *
+ * Copyright (C) 2003 Jamie Honan
+ */
+
+struct dst_gpio_enable {
+       u32     mask;
+       u32     enable;
+};
+
+struct dst_gpio_output {
+       u32     mask;
+       u32     highvals;
+};
+
+struct dst_gpio_read {
+       unsigned long value;
+};
+
+union dst_gpio_packet {
+       struct dst_gpio_enable enb;
+       struct dst_gpio_output outp;
+       struct dst_gpio_read rd;
+       int    psize;
+};
+
+#define DST_IG_ENABLE  0
+#define DST_IG_WRITE   1
+#define DST_IG_READ    2
+#define DST_IG_TS       3
+
+struct bt878;
+
+int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp);
+
+struct bt878 *bt878_find_by_i2c_adap(struct i2c_adapter *adap);
diff --git a/drivers/media/dvb/cinergyT2/Kconfig b/drivers/media/dvb/cinergyT2/Kconfig
new file mode 100644 (file)
index 0000000..2267140
--- /dev/null
@@ -0,0 +1,85 @@
+config DVB_CINERGYT2
+       tristate "Terratec CinergyT2/qanu USB2 DVB-T receiver"
+       depends on DVB_CORE && USB
+       help
+         Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers
+
+         Say Y if you own such a device and want to use it.
+
+
+config DVB_CINERGYT2_TUNING
+       bool "sophisticated fine-tuning for CinergyT2 cards"
+       depends on DVB_CINERGYT2
+       help
+         Here you can fine-tune some parameters of the CinergyT2 driver.
+
+         Normally you don't need to touch this, but in exotic setups you
+         may fine-tune your setup and adjust e.g. DMA buffer sizes for
+         a particular application.
+
+
+config DVB_CINERGYT2_STREAM_URB_COUNT
+       int "Number of queued USB Request Blocks for Highspeed Stream Transfers"
+       depends on DVB_CINERGYT2_TUNING
+        default "32"
+       help
+         USB Request Blocks for Highspeed Stream transfers are scheduled in
+         a queue for the Host Controller.
+
+         Usually the default value is a safe choice.
+
+         You may increase this number if you are using this device in a 
+         Server Environment with many high-traffic USB Highspeed devices 
+         sharing the same USB bus.
+
+
+config DVB_CINERGYT2_STREAM_BUF_SIZE
+       int "Size of URB Stream Buffers for Highspeed Transfers"
+       depends on DVB_CINERGYT2_TUNING
+        default "512"
+       help
+         Should be a multiple of native buffer size of 512 bytes.
+         Default value is a safe choice.
+
+         You may increase this number if you are using this device in a 
+         Server Environment with many high-traffic USB Highspeed devices 
+         sharing the same USB bus.
+
+
+config DVB_CINERGYT2_QUERY_INTERVAL
+       int "Status update interval [milliseconds]"
+       depends on DVB_CINERGYT2_TUNING
+        default "250"
+       help
+         This is the interval for status readouts from the demodulator.
+         You may try lower values if you need more responsive signal quality
+         measurements.
+
+         Please keep in mind that these updates cause traffic on the tuner
+         control bus and thus may or may not affect receiption sensitivity.
+
+         The default value should be a safe choice for common applications.
+
+
+config DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
+       bool "Register the onboard IR Remote Control Receiver as Input Device"
+       depends on DVB_CINERGYT2_TUNING
+        default "yes"
+       help
+         Enable this option if you want to use the onboard Infrared Remote 
+         Control Receiver as Linux-Input device.
+
+         Right now only the keycode table for the default Remote Control
+         delivered with the device is supported, please see the driver
+         source code to find out how to add support for other controls.
+
+
+config DVB_CINERGYT2_RC_QUERY_INTERVAL
+       int "Infrared Remote Controller update interval [milliseconds]"
+       depends on DVB_CINERGYT2_TUNING && DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
+        default "100"
+       help
+         If you have a very fast-repeating remote control you can try lower
+         values, for normal consumer receivers the default value should be
+         a safe choice.
+
diff --git a/drivers/media/dvb/cinergyT2/Makefile b/drivers/media/dvb/cinergyT2/Makefile
new file mode 100644 (file)
index 0000000..c51aece
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_CINERGYT2) += cinergyT2.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
new file mode 100644 (file)
index 0000000..fc17763
--- /dev/null
@@ -0,0 +1,973 @@
+/*
+ * TerraTec Cinergy T²/qanu USB2 DVB-T adapter.
+ *
+ * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
+ *                 Holger Waechtler <holger@qanu.de>
+ *
+ *  Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.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 <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/pci.h>
+#include <linux/input.h>
+#include <linux/dvb/frontend.h>
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+
+
+
+
+
+
+
+
+
+#ifdef CONFIG_DVB_CINERGYT2_TUNING
+       #define STREAM_URB_COUNT (CONFIG_DVB_CINERGYT2_STREAM_URB_COUNT)
+       #define STREAM_BUF_SIZE (CONFIG_DVB_CINERGYT2_STREAM_BUF_SIZE)
+       #define QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_QUERY_INTERVAL)
+       #ifdef CONFIG_DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
+               #define RC_QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_RC_QUERY_INTERVAL)
+               #define ENABLE_RC (1)
+       #endif
+#else
+       #define STREAM_URB_COUNT (32)
+       #define STREAM_BUF_SIZE (512)   /* bytes */
+       #define ENABLE_RC (1)
+       #define RC_QUERY_INTERVAL (100) /* milliseconds */
+       #define QUERY_INTERVAL (333)    /* milliseconds */
+#endif
+
+#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"
+
+static int debug;
+module_param_named(debug, debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(level, args...) \
+do {                                                                   \
+       if ((debug & level)) {                                          \
+               printk("%s: %s(): ", __stringify(KBUILD_MODNAME),       \
+                      __FUNCTION__);                                   \
+               printk(args); }                                         \
+} while (0)
+
+enum cinergyt2_ep1_cmd {
+       CINERGYT2_EP1_PID_TABLE_RESET           = 0x01,
+       CINERGYT2_EP1_PID_SETUP                 = 0x02,
+       CINERGYT2_EP1_CONTROL_STREAM_TRANSFER   = 0x03,
+       CINERGYT2_EP1_SET_TUNER_PARAMETERS      = 0x04,
+       CINERGYT2_EP1_GET_TUNER_STATUS          = 0x05,
+       CINERGYT2_EP1_START_SCAN                = 0x06,
+       CINERGYT2_EP1_CONTINUE_SCAN             = 0x07,
+       CINERGYT2_EP1_GET_RC_EVENTS             = 0x08,
+       CINERGYT2_EP1_SLEEP_MODE                = 0x09
+};
+
+struct dvbt_set_parameters_msg {
+       uint8_t cmd;
+       uint32_t freq;
+       uint8_t bandwidth;
+       uint16_t tps;
+       uint8_t flags;
+} __attribute__((packed));
+
+struct dvbt_get_status_msg {
+       uint32_t freq;
+       uint8_t bandwidth;
+       uint16_t tps;
+       uint8_t flags;
+       uint16_t gain;
+       uint8_t snr;
+       uint32_t viterbi_error_rate;
+       uint32_t rs_error_rate;
+       uint32_t uncorrected_block_count;
+       uint8_t lock_bits;
+       uint8_t prev_lock_bits;
+} __attribute__((packed));
+
+static struct dvb_frontend_info cinergyt2_fe_info = {
+       .name = DRIVER_NAME,
+       .type = FE_OFDM,
+       .frequency_min = 174000000,
+       .frequency_max = 862000000,
+       .frequency_stepsize = 166667,
+       .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
+               FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+               FE_CAN_FEC_AUTO |
+               FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+               FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+               FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_MUTE_TS
+};
+
+struct cinergyt2 {
+       struct dvb_demux demux;
+       struct usb_device *udev;
+       struct semaphore sem;
+       struct dvb_adapter *adapter;
+       struct dvb_device *fedev;
+       struct dmxdev dmxdev;
+       struct dvb_net dvbnet;
+
+       int streaming;
+       int sleeping;
+
+       struct dvbt_set_parameters_msg param;
+       struct dvbt_get_status_msg status;
+       struct work_struct query_work;
+
+       wait_queue_head_t poll_wq;
+       int pending_fe_events;
+
+       void *streambuf;
+       dma_addr_t streambuf_dmahandle;
+       struct urb *stream_urb[STREAM_URB_COUNT];
+
+#ifdef ENABLE_RC
+       struct input_dev rc_input_dev;
+       struct work_struct rc_query_work;
+       int rc_input_event;
+#endif
+};
+
+enum {
+       CINERGYT2_RC_EVENT_TYPE_NONE = 0x00,
+       CINERGYT2_RC_EVENT_TYPE_NEC  = 0x01,
+       CINERGYT2_RC_EVENT_TYPE_RC5  = 0x02
+};
+
+struct cinergyt2_rc_event {
+       char type;
+       uint32_t value;
+} __attribute__((packed));
+
+static const uint32_t rc_keys [] = {
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xfe01eb04,     KEY_POWER,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xfd02eb04,     KEY_1,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xfc03eb04,     KEY_2,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xfb04eb04,     KEY_3,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xfa05eb04,     KEY_4,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xf906eb04,     KEY_5,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xf807eb04,     KEY_6,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xf708eb04,     KEY_7,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xf609eb04,     KEY_8,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xf50aeb04,     KEY_9,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xf30ceb04,     KEY_0,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xf40beb04,     KEY_VIDEO,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xf20deb04,     KEY_REFRESH,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xf10eeb04,     KEY_SELECT,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xf00feb04,     KEY_EPG,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xef10eb04,     KEY_UP,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xeb14eb04,     KEY_DOWN,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xee11eb04,     KEY_LEFT,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xec13eb04,     KEY_RIGHT,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xed12eb04,     KEY_OK, 
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xea15eb04,     KEY_TEXT,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xe916eb04,     KEY_INFO,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xe817eb04,     KEY_RED,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xe718eb04,     KEY_GREEN,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xe619eb04,     KEY_YELLOW,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xe51aeb04,     KEY_BLUE,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xe31ceb04,     KEY_VOLUMEUP,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xe11eeb04,     KEY_VOLUMEDOWN,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xe21deb04,     KEY_MUTE,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xe41beb04,     KEY_CHANNELUP,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xe01feb04,     KEY_CHANNELDOWN,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xbf40eb04,     KEY_PAUSE,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xb34ceb04,     KEY_PLAY,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xa758eb04,     KEY_RECORD,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xab54eb04,     KEY_PREVIOUS,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xb748eb04,     KEY_STOP,
+       CINERGYT2_RC_EVENT_TYPE_NEC,    0xa35ceb04,     KEY_NEXT
+};
+
+static int cinergyt2_command (struct cinergyt2 *cinergyt2,
+                   char *send_buf, int send_buf_len,
+                             char *recv_buf, int recv_buf_len)
+{
+       int actual_len;
+       char dummy;
+       int ret;
+
+       ret = usb_bulk_msg(cinergyt2->udev, usb_sndbulkpipe(cinergyt2->udev, 1),
+                          send_buf, send_buf_len, &actual_len, HZ);
+
+       if (ret)
+               dprintk(1, "usb_bulk_msg (send) failed, err %i\n", ret);
+
+       if (!recv_buf)
+               recv_buf = &dummy;
+
+       ret = usb_bulk_msg(cinergyt2->udev, usb_rcvbulkpipe(cinergyt2->udev, 1),
+                          recv_buf, recv_buf_len, &actual_len, HZ);
+
+       if (ret)
+               dprintk(1, "usb_bulk_msg (read) failed, err %i\n", ret);
+
+       return ret ? ret : actual_len;
+}
+
+static void cinergyt2_control_stream_transfer (struct cinergyt2 *cinergyt2, int enable)
+{
+       char buf [] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 };
+       cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0);
+}
+
+static void cinergyt2_sleep (struct cinergyt2 *cinergyt2, int sleep)
+{
+       char buf [] = { CINERGYT2_EP1_SLEEP_MODE, sleep ? 1 : 0 };
+       cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0);
+       cinergyt2->sleeping = sleep;
+}
+
+static void cinergyt2_stream_irq (struct urb *urb, struct pt_regs *regs);
+
+static int cinergyt2_submit_stream_urb (struct cinergyt2 *cinergyt2, struct urb *urb)
+{
+       int err;
+
+       usb_fill_bulk_urb(urb,
+                         cinergyt2->udev,
+                         usb_rcvbulkpipe(cinergyt2->udev, 0x2),
+                         urb->transfer_buffer,
+                         STREAM_BUF_SIZE,
+                         cinergyt2_stream_irq,
+                         cinergyt2);
+
+       if ((err = usb_submit_urb(urb, GFP_ATOMIC)))
+               dprintk(1, "urb submission failed (err = %i)!\n", err);
+
+       return err;
+}
+
+static void cinergyt2_stream_irq (struct urb *urb, struct pt_regs *regs)
+{
+       struct cinergyt2 *cinergyt2 = urb->context;
+
+       if (urb->actual_length > 0)
+               dvb_dmx_swfilter(&cinergyt2->demux,
+                                urb->transfer_buffer, urb->actual_length);
+
+       if (cinergyt2->streaming)
+               cinergyt2_submit_stream_urb(cinergyt2, urb);
+}
+
+static void cinergyt2_free_stream_urbs (struct cinergyt2 *cinergyt2)
+{
+       int i;
+
+       for (i=0; i<STREAM_URB_COUNT; i++)
+               if (cinergyt2->stream_urb[i])
+                       usb_free_urb(cinergyt2->stream_urb[i]);
+
+       pci_free_consistent(NULL, STREAM_URB_COUNT*STREAM_BUF_SIZE,
+                           cinergyt2->streambuf, cinergyt2->streambuf_dmahandle);
+}
+
+static int cinergyt2_alloc_stream_urbs (struct cinergyt2 *cinergyt2)
+{
+       int i;
+
+       cinergyt2->streambuf = pci_alloc_consistent(NULL, 
+                                             STREAM_URB_COUNT*STREAM_BUF_SIZE,
+                                             &cinergyt2->streambuf_dmahandle);
+       if (!cinergyt2->streambuf) {
+               dprintk(1, "failed to alloc consistent stream memory area, bailing out!\n");
+               return -ENOMEM;
+       }
+
+       memset(cinergyt2->streambuf, 0, STREAM_URB_COUNT*STREAM_BUF_SIZE);
+
+       for (i=0; i<STREAM_URB_COUNT; i++) {
+               struct urb *urb;        
+
+               if (!(urb = usb_alloc_urb(0, GFP_ATOMIC))) {
+                       dprintk(1, "failed to alloc consistent stream urbs, bailing out!\n");
+                       cinergyt2_free_stream_urbs(cinergyt2);
+                       return -ENOMEM;
+               }
+
+               urb->transfer_buffer = cinergyt2->streambuf + i * STREAM_BUF_SIZE;
+               urb->transfer_buffer_length = STREAM_BUF_SIZE;
+
+               cinergyt2->stream_urb[i] = urb;
+       }
+
+       return 0;
+}
+
+static void cinergyt2_stop_stream_xfer (struct cinergyt2 *cinergyt2)
+{
+       int i;
+
+       cinergyt2_control_stream_transfer(cinergyt2, 0);
+
+       for (i=0; i<STREAM_URB_COUNT; i++)
+               if (cinergyt2->stream_urb[i])
+                       usb_kill_urb(cinergyt2->stream_urb[i]);
+}
+
+static int cinergyt2_start_stream_xfer (struct cinergyt2 *cinergyt2)
+{
+       int i, err;
+
+       for (i=0; i<STREAM_URB_COUNT; i++) {
+               if ((err = cinergyt2_submit_stream_urb(cinergyt2, cinergyt2->stream_urb[i]))) {
+                       cinergyt2_stop_stream_xfer(cinergyt2);
+                       dprintk(1, "failed urb submission (%i: err = %i)!\n", i, err);
+                       return err;
+               }
+       }
+
+       cinergyt2_control_stream_transfer(cinergyt2, 1);
+       return 0;
+}
+
+static int cinergyt2_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+       struct dvb_demux *demux = dvbdmxfeed->demux;
+       struct cinergyt2 *cinergyt2 = demux->priv;
+
+       if (down_interruptible(&cinergyt2->sem))
+               return -ERESTARTSYS;
+       
+       if (cinergyt2->streaming == 0)
+               cinergyt2_start_stream_xfer(cinergyt2);
+
+       cinergyt2->streaming++;
+       up(&cinergyt2->sem);
+       return 0;
+}
+
+static int cinergyt2_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+       struct dvb_demux *demux = dvbdmxfeed->demux;
+       struct cinergyt2 *cinergyt2 = demux->priv;      
+
+       if (down_interruptible(&cinergyt2->sem))
+               return -ERESTARTSYS;
+
+       if (--cinergyt2->streaming == 0)
+               cinergyt2_stop_stream_xfer(cinergyt2);
+
+       up(&cinergyt2->sem);
+       return 0;
+}
+
+/**
+ *  convert linux-dvb frontend parameter set into TPS.
+ *  See ETSI ETS-300744, section 4.6.2, table 9 for details.
+ *
+ *  This function is probably reusable and may better get placed in a support
+ *  library.
+ *
+ *  We replace errornous fields by default TPS fields (the ones with value 0).
+ */
+static uint16_t compute_tps (struct dvb_frontend_parameters *p)
+{
+       struct dvb_ofdm_parameters *op = &p->u.ofdm;
+       uint16_t tps = 0;
+
+       switch (op->code_rate_HP) {
+               case FEC_2_3:
+                       tps |= (1 << 7);
+                       break;
+               case FEC_3_4:
+                       tps |= (2 << 7);
+                       break;
+               case FEC_5_6:
+                       tps |= (3 << 7);
+                       break;
+               case FEC_7_8:
+                       tps |= (4 << 7);
+                       break;
+               case FEC_1_2:
+               case FEC_AUTO:
+               default:
+                       /* tps |= (0 << 7) */;
+       }
+
+       switch (op->code_rate_LP) {
+               case FEC_2_3:
+                       tps |= (1 << 4);
+                       break;
+               case FEC_3_4:
+                       tps |= (2 << 4);
+                       break;
+               case FEC_5_6:
+                       tps |= (3 << 4);
+                       break;
+               case FEC_7_8:
+                       tps |= (4 << 4);
+                       break;
+               case FEC_1_2:
+               case FEC_AUTO:
+               default:
+                       /* tps |= (0 << 4) */;
+       }
+
+       switch (op->constellation) {
+               case QAM_16:
+                       tps |= (1 << 13);
+                       break;
+               case QAM_64:
+                       tps |= (2 << 13);
+                       break;
+               case QPSK:
+               default:
+                       /* tps |= (0 << 13) */;
+       }
+
+       switch (op->transmission_mode) {
+               case TRANSMISSION_MODE_8K:
+                       tps |= (1 << 0);
+                       break;
+               case TRANSMISSION_MODE_2K:
+               default:
+                       /* tps |= (0 << 0) */;
+       }
+
+       switch (op->guard_interval) {
+               case GUARD_INTERVAL_1_16:
+                       tps |= (1 << 2);
+                       break;
+               case GUARD_INTERVAL_1_8:
+                       tps |= (2 << 2);
+                       break;
+               case GUARD_INTERVAL_1_4:
+                       tps |= (3 << 2);
+                       break;
+               case GUARD_INTERVAL_1_32:
+               default:
+                       /* tps |= (0 << 2) */;
+       }
+
+       switch (op->hierarchy_information) {
+               case HIERARCHY_1:
+                       tps |= (1 << 10);
+                       break;
+               case HIERARCHY_2:
+                       tps |= (2 << 10);
+                       break;
+               case HIERARCHY_4:
+                       tps |= (3 << 10);
+                       break;
+               case HIERARCHY_NONE:
+               default:
+                       /* tps |= (0 << 10) */;
+       }
+
+       return tps;
+}
+
+static int cinergyt2_open (struct inode *inode, struct file *file)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct cinergyt2 *cinergyt2 = dvbdev->priv;
+       int err;
+
+       if ((err = dvb_generic_open(inode, file)))
+               return err;
+
+       if (down_interruptible(&cinergyt2->sem))
+               return -ERESTARTSYS;
+
+       if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+               cinergyt2_sleep(cinergyt2, 0);
+               schedule_delayed_work(&cinergyt2->query_work, HZ/2);
+}
+
+       up(&cinergyt2->sem);
+       return 0;
+}
+
+static int cinergyt2_release (struct inode *inode, struct file *file)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct cinergyt2 *cinergyt2 = dvbdev->priv;
+
+       if (down_interruptible(&cinergyt2->sem))
+               return -ERESTARTSYS;
+
+       if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+               cancel_delayed_work(&cinergyt2->query_work);
+               flush_scheduled_work();
+               cinergyt2_sleep(cinergyt2, 1);
+       }
+
+       up(&cinergyt2->sem);
+
+       return dvb_generic_release(inode, file);
+       }
+
+static unsigned int cinergyt2_poll (struct file *file, struct poll_table_struct *wait)
+       {
+       struct dvb_device *dvbdev = file->private_data;
+       struct cinergyt2 *cinergyt2 = dvbdev->priv;
+       poll_wait(file, &cinergyt2->poll_wq, wait);
+       return (POLLIN | POLLRDNORM | POLLPRI);
+       }
+
+
+static int cinergyt2_ioctl (struct inode *inode, struct file *file,
+                    unsigned cmd, unsigned long arg)
+       {
+       struct dvb_device *dvbdev = file->private_data;
+       struct cinergyt2 *cinergyt2 = dvbdev->priv;
+       struct dvbt_get_status_msg *stat = &cinergyt2->status;
+       fe_status_t status = 0;
+
+       switch (cmd) {
+       case FE_GET_INFO:
+               return copy_to_user((void*) arg, &cinergyt2_fe_info,
+                                   sizeof(struct dvb_frontend_info));
+
+       case FE_READ_STATUS:
+               if (0xffff - le16_to_cpu(stat->gain) > 30)
+                       status |= FE_HAS_SIGNAL;
+               if (stat->lock_bits & (1 << 6))
+                       status |= FE_HAS_LOCK;
+               if (stat->lock_bits & (1 << 5))
+                       status |= FE_HAS_SYNC;
+               if (stat->lock_bits & (1 << 4))
+                       status |= FE_HAS_CARRIER;
+               if (stat->lock_bits & (1 << 1))
+                       status |= FE_HAS_VITERBI;
+
+               return copy_to_user((void *) arg, &status, sizeof(status));
+
+       case FE_READ_BER:
+               return put_user(le32_to_cpu(stat->viterbi_error_rate),
+                               (__u32 __user *) arg);
+
+       case FE_READ_SIGNAL_STRENGTH:
+               return put_user(0xffff - le16_to_cpu(stat->gain),
+                               (__u16 __user *) arg);
+
+       case FE_READ_SNR:
+               return put_user((stat->snr << 8) | stat->snr,
+                               (__u16 __user *) arg);
+
+       case FE_READ_UNCORRECTED_BLOCKS:
+               /* UNC are already converted to host byte order... */
+               return put_user(stat->uncorrected_block_count,
+                               (__u32 __user *) arg);
+       
+       case FE_SET_FRONTEND:
+       {
+               struct dvbt_set_parameters_msg *param = &cinergyt2->param;
+               struct dvb_frontend_parameters p;
+               int err;
+
+               if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+                       return -EPERM;
+
+               if (copy_from_user(&p, (void *) arg, sizeof(p)))
+                       return -EFAULT;
+
+               if (down_interruptible(&cinergyt2->sem))
+                       return -ERESTARTSYS;
+
+               param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
+               param->tps = cpu_to_le16(compute_tps(&p));
+               param->freq = cpu_to_le32(p.frequency / 1000);
+               param->bandwidth = 8 - p.u.ofdm.bandwidth - BANDWIDTH_8_MHZ;
+
+               stat->lock_bits = 0;
+               cinergyt2->pending_fe_events++;
+               wake_up_interruptible(&cinergyt2->poll_wq);
+
+               err = cinergyt2_command(cinergyt2,
+                                       (char *) param, sizeof(*param),
+                                       NULL, 0);
+
+               up(&cinergyt2->sem);
+
+               return (err < 0) ? err : 0;
+       }
+
+       case FE_GET_FRONTEND:
+               /**
+                *  trivial to implement (see struct dvbt_get_status_msg).
+                *  equivalent to FE_READ ioctls, but needs 
+                *  TPS -> linux-dvb parameter set conversion. Feel free
+                *  to implement this and send us a patch if you need this
+                *  functionality.
+                */
+               break;
+
+       case FE_GET_EVENT:
+       {
+               /**
+                *  for now we only fill the status field. the parameters
+                *  are trivial to fill as soon FE_GET_FRONTEND is done.
+                */
+               struct dvb_frontend_event *e = (void *) arg;
+               if (cinergyt2->pending_fe_events == 0) {
+                       if (file->f_flags & O_NONBLOCK)
+                               return -EWOULDBLOCK;
+                       wait_event_interruptible(cinergyt2->poll_wq,
+                                                cinergyt2->pending_fe_events > 0);
+               }
+               cinergyt2->pending_fe_events = 0;
+               return cinergyt2_ioctl(inode, file, FE_READ_STATUS,
+                                       (unsigned long) &e->status);
+       }
+
+       default:
+               ;
+       }
+
+       return -EINVAL;
+}
+
+static int cinergyt2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct dvb_device *dvbdev = file->private_data;
+       struct cinergyt2 *cinergyt2 = dvbdev->priv;
+       int ret = 0;
+
+       lock_kernel();
+
+       if (vma->vm_flags & (VM_WRITE | VM_EXEC)) {
+               ret = -EPERM;
+               goto bailout;
+       }
+
+       if (vma->vm_end > vma->vm_start + STREAM_URB_COUNT * STREAM_BUF_SIZE) {
+               ret = -EINVAL;
+               goto bailout;
+       }
+
+        vma->vm_flags |= (VM_IO | VM_DONTCOPY);
+       vma->vm_file = file;
+
+       ret = remap_pfn_range(vma, vma->vm_start,
+                             virt_to_phys(cinergyt2->streambuf) >> PAGE_SHIFT,
+                             vma->vm_end - vma->vm_start,
+                             vma->vm_page_prot) ? -EAGAIN : 0;
+bailout:
+       unlock_kernel();
+       return ret;
+}
+
+static struct file_operations cinergyt2_fops = {
+       .owner          = THIS_MODULE,
+       .ioctl          = cinergyt2_ioctl,
+       .poll           = cinergyt2_poll,
+       .open           = cinergyt2_open,
+       .release        = cinergyt2_release,
+       .mmap           = cinergyt2_mmap
+};
+
+static struct dvb_device cinergyt2_fe_template = {
+       .users = ~0,
+       .writers = 1,
+       .readers = (~0)-1,
+       .fops = &cinergyt2_fops
+};
+
+#ifdef ENABLE_RC
+static void cinergyt2_query_rc (void *data)
+{
+       struct cinergyt2 *cinergyt2 = (struct cinergyt2 *) data;
+       char buf [1] = { CINERGYT2_EP1_GET_RC_EVENTS };
+       struct cinergyt2_rc_event rc_events[12];
+       int n, len;
+
+       if (down_interruptible(&cinergyt2->sem))
+               return;
+
+       len = cinergyt2_command(cinergyt2, buf, sizeof(buf), 
+                            (char *) rc_events, sizeof(rc_events));
+
+       for (n=0; len>0 && n<(len/sizeof(rc_events[0])); n++) {
+               int i;
+
+               if (rc_events[n].type == CINERGYT2_RC_EVENT_TYPE_NEC &&
+                   rc_events[n].value == ~0)
+               {
+                       /**
+                        * keyrepeat bit. If we would handle this properly
+                        * we would need to emit down events as long the
+                        * keyrepeat goes, a up event if no further 
+                        * repeat bits occur. Would need a timer to implement
+                        * and no other driver does this, so we simply
+                        * emit the last key up/down sequence again.
+                        */
+               } else {
+                       cinergyt2->rc_input_event = KEY_MAX;
+                       for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i+=3) {
+                               if (rc_keys[i+0] == rc_events[n].type &&
+                                   rc_keys[i+1] == rc_events[n].value)
+                               {
+                                       cinergyt2->rc_input_event = rc_keys[i+2];
+                                       break;
+                               }
+                       }
+               }
+
+               if (cinergyt2->rc_input_event != KEY_MAX) {
+                       input_report_key(&cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 1);
+                       input_report_key(&cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 0);
+                       input_sync(&cinergyt2->rc_input_dev);
+               }
+       }
+
+       schedule_delayed_work(&cinergyt2->rc_query_work,
+                             msecs_to_jiffies(RC_QUERY_INTERVAL));
+
+       up(&cinergyt2->sem);
+}
+#endif
+
+static void cinergyt2_query (void *data)
+{
+       struct cinergyt2 *cinergyt2 = (struct cinergyt2 *) data;
+       char cmd [] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+       struct dvbt_get_status_msg *s = &cinergyt2->status;
+       uint8_t lock_bits;
+       uint32_t unc;
+
+       if (down_interruptible(&cinergyt2->sem))
+               return;
+
+       unc = s->uncorrected_block_count;
+       lock_bits = s->lock_bits;
+
+       cinergyt2_command(cinergyt2, cmd, sizeof(cmd), (char *) s, sizeof(*s));
+
+       unc += le32_to_cpu(s->uncorrected_block_count);
+       s->uncorrected_block_count = unc;
+
+       if (lock_bits != s->lock_bits) {
+               wake_up_interruptible(&cinergyt2->poll_wq);
+               cinergyt2->pending_fe_events++;
+       }
+
+       schedule_delayed_work(&cinergyt2->query_work,
+                             msecs_to_jiffies(QUERY_INTERVAL));
+
+       up(&cinergyt2->sem);
+}
+
+static int cinergyt2_probe (struct usb_interface *intf,
+                 const struct usb_device_id *id)
+{
+       struct cinergyt2 *cinergyt2;
+       int i, err;
+
+       if (!(cinergyt2 = kmalloc (sizeof(struct cinergyt2), GFP_KERNEL))) {
+               dprintk(1, "out of memory?!?\n");
+               return -ENOMEM;
+       }
+
+       memset (cinergyt2, 0, sizeof (struct cinergyt2));
+       usb_set_intfdata (intf, (void *) cinergyt2);
+
+       init_MUTEX(&cinergyt2->sem);
+       init_waitqueue_head (&cinergyt2->poll_wq);
+       INIT_WORK(&cinergyt2->query_work, cinergyt2_query, cinergyt2);
+
+       cinergyt2->udev = interface_to_usbdev(intf);
+       cinergyt2->param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
+       
+       if (cinergyt2_alloc_stream_urbs (cinergyt2) < 0) {
+               dprintk(1, "unable to allocate stream urbs\n");
+               kfree(cinergyt2);
+               return -ENOMEM;
+       }
+
+       dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE);
+
+       cinergyt2->demux.priv = cinergyt2;
+       cinergyt2->demux.filternum = 256;
+       cinergyt2->demux.feednum = 256;
+       cinergyt2->demux.start_feed = cinergyt2_start_feed;
+       cinergyt2->demux.stop_feed = cinergyt2_stop_feed;
+       cinergyt2->demux.dmx.capabilities = DMX_TS_FILTERING |
+                                        DMX_SECTION_FILTERING |
+                                        DMX_MEMORY_BASED_FILTERING;
+
+       if ((err = dvb_dmx_init(&cinergyt2->demux)) < 0) {
+               dprintk(1, "dvb_dmx_init() failed (err = %d)\n", err);
+               goto bailout;
+       }
+
+       cinergyt2->dmxdev.filternum = cinergyt2->demux.filternum;
+       cinergyt2->dmxdev.demux = &cinergyt2->demux.dmx;
+       cinergyt2->dmxdev.capabilities = 0;
+
+       if ((err = dvb_dmxdev_init(&cinergyt2->dmxdev, cinergyt2->adapter)) < 0) {
+               dprintk(1, "dvb_dmxdev_init() failed (err = %d)\n", err);
+               goto bailout;
+       }
+
+       if (dvb_net_init(cinergyt2->adapter, &cinergyt2->dvbnet, &cinergyt2->demux.dmx))
+               dprintk(1, "dvb_net_init() failed!\n");
+
+       dvb_register_device(cinergyt2->adapter, &cinergyt2->fedev,
+                           &cinergyt2_fe_template, cinergyt2,
+                           DVB_DEVICE_FRONTEND);
+
+#ifdef ENABLE_RC
+       init_input_dev(&cinergyt2->rc_input_dev);                       
+
+       cinergyt2->rc_input_dev.evbit[0] = BIT(EV_KEY);
+       cinergyt2->rc_input_dev.keycodesize = sizeof(unsigned char);
+       cinergyt2->rc_input_dev.keycodemax = KEY_MAX;
+       cinergyt2->rc_input_dev.name = DRIVER_NAME " remote control";
+
+       for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i+=3)
+               set_bit(rc_keys[i+2], cinergyt2->rc_input_dev.keybit);
+
+       input_register_device(&cinergyt2->rc_input_dev);
+
+       cinergyt2->rc_input_event = KEY_MAX;
+       
+       INIT_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc, cinergyt2);
+       schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
+#endif
+       return 0;
+
+bailout:
+       dvb_dmxdev_release(&cinergyt2->dmxdev);
+       dvb_dmx_release(&cinergyt2->demux);
+       dvb_unregister_adapter (cinergyt2->adapter);
+       cinergyt2_free_stream_urbs (cinergyt2);
+       kfree(cinergyt2);
+       return -ENOMEM;
+}
+
+static void cinergyt2_disconnect (struct usb_interface *intf)
+{
+       struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+
+       if (down_interruptible(&cinergyt2->sem))
+               return;
+
+#ifdef ENABLE_RC
+       cancel_delayed_work(&cinergyt2->rc_query_work);
+       flush_scheduled_work();
+       input_unregister_device(&cinergyt2->rc_input_dev);
+#endif
+
+       cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx);
+       dvb_net_release(&cinergyt2->dvbnet);
+       dvb_dmxdev_release(&cinergyt2->dmxdev);
+       dvb_dmx_release(&cinergyt2->demux);
+
+       dvb_unregister_device(cinergyt2->fedev);
+       dvb_unregister_adapter(cinergyt2->adapter);
+
+       cinergyt2_free_stream_urbs(cinergyt2);
+       up(&cinergyt2->sem);
+       kfree(cinergyt2);
+}
+
+static int cinergyt2_suspend (struct usb_interface *intf, u32 state)
+{
+       struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+
+       if (down_interruptible(&cinergyt2->sem))
+               return -ERESTARTSYS;
+
+       if (state > 0) {        /* state 0 seems to mean DEVICE_PM_ON */
+               struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+#ifdef ENABLE_RC
+               cancel_delayed_work(&cinergyt2->rc_query_work);
+#endif
+               cancel_delayed_work(&cinergyt2->query_work);
+               if (cinergyt2->streaming)
+                       cinergyt2_stop_stream_xfer(cinergyt2);
+               flush_scheduled_work();
+               cinergyt2_sleep(cinergyt2, 1);
+       }
+
+       up(&cinergyt2->sem);
+       return 0;
+}
+
+static int cinergyt2_resume (struct usb_interface *intf)
+{
+       struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+       struct dvbt_set_parameters_msg *param = &cinergyt2->param;
+
+       if (down_interruptible(&cinergyt2->sem))
+               return -ERESTARTSYS;
+
+       if (!cinergyt2->sleeping) {
+               cinergyt2_sleep(cinergyt2, 0);
+               cinergyt2_command(cinergyt2, (char *) param, sizeof(*param), NULL, 0);
+               if (cinergyt2->streaming)
+                       cinergyt2_start_stream_xfer(cinergyt2);
+               schedule_delayed_work(&cinergyt2->query_work, HZ/2);
+       }
+
+#ifdef ENABLE_RC
+       schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
+#endif
+       up(&cinergyt2->sem);
+       return 0;
+}
+
+static const struct usb_device_id cinergyt2_table [] __devinitdata = {
+       { USB_DEVICE(0x0ccd, 0x0038) },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(usb, cinergyt2_table);
+
+static struct usb_driver cinergyt2_driver = {
+       .owner          = THIS_MODULE,
+       .name   = "cinergyT2",
+       .probe          = cinergyt2_probe,
+       .disconnect     = cinergyt2_disconnect,
+       .suspend        = cinergyt2_suspend,
+       .resume         = cinergyt2_resume,
+       .id_table       = cinergyt2_table
+};
+
+static int __init cinergyt2_init (void)
+{
+       int err;
+
+       if ((err = usb_register(&cinergyt2_driver)) < 0)
+               dprintk(1, "usb_register() failed! (err %i)\n", err);
+
+       return err;
+}
+
+static void __exit cinergyt2_exit (void)
+{
+       usb_deregister(&cinergyt2_driver);
+}
+
+module_init (cinergyt2_init);
+module_exit (cinergyt2_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Holger Waechtler, Daniel Mack");
+
diff --git a/drivers/media/dvb/dibusb/Kconfig b/drivers/media/dvb/dibusb/Kconfig
new file mode 100644 (file)
index 0000000..7f73bbb
--- /dev/null
@@ -0,0 +1,57 @@
+config DVB_DIBUSB
+       tristate "DiBcom USB DVB-T devices (see help for device list)"
+       depends on DVB_CORE && USB
+       select FW_LOADER
+       select DVB_DIB3000MB
+       select DVB_DIB3000MC
+       help
+         Support for USB 1.1 and 2.0 DVB-T devices based on reference designs made by
+         DiBcom (http://www.dibcom.fr).
+
+         Devices supported by this driver:
+
+           TwinhanDTV USB-Ter (VP7041)
+               TwinhanDTV Magic Box (VP7041e)
+           KWorld V-Stream XPERT DTV - DVB-T USB
+           Hama DVB-T USB-Box
+           DiBcom reference device (non-public)
+           Ultima Electronic/Artec T1 USB TVBOX
+           Compro Videomate DVB-U2000 - DVB-T USB
+           Grandtec DVB-T USB
+           Avermedia AverTV DVBT USB
+           Yakumo DVB-T mobile USB2.0
+
+         The VP7041 seems to be identical to "CTS Portable" (Chinese
+         Television System).
+
+         These devices can be understood as budget ones, they "only" deliver
+         (a part of) the MPEG2 transport stream.
+
+         A firmware is needed to get the device working. See Documentation/dvb/README.dibusb
+         details.
+
+         Say Y if you own such a device and want to use it. You should build it as
+         a module.
+
+config DVB_DIBUSB_MISDESIGNED_DEVICES
+       bool "Enable support for some misdesigned (see help) devices, which identify with wrong IDs"
+       depends on DVB_DIBUSB
+       help
+         Somehow Artec/Ultima Electronic forgot to program the eeprom of some of their
+         USB1.1/USB2.0 devices.
+         So comes that they identify with the default Vendor and Product ID of the Cypress
+         CY7C64613 (AN2235) or Cypress FX2.
+
+         Affected device IDs:
+           0x0574:0x2235 (Artec T1 USB1.1, cold)
+           0x04b4:0x8613 (Artec T1 USB2.0, cold)
+           0x0574:0x1002 (Artec T1 USB2.0, warm)
+
+         Say Y if your device has one of the mentioned IDs.
+
+config DVB_DIBCOM_DEBUG
+       bool "Enable extended debug support for DiBcom USB device"
+       depends on DVB_DIBUSB
+       help
+         Say Y if you want to enable debuging. See modinfo dvb-dibusb for
+         debug levels.
diff --git a/drivers/media/dvb/dibusb/Makefile b/drivers/media/dvb/dibusb/Makefile
new file mode 100644 (file)
index 0000000..ab990cd
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_DIBUSB) += dvb-dibusb.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-core.c b/drivers/media/dvb/dibusb/dvb-dibusb-core.c
new file mode 100644 (file)
index 0000000..503ca57
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * Driver for mobile USB Budget DVB-T devices based on reference 
+ * design made by DiBcom (http://www.dibcom.fr/)
+ * 
+ * dvb-dibusb-core.c
+ * 
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * 
+ * based on GPL code from DiBcom, which has
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ *
+ * Remote control code added by David Matthews (dm@prolingua.co.uk)
+ * 
+ *     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, version 2.
+ *
+ * Acknowledgements
+ * 
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dib3000mb/mc/p frontends) are based.
+ * 
+ * see Documentation/dvb/README.dibusb for more information
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/moduleparam.h>
+
+/* debug */
+int dvb_dibusb_debug;
+module_param_named(debug, dvb_dibusb_debug,  int, 0644);
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#define DBSTATUS ""
+#else
+#define DBSTATUS " (debugging is not enabled)"
+#endif
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=alotmore,8=ts,16=err,32=rc (|-able))." DBSTATUS);
+#undef DBSTATUS
+
+static int pid_parse;
+module_param(pid_parse, int, 0644);
+MODULE_PARM_DESC(pid_parse, "enable pid parsing (filtering) when running at USB2.0");
+
+static int rc_query_interval;
+module_param(rc_query_interval, int, 0644);
+MODULE_PARM_DESC(rc_query_interval, "interval in msecs for remote control query (default: 100; min: 40)");
+
+/* Vendor IDs */
+#define USB_VID_ANCHOR                                         0x0547
+#define USB_VID_AVERMEDIA                                      0x14aa
+#define USB_VID_COMPRO                                         0x185b
+#define USB_VID_COMPRO_UNK                                     0x145f
+#define USB_VID_CYPRESS                                                0x04b4
+#define USB_VID_DIBCOM                                         0x10b8
+#define USB_VID_EMPIA                                          0xeb1a
+#define USB_VID_GRANDTEC                                       0x5032
+#define USB_VID_HYPER_PALTEK                           0x1025
+#define USB_VID_HANFTEK                                                0x15f4
+#define USB_VID_IMC_NETWORKS                           0x13d3
+#define USB_VID_TWINHAN                                                0x1822
+#define USB_VID_ULTIMA_ELECTRONIC                      0x05d8
+
+/* Product IDs */
+#define USB_PID_AVERMEDIA_DVBT_USB_COLD                0x0001
+#define USB_PID_AVERMEDIA_DVBT_USB_WARM                0x0002
+#define USB_PID_COMPRO_DVBU2000_COLD           0xd000
+#define USB_PID_COMPRO_DVBU2000_WARM           0xd001
+#define USB_PID_COMPRO_DVBU2000_UNK_COLD       0x010c
+#define USB_PID_COMPRO_DVBU2000_UNK_WARM       0x010d
+#define USB_PID_DIBCOM_MOD3000_COLD                    0x0bb8
+#define USB_PID_DIBCOM_MOD3000_WARM                    0x0bb9
+#define USB_PID_DIBCOM_MOD3001_COLD                    0x0bc6
+#define USB_PID_DIBCOM_MOD3001_WARM                    0x0bc7
+#define USB_PID_DIBCOM_ANCHOR_2135_COLD                0x2131
+#define USB_PID_GRANDTEC_DVBT_USB_COLD         0x0fa0
+#define USB_PID_GRANDTEC_DVBT_USB_WARM         0x0fa1
+#define USB_PID_KWORLD_VSTREAM_COLD                    0x17de
+#define USB_PID_KWORLD_VSTREAM_WARM                    0x17df
+#define USB_PID_TWINHAN_VP7041_COLD                    0x3201
+#define USB_PID_TWINHAN_VP7041_WARM                    0x3202
+#define USB_PID_ULTIMA_TVBOX_COLD                      0x8105
+#define USB_PID_ULTIMA_TVBOX_WARM                      0x8106
+#define USB_PID_ULTIMA_TVBOX_AN2235_COLD       0x8107
+#define USB_PID_ULTIMA_TVBOX_AN2235_WARM       0x8108
+#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD       0x2235
+#define USB_PID_ULTIMA_TVBOX_USB2_COLD         0x8109
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD      0x8613
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM      0x1002
+#define USB_PID_UNK_HYPER_PALTEK_COLD          0x005e
+#define USB_PID_UNK_HYPER_PALTEK_WARM          0x005f
+#define USB_PID_HANFTEK_UMT_010_COLD           0x0001
+#define USB_PID_HANFTEK_UMT_010_WARM           0x0025
+#define USB_PID_YAKUMO_DTT200U_COLD                    0x0201
+#define USB_PID_YAKUMO_DTT200U_WARM                    0x0301
+
+/* USB Driver stuff
+ * table of devices that this driver is working with
+ *
+ * ATTENTION: Never ever change the order of this table, the particular 
+ * devices depend on this order 
+ *
+ * Each entry is used as a reference in the device_struct. Currently this is 
+ * the only non-redundant way of assigning USB ids to actual devices I'm aware 
+ * of, because there is only one place in the code where the assignment of 
+ * vendor and product id is done, here.
+ */
+static struct usb_device_id dib_table [] = {
+/* 00 */       { USB_DEVICE(USB_VID_AVERMEDIA,         USB_PID_AVERMEDIA_DVBT_USB_COLD)},
+/* 01 */       { USB_DEVICE(USB_VID_AVERMEDIA,         USB_PID_AVERMEDIA_DVBT_USB_WARM)},
+/* 02 */       { USB_DEVICE(USB_VID_AVERMEDIA,         USB_PID_YAKUMO_DTT200U_COLD) },
+
+/* the following device is actually not supported, but when loading the 
+ * correct firmware (ie. its usb ids will change) everything works fine then 
+ */
+/* 03 */       { USB_DEVICE(USB_VID_AVERMEDIA,         USB_PID_YAKUMO_DTT200U_WARM) },
+
+/* 04 */       { USB_DEVICE(USB_VID_COMPRO,            USB_PID_COMPRO_DVBU2000_COLD) },
+/* 05 */       { USB_DEVICE(USB_VID_COMPRO,            USB_PID_COMPRO_DVBU2000_WARM) },
+/* 06 */       { USB_DEVICE(USB_VID_COMPRO_UNK,        USB_PID_COMPRO_DVBU2000_UNK_COLD) },
+/* 07 */       { USB_DEVICE(USB_VID_DIBCOM,            USB_PID_DIBCOM_MOD3000_COLD) },
+/* 08 */       { USB_DEVICE(USB_VID_DIBCOM,            USB_PID_DIBCOM_MOD3000_WARM) },
+/* 09 */       { USB_DEVICE(USB_VID_DIBCOM,            USB_PID_DIBCOM_MOD3001_COLD) },
+/* 10 */       { USB_DEVICE(USB_VID_DIBCOM,            USB_PID_DIBCOM_MOD3001_WARM) },
+/* 11 */       { USB_DEVICE(USB_VID_EMPIA,                     USB_PID_KWORLD_VSTREAM_COLD) },
+/* 12 */       { USB_DEVICE(USB_VID_EMPIA,                     USB_PID_KWORLD_VSTREAM_WARM) },
+/* 13 */       { USB_DEVICE(USB_VID_GRANDTEC,          USB_PID_GRANDTEC_DVBT_USB_COLD) },
+/* 14 */       { USB_DEVICE(USB_VID_GRANDTEC,          USB_PID_GRANDTEC_DVBT_USB_WARM) },
+/* 15 */       { USB_DEVICE(USB_VID_GRANDTEC,          USB_PID_DIBCOM_MOD3000_COLD) },
+/* 16 */       { USB_DEVICE(USB_VID_GRANDTEC,          USB_PID_DIBCOM_MOD3000_WARM) },
+/* 17 */       { USB_DEVICE(USB_VID_HYPER_PALTEK,      USB_PID_UNK_HYPER_PALTEK_COLD) },
+/* 18 */       { USB_DEVICE(USB_VID_HYPER_PALTEK,      USB_PID_UNK_HYPER_PALTEK_WARM) },
+/* 19 */       { USB_DEVICE(USB_VID_IMC_NETWORKS,      USB_PID_TWINHAN_VP7041_COLD) },
+/* 20 */       { USB_DEVICE(USB_VID_IMC_NETWORKS,      USB_PID_TWINHAN_VP7041_WARM) },
+/* 21 */       { USB_DEVICE(USB_VID_TWINHAN,           USB_PID_TWINHAN_VP7041_COLD) },
+/* 22 */       { USB_DEVICE(USB_VID_TWINHAN,           USB_PID_TWINHAN_VP7041_WARM) },
+/* 23 */       { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) },
+/* 24 */       { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) },
+/* 25 */       { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) },
+/* 26 */       { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) },
+/* 27 */       { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) },
+       
+/* 28 */       { USB_DEVICE(USB_VID_HANFTEK,           USB_PID_HANFTEK_UMT_010_COLD) },
+/* 29 */       { USB_DEVICE(USB_VID_HANFTEK,           USB_PID_HANFTEK_UMT_010_WARM) },
+
+/* 
+ * activate the following define when you have one of the devices and want to 
+ * build it from build-2.6 in dvb-kernel
+ */
+// #define CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES 
+#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
+/* 30 */       { USB_DEVICE(USB_VID_ANCHOR,            USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) },
+/* 31 */       { USB_DEVICE(USB_VID_CYPRESS,           USB_PID_ULTIMA_TVBOX_USB2_FX_COLD) },
+/* 32 */       { USB_DEVICE(USB_VID_ANCHOR,            USB_PID_ULTIMA_TVBOX_USB2_FX_WARM) },
+/* 33 */       { USB_DEVICE(USB_VID_ANCHOR,            USB_PID_DIBCOM_ANCHOR_2135_COLD) },
+#endif
+                       { }             /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, dib_table);
+
+static struct dibusb_usb_controller dibusb_usb_ctrl[] = {
+       { .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 },
+       { .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 },
+       { .name = "Cypress FX2",    .cpu_cs_register = 0xe600 },
+};
+
+struct dibusb_tuner dibusb_tuner[] = {
+       { DIBUSB_TUNER_CABLE_THOMSON, 
+         0x61 
+       },
+       { DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5,
+         0x60 
+       },
+       { DIBUSB_TUNER_CABLE_LG_TDTP_E102P,
+         0x61
+       },
+       { DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5,
+         0x60
+       },
+};
+
+static struct dibusb_demod dibusb_demod[] = {
+       { DIBUSB_DIB3000MB,
+         16,
+         { 0x8, 0 },
+       },
+       { DIBUSB_DIB3000MC,
+         32,
+         { 0x9, 0xa, 0xb, 0xc }, 
+       },
+       { DIBUSB_MT352,
+         254,
+         { 0xf, 0 }, 
+       },
+};
+
+static struct dibusb_device_class dibusb_device_classes[] = {
+       { .id = DIBUSB1_1, .usb_ctrl = &dibusb_usb_ctrl[0],
+         .firmware = "dvb-dibusb-5.0.0.11.fw",
+         .pipe_cmd = 0x01, .pipe_data = 0x02, 
+         .urb_count = 3, .urb_buffer_size = 4096,
+         DIBUSB_RC_NEC_PROTOCOL,
+         &dibusb_demod[DIBUSB_DIB3000MB],
+         &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
+       },
+       { DIBUSB1_1_AN2235, &dibusb_usb_ctrl[1],
+         "dvb-dibusb-an2235-1.fw",
+         0x01, 0x02, 
+         3, 4096,
+         DIBUSB_RC_NEC_PROTOCOL,
+         &dibusb_demod[DIBUSB_DIB3000MB],
+         &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
+       },
+       { DIBUSB2_0,&dibusb_usb_ctrl[2],
+         "dvb-dibusb-6.0.0.5.fw",
+         0x01, 0x06, 
+         3, 188*210,
+         DIBUSB_RC_NEC_PROTOCOL,
+         &dibusb_demod[DIBUSB_DIB3000MC],
+         &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5],
+       },
+       { UMT2_0, &dibusb_usb_ctrl[2],
+         "dvb-dibusb-umt-1.fw",
+         0x01, 0x02, 
+         15, 188*21,
+         DIBUSB_RC_NO,
+         &dibusb_demod[DIBUSB_MT352],
+//       &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5],
+         &dibusb_tuner[DIBUSB_TUNER_CABLE_LG_TDTP_E102P],
+       },
+};
+
+static struct dibusb_usb_device dibusb_devices[] = {
+       {       "TwinhanDTV USB1.1 / Magic Box / HAMA USB1.1 DVB-T device",
+               &dibusb_device_classes[DIBUSB1_1],
+               { &dib_table[19], &dib_table[21], NULL},
+               { &dib_table[20], &dib_table[22], NULL},
+       },
+       {       "KWorld V-Stream XPERT DTV - DVB-T USB1.1",
+               &dibusb_device_classes[DIBUSB1_1],
+               { &dib_table[11], NULL },
+               { &dib_table[12], NULL },
+       },
+       {       "Grandtec USB1.1 DVB-T",
+               &dibusb_device_classes[DIBUSB1_1],
+               { &dib_table[13], &dib_table[15], NULL },
+               { &dib_table[14], &dib_table[16], NULL },
+       },
+       {       "DiBcom USB1.1 DVB-T reference design (MOD3000)",
+               &dibusb_device_classes[DIBUSB1_1],
+               { &dib_table[7],  NULL },
+               { &dib_table[8],  NULL },
+       },
+       {       "Artec T1 USB1.1 TVBOX with AN2135",
+               &dibusb_device_classes[DIBUSB1_1],
+               { &dib_table[23], NULL },
+               { &dib_table[24], NULL },
+       },
+       {       "Artec T1 USB1.1 TVBOX with AN2235",
+               &dibusb_device_classes[DIBUSB1_1_AN2235],
+               { &dib_table[25], NULL },
+               { &dib_table[26], NULL },
+       },
+       {       "Avermedia AverTV DVBT USB1.1",
+               &dibusb_device_classes[DIBUSB1_1],
+               { &dib_table[0],  NULL },
+               { &dib_table[1],  NULL },
+       },
+       {       "Compro Videomate DVB-U2000 - DVB-T USB1.1 (please confirm to linux-dvb)",
+               &dibusb_device_classes[DIBUSB1_1],
+               { &dib_table[4], &dib_table[6], NULL},
+               { &dib_table[5], NULL },
+       },
+       {       "Unkown USB1.1 DVB-T device ???? please report the name to the author",
+               &dibusb_device_classes[DIBUSB1_1],
+               { &dib_table[17], NULL },
+               { &dib_table[18], NULL },
+       },
+       {       "DiBcom USB2.0 DVB-T reference design (MOD3000P)",
+               &dibusb_device_classes[DIBUSB2_0],
+               { &dib_table[9],  NULL },
+               { &dib_table[10], NULL },
+       },
+       {       "Artec T1 USB2.0 TVBOX (please report the warm ID)",
+               &dibusb_device_classes[DIBUSB2_0],
+               { &dib_table[27], NULL },
+               { NULL },
+       },
+       {       "AVermedia/Yakumo/Hama/Typhoon DVB-T USB2.0",
+               &dibusb_device_classes[UMT2_0],
+               { &dib_table[2], NULL },
+               { NULL },
+       },      
+       {       "Hanftek UMT-010 DVB-T USB2.0",
+               &dibusb_device_classes[UMT2_0],
+               { &dib_table[28], NULL },
+               { &dib_table[29], NULL },
+       },      
+#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
+       {       "Artec T1 USB1.1 TVBOX with AN2235 (misdesigned)",
+               &dibusb_device_classes[DIBUSB1_1_AN2235],
+               { &dib_table[30], NULL },
+               { NULL },
+       },
+       {       "Artec T1 USB2.0 TVBOX with FX2 IDs (misdesigned, please report the warm ID)",
+               &dibusb_device_classes[DIBUSB2_0],
+               { &dib_table[31], NULL },
+               { &dib_table[32], NULL }, /* undefined, it could be that the device will get another USB ID in warm state */
+       },
+       {       "DiBcom USB1.1 DVB-T reference design (MOD3000) with AN2135 default IDs",
+               &dibusb_device_classes[DIBUSB1_1],
+               { &dib_table[33], NULL },
+               { NULL },
+       },
+#endif
+};
+
+static int dibusb_exit(struct usb_dibusb *dib)
+{
+       deb_info("init_state before exiting everything: %x\n",dib->init_state);
+       dibusb_remote_exit(dib);
+       dibusb_fe_exit(dib);
+       dibusb_i2c_exit(dib);
+       dibusb_pid_list_exit(dib);
+       dibusb_dvb_exit(dib);
+       dibusb_urb_exit(dib);
+       deb_info("init_state should be zero now: %x\n",dib->init_state);
+       dib->init_state = DIBUSB_STATE_INIT;
+       kfree(dib);
+       return 0;
+}
+
+static int dibusb_init(struct usb_dibusb *dib)
+{
+       int ret = 0;
+       sema_init(&dib->usb_sem, 1);
+       sema_init(&dib->i2c_sem, 1);
+
+       dib->init_state = DIBUSB_STATE_INIT;
+       
+       if ((ret = dibusb_urb_init(dib)) ||
+               (ret = dibusb_dvb_init(dib)) || 
+               (ret = dibusb_pid_list_init(dib)) ||
+               (ret = dibusb_i2c_init(dib))) {
+               dibusb_exit(dib);
+               return ret;
+       }
+
+       if ((ret = dibusb_fe_init(dib)))
+               err("could not initialize a frontend.");
+       
+       if ((ret = dibusb_remote_init(dib)))
+               err("could not initialize remote control.");
+       
+       return 0;
+}
+
+static struct dibusb_usb_device * dibusb_find_device (struct usb_device *udev,int *cold)
+{
+       int i,j;
+       *cold = -1;
+       for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) {
+               for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].cold_ids[j] != NULL; j++) {
+                       deb_info("check for cold %x %x\n",dibusb_devices[i].cold_ids[j]->idVendor, dibusb_devices[i].cold_ids[j]->idProduct);
+                       if (dibusb_devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) &&
+                               dibusb_devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) {
+                               *cold = 1;
+                               return &dibusb_devices[i];
+                       }
+               }
+
+               for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].warm_ids[j] != NULL; j++) {
+                       deb_info("check for warm %x %x\n",dibusb_devices[i].warm_ids[j]->idVendor, dibusb_devices[i].warm_ids[j]->idProduct);
+                       if (dibusb_devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) &&
+                               dibusb_devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) {
+                               *cold = 0;
+                               return &dibusb_devices[i];
+                       }
+               }
+       }
+       return NULL;
+}
+
+/*
+ * USB 
+ */
+static int dibusb_probe(struct usb_interface *intf, 
+               const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct usb_dibusb *dib = NULL;
+       struct dibusb_usb_device *dibdev = NULL;
+       
+       int ret = -ENOMEM,cold=0;
+
+       if ((dibdev = dibusb_find_device(udev,&cold)) == NULL) {
+               err("something went very wrong, "
+                               "unknown product ID: %.4x",le16_to_cpu(udev->descriptor.idProduct));
+               return -ENODEV;
+       }
+       
+       if (cold == 1) {
+               info("found a '%s' in cold state, will try to load a firmware",dibdev->name);
+               ret = dibusb_loadfirmware(udev,dibdev);
+       } else {
+               info("found a '%s' in warm state.",dibdev->name);
+               dib = kmalloc(sizeof(struct usb_dibusb),GFP_KERNEL);
+               if (dib == NULL) {
+                       err("no memory");
+                       return ret;
+               }
+               memset(dib,0,sizeof(struct usb_dibusb));
+               
+               dib->udev = udev;
+               dib->dibdev = dibdev;
+
+               /* store parameters to structures */
+               dib->rc_query_interval = rc_query_interval;
+               dib->pid_parse = pid_parse;
+
+               usb_set_intfdata(intf, dib);
+               
+               ret = dibusb_init(dib);
+       }
+       
+       if (ret == 0)
+               info("%s successfully initialized and connected.",dibdev->name);
+       else 
+               info("%s error while loading driver (%d)",dibdev->name,ret);
+       return ret;
+}
+
+static void dibusb_disconnect(struct usb_interface *intf)
+{
+       struct usb_dibusb *dib = usb_get_intfdata(intf);
+       const char *name = DRIVER_DESC;
+       
+       usb_set_intfdata(intf,NULL);
+       if (dib != NULL && dib->dibdev != NULL) {
+               name = dib->dibdev->name;
+               dibusb_exit(dib);
+       }
+       info("%s successfully deinitialized and disconnected.",name);
+       
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+struct usb_driver dibusb_driver = {
+       .owner          = THIS_MODULE,
+       .name           = DRIVER_DESC,
+       .probe          = dibusb_probe,
+       .disconnect = dibusb_disconnect,
+       .id_table       = dib_table,
+};
+
+/* module stuff */
+static int __init usb_dibusb_init(void)
+{
+       int result;
+       if ((result = usb_register(&dibusb_driver))) {
+               err("usb_register failed. Error number %d",result);
+               return result;
+       }
+       
+       return 0;
+}
+
+static void __exit usb_dibusb_exit(void)
+{
+       /* deregister this driver from the USB subsystem */
+       usb_deregister(&dibusb_driver);
+}
+
+module_init (usb_dibusb_init);
+module_exit (usb_dibusb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c b/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c
new file mode 100644 (file)
index 0000000..d7dd6e7
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * dvb-dibusb-dvb.c is part of the driver for mobile USB Budget DVB-T devices 
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for initializing and handling the 
+ * linux-dvb API.
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/usb.h>
+#include <linux/version.h>
+
+static u32 urb_compl_count;
+
+/*
+ * MPEG2 TS DVB stuff 
+ */
+void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs)
+{
+       struct usb_dibusb *dib = urb->context;
+       int ret;
+
+       deb_ts("urb complete feedcount: %d, status: %d, length: %d\n",dib->feedcount,urb->status,
+                       urb->actual_length);
+
+       urb_compl_count++;
+       if (urb_compl_count % 500 == 0)
+               deb_info("%d urbs completed so far.\n",urb_compl_count);
+
+       switch (urb->status) {
+               case 0:         /* success */
+               case -ETIMEDOUT:    /* NAK */
+                       break;
+               case -ECONNRESET:   /* unlink */
+               case -ENOENT:
+               case -ESHUTDOWN:
+                       return;
+               default:        /* error */
+                       warn("urb completition error %d.", urb->status);
+       }
+
+       if (dib->feedcount > 0) {
+               deb_ts("URB return len: %d\n",urb->actual_length);
+               if (urb->actual_length % 188) 
+                       deb_ts("TS Packets: %d, %d\n", urb->actual_length/188,urb->actual_length % 188);
+
+               /* Francois recommends to drop not full-filled packets, even if they may 
+                * contain valid TS packets, at least for USB1.1
+                *
+                * if (urb->actual_length == dib->dibdev->parm->default_size && dib->dvb_is_ready) */
+               if (dib->init_state & DIBUSB_STATE_DVB)
+                       dvb_dmx_swfilter(&dib->demux, (u8*) urb->transfer_buffer,urb->actual_length);
+               else
+                       deb_ts("URB dropped because of the " 
+                                       "actual_length or !dvb_is_ready (%d).\n",dib->init_state & DIBUSB_STATE_DVB);
+       } else 
+               deb_ts("URB dropped because of feedcount.\n");
+
+       ret = usb_submit_urb(urb,GFP_ATOMIC);
+       deb_ts("urb resubmitted, (%d)\n",ret);
+}
+
+static int dibusb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) 
+{
+       struct usb_dibusb *dib = dvbdmxfeed->demux->priv;
+       int newfeedcount;
+       
+       if (dib == NULL)
+               return -ENODEV;
+
+       newfeedcount = dib->feedcount + (onoff ? 1 : -1);
+
+       /* 
+        * stop feed before setting a new pid if there will be no pid anymore 
+        */
+//     if ((dib->dibdev->parm->firmware_bug && dib->feedcount) || 
+       if (newfeedcount == 0) {
+               deb_ts("stop feeding\n");
+               if (dib->xfer_ops.fifo_ctrl != NULL) {
+                       if (dib->xfer_ops.fifo_ctrl(dib->fe,0)) {
+                               err("error while inhibiting fifo.");
+                               return -ENODEV;
+                       }
+               }
+       }
+       
+       dib->feedcount = newfeedcount;
+
+       /* get a free pid from the list and activate it on the device
+        * specific pid_filter
+        */
+       if (dib->pid_parse)
+               dibusb_ctrl_pid(dib,dvbdmxfeed,onoff);
+
+       /* 
+        * start the feed, either if there is the firmware bug or 
+        * if this was the first pid to set and there is still a pid for 
+        * reception.
+        */
+       
+//     if ((dib->dibdev->parm->firmware_bug)
+       if (dib->feedcount == onoff && dib->feedcount > 0) {
+
+               deb_ts("controlling pid parser\n");
+               if (dib->xfer_ops.pid_parse != NULL) {
+                       if (dib->xfer_ops.pid_parse(dib->fe,dib->pid_parse) < 0) {
+                               err("could not handle pid_parser");
+                       }
+               }
+               
+               deb_ts("start feeding\n");
+               if (dib->xfer_ops.fifo_ctrl != NULL) {
+                       if (dib->xfer_ops.fifo_ctrl(dib->fe,1)) {
+                               err("error while enabling fifo.");
+                               return -ENODEV;
+                       }
+               }
+               dibusb_streaming(dib,1);
+       }
+       return 0;
+}
+
+static int dibusb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+       deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type);
+       return dibusb_ctrl_feed(dvbdmxfeed,1);
+}
+
+static int dibusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+       deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type);
+       return dibusb_ctrl_feed(dvbdmxfeed,0);
+}
+
+int dibusb_dvb_init(struct usb_dibusb *dib)
+{
+       int ret;
+
+       urb_compl_count = 0;
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,4)
+    if ((ret = dvb_register_adapter(&dib->adapter, DRIVER_DESC)) < 0) {
+#else
+    if ((ret = dvb_register_adapter(&dib->adapter, DRIVER_DESC , 
+                       THIS_MODULE)) < 0) {
+#endif
+               deb_info("dvb_register_adapter failed: error %d", ret);
+               goto err;
+       }
+       dib->adapter->priv = dib;
+       
+/* i2c is done in dibusb_i2c_init */
+       
+       dib->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+
+       dib->demux.priv = (void *)dib;
+       /* get pidcount from demod */
+       dib->demux.feednum = dib->demux.filternum = 255;
+       dib->demux.start_feed = dibusb_start_feed;
+       dib->demux.stop_feed = dibusb_stop_feed;
+       dib->demux.write_to_decoder = NULL;
+       if ((ret = dvb_dmx_init(&dib->demux)) < 0) {
+               err("dvb_dmx_init failed: error %d",ret);
+               goto err_dmx;
+       }
+
+       dib->dmxdev.filternum = dib->demux.filternum;
+       dib->dmxdev.demux = &dib->demux.dmx;
+       dib->dmxdev.capabilities = 0;
+       if ((ret = dvb_dmxdev_init(&dib->dmxdev, dib->adapter)) < 0) {
+               err("dvb_dmxdev_init failed: error %d",ret);
+               goto err_dmx_dev;
+       }
+
+       dvb_net_init(dib->adapter, &dib->dvb_net, &dib->demux.dmx);
+
+       goto success;
+err_dmx_dev:
+       dvb_dmx_release(&dib->demux);
+err_dmx:
+       dvb_unregister_adapter(dib->adapter);
+err:
+       return ret;
+success:
+       dib->init_state |= DIBUSB_STATE_DVB;
+       return 0;
+}
+
+int dibusb_dvb_exit(struct usb_dibusb *dib)
+{
+       if (dib->init_state & DIBUSB_STATE_DVB) {
+               dib->init_state &= ~DIBUSB_STATE_DVB;
+               deb_info("unregistering DVB part\n");
+               dvb_net_release(&dib->dvb_net);
+               dib->demux.dmx.close(&dib->demux.dmx);
+               dvb_dmxdev_release(&dib->dmxdev);
+               dvb_dmx_release(&dib->demux);
+               dvb_unregister_adapter(dib->adapter);
+       }
+       return 0;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c b/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c
new file mode 100644 (file)
index 0000000..925c859
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * dvb-dibusb-fe-i2c.c is part of the driver for mobile USB Budget DVB-T devices 
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for attaching, initializing of an appropriate
+ * demodulator/frontend. I2C-stuff is also located here.
+ * 
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/usb.h>
+
+int dibusb_i2c_msg(struct usb_dibusb *dib, u8 addr, 
+               u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
+{
+       u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */
+       /* write only ? */
+       int wo = (rbuf == NULL || rlen == 0), 
+               len = 2 + wlen + (wo ? 0 : 2);
+       
+       sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ;
+       sndbuf[1] = (addr << 1) | (wo ? 0 : 1);
+
+       memcpy(&sndbuf[2],wbuf,wlen);
+       
+       if (!wo) {
+               sndbuf[wlen+2] = (rlen >> 8) & 0xff;
+               sndbuf[wlen+3] = rlen & 0xff;
+       }
+       
+       return dibusb_readwrite_usb(dib,sndbuf,len,rbuf,rlen);
+}
+
+/*
+ * I2C master xfer function
+ */
+static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
+{
+       struct usb_dibusb *dib = i2c_get_adapdata(adap);
+       int i;
+
+       if (down_interruptible(&dib->i2c_sem) < 0) 
+               return -EAGAIN;
+
+       if (num > 2)
+               warn("more than 2 i2c messages at a time is not handled yet. TODO.");
+       
+       for (i = 0; i < num; i++) {
+               /* write/read request */
+               if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+                       if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,
+                                               msg[i+1].buf,msg[i+1].len) < 0)
+                               break;
+                       i++;
+               } else 
+                       if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0)
+                               break;
+       }
+       
+       up(&dib->i2c_sem);
+       return i;       
+}
+
+static u32 dibusb_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dibusb_algo = {
+       .name                   = "DiBcom USB i2c algorithm",
+       .id                             = I2C_ALGO_BIT,
+       .master_xfer    = dibusb_i2c_xfer,
+       .functionality  = dibusb_i2c_func,
+};
+
+static int dibusb_general_demod_init(struct dvb_frontend *fe);
+static u8 dibusb_general_pll_addr(struct dvb_frontend *fe);
+static int dibusb_general_pll_init(struct dvb_frontend *fe, u8 pll_buf[5]);
+static int dibusb_general_pll_set(struct dvb_frontend *fe, 
+               struct dvb_frontend_parameters* params, u8 pll_buf[5]);
+
+static struct mt352_config mt352_hanftek_umt_010_config = {
+       .demod_address = 0x1e,
+       .demod_init = dibusb_general_demod_init,
+       .pll_set = dibusb_general_pll_set,
+};
+
+static int dibusb_tuner_quirk(struct usb_dibusb *dib) 
+{
+       switch (dib->dibdev->dev_cl->id) {
+               case DIBUSB1_1: /* some these device have the ENV77H11D5 and some the THOMSON CABLE */
+               case DIBUSB1_1_AN2235: { /* actually its this device, but in warm state they are indistinguishable */
+                       struct dibusb_tuner *t;
+                       u8 b[2] = { 0,0 } ,b2[1];
+                       struct i2c_msg msg[2] = {
+                               { .flags = 0, .buf = b, .len = 2 },
+                               { .flags = I2C_M_RD, .buf = b2, .len = 1},
+                       };
+                       
+                       t = &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5];
+                       
+                       msg[0].addr = msg[1].addr = t->pll_addr;
+               
+                       if (dib->xfer_ops.tuner_pass_ctrl != NULL)
+                               dib->xfer_ops.tuner_pass_ctrl(dib->fe,1,t->pll_addr);
+                       dibusb_i2c_xfer(&dib->i2c_adap,msg,2);
+                       if (dib->xfer_ops.tuner_pass_ctrl != NULL)
+                               dib->xfer_ops.tuner_pass_ctrl(dib->fe,0,t->pll_addr);
+
+                       if (b2[0] == 0xfe)
+                               info("this device has the Thomson Cable onboard. Which is default.");
+                       else {
+                               dib->tuner = t;
+                               info("this device has the Panasonic ENV77H11D5 onboard.");
+                       }
+                       break;  
+               }
+               default:
+                       break;
+       }
+       return 0;
+}
+
+/* there is a ugly pid_filter in the firmware of the umt devices, it is accessible 
+ * by i2c address 0x8. Don't know how to deactivate it and how many rows it has.
+ */
+static int dibusb_umt_pid_control(struct dvb_frontend *fe, int index, int pid, int onoff)
+{
+       struct usb_dibusb *dib = fe->dvb->priv;
+       u8 b[3];
+       b[0] = index;
+       if (onoff) {
+               b[1] = (pid >> 8) & 0xff;
+               b[2] = pid & 0xff;
+       } else {
+               b[1] = 0;
+               b[2] = 0;
+       }
+       dibusb_i2c_msg(dib, 0x8, b, 3, NULL,0);
+       dibusb_set_streaming_mode(dib,0);
+       dibusb_set_streaming_mode(dib,1);
+       return 0;
+}
+
+int dibusb_fe_init(struct usb_dibusb* dib)
+{
+       struct dib3000_config demod_cfg;
+       int i;
+       
+       if (dib->init_state & DIBUSB_STATE_I2C) { 
+               for (i = 0; i < sizeof(dib->dibdev->dev_cl->demod->i2c_addrs) / sizeof(unsigned char) && 
+                               dib->dibdev->dev_cl->demod->i2c_addrs[i] != 0; i++) {
+
+                       demod_cfg.demod_address = dib->dibdev->dev_cl->demod->i2c_addrs[i];
+                       demod_cfg.pll_addr = dibusb_general_pll_addr;
+                       demod_cfg.pll_set = dibusb_general_pll_set;
+                       demod_cfg.pll_init = dibusb_general_pll_init;
+
+                       switch (dib->dibdev->dev_cl->demod->id) {
+                               case DIBUSB_DIB3000MB:
+                                       dib->fe = dib3000mb_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops);
+                               break;
+                               case DIBUSB_DIB3000MC:
+                                       dib->fe = dib3000mc_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops);
+                               break;
+                               case DIBUSB_MT352:
+                                       mt352_hanftek_umt_010_config.demod_address = dib->dibdev->dev_cl->demod->i2c_addrs[i];
+                                       dib->fe = mt352_attach(&mt352_hanftek_umt_010_config, &dib->i2c_adap);
+                                       dib->xfer_ops.pid_ctrl = dibusb_umt_pid_control;
+                               break;
+                       }
+                       if (dib->fe != NULL) {
+                               info("found demodulator at i2c address 0x%x",dib->dibdev->dev_cl->demod->i2c_addrs[i]);
+                               break;
+                       }
+               }
+               if (dib->fe->ops->sleep != NULL)
+                       dib->fe_sleep = dib->fe->ops->sleep;
+               dib->fe->ops->sleep = dibusb_hw_sleep;
+
+               if (dib->fe->ops->init != NULL ) 
+                       dib->fe_init = dib->fe->ops->init;
+               dib->fe->ops->init = dibusb_hw_wakeup;
+       
+               /* setting the default tuner */ 
+               dib->tuner = dib->dibdev->dev_cl->tuner;
+
+               /* check which tuner is mounted on this device, in case this is unsure */
+               dibusb_tuner_quirk(dib);
+       }
+       if (dib->fe == NULL) {
+               err("A frontend driver was not found for device '%s'.",
+                      dib->dibdev->name);
+               return -ENODEV;
+       } else {
+               if (dvb_register_frontend(dib->adapter, dib->fe)) {
+                       err("Frontend registration failed.");
+                       if (dib->fe->ops->release)
+                               dib->fe->ops->release(dib->fe);
+                       dib->fe = NULL;
+                       return -ENODEV;
+               }
+       }
+       return 0;
+}
+
+int dibusb_fe_exit(struct usb_dibusb *dib)
+{
+       if (dib->fe != NULL)
+               dvb_unregister_frontend(dib->fe);
+       return 0;
+}
+
+int dibusb_i2c_init(struct usb_dibusb *dib)
+{
+       int ret = 0;
+
+       dib->adapter->priv = dib;
+
+       strncpy(dib->i2c_adap.name,dib->dibdev->name,I2C_NAME_SIZE);
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+       dib->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
+#else
+       dib->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
+#endif
+       dib->i2c_adap.algo              = &dibusb_algo;
+       dib->i2c_adap.algo_data = NULL;
+       dib->i2c_adap.id                = I2C_ALGO_BIT;
+       
+       i2c_set_adapdata(&dib->i2c_adap, dib);
+       
+       if ((ret = i2c_add_adapter(&dib->i2c_adap)) < 0)
+               err("could not add i2c adapter");
+       
+       dib->init_state |= DIBUSB_STATE_I2C;
+
+       return ret;
+}
+
+int dibusb_i2c_exit(struct usb_dibusb *dib)
+{
+       if (dib->init_state & DIBUSB_STATE_I2C)
+               i2c_del_adapter(&dib->i2c_adap);
+       dib->init_state &= ~DIBUSB_STATE_I2C;
+       return 0;
+}
+
+
+/* pll stuff, maybe removed soon (thx to Gerd/Andrew in advance) */
+static int thomson_cable_eu_pll_set(struct dvb_frontend_parameters *fep, u8 pllbuf[4])
+{
+       u32 tfreq = (fep->frequency + 36125000) / 62500;
+       int vu,p0,p1,p2;
+
+       if (fep->frequency > 403250000)
+               vu = 1, p2 = 1, p1 = 0, p0 = 1;
+       else if (fep->frequency > 115750000)
+               vu = 0, p2 = 1, p1 = 1, p0 = 0;
+       else if (fep->frequency > 44250000)
+               vu = 0, p2 = 0, p1 = 1, p0 = 1;
+       else
+               return -EINVAL;
+
+       pllbuf[0] = (tfreq >> 8) & 0x7f;
+       pllbuf[1] = tfreq & 0xff;
+       pllbuf[2] = 0x8e;
+       pllbuf[3] = (vu << 7) | (p2 << 2) | (p1 << 1) | p0;
+       return 0;
+}
+
+static int panasonic_cofdm_env57h1xd5_pll_set(struct dvb_frontend_parameters *fep, u8 pllbuf[4])
+{
+       u32 freq = fep->frequency;
+       u32 tfreq = ((freq + 36125000)*6 + 500000) / 1000000;
+       u8 TA, T210, R210, ctrl1, cp210, p4321;
+       if (freq > 858000000) {
+               err("frequency cannot be larger than 858 MHz.");
+               return -EINVAL;
+       }
+       
+       // contol data 1 : 1 | T/A=1 | T2,T1,T0 = 0,0,0 | R2,R1,R0 = 0,1,0
+       TA = 1;
+       T210 = 0;
+       R210 = 0x2;
+       ctrl1 = (1 << 7) | (TA << 6) | (T210 << 3) | R210;
+
+// ********    CHARGE PUMP CONFIG vs RF FREQUENCIES     *****************
+       if (freq < 470000000) 
+               cp210 = 2;  // VHF Low and High band ch E12 to E4 to E12
+       else if (freq < 526000000) 
+               cp210 = 4;  // UHF band Ch E21 to E27
+       else // if (freq < 862000000) 
+               cp210 = 5;  // UHF band ch E28 to E69
+
+//*********************    BW select  *******************************
+       if (freq < 153000000) 
+               p4321  = 1; // BW selected for VHF low
+       else if (freq < 470000000) 
+               p4321  = 2; // BW selected for VHF high E5 to E12
+       else // if (freq < 862000000) 
+               p4321  = 4; // BW selection for UHF E21 to E69
+
+       pllbuf[0] = (tfreq >> 8) & 0xff;
+       pllbuf[1] = (tfreq >> 0) & 0xff;
+       pllbuf[2] = 0xff & ctrl1;
+       pllbuf[3] =  (cp210 << 5) | (p4321);
+
+       return 0;
+}
+
+/*
+ *                         7   6               5       4       3       2       1       0
+ * Address Byte             1  1               0       0       0       MA1     MA0     R/~W=0
+ *
+ * Program divider byte 1   0  n14             n13     n12     n11     n10     n9      n8
+ * Program divider byte 2      n7      n6              n5      n4      n3      n2      n1      n0
+ *
+ * Control byte 1           1  T/A=1   T2      T1      T0      R2      R1      R0
+ *                          1  T/A=0   0       0       ATC     AL2     AL1     AL0
+ * 
+ * Control byte 2           CP2        CP1             CP0     BS5     BS4     BS3     BS2     BS1
+ * 
+ * MA0/1 = programmable address bits
+ * R/~W  = read/write bit (0 for writing)
+ * N14-0 = programmable LO frequency
+ * 
+ * T/A   = test AGC bit (0 = next 6 bits AGC setting, 
+ *                       1 = next 6 bits test and reference divider ratio settings)
+ * T2-0  = test bits
+ * R2-0  = reference divider ratio and programmable frequency step
+ * ATC   = AGC current setting and time constant
+ *         ATC = 0: AGC current = 220nA, AGC time constant = 2s
+ *         ATC = 1: AGC current = 9uA, AGC time constant = 50ms
+ * AL2-0 = AGC take-over point bits
+ * CP2-0 = charge pump current
+ * BS5-1 = PMOS ports control bits;
+ *             BSn = 0 corresponding port is off, high-impedance state (at power-on)
+ *             BSn = 1 corresponding port is on
+ */
+
+
+static int panasonic_cofdm_env77h11d5_tda6650_init(struct dvb_frontend *fe, u8 pllbuf[4])
+{
+       pllbuf[0] = 0x0b;
+       pllbuf[1] = 0xf5;
+       pllbuf[2] = 0x85;
+       pllbuf[3] = 0xab;
+       return 0;
+}
+
+static int panasonic_cofdm_env77h11d5_tda6650_set (struct dvb_frontend_parameters *fep,u8 pllbuf[4])
+{
+       int tuner_frequency = 0;
+       u8 band, cp, filter;
+
+       // determine charge pump
+       tuner_frequency = fep->frequency + 36166000;
+       if (tuner_frequency < 87000000)
+               return -EINVAL;
+       else if (tuner_frequency < 130000000)
+               cp = 3;
+       else if (tuner_frequency < 160000000)
+               cp = 5;
+       else if (tuner_frequency < 200000000)
+               cp = 6;
+       else if (tuner_frequency < 290000000)
+               cp = 3;
+       else if (tuner_frequency < 420000000)
+               cp = 5;
+       else if (tuner_frequency < 480000000)
+               cp = 6;
+       else if (tuner_frequency < 620000000)
+               cp = 3;
+       else if (tuner_frequency < 830000000)
+               cp = 5;
+       else if (tuner_frequency < 895000000)
+               cp = 7;
+       else
+               return -EINVAL;
+
+       // determine band
+       if (fep->frequency < 49000000)
+               return -EINVAL;
+       else if (fep->frequency < 161000000)
+               band = 1;
+       else if (fep->frequency < 444000000)
+               band = 2;
+       else if (fep->frequency < 861000000)
+               band = 4;
+       else
+               return -EINVAL;
+
+       // setup PLL filter
+       switch (fep->u.ofdm.bandwidth) {
+               case BANDWIDTH_6_MHZ:
+               case BANDWIDTH_7_MHZ:
+                       filter = 0;
+                       break;
+               case BANDWIDTH_8_MHZ:
+                       filter = 1;
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       // calculate divisor
+       // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
+       tuner_frequency = (((fep->frequency / 1000) * 6) + 217496) / 1000;
+
+       // setup tuner buffer
+       pllbuf[0] = (tuner_frequency >> 8) & 0x7f;
+       pllbuf[1] = tuner_frequency & 0xff;
+       pllbuf[2] = 0xca;
+       pllbuf[3] = (cp << 5) | (filter << 3) | band;
+       return 0;
+}
+
+/*
+ *                         7   6       5       4       3       2       1       0
+ * Address Byte             1  1       0       0       0       MA1     MA0     R/~W=0
+ *
+ * Program divider byte 1   0  n14     n13     n12     n11     n10     n9      n8
+ * Program divider byte 2      n7      n6      n5      n4      n3      n2      n1      n0
+ *
+ * Control byte             1  CP      T2      T1      T0      RSA     RSB     OS
+ * 
+ * Band Switch byte         X  X       X       P4      P3      P2      P1      P0
+ *
+ * Auxiliary byte           ATC        AL2     AL1     AL0     0       0       0       0
+ *
+ * Address: MA1        MA0     Address
+ *          0  0       c0
+ *          0  1       c2 (always valid)
+ *          1  0       c4
+ *          1  1       c6
+ *
+ *
+ * 
+ */
+
+static int lg_tdtp_e102p_tua6034(struct dvb_frontend_parameters* fep, u8 pllbuf[4]) 
+{
+       u32 div;
+       u8 p3210, p4;
+
+#define TUNER_MUL 62500
+
+       div = (fep->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL;
+
+       if (fep->frequency < 174500000) 
+               p3210 = 1; // not supported by the tdtp_e102p
+       else if (fep->frequency < 230000000) // VHF
+               p3210 = 2;
+       else 
+               p3210 = 4;
+
+       if (fep->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+               p4 = 0;
+       else 
+               p4 = 1;
+               
+       pllbuf[0] = (div >> 8) & 0x7f;
+       pllbuf[1] = div & 0xff;
+       pllbuf[2] = 0xce;
+       pllbuf[3] = (p4 << 4) | p3210;
+
+       return 0;
+}
+
+static int lg_tdtp_e102p_mt352_demod_init(struct dvb_frontend *fe)
+{
+       static u8 mt352_clock_config[] = { 0x89, 0xb0, 0x2d };
+       static u8 mt352_reset[] = { 0x50, 0x80 };
+       static u8 mt352_mclk_ratio[] = { 0x8b, 0x00 };
+       static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 };
+       static u8 mt352_agc_cfg[] = { 0x67, 0x14, 0x22 };
+       static u8 mt352_sec_agc_cfg[] = { 0x69, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x40, 0x40 };
+
+       static u8 mt352_unk [] = { 0xb5, 0x7a };
+
+       static u8 mt352_acq_ctl[] = { 0x53, 0x5f };
+       static u8 mt352_input_freq_1[] = { 0x56, 0xf1, 0x05 };
+       
+//     static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+       mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+       udelay(2000);
+       mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+       mt352_write(fe, mt352_mclk_ratio, sizeof(mt352_mclk_ratio));
+       
+       mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+       mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
+
+       mt352_write(fe, mt352_sec_agc_cfg, sizeof(mt352_sec_agc_cfg));
+
+       mt352_write(fe, mt352_unk, sizeof(mt352_unk));
+       
+       mt352_write(fe, mt352_acq_ctl, sizeof(mt352_acq_ctl));
+       mt352_write(fe, mt352_input_freq_1, sizeof(mt352_input_freq_1));
+       
+//     mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+       return 0;
+}
+
+static int dibusb_general_demod_init(struct dvb_frontend *fe)
+{
+       struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+       switch (dib->dibdev->dev_cl->id) {
+               case UMT2_0:
+                       return lg_tdtp_e102p_mt352_demod_init(fe);
+               default: /* other device classes do not have device specific demod inits */
+                       break;
+       }
+       return 0;
+}
+
+static u8 dibusb_general_pll_addr(struct dvb_frontend *fe)
+{
+       struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+       return dib->tuner->pll_addr;
+}
+
+static int dibusb_pll_i2c_helper(struct usb_dibusb *dib, u8 pll_buf[5], u8 buf[4])
+{
+       if (pll_buf == NULL) {
+               struct i2c_msg msg = { 
+                       .addr = dib->tuner->pll_addr, 
+                       .flags = 0, 
+                       .buf = buf, 
+                       .len = sizeof(buf) 
+               };
+               if (i2c_transfer (&dib->i2c_adap, &msg, 1) != 1)
+                       return -EIO;
+               msleep(1);
+       } else {
+               pll_buf[0] = dib->tuner->pll_addr << 1;
+               memcpy(&pll_buf[1],buf,4);
+       }
+
+       return 0;
+}
+
+static int dibusb_general_pll_init(struct dvb_frontend *fe, 
+               u8 pll_buf[5])
+{
+       struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+       u8 buf[4];
+       int ret=0;
+       switch (dib->tuner->id) {
+               case DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5:
+                       ret = panasonic_cofdm_env77h11d5_tda6650_init(fe,buf);
+                       break;
+               default:
+                       break;
+       }
+       
+       if (ret)
+               return ret;
+
+       return dibusb_pll_i2c_helper(dib,pll_buf,buf);
+}
+
+static int dibusb_general_pll_set(struct dvb_frontend *fe, 
+               struct dvb_frontend_parameters *fep, u8 pll_buf[5])
+{
+       struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+       u8 buf[4];
+       int ret=0;
+
+       switch (dib->tuner->id) {
+               case DIBUSB_TUNER_CABLE_THOMSON: 
+                       ret = thomson_cable_eu_pll_set(fep, buf); 
+                       break;
+               case DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5:
+                       ret = panasonic_cofdm_env57h1xd5_pll_set(fep, buf);
+                       break;
+               case DIBUSB_TUNER_CABLE_LG_TDTP_E102P:
+                       ret = lg_tdtp_e102p_tua6034(fep, buf); 
+                       break;
+               case DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5:
+                       ret = panasonic_cofdm_env77h11d5_tda6650_set(fep,buf);
+                       break;
+               default:
+                       warn("no pll programming routine found for tuner %d.\n",dib->tuner->id);
+                       ret = -ENODEV;
+                       break;
+       }
+       
+       if (ret)
+               return ret;
+       
+       return dibusb_pll_i2c_helper(dib,pll_buf,buf);
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c b/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c
new file mode 100644 (file)
index 0000000..972548a
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * dvb-dibusb-firmware.c is part of the driver for mobile USB Budget DVB-T devices 
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for downloading the firmware to the device.
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/firmware.h>
+#include <linux/usb.h>
+
+/*
+ * load a firmware packet to the device 
+ */
+static int dibusb_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 len)
+{
+       return usb_control_msg(udev, usb_sndctrlpipe(udev,0),
+                       0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5*HZ);
+}
+
+int dibusb_loadfirmware(struct usb_device *udev, struct dibusb_usb_device *dibdev)
+{
+       const struct firmware *fw = NULL;
+       u16 addr;
+       u8 *b,*p;
+       int ret = 0,i;
+       
+       if ((ret = request_firmware(&fw, dibdev->dev_cl->firmware, &udev->dev)) != 0) {
+               err("did not find a valid firmware file. (%s) "
+                       "Please see linux/Documentation/dvb/ for more details on firmware-problems.",
+                       dibdev->dev_cl->firmware);
+               return ret;
+       }
+       
+       p = kmalloc(fw->size,GFP_KERNEL);       
+       if (p != NULL) {
+               u8 reset;
+               /*
+                * you cannot use the fw->data as buffer for 
+                * usb_control_msg, a new buffer has to be
+                * created
+                */
+               memcpy(p,fw->data,fw->size);
+
+               /* stop the CPU */
+               reset = 1;
+               if ((ret = dibusb_writemem(udev,dibdev->dev_cl->usb_ctrl->cpu_cs_register,&reset,1)) != 1) 
+                       err("could not stop the USB controller CPU.");
+               for(i = 0; p[i+3] == 0 && i < fw->size; ) { 
+                       b = (u8 *) &p[i];
+                       addr = *((u16 *) &b[1]);
+
+                       ret = dibusb_writemem(udev,addr,&b[4],b[0]);
+               
+                       if (ret != b[0]) {
+                               err("error while transferring firmware "
+                                       "(transferred size: %d, block size: %d)",
+                                       ret,b[0]);
+                               ret = -EINVAL;
+                               break;
+                       }
+                       i += 5 + b[0];
+               }
+               /* length in ret */
+               if (ret > 0)
+                       ret = 0;
+               /* restart the CPU */
+               reset = 0;
+               if (ret || dibusb_writemem(udev,dibdev->dev_cl->usb_ctrl->cpu_cs_register,&reset,1) != 1) {
+                       err("could not restart the USB controller CPU.");
+                       ret = -EINVAL;
+               }
+
+               kfree(p);
+       } else { 
+               ret = -ENOMEM;
+       }
+       release_firmware(fw);
+
+       return ret;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-pid.c b/drivers/media/dvb/dibusb/dvb-dibusb-pid.c
new file mode 100644 (file)
index 0000000..91a3954
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * dvb-dibusb-pid.c is part of the driver for mobile USB Budget DVB-T devices 
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for initializing and handling the internal
+ * pid-list. This pid-list mirrors the information currently stored in the
+ * devices pid-list.
+ */
+#include "dvb-dibusb.h"
+
+int dibusb_pid_list_init(struct usb_dibusb *dib)
+{
+       int i;
+       dib->pid_list = kmalloc(sizeof(struct dibusb_pid) * dib->dibdev->dev_cl->demod->pid_filter_count,GFP_KERNEL);
+       if (dib->pid_list == NULL)
+               return -ENOMEM;
+
+       deb_xfer("initializing %d pids for the pid_list.\n",dib->dibdev->dev_cl->demod->pid_filter_count);
+       
+       dib->pid_list_lock = SPIN_LOCK_UNLOCKED;
+       memset(dib->pid_list,0,dib->dibdev->dev_cl->demod->pid_filter_count*(sizeof(struct dibusb_pid)));
+       for (i=0; i < dib->dibdev->dev_cl->demod->pid_filter_count; i++) {
+               dib->pid_list[i].index = i;
+               dib->pid_list[i].pid = 0;
+               dib->pid_list[i].active = 0;
+       }
+
+       dib->init_state |= DIBUSB_STATE_PIDLIST;
+       return 0;
+}
+
+void dibusb_pid_list_exit(struct usb_dibusb *dib)
+{
+       if (dib->init_state & DIBUSB_STATE_PIDLIST)
+               kfree(dib->pid_list);
+       dib->init_state &= ~DIBUSB_STATE_PIDLIST;
+}
+
+/* fetch a pid from pid_list and set it on or off */
+int dibusb_ctrl_pid(struct usb_dibusb *dib, struct dvb_demux_feed *dvbdmxfeed , int onoff)
+{
+       int i,ret = -1;
+       unsigned long flags;
+       u16 pid = dvbdmxfeed->pid;
+
+       if (onoff) {
+               spin_lock_irqsave(&dib->pid_list_lock,flags);
+               for (i=0; i < dib->dibdev->dev_cl->demod->pid_filter_count; i++)
+                       if (!dib->pid_list[i].active) {
+                               dib->pid_list[i].pid = pid;
+                               dib->pid_list[i].active = 1;
+                               ret = i;
+                               break;
+                       }
+               dvbdmxfeed->priv = &dib->pid_list[ret];
+               spin_unlock_irqrestore(&dib->pid_list_lock,flags);
+               
+               if (dib->xfer_ops.pid_ctrl != NULL) 
+                       dib->xfer_ops.pid_ctrl(dib->fe,dib->pid_list[ret].index,dib->pid_list[ret].pid,1);
+       } else {
+               struct dibusb_pid *dpid = dvbdmxfeed->priv;
+               
+               if (dib->xfer_ops.pid_ctrl != NULL) 
+                       dib->xfer_ops.pid_ctrl(dib->fe,dpid->index,0,0);
+               
+               dpid->pid = 0;
+               dpid->active = 0;
+               ret = dpid->index;
+       }
+       
+       /* a free pid from the list */
+       deb_info("setting pid: %5d %04x at index %d '%s'\n",pid,pid,ret,onoff ? "on" : "off");
+
+       return ret;
+}
+
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-remote.c b/drivers/media/dvb/dibusb/dvb-dibusb-remote.c
new file mode 100644 (file)
index 0000000..b3b6f24
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * dvb-dibusb-remote.c is part of the driver for mobile USB Budget DVB-T devices
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for handling the event device on the software
+ * side and the remote control on the hardware side.
+ */
+#include "dvb-dibusb.h"
+
+/* Table to map raw key codes to key events.  This should not be hard-wired
+   into the kernel.  */
+static const struct { u8 c0, c1, c2; uint32_t key; } rc_keys [] =
+{
+       /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */
+       { 0x00, 0xff, 0x16, KEY_POWER },
+       { 0x00, 0xff, 0x10, KEY_MUTE },
+       { 0x00, 0xff, 0x03, KEY_1 },
+       { 0x00, 0xff, 0x01, KEY_2 },
+       { 0x00, 0xff, 0x06, KEY_3 },
+       { 0x00, 0xff, 0x09, KEY_4 },
+       { 0x00, 0xff, 0x1d, KEY_5 },
+       { 0x00, 0xff, 0x1f, KEY_6 },
+       { 0x00, 0xff, 0x0d, KEY_7 },
+       { 0x00, 0xff, 0x19, KEY_8 },
+       { 0x00, 0xff, 0x1b, KEY_9 },
+       { 0x00, 0xff, 0x15, KEY_0 },
+       { 0x00, 0xff, 0x05, KEY_CHANNELUP },
+       { 0x00, 0xff, 0x02, KEY_CHANNELDOWN },
+       { 0x00, 0xff, 0x1e, KEY_VOLUMEUP },
+       { 0x00, 0xff, 0x0a, KEY_VOLUMEDOWN },
+       { 0x00, 0xff, 0x11, KEY_RECORD },
+       { 0x00, 0xff, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */
+       { 0x00, 0xff, 0x14, KEY_PLAY },
+       { 0x00, 0xff, 0x1a, KEY_STOP },
+       { 0x00, 0xff, 0x40, KEY_REWIND },
+       { 0x00, 0xff, 0x12, KEY_FASTFORWARD },
+       { 0x00, 0xff, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */
+       { 0x00, 0xff, 0x4c, KEY_PAUSE },
+       { 0x00, 0xff, 0x4d, KEY_SCREEN }, /* Full screen mode. */
+       { 0x00, 0xff, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
+       /* additional keys TwinHan VisionPlus, the Artec seemingly not have */
+       { 0x00, 0xff, 0x0c, KEY_CANCEL }, /* Cancel */
+       { 0x00, 0xff, 0x1c, KEY_EPG }, /* EPG */
+       { 0x00, 0xff, 0x00, KEY_TAB }, /* Tab */
+       { 0x00, 0xff, 0x48, KEY_INFO }, /* Preview */
+       { 0x00, 0xff, 0x04, KEY_LIST }, /* RecordList */
+       { 0x00, 0xff, 0x0f, KEY_TEXT }, /* Teletext */
+       /* Key codes for the KWorld/ADSTech/JetWay remote. */
+       { 0x86, 0x6b, 0x12, KEY_POWER },
+       { 0x86, 0x6b, 0x0f, KEY_SELECT }, /* source */
+       { 0x86, 0x6b, 0x0c, KEY_UNKNOWN }, /* scan */
+       { 0x86, 0x6b, 0x0b, KEY_EPG },
+       { 0x86, 0x6b, 0x10, KEY_MUTE },
+       { 0x86, 0x6b, 0x01, KEY_1 },
+       { 0x86, 0x6b, 0x02, KEY_2 },
+       { 0x86, 0x6b, 0x03, KEY_3 },
+       { 0x86, 0x6b, 0x04, KEY_4 },
+       { 0x86, 0x6b, 0x05, KEY_5 },
+       { 0x86, 0x6b, 0x06, KEY_6 },
+       { 0x86, 0x6b, 0x07, KEY_7 },
+       { 0x86, 0x6b, 0x08, KEY_8 },
+       { 0x86, 0x6b, 0x09, KEY_9 },
+       { 0x86, 0x6b, 0x0a, KEY_0 },
+       { 0x86, 0x6b, 0x18, KEY_ZOOM },
+       { 0x86, 0x6b, 0x1c, KEY_UNKNOWN }, /* preview */
+       { 0x86, 0x6b, 0x13, KEY_UNKNOWN }, /* snap */
+       { 0x86, 0x6b, 0x00, KEY_UNDO },
+       { 0x86, 0x6b, 0x1d, KEY_RECORD },
+       { 0x86, 0x6b, 0x0d, KEY_STOP },
+       { 0x86, 0x6b, 0x0e, KEY_PAUSE },
+       { 0x86, 0x6b, 0x16, KEY_PLAY },
+       { 0x86, 0x6b, 0x11, KEY_BACK },
+       { 0x86, 0x6b, 0x19, KEY_FORWARD },
+       { 0x86, 0x6b, 0x14, KEY_UNKNOWN }, /* pip */
+       { 0x86, 0x6b, 0x15, KEY_ESC },
+       { 0x86, 0x6b, 0x1a, KEY_UP },
+       { 0x86, 0x6b, 0x1e, KEY_DOWN },
+       { 0x86, 0x6b, 0x1f, KEY_LEFT },
+       { 0x86, 0x6b, 0x1b, KEY_RIGHT },
+};
+
+/*
+ * Read the remote control and feed the appropriate event.
+ * NEC protocol is used for remote controls
+ */
+static int dibusb_read_remote_control(struct usb_dibusb *dib)
+{
+       u8 b[1] = { DIBUSB_REQ_POLL_REMOTE }, rb[5];
+       int ret;
+       int i;
+       if ((ret = dibusb_readwrite_usb(dib,b,1,rb,5)))
+               return ret;
+
+       switch (rb[0]) {
+               case DIBUSB_RC_NEC_KEY_PRESSED:
+                       /* rb[1-3] is the actual key, rb[4] is a checksum */
+                       deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+                               rb[1], rb[2], rb[3], rb[4]);
+
+                       if ((0xff - rb[3]) != rb[4]) {
+                               deb_rc("remote control checksum failed.\n");
+                               break;
+                       }
+
+                       /* See if we can match the raw key code. */
+                       for (i = 0; i < sizeof(rc_keys)/sizeof(rc_keys[0]); i++) {
+                               if (rc_keys[i].c0 == rb[1] &&
+                                       rc_keys[i].c1 == rb[2] &&
+                                   rc_keys[i].c2 == rb[3]) {
+                                       dib->rc_input_event = rc_keys[i].key;
+                                       deb_rc("Translated key 0x%04x\n", dib->rc_input_event);
+                                       /* Signal down and up events for this key. */
+                                       input_report_key(&dib->rc_input_dev, dib->rc_input_event, 1);
+                                       input_report_key(&dib->rc_input_dev, dib->rc_input_event, 0);
+                                       input_sync(&dib->rc_input_dev);
+                                       break;
+                               }
+                       }
+                       break;
+               case DIBUSB_RC_NEC_EMPTY: /* No (more) remote control keys. */
+                       break;
+               case DIBUSB_RC_NEC_KEY_REPEATED:
+                       /* rb[1]..rb[4] are always zero.*/
+                       /* Repeats often seem to occur so for the moment just ignore this. */
+                       deb_rc("Key repeat\n");
+                       break;
+               default:
+                       break;
+       }
+       return 0;
+}
+
+/* Remote-control poll function - called every dib->rc_query_interval ms to see
+   whether the remote control has received anything. */
+static void dibusb_remote_query(void *data)
+{
+       struct usb_dibusb *dib = (struct usb_dibusb *) data;
+       /* TODO: need a lock here.  We can simply skip checking for the remote control
+          if we're busy. */
+       dibusb_read_remote_control(dib);
+       schedule_delayed_work(&dib->rc_query_work,
+                             msecs_to_jiffies(dib->rc_query_interval));
+}
+
+int dibusb_remote_init(struct usb_dibusb *dib)
+{
+       int i;
+
+       if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO)
+               return 0;
+       
+       /* Initialise the remote-control structures.*/
+       init_input_dev(&dib->rc_input_dev);
+
+       dib->rc_input_dev.evbit[0] = BIT(EV_KEY);
+       dib->rc_input_dev.keycodesize = sizeof(unsigned char);
+       dib->rc_input_dev.keycodemax = KEY_MAX;
+       dib->rc_input_dev.name = DRIVER_DESC " remote control";
+
+       for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i++)
+               set_bit(rc_keys[i].key, dib->rc_input_dev.keybit);
+
+       input_register_device(&dib->rc_input_dev);
+
+       dib->rc_input_event = KEY_MAX;
+
+       INIT_WORK(&dib->rc_query_work, dibusb_remote_query, dib);
+
+       /* Start the remote-control polling. */
+       if (dib->rc_query_interval < 40)
+               dib->rc_query_interval = 100; /* default */
+
+       info("schedule remote query interval to %d msecs.",dib->rc_query_interval);
+       schedule_delayed_work(&dib->rc_query_work,msecs_to_jiffies(dib->rc_query_interval));
+
+       dib->init_state |= DIBUSB_STATE_REMOTE;
+       
+       return 0;
+}
+
+int dibusb_remote_exit(struct usb_dibusb *dib)
+{
+       if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO)
+               return 0;
+
+       if (dib->init_state & DIBUSB_STATE_REMOTE) {
+               cancel_delayed_work(&dib->rc_query_work);
+               flush_scheduled_work();
+               input_unregister_device(&dib->rc_input_dev);
+       }
+       dib->init_state &= ~DIBUSB_STATE_REMOTE;
+       return 0;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-usb.c b/drivers/media/dvb/dibusb/dvb-dibusb-usb.c
new file mode 100644 (file)
index 0000000..656a011
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * dvb-dibusb-usb.c is part of the driver for mobile USB Budget DVB-T devices 
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for initializing and handling the 
+ * usb specific stuff.
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/version.h>
+#include <linux/pci.h>
+
+int dibusb_readwrite_usb(struct usb_dibusb *dib, u8 *wbuf, u16 wlen, u8 *rbuf,
+               u16 rlen)
+{
+       int actlen,ret = -ENOMEM;
+
+       if (wbuf == NULL || wlen == 0)
+               return -EINVAL;
+
+       if ((ret = down_interruptible(&dib->usb_sem)))
+               return ret;
+
+       if (dib->feedcount && 
+               wbuf[0] == DIBUSB_REQ_I2C_WRITE && 
+               dib->dibdev->dev_cl->id == DIBUSB1_1)
+               deb_err("BUG: writing to i2c, while TS-streaming destroys the stream."
+                               "(%x reg: %x %x)\n", wbuf[0],wbuf[2],wbuf[3]);
+                       
+       debug_dump(wbuf,wlen);
+
+       ret = usb_bulk_msg(dib->udev,usb_sndbulkpipe(dib->udev,
+                       dib->dibdev->dev_cl->pipe_cmd), wbuf,wlen,&actlen,
+                       DIBUSB_I2C_TIMEOUT);
+               
+       if (ret)
+               err("bulk message failed: %d (%d/%d)",ret,wlen,actlen);
+       else
+               ret = actlen != wlen ? -1 : 0;
+
+       /* an answer is expected, and no error before */
+       if (!ret && rbuf && rlen) {
+               ret = usb_bulk_msg(dib->udev,usb_rcvbulkpipe(dib->udev,
+                               dib->dibdev->dev_cl->pipe_cmd),rbuf,rlen,&actlen,
+                               DIBUSB_I2C_TIMEOUT);
+
+               if (ret)
+                       err("recv bulk message failed: %d",ret);
+               else {
+                       deb_alot("rlen: %d\n",rlen);
+                       debug_dump(rbuf,actlen);
+               }
+       }
+       
+       up(&dib->usb_sem);
+       return ret;
+}
+
+/*
+ * Cypress controls
+ */
+
+#if 0
+/* 
+ * #if 0'ing the following functions as they are not in use _now_, 
+ * but probably will be sometime.
+ */
+
+/*
+ * do not use this, just a workaround for a bug, 
+ * which will hopefully never occur :).
+ */
+int dibusb_interrupt_read_loop(struct usb_dibusb *dib)
+{
+       u8 b[1] = { DIBUSB_REQ_INTR_READ };
+       return dibusb_write_usb(dib,b,1);
+}
+
+#endif 
+static int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len)
+{
+       return dibusb_readwrite_usb(dib,buf,len,NULL,0);
+}
+
+/*
+ * ioctl for the firmware 
+ */
+static int dibusb_ioctl_cmd(struct usb_dibusb *dib, u8 cmd, u8 *param, int plen)
+{
+       u8 b[34];
+       int size = plen > 32 ? 32 : plen;
+       memset(b,0,34);
+       b[0] = DIBUSB_REQ_SET_IOCTL;
+       b[1] = cmd;
+
+       if (size > 0)
+               memcpy(&b[2],param,size);
+
+       return dibusb_write_usb(dib,b,34); //2+size);
+}
+
+/*
+ * ioctl for power control
+ */
+int dibusb_hw_wakeup(struct dvb_frontend *fe)
+{
+       struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv;
+       u8 b[1] = { DIBUSB_IOCTL_POWER_WAKEUP };
+       deb_info("dibusb-device is getting up.\n");
+       dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1);
+       
+       if (dib->fe_init)
+               return dib->fe_init(fe);
+       
+       return 0;
+}
+
+int dibusb_hw_sleep(struct dvb_frontend *fe)
+{
+       struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv;
+       u8 b[1] = { DIBUSB_IOCTL_POWER_SLEEP };
+       deb_info("dibusb-device is going to bed.\n");
+       dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1);
+
+       if (dib->fe_sleep)
+               return dib->fe_sleep(fe);
+       
+       return 0;
+}
+
+int dibusb_set_streaming_mode(struct usb_dibusb *dib,u8 mode)
+{
+       u8 b[2] = { DIBUSB_REQ_SET_STREAMING_MODE, mode };
+       return dibusb_readwrite_usb(dib,b,2,NULL,0);
+}
+
+int dibusb_streaming(struct usb_dibusb *dib,int onoff)
+{
+       switch (dib->dibdev->dev_cl->id) {
+               case DIBUSB2_0:
+                       if (onoff)
+                               return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_ENABLE_STREAM,NULL,0);
+                       else
+                               return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_DISABLE_STREAM,NULL,0);
+                       break;
+               case UMT2_0: 
+                       return dibusb_set_streaming_mode(dib,onoff);
+                       break;
+               default:
+                       break;
+       }
+       return 0;
+}
+
+int dibusb_urb_init(struct usb_dibusb *dib)
+{
+       int ret,i,bufsize,def_pid_parse = 1;
+       
+       /*
+        * when reloading the driver w/o replugging the device 
+        * a timeout occures, this helps
+        */
+       usb_clear_halt(dib->udev,usb_sndbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd));
+       usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd));
+       usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data));
+
+       /* allocate the array for the data transfer URBs */
+       dib->urb_list = kmalloc(dib->dibdev->dev_cl->urb_count*sizeof(struct urb *),GFP_KERNEL);
+       if (dib->urb_list == NULL)
+               return -ENOMEM;
+       memset(dib->urb_list,0,dib->dibdev->dev_cl->urb_count*sizeof(struct urb *));
+
+       dib->init_state |= DIBUSB_STATE_URB_LIST;
+       
+       bufsize = dib->dibdev->dev_cl->urb_count*dib->dibdev->dev_cl->urb_buffer_size;
+       deb_info("allocate %d bytes as buffersize for all URBs\n",bufsize);
+       /* allocate the actual buffer for the URBs */
+       if ((dib->buffer = pci_alloc_consistent(NULL,bufsize,&dib->dma_handle)) == NULL) {
+               deb_info("not enough memory.\n");
+               return -ENOMEM;
+       }
+       deb_info("allocation complete\n");
+       memset(dib->buffer,0,bufsize);
+       
+       dib->init_state |= DIBUSB_STATE_URB_BUF;
+
+       /* allocate and submit the URBs */
+       for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) {
+               if (!(dib->urb_list[i] = usb_alloc_urb(0,GFP_ATOMIC))) {
+                       return -ENOMEM;
+               }
+               deb_info("submitting URB no. %d\n",i);
+               
+               usb_fill_bulk_urb( dib->urb_list[i], dib->udev, 
+                               usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data),
+                               &dib->buffer[i*dib->dibdev->dev_cl->urb_buffer_size], 
+                               dib->dibdev->dev_cl->urb_buffer_size, 
+                               dibusb_urb_complete, dib);
+               
+               dib->urb_list[i]->transfer_flags = 0;
+
+               if ((ret = usb_submit_urb(dib->urb_list[i],GFP_ATOMIC))) {
+                       err("could not submit buffer urb no. %d\n",i);
+                       return ret;
+               }
+               dib->init_state |= DIBUSB_STATE_URB_SUBMIT;
+       }
+
+       /* dib->pid_parse here contains the value of the module parameter */
+       /* decide if pid parsing can be deactivated:
+        * is possible (by speed) and wanted (by user)
+        */
+       switch (dib->dibdev->dev_cl->id) {
+               case DIBUSB2_0:
+                       if (dib->udev->speed == USB_SPEED_HIGH && !dib->pid_parse) {
+                               def_pid_parse = 0;
+                               info("running at HIGH speed, will deliver the complete TS.");
+                       } else
+                               info("will use pid_parsing.");
+                       break;
+               default: 
+                       break;
+       }
+       /* from here on it contains the device and user decision */
+       dib->pid_parse = def_pid_parse;
+       
+       return 0;
+}
+
+int dibusb_urb_exit(struct usb_dibusb *dib)
+{
+       int i;
+       if (dib->init_state & DIBUSB_STATE_URB_LIST) {
+               for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) {
+                       if (dib->urb_list[i] != NULL) {
+                               deb_info("killing URB no. %d.\n",i);
+
+                               /* stop the URBs */
+                               usb_kill_urb(dib->urb_list[i]);
+                               
+                               deb_info("freeing URB no. %d.\n",i);
+                               /* free the URBs */
+                               usb_free_urb(dib->urb_list[i]);
+                       }
+               }
+               /* free the urb array */
+               kfree(dib->urb_list);
+               dib->init_state &= ~DIBUSB_STATE_URB_SUBMIT;
+               dib->init_state &= ~DIBUSB_STATE_URB_LIST;
+       }
+
+       if (dib->init_state & DIBUSB_STATE_URB_BUF)
+               pci_free_consistent(NULL,
+                       dib->dibdev->dev_cl->urb_buffer_size*dib->dibdev->dev_cl->urb_count,
+                       dib->buffer,dib->dma_handle);
+
+       dib->init_state &= ~DIBUSB_STATE_URB_BUF;
+       return 0;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb.h b/drivers/media/dvb/dibusb/dvb-dibusb.h
new file mode 100644 (file)
index 0000000..8a17fc0
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * dvb-dibusb.h
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ *     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, version 2.
+ *
+ * for more information see dvb-dibusb.c .
+ */
+
+#ifndef __DVB_DIBUSB_H__
+#define __DVB_DIBUSB_H__
+
+#include "dib3000.h"
+
+typedef enum {
+       DIBUSB1_1 = 0,
+       DIBUSB2_0,
+       DIBUSB1_1_AN2235,
+} dibusb_type;
+
+static const char * dibusb_fw_filenames1_1[] = {
+       "dvb-dibusb-5.0.0.11.fw"
+};
+
+static const char * dibusb_fw_filenames1_1_an2235[] = {
+       "dvb-dibusb-an2235-1.fw"
+};
+
+static const char * dibusb_fw_filenames2_0[] = {
+       "dvb-dibusb-6.0.0.5.fw"
+};
+
+struct dibusb_device_parameter {
+       dibusb_type type;
+       u8 demod_addr;
+       const char **fw_filenames;
+       const char *usb_controller;
+       u16 usb_cpu_csreg;
+
+       int num_urbs;
+       int urb_buf_size;
+       int default_size;
+       int firmware_bug;
+
+       int cmd_pipe;
+       int result_pipe;
+       int data_pipe;
+};
+
+static struct dibusb_device_parameter dibusb_dev_parm[3] = {
+       {       .type = DIBUSB1_1,
+               .demod_addr = 0x10,
+               .fw_filenames = dibusb_fw_filenames1_1,
+               .usb_controller = "Cypress AN2135",
+               .usb_cpu_csreg = 0x7f92,
+
+               .num_urbs = 3,
+               .urb_buf_size = 4096,
+               .default_size = 188*21,
+               .firmware_bug = 1,
+
+               .cmd_pipe = 0x01,
+               .result_pipe = 0x81,
+               .data_pipe = 0x82,
+       },
+       {       .type = DIBUSB2_0,
+               .demod_addr = 0x18,
+               .fw_filenames = dibusb_fw_filenames2_0,
+               .usb_controller = "Cypress FX2",
+               .usb_cpu_csreg = 0xe600,
+
+               .num_urbs = 3,
+               .urb_buf_size = 40960,
+               .default_size = 188*210,
+               .firmware_bug = 0,
+
+               .cmd_pipe = 0x01,
+               .result_pipe = 0x81,
+               .data_pipe = 0x86,
+       },
+       {       .type = DIBUSB1_1_AN2235,
+               .demod_addr = 0x10,
+               .fw_filenames = dibusb_fw_filenames1_1_an2235,
+               .usb_controller = "Cypress CY7C64613 (AN2235)",
+               .usb_cpu_csreg = 0x7f92,
+
+               .num_urbs = 3,
+               .urb_buf_size = 4096,
+               .default_size = 188*21,
+               .firmware_bug = 1,
+
+               .cmd_pipe = 0x01,
+               .result_pipe = 0x81,
+               .data_pipe = 0x82,
+       }
+};
+
+struct dibusb_device {
+       const char *name;
+       u16 cold_product_id;
+       u16 warm_product_id;
+       struct dibusb_device_parameter *parm;
+};
+
+/* Vendor IDs */
+#define USB_VID_ANCHOR                                         0x0547
+#define USB_VID_AVERMEDIA                                      0x14aa
+#define USB_VID_COMPRO                                         0x185b
+#define USB_VID_COMPRO_UNK                                     0x145f
+#define USB_VID_CYPRESS                                                0x04b4
+#define USB_VID_DIBCOM                                         0x10b8
+#define USB_VID_EMPIA                                          0xeb1a
+#define USB_VID_GRANDTEC                                       0x5032
+#define USB_VID_HYPER_PALTEK                           0x1025
+#define USB_VID_IMC_NETWORKS                           0x13d3
+#define USB_VID_TWINHAN                                                0x1822
+#define USB_VID_ULTIMA_ELECTRONIC                      0x05d8
+
+/* Product IDs */
+#define USB_PID_AVERMEDIA_DVBT_USB_COLD                0x0001
+#define USB_PID_AVERMEDIA_DVBT_USB_WARM                0x0002
+#define USB_PID_COMPRO_DVBU2000_COLD           0xd000
+#define USB_PID_COMPRO_DVBU2000_WARM           0xd001
+#define USB_PID_COMPRO_DVBU2000_UNK_COLD       0x010c
+#define USB_PID_COMPRO_DVBU2000_UNK_WARM       0x010d
+#define USB_PID_DIBCOM_MOD3000_COLD                    0x0bb8
+#define USB_PID_DIBCOM_MOD3000_WARM                    0x0bb9
+#define USB_PID_DIBCOM_MOD3001_COLD                    0x0bc6
+#define USB_PID_DIBCOM_MOD3001_WARM                    0x0bc7
+#define USB_PID_GRANDTEC_DVBT_USB_COLD         0x0fa0
+#define USB_PID_GRANDTEC_DVBT_USB_WARM         0x0fa1
+#define USB_PID_KWORLD_VSTREAM_COLD                    0x17de
+#define USB_PID_KWORLD_VSTREAM_WARM                    0x17df
+#define USB_PID_TWINHAN_VP7041_COLD                    0x3201
+#define USB_PID_TWINHAN_VP7041_WARM                    0x3202
+#define USB_PID_ULTIMA_TVBOX_COLD                      0x8105
+#define USB_PID_ULTIMA_TVBOX_WARM                      0x8106
+#define USB_PID_ULTIMA_TVBOX_AN2235_COLD       0x8107
+#define USB_PID_ULTIMA_TVBOX_AN2235_WARM       0x8108
+#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD       0x2235
+#define USB_PID_ULTIMA_TVBOX_USB2_COLD         0x8109
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD      0x8613
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM      0x1002
+#define USB_PID_UNK_HYPER_PALTEK_COLD          0x005e
+#define USB_PID_UNK_HYPER_PALTEK_WARM          0x005f
+#define USB_PID_YAKUMO_DTT200U_COLD                    0x0201
+#define USB_PID_YAKUMO_DTT200U_WARM                    0x0301
+
+#define DIBUSB_SUPPORTED_DEVICES       15
+
+/* USB Driver stuff */
+static struct dibusb_device dibusb_devices[DIBUSB_SUPPORTED_DEVICES] = {
+       {       .name = "TwinhanDTV USB1.1 / Magic Box / HAMA USB1.1 DVB-T device",
+               .cold_product_id = USB_PID_TWINHAN_VP7041_COLD,
+               .warm_product_id = USB_PID_TWINHAN_VP7041_WARM,
+               .parm = &dibusb_dev_parm[0],
+       },
+       {       .name = "KWorld V-Stream XPERT DTV - DVB-T USB1.1",
+               .cold_product_id = USB_PID_KWORLD_VSTREAM_COLD,
+               .warm_product_id = USB_PID_KWORLD_VSTREAM_WARM,
+               .parm = &dibusb_dev_parm[0],
+       },
+       {       .name = "Grandtec USB1.1 DVB-T/DiBcom USB1.1 DVB-T reference design (MOD3000)",
+               .cold_product_id = USB_PID_DIBCOM_MOD3000_COLD,
+               .warm_product_id = USB_PID_DIBCOM_MOD3000_WARM,
+               .parm = &dibusb_dev_parm[0],
+       },
+       {       .name = "Artec T1 USB1.1 TVBOX with AN2135",
+               .cold_product_id = USB_PID_ULTIMA_TVBOX_COLD,
+               .warm_product_id = USB_PID_ULTIMA_TVBOX_WARM,
+               .parm = &dibusb_dev_parm[0],
+       },
+       {       .name = "Artec T1 USB1.1 TVBOX with AN2235",
+               .cold_product_id = USB_PID_ULTIMA_TVBOX_AN2235_COLD,
+               .warm_product_id = USB_PID_ULTIMA_TVBOX_AN2235_WARM,
+               .parm = &dibusb_dev_parm[2],
+       },
+       {       .name = "Artec T1 USB1.1 TVBOX with AN2235 (misdesigned)",
+               .cold_product_id = USB_PID_ULTIMA_TVBOX_ANCHOR_COLD,
+               .warm_product_id = 0, /* undefined, this design becomes USB_PID_DIBCOM_MOD3000_WARM in warm state */
+               .parm = &dibusb_dev_parm[2],
+       },
+       {       .name = "Artec T1 USB2.0 TVBOX (please report the warm ID)",
+               .cold_product_id = USB_PID_ULTIMA_TVBOX_USB2_COLD,
+               .warm_product_id = 0, /* don't know, it is most likely that the device will get another USB ID in warm state */
+               .parm = &dibusb_dev_parm[1],
+       },
+       {       .name = "Artec T1 USB2.0 TVBOX with FX2 IDs (misdesigned, please report the warm ID)",
+               .cold_product_id = USB_PID_ULTIMA_TVBOX_USB2_FX_COLD,
+               .warm_product_id = USB_PID_ULTIMA_TVBOX_USB2_FX_WARM, /* undefined, it could be that the device will get another USB ID in warm state */
+               .parm = &dibusb_dev_parm[1],
+       },
+       {       .name = "Compro Videomate DVB-U2000 - DVB-T USB1.1",
+               .cold_product_id = USB_PID_COMPRO_DVBU2000_COLD,
+               .warm_product_id = USB_PID_COMPRO_DVBU2000_WARM,
+               .parm = &dibusb_dev_parm[0],
+       },
+       {       .name = "Compro Videomate DVB-U2000 - DVB-T USB1.1 (really ?? please report the name!)",
+               .cold_product_id = USB_PID_COMPRO_DVBU2000_UNK_COLD,
+               .warm_product_id = USB_PID_COMPRO_DVBU2000_UNK_WARM,
+               .parm = &dibusb_dev_parm[0],
+       },
+       {       .name = "Unkown USB1.1 DVB-T device ???? please report the name to the author",
+               .cold_product_id = USB_PID_UNK_HYPER_PALTEK_COLD,
+               .warm_product_id = USB_PID_UNK_HYPER_PALTEK_WARM,
+               .parm = &dibusb_dev_parm[0],
+       },
+       {       .name = "DiBcom USB2.0 DVB-T reference design (MOD3000P)",
+               .cold_product_id = USB_PID_DIBCOM_MOD3001_COLD,
+               .warm_product_id = USB_PID_DIBCOM_MOD3001_WARM,
+               .parm = &dibusb_dev_parm[1],
+       },
+       {       .name = "Grandtec DVB-T USB1.1",
+               .cold_product_id = USB_PID_GRANDTEC_DVBT_USB_COLD,
+               .warm_product_id = USB_PID_GRANDTEC_DVBT_USB_WARM,
+               .parm = &dibusb_dev_parm[0],
+       },
+       {       .name = "Avermedia AverTV DVBT USB1.1",
+               .cold_product_id = USB_PID_AVERMEDIA_DVBT_USB_COLD,
+               .warm_product_id = USB_PID_AVERMEDIA_DVBT_USB_WARM,
+               .parm = &dibusb_dev_parm[0],
+       },
+       {       .name = "Yakumo DVB-T mobile USB2.0",
+               .cold_product_id = USB_PID_YAKUMO_DTT200U_COLD,
+               .warm_product_id = USB_PID_YAKUMO_DTT200U_WARM,
+               .parm = &dibusb_dev_parm[1],
+       }
+};
+
+/* USB Driver stuff */
+/* table of devices that work with this driver */
+static struct usb_device_id dibusb_table [] = {
+       { USB_DEVICE(USB_VID_AVERMEDIA,         USB_PID_AVERMEDIA_DVBT_USB_COLD)},
+       { USB_DEVICE(USB_VID_AVERMEDIA,         USB_PID_AVERMEDIA_DVBT_USB_WARM)},
+       { USB_DEVICE(USB_VID_COMPRO,            USB_PID_COMPRO_DVBU2000_COLD) },
+       { USB_DEVICE(USB_VID_COMPRO,            USB_PID_COMPRO_DVBU2000_WARM) },
+       { USB_DEVICE(USB_VID_DIBCOM,            USB_PID_DIBCOM_MOD3000_COLD) },
+       { USB_DEVICE(USB_VID_DIBCOM,            USB_PID_DIBCOM_MOD3000_WARM) },
+       { USB_DEVICE(USB_VID_DIBCOM,            USB_PID_DIBCOM_MOD3001_COLD) },
+       { USB_DEVICE(USB_VID_DIBCOM,            USB_PID_DIBCOM_MOD3001_WARM) },
+       { USB_DEVICE(USB_VID_EMPIA,                     USB_PID_KWORLD_VSTREAM_COLD) },
+       { USB_DEVICE(USB_VID_EMPIA,                     USB_PID_KWORLD_VSTREAM_WARM) },
+       { USB_DEVICE(USB_VID_GRANDTEC,          USB_PID_GRANDTEC_DVBT_USB_COLD) },
+       { USB_DEVICE(USB_VID_GRANDTEC,          USB_PID_GRANDTEC_DVBT_USB_WARM) },
+       { USB_DEVICE(USB_VID_GRANDTEC,          USB_PID_DIBCOM_MOD3000_COLD) },
+       { USB_DEVICE(USB_VID_GRANDTEC,          USB_PID_DIBCOM_MOD3000_WARM) },
+       { USB_DEVICE(USB_VID_HYPER_PALTEK,      USB_PID_UNK_HYPER_PALTEK_COLD) },
+       { USB_DEVICE(USB_VID_HYPER_PALTEK,      USB_PID_UNK_HYPER_PALTEK_WARM) },
+       { USB_DEVICE(USB_VID_IMC_NETWORKS,      USB_PID_TWINHAN_VP7041_COLD) },
+       { USB_DEVICE(USB_VID_IMC_NETWORKS,      USB_PID_TWINHAN_VP7041_WARM) },
+       { USB_DEVICE(USB_VID_TWINHAN,           USB_PID_TWINHAN_VP7041_COLD) },
+       { USB_DEVICE(USB_VID_TWINHAN,           USB_PID_TWINHAN_VP7041_WARM) },
+       { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) },
+       { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) },
+       { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) },
+       { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) },
+       { USB_DEVICE(USB_VID_AVERMEDIA,         USB_PID_YAKUMO_DTT200U_COLD) },
+       { USB_DEVICE(USB_VID_AVERMEDIA,         USB_PID_YAKUMO_DTT200U_WARM) },
+       { USB_DEVICE(USB_PID_COMPRO_DVBU2000_UNK_COLD, USB_VID_COMPRO_UNK) },
+       { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) },
+
+/*
+ * activate the following define when you have one of the devices and want to
+ * build it from build-2.6 in dvb-kernel
+ */
+// #define CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
+#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
+       { USB_DEVICE(USB_VID_ANCHOR,            USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) },
+       { USB_DEVICE(USB_VID_CYPRESS,           USB_PID_ULTIMA_TVBOX_USB2_FX_COLD) },
+       { USB_DEVICE(USB_VID_ANCHOR,            USB_PID_ULTIMA_TVBOX_USB2_FX_WARM) },
+#endif
+       { }                 /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, dibusb_table);
+
+#define DIBUSB_I2C_TIMEOUT                             HZ*5
+
+struct usb_dibusb {
+       /* usb */
+       struct usb_device * udev;
+
+       struct dibusb_device * dibdev;
+
+       int feedcount;
+       int pid_parse;
+       struct dib3000_xfer_ops xfer_ops;
+
+       struct urb **urb_list;
+       u8 *buffer;
+       dma_addr_t dma_handle;
+
+       /* I2C */
+       struct i2c_adapter i2c_adap;
+       struct i2c_client i2c_client;
+
+       /* locking */
+       struct semaphore usb_sem;
+       struct semaphore i2c_sem;
+
+       /* dvb */
+       int dvb_is_ready;
+       struct dvb_adapter *adapter;
+       struct dmxdev dmxdev;
+       struct dvb_demux demux;
+       struct dvb_net dvb_net;
+       struct dvb_frontend* fe;
+
+       /* remote control */
+       struct input_dev rc_input_dev;
+       struct work_struct rc_query_work;
+       int rc_input_event;
+};
+
+
+/* types of first byte of each buffer */
+
+#define DIBUSB_REQ_START_READ                  0x00
+#define DIBUSB_REQ_START_DEMOD                 0x01
+#define DIBUSB_REQ_I2C_READ                    0x02
+#define DIBUSB_REQ_I2C_WRITE                   0x03
+
+/* prefix for reading the current RC key */
+#define DIBUSB_REQ_POLL_REMOTE                 0x04
+
+#define DIBUSB_RC_NEC_EMPTY                            0x00
+#define DIBUSB_RC_NEC_KEY_PRESSED              0x01
+#define DIBUSB_RC_NEC_KEY_REPEATED             0x02
+
+/* 0x05 0xXX */
+#define DIBUSB_REQ_SET_STREAMING_MODE  0x05
+
+/* interrupt the internal read loop, when blocking */
+#define DIBUSB_REQ_INTR_READ                   0x06
+
+/* IO control
+ * 0x07 <cmd 1 byte> <param 32 bytes>
+ */
+#define DIBUSB_REQ_SET_IOCTL                   0x07
+
+/* IOCTL commands */
+
+/* change the power mode in firmware */
+#define DIBUSB_IOCTL_CMD_POWER_MODE            0x00
+#define DIBUSB_IOCTL_POWER_SLEEP                       0x00
+#define DIBUSB_IOCTL_POWER_WAKEUP                      0x01
+
+#endif
diff --git a/drivers/media/dvb/frontends/at76c651.h b/drivers/media/dvb/frontends/at76c651.h
new file mode 100644 (file)
index 0000000..34054df
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * at76c651.c
+ *
+ * Atmel DVB-C Frontend Driver (at76c651)
+ *
+ * Copyright (C) 2001 fnbrd <fnbrd@gmx.de>
+ *             & 2002-2004 Andreas Oberritter <obi@linuxtv.org>
+ *             & 2003 Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ * 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.
+ *
+ * AT76C651
+ * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf
+ * http://www.atmel.com/atmel/acrobat/doc1320.pdf
+ */
+
+#ifndef AT76C651_H
+#define AT76C651_H
+
+#include <linux/dvb/frontend.h>
+
+struct at76c651_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* at76c651_attach(const struct at76c651_config* config,
+                                           struct i2c_adapter* i2c);
+
+#endif // AT76C651_H
diff --git a/drivers/media/dvb/frontends/cx22700.c b/drivers/media/dvb/frontends/cx22700.c
new file mode 100644 (file)
index 0000000..26744e7
--- /dev/null
@@ -0,0 +1,445 @@
+#/*
+    Conexant cx22700 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+       Holger Waechtler <holger@convergence.de>
+
+    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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "cx22700.h"
+
+
+struct cx22700_state {
+
+       struct i2c_adapter* i2c;
+
+       struct dvb_frontend_ops ops;
+
+       const struct cx22700_config* config;
+
+       struct dvb_frontend frontend;
+};
+
+
+static int debug;
+#define dprintk(args...) \
+       do { \
+               if (debug) printk(KERN_DEBUG "cx22700: " args); \
+       } while (0)
+
+static u8 init_tab [] = {
+       0x04, 0x10,
+       0x05, 0x09,
+       0x06, 0x00,
+       0x08, 0x04,
+       0x09, 0x00,
+       0x0a, 0x01,
+       0x15, 0x40,
+       0x16, 0x10,
+       0x17, 0x87,
+       0x18, 0x17,
+       0x1a, 0x10,
+       0x25, 0x04,
+       0x2e, 0x00,
+       0x39, 0x00,
+       0x3a, 0x04,
+       0x45, 0x08,
+       0x46, 0x02,
+       0x47, 0x05,
+};
+
+
+static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data)
+{
+       int ret;
+       u8 buf [] = { reg, data };
+       struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+       dprintk ("%s\n", __FUNCTION__);
+
+       ret = i2c_transfer (state->i2c, &msg, 1);
+
+       if (ret != 1)
+               printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+                       __FUNCTION__, reg, data, ret);
+
+       return (ret != 1) ? -1 : 0;
+}
+
+static int cx22700_readreg (struct cx22700_state* state, u8 reg)
+{
+       int ret;
+       u8 b0 [] = { reg };
+       u8 b1 [] = { 0 };
+       struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+                          { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+       dprintk ("%s\n", __FUNCTION__);
+
+       ret = i2c_transfer (state->i2c, msg, 2);
+
+       if (ret != 2) return -EIO;
+
+       return b1[0];
+}
+
+static int cx22700_set_inversion (struct cx22700_state* state, int inversion)
+{
+       u8 val;
+
+       dprintk ("%s\n", __FUNCTION__);
+
+       switch (inversion) {
+       case INVERSION_AUTO:
+               return -EOPNOTSUPP;
+       case INVERSION_ON:
+               val = cx22700_readreg (state, 0x09);
+               return cx22700_writereg (state, 0x09, val | 0x01);
+       case INVERSION_OFF:
+               val = cx22700_readreg (state, 0x09);
+               return cx22700_writereg (state, 0x09, val & 0xfe);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int cx22700_set_tps (struct cx22700_state *state, struct dvb_ofdm_parameters *p)
+{
+       static const u8 qam_tab [4] = { 0, 1, 0, 2 };
+       static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 };
+       u8 val;
+
+       dprintk ("%s\n", __FUNCTION__);
+
+       if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8)
+               return -EINVAL;
+
+       if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8)
+
+       if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5)
+               return -EINVAL;
+
+       if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+           p->guard_interval > GUARD_INTERVAL_1_4)
+               return -EINVAL;
+
+       if (p->transmission_mode != TRANSMISSION_MODE_2K &&
+           p->transmission_mode != TRANSMISSION_MODE_8K)
+               return -EINVAL;
+
+       if (p->constellation != QPSK &&
+           p->constellation != QAM_16 &&
+           p->constellation != QAM_64)
+               return -EINVAL;
+
+       if (p->hierarchy_information < HIERARCHY_NONE ||
+           p->hierarchy_information > HIERARCHY_4)
+               return -EINVAL;
+
+       if (p->bandwidth < BANDWIDTH_8_MHZ && p->bandwidth > BANDWIDTH_6_MHZ)
+               return -EINVAL;
+
+       if (p->bandwidth == BANDWIDTH_7_MHZ)
+               cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10));
+       else
+               cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10));
+
+       val = qam_tab[p->constellation - QPSK];
+       val |= p->hierarchy_information - HIERARCHY_NONE;
+
+       cx22700_writereg (state, 0x04, val);
+
+       val = fec_tab[p->code_rate_HP - FEC_1_2] << 3;
+       val |= fec_tab[p->code_rate_LP - FEC_1_2];
+
+       cx22700_writereg (state, 0x05, val);
+
+       val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2;
+       val |= p->transmission_mode - TRANSMISSION_MODE_2K;
+
+       cx22700_writereg (state, 0x06, val);
+
+       cx22700_writereg (state, 0x08, 0x04 | 0x02);  /* use user tps parameters */
+       cx22700_writereg (state, 0x08, 0x04);         /* restart aquisition */
+
+       return 0;
+}
+
+static int cx22700_get_tps (struct cx22700_state* state, struct dvb_ofdm_parameters *p)
+{
+       static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 };
+       static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4,
+                                                   FEC_5_6, FEC_7_8 };
+       u8 val;
+
+       dprintk ("%s\n", __FUNCTION__);
+
+       if (!(cx22700_readreg(state, 0x07) & 0x20))  /*  tps valid? */
+               return -EAGAIN;
+
+       val = cx22700_readreg (state, 0x01);
+
+       if ((val & 0x7) > 4)
+               p->hierarchy_information = HIERARCHY_AUTO;
+       else
+               p->hierarchy_information = HIERARCHY_NONE + (val & 0x7);
+
+       if (((val >> 3) & 0x3) > 2)
+               p->constellation = QAM_AUTO;
+       else
+               p->constellation = qam_tab[(val >> 3) & 0x3];
+
+       val = cx22700_readreg (state, 0x02);
+
+       if (((val >> 3) & 0x07) > 4)
+               p->code_rate_HP = FEC_AUTO;
+       else
+               p->code_rate_HP = fec_tab[(val >> 3) & 0x07];
+
+       if ((val & 0x07) > 4)
+               p->code_rate_LP = FEC_AUTO;
+       else
+               p->code_rate_LP = fec_tab[val & 0x07];
+
+       val = cx22700_readreg (state, 0x03);
+
+       p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3);
+       p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1);
+
+       return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+static int cx22700_init (struct dvb_frontend* fe)
+
+{      struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+       int i;
+
+       dprintk("cx22700_init: init chip\n");
+
+       cx22700_writereg (state, 0x00, 0x02);   /*  soft reset */
+       cx22700_writereg (state, 0x00, 0x00);
+
+       msleep(10);
+
+       for (i=0; i<sizeof(init_tab); i+=2)
+               cx22700_writereg (state, init_tab[i], init_tab[i+1]);
+
+       cx22700_writereg (state, 0x00, 0x01);
+
+       if (state->config->pll_init) {
+               cx22700_writereg (state, 0x0a, 0x00);  /* open i2c bus switch */
+               state->config->pll_init(fe);
+               cx22700_writereg (state, 0x0a, 0x01);  /* close i2c bus switch */
+       }
+
+       return 0;
+}
+
+static int cx22700_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+       u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+                  | (cx22700_readreg (state, 0x0e) << 1);
+       u8 sync = cx22700_readreg (state, 0x07);
+
+       *status = 0;
+
+       if (rs_ber < 0xff00)
+               *status |= FE_HAS_SIGNAL;
+
+       if (sync & 0x20)
+               *status |= FE_HAS_CARRIER;
+
+       if (sync & 0x10)
+               *status |= FE_HAS_VITERBI;
+
+       if (sync & 0x10)
+               *status |= FE_HAS_SYNC;
+
+       if (*status == 0x0f)
+               *status |= FE_HAS_LOCK;
+
+       return 0;
+}
+
+static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+       struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+       *ber = cx22700_readreg (state, 0x0c) & 0x7f;
+       cx22700_writereg (state, 0x0c, 0x00);
+
+       return 0;
+}
+
+static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+       struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+       u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+                  | (cx22700_readreg (state, 0x0e) << 1);
+       *signal_strength = ~rs_ber;
+
+       return 0;
+}
+
+static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+       struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+       u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+                  | (cx22700_readreg (state, 0x0e) << 1);
+       *snr = ~rs_ber;
+
+       return 0;
+}
+
+static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+       struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+       *ucblocks = cx22700_readreg (state, 0x0f);
+       cx22700_writereg (state, 0x0f, 0x00);
+
+       return 0;
+}
+
+static int cx22700_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+       cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/
+       cx22700_writereg (state, 0x00, 0x00);
+
+       cx22700_writereg (state, 0x0a, 0x00);  /* open i2c bus switch */
+       state->config->pll_set(fe, p);
+       cx22700_writereg (state, 0x0a, 0x01);  /* close i2c bus switch */
+       cx22700_set_inversion (state, p->inversion);
+       cx22700_set_tps (state, &p->u.ofdm);
+       cx22700_writereg (state, 0x37, 0x01);  /* PAL loop filter off */
+       cx22700_writereg (state, 0x00, 0x01);  /* restart acquire */
+
+       return 0;
+}
+
+static int cx22700_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+       u8 reg09 = cx22700_readreg (state, 0x09);
+
+       p->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF;
+       return cx22700_get_tps (state, &p->u.ofdm);
+}
+
+static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        fesettings->min_delay_ms = 150;
+        fesettings->step_size = 166667;
+        fesettings->max_drift = 166667*2;
+        return 0;
+}
+
+static void cx22700_release(struct dvb_frontend* fe)
+{
+       struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+       kfree(state);
+}
+
+static struct dvb_frontend_ops cx22700_ops;
+
+struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+                                   struct i2c_adapter* i2c)
+{
+       struct cx22700_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct cx22700_state*) kmalloc(sizeof(struct cx22700_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &cx22700_ops, sizeof(struct dvb_frontend_ops));
+
+       /* check if the demod is there */
+       if (cx22700_readreg(state, 0x07) < 0) goto error;
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops cx22700_ops = {
+
+       .info = {
+               .name                   = "Conexant CX22700 DVB-T",
+               .type                   = FE_OFDM,
+               .frequency_min          = 470000000,
+               .frequency_max          = 860000000,
+               .frequency_stepsize     = 166667,
+               .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                     FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                     FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+                     FE_CAN_RECOVER
+       },
+
+       .release = cx22700_release,
+
+       .init = cx22700_init,
+
+       .set_frontend = cx22700_set_frontend,
+       .get_frontend = cx22700_get_frontend,
+       .get_tune_settings = cx22700_get_tune_settings,
+
+       .read_status = cx22700_read_status,
+       .read_ber = cx22700_read_ber,
+       .read_signal_strength = cx22700_read_signal_strength,
+       .read_snr = cx22700_read_snr,
+       .read_ucblocks = cx22700_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx22700_attach);
diff --git a/drivers/media/dvb/frontends/cx22700.h b/drivers/media/dvb/frontends/cx22700.h
new file mode 100644 (file)
index 0000000..c9145b4
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+    Conexant CX22700 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+       Holger Waechtler <holger@convergence.de>
+
+    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 CX22700_H
+#define CX22700_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx22700_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+                                          struct i2c_adapter* i2c);
+
+#endif // CX22700_H
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c
new file mode 100644 (file)
index 0000000..15ae903
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+    Conexant 22702 DVB OFDM demodulator driver
+
+    based on:
+        Alps TDMB7 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+         Holger Waechtler <holger@convergence.de>
+
+    Copyright (C) 2004 Steven Toth <steve@toth.demon.co.uk>
+
+    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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
+#include "cx22702.h"
+
+
+struct cx22702_state {
+
+       struct i2c_adapter* i2c;
+
+       struct dvb_frontend_ops ops;
+
+       /* configuration settings */
+       const struct cx22702_config* config;
+
+       struct dvb_frontend frontend;
+
+       /* previous uncorrected block counter */
+       u8 prevUCBlocks;
+};
+
+static int debug = 0;
+#define dprintk        if (debug) printk
+
+/* Register values to initialise the demod */
+static u8 init_tab [] = {
+       0x00, 0x00, /* Stop aquisition */
+       0x0B, 0x06,
+       0x09, 0x01,
+       0x0D, 0x41,
+       0x16, 0x32,
+       0x20, 0x0A,
+       0x21, 0x17,
+       0x24, 0x3e,
+       0x26, 0xff,
+       0x27, 0x10,
+       0x28, 0x00,
+       0x29, 0x00,
+       0x2a, 0x10,
+       0x2b, 0x00,
+       0x2c, 0x10,
+       0x2d, 0x00,
+       0x48, 0xd4,
+       0x49, 0x56,
+       0x6b, 0x1e,
+       0xc8, 0x02,
+       0xf8, 0x02,
+       0xf9, 0x00,
+       0xfa, 0x00,
+       0xfb, 0x00,
+       0xfc, 0x00,
+       0xfd, 0x00,
+};
+
+static int cx22702_writereg (struct cx22702_state* state, u8 reg, u8 data)
+{
+       int ret;
+       u8 buf [] = { reg, data };
+       struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+       ret = i2c_transfer(state->i2c, &msg, 1);
+
+       if (ret != 1)
+               printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+                       __FUNCTION__, reg, data, ret);
+
+       return (ret != 1) ? -1 : 0;
+}
+
+static u8 cx22702_readreg (struct cx22702_state* state, u8 reg)
+{
+       int ret;
+       u8 b0 [] = { reg };
+       u8 b1 [] = { 0 };
+
+       struct i2c_msg msg [] = {
+               { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+               { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+       ret = i2c_transfer(state->i2c, msg, 2);
+
+       if (ret != 2)
+               printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+       return b1[0];
+}
+
+static int cx22702_set_inversion (struct cx22702_state *state, int inversion)
+{
+       u8 val;
+
+       switch (inversion) {
+
+               case INVERSION_AUTO:
+                       return -EOPNOTSUPP;
+
+               case INVERSION_ON:
+                       val = cx22702_readreg (state, 0x0C);
+                       return cx22702_writereg (state, 0x0C, val | 0x01);
+
+               case INVERSION_OFF:
+                       val = cx22702_readreg (state, 0x0C);
+                       return cx22702_writereg (state, 0x0C, val & 0xfe);
+
+               default:
+                       return -EINVAL;
+
+       }
+
+}
+
+/* Retrieve the demod settings */
+static int cx22702_get_tps (struct cx22702_state *state, struct dvb_ofdm_parameters *p)
+{
+       u8 val;
+
+       /* Make sure the TPS regs are valid */
+       if (!(cx22702_readreg(state, 0x0A) & 0x20))
+               return -EAGAIN;
+
+       val = cx22702_readreg (state, 0x01);
+       switch( (val&0x18)>>3) {
+               case 0: p->constellation =   QPSK; break;
+               case 1: p->constellation = QAM_16; break;
+               case 2: p->constellation = QAM_64; break;
+       }
+       switch( val&0x07 ) {
+               case 0: p->hierarchy_information = HIERARCHY_NONE; break;
+               case 1: p->hierarchy_information =    HIERARCHY_1; break;
+               case 2: p->hierarchy_information =    HIERARCHY_2; break;
+               case 3: p->hierarchy_information =    HIERARCHY_4; break;
+       }
+
+
+       val = cx22702_readreg (state, 0x02);
+       switch( (val&0x38)>>3 ) {
+               case 0: p->code_rate_HP = FEC_1_2; break;
+               case 1: p->code_rate_HP = FEC_2_3; break;
+               case 2: p->code_rate_HP = FEC_3_4; break;
+               case 3: p->code_rate_HP = FEC_5_6; break;
+               case 4: p->code_rate_HP = FEC_7_8; break;
+       }
+       switch( val&0x07 ) {
+               case 0: p->code_rate_LP = FEC_1_2; break;
+               case 1: p->code_rate_LP = FEC_2_3; break;
+               case 2: p->code_rate_LP = FEC_3_4; break;
+               case 3: p->code_rate_LP = FEC_5_6; break;
+               case 4: p->code_rate_LP = FEC_7_8; break;
+       }
+
+
+       val = cx22702_readreg (state, 0x03);
+       switch( (val&0x0c)>>2 ) {
+               case 0: p->guard_interval = GUARD_INTERVAL_1_32; break;
+               case 1: p->guard_interval = GUARD_INTERVAL_1_16; break;
+               case 2: p->guard_interval =  GUARD_INTERVAL_1_8; break;
+               case 3: p->guard_interval =  GUARD_INTERVAL_1_4; break;
+}
+       switch( val&0x03 ) {
+               case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break;
+               case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break;
+       }
+
+       return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
+static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       u8 val;
+       struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+       /* set PLL */
+        cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
+       state->config->pll_set(fe, p);
+        cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
+
+       /* set inversion */
+       cx22702_set_inversion (state, p->inversion);
+
+       /* set bandwidth */
+       switch(p->u.ofdm.bandwidth) {
+       case BANDWIDTH_6_MHZ:
+               cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x20 );
+               break;
+       case BANDWIDTH_7_MHZ:
+               cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x10 );
+               break;
+       case BANDWIDTH_8_MHZ:
+               cx22702_writereg(state, 0x0C, cx22702_readreg(state, 0x0C) &0xcf );
+               break;
+       default:
+               dprintk ("%s: invalid bandwidth\n",__FUNCTION__);
+               return -EINVAL;
+       }
+
+
+       p->u.ofdm.code_rate_LP = FEC_AUTO; //temp hack as manual not working
+
+       /* use auto configuration? */
+       if((p->u.ofdm.hierarchy_information==HIERARCHY_AUTO) ||
+          (p->u.ofdm.constellation==QAM_AUTO) ||
+          (p->u.ofdm.code_rate_HP==FEC_AUTO) ||
+          (p->u.ofdm.code_rate_LP==FEC_AUTO) ||
+          (p->u.ofdm.guard_interval==GUARD_INTERVAL_AUTO) ||
+          (p->u.ofdm.transmission_mode==TRANSMISSION_MODE_AUTO) ) {
+
+               /* TPS Source - use hardware driven values */
+               cx22702_writereg(state, 0x06, 0x10);
+               cx22702_writereg(state, 0x07, 0x9);
+               cx22702_writereg(state, 0x08, 0xC1);
+               cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) & 0xfc );
+               cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );
+               cx22702_writereg(state, 0x00, 0x01); /* Begin aquisition */
+               printk("%s: Autodetecting\n",__FUNCTION__);
+               return 0;
+       }
+
+       /* manually programmed values */
+       val=0;
+       switch(p->u.ofdm.constellation) {
+               case   QPSK: val = (val&0xe7); break;
+               case QAM_16: val = (val&0xe7)|0x08; break;
+               case QAM_64: val = (val&0xe7)|0x10; break;
+               default:
+                       dprintk ("%s: invalid constellation\n",__FUNCTION__);
+                       return -EINVAL;
+       }
+       switch(p->u.ofdm.hierarchy_information) {
+               case HIERARCHY_NONE: val = (val&0xf8); break;
+               case    HIERARCHY_1: val = (val&0xf8)|1; break;
+               case    HIERARCHY_2: val = (val&0xf8)|2; break;
+               case    HIERARCHY_4: val = (val&0xf8)|3; break;
+               default:
+                       dprintk ("%s: invalid hierarchy\n",__FUNCTION__);
+                       return -EINVAL;
+       }
+       cx22702_writereg (state, 0x06, val);
+
+       val=0;
+       switch(p->u.ofdm.code_rate_HP) {
+               case FEC_NONE:
+               case FEC_1_2: val = (val&0xc7); break;
+               case FEC_2_3: val = (val&0xc7)|0x08; break;
+               case FEC_3_4: val = (val&0xc7)|0x10; break;
+               case FEC_5_6: val = (val&0xc7)|0x18; break;
+               case FEC_7_8: val = (val&0xc7)|0x20; break;
+               default:
+                       dprintk ("%s: invalid code_rate_HP\n",__FUNCTION__);
+                       return -EINVAL;
+       }
+       switch(p->u.ofdm.code_rate_LP) {
+               case FEC_NONE:
+               case FEC_1_2: val = (val&0xf8); break;
+               case FEC_2_3: val = (val&0xf8)|1; break;
+               case FEC_3_4: val = (val&0xf8)|2; break;
+               case FEC_5_6: val = (val&0xf8)|3; break;
+               case FEC_7_8: val = (val&0xf8)|4; break;
+               default:
+                       dprintk ("%s: invalid code_rate_LP\n",__FUNCTION__);
+                       return -EINVAL;
+       }
+       cx22702_writereg (state, 0x07, val);
+
+       val=0;
+       switch(p->u.ofdm.guard_interval) {
+               case GUARD_INTERVAL_1_32: val = (val&0xf3); break;
+               case GUARD_INTERVAL_1_16: val = (val&0xf3)|0x04; break;
+               case  GUARD_INTERVAL_1_8: val = (val&0xf3)|0x08; break;
+               case  GUARD_INTERVAL_1_4: val = (val&0xf3)|0x0c; break;
+               default:
+                       dprintk ("%s: invalid guard_interval\n",__FUNCTION__);
+                       return -EINVAL;
+       }
+       switch(p->u.ofdm.transmission_mode) {
+               case TRANSMISSION_MODE_2K: val = (val&0xfc); break;
+               case TRANSMISSION_MODE_8K: val = (val&0xfc)|1; break;
+               default:
+                       dprintk ("%s: invalid transmission_mode\n",__FUNCTION__);
+                       return -EINVAL;
+       }
+       cx22702_writereg(state, 0x08, val);
+       cx22702_writereg(state, 0x0B, (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02 );
+       cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );
+
+       /* Begin channel aquisition */
+       cx22702_writereg(state, 0x00, 0x01);
+
+       return 0;
+}
+
+
+/* Reset the demod hardware and reset all of the configuration registers
+   to a default state. */
+static int cx22702_init (struct dvb_frontend* fe)
+{
+       int i;
+       struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+       cx22702_writereg (state, 0x00, 0x02);
+
+       msleep(10);
+
+       for (i=0; i<sizeof(init_tab); i+=2)
+               cx22702_writereg (state, init_tab[i], init_tab[i+1]);
+
+
+       /* init PLL */
+       if (state->config->pll_init) {
+               cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
+               state->config->pll_init(fe);
+               cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
+       }
+
+       return 0;
+}
+
+static int cx22702_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+       u8 reg0A;
+       u8 reg23;
+
+                       *status = 0;
+
+       reg0A = cx22702_readreg (state, 0x0A);
+       reg23 = cx22702_readreg (state, 0x23);
+
+                       dprintk ("%s: status demod=0x%02x agc=0x%02x\n"
+                               ,__FUNCTION__,reg0A,reg23);
+
+                       if(reg0A & 0x10) {
+                               *status |= FE_HAS_LOCK;
+                               *status |= FE_HAS_VITERBI;
+                               *status |= FE_HAS_SYNC;
+                       }
+
+                       if(reg0A & 0x20)
+                               *status |= FE_HAS_CARRIER;
+
+                       if(reg23 < 0xf0)
+                               *status |= FE_HAS_SIGNAL;
+
+       return 0;
+                       }
+
+static int cx22702_read_ber(struct dvb_frontend* fe, u32* ber)
+               {
+       struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+       if(cx22702_readreg (state, 0xE4) & 0x02) {
+                               /* Realtime statistics */
+               *ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+                       | (cx22702_readreg (state, 0xDF)&0x7F);
+                       } else {
+               /* Averagtine statistics */
+               *ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+                       | cx22702_readreg (state, 0xDF);
+               }
+
+       return 0;
+               }
+
+static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+               {
+       struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+       *signal_strength = cx22702_readreg (state, 0x23);
+
+       return 0;
+}
+
+static int cx22702_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+       struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+       u16 rs_ber=0;
+       if(cx22702_readreg (state, 0xE4) & 0x02) {
+               /* Realtime statistics */
+               rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+                       | (cx22702_readreg (state, 0xDF)& 0x7F);
+       } else {
+               /* Averagine statistics */
+               rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 8
+                       | cx22702_readreg (state, 0xDF);
+       }
+       *snr = ~rs_ber;
+
+       return 0;
+}
+
+static int cx22702_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+       struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+       u8 _ucblocks;
+
+       /* RS Uncorrectable Packet Count then reset */
+       _ucblocks = cx22702_readreg (state, 0xE3);
+       if (state->prevUCBlocks < _ucblocks) *ucblocks = (_ucblocks - state->prevUCBlocks);
+       else *ucblocks = state->prevUCBlocks - _ucblocks;
+       state->prevUCBlocks = _ucblocks;
+
+       return 0;
+}
+
+static int cx22702_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+       u8 reg0C = cx22702_readreg (state, 0x0C);
+
+       p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF;
+       return cx22702_get_tps (state, &p->u.ofdm);
+}
+
+static void cx22702_release(struct dvb_frontend* fe)
+{
+       struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+               kfree(state);
+       }
+
+static struct dvb_frontend_ops cx22702_ops;
+
+struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
+                                   struct i2c_adapter* i2c)
+{
+       struct cx22702_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct cx22702_state*) kmalloc(sizeof(struct cx22702_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &cx22702_ops, sizeof(struct dvb_frontend_ops));
+       state->prevUCBlocks = 0;
+
+       /* check if the demod is there */
+       if (cx22702_readreg(state, 0x1f) != 0x3) goto error;
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops cx22702_ops = {
+
+       .info = {
+               .name                   = "Conexant CX22702 DVB-T",
+               .type                   = FE_OFDM,
+               .frequency_min          = 177000000,
+               .frequency_max          = 858000000,
+               .frequency_stepsize     = 166666,
+               .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+               FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+               FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+               FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+               FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
+       },
+
+       .release = cx22702_release,
+
+       .init = cx22702_init,
+
+       .set_frontend = cx22702_set_tps,
+       .get_frontend = cx22702_get_frontend,
+
+       .read_status = cx22702_read_status,
+       .read_ber = cx22702_read_ber,
+       .read_signal_strength = cx22702_read_signal_strength,
+       .read_snr = cx22702_read_snr,
+       .read_ucblocks = cx22702_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable verbose debug messages");
+
+MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx22702_attach);
diff --git a/drivers/media/dvb/frontends/cx22702.h b/drivers/media/dvb/frontends/cx22702.h
new file mode 100644 (file)
index 0000000..6e34f99
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    Conexant 22702 DVB OFDM demodulator driver
+
+    based on:
+        Alps TDMB7 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+         Holger Waechtler <holger@convergence.de>
+
+    Copyright (C) 2004 Steven Toth <steve@toth.demon.co.uk>
+
+    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 CX22702_H
+#define CX22702_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx22702_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
+                                          struct i2c_adapter* i2c);
+
+#endif // CX22702_H
diff --git a/drivers/media/dvb/frontends/cx24110.h b/drivers/media/dvb/frontends/cx24110.h
new file mode 100644 (file)
index 0000000..6b663f4
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    cx24110 - Single Chip Satellite Channel Receiver driver module
+
+    Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de> based on
+    work
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+
+    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 CX24110_H
+#define CX24110_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24110_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
+                                          struct i2c_adapter* i2c);
+
+extern int cx24110_pll_write(struct dvb_frontend* fe, u32 data);
+
+#endif // CX24110_H
diff --git a/drivers/media/dvb/frontends/dib3000-common.c b/drivers/media/dvb/frontends/dib3000-common.c
new file mode 100644 (file)
index 0000000..3a1b892
--- /dev/null
@@ -0,0 +1,145 @@
+#include "dib3000-common.h"
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+static int debug;
+module_param(debug, int, 0x644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c,4=srch (|-able)).");
+#endif
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_i2c(args...) dprintk(0x02,args)
+#define deb_srch(args...) dprintk(0x04,args)
+
+
+int dib3000_read_reg(struct dib3000_state *state, u16 reg)
+{
+       u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff };
+       u8 rb[2];
+       struct i2c_msg msg[] = {
+               { .addr = state->config.demod_address, .flags = 0,        .buf = wb, .len = 2 },
+               { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+       };
+
+       if (i2c_transfer(state->i2c, msg, 2) != 2)
+               deb_i2c("i2c read error\n");
+
+       deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,
+                       (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]);
+
+       return (rb[0] << 8) | rb[1];
+}
+
+int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val)
+{
+       u8 b[] = {
+               (reg >> 8) & 0xff, reg & 0xff,
+               (val >> 8) & 0xff, val & 0xff,
+       };
+       struct i2c_msg msg[] = {
+               { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 }
+       };
+       deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val);
+
+       return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+
+int dib3000_init_pid_list(struct dib3000_state *state, int num)
+{
+       int i;
+       if (state != NULL) {
+               state->pid_list = kmalloc(sizeof(struct dib3000_pid) * num,GFP_KERNEL);
+               if (state->pid_list == NULL)
+                       return -ENOMEM;
+
+               deb_info("initializing %d pids for the pid_list.\n",num);
+               state->pid_list_lock = SPIN_LOCK_UNLOCKED;
+               memset(state->pid_list,0,num*(sizeof(struct dib3000_pid)));
+               for (i=0; i < num; i++) {
+                       state->pid_list[i].pid = 0;
+                       state->pid_list[i].active = 0;
+               }
+               state->feedcount = 0;
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+
+void dib3000_dealloc_pid_list(struct dib3000_state *state)
+{
+       if (state != NULL && state->pid_list != NULL)
+               kfree(state->pid_list);
+}
+
+/* fetch a pid from pid_list */
+int dib3000_get_pid_index(struct dib3000_pid pid_list[], int num_pids, int pid,
+               spinlock_t *pid_list_lock,int onoff)
+{
+       int i,ret = -1;
+       unsigned long flags;
+
+       spin_lock_irqsave(pid_list_lock,flags);
+       for (i=0; i < num_pids; i++)
+               if (onoff) {
+                       if (!pid_list[i].active) {
+                               pid_list[i].pid = pid;
+                               pid_list[i].active = 1;
+                               ret = i;
+                               break;
+                       }
+               } else {
+                       if (pid_list[i].active && pid_list[i].pid == pid) {
+                               pid_list[i].pid = 0;
+                               pid_list[i].active = 0;
+                               ret = i;
+                               break;
+                       }
+               }
+
+       deb_info("setting pid: %5d %04x at index %d '%s'\n",pid,pid,ret,onoff ? "on" : "off");
+
+       spin_unlock_irqrestore(pid_list_lock,flags);
+       return ret;
+}
+
+int dib3000_search_status(u16 irq,u16 lock)
+{
+       if (irq & 0x02) {
+               if (lock & 0x01) {
+                       deb_srch("auto search succeeded\n");
+                       return 1; // auto search succeeded
+               } else {
+                       deb_srch("auto search not successful\n");
+                       return 0; // auto search failed
+               }
+       } else if (irq & 0x01)  {
+               deb_srch("auto search failed\n");
+               return 0; // auto search failed
+       }
+       return -1; // try again
+}
+
+/* for auto search */
+u16 dib3000_seq[2][2][2] =     /* fft,gua,   inv   */
+       { /* fft */
+               { /* gua */
+                       { 0, 1 },                   /*  0   0   { 0,1 } */
+                       { 3, 9 },                   /*  0   1   { 0,1 } */
+               },
+               {
+                       { 2, 5 },                   /*  1   0   { 0,1 } */
+                       { 6, 11 },                  /*  1   1   { 0,1 } */
+               }
+       };
+
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de");
+MODULE_DESCRIPTION("Common functions for the dib3000mb/dib3000mc dvb frontend drivers");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dib3000_seq);
+
+EXPORT_SYMBOL(dib3000_read_reg);
+EXPORT_SYMBOL(dib3000_write_reg);
+EXPORT_SYMBOL(dib3000_init_pid_list);
+EXPORT_SYMBOL(dib3000_dealloc_pid_list);
+EXPORT_SYMBOL(dib3000_get_pid_index);
+EXPORT_SYMBOL(dib3000_search_status);
diff --git a/drivers/media/dvb/frontends/dib3000-common.h b/drivers/media/dvb/frontends/dib3000-common.h
new file mode 100644 (file)
index 0000000..2537a12
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * .h-files for the common use of the frontend drivers made by DiBcom
+ * DiBcom 3000-MB/MC/P
+ *
+ * DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DibCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ *
+ *     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, version 2.
+ *
+ * Acknowledgements
+ *
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ *
+ */
+
+#ifndef DIB3000_COMMON_H
+#define DIB3000_COMMON_H
+
+#include "dvb_frontend.h"
+#include "dib3000.h"
+
+/* info and err, taken from usb.h, if there is anything available like by default,
+ * please change !
+ */
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , __FILE__ , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n" , __FILE__ , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n" , __FILE__ , ## arg)
+
+/* a PID for the pid_filter list, when in use */
+struct dib3000_pid
+{
+       u16 pid;
+       int active;
+};
+
+/* frontend state */
+struct dib3000_state {
+       struct i2c_adapter* i2c;
+
+       struct dvb_frontend_ops ops;
+
+/* configuration settings */
+       struct dib3000_config config;
+
+       spinlock_t pid_list_lock;
+       struct dib3000_pid *pid_list;
+
+       int feedcount;
+
+       struct dvb_frontend frontend;
+       int timing_offset;
+       int timing_offset_comp_done;
+};
+
+/* commonly used methods by the dib3000mb/mc/p frontend */
+extern int dib3000_read_reg(struct dib3000_state *state, u16 reg);
+extern int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val);
+
+extern int dib3000_init_pid_list(struct dib3000_state *state, int num);
+extern void dib3000_dealloc_pid_list(struct dib3000_state *state);
+extern int dib3000_get_pid_index(struct dib3000_pid pid_list[], int num_pids,
+       int pid, spinlock_t *pid_list_lock,int onoff);
+
+extern int dib3000_search_status(u16 irq,u16 lock);
+
+/* handy shortcuts */
+#define rd(reg) dib3000_read_reg(state,reg)
+
+#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \
+       { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; }
+
+#define wr_foreach(a,v) { int i; \
+       if (sizeof(a) != sizeof(v)) \
+               err("sizeof: %d %d is different",sizeof(a),sizeof(v));\
+       for (i=0; i < sizeof(a)/sizeof(u16); i++) \
+               wr(a[i],v[i]); \
+       }
+
+#define set_or(reg,val) wr(reg,rd(reg) | val)
+
+#define set_and(reg,val) wr(reg,rd(reg) & val)
+
+
+/* debug */
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#define dprintk(level,args...) \
+    do { if ((debug & level)) { printk(args); } } while (0)
+#else
+#define dprintk(args...) do { } while (0)
+#endif
+
+/* mask for enabling a specific pid for the pid_filter */
+#define DIB3000_ACTIVATE_PID_FILTERING (0x2000)
+
+/* common values for tuning */
+#define DIB3000_ALPHA_0                                        (     0)
+#define DIB3000_ALPHA_1                                        (     1)
+#define DIB3000_ALPHA_2                                        (     2)
+#define DIB3000_ALPHA_4                                        (     4)
+
+#define DIB3000_CONSTELLATION_QPSK             (     0)
+#define DIB3000_CONSTELLATION_16QAM            (     1)
+#define DIB3000_CONSTELLATION_64QAM            (     2)
+
+#define DIB3000_GUARD_TIME_1_32                        (     0)
+#define DIB3000_GUARD_TIME_1_16                        (     1)
+#define DIB3000_GUARD_TIME_1_8                 (     2)
+#define DIB3000_GUARD_TIME_1_4                 (     3)
+
+#define DIB3000_TRANSMISSION_MODE_2K   (     0)
+#define DIB3000_TRANSMISSION_MODE_8K   (     1)
+
+#define DIB3000_SELECT_LP                              (     0)
+#define DIB3000_SELECT_HP                              (     1)
+
+#define DIB3000_FEC_1_2                                        (     1)
+#define DIB3000_FEC_2_3                                        (     2)
+#define DIB3000_FEC_3_4                                        (     3)
+#define DIB3000_FEC_5_6                                        (     5)
+#define DIB3000_FEC_7_8                                        (     7)
+
+#define DIB3000_HRCH_OFF                               (     0)
+#define DIB3000_HRCH_ON                                        (     1)
+
+#define DIB3000_DDS_INVERSION_OFF              (     0)
+#define DIB3000_DDS_INVERSION_ON               (     1)
+
+#define DIB3000_TUNER_WRITE_ENABLE(a)  (0xffff & (a << 7))
+#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 7) | (1 << 7)))
+
+/* for auto search */
+extern u16 dib3000_seq[2][2][2];
+
+#define DIB3000_REG_MANUFACTOR_ID              (  1025)
+#define DIB3000_I2C_ID_DIBCOM                  (0x01b3)
+
+#define DIB3000_REG_DEVICE_ID                  (  1026)
+#define DIB3000MB_DEVICE_ID                            (0x3000)
+#define DIB3000MC_DEVICE_ID                            (0x3001)
+#define DIB3000P_DEVICE_ID                             (0x3002)
+
+#endif // DIB3000_COMMON_H
diff --git a/drivers/media/dvb/frontends/dib3000.h b/drivers/media/dvb/frontends/dib3000.h
new file mode 100644 (file)
index 0000000..c6b6cae
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * public header file of the frontend drivers for mobile DVB-T demodulators
+ * DiBcom 3000-MB and DiBcom 3000-MC/P (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DibCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ *
+ *     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, version 2.
+ *
+ * Acknowledgements
+ *
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ *
+ */
+
+#ifndef DIB3000_H
+#define DIB3000_H
+
+#include <linux/dvb/frontend.h>
+
+struct dib3000_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* The i2c address of the PLL */
+       u8 pll_addr;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend *fe);
+       int (*pll_set)(struct dvb_frontend *fe, struct dvb_frontend_parameters* params);
+};
+
+struct dib3000_xfer_ops
+{
+       /* pid and transfer handling is done in the demodulator */
+       int (*pid_parse)(struct dvb_frontend *fe, int onoff);
+       int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff);
+       int (*pid_ctrl)(struct dvb_frontend *fe, int pid, int onoff);
+};
+
+extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+                                            struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops);
+
+extern struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config,
+                                            struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops);
+#endif // DIB3000_H
diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c
new file mode 100644 (file)
index 0000000..37eaa74
--- /dev/null
@@ -0,0 +1,833 @@
+/*
+ * Frontend driver for mobile DVB-T demodulator DiBcom 3000-MB
+ * DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DibCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ *
+ *     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, version 2.
+ *
+ * Acknowledgements
+ *
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "dib3000-common.h"
+#include "dib3000mb_priv.h"
+#include "dib3000.h"
+
+/* Version information */
+#define DRIVER_VERSION "0.1"
+#define DRIVER_DESC "DiBcom 3000-MB DVB-T demodulator driver"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+static int debug;
+module_param(debug, int, 0x644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able)).");
+#endif
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_xfer(args...) dprintk(0x02,args)
+#define deb_setf(args...) dprintk(0x04,args)
+#define deb_getf(args...) dprintk(0x08,args)
+
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+                                 struct dvb_frontend_parameters *fep);
+
+static int dib3000mb_set_frontend(struct dvb_frontend* fe,
+               struct dvb_frontend_parameters *fep, int tuner)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+       struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+       fe_code_rate_t fe_cr = FEC_NONE;
+       int search_state,seq;
+
+       if (tuner) {
+               wr(DIB3000MB_REG_TUNER,
+                               DIB3000_TUNER_WRITE_ENABLE(state->config.pll_addr));
+               state->config.pll_set(fe, fep);
+               wr(DIB3000MB_REG_TUNER,
+                               DIB3000_TUNER_WRITE_DISABLE(state->config.pll_addr));
+
+               deb_setf("bandwidth: ");
+               switch (ofdm->bandwidth) {
+                       case BANDWIDTH_8_MHZ:
+                               deb_setf("8 MHz\n");
+                               wr_foreach(dib3000mb_reg_timing_freq,dib3000mb_timing_freq[2]);
+                               wr_foreach(dib3000mb_reg_bandwidth,dib3000mb_bandwidth_8mhz);
+                               break;
+                       case BANDWIDTH_7_MHZ:
+                               deb_setf("7 MHz\n");
+                               wr_foreach(dib3000mb_reg_timing_freq,dib3000mb_timing_freq[1]);
+                               wr_foreach(dib3000mb_reg_bandwidth,dib3000mb_bandwidth_7mhz);
+                               break;
+                       case BANDWIDTH_6_MHZ:
+                               deb_setf("6 MHz\n");
+                               wr_foreach(dib3000mb_reg_timing_freq,dib3000mb_timing_freq[0]);
+                               wr_foreach(dib3000mb_reg_bandwidth,dib3000mb_bandwidth_6mhz);
+                               break;
+                       case BANDWIDTH_AUTO:
+                               return -EOPNOTSUPP;
+                       default:
+                               err("unkown bandwidth value.");
+                               return -EINVAL;
+               }
+       }
+       wr(DIB3000MB_REG_LOCK1_MASK,DIB3000MB_LOCK1_SEARCH_4);
+
+       deb_setf("transmission mode: ");
+       switch (ofdm->transmission_mode) {
+               case TRANSMISSION_MODE_2K:
+                       deb_setf("2k\n");
+                       wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_2K);
+                       break;
+               case TRANSMISSION_MODE_8K:
+                       deb_setf("8k\n");
+                       wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_8K);
+                       break;
+               case TRANSMISSION_MODE_AUTO:
+                       deb_setf("auto\n");
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       deb_setf("guard: ");
+       switch (ofdm->guard_interval) {
+               case GUARD_INTERVAL_1_32:
+                       deb_setf("1_32\n");
+                       wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_32);
+                       break;
+               case GUARD_INTERVAL_1_16:
+                       deb_setf("1_16\n");
+                       wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_16);
+                       break;
+               case GUARD_INTERVAL_1_8:
+                       deb_setf("1_8\n");
+                       wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_8);
+                       break;
+               case GUARD_INTERVAL_1_4:
+                       deb_setf("1_4\n");
+                       wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_4);
+                       break;
+               case GUARD_INTERVAL_AUTO:
+                       deb_setf("auto\n");
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       deb_setf("inversion: ");
+       switch (fep->inversion) {
+               case INVERSION_OFF:
+                       deb_setf("off\n");
+                       wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_OFF);
+                       break;
+               case INVERSION_AUTO:
+                       deb_setf("auto ");
+                       break;
+               case INVERSION_ON:
+                       deb_setf("on\n");
+                       wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_ON);
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       deb_setf("constellation: ");
+       switch (ofdm->constellation) {
+               case QPSK:
+                       deb_setf("qpsk\n");
+                       wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_QPSK);
+                       break;
+               case QAM_16:
+                       deb_setf("qam16\n");
+                       wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_16QAM);
+                       break;
+               case QAM_64:
+                       deb_setf("qam64\n");
+                       wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_64QAM);
+                       break;
+               case QAM_AUTO:
+                       break;
+               default:
+                       return -EINVAL;
+       }
+       deb_setf("hierachy: "); 
+       switch (ofdm->hierarchy_information) {
+               case HIERARCHY_NONE:
+                       deb_setf("none ");
+                       /* fall through */
+               case HIERARCHY_1:
+                       deb_setf("alpha=1\n");  
+                       wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_1);
+                       break;
+               case HIERARCHY_2:
+                       deb_setf("alpha=2\n");  
+                       wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_2);
+                       break;
+               case HIERARCHY_4:
+                       deb_setf("alpha=4\n");  
+                       wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_4);
+                       break;
+               case HIERARCHY_AUTO:
+                       deb_setf("alpha=auto\n");       
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       deb_setf("hierarchy: ");
+       if (ofdm->hierarchy_information == HIERARCHY_NONE) {
+               deb_setf("none\n");
+               wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_OFF);
+               wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_HP);
+               fe_cr = ofdm->code_rate_HP;
+       } else if (ofdm->hierarchy_information != HIERARCHY_AUTO) {
+               deb_setf("on\n");
+               wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_ON);
+               wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_LP);
+               fe_cr = ofdm->code_rate_LP;
+       }
+       deb_setf("fec: ");
+       switch (fe_cr) {
+               case FEC_1_2:
+                       deb_setf("1_2\n");
+                       wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_1_2);
+                       break;
+               case FEC_2_3:
+                       deb_setf("2_3\n");
+                       wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_2_3);
+                       break;
+               case FEC_3_4:
+                       deb_setf("3_4\n");
+                       wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_3_4);
+                       break;
+               case FEC_5_6:
+                       deb_setf("5_6\n");
+                       wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_5_6);
+                       break;
+               case FEC_7_8:
+                       deb_setf("7_8\n");
+                       wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_7_8);
+                       break;
+               case FEC_NONE:
+                       deb_setf("none ");
+                       break;
+               case FEC_AUTO:
+                       deb_setf("auto\n");
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       seq = dib3000_seq
+               [ofdm->transmission_mode == TRANSMISSION_MODE_AUTO]
+               [ofdm->guard_interval == GUARD_INTERVAL_AUTO]
+               [fep->inversion == INVERSION_AUTO];
+
+       deb_setf("seq? %d\n",seq);
+
+       wr(DIB3000MB_REG_SEQ,seq);
+
+       wr(DIB3000MB_REG_ISI,seq ? DIB3000MB_ISI_INHIBIT : DIB3000MB_ISI_ACTIVATE);
+
+       if (ofdm->transmission_mode == TRANSMISSION_MODE_2K) {
+               if (ofdm->guard_interval == GUARD_INTERVAL_1_8) {
+                       wr(DIB3000MB_REG_SYNC_IMPROVEMENT,DIB3000MB_SYNC_IMPROVE_2K_1_8);
+               } else {
+                       wr(DIB3000MB_REG_SYNC_IMPROVEMENT,DIB3000MB_SYNC_IMPROVE_DEFAULT);
+               }
+
+               wr(DIB3000MB_REG_UNK_121,DIB3000MB_UNK_121_2K);
+       } else {
+               wr(DIB3000MB_REG_UNK_121,DIB3000MB_UNK_121_DEFAULT);
+       }
+
+       wr(DIB3000MB_REG_MOBILE_ALGO,DIB3000MB_MOBILE_ALGO_OFF);
+       wr(DIB3000MB_REG_MOBILE_MODE_QAM,DIB3000MB_MOBILE_MODE_QAM_OFF);
+       wr(DIB3000MB_REG_MOBILE_MODE,DIB3000MB_MOBILE_MODE_OFF);
+
+       wr_foreach(dib3000mb_reg_agc_bandwidth,dib3000mb_agc_bandwidth_high);
+
+       wr(DIB3000MB_REG_ISI,DIB3000MB_ISI_ACTIVATE);
+
+       wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_AGC+DIB3000MB_RESTART_CTRL);
+       wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_OFF);
+
+       /* wait for AGC lock */
+       msleep(70);
+
+       wr_foreach(dib3000mb_reg_agc_bandwidth,dib3000mb_agc_bandwidth_low);
+
+       /* something has to be auto searched */
+       if (ofdm->constellation == QAM_AUTO ||
+               ofdm->hierarchy_information == HIERARCHY_AUTO ||
+               fe_cr == FEC_AUTO ||
+               fep->inversion == INVERSION_AUTO) {
+               int as_count=0;
+
+               deb_setf("autosearch enabled.\n");      
+
+               wr(DIB3000MB_REG_ISI,DIB3000MB_ISI_INHIBIT);
+
+               wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_AUTO_SEARCH);
+               wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_OFF);
+
+               while ((search_state =
+                               dib3000_search_status(
+                                       rd(DIB3000MB_REG_AS_IRQ_PENDING),
+                                       rd(DIB3000MB_REG_LOCK2_VALUE))) < 0 && as_count++ < 100)
+                       msleep(1);
+
+               deb_info("search_state after autosearch %d after %d checks\n",search_state,as_count);
+
+               if (search_state == 1) {
+                       struct dvb_frontend_parameters feps;
+                       if (dib3000mb_get_frontend(fe, &feps) == 0) {
+                               deb_setf("reading tuning data from frontend succeeded.\n");
+                               return dib3000mb_set_frontend(fe, &feps, 0);
+                       }
+               }
+
+       } else {
+               wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_CTRL);
+               wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_OFF);
+       }
+
+       return 0;
+}
+
+static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+       wr(DIB3000MB_REG_POWER_CONTROL,DIB3000MB_POWER_UP);
+
+       wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC);
+
+       wr(DIB3000MB_REG_RESET_DEVICE,DIB3000MB_RESET_DEVICE);
+       wr(DIB3000MB_REG_RESET_DEVICE,DIB3000MB_RESET_DEVICE_RST);
+
+       wr(DIB3000MB_REG_CLOCK,DIB3000MB_CLOCK_DEFAULT);
+
+       wr(DIB3000MB_REG_ELECT_OUT_MODE,DIB3000MB_ELECT_OUT_MODE_ON);
+
+       wr(DIB3000MB_REG_DDS_FREQ_MSB,DIB3000MB_DDS_FREQ_MSB);
+       wr(DIB3000MB_REG_DDS_FREQ_LSB,DIB3000MB_DDS_FREQ_LSB);
+
+       wr_foreach(dib3000mb_reg_timing_freq,dib3000mb_timing_freq[2]);
+
+       wr_foreach(dib3000mb_reg_impulse_noise,
+                       dib3000mb_impulse_noise_values[DIB3000MB_IMPNOISE_OFF]);
+
+       wr_foreach(dib3000mb_reg_agc_gain,dib3000mb_default_agc_gain);
+
+       wr(DIB3000MB_REG_PHASE_NOISE,DIB3000MB_PHASE_NOISE_DEFAULT);
+
+       wr_foreach(dib3000mb_reg_phase_noise, dib3000mb_default_noise_phase);
+
+       wr_foreach(dib3000mb_reg_lock_duration,dib3000mb_default_lock_duration);
+
+       wr_foreach(dib3000mb_reg_agc_bandwidth,dib3000mb_agc_bandwidth_low);
+
+       wr(DIB3000MB_REG_LOCK0_MASK,DIB3000MB_LOCK0_DEFAULT);
+       wr(DIB3000MB_REG_LOCK1_MASK,DIB3000MB_LOCK1_SEARCH_4);
+       wr(DIB3000MB_REG_LOCK2_MASK,DIB3000MB_LOCK2_DEFAULT);
+       wr(DIB3000MB_REG_SEQ, dib3000_seq[1][1][1]);
+
+       wr_foreach(dib3000mb_reg_bandwidth,dib3000mb_bandwidth_8mhz);
+
+       wr(DIB3000MB_REG_UNK_68,DIB3000MB_UNK_68);
+       wr(DIB3000MB_REG_UNK_69,DIB3000MB_UNK_69);
+       wr(DIB3000MB_REG_UNK_71,DIB3000MB_UNK_71);
+       wr(DIB3000MB_REG_UNK_77,DIB3000MB_UNK_77);
+       wr(DIB3000MB_REG_UNK_78,DIB3000MB_UNK_78);
+       wr(DIB3000MB_REG_ISI,DIB3000MB_ISI_INHIBIT);
+       wr(DIB3000MB_REG_UNK_92,DIB3000MB_UNK_92);
+       wr(DIB3000MB_REG_UNK_96,DIB3000MB_UNK_96);
+       wr(DIB3000MB_REG_UNK_97,DIB3000MB_UNK_97);
+       wr(DIB3000MB_REG_UNK_106,DIB3000MB_UNK_106);
+       wr(DIB3000MB_REG_UNK_107,DIB3000MB_UNK_107);
+       wr(DIB3000MB_REG_UNK_108,DIB3000MB_UNK_108);
+       wr(DIB3000MB_REG_UNK_122,DIB3000MB_UNK_122);
+       wr(DIB3000MB_REG_MOBILE_MODE_QAM,DIB3000MB_MOBILE_MODE_QAM_OFF);
+       wr(DIB3000MB_REG_BERLEN,DIB3000MB_BERLEN_DEFAULT);
+
+       wr_foreach(dib3000mb_reg_filter_coeffs,dib3000mb_filter_coeffs);
+
+       wr(DIB3000MB_REG_MOBILE_ALGO,DIB3000MB_MOBILE_ALGO_ON);
+       wr(DIB3000MB_REG_MULTI_DEMOD_MSB,DIB3000MB_MULTI_DEMOD_MSB);
+       wr(DIB3000MB_REG_MULTI_DEMOD_LSB,DIB3000MB_MULTI_DEMOD_LSB);
+
+       wr(DIB3000MB_REG_OUTPUT_MODE,DIB3000MB_OUTPUT_MODE_SLAVE);
+
+       wr(DIB3000MB_REG_FIFO_142,DIB3000MB_FIFO_142);
+       wr(DIB3000MB_REG_MPEG2_OUT_MODE,DIB3000MB_MPEG2_OUT_MODE_188);
+       wr(DIB3000MB_REG_PID_PARSE, DIB3000MB_PID_PARSE_ACTIVATE);
+       wr(DIB3000MB_REG_FIFO,DIB3000MB_FIFO_INHIBIT);
+       wr(DIB3000MB_REG_FIFO_146,DIB3000MB_FIFO_146);
+       wr(DIB3000MB_REG_FIFO_147,DIB3000MB_FIFO_147);
+
+       wr(DIB3000MB_REG_DATA_IN_DIVERSITY,DIB3000MB_DATA_DIVERSITY_IN_OFF);
+
+       if (state->config.pll_init) {
+               wr(DIB3000MB_REG_TUNER,
+                       DIB3000_TUNER_WRITE_ENABLE(state->config.pll_addr));
+               state->config.pll_init(fe);
+               wr(DIB3000MB_REG_TUNER,
+                       DIB3000_TUNER_WRITE_DISABLE(state->config.pll_addr));
+       }
+
+       return 0;
+}
+
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+                                 struct dvb_frontend_parameters *fep)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+       struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+       fe_code_rate_t *cr;
+       u16 tps_val;
+       int inv_test1,inv_test2;
+       u32 dds_val, threshold = 0x800000;
+
+       if (!rd(DIB3000MB_REG_TPS_LOCK))
+               return 0;
+
+       dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB);
+       if (dds_val < threshold)
+               inv_test1 = 0;
+       else if (dds_val == threshold)
+               inv_test1 = 1;
+       else
+               inv_test1 = 2;
+
+       dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB);
+       if (dds_val < threshold)
+               inv_test2 = 0;
+       else if (dds_val == threshold)
+               inv_test2 = 1;
+       else
+               inv_test2 = 2;
+
+       fep->inversion =
+               ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) ||
+               ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ?
+               INVERSION_ON : INVERSION_OFF;
+
+       deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion);
+
+       switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) {
+               case DIB3000_CONSTELLATION_QPSK:
+                       deb_getf("QPSK ");
+                       ofdm->constellation = QPSK;
+                       break;
+               case DIB3000_CONSTELLATION_16QAM:
+                       deb_getf("QAM16 ");
+                       ofdm->constellation = QAM_16;
+                       break;
+               case DIB3000_CONSTELLATION_64QAM:
+                       deb_getf("QAM64 ");
+                       ofdm->constellation = QAM_64;
+                       break;
+               default:
+                       err("Unexpected constellation returned by TPS (%d)", tps_val);
+                       break;
+       }
+       deb_getf("TPS: %d\n", tps_val);
+
+       if (rd(DIB3000MB_REG_TPS_HRCH)) {
+               deb_getf("HRCH ON\n");
+               cr = &ofdm->code_rate_LP;
+               ofdm->code_rate_HP = FEC_NONE;
+               switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) {
+                       case DIB3000_ALPHA_0:
+                               deb_getf("HIERARCHY_NONE ");
+                               ofdm->hierarchy_information = HIERARCHY_NONE;
+                               break;
+                       case DIB3000_ALPHA_1:
+                               deb_getf("HIERARCHY_1 ");
+                               ofdm->hierarchy_information = HIERARCHY_1;
+                               break;
+                       case DIB3000_ALPHA_2:
+                               deb_getf("HIERARCHY_2 ");
+                               ofdm->hierarchy_information = HIERARCHY_2;
+                               break;
+                       case DIB3000_ALPHA_4:
+                               deb_getf("HIERARCHY_4 ");
+                               ofdm->hierarchy_information = HIERARCHY_4;
+                               break;
+                       default:
+                               err("Unexpected ALPHA value returned by TPS (%d)", tps_val);
+                               break;
+               }
+               deb_getf("TPS: %d\n", tps_val);
+
+               tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP);
+       } else {
+               deb_getf("HRCH OFF\n");
+               cr = &ofdm->code_rate_HP;
+               ofdm->code_rate_LP = FEC_NONE;
+               ofdm->hierarchy_information = HIERARCHY_NONE;
+
+               tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP);
+       }
+
+       switch (tps_val) {
+               case DIB3000_FEC_1_2:
+                       deb_getf("FEC_1_2 ");
+                       *cr = FEC_1_2;
+                       break;
+               case DIB3000_FEC_2_3:
+                       deb_getf("FEC_2_3 ");
+                       *cr = FEC_2_3;
+                       break;
+               case DIB3000_FEC_3_4:
+                       deb_getf("FEC_3_4 ");
+                       *cr = FEC_3_4;
+                       break;
+               case DIB3000_FEC_5_6:
+                       deb_getf("FEC_5_6 ");
+                       *cr = FEC_4_5;
+                       break;
+               case DIB3000_FEC_7_8:
+                       deb_getf("FEC_7_8 ");
+                       *cr = FEC_7_8;
+                       break;
+               default:
+                       err("Unexpected FEC returned by TPS (%d)", tps_val);
+                       break;
+       }
+       deb_getf("TPS: %d\n",tps_val);
+
+       switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) {
+               case DIB3000_GUARD_TIME_1_32:
+                       deb_getf("GUARD_INTERVAL_1_32 ");
+                       ofdm->guard_interval = GUARD_INTERVAL_1_32;
+                       break;
+               case DIB3000_GUARD_TIME_1_16:
+                       deb_getf("GUARD_INTERVAL_1_16 ");
+                       ofdm->guard_interval = GUARD_INTERVAL_1_16;
+                       break;
+               case DIB3000_GUARD_TIME_1_8:
+                       deb_getf("GUARD_INTERVAL_1_8 ");
+                       ofdm->guard_interval = GUARD_INTERVAL_1_8;
+                       break;
+               case DIB3000_GUARD_TIME_1_4:
+                       deb_getf("GUARD_INTERVAL_1_4 ");
+                       ofdm->guard_interval = GUARD_INTERVAL_1_4;
+                       break;
+               default:
+                       err("Unexpected Guard Time returned by TPS (%d)", tps_val);
+                       break;
+       }
+       deb_getf("TPS: %d\n", tps_val);
+
+       switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) {
+               case DIB3000_TRANSMISSION_MODE_2K:
+                       deb_getf("TRANSMISSION_MODE_2K ");
+                       ofdm->transmission_mode = TRANSMISSION_MODE_2K;
+                       break;
+               case DIB3000_TRANSMISSION_MODE_8K:
+                       deb_getf("TRANSMISSION_MODE_8K ");
+                       ofdm->transmission_mode = TRANSMISSION_MODE_8K;
+                       break;
+               default:
+                       err("unexpected transmission mode return by TPS (%d)", tps_val);
+                       break;
+       }
+       deb_getf("TPS: %d\n", tps_val);
+
+       return 0;
+}
+
+static int dib3000mb_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+       *stat = 0;
+
+       if (rd(DIB3000MB_REG_AGC_LOCK))
+               *stat |= FE_HAS_SIGNAL;
+       if (rd(DIB3000MB_REG_CARRIER_LOCK))
+               *stat |= FE_HAS_CARRIER;
+       if (rd(DIB3000MB_REG_VIT_LCK))
+               *stat |= FE_HAS_VITERBI;
+       if (rd(DIB3000MB_REG_TS_SYNC_LOCK))
+               *stat |= (FE_HAS_SYNC | FE_HAS_LOCK);
+
+       deb_info("actual status is %2x\n",*stat);
+
+       deb_getf("tps %x %x %x %x %x\n",
+                       rd(DIB3000MB_REG_TPS_1),
+                       rd(DIB3000MB_REG_TPS_2),
+                       rd(DIB3000MB_REG_TPS_3),
+                       rd(DIB3000MB_REG_TPS_4),
+                       rd(DIB3000MB_REG_TPS_5));
+       
+       deb_info("autoval: tps: %d, qam: %d, hrch: %d, alpha: %d, hp: %d, lp: %d, guard: %d, fft: %d cell: %d\n",
+                       rd(DIB3000MB_REG_TPS_LOCK),
+                       rd(DIB3000MB_REG_TPS_QAM),
+                       rd(DIB3000MB_REG_TPS_HRCH),
+                       rd(DIB3000MB_REG_TPS_VIT_ALPHA),
+                       rd(DIB3000MB_REG_TPS_CODE_RATE_HP),
+                       rd(DIB3000MB_REG_TPS_CODE_RATE_LP),
+                       rd(DIB3000MB_REG_TPS_GUARD_TIME),
+                       rd(DIB3000MB_REG_TPS_FFT),
+                       rd(DIB3000MB_REG_TPS_CELL_ID));
+
+       //*stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+       return 0;
+}
+
+static int dib3000mb_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+       *ber = ((rd(DIB3000MB_REG_BER_MSB) << 16) | rd(DIB3000MB_REG_BER_LSB) );
+       return 0;
+}
+/*
+ * Amaury:
+ * signal strength is measured with dBm (power compared to mW)
+ * the standard range is -90dBm(low power) to -10 dBm (strong power),
+ * but the calibration is done for -100 dBm to 0dBm
+ */
+
+#define DIB3000MB_AGC_REF_dBm          -14
+#define DIB3000MB_GAIN_SLOPE_dBm       100
+#define DIB3000MB_GAIN_DELTA_dBm       -2
+static int dib3000mb_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+/* TODO log10 
+       u16 sigpow = rd(DIB3000MB_REG_SIGNAL_POWER), 
+               n_agc_power = rd(DIB3000MB_REG_AGC_POWER),
+               rf_power = rd(DIB3000MB_REG_RF_POWER);
+       double rf_power_dBm, ad_power_dBm, minar_power_dBm;
+       
+       if (n_agc_power == 0 )
+               n_agc_power = 1 ;
+
+       ad_power_dBm    = 10 * log10 ( (float)n_agc_power / (float)(1<<16) );
+       minor_power_dBm = ad_power_dBm - DIB3000MB_AGC_REF_dBm;
+       rf_power_dBm = (-DIB3000MB_GAIN_SLOPE_dBm * (float)rf_power / (float)(1<<16) + 
+                       DIB3000MB_GAIN_DELTA_dBm) + minor_power_dBm;
+       // relative rf_power 
+       *strength = (u16) ((rf_power_dBm + 100) / 100 * 0xffff);
+*/
+       *strength = rd(DIB3000MB_REG_SIGNAL_POWER) * 0xffff / 0x170;
+       return 0;
+}
+
+/*
+ * Amaury: 
+ * snr is the signal quality measured in dB.
+ * snr = 10*log10(signal power / noise power)
+ * the best quality is near 35dB (cable transmission & good modulator)
+ * the minimum without errors depend of transmission parameters
+ * some indicative values are given in en300744 Annex A
+ * ex : 16QAM 2/3 (Gaussian)  = 11.1 dB
+ *
+ * If SNR is above 20dB, BER should be always 0.
+ * choose 0dB as the minimum
+ */
+static int dib3000mb_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+       short sigpow = rd(DIB3000MB_REG_SIGNAL_POWER);
+       int icipow = ((rd(DIB3000MB_REG_NOISE_POWER_MSB) & 0xff) << 16) |
+               rd(DIB3000MB_REG_NOISE_POWER_LSB);
+/*
+       float snr_dBm=0;
+
+       if (sigpow > 0 && icipow > 0)
+               snr_dBm = 10.0 * log10( (float) (sigpow<<8) / (float)icipow )  ;
+       else if (sigpow > 0)
+               snr_dBm = 35;
+       
+       *snr = (u16) ((snr_dBm / 35) * 0xffff);
+*/
+       *snr = (sigpow << 8) / ((icipow > 0) ? icipow : 1);
+       return 0;
+}
+
+static int dib3000mb_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+       *unc = rd(DIB3000MB_REG_UNC);
+       return 0;
+}
+
+static int dib3000mb_sleep(struct dvb_frontend* fe)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+       wr(DIB3000MB_REG_POWER_CONTROL,DIB3000MB_POWER_DOWN);
+       return 0;
+}
+
+static int dib3000mb_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+       tune->min_delay_ms = 800;
+       tune->step_size = 166667;
+       tune->max_drift = 166667*2;
+                                       
+       return 0;
+}
+
+static int dib3000mb_fe_init_nonmobile(struct dvb_frontend* fe)
+{
+       return dib3000mb_fe_init(fe, 0);
+}
+
+static int dib3000mb_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep)
+{
+       return dib3000mb_set_frontend(fe, fep, 1);
+}
+
+static void dib3000mb_release(struct dvb_frontend* fe)
+{
+       struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+       kfree(state);
+}
+
+/* pid filter and transfer stuff */
+static int dib3000mb_pid_control(struct dvb_frontend *fe,int pid,int onoff)
+{
+       struct dib3000_state *state = fe->demodulator_priv;
+       int index = dib3000_get_pid_index(state->pid_list, DIB3000MB_NUM_PIDS, pid, &state->pid_list_lock,onoff);
+       pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0);
+
+       if (index >= 0) {
+               wr(index+DIB3000MB_REG_FIRST_PID,pid);
+       } else {
+               err("no more pids for filtering.");
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static int dib3000mb_fifo_control(struct dvb_frontend *fe, int onoff)
+{
+       struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+
+       deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling");
+       if (onoff) {
+               wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_ACTIVATE);
+       } else {
+               wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT);
+       }
+       return 0;
+       }
+
+static int dib3000mb_pid_parse(struct dvb_frontend *fe, int onoff)
+{
+       //struct dib3000_state *state = fe->demodulator_priv;
+       /* switch it off and on */
+       return 0;
+       }
+
+static struct dvb_frontend_ops dib3000mb_ops;
+
+struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+                                     struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops)
+{
+       struct dib3000_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct dib3000_state*) kmalloc(sizeof(struct dib3000_state), GFP_KERNEL);
+       if (state == NULL)
+               goto error;
+
+       /* setup the state */
+       state->i2c = i2c;
+       memcpy(&state->config,config,sizeof(struct dib3000_config));
+       memcpy(&state->ops, &dib3000mb_ops, sizeof(struct dvb_frontend_ops));
+
+       /* check for the correct demod */
+       if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM)
+               goto error;
+
+       if (rd(DIB3000_REG_DEVICE_ID) != DIB3000MB_DEVICE_ID)
+               goto error;
+
+       if (dib3000_init_pid_list(state,DIB3000MB_NUM_PIDS))
+               goto error;
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+
+       /* set the xfer operations */
+       xfer_ops->pid_parse = dib3000mb_pid_parse;
+       xfer_ops->fifo_ctrl = dib3000mb_fifo_control;
+       xfer_ops->pid_ctrl = dib3000mb_pid_control;
+
+       return &state->frontend;
+
+error:
+       if (state)
+       kfree(state);
+       return NULL;
+       }
+
+static struct dvb_frontend_ops dib3000mb_ops = {
+
+       .info = {
+               .name                   = "DiBcom 3000-MB DVB-T",
+               .type                   = FE_OFDM,
+               .frequency_min          = 44250000,
+               .frequency_max          = 867250000,
+               .frequency_stepsize     = 62500,
+               .caps = FE_CAN_INVERSION_AUTO |
+                               FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                               FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                               FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+                               FE_CAN_TRANSMISSION_MODE_AUTO |
+                               FE_CAN_GUARD_INTERVAL_AUTO |
+                               FE_CAN_HIERARCHY_AUTO,
+       },
+
+       .release = dib3000mb_release,
+
+       .init = dib3000mb_fe_init_nonmobile,
+       .sleep = dib3000mb_sleep,
+
+       .set_frontend = dib3000mb_set_frontend_and_tuner,
+       .get_frontend = dib3000mb_get_frontend,
+       .get_tune_settings = dib3000mb_fe_get_tune_settings,
+
+       .read_status = dib3000mb_read_status,
+       .read_ber = dib3000mb_read_ber,
+       .read_signal_strength = dib3000mb_read_signal_strength,
+       .read_snr = dib3000mb_read_snr,
+       .read_ucblocks = dib3000mb_read_unc_blocks,
+};
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dib3000mb_attach);
diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb/frontends/dib3000mb_priv.h
new file mode 100644 (file)
index 0000000..57e61aa
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * dib3000mb_priv.h
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ *     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, version 2.
+ *
+ * for more information see dib3000mb.c .
+ */
+
+#ifndef __DIB3000MB_PRIV_H_INCLUDED__
+#define __DIB3000MB_PRIV_H_INCLUDED__
+
+/* register addresses and some of their default values */
+
+/* restart subsystems */
+#define DIB3000MB_REG_RESTART                  (     0)
+
+#define DIB3000MB_RESTART_OFF                  (     0)
+#define DIB3000MB_RESTART_AUTO_SEARCH          (1 << 1)
+#define DIB3000MB_RESTART_CTRL                         (1 << 2)
+#define DIB3000MB_RESTART_AGC                          (1 << 3)
+
+/* FFT size */
+#define DIB3000MB_REG_FFT                              (     1)
+
+/* Guard time */
+#define DIB3000MB_REG_GUARD_TIME               (     2)
+
+/* QAM */
+#define DIB3000MB_REG_QAM                              (     3)
+
+/* Alpha coefficient high priority Viterbi algorithm */
+#define DIB3000MB_REG_VIT_ALPHA                        (     4)
+
+/* spectrum inversion */
+#define DIB3000MB_REG_DDS_INV                  (     5)
+
+/* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */
+#define DIB3000MB_REG_DDS_FREQ_MSB             (     6)
+#define DIB3000MB_REG_DDS_FREQ_LSB             (     7)
+#define DIB3000MB_DDS_FREQ_MSB                         (   178)
+#define DIB3000MB_DDS_FREQ_LSB                         (  8990)
+
+/* timing frequency (carrier spacing) */
+static u16 dib3000mb_reg_timing_freq[] = { 8,9 };
+static u16 dib3000mb_timing_freq[][2] = {
+       { 126 , 48873 }, /* 6 MHz */
+       { 147 , 57019 }, /* 7 MHz */
+       { 168 , 65164 }, /* 8 MHz */
+};
+
+/* impulse noise parameter */
+/* 36 ??? */
+
+static u16 dib3000mb_reg_impulse_noise[] = { 10,11,12,15,36 };
+
+enum dib3000mb_impulse_noise_type {
+       DIB3000MB_IMPNOISE_OFF,
+       DIB3000MB_IMPNOISE_MOBILE,
+       DIB3000MB_IMPNOISE_FIXED,
+       DIB3000MB_IMPNOISE_DEFAULT
+};
+
+static u16 dib3000mb_impulse_noise_values[][5] = {
+       { 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */
+       { 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */
+       { 0x0001, 0x0004, 0x0020, 0x01bd, 0x0399 }, /* fixed */
+       { 0x0000, 0x0002, 0x000a, 0x01ff, 0x0399 }, /* default */
+};
+
+/*
+ * Dual Automatic-Gain-Control
+ * - gains RF in tuner (AGC1)
+ * - gains IF after filtering (AGC2)
+ */
+
+/* also from 16 to 18 */
+static u16 dib3000mb_reg_agc_gain[] = {
+       19,20,21,22,23,24,25,26,27,28,29,30,31,32
+};
+
+static u16 dib3000mb_default_agc_gain[] =
+       { 0x0001, 52429,   623, 128, 166, 195, 61,   /* RF ??? */
+         0x0001, 53766, 38011,   0,  90,  33, 23 }; /* IF ??? */
+
+/* phase noise */
+/* 36 is set when setting the impulse noise */
+static u16 dib3000mb_reg_phase_noise[] = { 33,34,35,37,38 };
+
+static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 };
+
+/* lock duration */
+static u16 dib3000mb_reg_lock_duration[] = { 39,40 };
+static u16 dib3000mb_default_lock_duration[] = { 135, 135 };
+
+/* AGC loop bandwidth */
+static u16 dib3000mb_reg_agc_bandwidth[] = { 43,44,45,46,47,48,49,50 };
+
+static u16 dib3000mb_agc_bandwidth_low[]  =
+       { 2088, 10, 2088, 10, 3448, 5, 3448, 5 };
+static u16 dib3000mb_agc_bandwidth_high[] =
+       { 2349,  5, 2349,  5, 2586, 2, 2586, 2 };
+
+/*
+ * lock0 definition (coff_lock)
+ */
+#define DIB3000MB_REG_LOCK0_MASK               (    51)
+#define DIB3000MB_LOCK0_DEFAULT                                (     4)
+
+/*
+ * lock1 definition (cpil_lock)
+ * for auto search
+ * which values hide behind the lock masks
+ */
+#define DIB3000MB_REG_LOCK1_MASK               (    52)
+#define DIB3000MB_LOCK1_SEARCH_4                       (0x0004)
+#define DIB3000MB_LOCK1_SEARCH_2048                    (0x0800)
+#define DIB3000MB_LOCK1_DEFAULT                                (0x0001)
+
+/*
+ * lock2 definition (fec_lock) */
+#define DIB3000MB_REG_LOCK2_MASK               (    53)
+#define DIB3000MB_LOCK2_DEFAULT                                (0x0080)
+
+/*
+ * SEQ ? what was that again ... :)
+ * changes when, inversion, guard time and fft is
+ * either automatically detected or not
+ */
+#define DIB3000MB_REG_SEQ                              (    54)
+
+/* bandwidth */
+static u16 dib3000mb_reg_bandwidth[] = { 55,56,57,58,59,60,61,62,63,64,65,66,67 };
+static u16 dib3000mb_bandwidth_6mhz[] =
+       { 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 };
+
+static u16 dib3000mb_bandwidth_7mhz[] =
+       { 0, 28, 64421,  96, 39973, 483,  3255, 0, 1000, 0, 1010, 1, 45264 };
+
+static u16 dib3000mb_bandwidth_8mhz[] =
+       { 0, 25, 23600,  84, 34976, 422, 43808, 0, 1000, 0, 1010, 1, 45264 };
+
+#define DIB3000MB_REG_UNK_68                           (    68)
+#define DIB3000MB_UNK_68                                               (     0)
+
+#define DIB3000MB_REG_UNK_69                           (    69)
+#define DIB3000MB_UNK_69                                               (     0)
+
+#define DIB3000MB_REG_UNK_71                           (    71)
+#define DIB3000MB_UNK_71                                               (     0)
+
+#define DIB3000MB_REG_UNK_77                           (    77)
+#define DIB3000MB_UNK_77                                               (     6)
+
+#define DIB3000MB_REG_UNK_78                           (    78)
+#define DIB3000MB_UNK_78                                               (0x0080)
+
+/* isi */
+#define DIB3000MB_REG_ISI                              (    79)
+#define DIB3000MB_ISI_ACTIVATE                         (     0)
+#define DIB3000MB_ISI_INHIBIT                          (     1)
+
+/* sync impovement */
+#define DIB3000MB_REG_SYNC_IMPROVEMENT (    84)
+#define DIB3000MB_SYNC_IMPROVE_2K_1_8          (     3)
+#define DIB3000MB_SYNC_IMPROVE_DEFAULT         (     0)
+
+/* phase noise compensation inhibition */
+#define DIB3000MB_REG_PHASE_NOISE              (    87)
+#define DIB3000MB_PHASE_NOISE_DEFAULT  (     0)
+
+#define DIB3000MB_REG_UNK_92                           (    92)
+#define DIB3000MB_UNK_92                                               (0x0080)
+
+#define DIB3000MB_REG_UNK_96                           (    96)
+#define DIB3000MB_UNK_96                                               (0x0010)
+
+#define DIB3000MB_REG_UNK_97                           (    97)
+#define DIB3000MB_UNK_97                                               (0x0009)
+
+/* mobile mode ??? */
+#define DIB3000MB_REG_MOBILE_MODE              (   101)
+#define DIB3000MB_MOBILE_MODE_ON                       (     1)
+#define DIB3000MB_MOBILE_MODE_OFF                      (     0)
+
+#define DIB3000MB_REG_UNK_106                  (   106)
+#define DIB3000MB_UNK_106                                      (0x0080)
+
+#define DIB3000MB_REG_UNK_107                  (   107)
+#define DIB3000MB_UNK_107                                      (0x0080)
+
+#define DIB3000MB_REG_UNK_108                  (   108)
+#define DIB3000MB_UNK_108                                      (0x0080)
+
+/* fft */
+#define DIB3000MB_REG_UNK_121                  (   121)
+#define DIB3000MB_UNK_121_2K                           (     7)
+#define DIB3000MB_UNK_121_DEFAULT                      (     5)
+
+#define DIB3000MB_REG_UNK_122                  (   122)
+#define DIB3000MB_UNK_122                                      (  2867)
+
+/* QAM for mobile mode */
+#define DIB3000MB_REG_MOBILE_MODE_QAM  (   126)
+#define DIB3000MB_MOBILE_MODE_QAM_64           (     3)
+#define DIB3000MB_MOBILE_MODE_QAM_QPSK_16      (     1)
+#define DIB3000MB_MOBILE_MODE_QAM_OFF          (     0)
+
+/*
+ * data diversity when having more than one chip on-board
+ * see also DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY
+ */
+#define DIB3000MB_REG_DATA_IN_DIVERSITY                (   127)
+#define DIB3000MB_DATA_DIVERSITY_IN_OFF                        (     0)
+#define DIB3000MB_DATA_DIVERSITY_IN_ON                 (     2)
+
+/* vit hrch */
+#define DIB3000MB_REG_VIT_HRCH                 (   128)
+
+/* vit code rate */
+#define DIB3000MB_REG_VIT_CODE_RATE            (   129)
+
+/* vit select hp */
+#define DIB3000MB_REG_VIT_HP                   (   130)
+
+/* time frame for Bit-Error-Rate calculation */
+#define DIB3000MB_REG_BERLEN                   (   135)
+#define DIB3000MB_BERLEN_LONG                          (     0)
+#define DIB3000MB_BERLEN_DEFAULT                       (     1)
+#define DIB3000MB_BERLEN_MEDIUM                                (     2)
+#define DIB3000MB_BERLEN_SHORT                         (     3)
+
+/* 142 - 152 FIFO parameters
+ * which is what ?
+ */
+
+#define DIB3000MB_REG_FIFO_142                 (   142)
+#define DIB3000MB_FIFO_142                                     (     0)
+
+/* MPEG2 TS output mode */
+#define DIB3000MB_REG_MPEG2_OUT_MODE   (   143)
+#define DIB3000MB_MPEG2_OUT_MODE_204           (     0)
+#define DIB3000MB_MPEG2_OUT_MODE_188           (     1)
+
+#define DIB3000MB_REG_PID_PARSE                        (   144)
+#define DIB3000MB_PID_PARSE_INHIBIT            (     0)
+#define DIB3000MB_PID_PARSE_ACTIVATE   (     1)
+
+#define DIB3000MB_REG_FIFO                             (   145)
+#define DIB3000MB_FIFO_INHIBIT                         (     1)
+#define DIB3000MB_FIFO_ACTIVATE                                (     0)
+
+#define DIB3000MB_REG_FIFO_146                 (   146)
+#define DIB3000MB_FIFO_146                                     (     3)
+
+#define DIB3000MB_REG_FIFO_147                 (   147)
+#define DIB3000MB_FIFO_147                                     (0x0100)
+
+/*
+ * pidfilter
+ * it is not a hardware pidfilter but a filter which drops all pids
+ * except the ones set. Necessary because of the limited USB1.1 bandwidth.
+ * regs 153-168
+ */
+
+#define DIB3000MB_REG_FIRST_PID                        (   153)
+#define DIB3000MB_NUM_PIDS                             (    16)
+
+/*
+ * output mode
+ * USB devices have to use 'slave'-mode
+ * see also DIB3000MB_REG_ELECT_OUT_MODE
+ */
+#define DIB3000MB_REG_OUTPUT_MODE              (   169)
+#define DIB3000MB_OUTPUT_MODE_GATED_CLK                (     0)
+#define DIB3000MB_OUTPUT_MODE_CONT_CLK         (     1)
+#define DIB3000MB_OUTPUT_MODE_SERIAL           (     2)
+#define DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY   (     5)
+#define DIB3000MB_OUTPUT_MODE_SLAVE                    (     6)
+
+/* irq event mask */
+#define DIB3000MB_REG_IRQ_EVENT_MASK           (   170)
+#define DIB3000MB_IRQ_EVENT_MASK                               (     0)
+
+/* filter coefficients */
+static u16 dib3000mb_reg_filter_coeffs[] = {
+       171, 172, 173, 174, 175, 176, 177, 178,
+       179, 180, 181, 182, 183, 184, 185, 186,
+       188, 189, 190, 191, 192, 194
+};
+
+static u16 dib3000mb_filter_coeffs[] = {
+        226,  160,   29,
+        979,  998,   19,
+         22, 1019, 1006,
+       1022,   12,    6,
+       1017, 1017,    3,
+          6,       1019,
+       1021,    2,    3,
+          1,          0,
+};
+
+/*
+ * mobile algorithm (when you are moving with your device)
+ * but not faster than 90 km/h
+ */
+#define DIB3000MB_REG_MOBILE_ALGO              (   195)
+#define DIB3000MB_MOBILE_ALGO_ON                       (     0)
+#define DIB3000MB_MOBILE_ALGO_OFF                      (     1)
+
+/* multiple demodulators algorithm */
+#define DIB3000MB_REG_MULTI_DEMOD_MSB  (   206)
+#define DIB3000MB_REG_MULTI_DEMOD_LSB  (   207)
+
+/* terminator, no more demods */
+#define DIB3000MB_MULTI_DEMOD_MSB                      ( 32767)
+#define DIB3000MB_MULTI_DEMOD_LSB                      (  4095)
+
+/* bring the device into a known  */
+#define DIB3000MB_REG_RESET_DEVICE             (  1024)
+#define DIB3000MB_RESET_DEVICE                         (0x812c)
+#define DIB3000MB_RESET_DEVICE_RST                     (     0)
+
+/* hardware clock configuration */
+#define DIB3000MB_REG_CLOCK                            (  1027)
+#define DIB3000MB_CLOCK_DEFAULT                                (0x9000)
+#define DIB3000MB_CLOCK_DIVERSITY                      (0x92b0)
+
+/* power down config */
+#define DIB3000MB_REG_POWER_CONTROL            (  1028)
+#define DIB3000MB_POWER_DOWN                           (     1)
+#define DIB3000MB_POWER_UP                                     (     0)
+
+/* electrical output mode */
+#define DIB3000MB_REG_ELECT_OUT_MODE   (  1029)
+#define DIB3000MB_ELECT_OUT_MODE_OFF           (     0)
+#define DIB3000MB_ELECT_OUT_MODE_ON                    (     1)
+
+/* set the tuner i2c address */
+#define DIB3000MB_REG_TUNER                            (  1089)
+
+/* monitoring registers (read only) */
+
+/* agc loop locked (size: 1) */
+#define DIB3000MB_REG_AGC_LOCK                 (   324)
+
+/* agc power (size: 16) */
+#define DIB3000MB_REG_AGC_POWER                        (   325)
+
+/* agc1 value (16) */
+#define DIB3000MB_REG_AGC1_VALUE               (   326)
+
+/* agc2 value (16) */
+#define DIB3000MB_REG_AGC2_VALUE               (   327)
+
+/* total RF power (16), can be used for signal strength */
+#define DIB3000MB_REG_RF_POWER                 (   328)
+
+/* dds_frequency with offset (24) */
+#define DIB3000MB_REG_DDS_VALUE_MSB            (   339)
+#define DIB3000MB_REG_DDS_VALUE_LSB            (   340)
+
+/* timing offset signed (24) */
+#define DIB3000MB_REG_TIMING_OFFSET_MSB        (   341)
+#define DIB3000MB_REG_TIMING_OFFSET_LSB        (   342)
+
+/* fft start position (13) */
+#define DIB3000MB_REG_FFT_WINDOW_POS   (   353)
+
+/* carriers locked (1) */
+#define DIB3000MB_REG_CARRIER_LOCK             (   355)
+
+/* noise power (24) */
+#define DIB3000MB_REG_NOISE_POWER_MSB  (   372)
+#define DIB3000MB_REG_NOISE_POWER_LSB  (   373)
+
+#define DIB3000MB_REG_MOBILE_NOISE_MSB (   374)
+#define DIB3000MB_REG_MOBILE_NOISE_LSB (   375)
+
+/*
+ * signal power (16), this and the above can be
+ * used to calculate the signal/noise - ratio
+ */
+#define DIB3000MB_REG_SIGNAL_POWER             (   380)
+
+/* mer (24) */
+#define DIB3000MB_REG_MER_MSB                  (   381)
+#define DIB3000MB_REG_MER_LSB                  (   382)
+
+/*
+ * Transmission Parameter Signalling (TPS)
+ * the following registers can be used to get TPS-information.
+ * The values are according to the DVB-T standard.
+ */
+
+/* TPS locked (1) */
+#define DIB3000MB_REG_TPS_LOCK                 (   394)
+
+/* QAM from TPS (2) (values according to DIB3000MB_REG_QAM) */
+#define DIB3000MB_REG_TPS_QAM                  (   398)
+
+/* hierarchy from TPS (1) */
+#define DIB3000MB_REG_TPS_HRCH                 (   399)
+
+/* alpha from TPS (3) (values according to DIB3000MB_REG_VIT_ALPHA) */
+#define DIB3000MB_REG_TPS_VIT_ALPHA            (   400)
+
+/* code rate high priority from TPS (3) (values according to DIB3000MB_FEC_*) */
+#define DIB3000MB_REG_TPS_CODE_RATE_HP (   401)
+
+/* code rate low priority from TPS (3) if DIB3000MB_REG_TPS_VIT_ALPHA */
+#define DIB3000MB_REG_TPS_CODE_RATE_LP (   402)
+
+/* guard time from TPS (2) (values according to DIB3000MB_REG_GUARD_TIME */
+#define DIB3000MB_REG_TPS_GUARD_TIME   (   403)
+
+/* fft size from TPS (2) (values according to DIB3000MB_REG_FFT) */
+#define DIB3000MB_REG_TPS_FFT                  (   404)
+
+/* cell id from TPS (16) */
+#define DIB3000MB_REG_TPS_CELL_ID              (   406)
+
+/* TPS (68) */
+#define DIB3000MB_REG_TPS_1                            (   408)
+#define DIB3000MB_REG_TPS_2                            (   409)
+#define DIB3000MB_REG_TPS_3                            (   410)
+#define DIB3000MB_REG_TPS_4                            (   411)
+#define DIB3000MB_REG_TPS_5                            (   412)
+
+/* bit error rate (before RS correction) (21) */
+#define DIB3000MB_REG_BER_MSB                  (   414)
+#define DIB3000MB_REG_BER_LSB                  (   415)
+
+/* packet error rate (uncorrected TS packets) (16) */
+#define DIB3000MB_REG_PACKET_ERROR_RATE        (   417)
+
+/* uncorrected packet count (16) */
+#define DIB3000MB_REG_UNC                              (   420)
+
+/* viterbi locked (1) */
+#define DIB3000MB_REG_VIT_LCK                  (   421)
+
+/* viterbi inidcator (16) */
+#define DIB3000MB_REG_VIT_INDICATOR            (   422)
+
+/* transport stream sync lock (1) */
+#define DIB3000MB_REG_TS_SYNC_LOCK             (   423)
+
+/* transport stream RS lock (1) */
+#define DIB3000MB_REG_TS_RS_LOCK               (   424)
+
+/* lock mask 0 value (1) */
+#define DIB3000MB_REG_LOCK0_VALUE              (   425)
+
+/* lock mask 1 value (1) */
+#define DIB3000MB_REG_LOCK1_VALUE              (   426)
+
+/* lock mask 2 value (1) */
+#define DIB3000MB_REG_LOCK2_VALUE              (   427)
+
+/* interrupt pending for auto search */
+#define DIB3000MB_REG_AS_IRQ_PENDING   (   434)
+
+#endif
diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c
new file mode 100644 (file)
index 0000000..8015c21
--- /dev/null
@@ -0,0 +1,860 @@
+/*
+ * Frontend driver for mobile DVB-T demodulator DiBcom 3000-MC/P
+ * DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DibCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ *
+ *     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, version 2.
+ *
+ * Acknowledgements
+ *
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "dib3000-common.h"
+#include "dib3000mc_priv.h"
+#include "dib3000.h"
+
+/* Version information */
+#define DRIVER_VERSION "0.1"
+#define DRIVER_DESC "DiBcom 3000-MC DVB-T demodulator driver"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+static int debug;
+module_param(debug, int, 0x644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able)).");
+#endif
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_xfer(args...) dprintk(0x02,args)
+#define deb_setf(args...) dprintk(0x04,args)
+#define deb_getf(args...) dprintk(0x08,args)
+
+
+static int dib3000mc_set_impulse_noise(struct dib3000_state * state, int mode,
+       fe_transmit_mode_t transmission_mode, fe_bandwidth_t bandwidth)
+{
+       switch (transmission_mode) {
+               case TRANSMISSION_MODE_2K:
+                       wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[0]);
+                       break;
+               case TRANSMISSION_MODE_8K:
+                       wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[1]);
+                       break;
+               default:
+                       break;
+       }
+
+       switch (bandwidth) {
+/*             case BANDWIDTH_5_MHZ:
+                       wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[0]);
+                       break; */
+               case BANDWIDTH_6_MHZ:
+                       wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[1]);
+                       break;
+               case BANDWIDTH_7_MHZ:
+                       wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[2]);
+                       break;
+               case BANDWIDTH_8_MHZ:
+                       wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[3]);
+                       break;
+               default:
+                       break;
+       }
+
+       switch (mode) {
+               case 0: /* no impulse */ /* fall through */
+                       wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[0]);
+                       break;
+               case 1: /* new algo */
+                       wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[1]);
+                       set_or(DIB3000MC_REG_IMP_NOISE_55,DIB3000MC_IMP_NEW_ALGO(0)); /* gives 1<<10 */
+                       break;
+               default: /* old algo */
+                       wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[3]);
+                       break;
+       }
+       return 0;
+}
+
+static int dib3000mc_set_timing(struct dib3000_state *state, int upd_offset,
+               fe_transmit_mode_t fft, fe_bandwidth_t bw)
+{
+       u16 timf_msb,timf_lsb;
+       s32 tim_offset,tim_sgn;
+       u64 comp1,comp2,comp=0;
+
+       switch (bw) {
+               case BANDWIDTH_8_MHZ: comp = DIB3000MC_CLOCK_REF*8; break;
+               case BANDWIDTH_7_MHZ: comp = DIB3000MC_CLOCK_REF*7; break;
+               case BANDWIDTH_6_MHZ: comp = DIB3000MC_CLOCK_REF*6; break;
+               default: err("unknown bandwidth (%d)",bw); break;
+       }
+       timf_msb = (comp >> 16) & 0xff;
+       timf_lsb = (comp & 0xffff);
+
+       // Update the timing offset ;
+       if (upd_offset > 0) {
+               if (!state->timing_offset_comp_done) {
+                       msleep(200);
+                       state->timing_offset_comp_done = 1;
+               }
+               tim_offset = rd(DIB3000MC_REG_TIMING_OFFS_MSB);
+               if ((tim_offset & 0x2000) == 0x2000)
+                       tim_offset |= 0xC000;
+               if (fft == TRANSMISSION_MODE_2K)
+                       tim_offset <<= 2;
+               state->timing_offset += tim_offset;
+       }
+
+       tim_offset = state->timing_offset;
+       if (tim_offset < 0) {
+               tim_sgn = 1;
+               tim_offset = -tim_offset;
+       } else
+               tim_sgn = 0;
+
+       comp1 =  (u32)tim_offset * (u32)timf_lsb ;
+       comp2 =  (u32)tim_offset * (u32)timf_msb ;
+       comp  = ((comp1 >> 16) + comp2) >> 7;
+
+       if (tim_sgn == 0)
+               comp = (u32)(timf_msb << 16) + (u32) timf_lsb + comp;
+       else
+               comp = (u32)(timf_msb << 16) + (u32) timf_lsb - comp ;
+
+       timf_msb = (comp >> 16) & 0xff;
+       timf_lsb = comp & 0xffff;
+
+       wr(DIB3000MC_REG_TIMING_FREQ_MSB,timf_msb);
+       wr(DIB3000MC_REG_TIMING_FREQ_LSB,timf_lsb);
+       return 0;
+}
+
+static int dib3000mc_init_auto_scan(struct dib3000_state *state, fe_bandwidth_t bw, int boost)
+{
+       if (boost) {
+               wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_ON);
+       } else {
+               wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_OFF);
+       }
+       switch (bw) {
+               case BANDWIDTH_8_MHZ:
+                       wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz);
+                       break;
+               case BANDWIDTH_7_MHZ:
+                       wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_7mhz);
+                       break;
+               case BANDWIDTH_6_MHZ:
+                       wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_6mhz);
+                       break;
+/*             case BANDWIDTH_5_MHZ:
+                       wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_5mhz);
+                       break;*/
+               case BANDWIDTH_AUTO:
+                       return -EOPNOTSUPP;
+               default:
+                       err("unknown bandwidth value (%d).",bw);
+                       return -EINVAL;
+       }
+       if (boost) {
+               u32 timeout = (rd(DIB3000MC_REG_BW_TIMOUT_MSB) << 16) +
+                       rd(DIB3000MC_REG_BW_TIMOUT_LSB);
+               timeout *= 85; timeout >>= 7;
+               wr(DIB3000MC_REG_BW_TIMOUT_MSB,(timeout >> 16) & 0xffff);
+               wr(DIB3000MC_REG_BW_TIMOUT_LSB,timeout & 0xffff);
+       }
+       return 0;
+}
+
+static int dib3000mc_get_frontend(struct dvb_frontend* fe,
+                                 struct dvb_frontend_parameters *fep);
+
+static int dib3000mc_set_frontend(struct dvb_frontend* fe,
+                                 struct dvb_frontend_parameters *fep, int tuner)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+       struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+       fe_code_rate_t fe_cr = FEC_NONE;
+       int search_state, seq;
+       u16 val;
+       u8 fft=0, guard=0, qam=0, alpha=0, sel_hp=0, cr=0, hrch=0;
+
+       if (tuner) {
+               wr(DIB3000MC_REG_TUNER,
+                               DIB3000_TUNER_WRITE_ENABLE(state->config.pll_addr));
+               state->config.pll_set(fe, fep);
+               wr(DIB3000MC_REG_TUNER,
+                               DIB3000_TUNER_WRITE_DISABLE(state->config.pll_addr));
+       }
+
+       dib3000mc_set_timing(state,0,ofdm->transmission_mode,ofdm->bandwidth);
+       dib3000mc_init_auto_scan(state, ofdm->bandwidth, 0);
+
+       wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_AGC);
+       wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF);
+
+/* Default cfg isi offset adp */
+       wr_foreach(dib3000mc_reg_offset,dib3000mc_offset[0]);
+
+       wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT | DIB3000MC_ISI_INHIBIT);
+       wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[1]);
+       wr(DIB3000MC_REG_UNK_133,DIB3000MC_UNK_133);
+
+       wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general);
+       if (ofdm->bandwidth == BANDWIDTH_8_MHZ) {
+               wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[3]);
+       } else {
+               wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[0]);
+       }
+
+       switch (ofdm->transmission_mode) {
+               case TRANSMISSION_MODE_2K: fft = DIB3000_TRANSMISSION_MODE_2K; break;
+               case TRANSMISSION_MODE_8K: fft = DIB3000_TRANSMISSION_MODE_8K; break;
+               case TRANSMISSION_MODE_AUTO: break;
+               default: return -EINVAL;
+       }
+       switch (ofdm->guard_interval) {
+               case GUARD_INTERVAL_1_32: guard = DIB3000_GUARD_TIME_1_32; break;
+               case GUARD_INTERVAL_1_16: guard = DIB3000_GUARD_TIME_1_16; break;
+               case GUARD_INTERVAL_1_8: guard = DIB3000_GUARD_TIME_1_8; break;
+               case GUARD_INTERVAL_1_4: guard = DIB3000_GUARD_TIME_1_4; break;
+               case GUARD_INTERVAL_AUTO: break;
+               default: return -EINVAL;
+       }
+       switch (ofdm->constellation) {
+               case QPSK: qam = DIB3000_CONSTELLATION_QPSK; break;
+               case QAM_16: qam = DIB3000_CONSTELLATION_16QAM; break;
+               case QAM_64: qam = DIB3000_CONSTELLATION_64QAM; break;
+               case QAM_AUTO: break;
+               default: return -EINVAL;
+       }
+       switch (ofdm->hierarchy_information) {
+               case HIERARCHY_NONE: /* fall through */
+               case HIERARCHY_1: alpha = DIB3000_ALPHA_1; break;
+               case HIERARCHY_2: alpha = DIB3000_ALPHA_2; break;
+               case HIERARCHY_4: alpha = DIB3000_ALPHA_4; break;
+               case HIERARCHY_AUTO: break;
+               default: return -EINVAL;
+       }
+       if (ofdm->hierarchy_information == HIERARCHY_NONE) {
+               hrch = DIB3000_HRCH_OFF;
+               sel_hp = DIB3000_SELECT_HP;
+               fe_cr = ofdm->code_rate_HP;
+       } else if (ofdm->hierarchy_information != HIERARCHY_AUTO) {
+               hrch = DIB3000_HRCH_ON;
+               sel_hp = DIB3000_SELECT_LP;
+               fe_cr = ofdm->code_rate_LP;
+       }
+       switch (fe_cr) {
+               case FEC_1_2: cr = DIB3000_FEC_1_2; break;
+               case FEC_2_3: cr = DIB3000_FEC_2_3; break;
+               case FEC_3_4: cr = DIB3000_FEC_3_4; break;
+               case FEC_5_6: cr = DIB3000_FEC_5_6; break;
+               case FEC_7_8: cr = DIB3000_FEC_7_8; break;
+               case FEC_NONE: break;
+               case FEC_AUTO: break;
+               default: return -EINVAL;
+       }
+
+       wr(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_PARM(alpha,qam,guard,fft));
+       wr(DIB3000MC_REG_HRCH_PARM,DIB3000MC_HRCH_PARM(sel_hp,cr,hrch));
+
+       switch (fep->inversion) {
+               case INVERSION_OFF:
+                       wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF);
+                       break;
+               case INVERSION_AUTO:
+                       break;
+               case INVERSION_ON:
+                       wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_ON);
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       seq = dib3000_seq
+               [ofdm->transmission_mode == TRANSMISSION_MODE_AUTO]
+               [ofdm->guard_interval == GUARD_INTERVAL_AUTO]
+               [fep->inversion == INVERSION_AUTO];
+
+       deb_setf("seq? %d\n", seq);
+       wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS(seq,1));
+
+       dib3000mc_set_impulse_noise(state,0,ofdm->constellation,ofdm->bandwidth);
+
+       val = rd(DIB3000MC_REG_DEMOD_PARM);
+       wr(DIB3000MC_REG_DEMOD_PARM,val|DIB3000MC_DEMOD_RST_DEMOD_ON);
+       wr(DIB3000MC_REG_DEMOD_PARM,val);
+
+       msleep(70);
+
+       wr_foreach(dib3000mc_reg_agc_bandwidth, dib3000mc_agc_bandwidth);
+
+       /* something has to be auto searched */
+       if (ofdm->constellation == QAM_AUTO ||
+               ofdm->hierarchy_information == HIERARCHY_AUTO ||
+               ofdm->guard_interval == GUARD_INTERVAL_AUTO ||
+               ofdm->transmission_mode == TRANSMISSION_MODE_AUTO ||
+               fe_cr == FEC_AUTO ||
+               fep->inversion == INVERSION_AUTO
+               ) {
+               int as_count=0;
+
+               deb_setf("autosearch enabled.\n");
+
+               val = rd(DIB3000MC_REG_DEMOD_PARM);
+               wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON);
+               wr(DIB3000MC_REG_DEMOD_PARM,val);
+
+               while ((search_state = dib3000_search_status(
+                                       rd(DIB3000MC_REG_AS_IRQ),1)) < 0 && as_count++ < 100)
+                       msleep(10);
+
+               deb_info("search_state after autosearch %d after %d checks\n",search_state,as_count);
+
+               if (search_state == 1) {
+                       struct dvb_frontend_parameters feps;
+                       feps.u.ofdm.bandwidth = ofdm->bandwidth; /* bw is not auto searched */;
+                       if (dib3000mc_get_frontend(fe, &feps) == 0) {
+                               deb_setf("reading tuning data from frontend succeeded.\n");
+                               return dib3000mc_set_frontend(fe, &feps, 0);
+                       }
+               }
+       } else {
+               wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT|DIB3000MC_ISI_ACTIVATE);
+               wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[qam]);
+               /* set_offset_cfg */
+               wr_foreach(dib3000mc_reg_offset,
+                               dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]);
+
+//             dib3000mc_set_timing(1,ofdm->transmission_mode,ofdm->bandwidth);
+
+//             wr(DIB3000MC_REG_LOCK_MASK,DIB3000MC_ACTIVATE_LOCK_MASK); /* activates some locks if needed */
+
+/*             set_or(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_RST_AUTO_SRCH_ON);
+               set_or(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_RST_AUTO_SRCH_OFF);
+               wr(DIB3000MC_REG_RESTART_VIT,DIB3000MC_RESTART_VIT_ON);
+               wr(DIB3000MC_REG_RESTART_VIT,DIB3000MC_RESTART_VIT_OFF);*/
+       }
+
+       return 0;
+}
+
+
+static int dib3000mc_fe_init(struct dvb_frontend* fe, int mobile_mode)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+       state->timing_offset = 0;
+       state->timing_offset_comp_done = 0;
+
+       wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON);
+       wr(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_PAR_CONT_CLK);
+       wr(DIB3000MC_REG_RST_I2C_ADDR,
+               DIB3000MC_DEMOD_ADDR(state->config.demod_address) |
+               DIB3000MC_DEMOD_ADDR_ON);
+
+       wr(DIB3000MC_REG_RST_I2C_ADDR,
+               DIB3000MC_DEMOD_ADDR(state->config.demod_address));
+
+       wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_CONFIG);
+       wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF);
+
+       wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_UP);
+       wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_PUP_MOBILE);
+       wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_UP);
+       wr(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_INIT);
+
+       wr(DIB3000MC_REG_RST_UNC,DIB3000MC_RST_UNC_OFF);
+       wr(DIB3000MC_REG_UNK_19,DIB3000MC_UNK_19);
+
+       wr(33,5);
+       wr(36,81);
+       wr(DIB3000MC_REG_UNK_88,DIB3000MC_UNK_88);
+
+       wr(DIB3000MC_REG_UNK_99,DIB3000MC_UNK_99);
+       wr(DIB3000MC_REG_UNK_111,DIB3000MC_UNK_111_PH_N_MODE_0); /* phase noise algo off */
+
+       /* mobile mode - portable reception */
+       wr_foreach(dib3000mc_reg_mobile_mode,dib3000mc_mobile_mode[1]);
+
+/* TUNER_PANASONIC_ENV57H12D5: */
+       wr_foreach(dib3000mc_reg_agc_bandwidth,dib3000mc_agc_bandwidth);
+       wr_foreach(dib3000mc_reg_agc_bandwidth_general,dib3000mc_agc_bandwidth_general);
+       wr_foreach(dib3000mc_reg_agc,dib3000mc_agc_tuner[1]);
+
+       wr(DIB3000MC_REG_UNK_110,DIB3000MC_UNK_110);
+       wr(26,0x6680);
+       wr(DIB3000MC_REG_UNK_1,DIB3000MC_UNK_1);
+       wr(DIB3000MC_REG_UNK_2,DIB3000MC_UNK_2);
+       wr(DIB3000MC_REG_UNK_3,DIB3000MC_UNK_3);
+       wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS_DEFAULT);
+
+       wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general);
+       wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz);
+
+       wr(DIB3000MC_REG_UNK_4,DIB3000MC_UNK_4);
+
+       wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF);
+       wr(DIB3000MC_REG_SET_DDS_FREQ_LSB,DIB3000MC_DDS_FREQ_LSB);
+
+       dib3000mc_set_timing(state,0,TRANSMISSION_MODE_2K,BANDWIDTH_8_MHZ);
+//     wr_foreach(dib3000mc_reg_timing_freq,dib3000mc_timing_freq[3]);
+
+       wr(DIB3000MC_REG_UNK_120,DIB3000MC_UNK_120);
+       wr(DIB3000MC_REG_UNK_134,DIB3000MC_UNK_134);
+       wr(DIB3000MC_REG_FEC_CFG,DIB3000MC_FEC_CFG);
+
+       dib3000mc_set_impulse_noise(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ);
+
+/* output mode control, just the MPEG2_SLAVE */
+       set_or(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_SLAVE);
+       wr(DIB3000MC_REG_SMO_MODE,DIB3000MC_SMO_MODE_SLAVE);
+       wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_SLAVE);
+       wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_SLAVE);
+
+/* MPEG2_PARALLEL_CONTINUOUS_CLOCK
+       wr(DIB3000MC_REG_OUTMODE,
+               DIB3000MC_SET_OUTMODE(DIB3000MC_OM_PAR_CONT_CLK,
+                       rd(DIB3000MC_REG_OUTMODE)));
+
+       wr(DIB3000MC_REG_SMO_MODE,
+                       DIB3000MC_SMO_MODE_DEFAULT |
+                       DIB3000MC_SMO_MODE_188);
+
+       wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_DEFAULT);
+       wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON);
+*/
+/* diversity */
+       wr(DIB3000MC_REG_DIVERSITY1,DIB3000MC_DIVERSITY1_DEFAULT);
+       wr(DIB3000MC_REG_DIVERSITY2,DIB3000MC_DIVERSITY2_DEFAULT);
+
+       wr(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF);
+
+       set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_DIV_IN_OFF);
+
+
+/*     if (state->config->pll_init) {
+               wr(DIB3000MC_REG_TUNER,
+                       DIB3000_TUNER_WRITE_ENABLE(state->config->pll_addr));
+               state->config->pll_init(fe);
+               wr(DIB3000MC_REG_TUNER,
+                       DIB3000_TUNER_WRITE_DISABLE(state->config->pll_addr));
+       }*/
+       return 0;
+}
+
+static int dib3000mc_get_frontend(struct dvb_frontend* fe,
+                                 struct dvb_frontend_parameters *fep)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+       struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+       fe_code_rate_t *cr;
+       u16 tps_val,cr_val;
+       int inv_test1,inv_test2;
+       u32 dds_val, threshold = 0x1000000;
+
+       if (!(rd(DIB3000MC_REG_LOCK_507) & DIB3000MC_LOCK_507))
+               return 0;
+
+       dds_val = ((rd(DIB3000MC_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MC_REG_DDS_FREQ_LSB);
+       if (dds_val < threshold)
+               inv_test1 = 0;
+       else if (dds_val == threshold)
+               inv_test1 = 1;
+       else
+               inv_test1 = 2;
+
+       dds_val = ((rd(DIB3000MC_REG_SET_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MC_REG_SET_DDS_FREQ_LSB);
+       if (dds_val < threshold)
+               inv_test2 = 0;
+       else if (dds_val == threshold)
+               inv_test2 = 1;
+       else
+               inv_test2 = 2;
+
+       fep->inversion =
+               ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) ||
+               ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ?
+               INVERSION_ON : INVERSION_OFF;
+
+       deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion);
+
+       tps_val = rd(DIB3000MC_REG_TUNING_PARM);
+
+       switch (DIB3000MC_TP_QAM(tps_val)) {
+               case DIB3000_CONSTELLATION_QPSK:
+                       deb_getf("QPSK ");
+                       ofdm->constellation = QPSK;
+                       break;
+               case DIB3000_CONSTELLATION_16QAM:
+                       deb_getf("QAM16 ");
+                       ofdm->constellation = QAM_16;
+                       break;
+               case DIB3000_CONSTELLATION_64QAM:
+                       deb_getf("QAM64 ");
+                       ofdm->constellation = QAM_64;
+                       break;
+               default:
+                       err("Unexpected constellation returned by TPS (%d)", tps_val);
+                       break;
+       }
+
+       if (DIB3000MC_TP_HRCH(tps_val)) {
+               deb_getf("HRCH ON ");
+               cr = &ofdm->code_rate_LP;
+               ofdm->code_rate_HP = FEC_NONE;
+               switch (DIB3000MC_TP_ALPHA(tps_val)) {
+                       case DIB3000_ALPHA_0:
+                               deb_getf("HIERARCHY_NONE ");
+                               ofdm->hierarchy_information = HIERARCHY_NONE;
+                               break;
+                       case DIB3000_ALPHA_1:
+                               deb_getf("HIERARCHY_1 ");
+                               ofdm->hierarchy_information = HIERARCHY_1;
+                               break;
+                       case DIB3000_ALPHA_2:
+                               deb_getf("HIERARCHY_2 ");
+                               ofdm->hierarchy_information = HIERARCHY_2;
+                               break;
+                       case DIB3000_ALPHA_4:
+                               deb_getf("HIERARCHY_4 ");
+                               ofdm->hierarchy_information = HIERARCHY_4;
+                               break;
+                       default:
+                               err("Unexpected ALPHA value returned by TPS (%d)", tps_val);
+                               break;
+               }
+               cr_val = DIB3000MC_TP_FEC_CR_LP(tps_val);
+       } else {
+               deb_getf("HRCH OFF ");
+               cr = &ofdm->code_rate_HP;
+               ofdm->code_rate_LP = FEC_NONE;
+               ofdm->hierarchy_information = HIERARCHY_NONE;
+               cr_val = DIB3000MC_TP_FEC_CR_HP(tps_val);
+       }
+
+       switch (cr_val) {
+               case DIB3000_FEC_1_2:
+                       deb_getf("FEC_1_2 ");
+                       *cr = FEC_1_2;
+                       break;
+               case DIB3000_FEC_2_3:
+                       deb_getf("FEC_2_3 ");
+                       *cr = FEC_2_3;
+                       break;
+               case DIB3000_FEC_3_4:
+                       deb_getf("FEC_3_4 ");
+                       *cr = FEC_3_4;
+                       break;
+               case DIB3000_FEC_5_6:
+                       deb_getf("FEC_5_6 ");
+                       *cr = FEC_4_5;
+                       break;
+               case DIB3000_FEC_7_8:
+                       deb_getf("FEC_7_8 ");
+                       *cr = FEC_7_8;
+                       break;
+               default:
+                       err("Unexpected FEC returned by TPS (%d)", tps_val);
+                       break;
+       }
+
+       switch (DIB3000MC_TP_GUARD(tps_val)) {
+               case DIB3000_GUARD_TIME_1_32:
+                       deb_getf("GUARD_INTERVAL_1_32 ");
+                       ofdm->guard_interval = GUARD_INTERVAL_1_32;
+                       break;
+               case DIB3000_GUARD_TIME_1_16:
+                       deb_getf("GUARD_INTERVAL_1_16 ");
+                       ofdm->guard_interval = GUARD_INTERVAL_1_16;
+                       break;
+               case DIB3000_GUARD_TIME_1_8:
+                       deb_getf("GUARD_INTERVAL_1_8 ");
+                       ofdm->guard_interval = GUARD_INTERVAL_1_8;
+                       break;
+               case DIB3000_GUARD_TIME_1_4:
+                       deb_getf("GUARD_INTERVAL_1_4 ");
+                       ofdm->guard_interval = GUARD_INTERVAL_1_4;
+                       break;
+               default:
+                       err("Unexpected Guard Time returned by TPS (%d)", tps_val);
+                       break;
+       }
+
+       switch (DIB3000MC_TP_FFT(tps_val)) {
+               case DIB3000_TRANSMISSION_MODE_2K:
+                       deb_getf("TRANSMISSION_MODE_2K ");
+                       ofdm->transmission_mode = TRANSMISSION_MODE_2K;
+                       break;
+               case DIB3000_TRANSMISSION_MODE_8K:
+                       deb_getf("TRANSMISSION_MODE_8K ");
+                       ofdm->transmission_mode = TRANSMISSION_MODE_8K;
+                       break;
+               default:
+                       err("unexpected transmission mode return by TPS (%d)", tps_val);
+                       break;
+       }
+       return 0;
+}
+
+static int dib3000mc_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+       u16 lock = rd(DIB3000MC_REG_LOCKING);
+
+       *stat = 0;
+       if (DIB3000MC_AGC_LOCK(lock))
+               *stat |= FE_HAS_SIGNAL;
+       if (DIB3000MC_CARRIER_LOCK(lock))
+               *stat |= FE_HAS_CARRIER;
+       if (DIB3000MC_TPS_LOCK(lock)) /* VIT_LOCK ? */
+               *stat |= FE_HAS_VITERBI;
+       if (DIB3000MC_MPEG_SYNC_LOCK(lock))
+               *stat |= (FE_HAS_SYNC | FE_HAS_LOCK);
+
+       deb_info("actual status is %2x\n",*stat);
+
+       return 0;
+}
+
+static int dib3000mc_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+       *ber = ((rd(DIB3000MC_REG_BER_MSB) << 16) | rd(DIB3000MC_REG_BER_LSB));
+       return 0;
+}
+
+static int dib3000mc_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+       *unc = rd(DIB3000MC_REG_PACKET_ERROR_COUNT);
+       return 0;
+}
+
+/* see dib3000mb.c for calculation comments */
+static int dib3000mc_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+       u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB);
+       *strength = (((val >> 6) & 0xff) << 8) + (val & 0x3f);
+
+       deb_info("signal: mantisse = %d, exponent = %d\n",(*strength >> 8) & 0xff, *strength & 0xff);
+       return 0;
+}
+
+/* see dib3000mb.c for calculation comments */
+static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+       u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_MSB),
+               val2 = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB);
+       u16 sig,noise;
+
+       sig =   (((val >> 6) & 0xff) << 8) + (val & 0x3f);
+       noise = (((val >> 4) & 0xff) << 8) + ((val & 0xf) << 2) + ((val2 >> 14) & 0x3);
+       if (noise == 0)
+               *snr = 0xffff;
+       else
+               *snr = (u16) sig/noise;
+
+       deb_info("signal: mantisse = %d, exponent = %d\n",(sig >> 8) & 0xff, sig & 0xff);
+       deb_info("noise:  mantisse = %d, exponent = %d\n",(noise >> 8) & 0xff, noise & 0xff);
+       deb_info("snr: %d\n",*snr);
+       return 0;
+}
+
+static int dib3000mc_sleep(struct dvb_frontend* fe)
+{
+       struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+       set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_PWR_DOWN);
+       wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_DOWN);
+       wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_POWER_DOWN);
+       wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_DOWN);
+       return 0;
+}
+
+static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+       tune->min_delay_ms = 800;
+       tune->step_size = 166667;
+       tune->max_drift = 166667 * 2;
+
+       return 0;
+}
+
+static int dib3000mc_fe_init_nonmobile(struct dvb_frontend* fe)
+{
+       return dib3000mc_fe_init(fe, 0);
+}
+
+static int dib3000mc_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep)
+{
+       return dib3000mc_set_frontend(fe, fep, 1);
+}
+
+static void dib3000mc_release(struct dvb_frontend* fe)
+{
+       struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+       dib3000_dealloc_pid_list(state);
+       kfree(state);
+}
+
+/* pid filter and transfer stuff */
+static int dib3000mc_pid_control(struct dvb_frontend *fe,int pid,int onoff)
+{
+       struct dib3000_state *state = fe->demodulator_priv;
+       int index = dib3000_get_pid_index(state->pid_list, DIB3000MC_NUM_PIDS, pid, &state->pid_list_lock,onoff);
+       pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0);
+
+       if (index >= 0) {
+               wr(index+DIB3000MC_REG_FIRST_PID,pid);
+       } else {
+               err("no more pids for filtering.");
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static int dib3000mc_fifo_control(struct dvb_frontend *fe, int onoff)
+{
+       struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+       u16 tmp = rd(DIB3000MC_REG_SMO_MODE);
+       deb_xfer("%s fifo",onoff ? "enabling" : "disabling");
+       if (onoff) {
+               wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH);
+       } else {
+               wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH);
+       }
+       return 0;
+}
+
+static int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
+{
+       struct dib3000_state *state = fe->demodulator_priv;
+       u16 tmp = rd(DIB3000MC_REG_SMO_MODE);
+       deb_xfer("%s pid parsing",onoff ? "enabling" : "disabling");
+       if (onoff) {
+               wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_PID_PARSE);
+       } else {
+               wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_NO_PID_PARSE);
+       }
+       return 0;
+}
+
+static struct dvb_frontend_ops dib3000mc_ops;
+
+struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config,
+                                     struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops)
+{
+       struct dib3000_state* state = NULL;
+       u16 devid;
+
+       /* allocate memory for the internal state */
+       state = (struct dib3000_state*) kmalloc(sizeof(struct dib3000_state), GFP_KERNEL);
+       if (state == NULL)
+               goto error;
+
+       /* setup the state */
+       state->i2c = i2c;
+       memcpy(&state->config,config,sizeof(struct dib3000_config));
+       memcpy(&state->ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
+
+       /* check for the correct demod */
+       if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM)
+               goto error;
+
+       devid = rd(DIB3000_REG_DEVICE_ID);
+       if (devid != DIB3000MC_DEVICE_ID && devid != DIB3000P_DEVICE_ID)
+               goto error;
+
+
+       switch (devid) {
+               case DIB3000MC_DEVICE_ID:
+                       info("Found a DiBcom 3000-MC.");
+                       break;
+               case DIB3000P_DEVICE_ID:
+                       info("Found a DiBcom 3000-P.");
+                       break;
+       }
+
+       if (dib3000_init_pid_list(state,DIB3000MC_NUM_PIDS))
+               goto error;
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+
+       /* set the xfer operations */
+       xfer_ops->pid_parse = dib3000mc_pid_parse;
+       xfer_ops->fifo_ctrl = dib3000mc_fifo_control;
+       xfer_ops->pid_ctrl = dib3000mc_pid_control;
+
+       return &state->frontend;
+
+error:
+       if (state)
+               kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops dib3000mc_ops = {
+
+       .info = {
+               .name                   = "DiBcom 3000-MC/P DVB-T",
+               .type                   = FE_OFDM,
+               .frequency_min          = 44250000,
+               .frequency_max          = 867250000,
+               .frequency_stepsize     = 62500,
+               .caps = FE_CAN_INVERSION_AUTO |
+                               FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                               FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                               FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+                               FE_CAN_TRANSMISSION_MODE_AUTO |
+                               FE_CAN_GUARD_INTERVAL_AUTO |
+                               FE_CAN_HIERARCHY_AUTO,
+       },
+
+       .release = dib3000mc_release,
+
+       .init = dib3000mc_fe_init_nonmobile,
+       .sleep = dib3000mc_sleep,
+
+       .set_frontend = dib3000mc_set_frontend_and_tuner,
+       .get_frontend = dib3000mc_get_frontend,
+       .get_tune_settings = dib3000mc_fe_get_tune_settings,
+
+       .read_status = dib3000mc_read_status,
+       .read_ber = dib3000mc_read_ber,
+       .read_signal_strength = dib3000mc_read_signal_strength,
+       .read_snr = dib3000mc_read_snr,
+       .read_ucblocks = dib3000mc_read_unc_blocks,
+};
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dib3000mc_attach);
diff --git a/drivers/media/dvb/frontends/dib3000mc_priv.h b/drivers/media/dvb/frontends/dib3000mc_priv.h
new file mode 100644 (file)
index 0000000..21ccc13
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * dib3000mc_priv.h
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ *     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, version 2.
+ *
+ * for more information see dib3000mc.c .
+ */
+
+#ifndef __DIB3000MC_PRIV_H__
+#define __DIB3000MC_PRIV_H__
+
+/* info and err, taken from usb.h, if there is anything available like by default,
+ * please change !
+ */
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , __FILE__ , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n" , __FILE__ , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n" , __FILE__ , ## arg)
+
+// defines the phase noise algorithm to be used (O:Inhib, 1:CPE on)
+#define DEF_PHASE_NOISE_MODE                0
+
+// define Mobille algorithms
+#define DEF_MOBILE_MODE      Auto_Reception
+
+// defines the tuner type
+#define DEF_TUNER_TYPE   TUNER_PANASONIC_ENV57H13D5
+
+// defines the impule noise algorithm to be used
+#define DEF_IMPULSE_NOISE_MODE      0
+
+// defines the MPEG2 data output format
+#define DEF_MPEG2_OUTPUT_188       0
+
+// defines the MPEG2 data output format
+#define DEF_OUTPUT_MODE       MPEG2_PARALLEL_CONTINUOUS_CLOCK
+
+/*
+ * Demodulator parameters
+ * reg: 0  1 1  1 11 11 111
+ *         | |  |  |  |  |
+ *         | |  |  |  |  +-- alpha (000=0, 001=1, 010=2, 100=4)
+ *         | |  |  |  +----- constellation (00=QPSK, 01=16QAM, 10=64QAM)
+ *         | |  |  +-------- guard (00=1/32, 01=1/16, 10=1/8, 11=1/4)
+ *         | |  +----------- transmission mode (0=2k, 1=8k)
+ *         | |
+ *         | +-------------- restart autosearch for parameters
+ *         +---------------- restart the demodulator
+ * reg: 181      1 111 1
+ *               |  |  |
+ *               |  |  +- FEC applies for HP or LP (0=LP, 1=HP)
+ *               |  +---- FEC rate (001=1/2, 010=2/3, 011=3/4, 101=5/6, 111=7/8)
+ *               +------- hierarchy on (0=no, 1=yes)
+ */
+
+/* demodulator tuning parameter and restart options */
+#define DIB3000MC_REG_DEMOD_PARM               (     0)
+#define DIB3000MC_DEMOD_PARM(a,c,g,t)  ( \
+                (0x7 & a) | \
+               ((0x3 & c) << 3) | \
+               ((0x3 & g) << 5) | \
+               ((0x1 & t) << 7) )
+#define DIB3000MC_DEMOD_RST_AUTO_SRCH_ON       (1 << 8)
+#define DIB3000MC_DEMOD_RST_AUTO_SRCH_OFF      (0 << 8)
+#define DIB3000MC_DEMOD_RST_DEMOD_ON           (1 << 9)
+#define DIB3000MC_DEMOD_RST_DEMOD_OFF          (0 << 9)
+
+/* register for hierarchy parameters */
+#define DIB3000MC_REG_HRCH_PARM                        (   181)
+#define DIB3000MC_HRCH_PARM(s,f,h)             ( \
+                (0x1 & s) | \
+               ((0x7 & f) << 1) | \
+               ((0x1 & h) << 4) )
+
+/* timeout ??? */
+#define DIB3000MC_REG_UNK_1                            (     1)
+#define DIB3000MC_UNK_1                                        (  0x04)
+
+/* timeout ??? */
+#define DIB3000MC_REG_UNK_2                            (     2)
+#define DIB3000MC_UNK_2                                        (  0x04)
+
+/* timeout ??? */
+#define DIB3000MC_REG_UNK_3                            (     3)
+#define DIB3000MC_UNK_3                                        (0x1000)
+
+#define DIB3000MC_REG_UNK_4                            (     4)
+#define DIB3000MC_UNK_4                                        (0x0814)
+
+/* timeout ??? */
+#define DIB3000MC_REG_SEQ_TPS                  (     5)
+#define DIB3000MC_SEQ_TPS_DEFAULT              (     1)
+#define DIB3000MC_SEQ_TPS(s,t)                 ( \
+               ((s & 0x0f) << 4) | \
+               ((t & 0x01) << 8) )
+#define DIB3000MC_IS_TPS(v)                            ((v << 8) & 0x1)
+#define DIB3000MC_IS_AS(v)                             ((v >> 4) & 0xf)
+
+/* parameters for the bandwidth */
+#define DIB3000MC_REG_BW_TIMOUT_MSB            (     6)
+#define DIB3000MC_REG_BW_TIMOUT_LSB            (     7)
+
+static u16 dib3000mc_reg_bandwidth[] = { 6,7,8,9,10,11,16,17 };
+
+/*static u16 dib3000mc_bandwidth_5mhz[] =
+       { 0x28, 0x9380, 0x87, 0x4100, 0x2a4, 0x4500, 0x1, 0xb0d0 };*/
+
+static u16 dib3000mc_bandwidth_6mhz[] =
+       { 0x21, 0xd040, 0x70, 0xb62b, 0x233, 0x8ed5, 0x1, 0xb0d0 };
+
+static u16 dib3000mc_bandwidth_7mhz[] =
+       { 0x1c, 0xfba5, 0x60, 0x9c25, 0x1e3, 0x0cb7, 0x1, 0xb0d0 };
+
+static u16 dib3000mc_bandwidth_8mhz[] =
+       { 0x19, 0x5c30, 0x54, 0x88a0, 0x1a6, 0xab20, 0x1, 0xb0b0 };
+
+static u16 dib3000mc_reg_bandwidth_general[] = { 12,13,14,15 };
+static u16 dib3000mc_bandwidth_general[] = { 0x0000, 0x03e8, 0x0000, 0x03f2 };
+
+/* lock mask */
+#define DIB3000MC_REG_LOCK_MASK                        (    15)
+#define DIB3000MC_ACTIVATE_LOCK_MASK   (0x0800)
+
+/* reset the uncorrected packet count (??? do it 5 times) */
+#define DIB3000MC_REG_RST_UNC                  (    18)
+#define DIB3000MC_RST_UNC_ON                   (     1)
+#define DIB3000MC_RST_UNC_OFF                  (     0)
+
+#define DIB3000MC_REG_UNK_19                   (    19)
+#define DIB3000MC_UNK_19                               (     0)
+
+/* DDS frequency value (IF position) and inversion bit */
+#define DIB3000MC_REG_INVERSION                        (    21)
+#define DIB3000MC_REG_SET_DDS_FREQ_MSB (    21)
+#define DIB3000MC_DDS_FREQ_MSB_INV_OFF (0x0164)
+#define DIB3000MC_DDS_FREQ_MSB_INV_ON  (0x0364)
+
+#define DIB3000MC_REG_SET_DDS_FREQ_LSB (    22)
+#define DIB3000MC_DDS_FREQ_LSB                 (0x463d)
+
+/* timing frequencies setting */
+#define DIB3000MC_REG_TIMING_FREQ_MSB  (    23)
+#define DIB3000MC_REG_TIMING_FREQ_LSB  (    24)
+#define DIB3000MC_CLOCK_REF                            (0x151fd1)
+
+//static u16 dib3000mc_reg_timing_freq[] = { 23,24 };
+
+//static u16 dib3000mc_timing_freq[][2] = {
+//     { 0x69, 0x9f18 }, /* 5 MHz */
+//     { 0x7e ,0xbee9 }, /* 6 MHz */
+//     { 0x93 ,0xdebb }, /* 7 MHz */
+//     { 0xa8 ,0xfe8c }, /* 8 MHz */
+//};
+
+/* timeout ??? */
+static u16 dib3000mc_reg_offset[] = { 26,33 };
+
+static u16 dib3000mc_offset[][2] = {
+       { 26240, 5 }, /* default */
+       { 30336, 6 }, /* 8K */
+       { 38528, 8 }, /* 2K */
+};
+
+#define DIB3000MC_REG_ISI                              (    29)
+#define DIB3000MC_ISI_DEFAULT                  (0x1073)
+#define DIB3000MC_ISI_ACTIVATE                 (0x0000)
+#define DIB3000MC_ISI_INHIBIT                  (0x0200)
+
+/* impulse noise control */
+static u16 dib3000mc_reg_imp_noise_ctl[] = { 34,35 };
+
+static u16 dib3000mc_imp_noise_ctl[][2] = {
+       { 0x1294, 0xfff8 }, /* mode 0 */
+       { 0x1294, 0xfff8 }, /* mode 1 */
+       { 0x1294, 0xfff8 }, /* mode 2 */
+       { 0x1294, 0xfff8 }, /* mode 3 */
+       { 0x1294, 0xfff8 }, /* mode 4 */
+};
+
+/* AGC registers */
+static u16 dib3000mc_reg_agc[] = {
+       36,37,38,39,42,43,44,45,46,47,48,49
+};
+
+static u16 dib3000mc_agc_tuner[][12] = {
+       {       0x0051, 0x301d, 0x0000, 0x1cc7, 0xcf5c, 0x6666,
+               0xbae1, 0xa148, 0x3b5e, 0x3c1c, 0x001a, 0x2019
+       }, /* TUNER_PANASONIC_ENV77H04D5, */
+
+       {       0x0051, 0x301d, 0x0000, 0x1cc7, 0xdc29, 0x570a,
+               0xbae1, 0x8ccd, 0x3b6d, 0x551d, 0x000a, 0x951e
+       }, /* TUNER_PANASONIC_ENV57H13D5, TUNER_PANASONIC_ENV57H12D5 */
+
+       {       0x0051, 0x301d, 0x0000, 0x1cc7, 0xffff, 0xffff,
+               0xffff, 0x0000, 0xfdfd, 0x4040, 0x00fd, 0x4040
+       }, /* TUNER_SAMSUNG_DTOS333IH102, TUNER_RFAGCIN_UNKNOWN */
+
+       {       0x0196, 0x301d, 0x0000, 0x1cc7, 0xbd71, 0x5c29,
+               0xb5c3, 0x6148, 0x6569, 0x5127, 0x0033, 0x3537
+       }, /* TUNER_PROVIDER_X */
+       /* TODO TUNER_PANASONIC_ENV57H10D8, TUNER_PANASONIC_ENV57H11D8 */
+};
+
+/* AGC loop bandwidth */
+static u16 dib3000mc_reg_agc_bandwidth[] = { 40,41 };
+static u16 dib3000mc_agc_bandwidth[]  = { 0x119,0x330 };
+
+static u16 dib3000mc_reg_agc_bandwidth_general[] = { 50,51,52,53,54 };
+static u16 dib3000mc_agc_bandwidth_general[] =
+       { 0x8000, 0x91ca, 0x01ba, 0x0087, 0x0087 };
+
+#define DIB3000MC_REG_IMP_NOISE_55             (    55)
+#define DIB3000MC_IMP_NEW_ALGO(w)              (w | (1<<10))
+
+/* Impulse noise params */
+static u16 dib3000mc_reg_impulse_noise[] = { 55,56,57 };
+static u16 dib3000mc_impluse_noise[][3] = {
+       { 0x489, 0x89, 0x72 }, /* 5 MHz */
+       { 0x4a5, 0xa5, 0x89 }, /* 6 MHz */
+       { 0x4c0, 0xc0, 0xa0 }, /* 7 MHz */
+       { 0x4db, 0xdb, 0xb7 }, /* 8 Mhz */
+};
+
+static u16 dib3000mc_reg_fft[] = {
+       58,59,60,61,62,63,64,65,66,67,68,69,
+       70,71,72,73,74,75,76,77,78,79,80,81,
+       82,83,84,85,86
+};
+
+static u16 dib3000mc_fft_modes[][29] = {
+       {       0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c,
+               0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d,
+               0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3,
+               0x3feb, 0x7d2, 0x365e, 0x76, 0x48c,
+               0x3ffe, 0x5b3, 0x3feb, 0x76,   0x0, 0xd
+       }, /* fft mode 0 */
+       {       0x3b, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c,
+               0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d,
+               0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3,
+               0x3feb, 0x7d2, 0x365e, 0x76, 0x48c,
+               0x3ffe, 0x5b3, 0x3feb, 0x0,  0x8200, 0xd
+       }, /* fft mode 1 */
+};
+
+#define DIB3000MC_REG_UNK_88                   (    88)
+#define DIB3000MC_UNK_88                               (0x0410)
+
+static u16 dib3000mc_reg_bw[] = { 93,94,95,96,97,98 };
+static u16 dib3000mc_bw[][6] = {
+       { 0,0,0,0,0,0 }, /* 5 MHz */
+       { 0,0,0,0,0,0 }, /* 6 MHz */
+       { 0,0,0,0,0,0 }, /* 7 MHz */
+       { 0x20, 0x21, 0x20, 0x23, 0x20, 0x27 }, /* 8 MHz */
+};
+
+
+/* phase noise control */
+#define DIB3000MC_REG_UNK_99                   (    99)
+#define DIB3000MC_UNK_99                               (0x0220)
+
+#define DIB3000MC_REG_SCAN_BOOST               (   100)
+#define DIB3000MC_SCAN_BOOST_ON                        ((11 << 6) + 6)
+#define DIB3000MC_SCAN_BOOST_OFF               ((16 << 6) + 9)
+
+/* timeout ??? */
+#define DIB3000MC_REG_UNK_110                  (   110)
+#define DIB3000MC_UNK_110                              (  3277)
+
+#define DIB3000MC_REG_UNK_111                  (   111)
+#define DIB3000MC_UNK_111_PH_N_MODE_0  (     0)
+#define DIB3000MC_UNK_111_PH_N_MODE_1  (1 << 1)
+
+/* superious rm config */
+#define DIB3000MC_REG_UNK_120                  (   120)
+#define DIB3000MC_UNK_120                              (  8207)
+
+#define DIB3000MC_REG_UNK_133                  (   133)
+#define DIB3000MC_UNK_133                              ( 15564)
+
+#define DIB3000MC_REG_UNK_134                  (   134)
+#define DIB3000MC_UNK_134                              (     0)
+
+/* adapter config for constellation */
+static u16 dib3000mc_reg_adp_cfg[] = { 129, 130, 131, 132 };
+
+static u16 dib3000mc_adp_cfg[][4] = {
+       { 0x99a, 0x7fae, 0x333, 0x7ff0 }, /* QPSK  */
+       { 0x23d, 0x7fdf, 0x0a4, 0x7ff0 }, /* 16-QAM */
+       { 0x148, 0x7ff0, 0x0a4, 0x7ff8 }, /* 64-QAM */
+};
+
+static u16 dib3000mc_reg_mobile_mode[] = { 139, 140, 141, 175, 1032 };
+
+static u16 dib3000mc_mobile_mode[][5] = {
+       { 0x01, 0x0, 0x0, 0x00, 0x12c }, /* fixed */
+       { 0x01, 0x0, 0x0, 0x00, 0x12c }, /* portable */
+       { 0x00, 0x0, 0x0, 0x02, 0x000 }, /* mobile */
+       { 0x00, 0x0, 0x0, 0x02, 0x000 }, /* auto */
+};
+
+#define DIB3000MC_REG_DIVERSITY1               (   177)
+#define DIB3000MC_DIVERSITY1_DEFAULT   (     1)
+
+#define DIB3000MC_REG_DIVERSITY2               (   178)
+#define DIB3000MC_DIVERSITY2_DEFAULT   (     1)
+
+#define DIB3000MC_REG_DIVERSITY3               (   180)
+#define DIB3000MC_DIVERSITY3_IN_OFF            (0xfff0)
+#define DIB3000MC_DIVERSITY3_IN_ON             (0xfff6)
+
+#define DIB3000MC_REG_FEC_CFG                  (   195)
+#define DIB3000MC_FEC_CFG                              (  0x10)
+
+#define DIB3000MC_REG_SMO_MODE                 (   206)
+#define DIB3000MC_SMO_MODE_DEFAULT             (1 << 2)
+#define DIB3000MC_SMO_MODE_FIFO_FLUSH  (1 << 3)
+#define DIB3000MC_SMO_MODE_FIFO_UNFLUSH        ~DIB3000MC_SMO_MODE_FIFO_FLUSH
+#define DIB3000MC_SMO_MODE_PID_PARSE   (1 << 4)
+#define DIB3000MC_SMO_MODE_NO_PID_PARSE        ~DIB3000MC_SMO_MODE_PID_PARSE
+#define DIB3000MC_SMO_MODE_188                 (1 << 5)
+#define DIB3000MC_SMO_MODE_SLAVE               (DIB3000MC_SMO_MODE_DEFAULT | \
+                       DIB3000MC_SMO_MODE_188 | DIB3000MC_SMO_MODE_PID_PARSE | (1<<1))
+
+#define DIB3000MC_REG_FIFO_THRESHOLD   (   207)
+#define DIB3000MC_FIFO_THRESHOLD_DEFAULT       (  1792)
+#define DIB3000MC_FIFO_THRESHOLD_SLAVE (   512)
+/*
+ * pidfilter
+ * it is not a hardware pidfilter but a filter which drops all pids
+ * except the ones set. When connected to USB1.1 bandwidth this is important.
+ * DiB3000-MC/P can filter up to 32 PIDs
+ */
+#define DIB3000MC_REG_FIRST_PID                        (   212)
+#define DIB3000MC_NUM_PIDS                             (    32)
+
+#define DIB3000MC_REG_OUTMODE                  (   244)
+#define DIB3000MC_OM_PARALLEL_GATED_CLK        (     0)
+#define DIB3000MC_OM_PAR_CONT_CLK              (1 << 11)
+#define DIB3000MC_OM_SERIAL                            (2 << 11)
+#define DIB3000MC_OM_DIVOUT_ON                 (4 << 11)
+#define DIB3000MC_OM_SLAVE                             (DIB3000MC_OM_DIVOUT_ON | DIB3000MC_OM_PAR_CONT_CLK)
+
+#define DIB3000MC_REG_RF_POWER                 (   392)
+
+#define DIB3000MC_REG_FFT_POSITION             (   407)
+
+#define DIB3000MC_REG_DDS_FREQ_MSB             (   414)
+#define DIB3000MC_REG_DDS_FREQ_LSB             (   415)
+
+#define DIB3000MC_REG_TIMING_OFFS_MSB  (   416)
+#define DIB3000MC_REG_TIMING_OFFS_LSB  (   417)
+
+#define DIB3000MC_REG_TUNING_PARM              (   458)
+#define DIB3000MC_TP_QAM(v)                            ((v >> 13) & 0x03)
+#define DIB3000MC_TP_HRCH(v)                   ((v >> 12) & 0x01)
+#define DIB3000MC_TP_ALPHA(v)                  ((v >> 9) & 0x07)
+#define DIB3000MC_TP_FFT(v)                            ((v >> 8) & 0x01)
+#define DIB3000MC_TP_FEC_CR_HP(v)              ((v >> 5) & 0x07)
+#define DIB3000MC_TP_FEC_CR_LP(v)              ((v >> 2) & 0x07)
+#define DIB3000MC_TP_GUARD(v)                  (v & 0x03)
+
+#define DIB3000MC_REG_SIGNAL_NOISE_MSB (   483)
+#define DIB3000MC_REG_SIGNAL_NOISE_LSB (   484)
+
+#define DIB3000MC_REG_MER                              (   485)
+
+#define DIB3000MC_REG_BER_MSB                  (   500)
+#define DIB3000MC_REG_BER_LSB                  (   501)
+
+#define DIB3000MC_REG_PACKET_ERRORS            (   503)
+
+#define DIB3000MC_REG_PACKET_ERROR_COUNT       (   506)
+
+#define DIB3000MC_REG_LOCK_507                 (   507)
+#define DIB3000MC_LOCK_507                             (0x0002) // ? name correct ?
+
+#define DIB3000MC_REG_LOCKING                  (   509)
+#define DIB3000MC_AGC_LOCK(v)                  (v & 0x8000)
+#define DIB3000MC_CARRIER_LOCK(v)              (v & 0x2000)
+#define DIB3000MC_MPEG_SYNC_LOCK(v)            (v & 0x0080)
+#define DIB3000MC_MPEG_DATA_LOCK(v)            (v & 0x0040)
+#define DIB3000MC_TPS_LOCK(v)                  (v & 0x0004)
+
+#define DIB3000MC_REG_AS_IRQ                   (   511)
+#define DIB3000MC_AS_IRQ_SUCCESS               (1 << 1)
+#define DIB3000MC_AS_IRQ_FAIL                  (     1)
+
+#define DIB3000MC_REG_TUNER                            (   769)
+
+#define DIB3000MC_REG_RST_I2C_ADDR             (  1024)
+#define DIB3000MC_DEMOD_ADDR_ON                        (     1)
+#define DIB3000MC_DEMOD_ADDR(a)                        ((a << 3) & 0x03F0)
+
+#define DIB3000MC_REG_RESTART                  (  1027)
+#define DIB3000MC_RESTART_OFF                  (0x0000)
+#define DIB3000MC_RESTART_AGC                  (0x0800)
+#define DIB3000MC_RESTART_CONFIG               (0x8000)
+
+#define DIB3000MC_REG_RESTART_VIT              (  1028)
+#define DIB3000MC_RESTART_VIT_OFF              (     0)
+#define DIB3000MC_RESTART_VIT_ON               (     1)
+
+#define DIB3000MC_REG_CLK_CFG_1                        (  1031)
+#define DIB3000MC_CLK_CFG_1_POWER_UP   (     0)
+#define DIB3000MC_CLK_CFG_1_POWER_DOWN (0xffff)
+
+#define DIB3000MC_REG_CLK_CFG_2                        (  1032)
+#define DIB3000MC_CLK_CFG_2_PUP_FIXED  (0x012c)
+#define DIB3000MC_CLK_CFG_2_PUP_PORT   (0x0104)
+#define DIB3000MC_CLK_CFG_2_PUP_MOBILE  (0x0000)
+#define DIB3000MC_CLK_CFG_2_POWER_DOWN (0xffff)
+
+#define DIB3000MC_REG_CLK_CFG_3                        (  1033)
+#define DIB3000MC_CLK_CFG_3_POWER_UP   (     0)
+#define DIB3000MC_CLK_CFG_3_POWER_DOWN (0xfff5)
+
+#define DIB3000MC_REG_CLK_CFG_7                        (  1037)
+#define DIB3000MC_CLK_CFG_7_INIT               ( 12592)
+#define DIB3000MC_CLK_CFG_7_POWER_UP   (~0x0003)
+#define DIB3000MC_CLK_CFG_7_PWR_DOWN   (0x0003)
+#define DIB3000MC_CLK_CFG_7_DIV_IN_OFF (1 << 8)
+
+/* was commented out ??? */
+#define DIB3000MC_REG_CLK_CFG_8                        (  1038)
+#define DIB3000MC_CLK_CFG_8_POWER_UP   (0x160c)
+
+#define DIB3000MC_REG_CLK_CFG_9                        (  1039)
+#define DIB3000MC_CLK_CFG_9_POWER_UP   (     0)
+
+/* also clock ??? */
+#define DIB3000MC_REG_ELEC_OUT                 (  1040)
+#define DIB3000MC_ELEC_OUT_HIGH_Z              (     0)
+#define DIB3000MC_ELEC_OUT_DIV_OUT_ON  (     1)
+#define DIB3000MC_ELEC_OUT_SLAVE               (     3)
+
+#endif
diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.h b/drivers/media/dvb/frontends/dvb_dummy_fe.h
new file mode 100644 (file)
index 0000000..8210f19
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *  Driver for Dummy Frontend
+ *
+ *  Written by Emard <emard@softhome.net>
+ *
+ *  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 DVB_DUMMY_FE_H
+#define DVB_DUMMY_FE_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void);
+extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void);
+extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void);
+
+#endif // DVB_DUMMY_FE_H
diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c
new file mode 100644 (file)
index 0000000..af4cb09
--- /dev/null
@@ -0,0 +1,607 @@
+/*
+    driver for LSI L64781 COFDM demodulator
+
+    Copyright (C) 2001 Holger Waechtler <holger@convergence.de>
+                       for Convergence Integrated Media GmbH
+                       Marko Kohtala <marko.kohtala@nokia.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "l64781.h"
+
+
+struct l64781_state {
+
+       struct i2c_adapter* i2c;
+
+       struct dvb_frontend_ops ops;
+
+       const struct l64781_config* config;
+
+       struct dvb_frontend frontend;
+
+       /* private demodulator data */
+       int first:1;
+};
+
+#define dprintk(args...) \
+       do { \
+               if (debug) printk(KERN_DEBUG "l64781: " args); \
+       } while (0)
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+
+static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data)
+{
+       int ret;
+       u8 buf [] = { reg, data };
+       struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+       if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
+               dprintk ("%s: write_reg error (reg == %02x) = %02x!\n",
+                        __FUNCTION__, reg, ret);
+
+       return (ret != 1) ? -1 : 0;
+}
+
+static int l64781_readreg (struct l64781_state* state, u8 reg)
+{
+       int ret;
+       u8 b0 [] = { reg };
+       u8 b1 [] = { 0 };
+       struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+                          { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+       ret = i2c_transfer(state->i2c, msg, 2);
+
+       if (ret != 2) return ret;
+
+       return b1[0];
+}
+
+static void apply_tps (struct l64781_state* state)
+{
+       l64781_writereg (state, 0x2a, 0x00);
+       l64781_writereg (state, 0x2a, 0x01);
+
+       /* This here is a little bit questionable because it enables
+          the automatic update of TPS registers. I think we'd need to
+          handle the IRQ from FE to update some other registers as
+          well, or at least implement some magic to tuning to correct
+          to the TPS received from transmission. */
+       l64781_writereg (state, 0x2a, 0x02);
+}
+
+
+static void reset_afc (struct l64781_state* state)
+{
+       /* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for
+          timing offset */
+       l64781_writereg (state, 0x07, 0x9e); /* stall AFC */
+       l64781_writereg (state, 0x08, 0);    /* AFC INIT FREQ */
+       l64781_writereg (state, 0x09, 0);
+       l64781_writereg (state, 0x0a, 0);
+       l64781_writereg (state, 0x07, 0x8e);
+       l64781_writereg (state, 0x0e, 0);    /* AGC gain to zero in beginning */
+       l64781_writereg (state, 0x11, 0x80); /* stall TIM */
+       l64781_writereg (state, 0x10, 0);    /* TIM_OFFSET_LSB */
+       l64781_writereg (state, 0x12, 0);
+       l64781_writereg (state, 0x13, 0);
+       l64781_writereg (state, 0x11, 0x00);
+}
+
+static int reset_and_configure (struct l64781_state* state)
+{
+       u8 buf [] = { 0x06 };
+       struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 };
+       // NOTE: this is correct in writing to address 0x00
+
+       return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV;
+}
+
+static int apply_frontend_param (struct dvb_frontend* fe, struct dvb_frontend_parameters *param)
+{
+       struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+       /* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */
+       static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 };
+       /* QPSK, QAM_16, QAM_64 */
+       static const u8 qam_tab [] = { 2, 4, 0, 6 };
+       static const u8 bw_tab [] = { 8, 7, 6 };  /* 8Mhz, 7MHz, 6MHz */
+       static const u8 guard_tab [] = { 1, 2, 4, 8 };
+       /* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */
+       static const u32 ppm = 8000;
+       struct dvb_ofdm_parameters *p = &param->u.ofdm;
+       u32 ddfs_offset_fixed;
+/*     u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */
+/*                     bw_tab[p->bandWidth]<<10)/15625; */
+       u32 init_freq;
+       u32 spi_bias;
+       u8 val0x04;
+       u8 val0x05;
+       u8 val0x06;
+       int bw = p->bandwidth - BANDWIDTH_8_MHZ;
+
+       state->config->pll_set(fe, param);
+
+       if (param->inversion != INVERSION_ON &&
+           param->inversion != INVERSION_OFF)
+               return -EINVAL;
+
+       if (bw < 0 || bw > 2)
+               return -EINVAL;
+
+       if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 &&
+           p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 &&
+           p->code_rate_HP != FEC_7_8)
+               return -EINVAL;
+
+       if (p->hierarchy_information != HIERARCHY_NONE &&
+           (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 &&
+            p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 &&
+            p->code_rate_LP != FEC_7_8))
+               return -EINVAL;
+
+       if (p->constellation != QPSK && p->constellation != QAM_16 &&
+           p->constellation != QAM_64)
+               return -EINVAL;
+
+       if (p->transmission_mode != TRANSMISSION_MODE_2K &&
+           p->transmission_mode != TRANSMISSION_MODE_8K)
+               return -EINVAL;
+
+       if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+           p->guard_interval > GUARD_INTERVAL_1_4)
+               return -EINVAL;
+
+       if (p->hierarchy_information < HIERARCHY_NONE ||
+           p->hierarchy_information > HIERARCHY_4)
+               return -EINVAL;
+
+       ddfs_offset_fixed = 0x4000-(ppm<<16)/bw_tab[p->bandwidth]/1000000;
+
+       /* This works up to 20000 ppm, it overflows if too large ppm! */
+       init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) /
+                       bw_tab[p->bandwidth] & 0xFFFFFF);
+
+       /* SPI bias calculation is slightly modified to fit in 32bit */
+       /* will work for high ppm only... */
+       spi_bias = 378 * (1 << 10);
+       spi_bias *= 16;
+       spi_bias *= bw_tab[p->bandwidth];
+       spi_bias *= qam_tab[p->constellation];
+       spi_bias /= p->code_rate_HP + 1;
+       spi_bias /= (guard_tab[p->guard_interval] + 32);
+       spi_bias *= 1000ULL;
+       spi_bias /= 1000ULL + ppm/1000;
+       spi_bias *= p->code_rate_HP;
+
+       val0x04 = (p->transmission_mode << 2) | p->guard_interval;
+       val0x05 = fec_tab[p->code_rate_HP];
+
+       if (p->hierarchy_information != HIERARCHY_NONE)
+               val0x05 |= (p->code_rate_LP - FEC_1_2) << 3;
+
+       val0x06 = (p->hierarchy_information << 2) | p->constellation;
+
+       l64781_writereg (state, 0x04, val0x04);
+       l64781_writereg (state, 0x05, val0x05);
+       l64781_writereg (state, 0x06, val0x06);
+
+       reset_afc (state);
+
+       /* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */
+       l64781_writereg (state, 0x15,
+                        p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3);
+       l64781_writereg (state, 0x16, init_freq & 0xff);
+       l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff);
+       l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff);
+
+       l64781_writereg (state, 0x1b, spi_bias & 0xff);
+       l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff);
+       l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) |
+               (param->inversion == INVERSION_ON ? 0x80 : 0x00));
+
+       l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff);
+       l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f);
+
+       l64781_readreg (state, 0x00);  /*  clear interrupt registers... */
+       l64781_readreg (state, 0x01);  /*  dto. */
+
+       apply_tps (state);
+
+       return 0;
+}
+
+static int get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* param)
+{
+       struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+       int tmp;
+
+
+       tmp = l64781_readreg(state, 0x04);
+       switch(tmp & 3) {
+       case 0:
+               param->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+               break;
+       case 1:
+               param->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+               break;
+       case 2:
+               param->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+               break;
+       case 3:
+               param->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+               break;
+       }
+       switch((tmp >> 2) & 3) {
+       case 0:
+               param->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+               break;
+       case 1:
+               param->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+               break;
+       default:
+               printk("Unexpected value for transmission_mode\n");
+       }
+
+
+
+       tmp = l64781_readreg(state, 0x05);
+       switch(tmp & 7) {
+       case 0:
+               param->u.ofdm.code_rate_HP = FEC_1_2;
+               break;
+       case 1:
+               param->u.ofdm.code_rate_HP = FEC_2_3;
+               break;
+       case 2:
+               param->u.ofdm.code_rate_HP = FEC_3_4;
+               break;
+       case 3:
+               param->u.ofdm.code_rate_HP = FEC_5_6;
+               break;
+       case 4:
+               param->u.ofdm.code_rate_HP = FEC_7_8;
+               break;
+       default:
+               printk("Unexpected value for code_rate_HP\n");
+       }
+       switch((tmp >> 3) & 7) {
+       case 0:
+               param->u.ofdm.code_rate_LP = FEC_1_2;
+               break;
+       case 1:
+               param->u.ofdm.code_rate_LP = FEC_2_3;
+               break;
+       case 2:
+               param->u.ofdm.code_rate_LP = FEC_3_4;
+               break;
+       case 3:
+               param->u.ofdm.code_rate_LP = FEC_5_6;
+               break;
+       case 4:
+               param->u.ofdm.code_rate_LP = FEC_7_8;
+               break;
+       default:
+               printk("Unexpected value for code_rate_LP\n");
+       }
+
+
+       tmp = l64781_readreg(state, 0x06);
+       switch(tmp & 3) {
+       case 0:
+               param->u.ofdm.constellation = QPSK;
+               break;
+       case 1:
+               param->u.ofdm.constellation = QAM_16;
+               break;
+       case 2:
+               param->u.ofdm.constellation = QAM_64;
+               break;
+       default:
+               printk("Unexpected value for constellation\n");
+       }
+       switch((tmp >> 2) & 7) {
+       case 0:
+               param->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+               break;
+       case 1:
+               param->u.ofdm.hierarchy_information = HIERARCHY_1;
+               break;
+       case 2:
+               param->u.ofdm.hierarchy_information = HIERARCHY_2;
+               break;
+       case 3:
+               param->u.ofdm.hierarchy_information = HIERARCHY_4;
+               break;
+       default:
+               printk("Unexpected value for hierarchy\n");
+       }
+
+
+       tmp = l64781_readreg (state, 0x1d);
+       param->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF;
+
+       tmp = (int) (l64781_readreg (state, 0x08) |
+                    (l64781_readreg (state, 0x09) << 8) |
+                    (l64781_readreg (state, 0x0a) << 16));
+       param->frequency += tmp;
+
+       return 0;
+}
+
+static int l64781_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+       int sync = l64781_readreg (state, 0x32);
+       int gain = l64781_readreg (state, 0x0e);
+
+       l64781_readreg (state, 0x00);  /*  clear interrupt registers... */
+       l64781_readreg (state, 0x01);  /*  dto. */
+
+       *status = 0;
+
+       if (gain > 5)
+               *status |= FE_HAS_SIGNAL;
+
+       if (sync & 0x02) /* VCXO locked, this criteria should be ok */
+               *status |= FE_HAS_CARRIER;
+
+       if (sync & 0x20)
+               *status |= FE_HAS_VITERBI;
+
+       if (sync & 0x40)
+               *status |= FE_HAS_SYNC;
+
+       if (sync == 0x7f)
+               *status |= FE_HAS_LOCK;
+
+       return 0;
+}
+
+static int l64781_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+       struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+       /*   XXX FIXME: set up counting period (reg 0x26...0x28)
+        */
+       *ber = l64781_readreg (state, 0x39)
+           | (l64781_readreg (state, 0x3a) << 8);
+
+       return 0;
+}
+
+static int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+       struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+       u8 gain = l64781_readreg (state, 0x0e);
+       *signal_strength = (gain << 8) | gain;
+
+       return 0;
+}
+
+static int l64781_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+       struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+       u8 avg_quality = 0xff - l64781_readreg (state, 0x33);
+       *snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/
+
+       return 0;
+}
+
+static int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+       struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+       *ucblocks = l64781_readreg (state, 0x37)
+          | (l64781_readreg (state, 0x38) << 8);
+
+       return 0;
+}
+
+static int l64781_sleep(struct dvb_frontend* fe)
+{
+       struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+       /* Power down */
+       return l64781_writereg (state, 0x3e, 0x5a);
+}
+
+static int l64781_init(struct dvb_frontend* fe)
+{
+       struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+        reset_and_configure (state);
+
+       /* Power up */
+       l64781_writereg (state, 0x3e, 0xa5);
+
+       /* Reset hard */
+       l64781_writereg (state, 0x2a, 0x04);
+       l64781_writereg (state, 0x2a, 0x00);
+
+       /* Set tuner specific things */
+       /* AFC_POL, set also in reset_afc */
+       l64781_writereg (state, 0x07, 0x8e);
+
+       /* Use internal ADC */
+       l64781_writereg (state, 0x0b, 0x81);
+
+       /* AGC loop gain, and polarity is positive */
+       l64781_writereg (state, 0x0c, 0x84);
+
+       /* Internal ADC outputs two's complement */
+       l64781_writereg (state, 0x0d, 0x8c);
+
+       /* With ppm=8000, it seems the DTR_SENSITIVITY will result in
+           value of 2 with all possible bandwidths and guard
+           intervals, which is the initial value anyway. */
+        /*l64781_writereg (state, 0x19, 0x92);*/
+
+       /* Everything is two's complement, soft bit and CSI_OUT too */
+       l64781_writereg (state, 0x1e, 0x09);
+
+       if (state->config->pll_init) state->config->pll_init(fe);
+
+       /* delay a bit after first init attempt */
+       if (state->first) {
+               state->first = 0;
+               msleep(200);
+       }
+
+       return 0;
+}
+
+static int l64781_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        fesettings->min_delay_ms = 200;
+        fesettings->step_size = 166667;
+        fesettings->max_drift = 166667*2;
+        return 0;
+}
+
+static void l64781_release(struct dvb_frontend* fe)
+{
+       struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+       kfree(state);
+}
+
+static struct dvb_frontend_ops l64781_ops;
+
+struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+                                  struct i2c_adapter* i2c)
+{
+       struct l64781_state* state = NULL;
+       int reg0x3e = -1;
+       u8 b0 [] = { 0x1a };
+       u8 b1 [] = { 0x00 };
+       struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+                          { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+       /* allocate memory for the internal state */
+       state = (struct l64781_state*) kmalloc(sizeof(struct l64781_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &l64781_ops, sizeof(struct dvb_frontend_ops));
+       state->first = 1;
+
+       /**
+        *  the L64781 won't show up before we send the reset_and_configure()
+        *  broadcast. If nothing responds there is no L64781 on the bus...
+        */
+       if (reset_and_configure(state) < 0) {
+               dprintk("No response to reset and configure broadcast...\n");
+               goto error;
+       }
+
+       /* The chip always responds to reads */
+       if (i2c_transfer(state->i2c, msg, 2) != 2) {
+               dprintk("No response to read on I2C bus\n");
+               goto error;
+       }
+
+       /* Save current register contents for bailout */
+       reg0x3e = l64781_readreg(state, 0x3e);
+
+       /* Reading the POWER_DOWN register always returns 0 */
+       if (reg0x3e != 0) {
+               dprintk("Device doesn't look like L64781\n");
+               goto error;
+       }
+
+       /* Turn the chip off */
+       l64781_writereg (state, 0x3e, 0x5a);
+
+       /* Responds to all reads with 0 */
+       if (l64781_readreg(state, 0x1a) != 0) {
+               dprintk("Read 1 returned unexpcted value\n");
+               goto error;
+       }
+
+       /* Turn the chip on */
+       l64781_writereg (state, 0x3e, 0xa5);
+
+       /* Responds with register default value */
+       if (l64781_readreg(state, 0x1a) != 0xa1) {
+               dprintk("Read 2 returned unexpcted value\n");
+               goto error;
+       }
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (reg0x3e >= 0) l64781_writereg (state, 0x3e, reg0x3e);  /* restore reg 0x3e */
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops l64781_ops = {
+
+       .info = {
+               .name = "LSI L64781 DVB-T",
+               .type = FE_OFDM,
+       /*      .frequency_min = ???,*/
+       /*      .frequency_max = ???,*/
+               .frequency_stepsize = 166666,
+       /*      .frequency_tolerance = ???,*/
+       /*      .symbol_rate_tolerance = ???,*/
+               .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                     FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+                     FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+                     FE_CAN_MUTE_TS
+       },
+
+       .release = l64781_release,
+
+       .init = l64781_init,
+       .sleep = l64781_sleep,
+
+       .set_frontend = apply_frontend_param,
+       .get_frontend = get_frontend,
+       .get_tune_settings = l64781_get_tune_settings,
+
+       .read_status = l64781_read_status,
+       .read_ber = l64781_read_ber,
+       .read_signal_strength = l64781_read_signal_strength,
+       .read_snr = l64781_read_snr,
+       .read_ucblocks = l64781_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler, Marko Kohtala");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(l64781_attach);
diff --git a/drivers/media/dvb/frontends/l64781.h b/drivers/media/dvb/frontends/l64781.h
new file mode 100644 (file)
index 0000000..259dfff
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+    driver for LSI L64781 COFDM demodulator
+
+    Copyright (C) 2001 Holger Waechtler <holger@convergence.de>
+                       for Convergence Integrated Media GmbH
+                       Marko Kohtala <marko.kohtala@nokia.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef L64781_H
+#define L64781_H
+
+#include <linux/dvb/frontend.h>
+
+struct l64781_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+
+extern struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+                                         struct i2c_adapter* i2c);
+
+#endif // L64781_H
diff --git a/drivers/media/dvb/frontends/mt312_priv.h b/drivers/media/dvb/frontends/mt312_priv.h
new file mode 100644 (file)
index 0000000..5e0b95b
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+    Driver for Zarlink MT312 QPSK Frontend
+
+    Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.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.
+
+    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 _DVB_FRONTENDS_MT312_PRIV
+#define _DVB_FRONTENDS_MT312_PRIV
+
+enum mt312_reg_addr {
+       QPSK_INT_H = 0,
+       QPSK_INT_M = 1,
+       QPSK_INT_L = 2,
+       FEC_INT = 3,
+       QPSK_STAT_H = 4,
+       QPSK_STAT_L = 5,
+       FEC_STATUS = 6,
+       LNB_FREQ_H = 7,
+       LNB_FREQ_L = 8,
+       M_SNR_H = 9,
+       M_SNR_L = 10,
+       VIT_ERRCNT_H = 11,
+       VIT_ERRCNT_M = 12,
+       VIT_ERRCNT_L = 13,
+       RS_BERCNT_H = 14,
+       RS_BERCNT_M = 15,
+       RS_BERCNT_L = 16,
+       RS_UBC_H = 17,
+       RS_UBC_L = 18,
+       SIG_LEVEL = 19,
+       GPP_CTRL = 20,
+       RESET = 21,
+       DISEQC_MODE = 22,
+       SYM_RATE_H = 23,
+       SYM_RATE_L = 24,
+       VIT_MODE = 25,
+       QPSK_CTRL = 26,
+       GO = 27,
+       IE_QPSK_H = 28,
+       IE_QPSK_M = 29,
+       IE_QPSK_L = 30,
+       IE_FEC = 31,
+       QPSK_STAT_EN = 32,
+       FEC_STAT_EN = 33,
+       SYS_CLK = 34,
+       DISEQC_RATIO = 35,
+       DISEQC_INSTR = 36,
+       FR_LIM = 37,
+       FR_OFF = 38,
+       AGC_CTRL = 39,
+       AGC_INIT = 40,
+       AGC_REF = 41,
+       AGC_MAX = 42,
+       AGC_MIN = 43,
+       AGC_LK_TH = 44,
+       TS_AGC_LK_TH = 45,
+       AGC_PWR_SET = 46,
+       QPSK_MISC = 47,
+       SNR_THS_LOW = 48,
+       SNR_THS_HIGH = 49,
+       TS_SW_RATE = 50,
+       TS_SW_LIM_L = 51,
+       TS_SW_LIM_H = 52,
+       CS_SW_RATE_1 = 53,
+       CS_SW_RATE_2 = 54,
+       CS_SW_RATE_3 = 55,
+       CS_SW_RATE_4 = 56,
+       CS_SW_LIM = 57,
+       TS_LPK = 58,
+       TS_LPK_M = 59,
+       TS_LPK_L = 60,
+       CS_KPROP_H = 61,
+       CS_KPROP_L = 62,
+       CS_KINT_H = 63,
+       CS_KINT_L = 64,
+       QPSK_SCALE = 65,
+       TLD_OUTCLK_TH = 66,
+       TLD_INCLK_TH = 67,
+       FLD_TH = 68,
+       PLD_OUTLK3 = 69,
+       PLD_OUTLK2 = 70,
+       PLD_OUTLK1 = 71,
+       PLD_OUTLK0 = 72,
+       PLD_INLK3 = 73,
+       PLD_INLK2 = 74,
+       PLD_INLK1 = 75,
+       PLD_INLK0 = 76,
+       PLD_ACC_TIME = 77,
+       SWEEP_PAR = 78,
+       STARTUP_TIME = 79,
+       LOSSLOCK_TH = 80,
+       FEC_LOCK_TM = 81,
+       LOSSLOCK_TM = 82,
+       VIT_ERRPER_H = 83,
+       VIT_ERRPER_M = 84,
+       VIT_ERRPER_L = 85,
+       VIT_SETUP = 86,
+       VIT_REF0 = 87,
+       VIT_REF1 = 88,
+       VIT_REF2 = 89,
+       VIT_REF3 = 90,
+       VIT_REF4 = 91,
+       VIT_REF5 = 92,
+       VIT_REF6 = 93,
+       VIT_MAXERR = 94,
+       BA_SETUPT = 95,
+       OP_CTRL = 96,
+       FEC_SETUP = 97,
+       PROG_SYNC = 98,
+       AFC_SEAR_TH = 99,
+       CSACC_DIF_TH = 100,
+       QPSK_LK_CT = 101,
+       QPSK_ST_CT = 102,
+       MON_CTRL = 103,
+       QPSK_RESET = 104,
+       QPSK_TST_CT = 105,
+       QPSK_TST_ST = 106,
+       TEST_R = 107,
+       AGC_H = 108,
+       AGC_M = 109,
+       AGC_L = 110,
+       FREQ_ERR1_H = 111,
+       FREQ_ERR1_M = 112,
+       FREQ_ERR1_L = 113,
+       FREQ_ERR2_H = 114,
+       FREQ_ERR2_L = 115,
+       SYM_RAT_OP_H = 116,
+       SYM_RAT_OP_L = 117,
+       DESEQC2_INT = 118,
+       DISEQC2_STAT = 119,
+       DISEQC2_FIFO = 120,
+       DISEQC2_CTRL1 = 121,
+       DISEQC2_CTRL2 = 122,
+       MONITOR_H = 123,
+       MONITOR_L = 124,
+       TEST_MODE = 125,
+       ID = 126,
+       CONFIG = 127
+};
+
+enum mt312_model_id {
+       ID_VP310 = 1,
+       ID_MT312 = 3
+};
+
+#endif                         /* DVB_FRONTENDS_MT312_PRIV */
diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb/frontends/mt352.c
new file mode 100644 (file)
index 0000000..86a4627
--- /dev/null
@@ -0,0 +1,558 @@
+/*
+ *  Driver for Zarlink DVB-T MT352 demodulator
+ *
+ *  Written by Holger Waechtler <holger@qanu.de>
+ *      and Daniel Mack <daniel@qanu.de>
+ *
+ *  AVerMedia AVerTV DVB-T 771 support by
+ *       Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ *  Support for Samsung TDTC9251DH01C(M) tuner
+ *  Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ *                     Amauri  Celani  <acelani@essegi.net>
+ *
+ *  DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ *       Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ *
+ *  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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "mt352_priv.h"
+#include "mt352.h"
+
+struct mt352_state {
+
+       struct i2c_adapter* i2c;
+
+       struct dvb_frontend_ops ops;
+
+       /* configuration settings */
+       const struct mt352_config* config;
+
+       struct dvb_frontend frontend;
+};
+
+static int debug;
+#define dprintk(args...) \
+do {                                                                   \
+               if (debug) printk(KERN_DEBUG "mt352: " args); \
+} while (0)
+
+int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen)
+{
+       struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+       struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0,
+                              .buf = ibuf, .len = ilen };
+       int err = i2c_transfer(state->i2c, &msg, 1);
+       if (err != 1) {
+               dprintk("mt352_write() failed (err = %d)!\n", err);
+               return err;
+}
+
+       return 0;
+}
+
+static u8 mt352_read_register(struct mt352_state* state, u8 reg)
+{
+       int ret;
+       u8 b0 [] = { reg };
+       u8 b1 [] = { 0 };
+       struct i2c_msg msg [] = { { .addr = state->config->demod_address,
+                                   .flags = 0,
+                                   .buf = b0, .len = 1 },
+                                 { .addr = state->config->demod_address,
+                                   .flags = I2C_M_RD,
+                                   .buf = b1, .len = 1 } };
+
+       ret = i2c_transfer(state->i2c, msg, 2);
+
+       if (ret != 2)
+               dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+       return b1[0];
+}
+
+
+
+
+
+
+
+
+
+
+
+
+static int mt352_sleep(struct dvb_frontend* fe)
+{
+       static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 };
+
+       mt352_write(fe, mt352_softdown, sizeof(mt352_softdown));
+
+       return 0;
+}
+
+static int mt352_set_parameters(struct dvb_frontend* fe,
+                               struct dvb_frontend_parameters *param)
+{
+       struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+       unsigned char buf[14];
+       unsigned int tps = 0;
+       struct dvb_ofdm_parameters *op = &param->u.ofdm;
+       int i;
+
+       switch (op->code_rate_HP) {
+               case FEC_2_3:
+                       tps |= (1 << 7);
+                       break;
+               case FEC_3_4:
+                       tps |= (2 << 7);
+                       break;
+               case FEC_5_6:
+                       tps |= (3 << 7);
+                       break;
+               case FEC_7_8:
+                       tps |= (4 << 7);
+                       break;
+               case FEC_1_2:
+               case FEC_AUTO:
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       switch (op->code_rate_LP) {
+               case FEC_2_3:
+                       tps |= (1 << 4);
+                       break;
+               case FEC_3_4:
+                       tps |= (2 << 4);
+                       break;
+               case FEC_5_6:
+                       tps |= (3 << 4);
+                       break;
+               case FEC_7_8:
+                       tps |= (4 << 4);
+                       break;
+               case FEC_1_2:
+               case FEC_AUTO:
+                       break;
+               case FEC_NONE:
+                       if (op->hierarchy_information == HIERARCHY_AUTO ||
+                           op->hierarchy_information == HIERARCHY_NONE)
+                               break;
+               default:
+                       return -EINVAL;
+       }
+
+       switch (op->constellation) {
+               case QPSK:
+                       break;
+               case QAM_AUTO:
+               case QAM_16:
+                       tps |= (1 << 13);
+                       break;
+               case QAM_64:
+                       tps |= (2 << 13);
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       switch (op->transmission_mode) {
+               case TRANSMISSION_MODE_2K:
+               case TRANSMISSION_MODE_AUTO:
+                       break;
+               case TRANSMISSION_MODE_8K:
+                       tps |= (1 << 0);
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       switch (op->guard_interval) {
+               case GUARD_INTERVAL_1_32:
+               case GUARD_INTERVAL_AUTO:
+                       break;
+               case GUARD_INTERVAL_1_16:
+                       tps |= (1 << 2);
+                       break;
+               case GUARD_INTERVAL_1_8:
+                       tps |= (2 << 2);
+                       break;
+               case GUARD_INTERVAL_1_4:
+                       tps |= (3 << 2);
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       switch (op->hierarchy_information) {
+               case HIERARCHY_AUTO:
+               case HIERARCHY_NONE:
+                       break;
+               case HIERARCHY_1:
+                       tps |= (1 << 10);
+                       break;
+               case HIERARCHY_2:
+                       tps |= (2 << 10);
+                       break;
+               case HIERARCHY_4:
+                       tps |= (3 << 10);
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+
+       buf[0] = TPS_GIVEN_1; /* TPS_GIVEN_1 and following registers */
+
+       buf[1] = msb(tps);      /* TPS_GIVEN_(1|0) */
+       buf[2] = lsb(tps);
+
+       buf[3] = 0x50;
+
+       /**
+        *  these settings assume 20.48MHz f_ADC, for other tuners you might
+        *  need other values. See p. 33 in the MT352 Design Manual.
+        */
+       if (op->bandwidth == BANDWIDTH_8_MHZ) {
+               buf[4] = 0x72;  /* TRL_NOMINAL_RATE_(1|0) */
+               buf[5] = 0x49;
+       } else if (op->bandwidth == BANDWIDTH_7_MHZ) {
+               buf[4] = 0x64;
+               buf[5] = 0x00;
+       } else {                /* 6MHz */
+               buf[4] = 0x55;
+               buf[5] = 0xb7;
+       }
+
+       buf[6] = 0x31;  /* INPUT_FREQ_(1|0), 20.48MHz clock, 36.166667MHz IF */
+       buf[7] = 0x05;  /* see MT352 Design Manual page 32 for details */
+
+       state->config->pll_set(fe, param, buf+8);
+
+       buf[13] = 0x01; /* TUNER_GO!! */
+
+       /* Only send the tuning request if the tuner doesn't have the requested
+        * parameters already set.  Enhances tuning time and prevents stream
+        * breakup when retuning the same transponder. */
+       for (i = 1; i < 13; i++)
+               if (buf[i] != mt352_read_register(state, i + 0x50)) {
+                       mt352_write(fe, buf, sizeof(buf));
+                       break;
+               }
+
+       return 0;
+}
+
+static int mt352_get_parameters(struct dvb_frontend* fe,
+                               struct dvb_frontend_parameters *param)
+{
+       struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+       u16 tps;
+       u16 div;
+       u8 trl;
+       struct dvb_ofdm_parameters *op = &param->u.ofdm;
+       static const u8 tps_fec_to_api[8] =
+       {
+               FEC_1_2,
+               FEC_2_3,
+               FEC_3_4,
+               FEC_5_6,
+               FEC_7_8,
+               FEC_AUTO,
+               FEC_AUTO,
+               FEC_AUTO
+       };
+
+       if ( (mt352_read_register(state,0x00) & 0xC0) != 0xC0 )
+       {
+               return -EINVAL;
+       }
+
+       /* Use TPS_RECEIVED-registers, not the TPS_CURRENT-registers because
+        * the mt352 sometimes works with the wrong parameters
+        */
+       tps = (mt352_read_register(state, TPS_RECEIVED_1) << 8) | mt352_read_register(state, TPS_RECEIVED_0);
+       div = (mt352_read_register(state, CHAN_START_1) << 8) | mt352_read_register(state, CHAN_START_0);
+       trl = mt352_read_register(state, TRL_NOMINAL_RATE_1);
+
+       op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7];
+       op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7];
+
+       switch ( (tps >> 13) & 3)
+       {
+               case 0:
+                       op->constellation = QPSK;
+                       break;
+               case 1:
+                       op->constellation = QAM_16;
+                       break;
+               case 2:
+                       op->constellation = QAM_64;
+                       break;
+               default:
+                       op->constellation = QAM_AUTO;
+                       break;
+       }
+
+       op->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K : TRANSMISSION_MODE_2K;
+
+       switch ( (tps >> 2) & 3)
+       {
+               case 0:
+                       op->guard_interval = GUARD_INTERVAL_1_32;
+                       break;
+               case 1:
+                       op->guard_interval = GUARD_INTERVAL_1_16;
+                       break;
+               case 2:
+                       op->guard_interval = GUARD_INTERVAL_1_8;
+                       break;
+               case 3:
+                       op->guard_interval = GUARD_INTERVAL_1_4;
+                       break;
+               default:
+                       op->guard_interval = GUARD_INTERVAL_AUTO;
+                       break;
+       }
+
+       switch ( (tps >> 10) & 7)
+       {
+               case 0:
+                       op->hierarchy_information = HIERARCHY_NONE;
+                       break;
+               case 1:
+                       op->hierarchy_information = HIERARCHY_1;
+                       break;
+               case 2:
+                       op->hierarchy_information = HIERARCHY_2;
+                       break;
+               case 3:
+                       op->hierarchy_information = HIERARCHY_4;
+                       break;
+               default:
+                       op->hierarchy_information = HIERARCHY_AUTO;
+                       break;
+       }
+
+       param->frequency = ( 500 * (div - IF_FREQUENCYx6) ) / 3 * 1000;
+
+       if (trl == 0x72)
+       {
+               op->bandwidth = BANDWIDTH_8_MHZ;
+       }
+       else if (trl == 0x64)
+       {
+               op->bandwidth = BANDWIDTH_7_MHZ;
+       }
+       else
+       {
+               op->bandwidth = BANDWIDTH_6_MHZ;
+       }
+
+
+       if (mt352_read_register(state, STATUS_2) & 0x02)
+               param->inversion = INVERSION_OFF;
+       else
+               param->inversion = INVERSION_ON;
+
+       return 0;
+}
+
+static int mt352_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+       u8 r;
+
+               *status = 0;
+       r = mt352_read_register (state, STATUS_0);
+               if (r & (1 << 4))
+                       *status = FE_HAS_CARRIER;
+               if (r & (1 << 1))
+                       *status |= FE_HAS_VITERBI;
+               if (r & (1 << 5))
+                       *status |= FE_HAS_LOCK;
+
+       r = mt352_read_register (state, STATUS_1);
+               if (r & (1 << 1))
+                       *status |= FE_HAS_SYNC;
+
+       r = mt352_read_register (state, STATUS_3);
+               if (r & (1 << 6))
+                       *status |= FE_HAS_SIGNAL;
+
+       if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) !=
+                     (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))
+               *status &= ~FE_HAS_LOCK;
+
+       return 0;
+}
+
+static int mt352_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+       struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+
+       *ber = (mt352_read_register (state, RS_ERR_CNT_2) << 16) |
+              (mt352_read_register (state, RS_ERR_CNT_1) << 8) |
+              (mt352_read_register (state, RS_ERR_CNT_0));
+
+                       return 0;
+       }
+
+static int mt352_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+       struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+
+       u16 signal = (mt352_read_register (state, AGC_GAIN_3) << 8) |
+                    (mt352_read_register (state, AGC_GAIN_2));
+
+       *strength = ~signal;
+       return 0;
+}
+
+static int mt352_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+       struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+
+       u8 _snr = mt352_read_register (state, SNR);
+       *snr = (_snr << 8) | _snr;
+
+       return 0;
+}
+
+static int mt352_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+       struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+
+       *ucblocks = (mt352_read_register (state,  RS_UBC_1) << 8) |
+                   (mt352_read_register (state,  RS_UBC_0));
+
+       return 0;
+       }
+
+static int mt352_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
+{
+       fe_tune_settings->min_delay_ms = 800;
+       fe_tune_settings->step_size = 0;
+       fe_tune_settings->max_drift = 0;
+
+       return 0;
+       }
+
+static int mt352_init(struct dvb_frontend* fe)
+{
+       struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+
+       static u8 mt352_reset_attach [] = { RESET, 0xC0 };
+
+       if ((mt352_read_register(state, CLOCK_CTL) & 0x10) == 0 ||
+           (mt352_read_register(state, CONFIG) & 0x20) == 0) {
+
+       /* Do a "hard" reset */
+               mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach));
+               return state->config->demod_init(fe);
+       }
+
+       return 0;
+       }
+
+static void mt352_release(struct dvb_frontend* fe)
+{
+       struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+               kfree(state);
+       }
+
+static struct dvb_frontend_ops mt352_ops;
+
+struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+                                 struct i2c_adapter* i2c)
+{
+       struct mt352_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct mt352_state*) kmalloc(sizeof(struct mt352_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &mt352_ops, sizeof(struct dvb_frontend_ops));
+
+       /* check if the demod is there */
+       if (mt352_read_register(state, CHIP_ID) != ID_MT352) goto error;
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops mt352_ops = {
+
+       .info = {
+               .name                   = "Zarlink MT352 DVB-T",
+               .type                   = FE_OFDM,
+               .frequency_min          = 174000000,
+               .frequency_max          = 862000000,
+               .frequency_stepsize     = 166667,
+               .frequency_tolerance    = 0,
+               .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
+                       FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+                       FE_CAN_FEC_AUTO |
+                       FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+                       FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+                       FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER |
+                       FE_CAN_MUTE_TS
+       },
+
+       .release = mt352_release,
+
+       .init = mt352_init,
+       .sleep = mt352_sleep,
+
+       .set_frontend = mt352_set_parameters,
+       .get_frontend = mt352_get_parameters,
+       .get_tune_settings = mt352_get_tune_settings,
+
+       .read_status = mt352_read_status,
+       .read_ber = mt352_read_ber,
+       .read_signal_strength = mt352_read_signal_strength,
+       .read_snr = mt352_read_snr,
+       .read_ucblocks = mt352_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(mt352_attach);
+EXPORT_SYMBOL(mt352_write);
diff --git a/drivers/media/dvb/frontends/mt352.h b/drivers/media/dvb/frontends/mt352.h
new file mode 100644 (file)
index 0000000..635095b
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  Driver for Zarlink DVB-T MT352 demodulator
+ *
+ *  Written by Holger Waechtler <holger@qanu.de>
+ *      and Daniel Mack <daniel@qanu.de>
+ *
+ *  AVerMedia AVerTV DVB-T 771 support by
+ *       Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ *  Support for Samsung TDTC9251DH01C(M) tuner
+ *  Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ *                     Amauri  Celani  <acelani@essegi.net>
+ *
+ *  DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ *       Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ *
+ *  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 MT352_H
+#define MT352_H
+
+#include <linux/dvb/frontend.h>
+
+struct mt352_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* Initialise the demodulator and PLL. Cannot be NULL */
+       int (*demod_init)(struct dvb_frontend* fe);
+
+       /* PLL setup - fill out the supplied 5 byte buffer with your PLL settings.
+        * byte0: Set to pll i2c address (nonlinux; left shifted by 1)
+        * byte1-4: PLL configuration.
+        */
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf);
+};
+
+extern struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+                                        struct i2c_adapter* i2c);
+
+extern int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen);
+
+#endif // MT352_H
diff --git a/drivers/media/dvb/frontends/mt352_priv.h b/drivers/media/dvb/frontends/mt352_priv.h
new file mode 100644 (file)
index 0000000..44ad0d4
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *  Driver for Zarlink DVB-T MT352 demodulator
+ *
+ *  Written by Holger Waechtler <holger@qanu.de>
+ *      and Daniel Mack <daniel@qanu.de>
+ *
+ *  AVerMedia AVerTV DVB-T 771 support by
+ *       Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ *  Support for Samsung TDTC9251DH01C(M) tuner
+ *  Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ *                     Amauri  Celani  <acelani@essegi.net>
+ *
+ *  DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ *       Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ *
+ *  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 _MT352_PRIV_
+#define _MT352_PRIV_
+
+#define ID_MT352        0x13
+
+#define msb(x) (((x) >> 8) & 0xff)
+#define lsb(x) ((x) & 0xff)
+
+enum mt352_reg_addr {
+       STATUS_0           = 0x00,
+       STATUS_1           = 0x01,
+       STATUS_2           = 0x02,
+       STATUS_3           = 0x03,
+       STATUS_4           = 0x04,
+       INTERRUPT_0        = 0x05,
+       INTERRUPT_1        = 0x06,
+       INTERRUPT_2        = 0x07,
+       INTERRUPT_3        = 0x08,
+       SNR                = 0x09,
+       VIT_ERR_CNT_2      = 0x0A,
+       VIT_ERR_CNT_1      = 0x0B,
+       VIT_ERR_CNT_0      = 0x0C,
+       RS_ERR_CNT_2       = 0x0D,
+       RS_ERR_CNT_1       = 0x0E,
+       RS_ERR_CNT_0       = 0x0F,
+       RS_UBC_1           = 0x10,
+       RS_UBC_0           = 0x11,
+       AGC_GAIN_3         = 0x12,
+       AGC_GAIN_2         = 0x13,
+       AGC_GAIN_1         = 0x14,
+       AGC_GAIN_0         = 0x15,
+       FREQ_OFFSET_2      = 0x17,
+       FREQ_OFFSET_1      = 0x18,
+       FREQ_OFFSET_0      = 0x19,
+       TIMING_OFFSET_1    = 0x1A,
+       TIMING_OFFSET_0    = 0x1B,
+       CHAN_FREQ_1        = 0x1C,
+       CHAN_FREQ_0        = 0x1D,
+       TPS_RECEIVED_1     = 0x1E,
+       TPS_RECEIVED_0     = 0x1F,
+       TPS_CURRENT_1      = 0x20,
+       TPS_CURRENT_0      = 0x21,
+       TPS_CELL_ID_1      = 0x22,
+       TPS_CELL_ID_0      = 0x23,
+       TPS_MISC_DATA_2    = 0x24,
+       TPS_MISC_DATA_1    = 0x25,
+       TPS_MISC_DATA_0    = 0x26,
+       RESET              = 0x50,
+       TPS_GIVEN_1        = 0x51,
+       TPS_GIVEN_0        = 0x52,
+       ACQ_CTL            = 0x53,
+       TRL_NOMINAL_RATE_1 = 0x54,
+       TRL_NOMINAL_RATE_0 = 0x55,
+       INPUT_FREQ_1       = 0x56,
+       INPUT_FREQ_0       = 0x57,
+       TUNER_ADDR         = 0x58,
+       CHAN_START_1       = 0x59,
+       CHAN_START_0       = 0x5A,
+       CONT_1             = 0x5B,
+       CONT_0             = 0x5C,
+       TUNER_GO           = 0x5D,
+       STATUS_EN_0        = 0x5F,
+       STATUS_EN_1        = 0x60,
+       INTERRUPT_EN_0     = 0x61,
+       INTERRUPT_EN_1     = 0x62,
+       INTERRUPT_EN_2     = 0x63,
+       INTERRUPT_EN_3     = 0x64,
+       AGC_TARGET         = 0x67,
+       AGC_CTL            = 0x68,
+       CAPT_RANGE         = 0x75,
+       SNR_SELECT_1       = 0x79,
+       SNR_SELECT_0       = 0x7A,
+       RS_ERR_PER_1       = 0x7C,
+       RS_ERR_PER_0       = 0x7D,
+       CHIP_ID            = 0x7F,
+       CHAN_STOP_1        = 0x80,
+       CHAN_STOP_0        = 0x81,
+       CHAN_STEP_1        = 0x82,
+       CHAN_STEP_0        = 0x83,
+       FEC_LOCK_TIME      = 0x85,
+       OFDM_LOCK_TIME     = 0x86,
+       ACQ_DELAY          = 0x87,
+       SCAN_CTL           = 0x88,
+       CLOCK_CTL          = 0x89,
+       CONFIG             = 0x8A,
+       MCLK_RATIO         = 0x8B,
+       GPP_CTL            = 0x8C,
+       ADC_CTL_1          = 0x8E,
+       ADC_CTL_0          = 0x8F
+};
+
+/* here we assume 1/6MHz == 166.66kHz stepsize */
+#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
+
+#endif                          /* _MT352_PRIV_ */
diff --git a/drivers/media/dvb/frontends/nxt2002.c b/drivers/media/dvb/frontends/nxt2002.c
new file mode 100644 (file)
index 0000000..a5a3ab9
--- /dev/null
@@ -0,0 +1,670 @@
+/*
+    Support for B2C2/BBTI Technisat Air2PC - ATSC  
+
+    Copyright (C) 2004 Taylor Jacob <rtjacob@earthlink.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw"
+#define CRC_CCIT_MASK 0x1021
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "nxt2002.h"
+
+struct nxt2002_state {
+
+       struct i2c_adapter* i2c;
+       struct dvb_frontend_ops ops;
+       const struct nxt2002_config* config;
+       struct dvb_frontend frontend;
+
+       /* demodulator private data */
+       u8 initialised:1;
+};
+
+static int debug;
+#define dprintk(args...) \
+       do { \
+               if (debug) printk(KERN_DEBUG "nxt2002: " args); \
+       } while (0)
+
+static int i2c_writebytes (struct nxt2002_state* state, u8 reg, u8 *buf, u8 len)
+{
+       /* probbably a much better way or doing this */
+       u8 buf2 [256],x;
+       int err;
+       struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf2, .len = len + 1 };
+       
+       buf2[0] = reg;
+       for (x = 0 ; x < len ; x++)
+               buf2[x+1] = buf[x];
+
+       if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+               printk ("%s: i2c write error (addr %02x, err == %i)\n",
+                       __FUNCTION__, state->config->demod_address, err);
+               return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+static u8 i2c_readbytes (struct nxt2002_state* state, u8 reg, u8* buf, u8 len)
+{
+       u8 reg2 [] = { reg };
+
+       struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = reg2, .len = 1 },
+                       { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } };
+
+       int err;
+
+       if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) {
+               printk ("%s: i2c read error (addr %02x, err == %i)\n",
+                       __FUNCTION__, state->config->demod_address, err);
+               return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+static u16 nxt2002_crc(u16 crc, u8 c) 
+{
+
+       u8 i;
+       u16 input = (u16) c & 0xFF;
+  
+       input<<=8;
+       for(i=0 ;i<8 ;i++) {
+               if((crc ^ input) & 0x8000)
+                       crc=(crc<<1)^CRC_CCIT_MASK;
+               else
+                       crc<<=1;
+       input<<=1;
+       }
+       return crc;
+}
+
+static int nxt2002_writereg_multibyte (struct nxt2002_state* state, u8 reg, u8* data, u8 len)
+{
+       u8 buf;
+       dprintk("%s\n", __FUNCTION__);
+
+       /* set multi register length */
+       i2c_writebytes(state,0x34,&len,1);
+
+       /* set mutli register register */
+       i2c_writebytes(state,0x35,&reg,1);
+
+       /* send the actual data */
+       i2c_writebytes(state,0x36,data,len);
+
+       /* toggle the multireg write bit*/
+       buf = 0x02;
+       i2c_writebytes(state,0x21,&buf,1);
+
+       i2c_readbytes(state,0x21,&buf,1);
+
+       if ((buf & 0x02) == 0)
+               return 0;
+       dprintk("Error writing multireg register %02X\n",reg);
+
+       return 0;
+}
+
+static int nxt2002_readreg_multibyte (struct nxt2002_state* state, u8 reg, u8* data, u8 len)
+{
+       u8 len2;
+       dprintk("%s\n", __FUNCTION__);
+
+       /* set multi register length */
+       len2 = len & 0x80;
+       i2c_writebytes(state,0x34,&len2,1);
+
+       /* set mutli register register */
+       i2c_writebytes(state,0x35,&reg,1);
+
+       /* send the actual data */
+       i2c_readbytes(state,reg,data,len);
+
+       return 0;
+}
+
+static void nxt2002_microcontroller_stop (struct nxt2002_state* state)
+{
+       u8 buf[2],counter = 0;
+       dprintk("%s\n", __FUNCTION__);
+
+       buf[0] = 0x80;
+       i2c_writebytes(state,0x22,buf,1);
+
+       while (counter < 20) { 
+               i2c_readbytes(state,0x31,buf,1);
+               if (buf[0] & 0x40) 
+                       return;
+               msleep(10);
+               counter++;
+       }
+
+       dprintk("Timeout waiting for micro to stop.. This is ok after firmware upload\n");
+       return; 
+}
+
+static void nxt2002_microcontroller_start (struct nxt2002_state* state)
+{
+       u8 buf;
+       dprintk("%s\n", __FUNCTION__);
+
+       buf = 0x00;
+       i2c_writebytes(state,0x22,&buf,1);
+}
+
+static int nxt2002_writetuner (struct nxt2002_state* state, u8* data)
+{
+       u8 buf,count = 0;
+
+       dprintk("Tuner Bytes: %02X %02X %02X %02X\n",data[0],data[1],data[2],data[3]);
+
+       dprintk("%s\n", __FUNCTION__);
+       /* stop the micro first */
+       nxt2002_microcontroller_stop(state);
+
+       /* set the i2c transfer speed to the tuner */
+       buf = 0x03;
+       i2c_writebytes(state,0x20,&buf,1);
+
+       /* setup to transfer 4 bytes via i2c */
+       buf = 0x04;
+       i2c_writebytes(state,0x34,&buf,1);
+
+       /* write actual tuner bytes */
+       i2c_writebytes(state,0x36,data,4);
+
+       /* set tuner i2c address */
+       buf = 0xC2;
+       i2c_writebytes(state,0x35,&buf,1);
+
+       /* write UC Opmode to begin transfer */
+       buf = 0x80;
+       i2c_writebytes(state,0x21,&buf,1);
+       while (count < 20) {
+               i2c_readbytes(state,0x21,&buf,1);
+               if ((buf & 0x80)== 0x00)
+                       return 0;
+               msleep(100);
+               count++;
+       }
+
+       printk("nxt2002: timeout error writing tuner\n");
+       return 0;
+}
+
+static void nxt2002_agc_reset(struct nxt2002_state* state)
+{
+       u8 buf;
+       dprintk("%s\n", __FUNCTION__);
+
+       buf = 0x08;
+       i2c_writebytes(state,0x08,&buf,1);
+
+       buf = 0x00;
+       i2c_writebytes(state,0x08,&buf,1);
+
+       return;
+}
+
+static int nxt2002_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+
+       struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+       u8 buf[256],written = 0,chunkpos = 0;
+       u16 rambase,position,crc = 0;  
+
+       dprintk("%s\n", __FUNCTION__);
+       dprintk("Firmware is %zu bytes\n",fw->size);
+
+       /* Get the RAM base for this nxt2002 */
+       i2c_readbytes(state,0x10,buf,1);
+
+       
+       if (buf[0] & 0x10)
+               rambase = 0x1000;
+       else
+               rambase = 0x0000;
+
+       dprintk("rambase on this nxt2002 is %04X\n",rambase);
+
+       /* Hold the micro in reset while loading firmware */
+       buf[0] = 0x80;
+       i2c_writebytes(state,0x2B,buf,1);
+
+        
+       for (position = 0; position < fw->size ; position++) {
+               if (written == 0) {
+                       crc = 0;
+                       chunkpos = 0x28;
+                       buf[0] = ((rambase + position) >> 8);
+                       buf[1] = (rambase + position) & 0xFF;
+                       buf[2] = 0x81;
+                       /* write starting address */
+                       i2c_writebytes(state,0x29,buf,3);
+               }
+               written++;          
+               chunkpos++;
+
+               if ((written % 4) == 0)
+                       i2c_writebytes(state,chunkpos,&fw->data[position-3],4);
+
+               crc = nxt2002_crc(crc,fw->data[position]);
+
+            
+               if ((written == 255) || (position+1 == fw->size)) {
+                       /* write remaining bytes of firmware */
+                       i2c_writebytes(state, chunkpos+4-(written %4),
+                               &fw->data[position-(written %4) + 1],
+                               written %4);
+                       buf[0] = crc << 8;
+                       buf[1] = crc & 0xFF;
+               
+                       /* write crc */
+                       i2c_writebytes(state,0x2C,buf,2);
+
+                       /* do a read to stop things */
+                       i2c_readbytes(state,0x2A,buf,1);
+
+                       /* set transfer mode to complete */
+                       buf[0] = 0x80;
+                       i2c_writebytes(state,0x2B,buf,1);
+
+                       written = 0;
+               }
+       }
+
+       printk ("done.\n");
+       return 0;
+};
+
+
+static int nxt2002_setup_frontend_parameters (struct dvb_frontend* fe,
+                                            struct dvb_frontend_parameters *p)
+{
+       struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+       u32 freq = 0;
+       u16 tunerfreq = 0; 
+       u8 buf[4];
+
+       freq = 44000 + ( p->frequency / 1000 ); 
+
+       dprintk("freq = %d      p->frequency = %d\n",freq,p->frequency);
+
+       tunerfreq = freq * 24/4000;
+       buf[0] = (tunerfreq >> 8) & 0x7F;
+       buf[1] = (tunerfreq & 0xFF);
+        
+       if (p->frequency <= 214000000) {
+               buf[2] = 0x84 + (0x06 << 3);
+               buf[3] = (p->frequency <= 172000000) ? 0x01 : 0x02;
+       } else if (p->frequency <= 721000000) {
+               buf[2] = 0x84 + (0x07 << 3);
+               buf[3] = (p->frequency <= 467000000) ? 0x02 : 0x08;
+       } else if (p->frequency <= 841000000) {
+               buf[2] = 0x84 + (0x0E << 3);
+               buf[3] = 0x08;
+       } else {
+               buf[2] = 0x84 + (0x0F << 3);         
+               buf[3] = 0x02;
+       }
+
+       /* write frequency information */
+       nxt2002_writetuner(state,buf);
+
+       /* reset the agc now that tuning has been completed */
+       nxt2002_agc_reset(state);
+
+       /* set target power level */
+       buf[0] = 0x70;
+       i2c_writebytes(state,0x42,buf,1);
+
+       /* configure sdm */
+       buf[0] = 0x87;
+       i2c_writebytes(state,0x57,buf,1);
+
+       /* write sdm1 input */
+       buf[0] = 0x10;
+       buf[1] = 0x00;
+       nxt2002_writereg_multibyte(state,0x58,buf,2);
+
+       /* write sdmx input */
+       buf[0] = 0x60;
+       buf[1] = 0x00;
+       nxt2002_writereg_multibyte(state,0x5C,buf,2);
+
+       /* write adc power lpf fc */
+       buf[0] = 0x05;
+       i2c_writebytes(state,0x43,buf,1);
+
+       /* write adc power lpf fc */
+       buf[0] = 0x05;
+       i2c_writebytes(state,0x43,buf,1);
+
+       /* write accumulator2 input */
+       buf[0] = 0x80;
+       buf[1] = 0x00;
+       nxt2002_writereg_multibyte(state,0x4B,buf,2);
+
+       /* write kg1 */
+       buf[0] = 0x00;
+       i2c_writebytes(state,0x4D,buf,1);
+
+       /* write sdm12 lpf fc */
+       buf[0] = 0x44;
+       i2c_writebytes(state,0x55,buf,1);
+
+       /* write agc control reg */
+       buf[0] = 0x04;
+       i2c_writebytes(state,0x41,buf,1);
+
+       /* write agc ucgp0 */
+       buf[0] = 0x00;
+       i2c_writebytes(state,0x30,buf,1);
+
+       /* write agc control reg */
+       buf[0] = 0x00;
+       i2c_writebytes(state,0x41,buf,1);
+
+       /* write accumulator2 input */
+       buf[0] = 0x80;
+       buf[1] = 0x00;
+       nxt2002_writereg_multibyte(state,0x49,buf,2);
+       nxt2002_writereg_multibyte(state,0x4B,buf,2);
+
+       /* write agc control reg */
+       buf[0] = 0x04;
+       i2c_writebytes(state,0x41,buf,1);
+
+       nxt2002_microcontroller_start(state);
+
+       /* adjacent channel detection should be done here, but I don't 
+       have any stations with this need so I cannot test it */
+
+       return 0;
+}
+
+static int nxt2002_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+       u8 lock;
+       i2c_readbytes(state,0x31,&lock,1);
+
+       *status = 0;
+       if (lock & 0x20) {
+               *status |= FE_HAS_SIGNAL;
+               *status |= FE_HAS_CARRIER;
+               *status |= FE_HAS_VITERBI;
+               *status |= FE_HAS_SYNC;                
+               *status |= FE_HAS_LOCK;
+       }
+       return 0;
+}
+
+static int nxt2002_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+       struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+       u8 b[3];
+
+       nxt2002_readreg_multibyte(state,0xE6,b,3);        
+
+       *ber = ((b[0] << 8) + b[1]) * 8;
+       return 0;
+}
+
+static int nxt2002_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+       struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+       u8 b[2];
+       u16 temp = 0;
+
+       /* setup to read cluster variance */
+       b[0] = 0x00;
+       i2c_writebytes(state,0xA1,b,1);
+
+       /* get multreg val */
+       nxt2002_readreg_multibyte(state,0xA6,b,2);        
+
+       temp = (b[0] << 8) | b[1];
+       *strength = ((0x7FFF - temp) & 0x0FFF) * 16;
+
+       return 0;
+}
+
+static int nxt2002_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+
+       struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+       u8 b[2];
+       u16 temp = 0, temp2;
+       u32 snrdb = 0;
+
+       /* setup to read cluster variance */
+       b[0] = 0x00;
+       i2c_writebytes(state,0xA1,b,1);
+
+       /* get multreg val from 0xA6 */
+       nxt2002_readreg_multibyte(state,0xA6,b,2);        
+
+       temp = (b[0] << 8) | b[1];
+       temp2 = 0x7FFF - temp;
+
+       /* snr will be in db */
+       if (temp2 > 0x7F00)
+               snrdb = 1000*24 + ( 1000*(30-24) * ( temp2 - 0x7F00 ) / ( 0x7FFF - 0x7F00 ) );
+       else if (temp2 > 0x7EC0)
+               snrdb = 1000*18 + ( 1000*(24-18) * ( temp2 - 0x7EC0 ) / ( 0x7F00 - 0x7EC0 ) );
+       else if (temp2 > 0x7C00)
+               snrdb = 1000*12 + ( 1000*(18-12) * ( temp2 - 0x7C00 ) / ( 0x7EC0 - 0x7C00 ) );
+       else
+               snrdb = 1000*0 + ( 1000*(12-0) * ( temp2 - 0 ) / ( 0x7C00 - 0 ) );
+
+        /* the value reported back from the frontend will be FFFF=32db 0000=0db */
+
+       *snr = snrdb * (0xFFFF/32000);
+
+       return 0;
+}
+
+static int nxt2002_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+       struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+       u8 b[3];
+       nxt2002_readreg_multibyte(state,0xE6,b,3);
+
+       *ucblocks = b[2];
+
+       return 0;
+}
+
+static int nxt2002_sleep(struct dvb_frontend* fe)
+{
+       return 0;
+}
+
+static int nxt2002_init(struct dvb_frontend* fe)
+{
+       struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+       const struct firmware *fw;
+       int ret;
+       u8 buf[2];
+
+       if (!state->initialised) {
+               /* request the firmware, this will block until someone uploads it */
+               printk("nxt2002: Waiting for firmware upload...\n");
+               ret = state->config->request_firmware(fe, &fw, NXT2002_DEFAULT_FIRMWARE);
+               printk("nxt2002: Waiting for firmware upload(2)...\n");
+               if (ret) {
+                       printk("nxt2002: no firmware upload (timeout or file not found?)\n");
+                       return ret;
+               }
+
+               ret = nxt2002_load_firmware(fe, fw);
+               if (ret) {
+                       printk("nxt2002: writing firmware to device failed\n");
+                       release_firmware(fw);
+                       return ret;
+               }
+
+               /* Put the micro into reset */
+               nxt2002_microcontroller_stop(state);
+               /* ensure transfer is complete */
+               buf[0]=0;
+               i2c_writebytes(state,0x2B,buf,1);
+
+               /* Put the micro into reset for real this time */
+               nxt2002_microcontroller_stop(state);
+
+               /* soft reset everything (agc,frontend,eq,fec)*/
+               buf[0] = 0x0F;
+               i2c_writebytes(state,0x08,buf,1);
+               buf[0] = 0x00;
+               i2c_writebytes(state,0x08,buf,1);
+
+               /* write agc sdm configure */
+               buf[0] = 0xF1;          
+               i2c_writebytes(state,0x57,buf,1);
+
+               /* write mod output format */
+               buf[0] = 0x20;          
+               i2c_writebytes(state,0x09,buf,1);
+
+               /* write fec mpeg mode */
+               buf[0] = 0x7E;
+               buf[1] = 0x00;
+               i2c_writebytes(state,0xE9,buf,2);
+
+               /* write mux selection */
+               buf[0] = 0x00;
+               i2c_writebytes(state,0xCC,buf,1);
+
+               state->initialised = 1;
+       }
+
+       return 0;
+}
+
+static int nxt2002_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+       fesettings->min_delay_ms = 500;
+       fesettings->step_size = 0;
+       fesettings->max_drift = 0;
+       return 0;
+}
+
+static void nxt2002_release(struct dvb_frontend* fe)
+{
+       struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+       kfree(state);
+}
+
+static struct dvb_frontend_ops nxt2002_ops;
+
+struct dvb_frontend* nxt2002_attach(const struct nxt2002_config* config,
+                                  struct i2c_adapter* i2c)
+{
+       struct nxt2002_state* state = NULL;
+       u8 buf [] = {0,0,0,0,0};
+
+       /* allocate memory for the internal state */
+       state = (struct nxt2002_state*) kmalloc(sizeof(struct nxt2002_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &nxt2002_ops, sizeof(struct dvb_frontend_ops));
+       state->initialised = 0;
+
+        /* Check the first 5 registers to ensure this a revision we can handle */
+
+       i2c_readbytes(state, 0x00, buf, 5);
+       if (buf[0] != 0x04) goto error;                 /* device id */
+       if (buf[1] != 0x02) goto error;                 /* fab id */
+       if (buf[2] != 0x11) goto error;                 /* month */
+       if (buf[3] != 0x20) goto error;                 /* year msb */
+       if (buf[4] != 0x00) goto error;                 /* year lsb */
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops nxt2002_ops = {
+
+       .info = {
+               .name = "Nextwave nxt2002 VSB/QAM frontend",
+               .type = FE_ATSC,
+               .frequency_min =  54000000,
+               .frequency_max = 803000000,
+                /* stepsize is just a guess */
+               .frequency_stepsize = 166666,   
+               .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                       FE_CAN_8VSB 
+       },
+
+       .release = nxt2002_release,
+
+       .init = nxt2002_init,
+       .sleep = nxt2002_sleep,
+
+       .set_frontend = nxt2002_setup_frontend_parameters,
+       .get_tune_settings = nxt2002_get_tune_settings,
+
+       .read_status = nxt2002_read_status,
+       .read_ber = nxt2002_read_ber,
+       .read_signal_strength = nxt2002_read_signal_strength,
+       .read_snr = nxt2002_read_snr,
+       .read_ucblocks = nxt2002_read_ucblocks,
+
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("NXT2002 ATSC (8VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver");
+MODULE_AUTHOR("Taylor Jacob");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(nxt2002_attach);
diff --git a/drivers/media/dvb/frontends/nxt2002.h b/drivers/media/dvb/frontends/nxt2002.h
new file mode 100644 (file)
index 0000000..a177d5e
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+   Driver for the Nxt2002 demodulator
+*/
+
+#ifndef NXT2002_H
+#define NXT2002_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct nxt2002_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* request firmware for device */
+       int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* nxt2002_attach(const struct nxt2002_config* config,
+                                         struct i2c_adapter* i2c);
+
+#endif // NXT2002_H
diff --git a/drivers/media/dvb/frontends/nxt6000_priv.h b/drivers/media/dvb/frontends/nxt6000_priv.h
new file mode 100644 (file)
index 0000000..64b1a89
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Public Include File for DRV6000 users
+ * (ie. NxtWave Communications - NXT6000 demodulator driver)
+ *
+ * Copyright (C) 2001 NxtWave Communications, Inc.
+ *
+ */
+
+/*  Nxt6000 Register Addresses and Bit Masks */
+
+/* Maximum Register Number */
+#define MAXNXT6000REG          (0x9A)
+
+/* 0x1B A_VIT_BER_0  aka 0x3A */
+#define A_VIT_BER_0            (0x1B)
+
+/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */
+#define A_VIT_BER_TIMER_0      (0x1D)
+
+/* 0x21 RS_COR_STAT */
+#define RS_COR_STAT            (0x21)
+#define RSCORESTATUS           (0x03)
+
+/* 0x22 RS_COR_INTEN */
+#define RS_COR_INTEN           (0x22)
+
+/* 0x23 RS_COR_INSTAT */
+#define RS_COR_INSTAT          (0x23)
+#define INSTAT_ERROR           (0x04)
+#define LOCK_LOSS_BITS         (0x03)
+
+/* 0x24 RS_COR_SYNC_PARAM */
+#define RS_COR_SYNC_PARAM      (0x24)
+#define SYNC_PARAM             (0x03)
+
+/* 0x25 BER_CTRL */
+#define BER_CTRL               (0x25)
+#define BER_ENABLE             (0x02)
+#define BER_RESET              (0x01)
+
+/* 0x26 BER_PAY */
+#define BER_PAY                (0x26)
+
+/* 0x27 BER_PKT_L */
+#define BER_PKT_L              (0x27)
+#define BER_PKTOVERFLOW        (0x80)
+
+/* 0x30 VIT_COR_CTL */
+#define VIT_COR_CTL            (0x30)
+#define BER_CONTROL            (0x02)
+#define VIT_COR_MASK           (0x82)
+#define VIT_COR_RESYNC         (0x80)
+
+
+/* 0x32 VIT_SYNC_STATUS */
+#define VIT_SYNC_STATUS        (0x32)
+#define VITINSYNC              (0x80)
+
+/* 0x33 VIT_COR_INTEN */
+#define VIT_COR_INTEN          (0x33)
+#define GLOBAL_ENABLE          (0x80)
+
+/* 0x34 VIT_COR_INTSTAT */
+#define VIT_COR_INTSTAT        (0x34)
+#define BER_DONE               (0x08)
+#define BER_OVERFLOW           (0x10)
+
+                            /* 0x38 OFDM_BERTimer *//* Use the alias registers */
+#define A_VIT_BER_TIMER_0      (0x1D)
+
+                            /* 0x3A VIT_BER_TIMER_0 *//* Use the alias registers */
+#define A_VIT_BER_0            (0x1B)
+
+/* 0x40 OFDM_COR_CTL */
+#define OFDM_COR_CTL           (0x40)
+#define COREACT                (0x20)
+#define HOLDSM                 (0x10)
+#define WAIT_AGC               (0x02)
+#define WAIT_SYR               (0x03)
+
+/* 0x41 OFDM_COR_STAT */
+#define OFDM_COR_STAT          (0x41)
+#define COR_STATUS             (0x0F)
+#define MONITOR_TPS            (0x06)
+#define TPSLOCKED              (0x40)
+#define AGCLOCKED              (0x10)
+
+/* 0x42 OFDM_COR_INTEN */
+#define OFDM_COR_INTEN         (0x42)
+#define TPSRCVBAD              (0x04)
+#define TPSRCVCHANGED         (0x02)
+#define TPSRCVUPDATE           (0x01)
+
+/* 0x43 OFDM_COR_INSTAT */
+#define OFDM_COR_INSTAT        (0x43)
+
+/* 0x44 OFDM_COR_MODEGUARD */
+#define OFDM_COR_MODEGUARD     (0x44)
+#define FORCEMODE              (0x08)
+#define FORCEMODE8K                       (0x04)
+
+/* 0x45 OFDM_AGC_CTL */
+#define OFDM_AGC_CTL           (0x45)
+#define INITIAL_AGC_BW            (0x08)
+#define AGCNEG                 (0x02)
+#define AGCLAST                                   (0x10)
+
+/* 0x48 OFDM_AGC_TARGET */
+#define OFDM_AGC_TARGET                   (0x48)
+#define OFDM_AGC_TARGET_DEFAULT (0x28)
+#define OFDM_AGC_TARGET_IMPULSE (0x38)
+
+/* 0x49 OFDM_AGC_GAIN_1 */
+#define OFDM_AGC_GAIN_1        (0x49)
+
+/* 0x4B OFDM_ITB_CTL */
+#define OFDM_ITB_CTL           (0x4B)
+#define ITBINV                 (0x01)
+
+/* 0x4C OFDM_ITB_FREQ_1 */
+#define OFDM_ITB_FREQ_1        (0x4C)
+
+/* 0x4D OFDM_ITB_FREQ_2 */
+#define OFDM_ITB_FREQ_2        (0x4D)
+
+/* 0x4E  OFDM_CAS_CTL */
+#define OFDM_CAS_CTL           (0x4E)
+#define ACSDIS                 (0x40)
+#define CCSEN                  (0x80)
+
+/* 0x4F CAS_FREQ */
+#define CAS_FREQ               (0x4F)
+
+/* 0x51 OFDM_SYR_CTL */
+#define OFDM_SYR_CTL           (0x51)
+#define SIXTH_ENABLE           (0x80)
+#define SYR_TRACKING_DISABLE   (0x01)
+
+/* 0x52 OFDM_SYR_STAT */
+#define OFDM_SYR_STAT             (0x52)
+#define GI14_2K_SYR_LOCK          (0x13)
+#define GI14_8K_SYR_LOCK          (0x17)
+#define GI14_SYR_LOCK             (0x10)
+
+/* 0x55 OFDM_SYR_OFFSET_1 */
+#define OFDM_SYR_OFFSET_1      (0x55)
+
+/* 0x56 OFDM_SYR_OFFSET_2 */
+#define OFDM_SYR_OFFSET_2      (0x56)
+
+/* 0x58 OFDM_SCR_CTL */
+#define OFDM_SCR_CTL           (0x58)
+#define SYR_ADJ_DECAY_MASK     (0x70)
+#define SYR_ADJ_DECAY          (0x30)
+
+/* 0x59 OFDM_PPM_CTL_1 */
+#define OFDM_PPM_CTL_1         (0x59)
+#define PPMMAX_MASK            (0x30)
+#define PPM256                            (0x30)
+
+/* 0x5B OFDM_TRL_NOMINALRATE_1 */
+#define OFDM_TRL_NOMINALRATE_1 (0x5B)
+
+/* 0x5C OFDM_TRL_NOMINALRATE_2 */
+#define OFDM_TRL_NOMINALRATE_2 (0x5C)
+
+/* 0x5D OFDM_TRL_TIME_1 */
+#define OFDM_TRL_TIME_1        (0x5D)
+
+/* 0x60 OFDM_CRL_FREQ_1 */
+#define OFDM_CRL_FREQ_1        (0x60)
+
+/* 0x63 OFDM_CHC_CTL_1 */
+#define OFDM_CHC_CTL_1         (0x63)
+#define MANMEAN1               (0xF0);
+#define CHCFIR                 (0x01)
+
+/* 0x64 OFDM_CHC_SNR */
+#define OFDM_CHC_SNR           (0x64)
+
+/* 0x65 OFDM_BDI_CTL */
+#define OFDM_BDI_CTL           (0x65)
+#define LP_SELECT              (0x02)
+
+/* 0x67 OFDM_TPS_RCVD_1 */
+#define OFDM_TPS_RCVD_1        (0x67)
+#define TPSFRAME               (0x03)
+
+/* 0x68 OFDM_TPS_RCVD_2 */
+#define OFDM_TPS_RCVD_2        (0x68)
+
+/* 0x69 OFDM_TPS_RCVD_3 */
+#define OFDM_TPS_RCVD_3        (0x69)
+
+/* 0x6A OFDM_TPS_RCVD_4 */
+#define OFDM_TPS_RCVD_4        (0x6A)
+
+/* 0x6B OFDM_TPS_RESERVED_1 */
+#define OFDM_TPS_RESERVED_1    (0x6B)
+
+/* 0x6C OFDM_TPS_RESERVED_2 */
+#define OFDM_TPS_RESERVED_2    (0x6C)
+
+/* 0x73 OFDM_MSC_REV */
+#define OFDM_MSC_REV           (0x73)
+
+/* 0x76 OFDM_SNR_CARRIER_2 */
+#define OFDM_SNR_CARRIER_2     (0x76)
+#define MEAN_MASK              (0x80)
+#define MEANBIT                (0x80)
+
+/* 0x80 ANALOG_CONTROL_0 */
+#define ANALOG_CONTROL_0       (0x80)
+#define POWER_DOWN_ADC         (0x40)
+
+/* 0x81 ENABLE_TUNER_IIC */
+#define ENABLE_TUNER_IIC       (0x81)
+#define ENABLE_TUNER_BIT       (0x01)
+
+/* 0x82 EN_DMD_RACQ */
+#define EN_DMD_RACQ            (0x82)
+#define EN_DMD_RACQ_REG_VAL    (0x81)
+#define EN_DMD_RACQ_REG_VAL_14 (0x01)
+
+/* 0x84 SNR_COMMAND */
+#define SNR_COMMAND            (0x84)
+#define SNRStat                (0x80)
+
+/* 0x85 SNRCARRIERNUMBER_LSB */
+#define SNRCARRIERNUMBER_LSB   (0x85)
+
+/* 0x87 SNRMINTHRESHOLD_LSB */
+#define SNRMINTHRESHOLD_LSB    (0x87)
+
+/* 0x89 SNR_PER_CARRIER_LSB */
+#define SNR_PER_CARRIER_LSB    (0x89)
+
+/* 0x8B SNRBELOWTHRESHOLD_LSB */
+#define SNRBELOWTHRESHOLD_LSB  (0x8B)
+
+/* 0x91 RF_AGC_VAL_1 */
+#define RF_AGC_VAL_1           (0x91)
+
+/* 0x92 RF_AGC_STATUS */
+#define RF_AGC_STATUS          (0x92)
+
+/* 0x98 DIAG_CONFIG */
+#define DIAG_CONFIG            (0x98)
+#define DIAG_MASK              (0x70)
+#define TB_SET                 (0x10)
+#define TRAN_SELECT            (0x07)
+#define SERIAL_SELECT          (0x01)
+
+/* 0x99 SUB_DIAG_MODE_SEL */
+#define SUB_DIAG_MODE_SEL      (0x99)
+#define CLKINVERSION           (0x01)
+
+/* 0x9A TS_FORMAT */
+#define TS_FORMAT              (0x9A)
+#define ERROR_SENSE            (0x08)
+#define VALID_SENSE            (0x04)
+#define SYNC_SENSE             (0x02)
+#define GATED_CLOCK            (0x01)
+
+#define NXT6000ASICDEVICE      (0x0b)
diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb/frontends/sp8870.c
new file mode 100644 (file)
index 0000000..4a8178d
--- /dev/null
@@ -0,0 +1,613 @@
+/*
+    Driver for Spase SP8870 demodulator
+
+    Copyright (C) 1999 Juergen Peitz
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware alps_tdlb7" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "sp8870.h"
+
+
+struct sp8870_state {
+
+       struct i2c_adapter* i2c;
+
+       struct dvb_frontend_ops ops;
+
+       const struct sp8870_config* config;
+
+       struct dvb_frontend frontend;
+
+       /* demodulator private data */
+       u8 initialised:1;
+};
+
+static int debug;
+#define dprintk(args...) \
+       do { \
+               if (debug) printk(KERN_DEBUG "sp8870: " args); \
+       } while (0)
+
+/* firmware size for sp8870 */
+#define SP8870_FIRMWARE_SIZE 16382
+
+/* starting point for firmware in file 'Sc_main.mc' */
+#define SP8870_FIRMWARE_OFFSET 0x0A
+
+static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data)
+{
+        u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff };
+       struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 };
+       int err;
+
+        if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+               dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data);
+               return -EREMOTEIO;
+       }
+
+        return 0;
+}
+
+static int sp8870_readreg (struct sp8870_state* state, u16 reg)
+{
+       int ret;
+       u8 b0 [] = { reg >> 8 , reg & 0xff };
+       u8 b1 [] = { 0, 0 };
+       struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
+                          { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
+
+       ret = i2c_transfer (state->i2c, msg, 2);
+
+       if (ret != 2) {
+               dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+               return -1;
+       }
+
+       return (b1[0] << 8 | b1[1]);
+}
+
+static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw)
+{
+       struct i2c_msg msg;
+       char *fw_buf = fw->data;
+       int fw_pos;
+       u8 tx_buf[255];
+       int tx_len;
+       int err = 0;
+
+       dprintk ("%s: ...\n", __FUNCTION__);
+
+       if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET)
+               return -EINVAL;
+
+       // system controller stop
+       sp8870_writereg(state, 0x0F00, 0x0000);
+
+       // instruction RAM register hiword
+       sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF));
+
+       // instruction RAM MWR
+       sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16));
+
+       // do firmware upload
+       fw_pos = SP8870_FIRMWARE_OFFSET;
+       while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){
+               tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos;
+               // write register 0xCF0A
+               tx_buf[0] = 0xCF;
+               tx_buf[1] = 0x0A;
+               memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len);
+               msg.addr = state->config->demod_address;
+               msg.flags = 0;
+               msg.buf = tx_buf;
+               msg.len = tx_len + 2;
+               if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+                       printk("%s: firmware upload failed!\n", __FUNCTION__);
+                       printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
+                       return err;
+               }
+               fw_pos += tx_len;
+       }
+
+       dprintk ("%s: done!\n", __FUNCTION__);
+       return 0;
+};
+
+static void sp8870_microcontroller_stop (struct sp8870_state* state)
+{
+       sp8870_writereg(state, 0x0F08, 0x000);
+       sp8870_writereg(state, 0x0F09, 0x000);
+
+       // microcontroller STOP
+       sp8870_writereg(state, 0x0F00, 0x000);
+}
+
+static void sp8870_microcontroller_start (struct sp8870_state* state)
+{
+       sp8870_writereg(state, 0x0F08, 0x000);
+       sp8870_writereg(state, 0x0F09, 0x000);
+
+       // microcontroller START
+       sp8870_writereg(state, 0x0F00, 0x001);
+       // not documented but if we don't read 0x0D01 out here
+       // we don't get a correct data valid signal
+       sp8870_readreg(state, 0x0D01);
+}
+
+static int sp8870_read_data_valid_signal(struct sp8870_state* state)
+{
+       return (sp8870_readreg(state, 0x0D02) > 0);
+}
+
+static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
+{
+       int known_parameters = 1;
+
+       *reg0xc05 = 0x000;
+
+       switch (p->u.ofdm.constellation) {
+       case QPSK:
+               break;
+       case QAM_16:
+               *reg0xc05 |= (1 << 10);
+               break;
+       case QAM_64:
+               *reg0xc05 |= (2 << 10);
+               break;
+       case QAM_AUTO:
+               known_parameters = 0;
+               break;
+       default:
+               return -EINVAL;
+       };
+
+       switch (p->u.ofdm.hierarchy_information) {
+       case HIERARCHY_NONE:
+               break;
+       case HIERARCHY_1:
+               *reg0xc05 |= (1 << 7);
+               break;
+       case HIERARCHY_2:
+               *reg0xc05 |= (2 << 7);
+               break;
+       case HIERARCHY_4:
+               *reg0xc05 |= (3 << 7);
+               break;
+       case HIERARCHY_AUTO:
+               known_parameters = 0;
+               break;
+       default:
+               return -EINVAL;
+       };
+
+       switch (p->u.ofdm.code_rate_HP) {
+       case FEC_1_2:
+               break;
+       case FEC_2_3:
+               *reg0xc05 |= (1 << 3);
+               break;
+       case FEC_3_4:
+               *reg0xc05 |= (2 << 3);
+               break;
+       case FEC_5_6:
+               *reg0xc05 |= (3 << 3);
+               break;
+       case FEC_7_8:
+               *reg0xc05 |= (4 << 3);
+               break;
+       case FEC_AUTO:
+               known_parameters = 0;
+               break;
+       default:
+               return -EINVAL;
+       };
+
+       if (known_parameters)
+               *reg0xc05 |= (2 << 1);  /* use specified parameters */
+       else
+               *reg0xc05 |= (1 << 1);  /* enable autoprobing */
+
+       return 0;
+}
+
+static int sp8870_wake_up(struct sp8870_state* state)
+{
+       // enable TS output and interface pins
+       return sp8870_writereg(state, 0xC18, 0x00D);
+}
+
+static int sp8870_set_frontend_parameters (struct dvb_frontend* fe,
+                                          struct dvb_frontend_parameters *p)
+{
+       struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+       int  err;
+       u16 reg0xc05;
+
+       if ((err = configure_reg0xc05(p, &reg0xc05)))
+               return err;
+
+       // system controller stop
+       sp8870_microcontroller_stop(state);
+
+       // set tuner parameters
+       sp8870_writereg(state, 0x206, 0x001);
+       state->config->pll_set(fe, p);
+       sp8870_writereg(state, 0x206, 0x000);
+
+       // sample rate correction bit [23..17]
+       sp8870_writereg(state, 0x0319, 0x000A);
+
+       // sample rate correction bit [16..0]
+       sp8870_writereg(state, 0x031A, 0x0AAB);
+
+       // integer carrier offset
+       sp8870_writereg(state, 0x0309, 0x0400);
+
+       // fractional carrier offset
+       sp8870_writereg(state, 0x030A, 0x0000);
+
+       // filter for 6/7/8 Mhz channel
+       if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+               sp8870_writereg(state, 0x0311, 0x0002);
+       else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+               sp8870_writereg(state, 0x0311, 0x0001);
+       else
+               sp8870_writereg(state, 0x0311, 0x0000);
+
+       // scan order: 2k first = 0x0000, 8k first = 0x0001
+       if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K)
+               sp8870_writereg(state, 0x0338, 0x0000);
+       else
+               sp8870_writereg(state, 0x0338, 0x0001);
+
+       sp8870_writereg(state, 0xc05, reg0xc05);
+
+       // read status reg in order to clear pending irqs
+       sp8870_readreg(state, 0x200);
+
+       // system controller start
+       sp8870_microcontroller_start(state);
+
+       return 0;
+}
+
+static int sp8870_init (struct dvb_frontend* fe)
+{
+       struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+        const struct firmware *fw = NULL;
+
+       sp8870_wake_up(state);
+       if (state->initialised) return 0;
+       state->initialised = 1;
+
+       dprintk ("%s\n", __FUNCTION__);
+
+
+       /* request the firmware, this will block until someone uploads it */
+       printk("sp8870: waiting for firmware upload...\n");
+       if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) {
+               printk("sp8870: no firmware upload (timeout or file not found?)\n");
+               release_firmware(fw);
+               return -EIO;
+       }
+
+       if (sp8870_firmware_upload(state, fw)) {
+               printk("sp8870: writing firmware to device failed\n");
+               release_firmware(fw);
+               return -EIO;
+       }
+
+       /* enable TS output and interface pins */
+       sp8870_writereg(state, 0xc18, 0x00d);
+
+       // system controller stop
+       sp8870_microcontroller_stop(state);
+
+       // ADC mode
+       sp8870_writereg(state, 0x0301, 0x0003);
+
+       // Reed Solomon parity bytes passed to output
+       sp8870_writereg(state, 0x0C13, 0x0001);
+
+       // MPEG clock is suppressed if no valid data
+       sp8870_writereg(state, 0x0C14, 0x0001);
+
+       /* bit 0x010: enable data valid signal */
+       sp8870_writereg(state, 0x0D00, 0x010);
+       sp8870_writereg(state, 0x0D01, 0x000);
+
+       /* setup PLL */
+       if (state->config->pll_init) {
+               sp8870_writereg(state, 0x206, 0x001);
+               state->config->pll_init(fe);
+               sp8870_writereg(state, 0x206, 0x000);
+       }
+
+       return 0;
+}
+
+static int sp8870_read_status (struct dvb_frontend* fe, fe_status_t * fe_status)
+{
+       struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+       int status;
+       int signal;
+
+       *fe_status = 0;
+
+       status = sp8870_readreg (state, 0x0200);
+       if (status < 0)
+               return -EIO;
+
+       signal = sp8870_readreg (state, 0x0303);
+       if (signal < 0)
+               return -EIO;
+
+       if (signal > 0x0F)
+               *fe_status |= FE_HAS_SIGNAL;
+       if (status & 0x08)
+               *fe_status |= FE_HAS_SYNC;
+       if (status & 0x04)
+               *fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI;
+
+       return 0;
+}
+
+static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber)
+{
+       struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+       int ret;
+       u32 tmp;
+
+       *ber = 0;
+
+       ret = sp8870_readreg(state, 0xC08);
+       if (ret < 0)
+               return -EIO;
+
+       tmp = ret & 0x3F;
+
+       ret = sp8870_readreg(state, 0xC07);
+       if (ret < 0)
+               return -EIO;
+
+        tmp = ret << 6;
+
+       if (tmp >= 0x3FFF0)
+               tmp = ~0;
+
+       *ber = tmp;
+
+       return 0;
+}
+
+static int sp8870_read_signal_strength(struct dvb_frontend* fe,  u16 * signal)
+{
+       struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+       int ret;
+       u16 tmp;
+
+       *signal = 0;
+
+       ret = sp8870_readreg (state, 0x306);
+       if (ret < 0)
+               return -EIO;
+
+       tmp = ret << 8;
+
+       ret = sp8870_readreg (state, 0x303);
+       if (ret < 0)
+               return -EIO;
+
+       tmp |= ret;
+
+       if (tmp)
+               *signal = 0xFFFF - tmp;
+
+       return 0;
+}
+
+static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks)
+{
+       struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+       int ret;
+
+       *ublocks = 0;
+
+       ret = sp8870_readreg(state, 0xC0C);
+       if (ret < 0)
+               return -EIO;
+
+       if (ret == 0xFFFF)
+               ret = ~0;
+
+       *ublocks = ret;
+
+       return 0;
+}
+
+// number of trials to recover from lockup
+#define MAXTRIALS 5
+// maximum checks for data valid signal
+#define MAXCHECKS 100
+
+// only for debugging: counter for detected lockups
+static int lockups = 0;
+// only for debugging: counter for channel switches
+static int switches = 0;
+
+static int sp8870_set_frontend (struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+
+       /*
+           The firmware of the sp8870 sometimes locks up after setting frontend parameters.
+           We try to detect this by checking the data valid signal.
+           If it is not set after MAXCHECKS we try to recover the lockup by setting
+           the frontend parameters again.
+       */
+
+       int err = 0;
+       int valid = 0;
+       int trials = 0;
+       int check_count = 0;
+
+       dprintk("%s: frequency = %i\n", __FUNCTION__, p->frequency);
+
+       for (trials = 1; trials <= MAXTRIALS; trials++) {
+
+               if ((err = sp8870_set_frontend_parameters(fe, p)))
+                       return err;
+
+               for (check_count = 0; check_count < MAXCHECKS; check_count++) {
+//                     valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0);
+                       valid = sp8870_read_data_valid_signal(state);
+                       if (valid) {
+                               dprintk("%s: delay = %i usec\n",
+                                       __FUNCTION__, check_count * 10);
+                               break;
+                       }
+                       udelay(10);
+               }
+               if (valid)
+                       break;
+       }
+
+       if (!valid) {
+               printk("%s: firmware crash!!!!!!\n", __FUNCTION__);
+               return -EIO;
+       }
+
+       if (debug) {
+               if (valid) {
+                       if (trials > 1) {
+                               printk("%s: firmware lockup!!!\n", __FUNCTION__);
+                               printk("%s: recovered after %i trial(s))\n",  __FUNCTION__, trials - 1);
+                               lockups++;
+                       }
+               }
+               switches++;
+               printk("%s: switches = %i lockups = %i\n", __FUNCTION__, switches, lockups);
+       }
+
+       return 0;
+}
+
+static int sp8870_sleep(struct dvb_frontend* fe)
+{
+       struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+
+       // tristate TS output and disable interface pins
+       return sp8870_writereg(state, 0xC18, 0x000);
+}
+
+static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        fesettings->min_delay_ms = 350;
+        fesettings->step_size = 0;
+        fesettings->max_drift = 0;
+        return 0;
+}
+
+static void sp8870_release(struct dvb_frontend* fe)
+{
+       struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+       kfree(state);
+}
+
+static struct dvb_frontend_ops sp8870_ops;
+
+struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
+                                  struct i2c_adapter* i2c)
+{
+       struct sp8870_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct sp8870_state*) kmalloc(sizeof(struct sp8870_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &sp8870_ops, sizeof(struct dvb_frontend_ops));
+       state->initialised = 0;
+
+       /* check if the demod is there */
+       if (sp8870_readreg(state, 0x0200) < 0) goto error;
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops sp8870_ops = {
+
+       .info = {
+               .name                   = "Spase SP8870 DVB-T",
+               .type                   = FE_OFDM,
+               .frequency_min          = 470000000,
+               .frequency_max          = 860000000,
+               .frequency_stepsize     = 166666,
+               .caps                   = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
+                                         FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 |
+                                         FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                                         FE_CAN_QPSK | FE_CAN_QAM_16 |
+                                         FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+                                         FE_CAN_HIERARCHY_AUTO |  FE_CAN_RECOVER
+       },
+
+       .release = sp8870_release,
+
+       .init = sp8870_init,
+       .sleep = sp8870_sleep,
+
+       .set_frontend = sp8870_set_frontend,
+       .get_tune_settings = sp8870_get_tune_settings,
+
+       .read_status = sp8870_read_status,
+       .read_ber = sp8870_read_ber,
+       .read_signal_strength = sp8870_read_signal_strength,
+       .read_ucblocks = sp8870_read_uncorrected_blocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver");
+MODULE_AUTHOR("Juergen Peitz");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sp8870_attach);
diff --git a/drivers/media/dvb/frontends/sp8870.h b/drivers/media/dvb/frontends/sp8870.h
new file mode 100644 (file)
index 0000000..f3b555d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    Driver for Spase SP8870 demodulator
+
+    Copyright (C) 1999 Juergen Peitz
+
+    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 SP8870_H
+#define SP8870_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct sp8870_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+       /* request firmware for device */
+       int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
+                                         struct i2c_adapter* i2c);
+
+#endif // SP8870_H
diff --git a/drivers/media/dvb/frontends/sp887x.h b/drivers/media/dvb/frontends/sp887x.h
new file mode 100644 (file)
index 0000000..6a05d8f
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+   Driver for the Spase sp887x demodulator
+*/
+
+#ifndef SP887X_H
+#define SP887X_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct sp887x_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+
+       /* this should return the actual frequency tuned to */
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+       /* request firmware for device */
+       int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
+                                         struct i2c_adapter* i2c);
+
+#endif // SP887X_H
diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c
new file mode 100644 (file)
index 0000000..67dc53f
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+    Driver for STV0297 demodulator
+
+    Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+    Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de>
+
+    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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "stv0297.h"
+
+struct stv0297_state {
+
+        struct i2c_adapter* i2c;
+
+        struct dvb_frontend_ops ops;
+
+        const struct stv0297_config* config;
+
+        struct dvb_frontend frontend;
+
+        int freq_off;
+
+       unsigned long base_freq;
+
+       u8 pwm;
+};
+
+#if 1
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...)
+#endif
+
+#define STV0297_CLOCK_KHZ   28900
+
+static u8 init_tab [] = {
+  0x00, 0x09,
+  0x01, 0x69,
+  0x03, 0x00,
+  0x04, 0x00,
+  0x07, 0x00,
+  0x08, 0x00,
+  0x20, 0x00,
+  0x21, 0x40,
+  0x22, 0x00,
+  0x23, 0x00,
+  0x24, 0x40,
+  0x25, 0x88,
+  0x30, 0xff,
+  0x31, 0x00,
+  0x32, 0xff,
+  0x33, 0x00,
+  0x34, 0x50,
+  0x35, 0x7f,
+  0x36, 0x00,
+  0x37, 0x20,
+  0x38, 0x00,
+  0x40, 0x1c,
+  0x41, 0xff,
+  0x42, 0x29,
+       0x43, 0x00,
+  0x44, 0xff,
+  0x45, 0x00,
+  0x46, 0x00,
+  0x49, 0x04,
+  0x4a, 0xff,
+  0x4b, 0x7f,
+  0x52, 0x30,
+  0x55, 0xae,
+  0x56, 0x47,
+  0x57, 0xe1,
+  0x58, 0x3a,
+  0x5a, 0x1e,
+  0x5b, 0x34,
+  0x60, 0x00,
+  0x63, 0x00,
+  0x64, 0x00,
+  0x65, 0x00,
+  0x66, 0x00,
+  0x67, 0x00,
+  0x68, 0x00,
+  0x69, 0x00,
+  0x6a, 0x02,
+  0x6b, 0x00,
+  0x70, 0xff,
+  0x71, 0x00,
+  0x72, 0x00,
+  0x73, 0x00,
+  0x74, 0x0c,
+  0x80, 0x00,
+  0x81, 0x00,
+  0x82, 0x00,
+  0x83, 0x00,
+  0x84, 0x04,
+  0x85, 0x80,
+  0x86, 0x24,
+  0x87, 0x78,
+  0x88, 0x00,
+  0x89, 0x00,
+  0x90, 0x01,
+  0x91, 0x01,
+  0xa0, 0x00,
+  0xa1, 0x00,
+  0xa2, 0x00,
+  0xb0, 0x91,
+  0xb1, 0x0b,
+  0xc0, 0x53,
+  0xc1, 0x70,
+  0xc2, 0x12,
+  0xd0, 0x00,
+  0xd1, 0x00,
+  0xd2, 0x00,
+  0xd3, 0x00,
+  0xd4, 0x00,
+  0xd5, 0x00,
+  0xde, 0x00,
+  0xdf, 0x00,
+  0x61, 0x49,
+  0x62, 0x0b,
+  0x53, 0x08,
+  0x59, 0x08,
+};
+
+
+static int stv0297_writereg (struct stv0297_state* state, u8 reg, u8 data)
+{
+        int ret;
+        u8 buf [] = { reg, data };
+        struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+        ret = i2c_transfer (state->i2c, &msg, 1);
+
+        if (ret != 1)
+                dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+                  "ret == %i)\n", __FUNCTION__, reg, data, ret);
+
+        return (ret != 1) ? -1 : 0;
+}
+
+static int stv0297_readreg (struct stv0297_state* state, u8 reg)
+{
+        int ret;
+        u8 b0[] = { reg };
+        u8 b1[] = { 0 };
+        struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+                                  { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+        // this device needs a STOP between the register and data
+        if ((ret = i2c_transfer (state->i2c, &msg[0], 1)) != 1) {
+               dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret);
+                return -1;
+        }
+        if ((ret = i2c_transfer (state->i2c, &msg[1], 1)) != 1) {
+               dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret);
+                return -1;
+        }
+
+        return b1[0];
+}
+
+static int stv0297_writereg_mask (struct stv0297_state* state, u8 reg, u8 mask, u8 data)
+{
+        int val;
+
+        val = stv0297_readreg(state, reg);
+        val &= ~mask;
+        val |= (data & mask);
+        stv0297_writereg(state, reg, val);
+
+        return 0;
+}
+
+static int stv0297_readregs (struct stv0297_state* state, u8 reg1, u8 *b, u8 len)
+{
+        int ret;
+        struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg1, .len = 1 },
+                                  { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } };
+
+        // this device needs a STOP between the register and data
+        if ((ret = i2c_transfer (state->i2c, &msg[0], 1)) != 1) {
+               dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret);
+                return -1;
+        }
+        if ((ret = i2c_transfer (state->i2c, &msg[1], 1)) != 1) {
+               dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret);
+                return -1;
+        }
+
+        return 0;
+}
+
+static void stv0297_set_symbolrate(struct stv0297_state *state, u32 srate)
+{
+       long tmp;
+
+       tmp = 131072L * srate;  /* 131072 = 2^17  */
+       tmp = tmp / (STV0297_CLOCK_KHZ / 4);    /* 1/4 = 2^-2 */
+       tmp = tmp * 8192L;      /* 8192 = 2^13 */
+
+        stv0297_writereg (state, 0x55,(unsigned char)(tmp & 0xFF));
+        stv0297_writereg (state, 0x56,(unsigned char)(tmp>> 8));
+        stv0297_writereg (state, 0x57,(unsigned char)(tmp>>16));
+        stv0297_writereg (state, 0x58,(unsigned char)(tmp>>24));
+}
+
+static void stv0297_set_sweeprate(struct stv0297_state *state, short fshift, long symrate)
+{
+       long tmp;
+
+       tmp = (long) fshift *262144L;   /* 262144 = 2*18 */
+       tmp /= symrate;
+       tmp *= 1024;            /* 1024 = 2*10   */
+
+        // adjust
+        if (tmp >= 0) {
+                tmp += 500000;
+        } else {
+                tmp -= 500000;
+        }
+       tmp /= 1000000;
+
+        stv0297_writereg(state, 0x60, tmp & 0xFF);
+        stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0);
+}
+
+static void stv0297_set_carrieroffset(struct stv0297_state* state, long offset)
+{
+       long tmp;
+
+       /* symrate is hardcoded to 10000 */
+       tmp = offset * 26844L;  /* (2**28)/10000 */
+       if (tmp < 0)
+               tmp += 0x10000000;
+       tmp &= 0x0FFFFFFF;
+
+       stv0297_writereg(state, 0x66, (unsigned char) (tmp & 0xFF));
+       stv0297_writereg(state, 0x67, (unsigned char) (tmp >> 8));
+       stv0297_writereg(state, 0x68, (unsigned char) (tmp >> 16));
+       stv0297_writereg_mask(state, 0x69, 0x0F, (tmp >> 24) & 0x0f);
+}
+
+static long stv0297_get_carrieroffset(struct stv0297_state* state)
+{
+        s32 raw;
+       long tmp;
+
+        stv0297_writereg(state,0x6B, 0x00);
+
+        raw =   stv0297_readreg(state,0x66);
+        raw |= (stv0297_readreg(state,0x67) << 8);
+        raw |= (stv0297_readreg(state,0x68) << 16);
+        raw |= (stv0297_readreg(state,0x69) & 0x0F) << 24;
+
+        tmp = raw;
+       tmp /= 26844L;
+
+       return tmp;
+}
+
+static void stv0297_set_initialdemodfreq(struct stv0297_state* state, long freq)
+{
+/*
+        s64 tmp;
+
+        if (freq > 10000) freq -= STV0297_CLOCK_KHZ;
+
+        tmp = freq << 16;
+        do_div(tmp, STV0297_CLOCK_KHZ);
+        if (tmp > 0xffff) tmp = 0xffff; // check this calculation
+
+        stv0297_writereg_mask(state, 0x25, 0x80, 0x80);
+        stv0297_writereg(state, 0x21, tmp >> 8);
+        stv0297_writereg(state, 0x20, tmp);
+*/
+}
+
+static int stv0297_set_qam(struct stv0297_state* state, fe_modulation_t modulation)
+{
+        int val = 0;
+
+        switch(modulation) {
+        case QAM_16:
+                val = 0;
+                break;
+
+        case QAM_32:
+                val = 1;
+                break;
+
+        case QAM_64:
+                val = 4;
+                break;
+
+        case QAM_128:
+                val = 2;
+                break;
+
+        case QAM_256:
+                val = 3;
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        stv0297_writereg_mask(state, 0x00, 0x70, val << 4);
+
+        return 0;
+}
+
+static int stv0297_set_inversion(struct stv0297_state* state, fe_spectral_inversion_t inversion)
+{
+        int val = 0;
+
+        switch(inversion) {
+        case INVERSION_OFF:
+                val = 0;
+                break;
+
+        case INVERSION_ON:
+                val = 1;
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        stv0297_writereg_mask(state, 0x83, 0x08, val << 3);
+
+        return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+int stv0297_enable_plli2c(struct dvb_frontend* fe)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+
+        stv0297_writereg(state, 0x87, 0x78);
+        stv0297_writereg(state, 0x86, 0xc8);
+
+        return 0;
+}
+
+static int stv0297_init (struct dvb_frontend* fe)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        int i;
+
+       /* soft reset */
+        stv0297_writereg_mask(state, 0x80, 1, 1);
+        stv0297_writereg_mask(state, 0x80, 1, 0);
+
+       /* reset deinterleaver */
+        stv0297_writereg_mask(state, 0x81, 1, 1);
+        stv0297_writereg_mask(state, 0x81, 1, 0);
+
+       /* load init table */
+        for (i=0; i<sizeof(init_tab); i+=2) {
+                stv0297_writereg (state, init_tab[i], init_tab[i+1]);
+        }
+
+       /* set a dummy symbol rate */
+        stv0297_set_symbolrate(state, 6900);
+
+       /* invert AGC1 polarity */
+        stv0297_writereg_mask(state, 0x88, 0x10, 0x10);
+
+       /* setup bit error counting */
+        stv0297_writereg_mask(state, 0xA0, 0x80, 0x00);
+        stv0297_writereg_mask(state, 0xA0, 0x10, 0x00);
+        stv0297_writereg_mask(state, 0xA0, 0x08, 0x00);
+        stv0297_writereg_mask(state, 0xA0, 0x07, 0x04);
+
+       /* min + max PWM */
+        stv0297_writereg(state, 0x4a, 0x00);
+        stv0297_writereg(state, 0x4b, state->pwm);
+        msleep(200);
+
+       if (state->config->pll_init)
+               state->config->pll_init(fe);
+
+        return 0;
+}
+
+static int stv0297_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+
+        u8 sync = stv0297_readreg (state, 0xDF);
+
+        *status = 0;
+        if (sync & 0x80)
+               *status |=
+                       FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK;
+        return 0;
+}
+
+static int stv0297_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        u8 BER[3];
+
+        stv0297_writereg (state, 0xA0, 0x80);  // Start Counting bit errors for 4096 Bytes
+        mdelay(25);                            // Hopefully got 4096 Bytes
+        stv0297_readregs (state, 0xA0, BER, 3);
+        mdelay(25);
+        *ber = (BER[2] << 8 | BER[1]) / ( 8 * 4096);
+
+        return 0;
+}
+
+
+static int stv0297_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        u8 STRENGTH[2];
+
+        stv0297_readregs (state, 0x41, STRENGTH, 2);
+        *strength = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0];
+
+        return 0;
+}
+
+static int stv0297_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        u8 SNR[2];
+
+        stv0297_readregs (state, 0x07, SNR, 2);
+        *snr = SNR[1] << 8 | SNR[0];
+
+        return 0;
+}
+
+static int stv0297_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+
+        *ucblocks = (stv0297_readreg (state, 0xD5) << 8)
+                   | stv0297_readreg (state, 0xD4);
+
+        return 0;
+}
+
+static int stv0297_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        int u_threshold;
+        int initial_u;
+        int blind_u;
+        int delay;
+        int sweeprate;
+        int carrieroffset;
+        unsigned long starttime;
+        unsigned long timeout;
+
+        switch(p->u.qam.modulation) {
+        case QAM_16:
+        case QAM_32:
+        case QAM_64:
+          delay = 100;
+          sweeprate = 1500;
+          break;
+
+        case QAM_128:
+          delay = 150;
+          sweeprate = 1000;
+          break;
+
+        case QAM_256:
+          delay = 200;
+          sweeprate = 500;
+          break;
+
+        default:
+          return -EINVAL;
+        }
+
+        // determine inversion dependant parameters
+        carrieroffset = -330;
+        switch(p->inversion) {
+        case INVERSION_OFF:
+          break;
+
+        case INVERSION_ON:
+          sweeprate = -sweeprate;
+          carrieroffset = -carrieroffset;
+          break;
+
+        default:
+          return -EINVAL;
+        }
+
+        state->config->pll_set(fe, p);
+
+       /* clear software interrupts */
+       stv0297_writereg(state, 0x82, 0x0);
+
+       /* set initial demodulation frequency */
+        stv0297_set_initialdemodfreq(state, state->freq_off + 7250);
+
+       /* setup AGC */
+        stv0297_writereg_mask(state, 0x43, 0x10, 0x00);
+        stv0297_writereg(state, 0x41, 0x00);
+        stv0297_writereg_mask(state, 0x42, 0x03, 0x01);
+        stv0297_writereg_mask(state, 0x36, 0x60, 0x00);
+        stv0297_writereg_mask(state, 0x36, 0x18, 0x00);
+        stv0297_writereg_mask(state, 0x71, 0x80, 0x80);
+        stv0297_writereg(state, 0x72, 0x00);
+        stv0297_writereg(state, 0x73, 0x00);
+        stv0297_writereg_mask(state, 0x74, 0x0F, 0x00);
+        stv0297_writereg_mask(state, 0x43, 0x08, 0x00);
+        stv0297_writereg_mask(state, 0x71, 0x80, 0x00);
+
+       /* setup STL */
+        stv0297_writereg_mask(state, 0x5a, 0x20, 0x20);
+        stv0297_writereg_mask(state, 0x5b, 0x02, 0x02);
+        stv0297_writereg_mask(state, 0x5b, 0x02, 0x00);
+        stv0297_writereg_mask(state, 0x5b, 0x01, 0x00);
+        stv0297_writereg_mask(state, 0x5a, 0x40, 0x40);
+
+       /* disable frequency sweep */
+        stv0297_writereg_mask(state, 0x6a, 0x01, 0x00);
+
+       /* reset deinterleaver */
+        stv0297_writereg_mask(state, 0x81, 0x01, 0x01);
+        stv0297_writereg_mask(state, 0x81, 0x01, 0x00);
+
+       /* ??? */
+        stv0297_writereg_mask(state, 0x83, 0x20, 0x20);
+        stv0297_writereg_mask(state, 0x83, 0x20, 0x00);
+
+       /* reset equaliser */
+        u_threshold = stv0297_readreg(state, 0x00) & 0xf;
+        initial_u = stv0297_readreg(state, 0x01) >> 4;
+        blind_u = stv0297_readreg(state, 0x01) & 0xf;
+        stv0297_writereg_mask(state, 0x84, 0x01, 0x01);
+        stv0297_writereg_mask(state, 0x84, 0x01, 0x00);
+        stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold);
+        stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4);
+        stv0297_writereg_mask(state, 0x01, 0x0f, blind_u);
+
+       /* data comes from internal A/D */
+        stv0297_writereg_mask(state, 0x87, 0x80, 0x00);
+
+       /* clear phase registers */
+        stv0297_writereg(state, 0x63, 0x00);
+        stv0297_writereg(state, 0x64, 0x00);
+        stv0297_writereg(state, 0x65, 0x00);
+        stv0297_writereg(state, 0x66, 0x00);
+        stv0297_writereg(state, 0x67, 0x00);
+        stv0297_writereg(state, 0x68, 0x00);
+        stv0297_writereg_mask(state, 0x69, 0x0f, 0x00);
+
+       /* set parameters */
+        stv0297_set_qam(state, p->u.qam.modulation);
+        stv0297_set_symbolrate(state, p->u.qam.symbol_rate/1000);
+       stv0297_set_sweeprate(state, sweeprate, p->u.qam.symbol_rate / 1000);
+        stv0297_set_carrieroffset(state, carrieroffset);
+        stv0297_set_inversion(state, p->inversion);
+
+       /* kick off lock */
+        stv0297_writereg_mask(state, 0x88, 0x08, 0x08);
+        stv0297_writereg_mask(state, 0x5a, 0x20, 0x00);
+        stv0297_writereg_mask(state, 0x6a, 0x01, 0x01);
+        stv0297_writereg_mask(state, 0x43, 0x40, 0x40);
+        stv0297_writereg_mask(state, 0x5b, 0x30, 0x00);
+        stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c);
+        stv0297_writereg_mask(state, 0x03, 0x03, 0x03);
+        stv0297_writereg_mask(state, 0x43, 0x10, 0x10);
+
+       /* wait for WGAGC lock */
+        starttime = jiffies;
+        timeout = jiffies + (200*HZ)/1000;
+        while(time_before(jiffies, timeout)) {
+                msleep(10);
+               if (stv0297_readreg(state, 0x43) & 0x08)
+                       break;
+        }
+        if (time_after(jiffies, timeout)) {
+                goto timeout;
+        }
+        msleep(20);
+
+       /* wait for equaliser partial convergence */
+        timeout = jiffies + (50*HZ)/1000;
+        while(time_before(jiffies, timeout)) {
+                msleep(10);
+
+                if (stv0297_readreg(state, 0x82) & 0x04) {
+                       break;
+                }
+        }
+       if (time_after(jiffies, timeout)) {
+                goto timeout;
+        }
+
+       /* wait for equaliser full convergence */
+        timeout = jiffies + (delay*HZ)/1000;
+        while(time_before(jiffies, timeout)) {
+                msleep(10);
+
+                if (stv0297_readreg(state, 0x82) & 0x08) {
+                        break;
+                }
+        }
+        if (time_after(jiffies, timeout)) {
+                goto timeout;
+        }
+
+       /* disable sweep */
+        stv0297_writereg_mask(state, 0x6a, 1, 0);
+        stv0297_writereg_mask(state, 0x88, 8, 0);
+
+       /* wait for main lock */
+        timeout = jiffies + (20*HZ)/1000;
+        while(time_before(jiffies, timeout)) {
+                msleep(10);
+
+                if (stv0297_readreg(state, 0xDF) & 0x80) {
+                        break;
+                }
+        }
+        if (time_after(jiffies, timeout)) {
+                goto timeout;
+        }
+        msleep(100);
+
+       /* is it still locked after that delay? */
+        if (!(stv0297_readreg(state, 0xDF) & 0x80)) {
+                goto timeout;
+        }
+
+       /* success!! */
+        stv0297_writereg_mask(state, 0x5a, 0x40, 0x00);
+        state->freq_off = stv0297_get_carrieroffset(state);
+        state->base_freq = p->frequency;
+        return 0;
+
+timeout:
+        stv0297_writereg_mask(state, 0x6a, 0x01, 0x00);
+        return 0;
+}
+
+static int stv0297_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        int reg_00, reg_83;
+
+        reg_00 = stv0297_readreg(state, 0x00);
+        reg_83 = stv0297_readreg(state, 0x83);
+
+        p->frequency = state->base_freq + state->freq_off;
+        p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF;
+       p->u.qam.symbol_rate = 0;
+        p->u.qam.fec_inner = 0;
+
+        switch((reg_00 >> 4) & 0x7) {
+       case 0:
+               p->u.qam.modulation = QAM_16;
+               break;
+       case 1:
+               p->u.qam.modulation = QAM_32;
+               break;
+       case 2:
+               p->u.qam.modulation = QAM_128;
+               break;
+       case 3:
+               p->u.qam.modulation = QAM_256;
+               break;
+       case 4:
+               p->u.qam.modulation = QAM_64;
+               break;
+        }
+
+        return 0;
+}
+
+static void stv0297_release(struct dvb_frontend* fe)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        kfree(state);
+}
+
+static struct dvb_frontend_ops stv0297_ops;
+
+struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
+                                   struct i2c_adapter *i2c, int pwm)
+{
+        struct stv0297_state* state = NULL;
+
+        /* allocate memory for the internal state */
+        state = (struct stv0297_state*) kmalloc(sizeof(struct stv0297_state), GFP_KERNEL);
+       if (state == NULL)
+               goto error;
+
+        /* setup the state */
+        state->config = config;
+        state->i2c = i2c;
+        memcpy(&state->ops, &stv0297_ops, sizeof(struct dvb_frontend_ops));
+        state->freq_off = 0;
+        state->base_freq = 0;
+        state->pwm = pwm;
+
+        /* check if the demod is there */
+       if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20)
+               goto error;
+
+        /* create dvb_frontend */
+        state->frontend.ops = &state->ops;
+        state->frontend.demodulator_priv = state;
+        return &state->frontend;
+
+error:
+       if (state)
+               kfree(state);
+        return NULL;
+}
+
+static struct dvb_frontend_ops stv0297_ops = {
+
+        .info = {
+                .name                  = "ST STV0297 DVB-C",
+                .type                  = FE_QAM,
+                .frequency_min         = 64000000,
+                .frequency_max         = 1300000000,
+                .frequency_stepsize    = 62500,
+                .symbol_rate_min       = 870000,
+                .symbol_rate_max       = 11700000,
+                .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+                FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO},
+
+        .release = stv0297_release,
+
+        .init = stv0297_init,
+
+        .set_frontend = stv0297_set_frontend,
+        .get_frontend = stv0297_get_frontend,
+
+        .read_status = stv0297_read_status,
+        .read_ber = stv0297_read_ber,
+        .read_signal_strength = stv0297_read_signal_strength,
+        .read_snr = stv0297_read_snr,
+        .read_ucblocks = stv0297_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver");
+MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(stv0297_attach);
+EXPORT_SYMBOL(stv0297_enable_plli2c);
diff --git a/drivers/media/dvb/frontends/stv0297.h b/drivers/media/dvb/frontends/stv0297.h
new file mode 100644 (file)
index 0000000..355aa87
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+    Driver for STV0297 demodulator
+
+    Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de>
+
+    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 STV0297_H
+#define STV0297_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct stv0297_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
+                                          struct i2c_adapter* i2c, int pwm);
+extern int stv0297_enable_plli2c(struct dvb_frontend* fe);
+
+#endif // STV0297_H
diff --git a/drivers/media/dvb/frontends/stv0299.h b/drivers/media/dvb/frontends/stv0299.h
new file mode 100644 (file)
index 0000000..79457a8
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+    Driver for ST STV0299 demodulator
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+       <ralph@convergence.de>,
+       <holger@convergence.de>,
+       <js@convergence.de>
+
+
+    Philips SU1278/SH
+
+    Copyright (C) 2002 by Peter Schildmann <peter.schildmann@web.de>
+
+
+    LG TDQF-S001F
+
+    Copyright (C) 2002 Felix Domke <tmbinc@elitedvb.net>
+                    & Andreas Oberritter <obi@linuxtv.org>
+
+
+    Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B
+
+    Copyright (C) 2003 Vadim Catana <skystar@moldova.cc>:
+
+    Support for Philips SU1278 on Technotrend hardware
+
+    Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+
+    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 STV0299_H
+#define STV0299_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+#define STV0229_LOCKOUTPUT_0  0
+#define STV0229_LOCKOUTPUT_1  1
+#define STV0229_LOCKOUTPUT_CF 2
+#define STV0229_LOCKOUTPUT_LK 3
+
+#define STV0299_VOLT13_OP0 0
+#define STV0299_VOLT13_OP1 1
+
+struct stv0299_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* inittab - array of pairs of values.
+        * First of each pair is the register, second is the value.
+        * List should be terminated with an 0xff, 0xff pair.
+        */
+       u8* inittab;
+
+       /* master clock to use */
+       u32 mclk;
+
+       /* does the inversion require inversion? */
+       u8 invert:1;
+
+       /* Should the enhanced tuning code be used? */
+       u8 enhanced_tuning:1;
+
+       /* Skip reinitialisation? */
+       u8 skip_reinit:1;
+
+       /* LOCK OUTPUT setting */
+       u8 lock_output:2;
+
+       /* Is 13v controlled by OP0 or OP1? */
+       u8 volt13_op0_op1:1;
+
+       /* minimum delay before retuning */
+       int min_delay_ms;
+
+       /* Set the symbol rate */
+       int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio);
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data);
+
+extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
+                                          struct i2c_adapter* i2c);
+
+#endif // STV0299_H
diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c
new file mode 100644 (file)
index 0000000..df0b844
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+    TDA10021  - Single Chip Cable Channel Receiver driver module
+               used on the the Siemens DVB-C cards
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
+                   Suppport for TDA10021
+
+    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 <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "tda10021.h"
+
+
+struct tda10021_state {
+
+       struct i2c_adapter* i2c;
+
+       struct dvb_frontend_ops ops;
+
+       /* configuration settings */
+       const struct tda10021_config* config;
+
+       struct dvb_frontend frontend;
+
+       u8 pwm;
+       u8 reg0;
+};
+
+
+#if 0
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...)
+#endif
+
+static int verbose;
+
+#define XIN 57840000UL
+#define DISABLE_INVERSION(reg0)                do { reg0 |= 0x20; } while (0)
+#define ENABLE_INVERSION(reg0)         do { reg0 &= ~0x20; } while (0)
+#define HAS_INVERSION(reg0)            (!(reg0 & 0x20))
+
+#define FIN (XIN >> 4)
+
+int tda10021_inittab_size = 0x40;
+static u8 tda10021_inittab[0x40]=
+{
+       0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a,
+       0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40,
+       0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff,
+       0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00,
+       0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00,
+       0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58,
+       0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00,
+       0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00,
+};
+
+static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
+{
+        u8 buf[] = { reg, data };
+       struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+        int ret;
+
+       ret = i2c_transfer (state->i2c, &msg, 1);
+       if (ret != 1)
+               printk("DVB: TDA10021(%d): %s, writereg error "
+                       "(reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+                       state->frontend.dvb->num, __FUNCTION__, reg, data, ret);
+
+       msleep(10);
+       return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+
+static u8 tda10021_readreg (struct tda10021_state* state, u8 reg)
+{
+       u8 b0 [] = { reg };
+       u8 b1 [] = { 0 };
+       struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+                                 { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+       int ret;
+
+       ret = i2c_transfer (state->i2c, msg, 2);
+       if (ret != 2)
+               printk("DVB: TDA10021(%d): %s: readreg error (ret == %i)\n",
+                               state->frontend.dvb->num, __FUNCTION__, ret);
+       return b1[0];
+}
+
+//get access to tuner
+static int lock_tuner(struct tda10021_state* state)
+{
+       u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 };
+       struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+       if(i2c_transfer(state->i2c, &msg, 1) != 1)
+       {
+               printk("tda10021: lock tuner fails\n");
+               return -EREMOTEIO;
+       }
+       return 0;
+}
+
+//release access from tuner
+static int unlock_tuner(struct tda10021_state* state)
+{
+       u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f };
+       struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+       if(i2c_transfer(state->i2c, &msg_post, 1) != 1)
+       {
+               printk("tda10021: unlock tuner fails\n");
+               return -EREMOTEIO;
+       }
+       return 0;
+}
+
+static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0,
+                               fe_spectral_inversion_t inversion)
+{
+       reg0 |= state->reg0 & 0x63;
+
+       if (INVERSION_ON == inversion)
+               ENABLE_INVERSION(reg0);
+       else if (INVERSION_OFF == inversion)
+               DISABLE_INVERSION(reg0);
+
+       tda10021_writereg (state, 0x00, reg0 & 0xfe);
+       tda10021_writereg (state, 0x00, reg0 | 0x01);
+
+       state->reg0 = reg0;
+       return 0;
+}
+
+static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate)
+{
+       s32 BDR;
+       s32 BDRI;
+       s16 SFIL=0;
+       u16 NDEC = 0;
+       u32 tmp, ratio;
+
+       if (symbolrate > XIN/2)
+               symbolrate = XIN/2;
+       if (symbolrate < 500000)
+               symbolrate = 500000;
+
+       if (symbolrate < XIN/16) NDEC = 1;
+       if (symbolrate < XIN/32) NDEC = 2;
+       if (symbolrate < XIN/64) NDEC = 3;
+
+       if (symbolrate < (u32)(XIN/12.3)) SFIL = 1;
+       if (symbolrate < (u32)(XIN/16))  SFIL = 0;
+       if (symbolrate < (u32)(XIN/24.6)) SFIL = 1;
+       if (symbolrate < (u32)(XIN/32))  SFIL = 0;
+       if (symbolrate < (u32)(XIN/49.2)) SFIL = 1;
+       if (symbolrate < (u32)(XIN/64))  SFIL = 0;
+       if (symbolrate < (u32)(XIN/98.4)) SFIL = 1;
+
+       symbolrate <<= NDEC;
+       ratio = (symbolrate << 4) / FIN;
+       tmp =  ((symbolrate << 4) % FIN) << 8;
+       ratio = (ratio << 8) + tmp / FIN;
+       tmp = (tmp % FIN) << 8;
+       ratio = (ratio << 8) + (tmp + FIN/2) / FIN;
+
+       BDR = ratio;
+       BDRI = (((XIN << 5) / symbolrate) + 1) / 2;
+
+       if (BDRI > 0xFF)
+               BDRI = 0xFF;
+
+       SFIL = (SFIL << 4) | tda10021_inittab[0x0E];
+
+       NDEC = (NDEC << 6) | tda10021_inittab[0x03];
+
+       tda10021_writereg (state, 0x03, NDEC);
+       tda10021_writereg (state, 0x0a, BDR&0xff);
+       tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff);
+       tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f);
+
+       tda10021_writereg (state, 0x0d, BDRI);
+       tda10021_writereg (state, 0x0e, SFIL);
+
+       return 0;
+}
+
+
+
+
+
+
+
+
+
+
+static int tda10021_init (struct dvb_frontend *fe)
+{
+       struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+       int i;
+
+       dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num);
+
+       //tda10021_writereg (fe, 0, 0);
+
+       for (i=0; i<tda10021_inittab_size; i++)
+               tda10021_writereg (state, i, tda10021_inittab[i]);
+
+       tda10021_writereg (state, 0x34, state->pwm);
+
+       //Comment by markus
+       //0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0)
+       //0x2A[4] == BYPPLL -> Power down mode (default 1)
+       //0x2A[5] == LCK -> PLL Lock Flag
+       //0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0)
+
+       //Activate PLL
+       tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
+
+       if (state->config->pll_init) {
+               lock_tuner(state);
+               state->config->pll_init(fe);
+               unlock_tuner(state);
+       }
+
+       return 0;
+}
+
+static int tda10021_set_parameters (struct dvb_frontend *fe,
+                           struct dvb_frontend_parameters *p)
+{
+       struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+       //table for QAM4-QAM256 ready  QAM4  QAM16 QAM32 QAM64 QAM128 QAM256
+       //CONF
+       static const u8 reg0x00 [] = { 0x14, 0x00, 0x04, 0x08, 0x0c,  0x10 };
+       //AGCREF value
+       static const u8 reg0x01 [] = { 0x78, 0x8c, 0x8c, 0x6a, 0x78,  0x5c };
+       //LTHR value
+       static const u8 reg0x05 [] = { 0x78, 0x87, 0x64, 0x46, 0x36,  0x26 };
+       //MSETH
+       static const u8 reg0x08 [] = { 0x8c, 0xa2, 0x74, 0x43, 0x34,  0x23 };
+       //AREF
+       static const u8 reg0x09 [] = { 0x96, 0x91, 0x96, 0x6a, 0x7e,  0x6b };
+
+       int qam = p->u.qam.modulation;
+
+       if (qam < 0 || qam > 5)
+               return -EINVAL;
+
+       //printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate);
+
+       lock_tuner(state);
+       state->config->pll_set(fe, p);
+       unlock_tuner(state);
+
+       tda10021_set_symbolrate (state, p->u.qam.symbol_rate);
+       tda10021_writereg (state, 0x34, state->pwm);
+
+       tda10021_writereg (state, 0x01, reg0x01[qam]);
+       tda10021_writereg (state, 0x05, reg0x05[qam]);
+       tda10021_writereg (state, 0x08, reg0x08[qam]);
+       tda10021_writereg (state, 0x09, reg0x09[qam]);
+
+       tda10021_setup_reg0 (state, reg0x00[qam], p->inversion);
+
+       return 0;
+}
+
+static int tda10021_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+       int sync;
+
+       *status = 0;
+       //0x11[0] == EQALGO -> Equalizer algorithms state
+       //0x11[1] == CARLOCK -> Carrier locked
+       //0x11[2] == FSYNC -> Frame synchronisation
+       //0x11[3] == FEL -> Front End locked
+       //0x11[6] == NODVB -> DVB Mode Information
+       sync = tda10021_readreg (state, 0x11);
+
+       if (sync & 2)
+               *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER;
+
+       if (sync & 4)
+               *status |= FE_HAS_SYNC|FE_HAS_VITERBI;
+
+       if (sync & 8)
+               *status |= FE_HAS_LOCK;
+
+       return 0;
+}
+
+static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+       struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+       u32 _ber = tda10021_readreg(state, 0x14) |
+                 (tda10021_readreg(state, 0x15) << 8) |
+                ((tda10021_readreg(state, 0x16) & 0x0f) << 16);
+       *ber = 10 * _ber;
+
+       return 0;
+}
+
+static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+       struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+       u8 gain = tda10021_readreg(state, 0x17);
+       *strength = (gain << 8) | gain;
+
+       return 0;
+}
+
+static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+       struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+       u8 quality = ~tda10021_readreg(state, 0x18);
+       *snr = (quality << 8) | quality;
+
+       return 0;
+}
+
+static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+       struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+       *ucblocks = tda10021_readreg (state, 0x13) & 0x7f;
+       if (*ucblocks == 0x7f)
+               *ucblocks = 0xffffffff;
+
+       /* reset uncorrected block counter */
+       tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
+       tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
+
+       return 0;
+}
+
+static int tda10021_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+       int sync;
+       s8 afc = 0;
+
+       sync = tda10021_readreg(state, 0x11);
+       afc = tda10021_readreg(state, 0x19);
+       if (verbose) {
+               /* AFC only valid when carrier has been recovered */
+               printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" :
+                                 "DVB: TDA10021(%d): [AFC (%d) %dHz]\n",
+                       state->frontend.dvb->num, afc,
+                      -((s32)p->u.qam.symbol_rate * afc) >> 10);
+       }
+
+       p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF;
+       p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
+
+       p->u.qam.fec_inner = FEC_NONE;
+       p->frequency = ((p->frequency + 31250) / 62500) * 62500;
+
+       if (sync & 2)
+               p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10;
+
+       return 0;
+}
+
+static int tda10021_sleep(struct dvb_frontend* fe)
+{
+       struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+       tda10021_writereg (state, 0x1b, 0x02);  /* pdown ADC */
+       tda10021_writereg (state, 0x00, 0x80);  /* standby */
+
+       return 0;
+}
+
+static void tda10021_release(struct dvb_frontend* fe)
+{
+       struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+       kfree(state);
+}
+
+static struct dvb_frontend_ops tda10021_ops;
+
+struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+                                    struct i2c_adapter* i2c,
+                                    u8 pwm)
+{
+       struct tda10021_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct tda10021_state*) kmalloc(sizeof(struct tda10021_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &tda10021_ops, sizeof(struct dvb_frontend_ops));
+       state->pwm = pwm;
+       state->reg0 = tda10021_inittab[0];
+
+       /* check if the demod is there */
+       if ((tda10021_readreg(state, 0x1a) & 0xf0) != 0x70) goto error;
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops tda10021_ops = {
+
+       .info = {
+               .name = "Philips TDA10021 DVB-C",
+               .type = FE_QAM,
+               .frequency_stepsize = 62500,
+               .frequency_min = 51000000,
+               .frequency_max = 858000000,
+               .symbol_rate_min = (XIN/2)/64,     /* SACLK/64 == (XIN/2)/64 */
+               .symbol_rate_max = (XIN/2)/4,      /* SACLK/4 */
+       #if 0
+               .frequency_tolerance = ???,
+               .symbol_rate_tolerance = ???,  /* ppm */  /* == 8% (spec p. 5) */
+       #endif
+               .caps = 0x400 | //FE_CAN_QAM_4
+                       FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+                       FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+                       FE_CAN_FEC_AUTO
+       },
+
+       .release = tda10021_release,
+
+       .init = tda10021_init,
+       .sleep = tda10021_sleep,
+
+       .set_frontend = tda10021_set_parameters,
+       .get_frontend = tda10021_get_frontend,
+
+       .read_status = tda10021_read_status,
+       .read_ber = tda10021_read_ber,
+       .read_signal_strength = tda10021_read_signal_strength,
+       .read_snr = tda10021_read_snr,
+       .read_ucblocks = tda10021_read_ucblocks,
+};
+
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
+
+MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10021_attach);
diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda10021.h
new file mode 100644 (file)
index 0000000..a34a941
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+    TDA10021  - Single Chip Cable Channel Receiver driver module
+               used on the the Siemens DVB-C cards
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
+                   Suppport for TDA10021
+
+    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 TDA10021_H
+#define TDA10021_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda10021_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+                                           struct i2c_adapter* i2c,
+                                           u8 pwm);
+
+#endif // TDA10021_H
diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h
new file mode 100644 (file)
index 0000000..e452fc0
--- /dev/null
@@ -0,0 +1,56 @@
+  /*
+     Driver for Philips tda1004xh OFDM Frontend
+
+     (c) 2004 Andrew de Quincey
+
+     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 TDA1004X_H
+#define TDA1004X_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct tda1004x_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* does the "inversion" need inverted? */
+       u8 invert:1;
+
+       /* Does the OCLK signal need inverted? */
+       u8 invert_oclk:1;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+       /* request firmware for device */
+       int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+                                           struct i2c_adapter* i2c);
+
+extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+                                           struct i2c_adapter* i2c);
+
+extern int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data);
+
+#endif // TDA1004X_H
diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c
new file mode 100644 (file)
index 0000000..03c4366
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+    Driver for Philips TDA8083 based QPSK Demodulator
+
+    Copyright (C) 2001 Convergence Integrated Media GmbH
+
+    written by Ralph Metzler <ralph@convergence.de>
+
+    adoption to the new DVB frontend API and diagnostic ioctl's
+    by Holger Waechtler <holger@convergence.de>
+
+    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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "tda8083.h"
+
+
+struct tda8083_state {
+
+       struct i2c_adapter* i2c;
+
+       struct dvb_frontend_ops ops;
+
+       /* configuration settings */
+       const struct tda8083_config* config;
+
+       struct dvb_frontend frontend;
+};
+
+static int debug;
+#define dprintk(args...) \
+       do { \
+               if (debug) printk(KERN_DEBUG "tda8083: " args); \
+       } while (0)
+
+
+static u8 tda8083_init_tab [] = {
+       0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea,
+       0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10,
+       0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8,
+       0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00,
+       0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00
+};
+
+
+static int tda8083_writereg (struct tda8083_state* state, u8 reg, u8 data)
+{
+       int ret;
+       u8 buf [] = { reg, data };
+       struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+        ret = i2c_transfer(state->i2c, &msg, 1);
+
+        if (ret != 1)
+                dprintk ("%s: writereg error (reg %02x, ret == %i)\n",
+                       __FUNCTION__, reg, ret);
+
+        return (ret != 1) ? -1 : 0;
+}
+
+
+static int tda8083_readregs (struct tda8083_state* state, u8 reg1, u8 *b, u8 len)
+{
+       int ret;
+       struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg1, .len = 1 },
+                          { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } };
+
+       ret = i2c_transfer(state->i2c, msg, 2);
+
+       if (ret != 2)
+               dprintk ("%s: readreg error (reg %02x, ret == %i)\n",
+                       __FUNCTION__, reg1, ret);
+
+        return ret == 2 ? 0 : -1;
+}
+
+
+static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg)
+{
+       u8 val;
+
+       tda8083_readregs (state, reg, &val, 1);
+
+       return val;
+}
+
+
+
+static int tda8083_set_inversion (struct tda8083_state* state, fe_spectral_inversion_t inversion)
+{
+       /*  XXX FIXME: implement other modes than FEC_AUTO */
+       if (inversion == INVERSION_AUTO)
+               return 0;
+
+       return -EINVAL;
+}
+
+
+static int tda8083_set_fec (struct tda8083_state* state, fe_code_rate_t fec)
+{
+       if (fec == FEC_AUTO)
+               return tda8083_writereg (state, 0x07, 0xff);
+
+       if (fec >= FEC_1_2 && fec <= FEC_8_9)
+               return tda8083_writereg (state, 0x07, 1 << (FEC_8_9 - fec));
+
+       return -EINVAL;
+}
+
+
+static fe_code_rate_t tda8083_get_fec (struct tda8083_state* state)
+{
+       u8 index;
+       static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4,
+                                      FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 };
+
+       index = tda8083_readreg(state, 0x0e) & 0x07;
+
+       return fec_tab [index];
+}
+
+
+static int tda8083_set_symbolrate (struct tda8083_state* state, u32 srate)
+{
+        u32 ratio;
+       u32 tmp;
+       u8 filter;
+
+       if (srate > 32000000)
+                srate = 32000000;
+        if (srate < 500000)
+                srate = 500000;
+
+       filter = 0;
+       if (srate < 24000000)
+               filter = 2;
+       if (srate < 16000000)
+               filter = 3;
+
+       tmp = 31250 << 16;
+       ratio = tmp / srate;
+
+       tmp = (tmp % srate) << 8;
+       ratio = (ratio << 8) + tmp / srate;
+
+       tmp = (tmp % srate) << 8;
+       ratio = (ratio << 8) + tmp / srate;
+
+       dprintk("tda8083: ratio == %08x\n", (unsigned int) ratio);
+
+       tda8083_writereg (state, 0x05, filter);
+       tda8083_writereg (state, 0x02, (ratio >> 16) & 0xff);
+       tda8083_writereg (state, 0x03, (ratio >>  8) & 0xff);
+       tda8083_writereg (state, 0x04, (ratio      ) & 0xff);
+
+       tda8083_writereg (state, 0x00, 0x3c);
+       tda8083_writereg (state, 0x00, 0x04);
+
+       return 1;
+}
+
+
+static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout)
+{
+       unsigned long start = jiffies;
+
+       while (jiffies - start < timeout &&
+               !(tda8083_readreg(state, 0x02) & 0x80))
+       {
+               msleep(50);
+       };
+}
+
+static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t tone)
+{
+       tda8083_writereg (state, 0x26, 0xf1);
+
+       switch (tone) {
+       case SEC_TONE_OFF:
+               return tda8083_writereg (state, 0x29, 0x00);
+       case SEC_TONE_ON:
+               return tda8083_writereg (state, 0x29, 0x80);
+       default:
+               return -EINVAL;
+       };
+}
+
+
+static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t voltage)
+{
+       switch (voltage) {
+       case SEC_VOLTAGE_13:
+               return tda8083_writereg (state, 0x20, 0x00);
+       case SEC_VOLTAGE_18:
+               return tda8083_writereg (state, 0x20, 0x11);
+       default:
+               return -EINVAL;
+       };
+}
+
+static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_cmd_t burst)
+{
+       switch (burst) {
+       case SEC_MINI_A:
+               tda8083_writereg (state, 0x29, (5 << 2));  /* send burst A */
+               break;
+       case SEC_MINI_B:
+               tda8083_writereg (state, 0x29, (7 << 2));  /* send B */
+               break;
+       default:
+               return -EINVAL;
+       };
+
+       tda8083_wait_diseqc_fifo (state, 100);
+
+       return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static int tda8083_send_diseqc_msg (struct dvb_frontend* fe,
+                                   struct dvb_diseqc_master_cmd *m)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+       int i;
+
+       tda8083_writereg (state, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */
+
+       for (i=0; i<m->msg_len; i++)
+               tda8083_writereg (state, 0x23 + i, m->msg[i]);
+
+       tda8083_writereg (state, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */
+
+       tda8083_wait_diseqc_fifo (state, 100);
+
+       return 0;
+}
+
+static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+       u8 signal = ~tda8083_readreg (state, 0x01);
+       u8 sync = tda8083_readreg (state, 0x02);
+
+       *status = 0;
+
+       if (signal > 10)
+               *status |= FE_HAS_SIGNAL;
+
+       if (sync & 0x01)
+               *status |= FE_HAS_CARRIER;
+
+       if (sync & 0x02)
+               *status |= FE_HAS_VITERBI;
+
+       if (sync & 0x10)
+               *status |= FE_HAS_SYNC;
+
+       if ((sync & 0x1f) == 0x1f)
+               *status |= FE_HAS_LOCK;
+
+       return 0;
+}
+
+static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+       u8 signal = ~tda8083_readreg (state, 0x01);
+       *strength = (signal << 8) | signal;
+
+       return 0;
+}
+
+static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+       u8 _snr = tda8083_readreg (state, 0x08);
+       *snr = (_snr << 8) | _snr;
+
+       return 0;
+}
+
+static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+       state->config->pll_set(fe, p);
+       tda8083_set_inversion (state, p->inversion);
+       tda8083_set_fec (state, p->u.qpsk.fec_inner);
+       tda8083_set_symbolrate (state, p->u.qpsk.symbol_rate);
+
+       tda8083_writereg (state, 0x00, 0x3c);
+       tda8083_writereg (state, 0x00, 0x04);
+
+       return 0;
+}
+
+static int tda8083_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+       /*  FIXME: get symbolrate & frequency offset...*/
+       /*p->frequency = ???;*/
+       p->inversion = (tda8083_readreg (state, 0x0e) & 0x80) ?
+                       INVERSION_ON : INVERSION_OFF;
+       p->u.qpsk.fec_inner = tda8083_get_fec (state);
+       /*p->u.qpsk.symbol_rate = tda8083_get_symbolrate (state);*/
+
+       return 0;
+}
+
+static int tda8083_sleep(struct dvb_frontend* fe)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+       tda8083_writereg (state, 0x00, 0x02);
+       return 0;
+}
+
+static int tda8083_init(struct dvb_frontend* fe)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+       int i;
+
+       for (i=0; i<44; i++)
+               tda8083_writereg (state, i, tda8083_init_tab[i]);
+
+       if (state->config->pll_init) state->config->pll_init(fe);
+
+       tda8083_writereg (state, 0x00, 0x3c);
+       tda8083_writereg (state, 0x00, 0x04);
+
+       return 0;
+}
+
+static int tda8083_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+       tda8083_send_diseqc_burst (state, burst);
+       tda8083_writereg (state, 0x00, 0x3c);
+       tda8083_writereg (state, 0x00, 0x04);
+
+       return 0;
+}
+
+static int tda8083_diseqc_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+       tda8083_set_tone (state, tone);
+       tda8083_writereg (state, 0x00, 0x3c);
+       tda8083_writereg (state, 0x00, 0x04);
+
+       return 0;
+}
+
+static int tda8083_diseqc_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+       tda8083_set_voltage (state, voltage);
+       tda8083_writereg (state, 0x00, 0x3c);
+       tda8083_writereg (state, 0x00, 0x04);
+
+       return 0;
+}
+
+static void tda8083_release(struct dvb_frontend* fe)
+{
+       struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+       kfree(state);
+}
+
+static struct dvb_frontend_ops tda8083_ops;
+
+struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
+                                   struct i2c_adapter* i2c)
+{
+       struct tda8083_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct tda8083_state*) kmalloc(sizeof(struct tda8083_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &tda8083_ops, sizeof(struct dvb_frontend_ops));
+
+       /* check if the demod is there */
+       if ((tda8083_readreg(state, 0x00)) != 0x05) goto error;
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops tda8083_ops = {
+
+       .info = {
+               .name                   = "Philips TDA8083 DVB-S",
+               .type                   = FE_QPSK,
+               .frequency_min          = 950000,     /* FIXME: guessed! */
+               .frequency_max          = 1400000,    /* FIXME: guessed! */
+               .frequency_stepsize     = 125,   /* kHz for QPSK frontends */
+       /*      .frequency_tolerance    = ???,*/
+               .symbol_rate_min        = 1000000,   /* FIXME: guessed! */
+               .symbol_rate_max        = 45000000,  /* FIXME: guessed! */
+       /*      .symbol_rate_tolerance  = ???,*/
+               .caps = FE_CAN_INVERSION_AUTO |
+                       FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+                       FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+                       FE_CAN_QPSK | FE_CAN_MUTE_TS
+       },
+
+       .release = tda8083_release,
+
+       .init = tda8083_init,
+       .sleep = tda8083_sleep,
+
+       .set_frontend = tda8083_set_frontend,
+       .get_frontend = tda8083_get_frontend,
+
+       .read_status = tda8083_read_status,
+       .read_signal_strength = tda8083_read_signal_strength,
+       .read_snr = tda8083_read_snr,
+
+       .diseqc_send_master_cmd = tda8083_send_diseqc_msg,
+       .diseqc_send_burst = tda8083_diseqc_send_burst,
+       .set_tone = tda8083_diseqc_set_tone,
+       .set_voltage = tda8083_diseqc_set_voltage,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda8083_attach);
diff --git a/drivers/media/dvb/frontends/tda8083.h b/drivers/media/dvb/frontends/tda8083.h
new file mode 100644 (file)
index 0000000..4666633
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend
+
+    Copyright (C) 2001 Convergence Integrated Media GmbH
+
+    written by Ralph Metzler <ralph@convergence.de>
+
+    adoption to the new DVB frontend API and diagnostic ioctl's
+    by Holger Waechtler <holger@convergence.de>
+
+    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 TDA8083_H
+#define TDA8083_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda8083_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
+                                          struct i2c_adapter* i2c);
+
+#endif // TDA8083_H
diff --git a/drivers/media/dvb/frontends/tda80xx.c b/drivers/media/dvb/frontends/tda80xx.c
new file mode 100644 (file)
index 0000000..5b8828b
--- /dev/null
@@ -0,0 +1,749 @@
+/*
+ * tda80xx.c
+ *
+ * Philips TDA8044 / TDA8083 QPSK demodulator driver
+ *
+ * Copyright (C) 2001 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2002-2004 Andreas Oberritter <obi@linuxtv.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.
+ *
+ * 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 <linux/config.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/threads.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "tda80xx.h"
+
+enum {
+       ID_TDA8044 = 0x04,
+       ID_TDA8083 = 0x05,
+};
+
+
+struct tda80xx_state {
+
+       struct i2c_adapter* i2c;
+
+       struct dvb_frontend_ops ops;
+
+       /* configuration settings */
+       const struct tda80xx_config* config;
+
+       struct dvb_frontend frontend;
+
+       u32 clk;
+       int afc_loop;
+       struct work_struct worklet;
+       fe_code_rate_t code_rate;
+       fe_spectral_inversion_t spectral_inversion;
+       fe_status_t status;
+       u8 id;
+};
+
+static int debug = 1;
+#define dprintk        if (debug) printk
+
+static u8 tda8044_inittab_pre[] = {
+       0x02, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea,
+       0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x58,
+       0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68,
+       0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x00,
+       0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00
+};
+
+static u8 tda8044_inittab_post[] = {
+       0x04, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea,
+       0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x50,
+       0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68,
+       0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x6c,
+       0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00
+};
+
+static u8 tda8083_inittab[] = {
+       0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea,
+       0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10,
+       0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8,
+       0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00,
+       0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00
+};
+
+static __inline__ u32 tda80xx_div(u32 a, u32 b)
+{
+       return (a + (b / 2)) / b;
+}
+
+static __inline__ u32 tda80xx_gcd(u32 a, u32 b)
+{
+       u32 r;
+
+       while ((r = a % b)) {
+               a = b;
+               b = r;
+       }
+
+       return b;
+}
+
+static int tda80xx_read(struct tda80xx_state* state, u8 reg, u8 *buf, u8 len)
+{
+       int ret;
+       struct i2c_msg msg[] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg, .len = 1 },
+                         { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } };
+
+       ret = i2c_transfer(state->i2c, msg, 2);
+
+       if (ret != 2)
+               dprintk("%s: readreg error (reg %02x, ret == %i)\n",
+                               __FUNCTION__, reg, ret);
+
+       mdelay(10);
+
+       return (ret == 2) ? 0 : -EREMOTEIO;
+}
+
+static int tda80xx_write(struct tda80xx_state* state, u8 reg, const u8 *buf, u8 len)
+{
+       int ret;
+       u8 wbuf[len + 1];
+       struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = wbuf, .len = len + 1 };
+
+       wbuf[0] = reg;
+       memcpy(&wbuf[1], buf, len);
+
+       ret = i2c_transfer(state->i2c, &msg, 1);
+
+       if (ret != 1)
+               dprintk("%s: i2c xfer error (ret == %i)\n", __FUNCTION__, ret);
+
+       mdelay(10);
+
+       return (ret == 1) ? 0 : -EREMOTEIO;
+}
+
+static __inline__ u8 tda80xx_readreg(struct tda80xx_state* state, u8 reg)
+{
+       u8 val;
+
+       tda80xx_read(state, reg, &val, 1);
+
+       return val;
+}
+
+static __inline__ int tda80xx_writereg(struct tda80xx_state* state, u8 reg, u8 data)
+{
+       return tda80xx_write(state, reg, &data, 1);
+}
+
+static int tda80xx_set_parameters(struct tda80xx_state* state,
+                                 fe_spectral_inversion_t inversion,
+                                 u32 symbol_rate,
+                                 fe_code_rate_t fec_inner)
+{
+       u8 buf[15];
+       u64 ratio;
+       u32 clk;
+       u32 k;
+       u32 sr = symbol_rate;
+       u32 gcd;
+       u8 scd;
+
+       if (symbol_rate > (state->clk * 3) / 16)
+               scd = 0;
+       else if (symbol_rate > (state->clk * 3) / 32)
+               scd = 1;
+       else if (symbol_rate > (state->clk * 3) / 64)
+               scd = 2;
+       else
+               scd = 3;
+
+       clk = scd ? (state->clk / (scd * 2)) : state->clk;
+
+       /*
+        * Viterbi decoder:
+        * Differential decoding off
+        * Spectral inversion unknown
+        * QPSK modulation
+        */
+       if (inversion == INVERSION_ON)
+               buf[0] = 0x60;
+       else if (inversion == INVERSION_OFF)
+               buf[0] = 0x20;
+       else
+               buf[0] = 0x00;
+
+       /*
+        * CLK ratio:
+        * system clock frequency is up to 64 or 96 MHz
+        *
+        * formula:
+        * r = k * clk / symbol_rate
+        *
+        * k:   2^21 for caa 0..3,
+        *      2^20 for caa 4..5,
+        *      2^19 for caa 6..7
+        */
+       if (symbol_rate <= (clk * 3) / 32)
+               k = (1 << 19);
+       else if (symbol_rate <= (clk * 3) / 16)
+               k = (1 << 20);
+       else
+               k = (1 << 21);
+
+       gcd = tda80xx_gcd(clk, sr);
+       clk /= gcd;
+       sr /= gcd;
+
+       gcd = tda80xx_gcd(k, sr);
+       k /= gcd;
+       sr /= gcd;
+
+       ratio = (u64)k * (u64)clk;
+       do_div(ratio, sr);
+
+       buf[1] = ratio >> 16;
+       buf[2] = ratio >> 8;
+       buf[3] = ratio;
+
+       /* nyquist filter roll-off factor 35% */
+       buf[4] = 0x20;
+
+       clk = scd ? (state->clk / (scd * 2)) : state->clk;
+
+       /* Anti Alias Filter */
+       if (symbol_rate < (clk * 3) / 64)
+               printk("tda80xx: unsupported symbol rate: %u\n", symbol_rate);
+       else if (symbol_rate <= clk / 16)
+               buf[4] |= 0x07;
+       else if (symbol_rate <= (clk * 3) / 32)
+               buf[4] |= 0x06;
+       else if (symbol_rate <= clk / 8)
+               buf[4] |= 0x05;
+       else if (symbol_rate <= (clk * 3) / 16)
+               buf[4] |= 0x04;
+       else if (symbol_rate <= clk / 4)
+               buf[4] |= 0x03;
+       else if (symbol_rate <= (clk * 3) / 8)
+               buf[4] |= 0x02;
+       else if (symbol_rate <= clk / 2)
+               buf[4] |= 0x01;
+       else
+               buf[4] |= 0x00;
+
+       /* Sigma Delta converter */
+       buf[5] = 0x00;
+
+       /* FEC: Possible puncturing rates */
+       if (fec_inner == FEC_NONE)
+               buf[6] = 0x00;
+       else if ((fec_inner >= FEC_1_2) && (fec_inner <= FEC_8_9))
+               buf[6] = (1 << (8 - fec_inner));
+       else if (fec_inner == FEC_AUTO)
+               buf[6] = 0xff;
+       else
+               return -EINVAL;
+
+       /* carrier lock detector threshold value */
+       buf[7] = 0x30;
+       /* AFC1: proportional part settings */
+       buf[8] = 0x42;
+       /* AFC1: integral part settings */
+       buf[9] = 0x98;
+       /* PD: Leaky integrator SCPC mode */
+       buf[10] = 0x28;
+       /* AFC2, AFC1 controls */
+       buf[11] = 0x30;
+       /* PD: proportional part settings */
+       buf[12] = 0x42;
+       /* PD: integral part settings */
+       buf[13] = 0x99;
+       /* AGC */
+       buf[14] = 0x50 | scd;
+
+       printk("symbol_rate=%u clk=%u\n", symbol_rate, clk);
+
+       return tda80xx_write(state, 0x01, buf, sizeof(buf));
+}
+
+static int tda80xx_set_clk(struct tda80xx_state* state)
+{
+       u8 buf[2];
+
+       /* CLK proportional part */
+       buf[0] = (0x06 << 5) | 0x08;    /* CMP[2:0], CSP[4:0] */
+       /* CLK integral part */
+       buf[1] = (0x04 << 5) | 0x1a;    /* CMI[2:0], CSI[4:0] */
+
+       return tda80xx_write(state, 0x17, buf, sizeof(buf));
+}
+
+#if 0
+static int tda80xx_set_scpc_freq_offset(struct tda80xx_state* state)
+{
+       /* a constant value is nonsense here imho */
+       return tda80xx_writereg(state, 0x22, 0xf9);
+}
+#endif
+
+static int tda80xx_close_loop(struct tda80xx_state* state)
+{
+       u8 buf[2];
+
+       /* PD: Loop closed, LD: lock detect enable, SCPC: Sweep mode - AFC1 loop closed */
+       buf[0] = 0x68;
+       /* AFC1: Loop closed, CAR Feedback: 8192 */
+       buf[1] = 0x70;
+
+       return tda80xx_write(state, 0x0b, buf, sizeof(buf));
+}
+
+static irqreturn_t tda80xx_irq(int irq, void *priv, struct pt_regs *pt)
+{
+       schedule_work(priv);
+
+       return IRQ_HANDLED;
+}
+
+static void tda80xx_read_status_int(struct tda80xx_state* state)
+{
+       u8 val;
+
+       static const fe_spectral_inversion_t inv_tab[] = {
+               INVERSION_OFF, INVERSION_ON
+       };
+
+       static const fe_code_rate_t fec_tab[] = {
+               FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4,
+               FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8,
+       };
+
+       val = tda80xx_readreg(state, 0x02);
+
+       state->status = 0;
+
+       if (val & 0x01) /* demodulator lock */
+               state->status |= FE_HAS_SIGNAL;
+       if (val & 0x02) /* clock recovery lock */
+               state->status |= FE_HAS_CARRIER;
+       if (val & 0x04) /* viterbi lock */
+               state->status |= FE_HAS_VITERBI;
+       if (val & 0x08) /* deinterleaver lock (packet sync) */
+               state->status |= FE_HAS_SYNC;
+       if (val & 0x10) /* derandomizer lock (frame sync) */
+               state->status |= FE_HAS_LOCK;
+       if (val & 0x20) /* frontend can not lock */
+               state->status |= FE_TIMEDOUT;
+
+       if ((state->status & (FE_HAS_CARRIER)) && (state->afc_loop)) {
+               printk("tda80xx: closing loop\n");
+               tda80xx_close_loop(state);
+               state->afc_loop = 0;
+       }
+
+       if (state->status & (FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK)) {
+               val = tda80xx_readreg(state, 0x0e);
+               state->code_rate = fec_tab[val & 0x07];
+               if (state->status & (FE_HAS_SYNC | FE_HAS_LOCK))
+                       state->spectral_inversion = inv_tab[(val >> 7) & 0x01];
+               else
+                       state->spectral_inversion = INVERSION_AUTO;
+       }
+       else {
+               state->code_rate = FEC_AUTO;
+       }
+}
+
+static void tda80xx_worklet(void *priv)
+{
+       struct tda80xx_state *state = priv;
+
+       tda80xx_writereg(state, 0x00, 0x04);
+       enable_irq(state->config->irq);
+
+       tda80xx_read_status_int(state);
+}
+
+static void tda80xx_wait_diseqc_fifo(struct tda80xx_state* state)
+{
+       size_t i;
+
+       for (i = 0; i < 100; i++) {
+               if (tda80xx_readreg(state, 0x02) & 0x80)
+                       break;
+               msleep(10);
+       }
+}
+
+static int tda8044_init(struct dvb_frontend* fe)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+       int ret;
+
+       /*
+        * this function is a mess...
+        */
+
+       if ((ret = tda80xx_write(state, 0x00, tda8044_inittab_pre, sizeof(tda8044_inittab_pre))))
+               return ret;
+
+       tda80xx_writereg(state, 0x0f, 0x50);
+#if 1
+       tda80xx_writereg(state, 0x20, 0x8F);            /* FIXME */
+       tda80xx_writereg(state, 0x20, state->config->volt18setting);    /* FIXME */
+       //tda80xx_writereg(state, 0x00, 0x04);
+       tda80xx_writereg(state, 0x00, 0x0C);
+#endif
+       //tda80xx_writereg(state, 0x00, 0x08); /* Reset AFC1 loop filter */
+
+       tda80xx_write(state, 0x00, tda8044_inittab_post, sizeof(tda8044_inittab_post));
+
+       if (state->config->pll_init) {
+               tda80xx_writereg(state, 0x1c, 0x80);
+               state->config->pll_init(fe);
+               tda80xx_writereg(state, 0x1c, 0x00);
+       }
+
+       return 0;
+}
+
+static int tda8083_init(struct dvb_frontend* fe)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       tda80xx_write(state, 0x00, tda8083_inittab, sizeof(tda8083_inittab));
+
+       if (state->config->pll_init) {
+               tda80xx_writereg(state, 0x1c, 0x80);
+               state->config->pll_init(fe);
+               tda80xx_writereg(state, 0x1c, 0x00);
+       }
+
+       return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static int tda80xx_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       switch (voltage) {
+       case SEC_VOLTAGE_13:
+               return tda80xx_writereg(state, 0x20, state->config->volt13setting);
+       case SEC_VOLTAGE_18:
+               return tda80xx_writereg(state, 0x20, state->config->volt18setting);
+       case SEC_VOLTAGE_OFF:
+               return tda80xx_writereg(state, 0x20, 0);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int tda80xx_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       switch (tone) {
+       case SEC_TONE_OFF:
+               return tda80xx_writereg(state, 0x29, 0x00);
+       case SEC_TONE_ON:
+               return tda80xx_writereg(state, 0x29, 0x80);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int tda80xx_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       if (cmd->msg_len > 6)
+               return -EINVAL;
+
+       tda80xx_writereg(state, 0x29, 0x08 | (cmd->msg_len - 3));
+       tda80xx_write(state, 0x23, cmd->msg, cmd->msg_len);
+       tda80xx_writereg(state, 0x29, 0x0c | (cmd->msg_len - 3));
+       tda80xx_wait_diseqc_fifo(state);
+
+       return 0;
+}
+
+static int tda80xx_send_diseqc_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t cmd)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       switch (cmd) {
+       case SEC_MINI_A:
+               tda80xx_writereg(state, 0x29, 0x14);
+               break;
+       case SEC_MINI_B:
+               tda80xx_writereg(state, 0x29, 0x1c);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       tda80xx_wait_diseqc_fifo(state);
+
+       return 0;
+}
+
+static int tda80xx_sleep(struct dvb_frontend* fe)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       tda80xx_writereg(state, 0x00, 0x02);    /* enter standby */
+
+       return 0;
+}
+
+static int tda80xx_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       tda80xx_writereg(state, 0x1c, 0x80);
+       state->config->pll_set(fe, p);
+       tda80xx_writereg(state, 0x1c, 0x00);
+
+       tda80xx_set_parameters(state, p->inversion, p->u.qpsk.symbol_rate, p->u.qpsk.fec_inner);
+       tda80xx_set_clk(state);
+       //tda80xx_set_scpc_freq_offset(state);
+       state->afc_loop = 1;
+
+       return 0;
+}
+
+static int tda80xx_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       if (!state->config->irq)
+               tda80xx_read_status_int(state);
+
+       p->inversion = state->spectral_inversion;
+       p->u.qpsk.fec_inner = state->code_rate;
+
+       return 0;
+}
+
+static int tda80xx_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       if (!state->config->irq)
+               tda80xx_read_status_int(state);
+       *status = state->status;
+
+       return 0;
+}
+
+static int tda80xx_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+       int ret;
+       u8 buf[3];
+
+       if ((ret = tda80xx_read(state, 0x0b, buf, sizeof(buf))))
+               return ret;
+
+       *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2];
+
+       return 0;
+}
+
+static int tda80xx_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       u8 gain = ~tda80xx_readreg(state, 0x01);
+       *strength = (gain << 8) | gain;
+
+       return 0;
+}
+
+static int tda80xx_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       u8 quality = tda80xx_readreg(state, 0x08);
+       *snr = (quality << 8) | quality;
+
+       return 0;
+}
+
+static int tda80xx_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       *ucblocks = tda80xx_readreg(state, 0x0f);
+       if (*ucblocks == 0xff)
+               *ucblocks = 0xffffffff;
+
+       return 0;
+}
+
+static int tda80xx_init(struct dvb_frontend* fe)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       switch(state->id) {
+       case ID_TDA8044:
+               return tda8044_init(fe);
+
+       case ID_TDA8083:
+               return tda8083_init(fe);
+       }
+       return 0;
+}
+
+static void tda80xx_release(struct dvb_frontend* fe)
+{
+       struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+       if (state->config->irq)
+               free_irq(state->config->irq, &state->worklet);
+
+       kfree(state);
+}
+
+static struct dvb_frontend_ops tda80xx_ops;
+
+struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config,
+                                   struct i2c_adapter* i2c)
+{
+       struct tda80xx_state* state = NULL;
+       int ret;
+
+       /* allocate memory for the internal state */
+       state = (struct tda80xx_state*) kmalloc(sizeof(struct tda80xx_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &tda80xx_ops, sizeof(struct dvb_frontend_ops));
+       state->spectral_inversion = INVERSION_AUTO;
+       state->code_rate = FEC_AUTO;
+       state->status = 0;
+       state->afc_loop = 0;
+
+       /* check if the demod is there */
+       if (tda80xx_writereg(state, 0x89, 0x00) < 0) goto error;
+       state->id = tda80xx_readreg(state, 0x00);
+
+       switch (state->id) {
+       case ID_TDA8044:
+               state->clk = 96000000;
+               printk("tda80xx: Detected tda8044\n");
+               break;
+
+       case ID_TDA8083:
+               state->clk = 64000000;
+               printk("tda80xx: Detected tda8083\n");
+               break;
+
+       default:
+               goto error;
+       }
+
+       /* setup IRQ */
+       if (state->config->irq) {
+               INIT_WORK(&state->worklet, tda80xx_worklet, state);
+               if ((ret = request_irq(state->config->irq, tda80xx_irq, SA_ONESHOT, "tda80xx", &state->worklet)) < 0) {
+                       printk(KERN_ERR "tda80xx: request_irq failed (%d)\n", ret);
+                       goto error;
+               }
+       }
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops tda80xx_ops = {
+
+       .info = {
+               .name = "Philips TDA80xx DVB-S",
+               .type = FE_QPSK,
+               .frequency_min = 500000,
+               .frequency_max = 2700000,
+               .frequency_stepsize = 125,
+               .symbol_rate_min = 4500000,
+               .symbol_rate_max = 45000000,
+               .caps = FE_CAN_INVERSION_AUTO |
+                       FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+                       FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+                       FE_CAN_QPSK |
+                       FE_CAN_MUTE_TS
+       },
+
+       .release = tda80xx_release,
+
+       .init = tda80xx_init,
+       .sleep = tda80xx_sleep,
+
+       .set_frontend = tda80xx_set_frontend,
+       .get_frontend = tda80xx_get_frontend,
+
+       .read_status = tda80xx_read_status,
+       .read_ber = tda80xx_read_ber,
+       .read_signal_strength = tda80xx_read_signal_strength,
+       .read_snr = tda80xx_read_snr,
+       .read_ucblocks = tda80xx_read_ucblocks,
+
+       .diseqc_send_master_cmd = tda80xx_send_diseqc_msg,
+       .diseqc_send_burst = tda80xx_send_diseqc_burst,
+       .set_tone = tda80xx_set_tone,
+       .set_voltage = tda80xx_set_voltage,
+};
+
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("Philips TDA8044 / TDA8083 DVB-S Demodulator driver");
+MODULE_AUTHOR("Felix Domke, Andreas Oberritter");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda80xx_attach);
diff --git a/drivers/media/dvb/frontends/tda80xx.h b/drivers/media/dvb/frontends/tda80xx.h
new file mode 100644 (file)
index 0000000..cd639a0
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * tda80xx.c
+ *
+ * Philips TDA8044 / TDA8083 QPSK demodulator driver
+ *
+ * Copyright (C) 2001 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2002-2004 Andreas Oberritter <obi@linuxtv.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.
+ *
+ * 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 TDA80XX_H
+#define TDA80XX_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda80xx_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* IRQ to use (0=>no IRQ used) */
+       u32 irq;
+
+       /* Register setting to use for 13v */
+       u8 volt13setting;
+
+       /* Register setting to use for 18v */
+       u8 volt18setting;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config,
+                                          struct i2c_adapter* i2c);
+
+#endif // TDA80XX_H
diff --git a/drivers/media/dvb/frontends/ves1820.h b/drivers/media/dvb/frontends/ves1820.h
new file mode 100644 (file)
index 0000000..8739fec
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+    VES1820  - Single Chip Cable Channel Receiver driver module
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+
+    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 VES1820_H
+#define VES1820_H
+
+#include <linux/dvb/frontend.h>
+
+#define VES1820_SELAGC_PWM 0
+#define VES1820_SELAGC_SIGNAMPERR 1
+
+struct ves1820_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* value of XIN to use */
+       u32 xin;
+
+       /* does inversion need inverted? */
+       u8 invert:1;
+
+       /* SELAGC control */
+       u8 selagc:1;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
+                                          struct i2c_adapter* i2c,
+                                          u8 pwm);
+
+#endif // VES1820_H
diff --git a/drivers/media/dvb/frontends/ves1x93.h b/drivers/media/dvb/frontends/ves1x93.h
new file mode 100644 (file)
index 0000000..1627e37
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+    Driver for VES1893 and VES1993 QPSK Demodulators
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de>
+    Copyright (C) 2002 Dennis Noermann <dennis.noermann@noernet.de>
+    Copyright (C) 2002-2003 Andreas Oberritter <obi@linuxtv.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.
+
+    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 VES1X93_H
+#define VES1X93_H
+
+#include <linux/dvb/frontend.h>
+
+struct ves1x93_config
+{
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* value of XIN to use */
+       u32 xin;
+
+       /* should PWM be inverted? */
+       u8 invert_pwm:1;
+
+       /* PLL maintenance */
+       int (*pll_init)(struct dvb_frontend* fe);
+       int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
+                                          struct i2c_adapter* i2c);
+
+#endif // VES1X93_H
diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
new file mode 100644 (file)
index 0000000..ff0e521
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * TTUSB DEC Frontend Driver
+ *
+ * Copyright (C) 2003-2004 Alex Woods <linux-dvb@giblets.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.
+ *
+ * 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 "dvb_frontend.h"
+#include "ttusbdecfe.h"
+
+
+#define LOF_HI                 10600000
+#define LOF_LO                 9750000
+
+struct ttusbdecfe_state {
+
+       struct dvb_frontend_ops ops;
+
+       /* configuration settings */
+       const struct ttusbdecfe_config* config;
+
+       struct dvb_frontend frontend;
+
+       u8 hi_band;
+       u8 voltage;
+};
+
+
+static int ttusbdecfe_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       *status = FE_HAS_SIGNAL | FE_HAS_VITERBI |
+                 FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK;
+
+       return 0;
+}
+
+static int ttusbdecfe_dvbt_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+       u8 b[] = { 0x00, 0x00, 0x00, 0x03,
+                  0x00, 0x00, 0x00, 0x00,
+                  0x00, 0x00, 0x00, 0x01,
+                  0x00, 0x00, 0x00, 0xff,
+                  0x00, 0x00, 0x00, 0xff };
+
+       u32 freq = htonl(p->frequency / 1000);
+       memcpy(&b[4], &freq, sizeof (u32));
+       state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL);
+
+       return 0;
+}
+
+static int ttusbdecfe_dvbs_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+       struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+
+       u8 b[] = { 0x00, 0x00, 0x00, 0x01,
+                  0x00, 0x00, 0x00, 0x00,
+                  0x00, 0x00, 0x00, 0x01,
+                  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 };
+       u32 freq;
+       u32 sym_rate;
+       u32 band;
+       u32 lnb_voltage;
+
+       freq = htonl(p->frequency +
+              (state->hi_band ? LOF_HI : LOF_LO));
+       memcpy(&b[4], &freq, sizeof(u32));
+       sym_rate = htonl(p->u.qam.symbol_rate);
+       memcpy(&b[12], &sym_rate, sizeof(u32));
+       band = htonl(state->hi_band ? LOF_HI : LOF_LO);
+       memcpy(&b[24], &band, sizeof(u32));
+       lnb_voltage = htonl(state->voltage);
+       memcpy(&b[28], &lnb_voltage, sizeof(u32));
+
+       state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL);
+
+       return 0;
+}
+
+static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd)
+{
+       struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+       u8 b[] = { 0x00, 0xff, 0x00, 0x00,
+                  0x00, 0x00, 0x00, 0x00,
+                  0x00, 0x00 };
+
+       memcpy(&b[4], cmd->msg, cmd->msg_len);
+
+       state->config->send_command(fe, 0x72,
+                                   sizeof(b) - (6 - cmd->msg_len), b,
+                                   NULL, NULL);
+
+       return 0;
+}
+
+
+static int ttusbdecfe_dvbs_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+       struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+
+       state->hi_band = (SEC_TONE_ON == tone);
+
+       return 0;
+}
+
+
+static int ttusbdecfe_dvbs_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+       struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+
+       switch (voltage) {
+       case SEC_VOLTAGE_13:
+               state->voltage = 13;
+               break;
+       case SEC_VOLTAGE_18:
+               state->voltage = 18;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void ttusbdecfe_release(struct dvb_frontend* fe)
+{
+       struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+       kfree(state);
+}
+
+static struct dvb_frontend_ops ttusbdecfe_dvbt_ops;
+
+struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* config)
+{
+       struct ttusbdecfe_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct ttusbdecfe_state*) kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       memcpy(&state->ops, &ttusbdecfe_dvbt_ops, sizeof(struct dvb_frontend_ops));
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops ttusbdecfe_dvbs_ops;
+
+struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* config)
+{
+       struct ttusbdecfe_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct ttusbdecfe_state*) kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL);
+       if (state == NULL) goto error;
+
+       /* setup the state */
+       state->config = config;
+       state->voltage = 0;
+       state->hi_band = 0;
+       memcpy(&state->ops, &ttusbdecfe_dvbs_ops, sizeof(struct dvb_frontend_ops));
+
+       /* create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       if (state) kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops ttusbdecfe_dvbt_ops = {
+
+       .info = {
+               .name                   = "TechnoTrend/Hauppauge DEC2000-t Frontend",
+               .type                   = FE_OFDM,
+               .frequency_min          = 51000000,
+               .frequency_max          = 858000000,
+               .frequency_stepsize     = 62500,
+               .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                       FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+                       FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+                       FE_CAN_HIERARCHY_AUTO,
+       },
+
+       .release = ttusbdecfe_release,
+
+       .set_frontend = ttusbdecfe_dvbt_set_frontend,
+
+       .read_status = ttusbdecfe_read_status,
+};
+
+static struct dvb_frontend_ops ttusbdecfe_dvbs_ops = {
+
+       .info = {
+               .name                   = "TechnoTrend/Hauppauge DEC3000-s Frontend",
+               .type                   = FE_QPSK,
+               .frequency_min          = 950000,
+               .frequency_max          = 2150000,
+               .frequency_stepsize     = 125,
+               .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                       FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+                       FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+                       FE_CAN_HIERARCHY_AUTO,
+       },
+
+       .release = ttusbdecfe_release,
+
+       .set_frontend = ttusbdecfe_dvbs_set_frontend,
+
+       .read_status = ttusbdecfe_read_status,
+
+       .diseqc_send_master_cmd = ttusbdecfe_dvbs_diseqc_send_master_cmd,
+       .set_voltage = ttusbdecfe_dvbs_set_voltage,
+       .set_tone = ttusbdecfe_dvbs_set_tone,
+};
+
+MODULE_DESCRIPTION("TTUSB DEC DVB-T/S Demodulator driver");
+MODULE_AUTHOR("Alex Woods/Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ttusbdecfe_dvbt_attach);
+EXPORT_SYMBOL(ttusbdecfe_dvbs_attach);
diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.h b/drivers/media/dvb/ttusb-dec/ttusbdecfe.h
new file mode 100644 (file)
index 0000000..15ccc3d
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * TTUSB DEC Driver
+ *
+ * Copyright (C) 2003-2004 Alex Woods <linux-dvb@giblets.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.
+ *
+ * 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 TTUSBDECFE_H
+#define TTUSBDECFE_H
+
+#include <linux/dvb/frontend.h>
+
+struct ttusbdecfe_config
+{
+       int (*send_command)(struct dvb_frontend* fe, const u8 command,
+                           int param_length, const u8 params[],
+                           int *result_length, u8 cmd_result[]);
+};
+
+extern struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* config);
+
+extern struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* config);
+
+#endif // TTUSBDECFE_H
diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c
new file mode 100644 (file)
index 0000000..8db3503
--- /dev/null
@@ -0,0 +1,916 @@
+/*
+ * Colour AR M64278(VGA) driver for Video4Linux
+ *
+ * Copyright (C) 2003  Takeo Takahashi <takahashi.takeo@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Some code is taken from AR driver sample program for M3T-M32700UT.
+ *
+ * AR driver sample (M32R SDK):
+ *     Copyright (c) 2003 RENESAS TECHNOROGY CORPORATION
+ *     AND RENESAS SOLUTIONS CORPORATION
+ *     All Rights Reserved.
+ *
+ * 2003-09-01: Support w3cam by Takeo Takahashi
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/videodev.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/m32r.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#if 0
+#define DEBUG(n, args...) printk(args)
+#define CHECK_LOST     1
+#else
+#define DEBUG(n, args...)
+#define CHECK_LOST     0
+#endif
+
+/*
+ * USE_INT is always 0, interrupt mode is not available
+ * on linux due to lack of speed
+ */
+#define USE_INT                0       /* Don't modify */
+
+#define VERSION        "0.03"
+
+#define ar_inl(addr)           inl((unsigned long)(addr))
+#define ar_outl(val, addr)     outl((unsigned long)(val),(unsigned long)(addr))
+
+extern struct cpuinfo_m32r     boot_cpu_data;
+
+/*
+ * CCD pixel size
+ *     Note that M32700UT does not support CIF mode, but QVGA is
+ *     supported by M32700UT hardware using VGA mode of AR LSI.
+ *
+ *     Supported: VGA  (Normal mode, Interlace mode)
+ *                QVGA (Always Interlace mode of VGA)
+ *
+ */
+#define AR_WIDTH_VGA           640
+#define AR_HEIGHT_VGA          480
+#define AR_WIDTH_QVGA          320
+#define AR_HEIGHT_QVGA         240
+#define MIN_AR_WIDTH           AR_WIDTH_QVGA
+#define MIN_AR_HEIGHT          AR_HEIGHT_QVGA
+#define MAX_AR_WIDTH           AR_WIDTH_VGA
+#define MAX_AR_HEIGHT          AR_HEIGHT_VGA
+
+/* bits & bytes per pixel */
+#define AR_BITS_PER_PIXEL      16
+#define AR_BYTES_PER_PIXEL     (AR_BITS_PER_PIXEL/8)
+
+/* line buffer size */
+#define AR_LINE_BYTES_VGA      (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL)
+#define AR_LINE_BYTES_QVGA     (AR_WIDTH_QVGA * AR_BYTES_PER_PIXEL)
+#define MAX_AR_LINE_BYTES      AR_LINE_BYTES_VGA
+
+/* frame size & type */
+#define AR_FRAME_BYTES_VGA \
+       (AR_WIDTH_VGA * AR_HEIGHT_VGA * AR_BYTES_PER_PIXEL)
+#define AR_FRAME_BYTES_QVGA \
+       (AR_WIDTH_QVGA * AR_HEIGHT_QVGA * AR_BYTES_PER_PIXEL)
+#define MAX_AR_FRAME_BYTES \
+       (MAX_AR_WIDTH * MAX_AR_HEIGHT * AR_BYTES_PER_PIXEL)
+
+#define AR_MAX_FRAME           15
+
+/* capture size */
+#define AR_SIZE_VGA            0
+#define AR_SIZE_QVGA           1
+
+/* capture mode */
+#define AR_MODE_INTERLACE      0
+#define AR_MODE_NORMAL         1
+
+struct ar_device {
+       struct video_device *vdev;
+       unsigned int start_capture;     /* duaring capture in INT. mode. */
+#if USE_INT
+       unsigned char *line_buff;       /* DMA line buffer */
+#endif
+       unsigned char *frame[MAX_AR_HEIGHT];    /* frame data */
+       short size;                     /* capture size */
+       short mode;                     /* capture mode */
+       int width, height;
+       int frame_bytes, line_bytes;
+       wait_queue_head_t wait;
+       struct semaphore lock;
+};
+
+static int video_nr = -1;      /* video device number (first free) */
+static unsigned char   yuv[MAX_AR_FRAME_BYTES];
+
+/* module parameters */
+/* default frequency */
+#define DEFAULT_FREQ   50      /* 50 or 75 (MHz) is available as BCLK */
+static int freq = DEFAULT_FREQ;        /* BCLK: available 50 or 70 (MHz) */
+static int vga = 0;            /* default mode(0:QVGA mode, other:VGA mode) */
+static int vga_interlace = 0;  /* 0 is normal mode for, else interlace mode */
+MODULE_PARM(freq, "i");
+MODULE_PARM(vga, "i");
+MODULE_PARM(vga_interlace, "i");
+
+static int ar_initialize(struct video_device *dev);
+
+static inline void wait_for_vsync(void)
+{
+       while (ar_inl(ARVCR0) & ARVCR0_VDS)     /* wait for VSYNC */
+               cpu_relax();
+       while (!(ar_inl(ARVCR0) & ARVCR0_VDS))  /* wait for VSYNC */
+               cpu_relax();
+}
+
+static inline void wait_acknowledge(void)
+{
+       int i;
+
+       for (i = 0; i < 1000; i++)
+               cpu_relax();
+       while (ar_inl(PLDI2CSTS) & PLDI2CSTS_NOACK)
+               cpu_relax();
+}
+
+/*******************************************************************
+ * I2C functions
+ *******************************************************************/
+void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2,
+        unsigned long data3)
+{
+       int i;
+
+       /* Slave Address */
+       ar_outl(addr, PLDI2CDATA);
+       wait_for_vsync();
+
+       /* Start */
+       ar_outl(1, PLDI2CCND);
+       wait_acknowledge();
+
+       /* Transfer data 1 */
+       ar_outl(data1, PLDI2CDATA);
+       wait_for_vsync();
+       ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
+       wait_acknowledge();
+
+       /* Transfer data 2 */
+       ar_outl(data2, PLDI2CDATA);
+       wait_for_vsync();
+       ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
+       wait_acknowledge();
+
+       if (n == 3) {
+               /* Transfer data 3 */
+               ar_outl(data3, PLDI2CDATA);
+               wait_for_vsync();
+               ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
+               wait_acknowledge();
+       }
+
+       /* Stop */
+       for (i = 0; i < 100; i++)
+               cpu_relax();
+       ar_outl(2, PLDI2CCND);
+       ar_outl(2, PLDI2CCND);
+
+       while (ar_inl(PLDI2CSTS) & PLDI2CSTS_BB)
+               cpu_relax();
+}
+
+
+void init_iic(void)
+{
+       DEBUG(1, "init_iic:\n");
+
+       /*
+        * ICU Setting (iic)
+        */
+       /* I2C Setting */
+       ar_outl(0x0, PLDI2CCR);         /* I2CCR Disable                   */
+       ar_outl(0x0300, PLDI2CMOD);     /* I2CMOD ACK/8b-data/7b-addr/auto */
+       ar_outl(0x1, PLDI2CACK);        /* I2CACK ACK                      */
+
+       /* I2C CLK */
+       /* 50MH-100k */
+       if (freq == 75) {
+               ar_outl(369, PLDI2CFREQ);       /* BCLK = 75MHz */
+       } else if (freq == 50) {
+               ar_outl(244, PLDI2CFREQ);       /* BCLK = 50MHz */
+       } else {
+               ar_outl(244, PLDI2CFREQ);       /* default: BCLK = 50MHz */
+       }
+       ar_outl(0x1, PLDI2CCR);         /* I2CCR Enable */
+}
+
+/**************************************************************************
+ *
+ * Video4Linux Interface functions
+ *
+ **************************************************************************/
+
+static inline void disable_dma(void)
+{
+       ar_outl(0x8000, M32R_DMAEN_PORTL);      /* disable DMA0 */
+}
+
+static inline void enable_dma(void)
+{
+       ar_outl(0x8080, M32R_DMAEN_PORTL);      /* enable DMA0 */
+}
+
+static inline void clear_dma_status(void)
+{
+       ar_outl(0x8000, M32R_DMAEDET_PORTL);    /* clear status */
+}
+
+static inline void wait_for_vertical_sync(int exp_line)
+{
+#if CHECK_LOST
+       int tmout = 10000;      /* FIXME */
+       int l;
+
+       /*
+        * check HCOUNT because we cannot check vertical sync.
+        */
+       for (; tmout >= 0; tmout--) {
+               l = ar_inl(ARVHCOUNT);
+               if (l == exp_line)
+                       break;
+       }
+       if (tmout < 0)
+               printk("arv: lost %d -> %d\n", exp_line, l);
+#else
+       while (ar_inl(ARVHCOUNT) != exp_line)
+               cpu_relax();
+#endif
+}
+
+static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+       struct video_device *v = video_devdata(file);
+       struct ar_device *ar = v->priv;
+       long ret = ar->frame_bytes;             /* return read bytes */
+       unsigned long arvcr1 = 0;
+       unsigned long flags;
+       unsigned char *p;
+       int h, w;
+       unsigned char *py, *pu, *pv;
+#if ! USE_INT
+       int l;
+#endif
+
+       DEBUG(1, "ar_read()\n");
+
+       if (ar->size == AR_SIZE_QVGA)
+               arvcr1 |= ARVCR1_QVGA;
+       if (ar->mode == AR_MODE_NORMAL)
+               arvcr1 |= ARVCR1_NORMAL;
+
+       down(&ar->lock);
+
+#if USE_INT
+       local_irq_save(flags);
+       disable_dma();
+       ar_outl(0xa1871300, M32R_DMA0CR0_PORTL);
+       ar_outl(0x01000000, M32R_DMA0CR1_PORTL);
+
+       /* set AR FIFO address as source(BSEL5) */
+       ar_outl(ARDATA32, M32R_DMA0CSA_PORTL);
+       ar_outl(ARDATA32, M32R_DMA0RSA_PORTL);
+       ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL);     /* destination addr. */
+       ar_outl(ar->line_buff, M32R_DMA0RDA_PORTL);     /* reload address */
+       ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL);  /* byte count (bytes) */
+       ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL);  /* reload count (bytes) */
+
+       /*
+        * Okey , kicks AR LSI to invoke an interrupt
+        */
+       ar->start_capture = 0;
+       ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1);
+       local_irq_restore(flags);
+       /* .... AR interrupts .... */
+       interruptible_sleep_on(&ar->wait);
+       if (signal_pending(current)) {
+               printk("arv: interrupted while get frame data.\n");
+               ret = -EINTR;
+               goto out_up;
+       }
+#else  /* ! USE_INT */
+       /* polling */
+       ar_outl(arvcr1, ARVCR1);
+       disable_dma();
+       ar_outl(0x8000, M32R_DMAEDET_PORTL);
+       ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
+       ar_outl(0x01000000, M32R_DMA0CR1_PORTL);
+       ar_outl(ARDATA32, M32R_DMA0CSA_PORTL);
+       ar_outl(ARDATA32, M32R_DMA0RSA_PORTL);
+       ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL);
+       ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL);
+
+       local_irq_save(flags);
+       while (ar_inl(ARVHCOUNT) != 0)          /* wait for 0 */
+               cpu_relax();
+       if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) {
+               for (h = 0; h < ar->height; h++) {
+                       wait_for_vertical_sync(h);
+                       if (h < (AR_HEIGHT_VGA/2))
+                               l = h << 1;
+                       else
+                               l = (((h - (AR_HEIGHT_VGA/2)) << 1) + 1);
+                       ar_outl(virt_to_phys(ar->frame[l]), M32R_DMA0CDA_PORTL);
+                       enable_dma();
+                       while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000))
+                               cpu_relax();
+                       disable_dma();
+                       clear_dma_status();
+                       ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
+               }
+       } else {
+               for (h = 0; h < ar->height; h++) {
+                       wait_for_vertical_sync(h);
+                       ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL);
+                       enable_dma();
+                       while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000))
+                               cpu_relax();
+                       disable_dma();
+                       clear_dma_status();
+                       ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
+               }
+       }
+       local_irq_restore(flags);
+#endif /* ! USE_INT */
+
+       /*
+        * convert YUV422 to YUV422P
+        *      +--------------------+
+        *      |  Y0,Y1,...         |
+        *      |  ..............Yn  |
+        *      +--------------------+
+        *      |  U0,U1,........Un  |
+        *      +--------------------+
+        *      |  V0,V1,........Vn  |
+        *      +--------------------+
+        */
+       py = yuv;
+       pu = py + (ar->frame_bytes / 2);
+       pv = pu + (ar->frame_bytes / 4);
+       for (h = 0; h < ar->height; h++) {
+               p = ar->frame[h];
+               for (w = 0; w < ar->line_bytes; w += 4) {
+                       *py++ = *p++;
+                       *pu++ = *p++;
+                       *py++ = *p++;
+                       *pv++ = *p++;
+               }
+       }
+       if (copy_to_user(buf, yuv, ar->frame_bytes)) {
+               printk("arv: failed while copy_to_user yuv.\n");
+               ret = -EFAULT;
+               goto out_up;
+       }
+       DEBUG(1, "ret = %d\n", ret);
+out_up:
+       up(&ar->lock);
+       return ret;
+}
+
+static int ar_do_ioctl(struct inode *inode, struct file *file,
+                      unsigned int cmd, void *arg)
+{
+       struct video_device *dev = video_devdata(file);
+       struct ar_device *ar = dev->priv;
+
+       DEBUG(1, "ar_ioctl()\n");
+       switch(cmd) {
+       case VIDIOCGCAP:
+       {
+               struct video_capability *b = arg;
+               DEBUG(1, "VIDIOCGCAP:\n");
+               strcpy(b->name, ar->vdev->name);
+               b->type = VID_TYPE_CAPTURE;
+               b->channels = 0;
+               b->audios = 0;
+               b->maxwidth = MAX_AR_WIDTH;
+               b->maxheight = MAX_AR_HEIGHT;
+               b->minwidth = MIN_AR_WIDTH;
+               b->minheight = MIN_AR_HEIGHT;
+               return 0;
+       }
+       case VIDIOCGCHAN:
+               DEBUG(1, "VIDIOCGCHAN:\n");
+               return 0;
+       case VIDIOCSCHAN:
+               DEBUG(1, "VIDIOCSCHAN:\n");
+               return 0;
+       case VIDIOCGTUNER:
+               DEBUG(1, "VIDIOCGTUNER:\n");
+               return 0;
+       case VIDIOCSTUNER:
+               DEBUG(1, "VIDIOCSTUNER:\n");
+               return 0;
+       case VIDIOCGPICT:
+               DEBUG(1, "VIDIOCGPICT:\n");
+               return 0;
+       case VIDIOCSPICT:
+               DEBUG(1, "VIDIOCSPICT:\n");
+               return 0;
+       case VIDIOCCAPTURE:
+               DEBUG(1, "VIDIOCCAPTURE:\n");
+               return -EINVAL;
+       case VIDIOCGWIN:
+       {
+               struct video_window *w = arg;
+               DEBUG(1, "VIDIOCGWIN:\n");
+               memset(w, 0, sizeof(w));
+               w->width = ar->width;
+               w->height = ar->height;
+               return 0;
+       }
+       case VIDIOCSWIN:
+       {
+               struct video_window *w = arg;
+               DEBUG(1, "VIDIOCSWIN:\n");
+               if ((w->width != AR_WIDTH_VGA || w->height != AR_HEIGHT_VGA) &&
+                   (w->width != AR_WIDTH_QVGA || w->height != AR_HEIGHT_QVGA))
+                               return -EINVAL;
+
+               down(&ar->lock);
+               ar->width = w->width;
+               ar->height = w->height;
+               if (ar->width == AR_WIDTH_VGA) {
+                       ar->size = AR_SIZE_VGA;
+                       ar->frame_bytes = AR_FRAME_BYTES_VGA;
+                       ar->line_bytes = AR_LINE_BYTES_VGA;
+                       if (vga_interlace)
+                               ar->mode = AR_MODE_INTERLACE;
+                       else
+                               ar->mode = AR_MODE_NORMAL;
+               } else {
+                       ar->size = AR_SIZE_QVGA;
+                       ar->frame_bytes = AR_FRAME_BYTES_QVGA;
+                       ar->line_bytes = AR_LINE_BYTES_QVGA;
+                       ar->mode = AR_MODE_INTERLACE;
+               }
+               up(&ar->lock);
+               return 0;
+       }
+       case VIDIOCGFBUF:
+               DEBUG(1, "VIDIOCGFBUF:\n");
+               return -EINVAL;
+       case VIDIOCSFBUF:
+               DEBUG(1, "VIDIOCSFBUF:\n");
+               return -EINVAL;
+       case VIDIOCKEY:
+               DEBUG(1, "VIDIOCKEY:\n");
+               return 0;
+       case VIDIOCGFREQ:
+               DEBUG(1, "VIDIOCGFREQ:\n");
+               return -EINVAL;
+       case VIDIOCSFREQ:
+               DEBUG(1, "VIDIOCSFREQ:\n");
+               return -EINVAL;
+       case VIDIOCGAUDIO:
+               DEBUG(1, "VIDIOCGAUDIO:\n");
+               return -EINVAL;
+       case VIDIOCSAUDIO:
+               DEBUG(1, "VIDIOCSAUDIO:\n");
+               return -EINVAL;
+       case VIDIOCSYNC:
+               DEBUG(1, "VIDIOCSYNC:\n");
+               return -EINVAL;
+       case VIDIOCMCAPTURE:
+               DEBUG(1, "VIDIOCMCAPTURE:\n");
+               return -EINVAL;
+       case VIDIOCGMBUF:
+               DEBUG(1, "VIDIOCGMBUF:\n");
+               return -EINVAL;
+       case VIDIOCGUNIT:
+               DEBUG(1, "VIDIOCGUNIT:\n");
+               return -EINVAL;
+       case VIDIOCGCAPTURE:
+               DEBUG(1, "VIDIOCGCAPTURE:\n");
+               return -EINVAL;
+       case VIDIOCSCAPTURE:
+               DEBUG(1, "VIDIOCSCAPTURE:\n");
+               return -EINVAL;
+       case VIDIOCSPLAYMODE:
+               DEBUG(1, "VIDIOCSPLAYMODE:\n");
+               return -EINVAL;
+       case VIDIOCSWRITEMODE:
+               DEBUG(1, "VIDIOCSWRITEMODE:\n");
+               return -EINVAL;
+       case VIDIOCGPLAYINFO:
+               DEBUG(1, "VIDIOCGPLAYINFO:\n");
+               return -EINVAL;
+       case VIDIOCSMICROCODE:
+               DEBUG(1, "VIDIOCSMICROCODE:\n");
+               return -EINVAL;
+       case VIDIOCGVBIFMT:
+               DEBUG(1, "VIDIOCGVBIFMT:\n");
+               return -EINVAL;
+       case VIDIOCSVBIFMT:
+               DEBUG(1, "VIDIOCSVBIFMT:\n");
+               return -EINVAL;
+       default:
+               DEBUG(1, "Unknown ioctl(0x%08x)\n", cmd);
+               return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+static int ar_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+                   unsigned long arg)
+{
+       return video_usercopy(inode, file, cmd, arg, ar_do_ioctl);
+}
+
+#if USE_INT
+/*
+ * Interrupt handler
+ */
+static void ar_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+       struct ar_device *ar = dev;
+       unsigned int line_count;
+       unsigned int line_number;
+       unsigned int arvcr1;
+
+       line_count = ar_inl(ARVHCOUNT);                 /* line number */
+       if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) {
+               /* operations for interlace mode */
+               if ( line_count < (AR_HEIGHT_VGA/2) )   /* even line */
+                       line_number = (line_count << 1);
+               else                                    /* odd line */
+                       line_number =
+                       (((line_count - (AR_HEIGHT_VGA/2)) << 1) + 1);
+       } else {
+               line_number = line_count;
+       }
+
+       if (line_number == 0) {
+               /*
+                * It is an interrupt for line 0.
+                * we have to start capture.
+                */
+               disable_dma();
+#if 0
+               ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL);     /* needless? */
+#endif
+               memcpy(ar->frame[0], ar->line_buff, ar->line_bytes);
+#if 0
+               ar_outl(0xa1861300, M32R_DMA0CR0_PORTL);
+#endif
+               enable_dma();
+               ar->start_capture = 1;                  /* during capture */
+               return;
+       }
+
+       if (ar->start_capture == 1 && line_number <= (ar->height - 1)) {
+               disable_dma();
+               memcpy(ar->frame[line_number], ar->line_buff, ar->line_bytes);
+
+               /*
+                * if captured all line of a frame, disable AR interrupt
+                * and wake a process up.
+                */
+               if (line_number == (ar->height - 1)) {  /* end  of line */
+
+                       ar->start_capture = 0;
+
+                       /* disable AR interrupt request */
+                       arvcr1 = ar_inl(ARVCR1);
+                       arvcr1 &= ~ARVCR1_HIEN;         /* clear int. flag */
+                       ar_outl(arvcr1, ARVCR1);        /* disable */
+                       wake_up_interruptible(&ar->wait);
+               } else {
+#if 0
+                       ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL);
+                       ar_outl(0xa1861300, M32R_DMA0CR0_PORTL);
+#endif
+                       enable_dma();
+               }
+       }
+}
+#endif
+
+/*
+ * ar_initialize()
+ *     ar_initialize() is called by video_register_device() and
+ *     initializes AR LSI and peripherals.
+ *
+ *     -1 is returned in all failures.
+ *     0 is returned in success.
+ *
+ */
+static int ar_initialize(struct video_device *dev)
+{
+       struct ar_device *ar = (struct ar_device *)dev->priv;
+       unsigned long cr = 0;
+       int i,found=0;
+
+       DEBUG(1, "ar_initialize:\n");
+
+       /*
+        * initialize AR LSI
+        */
+       ar_outl(0, ARVCR0);             /* assert reset of AR LSI */
+       for (i = 0; i < 0x18; i++)      /* wait for over 10 cycles @ 27MHz */
+               cpu_relax();
+       ar_outl(ARVCR0_RST, ARVCR0);    /* negate reset of AR LSI (enable) */
+       for (i = 0; i < 0x40d; i++)     /* wait for over 420 cycles @ 27MHz */
+               cpu_relax();
+
+       /* AR uses INT3 of CPU as interrupt pin. */
+       ar_outl(ARINTSEL_INT3, ARINTSEL);
+
+       if (ar->size == AR_SIZE_QVGA)
+               cr |= ARVCR1_QVGA;
+       if (ar->mode == AR_MODE_NORMAL)
+               cr |= ARVCR1_NORMAL;
+       ar_outl(cr, ARVCR1);
+
+       /*
+        * Initialize IIC so that CPU can communicate with AR LSI,
+        * and send boot commands to AR LSI.
+        */
+       init_iic();
+
+       for (i = 0; i < 0x100000; i++) {        /* > 0xa1d10,  56ms */
+               if ((ar_inl(ARVCR0) & ARVCR0_VDS)) {    /* VSYNC */
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (found == 0)
+               return -ENODEV;
+
+       printk("arv: Initializing ");
+
+       iic(2,0x78,0x11,0x01,0x00);     /* start */
+       iic(3,0x78,0x12,0x00,0x06);
+       iic(3,0x78,0x12,0x12,0x30);
+       iic(3,0x78,0x12,0x15,0x58);
+       iic(3,0x78,0x12,0x17,0x30);
+       printk(".");
+       iic(3,0x78,0x12,0x1a,0x97);
+       iic(3,0x78,0x12,0x1b,0xff);
+       iic(3,0x78,0x12,0x1c,0xff);
+       iic(3,0x78,0x12,0x26,0x10);
+       iic(3,0x78,0x12,0x27,0x00);
+       printk(".");
+       iic(2,0x78,0x34,0x02,0x00);
+       iic(2,0x78,0x7a,0x10,0x00);
+       iic(2,0x78,0x80,0x39,0x00);
+       iic(2,0x78,0x81,0xe6,0x00);
+       iic(2,0x78,0x8d,0x00,0x00);
+       printk(".");
+       iic(2,0x78,0x8e,0x0c,0x00);
+       iic(2,0x78,0x8f,0x00,0x00);
+#if 0
+       iic(2,0x78,0x90,0x00,0x00);     /* AWB on=1 off=0 */
+#endif
+       iic(2,0x78,0x93,0x01,0x00);
+       iic(2,0x78,0x94,0xcd,0x00);
+       iic(2,0x78,0x95,0x00,0x00);
+       printk(".");
+       iic(2,0x78,0x96,0xa0,0x00);
+       iic(2,0x78,0x97,0x00,0x00);
+       iic(2,0x78,0x98,0x60,0x00);
+       iic(2,0x78,0x99,0x01,0x00);
+       iic(2,0x78,0x9a,0x19,0x00);
+       printk(".");
+       iic(2,0x78,0x9b,0x02,0x00);
+       iic(2,0x78,0x9c,0xe8,0x00);
+       iic(2,0x78,0x9d,0x02,0x00);
+       iic(2,0x78,0x9e,0x2e,0x00);
+       iic(2,0x78,0xb8,0x78,0x00);
+       iic(2,0x78,0xba,0x05,0x00);
+#if 0
+       iic(2,0x78,0x83,0x8c,0x00);     /* brightness */
+#endif
+       printk(".");
+
+       /* color correction */
+       iic(3,0x78,0x49,0x00,0x95);     /* a            */
+       iic(3,0x78,0x49,0x01,0x96);     /* b            */
+       iic(3,0x78,0x49,0x03,0x85);     /* c            */
+       iic(3,0x78,0x49,0x04,0x97);     /* d            */
+       iic(3,0x78,0x49,0x02,0x7e);     /* e(Lo)        */
+       iic(3,0x78,0x49,0x05,0xa4);     /* f(Lo)        */
+       iic(3,0x78,0x49,0x06,0x04);     /* e(Hi)        */
+       iic(3,0x78,0x49,0x07,0x04);     /* e(Hi)        */
+       iic(2,0x78,0x48,0x01,0x00);     /* on=1 off=0   */
+
+       printk(".");
+       iic(2,0x78,0x11,0x00,0x00);     /* end */
+       printk(" done\n");
+       return 0;
+}
+
+
+void ar_release(struct video_device *vfd)
+{
+       struct ar_device *ar = vfd->priv;
+       down(&ar->lock);
+       video_device_release(vfd);
+}
+
+/****************************************************************************
+ *
+ * Video4Linux Module functions
+ *
+ ****************************************************************************/
+static struct file_operations ar_fops = {
+       .owner          = THIS_MODULE,
+       .open           = video_exclusive_open,
+       .release        = video_exclusive_release,
+       .read           = ar_read,
+       .ioctl          = ar_ioctl,
+       .llseek         = no_llseek,
+};
+
+static struct video_device ar_template = {
+       .owner          = THIS_MODULE,
+       .name           = "Colour AR VGA",
+       .type           = VID_TYPE_CAPTURE,
+       .hardware       = VID_HARDWARE_ARV,
+       .fops           = &ar_fops,
+       .release        = ar_release,
+       .minor          = -1,
+};
+
+#define ALIGN4(x)      ((((int)(x)) & 0x3) == 0)
+static struct ar_device ardev;
+
+static int __init ar_init(void)
+{
+       struct ar_device *ar;
+       int ret;
+       int i;
+
+       DEBUG(1, "ar_init:\n");
+       ret = -EIO;
+       printk(KERN_INFO "arv: Colour AR VGA driver %s\n", VERSION);
+
+       ar = &ardev;
+       memset(ar, 0, sizeof(struct ar_device));
+
+#if USE_INT
+       /* allocate a DMA buffer for 1 line.  */
+       ar->line_buff = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL | GFP_DMA);
+       if (ar->line_buff == NULL || ! ALIGN4(ar->line_buff)) {
+               printk("arv: buffer allocation failed for DMA.\n");
+               ret = -ENOMEM;
+               goto out_end;
+       }
+#endif
+       /* allocate buffers for a frame */
+       for (i = 0; i < MAX_AR_HEIGHT; i++) {
+               ar->frame[i] = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL);
+               if (ar->frame[i] == NULL || ! ALIGN4(ar->frame[i])) {
+                       printk("arv: buffer allocation failed for frame.\n");
+                       ret = -ENOMEM;
+                       goto out_line_buff;
+               }
+       }
+
+       ar->vdev = video_device_alloc();
+       if (!ar->vdev) {
+               printk(KERN_ERR "arv: video_device_alloc() failed\n");
+               return -ENOMEM;
+       }
+       memcpy(ar->vdev, &ar_template, sizeof(ar_template));
+       ar->vdev->priv = ar;
+
+       if (vga) {
+               ar->width       = AR_WIDTH_VGA;
+               ar->height      = AR_HEIGHT_VGA;
+               ar->size        = AR_SIZE_VGA;
+               ar->frame_bytes = AR_FRAME_BYTES_VGA;
+               ar->line_bytes  = AR_LINE_BYTES_VGA;
+               if (vga_interlace)
+                       ar->mode = AR_MODE_INTERLACE;
+               else
+                       ar->mode = AR_MODE_NORMAL;
+       } else {
+               ar->width       = AR_WIDTH_QVGA;
+               ar->height      = AR_HEIGHT_QVGA;
+               ar->size        = AR_SIZE_QVGA;
+               ar->frame_bytes = AR_FRAME_BYTES_QVGA;
+               ar->line_bytes  = AR_LINE_BYTES_QVGA;
+               ar->mode        = AR_MODE_INTERLACE;
+       }
+       init_MUTEX(&ar->lock);
+       init_waitqueue_head(&ar->wait);
+
+#if USE_INT
+       if (request_irq(M32R_IRQ_INT3, ar_interrupt, 0, "arv", ar)) {
+               printk("arv: request_irq(%d) failed.\n", M32R_IRQ_INT3);
+               ret = -EIO;
+               goto out_irq;
+       }
+#endif
+
+       if (ar_initialize(ar->vdev) != 0) {
+               printk("arv: M64278 not found.\n");
+               ret = -ENODEV;
+               goto out_dev;
+       }
+
+       /*
+        * ok, we can initialize h/w according to parameters,
+        * so register video device as a frame grabber type.
+        * device is named "video[0-64]".
+        * video_register_device() initializes h/w using ar_initialize().
+        */
+       if (video_register_device(ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) {
+               /* return -1, -ENFILE(full) or others */
+               printk("arv: register video (Colour AR) failed.\n");
+               ret = -ENODEV;
+               goto out_dev;
+       }
+
+       printk("video%d: Found M64278 VGA (IRQ %d, Freq %dMHz).\n",
+               ar->vdev->minor, M32R_IRQ_INT3, freq);
+
+       return 0;
+
+out_dev:
+#if USE_INT
+       free_irq(M32R_IRQ_INT3, ar);
+
+out_irq:
+#endif
+       for (i = 0; i < MAX_AR_HEIGHT; i++) {
+               if (ar->frame[i])
+                       kfree(ar->frame[i]);
+       }
+
+out_line_buff:
+#if USE_INT
+       kfree(ar->line_buff);
+
+out_end:
+#endif
+       return ret;
+}
+
+
+static int __init ar_init_module(void)
+{
+       freq = (boot_cpu_data.bus_clock / 1000000);
+       printk("arv: Bus clock %d\n", freq);
+       if (freq != 50 && freq != 75)
+               freq = DEFAULT_FREQ;
+       return ar_init();
+}
+
+static void __exit ar_cleanup_module(void)
+{
+       struct ar_device *ar;
+       int i;
+
+       ar = &ardev;
+       video_unregister_device(ar->vdev);
+#if USE_INT
+       free_irq(M32R_IRQ_INT3, ar);
+#endif
+       for (i = 0; i < MAX_AR_HEIGHT; i++) {
+               if (ar->frame[i])
+                       kfree(ar->frame[i]);
+       }
+#if USE_INT
+       kfree(ar->line_buff);
+#endif
+}
+
+module_init(ar_init_module);
+module_exit(ar_cleanup_module);
+
+MODULE_AUTHOR("Takeo Takahashi <takahashi.takeo@renesas.com>");
+MODULE_DESCRIPTION("Colour AR M64278(VGA) for Video4Linux");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
new file mode 100644 (file)
index 0000000..ef56a5d
--- /dev/null
@@ -0,0 +1,904 @@
+/*
+ * $Id: cx88-blackbird.c,v 1.17 2004/11/07 13:17:15 kraxel Exp $
+ *
+ *  Support for a cx23416 mpeg encoder via cx2388x host port.
+ *  "blackbird" reference design.
+ *
+ *    (c) 2004 Jelle Foks <jelle@foks.8m.com>
+ *    (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ *
+ *  Includes parts from the ivtv driver( http://ivtv.sourceforge.net/),
+ *
+ *  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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include "cx88.h"
+
+MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int mpegbufs = 8;
+module_param(mpegbufs,int,0644);
+MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32");
+
+static unsigned int debug = 0;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages [blackbird]");
+
+#define dprintk(level,fmt, arg...)     if (debug >= level) \
+       printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg)
+
+static LIST_HEAD(cx8802_devlist);
+
+/* ------------------------------------------------------------------ */
+
+#define BLACKBIRD_FIRM_ENC_FILENAME "blackbird-fw-enc.bin"
+#define BLACKBIRD_FIRM_IMAGE_SIZE 256*1024
+
+/* defines below are from ivtv-driver.h */
+
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+
+/*Firmware API commands*/
+#define IVTV_API_ENC_PING_FW 0x00000080
+#define IVTV_API_ENC_GETVER 0x000000C4
+#define IVTV_API_ENC_HALT_FW 0x000000C3
+#define IVTV_API_STD_TIMEOUT 0x00010000 /*units??*/
+//#define IVTV_API_ASSIGN_PGM_INDEX_INFO 0x000000c7
+#define IVTV_API_ASSIGN_STREAM_TYPE 0x000000b9
+#define IVTV_API_ASSIGN_OUTPUT_PORT 0x000000bb
+#define IVTV_API_ASSIGN_FRAMERATE 0x0000008f
+#define IVTV_API_ASSIGN_FRAME_SIZE 0x00000091
+#define IVTV_API_ASSIGN_ASPECT_RATIO 0x00000099
+#define IVTV_API_ASSIGN_BITRATES 0x00000095
+#define IVTV_API_ASSIGN_GOP_PROPERTIES 0x00000097
+#define IVTV_API_ASSIGN_3_2_PULLDOWN 0x000000b1
+#define IVTV_API_ASSIGN_GOP_CLOSURE 0x000000c5
+#define IVTV_API_ASSIGN_AUDIO_PROPERTIES 0x000000bd
+#define IVTV_API_ASSIGN_DNR_FILTER_MODE 0x0000009b
+#define IVTV_API_ASSIGN_DNR_FILTER_PROPS 0x0000009d
+#define IVTV_API_ASSIGN_CORING_LEVELS 0x0000009f
+#define IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE 0x000000a1
+#define IVTV_API_ASSIGN_FRAME_DROP_RATE 0x000000d0
+#define IVTV_API_ASSIGN_PLACEHOLDER 0x000000d8
+#define IVTV_API_MUTE_VIDEO 0x000000d9
+#define IVTV_API_MUTE_AUDIO 0x000000da
+#define IVTV_API_INITIALIZE_INPUT 0x000000cd
+#define IVTV_API_REFRESH_INPUT 0x000000d3
+#define IVTV_API_ASSIGN_NUM_VSYNC_LINES 0x000000d6
+#define IVTV_API_BEGIN_CAPTURE 0x00000081
+//#define IVTV_API_PAUSE_ENCODER 0x000000d2
+//#define IVTV_API_EVENT_NOTIFICATION 0x000000d5
+#define IVTV_API_END_CAPTURE 0x00000082
+
+/* Registers */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/)
+
+/* ------------------------------------------------------------------ */
+
+static void host_setup(struct cx88_core *core)
+{
+       /* toggle reset of the host */
+       cx_write(MO_GPHST_SOFT_RST, 1);
+       udelay(100);
+       cx_write(MO_GPHST_SOFT_RST, 0);
+       udelay(100);
+
+       /* host port setup */
+       cx_write(MO_GPHST_WSC, 0x44444444U);
+       cx_write(MO_GPHST_XFR, 0);
+       cx_write(MO_GPHST_WDTH, 15);
+       cx_write(MO_GPHST_HDSHK, 0);
+       cx_write(MO_GPHST_MUX16, 0x44448888U);
+       cx_write(MO_GPHST_MODE, 0);
+}
+
+/* ------------------------------------------------------------------ */
+
+#define P1_MDATA0 0x390000
+#define P1_MDATA1 0x390001
+#define P1_MDATA2 0x390002
+#define P1_MDATA3 0x390003
+#define P1_MADDR2 0x390004
+#define P1_MADDR1 0x390005
+#define P1_MADDR0 0x390006
+#define P1_RDATA0 0x390008
+#define P1_RDATA1 0x390009
+#define P1_RDATA2 0x39000A
+#define P1_RDATA3 0x39000B
+#define P1_RADDR0 0x39000C
+#define P1_RADDR1 0x39000D
+#define P1_RRDWR  0x39000E
+
+static int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(1);
+       u32 gpio0,need;
+
+       need = state ? 2 : 0;
+       for (;;) {
+               gpio0 = cx_read(MO_GP0_IO) & 2;
+               if (need == gpio0)
+                       return 0;
+               if (time_after(jiffies,timeout))
+                       return -1;
+               udelay(1);
+       }
+}
+
+static int memory_write(struct cx88_core *core, u32 address, u32 value)
+{
+       /* Warning: address is dword address (4 bytes) */
+       cx_writeb(P1_MDATA0, (unsigned int)value);
+       cx_writeb(P1_MDATA1, (unsigned int)(value >> 8));
+       cx_writeb(P1_MDATA2, (unsigned int)(value >> 16));
+       cx_writeb(P1_MDATA3, (unsigned int)(value >> 24));
+       cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40);
+       cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
+       cx_writeb(P1_MADDR0, (unsigned int)address);
+       cx_read(P1_MDATA0);
+       cx_read(P1_MADDR0);
+
+       return wait_ready_gpio0_bit1(core,1);
+}
+
+static int memory_read(struct cx88_core *core, u32 address, u32 *value)
+{
+        int retval;
+       u32 val;
+
+       /* Warning: address is dword address (4 bytes) */
+       cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0);
+       cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
+       cx_writeb(P1_MADDR0, (unsigned int)address);
+       cx_read(P1_MADDR0);
+
+       retval = wait_ready_gpio0_bit1(core,1);
+
+       cx_writeb(P1_MDATA3, 0);
+       val     = (unsigned char)cx_read(P1_MDATA3) << 24;
+       cx_writeb(P1_MDATA2, 0);
+       val    |= (unsigned char)cx_read(P1_MDATA2) << 16;
+       cx_writeb(P1_MDATA1, 0);
+       val    |= (unsigned char)cx_read(P1_MDATA1) << 8;
+       cx_writeb(P1_MDATA0, 0);
+       val    |= (unsigned char)cx_read(P1_MDATA0);
+
+       *value  = val;
+       return retval;
+}
+
+static int register_write(struct cx88_core *core, u32 address, u32 value)
+{
+       cx_writeb(P1_RDATA0, (unsigned int)value);
+       cx_writeb(P1_RDATA1, (unsigned int)(value >> 8));
+       cx_writeb(P1_RDATA2, (unsigned int)(value >> 16));
+       cx_writeb(P1_RDATA3, (unsigned int)(value >> 24));
+       cx_writeb(P1_RADDR0, (unsigned int)address);
+       cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
+       cx_writeb(P1_RRDWR, 1);
+       cx_read(P1_RDATA0);
+       cx_read(P1_RADDR0);
+
+       return wait_ready_gpio0_bit1(core,1);
+#if 0
+       udelay(1000); /* without this, things don't go right (subsequent memory_write()'s don't get through */
+       /* ? would this be safe here? set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); */
+#endif
+}
+
+
+static int register_read(struct cx88_core *core, u32 address, u32 *value)
+{
+       int retval;
+       u32 val;
+
+       cx_writeb(P1_RADDR0, (unsigned int)address);
+       cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
+       cx_writeb(P1_RRDWR, 0);
+       cx_read(P1_RADDR0);
+
+       retval  = wait_ready_gpio0_bit1(core,1);
+       val     = (unsigned char)cx_read(P1_RDATA0);
+       val    |= (unsigned char)cx_read(P1_RDATA1) << 8;
+       val    |= (unsigned char)cx_read(P1_RDATA2) << 16;
+       val    |= (unsigned char)cx_read(P1_RDATA3) << 24;
+
+       *value  = val;
+       return retval;
+}
+
+/* ------------------------------------------------------------------ */
+
+/* We don't need to call the API often, so using just one mailbox will probably suffice */
+static int blackbird_api_cmd(struct cx8802_dev *dev, u32 command,
+                            u32 inputcnt, u32 outputcnt, ...)
+{
+       unsigned long timeout;
+       u32 value, flag, retval;
+       int i;
+       va_list args;
+       va_start(args, outputcnt);
+
+       dprintk(1,"%s: 0x%X\n", __FUNCTION__, command);
+
+       /* this may not be 100% safe if we can't read any memory location
+          without side effects */
+       memory_read(dev->core, dev->mailbox - 4, &value);
+       if (value != 0x12345678) {
+               dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n");
+               return -1;
+       }
+
+       memory_read(dev->core, dev->mailbox, &flag);
+       if (flag) {
+               dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag);
+               return -1;
+       }
+
+       flag |= 1; /* tell 'em we're working on it */
+       memory_write(dev->core, dev->mailbox, flag);
+
+       /* write command + args + fill remaining with zeros */
+       memory_write(dev->core, dev->mailbox + 1, command); /* command code */
+       memory_write(dev->core, dev->mailbox + 3, IVTV_API_STD_TIMEOUT); /* timeout */
+       for (i = 0; i < inputcnt ; i++) {
+               value = va_arg(args, int);
+               memory_write(dev->core, dev->mailbox + 4 + i, value);
+               dprintk(1, "API Input %d = %d\n", i, value);
+       }
+       for (; i < 16 ; i++)
+               memory_write(dev->core, dev->mailbox + 4 + i, 0);
+
+       flag |= 3; /* tell 'em we're done writing */
+       memory_write(dev->core, dev->mailbox, flag);
+
+       /* wait for firmware to handle the API command */
+       timeout = jiffies + msecs_to_jiffies(10);
+       for (;;) {
+               memory_read(dev->core, dev->mailbox, &flag);
+               if (0 == (flag & 4))
+                       break;
+               if (time_after(jiffies,timeout)) {
+                       dprintk(0, "ERROR: API Mailbox timeout\n");
+                       return -1;
+               }
+               udelay(10);
+       }
+
+       /* read output values */
+       for (i = 0; i < outputcnt ; i++) {
+               int *vptr = va_arg(args, int *);
+               memory_read(dev->core, dev->mailbox + 4 + i, vptr);
+               dprintk(1, "API Output %d = %d\n", i, *vptr);
+       }
+       va_end(args);
+
+       memory_read(dev->core, dev->mailbox + 2, &retval);
+       dprintk(1, "API result = %d\n",retval);
+
+       flag = 0;
+       memory_write(dev->core, dev->mailbox, flag);
+       return retval;
+}
+
+
+static int blackbird_find_mailbox(struct cx8802_dev *dev)
+{
+       u32 signature[4]={0x12345678, 0x34567812, 0x56781234, 0x78123456};
+       int signaturecnt=0;
+       u32 value;
+       int i;
+
+       for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) {
+               memory_read(dev->core, i, &value);
+               if (value == signature[signaturecnt])
+                       signaturecnt++;
+               else
+                       signaturecnt = 0;
+               if (4 == signaturecnt) {
+                       dprintk(1, "Mailbox signature found\n");
+                       return i;
+               }
+       }
+       dprintk(0, "Mailbox signature values not found!\n");
+       return -1;
+}
+
+static int blackbird_load_firmware(struct cx8802_dev *dev)
+{
+       static const unsigned char magic[8] = {
+               0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+       };
+       const struct firmware *firmware;
+       int i, retval = 0;
+       u32 value = 0;
+       u32 checksum = 0;
+       u32 *dataptr;
+
+       retval  = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED);
+        retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+        retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_REFRESH, 0x80000640);
+        retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+       msleep(1);
+        retval |= register_write(dev->core, IVTV_REG_APU, 0);
+
+       if (retval < 0)
+               dprintk(0, "Error with register_write\n");
+
+       retval = request_firmware(&firmware, BLACKBIRD_FIRM_ENC_FILENAME,
+                                 &dev->pci->dev);
+       if (retval != 0) {
+               dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n",
+                       BLACKBIRD_FIRM_ENC_FILENAME);
+               dprintk(0, "Please fix your hotplug setup, the board will "
+                       "not work without firmware loaded!\n");
+               return -1;
+       }
+
+       if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) {
+               dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n",
+                       firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE);
+               return -1;
+       }
+
+       if (0 != memcmp(firmware->data, magic, 8)) {
+               dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n");
+               return -1;
+       }
+
+       /* transfer to the chip */
+       dprintk(1,"Loading firmware ...\n");
+       dataptr = (u32*)firmware->data;
+       for (i = 0; i < (firmware->size >> 2); i++) {
+               value = *dataptr;
+               checksum += ~value;
+               memory_write(dev->core, i, value);
+               dataptr++;
+       }
+
+       /* read back to verify with the checksum */
+       for (i--; i >= 0; i--) {
+               memory_read(dev->core, i, &value);
+               checksum -= ~value;
+       }
+       if (checksum) {
+               dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n");
+               return -1;
+       }
+       release_firmware(firmware);
+       dprintk(0, "Firmware upload successful.\n");
+
+        retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+        retval |= register_read(dev->core, IVTV_REG_SPU, &value);
+        retval |= register_write(dev->core, IVTV_REG_SPU, value & 0xFFFFFFFE);
+       msleep(1);
+
+       retval |= register_read(dev->core, IVTV_REG_VPU, &value);
+        retval |= register_write(dev->core, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+       if (retval < 0)
+               dprintk(0, "Error with register_write\n");
+       return 0;
+}
+
+static void blackbird_codec_settings(struct cx8802_dev *dev)
+{
+       int bitrate_mode = 1;
+       int bitrate = 7500000;
+       int bitrate_peak = 7500000;
+
+       /* assign stream type */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 0); /* program stream */
+        //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 2); /* MPEG1 stream */
+        //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 3); /* PES A/V */
+        //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 10); /* DVD stream */
+
+        /* assign output port */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_OUTPUT_PORT, 1, 0, 1); /* 1 = Host */
+
+        /* assign framerate */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAMERATE, 1, 0, 0);
+
+        /* assign frame size */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAME_SIZE, 2, 0, 480, 720);
+
+        /* assign aspect ratio */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_ASPECT_RATIO, 1, 0, 2);
+
+        /* assign bitrates */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_BITRATES, 5, 0,
+                        bitrate_mode,         /* mode */
+                        bitrate,              /* bps */
+                        bitrate_peak / 400,   /* peak/400 */
+                        0, 0x70);             /* encoding buffer, ckennedy */
+
+        /* assign gop properties */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_PROPERTIES, 2, 0, 15, 3);
+        //blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_PROPERTIES, 2, 0, 2, 1);
+
+        /* assign 3 2 pulldown */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_3_2_PULLDOWN, 1, 0, 0);
+
+        /* note: it's not necessary to set the samplerate, the mpeg encoder seems to autodetect/adjust */
+       blackbird_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, (2<<2) | (8<<4));
+
+       /* assign gop closure */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_CLOSURE, 1, 0, 0);
+
+        /* assign audio properties */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, 0 | (2 << 2) | (14 << 4));
+
+        /* assign dnr filter mode */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_DNR_FILTER_MODE, 2, 0, 0, 0);
+
+        /* assign dnr filter props*/
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_DNR_FILTER_PROPS, 2, 0, 0, 0);
+
+        /* assign coring levels (luma_h, luma_l, chroma_h, chroma_l) */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_CORING_LEVELS, 4, 0, 0, 255, 0, 255);
+
+       /* assign spatial filter type: luma_t: 1 = horiz_only, chroma_t: 1 = horiz_only */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE, 2, 0, 1, 1);
+
+        /* assign frame drop rate */
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAME_DROP_RATE, 1, 0, 0);
+}
+
+static int blackbird_initialize_codec(struct cx8802_dev *dev)
+{
+       struct cx88_core *core = dev->core;
+       int version;
+       int retval;
+
+       dprintk(1,"Initialize codec\n");
+       retval = blackbird_api_cmd(dev, IVTV_API_ENC_PING_FW, 0, 0); /* ping */
+       if (retval < 0) {
+               /* ping was not successful, reset and upload firmware */
+               cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */
+               msleep(1);
+               cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */
+               msleep(1);
+               retval = blackbird_load_firmware(dev);
+               if (retval < 0)
+                       return retval;
+
+               dev->mailbox = blackbird_find_mailbox(dev);
+               if (dev->mailbox < 0)
+                       return -1;
+
+               retval = blackbird_api_cmd(dev, IVTV_API_ENC_PING_FW, 0, 0); /* ping */
+               if (retval < 0) {
+                       dprintk(0, "ERROR: Firmware ping failed!\n");
+                       return -1;
+               }
+
+               retval = blackbird_api_cmd(dev, IVTV_API_ENC_GETVER, 0, 1, &version);
+               if (retval < 0) {
+                       dprintk(0, "ERROR: Firmware get encoder version failed!\n");
+                       return -1;
+               }
+               dprintk(0, "Firmware version is 0x%08x\n", version);
+       }
+       msleep(1);
+
+       cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */
+       cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */
+       cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */
+       cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */
+
+#if 0 /* FIXME */
+       set_scale(dev, 720, 480, V4L2_FIELD_INTERLACED);
+#endif
+       blackbird_codec_settings(dev);
+       msleep(1);
+
+       //blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xef, 0xef);
+       blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xf0, 0xf0);
+       //blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0x180, 0x180);
+        blackbird_api_cmd(dev, IVTV_API_ASSIGN_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+       blackbird_api_cmd(dev, IVTV_API_INITIALIZE_INPUT, 0, 0); /* initialize the video input */
+
+       msleep(1);
+
+        blackbird_api_cmd(dev, IVTV_API_MUTE_VIDEO, 1, 0, 0);
+       msleep(1);
+        blackbird_api_cmd(dev, IVTV_API_MUTE_AUDIO, 1, 0, 0);
+       msleep(1);
+
+       blackbird_api_cmd(dev, IVTV_API_BEGIN_CAPTURE, 2, 0, 0, 0x13); /* start capturing to the host interface */
+       //blackbird_api_cmd(dev, IVTV_API_BEGIN_CAPTURE, 2, 0, 0, 0); /* start capturing to the host interface */
+       msleep(1);
+
+       blackbird_api_cmd(dev, IVTV_API_REFRESH_INPUT, 0,0);
+       return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(struct videobuf_queue *q,
+                       unsigned int *count, unsigned int *size)
+{
+       struct cx8802_fh *fh = q->priv_data;
+
+       fh->dev->ts_packet_size  = 512;
+       fh->dev->ts_packet_count = 100;
+
+       *size = fh->dev->ts_packet_size * fh->dev->ts_packet_count;
+       if (0 == *count)
+               *count = mpegbufs;
+       if (*count < 2)
+               *count = 2;
+       if (*count > 32)
+               *count = 32;
+       return 0;
+}
+
+static int
+bb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+              enum v4l2_field field)
+{
+       struct cx8802_fh *fh = q->priv_data;
+       return cx8802_buf_prepare(fh->dev, (struct cx88_buffer*)vb);
+}
+
+static void
+bb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       struct cx8802_fh *fh = q->priv_data;
+       cx8802_buf_queue(fh->dev, (struct cx88_buffer*)vb);
+}
+
+static void
+bb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       struct cx8802_fh *fh = q->priv_data;
+       cx88_free_buffer(fh->dev->pci, (struct cx88_buffer*)vb);
+}
+
+static struct videobuf_queue_ops blackbird_qops = {
+       .buf_setup    = bb_buf_setup,
+       .buf_prepare  = bb_buf_prepare,
+       .buf_queue    = bb_buf_queue,
+       .buf_release  = bb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int mpeg_do_ioctl(struct inode *inode, struct file *file,
+                        unsigned int cmd, void *arg)
+{
+       struct cx8802_fh  *fh  = file->private_data;
+       struct cx8802_dev *dev = fh->dev;
+
+       if (debug > 1)
+               cx88_print_ioctl(dev->core->name,cmd);
+
+       switch (cmd) {
+
+       /* --- capture ioctls ---------------------------------------- */
+       case VIDIOC_ENUM_FMT:
+       {
+               struct v4l2_fmtdesc *f = arg;
+               int index;
+
+               index = f->index;
+               if (index != 0)
+                       return -EINVAL;
+
+               memset(f,0,sizeof(*f));
+               f->index = index;
+               strlcpy(f->description, "MPEG TS", sizeof(f->description));
+               f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               f->pixelformat = V4L2_PIX_FMT_MPEG;
+               return 0;
+       }
+       case VIDIOC_G_FMT:
+       case VIDIOC_S_FMT:
+       case VIDIOC_TRY_FMT:
+       {
+               /* FIXME -- quick'n'dirty for exactly one size ... */
+               struct v4l2_format *f = arg;
+
+               memset(f,0,sizeof(*f));
+               f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               f->fmt.pix.width        = 720;
+               f->fmt.pix.height       = 576;
+               f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+               f->fmt.pix.sizeimage    = 1024 * 512 /* FIXME: BUFFER_SIZE */;
+       }
+
+       /* --- streaming capture ------------------------------------- */
+       case VIDIOC_REQBUFS:
+               return videobuf_reqbufs(&fh->mpegq, arg);
+
+       case VIDIOC_QUERYBUF:
+               return videobuf_querybuf(&fh->mpegq, arg);
+
+       case VIDIOC_QBUF:
+               return videobuf_qbuf(&fh->mpegq, arg);
+
+       case VIDIOC_DQBUF:
+               return videobuf_dqbuf(&fh->mpegq, arg,
+                                     file->f_flags & O_NONBLOCK);
+
+       case VIDIOC_STREAMON:
+               return videobuf_streamon(&fh->mpegq);
+
+       case VIDIOC_STREAMOFF:
+               return videobuf_streamoff(&fh->mpegq);
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int mpeg_ioctl(struct inode *inode, struct file *file,
+                      unsigned int cmd, unsigned long arg)
+{
+       return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl);
+}
+
+static int mpeg_open(struct inode *inode, struct file *file)
+{
+       int minor = iminor(inode);
+       struct cx8802_dev *h,*dev = NULL;
+       struct cx8802_fh *fh;
+       struct list_head *list;
+
+       list_for_each(list,&cx8802_devlist) {
+               h = list_entry(list, struct cx8802_dev, devlist);
+               if (h->mpeg_dev->minor == minor)
+                       dev = h;
+       }
+       if (NULL == dev)
+               return -ENODEV;
+
+       if (blackbird_initialize_codec(dev) < 0)
+               return -EINVAL;
+       dprintk(1,"open minor=%d\n",minor);
+
+       /* allocate + initialize per filehandle data */
+       fh = kmalloc(sizeof(*fh),GFP_KERNEL);
+       if (NULL == fh)
+               return -ENOMEM;
+       memset(fh,0,sizeof(*fh));
+       file->private_data = fh;
+       fh->dev      = dev;
+
+       videobuf_queue_init(&fh->mpegq, &blackbird_qops,
+                           dev->pci, &dev->slock,
+                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                           V4L2_FIELD_TOP,
+                           sizeof(struct cx88_buffer),
+                           fh);
+       return 0;
+}
+
+static int mpeg_release(struct inode *inode, struct file *file)
+{
+       struct cx8802_fh  *fh  = file->private_data;
+
+       blackbird_api_cmd(fh->dev, IVTV_API_END_CAPTURE, 3, 0, 1, 0, 0x13);
+
+       /* stop mpeg capture */
+       if (fh->mpegq.streaming)
+               videobuf_streamoff(&fh->mpegq);
+       if (fh->mpegq.reading)
+               videobuf_read_stop(&fh->mpegq);
+
+       file->private_data = NULL;
+       kfree(fh);
+       return 0;
+}
+
+static ssize_t
+mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+       struct cx8802_fh *fh = file->private_data;
+
+       return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
+                                   file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int
+mpeg_poll(struct file *file, struct poll_table_struct *wait)
+{
+       struct cx8802_fh *fh = file->private_data;
+
+       return videobuf_poll_stream(file, &fh->mpegq, wait);
+}
+
+static int
+mpeg_mmap(struct file *file, struct vm_area_struct * vma)
+{
+       struct cx8802_fh *fh = file->private_data;
+
+       return videobuf_mmap_mapper(&fh->mpegq, vma);
+}
+
+static struct file_operations mpeg_fops =
+{
+       .owner         = THIS_MODULE,
+       .open          = mpeg_open,
+       .release       = mpeg_release,
+       .read          = mpeg_read,
+       .poll          = mpeg_poll,
+       .mmap          = mpeg_mmap,
+       .ioctl         = mpeg_ioctl,
+       .llseek        = no_llseek,
+};
+
+static struct video_device cx8802_mpeg_template =
+{
+       .name          = "cx8802",
+       .type          = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES|VID_TYPE_MPEG_ENCODER,
+       .hardware      = 0,
+       .fops          = &mpeg_fops,
+       .minor         = -1,
+};
+
+/* ------------------------------------------------------------------ */
+
+static void blackbird_unregister_video(struct cx8802_dev *dev)
+{
+       if (dev->mpeg_dev) {
+               if (-1 != dev->mpeg_dev->minor)
+                       video_unregister_device(dev->mpeg_dev);
+               else
+                       video_device_release(dev->mpeg_dev);
+               dev->mpeg_dev = NULL;
+       }
+}
+
+static int blackbird_register_video(struct cx8802_dev *dev)
+{
+       int err;
+
+       dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci,
+                                      &cx8802_mpeg_template,"mpeg");
+       err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1);
+       if (err < 0) {
+               printk(KERN_INFO "%s/2: can't register mpeg device\n",
+                      dev->core->name);
+               return err;
+       }
+       printk(KERN_INFO "%s/2: registered device video%d [mpeg]\n",
+              dev->core->name,dev->mpeg_dev->minor & 0x1f);
+       return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+static int __devinit blackbird_probe(struct pci_dev *pci_dev,
+                                    const struct pci_device_id *pci_id)
+{
+       struct cx8802_dev *dev;
+       struct cx88_core  *core;
+       int err;
+
+       /* general setup */
+       core = cx88_core_get(pci_dev);
+       if (NULL == core)
+               return -EINVAL;
+
+       err = -ENODEV;
+       if (!cx88_boards[core->board].blackbird)
+               goto fail_core;
+
+       err = -ENOMEM;
+       dev = kmalloc(sizeof(*dev),GFP_KERNEL);
+       if (NULL == dev)
+               goto fail_core;
+       memset(dev,0,sizeof(*dev));
+       dev->pci = pci_dev;
+       dev->core = core;
+
+       err = cx8802_init_common(dev);
+       if (0 != err)
+               goto fail_free;
+
+       /* blackbird stuff */
+       printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
+              core->name);
+       host_setup(dev->core);
+
+       list_add_tail(&dev->devlist,&cx8802_devlist);
+       blackbird_register_video(dev);
+       return 0;
+
+ fail_free:
+       kfree(dev);
+ fail_core:
+       cx88_core_put(core,pci_dev);
+       return err;
+}
+
+static void __devexit blackbird_remove(struct pci_dev *pci_dev)
+{
+        struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+
+       /* blackbird */
+       blackbird_unregister_video(dev);
+       list_del(&dev->devlist);
+
+       /* common */
+       cx8802_fini_common(dev);
+}
+
+static struct pci_device_id cx8802_pci_tbl[] = {
+       {
+               .vendor       = 0x14f1,
+               .device       = 0x8802,
+                .subvendor    = PCI_ANY_ID,
+                .subdevice    = PCI_ANY_ID,
+       },{
+               /* --- end of list --- */
+       }
+};
+MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
+
+static struct pci_driver blackbird_pci_driver = {
+        .name     = "cx88-blackbird",
+        .id_table = cx8802_pci_tbl,
+        .probe    = blackbird_probe,
+        .remove   = __devexit_p(blackbird_remove),
+       .suspend  = cx8802_suspend_common,
+       .resume   = cx8802_resume_common,
+};
+
+static int blackbird_init(void)
+{
+       printk(KERN_INFO "cx2388x blackbird driver version %d.%d.%d loaded\n",
+              (CX88_VERSION_CODE >> 16) & 0xff,
+              (CX88_VERSION_CODE >>  8) & 0xff,
+              CX88_VERSION_CODE & 0xff);
+#ifdef SNAPSHOT
+       printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
+              SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
+#endif
+       return pci_module_init(&blackbird_pci_driver);
+}
+
+static void blackbird_fini(void)
+{
+       pci_unregister_driver(&blackbird_pci_driver);
+}
+
+module_init(blackbird_init);
+module_exit(blackbird_fini);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
new file mode 100644 (file)
index 0000000..277a6d6
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * $Id: cx88-dvb.c,v 1.19 2004/11/07 14:44:59 kraxel Exp $
+ *
+ * device driver for Conexant 2388x based TV cards
+ * MPEG Transport Stream (DVB) routines
+ *
+ * (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/suspend.h>
+
+#include "cx88.h"
+#include "cx22702.h"
+#include "mt352.h"
+#include "mt352_priv.h" /* FIXME */
+
+MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [dvb]");
+
+#define dprintk(level,fmt, arg...)     if (debug >= level) \
+       printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->core->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_buf_setup(struct videobuf_queue *q,
+                        unsigned int *count, unsigned int *size)
+{
+       struct cx8802_dev *dev = q->priv_data;
+
+       dev->ts_packet_size  = 188 * 4;
+       dev->ts_packet_count = 32;
+
+       *size  = dev->ts_packet_size * dev->ts_packet_count;
+       *count = 32;
+       return 0;
+}
+
+static int dvb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+                          enum v4l2_field field)
+{
+       struct cx8802_dev *dev = q->priv_data;
+       return cx8802_buf_prepare(dev, (struct cx88_buffer*)vb);
+}
+
+static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       struct cx8802_dev *dev = q->priv_data;
+       cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
+}
+
+static void dvb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       struct cx8802_dev *dev = q->priv_data;
+       cx88_free_buffer(dev->pci, (struct cx88_buffer*)vb);
+}
+
+struct videobuf_queue_ops dvb_qops = {
+       .buf_setup    = dvb_buf_setup,
+       .buf_prepare  = dvb_buf_prepare,
+       .buf_queue    = dvb_buf_queue,
+       .buf_release  = dvb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
+{
+       static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x39 };
+       static u8 reset []         = { RESET,      0x80 };
+       static u8 adc_ctl_1_cfg [] = { ADC_CTL_1,  0x40 };
+       static u8 agc_cfg []       = { AGC_TARGET, 0x24, 0x20 };
+       static u8 gpp_ctl_cfg []   = { GPP_CTL,    0x33 };
+       static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+
+       mt352_write(fe, clock_config,   sizeof(clock_config));
+       udelay(200);
+       mt352_write(fe, reset,          sizeof(reset));
+       mt352_write(fe, adc_ctl_1_cfg,  sizeof(adc_ctl_1_cfg));
+
+       mt352_write(fe, agc_cfg,        sizeof(agc_cfg));
+       mt352_write(fe, gpp_ctl_cfg,    sizeof(gpp_ctl_cfg));
+       mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+       return 0;
+}
+
+#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
+
+static int lg_z201_pll_set(struct dvb_frontend* fe,
+                          struct dvb_frontend_parameters* params, u8* pllbuf)
+{
+       u32 div;
+       unsigned char cp = 0;
+       unsigned char bs = 0;
+
+       div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+       if (params->frequency < 542000000) cp = 0xbc;
+       else if (params->frequency < 830000000) cp = 0xf4;
+       else cp = 0xfc;
+
+       if (params->frequency == 0) bs = 0x03;
+       else if (params->frequency < 157500000) bs = 0x01;
+       else if (params->frequency < 443250000) bs = 0x02;
+       else bs = 0x04;
+
+       pllbuf[0] = 0xC2; /* Note: non-linux standard PLL I2C address */
+       pllbuf[1] = div >> 8;
+       pllbuf[2] = div & 0xff;
+       pllbuf[3] = cp;
+       pllbuf[4] = bs;
+
+       return 0;
+}
+
+static int thomson_dtt7579_pll_set(struct dvb_frontend* fe,
+                                  struct dvb_frontend_parameters* params,
+                                  u8* pllbuf)
+{
+       u32 div;
+       unsigned char cp = 0;
+       unsigned char bs = 0;
+
+       div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+       if (params->frequency < 542000000) cp = 0xb4;
+       else if (params->frequency < 771000000) cp = 0xbc;
+       else cp = 0xf4;
+
+        if (params->frequency == 0) bs = 0x03;
+       else if (params->frequency < 443250000) bs = 0x02;
+       else bs = 0x08;
+
+       pllbuf[0] = 0xc0; // Note: non-linux standard PLL i2c address
+       pllbuf[1] = div >> 8;
+       pllbuf[2] = div & 0xff;
+       pllbuf[3] = cp;
+       pllbuf[4] = bs;
+
+       return 0;
+}
+
+struct mt352_config dvico_fusionhdtv_dvbt1 = {
+       .demod_address = 0x0F,
+       .demod_init    = dvico_fusionhdtv_demod_init,
+       .pll_set       = lg_z201_pll_set,
+};
+
+struct mt352_config dvico_fusionhdtv_dvbt_plus = {
+       .demod_address = 0x0F,
+       .demod_init    = dvico_fusionhdtv_demod_init,
+       .pll_set       = thomson_dtt7579_pll_set,
+};
+
+static int dvb_register(struct cx8802_dev *dev)
+{
+       /* init struct videobuf_dvb */
+       dev->dvb.name = dev->core->name;
+
+       /* init frontend */
+       switch (dev->core->board) {
+       case CX88_BOARD_HAUPPAUGE_DVB_T1:
+       case CX88_BOARD_CONEXANT_DVB_T1:
+               dev->dvb.frontend = cx22702_create(&dev->core->i2c_adap,
+                                                  dev->core->pll_addr,
+                                                  dev->core->pll_type,
+                                                  dev->core->demod_addr);
+               break;
+       case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
+               dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv_dvbt1,
+                                                &dev->core->i2c_adap);
+               if (dev->dvb.frontend) {
+                       dev->dvb.frontend->ops->info.frequency_min = 174000000;
+                       dev->dvb.frontend->ops->info.frequency_max = 862000000;
+               }
+               break;
+       case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
+               dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv_dvbt_plus,
+                                                &dev->core->i2c_adap);
+               if (dev->dvb.frontend) {
+                       dev->dvb.frontend->ops->info.frequency_min = 174000000;
+                       dev->dvb.frontend->ops->info.frequency_max = 862000000;
+               }
+               break;
+       default:
+               printk("%s: FIXME: frontend handling not here yet ...\n",
+                      dev->core->name);
+               break;
+       }
+       if (NULL == dev->dvb.frontend)
+               return -1;
+
+       /* Copy the board name into the DVB structure */
+       strlcpy(dev->dvb.frontend->ops->info.name,
+               cx88_boards[dev->core->board].name,
+               sizeof(dev->dvb.frontend->ops->info.name));
+
+       /* register everything */
+       return videobuf_dvb_register(&dev->dvb);
+}
+
+/* ----------------------------------------------------------- */
+
+static int __devinit dvb_probe(struct pci_dev *pci_dev,
+                              const struct pci_device_id *pci_id)
+{
+       struct cx8802_dev *dev;
+       struct cx88_core  *core;
+       int err;
+
+       /* general setup */
+       core = cx88_core_get(pci_dev);
+       if (NULL == core)
+               return -EINVAL;
+
+       err = -ENODEV;
+       if (!cx88_boards[core->board].dvb)
+               goto fail_core;
+
+       err = -ENOMEM;
+       dev = kmalloc(sizeof(*dev),GFP_KERNEL);
+       if (NULL == dev)
+               goto fail_core;
+       memset(dev,0,sizeof(*dev));
+       dev->pci = pci_dev;
+       dev->core = core;
+
+       err = cx8802_init_common(dev);
+       if (0 != err)
+               goto fail_free;
+
+       /* dvb stuff */
+       printk("%s/2: cx2388x based dvb card\n", core->name);
+       videobuf_queue_init(&dev->dvb.dvbq, &dvb_qops,
+                           dev->pci, &dev->slock,
+                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                           V4L2_FIELD_TOP,
+                           sizeof(struct cx88_buffer),
+                           dev);
+       err = dvb_register(dev);
+       if (0 != err)
+               goto fail_free;
+       return 0;
+
+ fail_free:
+       kfree(dev);
+ fail_core:
+       cx88_core_put(core,pci_dev);
+       return err;
+}
+
+static void __devexit dvb_remove(struct pci_dev *pci_dev)
+{
+        struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+
+       /* dvb */
+       videobuf_dvb_unregister(&dev->dvb);
+
+       /* common */
+       cx8802_fini_common(dev);
+       cx88_core_put(dev->core,dev->pci);
+       kfree(dev);
+}
+
+static struct pci_device_id cx8802_pci_tbl[] = {
+       {
+               .vendor       = 0x14f1,
+               .device       = 0x8802,
+                .subvendor    = PCI_ANY_ID,
+                .subdevice    = PCI_ANY_ID,
+       },{
+               /* --- end of list --- */
+       }
+};
+MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
+
+static struct pci_driver dvb_pci_driver = {
+        .name     = "cx88-dvb",
+        .id_table = cx8802_pci_tbl,
+        .probe    = dvb_probe,
+        .remove   = __devexit_p(dvb_remove),
+       .suspend  = cx8802_suspend_common,
+       .resume   = cx8802_resume_common,
+};
+
+static int dvb_init(void)
+{
+       printk(KERN_INFO "cx2388x dvb driver version %d.%d.%d loaded\n",
+              (CX88_VERSION_CODE >> 16) & 0xff,
+              (CX88_VERSION_CODE >>  8) & 0xff,
+              CX88_VERSION_CODE & 0xff);
+#ifdef SNAPSHOT
+       printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
+              SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
+#endif
+       return pci_module_init(&dvb_pci_driver);
+}
+
+static void dvb_fini(void)
+{
+       pci_unregister_driver(&dvb_pci_driver);
+}
+
+module_init(dvb_init);
+module_exit(dvb_fini);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * compile-command: "make DVB=1"
+ * End:
+ */
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
new file mode 100644 (file)
index 0000000..b538c83
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+ * $Id: cx88-mpeg.c,v 1.14 2004/10/25 11:26:36 kraxel Exp $
+ *
+ *  Support for the mpeg transport stream transfers
+ *  PCI function #2 of the cx2388x.
+ *
+ *    (c) 2004 Jelle Foks <jelle@foks.8m.com>
+ *    (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ *    (c) 2004 Gerd Knorr <kraxel@bytesex.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.
+ *
+ *  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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <asm/delay.h>
+
+#include "cx88.h"
+
+/* ------------------------------------------------------------------ */
+
+MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug = 0;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
+
+#define dprintk(level,fmt, arg...)     if (debug >= level) \
+       printk(KERN_DEBUG "%s/2: " fmt, dev->core->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int cx8802_start_dma(struct cx8802_dev    *dev,
+                           struct cx88_dmaqueue *q,
+                           struct cx88_buffer   *buf)
+{
+       struct cx88_core *core = dev->core;
+
+       dprintk(1, "cx8802_start_mpegport_dma %d\n", buf->vb.width);
+
+       /* setup fifo + format */
+       cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28],
+                               dev->ts_packet_size, buf->risc.dma);
+
+       /* write TS length to chip */
+       cx_write(MO_TS_LNGTH, buf->vb.width);
+
+#if 1
+       /* FIXME: this needs a review.
+        * also: move to cx88-blackbird + cx88-dvb source files? */
+
+       if (cx88_boards[core->board].dvb) {
+               /* Setup TS portion of chip */
+               cx_write(TS_GEN_CNTRL, 0x0c);
+       }
+
+       if (cx88_boards[core->board].blackbird) {
+               cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
+
+               // cx_write(TS_F2_CMD_STAT_MM, 0x2900106); /* F2_CMD_STAT_MM defaults + master + memory space */
+               cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */
+               udelay(100);
+
+               cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */
+               //cx_write(TS_HW_SOP_CNTRL, 0x2F0BC0); /* mpeg start byte ts: 0x2F0BC0 ? */
+               cx_write(TS_VALERR_CNTRL, 0x2000);
+
+               cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */
+               udelay(100);
+       }
+#endif
+
+       /* reset counter */
+       cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET);
+       q->count = 1;
+
+       /* enable irqs */
+       cx_set(MO_PCI_INTMSK, 0x00fc04);
+       cx_write(MO_TS_INTMSK,  0x1f0011);
+
+       /* start dma */
+       cx_write(MO_DEV_CNTRL2, (1<<5)); /* FIXME: s/write/set/ ??? */
+       cx_write(MO_TS_DMACNTRL, 0x11);
+       return 0;
+}
+
+static int cx8802_stop_dma(struct cx8802_dev *dev)
+{
+       struct cx88_core *core = dev->core;
+
+       /* stop dma */
+       cx_clear(MO_TS_DMACNTRL, 0x11);
+
+       /* disable irqs */
+       cx_clear(MO_PCI_INTMSK, 0x000004);
+       cx_clear(MO_TS_INTMSK, 0x1f0011);
+
+       /* Reset the controller */
+       cx_write(TS_GEN_CNTRL, 0xcd);
+       return 0;
+}
+
+static int cx8802_restart_queue(struct cx8802_dev    *dev,
+                               struct cx88_dmaqueue *q)
+{
+       struct cx88_buffer *buf;
+       struct list_head *item;
+
+       if (list_empty(&q->active))
+               return 0;
+
+       buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+       dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+               buf, buf->vb.i);
+       cx8802_start_dma(dev, q, buf);
+       list_for_each(item,&q->active) {
+               buf = list_entry(item, struct cx88_buffer, vb.queue);
+               buf->count = q->count++;
+       }
+       mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+       return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf)
+{
+       int size = dev->ts_packet_size * dev->ts_packet_count;
+       int rc;
+
+       dprintk(1, "%s: %p\n", __FUNCTION__, buf);
+       if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+               return -EINVAL;
+
+       if (STATE_NEEDS_INIT == buf->vb.state) {
+               buf->vb.width  = dev->ts_packet_size;
+               buf->vb.height = dev->ts_packet_count;
+               buf->vb.size   = size;
+               buf->vb.field  = V4L2_FIELD_TOP;
+
+               if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL)))
+                       goto fail;
+               cx88_risc_databuffer(dev->pci, &buf->risc,
+                                    buf->vb.dma.sglist,
+                                    buf->vb.width, buf->vb.height);
+       }
+       buf->vb.state = STATE_PREPARED;
+       return 0;
+
+ fail:
+       cx88_free_buffer(dev->pci,buf);
+       return rc;
+}
+
+void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
+{
+       struct cx88_buffer    *prev;
+       struct cx88_dmaqueue  *q    = &dev->mpegq;
+
+       /* add jump to stopper */
+       buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+       buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+
+       if (list_empty(&q->active)) {
+               list_add_tail(&buf->vb.queue,&q->active);
+               cx8802_start_dma(dev, q, buf);
+               buf->vb.state = STATE_ACTIVE;
+               buf->count    = q->count++;
+               mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+               dprintk(2,"[%p/%d] %s - first active\n",
+                       buf, buf->vb.i, __FUNCTION__);
+
+       } else {
+               prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
+               list_add_tail(&buf->vb.queue,&q->active);
+               buf->vb.state = STATE_ACTIVE;
+               buf->count    = q->count++;
+               prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+               dprintk(2,"[%p/%d] %s - append to active\n",
+                       buf, buf->vb.i, __FUNCTION__);
+       }
+}
+
+/* ----------------------------------------------------------- */
+
+static void do_cancel_buffers(struct cx8802_dev *dev, char *reason, int restart)
+{
+       struct cx88_dmaqueue *q = &dev->mpegq;
+       struct cx88_buffer *buf;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->slock,flags);
+       while (!list_empty(&q->active)) {
+               buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+               list_del(&buf->vb.queue);
+               buf->vb.state = STATE_ERROR;
+               wake_up(&buf->vb.done);
+               dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",
+                       buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
+       }
+       if (restart)
+               cx8802_restart_queue(dev,q);
+       spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+void cx8802_cancel_buffers(struct cx8802_dev *dev)
+{
+       struct cx88_dmaqueue *q = &dev->mpegq;
+
+       del_timer_sync(&q->timeout);
+       cx8802_stop_dma(dev);
+       do_cancel_buffers(dev,"cancel",0);
+}
+
+static void cx8802_timeout(unsigned long data)
+{
+       struct cx8802_dev *dev = (struct cx8802_dev*)data;
+
+       dprintk(1, "%s\n",__FUNCTION__);
+
+       if (debug)
+               cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
+       cx8802_stop_dma(dev);
+       do_cancel_buffers(dev,"timeout",1);
+}
+
+static void cx8802_mpeg_irq(struct cx8802_dev *dev)
+{
+       struct cx88_core *core = dev->core;
+       u32 status, mask, count;
+
+       status = cx_read(MO_TS_INTSTAT);
+       mask   = cx_read(MO_TS_INTMSK);
+       if (0 == (status & mask))
+               return;
+
+       cx_write(MO_TS_INTSTAT, status);
+       if (debug || (status & mask & ~0xff))
+               cx88_print_irqbits(core->name, "irq mpeg ",
+                                  cx88_mpeg_irqs, status, mask);
+
+       /* risc op code error */
+       if (status & (1 << 16)) {
+               printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name);
+               cx_clear(MO_TS_DMACNTRL, 0x11);
+               cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
+       }
+
+       /* risc1 y */
+       if (status & 0x01) {
+               spin_lock(&dev->slock);
+               count = cx_read(MO_TS_GPCNT);
+               cx88_wakeup(dev->core, &dev->mpegq, count);
+               spin_unlock(&dev->slock);
+       }
+
+       /* risc2 y */
+       if (status & 0x10) {
+               spin_lock(&dev->slock);
+               cx8802_restart_queue(dev,&dev->mpegq);
+               spin_unlock(&dev->slock);
+       }
+
+        /* other general errors */
+        if (status & 0x1f0100) {
+                spin_lock(&dev->slock);
+               cx8802_stop_dma(dev);
+                cx8802_restart_queue(dev,&dev->mpegq);
+                spin_unlock(&dev->slock);
+        }
+}
+
+static irqreturn_t cx8802_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct cx8802_dev *dev = dev_id;
+       struct cx88_core *core = dev->core;
+       u32 status, mask;
+       int loop, handled = 0;
+
+       for (loop = 0; loop < 10; loop++) {
+               status = cx_read(MO_PCI_INTSTAT) & (~0x1f | 0x04);
+               mask   = cx_read(MO_PCI_INTMSK);
+               if (0 == (status & mask))
+                       goto out;
+               handled = 1;
+               cx_write(MO_PCI_INTSTAT, status);
+
+               if (status & mask & ~0x1f)
+                       cx88_irq(core,status,mask);
+               if (status & 0x04)
+                       cx8802_mpeg_irq(dev);
+       };
+       if (10 == loop) {
+               printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",
+                      core->name);
+               cx_write(MO_PCI_INTMSK,0);
+       }
+
+ out:
+       return IRQ_RETVAL(handled);
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff                                              */
+
+int cx8802_init_common(struct cx8802_dev *dev)
+{
+       int err;
+
+       /* pci init */
+       if (pci_enable_device(dev->pci))
+               return -EIO;
+       pci_set_master(dev->pci);
+       if (!pci_dma_supported(dev->pci,0xffffffff)) {
+               printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name);
+               return -EIO;
+       }
+
+       pci_read_config_byte(dev->pci, PCI_CLASS_REVISION, &dev->pci_rev);
+        pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER,  &dev->pci_lat);
+        printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, "
+              "latency: %d, mmio: 0x%lx\n", dev->core->name,
+              pci_name(dev->pci), dev->pci_rev, dev->pci->irq,
+              dev->pci_lat,pci_resource_start(dev->pci,0));
+
+       /* initialize driver struct */
+        init_MUTEX(&dev->lock);
+       dev->slock = SPIN_LOCK_UNLOCKED;
+
+       /* init dma queue */
+       INIT_LIST_HEAD(&dev->mpegq.active);
+       INIT_LIST_HEAD(&dev->mpegq.queued);
+       dev->mpegq.timeout.function = cx8802_timeout;
+       dev->mpegq.timeout.data     = (unsigned long)dev;
+       init_timer(&dev->mpegq.timeout);
+       cx88_risc_stopper(dev->pci,&dev->mpegq.stopper,
+                         MO_TS_DMACNTRL,0x11,0x00);
+
+#if 0 /* FIXME */
+       /* initialize hardware */
+       cx8802_reset(dev);
+#endif
+
+       /* get irq */
+       err = request_irq(dev->pci->irq, cx8802_irq,
+                         SA_SHIRQ | SA_INTERRUPT, dev->core->name, dev);
+       if (err < 0) {
+               printk(KERN_ERR "%s: can't get IRQ %d\n",
+                      dev->core->name, dev->pci->irq);
+               return err;
+       }
+
+#if 0 /* FIXME */
+       /* register i2c bus + load i2c helpers */
+       cx88_card_setup(dev);
+#endif
+
+       /* everything worked */
+       pci_set_drvdata(dev->pci,dev);
+       return 0;
+}
+
+void cx8802_fini_common(struct cx8802_dev *dev)
+{
+       cx8802_stop_dma(dev);
+       pci_disable_device(dev->pci);
+
+       /* unregister stuff */
+       free_irq(dev->pci->irq, dev);
+       pci_set_drvdata(dev->pci, NULL);
+
+       /* free memory */
+       btcx_riscmem_free(dev->pci,&dev->mpegq.stopper);
+}
+
+/* ----------------------------------------------------------- */
+
+int cx8802_suspend_common(struct pci_dev *pci_dev, u32 state)
+{
+        struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+       struct cx88_core *core = dev->core;
+
+       /* stop mpeg dma */
+       spin_lock(&dev->slock);
+       if (!list_empty(&dev->mpegq.active)) {
+               printk("%s: suspend mpeg\n", core->name);
+               cx8802_stop_dma(dev);
+               del_timer(&dev->mpegq.timeout);
+       }
+       spin_unlock(&dev->slock);
+
+#if 1
+       /* FIXME -- shutdown device */
+       cx88_shutdown(dev->core);
+#endif
+
+       pci_save_state(pci_dev);
+       if (0 != pci_set_power_state(pci_dev, state)) {
+               pci_disable_device(pci_dev);
+               dev->state.disabled = 1;
+       }
+       return 0;
+}
+
+int cx8802_resume_common(struct pci_dev *pci_dev)
+{
+        struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+       struct cx88_core *core = dev->core;
+
+       if (dev->state.disabled) {
+               pci_enable_device(pci_dev);
+               dev->state.disabled = 0;
+       }
+       pci_set_power_state(pci_dev, 0);
+       pci_restore_state(pci_dev);
+
+#if 1
+       /* FIXME: re-initialize hardware */
+       cx88_reset(dev->core);
+#endif
+
+       /* restart video+vbi capture */
+       spin_lock(&dev->slock);
+       if (!list_empty(&dev->mpegq.active)) {
+               printk("%s: resume mpeg\n", core->name);
+               cx8802_restart_queue(dev,&dev->mpegq);
+       }
+       spin_unlock(&dev->slock);
+
+       return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+EXPORT_SYMBOL(cx8802_buf_prepare);
+EXPORT_SYMBOL(cx8802_buf_queue);
+EXPORT_SYMBOL(cx8802_cancel_buffers);
+
+EXPORT_SYMBOL(cx8802_init_common);
+EXPORT_SYMBOL(cx8802_fini_common);
+
+EXPORT_SYMBOL(cx8802_suspend_common);
+EXPORT_SYMBOL(cx8802_resume_common);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
new file mode 100644 (file)
index 0000000..b51f0ac
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * $Id: saa7134-dvb.c,v 1.4 2004/11/07 14:44:59 kraxel Exp $
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  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 <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/suspend.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_init(struct saa7134_dev *dev)
+{
+       printk("%s: %s\n",dev->name,__FUNCTION__);
+
+       /* init struct videobuf_dvb */
+       dev->dvb.name = dev->name;
+       videobuf_queue_init(&dev->dvb.dvbq, &saa7134_ts_qops,
+                           dev->pci, &dev->slock,
+                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                           V4L2_FIELD_TOP,
+                           sizeof(struct saa7134_buf),
+                           dev);
+
+       /* TODO: init frontend */
+       if (NULL == dev->dvb.frontend)
+               return -1;
+
+       /* register everything else */
+       return videobuf_dvb_register(&dev->dvb);
+}
+
+static int dvb_fini(struct saa7134_dev *dev)
+{
+       printk("%s: %s\n",dev->name,__FUNCTION__);
+       videobuf_dvb_unregister(&dev->dvb);
+       return 0;
+}
+
+static struct saa7134_mpeg_ops dvb_ops = {
+       .type          = SAA7134_MPEG_DVB,
+       .init          = dvb_init,
+       .fini          = dvb_fini,
+};
+
+static int __init dvb_register(void)
+{
+       return saa7134_ts_register(&dvb_ops);
+}
+
+static void __exit dvb_unregister(void)
+{
+       saa7134_ts_unregister(&dvb_ops);
+}
+
+module_init(dvb_register);
+module_exit(dvb_unregister);
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * compile-command: "make DVB=1"
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
new file mode 100644 (file)
index 0000000..c2bca39
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * $Id: saa7134-empress.c,v 1.3 2004/11/07 13:17:15 kraxel Exp $
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  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 <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+#include <media/saa6752hs.h>
+
+/* ------------------------------------------------------------------ */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+module_param_array(empress_nr, int, NULL, 0444);
+MODULE_PARM_DESC(empress_nr,"ts device number");
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+#define dprintk(fmt, arg...)   if (debug)                      \
+       printk(KERN_DEBUG "%s/empress: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static void ts_reset_encoder(struct saa7134_dev* dev)
+{
+       saa_writeb(SAA7134_SPECIAL_MODE, 0x00);
+       msleep(10);
+       saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+       msleep(100);
+}
+
+static int ts_init_encoder(struct saa7134_dev* dev, void* arg)
+{
+       ts_reset_encoder(dev);
+       saa7134_i2c_call_clients(dev, MPEG_SETPARAMS, arg);
+       return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int ts_open(struct inode *inode, struct file *file)
+{
+       int minor = iminor(inode);
+       struct saa7134_dev *h,*dev = NULL;
+       struct list_head *list;
+       int err;
+
+       list_for_each(list,&saa7134_devlist) {
+               h = list_entry(list, struct saa7134_dev, devlist);
+               if (h->empress_dev && h->empress_dev->minor == minor)
+                       dev = h;
+       }
+       if (NULL == dev)
+               return -ENODEV;
+
+       dprintk("open minor=%d\n",minor);
+       down(&dev->empress_tsq.lock);
+       err = -EBUSY;
+       if (dev->empress_users)
+               goto done;
+
+       dev->empress_users++;
+       file->private_data = dev;
+       ts_init_encoder(dev, NULL);
+       err = 0;
+
+ done:
+       up(&dev->empress_tsq.lock);
+       return err;
+}
+
+static int ts_release(struct inode *inode, struct file *file)
+{
+       struct saa7134_dev *dev = file->private_data;
+
+       if (dev->empress_tsq.streaming)
+               videobuf_streamoff(&dev->empress_tsq);
+       down(&dev->empress_tsq.lock);
+       if (dev->empress_tsq.reading)
+               videobuf_read_stop(&dev->empress_tsq);
+       dev->empress_users--;
+
+       /* stop the encoder */
+       ts_reset_encoder(dev);
+
+       up(&dev->empress_tsq.lock);
+       return 0;
+}
+
+static ssize_t
+ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+       struct saa7134_dev *dev = file->private_data;
+
+       return videobuf_read_stream(&dev->empress_tsq,
+                                   data, count, ppos, 0,
+                                   file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int
+ts_poll(struct file *file, struct poll_table_struct *wait)
+{
+       struct saa7134_dev *dev = file->private_data;
+
+       return videobuf_poll_stream(file, &dev->empress_tsq, wait);
+}
+
+
+static int
+ts_mmap(struct file *file, struct vm_area_struct * vma)
+{
+       struct saa7134_dev *dev = file->private_data;
+
+       return videobuf_mmap_mapper(&dev->empress_tsq, vma);
+}
+
+/*
+ * This function is _not_ called directly, but from
+ * video_generic_ioctl (and maybe others).  userspace
+ * copying is done already, arg is a kernel pointer.
+ */
+static int ts_do_ioctl(struct inode *inode, struct file *file,
+                      unsigned int cmd, void *arg)
+{
+       struct saa7134_dev *dev = file->private_data;
+
+       if (debug > 1)
+               saa7134_print_ioctl(dev->name,cmd);
+       switch (cmd) {
+       case VIDIOC_QUERYCAP:
+       {
+               struct v4l2_capability *cap = arg;
+
+               memset(cap,0,sizeof(*cap));
+                strcpy(cap->driver, "saa7134");
+               strlcpy(cap->card, saa7134_boards[dev->board].name,
+                       sizeof(cap->card));
+               sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
+               cap->version = SAA7134_VERSION_CODE;
+               cap->capabilities =
+                       V4L2_CAP_VIDEO_CAPTURE |
+                       V4L2_CAP_READWRITE |
+                       V4L2_CAP_STREAMING;
+               return 0;
+       }
+
+       /* --- input switching --------------------------------------- */
+       case VIDIOC_ENUMINPUT:
+       {
+               struct v4l2_input *i = arg;
+
+               if (i->index != 0)
+                       return -EINVAL;
+               i->type = V4L2_INPUT_TYPE_CAMERA;
+               strcpy(i->name,"CCIR656");
+               return 0;
+       }
+       case VIDIOC_G_INPUT:
+       {
+               int *i = arg;
+               *i = 0;
+               return 0;
+       }
+       case VIDIOC_S_INPUT:
+       {
+               int *i = arg;
+
+               if (*i != 0)
+                       return -EINVAL;
+               return 0;
+       }
+       /* --- capture ioctls ---------------------------------------- */
+
+       case VIDIOC_ENUM_FMT:
+       {
+               struct v4l2_fmtdesc *f = arg;
+               int index;
+
+               index = f->index;
+               if (index != 0)
+                       return -EINVAL;
+
+               memset(f,0,sizeof(*f));
+               f->index = index;
+               strlcpy(f->description, "MPEG TS", sizeof(f->description));
+               f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               f->pixelformat = V4L2_PIX_FMT_MPEG;
+               return 0;
+       }
+
+       case VIDIOC_G_FMT:
+       {
+               struct v4l2_format *f = arg;
+
+               memset(f,0,sizeof(*f));
+               f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+               /* FIXME: translate subsampling type EMPRESS into
+                *        width/height: */
+               f->fmt.pix.width        = 720; /* D1 */
+               f->fmt.pix.height       = 576;
+               f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+               f->fmt.pix.sizeimage    = TS_PACKET_SIZE * dev->ts.nr_packets;
+               return 0;
+       }
+
+       case VIDIOC_S_FMT:
+       {
+               struct v4l2_format *f = arg;
+
+               if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                   return -EINVAL;
+
+               /*
+                 FIXME: translate and round width/height into EMPRESS
+                 subsample type:
+
+                         type  |   PAL   |  NTSC
+                       ---------------------------
+                         SIF   | 352x288 | 352x240
+                        1/2 D1 | 352x576 | 352x480
+                        2/3 D1 | 480x576 | 480x480
+                         D1    | 720x576 | 720x480
+               */
+
+               f->fmt.pix.width        = 720; /* D1 */
+               f->fmt.pix.height       = 576;
+               f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+               f->fmt.pix.sizeimage    = TS_PACKET_SIZE* dev->ts.nr_packets;
+               return 0;
+       }
+
+       case VIDIOC_REQBUFS:
+               return videobuf_reqbufs(&dev->empress_tsq,arg);
+
+       case VIDIOC_QUERYBUF:
+               return videobuf_querybuf(&dev->empress_tsq,arg);
+
+       case VIDIOC_QBUF:
+               return videobuf_qbuf(&dev->empress_tsq,arg);
+
+       case VIDIOC_DQBUF:
+               return videobuf_dqbuf(&dev->empress_tsq,arg,
+                                     file->f_flags & O_NONBLOCK);
+
+       case VIDIOC_STREAMON:
+               return videobuf_streamon(&dev->empress_tsq);
+
+       case VIDIOC_STREAMOFF:
+               return videobuf_streamoff(&dev->empress_tsq);
+
+       case VIDIOC_QUERYCTRL:
+       case VIDIOC_G_CTRL:
+       case VIDIOC_S_CTRL:
+               return saa7134_common_ioctl(dev, cmd, arg);
+
+       case MPEG_SETPARAMS:
+               return ts_init_encoder(dev, arg);
+
+       default:
+               return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+static int ts_ioctl(struct inode *inode, struct file *file,
+                    unsigned int cmd, unsigned long arg)
+{
+       return video_usercopy(inode, file, cmd, arg, ts_do_ioctl);
+}
+
+static struct file_operations ts_fops =
+{
+       .owner    = THIS_MODULE,
+       .open     = ts_open,
+       .release  = ts_release,
+       .read     = ts_read,
+       .poll     = ts_poll,
+       .mmap     = ts_mmap,
+       .ioctl    = ts_ioctl,
+       .llseek   = no_llseek,
+};
+
+/* ----------------------------------------------------------- */
+
+static struct video_device saa7134_empress_template =
+{
+       .name          = "saa7134-empress",
+       .type          = 0 /* FIXME */,
+       .type2         = 0 /* FIXME */,
+       .hardware      = 0,
+       .fops          = &ts_fops,
+       .minor         = -1,
+};
+
+static int empress_init(struct saa7134_dev *dev)
+{
+       int err;
+
+       dprintk("%s: %s\n",dev->name,__FUNCTION__);
+       dev->empress_dev = video_device_alloc();
+       if (NULL == dev->empress_dev)
+               return -ENOMEM;
+       *(dev->empress_dev) = saa7134_empress_template;
+       dev->empress_dev->dev     = &dev->pci->dev;
+       dev->empress_dev->release = video_device_release;
+       snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name),
+                "%s empress (%s)", dev->name,
+                saa7134_boards[dev->board].name);
+
+       err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER,
+                                   empress_nr[dev->nr]);
+       if (err < 0) {
+               printk(KERN_INFO "%s: can't register video device\n",
+                      dev->name);
+               video_device_release(dev->empress_dev);
+               dev->empress_dev = NULL;
+               return err;
+       }
+       printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
+              dev->name,dev->empress_dev->minor & 0x1f);
+
+       videobuf_queue_init(&dev->empress_tsq, &saa7134_ts_qops,
+                           dev->pci, &dev->slock,
+                           V4L2_BUF_TYPE_VIDEO_CAPTURE,
+                           V4L2_FIELD_ALTERNATE,
+                           sizeof(struct saa7134_buf),
+                           dev);
+       return 0;
+}
+
+static int empress_fini(struct saa7134_dev *dev)
+{
+       dprintk("%s: %s\n",dev->name,__FUNCTION__);
+
+       if (NULL == dev->empress_dev)
+               return 0;
+       video_unregister_device(dev->empress_dev);
+       dev->empress_dev = NULL;
+       return 0;
+}
+
+static struct saa7134_mpeg_ops empress_ops = {
+       .type          = SAA7134_MPEG_EMPRESS,
+       .init          = empress_init,
+       .fini          = empress_fini,
+};
+
+static int __init empress_register(void)
+{
+       return saa7134_ts_register(&empress_ops);
+}
+
+static void __exit empress_unregister(void)
+{
+       saa7134_ts_unregister(&empress_ops);
+}
+
+module_init(empress_register);
+module_exit(empress_unregister);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
new file mode 100644 (file)
index 0000000..21b844a
--- /dev/null
@@ -0,0 +1,577 @@
+/*
+ * tveeprom - eeprom decoder for tvcard configuration eeproms
+ *
+ * Data and decoding routines shamelessly borrowed from bttv-cards.c
+ * eeprom access routine shamelessly borrowed from bttv-if.c
+ * which are:
+
+    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+                           & Marcus Metzler (mocm@thp.uni-koeln.de)
+    (c) 1999-2001 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+ * Adjustments to fit a more general model and all bugs:
+
+       Copyright (C) 2003 John Klar <linpvr at projectplasma.com>
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+
+MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver");
+MODULE_AUTHOR("John Klar");
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+#define STRM(array,i) (i < sizeof(array)/sizeof(char*) ? array[i] : "unknown")
+
+#define dprintk(num, args...) \
+       do { \
+               if (debug >= num) \
+                       printk(KERN_INFO "tveeprom: " args); \
+       } while (0)
+
+#define TVEEPROM_KERN_ERR(args...) printk(KERN_ERR "tveeprom: " args);
+#define TVEEPROM_KERN_INFO(args...) printk(KERN_INFO "tveeprom: " args);
+
+/* ----------------------------------------------------------------------- */
+/* some hauppauge specific stuff                                           */
+
+static struct HAUPPAUGE_TUNER_FMT
+{
+       int     id;
+       char *name;
+}
+hauppauge_tuner_fmt[] =
+{
+       { 0x00000000, "unknown1" },
+       { 0x00000000, "unknown2" },
+       { 0x00000007, "PAL(B/G)" },
+       { 0x00001000, "NTSC(M)" },
+       { 0x00000010, "PAL(I)" },
+       { 0x00400000, "SECAM(L/L�)" },
+       { 0x00000e00, "PAL(D/K)" },
+       { 0x03000000, "ATSC Digital" },
+};
+
+/* This is the full list of possible tuners. Many thanks to Hauppauge for
+   supplying this information. Note that many tuners where only used for
+   testing and never made it to the outside world. So you will only see
+   a subset in actual produced cards. */
+static struct HAUPPAUGE_TUNER
+{
+       int  id;
+       char *name;
+}
+hauppauge_tuner[] =
+{
+       /* 0-9 */
+       { TUNER_ABSENT,        "None" },
+       { TUNER_ABSENT,        "External" },
+       { TUNER_ABSENT,        "Unspecified" },
+       { TUNER_PHILIPS_PAL,   "Philips FI1216" },
+       { TUNER_PHILIPS_SECAM, "Philips FI1216MF" },
+       { TUNER_PHILIPS_NTSC,  "Philips FI1236" },
+       { TUNER_PHILIPS_PAL_I, "Philips FI1246" },
+       { TUNER_PHILIPS_PAL_DK,"Philips FI1256" },
+       { TUNER_PHILIPS_PAL,   "Philips FI1216 MK2" },
+       { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" },
+       /* 10-19 */
+       { TUNER_PHILIPS_NTSC,  "Philips FI1236 MK2" },
+       { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" },
+       { TUNER_PHILIPS_PAL_DK,"Philips FI1256 MK2" },
+       { TUNER_TEMIC_NTSC,    "Temic 4032FY5" },
+       { TUNER_TEMIC_PAL,     "Temic 4002FH5" },
+       { TUNER_TEMIC_PAL_I,   "Temic 4062FY5" },
+       { TUNER_PHILIPS_PAL,   "Philips FR1216 MK2" },
+       { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" },
+       { TUNER_PHILIPS_NTSC,  "Philips FR1236 MK2" },
+       { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" },
+       /* 20-29 */
+       { TUNER_PHILIPS_PAL_DK,"Philips FR1256 MK2" },
+       { TUNER_PHILIPS_PAL,   "Philips FM1216" },
+       { TUNER_PHILIPS_SECAM, "Philips FM1216MF" },
+       { TUNER_PHILIPS_NTSC,  "Philips FM1236" },
+       { TUNER_PHILIPS_PAL_I, "Philips FM1246" },
+       { TUNER_PHILIPS_PAL_DK,"Philips FM1256" },
+       { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" },
+       { TUNER_ABSENT,        "Samsung TCPN9082D" },
+       { TUNER_ABSENT,        "Samsung TCPM9092P" },
+       { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" },
+       /* 30-39 */
+       { TUNER_ABSENT,        "Samsung TCPN9085D" },
+       { TUNER_ABSENT,        "Samsung TCPB9085P" },
+       { TUNER_ABSENT,        "Samsung TCPL9091P" },
+       { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" },
+       { TUNER_PHILIPS_FQ1216ME,   "Philips FQ1216 ME" },
+       { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" },
+        { TUNER_PHILIPS_NTSC,        "Philips TD1536" },
+        { TUNER_PHILIPS_NTSC,        "Philips TD1536D" },
+       { TUNER_PHILIPS_NTSC,  "Philips FMR1236" }, /* mono radio */
+       { TUNER_ABSENT,        "Philips FI1256MP" },
+       /* 40-49 */
+       { TUNER_ABSENT,        "Samsung TCPQ9091P" },
+       { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" },
+       { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" },
+       { TUNER_TEMIC_4046FM5,     "Temic 4046FM5" },
+       { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" },
+       { TUNER_ABSENT,        "Philips TD1536D FH 44"},
+       { TUNER_LG_NTSC_FM,    "LG TP18NSR01F"},
+       { TUNER_LG_PAL_FM,     "LG TP18PSB01D"},
+       { TUNER_LG_PAL,        "LG TP18PSB11D"},
+       { TUNER_LG_PAL_I_FM,   "LG TAPC-I001D"},
+       /* 50-59 */
+       { TUNER_LG_PAL_I,      "LG TAPC-I701D"},
+       { TUNER_ABSENT,        "Temic 4042FI5"},
+       { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"},
+       { TUNER_ABSENT,        "LG TPI8NSR11F"},
+       { TUNER_ABSENT,        "Microtune 4049 FM5 Alt I2C"},
+       { TUNER_ABSENT,        "Philips FQ1216ME MK3"},
+       { TUNER_ABSENT,        "Philips FI1236 MK3"},
+       { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"},
+       { TUNER_ABSENT,        "Philips FM1236 MK3"},
+       { TUNER_ABSENT,        "Philips FM1216MP MK3"},
+       /* 60-69 */
+       { TUNER_ABSENT,        "LG S001D MK3"},
+       { TUNER_ABSENT,        "LG M001D MK3"},
+       { TUNER_ABSENT,        "LG S701D MK3"},
+       { TUNER_ABSENT,        "LG M701D MK3"},
+       { TUNER_ABSENT,        "Temic 4146FM5"},
+       { TUNER_ABSENT,        "Temic 4136FY5"},
+       { TUNER_ABSENT,        "Temic 4106FH5"},
+       { TUNER_ABSENT,        "Philips FQ1216LMP MK3"},
+       { TUNER_LG_NTSC_TAPE,  "LG TAPE H001F MK3"},
+       { TUNER_ABSENT,        "LG TAPE H701F MK3"},
+       /* 70-79 */
+       { TUNER_ABSENT,        "LG TALN H200T"},
+       { TUNER_ABSENT,        "LG TALN H250T"},
+       { TUNER_ABSENT,        "LG TALN M200T"},
+       { TUNER_ABSENT,        "LG TALN Z200T"},
+       { TUNER_ABSENT,        "LG TALN S200T"},
+       { TUNER_ABSENT,        "Thompson DTT7595"},
+       { TUNER_ABSENT,        "Thompson DTT7592"},
+       { TUNER_ABSENT,        "Silicon TDA8275C1 8290"},
+       { TUNER_ABSENT,        "Silicon TDA8275C1 8290 FM"},
+       { TUNER_ABSENT,        "Thompson DTT757"},
+       /* 80-89 */
+       { TUNER_ABSENT,        "Philips FQ1216LME MK3"},
+       { TUNER_ABSENT,        "LG TAPC G701D"},
+       { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"},
+       { TUNER_ABSENT,        "TCL 2002MB 3"},
+       { TUNER_ABSENT,        "TCL 2002MI 3"},
+       { TUNER_TCL_2002N,     "TCL 2002N 6A"},
+       { TUNER_ABSENT,        "Philips FQ1236 MK3"},
+       { TUNER_ABSENT,        "Samsung TCPN 2121P30A"},
+       { TUNER_ABSENT,        "Samsung TCPE 4121P30A"},
+       { TUNER_ABSENT,        "TCL MFPE05 2"},
+       /* 90-99 */
+       { TUNER_ABSENT,        "LG TALN H202T"},
+       { TUNER_ABSENT,        "Philips FQ1216AME MK4"},
+       { TUNER_ABSENT,        "Philips FQ1236A MK4"},
+       { TUNER_ABSENT,        "Philips FQ1286A MK4"},
+       { TUNER_ABSENT,        "Philips FQ1216ME MK5"},
+       { TUNER_ABSENT,        "Philips FQ1236 MK5"},
+};
+
+static char *sndtype[] = {
+       "None", "TEA6300", "TEA6320", "TDA9850", "MSP3400C", "MSP3410D",
+       "MSP3415", "MSP3430", "MSP3438", "CS5331", "MSP3435", "MSP3440",
+       "MSP3445", "MSP3411", "MSP3416", "MSP3425",
+
+       "Type 0x10","Type 0x11","Type 0x12","Type 0x13",
+       "Type 0x14","Type 0x15","Type 0x16","Type 0x17",
+       "Type 0x18","MSP4418","Type 0x1a","MSP4448",
+       "Type 0x1c","Type 0x1d","Type 0x1e","Type 0x1f",
+};
+
+static int hasRadioTuner(int tunerType)
+{
+        switch (tunerType) {
+                case 18: //PNPEnv_TUNER_FR1236_MK2:
+                case 23: //PNPEnv_TUNER_FM1236:
+                case 38: //PNPEnv_TUNER_FMR1236:
+                case 16: //PNPEnv_TUNER_FR1216_MK2:
+                case 19: //PNPEnv_TUNER_FR1246_MK2:
+                case 21: //PNPEnv_TUNER_FM1216:
+                case 24: //PNPEnv_TUNER_FM1246:
+                case 17: //PNPEnv_TUNER_FR1216MF_MK2:
+                case 22: //PNPEnv_TUNER_FM1216MF:
+                case 20: //PNPEnv_TUNER_FR1256_MK2:
+                case 25: //PNPEnv_TUNER_FM1256:
+                case 33: //PNPEnv_TUNER_4039FR5:
+                case 42: //PNPEnv_TUNER_4009FR5:
+                case 52: //PNPEnv_TUNER_4049FM5:
+                case 54: //PNPEnv_TUNER_4049FM5_AltI2C:
+                case 44: //PNPEnv_TUNER_4009FN5:
+                case 31: //PNPEnv_TUNER_TCPB9085P:
+                case 30: //PNPEnv_TUNER_TCPN9085D:
+                case 46: //PNPEnv_TUNER_TP18NSR01F:
+                case 47: //PNPEnv_TUNER_TP18PSB01D:
+                case 49: //PNPEnv_TUNER_TAPC_I001D:
+                case 60: //PNPEnv_TUNER_TAPE_S001D_MK3:
+                case 57: //PNPEnv_TUNER_FM1216ME_MK3:
+                case 59: //PNPEnv_TUNER_FM1216MP_MK3:
+                case 58: //PNPEnv_TUNER_FM1236_MK3:
+                case 68: //PNPEnv_TUNER_TAPE_H001F_MK3:
+                case 61: //PNPEnv_TUNER_TAPE_M001D_MK3:
+                case 78: //PNPEnv_TUNER_TDA8275C1_8290_FM:
+                case 89: //PNPEnv_TUNER_TCL_MFPE05_2:
+                    return 1;
+        }
+        return 0;
+}
+
+void tveeprom_hauppauge_analog(struct tveeprom *tvee, unsigned char *eeprom_data)
+{
+       /* ----------------------------------------------
+       ** The hauppauge eeprom format is tagged
+       **
+       ** if packet[0] == 0x84, then packet[0..1] == length
+       ** else length = packet[0] & 3f;
+       ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum
+       **
+       ** In our (ivtv) case we're interested in the following:
+       ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuners)
+       ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into hauppauge_fmts)
+       ** radio:      tag [00].{last} or [0e].00  (bitmask.  bit2=FM)
+       ** audio proc: tag [02].01 or [05].00 (lower nibble indexes lut?)
+
+       ** Fun info:
+       ** model:      tag [00].07-08 or [06].00-01
+       ** revision:   tag [00].09-0b or [06].04-06
+       ** serial#:    tag [01].05-07 or [04].04-06
+
+       ** # of inputs/outputs ???
+       */
+
+       int i, j, len, done, tag, tuner = 0, t_format = 0;
+       char *t_name = NULL, *t_fmt_name = NULL;
+
+       dprintk(1, "%s\n",__FUNCTION__);
+       tvee->revision = done = len = 0;
+       for (i = 0; !done && i < 256; i += len) {
+               dprintk(2, "processing pos = %02x (%02x, %02x)\n",
+                       i, eeprom_data[i], eeprom_data[i + 1]);
+
+               if (eeprom_data[i] == 0x84) {
+                       len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8);
+                       i+=3;
+               } else if ((eeprom_data[i] & 0xf0) == 0x70) {
+                       if ((eeprom_data[i] & 0x08)) {
+                               /* verify checksum! */
+                               done = 1;
+                               break;
+                       }
+                       len = eeprom_data[i] & 0x07;
+                       ++i;
+               } else {
+                       TVEEPROM_KERN_ERR("Encountered bad packet header [%02x]. "
+                                  "Corrupt or not a Hauppauge eeprom.\n", eeprom_data[i]);
+                       return;
+               }
+
+               dprintk(1, "%3d [%02x] ", len, eeprom_data[i]);
+               for(j = 1; j < len; j++) {
+                       dprintk(1, "%02x ", eeprom_data[i + j]);
+               }
+               dprintk(1, "\n");
+
+               /* process by tag */
+               tag = eeprom_data[i];
+               switch (tag) {
+               case 0x00:
+                       tuner = eeprom_data[i+6];
+                       t_format = eeprom_data[i+5];
+                       tvee->has_radio = eeprom_data[i+len-1];
+                       tvee->model =
+                               eeprom_data[i+8] +
+                               (eeprom_data[i+9] << 8);
+                       tvee->revision = eeprom_data[i+10] +
+                               (eeprom_data[i+11] << 8) +
+                               (eeprom_data[i+12] << 16);
+                       break;
+               case 0x01:
+                       tvee->serial_number =
+                               eeprom_data[i+6] +
+                               (eeprom_data[i+7] << 8) +
+                               (eeprom_data[i+8] << 16);
+                       break;
+               case 0x02:
+                       tvee->audio_processor = eeprom_data[i+2] & 0x0f;
+                       break;
+               case 0x04:
+                       tvee->serial_number =
+                               eeprom_data[i+5] +
+                               (eeprom_data[i+6] << 8) +
+                               (eeprom_data[i+7] << 16);
+                       break;
+               case 0x05:
+                       tvee->audio_processor = eeprom_data[i+1] & 0x0f;
+                       break;
+               case 0x06:
+                       tvee->model =
+                               eeprom_data[i+1] +
+                               (eeprom_data[i+2] << 8);
+                       tvee->revision = eeprom_data[i+5] +
+                               (eeprom_data[i+6] << 8) +
+                               (eeprom_data[i+7] << 16);
+                       break;
+               case 0x0a:
+                       tuner = eeprom_data[i+2];
+                       t_format = eeprom_data[i+1];
+                       break;
+               case 0x0e:
+                       tvee->has_radio = eeprom_data[i+1];
+                       break;
+               default:
+                       dprintk(1, "Not sure what to do with tag [%02x]\n", tag);
+                       /* dump the rest of the packet? */
+               }
+
+       }
+
+       if (!done) {
+               TVEEPROM_KERN_ERR("Ran out of data!\n");
+               return;
+       }
+
+       if (tvee->revision != 0) {
+               tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f);
+               tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f);
+               tvee->rev_str[2] = 32 + ((tvee->revision >>  6) & 0x3f);
+               tvee->rev_str[3] = 32 + ( tvee->revision        & 0x3f);
+               tvee->rev_str[4] = 0;
+       }
+
+        if (hasRadioTuner(tuner) && !tvee->has_radio) {
+           TVEEPROM_KERN_INFO("The eeprom says no radio is present, but the tuner type\n");
+           TVEEPROM_KERN_INFO("indicates otherwise. I will assume that radio is present.\n");
+            tvee->has_radio = 1;
+        }
+
+       if (tuner < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) {
+               tvee->tuner_type = hauppauge_tuner[tuner].id;
+               t_name = hauppauge_tuner[tuner].name;
+       } else {
+               t_name = "<unknown>";
+       }
+
+       tvee->tuner_formats = 0;
+       t_fmt_name = "<none>";
+       for (i = 0; i < 8; i++) {
+               if (t_format & (1<<i)) {
+                       tvee->tuner_formats |= hauppauge_tuner_fmt[i].id;
+                       /* yuck */
+                       t_fmt_name = hauppauge_tuner_fmt[i].name;
+               }
+       }
+
+#if 0
+       if (t_format < sizeof(hauppauge_tuner_fmt)/sizeof(struct HAUPPAUGE_TUNER_FMT)) {
+               tvee->tuner_formats = hauppauge_tuner_fmt[t_format].id;
+               t_fmt_name = hauppauge_tuner_fmt[t_format].name;
+       } else {
+               t_fmt_name = "<unknown>";
+       }
+#endif
+
+       TVEEPROM_KERN_INFO("Hauppauge: model = %d, rev = %s, serial# = %d\n",
+                  tvee->model,
+                  tvee->rev_str,
+                  tvee->serial_number);
+       TVEEPROM_KERN_INFO("tuner = %s (idx = %d, type = %d)\n",
+                  t_name,
+                  tuner,
+                  tvee->tuner_type);
+       TVEEPROM_KERN_INFO("tuner fmt = %s (eeprom = 0x%02x, v4l2 = 0x%08x)\n",
+                  t_fmt_name,
+                  t_format,
+                  tvee->tuner_formats);
+
+       TVEEPROM_KERN_INFO("audio_processor = %s (type = %d)\n",
+                  STRM(sndtype,tvee->audio_processor),
+                  tvee->audio_processor);
+
+}
+EXPORT_SYMBOL(tveeprom_hauppauge_analog);
+
+/* ----------------------------------------------------------------------- */
+/* generic helper functions                                                */
+
+int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len)
+{
+       unsigned char buf;
+       int err;
+
+       dprintk(1, "%s\n",__FUNCTION__);
+       buf = 0;
+       if (1 != (err = i2c_master_send(c,&buf,1))) {
+               printk(KERN_INFO "tveeprom(%s): Huh, no eeprom present (err=%d)?\n",
+                      c->name,err);
+               return -1;
+       }
+       if (len != (err = i2c_master_recv(c,eedata,len))) {
+               printk(KERN_WARNING "tveeprom(%s): i2c eeprom read error (err=%d)\n",
+                      c->name,err);
+               return -1;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(tveeprom_read);
+
+int tveeprom_dump(unsigned char *eedata, int len)
+{
+       int i;
+
+       dprintk(1, "%s\n",__FUNCTION__);
+       for (i = 0; i < len; i++) {
+               if (0 == (i % 16))
+                       printk(KERN_INFO "tveeprom: %02x:",i);
+               printk(" %02x",eedata[i]);
+               if (15 == (i % 16))
+                       printk("\n");
+       }
+       return 0;
+}
+EXPORT_SYMBOL(tveeprom_dump);
+
+/* ----------------------------------------------------------------------- */
+/* needed for ivtv.sf.net at the moment.  Should go away in the long       */
+/* run, just call the exported tveeprom_* directly, there is no point in   */
+/* using the indirect way via i2c_driver->command()                        */
+
+#ifndef I2C_DRIVERID_TVEEPROM
+# define I2C_DRIVERID_TVEEPROM I2C_DRIVERID_EXP2
+#endif
+
+static unsigned short normal_i2c[] = {
+       0xa0 >> 1,
+       I2C_CLIENT_END,
+};
+static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
+I2C_CLIENT_INSMOD;
+
+struct i2c_driver i2c_driver_tveeprom;
+
+static int
+tveeprom_command(struct i2c_client *client,
+                unsigned int       cmd,
+                void              *arg)
+{
+       struct tveeprom eeprom;
+       u32 *eeprom_props = arg;
+       u8 *buf;
+
+       switch (cmd) {
+       case 0:
+               buf = kmalloc(256,GFP_KERNEL);
+               memset(buf,0,256);
+               tveeprom_read(client,buf,256);
+               tveeprom_hauppauge_analog(&eeprom,buf);
+               kfree(buf);
+               eeprom_props[0] = eeprom.tuner_type;
+               eeprom_props[1] = eeprom.tuner_formats;
+               eeprom_props[2] = eeprom.model;
+               eeprom_props[3] = eeprom.revision;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+tveeprom_detect_client(struct i2c_adapter *adapter,
+                      int                 address,
+                      int                 kind)
+{
+       struct i2c_client *client;
+
+       dprintk(1,"%s: id 0x%x @ 0x%x\n",__FUNCTION__,
+              adapter->id, address << 1);
+       client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+       if (NULL == client)
+               return -ENOMEM;
+       memset(client, 0, sizeof(struct i2c_client));
+       client->addr = address;
+       client->adapter = adapter;
+       client->driver = &i2c_driver_tveeprom;
+       client->flags = I2C_CLIENT_ALLOW_USE;
+       snprintf(client->name, sizeof(client->name), "tveeprom");
+        i2c_attach_client(client);
+       return 0;
+}
+
+static int
+tveeprom_attach_adapter (struct i2c_adapter *adapter)
+{
+       dprintk(1,"%s: id 0x%x\n",__FUNCTION__,adapter->id);
+       if (adapter->id != (I2C_ALGO_BIT | I2C_HW_B_BT848))
+               return 0;
+       return i2c_probe(adapter, &addr_data, tveeprom_detect_client);
+}
+
+static int
+tveeprom_detach_client (struct i2c_client *client)
+{
+       int err;
+
+       err = i2c_detach_client(client);
+       if (err < 0)
+               return err;
+       kfree(client);
+       return 0;
+}
+
+struct i2c_driver i2c_driver_tveeprom = {
+       .owner          = THIS_MODULE,
+       .name           = "tveeprom",
+       .id             = I2C_DRIVERID_TVEEPROM,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = tveeprom_attach_adapter,
+       .detach_client  = tveeprom_detach_client,
+       .command        = tveeprom_command,
+};
+
+static int __init tveeprom_init(void)
+{
+       return i2c_add_driver(&i2c_driver_tveeprom);
+}
+
+static void __exit tveeprom_exit(void)
+{
+       i2c_del_driver(&i2c_driver_tveeprom);
+}
+
+module_init(tveeprom_init);
+module_exit(tveeprom_exit);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/video-buf-dvb.c b/drivers/media/video/video-buf-dvb.c
new file mode 100644 (file)
index 0000000..6f3d6ac
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * $Id: video-buf-dvb.c,v 1.5 2004/11/07 13:17:15 kraxel Exp $
+ *
+ * some helper function for simple DVB cards which simply DMA the
+ * complete transport stream and let the computer sort everything else
+ * (i.e. we are using the software demux, ...).  Also uses the
+ * video-buf to manage DMA buffers.
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/suspend.h>
+
+#include <media/video-buf.h>
+#include <media/video-buf-dvb.h>
+
+/* ------------------------------------------------------------------ */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+#define dprintk(fmt, arg...)   if (debug)                      \
+       printk(KERN_DEBUG "%s/dvb: " fmt, dvb->name, ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int videobuf_dvb_thread(void *data)
+{
+       struct videobuf_dvb *dvb = data;
+       struct videobuf_buffer *buf;
+       unsigned long flags;
+       int err;
+
+       dprintk("dvb thread started\n");
+       videobuf_read_start(&dvb->dvbq);
+
+       for (;;) {
+               /* fetch next buffer */
+               buf = list_entry(dvb->dvbq.stream.next,
+                                struct videobuf_buffer, stream);
+               list_del(&buf->stream);
+               err = videobuf_waiton(buf,0,1);
+               BUG_ON(0 != err);
+
+               /* no more feeds left or stop_feed() asked us to quit */
+               if (0 == dvb->nfeeds)
+                       break;
+               if (kthread_should_stop())
+                       break;
+               if (current->flags & PF_FREEZE)
+                       refrigerator(PF_FREEZE);
+
+               /* feed buffer data to demux */
+               if (buf->state == STATE_DONE)
+                       dvb_dmx_swfilter(&dvb->demux, buf->dma.vmalloc,
+                                        buf->size);
+
+               /* requeue buffer */
+               list_add_tail(&buf->stream,&dvb->dvbq.stream);
+               spin_lock_irqsave(dvb->dvbq.irqlock,flags);
+               dvb->dvbq.ops->buf_queue(&dvb->dvbq,buf);
+               spin_unlock_irqrestore(dvb->dvbq.irqlock,flags);
+       }
+
+       videobuf_read_stop(&dvb->dvbq);
+       dprintk("dvb thread stopped\n");
+
+       /* Hmm, linux becomes *very* unhappy without this ... */
+       while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule();
+       }
+       return 0;
+}
+
+static int videobuf_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+       struct dvb_demux *demux  = feed->demux;
+       struct videobuf_dvb *dvb = demux->priv;
+       int rc;
+
+       if (!demux->dmx.frontend)
+               return -EINVAL;
+
+       down(&dvb->lock);
+       dvb->nfeeds++;
+       rc = dvb->nfeeds;
+
+       if (NULL != dvb->thread)
+               goto out;
+       dvb->thread = kthread_run(videobuf_dvb_thread,
+                                 dvb, "%s dvb", dvb->name);
+       if (IS_ERR(dvb->thread)) {
+               rc = PTR_ERR(dvb->thread);
+               dvb->thread = NULL;
+       }
+
+out:
+       up(&dvb->lock);
+       return rc;
+}
+
+static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+       struct dvb_demux *demux  = feed->demux;
+       struct videobuf_dvb *dvb = demux->priv;
+       int err = 0;
+
+       down(&dvb->lock);
+       dvb->nfeeds--;
+       if (0 == dvb->nfeeds  &&  NULL != dvb->thread) {
+               // FIXME: cx8802_cancel_buffers(dev);
+               err = kthread_stop(dvb->thread);
+               dvb->thread = NULL;
+       }
+       up(&dvb->lock);
+       return err;
+}
+
+/* ------------------------------------------------------------------ */
+
+int videobuf_dvb_register(struct videobuf_dvb *dvb)
+{
+       int result;
+
+       init_MUTEX(&dvb->lock);
+
+       /* register adapter */
+       result = dvb_register_adapter(&dvb->adapter, dvb->name, THIS_MODULE);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_adapter;
+       }
+
+       /* register frontend */
+       result = dvb_register_frontend(dvb->adapter, dvb->frontend);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_frontend;
+       }
+
+       /* register demux stuff */
+       dvb->demux.dmx.capabilities =
+               DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+               DMX_MEMORY_BASED_FILTERING;
+       dvb->demux.priv       = dvb;
+       dvb->demux.filternum  = 256;
+       dvb->demux.feednum    = 256;
+       dvb->demux.start_feed = videobuf_dvb_start_feed;
+       dvb->demux.stop_feed  = videobuf_dvb_stop_feed;
+       result = dvb_dmx_init(&dvb->demux);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_dmx;
+       }
+
+       dvb->dmxdev.filternum    = 256;
+       dvb->dmxdev.demux        = &dvb->demux.dmx;
+       dvb->dmxdev.capabilities = 0;
+       result = dvb_dmxdev_init(&dvb->dmxdev, dvb->adapter);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_dmxdev;
+       }
+
+       dvb->fe_hw.source = DMX_FRONTEND_0;
+       result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_hw;
+       }
+
+       dvb->fe_mem.source = DMX_MEMORY_FE;
+       result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_mem;
+       }
+
+       result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+       if (result < 0) {
+               printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
+                      dvb->name, result);
+               goto fail_fe_conn;
+       }
+
+       /* register network adapter */
+       dvb_net_init(dvb->adapter, &dvb->net, &dvb->demux.dmx);
+       return 0;
+
+fail_fe_conn:
+       dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+       dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+       dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+       dvb_dmx_release(&dvb->demux);
+fail_dmx:
+       dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+       dvb_unregister_adapter(dvb->adapter);
+fail_adapter:
+       return result;
+}
+
+void videobuf_dvb_unregister(struct videobuf_dvb *dvb)
+{
+       dvb_net_release(&dvb->net);
+       dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+       dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+       dvb_dmxdev_release(&dvb->dmxdev);
+       dvb_dmx_release(&dvb->demux);
+       dvb_unregister_frontend(dvb->frontend);
+       dvb_unregister_adapter(dvb->adapter);
+}
+
+EXPORT_SYMBOL(videobuf_dvb_register);
+EXPORT_SYMBOL(videobuf_dvb_unregister);
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * compile-command: "make DVB=1"
+ * End:
+ */
diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c
new file mode 100644 (file)
index 0000000..0871134
--- /dev/null
@@ -0,0 +1,1596 @@
+/*
+ *  linux/drivers/mmc/wbsd.c
+ *
+ *  Copyright (C) 2004 Pierre Ossman, 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "wbsd.h"
+
+#define DRIVER_NAME "wbsd"
+#define DRIVER_VERSION "1.0"
+
+#ifdef CONFIG_MMC_DEBUG
+#define DBG(x...) \
+       printk(KERN_DEBUG DRIVER_NAME ": " x)
+#define DBGF(f, x...) \
+       printk(KERN_DEBUG DRIVER_NAME " [%s()]: " f, __func__, ##x)
+#else
+#define DBG(x...)      do { } while (0)
+#define DBGF(x...)     do { } while (0)
+#endif
+
+static unsigned int io = 0x248;
+static unsigned int irq = 6;
+static int dma = 2;
+
+#ifdef CONFIG_MMC_DEBUG
+void DBG_REG(int reg, u8 value)
+{
+       int i;
+       
+       printk(KERN_DEBUG "wbsd: Register %d: 0x%02X %3d '%c' ",
+               reg, (int)value, (int)value, (value < 0x20)?'.':value);
+       
+       for (i = 7;i >= 0;i--)
+       {
+               if (value & (1 << i))
+                       printk("x");
+               else
+                       printk(".");
+       }
+       
+       printk("\n");
+}
+#else
+#define DBG_REG(r, v) do {}  while (0)
+#endif
+
+/*
+ * Basic functions
+ */
+
+static inline void wbsd_unlock_config(struct wbsd_host* host)
+{
+       outb(host->unlock_code, host->config);
+       outb(host->unlock_code, host->config);
+}
+
+static inline void wbsd_lock_config(struct wbsd_host* host)
+{
+       outb(LOCK_CODE, host->config);
+}
+
+static inline void wbsd_write_config(struct wbsd_host* host, u8 reg, u8 value)
+{
+       outb(reg, host->config);
+       outb(value, host->config + 1);
+}
+
+static inline u8 wbsd_read_config(struct wbsd_host* host, u8 reg)
+{
+       outb(reg, host->config);
+       return inb(host->config + 1);
+}
+
+static inline void wbsd_write_index(struct wbsd_host* host, u8 index, u8 value)
+{
+       outb(index, host->base + WBSD_IDXR);
+       outb(value, host->base + WBSD_DATAR);
+}
+
+static inline u8 wbsd_read_index(struct wbsd_host* host, u8 index)
+{
+       outb(index, host->base + WBSD_IDXR);
+       return inb(host->base + WBSD_DATAR);
+}
+
+/*
+ * Common routines
+ */
+
+static void wbsd_init_device(struct wbsd_host* host)
+{
+       u8 setup, ier;
+       
+       /*
+        * Reset chip (SD/MMC part) and fifo.
+        */
+       setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+       setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET;
+       wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+       
+       /*
+        * Read back default clock.
+        */
+       host->clk = wbsd_read_index(host, WBSD_IDX_CLK);
+
+       /*
+        * Power down port.
+        */
+       outb(WBSD_POWER_N, host->base + WBSD_CSR);
+       
+       /*
+        * Set maximum timeout.
+        */
+       wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F);
+       
+       /*
+        * Enable interesting interrupts.
+        */
+       ier = 0;
+       ier |= WBSD_EINT_CARD;
+       ier |= WBSD_EINT_FIFO_THRE;
+       ier |= WBSD_EINT_CCRC;
+       ier |= WBSD_EINT_TIMEOUT;
+       ier |= WBSD_EINT_CRC;
+       ier |= WBSD_EINT_TC;
+
+       outb(ier, host->base + WBSD_EIR);
+
+       /*
+        * Clear interrupts.
+        */
+       inb(host->base + WBSD_ISR);
+}
+
+static void wbsd_reset(struct wbsd_host* host)
+{
+       u8 setup;
+       
+       printk(KERN_ERR DRIVER_NAME ": Resetting chip\n");
+       
+       /*
+        * Soft reset of chip (SD/MMC part).
+        */
+       setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+       setup |= WBSD_SOFT_RESET;
+       wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+}
+
+static void wbsd_request_end(struct wbsd_host* host, struct mmc_request* mrq)
+{
+       unsigned long dmaflags;
+       
+       DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode);
+       
+       if (host->dma >= 0)
+       {
+               /*
+                * Release ISA DMA controller.
+                */
+               dmaflags = claim_dma_lock();
+               disable_dma(host->dma);
+               clear_dma_ff(host->dma);
+               release_dma_lock(dmaflags);
+
+               /*
+                * Disable DMA on host.
+                */
+               wbsd_write_index(host, WBSD_IDX_DMA, 0);
+       }
+       
+       host->mrq = NULL;
+
+       /*
+        * MMC layer might call back into the driver so first unlock.
+        */
+       spin_unlock(&host->lock);
+       mmc_request_done(host->mmc, mrq);
+       spin_lock(&host->lock);
+}
+
+/*
+ * Scatter/gather functions
+ */
+
+static inline void wbsd_init_sg(struct wbsd_host* host, struct mmc_data* data)
+{
+       struct request* req = data->req;
+       
+       /*
+        * Get info. about SG list from data structure.
+        */
+       host->cur_sg = data->sg;
+       host->num_sg = data->sg_len;
+
+       host->offset = 0;
+       host->remain = host->cur_sg->length;
+}
+
+static inline int wbsd_next_sg(struct wbsd_host* host)
+{
+       /*
+        * Skip to next SG entry.
+        */
+       host->cur_sg++;
+       host->num_sg--;
+
+       /*
+        * Any entries left?
+        */
+       if (host->num_sg > 0)
+         {
+           host->offset = 0;
+           host->remain = host->cur_sg->length;
+         }
+       
+       return host->num_sg;
+}
+
+static inline char* wbsd_kmap_sg(struct wbsd_host* host)
+{
+       return kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) +
+               host->cur_sg->offset;
+}
+
+static inline void wbsd_kunmap_sg(struct wbsd_host* host)
+{
+       kunmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ);
+}
+
+static inline void wbsd_sg_to_dma(struct wbsd_host* host, struct mmc_data* data)
+{
+       unsigned int len, i, size;
+       struct scatterlist* sg;
+       char* dmabuf = host->dma_buffer;
+       char* sgbuf;
+       
+       size = host->size;
+       
+       sg = data->sg;
+       len = data->sg_len;
+       
+       /*
+        * Just loop through all entries. Size might not
+        * be the entire list though so make sure that
+        * we do not transfer too much.
+        */
+       for (i = 0;i < len;i++)
+       {
+               sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset;
+               if (size < sg[i].length)
+                       memcpy(dmabuf, sgbuf, size);
+               else
+                       memcpy(dmabuf, sgbuf, sg[i].length);
+               kunmap_atomic(sg[i].page, KM_BIO_SRC_IRQ);
+               dmabuf += sg[i].length;
+               
+               if (size < sg[i].length)
+                       size = 0;
+               else
+                       size -= sg[i].length;
+       
+               if (size == 0)
+                       break;
+       }
+       
+       /*
+        * Check that we didn't get a request to transfer
+        * more data than can fit into the SG list.
+        */
+       
+       BUG_ON(size != 0);
+       
+       host->size -= size;
+}
+
+static inline void wbsd_dma_to_sg(struct wbsd_host* host, struct mmc_data* data)
+{
+       unsigned int len, i, size;
+       struct scatterlist* sg;
+       char* dmabuf = host->dma_buffer;
+       char* sgbuf;
+       
+       size = host->size;
+       
+       sg = data->sg;
+       len = data->sg_len;
+       
+       /*
+        * Just loop through all entries. Size might not
+        * be the entire list though so make sure that
+        * we do not transfer too much.
+        */
+       for (i = 0;i < len;i++)
+       {
+               sgbuf = kmap_atomic(sg[i].page, KM_BIO_SRC_IRQ) + sg[i].offset;
+               if (size < sg[i].length)
+                       memcpy(sgbuf, dmabuf, size);
+               else
+                       memcpy(sgbuf, dmabuf, sg[i].length);
+               kunmap_atomic(sg[i].page, KM_BIO_SRC_IRQ);
+               dmabuf += sg[i].length;
+               
+               if (size < sg[i].length)
+                       size = 0;
+               else
+                       size -= sg[i].length;
+               
+               if (size == 0)
+                       break;
+       }
+       
+       /*
+        * Check that we didn't get a request to transfer
+        * more data than can fit into the SG list.
+        */
+       
+       BUG_ON(size != 0);
+       
+       host->size -= size;
+}
+
+/*
+ * Command handling
+ */
+static inline void wbsd_get_short_reply(struct wbsd_host* host,
+       struct mmc_command* cmd)
+{
+       /*
+        * Correct response type?
+        */
+       if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT)
+       {
+               cmd->error = MMC_ERR_INVALID;
+               return;
+       }
+       
+       cmd->resp[0] =
+               wbsd_read_index(host, WBSD_IDX_RESP12) << 24;
+       cmd->resp[0] |=
+               wbsd_read_index(host, WBSD_IDX_RESP13) << 16;
+       cmd->resp[0] |=
+               wbsd_read_index(host, WBSD_IDX_RESP14) << 8;
+       cmd->resp[0] |=
+               wbsd_read_index(host, WBSD_IDX_RESP15) << 0;
+       cmd->resp[1] =
+               wbsd_read_index(host, WBSD_IDX_RESP16) << 24;
+}
+
+static inline void wbsd_get_long_reply(struct wbsd_host* host,
+       struct mmc_command* cmd)
+{
+       int i;
+       
+       /*
+        * Correct response type?
+        */
+       if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG)
+       {
+               cmd->error = MMC_ERR_INVALID;
+               return;
+       }
+       
+       for (i = 0;i < 4;i++)
+       {
+               cmd->resp[i] =
+                       wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24;
+               cmd->resp[i] |=
+                       wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16;
+               cmd->resp[i] |=
+                       wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8;
+               cmd->resp[i] |=
+                       wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0;
+       }
+}
+
+static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs);
+
+static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd)
+{
+       int i;
+       u8 status, eir, isr;
+       
+       DBGF("Sending cmd (%x)\n", cmd->opcode);
+
+       /*
+        * Disable interrupts as the interrupt routine
+        * will destroy the contents of ISR.
+        */
+       eir = inb(host->base + WBSD_EIR);
+       outb(0, host->base + WBSD_EIR);
+       
+       /*
+        * Send the command (CRC calculated by host).
+        */
+       outb(cmd->opcode, host->base + WBSD_CMDR);
+       for (i = 3;i >= 0;i--)
+               outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR);
+       
+       cmd->error = MMC_ERR_NONE;
+       
+       /*
+        * Wait for the request to complete.
+        */
+       do {
+               status = wbsd_read_index(host, WBSD_IDX_STATUS);
+       } while (status & WBSD_CARDTRAFFIC);
+
+       /*
+        * Do we expect a reply?
+        */
+       if ((cmd->flags & MMC_RSP_MASK) != MMC_RSP_NONE)
+       {
+               /*
+                * Read back status.
+                */
+               isr = inb(host->base + WBSD_ISR);
+               
+               /* Card removed? */
+               if (isr & WBSD_INT_CARD)
+                       cmd->error = MMC_ERR_TIMEOUT;
+               /* Timeout? */
+               else if (isr & WBSD_INT_TIMEOUT)
+                       cmd->error = MMC_ERR_TIMEOUT;
+               /* CRC? */
+               else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC))
+                       cmd->error = MMC_ERR_BADCRC;
+               /* All ok */
+               else
+               {
+                       if ((cmd->flags & MMC_RSP_MASK) == MMC_RSP_SHORT)
+                               wbsd_get_short_reply(host, cmd);
+                       else
+                               wbsd_get_long_reply(host, cmd);
+               }
+       }
+
+       /*
+        * Restore interrupt mask to previous value.
+        */
+       outb(eir, host->base + WBSD_EIR);
+       
+       /*
+        * Call the interrupt routine to jump start
+        * interrupts.
+        */
+       wbsd_irq(0, host, NULL);
+
+       DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error);
+}
+
+/*
+ * Data functions
+ */
+
+static void wbsd_empty_fifo(struct wbsd_host* host)
+{
+       struct mmc_data* data = host->mrq->cmd->data;
+       char* buffer;
+       
+       /*
+        * Handle excessive data.
+        */
+       if (data->bytes_xfered == host->size)
+               return;
+       
+       buffer = wbsd_kmap_sg(host) + host->offset;
+
+       /*
+        * Drain the fifo. This has a tendency to loop longer
+        * than the FIFO length (usually one block).
+        */
+       while (!(inb(host->base + WBSD_FSR) & WBSD_FIFO_EMPTY))
+       {
+               *buffer = inb(host->base + WBSD_DFR);
+               buffer++;
+               host->offset++;
+               host->remain--;
+
+               data->bytes_xfered++;
+               
+               /*
+                * Transfer done?
+                */
+               if (data->bytes_xfered == host->size)
+               {
+                       wbsd_kunmap_sg(host);                           
+                       return;
+               }
+               
+               /*
+                * End of scatter list entry?
+                */
+               if (host->remain == 0)
+               {
+                       wbsd_kunmap_sg(host);
+                       
+                       /*
+                        * Get next entry. Check if last.
+                        */
+                       if (!wbsd_next_sg(host))
+                       {
+                               /*
+                                * We should never reach this point.
+                                * It means that we're trying to
+                                * transfer more blocks than can fit
+                                * into the scatter list.
+                                */
+                               BUG_ON(1);
+                               
+                               host->size = data->bytes_xfered;
+                               
+                               return;
+                       }
+                       
+                       buffer = wbsd_kmap_sg(host);
+               }
+       }
+       
+       wbsd_kunmap_sg(host);
+}
+
+static void wbsd_fill_fifo(struct wbsd_host* host)
+{
+       struct mmc_data* data = host->mrq->cmd->data;
+       char* buffer;
+       
+       /*
+        * Check that we aren't being called after the
+        * entire buffer has been transfered.
+        */
+       if (data->bytes_xfered == host->size)
+               return;
+
+       buffer = wbsd_kmap_sg(host) + host->offset;
+
+       /*
+        * Fill the fifo. This has a tendency to loop longer
+        * than the FIFO length (usually one block).
+        */
+       while (!(inb(host->base + WBSD_FSR) & WBSD_FIFO_FULL))
+       {
+               outb(*buffer, host->base + WBSD_DFR);
+               buffer++;
+               host->offset++;
+               host->remain--;
+               
+               data->bytes_xfered++;
+               
+               /*
+                * Transfer done?
+                */
+               if (data->bytes_xfered == host->size)
+               {
+                       wbsd_kunmap_sg(host);
+                       return;
+               }
+
+               /*
+                * End of scatter list entry?
+                */
+               if (host->remain == 0)
+               {
+                       wbsd_kunmap_sg(host);
+                       
+                       /*
+                        * Get next entry. Check if last.
+                        */
+                       if (!wbsd_next_sg(host))
+                       {
+                               /*
+                                * We should never reach this point.
+                                * It means that we're trying to
+                                * transfer more blocks than can fit
+                                * into the scatter list.
+                                */
+                               BUG_ON(1);
+                               
+                               host->size = data->bytes_xfered;
+                               
+                               return;
+                       }
+                       
+                       buffer = wbsd_kmap_sg(host);
+               }
+       }
+       
+       wbsd_kunmap_sg(host);
+}
+
+static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data)
+{
+       u16 blksize;
+       u8 setup;
+       unsigned long dmaflags;
+
+       DBGF("blksz %04x blks %04x flags %08x\n",
+               1 << data->blksz_bits, data->blocks, data->flags);
+       DBGF("tsac %d ms nsac %d clk\n",
+               data->timeout_ns / 1000000, data->timeout_clks);
+       
+       /*
+        * Calculate size.
+        */
+       host->size = data->blocks << data->blksz_bits;
+
+       /*
+        * Check timeout values for overflow.
+        * (Yes, some cards cause this value to overflow).
+        */
+       if (data->timeout_ns > 127000000)
+               wbsd_write_index(host, WBSD_IDX_TAAC, 127);
+       else
+               wbsd_write_index(host, WBSD_IDX_TAAC, data->timeout_ns/1000000);
+       
+       if (data->timeout_clks > 255)
+               wbsd_write_index(host, WBSD_IDX_NSAC, 255);
+       else
+               wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks);
+       
+       /*
+        * Inform the chip of how large blocks will be
+        * sent. It needs this to determine when to
+        * calculate CRC.
+        *
+        * Space for CRC must be included in the size.
+        */
+       blksize = (1 << data->blksz_bits) + 2;
+       
+       wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
+       wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+
+       /*
+        * Clear the FIFO. This is needed even for DMA
+        * transfers since the chip still uses the FIFO
+        * internally.
+        */
+       setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+       setup |= WBSD_FIFO_RESET;
+       wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+       
+       /*
+        * DMA transfer?
+        */
+       if (host->dma >= 0)
+       {       
+               /*
+                * The buffer for DMA is only 64 kB.
+                */
+               BUG_ON(host->size > 0x10000);
+               if (host->size > 0x10000)
+               {
+                       data->error = MMC_ERR_INVALID;
+                       return;
+               }
+               
+               /*
+                * Transfer data from the SG list to
+                * the DMA buffer.
+                */
+               if (data->flags & MMC_DATA_WRITE)
+                       wbsd_sg_to_dma(host, data);
+               
+               /*
+                * Initialise the ISA DMA controller.
+                */     
+               dmaflags = claim_dma_lock();
+               disable_dma(host->dma);
+               clear_dma_ff(host->dma);
+               if (data->flags & MMC_DATA_READ)
+                       set_dma_mode(host->dma, DMA_MODE_READ);
+               else
+                       set_dma_mode(host->dma, DMA_MODE_WRITE);
+               set_dma_addr(host->dma, host->dma_addr);
+               set_dma_count(host->dma, host->size);
+
+               enable_dma(host->dma);
+               release_dma_lock(dmaflags);
+
+               /*
+                * Enable DMA on the host.
+                */
+               wbsd_write_index(host, WBSD_IDX_DMA,
+                       WBSD_DMA_SINGLE | WBSD_DMA_ENABLE);
+       }
+       else
+       {
+               /*
+                * This flag is used to keep printk
+                * output to a minimum.
+                */
+               host->firsterr = 1;
+               
+               /*
+                * Initialise the SG list.
+                */
+               wbsd_init_sg(host, data);
+       
+               /*
+                * Turn off DMA.
+                */
+               wbsd_write_index(host, WBSD_IDX_DMA, 0);
+       
+               /*
+                * Set up FIFO threshold levels (and fill
+                * buffer if doing a write).
+                */
+               if (data->flags & MMC_DATA_READ)
+               {
+                       wbsd_write_index(host, WBSD_IDX_FIFOEN,
+                               WBSD_FIFOEN_FULL | 8);
+               }
+               else
+               {
+                       wbsd_write_index(host, WBSD_IDX_FIFOEN,
+                               WBSD_FIFOEN_EMPTY | 8);
+                       wbsd_fill_fifo(host);
+               }
+       }       
+               
+       data->error = MMC_ERR_NONE;
+}
+
+static void wbsd_finish_data(struct wbsd_host* host, struct mmc_data* data)
+{
+       unsigned long dmaflags;
+       int count;
+       
+       WARN_ON(host->mrq == NULL);
+
+       /*
+        * Send a stop command if needed.
+        */
+       if (data->stop)
+               wbsd_send_command(host, data->stop);
+       
+       /*
+        * DMA transfer?
+        */
+       if (host->dma >= 0)
+       {
+               /*
+                * Disable DMA on the host.
+                */
+               wbsd_write_index(host, WBSD_IDX_DMA, 0);
+               
+               /*
+                * Turn of ISA DMA controller.
+                */
+               dmaflags = claim_dma_lock();
+               disable_dma(host->dma);
+               clear_dma_ff(host->dma);
+               count = get_dma_residue(host->dma);
+               release_dma_lock(dmaflags);
+               
+               /*
+                * Any leftover data?
+                */
+               if (count)
+               {
+                       printk(KERN_ERR DRIVER_NAME ": Incomplete DMA "
+                               "transfer. %d bytes left.\n", count);
+                       
+                       data->error = MMC_ERR_FAILED;
+               }
+               else
+               {
+                       /*
+                        * Transfer data from DMA buffer to
+                        * SG list.
+                        */
+                       if (data->flags & MMC_DATA_READ)
+                               wbsd_dma_to_sg(host, data);
+                       
+                       data->bytes_xfered = host->size;
+               }
+       }
+       
+       DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered);
+       
+       wbsd_request_end(host, host->mrq);
+}
+
+/*
+ * MMC Callbacks
+ */
+
+static void wbsd_request(struct mmc_host* mmc, struct mmc_request* mrq)
+{
+       struct wbsd_host* host = mmc_priv(mmc);
+       struct mmc_command* cmd;
+
+       /*
+        * Disable tasklets to avoid a deadlock.
+        */
+       spin_lock_bh(&host->lock);
+
+       BUG_ON(host->mrq != NULL);
+
+       cmd = mrq->cmd;
+
+       host->mrq = mrq;
+       
+       /*
+        * If there is no card in the slot then
+        * timeout immediatly.
+        */
+       if (!(inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT))
+       {
+               cmd->error = MMC_ERR_TIMEOUT;
+               goto done;
+       }
+
+       /*
+        * Does the request include data?
+        */
+       if (cmd->data)
+       {
+               wbsd_prepare_data(host, cmd->data);
+               
+               if (cmd->data->error != MMC_ERR_NONE)
+                       goto done;
+       }
+       
+       wbsd_send_command(host, cmd);
+
+       /*
+        * If this is a data transfer the request
+        * will be finished after the data has
+        * transfered.
+        */     
+       if (cmd->data && (cmd->error == MMC_ERR_NONE))
+       {               
+               spin_unlock_bh(&host->lock);
+
+               return;
+       }
+               
+done:
+       wbsd_request_end(host, mrq);
+
+       spin_unlock_bh(&host->lock);
+}
+
+static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
+{
+       struct wbsd_host* host = mmc_priv(mmc);
+       u8 clk, setup, pwr;
+       
+       DBGF("clock %uHz busmode %u powermode %u Vdd %u\n",
+               ios->clock, ios->bus_mode, ios->power_mode, ios->vdd);
+
+       spin_lock_bh(&host->lock);
+
+       /*
+        * Reset the chip on each power off.
+        * Should clear out any weird states.
+        */
+       if (ios->power_mode == MMC_POWER_OFF)
+               wbsd_init_device(host);
+       
+       if (ios->clock >= 24000000)
+               clk = WBSD_CLK_24M;
+       else if (ios->clock >= 16000000)
+               clk = WBSD_CLK_16M;
+       else if (ios->clock >= 12000000)
+               clk = WBSD_CLK_12M;
+       else
+               clk = WBSD_CLK_375K;
+
+       /*
+        * Only write to the clock register when
+        * there is an actual change.
+        */
+       if (clk != host->clk)
+       {
+               wbsd_write_index(host, WBSD_IDX_CLK, clk);
+               host->clk = clk;
+       }
+
+       if (ios->power_mode != MMC_POWER_OFF)
+       {
+               /*
+                * Power up card.
+                */
+               pwr = inb(host->base + WBSD_CSR);
+               pwr &= ~WBSD_POWER_N;
+               outb(pwr, host->base + WBSD_CSR);
+
+               /*
+                * This behaviour is stolen from the
+                * Windows driver. Don't know why, but
+                * it is needed.
+                */
+               setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+               if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+                       setup |= WBSD_DAT3_H;
+               else
+                       setup &= ~WBSD_DAT3_H;
+               wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+
+               mdelay(1);
+       }
+
+       spin_unlock_bh(&host->lock);
+}
+
+/*
+ * Tasklets
+ */
+
+inline static struct mmc_data* wbsd_get_data(struct wbsd_host* host)
+{
+       WARN_ON(!host->mrq);
+       if (!host->mrq)
+               return NULL;
+
+       WARN_ON(!host->mrq->cmd);
+       if (!host->mrq->cmd)
+               return NULL;
+
+       WARN_ON(!host->mrq->cmd->data);
+       if (!host->mrq->cmd->data)
+               return NULL;
+       
+       return host->mrq->cmd->data;
+}
+
+static void wbsd_tasklet_card(unsigned long param)
+{
+       struct wbsd_host* host = (struct wbsd_host*)param;
+       u8 csr;
+       
+       spin_lock(&host->lock);
+       
+       csr = inb(host->base + WBSD_CSR);
+       WARN_ON(csr == 0xff);
+       
+       if (csr & WBSD_CARDPRESENT)
+               DBG("Card inserted\n");
+       else
+       {
+               DBG("Card removed\n");
+               
+               if (host->mrq)
+               {
+                       printk(KERN_ERR DRIVER_NAME
+                               ": Card removed during transfer!\n");
+                       wbsd_reset(host);
+                       
+                       host->mrq->cmd->error = MMC_ERR_FAILED;
+                       tasklet_schedule(&host->finish_tasklet);
+               }
+       }
+       
+       /*
+        * Unlock first since we might get a call back.
+        */
+       spin_unlock(&host->lock);
+
+       mmc_detect_change(host->mmc);
+}
+
+static void wbsd_tasklet_fifo(unsigned long param)
+{
+       struct wbsd_host* host = (struct wbsd_host*)param;
+       struct mmc_data* data;
+       
+       spin_lock(&host->lock);
+               
+       if (!host->mrq)
+               goto end;
+       
+       data = wbsd_get_data(host);
+       if (!data)
+               goto end;
+
+       if (data->flags & MMC_DATA_WRITE)
+               wbsd_fill_fifo(host);
+       else
+               wbsd_empty_fifo(host);
+
+       /*
+        * Done?
+        */
+       if (host->size == data->bytes_xfered)
+       {
+               wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
+               tasklet_schedule(&host->finish_tasklet);
+       }
+
+end:   
+       spin_unlock(&host->lock);
+}
+
+static void wbsd_tasklet_crc(unsigned long param)
+{
+       struct wbsd_host* host = (struct wbsd_host*)param;
+       struct mmc_data* data;
+       
+       spin_lock(&host->lock);
+       
+       WARN_ON(!host->mrq);
+       if (!host->mrq)
+               goto end;
+       
+       data = wbsd_get_data(host);
+       if (!data)
+               goto end;
+       
+       DBGF("CRC error\n");
+
+       data->error = MMC_ERR_BADCRC;
+       
+       tasklet_schedule(&host->finish_tasklet);
+
+end:           
+       spin_unlock(&host->lock);
+}
+
+static void wbsd_tasklet_timeout(unsigned long param)
+{
+       struct wbsd_host* host = (struct wbsd_host*)param;
+       struct mmc_data* data;
+       
+       spin_lock(&host->lock);
+       
+       WARN_ON(!host->mrq);
+       if (!host->mrq)
+               goto end;
+       
+       data = wbsd_get_data(host);
+       if (!data)
+               goto end;
+       
+       DBGF("Timeout\n");
+
+       data->error = MMC_ERR_TIMEOUT;
+       
+       tasklet_schedule(&host->finish_tasklet);
+
+end:   
+       spin_unlock(&host->lock);
+}
+
+static void wbsd_tasklet_finish(unsigned long param)
+{
+       struct wbsd_host* host = (struct wbsd_host*)param;
+       struct mmc_data* data;
+       
+       spin_lock(&host->lock);
+       
+       WARN_ON(!host->mrq);
+       if (!host->mrq)
+               goto end;
+       
+       data = wbsd_get_data(host);
+       if (!data)
+               goto end;
+
+       wbsd_finish_data(host, data);
+       
+end:   
+       spin_unlock(&host->lock);
+}
+
+static void wbsd_tasklet_block(unsigned long param)
+{
+       struct wbsd_host* host = (struct wbsd_host*)param;
+       struct mmc_data* data;
+       
+       spin_lock(&host->lock);
+
+       if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) !=
+               WBSD_CRC_OK)
+       {
+               data = wbsd_get_data(host);
+               if (!data)
+                       goto end;
+               
+               DBGF("CRC error\n");
+
+               data->error = MMC_ERR_BADCRC;
+       
+               tasklet_schedule(&host->finish_tasklet);
+       }
+
+end:   
+       spin_unlock(&host->lock);
+}
+
+/*
+ * Interrupt handling
+ */
+
+static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct wbsd_host* host = dev_id;
+       int isr;
+       
+       isr = inb(host->base + WBSD_ISR);
+
+       /*
+        * Was it actually our hardware that caused the interrupt?
+        */
+       if (isr == 0xff || isr == 0x00)
+               return IRQ_NONE;
+
+       /*
+        * Schedule tasklets as needed.
+        */
+       if (isr & WBSD_INT_CARD)
+               tasklet_schedule(&host->card_tasklet);
+       if (isr & WBSD_INT_FIFO_THRE)
+               tasklet_hi_schedule(&host->fifo_tasklet);
+       if (isr & WBSD_INT_CRC)
+               tasklet_hi_schedule(&host->crc_tasklet);
+       if (isr & WBSD_INT_TIMEOUT)
+               tasklet_hi_schedule(&host->timeout_tasklet);
+       if (isr & WBSD_INT_BUSYEND)
+               tasklet_hi_schedule(&host->block_tasklet);
+       if (isr & WBSD_INT_TC)
+               tasklet_schedule(&host->finish_tasklet);
+       
+       return IRQ_HANDLED;
+}
+
+/*
+ * Support functions for probe
+ */
+
+static int wbsd_scan(struct wbsd_host* host)
+{
+       int i, j, k;
+       int id;
+       
+       /*
+        * Iterate through all ports, all codes to
+        * find hardware that is in our known list.
+        */
+       for (i = 0;i < sizeof(config_ports)/sizeof(int);i++)
+       {
+               if (!request_region(config_ports[i], 2, DRIVER_NAME))
+                       continue;
+                       
+               for (j = 0;j < sizeof(unlock_codes)/sizeof(int);j++)
+               {
+                       id = 0xFFFF;
+                       
+                       outb(unlock_codes[j], config_ports[i]);
+                       outb(unlock_codes[j], config_ports[i]);
+                       
+                       outb(WBSD_CONF_ID_HI, config_ports[i]);
+                       id = inb(config_ports[i] + 1) << 8;
+
+                       outb(WBSD_CONF_ID_LO, config_ports[i]);
+                       id |= inb(config_ports[i] + 1);
+                       
+                       for (k = 0;k < sizeof(valid_ids)/sizeof(int);k++)
+                       {
+                               if (id == valid_ids[k])
+                               {                               
+                                       host->chip_id = id;
+                                       host->config = config_ports[i];
+                                       host->unlock_code = unlock_codes[i];
+                               
+                                       return 0;
+                               }
+                       }
+                       
+                       if (id != 0xFFFF)
+                       {
+                               DBG("Unknown hardware (id %x) found at %x\n",
+                                       id, config_ports[i]);
+                       }
+
+                       outb(LOCK_CODE, config_ports[i]);
+               }
+               
+               release_region(config_ports[i], 2);
+       }
+       
+       return -ENODEV;
+}
+
+static int wbsd_request_regions(struct wbsd_host* host)
+{
+       if (io & 0x7)
+               return -EINVAL;
+       
+       if (!request_region(io, 8, DRIVER_NAME))
+               return -EIO;
+       
+       host->base = io;
+               
+       return 0;
+}
+
+static void wbsd_release_regions(struct wbsd_host* host)
+{
+       if (host->base)
+               release_region(host->base, 8);
+
+       if (host->config)
+               release_region(host->config, 2);
+}
+
+static void wbsd_init_dma(struct wbsd_host* host)
+{
+       host->dma = -1;
+       
+       if (dma < 0)
+               return;
+       
+       if (request_dma(dma, DRIVER_NAME))
+               goto err;
+       
+       /*
+        * We need to allocate a special buffer in
+        * order for ISA to be able to DMA to it.
+        */
+       host->dma_buffer = kmalloc(65536,
+               GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
+       if (!host->dma_buffer)
+               goto free;
+
+       /*
+        * Translate the address to a physical address.
+        */
+       host->dma_addr = isa_virt_to_bus(host->dma_buffer);
+                       
+       /*
+        * ISA DMA must be aligned on a 64k basis.
+        */
+       if ((host->dma_addr & 0xffff) != 0)
+               goto kfree;
+       /*
+        * ISA cannot access memory above 16 MB.
+        */
+       else if (host->dma_addr >= 0x1000000)
+               goto kfree;
+
+       host->dma = dma;
+       
+       return;
+       
+kfree:
+       /*
+        * If we've gotten here then there is some kind of alignment bug
+        */
+       BUG_ON(1);
+       
+       kfree(host->dma_buffer);
+       host->dma_buffer = NULL;
+
+free:
+       free_dma(dma);
+
+err:
+       printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. "
+               "Falling back on FIFO.\n", dma);
+}
+
+static struct mmc_host_ops wbsd_ops = {
+       .request        = wbsd_request,
+       .set_ios        = wbsd_set_ios,
+};
+
+/*
+ * Device probe
+ */
+
+static int wbsd_probe(struct device* dev)
+{
+       struct wbsd_host* host = NULL;
+       struct mmc_host* mmc = NULL;
+       int ret;
+       
+       /*
+        * Allocate MMC structure.
+        */
+       mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
+       if (!mmc)
+               return -ENOMEM;
+       
+       host = mmc_priv(mmc);
+       host->mmc = mmc;
+       
+       /*
+        * Scan for hardware.
+        */
+       ret = wbsd_scan(host);
+       if (ret)
+               goto freemmc;
+
+       /*
+        * Reset the chip.
+        */     
+       wbsd_write_config(host, WBSD_CONF_SWRST, 1);
+       wbsd_write_config(host, WBSD_CONF_SWRST, 0);
+
+       /*
+        * Allocate I/O ports.
+        */
+       ret = wbsd_request_regions(host);
+       if (ret)
+               goto release;
+
+       /*
+        * Set host parameters.
+        */
+       mmc->ops = &wbsd_ops;
+       mmc->f_min = 375000;
+       mmc->f_max = 24000000;
+       mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+       
+       spin_lock_init(&host->lock);
+
+       /*
+        * Select SD/MMC function.
+        */
+       wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+       
+       /*
+        * Set up card detection.
+        */
+       wbsd_write_config(host, WBSD_CONF_PINS, 0x02);
+       
+       /*
+        * Configure I/O port.
+        */
+       wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
+       wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
+
+       /*
+        * Allocate interrupt.
+        */
+       ret = request_irq(irq, wbsd_irq, SA_SHIRQ, DRIVER_NAME, host);
+       if (ret)
+               goto release;
+       
+       host->irq = irq;
+       
+       /*
+        * Set up tasklets.
+        */
+       tasklet_init(&host->card_tasklet, wbsd_tasklet_card, (unsigned long)host);
+       tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, (unsigned long)host);
+       tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc, (unsigned long)host);
+       tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, (unsigned long)host);
+       tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, (unsigned long)host);
+       tasklet_init(&host->block_tasklet, wbsd_tasklet_block, (unsigned long)host);
+       
+       /*
+        * Configure interrupt.
+        */
+       wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
+       
+       /*
+        * Allocate DMA.
+        */
+       wbsd_init_dma(host);
+       
+       /*
+        * If all went well, then configure DMA.
+        */
+       if (host->dma >= 0)
+               wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
+       
+       /*
+        * Maximum number of segments. Worst case is one sector per segment
+        * so this will be 64kB/512.
+        */
+       mmc->max_hw_segs = NR_SG;
+       mmc->max_phys_segs = NR_SG;
+       
+       /*
+        * Maximum number of sectors in one transfer. Also limited by 64kB
+        * buffer.
+        */
+       mmc->max_sectors = 128;
+       
+       /*
+        * Maximum segment size. Could be one segment with the maximum number
+        * of segments.
+        */
+       mmc->max_seg_size = mmc->max_sectors * 512;
+       
+       /*
+        * Enable chip.
+        */
+       wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
+       
+       /*
+        * Power up chip.
+        */
+       wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
+       
+       /*
+        * Power Management stuff. No idea how this works.
+        * Not tested.
+        */
+#ifdef CONFIG_PM
+       wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
+#endif
+
+       /*
+        * Reset the chip into a known state.
+        */
+       wbsd_init_device(host);
+       
+       dev_set_drvdata(dev, mmc);
+       
+       /*
+        * Add host to MMC layer.
+        */
+       mmc_add_host(mmc);
+
+       printk(KERN_INFO "%s: W83L51xD id %x at 0x%x irq %d dma %d\n",
+               mmc->host_name, (int)host->chip_id, (int)host->base,
+               (int)host->irq, (int)host->dma);
+
+       return 0;
+
+release:
+       wbsd_release_regions(host);
+
+freemmc:
+       mmc_free_host(mmc);
+
+       return ret;
+}
+
+/*
+ * Device remove
+ */
+
+static int wbsd_remove(struct device* dev)
+{
+       struct mmc_host* mmc = dev_get_drvdata(dev);
+       struct wbsd_host* host;
+       
+       if (!mmc)
+               return 0;
+
+       host = mmc_priv(mmc);
+       
+       /*
+        * Unregister host with MMC layer.
+        */
+       mmc_remove_host(mmc);
+
+       /*
+        * Power down the SD/MMC function.
+        */
+       wbsd_unlock_config(host);
+       wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+       wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
+       wbsd_lock_config(host);
+       
+       /*
+        * Free resources.
+        */
+       if (host->dma_buffer)
+               kfree(host->dma_buffer);
+       
+       if (host->dma >= 0)
+               free_dma(host->dma);
+
+       free_irq(host->irq, host);
+       
+       tasklet_kill(&host->card_tasklet);
+       tasklet_kill(&host->fifo_tasklet);
+       tasklet_kill(&host->crc_tasklet);
+       tasklet_kill(&host->timeout_tasklet);
+       tasklet_kill(&host->finish_tasklet);
+       tasklet_kill(&host->block_tasklet);
+       
+       wbsd_release_regions(host);
+       
+       mmc_free_host(mmc);
+
+       return 0;
+}
+
+/*
+ * Power management
+ */
+
+#ifdef CONFIG_PM
+static int wbsd_suspend(struct device *dev, u32 state, u32 level)
+{
+       DBGF("Not yet supported\n");
+
+       return 0;
+}
+
+static int wbsd_resume(struct device *dev, u32 level)
+{
+       DBGF("Not yet supported\n");
+
+       return 0;
+}
+#else
+#define wbsd_suspend NULL
+#define wbsd_resume NULL
+#endif
+
+static void wbsd_release(struct device *dev)
+{
+}
+
+static struct platform_device wbsd_device = {
+       .name           = DRIVER_NAME,
+       .id                     = -1,
+       .dev            = {
+               .release = wbsd_release,
+       },
+};
+
+static struct device_driver wbsd_driver = {
+       .name           = DRIVER_NAME,
+       .bus            = &platform_bus_type,
+       .probe          = wbsd_probe,
+       .remove         = wbsd_remove,
+       
+       .suspend        = wbsd_suspend,
+       .resume         = wbsd_resume,
+};
+
+/*
+ * Module loading/unloading
+ */
+
+static int __init wbsd_drv_init(void)
+{
+       int result;
+       
+       printk(KERN_INFO DRIVER_NAME
+               ": Winbond W83L51xD SD/MMC card interface driver, "
+               DRIVER_VERSION "\n");
+       printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
+       
+       result = driver_register(&wbsd_driver);
+       if (result < 0)
+               return result;
+
+       result = platform_device_register(&wbsd_device);
+       if (result < 0)
+               return result;
+
+       return 0;
+}
+
+static void __exit wbsd_drv_exit(void)
+{
+       platform_device_unregister(&wbsd_device);
+       
+       driver_unregister(&wbsd_driver);
+
+       DBG("unloaded\n");
+}
+
+module_init(wbsd_drv_init);
+module_exit(wbsd_drv_exit);
+module_param(io, uint, 0444);
+module_param(irq, uint, 0444);
+module_param(dma, int, 0444);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
+
+MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");
+MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");
+MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");
diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h
new file mode 100644 (file)
index 0000000..51652c3
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ *  linux/drivers/mmc/wbsd.h
+ *
+ *  Copyright (C) 2004 Pierre Ossman, 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+const int config_ports[] = { 0x2E, 0x4E };
+const int unlock_codes[] = { 0x83, 0x87 };
+
+const int valid_ids[] = {
+       0x7112,
+       };
+
+#define LOCK_CODE              0xAA
+
+#define WBSD_CONF_SWRST                0x02
+#define WBSD_CONF_DEVICE       0x07
+#define WBSD_CONF_ID_HI                0x20
+#define WBSD_CONF_ID_LO                0x21
+#define WBSD_CONF_POWER                0x22
+#define WBSD_CONF_PME          0x23
+#define WBSD_CONF_PMES         0x24
+
+#define WBSD_CONF_ENABLE       0x30
+#define WBSD_CONF_PORT_HI      0x60
+#define WBSD_CONF_PORT_LO      0x61
+#define WBSD_CONF_IRQ          0x70
+#define WBSD_CONF_DRQ          0x74
+
+#define WBSD_CONF_PINS         0xF0
+
+#define DEVICE_SD              0x03
+
+#define WBSD_CMDR              0x00
+#define WBSD_DFR               0x01
+#define WBSD_EIR               0x02
+#define WBSD_ISR               0x03
+#define WBSD_FSR               0x04
+#define WBSD_IDXR              0x05
+#define WBSD_DATAR             0x06
+#define WBSD_CSR               0x07
+
+#define WBSD_EINT_CARD         0x40
+#define WBSD_EINT_FIFO_THRE    0x20
+#define WBSD_EINT_CCRC         0x10
+#define WBSD_EINT_TIMEOUT      0x08
+#define WBSD_EINT_PROGEND      0x04
+#define WBSD_EINT_CRC          0x02
+#define WBSD_EINT_TC           0x01
+
+#define WBSD_INT_PENDING       0x80
+#define WBSD_INT_CARD          0x40
+#define WBSD_INT_FIFO_THRE     0x20
+#define WBSD_INT_CRC           0x10
+#define WBSD_INT_TIMEOUT       0x08
+#define WBSD_INT_PROGEND       0x04
+#define WBSD_INT_BUSYEND       0x02
+#define WBSD_INT_TC            0x01
+
+#define WBSD_FIFO_EMPTY                0x80
+#define WBSD_FIFO_FULL         0x40
+#define WBSD_FIFO_EMTHRE       0x20
+#define WBSD_FIFO_FUTHRE       0x10
+#define WBSD_FIFO_SZMASK       0x0F
+
+#define WBSD_MSLED             0x20
+#define WBSD_POWER_N           0x10
+#define WBSD_WRPT              0x04
+#define WBSD_CARDPRESENT       0x01
+
+#define WBSD_IDX_CLK           0x01
+#define WBSD_IDX_PBSMSB                0x02
+#define WBSD_IDX_TAAC          0x03
+#define WBSD_IDX_NSAC          0x04
+#define WBSD_IDX_PBSLSB                0x05
+#define WBSD_IDX_SETUP         0x06
+#define WBSD_IDX_DMA           0x07
+#define WBSD_IDX_FIFOEN                0x08
+#define WBSD_IDX_STATUS                0x10
+#define WBSD_IDX_RSPLEN                0x1E
+#define WBSD_IDX_RESP0         0x1F
+#define WBSD_IDX_RESP1         0x20
+#define WBSD_IDX_RESP2         0x21
+#define WBSD_IDX_RESP3         0x22
+#define WBSD_IDX_RESP4         0x23
+#define WBSD_IDX_RESP5         0x24
+#define WBSD_IDX_RESP6         0x25
+#define WBSD_IDX_RESP7         0x26
+#define WBSD_IDX_RESP8         0x27
+#define WBSD_IDX_RESP9         0x28
+#define WBSD_IDX_RESP10                0x29
+#define WBSD_IDX_RESP11                0x2A
+#define WBSD_IDX_RESP12                0x2B
+#define WBSD_IDX_RESP13                0x2C
+#define WBSD_IDX_RESP14                0x2D
+#define WBSD_IDX_RESP15                0x2E
+#define WBSD_IDX_RESP16                0x2F
+#define WBSD_IDX_CRCSTATUS     0x30
+#define WBSD_IDX_ISR           0x3F
+
+#define WBSD_CLK_375K          0x00
+#define WBSD_CLK_12M           0x01
+#define WBSD_CLK_16M           0x02
+#define WBSD_CLK_24M           0x03
+
+#define WBSD_DAT3_H            0x08
+#define WBSD_FIFO_RESET                0x04
+#define WBSD_SOFT_RESET                0x02
+#define WBSD_INC_INDEX         0x01
+
+#define WBSD_DMA_SINGLE                0x02
+#define WBSD_DMA_ENABLE                0x01
+
+#define WBSD_FIFOEN_EMPTY      0x20
+#define WBSD_FIFOEN_FULL       0x10
+#define WBSD_FIFO_THREMASK     0x0F
+
+#define WBSD_BUSY              0x20
+#define WBSD_CARDTRAFFIC       0x04
+#define WBSD_SENDCMD           0x02
+#define WBSD_RECVRES           0x01
+
+#define WBSD_RSP_SHORT         0x00
+#define WBSD_RSP_LONG          0x01
+
+#define WBSD_CRC_MASK          0x1F
+#define WBSD_CRC_OK            0x05 /* S010E (00101) */
+#define WBSD_CRC_FAIL          0x0B /* S101E (01011) */
+
+
+/* 64kB / 512 */
+#define NR_SG                  128
+
+struct wbsd_host
+{
+       struct mmc_host*        mmc;            /* MMC structure */
+       
+       spinlock_t              lock;           /* Mutex */
+
+       struct mmc_request*     mrq;            /* Current request */
+       
+       struct scatterlist      sg[NR_SG];      /* SG list */
+       struct scatterlist*     cur_sg;         /* Current SG entry */
+       unsigned int            num_sg;         /* Number of entries left */
+       
+       unsigned int            offset;         /* Offset into current entry */
+       unsigned int            remain;         /* Data left in curren entry */
+
+       int                     size;           /* Total size of transfer */
+       
+       char*                   dma_buffer;     /* ISA DMA buffer */
+       dma_addr_t              dma_addr;       /* Physical address for same */
+
+       int                     firsterr;       /* See fifo functions */
+       
+       u8                      clk;            /* Current clock speed */
+       
+       int                     config;         /* Config port */
+       u8                      unlock_code;    /* Code to unlock config */
+
+       int                     chip_id;        /* ID of controller */
+       
+       int                     base;           /* I/O port base */
+       int                     irq;            /* Interrupt */
+       int                     dma;            /* DMA channel */
+       
+       struct tasklet_struct   card_tasklet;   /* Tasklet structures */
+       struct tasklet_struct   fifo_tasklet;
+       struct tasklet_struct   crc_tasklet;
+       struct tasklet_struct   timeout_tasklet;
+       struct tasklet_struct   finish_tasklet;
+       struct tasklet_struct   block_tasklet;
+};
diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h
new file mode 100644 (file)
index 0000000..fbf4470
--- /dev/null
@@ -0,0 +1,107 @@
+#ifndef FWH_LOCK_H
+#define FWH_LOCK_H
+
+
+enum fwh_lock_state {
+        FWH_UNLOCKED   = 0,
+       FWH_DENY_WRITE = 1,
+       FWH_IMMUTABLE  = 2,
+       FWH_DENY_READ  = 4,
+};
+
+struct fwh_xxlock_thunk {
+       enum fwh_lock_state val;
+       flstate_t state;
+};
+
+
+#define FWH_XXLOCK_ONEBLOCK_LOCK   ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING})
+#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED,   FL_UNLOCKING})
+
+/*
+ * This locking/unlock is specific to firmware hub parts.  Only one
+ * is known that supports the Intel command set.    Firmware
+ * hub parts cannot be interleaved as they are on the LPC bus
+ * so this code has not been tested with interleaved chips,
+ * and will likely fail in that context.
+ */
+static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, 
+       unsigned long adr, int len, void *thunk)
+{
+       struct cfi_private *cfi = map->fldrv_priv;
+       struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk;
+       int ret;
+
+       /* Refuse the operation if the we cannot look behind the chip */
+       if (chip->start < 0x400000) {
+               DEBUG( MTD_DEBUG_LEVEL3,
+                       "MTD %s(): chip->start: %lx wanted >= 0x400000\n",
+                       __func__, chip->start );
+               return -EIO;
+       }
+       /*
+        * lock block registers:
+        * - on 64k boundariesand
+        * - bit 1 set high
+        * - block lock registers are 4MiB lower - overflow subtract (danger)
+        * 
+        * The address manipulation is first done on the logical address
+        * which is 0 at the start of the chip, and then the offset of
+        * the individual chip is addted to it.  Any other order a weird
+        * map offset could cause problems.
+        */
+       adr = (adr & ~0xffffUL) | 0x2;
+       adr += chip->start - 0x400000;
+
+       /*
+        * This is easy because these are writes to registers and not writes
+        * to flash memory - that means that we don't have to check status
+        * and timeout.
+        */
+       cfi_spin_lock(chip->mutex);
+       ret = get_chip(map, chip, adr, FL_LOCKING);
+       if (ret) {
+               cfi_spin_unlock(chip->mutex);
+               return ret;
+       }
+
+       chip->state = xxlt->state;
+       map_write(map, CMD(xxlt->val), adr);
+
+       /* Done and happy. */
+       chip->state = FL_READY;
+       put_chip(map, chip, adr);
+       cfi_spin_unlock(chip->mutex);
+       return 0;
+}
+
+
+static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+       int ret;
+
+       ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
+               (void *)&FWH_XXLOCK_ONEBLOCK_LOCK);
+
+       return ret;
+}
+
+
+static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+       int ret;
+
+       ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
+               (void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK);
+       
+       return ret;
+}
+
+static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param)
+{
+       printk(KERN_NOTICE "using fwh lock/unlock method\n");
+       /* Setup for the chips with the fwh lock method */
+       mtd->lock   = fwh_lock_varsize;
+       mtd->unlock = fwh_unlock_varsize;
+}
+#endif /* FWH_LOCK_H */
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
new file mode 100644 (file)
index 0000000..1cbdfce
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * $Id: block2mtd.c,v 1.23 2005/01/05 17:05:46 dwmw2 Exp $
+ *
+ * block2mtd.c - create an mtd from a block device
+ *
+ * Copyright (C) 2001,2002     Simon Evans <spse@secret.org.uk>
+ * Copyright (C) 2004          Gareth Bult <Gareth@Encryptec.net>
+ * Copyright (C) 2004,2005     Jörn Engel <joern@wh.fh-wedel.de>
+ *
+ * Licence: GPL
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/bio.h>
+#include <linux/pagemap.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/buffer_head.h>
+
+#define VERSION "$Revision: 1.23 $"
+
+
+#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args)
+#define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args)
+
+
+/* Info for the block device */
+struct block2mtd_dev {
+       struct list_head list;
+       struct block_device *blkdev;
+       struct mtd_info mtd;
+       struct semaphore write_mutex;
+};
+
+
+/* Static info about the MTD, used in cleanup_module */
+static LIST_HEAD(blkmtd_device_list);
+
+
+#define PAGE_READAHEAD 64
+void cache_readahead(struct address_space *mapping, int index)
+{
+       filler_t *filler = (filler_t*)mapping->a_ops->readpage;
+       int i, pagei;
+       unsigned ret = 0;
+       unsigned long end_index;
+       struct page *page;
+       LIST_HEAD(page_pool);
+       struct inode *inode = mapping->host;
+       loff_t isize = i_size_read(inode);
+
+       if (!isize) {
+               INFO("iSize=0 in cache_readahead\n");
+               return;
+       }
+
+       end_index = ((isize - 1) >> PAGE_CACHE_SHIFT);
+
+       spin_lock_irq(&mapping->tree_lock);
+       for (i = 0; i < PAGE_READAHEAD; i++) {
+               pagei = index + i;
+               if (pagei > end_index) {
+                       INFO("Overrun end of disk in cache readahead\n");
+                       break;
+               }
+               page = radix_tree_lookup(&mapping->page_tree, pagei);
+               if (page && (!i))
+                       break;
+               if (page)
+                       continue;
+               spin_unlock_irq(&mapping->tree_lock);
+               page = page_cache_alloc_cold(mapping);
+               spin_lock_irq(&mapping->tree_lock);
+               if (!page)
+                       break;
+               page->index = pagei;
+               list_add(&page->lru, &page_pool);
+               ret++;
+       }
+       spin_unlock_irq(&mapping->tree_lock);
+       if (ret)
+               read_cache_pages(mapping, &page_pool, filler, NULL);
+}
+
+
+static struct page* page_readahead(struct address_space *mapping, int index)
+{
+       filler_t *filler = (filler_t*)mapping->a_ops->readpage;
+       //do_page_cache_readahead(mapping, index, XXX, 64);
+       cache_readahead(mapping, index);
+       return read_cache_page(mapping, index, filler, NULL);
+}
+
+
+/* erase a specified part of the device */
+static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)
+{
+       struct address_space *mapping = dev->blkdev->bd_inode->i_mapping;
+       struct page *page;
+       int index = to >> PAGE_SHIFT;   // page index
+       int pages = len >> PAGE_SHIFT;
+       u_long *p;
+       u_long *max;
+
+       while (pages) {
+               page = page_readahead(mapping, index);
+               if (!page)
+                       return -ENOMEM;
+               if (IS_ERR(page))
+                       return PTR_ERR(page);
+
+               max = (u_long*)page_address(page) + PAGE_SIZE;
+               for (p=(u_long*)page_address(page); p<max; p++) 
+                       if (*p != -1UL) {
+                               lock_page(page);
+                               memset(page_address(page), 0xff, PAGE_SIZE);
+                               set_page_dirty(page);
+                               unlock_page(page);
+                               break;
+                       }
+
+               page_cache_release(page);
+               pages--;
+               index++;
+       }
+       return 0;
+}
+static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+       struct block2mtd_dev *dev = mtd->priv;
+       size_t from = instr->addr;
+       size_t len = instr->len;
+       int err;
+
+       instr->state = MTD_ERASING;
+       down(&dev->write_mutex);
+       err = _block2mtd_erase(dev, from, len);
+       up(&dev->write_mutex);
+       if (err) {
+               ERROR("erase failed err = %d", err);
+               instr->state = MTD_ERASE_FAILED;
+       } else
+               instr->state = MTD_ERASE_DONE;
+
+       instr->state = MTD_ERASE_DONE;
+       mtd_erase_callback(instr);
+       return err;
+}
+
+
+static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+               size_t *retlen, u_char *buf)
+{
+       struct block2mtd_dev *dev = mtd->priv;
+       struct page *page;
+       int index = from >> PAGE_SHIFT;
+       int offset = from & (PAGE_SHIFT-1);
+       int cpylen;
+
+       if (from > mtd->size)
+               return -EINVAL;
+       if (from + len > mtd->size)
+               len = mtd->size - from;
+
+       if (retlen)
+               *retlen = 0;
+
+       while (len) {
+               if ((offset + len) > PAGE_SIZE)
+                       cpylen = PAGE_SIZE - offset;    // multiple pages
+               else
+                       cpylen = len;   // this page
+               len = len - cpylen;
+
+               //      Get page
+               page = page_readahead(dev->blkdev->bd_inode->i_mapping, index);
+               if (!page)
+                       return -ENOMEM;
+               if (IS_ERR(page))
+                       return PTR_ERR(page);
+
+               memcpy(buf, page_address(page) + offset, cpylen);
+               page_cache_release(page);
+
+               if (retlen)
+                       *retlen += cpylen;
+               buf += cpylen;
+               offset = 0;
+               index++;
+       }
+       return 0;
+}
+
+
+/* write data to the underlying device */
+static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
+               loff_t to, size_t len, size_t *retlen)
+{
+       struct page *page;
+       struct address_space *mapping = dev->blkdev->bd_inode->i_mapping;
+       int index = to >> PAGE_SHIFT;   // page index
+       int offset = to & ~PAGE_MASK;   // page offset
+       int cpylen;
+
+       if (retlen)
+               *retlen = 0;
+       while (len) {
+               if ((offset+len) > PAGE_SIZE) 
+                       cpylen = PAGE_SIZE - offset;    // multiple pages
+               else
+                       cpylen = len;                   // this page
+               len = len - cpylen;
+
+               //      Get page
+               page = page_readahead(mapping, index);
+               if (!page)
+                       return -ENOMEM;
+               if (IS_ERR(page))
+                       return PTR_ERR(page);
+
+               if (memcmp(page_address(page)+offset, buf, cpylen)) {
+                       lock_page(page);
+                       memcpy(page_address(page) + offset, buf, cpylen);
+                       set_page_dirty(page);
+                       unlock_page(page);
+               }
+               page_cache_release(page);
+
+               if (retlen)
+                       *retlen += cpylen;
+
+               buf += cpylen;
+               offset = 0;
+               index++;
+       }
+       return 0;
+}
+static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
+               size_t *retlen, const u_char *buf)
+{
+       struct block2mtd_dev *dev = mtd->priv;
+       int err;
+
+       if (!len)
+               return 0;
+       if (to >= mtd->size)
+               return -ENOSPC;
+       if (to + len > mtd->size)
+               len = mtd->size - to;
+
+       down(&dev->write_mutex);
+       err = _block2mtd_write(dev, buf, to, len, retlen);
+       up(&dev->write_mutex);
+       if (err > 0)
+               err = 0;
+       return err;
+}
+
+
+/* sync the device - wait until the write queue is empty */
+static void block2mtd_sync(struct mtd_info *mtd)
+{
+       struct block2mtd_dev *dev = mtd->priv;
+       sync_blockdev(dev->blkdev);
+       return;
+}
+
+
+static void block2mtd_free_device(struct block2mtd_dev *dev)
+{
+       if (!dev)
+               return;
+
+       kfree(dev->mtd.name);
+
+       if (dev->blkdev) {
+               invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping);
+               close_bdev_excl(dev->blkdev);
+       }
+
+       kfree(dev);
+}
+
+
+/* FIXME: ensure that mtd->size % erase_size == 0 */
+static struct block2mtd_dev *add_device(char *devname, int erase_size)
+{
+       struct block_device *bdev;
+       struct block2mtd_dev *dev;
+
+       if (!devname)
+               return NULL;
+
+       dev = kmalloc(sizeof(struct block2mtd_dev), GFP_KERNEL);
+       if (!dev)
+               return NULL;
+       memset(dev, 0, sizeof(*dev));
+
+       /* Get a handle on the device */
+       bdev = open_bdev_excl(devname, O_RDWR, NULL);
+       if (IS_ERR(bdev)) {
+               ERROR("error: cannot open device %s", devname);
+               goto devinit_err;
+       }
+       dev->blkdev = bdev;
+
+       if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
+               ERROR("attempting to use an MTD device as a block device");
+               goto devinit_err;
+       }
+
+       init_MUTEX(&dev->write_mutex);
+
+       /* Setup the MTD structure */
+       /* make the name contain the block device in */
+       dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname),
+                       GFP_KERNEL);
+       if (!dev->mtd.name)
+               goto devinit_err;
+
+       sprintf(dev->mtd.name, "block2mtd: %s", devname);
+
+       dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
+       dev->mtd.erasesize = erase_size;
+       dev->mtd.type = MTD_RAM;
+       dev->mtd.flags = MTD_CAP_RAM;
+       dev->mtd.erase = block2mtd_erase;
+       dev->mtd.write = block2mtd_write;
+       dev->mtd.writev = default_mtd_writev;
+       dev->mtd.sync = block2mtd_sync;
+       dev->mtd.read = block2mtd_read;
+       dev->mtd.readv = default_mtd_readv;
+       dev->mtd.priv = dev;
+       dev->mtd.owner = THIS_MODULE;
+
+       if (add_mtd_device(&dev->mtd)) {
+               /* Device didnt get added, so free the entry */
+               goto devinit_err;
+       }
+       list_add(&dev->list, &blkmtd_device_list);
+       INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index,
+                       dev->mtd.name + strlen("blkmtd: "),
+                       dev->mtd.erasesize >> 10, dev->mtd.erasesize);
+       return dev;
+
+devinit_err:
+       block2mtd_free_device(dev);
+       return NULL;
+}
+
+
+static int ustrtoul(const char *cp, char **endp, unsigned int base)
+{
+       unsigned long result = simple_strtoul(cp, endp, base);
+       switch (**endp) {
+       case 'G' :
+               result *= 1024;
+       case 'M':
+               result *= 1024;
+       case 'k':
+               result *= 1024;
+       /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
+               if ((*endp)[1] == 'i')
+                       (*endp) += 2;
+       }
+       return result;
+}
+
+
+static int parse_num32(u32 *num32, const char *token)
+{
+       char *endp;
+       unsigned long n;
+
+       n = ustrtoul(token, &endp, 0);
+       if (*endp)
+               return -EINVAL;
+
+       *num32 = n;
+       return 0;
+}
+
+
+static int parse_name(char **pname, const char *token, size_t limit)
+{
+       size_t len;
+       char *name;
+
+       len = strlen(token) + 1;
+       if (len > limit)
+               return -ENOSPC;
+
+       name = kmalloc(len, GFP_KERNEL);
+       if (!name)
+               return -ENOMEM;
+
+       strcpy(name, token);
+
+       *pname = name;
+       return 0;
+}
+
+
+static inline void kill_final_newline(char *str)
+{
+       char *newline = strrchr(str, '\n');
+       if (newline && !newline[1])
+               *newline = 0;
+}
+
+
+#define parse_err(fmt, args...) do {           \
+       ERROR("block2mtd: " fmt "\n", ## args); \
+       return 0;                               \
+} while (0)
+
+static int block2mtd_setup(const char *val, struct kernel_param *kp)
+{
+       char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */
+       char *token[2];
+       char *name;
+       u32 erase_size = PAGE_SIZE;
+       int i, ret;
+
+       if (strnlen(val, sizeof(buf)) >= sizeof(buf))
+               parse_err("parameter too long");
+
+       strcpy(str, val);
+       kill_final_newline(str);
+
+       for (i=0; i<2; i++)
+               token[i] = strsep(&str, ",");
+
+       if (str)
+               parse_err("too many arguments");
+
+       if (!token[0])
+               parse_err("no argument");
+
+       ret = parse_name(&name, token[0], 80);
+       if (ret == -ENOMEM)
+               parse_err("out of memory");
+       if (ret == -ENOSPC)
+               parse_err("name too long");
+       if (ret)
+               return 0;
+
+       if (token[1]) {
+               ret = parse_num32(&erase_size, token[1]);
+               if (ret)
+                       parse_err("illegal erase size");
+       }
+
+       add_device(name, erase_size);
+
+       return 0;
+}
+
+
+module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200);
+MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>]\"");
+
+static int __init block2mtd_init(void)
+{
+       INFO("version " VERSION);
+       return 0;
+}
+
+
+static void __devexit block2mtd_exit(void)
+{
+       struct list_head *pos, *next;
+
+       /* Remove the MTD devices */
+       list_for_each_safe(pos, next, &blkmtd_device_list) {
+               struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
+               block2mtd_sync(&dev->mtd);
+               del_mtd_device(&dev->mtd);
+               INFO("mtd%d: [%s] removed", dev->mtd.index,
+                               dev->mtd.name + strlen("blkmtd: "));
+               list_del(&dev->list);
+               block2mtd_free_device(dev);
+       }
+}
+
+
+module_init(block2mtd_init);
+module_exit(block2mtd_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Simon Evans <spse@secret.org.uk> and others");
+MODULE_DESCRIPTION("Emulate an MTD using a block device");
diff --git a/drivers/mtd/maps/bast-flash.c b/drivers/mtd/maps/bast-flash.c
new file mode 100644 (file)
index 0000000..44de3a8
--- /dev/null
@@ -0,0 +1,227 @@
+/* linux/drivers/mtd/maps/bast_flash.c
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Simtec Bast (EB2410ITX) NOR MTD Mapping driver
+ *
+ * Changelog:
+ *     20-Sep-2004  BJD  Initial version
+ *
+ * $Id: bast-flash.c,v 1.1 2004/09/21 14:29:04 bjd Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/mach/flash.h>
+
+#include <asm/arch/map.h>
+#include <asm/arch/bast-map.h>
+#include <asm/arch/bast-cpld.h>
+
+#ifdef CONFIG_MTD_BAST_MAXSIZE
+#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * (1024*1024))
+#else
+#define AREA_MAXSIZE (32*1024*1024)
+#endif
+
+#define PFX "bast-flash: "
+
+struct bast_flash_info {
+       struct mtd_info         *mtd;
+       struct map_info          map;
+       struct mtd_partition    *partitions;
+       struct resource         *area;
+};
+
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+static struct bast_flash_info *to_bast_info(struct device *dev)
+{
+       return (struct bast_flash_info *)dev_get_drvdata(dev);
+}
+
+static void bast_flash_setrw(int to)
+{
+       unsigned int val;
+       unsigned long flags;
+
+       local_irq_save(flags);
+       val = __raw_readb(BAST_VA_CTRL3);
+       
+       if (to)
+               val |= BAST_CPLD_CTRL3_ROMWEN;
+       else
+               val &= ~BAST_CPLD_CTRL3_ROMWEN;
+
+       pr_debug("new cpld ctrl3=%02x\n", val);
+
+       __raw_writeb(val, BAST_VA_CTRL3);
+       local_irq_restore(flags);
+}
+
+static int bast_flash_remove(struct device *dev)
+{
+       struct bast_flash_info *info = to_bast_info(dev);
+
+       dev_set_drvdata(dev, NULL);
+
+       if (info == NULL) 
+               return 0;
+
+       if (info->map.virt != NULL)
+               iounmap(info->map.virt);
+
+       if (info->mtd) {
+               del_mtd_partitions(info->mtd);
+               map_destroy(info->mtd);
+       }
+
+       if (info->partitions)
+               kfree(info->partitions);
+
+       if (info->area) {
+               release_resource(info->area);
+               kfree(info->area);
+       }
+       
+       kfree(info);
+
+       return 0;
+}
+
+static int bast_flash_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct bast_flash_info *info;
+       struct resource *res;
+       int err = 0;
+
+       info = kmalloc(sizeof(*info), GFP_KERNEL);
+       if (info == NULL) {
+               printk(KERN_ERR PFX "no memory for flash info\n");
+               err = -ENOMEM;
+               goto exit_error;
+       }
+
+       memzero(info, sizeof(*info));
+       dev_set_drvdata(dev, info);
+
+       res = pdev->resource;  /* assume that the flash has one resource */
+
+       info->map.phys = res->start;
+       info->map.size = res->end - res->start + 1;
+       info->map.name = dev->bus_id;   
+       info->map.bankwidth = 2;
+       
+       if (info->map.size > AREA_MAXSIZE)
+               info->map.size = AREA_MAXSIZE;
+
+       pr_debug("%s: area %08lx, size %ld\n", __FUNCTION__,
+                info->map.phys, info->map.size);
+       
+       info->area = request_mem_region(res->start, info->map.size,
+                                       pdev->name);
+       if (info->area == NULL) {
+               printk(KERN_ERR PFX "cannot reserve flash memory region\n");
+               err = -ENOENT;
+               goto exit_error;
+       }
+
+       info->map.virt = ioremap(res->start, info->map.size);
+       pr_debug("%s: virt at %08x\n", __FUNCTION__, (int)info->map.virt);
+
+       if (info->map.virt == 0) {
+               printk(KERN_ERR PFX "failed to ioremap() region\n");
+               err = -EIO;
+               goto exit_error;
+       }
+       simple_map_init(&info->map);
+
+       /* enable the write to the flash area */
+
+       bast_flash_setrw(1);
+
+       /* probe for the device(s) */
+
+       info->mtd = do_map_probe("jedec_probe", &info->map);
+       if (info->mtd == NULL)
+               info->mtd = do_map_probe("cfi_probe", &info->map);
+
+       if (info->mtd == NULL) {
+               printk(KERN_ERR PFX "map_probe() failed\n");
+               err = -ENXIO;
+               goto exit_error;
+       }
+
+       /* mark ourselves as the owner */
+       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 PFX "cannot add/parse partitions\n");
+       }
+
+       if (err == 0)
+               return 0;
+
+       /* fall through to exit error */
+
+ exit_error:
+       bast_flash_remove(dev);
+       return err;
+}
+
+static struct device_driver bast_flash_driver = {
+       .name           = "bast-nor",
+       .bus            = &platform_bus_type,
+       .probe          = bast_flash_probe,
+       .remove         = bast_flash_remove,
+};
+
+static int __init bast_flash_init(void)
+{
+       printk("BAST NOR-Flash Driver, (c) 2004 Simtec Electronics\n");
+       return driver_register(&bast_flash_driver);
+}
+
+static void __exit bast_flash_exit(void)
+{
+       driver_unregister(&bast_flash_driver);
+}
+
+module_init(bast_flash_init);
+module_exit(bast_flash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("BAST MTD Map driver");
diff --git a/drivers/mtd/maps/chestnut.c b/drivers/mtd/maps/chestnut.c
new file mode 100644 (file)
index 0000000..1cb5f15
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * drivers/mtd/maps/chestnut.c
+ *
+ * $Id: chestnut.c,v 1.1 2005/01/05 16:59:50 dwmw2 Exp $
+ *
+ * Flash map driver for IBM Chestnut (750FXGX Eval)
+ *
+ * Chose not to enable 8 bit flash as it contains the firmware and board
+ * info.  Thus only the 32bit flash is supported.
+ *
+ * Author: <source@mvista.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <platforms/chestnut.h>
+
+static struct map_info chestnut32_map = {
+       .name           = "User FS",
+       .size           = CHESTNUT_32BIT_SIZE,
+       .bankwidth      = 4,
+       .phys           = CHESTNUT_32BIT_BASE,
+};
+
+static struct mtd_partition chestnut32_partitions[] = {
+       {
+               .name   = "User FS",
+               .offset = 0,
+               .size   = CHESTNUT_32BIT_SIZE,
+       }
+};
+
+static struct mtd_info *flash32;
+
+int __init init_chestnut(void)
+{
+       /* 32-bit FLASH */
+
+       chestnut32_map.virt = ioremap(chestnut32_map.phys, chestnut32_map.size);
+
+       if (!chestnut32_map.virt) {
+               printk(KERN_NOTICE "Failed to ioremap 32-bit flash\n");
+               return -EIO;
+       }
+
+       simple_map_init(&chestnut32_map);
+
+       flash32 = do_map_probe("cfi_probe", &chestnut32_map);
+       if (flash32) {
+               flash32->owner = THIS_MODULE;
+               add_mtd_partitions(flash32, chestnut32_partitions,
+                                       ARRAY_SIZE(chestnut32_partitions));
+       } else {
+               printk(KERN_NOTICE "map probe failed for 32-bit flash\n");
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static void __exit
+cleanup_chestnut(void)
+{
+       if (flash32) {
+               del_mtd_partitions(flash32);
+               map_destroy(flash32);
+       }
+
+       if (chestnut32_map.virt) {
+               iounmap((void *)chestnut32_map.virt);
+               chestnut32_map.virt = 0;
+       }
+}
+
+module_init(init_chestnut);
+module_exit(cleanup_chestnut);
+
+MODULE_DESCRIPTION("MTD map and partitions for IBM Chestnut (750fxgx Eval)");
+MODULE_AUTHOR("<source@mvista.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/maps/ipaq-flash.c b/drivers/mtd/maps/ipaq-flash.c
new file mode 100644 (file)
index 0000000..914c0f5
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * Flash memory access on iPAQ Handhelds (either SA1100 or PXA250 based)
+ * 
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ * (C) 2002 Hewlett-Packard Company <jamey.hicks@hp.com>
+ * (C) 2003 Christian Pellegrin <chri@ascensit.com>, <chri@infis.univ.ts.it>: concatenation of multiple flashes
+ * 
+ * $Id: ipaq-flash.c,v 1.3 2004/11/04 13:24:15 gleixner Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/page.h>
+#include <asm/mach-types.h>
+#include <asm/system.h>
+#include <asm/errno.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#ifdef CONFIG_MTD_CONCAT
+#include <linux/mtd/concat.h>
+#endif
+
+#include <asm/hardware.h>
+#include <asm/arch-sa1100/h3600.h>
+#include <asm/io.h>
+
+
+#ifndef CONFIG_IPAQ_HANDHELD
+#error This is for iPAQ Handhelds only
+#endif
+#ifdef CONFIG_SA1100_JORNADA56X
+
+static void jornada56x_set_vpp(struct map_info *map, int vpp)
+{
+       if (vpp)
+               GPSR = GPIO_GPIO26;
+       else
+               GPCR = GPIO_GPIO26;
+       GPDR |= GPIO_GPIO26;
+}
+
+#endif
+
+#ifdef CONFIG_SA1100_JORNADA720
+
+static void jornada720_set_vpp(struct map_info *map, int vpp)
+{
+       if (vpp)
+               PPSR |= 0x80;
+       else
+               PPSR &= ~0x80;
+       PPDR |= 0x80;
+}
+
+#endif
+
+#define MAX_IPAQ_CS 2          /* Number of CS we are going to test */
+
+#define IPAQ_MAP_INIT(X) \
+       { \
+               name:           "IPAQ flash " X, \
+       }
+
+
+static struct map_info ipaq_map[MAX_IPAQ_CS] = {
+       IPAQ_MAP_INIT("bank 1"),
+       IPAQ_MAP_INIT("bank 2")
+};
+
+static struct mtd_info *my_sub_mtd[MAX_IPAQ_CS] = {
+       NULL,
+       NULL
+};
+
+/*
+ * Here are partition information for all known IPAQ-based devices.
+ * See include/linux/mtd/partitions.h for definition of the mtd_partition
+ * structure.
+ *
+ * The *_max_flash_size is the maximum possible mapped flash size which
+ * is not necessarily the actual flash size.  It must be no more than
+ * the value specified in the "struct map_desc *_io_desc" mapping
+ * definition for the corresponding machine.
+ *
+ * Please keep these in alphabetical order, and formatted as per existing
+ * entries.  Thanks.
+ */
+
+#ifdef CONFIG_IPAQ_HANDHELD
+static unsigned long h3xxx_max_flash_size = 0x04000000;
+static struct mtd_partition h3xxx_partitions[] = {
+       {
+               name:           "H3XXX boot firmware",
+#ifndef CONFIG_LAB
+               size:           0x00040000,
+#else
+               size:           0x00080000,
+#endif
+               offset:         0,
+#ifndef CONFIG_LAB
+               mask_flags:     MTD_WRITEABLE,  /* force read-only */
+#endif
+       }, 
+       {
+               name:           "H3XXX root jffs2",
+#ifndef CONFIG_LAB
+               size:           0x2000000 - 2*0x40000, /* Warning, this is fixed later */
+               offset:         0x00040000,
+#else
+               size:           0x2000000 - 0x40000 - 0x80000, /* Warning, this is fixed later */
+               offset:         0x00080000,
+#endif
+       },
+       {
+               name:           "asset",
+               size:           0x40000,
+               offset:         0x2000000 - 0x40000, /* Warning, this is fixed later */
+               mask_flags:     MTD_WRITEABLE,  /* force read-only */
+       }
+};
+
+#ifndef CONFIG_MTD_CONCAT
+static struct mtd_partition h3xxx_partitions_bank2[] = {
+       /* this is used only on 2 CS machines when concat is not present */
+       {
+               name:           "second H3XXX root jffs2",
+               size:           0x1000000 - 0x40000, /* Warning, this is fixed later */
+               offset:         0x00000000,
+       },
+       {
+               name:           "second asset",
+               size:           0x40000,
+               offset:         0x1000000 - 0x40000, /* Warning, this is fixed later */
+               mask_flags:     MTD_WRITEABLE,  /* force read-only */
+       }
+};
+#endif
+
+static spinlock_t ipaq_vpp_lock = SPIN_LOCK_UNLOCKED;
+
+static void h3xxx_set_vpp(struct map_info *map, int vpp)
+{
+       static int nest = 0;
+       
+       spin_lock(&ipaq_vpp_lock);
+       if (vpp)
+               nest++;
+       else
+               nest--;
+       if (nest)
+               assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 1);
+       else
+               assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 0);
+       spin_unlock(&ipaq_vpp_lock);
+}
+
+#endif
+
+#if defined(CONFIG_SA1100_JORNADA56X) || defined(CONFIG_SA1100_JORNADA720)
+static unsigned long jornada_max_flash_size = 0x02000000;
+static struct mtd_partition jornada_partitions[] = {
+       {
+               name:           "Jornada boot firmware",
+               size:           0x00040000,
+               offset:         0,
+               mask_flags:     MTD_WRITEABLE,  /* force read-only */
+       }, {
+               name:           "Jornada root jffs2",
+               size:           MTDPART_SIZ_FULL,
+               offset:         0x00040000,
+       }
+};
+#endif
+
+
+static struct mtd_partition *parsed_parts;
+static struct mtd_info *mymtd;
+
+static unsigned long cs_phys[] = {
+#ifdef CONFIG_ARCH_SA1100
+       SA1100_CS0_PHYS,
+       SA1100_CS1_PHYS,
+       SA1100_CS2_PHYS,
+       SA1100_CS3_PHYS,
+       SA1100_CS4_PHYS,
+       SA1100_CS5_PHYS,
+#else 
+       PXA_CS0_PHYS,
+       PXA_CS1_PHYS,
+       PXA_CS2_PHYS,
+       PXA_CS3_PHYS,
+       PXA_CS4_PHYS,
+       PXA_CS5_PHYS,
+#endif
+};
+
+static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
+
+static int __init h1900_special_case(void);
+
+int __init ipaq_mtd_init(void)
+{
+       struct mtd_partition *parts = NULL;
+       int nb_parts = 0;
+       int parsed_nr_parts = 0;
+       const char *part_type;
+       int i; /* used when we have >1 flash chips */
+       unsigned long tot_flashsize = 0; /* used when we have >1 flash chips */
+
+       /* Default flash bankwidth */
+       // ipaq_map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
+       
+       if (machine_is_h1900())
+       {
+               /* For our intents, the h1900 is not a real iPAQ, so we special-case it. */
+               return h1900_special_case();
+       }
+
+       if (machine_is_h3100() || machine_is_h1900())
+               for(i=0; i<MAX_IPAQ_CS; i++)
+                       ipaq_map[i].bankwidth = 2;
+       else
+               for(i=0; i<MAX_IPAQ_CS; i++)
+                       ipaq_map[i].bankwidth = 4;
+                       
+       /*
+        * Static partition definition selection
+        */
+       part_type = "static";
+
+       simple_map_init(&ipaq_map[0]);
+       simple_map_init(&ipaq_map[1]);
+
+#ifdef CONFIG_IPAQ_HANDHELD
+       if (machine_is_ipaq()) {
+               parts = h3xxx_partitions;
+               nb_parts = ARRAY_SIZE(h3xxx_partitions);
+               for(i=0; i<MAX_IPAQ_CS; i++) {
+                       ipaq_map[i].size = h3xxx_max_flash_size;
+                       ipaq_map[i].set_vpp = h3xxx_set_vpp;
+                       ipaq_map[i].phys = cs_phys[i];
+                       ipaq_map[i].virt = __ioremap(cs_phys[i], 0x04000000, 0, 1);
+                       if (machine_is_h3100 () || machine_is_h1900())
+                               ipaq_map[i].bankwidth = 2;
+               }
+               if (machine_is_h3600()) {
+                       /* No asset partition here */
+                       h3xxx_partitions[1].size += 0x40000;
+                       nb_parts--;
+               }
+       }
+#endif
+#ifdef CONFIG_ARCH_H5400
+       if (machine_is_h5400()) {
+               ipaq_map[0].size = 0x02000000;
+               ipaq_map[1].size = 0x02000000;
+               ipaq_map[1].phys = 0x02000000;
+               ipaq_map[1].virt = ipaq_map[0].virt + 0x02000000;
+       }
+#endif
+#ifdef CONFIG_ARCH_H1900
+       if (machine_is_h1900()) {
+               ipaq_map[0].size = 0x00400000;
+               ipaq_map[1].size = 0x02000000;
+               ipaq_map[1].phys = 0x00080000;
+               ipaq_map[1].virt = ipaq_map[0].virt + 0x00080000;
+       }
+#endif
+
+#ifdef CONFIG_SA1100_JORNADA56X
+       if (machine_is_jornada56x()) {
+               parts = jornada_partitions;
+               nb_parts = ARRAY_SIZE(jornada_partitions);
+               ipaq_map[0].size = jornada_max_flash_size;
+               ipaq_map[0].set_vpp = jornada56x_set_vpp;
+               ipaq_map[0].virt = (__u32)__ioremap(0x0, 0x04000000, 0, 1);
+       }
+#endif
+#ifdef CONFIG_SA1100_JORNADA720
+       if (machine_is_jornada720()) {
+               parts = jornada_partitions;
+               nb_parts = ARRAY_SIZE(jornada_partitions);
+               ipaq_map[0].size = jornada_max_flash_size;
+               ipaq_map[0].set_vpp = jornada720_set_vpp;
+       }
+#endif
+
+
+       if (machine_is_ipaq()) { /* for iPAQs only */
+               for(i=0; i<MAX_IPAQ_CS; i++) {
+                       printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with CFI.\n", ipaq_map[i].bankwidth*8, ipaq_map[i].virt);
+                       my_sub_mtd[i] = do_map_probe("cfi_probe", &ipaq_map[i]);
+                       if (!my_sub_mtd[i]) {
+                               printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with JEDEC.\n", ipaq_map[i].bankwidth*8, ipaq_map[i].virt);
+                               my_sub_mtd[i] = do_map_probe("jedec_probe", &ipaq_map[i]);
+                       }
+                       if (!my_sub_mtd[i]) {
+                               printk(KERN_NOTICE "iPAQ flash: failed to find flash.\n");
+                               if (i)
+                                       break;
+                               else
+                                       return -ENXIO;
+                       } else
+                               printk(KERN_NOTICE "iPAQ flash: found %d bytes\n", my_sub_mtd[i]->size);
+                       
+                       /* do we really need this debugging? --joshua 20030703 */
+                       // printk("my_sub_mtd[%d]=%p\n", i, my_sub_mtd[i]);
+                       my_sub_mtd[i]->owner = THIS_MODULE;
+                       tot_flashsize += my_sub_mtd[i]->size;
+               }
+#ifdef CONFIG_MTD_CONCAT
+               /* fix the asset location */
+#      ifdef CONFIG_LAB
+               h3xxx_partitions[1].size = tot_flashsize - 0x40000 - 0x80000 /* extra big boot block */;
+#      else
+               h3xxx_partitions[1].size = tot_flashsize - 2 * 0x40000;
+#      endif
+               h3xxx_partitions[2].offset = tot_flashsize - 0x40000;
+               /* and concat the devices */
+               mymtd = mtd_concat_create(&my_sub_mtd[0], i,
+                                         "ipaq");
+               if (!mymtd) {
+                       printk("Cannot create iPAQ concat device\n");
+                       return -ENXIO;
+               }
+#else
+               mymtd = my_sub_mtd[0];
+
+               /* 
+                *In the very near future, command line partition parsing
+                * will use the device name as 'mtd-id' instead of a value
+                * passed to the parse_cmdline_partitions() routine. Since
+                * the bootldr says 'ipaq', make sure it continues to work. 
+                */
+               mymtd->name = "ipaq";
+
+               if ((machine_is_h3600())) {
+#      ifdef CONFIG_LAB
+                       h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x80000;
+#      else
+                       h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000;
+#      endif
+                       nb_parts = 2;
+               } else {
+#      ifdef CONFIG_LAB
+                       h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000 - 0x80000; /* extra big boot block */
+#      else
+                       h3xxx_partitions[1].size = my_sub_mtd[0]->size - 2*0x40000;
+#      endif
+                       h3xxx_partitions[2].offset = my_sub_mtd[0]->size - 0x40000;
+               }
+
+               if (my_sub_mtd[1]) {
+#      ifdef CONFIG_LAB
+                       h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x80000;
+#      else
+                       h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x40000;
+#      endif
+                       h3xxx_partitions_bank2[1].offset = my_sub_mtd[1]->size - 0x40000;
+               }
+#endif
+       }
+       else {
+               /*
+                * Now let's probe for the actual flash.  Do it here since
+                * specific machine settings might have been set above.
+                */
+               printk(KERN_NOTICE "IPAQ flash: probing %d-bit flash bus, window=%lx\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt);
+               mymtd = do_map_probe("cfi_probe", &ipaq_map[0]);
+               if (!mymtd)
+                       return -ENXIO;
+               mymtd->owner = THIS_MODULE;
+       }
+
+
+       /*
+        * Dynamic partition selection stuff (might override the static ones)
+        */
+
+        i = parse_mtd_partitions(mymtd, part_probes, &parsed_parts, 0);
+                       
+        if (i > 0) {
+                nb_parts = parsed_nr_parts = i;
+                parts = parsed_parts;
+                part_type = "dynamic";
+        }
+
+        if (!parts) {
+                printk(KERN_NOTICE "IPAQ flash: no partition info available, registering whole flash at once\n");
+                add_mtd_device(mymtd);
+#ifndef CONFIG_MTD_CONCAT
+                if (my_sub_mtd[1])
+                        add_mtd_device(my_sub_mtd[1]);
+#endif
+        } else {
+                printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+                add_mtd_partitions(mymtd, parts, nb_parts);
+#ifndef CONFIG_MTD_CONCAT
+                if (my_sub_mtd[1])
+                        add_mtd_partitions(my_sub_mtd[1], h3xxx_partitions_bank2, ARRAY_SIZE(h3xxx_partitions_bank2));
+#endif
+        }
+
+        return 0;
+}
+
+static void __exit ipaq_mtd_cleanup(void)
+{
+       int i;
+
+       if (mymtd) {
+               del_mtd_partitions(mymtd);
+#ifndef CONFIG_MTD_CONCAT
+               if (my_sub_mtd[1])
+                       del_mtd_partitions(my_sub_mtd[1]);
+#endif
+               map_destroy(mymtd);
+#ifdef CONFIG_MTD_CONCAT
+               for(i=0; i<MAX_IPAQ_CS; i++) 
+#else
+                       for(i=1; i<MAX_IPAQ_CS; i++) 
+#endif           
+                       {
+                               if (my_sub_mtd[i])
+                                       map_destroy(my_sub_mtd[i]);
+                       }
+               if (parsed_parts)
+                       kfree(parsed_parts);
+       }
+}
+
+static int __init h1900_special_case(void)
+{
+       /* The iPAQ h1900 is a special case - it has weird ROM. */
+       simple_map_init(&ipaq_map[0]);
+       ipaq_map[0].size = 0x80000;
+       ipaq_map[0].set_vpp = h3xxx_set_vpp;
+       ipaq_map[0].phys = 0x0;
+       ipaq_map[0].virt = __ioremap(0x0, 0x04000000, 0, 1);
+       ipaq_map[0].bankwidth = 2;
+       
+       printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with JEDEC.\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt);
+       mymtd = do_map_probe("jedec_probe", &ipaq_map[0]);
+       if (!mymtd)
+               return -ENODEV;
+       add_mtd_device(mymtd);
+       printk(KERN_NOTICE "iPAQ flash: registered h1910 flash\n");
+       
+       return 0;
+}
+
+module_init(ipaq_mtd_init);
+module_exit(ipaq_mtd_cleanup);
+
+MODULE_AUTHOR("Jamey Hicks");
+MODULE_DESCRIPTION("IPAQ CFI map driver");
+MODULE_LICENSE("MIT");
diff --git a/drivers/mtd/maps/ocotea.c b/drivers/mtd/maps/ocotea.c
new file mode 100644 (file)
index 0000000..6e559bc
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Mapping for Ocotea user flash
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Copyright 2002-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.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#include <asm/ibm44x.h>
+#include <platforms/4xx/ocotea.h>
+
+static struct mtd_info *flash;
+
+static struct map_info ocotea_small_map = {
+       .name =         "Ocotea small flash",
+       .size =         OCOTEA_SMALL_FLASH_SIZE,
+       .buswidth =     1,
+};
+
+static struct map_info ocotea_large_map = {
+       .name =         "Ocotea large flash",
+       .size =         OCOTEA_LARGE_FLASH_SIZE,
+       .buswidth =     1,
+};
+
+static struct mtd_partition ocotea_small_partitions[] = {
+       {
+               .name =   "pibs",
+               .offset = 0x0,
+               .size =   0x100000,
+       }
+};
+
+static struct mtd_partition ocotea_large_partitions[] = {
+       {
+               .name =   "fs",
+               .offset = 0,
+               .size =   0x300000,
+       },
+       {
+               .name =   "firmware",
+               .offset = 0x300000,
+               .size =   0x100000,
+       }
+};
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+int __init init_ocotea(void)
+{
+       u8 fpga0_reg;
+       u8 *fpga0_adr;
+       unsigned long long small_flash_base, large_flash_base;
+
+       fpga0_adr = ioremap64(OCOTEA_FPGA_ADDR, 16);
+       if (!fpga0_adr)
+               return -ENOMEM;
+
+       fpga0_reg = readb((unsigned long)fpga0_adr);
+       iounmap(fpga0_adr);
+
+       if (OCOTEA_BOOT_LARGE_FLASH(fpga0_reg)) {
+               small_flash_base = OCOTEA_SMALL_FLASH_HIGH;
+               large_flash_base = OCOTEA_LARGE_FLASH_LOW;
+       }
+       else {
+               small_flash_base = OCOTEA_SMALL_FLASH_LOW;
+               large_flash_base = OCOTEA_LARGE_FLASH_HIGH;
+       }
+
+       ocotea_small_map.phys = small_flash_base;
+       ocotea_small_map.virt = ioremap64(small_flash_base,
+                                        ocotea_small_map.size);
+
+       if (!ocotea_small_map.virt) {
+               printk("Failed to ioremap flash\n");
+               return -EIO;
+       }
+
+       simple_map_init(&ocotea_small_map);
+
+       flash = do_map_probe("map_rom", &ocotea_small_map);
+       if (flash) {
+               flash->owner = THIS_MODULE;
+               add_mtd_partitions(flash, ocotea_small_partitions,
+                                       NB_OF(ocotea_small_partitions));
+       } else {
+               printk("map probe failed for flash\n");
+               return -ENXIO;
+       }
+
+       ocotea_large_map.phys = large_flash_base;
+       ocotea_large_map.virt = ioremap64(large_flash_base,
+                                        ocotea_large_map.size);
+
+       if (!ocotea_large_map.virt) {
+               printk("Failed to ioremap flash\n");
+               return -EIO;
+       }
+
+       simple_map_init(&ocotea_large_map);
+
+       flash = do_map_probe("cfi_probe", &ocotea_large_map);
+       if (flash) {
+               flash->owner = THIS_MODULE;
+               add_mtd_partitions(flash, ocotea_large_partitions,
+                                       NB_OF(ocotea_large_partitions));
+       } else {
+               printk("map probe failed for flash\n");
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static void __exit cleanup_ocotea(void)
+{
+       if (flash) {
+               del_mtd_partitions(flash);
+               map_destroy(flash);
+       }
+
+       if (ocotea_small_map.virt) {
+               iounmap((void *)ocotea_small_map.virt);
+               ocotea_small_map.virt = 0;
+       }
+
+       if (ocotea_large_map.virt) {
+               iounmap((void *)ocotea_large_map.virt);
+               ocotea_large_map.virt = 0;
+       }
+}
+
+module_init(init_ocotea);
+module_exit(cleanup_ocotea);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matt Porter <mporter@kernel.crashing.org>");
+MODULE_DESCRIPTION("MTD map and partitions for IBM 440GX Ocotea boards");
diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c
new file mode 100644 (file)
index 0000000..b3b39cb
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * sharpsl-flash.c
+ * 
+ * Copyright (C) 2001 Lineo Japan, Inc.
+ * Copyright (C) 2002  SHARP
+ *
+ * $Id: sharpsl-flash.c,v 1.2 2004/11/24 20:38:06 rpurdie Exp $
+ *
+ * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp
+ *          Handle mapping of the flash on the RPX Lite and CLLF boards
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#define WINDOW_ADDR 0x00000000
+#define WINDOW_SIZE 0x01000000
+#define BANK_WIDTH 2
+
+static struct mtd_info *mymtd;
+
+struct map_info sharpsl_map = {
+       .name = "sharpsl-flash",
+       .size = WINDOW_SIZE,
+       .bankwidth = BANK_WIDTH,
+       .phys = WINDOW_ADDR
+};
+
+static struct mtd_partition sharpsl_partitions[1] = {
+       {
+               name:           "Filesystem",
+               size:           0x006d0000,
+               offset:         0x00120000
+       }
+};
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+int __init init_sharpsl(void)
+{
+       struct mtd_partition *parts;
+       int nb_parts = 0;
+       char *part_type = "static";
+
+       printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
+       sharpsl_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
+       if (!sharpsl_map.virt) {
+               printk("Failed to ioremap\n");
+               return -EIO;
+       }
+       mymtd = do_map_probe("map_rom", &sharpsl_map);
+       if (!mymtd) {
+               iounmap(sharpsl_map.virt);
+               return -ENXIO;
+       }
+
+       mymtd->owner = THIS_MODULE;
+
+       parts = sharpsl_partitions;
+       nb_parts = NB_OF(sharpsl_partitions);
+
+       printk(KERN_NOTICE "Using %s partision definition\n", part_type);
+       add_mtd_partitions(mymtd, parts, nb_parts);
+
+       return 0;
+}
+
+static void __exit cleanup_sharpsl(void)
+{
+       if (mymtd) {
+               del_mtd_partitions(mymtd);
+               map_destroy(mymtd);
+       }
+       if (sharpsl_map.virt) {
+               iounmap(sharpsl_map.virt);
+               sharpsl_map.virt = 0;
+       }
+}
+
+module_init(init_sharpsl);
+module_exit(cleanup_sharpsl);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("SHARP (Original: Arnold Christensen <AKC@pel.dk>)");
+MODULE_DESCRIPTION("MTD map driver for SHARP SL series");
diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c
new file mode 100644 (file)
index 0000000..f90998b
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * ts5500_flash.c -- MTD map driver for Technology Systems TS-5500 board
+ *
+ * Copyright (C) 2004 Sean Young <sean@mess.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.
+ *
+ * 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
+ *
+ * Note:
+ * - In order for detection to work, jumper 3 must be set.
+ * - Drive A and B use a proprietary FTL from General Software which isn't 
+ *   supported as of yet so standard drives can't be mounted; you can create 
+ *   your own (e.g. jffs) file system.
+ * - If you have created your own jffs file system and the bios overwrites 
+ *   it during boot, try disabling Drive A: and B: in the boot order.
+ *
+ * $Id: ts5500_flash.c,v 1.1 2004/09/20 15:33:26 sean Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
+#define WINDOW_ADDR    0x09400000
+#define WINDOW_SIZE    0x00200000
+
+static struct map_info ts5500_map = {
+       .name = "TS-5500 Flash",
+       .size = WINDOW_SIZE,
+       .bankwidth = 1,
+       .phys = WINDOW_ADDR
+};
+
+#ifdef CONFIG_MTD_PARTITIONS
+static struct mtd_partition ts5500_partitions[] = {
+       {
+               .name = "Drive A",
+               .offset = 0,
+               .size = 0x0e0000
+       },
+       {
+               .name = "BIOS",
+               .offset = 0x0e0000,
+               .size = 0x020000,
+       },
+       {
+               .name = "Drive B",
+               .offset = 0x100000,
+               .size = 0x100000
+       }
+};
+
+#define NUM_PARTITIONS (sizeof(ts5500_partitions)/sizeof(struct mtd_partition))
+
+#endif
+
+static struct mtd_info *mymtd;
+
+static int __init init_ts5500_map(void)
+{
+       int rc = 0;
+
+       ts5500_map.virt = ioremap_nocache(ts5500_map.phys, ts5500_map.size);
+
+       if(!ts5500_map.virt) {
+               printk(KERN_ERR "Failed to ioremap_nocache\n");
+               rc = -EIO;
+               goto err_out_ioremap;
+       }
+
+       simple_map_init(&ts5500_map);
+
+       mymtd = do_map_probe("jedec_probe", &ts5500_map);
+       if(!mymtd)
+               mymtd = do_map_probe("map_rom", &ts5500_map);
+
+       if(!mymtd) {
+               rc = -ENXIO;
+               goto err_out_map;
+       }
+
+       mymtd->owner = THIS_MODULE;
+#ifdef CONFIG_MTD_PARTITIONS
+       add_mtd_partitions(mymtd, ts5500_partitions, NUM_PARTITIONS);
+#else  
+       add_mtd_device(mymtd);
+#endif
+
+       return 0;
+
+err_out_map:
+       map_destroy(mymtd);
+err_out_ioremap:
+       iounmap(ts5500_map.virt);
+
+       return rc;
+}
+
+static void __exit cleanup_ts5500_map(void)
+{
+       if (mymtd) {
+#ifdef CONFIG_MTD_PARTITIONS
+               del_mtd_partitions(mymtd);
+#else
+               del_mtd_device(mymtd);
+#endif
+               map_destroy(mymtd);
+       }
+
+       if (ts5500_map.virt) {
+               iounmap(ts5500_map.virt);
+               ts5500_map.virt = NULL;
+       }
+}
+
+module_init(init_ts5500_map);
+module_exit(cleanup_ts5500_map);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_DESCRIPTION("MTD map driver for Techology Systems TS-5500 board");
+
diff --git a/drivers/mtd/maps/walnut.c b/drivers/mtd/maps/walnut.c
new file mode 100644 (file)
index 0000000..d6137b1
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * $Id: walnut.c,v 1.2 2004/12/10 12:07:42 holindho Exp $
+ * 
+ * Mapping for Walnut flash
+ * (used ebony.c as a "framework")
+ * 
+ * Heikki Lindholm <holindho@infradead.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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <asm/io.h>
+#include <asm/ibm4xx.h>
+#include <platforms/4xx/walnut.h>
+
+/* these should be in platforms/4xx/walnut.h ? */
+#define WALNUT_FLASH_ONBD_N(x)         (x & 0x02)
+#define WALNUT_FLASH_SRAM_SEL(x)       (x & 0x01)
+#define WALNUT_FLASH_LOW               0xFFF00000
+#define WALNUT_FLASH_HIGH              0xFFF80000
+#define WALNUT_FLASH_SIZE              0x80000
+
+static struct mtd_info *flash;
+
+static struct map_info walnut_map = {
+       .name =         "Walnut flash",
+       .size =         WALNUT_FLASH_SIZE,
+       .bankwidth =    1,
+};
+
+/* Actually, OpenBIOS is the last 128 KiB of the flash - better
+ * partitioning could be made */
+static struct mtd_partition walnut_partitions[] = {
+       {
+               .name =   "OpenBIOS",
+               .offset = 0x0,
+               .size =   WALNUT_FLASH_SIZE,
+               /*.mask_flags = MTD_WRITEABLE, */ /* force read-only */         
+       }
+};
+
+int __init init_walnut(void)
+{
+       u8 fpga_brds1;
+       void *fpga_brds1_adr;
+       void *fpga_status_adr;
+       unsigned long flash_base;
+
+       /* this should already be mapped (platform/4xx/walnut.c) */
+       fpga_status_adr = ioremap(WALNUT_FPGA_BASE, 8);
+       if (!fpga_status_adr)
+               return -ENOMEM;
+
+       fpga_brds1_adr = fpga_status_adr+5;
+       fpga_brds1 = readb(fpga_brds1_adr);
+       /* iounmap(fpga_status_adr); */
+
+       if (WALNUT_FLASH_ONBD_N(fpga_brds1)) {
+               printk("The on-board flash is disabled (U79 sw 5)!");
+               return -EIO;
+       }
+       if (WALNUT_FLASH_SRAM_SEL(fpga_brds1)) 
+               flash_base = WALNUT_FLASH_LOW;
+       else
+               flash_base = WALNUT_FLASH_HIGH;
+       
+       walnut_map.phys = flash_base;
+       walnut_map.virt =
+               (void __iomem *)ioremap(flash_base, walnut_map.size);
+
+       if (!walnut_map.virt) {
+               printk("Failed to ioremap flash.\n");
+               return -EIO;
+       }
+
+       simple_map_init(&walnut_map);
+
+       flash = do_map_probe("jedec_probe", &walnut_map);
+       if (flash) {
+               flash->owner = THIS_MODULE;
+               add_mtd_partitions(flash, walnut_partitions,
+                                       ARRAY_SIZE(walnut_partitions));
+       } else {
+               printk("map probe failed for flash\n");
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static void __exit cleanup_walnut(void)
+{
+       if (flash) {
+               del_mtd_partitions(flash);
+               map_destroy(flash);
+       }
+
+       if (walnut_map.virt) {
+               iounmap((void *)walnut_map.virt);
+               walnut_map.virt = 0;
+       }
+}
+
+module_init(init_walnut);
+module_exit(cleanup_walnut);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Heikki Lindholm <holindho@infradead.org>");
+MODULE_DESCRIPTION("MTD map and partitions for IBM 405GP Walnut boards");
diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c
new file mode 100644 (file)
index 0000000..3825a7a
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ *  drivers/mtd/nand/h1910.c
+ *
+ *  Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com)
+ *
+ *  Derived from drivers/mtd/nand/edb7312.c
+ *       Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
+ *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * $Id: h1910.c,v 1.5 2004/11/04 12:53:10 gleixner Exp $
+ *
+ * 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.
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash device found on the
+ *   iPAQ h1910 board which utilizes the Samsung K9F2808 part. This is
+ *   a 128Mibit (16MiB x 8 bits) NAND flash device.
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
+#include <asm/sizes.h>
+#include <asm/arch/h1900-gpio.h>
+#include <asm/arch/ipaq.h>
+
+/*
+ * MTD structure for EDB7312 board
+ */
+static struct mtd_info *h1910_nand_mtd = NULL;
+
+/*
+ * Module stuff
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+/*
+ * Define static partitions for flash device
+ */
+static struct mtd_partition partition_info[] = {
+       { name: "h1910 NAND Flash",
+                 offset: 0,
+                 size: 16*1024*1024 }
+};
+#define NUM_PARTITIONS 1
+
+#endif
+
+
+/* 
+ *     hardware specific access to control-lines
+ */
+static void h1910_hwcontrol(struct mtd_info *mtd, int cmd) 
+{
+       struct nand_chip* this = (struct nand_chip *) (mtd->priv);
+       
+       switch(cmd) {
+               
+       case NAND_CTL_SETCLE: 
+               this->IO_ADDR_R |= (1 << 2);
+               this->IO_ADDR_W |= (1 << 2);
+               break;
+       case NAND_CTL_CLRCLE: 
+               this->IO_ADDR_R &= ~(1 << 2);
+               this->IO_ADDR_W &= ~(1 << 2);
+               break;
+               
+       case NAND_CTL_SETALE:
+               this->IO_ADDR_R |= (1 << 3);
+               this->IO_ADDR_W |= (1 << 3);
+               break;
+       case NAND_CTL_CLRALE:
+               this->IO_ADDR_R &= ~(1 << 3);
+               this->IO_ADDR_W &= ~(1 << 3);
+               break;
+               
+       case NAND_CTL_SETNCE:
+               break;
+       case NAND_CTL_CLRNCE:
+               break;
+       }
+}
+
+/*
+ *     read device ready pin
+ */
+#if 0
+static int h1910_device_ready(struct mtd_info *mtd)
+{
+       return (GPLR(55) & GPIO_bit(55));
+}
+#endif
+
+/*
+ * Main initialization routine
+ */
+static int __init h1910_init (void)
+{
+       struct nand_chip *this;
+       const char *part_type = 0;
+       int mtd_parts_nb = 0;
+       struct mtd_partition *mtd_parts = 0;
+       void __iomem *nandaddr;
+       
+       if (!machine_is_h1900())
+               return -ENODEV;
+               
+       nandaddr = __ioremap(0x08000000, 0x1000, 0, 1);
+       if (!nandaddr) {
+               printk("Failed to ioremap nand flash.\n");
+               return -ENOMEM;
+       }
+       
+       /* Allocate memory for MTD device structure and private data */
+       h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) + 
+                            sizeof(struct nand_chip),
+                            GFP_KERNEL);
+       if (!h1910_nand_mtd) {
+               printk("Unable to allocate h1910 NAND MTD device structure.\n");
+               iounmap ((void *) nandaddr);
+               return -ENOMEM;
+       }
+       
+       /* Get pointer to private data */
+       this = (struct nand_chip *) (&h1910_nand_mtd[1]);
+       
+       /* Initialize structures */
+       memset((char *) h1910_nand_mtd, 0, sizeof(struct mtd_info));
+       memset((char *) this, 0, sizeof(struct nand_chip));
+       
+       /* Link the private data with the MTD structure */
+       h1910_nand_mtd->priv = this;
+       
+       /*
+        * Enable VPEN
+        */
+       GPSR(37) = GPIO_bit(37);
+       
+       /* insert callbacks */
+       this->IO_ADDR_R = nandaddr;
+       this->IO_ADDR_W = nandaddr;
+       this->hwcontrol = h1910_hwcontrol;
+       this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */
+       /* 15 us command delay time */
+       this->chip_delay = 50;
+       this->eccmode = NAND_ECC_SOFT;
+       this->options = NAND_NO_AUTOINCR;
+       
+       /* Scan to find existence of the device */
+       if (nand_scan (h1910_nand_mtd, 1)) {
+               printk(KERN_NOTICE "No NAND device - returning -ENXIO\n");
+               kfree (h1910_nand_mtd);
+               iounmap ((void *) nandaddr);
+               return -ENXIO;
+       }
+       
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+       mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts, 
+                                               "h1910-nand");
+       if (mtd_parts_nb > 0)
+         part_type = "command line";
+       else
+         mtd_parts_nb = 0;
+#endif
+       if (mtd_parts_nb == 0)
+       {
+               mtd_parts = partition_info;
+               mtd_parts_nb = NUM_PARTITIONS;
+               part_type = "static";
+       }
+       
+       /* Register the partitions */
+       printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+       add_mtd_partitions(h1910_nand_mtd, mtd_parts, mtd_parts_nb);
+       
+       /* Return happy */
+       return 0;
+}
+module_init(h1910_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit h1910_cleanup (void)
+{
+       struct nand_chip *this = (struct nand_chip *) &h1910_nand_mtd[1];
+       
+       /* Release resources, unregister device */
+       nand_release (h1910_nand_mtd);
+
+       /* Release io resource */
+       iounmap ((void *) this->IO_ADDR_W);
+
+       /* Free the MTD device structure */
+       kfree (h1910_nand_mtd);
+}
+module_exit(h1910_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joshua Wise <joshua at joshuawise dot com>");
+MODULE_DESCRIPTION("NAND flash driver for iPAQ h1910");
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
new file mode 100644 (file)
index 0000000..13feefd
--- /dev/null
@@ -0,0 +1,1613 @@
+/*
+ * NAND flash simulator.
+ *
+ * Author: Artem B. Bityuckiy <dedekind@oktetlabs.ru>, <dedekind@infradead.org>
+ *
+ * Copyright (C) 2004 Nokia Corporation 
+ *
+ * Note: NS means "NAND Simulator".
+ * Note: Input means input TO flash chip, output means output FROM chip.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: nandsim.c,v 1.7 2004/12/06 11:53:06 dedekind Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/delay.h>
+#ifdef CONFIG_NS_ABS_POS
+#include <asm/io.h>
+#endif
+
+
+/* Default simulator parameters values */
+#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE)  || \
+    !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \
+    !defined(CONFIG_NANDSIM_THIRD_ID_BYTE)  || \
+    !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE)
+#define CONFIG_NANDSIM_FIRST_ID_BYTE  0x98
+#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39
+#define CONFIG_NANDSIM_THIRD_ID_BYTE  0xFF /* No byte */
+#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */
+#endif
+
+#ifndef CONFIG_NANDSIM_ACCESS_DELAY
+#define CONFIG_NANDSIM_ACCESS_DELAY 25
+#endif
+#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY
+#define CONFIG_NANDSIM_PROGRAMM_DELAY 200
+#endif
+#ifndef CONFIG_NANDSIM_ERASE_DELAY
+#define CONFIG_NANDSIM_ERASE_DELAY 2
+#endif
+#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE
+#define CONFIG_NANDSIM_OUTPUT_CYCLE 40
+#endif
+#ifndef CONFIG_NANDSIM_INPUT_CYCLE
+#define CONFIG_NANDSIM_INPUT_CYCLE  50
+#endif
+#ifndef CONFIG_NANDSIM_BUS_WIDTH
+#define CONFIG_NANDSIM_BUS_WIDTH  8
+#endif
+#ifndef CONFIG_NANDSIM_DO_DELAYS
+#define CONFIG_NANDSIM_DO_DELAYS  0
+#endif
+#ifndef CONFIG_NANDSIM_LOG
+#define CONFIG_NANDSIM_LOG        0
+#endif
+#ifndef CONFIG_NANDSIM_DBG
+#define CONFIG_NANDSIM_DBG        0
+#endif
+
+static uint first_id_byte  = CONFIG_NANDSIM_FIRST_ID_BYTE;
+static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE;
+static uint third_id_byte  = CONFIG_NANDSIM_THIRD_ID_BYTE;
+static uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE;
+static uint access_delay   = CONFIG_NANDSIM_ACCESS_DELAY;
+static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY;
+static uint erase_delay    = CONFIG_NANDSIM_ERASE_DELAY;
+static uint output_cycle   = CONFIG_NANDSIM_OUTPUT_CYCLE;
+static uint input_cycle    = CONFIG_NANDSIM_INPUT_CYCLE;
+static uint bus_width      = CONFIG_NANDSIM_BUS_WIDTH;
+static uint do_delays      = CONFIG_NANDSIM_DO_DELAYS;
+static uint log            = CONFIG_NANDSIM_LOG;
+static uint dbg            = CONFIG_NANDSIM_DBG;
+
+module_param(first_id_byte,  uint, 0400);
+module_param(second_id_byte, uint, 0400);
+module_param(third_id_byte,  uint, 0400);
+module_param(fourth_id_byte, uint, 0400);
+module_param(access_delay,   uint, 0400);
+module_param(programm_delay, uint, 0400);
+module_param(erase_delay,    uint, 0400);
+module_param(output_cycle,   uint, 0400);
+module_param(input_cycle,    uint, 0400);
+module_param(bus_width,      uint, 0400);
+module_param(do_delays,      uint, 0400);
+module_param(log,            uint, 0400);
+module_param(dbg,            uint, 0400);
+
+MODULE_PARM_DESC(first_id_byte,  "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)");
+MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
+MODULE_PARM_DESC(third_id_byte,  "The third byte returned by NAND Flash 'read ID' command");
+MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
+MODULE_PARM_DESC(access_delay,   "Initial page access delay (microiseconds)");
+MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");
+MODULE_PARM_DESC(erase_delay,    "Sector erase delay (milliseconds)");
+MODULE_PARM_DESC(output_cycle,   "Word output (from flash) time (nanodeconds)");
+MODULE_PARM_DESC(input_cycle,    "Word input (to flash) time (nanodeconds)");
+MODULE_PARM_DESC(bus_width,      "Chip's bus width (8- or 16-bit)");
+MODULE_PARM_DESC(do_delays,      "Simulate NAND delays using busy-waits if not zero");
+MODULE_PARM_DESC(log,            "Perform logging if not zero");
+MODULE_PARM_DESC(dbg,            "Output debug information if not zero");
+
+/* The largest possible page size */
+#define NS_LARGEST_PAGE_SIZE   2048
+       
+/* The prefix for simulator output */
+#define NS_OUTPUT_PREFIX "[nandsim]"
+
+/* Simulator's output macros (logging, debugging, warning, error) */
+#define NS_LOG(args...) \
+       do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0)
+#define NS_DBG(args...) \
+       do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0)
+#define NS_WARN(args...) \
+       do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0)
+#define NS_ERR(args...) \
+       do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0)
+
+/* Busy-wait delay macros (microseconds, milliseconds) */
+#define NS_UDELAY(us) \
+        do { if (do_delays) udelay(us); } while(0)
+#define NS_MDELAY(us) \
+        do { if (do_delays) mdelay(us); } while(0)
+       
+/* Is the nandsim structure initialized ? */
+#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0)
+
+/* Good operation completion status */
+#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0)))
+
+/* Operation failed completion status */
+#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns)) 
+
+/* Calculate the page offset in flash RAM image by (row, column) address */
+#define NS_RAW_OFFSET(ns) \
+       (((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column)
+       
+/* Calculate the OOB offset in flash RAM image by (row, column) address */
+#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz)
+
+/* After a command is input, the simulator goes to one of the following states */
+#define STATE_CMD_READ0        0x00000001 /* read data from the beginning of page */
+#define STATE_CMD_READ1        0x00000002 /* read data from the second half of page */
+#define STATE_CMD_READSTART      0x00000003 /* read data second command (large page devices) */
+#define STATE_CMD_PAGEPROG     0x00000004 /* start page programm */
+#define STATE_CMD_READOOB      0x00000005 /* read OOB area */
+#define STATE_CMD_ERASE1       0x00000006 /* sector erase first command */
+#define STATE_CMD_STATUS       0x00000007 /* read status */
+#define STATE_CMD_STATUS_M     0x00000008 /* read multi-plane status (isn't implemented) */
+#define STATE_CMD_SEQIN        0x00000009 /* sequential data imput */
+#define STATE_CMD_READID       0x0000000A /* read ID */
+#define STATE_CMD_ERASE2       0x0000000B /* sector erase second command */
+#define STATE_CMD_RESET        0x0000000C /* reset */
+#define STATE_CMD_MASK         0x0000000F /* command states mask */
+
+/* After an addres is input, the simulator goes to one of these states */
+#define STATE_ADDR_PAGE        0x00000010 /* full (row, column) address is accepted */
+#define STATE_ADDR_SEC         0x00000020 /* sector address was accepted */
+#define STATE_ADDR_ZERO        0x00000030 /* one byte zero address was accepted */
+#define STATE_ADDR_MASK        0x00000030 /* address states mask */
+
+/* Durind data input/output the simulator is in these states */
+#define STATE_DATAIN           0x00000100 /* waiting for data input */
+#define STATE_DATAIN_MASK      0x00000100 /* data input states mask */
+
+#define STATE_DATAOUT          0x00001000 /* waiting for page data output */
+#define STATE_DATAOUT_ID       0x00002000 /* waiting for ID bytes output */
+#define STATE_DATAOUT_STATUS   0x00003000 /* waiting for status output */
+#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */
+#define STATE_DATAOUT_MASK     0x00007000 /* data output states mask */
+
+/* Previous operation is done, ready to accept new requests */
+#define STATE_READY            0x00000000
+
+/* This state is used to mark that the next state isn't known yet */
+#define STATE_UNKNOWN          0x10000000
+
+/* Simulator's actions bit masks */
+#define ACTION_CPY       0x00100000 /* copy page/OOB to the internal buffer */
+#define ACTION_PRGPAGE   0x00200000 /* programm the internal buffer to flash */
+#define ACTION_SECERASE  0x00300000 /* erase sector */
+#define ACTION_ZEROOFF   0x00400000 /* don't add any offset to address */
+#define ACTION_HALFOFF   0x00500000 /* add to address half of page */
+#define ACTION_OOBOFF    0x00600000 /* add to address OOB offset */
+#define ACTION_MASK      0x00700000 /* action mask */
+
+#define NS_OPER_NUM      12 /* Number of operations supported by the simulator */
+#define NS_OPER_STATES   6  /* Maximum number of states in operation */
+
+#define OPT_ANY          0xFFFFFFFF /* any chip supports this operation */
+#define OPT_PAGE256      0x00000001 /* 256-byte  page chips */
+#define OPT_PAGE512      0x00000002 /* 512-byte  page chips */
+#define OPT_PAGE2048     0x00000008 /* 2048-byte page chips */
+#define OPT_SMARTMEDIA   0x00000010 /* SmartMedia technology chips */
+#define OPT_AUTOINCR     0x00000020 /* page number auto inctimentation is possible */
+#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
+#define OPT_LARGEPAGE    (OPT_PAGE2048) /* 2048-byte page chips */
+#define OPT_SMALLPAGE    (OPT_PAGE256  | OPT_PAGE512)  /* 256 and 512-byte page chips */
+
+/* Remove action bits ftom state */
+#define NS_STATE(x) ((x) & ~ACTION_MASK)
+       
+/* 
+ * Maximum previous states which need to be saved. Currently saving is
+ * only needed for page programm operation with preceeded read command
+ * (which is only valid for 512-byte pages).
+ */
+#define NS_MAX_PREVSTATES 1
+
+/* 
+ * The structure which describes all the internal simulator data.
+ */
+struct nandsim {
+       struct mtd_partition part;
+
+       uint busw;              /* flash chip bus width (8 or 16) */
+       u_char ids[4];          /* chip's ID bytes */
+       uint32_t options;       /* chip's characteristic bits */
+       uint32_t state;         /* current chip state */
+       uint32_t nxstate;       /* next expected state */
+       
+       uint32_t *op;           /* current operation, NULL operations isn't known yet  */
+       uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */
+       uint16_t npstates;      /* number of previous states saved */
+       uint16_t stateidx;      /* current state index */
+
+       /* The simulated NAND flash image */
+       union flash_media {
+               u_char *byte;
+               uint16_t    *word;
+       } mem;
+
+       /* Internal buffer of page + OOB size bytes */
+       union internal_buffer {
+               u_char *byte;    /* for byte access */
+               uint16_t *word;  /* for 16-bit word access */
+       } buf;
+
+       /* NAND flash "geometry" */
+       struct nandsin_geometry {
+               uint32_t totsz;     /* total flash size, bytes */
+               uint32_t secsz;     /* flash sector (erase block) size, bytes */
+               uint pgsz;          /* NAND flash page size, bytes */
+               uint oobsz;         /* page OOB area size, bytes */
+               uint32_t totszoob;  /* total flash size including OOB, bytes */
+               uint pgszoob;       /* page size including OOB , bytes*/
+               uint secszoob;      /* sector size including OOB, bytes */
+               uint pgnum;         /* total number of pages */
+               uint pgsec;         /* number of pages per sector */
+               uint secshift;      /* bits number in sector size */
+               uint pgshift;       /* bits number in page size */
+               uint oobshift;      /* bits number in OOB size */
+               uint pgaddrbytes;   /* bytes per page address */
+               uint secaddrbytes;  /* bytes per sector address */
+               uint idbytes;       /* the number ID bytes that this chip outputs */
+       } geom;
+
+       /* NAND flash internal registers */
+       struct nandsim_regs {
+               unsigned command; /* the command register */
+               u_char   status;  /* the status register */
+               uint     row;     /* the page number */
+               uint     column;  /* the offset within page */
+               uint     count;   /* internal counter */
+               uint     num;     /* number of bytes which must be processed */
+               uint     off;     /* fixed page offset */
+       } regs;
+
+       /* NAND flash lines state */
+        struct ns_lines_status {
+                int ce;  /* chip Enable */
+                int cle; /* command Latch Enable */
+                int ale; /* address Latch Enable */
+                int wp;  /* write Protect */
+        } lines;
+};
+
+/*
+ * Operations array. To perform any operation the simulator must pass
+ * through the correspondent states chain.
+ */
+static struct nandsim_operations {
+       uint32_t reqopts;  /* options which are required to perform the operation */
+       uint32_t states[NS_OPER_STATES]; /* operation's states */
+} ops[NS_OPER_NUM] = {
+       /* Read page + OOB from the beginning */
+       {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY,
+                       STATE_DATAOUT, STATE_READY}},
+       /* Read page + OOB from the second half */
+       {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY,
+                       STATE_DATAOUT, STATE_READY}},
+       /* Read OOB */
+       {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY,
+                       STATE_DATAOUT, STATE_READY}},
+       /* Programm page starting from the beginning */
+       {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN,
+                       STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+       /* Programm page starting from the beginning */
+       {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE,
+                             STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+       /* Programm page starting from the second half */
+       {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE,
+                             STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+       /* Programm OOB */
+       {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE,
+                             STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}},
+       /* Erase sector */
+       {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}},
+       /* Read status */
+       {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}},
+       /* Read multi-plane status */
+       {OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}},
+       /* Read ID */
+       {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}},
+       /* Large page devices read page */
+       {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY,
+                              STATE_DATAOUT, STATE_READY}}
+};
+
+/* MTD structure for NAND controller */
+static struct mtd_info *nsmtd;
+
+static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
+
+/*
+ * Initialize the nandsim structure.
+ *
+ * RETURNS: 0 if success, -ERRNO if failure.
+ */
+static int
+init_nandsim(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+       struct nandsim   *ns   = (struct nandsim *)(chip->priv);
+       int i;
+
+       if (NS_IS_INITIALIZED(ns)) {
+               NS_ERR("init_nandsim: nandsim is already initialized\n");
+               return -EIO;
+       }
+
+       /* Force mtd to not do delays */
+       chip->chip_delay = 0;
+
+       /* Initialize the NAND flash parameters */
+       ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8;
+       ns->geom.totsz    = mtd->size;
+       ns->geom.pgsz     = mtd->oobblock;
+       ns->geom.oobsz    = mtd->oobsize;
+       ns->geom.secsz    = mtd->erasesize;
+       ns->geom.pgszoob  = ns->geom.pgsz + ns->geom.oobsz;
+       ns->geom.pgnum    = ns->geom.totsz / ns->geom.pgsz;
+       ns->geom.totszoob = ns->geom.totsz + ns->geom.pgnum * ns->geom.oobsz;
+       ns->geom.secshift = ffs(ns->geom.secsz) - 1;
+       ns->geom.pgshift  = chip->page_shift;
+       ns->geom.oobshift = ffs(ns->geom.oobsz) - 1;
+       ns->geom.pgsec    = ns->geom.secsz / ns->geom.pgsz;
+       ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec;
+       ns->options = 0;
+
+       if (ns->geom.pgsz == 256) {
+               ns->options |= OPT_PAGE256;
+       }
+       else if (ns->geom.pgsz == 512) {
+               ns->options |= (OPT_PAGE512 | OPT_AUTOINCR);
+               if (ns->busw == 8)
+                       ns->options |= OPT_PAGE512_8BIT;
+       } else if (ns->geom.pgsz == 2048) {
+               ns->options |= OPT_PAGE2048;
+       } else {
+               NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz);
+               return -EIO;
+       }
+
+       if (ns->options & OPT_SMALLPAGE) {
+               if (ns->geom.totsz < (64 << 20)) {
+                       ns->geom.pgaddrbytes  = 3;
+                       ns->geom.secaddrbytes = 2;
+               } else {
+                       ns->geom.pgaddrbytes  = 4;
+                       ns->geom.secaddrbytes = 3;
+               }
+       } else {
+               if (ns->geom.totsz <= (128 << 20)) {
+                       ns->geom.pgaddrbytes  = 5;
+                       ns->geom.secaddrbytes = 2;
+               } else {
+                       ns->geom.pgaddrbytes  = 5;
+                       ns->geom.secaddrbytes = 3;
+               }
+       }
+       
+       /* Detect how many ID bytes the NAND chip outputs */
+        for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+                if (second_id_byte != nand_flash_ids[i].id)
+                        continue;
+               if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR))
+                       ns->options |= OPT_AUTOINCR;
+       }
+
+       if (ns->busw == 16)
+               NS_WARN("16-bit flashes support wasn't tested\n");
+
+       printk("flash size: %u MiB\n",          ns->geom.totsz >> 20);
+       printk("page size: %u bytes\n",         ns->geom.pgsz);
+       printk("OOB area size: %u bytes\n",     ns->geom.oobsz);
+       printk("sector size: %u KiB\n",         ns->geom.secsz >> 10);
+       printk("pages number: %u\n",            ns->geom.pgnum);
+       printk("pages per sector: %u\n",        ns->geom.pgsec);
+       printk("bus width: %u\n",               ns->busw);
+       printk("bits in sector size: %u\n",     ns->geom.secshift);
+       printk("bits in page size: %u\n",       ns->geom.pgshift);
+       printk("bits in OOB size: %u\n",        ns->geom.oobshift);
+       printk("flash size with OOB: %u KiB\n", ns->geom.totszoob >> 10);
+       printk("page address bytes: %u\n",      ns->geom.pgaddrbytes);
+       printk("sector address bytes: %u\n",    ns->geom.secaddrbytes);
+       printk("options: %#x\n",                ns->options);
+
+       /* Map / allocate and initialize the flash image */
+#ifdef CONFIG_NS_ABS_POS
+       ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob);
+       if (!ns->mem.byte) {
+               NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n", 
+                       (void *)CONFIG_NS_ABS_POS);
+               return -ENOMEM;
+       }
+#else
+       ns->mem.byte = vmalloc(ns->geom.totszoob);
+       if (!ns->mem.byte) {
+               NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n",
+                       ns->geom.totszoob);
+               return -ENOMEM;
+       }
+       memset(ns->mem.byte, 0xFF, ns->geom.totszoob);
+#endif
+
+       /* Allocate / initialize the internal buffer */
+       ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
+       if (!ns->buf.byte) {
+               NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
+                       ns->geom.pgszoob);
+               goto error;
+       }
+       memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
+
+       /* Fill the partition_info structure */
+       ns->part.name   = "NAND simulator partition";
+       ns->part.offset = 0;
+       ns->part.size   = ns->geom.totsz;
+
+       return 0;
+
+error:
+#ifdef CONFIG_NS_ABS_POS
+       iounmap(ns->mem.byte);
+#else
+       vfree(ns->mem.byte);
+#endif
+
+       return -ENOMEM;
+}
+
+/*
+ * Free the nandsim structure.
+ */
+static void
+free_nandsim(struct nandsim *ns)
+{
+       kfree(ns->buf.byte);
+
+#ifdef CONFIG_NS_ABS_POS
+       iounmap(ns->mem.byte);
+#else
+       vfree(ns->mem.byte);
+#endif
+
+       return;
+}
+
+/*
+ * Returns the string representation of 'state' state.
+ */
+static char *
+get_state_name(uint32_t state)
+{
+       switch (NS_STATE(state)) {
+               case STATE_CMD_READ0:
+                       return "STATE_CMD_READ0";
+               case STATE_CMD_READ1:
+                       return "STATE_CMD_READ1";
+               case STATE_CMD_PAGEPROG:
+                       return "STATE_CMD_PAGEPROG";
+               case STATE_CMD_READOOB:
+                       return "STATE_CMD_READOOB";
+               case STATE_CMD_READSTART:
+                       return "STATE_CMD_READSTART";
+               case STATE_CMD_ERASE1:
+                       return "STATE_CMD_ERASE1";
+               case STATE_CMD_STATUS:
+                       return "STATE_CMD_STATUS";
+               case STATE_CMD_STATUS_M:
+                       return "STATE_CMD_STATUS_M";
+               case STATE_CMD_SEQIN:
+                       return "STATE_CMD_SEQIN";
+               case STATE_CMD_READID:
+                       return "STATE_CMD_READID";
+               case STATE_CMD_ERASE2:
+                       return "STATE_CMD_ERASE2";
+               case STATE_CMD_RESET:
+                       return "STATE_CMD_RESET";
+               case STATE_ADDR_PAGE:
+                       return "STATE_ADDR_PAGE";
+               case STATE_ADDR_SEC:
+                       return "STATE_ADDR_SEC";
+               case STATE_ADDR_ZERO:
+                       return "STATE_ADDR_ZERO";
+               case STATE_DATAIN:
+                       return "STATE_DATAIN";
+               case STATE_DATAOUT:
+                       return "STATE_DATAOUT";
+               case STATE_DATAOUT_ID:
+                       return "STATE_DATAOUT_ID";
+               case STATE_DATAOUT_STATUS:
+                       return "STATE_DATAOUT_STATUS";
+               case STATE_DATAOUT_STATUS_M:
+                       return "STATE_DATAOUT_STATUS_M";
+               case STATE_READY:
+                       return "STATE_READY";
+               case STATE_UNKNOWN:
+                       return "STATE_UNKNOWN";
+       }
+
+       NS_ERR("get_state_name: unknown state, BUG\n");
+       return NULL;
+}
+
+/*
+ * Check if command is valid.
+ *
+ * RETURNS: 1 if wrong command, 0 if right.
+ */
+static int
+check_command(int cmd)
+{
+       switch (cmd) {
+               
+       case NAND_CMD_READ0:
+       case NAND_CMD_READSTART:
+       case NAND_CMD_PAGEPROG:
+       case NAND_CMD_READOOB:
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_STATUS:
+       case NAND_CMD_SEQIN:
+       case NAND_CMD_READID:
+       case NAND_CMD_ERASE2:
+       case NAND_CMD_RESET:
+       case NAND_CMD_READ1:
+               return 0;
+               
+       case NAND_CMD_STATUS_MULTI:
+       default:
+               return 1;
+       }
+}
+
+/*
+ * Returns state after command is accepted by command number.
+ */
+static uint32_t
+get_state_by_command(unsigned command)
+{
+       switch (command) {
+               case NAND_CMD_READ0:
+                       return STATE_CMD_READ0;
+               case NAND_CMD_READ1:
+                       return STATE_CMD_READ1;
+               case NAND_CMD_PAGEPROG:
+                       return STATE_CMD_PAGEPROG;
+               case NAND_CMD_READSTART:
+                       return STATE_CMD_READSTART;
+               case NAND_CMD_READOOB:
+                       return STATE_CMD_READOOB;
+               case NAND_CMD_ERASE1:
+                       return STATE_CMD_ERASE1;
+               case NAND_CMD_STATUS:
+                       return STATE_CMD_STATUS;
+               case NAND_CMD_STATUS_MULTI:
+                       return STATE_CMD_STATUS_M;
+               case NAND_CMD_SEQIN:
+                       return STATE_CMD_SEQIN;
+               case NAND_CMD_READID:
+                       return STATE_CMD_READID;
+               case NAND_CMD_ERASE2:
+                       return STATE_CMD_ERASE2;
+               case NAND_CMD_RESET:
+                       return STATE_CMD_RESET;
+       }
+
+       NS_ERR("get_state_by_command: unknown command, BUG\n");
+       return 0;
+}
+
+/*
+ * Move an address byte to the correspondent internal register.
+ */
+static inline void
+accept_addr_byte(struct nandsim *ns, u_char bt)
+{
+       uint byte = (uint)bt;
+       
+       if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes))
+               ns->regs.column |= (byte << 8 * ns->regs.count);
+       else {
+               ns->regs.row |= (byte << 8 * (ns->regs.count -
+                                               ns->geom.pgaddrbytes +
+                                               ns->geom.secaddrbytes));
+       }
+
+       return;
+}
+               
+/*
+ * Switch to STATE_READY state.
+ */
+static inline void 
+switch_to_ready_state(struct nandsim *ns, u_char status)
+{
+       NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY));
+
+       ns->state       = STATE_READY;
+       ns->nxstate     = STATE_UNKNOWN;
+       ns->op          = NULL;
+       ns->npstates    = 0;
+       ns->stateidx    = 0;
+       ns->regs.num    = 0;
+       ns->regs.count  = 0;
+       ns->regs.off    = 0;
+       ns->regs.row    = 0;
+       ns->regs.column = 0;
+       ns->regs.status = status;
+}
+
+/*
+ * If the operation isn't known yet, try to find it in the global array
+ * of supported operations.
+ *
+ * Operation can be unknown because of the following.
+ *   1. New command was accepted and this is the firs call to find the
+ *      correspondent states chain. In this case ns->npstates = 0;
+ *   2. There is several operations which begin with the same command(s)
+ *      (for example program from the second half and read from the
+ *      second half operations both begin with the READ1 command). In this
+ *      case the ns->pstates[] array contains previous states.
+ * 
+ * Thus, the function tries to find operation containing the following
+ * states (if the 'flag' parameter is 0):
+ *    ns->pstates[0], ... ns->pstates[ns->npstates], ns->state
+ *
+ * If (one and only one) matching operation is found, it is accepted (
+ * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is
+ * zeroed).
+ * 
+ * If there are several maches, the current state is pushed to the
+ * ns->pstates.
+ *
+ * The operation can be unknown only while commands are input to the chip.
+ * As soon as address command is accepted, the operation must be known.
+ * In such situation the function is called with 'flag' != 0, and the
+ * operation is searched using the following pattern:
+ *     ns->pstates[0], ... ns->pstates[ns->npstates], <address input>
+ * 
+ * It is supposed that this pattern must either match one operation on
+ * none. There can't be ambiguity in that case.
+ *
+ * If no matches found, the functions does the following:
+ *   1. if there are saved states present, try to ignore them and search
+ *      again only using the last command. If nothing was found, switch
+ *      to the STATE_READY state.
+ *   2. if there are no saved states, switch to the STATE_READY state.
+ *
+ * RETURNS: -2 - no matched operations found.
+ *          -1 - several matches.
+ *           0 - operation is found.
+ */
+static int
+find_operation(struct nandsim *ns, uint32_t flag)
+{
+       int opsfound = 0;
+       int i, j, idx = 0;
+       
+       for (i = 0; i < NS_OPER_NUM; i++) {
+
+               int found = 1;
+       
+               if (!(ns->options & ops[i].reqopts))
+                       /* Ignore operations we can't perform */
+                       continue;
+                       
+               if (flag) {
+                       if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK))
+                               continue;
+               } else {
+                       if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates]))
+                               continue;
+               }
+
+               for (j = 0; j < ns->npstates; j++) 
+                       if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j])
+                               && (ns->options & ops[idx].reqopts)) {
+                               found = 0;
+                               break;
+                       }
+
+               if (found) {
+                       idx = i;
+                       opsfound += 1;
+               }
+       }
+
+       if (opsfound == 1) {
+               /* Exact match */
+               ns->op = &ops[idx].states[0];
+               if (flag) {
+                       /* 
+                        * In this case the find_operation function was
+                        * called when address has just began input. But it isn't
+                        * yet fully input and the current state must
+                        * not be one of STATE_ADDR_*, but the STATE_ADDR_*
+                        * state must be the next state (ns->nxstate).
+                        */
+                       ns->stateidx = ns->npstates - 1;
+               } else {
+                       ns->stateidx = ns->npstates;
+               }
+               ns->npstates = 0;
+               ns->state = ns->op[ns->stateidx];
+               ns->nxstate = ns->op[ns->stateidx + 1];
+               NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n",
+                               idx, get_state_name(ns->state), get_state_name(ns->nxstate));
+               return 0;
+       }
+       
+       if (opsfound == 0) {
+               /* Nothing was found. Try to ignore previous commands (if any) and search again */
+               if (ns->npstates != 0) {
+                       NS_DBG("find_operation: no operation found, try again with state %s\n",
+                                       get_state_name(ns->state));
+                       ns->npstates = 0;
+                       return find_operation(ns, 0);
+
+               }
+               NS_DBG("find_operation: no operations found\n");
+               switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+               return -2;
+       }
+       
+       if (flag) {
+               /* This shouldn't happen */
+               NS_DBG("find_operation: BUG, operation must be known if address is input\n");
+               return -2;
+       }
+       
+       NS_DBG("find_operation: there is still ambiguity\n");
+
+       ns->pstates[ns->npstates++] = ns->state;
+
+       return -1;
+}
+
+/*
+ * If state has any action bit, perform this action.
+ *
+ * RETURNS: 0 if success, -1 if error.
+ */
+static int
+do_state_action(struct nandsim *ns, uint32_t action)
+{
+       int i, num;
+       int busdiv = ns->busw == 8 ? 1 : 2;
+
+       action &= ACTION_MASK;
+       
+       /* Check that page address input is correct */
+       if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) {
+               NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row);
+               return -1;
+       }
+
+       switch (action) {
+
+       case ACTION_CPY:
+               /*
+                * Copy page data to the internal buffer.
+                */
+
+               /* Column shouldn't be very large */
+               if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) {
+                       NS_ERR("do_state_action: column number is too large\n");
+                       break;
+               }
+               num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+               memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num);
+
+               NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
+                       num, NS_RAW_OFFSET(ns) + ns->regs.off);
+               
+               if (ns->regs.off == 0)
+                       NS_LOG("read page %d\n", ns->regs.row);
+               else if (ns->regs.off < ns->geom.pgsz)
+                       NS_LOG("read page %d (second half)\n", ns->regs.row);
+               else
+                       NS_LOG("read OOB of page %d\n", ns->regs.row);
+               
+               NS_UDELAY(access_delay);
+               NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv);
+
+               break;
+
+       case ACTION_SECERASE:
+               /*
+                * Erase sector.
+                */
+               
+               if (ns->lines.wp) {
+                       NS_ERR("do_state_action: device is write-protected, ignore sector erase\n");
+                       return -1;
+               }
+               
+               if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec
+                       || (ns->regs.row & ~(ns->geom.secsz - 1))) {
+                       NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row);
+                       return -1;
+               }
+               
+               ns->regs.row = (ns->regs.row <<
+                               8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
+               ns->regs.column = 0;
+               
+               NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
+                               ns->regs.row, NS_RAW_OFFSET(ns));
+               NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
+
+               memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob);
+               
+               NS_MDELAY(erase_delay);
+               
+               break;
+
+       case ACTION_PRGPAGE:
+               /*
+                * Programm page - move internal buffer data to the page.
+                */
+
+               if (ns->lines.wp) {
+                       NS_WARN("do_state_action: device is write-protected, programm\n");
+                       return -1;
+               }
+
+               num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+               if (num != ns->regs.count) {
+                       NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n",
+                                       ns->regs.count, num);
+                       return -1;
+               }
+
+               for (i = 0; i < num; i++)
+                       ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i];
+
+               NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
+                       num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
+               NS_LOG("programm page %d\n", ns->regs.row);
+               
+               NS_UDELAY(programm_delay);
+               NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
+               
+               break;
+       
+       case ACTION_ZEROOFF:
+               NS_DBG("do_state_action: set internal offset to 0\n");
+               ns->regs.off = 0;
+               break;
+
+       case ACTION_HALFOFF:
+               if (!(ns->options & OPT_PAGE512_8BIT)) {
+                       NS_ERR("do_state_action: BUG! can't skip half of page for non-512"
+                               "byte page size 8x chips\n");
+                       return -1;
+               }
+               NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2);
+               ns->regs.off = ns->geom.pgsz/2;
+               break;
+
+       case ACTION_OOBOFF:
+               NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz);
+               ns->regs.off = ns->geom.pgsz;
+               break;
+               
+       default:
+               NS_DBG("do_state_action: BUG! unknown action\n");
+       }
+
+       return 0;
+}
+
+/*
+ * Switch simulator's state.
+ */
+static void
+switch_state(struct nandsim *ns)
+{
+       if (ns->op) {
+               /*
+                * The current operation have already been identified.
+                * Just follow the states chain.
+                */
+               
+               ns->stateidx += 1;
+               ns->state = ns->nxstate;
+               ns->nxstate = ns->op[ns->stateidx + 1];
+
+               NS_DBG("switch_state: operation is known, switch to the next state, "
+                       "state: %s, nxstate: %s\n",
+                       get_state_name(ns->state), get_state_name(ns->nxstate));
+
+               /* See, whether we need to do some action */
+               if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
+                       switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+                       return;
+               }
+               
+       } else {
+               /*
+                * We don't yet know which operation we perform.
+                * Try to identify it.
+                */
+
+               /*  
+                *  The only event causing the switch_state function to
+                *  be called with yet unknown operation is new command.
+                */
+               ns->state = get_state_by_command(ns->regs.command);
+
+               NS_DBG("switch_state: operation is unknown, try to find it\n");
+
+               if (find_operation(ns, 0) != 0)
+                       return;
+
+               if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
+                       switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+                       return;
+               }
+       }
+
+       /* For 16x devices column means the page offset in words */
+       if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) {
+               NS_DBG("switch_state: double the column number for 16x device\n");
+               ns->regs.column <<= 1;
+       }
+
+       if (NS_STATE(ns->nxstate) == STATE_READY) {
+               /*
+                * The current state is the last. Return to STATE_READY
+                */
+
+               u_char status = NS_STATUS_OK(ns);
+               
+               /* In case of data states, see if all bytes were input/output */
+               if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK))
+                       && ns->regs.count != ns->regs.num) {
+                       NS_WARN("switch_state: not all bytes were processed, %d left\n",
+                                       ns->regs.num - ns->regs.count);
+                       status = NS_STATUS_FAILED(ns);
+               }
+                               
+               NS_DBG("switch_state: operation complete, switch to STATE_READY state\n");
+
+               switch_to_ready_state(ns, status);
+
+               return;
+       } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) {
+               /* 
+                * If the next state is data input/output, switch to it now
+                */
+               
+               ns->state      = ns->nxstate;
+               ns->nxstate    = ns->op[++ns->stateidx + 1];
+               ns->regs.num   = ns->regs.count = 0;
+
+               NS_DBG("switch_state: the next state is data I/O, switch, "
+                       "state: %s, nxstate: %s\n",
+                       get_state_name(ns->state), get_state_name(ns->nxstate));
+
+               /*
+                * Set the internal register to the count of bytes which
+                * are expected to be input or output
+                */
+               switch (NS_STATE(ns->state)) {
+                       case STATE_DATAIN:
+                       case STATE_DATAOUT:
+                               ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
+                               break;
+                               
+                       case STATE_DATAOUT_ID:
+                               ns->regs.num = ns->geom.idbytes;
+                               break;
+                               
+                       case STATE_DATAOUT_STATUS:
+                       case STATE_DATAOUT_STATUS_M:
+                               ns->regs.count = ns->regs.num = 0;
+                               break;
+                               
+                       default:
+                               NS_ERR("switch_state: BUG! unknown data state\n");
+               }
+
+       } else if (ns->nxstate & STATE_ADDR_MASK) {
+               /*
+                * If the next state is address input, set the internal
+                * register to the number of expected address bytes
+                */
+
+               ns->regs.count = 0;
+               
+               switch (NS_STATE(ns->nxstate)) {
+                       case STATE_ADDR_PAGE:
+                               ns->regs.num = ns->geom.pgaddrbytes;
+               
+                               break;
+                       case STATE_ADDR_SEC:
+                               ns->regs.num = ns->geom.secaddrbytes;
+                               break;
+       
+                       case STATE_ADDR_ZERO:
+                               ns->regs.num = 1;
+                               break;
+
+                       default:
+                               NS_ERR("switch_state: BUG! unknown address state\n");
+               }
+       } else {
+               /* 
+                * Just reset internal counters.
+                */
+
+               ns->regs.num = 0;
+               ns->regs.count = 0;
+       }
+}
+
+static void
+ns_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+       struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
+
+       switch (cmd) {
+
+       /* set CLE line high */
+       case NAND_CTL_SETCLE:
+               NS_DBG("ns_hwcontrol: start command latch cycles\n");
+               ns->lines.cle  = 1;
+               break;
+
+       /* set CLE line low */
+       case NAND_CTL_CLRCLE:
+               NS_DBG("ns_hwcontrol: stop command latch cycles\n");
+               ns->lines.cle  = 0;
+               break;
+
+       /* set ALE line high */
+       case NAND_CTL_SETALE:
+               NS_DBG("ns_hwcontrol: start address latch cycles\n");
+               ns->lines.ale   = 1;
+               break;
+
+       /* set ALE line low */
+       case NAND_CTL_CLRALE:
+               NS_DBG("ns_hwcontrol: stop address latch cycles\n");
+               ns->lines.ale  = 0;
+               break;
+
+       /* set WP line high */
+       case NAND_CTL_SETWP:
+               NS_DBG("ns_hwcontrol: enable write protection\n");
+               ns->lines.wp = 1;
+               break;
+
+       /* set WP line low */
+       case NAND_CTL_CLRWP:
+               NS_DBG("ns_hwcontrol: disable write protection\n");
+               ns->lines.wp = 0;
+               break;
+
+       /* set CE line low */
+       case NAND_CTL_SETNCE:
+               NS_DBG("ns_hwcontrol: enable chip\n");
+               ns->lines.ce = 1;
+               break;
+
+       /* set CE line high */
+       case NAND_CTL_CLRNCE:
+               NS_DBG("ns_hwcontrol: disable chip\n");
+               ns->lines.ce = 0;
+               break;
+
+       default:
+               NS_ERR("hwcontrol: unknown command\n");
+        }
+
+       return;
+}
+
+static u_char
+ns_nand_read_byte(struct mtd_info *mtd)
+{
+        struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
+       u_char outb = 0x00;
+
+       /* Sanity and correctness checks */
+       if (!ns->lines.ce) {
+               NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb);
+               return outb;
+       }
+       if (ns->lines.ale || ns->lines.cle) {
+               NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb);
+               return outb;
+       }
+       if (!(ns->state & STATE_DATAOUT_MASK)) {
+               NS_WARN("read_byte: unexpected data output cycle, state is %s "
+                       "return %#x\n", get_state_name(ns->state), (uint)outb);
+               return outb;
+       }
+
+       /* Status register may be read as many times as it is wanted */
+       if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) {
+               NS_DBG("read_byte: return %#x status\n", ns->regs.status);
+               return ns->regs.status;
+       }
+
+       /* Check if there is any data in the internal buffer which may be read */
+       if (ns->regs.count == ns->regs.num) {
+               NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb);
+               return outb;
+       }
+
+       switch (NS_STATE(ns->state)) {
+               case STATE_DATAOUT:
+                       if (ns->busw == 8) {
+                               outb = ns->buf.byte[ns->regs.count];
+                               ns->regs.count += 1;
+                       } else {
+                               outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]);
+                               ns->regs.count += 2;
+                       }
+                       break;
+               case STATE_DATAOUT_ID:
+                       NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num);
+                       outb = ns->ids[ns->regs.count];
+                       ns->regs.count += 1;
+                       break;
+               default:
+                       BUG();
+       }
+       
+       if (ns->regs.count == ns->regs.num) {
+               NS_DBG("read_byte: all bytes were read\n");
+
+               /*
+                * The OPT_AUTOINCR allows to read next conseqitive pages without
+                * new read operation cycle.
+                */
+               if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
+                       ns->regs.count = 0;
+                       if (ns->regs.row + 1 < ns->geom.pgnum)
+                               ns->regs.row += 1;
+                       NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row);
+                       do_state_action(ns, ACTION_CPY);
+               }
+               else if (NS_STATE(ns->nxstate) == STATE_READY)
+                       switch_state(ns);
+               
+       }
+       
+       return outb;
+}
+
+static void
+ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+        struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
+       
+       /* Sanity and correctness checks */
+       if (!ns->lines.ce) {
+               NS_ERR("write_byte: chip is disabled, ignore write\n");
+               return;
+       }
+       if (ns->lines.ale && ns->lines.cle) {
+               NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n");
+               return;
+       }
+                       
+       if (ns->lines.cle == 1) {
+               /*
+                * The byte written is a command.
+                */
+
+               if (byte == NAND_CMD_RESET) {
+                       NS_LOG("reset chip\n");
+                       switch_to_ready_state(ns, NS_STATUS_OK(ns));
+                       return;
+               }
+
+               /* 
+                * Chip might still be in STATE_DATAOUT
+                * (if OPT_AUTOINCR feature is supported), STATE_DATAOUT_STATUS or
+                * STATE_DATAOUT_STATUS_M state. If so, switch state.
+                */
+               if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS
+                       || NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M
+                       || ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT))
+                       switch_state(ns);
+
+               /* Check if chip is expecting command */
+               if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) {
+                       /*
+                        * We are in situation when something else (not command)
+                        * was expected but command was input. In this case ignore
+                        * previous command(s)/state(s) and accept the last one.
+                        */
+                       NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, "
+                               "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate));
+                       switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+               }
+               
+               /* Check that the command byte is correct */
+               if (check_command(byte)) {
+                       NS_ERR("write_byte: unknown command %#x\n", (uint)byte);
+                       return;
+               }
+               
+               NS_DBG("command byte corresponding to %s state accepted\n",
+                       get_state_name(get_state_by_command(byte)));
+               ns->regs.command = byte;
+               switch_state(ns);
+
+       } else if (ns->lines.ale == 1) {
+               /*
+                * The byte written is an address.
+                */
+
+               if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) {
+
+                       NS_DBG("write_byte: operation isn't known yet, identify it\n");
+
+                       if (find_operation(ns, 1) < 0)
+                               return;
+                       
+                       if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) {
+                               switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+                               return;
+                       }
+                               
+                       ns->regs.count = 0;
+                       switch (NS_STATE(ns->nxstate)) {
+                               case STATE_ADDR_PAGE:
+                                       ns->regs.num = ns->geom.pgaddrbytes;
+                                       break;
+                               case STATE_ADDR_SEC:
+                                       ns->regs.num = ns->geom.secaddrbytes;
+                                       break;
+                               case STATE_ADDR_ZERO:
+                                       ns->regs.num = 1;
+                                       break;
+                               default:
+                                       BUG();
+                       }
+               }
+
+               /* Check that chip is expecting address */
+               if (!(ns->nxstate & STATE_ADDR_MASK)) {
+                       NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, "
+                               "switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate));
+                       switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+                       return;
+               }
+               
+               /* Check if this is expected byte */
+               if (ns->regs.count == ns->regs.num) {
+                       NS_ERR("write_byte: no more address bytes expected\n");
+                       switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+                       return;
+               }
+
+               accept_addr_byte(ns, byte);
+
+               ns->regs.count += 1;
+
+               NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n",
+                               (uint)byte, ns->regs.count, ns->regs.num);
+
+               if (ns->regs.count == ns->regs.num) {
+                       NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column);
+                       switch_state(ns);
+               }
+               
+       } else {
+               /*
+                * The byte written is an input data.
+                */
+               
+               /* Check that chip is expecting data input */
+               if (!(ns->state & STATE_DATAIN_MASK)) {
+                       NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, "
+                               "switch to %s\n", (uint)byte,
+                               get_state_name(ns->state), get_state_name(STATE_READY));
+                       switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+                       return;
+               }
+
+               /* Check if this is expected byte */
+               if (ns->regs.count == ns->regs.num) {
+                       NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n",
+                                       ns->regs.num);
+                       return;
+               }
+
+               if (ns->busw == 8) {
+                       ns->buf.byte[ns->regs.count] = byte;
+                       ns->regs.count += 1;
+               } else {
+                       ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte);
+                       ns->regs.count += 2;
+               }
+       }
+
+       return;
+}
+
+static int
+ns_device_ready(struct mtd_info *mtd)
+{
+       NS_DBG("device_ready\n");
+       return 1;
+}
+
+static uint16_t
+ns_nand_read_word(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+
+       NS_DBG("read_word\n");
+       
+       return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
+}
+
+static void
+ns_nand_write_word(struct mtd_info *mtd, uint16_t word)
+{
+       struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+       
+       NS_DBG("write_word\n");
+       
+       chip->write_byte(mtd, word & 0xFF);
+       chip->write_byte(mtd, word >> 8);
+}
+
+static void 
+ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+        struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
+
+       /* Check that chip is expecting data input */
+       if (!(ns->state & STATE_DATAIN_MASK)) {
+               NS_ERR("write_buf: data input isn't expected, state is %s, "
+                       "switch to STATE_READY\n", get_state_name(ns->state));
+               switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+               return;
+       }
+
+       /* Check if these are expected bytes */
+       if (ns->regs.count + len > ns->regs.num) {
+               NS_ERR("write_buf: too many input bytes\n");
+               switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+               return;
+       }
+
+       memcpy(ns->buf.byte + ns->regs.count, buf, len);
+       ns->regs.count += len;
+       
+       if (ns->regs.count == ns->regs.num) {
+               NS_DBG("write_buf: %d bytes were written\n", ns->regs.count);
+       }
+}
+
+static void 
+ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+        struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
+
+       /* Sanity and correctness checks */
+       if (!ns->lines.ce) {
+               NS_ERR("read_buf: chip is disabled\n");
+               return;
+       }
+       if (ns->lines.ale || ns->lines.cle) {
+               NS_ERR("read_buf: ALE or CLE pin is high\n");
+               return;
+       }
+       if (!(ns->state & STATE_DATAOUT_MASK)) {
+               NS_WARN("read_buf: unexpected data output cycle, current state is %s\n",
+                       get_state_name(ns->state));
+               return;
+       }
+
+       if (NS_STATE(ns->state) != STATE_DATAOUT) {
+               int i;
+
+               for (i = 0; i < len; i++)
+                       buf[i] = ((struct nand_chip *)mtd->priv)->read_byte(mtd);
+
+               return;
+       }
+
+       /* Check if these are expected bytes */
+       if (ns->regs.count + len > ns->regs.num) {
+               NS_ERR("read_buf: too many bytes to read\n");
+               switch_to_ready_state(ns, NS_STATUS_FAILED(ns));
+               return;
+       }
+
+       memcpy(buf, ns->buf.byte + ns->regs.count, len);
+       ns->regs.count += len;
+       
+       if (ns->regs.count == ns->regs.num) {
+               if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) {
+                       ns->regs.count = 0;
+                       if (ns->regs.row + 1 < ns->geom.pgnum)
+                               ns->regs.row += 1;
+                       NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row);
+                       do_state_action(ns, ACTION_CPY);
+               }
+               else if (NS_STATE(ns->nxstate) == STATE_READY)
+                       switch_state(ns);
+       }
+       
+       return;
+}
+
+static int 
+ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+       ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len);
+
+       if (!memcmp(buf, &ns_verify_buf[0], len)) {
+               NS_DBG("verify_buf: the buffer is OK\n");
+               return 0;
+       } else {
+               NS_DBG("verify_buf: the buffer is wrong\n");
+               return -EFAULT;
+       }
+}
+
+/*
+ * Having only NAND chip IDs we call nand_scan which detects NAND flash
+ * parameters and then calls scan_bbt in order to scan/find/build the
+ * NAND flash bad block table. But since at that moment the NAND flash
+ * image isn't allocated in the simulator, errors arise. To avoid this
+ * we redefine the scan_bbt callback and initialize the nandsim structure
+ * before the flash media scanning.
+ */
+int ns_scan_bbt(struct mtd_info *mtd)
+{ 
+       struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+       struct nandsim   *ns   = (struct nandsim *)(chip->priv);
+       int retval;
+
+       if (!NS_IS_INITIALIZED(ns))
+               if ((retval = init_nandsim(mtd)) != 0) {
+                       NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
+                       return retval;
+               }
+       if ((retval = nand_default_bbt(mtd)) != 0) {
+               free_nandsim(ns);
+               return retval;
+       }
+
+       return 0;
+}
+
+/*
+ * Module initialization function
+ */
+int __init ns_init_module(void)
+{
+       struct nand_chip *chip;
+       struct nandsim *nand;
+       int retval = -ENOMEM;
+
+       if (bus_width != 8 && bus_width != 16) {
+               NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
+               return -EINVAL;
+       }
+       
+       /* Allocate and initialize mtd_info, nand_chip and nandsim structures */
+       nsmtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip)
+                               + sizeof(struct nandsim), GFP_KERNEL);
+       if (!nsmtd) {
+               NS_ERR("unable to allocate core structures.\n");
+               return -ENOMEM;
+       }
+       memset(nsmtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip) +
+                       sizeof(struct nandsim));
+       chip        = (struct nand_chip *)(nsmtd + 1);
+        nsmtd->priv = (void *)chip;
+       nand        = (struct nandsim *)(chip + 1);
+       chip->priv  = (void *)nand;     
+
+       /*
+        * Register simulator's callbacks.
+        */
+       chip->hwcontrol  = ns_hwcontrol;
+       chip->read_byte  = ns_nand_read_byte;
+       chip->dev_ready  = ns_device_ready;
+       chip->scan_bbt   = ns_scan_bbt;
+       chip->write_byte = ns_nand_write_byte;
+       chip->write_buf  = ns_nand_write_buf;
+       chip->read_buf   = ns_nand_read_buf;
+       chip->verify_buf = ns_nand_verify_buf;
+       chip->write_word = ns_nand_write_word;
+       chip->read_word  = ns_nand_read_word;
+       chip->eccmode    = NAND_ECC_SOFT;
+
+       /* 
+        * Perform minimum nandsim structure initialization to handle
+        * the initial ID read command correctly 
+        */
+       if (third_id_byte != 0xFF || fourth_id_byte != 0xFF)
+               nand->geom.idbytes = 4;
+       else
+               nand->geom.idbytes = 2;
+       nand->regs.status = NS_STATUS_OK(nand);
+       nand->nxstate = STATE_UNKNOWN;
+       nand->options |= OPT_PAGE256; /* temporary value */
+       nand->ids[0] = first_id_byte;
+       nand->ids[1] = second_id_byte;
+       nand->ids[2] = third_id_byte;
+       nand->ids[3] = fourth_id_byte;
+       if (bus_width == 16) {
+               nand->busw = 16;
+               chip->options |= NAND_BUSWIDTH_16;
+       }
+
+       if ((retval = nand_scan(nsmtd, 1)) != 0) {
+               NS_ERR("can't register NAND Simulator\n");
+               if (retval > 0)
+                       retval = -ENXIO;
+               goto error;
+       }
+
+       /* Register NAND as one big partition */
+       add_mtd_partitions(nsmtd, &nand->part, 1);
+
+        return 0;
+
+error:
+       kfree(nsmtd);
+
+       return retval;
+}
+
+module_init(ns_init_module);
+
+/*
+ * Module clean-up function
+ */
+static void __exit ns_cleanup_module(void)
+{
+       struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv);
+
+       free_nandsim(ns);    /* Free nandsim private resources */
+       nand_release(nsmtd); /* Unregisterd drived */
+       kfree(nsmtd);        /* Free other structures */
+}
+
+module_exit(ns_cleanup_module);
+
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR ("Artem B. Bityuckiy");
+MODULE_DESCRIPTION ("The NAND flash simulator");
+
diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c
new file mode 100644 (file)
index 0000000..02305a2
--- /dev/null
@@ -0,0 +1,559 @@
+/*
+ *  drivers/mtd/nand/rtc_from4.c
+ *
+ *  Copyright (C) 2004  Red Hat, Inc.
+ * 
+ *  Derived from drivers/mtd/nand/spia.c
+ *       Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+ * $Id: rtc_from4.c,v 1.7 2004/11/04 12:53:10 gleixner Exp $
+ *
+ * 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.
+ *
+ * Overview:
+ *   This is a device driver for the AG-AND flash device found on the
+ *   Renesas Technology Corp. Flash ROM 4-slot interface board (FROM_BOARD4), 
+ *   which utilizes the Renesas HN29V1G91T-30 part. 
+ *   This chip is a 1 GBibit (128MiB x 8 bits) AG-AND flash device.
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/rslib.h>
+#include <linux/module.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+
+/*
+ * MTD structure for Renesas board
+ */
+static struct mtd_info *rtc_from4_mtd = NULL;
+
+#define RTC_FROM4_MAX_CHIPS    2
+
+/* HS77x9 processor register defines */
+#define SH77X9_BCR1    ((volatile unsigned short *)(0xFFFFFF60))
+#define SH77X9_BCR2    ((volatile unsigned short *)(0xFFFFFF62))
+#define SH77X9_WCR1    ((volatile unsigned short *)(0xFFFFFF64))
+#define SH77X9_WCR2    ((volatile unsigned short *)(0xFFFFFF66))
+#define SH77X9_MCR     ((volatile unsigned short *)(0xFFFFFF68))
+#define SH77X9_PCR     ((volatile unsigned short *)(0xFFFFFF6C))
+#define SH77X9_FRQCR   ((volatile unsigned short *)(0xFFFFFF80))
+
+/*
+ * Values specific to the Renesas Technology Corp. FROM_BOARD4 (used with HS77x9 processor)
+ */
+/* Address where flash is mapped */
+#define RTC_FROM4_FIO_BASE     0x14000000
+
+/* CLE and ALE are tied to address lines 5 & 4, respectively */
+#define RTC_FROM4_CLE          (1 << 5)
+#define RTC_FROM4_ALE          (1 << 4)
+
+/* address lines A24-A22 used for chip selection */
+#define RTC_FROM4_NAND_ADDR_SLOT3      (0x00800000)
+#define RTC_FROM4_NAND_ADDR_SLOT4      (0x00C00000)
+#define RTC_FROM4_NAND_ADDR_FPGA       (0x01000000)
+/* mask address lines A24-A22 used for chip selection */
+#define RTC_FROM4_NAND_ADDR_MASK       (RTC_FROM4_NAND_ADDR_SLOT3 | RTC_FROM4_NAND_ADDR_SLOT4 | RTC_FROM4_NAND_ADDR_FPGA)
+
+/* FPGA status register for checking device ready (bit zero) */
+#define RTC_FROM4_FPGA_SR              (RTC_FROM4_NAND_ADDR_FPGA | 0x00000002)
+#define RTC_FROM4_DEVICE_READY         0x0001
+
+/* FPGA Reed-Solomon ECC Control register */
+
+#define RTC_FROM4_RS_ECC_CTL           (RTC_FROM4_NAND_ADDR_FPGA | 0x00000050)
+#define RTC_FROM4_RS_ECC_CTL_CLR       (1 << 7)
+#define RTC_FROM4_RS_ECC_CTL_GEN       (1 << 6)
+#define RTC_FROM4_RS_ECC_CTL_FD_E      (1 << 5)
+
+/* FPGA Reed-Solomon ECC code base */
+#define RTC_FROM4_RS_ECC               (RTC_FROM4_NAND_ADDR_FPGA | 0x00000060)
+#define RTC_FROM4_RS_ECCN              (RTC_FROM4_NAND_ADDR_FPGA | 0x00000080)
+
+/* FPGA Reed-Solomon ECC check register */
+#define RTC_FROM4_RS_ECC_CHK           (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070)
+#define RTC_FROM4_RS_ECC_CHK_ERROR     (1 << 7)
+
+/* Undefine for software ECC */
+#define RTC_FROM4_HWECC        1
+
+/*
+ * Module stuff
+ */
+static void __iomem *rtc_from4_fio_base = P2SEGADDR(RTC_FROM4_FIO_BASE);
+
+const static struct mtd_partition partition_info[] = {
+        {
+                .name   = "Renesas flash partition 1",
+                .offset = 0,
+                .size   = MTDPART_SIZ_FULL
+        },
+};
+#define NUM_PARTITIONS 1
+
+/* 
+ *     hardware specific flash bbt decriptors
+ *     Note: this is to allow debugging by disabling 
+ *             NAND_BBT_CREATE and/or NAND_BBT_WRITE
+ *
+ */
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr rtc_from4_bbt_main_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+               | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+       .offs = 40,
+       .len = 4,
+       .veroffs = 44,
+       .maxblocks = 4,
+       .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+               | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+       .offs = 40,
+       .len = 4,
+       .veroffs = 44,
+       .maxblocks = 4,
+       .pattern = mirror_pattern
+};
+
+
+
+#ifdef RTC_FROM4_HWECC
+
+/* the Reed Solomon control structure */
+static struct rs_control *rs_decoder;
+
+/* 
+ *      hardware specific Out Of Band information
+ */
+static struct nand_oobinfo rtc_from4_nand_oobinfo = {
+       .useecc = MTD_NANDECC_AUTOPLACE,
+       .eccbytes = 32,
+       .eccpos = {
+                0,  1,  2,  3,  4,  5,  6,  7,
+                8,  9, 10, 11, 12, 13, 14, 15,
+               16, 17, 18, 19, 20, 21, 22, 23,
+               24, 25, 26, 27, 28, 29, 30, 31},
+       .oobfree = { {32, 32} }
+};
+
+/* Aargh. I missed the reversed bit order, when I
+ * was talking to Renesas about the FPGA.
+ *
+ * The table is used for bit reordering and inversion
+ * of the ecc byte which we get from the FPGA
+ */
+static uint8_t revbits[256] = {
+        0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+        0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+        0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+        0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+        0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+        0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+        0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+        0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+        0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+        0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+        0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+        0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+        0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+        0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+        0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+        0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+        0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+        0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+        0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+        0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+        0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+        0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+        0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+        0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+        0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+        0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+        0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+        0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+        0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+        0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+        0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+        0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+};
+
+#endif
+
+
+
+/* 
+ * rtc_from4_hwcontrol - hardware specific access to control-lines
+ * @mtd:       MTD device structure
+ * @cmd:       hardware control command
+ *
+ * Address lines (A5 and A4) are used to control Command and Address Latch 
+ * Enable on this board, so set the read/write address appropriately.
+ *
+ * Chip Enable is also controlled by the Chip Select (CS5) and 
+ * Address lines (A24-A22), so no action is required here.
+ *
+ */
+static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+       struct nand_chip* this = (struct nand_chip *) (mtd->priv);
+       
+       switch(cmd) {
+               
+       case NAND_CTL_SETCLE: 
+               this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE);
+               break;
+       case NAND_CTL_CLRCLE: 
+               this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE);
+               break;
+               
+       case NAND_CTL_SETALE:
+               this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE);
+               break;
+       case NAND_CTL_CLRALE:
+               this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE);
+               break;
+               
+       case NAND_CTL_SETNCE:
+               break;
+       case NAND_CTL_CLRNCE:
+               break;
+
+       }
+}
+
+
+/*
+ * rtc_from4_nand_select_chip - hardware specific chip select
+ * @mtd:       MTD device structure
+ * @chip:      Chip to select (0 == slot 3, 1 == slot 4)
+ *
+ * The chip select is based on address lines A24-A22.
+ * This driver uses flash slots 3 and 4 (A23-A22).
+ *
+ */
+static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+        struct nand_chip *this = mtd->priv;
+
+       this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK);
+       this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK);
+
+        switch(chip) {
+
+        case 0:                /* select slot 3 chip */
+               this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3);
+               this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3);
+                break;
+        case 1:                /* select slot 4 chip */
+               this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4);
+               this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4);
+                break;
+
+        }
+}
+
+
+
+/*
+ * rtc_from4_nand_device_ready - hardware specific ready/busy check
+ * @mtd:       MTD device structure
+ *
+ * This board provides the Ready/Busy state in the status register
+ * of the FPGA.  Bit zero indicates the RDY(1)/BSY(0) signal.
+ *
+ */
+static int rtc_from4_nand_device_ready(struct mtd_info *mtd)
+{
+       unsigned short status;
+
+       status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_FPGA_SR));
+
+       return (status & RTC_FROM4_DEVICE_READY);
+
+}
+
+#ifdef RTC_FROM4_HWECC
+/*
+ * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function
+ * @mtd:       MTD device structure
+ * @mode:      I/O mode; read or write
+ *
+ * enable hardware ECC for data read or write 
+ *
+ */
+static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       volatile unsigned short * rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL);
+       unsigned short status;
+
+       switch (mode) {
+           case NAND_ECC_READ :
+               status =  RTC_FROM4_RS_ECC_CTL_CLR 
+                       | RTC_FROM4_RS_ECC_CTL_FD_E;
+
+               *rs_ecc_ctl = status;
+               break;
+
+           case NAND_ECC_READSYN :
+               status =  0x00;
+
+               *rs_ecc_ctl = status;
+               break;
+
+           case NAND_ECC_WRITE :
+               status =  RTC_FROM4_RS_ECC_CTL_CLR 
+                       | RTC_FROM4_RS_ECC_CTL_GEN 
+                       | RTC_FROM4_RS_ECC_CTL_FD_E;
+
+               *rs_ecc_ctl = status;
+               break;
+
+           default:
+               BUG();
+               break;
+       }
+
+}
+
+/*
+ * rtc_from4_calculate_ecc - hardware specific code to read ECC code
+ * @mtd:       MTD device structure
+ * @dat:       buffer containing the data to generate ECC codes
+ * @ecc_code   ECC codes calculated
+ *
+ * The ECC code is calculated by the FPGA.  All we have to do is read the values
+ * from the FPGA registers.
+ *
+ * Note: We read from the inverted registers, since data is inverted before
+ * the code is calculated. So all 0xff data (blank page) results in all 0xff rs code
+ *
+ */
+static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+       volatile unsigned short * rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN);
+       unsigned short value;
+       int i;
+
+       for (i = 0; i < 8; i++) {
+               value = *rs_eccn;
+               ecc_code[i] = (unsigned char)value;
+               rs_eccn++;
+       }
+       ecc_code[7] |= 0x0f;    /* set the last four bits (not used) */
+}
+
+/*
+ * rtc_from4_correct_data - hardware specific code to correct data using ECC code
+ * @mtd:       MTD device structure
+ * @buf:       buffer containing the data to generate ECC codes
+ * @ecc1       ECC codes read
+ * @ecc2       ECC codes calculated
+ *
+ * The FPGA tells us fast, if there's an error or not. If no, we go back happy
+ * else we read the ecc results from the fpga and call the rs library to decode
+ * and hopefully correct the error
+ *
+ * For now I use the code, which we read from the FLASH to use the RS lib,
+ * as the syndrom conversion has a unresolved issue.
+ */
+static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2)
+{
+       int i, j, res;
+       unsigned short status; 
+       uint16_t par[6], syn[6], tmp;
+       uint8_t ecc[8];
+        volatile unsigned short *rs_ecc;
+
+       status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK));
+
+       if (!(status & RTC_FROM4_RS_ECC_CHK_ERROR)) {
+               return 0;
+       }
+
+       /* Read the syndrom pattern from the FPGA and correct the bitorder */
+       rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC);
+        for (i = 0; i < 8; i++) {
+                ecc[i] = revbits[(*rs_ecc) & 0xFF];
+                rs_ecc++;
+        }
+
+       /* convert into 6 10bit syndrome fields */
+       par[5] = rs_decoder->index_of[(((uint16_t)ecc[0] >> 0) & 0x0ff) | 
+                                     (((uint16_t)ecc[1] << 8) & 0x300)];
+       par[4] = rs_decoder->index_of[(((uint16_t)ecc[1] >> 2) & 0x03f) |
+                                     (((uint16_t)ecc[2] << 6) & 0x3c0)];
+       par[3] = rs_decoder->index_of[(((uint16_t)ecc[2] >> 4) & 0x00f) |
+                                     (((uint16_t)ecc[3] << 4) & 0x3f0)];
+       par[2] = rs_decoder->index_of[(((uint16_t)ecc[3] >> 6) & 0x003) |
+                                     (((uint16_t)ecc[4] << 2) & 0x3fc)];
+       par[1] = rs_decoder->index_of[(((uint16_t)ecc[5] >> 0) & 0x0ff) |
+                                     (((uint16_t)ecc[6] << 8) & 0x300)];
+       par[0] = (((uint16_t)ecc[6] >> 2) & 0x03f) | (((uint16_t)ecc[7] << 6) & 0x3c0);
+
+       /* Convert to computable syndrome */
+       for (i = 0; i < 6; i++) {
+               syn[i] = par[0];
+               for (j = 1; j < 6; j++)
+                       if (par[j] != rs_decoder->nn)
+                               syn[i] ^= rs_decoder->alpha_to[rs_modnn(rs_decoder, par[j] + i * j)];
+
+               /* Convert to index form */
+               syn[i] = rs_decoder->index_of[syn[i]];
+       }
+
+       /* Let the library code do its magic.*/
+       res = decode_rs8(rs_decoder, buf, par, 512, syn, 0, NULL, 0xff, NULL);
+       if (res > 0) {
+               DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: " 
+                       "ECC corrected %d errors on read\n", res);
+       }
+       return res;
+}
+#endif
+
+/*
+ * Main initialization routine
+ */
+int __init rtc_from4_init (void)
+{
+       struct nand_chip *this;
+       unsigned short bcr1, bcr2, wcr2;
+
+       /* Allocate memory for MTD device structure and private data */
+       rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip),
+                               GFP_KERNEL);
+       if (!rtc_from4_mtd) {
+               printk ("Unable to allocate Renesas NAND MTD device structure.\n");
+               return -ENOMEM;
+       }
+
+       /* Get pointer to private data */
+       this = (struct nand_chip *) (&rtc_from4_mtd[1]);
+
+       /* Initialize structures */
+       memset((char *) rtc_from4_mtd, 0, sizeof(struct mtd_info));
+       memset((char *) this, 0, sizeof(struct nand_chip));
+
+       /* Link the private data with the MTD structure */
+       rtc_from4_mtd->priv = this;
+
+       /* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */
+       bcr1 = *SH77X9_BCR1 & ~0x0002;
+       bcr1 |= 0x0002;
+       *SH77X9_BCR1 = bcr1;
+
+       /* set */
+       bcr2 = *SH77X9_BCR2 & ~0x0c00;
+       bcr2 |= 0x0800;
+       *SH77X9_BCR2 = bcr2;
+
+       /* set area 5 wait states */
+       wcr2 = *SH77X9_WCR2 & ~0x1c00;
+       wcr2 |= 0x1c00;
+       *SH77X9_WCR2 = wcr2;
+
+       /* Set address of NAND IO lines */
+       this->IO_ADDR_R = rtc_from4_fio_base;
+       this->IO_ADDR_W = rtc_from4_fio_base;
+       /* Set address of hardware control function */
+       this->hwcontrol = rtc_from4_hwcontrol;
+       /* Set address of chip select function */
+        this->select_chip = rtc_from4_nand_select_chip;
+       /* command delay time (in us) */
+       this->chip_delay = 100;
+       /* return the status of the Ready/Busy line */
+       this->dev_ready = rtc_from4_nand_device_ready;
+
+#ifdef RTC_FROM4_HWECC
+       printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n");
+
+        this->eccmode = NAND_ECC_HW8_512;
+       this->options |= NAND_HWECC_SYNDROME;
+       /* set the nand_oobinfo to support FPGA H/W error detection */
+       this->autooob = &rtc_from4_nand_oobinfo;
+       this->enable_hwecc = rtc_from4_enable_hwecc;
+       this->calculate_ecc = rtc_from4_calculate_ecc;
+       this->correct_data = rtc_from4_correct_data;
+#else
+       printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n");
+
+       this->eccmode = NAND_ECC_SOFT;
+#endif
+
+       /* set the bad block tables to support debugging */
+       this->bbt_td = &rtc_from4_bbt_main_descr;
+       this->bbt_md = &rtc_from4_bbt_mirror_descr;
+
+       /* Scan to find existence of the device */
+       if (nand_scan(rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) {
+               kfree(rtc_from4_mtd);
+               return -ENXIO;
+       }
+
+       /* Register the partitions */
+       add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS);
+
+#ifdef RTC_FROM4_HWECC
+       /* We could create the decoder on demand, if memory is a concern.
+        * This way we have it handy, if an error happens 
+        *
+        * Symbolsize is 10 (bits)
+        * Primitve polynomial is x^10+x^3+1
+        * first consecutive root is 0
+        * primitve element to generate roots = 1
+        * generator polinomial degree = 6
+        */
+       rs_decoder = init_rs(10, 0x409, 0, 1, 6);
+       if (!rs_decoder) {
+               printk (KERN_ERR "Could not create a RS decoder\n");
+               nand_release(rtc_from4_mtd);
+               kfree(rtc_from4_mtd);
+               return -ENOMEM;
+       }
+#endif
+       /* Return happy */
+       return 0;
+}
+module_init(rtc_from4_init);
+
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit rtc_from4_cleanup (void)
+{
+       /* Release resource, unregister partitions */
+       nand_release(rtc_from4_mtd);
+
+       /* Free the MTD device structure */
+       kfree (rtc_from4_mtd);
+
+#ifdef RTC_FROM4_HWECC
+       /* Free the reed solomon resources */
+       if (rs_decoder) {
+               free_rs(rs_decoder);
+       }
+#endif
+}
+module_exit(rtc_from4_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("d.marlin <dmarlin@redhat.com");
+MODULE_DESCRIPTION("Board-specific glue layer for AG-AND flash on Renesas FROM_BOARD4");
+
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
new file mode 100644 (file)
index 0000000..d11fe47
--- /dev/null
@@ -0,0 +1,704 @@
+/* linux/drivers/mtd/nand/s3c2410.c
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Samsung S3C2410 NAND driver
+ *
+ * Changelog:
+ *     21-Sep-2004  BJD  Initial version
+ *     23-Sep-2004  BJD  Mulitple device support
+ *     28-Sep-2004  BJD  Fixed ECC placement for Hardware mode
+ *     12-Oct-2004  BJD  Fixed errors in use of platform data
+ *
+ * $Id: s3c2410.c,v 1.5 2004/10/12 10:10:15 bjd Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <config/mtd/nand/s3c2410/hwecc.h>
+#include <config/mtd/nand/s3c2410/debug.h>
+
+#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/hardware/clock.h>
+
+#include <asm/arch/regs-nand.h>
+#include <asm/arch/nand.h>
+
+#define PFX "s3c2410-nand: "
+
+#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
+static int hardware_ecc = 1;
+#else
+static int hardware_ecc = 0;
+#endif
+
+/* new oob placement block for use with hardware ecc generation
+ */
+
+static struct nand_oobinfo nand_hw_eccoob = {
+       .useecc = MTD_NANDECC_AUTOPLACE,
+       .eccbytes = 3,
+       .eccpos = {0, 1, 2 },
+       .oobfree = { {8, 8} }
+};
+
+/* controller and mtd information */
+
+struct s3c2410_nand_info;
+
+struct s3c2410_nand_mtd {
+       struct mtd_info                 mtd;
+       struct nand_chip                chip;
+       struct s3c2410_nand_set         *set;
+       struct s3c2410_nand_info        *info;
+       int                             scan_res;
+};
+
+/* overview of the s3c2410 nand state */
+
+struct s3c2410_nand_info {
+       /* mtd info */
+       struct nand_hw_control          controller;
+       struct s3c2410_nand_mtd         *mtds;
+       struct s3c2410_platform_nand    *platform;
+
+       /* device info */
+       struct device                   *device;
+       struct resource                 *area;
+       struct clk                      *clk;
+       void                            *regs;
+       int                             mtd_count;
+};
+
+/* conversion functions */
+
+static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd)
+{
+       return container_of(mtd, struct s3c2410_nand_mtd, mtd);
+}
+
+static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd)
+{
+       return s3c2410_nand_mtd_toours(mtd)->info;
+}
+
+static struct s3c2410_nand_info *to_nand_info(struct device *dev)
+{
+       return (struct s3c2410_nand_info *)dev_get_drvdata(dev);
+}
+
+static struct s3c2410_platform_nand *to_nand_plat(struct device *dev)
+{
+       return (struct s3c2410_platform_nand *)dev->platform_data;
+}
+
+/* timing calculations */
+
+#define NS_IN_KHZ 10000000
+
+static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
+{
+       int result;
+
+       result = (wanted * NS_IN_KHZ) / clk;
+       result++;
+
+       pr_debug("result %d from %ld, %d\n", result, clk, wanted);
+
+       if (result > max) {
+               printk("%d ns is too big for current clock rate %ld\n",
+                      wanted, clk);
+               return -1;
+       }
+
+       if (result < 1)
+               result = 1;
+
+       return result;
+}
+
+#define to_ns(ticks,clk) (((clk) * (ticks)) / NS_IN_KHZ)
+
+/* controller setup */
+
+static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, 
+                              struct device *dev)
+{
+       struct s3c2410_platform_nand *plat = to_nand_plat(dev);
+       unsigned int tacls, twrph0, twrph1;
+       unsigned long clkrate = clk_get_rate(info->clk);
+       unsigned long cfg;
+
+       /* calculate the timing information for the controller */
+
+       if (plat != NULL) {
+               tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8);
+               twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
+               twrph1 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
+       } else {
+               /* default timings */
+               tacls = 8;
+               twrph0 = 8;
+               twrph1 = 8;
+       }
+       
+       if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {
+               printk(KERN_ERR PFX "cannot get timings suitable for board\n");
+               return -EINVAL;
+       }
+
+       printk(KERN_INFO PFX "timing: Tacls %ldns, Twrph0 %ldns, Twrph1 %ldns\n",
+              to_ns(tacls, clkrate),
+              to_ns(twrph0, clkrate),
+              to_ns(twrph1, clkrate));
+
+       cfg  = S3C2410_NFCONF_EN;
+       cfg |= S3C2410_NFCONF_TACLS(tacls-1);
+       cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1);
+       cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1);
+
+       pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
+
+       writel(cfg, info->regs + S3C2410_NFCONF);
+       return 0;
+}
+
+/* select chip */
+
+static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+       struct s3c2410_nand_info *info;
+       struct s3c2410_nand_mtd *nmtd; 
+       struct nand_chip *this = mtd->priv;
+       unsigned long cur;
+
+       nmtd = (struct s3c2410_nand_mtd *)this->priv;
+       info = nmtd->info;
+
+       cur = readl(info->regs + S3C2410_NFCONF);
+
+       if (chip == -1) {
+               cur |= S3C2410_NFCONF_nFCE;
+       } else {
+               if (chip > nmtd->set->nr_chips) {
+                       printk(KERN_ERR PFX "chip %d out of range\n", chip);
+                       return;
+               }
+
+               if (info->platform != NULL) {
+                       if (info->platform->select_chip != NULL)
+                               (info->platform->select_chip)(nmtd->set, chip);
+               }
+
+               cur &= ~S3C2410_NFCONF_nFCE;
+       }
+
+       writel(cur, info->regs + S3C2410_NFCONF);
+}
+
+/* command and control functions */
+
+static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+       unsigned long cur;
+
+       switch (cmd) {
+       case NAND_CTL_SETNCE:
+               cur = readl(info->regs + S3C2410_NFCONF);
+               cur &= ~S3C2410_NFCONF_nFCE;
+               writel(cur, info->regs + S3C2410_NFCONF);
+               break;
+
+       case NAND_CTL_CLRNCE:
+               cur = readl(info->regs + S3C2410_NFCONF);
+               cur |= S3C2410_NFCONF_nFCE;
+               writel(cur, info->regs + S3C2410_NFCONF);
+               break;
+
+               /* we don't need to implement these */
+       case NAND_CTL_SETCLE:
+       case NAND_CTL_CLRCLE:
+       case NAND_CTL_SETALE:
+       case NAND_CTL_CLRALE:
+               pr_debug(PFX "s3c2410_nand_hwcontrol(%d) unusedn", cmd);
+               break;
+       }
+}
+
+/* s3c2410_nand_command
+ *
+ * This function implements sending commands and the relevant address
+ * information to the chip, via the hardware controller. Since the
+ * S3C2410 generates the correct ALE/CLE signaling automatically, we
+ * do not need to use hwcontrol.
+*/
+
+static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command,
+                                 int column, int page_addr)
+{
+       register struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+       register struct nand_chip *this = mtd->priv;
+
+       /*
+        * Write out the command to the device.
+        */
+       if (command == NAND_CMD_SEQIN) {
+               int readcmd;
+
+               if (column >= mtd->oobblock) {
+                       /* OOB area */
+                       column -= mtd->oobblock;
+                       readcmd = NAND_CMD_READOOB;
+               } else if (column < 256) {
+                       /* First 256 bytes --> READ0 */
+                       readcmd = NAND_CMD_READ0;
+               } else {
+                       column -= 256;
+                       readcmd = NAND_CMD_READ1;
+               }
+               
+               writeb(readcmd, info->regs + S3C2410_NFCMD);
+       }
+       writeb(command, info->regs + S3C2410_NFCMD);
+
+       /* Set ALE and clear CLE to start address cycle */
+
+       if (column != -1 || page_addr != -1) {
+
+               /* Serially input address */
+               if (column != -1) {
+                       /* Adjust columns for 16 bit buswidth */
+                       if (this->options & NAND_BUSWIDTH_16)
+                               column >>= 1;
+                       writeb(column, info->regs + S3C2410_NFADDR);
+               }
+               if (page_addr != -1) {
+                       writeb((unsigned char) (page_addr), info->regs + S3C2410_NFADDR);
+                       writeb((unsigned char) (page_addr >> 8), info->regs + S3C2410_NFADDR);
+                       /* One more address cycle for higher density devices */
+                       if (this->chipsize & 0x0c000000) 
+                               writeb((unsigned char) ((page_addr >> 16) & 0x0f),
+                                      info->regs + S3C2410_NFADDR);
+               }
+               /* Latch in address */
+       }
+       
+       /* 
+        * program and erase have their own busy handlers 
+        * status and sequential in needs no delay
+       */
+       switch (command) {
+                       
+       case NAND_CMD_PAGEPROG:
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_ERASE2:
+       case NAND_CMD_SEQIN:
+       case NAND_CMD_STATUS:
+               return;
+
+       case NAND_CMD_RESET:
+               if (this->dev_ready)    
+                       break;
+
+               udelay(this->chip_delay);
+               writeb(NAND_CMD_STATUS, info->regs + S3C2410_NFCMD);
+
+               while ( !(this->read_byte(mtd) & 0x40));
+               return;
+
+       /* This applies to read commands */     
+       default:
+               /* 
+                * If we don't have access to the busy pin, we apply the given
+                * command delay
+               */
+               if (!this->dev_ready) {
+                       udelay (this->chip_delay);
+                       return;
+               }       
+       }
+       
+       /* Apply this short delay always to ensure that we do wait tWB in
+        * any case on any machine. */
+       ndelay (100);
+       /* wait until command is processed */
+       while (!this->dev_ready(mtd));
+}
+
+
+/* s3c2410_nand_devready()
+ *
+ * returns 0 if the nand is busy, 1 if it is ready
+*/
+
+static int s3c2410_nand_devready(struct mtd_info *mtd)
+{
+       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+       
+       return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
+}
+
+/* ECC handling functions */
+
+static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+                                    u_char *read_ecc, u_char *calc_ecc)
+{
+       pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n",
+                mtd, dat, read_ecc, calc_ecc);
+
+       pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n",
+                read_ecc[0], read_ecc[1], read_ecc[2],
+                calc_ecc[0], calc_ecc[1], calc_ecc[2]);
+
+       if (read_ecc[0] == calc_ecc[0] &&
+           read_ecc[1] == calc_ecc[1] &&
+           read_ecc[2] == calc_ecc[2]) 
+               return 0;
+
+       /* we curently have no method for correcting the error */
+
+       return -1;
+}
+
+static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+       unsigned long ctrl;
+
+       ctrl = readl(info->regs + S3C2410_NFCONF);
+       ctrl |= S3C2410_NFCONF_INITECC;
+       writel(ctrl, info->regs + S3C2410_NFCONF);
+}
+
+static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
+                                     const u_char *dat, u_char *ecc_code)
+{
+       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+
+       ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0);
+       ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
+       ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
+
+       pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n",
+                ecc_code[0], ecc_code[1], ecc_code[2]);
+
+       return 0;
+}
+
+
+/* over-ride the standard functions for a little more speed? */
+
+static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+       struct nand_chip *this = (struct nand_chip *)mtd->priv;
+       readsb(this->IO_ADDR_R, buf, len);
+}
+
+static void s3c2410_nand_write_buf(struct mtd_info *mtd,
+                                  const u_char *buf, int len)
+{
+       struct nand_chip *this = (struct nand_chip *)mtd->priv;
+       writesb(this->IO_ADDR_W, buf, len);
+}
+
+/* device management functions */
+
+static int s3c2410_nand_remove(struct device *dev)
+{
+       struct s3c2410_nand_info *info = to_nand_info(dev);
+
+       dev_set_drvdata(dev, NULL);
+
+       if (info == NULL) 
+               return 0;
+
+       /* first thing we need to do is release all our mtds
+        * and their partitions, then go through freeing the
+        * resources used 
+        */
+       
+       if (info->mtds != NULL) {
+               struct s3c2410_nand_mtd *ptr = info->mtds;
+               int mtdno;
+
+               for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) {
+                       pr_debug("releasing mtd %d (%p)\n", mtdno, ptr);
+                       nand_release(&ptr->mtd);
+               }
+
+               kfree(info->mtds);
+       }
+
+       /* free the common resources */
+
+       if (info->clk != NULL && !IS_ERR(info->clk)) {
+               clk_disable(info->clk);
+               clk_unuse(info->clk);
+               clk_put(info->clk);
+       }
+
+       if (info->regs != NULL) {
+               iounmap(info->regs);
+               info->regs = NULL;
+       }
+
+       if (info->area != NULL) {
+               release_resource(info->area);
+               kfree(info->area);
+               info->area = NULL;
+       }
+
+       kfree(info);
+
+       return 0;
+}
+
+#ifdef CONFIG_MTD_PARTITIONS
+static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
+                                     struct s3c2410_nand_mtd *mtd,
+                                     struct s3c2410_nand_set *set)
+{
+       if (set == NULL)
+               return add_mtd_device(&mtd->mtd);
+
+       if (set->nr_partitions > 0 && set->partitions != NULL) {
+               return add_mtd_partitions(&mtd->mtd,
+                                         set->partitions,
+                                         set->nr_partitions);
+       }
+
+       return add_mtd_device(&mtd->mtd);
+}
+#else
+static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
+                                     struct s3c2410_nand_mtd *mtd,
+                                     struct s3c2410_nand_set *set)
+{
+       return add_mtd_device(&mtd->mtd);
+}
+#endif
+
+/* s3c2410_nand_init_chip
+ *
+ * init a single instance of an chip 
+*/
+
+static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
+                                  struct s3c2410_nand_mtd *nmtd,
+                                  struct s3c2410_nand_set *set)
+{
+       struct nand_chip *chip = &nmtd->chip;
+
+       chip->IO_ADDR_R    = (char *)info->regs + S3C2410_NFDATA;
+       chip->IO_ADDR_W    = (char *)info->regs + S3C2410_NFDATA;
+       chip->hwcontrol    = s3c2410_nand_hwcontrol;
+       chip->dev_ready    = s3c2410_nand_devready;
+       chip->cmdfunc      = s3c2410_nand_command;
+       chip->write_buf    = s3c2410_nand_write_buf;
+       chip->read_buf     = s3c2410_nand_read_buf;
+       chip->select_chip  = s3c2410_nand_select_chip;
+       chip->chip_delay   = 50;
+       chip->priv         = nmtd;
+       chip->options      = 0;
+       chip->controller   = &info->controller;
+
+       nmtd->info         = info;
+       nmtd->mtd.priv     = chip;
+       nmtd->set          = set;
+
+       if (hardware_ecc) {
+               chip->correct_data  = s3c2410_nand_correct_data;
+               chip->enable_hwecc  = s3c2410_nand_enable_hwecc;
+               chip->calculate_ecc = s3c2410_nand_calculate_ecc;
+               chip->eccmode       = NAND_ECC_HW3_512;
+               chip->autooob       = &nand_hw_eccoob;
+       } else {
+               chip->eccmode       = NAND_ECC_SOFT;
+       }
+}
+
+/* s3c2410_nand_probe
+ *
+ * called by device layer when it finds a device matching
+ * one our driver can handled. This code checks to see if
+ * it can allocate all necessary resources then calls the
+ * nand layer to look for devices
+*/
+
+static int s3c2410_nand_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct s3c2410_platform_nand *plat = to_nand_plat(dev);
+       struct s3c2410_nand_info *info;
+       struct s3c2410_nand_mtd *nmtd;
+       struct s3c2410_nand_set *sets;
+       struct resource *res;
+       int err = 0;
+       int size;
+       int nr_sets;
+       int setno;
+
+       pr_debug("s3c2410_nand_probe(%p)\n", dev);
+
+       info = kmalloc(sizeof(*info), GFP_KERNEL);
+       if (info == NULL) {
+               printk(KERN_ERR PFX "no memory for flash info\n");
+               err = -ENOMEM;
+               goto exit_error;
+       }
+
+       memzero(info, sizeof(*info));
+       dev_set_drvdata(dev, info);
+
+       spin_lock_init(&info->controller.lock);
+
+       /* get the clock source and enable it */
+
+       info->clk = clk_get(dev, "nand");
+       if (IS_ERR(info->clk)) {
+               printk(KERN_ERR PFX "failed to get clock");
+               err = -ENOENT;
+               goto exit_error;
+       }
+
+       clk_use(info->clk);
+       clk_enable(info->clk);
+
+       /* allocate and map the resource */
+
+       res = pdev->resource;  /* assume that the flash has one resource */
+       size = res->end - res->start + 1;
+
+       info->area = request_mem_region(res->start, size, pdev->name);
+
+       if (info->area == NULL) {
+               printk(KERN_ERR PFX "cannot reserve register region\n");
+               err = -ENOENT;
+               goto exit_error;
+       }
+
+       info->device = dev;
+       info->platform = plat;
+       info->regs = ioremap(res->start, size);
+
+       if (info->regs == NULL) {
+               printk(KERN_ERR PFX "cannot reserve register region\n");
+               err = -EIO;
+               goto exit_error;
+       }               
+
+       printk(KERN_INFO PFX "mapped registers at %p\n", info->regs);
+
+       /* initialise the hardware */
+
+       err = s3c2410_nand_inithw(info, dev);
+       if (err != 0)
+               goto exit_error;
+
+       sets = (plat != NULL) ? plat->sets : NULL;
+       nr_sets = (plat != NULL) ? plat->nr_sets : 1;
+
+       info->mtd_count = nr_sets;
+
+       /* allocate our information */
+
+       size = nr_sets * sizeof(*info->mtds);
+       info->mtds = kmalloc(size, GFP_KERNEL);
+       if (info->mtds == NULL) {
+               printk(KERN_ERR PFX "failed to allocate mtd storage\n");
+               err = -ENOMEM;
+               goto exit_error;
+       }
+
+       memzero(info->mtds, size);
+
+       /* initialise all possible chips */
+
+       nmtd = info->mtds;
+
+       for (setno = 0; setno < nr_sets; setno++, nmtd++) {
+               pr_debug("initialising set %d (%p, info %p)\n",
+                        setno, nmtd, info);
+               
+               s3c2410_nand_init_chip(info, nmtd, sets);
+
+               nmtd->scan_res = nand_scan(&nmtd->mtd,
+                                          (sets) ? sets->nr_chips : 1);
+
+               if (nmtd->scan_res == 0) {
+                       s3c2410_nand_add_partition(info, nmtd, sets);
+               }
+
+               if (sets != NULL)
+                       sets++;
+       }
+       
+       pr_debug("initialised ok\n");
+       return 0;
+
+ exit_error:
+       s3c2410_nand_remove(dev);
+
+       if (err == 0)
+               err = -EINVAL;
+       return err;
+}
+
+static struct device_driver s3c2410_nand_driver = {
+       .name           = "s3c2410-nand",
+       .bus            = &platform_bus_type,
+       .probe          = s3c2410_nand_probe,
+       .remove         = s3c2410_nand_remove,
+};
+
+static int __init s3c2410_nand_init(void)
+{
+       printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n");
+       return driver_register(&s3c2410_nand_driver);
+}
+
+static void __exit s3c2410_nand_exit(void)
+{
+       driver_unregister(&s3c2410_nand_driver);
+}
+
+module_init(s3c2410_nand_init);
+module_exit(s3c2410_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C2410 MTD NAND driver");
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c
new file mode 100755 (executable)
index 0000000..2957279
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * drivers/mtd/nand/sharpsl.c
+ *
+ *  Copyright (C) 2004 Richard Purdie
+ *
+ *  $Id: sharpsl.c,v 1.3 2005/01/03 14:53:50 rpurdie Exp $
+ *
+ *  Based on Sharp's NAND driver sharp_sl.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 <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+
+static void __iomem *sharpsl_io_base;
+static int sharpsl_phys_base = 0x0C000000;
+
+/* register offset */
+#define ECCLPLB                sharpsl_io_base+0x00    /* line parity 7 - 0 bit */
+#define ECCLPUB                sharpsl_io_base+0x04    /* line parity 15 - 8 bit */
+#define ECCCP          sharpsl_io_base+0x08    /* column parity 5 - 0 bit */
+#define ECCCNTR                sharpsl_io_base+0x0C    /* ECC byte counter */
+#define ECCCLRR                sharpsl_io_base+0x10    /* cleare ECC */
+#define FLASHIO                sharpsl_io_base+0x14    /* Flash I/O */
+#define FLASHCTL       sharpsl_io_base+0x18    /* Flash Control */
+
+/* Flash control bit */
+#define FLRYBY         (1 << 5)
+#define FLCE1          (1 << 4)
+#define FLWP           (1 << 3)
+#define FLALE          (1 << 2)
+#define FLCLE          (1 << 1)
+#define FLCE0          (1 << 0)
+
+
+/*
+ * MTD structure for SharpSL
+ */
+static struct mtd_info *sharpsl_mtd = NULL;
+
+/*
+ * Define partitions for flash device
+ */
+#define DEFAULT_NUM_PARTITIONS 3
+
+static int nr_partitions;
+static struct mtd_partition sharpsl_nand_default_partition_info[] = {
+       {
+       .name = "System Area",
+       .offset = 0,
+       .size = 7 * 1024 * 1024,
+       },
+       {
+       .name = "Root Filesystem",
+       .offset = 7 * 1024 * 1024,
+       .size = 30 * 1024 * 1024,
+       },
+       {
+       .name = "Home Filesystem",
+       .offset = MTDPART_OFS_APPEND ,
+       .size = MTDPART_SIZ_FULL ,
+       },
+};
+
+/* 
+ *     hardware specific access to control-lines
+ */
+static void
+sharpsl_nand_hwcontrol(struct mtd_info* mtd, int cmd)
+{
+       switch (cmd) {
+       case NAND_CTL_SETCLE: 
+               writeb(readb(FLASHCTL) | FLCLE, FLASHCTL);
+               break;
+       case NAND_CTL_CLRCLE:
+               writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL);
+               break;
+
+       case NAND_CTL_SETALE:
+               writeb(readb(FLASHCTL) | FLALE, FLASHCTL);
+               break;
+       case NAND_CTL_CLRALE:
+               writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL);
+               break;
+
+       case NAND_CTL_SETNCE: 
+               writeb(readb(FLASHCTL) & ~(FLCE0|FLCE1), FLASHCTL);
+               break;
+       case NAND_CTL_CLRNCE: 
+               writeb(readb(FLASHCTL) | (FLCE0|FLCE1), FLASHCTL);
+               break;
+       }
+}
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr sharpsl_bbt = {
+       .options = 0,
+       .offs = 4,
+       .len = 2,
+       .pattern = scan_ff_pattern
+};
+
+static int
+sharpsl_nand_dev_ready(struct mtd_info* mtd)
+{
+       return !((readb(FLASHCTL) & FLRYBY) == 0);
+}
+
+static void
+sharpsl_nand_enable_hwecc(struct mtd_info* mtd, int mode)
+{
+       writeb(0 ,ECCCLRR);
+}
+
+static int
+sharpsl_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat,
+                               u_char* ecc_code)
+{
+       ecc_code[0] = ~readb(ECCLPUB);
+       ecc_code[1] = ~readb(ECCLPLB);
+       ecc_code[2] = (~readb(ECCCP) << 2) | 0x03;
+       return readb(ECCCNTR) != 0;
+}
+
+
+#ifdef CONFIG_MTD_PARTITIONS
+const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+
+/*
+ * Main initialization routine
+ */
+int __init
+sharpsl_nand_init(void)
+{
+       struct nand_chip *this;
+       struct mtd_partition* sharpsl_partition_info;
+       int err = 0;
+
+       /* Allocate memory for MTD device structure and private data */
+       sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
+                               GFP_KERNEL);
+       if (!sharpsl_mtd) {
+               printk ("Unable to allocate SharpSL NAND MTD device structure.\n");
+               return -ENOMEM;
+       }
+       
+       /* map physical adress */
+       sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000);
+       if(!sharpsl_io_base){
+               printk("ioremap to access Sharp SL NAND chip failed\n");
+               kfree(sharpsl_mtd);
+               return -EIO;
+       }
+       
+       /* Get pointer to private data */
+       this = (struct nand_chip *) (&sharpsl_mtd[1]);
+
+       /* Initialize structures */
+       memset((char *) sharpsl_mtd, 0, sizeof(struct mtd_info));
+       memset((char *) this, 0, sizeof(struct nand_chip));
+
+       /* Link the private data with the MTD structure */
+       sharpsl_mtd->priv = this;
+
+       /*
+        * PXA initialize
+        */
+       writeb(readb(FLASHCTL) | FLWP, FLASHCTL);
+
+       /* Set address of NAND IO lines */
+       this->IO_ADDR_R = FLASHIO;
+       this->IO_ADDR_W = FLASHIO;
+       /* Set address of hardware control function */
+       this->hwcontrol = sharpsl_nand_hwcontrol;
+       this->dev_ready = sharpsl_nand_dev_ready;
+       /* 15 us command delay time */
+       this->chip_delay = 15;
+       /* set eccmode using hardware ECC */
+       this->eccmode = NAND_ECC_HW3_256;
+       this->enable_hwecc = sharpsl_nand_enable_hwecc;
+       this->calculate_ecc = sharpsl_nand_calculate_ecc;
+       this->correct_data = nand_correct_data;
+       this->badblock_pattern = &sharpsl_bbt;
+
+       /* Scan to find existence of the device */
+       err=nand_scan(sharpsl_mtd,1);
+       if (err) {
+               iounmap(sharpsl_io_base);
+               kfree(sharpsl_mtd);
+               return err;
+       }
+
+       /* Register the partitions */
+       sharpsl_mtd->name = "sharpsl-nand";
+       nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes,
+                                               &sharpsl_partition_info, 0);
+                                                
+       if (nr_partitions <= 0) {
+               nr_partitions = DEFAULT_NUM_PARTITIONS;
+               sharpsl_partition_info = sharpsl_nand_default_partition_info;
+               if (machine_is_poodle()) {
+                       sharpsl_partition_info[1].size=22 * 1024 * 1024;
+               } else if (machine_is_corgi() || machine_is_shepherd()) {
+                       sharpsl_partition_info[1].size=25 * 1024 * 1024;
+               } else if (machine_is_husky()) {
+                       sharpsl_partition_info[1].size=53 * 1024 * 1024;
+               }               
+       }
+
+       if (machine_is_husky()) {
+               /* Need to use small eraseblock size for backward compatibility */
+               sharpsl_mtd->flags |= MTD_NO_VIRTBLOCKS;
+       }
+
+       add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions);
+
+       /* Return happy */
+       return 0;
+}
+module_init(sharpsl_nand_init);
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit sharpsl_nand_cleanup(void)
+{
+       struct nand_chip *this = (struct nand_chip *) &sharpsl_mtd[1];
+
+       /* Release resources, unregister device */
+       nand_release(sharpsl_mtd);
+
+       iounmap(sharpsl_io_base);
+
+       /* Free the MTD device structure */
+       kfree(sharpsl_mtd);
+}
+module_exit(sharpsl_nand_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series");
diff --git a/drivers/net/arcnet/capmode.c b/drivers/net/arcnet/capmode.c
new file mode 100644 (file)
index 0000000..16e155b
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Linux ARCnet driver - "cap mode" packet encapsulation.
+ * It adds sequence numbers to packets for communicating between a user space
+ * application and the driver. After a transmit it sends a packet with protocol
+ * byte 0 back up to the userspace containing the sequence number of the packet
+ * plus the transmit-status on the ArcNet.
+ *
+ * Written 2002-4 by Esben Nielsen, Vestas Wind Systems A/S
+ * Derived from arc-rawmode.c by Avery Pennarun.
+ * arc-rawmode was in turned based on skeleton.c, see below.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.  This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/arcdevice.h>
+
+#define VERSION "arcnet: cap mode (`c') encapsulation support loaded.\n"
+
+
+static void rx(struct net_device *dev, int bufnum,
+              struct archdr *pkthdr, int length);
+static int build_header(struct sk_buff *skb,
+                       struct net_device *dev,
+                       unsigned short type,
+                       uint8_t daddr);
+static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
+                     int bufnum);
+static int ack_tx(struct net_device *dev, int acked);
+
+
+struct ArcProto capmode_proto =
+{
+       'r',
+       XMTU,
+       0,
+               rx,
+       build_header,
+       prepare_tx,
+       NULL,
+       ack_tx
+};
+
+
+void arcnet_cap_init(void)
+{
+       int count;
+
+       for (count = 1; count <= 8; count++)
+               if (arc_proto_map[count] == arc_proto_default)
+                       arc_proto_map[count] = &capmode_proto;
+
+       /* for cap mode, we only set the bcast proto if there's no better one */
+       if (arc_bcast_proto == arc_proto_default)
+               arc_bcast_proto = &capmode_proto;
+
+       arc_proto_default = &capmode_proto;
+       arc_raw_proto = &capmode_proto;
+}
+
+
+#ifdef MODULE
+
+int __init init_module(void)
+{
+       printk(VERSION);
+       arcnet_cap_init();
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       arcnet_unregister_proto(&capmode_proto);
+}
+
+MODULE_LICENSE("GPL");
+#endif                         /* MODULE */
+
+
+
+/* packet receiver */
+static void rx(struct net_device *dev, int bufnum,
+              struct archdr *pkthdr, int length)
+{
+       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct sk_buff *skb;
+       struct archdr *pkt = pkthdr;
+       char *pktbuf, *pkthdrbuf;
+       int ofs;
+
+       BUGMSG(D_DURING, "it's a raw(cap) packet (length=%d)\n", length);
+
+       if (length >= MinTU)
+               ofs = 512 - length;
+       else
+               ofs = 256 - length;
+
+       skb = alloc_skb(length + ARC_HDR_SIZE + sizeof(int), GFP_ATOMIC);
+       if (skb == NULL) {
+               BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
+               lp->stats.rx_dropped++;
+               return;
+       }
+       skb_put(skb, length + ARC_HDR_SIZE + sizeof(int));
+       skb->dev = dev;
+
+       pkt = (struct archdr *) skb->data;
+
+       skb->mac.raw = skb->data;
+       skb_pull(skb, ARC_HDR_SIZE);
+
+       /* up to sizeof(pkt->soft) has already been copied from the card */
+       /* squeeze in an int for the cap encapsulation */
+
+       /* use these variables to be sure we count in bytes, not in
+          sizeof(struct archdr) */
+       pktbuf=(char*)pkt;
+       pkthdrbuf=(char*)pkthdr;
+       memcpy(pktbuf, pkthdrbuf, ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto));
+       memcpy(pktbuf+ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto)+sizeof(int),
+              pkthdrbuf+ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto),
+              sizeof(struct archdr)-ARC_HDR_SIZE-sizeof(pkt->soft.cap.proto));
+
+       if (length > sizeof(pkt->soft))
+               lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
+                                     pkt->soft.raw + sizeof(pkt->soft)
+                                     + sizeof(int),
+                                     length - sizeof(pkt->soft));
+
+       BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+
+       skb->protocol = __constant_htons(ETH_P_ARCNET);
+;
+       netif_rx(skb);
+       dev->last_rx = jiffies;
+}
+
+
+/*
+ * Create the ARCnet hard/soft headers for cap mode.
+ * There aren't any soft headers in cap mode - not even the protocol id.
+ */
+static int build_header(struct sk_buff *skb,
+                       struct net_device *dev,
+                       unsigned short type,
+                       uint8_t daddr)
+{
+       int hdr_size = ARC_HDR_SIZE;
+       struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
+
+       BUGMSG(D_PROTO, "Preparing header for cap packet %x.\n",
+              *((int*)&pkt->soft.cap.cookie[0]));
+       /*
+        * Set the source hardware address.
+        *
+        * This is pretty pointless for most purposes, but it can help in
+        * debugging.  ARCnet does not allow us to change the source address in
+        * the actual packet sent)
+        */
+       pkt->hard.source = *dev->dev_addr;
+
+       /* see linux/net/ethernet/eth.c to see where I got the following */
+
+       if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+               /*
+                * FIXME: fill in the last byte of the dest ipaddr here to better
+                * comply with RFC1051 in "noarp" mode.
+                */
+               pkt->hard.dest = 0;
+               return hdr_size;
+       }
+       /* otherwise, just fill it in and go! */
+       pkt->hard.dest = daddr;
+
+       return hdr_size;        /* success */
+}
+
+
+static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
+                     int bufnum)
+{
+       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arc_hardware *hard = &pkt->hard;
+       int ofs;
+
+
+       /* hard header is not included in packet length */
+       length -= ARC_HDR_SIZE;
+       /* And neither is the cookie field */
+       length -= sizeof(int);
+
+       BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
+              lp->next_tx, lp->cur_tx, bufnum);
+
+       BUGMSG(D_PROTO, "Sending for cap packet %x.\n",
+              *((int*)&pkt->soft.cap.cookie[0]));
+
+       if (length > XMTU) {
+               /* should never happen! other people already check for this. */
+               BUGMSG(D_NORMAL, "Bug!  prepare_tx with size %d (> %d)\n",
+                      length, XMTU);
+               length = XMTU;
+       }
+       if (length > MinTU) {
+               hard->offset[0] = 0;
+               hard->offset[1] = ofs = 512 - length;
+       } else if (length > MTU) {
+               hard->offset[0] = 0;
+               hard->offset[1] = ofs = 512 - length - 3;
+       } else
+               hard->offset[0] = ofs = 256 - length;
+
+       BUGMSG(D_DURING, "prepare_tx: length=%d ofs=%d\n",
+              length,ofs);
+
+       // Copy the arcnet-header + the protocol byte down:
+       lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
+       lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft.cap.proto,
+                           sizeof(pkt->soft.cap.proto));
+
+       // Skip the extra integer we have written into it as a cookie
+       // but write the rest of the message:
+       lp->hw.copy_to_card(dev, bufnum, ofs+1,
+                           ((unsigned char*)&pkt->soft.cap.mes),length-1);
+
+       lp->lastload_dest = hard->dest;
+
+       return 1;               /* done */
+}
+
+
+static int ack_tx(struct net_device *dev, int acked)
+{
+  struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+  struct sk_buff *ackskb;
+  struct archdr *ackpkt;
+  int length=sizeof(struct arc_cap);
+
+  BUGMSG(D_DURING, "capmode: ack_tx: protocol: %x: result: %d\n",
+        lp->outgoing.skb->protocol, acked);
+
+  BUGLVL(D_SKB) arcnet_dump_skb(dev, lp->outgoing.skb, "ack_tx");
+
+  /* Now alloc a skb to send back up through the layers: */
+  ackskb = alloc_skb(length + ARC_HDR_SIZE , GFP_ATOMIC);
+  if (ackskb == NULL) {
+         BUGMSG(D_NORMAL, "Memory squeeze, can't acknowledge.\n");
+         goto free_outskb;
+  }
+
+  skb_put(ackskb, length + ARC_HDR_SIZE );
+  ackskb->dev = dev;
+
+  ackpkt = (struct archdr *) ackskb->data;
+
+  ackskb->mac.raw = ackskb->data;
+  /* skb_pull(ackskb, ARC_HDR_SIZE); */
+
+
+  memcpy(ackpkt, lp->outgoing.skb->data, ARC_HDR_SIZE+sizeof(struct arc_cap));
+  ackpkt->soft.cap.proto=0; /* using protocol 0 for acknowledge */
+  ackpkt->soft.cap.mes.ack=acked;
+
+  BUGMSG(D_PROTO, "Ackknowledge for cap packet %x.\n",
+        *((int*)&ackpkt->soft.cap.cookie[0]));
+
+  ackskb->protocol = __constant_htons(ETH_P_ARCNET);
+
+  BUGLVL(D_SKB) arcnet_dump_skb(dev, ackskb, "ack_tx_recv");
+  netif_rx(ackskb);
+
+ free_outskb:
+  dev_kfree_skb_irq(lp->outgoing.skb);
+  lp->outgoing.proto = NULL; /* We are always finished when in this protocol */
+
+  return 0;
+}
diff --git a/drivers/net/cris/Makefile b/drivers/net/cris/Makefile
new file mode 100644 (file)
index 0000000..b4e8932
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_ETRAX_ARCH_V10) += eth_v10.o
diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c
new file mode 100644 (file)
index 0000000..4426708
--- /dev/null
@@ -0,0 +1,1836 @@
+/* $Id: ethernet.c,v 1.31 2004/10/18 14:49:03 starvik Exp $
+ *
+ * e100net.c: A network driver for the ETRAX 100LX network controller.
+ *
+ * Copyright (c) 1998-2002 Axis Communications AB.
+ *
+ * The outline of this driver comes from skeleton.c.
+ *
+ * $Log: ethernet.c,v $
+ * Revision 1.31  2004/10/18 14:49:03  starvik
+ * Use RX interrupt as random source
+ *
+ * Revision 1.30  2004/09/29 10:44:04  starvik
+ * Enabed MAC-address output again
+ *
+ * Revision 1.29  2004/08/24 07:14:05  starvik
+ * Make use of generic MDIO interface and constants.
+ *
+ * Revision 1.28  2004/08/20 09:37:11  starvik
+ * Added support for Intel LXT972A. Creds to Randy Scarborough.
+ *
+ * Revision 1.27  2004/08/16 12:37:22  starvik
+ * Merge of Linux 2.6.8
+ *
+ * Revision 1.25  2004/06/21 10:29:57  starvik
+ * Merge of Linux 2.6.7
+ *
+ * Revision 1.23  2004/06/09 05:29:22  starvik
+ * Avoid any race where R_DMA_CH1_FIRST is NULL (may trigger cache bug).
+ *
+ * Revision 1.22  2004/05/14 07:58:03  starvik
+ * Merge of changes from 2.4
+ *
+ * Revision 1.20  2004/03/11 11:38:40  starvik
+ * Merge of Linux 2.6.4
+ *
+ * Revision 1.18  2003/12/03 13:45:46  starvik
+ * Use hardware pad for short packets to prevent information leakage.
+ *
+ * Revision 1.17  2003/07/04 08:27:37  starvik
+ * Merge of Linux 2.5.74
+ *
+ * Revision 1.16  2003/04/24 08:28:22  starvik
+ * New LED behaviour: LED off when no link
+ *
+ * Revision 1.15  2003/04/09 05:20:47  starvik
+ * Merge of Linux 2.5.67
+ *
+ * Revision 1.13  2003/03/06 16:11:01  henriken
+ * Off by one error in group address register setting.
+ *
+ * Revision 1.12  2003/02/27 17:24:19  starvik
+ * Corrected Rev to Revision
+ *
+ * Revision 1.11  2003/01/24 09:53:21  starvik
+ * Oops. Initialize GA to 0, not to 1
+ *
+ * Revision 1.10  2003/01/24 09:50:55  starvik
+ * Initialize GA_0 and GA_1 to 0 to avoid matching of unwanted packets
+ *
+ * Revision 1.9  2002/12/13 07:40:58  starvik
+ * Added basic ethtool interface
+ * Handled out of memory when allocating new buffers
+ *
+ * Revision 1.8  2002/12/11 13:13:57  starvik
+ * Added arch/ to v10 specific includes
+ * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer)
+ *
+ * Revision 1.7  2002/11/26 09:41:42  starvik
+ * Added e100_set_config (standard interface to set media type)
+ * Added protection against preemptive scheduling
+ * Added standard MII ioctls
+ *
+ * Revision 1.6  2002/11/21 07:18:18  starvik
+ * Timers must be initialized in 2.5.48
+ *
+ * Revision 1.5  2002/11/20 11:56:11  starvik
+ * Merge of Linux 2.5.48
+ *
+ * Revision 1.4  2002/11/18 07:26:46  starvik
+ * Linux 2.5 port of latest Linux 2.4 ethernet driver
+ *
+ * Revision 1.33  2002/10/02 20:16:17  hp
+ * SETF, SETS: Use underscored IO_x_ macros rather than incorrect token concatenation
+ *
+ * Revision 1.32  2002/09/16 06:05:58  starvik
+ * Align memory returned by dev_alloc_skb
+ * Moved handling of sent packets to interrupt to avoid reference counting problem
+ *
+ * Revision 1.31  2002/09/10 13:28:23  larsv
+ * Return -EINVAL for unknown ioctls to avoid confusing tools that tests
+ * for supported functionality by issuing special ioctls, i.e. wireless
+ * extensions.
+ *
+ * Revision 1.30  2002/05/07 18:50:08  johana
+ * Correct spelling in comments.
+ *
+ * Revision 1.29  2002/05/06 05:38:49  starvik
+ * Performance improvements:
+ *    Large packets are not copied (breakpoint set to 256 bytes)
+ *    The cache bug workaround is delayed until half of the receive list
+ *      has been used
+ *    Added transmit list
+ *    Transmit interrupts are only enabled when transmit queue is full
+ *
+ * Revision 1.28.2.1  2002/04/30 08:15:51  starvik
+ * Performance improvements:
+ *   Large packets are not copied (breakpoint set to 256 bytes)
+ *   The cache bug workaround is delayed until half of the receive list
+ *     has been used.
+ *   Added transmit list
+ *   Transmit interrupts are only enabled when transmit queue is full
+ *
+ * Revision 1.28  2002/04/22 11:47:21  johana
+ * Fix according to 2.4.19-pre7. time_after/time_before and
+ * missing end of comment.
+ * The patch has a typo for ethernet.c in e100_clear_network_leds(),
+ *  that is fixed here.
+ *
+ * Revision 1.27  2002/04/12 11:55:11  bjornw
+ * Added TODO
+ *
+ * Revision 1.26  2002/03/15 17:11:02  bjornw
+ * Use prepare_rx_descriptor after the CPU has touched the receiving descs
+ *
+ * Revision 1.25  2002/03/08 13:07:53  bjornw
+ * Unnecessary spinlock removed
+ *
+ * Revision 1.24  2002/02/20 12:57:43  fredriks
+ * Replaced MIN() with min().
+ *
+ * Revision 1.23  2002/02/20 10:58:14  fredriks
+ * Strip the Ethernet checksum (4 bytes) before forwarding a frame to upper layers.
+ *
+ * Revision 1.22  2002/01/30 07:48:22  matsfg
+ * Initiate R_NETWORK_TR_CTRL
+ *
+ * Revision 1.21  2001/11/23 11:54:49  starvik
+ * Added IFF_PROMISC and IFF_ALLMULTI handling in set_multicast_list
+ * Removed compiler warnings
+ *
+ * Revision 1.20  2001/11/12 19:26:00  pkj
+ * * Corrected e100_negotiate() to not assign half to current_duplex when
+ *   it was supposed to compare them...
+ * * Cleaned up failure handling in e100_open().
+ * * Fixed compiler warnings.
+ *
+ * Revision 1.19  2001/11/09 07:43:09  starvik
+ * Added full duplex support
+ * Added ioctl to set speed and duplex
+ * Clear LED timer only runs when LED is lit
+ *
+ * Revision 1.18  2001/10/03 14:40:43  jonashg
+ * Update rx_bytes counter.
+ *
+ * Revision 1.17  2001/06/11 12:43:46  olof
+ * Modified defines for network LED behavior
+ *
+ * Revision 1.16  2001/05/30 06:12:46  markusl
+ * TxDesc.next should not be set to NULL
+ *
+ * Revision 1.15  2001/05/29 10:27:04  markusl
+ * Updated after review remarks:
+ * +Use IO_EXTRACT
+ * +Handle underrun
+ *
+ * Revision 1.14  2001/05/29 09:20:14  jonashg
+ * Use driver name on printk output so one can tell which driver that complains.
+ *
+ * Revision 1.13  2001/05/09 12:35:59  johana
+ * Use DMA_NBR and IRQ_NBR defines from dma.h and irq.h
+ *
+ * Revision 1.12  2001/04/05 11:43:11  tobiasa
+ * Check dev before panic.
+ *
+ * Revision 1.11  2001/04/04 11:21:05  markusl
+ * Updated according to review remarks
+ *
+ * Revision 1.10  2001/03/26 16:03:06  bjornw
+ * Needs linux/config.h
+ *
+ * Revision 1.9  2001/03/19 14:47:48  pkj
+ * * Make sure there is always a pause after the network LEDs are
+ *   changed so they will not look constantly lit during heavy traffic.
+ * * Always use HZ when setting times relative to jiffies.
+ * * Use LED_NETWORK_SET() when setting the network LEDs.
+ *
+ * Revision 1.8  2001/02/27 13:52:48  bjornw
+ * malloc.h -> slab.h
+ *
+ * Revision 1.7  2001/02/23 13:46:38  bjornw
+ * Spellling check
+ *
+ * Revision 1.6  2001/01/26 15:21:04  starvik
+ * Don't disable interrupts while reading MDIO registers (MDIO is slow)
+ * Corrected promiscuous mode
+ * Improved deallocation of IRQs ("ifconfig eth0 down" now works)
+ *
+ * Revision 1.5  2000/11/29 17:22:22  bjornw
+ * Get rid of the udword types legacy stuff
+ *
+ * Revision 1.4  2000/11/22 16:36:09  bjornw
+ * Please marketing by using the correct case when spelling Etrax.
+ *
+ * Revision 1.3  2000/11/21 16:43:04  bjornw
+ * Minor short->int change
+ *
+ * Revision 1.2  2000/11/08 14:27:57  bjornw
+ * 2.4 port
+ *
+ * Revision 1.1  2000/11/06 13:56:00  bjornw
+ * Verbatim copy of the 1.24 version of e100net.c from elinux
+ *
+ * Revision 1.24  2000/10/04 15:55:23  bjornw
+ * * Use virt_to_phys etc. for DMA addresses
+ * * Removed bogus CHECKSUM_UNNECESSARY
+ *
+ *
+ */
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/if.h>
+#include <linux/mii.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+
+#include <asm/arch/svinto.h>/* DMA and register descriptions */
+#include <asm/io.h>         /* LED_* I/O functions */
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/ethernet.h>
+#include <asm/cache.h>
+
+//#define ETHDEBUG
+#define D(x)
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static const char* cardname = "ETRAX 100LX built-in ethernet controller";
+
+/* A default ethernet address. Highlevel SW will set the real one later */
+
+static struct sockaddr default_mac = {
+       0,
+       { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
+};
+
+/* Information that need to be kept for each board. */
+struct net_local {
+       struct net_device_stats stats;
+       struct mii_if_info mii_if;
+
+       /* Tx control lock.  This protects the transmit buffer ring
+        * state along with the "tx full" state of the driver.  This
+        * means all netif_queue flow control actions are protected
+        * by this lock as well.
+        */
+       spinlock_t lock;
+};
+
+typedef struct etrax_eth_descr
+{
+       etrax_dma_descr descr;
+       struct sk_buff* skb;
+} etrax_eth_descr;
+
+/* Some transceivers requires special handling */
+struct transceiver_ops
+{
+       unsigned int oui;
+       void (*check_speed)(struct net_device* dev);
+       void (*check_duplex)(struct net_device* dev);
+};
+
+struct transceiver_ops* transceiver;
+
+/* Duplex settings */
+enum duplex
+{
+       half,
+       full,
+       autoneg
+};
+
+/* Dma descriptors etc. */
+
+#define MAX_MEDIA_DATA_SIZE 1518
+
+#define MIN_PACKET_LEN      46
+#define ETHER_HEAD_LEN      14
+
+/*
+** MDIO constants.
+*/
+#define MDIO_START                          0x1
+#define MDIO_READ                           0x2
+#define MDIO_WRITE                          0x1
+#define MDIO_PREAMBLE              0xfffffffful
+
+/* Broadcom specific */
+#define MDIO_AUX_CTRL_STATUS_REG           0x18
+#define MDIO_BC_FULL_DUPLEX_IND             0x1
+#define MDIO_BC_SPEED                       0x2
+
+/* TDK specific */
+#define MDIO_TDK_DIAGNOSTIC_REG              18
+#define MDIO_TDK_DIAGNOSTIC_RATE          0x400
+#define MDIO_TDK_DIAGNOSTIC_DPLX          0x800
+
+/*Intel LXT972A specific*/
+#define MDIO_INT_STATUS_REG_2                  0x0011
+#define MDIO_INT_FULL_DUPLEX_IND               ( 1 << 9 )
+#define MDIO_INT_SPEED                         ( 1 << 14 )
+
+/* Network flash constants */
+#define NET_FLASH_TIME                  (HZ/50) /* 20 ms */
+#define NET_FLASH_PAUSE                (HZ/100) /* 10 ms */
+#define NET_LINK_UP_CHECK_INTERVAL       (2*HZ) /* 2 s   */
+#define NET_DUPLEX_CHECK_INTERVAL        (2*HZ) /* 2 s   */
+
+#define NO_NETWORK_ACTIVITY 0
+#define NETWORK_ACTIVITY    1
+
+#define NBR_OF_RX_DESC     64
+#define NBR_OF_TX_DESC     256
+
+/* Large packets are sent directly to upper layers while small packets are */
+/* copied (to reduce memory waste). The following constant decides the breakpoint */
+#define RX_COPYBREAK 256
+
+/* Due to a chip bug we need to flush the cache when descriptors are returned */
+/* to the DMA. To decrease performance impact we return descriptors in chunks. */
+/* The following constant determines the number of descriptors to return. */
+#define RX_QUEUE_THRESHOLD  NBR_OF_RX_DESC/2
+
+#define GET_BIT(bit,val)   (((val) >> (bit)) & 0x01)
+
+/* Define some macros to access ETRAX 100 registers */
+#define SETF(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
+                                         IO_FIELD_(reg##_, field##_, val)
+#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
+                                         IO_STATE_(reg##_, field##_, _##val)
+
+static etrax_eth_descr *myNextRxDesc;  /* Points to the next descriptor to
+                                          to be processed */
+static etrax_eth_descr *myLastRxDesc;  /* The last processed descriptor */
+static etrax_eth_descr *myPrevRxDesc;  /* The descriptor right before myNextRxDesc */
+
+static etrax_eth_descr RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned(32)));
+
+static etrax_eth_descr* myFirstTxDesc; /* First packet not yet sent */
+static etrax_eth_descr* myLastTxDesc;  /* End of send queue */
+static etrax_eth_descr* myNextTxDesc;  /* Next descriptor to use */
+static etrax_eth_descr TxDescList[NBR_OF_TX_DESC] __attribute__ ((aligned(32)));
+
+static unsigned int network_rec_config_shadow = 0;
+static unsigned int mdio_phy_addr; /* Transciever address */
+
+static unsigned int network_tr_ctrl_shadow = 0;
+
+/* Network speed indication. */
+static struct timer_list speed_timer = TIMER_INITIALIZER(NULL, 0, 0);
+static struct timer_list clear_led_timer = TIMER_INITIALIZER(NULL, 0, 0);
+static int current_speed; /* Speed read from transceiver */
+static int current_speed_selection; /* Speed selected by user */
+static unsigned long led_next_time;
+static int led_active;
+static int rx_queue_len;
+
+/* Duplex */
+static struct timer_list duplex_timer = TIMER_INITIALIZER(NULL, 0, 0);
+static int full_duplex;
+static enum duplex current_duplex;
+
+/* Index to functions, as function prototypes. */
+
+static int etrax_ethernet_init(void);
+
+static int e100_open(struct net_device *dev);
+static int e100_set_mac_address(struct net_device *dev, void *addr);
+static int e100_send_packet(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t e100rxtx_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void e100_rx(struct net_device *dev);
+static int e100_close(struct net_device *dev);
+static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static int e100_ethtool_ioctl(struct net_device* dev, struct ifreq *ifr);
+static int e100_set_config(struct net_device* dev, struct ifmap* map);
+static void e100_tx_timeout(struct net_device *dev);
+static struct net_device_stats *e100_get_stats(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void e100_hardware_send_packet(char *buf, int length);
+static void update_rx_stats(struct net_device_stats *);
+static void update_tx_stats(struct net_device_stats *);
+static int e100_probe_transceiver(struct net_device* dev);
+
+static void e100_check_speed(unsigned long priv);
+static void e100_set_speed(struct net_device* dev, unsigned long speed);
+static void e100_check_duplex(unsigned long priv);
+static void e100_set_duplex(struct net_device* dev, enum duplex);
+static void e100_negotiate(struct net_device* dev);
+
+static int e100_get_mdio_reg(struct net_device *dev, int phy_id, int location);
+static void e100_set_mdio_reg(struct net_device *dev, int phy_id, int location, int value);
+
+static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd);
+static void e100_send_mdio_bit(unsigned char bit);
+static unsigned char e100_receive_mdio_bit(void);
+static void e100_reset_transceiver(struct net_device* net);
+
+static void e100_clear_network_leds(unsigned long dummy);
+static void e100_set_network_leds(int active);
+
+static void broadcom_check_speed(struct net_device* dev);
+static void broadcom_check_duplex(struct net_device* dev);
+static void tdk_check_speed(struct net_device* dev);
+static void tdk_check_duplex(struct net_device* dev);
+static void intel_check_speed(struct net_device* dev);
+static void intel_check_duplex(struct net_device* dev);
+static void generic_check_speed(struct net_device* dev);
+static void generic_check_duplex(struct net_device* dev);
+
+struct transceiver_ops transceivers[] =
+{
+       {0x1018, broadcom_check_speed, broadcom_check_duplex},  /* Broadcom */
+       {0xC039, tdk_check_speed, tdk_check_duplex},            /* TDK 2120 */
+       {0x039C, tdk_check_speed, tdk_check_duplex},            /* TDK 2120C */
+        {0x04de, intel_check_speed, intel_check_duplex},       /* Intel LXT972A*/
+       {0x0000, generic_check_speed, generic_check_duplex}     /* Generic, must be last */
+};
+
+#define tx_done(dev) (*R_DMA_CH0_CMD == 0)
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+
+static int __init
+etrax_ethernet_init(void)
+{
+       struct net_device *dev;
+        struct net_local* np;
+       int i, err;
+
+       printk(KERN_INFO
+              "ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2003 Axis Communications AB\n");
+
+       dev = alloc_etherdev(sizeof(struct net_local));
+       np = dev->priv;
+
+       if (!dev)
+               return -ENOMEM;
+
+       dev->base_addr = (unsigned int)R_NETWORK_SA_0; /* just to have something to show */
+
+       /* now setup our etrax specific stuff */
+
+       dev->irq = NETWORK_DMA_RX_IRQ_NBR; /* we really use DMATX as well... */
+       dev->dma = NETWORK_RX_DMA_NBR;
+
+       /* fill in our handlers so the network layer can talk to us in the future */
+
+       dev->open               = e100_open;
+       dev->hard_start_xmit    = e100_send_packet;
+       dev->stop               = e100_close;
+       dev->get_stats          = e100_get_stats;
+       dev->set_multicast_list = set_multicast_list;
+       dev->set_mac_address    = e100_set_mac_address;
+       dev->do_ioctl           = e100_ioctl;
+       dev->set_config         = e100_set_config;
+       dev->tx_timeout         = e100_tx_timeout;
+
+       /* Initialise the list of Etrax DMA-descriptors */
+
+       /* Initialise receive descriptors */
+
+       for (i = 0; i < NBR_OF_RX_DESC; i++) {
+               /* Allocate two extra cachelines to make sure that buffer used by DMA
+                * does not share cacheline with any other data (to avoid cache bug)
+                */
+               RxDescList[i].skb = dev_alloc_skb(MAX_MEDIA_DATA_SIZE + 2 * L1_CACHE_BYTES);
+               RxDescList[i].descr.ctrl   = 0;
+               RxDescList[i].descr.sw_len = MAX_MEDIA_DATA_SIZE;
+               RxDescList[i].descr.next   = virt_to_phys(&RxDescList[i + 1]);
+               RxDescList[i].descr.buf    = L1_CACHE_ALIGN(virt_to_phys(RxDescList[i].skb->data));
+               RxDescList[i].descr.status = 0;
+               RxDescList[i].descr.hw_len = 0;
+               prepare_rx_descriptor(&RxDescList[i].descr);
+       }
+
+       RxDescList[NBR_OF_RX_DESC - 1].descr.ctrl   = d_eol;
+       RxDescList[NBR_OF_RX_DESC - 1].descr.next   = virt_to_phys(&RxDescList[0]);
+       rx_queue_len = 0;
+
+       /* Initialize transmit descriptors */
+       for (i = 0; i < NBR_OF_TX_DESC; i++) {
+               TxDescList[i].descr.ctrl   = 0;
+               TxDescList[i].descr.sw_len = 0;
+               TxDescList[i].descr.next   = virt_to_phys(&TxDescList[i + 1].descr);
+               TxDescList[i].descr.buf    = 0;
+               TxDescList[i].descr.status = 0;
+               TxDescList[i].descr.hw_len = 0;
+               TxDescList[i].skb = 0;
+       }
+
+       TxDescList[NBR_OF_TX_DESC - 1].descr.ctrl   = d_eol;
+       TxDescList[NBR_OF_TX_DESC - 1].descr.next   = virt_to_phys(&TxDescList[0].descr);
+
+       /* Initialise initial pointers */
+
+       myNextRxDesc  = &RxDescList[0];
+       myLastRxDesc  = &RxDescList[NBR_OF_RX_DESC - 1];
+       myPrevRxDesc  = &RxDescList[NBR_OF_RX_DESC - 1];
+       myFirstTxDesc = &TxDescList[0];
+       myNextTxDesc  = &TxDescList[0];
+       myLastTxDesc  = &TxDescList[NBR_OF_TX_DESC - 1];
+
+       /* Register device */
+       err = register_netdev(dev);
+       if (err) {
+               free_netdev(dev);
+               return err;
+       }
+
+       /* set the default MAC address */
+
+       e100_set_mac_address(dev, &default_mac);
+
+       /* Initialize speed indicator stuff. */
+
+       current_speed = 10;
+       current_speed_selection = 0; /* Auto */
+       speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
+        duplex_timer.data = (unsigned long)dev;
+       speed_timer.function = e100_check_speed;
+
+       clear_led_timer.function = e100_clear_network_leds;
+
+       full_duplex = 0;
+       current_duplex = autoneg;
+       duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
+        duplex_timer.data = (unsigned long)dev;
+       duplex_timer.function = e100_check_duplex;
+
+        /* Initialize mii interface */
+       np->mii_if.phy_id = mdio_phy_addr;
+       np->mii_if.phy_id_mask = 0x1f;
+       np->mii_if.reg_num_mask = 0x1f;
+       np->mii_if.dev = dev;
+       np->mii_if.mdio_read = e100_get_mdio_reg;
+       np->mii_if.mdio_write = e100_set_mdio_reg;
+
+       /* Initialize group address registers to make sure that no */
+       /* unwanted addresses are matched */
+       *R_NETWORK_GA_0 = 0x00000000;
+       *R_NETWORK_GA_1 = 0x00000000;
+       return 0;
+}
+
+/* set MAC address of the interface. called from the core after a
+ * SIOCSIFADDR ioctl, and from the bootup above.
+ */
+
+static int
+e100_set_mac_address(struct net_device *dev, void *p)
+{
+       struct net_local *np = (struct net_local *)dev->priv;
+       struct sockaddr *addr = p;
+       int i;
+
+       spin_lock(&np->lock); /* preemption protection */
+
+       /* remember it */
+
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+       /* Write it to the hardware.
+        * Note the way the address is wrapped:
+        * *R_NETWORK_SA_0 = a0_0 | (a0_1 << 8) | (a0_2 << 16) | (a0_3 << 24);
+        * *R_NETWORK_SA_1 = a0_4 | (a0_5 << 8);
+        */
+
+       *R_NETWORK_SA_0 = dev->dev_addr[0] | (dev->dev_addr[1] << 8) |
+               (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24);
+       *R_NETWORK_SA_1 = dev->dev_addr[4] | (dev->dev_addr[5] << 8);
+       *R_NETWORK_SA_2 = 0;
+
+       /* show it in the log as well */
+
+       printk(KERN_INFO "%s: changed MAC to ", dev->name);
+
+       for (i = 0; i < 5; i++)
+               printk("%02X:", dev->dev_addr[i]);
+
+       printk("%02X\n", dev->dev_addr[i]);
+
+       spin_unlock(&np->lock);
+
+       return 0;
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int
+e100_open(struct net_device *dev)
+{
+       unsigned long flags;
+
+       /* enable the MDIO output pin */
+
+       *R_NETWORK_MGM_CTRL = IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable);
+
+       *R_IRQ_MASK0_CLR =
+               IO_STATE(R_IRQ_MASK0_CLR, overrun, clr) |
+               IO_STATE(R_IRQ_MASK0_CLR, underrun, clr) |
+               IO_STATE(R_IRQ_MASK0_CLR, excessive_col, clr);
+
+       /* clear dma0 and 1 eop and descr irq masks */
+       *R_IRQ_MASK2_CLR =
+               IO_STATE(R_IRQ_MASK2_CLR, dma0_descr, clr) |
+               IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) |
+               IO_STATE(R_IRQ_MASK2_CLR, dma1_descr, clr) |
+               IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr);
+
+       /* Reset and wait for the DMA channels */
+
+       RESET_DMA(NETWORK_TX_DMA_NBR);
+       RESET_DMA(NETWORK_RX_DMA_NBR);
+       WAIT_DMA(NETWORK_TX_DMA_NBR);
+       WAIT_DMA(NETWORK_RX_DMA_NBR);
+
+       /* Initialise the etrax network controller */
+
+       /* allocate the irq corresponding to the receiving DMA */
+
+       if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rxtx_interrupt,
+                       SA_SAMPLE_RANDOM, cardname, (void *)dev)) {
+               goto grace_exit0;
+       }
+
+       /* allocate the irq corresponding to the transmitting DMA */
+
+       if (request_irq(NETWORK_DMA_TX_IRQ_NBR, e100rxtx_interrupt, 0,
+                       cardname, (void *)dev)) {
+               goto grace_exit1;
+       }
+
+       /* allocate the irq corresponding to the network errors etc */
+
+       if (request_irq(NETWORK_STATUS_IRQ_NBR, e100nw_interrupt, 0,
+                       cardname, (void *)dev)) {
+               goto grace_exit2;
+       }
+
+       /* give the HW an idea of what MAC address we want */
+
+       *R_NETWORK_SA_0 = dev->dev_addr[0] | (dev->dev_addr[1] << 8) |
+               (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24);
+       *R_NETWORK_SA_1 = dev->dev_addr[4] | (dev->dev_addr[5] << 8);
+       *R_NETWORK_SA_2 = 0;
+
+#if 0
+       /* use promiscuous mode for testing */
+       *R_NETWORK_GA_0 = 0xffffffff;
+       *R_NETWORK_GA_1 = 0xffffffff;
+
+       *R_NETWORK_REC_CONFIG = 0xd; /* broadcast rec, individ. rec, ma0 enabled */
+#else
+       SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, broadcast, receive);
+       SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, ma0, enable);
+       SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+       *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+#endif
+
+       *R_NETWORK_GEN_CONFIG =
+               IO_STATE(R_NETWORK_GEN_CONFIG, phy,    mii_clk) |
+               IO_STATE(R_NETWORK_GEN_CONFIG, enable, on);
+
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, delay, none);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, cancel, dont);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, cd, enable);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, retry, enable);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, pad, enable);
+       SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, crc, enable);
+       *R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
+
+       save_flags(flags);
+       cli();
+
+       /* enable the irq's for ethernet DMA */
+
+       *R_IRQ_MASK2_SET =
+               IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) |
+               IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set);
+
+       *R_IRQ_MASK0_SET =
+               IO_STATE(R_IRQ_MASK0_SET, overrun,       set) |
+               IO_STATE(R_IRQ_MASK0_SET, underrun,      set) |
+               IO_STATE(R_IRQ_MASK0_SET, excessive_col, set);
+
+       /* make sure the irqs are cleared */
+
+       *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
+       *R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do);
+
+       /* make sure the rec and transmit error counters are cleared */
+
+       (void)*R_REC_COUNTERS;  /* dummy read */
+       (void)*R_TR_COUNTERS;   /* dummy read */
+
+       /* start the receiving DMA channel so we can receive packets from now on */
+
+       *R_DMA_CH1_FIRST = virt_to_phys(myNextRxDesc);
+       *R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, start);
+
+       /* Set up transmit DMA channel so it can be restarted later */
+
+       *R_DMA_CH0_FIRST = 0;
+       *R_DMA_CH0_DESCR = virt_to_phys(myLastTxDesc);
+
+       restore_flags(flags);
+
+       /* Probe for transceiver */
+       if (e100_probe_transceiver(dev))
+               goto grace_exit3;
+
+       /* Start duplex/speed timers */
+       add_timer(&speed_timer);
+       add_timer(&duplex_timer);
+
+       /* We are now ready to accept transmit requeusts from
+        * the queueing layer of the networking.
+        */
+       netif_start_queue(dev);
+
+       return 0;
+
+grace_exit3:
+       free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
+grace_exit2:
+       free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
+grace_exit1:
+       free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
+grace_exit0:
+       return -EAGAIN;
+}
+
+
+static void
+generic_check_speed(struct net_device* dev)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE);
+       if ((data & ADVERTISE_100FULL) ||
+           (data & ADVERTISE_100HALF))
+               current_speed = 100;
+       else
+               current_speed = 10;
+}
+
+static void
+tdk_check_speed(struct net_device* dev)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_TDK_DIAGNOSTIC_REG);
+       current_speed = (data & MDIO_TDK_DIAGNOSTIC_RATE ? 100 : 10);
+}
+
+static void
+broadcom_check_speed(struct net_device* dev)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_AUX_CTRL_STATUS_REG);
+       current_speed = (data & MDIO_BC_SPEED ? 100 : 10);
+}
+
+static void
+intel_check_speed(struct net_device* dev)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_INT_STATUS_REG_2);
+       current_speed = (data & MDIO_INT_SPEED ? 100 : 10);
+}
+
+static void
+e100_check_speed(unsigned long priv)
+{
+       struct net_device* dev = (struct net_device*)priv;
+       static int led_initiated = 0;
+       unsigned long data;
+       int old_speed = current_speed;
+
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMSR);
+       if (!(data & BMSR_LSTATUS)) {
+               current_speed = 0;
+       } else {
+               transceiver->check_speed(dev);
+       }
+
+       if ((old_speed != current_speed) || !led_initiated) {
+               led_initiated = 1;
+               e100_set_network_leds(NO_NETWORK_ACTIVITY);
+       }
+
+       /* Reinitialize the timer. */
+       speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
+       add_timer(&speed_timer);
+}
+
+static void
+e100_negotiate(struct net_device* dev)
+{
+       unsigned short data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE);
+
+       /* Discard old speed and duplex settings */
+       data &= ~(ADVERTISE_100HALF | ADVERTISE_100FULL |
+                 ADVERTISE_10HALF | ADVERTISE_10FULL);
+
+       switch (current_speed_selection) {
+               case 10 :
+                       if (current_duplex == full)
+                               data |= ADVERTISE_10FULL;
+                       else if (current_duplex == half)
+                               data |= ADVERTISE_10HALF;
+                       else
+                               data |= ADVERTISE_10HALF | ADVERTISE_10FULL;
+                       break;
+
+               case 100 :
+                        if (current_duplex == full)
+                               data |= ADVERTISE_100FULL;
+                       else if (current_duplex == half)
+                               data |= ADVERTISE_100HALF;
+                       else
+                               data |= ADVERTISE_100HALF | ADVERTISE_100FULL;
+                       break;
+
+               case 0 : /* Auto */
+                        if (current_duplex == full)
+                               data |= ADVERTISE_100FULL | ADVERTISE_10FULL;
+                       else if (current_duplex == half)
+                               data |= ADVERTISE_100HALF | ADVERTISE_10HALF;
+                       else
+                               data |= ADVERTISE_10HALF | ADVERTISE_10FULL |
+                                 ADVERTISE_100HALF | ADVERTISE_100FULL;
+                       break;
+
+               default : /* assume autoneg speed and duplex */
+                       data |= ADVERTISE_10HALF | ADVERTISE_10FULL |
+                                 ADVERTISE_100HALF | ADVERTISE_100FULL;
+       }
+
+       e100_set_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE, data);
+
+       /* Renegotiate with link partner */
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMCR);
+       data |= BMCR_ANENABLE | BMCR_ANRESTART;
+
+       e100_set_mdio_reg(dev, mdio_phy_addr, MII_BMCR, data);
+}
+
+static void
+e100_set_speed(struct net_device* dev, unsigned long speed)
+{
+       if (speed != current_speed_selection) {
+               current_speed_selection = speed;
+               e100_negotiate(dev);
+       }
+}
+
+static void
+e100_check_duplex(unsigned long priv)
+{
+       struct net_device *dev = (struct net_device *)priv;
+       struct net_local *np = (struct net_local *)dev->priv;
+       int old_duplex = full_duplex;
+       transceiver->check_duplex(dev);
+       if (old_duplex != full_duplex) {
+               /* Duplex changed */
+               SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+               *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+       }
+
+       /* Reinitialize the timer. */
+       duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
+       add_timer(&duplex_timer);
+       np->mii_if.full_duplex = full_duplex;
+}
+
+static void
+generic_check_duplex(struct net_device* dev)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_ADVERTISE);
+       if ((data & ADVERTISE_10FULL) ||
+           (data & ADVERTISE_100FULL))
+               full_duplex = 1;
+       else
+               full_duplex = 0;
+}
+
+static void
+tdk_check_duplex(struct net_device* dev)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_TDK_DIAGNOSTIC_REG);
+       full_duplex = (data & MDIO_TDK_DIAGNOSTIC_DPLX) ? 1 : 0;
+}
+
+static void
+broadcom_check_duplex(struct net_device* dev)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_AUX_CTRL_STATUS_REG);
+       full_duplex = (data & MDIO_BC_FULL_DUPLEX_IND) ? 1 : 0;
+}
+
+static void
+intel_check_duplex(struct net_device* dev)
+{
+       unsigned long data;
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MDIO_INT_STATUS_REG_2);
+       full_duplex = (data & MDIO_INT_FULL_DUPLEX_IND) ? 1 : 0;
+}
+
+static void
+e100_set_duplex(struct net_device* dev, enum duplex new_duplex)
+{
+       if (new_duplex != current_duplex) {
+               current_duplex = new_duplex;
+               e100_negotiate(dev);
+       }
+}
+
+static int
+e100_probe_transceiver(struct net_device* dev)
+{
+       unsigned int phyid_high;
+       unsigned int phyid_low;
+       unsigned int oui;
+       struct transceiver_ops* ops = NULL;
+
+       /* Probe MDIO physical address */
+       for (mdio_phy_addr = 0; mdio_phy_addr <= 31; mdio_phy_addr++) {
+               if (e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMSR) != 0xffff)
+                       break;
+       }
+       if (mdio_phy_addr == 32)
+                return -ENODEV;
+
+       /* Get manufacturer */
+       phyid_high = e100_get_mdio_reg(dev, mdio_phy_addr, MII_PHYSID1);
+       phyid_low = e100_get_mdio_reg(dev, mdio_phy_addr, MII_PHYSID2);
+       oui = (phyid_high << 6) | (phyid_low >> 10);
+
+       for (ops = &transceivers[0]; ops->oui; ops++) {
+               if (ops->oui == oui)
+                       break;
+       }
+       transceiver = ops;
+
+       return 0;
+}
+
+static int
+e100_get_mdio_reg(struct net_device *dev, int phy_id, int location)
+{
+       unsigned short cmd;    /* Data to be sent on MDIO port */
+       int data;   /* Data read from MDIO */
+       int bitCounter;
+
+       /* Start of frame, OP Code, Physical Address, Register Address */
+       cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (phy_id << 7) |
+               (location << 2);
+
+       e100_send_mdio_cmd(cmd, 0);
+
+       data = 0;
+
+       /* Data... */
+       for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
+               data |= (e100_receive_mdio_bit() << bitCounter);
+       }
+
+       return data;
+}
+
+static void
+e100_set_mdio_reg(struct net_device *dev, int phy_id, int location, int value)
+{
+       int bitCounter;
+       unsigned short cmd;
+
+       cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (phy_id << 7) |
+             (location << 2);
+
+       e100_send_mdio_cmd(cmd, 1);
+
+       /* Data... */
+       for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
+               e100_send_mdio_bit(GET_BIT(bitCounter, value));
+       }
+
+}
+
+static void
+e100_send_mdio_cmd(unsigned short cmd, int write_cmd)
+{
+       int bitCounter;
+       unsigned char data = 0x2;
+
+       /* Preamble */
+       for (bitCounter = 31; bitCounter>= 0; bitCounter--)
+               e100_send_mdio_bit(GET_BIT(bitCounter, MDIO_PREAMBLE));
+
+       for (bitCounter = 15; bitCounter >= 2; bitCounter--)
+               e100_send_mdio_bit(GET_BIT(bitCounter, cmd));
+
+       /* Turnaround */
+       for (bitCounter = 1; bitCounter >= 0 ; bitCounter--)
+               if (write_cmd)
+                       e100_send_mdio_bit(GET_BIT(bitCounter, data));
+               else
+                       e100_receive_mdio_bit();
+}
+
+static void
+e100_send_mdio_bit(unsigned char bit)
+{
+       *R_NETWORK_MGM_CTRL =
+               IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable) |
+               IO_FIELD(R_NETWORK_MGM_CTRL, mdio, bit);
+       udelay(1);
+       *R_NETWORK_MGM_CTRL =
+               IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable) |
+               IO_MASK(R_NETWORK_MGM_CTRL, mdck) |
+               IO_FIELD(R_NETWORK_MGM_CTRL, mdio, bit);
+       udelay(1);
+}
+
+static unsigned char
+e100_receive_mdio_bit()
+{
+       unsigned char bit;
+       *R_NETWORK_MGM_CTRL = 0;
+       bit = IO_EXTRACT(R_NETWORK_STAT, mdio, *R_NETWORK_STAT);
+       udelay(1);
+       *R_NETWORK_MGM_CTRL = IO_MASK(R_NETWORK_MGM_CTRL, mdck);
+       udelay(1);
+       return bit;
+}
+
+static void
+e100_reset_transceiver(struct net_device* dev)
+{
+       unsigned short cmd;
+       unsigned short data;
+       int bitCounter;
+
+       data = e100_get_mdio_reg(dev, mdio_phy_addr, MII_BMCR);
+
+       cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) | (MII_BMCR << 2);
+
+       e100_send_mdio_cmd(cmd, 1);
+
+       data |= 0x8000;
+
+       for (bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
+               e100_send_mdio_bit(GET_BIT(bitCounter, data));
+       }
+}
+
+/* Called by upper layers if they decide it took too long to complete
+ * sending a packet - we need to reset and stuff.
+ */
+
+static void
+e100_tx_timeout(struct net_device *dev)
+{
+       struct net_local *np = (struct net_local *)dev->priv;
+       unsigned long flags;
+
+       spin_lock_irqsave(&np->lock, flags);
+
+       printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
+              tx_done(dev) ? "IRQ problem" : "network cable problem");
+
+       /* remember we got an error */
+
+       np->stats.tx_errors++;
+
+       /* reset the TX DMA in case it has hung on something */
+
+       RESET_DMA(NETWORK_TX_DMA_NBR);
+       WAIT_DMA(NETWORK_TX_DMA_NBR);
+
+       /* Reset the transceiver. */
+
+       e100_reset_transceiver(dev);
+
+       /* and get rid of the packets that never got an interrupt */
+       while (myFirstTxDesc != myNextTxDesc)
+       {
+               dev_kfree_skb(myFirstTxDesc->skb);
+               myFirstTxDesc->skb = 0;
+               myFirstTxDesc = phys_to_virt(myFirstTxDesc->descr.next);
+       }
+
+       /* Set up transmit DMA channel so it can be restarted later */
+       *R_DMA_CH0_FIRST = 0;
+       *R_DMA_CH0_DESCR = virt_to_phys(myLastTxDesc);
+
+       /* tell the upper layers we're ok again */
+
+       netif_wake_queue(dev);
+       spin_unlock_irqrestore(&np->lock, flags);
+}
+
+
+/* This will only be invoked if the driver is _not_ in XOFF state.
+ * What this means is that we need not check it, and that this
+ * invariant will hold if we make sure that the netif_*_queue()
+ * calls are done at the proper times.
+ */
+
+static int
+e100_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+       struct net_local *np = (struct net_local *)dev->priv;
+       unsigned char *buf = skb->data;
+       unsigned long flags;
+
+#ifdef ETHDEBUG
+       printk("send packet len %d\n", length);
+#endif
+       spin_lock_irqsave(&np->lock, flags);  /* protect from tx_interrupt and ourself */
+
+       myNextTxDesc->skb = skb;
+
+       dev->trans_start = jiffies;
+
+       e100_hardware_send_packet(buf, skb->len);
+
+       myNextTxDesc = phys_to_virt(myNextTxDesc->descr.next);
+
+       /* Stop queue if full */
+       if (myNextTxDesc == myFirstTxDesc) {
+               netif_stop_queue(dev);
+       }
+
+       spin_unlock_irqrestore(&np->lock, flags);
+
+       return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ *   Handle the network interface interrupts.
+ */
+
+static irqreturn_t
+e100rxtx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct net_local *np = (struct net_local *)dev->priv;
+       unsigned long irqbits = *R_IRQ_MASK2_RD;
+
+       /* Disable RX/TX IRQs to avoid reentrancy */
+       *R_IRQ_MASK2_CLR =
+         IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) |
+         IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr);
+
+       /* Handle received packets */
+       if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
+               /* acknowledge the eop interrupt */
+
+               *R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do);
+
+               /* check if one or more complete packets were indeed received */
+
+               while ((*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) &&
+                      (myNextRxDesc != myLastRxDesc)) {
+                       /* Take out the buffer and give it to the OS, then
+                        * allocate a new buffer to put a packet in.
+                        */
+                       e100_rx(dev);
+                       ((struct net_local *)dev->priv)->stats.rx_packets++;
+                       /* restart/continue on the channel, for safety */
+                       *R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, restart);
+                       /* clear dma channel 1 eop/descr irq bits */
+                       *R_DMA_CH1_CLR_INTR =
+                               IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do) |
+                               IO_STATE(R_DMA_CH1_CLR_INTR, clr_descr, do);
+
+                       /* now, we might have gotten another packet
+                          so we have to loop back and check if so */
+               }
+       }
+
+       /* Report any packets that have been sent */
+       while (myFirstTxDesc != phys_to_virt(*R_DMA_CH0_FIRST) &&
+              myFirstTxDesc != myNextTxDesc)
+       {
+               np->stats.tx_bytes += myFirstTxDesc->skb->len;
+               np->stats.tx_packets++;
+
+               /* dma is ready with the transmission of the data in tx_skb, so now
+                  we can release the skb memory */
+               dev_kfree_skb_irq(myFirstTxDesc->skb);
+               myFirstTxDesc->skb = 0;
+               myFirstTxDesc = phys_to_virt(myFirstTxDesc->descr.next);
+       }
+
+       if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) {
+               /* acknowledge the eop interrupt and wake up queue */
+               *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
+               netif_wake_queue(dev);
+       }
+
+       /* Enable RX/TX IRQs again */
+       *R_IRQ_MASK2_SET =
+         IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) |
+         IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+e100nw_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct net_local *np = (struct net_local *)dev->priv;
+       unsigned long irqbits = *R_IRQ_MASK0_RD;
+
+       /* check for underrun irq */
+       if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) {
+               SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
+               *R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
+               SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, nop);
+               np->stats.tx_errors++;
+               D(printk("ethernet receiver underrun!\n"));
+       }
+
+       /* check for overrun irq */
+       if (irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) {
+               update_rx_stats(&np->stats); /* this will ack the irq */
+               D(printk("ethernet receiver overrun!\n"));
+       }
+       /* check for excessive collision irq */
+       if (irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) {
+               SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
+               *R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
+               SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, nop);
+               *R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
+               np->stats.tx_errors++;
+               D(printk("ethernet excessive collisions!\n"));
+       }
+       return IRQ_HANDLED;
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+e100_rx(struct net_device *dev)
+{
+       struct sk_buff *skb;
+       int length = 0;
+       struct net_local *np = (struct net_local *)dev->priv;
+       unsigned char *skb_data_ptr;
+#ifdef ETHDEBUG
+       int i;
+#endif
+
+       if (!led_active && time_after(jiffies, led_next_time)) {
+               /* light the network leds depending on the current speed. */
+               e100_set_network_leds(NETWORK_ACTIVITY);
+
+               /* Set the earliest time we may clear the LED */
+               led_next_time = jiffies + NET_FLASH_TIME;
+               led_active = 1;
+               mod_timer(&clear_led_timer, jiffies + HZ/10);
+       }
+
+       length = myNextRxDesc->descr.hw_len - 4;
+       ((struct net_local *)dev->priv)->stats.rx_bytes += length;
+
+#ifdef ETHDEBUG
+       printk("Got a packet of length %d:\n", length);
+       /* dump the first bytes in the packet */
+       skb_data_ptr = (unsigned char *)phys_to_virt(myNextRxDesc->descr.buf);
+       for (i = 0; i < 8; i++) {
+               printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8,
+                      skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3],
+                      skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]);
+               skb_data_ptr += 8;
+       }
+#endif
+
+       if (length < RX_COPYBREAK) {
+               /* Small packet, copy data */
+               skb = dev_alloc_skb(length - ETHER_HEAD_LEN);
+               if (!skb) {
+                       np->stats.rx_errors++;
+                       printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
+                       return;
+               }
+
+               skb_put(skb, length - ETHER_HEAD_LEN);        /* allocate room for the packet body */
+               skb_data_ptr = skb_push(skb, ETHER_HEAD_LEN); /* allocate room for the header */
+
+#ifdef ETHDEBUG
+               printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n",
+                 skb->head, skb->data, skb->tail, skb->end);
+               printk("copying packet to 0x%x.\n", skb_data_ptr);
+#endif
+
+               memcpy(skb_data_ptr, phys_to_virt(myNextRxDesc->descr.buf), length);
+       }
+       else {
+               /* Large packet, send directly to upper layers and allocate new
+                * memory (aligned to cache line boundary to avoid bug).
+                * Before sending the skb to upper layers we must make sure that
+                * skb->data points to the aligned start of the packet.
+                */
+               int align;
+               struct sk_buff *new_skb = dev_alloc_skb(MAX_MEDIA_DATA_SIZE + 2 * L1_CACHE_BYTES);
+               if (!new_skb) {
+                       np->stats.rx_errors++;
+                       printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
+                       return;
+               }
+               skb = myNextRxDesc->skb;
+               align = (int)phys_to_virt(myNextRxDesc->descr.buf) - (int)skb->data;
+               skb_put(skb, length + align);
+               skb_pull(skb, align); /* Remove alignment bytes */
+               myNextRxDesc->skb = new_skb;
+               myNextRxDesc->descr.buf = L1_CACHE_ALIGN(virt_to_phys(myNextRxDesc->skb->data));
+       }
+
+       skb->dev = dev;
+       skb->protocol = eth_type_trans(skb, dev);
+
+       /* Send the packet to the upper layers */
+       netif_rx(skb);
+
+       /* Prepare for next packet */
+       myNextRxDesc->descr.status = 0;
+       myPrevRxDesc = myNextRxDesc;
+       myNextRxDesc = phys_to_virt(myNextRxDesc->descr.next);
+
+       rx_queue_len++;
+
+       /* Check if descriptors should be returned */
+       if (rx_queue_len == RX_QUEUE_THRESHOLD) {
+               flush_etrax_cache();
+               myPrevRxDesc->descr.ctrl |= d_eol;
+               myLastRxDesc->descr.ctrl &= ~d_eol;
+               myLastRxDesc = myPrevRxDesc;
+               rx_queue_len = 0;
+       }
+}
+
+/* The inverse routine to net_open(). */
+static int
+e100_close(struct net_device *dev)
+{
+       struct net_local *np = (struct net_local *)dev->priv;
+
+       printk(KERN_INFO "Closing %s.\n", dev->name);
+
+       netif_stop_queue(dev);
+
+       *R_IRQ_MASK0_CLR =
+               IO_STATE(R_IRQ_MASK0_CLR, overrun, clr) |
+               IO_STATE(R_IRQ_MASK0_CLR, underrun, clr) |
+               IO_STATE(R_IRQ_MASK0_CLR, excessive_col, clr);
+
+       *R_IRQ_MASK2_CLR =
+               IO_STATE(R_IRQ_MASK2_CLR, dma0_descr, clr) |
+               IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) |
+               IO_STATE(R_IRQ_MASK2_CLR, dma1_descr, clr) |
+               IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr);
+
+       /* Stop the receiver and the transmitter */
+
+       RESET_DMA(NETWORK_TX_DMA_NBR);
+       RESET_DMA(NETWORK_RX_DMA_NBR);
+
+       /* Flush the Tx and disable Rx here. */
+
+       free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
+       free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
+       free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
+
+       /* Update the statistics here. */
+
+       update_rx_stats(&np->stats);
+       update_tx_stats(&np->stats);
+
+       /* Stop speed/duplex timers */
+       del_timer(&speed_timer);
+       del_timer(&duplex_timer);
+
+       return 0;
+}
+
+static int
+e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       struct mii_ioctl_data *data = if_mii(ifr);
+       struct net_local *np = netdev_priv(dev);
+
+       spin_lock(&np->lock); /* Preempt protection */
+       switch (cmd) {
+               case SIOCETHTOOL:
+                       return e100_ethtool_ioctl(dev,ifr);
+               case SIOCGMIIPHY: /* Get PHY address */
+                       data->phy_id = mdio_phy_addr;
+                       break;
+               case SIOCGMIIREG: /* Read MII register */
+                       data->val_out = e100_get_mdio_reg(dev, mdio_phy_addr, data->reg_num);
+                       break;
+               case SIOCSMIIREG: /* Write MII register */
+                       e100_set_mdio_reg(dev, mdio_phy_addr, data->reg_num, data->val_in);
+                       break;
+               /* The ioctls below should be considered obsolete but are */
+               /* still present for compatability with old scripts/apps  */
+               case SET_ETH_SPEED_10:                  /* 10 Mbps */
+                       e100_set_speed(dev, 10);
+                       break;
+               case SET_ETH_SPEED_100:                /* 100 Mbps */
+                       e100_set_speed(dev, 100);
+                       break;
+               case SET_ETH_SPEED_AUTO:              /* Auto negotiate speed */
+                       e100_set_speed(dev, 0);
+                       break;
+               case SET_ETH_DUPLEX_HALF:              /* Half duplex. */
+                       e100_set_duplex(dev, half);
+                       break;
+               case SET_ETH_DUPLEX_FULL:              /* Full duplex. */
+                       e100_set_duplex(dev, full);
+                       break;
+               case SET_ETH_DUPLEX_AUTO:             /* Autonegotiate duplex*/
+                       e100_set_duplex(dev, autoneg);
+                       break;
+               default:
+                       return -EINVAL;
+       }
+       spin_unlock(&np->lock);
+       return 0;
+}
+
+static int
+e100_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr)
+{
+       struct ethtool_cmd ecmd;
+
+       if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd)))
+               return -EFAULT;
+
+       switch (ecmd.cmd) {
+               case ETHTOOL_GSET:
+               {
+                       memset((void *) &ecmd, 0, sizeof (ecmd));
+                       ecmd.supported =
+                         SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII |
+                         SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+                         SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full;
+                       ecmd.port = PORT_TP;
+                       ecmd.transceiver = XCVR_EXTERNAL;
+                       ecmd.phy_address = mdio_phy_addr;
+                       ecmd.speed = current_speed;
+                       ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
+                       ecmd.advertising = ADVERTISED_TP;
+                       if (current_duplex == autoneg && current_speed_selection == 0)
+                               ecmd.advertising |= ADVERTISED_Autoneg;
+                       else {
+                               ecmd.advertising |=
+                                 ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+                                 ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
+                               if (current_speed_selection == 10)
+                                       ecmd.advertising &= ~(ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full);
+                               else if (current_speed_selection == 100)
+                                       ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full);
+                               if (current_duplex == half)
+                                       ecmd.advertising &= ~(ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Full);
+                               else if (current_duplex == full)
+                                       ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half);
+                       }
+                       ecmd.autoneg = AUTONEG_ENABLE;
+                       if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd)))
+                               return -EFAULT;
+               }
+               break;
+               case ETHTOOL_SSET:
+               {
+                       if (!capable(CAP_NET_ADMIN)) {
+                               return -EPERM;
+                       }
+                       if (ecmd.autoneg == AUTONEG_ENABLE) {
+                               e100_set_duplex(dev, autoneg);
+                               e100_set_speed(dev, 0);
+                       } else {
+                               e100_set_duplex(dev, ecmd.duplex == DUPLEX_HALF ? half : full);
+                               e100_set_speed(dev, ecmd.speed == SPEED_10 ? 10: 100);
+                       }
+               }
+               break;
+               case ETHTOOL_GDRVINFO:
+               {
+                       struct ethtool_drvinfo info;
+                       memset((void *) &info, 0, sizeof (info));
+                       strncpy(info.driver, "ETRAX 100LX", sizeof(info.driver) - 1);
+                       strncpy(info.version, "$Revision: 1.31 $", sizeof(info.version) - 1);
+                       strncpy(info.fw_version, "N/A", sizeof(info.fw_version) - 1);
+                       strncpy(info.bus_info, "N/A", sizeof(info.bus_info) - 1);
+                       info.regdump_len = 0;
+                       info.eedump_len = 0;
+                       info.testinfo_len = 0;
+                       if (copy_to_user(ifr->ifr_data, &info, sizeof (info)))
+                               return -EFAULT;
+               }
+               break;
+               case ETHTOOL_NWAY_RST:
+                       if (current_duplex == autoneg && current_speed_selection == 0)
+                               e100_negotiate(dev);
+               break;
+               default:
+                       return -EOPNOTSUPP;
+               break;
+       }
+       return 0;
+}
+
+static int
+e100_set_config(struct net_device *dev, struct ifmap *map)
+{
+       struct net_local *np = (struct net_local *)dev->priv;
+       spin_lock(&np->lock); /* Preempt protection */
+
+       switch(map->port) {
+               case IF_PORT_UNKNOWN:
+                       /* Use autoneg */
+                       e100_set_speed(dev, 0);
+                       e100_set_duplex(dev, autoneg);
+                       break;
+               case IF_PORT_10BASET:
+                       e100_set_speed(dev, 10);
+                       e100_set_duplex(dev, autoneg);
+                       break;
+               case IF_PORT_100BASET:
+               case IF_PORT_100BASETX:
+                       e100_set_speed(dev, 100);
+                       e100_set_duplex(dev, autoneg);
+                       break;
+               case IF_PORT_100BASEFX:
+               case IF_PORT_10BASE2:
+               case IF_PORT_AUI:
+                       spin_unlock(&np->lock);
+                       return -EOPNOTSUPP;
+                       break;
+               default:
+                       printk(KERN_ERR "%s: Invalid media selected", dev->name);
+                       spin_unlock(&np->lock);
+                       return -EINVAL;
+       }
+       spin_unlock(&np->lock);
+       return 0;
+}
+
+static void
+update_rx_stats(struct net_device_stats *es)
+{
+       unsigned long r = *R_REC_COUNTERS;
+       /* update stats relevant to reception errors */
+       es->rx_fifo_errors += IO_EXTRACT(R_REC_COUNTERS, congestion, r);
+       es->rx_crc_errors += IO_EXTRACT(R_REC_COUNTERS, crc_error, r);
+       es->rx_frame_errors += IO_EXTRACT(R_REC_COUNTERS, alignment_error, r);
+       es->rx_length_errors += IO_EXTRACT(R_REC_COUNTERS, oversize, r);
+}
+
+static void
+update_tx_stats(struct net_device_stats *es)
+{
+       unsigned long r = *R_TR_COUNTERS;
+       /* update stats relevant to transmission errors */
+       es->collisions +=
+               IO_EXTRACT(R_TR_COUNTERS, single_col, r) +
+               IO_EXTRACT(R_TR_COUNTERS, multiple_col, r);
+       es->tx_errors += IO_EXTRACT(R_TR_COUNTERS, deferred, r);
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *
+e100_get_stats(struct net_device *dev)
+{
+       struct net_local *lp = (struct net_local *)dev->priv;
+       unsigned long flags;
+       spin_lock_irqsave(&lp->lock, flags);
+
+       update_rx_stats(&lp->stats);
+       update_tx_stats(&lp->stats);
+
+       spin_unlock_irqrestore(&lp->lock, flags);
+       return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1     Promiscuous mode, receive all packets
+ * num_addrs == 0      Normal mode, clear multicast list
+ * num_addrs > 0       Multicast mode, receive normal and MC packets,
+ *                     and do best-effort filtering.
+ */
+static void
+set_multicast_list(struct net_device *dev)
+{
+       struct net_local *lp = (struct net_local *)dev->priv;
+       int num_addr = dev->mc_count;
+       unsigned long int lo_bits;
+       unsigned long int hi_bits;
+       spin_lock(&lp->lock);
+       if (dev->flags & IFF_PROMISC)
+       {
+               /* promiscuous mode */
+               lo_bits = 0xfffffffful;
+               hi_bits = 0xfffffffful;
+
+               /* Enable individual receive */
+               SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, receive);
+               *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+       } else if (dev->flags & IFF_ALLMULTI) {
+               /* enable all multicasts */
+               lo_bits = 0xfffffffful;
+               hi_bits = 0xfffffffful;
+
+               /* Disable individual receive */
+               SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+               *R_NETWORK_REC_CONFIG =  network_rec_config_shadow;
+       } else if (num_addr == 0) {
+               /* Normal, clear the mc list */
+               lo_bits = 0x00000000ul;
+               hi_bits = 0x00000000ul;
+
+               /* Disable individual receive */
+               SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+               *R_NETWORK_REC_CONFIG =  network_rec_config_shadow;
+       } else {
+               /* MC mode, receive normal and MC packets */
+               char hash_ix;
+               struct dev_mc_list *dmi = dev->mc_list;
+               int i;
+               char *baddr;
+               lo_bits = 0x00000000ul;
+               hi_bits = 0x00000000ul;
+               for (i=0; i<num_addr; i++) {
+                       /* Calculate the hash index for the GA registers */
+
+                       hash_ix = 0;
+                       baddr = dmi->dmi_addr;
+                       hash_ix ^= (*baddr) & 0x3f;
+                       hash_ix ^= ((*baddr) >> 6) & 0x03;
+                       ++baddr;
+                       hash_ix ^= ((*baddr) << 2) & 0x03c;
+                       hash_ix ^= ((*baddr) >> 4) & 0xf;
+                       ++baddr;
+                       hash_ix ^= ((*baddr) << 4) & 0x30;
+                       hash_ix ^= ((*baddr) >> 2) & 0x3f;
+                       ++baddr;
+                       hash_ix ^= (*baddr) & 0x3f;
+                       hash_ix ^= ((*baddr) >> 6) & 0x03;
+                       ++baddr;
+                       hash_ix ^= ((*baddr) << 2) & 0x03c;
+                       hash_ix ^= ((*baddr) >> 4) & 0xf;
+                       ++baddr;
+                       hash_ix ^= ((*baddr) << 4) & 0x30;
+                       hash_ix ^= ((*baddr) >> 2) & 0x3f;
+
+                       hash_ix &= 0x3f;
+
+                       if (hash_ix >= 32) {
+                               hi_bits |= (1 << (hash_ix-32));
+                       }
+                       else {
+                               lo_bits |= (1 << hash_ix);
+                       }
+                       dmi = dmi->next;
+               }
+               /* Disable individual receive */
+               SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+               *R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+       }
+       *R_NETWORK_GA_0 = lo_bits;
+       *R_NETWORK_GA_1 = hi_bits;
+       spin_unlock(&lp->lock);
+}
+
+void
+e100_hardware_send_packet(char *buf, int length)
+{
+       D(printk("e100 send pack, buf 0x%x len %d\n", buf, length));
+
+       if (!led_active && time_after(jiffies, led_next_time)) {
+               /* light the network leds depending on the current speed. */
+               e100_set_network_leds(NETWORK_ACTIVITY);
+
+               /* Set the earliest time we may clear the LED */
+               led_next_time = jiffies + NET_FLASH_TIME;
+               led_active = 1;
+               mod_timer(&clear_led_timer, jiffies + HZ/10);
+       }
+
+       /* configure the tx dma descriptor */
+       myNextTxDesc->descr.sw_len = length;
+       myNextTxDesc->descr.ctrl = d_eop | d_eol | d_wait;
+       myNextTxDesc->descr.buf = virt_to_phys(buf);
+
+        /* Move end of list */
+        myLastTxDesc->descr.ctrl &= ~d_eol;
+        myLastTxDesc = myNextTxDesc;
+
+       /* Restart DMA channel */
+       *R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, restart);
+}
+
+static void
+e100_clear_network_leds(unsigned long dummy)
+{
+       if (led_active && time_after(jiffies, led_next_time)) {
+               e100_set_network_leds(NO_NETWORK_ACTIVITY);
+
+               /* Set the earliest time we may set the LED */
+               led_next_time = jiffies + NET_FLASH_PAUSE;
+               led_active = 0;
+       }
+}
+
+static void
+e100_set_network_leds(int active)
+{
+#if defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK)
+       int light_leds = (active == NO_NETWORK_ACTIVITY);
+#elif defined(CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY)
+       int light_leds = (active == NETWORK_ACTIVITY);
+#else
+#error "Define either CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK or CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY"
+#endif
+
+       if (!current_speed) {
+               /* Make LED red, link is down */
+#if defined(CONFIG_ETRAX_NETWORK_RED_ON_NO_CONNECTION)
+               LED_NETWORK_SET(LED_RED);
+#else
+               LED_NETWORK_SET(LED_OFF);
+#endif
+       }
+       else if (light_leds) {
+               if (current_speed == 10) {
+                       LED_NETWORK_SET(LED_ORANGE);
+               } else {
+                       LED_NETWORK_SET(LED_GREEN);
+               }
+       }
+       else {
+               LED_NETWORK_SET(LED_OFF);
+       }
+}
+
+static int
+etrax_init_module(void)
+{
+       return etrax_ethernet_init();
+}
+
+static int __init
+e100_boot_setup(char* str)
+{
+       struct sockaddr sa = {0};
+       int i;
+
+       /* Parse the colon separated Ethernet station address */
+       for (i = 0; i <  ETH_ALEN; i++) {
+               unsigned int tmp;
+               if (sscanf(str + 3*i, "%2x", &tmp) != 1) {
+                       printk(KERN_WARNING "Malformed station address");
+                       return 0;
+               }
+               sa.sa_data[i] = (char)tmp;
+       }
+
+       default_mac = sa;
+       return 1;
+}
+
+__setup("etrax100_eth=", e100_boot_setup);
+
+module_init(etrax_init_module);
diff --git a/drivers/net/sk98lin/skethtool.c b/drivers/net/sk98lin/skethtool.c
new file mode 100644 (file)
index 0000000..0aed3b9
--- /dev/null
@@ -0,0 +1,555 @@
+/******************************************************************************
+ *
+ * Name:        skethtool.c
+ * Project:     GEnesis, PCI Gigabit Ethernet Adapter
+ * Version:     $Revision: 1.7 $
+ * Date:        $Date: 2004/09/29 13:32:07 $
+ * Purpose:     All functions regarding ethtool handling
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ *     (C)Copyright 1998-2002 SysKonnect GmbH.
+ *     (C)Copyright 2002-2004 Marvell.
+ *
+ *     Driver for Marvell Yukon/2 chipset and SysKonnect Gigabit Ethernet 
+ *      Server Adapters.
+ *
+ *     Author: Ralph Roesler (rroesler@syskonnect.de)
+ *             Mirko Lindner (mlindner@syskonnect.de)
+ *
+ *     Address all question to: linux@syskonnect.de
+ *
+ *     The technical manual for the adapters is available from SysKonnect's
+ *     web pages: www.syskonnect.com
+ *     
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     The information in this file is provided "AS IS" without warranty.
+ *
+ *****************************************************************************/
+
+#include "h/skdrv1st.h"
+#include "h/skdrv2nd.h"
+#include "h/skversion.h"
+
+#include <linux/ethtool.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+
+/******************************************************************************
+ *
+ * Defines
+ *
+ *****************************************************************************/
+
+#define SUPP_COPPER_ALL (SUPPORTED_10baseT_Half  | SUPPORTED_10baseT_Full  | \
+                         SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \
+                         SUPPORTED_1000baseT_Half| SUPPORTED_1000baseT_Full| \
+                         SUPPORTED_TP)
+
+#define ADV_COPPER_ALL  (ADVERTISED_10baseT_Half  | ADVERTISED_10baseT_Full  | \
+                         ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \
+                         ADVERTISED_1000baseT_Half| ADVERTISED_1000baseT_Full| \
+                         ADVERTISED_TP)
+
+#define SUPP_FIBRE_ALL  (SUPPORTED_1000baseT_Full | \
+                         SUPPORTED_FIBRE          | \
+                         SUPPORTED_Autoneg)
+
+#define ADV_FIBRE_ALL   (ADVERTISED_1000baseT_Full | \
+                         ADVERTISED_FIBRE          | \
+                         ADVERTISED_Autoneg)
+
+
+/******************************************************************************
+ *
+ * Local Functions
+ *
+ *****************************************************************************/
+
+/*****************************************************************************
+ *
+ *     getSettings - retrieves the current settings of the selected adapter
+ *
+ * Description:
+ *     The current configuration of the selected adapter is returned.
+ *     This configuration involves a)speed, b)duplex and c)autoneg plus
+ *     a number of other variables.
+ *
+ * Returns:    always 0
+ *
+ */
+static int getSettings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+       const DEV_NET *pNet = netdev_priv(dev);
+       int port = pNet->PortNr;
+       const SK_AC *pAC = pNet->pAC;
+       const SK_GEPORT *pPort = &pAC->GIni.GP[port];
+
+       static int DuplexAutoNegConfMap[9][3]= {
+               { -1                     , -1         , -1              },
+               { 0                      , -1         , -1              },
+               { SK_LMODE_HALF          , DUPLEX_HALF, AUTONEG_DISABLE },
+               { SK_LMODE_FULL          , DUPLEX_FULL, AUTONEG_DISABLE },
+               { SK_LMODE_AUTOHALF      , DUPLEX_HALF, AUTONEG_ENABLE  },
+               { SK_LMODE_AUTOFULL      , DUPLEX_FULL, AUTONEG_ENABLE  },
+               { SK_LMODE_AUTOBOTH      , DUPLEX_FULL, AUTONEG_ENABLE  },
+               { SK_LMODE_AUTOSENSE     , -1         , -1              },
+               { SK_LMODE_INDETERMINATED, -1         , -1              }
+       };
+       static int SpeedConfMap[6][2] = {
+               { 0                       , -1         },
+               { SK_LSPEED_AUTO          , -1         },
+               { SK_LSPEED_10MBPS        , SPEED_10   },
+               { SK_LSPEED_100MBPS       , SPEED_100  },
+               { SK_LSPEED_1000MBPS      , SPEED_1000 },
+               { SK_LSPEED_INDETERMINATED, -1         }
+       };
+       static int AdvSpeedMap[6][2] = {
+               { 0                       , -1         },
+               { SK_LSPEED_AUTO          , -1         },
+               { SK_LSPEED_10MBPS        , ADVERTISED_10baseT_Half   | ADVERTISED_10baseT_Full },
+               { SK_LSPEED_100MBPS       , ADVERTISED_100baseT_Half  | ADVERTISED_100baseT_Full },
+               { SK_LSPEED_1000MBPS      , ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full},
+               { SK_LSPEED_INDETERMINATED, -1         }
+       };
+
+       ecmd->phy_address = port;
+       ecmd->speed       = SpeedConfMap[pPort->PLinkSpeedUsed][1];
+       ecmd->duplex      = DuplexAutoNegConfMap[pPort->PLinkModeStatus][1];
+       ecmd->autoneg     = DuplexAutoNegConfMap[pPort->PLinkModeStatus][2];
+       ecmd->transceiver = XCVR_INTERNAL;
+
+       if (pAC->GIni.GICopperType) {
+               ecmd->port        = PORT_TP;
+               ecmd->supported   = (SUPP_COPPER_ALL|SUPPORTED_Autoneg);
+               if (pAC->GIni.GIGenesis) {
+                       ecmd->supported &= ~(SUPPORTED_10baseT_Half);
+                       ecmd->supported &= ~(SUPPORTED_10baseT_Full);
+                       ecmd->supported &= ~(SUPPORTED_100baseT_Half);
+                       ecmd->supported &= ~(SUPPORTED_100baseT_Full);
+               } else {
+                       if (pAC->GIni.GIChipId == CHIP_ID_YUKON) {
+                               ecmd->supported &= ~(SUPPORTED_1000baseT_Half);
+                       } 
+#ifdef CHIP_ID_YUKON_FE
+                       if (pAC->GIni.GIChipId == CHIP_ID_YUKON_FE) {
+                               ecmd->supported &= ~(SUPPORTED_1000baseT_Half);
+                               ecmd->supported &= ~(SUPPORTED_1000baseT_Full);
+                       }
+#endif
+               }
+               if (pAC->GIni.GP[0].PLinkSpeed != SK_LSPEED_AUTO) {
+                       ecmd->advertising = AdvSpeedMap[pPort->PLinkSpeed][1];
+                       if (pAC->GIni.GIChipId == CHIP_ID_YUKON) {
+                               ecmd->advertising &= ~(SUPPORTED_1000baseT_Half);
+                       } 
+               } else {
+                       ecmd->advertising = ecmd->supported;
+               }
+
+               if (ecmd->autoneg == AUTONEG_ENABLE) 
+                       ecmd->advertising |= ADVERTISED_Autoneg;
+       } else {
+               ecmd->port        = PORT_FIBRE;
+               ecmd->supported   = SUPP_FIBRE_ALL;
+               ecmd->advertising = ADV_FIBRE_ALL;
+       }
+       return 0;
+}
+
+/*
+ * MIB infrastructure uses instance value starting at 1
+ * based on board and port.
+ */
+static inline u32 pnmiInstance(const DEV_NET *pNet)
+{
+       return 1 + (pNet->pAC->RlmtNets == 2) + pNet->PortNr;
+}
+
+/*****************************************************************************
+ *
+ *     setSettings - configures the settings of a selected adapter
+ *
+ * Description:
+ *     Possible settings that may be altered are a)speed, b)duplex or 
+ *     c)autonegotiation.
+ *
+ * Returns:
+ *     0:      everything fine, no error
+ *     <0:     the return value is the error code of the failure 
+ */
+static int setSettings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+       DEV_NET *pNet = netdev_priv(dev);
+       SK_AC *pAC = pNet->pAC;
+       u32 instance;
+       char buf[4];
+       int len = 1;
+
+       if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100 
+           && ecmd->speed != SPEED_1000)
+               return -EINVAL;
+
+       if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+               return -EINVAL;
+
+       if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
+               return -EINVAL;
+
+       if (ecmd->autoneg == AUTONEG_DISABLE)
+               *buf = (ecmd->duplex == DUPLEX_FULL) 
+                       ? SK_LMODE_FULL : SK_LMODE_HALF;
+       else
+               *buf = (ecmd->duplex == DUPLEX_FULL) 
+                       ? SK_LMODE_AUTOFULL : SK_LMODE_AUTOHALF;
+       
+       instance = pnmiInstance(pNet);
+       if (SkPnmiSetVar(pAC, pAC->IoBase, OID_SKGE_LINK_MODE, 
+                          &buf, &len, instance, pNet->NetNr) != SK_PNMI_ERR_OK)
+               return -EINVAL;
+
+       switch(ecmd->speed) {
+       case SPEED_1000:
+               *buf = SK_LSPEED_1000MBPS;
+               break;
+       case SPEED_100:
+               *buf = SK_LSPEED_100MBPS;
+               break;
+       case SPEED_10:
+               *buf = SK_LSPEED_10MBPS;
+       }
+
+       if (SkPnmiSetVar(pAC, pAC->IoBase, OID_SKGE_SPEED_MODE, 
+                        &buf, &len, instance, pNet->NetNr) != SK_PNMI_ERR_OK)
+               return -EINVAL;
+
+       return 0;
+}
+
+/*****************************************************************************
+ *
+ *     getDriverInfo - returns generic driver and adapter information
+ *
+ * Description:
+ *     Generic driver information is returned via this function, such as
+ *     the name of the driver, its version and and firmware version.
+ *     In addition to this, the location of the selected adapter is 
+ *     returned as a bus info string (e.g. '01:05.0').
+ *     
+ * Returns:    N/A
+ *
+ */
+static void getDriverInfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+       const DEV_NET   *pNet = netdev_priv(dev);
+       const SK_AC *pAC = pNet->pAC;
+       char vers[32];
+
+       snprintf(vers, sizeof(vers)-1, VER_STRING "(v%d.%d)",
+               (pAC->GIni.GIPciHwRev >> 4) & 0xf, pAC->GIni.GIPciHwRev & 0xf);
+
+       strlcpy(info->driver, DRIVER_FILE_NAME, sizeof(info->driver));
+       strcpy(info->version, vers);
+       strcpy(info->fw_version, "N/A");
+       strlcpy(info->bus_info, pAC->PciDev->slot_name, ETHTOOL_BUSINFO_LEN);
+}
+
+/*
+ * Ethtool statistics support.
+ */
+static const char StringsStats[][ETH_GSTRING_LEN] = {
+       "rx_packets",   "tx_packets",
+       "rx_bytes",     "tx_bytes",
+       "rx_errors",    "tx_errors",    
+       "rx_dropped",   "tx_dropped",
+       "multicasts",   "collisions",   
+       "rx_length_errors",             "rx_buffer_overflow_errors",
+       "rx_crc_errors",                "rx_frame_errors",
+       "rx_too_short_errors",          "rx_too_long_errors",
+       "rx_carrier_extension_errors",  "rx_symbol_errors",
+       "rx_llc_mac_size_errors",       "rx_carrier_errors",    
+       "rx_jabber_errors",             "rx_missed_errors",
+       "tx_abort_collision_errors",    "tx_carrier_errors",
+       "tx_buffer_underrun_errors",    "tx_heartbeat_errors",
+       "tx_window_errors",
+};
+
+static int getStatsCount(struct net_device *dev)
+{
+       return ARRAY_SIZE(StringsStats);
+}
+
+static void getStrings(struct net_device *dev, u32 stringset, u8 *data)
+{
+       switch(stringset) {
+       case ETH_SS_STATS:
+               memcpy(data, *StringsStats, sizeof(StringsStats));
+               break;
+       }
+}
+
+static void getEthtoolStats(struct net_device *dev,
+                           struct ethtool_stats *stats, u64 *data)
+{
+       const DEV_NET   *pNet = netdev_priv(dev);
+       const SK_AC *pAC = pNet->pAC;
+       const SK_PNMI_STRUCT_DATA *pPnmiStruct = &pAC->PnmiStruct;
+
+       *data++ = pPnmiStruct->Stat[0].StatRxOkCts;
+       *data++ = pPnmiStruct->Stat[0].StatTxOkCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxOctetsOkCts;
+       *data++ = pPnmiStruct->Stat[0].StatTxOctetsOkCts;
+       *data++ = pPnmiStruct->InErrorsCts;
+       *data++ = pPnmiStruct->Stat[0].StatTxSingleCollisionCts;
+       *data++ = pPnmiStruct->RxNoBufCts;
+       *data++ = pPnmiStruct->TxNoBufCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxMulticastOkCts;
+       *data++ = pPnmiStruct->Stat[0].StatTxSingleCollisionCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxRuntCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxFifoOverflowCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxFcsCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxFramingCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxShortsCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxTooLongCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxCextCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxSymbolCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxIRLengthCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxCarrierCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxJabberCts;
+       *data++ = pPnmiStruct->Stat[0].StatRxMissedCts;
+       *data++ = pAC->stats.tx_aborted_errors;
+       *data++ = pPnmiStruct->Stat[0].StatTxCarrierCts;
+       *data++ = pPnmiStruct->Stat[0].StatTxFifoUnderrunCts;
+       *data++ = pPnmiStruct->Stat[0].StatTxCarrierCts;
+       *data++ = pAC->stats.tx_window_errors;
+}
+
+
+/*****************************************************************************
+ *
+ *     toggleLeds - Changes the LED state of an adapter
+ *
+ * Description:
+ *     This function changes the current state of all LEDs of an adapter so
+ *     that it can be located by a user. 
+ *
+ * Returns:    N/A
+ *
+ */
+static void toggleLeds(DEV_NET *pNet, int on)
+{
+       SK_AC *pAC = pNet->pAC;
+       int port = pNet->PortNr;
+       void __iomem *io = pAC->IoBase;
+
+       if (pAC->GIni.GIGenesis) {
+               SK_OUT8(io, MR_ADDR(port,LNK_LED_REG), 
+                       on ? SK_LNK_ON : SK_LNK_OFF);
+               SkGeYellowLED(pAC, io, 
+                             on ? (LED_ON >> 1) : (LED_OFF >> 1));
+               SkGeXmitLED(pAC, io, MR_ADDR(port,RX_LED_INI),
+                           on ? SK_LED_TST : SK_LED_DIS);
+
+               if (pAC->GIni.GP[port].PhyType == SK_PHY_BCOM)
+                       SkXmPhyWrite(pAC, io, port, PHY_BCOM_P_EXT_CTRL, 
+                                    on ? PHY_B_PEC_LED_ON : PHY_B_PEC_LED_OFF);
+               else if (pAC->GIni.GP[port].PhyType == SK_PHY_LONE)
+                       SkXmPhyWrite(pAC, io, port, PHY_LONE_LED_CFG,
+                                    on ? 0x0800 : PHY_L_LC_LEDT);
+               else
+                       SkGeXmitLED(pAC, io, MR_ADDR(port,TX_LED_INI),
+                                   on ? SK_LED_TST : SK_LED_DIS);
+       } else {
+               const u16 YukLedOn = (PHY_M_LED_MO_DUP(MO_LED_ON)  |
+                                     PHY_M_LED_MO_10(MO_LED_ON)   |
+                                     PHY_M_LED_MO_100(MO_LED_ON)  |
+                                     PHY_M_LED_MO_1000(MO_LED_ON) | 
+                                     PHY_M_LED_MO_RX(MO_LED_ON));
+               const u16  YukLedOff = (PHY_M_LED_MO_DUP(MO_LED_OFF)  |
+                                       PHY_M_LED_MO_10(MO_LED_OFF)   |
+                                       PHY_M_LED_MO_100(MO_LED_OFF)  |
+                                       PHY_M_LED_MO_1000(MO_LED_OFF) | 
+                                       PHY_M_LED_MO_RX(MO_LED_OFF));
+       
+
+               SkGmPhyWrite(pAC,io,port,PHY_MARV_LED_CTRL,0);
+               SkGmPhyWrite(pAC,io,port,PHY_MARV_LED_OVER, 
+                            on ? YukLedOn : YukLedOff);
+       }
+}
+
+/*****************************************************************************
+ *
+ *     skGeBlinkTimer - Changes the LED state of an adapter
+ *
+ * Description:
+ *     This function changes the current state of all LEDs of an adapter so
+ *     that it can be located by a user. If the requested time interval for
+ *     this test has elapsed, this function cleans up everything that was 
+ *     temporarily setup during the locate NIC test. This involves of course
+ *     also closing or opening any adapter so that the initial board state 
+ *     is recovered.
+ *
+ * Returns:    N/A
+ *
+ */
+void SkGeBlinkTimer(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *) data;
+       DEV_NET *pNet = netdev_priv(dev);
+       SK_AC *pAC = pNet->pAC;
+
+       toggleLeds(pNet, pAC->LedsOn);
+
+       pAC->LedsOn = !pAC->LedsOn;
+       mod_timer(&pAC->BlinkTimer, jiffies + HZ/4);
+}
+
+/*****************************************************************************
+ *
+ *     locateDevice - start the locate NIC feature of the elected adapter 
+ *
+ * Description:
+ *     This function is used if the user want to locate a particular NIC.
+ *     All LEDs are regularly switched on and off, so the NIC can easily
+ *     be identified.
+ *
+ * Returns:    
+ *     ==0:    everything fine, no error, locateNIC test was started
+ *     !=0:    one locateNIC test runs already
+ *
+ */
+static int locateDevice(struct net_device *dev, u32 data)
+{
+       DEV_NET *pNet = netdev_priv(dev);
+       SK_AC *pAC = pNet->pAC;
+
+       if(!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ))
+               data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ);
+
+       /* start blinking */
+       pAC->LedsOn = 0;
+       mod_timer(&pAC->BlinkTimer, jiffies);
+       msleep_interruptible(data * 1000);
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(data * HZ);
+       del_timer_sync(&pAC->BlinkTimer);
+       toggleLeds(pNet, 0);
+
+       return 0;
+}
+
+/*****************************************************************************
+ *
+ *     getPauseParams - retrieves the pause parameters
+ *
+ * Description:
+ *     All current pause parameters of a selected adapter are placed 
+ *     in the passed ethtool_pauseparam structure and are returned.
+ *
+ * Returns:    N/A
+ *
+ */
+static void getPauseParams(struct net_device *dev, struct ethtool_pauseparam *epause) 
+{
+       DEV_NET *pNet = netdev_priv(dev);
+       SK_AC *pAC = pNet->pAC;
+       SK_GEPORT *pPort = &pAC->GIni.GP[pNet->PortNr];
+
+       epause->rx_pause = (pPort->PFlowCtrlMode == SK_FLOW_MODE_SYMMETRIC) ||
+                 (pPort->PFlowCtrlMode == SK_FLOW_MODE_SYM_OR_REM);
+
+       epause->tx_pause = epause->rx_pause || (pPort->PFlowCtrlMode == SK_FLOW_MODE_LOC_SEND);
+       epause->autoneg = epause->rx_pause || epause->tx_pause;
+}
+
+/*****************************************************************************
+ *
+ *     setPauseParams - configures the pause parameters of an adapter
+ *
+ * Description:
+ *     This function sets the Rx or Tx pause parameters 
+ *
+ * Returns:
+ *     ==0:    everything fine, no error
+ *     !=0:    the return value is the error code of the failure 
+ */
+static int setPauseParams(struct net_device *dev , struct ethtool_pauseparam *epause)
+{
+       DEV_NET *pNet = netdev_priv(dev);
+       SK_AC *pAC = pNet->pAC;
+       SK_GEPORT *pPort = &pAC->GIni.GP[pNet->PortNr];
+       u32     instance = pnmiInstance(pNet);
+       struct ethtool_pauseparam old;
+       u8      oldspeed = pPort->PLinkSpeedUsed;
+       char    buf[4];
+       int     len = 1;
+       int ret;
+
+       /*
+       ** we have to determine the current settings to see if 
+       ** the operator requested any modification of the flow 
+       ** control parameters...
+       */
+       getPauseParams(dev, &old);
+
+       /*
+       ** perform modifications regarding the changes 
+       ** requested by the operator
+       */
+       if (epause->autoneg != old.autoneg) 
+               *buf = epause->autoneg ? SK_FLOW_MODE_NONE : SK_FLOW_MODE_SYMMETRIC;
+       else {
+               if (epause->rx_pause && epause->tx_pause) 
+                       *buf = SK_FLOW_MODE_SYMMETRIC;
+               else if (epause->rx_pause && !epause->tx_pause)
+                       *buf =  SK_FLOW_MODE_SYM_OR_REM;
+               else if (!epause->rx_pause && epause->tx_pause)
+                       *buf =  SK_FLOW_MODE_LOC_SEND;
+               else
+                       *buf = SK_FLOW_MODE_NONE;
+       }
+
+       ret = SkPnmiSetVar(pAC, pAC->IoBase, OID_SKGE_FLOWCTRL_MODE,
+                        &buf, &len, instance, pNet->NetNr);
+
+       if (ret != SK_PNMI_ERR_OK) {
+               SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_CTRL,
+                          ("ethtool (sk98lin): error changing rx/tx pause (%i)\n", ret));
+               goto err;
+       }
+
+       /*
+       ** It may be that autoneg has been disabled! Therefore
+       ** set the speed to the previously used value...
+       */
+       if (!epause->autoneg) {
+               len = 1;
+               ret = SkPnmiSetVar(pAC, pAC->IoBase, OID_SKGE_SPEED_MODE, 
+                                  &oldspeed, &len, instance, pNet->NetNr);
+               if (ret != SK_PNMI_ERR_OK) 
+                       SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_CTRL,
+                                  ("ethtool (sk98lin): error setting speed (%i)\n", ret));
+       }
+ err:
+        return ret ? -EIO : 0;
+}
+
+struct ethtool_ops SkGeEthtoolOps = {
+       .get_settings           = getSettings,
+       .set_settings           = setSettings,
+       .get_drvinfo            = getDriverInfo,
+       .get_strings            = getStrings,
+       .get_stats_count        = getStatsCount,
+       .get_ethtool_stats      = getEthtoolStats,
+       .phys_id                = locateDevice,
+       .get_pauseparam         = getPauseParams,
+       .set_pauseparam         = setPauseParams,
+};
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
new file mode 100644 (file)
index 0000000..968eb32
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * File:       pci-acpi.c
+ * Purpose:    Provide PCI supports in ACPI
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <acpi/acpi.h>
+#include <acpi/acnamesp.h>
+#include <acpi/acresrc.h>
+#include <acpi/acpi_bus.h>
+
+#include <linux/pci-acpi.h>
+
+static u32 ctrlset_buf[3] = {0, 0, 0};
+static u32 global_ctrlsets = 0;
+u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
+
+static acpi_status  
+acpi_query_osc (
+       acpi_handle     handle,
+       u32             level,
+       void            *context,
+       void            **retval )
+{
+       acpi_status             status;
+       struct acpi_object_list input;
+       union acpi_object       in_params[4];
+       struct acpi_buffer      output;
+       union acpi_object       out_obj;        
+       u32                     osc_dw0;
+
+       /* Setting up output buffer */
+       output.length = sizeof(out_obj) + 3*sizeof(u32);  
+       output.pointer = &out_obj;
+       
+       /* Setting up input parameters */
+       input.count = 4;
+       input.pointer = in_params;
+       in_params[0].type               = ACPI_TYPE_BUFFER;
+       in_params[0].buffer.length      = 16;
+       in_params[0].buffer.pointer     = OSC_UUID;
+       in_params[1].type               = ACPI_TYPE_INTEGER;
+       in_params[1].integer.value      = 1;
+       in_params[2].type               = ACPI_TYPE_INTEGER;
+       in_params[2].integer.value      = 3;
+       in_params[3].type               = ACPI_TYPE_BUFFER;
+       in_params[3].buffer.length      = 12;
+       in_params[3].buffer.pointer     = (u8 *)context;
+
+       status = acpi_evaluate_object(handle, "_OSC", &input, &output);
+       if (ACPI_FAILURE (status)) {
+               printk(KERN_DEBUG  
+                       "Evaluate _OSC Set fails. Status = 0x%04x\n", status);
+               return status;
+       }
+       if (out_obj.type != ACPI_TYPE_BUFFER) {
+               printk(KERN_DEBUG  
+                       "Evaluate _OSC returns wrong type\n");
+               return AE_TYPE;
+       }
+       osc_dw0 = *((u32 *) out_obj.buffer.pointer);
+       if (osc_dw0) {
+               if (osc_dw0 & OSC_REQUEST_ERROR)
+                       printk(KERN_DEBUG "_OSC request fails\n"); 
+               if (osc_dw0 & OSC_INVALID_UUID_ERROR)
+                       printk(KERN_DEBUG "_OSC invalid UUID\n"); 
+               if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
+                       printk(KERN_DEBUG "_OSC invalid revision\n"); 
+               if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
+                       /* Update Global Control Set */
+                       global_ctrlsets = *((u32 *)(out_obj.buffer.pointer+8));
+                       return AE_OK;
+               }
+               return AE_ERROR;
+       }
+
+       /* Update Global Control Set */
+       global_ctrlsets = *((u32 *)(out_obj.buffer.pointer + 8));
+       return AE_OK;
+}
+
+
+static acpi_status  
+acpi_run_osc (
+       acpi_handle     handle,
+       u32             level,
+       void            *context,
+       void            **retval )
+{
+       acpi_status             status;
+       struct acpi_object_list input;
+       union acpi_object       in_params[4];
+       struct acpi_buffer      output;
+       union acpi_object       out_obj;        
+       u32                     osc_dw0;
+
+       /* Setting up output buffer */
+       output.length = sizeof(out_obj) + 3*sizeof(u32);  
+       output.pointer = &out_obj;
+       
+       /* Setting up input parameters */
+       input.count = 4;
+       input.pointer = in_params;
+       in_params[0].type               = ACPI_TYPE_BUFFER;
+       in_params[0].buffer.length      = 16;
+       in_params[0].buffer.pointer     = OSC_UUID;
+       in_params[1].type               = ACPI_TYPE_INTEGER;
+       in_params[1].integer.value      = 1;
+       in_params[2].type               = ACPI_TYPE_INTEGER;
+       in_params[2].integer.value      = 3;
+       in_params[3].type               = ACPI_TYPE_BUFFER;
+       in_params[3].buffer.length      = 12;
+       in_params[3].buffer.pointer     = (u8 *)context;
+
+       status = acpi_evaluate_object(handle, "_OSC", &input, &output);
+       if (ACPI_FAILURE (status)) {
+               printk(KERN_DEBUG  
+                       "Evaluate _OSC Set fails. Status = 0x%04x\n", status);
+               return status;
+       }
+       if (out_obj.type != ACPI_TYPE_BUFFER) {
+               printk(KERN_DEBUG  
+                       "Evaluate _OSC returns wrong type\n");
+               return AE_TYPE;
+       }
+       osc_dw0 = *((u32 *) out_obj.buffer.pointer);
+       if (osc_dw0) {
+               if (osc_dw0 & OSC_REQUEST_ERROR)
+                       printk(KERN_DEBUG "_OSC request fails\n"); 
+               if (osc_dw0 & OSC_INVALID_UUID_ERROR)
+                       printk(KERN_DEBUG "_OSC invalid UUID\n"); 
+               if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
+                       printk(KERN_DEBUG "_OSC invalid revision\n"); 
+               if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
+                       printk(KERN_DEBUG "_OSC FW not grant req. control\n");
+                       return AE_SUPPORT;
+               }
+               return AE_ERROR;
+       }
+       return AE_OK;
+}
+
+/**
+ * pci_osc_support_set - register OS support to Firmware
+ * @flags: OS support bits
+ *
+ * Update OS support fields and doing a _OSC Query to obtain an update
+ * from Firmware on supported control bits.
+ **/
+acpi_status pci_osc_support_set(u32 flags)
+{
+       u32 temp;
+
+       if (!(flags & OSC_SUPPORT_MASKS)) {
+               return AE_TYPE;
+       }
+       ctrlset_buf[OSC_SUPPORT_TYPE] |= (flags & OSC_SUPPORT_MASKS);
+
+       /* do _OSC query for all possible controls */
+       temp = ctrlset_buf[OSC_CONTROL_TYPE];
+       ctrlset_buf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
+       ctrlset_buf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
+       acpi_get_devices ( PCI_ROOT_HID_STRING,
+                       acpi_query_osc,
+                       ctrlset_buf,
+                       NULL );
+       ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE;
+       ctrlset_buf[OSC_CONTROL_TYPE] = temp;
+       return AE_OK;
+}
+EXPORT_SYMBOL(pci_osc_support_set);
+
+/**
+ * pci_osc_control_set - commit requested control to Firmware
+ * @flags: driver's requested control bits
+ *
+ * Attempt to take control from Firmware on requested control bits.
+ **/
+acpi_status pci_osc_control_set(u32 flags)
+{
+       acpi_status     status;
+       u32             ctrlset;
+
+       ctrlset = (flags & OSC_CONTROL_MASKS);
+       if (!ctrlset) {
+               return AE_TYPE;
+       }
+       if (ctrlset_buf[OSC_SUPPORT_TYPE] && 
+               ((global_ctrlsets & ctrlset) != ctrlset)) {
+               return AE_SUPPORT;
+       }
+       ctrlset_buf[OSC_CONTROL_TYPE] |= ctrlset;
+       status = acpi_get_devices ( PCI_ROOT_HID_STRING,
+                               acpi_run_osc,
+                               ctrlset_buf,
+                               NULL );
+       if (ACPI_FAILURE (status)) {
+               ctrlset_buf[OSC_CONTROL_TYPE] &= ~ctrlset;
+       }
+       
+       return status;
+}
+EXPORT_SYMBOL(pci_osc_control_set);
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
new file mode 100644 (file)
index 0000000..6258a9c
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# PCI Express Port Bus Configuration
+#
+config PCIEPORTBUS
+       bool "PCI Express support"
+       depends on PCI_GOMMCONFIG || PCI_GOANY
+       default n
+
+       ---help---
+       This automatically enables PCI Express Port Bus support. Users can
+       choose Native Hot-Plug support, Advanced Error Reporting support,
+       Power Management Event support and Virtual Channel support to run
+       on PCI Express Ports (Root or Switch).
+
+#
+# Include service Kconfig here
+#
+config HOTPLUG_PCI_PCIE
+       tristate "PCI Express Hotplug driver"
+       depends on HOTPLUG_PCI && PCIEPORTBUS
+       help
+         Say Y here if you have a motherboard that supports PCI Express Native
+         Hotplug
+
+         To compile this driver as a module, choose M here: the
+         module will be called pciehp.
+
+         When in doubt, say N.
+
+config HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
+       bool "Use polling mechanism for hot-plug events (for testing purpose)"
+       depends on HOTPLUG_PCI_PCIE
+       help
+         Say Y here if you want to use the polling mechanism for hot-plug 
+         events for early platform testing.
+          
+         When in doubt, say N.
+
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
new file mode 100644 (file)
index 0000000..984fa87
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for PCI-Express PORT Driver
+#
+
+pcieportdrv-y                  := portdrv_core.o portdrv_pci.o portdrv_bus.o
+
+obj-$(CONFIG_PCIEPORTBUS)      += pcieportdrv.o
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
new file mode 100644 (file)
index 0000000..6c3ad89
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * File:       portdrv.h
+ * Purpose:    PCI Express Port Bus Driver's Internal Data Structures
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#ifndef _PORTDRV_H_
+#define _PORTDRV_H_
+
+#if !defined(PCI_CAP_ID_PME)
+#define PCI_CAP_ID_PME                 1
+#endif
+
+#if !defined(PCI_CAP_ID_EXP)
+#define PCI_CAP_ID_EXP                 0x10
+#endif
+
+#define PORT_TYPE_MASK                 0xf
+#define PORT_TO_SLOT_MASK              0x100
+#define SLOT_HP_CAPABLE_MASK           0x40
+#define PCIE_CAPABILITIES_REG          0x2
+#define PCIE_SLOT_CAPABILITIES_REG     0x14
+#define PCIE_PORT_DEVICE_MAXSERVICES   4
+#define PCI_CFG_SPACE_SIZE             256
+
+#define get_descriptor_id(type, service) (((type - 4) << 4) | service)
+
+extern struct bus_type pcie_port_bus_type;
+extern int pcie_port_device_probe(struct pci_dev *dev);
+extern int pcie_port_device_register(struct pci_dev *dev);
+#ifdef CONFIG_PM
+extern int pcie_port_device_suspend(struct pci_dev *dev, u32 state);
+extern int pcie_port_device_resume(struct pci_dev *dev);
+#endif
+extern void pcie_port_device_remove(struct pci_dev *dev);
+extern void pcie_port_bus_register(void);
+extern void pcie_port_bus_unregister(void);
+
+#endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
new file mode 100644 (file)
index 0000000..9a02f28
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * File:       portdrv_bus.c
+ * Purpose:    PCI Express Port Bus Driver's Bus Overloading Functions
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+
+#include <linux/pcieport_if.h>
+
+static int pcie_port_bus_match(struct device *dev, struct device_driver *drv);
+static int pcie_port_bus_suspend(struct device *dev, u32 state);
+static int pcie_port_bus_resume(struct device *dev);
+
+struct bus_type pcie_port_bus_type = {
+       .name           = "pci_express",
+       .match          = pcie_port_bus_match,
+       .suspend        = pcie_port_bus_suspend,
+       .resume         = pcie_port_bus_resume, 
+};
+
+static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct pcie_device *pciedev;
+       struct pcie_port_service_driver *driver;
+
+       if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
+               return 0;
+       
+       pciedev = to_pcie_device(dev);
+       driver = to_service_driver(drv);
+       if (   (driver->id_table->vendor != PCI_ANY_ID && 
+               driver->id_table->vendor != pciedev->id.vendor) ||
+              (driver->id_table->device != PCI_ANY_ID &&
+               driver->id_table->device != pciedev->id.device) ||      
+               driver->id_table->port_type != pciedev->id.port_type ||
+               driver->id_table->service_type != pciedev->id.service_type )
+               return 0;
+
+       return 1;
+}
+
+static int pcie_port_bus_suspend(struct device *dev, u32 state)
+{
+       struct pcie_device *pciedev;
+       struct pcie_port_service_driver *driver;
+
+       if (!dev || !dev->driver)
+               return 0;
+
+       pciedev = to_pcie_device(dev);
+       driver = to_service_driver(dev->driver);
+       if (driver && driver->suspend)
+               driver->suspend(pciedev, state);
+       return 0;
+}
+
+static int pcie_port_bus_resume(struct device *dev)
+{
+       struct pcie_device *pciedev;
+       struct pcie_port_service_driver *driver;
+
+       if (!dev || !dev->driver)
+               return 0;
+
+       pciedev = to_pcie_device(dev);
+       driver = to_service_driver(dev->driver);
+       if (driver && driver->resume)
+               driver->resume(pciedev);
+       return 0;
+}
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
new file mode 100644 (file)
index 0000000..127f64f
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * File:       portdrv_core.c
+ * Purpose:    PCI Express Port Bus Driver's Core Functions
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/pcieport_if.h>
+
+#include "portdrv.h"
+
+extern int pcie_mch_quirk;     /* MSI-quirk Indicator */
+
+static int pcie_port_probe_service(struct device *dev)
+{
+       struct pcie_device *pciedev;
+       struct pcie_port_service_driver *driver;
+       int status = -ENODEV;
+
+       if (!dev || !dev->driver)
+               return status;
+
+       driver = to_service_driver(dev->driver);
+       if (!driver || !driver->probe)
+               return status;
+
+       pciedev = to_pcie_device(dev);
+       status = driver->probe(pciedev, driver->id_table);
+       if (!status) {
+               printk(KERN_DEBUG "Load service driver %s on pcie device %s\n",
+                       driver->name, dev->bus_id);
+               get_device(dev);
+       }
+       return status;
+}
+
+static int pcie_port_remove_service(struct device *dev)
+{
+       struct pcie_device *pciedev;
+       struct pcie_port_service_driver *driver;
+
+       if (!dev || !dev->driver)
+               return 0;
+
+       pciedev = to_pcie_device(dev);
+       driver = to_service_driver(dev->driver);
+       if (driver && driver->remove) { 
+               printk(KERN_DEBUG "Unload service driver %s on pcie device %s\n",
+                       driver->name, dev->bus_id);
+               driver->remove(pciedev);
+               put_device(dev);
+       }
+       return 0;
+}
+
+static void pcie_port_shutdown_service(struct device *dev) {}
+
+static int pcie_port_suspend_service(struct device *dev, u32 state, u32 level)
+{
+       struct pcie_device *pciedev;
+       struct pcie_port_service_driver *driver;
+
+       if (!dev || !dev->driver)
+               return 0;
+
+       pciedev = to_pcie_device(dev);
+       driver = to_service_driver(dev->driver);
+       if (driver && driver->suspend)
+               driver->suspend(pciedev, state);
+       return 0;
+}
+
+static int pcie_port_resume_service(struct device *dev, u32 state)
+{
+       struct pcie_device *pciedev;
+       struct pcie_port_service_driver *driver;
+
+       if (!dev || !dev->driver)
+               return 0;
+
+       pciedev = to_pcie_device(dev);
+       driver = to_service_driver(dev->driver);
+
+       if (driver && driver->resume)
+               driver->resume(pciedev);
+       return 0;
+}
+
+/*
+ * release_pcie_device
+ *     
+ *     Being invoked automatically when device is being removed 
+ *     in response to device_unregister(dev) call.
+ *     Release all resources being claimed.
+ */
+static void release_pcie_device(struct device *dev)
+{
+       printk(KERN_DEBUG "Free Port Service[%s]\n", dev->bus_id);
+       kfree(to_pcie_device(dev));                     
+}
+
+static int is_msi_quirked(struct pci_dev *dev)
+{
+       int port_type, quirk = 0;
+       u16 reg16;
+
+       pci_read_config_word(dev, 
+               pci_find_capability(dev, PCI_CAP_ID_EXP) + 
+               PCIE_CAPABILITIES_REG, &reg16);
+       port_type = (reg16 >> 4) & PORT_TYPE_MASK;
+       switch(port_type) {
+       case PCIE_RC_PORT:
+               if (pcie_mch_quirk == 1)
+                       quirk = 1;
+               break;
+       case PCIE_SW_UPSTREAM_PORT:
+       case PCIE_SW_DOWNSTREAM_PORT:
+       default:
+               break;  
+       }
+       return quirk;
+}
+       
+static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
+{
+       int i, pos, nvec, status = -EINVAL;
+       int interrupt_mode = PCIE_PORT_INTx_MODE;
+
+       /* Set INTx as default */
+       for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
+               if (mask & (1 << i)) 
+                       nvec++;
+               vectors[i] = dev->irq;
+       }
+       
+       /* Check MSI quirk */
+       if (is_msi_quirked(dev))
+               return interrupt_mode;
+
+       /* Select MSI-X over MSI if supported */                
+       pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
+       if (pos) {
+               struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] = 
+                       {{0, 0}, {0, 1}, {0, 2}, {0, 3}};
+               printk("%s Found MSIX capability\n", __FUNCTION__);
+               status = pci_enable_msix(dev, msix_entries, nvec);
+               if (!status) {
+                       int j = 0;
+
+                       interrupt_mode = PCIE_PORT_MSIX_MODE;
+                       for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
+                               if (mask & (1 << i)) 
+                                       vectors[i] = msix_entries[j++].vector;
+                       }
+               }
+       } 
+       if (status) {
+               pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
+               if (pos) {
+                       printk("%s Found MSI capability\n", __FUNCTION__);
+                       status = pci_enable_msi(dev);
+                       if (!status) {
+                               interrupt_mode = PCIE_PORT_MSI_MODE;
+                               for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++)
+                                       vectors[i] = dev->irq;
+                       }
+               }
+       } 
+       return interrupt_mode;
+}
+
+static int get_port_device_capability(struct pci_dev *dev)
+{
+       int services = 0, pos;
+       u16 reg16;
+       u32 reg32;
+
+       pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+       pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg16);
+       /* Hot-Plug Capable */
+       if (reg16 & PORT_TO_SLOT_MASK) {
+               pci_read_config_dword(dev, 
+                       pos + PCIE_SLOT_CAPABILITIES_REG, &reg32);
+               if (reg32 & SLOT_HP_CAPABLE_MASK)
+                       services |= PCIE_PORT_SERVICE_HP;
+       } 
+       /* PME Capable */
+       pos = pci_find_capability(dev, PCI_CAP_ID_PME);
+       if (pos) 
+               services |= PCIE_PORT_SERVICE_PME;
+       
+       pos = PCI_CFG_SPACE_SIZE;
+       while (pos) {
+               pci_read_config_dword(dev, pos, &reg32);
+               switch (reg32 & 0xffff) {
+               case PCI_EXT_CAP_ID_ERR:
+                       services |= PCIE_PORT_SERVICE_AER;
+                       pos = reg32 >> 20;
+                       break;
+               case PCI_EXT_CAP_ID_VC:
+                       services |= PCIE_PORT_SERVICE_VC;
+                       pos = reg32 >> 20;
+                       break;
+               default:
+                       pos = 0;
+                       break;
+               }
+       }
+
+       return services;
+}
+
+static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, 
+       int port_type, int service_type, int irq, int irq_mode)
+{
+       struct device *device;
+
+       dev->port = parent;
+       dev->interrupt_mode = irq_mode;
+       dev->irq = irq;
+       dev->id.vendor = parent->vendor;
+       dev->id.device = parent->device;
+       dev->id.port_type = port_type;
+       dev->id.service_type = (1 << service_type);
+
+       /* Initialize generic device interface */
+       device = &dev->device;
+       memset(device, 0, sizeof(struct device));
+       INIT_LIST_HEAD(&device->node);
+       INIT_LIST_HEAD(&device->children);
+       INIT_LIST_HEAD(&device->bus_list);
+       device->bus = &pcie_port_bus_type;
+       device->driver = NULL;
+       device->driver_data = NULL; 
+       device->release = release_pcie_device;  /* callback to free pcie dev */
+       sprintf(&device->bus_id[0], "pcie%02x", 
+               get_descriptor_id(port_type, service_type));
+       device->parent = &parent->dev;
+}
+
+static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, 
+       int port_type, int service_type, int irq, int irq_mode)
+{
+       struct pcie_device *device;
+
+       device = kmalloc(sizeof(struct pcie_device), GFP_KERNEL);
+       if (!device)
+               return NULL;
+
+       memset(device, 0, sizeof(struct pcie_device));
+       pcie_device_init(parent, device, port_type, service_type, irq,irq_mode);
+       printk(KERN_DEBUG "Allocate Port Service[%s]\n", device->device.bus_id);
+       return device;
+}
+
+int pcie_port_device_probe(struct pci_dev *dev)
+{
+       int pos, type;
+       u16 reg;
+
+       if (!(pos = pci_find_capability(dev, PCI_CAP_ID_EXP)))
+               return -ENODEV;
+
+       pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg);
+       type = (reg >> 4) & PORT_TYPE_MASK;
+       if (    type == PCIE_RC_PORT || type == PCIE_SW_UPSTREAM_PORT ||
+               type == PCIE_SW_DOWNSTREAM_PORT )  
+               return 0;
+       return -ENODEV;
+}
+
+int pcie_port_device_register(struct pci_dev *dev)
+{
+       int status, type, capabilities, irq_mode, i;
+       int vectors[PCIE_PORT_DEVICE_MAXSERVICES];
+       u16 reg16;
+
+       /* Get port type */
+       pci_read_config_word(dev, 
+               pci_find_capability(dev, PCI_CAP_ID_EXP) + 
+               PCIE_CAPABILITIES_REG, &reg16);
+       type = (reg16 >> 4) & PORT_TYPE_MASK;
+
+       /* Now get port services */
+       capabilities = get_port_device_capability(dev);
+       irq_mode = assign_interrupt_mode(dev, vectors, capabilities);
+
+       /* Allocate child services if any */
+       for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
+               struct pcie_device *child;
+
+               if (capabilities & (1 << i)) {
+                       child = alloc_pcie_device(
+                               dev,            /* parent */
+                               type,           /* port type */ 
+                               i,              /* service type */
+                               vectors[i],     /* irq */
+                               irq_mode        /* interrupt mode */);
+                       if (child) { 
+                               status = device_register(&child->device);
+                               if (status) {
+                                       kfree(child);
+                                       continue;
+                               }
+                               get_device(&child->device);
+                       }
+               }
+       }
+       return 0;
+}
+
+#ifdef CONFIG_PM
+int pcie_port_device_suspend(struct pci_dev *dev, u32 state)
+{
+       struct list_head                *head, *tmp;
+       struct device                   *parent, *child;
+       struct device_driver            *driver;
+       struct pcie_port_service_driver *service_driver;
+
+       parent = &dev->dev;
+       head = &parent->children;
+       tmp = head->next;
+       while (head != tmp) {
+               child = container_of(tmp, struct device, node);
+               tmp = tmp->next;
+               if (child->bus != &pcie_port_bus_type)
+                       continue;
+               driver = child->driver;
+               if (!driver)
+                       continue;
+               service_driver = to_service_driver(driver);
+               if (service_driver->suspend)  
+                       service_driver->suspend(to_pcie_device(child), state);
+       }
+       return 0; 
+}
+
+int pcie_port_device_resume(struct pci_dev *dev) 
+{ 
+       struct list_head                *head, *tmp;
+       struct device                   *parent, *child;
+       struct device_driver            *driver;
+       struct pcie_port_service_driver *service_driver;
+
+       parent = &dev->dev;
+       head = &parent->children;
+       tmp = head->next;
+       while (head != tmp) {
+               child = container_of(tmp, struct device, node);
+               tmp = tmp->next;
+               if (child->bus != &pcie_port_bus_type)
+                       continue;
+               driver = child->driver;
+               if (!driver)
+                       continue;
+               service_driver = to_service_driver(driver);
+               if (service_driver->resume)  
+                       service_driver->resume(to_pcie_device(child));
+       }
+       return 0; 
+
+}
+#endif
+
+void pcie_port_device_remove(struct pci_dev *dev)
+{
+       struct list_head                *head, *tmp;
+       struct device                   *parent, *child;
+       struct device_driver            *driver;
+       struct pcie_port_service_driver *service_driver;
+       int interrupt_mode = PCIE_PORT_INTx_MODE;
+
+       parent = &dev->dev;
+       head = &parent->children;
+       tmp = head->next;
+       while (head != tmp) {
+               child = container_of(tmp, struct device, node);
+               tmp = tmp->next;
+               if (child->bus != &pcie_port_bus_type)
+                       continue;
+               driver = child->driver;
+               if (driver) { 
+                       service_driver = to_service_driver(driver);
+                       if (service_driver->remove)  
+                               service_driver->remove(to_pcie_device(child));
+               }
+               interrupt_mode = (to_pcie_device(child))->interrupt_mode;
+               put_device(child);
+               device_unregister(child);
+       }
+       /* Switch to INTx by default if MSI enabled */
+       if (interrupt_mode == PCIE_PORT_MSIX_MODE)
+               pci_disable_msix(dev);
+       else if (interrupt_mode == PCIE_PORT_MSI_MODE)
+               pci_disable_msi(dev);
+}
+
+void pcie_port_bus_register(void)
+{
+       bus_register(&pcie_port_bus_type);
+}
+
+void pcie_port_bus_unregister(void)
+{
+       bus_unregister(&pcie_port_bus_type);
+}
+
+int pcie_port_service_register(struct pcie_port_service_driver *new)
+{
+       new->driver.name = (char *)new->name;
+       new->driver.bus = &pcie_port_bus_type;
+       new->driver.probe = pcie_port_probe_service;
+       new->driver.remove = pcie_port_remove_service;
+       new->driver.shutdown = pcie_port_shutdown_service;
+       new->driver.suspend = pcie_port_suspend_service;
+       new->driver.resume = pcie_port_resume_service;
+
+       return driver_register(&new->driver);
+} 
+
+void pcie_port_service_unregister(struct pcie_port_service_driver *new)
+{
+       driver_unregister(&new->driver);
+}
+
+EXPORT_SYMBOL(pcie_port_service_register);
+EXPORT_SYMBOL(pcie_port_service_unregister);
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
new file mode 100644 (file)
index 0000000..daccaf5
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * File:       portdrv_pci.c
+ * Purpose:    PCI Express Port Bus Driver
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/pcieport_if.h>
+
+#include "portdrv.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
+#define DRIVER_DESC "PCIE Port Bus Driver"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* global data */
+static const char device_name[] = "pcieport-driver";
+
+/*
+ * pcie_portdrv_probe - Probe PCI-Express port devices
+ * @dev: PCI-Express port device being probed
+ *
+ * If detected invokes the pcie_port_device_register() method for 
+ * this port device.
+ *
+ */
+static int __devinit pcie_portdrv_probe (struct pci_dev *dev, 
+                               const struct pci_device_id *id )
+{
+       int                     status;
+
+       status = pcie_port_device_probe(dev);
+       if (status)
+               return status;
+
+       if (pci_enable_device(dev) < 0) 
+               return -ENODEV;
+       
+       pci_set_master(dev);
+        if (!dev->irq) {
+               printk(KERN_WARNING 
+               "%s->Dev[%04x:%04x] has invalid IRQ. Check vendor BIOS\n", 
+               __FUNCTION__, dev->device, dev->vendor);
+       }
+       if (pcie_port_device_register(dev)) 
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void pcie_portdrv_remove (struct pci_dev *dev)
+{
+       pcie_port_device_remove(dev);
+}
+
+#ifdef CONFIG_PM
+static int pcie_portdrv_suspend (struct pci_dev *dev, u32 state)
+{
+       return pcie_port_device_suspend(dev, state);
+}
+
+static int pcie_portdrv_resume (struct pci_dev *dev)
+{
+       return pcie_port_device_resume(dev);
+}
+#endif
+
+/*
+ * LINUX Device Driver Model
+ */
+static const struct pci_device_id port_pci_ids[] = { {
+       /* handle any PCI-Express port */
+       PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0),
+       }, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, port_pci_ids);
+
+static struct pci_driver pcie_portdrv = {
+       .name           = (char *)device_name,
+       .id_table       = &port_pci_ids[0],
+
+       .probe          = pcie_portdrv_probe,
+       .remove         = pcie_portdrv_remove,
+
+#ifdef CONFIG_PM
+       .suspend        = pcie_portdrv_suspend,
+       .resume         = pcie_portdrv_resume,
+#endif /* PM */
+};
+
+static int __init pcie_portdrv_init(void)
+{
+       int retval = 0;
+
+       pcie_port_bus_register();
+       retval = pci_module_init(&pcie_portdrv);
+       if (retval)
+               pcie_port_bus_unregister();
+       return retval;
+}
+
+static void __exit pcie_portdrv_exit(void) 
+{
+       pci_unregister_driver(&pcie_portdrv);
+       pcie_port_bus_unregister();
+}
+
+module_init(pcie_portdrv_init);
+module_exit(pcie_portdrv_exit);
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
new file mode 100644 (file)
index 0000000..54e4ba7
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * drivers/pci/rom.c
+ *
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
+ * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
+ *
+ * PCI ROM access routines
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include "pci.h"
+
+/**
+ * pci_enable_rom - enable ROM decoding for a PCI device
+ * @dev: PCI device to enable
+ *
+ * Enable ROM decoding on @dev.  This involves simply turning on the last
+ * bit of the PCI ROM BAR.  Note that some cards may share address decoders
+ * between the ROM and other resources, so enabling it may disable access
+ * to MMIO registers or other card memory.
+ */
+static void
+pci_enable_rom(struct pci_dev *pdev)
+{
+       u32 rom_addr;
+       
+       pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+       rom_addr |= PCI_ROM_ADDRESS_ENABLE;
+       pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_disable_rom - disable ROM decoding for a PCI device
+ * @dev: PCI device to disable
+ *
+ * Disable ROM decoding on a PCI device by turning off the last bit in the
+ * ROM BAR.
+ */
+static void
+pci_disable_rom(struct pci_dev *pdev)
+{
+       u32 rom_addr;
+       pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
+       rom_addr &= ~PCI_ROM_ADDRESS_ENABLE;
+       pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
+}
+
+/**
+ * pci_map_rom - map a PCI ROM to kernel space
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
+{
+       struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+       loff_t start;
+       void __iomem *rom;
+       void __iomem *image;
+       int last_image;
+       
+       if (res->flags & IORESOURCE_ROM_SHADOW) {       /* IORESOURCE_ROM_SHADOW only set on x86 */
+               start = (loff_t)0xC0000;        /* primary video rom always starts here */
+               *size = 0x20000;                /* cover C000:0 through E000:0 */
+       } else {
+               if (res->flags & IORESOURCE_ROM_COPY) {
+                       *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+                       return (void __iomem *)pci_resource_start(pdev, PCI_ROM_RESOURCE);
+               } else {
+                       /* assign the ROM an address if it doesn't have one */
+                       if (res->parent == NULL)
+                               pci_assign_resource(pdev, PCI_ROM_RESOURCE);
+       
+                       start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
+                       *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
+                       if (*size == 0)
+                               return NULL;
+                       
+                       /* Enable ROM space decodes */
+                       pci_enable_rom(pdev);
+               }
+       }
+       
+       rom = ioremap(start, *size);
+       if (!rom) {
+               /* restore enable if ioremap fails */
+               if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+                       pci_disable_rom(pdev);
+               return NULL;
+       }               
+
+       /* Try to find the true size of the ROM since sometimes the PCI window */
+       /* size is much larger than the actual size of the ROM. */
+       /* True size is important if the ROM is going to be copied. */
+       image = rom;
+       do {
+               void __iomem *pds;
+               /* Standard PCI ROMs start out with these bytes 55 AA */
+               if (readb(image) != 0x55)
+                       break;
+               if (readb(image + 1) != 0xAA)
+                       break;
+               /* get the PCI data structure and check its signature */
+               pds = image + readw(image + 24);
+               if (readb(pds) != 'P')
+                       break;
+               if (readb(pds + 1) != 'C')
+                       break;
+               if (readb(pds + 2) != 'I')
+                       break;
+               if (readb(pds + 3) != 'R')
+                       break;
+               last_image = readb(pds + 21) & 0x80;
+               /* this length is reliable */
+               image += readw(pds + 16) * 512;
+       } while (!last_image);
+
+       *size = image - rom;
+
+       return rom;
+}
+
+/**
+ * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
+ * @dev: pointer to pci device struct
+ * @size: pointer to receive size of pci window over ROM
+ * @return: kernel virtual pointer to image of ROM
+ *
+ * Map a PCI ROM into kernel space. If ROM is boot video ROM,
+ * the shadow BIOS copy will be returned instead of the 
+ * actual ROM.
+ */
+void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
+{
+       struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+       void __iomem *rom;
+       
+       rom = pci_map_rom(pdev, size);
+       if (!rom)
+               return NULL;
+               
+       if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW))
+               return rom;
+               
+       res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
+       if (!res->start) 
+               return rom;
+
+       res->end = res->start + *size; 
+       memcpy_fromio((void*)res->start, rom, *size);
+       pci_unmap_rom(pdev, rom);
+       res->flags |= IORESOURCE_ROM_COPY;
+       
+       return (void __iomem *)res->start;
+}
+
+/**
+ * pci_unmap_rom - unmap the ROM from kernel space
+ * @dev: pointer to pci device struct
+ * @rom: virtual address of the previous mapping
+ *
+ * Remove a mapping of a previously mapped ROM
+ */
+void 
+pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
+{
+       struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+
+       if (res->flags & IORESOURCE_ROM_COPY)
+               return;
+               
+       iounmap(rom);
+               
+       /* Disable again before continuing, leave enabled if pci=rom */
+       if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW)))
+               pci_disable_rom(pdev);
+}
+
+/**
+ * pci_remove_rom - disable the ROM and remove its sysfs attribute
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_remove_rom(struct pci_dev *pdev) 
+{
+       struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+       
+       if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
+               sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
+       if (!(res->flags & (IORESOURCE_ROM_ENABLE | IORESOURCE_ROM_SHADOW | IORESOURCE_ROM_COPY)))
+               pci_disable_rom(pdev);
+}
+
+/**
+ * pci_cleanup_rom - internal routine for freeing the ROM copy created 
+ * by pci_map_rom_copy called from remove.c
+ * @dev: pointer to pci device struct
+ *
+ */
+void 
+pci_cleanup_rom(struct pci_dev *pdev) 
+{
+       struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
+       if (res->flags & IORESOURCE_ROM_COPY) {
+               kfree((void*)res->start);
+               res->flags &= ~IORESOURCE_ROM_COPY;
+               res->start = 0;
+               res->end = 0;
+       }
+}
+
+EXPORT_SYMBOL(pci_map_rom);
+EXPORT_SYMBOL(pci_map_rom_copy);
+EXPORT_SYMBOL(pci_unmap_rom);
+EXPORT_SYMBOL(pci_remove_rom);
diff --git a/drivers/pcmcia/au1000_db1x00.c b/drivers/pcmcia/au1000_db1x00.c
new file mode 100644 (file)
index 0000000..42cf8bf
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ *
+ * Alchemy Semi Db1x00 boards specific pcmcia routines.
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *             ppopov@mvista.com or source@mvista.com
+ *
+ * Copyright 2004 Pete Popov, updated the driver to 2.6.
+ * Followed the sa11xx API and largely copied many of the hardware
+ * independent functions.
+ *
+ * ########################################################################
+ *
+ *  This program is free software; you can distribute 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 it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * ########################################################################
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/signal.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-db1x00/db1x00.h>
+
+#include "au1000_generic.h"
+
+#if 0
+#define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args)
+#else
+#define debug(x,args...)
+#endif
+
+static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+
+struct au1000_pcmcia_socket au1000_pcmcia_socket[PCMCIA_NUM_SOCKS];
+extern int au1x00_pcmcia_socket_probe(struct device *, struct pcmcia_low_level *, int, int);
+
+static int db1x00_pcmcia_hw_init(struct au1000_pcmcia_socket *skt)
+{
+#ifdef CONFIG_MIPS_DB1550
+       skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_3;
+#else
+       skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_2;
+#endif
+       return 0;
+}
+
+static void db1x00_pcmcia_shutdown(struct au1000_pcmcia_socket *skt)
+{
+       bcsr->pcmcia = 0; /* turn off power */
+       au_sync_delay(2);
+}
+
+static void
+db1x00_pcmcia_socket_state(struct au1000_pcmcia_socket *skt, struct pcmcia_state *state)
+{
+       u32 inserted;
+       unsigned char vs;
+
+       state->ready = 0;
+       state->vs_Xv = 0;
+       state->vs_3v = 0;
+       state->detect = 0;
+
+       switch (skt->nr) {
+       case 0:
+               vs = bcsr->status & 0x3;
+               inserted = !(bcsr->status & (1<<4));
+               break;
+       case 1:
+               vs = (bcsr->status & 0xC)>>2;
+               inserted = !(bcsr->status & (1<<5));
+               break;
+       default:/* should never happen */
+               return;
+       }
+
+       if (inserted)
+               debug("db1x00 socket %d: inserted %d, vs %d pcmcia %x\n",
+                               skt->nr, inserted, vs, bcsr->pcmcia);
+
+       if (inserted) {
+               switch (vs) {
+                       case 0:
+                       case 2:
+                               state->vs_3v=1;
+                               break;
+                       case 3: /* 5V */
+                               break;
+                       default:
+                               /* return without setting 'detect' */
+                               printk(KERN_ERR "db1x00 bad VS (%d)\n",
+                                               vs);
+               }
+               state->detect = 1;
+               state->ready = 1;
+       }
+       else {
+               /* if the card was previously inserted and then ejected,
+                * we should turn off power to it
+                */
+               if ((skt->nr == 0) && (bcsr->pcmcia & BCSR_PCMCIA_PC0RST)) {
+                       bcsr->pcmcia &= ~(BCSR_PCMCIA_PC0RST |
+                                       BCSR_PCMCIA_PC0DRVEN |
+                                       BCSR_PCMCIA_PC0VPP |
+                                       BCSR_PCMCIA_PC0VCC);
+                       au_sync_delay(10);
+               }
+               else if ((skt->nr == 1) && bcsr->pcmcia & BCSR_PCMCIA_PC1RST) {
+                       bcsr->pcmcia &= ~(BCSR_PCMCIA_PC1RST |
+                                       BCSR_PCMCIA_PC1DRVEN |
+                                       BCSR_PCMCIA_PC1VPP |
+                                       BCSR_PCMCIA_PC1VCC);
+                       au_sync_delay(10);
+               }
+       }
+
+       state->bvd1=1;
+       state->bvd2=1;
+       state->wrprot=0;
+}
+
+static int
+db1x00_pcmcia_configure_socket(struct au1000_pcmcia_socket *skt, struct socket_state_t *state)
+{
+       u16 pwr;
+       int sock = skt->nr;
+
+       debug("config_skt %d Vcc %dV Vpp %dV, reset %d\n",
+                       sock, state->Vcc, state->Vpp,
+                       state->flags & SS_RESET);
+
+       /* pcmcia reg was set to zero at init time. Be careful when
+        * initializing a socket not to wipe out the settings of the
+        * other socket.
+        */
+       pwr = bcsr->pcmcia;
+       pwr &= ~(0xf << sock*8); /* clear voltage settings */
+
+       state->Vpp = 0;
+       switch(state->Vcc){
+               case 0:  /* Vcc 0 */
+                       pwr |= SET_VCC_VPP(0,0,sock);
+                       break;
+               case 50: /* Vcc 5V */
+                       switch(state->Vpp) {
+                               case 0:
+                                       pwr |= SET_VCC_VPP(2,0,sock);
+                                       break;
+                               case 50:
+                                       pwr |= SET_VCC_VPP(2,1,sock);
+                                       break;
+                               case 12:
+                                       pwr |= SET_VCC_VPP(2,2,sock);
+                                       break;
+                               case 33:
+                               default:
+                                       pwr |= SET_VCC_VPP(0,0,sock);
+                                       printk("%s: bad Vcc/Vpp (%d:%d)\n",
+                                                       __FUNCTION__,
+                                                       state->Vcc,
+                                                       state->Vpp);
+                                       break;
+                       }
+                       break;
+               case 33: /* Vcc 3.3V */
+                       switch(state->Vpp) {
+                               case 0:
+                                       pwr |= SET_VCC_VPP(1,0,sock);
+                                       break;
+                               case 12:
+                                       pwr |= SET_VCC_VPP(1,2,sock);
+                                       break;
+                               case 33:
+                                       pwr |= SET_VCC_VPP(1,1,sock);
+                                       break;
+                               case 50:
+                               default:
+                                       pwr |= SET_VCC_VPP(0,0,sock);
+                                       printk("%s: bad Vcc/Vpp (%d:%d)\n",
+                                                       __FUNCTION__,
+                                                       state->Vcc,
+                                                       state->Vpp);
+                                       break;
+                       }
+                       break;
+               default: /* what's this ? */
+                       pwr |= SET_VCC_VPP(0,0,sock);
+                       printk(KERN_ERR "%s: bad Vcc %d\n",
+                                       __FUNCTION__, state->Vcc);
+                       break;
+       }
+
+       bcsr->pcmcia = pwr;
+       au_sync_delay(300);
+
+       if (sock == 0) {
+               if (!(state->flags & SS_RESET)) {
+                       pwr |= BCSR_PCMCIA_PC0DRVEN;
+                       bcsr->pcmcia = pwr;
+                       au_sync_delay(300);
+                       pwr |= BCSR_PCMCIA_PC0RST;
+                       bcsr->pcmcia = pwr;
+                       au_sync_delay(100);
+               }
+               else {
+                       pwr &= ~(BCSR_PCMCIA_PC0RST | BCSR_PCMCIA_PC0DRVEN);
+                       bcsr->pcmcia = pwr;
+                       au_sync_delay(100);
+               }
+       }
+       else {
+               if (!(state->flags & SS_RESET)) {
+                       pwr |= BCSR_PCMCIA_PC1DRVEN;
+                       bcsr->pcmcia = pwr;
+                       au_sync_delay(300);
+                       pwr |= BCSR_PCMCIA_PC1RST;
+                       bcsr->pcmcia = pwr;
+                       au_sync_delay(100);
+               }
+               else {
+                       pwr &= ~(BCSR_PCMCIA_PC1RST | BCSR_PCMCIA_PC1DRVEN);
+                       bcsr->pcmcia = pwr;
+                       au_sync_delay(100);
+               }
+       }
+       return 0;
+}
+
+/*
+ * Enable card status IRQs on (re-)initialisation.  This can
+ * be called at initialisation, power management event, or
+ * pcmcia event.
+ */
+void db1x00_socket_init(struct au1000_pcmcia_socket *skt)
+{
+       /* nothing to do for now */
+}
+
+/*
+ * Disable card status IRQs and PCMCIA bus on suspend.
+ */
+void db1x00_socket_suspend(struct au1000_pcmcia_socket *skt)
+{
+       /* nothing to do for now */
+}
+
+struct pcmcia_low_level db1x00_pcmcia_ops = {
+       .owner                  = THIS_MODULE,
+
+       .hw_init                = db1x00_pcmcia_hw_init,
+       .hw_shutdown            = db1x00_pcmcia_shutdown,
+
+       .socket_state           = db1x00_pcmcia_socket_state,
+       .configure_socket       = db1x00_pcmcia_configure_socket,
+
+       .socket_init            = db1x00_socket_init,
+       .socket_suspend         = db1x00_socket_suspend
+};
+
+int __init au1x_board_init(struct device *dev)
+{
+       int ret = -ENODEV;
+       bcsr->pcmcia = 0; /* turn off power, if it's not already off */
+       au_sync_delay(2);
+       ret = au1x00_pcmcia_socket_probe(dev, &db1x00_pcmcia_ops, 0, 2);
+       return ret;
+}
diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h
new file mode 100644 (file)
index 0000000..417bc15
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Alchemy Semi Au1000 pcmcia driver include file
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *             ppopov@mvista.com or source@mvista.com
+ *
+ *  This program is free software; you can distribute 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 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 __ASM_AU1000_PCMCIA_H
+#define __ASM_AU1000_PCMCIA_H
+
+/* include the world */
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+
+#define AU1000_PCMCIA_POLL_PERIOD    (2*HZ)
+#define AU1000_PCMCIA_IO_SPEED       (255)
+#define AU1000_PCMCIA_MEM_SPEED      (300)
+
+#define AU1X_SOCK0_IO        0xF00000000
+#define AU1X_SOCK0_PHYS_ATTR 0xF40000000
+#define AU1X_SOCK0_PHYS_MEM  0xF80000000
+/* pseudo 32 bit phys addresses, which get fixed up to the
+ * real 36 bit address in fixup_bigphys_addr() */
+#define AU1X_SOCK0_PSEUDO_PHYS_ATTR 0xF4000000
+#define AU1X_SOCK0_PSEUDO_PHYS_MEM  0xF8000000
+
+/* pcmcia socket 1 needs external glue logic so the memory map
+ * differs from board to board.
+ */
+#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1550)
+#define AU1X_SOCK1_IO        0xF08000000
+#define AU1X_SOCK1_PHYS_ATTR 0xF48000000
+#define AU1X_SOCK1_PHYS_MEM  0xF88000000
+#define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4800000
+#define AU1X_SOCK1_PSEUDO_PHYS_MEM  0xF8800000
+#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) || defined(CONFIG_MIPS_DB1500) || defined(CONFIG_MIPS_DB1550)
+#define AU1X_SOCK1_IO        0xF04000000
+#define AU1X_SOCK1_PHYS_ATTR 0xF44000000
+#define AU1X_SOCK1_PHYS_MEM  0xF84000000
+#define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4400000
+#define AU1X_SOCK1_PSEUDO_PHYS_MEM  0xF8400000
+#endif
+
+struct pcmcia_state {
+  unsigned detect: 1,
+            ready: 1,
+           wrprot: 1,
+            bvd1: 1,
+            bvd2: 1,
+            vs_3v: 1,
+            vs_Xv: 1;
+};
+
+struct pcmcia_configure {
+  unsigned sock: 8,
+            vcc: 8,
+            vpp: 8,
+         output: 1,
+        speaker: 1,
+          reset: 1;
+};
+
+struct pcmcia_irqs {
+       int sock;
+       int irq;
+       const char *str;
+};
+
+
+struct au1000_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;
+       ioaddr_t                phys_io;
+       unsigned int            phys_attr;
+       unsigned int            phys_mem;
+       unsigned short          speed_io, speed_attr, speed_mem;
+
+       unsigned int            irq_state;
+
+       struct timer_list       poll_timer;
+};
+
+struct pcmcia_low_level {
+       struct module *owner;
+
+       int (*hw_init)(struct au1000_pcmcia_socket *);
+       void (*hw_shutdown)(struct au1000_pcmcia_socket *);
+
+       void (*socket_state)(struct au1000_pcmcia_socket *, struct pcmcia_state *);
+       int (*configure_socket)(struct au1000_pcmcia_socket *, struct 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 au1000_pcmcia_socket *);
+
+       /*
+        * Disable card status IRQs and PCMCIA bus on suspend.
+        */
+       void (*socket_suspend)(struct au1000_pcmcia_socket *);
+};
+
+extern int au1x_board_init(struct device *dev);
+
+#endif /* __ASM_AU1000_PCMCIA_H */
diff --git a/drivers/pcmcia/au1000_xxs1500.c b/drivers/pcmcia/au1000_xxs1500.c
new file mode 100644 (file)
index 0000000..1dfc776
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ *
+ * MyCable board specific pcmcia routines.
+ *
+ * Copyright 2003 MontaVista Software Inc.
+ * Author: Pete Popov, MontaVista Software, Inc.
+ *             ppopov@mvista.com or source@mvista.com
+ *
+ * ########################################################################
+ *
+ *  This program is free software; you can distribute 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 it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * ########################################################################
+ *
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/tqueue.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/version.h>
+#include <linux/types.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/bus_ops.h>
+#include "cs_internal.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <asm/au1000.h>
+#include <asm/au1000_pcmcia.h>
+#include <asm/xxs1500.h>
+
+#if 0
+#define DEBUG(x,args...)       printk(__FUNCTION__ ": " x,##args)
+#else
+#define DEBUG(x,args...)
+#endif
+
+static int xxs1500_pcmcia_init(struct pcmcia_init *init)
+{
+       return PCMCIA_NUM_SOCKS;
+}
+
+static int xxs1500_pcmcia_shutdown(void)
+{
+       /* turn off power */
+       au_writel(au_readl(GPIO2_PINSTATE) | (1<<14)|(1<<30),
+                       GPIO2_OUTPUT);
+       au_sync_delay(100);
+
+       /* assert reset */
+       au_writel(au_readl(GPIO2_PINSTATE) | (1<<4)|(1<<20),
+                       GPIO2_OUTPUT);
+       au_sync_delay(100);
+       return 0;
+}
+
+
+static int
+xxs1500_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state)
+{
+       u32 inserted; u32 vs;
+       unsigned long gpio, gpio2;
+
+       if(sock > PCMCIA_MAX_SOCK) return -1;
+
+       gpio = au_readl(SYS_PINSTATERD);
+       gpio2 = au_readl(GPIO2_PINSTATE);
+
+       vs = gpio2 & ((1<<8) | (1<<9));
+       inserted = (!(gpio & 0x1) && !(gpio & 0x2));
+
+       state->ready = 0;
+       state->vs_Xv = 0;
+       state->vs_3v = 0;
+       state->detect = 0;
+
+       if (inserted) {
+               switch (vs) {
+                       case 0:
+                       case 1:
+                       case 2:
+                               state->vs_3v=1;
+                               break;
+                       case 3: /* 5V */
+                       default:
+                               /* return without setting 'detect' */
+                               printk(KERN_ERR "au1x00_cs: unsupported VS\n",
+                                               vs);
+                               return;
+               }
+               state->detect = 1;
+       }
+
+       if (state->detect) {
+               state->ready = 1;
+       }
+
+       state->bvd1= gpio2 & (1<<10);
+       state->bvd2 = gpio2 & (1<<11);
+       state->wrprot=0;
+       return 1;
+}
+
+
+static int xxs1500_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
+{
+
+       if(info->sock > PCMCIA_MAX_SOCK) return -1;
+       info->irq = PCMCIA_IRQ;
+       return 0;
+}
+
+
+static int
+xxs1500_pcmcia_configure_socket(const struct pcmcia_configure *configure)
+{
+
+       if(configure->sock > PCMCIA_MAX_SOCK) return -1;
+
+       DEBUG("Vcc %dV Vpp %dV, reset %d\n",
+                       configure->vcc, configure->vpp, configure->reset);
+
+       switch(configure->vcc){
+               case 33: /* Vcc 3.3V */
+                       /* turn on power */
+                       DEBUG("turn on power\n");
+                       au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<14))|(1<<30),
+                                       GPIO2_OUTPUT);
+                       au_sync_delay(100);
+                       break;
+               case 50: /* Vcc 5V */
+               default: /* what's this ? */
+                       printk(KERN_ERR "au1x00_cs: unsupported VCC\n");
+               case 0:  /* Vcc 0 */
+                       /* turn off power */
+                       au_sync_delay(100);
+                       au_writel(au_readl(GPIO2_PINSTATE) | (1<<14)|(1<<30),
+                                       GPIO2_OUTPUT);
+                       break;
+       }
+
+       if (!configure->reset) {
+               DEBUG("deassert reset\n");
+               au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<4))|(1<<20),
+                               GPIO2_OUTPUT);
+               au_sync_delay(100);
+               au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<5))|(1<<21),
+                               GPIO2_OUTPUT);
+       }
+       else {
+               DEBUG("assert reset\n");
+               au_writel(au_readl(GPIO2_PINSTATE) | (1<<4)|(1<<20),
+                               GPIO2_OUTPUT);
+       }
+       au_sync_delay(100);
+       return 0;
+}
+
+struct pcmcia_low_level xxs1500_pcmcia_ops = {
+       xxs1500_pcmcia_init,
+       xxs1500_pcmcia_shutdown,
+       xxs1500_pcmcia_socket_state,
+       xxs1500_pcmcia_get_irq_info,
+       xxs1500_pcmcia_configure_socket
+};
diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c
new file mode 100644 (file)
index 0000000..836058d
--- /dev/null
@@ -0,0 +1,879 @@
+/*
+ *  drivers/pcmcia/m32r_cfc.c
+ *
+ *  Device driver for the CFC functionality of M32R.
+ *
+ *  Copyright (c) 2001, 2002, 2003, 2004
+ *    Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+
+#undef MAX_IO_WIN      /* FIXME */
+#define MAX_IO_WIN 1
+#undef MAX_WIN         /* FIXME */
+#define MAX_WIN 1
+
+#include "m32r_cfc.h"
+
+#ifdef DEBUG
+static int m32r_cfc_debug;
+module_param(m32r_cfc_debug, int, 0644);
+#define debug(lvl, fmt, arg...) do {                           \
+       if (m32r_cfc_debug > (lvl))                             \
+               printk(KERN_DEBUG "m32r_cfc: " fmt , ## arg);   \
+} while (0)
+#else
+#define debug(n, args...) do { } while (0)
+#endif
+
+/* Poll status interval -- 0 means default to interrupt */
+static int poll_interval = 0;
+
+typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t;
+
+typedef struct pcc_socket {
+       u_short                 type, flags;
+       struct pcmcia_socket    socket;
+       unsigned int            number;
+       ioaddr_t                ioaddr;
+       u_long                  mapaddr;
+       u_long                  base;   /* PCC register base */
+       u_char                  cs_irq1, cs_irq2, intr;
+       pccard_io_map           io_map[MAX_IO_WIN];
+       pccard_mem_map          mem_map[MAX_WIN];
+       u_char                  io_win;
+       u_char                  mem_win;
+       pcc_as_t                current_space;
+       u_char                  last_iodbex;
+#ifdef CONFIG_PROC_FS
+       struct proc_dir_entry *proc;
+#endif
+} pcc_socket_t;
+
+static int pcc_sockets = 0;
+static pcc_socket_t socket[M32R_MAX_PCC] = {
+       { 0, }, /* ... */
+};
+
+/*====================================================================*/
+
+static unsigned int pcc_get(u_short, unsigned int);
+static void pcc_set(u_short, unsigned int , unsigned int );
+
+static spinlock_t pcc_lock = SPIN_LOCK_UNLOCKED;
+
+#if !defined(CONFIG_PLAT_USRV)
+static inline u_long pcc_port2addr(unsigned long port, int size) {
+       u_long addr = 0;
+       u_long odd;
+
+       if (size == 1) {        /* byte access */
+               odd = (port&1) << 11;
+               port -= port & 1;
+               addr = CFC_IO_MAPBASE_BYTE - CFC_IOPORT_BASE + odd + port;
+       } else if (size == 2)
+               addr = CFC_IO_MAPBASE_WORD - CFC_IOPORT_BASE + port;
+
+       return addr;
+}
+#else  /* CONFIG_PLAT_USRV */
+static inline u_long pcc_port2addr(unsigned long port, int size) {
+       u_long odd;
+       u_long addr = ((port - CFC_IOPORT_BASE) & 0xf000) << 8;
+
+       if (size == 1) {        /* byte access */
+               odd = port & 1;
+               port -= odd;
+               odd <<= 11;
+               addr = (addr | CFC_IO_MAPBASE_BYTE) + odd + (port & 0xfff);
+       } else if (size == 2)   /* word access */
+               addr = (addr | CFC_IO_MAPBASE_WORD) + (port & 0xfff);
+
+       return addr;
+}
+#endif /* CONFIG_PLAT_USRV */
+
+void pcc_ioread_byte(int sock, unsigned long port, void *buf, size_t size,
+       size_t nmemb, int flag)
+{
+       u_long addr;
+       unsigned char *bp = (unsigned char *)buf;
+       unsigned long flags;
+
+       debug(3, "m32r_cfc: pcc_ioread_byte: sock=%d, port=%#lx, buf=%p, "
+                "size=%u, nmemb=%d, flag=%d\n",
+                 sock, port, buf, size, nmemb, flag);
+
+       addr = pcc_port2addr(port, 1);
+       if (!addr) {
+               printk("m32r_cfc:ioread_byte null port :%#lx\n",port);
+               return;
+       }
+       debug(3, "m32r_cfc: pcc_ioread_byte: addr=%#lx\n", addr);
+
+       spin_lock_irqsave(&pcc_lock, flags);
+       /* read Byte */
+       while (nmemb--)
+               *bp++ = readb(addr);
+       spin_unlock_irqrestore(&pcc_lock, flags);
+}
+
+void pcc_ioread_word(int sock, unsigned long port, void *buf, size_t size,
+       size_t nmemb, int flag)
+{
+       u_long addr;
+       unsigned short *bp = (unsigned short *)buf;
+       unsigned long flags;
+
+       debug(3, "m32r_cfc: pcc_ioread_word: sock=%d, port=%#lx, "
+                "buf=%p, size=%u, nmemb=%d, flag=%d\n",
+                sock, port, buf, size, nmemb, flag);
+
+       if (size != 2)
+               printk("m32r_cfc: ioread_word :illigal size %u : %#lx\n", size,
+                       port);
+       if (size == 9)
+               printk("m32r_cfc: ioread_word :insw \n");
+
+       addr = pcc_port2addr(port, 2);
+       if (!addr) {
+               printk("m32r_cfc:ioread_word null port :%#lx\n",port);
+               return;
+       }
+       debug(3, "m32r_cfc: pcc_ioread_word: addr=%#lx\n", addr);
+
+       spin_lock_irqsave(&pcc_lock, flags);
+       /* read Word */
+       while (nmemb--)
+               *bp++ = readw(addr);
+       spin_unlock_irqrestore(&pcc_lock, flags);
+}
+
+void pcc_iowrite_byte(int sock, unsigned long port, void *buf, size_t size,
+       size_t nmemb, int flag)
+{
+       u_long addr;
+       unsigned char *bp = (unsigned char *)buf;
+       unsigned long flags;
+
+       debug(3, "m32r_cfc: pcc_iowrite_byte: sock=%d, port=%#lx, "
+                "buf=%p, size=%u, nmemb=%d, flag=%d\n",
+                sock, port, buf, size, nmemb, flag);
+
+       /* write Byte */
+       addr = pcc_port2addr(port, 1);
+       if (!addr) {
+               printk("m32r_cfc:iowrite_byte null port:%#lx\n",port);
+               return;
+       }
+       debug(3, "m32r_cfc: pcc_iowrite_byte: addr=%#lx\n", addr);
+
+       spin_lock_irqsave(&pcc_lock, flags);
+       while (nmemb--)
+               writeb(*bp++, addr);
+       spin_unlock_irqrestore(&pcc_lock, flags);
+}
+
+void pcc_iowrite_word(int sock, unsigned long port, void *buf, size_t size,
+       size_t nmemb, int flag)
+{
+       u_long addr;
+       unsigned short *bp = (unsigned short *)buf;
+       unsigned long flags;
+
+       debug(3, "m32r_cfc: pcc_iowrite_word: sock=%d, port=%#lx, "
+                "buf=%p, size=%u, nmemb=%d, flag=%d\n",
+                sock, port, buf, size, nmemb, flag);
+
+       if(size != 2)
+               printk("m32r_cfc: iowrite_word :illigal size %u : %#lx\n",
+                       size, port);
+       if(size == 9)
+               printk("m32r_cfc: iowrite_word :outsw \n");
+
+       addr = pcc_port2addr(port, 2);
+       if (!addr) {
+               printk("m32r_cfc:iowrite_word null addr :%#lx\n",port);
+               return;
+       }
+#if 1
+       if (addr & 1) {
+               printk("m32r_cfc:iowrite_word port addr (%#lx):%#lx\n", port,
+                       addr);
+               return;
+       }
+#endif
+       debug(3, "m32r_cfc: pcc_iowrite_word: addr=%#lx\n", addr);
+
+       spin_lock_irqsave(&pcc_lock, flags);
+       while (nmemb--)
+               writew(*bp++, addr);
+       spin_unlock_irqrestore(&pcc_lock, flags);
+}
+
+/*====================================================================*/
+
+#define IS_ALIVE               0x8000
+
+typedef struct pcc_t {
+       char                    *name;
+       u_short                 flags;
+} pcc_t;
+
+static pcc_t pcc[] = {
+#if !defined(CONFIG_PLAT_USRV)
+       { "m32r_cfc", 0 }, { "", 0 },
+#else  /* CONFIG_PLAT_USRV */
+       { "m32r_cfc", 0 }, { "m32r_cfc", 0 }, { "m32r_cfc", 0 },
+       { "m32r_cfc", 0 }, { "m32r_cfc", 0 }, { "", 0 },
+#endif /* CONFIG_PLAT_USRV */
+};
+
+static irqreturn_t pcc_interrupt(int, void *, struct pt_regs *);
+
+/*====================================================================*/
+
+static struct timer_list poll_timer;
+
+static unsigned int pcc_get(u_short sock, unsigned int reg)
+{
+       unsigned int val = inw(reg);
+       debug(3, "m32r_cfc: pcc_get: reg(0x%08x)=0x%04x\n", reg, val);
+       return val;
+}
+
+
+static void pcc_set(u_short sock, unsigned int reg, unsigned int data)
+{
+       outw(data, reg);
+       debug(3, "m32r_cfc: pcc_set: reg(0x%08x)=0x%04x\n", reg, data);
+}
+
+/*======================================================================
+
+       See if a card is present, powered up, in IO mode, and already
+       bound to a (non PC Card) Linux driver.  We leave these alone.
+
+       We make an exception for cards that seem to be serial devices.
+
+======================================================================*/
+
+static int __init is_alive(u_short sock)
+{
+       unsigned int stat;
+
+       debug(3, "m32r_cfc: is_alive:\n");
+
+       printk("CF: ");
+       stat = pcc_get(sock, (unsigned int)PLD_CFSTS);
+       if (!stat)
+               printk("No ");
+       printk("Card is detected at socket %d : stat = 0x%08x\n", sock, stat);
+       debug(3, "m32r_cfc: is_alive: sock stat is 0x%04x\n", stat);
+
+       return 0;
+}
+
+static void add_pcc_socket(ulong base, int irq, ulong mapaddr, ioaddr_t ioaddr)
+{
+       pcc_socket_t *t = &socket[pcc_sockets];
+
+       debug(3, "m32r_cfc: add_pcc_socket: base=%#lx, irq=%d, "
+                "mapaddr=%#lx, ioaddr=%08x\n",
+                base, irq, mapaddr, ioaddr);
+
+       /* add sockets */
+       t->ioaddr = ioaddr;
+       t->mapaddr = mapaddr;
+#if !defined(CONFIG_PLAT_USRV)
+       t->base = 0;
+       t->flags = 0;
+       t->cs_irq1 = irq;               // insert irq
+       t->cs_irq2 = irq + 1;           // eject irq
+#else  /* CONFIG_PLAT_USRV */
+       t->base = base;
+       t->flags = 0;
+       t->cs_irq1 = 0;                 // insert irq
+       t->cs_irq2 = 0;                 // eject irq
+#endif /* CONFIG_PLAT_USRV */
+
+       if (is_alive(pcc_sockets))
+               t->flags |= IS_ALIVE;
+
+       /* add pcc */
+#if !defined(CONFIG_PLAT_USRV)
+       request_region((unsigned int)PLD_CFRSTCR, 0x20, "m32r_cfc");
+#else  /* CONFIG_PLAT_USRV */
+       {
+               unsigned int reg_base;
+
+               reg_base = (unsigned int)PLD_CFRSTCR;
+               reg_base |= pcc_sockets << 8;
+               request_region(reg_base, 0x20, "m32r_cfc");
+       }
+#endif /* CONFIG_PLAT_USRV */
+       printk(KERN_INFO "  %s ", pcc[pcc_sockets].name);
+       printk("pcc at 0x%08lx\n", t->base);
+
+       /* Update socket interrupt information, capabilities */
+       t->socket.features |= (SS_CAP_PCCARD | SS_CAP_STATIC_MAP);
+       t->socket.map_size = M32R_PCC_MAPSIZE;
+       t->socket.io_offset = ioaddr;   /* use for io access offset */
+       t->socket.irq_mask = 0;
+#if !defined(CONFIG_PLAT_USRV)
+       t->socket.pci_irq = PLD_IRQ_CFIREQ ;    /* card interrupt */
+#else  /* CONFIG_PLAT_USRV */
+       t->socket.pci_irq = PLD_IRQ_CF0 + pcc_sockets;
+#endif /* CONFIG_PLAT_USRV */
+
+#ifndef CONFIG_PLAT_USRV
+       /* insert interrupt */
+       request_irq(irq, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt);
+       /* eject interrupt */
+       request_irq(irq+1, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt);
+
+       debug(3, "m32r_cfc: enable CFMSK, RDYSEL\n");
+       pcc_set(pcc_sockets, (unsigned int)PLD_CFIMASK, 0x01);
+#endif /* CONFIG_PLAT_USRV */
+#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
+       pcc_set(pcc_sockets, (unsigned int)PLD_CFCR1, 0x0200);
+#endif
+       pcc_sockets++;
+
+       return;
+}
+
+
+/*====================================================================*/
+
+static irqreturn_t pcc_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+       int i;
+       u_int events = 0;
+       int handled = 0;
+
+       debug(3, "m32r_cfc: pcc_interrupt: irq=%d, dev=%p, regs=%p\n",
+               irq, dev, regs);
+       for (i = 0; i < pcc_sockets; i++) {
+               if (socket[i].cs_irq1 != irq && socket[i].cs_irq2 != irq)
+                       continue;
+
+               handled = 1;
+               debug(3, "m32r_cfc: pcc_interrupt: socket %d irq 0x%02x ",
+                       i, irq);
+               events |= SS_DETECT;    /* insert or eject */
+               if (events)
+                       pcmcia_parse_events(&socket[i].socket, events);
+       }
+       debug(3, "m32r_cfc: pcc_interrupt: done\n");
+
+       return IRQ_RETVAL(handled);
+} /* pcc_interrupt */
+
+static void pcc_interrupt_wrapper(u_long data)
+{
+       debug(3, "m32r_cfc: pcc_interrupt_wrapper:\n");
+       pcc_interrupt(0, NULL, NULL);
+       init_timer(&poll_timer);
+       poll_timer.expires = jiffies + poll_interval;
+       add_timer(&poll_timer);
+}
+
+/*====================================================================*/
+
+static int _pcc_get_status(u_short sock, u_int *value)
+{
+       u_int status;
+
+       debug(3, "m32r_cfc: _pcc_get_status:\n");
+       status = pcc_get(sock, (unsigned int)PLD_CFSTS);
+       *value = (status) ? SS_DETECT : 0;
+       debug(3, "m32r_cfc: _pcc_get_status: status=0x%08x\n", status);
+
+#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
+       if ( status ) {
+               /* enable CF power */
+               status = inw((unsigned int)PLD_CPCR);
+               if (!(status & PLD_CPCR_CF)) {
+                       debug(3, "m32r_cfc: _pcc_get_status: "
+                                "power on (CPCR=0x%08x)\n", status);
+                       status |= PLD_CPCR_CF;
+                       outw(status, (unsigned int)PLD_CPCR);
+                       udelay(100);
+               }
+               *value |= SS_POWERON;
+
+               pcc_set(sock, (unsigned int)PLD_CFBUFCR,0);/* enable buffer */
+               udelay(100);
+
+               *value |= SS_READY;             /* always ready */
+               *value |= SS_3VCARD;
+       } else {
+               /* disable CF power */
+               status = inw((unsigned int)PLD_CPCR);
+               status &= ~PLD_CPCR_CF;
+               outw(status, (unsigned int)PLD_CPCR);
+               udelay(100);
+               debug(3, "m32r_cfc: _pcc_get_status: "
+                        "power off (CPCR=0x%08x)\n", status);
+       }
+#elif defined(CONFIG_PLAT_MAPPI2)
+       if ( status ) {
+               status = pcc_get(sock, (unsigned int)PLD_CPCR);
+               if (status == 0) { /* power off */
+                       pcc_set(sock, (unsigned int)PLD_CPCR, 1);
+                       pcc_set(sock, (unsigned int)PLD_CFBUFCR,0); /* force buffer off for ZA-36 */
+                       udelay(50);
+               }
+               status = pcc_get(sock, (unsigned int)PLD_CFBUFCR);
+               if (status != 0) { /* buffer off */
+                       pcc_set(sock, (unsigned int)PLD_CFBUFCR,0);
+                       udelay(50);
+                       pcc_set(sock, (unsigned int)PLD_CFRSTCR, 0x0101);
+                       udelay(25); /* for IDE reset */
+                       pcc_set(sock, (unsigned int)PLD_CFRSTCR, 0x0100);
+                       mdelay(2);  /* for IDE reset */
+               } else {
+                       *value |= SS_POWERON;
+                       *value |= SS_READY;
+               }
+       }
+#else
+#error no platform configuration
+#endif
+       debug(3, "m32r_cfc: _pcc_get_status: GetStatus(%d) = %#4.4x\n",
+                sock, *value);
+       return 0;
+} /* _get_status */
+
+/*====================================================================*/
+
+static int _pcc_get_socket(u_short sock, socket_state_t *state)
+{
+//     pcc_socket_t *t = &socket[sock];
+
+#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
+       state->flags = 0;
+       state->csc_mask = SS_DETECT;
+       state->csc_mask |= SS_READY;
+       state->io_irq = 0;
+       state->Vcc = 33;        /* 3.3V fixed */
+       state->Vpp = 33;
+#endif
+       debug(3, "m32r_cfc: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, "
+                 "io_irq %d, csc_mask %#2.2x\n", sock, state->flags,
+                 state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+       return 0;
+} /* _get_socket */
+
+/*====================================================================*/
+
+static int _pcc_set_socket(u_short sock, socket_state_t *state)
+{
+#if defined(CONFIG_PLAT_MAPPI2)
+       u_long reg = 0;
+#endif
+       debug(3, "m32r_cfc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+                 "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags,
+                 state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+
+#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT)
+       if (state->Vcc) {
+               if ((state->Vcc != 50) && (state->Vcc != 33))
+                       return -EINVAL;
+               /* accept 5V and 3.3V */
+       }
+#elif defined(CONFIG_PLAT_MAPPI2)
+       if (state->Vcc) {
+               /*
+                * 5V only
+                */
+               if (state->Vcc == 50) {
+                       reg |= PCCSIGCR_VEN;
+               } else {
+                       return -EINVAL;
+               }
+       }
+#endif
+
+       if (state->flags & SS_RESET) {
+               debug(3, ":RESET\n");
+               pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x101);
+       }else{
+               pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x100);
+       }
+       if (state->flags & SS_OUTPUT_ENA){
+               debug(3, ":OUTPUT_ENA\n");
+               /* bit clear */
+               pcc_set(sock,(unsigned int)PLD_CFBUFCR,0);
+       } else {
+               pcc_set(sock,(unsigned int)PLD_CFBUFCR,1);
+       }
+
+#ifdef DEBUG
+       if(state->flags & SS_IOCARD){
+               debug(3, ":IOCARD");
+       }
+       if (state->flags & SS_PWR_AUTO) {
+               debug(3, ":PWR_AUTO");
+       }
+       if (state->csc_mask & SS_DETECT)
+               debug(3, ":csc-SS_DETECT");
+       if (state->flags & SS_IOCARD) {
+               if (state->csc_mask & SS_STSCHG)
+                       debug(3, ":STSCHG");
+       } else {
+               if (state->csc_mask & SS_BATDEAD)
+                       debug(3, ":BATDEAD");
+               if (state->csc_mask & SS_BATWARN)
+                       debug(3, ":BATWARN");
+               if (state->csc_mask & SS_READY)
+                       debug(3, ":READY");
+       }
+       debug(3, "\n");
+#endif
+       return 0;
+} /* _set_socket */
+
+/*====================================================================*/
+
+static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io)
+{
+       u_char map;
+
+       debug(3, "m32r_cfc: SetIOMap(%d, %d, %#2.2x, %d ns, "
+                 "%#4.4x-%#4.4x)\n", sock, io->map, io->flags,
+                 io->speed, io->start, io->stop);
+       map = io->map;
+
+       return 0;
+} /* _set_io_map */
+
+/*====================================================================*/
+
+static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem)
+{
+
+       u_char map = mem->map;
+       u_long addr;
+       pcc_socket_t *t = &socket[sock];
+
+       debug(3, "m32r_cfc: SetMemMap(%d, %d, %#2.2x, %d ns, "
+                "%#5.5lx, %#5.5x)\n", sock, map, mem->flags,
+                mem->speed, mem->static_start, mem->card_start);
+
+       /*
+        * sanity check
+        */
+       if ((map > MAX_WIN) || (mem->card_start > 0x3ffffff)){
+               return -EINVAL;
+       }
+
+       /*
+        * de-activate
+        */
+       if ((mem->flags & MAP_ACTIVE) == 0) {
+               t->current_space = as_none;
+               return 0;
+       }
+
+       /*
+        * Set mode
+        */
+       if (mem->flags & MAP_ATTRIB) {
+               t->current_space = as_attr;
+       } else {
+               t->current_space = as_comm;
+       }
+
+       /*
+        * Set address
+        */
+       addr = t->mapaddr + (mem->card_start & M32R_PCC_MAPMASK);
+       mem->static_start = addr + mem->card_start;
+
+       return 0;
+
+} /* _set_mem_map */
+
+#if 0 /* driver model ordering issue */
+/*======================================================================
+
+       Routines for accessing socket information and register dumps via
+       /proc/bus/pccard/...
+
+======================================================================*/
+
+static ssize_t show_info(struct class_device *class_dev, char *buf)
+{
+       pcc_socket_t *s = container_of(class_dev, struct pcc_socket,
+               socket.dev);
+
+       return sprintf(buf, "type:     %s\nbase addr:    0x%08lx\n",
+               pcc[s->type].name, s->base);
+}
+
+static ssize_t show_exca(struct class_device *class_dev, char *buf)
+{
+       /* FIXME */
+
+       return 0;
+}
+
+static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
+static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
+#endif
+
+/*====================================================================*/
+
+/* this is horribly ugly... proper locking needs to be done here at
+ * some time... */
+#define LOCKED(x) do {                                 \
+       int retval;                                     \
+       unsigned long flags;                            \
+       spin_lock_irqsave(&pcc_lock, flags);            \
+       retval = x;                                     \
+       spin_unlock_irqrestore(&pcc_lock, flags);       \
+       return retval;                                  \
+} while (0)
+
+
+static int pcc_get_status(struct pcmcia_socket *s, u_int *value)
+{
+       unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+       if (socket[sock].flags & IS_ALIVE) {
+               debug(3, "m32r_cfc: pcc_get_status: sock(%d) -EINVAL\n", sock);
+               *value = 0;
+               return -EINVAL;
+       }
+       debug(3, "m32r_cfc: pcc_get_status: sock(%d)\n", sock);
+       LOCKED(_pcc_get_status(sock, value));
+}
+
+static int pcc_get_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+       unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+       if (socket[sock].flags & IS_ALIVE) {
+               debug(3, "m32r_cfc: pcc_get_socket: sock(%d) -EINVAL\n", sock);
+               return -EINVAL;
+       }
+       debug(3, "m32r_cfc: pcc_get_socket: sock(%d)\n", sock);
+       LOCKED(_pcc_get_socket(sock, state));
+}
+
+static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+       unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+       if (socket[sock].flags & IS_ALIVE) {
+               debug(3, "m32r_cfc: pcc_set_socket: sock(%d) -EINVAL\n", sock);
+               return -EINVAL;
+       }
+       debug(3, "m32r_cfc: pcc_set_socket: sock(%d)\n", sock);
+       LOCKED(_pcc_set_socket(sock, state));
+}
+
+static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
+{
+       unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+       if (socket[sock].flags & IS_ALIVE) {
+               debug(3, "m32r_cfc: pcc_set_io_map: sock(%d) -EINVAL\n", sock);
+               return -EINVAL;
+       }
+       debug(3, "m32r_cfc: pcc_set_io_map: sock(%d)\n", sock);
+       LOCKED(_pcc_set_io_map(sock, io));
+}
+
+static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
+{
+       unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+       if (socket[sock].flags & IS_ALIVE) {
+               debug(3, "m32r_cfc: pcc_set_mem_map: sock(%d) -EINVAL\n", sock);
+               return -EINVAL;
+       }
+       debug(3, "m32r_cfc: pcc_set_mem_map: sock(%d)\n", sock);
+       LOCKED(_pcc_set_mem_map(sock, mem));
+}
+
+static int pcc_init(struct pcmcia_socket *s)
+{
+       debug(3, "m32r_cfc: pcc_init()\n");
+       return 0;
+}
+
+static int pcc_suspend(struct pcmcia_socket *sock)
+{
+       debug(3, "m32r_cfc: pcc_suspend()\n");
+       return pcc_set_socket(sock, &dead_socket);
+}
+
+static struct pccard_operations pcc_operations = {
+       .init                   = pcc_init,
+       .suspend                = pcc_suspend,
+       .get_status             = pcc_get_status,
+       .get_socket             = pcc_get_socket,
+       .set_socket             = pcc_set_socket,
+       .set_io_map             = pcc_set_io_map,
+       .set_mem_map            = pcc_set_mem_map,
+};
+
+/*====================================================================*/
+
+static int m32r_pcc_suspend(struct device *dev, u32 state, u32 level)
+{
+       int ret = 0;
+       if (level == SUSPEND_SAVE_STATE)
+               ret = pcmcia_socket_dev_suspend(dev, state);
+       return ret;
+}
+
+static int m32r_pcc_resume(struct device *dev, u32 level)
+{
+       int ret = 0;
+       if (level == RESUME_RESTORE_STATE)
+               ret = pcmcia_socket_dev_resume(dev);
+       return ret;
+}
+
+
+static struct device_driver pcc_driver = {
+       .name = "cfc",
+       .bus = &platform_bus_type,
+       .suspend = m32r_pcc_suspend,
+       .resume = m32r_pcc_resume,
+};
+
+static struct platform_device pcc_device = {
+       .name = "cfc",
+       .id = 0,
+};
+
+/*====================================================================*/
+
+static int __init init_m32r_pcc(void)
+{
+       int i, ret;
+
+       ret = driver_register(&pcc_driver);
+       if (ret)
+               return ret;
+
+       ret = platform_device_register(&pcc_device);
+       if (ret){
+               driver_unregister(&pcc_driver);
+               return ret;
+       }
+
+#if defined(CONFIG_PLAT_MAPPI2)
+       pcc_set(0, (unsigned int)PLD_CFCR0, 0x0f0f);
+       pcc_set(0, (unsigned int)PLD_CFCR1, 0x0200);
+#endif
+
+       pcc_sockets = 0;
+
+#if !defined(CONFIG_PLAT_USRV)
+       add_pcc_socket(M32R_PCC0_BASE, PLD_IRQ_CFC_INSERT, CFC_ATTR_MAPBASE,
+                      CFC_IOPORT_BASE);
+#else  /* CONFIG_PLAT_USRV */
+       {
+               ulong base, mapaddr;
+               ioaddr_t ioaddr;
+
+               for (i = 0 ; i < M32R_MAX_PCC ; i++) {
+                       base = (ulong)PLD_CFRSTCR;
+                       base = base | (i << 8);
+                       ioaddr = (i + 1) << 12;
+                       mapaddr = CFC_ATTR_MAPBASE | (i << 20);
+                       add_pcc_socket(base, 0, mapaddr, ioaddr);
+               }
+       }
+#endif /* CONFIG_PLAT_USRV */
+
+       if (pcc_sockets == 0) {
+               printk("socket is not found.\n");
+               platform_device_unregister(&pcc_device);
+               driver_unregister(&pcc_driver);
+               return -ENODEV;
+       }
+
+       /* Set up interrupt handler(s) */
+
+       for (i = 0 ; i < pcc_sockets ; i++) {
+               socket[i].socket.dev.dev = &pcc_device.dev;
+               socket[i].socket.ops = &pcc_operations;
+               socket[i].socket.owner = THIS_MODULE;
+               socket[i].number = i;
+               ret = pcmcia_register_socket(&socket[i].socket);
+               if (ret && i--) {
+                       for (; i>= 0; i--)
+                               pcmcia_unregister_socket(&socket[i].socket);
+                       break;
+               }
+#if 0  /* driver model ordering issue */
+               class_device_create_file(&socket[i].socket.dev,
+                                        &class_device_attr_info);
+               class_device_create_file(&socket[i].socket.dev,
+                                        &class_device_attr_exca);
+#endif
+       }
+
+       /* Finally, schedule a polling interrupt */
+       if (poll_interval != 0) {
+               poll_timer.function = pcc_interrupt_wrapper;
+               poll_timer.data = 0;
+               init_timer(&poll_timer);
+               poll_timer.expires = jiffies + poll_interval;
+               add_timer(&poll_timer);
+       }
+
+       return 0;
+} /* init_m32r_pcc */
+
+static void __exit exit_m32r_pcc(void)
+{
+       int i;
+
+       for (i = 0; i < pcc_sockets; i++)
+               pcmcia_unregister_socket(&socket[i].socket);
+
+       platform_device_unregister(&pcc_device);
+       if (poll_interval != 0)
+               del_timer_sync(&poll_timer);
+
+       driver_unregister(&pcc_driver);
+} /* exit_m32r_pcc */
+
+module_init(init_m32r_pcc);
+module_exit(exit_m32r_pcc);
+MODULE_LICENSE("Dual MPL/GPL");
+/*====================================================================*/
diff --git a/drivers/pcmcia/m32r_cfc.h b/drivers/pcmcia/m32r_cfc.h
new file mode 100644 (file)
index 0000000..17c1db7
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2001 by Hiroyuki Kondo
+ */
+
+#if !defined(CONFIG_M32R_CFC_NUM)
+#define M32R_MAX_PCC   2
+#else
+#define M32R_MAX_PCC   CONFIG_M32R_CFC_NUM
+#endif
+
+/*
+ * M32R PC Card Controler
+ */
+#define M32R_PCC0_BASE        0x00ef7000
+#define M32R_PCC1_BASE        0x00ef7020
+
+/*
+ * Register offsets
+ */
+#define PCCR            0x00
+#define PCADR           0x04
+#define PCMOD           0x08
+#define PCIRC           0x0c
+#define PCCSIGCR        0x10
+#define PCATCR          0x14
+
+/*
+ * PCCR
+ */
+#define PCCR_PCEN       (1UL<<(31-31))
+
+/*
+ * PCIRC
+ */
+#define PCIRC_BWERR     (1UL<<(31-7))
+#define PCIRC_CDIN1     (1UL<<(31-14))
+#define PCIRC_CDIN2     (1UL<<(31-15))
+#define PCIRC_BEIEN     (1UL<<(31-23))
+#define PCIRC_CIIEN     (1UL<<(31-30))
+#define PCIRC_COIEN     (1UL<<(31-31))
+
+/*
+ * PCCSIGCR
+ */
+#define PCCSIGCR_SEN    (1UL<<(31-3))
+#define PCCSIGCR_VEN    (1UL<<(31-7))
+#define PCCSIGCR_CRST   (1UL<<(31-15))
+#define PCCSIGCR_COCR   (1UL<<(31-31))
+
+/*
+ *
+ */
+#define PCMOD_AS_ATTRIB        (1UL<<(31-19))
+#define PCMOD_AS_IO    (1UL<<(31-18))
+
+#define PCMOD_CBSZ     (1UL<<(31-23)) /* set for 8bit */
+
+#define PCMOD_DBEX     (1UL<<(31-31)) /* set for excahnge */
+
+/*
+ * M32R PCC Map addr
+ */
+
+#define M32R_PCC0_MAPBASE        0x14000000
+#define M32R_PCC1_MAPBASE        0x16000000
+
+#define M32R_PCC_MAPMAX                 0x02000000
+
+#define M32R_PCC_MAPSIZE        0x00001000 /* XXX */
+#define M32R_PCC_MAPMASK       (~(M32R_PCC_MAPMAX-1))
+
+#define CFC_IOPORT_BASE                0x1000
+
+#if !defined(CONFIG_PLAT_USRV)
+#define CFC_ATTR_MAPBASE        0x0c014000
+#define CFC_IO_MAPBASE_BYTE     0xac012000
+#define CFC_IO_MAPBASE_WORD     0xac002000
+#else  /* CONFIG_PLAT_USRV */
+#define CFC_ATTR_MAPBASE       0x04014000
+#define CFC_IO_MAPBASE_BYTE    0xa4012000
+#define CFC_IO_MAPBASE_WORD    0xa4002000
+#endif /* CONFIG_PLAT_USRV */
+
diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c
new file mode 100644 (file)
index 0000000..aae7a16
--- /dev/null
@@ -0,0 +1,816 @@
+/*
+ *  drivers/pcmcia/m32r_pcc.c
+ *
+ *  Device driver for the PCMCIA functionality of M32R.
+ *
+ *  Copyright (c) 2001, 2002, 2003, 2004
+ *    Hiroyuki Kondo, Naoto Sugai, Hayato Fujiwara
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/addrspace.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+
+/* XXX: should be moved into asm/irq.h */
+#define PCC0_IRQ 24
+#define PCC1_IRQ 25
+
+#include "m32r_pcc.h"
+
+#define CHAOS_PCC_DEBUG
+#ifdef CHAOS_PCC_DEBUG
+       static volatile u_short dummy_readbuf;
+#endif
+
+#define PCC_DEBUG_DBEX
+
+#ifdef DEBUG
+static int m32r_pcc_debug;
+module_param(m32r_pcc_debug, int, 0644);
+#define debug(lvl, fmt, arg...) do {                           \
+       if (m32r_pcc_debug > (lvl))                             \
+               printk(KERN_DEBUG "m32r_pcc: " fmt , ## arg);   \
+} while (0)
+#else
+#define debug(n, args...) do { } while (0)
+#endif
+
+/* Poll status interval -- 0 means default to interrupt */
+static int poll_interval = 0;
+
+typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t;
+
+typedef struct pcc_socket {
+       u_short                 type, flags;
+       struct pcmcia_socket    socket;
+       unsigned int            number;
+       ioaddr_t                ioaddr;
+       u_long                  mapaddr;
+       u_long                  base;   /* PCC register base */
+       u_char                  cs_irq, intr;
+       pccard_io_map           io_map[MAX_IO_WIN];
+       pccard_mem_map          mem_map[MAX_WIN];
+       u_char                  io_win;
+       u_char                  mem_win;
+       pcc_as_t                current_space;
+       u_char                  last_iodbex;
+#ifdef CHAOS_PCC_DEBUG
+       u_char                  last_iosize;
+#endif
+#ifdef CONFIG_PROC_FS
+       struct proc_dir_entry *proc;
+#endif
+} pcc_socket_t;
+
+static int pcc_sockets = 0;
+static pcc_socket_t socket[M32R_MAX_PCC] = {
+       { 0, }, /* ... */
+};
+
+/*====================================================================*/
+
+static unsigned int pcc_get(u_short, unsigned int);
+static void pcc_set(u_short, unsigned int , unsigned int );
+
+static spinlock_t pcc_lock = SPIN_LOCK_UNLOCKED;
+
+void pcc_iorw(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int wr, int flag)
+{
+       u_long addr;
+       u_long flags;
+       int need_ex;
+#ifdef PCC_DEBUG_DBEX
+       int _dbex;
+#endif
+       pcc_socket_t *t = &socket[sock];
+#ifdef CHAOS_PCC_DEBUG
+       int map_changed = 0;
+#endif
+
+       /* Need lock ? */
+       spin_lock_irqsave(&pcc_lock, flags);
+
+       /*
+        * Check if need dbex
+        */
+       need_ex = (size > 1 && flag == 0) ? PCMOD_DBEX : 0;
+#ifdef PCC_DEBUG_DBEX
+       _dbex = need_ex;
+       need_ex = 0;
+#endif
+
+       /*
+        * calculate access address
+        */
+       addr = t->mapaddr + port - t->ioaddr + KSEG1; /* XXX */
+
+       /*
+        * Check current mapping
+        */
+       if (t->current_space != as_io || t->last_iodbex != need_ex) {
+
+               u_long cbsz;
+
+               /*
+                * Disable first
+                */
+               pcc_set(sock, PCCR, 0);
+
+               /*
+                * Set mode and io address
+                */
+               cbsz = (t->flags & MAP_16BIT) ? 0 : PCMOD_CBSZ;
+               pcc_set(sock, PCMOD, PCMOD_AS_IO | cbsz | need_ex);
+               pcc_set(sock, PCADR, addr & 0x1ff00000);
+
+               /*
+                * Enable and read it
+                */
+               pcc_set(sock, PCCR, 1);
+
+#ifdef CHAOS_PCC_DEBUG
+#if 0
+               map_changed = (t->current_space == as_attr && size == 2); /* XXX */
+#else
+               map_changed = 1;
+#endif
+#endif
+               t->current_space = as_io;
+       }
+
+       /*
+        * access to IO space
+        */
+       if (size == 1) {
+               /* Byte */
+               unsigned char *bp = (unsigned char *)buf;
+
+#ifdef CHAOS_DEBUG
+               if (map_changed) {
+                       dummy_readbuf = readb(addr);
+               }
+#endif
+               if (wr) {
+                       /* write Byte */
+                       while (nmemb--) {
+                               writeb(*bp++, addr);
+                       }
+               } else {
+                       /* read Byte */
+                       while (nmemb--) {
+                       *bp++ = readb(addr);
+                       }
+               }
+       } else {
+               /* Word */
+               unsigned short *bp = (unsigned short *)buf;
+
+#ifdef CHAOS_PCC_DEBUG
+               if (map_changed) {
+                       dummy_readbuf = readw(addr);
+               }
+#endif
+               if (wr) {
+                       /* write Word */
+                       while (nmemb--) {
+#ifdef PCC_DEBUG_DBEX
+                               if (_dbex) {
+                                       unsigned char *cp = (unsigned char *)bp;
+                                       unsigned short tmp;
+                                       tmp = cp[1] << 8 | cp[0];
+                                       writew(tmp, addr);
+                                       bp++;
+                               } else
+#endif
+                               writew(*bp++, addr);
+               }
+           } else {
+               /* read Word */
+               while (nmemb--) {
+#ifdef  PCC_DEBUG_DBEX
+                               if (_dbex) {
+                                       unsigned char *cp = (unsigned char *)bp;
+                                       unsigned short tmp;
+                                       tmp = readw(addr);
+                                       cp[0] = tmp & 0xff;
+                                       cp[1] = (tmp >> 8) & 0xff;
+                                       bp++;
+                               } else
+#endif
+                               *bp++ = readw(addr);
+               }
+           }
+       }
+
+#if 1
+       /* addr is no longer used */
+       if ((addr = pcc_get(sock, PCIRC)) & PCIRC_BWERR) {
+         printk("m32r_pcc: BWERR detected : port 0x%04lx : iosize %dbit\n",
+                        port, size * 8);
+         pcc_set(sock, PCIRC, addr);
+       }
+#endif
+       /*
+        * save state
+        */
+       t->last_iosize = size;
+       t->last_iodbex = need_ex;
+
+       /* Need lock ? */
+
+       spin_unlock_irqrestore(&pcc_lock,flags);
+
+       return;
+}
+
+void pcc_ioread(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int flag) {
+       pcc_iorw(sock, port, buf, size, nmemb, 0, flag);
+}
+
+void pcc_iowrite(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int flag) {
+    pcc_iorw(sock, port, buf, size, nmemb, 1, flag);
+}
+
+/*====================================================================*/
+
+#define IS_ALIVE               0x8000
+
+typedef struct pcc_t {
+       char                    *name;
+       u_short                 flags;
+} pcc_t;
+
+static pcc_t pcc[] = {
+       { "xnux2", 0 }, { "xnux2", 0 },
+};
+
+static irqreturn_t pcc_interrupt(int, void *, struct pt_regs *);
+
+/*====================================================================*/
+
+static struct timer_list poll_timer;
+
+static unsigned int pcc_get(u_short sock, unsigned int reg)
+{
+       return inl(socket[sock].base + reg);
+}
+
+
+static void pcc_set(u_short sock, unsigned int reg, unsigned int data)
+{
+       outl(data, socket[sock].base + reg);
+}
+
+/*======================================================================
+
+       See if a card is present, powered up, in IO mode, and already
+       bound to a (non PC Card) Linux driver.  We leave these alone.
+
+       We make an exception for cards that seem to be serial devices.
+
+======================================================================*/
+
+static int __init is_alive(u_short sock)
+{
+       unsigned int stat;
+       unsigned int f;
+
+       stat = pcc_get(sock, PCIRC);
+       f = (stat & (PCIRC_CDIN1 | PCIRC_CDIN2)) >> 16;
+       if(!f){
+               printk("m32r_pcc: No Card is detected at socket %d : stat = 0x%08x\n",stat,sock);
+               return 0;
+       }
+       if(f!=3)
+               printk("m32r_pcc: Insertion fail (%.8x) at socket %d\n",stat,sock);
+       else
+               printk("m32r_pcc: Card is Inserted at socket %d(%.8x)\n",sock,stat);
+       return 0;
+}
+
+static void add_pcc_socket(ulong base, int irq, ulong mapaddr, ioaddr_t ioaddr)
+{
+       pcc_socket_t *t = &socket[pcc_sockets];
+
+       /* add sockets */
+       t->ioaddr = ioaddr;
+       t->mapaddr = mapaddr;
+       t->base = base;
+#ifdef CHAOS_PCC_DEBUG
+       t->flags = MAP_16BIT;
+#else
+       t->flags = 0;
+#endif
+       if (is_alive(pcc_sockets))
+               t->flags |= IS_ALIVE;
+
+       /* add pcc */
+       if (t->base > 0) {
+               request_region(t->base, 0x20, "m32r-pcc");
+       }
+
+       printk(KERN_INFO "  %s ", pcc[pcc_sockets].name);
+       printk("pcc at 0x%08lx\n", t->base);
+
+       /* Update socket interrupt information, capabilities */
+       t->socket.features |= (SS_CAP_PCCARD | SS_CAP_STATIC_MAP);
+       t->socket.map_size = M32R_PCC_MAPSIZE;
+       t->socket.io_offset = ioaddr;   /* use for io access offset */
+       t->socket.irq_mask = 0;
+       t->socket.pci_irq = 2 + pcc_sockets; /* XXX */
+
+       request_irq(irq, pcc_interrupt, 0, "m32r-pcc", pcc_interrupt);
+
+       pcc_sockets++;
+
+       return;
+}
+
+
+/*====================================================================*/
+
+static irqreturn_t pcc_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+       int i, j, irc;
+       u_int events, active;
+       int handled = 0;
+
+       debug(4, "m32r: pcc_interrupt(%d)\n", irq);
+
+       for (j = 0; j < 20; j++) {
+               active = 0;
+               for (i = 0; i < pcc_sockets; i++) {
+                       if ((socket[i].cs_irq != irq) &&
+                               (socket[i].socket.pci_irq != irq))
+                               continue;
+                       handled = 1;
+                       irc = pcc_get(i, PCIRC);
+                       irc >>=16;
+                       debug(2, "m32r-pcc:interrput: socket %d pcirc 0x%02x ", i, irc);
+                       if (!irc)
+                               continue;
+
+                       events = (irc) ? SS_DETECT : 0;
+                       events |= (pcc_get(i,PCCR) & PCCR_PCEN) ? SS_READY : 0;
+                       debug(2, " event 0x%02x\n", events);
+
+                       if (events)
+                               pcmcia_parse_events(&socket[i].socket, events);
+
+                       active |= events;
+                       active = 0;
+               }
+               if (!active) break;
+       }
+       if (j == 20)
+               printk(KERN_NOTICE "m32r-pcc: infinite loop in interrupt handler\n");
+
+       debug(4, "m32r-pcc: interrupt done\n");
+
+       return IRQ_RETVAL(handled);
+} /* pcc_interrupt */
+
+static void pcc_interrupt_wrapper(u_long data)
+{
+       pcc_interrupt(0, NULL, NULL);
+       init_timer(&poll_timer);
+       poll_timer.expires = jiffies + poll_interval;
+       add_timer(&poll_timer);
+}
+
+/*====================================================================*/
+
+static int _pcc_get_status(u_short sock, u_int *value)
+{
+       u_int status;
+
+       status = pcc_get(sock,PCIRC);
+       *value = ((status & PCIRC_CDIN1) && (status & PCIRC_CDIN2))
+               ? SS_DETECT : 0;
+
+       status = pcc_get(sock,PCCR);
+
+#if 0
+       *value |= (status & PCCR_PCEN) ? SS_READY : 0;
+#else
+       *value |= SS_READY; /* XXX: always */
+#endif
+
+       status = pcc_get(sock,PCCSIGCR);
+       *value |= (status & PCCSIGCR_VEN) ? SS_POWERON : 0;
+
+       debug(3, "m32r-pcc: GetStatus(%d) = %#4.4x\n", sock, *value);
+       return 0;
+} /* _get_status */
+
+/*====================================================================*/
+
+static int _pcc_get_socket(u_short sock, socket_state_t *state)
+{
+       debug(3, "m32r-pcc: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, "
+                 "io_irq %d, csc_mask %#2.2x\n", sock, state->flags,
+                 state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+       return 0;
+} /* _get_socket */
+
+/*====================================================================*/
+
+static int _pcc_set_socket(u_short sock, socket_state_t *state)
+{
+       u_long reg = 0;
+
+       debug(3, "m32r-pcc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, "
+                 "io_irq %d, csc_mask %#2.2x)", sock, state->flags,
+                 state->Vcc, state->Vpp, state->io_irq, state->csc_mask);
+
+       if (state->Vcc) {
+               /*
+                * 5V only
+                */
+               if (state->Vcc == 50) {
+                       reg |= PCCSIGCR_VEN;
+               } else {
+                       return -EINVAL;
+               }
+       }
+
+       if (state->flags & SS_RESET) {
+               debug(3, ":RESET\n");
+               reg |= PCCSIGCR_CRST;
+       }
+       if (state->flags & SS_OUTPUT_ENA){
+               debug(3, ":OUTPUT_ENA\n");
+               /* bit clear */
+       } else {
+               reg |= PCCSIGCR_SEN;
+       }
+
+       pcc_set(sock,PCCSIGCR,reg);
+
+#ifdef DEBUG
+       if(state->flags & SS_IOCARD){
+               debug(3, ":IOCARD");
+       }
+       if (state->flags & SS_PWR_AUTO) {
+               debug(3, ":PWR_AUTO");
+       }
+       if (state->csc_mask & SS_DETECT)
+               debug(3, ":csc-SS_DETECT");
+       if (state->flags & SS_IOCARD) {
+               if (state->csc_mask & SS_STSCHG)
+                       debug(3, ":STSCHG");
+       } else {
+               if (state->csc_mask & SS_BATDEAD)
+                       debug(3, ":BATDEAD");
+               if (state->csc_mask & SS_BATWARN)
+                       debug(3, ":BATWARN");
+               if (state->csc_mask & SS_READY)
+                       debug(3, ":READY");
+       }
+       debug(3, "\n");
+#endif
+       return 0;
+} /* _set_socket */
+
+/*====================================================================*/
+
+static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io)
+{
+       u_char map;
+
+       debug(3, "m32r-pcc: SetIOMap(%d, %d, %#2.2x, %d ns, "
+                 "%#4.4x-%#4.4x)\n", sock, io->map, io->flags,
+                 io->speed, io->start, io->stop);
+       map = io->map;
+
+       return 0;
+} /* _set_io_map */
+
+/*====================================================================*/
+
+static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem)
+{
+
+       u_char map = mem->map;
+       u_long mode;
+       u_long addr;
+       pcc_socket_t *t = &socket[sock];
+#ifdef CHAOS_PCC_DEBUG
+#if 0
+       pcc_as_t last = t->current_space;
+#endif
+#endif
+
+       debug(3, "m32r-pcc: SetMemMap(%d, %d, %#2.2x, %d ns, "
+                "%#5.5lx,  %#5.5x)\n", sock, map, mem->flags,
+                mem->speed, mem->static_start, mem->card_start);
+
+       /*
+        * sanity check
+        */
+       if ((map > MAX_WIN) || (mem->card_start > 0x3ffffff)){
+               return -EINVAL;
+       }
+
+       /*
+        * de-activate
+        */
+       if ((mem->flags & MAP_ACTIVE) == 0) {
+               t->current_space = as_none;
+               return 0;
+       }
+
+       /*
+        * Disable first
+        */
+       pcc_set(sock, PCCR, 0);
+
+       /*
+        * Set mode
+        */
+       if (mem->flags & MAP_ATTRIB) {
+               mode = PCMOD_AS_ATTRIB | PCMOD_CBSZ;
+               t->current_space = as_attr;
+       } else {
+               mode = 0; /* common memory */
+               t->current_space = as_comm;
+       }
+       pcc_set(sock, PCMOD, mode);
+
+       /*
+        * Set address
+        */
+       addr = t->mapaddr + (mem->card_start & M32R_PCC_MAPMASK);
+       pcc_set(sock, PCADR, addr);
+
+       mem->static_start = addr + mem->card_start;
+
+       /*
+        * Enable again
+        */
+       pcc_set(sock, PCCR, 1);
+
+#ifdef CHAOS_PCC_DEBUG
+#if 0
+       if (last != as_attr) {
+#else
+       if (1) {
+#endif
+               dummy_readbuf = *(u_char *)(addr + KSEG1);
+       }
+#endif
+
+       return 0;
+
+} /* _set_mem_map */
+
+#if 0 /* driver model ordering issue */
+/*======================================================================
+
+       Routines for accessing socket information and register dumps via
+       /proc/bus/pccard/...
+
+======================================================================*/
+
+static ssize_t show_info(struct class_device *class_dev, char *buf)
+{
+       pcc_socket_t *s = container_of(class_dev, struct pcc_socket,
+               socket.dev);
+
+       return sprintf(buf, "type:     %s\nbase addr:    0x%08lx\n",
+               pcc[s->type].name, s->base);
+}
+
+static ssize_t show_exca(struct class_device *class_dev, char *buf)
+{
+       /* FIXME */
+
+       return 0;
+}
+
+static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL);
+static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL);
+#endif
+
+/*====================================================================*/
+
+/* this is horribly ugly... proper locking needs to be done here at
+ * some time... */
+#define LOCKED(x) do {                                 \
+       int retval;                                     \
+       unsigned long flags;                            \
+       spin_lock_irqsave(&pcc_lock, flags);            \
+       retval = x;                                     \
+       spin_unlock_irqrestore(&pcc_lock, flags);       \
+       return retval;                                  \
+} while (0)
+
+
+static int pcc_get_status(struct pcmcia_socket *s, u_int *value)
+{
+       unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+       if (socket[sock].flags & IS_ALIVE) {
+               *value = 0;
+               return -EINVAL;
+       }
+       LOCKED(_pcc_get_status(sock, value));
+}
+
+static int pcc_get_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+       unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+       if (socket[sock].flags & IS_ALIVE)
+               return -EINVAL;
+       LOCKED(_pcc_get_socket(sock, state));
+}
+
+static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state)
+{
+       unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+       if (socket[sock].flags & IS_ALIVE)
+               return -EINVAL;
+
+       LOCKED(_pcc_set_socket(sock, state));
+}
+
+static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
+{
+       unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+       if (socket[sock].flags & IS_ALIVE)
+               return -EINVAL;
+       LOCKED(_pcc_set_io_map(sock, io));
+}
+
+static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem)
+{
+       unsigned int sock = container_of(s, struct pcc_socket, socket)->number;
+
+       if (socket[sock].flags & IS_ALIVE)
+               return -EINVAL;
+       LOCKED(_pcc_set_mem_map(sock, mem));
+}
+
+static int pcc_init(struct pcmcia_socket *s)
+{
+       debug(4, "m32r-pcc: init call\n");
+       return 0;
+}
+
+static int pcc_suspend(struct pcmcia_socket *sock)
+{
+       return pcc_set_socket(sock, &dead_socket);
+}
+
+static struct pccard_operations pcc_operations = {
+       .init                   = pcc_init,
+       .suspend                = pcc_suspend,
+       .get_status             = pcc_get_status,
+       .get_socket             = pcc_get_socket,
+       .set_socket             = pcc_set_socket,
+       .set_io_map             = pcc_set_io_map,
+       .set_mem_map            = pcc_set_mem_map,
+};
+
+/*====================================================================*/
+
+static int m32r_pcc_suspend(struct device *dev, u32 state, u32 level)
+{
+       int ret = 0;
+       if (level == SUSPEND_SAVE_STATE)
+               ret = pcmcia_socket_dev_suspend(dev, state);
+       return ret;
+}
+
+static int m32r_pcc_resume(struct device *dev, u32 level)
+{
+       int ret = 0;
+       if (level == RESUME_RESTORE_STATE)
+               ret = pcmcia_socket_dev_resume(dev);
+       return ret;
+}
+
+
+static struct device_driver pcc_driver = {
+       .name = "pcc",
+       .bus = &platform_bus_type,
+       .suspend = m32r_pcc_suspend,
+       .resume = m32r_pcc_resume,
+};
+
+static struct platform_device pcc_device = {
+       .name = "pcc",
+       .id = 0,
+};
+
+/*====================================================================*/
+
+static int __init init_m32r_pcc(void)
+{
+       int i, ret;
+
+       ret = driver_register(&pcc_driver);
+       if (ret)
+               return ret;
+
+       ret = platform_device_register(&pcc_device);
+       if (ret){
+               driver_unregister(&pcc_driver);
+               return ret;
+       }
+
+       printk(KERN_INFO "m32r PCC probe:\n");
+
+       pcc_sockets = 0;
+
+       add_pcc_socket(M32R_PCC0_BASE, PCC0_IRQ, M32R_PCC0_MAPBASE, 0x1000);
+
+#ifdef CONFIG_M32RPCC_SLOT2
+       add_pcc_socket(M32R_PCC1_BASE, PCC1_IRQ, M32R_PCC1_MAPBASE, 0x2000);
+#endif
+
+       if (pcc_sockets == 0) {
+               printk("socket is not found.\n");
+               platform_device_unregister(&pcc_device);
+               driver_unregister(&pcc_driver);
+               return -ENODEV;
+       }
+
+       /* Set up interrupt handler(s) */
+
+       for (i = 0 ; i < pcc_sockets ; i++) {
+               socket[i].socket.dev.dev = &pcc_device.dev;
+               socket[i].socket.ops = &pcc_operations;
+               socket[i].socket.owner = THIS_MODULE;
+               socket[i].number = i;
+               ret = pcmcia_register_socket(&socket[i].socket);
+               if (ret && i--) {
+                       for (; i>= 0; i--)
+                               pcmcia_unregister_socket(&socket[i].socket);
+                       break;
+               }
+#if 0  /* driver model ordering issue */
+               class_device_create_file(&socket[i].socket.dev,
+                                        &class_device_attr_info);
+               class_device_create_file(&socket[i].socket.dev,
+                                        &class_device_attr_exca);
+#endif
+       }
+
+       /* Finally, schedule a polling interrupt */
+       if (poll_interval != 0) {
+               poll_timer.function = pcc_interrupt_wrapper;
+               poll_timer.data = 0;
+               init_timer(&poll_timer);
+               poll_timer.expires = jiffies + poll_interval;
+               add_timer(&poll_timer);
+       }
+
+       return 0;
+} /* init_m32r_pcc */
+
+static void __exit exit_m32r_pcc(void)
+{
+       int i;
+
+       for (i = 0; i < pcc_sockets; i++)
+               pcmcia_unregister_socket(&socket[i].socket);
+
+       platform_device_unregister(&pcc_device);
+       if (poll_interval != 0)
+               del_timer_sync(&poll_timer);
+
+       driver_unregister(&pcc_driver);
+} /* exit_m32r_pcc */
+
+module_init(init_m32r_pcc);
+module_exit(exit_m32r_pcc);
+MODULE_LICENSE("Dual MPL/GPL");
+/*====================================================================*/
diff --git a/drivers/pcmcia/m32r_pcc.h b/drivers/pcmcia/m32r_pcc.h
new file mode 100644 (file)
index 0000000..e4fffe4
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2001 by Hiroyuki Kondo
+ */
+
+#define M32R_MAX_PCC   2
+
+/*
+ * M32R PC Card Controler
+ */
+#define M32R_PCC0_BASE        0x00ef7000
+#define M32R_PCC1_BASE        0x00ef7020
+
+/*
+ * Register offsets
+ */
+#define PCCR            0x00
+#define PCADR           0x04
+#define PCMOD           0x08
+#define PCIRC           0x0c
+#define PCCSIGCR        0x10
+#define PCATCR          0x14
+
+/*
+ * PCCR
+ */
+#define PCCR_PCEN       (1UL<<(31-31))
+
+/*
+ * PCIRC
+ */
+#define PCIRC_BWERR     (1UL<<(31-7))
+#define PCIRC_CDIN1     (1UL<<(31-14))
+#define PCIRC_CDIN2     (1UL<<(31-15))
+#define PCIRC_BEIEN     (1UL<<(31-23))
+#define PCIRC_CIIEN     (1UL<<(31-30))
+#define PCIRC_COIEN     (1UL<<(31-31))
+
+/*
+ * PCCSIGCR
+ */
+#define PCCSIGCR_SEN    (1UL<<(31-3))
+#define PCCSIGCR_VEN    (1UL<<(31-7))
+#define PCCSIGCR_CRST   (1UL<<(31-15))
+#define PCCSIGCR_COCR   (1UL<<(31-31))
+
+/*
+ *
+ */
+#define PCMOD_AS_ATTRIB        (1UL<<(31-19))
+#define PCMOD_AS_IO    (1UL<<(31-18))
+
+#define PCMOD_CBSZ     (1UL<<(31-23)) /* set for 8bit */
+
+#define PCMOD_DBEX     (1UL<<(31-31)) /* set for excahnge */
+
+/*
+ * M32R PCC Map addr
+ */
+#define M32R_PCC0_MAPBASE        0x14000000
+#define M32R_PCC1_MAPBASE        0x16000000
+
+#define M32R_PCC_MAPMAX                 0x02000000
+
+#define M32R_PCC_MAPSIZE        0x00001000 /* XXX */
+#define M32R_PCC_MAPMASK       (~(M32R_PCC_MAPMAX-1))
diff --git a/drivers/pcmcia/pcmcia_compat.c b/drivers/pcmcia/pcmcia_compat.c
new file mode 100644 (file)
index 0000000..673d345
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * PCMCIA 16-bit compatibility functions
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * Copyright (C) 2004 Dominik Brodowski
+ *
+ * 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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#define IN_CARD_SERVICES
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ss.h>
+
+#include "cs_internal.h"
+
+int pcmcia_get_first_tuple(client_handle_t handle, tuple_t *tuple)
+{
+       struct pcmcia_socket *s;
+       if (CHECK_HANDLE(handle))
+               return CS_BAD_HANDLE;
+       s = SOCKET(handle);
+       return pccard_get_first_tuple(s, handle->Function, tuple);
+}
+EXPORT_SYMBOL(pcmcia_get_first_tuple);
+
+int pcmcia_get_next_tuple(client_handle_t handle, tuple_t *tuple)
+{
+       struct pcmcia_socket *s;
+       if (CHECK_HANDLE(handle))
+               return CS_BAD_HANDLE;
+       s = SOCKET(handle);
+       return pccard_get_next_tuple(s, handle->Function, tuple);
+}
+EXPORT_SYMBOL(pcmcia_get_next_tuple);
+
+int pcmcia_get_tuple_data(client_handle_t handle, tuple_t *tuple)
+{
+       struct pcmcia_socket *s;
+       if (CHECK_HANDLE(handle))
+               return CS_BAD_HANDLE;
+       s = SOCKET(handle);
+       return pccard_get_tuple_data(s, tuple);
+}
+EXPORT_SYMBOL(pcmcia_get_tuple_data);
+
+int pcmcia_parse_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+       return pccard_parse_tuple(tuple, parse);
+}
+EXPORT_SYMBOL(pcmcia_parse_tuple);
+
+int pcmcia_validate_cis(client_handle_t handle, cisinfo_t *info)
+{
+       struct pcmcia_socket *s;
+       if (CHECK_HANDLE(handle))
+               return CS_BAD_HANDLE;
+       s = SOCKET(handle);
+       return pccard_validate_cis(s, handle->Function, info);
+}
+EXPORT_SYMBOL(pcmcia_validate_cis);
+
+int pcmcia_get_configuration_info(client_handle_t handle,
+                                 config_info_t *config)
+{
+       struct pcmcia_socket *s;
+
+       if ((CHECK_HANDLE(handle)) || !config)
+               return CS_BAD_HANDLE;
+       s = SOCKET(handle);
+       if (!s)
+               return CS_BAD_HANDLE;
+       return pccard_get_configuration_info(s, handle->Function, config);
+}
+EXPORT_SYMBOL(pcmcia_get_configuration_info);
+
+int pcmcia_reset_card(client_handle_t handle, client_req_t *req)
+{
+       struct pcmcia_socket *skt;
+    
+       if (CHECK_HANDLE(handle))
+               return CS_BAD_HANDLE;
+       skt = SOCKET(handle);
+       if (!skt)
+               return CS_BAD_HANDLE;
+
+       return pccard_reset_card(skt);
+}
+EXPORT_SYMBOL(pcmcia_reset_card);
+
+int pcmcia_get_status(client_handle_t handle, cs_status_t *status)
+{
+       struct pcmcia_socket *s;
+       if (CHECK_HANDLE(handle))
+               return CS_BAD_HANDLE;
+       s = SOCKET(handle);
+       return pccard_get_status(s, handle->Function, status);
+}
+EXPORT_SYMBOL(pcmcia_get_status);
+
+int pcmcia_access_configuration_register(client_handle_t handle,
+                                        conf_reg_t *reg)
+{
+       struct pcmcia_socket *s;
+       if (CHECK_HANDLE(handle))
+               return CS_BAD_HANDLE;
+       s = SOCKET(handle);
+       return pccard_access_configuration_register(s, handle->Function, reg);
+}
+EXPORT_SYMBOL(pcmcia_access_configuration_register);
+
+#ifdef CONFIG_PCMCIA_OBSOLETE
+
+int pcmcia_get_first_window(window_handle_t *win, win_req_t *req)
+{
+    if ((win == NULL) || ((*win)->magic != WINDOW_MAGIC))
+       return CS_BAD_HANDLE;
+
+    return pcmcia_get_window(((client_handle_t)*win)->Socket, win, 0, req);
+}
+EXPORT_SYMBOL(pcmcia_get_first_window);
+
+int pcmcia_get_next_window(window_handle_t *win, win_req_t *req)
+{
+    if ((win == NULL) || ((*win)->magic != WINDOW_MAGIC))
+       return CS_BAD_HANDLE;
+    return pcmcia_get_window((*win)->sock, win, (*win)->index+1, req);
+}
+EXPORT_SYMBOL(pcmcia_get_next_window);
+
+#endif
diff --git a/drivers/pcmcia/pxa2xx_sharpsl.c b/drivers/pcmcia/pxa2xx_sharpsl.c
new file mode 100644 (file)
index 0000000..cabb91f
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Sharp SL-C7xx Series PCMCIA routines
+ *
+ * Copyright (c) 2004-2005 Richard Purdie
+ *
+ * Based on Sharp's 2.4 kernel patches and pxa2xx_mainstone.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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+
+#include <asm/hardware/scoop.h>
+#include <asm/arch/corgi.h>
+#include <asm/arch/pxa-regs.h>
+
+#include "soc_common.h"
+
+#define        NO_KEEP_VS 0x0001
+
+static unsigned char keep_vs;
+static unsigned char keep_rd;
+
+static struct pcmcia_irqs irqs[] = {
+       { 0, CORGI_IRQ_GPIO_CF_CD, "PCMCIA0 CD"},
+};
+
+static void sharpsl_pcmcia_init_reset(void)
+{
+       reset_scoop();
+       keep_vs = NO_KEEP_VS;
+       keep_rd = 0;
+}
+
+static int sharpsl_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+       int ret;
+
+       /*
+        * Setup default state of GPIO outputs
+        * before we enable them as outputs.
+        */
+       GPSR(GPIO48_nPOE) =
+               GPIO_bit(GPIO48_nPOE) |
+               GPIO_bit(GPIO49_nPWE) |
+               GPIO_bit(GPIO50_nPIOR) |
+               GPIO_bit(GPIO51_nPIOW) |
+               GPIO_bit(GPIO52_nPCE_1) |
+               GPIO_bit(GPIO53_nPCE_2);
+
+       pxa_gpio_mode(GPIO48_nPOE_MD);
+       pxa_gpio_mode(GPIO49_nPWE_MD);
+       pxa_gpio_mode(GPIO50_nPIOR_MD);
+       pxa_gpio_mode(GPIO51_nPIOW_MD);
+       pxa_gpio_mode(GPIO52_nPCE_1_MD);
+       pxa_gpio_mode(GPIO53_nPCE_2_MD);
+       pxa_gpio_mode(GPIO54_pSKTSEL_MD);
+       pxa_gpio_mode(GPIO55_nPREG_MD);
+       pxa_gpio_mode(GPIO56_nPWAIT_MD);
+       pxa_gpio_mode(GPIO57_nIOIS16_MD);
+
+       /* Register interrupts */
+       ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+       if (ret) {
+               printk(KERN_ERR "Request for Compact Flash IRQ failed\n");
+               return ret;
+       }
+
+       /* Enable interrupt */
+       write_scoop_reg(SCOOP_IMR, 0x00C0);
+       write_scoop_reg(SCOOP_MCR, 0x0101);
+       keep_vs = NO_KEEP_VS;
+
+       skt->irq = CORGI_IRQ_GPIO_CF_IRQ;
+
+       return 0;
+}
+
+static void sharpsl_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+       soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+       /* CF_BUS_OFF */
+       sharpsl_pcmcia_init_reset();
+}
+
+
+static void sharpsl_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+                                   struct pcmcia_state *state)
+{
+       unsigned short cpr, csr;
+
+       cpr = read_scoop_reg(SCOOP_CPR);
+
+       write_scoop_reg(SCOOP_IRM, 0x00FF);
+       write_scoop_reg(SCOOP_ISR, 0x0000);
+       write_scoop_reg(SCOOP_IRM, 0x0000);
+       csr = read_scoop_reg(SCOOP_CSR);
+       if (csr & 0x0004) {
+               /* card eject */
+               write_scoop_reg(SCOOP_CDR, 0x0000);
+               keep_vs = NO_KEEP_VS;
+       }
+       else if (!(keep_vs & NO_KEEP_VS)) {
+               /* keep vs1,vs2 */
+               write_scoop_reg(SCOOP_CDR, 0x0000);
+               csr |= keep_vs;
+       }
+       else if (cpr & 0x0003) {
+               /* power on */
+               write_scoop_reg(SCOOP_CDR, 0x0000);
+               keep_vs = (csr & 0x00C0);
+       }
+       else {
+               /* card detect */
+               write_scoop_reg(SCOOP_CDR, 0x0002);
+       }
+
+       state->detect = (csr & 0x0004) ? 0 : 1;
+       state->ready  = (csr & 0x0002) ? 1 : 0;
+       state->bvd1   = (csr & 0x0010) ? 1 : 0;
+       state->bvd2   = (csr & 0x0020) ? 1 : 0;
+       state->wrprot = (csr & 0x0008) ? 1 : 0;
+       state->vs_3v  = (csr & 0x0040) ? 0 : 1;
+       state->vs_Xv  = (csr & 0x0080) ? 0 : 1;
+
+       if ((cpr & 0x0080) && ((cpr & 0x8040) != 0x8040)) {
+               printk(KERN_ERR "sharpsl_pcmcia_socket_state(): CPR=%04X, Low voltage!\n", cpr);
+       }
+
+}
+
+
+static int sharpsl_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+                                      const socket_state_t *state)
+{
+       unsigned long flags;
+
+       unsigned short cpr, ncpr, ccr, nccr, mcr, nmcr, imr, nimr;
+
+       switch (state->Vcc) {
+       case    0:      break;
+       case    33:     break;
+       case    50:     break;
+       default:
+                printk(KERN_ERR "sharpsl_pcmcia_configure_socket(): bad Vcc %u\n", state->Vcc);
+                return -1;
+       }
+
+       if ((state->Vpp!=state->Vcc) && (state->Vpp!=0)) {
+               printk(KERN_ERR "CF slot cannot support Vpp %u\n", state->Vpp);
+               return -1;
+       }
+
+       local_irq_save(flags);
+
+       nmcr = (mcr = read_scoop_reg(SCOOP_MCR)) & ~0x0010;
+       ncpr = (cpr = read_scoop_reg(SCOOP_CPR)) & ~0x0083;
+       nccr = (ccr = read_scoop_reg(SCOOP_CCR)) & ~0x0080;
+       nimr = (imr = read_scoop_reg(SCOOP_IMR)) & ~0x003E;
+
+       ncpr |= (state->Vcc == 33) ? 0x0001 :
+                               (state->Vcc == 50) ? 0x0002 : 0;
+       nmcr |= (state->flags&SS_IOCARD) ? 0x0010 : 0;
+       ncpr |= (state->flags&SS_OUTPUT_ENA) ? 0x0080 : 0;
+       nccr |= (state->flags&SS_RESET)? 0x0080: 0;
+       nimr |= ((skt->status&SS_DETECT) ? 0x0004 : 0)|
+                       ((skt->status&SS_READY)  ? 0x0002 : 0)|
+                       ((skt->status&SS_BATDEAD)? 0x0010 : 0)|
+                       ((skt->status&SS_BATWARN)? 0x0020 : 0)|
+                       ((skt->status&SS_STSCHG) ? 0x0010 : 0)|
+                       ((skt->status&SS_WRPROT) ? 0x0008 : 0);
+
+       if (!(ncpr & 0x0003)) {
+               keep_rd = 0;
+       } else if (!keep_rd) {
+               if (nccr & 0x0080)
+                       keep_rd = 1;
+               else
+                       nccr |= 0x0080;
+       }
+
+       if (mcr != nmcr)
+               write_scoop_reg(SCOOP_MCR, nmcr);
+       if (cpr != ncpr)
+               write_scoop_reg(SCOOP_CPR, ncpr);
+       if (ccr != nccr)
+               write_scoop_reg(SCOOP_CCR, nccr);
+       if (imr != nimr)
+               write_scoop_reg(SCOOP_IMR, nimr);
+
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+static void sharpsl_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+}
+
+static void sharpsl_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+}
+
+static struct pcmcia_low_level sharpsl_pcmcia_ops = {
+       .owner                          = THIS_MODULE,
+       .hw_init                        = sharpsl_pcmcia_hw_init,
+       .hw_shutdown            = sharpsl_pcmcia_hw_shutdown,
+       .socket_state           = sharpsl_pcmcia_socket_state,
+       .configure_socket       = sharpsl_pcmcia_configure_socket,
+       .socket_init            = sharpsl_pcmcia_socket_init,
+       .socket_suspend         = sharpsl_pcmcia_socket_suspend,
+       .first                          = 0,
+       .nr                                     = 1,
+};
+
+static struct platform_device *sharpsl_pcmcia_device;
+
+static int __init sharpsl_pcmcia_init(void)
+{
+       int ret;
+
+       sharpsl_pcmcia_device = kmalloc(sizeof(*sharpsl_pcmcia_device), GFP_KERNEL);
+       if (!sharpsl_pcmcia_device)
+               return -ENOMEM;
+       memset(sharpsl_pcmcia_device, 0, sizeof(*sharpsl_pcmcia_device));
+       sharpsl_pcmcia_device->name = "pxa2xx-pcmcia";
+       sharpsl_pcmcia_device->dev.platform_data = &sharpsl_pcmcia_ops;
+
+       ret = platform_device_register(sharpsl_pcmcia_device);
+       if (ret)
+               kfree(sharpsl_pcmcia_device);
+
+       return ret;
+}
+
+static void __exit sharpsl_pcmcia_exit(void)
+{
+       /*
+        * This call is supposed to free our sharpsl_pcmcia_device.
+        * Unfortunately platform_device don't have a free method, and
+        * we can't assume it's free of any reference at this point so we
+        * can't free it either.
+        */
+       platform_device_unregister(sharpsl_pcmcia_device);
+}
+
+module_init(sharpsl_pcmcia_init);
+module_exit(sharpsl_pcmcia_exit);
+
+MODULE_DESCRIPTION("Sharp SL Series PCMCIA Support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
new file mode 100644 (file)
index 0000000..41c246c
--- /dev/null
@@ -0,0 +1,818 @@
+/*
+ * rsrc_nonstatic.c -- Resource management routines for !SS_CAP_STATIC_MAP sockets
+ *
+ * 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.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ * (C) 1999            David A. Hinds
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/bulkmem.h>
+#include <pcmcia/cistpl.h>
+#include "cs_internal.h"
+
+MODULE_AUTHOR("David A. Hinds, Dominik Brodowski");
+MODULE_LICENSE("GPL");
+
+/* Parameters that can be set with 'insmod' */
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
+
+INT_MODULE_PARM(probe_mem,     1);             /* memory probe? */
+#ifdef CONFIG_PCMCIA_PROBE
+INT_MODULE_PARM(probe_io,      1);             /* IO port probe? */
+INT_MODULE_PARM(mem_limit,     0x10000);
+#endif
+
+/* for io_db and mem_db */
+struct resource_map {
+       u_long                  base, num;
+       struct resource_map     *next;
+};
+
+struct socket_data {
+       struct resource_map             mem_db;
+       struct resource_map             io_db;
+       unsigned int                    rsrc_mem_probe;
+};
+
+static DECLARE_MUTEX(rsrc_sem);
+#define MEM_PROBE_LOW  (1 << 0)
+#define MEM_PROBE_HIGH (1 << 1)
+
+
+/*======================================================================
+
+    Linux resource management extensions
+
+======================================================================*/
+
+static struct resource *
+make_resource(unsigned long b, unsigned long n, int flags, char *name)
+{
+       struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
+
+       if (res) {
+               memset(res, 0, sizeof(*res));
+               res->name = name;
+               res->start = b;
+               res->end = b + n - 1;
+               res->flags = flags;
+       }
+       return res;
+}
+
+static struct resource *
+claim_region(struct pcmcia_socket *s, unsigned long base, unsigned long size,
+            int type, char *name)
+{
+       struct resource *res, *parent;
+
+       parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource;
+       res = make_resource(base, size, type | IORESOURCE_BUSY, name);
+
+       if (res) {
+#ifdef CONFIG_PCI
+               if (s && s->cb_dev)
+                       parent = pci_find_parent_resource(s->cb_dev, res);
+#endif
+               if (!parent || request_resource(parent, res)) {
+                       kfree(res);
+                       res = NULL;
+               }
+       }
+       return res;
+}
+
+static void free_region(struct resource *res)
+{
+       if (res) {
+               release_resource(res);
+               kfree(res);
+       }
+}
+
+/*======================================================================
+
+    These manage the internal databases of available resources.
+
+======================================================================*/
+
+static int add_interval(struct resource_map *map, u_long base, u_long num)
+{
+    struct resource_map *p, *q;
+
+    for (p = map; ; p = p->next) {
+       if ((p != map) && (p->base+p->num-1 >= base))
+           return -1;
+       if ((p->next == map) || (p->next->base > base+num-1))
+           break;
+    }
+    q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
+    if (!q) return CS_OUT_OF_RESOURCE;
+    q->base = base; q->num = num;
+    q->next = p->next; p->next = q;
+    return CS_SUCCESS;
+}
+
+/*====================================================================*/
+
+static int sub_interval(struct resource_map *map, u_long base, u_long num)
+{
+    struct resource_map *p, *q;
+
+    for (p = map; ; p = q) {
+       q = p->next;
+       if (q == map)
+           break;
+       if ((q->base+q->num > base) && (base+num > q->base)) {
+           if (q->base >= base) {
+               if (q->base+q->num <= base+num) {
+                   /* Delete whole block */
+                   p->next = q->next;
+                   kfree(q);
+                   /* don't advance the pointer yet */
+                   q = p;
+               } else {
+                   /* Cut off bit from the front */
+                   q->num = q->base + q->num - base - num;
+                   q->base = base + num;
+               }
+           } else if (q->base+q->num <= base+num) {
+               /* Cut off bit from the end */
+               q->num = base - q->base;
+           } else {
+               /* Split the block into two pieces */
+               p = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
+               if (!p) return CS_OUT_OF_RESOURCE;
+               p->base = base+num;
+               p->num = q->base+q->num - p->base;
+               q->num = base - q->base;
+               p->next = q->next ; q->next = p;
+           }
+       }
+    }
+    return CS_SUCCESS;
+}
+
+/*======================================================================
+
+    These routines examine a region of IO or memory addresses to
+    determine what ranges might be genuinely available.
+
+======================================================================*/
+
+#ifdef CONFIG_PCMCIA_PROBE
+static void do_io_probe(struct pcmcia_socket *s, kio_addr_t base, kio_addr_t num)
+{
+    struct resource *res;
+    struct socket_data *s_data = s->resource_data;
+    kio_addr_t i, j, bad;
+    int any;
+    u_char *b, hole, most;
+
+    printk(KERN_INFO "cs: IO port probe %#lx-%#lx:",
+          base, base+num-1);
+
+    /* First, what does a floating port look like? */
+    b = kmalloc(256, GFP_KERNEL);
+    if (!b) {
+            printk(KERN_ERR "do_io_probe: unable to kmalloc 256 bytes");
+            return;
+    }
+    memset(b, 0, 256);
+    for (i = base, most = 0; i < base+num; i += 8) {
+       res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");
+       if (!res)
+           continue;
+       hole = inb(i);
+       for (j = 1; j < 8; j++)
+           if (inb(i+j) != hole) break;
+       free_region(res);
+       if ((j == 8) && (++b[hole] > b[most]))
+           most = hole;
+       if (b[most] == 127) break;
+    }
+    kfree(b);
+
+    bad = any = 0;
+    for (i = base; i < base+num; i += 8) {
+       res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA IO probe");
+       if (!res)
+           continue;
+       for (j = 0; j < 8; j++)
+           if (inb(i+j) != most) break;
+       free_region(res);
+       if (j < 8) {
+           if (!any)
+               printk(" excluding");
+           if (!bad)
+               bad = any = i;
+       } else {
+           if (bad) {
+               sub_interval(&s_data->io_db, bad, i-bad);
+               printk(" %#lx-%#lx", bad, i-1);
+               bad = 0;
+           }
+       }
+    }
+    if (bad) {
+       if ((num > 16) && (bad == base) && (i == base+num)) {
+           printk(" nothing: probe failed.\n");
+           return;
+       } else {
+           sub_interval(&s_data->io_db, bad, i-bad);
+           printk(" %#lx-%#lx", bad, i-1);
+       }
+    }
+
+    printk(any ? "\n" : " clean.\n");
+}
+#endif
+
+/*======================================================================
+
+    This is tricky... when we set up CIS memory, we try to validate
+    the memory window space allocations.
+
+======================================================================*/
+
+/* Validation function for cards with a valid CIS */
+static int readable(struct pcmcia_socket *s, struct resource *res, cisinfo_t *info)
+{
+       int ret = -1;
+
+       s->cis_mem.res = res;
+       s->cis_virt = ioremap(res->start, s->map_size);
+       if (s->cis_virt) {
+               ret = pccard_validate_cis(s, BIND_FN_ALL, info);
+               /* invalidate mapping and CIS cache */
+               iounmap(s->cis_virt);
+               s->cis_virt = NULL;
+               destroy_cis_cache(s);
+       }
+       s->cis_mem.res = NULL;
+       if ((ret != 0) || (info->Chains == 0))
+               return 0;
+       return 1;
+}
+
+/* Validation function for simple memory cards */
+static int checksum(struct pcmcia_socket *s, struct resource *res)
+{
+       pccard_mem_map map;
+       int i, a = 0, b = -1, d;
+       void __iomem *virt;
+
+       virt = ioremap(res->start, s->map_size);
+       if (virt) {
+               map.map = 0;
+               map.flags = MAP_ACTIVE;
+               map.speed = 0;
+               map.res = res;
+               map.card_start = 0;
+               s->ops->set_mem_map(s, &map);
+
+               /* Don't bother checking every word... */
+               for (i = 0; i < s->map_size; i += 44) {
+                       d = readl(virt+i);
+                       a += d;
+                       b &= d;
+               }
+
+               map.flags = 0;
+               s->ops->set_mem_map(s, &map);
+
+               iounmap(virt);
+       }
+
+       return (b == -1) ? -1 : (a>>1);
+}
+
+static int
+cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size)
+{
+       struct resource *res1, *res2;
+       cisinfo_t info1, info2;
+       int ret = 0;
+
+       res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
+       res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");
+
+       if (res1 && res2) {
+               ret = readable(s, res1, &info1);
+               ret += readable(s, res2, &info2);
+       }
+
+       free_region(res2);
+       free_region(res1);
+
+       return (ret == 2) && (info1.Chains == info2.Chains);
+}
+
+static int
+checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size)
+{
+       struct resource *res1, *res2;
+       int a = -1, b = -1;
+
+       res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe");
+       res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "cs memory probe");
+
+       if (res1 && res2) {
+               a = checksum(s, res1);
+               b = checksum(s, res2);
+       }
+
+       free_region(res2);
+       free_region(res1);
+
+       return (a == b) && (a >= 0);
+}
+
+/*======================================================================
+
+    The memory probe.  If the memory list includes a 64K-aligned block
+    below 1MB, we probe in 64K chunks, and as soon as we accumulate at
+    least mem_limit free space, we quit.
+
+======================================================================*/
+
+static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
+{
+    struct socket_data *s_data = s->resource_data;
+    u_long i, j, bad, fail, step;
+
+    printk(KERN_INFO "cs: memory probe 0x%06lx-0x%06lx:",
+          base, base+num-1);
+    bad = fail = 0;
+    step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
+    /* cis_readable wants to map 2x map_size */
+    if (step < 2 * s->map_size)
+       step = 2 * s->map_size;
+    for (i = j = base; i < base+num; i = j + step) {
+       if (!fail) {
+           for (j = i; j < base+num; j += step) {
+               if (cis_readable(s, j, step))
+                   break;
+           }
+           fail = ((i == base) && (j == base+num));
+       }
+       if (fail) {
+           for (j = i; j < base+num; j += 2*step)
+               if (checksum_match(s, j, step) &&
+                   checksum_match(s, j + step, step))
+                   break;
+       }
+       if (i != j) {
+           if (!bad) printk(" excluding");
+           printk(" %#05lx-%#05lx", i, j-1);
+           sub_interval(&s_data->mem_db, i, j-i);
+           bad += j-i;
+       }
+    }
+    printk(bad ? "\n" : " clean.\n");
+    return (num - bad);
+}
+
+#ifdef CONFIG_PCMCIA_PROBE
+
+static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
+{
+    struct socket_data *s_data = s->resource_data;
+    u_long ok;
+    if (m == &s_data->mem_db)
+       return 0;
+    ok = inv_probe(m->next, s);
+    if (ok) {
+       if (m->base >= 0x100000)
+           sub_interval(&s_data->mem_db, m->base, m->num);
+       return ok;
+    }
+    if (m->base < 0x100000)
+       return 0;
+    return do_mem_probe(m->base, m->num, s);
+}
+
+static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
+{
+    struct resource_map *m, mm;
+    static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
+    u_long b, i, ok = 0;
+    struct socket_data *s_data = s->resource_data;
+
+    /* We do up to four passes through the list */
+    if (probe_mask & MEM_PROBE_HIGH) {
+       if (inv_probe(s_data->mem_db.next, s) > 0)
+           return;
+       printk(KERN_NOTICE "cs: warning: no high memory space "
+              "available!\n");
+    }
+    if ((probe_mask & MEM_PROBE_LOW) == 0)
+       return;
+    for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
+       mm = *m;
+       /* Only probe < 1 MB */
+       if (mm.base >= 0x100000) continue;
+       if ((mm.base | mm.num) & 0xffff) {
+           ok += do_mem_probe(mm.base, mm.num, s);
+           continue;
+       }
+       /* Special probe for 64K-aligned block */
+       for (i = 0; i < 4; i++) {
+           b = order[i] << 12;
+           if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) {
+               if (ok >= mem_limit)
+                   sub_interval(&s_data->mem_db, b, 0x10000);
+               else
+                   ok += do_mem_probe(b, 0x10000, s);
+           }
+       }
+    }
+}
+
+#else /* CONFIG_PCMCIA_PROBE */
+
+static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
+{
+       struct resource_map *m, mm;
+       struct socket_data *s_data = s->resource_data;
+
+       for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
+               mm = *m;
+               if (do_mem_probe(mm.base, mm.num, s))
+                       break;
+       }
+}
+
+#endif /* CONFIG_PCMCIA_PROBE */
+
+
+/*
+ * Locking note: this is the only place where we take
+ * both rsrc_sem and skt_sem.
+ */
+static void pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
+{
+       struct socket_data *s_data = s->resource_data;
+       if (probe_mem) {
+               unsigned int probe_mask;
+
+               down(&rsrc_sem);
+
+               probe_mask = MEM_PROBE_LOW;
+               if (s->features & SS_CAP_PAGE_REGS)
+                       probe_mask = MEM_PROBE_HIGH;
+
+               if (probe_mask & ~s_data->rsrc_mem_probe) {
+                       s_data->rsrc_mem_probe |= probe_mask;
+
+                       down(&s->skt_sem);
+
+                       if (s->state & SOCKET_PRESENT)
+                               validate_mem(s, probe_mask);
+
+                       up(&s->skt_sem);
+               }
+
+               up(&rsrc_sem);
+       }
+}
+
+struct pcmcia_align_data {
+       unsigned long   mask;
+       unsigned long   offset;
+       struct resource_map     *map;
+};
+
+static void
+pcmcia_common_align(void *align_data, struct resource *res,
+                   unsigned long size, unsigned long align)
+{
+       struct pcmcia_align_data *data = align_data;
+       unsigned long start;
+       /*
+        * Ensure that we have the correct start address
+        */
+       start = (res->start & ~data->mask) + data->offset;
+       if (start < res->start)
+               start += data->mask + 1;
+       res->start = start;
+}
+
+static void
+pcmcia_align(void *align_data, struct resource *res,
+            unsigned long size, unsigned long align)
+{
+       struct pcmcia_align_data *data = align_data;
+       struct resource_map *m;
+
+       pcmcia_common_align(data, res, size, align);
+
+       for (m = data->map->next; m != data->map; m = m->next) {
+               unsigned long start = m->base;
+               unsigned long end = m->base + m->num - 1;
+
+               /*
+                * If the lower resources are not available, try aligning
+                * to this entry of the resource database to see if it'll
+                * fit here.
+                */
+               if (res->start < start) {
+                       res->start = start;
+                       pcmcia_common_align(data, res, size, align);
+               }
+
+               /*
+                * If we're above the area which was passed in, there's
+                * no point proceeding.
+                */
+               if (res->start >= res->end)
+                       break;
+
+               if ((res->start + size - 1) <= end)
+                       break;
+       }
+
+       /*
+        * If we failed to find something suitable, ensure we fail.
+        */
+       if (m == data->map)
+               res->start = res->end;
+}
+
+/*
+ * Adjust an existing IO region allocation, but making sure that we don't
+ * encroach outside the resources which the user supplied.
+ */
+static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start,
+                                     unsigned long r_end, struct pcmcia_socket *s)
+{
+       struct resource_map *m;
+       struct socket_data *s_data = s->resource_data;
+       int ret = -ENOMEM;
+
+       down(&rsrc_sem);
+       for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
+               unsigned long start = m->base;
+               unsigned long end = m->base + m->num - 1;
+
+               if (start > r_start || r_end > end)
+                       continue;
+
+               ret = adjust_resource(res, r_start, r_end - r_start + 1);
+               break;
+       }
+       up(&rsrc_sem);
+
+       return ret;
+}
+
+/*======================================================================
+
+    These find ranges of I/O ports or memory addresses that are not
+    currently allocated by other devices.
+
+    The 'align' field should reflect the number of bits of address
+    that need to be preserved from the initial value of *base.  It
+    should be a power of two, greater than or equal to 'num'.  A value
+    of 0 means that all bits of *base are significant.  *base should
+    also be strictly less than 'align'.
+
+======================================================================*/
+
+struct resource *nonstatic_find_io_region(unsigned long base, int num,
+                  unsigned long align, struct pcmcia_socket *s)
+{
+       struct resource *res = make_resource(0, num, IORESOURCE_IO, s->dev.class_id);
+       struct socket_data *s_data = s->resource_data;
+       struct pcmcia_align_data data;
+       unsigned long min = base;
+       int ret;
+
+       if (align == 0)
+               align = 0x10000;
+
+       data.mask = align - 1;
+       data.offset = base & data.mask;
+       data.map = &s_data->io_db;
+
+       down(&rsrc_sem);
+#ifdef CONFIG_PCI
+       if (s->cb_dev) {
+               ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
+                                            min, 0, pcmcia_align, &data);
+       } else
+#endif
+               ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
+                                       1, pcmcia_align, &data);
+       up(&rsrc_sem);
+
+       if (ret != 0) {
+               kfree(res);
+               res = NULL;
+       }
+       return res;
+}
+
+struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long align,
+                                int low, struct pcmcia_socket *s)
+{
+       struct resource *res = make_resource(0, num, IORESOURCE_MEM, s->dev.class_id);
+       struct socket_data *s_data = s->resource_data;
+       struct pcmcia_align_data data;
+       unsigned long min, max;
+       int ret, i;
+
+       low = low || !(s->features & SS_CAP_PAGE_REGS);
+
+       data.mask = align - 1;
+       data.offset = base & data.mask;
+       data.map = &s_data->mem_db;
+
+       for (i = 0; i < 2; i++) {
+               if (low) {
+                       max = 0x100000UL;
+                       min = base < max ? base : 0;
+               } else {
+                       max = ~0UL;
+                       min = 0x100000UL + base;
+               }
+
+               down(&rsrc_sem);
+#ifdef CONFIG_PCI
+               if (s->cb_dev) {
+                       ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
+                                                    1, min, 0,
+                                                    pcmcia_align, &data);
+               } else
+#endif
+                       ret = allocate_resource(&iomem_resource, res, num, min,
+                                               max, 1, pcmcia_align, &data);
+               up(&rsrc_sem);
+               if (ret == 0 || low)
+                       break;
+               low = 1;
+       }
+
+       if (ret != 0) {
+               kfree(res);
+               res = NULL;
+       }
+       return res;
+}
+
+
+static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj)
+{
+       u_long base, num;
+       struct socket_data *data = s->resource_data;
+       int ret;
+
+       base = adj->resource.memory.Base;
+       num = adj->resource.memory.Size;
+       if ((num == 0) || (base+num-1 < base))
+               return CS_BAD_SIZE;
+
+       ret = CS_SUCCESS;
+
+       down(&rsrc_sem);
+       switch (adj->Action) {
+       case ADD_MANAGED_RESOURCE:
+               ret = add_interval(&data->mem_db, base, num);
+               break;
+       case REMOVE_MANAGED_RESOURCE:
+               ret = sub_interval(&data->mem_db, base, num);
+               if (ret == CS_SUCCESS) {
+                       struct pcmcia_socket *socket;
+                       down_read(&pcmcia_socket_list_rwsem);
+                       list_for_each_entry(socket, &pcmcia_socket_list, socket_list)
+                               release_cis_mem(socket);
+                       up_read(&pcmcia_socket_list_rwsem);
+               }
+               break;
+       default:
+               ret = CS_UNSUPPORTED_FUNCTION;
+       }
+       up(&rsrc_sem);
+
+       return ret;
+}
+
+
+static int adjust_io(struct pcmcia_socket *s, adjust_t *adj)
+{
+       struct socket_data *data = s->resource_data;
+       kio_addr_t base, num;
+       int ret = CS_SUCCESS;
+
+       base = adj->resource.io.BasePort;
+       num = adj->resource.io.NumPorts;
+       if ((base < 0) || (base > 0xffff))
+               return CS_BAD_BASE;
+       if ((num <= 0) || (base+num > 0x10000) || (base+num <= base))
+               return CS_BAD_SIZE;
+
+       down(&rsrc_sem);
+       switch (adj->Action) {
+       case ADD_MANAGED_RESOURCE:
+               if (add_interval(&data->io_db, base, num) != 0) {
+                       ret = CS_IN_USE;
+                       break;
+               }
+#ifdef CONFIG_PCMCIA_PROBE
+               if (probe_io)
+                       do_io_probe(s, base, num);
+#endif
+               break;
+       case REMOVE_MANAGED_RESOURCE:
+               sub_interval(&data->io_db, base, num);
+               break;
+       default:
+               ret = CS_UNSUPPORTED_FUNCTION;
+               break;
+       }
+       up(&rsrc_sem);
+
+       return ret;
+}
+
+
+static int nonstatic_adjust_resource_info(struct pcmcia_socket *s, adjust_t *adj)
+{
+       switch (adj->Resource) {
+       case RES_MEMORY_RANGE:
+               return adjust_memory(s, adj);
+       case RES_IO_RANGE:
+               return adjust_io(s, adj);
+       }
+       return CS_UNSUPPORTED_FUNCTION;
+}
+
+static int nonstatic_init(struct pcmcia_socket *s)
+{
+       struct socket_data *data;
+
+       data = kmalloc(sizeof(struct socket_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->mem_db.next = &data->mem_db;
+       data->io_db.next = &data->io_db;
+
+       s->resource_data = (void *) data;
+
+       return 0;
+}
+
+static void nonstatic_release_resource_db(struct pcmcia_socket *s)
+{
+       struct socket_data *data = s->resource_data;
+       struct resource_map *p, *q;
+
+       down(&rsrc_sem);
+       for (p = data->mem_db.next; p != &data->mem_db; p = q) {
+               q = p->next;
+               kfree(p);
+       }
+       for (p = data->io_db.next; p != &data->io_db; p = q) {
+               q = p->next;
+               kfree(p);
+       }
+       up(&rsrc_sem);
+}
+
+
+struct pccard_resource_ops pccard_nonstatic_ops = {
+       .validate_mem = pcmcia_nonstatic_validate_mem,
+       .adjust_io_region = nonstatic_adjust_io_region,
+       .find_io = nonstatic_find_io_region,
+       .find_mem = nonstatic_find_mem_region,
+       .adjust_resource = nonstatic_adjust_resource_info,
+       .init = nonstatic_init,
+       .exit = nonstatic_release_resource_db,
+};
+EXPORT_SYMBOL(pccard_nonstatic_ops);
diff --git a/drivers/pcmcia/vrc4171_card.c b/drivers/pcmcia/vrc4171_card.c
new file mode 100644 (file)
index 0000000..24a544c
--- /dev/null
@@ -0,0 +1,744 @@
+/*
+ * vrc4171_card.c, NEC VRC4171 Card Controller driver for Socket Services.
+ *
+ * Copyright (C) 2003  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/vr41xx/vrc4171.h>
+
+#include <pcmcia/ss.h>
+
+#include "i82365.h"
+
+MODULE_DESCRIPTION("NEC VRC4171 Card Controllers driver for Socket Services");
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@hh.iij4u.or.jp>");
+MODULE_LICENSE("GPL");
+
+#define CARD_MAX_SLOTS         2
+#define CARD_SLOTA             0
+#define CARD_SLOTB             1
+#define CARD_SLOTB_OFFSET      0x40
+
+#define CARD_MEM_START         0x10000000
+#define CARD_MEM_END           0x13ffffff
+#define CARD_MAX_MEM_OFFSET    0x3ffffff
+#define CARD_MAX_MEM_SPEED     1000
+
+#define CARD_CONTROLLER_INDEX  0x03e0
+#define CARD_CONTROLLER_DATA   0x03e1
+#define CARD_CONTROLLER_SIZE   2
+ /* Power register */
+  #define VPP_GET_VCC          0x01
+  #define POWER_ENABLE         0x10
+ #define CARD_VOLTAGE_SENSE    0x1f
+  #define VCC_3VORXV_CAPABLE   0x00
+  #define VCC_XV_ONLY          0x01
+  #define VCC_3V_CAPABLE       0x02
+  #define VCC_5V_ONLY          0x03
+ #define CARD_VOLTAGE_SELECT   0x2f
+  #define VCC_3V               0x01
+  #define VCC_5V               0x00
+  #define VCC_XV               0x02
+  #define VCC_STATUS_3V                0x02
+  #define VCC_STATUS_5V                0x01
+  #define VCC_STATUS_XV                0x03
+ #define GLOBAL_CONTROL                0x1e
+  #define EXWRBK               0x04
+  #define IRQPM_EN             0x08
+  #define CLRPMIRQ             0x10
+
+#define IO_MAX_MAPS    2
+#define MEM_MAX_MAPS   5
+
+enum {
+       SLOT_PROBE = 0,
+       SLOT_NOPROBE_IO,
+       SLOT_NOPROBE_MEM,
+       SLOT_NOPROBE_ALL
+};
+
+typedef struct vrc4171_socket {
+       int noprobe;
+       struct pcmcia_socket pcmcia_socket;
+       char name[24];
+       int csc_irq;
+       int io_irq;
+} vrc4171_socket_t;
+
+static vrc4171_socket_t vrc4171_sockets[CARD_MAX_SLOTS];
+static int vrc4171_slotb = SLOTB_IS_NONE;
+static unsigned int vrc4171_irq;
+static uint16_t vrc4171_irq_mask = 0xdeb8;
+
+static inline uint8_t exca_read_byte(int slot, uint8_t index)
+{
+       if (slot == CARD_SLOTB)
+               index += CARD_SLOTB_OFFSET;
+
+       outb(index, CARD_CONTROLLER_INDEX);
+       return inb(CARD_CONTROLLER_DATA);
+}
+
+static inline uint16_t exca_read_word(int slot, uint8_t index)
+{
+       uint16_t data;
+
+       if (slot == CARD_SLOTB)
+               index += CARD_SLOTB_OFFSET;
+
+       outb(index++, CARD_CONTROLLER_INDEX);
+       data = inb(CARD_CONTROLLER_DATA);
+
+       outb(index, CARD_CONTROLLER_INDEX);
+       data |= ((uint16_t)inb(CARD_CONTROLLER_DATA)) << 8;
+
+       return data;
+}
+
+static inline uint8_t exca_write_byte(int slot, uint8_t index, uint8_t data)
+{
+       if (slot == CARD_SLOTB)
+               index += CARD_SLOTB_OFFSET;
+
+       outb(index, CARD_CONTROLLER_INDEX);
+       outb(data, CARD_CONTROLLER_DATA);
+
+       return data;
+}
+
+static inline uint16_t exca_write_word(int slot, uint8_t index, uint16_t data)
+{
+       if (slot == CARD_SLOTB)
+               index += CARD_SLOTB_OFFSET;
+
+       outb(index++, CARD_CONTROLLER_INDEX);
+       outb(data, CARD_CONTROLLER_DATA);
+
+       outb(index, CARD_CONTROLLER_INDEX);
+       outb((uint8_t)(data >> 8), CARD_CONTROLLER_DATA);
+
+       return data;
+}
+
+static inline int search_nonuse_irq(void)
+{
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               if (vrc4171_irq_mask & (1 << i)) {
+                       vrc4171_irq_mask &= ~(1 << i);
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
+static int pccard_init(struct pcmcia_socket *sock)
+{
+       vrc4171_socket_t *socket;
+       unsigned int slot;
+
+       sock->features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
+       sock->irq_mask = 0;
+       sock->map_size = 0x1000;
+       sock->pci_irq = vrc4171_irq;
+
+       slot = sock->sock;
+       socket = &vrc4171_sockets[slot];
+       socket->csc_irq = search_nonuse_irq();
+       socket->io_irq = search_nonuse_irq();
+
+       return 0;
+}
+
+static int pccard_suspend(struct pcmcia_socket *sock)
+{
+       return -EINVAL;
+}
+
+static int pccard_get_status(struct pcmcia_socket *sock, u_int *value)
+{
+       unsigned int slot;
+       uint8_t status, sense;
+       u_int val = 0;
+
+       if (sock == NULL || sock->sock >= CARD_MAX_SLOTS || value == NULL)
+               return -EINVAL;
+
+       slot = sock->sock;
+
+       status = exca_read_byte(slot, I365_STATUS);
+       if (exca_read_byte(slot, I365_INTCTL) & I365_PC_IOCARD) {
+               if (status & I365_CS_STSCHG)
+                       val |= SS_STSCHG;
+       } else {
+               if (!(status & I365_CS_BVD1))
+                       val |= SS_BATDEAD;
+               else if ((status & (I365_CS_BVD1 | I365_CS_BVD2)) == I365_CS_BVD1)
+                       val |= SS_BATWARN;
+       }
+       if ((status & I365_CS_DETECT) == I365_CS_DETECT)
+               val |= SS_DETECT;
+       if (status & I365_CS_WRPROT)
+               val |= SS_WRPROT;
+       if (status & I365_CS_READY)
+               val |= SS_READY;
+       if (status & I365_CS_POWERON)
+               val |= SS_POWERON;
+
+       sense = exca_read_byte(slot, CARD_VOLTAGE_SENSE);
+       switch (sense) {
+       case VCC_3VORXV_CAPABLE:
+               val |= SS_3VCARD | SS_XVCARD;
+               break;
+       case VCC_XV_ONLY:
+               val |= SS_XVCARD;
+               break;
+       case VCC_3V_CAPABLE:
+               val |= SS_3VCARD;
+               break;
+       default:
+               /* 5V only */
+               break;
+       }
+
+       *value = val;
+
+       return 0;
+}
+
+static inline u_char get_Vcc_value(uint8_t voltage)
+{
+       switch (voltage) {
+       case VCC_STATUS_3V:
+               return 33;
+       case VCC_STATUS_5V:
+               return 50;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static inline u_char get_Vpp_value(uint8_t power, u_char Vcc)
+{
+       if ((power & 0x03) == 0x01 || (power & 0x03) == 0x02)
+               return Vcc;
+
+       return 0;
+}
+
+static int pccard_get_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+       unsigned int slot;
+       uint8_t power, voltage, control, cscint;
+
+       if (sock == NULL || sock->sock >= CARD_MAX_SLOTS || state == NULL)
+               return -EINVAL;
+
+       slot = sock->sock;
+
+       power = exca_read_byte(slot, I365_POWER);
+       voltage = exca_read_byte(slot, CARD_VOLTAGE_SELECT);
+
+       state->Vcc = get_Vcc_value(voltage);
+       state->Vpp = get_Vpp_value(power, state->Vcc);
+
+       state->flags = 0;
+       if (power & POWER_ENABLE)
+               state->flags |= SS_PWR_AUTO;
+       if (power & I365_PWR_OUT)
+               state->flags |= SS_OUTPUT_ENA;
+
+       control = exca_read_byte(slot, I365_INTCTL);
+       if (control & I365_PC_IOCARD)
+               state->flags |= SS_IOCARD;
+       if (!(control & I365_PC_RESET))
+               state->flags |= SS_RESET;
+
+        cscint = exca_read_byte(slot, I365_CSCINT);
+       state->csc_mask = 0;
+       if (state->flags & SS_IOCARD) {
+               if (cscint & I365_CSC_STSCHG)
+                       state->flags |= SS_STSCHG;
+       } else {
+               if (cscint & I365_CSC_BVD1)
+                       state->csc_mask |= SS_BATDEAD;
+               if (cscint & I365_CSC_BVD2)
+                       state->csc_mask |= SS_BATWARN;
+       }
+       if (cscint & I365_CSC_READY)
+               state->csc_mask |= SS_READY;
+       if (cscint & I365_CSC_DETECT)
+               state->csc_mask |= SS_DETECT;
+
+       return 0;
+}
+
+static inline uint8_t set_Vcc_value(u_char Vcc)
+{
+       switch (Vcc) {
+       case 33:
+               return VCC_3V;
+       case 50:
+               return VCC_5V;
+       }
+
+       /* Small voltage is chosen for safety. */
+       return VCC_3V;
+}
+
+static int pccard_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
+{
+       vrc4171_socket_t *socket;
+       unsigned int slot;
+       uint8_t voltage, power, control, cscint;
+
+       if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
+           (state->Vpp != state->Vcc && state->Vpp != 0) ||
+           (state->Vcc != 50 && state->Vcc != 33 && state->Vcc != 0))
+               return -EINVAL;
+
+       slot = sock->sock;
+       socket = &vrc4171_sockets[slot];
+
+       spin_lock_irq(&sock->lock);
+
+       voltage = set_Vcc_value(state->Vcc);
+       exca_write_byte(slot, CARD_VOLTAGE_SELECT, voltage);
+
+       power = POWER_ENABLE;
+       if (state->Vpp == state->Vcc)
+               power |= VPP_GET_VCC;
+       if (state->flags & SS_OUTPUT_ENA)
+               power |= I365_PWR_OUT;
+       exca_write_byte(slot, I365_POWER, power);
+
+       control = 0;
+       if (state->io_irq != 0)
+               control |= socket->io_irq;
+       if (state->flags & SS_IOCARD)
+               control |= I365_PC_IOCARD;
+       if (state->flags & SS_RESET)
+               control &= ~I365_PC_RESET;
+       else
+               control |= I365_PC_RESET;
+       exca_write_byte(slot, I365_INTCTL, control);
+
+        cscint = 0;
+        exca_write_byte(slot, I365_CSCINT, cscint);
+       exca_read_byte(slot, I365_CSC); /* clear CardStatus change */
+       if (state->csc_mask != 0)
+               cscint |= socket->csc_irq << 8;
+       if (state->flags & SS_IOCARD) {
+               if (state->csc_mask & SS_STSCHG)
+                       cscint |= I365_CSC_STSCHG;
+       } else {
+               if (state->csc_mask & SS_BATDEAD)
+                       cscint |= I365_CSC_BVD1;
+               if (state->csc_mask & SS_BATWARN)
+                       cscint |= I365_CSC_BVD2;
+       }
+       if (state->csc_mask & SS_READY)
+               cscint |= I365_CSC_READY;
+       if (state->csc_mask & SS_DETECT)
+               cscint |= I365_CSC_DETECT;
+        exca_write_byte(slot, I365_CSCINT, cscint);
+
+       spin_unlock_irq(&sock->lock);
+
+       return 0;
+}
+
+static int pccard_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io)
+{
+       unsigned int slot;
+       uint8_t ioctl, addrwin;
+       u_char map;
+
+       if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
+           io == NULL || io->map >= IO_MAX_MAPS ||
+           io->start > 0xffff || io->stop > 0xffff || io->start > io->stop)
+               return -EINVAL;
+
+       slot = sock->sock;
+       map = io->map;
+
+       addrwin = exca_read_byte(slot, I365_ADDRWIN);
+       if (addrwin & I365_ENA_IO(map)) {
+               addrwin &= ~I365_ENA_IO(map);
+               exca_write_byte(slot, I365_ADDRWIN, addrwin);
+       }
+
+       exca_write_word(slot, I365_IO(map)+I365_W_START, io->start);
+       exca_write_word(slot, I365_IO(map)+I365_W_STOP, io->stop);
+
+       ioctl = 0;
+       if (io->speed > 0)
+               ioctl |= I365_IOCTL_WAIT(map);
+       if (io->flags & MAP_16BIT)
+               ioctl |= I365_IOCTL_16BIT(map);
+       if (io->flags & MAP_AUTOSZ)
+               ioctl |= I365_IOCTL_IOCS16(map);
+       if (io->flags & MAP_0WS)
+               ioctl |= I365_IOCTL_0WS(map);
+       exca_write_byte(slot, I365_IOCTL, ioctl);
+
+       if (io->flags & MAP_ACTIVE) {
+               addrwin |= I365_ENA_IO(map);
+               exca_write_byte(slot, I365_ADDRWIN, addrwin);
+       }
+
+       return 0;
+}
+
+static int pccard_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem)
+{
+       unsigned int slot;
+       uint16_t start, stop, offset;
+       uint8_t addrwin;
+       u_char map;
+
+       if (sock == NULL || sock->sock >= CARD_MAX_SLOTS ||
+           mem == NULL || mem->map >= MEM_MAX_MAPS ||
+           mem->sys_start < CARD_MEM_START || mem->sys_start > CARD_MEM_END ||
+           mem->sys_stop < CARD_MEM_START || mem->sys_stop > CARD_MEM_END ||
+           mem->sys_start > mem->sys_stop ||
+           mem->card_start > CARD_MAX_MEM_OFFSET ||
+           mem->speed > CARD_MAX_MEM_SPEED)
+               return -EINVAL;
+
+       slot = sock->sock;
+       map = mem->map;
+
+       addrwin = exca_read_byte(slot, I365_ADDRWIN);
+       if (addrwin & I365_ENA_MEM(map)) {
+               addrwin &= ~I365_ENA_MEM(map);
+               exca_write_byte(slot, I365_ADDRWIN, addrwin);
+       }
+
+       start = (mem->sys_start >> 12) & 0x3fff;
+       if (mem->flags & MAP_16BIT)
+               start |= I365_MEM_16BIT;
+       exca_write_word(slot, I365_MEM(map)+I365_W_START, start);
+
+       stop = (mem->sys_stop >> 12) & 0x3fff;
+       switch (mem->speed) {
+       case 0:
+               break;
+       case 1:
+               stop |= I365_MEM_WS0;
+               break;
+       case 2:
+               stop |= I365_MEM_WS1;
+               break;
+       default:
+               stop |= I365_MEM_WS0 | I365_MEM_WS1;
+               break;
+       }
+       exca_write_word(slot, I365_MEM(map)+I365_W_STOP, stop);
+
+       offset = (mem->card_start >> 12) & 0x3fff;
+       if (mem->flags & MAP_ATTRIB)
+               offset |= I365_MEM_REG;
+       if (mem->flags & MAP_WRPROT)
+               offset |= I365_MEM_WRPROT;
+       exca_write_word(slot, I365_MEM(map)+I365_W_OFF, offset);
+
+       if (mem->flags & MAP_ACTIVE) {
+               addrwin |= I365_ENA_MEM(map);
+               exca_write_byte(slot, I365_ADDRWIN, addrwin);
+       }
+
+       return 0;
+}
+
+static struct pccard_operations vrc4171_pccard_operations = {
+       .init                   = pccard_init,
+       .suspend                = pccard_suspend,
+       .get_status             = pccard_get_status,
+       .get_socket             = pccard_get_socket,
+       .set_socket             = pccard_set_socket,
+       .set_io_map             = pccard_set_io_map,
+       .set_mem_map            = pccard_set_mem_map,
+};
+
+static inline unsigned int get_events(int slot)
+{
+       unsigned int events = 0;
+       uint8_t status, csc;
+
+       status = exca_read_byte(slot, I365_STATUS);
+       csc = exca_read_byte(slot, I365_CSC);
+
+       if (exca_read_byte(slot, I365_INTCTL) & I365_PC_IOCARD) {
+               if ((csc & I365_CSC_STSCHG) && (status & I365_CS_STSCHG))
+                       events |= SS_STSCHG;
+       } else {
+               if (csc & (I365_CSC_BVD1 | I365_CSC_BVD2)) {
+                       if (!(status & I365_CS_BVD1))
+                               events |= SS_BATDEAD;
+                       else if ((status & (I365_CS_BVD1 | I365_CS_BVD2)) == I365_CS_BVD1)
+                               events |= SS_BATWARN;
+               }
+       }
+       if ((csc & I365_CSC_READY) && (status & I365_CS_READY))
+               events |= SS_READY;
+       if ((csc & I365_CSC_DETECT) && ((status & I365_CS_DETECT) == I365_CS_DETECT))
+               events |= SS_DETECT;
+
+       return events;
+}
+
+static irqreturn_t pccard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       vrc4171_socket_t *socket;
+       unsigned int events;
+       irqreturn_t retval = IRQ_NONE;
+       uint16_t status;
+
+       status = vrc4171_get_irq_status();
+       if (status & IRQ_A) {
+               socket = &vrc4171_sockets[CARD_SLOTA];
+               if (socket->noprobe == SLOT_PROBE) {
+                       if (status & (1 << socket->csc_irq)) {
+                               events = get_events(CARD_SLOTA);
+                               if (events != 0) {
+                                       pcmcia_parse_events(&socket->pcmcia_socket, events);
+                                       retval = IRQ_HANDLED;
+                               }
+                       }
+               }
+       }
+
+       if (status & IRQ_B) {
+               socket = &vrc4171_sockets[CARD_SLOTB];
+               if (socket->noprobe == SLOT_PROBE) {
+                       if (status & (1 << socket->csc_irq)) {
+                               events = get_events(CARD_SLOTB);
+                               if (events != 0) {
+                                       pcmcia_parse_events(&socket->pcmcia_socket, events);
+                                       retval = IRQ_HANDLED;
+                               }
+                       }
+               }
+       }
+
+       return retval;
+}
+
+static inline void reserve_using_irq(int slot)
+{
+       unsigned int irq;
+
+       irq = exca_read_byte(slot, I365_INTCTL);
+       irq &= 0x0f;
+       vrc4171_irq_mask &= ~(1 << irq);
+
+       irq = exca_read_byte(slot, I365_CSCINT);
+       irq = (irq & 0xf0) >> 4;
+       vrc4171_irq_mask &= ~(1 << irq);
+}
+
+static int __devinit vrc4171_add_socket(int slot)
+{
+       vrc4171_socket_t *socket;
+       int retval;
+
+       if (slot >= CARD_MAX_SLOTS)
+               return -EINVAL;
+
+       socket = &vrc4171_sockets[slot];
+       if (socket->noprobe != SLOT_PROBE) {
+               uint8_t addrwin;
+
+               switch (socket->noprobe) {
+               case SLOT_NOPROBE_MEM:
+                       addrwin = exca_read_byte(slot, I365_ADDRWIN);
+                       addrwin &= 0x1f;
+                       exca_write_byte(slot, I365_ADDRWIN, addrwin);
+                       break;
+               case SLOT_NOPROBE_IO:
+                       addrwin = exca_read_byte(slot, I365_ADDRWIN);
+                       addrwin &= 0xc0;
+                       exca_write_byte(slot, I365_ADDRWIN, addrwin);
+                       break;
+               default:
+                       break;
+               }
+
+               reserve_using_irq(slot);
+
+               return 0;
+       }
+
+       sprintf(socket->name, "NEC VRC4171 Card Slot %1c", 'A' + slot);
+
+       socket->pcmcia_socket.ops = &vrc4171_pccard_operations;
+
+       retval = pcmcia_register_socket(&socket->pcmcia_socket);
+       if (retval != 0)
+               return retval;
+
+       exca_write_byte(slot, I365_ADDRWIN, 0);
+
+       exca_write_byte(slot, GLOBAL_CONTROL, 0);
+
+       return 0;
+}
+
+static void vrc4171_remove_socket(int slot)
+{
+       vrc4171_socket_t *socket;
+
+       if (slot >= CARD_MAX_SLOTS)
+               return;
+
+       socket = &vrc4171_sockets[slot];
+
+       pcmcia_unregister_socket(&socket->pcmcia_socket);
+}
+
+static int __devinit vrc4171_card_setup(char *options)
+{
+       if (options == NULL || *options == '\0')
+               return 0;
+
+       if (strncmp(options, "irq:", 4) == 0) {
+               int irq;
+               options += 4;
+               irq = simple_strtoul(options, &options, 0);
+               if (irq >= 0 && irq < NR_IRQS)
+                       vrc4171_irq = irq;
+
+               if (*options != ',')
+                       return 0;
+               options++;
+       }
+
+       if (strncmp(options, "slota:", 6) == 0) {
+               options += 6;
+               if (*options != '\0') {
+                       if (strncmp(options, "memnoprobe", 10) == 0) {
+                               vrc4171_sockets[CARD_SLOTA].noprobe = SLOT_NOPROBE_MEM;
+                               options += 10;
+                       } else if (strncmp(options, "ionoprobe", 9) == 0) {
+                               vrc4171_sockets[CARD_SLOTA].noprobe = SLOT_NOPROBE_IO;
+                               options += 9;
+                       } else if ( strncmp(options, "noprobe", 7) == 0) {
+                               vrc4171_sockets[CARD_SLOTA].noprobe = SLOT_NOPROBE_ALL;
+                               options += 7;
+                       }
+
+                       if (*options != ',')
+                               return 0;
+                       options++;
+               } else
+                       return 0;
+
+       }
+
+       if (strncmp(options, "slotb:", 6) == 0) {
+               options += 6;
+               if (*options != '\0') {
+                       if (strncmp(options, "pccard", 6) == 0) {
+                               vrc4171_slotb = SLOTB_IS_PCCARD;
+                               options += 6;
+                       } else if (strncmp(options, "cf", 2) == 0) {
+                               vrc4171_slotb = SLOTB_IS_CF;
+                               options += 2;
+                       } else if (strncmp(options, "flashrom", 8) == 0) {
+                               vrc4171_slotb = SLOTB_IS_FLASHROM;
+                               options += 8;
+                       } else if (strncmp(options, "none", 4) == 0) {
+                               vrc4171_slotb = SLOTB_IS_NONE;
+                               options += 4;
+                       }
+
+                       if (*options != ',')
+                               return 0;
+                       options++;
+
+                       if (strncmp(options, "memnoprobe", 10) == 0)
+                               vrc4171_sockets[CARD_SLOTB].noprobe = SLOT_NOPROBE_MEM;
+                       if (strncmp(options, "ionoprobe", 9) == 0)
+                               vrc4171_sockets[CARD_SLOTB].noprobe = SLOT_NOPROBE_IO;
+                       if (strncmp(options, "noprobe", 7) == 0)
+                               vrc4171_sockets[CARD_SLOTB].noprobe = SLOT_NOPROBE_ALL;
+               }
+       }
+
+       return 0;
+}
+
+__setup("vrc4171_card=", vrc4171_card_setup);
+
+static int __devinit vrc4171_card_init(void)
+{
+       int retval, slot;
+
+       vrc4171_set_multifunction_pin(vrc4171_slotb);
+
+       if (request_region(CARD_CONTROLLER_INDEX, CARD_CONTROLLER_SIZE,
+                              "NEC VRC4171 Card Controller") == NULL)
+               return -EBUSY;
+
+       for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
+               if (slot == CARD_SLOTB && vrc4171_slotb == SLOTB_IS_NONE)
+                       break;
+
+               retval = vrc4171_add_socket(slot);
+               if (retval != 0)
+                       return retval;
+       }
+
+       retval = request_irq(vrc4171_irq, pccard_interrupt, SA_SHIRQ,
+                            "NEC VRC4171 Card Controller", vrc4171_sockets);
+       if (retval < 0) {
+               for (slot = 0; slot < CARD_MAX_SLOTS; slot++)
+                       vrc4171_remove_socket(slot);
+
+               return retval;
+       }
+
+       printk(KERN_INFO "NEC VRC4171 Card Controller, connected to IRQ %d\n", vrc4171_irq);
+
+       return 0;
+}
+
+static void __devexit vrc4171_card_exit(void)
+{
+       int slot;
+
+       for (slot = 0; slot < CARD_MAX_SLOTS; slot++)
+               vrc4171_remove_socket(slot);
+
+       release_region(CARD_CONTROLLER_INDEX, CARD_CONTROLLER_SIZE);
+}
+
+module_init(vrc4171_card_init);
+module_exit(vrc4171_card_exit);
diff --git a/drivers/pcmcia/vrc4173_cardu.c b/drivers/pcmcia/vrc4173_cardu.c
new file mode 100644 (file)
index 0000000..49de11e
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ * FILE NAME
+ *     drivers/pcmcia/vrc4173_cardu.c
+ *
+ * BRIEF MODULE DESCRIPTION
+ *     NEC VRC4173 CARDU driver for Socket Services
+ *     (This device doesn't support CardBus. it is supporting only 16bit PC Card.)
+ *
+ * Copyright 2002,2003 Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ *  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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+
+#include <pcmcia/ss.h>
+
+#include "vrc4173_cardu.h"
+
+MODULE_DESCRIPTION("NEC VRC4173 CARDU driver for Socket Services");
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@hh.iij4u.or.jp>");
+MODULE_LICENSE("GPL");
+
+static int vrc4173_cardu_slots;
+
+static vrc4173_socket_t cardu_sockets[CARDU_MAX_SOCKETS];
+
+extern struct socket_info_t *pcmcia_register_socket (int slot,
+                                                     struct pccard_operations *vtable,
+                                                     int use_bus_pm);
+extern void pcmcia_unregister_socket(struct socket_info_t *s);
+
+static inline uint8_t exca_readb(vrc4173_socket_t *socket, uint16_t offset)
+{
+       return readb(socket->base + EXCA_REGS_BASE + offset);
+}
+
+static inline uint16_t exca_readw(vrc4173_socket_t *socket, uint16_t offset)
+{
+       uint16_t val;
+
+       val = readb(socket->base + EXCA_REGS_BASE + offset);
+       val |= (u16)readb(socket->base + EXCA_REGS_BASE + offset + 1) << 8;
+
+       return val;
+}
+
+static inline void exca_writeb(vrc4173_socket_t *socket, uint16_t offset, uint8_t val)
+{
+       writeb(val, socket->base + EXCA_REGS_BASE + offset);
+}
+
+static inline void exca_writew(vrc4173_socket_t *socket, uint8_t offset, uint16_t val)
+{
+       writeb((u8)val, socket->base + EXCA_REGS_BASE + offset);
+       writeb((u8)(val >> 8), socket->base + EXCA_REGS_BASE + offset + 1);
+}
+
+static inline uint32_t cardbus_socket_readl(vrc4173_socket_t *socket, u16 offset)
+{
+       return readl(socket->base + CARDBUS_SOCKET_REGS_BASE + offset);
+}
+
+static inline void cardbus_socket_writel(vrc4173_socket_t *socket, u16 offset, uint32_t val)
+{
+       writel(val, socket->base + CARDBUS_SOCKET_REGS_BASE + offset);
+}
+
+static void cardu_pciregs_init(struct pci_dev *dev)
+{
+       u32 syscnt;
+       u16 brgcnt;
+       u8 devcnt;
+
+       pci_write_config_dword(dev, 0x1c, 0x10000000);
+       pci_write_config_dword(dev, 0x20, 0x17fff000);
+       pci_write_config_dword(dev, 0x2c, 0);
+       pci_write_config_dword(dev, 0x30, 0xfffc);
+
+       pci_read_config_word(dev, BRGCNT, &brgcnt);
+       brgcnt &= ~IREQ_INT;
+       pci_write_config_word(dev, BRGCNT, brgcnt);
+
+       pci_read_config_dword(dev, SYSCNT, &syscnt);
+       syscnt &= ~(BAD_VCC_REQ_DISB|PCPCI_EN|CH_ASSIGN_MASK|SUB_ID_WR_EN|PCI_CLK_RIN);
+       syscnt |= (CH_ASSIGN_NODMA|ASYN_INT_MODE);
+       pci_write_config_dword(dev, SYSCNT, syscnt);
+
+       pci_read_config_byte(dev, DEVCNT, &devcnt);
+       devcnt &= ~(ZOOM_VIDEO_EN|SR_PCI_INT_SEL_MASK|PCI_INT_MODE|IRQ_MODE);
+       devcnt |= (SR_PCI_INT_SEL_NONE|IFG);
+       pci_write_config_byte(dev, DEVCNT, devcnt);
+
+       pci_write_config_byte(dev, CHIPCNT, S_PREF_DISB);
+
+       pci_write_config_byte(dev, SERRDIS, 0);
+}
+
+static int cardu_init(unsigned int slot)
+{
+       vrc4173_socket_t *socket = &cardu_sockets[slot];
+
+       cardu_pciregs_init(socket->dev);
+
+       /* CARD_SC bits are cleared by reading CARD_SC. */
+       exca_writeb(socket, GLO_CNT, 0);
+
+       socket->cap.features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
+       socket->cap.irq_mask = 0;
+       socket->cap.map_size = 0x1000;
+       socket->cap.pci_irq  = socket->dev->irq;
+       socket->events = 0;
+       spin_lock_init(socket->event_lock);
+
+       /* Enable PC Card status interrupts */
+       exca_writeb(socket, CARD_SCI, CARD_DT_EN|RDY_EN|BAT_WAR_EN|BAT_DEAD_EN);
+
+       return 0;
+}
+
+static int cardu_suspend(unsigned int slot)
+{
+       return -EINVAL;
+}
+
+static int cardu_register_callback(unsigned int sock,
+                                           void (*handler)(void *, unsigned int),
+                                           void * info)
+{
+       vrc4173_socket_t *socket = &cardu_sockets[sock];
+
+       socket->handler = handler;
+       socket->info = info;
+
+       return 0;
+}
+
+static int cardu_inquire_socket(unsigned int sock, socket_cap_t *cap)
+{
+       vrc4173_socket_t *socket = &cardu_sockets[sock];
+
+       *cap = socket->cap;
+
+       return 0;
+}
+
+static int cardu_get_status(unsigned int sock, u_int *value)
+{
+       vrc4173_socket_t *socket = &cardu_sockets[sock];
+       uint32_t state;
+       uint8_t status;
+       u_int val = 0;
+
+       status = exca_readb(socket, IF_STATUS);
+       if (status & CARD_PWR) val |= SS_POWERON;
+       if (status & READY) val |= SS_READY;
+       if (status & CARD_WP) val |= SS_WRPROT;
+       if ((status & (CARD_DETECT1|CARD_DETECT2)) == (CARD_DETECT1|CARD_DETECT2))
+               val |= SS_DETECT;
+       if (exca_readb(socket, INT_GEN_CNT) & CARD_TYPE_IO) {
+               if (status & STSCHG) val |= SS_STSCHG;
+       } else {
+               status &= BV_DETECT_MASK;
+               if (status != BV_DETECT_GOOD) {
+                       if (status == BV_DETECT_WARN) val |= SS_BATWARN;
+                       else val |= SS_BATDEAD;
+               }
+       }
+
+       state = cardbus_socket_readl(socket, SKT_PRE_STATE);
+       if (state & VOL_3V_CARD_DT) val |= SS_3VCARD;
+       if (state & VOL_XV_CARD_DT) val |= SS_XVCARD;
+       if (state & CB_CARD_DT) val |= SS_CARDBUS;
+       if (!(state &
+             (VOL_YV_CARD_DT|VOL_XV_CARD_DT|VOL_3V_CARD_DT|VOL_5V_CARD_DT|CCD20|CCD10)))
+               val |= SS_PENDING;
+
+       *value = val;
+
+       return 0;
+}
+
+static inline u_char get_Vcc_value(uint8_t val)
+{
+       switch (val & VCC_MASK) {
+       case VCC_3V:
+               return 33;
+       case VCC_5V:
+               return 50;
+       }
+
+       return 0;
+}
+
+static inline u_char get_Vpp_value(uint8_t val)
+{
+       switch (val & VPP_MASK) {
+       case VPP_12V:
+               return 120;
+       case VPP_VCC:
+               return get_Vcc_value(val);
+       }
+
+       return 0;
+}
+
+static int cardu_get_socket(unsigned int sock, socket_state_t *state)
+{
+       vrc4173_socket_t *socket = &cardu_sockets[sock];
+       uint8_t val;
+
+       val = exca_readb(socket, PWR_CNT);
+       state->Vcc = get_Vcc_value(val);
+       state->Vpp = get_Vpp_value(val);
+       state->flags = 0;
+       if (val & CARD_OUT_EN) state->flags |= SS_OUTPUT_ENA;
+
+       val = exca_readb(socket, INT_GEN_CNT);
+       if (!(val & CARD_REST0)) state->flags |= SS_RESET;
+       if (val & CARD_TYPE_IO) state->flags |= SS_IOCARD;
+
+       return 0;
+}
+
+static inline uint8_t set_Vcc_value(u_char Vcc)
+{
+       switch (Vcc) {
+       case 33:
+               return VCC_3V;
+       case 50:
+               return VCC_5V;
+       }
+
+       return VCC_0V;
+}
+
+static inline uint8_t set_Vpp_value(u_char Vpp)
+{
+       switch (Vpp) {
+       case 33:
+       case 50:
+               return VPP_VCC;
+       case 120:
+               return VPP_12V;
+       }
+
+       return VPP_0V;
+}
+
+static int cardu_set_socket(unsigned int sock, socket_state_t *state)
+{
+       vrc4173_socket_t *socket = &cardu_sockets[sock];
+       uint8_t val;
+
+       if (((state->Vpp == 33) || (state->Vpp == 50)) && (state->Vpp != state->Vcc))
+                       return -EINVAL;
+
+       val = set_Vcc_value(state->Vcc);
+       val |= set_Vpp_value(state->Vpp);
+       if (state->flags & SS_OUTPUT_ENA) val |= CARD_OUT_EN;
+       exca_writeb(socket, PWR_CNT, val);
+
+       val = exca_readb(socket, INT_GEN_CNT) & CARD_REST0;
+       if (state->flags & SS_RESET) val &= ~CARD_REST0;
+       else val |= CARD_REST0;
+       if (state->flags & SS_IOCARD) val |= CARD_TYPE_IO;
+       exca_writeb(socket, INT_GEN_CNT, val);
+
+       return 0;
+}
+
+static int cardu_get_io_map(unsigned int sock, struct pccard_io_map *io)
+{
+       vrc4173_socket_t *socket = &cardu_sockets[sock];
+       uint8_t ioctl, window;
+       u_char map;
+
+       map = io->map;
+       if (map > 1)
+               return -EINVAL;
+
+       io->start = exca_readw(socket, IO_WIN_SA(map));
+       io->stop = exca_readw(socket, IO_WIN_EA(map));
+
+       ioctl = exca_readb(socket, IO_WIN_CNT);
+       window = exca_readb(socket, ADR_WIN_EN);
+       io->flags  = (window & IO_WIN_EN(map)) ? MAP_ACTIVE : 0;
+       if (ioctl & IO_WIN_DATA_AUTOSZ(map))
+               io->flags |= MAP_AUTOSZ;
+       else if (ioctl & IO_WIN_DATA_16BIT(map))
+               io->flags |= MAP_16BIT;
+
+       return 0;
+}
+
+static int cardu_set_io_map(unsigned int sock, struct pccard_io_map *io)
+{
+       vrc4173_socket_t *socket = &cardu_sockets[sock];
+       uint16_t ioctl;
+       uint8_t window, enable;
+       u_char map;
+
+       map = io->map;
+       if (map > 1)
+               return -EINVAL;
+
+       window = exca_readb(socket, ADR_WIN_EN);
+       enable = IO_WIN_EN(map);
+
+       if (window & enable) {
+               window &= ~enable;
+               exca_writeb(socket, ADR_WIN_EN, window);
+       }
+
+       exca_writew(socket, IO_WIN_SA(map), io->start);
+       exca_writew(socket, IO_WIN_EA(map), io->stop);
+
+       ioctl = exca_readb(socket, IO_WIN_CNT) & ~IO_WIN_CNT_MASK(map);
+       if (io->flags & MAP_AUTOSZ) ioctl |= IO_WIN_DATA_AUTOSZ(map);
+       else if (io->flags & MAP_16BIT) ioctl |= IO_WIN_DATA_16BIT(map);
+       exca_writeb(socket, IO_WIN_CNT, ioctl);
+
+       if (io->flags & MAP_ACTIVE)
+               exca_writeb(socket, ADR_WIN_EN, window | enable);
+
+       return 0;
+}
+
+static int cardu_get_mem_map(unsigned int sock, struct pccard_mem_map *mem)
+{
+       vrc4173_socket_t *socket = &cardu_sockets[sock];
+       uint32_t start, stop, offset, page;
+       uint8_t window;
+       u_char map;
+
+       map = mem->map;
+       if (map > 4)
+               return -EINVAL;
+
+       window = exca_readb(socket, ADR_WIN_EN);
+       mem->flags = (window & MEM_WIN_EN(map)) ? MAP_ACTIVE : 0;
+
+       start = exca_readw(socket, MEM_WIN_SA(map));
+       mem->flags |= (start & MEM_WIN_DSIZE) ? MAP_16BIT : 0;
+       start = (start & 0x0fff) << 12;
+
+       stop = exca_readw(socket, MEM_WIN_EA(map));
+       stop = ((stop & 0x0fff) << 12) + 0x0fff;
+
+       offset = exca_readw(socket, MEM_WIN_OA(map));
+       mem->flags |= (offset & MEM_WIN_WP) ? MAP_WRPROT : 0;
+       mem->flags |= (offset & MEM_WIN_REGSET) ? MAP_ATTRIB : 0;
+       offset = ((offset & 0x3fff) << 12) + start;
+       mem->card_start = offset & 0x03ffffff;
+
+       page = exca_readb(socket, MEM_WIN_SAU(map)) << 24;
+       mem->sys_start = start + page;
+       mem->sys_stop = start + page;
+
+       return 0;
+}
+
+static int cardu_set_mem_map(unsigned int sock, struct pccard_mem_map *mem)
+{
+       vrc4173_socket_t *socket = &cardu_sockets[sock];
+       uint16_t value;
+       uint8_t window, enable;
+       u_long sys_start, sys_stop, card_start;
+       u_char map;
+
+       map = mem->map;
+       sys_start = mem->sys_start;
+       sys_stop = mem->sys_stop;
+       card_start = mem->card_start;
+
+       if (map > 4 || sys_start > sys_stop || ((sys_start ^ sys_stop) >> 24) ||
+           (card_start >> 26))
+               return -EINVAL;
+
+       window = exca_readb(socket, ADR_WIN_EN);
+       enable = MEM_WIN_EN(map);
+       if (window & enable) {
+               window &= ~enable;
+               exca_writeb(socket, ADR_WIN_EN, window);
+       }
+
+       exca_writeb(socket, MEM_WIN_SAU(map), sys_start >> 24);
+
+       value = (sys_start >> 12) & 0x0fff;
+       if (mem->flags & MAP_16BIT) value |= MEM_WIN_DSIZE;
+       exca_writew(socket, MEM_WIN_SA(map), value);
+
+       value = (sys_stop >> 12) & 0x0fff;
+       exca_writew(socket, MEM_WIN_EA(map), value);
+
+       value = ((card_start - sys_start) >> 12) & 0x3fff;
+       if (mem->flags & MAP_WRPROT) value |= MEM_WIN_WP;
+       if (mem->flags & MAP_ATTRIB) value |= MEM_WIN_REGSET;
+       exca_writew(socket, MEM_WIN_OA(map), value);
+
+       if (mem->flags & MAP_ACTIVE)
+               exca_writeb(socket, ADR_WIN_EN, window | enable);
+
+       return 0;
+}
+
+static void cardu_proc_setup(unsigned int sock, struct proc_dir_entry *base)
+{
+}
+
+static struct pccard_operations cardu_operations = {
+       .init                   = cardu_init,
+       .suspend                = cardu_suspend,
+       .register_callback      = cardu_register_callback,
+       .inquire_socket         = cardu_inquire_socket,
+       .get_status             = cardu_get_status,
+       .get_socket             = cardu_get_socket,
+       .set_socket             = cardu_set_socket,
+       .get_io_map             = cardu_get_io_map,
+       .set_io_map             = cardu_set_io_map,
+       .get_mem_map            = cardu_get_mem_map,
+       .set_mem_map            = cardu_set_mem_map,
+       .proc_setup             = cardu_proc_setup,
+};
+
+static void cardu_bh(void *data)
+{
+       vrc4173_socket_t *socket = (vrc4173_socket_t *)data;
+       uint16_t events;
+
+       spin_lock_irq(&socket->event_lock);
+       events = socket->events;
+       socket->events = 0;
+       spin_unlock_irq(&socket->event_lock);
+
+       if (socket->handler)
+               socket->handler(socket->info, events);
+}
+
+static uint16_t get_events(vrc4173_socket_t *socket)
+{
+       uint16_t events = 0;
+       uint8_t csc, status;
+
+       status = exca_readb(socket, IF_STATUS);
+       csc = exca_readb(socket, CARD_SC);
+       if ((csc & CARD_DT_CHG) &&
+           ((status & (CARD_DETECT1|CARD_DETECT2)) == (CARD_DETECT1|CARD_DETECT2)))
+               events |= SS_DETECT;
+
+       if ((csc & RDY_CHG) && (status & READY))
+               events |= SS_READY;
+
+       if (exca_readb(socket, INT_GEN_CNT) & CARD_TYPE_IO) {
+               if ((csc & BAT_DEAD_ST_CHG) && (status & STSCHG))
+                       events |= SS_STSCHG;
+       } else {
+               if (csc & (BAT_WAR_CHG|BAT_DEAD_ST_CHG)) {
+                       if ((status & BV_DETECT_MASK) != BV_DETECT_GOOD) {
+                               if (status == BV_DETECT_WARN) events |= SS_BATWARN;
+                               else events |= SS_BATDEAD;
+                       }
+               }
+       }
+
+       return events;
+}
+
+static void cardu_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       vrc4173_socket_t *socket = (vrc4173_socket_t *)dev_id;
+       uint16_t events;
+
+       INIT_WORK(&socket->tq_work, cardu_bh, socket);
+
+       events = get_events(socket);
+       if (events) {
+               spin_lock(&socket->event_lock);
+               socket->events |= events;
+               spin_unlock(&socket->event_lock);
+               schedule_work(&socket->tq_work);
+       }
+}
+
+static int __devinit vrc4173_cardu_probe(struct pci_dev *dev,
+                                         const struct pci_device_id *ent)
+{
+       vrc4173_socket_t *socket;
+       unsigned long start, len, flags;
+       int slot, err;
+
+       slot = vrc4173_cardu_slots++;
+       socket = &cardu_sockets[slot];
+       if (socket->noprobe != 0)
+               return -EBUSY;
+
+       sprintf(socket->name, "NEC VRC4173 CARDU%1d", slot+1);
+
+       if ((err = pci_enable_device(dev)) < 0)
+               return err;
+
+       start = pci_resource_start(dev, 0);
+       if (start == 0)
+               return -ENODEV;
+
+       len = pci_resource_len(dev, 0);
+       if (len == 0)
+               return -ENODEV;
+
+       if (((flags = pci_resource_flags(dev, 0)) & IORESOURCE_MEM) == 0)
+               return -EBUSY;
+
+       if ((err = pci_request_regions(dev, socket->name)) < 0)
+               return err;
+
+       socket->base = ioremap(start, len);
+       if (socket->base == NULL)
+               return -ENODEV;
+
+       socket->dev = dev;
+
+       socket->pcmcia_socket = pcmcia_register_socket(slot, &cardu_operations, 1);
+       if (socket->pcmcia_socket == NULL) {
+               iounmap(socket->base);
+               socket->base = NULL;
+               return -ENOMEM;
+       }
+
+       if (request_irq(dev->irq, cardu_interrupt, SA_SHIRQ, socket->name, socket) < 0) {
+               pcmcia_unregister_socket(socket->pcmcia_socket);
+               socket->pcmcia_socket = NULL;
+               iounmap(socket->base);
+               socket->base = NULL;
+               return -EBUSY;
+       }
+
+       printk(KERN_INFO "%s at %#08lx, IRQ %d\n", socket->name, start, dev->irq);
+
+       return 0;
+}
+
+static int __devinit vrc4173_cardu_setup(char *options)
+{
+       if (options == NULL || *options == '\0')
+               return 0;
+
+       if (strncmp(options, "cardu1:", 7) == 0) {
+               options += 7;
+               if (*options != '\0') {
+                       if (strncmp(options, "noprobe", 7) == 0) {
+                               cardu_sockets[CARDU1].noprobe = 1;
+                               options += 7;
+                       }
+
+                       if (*options != ',')
+                               return 0;
+               } else
+                       return 0;
+       }
+
+       if (strncmp(options, "cardu2:", 7) == 0) {
+               options += 7;
+               if ((*options != '\0') && (strncmp(options, "noprobe", 7) == 0))
+                       cardu_sockets[CARDU2].noprobe = 1;
+       }
+
+       return 0;
+}
+
+__setup("vrc4173_cardu=", vrc4173_cardu_setup);
+
+static struct pci_device_id vrc4173_cardu_id_table[] __devinitdata = {
+       {       .vendor         = PCI_VENDOR_ID_NEC,
+               .device         = PCI_DEVICE_ID_NEC_NAPCCARD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID, },
+        {0, }
+};
+
+static struct pci_driver vrc4173_cardu_driver = {
+       .name           = "NEC VRC4173 CARDU",
+       .probe          = vrc4173_cardu_probe,
+       .id_table       = vrc4173_cardu_id_table,
+};
+
+static int __devinit vrc4173_cardu_init(void)
+{
+       vrc4173_cardu_slots = 0;
+
+       return pci_module_init(&vrc4173_cardu_driver);
+}
+
+static void __devexit vrc4173_cardu_exit(void)
+{
+       pci_unregister_driver(&vrc4173_cardu_driver);
+}
+
+module_init(vrc4173_cardu_init);
+module_exit(vrc4173_cardu_exit);
diff --git a/drivers/pcmcia/vrc4173_cardu.h b/drivers/pcmcia/vrc4173_cardu.h
new file mode 100644 (file)
index 0000000..113726f
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * FILE NAME
+ *     drivers/pcmcia/vrc4173_cardu.h
+ *
+ * BRIEF MODULE DESCRIPTION
+ *     Include file for NEC VRC4173 CARDU.
+ *
+ * Copyright 2002 Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ *  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.
+ */
+#ifndef _VRC4173_CARDU_H
+#define _VRC4173_CARDU_H
+
+#include <linux/pci.h>
+
+#include <pcmcia/ss.h>
+
+#define CARDU_MAX_SOCKETS      2
+#define CARDU1                 0
+#define CARDU2                 1
+
+/*
+ * PCI Configuration Registers
+ */
+#define BRGCNT                 0x3e
+ #define POST_WR_EN            0x0400
+ #define MEM1_PREF_EN          0x0200
+ #define MEM0_PREF_EN          0x0100
+ #define IREQ_INT              0x0080
+ #define CARD_RST              0x0040
+ #define MABORT_MODE           0x0020
+ #define VGA_EN                        0x0008
+ #define ISA_EN                        0x0004
+ #define SERR_EN               0x0002
+ #define PERR_EN               0x0001
+
+#define SYSCNT                 0x80
+ #define BAD_VCC_REQ_DISB      0x00200000
+ #define PCPCI_EN              0x00080000
+ #define CH_ASSIGN_MASK                0x00070000
+ #define CH_ASSIGN_NODMA       0x00040000
+ #define SUB_ID_WR_EN          0x00000008
+ #define ASYN_INT_MODE         0x00000004
+ #define PCI_CLK_RIN           0x00000002
+
+#define DEVCNT                 0x91
+ #define ZOOM_VIDEO_EN         0x40
+ #define SR_PCI_INT_SEL_MASK   0x18
+ #define SR_PCI_INT_SEL_NONE   0x00
+ #define PCI_INT_MODE          0x04
+ #define IRQ_MODE              0x02
+ #define IFG                   0x01
+
+#define CHIPCNT                        0x9c
+ #define S_PREF_DISB           0x10
+
+#define SERRDIS                        0x9f
+ #define SERR_DIS_MAB          0x10
+ #define SERR_DIS_TAB          0x08
+ #define SERR_DIS_DT_PERR      0x04
+
+/*
+ * ExCA Registers
+ */
+#define EXCA_REGS_BASE         0x800
+#define EXCA_REGS_SIZE         0x800
+
+#define ID_REV                 0x000
+ #define IF_TYPE_16BIT         0x80
+
+#define IF_STATUS              0x001
+ #define CARD_PWR              0x40
+ #define READY                 0x20
+ #define CARD_WP               0x10
+ #define CARD_DETECT2          0x08
+ #define CARD_DETECT1          0x04
+ #define BV_DETECT_MASK                0x03
+ #define BV_DETECT_GOOD                0x03    /* Memory card */
+ #define BV_DETECT_WARN                0x02
+ #define BV_DETECT_BAD1                0x01
+ #define BV_DETECT_BAD0                0x00
+ #define STSCHG                        0x02    /* I/O card */
+ #define SPKR                  0x01
+
+#define PWR_CNT                        0x002
+ #define CARD_OUT_EN           0x80
+ #define VCC_MASK              0x18
+ #define VCC_3V                        0x18
+ #define VCC_5V                        0x10
+ #define VCC_0V                        0x00
+ #define VPP_MASK              0x03
+ #define VPP_12V               0x02
+ #define VPP_VCC               0x01
+ #define VPP_0V                        0x00
+
+#define INT_GEN_CNT            0x003
+ #define CARD_REST0            0x40
+ #define CARD_TYPE_MASK                0x20
+ #define CARD_TYPE_IO          0x20
+ #define CARD_TYPE_MEM         0x00
+
+#define CARD_SC                        0x004
+ #define CARD_DT_CHG           0x08
+ #define RDY_CHG               0x04
+ #define BAT_WAR_CHG           0x02
+ #define BAT_DEAD_ST_CHG       0x01
+
+#define CARD_SCI               0x005
+ #define CARD_DT_EN            0x08
+ #define RDY_EN                        0x04
+ #define BAT_WAR_EN            0x02
+ #define BAT_DEAD_EN           0x01
+
+#define ADR_WIN_EN             0x006
+ #define IO_WIN_EN(x)          (0x40 << (x))
+ #define MEM_WIN_EN(x)         (0x01 << (x))
+
+#define IO_WIN_CNT             0x007
+ #define IO_WIN_CNT_MASK(x)    (0x03 << ((x) << 2))
+ #define IO_WIN_DATA_AUTOSZ(x) (0x02 << ((x) << 2))
+ #define IO_WIN_DATA_16BIT(x)  (0x01 << ((x) << 2))
+
+#define IO_WIN_SA(x)           (0x008 + ((x) << 2))
+#define IO_WIN_EA(x)           (0x00a + ((x) << 2))
+
+#define MEM_WIN_SA(x)          (0x010 + ((x) << 3))
+ #define MEM_WIN_DSIZE         0x8000
+
+#define MEM_WIN_EA(x)          (0x012 + ((x) << 3))
+
+#define MEM_WIN_OA(x)          (0x014 + ((x) << 3))
+ #define MEM_WIN_WP            0x8000
+ #define MEM_WIN_REGSET                0x4000
+
+#define GEN_CNT                        0x016
+ #define VS2_STATUS            0x80
+ #define VS1_STATUS            0x40
+ #define EXCA_REG_RST_EN       0x02
+
+#define GLO_CNT                        0x01e
+ #define FUN_INT_LEV           0x08
+ #define INT_WB_CLR            0x04
+ #define CSC_INT_LEV           0x02
+
+#define IO_WIN_OAL(x)          (0x036 + ((x) << 1))
+#define IO_WIN_OAH(x)          (0x037 + ((x) << 1))
+
+#define MEM_WIN_SAU(x)         (0x040 + (x))
+
+#define IO_SETUP_TIM           0x080
+#define IO_CMD_TIM             0x081
+#define IO_HOLD_TIM            0x082
+#define MEM_SETUP_TIM(x)       (0x084 + ((x) << 2))
+#define MEM_CMD_TIM(x)         (0x085 + ((x) << 2))
+#define MEM_HOLD_TIM(x)                (0x086 + ((x) << 2))
+ #define TIM_CLOCKS(x)         ((x) - 1)
+
+#define MEM_TIM_SEL1           0x08c
+#define MEM_TIM_SEL2           0x08d
+ #define MEM_WIN_TIMSEL1(x)    (0x03 << (((x) & 3) << 1))
+
+#define MEM_WIN_PWEN           0x091
+ #define POSTWEN               0x01
+
+/*
+ * CardBus Socket Registers
+ */
+#define CARDBUS_SOCKET_REGS_BASE       0x000
+#define CARDBUS_SOCKET_REGS_SIZE       0x800
+
+#define SKT_EV                 0x000
+ #define POW_CYC_EV            0x00000008
+ #define CCD2_EV               0x00000004
+ #define CCD1_EV               0x00000002
+ #define CSTSCHG_EV            0x00000001
+
+#define SKT_MASK               0x004
+ #define POW_CYC_MASK          0x00000008
+ #define CCD_MASK              0x00000006
+ #define CSC_MASK              0x00000001
+
+#define SKT_PRE_STATE          0x008
+#define SKT_FORCE_EV           0x00c
+ #define VOL_3V_SKT            0x20000000
+ #define VOL_5V_SKT            0x10000000
+ #define CVS_TEST              0x00004000
+ #define VOL_YV_CARD_DT                0x00002000
+ #define VOL_XV_CARD_DT                0x00001000
+ #define VOL_3V_CARD_DT                0x00000800
+ #define VOL_5V_CARD_DT                0x00000400
+ #define BAD_VCC_REQ           0x00000200
+ #define DATA_LOST             0x00000100
+ #define NOT_A_CARD            0x00000080
+ #define CREADY                        0x00000040
+ #define CB_CARD_DT            0x00000020
+ #define R2_CARD_DT            0x00000010
+ #define POW_UP                        0x00000008
+ #define CCD20                 0x00000004
+ #define CCD10                 0x00000002
+ #define CSTSCHG               0x00000001
+
+#define SKT_CNT                        0x010
+ #define STP_CLK_EN            0x00000080
+ #define VCC_CNT_MASK          0x00000070
+ #define VCC_CNT_3V            0x00000030
+ #define VCC_CNT_5V            0x00000020
+ #define VCC_CNT_0V            0x00000000
+ #define VPP_CNT_MASK          0x00000007
+ #define VPP_CNT_3V            0x00000003
+ #define VPP_CNT_5V            0x00000002
+ #define VPP_CNT_12V           0x00000001
+ #define VPP_CNT_0V            0x00000000
+
+typedef struct vrc4173_socket {
+       int noprobe;
+       struct pci_dev *dev;
+       void *base;
+       void (*handler)(void *, unsigned int);
+       void *info;
+       socket_cap_t cap;
+       spinlock_t event_lock;
+       uint16_t events;
+       struct socket_info_t *pcmcia_socket;
+       struct work_struct tq_work;
+       char name[20];
+} vrc4173_socket_t;
+
+#endif /* _VRC4173_CARDU_H */
diff --git a/drivers/pnp/pnpacpi/Kconfig b/drivers/pnp/pnpacpi/Kconfig
new file mode 100644 (file)
index 0000000..0782cdc
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# Plug and Play ACPI configuration
+#
+config PNPACPI
+       bool "Plug and Play ACPI support (EXPERIMENTAL)"
+       depends on PNP && ACPI_BUS && EXPERIMENTAL
+       default y
+       ---help---
+         Linux uses the PNPACPI to autodetect built-in
+         mainboard resources (e.g. parallel port resources).
+
+          Some features (e.g. real hotplug) are not currently
+          implemented.
+
+          If you would like the kernel to detect and allocate resources to
+          your mainboard devices (on some systems they are disabled by the
+          BIOS) say Y here.  Also the PNPACPI can help prevent resource
+          conflicts between mainboard devices and other bus devices.
diff --git a/drivers/pnp/pnpacpi/Makefile b/drivers/pnp/pnpacpi/Makefile
new file mode 100644 (file)
index 0000000..905326f
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the kernel PNPACPI driver.
+#
+
+obj-y := core.o rsparser.o
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c
new file mode 100644 (file)
index 0000000..9a9352f
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * pnpacpi -- PnP ACPI driver
+ *
+ * Copyright (c) 2004 Matthieu Castet <castet.matthieu@free.fr>
+ * Copyright (c) 2004 Li Shaohua <shaohua.li@intel.com>
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/acpi.h>
+#include <linux/pnp.h>
+#include <acpi/acpi_bus.h>
+#include "pnpacpi.h"
+
+static int num = 0;
+
+static char __initdata excluded_id_list[] =
+       "PNP0C0A," /* Battery */
+       "PNP0C0C,PNP0C0E,PNP0C0D," /* Button */
+       "PNP0C09," /* EC */
+       "PNP0C0B," /* Fan */
+       "PNP0A03," /* PCI root */
+       "PNP0C0F," /* Link device */
+       "PNP0000," /* PIC */
+       "PNP0100," /* Timer */
+       ;
+static inline int is_exclusive_device(struct acpi_device *dev)
+{
+       return (!acpi_match_ids(dev, excluded_id_list));
+}
+
+void *pnpacpi_kmalloc(size_t size, int f)
+{
+       void *p = kmalloc(size, f);
+       if (p)
+               memset(p, 0, size);
+       return p;
+}
+
+/*
+ * Compatible Device IDs
+ */
+#define TEST_HEX(c) \
+       if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \
+               return 0
+#define TEST_ALPHA(c) \
+       if (!('@' <= (c) || (c) <= 'Z')) \
+               return 0
+static int __init ispnpidacpi(char *id)
+{
+       TEST_ALPHA(id[0]);
+       TEST_ALPHA(id[1]);
+       TEST_ALPHA(id[2]);
+       TEST_HEX(id[3]);
+       TEST_HEX(id[4]);
+       TEST_HEX(id[5]);
+       TEST_HEX(id[6]);
+       if (id[7] != '\0')
+               return 0;
+       return 1;
+}
+
+static void __init pnpidacpi_to_pnpid(char *id, char *str)
+{
+       str[0] = id[0];
+       str[1] = id[1];
+       str[2] = id[2];
+       str[3] = tolower(id[3]);
+       str[4] = tolower(id[4]);
+       str[5] = tolower(id[5]);
+       str[6] = tolower(id[6]);
+       str[7] = '\0';
+}
+
+static int pnpacpi_get_resources(struct pnp_dev * dev, struct pnp_resource_table * res)
+{
+       acpi_status status;
+       status = pnpacpi_parse_allocated_resource((acpi_handle)dev->data, 
+               &dev->res);
+       return ACPI_FAILURE(status) ? -ENODEV : 0;
+}
+
+static int pnpacpi_set_resources(struct pnp_dev * dev, struct pnp_resource_table * res)
+{
+       acpi_handle handle = dev->data;
+       struct acpi_buffer buffer;
+       int ret = 0;
+       acpi_status status;
+
+       ret = pnpacpi_build_resource_template(handle, &buffer);
+       if (ret)
+               return ret;
+       ret = pnpacpi_encode_resources(res, &buffer);
+       if (ret) {
+               kfree(buffer.pointer);
+               return ret;
+       }
+       status = acpi_set_current_resources(handle, &buffer);
+       if (ACPI_FAILURE(status))
+               ret = -EINVAL;
+       kfree(buffer.pointer);
+       return ret;
+}
+
+static int pnpacpi_disable_resources(struct pnp_dev *dev)
+{
+       acpi_status status;
+       
+       /* acpi_unregister_gsi(pnp_irq(dev, 0)); */
+       status = acpi_evaluate_object((acpi_handle)dev->data, 
+               "_DIS", NULL, NULL);
+       return ACPI_FAILURE(status) ? -ENODEV : 0;
+}
+
+struct pnp_protocol pnpacpi_protocol = {
+       .name   = "Plug and Play ACPI",
+       .get    = pnpacpi_get_resources,
+       .set    = pnpacpi_set_resources,
+       .disable = pnpacpi_disable_resources,
+};
+
+static int __init pnpacpi_add_device(struct acpi_device *device)
+{
+       acpi_handle temp = NULL;
+       acpi_status status;
+       struct pnp_id *dev_id;
+       struct pnp_dev *dev;
+
+       if (!ispnpidacpi(acpi_device_hid(device)) ||
+               is_exclusive_device(device))
+               return 0;
+
+       pnp_dbg("ACPI device : hid %s", acpi_device_hid(device));
+       dev =  pnpacpi_kmalloc(sizeof(struct pnp_dev), GFP_KERNEL);
+       if (!dev) {
+               pnp_err("Out of memory");
+               return -ENOMEM;
+       }
+       dev->data = device->handle;
+       /* .enabled means if the device can decode the resources */
+       dev->active = device->status.enabled;
+       status = acpi_get_handle(device->handle, "_SRS", &temp);
+       if (ACPI_SUCCESS(status))
+               dev->capabilities |= PNP_CONFIGURABLE;
+       dev->capabilities |= PNP_READ;
+       if (device->flags.dynamic_status)
+               dev->capabilities |= PNP_WRITE;
+       if (device->flags.removable)
+               dev->capabilities |= PNP_REMOVABLE;
+       status = acpi_get_handle(device->handle, "_DIS", &temp);
+       if (ACPI_SUCCESS(status))
+               dev->capabilities |= PNP_DISABLE;
+
+       dev->protocol = &pnpacpi_protocol;
+
+       if (strlen(acpi_device_name(device)))
+               strncpy(dev->name, acpi_device_name(device), sizeof(dev->name));
+       else
+               strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name));
+
+       dev->number = num;
+       
+       /* set the initial values for the PnP device */
+       dev_id = pnpacpi_kmalloc(sizeof(struct pnp_id), GFP_KERNEL);
+       if (!dev_id)
+               goto err;
+       pnpidacpi_to_pnpid(acpi_device_hid(device), dev_id->id);
+       pnp_add_id(dev_id, dev);
+
+       if(dev->active) {
+               /* parse allocated resource */
+               status = pnpacpi_parse_allocated_resource(device->handle, &dev->res);
+               if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
+                       pnp_err("PnPACPI: METHOD_NAME__CRS failure for %s", dev_id->id);
+                       goto err1;
+               }
+       }
+
+       if(dev->capabilities & PNP_CONFIGURABLE) {
+               status = pnpacpi_parse_resource_option_data(device->handle, 
+                       dev);
+               if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
+                       pnp_err("PnPACPI: METHOD_NAME__PRS failure for %s", dev_id->id);
+                       goto err1;
+               }
+       }
+       
+       /* parse compatible ids */
+       if (device->flags.compatible_ids) {
+               struct acpi_compatible_id_list *cid_list = device->pnp.cid_list;
+               int i;
+
+               for (i = 0; i < cid_list->count; i++) {
+                       if (!ispnpidacpi(cid_list->id[i].value))
+                               continue;
+                       dev_id = pnpacpi_kmalloc(sizeof(struct pnp_id), 
+                               GFP_KERNEL);
+                       if (!dev_id)
+                               continue;
+
+                       pnpidacpi_to_pnpid(cid_list->id[i].value, dev_id->id);
+                       pnp_add_id(dev_id, dev);
+               }
+       }
+
+       /* clear out the damaged flags */
+       if (!dev->active)
+               pnp_init_resource_table(&dev->res);
+       pnp_add_device(dev);
+       num ++;
+
+       return AE_OK;
+err1:
+       kfree(dev_id);
+err:
+       kfree(dev);
+       return -EINVAL;
+}
+
+static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
+       u32 lvl, void *context, void **rv)
+{
+       struct acpi_device *device;
+
+       if (!acpi_bus_get_device(handle, &device))
+               pnpacpi_add_device(device);
+       return AE_OK;
+}
+
+int __init pnpacpi_init(void)
+{
+       if (acpi_disabled) {
+               pnp_info("PnP ACPI: ACPI disable");
+               return 0;
+       }
+       pnp_info("PnP ACPI init");
+       pnp_register_protocol(&pnpacpi_protocol);
+       acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+                       ACPI_UINT32_MAX, pnpacpi_add_device_handler,
+                       NULL, NULL);
+       pnp_info("PnP ACPI: found %d devices", num);
+       return 0;
+}
+subsys_initcall(pnpacpi_init);
+
+EXPORT_SYMBOL(pnpacpi_protocol);
diff --git a/drivers/pnp/pnpacpi/pnpacpi.h b/drivers/pnp/pnpacpi/pnpacpi.h
new file mode 100644 (file)
index 0000000..76f907e
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef ACPI_PNP_H
+#define ACPI_PNP_H
+
+#include <acpi/acpi_bus.h>
+#include <linux/acpi.h>
+#include <linux/pnp.h>
+
+void *pnpacpi_kmalloc(size_t size, int f);
+acpi_status pnpacpi_parse_allocated_resource(acpi_handle, struct pnp_resource_table*);
+acpi_status pnpacpi_parse_resource_option_data(acpi_handle, struct pnp_dev*);
+int pnpacpi_encode_resources(struct pnp_resource_table *, struct acpi_buffer *);
+int pnpacpi_build_resource_template(acpi_handle, struct acpi_buffer*);
+#endif
diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c
new file mode 100644 (file)
index 0000000..df102f9
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ * pnpacpi -- PnP ACPI driver
+ *
+ * Copyright (c) 2004 Matthieu Castet <castet.matthieu@free.fr>
+ * Copyright (c) 2004 Li Shaohua <shaohua.li@intel.com>
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include "pnpacpi.h"
+
+#ifdef CONFIG_IA64
+#define valid_IRQ(i) (1)
+#else
+#define valid_IRQ(i) (((i) != 0) && ((i) != 2))
+#endif
+
+/*
+ * Allocated Resources
+ */
+static int irq_flags(int edge_level, int active_high_low)
+{
+       int flag;
+       if (edge_level == ACPI_LEVEL_SENSITIVE) {
+               if(active_high_low == ACPI_ACTIVE_LOW)
+                       flag = IORESOURCE_IRQ_LOWLEVEL;
+               else
+                       flag = IORESOURCE_IRQ_HIGHLEVEL;
+       }
+       else {
+               if(active_high_low == ACPI_ACTIVE_LOW)
+                       flag = IORESOURCE_IRQ_LOWEDGE;
+               else
+                       flag = IORESOURCE_IRQ_HIGHEDGE;
+       }
+       return flag;
+}
+
+static void decode_irq_flags(int flag, int *edge_level, int *active_high_low)
+{
+       switch (flag) {
+       case IORESOURCE_IRQ_LOWLEVEL:
+               *edge_level = ACPI_LEVEL_SENSITIVE;
+               *active_high_low = ACPI_ACTIVE_LOW;
+               break;
+       case IORESOURCE_IRQ_HIGHLEVEL:  
+               *edge_level = ACPI_LEVEL_SENSITIVE;
+               *active_high_low = ACPI_ACTIVE_HIGH;
+               break;
+       case IORESOURCE_IRQ_LOWEDGE:
+               *edge_level = ACPI_EDGE_SENSITIVE;
+               *active_high_low = ACPI_ACTIVE_LOW;
+               break;
+       case IORESOURCE_IRQ_HIGHEDGE:
+               *edge_level = ACPI_EDGE_SENSITIVE;
+               *active_high_low = ACPI_ACTIVE_HIGH;
+               break;
+       }
+}
+
+static void
+pnpacpi_parse_allocated_irqresource(struct pnp_resource_table * res, int irq)
+{
+       int i = 0;
+       while (!(res->irq_resource[i].flags & IORESOURCE_UNSET) &&
+                       i < PNP_MAX_IRQ)
+               i++;
+       if (i < PNP_MAX_IRQ) {
+               res->irq_resource[i].flags = IORESOURCE_IRQ;  //Also clears _UNSET flag
+               if (irq == -1) {
+                       res->irq_resource[i].flags |= IORESOURCE_DISABLED;
+                       return;
+               }
+               res->irq_resource[i].start =(unsigned long) irq;
+               res->irq_resource[i].end = (unsigned long) irq;
+       }
+}
+
+static void
+pnpacpi_parse_allocated_dmaresource(struct pnp_resource_table * res, int dma)
+{
+       int i = 0;
+       while (!(res->dma_resource[i].flags & IORESOURCE_UNSET) &&
+                       i < PNP_MAX_DMA)
+               i++;
+       if (i < PNP_MAX_DMA) {
+               res->dma_resource[i].flags = IORESOURCE_DMA;  // Also clears _UNSET flag
+               if (dma == -1) {
+                       res->dma_resource[i].flags |= IORESOURCE_DISABLED;
+                       return;
+               }
+               res->dma_resource[i].start =(unsigned long) dma;
+               res->dma_resource[i].end = (unsigned long) dma;
+       }
+}
+
+static void
+pnpacpi_parse_allocated_ioresource(struct pnp_resource_table * res,
+       int io, int len)
+{
+       int i = 0;
+       while (!(res->port_resource[i].flags & IORESOURCE_UNSET) &&
+                       i < PNP_MAX_PORT)
+               i++;
+       if (i < PNP_MAX_PORT) {
+               res->port_resource[i].flags = IORESOURCE_IO;  // Also clears _UNSET flag
+               if (len <= 0 || (io + len -1) >= 0x10003) {
+                       res->port_resource[i].flags |= IORESOURCE_DISABLED;
+                       return;
+               }
+               res->port_resource[i].start = (unsigned long) io;
+               res->port_resource[i].end = (unsigned long)(io + len - 1);
+       }
+}
+
+static void
+pnpacpi_parse_allocated_memresource(struct pnp_resource_table * res,
+       int mem, int len)
+{
+       int i = 0;
+       while (!(res->mem_resource[i].flags & IORESOURCE_UNSET) &&
+                       (i < PNP_MAX_MEM))
+               i++;
+       if (i < PNP_MAX_MEM) {
+               res->mem_resource[i].flags = IORESOURCE_MEM;  // Also clears _UNSET flag
+               if (len <= 0) {
+                       res->mem_resource[i].flags |= IORESOURCE_DISABLED;
+                       return;
+               }
+               res->mem_resource[i].start = (unsigned long) mem;
+               res->mem_resource[i].end = (unsigned long)(mem + len - 1);
+       }
+}
+
+
+static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
+       void *data)
+{
+       struct pnp_resource_table * res_table = (struct pnp_resource_table *)data;
+
+       switch (res->id) {
+       case ACPI_RSTYPE_IRQ:
+               if ((res->data.irq.number_of_interrupts > 0) &&
+                       valid_IRQ(res->data.irq.interrupts[0])) {
+                       pnpacpi_parse_allocated_irqresource(res_table, 
+                               acpi_register_gsi(res->data.irq.interrupts[0],
+                                       res->data.irq.edge_level,
+                                       res->data.irq.active_high_low));
+                       pcibios_penalize_isa_irq(res->data.irq.interrupts[0]);
+               }
+               break;
+
+       case ACPI_RSTYPE_EXT_IRQ:
+               if ((res->data.extended_irq.number_of_interrupts > 0) &&
+                       valid_IRQ(res->data.extended_irq.interrupts[0])) {
+                       pnpacpi_parse_allocated_irqresource(res_table, 
+                               acpi_register_gsi(res->data.extended_irq.interrupts[0],
+                                       res->data.extended_irq.edge_level,
+                                       res->data.extended_irq.active_high_low));
+                       pcibios_penalize_isa_irq(res->data.extended_irq.interrupts[0]);
+               }
+               break;
+       case ACPI_RSTYPE_DMA:
+               if (res->data.dma.number_of_channels > 0)
+                       pnpacpi_parse_allocated_dmaresource(res_table, 
+                                       res->data.dma.channels[0]);
+               break;
+       case ACPI_RSTYPE_IO:
+               pnpacpi_parse_allocated_ioresource(res_table, 
+                               res->data.io.min_base_address, 
+                               res->data.io.range_length);
+               break;
+       case ACPI_RSTYPE_FIXED_IO:
+               pnpacpi_parse_allocated_ioresource(res_table, 
+                               res->data.fixed_io.base_address, 
+                               res->data.fixed_io.range_length);
+               break;
+       case ACPI_RSTYPE_MEM24:
+               pnpacpi_parse_allocated_memresource(res_table, 
+                               res->data.memory24.min_base_address, 
+                               res->data.memory24.range_length);
+               break;
+       case ACPI_RSTYPE_MEM32:
+               pnpacpi_parse_allocated_memresource(res_table, 
+                               res->data.memory32.min_base_address, 
+                               res->data.memory32.range_length);
+               break;
+       case ACPI_RSTYPE_FIXED_MEM32:
+               pnpacpi_parse_allocated_memresource(res_table, 
+                               res->data.fixed_memory32.range_base_address, 
+                               res->data.fixed_memory32.range_length);
+               break;
+       case ACPI_RSTYPE_ADDRESS16:
+               pnpacpi_parse_allocated_memresource(res_table, 
+                               res->data.address16.min_address_range, 
+                               res->data.address16.address_length);
+               break;
+       case ACPI_RSTYPE_ADDRESS32:
+               pnpacpi_parse_allocated_memresource(res_table, 
+                               res->data.address32.min_address_range, 
+                               res->data.address32.address_length);
+               break;
+       case ACPI_RSTYPE_ADDRESS64:
+               pnpacpi_parse_allocated_memresource(res_table, 
+               res->data.address64.min_address_range, 
+               res->data.address64.address_length);
+               break;
+       default:
+               pnp_warn("PnPACPI: Alloc type : %d not handle", 
+                               res->id);
+               return AE_ERROR;
+       }
+                       
+       return AE_OK;
+}
+
+acpi_status pnpacpi_parse_allocated_resource(acpi_handle handle, struct pnp_resource_table * res)
+{
+       /* Blank the resource table values */
+       pnp_init_resource_table(res);
+
+       return acpi_walk_resources(handle, METHOD_NAME__CRS, pnpacpi_allocated_resource, res);
+}
+
+static void pnpacpi_parse_dma_option(struct pnp_option *option, struct acpi_resource_dma *p)
+{
+       int i;
+       struct pnp_dma * dma;
+
+       if (p->number_of_channels == 0)
+               return;
+       dma = pnpacpi_kmalloc(sizeof(struct pnp_dma), GFP_KERNEL);
+       if (!dma)
+               return;
+
+       for(i = 0; i < p->number_of_channels; i++)
+               dma->map |= 1 << p->channels[i];
+       dma->flags = 0;
+       if (p->bus_master)
+               dma->flags |= IORESOURCE_DMA_MASTER;
+       switch (p->type) {
+       case ACPI_COMPATIBILITY:
+               dma->flags |= IORESOURCE_DMA_COMPATIBLE;
+               break;
+       case ACPI_TYPE_A:
+               dma->flags |= IORESOURCE_DMA_TYPEA;
+               break;
+       case ACPI_TYPE_B:
+               dma->flags |= IORESOURCE_DMA_TYPEB;
+               break;
+       case ACPI_TYPE_F:
+               dma->flags |= IORESOURCE_DMA_TYPEF;
+               break;
+       default:
+               /* Set a default value ? */
+               dma->flags |= IORESOURCE_DMA_COMPATIBLE;
+               pnp_err("Invalid DMA type");
+       }
+       switch (p->transfer) {
+       case ACPI_TRANSFER_8:
+               dma->flags |= IORESOURCE_DMA_8BIT;
+               break;
+       case ACPI_TRANSFER_8_16:
+               dma->flags |= IORESOURCE_DMA_8AND16BIT;
+               break;
+       case ACPI_TRANSFER_16:
+               dma->flags |= IORESOURCE_DMA_16BIT;
+               break;
+       default:
+               /* Set a default value ? */
+               dma->flags |= IORESOURCE_DMA_8AND16BIT;
+               pnp_err("Invalid DMA transfer type");
+       }
+
+       pnp_register_dma_resource(option,dma);
+       return;
+}
+
+       
+static void pnpacpi_parse_irq_option(struct pnp_option *option,
+       struct acpi_resource_irq *p)
+{
+       int i;
+       struct pnp_irq * irq;
+       
+       if (p->number_of_interrupts == 0)
+               return;
+       irq = pnpacpi_kmalloc(sizeof(struct pnp_irq), GFP_KERNEL);
+       if (!irq)
+               return;
+
+       for(i = 0; i < p->number_of_interrupts; i++)
+               if (p->interrupts[i])
+                       __set_bit(p->interrupts[i], irq->map);
+       irq->flags = irq_flags(p->edge_level, p->active_high_low);
+
+       pnp_register_irq_resource(option, irq);
+       return;
+}
+
+static void pnpacpi_parse_ext_irq_option(struct pnp_option *option,
+       struct acpi_resource_ext_irq *p)
+{
+       int i;
+       struct pnp_irq * irq;
+
+       if (p->number_of_interrupts == 0)
+               return;
+       irq = pnpacpi_kmalloc(sizeof(struct pnp_irq), GFP_KERNEL);
+       if (!irq)
+               return;
+
+       for(i = 0; i < p->number_of_interrupts; i++)
+               if (p->interrupts[i])
+                       __set_bit(p->interrupts[i], irq->map);
+       irq->flags = irq_flags(p->edge_level, p->active_high_low);
+
+       pnp_register_irq_resource(option, irq);
+       return;
+}
+
+static void
+pnpacpi_parse_port_option(struct pnp_option *option,
+       struct acpi_resource_io *io)
+{
+       struct pnp_port * port;
+
+       if (io->range_length == 0)
+               return;
+       port = pnpacpi_kmalloc(sizeof(struct pnp_port), GFP_KERNEL);
+       if (!port)
+               return;
+       port->min = io->min_base_address;
+       port->max = io->max_base_address;
+       port->align = io->alignment;
+       port->size = io->range_length;
+       port->flags = ACPI_DECODE_16 == io->io_decode ? 
+               PNP_PORT_FLAG_16BITADDR : 0;
+       pnp_register_port_resource(option,port);
+       return;
+}
+
+static void
+pnpacpi_parse_fixed_port_option(struct pnp_option *option,
+       struct acpi_resource_fixed_io *io)
+{
+       struct pnp_port * port;
+
+       if (io->range_length == 0)
+               return;
+       port = pnpacpi_kmalloc(sizeof(struct pnp_port), GFP_KERNEL);
+       if (!port)
+               return;
+       port->min = port->max = io->base_address;
+       port->size = io->range_length;
+       port->align = 0;
+       port->flags = PNP_PORT_FLAG_FIXED;
+       pnp_register_port_resource(option,port);
+       return;
+}
+
+static void
+pnpacpi_parse_mem24_option(struct pnp_option *option,
+       struct acpi_resource_mem24 *p)
+{
+       struct pnp_mem * mem;
+
+       if (p->range_length == 0)
+               return;
+       mem = pnpacpi_kmalloc(sizeof(struct pnp_mem), GFP_KERNEL);
+       if (!mem)
+               return;
+       mem->min = p->min_base_address;
+       mem->max = p->max_base_address;
+       mem->align = p->alignment;
+       mem->size = p->range_length;
+
+       mem->flags = (ACPI_READ_WRITE_MEMORY == p->read_write_attribute) ?
+                       IORESOURCE_MEM_WRITEABLE : 0;
+
+       pnp_register_mem_resource(option,mem);
+       return;
+}
+
+static void
+pnpacpi_parse_mem32_option(struct pnp_option *option,
+       struct acpi_resource_mem32 *p)
+{
+       struct pnp_mem * mem;
+
+       if (p->range_length == 0)
+               return;
+       mem = pnpacpi_kmalloc(sizeof(struct pnp_mem), GFP_KERNEL);
+       if (!mem)
+               return;
+       mem->min = p->min_base_address;
+       mem->max = p->max_base_address;
+       mem->align = p->alignment;
+       mem->size = p->range_length;
+
+       mem->flags = (ACPI_READ_WRITE_MEMORY == p->read_write_attribute) ?
+                       IORESOURCE_MEM_WRITEABLE : 0;
+
+       pnp_register_mem_resource(option,mem);
+       return;
+}
+
+static void
+pnpacpi_parse_fixed_mem32_option(struct pnp_option *option,
+       struct acpi_resource_fixed_mem32 *p)
+{
+       struct pnp_mem * mem;
+
+       if (p->range_length == 0)
+               return;
+       mem = pnpacpi_kmalloc(sizeof(struct pnp_mem), GFP_KERNEL);
+       if (!mem)
+               return;
+       mem->min = mem->max = p->range_base_address;
+       mem->size = p->range_length;
+       mem->align = 0;
+
+       mem->flags = (ACPI_READ_WRITE_MEMORY == p->read_write_attribute) ?
+                       IORESOURCE_MEM_WRITEABLE : 0;
+
+       pnp_register_mem_resource(option,mem);
+       return;
+}
+
+struct acpipnp_parse_option_s {
+       struct pnp_option *option;
+       struct pnp_dev *dev;
+};
+
+static acpi_status pnpacpi_option_resource(struct acpi_resource *res, 
+       void *data)
+{
+       int priority = 0;
+       struct acpipnp_parse_option_s *parse_data = (struct acpipnp_parse_option_s *)data;
+       struct pnp_dev *dev = parse_data->dev;
+       struct pnp_option *option = parse_data->option;
+
+       switch (res->id) {
+               case ACPI_RSTYPE_IRQ:
+                       pnpacpi_parse_irq_option(option, &res->data.irq);
+                       break;
+               case ACPI_RSTYPE_EXT_IRQ:
+                       pnpacpi_parse_ext_irq_option(option,
+                               &res->data.extended_irq);
+                       break;
+               case ACPI_RSTYPE_DMA:
+                       pnpacpi_parse_dma_option(option, &res->data.dma);       
+                       break;
+               case ACPI_RSTYPE_IO:
+                       pnpacpi_parse_port_option(option, &res->data.io);
+                       break;
+               case ACPI_RSTYPE_FIXED_IO:
+                       pnpacpi_parse_fixed_port_option(option,
+                               &res->data.fixed_io);
+                       break;
+               case ACPI_RSTYPE_MEM24:
+                       pnpacpi_parse_mem24_option(option, &res->data.memory24);
+                       break;
+               case ACPI_RSTYPE_MEM32:
+                       pnpacpi_parse_mem32_option(option, &res->data.memory32);
+                       break;
+               case ACPI_RSTYPE_FIXED_MEM32:
+                       pnpacpi_parse_fixed_mem32_option(option,
+                               &res->data.fixed_memory32);
+                       break;
+               case ACPI_RSTYPE_START_DPF:
+                       switch (res->data.start_dpf.compatibility_priority) {
+                               case ACPI_GOOD_CONFIGURATION:
+                                       priority = PNP_RES_PRIORITY_PREFERRED;
+                                       break;
+                                       
+                               case ACPI_ACCEPTABLE_CONFIGURATION:
+                                       priority = PNP_RES_PRIORITY_ACCEPTABLE;
+                                       break;
+
+                               case ACPI_SUB_OPTIMAL_CONFIGURATION:
+                                       priority = PNP_RES_PRIORITY_FUNCTIONAL;
+                                       break;
+                               default:
+                                       priority = PNP_RES_PRIORITY_INVALID;
+                                       break;
+                       }
+                       /* TBD: Considering performace/robustness bits */
+                       option = pnp_register_dependent_option(dev, priority);
+                       if (!option)
+                               return AE_ERROR;
+                       parse_data->option = option;    
+                       break;
+               case ACPI_RSTYPE_END_DPF:
+                       return AE_CTRL_TERMINATE;
+               default:
+                       pnp_warn("PnPACPI:Option type: %d not handle", res->id);
+                       return AE_ERROR;
+       }
+                       
+       return AE_OK;
+}
+
+acpi_status pnpacpi_parse_resource_option_data(acpi_handle handle, 
+       struct pnp_dev *dev)
+{
+       acpi_status status;
+       struct acpipnp_parse_option_s parse_data;
+
+       parse_data.option = pnp_register_independent_option(dev);
+       if (!parse_data.option)
+               return AE_ERROR;
+       parse_data.dev = dev;
+       status = acpi_walk_resources(handle, METHOD_NAME__PRS, 
+               pnpacpi_option_resource, &parse_data);
+
+       return status;
+}
+
+/*
+ * Set resource
+ */
+static acpi_status pnpacpi_count_resources(struct acpi_resource *res,
+       void *data)
+{
+       int *res_cnt = (int *)data;
+       switch (res->id) {
+       case ACPI_RSTYPE_IRQ:
+       case ACPI_RSTYPE_EXT_IRQ:
+       case ACPI_RSTYPE_DMA:
+       case ACPI_RSTYPE_IO:
+       case ACPI_RSTYPE_FIXED_IO:
+       case ACPI_RSTYPE_MEM24:
+       case ACPI_RSTYPE_MEM32:
+       case ACPI_RSTYPE_FIXED_MEM32:
+#if 0
+       case ACPI_RSTYPE_ADDRESS16:
+       case ACPI_RSTYPE_ADDRESS32:
+       case ACPI_RSTYPE_ADDRESS64:
+#endif
+               (*res_cnt) ++;
+       default:
+               return AE_OK;
+       }
+       return AE_OK;
+}
+
+static acpi_status pnpacpi_type_resources(struct acpi_resource *res,
+       void *data)
+{
+       struct acpi_resource **resource = (struct acpi_resource **)data;        
+       switch (res->id) {
+       case ACPI_RSTYPE_IRQ:
+       case ACPI_RSTYPE_EXT_IRQ:
+       case ACPI_RSTYPE_DMA:
+       case ACPI_RSTYPE_IO:
+       case ACPI_RSTYPE_FIXED_IO:
+       case ACPI_RSTYPE_MEM24:
+       case ACPI_RSTYPE_MEM32:
+       case ACPI_RSTYPE_FIXED_MEM32:
+#if 0
+       case ACPI_RSTYPE_ADDRESS16:
+       case ACPI_RSTYPE_ADDRESS32:
+       case ACPI_RSTYPE_ADDRESS64:
+#endif
+               (*resource)->id = res->id;
+               (*resource)++;
+       default:
+               return AE_OK;
+       }
+
+       return AE_OK;
+}
+
+int pnpacpi_build_resource_template(acpi_handle handle, 
+       struct acpi_buffer *buffer)
+{
+       struct acpi_resource *resource;
+       int res_cnt = 0;
+       acpi_status status;
+
+       status = acpi_walk_resources(handle, METHOD_NAME__CRS, 
+               pnpacpi_count_resources, &res_cnt);
+       if (ACPI_FAILURE(status)) {
+               pnp_err("Evaluate _CRS failed");
+               return -EINVAL;
+       }
+       if (!res_cnt)
+               return -EINVAL;
+       buffer->length = sizeof(struct acpi_resource) * (res_cnt + 1) + 1;
+       buffer->pointer = pnpacpi_kmalloc(buffer->length - 1, GFP_KERNEL);
+       if (!buffer->pointer)
+               return -ENOMEM;
+       pnp_dbg("Res cnt %d", res_cnt);
+       resource = (struct acpi_resource *)buffer->pointer;
+       status = acpi_walk_resources(handle, METHOD_NAME__CRS, 
+               pnpacpi_type_resources, &resource);
+       if (ACPI_FAILURE(status)) {
+               kfree(buffer->pointer);
+               pnp_err("Evaluate _CRS failed");
+               return -EINVAL;
+       }
+       /* resource will pointer the end resource now */
+       resource->id = ACPI_RSTYPE_END_TAG;
+
+       return 0;
+}
+
+static void pnpacpi_encode_irq(struct acpi_resource *resource, 
+       struct resource *p)
+{
+       int edge_level, active_high_low;
+       
+       decode_irq_flags(p->flags & IORESOURCE_BITS, &edge_level, 
+               &active_high_low);
+       resource->id = ACPI_RSTYPE_IRQ;
+       resource->length = sizeof(struct acpi_resource);
+       resource->data.irq.edge_level = edge_level;
+       resource->data.irq.active_high_low = active_high_low;
+       if (edge_level == ACPI_EDGE_SENSITIVE)
+               resource->data.irq.shared_exclusive = ACPI_EXCLUSIVE;
+       else
+               resource->data.irq.shared_exclusive = ACPI_SHARED;
+       resource->data.irq.number_of_interrupts = 1;
+       resource->data.irq.interrupts[0] = p->start;
+}
+
+static void pnpacpi_encode_ext_irq(struct acpi_resource *resource,
+       struct resource *p)
+{
+       int edge_level, active_high_low;
+       
+       decode_irq_flags(p->flags & IORESOURCE_BITS, &edge_level, 
+               &active_high_low);
+       resource->id = ACPI_RSTYPE_EXT_IRQ;
+       resource->length = sizeof(struct acpi_resource);
+       resource->data.extended_irq.producer_consumer = ACPI_CONSUMER;
+       resource->data.extended_irq.edge_level = edge_level;
+       resource->data.extended_irq.active_high_low = active_high_low;
+       if (edge_level == ACPI_EDGE_SENSITIVE)
+               resource->data.irq.shared_exclusive = ACPI_EXCLUSIVE;
+       else
+               resource->data.irq.shared_exclusive = ACPI_SHARED;
+       resource->data.extended_irq.number_of_interrupts = 1;
+       resource->data.extended_irq.interrupts[0] = p->start;
+}
+
+static void pnpacpi_encode_dma(struct acpi_resource *resource,
+       struct resource *p)
+{
+       resource->id = ACPI_RSTYPE_DMA;
+       resource->length = sizeof(struct acpi_resource);
+       /* Note: pnp_assign_dma will copy pnp_dma->flags into p->flags */
+       if (p->flags & IORESOURCE_DMA_COMPATIBLE)
+               resource->data.dma.type = ACPI_COMPATIBILITY;
+       else if (p->flags & IORESOURCE_DMA_TYPEA)
+               resource->data.dma.type = ACPI_TYPE_A;
+       else if (p->flags & IORESOURCE_DMA_TYPEB)
+               resource->data.dma.type = ACPI_TYPE_B;
+       else if (p->flags & IORESOURCE_DMA_TYPEF)
+               resource->data.dma.type = ACPI_TYPE_F;
+       if (p->flags & IORESOURCE_DMA_8BIT)
+               resource->data.dma.transfer = ACPI_TRANSFER_8;
+       else if (p->flags & IORESOURCE_DMA_8AND16BIT)
+               resource->data.dma.transfer = ACPI_TRANSFER_8_16;
+       else if (p->flags & IORESOURCE_DMA_16BIT)
+               resource->data.dma.transfer = ACPI_TRANSFER_16;
+       resource->data.dma.bus_master = p->flags & IORESOURCE_DMA_MASTER;
+       resource->data.dma.number_of_channels = 1;
+       resource->data.dma.channels[0] = p->start;
+}
+
+static void pnpacpi_encode_io(struct acpi_resource *resource,
+       struct resource *p)
+{
+       resource->id = ACPI_RSTYPE_IO;
+       resource->length = sizeof(struct acpi_resource);
+       /* Note: pnp_assign_port will copy pnp_port->flags into p->flags */
+       resource->data.io.io_decode = (p->flags & PNP_PORT_FLAG_16BITADDR)?
+               ACPI_DECODE_16 : ACPI_DECODE_10; 
+       resource->data.io.min_base_address = p->start;
+       resource->data.io.max_base_address = p->end;
+       resource->data.io.alignment = 0; /* Correct? */
+       resource->data.io.range_length = p->end - p->start + 1;
+}
+
+static void pnpacpi_encode_fixed_io(struct acpi_resource *resource,
+       struct resource *p)
+{
+       resource->id = ACPI_RSTYPE_FIXED_IO;
+       resource->length = sizeof(struct acpi_resource);
+       resource->data.fixed_io.base_address = p->start;
+       resource->data.fixed_io.range_length = p->end - p->start + 1;
+}
+
+static void pnpacpi_encode_mem24(struct acpi_resource *resource,
+       struct resource *p)
+{
+       resource->id = ACPI_RSTYPE_MEM24;
+       resource->length = sizeof(struct acpi_resource);
+       /* Note: pnp_assign_mem will copy pnp_mem->flags into p->flags */
+       resource->data.memory24.read_write_attribute =
+               (p->flags & IORESOURCE_MEM_WRITEABLE) ?
+               ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY;
+       resource->data.memory24.min_base_address = p->start;
+       resource->data.memory24.max_base_address = p->end;
+       resource->data.memory24.alignment = 0;
+       resource->data.memory24.range_length = p->end - p->start + 1;
+}
+
+static void pnpacpi_encode_mem32(struct acpi_resource *resource,
+       struct resource *p)
+{
+       resource->id = ACPI_RSTYPE_MEM32;
+       resource->length = sizeof(struct acpi_resource);
+       resource->data.memory32.read_write_attribute =
+               (p->flags & IORESOURCE_MEM_WRITEABLE) ?
+               ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY;
+       resource->data.memory32.min_base_address = p->start;
+       resource->data.memory32.max_base_address = p->end;
+       resource->data.memory32.alignment = 0;
+       resource->data.memory32.range_length = p->end - p->start + 1;
+}
+
+static void pnpacpi_encode_fixed_mem32(struct acpi_resource *resource,
+       struct resource *p)
+{
+       resource->id = ACPI_RSTYPE_FIXED_MEM32;
+       resource->length = sizeof(struct acpi_resource);
+       resource->data.fixed_memory32.read_write_attribute =
+               (p->flags & IORESOURCE_MEM_WRITEABLE) ?
+               ACPI_READ_WRITE_MEMORY : ACPI_READ_ONLY_MEMORY;
+       resource->data.fixed_memory32.range_base_address = p->start;
+       resource->data.fixed_memory32.range_length = p->end - p->start + 1;
+}
+
+int pnpacpi_encode_resources(struct pnp_resource_table *res_table, 
+       struct acpi_buffer *buffer)
+{
+       int i = 0;
+       /* pnpacpi_build_resource_template allocates extra mem */
+       int res_cnt = (buffer->length - 1)/sizeof(struct acpi_resource) - 1;
+       struct acpi_resource *resource = (struct acpi_resource*)buffer->pointer;
+       int port = 0, irq = 0, dma = 0, mem = 0;
+
+       pnp_dbg("res cnt %d", res_cnt);
+       while (i < res_cnt) {
+               switch(resource->id) {
+               case ACPI_RSTYPE_IRQ:
+                       pnp_dbg("Encode irq");
+                       pnpacpi_encode_irq(resource, 
+                               &res_table->irq_resource[irq]);
+                       irq++;
+                       break;
+
+               case ACPI_RSTYPE_EXT_IRQ:
+                       pnp_dbg("Encode ext irq");
+                       pnpacpi_encode_ext_irq(resource, 
+                               &res_table->irq_resource[irq]);
+                       irq++;
+                       break;
+               case ACPI_RSTYPE_DMA:
+                       pnp_dbg("Encode dma");
+                       pnpacpi_encode_dma(resource, 
+                               &res_table->dma_resource[dma]);
+                       dma ++;
+                       break;
+               case ACPI_RSTYPE_IO:
+                       pnp_dbg("Encode io");
+                       pnpacpi_encode_io(resource, 
+                               &res_table->port_resource[port]);
+                       port ++;
+                       break;
+               case ACPI_RSTYPE_FIXED_IO:
+                       pnp_dbg("Encode fixed io");
+                       pnpacpi_encode_fixed_io(resource,
+                               &res_table->port_resource[port]);
+                       port ++;
+                       break;
+               case ACPI_RSTYPE_MEM24:
+                       pnp_dbg("Encode mem24");
+                       pnpacpi_encode_mem24(resource,
+                               &res_table->mem_resource[mem]);
+                       mem ++;
+                       break;
+               case ACPI_RSTYPE_MEM32:
+                       pnp_dbg("Encode mem32");
+                       pnpacpi_encode_mem32(resource,
+                               &res_table->mem_resource[mem]);
+                       mem ++;
+                       break;
+               case ACPI_RSTYPE_FIXED_MEM32:
+                       pnp_dbg("Encode fixed mem32");
+                       pnpacpi_encode_fixed_mem32(resource,
+                               &res_table->mem_resource[mem]);
+                       mem ++;
+                       break;
+               default: /* other type */
+                       pnp_warn("Invalid type");
+                       return -EINVAL;
+               }
+               resource ++;
+               i ++;
+       }
+       return 0;
+}
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
new file mode 100644 (file)
index 0000000..5fd3ad8
--- /dev/null
@@ -0,0 +1,662 @@
+/*
+ * drivers/s390/char/monreader.c
+ *
+ * Character device driver for reading z/VM *MONITOR service records.
+ *
+ * Copyright (C) 2004 IBM Corporation, IBM Deutschland Entwicklung GmbH.
+ *
+ * Author: Gerald Schaefer <geraldsc@de.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/ctype.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <asm/ebcdic.h>
+#include <asm/extmem.h>
+#include <linux/poll.h>
+#include "../net/iucv.h"
+
+
+//#define MON_DEBUG                    /* Debug messages on/off */
+
+#define MON_NAME "monreader"
+
+#define P_INFO(x...)   printk(KERN_INFO MON_NAME " info: " x)
+#define P_ERROR(x...)  printk(KERN_ERR MON_NAME " error: " x)
+#define P_WARNING(x...)        printk(KERN_WARNING MON_NAME " warning: " x)
+
+#ifdef MON_DEBUG
+#define P_DEBUG(x...)   printk(KERN_DEBUG MON_NAME " debug: " x)
+#else
+#define P_DEBUG(x...)   do {} while (0)
+#endif
+
+#define MON_COLLECT_SAMPLE 0x80
+#define MON_COLLECT_EVENT  0x40
+#define MON_SERVICE       "*MONITOR"
+#define MON_IN_USE        0x01
+#define MON_MSGLIM        255
+
+static char mon_dcss_name[9] = "MONDCSS\0";
+
+struct mon_msg {
+       u32 pos;
+       u32 mca_offset;
+       iucv_MessagePending local_eib;
+       char msglim_reached;
+       char replied_msglim;
+};
+
+struct mon_private {
+       u16 pathid;
+       iucv_handle_t iucv_handle;
+       struct mon_msg *msg_array[MON_MSGLIM];
+       unsigned int   write_index;
+       unsigned int   read_index;
+       atomic_t msglim_count;
+       atomic_t read_ready;
+       atomic_t iucv_connected;
+       atomic_t iucv_severed;
+};
+
+static unsigned long mon_in_use = 0;
+
+static unsigned long mon_dcss_start;
+static unsigned long mon_dcss_end;
+
+static DECLARE_WAIT_QUEUE_HEAD(mon_read_wait_queue);
+static DECLARE_WAIT_QUEUE_HEAD(mon_conn_wait_queue);
+
+static u8 iucv_host[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static u8 user_data_connect[16] = {
+       /* Version code, must be 0x01 for shared mode */
+       0x01,
+       /* what to collect */
+       MON_COLLECT_SAMPLE | MON_COLLECT_EVENT,
+       /* DCSS name in EBCDIC, 8 bytes padded with blanks */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static u8 user_data_sever[16] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+
+/******************************************************************************
+ *                             helper functions                               *
+ *****************************************************************************/
+/*
+ * Create the 8 bytes EBCDIC DCSS segment name from
+ * an ASCII name, incl. padding
+ */
+static inline void
+dcss_mkname(char *ascii_name, char *ebcdic_name)
+{
+       int i;
+
+       for (i = 0; i < 8; i++) {
+               if (ascii_name[i] == '\0')
+                       break;
+               ebcdic_name[i] = toupper(ascii_name[i]);
+       };
+       for (; i < 8; i++)
+               ebcdic_name[i] = ' ';
+       ASCEBC(ebcdic_name, 8);
+}
+
+/*
+ * print appropriate error message for segment_load()/segment_type()
+ * return code
+ */
+static void
+mon_segment_warn(int rc, char* seg_name)
+{
+       switch (rc) {
+       case -ENOENT:
+               P_WARNING("cannot load/query segment %s, does not exist\n",
+                         seg_name);
+               break;
+       case -ENOSYS:
+               P_WARNING("cannot load/query segment %s, not running on VM\n",
+                         seg_name);
+               break;
+       case -EIO:
+               P_WARNING("cannot load/query segment %s, hardware error\n",
+                         seg_name);
+               break;
+       case -ENOTSUPP:
+               P_WARNING("cannot load/query segment %s, is a multi-part "
+                         "segment\n", seg_name);
+               break;
+       case -ENOSPC:
+               P_WARNING("cannot load/query segment %s, overlaps with "
+                         "storage\n", seg_name);
+               break;
+       case -EBUSY:
+               P_WARNING("cannot load/query segment %s, overlaps with "
+                         "already loaded dcss\n", seg_name);
+               break;
+       case -EPERM:
+               P_WARNING("cannot load/query segment %s, already loaded in "
+                         "incompatible mode\n", seg_name);
+               break;
+       case -ENOMEM:
+               P_WARNING("cannot load/query segment %s, out of memory\n",
+                         seg_name);
+               break;
+       case -ERANGE:
+               P_WARNING("cannot load/query segment %s, exceeds kernel "
+                         "mapping range\n", seg_name);
+               break;
+       default:
+               P_WARNING("cannot load/query segment %s, return value %i\n",
+                         seg_name, rc);
+               break;
+       }
+}
+
+static inline unsigned long
+mon_mca_start(struct mon_msg *monmsg)
+{
+       return monmsg->local_eib.ln1msg1.iprmmsg1_u32;
+}
+
+static inline unsigned long
+mon_mca_end(struct mon_msg *monmsg)
+{
+       return monmsg->local_eib.ln1msg2.ipbfln1f;
+}
+
+static inline u8
+mon_mca_type(struct mon_msg *monmsg, u8 index)
+{
+       return *((u8 *) mon_mca_start(monmsg) + monmsg->mca_offset + index);
+}
+
+static inline u32
+mon_mca_size(struct mon_msg *monmsg)
+{
+       return mon_mca_end(monmsg) - mon_mca_start(monmsg) + 1;
+}
+
+static inline u32
+mon_rec_start(struct mon_msg *monmsg)
+{
+       return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 4));
+}
+
+static inline u32
+mon_rec_end(struct mon_msg *monmsg)
+{
+       return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 8));
+}
+
+static inline int
+mon_check_mca(struct mon_msg *monmsg)
+{
+       if ((mon_rec_end(monmsg) <= mon_rec_start(monmsg)) ||
+           (mon_rec_start(monmsg) < mon_dcss_start) ||
+           (mon_rec_end(monmsg) > mon_dcss_end) ||
+           (mon_mca_type(monmsg, 0) == 0) ||
+           (mon_mca_size(monmsg) % 12 != 0) ||
+           (mon_mca_end(monmsg) <= mon_mca_start(monmsg)) ||
+           (mon_mca_end(monmsg) > mon_dcss_end) ||
+           (mon_mca_start(monmsg) < mon_dcss_start) ||
+           ((mon_mca_type(monmsg, 1) == 0) && (mon_mca_type(monmsg, 2) == 0)))
+       {
+               P_DEBUG("READ, IGNORED INVALID MCA\n\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static inline int
+mon_send_reply(struct mon_msg *monmsg, struct mon_private *monpriv)
+{
+       u8 prmmsg[8];
+       int rc;
+
+       P_DEBUG("read, REPLY: pathid = 0x%04X, msgid = 0x%08X, trgcls = "
+               "0x%08X\n\n",
+               monmsg->local_eib.ippathid, monmsg->local_eib.ipmsgid,
+               monmsg->local_eib.iptrgcls);
+       rc = iucv_reply_prmmsg(monmsg->local_eib.ippathid,
+                               monmsg->local_eib.ipmsgid,
+                               monmsg->local_eib.iptrgcls,
+                               0, prmmsg);
+       atomic_dec(&monpriv->msglim_count);
+       if (likely(!monmsg->msglim_reached)) {
+               monmsg->pos = 0;
+               monmsg->mca_offset = 0;
+               monpriv->read_index = (monpriv->read_index + 1) %
+                                     MON_MSGLIM;
+               atomic_dec(&monpriv->read_ready);
+       } else
+               monmsg->replied_msglim = 1;
+       if (rc) {
+               P_ERROR("read, IUCV reply failed with rc = %i\n\n", rc);
+               return -EIO;
+       }
+       return 0;
+}
+
+static inline struct mon_private *
+mon_alloc_mem(void)
+{
+       int i,j;
+       struct mon_private *monpriv;
+
+       monpriv = kmalloc(sizeof(struct mon_private), GFP_KERNEL);
+       if (!monpriv) {
+               P_ERROR("no memory for monpriv\n");
+               return NULL;
+       }
+       memset(monpriv, 0, sizeof(struct mon_private));
+       for (i = 0; i < MON_MSGLIM; i++) {
+               monpriv->msg_array[i] = kmalloc(sizeof(struct mon_msg),
+                                                   GFP_KERNEL);
+               if (!monpriv->msg_array[i]) {
+                       P_ERROR("open, no memory for msg_array\n");
+                       for (j = 0; j < i; j++)
+                               kfree(monpriv->msg_array[j]);
+                       return NULL;
+               }
+               memset(monpriv->msg_array[i], 0, sizeof(struct mon_msg));
+       }
+       return monpriv;
+}
+
+static inline void
+mon_read_debug(struct mon_msg *monmsg, struct mon_private *monpriv)
+{
+#ifdef MON_DEBUG
+       u8 msg_type[2], mca_type;
+       unsigned long records_len;
+
+       records_len = mon_rec_end(monmsg) - mon_rec_start(monmsg) + 1;
+
+       memcpy(msg_type, &monmsg->local_eib.iptrgcls, 2);
+       EBCASC(msg_type, 2);
+       mca_type = mon_mca_type(monmsg, 0);
+       EBCASC(&mca_type, 1);
+
+       P_DEBUG("read, mon_read_index = %i, mon_write_index = %i\n",
+               monpriv->read_index, monpriv->write_index);
+       P_DEBUG("read, pathid = 0x%04X, msgid = 0x%08X, trgcls = 0x%08X\n",
+               monmsg->local_eib.ippathid, monmsg->local_eib.ipmsgid,
+               monmsg->local_eib.iptrgcls);
+       P_DEBUG("read, msg_type = '%c%c', mca_type = '%c' / 0x%X / 0x%X\n",
+               msg_type[0], msg_type[1], mca_type ? mca_type : 'X',
+               mon_mca_type(monmsg, 1), mon_mca_type(monmsg, 2));
+       P_DEBUG("read, MCA: start = 0x%lX, end = 0x%lX\n",
+               mon_mca_start(monmsg), mon_mca_end(monmsg));
+       P_DEBUG("read, REC: start = 0x%X, end = 0x%X, len = %lu\n\n",
+               mon_rec_start(monmsg), mon_rec_end(monmsg), records_len);
+       if (mon_mca_size(monmsg) > 12)
+               P_DEBUG("READ, MORE THAN ONE MCA\n\n");
+#endif
+}
+
+static inline void
+mon_next_mca(struct mon_msg *monmsg)
+{
+       if (likely((mon_mca_size(monmsg) - monmsg->mca_offset) == 12))
+               return;
+       P_DEBUG("READ, NEXT MCA\n\n");
+       monmsg->mca_offset += 12;
+       monmsg->pos = 0;
+}
+
+static inline struct mon_msg *
+mon_next_message(struct mon_private *monpriv)
+{
+       struct mon_msg *monmsg;
+
+       if (!atomic_read(&monpriv->read_ready))
+               return NULL;
+       monmsg = monpriv->msg_array[monpriv->read_index];
+       if (unlikely(monmsg->replied_msglim)) {
+               monmsg->replied_msglim = 0;
+               monmsg->msglim_reached = 0;
+               monmsg->pos = 0;
+               monmsg->mca_offset = 0;
+               P_WARNING("read, message limit reached\n");
+               monpriv->read_index = (monpriv->read_index + 1) %
+                                     MON_MSGLIM;
+               atomic_dec(&monpriv->read_ready);
+               return ERR_PTR(-EOVERFLOW);
+       }
+       return monmsg;
+}
+
+
+/******************************************************************************
+ *                               IUCV handler                                 *
+ *****************************************************************************/
+static void
+mon_iucv_ConnectionComplete(iucv_ConnectionComplete *eib, void *pgm_data)
+{
+       struct mon_private *monpriv = (struct mon_private *) pgm_data;
+
+       P_DEBUG("IUCV connection completed\n");
+       P_DEBUG("IUCV ACCEPT (from *MONITOR): Version = 0x%02X, Event = "
+               "0x%02X, Sample = 0x%02X\n",
+               eib->ipuser[0], eib->ipuser[1], eib->ipuser[2]);
+       atomic_set(&monpriv->iucv_connected, 1);
+       wake_up(&mon_conn_wait_queue);
+}
+
+static void
+mon_iucv_ConnectionSevered(iucv_ConnectionSevered *eib, void *pgm_data)
+{
+       struct mon_private *monpriv = (struct mon_private *) pgm_data;
+
+       P_ERROR("IUCV connection severed with rc = 0x%X\n",
+               (u8) eib->ipuser[0]);
+       atomic_set(&monpriv->iucv_severed, 1);
+       wake_up(&mon_conn_wait_queue);
+       wake_up_interruptible(&mon_read_wait_queue);
+}
+
+static void
+mon_iucv_MessagePending(iucv_MessagePending *eib, void *pgm_data)
+{
+       struct mon_private *monpriv = (struct mon_private *) pgm_data;
+
+       P_DEBUG("IUCV message pending\n");
+       memcpy(&monpriv->msg_array[monpriv->write_index]->local_eib, eib,
+              sizeof(iucv_MessagePending));
+       if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) {
+               P_WARNING("IUCV message pending, message limit (%i) reached\n",
+                         MON_MSGLIM);
+               monpriv->msg_array[monpriv->write_index]->msglim_reached = 1;
+       }
+       monpriv->write_index = (monpriv->write_index + 1) % MON_MSGLIM;
+       atomic_inc(&monpriv->read_ready);
+       wake_up_interruptible(&mon_read_wait_queue);
+}
+
+static iucv_interrupt_ops_t mon_iucvops = {
+       .ConnectionComplete = mon_iucv_ConnectionComplete,
+       .ConnectionSevered  = mon_iucv_ConnectionSevered,
+       .MessagePending     = mon_iucv_MessagePending,
+};
+
+/******************************************************************************
+ *                               file operations                              *
+ *****************************************************************************/
+static int
+mon_open(struct inode *inode, struct file *filp)
+{
+       int rc, i;
+       struct mon_private *monpriv;
+
+       /*
+        * only one user allowed
+        */
+       if (test_and_set_bit(MON_IN_USE, &mon_in_use))
+               return -EBUSY;
+
+       monpriv = mon_alloc_mem();
+       if (!monpriv)
+               return -ENOMEM;
+
+       /*
+        * Register with IUCV and connect to *MONITOR service
+        */
+       monpriv->iucv_handle = iucv_register_program("my_monreader    ",
+                                                       MON_SERVICE,
+                                                       NULL,
+                                                       &mon_iucvops,
+                                                       monpriv);
+       if (!monpriv->iucv_handle) {
+               P_ERROR("failed to register with iucv driver\n");
+               rc = -EIO;
+               goto out_error;
+       }
+       P_INFO("open, registered with IUCV\n");
+
+       rc = iucv_connect(&monpriv->pathid, MON_MSGLIM, user_data_connect,
+                         MON_SERVICE, iucv_host, IPRMDATA, NULL, NULL,
+                         monpriv->iucv_handle, NULL);
+       if (rc) {
+               P_ERROR("iucv connection to *MONITOR failed with "
+                       "IPUSER SEVER code = %i\n", rc);
+               rc = -EIO;
+               goto out_unregister;
+       }
+       /*
+        * Wait for connection confirmation
+        */
+       wait_event(mon_conn_wait_queue,
+                  atomic_read(&monpriv->iucv_connected) ||
+                  atomic_read(&monpriv->iucv_severed));
+       if (atomic_read(&monpriv->iucv_severed)) {
+               atomic_set(&monpriv->iucv_severed, 0);
+               atomic_set(&monpriv->iucv_connected, 0);
+               rc = -EIO;
+               goto out_unregister;
+       }
+       P_INFO("open, established connection to *MONITOR service\n\n");
+       filp->private_data = monpriv;
+       return nonseekable_open(inode, filp);
+
+out_unregister:
+       iucv_unregister_program(monpriv->iucv_handle);
+out_error:
+       for (i = 0; i < MON_MSGLIM; i++)
+               kfree(monpriv->msg_array[i]);
+       kfree(monpriv);
+       clear_bit(MON_IN_USE, &mon_in_use);
+       return rc;
+}
+
+static int
+mon_close(struct inode *inode, struct file *filp)
+{
+       int rc, i;
+       struct mon_private *monpriv = filp->private_data;
+
+       /*
+        * Close IUCV connection and unregister
+        */
+       rc = iucv_sever(monpriv->pathid, user_data_sever);
+       if (rc)
+               P_ERROR("close, iucv_sever failed with rc = %i\n", rc);
+       else
+               P_INFO("close, terminated connection to *MONITOR service\n");
+
+       rc = iucv_unregister_program(monpriv->iucv_handle);
+       if (rc)
+               P_ERROR("close, iucv_unregister failed with rc = %i\n", rc);
+       else
+               P_INFO("close, unregistered with IUCV\n");
+
+       atomic_set(&monpriv->iucv_severed, 0);
+       atomic_set(&monpriv->iucv_connected, 0);
+       atomic_set(&monpriv->read_ready, 0);
+       atomic_set(&monpriv->msglim_count, 0);
+       monpriv->write_index  = 0;
+       monpriv->read_index   = 0;
+
+       for (i = 0; i < MON_MSGLIM; i++)
+               kfree(monpriv->msg_array[i]);
+       kfree(monpriv);
+       clear_bit(MON_IN_USE, &mon_in_use);
+       return 0;
+}
+
+static ssize_t
+mon_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
+{
+       struct mon_private *monpriv = filp->private_data;
+       struct mon_msg *monmsg;
+       int ret;
+       u32 mce_start;
+
+       monmsg = mon_next_message(monpriv);
+       if (IS_ERR(monmsg))
+               return PTR_ERR(monmsg);
+
+       if (!monmsg) {
+               if (filp->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+               ret = wait_event_interruptible(mon_read_wait_queue,
+                                       atomic_read(&monpriv->read_ready) ||
+                                       atomic_read(&monpriv->iucv_severed));
+               if (ret)
+                       return ret;
+               if (unlikely(atomic_read(&monpriv->iucv_severed)))
+                       return -EIO;
+               monmsg = monpriv->msg_array[monpriv->read_index];
+       }
+
+       if (!monmsg->pos) {
+               monmsg->pos = mon_mca_start(monmsg) + monmsg->mca_offset;
+               mon_read_debug(monmsg, monpriv);
+       }
+       if (mon_check_mca(monmsg))
+               goto reply;
+
+       /* read monitor control element (12 bytes) first */
+       mce_start = mon_mca_start(monmsg) + monmsg->mca_offset;
+       if ((monmsg->pos >= mce_start) && (monmsg->pos < mce_start + 12)) {
+               count = min(count, (size_t) mce_start + 12 - monmsg->pos);
+               ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos,
+                                  count);
+               if (ret)
+                       return -EFAULT;
+               monmsg->pos += count;
+               if (monmsg->pos == mce_start + 12)
+                       monmsg->pos = mon_rec_start(monmsg);
+               goto out_copy;
+       }
+
+       /* read records */
+       if (monmsg->pos <= mon_rec_end(monmsg)) {
+               count = min(count, (size_t) mon_rec_end(monmsg) - monmsg->pos
+                                           + 1);
+               ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos,
+                                  count);
+               if (ret)
+                       return -EFAULT;
+               monmsg->pos += count;
+               if (monmsg->pos > mon_rec_end(monmsg))
+                       mon_next_mca(monmsg);
+               goto out_copy;
+       }
+reply:
+       ret = mon_send_reply(monmsg, monpriv);
+       return ret;
+
+out_copy:
+       *ppos += count;
+       return count;
+}
+
+static unsigned int
+mon_poll(struct file *filp, struct poll_table_struct *p)
+{
+       struct mon_private *monpriv = filp->private_data;
+
+       poll_wait(filp, &mon_read_wait_queue, p);
+       if (unlikely(atomic_read(&monpriv->iucv_severed)))
+               return POLLERR;
+       if (atomic_read(&monpriv->read_ready))
+               return POLLIN | POLLRDNORM;
+       return 0;
+}
+
+static struct file_operations mon_fops = {
+       .owner   = THIS_MODULE,
+       .open    = &mon_open,
+       .release = &mon_close,
+       .read    = &mon_read,
+       .poll    = &mon_poll,
+};
+
+static struct miscdevice mon_dev = {
+       .name       = "monreader",
+       .devfs_name = "monreader",
+       .fops       = &mon_fops,
+       .minor      = MISC_DYNAMIC_MINOR,
+};
+
+/******************************************************************************
+ *                              module init/exit                              *
+ *****************************************************************************/
+static int __init
+mon_init(void)
+{
+       int rc;
+
+       if (!MACHINE_IS_VM) {
+               P_ERROR("not running under z/VM, driver not loaded\n");
+               return -ENODEV;
+       }
+
+       rc = segment_type(mon_dcss_name);
+       if (rc < 0) {
+               mon_segment_warn(rc, mon_dcss_name);
+               return rc;
+       }
+       if (rc != SEG_TYPE_SC) {
+               P_ERROR("segment %s has unsupported type, should be SC\n",
+                       mon_dcss_name);
+               return -EINVAL;
+       }
+
+       rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
+                         &mon_dcss_start, &mon_dcss_end);
+       if (rc < 0) {
+               mon_segment_warn(rc, mon_dcss_name);
+               return -EINVAL;
+       }
+       dcss_mkname(mon_dcss_name, &user_data_connect[8]);
+
+       rc = misc_register(&mon_dev);
+       if (rc < 0 ) {
+               P_ERROR("misc_register failed, rc = %i\n", rc);
+               goto out;
+       }
+       P_INFO("Loaded segment %s from %p to %p, size = %lu Byte\n",
+               mon_dcss_name, (void *) mon_dcss_start, (void *) mon_dcss_end,
+               mon_dcss_end - mon_dcss_start + 1);
+       return 0;
+
+out:
+       segment_unload(mon_dcss_name);
+       return rc;
+}
+
+static void __exit
+mon_exit(void)
+{
+       segment_unload(mon_dcss_name);
+       WARN_ON(misc_deregister(&mon_dev) != 0);
+       return;
+}
+
+
+module_init(mon_init);
+module_exit(mon_exit);
+
+module_param_string(mondcss, mon_dcss_name, 9, 0444);
+MODULE_PARM_DESC(mondcss, "Name of DCSS segment to be used for *MONITOR "
+                "service, max. 8 chars. Default is MONDCSS");
+
+MODULE_AUTHOR("Gerald Schaefer <geraldsc@de.ibm.com>");
+MODULE_DESCRIPTION("Character device driver for reading z/VM "
+                  "monitor service records.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
new file mode 100644 (file)
index 0000000..924c5dc
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ *  drivers/s390/char/sclp_quiesce.c
+ *     signal quiesce handler
+ *
+ *  (C) Copyright IBM Corp. 1999,2004
+ *  Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *             Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/cpumask.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <asm/atomic.h>
+#include <asm/ptrace.h>
+#include <asm/sigp.h>
+
+#include "sclp.h"
+
+
+#ifdef CONFIG_SMP
+/* Signal completion of shutdown process. All CPUs except the first to enter
+ * this function: go to stopped state. First CPU: wait until all other
+ * CPUs are in stopped or check stop state. Afterwards, load special PSW
+ * to indicate completion. */
+static void
+do_load_quiesce_psw(void * __unused)
+{
+       static atomic_t cpuid = ATOMIC_INIT(-1);
+       psw_t quiesce_psw;
+       __u32 status;
+       int i;
+
+       if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid))
+               signal_processor(smp_processor_id(), sigp_stop);
+       /* Wait for all other cpus to enter stopped state */
+       i = 1;
+       while (i < NR_CPUS) {
+               if (!cpu_online(i)) {
+                       i++;
+                       continue;
+               }
+               switch (signal_processor_ps(&status, 0, i, sigp_sense)) {
+               case sigp_order_code_accepted:
+               case sigp_status_stored:
+                       /* Check for stopped and check stop state */
+                       if (status & 0x50)
+                               i++;
+                       break;
+               case sigp_busy:
+                       break;
+               case sigp_not_operational:
+                       i++;
+                       break;
+               }
+       }
+       /* Quiesce the last cpu with the special psw */
+       quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
+       quiesce_psw.addr = 0xfff;
+       __load_psw(quiesce_psw);
+}
+
+/* Shutdown handler. Perform shutdown function on all CPUs. */
+static void
+do_machine_quiesce(void)
+{
+       on_each_cpu(do_load_quiesce_psw, NULL, 0, 0);
+}
+#else
+/* Shutdown handler. Signal completion of shutdown by loading special PSW. */
+static void
+do_machine_quiesce(void)
+{
+       psw_t quiesce_psw;
+
+       quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
+       quiesce_psw.addr = 0xfff;
+       __load_psw(quiesce_psw);
+}
+#endif
+
+extern void ctrl_alt_del(void);
+
+/* Handler for quiesce event. Start shutdown procedure. */
+static void
+sclp_quiesce_handler(struct evbuf_header *evbuf)
+{
+       _machine_restart = (void *) do_machine_quiesce;
+       _machine_halt = do_machine_quiesce;
+       _machine_power_off = do_machine_quiesce;
+       ctrl_alt_del();
+}
+
+static struct sclp_register sclp_quiesce_event = {
+       .receive_mask = EvTyp_SigQuiesce_Mask,
+       .receiver_fn = sclp_quiesce_handler
+};
+
+/* Initialize quiesce driver. */
+static int __init
+sclp_quiesce_init(void)
+{
+       int rc;
+
+       rc = sclp_register(&sclp_quiesce_event);
+       if (rc)
+               printk(KERN_WARNING "sclp: could not register quiesce handler "
+                      "(rc=%d)\n", rc);
+       return rc;
+}
+
+module_init(sclp_quiesce_init);
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
new file mode 100644 (file)
index 0000000..88694e7
--- /dev/null
@@ -0,0 +1,920 @@
+/*
+ * drivers/s390/char/vmlogrdr.c
+ *     character device driver for reading z/VM system service records
+ *
+ *
+ *     Copyright (C) 2004 IBM Corporation
+ *     character device driver for reading z/VM system service records,
+ *     Version 1.0
+ *     Author(s): Xenia Tkatschow <xenia@us.ibm.com>
+ *                Stefan Weinhuber <wein@de.ibm.com>
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <asm/cpcmd.h>
+#include <asm/debug.h>
+#include <asm/ebcdic.h>
+#include "../net/iucv.h"
+#include <linux/kmod.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/string.h>
+
+
+
+MODULE_AUTHOR
+       ("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n"
+        "                            Stefan Weinhuber (wein@de.ibm.com)");
+MODULE_DESCRIPTION ("Character device driver for reading z/VM "
+                   "system service records.");
+MODULE_LICENSE("GPL");
+
+
+/*
+ * The size of the buffer for iucv data transfer is one page,
+ * but in addition to the data we read from iucv we also
+ * place an integer and some characters into that buffer,
+ * so the maximum size for record data is a little less then
+ * one page.
+ */
+#define NET_BUFFER_SIZE        (PAGE_SIZE - sizeof(int) - sizeof(FENCE))
+
+/*
+ * The elements that are concurrently accessed by bottom halves are
+ * connection_established, iucv_path_severed, local_interrupt_buffer
+ * and receive_ready. The first three can be protected by
+ * priv_lock.  receive_ready is atomic, so it can be incremented and
+ * decremented without holding a lock.
+ * The variable dev_in_use needs to be protected by the lock, since
+ * it's a flag used by open to make sure that the device is opened only
+ * by one user at the same time.
+ */
+struct vmlogrdr_priv_t {
+       char system_service[8];
+       char internal_name[8];
+       char recording_name[8];
+       u16 pathid;
+       int connection_established;
+       int iucv_path_severed;
+       iucv_MessagePending local_interrupt_buffer;
+       atomic_t receive_ready;
+       iucv_handle_t iucv_handle;
+       int minor_num;
+       char * buffer;
+       char * current_position;
+       int remaining;
+       ulong residual_length;
+       int buffer_free;
+       int dev_in_use; /* 1: already opened, 0: not opened*/
+       spinlock_t priv_lock;
+       struct device  *device;
+       struct class_device  *class_device;
+       int autorecording;
+       int autopurge;
+};
+
+
+/*
+ * File operation structure for vmlogrdr devices
+ */
+static int vmlogrdr_open(struct inode *, struct file *);
+static int vmlogrdr_release(struct inode *, struct file *);
+static ssize_t vmlogrdr_read (struct file *filp, char *data, size_t count,
+                              loff_t * ppos);
+
+static struct file_operations vmlogrdr_fops = {
+       .owner   = THIS_MODULE,
+       .open    = vmlogrdr_open,
+       .release = vmlogrdr_release,
+       .read    = vmlogrdr_read,
+};
+
+
+static u8 iucvMagic[16] = {
+       0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+       0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
+};
+
+
+static u8 mask[] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+
+static u8 iucv_host[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+
+static void
+vmlogrdr_iucv_ConnectionComplete(iucv_ConnectionComplete *eib, void *pgm_data);
+static void
+vmlogrdr_iucv_ConnectionSevered(iucv_ConnectionSevered *eib, void *pgm_data);
+static void
+vmlogrdr_iucv_MessagePending(iucv_MessagePending *eib, void *pgm_data);
+
+
+static iucv_interrupt_ops_t vmlogrdr_iucvops = {
+       .ConnectionComplete = vmlogrdr_iucv_ConnectionComplete,
+       .ConnectionSevered  = vmlogrdr_iucv_ConnectionSevered,
+       .MessagePending     = vmlogrdr_iucv_MessagePending,
+};
+
+
+DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue);
+DECLARE_WAIT_QUEUE_HEAD(read_wait_queue);
+
+/*
+ * pointer to system service private structure
+ * minor number 0 --> logrec
+ * minor number 1 --> account
+ * minor number 2 --> symptom
+ */
+
+static struct vmlogrdr_priv_t sys_ser[] = {
+       { .system_service = "*LOGREC ",
+         .internal_name  = "logrec",
+         .recording_name = "EREP",
+         .minor_num      = 0,
+         .buffer_free    = 1,
+         .priv_lock      = SPIN_LOCK_UNLOCKED,
+         .autorecording  = 1,
+         .autopurge      = 1,
+       },
+       { .system_service = "*ACCOUNT",
+         .internal_name  = "account",
+         .recording_name = "ACCOUNT",
+         .minor_num      = 1,
+         .buffer_free    = 1,
+         .priv_lock      = SPIN_LOCK_UNLOCKED,
+         .autorecording  = 1,
+         .autopurge      = 1,
+       },
+       { .system_service = "*SYMPTOM",
+         .internal_name  = "symptom",
+         .recording_name = "SYMPTOM",
+         .minor_num      = 2,
+         .buffer_free    = 1,
+         .priv_lock      = SPIN_LOCK_UNLOCKED,
+         .autorecording  = 1,
+         .autopurge      = 1,
+       }
+};
+
+#define MAXMINOR  (sizeof(sys_ser)/sizeof(struct vmlogrdr_priv_t))
+
+static char FENCE[] = {"EOR"};
+static int vmlogrdr_major = 0;
+static struct cdev  *vmlogrdr_cdev = NULL;
+static int recording_class_AB;
+
+
+static void
+vmlogrdr_iucv_ConnectionComplete (iucv_ConnectionComplete * eib,
+                                  void * pgm_data)
+{
+       struct vmlogrdr_priv_t * logptr = pgm_data;
+       spin_lock(&logptr->priv_lock);
+       logptr->connection_established = 1;
+       spin_unlock(&logptr->priv_lock);
+       wake_up(&conn_wait_queue);
+       return;
+}
+
+
+static void
+vmlogrdr_iucv_ConnectionSevered (iucv_ConnectionSevered * eib, void * pgm_data)
+{
+       u8 reason = (u8) eib->ipuser[8];
+       struct vmlogrdr_priv_t * logptr = pgm_data;
+
+       printk (KERN_ERR "vmlogrdr: connection severed with"
+               " reason %i\n", reason);
+
+       spin_lock(&logptr->priv_lock);
+       logptr->connection_established = 0;
+       logptr->iucv_path_severed = 1;
+       spin_unlock(&logptr->priv_lock);
+
+       wake_up(&conn_wait_queue);
+       /* just in case we're sleeping waiting for a record */
+       wake_up_interruptible(&read_wait_queue);
+}
+
+
+static void
+vmlogrdr_iucv_MessagePending (iucv_MessagePending * eib, void * pgm_data)
+{
+       struct vmlogrdr_priv_t * logptr = pgm_data;
+
+       /*
+        * This function is the bottom half so it should be quick.
+        * Copy the external interrupt data into our local eib and increment
+        * the usage count
+        */
+       spin_lock(&logptr->priv_lock);
+       memcpy(&(logptr->local_interrupt_buffer), eib, sizeof(*eib));
+       atomic_inc(&logptr->receive_ready);
+       spin_unlock(&logptr->priv_lock);
+       wake_up_interruptible(&read_wait_queue);
+}
+
+
+static int
+vmlogrdr_get_recording_class_AB(void) {
+       char cp_command[]="QUERY COMMAND RECORDING ";
+       char cp_response[80];
+       char *tail;
+       int len,i;
+
+       printk (KERN_DEBUG "vmlogrdr: query command: %s\n", cp_command);
+       cpcmd(cp_command, cp_response, sizeof(cp_response));
+       printk (KERN_DEBUG "vmlogrdr: response: %s", cp_response);
+       len = strnlen(cp_response,sizeof(cp_response));
+       // now the parsing
+       tail=strnchr(cp_response,len,'=');
+       if (!tail)
+               return 0;
+       tail++;
+       if (!strncmp("ANY",tail,3))
+               return 1;
+       if (!strncmp("NONE",tail,4))
+               return 0;
+       /*
+        * expect comma separated list of classes here, if one of them
+        * is A or B return 1 otherwise 0
+        */
+        for (i=tail-cp_response; i<len; i++)
+               if ( cp_response[i]=='A' || cp_response[i]=='B' )
+                       return 1;
+       return 0;
+}
+
+
+static int
+vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, int action, int purge) {
+
+       char cp_command[80];
+       char cp_response[160];
+       char *onoff, *qid_string;
+
+       memset(cp_command, 0x00, sizeof(cp_command));
+       memset(cp_response, 0x00, sizeof(cp_response));
+
+        onoff = ((action == 1) ? "ON" : "OFF");
+       qid_string = ((recording_class_AB == 1) ? " QID * " : "");
+
+        /*
+        * The recording commands needs to be called with option QID
+        * for guests that have previlege classes A or B.
+        * Purging has to be done as separate step, because recording
+        * can't be switched on as long as records are on the queue.
+        * Doing both at the same time doesn't work.
+        */
+
+       if (purge) {
+               snprintf(cp_command, sizeof(cp_command),
+                        "RECORDING %s PURGE %s",
+                        logptr->recording_name,
+                        qid_string);
+
+               printk (KERN_DEBUG "vmlogrdr: recording command: %s\n",
+                       cp_command);
+               cpcmd(cp_command, cp_response, sizeof(cp_response));
+               printk (KERN_DEBUG "vmlogrdr: recording response: %s",
+                       cp_response);
+       }
+
+       memset(cp_command, 0x00, sizeof(cp_command));
+       memset(cp_response, 0x00, sizeof(cp_response));
+       snprintf(cp_command, sizeof(cp_command), "RECORDING %s %s %s",
+               logptr->recording_name,
+               onoff,
+               qid_string);
+
+       printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);
+       cpcmd(cp_command, cp_response, sizeof(cp_response));
+       printk (KERN_DEBUG "vmlogrdr: recording response: %s",
+               cp_response);
+       /* The recording command will usually answer with 'Command complete'
+        * on success, but when the specific service was never connected
+        * before then there might be an additional informational message
+        * 'HCPCRC8072I Recording entry not found' before the
+         * 'Command complete'. So I use strstr rather then the strncmp.
+        */
+       if (strstr(cp_response,"Command complete"))
+               return 0;
+       else
+               return -EIO;
+
+}
+
+
+static int
+vmlogrdr_open (struct inode *inode, struct file *filp)
+{
+       int dev_num = 0;
+       struct vmlogrdr_priv_t * logptr = NULL;
+       int connect_rc = 0;
+       int ret;
+
+       dev_num = iminor(inode);
+       if (dev_num > MAXMINOR)
+               return -ENODEV;
+
+       logptr = &sys_ser[dev_num];
+       if (logptr == NULL)
+               return -ENODEV;
+
+       /*
+        * only allow for blocking reads to be open
+        */
+       if (filp->f_flags & O_NONBLOCK)
+               return -ENOSYS;
+
+       /* Besure this device hasn't already been opened */
+       spin_lock_bh(&logptr->priv_lock);
+       if (logptr->dev_in_use) {
+               spin_unlock_bh(&logptr->priv_lock);
+               return -EBUSY;
+       } else {
+               logptr->dev_in_use = 1;
+               spin_unlock_bh(&logptr->priv_lock);
+       }
+
+       atomic_set(&logptr->receive_ready, 0);
+       logptr->buffer_free = 1;
+
+       /* set the file options */
+       filp->private_data = logptr;
+       filp->f_op = &vmlogrdr_fops;
+
+       /* start recording for this service*/
+       ret=0;
+       if (logptr->autorecording)
+               ret = vmlogrdr_recording(logptr,1,logptr->autopurge);
+       if (ret)
+               printk (KERN_WARNING "vmlogrdr: failed to start "
+                       "recording automatically\n");
+
+       /* Register with iucv driver */
+       logptr->iucv_handle = iucv_register_program(iucvMagic,
+                       logptr->system_service, mask, &vmlogrdr_iucvops,
+                       logptr);
+
+       if (logptr->iucv_handle == NULL) {
+               printk (KERN_ERR "vmlogrdr: failed to register with"
+                       "iucv driver\n");
+               goto not_registered;
+       }
+
+       /* create connection to the system service */
+       spin_lock_bh(&logptr->priv_lock);
+       logptr->connection_established = 0;
+       logptr->iucv_path_severed = 0;
+       spin_unlock_bh(&logptr->priv_lock);
+
+       connect_rc = iucv_connect (&(logptr->pathid), 10, iucvMagic,
+                                       logptr->system_service, iucv_host, 0,
+                                       NULL, NULL,
+                                       logptr->iucv_handle, NULL);
+       if (connect_rc) {
+               printk (KERN_ERR "vmlogrdr: iucv connection to %s "
+                       "failed with rc %i \n", logptr->system_service,
+                       connect_rc);
+               goto not_connected;
+       }
+
+       /* We've issued the connect and now we must wait for a
+        * ConnectionComplete or ConnectinSevered Interrupt
+        * before we can continue to process.
+        */
+       wait_event(conn_wait_queue, (logptr->connection_established)
+                  || (logptr->iucv_path_severed));
+       if (logptr->iucv_path_severed) {
+               goto not_connected;
+       }
+
+       return 0;
+
+not_connected:
+       iucv_unregister_program(logptr->iucv_handle);
+       logptr->iucv_handle = NULL;
+not_registered:
+       if (logptr->autorecording)
+               vmlogrdr_recording(logptr,0,logptr->autopurge);
+       logptr->dev_in_use = 0;
+       return -EIO;
+
+
+}
+
+
+static int
+vmlogrdr_release (struct inode *inode, struct file *filp)
+{
+       int ret;
+
+       struct vmlogrdr_priv_t * logptr = filp->private_data;
+
+       iucv_unregister_program(logptr->iucv_handle);
+       logptr->iucv_handle = NULL;
+
+       if (logptr->autorecording) {
+               ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
+               if (ret)
+                       printk (KERN_WARNING "vmlogrdr: failed to stop "
+                               "recording automatically\n");
+       }
+       logptr->dev_in_use = 0;
+
+       return 0;
+}
+
+
+static int
+vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv) {
+       int rc, *temp;
+       /* we need to keep track of two data sizes here:
+        * The number of bytes we need to receive from iucv and
+        * the total number of bytes we actually write into the buffer.
+        */
+       int user_data_count, iucv_data_count;
+       char * buffer;
+
+       if (atomic_read(&priv->receive_ready)) {
+               spin_lock_bh(&priv->priv_lock);
+               if (priv->residual_length){
+                       /* receive second half of a record */
+                       iucv_data_count = priv->residual_length;
+                       user_data_count = 0;
+                       buffer = priv->buffer;
+               } else {
+                       /* receive a new record:
+                        * We need to return the total length of the record
+                         * + size of FENCE in the first 4 bytes of the buffer.
+                        */
+                       iucv_data_count =
+                               priv->local_interrupt_buffer.ln1msg2.ipbfln1f;
+                       user_data_count = sizeof(int);
+                       temp = (int*)priv->buffer;
+                       *temp= iucv_data_count + sizeof(FENCE);
+                       buffer = priv->buffer + sizeof(int);
+               }
+               /*
+                * If the record is bigger then our buffer, we receive only
+                * a part of it. We can get the rest later.
+                */
+               if (iucv_data_count > NET_BUFFER_SIZE)
+                       iucv_data_count = NET_BUFFER_SIZE;
+               rc = iucv_receive(priv->pathid,
+                                 priv->local_interrupt_buffer.ipmsgid,
+                                 priv->local_interrupt_buffer.iptrgcls,
+                                 buffer,
+                                 iucv_data_count,
+                                 NULL,
+                                 NULL,
+                                 &priv->residual_length);
+               spin_unlock_bh(&priv->priv_lock);
+               /* An rc of 5 indicates that the record was bigger then
+                * the buffer, which is OK for us. A 9 indicates that the
+                * record was purged befor we could receive it.
+                */
+               if (rc == 5)
+                       rc = 0;
+               if (rc == 9)
+                       atomic_set(&priv->receive_ready, 0);
+       } else {
+               rc = 1;
+       }
+       if (!rc) {
+               priv->buffer_free = 0;
+               user_data_count += iucv_data_count;
+               priv->current_position = priv->buffer;
+               if (priv->residual_length == 0){
+                       /* the whole record has been captured,
+                        * now add the fence */
+                       atomic_dec(&priv->receive_ready);
+                       buffer = priv->buffer + user_data_count;
+                       memcpy(buffer, FENCE, sizeof(FENCE));
+                       user_data_count += sizeof(FENCE);
+               }
+               priv->remaining = user_data_count;
+       }
+
+       return rc;
+}
+
+
+static ssize_t
+vmlogrdr_read (struct file *filp, char *data, size_t count, loff_t * ppos)
+{
+       int rc;
+       struct vmlogrdr_priv_t * priv = filp->private_data;
+
+       while (priv->buffer_free) {
+               rc = vmlogrdr_receive_data(priv);
+               if (rc) {
+                       rc = wait_event_interruptible(read_wait_queue,
+                                       atomic_read(&priv->receive_ready));
+                       if (rc)
+                               return rc;
+               }
+       }
+       /* copy only up to end of record */
+       if (count > priv->remaining)
+               count = priv->remaining;
+
+       if (copy_to_user(data, priv->current_position, count))
+               return -EFAULT;
+
+       *ppos += count;
+       priv->current_position += count;
+       priv->remaining -= count;
+
+       /* if all data has been transferred, set buffer free */
+       if (priv->remaining == 0)
+               priv->buffer_free = 1;
+
+       return count;
+}
+
+static ssize_t
+vmlogrdr_autopurge_store(struct device * dev, const char * buf, size_t count) {
+       struct vmlogrdr_priv_t *priv = dev->driver_data;
+       ssize_t ret = count;
+
+       switch (buf[0]) {
+       case '0':
+               priv->autopurge=0;
+               break;
+       case '1':
+               priv->autopurge=1;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+
+static ssize_t
+vmlogrdr_autopurge_show(struct device *dev, char *buf) {
+       struct vmlogrdr_priv_t *priv = dev->driver_data;
+       return sprintf(buf, "%u\n", priv->autopurge);
+}
+
+
+static DEVICE_ATTR(autopurge, 0644, vmlogrdr_autopurge_show,
+                  vmlogrdr_autopurge_store);
+
+
+static ssize_t
+vmlogrdr_purge_store(struct device * dev, const char * buf, size_t count) {
+
+       char cp_command[80];
+       char cp_response[80];
+       struct vmlogrdr_priv_t *priv = dev->driver_data;
+
+       if (buf[0] != '1')
+               return -EINVAL;
+
+       memset(cp_command, 0x00, sizeof(cp_command));
+       memset(cp_response, 0x00, sizeof(cp_response));
+
+        /*
+        * The recording command needs to be called with option QID
+        * for guests that have previlege classes A or B.
+        * Other guests will not recognize the command and we have to
+        * issue the same command without the QID parameter.
+        */
+
+       if (recording_class_AB)
+               snprintf(cp_command, sizeof(cp_command),
+                        "RECORDING %s PURGE QID * ",
+                        priv->recording_name);
+       else
+               snprintf(cp_command, sizeof(cp_command),
+                        "RECORDING %s PURGE ",
+                        priv->recording_name);
+
+       printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command);
+       cpcmd(cp_command, cp_response, sizeof(cp_response));
+       printk (KERN_DEBUG "vmlogrdr: recording response: %s",
+               cp_response);
+
+       return count;
+}
+
+
+static DEVICE_ATTR(purge, 0200, NULL, vmlogrdr_purge_store);
+
+
+static ssize_t
+vmlogrdr_autorecording_store(struct device *dev, const char *buf,
+                            size_t count) {
+       struct vmlogrdr_priv_t *priv = dev->driver_data;
+       ssize_t ret = count;
+
+       switch (buf[0]) {
+       case '0':
+               priv->autorecording=0;
+               break;
+       case '1':
+               priv->autorecording=1;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+
+static ssize_t
+vmlogrdr_autorecording_show(struct device *dev, char *buf) {
+       struct vmlogrdr_priv_t *priv = dev->driver_data;
+       return sprintf(buf, "%u\n", priv->autorecording);
+}
+
+
+static DEVICE_ATTR(autorecording, 0644, vmlogrdr_autorecording_show,
+                  vmlogrdr_autorecording_store);
+
+
+static ssize_t
+vmlogrdr_recording_store(struct device * dev, const char * buf, size_t count) {
+
+       struct vmlogrdr_priv_t *priv = dev->driver_data;
+       ssize_t ret;
+
+       switch (buf[0]) {
+       case '0':
+               ret = vmlogrdr_recording(priv,0,0);
+               break;
+       case '1':
+               ret = vmlogrdr_recording(priv,1,0);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       if (ret)
+               return ret;
+       else
+               return count;
+
+}
+
+
+static DEVICE_ATTR(recording, 0200, NULL, vmlogrdr_recording_store);
+
+
+static ssize_t
+vmlogrdr_recording_status_show(struct device_driver *driver, char *buf) {
+
+       char cp_command[] = "QUERY RECORDING ";
+       int len;
+
+       cpcmd(cp_command, buf, 4096);
+       len = strlen(buf);
+       return len;
+}
+
+
+static DRIVER_ATTR(recording_status, 0444, vmlogrdr_recording_status_show,
+                  NULL);
+
+static struct attribute *vmlogrdr_attrs[] = {
+       &dev_attr_autopurge.attr,
+       &dev_attr_purge.attr,
+       &dev_attr_autorecording.attr,
+       &dev_attr_recording.attr,
+       NULL,
+};
+
+static struct attribute_group vmlogrdr_attr_group = {
+       .attrs = vmlogrdr_attrs,
+};
+
+static struct class_simple *vmlogrdr_class;
+static struct device_driver vmlogrdr_driver = {
+       .name = "vmlogrdr",
+       .bus  = &iucv_bus,
+};
+
+
+static int
+vmlogrdr_register_driver(void) {
+       int ret;
+
+       ret = driver_register(&vmlogrdr_driver);
+       if (ret) {
+               printk(KERN_ERR "vmlogrdr: failed to register driver.\n");
+               return ret;
+       }
+
+       ret = driver_create_file(&vmlogrdr_driver,
+                                &driver_attr_recording_status);
+       if (ret) {
+               printk(KERN_ERR "vmlogrdr: failed to add driver attribute.\n");
+               goto unregdriver;
+       }
+
+       vmlogrdr_class = class_simple_create(THIS_MODULE, "vmlogrdr");
+       if (IS_ERR(vmlogrdr_class)) {
+               printk(KERN_ERR "vmlogrdr: failed to create class.\n");
+               ret=PTR_ERR(vmlogrdr_class);
+               vmlogrdr_class=NULL;
+               goto unregattr;
+       }
+       return 0;
+
+unregattr:
+       driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);
+unregdriver:
+       driver_unregister(&vmlogrdr_driver);
+       return ret;
+}
+
+
+static void
+vmlogrdr_unregister_driver(void) {
+       class_simple_destroy(vmlogrdr_class);
+       vmlogrdr_class = NULL;
+       driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);
+       driver_unregister(&vmlogrdr_driver);
+       return;
+}
+
+
+static int
+vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) {
+       struct device *dev;
+       int ret;
+
+       dev = kmalloc(sizeof(struct device), GFP_KERNEL);
+       if (dev) {
+               memset(dev, 0, sizeof(struct device));
+               snprintf(dev->bus_id, BUS_ID_SIZE, "%s",
+                        priv->internal_name);
+               dev->bus = &iucv_bus;
+               dev->parent = iucv_root;
+               dev->driver = &vmlogrdr_driver;
+               /*
+                * The release function could be called after the
+                * module has been unloaded. It's _only_ task is to
+                * free the struct. Therefore, we specify kfree()
+                * directly here. (Probably a little bit obfuscating
+                * but legitime ...).
+                */
+               dev->release = (void (*)(struct device *))kfree;
+       } else
+               return -ENOMEM;
+       ret = device_register(dev);
+       if (ret)
+               return ret;
+
+       ret = sysfs_create_group(&dev->kobj, &vmlogrdr_attr_group);
+       if (ret) {
+               device_unregister(dev);
+               return ret;
+       }
+       priv->class_device = class_simple_device_add(
+                               vmlogrdr_class,
+                               MKDEV(vmlogrdr_major, priv->minor_num),
+                               dev,
+                               "%s", dev->bus_id );
+       if (IS_ERR(priv->class_device)) {
+               ret = PTR_ERR(priv->class_device);
+               priv->class_device=NULL;
+               sysfs_remove_group(&dev->kobj, &vmlogrdr_attr_group);
+               device_unregister(dev);
+               return ret;
+       }
+       dev->driver_data = priv;
+       priv->device = dev;
+       return 0;
+}
+
+
+static int
+vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv ) {
+       class_simple_device_remove(MKDEV(vmlogrdr_major, priv->minor_num));
+       if (priv->device != NULL) {
+               sysfs_remove_group(&priv->device->kobj, &vmlogrdr_attr_group);
+               device_unregister(priv->device);
+               priv->device=NULL;
+       }
+       return 0;
+}
+
+
+static int
+vmlogrdr_register_cdev(dev_t dev) {
+       int rc = 0;
+       vmlogrdr_cdev = cdev_alloc();
+       if (!vmlogrdr_cdev) {
+               return -ENOMEM;
+       }
+       vmlogrdr_cdev->owner = THIS_MODULE;
+       vmlogrdr_cdev->ops = &vmlogrdr_fops;
+       vmlogrdr_cdev->dev = dev;
+       rc = cdev_add(vmlogrdr_cdev, vmlogrdr_cdev->dev, MAXMINOR);
+       if (!rc)
+               return 0;
+
+       // cleanup: cdev is not fully registered, no cdev_del here!
+       kobject_put(&vmlogrdr_cdev->kobj);
+       vmlogrdr_cdev=NULL;
+       return rc;
+}
+
+
+static void
+vmlogrdr_cleanup(void) {
+        int i;
+       if (vmlogrdr_cdev) {
+               cdev_del(vmlogrdr_cdev);
+               vmlogrdr_cdev=NULL;
+       }
+       for (i=0; i < MAXMINOR; ++i ) {
+               vmlogrdr_unregister_device(&sys_ser[i]);
+               free_page((unsigned long)sys_ser[i].buffer);
+       }
+       vmlogrdr_unregister_driver();
+       if (vmlogrdr_major) {
+               unregister_chrdev_region(MKDEV(vmlogrdr_major, 0), MAXMINOR);
+               vmlogrdr_major=0;
+       }
+}
+
+
+static int
+vmlogrdr_init(void)
+{
+       int rc;
+       int i;
+       dev_t dev;
+
+       if (! MACHINE_IS_VM) {
+               printk (KERN_ERR "vmlogrdr: not running under VM, "
+                               "driver not loaded.\n");
+               return -ENODEV;
+       }
+
+        recording_class_AB = vmlogrdr_get_recording_class_AB();
+
+       rc = alloc_chrdev_region(&dev, 0, MAXMINOR, "vmlogrdr");
+       if (rc)
+               return rc;
+       vmlogrdr_major = MAJOR(dev);
+
+       rc=vmlogrdr_register_driver();
+       if (rc)
+               goto cleanup;
+
+       for (i=0; i < MAXMINOR; ++i ) {
+               sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
+               if (!sys_ser[i].buffer) {
+                       rc = ENOMEM;
+                       break;
+               }
+               sys_ser[i].current_position = sys_ser[i].buffer;
+               rc=vmlogrdr_register_device(&sys_ser[i]);
+               if (rc)
+                       break;
+       }
+       if (rc)
+               goto cleanup;
+
+       rc = vmlogrdr_register_cdev(dev);
+       if (rc)
+               goto cleanup;
+       printk (KERN_INFO "vmlogrdr: driver loaded\n");
+       return 0;
+
+cleanup:
+       vmlogrdr_cleanup();
+       printk (KERN_ERR "vmlogrdr: driver not loaded.\n");
+       return rc;
+}
+
+
+static void
+vmlogrdr_exit(void)
+{
+       vmlogrdr_cleanup();
+       printk (KERN_INFO "vmlogrdr: driver unloaded\n");
+       return;
+}
+
+
+module_init(vmlogrdr_init);
+module_exit(vmlogrdr_exit);
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c
new file mode 100644 (file)
index 0000000..22cf4fe
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Watchdog implementation based on z/VM Watchdog Timer API
+ *
+ * The user space watchdog daemon can use this driver as
+ * /dev/vmwatchdog to have z/VM execute the specified CP
+ * command when the timeout expires. The default command is
+ * "IPL", which which cause an immediate reboot.
+ */
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/watchdog.h>
+
+#include <asm/ebcdic.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define MAX_CMDLEN 240
+#define MIN_INTERVAL 15
+static char vmwdt_cmd[MAX_CMDLEN] = "IPL";
+static int vmwdt_conceal;
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+static int vmwdt_nowayout = 1;
+#else
+static int vmwdt_nowayout = 0;
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
+MODULE_DESCRIPTION("z/VM Watchdog Timer");
+module_param_string(cmd, vmwdt_cmd, MAX_CMDLEN, 0644);
+MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers");
+module_param_named(conceal, vmwdt_conceal, bool, 0644);
+MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog "
+               " is active");
+module_param_named(nowayout, vmwdt_nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"
+               " (default=CONFIG_WATCHDOG_NOWAYOUT)");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+
+static unsigned int vmwdt_interval = 60;
+static unsigned long vmwdt_is_open;
+static int vmwdt_expect_close;
+
+enum vmwdt_func {
+       /* function codes */
+       wdt_init   = 0,
+       wdt_change = 1,
+       wdt_cancel = 2,
+       /* flags */
+       wdt_conceal = 0x80000000,
+};
+
+static int __diag288(enum vmwdt_func func, unsigned int timeout,
+                           char *cmd, size_t len)
+{
+       register unsigned long __func asm("2");
+       register unsigned long __timeout asm("3");
+       register unsigned long __cmdp asm("4");
+       register unsigned long __cmdl asm("5");
+       int err;
+
+       __func = func;
+       __timeout = timeout;
+       __cmdp = virt_to_phys(cmd);
+       __cmdl = len;
+       err = 0;
+       asm volatile (
+#ifdef __s390x__
+                      "diag %2,%4,0x288\n"
+               "1:     \n"
+               ".section .fixup,\"ax\"\n"
+               "2:     lghi %0,%1\n"
+               "       jg 1b\n"
+               ".previous\n"
+               ".section __ex_table,\"a\"\n"
+               "       .align 8\n"
+               "       .quad 1b,2b\n"
+               ".previous\n"
+#else
+                      "diag %2,%4,0x288\n"
+               "1:     \n"
+               ".section .fixup,\"ax\"\n"
+               "2:     lhi %0,%1\n"
+               "       bras 1,3f\n"
+               "       .long 1b\n"
+               "3:     l 1,0(1)\n"
+               "       br 1\n"
+               ".previous\n"
+               ".section __ex_table,\"a\"\n"
+               "       .align 4\n"
+               "       .long 1b,2b\n"
+               ".previous\n"
+#endif
+               : "+&d"(err)
+               : "i"(-EINVAL), "d"(__func), "d"(__timeout),
+                 "d"(__cmdp), "d"(__cmdl)
+               : "1", "cc");
+       return err;
+}
+
+static int vmwdt_keepalive(void)
+{
+       /* we allocate new memory every time to avoid having
+        * to track the state. static allocation is not an
+        * option since that might not be contiguous in real
+        * storage in case of a modular build */
+       static char *ebc_cmd;
+       size_t len;
+       int ret;
+       unsigned int func;
+
+       ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL);
+       if (!ebc_cmd)
+               return -ENOMEM;
+
+       len = strlcpy(ebc_cmd, vmwdt_cmd, MAX_CMDLEN);
+       ASCEBC(ebc_cmd, MAX_CMDLEN);
+       EBC_TOUPPER(ebc_cmd, MAX_CMDLEN);
+
+       func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init;
+       ret = __diag288(func, vmwdt_interval, ebc_cmd, len);
+       kfree(ebc_cmd);
+
+       if (ret) {
+               printk(KERN_WARNING "%s: problem setting interval %d, "
+                       "cmd %s\n", __FUNCTION__, vmwdt_interval,
+                       vmwdt_cmd);
+       }
+       return ret;
+}
+
+static int vmwdt_disable(void)
+{
+       int ret = __diag288(wdt_cancel, 0, "", 0);
+       if (ret) {
+               printk(KERN_WARNING "%s: problem disabling watchdog\n",
+                       __FUNCTION__);
+       }
+       return ret;
+}
+
+static int __init vmwdt_probe(void)
+{
+       /* there is no real way to see if the watchdog is supported,
+        * so we try initializing it with a NOP command ("BEGIN")
+        * that won't cause any harm even if the following disable
+        * fails for some reason */
+       static char __initdata ebc_begin[] = {
+               194, 197, 199, 201, 213
+       };
+       if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) {
+               printk(KERN_INFO "z/VM watchdog not available\n");
+               return -EINVAL;
+       }
+       return vmwdt_disable();
+}
+
+static int vmwdt_open(struct inode *i, struct file *f)
+{
+       int ret;
+       if (test_and_set_bit(0, &vmwdt_is_open))
+               return -EBUSY;
+       ret = vmwdt_keepalive();
+       if (ret)
+               clear_bit(0, &vmwdt_is_open);
+       return ret ? ret : nonseekable_open(i, f);
+}
+
+static int vmwdt_close(struct inode *i, struct file *f)
+{
+       if (vmwdt_expect_close == 42)
+               vmwdt_disable();
+       vmwdt_expect_close = 0;
+       clear_bit(0, &vmwdt_is_open);
+       return 0;
+}
+
+static struct watchdog_info vmwdt_info = {
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+       .firmware_version = 0,
+       .identity = "z/VM Watchdog Timer",
+};
+
+static int vmwdt_ioctl(struct inode *i, struct file *f,
+                         unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case WDIOC_GETSUPPORT:
+               if (copy_to_user((void __user *)arg, &vmwdt_info,
+                                       sizeof(vmwdt_info)))
+                       return -EFAULT;
+               return 0;
+       case WDIOC_GETSTATUS:
+       case WDIOC_GETBOOTSTATUS:
+               return put_user(0, (int *)arg);
+       case WDIOC_GETTEMP:
+               return -EINVAL;
+       case WDIOC_SETOPTIONS:
+               {
+                       int options, ret;
+                       if (get_user(options, (int __user *)arg))
+                               return -EFAULT;
+                       ret = -EINVAL;
+                       if (options & WDIOS_DISABLECARD) {
+                               ret = vmwdt_disable();
+                               if (ret)
+                                       return ret;
+                       }
+                       if (options & WDIOS_ENABLECARD) {
+                               ret = vmwdt_keepalive();
+                       }
+                       return ret;
+               }
+       case WDIOC_GETTIMEOUT:
+               return put_user(vmwdt_interval, (int __user *)arg);
+       case WDIOC_SETTIMEOUT:
+               {
+                       int interval;
+                       if (get_user(interval, (int __user *)arg))
+                               return -EFAULT;
+                       if (interval < MIN_INTERVAL)
+                               return -EINVAL;
+                       vmwdt_interval = interval;
+               }
+               return vmwdt_keepalive();
+       case WDIOC_KEEPALIVE:
+               return vmwdt_keepalive();
+       }
+
+       return -EINVAL;
+}
+
+static ssize_t vmwdt_write(struct file *f, const char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       if(count) {
+               if (!vmwdt_nowayout) {
+                       size_t i;
+
+                       /* note: just in case someone wrote the magic character
+                        * five months ago... */
+                       vmwdt_expect_close = 0;
+
+                       for (i = 0; i != count; i++) {
+                               char c;
+                               if (get_user(c, buf+i))
+                                       return -EFAULT;
+                               if (c == 'V')
+                                       vmwdt_expect_close = 42;
+                       }
+               }
+               /* someone wrote to us, we should restart timer */
+               vmwdt_keepalive();
+       }
+       return count;
+}
+
+static struct file_operations vmwdt_fops = {
+       .open    = &vmwdt_open,
+       .release = &vmwdt_close,
+       .ioctl   = &vmwdt_ioctl,
+       .write   = &vmwdt_write,
+       .owner   = THIS_MODULE,
+};
+
+static struct miscdevice vmwdt_dev = {
+       .minor      = WATCHDOG_MINOR,
+       .name       = "watchdog",
+       .fops       = &vmwdt_fops,
+};
+
+static int __init vmwdt_init(void)
+{
+       int ret;
+
+       ret = vmwdt_probe();
+       if (ret)
+               return ret;
+       return misc_register(&vmwdt_dev);
+}
+module_init(vmwdt_init);
+
+static void __exit vmwdt_exit(void)
+{
+       WARN_ON(misc_deregister(&vmwdt_dev) != 0);
+}
+module_exit(vmwdt_exit);
diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c
new file mode 100644 (file)
index 0000000..c34403c
--- /dev/null
@@ -0,0 +1,1202 @@
+/*
+ * Initio A100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * Copyright (c) 2003-2004 Christoph Hellwig
+ * 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, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+/*
+ * Revision History:
+ * 07/02/98 hl - v.91n Initial drivers.
+ * 09/14/98 hl - v1.01 Support new Kernel.
+ * 09/22/98 hl - v1.01a Support reset.
+ * 09/24/98 hl - v1.01b Fixed reset.
+ * 10/05/98 hl - v1.02 split the source code and release.
+ * 12/19/98 bv - v1.02a Use spinlocks for 2.1.95 and up
+ * 01/31/99 bv - v1.02b Use mdelay instead of waitForPause
+ * 08/08/99 bv - v1.02c Use waitForPause again.
+ * 06/25/02 Doug Ledford <dledford@redhat.com> - v1.02d
+ *          - Remove limit on number of controllers
+ *          - Port to DMA mapping API
+ *          - Clean up interrupt handler registration
+ *          - Fix memory leaks
+ *          - Fix allocation of scsi host structs and private data
+ * 11/18/03 Christoph Hellwig <hch@lst.de>
+ *         - Port to new probing API
+ *         - Fix some more leaks in init failure cases
+ * 9/28/04 Christoph Hellwig <hch@lst.de>
+ *         - merge the two source files
+ *         - remove internal queueing code
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "a100u2w.h"
+
+
+#define JIFFIES_TO_MS(t) ((t) * 1000 / HZ)
+#define MS_TO_JIFFIES(j) ((j * HZ) / 1000)
+
+static ORC_SCB *orc_alloc_scb(ORC_HCS * hcsp);
+static void inia100SCBPost(BYTE * pHcb, BYTE * pScb);
+
+static NVRAM nvram, *nvramp = &nvram;
+static UCHAR dftNvRam[64] =
+{
+/*----------header -------------*/
+       0x01,                   /* 0x00: Sub System Vendor ID 0 */
+       0x11,                   /* 0x01: Sub System Vendor ID 1 */
+       0x60,                   /* 0x02: Sub System ID 0        */
+       0x10,                   /* 0x03: Sub System ID 1        */
+       0x00,                   /* 0x04: SubClass               */
+       0x01,                   /* 0x05: Vendor ID 0            */
+       0x11,                   /* 0x06: Vendor ID 1            */
+       0x60,                   /* 0x07: Device ID 0            */
+       0x10,                   /* 0x08: Device ID 1            */
+       0x00,                   /* 0x09: Reserved               */
+       0x00,                   /* 0x0A: Reserved               */
+       0x01,                   /* 0x0B: Revision of Data Structure     */
+                               /* -- Host Adapter Structure --- */
+       0x01,                   /* 0x0C: Number Of SCSI Channel */
+       0x01,                   /* 0x0D: BIOS Configuration 1   */
+       0x00,                   /* 0x0E: BIOS Configuration 2   */
+       0x00,                   /* 0x0F: BIOS Configuration 3   */
+                               /* --- SCSI Channel 0 Configuration --- */
+       0x07,                   /* 0x10: H/A ID                 */
+       0x83,                   /* 0x11: Channel Configuration  */
+       0x20,                   /* 0x12: MAX TAG per target     */
+       0x0A,                   /* 0x13: SCSI Reset Recovering time     */
+       0x00,                   /* 0x14: Channel Configuration4 */
+       0x00,                   /* 0x15: Channel Configuration5 */
+                               /* SCSI Channel 0 Target Configuration  */
+                               /* 0x16-0x25                    */
+       0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+       0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+                               /* --- SCSI Channel 1 Configuration --- */
+       0x07,                   /* 0x26: H/A ID                 */
+       0x83,                   /* 0x27: Channel Configuration  */
+       0x20,                   /* 0x28: MAX TAG per target     */
+       0x0A,                   /* 0x29: SCSI Reset Recovering time     */
+       0x00,                   /* 0x2A: Channel Configuration4 */
+       0x00,                   /* 0x2B: Channel Configuration5 */
+                               /* SCSI Channel 1 Target Configuration  */
+                               /* 0x2C-0x3B                    */
+       0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+       0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+       0x00,                   /* 0x3C: Reserved               */
+       0x00,                   /* 0x3D: Reserved               */
+       0x00,                   /* 0x3E: Reserved               */
+       0x00                    /* 0x3F: Checksum               */
+};
+
+
+/***************************************************************************/
+static void waitForPause(unsigned amount)
+{
+       ULONG the_time = jiffies + MS_TO_JIFFIES(amount);
+       while (time_before_eq(jiffies, the_time))
+               cpu_relax();
+}
+
+/***************************************************************************/
+static UCHAR waitChipReady(ORC_HCS * hcsp)
+{
+       int i;
+
+       for (i = 0; i < 10; i++) {      /* Wait 1 second for report timeout     */
+               if (ORC_RD(hcsp->HCS_Base, ORC_HCTRL) & HOSTSTOP)       /* Wait HOSTSTOP set */
+                       return 1;
+               waitForPause(100);      /* wait 100ms before try again  */
+       }
+       return 0;
+}
+
+/***************************************************************************/
+static UCHAR waitFWReady(ORC_HCS * hcsp)
+{
+       int i;
+
+       for (i = 0; i < 10; i++) {      /* Wait 1 second for report timeout     */
+               if (ORC_RD(hcsp->HCS_Base, ORC_HSTUS) & RREADY)         /* Wait READY set */
+                       return 1;
+               waitForPause(100);      /* wait 100ms before try again  */
+       }
+       return 0;
+}
+
+/***************************************************************************/
+static UCHAR waitSCSIRSTdone(ORC_HCS * hcsp)
+{
+       int i;
+
+       for (i = 0; i < 10; i++) {      /* Wait 1 second for report timeout     */
+               if (!(ORC_RD(hcsp->HCS_Base, ORC_HCTRL) & SCSIRST))     /* Wait SCSIRST done */
+                       return 1;
+               waitForPause(100);      /* wait 100ms before try again  */
+       }
+       return 0;
+}
+
+/***************************************************************************/
+static UCHAR waitHDOoff(ORC_HCS * hcsp)
+{
+       int i;
+
+       for (i = 0; i < 10; i++) {      /* Wait 1 second for report timeout     */
+               if (!(ORC_RD(hcsp->HCS_Base, ORC_HCTRL) & HDO))         /* Wait HDO off */
+                       return 1;
+               waitForPause(100);      /* wait 100ms before try again  */
+       }
+       return 0;
+}
+
+/***************************************************************************/
+static UCHAR waitHDIset(ORC_HCS * hcsp, UCHAR * pData)
+{
+       int i;
+
+       for (i = 0; i < 10; i++) {      /* Wait 1 second for report timeout     */
+               if ((*pData = ORC_RD(hcsp->HCS_Base, ORC_HSTUS)) & HDI)
+                       return 1;       /* Wait HDI set */
+               waitForPause(100);      /* wait 100ms before try again  */
+       }
+       return 0;
+}
+
+/***************************************************************************/
+static unsigned short get_FW_version(ORC_HCS * hcsp)
+{
+       UCHAR bData;
+       union {
+               unsigned short sVersion;
+               unsigned char cVersion[2];
+       } Version;
+
+       ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_VERSION);
+       ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+       if (waitHDOoff(hcsp) == 0)      /* Wait HDO off   */
+               return 0;
+
+       if (waitHDIset(hcsp, &bData) == 0)      /* Wait HDI set   */
+               return 0;
+       Version.cVersion[0] = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+       ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData);      /* Clear HDI            */
+
+       if (waitHDIset(hcsp, &bData) == 0)      /* Wait HDI set   */
+               return 0;
+       Version.cVersion[1] = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+       ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData);      /* Clear HDI            */
+
+       return (Version.sVersion);
+}
+
+/***************************************************************************/
+static UCHAR set_NVRAM(ORC_HCS * hcsp, unsigned char address, unsigned char value)
+{
+       ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_SET_NVM);    /* Write command */
+       ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+       if (waitHDOoff(hcsp) == 0)      /* Wait HDO off   */
+               return 0;
+
+       ORC_WR(hcsp->HCS_Base + ORC_HDATA, address);    /* Write address */
+       ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+       if (waitHDOoff(hcsp) == 0)      /* Wait HDO off   */
+               return 0;
+
+       ORC_WR(hcsp->HCS_Base + ORC_HDATA, value);      /* Write value  */
+       ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+       if (waitHDOoff(hcsp) == 0)      /* Wait HDO off   */
+               return 0;
+
+       return 1;
+}
+
+/***************************************************************************/
+static UCHAR get_NVRAM(ORC_HCS * hcsp, unsigned char address, unsigned char *pDataIn)
+{
+       unsigned char bData;
+
+       ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_GET_NVM);    /* Write command */
+       ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+       if (waitHDOoff(hcsp) == 0)      /* Wait HDO off   */
+               return 0;
+
+       ORC_WR(hcsp->HCS_Base + ORC_HDATA, address);    /* Write address */
+       ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+       if (waitHDOoff(hcsp) == 0)      /* Wait HDO off   */
+               return 0;
+
+       if (waitHDIset(hcsp, &bData) == 0)      /* Wait HDI set   */
+               return 0;
+       *pDataIn = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+       ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData);      /* Clear HDI    */
+
+       return 1;
+}
+
+/***************************************************************************/
+static void orc_exec_scb(ORC_HCS * hcsp, ORC_SCB * scbp)
+{
+       scbp->SCB_Status = ORCSCB_POST;
+       ORC_WR(hcsp->HCS_Base + ORC_PQUEUE, scbp->SCB_ScbIdx);
+       return;
+}
+
+
+/***********************************************************************
+ Read SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+static int se2_rd_all(ORC_HCS * hcsp)
+{
+       int i;
+       UCHAR *np, chksum = 0;
+
+       np = (UCHAR *) nvramp;
+       for (i = 0; i < 64; i++, np++) {        /* <01> */
+               if (get_NVRAM(hcsp, (unsigned char) i, np) == 0)
+                       return -1;
+//      *np++ = get_NVRAM(hcsp, (unsigned char ) i);
+       }
+
+/*------ Is ckecksum ok ? ------*/
+       np = (UCHAR *) nvramp;
+       for (i = 0; i < 63; i++)
+               chksum += *np++;
+
+       if (nvramp->CheckSum != (UCHAR) chksum)
+               return -1;
+       return 1;
+}
+
+/************************************************************************
+ Update SCSI H/A configuration parameters from serial EEPROM
+*************************************************************************/
+static void se2_update_all(ORC_HCS * hcsp)
+{                              /* setup default pattern  */
+       int i;
+       UCHAR *np, *np1, chksum = 0;
+
+       /* Calculate checksum first   */
+       np = (UCHAR *) dftNvRam;
+       for (i = 0; i < 63; i++)
+               chksum += *np++;
+       *np = chksum;
+
+       np = (UCHAR *) dftNvRam;
+       np1 = (UCHAR *) nvramp;
+       for (i = 0; i < 64; i++, np++, np1++) {
+               if (*np != *np1) {
+                       set_NVRAM(hcsp, (unsigned char) i, *np);
+               }
+       }
+       return;
+}
+
+/*************************************************************************
+ Function name  : read_eeprom
+**************************************************************************/
+static void read_eeprom(ORC_HCS * hcsp)
+{
+       if (se2_rd_all(hcsp) != 1) {
+               se2_update_all(hcsp);   /* setup default pattern        */
+               se2_rd_all(hcsp);       /* load again                   */
+       }
+}
+
+
+/***************************************************************************/
+static UCHAR load_FW(ORC_HCS * hcsp)
+{
+       U32 dData;
+       USHORT wBIOSAddress;
+       USHORT i;
+       UCHAR *pData, bData;
+
+
+       bData = ORC_RD(hcsp->HCS_Base, ORC_GCFG);
+       ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData | EEPRG);       /* Enable EEPROM programming */
+       ORC_WR(hcsp->HCS_Base + ORC_EBIOSADR2, 0x00);
+       ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x00);
+       if (ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA) != 0x55) {
+               ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData);       /* Disable EEPROM programming */
+               return 0;
+       }
+       ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x01);
+       if (ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA) != 0xAA) {
+               ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData);       /* Disable EEPROM programming */
+               return 0;
+       }
+       ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST | DOWNLOAD);       /* Enable SRAM programming */
+       pData = (UCHAR *) & dData;
+       dData = 0;              /* Initial FW address to 0 */
+       ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x10);
+       *pData = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA);         /* Read from BIOS */
+       ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x11);
+       *(pData + 1) = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA);   /* Read from BIOS */
+       ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, 0x12);
+       *(pData + 2) = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA);   /* Read from BIOS */
+       ORC_WR(hcsp->HCS_Base + ORC_EBIOSADR2, *(pData + 2));
+       ORC_WRLONG(hcsp->HCS_Base + ORC_FWBASEADR, dData);      /* Write FW address */
+
+       wBIOSAddress = (USHORT) dData;  /* FW code locate at BIOS address + ? */
+       for (i = 0, pData = (UCHAR *) & dData;  /* Download the code    */
+            i < 0x1000;        /* Firmware code size = 4K      */
+            i++, wBIOSAddress++) {
+               ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, wBIOSAddress);
+               *pData++ = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA);       /* Read from BIOS */
+               if ((i % 4) == 3) {
+                       ORC_WRLONG(hcsp->HCS_Base + ORC_RISCRAM, dData);        /* Write every 4 bytes */
+                       pData = (UCHAR *) & dData;
+               }
+       }
+
+       ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST | DOWNLOAD);       /* Reset program count 0 */
+       wBIOSAddress -= 0x1000; /* Reset the BIOS adddress      */
+       for (i = 0, pData = (UCHAR *) & dData;  /* Check the code       */
+            i < 0x1000;        /* Firmware code size = 4K      */
+            i++, wBIOSAddress++) {
+               ORC_WRSHORT(hcsp->HCS_Base + ORC_EBIOSADR0, wBIOSAddress);
+               *pData++ = ORC_RD(hcsp->HCS_Base, ORC_EBIOSDATA);       /* Read from BIOS */
+               if ((i % 4) == 3) {
+                       if (ORC_RDLONG(hcsp->HCS_Base, ORC_RISCRAM) != dData) {
+                               ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST);  /* Reset program to 0 */
+                               ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData);       /*Disable EEPROM programming */
+                               return 0;
+                       }
+                       pData = (UCHAR *) & dData;
+               }
+       }
+       ORC_WR(hcsp->HCS_Base + ORC_RISCCTL, PRGMRST);  /* Reset program to 0   */
+       ORC_WR(hcsp->HCS_Base + ORC_GCFG, bData);       /* Disable EEPROM programming */
+       return 1;
+}
+
+/***************************************************************************/
+static void setup_SCBs(ORC_HCS * hcsp)
+{
+       ORC_SCB *pVirScb;
+       int i;
+       ESCB *pVirEscb;
+       dma_addr_t pPhysEscb;
+
+       /* Setup SCB HCS_Base and SCB Size registers */
+       ORC_WR(hcsp->HCS_Base + ORC_SCBSIZE, ORC_MAXQUEUE);     /* Total number of SCBs */
+       /* SCB HCS_Base address 0      */
+       ORC_WRLONG(hcsp->HCS_Base + ORC_SCBBASE0, hcsp->HCS_physScbArray);
+       /* SCB HCS_Base address 1      */
+       ORC_WRLONG(hcsp->HCS_Base + ORC_SCBBASE1, hcsp->HCS_physScbArray);
+
+       /* setup scatter list address with one buffer */
+       pVirScb = hcsp->HCS_virScbArray;
+       pVirEscb = hcsp->HCS_virEscbArray;
+
+       for (i = 0; i < ORC_MAXQUEUE; i++) {
+               pPhysEscb = (hcsp->HCS_physEscbArray + (sizeof(ESCB) * i));
+               pVirScb->SCB_SGPAddr = (U32) pPhysEscb;
+               pVirScb->SCB_SensePAddr = (U32) pPhysEscb;
+               pVirScb->SCB_EScb = pVirEscb;
+               pVirScb->SCB_ScbIdx = i;
+               pVirScb++;
+               pVirEscb++;
+       }
+
+       return;
+}
+
+/***************************************************************************/
+static void initAFlag(ORC_HCS * hcsp)
+{
+       UCHAR i, j;
+
+       for (i = 0; i < MAX_CHANNELS; i++) {
+               for (j = 0; j < 8; j++) {
+                       hcsp->BitAllocFlag[i][j] = 0xffffffff;
+               }
+       }
+}
+
+/***************************************************************************/
+static int init_orchid(ORC_HCS * hcsp)
+{
+       UBYTE *readBytep;
+       USHORT revision;
+       UCHAR i;
+
+       initAFlag(hcsp);
+       ORC_WR(hcsp->HCS_Base + ORC_GIMSK, 0xFF);       /* Disable all interrupt        */
+       if (ORC_RD(hcsp->HCS_Base, ORC_HSTUS) & RREADY) {       /* Orchid is ready              */
+               revision = get_FW_version(hcsp);
+               if (revision == 0xFFFF) {
+                       ORC_WR(hcsp->HCS_Base + ORC_HCTRL, DEVRST);     /* Reset Host Adapter   */
+                       if (waitChipReady(hcsp) == 0)
+                               return (-1);
+                       load_FW(hcsp);  /* Download FW                  */
+                       setup_SCBs(hcsp);       /* Setup SCB HCS_Base and SCB Size registers */
+                       ORC_WR(hcsp->HCS_Base + ORC_HCTRL, 0);  /* clear HOSTSTOP       */
+                       if (waitFWReady(hcsp) == 0)
+                               return (-1);
+                       /* Wait for firmware ready     */
+               } else {
+                       setup_SCBs(hcsp);       /* Setup SCB HCS_Base and SCB Size registers */
+               }
+       } else {                /* Orchid is not Ready          */
+               ORC_WR(hcsp->HCS_Base + ORC_HCTRL, DEVRST);     /* Reset Host Adapter   */
+               if (waitChipReady(hcsp) == 0)
+                       return (-1);
+               load_FW(hcsp);  /* Download FW                  */
+               setup_SCBs(hcsp);       /* Setup SCB HCS_Base and SCB Size registers */
+               ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);        /* Do Hardware Reset &  */
+
+               /*     clear HOSTSTOP  */
+               if (waitFWReady(hcsp) == 0)             /* Wait for firmware ready      */
+                       return (-1);
+       }
+
+/*------------- get serial EEProm settting -------*/
+
+       read_eeprom(hcsp);
+
+       if (nvramp->Revision != 1)
+               return (-1);
+
+       hcsp->HCS_SCSI_ID = nvramp->SCSI0Id;
+       hcsp->HCS_BIOS = nvramp->BIOSConfig1;
+       hcsp->HCS_MaxTar = MAX_TARGETS;
+       readBytep = (UCHAR *) & (nvramp->Target00Config);
+       for (i = 0; i < 16; readBytep++, i++) {
+               hcsp->TargetFlag[i] = *readBytep;
+               hcsp->MaximumTags[i] = ORC_MAXTAGS;
+       }                       /* for                          */
+
+       if (nvramp->SCSI0Config & NCC_BUSRESET) {       /* Reset SCSI bus               */
+               hcsp->HCS_Flags |= HCF_SCSI_RESET;
+       }
+       ORC_WR(hcsp->HCS_Base + ORC_GIMSK, 0xFB);       /* enable RP FIFO interrupt     */
+       return (0);
+}
+
+/*****************************************************************************
+ Function name  : orc_reset_scsi_bus
+ Description    : Reset registers, reset a hanging bus and
+                  kill active and disconnected commands for target w/o soft reset
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static int orc_reset_scsi_bus(ORC_HCS * pHCB)
+{                              /* I need Host Control Block Information */
+       ULONG flags;
+
+       spin_lock_irqsave(&(pHCB->BitAllocFlagLock), flags);
+
+       initAFlag(pHCB);
+       /* reset scsi bus */
+       ORC_WR(pHCB->HCS_Base + ORC_HCTRL, SCSIRST);
+       if (waitSCSIRSTdone(pHCB) == 0) {
+               spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+               return FAILED;
+       } else {
+               spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+               return SUCCESS;
+       }
+}
+
+/*****************************************************************************
+ Function name  : orc_device_reset
+ Description    : Reset registers, reset a hanging bus and
+                  kill active and disconnected commands for target w/o soft reset
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static int orc_device_reset(ORC_HCS * pHCB, struct scsi_cmnd *SCpnt, unsigned int target)
+{                              /* I need Host Control Block Information */
+       ORC_SCB *pScb;
+       ESCB *pVirEscb;
+       ORC_SCB *pVirScb;
+       UCHAR i;
+       ULONG flags;
+
+       spin_lock_irqsave(&(pHCB->BitAllocFlagLock), flags);
+       pScb = (ORC_SCB *) NULL;
+       pVirEscb = (ESCB *) NULL;
+
+       /* setup scatter list address with one buffer */
+       pVirScb = pHCB->HCS_virScbArray;
+
+       initAFlag(pHCB);
+       /* device reset */
+       for (i = 0; i < ORC_MAXQUEUE; i++) {
+               pVirEscb = pVirScb->SCB_EScb;
+               if ((pVirScb->SCB_Status) && (pVirEscb->SCB_Srb == SCpnt))
+                       break;
+               pVirScb++;
+       }
+
+       if (i == ORC_MAXQUEUE) {
+               printk("Unable to Reset - No SCB Found\n");
+               spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+               return FAILED;
+       }
+       if ((pScb = orc_alloc_scb(pHCB)) == NULL) {
+               spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+               return FAILED;
+       }
+       pScb->SCB_Opcode = ORC_BUSDEVRST;
+       pScb->SCB_Target = target;
+       pScb->SCB_HaStat = 0;
+       pScb->SCB_TaStat = 0;
+       pScb->SCB_Status = 0x0;
+       pScb->SCB_Link = 0xFF;
+       pScb->SCB_Reserved0 = 0;
+       pScb->SCB_Reserved1 = 0;
+       pScb->SCB_XferLen = 0;
+       pScb->SCB_SGLen = 0;
+
+       pVirEscb->SCB_Srb = NULL;
+       pVirEscb->SCB_Srb = SCpnt;
+       orc_exec_scb(pHCB, pScb);       /* Start execute SCB            */
+       spin_unlock_irqrestore(&(pHCB->BitAllocFlagLock), flags);
+       return SUCCESS;
+}
+
+
+/***************************************************************************/
+static ORC_SCB *__orc_alloc_scb(ORC_HCS * hcsp)
+{
+       ORC_SCB *pTmpScb;
+       UCHAR Ch;
+       ULONG idx;
+       UCHAR index;
+       UCHAR i;
+
+       Ch = hcsp->HCS_Index;
+       for (i = 0; i < 8; i++) {
+               for (index = 0; index < 32; index++) {
+                       if ((hcsp->BitAllocFlag[Ch][i] >> index) & 0x01) {
+                               hcsp->BitAllocFlag[Ch][i] &= ~(1 << index);
+                               break;
+                       }
+               }
+               idx = index + 32 * i;
+               pTmpScb = (ORC_SCB *) ((ULONG) hcsp->HCS_virScbArray + (idx * sizeof(ORC_SCB)));
+               return (pTmpScb);
+       }
+       return (NULL);
+}
+
+static ORC_SCB *orc_alloc_scb(ORC_HCS * hcsp)
+{
+       ORC_SCB *pTmpScb;
+       ULONG flags;
+
+       spin_lock_irqsave(&(hcsp->BitAllocFlagLock), flags);
+       pTmpScb = __orc_alloc_scb(hcsp);
+       spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+       return (pTmpScb);
+}
+
+
+/***************************************************************************/
+static void orc_release_scb(ORC_HCS * hcsp, ORC_SCB * scbp)
+{
+       ULONG flags;
+       UCHAR Index;
+       UCHAR i;
+       UCHAR Ch;
+
+       spin_lock_irqsave(&(hcsp->BitAllocFlagLock), flags);
+       Ch = hcsp->HCS_Index;
+       Index = scbp->SCB_ScbIdx;
+       i = Index / 32;
+       Index %= 32;
+       hcsp->BitAllocFlag[Ch][i] |= (1 << Index);
+       spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+}
+
+/*****************************************************************************
+ Function name  : abort_SCB
+ Description    : Abort a queued command.
+                        (commands that are on the bus can't be aborted easily)
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static int abort_SCB(ORC_HCS * hcsp, ORC_SCB * pScb)
+{
+       unsigned char bData, bStatus;
+
+       ORC_WR(hcsp->HCS_Base + ORC_HDATA, ORC_CMD_ABORT_SCB);  /* Write command */
+       ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+       if (waitHDOoff(hcsp) == 0)      /* Wait HDO off   */
+               return 0;
+
+       ORC_WR(hcsp->HCS_Base + ORC_HDATA, pScb->SCB_ScbIdx);   /* Write address */
+       ORC_WR(hcsp->HCS_Base + ORC_HCTRL, HDO);
+       if (waitHDOoff(hcsp) == 0)      /* Wait HDO off   */
+               return 0;
+
+       if (waitHDIset(hcsp, &bData) == 0)      /* Wait HDI set   */
+               return 0;
+       bStatus = ORC_RD(hcsp->HCS_Base, ORC_HDATA);
+       ORC_WR(hcsp->HCS_Base + ORC_HSTUS, bData);      /* Clear HDI    */
+
+       if (bStatus == 1)       /* 0 - Successfully               */
+               return 0;       /* 1 - Fail                     */
+       return 1;
+}
+
+/*****************************************************************************
+ Function name  : inia100_abort
+ Description    : Abort a queued command.
+                        (commands that are on the bus can't be aborted easily)
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static int orc_abort_srb(ORC_HCS * hcsp, struct scsi_cmnd *SCpnt)
+{
+       ESCB *pVirEscb;
+       ORC_SCB *pVirScb;
+       UCHAR i;
+       ULONG flags;
+
+       spin_lock_irqsave(&(hcsp->BitAllocFlagLock), flags);
+
+       pVirScb = hcsp->HCS_virScbArray;
+
+       for (i = 0; i < ORC_MAXQUEUE; i++, pVirScb++) {
+               pVirEscb = pVirScb->SCB_EScb;
+               if ((pVirScb->SCB_Status) && (pVirEscb->SCB_Srb == SCpnt)) {
+                       if (pVirScb->SCB_TagMsg == 0) {
+                               spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+                               return FAILED;
+                       } else {
+                               if (abort_SCB(hcsp, pVirScb)) {
+                                       pVirEscb->SCB_Srb = NULL;
+                                       spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+                                       return SUCCESS;
+                               } else {
+                                       spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+                                       return FAILED;
+                               }
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&(hcsp->BitAllocFlagLock), flags);
+       return FAILED;
+}
+
+/***********************************************************************
+ Routine Description:
+         This is the interrupt service routine for the Orchid SCSI adapter.
+         It reads the interrupt register to determine if the adapter is indeed
+         the source of the interrupt and clears the interrupt at the device.
+ Arguments:
+         HwDeviceExtension - HBA miniport driver's adapter data storage
+ Return Value:
+***********************************************************************/
+static void orc_interrupt(
+                         ORC_HCS * hcsp
+)
+{
+       BYTE bScbIdx;
+       ORC_SCB *pScb;
+
+       if (ORC_RD(hcsp->HCS_Base, ORC_RQUEUECNT) == 0) {
+               return;         // 0;
+
+       }
+       do {
+               bScbIdx = ORC_RD(hcsp->HCS_Base, ORC_RQUEUE);
+
+               pScb = (ORC_SCB *) ((ULONG) hcsp->HCS_virScbArray + (ULONG) (sizeof(ORC_SCB) * bScbIdx));
+               pScb->SCB_Status = 0x0;
+
+               inia100SCBPost((BYTE *) hcsp, (BYTE *) pScb);
+       } while (ORC_RD(hcsp->HCS_Base, ORC_RQUEUECNT));
+       return;                 //1;
+
+}                              /* End of I1060Interrupt() */
+
+/*****************************************************************************
+ Function name  : inia100BuildSCB
+ Description    : 
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static void inia100BuildSCB(ORC_HCS * pHCB, ORC_SCB * pSCB, struct scsi_cmnd * SCpnt)
+{                              /* Create corresponding SCB     */
+       struct scatterlist *pSrbSG;
+       ORC_SG *pSG;            /* Pointer to SG list           */
+       int i, count_sg;
+       ESCB *pEScb;
+
+       pEScb = pSCB->SCB_EScb;
+       pEScb->SCB_Srb = SCpnt;
+       pSG = NULL;
+
+       pSCB->SCB_Opcode = ORC_EXECSCSI;
+       pSCB->SCB_Flags = SCF_NO_DCHK;  /* Clear done bit               */
+       pSCB->SCB_Target = SCpnt->device->id;
+       pSCB->SCB_Lun = SCpnt->device->lun;
+       pSCB->SCB_Reserved0 = 0;
+       pSCB->SCB_Reserved1 = 0;
+       pSCB->SCB_SGLen = 0;
+
+       if ((pSCB->SCB_XferLen = (U32) SCpnt->request_bufflen)) {
+               pSG = (ORC_SG *) & pEScb->ESCB_SGList[0];
+               if (SCpnt->use_sg) {
+                       pSrbSG = (struct scatterlist *) SCpnt->request_buffer;
+                       count_sg = pci_map_sg(pHCB->pdev, pSrbSG, SCpnt->use_sg,
+                                       SCpnt->sc_data_direction);
+                       pSCB->SCB_SGLen = (U32) (count_sg * 8);
+                       for (i = 0; i < count_sg; i++, pSG++, pSrbSG++) {
+                               pSG->SG_Ptr = (U32) sg_dma_address(pSrbSG);
+                               pSG->SG_Len = (U32) sg_dma_len(pSrbSG);
+                       }
+               } else if (SCpnt->request_bufflen != 0) {/* Non SG */
+                       pSCB->SCB_SGLen = 0x8;
+                       SCpnt->SCp.dma_handle = pci_map_single(pHCB->pdev,
+                                       SCpnt->request_buffer,
+                                       SCpnt->request_bufflen,
+                                       SCpnt->sc_data_direction);
+                       pSG->SG_Ptr = (U32) SCpnt->SCp.dma_handle;
+                       pSG->SG_Len = (U32) SCpnt->request_bufflen;
+               } else {
+                       pSCB->SCB_SGLen = 0;
+                       pSG->SG_Ptr = 0;
+                       pSG->SG_Len = 0;
+               }
+       }
+       pSCB->SCB_SGPAddr = (U32) pSCB->SCB_SensePAddr;
+       pSCB->SCB_HaStat = 0;
+       pSCB->SCB_TaStat = 0;
+       pSCB->SCB_Link = 0xFF;
+       pSCB->SCB_SenseLen = SENSE_SIZE;
+       pSCB->SCB_CDBLen = SCpnt->cmd_len;
+       if (pSCB->SCB_CDBLen >= IMAX_CDB) {
+               printk("max cdb length= %x\b", SCpnt->cmd_len);
+               pSCB->SCB_CDBLen = IMAX_CDB;
+       }
+       pSCB->SCB_Ident = SCpnt->device->lun | DISC_ALLOW;
+       if (SCpnt->device->tagged_supported) {  /* Tag Support                  */
+               pSCB->SCB_TagMsg = SIMPLE_QUEUE_TAG;    /* Do simple tag only   */
+       } else {
+               pSCB->SCB_TagMsg = 0;   /* No tag support               */
+       }
+       memcpy(&pSCB->SCB_CDB[0], &SCpnt->cmnd, pSCB->SCB_CDBLen);
+       return;
+}
+
+/*****************************************************************************
+ Function name  : inia100_queue
+ Description    : Queue a command and setup interrupts for a free bus.
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static int inia100_queue(struct scsi_cmnd * SCpnt, void (*done) (struct scsi_cmnd *))
+{
+       register ORC_SCB *pSCB;
+       ORC_HCS *pHCB;          /* Point to Host adapter control block */
+
+       pHCB = (ORC_HCS *) SCpnt->device->host->hostdata;
+       SCpnt->scsi_done = done;
+       /* Get free SCSI control block  */
+       if ((pSCB = orc_alloc_scb(pHCB)) == NULL)
+               return SCSI_MLQUEUE_HOST_BUSY;
+
+       inia100BuildSCB(pHCB, pSCB, SCpnt);
+       orc_exec_scb(pHCB, pSCB);       /* Start execute SCB            */
+
+       return (0);
+}
+
+/*****************************************************************************
+ Function name  : inia100_abort
+ Description    : Abort a queued command.
+                        (commands that are on the bus can't be aborted easily)
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static int inia100_abort(struct scsi_cmnd * SCpnt)
+{
+       ORC_HCS *hcsp;
+
+       hcsp = (ORC_HCS *) SCpnt->device->host->hostdata;
+       return orc_abort_srb(hcsp, SCpnt);
+}
+
+/*****************************************************************************
+ Function name  : inia100_reset
+ Description    : Reset registers, reset a hanging bus and
+                  kill active and disconnected commands for target w/o soft reset
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static int inia100_bus_reset(struct scsi_cmnd * SCpnt)
+{                              /* I need Host Control Block Information */
+       ORC_HCS *pHCB;
+       pHCB = (ORC_HCS *) SCpnt->device->host->hostdata;
+       return orc_reset_scsi_bus(pHCB);
+}
+
+/*****************************************************************************
+ Function name  : inia100_device_reset
+ Description    : Reset the device
+ Input          : pHCB  -       Pointer to host adapter structure
+ Output         : None.
+ Return         : pSRB  -       Pointer to SCSI request block.
+*****************************************************************************/
+static int inia100_device_reset(struct scsi_cmnd * SCpnt)
+{                              /* I need Host Control Block Information */
+       ORC_HCS *pHCB;
+       pHCB = (ORC_HCS *) SCpnt->device->host->hostdata;
+       return orc_device_reset(pHCB, SCpnt, SCpnt->device->id);
+
+}
+
+/*****************************************************************************
+ Function name  : inia100SCBPost
+ Description    : This is callback routine be called when orc finish one
+                       SCSI command.
+ Input          : pHCB  -       Pointer to host adapter control block.
+                 pSCB  -       Pointer to SCSI control block.
+ Output         : None.
+ Return         : None.
+*****************************************************************************/
+static void inia100SCBPost(BYTE * pHcb, BYTE * pScb)
+{
+       struct scsi_cmnd *pSRB; /* Pointer to SCSI request block */
+       ORC_HCS *pHCB;
+       ORC_SCB *pSCB;
+       ESCB *pEScb;
+
+       pHCB = (ORC_HCS *) pHcb;
+       pSCB = (ORC_SCB *) pScb;
+       pEScb = pSCB->SCB_EScb;
+       if ((pSRB = (struct scsi_cmnd *) pEScb->SCB_Srb) == 0) {
+               printk("inia100SCBPost: SRB pointer is empty\n");
+               orc_release_scb(pHCB, pSCB);    /* Release SCB for current channel */
+               return;
+       }
+       pEScb->SCB_Srb = NULL;
+
+       switch (pSCB->SCB_HaStat) {
+       case 0x0:
+       case 0xa:               /* Linked command complete without error and linked normally */
+       case 0xb:               /* Linked command complete without error interrupt generated */
+               pSCB->SCB_HaStat = 0;
+               break;
+
+       case 0x11:              /* Selection time out-The initiator selection or target
+                                  reselection was not complete within the SCSI Time out period */
+               pSCB->SCB_HaStat = DID_TIME_OUT;
+               break;
+
+       case 0x14:              /* Target bus phase sequence failure-An invalid bus phase or bus
+                                  phase sequence was requested by the target. The host adapter
+                                  will generate a SCSI Reset Condition, notifying the host with
+                                  a SCRD interrupt */
+               pSCB->SCB_HaStat = DID_RESET;
+               break;
+
+       case 0x1a:              /* SCB Aborted. 07/21/98 */
+               pSCB->SCB_HaStat = DID_ABORT;
+               break;
+
+       case 0x12:              /* Data overrun/underrun-The target attempted to transfer more data
+                                  than was allocated by the Data Length field or the sum of the
+                                  Scatter / Gather Data Length fields. */
+       case 0x13:              /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+       case 0x16:              /* Invalid CCB Operation Code-The first byte of the CCB was invalid. */
+
+       default:
+               printk("inia100: %x %x\n", pSCB->SCB_HaStat, pSCB->SCB_TaStat);
+               pSCB->SCB_HaStat = DID_ERROR;   /* Couldn't find any better */
+               break;
+       }
+
+       if (pSCB->SCB_TaStat == 2) {    /* Check condition              */
+               memcpy((unsigned char *) &pSRB->sense_buffer[0],
+                  (unsigned char *) &pEScb->ESCB_SGList[0], SENSE_SIZE);
+       }
+       pSRB->result = pSCB->SCB_TaStat | (pSCB->SCB_HaStat << 16);
+
+       if (pSRB->use_sg) {
+               pci_unmap_sg(pHCB->pdev,
+                            (struct scatterlist *)pSRB->request_buffer,
+                            pSRB->use_sg, pSRB->sc_data_direction);
+       } else if (pSRB->request_bufflen != 0) {
+               pci_unmap_single(pHCB->pdev, pSRB->SCp.dma_handle,
+                                pSRB->request_bufflen,
+                                pSRB->sc_data_direction);
+       }
+
+       pSRB->scsi_done(pSRB);  /* Notify system DONE           */
+
+       orc_release_scb(pHCB, pSCB);    /* Release SCB for current channel */
+}
+
+/*
+ * Interrupt handler (main routine of the driver)
+ */
+static irqreturn_t inia100_intr(int irqno, void *devid, struct pt_regs *regs)
+{
+       struct Scsi_Host *host = (struct Scsi_Host *)devid;
+       ORC_HCS *pHcb = (ORC_HCS *)host->hostdata;
+       unsigned long flags;
+
+       spin_lock_irqsave(host->host_lock, flags);
+       orc_interrupt(pHcb);
+       spin_unlock_irqrestore(host->host_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+static struct scsi_host_template inia100_template = {
+       .proc_name              = "inia100",
+       .name                   = inia100_REVID,
+       .queuecommand           = inia100_queue,
+       .eh_abort_handler       = inia100_abort,
+       .eh_bus_reset_handler   = inia100_bus_reset,
+       .eh_device_reset_handler = inia100_device_reset,
+       .can_queue              = 1,
+       .this_id                = 1,
+       .sg_tablesize           = SG_ALL,
+       .cmd_per_lun            = 1,
+       .use_clustering         = ENABLE_CLUSTERING,
+};
+
+static int __devinit inia100_probe_one(struct pci_dev *pdev,
+               const struct pci_device_id *id)
+{
+       struct Scsi_Host *shost;
+       ORC_HCS *pHCB;
+       unsigned long port, bios;
+       int error = -ENODEV;
+       u32 sz;
+       unsigned long dBiosAdr;
+       char *pbBiosAdr;
+
+       if (pci_enable_device(pdev))
+               goto out;
+       if (pci_set_dma_mask(pdev, 0xffffffffULL)) {
+               printk(KERN_WARNING "Unable to set 32bit DMA "
+                                   "on inia100 adapter, ignoring.\n");
+               goto out_disable_device;
+       }
+
+       pci_set_master(pdev);
+
+       port = pci_resource_start(pdev, 0);
+       if (!request_region(port, 256, "inia100")) {
+               printk(KERN_WARNING "inia100: io port 0x%lx, is busy.\n", port);
+               goto out_disable_device;
+       }
+
+       /* <02> read from base address + 0x50 offset to get the bios balue. */
+       bios = ORC_RDWORD(port, 0x50);
+
+
+       shost = scsi_host_alloc(&inia100_template, sizeof(ORC_HCS));
+       if (!shost)
+               goto out_release_region;
+
+       pHCB = (ORC_HCS *)shost->hostdata;
+       pHCB->pdev = pdev;
+       pHCB->HCS_Base = port;
+       pHCB->HCS_BIOS = bios;
+       spin_lock_init(&pHCB->BitAllocFlagLock);
+
+       /* Get total memory needed for SCB */
+       sz = ORC_MAXQUEUE * sizeof(ORC_SCB);
+       pHCB->HCS_virScbArray = pci_alloc_consistent(pdev, sz,
+                       &pHCB->HCS_physScbArray);
+       if (!pHCB->HCS_virScbArray) {
+               printk("inia100: SCB memory allocation error\n");
+               goto out_host_put;
+       }
+       memset(pHCB->HCS_virScbArray, 0, sz);
+
+       /* Get total memory needed for ESCB */
+       sz = ORC_MAXQUEUE * sizeof(ESCB);
+       pHCB->HCS_virEscbArray = pci_alloc_consistent(pdev, sz,
+                       &pHCB->HCS_physEscbArray);
+       if (!pHCB->HCS_virEscbArray) {
+               printk("inia100: ESCB memory allocation error\n");
+               goto out_free_scb_array;
+       }
+       memset(pHCB->HCS_virEscbArray, 0, sz);
+
+       dBiosAdr = pHCB->HCS_BIOS;
+       dBiosAdr = (dBiosAdr << 4);
+       pbBiosAdr = phys_to_virt(dBiosAdr);
+       if (init_orchid(pHCB)) {        /* Initialize orchid chip */
+               printk("inia100: initial orchid fail!!\n");
+               goto out_free_escb_array;
+       }
+
+       shost->io_port = pHCB->HCS_Base;
+       shost->n_io_port = 0xff;
+       shost->can_queue = ORC_MAXQUEUE;
+       shost->unique_id = shost->io_port;
+       shost->max_id = pHCB->HCS_MaxTar;
+       shost->max_lun = 16;
+       shost->irq = pHCB->HCS_Intr = pdev->irq;
+       shost->this_id = pHCB->HCS_SCSI_ID;     /* Assign HCS index */
+       shost->sg_tablesize = TOTAL_SG_ENTRY;
+
+       /* Initial orc chip           */
+       error = request_irq(pdev->irq, inia100_intr, SA_SHIRQ,
+                       "inia100", shost);
+       if (error < 0) {
+               printk(KERN_WARNING "inia100: unable to get irq %d\n",
+                               pdev->irq);
+               goto out_free_escb_array;
+       }
+
+       pci_set_drvdata(pdev, shost);
+
+       error = scsi_add_host(shost, &pdev->dev);
+       if (error)
+               goto out_free_irq;
+
+       scsi_scan_host(shost);
+       return 0;
+
+ out_free_irq:
+        free_irq(shost->irq, shost);
+ out_free_escb_array:
+       pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(ESCB),
+                       pHCB->HCS_virEscbArray, pHCB->HCS_physEscbArray);
+ out_free_scb_array:
+       pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(ORC_SCB),
+                       pHCB->HCS_virScbArray, pHCB->HCS_physScbArray);
+ out_host_put:
+       scsi_host_put(shost);
+ out_release_region:
+        release_region(port, 256);
+ out_disable_device:
+       pci_disable_device(pdev);
+ out:
+       return error;
+}
+
+static void __devexit inia100_remove_one(struct pci_dev *pdev)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       ORC_HCS *pHCB = (ORC_HCS *)shost->hostdata;
+
+       scsi_remove_host(shost);
+
+        free_irq(shost->irq, shost);
+       pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(ESCB),
+                       pHCB->HCS_virEscbArray, pHCB->HCS_physEscbArray);
+       pci_free_consistent(pdev, ORC_MAXQUEUE * sizeof(ORC_SCB),
+                       pHCB->HCS_virScbArray, pHCB->HCS_physScbArray);
+        release_region(shost->io_port, 256);
+
+       scsi_host_put(shost);
+} 
+
+static struct pci_device_id inia100_pci_tbl[] = {
+       {PCI_VENDOR_ID_INIT, 0x1060, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0,}
+};
+MODULE_DEVICE_TABLE(pci, inia100_pci_tbl);
+
+static struct pci_driver inia100_pci_driver = {
+       .name           = "inia100",
+       .id_table       = inia100_pci_tbl,
+       .probe          = inia100_probe_one,
+       .remove         = __devexit_p(inia100_remove_one),
+};
+
+static int __init inia100_init(void)
+{
+       return pci_module_init(&inia100_pci_driver);
+}
+
+static void __exit inia100_exit(void)
+{
+       pci_unregister_driver(&inia100_pci_driver);
+}
+
+MODULE_DESCRIPTION("Initio A100U2W SCSI driver");
+MODULE_AUTHOR("Initio Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
+
+module_init(inia100_init);
+module_exit(inia100_exit);
diff --git a/drivers/scsi/a100u2w.h b/drivers/scsi/a100u2w.h
new file mode 100644 (file)
index 0000000..6f542d2
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Initio A100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * 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, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+/*
+ * Revision History:
+ * 06/18/98 HL, Initial production Version 1.02
+ * 12/19/98 bv, Use spinlocks for 2.1.95 and up
+ * 06/25/02 Doug Ledford <dledford@redhat.com>
+ *      - This and the i60uscsi.h file are almost identical,
+ *        merged them into a single header used by both .c files.
+ */
+
+#define inia100_REVID "Initio INI-A100U2W SCSI device driver; Revision: 1.02d"
+
+#define ULONG   unsigned long
+#define USHORT  unsigned short
+#define UCHAR   unsigned char
+#define BYTE    unsigned char
+#define WORD    unsigned short
+#define DWORD   unsigned long
+#define UBYTE   unsigned char
+#define UWORD   unsigned short
+#define UDWORD  unsigned long
+#define U32     u32
+
+#if 1
+#define ORC_MAXQUEUE           245
+#define ORC_MAXTAGS            64
+#else
+#define ORC_MAXQUEUE           25
+#define ORC_MAXTAGS            8
+#endif
+
+#define TOTAL_SG_ENTRY         32
+#define MAX_TARGETS            16
+#define IMAX_CDB                       15
+#define SENSE_SIZE             14
+
+/************************************************************************/
+/*              Scatter-Gather Element Structure                        */
+/************************************************************************/
+typedef struct ORC_SG_Struc {
+       U32 SG_Ptr;             /* Data Pointer */
+       U32 SG_Len;             /* Data Length */
+} ORC_SG;
+
+/* SCSI related definition                                              */
+#define DISC_NOT_ALLOW          0x80   /* Disconnect is not allowed    */
+#define DISC_ALLOW              0xC0   /* Disconnect is allowed        */
+
+
+#define ORC_OFFSET_SCB                 16
+#define ORC_MAX_SCBS               250
+#define MAX_CHANNELS       2
+#define MAX_ESCB_ELE                           64
+#define TCF_DRV_255_63     0x0400
+
+/********************************************************/
+/*      Orchid Host Command Set                         */
+/********************************************************/
+#define ORC_CMD_NOP            0x00    /* Host command - NOP             */
+#define ORC_CMD_VERSION                0x01    /* Host command - Get F/W version */
+#define ORC_CMD_ECHO           0x02    /* Host command - ECHO            */
+#define ORC_CMD_SET_NVM                0x03    /* Host command - Set NVRAM       */
+#define ORC_CMD_GET_NVM                0x04    /* Host command - Get NVRAM       */
+#define ORC_CMD_GET_BUS_STATUS 0x05    /* Host command - Get SCSI bus status */
+#define ORC_CMD_ABORT_SCB      0x06    /* Host command - Abort SCB       */
+#define ORC_CMD_ISSUE_SCB      0x07    /* Host command - Issue SCB       */
+
+/********************************************************/
+/*              Orchid Register Set                     */
+/********************************************************/
+#define ORC_GINTS      0xA0    /* Global Interrupt Status        */
+#define QINT           0x04    /* Reply Queue Interrupt  */
+#define ORC_GIMSK      0xA1    /* Global Interrupt MASK  */
+#define MQINT          0x04    /* Mask Reply Queue Interrupt     */
+#define        ORC_GCFG        0xA2    /* Global Configure               */
+#define EEPRG          0x01    /* Enable EEPROM programming */
+#define        ORC_GSTAT       0xA3    /* Global status          */
+#define WIDEBUS                0x10    /* Wide SCSI Devices connected    */
+#define ORC_HDATA      0xA4    /* Host Data                      */
+#define ORC_HCTRL      0xA5    /* Host Control                   */
+#define SCSIRST                0x80    /* SCSI bus reset         */
+#define HDO                    0x40    /* Host data out          */
+#define HOSTSTOP               0x02    /* Host stop RISC engine  */
+#define DEVRST         0x01    /* Device reset                   */
+#define ORC_HSTUS      0xA6    /* Host Status                    */
+#define HDI                    0x02    /* Host data in                   */
+#define RREADY         0x01    /* RISC engine is ready to receive */
+#define        ORC_NVRAM       0xA7    /* Nvram port address             */
+#define SE2CS          0x008
+#define SE2CLK         0x004
+#define SE2DO          0x002
+#define SE2DI          0x001
+#define ORC_PQUEUE     0xA8    /* Posting queue FIFO             */
+#define ORC_PQCNT      0xA9    /* Posting queue FIFO Cnt */
+#define ORC_RQUEUE     0xAA    /* Reply queue FIFO               */
+#define ORC_RQUEUECNT  0xAB    /* Reply queue FIFO Cnt           */
+#define        ORC_FWBASEADR   0xAC    /* Firmware base address  */
+
+#define        ORC_EBIOSADR0 0xB0      /* External Bios address */
+#define        ORC_EBIOSADR1 0xB1      /* External Bios address */
+#define        ORC_EBIOSADR2 0xB2      /* External Bios address */
+#define        ORC_EBIOSDATA 0xB3      /* External Bios address */
+
+#define        ORC_SCBSIZE     0xB7    /* SCB size register              */
+#define        ORC_SCBBASE0    0xB8    /* SCB base address 0             */
+#define        ORC_SCBBASE1    0xBC    /* SCB base address 1             */
+
+#define        ORC_RISCCTL     0xE0    /* RISC Control                   */
+#define PRGMRST                0x002
+#define DOWNLOAD               0x001
+#define        ORC_PRGMCTR0    0xE2    /* RISC program counter           */
+#define        ORC_PRGMCTR1    0xE3    /* RISC program counter           */
+#define        ORC_RISCRAM     0xEC    /* RISC RAM data port 4 bytes     */
+
+typedef struct orc_extended_scb {      /* Extended SCB                 */
+       ORC_SG ESCB_SGList[TOTAL_SG_ENTRY];     /*0 Start of SG list              */
+       struct scsi_cmnd *SCB_Srb;      /*50 SRB Pointer */
+} ESCB;
+
+/***********************************************************************
+               SCSI Control Block
+************************************************************************/
+typedef struct orc_scb {       /* Scsi_Ctrl_Blk                */
+       UBYTE SCB_Opcode;       /*00 SCB command code&residual  */
+       UBYTE SCB_Flags;        /*01 SCB Flags                  */
+       UBYTE SCB_Target;       /*02 Target Id                  */
+       UBYTE SCB_Lun;          /*03 Lun                        */
+       U32 SCB_Reserved0;      /*04 Reserved for ORCHID must 0 */
+       U32 SCB_XferLen;        /*08 Data Transfer Length       */
+       U32 SCB_Reserved1;      /*0C Reserved for ORCHID must 0 */
+       U32 SCB_SGLen;          /*10 SG list # * 8              */
+       U32 SCB_SGPAddr;        /*14 SG List Buf physical Addr  */
+       U32 SCB_SGPAddrHigh;    /*18 SG Buffer high physical Addr */
+       UBYTE SCB_HaStat;       /*1C Host Status                */
+       UBYTE SCB_TaStat;       /*1D Target Status              */
+       UBYTE SCB_Status;       /*1E SCB status                 */
+       UBYTE SCB_Link;         /*1F Link pointer, default 0xFF */
+       UBYTE SCB_SenseLen;     /*20 Sense Allocation Length    */
+       UBYTE SCB_CDBLen;       /*21 CDB Length                 */
+       UBYTE SCB_Ident;        /*22 Identify                   */
+       UBYTE SCB_TagMsg;       /*23 Tag Message                */
+       UBYTE SCB_CDB[IMAX_CDB];        /*24 SCSI CDBs                  */
+       UBYTE SCB_ScbIdx;       /*3C Index for this ORCSCB      */
+       U32 SCB_SensePAddr;     /*34 Sense Buffer physical Addr */
+
+       ESCB *SCB_EScb;         /*38 Extended SCB Pointer       */
+#ifndef ALPHA
+       UBYTE SCB_Reserved2[4]; /*3E Reserved for Driver use    */
+#endif
+} ORC_SCB;
+
+/* Opcodes of ORCSCB_Opcode */
+#define ORC_EXECSCSI   0x00    /* SCSI initiator command with residual */
+#define ORC_BUSDEVRST  0x01    /* SCSI Bus Device Reset  */
+
+/* Status of ORCSCB_Status */
+#define ORCSCB_COMPLETE        0x00    /* SCB request completed  */
+#define ORCSCB_POST    0x01    /* SCB is posted by the HOST      */
+
+/* Bit Definition for ORCSCB_Flags */
+#define SCF_DISINT     0x01    /* Disable HOST interrupt */
+#define SCF_DIR                0x18    /* Direction bits         */
+#define SCF_NO_DCHK    0x00    /* Direction determined by SCSI   */
+#define SCF_DIN                0x08    /* From Target to Initiator       */
+#define SCF_DOUT       0x10    /* From Initiator to Target       */
+#define SCF_NO_XF      0x18    /* No data transfer               */
+#define SCF_POLL   0x40
+
+/* Error Codes for ORCSCB_HaStat */
+#define HOST_SEL_TOUT  0x11
+#define HOST_DO_DU     0x12
+#define HOST_BUS_FREE  0x13
+#define HOST_BAD_PHAS  0x14
+#define HOST_INV_CMD   0x16
+#define HOST_SCSI_RST  0x1B
+#define HOST_DEV_RST   0x1C
+
+
+/* Error Codes for ORCSCB_TaStat */
+#define TARGET_CHK_COND        0x02
+#define TARGET_BUSY    0x08
+#define TARGET_TAG_FULL        0x28
+
+
+/***********************************************************************
+               Target Device Control Structure
+**********************************************************************/
+
+typedef struct ORC_Tar_Ctrl_Struc {
+       UBYTE TCS_DrvDASD;      /* 6 */
+       UBYTE TCS_DrvSCSI;      /* 7 */
+       UBYTE TCS_DrvHead;      /* 8 */
+       UWORD TCS_DrvFlags;     /* 4 */
+       UBYTE TCS_DrvSector;    /* 7 */
+} ORC_TCS;
+
+/* Bit Definition for TCF_DrvFlags */
+#define        TCS_DF_NODASD_SUPT      0x20    /* Suppress OS/2 DASD Mgr support */
+#define        TCS_DF_NOSCSI_SUPT      0x40    /* Suppress OS/2 SCSI Mgr support */
+
+
+/***********************************************************************
+              Host Adapter Control Structure
+************************************************************************/
+typedef struct ORC_Ha_Ctrl_Struc {
+       USHORT HCS_Base;        /* 00 */
+       UBYTE HCS_Index;        /* 02 */
+       UBYTE HCS_Intr;         /* 04 */
+       UBYTE HCS_SCSI_ID;      /* 06    H/A SCSI ID */
+       UBYTE HCS_BIOS;         /* 07    BIOS configuration */
+
+       UBYTE HCS_Flags;        /* 0B */
+       UBYTE HCS_HAConfig1;    /* 1B    SCSI0MAXTags */
+       UBYTE HCS_MaxTar;       /* 1B    SCSI0MAXTags */
+
+       USHORT HCS_Units;       /* Number of units this adapter  */
+       USHORT HCS_AFlags;      /* Adapter info. defined flags   */
+       ULONG HCS_Timeout;      /* Adapter timeout value   */
+       ORC_SCB *HCS_virScbArray;       /* 28 Virtual Pointer to SCB array */
+       dma_addr_t HCS_physScbArray;    /* Scb Physical address */
+       ESCB *HCS_virEscbArray; /* Virtual pointer to ESCB Scatter list */
+       dma_addr_t HCS_physEscbArray;   /* scatter list Physical address */
+       UBYTE TargetFlag[16];   /* 30  target configuration, TCF_EN_TAG */
+       UBYTE MaximumTags[16];  /* 40  ORC_MAX_SCBS */
+       UBYTE ActiveTags[16][16];       /* 50 */
+       ORC_TCS HCS_Tcs[16];    /* 28 */
+       U32 BitAllocFlag[MAX_CHANNELS][8];      /* Max STB is 256, So 256/32 */
+       spinlock_t BitAllocFlagLock;
+       struct pci_dev *pdev;
+} ORC_HCS;
+
+/* Bit Definition for HCS_Flags */
+
+#define HCF_SCSI_RESET 0x01    /* SCSI BUS RESET         */
+#define HCF_PARITY     0x02    /* parity card                    */
+#define HCF_LVDS       0x10    /* parity card                    */
+
+/* Bit Definition for TargetFlag */
+
+#define TCF_EN_255         0x08
+#define TCF_EN_TAG         0x10
+#define TCF_BUSY             0x20
+#define TCF_DISCONNECT 0x40
+#define TCF_SPIN_UP      0x80
+
+/* Bit Definition for HCS_AFlags */
+#define        HCS_AF_IGNORE           0x01    /* Adapter ignore         */
+#define        HCS_AF_DISABLE_RESET    0x10    /* Adapter disable reset  */
+#define        HCS_AF_DISABLE_ADPT     0x80    /* Adapter disable                */
+
+typedef struct _NVRAM {
+/*----------header ---------------*/
+        UCHAR SubVendorID0;     /* 00 - Sub Vendor ID           */
+        UCHAR SubVendorID1;     /* 00 - Sub Vendor ID           */
+        UCHAR SubSysID0;        /* 02 - Sub System ID           */
+        UCHAR SubSysID1;        /* 02 - Sub System ID           */
+        UCHAR SubClass;         /* 04 - Sub Class               */
+        UCHAR VendorID0;        /* 05 - Vendor ID               */
+        UCHAR VendorID1;        /* 05 - Vendor ID               */
+        UCHAR DeviceID0;        /* 07 - Device ID               */
+        UCHAR DeviceID1;        /* 07 - Device ID               */
+        UCHAR Reserved0[2];     /* 09 - Reserved                */
+        UCHAR Revision;         /* 0B - Revision of data structure */
+        /* ----Host Adapter Structure ---- */
+        UCHAR NumOfCh;          /* 0C - Number of SCSI channel  */
+        UCHAR BIOSConfig1;      /* 0D - BIOS configuration 1    */
+        UCHAR BIOSConfig2;      /* 0E - BIOS boot channel&target ID */
+        UCHAR BIOSConfig3;      /* 0F - BIOS configuration 3    */
+        /* ----SCSI channel Structure ---- */
+        /* from "CTRL-I SCSI Host Adapter SetUp menu "  */
+        UCHAR SCSI0Id;          /* 10 - Channel 0 SCSI ID       */
+        UCHAR SCSI0Config;      /* 11 - Channel 0 SCSI configuration */
+        UCHAR SCSI0MaxTags;     /* 12 - Channel 0 Maximum tags  */
+        UCHAR SCSI0ResetTime;   /* 13 - Channel 0 Reset recovering time */
+        UCHAR ReservedforChannel0[2];   /* 14 - Reserved                */
+
+        /* ----SCSI target Structure ----  */
+        /* from "CTRL-I SCSI device SetUp menu "                        */
+        UCHAR Target00Config;   /* 16 - Channel 0 Target 0 config */
+        UCHAR Target01Config;   /* 17 - Channel 0 Target 1 config */
+        UCHAR Target02Config;   /* 18 - Channel 0 Target 2 config */
+        UCHAR Target03Config;   /* 19 - Channel 0 Target 3 config */
+        UCHAR Target04Config;   /* 1A - Channel 0 Target 4 config */
+        UCHAR Target05Config;   /* 1B - Channel 0 Target 5 config */
+        UCHAR Target06Config;   /* 1C - Channel 0 Target 6 config */
+        UCHAR Target07Config;   /* 1D - Channel 0 Target 7 config */
+        UCHAR Target08Config;   /* 1E - Channel 0 Target 8 config */
+        UCHAR Target09Config;   /* 1F - Channel 0 Target 9 config */
+        UCHAR Target0AConfig;   /* 20 - Channel 0 Target A config */
+        UCHAR Target0BConfig;   /* 21 - Channel 0 Target B config */
+        UCHAR Target0CConfig;   /* 22 - Channel 0 Target C config */
+        UCHAR Target0DConfig;   /* 23 - Channel 0 Target D config */
+        UCHAR Target0EConfig;   /* 24 - Channel 0 Target E config */
+        UCHAR Target0FConfig;   /* 25 - Channel 0 Target F config */
+
+        UCHAR SCSI1Id;          /* 26 - Channel 1 SCSI ID       */
+        UCHAR SCSI1Config;      /* 27 - Channel 1 SCSI configuration */
+        UCHAR SCSI1MaxTags;     /* 28 - Channel 1 Maximum tags  */
+        UCHAR SCSI1ResetTime;   /* 29 - Channel 1 Reset recovering time */
+        UCHAR ReservedforChannel1[2];   /* 2A - Reserved                */
+
+        /* ----SCSI target Structure ----  */
+        /* from "CTRL-I SCSI device SetUp menu "                                          */
+        UCHAR Target10Config;   /* 2C - Channel 1 Target 0 config */
+        UCHAR Target11Config;   /* 2D - Channel 1 Target 1 config */
+        UCHAR Target12Config;   /* 2E - Channel 1 Target 2 config */
+        UCHAR Target13Config;   /* 2F - Channel 1 Target 3 config */
+        UCHAR Target14Config;   /* 30 - Channel 1 Target 4 config */
+        UCHAR Target15Config;   /* 31 - Channel 1 Target 5 config */
+        UCHAR Target16Config;   /* 32 - Channel 1 Target 6 config */
+        UCHAR Target17Config;   /* 33 - Channel 1 Target 7 config */
+        UCHAR Target18Config;   /* 34 - Channel 1 Target 8 config */
+        UCHAR Target19Config;   /* 35 - Channel 1 Target 9 config */
+        UCHAR Target1AConfig;   /* 36 - Channel 1 Target A config */
+        UCHAR Target1BConfig;   /* 37 - Channel 1 Target B config */
+        UCHAR Target1CConfig;   /* 38 - Channel 1 Target C config */
+        UCHAR Target1DConfig;   /* 39 - Channel 1 Target D config */
+        UCHAR Target1EConfig;   /* 3A - Channel 1 Target E config */
+        UCHAR Target1FConfig;   /* 3B - Channel 1 Target F config */
+        UCHAR reserved[3];      /* 3C - Reserved                */
+        /* ---------- CheckSum ----------       */
+        UCHAR CheckSum;         /* 3F - Checksum of NVRam       */
+} NVRAM, *PNVRAM;
+
+/* Bios Configuration for nvram->BIOSConfig1                            */
+#define NBC_BIOSENABLE  0x01    /* BIOS enable                    */
+#define NBC_CDROM       0x02    /* Support bootable CDROM */
+#define NBC_REMOVABLE   0x04    /* Support removable drive        */
+
+/* Bios Configuration for nvram->BIOSConfig2                            */
+#define NBB_TARGET_MASK 0x0F    /* Boot SCSI target ID number     */
+#define NBB_CHANL_MASK  0xF0    /* Boot SCSI channel number       */
+
+/* Bit definition for nvram->SCSIConfig                                 */
+#define NCC_BUSRESET    0x01    /* Reset SCSI bus at power up     */
+#define NCC_PARITYCHK   0x02    /* SCSI parity enable             */
+#define NCC_LVDS        0x10    /* Enable LVDS                    */
+#define NCC_ACTTERM1    0x20    /* Enable active terminator 1     */
+#define NCC_ACTTERM2    0x40    /* Enable active terminator 2     */
+#define NCC_AUTOTERM    0x80    /* Enable auto termination        */
+
+/* Bit definition for nvram->TargetxConfig                              */
+#define NTC_PERIOD      0x07    /* Maximum Sync. Speed            */
+#define NTC_1GIGA       0x08    /* 255 head / 63 sectors (64/32) */
+#define NTC_NO_SYNC     0x10    /* NO SYNC. NEGO          */
+#define NTC_NO_WIDESYNC 0x20    /* NO WIDE SYNC. NEGO             */
+#define NTC_DISC_ENABLE 0x40    /* Enable SCSI disconnect */
+#define NTC_SPINUP      0x80    /* Start disk drive               */
+
+/* Default NVRam values                                                 */
+#define NBC_DEFAULT     (NBC_ENABLE)
+#define NCC_DEFAULT     (NCC_BUSRESET | NCC_AUTOTERM | NCC_PARITYCHK)
+#define NCC_MAX_TAGS    0x20    /* Maximum tags per target        */
+#define NCC_RESET_TIME  0x0A    /* SCSI RESET recovering time     */
+#define NTC_DEFAULT     (NTC_1GIGA | NTC_NO_WIDESYNC | NTC_DISC_ENABLE)
+
+#define ORC_RD(x,y)             (UCHAR)(inb(  (int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+#define ORC_RDWORD(x,y)         (short)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+#define ORC_RDLONG(x,y)         (long)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+
+#define ORC_WR(     adr,data)   outb( (UCHAR)(data), (int)(adr))
+#define ORC_WRSHORT(adr,data)   outw( (UWORD)(data), (int)(adr))
+#define ORC_WRLONG( adr,data)   outl( (ULONG)(data), (int)(adr))
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
new file mode 100644 (file)
index 0000000..8dd5d1f
--- /dev/null
@@ -0,0 +1,1045 @@
+/*
+ *  ahci.c - AHCI SATA support
+ *
+ *  Copyright 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.
+ *
+ * Version 1.0 of the AHCI specification:
+ * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/io.h>
+
+#define DRV_NAME       "ahci"
+#define DRV_VERSION    "1.00"
+
+
+enum {
+       AHCI_PCI_BAR            = 5,
+       AHCI_MAX_SG             = 168, /* hardware max is 64K */
+       AHCI_DMA_BOUNDARY       = 0xffffffff,
+       AHCI_USE_CLUSTERING     = 0,
+       AHCI_CMD_SLOT_SZ        = 32 * 32,
+       AHCI_RX_FIS_SZ          = 256,
+       AHCI_CMD_TBL_HDR        = 0x80,
+       AHCI_CMD_TBL_SZ         = AHCI_CMD_TBL_HDR + (AHCI_MAX_SG * 16),
+       AHCI_PORT_PRIV_DMA_SZ   = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_SZ +
+                                 AHCI_RX_FIS_SZ,
+       AHCI_IRQ_ON_SG          = (1 << 31),
+       AHCI_CMD_ATAPI          = (1 << 5),
+       AHCI_CMD_WRITE          = (1 << 6),
+
+       RX_FIS_D2H_REG          = 0x40, /* offset of D2H Register FIS data */
+
+       board_ahci              = 0,
+
+       /* global controller registers */
+       HOST_CAP                = 0x00, /* host capabilities */
+       HOST_CTL                = 0x04, /* global host control */
+       HOST_IRQ_STAT           = 0x08, /* interrupt status */
+       HOST_PORTS_IMPL         = 0x0c, /* bitmap of implemented ports */
+       HOST_VERSION            = 0x10, /* AHCI spec. version compliancy */
+
+       /* HOST_CTL bits */
+       HOST_RESET              = (1 << 0),  /* reset controller; self-clear */
+       HOST_IRQ_EN             = (1 << 1),  /* global IRQ enable */
+       HOST_AHCI_EN            = (1 << 31), /* AHCI enabled */
+
+       /* HOST_CAP bits */
+       HOST_CAP_64             = (1 << 31), /* PCI DAC (64-bit DMA) support */
+
+       /* registers for each SATA port */
+       PORT_LST_ADDR           = 0x00, /* command list DMA addr */
+       PORT_LST_ADDR_HI        = 0x04, /* command list DMA addr hi */
+       PORT_FIS_ADDR           = 0x08, /* FIS rx buf addr */
+       PORT_FIS_ADDR_HI        = 0x0c, /* FIS rx buf addr hi */
+       PORT_IRQ_STAT           = 0x10, /* interrupt status */
+       PORT_IRQ_MASK           = 0x14, /* interrupt enable/disable mask */
+       PORT_CMD                = 0x18, /* port command */
+       PORT_TFDATA             = 0x20, /* taskfile data */
+       PORT_SIG                = 0x24, /* device TF signature */
+       PORT_CMD_ISSUE          = 0x38, /* command issue */
+       PORT_SCR                = 0x28, /* SATA phy register block */
+       PORT_SCR_STAT           = 0x28, /* SATA phy register: SStatus */
+       PORT_SCR_CTL            = 0x2c, /* SATA phy register: SControl */
+       PORT_SCR_ERR            = 0x30, /* SATA phy register: SError */
+       PORT_SCR_ACT            = 0x34, /* SATA phy register: SActive */
+
+       /* PORT_IRQ_{STAT,MASK} bits */
+       PORT_IRQ_COLD_PRES      = (1 << 31), /* cold presence detect */
+       PORT_IRQ_TF_ERR         = (1 << 30), /* task file error */
+       PORT_IRQ_HBUS_ERR       = (1 << 29), /* host bus fatal error */
+       PORT_IRQ_HBUS_DATA_ERR  = (1 << 28), /* host bus data error */
+       PORT_IRQ_IF_ERR         = (1 << 27), /* interface fatal error */
+       PORT_IRQ_IF_NONFATAL    = (1 << 26), /* interface non-fatal error */
+       PORT_IRQ_OVERFLOW       = (1 << 24), /* xfer exhausted available S/G */
+       PORT_IRQ_BAD_PMP        = (1 << 23), /* incorrect port multiplier */
+
+       PORT_IRQ_PHYRDY         = (1 << 22), /* PhyRdy changed */
+       PORT_IRQ_DEV_ILCK       = (1 << 7), /* device interlock */
+       PORT_IRQ_CONNECT        = (1 << 6), /* port connect change status */
+       PORT_IRQ_SG_DONE        = (1 << 5), /* descriptor processed */
+       PORT_IRQ_UNK_FIS        = (1 << 4), /* unknown FIS rx'd */
+       PORT_IRQ_SDB_FIS        = (1 << 3), /* Set Device Bits FIS rx'd */
+       PORT_IRQ_DMAS_FIS       = (1 << 2), /* DMA Setup FIS rx'd */
+       PORT_IRQ_PIOS_FIS       = (1 << 1), /* PIO Setup FIS rx'd */
+       PORT_IRQ_D2H_REG_FIS    = (1 << 0), /* D2H Register FIS rx'd */
+
+       PORT_IRQ_FATAL          = PORT_IRQ_TF_ERR |
+                                 PORT_IRQ_HBUS_ERR |
+                                 PORT_IRQ_HBUS_DATA_ERR |
+                                 PORT_IRQ_IF_ERR,
+       DEF_PORT_IRQ            = PORT_IRQ_FATAL | PORT_IRQ_PHYRDY |
+                                 PORT_IRQ_CONNECT | PORT_IRQ_SG_DONE |
+                                 PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_FIS |
+                                 PORT_IRQ_DMAS_FIS | PORT_IRQ_PIOS_FIS |
+                                 PORT_IRQ_D2H_REG_FIS,
+
+       /* PORT_CMD bits */
+       PORT_CMD_LIST_ON        = (1 << 15), /* cmd list DMA engine running */
+       PORT_CMD_FIS_ON         = (1 << 14), /* FIS DMA engine running */
+       PORT_CMD_FIS_RX         = (1 << 4), /* Enable FIS receive DMA engine */
+       PORT_CMD_POWER_ON       = (1 << 2), /* Power up device */
+       PORT_CMD_SPIN_UP        = (1 << 1), /* Spin up device */
+       PORT_CMD_START          = (1 << 0), /* Enable port DMA engine */
+
+       PORT_CMD_ICC_ACTIVE     = (0x1 << 28), /* Put i/f in active state */
+       PORT_CMD_ICC_PARTIAL    = (0x2 << 28), /* Put i/f in partial state */
+       PORT_CMD_ICC_SLUMBER    = (0x6 << 28), /* Put i/f in slumber state */
+};
+
+struct ahci_cmd_hdr {
+       u32                     opts;
+       u32                     status;
+       u32                     tbl_addr;
+       u32                     tbl_addr_hi;
+       u32                     reserved[4];
+};
+
+struct ahci_sg {
+       u32                     addr;
+       u32                     addr_hi;
+       u32                     reserved;
+       u32                     flags_size;
+};
+
+struct ahci_host_priv {
+       unsigned long           flags;
+       u32                     cap;    /* cache of HOST_CAP register */
+       u32                     port_map; /* cache of HOST_PORTS_IMPL reg */
+};
+
+struct ahci_port_priv {
+       struct ahci_cmd_hdr     *cmd_slot;
+       dma_addr_t              cmd_slot_dma;
+       void                    *cmd_tbl;
+       dma_addr_t              cmd_tbl_dma;
+       struct ahci_sg          *cmd_tbl_sg;
+       void                    *rx_fis;
+       dma_addr_t              rx_fis_dma;
+};
+
+static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static int ahci_qc_issue(struct ata_queued_cmd *qc);
+static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
+static void ahci_phy_reset(struct ata_port *ap);
+static void ahci_irq_clear(struct ata_port *ap);
+static void ahci_eng_timeout(struct ata_port *ap);
+static int ahci_port_start(struct ata_port *ap);
+static void ahci_port_stop(struct ata_port *ap);
+static void ahci_host_stop(struct ata_host_set *host_set);
+static void ahci_qc_prep(struct ata_queued_cmd *qc);
+static u8 ahci_check_status(struct ata_port *ap);
+static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
+
+static Scsi_Host_Template ahci_sht = {
+       .module                 = THIS_MODULE,
+       .name                   = DRV_NAME,
+       .ioctl                  = ata_scsi_ioctl,
+       .queuecommand           = ata_scsi_queuecmd,
+       .eh_strategy_handler    = ata_scsi_error,
+       .can_queue              = ATA_DEF_QUEUE,
+       .this_id                = ATA_SHT_THIS_ID,
+       .sg_tablesize           = AHCI_MAX_SG,
+       .max_sectors            = ATA_MAX_SECTORS,
+       .cmd_per_lun            = ATA_SHT_CMD_PER_LUN,
+       .emulated               = ATA_SHT_EMULATED,
+       .use_clustering         = AHCI_USE_CLUSTERING,
+       .proc_name              = DRV_NAME,
+       .dma_boundary           = AHCI_DMA_BOUNDARY,
+       .slave_configure        = ata_scsi_slave_config,
+       .bios_param             = ata_std_bios_param,
+};
+
+static struct ata_port_operations ahci_ops = {
+       .port_disable           = ata_port_disable,
+
+       .check_status           = ahci_check_status,
+       .dev_select             = ata_noop_dev_select,
+
+       .phy_reset              = ahci_phy_reset,
+
+       .qc_prep                = ahci_qc_prep,
+       .qc_issue               = ahci_qc_issue,
+
+       .eng_timeout            = ahci_eng_timeout,
+
+       .irq_handler            = ahci_interrupt,
+       .irq_clear              = ahci_irq_clear,
+
+       .scr_read               = ahci_scr_read,
+       .scr_write              = ahci_scr_write,
+
+       .port_start             = ahci_port_start,
+       .port_stop              = ahci_port_stop,
+       .host_stop              = ahci_host_stop,
+};
+
+static struct ata_port_info ahci_port_info[] = {
+       /* board_ahci */
+       {
+               .sht            = &ahci_sht,
+               .host_flags     = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+                                 ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
+                                 ATA_FLAG_PIO_DMA,
+               .pio_mask       = 0x03, /* pio3-4 */
+               .udma_mask      = 0x7f, /* udma0-6 ; FIXME */
+               .port_ops       = &ahci_ops,
+       },
+};
+
+static struct pci_device_id ahci_pci_tbl[] = {
+       { PCI_VENDOR_ID_INTEL, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_ahci },
+       { PCI_VENDOR_ID_INTEL, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_ahci },
+       { }     /* terminate list */
+};
+
+
+static struct pci_driver ahci_pci_driver = {
+       .name                   = DRV_NAME,
+       .id_table               = ahci_pci_tbl,
+       .probe                  = ahci_init_one,
+       .remove                 = ata_pci_remove_one,
+};
+
+
+static inline unsigned long ahci_port_base_ul (unsigned long base, unsigned int port)
+{
+       return base + 0x100 + (port * 0x80);
+}
+
+static inline void *ahci_port_base (void *base, unsigned int port)
+{
+       return (void *) ahci_port_base_ul((unsigned long)base, port);
+}
+
+static void ahci_host_stop(struct ata_host_set *host_set)
+{
+       struct ahci_host_priv *hpriv = host_set->private_data;
+       kfree(hpriv);
+}
+
+static int ahci_port_start(struct ata_port *ap)
+{
+       struct pci_dev *pdev = ap->host_set->pdev;
+       struct ahci_host_priv *hpriv = ap->host_set->private_data;
+       struct ahci_port_priv *pp;
+       int rc;
+       void *mem, *mmio = ap->host_set->mmio_base;
+       void *port_mmio = ahci_port_base(mmio, ap->port_no);
+       dma_addr_t mem_dma;
+
+       rc = ata_port_start(ap);
+       if (rc)
+               return rc;
+
+       pp = kmalloc(sizeof(*pp), GFP_KERNEL);
+       if (!pp) {
+               rc = -ENOMEM;
+               goto err_out;
+       }
+       memset(pp, 0, sizeof(*pp));
+
+       mem = pci_alloc_consistent(pdev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma);
+       if (!mem) {
+               rc = -ENOMEM;
+               goto err_out_kfree;
+       }
+       memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ);
+
+       /*
+        * First item in chunk of DMA memory: 32-slot command table,
+        * 32 bytes each in size
+        */
+       pp->cmd_slot = mem;
+       pp->cmd_slot_dma = mem_dma;
+
+       mem += AHCI_CMD_SLOT_SZ;
+       mem_dma += AHCI_CMD_SLOT_SZ;
+
+       /*
+        * Second item: Received-FIS area
+        */
+       pp->rx_fis = mem;
+       pp->rx_fis_dma = mem_dma;
+
+       mem += AHCI_RX_FIS_SZ;
+       mem_dma += AHCI_RX_FIS_SZ;
+
+       /*
+        * Third item: data area for storing a single command
+        * and its scatter-gather table
+        */
+       pp->cmd_tbl = mem;
+       pp->cmd_tbl_dma = mem_dma;
+
+       pp->cmd_tbl_sg = mem + AHCI_CMD_TBL_HDR;
+
+       ap->private_data = pp;
+
+       if (hpriv->cap & HOST_CAP_64)
+               writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
+       writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
+       readl(port_mmio + PORT_LST_ADDR); /* flush */
+
+       if (hpriv->cap & HOST_CAP_64)
+               writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
+       writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
+       readl(port_mmio + PORT_FIS_ADDR); /* flush */
+
+       writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
+              PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
+              PORT_CMD_START, port_mmio + PORT_CMD);
+       readl(port_mmio + PORT_CMD); /* flush */
+
+       return 0;
+
+err_out_kfree:
+       kfree(pp);
+err_out:
+       ata_port_stop(ap);
+       return rc;
+}
+
+
+static void ahci_port_stop(struct ata_port *ap)
+{
+       struct pci_dev *pdev = ap->host_set->pdev;
+       struct ahci_port_priv *pp = ap->private_data;
+       void *mmio = ap->host_set->mmio_base;
+       void *port_mmio = ahci_port_base(mmio, ap->port_no);
+       u32 tmp;
+
+       tmp = readl(port_mmio + PORT_CMD);
+       tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX);
+       writel(tmp, port_mmio + PORT_CMD);
+       readl(port_mmio + PORT_CMD); /* flush */
+
+       /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so
+        * this is slightly incorrect.
+        */
+       msleep(500);
+
+       ap->private_data = NULL;
+       pci_free_consistent(pdev, AHCI_PORT_PRIV_DMA_SZ,
+                           pp->cmd_slot, pp->cmd_slot_dma);
+       kfree(pp);
+       ata_port_stop(ap);
+}
+
+static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in)
+{
+       unsigned int sc_reg;
+
+       switch (sc_reg_in) {
+       case SCR_STATUS:        sc_reg = 0; break;
+       case SCR_CONTROL:       sc_reg = 1; break;
+       case SCR_ERROR:         sc_reg = 2; break;
+       case SCR_ACTIVE:        sc_reg = 3; break;
+       default:
+               return 0xffffffffU;
+       }
+
+       return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+
+static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in,
+                              u32 val)
+{
+       unsigned int sc_reg;
+
+       switch (sc_reg_in) {
+       case SCR_STATUS:        sc_reg = 0; break;
+       case SCR_CONTROL:       sc_reg = 1; break;
+       case SCR_ERROR:         sc_reg = 2; break;
+       case SCR_ACTIVE:        sc_reg = 3; break;
+       default:
+               return;
+       }
+
+       writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+static void ahci_phy_reset(struct ata_port *ap)
+{
+       void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+       struct ata_taskfile tf;
+       struct ata_device *dev = &ap->device[0];
+       u32 tmp;
+
+       __sata_phy_reset(ap);
+
+       if (ap->flags & ATA_FLAG_PORT_DISABLED)
+               return;
+
+       tmp = readl(port_mmio + PORT_SIG);
+       tf.lbah         = (tmp >> 24)   & 0xff;
+       tf.lbam         = (tmp >> 16)   & 0xff;
+       tf.lbal         = (tmp >> 8)    & 0xff;
+       tf.nsect        = (tmp)         & 0xff;
+
+       dev->class = ata_dev_classify(&tf);
+       if (!ata_dev_present(dev))
+               ata_port_disable(ap);
+}
+
+static u8 ahci_check_status(struct ata_port *ap)
+{
+       void *mmio = (void *) ap->ioaddr.cmd_addr;
+
+       return readl(mmio + PORT_TFDATA) & 0xFF;
+}
+
+static void ahci_fill_sg(struct ata_queued_cmd *qc)
+{
+       struct ahci_port_priv *pp = qc->ap->private_data;
+       unsigned int i;
+
+       VPRINTK("ENTER\n");
+
+       /*
+        * Next, the S/G list.
+        */
+       for (i = 0; i < qc->n_elem; i++) {
+               u32 sg_len;
+               dma_addr_t addr;
+
+               addr = sg_dma_address(&qc->sg[i]);
+               sg_len = sg_dma_len(&qc->sg[i]);
+
+               pp->cmd_tbl_sg[i].addr = cpu_to_le32(addr & 0xffffffff);
+               pp->cmd_tbl_sg[i].addr_hi = cpu_to_le32((addr >> 16) >> 16);
+               pp->cmd_tbl_sg[i].flags_size = cpu_to_le32(sg_len - 1);
+       }
+}
+
+static void ahci_qc_prep(struct ata_queued_cmd *qc)
+{
+       struct ahci_port_priv *pp = qc->ap->private_data;
+       u32 opts;
+       const u32 cmd_fis_len = 5; /* five dwords */
+
+       /*
+        * Fill in command slot information (currently only one slot,
+        * slot 0, is currently since we don't do queueing)
+        */
+
+       opts = (qc->n_elem << 16) | cmd_fis_len;
+       if (qc->tf.flags & ATA_TFLAG_WRITE)
+               opts |= AHCI_CMD_WRITE;
+
+       switch (qc->tf.protocol) {
+       case ATA_PROT_ATAPI:
+       case ATA_PROT_ATAPI_NODATA:
+       case ATA_PROT_ATAPI_DMA:
+               opts |= AHCI_CMD_ATAPI;
+               break;
+
+       default:
+               /* do nothing */
+               break;
+       }
+
+       pp->cmd_slot[0].opts = cpu_to_le32(opts);
+       pp->cmd_slot[0].status = 0;
+       pp->cmd_slot[0].tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff);
+       pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16);
+
+       /*
+        * Fill in command table information.  First, the header,
+        * a SATA Register - Host to Device command FIS.
+        */
+       ata_tf_to_fis(&qc->tf, pp->cmd_tbl, 0);
+
+       if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+               return;
+
+       ahci_fill_sg(qc);
+}
+
+static inline void ahci_dma_complete (struct ata_port *ap,
+                                     struct ata_queued_cmd *qc,
+                                    int have_err)
+{
+       /* get drive status; clear intr; complete txn */
+       ata_qc_complete(ata_qc_from_tag(ap, ap->active_tag),
+                       have_err ? ATA_ERR : 0);
+}
+
+static void ahci_intr_error(struct ata_port *ap, u32 irq_stat)
+{
+       void *mmio = ap->host_set->mmio_base;
+       void *port_mmio = ahci_port_base(mmio, ap->port_no);
+       u32 tmp;
+       int work;
+
+       /* stop DMA */
+       tmp = readl(port_mmio + PORT_CMD);
+       tmp &= PORT_CMD_START | PORT_CMD_FIS_RX;
+       writel(tmp, port_mmio + PORT_CMD);
+
+       /* wait for engine to stop.  TODO: this could be
+        * as long as 500 msec
+        */
+       work = 1000;
+       while (work-- > 0) {
+               tmp = readl(port_mmio + PORT_CMD);
+               if ((tmp & PORT_CMD_LIST_ON) == 0)
+                       break;
+               udelay(10);
+       }
+
+       /* clear SATA phy error, if any */
+       tmp = readl(port_mmio + PORT_SCR_ERR);
+       writel(tmp, port_mmio + PORT_SCR_ERR);
+
+       /* if DRQ/BSY is set, device needs to be reset.
+        * if so, issue COMRESET
+        */
+       tmp = readl(port_mmio + PORT_TFDATA);
+       if (tmp & (ATA_BUSY | ATA_DRQ)) {
+               writel(0x301, port_mmio + PORT_SCR_CTL);
+               readl(port_mmio + PORT_SCR_CTL); /* flush */
+               udelay(10);
+               writel(0x300, port_mmio + PORT_SCR_CTL);
+               readl(port_mmio + PORT_SCR_CTL); /* flush */
+       }
+
+       /* re-start DMA */
+       tmp = readl(port_mmio + PORT_CMD);
+       tmp |= PORT_CMD_START | PORT_CMD_FIS_RX;
+       writel(tmp, port_mmio + PORT_CMD);
+       readl(port_mmio + PORT_CMD); /* flush */
+
+       printk(KERN_WARNING "ata%u: error occurred, port reset\n", ap->port_no);
+}
+
+static void ahci_eng_timeout(struct ata_port *ap)
+{
+       void *mmio = ap->host_set->mmio_base;
+       void *port_mmio = ahci_port_base(mmio, ap->port_no);
+       struct ata_queued_cmd *qc;
+
+       DPRINTK("ENTER\n");
+
+       ahci_intr_error(ap, readl(port_mmio + PORT_IRQ_STAT));
+
+       qc = ata_qc_from_tag(ap, ap->active_tag);
+       if (!qc) {
+               printk(KERN_ERR "ata%u: BUG: timeout without command\n",
+                      ap->id);
+       } else {
+               /* hack alert!  We cannot use the supplied completion
+                * function from inside the ->eh_strategy_handler() thread.
+                * libata is the only user of ->eh_strategy_handler() in
+                * any kernel, so the default scsi_done() assumes it is
+                * not being called from the SCSI EH.
+                */
+               qc->scsidone = scsi_finish_command;
+               ata_qc_complete(qc, ATA_ERR);
+       }
+
+}
+
+static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
+{
+       void *mmio = ap->host_set->mmio_base;
+       void *port_mmio = ahci_port_base(mmio, ap->port_no);
+       u32 status, serr, ci;
+
+       serr = readl(port_mmio + PORT_SCR_ERR);
+       writel(serr, port_mmio + PORT_SCR_ERR);
+
+       status = readl(port_mmio + PORT_IRQ_STAT);
+       writel(status, port_mmio + PORT_IRQ_STAT);
+
+       ci = readl(port_mmio + PORT_CMD_ISSUE);
+       if (likely((ci & 0x1) == 0)) {
+               if (qc) {
+                       ata_qc_complete(qc, 0);
+                       qc = NULL;
+               }
+       }
+
+       if (status & PORT_IRQ_FATAL) {
+               ahci_intr_error(ap, status);
+               if (qc)
+                       ata_qc_complete(qc, ATA_ERR);
+       }
+
+       return 1;
+}
+
+static void ahci_irq_clear(struct ata_port *ap)
+{
+       /* TODO */
+}
+
+static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
+{
+       struct ata_host_set *host_set = dev_instance;
+       struct ahci_host_priv *hpriv;
+       unsigned int i, handled = 0;
+       void *mmio;
+       u32 irq_stat, irq_ack = 0;
+
+       VPRINTK("ENTER\n");
+
+       hpriv = host_set->private_data;
+       mmio = host_set->mmio_base;
+
+       /* sigh.  0xffffffff is a valid return from h/w */
+       irq_stat = readl(mmio + HOST_IRQ_STAT);
+       irq_stat &= hpriv->port_map;
+       if (!irq_stat)
+               return IRQ_NONE;
+
+        spin_lock(&host_set->lock);
+
+        for (i = 0; i < host_set->n_ports; i++) {
+               struct ata_port *ap;
+               u32 tmp;
+
+               VPRINTK("port %u\n", i);
+               ap = host_set->ports[i];
+               tmp = irq_stat & (1 << i);
+               if (tmp && ap) {
+                       struct ata_queued_cmd *qc;
+                       qc = ata_qc_from_tag(ap, ap->active_tag);
+                       if (ahci_host_intr(ap, qc))
+                               irq_ack |= (1 << i);
+               }
+       }
+
+       if (irq_ack) {
+               writel(irq_ack, mmio + HOST_IRQ_STAT);
+               handled = 1;
+       }
+
+        spin_unlock(&host_set->lock);
+
+       VPRINTK("EXIT\n");
+
+       return IRQ_RETVAL(handled);
+}
+
+static int ahci_qc_issue(struct ata_queued_cmd *qc)
+{
+       struct ata_port *ap = qc->ap;
+       void *port_mmio = (void *) ap->ioaddr.cmd_addr;
+
+       writel(1, port_mmio + PORT_SCR_ACT);
+       readl(port_mmio + PORT_SCR_ACT);        /* flush */
+
+       writel(1, port_mmio + PORT_CMD_ISSUE);
+       readl(port_mmio + PORT_CMD_ISSUE);      /* flush */
+
+       return 0;
+}
+
+static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
+                           unsigned int port_idx)
+{
+       VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx);
+       base = ahci_port_base_ul(base, port_idx);
+       VPRINTK("base now==0x%lx\n", base);
+
+       port->cmd_addr          = base;
+       port->scr_addr          = base + PORT_SCR;
+
+       VPRINTK("EXIT\n");
+}
+
+static int ahci_host_init(struct ata_probe_ent *probe_ent)
+{
+       struct ahci_host_priv *hpriv = probe_ent->private_data;
+       struct pci_dev *pdev = probe_ent->pdev;
+       void __iomem *mmio = probe_ent->mmio_base;
+       u32 tmp, cap_save;
+       u16 tmp16;
+       unsigned int i, j, using_dac;
+       int rc;
+       void __iomem *port_mmio;
+
+       cap_save = readl(mmio + HOST_CAP);
+       cap_save &= ( (1<<28) | (1<<17) );
+       cap_save |= (1 << 27);
+
+       /* global controller reset */
+       tmp = readl(mmio + HOST_CTL);
+       if ((tmp & HOST_RESET) == 0) {
+               writel(tmp | HOST_RESET, mmio + HOST_CTL);
+               readl(mmio + HOST_CTL); /* flush */
+       }
+
+       /* reset must complete within 1 second, or
+        * the hardware should be considered fried.
+        */
+       ssleep(1);
+
+       tmp = readl(mmio + HOST_CTL);
+       if (tmp & HOST_RESET) {
+               printk(KERN_ERR DRV_NAME "(%s): controller reset failed (0x%x)\n",
+                       pci_name(pdev), tmp);
+               return -EIO;
+       }
+
+       writel(HOST_AHCI_EN, mmio + HOST_CTL);
+       (void) readl(mmio + HOST_CTL);  /* flush */
+       writel(cap_save, mmio + HOST_CAP);
+       writel(0xf, mmio + HOST_PORTS_IMPL);
+       (void) readl(mmio + HOST_PORTS_IMPL);   /* flush */
+
+       pci_read_config_word(pdev, 0x92, &tmp16);
+       tmp16 |= 0xf;
+       pci_write_config_word(pdev, 0x92, tmp16);
+
+       hpriv->cap = readl(mmio + HOST_CAP);
+       hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
+       probe_ent->n_ports = (hpriv->cap & 0x1f) + 1;
+
+       VPRINTK("cap 0x%x  port_map 0x%x  n_ports %d\n",
+               hpriv->cap, hpriv->port_map, probe_ent->n_ports);
+
+       using_dac = hpriv->cap & HOST_CAP_64;
+       if (using_dac &&
+           !pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) {
+               rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
+               if (rc) {
+                       rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL);
+                       if (rc) {
+                               printk(KERN_ERR DRV_NAME "(%s): 64-bit DMA enable failed\n",
+                                       pci_name(pdev));
+                               return rc;
+                       }
+               }
+
+               hpriv->flags |= HOST_CAP_64;
+       } else {
+               rc = pci_set_dma_mask(pdev, 0xffffffffULL);
+               if (rc) {
+                       printk(KERN_ERR DRV_NAME "(%s): 32-bit DMA enable failed\n",
+                               pci_name(pdev));
+                       return rc;
+               }
+               rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL);
+               if (rc) {
+                       printk(KERN_ERR DRV_NAME "(%s): 32-bit consistent DMA enable failed\n",
+                               pci_name(pdev));
+                       return rc;
+               }
+       }
+
+       for (i = 0; i < probe_ent->n_ports; i++) {
+#if 0 /* BIOSen initialize this incorrectly */
+               if (!(hpriv->port_map & (1 << i)))
+                       continue;
+#endif
+
+               port_mmio = ahci_port_base(mmio, i);
+               VPRINTK("mmio %p  port_mmio %p\n", mmio, port_mmio);
+
+               ahci_setup_port(&probe_ent->port[i],
+                               (unsigned long) mmio, i);
+
+               /* make sure port is not active */
+               tmp = readl(port_mmio + PORT_CMD);
+               VPRINTK("PORT_CMD 0x%x\n", tmp);
+               if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
+                          PORT_CMD_FIS_RX | PORT_CMD_START)) {
+                       tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
+                                PORT_CMD_FIS_RX | PORT_CMD_START);
+                       writel(tmp, port_mmio + PORT_CMD);
+                       readl(port_mmio + PORT_CMD); /* flush */
+
+                       /* spec says 500 msecs for each bit, so
+                        * this is slightly incorrect.
+                        */
+                       msleep(500);
+               }
+
+               writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD);
+
+               j = 0;
+               while (j < 100) {
+                       msleep(10);
+                       tmp = readl(port_mmio + PORT_SCR_STAT);
+                       if ((tmp & 0xf) == 0x3)
+                               break;
+                       j++;
+               }
+
+               tmp = readl(port_mmio + PORT_SCR_ERR);
+               VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
+               writel(tmp, port_mmio + PORT_SCR_ERR);
+
+               /* ack any pending irq events for this port */
+               tmp = readl(port_mmio + PORT_IRQ_STAT);
+               VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
+               if (tmp)
+                       writel(tmp, port_mmio + PORT_IRQ_STAT);
+
+               writel(1 << i, mmio + HOST_IRQ_STAT);
+
+               /* set irq mask (enables interrupts) */
+               writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
+       }
+
+       tmp = readl(mmio + HOST_CTL);
+       VPRINTK("HOST_CTL 0x%x\n", tmp);
+       writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
+       tmp = readl(mmio + HOST_CTL);
+       VPRINTK("HOST_CTL 0x%x\n", tmp);
+
+       pci_set_master(pdev);
+
+       return 0;
+}
+
+/* move to PCI layer, integrate w/ MSI stuff */
+static void pci_enable_intx(struct pci_dev *pdev)
+{
+       u16 pci_command;
+
+       pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+       if (pci_command & PCI_COMMAND_INTX_DISABLE) {
+               pci_command &= ~PCI_COMMAND_INTX_DISABLE;
+               pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+       }
+}
+
+static void ahci_print_info(struct ata_probe_ent *probe_ent)
+{
+       struct ahci_host_priv *hpriv = probe_ent->private_data;
+       struct pci_dev *pdev = probe_ent->pdev;
+       void *mmio = probe_ent->mmio_base;
+       u32 vers, cap, impl, speed;
+       const char *speed_s;
+       u16 cc;
+       const char *scc_s;
+
+       vers = readl(mmio + HOST_VERSION);
+       cap = hpriv->cap;
+       impl = hpriv->port_map;
+
+       speed = (cap >> 20) & 0xf;
+       if (speed == 1)
+               speed_s = "1.5";
+       else if (speed == 2)
+               speed_s = "3";
+       else
+               speed_s = "?";
+
+       pci_read_config_word(pdev, 0x0a, &cc);
+       if (cc == 0x0101)
+               scc_s = "IDE";
+       else if (cc == 0x0106)
+               scc_s = "SATA";
+       else if (cc == 0x0104)
+               scc_s = "RAID";
+       else
+               scc_s = "unknown";
+
+       printk(KERN_INFO DRV_NAME "(%s) AHCI %02x%02x.%02x%02x "
+               "%u slots %u ports %s Gbps 0x%x impl %s mode\n"
+               ,
+               pci_name(pdev),
+
+               (vers >> 24) & 0xff,
+               (vers >> 16) & 0xff,
+               (vers >> 8) & 0xff,
+               vers & 0xff,
+
+               ((cap >> 8) & 0x1f) + 1,
+               (cap & 0x1f) + 1,
+               speed_s,
+               impl,
+               scc_s);
+
+       printk(KERN_INFO DRV_NAME "(%s) flags: "
+               "%s%s%s%s%s%s"
+               "%s%s%s%s%s%s%s\n"
+               ,
+               pci_name(pdev),
+
+               cap & (1 << 31) ? "64bit " : "",
+               cap & (1 << 30) ? "ncq " : "",
+               cap & (1 << 28) ? "ilck " : "",
+               cap & (1 << 27) ? "stag " : "",
+               cap & (1 << 26) ? "pm " : "",
+               cap & (1 << 25) ? "led " : "",
+
+               cap & (1 << 24) ? "clo " : "",
+               cap & (1 << 19) ? "nz " : "",
+               cap & (1 << 18) ? "only " : "",
+               cap & (1 << 17) ? "pmp " : "",
+               cap & (1 << 15) ? "pio " : "",
+               cap & (1 << 14) ? "slum " : "",
+               cap & (1 << 13) ? "part " : ""
+               );
+}
+
+static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       static int printed_version;
+       struct ata_probe_ent *probe_ent = NULL;
+       struct ahci_host_priv *hpriv;
+       unsigned long base;
+       void *mmio_base;
+       unsigned int board_idx = (unsigned int) ent->driver_data;
+       int rc;
+
+       VPRINTK("ENTER\n");
+
+       if (!printed_version++)
+               printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+       /*
+        * If this driver happens to only be useful on Apple's K2, then
+        * we should check that here as it has a normal Serverworks ID
+        */
+       rc = pci_enable_device(pdev);
+       if (rc)
+               return rc;
+
+       rc = pci_request_regions(pdev, DRV_NAME);
+       if (rc)
+               goto err_out;
+
+       pci_enable_intx(pdev);
+
+       probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+       if (probe_ent == NULL) {
+               rc = -ENOMEM;
+               goto err_out_regions;
+       }
+
+       memset(probe_ent, 0, sizeof(*probe_ent));
+       probe_ent->pdev = pdev;
+       INIT_LIST_HEAD(&probe_ent->node);
+
+       mmio_base = ioremap(pci_resource_start(pdev, AHCI_PCI_BAR),
+                           pci_resource_len(pdev, AHCI_PCI_BAR));
+       if (mmio_base == NULL) {
+               rc = -ENOMEM;
+               goto err_out_free_ent;
+       }
+       base = (unsigned long) mmio_base;
+
+       hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);
+       if (!hpriv) {
+               rc = -ENOMEM;
+               goto err_out_iounmap;
+       }
+       memset(hpriv, 0, sizeof(*hpriv));
+
+       probe_ent->sht          = ahci_port_info[board_idx].sht;
+       probe_ent->host_flags   = ahci_port_info[board_idx].host_flags;
+       probe_ent->pio_mask     = ahci_port_info[board_idx].pio_mask;
+       probe_ent->udma_mask    = ahci_port_info[board_idx].udma_mask;
+       probe_ent->port_ops     = ahci_port_info[board_idx].port_ops;
+
+               probe_ent->irq = pdev->irq;
+               probe_ent->irq_flags = SA_SHIRQ;
+       probe_ent->mmio_base = mmio_base;
+       probe_ent->private_data = hpriv;
+
+       /* initialize adapter */
+       rc = ahci_host_init(probe_ent);
+       if (rc)
+               goto err_out_hpriv;
+
+       ahci_print_info(probe_ent);
+
+       /* FIXME: check ata_device_add return value */
+       ata_device_add(probe_ent);
+       kfree(probe_ent);
+
+       return 0;
+
+err_out_hpriv:
+       kfree(hpriv);
+err_out_iounmap:
+       iounmap(mmio_base);
+err_out_free_ent:
+       kfree(probe_ent);
+err_out_regions:
+       pci_release_regions(pdev);
+err_out:
+       pci_disable_device(pdev);
+       return rc;
+}
+
+
+static int __init ahci_init(void)
+{
+       return pci_module_init(&ahci_pci_driver);
+}
+
+
+static void __exit ahci_exit(void)
+{
+       pci_unregister_driver(&ahci_pci_driver);
+}
+
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("AHCI SATA low-level driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, ahci_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(ahci_init);
+module_exit(ahci_exit);
diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.h b/drivers/scsi/aic7xxx/aic79xx_pci.h
new file mode 100644 (file)
index 0000000..b5cfeab
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Adaptec AIC79xx device driver for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id$
+ *
+ */
+#ifndef _AIC79XX_PCI_H_
+#define _AIC79XX_PCI_H_
+
+#define ID_ALL_MASK                    0xFFFFFFFFFFFFFFFFull
+#define ID_ALL_IROC_MASK               0xFF7FFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK             0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK           0xFFF0FFFF00000000ull
+#define ID_9005_GENERIC_IROC_MASK      0xFF70FFFF00000000ull
+
+#define ID_AIC7901                     0x800F9005FFFF9005ull
+#define ID_AHA_29320A                  0x8000900500609005ull
+#define ID_AHA_29320ALP                        0x8017900500449005ull
+
+#define ID_AIC7901A                    0x801E9005FFFF9005ull
+#define ID_AHA_29320                   0x8012900500429005ull
+#define ID_AHA_29320B                  0x8013900500439005ull
+#define ID_AHA_29320LP                 0x8014900500449005ull
+
+#define ID_AIC7902                     0x801F9005FFFF9005ull
+#define ID_AIC7902_B                   0x801D9005FFFF9005ull
+#define ID_AHA_39320                   0x8010900500409005ull
+#define ID_AHA_39320_B                 0x8015900500409005ull
+#define ID_AHA_39320A                  0x8016900500409005ull
+#define ID_AHA_39320D                  0x8011900500419005ull
+#define ID_AHA_39320D_B                        0x801C900500419005ull
+#define ID_AHA_39320D_HP               0x8011900500AC0E11ull
+#define ID_AHA_39320D_B_HP             0x801C900500AC0E11ull
+
+#endif /* _AIC79XX_PCI_H_ */
diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.h b/drivers/scsi/aic7xxx/aic7xxx_pci.h
new file mode 100644 (file)
index 0000000..be27fcb
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 2000-2001 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ *
+ * $Id$
+ *
+ */
+#ifndef _AIC7XXX_PCI_H_
+#define _AIC7XXX_PCI_H_
+
+#define ID_ALL_MASK                    0xFFFFFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK             0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK           0xFFF0FFFF00000000ull
+#define ID_9005_SISL_MASK              0x000FFFFF00000000ull
+#define ID_9005_SISL_ID                        0x0005900500000000ull
+#define ID_AIC7850                     0x5078900400000000ull
+#define ID_AHA_2902_04_10_15_20C_30C   0x5078900478509004ull
+#define ID_AIC7855                     0x5578900400000000ull
+#define ID_AIC7859                     0x3860900400000000ull
+#define ID_AHA_2930CU                  0x3860900438699004ull
+#define ID_AIC7860                     0x6078900400000000ull
+#define ID_AIC7860C                    0x6078900478609004ull
+#define ID_AHA_1480A                   0x6075900400000000ull
+#define ID_AHA_2940AU_0                        0x6178900400000000ull
+#define ID_AHA_2940AU_1                        0x6178900478619004ull
+#define ID_AHA_2940AU_CN               0x2178900478219004ull
+#define ID_AHA_2930C_VAR               0x6038900438689004ull
+
+#define ID_AIC7870                     0x7078900400000000ull
+#define ID_AHA_2940                    0x7178900400000000ull
+#define ID_AHA_3940                    0x7278900400000000ull
+#define ID_AHA_398X                    0x7378900400000000ull
+#define ID_AHA_2944                    0x7478900400000000ull
+#define ID_AHA_3944                    0x7578900400000000ull
+#define ID_AHA_4944                    0x7678900400000000ull
+
+#define ID_AIC7880                     0x8078900400000000ull
+#define ID_AIC7880_B                   0x8078900478809004ull
+#define ID_AHA_2940U                   0x8178900400000000ull
+#define ID_AHA_3940U                   0x8278900400000000ull
+#define ID_AHA_2944U                   0x8478900400000000ull
+#define ID_AHA_3944U                   0x8578900400000000ull
+#define ID_AHA_398XU                   0x8378900400000000ull
+#define ID_AHA_4944U                   0x8678900400000000ull
+#define ID_AHA_2940UB                  0x8178900478819004ull
+#define ID_AHA_2930U                   0x8878900478889004ull
+#define ID_AHA_2940U_PRO               0x8778900478879004ull
+#define ID_AHA_2940U_CN                        0x0078900478009004ull
+
+#define ID_AIC7895                     0x7895900478959004ull
+#define ID_AIC7895_ARO                 0x7890900478939004ull
+#define ID_AIC7895_ARO_MASK            0xFFF0FFFFFFFFFFFFull
+#define ID_AHA_2940U_DUAL              0x7895900478919004ull
+#define ID_AHA_3940AU                  0x7895900478929004ull
+#define ID_AHA_3944AU                  0x7895900478949004ull
+
+#define ID_AIC7890                     0x001F9005000F9005ull
+#define ID_AIC7890_ARO                 0x00139005000F9005ull
+#define ID_AAA_131U2                   0x0013900500039005ull
+#define ID_AHA_2930U2                  0x0011900501819005ull
+#define ID_AHA_2940U2B                 0x00109005A1009005ull
+#define ID_AHA_2940U2_OEM              0x0010900521809005ull
+#define ID_AHA_2940U2                  0x00109005A1809005ull
+#define ID_AHA_2950U2B                 0x00109005E1009005ull
+
+#define ID_AIC7892                     0x008F9005FFFF9005ull
+#define ID_AIC7892_ARO                 0x00839005FFFF9005ull
+#define ID_AHA_29160                   0x00809005E2A09005ull
+#define ID_AHA_29160_CPQ               0x00809005E2A00E11ull
+#define ID_AHA_29160N                  0x0080900562A09005ull
+#define ID_AHA_29160C                  0x0080900562209005ull
+#define ID_AHA_29160B                  0x00809005E2209005ull
+#define ID_AHA_19160B                  0x0081900562A19005ull
+
+#define ID_AIC7896                     0x005F9005FFFF9005ull
+#define ID_AIC7896_ARO                 0x00539005FFFF9005ull
+#define ID_AHA_3950U2B_0               0x00509005FFFF9005ull
+#define ID_AHA_3950U2B_1               0x00509005F5009005ull
+#define ID_AHA_3950U2D_0               0x00519005FFFF9005ull
+#define ID_AHA_3950U2D_1               0x00519005B5009005ull
+
+#define ID_AIC7899                     0x00CF9005FFFF9005ull
+#define ID_AIC7899_ARO                 0x00C39005FFFF9005ull
+#define ID_AHA_3960D                   0x00C09005F6209005ull
+#define ID_AHA_3960D_CPQ               0x00C09005F6200E11ull
+
+#define ID_AIC7810                     0x1078900400000000ull
+#define ID_AIC7815                     0x7815900400000000ull
+
+#endif /* _AIC7XXX_PCI_H_ */
diff --git a/drivers/scsi/gdth_kcompat.h b/drivers/scsi/gdth_kcompat.h
new file mode 100644 (file)
index 0000000..e6cf0ed
--- /dev/null
@@ -0,0 +1,21 @@
+
+
+#ifndef IRQ_HANDLED
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#endif
+
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(x)
+#endif
+
+#ifndef SERVICE_ACTION_IN
+#define SERVICE_ACTION_IN      0x9e
+#endif
+#ifndef READ_16
+#define READ_16                        0x88
+#endif
+#ifndef WRITE_16
+#define WRITE_16               0x8a
+#endif
diff --git a/drivers/scsi/initio.c b/drivers/scsi/initio.c
new file mode 100644 (file)
index 0000000..a7b74d8
--- /dev/null
@@ -0,0 +1,3184 @@
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl>
+ * 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, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ *************************************************************************
+ *
+ * DESCRIPTION:
+ *
+ * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host
+ * adapters
+ *
+ * 08/06/97 hc - v1.01h
+ *             - Support inic-940 and inic-935
+ * 09/26/97 hc - v1.01i
+ *             - Make correction from J.W. Schultz suggestion
+ * 10/13/97 hc - Support reset function
+ * 10/21/97 hc - v1.01j
+ *             - Support 32 LUN (SCSI 3)
+ * 01/14/98 hc - v1.01k
+ *             - Fix memory allocation problem
+ * 03/04/98 hc - v1.01l
+ *             - Fix tape rewind which will hang the system problem
+ *             - Set can_queue to tul_num_scb
+ * 06/25/98 hc - v1.01m
+ *             - Get it work for kernel version >= 2.1.75
+ *             - Dynamic assign SCSI bus reset holding time in init_tulip()
+ * 07/02/98 hc - v1.01n
+ *             - Support 0002134A
+ * 08/07/98 hc  - v1.01o
+ *             - Change the tul_abort_srb routine to use scsi_done. <01>
+ * 09/07/98 hl  - v1.02
+ *              - Change the INI9100U define and proc_dir_entry to
+ *                reflect the newer Kernel 2.1.118, but the v1.o1o
+ *                should work with Kernel 2.1.118.
+ * 09/20/98 wh  - v1.02a
+ *              - Support Abort command.
+ *              - Handle reset routine.
+ * 09/21/98 hl  - v1.03
+ *              - remove comments.
+ * 12/09/98 bv - v1.03a
+ *             - Removed unused code
+ * 12/13/98 bv - v1.03b
+ *             - Remove cli() locking for kernels >= 2.1.95. This uses
+ *               spinlocks to serialize access to the pSRB_head and
+ *               pSRB_tail members of the HCS structure.
+ * 09/01/99 bv - v1.03d
+ *             - Fixed a deadlock problem in SMP.
+ * 21/01/99 bv - v1.03e
+ *             - Add support for the Domex 3192U PCI SCSI
+ *               This is a slightly modified patch by
+ *               Brian Macy <bmacy@sunshinecomputing.com>
+ * 22/02/99 bv - v1.03f
+ *             - Didn't detect the INIC-950 in 2.0.x correctly.
+ *               Now fixed.
+ * 05/07/99 bv - v1.03g
+ *             - Changed the assumption that HZ = 100
+ * 10/17/03 mc - v1.04
+ *             - added new DMA API support
+ * 06/01/04 jmd        - v1.04a
+ *             - Re-add reset_bus support
+ **************************************************************************/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <asm/io.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+
+#include "initio.h"
+
+#define SENSE_SIZE             14
+
+#define i91u_MAXQUEUE          2
+#define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.04a"
+
+#define INI_VENDOR_ID   0x1101 /* Initio's PCI vendor ID       */
+#define DMX_VENDOR_ID  0x134a  /* Domex's PCI vendor ID        */
+#define I950_DEVICE_ID 0x9500  /* Initio's inic-950 product ID   */
+#define I940_DEVICE_ID 0x9400  /* Initio's inic-940 product ID   */
+#define I935_DEVICE_ID 0x9401  /* Initio's inic-935 product ID   */
+#define I920_DEVICE_ID 0x0002  /* Initio's other product ID      */
+
+#ifdef DEBUG_i91u
+static unsigned int i91u_debug = DEBUG_DEFAULT;
+#endif
+
+#define TULSZ(sz)     (sizeof(sz) / sizeof(sz[0]))
+#define TUL_RDWORD(x,y)         (short)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) ))
+
+typedef struct PCI_ID_Struc {
+       unsigned short vendor_id;
+       unsigned short device_id;
+} PCI_ID;
+
+static int tul_num_ch = 4;     /* Maximum 4 adapters           */
+static int tul_num_scb;
+static int tul_tag_enable = 1;
+static SCB *tul_scb;
+
+#ifdef DEBUG_i91u
+static int setup_debug = 0;
+#endif
+
+static void i91uSCBPost(BYTE * pHcb, BYTE * pScb);
+
+static const PCI_ID i91u_pci_devices[] = {
+       { INI_VENDOR_ID, I950_DEVICE_ID },
+       { INI_VENDOR_ID, I940_DEVICE_ID },
+       { INI_VENDOR_ID, I935_DEVICE_ID },
+       { INI_VENDOR_ID, I920_DEVICE_ID },
+       { DMX_VENDOR_ID, I920_DEVICE_ID },
+};
+
+#define DEBUG_INTERRUPT 0
+#define DEBUG_QUEUE     0
+#define DEBUG_STATE     0
+#define INT_DISC       0
+
+/*--- external functions --*/
+static void tul_se2_wait(void);
+
+/*--- forward refrence ---*/
+static SCB *tul_find_busy_scb(HCS * pCurHcb, WORD tarlun);
+static SCB *tul_find_done_scb(HCS * pCurHcb);
+
+static int tulip_main(HCS * pCurHcb);
+
+static int tul_next_state(HCS * pCurHcb);
+static int tul_state_1(HCS * pCurHcb);
+static int tul_state_2(HCS * pCurHcb);
+static int tul_state_3(HCS * pCurHcb);
+static int tul_state_4(HCS * pCurHcb);
+static int tul_state_5(HCS * pCurHcb);
+static int tul_state_6(HCS * pCurHcb);
+static int tul_state_7(HCS * pCurHcb);
+static int tul_xfer_data_in(HCS * pCurHcb);
+static int tul_xfer_data_out(HCS * pCurHcb);
+static int tul_xpad_in(HCS * pCurHcb);
+static int tul_xpad_out(HCS * pCurHcb);
+static int tul_status_msg(HCS * pCurHcb);
+
+static int tul_msgin(HCS * pCurHcb);
+static int tul_msgin_sync(HCS * pCurHcb);
+static int tul_msgin_accept(HCS * pCurHcb);
+static int tul_msgout_reject(HCS * pCurHcb);
+static int tul_msgin_extend(HCS * pCurHcb);
+
+static int tul_msgout_ide(HCS * pCurHcb);
+static int tul_msgout_abort_targ(HCS * pCurHcb);
+static int tul_msgout_abort_tag(HCS * pCurHcb);
+
+static int tul_bus_device_reset(HCS * pCurHcb);
+static void tul_select_atn(HCS * pCurHcb, SCB * pCurScb);
+static void tul_select_atn3(HCS * pCurHcb, SCB * pCurScb);
+static void tul_select_atn_stop(HCS * pCurHcb, SCB * pCurScb);
+static int int_tul_busfree(HCS * pCurHcb);
+int int_tul_scsi_rst(HCS * pCurHcb);
+static int int_tul_bad_seq(HCS * pCurHcb);
+static int int_tul_resel(HCS * pCurHcb);
+static int tul_sync_done(HCS * pCurHcb);
+static int wdtr_done(HCS * pCurHcb);
+static int wait_tulip(HCS * pCurHcb);
+static int tul_wait_done_disc(HCS * pCurHcb);
+static int tul_wait_disc(HCS * pCurHcb);
+static void tulip_scsi(HCS * pCurHcb);
+static int tul_post_scsi_rst(HCS * pCurHcb);
+
+static void tul_se2_ew_en(WORD CurBase);
+static void tul_se2_ew_ds(WORD CurBase);
+static int tul_se2_rd_all(WORD CurBase);
+static void tul_se2_update_all(WORD CurBase);  /* setup default pattern */
+static void tul_read_eeprom(WORD CurBase);
+
+                               /* ---- EXTERNAL VARIABLES ---- */
+HCS tul_hcs[MAX_SUPPORTED_ADAPTERS];
+                               /* ---- INTERNAL VARIABLES ---- */
+static INI_ADPT_STRUCT i91u_adpt[MAX_SUPPORTED_ADAPTERS];
+
+/*NVRAM nvram, *nvramp = &nvram; */
+static NVRAM i91unvram;
+static NVRAM *i91unvramp;
+
+
+
+static UCHAR i91udftNvRam[64] =
+{
+/*----------- header -----------*/
+       0x25, 0xc9,             /* Signature    */
+       0x40,                   /* Size         */
+       0x01,                   /* Revision     */
+       /* -- Host Adapter Structure -- */
+       0x95,                   /* ModelByte0   */
+       0x00,                   /* ModelByte1   */
+       0x00,                   /* ModelInfo    */
+       0x01,                   /* NumOfCh      */
+       NBC1_DEFAULT,           /* BIOSConfig1  */
+       0,                      /* BIOSConfig2  */
+       0,                      /* HAConfig1    */
+       0,                      /* HAConfig2    */
+       /* SCSI channel 0 and target Structure  */
+       7,                      /* SCSIid       */
+       NCC1_DEFAULT,           /* SCSIconfig1  */
+       0,                      /* SCSIconfig2  */
+       0x10,                   /* NumSCSItarget */
+
+       NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+       NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+       NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+       NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+
+       /* SCSI channel 1 and target Structure  */
+       7,                      /* SCSIid       */
+       NCC1_DEFAULT,           /* SCSIconfig1  */
+       0,                      /* SCSIconfig2  */
+       0x10,                   /* NumSCSItarget */
+
+       NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+       NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+       NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+       NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0};                  /*      - CheckSum -            */
+
+
+static UCHAR tul_rate_tbl[8] = /* fast 20      */
+{
+                               /* nanosecond devide by 4 */
+       12,                     /* 50ns,  20M   */
+       18,                     /* 75ns,  13.3M */
+       25,                     /* 100ns, 10M   */
+       31,                     /* 125ns, 8M    */
+       37,                     /* 150ns, 6.6M  */
+       43,                     /* 175ns, 5.7M  */
+       50,                     /* 200ns, 5M    */
+       62                      /* 250ns, 4M    */
+};
+
+static void tul_do_pause(unsigned amount)
+{                              /* Pause for amount jiffies */
+       unsigned long the_time = jiffies + amount;
+
+       while (time_before_eq(jiffies, the_time));
+}
+
+/*-- forward reference --*/
+
+/*******************************************************************
+       Use memeory refresh time        ~ 15us * 2
+********************************************************************/
+void tul_se2_wait(void)
+{
+#if 1
+       udelay(30);
+#else
+       UCHAR readByte;
+
+       readByte = TUL_RD(0, 0x61);
+       if ((readByte & 0x10) == 0x10) {
+               for (;;) {
+                       readByte = TUL_RD(0, 0x61);
+                       if ((readByte & 0x10) == 0x10)
+                               break;
+               }
+               for (;;) {
+                       readByte = TUL_RD(0, 0x61);
+                       if ((readByte & 0x10) != 0x10)
+                               break;
+               }
+       } else {
+               for (;;) {
+                       readByte = TUL_RD(0, 0x61);
+                       if ((readByte & 0x10) == 0x10)
+                               break;
+               }
+               for (;;) {
+                       readByte = TUL_RD(0, 0x61);
+                       if ((readByte & 0x10) != 0x10)
+                               break;
+               }
+       }
+#endif
+}
+
+
+/******************************************************************
+ Input: instruction for  Serial E2PROM
+
+ EX: se2_rd(0 call se2_instr() to send address and read command
+
+        StartBit  OP_Code   Address                Data
+        --------- --------  ------------------     -------
+        1         1 , 0     A5,A4,A3,A2,A1,A0      D15-D0
+
+                +-----------------------------------------------------
+                |
+ CS -----+
+                       +--+  +--+  +--+  +--+  +--+
+                       ^  |  ^  |  ^  |  ^  |  ^  |
+                       |  |  |  |  |  |  |  |  |  |
+ CLK -------+  +--+  +--+  +--+  +--+  +--
+ (leading edge trigger)
+
+                +--1-----1--+
+                | SB    OP  |  OP    A5    A4
+ DI  ----+           +--0------------------
+ (address and cmd sent to nvram)
+
+        -------------------------------------------+
+                                                                                               |
+ DO                                             +---
+ (data sent from nvram)
+
+
+******************************************************************/
+void tul_se2_instr(WORD CurBase, UCHAR instr)
+{
+       int i;
+       UCHAR b;
+
+       TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO);     /* cs+start bit */
+       tul_se2_wait();
+       TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK | SE2DO);    /* +CLK */
+       tul_se2_wait();
+
+       for (i = 0; i < 8; i++) {
+               if (instr & 0x80)
+                       b = SE2CS | SE2DO;      /* -CLK+dataBit */
+               else
+                       b = SE2CS;      /* -CLK */
+               TUL_WR(CurBase + TUL_NVRAM, b);
+               tul_se2_wait();
+               TUL_WR(CurBase + TUL_NVRAM, b | SE2CLK);        /* +CLK */
+               tul_se2_wait();
+               instr <<= 1;
+       }
+       TUL_WR(CurBase + TUL_NVRAM, SE2CS);     /* -CLK */
+       tul_se2_wait();
+       return;
+}
+
+
+/******************************************************************
+ Function name  : tul_se2_ew_en
+ Description    : Enable erase/write state of serial EEPROM
+******************************************************************/
+void tul_se2_ew_en(WORD CurBase)
+{
+       tul_se2_instr(CurBase, 0x30);   /* EWEN */
+       TUL_WR(CurBase + TUL_NVRAM, 0);         /* -CS  */
+       tul_se2_wait();
+       return;
+}
+
+
+/************************************************************************
+ Disable erase/write state of serial EEPROM
+*************************************************************************/
+void tul_se2_ew_ds(WORD CurBase)
+{
+       tul_se2_instr(CurBase, 0);      /* EWDS */
+       TUL_WR(CurBase + TUL_NVRAM, 0);         /* -CS  */
+       tul_se2_wait();
+       return;
+}
+
+
+/******************************************************************
+       Input  :address of Serial E2PROM
+       Output :value stored in  Serial E2PROM
+*******************************************************************/
+USHORT tul_se2_rd(WORD CurBase, ULONG adr)
+{
+       UCHAR instr, readByte;
+       USHORT readWord;
+       int i;
+
+       instr = (UCHAR) (adr | 0x80);
+       tul_se2_instr(CurBase, instr);  /* READ INSTR */
+       readWord = 0;
+
+       for (i = 15; i >= 0; i--) {
+               TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK);    /* +CLK */
+               tul_se2_wait();
+               TUL_WR(CurBase + TUL_NVRAM, SE2CS);     /* -CLK */
+
+               /* sample data after the following edge of clock  */
+               readByte = TUL_RD(CurBase, TUL_NVRAM);
+               readByte &= SE2DI;
+               readWord += (readByte << i);
+               tul_se2_wait(); /* 6/20/95 */
+       }
+
+       TUL_WR(CurBase + TUL_NVRAM, 0);         /* no chip select */
+       tul_se2_wait();
+       return readWord;
+}
+
+
+/******************************************************************
+ Input: new value in  Serial E2PROM, address of Serial E2PROM
+*******************************************************************/
+void tul_se2_wr(WORD CurBase, UCHAR adr, USHORT writeWord)
+{
+       UCHAR readByte;
+       UCHAR instr;
+       int i;
+
+       instr = (UCHAR) (adr | 0x40);
+       tul_se2_instr(CurBase, instr);  /* WRITE INSTR */
+       for (i = 15; i >= 0; i--) {
+               if (writeWord & 0x8000)
+                       TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO);     /* -CLK+dataBit 1 */
+               else
+                       TUL_WR(CurBase + TUL_NVRAM, SE2CS);     /* -CLK+dataBit 0 */
+               tul_se2_wait();
+               TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK);    /* +CLK */
+               tul_se2_wait();
+               writeWord <<= 1;
+       }
+       TUL_WR(CurBase + TUL_NVRAM, SE2CS);     /* -CLK */
+       tul_se2_wait();
+       TUL_WR(CurBase + TUL_NVRAM, 0);         /* -CS  */
+       tul_se2_wait();
+
+       TUL_WR(CurBase + TUL_NVRAM, SE2CS);     /* +CS  */
+       tul_se2_wait();
+
+       for (;;) {
+               TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK);    /* +CLK */
+               tul_se2_wait();
+               TUL_WR(CurBase + TUL_NVRAM, SE2CS);     /* -CLK */
+               tul_se2_wait();
+               if ((readByte = TUL_RD(CurBase, TUL_NVRAM)) & SE2DI)
+                       break;  /* write complete */
+       }
+       TUL_WR(CurBase + TUL_NVRAM, 0);         /* -CS */
+       return;
+}
+
+
+/***********************************************************************
+ Read SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+int tul_se2_rd_all(WORD CurBase)
+{
+       int i;
+       ULONG chksum = 0;
+       USHORT *np;
+
+       i91unvramp = &i91unvram;
+       np = (USHORT *) i91unvramp;
+       for (i = 0; i < 32; i++) {
+               *np++ = tul_se2_rd(CurBase, i);
+       }
+
+/*--------------------Is signature "ini" ok ? ----------------*/
+       if (i91unvramp->NVM_Signature != INI_SIGNATURE)
+               return -1;
+/*---------------------- Is ckecksum ok ? ----------------------*/
+       np = (USHORT *) i91unvramp;
+       for (i = 0; i < 31; i++)
+               chksum += *np++;
+       if (i91unvramp->NVM_CheckSum != (USHORT) chksum)
+               return -1;
+       return 1;
+}
+
+
+/***********************************************************************
+ Update SCSI H/A configuration parameters from serial EEPROM
+************************************************************************/
+void tul_se2_update_all(WORD CurBase)
+{                              /* setup default pattern */
+       int i;
+       ULONG chksum = 0;
+       USHORT *np, *np1;
+
+       i91unvramp = &i91unvram;
+       /* Calculate checksum first */
+       np = (USHORT *) i91udftNvRam;
+       for (i = 0; i < 31; i++)
+               chksum += *np++;
+       *np = (USHORT) chksum;
+       tul_se2_ew_en(CurBase); /* Enable write  */
+
+       np = (USHORT *) i91udftNvRam;
+       np1 = (USHORT *) i91unvramp;
+       for (i = 0; i < 32; i++, np++, np1++) {
+               if (*np != *np1) {
+                       tul_se2_wr(CurBase, i, *np);
+               }
+       }
+
+       tul_se2_ew_ds(CurBase); /* Disable write   */
+       return;
+}
+
+/*************************************************************************
+ Function name  : read_eeprom
+**************************************************************************/
+void tul_read_eeprom(WORD CurBase)
+{
+       UCHAR gctrl;
+
+       i91unvramp = &i91unvram;
+/*------Enable EEProm programming ---*/
+       gctrl = TUL_RD(CurBase, TUL_GCTRL);
+       TUL_WR(CurBase + TUL_GCTRL, gctrl | TUL_GCTRL_EEPROM_BIT);
+       if (tul_se2_rd_all(CurBase) != 1) {
+               tul_se2_update_all(CurBase);    /* setup default pattern */
+               tul_se2_rd_all(CurBase);        /* load again  */
+       }
+/*------ Disable EEProm programming ---*/
+       gctrl = TUL_RD(CurBase, TUL_GCTRL);
+       TUL_WR(CurBase + TUL_GCTRL, gctrl & ~TUL_GCTRL_EEPROM_BIT);
+}                              /* read_eeprom */
+
+int Addi91u_into_Adapter_table(WORD wBIOS, WORD wBASE, BYTE bInterrupt,
+                              BYTE bBus, BYTE bDevice)
+{
+       int i, j;
+
+       for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) {
+               if (i91u_adpt[i].ADPT_BIOS < wBIOS)
+                       continue;
+               if (i91u_adpt[i].ADPT_BIOS == wBIOS) {
+                       if (i91u_adpt[i].ADPT_BASE == wBASE) {
+                               if (i91u_adpt[i].ADPT_Bus != 0xFF)
+                                       return 1;
+                       } else if (i91u_adpt[i].ADPT_BASE < wBASE)
+                                       continue;
+               }
+               for (j = MAX_SUPPORTED_ADAPTERS - 1; j > i; j--) {
+                       i91u_adpt[j].ADPT_BASE = i91u_adpt[j - 1].ADPT_BASE;
+                       i91u_adpt[j].ADPT_INTR = i91u_adpt[j - 1].ADPT_INTR;
+                       i91u_adpt[j].ADPT_BIOS = i91u_adpt[j - 1].ADPT_BIOS;
+                       i91u_adpt[j].ADPT_Bus = i91u_adpt[j - 1].ADPT_Bus;
+                       i91u_adpt[j].ADPT_Device = i91u_adpt[j - 1].ADPT_Device;
+               }
+               i91u_adpt[i].ADPT_BASE = wBASE;
+               i91u_adpt[i].ADPT_INTR = bInterrupt;
+               i91u_adpt[i].ADPT_BIOS = wBIOS;
+               i91u_adpt[i].ADPT_Bus = bBus;
+               i91u_adpt[i].ADPT_Device = bDevice;
+               return 0;
+       }
+       return 1;
+}
+
+void init_i91uAdapter_table(void)
+{
+       int i;
+
+       for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) {  /* Initialize adapter structure */
+               i91u_adpt[i].ADPT_BIOS = 0xffff;
+               i91u_adpt[i].ADPT_BASE = 0xffff;
+               i91u_adpt[i].ADPT_INTR = 0xff;
+               i91u_adpt[i].ADPT_Bus = 0xff;
+               i91u_adpt[i].ADPT_Device = 0xff;
+       }
+       return;
+}
+
+void tul_stop_bm(HCS * pCurHcb)
+{
+
+       if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) {   /* if DMA xfer is pending, abort DMA xfer */
+               TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO);
+               /* wait Abort DMA xfer done */
+               while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0);
+       }
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+}
+
+/***************************************************************************/
+void get_tulipPCIConfig(HCS * pCurHcb, int ch_idx)
+{
+       pCurHcb->HCS_Base = i91u_adpt[ch_idx].ADPT_BASE;        /* Supply base address  */
+       pCurHcb->HCS_BIOS = i91u_adpt[ch_idx].ADPT_BIOS;        /* Supply BIOS address  */
+       pCurHcb->HCS_Intr = i91u_adpt[ch_idx].ADPT_INTR;        /* Supply interrupt line */
+       return;
+}
+
+/***************************************************************************/
+int tul_reset_scsi(HCS * pCurHcb, int seconds)
+{
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_BUS);
+
+       while (!((pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt)) & TSS_SCSIRST_INT));
+       /* reset tulip chip */
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, 0);
+
+       /* Stall for a while, wait for target's firmware ready,make it 2 sec ! */
+       /* SONY 5200 tape drive won't work if only stall for 1 sec */
+       tul_do_pause(seconds * HZ);
+
+       TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+       return (SCSI_RESET_SUCCESS);
+}
+
+/***************************************************************************/
+int init_tulip(HCS * pCurHcb, SCB * scbp, int tul_num_scb, BYTE * pbBiosAdr, int seconds)
+{
+       int i;
+       BYTE *pwFlags;
+       BYTE *pbHeads;
+       SCB *pTmpScb, *pPrevScb = NULL;
+
+       pCurHcb->HCS_NumScbs = tul_num_scb;
+       pCurHcb->HCS_Semaph = 1;
+       spin_lock_init(&pCurHcb->HCS_SemaphLock);
+       pCurHcb->HCS_JSStatus0 = 0;
+       pCurHcb->HCS_Scb = scbp;
+       pCurHcb->HCS_NxtPend = scbp;
+       pCurHcb->HCS_NxtAvail = scbp;
+       for (i = 0, pTmpScb = scbp; i < tul_num_scb; i++, pTmpScb++) {
+               pTmpScb->SCB_TagId = i;
+               if (i != 0)
+                       pPrevScb->SCB_NxtScb = pTmpScb;
+               pPrevScb = pTmpScb;
+       }
+       pPrevScb->SCB_NxtScb = NULL;
+       pCurHcb->HCS_ScbEnd = pTmpScb;
+       pCurHcb->HCS_FirstAvail = scbp;
+       pCurHcb->HCS_LastAvail = pPrevScb;
+       spin_lock_init(&pCurHcb->HCS_AvailLock);
+       pCurHcb->HCS_FirstPend = NULL;
+       pCurHcb->HCS_LastPend = NULL;
+       pCurHcb->HCS_FirstBusy = NULL;
+       pCurHcb->HCS_LastBusy = NULL;
+       pCurHcb->HCS_FirstDone = NULL;
+       pCurHcb->HCS_LastDone = NULL;
+       pCurHcb->HCS_ActScb = NULL;
+       pCurHcb->HCS_ActTcs = NULL;
+
+       tul_read_eeprom(pCurHcb->HCS_Base);
+/*---------- get H/A configuration -------------*/
+       if (i91unvramp->NVM_SCSIInfo[0].NVM_NumOfTarg == 8)
+               pCurHcb->HCS_MaxTar = 8;
+       else
+               pCurHcb->HCS_MaxTar = 16;
+
+       pCurHcb->HCS_Config = i91unvramp->NVM_SCSIInfo[0].NVM_ChConfig1;
+
+       pCurHcb->HCS_SCSI_ID = i91unvramp->NVM_SCSIInfo[0].NVM_ChSCSIID;
+       pCurHcb->HCS_IdMask = ~(1 << pCurHcb->HCS_SCSI_ID);
+
+#if CHK_PARITY
+       /* Enable parity error response */
+       TUL_WR(pCurHcb->HCS_Base + TUL_PCMD, TUL_RD(pCurHcb->HCS_Base, TUL_PCMD) | 0x40);
+#endif
+
+       /* Mask all the interrupt       */
+       TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+
+       tul_stop_bm(pCurHcb);
+       /* --- Initialize the tulip --- */
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_CHIP);
+
+       /* program HBA's SCSI ID        */
+       TUL_WR(pCurHcb->HCS_Base + TUL_SScsiId, pCurHcb->HCS_SCSI_ID << 4);
+
+       /* Enable Initiator Mode ,phase latch,alternate sync period mode,
+          disable SCSI reset */
+       if (pCurHcb->HCS_Config & HCC_EN_PAR)
+               pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT | TSC_EN_SCSI_PAR);
+       else
+               pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_SConf1);
+
+       /* Enable HW reselect           */
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, 0);
+
+       /* selection time out = 250 ms */
+       TUL_WR(pCurHcb->HCS_Base + TUL_STimeOut, 153);
+
+/*--------- Enable SCSI terminator -----*/
+       TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, (pCurHcb->HCS_Config & (HCC_ACT_TERM1 | HCC_ACT_TERM2)));
+       TUL_WR(pCurHcb->HCS_Base + TUL_GCTRL1,
+              ((pCurHcb->HCS_Config & HCC_AUTO_TERM) >> 4) | (TUL_RD(pCurHcb->HCS_Base, TUL_GCTRL1) & 0xFE));
+
+       for (i = 0,
+            pwFlags = & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config),
+            pbHeads = pbBiosAdr + 0x180;
+            i < pCurHcb->HCS_MaxTar;
+            i++, pwFlags++) {
+               pCurHcb->HCS_Tcs[i].TCS_Flags = *pwFlags & ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+               if (pCurHcb->HCS_Tcs[i].TCS_Flags & TCF_EN_255)
+                       pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63;
+               else
+                       pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0;
+               pCurHcb->HCS_Tcs[i].TCS_JS_Period = 0;
+               pCurHcb->HCS_Tcs[i].TCS_SConfig0 = pCurHcb->HCS_SConf1;
+               pCurHcb->HCS_Tcs[i].TCS_DrvHead = *pbHeads++;
+               if (pCurHcb->HCS_Tcs[i].TCS_DrvHead == 255)
+                       pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63;
+               else
+                       pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0;
+               pCurHcb->HCS_Tcs[i].TCS_DrvSector = *pbHeads++;
+               pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY;
+               pCurHcb->HCS_ActTags[i] = 0;
+               pCurHcb->HCS_MaxTags[i] = 0xFF;
+       }                       /* for                          */
+       printk("i91u: PCI Base=0x%04X, IRQ=%d, BIOS=0x%04X0, SCSI ID=%d\n",
+              pCurHcb->HCS_Base, pCurHcb->HCS_Intr,
+              pCurHcb->HCS_BIOS, pCurHcb->HCS_SCSI_ID);
+/*------------------- reset SCSI Bus ---------------------------*/
+       if (pCurHcb->HCS_Config & HCC_SCSI_RESET) {
+               printk("i91u: Reset SCSI Bus ... \n");
+               tul_reset_scsi(pCurHcb, seconds);
+       }
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCFG1, 0x17);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SIntEnable, 0xE9);
+       return (0);
+}
+
+/***************************************************************************/
+SCB *tul_alloc_scb(HCS * hcsp)
+{
+       SCB *pTmpScb;
+       ULONG flags;
+       spin_lock_irqsave(&(hcsp->HCS_AvailLock), flags);
+       if ((pTmpScb = hcsp->HCS_FirstAvail) != NULL) {
+#if DEBUG_QUEUE
+               printk("find scb at %08lx\n", (ULONG) pTmpScb);
+#endif
+               if ((hcsp->HCS_FirstAvail = pTmpScb->SCB_NxtScb) == NULL)
+                       hcsp->HCS_LastAvail = NULL;
+               pTmpScb->SCB_NxtScb = NULL;
+               pTmpScb->SCB_Status = SCB_RENT;
+       }
+       spin_unlock_irqrestore(&(hcsp->HCS_AvailLock), flags);
+       return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_release_scb(HCS * hcsp, SCB * scbp)
+{
+       ULONG flags;
+
+#if DEBUG_QUEUE
+       printk("Release SCB %lx; ", (ULONG) scbp);
+#endif
+       spin_lock_irqsave(&(hcsp->HCS_AvailLock), flags);
+       scbp->SCB_Srb = NULL;
+       scbp->SCB_Status = 0;
+       scbp->SCB_NxtScb = NULL;
+       if (hcsp->HCS_LastAvail != NULL) {
+               hcsp->HCS_LastAvail->SCB_NxtScb = scbp;
+               hcsp->HCS_LastAvail = scbp;
+       } else {
+               hcsp->HCS_FirstAvail = scbp;
+               hcsp->HCS_LastAvail = scbp;
+       }
+       spin_unlock_irqrestore(&(hcsp->HCS_AvailLock), flags);
+}
+
+/***************************************************************************/
+void tul_append_pend_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+       printk("Append pend SCB %lx; ", (ULONG) scbp);
+#endif
+       scbp->SCB_Status = SCB_PEND;
+       scbp->SCB_NxtScb = NULL;
+       if (pCurHcb->HCS_LastPend != NULL) {
+               pCurHcb->HCS_LastPend->SCB_NxtScb = scbp;
+               pCurHcb->HCS_LastPend = scbp;
+       } else {
+               pCurHcb->HCS_FirstPend = scbp;
+               pCurHcb->HCS_LastPend = scbp;
+       }
+}
+
+/***************************************************************************/
+void tul_push_pend_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+       printk("Push pend SCB %lx; ", (ULONG) scbp);
+#endif
+       scbp->SCB_Status = SCB_PEND;
+       if ((scbp->SCB_NxtScb = pCurHcb->HCS_FirstPend) != NULL) {
+               pCurHcb->HCS_FirstPend = scbp;
+       } else {
+               pCurHcb->HCS_FirstPend = scbp;
+               pCurHcb->HCS_LastPend = scbp;
+       }
+}
+
+/***************************************************************************/
+SCB *tul_find_first_pend_scb(HCS * pCurHcb)
+{
+       SCB *pFirstPend;
+
+
+       pFirstPend = pCurHcb->HCS_FirstPend;
+       while (pFirstPend != NULL) {
+               if (pFirstPend->SCB_Opcode != ExecSCSI) {
+                       return (pFirstPend);
+               }
+               if (pFirstPend->SCB_TagMsg == 0) {
+                       if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] == 0) &&
+                           !(pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) {
+                               return (pFirstPend);
+                       }
+               } else {
+                       if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] >=
+                         pCurHcb->HCS_MaxTags[pFirstPend->SCB_Target]) |
+                           (pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) {
+                               pFirstPend = pFirstPend->SCB_NxtScb;
+                               continue;
+                       }
+                       return (pFirstPend);
+               }
+               pFirstPend = pFirstPend->SCB_NxtScb;
+       }
+
+
+       return (pFirstPend);
+}
+/***************************************************************************/
+SCB *tul_pop_pend_scb(HCS * pCurHcb)
+{
+       SCB *pTmpScb;
+
+       if ((pTmpScb = pCurHcb->HCS_FirstPend) != NULL) {
+               if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+                       pCurHcb->HCS_LastPend = NULL;
+               pTmpScb->SCB_NxtScb = NULL;
+       }
+#if DEBUG_QUEUE
+       printk("Pop pend SCB %lx; ", (ULONG) pTmpScb);
+#endif
+       return (pTmpScb);
+}
+
+
+/***************************************************************************/
+void tul_unlink_pend_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+       SCB *pTmpScb, *pPrevScb;
+
+#if DEBUG_QUEUE
+       printk("unlink pend SCB %lx; ", (ULONG) pCurScb);
+#endif
+
+       pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend;
+       while (pTmpScb != NULL) {
+               if (pCurScb == pTmpScb) {       /* Unlink this SCB              */
+                       if (pTmpScb == pCurHcb->HCS_FirstPend) {
+                               if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+                                       pCurHcb->HCS_LastPend = NULL;
+                       } else {
+                               pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+                               if (pTmpScb == pCurHcb->HCS_LastPend)
+                                       pCurHcb->HCS_LastPend = pPrevScb;
+                       }
+                       pTmpScb->SCB_NxtScb = NULL;
+                       break;
+               }
+               pPrevScb = pTmpScb;
+               pTmpScb = pTmpScb->SCB_NxtScb;
+       }
+       return;
+}
+/***************************************************************************/
+void tul_append_busy_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+       printk("append busy SCB %lx; ", (ULONG) scbp);
+#endif
+       if (scbp->SCB_TagMsg)
+               pCurHcb->HCS_ActTags[scbp->SCB_Target]++;
+       else
+               pCurHcb->HCS_Tcs[scbp->SCB_Target].TCS_Flags |= TCF_BUSY;
+       scbp->SCB_Status = SCB_BUSY;
+       scbp->SCB_NxtScb = NULL;
+       if (pCurHcb->HCS_LastBusy != NULL) {
+               pCurHcb->HCS_LastBusy->SCB_NxtScb = scbp;
+               pCurHcb->HCS_LastBusy = scbp;
+       } else {
+               pCurHcb->HCS_FirstBusy = scbp;
+               pCurHcb->HCS_LastBusy = scbp;
+       }
+}
+
+/***************************************************************************/
+SCB *tul_pop_busy_scb(HCS * pCurHcb)
+{
+       SCB *pTmpScb;
+
+
+       if ((pTmpScb = pCurHcb->HCS_FirstBusy) != NULL) {
+               if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+                       pCurHcb->HCS_LastBusy = NULL;
+               pTmpScb->SCB_NxtScb = NULL;
+               if (pTmpScb->SCB_TagMsg)
+                       pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+               else
+                       pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY;
+       }
+#if DEBUG_QUEUE
+       printk("Pop busy SCB %lx; ", (ULONG) pTmpScb);
+#endif
+       return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_unlink_busy_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+       SCB *pTmpScb, *pPrevScb;
+
+#if DEBUG_QUEUE
+       printk("unlink busy SCB %lx; ", (ULONG) pCurScb);
+#endif
+
+       pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;
+       while (pTmpScb != NULL) {
+               if (pCurScb == pTmpScb) {       /* Unlink this SCB              */
+                       if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+                               if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+                                       pCurHcb->HCS_LastBusy = NULL;
+                       } else {
+                               pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+                               if (pTmpScb == pCurHcb->HCS_LastBusy)
+                                       pCurHcb->HCS_LastBusy = pPrevScb;
+                       }
+                       pTmpScb->SCB_NxtScb = NULL;
+                       if (pTmpScb->SCB_TagMsg)
+                               pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+                       else
+                               pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY;
+                       break;
+               }
+               pPrevScb = pTmpScb;
+               pTmpScb = pTmpScb->SCB_NxtScb;
+       }
+       return;
+}
+
+/***************************************************************************/
+SCB *tul_find_busy_scb(HCS * pCurHcb, WORD tarlun)
+{
+       SCB *pTmpScb, *pPrevScb;
+       WORD scbp_tarlun;
+
+
+       pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;
+       while (pTmpScb != NULL) {
+               scbp_tarlun = (pTmpScb->SCB_Lun << 8) | (pTmpScb->SCB_Target);
+               if (scbp_tarlun == tarlun) {    /* Unlink this SCB              */
+                       break;
+               }
+               pPrevScb = pTmpScb;
+               pTmpScb = pTmpScb->SCB_NxtScb;
+       }
+#if DEBUG_QUEUE
+       printk("find busy SCB %lx; ", (ULONG) pTmpScb);
+#endif
+       return (pTmpScb);
+}
+
+/***************************************************************************/
+void tul_append_done_scb(HCS * pCurHcb, SCB * scbp)
+{
+
+#if DEBUG_QUEUE
+       printk("append done SCB %lx; ", (ULONG) scbp);
+#endif
+
+       scbp->SCB_Status = SCB_DONE;
+       scbp->SCB_NxtScb = NULL;
+       if (pCurHcb->HCS_LastDone != NULL) {
+               pCurHcb->HCS_LastDone->SCB_NxtScb = scbp;
+               pCurHcb->HCS_LastDone = scbp;
+       } else {
+               pCurHcb->HCS_FirstDone = scbp;
+               pCurHcb->HCS_LastDone = scbp;
+       }
+}
+
+/***************************************************************************/
+SCB *tul_find_done_scb(HCS * pCurHcb)
+{
+       SCB *pTmpScb;
+
+
+       if ((pTmpScb = pCurHcb->HCS_FirstDone) != NULL) {
+               if ((pCurHcb->HCS_FirstDone = pTmpScb->SCB_NxtScb) == NULL)
+                       pCurHcb->HCS_LastDone = NULL;
+               pTmpScb->SCB_NxtScb = NULL;
+       }
+#if DEBUG_QUEUE
+       printk("find done SCB %lx; ", (ULONG) pTmpScb);
+#endif
+       return (pTmpScb);
+}
+
+/***************************************************************************/
+int tul_abort_srb(HCS * pCurHcb, struct scsi_cmnd *srbp)
+{
+       ULONG flags;
+       SCB *pTmpScb, *pPrevScb;
+
+       spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+       if ((pCurHcb->HCS_Semaph == 0) && (pCurHcb->HCS_ActScb == NULL)) {
+               TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+               /* disable Jasmin SCSI Int        */
+
+                spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+               tulip_main(pCurHcb);
+
+               spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+               pCurHcb->HCS_Semaph = 1;
+               TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+
+               spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+               return SCSI_ABORT_SNOOZE;
+       }
+       pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend;    /* Check Pend queue */
+       while (pTmpScb != NULL) {
+               /* 07/27/98 */
+               if (pTmpScb->SCB_Srb == srbp) {
+                       if (pTmpScb == pCurHcb->HCS_ActScb) {
+                               spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+                               return SCSI_ABORT_BUSY;
+                       } else if (pTmpScb == pCurHcb->HCS_FirstPend) {
+                               if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL)
+                                       pCurHcb->HCS_LastPend = NULL;
+                       } else {
+                               pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+                               if (pTmpScb == pCurHcb->HCS_LastPend)
+                                       pCurHcb->HCS_LastPend = pPrevScb;
+                       }
+                       pTmpScb->SCB_HaStat = HOST_ABORTED;
+                       pTmpScb->SCB_Flags |= SCF_DONE;
+                       if (pTmpScb->SCB_Flags & SCF_POST)
+                               (*pTmpScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pTmpScb);
+                       spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+                       return SCSI_ABORT_SUCCESS;
+               }
+               pPrevScb = pTmpScb;
+               pTmpScb = pTmpScb->SCB_NxtScb;
+       }
+
+       pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;    /* Check Busy queue */
+       while (pTmpScb != NULL) {
+
+               if (pTmpScb->SCB_Srb == srbp) {
+
+                       if (pTmpScb == pCurHcb->HCS_ActScb) {
+                               spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+                               return SCSI_ABORT_BUSY;
+                       } else if (pTmpScb->SCB_TagMsg == 0) {
+                               spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+                               return SCSI_ABORT_BUSY;
+                       } else {
+                               pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--;
+                               if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+                                       if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+                                               pCurHcb->HCS_LastBusy = NULL;
+                               } else {
+                                       pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+                                       if (pTmpScb == pCurHcb->HCS_LastBusy)
+                                               pCurHcb->HCS_LastBusy = pPrevScb;
+                               }
+                               pTmpScb->SCB_NxtScb = NULL;
+
+
+                               pTmpScb->SCB_HaStat = HOST_ABORTED;
+                               pTmpScb->SCB_Flags |= SCF_DONE;
+                               if (pTmpScb->SCB_Flags & SCF_POST)
+                                       (*pTmpScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pTmpScb);
+                               spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+                               return SCSI_ABORT_SUCCESS;
+                       }
+               }
+               pPrevScb = pTmpScb;
+               pTmpScb = pTmpScb->SCB_NxtScb;
+       }
+       spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+       return (SCSI_ABORT_NOT_RUNNING);
+}
+
+/***************************************************************************/
+int tul_bad_seq(HCS * pCurHcb)
+{
+       SCB *pCurScb;
+
+       printk("tul_bad_seg c=%d\n", pCurHcb->HCS_Index);
+
+       if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) {
+               tul_unlink_busy_scb(pCurHcb, pCurScb);
+               pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+               pCurScb->SCB_TaStat = 0;
+               tul_append_done_scb(pCurHcb, pCurScb);
+       }
+       tul_stop_bm(pCurHcb);
+
+       tul_reset_scsi(pCurHcb, 8);     /* 7/29/98 */
+
+       return (tul_post_scsi_rst(pCurHcb));
+}
+
+/************************************************************************/
+int tul_device_reset(HCS * pCurHcb, struct scsi_cmnd *pSrb,
+               unsigned int target, unsigned int ResetFlags)
+{
+       ULONG flags;
+       SCB *pScb;
+       spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+       if (ResetFlags & SCSI_RESET_ASYNCHRONOUS) {
+
+               if ((pCurHcb->HCS_Semaph == 0) && (pCurHcb->HCS_ActScb == NULL)) {
+                       TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+                       /* disable Jasmin SCSI Int        */
+
+                       spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+                       tulip_main(pCurHcb);
+
+                       spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+                       pCurHcb->HCS_Semaph = 1;
+                       TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+
+                       spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+                       return SCSI_RESET_SNOOZE;
+               }
+               pScb = pCurHcb->HCS_FirstBusy;  /* Check Busy queue */
+               while (pScb != NULL) {
+                       if (pScb->SCB_Srb == pSrb)
+                               break;
+                       pScb = pScb->SCB_NxtScb;
+               }
+               if (pScb == NULL) {
+                       printk("Unable to Reset - No SCB Found\n");
+
+                       spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+                       return SCSI_RESET_NOT_RUNNING;
+               }
+       }
+       if ((pScb = tul_alloc_scb(pCurHcb)) == NULL) {
+               spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+               return SCSI_RESET_NOT_RUNNING;
+       }
+       pScb->SCB_Opcode = BusDevRst;
+       pScb->SCB_Flags = SCF_POST;
+       pScb->SCB_Target = target;
+       pScb->SCB_Mode = 0;
+
+       pScb->SCB_Srb = NULL;
+       if (ResetFlags & SCSI_RESET_SYNCHRONOUS) {
+               pScb->SCB_Srb = pSrb;
+       }
+       tul_push_pend_scb(pCurHcb, pScb);       /* push this SCB to Pending queue */
+
+       if (pCurHcb->HCS_Semaph == 1) {
+               TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+               /* disable Jasmin SCSI Int        */
+               pCurHcb->HCS_Semaph = 0;
+
+               spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+               tulip_main(pCurHcb);
+
+                spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+               pCurHcb->HCS_Semaph = 1;
+               TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+       }
+       spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+       return SCSI_RESET_PENDING;
+}
+
+int tul_reset_scsi_bus(HCS * pCurHcb)
+{
+       ULONG flags;
+
+       spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+       TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+       pCurHcb->HCS_Semaph = 0;
+
+       spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+       tul_stop_bm(pCurHcb);
+
+       tul_reset_scsi(pCurHcb, 2);     /* 7/29/98 */
+
+       spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+       tul_post_scsi_rst(pCurHcb);
+
+        spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+       tulip_main(pCurHcb);
+
+        spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+       pCurHcb->HCS_Semaph = 1;
+       TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+       spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+       return (SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET);
+}
+
+/************************************************************************/
+void tul_exec_scb(HCS * pCurHcb, SCB * pCurScb)
+{
+       ULONG flags;
+
+       pCurScb->SCB_Mode = 0;
+
+       pCurScb->SCB_SGIdx = 0;
+       pCurScb->SCB_SGMax = pCurScb->SCB_SGLen;
+
+       spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+       tul_append_pend_scb(pCurHcb, pCurScb);  /* Append this SCB to Pending queue */
+
+/* VVVVV 07/21/98 */
+       if (pCurHcb->HCS_Semaph == 1) {
+               TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+               /* disable Jasmin SCSI Int        */
+               pCurHcb->HCS_Semaph = 0;
+
+               spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+
+               tulip_main(pCurHcb);
+
+               spin_lock_irqsave(&(pCurHcb->HCS_SemaphLock), flags);
+
+               pCurHcb->HCS_Semaph = 1;
+               TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+       }
+       spin_unlock_irqrestore(&(pCurHcb->HCS_SemaphLock), flags);
+       return;
+}
+
+/***************************************************************************/
+int tul_isr(HCS * pCurHcb)
+{
+       /* Enter critical section       */
+
+       if (TUL_RD(pCurHcb->HCS_Base, TUL_Int) & TSS_INT_PENDING) {
+               if (pCurHcb->HCS_Semaph == 1) {
+                       TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F);
+                       /* Disable Tulip SCSI Int */
+                       pCurHcb->HCS_Semaph = 0;
+
+                       tulip_main(pCurHcb);
+
+                       pCurHcb->HCS_Semaph = 1;
+                       TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F);
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+/***************************************************************************/
+int tulip_main(HCS * pCurHcb)
+{
+       SCB *pCurScb;
+
+       for (;;) {
+
+               tulip_scsi(pCurHcb);    /* Call tulip_scsi              */
+
+               while ((pCurScb = tul_find_done_scb(pCurHcb)) != NULL) {        /* find done entry */
+                       if (pCurScb->SCB_TaStat == INI_QUEUE_FULL) {
+                               pCurHcb->HCS_MaxTags[pCurScb->SCB_Target] =
+                                   pCurHcb->HCS_ActTags[pCurScb->SCB_Target] - 1;
+                               pCurScb->SCB_TaStat = 0;
+                               tul_append_pend_scb(pCurHcb, pCurScb);
+                               continue;
+                       }
+                       if (!(pCurScb->SCB_Mode & SCM_RSENS)) {         /* not in auto req. sense mode */
+                               if (pCurScb->SCB_TaStat == 2) {
+
+                                       /* clr sync. nego flag */
+
+                                       if (pCurScb->SCB_Flags & SCF_SENSE) {
+                                               BYTE len;
+                                               len = pCurScb->SCB_SenseLen;
+                                               if (len == 0)
+                                                       len = 1;
+                                               pCurScb->SCB_BufLen = pCurScb->SCB_SenseLen;
+                                               pCurScb->SCB_BufPtr = pCurScb->SCB_SensePtr;
+                                               pCurScb->SCB_Flags &= ~(SCF_SG | SCF_DIR);      /* for xfer_data_in */
+/*                      pCurScb->SCB_Flags |= SCF_NO_DCHK;      */
+                                               /* so, we won't report worng direction in xfer_data_in,
+                                                  and won't report HOST_DO_DU in state_6 */
+                                               pCurScb->SCB_Mode = SCM_RSENS;
+                                               pCurScb->SCB_Ident &= 0xBF;     /* Disable Disconnect */
+                                               pCurScb->SCB_TagMsg = 0;
+                                               pCurScb->SCB_TaStat = 0;
+                                               pCurScb->SCB_CDBLen = 6;
+                                               pCurScb->SCB_CDB[0] = SCSICMD_RequestSense;
+                                               pCurScb->SCB_CDB[1] = 0;
+                                               pCurScb->SCB_CDB[2] = 0;
+                                               pCurScb->SCB_CDB[3] = 0;
+                                               pCurScb->SCB_CDB[4] = len;
+                                               pCurScb->SCB_CDB[5] = 0;
+                                               tul_push_pend_scb(pCurHcb, pCurScb);
+                                               break;
+                                       }
+                               }
+                       } else {        /* in request sense mode */
+
+                               if (pCurScb->SCB_TaStat == 2) {         /* check contition status again after sending
+                                                                          requset sense cmd 0x3 */
+                                       pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+                               }
+                               pCurScb->SCB_TaStat = 2;
+                       }
+                       pCurScb->SCB_Flags |= SCF_DONE;
+                       if (pCurScb->SCB_Flags & SCF_POST) {
+                               (*pCurScb->SCB_Post) ((BYTE *) pCurHcb, (BYTE *) pCurScb);
+                       }
+               }               /* while */
+
+               /* find_active: */
+               if (TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0) & TSS_INT_PENDING)
+                       continue;
+
+               if (pCurHcb->HCS_ActScb) {      /* return to OS and wait for xfer_done_ISR/Selected_ISR */
+                       return 1;       /* return to OS, enable interrupt */
+               }
+               /* Check pending SCB            */
+               if (tul_find_first_pend_scb(pCurHcb) == NULL) {
+                       return 1;       /* return to OS, enable interrupt */
+               }
+       }                       /* End of for loop */
+       /* statement won't reach here */
+}
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+/***************************************************************************/
+void tulip_scsi(HCS * pCurHcb)
+{
+       SCB *pCurScb;
+       TCS *pCurTcb;
+
+       /* make sure to service interrupt asap */
+
+       if ((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0)) & TSS_INT_PENDING) {
+
+               pCurHcb->HCS_Phase = pCurHcb->HCS_JSStatus0 & TSS_PH_MASK;
+               pCurHcb->HCS_JSStatus1 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1);
+               pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+               if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) {     /* SCSI bus reset detected      */
+                       int_tul_scsi_rst(pCurHcb);
+                       return;
+               }
+               if (pCurHcb->HCS_JSInt & TSS_RESEL_INT) {       /* if selected/reselected interrupt */
+                       if (int_tul_resel(pCurHcb) == 0)
+                               tul_next_state(pCurHcb);
+                       return;
+               }
+               if (pCurHcb->HCS_JSInt & TSS_SEL_TIMEOUT) {
+                       int_tul_busfree(pCurHcb);
+                       return;
+               }
+               if (pCurHcb->HCS_JSInt & TSS_DISC_INT) {        /* BUS disconnection            */
+                       int_tul_busfree(pCurHcb);       /* unexpected bus free or sel timeout */
+                       return;
+               }
+               if (pCurHcb->HCS_JSInt & (TSS_FUNC_COMP | TSS_BUS_SERV)) {      /* func complete or Bus service */
+                       if ((pCurScb = pCurHcb->HCS_ActScb) != NULL)
+                               tul_next_state(pCurHcb);
+                       return;
+               }
+       }
+       if (pCurHcb->HCS_ActScb != NULL)
+               return;
+
+       if ((pCurScb = tul_find_first_pend_scb(pCurHcb)) == NULL)
+               return;
+
+       /* program HBA's SCSI ID & target SCSI ID */
+       TUL_WR(pCurHcb->HCS_Base + TUL_SScsiId,
+            (pCurHcb->HCS_SCSI_ID << 4) | (pCurScb->SCB_Target & 0x0F));
+       if (pCurScb->SCB_Opcode == ExecSCSI) {
+               pCurTcb = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+
+               if (pCurScb->SCB_TagMsg)
+                       pCurTcb->TCS_DrvFlags |= TCF_DRV_EN_TAG;
+               else
+                       pCurTcb->TCS_DrvFlags &= ~TCF_DRV_EN_TAG;
+
+               TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurTcb->TCS_JS_Period);
+               if ((pCurTcb->TCS_Flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) {        /* do wdtr negotiation          */
+                       tul_select_atn_stop(pCurHcb, pCurScb);
+               } else {
+                       if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {   /* do sync negotiation          */
+                               tul_select_atn_stop(pCurHcb, pCurScb);
+                       } else {
+                               if (pCurScb->SCB_TagMsg)
+                                       tul_select_atn3(pCurHcb, pCurScb);
+                               else
+                                       tul_select_atn(pCurHcb, pCurScb);
+                       }
+               }
+               if (pCurScb->SCB_Flags & SCF_POLL) {
+                       while (wait_tulip(pCurHcb) != -1) {
+                               if (tul_next_state(pCurHcb) == -1)
+                                       break;
+                       }
+               }
+       } else if (pCurScb->SCB_Opcode == BusDevRst) {
+               tul_select_atn_stop(pCurHcb, pCurScb);
+               pCurScb->SCB_NxtStat = 8;
+               if (pCurScb->SCB_Flags & SCF_POLL) {
+                       while (wait_tulip(pCurHcb) != -1) {
+                               if (tul_next_state(pCurHcb) == -1)
+                                       break;
+                       }
+               }
+       } else if (pCurScb->SCB_Opcode == AbortCmd) {
+               if (tul_abort_srb(pCurHcb, pCurScb->SCB_Srb) != 0) {
+
+
+                       tul_unlink_pend_scb(pCurHcb, pCurScb);
+
+                       tul_release_scb(pCurHcb, pCurScb);
+               } else {
+                       pCurScb->SCB_Opcode = BusDevRst;
+                       tul_select_atn_stop(pCurHcb, pCurScb);
+                       pCurScb->SCB_NxtStat = 8;
+               }
+
+/* 08/03/98 */
+       } else {
+               tul_unlink_pend_scb(pCurHcb, pCurScb);
+               pCurScb->SCB_HaStat = 0x16;     /* bad command */
+               tul_append_done_scb(pCurHcb, pCurScb);
+       }
+       return;
+}
+
+
+/***************************************************************************/
+int tul_next_state(HCS * pCurHcb)
+{
+       int next;
+
+       next = pCurHcb->HCS_ActScb->SCB_NxtStat;
+       for (;;) {
+               switch (next) {
+               case 1:
+                       next = tul_state_1(pCurHcb);
+                       break;
+               case 2:
+                       next = tul_state_2(pCurHcb);
+                       break;
+               case 3:
+                       next = tul_state_3(pCurHcb);
+                       break;
+               case 4:
+                       next = tul_state_4(pCurHcb);
+                       break;
+               case 5:
+                       next = tul_state_5(pCurHcb);
+                       break;
+               case 6:
+                       next = tul_state_6(pCurHcb);
+                       break;
+               case 7:
+                       next = tul_state_7(pCurHcb);
+                       break;
+               case 8:
+                       return (tul_bus_device_reset(pCurHcb));
+               default:
+                       return (tul_bad_seq(pCurHcb));
+               }
+               if (next <= 0)
+                       return next;
+       }
+}
+
+
+/***************************************************************************/
+/* sTate after selection with attention & stop */
+int tul_state_1(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+       TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+#if DEBUG_STATE
+       printk("-s1-");
+#endif
+
+       tul_unlink_pend_scb(pCurHcb, pCurScb);
+       tul_append_busy_scb(pCurHcb, pCurScb);
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+       /* ATN on */
+       if (pCurHcb->HCS_Phase == MSG_OUT) {
+
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, (TSC_EN_BUS_IN | TSC_HW_RESELECT));
+
+               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+
+               if (pCurScb->SCB_TagMsg) {
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagMsg);
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagId);
+               }
+               if ((pCurTcb->TCS_Flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) {
+
+                       pCurTcb->TCS_Flags |= TCF_WDTR_DONE;
+
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 2);       /* Extended msg length */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);       /* Sync request */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1);       /* Start from 16 bits */
+               } else if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {
+
+                       pCurTcb->TCS_Flags |= TCF_SYNC_DONE;
+
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);       /* extended msg length */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1);       /* sync request */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, tul_rate_tbl[pCurTcb->TCS_Flags & TCF_SCSI_RATE]);
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MAX_OFFSET);      /* REQ/ACK offset */
+               }
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+               if (wait_tulip(pCurHcb) == -1)
+                       return (-1);
+       }
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+       return (3);
+}
+
+
+/***************************************************************************/
+/* state after selection with attention */
+/* state after selection with attention3 */
+int tul_state_2(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+       TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+#if DEBUG_STATE
+       printk("-s2-");
+#endif
+
+       tul_unlink_pend_scb(pCurHcb, pCurScb);
+       tul_append_busy_scb(pCurHcb, pCurScb);
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+
+       if (pCurHcb->HCS_JSStatus1 & TSS_CMD_PH_CMP) {
+               return (4);
+       }
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+       return (3);
+}
+
+/***************************************************************************/
+/* state before CDB xfer is done */
+int tul_state_3(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+       TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+       int i;
+
+#if DEBUG_STATE
+       printk("-s3-");
+#endif
+       for (;;) {
+               switch (pCurHcb->HCS_Phase) {
+               case CMD_OUT:   /* Command out phase            */
+                       for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+                       if (wait_tulip(pCurHcb) == -1)
+                               return (-1);
+                       if (pCurHcb->HCS_Phase == CMD_OUT) {
+                               return (tul_bad_seq(pCurHcb));
+                       }
+                       return (4);
+
+               case MSG_IN:    /* Message in phase             */
+                       pCurScb->SCB_NxtStat = 3;
+                       if (tul_msgin(pCurHcb) == -1)
+                               return (-1);
+                       break;
+
+               case STATUS_IN: /* Status phase                 */
+                       if (tul_status_msg(pCurHcb) == -1)
+                               return (-1);
+                       break;
+
+               case MSG_OUT:   /* Message out phase            */
+                       if (pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) {
+
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP);         /* msg nop */
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+                               if (wait_tulip(pCurHcb) == -1)
+                                       return (-1);
+
+                       } else {
+                               pCurTcb->TCS_Flags |= TCF_SYNC_DONE;
+
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);       /* ext. msg len */
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1);       /* sync request */
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, tul_rate_tbl[pCurTcb->TCS_Flags & TCF_SCSI_RATE]);
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MAX_OFFSET);      /* REQ/ACK offset */
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+                               if (wait_tulip(pCurHcb) == -1)
+                                       return (-1);
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7));
+
+                       }
+                       break;
+
+               default:
+                       return (tul_bad_seq(pCurHcb));
+               }
+       }
+}
+
+
+/***************************************************************************/
+int tul_state_4(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+#if DEBUG_STATE
+       printk("-s4-");
+#endif
+       if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_NO_XF) {
+               return (6);     /* Go to state 6                */
+       }
+       for (;;) {
+               if (pCurScb->SCB_BufLen == 0)
+                       return (6);     /* Go to state 6                */
+
+               switch (pCurHcb->HCS_Phase) {
+
+               case STATUS_IN: /* Status phase                 */
+                       if ((pCurScb->SCB_Flags & SCF_DIR) != 0) {      /* if direction bit set then report data underrun */
+                               pCurScb->SCB_HaStat = HOST_DO_DU;
+                       }
+                       if ((tul_status_msg(pCurHcb)) == -1)
+                               return (-1);
+                       break;
+
+               case MSG_IN:    /* Message in phase             */
+                       pCurScb->SCB_NxtStat = 0x4;
+                       if (tul_msgin(pCurHcb) == -1)
+                               return (-1);
+                       break;
+
+               case MSG_OUT:   /* Message out phase            */
+                       if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+                               pCurScb->SCB_BufLen = 0;
+                               pCurScb->SCB_HaStat = HOST_DO_DU;
+                               if (tul_msgout_ide(pCurHcb) == -1)
+                                       return (-1);
+                               return (6);     /* Go to state 6                */
+                       } else {
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP);         /* msg nop */
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+                               if (wait_tulip(pCurHcb) == -1)
+                                       return (-1);
+                       }
+                       break;
+
+               case DATA_IN:   /* Data in phase                */
+                       return (tul_xfer_data_in(pCurHcb));
+
+               case DATA_OUT:  /* Data out phase               */
+                       return (tul_xfer_data_out(pCurHcb));
+
+               default:
+                       return (tul_bad_seq(pCurHcb));
+               }
+       }
+}
+
+
+/***************************************************************************/
+/* state after dma xfer done or phase change before xfer done */
+int tul_state_5(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+       long cnt, xcnt;         /* cannot use unsigned !! code: if (xcnt < 0) */
+
+#if DEBUG_STATE
+       printk("-s5-");
+#endif
+/*------ get remaining count -------*/
+
+       cnt = TUL_RDLONG(pCurHcb->HCS_Base, TUL_SCnt0) & 0x0FFFFFF;
+
+       if (TUL_RD(pCurHcb->HCS_Base, TUL_XCmd) & 0x20) {
+               /* ----------------------- DATA_IN ----------------------------- */
+               /* check scsi parity error */
+               if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+                       pCurScb->SCB_HaStat = HOST_DO_DU;
+               }
+               if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) {   /* DMA xfer pending, Send STOP  */
+                       /* tell Hardware  scsi xfer has been terminated */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, TUL_RD(pCurHcb->HCS_Base, TUL_XCtrl) | 0x80);
+                       /* wait until DMA xfer not pending */
+                       while (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND);
+               }
+       } else {
+/*-------- DATA OUT -----------*/
+               if ((TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1) & TSS_XFER_CMP) == 0) {
+                       if (pCurHcb->HCS_ActTcs->TCS_JS_Period & TSC_WIDE_SCSI)
+                               cnt += (TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F) << 1;
+                       else
+                               cnt += (TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F);
+               }
+               if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) {   /* if DMA xfer is pending, abort DMA xfer */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT);
+                       /* wait Abort DMA xfer done */
+                       while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0);
+               }
+               if ((cnt == 1) && (pCurHcb->HCS_Phase == DATA_OUT)) {
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+                       if (wait_tulip(pCurHcb) == -1) {
+                               return (-1);
+                       }
+                       cnt = 0;
+               } else {
+                       if ((TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1) & TSS_XFER_CMP) == 0)
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+               }
+       }
+
+       if (cnt == 0) {
+               pCurScb->SCB_BufLen = 0;
+               return (6);     /* Go to state 6                */
+       }
+       /* Update active data pointer */
+       xcnt = (long) pCurScb->SCB_BufLen - cnt;        /* xcnt== bytes already xferred */
+       pCurScb->SCB_BufLen = (U32) cnt;        /* cnt == bytes left to be xferred */
+       if (pCurScb->SCB_Flags & SCF_SG) {
+               register SG *sgp;
+               ULONG i;
+
+               sgp = &pCurScb->SCB_SGList[pCurScb->SCB_SGIdx];
+               for (i = pCurScb->SCB_SGIdx; i < pCurScb->SCB_SGMax; sgp++, i++) {
+                       xcnt -= (long) sgp->SG_Len;
+                       if (xcnt < 0) {         /* this sgp xfer half done */
+                               xcnt += (long) sgp->SG_Len;     /* xcnt == bytes xferred in this sgp */
+                               sgp->SG_Ptr += (U32) xcnt;      /* new ptr to be xfer */
+                               sgp->SG_Len -= (U32) xcnt;      /* new len to be xfer */
+                               pCurScb->SCB_BufPtr += ((U32) (i - pCurScb->SCB_SGIdx) << 3);
+                               /* new SG table ptr */
+                               pCurScb->SCB_SGLen = (BYTE) (pCurScb->SCB_SGMax - i);
+                               /* new SG table len */
+                               pCurScb->SCB_SGIdx = (WORD) i;
+                               /* for next disc and come in this loop */
+                               return (4);     /* Go to state 4                */
+                       }
+                       /* else (xcnt >= 0 , i.e. this sgp already xferred */
+               }               /* for */
+               return (6);     /* Go to state 6                */
+       } else {
+               pCurScb->SCB_BufPtr += (U32) xcnt;
+       }
+       return (4);             /* Go to state 4                */
+}
+
+/***************************************************************************/
+/* state after Data phase */
+int tul_state_6(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+#if DEBUG_STATE
+       printk("-s6-");
+#endif
+       for (;;) {
+               switch (pCurHcb->HCS_Phase) {
+               case STATUS_IN: /* Status phase                 */
+                       if ((tul_status_msg(pCurHcb)) == -1)
+                               return (-1);
+                       break;
+
+               case MSG_IN:    /* Message in phase             */
+                       pCurScb->SCB_NxtStat = 6;
+                       if ((tul_msgin(pCurHcb)) == -1)
+                               return (-1);
+                       break;
+
+               case MSG_OUT:   /* Message out phase            */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP);         /* msg nop */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+                       if (wait_tulip(pCurHcb) == -1)
+                               return (-1);
+                       break;
+
+               case DATA_IN:   /* Data in phase                */
+                       return (tul_xpad_in(pCurHcb));
+
+               case DATA_OUT:  /* Data out phase               */
+                       return (tul_xpad_out(pCurHcb));
+
+               default:
+                       return (tul_bad_seq(pCurHcb));
+               }
+       }
+}
+
+/***************************************************************************/
+int tul_state_7(HCS * pCurHcb)
+{
+       int cnt, i;
+
+#if DEBUG_STATE
+       printk("-s7-");
+#endif
+       /* flush SCSI FIFO */
+       cnt = TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F;
+       if (cnt) {
+               for (i = 0; i < cnt; i++)
+                       TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+       }
+       switch (pCurHcb->HCS_Phase) {
+       case DATA_IN:           /* Data in phase                */
+       case DATA_OUT:          /* Data out phase               */
+               return (tul_bad_seq(pCurHcb));
+       default:
+               return (6);     /* Go to state 6                */
+       }
+}
+
+/***************************************************************************/
+int tul_xfer_data_in(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+       if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_DOUT) {
+               return (6);     /* wrong direction */
+       }
+       TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, pCurScb->SCB_BufLen);
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_DMA_IN);    /* 7/25/95 */
+
+       if (pCurScb->SCB_Flags & SCF_SG) {      /* S/G xfer */
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, ((ULONG) pCurScb->SCB_SGLen) << 3);
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+               TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_SG_IN);
+       } else {
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, pCurScb->SCB_BufLen);
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+               TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_IN);
+       }
+       pCurScb->SCB_NxtStat = 0x5;
+       return (0);             /* return to OS, wait xfer done , let jas_isr come in */
+}
+
+
+/***************************************************************************/
+int tul_xfer_data_out(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+       if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_DIN) {
+               return (6);     /* wrong direction */
+       }
+       TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, pCurScb->SCB_BufLen);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_DMA_OUT);
+
+       if (pCurScb->SCB_Flags & SCF_SG) {      /* S/G xfer */
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, ((ULONG) pCurScb->SCB_SGLen) << 3);
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+               TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_SG_OUT);
+       } else {
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_XCntH, pCurScb->SCB_BufLen);
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_XAddH, pCurScb->SCB_BufPtr);
+               TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_OUT);
+       }
+
+       pCurScb->SCB_NxtStat = 0x5;
+       return (0);             /* return to OS, wait xfer done , let jas_isr come in */
+}
+
+
+/***************************************************************************/
+int tul_xpad_in(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+       TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+
+       if ((pCurScb->SCB_Flags & SCF_DIR) != SCF_NO_DCHK) {
+               pCurScb->SCB_HaStat = HOST_DO_DU;       /* over run             */
+       }
+       for (;;) {
+               if (pCurTcb->TCS_JS_Period & TSC_WIDE_SCSI)
+                       TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 2);
+               else
+                       TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+               if ((wait_tulip(pCurHcb)) == -1) {
+                       return (-1);
+               }
+               if (pCurHcb->HCS_Phase != DATA_IN) {
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+                       return (6);
+               }
+               TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+       }
+}
+
+int tul_xpad_out(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+       TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+
+       if ((pCurScb->SCB_Flags & SCF_DIR) != SCF_NO_DCHK) {
+               pCurScb->SCB_HaStat = HOST_DO_DU;       /* over run             */
+       }
+       for (;;) {
+               if (pCurTcb->TCS_JS_Period & TSC_WIDE_SCSI)
+                       TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 2);
+               else
+                       TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+
+               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 0);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+               if ((wait_tulip(pCurHcb)) == -1) {
+                       return (-1);
+               }
+               if (pCurHcb->HCS_Phase != DATA_OUT) {   /* Disable wide CPU to allow read 16 bits */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+                       return (6);
+               }
+       }
+}
+
+
+/***************************************************************************/
+int tul_status_msg(HCS * pCurHcb)
+{                              /* status & MSG_IN */
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+       BYTE msg;
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_CMD_COMP);
+       if ((wait_tulip(pCurHcb)) == -1) {
+               return (-1);
+       }
+       /* get status */
+       pCurScb->SCB_TaStat = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+
+       if (pCurHcb->HCS_Phase == MSG_OUT) {
+               if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_PARITY);
+               } else {
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP);
+               }
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+               return (wait_tulip(pCurHcb));
+       }
+       if (pCurHcb->HCS_Phase == MSG_IN) {
+               msg = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+               if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) {   /* Parity error                 */
+                       if ((tul_msgin_accept(pCurHcb)) == -1)
+                               return (-1);
+                       if (pCurHcb->HCS_Phase != MSG_OUT)
+                               return (tul_bad_seq(pCurHcb));
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_PARITY);
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+                       return (wait_tulip(pCurHcb));
+               }
+               if (msg == 0) { /* Command complete             */
+
+                       if ((pCurScb->SCB_TaStat & 0x18) == 0x10) {     /* No link support              */
+                               return (tul_bad_seq(pCurHcb));
+                       }
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+                       return tul_wait_done_disc(pCurHcb);
+
+               }
+               if ((msg == MSG_LINK_COMP) || (msg == MSG_LINK_FLAG)) {
+                       if ((pCurScb->SCB_TaStat & 0x18) == 0x10)
+                               return (tul_msgin_accept(pCurHcb));
+               }
+       }
+       return (tul_bad_seq(pCurHcb));
+}
+
+
+/***************************************************************************/
+/* scsi bus free */
+int int_tul_busfree(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+
+       if (pCurScb != NULL) {
+               if (pCurScb->SCB_Status & SCB_SELECT) {         /* selection timeout */
+                       tul_unlink_pend_scb(pCurHcb, pCurScb);
+                       pCurScb->SCB_HaStat = HOST_SEL_TOUT;
+                       tul_append_done_scb(pCurHcb, pCurScb);
+               } else {        /* Unexpected bus free          */
+                       tul_unlink_busy_scb(pCurHcb, pCurScb);
+                       pCurScb->SCB_HaStat = HOST_BUS_FREE;
+                       tul_append_done_scb(pCurHcb, pCurScb);
+               }
+               pCurHcb->HCS_ActScb = NULL;
+               pCurHcb->HCS_ActTcs = NULL;
+       }
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);         /* Flush SCSI FIFO  */
+       TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);        /* Enable HW reselect       */
+       return (-1);
+}
+
+
+/***************************************************************************/
+/* scsi bus reset */
+int int_tul_scsi_rst(HCS * pCurHcb)
+{
+       SCB *pCurScb;
+       int i;
+
+       /* if DMA xfer is pending, abort DMA xfer */
+       if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & 0x01) {
+               TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO);
+               /* wait Abort DMA xfer done */
+               while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & 0x04) == 0);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+       }
+       /* Abort all active & disconnected scb */
+       while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+               pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+               tul_append_done_scb(pCurHcb, pCurScb);
+       }
+       pCurHcb->HCS_ActScb = NULL;
+       pCurHcb->HCS_ActTcs = NULL;
+
+       /* clr sync nego. done flag */
+       for (i = 0; i < pCurHcb->HCS_MaxTar; i++) {
+               pCurHcb->HCS_Tcs[i].TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+       }
+       return (-1);
+}
+
+
+/***************************************************************************/
+/* scsi reselection */
+int int_tul_resel(HCS * pCurHcb)
+{
+       SCB *pCurScb;
+       TCS *pCurTcb;
+       BYTE tag, msg = 0;
+       BYTE tar, lun;
+
+       if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) {
+               if (pCurScb->SCB_Status & SCB_SELECT) {         /* if waiting for selection complete */
+                       pCurScb->SCB_Status &= ~SCB_SELECT;
+               }
+               pCurHcb->HCS_ActScb = NULL;
+       }
+       /* --------- get target id---------------------- */
+       tar = TUL_RD(pCurHcb->HCS_Base, TUL_SBusId);
+       /* ------ get LUN from Identify message----------- */
+       lun = TUL_RD(pCurHcb->HCS_Base, TUL_SIdent) & 0x0F;
+       /* 07/22/98 from 0x1F -> 0x0F */
+       pCurTcb = &pCurHcb->HCS_Tcs[tar];
+       pCurHcb->HCS_ActTcs = pCurTcb;
+       TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurTcb->TCS_JS_Period);
+
+
+       /* ------------- tag queueing ? ------------------- */
+       if (pCurTcb->TCS_DrvFlags & TCF_DRV_EN_TAG) {
+               if ((tul_msgin_accept(pCurHcb)) == -1)
+                       return (-1);
+               if (pCurHcb->HCS_Phase != MSG_IN)
+                       goto no_tag;
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+               if ((wait_tulip(pCurHcb)) == -1)
+                       return (-1);
+               msg = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);     /* Read Tag Message    */
+
+               if ((msg < MSG_STAG) || (msg > MSG_OTAG))       /* Is simple Tag      */
+                       goto no_tag;
+
+               if ((tul_msgin_accept(pCurHcb)) == -1)
+                       return (-1);
+
+               if (pCurHcb->HCS_Phase != MSG_IN)
+                       goto no_tag;
+
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+               if ((wait_tulip(pCurHcb)) == -1)
+                       return (-1);
+               tag = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);     /* Read Tag ID       */
+               pCurScb = pCurHcb->HCS_Scb + tag;
+               if ((pCurScb->SCB_Target != tar) || (pCurScb->SCB_Lun != lun)) {
+                       return tul_msgout_abort_tag(pCurHcb);
+               }
+               if (pCurScb->SCB_Status != SCB_BUSY) {  /* 03/24/95             */
+                       return tul_msgout_abort_tag(pCurHcb);
+               }
+               pCurHcb->HCS_ActScb = pCurScb;
+               if ((tul_msgin_accept(pCurHcb)) == -1)
+                       return (-1);
+       } else {                /* No tag               */
+             no_tag:
+               if ((pCurScb = tul_find_busy_scb(pCurHcb, tar | (lun << 8))) == NULL) {
+                       return tul_msgout_abort_targ(pCurHcb);
+               }
+               pCurHcb->HCS_ActScb = pCurScb;
+               if (!(pCurTcb->TCS_DrvFlags & TCF_DRV_EN_TAG)) {
+                       if ((tul_msgin_accept(pCurHcb)) == -1)
+                               return (-1);
+               }
+       }
+       return 0;
+}
+
+
+/***************************************************************************/
+int int_tul_bad_seq(HCS * pCurHcb)
+{                              /* target wrong phase           */
+       SCB *pCurScb;
+       int i;
+
+       tul_reset_scsi(pCurHcb, 10);
+
+       while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+               pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+               tul_append_done_scb(pCurHcb, pCurScb);
+       }
+       for (i = 0; i < pCurHcb->HCS_MaxTar; i++) {
+               pCurHcb->HCS_Tcs[i].TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+       }
+       return (-1);
+}
+
+
+/***************************************************************************/
+int tul_msgout_abort_targ(HCS * pCurHcb)
+{
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+       if (tul_msgin_accept(pCurHcb) == -1)
+               return (-1);
+       if (pCurHcb->HCS_Phase != MSG_OUT)
+               return (tul_bad_seq(pCurHcb));
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_ABORT);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+       return tul_wait_disc(pCurHcb);
+}
+
+/***************************************************************************/
+int tul_msgout_abort_tag(HCS * pCurHcb)
+{
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+       if (tul_msgin_accept(pCurHcb) == -1)
+               return (-1);
+       if (pCurHcb->HCS_Phase != MSG_OUT)
+               return (tul_bad_seq(pCurHcb));
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_ABORT_TAG);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+       return tul_wait_disc(pCurHcb);
+
+}
+
+/***************************************************************************/
+int tul_msgin(HCS * pCurHcb)
+{
+       TCS *pCurTcb;
+
+       for (;;) {
+
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+               if ((wait_tulip(pCurHcb)) == -1)
+                       return (-1);
+
+               switch (TUL_RD(pCurHcb->HCS_Base, TUL_SFifo)) {
+               case MSG_DISC:  /* Disconnect msg */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+
+                       return tul_wait_disc(pCurHcb);
+
+               case MSG_SDP:
+               case MSG_RESTORE:
+               case MSG_NOP:
+                       tul_msgin_accept(pCurHcb);
+                       break;
+
+               case MSG_REJ:   /* Clear ATN first              */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SSignal,
+                              (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)));
+                       pCurTcb = pCurHcb->HCS_ActTcs;
+                       if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) {   /* do sync nego */
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+                       }
+                       tul_msgin_accept(pCurHcb);
+                       break;
+
+               case MSG_EXTEND:        /* extended msg */
+                       tul_msgin_extend(pCurHcb);
+                       break;
+
+               case MSG_IGNOREWIDE:
+                       tul_msgin_accept(pCurHcb);
+                       break;
+
+                       /* get */
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+                       if (wait_tulip(pCurHcb) == -1)
+                               return -1;
+
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 0);       /* put pad  */
+                       TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);   /* get IGNORE field */
+                       TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);   /* get pad */
+
+                       tul_msgin_accept(pCurHcb);
+                       break;
+
+               case MSG_COMP:
+                       {
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+                               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+                               return tul_wait_done_disc(pCurHcb);
+                       }
+               default:
+                       tul_msgout_reject(pCurHcb);
+                       break;
+               }
+               if (pCurHcb->HCS_Phase != MSG_IN)
+                       return (pCurHcb->HCS_Phase);
+       }
+       /* statement won't reach here */
+}
+
+
+
+
+/***************************************************************************/
+int tul_msgout_reject(HCS * pCurHcb)
+{
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+
+       if ((tul_msgin_accept(pCurHcb)) == -1)
+               return (-1);
+
+       if (pCurHcb->HCS_Phase == MSG_OUT) {
+               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_REJ);         /* Msg reject           */
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+               return (wait_tulip(pCurHcb));
+       }
+       return (pCurHcb->HCS_Phase);
+}
+
+
+
+/***************************************************************************/
+int tul_msgout_ide(HCS * pCurHcb)
+{
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_IDE);         /* Initiator Detected Error */
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+       return (wait_tulip(pCurHcb));
+}
+
+
+/***************************************************************************/
+int tul_msgin_extend(HCS * pCurHcb)
+{
+       BYTE len, idx;
+
+       if (tul_msgin_accept(pCurHcb) != MSG_IN)
+               return (pCurHcb->HCS_Phase);
+
+       /* Get extended msg length      */
+       TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+       if (wait_tulip(pCurHcb) == -1)
+               return (-1);
+
+       len = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+       pCurHcb->HCS_Msg[0] = len;
+       for (idx = 1; len != 0; len--) {
+
+               if ((tul_msgin_accept(pCurHcb)) != MSG_IN)
+                       return (pCurHcb->HCS_Phase);
+               TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN);
+               if (wait_tulip(pCurHcb) == -1)
+                       return (-1);
+               pCurHcb->HCS_Msg[idx++] = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo);
+       }
+       if (pCurHcb->HCS_Msg[1] == 1) {         /* if it's synchronous data transfer request */
+               if (pCurHcb->HCS_Msg[0] != 3)   /* if length is not right */
+                       return (tul_msgout_reject(pCurHcb));
+               if (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_SYNC_NEGO) {        /* Set OFFSET=0 to do async, nego back */
+                       pCurHcb->HCS_Msg[3] = 0;
+               } else {
+                       if ((tul_msgin_sync(pCurHcb) == 0) &&
+                           (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_SYNC_DONE)) {
+                               tul_sync_done(pCurHcb);
+                               return (tul_msgin_accept(pCurHcb));
+                       }
+               }
+
+               TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+               if ((tul_msgin_accept(pCurHcb)) != MSG_OUT)
+                       return (pCurHcb->HCS_Phase);
+               /* sync msg out */
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);
+
+               tul_sync_done(pCurHcb);
+
+               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 1);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[2]);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[3]);
+
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+               return (wait_tulip(pCurHcb));
+       }
+       if ((pCurHcb->HCS_Msg[0] != 2) || (pCurHcb->HCS_Msg[1] != 3))
+               return (tul_msgout_reject(pCurHcb));
+       /* if it's WIDE DATA XFER REQ   */
+       if (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_WDTR) {
+               pCurHcb->HCS_Msg[2] = 0;
+       } else {
+               if (pCurHcb->HCS_Msg[2] > 2)    /* > 32 bits            */
+                       return (tul_msgout_reject(pCurHcb));
+               if (pCurHcb->HCS_Msg[2] == 2) {         /* == 32                */
+                       pCurHcb->HCS_Msg[2] = 1;
+               } else {
+                       if ((pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_WDTR) == 0) {
+                               wdtr_done(pCurHcb);
+                               if ((pCurHcb->HCS_ActTcs->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0)
+                                       TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+                               return (tul_msgin_accept(pCurHcb));
+                       }
+               }
+       }
+       TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN));
+
+       if (tul_msgin_accept(pCurHcb) != MSG_OUT)
+               return (pCurHcb->HCS_Phase);
+       /* WDTR msg out                 */
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_EXTEND);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 2);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 3);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurHcb->HCS_Msg[2]);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+       return (wait_tulip(pCurHcb));
+}
+
+/***************************************************************************/
+int tul_msgin_sync(HCS * pCurHcb)
+{
+       char default_period;
+
+       default_period = tul_rate_tbl[pCurHcb->HCS_ActTcs->TCS_Flags & TCF_SCSI_RATE];
+       if (pCurHcb->HCS_Msg[3] > MAX_OFFSET) {
+               pCurHcb->HCS_Msg[3] = MAX_OFFSET;
+               if (pCurHcb->HCS_Msg[2] < default_period) {
+                       pCurHcb->HCS_Msg[2] = default_period;
+                       return 1;
+               }
+               if (pCurHcb->HCS_Msg[2] >= 59) {        /* Change to async              */
+                       pCurHcb->HCS_Msg[3] = 0;
+               }
+               return 1;
+       }
+       /* offset requests asynchronous transfers ? */
+       if (pCurHcb->HCS_Msg[3] == 0) {
+               return 0;
+       }
+       if (pCurHcb->HCS_Msg[2] < default_period) {
+               pCurHcb->HCS_Msg[2] = default_period;
+               return 1;
+       }
+       if (pCurHcb->HCS_Msg[2] >= 59) {
+               pCurHcb->HCS_Msg[3] = 0;
+               return 1;
+       }
+       return 0;
+}
+
+
+/***************************************************************************/
+int wdtr_done(HCS * pCurHcb)
+{
+       pCurHcb->HCS_ActTcs->TCS_Flags &= ~TCF_SYNC_DONE;
+       pCurHcb->HCS_ActTcs->TCS_Flags |= TCF_WDTR_DONE;
+
+       pCurHcb->HCS_ActTcs->TCS_JS_Period = 0;
+       if (pCurHcb->HCS_Msg[2]) {      /* if 16 bit */
+               pCurHcb->HCS_ActTcs->TCS_JS_Period |= TSC_WIDE_SCSI;
+       }
+       pCurHcb->HCS_ActTcs->TCS_SConfig0 &= ~TSC_ALT_PERIOD;
+       TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_ActTcs->TCS_SConfig0);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurHcb->HCS_ActTcs->TCS_JS_Period);
+
+       return 1;
+}
+
+/***************************************************************************/
+int tul_sync_done(HCS * pCurHcb)
+{
+       int i;
+
+       pCurHcb->HCS_ActTcs->TCS_Flags |= TCF_SYNC_DONE;
+
+       if (pCurHcb->HCS_Msg[3]) {
+               pCurHcb->HCS_ActTcs->TCS_JS_Period |= pCurHcb->HCS_Msg[3];
+               for (i = 0; i < 8; i++) {
+                       if (tul_rate_tbl[i] >= pCurHcb->HCS_Msg[2])     /* pick the big one */
+                               break;
+               }
+               pCurHcb->HCS_ActTcs->TCS_JS_Period |= (i << 4);
+               pCurHcb->HCS_ActTcs->TCS_SConfig0 |= TSC_ALT_PERIOD;
+       }
+       TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_ActTcs->TCS_SConfig0);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurHcb->HCS_ActTcs->TCS_JS_Period);
+
+       return (-1);
+}
+
+
+int tul_post_scsi_rst(HCS * pCurHcb)
+{
+       SCB *pCurScb;
+       TCS *pCurTcb;
+       int i;
+
+       pCurHcb->HCS_ActScb = NULL;
+       pCurHcb->HCS_ActTcs = NULL;
+       pCurHcb->HCS_Flags = 0;
+
+       while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) {
+               pCurScb->SCB_HaStat = HOST_BAD_PHAS;
+               tul_append_done_scb(pCurHcb, pCurScb);
+       }
+       /* clear sync done flag         */
+       pCurTcb = &pCurHcb->HCS_Tcs[0];
+       for (i = 0; i < pCurHcb->HCS_MaxTar; pCurTcb++, i++) {
+               pCurTcb->TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);
+               /* Initialize the sync. xfer register values to an asyn xfer */
+               pCurTcb->TCS_JS_Period = 0;
+               pCurTcb->TCS_SConfig0 = pCurHcb->HCS_SConf1;
+               pCurHcb->HCS_ActTags[0] = 0;    /* 07/22/98 */
+               pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY;     /* 07/22/98 */
+       }                       /* for */
+
+       return (-1);
+}
+
+/***************************************************************************/
+void tul_select_atn_stop(HCS * pCurHcb, SCB * pCurScb)
+{
+       pCurScb->SCB_Status |= SCB_SELECT;
+       pCurScb->SCB_NxtStat = 0x1;
+       pCurHcb->HCS_ActScb = pCurScb;
+       pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SELATNSTOP);
+       return;
+}
+
+
+/***************************************************************************/
+void tul_select_atn(HCS * pCurHcb, SCB * pCurScb)
+{
+       int i;
+
+       pCurScb->SCB_Status |= SCB_SELECT;
+       pCurScb->SCB_NxtStat = 0x2;
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+       for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+       pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+       pCurHcb->HCS_ActScb = pCurScb;
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SEL_ATN);
+       return;
+}
+
+/***************************************************************************/
+void tul_select_atn3(HCS * pCurHcb, SCB * pCurScb)
+{
+       int i;
+
+       pCurScb->SCB_Status |= SCB_SELECT;
+       pCurScb->SCB_NxtStat = 0x2;
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagMsg);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_TagId);
+       for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++)
+               TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]);
+       pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target];
+       pCurHcb->HCS_ActScb = pCurScb;
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SEL_ATN3);
+       return;
+}
+
+/***************************************************************************/
+/* SCSI Bus Device Reset */
+int tul_bus_device_reset(HCS * pCurHcb)
+{
+       SCB *pCurScb = pCurHcb->HCS_ActScb;
+       TCS *pCurTcb = pCurHcb->HCS_ActTcs;
+       SCB *pTmpScb, *pPrevScb;
+       BYTE tar;
+
+       if (pCurHcb->HCS_Phase != MSG_OUT) {
+               return (int_tul_bad_seq(pCurHcb));      /* Unexpected phase             */
+       }
+       tul_unlink_pend_scb(pCurHcb, pCurScb);
+       tul_release_scb(pCurHcb, pCurScb);
+
+
+       tar = pCurScb->SCB_Target;      /* target                       */
+       pCurTcb->TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE | TCF_BUSY);
+       /* clr sync. nego & WDTR flags  07/22/98 */
+
+       /* abort all SCB with same target */
+       pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy;    /* Check Busy queue */
+       while (pTmpScb != NULL) {
+
+               if (pTmpScb->SCB_Target == tar) {
+                       /* unlink it */
+                       if (pTmpScb == pCurHcb->HCS_FirstBusy) {
+                               if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL)
+                                       pCurHcb->HCS_LastBusy = NULL;
+                       } else {
+                               pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb;
+                               if (pTmpScb == pCurHcb->HCS_LastBusy)
+                                       pCurHcb->HCS_LastBusy = pPrevScb;
+                       }
+                       pTmpScb->SCB_HaStat = HOST_ABORTED;
+                       tul_append_done_scb(pCurHcb, pTmpScb);
+               }
+               /* Previous haven't change      */
+               else {
+                       pPrevScb = pTmpScb;
+               }
+               pTmpScb = pTmpScb->SCB_NxtScb;
+       }
+
+       TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_DEVRST);
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT);
+
+       return tul_wait_disc(pCurHcb);
+
+}
+
+/***************************************************************************/
+int tul_msgin_accept(HCS * pCurHcb)
+{
+       TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT);
+       return (wait_tulip(pCurHcb));
+}
+
+/***************************************************************************/
+int wait_tulip(HCS * pCurHcb)
+{
+
+       while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+                & TSS_INT_PENDING));
+
+       pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+       pCurHcb->HCS_Phase = pCurHcb->HCS_JSStatus0 & TSS_PH_MASK;
+       pCurHcb->HCS_JSStatus1 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1);
+
+       if (pCurHcb->HCS_JSInt & TSS_RESEL_INT) {       /* if SCSI bus reset detected   */
+               return (int_tul_resel(pCurHcb));
+       }
+       if (pCurHcb->HCS_JSInt & TSS_SEL_TIMEOUT) {     /* if selected/reselected timeout interrupt */
+               return (int_tul_busfree(pCurHcb));
+       }
+       if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) {     /* if SCSI bus reset detected   */
+               return (int_tul_scsi_rst(pCurHcb));
+       }
+       if (pCurHcb->HCS_JSInt & TSS_DISC_INT) {        /* BUS disconnection            */
+               if (pCurHcb->HCS_Flags & HCF_EXPECT_DONE_DISC) {
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);         /* Flush SCSI FIFO  */
+                       tul_unlink_busy_scb(pCurHcb, pCurHcb->HCS_ActScb);
+                       pCurHcb->HCS_ActScb->SCB_HaStat = 0;
+                       tul_append_done_scb(pCurHcb, pCurHcb->HCS_ActScb);
+                       pCurHcb->HCS_ActScb = NULL;
+                       pCurHcb->HCS_ActTcs = NULL;
+                       pCurHcb->HCS_Flags &= ~HCF_EXPECT_DONE_DISC;
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);        /* Enable HW reselect       */
+                       return (-1);
+               }
+               if (pCurHcb->HCS_Flags & HCF_EXPECT_DISC) {
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);         /* Flush SCSI FIFO  */
+                       pCurHcb->HCS_ActScb = NULL;
+                       pCurHcb->HCS_ActTcs = NULL;
+                       pCurHcb->HCS_Flags &= ~HCF_EXPECT_DISC;
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+                       TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);        /* Enable HW reselect       */
+                       return (-1);
+               }
+               return (int_tul_busfree(pCurHcb));
+       }
+       if (pCurHcb->HCS_JSInt & (TSS_FUNC_COMP | TSS_BUS_SERV)) {
+               return (pCurHcb->HCS_Phase);
+       }
+       return (pCurHcb->HCS_Phase);
+}
+/***************************************************************************/
+int tul_wait_disc(HCS * pCurHcb)
+{
+
+       while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+                & TSS_INT_PENDING));
+
+
+       pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+       if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) {     /* if SCSI bus reset detected   */
+               return (int_tul_scsi_rst(pCurHcb));
+       }
+       if (pCurHcb->HCS_JSInt & TSS_DISC_INT) {        /* BUS disconnection            */
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);         /* Flush SCSI FIFO  */
+               TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);        /* Enable HW reselect       */
+               pCurHcb->HCS_ActScb = NULL;
+               return (-1);
+       }
+       return (tul_bad_seq(pCurHcb));
+}
+
+/***************************************************************************/
+int tul_wait_done_disc(HCS * pCurHcb)
+{
+
+
+       while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0))
+                & TSS_INT_PENDING));
+
+       pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt);
+
+
+       if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) {     /* if SCSI bus reset detected   */
+               return (int_tul_scsi_rst(pCurHcb));
+       }
+       if (pCurHcb->HCS_JSInt & TSS_DISC_INT) {        /* BUS disconnection            */
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO);         /* Flush SCSI FIFO  */
+               TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT);
+               TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);        /* Enable HW reselect       */
+               tul_unlink_busy_scb(pCurHcb, pCurHcb->HCS_ActScb);
+
+               tul_append_done_scb(pCurHcb, pCurHcb->HCS_ActScb);
+               pCurHcb->HCS_ActScb = NULL;
+               return (-1);
+       }
+       return (tul_bad_seq(pCurHcb));
+}
+
+static irqreturn_t i91u_intr(int irqno, void *dev_id, struct pt_regs *regs)
+{
+       struct Scsi_Host *dev = dev_id;
+       unsigned long flags;
+       
+       spin_lock_irqsave(dev->host_lock, flags);
+       tul_isr((HCS *)dev->base);
+       spin_unlock_irqrestore(dev->host_lock, flags);
+       return IRQ_HANDLED;
+}
+
+static int tul_NewReturnNumberOfAdapters(void)
+{
+       struct pci_dev *pDev = NULL;    /* Start from none              */
+       int iAdapters = 0;
+       long dRegValue;
+       WORD wBIOS;
+       int i = 0;
+
+       init_i91uAdapter_table();
+
+       for (i = 0; i < TULSZ(i91u_pci_devices); i++)
+       {
+               while ((pDev = pci_find_device(i91u_pci_devices[i].vendor_id, i91u_pci_devices[i].device_id, pDev)) != NULL) {
+                       if (pci_enable_device(pDev))
+                               continue;
+                       pci_read_config_dword(pDev, 0x44, (u32 *) & dRegValue);
+                       wBIOS = (UWORD) (dRegValue & 0xFF);
+                       if (((dRegValue & 0xFF00) >> 8) == 0xFF)
+                               dRegValue = 0;
+                       wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8));
+                       if (pci_set_dma_mask(pDev, 0xffffffff)) {
+                               printk(KERN_WARNING 
+                                      "i91u: Could not set 32 bit DMA mask\n");
+                               continue;
+                       }
+
+                       if (Addi91u_into_Adapter_table(wBIOS,
+                                                       (pDev->resource[0].start),
+                                                       pDev->irq,
+                                                       pDev->bus->number,
+                                                       (pDev->devfn >> 3)
+                               ) == 0)
+                               iAdapters++;
+               }
+       }
+
+       return (iAdapters);
+}
+
+static int i91u_detect(struct scsi_host_template * tpnt)
+{
+       HCS *pHCB;
+       struct Scsi_Host *hreg;
+       unsigned long i;        /* 01/14/98                     */
+       int ok = 0, iAdapters;
+       ULONG dBiosAdr;
+       BYTE *pbBiosAdr;
+
+       /* Get total number of adapters in the motherboard */
+       iAdapters = tul_NewReturnNumberOfAdapters();
+       if (iAdapters == 0)     /* If no tulip founded, return */
+               return (0);
+
+       tul_num_ch = (iAdapters > tul_num_ch) ? tul_num_ch : iAdapters;
+       /* Update actually channel number */
+       if (tul_tag_enable) {   /* 1.01i                  */
+               tul_num_scb = MAX_TARGETS * i91u_MAXQUEUE;
+       } else {
+               tul_num_scb = MAX_TARGETS + 3;  /* 1-tape, 1-CD_ROM, 1- extra */
+       }                       /* Update actually SCBs per adapter */
+
+       /* Get total memory needed for HCS */
+       i = tul_num_ch * sizeof(HCS);
+       memset((unsigned char *) &tul_hcs[0], 0, i);    /* Initialize tul_hcs 0 */
+       /* Get total memory needed for SCB */
+
+       for (; tul_num_scb >= MAX_TARGETS + 3; tul_num_scb--) {
+               i = tul_num_ch * tul_num_scb * sizeof(SCB);
+               if ((tul_scb = (SCB *) kmalloc(i, GFP_ATOMIC | GFP_DMA)) != NULL)
+                       break;
+       }
+       if (tul_scb == NULL) {
+               printk("i91u: SCB memory allocation error\n");
+               return (0);
+       }
+       memset((unsigned char *) tul_scb, 0, i);
+
+       for (i = 0, pHCB = &tul_hcs[0];         /* Get pointer for control block */
+            i < tul_num_ch;
+            i++, pHCB++) {
+               get_tulipPCIConfig(pHCB, i);
+
+               dBiosAdr = pHCB->HCS_BIOS;
+               dBiosAdr = (dBiosAdr << 4);
+
+               pbBiosAdr = phys_to_virt(dBiosAdr);
+
+               init_tulip(pHCB, tul_scb + (i * tul_num_scb), tul_num_scb, pbBiosAdr, 10);
+               request_region(pHCB->HCS_Base, 256, "i91u"); /* Register */ 
+
+               pHCB->HCS_Index = i;    /* 7/29/98 */
+               hreg = scsi_register(tpnt, sizeof(HCS));
+               if(hreg == NULL) {
+                       release_region(pHCB->HCS_Base, 256);
+                       return 0;
+               }
+               hreg->io_port = pHCB->HCS_Base;
+               hreg->n_io_port = 0xff;
+               hreg->can_queue = tul_num_scb;  /* 03/05/98                      */
+               hreg->unique_id = pHCB->HCS_Base;
+               hreg->max_id = pHCB->HCS_MaxTar;
+               hreg->max_lun = 32;     /* 10/21/97                     */
+               hreg->irq = pHCB->HCS_Intr;
+               hreg->this_id = pHCB->HCS_SCSI_ID;      /* Assign HCS index           */
+               hreg->base = (unsigned long)pHCB;
+               hreg->sg_tablesize = TOTAL_SG_ENTRY;    /* Maximun support is 32 */
+
+               /* Initial tulip chip           */
+               ok = request_irq(pHCB->HCS_Intr, i91u_intr, SA_INTERRUPT | SA_SHIRQ, "i91u", hreg);
+               if (ok < 0) {
+                       printk(KERN_WARNING "i91u: unable to request IRQ %d\n\n", pHCB->HCS_Intr);
+                       return 0;
+               }
+       }
+
+       tpnt->this_id = -1;
+       tpnt->can_queue = 1;
+
+       return 1;
+}
+
+static void i91uBuildSCB(HCS * pHCB, SCB * pSCB, struct scsi_cmnd * SCpnt)
+{                              /* Create corresponding SCB     */
+       struct scatterlist *pSrbSG;
+       SG *pSG;                /* Pointer to SG list           */
+       int i;
+       long TotalLen;
+       dma_addr_t dma_addr;
+
+       pSCB->SCB_Post = i91uSCBPost;   /* i91u's callback routine      */
+       pSCB->SCB_Srb = SCpnt;
+       pSCB->SCB_Opcode = ExecSCSI;
+       pSCB->SCB_Flags = SCF_POST;     /* After SCSI done, call post routine */
+       pSCB->SCB_Target = SCpnt->device->id;
+       pSCB->SCB_Lun = SCpnt->device->lun;
+       pSCB->SCB_Ident = SCpnt->device->lun | DISC_ALLOW;
+
+       pSCB->SCB_Flags |= SCF_SENSE;   /* Turn on auto request sense   */
+       dma_addr = dma_map_single(&pHCB->pci_dev->dev, SCpnt->sense_buffer,
+                                 SENSE_SIZE, DMA_FROM_DEVICE);
+       pSCB->SCB_SensePtr = cpu_to_le32((u32)dma_addr);
+       pSCB->SCB_SenseLen = cpu_to_le32(SENSE_SIZE);
+       SCpnt->SCp.ptr = (char *)(unsigned long)dma_addr;
+
+       pSCB->SCB_CDBLen = SCpnt->cmd_len;
+       pSCB->SCB_HaStat = 0;
+       pSCB->SCB_TaStat = 0;
+       memcpy(&pSCB->SCB_CDB[0], &SCpnt->cmnd, SCpnt->cmd_len);
+
+       if (SCpnt->device->tagged_supported) {  /* Tag Support                  */
+               pSCB->SCB_TagMsg = SIMPLE_QUEUE_TAG;    /* Do simple tag only   */
+       } else {
+               pSCB->SCB_TagMsg = 0;   /* No tag support               */
+       }
+       /* todo handle map_sg error */
+       if (SCpnt->use_sg) {
+               dma_addr = dma_map_single(&pHCB->pci_dev->dev, &pSCB->SCB_SGList[0],
+                                         sizeof(struct SG_Struc) * TOTAL_SG_ENTRY,
+                                         DMA_BIDIRECTIONAL);
+               pSCB->SCB_BufPtr = cpu_to_le32((u32)dma_addr);
+               SCpnt->SCp.dma_handle = dma_addr;
+
+               pSrbSG = (struct scatterlist *) SCpnt->request_buffer;
+               pSCB->SCB_SGLen = dma_map_sg(&pHCB->pci_dev->dev, pSrbSG,
+                                            SCpnt->use_sg, SCpnt->sc_data_direction);
+
+               pSCB->SCB_Flags |= SCF_SG;      /* Turn on SG list flag       */
+               for (i = 0, TotalLen = 0, pSG = &pSCB->SCB_SGList[0];   /* 1.01g */
+                    i < pSCB->SCB_SGLen; i++, pSG++, pSrbSG++) {
+                       pSG->SG_Ptr = cpu_to_le32((u32)sg_dma_address(pSrbSG));
+                       TotalLen += pSG->SG_Len = cpu_to_le32((u32)sg_dma_len(pSrbSG));
+               }
+
+               pSCB->SCB_BufLen = (SCpnt->request_bufflen > TotalLen) ?
+                   TotalLen : SCpnt->request_bufflen;
+       } else if (SCpnt->request_bufflen) {            /* Non SG */
+               dma_addr = dma_map_single(&pHCB->pci_dev->dev, SCpnt->request_buffer,
+                                         SCpnt->request_bufflen,
+                                         SCpnt->sc_data_direction);
+               SCpnt->SCp.dma_handle = dma_addr;
+               pSCB->SCB_BufPtr = cpu_to_le32((u32)dma_addr);
+               pSCB->SCB_BufLen = cpu_to_le32((u32)SCpnt->request_bufflen);
+               pSCB->SCB_SGLen = 0;
+       } else {
+               pSCB->SCB_BufLen = 0;
+               pSCB->SCB_SGLen = 0;
+       }
+}
+
+static int i91u_queuecommand(struct scsi_cmnd *cmd,
+               void (*done)(struct scsi_cmnd *))
+{
+       HCS *pHCB = (HCS *) cmd->device->host->base;
+       register SCB *pSCB;
+
+       cmd->scsi_done = done;
+
+       pSCB = tul_alloc_scb(pHCB);
+       if (!pSCB)
+               return SCSI_MLQUEUE_HOST_BUSY;
+
+       i91uBuildSCB(pHCB, pSCB, cmd);
+       tul_exec_scb(pHCB, pSCB);
+       return 0;
+}
+
+#if 0 /* no new EH yet */
+/*
+ *  Abort a queued command
+ *  (commands that are on the bus can't be aborted easily)
+ */
+static int i91u_abort(struct scsi_cmnd * SCpnt)
+{
+       HCS *pHCB;
+
+       pHCB = (HCS *) SCpnt->device->host->base;
+       return tul_abort_srb(pHCB, SCpnt);
+}
+
+/*
+ *  Reset registers, reset a hanging bus and
+ *  kill active and disconnected commands for target w/o soft reset
+ */
+static int i91u_reset(struct scsi_cmnd * SCpnt, unsigned int reset_flags)
+{                              /* I need Host Control Block Information */
+       HCS *pHCB;
+
+       pHCB = (HCS *) SCpnt->device->host->base;
+
+       if (reset_flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET))
+               return tul_reset_scsi_bus(pHCB);
+       else
+               return tul_device_reset(pHCB, SCpnt, SCpnt->device->id, reset_flags);
+}
+#endif
+
+static int i91u_bus_reset(struct scsi_cmnd * SCpnt)
+{
+       HCS *pHCB;
+
+       pHCB = (HCS *) SCpnt->device->host->base;
+       tul_reset_scsi(pHCB, 0);
+       return SUCCESS;
+}
+
+/*
+ * Return the "logical geometry"
+ */
+static int i91u_biosparam(struct scsi_device *sdev, struct block_device *dev,
+               sector_t capacity, int *info_array)
+{
+       HCS *pHcb;              /* Point to Host adapter control block */
+       TCS *pTcb;
+
+       pHcb = (HCS *) sdev->host->base;
+       pTcb = &pHcb->HCS_Tcs[sdev->id];
+
+       if (pTcb->TCS_DrvHead) {
+               info_array[0] = pTcb->TCS_DrvHead;
+               info_array[1] = pTcb->TCS_DrvSector;
+               info_array[2] = (unsigned long)capacity / pTcb->TCS_DrvHead / pTcb->TCS_DrvSector;
+       } else {
+               if (pTcb->TCS_DrvFlags & TCF_DRV_255_63) {
+                       info_array[0] = 255;
+                       info_array[1] = 63;
+                       info_array[2] = (unsigned long)capacity / 255 / 63;
+               } else {
+                       info_array[0] = 64;
+                       info_array[1] = 32;
+                       info_array[2] = (unsigned long)capacity >> 11;
+               }
+       }
+
+#if defined(DEBUG_BIOSPARAM)
+       if (i91u_debug & debug_biosparam) {
+               printk("bios geometry: head=%d, sec=%d, cyl=%d\n",
+                      info_array[0], info_array[1], info_array[2]);
+               printk("WARNING: check, if the bios geometry is correct.\n");
+       }
+#endif
+
+       return 0;
+}
+
+static void i91u_unmap_cmnd(struct pci_dev *pci_dev, struct scsi_cmnd *cmnd)
+{
+       /* auto sense buffer */
+       if (cmnd->SCp.ptr) {
+               dma_unmap_single(&pci_dev->dev,
+                                (dma_addr_t)((unsigned long)cmnd->SCp.ptr),
+                                SENSE_SIZE, DMA_FROM_DEVICE);
+               cmnd->SCp.ptr = NULL;
+       }
+
+       /* request buffer */
+       if (cmnd->use_sg) {
+               dma_unmap_single(&pci_dev->dev, cmnd->SCp.dma_handle,
+                                sizeof(struct SG_Struc) * TOTAL_SG_ENTRY,
+                                DMA_BIDIRECTIONAL);
+
+               dma_unmap_sg(&pci_dev->dev, cmnd->request_buffer,
+                            cmnd->use_sg,
+                            cmnd->sc_data_direction);
+       } else if (cmnd->request_bufflen) {
+               dma_unmap_single(&pci_dev->dev, cmnd->SCp.dma_handle,
+                                cmnd->request_bufflen,
+                                cmnd->sc_data_direction);
+       }
+}
+
+/*****************************************************************************
+ Function name  : i91uSCBPost
+ Description    : This is callback routine be called when tulip finish one
+                       SCSI command.
+ Input          : pHCB  -       Pointer to host adapter control block.
+                 pSCB  -       Pointer to SCSI control block.
+ Output         : None.
+ Return         : None.
+*****************************************************************************/
+static void i91uSCBPost(BYTE * pHcb, BYTE * pScb)
+{
+       struct scsi_cmnd *pSRB; /* Pointer to SCSI request block */
+       HCS *pHCB;
+       SCB *pSCB;
+
+       pHCB = (HCS *) pHcb;
+       pSCB = (SCB *) pScb;
+       if ((pSRB = pSCB->SCB_Srb) == 0) {
+               printk("i91uSCBPost: SRB pointer is empty\n");
+
+               tul_release_scb(pHCB, pSCB);    /* Release SCB for current channel */
+               return;
+       }
+       switch (pSCB->SCB_HaStat) {
+       case 0x0:
+       case 0xa:               /* Linked command complete without error and linked normally */
+       case 0xb:               /* Linked command complete without error interrupt generated */
+               pSCB->SCB_HaStat = 0;
+               break;
+
+       case 0x11:              /* Selection time out-The initiator selection or target
+                                  reselection was not complete within the SCSI Time out period */
+               pSCB->SCB_HaStat = DID_TIME_OUT;
+               break;
+
+       case 0x14:              /* Target bus phase sequence failure-An invalid bus phase or bus
+                                  phase sequence was requested by the target. The host adapter
+                                  will generate a SCSI Reset Condition, notifying the host with
+                                  a SCRD interrupt */
+               pSCB->SCB_HaStat = DID_RESET;
+               break;
+
+       case 0x1a:              /* SCB Aborted. 07/21/98 */
+               pSCB->SCB_HaStat = DID_ABORT;
+               break;
+
+       case 0x12:              /* Data overrun/underrun-The target attempted to transfer more data
+                                  than was allocated by the Data Length field or the sum of the
+                                  Scatter / Gather Data Length fields. */
+       case 0x13:              /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+       case 0x16:              /* Invalid SCB Operation Code. */
+
+       default:
+               printk("ini9100u: %x %x\n", pSCB->SCB_HaStat, pSCB->SCB_TaStat);
+               pSCB->SCB_HaStat = DID_ERROR;   /* Couldn't find any better */
+               break;
+       }
+
+       pSRB->result = pSCB->SCB_TaStat | (pSCB->SCB_HaStat << 16);
+
+       if (pSRB == NULL) {
+               printk("pSRB is NULL\n");
+       }
+
+       i91u_unmap_cmnd(pHCB->pci_dev, pSRB);
+       pSRB->scsi_done(pSRB);  /* Notify system DONE           */
+
+       tul_release_scb(pHCB, pSCB);    /* Release SCB for current channel */
+}
+
+/*
+ * Release ressources
+ */
+static int i91u_release(struct Scsi_Host *hreg)
+{
+       free_irq(hreg->irq, hreg);
+       release_region(hreg->io_port, 256);
+       return 0;
+}
+MODULE_LICENSE("Dual BSD/GPL");
+
+static struct scsi_host_template driver_template = {
+       .proc_name      = "INI9100U",
+       .name           = i91u_REVID,
+       .detect         = i91u_detect,
+       .release        = i91u_release,
+       .queuecommand   = i91u_queuecommand,
+//     .abort          = i91u_abort,
+//     .reset          = i91u_reset,
+       .eh_bus_reset_handler = i91u_bus_reset,
+       .bios_param     = i91u_biosparam,
+       .can_queue      = 1,
+       .this_id        = 1,
+       .sg_tablesize   = SG_ALL,
+       .cmd_per_lun    = 1,
+       .use_clustering = ENABLE_CLUSTERING,
+};
+#include "scsi_module.c"
+
diff --git a/drivers/scsi/initio.h b/drivers/scsi/initio.h
new file mode 100644 (file)
index 0000000..df3ed7c
--- /dev/null
@@ -0,0 +1,739 @@
+/**************************************************************************
+ * Initio 9100 device driver for Linux.
+ *
+ * Copyright (c) 1994-1998 Initio Corporation
+ * 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, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU General Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ **************************************************************************/
+
+
+#include <linux/config.h>
+#include <linux/types.h>
+
+#define ULONG   unsigned long
+#define USHORT  unsigned short
+#define UCHAR   unsigned char
+#define BYTE    unsigned char
+#define WORD    unsigned short
+#define DWORD   unsigned long
+#define UBYTE   unsigned char
+#define UWORD   unsigned short
+#define UDWORD  unsigned long
+#define U32     u32
+
+#define TOTAL_SG_ENTRY         32
+#define MAX_SUPPORTED_ADAPTERS  8
+#define MAX_OFFSET             15
+#define MAX_TARGETS            16
+
+typedef struct {
+       unsigned short base;
+       unsigned short vec;
+} i91u_config;
+
+/***************************************/
+/*  Tulip Configuration Register Set */
+/***************************************/
+#define TUL_PVID        0x00   /* Vendor ID                    */
+#define TUL_PDID        0x02   /* Device ID                    */
+#define TUL_PCMD        0x04   /* Command                      */
+#define TUL_PSTUS       0x06   /* Status                       */
+#define TUL_PRID        0x08   /* Revision number              */
+#define TUL_PPI         0x09   /* Programming interface        */
+#define TUL_PSC         0x0A   /* Sub Class                    */
+#define TUL_PBC         0x0B   /* Base Class                   */
+#define TUL_PCLS        0x0C   /* Cache line size              */
+#define TUL_PLTR        0x0D   /* Latency timer                */
+#define TUL_PHDT        0x0E   /* Header type                  */
+#define TUL_PBIST       0x0F   /* BIST                         */
+#define TUL_PBAD        0x10   /* Base address                 */
+#define TUL_PBAD1       0x14   /* Base address                 */
+#define TUL_PBAD2       0x18   /* Base address                 */
+#define TUL_PBAD3       0x1C   /* Base address                 */
+#define TUL_PBAD4       0x20   /* Base address                 */
+#define TUL_PBAD5       0x24   /* Base address                 */
+#define TUL_PRSVD       0x28   /* Reserved                     */
+#define TUL_PRSVD1      0x2C   /* Reserved                     */
+#define TUL_PRAD        0x30   /* Expansion ROM base address   */
+#define TUL_PRSVD2      0x34   /* Reserved                     */
+#define TUL_PRSVD3      0x38   /* Reserved                     */
+#define TUL_PINTL       0x3C   /* Interrupt line               */
+#define TUL_PINTP       0x3D   /* Interrupt pin                */
+#define TUL_PIGNT       0x3E   /* MIN_GNT                      */
+#define TUL_PMGNT       0x3F   /* MAX_GNT                      */
+
+/************************/
+/*  Jasmin Register Set */
+/************************/
+#define TUL_HACFG0      0x40   /* H/A Configuration Register 0         */
+#define TUL_HACFG1      0x41   /* H/A Configuration Register 1         */
+#define TUL_HACFG2      0x42   /* H/A Configuration Register 2         */
+
+#define TUL_SDCFG0      0x44   /* SCSI Device Configuration 0          */
+#define TUL_SDCFG1      0x45   /* SCSI Device Configuration 1          */
+#define TUL_SDCFG2      0x46   /* SCSI Device Configuration 2          */
+#define TUL_SDCFG3      0x47   /* SCSI Device Configuration 3          */
+
+#define TUL_GINTS       0x50   /* Global Interrupt Status Register     */
+#define TUL_GIMSK       0x52   /* Global Interrupt MASK Register       */
+#define TUL_GCTRL       0x54   /* Global Control Register              */
+#define TUL_GCTRL_EEPROM_BIT    0x04
+#define TUL_GCTRL1      0x55   /* Global Control Register              */
+#define TUL_DMACFG      0x5B   /* DMA configuration                    */
+#define TUL_NVRAM       0x5D   /* Non-volatile RAM port                */
+
+#define TUL_SCnt0       0x80   /* 00 R/W Transfer Counter Low          */
+#define TUL_SCnt1       0x81   /* 01 R/W Transfer Counter Mid          */
+#define TUL_SCnt2       0x82   /* 02 R/W Transfer Count High           */
+#define TUL_SFifoCnt    0x83   /* 03 R   FIFO counter                  */
+#define TUL_SIntEnable  0x84   /* 03 W   Interrupt enble               */
+#define TUL_SInt        0x84   /* 04 R   Interrupt Register            */
+#define TUL_SCtrl0      0x85   /* 05 W   Control 0                     */
+#define TUL_SStatus0    0x85   /* 05 R   Status 0                      */
+#define TUL_SCtrl1      0x86   /* 06 W   Control 1                     */
+#define TUL_SStatus1    0x86   /* 06 R   Status 1                      */
+#define TUL_SConfig     0x87   /* 07 W   Configuration                 */
+#define TUL_SStatus2    0x87   /* 07 R   Status 2                      */
+#define TUL_SPeriod     0x88   /* 08 W   Sync. Transfer Period & Offset */
+#define TUL_SOffset     0x88   /* 08 R   Offset                        */
+#define TUL_SScsiId     0x89   /* 09 W   SCSI ID                       */
+#define TUL_SBusId      0x89   /* 09 R   SCSI BUS ID                   */
+#define TUL_STimeOut    0x8A   /* 0A W   Sel/Resel Time Out Register   */
+#define TUL_SIdent      0x8A   /* 0A R   Identify Message Register     */
+#define TUL_SAvail      0x8A   /* 0A R   Availiable Counter Register   */
+#define TUL_SData       0x8B   /* 0B R/W SCSI data in/out              */
+#define TUL_SFifo       0x8C   /* 0C R/W FIFO                          */
+#define TUL_SSignal     0x90   /* 10 R/W SCSI signal in/out            */
+#define TUL_SCmd        0x91   /* 11 R/W Command                       */
+#define TUL_STest0      0x92   /* 12 R/W Test0                         */
+#define TUL_STest1      0x93   /* 13 R/W Test1                         */
+#define TUL_SCFG1      0x94    /* 14 R/W Configuration                 */
+
+#define TUL_XAddH       0xC0   /*DMA Transfer Physical Address         */
+#define TUL_XAddW       0xC8   /*DMA Current Transfer Physical Address */
+#define TUL_XCntH       0xD0   /*DMA Transfer Counter                  */
+#define TUL_XCntW       0xD4   /*DMA Current Transfer Counter          */
+#define TUL_XCmd        0xD8   /*DMA Command Register                  */
+#define TUL_Int         0xDC   /*Interrupt Register                    */
+#define TUL_XStatus     0xDD   /*DMA status Register                   */
+#define TUL_Mask        0xE0   /*Interrupt Mask Register               */
+#define TUL_XCtrl       0xE4   /*DMA Control Register                  */
+#define TUL_XCtrl1      0xE5   /*DMA Control Register 1                */
+#define TUL_XFifo       0xE8   /*DMA FIFO                              */
+
+#define TUL_WCtrl       0xF7   /*Bus master wait state control         */
+#define TUL_DCtrl       0xFB   /*DMA delay control                     */
+
+/*----------------------------------------------------------------------*/
+/*   bit definition for Command register of Configuration Space Header  */
+/*----------------------------------------------------------------------*/
+#define BUSMS           0x04   /* BUS MASTER Enable                    */
+#define IOSPA           0x01   /* IO Space Enable                      */
+
+/*----------------------------------------------------------------------*/
+/* Command Codes of Tulip SCSI Command register                         */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_RESEL    0x80   /* Enable Reselection                   */
+#define TSC_CMD_COMP    0x84   /* Command Complete Sequence            */
+#define TSC_SEL         0x01   /* Select Without ATN Sequence          */
+#define TSC_SEL_ATN     0x11   /* Select With ATN Sequence             */
+#define TSC_SEL_ATN_DMA 0x51   /* Select With ATN Sequence with DMA    */
+#define TSC_SEL_ATN3    0x31   /* Select With ATN3 Sequence            */
+#define TSC_SEL_ATNSTOP 0x12   /* Select With ATN and Stop Sequence    */
+#define TSC_SELATNSTOP  0x1E   /* Select With ATN and Stop Sequence    */
+
+#define TSC_SEL_ATN_DIRECT_IN   0x95   /* Select With ATN Sequence     */
+#define TSC_SEL_ATN_DIRECT_OUT  0x15   /* Select With ATN Sequence     */
+#define TSC_SEL_ATN3_DIRECT_IN  0xB5   /* Select With ATN3 Sequence    */
+#define TSC_SEL_ATN3_DIRECT_OUT 0x35   /* Select With ATN3 Sequence    */
+#define TSC_XF_DMA_OUT_DIRECT   0x06   /* DMA Xfer Infomation out      */
+#define TSC_XF_DMA_IN_DIRECT    0x86   /* DMA Xfer Infomation in       */
+
+#define TSC_XF_DMA_OUT  0x43   /* DMA Xfer Infomation out              */
+#define TSC_XF_DMA_IN   0xC3   /* DMA Xfer Infomation in               */
+#define TSC_XF_FIFO_OUT 0x03   /* FIFO Xfer Infomation out             */
+#define TSC_XF_FIFO_IN  0x83   /* FIFO Xfer Infomation in              */
+
+#define TSC_MSG_ACCEPT  0x0F   /* Message Accept                       */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Control 0 Register                     */
+/*----------------------------------------------------------------------*/
+#define TSC_RST_SEQ     0x20   /* Reset sequence counter               */
+#define TSC_FLUSH_FIFO  0x10   /* Flush FIFO                           */
+#define TSC_ABT_CMD     0x04   /* Abort command (sequence)             */
+#define TSC_RST_CHIP    0x02   /* Reset SCSI Chip                      */
+#define TSC_RST_BUS     0x01   /* Reset SCSI Bus                       */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Control 1 Register                     */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_SCAM     0x80   /* Enable SCAM                          */
+#define TSC_TIMER       0x40   /* Select timeout unit                  */
+#define TSC_EN_SCSI2    0x20   /* SCSI-2 mode                          */
+#define TSC_PWDN        0x10   /* Power down mode                      */
+#define TSC_WIDE_CPU    0x08   /* Wide CPU                             */
+#define TSC_HW_RESELECT 0x04   /* Enable HW reselect                   */
+#define TSC_EN_BUS_OUT  0x02   /* Enable SCSI data bus out latch       */
+#define TSC_EN_BUS_IN   0x01   /* Enable SCSI data bus in latch        */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Configuration Register                 */
+/*----------------------------------------------------------------------*/
+#define TSC_EN_LATCH    0x80   /* Enable phase latch                   */
+#define TSC_INITIATOR   0x40   /* Initiator mode                       */
+#define TSC_EN_SCSI_PAR 0x20   /* Enable SCSI parity                   */
+#define TSC_DMA_8BIT    0x10   /* Alternate dma 8-bits mode            */
+#define TSC_DMA_16BIT   0x08   /* Alternate dma 16-bits mode           */
+#define TSC_EN_WDACK    0x04   /* Enable DACK while wide SCSI xfer     */
+#define TSC_ALT_PERIOD  0x02   /* Alternate sync period mode           */
+#define TSC_DIS_SCSIRST 0x01   /* Disable SCSI bus reset us            */
+
+#define TSC_INITDEFAULT (TSC_INITIATOR | TSC_EN_LATCH | TSC_ALT_PERIOD | TSC_DIS_SCSIRST)
+
+#define TSC_WIDE_SCSI   0x80   /* Enable Wide SCSI                     */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI signal Register                        */
+/*----------------------------------------------------------------------*/
+#define TSC_RST_ACK     0x00   /* Release ACK signal                   */
+#define TSC_RST_ATN     0x00   /* Release ATN signal                   */
+#define TSC_RST_BSY     0x00   /* Release BSY signal                   */
+
+#define TSC_SET_ACK     0x40   /* ACK signal                           */
+#define TSC_SET_ATN     0x08   /* ATN signal                           */
+
+#define TSC_REQI        0x80   /* REQ signal                           */
+#define TSC_ACKI        0x40   /* ACK signal                           */
+#define TSC_BSYI        0x20   /* BSY signal                           */
+#define TSC_SELI        0x10   /* SEL signal                           */
+#define TSC_ATNI        0x08   /* ATN signal                           */
+#define TSC_MSGI        0x04   /* MSG signal                           */
+#define TSC_CDI         0x02   /* C/D signal                           */
+#define TSC_IOI         0x01   /* I/O signal                           */
+
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 0 Register                      */
+/*----------------------------------------------------------------------*/
+#define TSS_INT_PENDING 0x80   /* Interrupt pending            */
+#define TSS_SEQ_ACTIVE  0x40   /* Sequencer active             */
+#define TSS_XFER_CNT    0x20   /* Transfer counter zero        */
+#define TSS_FIFO_EMPTY  0x10   /* FIFO empty                   */
+#define TSS_PAR_ERROR   0x08   /* SCSI parity error            */
+#define TSS_PH_MASK     0x07   /* SCSI phase mask              */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 1 Register                      */
+/*----------------------------------------------------------------------*/
+#define TSS_STATUS_RCV  0x08   /* Status received              */
+#define TSS_MSG_SEND    0x40   /* Message sent                 */
+#define TSS_CMD_PH_CMP  0x20   /* command phase done              */
+#define TSS_DATA_PH_CMP 0x10   /* Data phase done              */
+#define TSS_STATUS_SEND 0x08   /* Status sent                  */
+#define TSS_XFER_CMP    0x04   /* Transfer completed           */
+#define TSS_SEL_CMP     0x02   /* Selection completed          */
+#define TSS_ARB_CMP     0x01   /* Arbitration completed        */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Status 2 Register                      */
+/*----------------------------------------------------------------------*/
+#define TSS_CMD_ABTED   0x80   /* Command aborted              */
+#define TSS_OFFSET_0    0x40   /* Offset counter zero          */
+#define TSS_FIFO_FULL   0x20   /* FIFO full                    */
+#define TSS_TIMEOUT_0   0x10   /* Timeout counter zero         */
+#define TSS_BUSY_RLS    0x08   /* Busy release                 */
+#define TSS_PH_MISMATCH 0x04   /* Phase mismatch               */
+#define TSS_SCSI_BUS_EN 0x02   /* SCSI data bus enable         */
+#define TSS_SCSIRST     0x01   /* SCSI bus reset in progress   */
+
+/*----------------------------------------------------------------------*/
+/* bit definition for Tulip SCSI Interrupt Register                     */
+/*----------------------------------------------------------------------*/
+#define TSS_RESEL_INT   0x80   /* Reselected interrupt         */
+#define TSS_SEL_TIMEOUT 0x40   /* Selected/reselected timeout  */
+#define TSS_BUS_SERV    0x20
+#define TSS_SCSIRST_INT 0x10   /* SCSI bus reset detected      */
+#define TSS_DISC_INT    0x08   /* Disconnected interrupt       */
+#define TSS_SEL_INT     0x04   /* Select interrupt             */
+#define TSS_SCAM_SEL    0x02   /* SCAM selected                */
+#define TSS_FUNC_COMP   0x01
+
+/*----------------------------------------------------------------------*/
+/* SCSI Phase Codes.                                                    */
+/*----------------------------------------------------------------------*/
+#define DATA_OUT        0
+#define DATA_IN         1      /* 4                            */
+#define CMD_OUT         2
+#define STATUS_IN       3      /* 6                            */
+#define MSG_OUT         6      /* 3                            */
+#define MSG_IN          7
+
+
+
+/*----------------------------------------------------------------------*/
+/* Command Codes of Tulip xfer Command register                         */
+/*----------------------------------------------------------------------*/
+#define TAX_X_FORC      0x02
+#define TAX_X_ABT       0x04
+#define TAX_X_CLR_FIFO  0x08
+
+#define TAX_X_IN        0x21
+#define TAX_X_OUT       0x01
+#define TAX_SG_IN       0xA1
+#define TAX_SG_OUT      0x81
+
+/*----------------------------------------------------------------------*/
+/* Tulip Interrupt Register                                             */
+/*----------------------------------------------------------------------*/
+#define XCMP            0x01
+#define FCMP            0x02
+#define XABT            0x04
+#define XERR            0x08
+#define SCMP            0x10
+#define IPEND           0x80
+
+/*----------------------------------------------------------------------*/
+/* Tulip DMA Status Register                                            */
+/*----------------------------------------------------------------------*/
+#define XPEND           0x01   /* Transfer pending             */
+#define FEMPTY          0x02   /* FIFO empty                   */
+
+
+
+/*----------------------------------------------------------------------*/
+/* bit definition for TUL_GCTRL                                         */
+/*----------------------------------------------------------------------*/
+#define EXTSG           0x80
+#define EXTAD           0x60
+#define SEG4K           0x08
+#define EEPRG           0x04
+#define MRMUL           0x02
+
+/*----------------------------------------------------------------------*/
+/* bit definition for TUL_NVRAM                                         */
+/*----------------------------------------------------------------------*/
+#define SE2CS           0x08
+#define SE2CLK          0x04
+#define SE2DO           0x02
+#define SE2DI           0x01
+
+
+/************************************************************************/
+/*              Scatter-Gather Element Structure                        */
+/************************************************************************/
+typedef struct SG_Struc {
+       U32 SG_Ptr;             /* Data Pointer */
+       U32 SG_Len;             /* Data Length */
+} SG;
+
+/***********************************************************************
+               SCSI Control Block
+************************************************************************/
+typedef struct Scsi_Ctrl_Blk {
+       struct Scsi_Ctrl_Blk *SCB_NxtScb;
+       UBYTE SCB_Status;       /*4 */
+       UBYTE SCB_NxtStat;      /*5 */
+       UBYTE SCB_Mode;         /*6 */
+       UBYTE SCB_Msgin;        /*7 SCB_Res0 */
+       UWORD SCB_SGIdx;        /*8 */
+       UWORD SCB_SGMax;        /*A */
+#ifdef ALPHA
+       U32 SCB_Reserved[2];    /*C */
+#else
+       U32 SCB_Reserved[3];    /*C */
+#endif
+
+       U32 SCB_XferLen;        /*18 Current xfer len           */
+       U32 SCB_TotXLen;        /*1C Total xfer len             */
+       U32 SCB_PAddr;          /*20 SCB phy. Addr. */
+
+       UBYTE SCB_Opcode;       /*24 SCB command code */
+       UBYTE SCB_Flags;        /*25 SCB Flags */
+       UBYTE SCB_Target;       /*26 Target Id */
+       UBYTE SCB_Lun;          /*27 Lun */
+       U32 SCB_BufPtr;         /*28 Data Buffer Pointer */
+       U32 SCB_BufLen;         /*2C Data Allocation Length */
+       UBYTE SCB_SGLen;        /*30 SG list # */
+       UBYTE SCB_SenseLen;     /*31 Sense Allocation Length */
+       UBYTE SCB_HaStat;       /*32 */
+       UBYTE SCB_TaStat;       /*33 */
+       UBYTE SCB_CDBLen;       /*34 CDB Length */
+       UBYTE SCB_Ident;        /*35 Identify */
+       UBYTE SCB_TagMsg;       /*36 Tag Message */
+       UBYTE SCB_TagId;        /*37 Queue Tag */
+       UBYTE SCB_CDB[12];      /*38 */
+       U32 SCB_SGPAddr;        /*44 SG List/Sense Buf phy. Addr. */
+       U32 SCB_SensePtr;       /*48 Sense data pointer */
+       void (*SCB_Post) (BYTE *, BYTE *);      /*4C POST routine */
+       struct scsi_cmnd *SCB_Srb;      /*50 SRB Pointer */
+       SG SCB_SGList[TOTAL_SG_ENTRY];  /*54 Start of SG list */
+} SCB;
+
+/* Bit Definition for SCB_Status */
+#define SCB_RENT        0x01
+#define SCB_PEND        0x02
+#define SCB_CONTIG      0x04   /* Contigent Allegiance */
+#define SCB_SELECT      0x08
+#define SCB_BUSY        0x10
+#define SCB_DONE        0x20
+
+
+/* Opcodes of SCB_Opcode */
+#define ExecSCSI        0x1
+#define BusDevRst       0x2
+#define AbortCmd        0x3
+
+
+/* Bit Definition for SCB_Mode */
+#define SCM_RSENS       0x01   /* request sense mode */
+
+
+/* Bit Definition for SCB_Flags */
+#define SCF_DONE        0x01
+#define SCF_POST        0x02
+#define SCF_SENSE       0x04
+#define SCF_DIR         0x18
+#define SCF_NO_DCHK     0x00
+#define SCF_DIN         0x08
+#define SCF_DOUT        0x10
+#define SCF_NO_XF       0x18
+#define SCF_WR_VF       0x20   /* Write verify turn on         */
+#define SCF_POLL        0x40
+#define SCF_SG          0x80
+
+/* Error Codes for SCB_HaStat */
+#define HOST_SEL_TOUT   0x11
+#define HOST_DO_DU      0x12
+#define HOST_BUS_FREE   0x13
+#define HOST_BAD_PHAS   0x14
+#define HOST_INV_CMD    0x16
+#define HOST_ABORTED    0x1A   /* 07/21/98 */
+#define HOST_SCSI_RST   0x1B
+#define HOST_DEV_RST    0x1C
+
+/* Error Codes for SCB_TaStat */
+#define TARGET_CHKCOND  0x02
+#define TARGET_BUSY     0x08
+#define INI_QUEUE_FULL 0x28
+
+/* SCSI MESSAGE */
+#define MSG_COMP        0x00
+#define MSG_EXTEND      0x01
+#define MSG_SDP         0x02
+#define MSG_RESTORE     0x03
+#define MSG_DISC        0x04
+#define MSG_IDE         0x05
+#define MSG_ABORT       0x06
+#define MSG_REJ         0x07
+#define MSG_NOP         0x08
+#define MSG_PARITY      0x09
+#define MSG_LINK_COMP   0x0A
+#define MSG_LINK_FLAG   0x0B
+#define MSG_DEVRST      0x0C
+#define MSG_ABORT_TAG   0x0D
+
+/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */
+#define MSG_STAG        0x20
+#define MSG_HTAG        0x21
+#define MSG_OTAG        0x22
+
+#define MSG_IGNOREWIDE  0x23
+
+#define MSG_IDENT   0x80
+
+/***********************************************************************
+               Target Device Control Structure
+**********************************************************************/
+
+typedef struct Tar_Ctrl_Struc {
+       UWORD TCS_Flags;        /* 0 */
+       UBYTE TCS_JS_Period;    /* 2 */
+       UBYTE TCS_SConfig0;     /* 3 */
+
+       UWORD TCS_DrvFlags;     /* 4 */
+       UBYTE TCS_DrvHead;      /* 6 */
+       UBYTE TCS_DrvSector;    /* 7 */
+} TCS;
+
+/***********************************************************************
+               Target Device Control Structure
+**********************************************************************/
+
+/* Bit Definition for TCF_Flags */
+#define TCF_SCSI_RATE           0x0007
+#define TCF_EN_DISC             0x0008
+#define TCF_NO_SYNC_NEGO        0x0010
+#define TCF_NO_WDTR             0x0020
+#define TCF_EN_255              0x0040
+#define TCF_EN_START            0x0080
+#define TCF_WDTR_DONE           0x0100
+#define TCF_SYNC_DONE           0x0200
+#define TCF_BUSY                0x0400
+
+
+/* Bit Definition for TCF_DrvFlags */
+#define TCF_DRV_BUSY            0x01   /* Indicate target busy(driver) */
+#define TCF_DRV_EN_TAG          0x0800
+#define TCF_DRV_255_63          0x0400
+
+typedef struct I91u_Adpt_Struc {
+       UWORD ADPT_BIOS;        /* 0 */
+       UWORD ADPT_BASE;        /* 1 */
+       UBYTE ADPT_Bus;         /* 2 */
+       UBYTE ADPT_Device;      /* 3 */
+       UBYTE ADPT_INTR;        /* 4 */
+} INI_ADPT_STRUCT;
+
+
+/***********************************************************************
+             Host Adapter Control Structure
+************************************************************************/
+typedef struct Ha_Ctrl_Struc {
+       UWORD HCS_Base;         /* 00 */
+       UWORD HCS_BIOS;         /* 02 */
+       UBYTE HCS_Intr;         /* 04 */
+       UBYTE HCS_SCSI_ID;      /* 05 */
+       UBYTE HCS_MaxTar;       /* 06 */
+       UBYTE HCS_NumScbs;      /* 07 */
+
+       UBYTE HCS_Flags;        /* 08 */
+       UBYTE HCS_Index;        /* 09 */
+       UBYTE HCS_HaId;         /* 0A */
+       UBYTE HCS_Config;       /* 0B */
+       UWORD HCS_IdMask;       /* 0C */
+       UBYTE HCS_Semaph;       /* 0E */
+       UBYTE HCS_Phase;        /* 0F */
+       UBYTE HCS_JSStatus0;    /* 10 */
+       UBYTE HCS_JSInt;        /* 11 */
+       UBYTE HCS_JSStatus1;    /* 12 */
+       UBYTE HCS_SConf1;       /* 13 */
+
+       UBYTE HCS_Msg[8];       /* 14 */
+       SCB *HCS_NxtAvail;      /* 1C */
+       SCB *HCS_Scb;           /* 20 */
+       SCB *HCS_ScbEnd;        /* 24 */
+       SCB *HCS_NxtPend;       /* 28 */
+       SCB *HCS_NxtContig;     /* 2C */
+       SCB *HCS_ActScb;        /* 30 */
+       TCS *HCS_ActTcs;        /* 34 */
+
+       SCB *HCS_FirstAvail;    /* 38 */
+       SCB *HCS_LastAvail;     /* 3C */
+       SCB *HCS_FirstPend;     /* 40 */
+       SCB *HCS_LastPend;      /* 44 */
+       SCB *HCS_FirstBusy;     /* 48 */
+       SCB *HCS_LastBusy;      /* 4C */
+       SCB *HCS_FirstDone;     /* 50 */
+       SCB *HCS_LastDone;      /* 54 */
+       UBYTE HCS_MaxTags[16];  /* 58 */
+       UBYTE HCS_ActTags[16];  /* 68 */
+       TCS HCS_Tcs[MAX_TARGETS];       /* 78 */
+       spinlock_t HCS_AvailLock;
+       spinlock_t HCS_SemaphLock;
+       struct pci_dev *pci_dev;
+} HCS;
+
+/* Bit Definition for HCB_Config */
+#define HCC_SCSI_RESET          0x01
+#define HCC_EN_PAR              0x02
+#define HCC_ACT_TERM1           0x04
+#define HCC_ACT_TERM2           0x08
+#define HCC_AUTO_TERM           0x10
+#define HCC_EN_PWR              0x80
+
+/* Bit Definition for HCB_Flags */
+#define HCF_EXPECT_DISC         0x01
+#define HCF_EXPECT_SELECT       0x02
+#define HCF_EXPECT_RESET        0x10
+#define HCF_EXPECT_DONE_DISC    0x20
+
+/******************************************************************
+       Serial EEProm
+*******************************************************************/
+
+typedef struct _NVRAM_SCSI {   /* SCSI channel configuration   */
+       UCHAR NVM_ChSCSIID;     /* 0Ch -> Channel SCSI ID       */
+       UCHAR NVM_ChConfig1;    /* 0Dh -> Channel config 1      */
+       UCHAR NVM_ChConfig2;    /* 0Eh -> Channel config 2      */
+       UCHAR NVM_NumOfTarg;    /* 0Fh -> Number of SCSI target */
+       /* SCSI target configuration    */
+       UCHAR NVM_Targ0Config;  /* 10h -> Target 0 configuration */
+       UCHAR NVM_Targ1Config;  /* 11h -> Target 1 configuration */
+       UCHAR NVM_Targ2Config;  /* 12h -> Target 2 configuration */
+       UCHAR NVM_Targ3Config;  /* 13h -> Target 3 configuration */
+       UCHAR NVM_Targ4Config;  /* 14h -> Target 4 configuration */
+       UCHAR NVM_Targ5Config;  /* 15h -> Target 5 configuration */
+       UCHAR NVM_Targ6Config;  /* 16h -> Target 6 configuration */
+       UCHAR NVM_Targ7Config;  /* 17h -> Target 7 configuration */
+       UCHAR NVM_Targ8Config;  /* 18h -> Target 8 configuration */
+       UCHAR NVM_Targ9Config;  /* 19h -> Target 9 configuration */
+       UCHAR NVM_TargAConfig;  /* 1Ah -> Target A configuration */
+       UCHAR NVM_TargBConfig;  /* 1Bh -> Target B configuration */
+       UCHAR NVM_TargCConfig;  /* 1Ch -> Target C configuration */
+       UCHAR NVM_TargDConfig;  /* 1Dh -> Target D configuration */
+       UCHAR NVM_TargEConfig;  /* 1Eh -> Target E configuration */
+       UCHAR NVM_TargFConfig;  /* 1Fh -> Target F configuration */
+} NVRAM_SCSI;
+
+typedef struct _NVRAM {
+/*----------header ---------------*/
+       USHORT NVM_Signature;   /* 0,1: Signature */
+       UCHAR NVM_Size;         /* 2:   Size of data structure */
+       UCHAR NVM_Revision;     /* 3:   Revision of data structure */
+       /* ----Host Adapter Structure ---- */
+       UCHAR NVM_ModelByte0;   /* 4:   Model number (byte 0) */
+       UCHAR NVM_ModelByte1;   /* 5:   Model number (byte 1) */
+       UCHAR NVM_ModelInfo;    /* 6:   Model information         */
+       UCHAR NVM_NumOfCh;      /* 7:   Number of SCSI channel */
+       UCHAR NVM_BIOSConfig1;  /* 8:   BIOS configuration 1  */
+       UCHAR NVM_BIOSConfig2;  /* 9:   BIOS configuration 2  */
+       UCHAR NVM_HAConfig1;    /* A:   Hoat adapter configuration 1 */
+       UCHAR NVM_HAConfig2;    /* B:   Hoat adapter configuration 2 */
+       NVRAM_SCSI NVM_SCSIInfo[2];
+       UCHAR NVM_reserved[10];
+       /* ---------- CheckSum ----------       */
+       USHORT NVM_CheckSum;    /* 0x3E, 0x3F: Checksum of NVRam        */
+} NVRAM, *PNVRAM;
+
+/* Bios Configuration for nvram->BIOSConfig1                            */
+#define NBC1_ENABLE             0x01   /* BIOS enable                  */
+#define NBC1_8DRIVE             0x02   /* Support more than 2 drives   */
+#define NBC1_REMOVABLE          0x04   /* Support removable drive      */
+#define NBC1_INT19              0x08   /* Intercept int 19h            */
+#define NBC1_BIOSSCAN           0x10   /* Dynamic BIOS scan            */
+#define NBC1_LUNSUPPORT         0x40   /* Support LUN                  */
+
+/* HA Configuration Byte 1                                              */
+#define NHC1_BOOTIDMASK 0x0F   /* Boot ID number               */
+#define NHC1_LUNMASK    0x70   /* Boot LUN number              */
+#define NHC1_CHANMASK   0x80   /* Boot Channel number          */
+
+/* Bit definition for nvram->SCSIconfig1                                */
+#define NCC1_BUSRESET           0x01   /* Reset SCSI bus at power up   */
+#define NCC1_PARITYCHK          0x02   /* SCSI parity enable           */
+#define NCC1_ACTTERM1           0x04   /* Enable active terminator 1   */
+#define NCC1_ACTTERM2           0x08   /* Enable active terminator 2   */
+#define NCC1_AUTOTERM           0x10   /* Enable auto terminator       */
+#define NCC1_PWRMGR             0x80   /* Enable power management      */
+
+/* Bit definition for SCSI Target configuration byte                    */
+#define NTC_DISCONNECT          0x08   /* Enable SCSI disconnect       */
+#define NTC_SYNC                0x10   /* SYNC_NEGO                    */
+#define NTC_NO_WDTR             0x20   /* SYNC_NEGO                    */
+#define NTC_1GIGA               0x40   /* 255 head / 63 sectors (64/32) */
+#define NTC_SPINUP              0x80   /* Start disk drive             */
+
+/*      Default NVRam values                                            */
+#define INI_SIGNATURE           0xC925
+#define NBC1_DEFAULT            (NBC1_ENABLE)
+#define NCC1_DEFAULT            (NCC1_BUSRESET | NCC1_AUTOTERM | NCC1_PARITYCHK)
+#define NTC_DEFAULT             (NTC_NO_WDTR | NTC_1GIGA | NTC_DISCONNECT)
+
+/* SCSI related definition                                              */
+#define DISC_NOT_ALLOW          0x80   /* Disconnect is not allowed    */
+#define DISC_ALLOW              0xC0   /* Disconnect is allowed        */
+#define SCSICMD_RequestSense    0x03
+
+typedef struct _HCSinfo {
+       ULONG base;
+       UCHAR vec;
+       UCHAR bios;             /* High byte of BIOS address */
+       USHORT BaseAndBios;     /* high byte: pHcsInfo->bios,low byte:pHcsInfo->base */
+} HCSINFO;
+
+#define TUL_RD(x,y)             (UCHAR)(inb(  (int)((ULONG)(x+y)) ))
+#define TUL_RDLONG(x,y)         (ULONG)(inl((int)((ULONG)(x+y)) ))
+#define TUL_WR(     adr,data)   outb( (UCHAR)(data), (int)(adr))
+#define TUL_WRSHORT(adr,data)   outw( (UWORD)(data), (int)(adr))
+#define TUL_WRLONG( adr,data)   outl( (ULONG)(data), (int)(adr))
+
+#define SCSI_ABORT_SNOOZE 0
+#define SCSI_ABORT_SUCCESS 1
+#define SCSI_ABORT_PENDING 2
+#define SCSI_ABORT_BUSY 3
+#define SCSI_ABORT_NOT_RUNNING 4
+#define SCSI_ABORT_ERROR 5
+
+#define SCSI_RESET_SNOOZE 0
+#define SCSI_RESET_PUNT 1
+#define SCSI_RESET_SUCCESS 2
+#define SCSI_RESET_PENDING 3
+#define SCSI_RESET_WAKEUP 4
+#define SCSI_RESET_NOT_RUNNING 5
+#define SCSI_RESET_ERROR 6
+
+#define SCSI_RESET_SYNCHRONOUS         0x01
+#define SCSI_RESET_ASYNCHRONOUS                0x02
+#define SCSI_RESET_SUGGEST_BUS_RESET   0x04
+#define SCSI_RESET_SUGGEST_HOST_RESET  0x08
+
+#define SCSI_RESET_BUS_RESET 0x100
+#define SCSI_RESET_HOST_RESET 0x200
+#define SCSI_RESET_ACTION   0xff
+
+extern void init_i91uAdapter_table(void);
+extern int Addi91u_into_Adapter_table(WORD, WORD, BYTE, BYTE, BYTE);
+extern int tul_ReturnNumberOfAdapters(void);
+extern void get_tulipPCIConfig(HCS * pHCB, int iChannel_index);
+extern int init_tulip(HCS * pHCB, SCB * pSCB, int tul_num_scb, BYTE * pbBiosAdr, int reset_time);
+extern SCB *tul_alloc_scb(HCS * pHCB);
+extern int tul_abort_srb(HCS * pHCB, struct scsi_cmnd * pSRB);
+extern void tul_exec_scb(HCS * pHCB, SCB * pSCB);
+extern void tul_release_scb(HCS * pHCB, SCB * pSCB);
+extern void tul_stop_bm(HCS * pHCB);
+extern int tul_reset_scsi(HCS * pCurHcb, int seconds);
+extern int tul_isr(HCS * pHCB);
+extern int tul_reset(HCS * pHCB, struct scsi_cmnd * pSRB, unsigned char target);
+extern int tul_reset_scsi_bus(HCS * pCurHcb);
+extern int tul_device_reset(HCS * pCurHcb, struct scsi_cmnd *pSrb,
+               unsigned int target, unsigned int ResetFlags);
+                               /* ---- EXTERNAL VARIABLES ---- */
+extern HCS tul_hcs[];
diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile
new file mode 100644 (file)
index 0000000..5f06e63
--- /dev/null
@@ -0,0 +1,7 @@
+# Driver for Emulex LightPulse fibre channel host bus adapters.
+EXTRA_CFLAGS +=  -DFC_TRANS_VER1
+obj-$(CONFIG_SCSI_LPFC) := lpfc.o
+
+lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o \
+lpfc_hbadisc.o lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o lpfc_scsiport.o \
+lpfc_fcp.o
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
new file mode 100644 (file)
index 0000000..4fea35c
--- /dev/null
@@ -0,0 +1,449 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc.h 1.134 2004/11/18 14:53:45EST sf_support Exp  $
+ */
+
+#ifndef _H_LPFC
+#define _H_LPFC
+
+struct lpfc_sli2_slim;
+
+#define LPFC_MAX_TARGET                 256    /* max nunber of targets
+                                                  supported */
+#define LPFC_MAX_DISC_THREADS           64     /* max outstanding discovery els
+                                                  requests */
+#define LPFC_MAX_NS_RETRY               3      /* Try to get to the NameServer
+                                                  3 times and then give up. */
+#define LPFC_DFT_HBA_Q_DEPTH            2048   /* max cmds per hba */
+#define LPFC_LC_HBA_Q_DEPTH             1024   /* max cmds per low cost hba */
+#define LPFC_LP101_HBA_Q_DEPTH          128    /* max cmds per low cost hba */
+
+/* Define the SLIM2 page size. */
+#define LPFC_SLIM2_PAGE_AREA  8192
+
+/* Define macros for 64 bit support */
+#define putPaddrLow(addr)    ((uint32_t) (0xffffffff & (u64)(addr)))
+#define putPaddrHigh(addr)   ((uint32_t) (0xffffffff & (((u64)(addr))>>32)))
+#define getPaddr(high, low)  ((dma_addr_t)( \
+                            (( (u64)(high)<<16 ) << 16)|( (u64)(low))))
+/* Provide maximum configuration definitions. */
+#define LPFC_DRVR_TIMEOUT  16          /* driver iocb timeout value in sec */
+#define MAX_FCP_TARGET     256         /* max num of FCP targets supported */
+#define FC_MAX_ADPTMSG     64
+
+#define MAX_HBAEVT 32
+
+#if __LITTLE_ENDIAN
+
+#define putLunLow(lunlow, lun)              \
+   {                                        \
+   lunlow = 0;                              \
+   }
+
+#define putLunHigh(lunhigh, lun)            \
+   {                                        \
+   lunhigh = swab16(lun);                   \
+   }
+
+#else                          /* BIG_ENDIAN_HOST */
+
+#define putLunLow(lunlow, lun)              \
+   {                                        \
+   lunlow = 0;                              \
+   }
+
+#define putLunHigh(lunhigh, lun)            \
+   {                                        \
+   lunhigh = (uint32_t)(lun << 16);         \
+   }
+#endif
+
+/****************************************************************************/
+/*      Device VPD save area                                                */
+/****************************************************************************/
+typedef struct lpfc_vpd {
+       uint32_t status;        /* vpd status value */
+       uint32_t length;        /* number of bytes actually returned */
+       struct {
+               uint32_t rsvd1; /* Revision numbers */
+               uint32_t biuRev;
+               uint32_t smRev;
+               uint32_t smFwRev;
+               uint32_t endecRev;
+               uint16_t rBit;
+               uint8_t fcphHigh;
+               uint8_t fcphLow;
+               uint8_t feaLevelHigh;
+               uint8_t feaLevelLow;
+               uint32_t postKernRev;
+               uint32_t opFwRev;
+               uint8_t opFwName[16];
+               uint32_t sli1FwRev;
+               uint8_t sli1FwName[16];
+               uint32_t sli2FwRev;
+               uint8_t sli2FwName[16];
+       } rev;
+} lpfc_vpd_t;
+
+struct lpfc_scsi_buf;
+
+struct lpfc_hba_event {
+       uint32_t fc_eventcode;
+       uint32_t fc_evdata1;
+       uint32_t fc_evdata2;
+       uint32_t fc_evdata3;
+       uint32_t fc_evdata4;
+};
+
+/*
+ * lpfc stat counters
+ */
+struct lpfc_stats {
+       /* Statistics for ELS commands */
+       uint32_t elsLogiCol;
+       uint32_t elsRetryExceeded;
+       uint32_t elsXmitRetry;
+       uint32_t elsDelayRetry;
+       uint32_t elsRcvDrop;
+       uint32_t elsRcvFrame;
+       uint32_t elsRcvRSCN;
+       uint32_t elsRcvRNID;
+       uint32_t elsRcvFARP;
+       uint32_t elsRcvFARPR;
+       uint32_t elsRcvFLOGI;
+       uint32_t elsRcvPLOGI;
+       uint32_t elsRcvADISC;
+       uint32_t elsRcvPDISC;
+       uint32_t elsRcvFAN;
+       uint32_t elsRcvLOGO;
+       uint32_t elsRcvPRLO;
+       uint32_t elsRcvPRLI;
+       uint32_t elsRcvRRQ;
+       uint32_t elsXmitFLOGI;
+       uint32_t elsXmitPLOGI;
+       uint32_t elsXmitPRLI;
+       uint32_t elsXmitADISC;
+       uint32_t elsXmitLOGO;
+       uint32_t elsXmitSCR;
+       uint32_t elsXmitRNID;
+       uint32_t elsXmitFARP;
+       uint32_t elsXmitFARPR;
+       uint32_t elsXmitACC;
+       uint32_t elsXmitLSRJT;
+
+       uint32_t frameRcvBcast;
+       uint32_t frameRcvMulti;
+       uint32_t strayXmitCmpl;
+       uint32_t frameXmitDelay;
+       uint32_t xriCmdCmpl;
+       uint32_t xriStatErr;
+       uint32_t LinkUp;
+       uint32_t LinkDown;
+       uint32_t LinkMultiEvent;
+       uint32_t NoRcvBuf;
+       uint32_t fcpCmd;
+       uint32_t fcpCmpl;
+       uint32_t fcpRspErr;
+       uint32_t fcpRemoteStop;
+       uint32_t fcpPortRjt;
+       uint32_t fcpPortBusy;
+       uint32_t fcpError;
+       uint32_t fcpLocalErr;
+};
+
+enum sysfs_mbox_state {
+       SMBOX_IDLE,
+       SMBOX_WRITING,
+       SMBOX_READING
+};
+
+struct lpfc_sysfs_mbox {
+       enum sysfs_mbox_state state;
+       size_t                offset;
+       struct lpfcMboxq *    mbox;
+};
+
+struct lpfc_hba {
+       uint32_t intr_inited;           /* flag for interrupt registration */
+       struct list_head hba_list;      /* List of hbas/ports */
+       struct lpfc_sli sli;
+       struct lpfc_sli2_slim *slim2p;
+       dma_addr_t slim2p_mapping;
+
+       uint32_t hba_state;
+
+#define LPFC_INIT_START           1    /* Initial state after board reset */
+#define LPFC_INIT_MBX_CMDS        2    /* Initialize HBA with mbox commands */
+#define LPFC_LINK_DOWN            3    /* HBA initialized, link is down */
+#define LPFC_LINK_UP              4    /* Link is up  - issue READ_LA */
+#define LPFC_LOCAL_CFG_LINK       5    /* local NPORT Id configured */
+#define LPFC_FLOGI                6    /* FLOGI sent to Fabric */
+#define LPFC_FABRIC_CFG_LINK      7    /* Fabric assigned NPORT Id
+                                          configured */
+#define LPFC_NS_REG               8    /* Register with NameServer */
+#define LPFC_NS_QRY               9    /* Query NameServer for NPort ID list */
+#define LPFC_BUILD_DISC_LIST      10   /* Build ADISC and PLOGI lists for
+                                        * device authentication / discovery */
+#define LPFC_DISC_AUTH            11   /* Processing ADISC list */
+#define LPFC_CLEAR_LA             12   /* authentication cmplt - issue
+                                          CLEAR_LA */
+#define LPFC_HBA_READY            32
+#define LPFC_HBA_ERROR            0xff
+
+       uint8_t fc_linkspeed;   /* Link speed after last READ_LA */
+
+       uint32_t fc_eventTag;   /* event tag for link attention */
+       uint32_t fc_prli_sent;  /* cntr for outstanding PRLIs */
+
+       uint32_t num_disc_nodes;        /*in addition to hba_state */
+
+       uint8_t fcp_mapping;    /* Map FCP devices based on WWNN WWPN or DID */
+#define FCP_SEED_WWNN   0x1
+#define FCP_SEED_WWPN   0x2
+#define FCP_SEED_DID    0x4
+#define FCP_SEED_MASK   0x7
+#define FCP_SEED_AUTO   0x8    /* binding was created by auto mapping */
+
+       struct timer_list fc_estabtmo;  /* link establishment timer */
+       struct timer_list fc_disctmo;   /* Discovery rescue timer */
+       struct timer_list fc_fdmitmo;   /* fdmi timer */
+       struct timer_list fc_scantmo;   /* scsi scan host timer */
+
+
+       void *fc_evt_head;      /* waiting for event queue */
+       void *fc_evt_tail;      /* waiting for event queue */
+
+       uint16_t hba_event_put; /* hbaevent event put word anchor */
+       uint16_t hba_event_get; /* hbaevent event get word anchor */
+       uint32_t hba_event_missed;      /* hbaevent missed event word anchor */
+       uint32_t sid_cnt;       /* SCSI ID counter */
+
+       struct lpfc_hba_event hbaevt[MAX_HBAEVT];
+
+       /* These fields used to be binfo */
+       struct lpfc_name fc_nodename;   /* fc nodename */
+       struct lpfc_name fc_portname;   /* fc portname */
+       uint32_t fc_pref_DID;   /* preferred D_ID */
+       uint8_t fc_pref_ALPA;   /* preferred AL_PA */
+       uint32_t fc_edtov;      /* E_D_TOV timer value */
+       uint32_t fc_arbtov;     /* ARB_TOV timer value */
+       uint32_t fc_ratov;      /* R_A_TOV timer value */
+       uint32_t fc_rttov;      /* R_T_TOV timer value */
+       uint32_t fc_altov;      /* AL_TOV timer value */
+       uint32_t fc_crtov;      /* C_R_TOV timer value */
+       uint32_t fc_citov;      /* C_I_TOV timer value */
+       uint32_t fc_myDID;      /* fibre channel S_ID */
+       uint32_t fc_prevDID;    /* previous fibre channel S_ID */
+
+       struct serv_parm fc_sparam;     /* buffer for our service parameters */
+       struct serv_parm fc_fabparam;   /* fabric service parameters buffer */
+       uint8_t alpa_map[128];  /* AL_PA map from READ_LA */
+
+       uint8_t fc_ns_retry;    /* retries for fabric nameserver */
+       uint32_t fc_nlp_cnt;    /* outstanding NODELIST requests */
+       uint32_t fc_rscn_id_cnt;        /* count of RSCNs payloads in list */
+       struct lpfc_dmabuf *fc_rscn_id_list[FC_MAX_HOLD_RSCN];
+       uint32_t lmt;
+       uint32_t fc_flag;       /* FC flags */
+#define FC_PT2PT                0x1    /* pt2pt with no fabric */
+#define FC_PT2PT_PLOGI          0x2    /* pt2pt initiate PLOGI */
+#define FC_DISC_TMO             0x4    /* Discovery timer running */
+#define FC_PUBLIC_LOOP          0x8    /* Public loop */
+#define FC_LBIT                 0x10   /* LOGIN bit in loopinit set */
+#define FC_RSCN_MODE            0x20   /* RSCN cmd rcv'ed */
+#define FC_NLP_MORE             0x40   /* More node to process in node tbl */
+#define FC_OFFLINE_MODE         0x80   /* Interface is offline for diag */
+#define FC_FABRIC               0x100  /* We are fabric attached */
+#define FC_ESTABLISH_LINK       0x200  /* Reestablish Link */
+#define FC_RSCN_DISCOVERY       0x400  /* Authenticate all devices after RSCN*/
+#define FC_LOADING             0x1000  /* HBA in process of loading drvr */
+#define FC_UNLOADING           0x2000  /* HBA in process of unloading drvr */
+#define FC_SCSI_SCAN_TMO        0x4000         /* scsi scan timer running */
+#define FC_ABORT_DISCOVERY      0x8000         /* we want to abort discovery */
+
+       uint32_t fc_topology;   /* link topology, from LINK INIT */
+
+       struct lpfc_stats fc_stat;
+
+       /* These are the head/tail pointers for the bind, plogi, adisc, unmap,
+        *  and map lists.  Their counters are immediately following.
+        */
+       struct list_head fc_nlpbind_list;
+       struct list_head fc_plogi_list;
+       struct list_head fc_adisc_list;
+       struct list_head fc_reglogin_list;
+       struct list_head fc_prli_list;
+       struct list_head fc_nlpunmap_list;
+       struct list_head fc_nlpmap_list;
+       struct list_head fc_npr_list;
+       struct list_head fc_unused_list;
+
+       /* Keep counters for the number of entries in each list. */
+       uint16_t fc_bind_cnt;
+       uint16_t fc_plogi_cnt;
+       uint16_t fc_adisc_cnt;
+       uint16_t fc_reglogin_cnt;
+       uint16_t fc_prli_cnt;
+       uint16_t fc_unmap_cnt;
+       uint16_t fc_map_cnt;
+       uint16_t fc_npr_cnt;
+       uint16_t fc_unused_cnt;
+       struct lpfc_nodelist fc_fcpnodev; /* nodelist entry for no device */
+       uint32_t nport_event_cnt;       /* timestamp for nlplist entry */
+
+       struct lpfc_target *device_queue_hash[MAX_FCP_TARGET];
+#define LPFC_RPI_HASH_SIZE     64
+#define LPFC_RPI_HASH_FUNC(x)  ((x) & (0x3f))
+       /* ptr to active D_ID / RPIs */
+       struct lpfc_nodelist *fc_nlplookup[LPFC_RPI_HASH_SIZE];
+       uint32_t wwnn[2];
+       uint32_t RandomData[7];
+
+       uint32_t cfg_log_verbose;
+       uint32_t cfg_lun_queue_depth;
+       uint32_t cfg_nodev_tmo;
+       uint32_t cfg_hba_queue_depth;
+       uint32_t cfg_automap;
+       uint32_t cfg_fcp_class;
+       uint32_t cfg_use_adisc;
+       uint32_t cfg_ack0;
+       uint32_t cfg_topology;
+       uint32_t cfg_scan_down;
+       uint32_t cfg_link_speed;
+       uint32_t cfg_cr_delay;
+       uint32_t cfg_cr_count;
+       uint32_t cfg_fdmi_on;
+       uint32_t cfg_fcp_bind_method;
+       uint32_t cfg_discovery_threads;
+       uint32_t cfg_max_luns;
+       uint32_t cfg_scsi_hotplug;
+
+       lpfc_vpd_t vpd;         /* vital product data */
+
+#if defined(FC_TRANS_265_BLKPATCH)
+       /*
+        * Provide a per-HBA timer for 2.6.5 kernels patched with the
+        * block/unblock FC transport patch.
+        */
+       struct timer_list dev_loss_timer;
+#endif
+
+       struct Scsi_Host *host;
+       struct pci_dev *pcidev;
+       struct list_head      dpc_disc;
+
+       pid_t                 dpc_pid;
+       int                   dpc_kill;
+       struct completion     dpc_startup;
+       struct completion     dpc_exiting;
+       struct semaphore     *dpc_wait;
+
+       unsigned long pci_bar0_map;     /* Physical address for PCI BAR0 */
+       unsigned long pci_bar2_map;     /* Physical address for PCI BAR2 */
+       void *slim_memmap_p;            /* Kernel memory mapped address for PCI
+                                          BAR0 */
+       void *ctrl_regs_memmap_p;       /* Kernel memory mapped address for PCI
+                                          BAR2 */
+
+       void *MBslimaddr;       /* virtual address for mbox cmds */
+       void *HAregaddr;        /* virtual address for host attn reg */
+       void *CAregaddr;        /* virtual address for chip attn reg */
+       void *HSregaddr;        /* virtual address for host status reg */
+       void *HCregaddr;        /* virtual address for host ctl reg */
+       wait_queue_head_t linkevtwq;
+       wait_queue_head_t rscnevtwq;
+       wait_queue_head_t ctevtwq;
+
+       uint8_t brd_no;         /* FC board number */
+
+       char SerialNumber[32];  /* adapter Serial Number */
+       char OptionROMVersion[32];      /* adapter BIOS / Fcode version */
+
+       struct timer_list els_tmofunc;
+
+       void *link_stats;
+
+       /*
+        * stat  counters
+        */
+       uint64_t fc4InputRequests;
+       uint64_t fc4OutputRequests;
+       uint64_t fc4ControlRequests;
+
+       struct lpfc_sysfs_mbox sysfs_mbox;
+;
+       /* pci_mem_pools */
+       struct pci_pool *lpfc_scsi_dma_ext_pool;
+       struct pci_pool *lpfc_mbuf_pool;
+       struct lpfc_dma_pool lpfc_mbuf_safety_pool;
+       mempool_t *scsibuf_mem_pool;
+
+       mempool_t *iocb_mem_pool;
+       mempool_t *mbox_mem_pool;
+       mempool_t *nlp_mem_pool;
+       mempool_t *bind_mem_pool;
+       struct list_head freebufList;
+       struct list_head ctrspbuflist;
+       struct list_head rnidrspbuflist;
+};
+
+/* event mask definitions */
+#define FC_REG_LINK_EVENT       0x1    /* Register for link up / down events */
+#define FC_REG_RSCN_EVENT       0x2    /* Register for RSCN events */
+#define FC_REG_CT_EVENT         0x4    /* Register for CT request events */
+
+#define FC_FSTYPE_ALL 0xffff   /* match on all fsTypes */
+
+typedef struct fcEVT {         /* Kernel level Event structure */
+       uint32_t evt_handle;
+       uint32_t evt_mask;
+       uint32_t evt_data0;
+       uint16_t evt_sleep;
+       uint16_t evt_flags;
+       void    *evt_type;
+       void    *evt_next;
+       void    *evt_data1;
+       uint32_t evt_data2;
+} fcEVT_t;
+
+typedef struct fcEVTHDR {      /* Kernel level Event Header */
+       uint32_t e_handle;
+       uint32_t e_mask;
+       uint16_t e_mode;
+#define E_SLEEPING_MODE     0x0001
+       uint16_t e_refcnt;
+       uint16_t e_flag;
+#define E_GET_EVENT_ACTIVE  0x0001
+       fcEVT_t *e_head;
+       fcEVT_t *e_tail;
+       void    *e_next_header;
+       void    *e_type;
+} fcEVTHDR_t;
+
+struct rnidrsp {
+       void *buf;
+       uint32_t uniqueid;
+       struct list_head list;
+       uint32_t data;
+};
+
+#endif                         /* _H_LPFC */
diff --git a/drivers/scsi/lpfc/lpfc_compat.h b/drivers/scsi/lpfc/lpfc_compat.h
new file mode 100644 (file)
index 0000000..43531b5
--- /dev/null
@@ -0,0 +1,120 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_compat.h 1.28 2004/11/09 14:49:24EST sf_support Exp  $
+ *
+ * This file provides macros to aid compilation in the Linux 2.4 kernel
+ * over various platform architectures.
+ */
+
+#ifndef _H_LPFC_COMPAT
+#define  _H_LPFC_COMPAT
+
+
+/*******************************************************************
+Note: HBA's SLI memory contains little-endian LW.
+Thus to access it from a little-endian host,
+memcpy_toio() and memcpy_fromio() can be used.
+However on a big-endian host, copy 4 bytes at a time,
+using writel() and readl().
+ *******************************************************************/
+
+#if __BIG_ENDIAN
+
+static inline void
+lpfc_memcpy_to_slim( void *dest, void *src, unsigned int bytes)
+{
+       uint32_t *dest32;
+       uint32_t *src32;
+       unsigned int four_bytes;
+
+
+       dest32  = (uint32_t *) dest;
+       src32  = (uint32_t *) src;
+
+       /* write input bytes, 4 bytes at a time */
+       for (four_bytes = bytes /4; four_bytes > 0; four_bytes--) {
+               writel( *src32, dest32);
+               readl(dest32); /* flush */
+               dest32++;
+               src32++;
+       }
+
+       return;
+}
+
+static inline void
+lpfc_memcpy_from_slim( void *dest, void *src, unsigned int bytes)
+{
+       uint32_t *dest32;
+       uint32_t *src32;
+       unsigned int four_bytes;
+
+
+       dest32  = (uint32_t *) dest;
+       src32  = (uint32_t *) src;
+
+       /* read input bytes, 4 bytes at a time */
+       for (four_bytes = bytes /4; four_bytes > 0; four_bytes--) {
+               *dest32 = readl( src32);
+               dest32++;
+               src32++;
+       }
+
+       return;
+}
+
+#else
+
+static inline void
+lpfc_memcpy_to_slim( void *dest, void *src, unsigned int bytes)
+{
+       /* actually returns 1 byte past dest */
+       memcpy_toio( dest, src, bytes);
+}
+
+static inline void
+lpfc_memcpy_from_slim( void *dest, void *src, unsigned int bytes)
+{
+       /* actually returns 1 byte past dest */
+       memcpy_fromio( dest, src, bytes);
+}
+
+#endif /* __BIG_ENDIAN */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6)
+#define msleep(x) do { \
+                       set_current_state(TASK_UNINTERRUPTIBLE); \
+                       schedule_timeout((x)); \
+               } while (0);
+/* Provide local msecs_to_jiffies call for earlier kernels */
+static inline unsigned long msecs_to_jiffies(const unsigned int m)
+{
+#if HZ <= 1000 && !(1000 % HZ)
+       return (m + (1000 / HZ) - 1) / (1000 / HZ);
+#elif HZ > 1000 && !(HZ % 1000)
+       return m * (HZ / 1000);
+#else
+       return (m * HZ + 999) / 1000;
+#endif
+}
+#endif
+#endif                         /*  _H_LPFC_COMPAT */
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
new file mode 100644 (file)
index 0000000..ac414f8
--- /dev/null
@@ -0,0 +1,265 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_crtn.h 1.140 2004/11/17 19:00:57EST sf_support Exp  $
+ */
+
+#ifndef _H_LPFC_CRTN
+#define _H_LPFC_CRTN
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+
+#include "lpfc_disc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_scsi.h"
+#include "lpfc_sli.h"
+
+
+void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_config_link(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_read_sparam(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_read_config(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_set_slim(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
+int lpfc_reg_login(struct lpfc_hba *, uint32_t, uint8_t *, LPFC_MBOXQ_t *,
+                  uint32_t);
+void lpfc_unreg_login(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *);
+void lpfc_unreg_did(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *);
+void lpfc_init_link(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t);
+
+
+int lpfc_linkdown(struct lpfc_hba *);
+void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
+
+void lpfc_mbx_cmpl_clear_la(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+int lpfc_consistent_bind_save(struct lpfc_hba *, struct lpfc_bindlist *);
+int lpfc_nlp_plogi(struct lpfc_hba *, struct lpfc_nodelist *);
+int lpfc_nlp_adisc(struct lpfc_hba *, struct lpfc_nodelist *);
+int lpfc_nlp_unmapped(struct lpfc_hba *, struct lpfc_nodelist *);
+int lpfc_nlp_mapped(struct lpfc_hba *, struct lpfc_nodelist *,
+                   struct lpfc_bindlist *);
+int lpfc_nlp_list(struct lpfc_hba *, struct lpfc_nodelist *, int);
+void lpfc_set_disctmo(struct lpfc_hba *);
+int lpfc_can_disctmo(struct lpfc_hba *);
+int lpfc_unreg_rpi(struct lpfc_hba *, struct lpfc_nodelist *);
+int lpfc_check_sli_ndlp(struct lpfc_hba *, struct lpfc_sli_ring *,
+                   struct lpfc_iocbq *, struct lpfc_nodelist *);
+int lpfc_nlp_remove(struct lpfc_hba *, struct lpfc_nodelist *);
+void lpfc_nlp_init(struct lpfc_hba *, struct lpfc_nodelist *, uint32_t);
+struct lpfc_nodelist *lpfc_setup_disc_node(struct lpfc_hba *, uint32_t);
+struct lpfc_nodelist *lpfc_setup_rscn_node(struct lpfc_hba *, uint32_t);
+void lpfc_disc_list_loopmap(struct lpfc_hba *);
+void lpfc_disc_start(struct lpfc_hba *);
+void lpfc_disc_flush_list(struct lpfc_hba *);
+void lpfc_establish_link_tmo(unsigned long);
+void lpfc_disc_timeout(unsigned long);
+void lpfc_scan_timeout(unsigned long);
+struct lpfc_target *lpfc_find_target(struct lpfc_hba *, uint32_t,
+                       struct lpfc_nodelist *);
+void lpfc_set_failmask(struct lpfc_hba *, struct lpfc_nodelist *, uint32_t,
+                      uint32_t);
+void lpfc_process_nodev_timeout(struct lpfc_hba *, struct lpfc_nodelist *);
+
+struct lpfc_nodelist *lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi);
+struct lpfc_nodelist *lpfc_findnode_remove_rpi(struct lpfc_hba * phba,
+                                              uint16_t rpi);
+void lpfc_addnode_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+                     uint16_t rpi);
+
+int lpfc_discq_post_event(struct lpfc_hba *, void *, void *, uint32_t);
+int lpfc_do_dpc(void *);
+int lpfc_disc_state_machine(struct lpfc_hba *, struct lpfc_nodelist *, void *,
+                           uint32_t);
+
+uint32_t lpfc_cmpl_prli_reglogin_issue(struct lpfc_hba *,
+                                      struct lpfc_nodelist *, void *,
+                                      uint32_t);
+uint32_t lpfc_cmpl_plogi_prli_issue(struct lpfc_hba *, struct lpfc_nodelist *,
+                                   void *, uint32_t);
+
+int lpfc_check_sparm(struct lpfc_hba *, struct lpfc_nodelist *,
+                    struct serv_parm *, uint32_t);
+int lpfc_els_abort_flogi(struct lpfc_hba *);
+int lpfc_initial_flogi(struct lpfc_hba *);
+int lpfc_issue_els_plogi(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t);
+int lpfc_issue_els_prli(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t);
+int lpfc_issue_els_adisc(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t);
+int lpfc_issue_els_logo(struct lpfc_hba *, struct lpfc_nodelist *, uint8_t);
+int lpfc_issue_els_scr(struct lpfc_hba *, uint32_t, uint8_t);
+int lpfc_els_free_iocb(struct lpfc_hba *, struct lpfc_iocbq *);
+int lpfc_els_rsp_acc(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *,
+                    struct lpfc_nodelist *, LPFC_MBOXQ_t *, uint8_t);
+int lpfc_els_rsp_reject(struct lpfc_hba *, uint32_t, struct lpfc_iocbq *,
+                       struct lpfc_nodelist *);
+int lpfc_els_rsp_adisc_acc(struct lpfc_hba *, struct lpfc_iocbq *,
+                          struct lpfc_nodelist *);
+int lpfc_els_rsp_prli_acc(struct lpfc_hba *, struct lpfc_iocbq *,
+                         struct lpfc_nodelist *);
+void lpfc_els_retry_delay(unsigned long);
+void lpfc_els_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
+                         struct lpfc_iocbq *);
+int lpfc_els_handle_rscn(struct lpfc_hba *);
+int lpfc_els_flush_rscn(struct lpfc_hba *);
+int lpfc_rscn_payload_check(struct lpfc_hba *, uint32_t);
+void lpfc_els_flush_cmd(struct lpfc_hba *);
+int lpfc_els_disc_adisc(struct lpfc_hba *);
+int lpfc_els_disc_plogi(struct lpfc_hba *);
+void lpfc_els_timeout_handler(unsigned long ptr);
+
+void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
+                        struct lpfc_iocbq *);
+int lpfc_ns_cmd(struct lpfc_hba *, struct lpfc_nodelist *, int);
+int lpfc_fdmi_cmd(struct lpfc_hba *, struct lpfc_nodelist *, int);
+void lpfc_fdmi_tmo(unsigned long);
+
+int lpfc_config_port_prep(struct lpfc_hba *);
+int lpfc_config_port_post(struct lpfc_hba *);
+int lpfc_hba_down_prep(struct lpfc_hba *);
+void lpfc_handle_eratt(struct lpfc_hba *, uint32_t);
+void lpfc_handle_latt(struct lpfc_hba *);
+void lpfc_hba_init(struct lpfc_hba *, uint32_t *);
+int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int, int);
+void lpfc_cleanup(struct lpfc_hba *, uint32_t);
+int lpfc_scsi_free(struct lpfc_hba *);
+void lpfc_decode_firmware_rev(struct lpfc_hba *, char *, int);
+uint8_t *lpfc_get_lpfchba_info(struct lpfc_hba *, uint8_t *);
+int lpfc_fcp_abort(struct lpfc_hba *, int, int, int);
+int lpfc_put_event(struct lpfc_hba *, uint32_t, uint32_t, void *,
+                      uint32_t, uint32_t);
+void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);
+int lpfc_online(struct lpfc_hba *);
+int lpfc_offline(struct lpfc_hba *);
+
+
+
+int lpfc_sli_queue_setup(struct lpfc_hba *);
+void lpfc_slim_access(struct lpfc_hba *);
+
+void lpfc_handle_eratt(struct lpfc_hba *, uint32_t);
+void lpfc_handle_latt(struct lpfc_hba *);
+irqreturn_t lpfc_intr_handler(int, void *, struct pt_regs *);
+
+void lpfc_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_config_ring(struct lpfc_hba *, int, LPFC_MBOXQ_t *);
+void lpfc_config_port(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_mbox_put(struct lpfc_hba *, LPFC_MBOXQ_t *);
+LPFC_MBOXQ_t *lpfc_mbox_get(struct lpfc_hba *);
+
+int lpfc_mem_alloc(struct lpfc_hba *);
+void lpfc_mem_free(struct lpfc_hba *);
+
+struct lpfc_iocbq *
+lpfc_prep_els_iocb(struct lpfc_hba * phba,
+                  uint8_t expectRsp,
+                  uint16_t cmdSize,
+                  uint8_t retry, struct lpfc_nodelist * ndlp, uint32_t elscmd);
+
+int lpfc_sli_hba_setup(struct lpfc_hba *);
+int lpfc_sli_hba_down(struct lpfc_hba *);
+int lpfc_sli_intr(struct lpfc_hba *);
+int lpfc_sli_issue_mbox(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
+int lpfc_sli_issue_iocb(struct lpfc_hba *, struct lpfc_sli_ring *,
+                       struct lpfc_iocbq *, uint32_t);
+void lpfc_sli_pcimem_bcopy(uint32_t *, uint32_t *, uint32_t);
+int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *,
+                            struct lpfc_dmabuf *);
+struct lpfc_dmabuf *lpfc_sli_ringpostbuf_get(struct lpfc_hba *,
+                                            struct lpfc_sli_ring *,
+                                            dma_addr_t);
+uint32_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_sli_ring *);
+int lpfc_sli_issue_abort_iotag32(struct lpfc_hba *, struct lpfc_sli_ring *,
+                                struct lpfc_iocbq *);
+int lpfc_sli_abort_iocb_ctx(struct lpfc_hba *, struct lpfc_sli_ring *,
+                           uint32_t);
+int lpfc_sli_sum_iocb_host(struct lpfc_hba *, struct lpfc_sli_ring *);
+int lpfc_sli_abort_iocb_host(struct lpfc_hba *, struct lpfc_sli_ring *, int);
+int lpfc_sli_sum_iocb_lun(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t,
+                         uint64_t);
+int lpfc_sli_abort_iocb_lun(struct lpfc_hba *, struct lpfc_sli_ring *, uint16_t,
+                           uint64_t, int);
+int lpfc_sli_abort_iocb_tgt(struct lpfc_hba *, struct lpfc_sli_ring *,
+                           uint16_t, int);
+void lpfc_mbox_timeout(unsigned long);
+
+void lpfc_map_fcp_cmnd_to_bpl(struct lpfc_hba *, struct lpfc_scsi_buf *);
+void lpfc_free_scsi_cmd(struct lpfc_scsi_buf *);
+uint32_t lpfc_os_timeout_transform(struct lpfc_hba *, uint32_t);
+
+struct lpfc_nodelist *
+lpfc_findnode_wwpn(struct lpfc_hba * phba, uint32_t order,
+                  struct lpfc_name * wwpn);
+struct lpfc_nodelist *
+lpfc_findnode_wwnn(struct lpfc_hba * phba, uint32_t order,
+                  struct lpfc_name * wwnn);
+struct lpfc_nodelist *lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order,
+                                       uint32_t did);
+
+void lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp);
+
+int lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq,
+                        uint32_t timeout);
+
+int
+lpfc_sli_issue_iocb_wait(struct lpfc_hba * phba,
+                        struct lpfc_sli_ring * pring,
+                        struct lpfc_iocbq * piocb,
+                        struct lpfc_iocbq * prspiocbq, uint32_t timeout);
+int lpfc_sli_issue_iocb_wait_high_priority(struct lpfc_hba * phba,
+                                          struct lpfc_sli_ring * pring,
+                                          struct lpfc_iocbq * piocb,
+                                          uint32_t flag,
+                                          struct lpfc_iocbq * prspiocbq,
+                                          uint32_t timeout);
+void lpfc_sli_wake_iocb_high_priority(struct lpfc_hba * phba,
+                                     struct lpfc_iocbq * queue1,
+                                     struct lpfc_iocbq * queue2);
+void *lpfc_mbuf_alloc(struct lpfc_hba *, int, dma_addr_t *);
+void lpfc_mbuf_free(struct lpfc_hba *, void *, dma_addr_t);
+
+int  lpfc_stop_timer(struct lpfc_hba *);
+
+
+/* Function prototypes. */
+int lpfc_queuecommand(struct scsi_cmnd *, void (*done) (struct scsi_cmnd *));
+int lpfc_abort_handler(struct scsi_cmnd *);
+int lpfc_reset_bus_handler(struct scsi_cmnd *);
+int lpfc_reset_lun_handler(struct scsi_cmnd *);
+
+#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH)
+void lpfc_target_unblock(struct lpfc_hba *, struct lpfc_target *);
+void lpfc_target_block(struct lpfc_hba *, struct lpfc_target *);
+int lpfc_target_remove(struct lpfc_hba *, struct lpfc_target *);
+int lpfc_target_add(struct lpfc_hba *, struct lpfc_target *);
+#endif
+
+#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
+#define HBA_EVENT_RSCN                   5
+#define HBA_EVENT_LINK_UP                2
+#define HBA_EVENT_LINK_DOWN              3
+#endif                         /* _H_LPFC_CRTN */
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
new file mode 100644 (file)
index 0000000..83342c4
--- /dev/null
@@ -0,0 +1,1288 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_ct.c 1.143 2004/11/17 14:50:38EST sf_support Exp  $
+ *
+ * Fibre Channel SCSI LAN Device Driver CT support
+ */
+
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/utsname.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+#include "lpfc_hw.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_mem.h"
+#include "lpfc_version.h"
+
+
+#define HBA_PORTSPEED_UNKNOWN               0  /* Unknown - transceiver 
+                                                * incapable of reporting */
+#define HBA_PORTSPEED_1GBIT                 1  /* 1 GBit/sec */
+#define HBA_PORTSPEED_2GBIT                 2  /* 2 GBit/sec */
+#define HBA_PORTSPEED_4GBIT                 8   /* 4 GBit/sec */
+#define HBA_PORTSPEED_8GBIT                16   /* 8 GBit/sec */
+#define HBA_PORTSPEED_10GBIT                4  /* 10 GBit/sec */
+#define HBA_PORTSPEED_NOT_NEGOTIATED        5  /* Speed not established */
+
+#define FOURBYTES      4
+
+
+static char *lpfc_release_version = LPFC_DRIVER_VERSION;
+
+/*
+ * lpfc_ct_unsol_event
+ */
+void
+lpfc_ct_unsol_event(struct lpfc_hba * phba,
+                   struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocbq)
+{
+
+       struct lpfc_iocbq *next_piocbq;
+       struct lpfc_dmabuf *pmbuf = NULL;
+       struct lpfc_dmabuf *matp, *next_matp;
+       uint32_t ctx = 0, count = 0;
+       IOCB_t *icmd = &piocbq->iocb;
+       int i, status, go_exit = 0;
+       struct list_head head;
+
+       if (icmd->ulpStatus)
+               return;
+
+       list_add_tail(&head, &piocbq->list);
+       list_for_each_entry_safe(piocbq, next_piocbq, &head, list) {
+               icmd = &piocbq->iocb;
+               if (ctx == 0)
+                       ctx = (uint32_t) (icmd->ulpContext);
+               if (icmd->ulpStatus) {
+                       if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+                               ((icmd->un.ulpWord[4] & 0xff)
+                                == IOERR_RCV_BUFFER_WAITING)) {
+                               phba->fc_stat.NoRcvBuf++;
+                               lpfc_post_buffer(phba, pring, 0, 1);
+                       }
+                       go_exit = 1;
+                       goto ct_unsol_event_exit_piocbq;
+               }
+
+               if (icmd->ulpBdeCount == 0)
+                       continue;
+
+               for (i = 0; i < icmd->ulpBdeCount; i++) {
+                       matp = lpfc_sli_ringpostbuf_get(phba, pring,
+                                                       getPaddr(icmd->un.
+                                                                cont64[i].
+                                                                addrHigh,
+                                                                icmd->un.
+                                                                cont64[i].
+                                                                addrLow));
+                       if (!matp) {
+                               /* Insert lpfc log message here */
+                               go_exit = 1;
+                               goto ct_unsol_event_exit_piocbq;
+                       }
+
+                       /* Typically for Unsolicited CT requests */
+                       if (!pmbuf) {
+                               pmbuf = matp;
+                               INIT_LIST_HEAD(&pmbuf->list);
+                       } else
+                               list_add_tail(&matp->list, &pmbuf->list);
+
+                       count += icmd->un.cont64[i].tus.f.bdeSize;
+               }
+
+               lpfc_post_buffer(phba, pring, i, 1);
+               icmd->ulpBdeCount = 0;
+       }
+ct_unsol_event_exit_piocbq:
+       list_del(&head);
+       /*
+        * if not early-exiting and there is pmbuf,
+        * then do  FC_REG_CT_EVENT for libdfc
+        */
+       if (!go_exit  &&  pmbuf) {
+               status = lpfc_put_event(phba, FC_REG_CT_EVENT, ctx,
+                                      (void *)pmbuf, count, 0);
+               if (status)
+                       /* Need to free IOCB buffer ? */
+                       return;
+       }
+       if (pmbuf) {
+               list_for_each_entry_safe(matp, next_matp, &pmbuf->list, list) {
+                       lpfc_mbuf_free(phba, matp->virt, matp->phys);
+                       list_del(&matp->list);
+                       kfree(matp);
+               }
+               lpfc_mbuf_free(phba, pmbuf->virt, pmbuf->phys);
+               kfree(pmbuf);
+       }
+       return;
+}
+
+static void
+lpfc_free_ct_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mlist)
+{
+       struct lpfc_dmabuf *mlast, *next_mlast;
+
+       list_for_each_entry_safe(mlast, next_mlast, &mlist->list, list) {
+               lpfc_mbuf_free(phba, mlast->virt, mlast->phys);
+               list_del(&mlast->list);
+               kfree(mlast);
+       }
+       lpfc_mbuf_free(phba, mlist->virt, mlist->phys);
+       kfree(mlist);
+       return;
+}
+
+static struct lpfc_dmabuf *
+lpfc_alloc_ct_rsp(struct lpfc_hba * phba, int cmdcode, struct ulp_bde64 * bpl,
+                 uint32_t size, int *entries)
+{
+       struct lpfc_dmabuf *mlist = NULL;
+       struct lpfc_dmabuf *mp;
+       int cnt, i = 0;
+
+       /* We get chucks of FCELSSIZE */
+       cnt = size > FCELSSIZE ? FCELSSIZE: size;
+
+       while (size) {
+               /* Allocate buffer for rsp payload */
+               mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_ATOMIC);
+               if (!mp) {
+                       if (mlist)
+                               lpfc_free_ct_rsp(phba, mlist);
+                       return NULL;
+               }
+
+               INIT_LIST_HEAD(&mp->list);
+
+               if (cmdcode == be16_to_cpu(SLI_CTNS_GID_FT))
+                       mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys));
+               else
+                       mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys));
+
+               if (!mp->virt) {
+                       kfree(mp);
+                       lpfc_free_ct_rsp(phba, mlist);
+                       return NULL;
+               }
+
+               /* Queue it to a linked list */
+               if (!mlist)
+                       mlist = mp;
+               else
+                       list_add_tail(&mp->list, &mlist->list);
+
+               bpl->tus.f.bdeFlags = BUFF_USE_RCV;
+               /* build buffer ptr list for IOCB */
+               bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) );
+               bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) );
+               bpl->tus.f.bdeSize = (uint16_t) cnt;
+               bpl->tus.w = le32_to_cpu(bpl->tus.w);
+               bpl++;
+
+               i++;
+               size -= cnt;
+       }
+
+       *entries = i;
+       return mlist;
+}
+
+static int
+lpfc_gen_req(struct lpfc_hba *phba, struct lpfc_dmabuf *bmp,
+            struct lpfc_dmabuf *inp, struct lpfc_dmabuf *outp,
+            void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
+                    struct lpfc_iocbq *),
+            struct lpfc_nodelist *ndlp, uint32_t usr_flg, uint32_t num_entry,
+            uint32_t tmo)
+{
+
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_sli_ring *pring = &psli->ring[LPFC_ELS_RING];
+       IOCB_t *icmd;
+       struct lpfc_iocbq *geniocb;
+
+       /* Allocate buffer for  command iocb */
+       geniocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC);
+       if (!geniocb) {
+               return 1;
+       }
+       memset(geniocb, 0, sizeof (struct lpfc_iocbq));
+       icmd = &geniocb->iocb;
+
+       icmd->un.genreq64.bdl.ulpIoTag32 = 0;
+       icmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
+       icmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys);
+       icmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BDL;
+       icmd->un.genreq64.bdl.bdeSize = (num_entry * sizeof (struct ulp_bde64));
+
+       if (usr_flg)
+               geniocb->context3 = NULL;
+       else
+               geniocb->context3 = (uint8_t *) bmp;
+
+       /* Save for completion so we can release these resources */
+       geniocb->context1 = (uint8_t *) inp;
+       geniocb->context2 = (uint8_t *) outp;
+
+       /* Fill in payload, bp points to frame payload */
+       icmd->ulpCommand = CMD_GEN_REQUEST64_CR;
+
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       icmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring);
+
+       /* Fill in rest of iocb */
+       icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
+       icmd->un.genreq64.w5.hcsw.Dfctl = 0;
+       icmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL;
+       icmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP;
+
+       if (!tmo)
+               tmo = (2 * phba->fc_ratov) + 1;
+       icmd->ulpTimeout = tmo;
+       icmd->ulpBdeCount = 1;
+       icmd->ulpLe = 1;
+       icmd->ulpClass = CLASS3;
+       icmd->ulpContext = ndlp->nlp_rpi;
+
+       /* Issue GEN REQ IOCB for NPORT <did> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0119 Issue GEN REQ IOCB for NPORT x%x "
+                       "Data: x%x x%x\n", phba->brd_no, icmd->un.ulpWord[5],
+                       icmd->ulpIoTag, phba->hba_state);
+       geniocb->iocb_cmpl = cmpl;
+       geniocb->drvrTimeout = icmd->ulpTimeout + LPFC_DRVR_TIMEOUT;
+       if (lpfc_sli_issue_iocb(phba, pring, geniocb, 0) == IOCB_ERROR) {
+               mempool_free( geniocb, phba->iocb_mem_pool);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int
+lpfc_ct_cmd(struct lpfc_hba *phba, struct lpfc_dmabuf *inmp,
+           struct lpfc_dmabuf *bmp, struct lpfc_nodelist *ndlp,
+           void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
+                         struct lpfc_iocbq *),
+           uint32_t rsp_size)
+{
+       struct ulp_bde64 *bpl = (struct ulp_bde64 *) bmp->virt;
+       struct lpfc_dmabuf *outmp;
+       int cnt = 0, status;
+       int cmdcode = ((struct lpfc_sli_ct_request *) inmp->virt)->
+               CommandResponse.bits.CmdRsp;
+
+       bpl++;                  /* Skip past ct request */
+
+       /* Put buffer(s) for ct rsp in bpl */
+       outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt);
+       if (!outmp)
+               return -ENOMEM;
+
+       status = lpfc_gen_req(phba, bmp, inmp, outmp, cmpl, ndlp, 0,
+                             cnt+1, 0);
+       if (status) {
+               lpfc_free_ct_rsp(phba, outmp);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static int
+lpfc_ns_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mp, uint32_t Size)
+{
+       struct lpfc_sli_ct_request *Response =
+               (struct lpfc_sli_ct_request *) mp->virt;
+       struct lpfc_nodelist *ndlp = NULL;
+       struct lpfc_dmabuf *mlast, *next_mp;
+       uint32_t *ctptr = (uint32_t *) & Response->un.gid.PortType;
+       uint32_t Did;
+       uint32_t CTentry;
+       int Cnt;
+       struct list_head head;
+
+       lpfc_set_disctmo(phba);
+
+       Cnt = Size  > FCELSSIZE ? FCELSSIZE : Size;
+
+       list_add_tail(&head, &mp->list);
+       list_for_each_entry_safe(mp, next_mp, &head, list) {
+               mlast = mp;
+               pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys,
+                       LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+               Size -= Cnt;
+
+               if (!ctptr)
+                       ctptr = (uint32_t *) mlast->virt;
+               else
+                       Cnt -= 16;      /* subtract length of CT header */
+
+               /* Loop through entire NameServer list of DIDs */
+               while (Cnt) {
+
+                       /* Get next DID from NameServer List */
+                       CTentry = *ctptr++;
+                       Did = ((be32_to_cpu(CTentry)) & Mask_DID);
+
+                       ndlp = NULL;
+                       if (Did != phba->fc_myDID) {
+                               /* Check for rscn processing or not */
+                               ndlp = lpfc_setup_disc_node(phba, Did);
+                       }
+                       /* Mark all node table entries that are in the
+                          Nameserver */
+                       if (ndlp) {
+                               /* NameServer Rsp */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                                               "%d:0238 Process x%x NameServer"
+                                               " Rsp Data: x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               Did, ndlp->nlp_flag,
+                                               phba->fc_flag,
+                                               phba->fc_rscn_id_cnt);
+                       } else {
+                               /* NameServer Rsp */
+                               lpfc_printf_log(phba,
+                                               KERN_INFO,
+                                               LOG_DISCOVERY,
+                                               "%d:0239 Skip x%x NameServer "
+                                               "Rsp Data: x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               Did, Size, phba->fc_flag,
+                                               phba->fc_rscn_id_cnt);
+                       }
+
+                       if (CTentry & (be32_to_cpu(SLI_CT_LAST_ENTRY)))
+                               goto nsout1;
+                       Cnt -= sizeof (uint32_t);
+               }
+               ctptr = NULL;
+
+       }
+
+nsout1:
+       list_del(&head);
+
+       /* Here we are finished in the case RSCN */
+       if (phba->hba_state == LPFC_HBA_READY) {
+               lpfc_els_flush_rscn(phba);
+               phba->fc_flag |= FC_RSCN_MODE; /* we are still in RSCN mode */
+       }
+       return 0;
+}
+
+
+
+
+static void
+lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                       struct lpfc_iocbq * rspiocb)
+{
+       IOCB_t *irsp;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       struct lpfc_dmabuf *inp;
+       struct lpfc_dmabuf *outp;
+       struct lpfc_nodelist *ndlp;
+       struct lpfc_sli_ct_request *CTrsp;
+
+       psli = &phba->sli;
+       /* we pass cmdiocb to state machine which needs rspiocb as well */
+       cmdiocb->context_un.rsp_iocb = rspiocb;
+
+       inp = (struct lpfc_dmabuf *) cmdiocb->context1;
+       outp = (struct lpfc_dmabuf *) cmdiocb->context2;
+       bmp = (struct lpfc_dmabuf *) cmdiocb->context3;
+
+       irsp = &rspiocb->iocb;
+       if (irsp->ulpStatus) {
+               if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+                       ((irsp->un.ulpWord[4] == IOERR_SLI_DOWN) ||
+                        (irsp->un.ulpWord[4] == IOERR_SLI_ABORTED))) {
+                       goto out;
+               }
+
+               /* Check for retry */
+               if (phba->fc_ns_retry < LPFC_MAX_NS_RETRY) {
+                       phba->fc_ns_retry++;
+                       /* CT command is being retried */
+                       ndlp =
+                           lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED,
+                                             NameServer_DID);
+                       if (ndlp) {
+                               if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) ==
+                                   0) {
+                                       goto out;
+                               }
+                       }
+               }
+       } else {
+               /* Good status, continue checking */
+               CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
+               if (CTrsp->CommandResponse.bits.CmdRsp ==
+                   be16_to_cpu(SLI_CT_RESPONSE_FS_ACC)) {
+                       lpfc_ns_rsp(phba, outp,
+                                   (uint32_t) (irsp->un.genreq64.bdl.bdeSize));
+               } else if (CTrsp->CommandResponse.bits.CmdRsp ==
+                          be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) {
+                       /* NameServer Rsp Error */
+                       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                                       "%d:0240 NameServer Rsp Error "
+                                       "Data: x%x x%x x%x x%x\n",
+                                       phba->brd_no,
+                                       CTrsp->CommandResponse.bits.CmdRsp,
+                                       (uint32_t) CTrsp->ReasonCode,
+                                       (uint32_t) CTrsp->Explanation,
+                                       phba->fc_flag);
+               } else {
+                       /* NameServer Rsp Error */
+                       lpfc_printf_log(phba,
+                                       KERN_INFO,
+                                       LOG_DISCOVERY,
+                                       "%d:0241 NameServer Rsp Error "
+                                       "Data: x%x x%x x%x x%x\n",
+                                       phba->brd_no,
+                                       CTrsp->CommandResponse.bits.CmdRsp,
+                                       (uint32_t) CTrsp->ReasonCode,
+                                       (uint32_t) CTrsp->Explanation,
+                                       phba->fc_flag);
+               }
+       }
+       /* Link up / RSCN discovery */
+       lpfc_disc_start(phba);
+out:
+       lpfc_free_ct_rsp(phba, outp);
+       lpfc_mbuf_free(phba, inp->virt, inp->phys);
+       lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+       kfree(inp);
+       kfree(bmp);
+       mempool_free( cmdiocb, phba->iocb_mem_pool);
+       return;
+}
+
+static void
+lpfc_cmpl_ct_cmd_rft_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                       struct lpfc_iocbq * rspiocb)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       struct lpfc_dmabuf *inp;
+       struct lpfc_dmabuf *outp;
+       IOCB_t *irsp;
+       struct lpfc_sli_ct_request *CTrsp;
+
+       psli = &phba->sli;
+       /* we pass cmdiocb to state machine which needs rspiocb as well */
+       cmdiocb->context_un.rsp_iocb = rspiocb;
+
+       inp = (struct lpfc_dmabuf *) cmdiocb->context1;
+       outp = (struct lpfc_dmabuf *) cmdiocb->context2;
+       bmp = (struct lpfc_dmabuf *) cmdiocb->context3;
+       irsp = &rspiocb->iocb;
+
+       CTrsp = (struct lpfc_sli_ct_request *) outp->virt;
+
+       /* RFT request completes status <ulpStatus> CmdRsp <CmdRsp> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                       "%d:0209 RFT request completes ulpStatus x%x "
+                       "CmdRsp x%x\n", phba->brd_no, irsp->ulpStatus,
+                       CTrsp->CommandResponse.bits.CmdRsp);
+
+       lpfc_free_ct_rsp(phba, outp);
+       lpfc_mbuf_free(phba, inp->virt, inp->phys);
+       lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+       kfree(inp);
+       kfree(bmp);
+       mempool_free( cmdiocb, phba->iocb_mem_pool);
+       return;
+}
+
+static void
+lpfc_cmpl_ct_cmd_rnn_id(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                       struct lpfc_iocbq * rspiocb)
+{
+       lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb);
+       return;
+}
+
+static void
+lpfc_cmpl_ct_cmd_rsnn_nn(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                        struct lpfc_iocbq * rspiocb)
+{
+       lpfc_cmpl_ct_cmd_rft_id(phba, cmdiocb, rspiocb);
+       return;
+}
+
+void
+lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp)
+{
+       uint8_t buf[16];
+       char fwrev[16];
+
+       lpfc_decode_firmware_rev(phba, fwrev, 0);
+       lpfc_get_hba_model_desc(phba, buf, NULL);
+       sprintf(symbp, "Emulex %s FV%s DV%s", buf, fwrev, lpfc_release_version);
+}
+
+/*
+ * lpfc_ns_cmd
+ * Description:
+ *    Issue Cmd to NameServer
+ *       SLI_CTNS_GID_FT
+ *       LI_CTNS_RFT_ID
+ */
+int
+lpfc_ns_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode)
+{
+       struct lpfc_dmabuf *mp, *bmp;
+       struct lpfc_sli_ct_request *CtReq;
+       struct ulp_bde64 *bpl;
+       void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
+                     struct lpfc_iocbq *) = NULL;
+       uint32_t rsp_size = 1024;
+
+       /* fill in BDEs for command */
+       /* Allocate buffer for command payload */
+       mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC);
+       if (!mp)
+               goto ns_cmd_exit;
+
+       INIT_LIST_HEAD(&mp->list);
+       mp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(mp->phys));
+       if (!mp->virt)
+               goto ns_cmd_free_mp;
+
+       /* Allocate buffer for Buffer ptr list */
+       bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC);
+       if (!bmp)
+               goto ns_cmd_free_mpvirt;
+
+       INIT_LIST_HEAD(&bmp->list);
+       bmp->virt = lpfc_mbuf_alloc(phba, MEM_PRI, &(bmp->phys));
+       if (!bmp->virt)
+               goto ns_cmd_free_bmp;
+
+       /* NameServer Req */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_DISCOVERY,
+                       "%d:0236 NameServer Req Data: x%x x%x x%x\n",
+                       phba->brd_no, cmdcode, phba->fc_flag,
+                       phba->fc_rscn_id_cnt);
+
+       bpl = (struct ulp_bde64 *) bmp->virt;
+       memset(bpl, 0, sizeof(struct ulp_bde64));
+       bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) );
+       bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) );
+       bpl->tus.f.bdeFlags = 0;
+       if (cmdcode == SLI_CTNS_GID_FT)
+               bpl->tus.f.bdeSize = GID_REQUEST_SZ;
+       else if (cmdcode == SLI_CTNS_RFT_ID)
+               bpl->tus.f.bdeSize = RFT_REQUEST_SZ;
+       else if (cmdcode == SLI_CTNS_RNN_ID)
+               bpl->tus.f.bdeSize = RNN_REQUEST_SZ;
+       else if (cmdcode == SLI_CTNS_RSNN_NN)
+               bpl->tus.f.bdeSize = RSNN_REQUEST_SZ;
+       else
+               bpl->tus.f.bdeSize = 0;
+       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+       CtReq = (struct lpfc_sli_ct_request *) mp->virt;
+       memset(CtReq, 0, sizeof (struct lpfc_sli_ct_request));
+       CtReq->RevisionId.bits.Revision = SLI_CT_REVISION;
+       CtReq->RevisionId.bits.InId = 0;
+       CtReq->FsType = SLI_CT_DIRECTORY_SERVICE;
+       CtReq->FsSubType = SLI_CT_DIRECTORY_NAME_SERVER;
+       CtReq->CommandResponse.bits.Size = 0;
+       switch (cmdcode) {
+       case SLI_CTNS_GID_FT:
+               CtReq->CommandResponse.bits.CmdRsp =
+                   be16_to_cpu(SLI_CTNS_GID_FT);
+               CtReq->un.gid.Fc4Type = SLI_CTPT_FCP;
+               if (phba->hba_state < LPFC_HBA_READY)
+                       phba->hba_state = LPFC_NS_QRY;
+               lpfc_set_disctmo(phba);
+               cmpl = lpfc_cmpl_ct_cmd_gid_ft;
+               rsp_size = FC_MAX_NS_RSP;
+               break;
+
+       case SLI_CTNS_RFT_ID:
+               CtReq->CommandResponse.bits.CmdRsp =
+                   be16_to_cpu(SLI_CTNS_RFT_ID);
+               CtReq->un.rft.PortId = be32_to_cpu(phba->fc_myDID);
+               CtReq->un.rft.fcpReg = 1;
+               cmpl = lpfc_cmpl_ct_cmd_rft_id;
+               break;
+
+       case SLI_CTNS_RNN_ID:
+               CtReq->CommandResponse.bits.CmdRsp =
+                   be16_to_cpu(SLI_CTNS_RNN_ID);
+               CtReq->un.rnn.PortId = be32_to_cpu(phba->fc_myDID);
+               memcpy(CtReq->un.rnn.wwnn,  &phba->fc_nodename,
+                      sizeof (struct lpfc_name));
+               cmpl = lpfc_cmpl_ct_cmd_rnn_id;
+               break;
+
+       case SLI_CTNS_RSNN_NN:
+               CtReq->CommandResponse.bits.CmdRsp =
+                   be16_to_cpu(SLI_CTNS_RSNN_NN);
+               memcpy(CtReq->un.rsnn.wwnn, &phba->fc_nodename,
+                      sizeof (struct lpfc_name));
+               lpfc_get_hba_sym_node_name(phba, CtReq->un.rsnn.symbname);
+               CtReq->un.rsnn.len = strlen(CtReq->un.rsnn.symbname);
+               cmpl = lpfc_cmpl_ct_cmd_rsnn_nn;
+               break;
+       }
+
+       if (!lpfc_ct_cmd(phba, mp, bmp, ndlp, cmpl, rsp_size))
+               /* On success, The cmpl function will free the buffers */
+               return 0;
+
+       lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+ns_cmd_free_bmp:
+       kfree(bmp);
+ns_cmd_free_mpvirt:
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ns_cmd_free_mp:
+       kfree(mp);
+ns_cmd_exit:
+       return 1;
+}
+
+static void
+lpfc_cmpl_ct_cmd_fdmi(struct lpfc_hba * phba,
+                     struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb)
+{
+       struct lpfc_dmabuf *bmp = cmdiocb->context3;
+       struct lpfc_dmabuf *inp = cmdiocb->context1;
+       struct lpfc_dmabuf *outp = cmdiocb->context2;
+       struct lpfc_sli_ct_request *CTrsp = outp->virt;
+       struct lpfc_sli_ct_request *CTcmd = inp->virt;
+       struct lpfc_nodelist *ndlp;
+       uint16_t fdmi_cmd = CTcmd->CommandResponse.bits.CmdRsp;
+       uint16_t fdmi_rsp = CTrsp->CommandResponse.bits.CmdRsp;
+
+       ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID);
+       if (fdmi_rsp == be16_to_cpu(SLI_CT_RESPONSE_FS_RJT)) {
+               /* FDMI rsp failed */
+               lpfc_printf_log(phba,
+                               KERN_INFO,
+                               LOG_DISCOVERY,
+                               "%d:0220 FDMI rsp failed Data: x%x\n",
+                               phba->brd_no,
+                              be16_to_cpu(fdmi_cmd));
+       }
+
+       switch (be16_to_cpu(fdmi_cmd)) {
+       case SLI_MGMT_RHBA:
+               lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_RPA);
+               break;
+
+       case SLI_MGMT_RPA:
+               break;
+
+       case SLI_MGMT_DHBA:
+               lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DPRT);
+               break;
+
+       case SLI_MGMT_DPRT:
+               lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_RHBA);
+               break;
+       }
+
+       lpfc_free_ct_rsp(phba, outp);
+       lpfc_mbuf_free(phba, inp->virt, inp->phys);
+       lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+       kfree(inp);
+       kfree(bmp);
+       mempool_free(cmdiocb, phba->iocb_mem_pool);
+       return;
+}
+int
+lpfc_fdmi_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode)
+{
+       struct lpfc_dmabuf *mp, *bmp;
+       struct lpfc_sli_ct_request *CtReq;
+       struct ulp_bde64 *bpl;
+       uint32_t size;
+       REG_HBA *rh;
+       PORT_ENTRY *pe;
+       REG_PORT_ATTRIBUTE *pab;
+       ATTRIBUTE_BLOCK *ab;
+       ATTRIBUTE_ENTRY *ae;
+       void (*cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
+                     struct lpfc_iocbq *);
+
+
+       /* fill in BDEs for command */
+       /* Allocate buffer for command payload */
+       mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC);
+       if (!mp)
+               goto fdmi_cmd_exit;
+
+       mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys));
+       if (!mp->virt)
+               goto fdmi_cmd_free_mp;
+
+       /* Allocate buffer for Buffer ptr list */
+       bmp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC);
+       if (!bmp)
+               goto fdmi_cmd_free_mpvirt;
+
+       bmp->virt = lpfc_mbuf_alloc(phba, 0, &(bmp->phys));
+       if (!bmp->virt)
+               goto fdmi_cmd_free_bmp;
+
+       INIT_LIST_HEAD(&mp->list);
+       INIT_LIST_HEAD(&bmp->list);
+
+       /* FDMI request */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_DISCOVERY,
+                       "%d:0218 FDMI Request Data: x%x x%x x%x\n",
+                       phba->brd_no,
+                      phba->fc_flag, phba->hba_state, cmdcode);
+
+       CtReq = (struct lpfc_sli_ct_request *) mp->virt;
+
+       memset(CtReq, 0, sizeof(struct lpfc_sli_ct_request));
+       CtReq->RevisionId.bits.Revision = SLI_CT_REVISION;
+       CtReq->RevisionId.bits.InId = 0;
+
+       CtReq->FsType = SLI_CT_MANAGEMENT_SERVICE;
+       CtReq->FsSubType = SLI_CT_FDMI_Subtypes;
+       size = 0;
+
+       switch (cmdcode) {
+       case SLI_MGMT_RHBA:
+               {
+                       lpfc_vpd_t *vp = &phba->vpd;
+                       uint32_t i, j, incr;
+                       int len;
+
+                       CtReq->CommandResponse.bits.CmdRsp =
+                           be16_to_cpu(SLI_MGMT_RHBA);
+                       CtReq->CommandResponse.bits.Size = 0;
+                       rh = (REG_HBA *) & CtReq->un.PortID;
+                       memcpy(&rh->hi.PortName, &phba->fc_sparam.portName,
+                              sizeof (struct lpfc_name));
+                       /* One entry (port) per adapter */
+                       rh->rpl.EntryCnt = be32_to_cpu(1);
+                       memcpy(&rh->rpl.pe, &phba->fc_sparam.portName,
+                              sizeof (struct lpfc_name));
+
+                       /* point to the HBA attribute block */
+                       size = 2 * sizeof (struct lpfc_name) + FOURBYTES;
+                       ab = (ATTRIBUTE_BLOCK *) ((uint8_t *) rh + size);
+                       ab->EntryCnt = 0;
+
+                       /* Point to the beginning of the first HBA attribute
+                          entry */
+                       /* #1 HBA attribute entry */
+                       size += FOURBYTES;
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(NODE_NAME);
+                       ae->ad.bits.AttrLen =  be16_to_cpu(FOURBYTES
+                                               + sizeof (struct lpfc_name));
+                       memcpy(&ae->un.NodeName, &phba->fc_sparam.nodeName,
+                              sizeof (struct lpfc_name));
+                       ab->EntryCnt++;
+                       size += FOURBYTES + sizeof (struct lpfc_name);
+
+                       /* #2 HBA attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(MANUFACTURER);
+                       strcpy(ae->un.Manufacturer, "Emulex Corporation");
+                       len = strlen(ae->un.Manufacturer);
+                       len += (len & 3) ? (4 - (len & 3)) : 4;
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+                       ab->EntryCnt++;
+                       size += FOURBYTES + len;
+
+                       /* #3 HBA attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(SERIAL_NUMBER);
+                       strcpy(ae->un.SerialNumber, phba->SerialNumber);
+                       len = strlen(ae->un.SerialNumber);
+                       len += (len & 3) ? (4 - (len & 3)) : 4;
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+                       ab->EntryCnt++;
+                       size += FOURBYTES + len;
+
+                       /* #4 HBA attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(MODEL);
+                       lpfc_get_hba_model_desc(phba, ae->un.Model, NULL);
+                       len = strlen(ae->un.Model);
+                       len += (len & 3) ? (4 - (len & 3)) : 4;
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+                       ab->EntryCnt++;
+                       size += FOURBYTES + len;
+
+                       /* #5 HBA attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(MODEL_DESCRIPTION);
+                       lpfc_get_hba_model_desc(phba, NULL,
+                                ae->un.ModelDescription);
+                       len = strlen(ae->un.ModelDescription);
+                       len += (len & 3) ? (4 - (len & 3)) : 4;
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+                       ab->EntryCnt++;
+                       size += FOURBYTES + len;
+
+                       /* #6 HBA attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(HARDWARE_VERSION);
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 8);
+                       /* Convert JEDEC ID to ascii for hardware version */
+                       incr = vp->rev.biuRev;
+                       for (i = 0; i < 8; i++) {
+                               j = (incr & 0xf);
+                               if (j <= 9)
+                                       ae->un.HardwareVersion[7 - i] =
+                                           (char)((uint8_t) 0x30 +
+                                                  (uint8_t) j);
+                               else
+                                       ae->un.HardwareVersion[7 - i] =
+                                           (char)((uint8_t) 0x61 +
+                                                  (uint8_t) (j - 10));
+                               incr = (incr >> 4);
+                       }
+                       ab->EntryCnt++;
+                       size += FOURBYTES + 8;
+
+                       /* #7 HBA attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(DRIVER_VERSION);
+                       strcpy(ae->un.DriverVersion, lpfc_release_version);
+                       len = strlen(ae->un.DriverVersion);
+                       len += (len & 3) ? (4 - (len & 3)) : 4;
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+                       ab->EntryCnt++;
+                       size += FOURBYTES + len;
+
+                       /* #8 HBA attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(OPTION_ROM_VERSION);
+                       strcpy(ae->un.OptionROMVersion, phba->OptionROMVersion);
+                       len = strlen(ae->un.OptionROMVersion);
+                       len += (len & 3) ? (4 - (len & 3)) : 4;
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+                       ab->EntryCnt++;
+                       size += FOURBYTES + len;
+
+                       /* #9 HBA attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(FIRMWARE_VERSION);
+                       lpfc_decode_firmware_rev(phba, ae->un.FirmwareVersion,
+                               1);
+                       len = strlen(ae->un.FirmwareVersion);
+                       len += (len & 3) ? (4 - (len & 3)) : 4;
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+                       ab->EntryCnt++;
+                       size += FOURBYTES + len;
+
+                       /* #10 HBA attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(OS_NAME_VERSION);
+                       sprintf(ae->un.OsNameVersion, "%s %s %s",
+                               system_utsname.sysname, system_utsname.release,
+                               system_utsname.version);
+                       len = strlen(ae->un.OsNameVersion);
+                       len += (len & 3) ? (4 - (len & 3)) : 4;
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+                       ab->EntryCnt++;
+                       size += FOURBYTES + len;
+
+                       /* #11 HBA attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(MAX_CT_PAYLOAD_LEN);
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
+                       ae->un.MaxCTPayloadLen = (65 * 4096);
+                       ab->EntryCnt++;
+                       size += FOURBYTES + 4;
+
+                       ab->EntryCnt = be32_to_cpu(ab->EntryCnt);
+                       /* Total size */
+                       size = GID_REQUEST_SZ - 4 + size;
+               }
+               break;
+
+       case SLI_MGMT_RPA:
+               {
+                       lpfc_vpd_t *vp;
+                       struct serv_parm *hsp;
+                       int len;
+
+                       vp = &phba->vpd;
+
+                       CtReq->CommandResponse.bits.CmdRsp =
+                           be16_to_cpu(SLI_MGMT_RPA);
+                       CtReq->CommandResponse.bits.Size = 0;
+                       pab = (REG_PORT_ATTRIBUTE *) & CtReq->un.PortID;
+                       size = sizeof (struct lpfc_name) + FOURBYTES;
+                       memcpy((uint8_t *) & pab->PortName,
+                              (uint8_t *) & phba->fc_sparam.portName,
+                              sizeof (struct lpfc_name));
+                       pab->ab.EntryCnt = 0;
+
+                       /* #1 Port attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_FC4_TYPES);
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 32);
+                       ae->un.SupportFC4Types[2] = 1;
+                       ae->un.SupportFC4Types[7] = 1;
+                       pab->ab.EntryCnt++;
+                       size += FOURBYTES + 32;
+
+                       /* #2 Port attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(SUPPORTED_SPEED);
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
+                       if (FC_JEDEC_ID(vp->rev.biuRev) == VIPER_JEDEC_ID)
+                               ae->un.SupportSpeed = HBA_PORTSPEED_10GBIT;
+                       else if (FC_JEDEC_ID(vp->rev.biuRev) == HELIOS_JEDEC_ID)
+                               ae->un.SupportSpeed = HBA_PORTSPEED_4GBIT;
+                       else if ((FC_JEDEC_ID(vp->rev.biuRev) ==
+                                 CENTAUR_2G_JEDEC_ID)
+                                || (FC_JEDEC_ID(vp->rev.biuRev) ==
+                                    PEGASUS_JEDEC_ID)
+                                || (FC_JEDEC_ID(vp->rev.biuRev) ==
+                                    THOR_JEDEC_ID))
+                               ae->un.SupportSpeed = HBA_PORTSPEED_2GBIT;
+                       else
+                               ae->un.SupportSpeed = HBA_PORTSPEED_1GBIT;
+                       pab->ab.EntryCnt++;
+                       size += FOURBYTES + 4;
+
+                       /* #3 Port attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(PORT_SPEED);
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
+                       switch(phba->fc_linkspeed) {
+                               case LA_1GHZ_LINK:
+                                       ae->un.PortSpeed = HBA_PORTSPEED_1GBIT;
+                               break;
+                               case LA_2GHZ_LINK:
+                                       ae->un.PortSpeed = HBA_PORTSPEED_2GBIT;
+                               break;
+                               case LA_4GHZ_LINK:
+                                       ae->un.PortSpeed = HBA_PORTSPEED_4GBIT;
+                               break;
+                               default:
+                                       ae->un.PortSpeed =
+                                               HBA_PORTSPEED_UNKNOWN;
+                               break;
+                       }
+                       pab->ab.EntryCnt++;
+                       size += FOURBYTES + 4;
+
+                       /* #4 Port attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(MAX_FRAME_SIZE);
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + 4);
+                       hsp = (struct serv_parm *) & phba->fc_sparam;
+                       ae->un.MaxFrameSize =
+                           (((uint32_t) hsp->cmn.
+                             bbRcvSizeMsb) << 8) | (uint32_t) hsp->cmn.
+                           bbRcvSizeLsb;
+                       pab->ab.EntryCnt++;
+                       size += FOURBYTES + 4;
+
+                       /* #5 Port attribute entry */
+                       ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab + size);
+                       ae->ad.bits.AttrType = be16_to_cpu(OS_DEVICE_NAME);
+                       strcpy((char *)ae->un.OsDeviceName, LPFC_DRIVER_NAME);
+                       len = strlen((char *)ae->un.OsDeviceName);
+                       len += (len & 3) ? (4 - (len & 3)) : 4;
+                       ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
+                       pab->ab.EntryCnt++;
+                       size += FOURBYTES + len;
+
+                       if (phba->cfg_fdmi_on == 2) {
+                               /* #6 Port attribute entry */
+                               ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) pab +
+                                                         size);
+                               ae->ad.bits.AttrType = be16_to_cpu(HOST_NAME);
+                               sprintf(ae->un.HostName, "%s",
+                                       system_utsname.nodename);
+                               len = strlen(ae->un.HostName);
+                               len += (len & 3) ? (4 - (len & 3)) : 4;
+                               ae->ad.bits.AttrLen =
+                                   be16_to_cpu(FOURBYTES + len);
+                               pab->ab.EntryCnt++;
+                               size += FOURBYTES + len;
+                       }
+
+                       pab->ab.EntryCnt = be32_to_cpu(pab->ab.EntryCnt);
+                       /* Total size */
+                       size = GID_REQUEST_SZ - 4 + size;
+               }
+               break;
+
+       case SLI_MGMT_DHBA:
+               CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DHBA);
+               CtReq->CommandResponse.bits.Size = 0;
+               pe = (PORT_ENTRY *) & CtReq->un.PortID;
+               memcpy((uint8_t *) & pe->PortName,
+                      (uint8_t *) & phba->fc_sparam.portName,
+                      sizeof (struct lpfc_name));
+               size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name);
+               break;
+
+       case SLI_MGMT_DPRT:
+               CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_MGMT_DPRT);
+               CtReq->CommandResponse.bits.Size = 0;
+               pe = (PORT_ENTRY *) & CtReq->un.PortID;
+               memcpy((uint8_t *) & pe->PortName,
+                      (uint8_t *) & phba->fc_sparam.portName,
+                      sizeof (struct lpfc_name));
+               size = GID_REQUEST_SZ - 4 + sizeof (struct lpfc_name);
+               break;
+       }
+
+       bpl = (struct ulp_bde64 *) bmp->virt;
+       bpl->addrHigh = le32_to_cpu( putPaddrHigh(mp->phys) );
+       bpl->addrLow = le32_to_cpu( putPaddrLow(mp->phys) );
+       bpl->tus.f.bdeFlags = 0;
+       bpl->tus.f.bdeSize = size;
+       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+       cmpl = lpfc_cmpl_ct_cmd_fdmi;
+
+       if (!lpfc_ct_cmd(phba, mp, bmp, ndlp, cmpl, FC_MAX_NS_RSP))
+               return 0;
+
+       lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+fdmi_cmd_free_bmp:
+       kfree(bmp);
+fdmi_cmd_free_mpvirt:
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+fdmi_cmd_free_mp:
+       kfree(mp);
+fdmi_cmd_exit:
+       /* Issue FDMI request failed */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_DISCOVERY,
+                       "%d:0244 Issue FDMI request failed Data: x%x\n",
+                       phba->brd_no,
+                       cmdcode);
+       return 1;
+}
+
+
+void
+lpfc_fdmi_tmo(unsigned long ptr)
+{
+       struct lpfc_hba *phba = (struct lpfc_hba*)ptr;
+       struct lpfc_nodelist *ndlp;
+       unsigned long iflag;
+
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+       ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID);
+       if (ndlp) {
+               if (system_utsname.nodename[0] != '\0') {
+                       lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA);
+               } else {
+                       mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60);
+               }
+       }
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+       return;
+}
+
+
+void
+lpfc_decode_firmware_rev(struct lpfc_hba * phba, char *fwrevision, int flag)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       lpfc_vpd_t *vp = &phba->vpd;
+       uint32_t b1, b2, b3, b4, i, rev;
+       char c;
+       uint32_t *ptr, str[4];
+       uint8_t *fwname;
+
+       if (vp->rev.rBit) {
+               if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)
+                       rev = vp->rev.sli2FwRev;
+               else
+                       rev = vp->rev.sli1FwRev;
+
+               b1 = (rev & 0x0000f000) >> 12;
+               b2 = (rev & 0x00000f00) >> 8;
+               b3 = (rev & 0x000000c0) >> 6;
+               b4 = (rev & 0x00000030) >> 4;
+
+               switch (b4) {
+               case 0:
+                       c = 'N';
+                       break;
+               case 1:
+                       c = 'A';
+                       break;
+               case 2:
+                       c = 'B';
+                       break;
+               default:
+                       c = 0;
+                       break;
+               }
+               b4 = (rev & 0x0000000f);
+
+               if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)
+                       fwname = vp->rev.sli2FwName;
+               else
+                       fwname = vp->rev.sli1FwName;
+
+               for (i = 0; i < 16; i++)
+                       if(fwname[i] == 0x20)
+                               fwname[i] = 0;
+
+               ptr = (uint32_t*)fwname;
+
+               for (i = 0; i < 3; i++)
+                       str[i] = be32_to_cpu(*ptr++);
+
+               if (c == 0) {
+                       if (flag)
+                               sprintf(fwrevision, "%d.%d%d (%s)",
+                                       b1, b2, b3, (char *)str);
+                       else
+                               sprintf(fwrevision, "%d.%d%d", b1,
+                                       b2, b3);
+               } else {
+                       if (flag)
+                               sprintf(fwrevision, "%d.%d%d%c%d (%s)",
+                                       b1, b2, b3, c,
+                                       b4, (char *)str);
+                       else
+                               sprintf(fwrevision, "%d.%d%d%c%d",
+                                       b1, b2, b3, c, b4);
+               }
+       } else {
+               rev = vp->rev.smFwRev;
+
+               b1 = (rev & 0xff000000) >> 24;
+               b2 = (rev & 0x00f00000) >> 20;
+               b3 = (rev & 0x000f0000) >> 16;
+               c  = (rev & 0x0000ff00) >> 8;
+               b4 = (rev & 0x000000ff);
+
+               if (flag)
+                       sprintf(fwrevision, "%d.%d%d%c%d ", b1,
+                               b2, b3, c, b4);
+               else
+                       sprintf(fwrevision, "%d.%d%d%c%d ", b1,
+                               b2, b3, c, b4);
+       }
+       return;
+}
+
+void
+lpfc_get_hba_model_desc(struct lpfc_hba * phba, uint8_t * mdp, uint8_t * descp)
+{
+       lpfc_vpd_t *vp;
+       uint32_t id;
+       char str[16];
+
+       vp = &phba->vpd;
+       pci_read_config_dword(phba->pcidev, PCI_VENDOR_ID, &id);
+
+       switch ((id >> 16) & 0xffff) {
+       case PCI_DEVICE_ID_SUPERFLY:
+               if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3)
+                       strcpy(str, "LP7000 1");
+               else
+                       strcpy(str, "LP7000E 1");
+               break;
+       case PCI_DEVICE_ID_DRAGONFLY:
+               strcpy(str, "LP8000 1");
+               break;
+       case PCI_DEVICE_ID_CENTAUR:
+               if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID)
+                       strcpy(str, "LP9002 2");
+               else
+                       strcpy(str, "LP9000 1");
+               break;
+       case PCI_DEVICE_ID_RFLY:
+               strcpy(str, "LP952 2");
+               break;
+       case PCI_DEVICE_ID_PEGASUS:
+               strcpy(str, "LP9802 2");
+               break;
+       case PCI_DEVICE_ID_THOR:
+               strcpy(str, "LP10000 2");
+               break;
+       case PCI_DEVICE_ID_VIPER:
+               strcpy(str, "LPX1000 10");
+               break;
+       case PCI_DEVICE_ID_PFLY:
+               strcpy(str, "LP982 2");
+               break;
+       case PCI_DEVICE_ID_TFLY:
+               strcpy(str, "LP1050 2");
+               break;
+       case PCI_DEVICE_ID_HELIOS:
+               strcpy(str, "LP11000 4");
+               break;
+       case PCI_DEVICE_ID_BMID:
+               strcpy(str, "LP1150 4");
+               break;
+       case PCI_DEVICE_ID_BSMB:
+               strcpy(str, "LP111 4");
+               break;
+       case PCI_DEVICE_ID_ZEPHYR:
+               strcpy(str, "LP11000e 4");
+               break;
+       case PCI_DEVICE_ID_ZMID:
+               strcpy(str, "LP1150e 4");
+               break;
+       case PCI_DEVICE_ID_ZSMB:
+               strcpy(str, "LP111e 4");
+               break;
+       case PCI_DEVICE_ID_LP101:
+               strcpy(str, "LP101 2");
+               break;
+       }
+       if (mdp)
+               sscanf(str, "%s", mdp);
+       if (descp)
+               sprintf(descp, "Emulex LightPulse %s Gigabit PCI Fibre "
+                       "Channel Adapter", str);
+}
+
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h
new file mode 100644 (file)
index 0000000..0b7ae7f
--- /dev/null
@@ -0,0 +1,270 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_disc.h 1.45 2004/11/10 11:40:40EST sf_support Exp  $
+ */
+
+#ifndef  _H_LPFC_DISC
+#define  _H_LPFC_DISC
+
+#include "lpfc_hw.h"
+
+struct lpfc_target;
+
+#define FC_MAX_HOLD_RSCN     32              /* max number of deferred RSCNs */
+#define FC_MAX_NS_RSP        65536    /* max size NameServer rsp */
+#define FC_MAXLOOP           126      /* max devices supported on a fc loop */
+#define LPFC_DISC_FLOGI_TMO  10              /* Discovery FLOGI ratov */
+
+/* Defines for failMask bitmask
+ * These are reasons that the device is not currently available
+ * for I/O to be sent.
+ */
+#define LPFC_DEV_LINK_DOWN       0x1   /* Link is down */
+#define LPFC_DEV_DISAPPEARED     0x2   /* Device disappeared from mapped
+                                          list */
+#define LPFC_DEV_DISCOVERY_INP   0x4   /* Device to go through discovery */
+#define LPFC_DEV_DISCONNECTED    0x8   /* noactive connection to remote dev */
+
+/* These defines are used for set failMask routines */
+#define LPFC_SET_BITMASK               1
+#define LPFC_CLR_BITMASK               2
+
+/* Provide an enumeration for the Types of addresses a FARP can resolve. */
+typedef enum lpfc_farp_addr_type {
+       LPFC_FARP_BY_IEEE,
+       LPFC_FARP_BY_WWPN,
+       LPFC_FARP_BY_WWNN,
+} LPFC_FARP_ADDR_TYPE;
+
+/* This is the protocol dependent definition for a Node List Entry.
+ * This is used by Fibre Channel protocol to support FCP.
+ */
+
+struct lpfc_bindlist {
+       struct list_head      nlp_listp;
+       struct lpfc_target    *nlp_Target;      /* ptr to the tgt structure */
+       struct lpfc_name      nlp_portname;     /* port name */
+       struct lpfc_name      nlp_nodename;     /* node name */
+       uint16_t              nlp_bind_type;
+       uint16_t              nlp_sid;          /* scsi id */
+       uint32_t              nlp_DID;          /* FibreChannel D_ID of entry */
+};
+
+struct lpfc_nodelist {
+       struct list_head nlp_listp;
+       struct lpfc_name nlp_portname;          /* port name */
+       struct lpfc_name nlp_nodename;          /* node name */
+       uint32_t         nlp_failMask;          /* failure mask for device */
+       uint32_t         nlp_flag;              /* entry  flags */
+       uint32_t         nlp_DID;               /* FC D_ID of entry */
+       uint32_t         nlp_last_elscmd;       /* Last ELS cmd sent */
+       uint16_t         nlp_type;
+#define NLP_FC_NODE        0x1                 /* entry is an FC node */
+#define NLP_FABRIC         0x4                 /* entry rep a Fabric entity */
+#define NLP_FCP_TARGET     0x8                 /* entry is an FCP target */
+
+       uint16_t        nlp_rpi;
+       uint16_t        nlp_state;              /* state transition indicator */
+       uint16_t        nlp_xri;                /* output exchange id for RPI */
+       uint16_t        nlp_sid;                /* scsi id */
+       uint8_t         nlp_retry;              /* used for ELS retries */
+       uint8_t         nlp_disc_refcnt;        /* used for DSM */
+       uint8_t         nlp_fcp_info;           /* class info, bits 0-3 */
+#define NLP_FCP_2_DEVICE   0x10                        /* FCP-2 device */
+
+       struct timer_list   nlp_delayfunc;      /* Used for delayed ELS cmds */
+       struct timer_list   nlp_tmofunc;        /* Used for nodev tmo */
+       struct lpfc_target *nlp_Target;         /* Pointer to the target
+                                                  structure */
+
+       struct lpfc_bindlist *nlp_listp_bind;   /* Linked list bounded remote
+                                                  ports */
+       struct lpfc_nodelist *nlp_rpi_hash_next;
+       struct lpfc_hba      *nlp_phba;
+};
+
+/*++
+ * lpfc_node_farp_list:
+ *   This data structure defines the attributes associated with
+ *   an outstanding FARP REQ to a remote node.
+ *
+ *   listentry - head of this list of pending farp requests.
+ *   rnode_addr - The address of the remote node.  Either the IEEE, WWPN, or
+ *                WWNN.  Used in the FARP request.
+ *
+ --*/
+struct lpfc_node_farp_pend {
+       struct list_head listentry;
+       struct lpfc_name rnode_addr;
+};
+
+/* Defines for nlp_flag (uint32) */
+#define NLP_NO_LIST        0x0         /* Indicates immediately free node */
+#define NLP_UNUSED_LIST    0x1         /* Flg to indicate node will be freed */
+#define NLP_PLOGI_LIST     0x2         /* Flg to indicate sent PLOGI */
+#define NLP_ADISC_LIST     0x3         /* Flg to indicate sent ADISC */
+#define NLP_REGLOGIN_LIST  0x4         /* Flg to indicate sent REG_LOGIN */
+#define NLP_PRLI_LIST      0x5         /* Flg to indicate sent PRLI */
+#define NLP_UNMAPPED_LIST  0x6         /* Node is now unmapped */
+#define NLP_MAPPED_LIST    0x7         /* Node is now mapped */
+#define NLP_NPR_LIST       0x8         /* Node is in NPort Recovery state */
+#define NLP_JUST_DQ        0x9         /* just deque ndlp in lpfc_nlp_list */
+#define NLP_LIST_MASK      0xf         /* mask to see what list node is on */
+#define NLP_PLOGI_SND      0x20                /* sent PLOGI request for this entry */
+#define NLP_PRLI_SND       0x40                /* sent PRLI request for this entry */
+#define NLP_ADISC_SND      0x80                /* sent ADISC request for this entry */
+#define NLP_LOGO_SND       0x100       /* sent LOGO request for this entry */
+#define NLP_RNID_SND       0x400       /* sent RNID request for this entry */
+#define NLP_ELS_SND_MASK   0x7e0       /* sent ELS request for this entry */
+#define NLP_AUTOMAP        0x800       /* Entry was automap'ed */
+#define NLP_SEED_WWPN      0x1000      /* Entry scsi id is seeded for WWPN */
+#define NLP_SEED_WWNN      0x2000      /* Entry scsi id is seeded for WWNN */
+#define NLP_SEED_DID       0x4000      /* Entry scsi id is seeded for DID */
+#define NLP_SEED_MASK      0x807000    /* mask for seeded flags */
+#define NLP_NS_NODE        0x8000      /* Authenticated entry by NameServer */
+#define NLP_NODEV_TMO      0x10000     /* nodev timeout is running for node */
+#define NLP_DELAY_TMO      0x20000     /* delay timeout is running for node */
+#define NLP_NPR_2B_DISC    0x40000     /* node is included in num_disc_nodes */
+#define NLP_RCV_PLOGI      0x80000     /* Rcv'ed PLOGI from remote system */
+#define NLP_LOGO_ACC       0x100000    /* Process LOGO after ACC completes */
+#define NLP_TGT_NO_SCSIID  0x200000    /* good PRLI but no binding for scsid */
+#define NLP_SEED_ALPA      0x800000    /* SCSI id is derived from alpa array */
+#define NLP_ACC_REGLOGIN   0x1000000   /* Issue Reg Login after successful
+                                          ACC */
+#define NLP_NPR_ADISC      0x2000000   /* Issue ADISC when dq'ed from
+                                          NPR list */
+#define NLP_DELAY_REMOVE   0x4000000   /* Defer removal till end of DSM */
+
+/* Defines for list searchs */
+#define NLP_SEARCH_MAPPED    0x1       /* search mapped */
+#define NLP_SEARCH_UNMAPPED  0x2       /* search unmapped */
+#define NLP_SEARCH_PLOGI     0x4       /* search plogi */
+#define NLP_SEARCH_ADISC     0x8       /* search adisc */
+#define NLP_SEARCH_REGLOGIN  0x10      /* search reglogin */
+#define NLP_SEARCH_PRLI      0x20      /* search prli */
+#define NLP_SEARCH_NPR       0x40      /* search npr */
+#define NLP_SEARCH_UNUSED    0x80      /* search mapped */
+#define NLP_SEARCH_ALL       0xff      /* search all lists */
+
+/* There are 4 different double linked lists nodelist entries can reside on.
+ * The Port Login (PLOGI) list and Address Discovery (ADISC) list are used
+ * when Link Up discovery or Registered State Change Notification (RSCN)
+ * processing is needed.  Each list holds the nodes that require a PLOGI or
+ * ADISC Extended Link Service (ELS) request.  These lists keep track of the
+ * nodes affected by an RSCN, or a Link Up (Typically, all nodes are effected
+ * by Link Up) event.  The unmapped_list contains all nodes that have
+ * successfully logged into at the Fibre Channel level.  The
+ * mapped_list will contain all nodes that are mapped FCP targets.
+ *
+ * The bind list is a list of undiscovered (potentially non-existent) nodes
+ * that we have saved binding information on. This information is used when
+ * nodes transition from the unmapped to the mapped list.
+ */
+
+/* Defines for nlp_state */
+#define NLP_STE_UNUSED_NODE       0x0  /* node is just allocated */
+#define NLP_STE_PLOGI_ISSUE       0x1  /* PLOGI was sent to NL_PORT */
+#define NLP_STE_ADISC_ISSUE       0x2  /* ADISC was sent to NL_PORT */
+#define NLP_STE_REG_LOGIN_ISSUE   0x3  /* REG_LOGIN was issued for NL_PORT */
+#define NLP_STE_PRLI_ISSUE        0x4  /* PRLI was sent to NL_PORT */
+#define NLP_STE_UNMAPPED_NODE     0x5  /* PRLI completed from NL_PORT */
+#define NLP_STE_MAPPED_NODE       0x6  /* Identified as a FCP Target */
+#define NLP_STE_NPR_NODE          0x7  /* NPort disappeared */
+#define NLP_STE_MAX_STATE         0x8
+#define NLP_STE_FREED_NODE        0xff /* node entry was freed to MEM_NLP */
+
+/* For UNUSED_NODE state, the node has just been allocated.
+ * For PLOGI_ISSUE and REG_LOGIN_ISSUE, the node is on
+ * the PLOGI list. For REG_LOGIN_COMPL, the node is taken off the PLOGI list
+ * and put on the unmapped list. For ADISC processing, the node is taken off
+ * the ADISC list and placed on either the mapped or unmapped list (depending
+ * on its previous state). Once on the unmapped list, a PRLI is issued and the
+ * state changed to PRLI_ISSUE. When the PRLI completion occurs, the state is
+ * changed to PRLI_COMPL. If the completion indicates a mapped
+ * node, the node is taken off the unmapped list. The binding list is checked
+ * for a valid binding, or a binding is automatically assigned. If binding
+ * assignment is unsuccessful, the node is left on the unmapped list. If
+ * binding assignment is successful, the associated binding list entry (if
+ * any) is removed, and the node is placed on the mapped list.
+ */
+/*
+ * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped
+ * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers
+ * expire, all effected nodes will receive a DEVICE_RM event.
+ */
+/*
+ * For a Link Up or RSCN, all nodes will move from the mapped / unmapped lists
+ * to either the ADISC or PLOGI list.  After a Nameserver query or ALPA loopmap
+ * check, additional nodes may be added (DEVICE_ADD) or removed (DEVICE_RM) to /
+ * from the PLOGI or ADISC lists. Once the PLOGI and ADISC lists are populated,
+ * we will first process the ADISC list.  32 entries are processed initially and
+ * ADISC is initited for each one.  Completions / Events for each node are
+ * funnelled thru the state machine.  As each node finishes ADISC processing, it
+ * starts ADISC for any nodes waiting for ADISC processing. If no nodes are
+ * waiting, and the ADISC list count is identically 0, then we are done. For
+ * Link Up discovery, since all nodes on the PLOGI list are UNREG_LOGIN'ed, we
+ * can issue a CLEAR_LA and reenable Link Events. Next we will process the PLOGI
+ * list.  32 entries are processed initially and PLOGI is initited for each one.
+ * Completions / Events for each node are funnelled thru the state machine.  As
+ * each node finishes PLOGI processing, it starts PLOGI for any nodes waiting
+ * for PLOGI processing. If no nodes are waiting, and the PLOGI list count is
+ * identically 0, then we are done. We have now completed discovery / RSCN
+ * handling. Upon completion, ALL nodes should be on either the mapped or
+ * unmapped lists.
+ */
+
+/* Defines for Node List Entry Events that could happen */
+#define NLP_EVT_RCV_PLOGI         0x0  /* Rcv'd an ELS PLOGI command */
+#define NLP_EVT_RCV_PRLI          0x1  /* Rcv'd an ELS PRLI  command */
+#define NLP_EVT_RCV_LOGO          0x2  /* Rcv'd an ELS LOGO  command */
+#define NLP_EVT_RCV_ADISC         0x3  /* Rcv'd an ELS ADISC command */
+#define NLP_EVT_RCV_PDISC         0x4  /* Rcv'd an ELS PDISC command */
+#define NLP_EVT_RCV_PRLO          0x5  /* Rcv'd an ELS PRLO  command */
+#define NLP_EVT_CMPL_PLOGI        0x6  /* Sent an ELS PLOGI command */
+#define NLP_EVT_CMPL_PRLI         0x7  /* Sent an ELS PRLI  command */
+#define NLP_EVT_CMPL_LOGO         0x8  /* Sent an ELS LOGO  command */
+#define NLP_EVT_CMPL_ADISC        0x9  /* Sent an ELS ADISC command */
+#define NLP_EVT_CMPL_REG_LOGIN    0xa  /* REG_LOGIN mbox cmd completed */
+#define NLP_EVT_DEVICE_RM         0xb  /* Device not found in NS / ALPAmap */
+#define NLP_EVT_DEVICE_RECOVERY   0xc  /* Device existence unknown */
+#define NLP_EVT_MAX_EVENT         0xd
+
+/* structure used to queue event to the discovery tasklet */
+struct lpfc_disc_evt {
+       struct list_head      evt_listp;
+       void                * evt_arg1;
+       void                * evt_arg2;
+       uint32_t              evt;
+};
+typedef struct lpfc_disc_evt LPFC_DISC_EVT_t;
+
+#define LPFC_EVT_MBOX          0x1
+#define LPFC_EVT_SOL_IOCB      0x2
+#define LPFC_EVT_UNSOL_IOCB    0x3
+#define LPFC_EVT_NODEV_TMO     0x4
+#define LPFC_EVT_SCAN          0x5
+
+/* Definitions for Binding Entry Type for lpfc_parse_binding_entry()  */
+#define LPFC_BIND_WW_NN_PN   0
+#define LPFC_BIND_DID        1
+
+#endif                         /* _H_LPFC_DISC */
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
new file mode 100644 (file)
index 0000000..427f68b
--- /dev/null
@@ -0,0 +1,3235 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_els.c 1.152 2004/11/18 18:27:53EST sf_support Exp  $
+ */
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+#include "lpfc_hw.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_mem.h"
+
+
+static int lpfc_els_retry(struct lpfc_hba *, struct lpfc_iocbq *,
+                         struct lpfc_iocbq *);
+static int lpfc_max_els_tries = 3;
+
+static int
+lpfc_els_chk_latt(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       LPFC_MBOXQ_t *mbox;
+       uint32_t ha_copy;
+
+       psli = &phba->sli;
+
+       if ((phba->hba_state < LPFC_HBA_READY) &&
+               (phba->hba_state != LPFC_LINK_DOWN)) {
+
+               /* Read the HBA Host Attention Register */
+               ha_copy = readl(phba->HAregaddr);
+
+               if (ha_copy & HA_LATT) {        /* Link Attention interrupt */
+
+                       /* Pending Link Event during Discovery */
+                       lpfc_printf_log(phba, KERN_WARNING, LOG_DISCOVERY,
+                                       "%d:0237 Pending Link Event during "
+                                       "Discovery: State x%x\n",
+                                       phba->brd_no, phba->hba_state);
+
+                       /* CLEAR_LA should re-enable link attention events and
+                        * we should then imediately take a LATT event. The 
+                        * LATT processing should call lpfc_linkdown() which
+                        * will cleanup any left over in-progress discovery
+                        * events.
+                        */
+                       phba->fc_flag |= FC_ABORT_DISCOVERY;
+
+                       if (phba->hba_state != LPFC_CLEAR_LA) {
+                               if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+                                                         GFP_ATOMIC))) {
+                                       phba->hba_state = LPFC_CLEAR_LA;
+                                       lpfc_clear_la(phba, mbox);
+                                       mbox->mbox_cmpl =
+                                           lpfc_mbx_cmpl_clear_la;
+                                       if (lpfc_sli_issue_mbox
+                                           (phba, mbox,
+                                            (MBX_NOWAIT | MBX_STOP_IOCB))
+                                           == MBX_NOT_FINISHED) {
+                                               mempool_free(mbox,
+                                                    phba->mbox_mem_pool);
+                                               phba->hba_state =
+                                                   LPFC_HBA_ERROR;
+                                       }
+                               }
+                       }
+                       return (1);
+               }
+       }
+
+       return (0);
+}
+
+struct lpfc_iocbq *
+lpfc_prep_els_iocb(struct lpfc_hba * phba,
+                  uint8_t expectRsp,
+                  uint16_t cmdSize,
+                  uint8_t retry, struct lpfc_nodelist * ndlp, uint32_t elscmd)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_dmabuf *pcmd, *prsp, *pbuflist;
+       struct ulp_bde64 *bpl;
+       IOCB_t *icmd;
+       uint32_t tag;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+
+       if (phba->hba_state < LPFC_LINK_UP)
+               return  NULL;
+
+
+       /* Allocate buffer for  command iocb */
+       elsiocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC);
+       if (!elsiocb)
+               return NULL;
+
+       memset(elsiocb, 0, sizeof (struct lpfc_iocbq));
+       icmd = &elsiocb->iocb;
+
+       /* fill in BDEs for command */
+       /* Allocate buffer for command payload */
+       if (((pcmd = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) ||
+           ((pcmd->virt = lpfc_mbuf_alloc(phba,
+                                          MEM_PRI, &(pcmd->phys))) == 0)) {
+               if (pcmd)
+                       kfree(pcmd);
+               mempool_free( elsiocb, phba->iocb_mem_pool);
+               return NULL;
+       }
+
+       INIT_LIST_HEAD(&pcmd->list);
+
+       /* Allocate buffer for response payload */
+       if (expectRsp) {
+               prsp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC);
+               if (prsp)
+                       prsp->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
+                                                    &prsp->phys);
+               if (prsp == 0 || prsp->virt == 0) {
+                       if (prsp)
+                               kfree(prsp);
+                       lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);
+                       kfree(pcmd);
+                       mempool_free( elsiocb, phba->iocb_mem_pool);
+                       return NULL;
+               }
+               INIT_LIST_HEAD(&prsp->list);
+       } else {
+               prsp = NULL;
+       }
+
+       /* Allocate buffer for Buffer ptr list */
+       pbuflist = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC);
+       if (pbuflist)
+           pbuflist->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
+                                            &pbuflist->phys);
+       if (pbuflist == 0 || pbuflist->virt == 0) {
+               mempool_free( elsiocb, phba->iocb_mem_pool);
+               lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);
+               lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
+               kfree(pcmd);
+               kfree(prsp);
+               if (pbuflist)
+                       kfree(pbuflist);
+               return NULL;
+       }
+
+       INIT_LIST_HEAD(&pbuflist->list);
+
+       icmd->un.elsreq64.bdl.addrHigh = putPaddrHigh(pbuflist->phys);
+       icmd->un.elsreq64.bdl.addrLow = putPaddrLow(pbuflist->phys);
+       icmd->un.elsreq64.bdl.bdeFlags = BUFF_TYPE_BDL;
+       if (expectRsp) {
+               icmd->un.elsreq64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64));
+               icmd->un.elsreq64.remoteID = ndlp->nlp_DID;     /* DID */
+               icmd->ulpCommand = CMD_ELS_REQUEST64_CR;
+       } else {
+               icmd->un.elsreq64.bdl.bdeSize = sizeof (struct ulp_bde64);
+               icmd->ulpCommand = CMD_XMIT_ELS_RSP64_CX;
+       }
+
+       /* NOTE: we don't use ulpIoTag0 because it is a t2 structure */
+       tag = lpfc_sli_next_iotag(phba, pring);
+       icmd->ulpIoTag = (uint16_t)(tag & 0xffff);
+       icmd->un.elsreq64.bdl.ulpIoTag32 = tag;
+       icmd->ulpBdeCount = 1;
+       icmd->ulpLe = 1;
+       icmd->ulpClass = CLASS3;
+
+       bpl = (struct ulp_bde64 *) pbuflist->virt;
+       bpl->addrLow = le32_to_cpu(putPaddrLow(pcmd->phys));
+       bpl->addrHigh = le32_to_cpu(putPaddrHigh(pcmd->phys));
+       bpl->tus.f.bdeSize = cmdSize;
+       bpl->tus.f.bdeFlags = 0;
+       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+       if (expectRsp) {
+               bpl++;
+               bpl->addrLow = le32_to_cpu(putPaddrLow(prsp->phys));
+               bpl->addrHigh = le32_to_cpu(putPaddrHigh(prsp->phys));
+               bpl->tus.f.bdeSize = FCELSSIZE;
+               bpl->tus.f.bdeFlags = BUFF_USE_RCV;
+               bpl->tus.w = le32_to_cpu(bpl->tus.w);
+       }
+
+       /* Save for completion so we can release these resources */
+       elsiocb->context1 = (uint8_t *) ndlp;
+       elsiocb->context2 = (uint8_t *) pcmd;
+       elsiocb->context3 = (uint8_t *) pbuflist;
+       elsiocb->retry = retry;
+       elsiocb->drvrTimeout = (phba->fc_ratov << 1) + LPFC_DRVR_TIMEOUT;
+
+       if (prsp) {
+               list_add(&prsp->list, &pcmd->list);
+       }
+
+       /* The els iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to
+        * the physical address.
+        */
+       pci_dma_sync_single_for_device(phba->pcidev, pbuflist->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       if (expectRsp) {
+               /* Xmit ELS command <elsCmd> to remote NPORT <did> */
+               lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                               "%d:0116 Xmit ELS command x%x to remote "
+                               "NPORT x%x Data: x%x x%x\n",
+                               phba->brd_no, elscmd,
+                               ndlp->nlp_DID, icmd->ulpIoTag, phba->hba_state);
+       } else {
+               /* Xmit ELS response <elsCmd> to remote NPORT <did> */
+               lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                               "%d:0117 Xmit ELS response x%x to remote "
+                               "NPORT x%x Data: x%x x%x\n",
+                               phba->brd_no, elscmd,
+                               ndlp->nlp_DID, icmd->ulpIoTag, cmdSize);
+       }
+
+       return (elsiocb);
+}
+
+static void
+lpfc_cmpl_els_flogi(struct lpfc_hba * phba,
+                   struct lpfc_iocbq * cmdiocb, struct lpfc_iocbq * rspiocb)
+{
+       IOCB_t *irsp;
+       struct lpfc_dmabuf *pcmd, *prsp;
+       struct serv_parm *sp;
+       uint32_t *lp;
+       LPFC_MBOXQ_t *mbox;
+       struct lpfc_sli *psli;
+       struct lpfc_nodelist *ndlp;
+       int rc;
+
+       psli = &phba->sli;
+       irsp = &(rspiocb->iocb);
+       ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       /* Check to see if link went down during discovery */
+       if (lpfc_els_chk_latt(phba)) {
+               lpfc_nlp_remove(phba, ndlp);
+               goto out;
+       }
+
+       if (irsp->ulpStatus) {
+               /* Check for retry */
+               if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+                       /* ELS command is being retried */
+                       goto out;
+               }
+               /* FLOGI failed, so there is no fabric */
+               phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+
+               /* If private loop, then allow max outstandting els to be
+                * LPFC_MAX_DISC_THREADS (32). Scanning in the case of no
+                * alpa map would take too long otherwise.
+                */
+               if (phba->alpa_map[0] == 0) {
+                       phba->cfg_discovery_threads =
+                           LPFC_MAX_DISC_THREADS;
+               }
+
+               /* FLOGI failure */
+               lpfc_printf_log(phba,
+                               KERN_INFO,
+                               LOG_ELS,
+                               "%d:0100 FLOGI failure Data: x%x x%x\n",
+                               phba->brd_no,
+                               irsp->ulpStatus, irsp->un.ulpWord[4]);
+       } else {
+               /* The FLogI succeeded.  Sync the data for the CPU before
+                * accessing it.
+                */
+               prsp = (struct lpfc_dmabuf *) pcmd->list.next;
+               lp = (uint32_t *) prsp->virt;
+
+               /* The HBA populated the response buffer.  Flush cpu cache to
+                * before the driver touches this memory.
+                */
+               pci_dma_sync_single_for_cpu(phba->pcidev, prsp->phys,
+                       LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+               sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));
+
+               /* FLOGI completes successfully */
+               lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                               "%d:0101 FLOGI completes sucessfully "
+                               "Data: x%x x%x x%x x%x\n",
+                               phba->brd_no,
+                               irsp->un.ulpWord[4], sp->cmn.e_d_tov,
+                               sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution);
+
+               if (phba->hba_state == LPFC_FLOGI) {
+                       /* If Common Service Parameters indicate Nport
+                        * we are point to point, if Fport we are Fabric.
+                        */
+                       if (sp->cmn.fPort) {
+                               phba->fc_flag |= FC_FABRIC;
+                               if (sp->cmn.edtovResolution) {
+                                       /* E_D_TOV ticks are in nanoseconds */
+                                       phba->fc_edtov =
+                                           (be32_to_cpu(sp->cmn.e_d_tov) +
+                                            999999) / 1000000;
+                               } else {
+                                       /* E_D_TOV ticks are in milliseconds */
+                                       phba->fc_edtov =
+                                           be32_to_cpu(sp->cmn.e_d_tov);
+                               }
+                               phba->fc_ratov =
+                                   (be32_to_cpu(sp->cmn.w2.r_a_tov) +
+                                    999) / 1000;
+
+                               if (phba->fc_topology == TOPOLOGY_LOOP) {
+                                       phba->fc_flag |= FC_PUBLIC_LOOP;
+                               } else {
+                                       /* If we are a N-port connected to a
+                                        * Fabric, fixup sparam's so logins to
+                                        * devices on remote loops work.
+                                        */
+                                       phba->fc_sparam.cmn.altBbCredit = 1;
+                               }
+
+                               phba->fc_myDID = irsp->un.ulpWord[4] & Mask_DID;
+
+                               memcpy(&ndlp->nlp_portname, &sp->portName,
+                                      sizeof (struct lpfc_name));
+                               memcpy(&ndlp->nlp_nodename, &sp->nodeName,
+                                      sizeof (struct lpfc_name));
+                               memcpy(&phba->fc_fabparam, sp,
+                                      sizeof (struct serv_parm));
+                               if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+                                                         GFP_ATOMIC)) == 0) {
+                                       goto flogifail;
+                               }
+                               phba->hba_state = LPFC_FABRIC_CFG_LINK;
+                               lpfc_config_link(phba, mbox);
+                               if (lpfc_sli_issue_mbox
+                                   (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB))
+                                   == MBX_NOT_FINISHED) {
+                                       mempool_free(mbox, phba->mbox_mem_pool);
+                                       goto flogifail;
+                               }
+
+                               if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+                                                         GFP_ATOMIC)) == 0) {
+                                       goto flogifail;
+                               }
+                               if (lpfc_reg_login(phba, Fabric_DID,
+                                                  (uint8_t *) sp, mbox,
+                                                  0) == 0) {
+                                       /* set_slim mailbox command needs to
+                                        * execute first, queue this command to
+                                        * be processed later.
+                                        */
+                                       mbox->mbox_cmpl =
+                                           lpfc_mbx_cmpl_fabric_reg_login;
+                                       mbox->context2 = ndlp;
+                                       if (lpfc_sli_issue_mbox
+                                           (phba, mbox,
+                                            (MBX_NOWAIT | MBX_STOP_IOCB))
+                                           == MBX_NOT_FINISHED) {
+                                               mempool_free(mbox,
+                                                    phba->mbox_mem_pool);
+                                               goto flogifail;
+                                       }
+                               } else {
+                                       mempool_free(mbox, phba->mbox_mem_pool);
+                                       goto flogifail;
+                               }
+                       } else {
+                               /* We FLOGIed into an NPort, initiate pt2pt
+                                  protocol */
+                               phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+                               phba->fc_edtov = FF_DEF_EDTOV;
+                               phba->fc_ratov = FF_DEF_RATOV;
+                               rc = memcmp(&phba->fc_portname, &sp->portName,
+                                           sizeof(struct lpfc_name));
+                               if (rc >= 0) {
+                                       /* This side will initiate the PLOGI */
+                                       phba->fc_flag |= FC_PT2PT_PLOGI;
+
+                                       /* N_Port ID cannot be 0, set our to
+                                        * LocalID the other side will be
+                                        * RemoteID.
+                                        */
+
+                                       /* not equal */
+                                       if (rc)
+                                               phba->fc_myDID = PT2PT_LocalID;
+
+                                       if ((mbox =
+                                            mempool_alloc(phba->mbox_mem_pool,
+                                                          GFP_ATOMIC))
+                                           == 0) {
+                                               goto flogifail;
+                                       }
+                                       lpfc_config_link(phba, mbox);
+                                       if (lpfc_sli_issue_mbox
+                                           (phba, mbox,
+                                            (MBX_NOWAIT | MBX_STOP_IOCB))
+                                           == MBX_NOT_FINISHED) {
+                                               mempool_free(mbox,
+                                                    phba->mbox_mem_pool);
+                                               goto flogifail;
+                                       }
+                                       mempool_free( ndlp, phba->nlp_mem_pool);
+
+                                       if ((ndlp =
+                                            lpfc_findnode_did(phba,
+                                                              NLP_SEARCH_ALL,
+                                                              PT2PT_RemoteID))
+                                           == 0) {
+                                               /* Cannot find existing Fabric
+                                                  ndlp, so allocate a new
+                                                  one */
+                                               if ((ndlp =
+                                                    mempool_alloc(
+                                                          phba->nlp_mem_pool,
+                                                          GFP_ATOMIC)) == 0) {
+                                                       goto flogifail;
+                                               }
+                                               lpfc_nlp_init(phba, ndlp,
+                                                       PT2PT_RemoteID);
+                                       }
+                                       memcpy(&ndlp->nlp_portname,
+                                              &sp->portName,
+                                              sizeof (struct lpfc_name));
+                                       memcpy(&ndlp->nlp_nodename,
+                                              &sp->nodeName,
+                                              sizeof (struct lpfc_name));
+                                       ndlp->nlp_state = NLP_STE_NPR_NODE;
+                                       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+                                       ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+                               }
+                               else {
+                                       /* This side will wait for the PLOGI */
+                                       mempool_free( ndlp, phba->nlp_mem_pool);
+                               }
+
+                               phba->fc_flag |= FC_PT2PT;
+
+                               /* Start discovery - this should just do
+                                  CLEAR_LA */
+                               lpfc_disc_start(phba);
+                       }
+                       goto out;
+               }
+       }
+
+flogifail:
+       lpfc_nlp_remove(phba, ndlp);
+
+       if((irsp->ulpStatus != IOSTAT_LOCAL_REJECT) ||
+          ((irsp->un.ulpWord[4] != IOERR_SLI_ABORTED) &&
+          (irsp->un.ulpWord[4] != IOERR_SLI_DOWN))) {
+
+               /* FLOGI failed, so just use loop map to make discovery list */
+               lpfc_disc_list_loopmap(phba);
+
+               /* Start discovery */
+               lpfc_disc_start(phba);
+       }
+
+out:
+       lpfc_els_free_iocb(phba, cmdiocb);
+       return;
+}
+
+static int
+lpfc_issue_els_flogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+                    uint8_t retry)
+{
+       struct serv_parm *sp;
+       IOCB_t *icmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+       uint32_t tmo;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+
+       cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm));
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+                                         ndlp, ELS_CMD_FLOGI)) == 0) {
+               return (1);
+       }
+
+       icmd = &elsiocb->iocb;
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       /* For FLOGI request, remainder of payload is service parameters */
+       *((uint32_t *) (pcmd)) = ELS_CMD_FLOGI;
+       pcmd += sizeof (uint32_t);
+       memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm));
+       sp = (struct serv_parm *) pcmd;
+
+       /* Setup CSPs accordingly for Fabric */
+       sp->cmn.e_d_tov = 0;
+       sp->cmn.w2.r_a_tov = 0;
+       sp->cls1.classValid = 0;
+       sp->cls2.seqDelivery = 1;
+       sp->cls3.seqDelivery = 1;
+       if (sp->cmn.fcphLow < FC_PH3)
+               sp->cmn.fcphLow = FC_PH3;
+       if (sp->cmn.fcphHigh < FC_PH3)
+               sp->cmn.fcphHigh = FC_PH3;
+
+       tmo = phba->fc_ratov;
+       phba->fc_ratov = LPFC_DISC_FLOGI_TMO;
+       lpfc_set_disctmo(phba);
+       phba->fc_ratov = tmo;
+
+       /* Flush the els buffer to main store for the HBA.  This context always
+        * comes from the driver's dma pool and is always LPFC_BPL_SIZE.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       phba->fc_stat.elsXmitFLOGI++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_flogi;
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       return (0);
+}
+
+int
+lpfc_els_abort_flogi(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       struct lpfc_nodelist *ndlp;
+       IOCB_t *icmd;
+       struct list_head *curr, *next;
+
+       /* Abort outstanding I/O on NPort <nlp_DID> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                       "%d:0201 Abort outstanding I/O on NPort x%x\n",
+                       phba->brd_no, Fabric_DID);
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];
+
+       /* check the txcmplq */
+       list_for_each_safe(curr, next, &pring->txcmplq) {
+               next_iocb = list_entry(curr, struct lpfc_iocbq, list);
+               iocb = next_iocb;
+               /* Check to see if iocb matches the nport we are
+                  looking for */
+               icmd = &iocb->iocb;
+               if (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) {
+                       ndlp = (struct lpfc_nodelist *)(iocb->context1);
+                       if(ndlp && (ndlp->nlp_DID == Fabric_DID)) {
+                               /* It matches, so deque and call compl
+                                  with an error */
+                               list_del(&iocb->list);
+                               pring->txcmplq_cnt--;
+
+                               if ((icmd->un.elsreq64.bdl.ulpIoTag32)) {
+                                       lpfc_sli_issue_abort_iotag32
+                                           (phba, pring, iocb);
+                               }
+                               if (iocb->iocb_cmpl) {
+                                       icmd->ulpStatus =
+                                           IOSTAT_LOCAL_REJECT;
+                                       icmd->un.ulpWord[4] =
+                                           IOERR_SLI_ABORTED;
+                                       (iocb->iocb_cmpl) (phba, iocb, iocb);
+                               } else {
+                                       mempool_free(iocb, phba->iocb_mem_pool);
+                               }
+                       }
+               }
+       }
+       return (0);
+}
+
+int
+lpfc_initial_flogi(struct lpfc_hba * phba)
+{
+       struct lpfc_nodelist *ndlp;
+
+       /* First look for Fabric ndlp on the unmapped list */
+
+       if ((ndlp =
+            lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED,
+                              Fabric_DID)) == 0) {
+               /* Cannot find existing Fabric ndlp, so allocate a new one */
+               if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC))
+                   == 0) {
+                       return (0);
+               }
+               lpfc_nlp_init(phba, ndlp, Fabric_DID);
+       }
+       else {
+               phba->fc_unmap_cnt--;
+               list_del(&ndlp->nlp_listp);
+               ndlp->nlp_flag &= ~NLP_LIST_MASK;
+       }
+       if (lpfc_issue_els_flogi(phba, ndlp, 0)) {
+               mempool_free( ndlp, phba->nlp_mem_pool);
+       }
+       return (1);
+}
+
+static void
+lpfc_more_plogi(struct lpfc_hba * phba)
+{
+       int sentplogi;
+
+       if (phba->num_disc_nodes)
+               phba->num_disc_nodes--;
+
+       /* Continue discovery with <num_disc_nodes> PLOGIs to go */
+       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                       "%d:0232 Continue discovery with %d PLOGIs to go "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, phba->num_disc_nodes, phba->fc_plogi_cnt,
+                       phba->fc_flag, phba->hba_state);
+
+       /* Check to see if there are more PLOGIs to be sent */
+       if (phba->fc_flag & FC_NLP_MORE) {
+               /* go thru NPR list and issue any remaining ELS PLOGIs */
+               sentplogi = lpfc_els_disc_plogi(phba);
+       }
+       return;
+}
+
+static void
+lpfc_cmpl_els_plogi(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                   struct lpfc_iocbq * rspiocb)
+{
+       IOCB_t *irsp;
+       struct lpfc_sli *psli;
+       struct lpfc_nodelist *ndlp;
+       int disc, rc, did, type;
+
+       psli = &phba->sli;
+
+       /* we pass cmdiocb to state machine which needs rspiocb as well */
+       cmdiocb->context_un.rsp_iocb = rspiocb;
+
+       irsp = &rspiocb->iocb;
+       ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+       ndlp->nlp_flag &= ~NLP_PLOGI_SND;
+
+       /* Since ndlp can be freed in the disc state machine, note if this node
+        * is being used during discovery.
+        */
+       disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC);
+       rc   = 0;
+
+       /* PLOGI completes to NPort <nlp_DID> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0102 PLOGI completes to NPort x%x "
+                       "Data: x%x x%x x%x x%x\n",
+                       phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus,
+                       irsp->un.ulpWord[4], disc, phba->num_disc_nodes);
+
+       /* Check to see if link went down during discovery */
+       if (lpfc_els_chk_latt(phba)) {
+               ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+               goto out;
+       }
+
+       /* ndlp could be freed in DSM, save these values now */
+       type = ndlp->nlp_type;
+       did = ndlp->nlp_DID;
+
+       if (irsp->ulpStatus) {
+               /* Check for retry */
+               if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+                       /* ELS command is being retried */
+                       if (disc) {
+                               ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+                       }
+                       goto out;
+               }
+
+               /* PLOGI failed */
+               /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
+               if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+                  ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) ||
+                  (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) {
+                       disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC);
+               }
+               else {
+                       rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+                                       NLP_EVT_CMPL_PLOGI);
+               }
+       } else {
+               /* Good status, call state machine */
+               rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+                                       NLP_EVT_CMPL_PLOGI);
+       }
+
+       if(type & NLP_FABRIC) {
+               /* If we cannot login to Nameserver, kick off discovery now */
+               if ((did == NameServer_DID) && (rc == NLP_STE_FREED_NODE)) {
+                       lpfc_disc_start(phba);
+               }
+               goto out;
+       }
+
+       if (disc && phba->num_disc_nodes) {
+               /* Check to see if there are more PLOGIs to be sent */
+               lpfc_more_plogi(phba);
+       }
+
+       if (rc != NLP_STE_FREED_NODE)
+               ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+
+       if (phba->num_disc_nodes == 0) {
+               lpfc_can_disctmo(phba);
+               if (phba->fc_flag & FC_RSCN_MODE) {
+                       /* Check to see if more RSCNs came in while we were
+                        * processing this one.
+                        */
+                       if ((phba->fc_rscn_id_cnt == 0) &&
+                           (!(phba->fc_flag & FC_RSCN_DISCOVERY))) {
+                               lpfc_els_flush_rscn(phba);
+                       } else {
+                               lpfc_els_handle_rscn(phba);
+                       }
+               }
+       }
+
+out:
+       lpfc_els_free_iocb(phba, cmdiocb);
+       return;
+}
+
+int
+lpfc_issue_els_plogi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+                    uint8_t retry)
+{
+       struct serv_parm *sp;
+       IOCB_t *icmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+
+       cmdsize = (sizeof (uint32_t) + sizeof (struct serv_parm));
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+                                         ndlp, ELS_CMD_PLOGI)) == 0) {
+               return (1);
+       }
+
+       icmd = &elsiocb->iocb;
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       /* For PLOGI request, remainder of payload is service parameters */
+       *((uint32_t *) (pcmd)) = ELS_CMD_PLOGI;
+       pcmd += sizeof (uint32_t);
+       memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm));
+       sp = (struct serv_parm *) pcmd;
+
+       if (sp->cmn.fcphLow < FC_PH_4_3)
+               sp->cmn.fcphLow = FC_PH_4_3;
+
+       if (sp->cmn.fcphHigh < FC_PH3)
+               sp->cmn.fcphHigh = FC_PH3;
+
+       /* The lpfc iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       phba->fc_stat.elsXmitPLOGI++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_plogi;
+       ndlp->nlp_flag |= NLP_PLOGI_SND;
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               ndlp->nlp_flag &= ~NLP_PLOGI_SND;
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       return (0);
+}
+
+static void
+lpfc_cmpl_els_prli(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                  struct lpfc_iocbq * rspiocb)
+{
+       IOCB_t *irsp;
+       struct lpfc_sli *psli;
+       struct lpfc_nodelist *ndlp;
+
+       psli = &phba->sli;
+       /* we pass cmdiocb to state machine which needs rspiocb as well */
+       cmdiocb->context_un.rsp_iocb = rspiocb;
+
+       irsp = &(rspiocb->iocb);
+       ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+       ndlp->nlp_flag &= ~NLP_PRLI_SND;
+
+       /* PRLI completes to NPort <nlp_DID> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0103 PRLI completes to NPort x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus,
+                       irsp->un.ulpWord[4], phba->num_disc_nodes);
+
+       phba->fc_prli_sent--;
+       /* Check to see if link went down during discovery */
+       if (lpfc_els_chk_latt(phba))
+               goto out;
+
+       if (irsp->ulpStatus) {
+               /* Check for retry */
+               if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+                       /* ELS command is being retried */
+                       goto out;
+               }
+               /* PRLI failed */
+               /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
+               if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+                  ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) ||
+                  (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) {
+                       goto out;
+               }
+               else {
+                       lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+                                       NLP_EVT_CMPL_PRLI);
+               }
+       } else {
+               /* Good status, call state machine */
+               lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_PRLI);
+       }
+
+out:
+       lpfc_els_free_iocb(phba, cmdiocb);
+       return;
+}
+
+int
+lpfc_issue_els_prli(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+                   uint8_t retry)
+{
+       PRLI *npr;
+       IOCB_t *icmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+
+       cmdsize = (sizeof (uint32_t) + sizeof (PRLI));
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+                                         ndlp, ELS_CMD_PRLI)) == 0) {
+               return (1);
+       }
+
+       icmd = &elsiocb->iocb;
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       /* For PRLI request, remainder of payload is service parameters */
+       memset(pcmd, 0, (sizeof (PRLI) + sizeof (uint32_t)));
+       *((uint32_t *) (pcmd)) = ELS_CMD_PRLI;
+       pcmd += sizeof (uint32_t);
+
+       /* For PRLI, remainder of payload is PRLI parameter page */
+       npr = (PRLI *) pcmd;
+       /*
+        * If our firmware version is 3.20 or later,
+        * set the following bits for FC-TAPE support.
+        */
+       if (phba->vpd.rev.feaLevelHigh >= 0x02) {
+               npr->ConfmComplAllowed = 1;
+               npr->Retry = 1;
+               npr->TaskRetryIdReq = 1;
+       }
+       npr->estabImagePair = 1;
+       npr->readXferRdyDis = 1;
+
+       /* For FCP support */
+       npr->prliType = PRLI_FCP_TYPE;
+       npr->initiatorFunc = 1;
+
+       /* The lpfc iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       phba->fc_stat.elsXmitPRLI++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_prli;
+       ndlp->nlp_flag |= NLP_PRLI_SND;
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               ndlp->nlp_flag &= ~NLP_PRLI_SND;
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       phba->fc_prli_sent++;
+       return (0);
+}
+
+static void
+lpfc_more_adisc(struct lpfc_hba * phba)
+{
+       int sentadisc;
+
+       if (phba->num_disc_nodes)
+               phba->num_disc_nodes--;
+
+       /* Continue discovery with <num_disc_nodes> ADISCs to go */
+       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                       "%d:0210 Continue discovery with %d ADISCs to go "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, phba->num_disc_nodes, phba->fc_adisc_cnt,
+                       phba->fc_flag, phba->hba_state);
+
+       /* Check to see if there are more ADISCs to be sent */
+       if (phba->fc_flag & FC_NLP_MORE) {
+               lpfc_set_disctmo(phba);
+
+               /* go thru NPR list and issue any remaining ELS ADISCs */
+               sentadisc = lpfc_els_disc_adisc(phba);
+       }
+       return;
+}
+
+static void
+lpfc_rscn_disc(struct lpfc_hba * phba)
+{
+       /* RSCN discovery */
+       /* go thru NPR list and issue ELS PLOGIs */
+       if (phba->fc_npr_cnt) {
+               lpfc_els_disc_plogi(phba);
+               return;
+       }
+       if (phba->fc_flag & FC_RSCN_MODE) {
+               /* Check to see if more RSCNs came in while we were
+                * processing this one.
+                */
+               if ((phba->fc_rscn_id_cnt == 0) &&
+                   (!(phba->fc_flag & FC_RSCN_DISCOVERY))) {
+                       lpfc_els_flush_rscn(phba);
+               } else {
+                       lpfc_els_handle_rscn(phba);
+               }
+       }
+}
+
+static void
+lpfc_cmpl_els_adisc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                   struct lpfc_iocbq * rspiocb)
+{
+       IOCB_t *irsp;
+       struct lpfc_sli *psli;
+       struct lpfc_nodelist *ndlp;
+       LPFC_MBOXQ_t *mbox;
+       int disc;
+
+       psli = &phba->sli;
+
+       /* we pass cmdiocb to state machine which needs rspiocb as well */
+       cmdiocb->context_un.rsp_iocb = rspiocb;
+
+       irsp = &(rspiocb->iocb);
+       ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+       ndlp->nlp_flag &= ~NLP_ADISC_SND;
+
+       /* Since ndlp can be freed in the disc state machine, note if this node
+        * is being used during discovery.
+        */
+       disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC);
+
+       /* ADISC completes to NPort <nlp_DID> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0104 ADISC completes to NPort x%x "
+                       "Data: x%x x%x x%x x%x\n",
+                       phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus,
+                       irsp->un.ulpWord[4], disc, phba->num_disc_nodes);
+
+       /* Check to see if link went down during discovery */
+       if (lpfc_els_chk_latt(phba)) {
+               ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+               goto out;
+       }
+
+       if (irsp->ulpStatus) {
+               /* Check for retry */
+               if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+                       /* ELS command is being retried */
+                       if (disc) {
+                               ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+                               lpfc_set_disctmo(phba);
+                       }
+                       goto out;
+               }
+               /* ADISC failed */
+               /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
+               if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+                  ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) ||
+                  (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) {
+                       disc = (ndlp->nlp_flag & NLP_NPR_2B_DISC);
+               }
+               else {
+                       lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+                                       NLP_EVT_CMPL_ADISC);
+               }
+       } else {
+               /* Good status, call state machine */
+               lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+                                       NLP_EVT_CMPL_ADISC);
+       }
+
+       if (disc && phba->num_disc_nodes) {
+               /* Check to see if there are more ADISCs to be sent */
+               lpfc_more_adisc(phba);
+
+               /* Check to see if we are done with ADISC authentication */
+               if (phba->num_disc_nodes == 0) {
+                       /* If we get here, there is nothing left to wait for */
+                       if ((phba->hba_state < LPFC_HBA_READY) &&
+                           (phba->hba_state != LPFC_CLEAR_LA)) {
+                               /* Link up discovery */
+                               if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+                                                         GFP_ATOMIC))) {
+                                       phba->hba_state = LPFC_CLEAR_LA;
+                                       lpfc_clear_la(phba, mbox);
+                                       mbox->mbox_cmpl =
+                                           lpfc_mbx_cmpl_clear_la;
+                                       if (lpfc_sli_issue_mbox
+                                           (phba, mbox,
+                                            (MBX_NOWAIT | MBX_STOP_IOCB))
+                                           == MBX_NOT_FINISHED) {
+                                               mempool_free(mbox,
+                                                    phba->mbox_mem_pool);
+                                               lpfc_disc_flush_list(phba);
+                                               psli->ring[(psli->ip_ring)].
+                                                   flag &=
+                                                   ~LPFC_STOP_IOCB_EVENT;
+                                               psli->ring[(psli->fcp_ring)].
+                                                   flag &=
+                                                   ~LPFC_STOP_IOCB_EVENT;
+                                               psli->ring[(psli->next_ring)].
+                                                   flag &=
+                                                   ~LPFC_STOP_IOCB_EVENT;
+                                               phba->hba_state =
+                                                   LPFC_HBA_READY;
+                                       }
+                               }
+                       } else {
+                               lpfc_rscn_disc(phba);
+                       }
+               }
+       }
+       ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+out:
+       lpfc_els_free_iocb(phba, cmdiocb);
+       return;
+}
+
+int
+lpfc_issue_els_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+                    uint8_t retry)
+{
+       ADISC *ap;
+       IOCB_t *icmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+
+       cmdsize = (sizeof (uint32_t) + sizeof (ADISC));
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+                                         ndlp, ELS_CMD_ADISC)) == 0) {
+               return (1);
+       }
+
+       icmd = &elsiocb->iocb;
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       /* For ADISC request, remainder of payload is service parameters */
+       *((uint32_t *) (pcmd)) = ELS_CMD_ADISC;
+       pcmd += sizeof (uint32_t);
+
+       /* Fill in ADISC payload */
+       ap = (ADISC *) pcmd;
+       ap->hardAL_PA = phba->fc_pref_ALPA;
+       memcpy(&ap->portName, &phba->fc_portname, sizeof (struct lpfc_name));
+       memcpy(&ap->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name));
+       ap->DID = be32_to_cpu(phba->fc_myDID);
+
+       /* The lpfc iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to
+        * the physical address.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       phba->fc_stat.elsXmitADISC++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_adisc;
+       ndlp->nlp_flag |= NLP_ADISC_SND;
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               ndlp->nlp_flag &= ~NLP_ADISC_SND;
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       return (0);
+}
+
+static void
+lpfc_cmpl_els_logo(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                  struct lpfc_iocbq * rspiocb)
+{
+       IOCB_t *irsp;
+       struct lpfc_sli *psli;
+       struct lpfc_nodelist *ndlp;
+
+       psli = &phba->sli;
+       /* we pass cmdiocb to state machine which needs rspiocb as well */
+       cmdiocb->context_un.rsp_iocb = rspiocb;
+
+       irsp = &(rspiocb->iocb);
+       ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+       ndlp->nlp_flag &= ~NLP_LOGO_SND;
+
+       /* LOGO completes to NPort <nlp_DID> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0105 LOGO completes to NPort x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, ndlp->nlp_DID, irsp->ulpStatus,
+                       irsp->un.ulpWord[4], phba->num_disc_nodes);
+
+       /* Check to see if link went down during discovery */
+       if (lpfc_els_chk_latt(phba))
+               goto out;
+
+       if (irsp->ulpStatus) {
+               /* Check for retry */
+               if (lpfc_els_retry(phba, cmdiocb, rspiocb)) {
+                       /* ELS command is being retried */
+                       goto out;
+               }
+               /* LOGO failed */
+               /* Do not call DSM for lpfc_els_abort'ed ELS cmds */
+               if((irsp->ulpStatus == IOSTAT_LOCAL_REJECT) &&
+                  ((irsp->un.ulpWord[4] == IOERR_SLI_ABORTED) ||
+                  (irsp->un.ulpWord[4] == IOERR_SLI_DOWN))) {
+                       goto out;
+               }
+               else {
+                       lpfc_disc_state_machine(phba, ndlp, cmdiocb,
+                                       NLP_EVT_CMPL_LOGO);
+               }
+       } else {
+               /* Good status, call state machine */
+               lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_LOGO);
+
+               if(ndlp->nlp_flag & NLP_DELAY_TMO) {
+                       lpfc_unreg_rpi(phba, ndlp);
+               }
+       }
+
+out:
+       lpfc_els_free_iocb(phba, cmdiocb);
+       return;
+}
+
+int
+lpfc_issue_els_logo(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+                   uint8_t retry)
+{
+       IOCB_t *icmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];
+
+       cmdsize = 2 * (sizeof (uint32_t) + sizeof (struct lpfc_name));
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+                                         ndlp, ELS_CMD_LOGO)) == 0) {
+               return (1);
+       }
+
+       icmd = &elsiocb->iocb;
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+       *((uint32_t *) (pcmd)) = ELS_CMD_LOGO;
+       pcmd += sizeof (uint32_t);
+
+       /* Fill in LOGO payload */
+       *((uint32_t *) (pcmd)) = be32_to_cpu(phba->fc_myDID);
+       pcmd += sizeof (uint32_t);
+       memcpy(pcmd, &phba->fc_portname, sizeof (struct lpfc_name));
+
+       /* The els iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to
+        * the physical address.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       phba->fc_stat.elsXmitLOGO++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_logo;
+       ndlp->nlp_flag |= NLP_LOGO_SND;
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               ndlp->nlp_flag &= ~NLP_LOGO_SND;
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       return (0);
+}
+
+static void
+lpfc_cmpl_els_cmd(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                 struct lpfc_iocbq * rspiocb)
+{
+       IOCB_t *irsp;
+
+       irsp = &rspiocb->iocb;
+
+       /* ELS cmd tag <ulpIoTag> completes */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_ELS,
+                       "%d:0106 ELS cmd tag x%x completes Data: x%x x%x\n",
+                       phba->brd_no,
+                       irsp->ulpIoTag, irsp->ulpStatus, irsp->un.ulpWord[4]);
+
+       /* Check to see if link went down during discovery */
+       lpfc_els_chk_latt(phba);
+       lpfc_els_free_iocb(phba, cmdiocb);
+       return;
+}
+
+int
+lpfc_issue_els_scr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry)
+{
+       IOCB_t *icmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+       struct lpfc_nodelist *ndlp;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+       cmdsize = (sizeof (uint32_t) + sizeof (SCR));
+       if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) == 0) {
+               return (1);
+       }
+
+       lpfc_nlp_init(phba, ndlp, nportid);
+
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+                                         ndlp, ELS_CMD_SCR)) == 0) {
+               mempool_free( ndlp, phba->nlp_mem_pool);
+               return (1);
+       }
+
+       icmd = &elsiocb->iocb;
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       *((uint32_t *) (pcmd)) = ELS_CMD_SCR;
+       pcmd += sizeof (uint32_t);
+
+       /* For SCR, remainder of payload is SCR parameter page */
+       memset(pcmd, 0, sizeof (SCR));
+       ((SCR *) pcmd)->Function = SCR_FUNC_FULL;
+
+       /* The els iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to
+        * the physical address.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       phba->fc_stat.elsXmitSCR++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               mempool_free( ndlp, phba->nlp_mem_pool);
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       mempool_free( ndlp, phba->nlp_mem_pool);
+       return (0);
+}
+
+static int
+lpfc_issue_els_farpr(struct lpfc_hba * phba, uint32_t nportid, uint8_t retry)
+{
+       IOCB_t *icmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       FARP *fp;
+       uint8_t *pcmd;
+       uint32_t *lp;
+       uint16_t cmdsize;
+       struct lpfc_nodelist *ondlp;
+       struct lpfc_nodelist *ndlp;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+       cmdsize = (sizeof (uint32_t) + sizeof (FARP));
+       if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC)) == 0) {
+               return (1);
+       }
+       lpfc_nlp_init(phba, ndlp, nportid);
+
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry,
+                                         ndlp, ELS_CMD_RNID)) == 0) {
+               mempool_free( ndlp, phba->nlp_mem_pool);
+               return (1);
+       }
+
+       icmd = &elsiocb->iocb;
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       *((uint32_t *) (pcmd)) = ELS_CMD_FARPR;
+       pcmd += sizeof (uint32_t);
+
+       /* Fill in FARPR payload */
+       fp = (FARP *) (pcmd);
+       memset(fp, 0, sizeof (FARP));
+       lp = (uint32_t *) pcmd;
+       *lp++ = be32_to_cpu(nportid);
+       *lp++ = be32_to_cpu(phba->fc_myDID);
+       fp->Rflags = 0;
+       fp->Mflags = (FARP_MATCH_PORT | FARP_MATCH_NODE);
+
+       memcpy(&fp->RportName, &phba->fc_portname, sizeof (struct lpfc_name));
+       memcpy(&fp->RnodeName, &phba->fc_nodename, sizeof (struct lpfc_name));
+       if ((ondlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, nportid))) {
+               memcpy(&fp->OportName, &ondlp->nlp_portname,
+                      sizeof (struct lpfc_name));
+               memcpy(&fp->OnodeName, &ondlp->nlp_nodename,
+                      sizeof (struct lpfc_name));
+       }
+
+       /* The els iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to
+        * the physical address.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       phba->fc_stat.elsXmitFARPR++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_cmd;
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               mempool_free( ndlp, phba->nlp_mem_pool);
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       mempool_free( ndlp, phba->nlp_mem_pool);
+       return (0);
+}
+
+void
+lpfc_els_retry_delay(unsigned long ptr)
+{
+       struct lpfc_hba *phba;
+       struct lpfc_nodelist *ndlp;
+       uint32_t cmd;
+       uint32_t did;
+       uint8_t retry;
+       unsigned long iflag;
+
+       ndlp = (struct lpfc_nodelist *)ptr;
+       phba = ndlp->nlp_phba;
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+       did = (uint32_t) (ndlp->nlp_DID);
+       cmd = (uint32_t) (ndlp->nlp_last_elscmd);
+
+       ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+       retry = ndlp->nlp_retry;
+
+       switch (cmd) {
+       case ELS_CMD_FLOGI:
+               lpfc_issue_els_flogi(phba, ndlp, retry);
+               break;
+       case ELS_CMD_PLOGI:
+               ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+               lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+               lpfc_issue_els_plogi(phba, ndlp, retry);
+               break;
+       case ELS_CMD_ADISC:
+               ndlp->nlp_state = NLP_STE_ADISC_ISSUE;
+               lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST);
+               lpfc_issue_els_adisc(phba, ndlp, retry);
+               break;
+       case ELS_CMD_PRLI:
+               ndlp->nlp_state = NLP_STE_PRLI_ISSUE;
+               lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST);
+               lpfc_issue_els_prli(phba, ndlp, retry);
+               break;
+       case ELS_CMD_LOGO:
+               ndlp->nlp_state = NLP_STE_NPR_NODE;
+               lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+               lpfc_issue_els_logo(phba, ndlp, retry);
+               break;
+       }
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+       return;
+}
+
+static int
+lpfc_els_retry(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+              struct lpfc_iocbq * rspiocb)
+{
+       IOCB_t *irsp;
+       struct lpfc_dmabuf *pcmd;
+       struct lpfc_nodelist *ndlp;
+       uint32_t *elscmd;
+       struct ls_rjt stat;
+       int retry, maxretry;
+       int delay;
+       uint32_t cmd;
+
+       retry = 0;
+       delay = 0;
+       maxretry = lpfc_max_els_tries;
+       irsp = &rspiocb->iocb;
+       ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       cmd = 0;
+       /* Note: context2 may be 0 for internal driver abort
+        * of delays ELS command.
+        */
+
+       if (pcmd && pcmd->virt) {
+               elscmd = (uint32_t *) (pcmd->virt);
+               cmd = *elscmd++;
+       }
+
+       switch (irsp->ulpStatus) {
+       case IOSTAT_FCP_RSP_ERROR:
+       case IOSTAT_REMOTE_STOP:
+               break;
+
+       case IOSTAT_LOCAL_REJECT:
+               switch ((irsp->un.ulpWord[4] & 0xff)) {
+               case IOERR_LOOP_OPEN_FAILURE:
+                       if (cmd == ELS_CMD_PLOGI) {
+                               if (cmdiocb->retry == 0) {
+                                       delay = 1;
+                               }
+                       }
+                       retry = 1;
+                       break;
+
+               case IOERR_SEQUENCE_TIMEOUT:
+                       retry = 1;
+                       if ((cmd == ELS_CMD_FLOGI)
+                           && (phba->fc_topology != TOPOLOGY_LOOP)) {
+                               delay = 1;
+                               maxretry = 48;
+                       }
+                       break;
+
+               case IOERR_NO_RESOURCES:
+                       if (cmd == ELS_CMD_PLOGI) {
+                               delay = 1;
+                       }
+                       retry = 1;
+                       break;
+
+               case IOERR_INVALID_RPI:
+                       retry = 1;
+                       break;
+               }
+               break;
+
+       case IOSTAT_NPORT_RJT:
+       case IOSTAT_FABRIC_RJT:
+               if (irsp->un.ulpWord[4] & RJT_UNAVAIL_TEMP) {
+                       retry = 1;
+                       break;
+               }
+               break;
+
+       case IOSTAT_NPORT_BSY:
+       case IOSTAT_FABRIC_BSY:
+               retry = 1;
+               break;
+
+       case IOSTAT_LS_RJT:
+               stat.un.lsRjtError = be32_to_cpu(irsp->un.ulpWord[4]);
+               /* Added for Vendor specifc support
+                * Just keep retrying for these Rsn / Exp codes
+                */
+               switch (stat.un.b.lsRjtRsnCode) {
+               case LSRJT_UNABLE_TPC:
+                       if (stat.un.b.lsRjtRsnCodeExp ==
+                           LSEXP_CMD_IN_PROGRESS) {
+                               if (cmd == ELS_CMD_PLOGI) {
+                                       delay = 1;
+                                       maxretry = 48;
+                               }
+                               retry = 1;
+                               break;
+                       }
+                       if (cmd == ELS_CMD_PLOGI) {
+                               delay = 1;
+                               maxretry = lpfc_max_els_tries + 1;
+                               retry = 1;
+                               break;
+                       }
+                       break;
+
+               case LSRJT_LOGICAL_BSY:
+                       if (cmd == ELS_CMD_PLOGI) {
+                               delay = 1;
+                               maxretry = 48;
+                       }
+                       retry = 1;
+                       break;
+               }
+               break;
+
+       case IOSTAT_INTERMED_RSP:
+       case IOSTAT_BA_RJT:
+               break;
+
+       default:
+               break;
+       }
+
+       if (ndlp->nlp_DID == FDMI_DID) {
+               retry = 1;
+       }
+
+       if ((++cmdiocb->retry) >= maxretry) {
+               phba->fc_stat.elsRetryExceeded++;
+               retry = 0;
+       }
+
+       if (retry) {
+
+               /* Retry ELS command <elsCmd> to remote NPORT <did> */
+               lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                               "%d:0107 Retry ELS command x%x to remote "
+                               "NPORT x%x Data: x%x x%x\n",
+                               phba->brd_no,
+                               cmd, ndlp->nlp_DID, cmdiocb->retry, delay);
+
+               if ((cmd == ELS_CMD_PLOGI) || (cmd == ELS_CMD_ADISC)) {
+                       /* If discovery / RSCN timer is running, reset it */
+                       if (timer_pending(&phba->fc_disctmo) ||
+                             (phba->fc_flag & FC_RSCN_MODE)) {
+                               lpfc_set_disctmo(phba);
+                       }
+               }
+
+               phba->fc_stat.elsXmitRetry++;
+               if (delay) {
+                       phba->fc_stat.elsDelayRetry++;
+                       ndlp->nlp_retry = cmdiocb->retry;
+
+                       mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+                       ndlp->nlp_flag |= NLP_DELAY_TMO;
+
+                       ndlp->nlp_state = NLP_STE_NPR_NODE;
+                       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+                       ndlp->nlp_last_elscmd = cmd;
+
+                       return (1);
+               }
+               switch (cmd) {
+               case ELS_CMD_FLOGI:
+                       lpfc_issue_els_flogi(phba, ndlp, cmdiocb->retry);
+                       return (1);
+               case ELS_CMD_PLOGI:
+                       ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+                       lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+                       lpfc_issue_els_plogi(phba, ndlp, cmdiocb->retry);
+                       return (1);
+               case ELS_CMD_ADISC:
+                       ndlp->nlp_state = NLP_STE_ADISC_ISSUE;
+                       lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST);
+                       lpfc_issue_els_adisc(phba, ndlp, cmdiocb->retry);
+                       return (1);
+               case ELS_CMD_PRLI:
+                       ndlp->nlp_state = NLP_STE_PRLI_ISSUE;
+                       lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST);
+                       lpfc_issue_els_prli(phba, ndlp, cmdiocb->retry);
+                       return (1);
+               case ELS_CMD_LOGO:
+                       ndlp->nlp_state = NLP_STE_NPR_NODE;
+                       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+                       lpfc_issue_els_logo(phba, ndlp, cmdiocb->retry);
+                       return (1);
+               }
+       }
+
+       /* No retry ELS command <elsCmd> to remote NPORT <did> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0108 No retry ELS command x%x to remote NPORT x%x "
+                       "Data: x%x x%x\n",
+                       phba->brd_no,
+                       cmd, ndlp->nlp_DID, cmdiocb->retry, ndlp->nlp_flag);
+
+       return (0);
+}
+
+int
+lpfc_els_free_iocb(struct lpfc_hba * phba, struct lpfc_iocbq * elsiocb)
+{
+       struct lpfc_dmabuf *buf_ptr, *buf_ptr1;
+
+       /* context2  = cmd,  context2->next = rsp, context3 = bpl */
+       if (elsiocb->context2) {
+               buf_ptr1 = (struct lpfc_dmabuf *) elsiocb->context2;
+               /* Free the response before processing the command.  */
+               if (!list_empty(&buf_ptr1->list)) {
+                       buf_ptr = list_entry(buf_ptr1->list.next,
+                                            struct lpfc_dmabuf, list);
+                       lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
+                       kfree(buf_ptr);
+               }
+               lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys);
+               kfree(buf_ptr1);
+       }
+
+       if (elsiocb->context3) {
+               buf_ptr = (struct lpfc_dmabuf *) elsiocb->context3;
+               lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
+               kfree(buf_ptr);
+       }
+
+       mempool_free( elsiocb, phba->iocb_mem_pool);
+       return 0;
+}
+
+static void
+lpfc_cmpl_els_logo_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                      struct lpfc_iocbq * rspiocb)
+{
+       struct lpfc_nodelist *ndlp;
+
+       ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+
+       /* ACC to LOGO completes to NPort <nlp_DID> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0109 ACC to LOGO completes to NPort x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag,
+                       ndlp->nlp_state, ndlp->nlp_rpi);
+
+       ndlp->nlp_flag &= ~NLP_LOGO_ACC;
+
+       switch (ndlp->nlp_state) {
+       case NLP_STE_UNUSED_NODE:       /* node is just allocated */
+               lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+               break;
+       case NLP_STE_NPR_NODE:          /* NPort Recovery mode */
+               lpfc_unreg_rpi(phba, ndlp);
+               break;
+       default:
+               break;
+       }
+       lpfc_els_free_iocb(phba, cmdiocb);
+       return;
+}
+
+static void
+lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                 struct lpfc_iocbq * rspiocb)
+{
+       struct lpfc_nodelist *ndlp;
+       LPFC_MBOXQ_t *mbox = NULL;
+
+       ndlp = (struct lpfc_nodelist *) cmdiocb->context1;
+       if (cmdiocb->context_un.mbox)
+               mbox = cmdiocb->context_un.mbox;
+
+
+       /* Check to see if link went down during discovery */
+       if ((lpfc_els_chk_latt(phba)) || !ndlp) {
+               if (mbox) {
+                       mempool_free( mbox, phba->mbox_mem_pool);
+               }
+               goto out;
+       }
+
+       /* ELS response tag <ulpIoTag> completes */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0110 ELS response tag x%x completes "
+                       "Data: x%x x%x x%x x%x x%x x%x\n",
+                       phba->brd_no,
+                       cmdiocb->iocb.ulpIoTag, rspiocb->iocb.ulpStatus,
+                       rspiocb->iocb.un.ulpWord[4], ndlp->nlp_DID,
+                       ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
+
+       if (mbox) {
+               if ((rspiocb->iocb.ulpStatus == 0)
+                   && (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) {
+                       /* set_slim mailbox command needs to execute first,
+                        * queue this command to be processed later.
+                        */
+                       lpfc_unreg_rpi(phba, ndlp);
+                       mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login;
+                       mbox->context2 = ndlp;
+                       ndlp->nlp_state = NLP_STE_REG_LOGIN_ISSUE;
+                       lpfc_nlp_list(phba, ndlp, NLP_REGLOGIN_LIST);
+                       if (lpfc_sli_issue_mbox(phba, mbox,
+                                               (MBX_NOWAIT | MBX_STOP_IOCB))
+                           != MBX_NOT_FINISHED) {
+                               goto out;
+                       }
+                       /* NOTE: we should have messages for unsuccessful
+                          reglogin */
+                       mempool_free( mbox, phba->mbox_mem_pool);
+               } else {
+                       mempool_free( mbox, phba->mbox_mem_pool);
+                       if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) {
+                               lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+                       }
+               }
+       }
+out:
+       if(ndlp)
+               ndlp->nlp_flag &= ~NLP_ACC_REGLOGIN;
+       lpfc_els_free_iocb(phba, cmdiocb);
+       return;
+}
+
+int
+lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag,
+                struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp,
+                LPFC_MBOXQ_t * mbox, uint8_t newnode)
+{
+       IOCB_t *icmd;
+       IOCB_t *oldcmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+       oldcmd = &oldiocb->iocb;
+
+       switch (flag) {
+       case ELS_CMD_ACC:
+               cmdsize = sizeof (uint32_t);
+               if ((elsiocb =
+                    lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+                                       ndlp, ELS_CMD_ACC)) == 0) {
+                       return (1);
+               }
+               icmd = &elsiocb->iocb;
+               icmd->ulpContext = oldcmd->ulpContext;  /* Xri */
+               pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+               *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+               pcmd += sizeof (uint32_t);
+               break;
+       case ELS_CMD_PLOGI:
+               cmdsize = (sizeof (struct serv_parm) + sizeof (uint32_t));
+               if ((elsiocb =
+                    lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+                                       ndlp, ELS_CMD_ACC)) == 0) {
+                       return (1);
+               }
+               icmd = &elsiocb->iocb;
+               icmd->ulpContext = oldcmd->ulpContext;  /* Xri */
+               pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+               if (mbox)
+                       elsiocb->context_un.mbox = mbox;
+
+               *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+               pcmd += sizeof (uint32_t);
+               memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm));
+               break;
+       default:
+               return (1);
+       }
+
+       if (newnode)
+               elsiocb->context1 = NULL;
+
+       /* Xmit ELS ACC response tag <ulpIoTag> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0128 Xmit ELS ACC response tag x%x "
+                       "Data: x%x x%x x%x x%x x%x\n",
+                       phba->brd_no,
+                       elsiocb->iocb.ulpIoTag,
+                       elsiocb->iocb.ulpContext, ndlp->nlp_DID,
+                       ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
+
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       if (ndlp->nlp_flag & NLP_LOGO_ACC) {
+               elsiocb->iocb_cmpl = lpfc_cmpl_els_logo_acc;
+       } else {
+               elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
+       }
+
+       phba->fc_stat.elsXmitACC++;
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       return (0);
+}
+
+int
+lpfc_els_rsp_reject(struct lpfc_hba * phba, uint32_t rejectError,
+                   struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp)
+{
+       IOCB_t *icmd;
+       IOCB_t *oldcmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+
+       cmdsize = 2 * sizeof (uint32_t);
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+                                         ndlp, ELS_CMD_LS_RJT)) == 0) {
+               return (1);
+       }
+
+       icmd = &elsiocb->iocb;
+       oldcmd = &oldiocb->iocb;
+       icmd->ulpContext = oldcmd->ulpContext;  /* Xri */
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       *((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT;
+       pcmd += sizeof (uint32_t);
+       *((uint32_t *) (pcmd)) = rejectError;
+
+       /* The els iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to
+        * the physical address.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       /* Xmit ELS RJT <err> response tag <ulpIoTag> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0129 Xmit ELS RJT x%x response tag x%x "
+                       "Data: x%x x%x x%x x%x x%x\n",
+                       phba->brd_no,
+                       rejectError, elsiocb->iocb.ulpIoTag,
+                       elsiocb->iocb.ulpContext, ndlp->nlp_DID,
+                       ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
+
+       phba->fc_stat.elsXmitLSRJT++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
+
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       return (0);
+}
+
+int
+lpfc_els_rsp_adisc_acc(struct lpfc_hba * phba,
+                      struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp)
+{
+       ADISC *ap;
+       IOCB_t *icmd;
+       IOCB_t *oldcmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+
+       cmdsize = sizeof (uint32_t) + sizeof (ADISC);
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+                                         ndlp, ELS_CMD_ACC)) == 0) {
+               return (1);
+       }
+
+       /* Xmit ADISC ACC response tag <ulpIoTag> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0130 Xmit ADISC ACC response tag x%x "
+                       "Data: x%x x%x x%x x%x x%x\n",
+                       phba->brd_no,
+                       elsiocb->iocb.ulpIoTag,
+                       elsiocb->iocb.ulpContext, ndlp->nlp_DID,
+                       ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
+
+       icmd = &elsiocb->iocb;
+       oldcmd = &oldiocb->iocb;
+       icmd->ulpContext = oldcmd->ulpContext;  /* Xri */
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+       pcmd += sizeof (uint32_t);
+
+       ap = (ADISC *) (pcmd);
+       ap->hardAL_PA = phba->fc_pref_ALPA;
+       memcpy(&ap->portName, &phba->fc_portname, sizeof (struct lpfc_name));
+       memcpy(&ap->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name));
+       ap->DID = be32_to_cpu(phba->fc_myDID);
+
+       /* The els iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to
+        * the physical address.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       phba->fc_stat.elsXmitACC++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
+
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       return (0);
+}
+
+int
+lpfc_els_rsp_prli_acc(struct lpfc_hba * phba,
+                     struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp)
+{
+       PRLI *npr;
+       lpfc_vpd_t *vpd;
+       IOCB_t *icmd;
+       IOCB_t *oldcmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];     /* ELS ring */
+
+       cmdsize = sizeof (uint32_t) + sizeof (PRLI);
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+                                         ndlp,
+                                         (ELS_CMD_ACC |
+                                          (ELS_CMD_PRLI & ~ELS_RSP_MASK)))) ==
+           0) {
+               return (1);
+       }
+
+       /* Xmit PRLI ACC response tag <ulpIoTag> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0131 Xmit PRLI ACC response tag x%x "
+                       "Data: x%x x%x x%x x%x x%x\n",
+                       phba->brd_no,
+                       elsiocb->iocb.ulpIoTag,
+                       elsiocb->iocb.ulpContext, ndlp->nlp_DID,
+                       ndlp->nlp_flag, ndlp->nlp_state, ndlp->nlp_rpi);
+
+       icmd = &elsiocb->iocb;
+       oldcmd = &oldiocb->iocb;
+       icmd->ulpContext = oldcmd->ulpContext;  /* Xri */
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       *((uint32_t *) (pcmd)) = (ELS_CMD_ACC | (ELS_CMD_PRLI & ~ELS_RSP_MASK));
+       pcmd += sizeof (uint32_t);
+
+       /* For PRLI, remainder of payload is PRLI parameter page */
+       memset(pcmd, 0, sizeof (PRLI));
+
+       npr = (PRLI *) pcmd;
+       vpd = &phba->vpd;
+       /*
+        * If our firmware version is 3.20 or later,
+        * set the following bits for FC-TAPE support.
+        */
+       if (vpd->rev.feaLevelHigh >= 0x02) {
+               npr->ConfmComplAllowed = 1;
+               npr->Retry = 1;
+               npr->TaskRetryIdReq = 1;
+       }
+
+       npr->acceptRspCode = PRLI_REQ_EXECUTED;
+       npr->estabImagePair = 1;
+       npr->readXferRdyDis = 1;
+       npr->ConfmComplAllowed = 1;
+
+       npr->prliType = PRLI_FCP_TYPE;
+       npr->initiatorFunc = 1;
+
+       /* The els iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to
+        * the physical address.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       phba->fc_stat.elsXmitACC++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
+
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       return (0);
+}
+
+static int
+lpfc_els_rsp_rnid_acc(struct lpfc_hba * phba,
+                     uint8_t format,
+                     struct lpfc_iocbq * oldiocb, struct lpfc_nodelist * ndlp)
+{
+       RNID *rn;
+       IOCB_t *icmd;
+       IOCB_t *oldcmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       struct lpfc_dmabuf *bmp;
+       uint8_t *pcmd;
+       uint16_t cmdsize;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];
+
+       cmdsize = sizeof (uint32_t) + sizeof (uint32_t)
+               + (2 * sizeof (struct lpfc_name));
+       if (format)
+               cmdsize += sizeof (RNID_TOP_DISC);
+
+       if ((elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry,
+                                         ndlp, ELS_CMD_ACC)) == 0) {
+               return (1);
+       }
+
+       /* Xmit RNID ACC response tag <ulpIoTag> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0132 Xmit RNID ACC response tag x%x "
+                       "Data: x%x\n",
+                       phba->brd_no,
+                       elsiocb->iocb.ulpIoTag,
+                       elsiocb->iocb.ulpContext);
+
+       icmd = &elsiocb->iocb;
+       oldcmd = &oldiocb->iocb;
+       icmd->ulpContext = oldcmd->ulpContext;  /* Xri */
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+       pcmd += sizeof (uint32_t);
+
+       memset(pcmd, 0, sizeof (RNID));
+       rn = (RNID *) (pcmd);
+       rn->Format = format;
+       rn->CommonLen = (2 * sizeof (struct lpfc_name));
+       memcpy(&rn->portName, &phba->fc_portname, sizeof (struct lpfc_name));
+       memcpy(&rn->nodeName, &phba->fc_nodename, sizeof (struct lpfc_name));
+       switch (format) {
+       case 0:
+               rn->SpecificLen = 0;
+               break;
+       case RNID_TOPOLOGY_DISC:
+               rn->SpecificLen = sizeof (RNID_TOP_DISC);
+               memcpy(&rn->un.topologyDisc.portName,
+                      &phba->fc_portname, sizeof (struct lpfc_name));
+               rn->un.topologyDisc.unitType = RNID_HBA;
+               rn->un.topologyDisc.physPort = 0;
+               rn->un.topologyDisc.attachedNodes = 0;
+               break;
+       default:
+               rn->CommonLen = 0;
+               rn->SpecificLen = 0;
+               break;
+       }
+
+       /* The els iocb is fully initialize.  Flush it to main store for the
+        * HBA.  Note that all els iocb context buffer are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE. Get a short-hand pointer to
+        * the physical address.
+        */
+       bmp = (struct lpfc_dmabuf *) (elsiocb->context2);
+       pci_dma_sync_single_for_device(phba->pcidev, bmp->phys,
+               LPFC_BPL_SIZE, PCI_DMA_TODEVICE);
+
+       phba->fc_stat.elsXmitACC++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_acc;
+       elsiocb->context1 = NULL;  /* Don't need ndlp for cmpl,
+                                   * it could be freed */
+
+       if (lpfc_sli_issue_iocb(phba, pring, elsiocb, 0) == IOCB_ERROR) {
+               lpfc_els_free_iocb(phba, elsiocb);
+               return (1);
+       }
+       return (0);
+}
+
+int
+lpfc_els_disc_adisc(struct lpfc_hba * phba)
+{
+       int sentadisc;
+       struct lpfc_nodelist *ndlp, *next_ndlp;
+
+       sentadisc = 0;
+       /* go thru NPR list and issue any remaining ELS ADISCs */
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+                       nlp_listp) {
+               if(ndlp->nlp_flag & NLP_NPR_2B_DISC) {
+                       if(ndlp->nlp_flag & NLP_NPR_ADISC) {
+                               ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+                               ndlp->nlp_state = NLP_STE_ADISC_ISSUE;
+                               lpfc_nlp_list(phba, ndlp,
+                                       NLP_ADISC_LIST);
+                               lpfc_issue_els_adisc(phba, ndlp, 0);
+                               sentadisc++;
+                               phba->num_disc_nodes++;
+                               if (phba->num_disc_nodes >=
+                                   phba->cfg_discovery_threads) {
+                                       phba->fc_flag |= FC_NLP_MORE;
+                                       break;
+                               }
+                       }
+               }
+       }
+       if (sentadisc == 0) {
+               phba->fc_flag &= ~FC_NLP_MORE;
+       }
+       return(sentadisc);
+}
+
+int
+lpfc_els_disc_plogi(struct lpfc_hba * phba)
+{
+       int sentplogi;
+       struct lpfc_nodelist *ndlp, *next_ndlp;
+
+       sentplogi = 0;
+       /* go thru NPR list and issue any remaining ELS PLOGIs */
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+                               nlp_listp) {
+               if((ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
+                  (!(ndlp->nlp_flag & NLP_DELAY_TMO))) {
+                       if(!(ndlp->nlp_flag & NLP_NPR_ADISC)) {
+                               ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+                               lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+                               lpfc_issue_els_plogi(phba, ndlp, 0);
+                               sentplogi++;
+                               phba->num_disc_nodes++;
+                               if (phba->num_disc_nodes >=
+                                   phba->cfg_discovery_threads) {
+                                       phba->fc_flag |= FC_NLP_MORE;
+                                       break;
+                               }
+                       }
+               }
+       }
+       if (sentplogi == 0) {
+               phba->fc_flag &= ~FC_NLP_MORE;
+       }
+       return(sentplogi);
+}
+
+int
+lpfc_els_flush_rscn(struct lpfc_hba * phba)
+{
+       struct lpfc_dmabuf *mp;
+       int i;
+
+       for (i = 0; i < phba->fc_rscn_id_cnt; i++) {
+               mp = phba->fc_rscn_id_list[i];
+               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+               kfree(mp);
+               phba->fc_rscn_id_list[i] = NULL;
+       }
+       phba->fc_rscn_id_cnt = 0;
+       phba->fc_flag &= ~(FC_RSCN_MODE | FC_RSCN_DISCOVERY);
+       lpfc_can_disctmo(phba);
+       return (0);
+}
+
+int
+lpfc_rscn_payload_check(struct lpfc_hba * phba, uint32_t did)
+{
+       D_ID ns_did;
+       D_ID rscn_did;
+       struct lpfc_dmabuf *mp;
+       uint32_t *lp;
+       uint32_t payload_len, cmd, i, match;
+
+       ns_did.un.word = did;
+       match = 0;
+
+       /* If we are doing a FULL RSCN rediscovery, match everything */
+       if (phba->fc_flag & FC_RSCN_DISCOVERY) {
+               return (did);
+       }
+
+       for (i = 0; i < phba->fc_rscn_id_cnt; i++) {
+               mp = phba->fc_rscn_id_list[i];
+               lp = (uint32_t *) mp->virt;
+               cmd = *lp++;
+               payload_len = be32_to_cpu(cmd) & 0xffff; /* payload length */
+               payload_len -= sizeof (uint32_t);       /* take off word 0 */
+               while (payload_len) {
+                       rscn_did.un.word = *lp++;
+                       rscn_did.un.word = be32_to_cpu(rscn_did.un.word);
+                       payload_len -= sizeof (uint32_t);
+                       switch (rscn_did.un.b.resv) {
+                       case 0: /* Single N_Port ID effected */
+                               if (ns_did.un.word == rscn_did.un.word) {
+                                       match = did;
+                               }
+                               break;
+                       case 1: /* Whole N_Port Area effected */
+                               if ((ns_did.un.b.domain == rscn_did.un.b.domain)
+                                   && (ns_did.un.b.area == rscn_did.un.b.area))
+                                       {
+                                               match = did;
+                                       }
+                               break;
+                       case 2: /* Whole N_Port Domain effected */
+                               if (ns_did.un.b.domain == rscn_did.un.b.domain)
+                                       {
+                                               match = did;
+                                       }
+                               break;
+                       case 3: /* Whole Fabric effected */
+                               match = did;
+                               break;
+                       default:
+                               /* Unknown Identifier in RSCN list */
+                               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+                                               "%d:0217 Unknown Identifier in "
+                                               "RSCN payload Data: x%x\n",
+                                               phba->brd_no, rscn_did.un.word);
+                               break;
+                       }
+                       if (match) {
+                               break;
+                       }
+               }
+       }
+       return (match);
+}
+
+static int
+lpfc_rscn_recovery_check(struct lpfc_hba * phba)
+{
+       struct lpfc_nodelist *ndlp = NULL, *next_ndlp;
+       struct list_head *listp;
+       struct list_head *node_list[7];
+       int i;
+
+       /* Look at all nodes effected by pending RSCNs and move
+        * them to NPR list.
+        */
+       node_list[0] = &phba->fc_npr_list;  /* MUST do this list first */
+       node_list[1] = &phba->fc_nlpmap_list;
+       node_list[2] = &phba->fc_nlpunmap_list;
+       node_list[3] = &phba->fc_prli_list;
+       node_list[4] = &phba->fc_reglogin_list;
+       node_list[5] = &phba->fc_adisc_list;
+       node_list[6] = &phba->fc_plogi_list;
+       for (i = 0; i < 7; i++) {
+               listp = node_list[i];
+               if (list_empty(listp))
+                       continue;
+
+               list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) {
+                       if((lpfc_rscn_payload_check(phba, ndlp->nlp_DID))) {
+                               /* part of RSCN, process this entry */
+                               lpfc_set_failmask(phba, ndlp,
+                                       LPFC_DEV_DISCOVERY_INP,
+                                       LPFC_SET_BITMASK);
+
+                               lpfc_disc_state_machine(phba, ndlp, NULL,
+                                               NLP_EVT_DEVICE_RECOVERY);
+                               if(ndlp->nlp_flag & NLP_DELAY_TMO) {
+                                       ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+                                       del_timer_sync(&ndlp->nlp_delayfunc);
+                               }
+                       }
+               }
+       }
+       return (0);
+}
+
+static int
+lpfc_els_rcv_rscn(struct lpfc_hba * phba,
+                 struct lpfc_iocbq * cmdiocb,
+                 struct lpfc_nodelist * ndlp, uint8_t newnode)
+{
+       struct lpfc_dmabuf *pcmd;
+       uint32_t *lp;
+       IOCB_t *icmd;
+       uint32_t payload_len, cmd;
+
+       icmd = &cmdiocb->iocb;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       lp = (uint32_t *) pcmd->virt;
+
+       /* The response iocb was populated by the HBA.  Flush it to main store
+        * for the driver.  Note that all iocb context buffers are from the
+        * driver's dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_device(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       cmd = *lp++;
+       payload_len = be32_to_cpu(cmd) & 0xffff;        /* payload length */
+       payload_len -= sizeof (uint32_t);       /* take off word 0 */
+       cmd &= ELS_CMD_MASK;
+
+       /* RSCN received */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_DISCOVERY,
+                       "%d:0214 RSCN received Data: x%x x%x x%x x%x\n",
+                       phba->brd_no,
+                       phba->fc_flag, payload_len, *lp, phba->fc_rscn_id_cnt);
+
+       /* if we are already processing an RSCN, save the received
+        * RSCN payload buffer, cmdiocb->context2 to process later.
+        * If we zero, cmdiocb->context2, the calling routine will
+        * not try to free it.
+        */
+       if (phba->fc_flag & FC_RSCN_MODE) {
+               if ((phba->fc_rscn_id_cnt < FC_MAX_HOLD_RSCN) &&
+                   !(phba->fc_flag & FC_RSCN_DISCOVERY)) {
+                       phba->fc_rscn_id_list[phba->fc_rscn_id_cnt++] = pcmd;
+                       cmdiocb->context2 = NULL;
+                       /* Deferred RSCN */
+                       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                                       "%d:0235 Deferred RSCN "
+                                       "Data: x%x x%x x%x\n",
+                                       phba->brd_no, phba->fc_rscn_id_cnt,
+                                       phba->fc_flag, phba->hba_state);
+               } else {
+                       phba->fc_flag |= FC_RSCN_DISCOVERY;
+                       /* ReDiscovery RSCN */
+                       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                                       "%d:0234 ReDiscovery RSCN "
+                                       "Data: x%x x%x x%x\n",
+                                       phba->brd_no, phba->fc_rscn_id_cnt,
+                                       phba->fc_flag, phba->hba_state);
+               }
+               /* Send back ACC */
+               lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL,
+                                                               newnode);
+
+               /* send RECOVERY event for ALL nodes that match RSCN payload */
+               lpfc_rscn_recovery_check(phba);
+               return (0);
+       }
+
+       phba->fc_flag |= FC_RSCN_MODE;
+       phba->fc_rscn_id_list[phba->fc_rscn_id_cnt++] = pcmd;
+       /*
+        * If we zero, cmdiocb->context2, the calling routine will
+        * not try to free it.
+        */
+       cmdiocb->context2 = NULL;
+
+       lpfc_set_disctmo(phba);
+
+       /* Send back ACC */
+       lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, newnode);
+
+       /* send RECOVERY event for ALL nodes that match RSCN payload */
+       lpfc_rscn_recovery_check(phba);
+
+       return (lpfc_els_handle_rscn(phba));
+}
+
+int
+lpfc_els_handle_rscn(struct lpfc_hba * phba)
+{
+       struct lpfc_nodelist *ndlp;
+
+       lpfc_put_event(phba, HBA_EVENT_RSCN, phba->fc_myDID,
+                         (void *)(unsigned long)(phba->fc_myDID), 0, 0);
+
+       /* Start timer for RSCN processing */
+       lpfc_set_disctmo(phba);
+
+       /* RSCN processed */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_DISCOVERY,
+                       "%d:0215 RSCN processed Data: x%x x%x x%x x%x\n",
+                       phba->brd_no,
+                       phba->fc_flag, 0, phba->fc_rscn_id_cnt,
+                       phba->hba_state);
+
+       /* To process RSCN, first compare RSCN data with NameServer */
+       phba->fc_ns_retry = 0;
+       if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED,
+                                     NameServer_DID))) {
+               /* Good ndlp, issue CT Request to NameServer */
+               if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) == 0) {
+                       /* Wait for NameServer query cmpl before we can
+                          continue */
+                       return (1);
+               }
+       } else {
+               /* If login to NameServer does not exist, issue one */
+               /* Good status, issue PLOGI to NameServer */
+               if ((ndlp =
+                    lpfc_findnode_did(phba, NLP_SEARCH_ALL, NameServer_DID))) {
+                       /* Wait for NameServer login cmpl before we can
+                          continue */
+                       return (1);
+               }
+               if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC))
+                   == 0) {
+                       lpfc_els_flush_rscn(phba);
+                       return (0);
+               } else {
+                       lpfc_nlp_init(phba, ndlp, NameServer_DID);
+                       ndlp->nlp_type |= NLP_FABRIC;
+                       ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+                       lpfc_issue_els_plogi(phba, ndlp, 0);
+                       /* Wait for NameServer login cmpl before we can
+                          continue */
+                       return (1);
+               }
+       }
+
+       lpfc_els_flush_rscn(phba);
+       return (0);
+}
+
+static int
+lpfc_els_rcv_flogi(struct lpfc_hba * phba,
+                  struct lpfc_iocbq * cmdiocb,
+                  struct lpfc_nodelist * ndlp, uint8_t newnode)
+{
+       struct lpfc_dmabuf *pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       uint32_t *lp = (uint32_t *) pcmd->virt;
+       IOCB_t *icmd = &cmdiocb->iocb;
+       struct serv_parm *sp;
+       LPFC_MBOXQ_t *mbox;
+       struct ls_rjt stat;
+       uint32_t cmd, did;
+
+
+       /* The response iocb was populated by the HBA.  Flush it to main store
+        * for the driver.  Note that all iocb context buffers are from the
+        * driver's dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_device(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       cmd = *lp++;
+       sp = (struct serv_parm *) lp;
+
+       /* FLOGI received */
+
+       lpfc_set_disctmo(phba);
+
+       if (phba->fc_topology == TOPOLOGY_LOOP) {
+               /* We should never receive a FLOGI in loop mode, ignore it */
+               did = icmd->un.elsreq64.remoteID;
+
+               /* An FLOGI ELS command <elsCmd> was received from DID <did> in
+                  Loop Mode */
+               lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+                               "%d:0113 An FLOGI ELS command x%x was received "
+                               "from DID x%x in Loop Mode\n",
+                               phba->brd_no, cmd, did);
+               return (1);
+       }
+
+       did = Fabric_DID;
+
+       if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3))) {
+               /* For a FLOGI we accept, then if our portname is greater
+                * then the remote portname we initiate Nport login.
+                */
+               int rc;
+
+               rc = memcmp(&phba->fc_portname, &sp->portName,
+                           sizeof (struct lpfc_name));
+
+               if (!rc) {
+                       if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+                                                 GFP_ATOMIC)) == 0) {
+                               return (1);
+                       }
+                       lpfc_linkdown(phba);
+                       lpfc_init_link(phba, mbox,
+                                      phba->cfg_topology,
+                                      phba->cfg_link_speed);
+                       mbox->mb.un.varInitLnk.lipsr_AL_PA = 0;
+                       if (lpfc_sli_issue_mbox
+                           (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB))
+                           == MBX_NOT_FINISHED) {
+                               mempool_free( mbox, phba->mbox_mem_pool);
+                       }
+                       return (1);
+               }
+
+               else if (rc > 0) {      /* greater than */
+                       phba->fc_flag |= FC_PT2PT_PLOGI;
+               }
+               phba->fc_flag |= FC_PT2PT;
+               phba->fc_flag &= ~(FC_FABRIC | FC_PUBLIC_LOOP);
+       } else {
+               /* Reject this request because invalid parameters */
+               stat.un.b.lsRjtRsvd0 = 0;
+               stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+               stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;
+               stat.un.b.vendorUnique = 0;
+               lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+               return (1);
+       }
+
+       /* Send back ACC */
+       lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, newnode);
+
+       return (0);
+}
+
+static int
+lpfc_els_rcv_rnid(struct lpfc_hba * phba,
+                 struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp)
+{
+       struct lpfc_dmabuf *pcmd;
+       uint32_t *lp;
+       IOCB_t *icmd;
+       RNID *rn;
+       struct ls_rjt stat;
+       uint32_t cmd, did;
+
+       icmd = &cmdiocb->iocb;
+       did = icmd->un.elsreq64.remoteID;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       lp = (uint32_t *) pcmd->virt;
+
+       /* The response iocb was populated by the HBA.  Flush it to main store
+        * for the driver.  Note that all iocb context buffers are from the
+        * driver's dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_device(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       cmd = *lp++;
+       rn = (RNID *) lp;
+
+       /* RNID received */
+
+       switch (rn->Format) {
+       case 0:
+       case RNID_TOPOLOGY_DISC:
+               /* Send back ACC */
+               lpfc_els_rsp_rnid_acc(phba, rn->Format, cmdiocb, ndlp);
+               break;
+       default:
+               /* Reject this request because format not supported */
+               stat.un.b.lsRjtRsvd0 = 0;
+               stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+               stat.un.b.lsRjtRsnCodeExp = LSEXP_CANT_GIVE_DATA;
+               stat.un.b.vendorUnique = 0;
+               lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+       }
+       return (0);
+}
+
+static int
+lpfc_els_rcv_rrq(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                struct lpfc_nodelist * ndlp)
+{
+       struct lpfc_dmabuf *pcmd;
+       uint32_t *lp;
+       IOCB_t *icmd;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       RRQ *rrq;
+       uint32_t cmd, did;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_FCP_RING];
+       icmd = &cmdiocb->iocb;
+       did = icmd->un.elsreq64.remoteID;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       lp = (uint32_t *) pcmd->virt;
+
+       /* The response iocb was populated by the HBA.  Flush it to main store
+        * for the driver.  Note that all iocb context buffers are from the
+        * driver's dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       cmd = *lp++;
+       rrq = (RRQ *) lp;
+
+       /* RRQ received */
+       /* Get oxid / rxid from payload and abort it */
+       if ((rrq->SID == be32_to_cpu(phba->fc_myDID))) {
+               lpfc_sli_abort_iocb_ctx(phba, pring, rrq->Oxid);
+       } else {
+               lpfc_sli_abort_iocb_ctx(phba, pring, rrq->Rxid);
+       }
+       /* ACCEPT the rrq request */
+       lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+
+       return 0;
+}
+
+static int
+lpfc_els_rcv_farp(struct lpfc_hba * phba,
+                 struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp)
+{
+       struct lpfc_dmabuf *pcmd;
+       uint32_t *lp;
+       IOCB_t *icmd;
+       FARP *fp;
+       uint32_t cmd, cnt, did;
+
+       icmd = &cmdiocb->iocb;
+       did = icmd->un.elsreq64.remoteID;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       lp = (uint32_t *) pcmd->virt;
+
+       /* The response iocb was populated by the HBA.  Flush it to main store
+        * for the driver.  Note that all iocb context buffers are from the
+        * driver's dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       cmd = *lp++;
+       fp = (FARP *) lp;
+
+       /* FARP-REQ received from DID <did> */
+       lpfc_printf_log(phba,
+                        KERN_INFO,
+                        LOG_IP,
+                        "%d:0601 FARP-REQ received from DID x%x\n",
+                        phba->brd_no, did);
+
+       /* We will only support match on WWPN or WWNN */
+       if (fp->Mflags & ~(FARP_MATCH_NODE | FARP_MATCH_PORT)) {
+               return (0);
+       }
+
+       cnt = 0;
+       /* If this FARP command is searching for my portname */
+       if (fp->Mflags & FARP_MATCH_PORT) {
+               if (memcmp(&fp->RportName, &phba->fc_portname,
+                          sizeof (struct lpfc_name)) == 0)
+                       cnt = 1;
+       }
+
+       /* If this FARP command is searching for my nodename */
+       if (fp->Mflags & FARP_MATCH_NODE) {
+               if (memcmp(&fp->RnodeName, &phba->fc_nodename,
+                          sizeof (struct lpfc_name)) == 0)
+                       cnt = 1;
+       }
+
+       if (cnt) {
+               if((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) ||
+                  (ndlp->nlp_state == NLP_STE_MAPPED_NODE)) {
+                       /* Log back into the node before sending the FARP. */
+                       if (fp->Rflags & FARP_REQUEST_PLOGI) {
+                               ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+                               lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+                               lpfc_issue_els_plogi(phba, ndlp, 0);
+                       }
+
+                       /* Send a FARP response to that node */
+                       if (fp->Rflags & FARP_REQUEST_FARPR) {
+                               lpfc_issue_els_farpr(phba, did, 0);
+                       }
+               }
+       }
+       return (0);
+}
+
+static int
+lpfc_els_rcv_farpr(struct lpfc_hba * phba,
+                  struct lpfc_iocbq * cmdiocb, struct lpfc_nodelist * ndlp)
+{
+       struct lpfc_dmabuf *pcmd;
+       uint32_t *lp;
+       IOCB_t *icmd;
+       uint32_t cmd, did;
+
+       icmd = &cmdiocb->iocb;
+       did = icmd->un.elsreq64.remoteID;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       lp = (uint32_t *) pcmd->virt;
+
+       /* The response iocb was populated by the HBA.  Flush it to main store
+        * for the driver.  Note that all iocb context buffers are from the
+        * driver's dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       cmd = *lp++;
+       /* FARP-RSP received from DID <did> */
+       lpfc_printf_log(phba,
+                        KERN_INFO,
+                        LOG_IP,
+                        "%d:0600 FARP-RSP received from DID x%x\n",
+                        phba->brd_no, did);
+
+       /* ACCEPT the Farp resp request */
+       lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+
+       return 0;
+}
+
+static int
+lpfc_els_rcv_fan(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                struct lpfc_nodelist * ndlp)
+{
+       struct lpfc_dmabuf *pcmd;
+       uint32_t *lp;
+       IOCB_t *icmd;
+       FAN *fp;
+       uint32_t cmd, did;
+
+       icmd = &cmdiocb->iocb;
+       did = icmd->un.elsreq64.remoteID;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       lp = (uint32_t *) pcmd->virt;
+
+       /* The response iocb was populated by the HBA.  Flush it to main store
+        * for the driver.  Note that all iocb context buffers are from the
+        * driver's dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       cmd = *lp++;
+       fp = (FAN *) lp;
+
+       /* FAN received */
+
+       /* ACCEPT the FAN request */
+       lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+
+       if (phba->hba_state == LPFC_LOCAL_CFG_LINK) {
+               /* The discovery state machine needs to take a different
+                * action if this node has switched fabrics
+                */
+               if ((memcmp(&fp->FportName, &phba->fc_fabparam.portName,
+                           sizeof (struct lpfc_name)) != 0)
+                   ||
+                   (memcmp(&fp->FnodeName, &phba->fc_fabparam.nodeName,
+                           sizeof (struct lpfc_name)) != 0)) {
+                       /* This node has switched fabrics.  An FLOGI is required
+                        * after the timeout
+                        */
+                       return (0);
+               }
+
+               /* Start discovery */
+               lpfc_disc_start(phba);
+       }
+
+       return (0);
+}
+
+void
+lpfc_els_timeout_handler(unsigned long ptr)
+{
+       struct lpfc_hba *phba;
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_iocbq *tmp_iocb, *piocb;
+       IOCB_t *cmd = NULL;
+       struct lpfc_dmabuf *pcmd;
+       struct list_head *dlp;
+       uint32_t *elscmd;
+       uint32_t els_command;
+       uint32_t timeout;
+       uint32_t remote_ID;
+       unsigned long iflag;
+
+       phba = (struct lpfc_hba *)ptr;
+       if(phba == 0)
+               return;
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+       timeout = (uint32_t)(phba->fc_ratov << 1);
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];
+       dlp = &pring->txcmplq;
+
+       list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) {
+               cmd = &piocb->iocb;
+
+               if (piocb->iocb_flag & LPFC_IO_LIBDFC) {
+                       continue;
+               }
+               pcmd = (struct lpfc_dmabuf *) piocb->context2;
+               elscmd = (uint32_t *) (pcmd->virt);
+               els_command = *elscmd;
+
+               if ((els_command == ELS_CMD_FARP)
+                   || (els_command == ELS_CMD_FARPR)) {
+                       continue;
+               }
+
+               if (piocb->drvrTimeout > 0) {
+                       if (piocb->drvrTimeout >= timeout) {
+                               piocb->drvrTimeout -= timeout;
+                       } else {
+                               piocb->drvrTimeout = 0;
+                       }
+                       continue;
+               }
+
+               list_del(&piocb->list);
+               pring->txcmplq_cnt--;
+
+               if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) {
+                       struct lpfc_nodelist *ndlp;
+
+                       ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext);
+                       remote_ID = ndlp->nlp_DID;
+                       if (cmd->un.elsreq64.bdl.ulpIoTag32) {
+                               lpfc_sli_issue_abort_iotag32(phba,
+                                       pring, piocb);
+                       }
+               } else {
+                       remote_ID = cmd->un.elsreq64.remoteID;
+               }
+
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_ELS,
+                               "%d:0127 ELS timeout Data: x%x x%x x%x x%x\n",
+                               phba->brd_no, els_command,
+                               remote_ID, cmd->ulpCommand, cmd->ulpIoTag);
+
+               /*
+                * The iocb has timed out; abort it.
+                */
+               if (piocb->iocb_cmpl) {
+                       cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                       cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                       (piocb->iocb_cmpl) (phba, piocb, piocb);
+               } else {
+                       mempool_free(piocb, phba->iocb_mem_pool);
+               }
+       }
+
+       phba->els_tmofunc.expires = jiffies + HZ * timeout;
+       add_timer(&phba->els_tmofunc);
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+}
+
+void
+lpfc_els_flush_cmd(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_iocbq *tmp_iocb, *piocb;
+       IOCB_t *cmd = NULL;
+       struct lpfc_dmabuf *pcmd;
+       uint32_t *elscmd;
+       uint32_t els_command;
+       uint32_t remote_ID;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];
+
+       list_for_each_entry_safe(piocb, tmp_iocb, &pring->txq, list) {
+               cmd = &piocb->iocb;
+
+               if (piocb->iocb_flag & LPFC_IO_LIBDFC) {
+                       continue;
+               }
+
+               /* Do not flush out the QUE_RING and ABORT/CLOSE iocbs */
+               if ((cmd->ulpCommand == CMD_QUE_RING_BUF_CN) ||
+                   (cmd->ulpCommand == CMD_QUE_RING_BUF64_CN) ||
+                   (cmd->ulpCommand == CMD_CLOSE_XRI_CN) ||
+                   (cmd->ulpCommand == CMD_ABORT_XRI_CN)) {
+                       continue;
+               }
+
+               pcmd = (struct lpfc_dmabuf *) piocb->context2;
+               elscmd = (uint32_t *) (pcmd->virt);
+               els_command = *elscmd;
+
+               if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) {
+                       struct lpfc_nodelist *ndlp;
+
+                       ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext);
+                       remote_ID = ndlp->nlp_DID;
+                       if (phba->hba_state == LPFC_HBA_READY) {
+                               continue;
+                       }
+               } else {
+                       remote_ID = cmd->un.elsreq64.remoteID;
+               }
+
+               list_del(&piocb->list);
+               pring->txcmplq_cnt--;
+
+               cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+               cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+
+               if (piocb->iocb_cmpl) {
+                       (piocb->iocb_cmpl) (phba, piocb, piocb);
+               } else {
+                       mempool_free( piocb, phba->iocb_mem_pool);
+               }
+       }
+
+       list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) {
+               cmd = &piocb->iocb;
+
+               if (piocb->iocb_flag & LPFC_IO_LIBDFC) {
+                       continue;
+               }
+               pcmd = (struct lpfc_dmabuf *) piocb->context2;
+               elscmd = (uint32_t *) (pcmd->virt);
+               els_command = *elscmd;
+
+               if (cmd->ulpCommand == CMD_GEN_REQUEST64_CR) {
+                       struct lpfc_nodelist *ndlp;
+
+                       ndlp = lpfc_findnode_rpi(phba, cmd->ulpContext);
+                       remote_ID = ndlp->nlp_DID;
+                       if (phba->hba_state == LPFC_HBA_READY) {
+                               continue;
+                       }
+               } else {
+                       remote_ID = cmd->un.elsreq64.remoteID;
+               }
+
+               list_del(&piocb->list);
+               pring->txcmplq_cnt--;
+
+               cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+               cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+
+               if (piocb->iocb_cmpl) {
+                       (piocb->iocb_cmpl) (phba, piocb, piocb);
+               } else {
+                       mempool_free( piocb, phba->iocb_mem_pool);
+               }
+       }
+       return;
+}
+
+void
+lpfc_els_unsol_event(struct lpfc_hba * phba,
+                    struct lpfc_sli_ring * pring, struct lpfc_iocbq * elsiocb)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_nodelist *ndlp;
+       struct lpfc_dmabuf *mp;
+       uint32_t *lp;
+       IOCB_t *icmd;
+       struct ls_rjt stat;
+       uint32_t cmd;
+       uint32_t did;
+       uint32_t newnode;
+       uint32_t drop_cmd = 0;  /* by default do NOT drop received cmd */
+       uint32_t rjt_err = 0;
+
+       psli = &phba->sli;
+       icmd = &elsiocb->iocb;
+
+       /* type of ELS cmd is first 32bit word in packet */
+       mp = lpfc_sli_ringpostbuf_get(phba, pring, getPaddr(icmd->un.
+                                                           cont64[0].
+                                                           addrHigh,
+                                                           icmd->un.
+                                                           cont64[0].addrLow));
+       if (mp == 0) {
+               drop_cmd = 1;
+               goto dropit;
+       }
+
+       newnode = 0;
+       lp = (uint32_t *) mp->virt;
+       cmd = *lp++;
+       lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], 1, 1);
+
+       if (icmd->ulpStatus) {
+               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+               kfree(mp);
+               drop_cmd = 1;
+               goto dropit;
+       }
+
+       /* Check to see if link went down during discovery */
+       if (lpfc_els_chk_latt(phba)) {
+               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+               kfree(mp);
+               drop_cmd = 1;
+               goto dropit;
+       }
+
+       did = icmd->un.rcvels.remoteID;
+       if ((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) {
+               /* Cannot find existing Fabric ndlp, so allocate a new one */
+               if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC))
+                   == 0) {
+                       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+                       kfree(mp);
+                       drop_cmd = 1;
+                       goto dropit;
+               }
+
+               lpfc_nlp_init(phba, ndlp, did);
+               newnode = 1;
+               if ((did & Fabric_DID_MASK) == Fabric_DID_MASK) {
+                       ndlp->nlp_type |= NLP_FABRIC;
+               }
+       }
+
+       phba->fc_stat.elsRcvFrame++;
+       elsiocb->context1 = ndlp;
+       elsiocb->context2 = mp;
+
+       if ((cmd & ELS_CMD_MASK) == ELS_CMD_RSCN) {
+               cmd &= ELS_CMD_MASK;
+       }
+       /* ELS command <elsCmd> received from NPORT <did> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "%d:0112 ELS command x%x received from NPORT x%x "
+                       "Data: x%x\n", phba->brd_no, cmd, did, phba->hba_state);
+
+       switch (cmd) {
+       case ELS_CMD_PLOGI:
+               phba->fc_stat.elsRcvPLOGI++;
+               if(phba->hba_state < LPFC_DISC_AUTH) {
+                       rjt_err = LSEXP_NOTHING_MORE;
+                       break;
+               }
+               lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PLOGI);
+               break;
+       case ELS_CMD_FLOGI:
+               phba->fc_stat.elsRcvFLOGI++;
+               lpfc_els_rcv_flogi(phba, elsiocb, ndlp, newnode);
+               if (newnode) {
+                       mempool_free( ndlp, phba->nlp_mem_pool);
+               }
+               break;
+       case ELS_CMD_LOGO:
+               phba->fc_stat.elsRcvLOGO++;
+               if(phba->hba_state < LPFC_DISC_AUTH) {
+                       rjt_err = LSEXP_NOTHING_MORE;
+                       break;
+               }
+               lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_LOGO);
+               break;
+       case ELS_CMD_PRLO:
+               phba->fc_stat.elsRcvPRLO++;
+               if(phba->hba_state < LPFC_DISC_AUTH) {
+                       rjt_err = LSEXP_NOTHING_MORE;
+                       break;
+               }
+               lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLO);
+               break;
+       case ELS_CMD_RSCN:
+               phba->fc_stat.elsRcvRSCN++;
+               lpfc_els_rcv_rscn(phba, elsiocb, ndlp, newnode);
+               if (newnode) {
+                       mempool_free( ndlp, phba->nlp_mem_pool);
+               }
+               break;
+       case ELS_CMD_ADISC:
+               phba->fc_stat.elsRcvADISC++;
+               if(phba->hba_state < LPFC_DISC_AUTH) {
+                       rjt_err = LSEXP_NOTHING_MORE;
+                       break;
+               }
+               lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_ADISC);
+               break;
+       case ELS_CMD_PDISC:
+               phba->fc_stat.elsRcvPDISC++;
+               if(phba->hba_state < LPFC_DISC_AUTH) {
+                       rjt_err = LSEXP_NOTHING_MORE;
+                       break;
+               }
+               lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PDISC);
+               break;
+       case ELS_CMD_FARPR:
+               phba->fc_stat.elsRcvFARPR++;
+               lpfc_els_rcv_farpr(phba, elsiocb, ndlp);
+               break;
+       case ELS_CMD_FARP:
+               phba->fc_stat.elsRcvFARP++;
+               lpfc_els_rcv_farp(phba, elsiocb, ndlp);
+               break;
+       case ELS_CMD_FAN:
+               phba->fc_stat.elsRcvFAN++;
+               lpfc_els_rcv_fan(phba, elsiocb, ndlp);
+               break;
+       case ELS_CMD_RRQ:
+               phba->fc_stat.elsRcvRRQ++;
+               lpfc_els_rcv_rrq(phba, elsiocb, ndlp);
+               break;
+       case ELS_CMD_PRLI:
+               phba->fc_stat.elsRcvPRLI++;
+               if(phba->hba_state < LPFC_DISC_AUTH) {
+                       rjt_err = LSEXP_NOTHING_MORE;
+                       break;
+               }
+               lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PRLI);
+               break;
+       case ELS_CMD_RNID:
+               phba->fc_stat.elsRcvRNID++;
+               lpfc_els_rcv_rnid(phba, elsiocb, ndlp);
+               break;
+       default:
+               /* Unsupported ELS command, reject */
+               rjt_err = LSEXP_NOTHING_MORE;
+
+               /* Unknown ELS command <elsCmd> received from NPORT <did> */
+               lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+                               "%d:0115 Unknown ELS command x%x received from "
+                               "NPORT x%x\n", phba->brd_no, cmd, did);
+               if (newnode) {
+                       mempool_free( ndlp, phba->nlp_mem_pool);
+               }
+               break;
+       }
+
+       /* check if need to LS_RJT received ELS cmd */
+       if (rjt_err) {
+               stat.un.b.lsRjtRsvd0 = 0;
+               stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+               stat.un.b.lsRjtRsnCodeExp = rjt_err;
+               stat.un.b.vendorUnique = 0;
+               lpfc_els_rsp_reject(phba, stat.un.lsRjtError, elsiocb, ndlp);
+       }
+
+       if (elsiocb->context2) {
+               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+               kfree(mp);
+       }
+dropit:
+       /* check if need to drop received ELS cmd */
+       if (drop_cmd == 1) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_ELS,
+                               "%d:0111 Dropping received ELS cmd "
+                               "Data: x%x x%x\n", phba->brd_no,
+                               icmd->ulpStatus, icmd->un.ulpWord[4]);
+               phba->fc_stat.elsRcvDrop++;
+       }
+       return;
+}
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
new file mode 100644 (file)
index 0000000..74c4543
--- /dev/null
@@ -0,0 +1,2703 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_hbadisc.c 1.199 2004/11/18 20:19:30EST sf_support Exp  $
+ */
+
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+#include "lpfc_fcp.h"
+#include "lpfc_hw.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_mem.h"
+
+/* AlpaArray for assignment of scsid for scan-down and bind_method */
+uint8_t lpfcAlpaArray[] = {
+       0xEF, 0xE8, 0xE4, 0xE2, 0xE1, 0xE0, 0xDC, 0xDA, 0xD9, 0xD6,
+       0xD5, 0xD4, 0xD3, 0xD2, 0xD1, 0xCE, 0xCD, 0xCC, 0xCB, 0xCA,
+       0xC9, 0xC7, 0xC6, 0xC5, 0xC3, 0xBC, 0xBA, 0xB9, 0xB6, 0xB5,
+       0xB4, 0xB3, 0xB2, 0xB1, 0xAE, 0xAD, 0xAC, 0xAB, 0xAA, 0xA9,
+       0xA7, 0xA6, 0xA5, 0xA3, 0x9F, 0x9E, 0x9D, 0x9B, 0x98, 0x97,
+       0x90, 0x8F, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7C, 0x7A, 0x79,
+       0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6E, 0x6D, 0x6C, 0x6B,
+       0x6A, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5C, 0x5A, 0x59, 0x56,
+       0x55, 0x54, 0x53, 0x52, 0x51, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A,
+       0x49, 0x47, 0x46, 0x45, 0x43, 0x3C, 0x3A, 0x39, 0x36, 0x35,
+       0x34, 0x33, 0x32, 0x31, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29,
+       0x27, 0x26, 0x25, 0x23, 0x1F, 0x1E, 0x1D, 0x1B, 0x18, 0x17,
+       0x10, 0x0F, 0x08, 0x04, 0x02, 0x01
+};
+
+static void
+lpfc_evt_iocb_free(struct lpfc_hba * phba, struct lpfc_iocbq * saveq)
+{
+       struct lpfc_iocbq  *rspiocbp, *tmpiocbp;
+
+       /* Free up iocb buffer chain for cmd just processed */
+       list_for_each_entry_safe(rspiocbp, tmpiocbp,
+               &saveq->list, list) {
+               list_del(&rspiocbp->list);
+               mempool_free( rspiocbp, phba->iocb_mem_pool);
+       }
+       mempool_free( saveq, phba->iocb_mem_pool);
+}
+
+void
+lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
+{
+       struct lpfc_target *targetp;
+       int scsid;
+
+       if (!(ndlp->nlp_type & NLP_FABRIC)) {
+               /* Nodev timeout on NPort <nlp_DID> */
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+                       "%d:0203 Nodev timeout on NPort x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag,
+                       ndlp->nlp_state, ndlp->nlp_rpi);
+       }
+
+       ndlp->nlp_flag &= ~NLP_NODEV_TMO;
+
+       for(scsid=0;scsid<MAX_FCP_TARGET;scsid++) {
+               targetp = phba->device_queue_hash[scsid];
+               /* First see if the SCSI ID has an allocated struct
+                  lpfc_target */
+               if (targetp) {
+                       if (targetp->pnode == ndlp) {
+                               /* flush the target */
+                               lpfc_sli_abort_iocb_tgt(phba,
+                                       &phba->sli.ring[phba->sli.fcp_ring],
+                                       scsid, LPFC_ABORT_ALLQ);
+                       }
+               }
+       }
+
+       lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RM);
+       return;
+}
+
+static void
+lpfc_disc_done(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       LPFC_DISC_EVT_t  *evtp, *next_evtp;
+       LPFC_MBOXQ_t *pmb;
+       struct lpfc_iocbq  *cmdiocbp, *saveq;
+       struct lpfc_nodelist  *ndlp;
+       LPFC_RING_MASK_t *func;
+       struct Scsi_Host *shost;
+       LIST_HEAD(local_dpc_disc);
+
+       list_splice_init(&phba->dpc_disc, &local_dpc_disc);
+
+       /* check discovery event list */
+       list_for_each_entry_safe(evtp, next_evtp, &local_dpc_disc, evt_listp) {
+               list_del(&evtp->evt_listp);
+
+               switch(evtp->evt) {
+               case LPFC_EVT_MBOX:
+                       pmb = (LPFC_MBOXQ_t *)(evtp->evt_arg1);
+                       (pmb->mbox_cmpl) (phba, pmb);
+                       break;
+               case LPFC_EVT_SOL_IOCB:
+                       cmdiocbp = (struct lpfc_iocbq *)(evtp->evt_arg1);
+                       saveq = (struct lpfc_iocbq *)(evtp->evt_arg2);
+                       (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq);
+                       lpfc_evt_iocb_free(phba, saveq);
+                       break;
+               case LPFC_EVT_UNSOL_IOCB:
+                       func = (LPFC_RING_MASK_t *)(evtp->evt_arg1);
+                       saveq = (struct lpfc_iocbq *)(evtp->evt_arg2);
+                       (func->lpfc_sli_rcv_unsol_event) (phba,
+                       &psli->ring[LPFC_ELS_RING], saveq);
+                       lpfc_evt_iocb_free(phba, saveq);
+                       break;
+               case LPFC_EVT_NODEV_TMO:
+                       ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1);
+                       lpfc_process_nodev_timeout(phba, ndlp);
+                       break;
+               case LPFC_EVT_SCAN:
+                       /* SCSI HOTPLUG supported */
+                       shost = phba->host;
+#ifdef USE_SCAN_TARGET
+                       {
+                       struct lpfc_target   *targetp;
+
+                       targetp = (struct lpfc_target *)(evtp->evt_arg1);
+                       lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP,
+                       "%d:0251 Rescanning scsi target %d\n",
+                       phba->brd_no, targetp->scsi_id);
+
+                       if(targetp ==
+                          phba->device_queue_hash[targetp->scsi_id]) {
+                               spin_unlock_irq(phba->host->host_lock);
+                               scsi_scan_single_target(shost, 0,
+                                       targetp->scsi_id);
+                               spin_lock_irq(phba->host->host_lock);
+                       }
+                       }
+#else
+                       lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY | LOG_FCP,
+                       "%d:0251 Rescanning scsi host\n", phba->brd_no);
+                       spin_unlock_irq(shost->host_lock);
+                       scsi_scan_host(shost);
+                       spin_lock_irq(shost->host_lock);
+#endif
+                       break;
+               }
+               kfree(evtp);
+       }
+}
+
+int
+lpfc_do_dpc(void *p)
+{
+       unsigned long flags;
+       DECLARE_MUTEX_LOCKED(sem);
+       struct lpfc_hba *phba = (struct lpfc_hba *)p;
+
+       lock_kernel();
+
+       daemonize("lpfc_dpc_%d", phba->brd_no);
+       allow_signal(SIGHUP);
+
+       phba->dpc_wait = &sem;
+       set_user_nice(current, -20);
+
+       unlock_kernel();
+
+       complete(&phba->dpc_startup);
+
+       while (1) {
+               if (down_interruptible(&sem))
+                       break;
+
+               if (signal_pending(current))
+                       break;
+
+               if (phba->dpc_kill)
+                       break;
+
+               spin_lock_irqsave(phba->host->host_lock, flags);
+               lpfc_disc_done(phba);
+               spin_unlock_irqrestore(phba->host->host_lock, flags);
+       }
+
+       /* Zero out semaphore we were waiting on. */
+       phba->dpc_wait = NULL;
+       complete_and_exit(&phba->dpc_exiting, 0);
+       return(0);
+}
+
+/*
+ * This is only called to handle FC discovery events. Since this a rare
+ * occurance, we allocate an LPFC_DISC_EVT_t structure here instead of
+ * embedding it in the IOCB.
+ */
+int
+lpfc_discq_post_event(struct lpfc_hba * phba, void *arg1, void *arg2,
+                     uint32_t evt)
+{
+       LPFC_DISC_EVT_t  *evtp;
+
+       /* All Mailbox completions and LPFC_ELS_RING rcv ring IOCB events
+        * will be queued to DPC for processing
+        */
+       evtp = (LPFC_DISC_EVT_t *) kmalloc(sizeof(LPFC_DISC_EVT_t), GFP_ATOMIC);
+       if (!evtp)
+               return 0;
+
+       evtp->evt_arg1  = arg1;
+       evtp->evt_arg2  = arg2;
+       evtp->evt       = evt;
+       evtp->evt_listp.next = NULL;
+       evtp->evt_listp.prev = NULL;
+
+       /* Queue the event to the DPC to be processed later */
+       list_add_tail(&evtp->evt_listp, &phba->dpc_disc);
+       if (phba->dpc_wait)
+               up(phba->dpc_wait);
+
+       return 1;
+}
+
+int
+lpfc_linkdown(struct lpfc_hba * phba)
+{
+       struct lpfc_sli       *psli;
+       struct lpfc_nodelist  *ndlp, *next_ndlp;
+       struct list_head *listp;
+       struct list_head *node_list[7];
+       LPFC_MBOXQ_t     *mb;
+       int               rc, i;
+
+       psli = &phba->sli;
+       phba->hba_state = LPFC_LINK_DOWN;
+
+#if !defined(FC_TRANS_VER1) && !defined(FC_TRANS_265_BLKPATCH)
+       /* Stop all requests to the driver from the midlayer. */
+       scsi_block_requests(phba->host);
+#endif
+
+       lpfc_put_event(phba, HBA_EVENT_LINK_DOWN, phba->fc_myDID, NULL, 0, 0);
+
+       /* Clean up any firmware default rpi's */
+       if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) {
+               lpfc_unreg_did(phba, 0xffffffff, mb);
+               if (lpfc_sli_issue_mbox(phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB))
+                   == MBX_NOT_FINISHED) {
+                       mempool_free( mb, phba->mbox_mem_pool);
+               }
+       }
+
+       /* Cleanup any outstanding RSCN activity */
+       lpfc_els_flush_rscn(phba);
+
+       /* Cleanup any outstanding ELS commands */
+       lpfc_els_flush_cmd(phba);
+
+       /*
+        * If this function was called by the lpfc_do_dpc, don't recurse into
+        * the routine again.  If not, just process any outstanding
+        * discovery events.
+        */
+       if (!list_empty(&phba->dpc_disc)) {
+               lpfc_disc_done(phba);
+       }
+
+       /* Issue a LINK DOWN event to all nodes */
+       node_list[0] = &phba->fc_npr_list;  /* MUST do this list first */
+       node_list[1] = &phba->fc_nlpmap_list;
+       node_list[2] = &phba->fc_nlpunmap_list;
+       node_list[3] = &phba->fc_prli_list;
+       node_list[4] = &phba->fc_reglogin_list;
+       node_list[5] = &phba->fc_adisc_list;
+       node_list[6] = &phba->fc_plogi_list;
+       for (i = 0; i < 7; i++) {
+               listp = node_list[i];
+               if (list_empty(listp))
+                       continue;
+
+               list_for_each_entry_safe(ndlp, next_ndlp, listp, nlp_listp) {
+                       /* Fabric nodes are not handled thru state machine for
+                          link down */
+                       if (ndlp->nlp_type & NLP_FABRIC) {
+                               /* Remove ALL Fabric nodes except Fabric_DID */
+                               if (ndlp->nlp_DID != Fabric_DID) {
+                                       /* Take it off current list and free */
+                                       lpfc_nlp_list(phba, ndlp,
+                                               NLP_NO_LIST);
+                               }
+                       }
+                       else {
+                               lpfc_set_failmask(phba, ndlp,
+                                                 LPFC_DEV_LINK_DOWN,
+                                                 LPFC_SET_BITMASK);
+
+                               rc = lpfc_disc_state_machine(phba, ndlp, NULL,
+                                                    NLP_EVT_DEVICE_RECOVERY);
+                       }
+               }
+       }
+
+       /* free any ndlp's on unused list */
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list,
+                               nlp_listp) {
+               lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       }
+
+       /* Setup myDID for link up if we are in pt2pt mode */
+       if (phba->fc_flag & FC_PT2PT) {
+               phba->fc_myDID = 0;
+               if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) {
+                       lpfc_config_link(phba, mb);
+                       if (lpfc_sli_issue_mbox
+                           (phba, mb, (MBX_NOWAIT | MBX_STOP_IOCB))
+                           == MBX_NOT_FINISHED) {
+                               mempool_free( mb, phba->mbox_mem_pool);
+                       }
+               }
+               phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI);
+       }
+       phba->fc_flag &= ~FC_LBIT;
+
+       /* Turn off discovery timer if its running */
+       lpfc_can_disctmo(phba);
+
+       /* Must process IOCBs on all rings to handle ABORTed I/Os */
+       return (0);
+}
+
+static int
+lpfc_linkup(struct lpfc_hba * phba)
+{
+       struct lpfc_nodelist *ndlp, *next_ndlp;
+       struct list_head *listp;
+       struct list_head *node_list[7];
+       int i;
+
+       phba->hba_state = LPFC_LINK_UP;
+       phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY |
+                          FC_RSCN_MODE | FC_NLP_MORE | FC_RSCN_DISCOVERY);
+       phba->fc_ns_retry = 0;
+
+
+       lpfc_put_event(phba, HBA_EVENT_LINK_UP, phba->fc_myDID,
+                       (void *)(unsigned long)(phba->fc_topology),
+                       0, phba->fc_linkspeed);
+
+       /*
+        * Clean up old Fabric NLP_FABRIC logins.
+        */
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list,
+                               nlp_listp) {
+               if (ndlp->nlp_DID == Fabric_DID) {
+                       /* Take it off current list and free */
+                       lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+               }
+       }
+
+       /* free any ndlp's on unused list */
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list,
+                               nlp_listp) {
+               lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       }
+
+       /* Mark all nodes for LINK UP */
+       node_list[0] = &phba->fc_plogi_list;
+       node_list[1] = &phba->fc_adisc_list;
+       node_list[2] = &phba->fc_reglogin_list;
+       node_list[3] = &phba->fc_prli_list;
+       node_list[4] = &phba->fc_nlpunmap_list;
+       node_list[5] = &phba->fc_nlpmap_list;
+       node_list[6] = &phba->fc_npr_list;
+       for (i = 0; i < 7; i++) {
+               listp = node_list[i];
+               if (list_empty(listp))
+                       continue;
+
+               list_for_each_entry(ndlp, listp, nlp_listp) {
+                       lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCOVERY_INP,
+                                         LPFC_SET_BITMASK);
+                       lpfc_set_failmask(phba, ndlp, LPFC_DEV_LINK_DOWN,
+                                         LPFC_CLR_BITMASK);
+               }
+       }
+
+#if !defined(FC_TRANS_VER1) && !defined(FC_TRANS_265_BLKPATCH)
+       spin_unlock_irq(phba->host->host_lock);
+       scsi_unblock_requests(phba->host);
+       spin_lock_irq(phba->host->host_lock);
+#endif
+       return 0;
+}
+
+/*
+ * This routine handles processing a CLEAR_LA mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       struct lpfc_sli *psli;
+       MAILBOX_t *mb;
+       uint32_t control;
+
+       psli = &phba->sli;
+       mb = &pmb->mb;
+       /* Since we don't do discovery right now, turn these off here */
+       psli->ring[psli->ip_ring].flag &= ~LPFC_STOP_IOCB_EVENT;
+       psli->ring[psli->fcp_ring].flag &= ~LPFC_STOP_IOCB_EVENT;
+       psli->ring[psli->next_ring].flag &= ~LPFC_STOP_IOCB_EVENT;
+       /* Check for error */
+       if ((mb->mbxStatus) && (mb->mbxStatus != 0x1601)) {
+               /* CLEAR_LA mbox error <mbxStatus> state <hba_state> */
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                               "%d:0320 CLEAR_LA mbxStatus error x%x hba "
+                               "state x%x\n",
+                               phba->brd_no, mb->mbxStatus, phba->hba_state);
+
+               phba->hba_state = LPFC_HBA_ERROR;
+               goto out;
+       }
+
+       if(phba->fc_flag & FC_ABORT_DISCOVERY)
+               goto out;
+
+       phba->num_disc_nodes = 0;
+       /* go thru NPR list and issue ELS PLOGIs */
+       if (phba->fc_npr_cnt) {
+               lpfc_els_disc_plogi(phba);
+       }
+
+       phba->hba_state = LPFC_HBA_READY;
+
+out:
+       phba->fc_flag &= ~FC_ABORT_DISCOVERY;
+       /* Device Discovery completes */
+       lpfc_printf_log(phba,
+                        KERN_INFO,
+                        LOG_DISCOVERY,
+                        "%d:0225 Device Discovery completes\n",
+                        phba->brd_no);
+
+       mempool_free( pmb, phba->mbox_mem_pool);
+       if (phba->fc_flag & FC_ESTABLISH_LINK) {
+               phba->fc_flag &= ~FC_ESTABLISH_LINK;
+       }
+       del_timer_sync(&phba->fc_estabtmo);
+       lpfc_can_disctmo(phba);
+
+       /* turn on Link Attention interrupts */
+       psli->sliinit.sli_flag |= LPFC_PROCESS_LA;
+       control = readl(phba->HCregaddr);
+       control |= HC_LAINT_ENA;
+       writel(control, phba->HCregaddr);
+       readl(phba->HCregaddr); /* flush */
+
+       return;
+}
+
+static void
+lpfc_mbx_cmpl_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       struct lpfc_sli *psli;
+       MAILBOX_t *mb;
+
+       psli = &phba->sli;
+       mb = &pmb->mb;
+       /* Check for error */
+       if (mb->mbxStatus) {
+               /* CONFIG_LINK mbox error <mbxStatus> state <hba_state> */
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                               "%d:0306 CONFIG_LINK mbxStatus error x%x "
+                               "HBA state x%x\n",
+                               phba->brd_no, mb->mbxStatus, phba->hba_state);
+
+               lpfc_linkdown(phba);
+               phba->hba_state = LPFC_HBA_ERROR;
+               goto out;
+       }
+
+       if (phba->hba_state == LPFC_LOCAL_CFG_LINK) {
+               if (phba->fc_topology == TOPOLOGY_LOOP) {
+                       /* If we are public loop and L bit was set */
+                       if ((phba->fc_flag & FC_PUBLIC_LOOP) &&
+                           !(phba->fc_flag & FC_LBIT)) {
+                               /* Need to wait for FAN - use discovery timer
+                                * for timeout.  hba_state is identically
+                                * LPFC_LOCAL_CFG_LINK while waiting for FAN
+                                */
+                               lpfc_set_disctmo(phba);
+                               mempool_free( pmb, phba->mbox_mem_pool);
+                               return;
+                       }
+               }
+
+               /* Start discovery by sending a FLOGI hba_state is identically
+                * LPFC_FLOGI while waiting for FLOGI cmpl
+                */
+               phba->hba_state = LPFC_FLOGI;
+               lpfc_set_disctmo(phba);
+               lpfc_initial_flogi(phba);
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return;
+       }
+       if (phba->hba_state == LPFC_FABRIC_CFG_LINK) {
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return;
+       }
+
+out:
+       /* CONFIG_LINK bad hba state <hba_state> */
+       lpfc_printf_log(phba,
+                       KERN_ERR,
+                       LOG_DISCOVERY,
+                       "%d:0200 CONFIG_LINK bad hba state x%x\n",
+                       phba->brd_no, phba->hba_state);
+
+       if (phba->hba_state != LPFC_CLEAR_LA) {
+               lpfc_clear_la(phba, pmb);
+               pmb->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+               if (lpfc_sli_issue_mbox(phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB))
+                   == MBX_NOT_FINISHED) {
+                       mempool_free( pmb, phba->mbox_mem_pool);
+                       lpfc_disc_flush_list(phba);
+                       psli->ring[(psli->ip_ring)].flag &=
+                               ~LPFC_STOP_IOCB_EVENT;
+                       psli->ring[(psli->fcp_ring)].flag &=
+                               ~LPFC_STOP_IOCB_EVENT;
+                       psli->ring[(psli->next_ring)].flag &=
+                               ~LPFC_STOP_IOCB_EVENT;
+                       phba->hba_state = LPFC_HBA_READY;
+               }
+       } else {
+               mempool_free( pmb, phba->mbox_mem_pool);
+       }
+       return;
+}
+
+static void
+lpfc_mbx_cmpl_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       MAILBOX_t *mb = &pmb->mb;
+       struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) pmb->context1;
+
+
+       /* Check for error */
+       if (mb->mbxStatus) {
+               /* READ_SPARAM mbox error <mbxStatus> state <hba_state> */
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                               "%d:0319 READ_SPARAM mbxStatus error x%x "
+                               "hba state x%x>\n",
+                               phba->brd_no, mb->mbxStatus, phba->hba_state);
+
+               lpfc_linkdown(phba);
+               phba->hba_state = LPFC_HBA_ERROR;
+               goto out;
+       }
+
+       /* The mailbox was populated by the HBA.  Flush it to main store for the
+        * driver.  Note that all context buffers are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE,
+                       PCI_DMA_FROMDEVICE);
+
+       memcpy((uint8_t *) & phba->fc_sparam, (uint8_t *) mp->virt,
+              sizeof (struct serv_parm));
+       memcpy((uint8_t *) & phba->fc_nodename,
+              (uint8_t *) & phba->fc_sparam.nodeName,
+              sizeof (struct lpfc_name));
+       memcpy((uint8_t *) & phba->fc_portname,
+              (uint8_t *) & phba->fc_sparam.portName,
+              sizeof (struct lpfc_name));
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       mempool_free( pmb, phba->mbox_mem_pool);
+       return;
+
+out:
+       pmb->context1 = NULL;
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       if (phba->hba_state != LPFC_CLEAR_LA) {
+               lpfc_clear_la(phba, pmb);
+               pmb->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+               if (lpfc_sli_issue_mbox(phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB))
+                   == MBX_NOT_FINISHED) {
+                       mempool_free( pmb, phba->mbox_mem_pool);
+                       lpfc_disc_flush_list(phba);
+                       psli->ring[(psli->ip_ring)].flag &=
+                           ~LPFC_STOP_IOCB_EVENT;
+                       psli->ring[(psli->fcp_ring)].flag &=
+                           ~LPFC_STOP_IOCB_EVENT;
+                       psli->ring[(psli->next_ring)].flag &=
+                           ~LPFC_STOP_IOCB_EVENT;
+                       phba->hba_state = LPFC_HBA_READY;
+               }
+       } else {
+               mempool_free( pmb, phba->mbox_mem_pool);
+       }
+       return;
+}
+
+/*
+ * This routine handles processing a READ_LA mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       READ_LA_VAR *la;
+       LPFC_MBOXQ_t *mbox;
+       MAILBOX_t *mb = &pmb->mb;
+       struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
+       uint32_t control;
+       int i;
+
+       /* Check for error */
+       if (mb->mbxStatus) {
+               /* READ_LA mbox error <mbxStatus> state <hba_state> */
+               lpfc_printf_log(phba,
+                               KERN_INFO,
+                               LOG_LINK_EVENT,
+                               "%d:1307 READ_LA mbox error x%x state x%x\n",
+                               phba->brd_no,
+                               mb->mbxStatus, phba->hba_state);
+
+               lpfc_linkdown(phba);
+               phba->hba_state = LPFC_HBA_ERROR;
+
+               /* turn on Link Attention interrupts */
+               psli->sliinit.sli_flag |= LPFC_PROCESS_LA;
+               control = readl(phba->HCregaddr);
+               control |= HC_LAINT_ENA;
+               writel(control, phba->HCregaddr);
+               readl(phba->HCregaddr); /* flush */
+               return;
+       }
+       la = (READ_LA_VAR *) & pmb->mb.un.varReadLA;
+
+
+       /* The mailbox was populated by the HBA.  Flush it to main store for the
+        * driver.  Note that all context buffers are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE,
+                                   PCI_DMA_FROMDEVICE);
+
+       /* Get Loop Map information */
+       if (mp) {
+               memcpy(&phba->alpa_map[0], mp->virt, 128);
+       } else {
+               memset(&phba->alpa_map[0], 0, 128);
+       }
+
+       if (((phba->fc_eventTag + 1) < la->eventTag) ||
+           (phba->fc_eventTag == la->eventTag)) {
+               phba->fc_stat.LinkMultiEvent++;
+               if (la->attType == AT_LINK_UP) {
+                       if (phba->fc_eventTag != 0) {
+
+                               lpfc_linkdown(phba);
+                       }
+               }
+       }
+
+       phba->fc_eventTag = la->eventTag;
+
+       if (la->attType == AT_LINK_UP) {
+               phba->fc_stat.LinkUp++;
+               /* Link Up Event <eventTag> received */
+               lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
+                               "%d:1303 Link Up Event x%x received "
+                               "Data: x%x x%x x%x x%x\n",
+                               phba->brd_no, la->eventTag, phba->fc_eventTag,
+                               la->granted_AL_PA, la->UlnkSpeed,
+                               phba->alpa_map[0]);
+
+               switch(la->UlnkSpeed) {
+                       case LA_1GHZ_LINK:
+                               phba->fc_linkspeed = LA_1GHZ_LINK;
+                       break;
+                       case LA_2GHZ_LINK:
+                               phba->fc_linkspeed = LA_2GHZ_LINK;
+                       break;
+                       case LA_4GHZ_LINK:
+                               phba->fc_linkspeed = LA_4GHZ_LINK;
+                       break;
+                       default:
+                               phba->fc_linkspeed = LA_UNKNW_LINK;
+                       break;
+               }
+
+               if ((phba->fc_topology = la->topology) == TOPOLOGY_LOOP) {
+
+                       if (la->il) {
+                               phba->fc_flag |= FC_LBIT;
+                       }
+
+                       phba->fc_myDID = la->granted_AL_PA;
+
+                       i = la->un.lilpBde64.tus.f.bdeSize;
+                       if (i == 0) {
+                               phba->alpa_map[0] = 0;
+                       } else {
+                               if (phba->cfg_log_verbose
+                                   & LOG_LINK_EVENT) {
+                                       int numalpa, j, k;
+                                       union {
+                                               uint8_t pamap[16];
+                                               struct {
+                                                       uint32_t wd1;
+                                                       uint32_t wd2;
+                                                       uint32_t wd3;
+                                                       uint32_t wd4;
+                                               } pa;
+                                       } un;
+
+                                       numalpa = phba->alpa_map[0];
+                                       j = 0;
+                                       while (j < numalpa) {
+                                               memset(un.pamap, 0, 16);
+                                               for (k = 1; j < numalpa; k++) {
+                                                       un.pamap[k - 1] =
+                                                           phba->alpa_map[j +
+                                                                          1];
+                                                       j++;
+                                                       if (k == 16)
+                                                               break;
+                                               }
+                                               /* Link Up Event ALPA map */
+                                               lpfc_printf_log(phba,
+                                                       KERN_WARNING,
+                                                       LOG_LINK_EVENT,
+                                                       "%d:1304 Link Up Event "
+                                                       "ALPA map Data: x%x "
+                                                       "x%x x%x x%x\n",
+                                                       phba->brd_no,
+                                                       un.pa.wd1, un.pa.wd2,
+                                                       un.pa.wd3, un.pa.wd4);
+                                       }
+                               }
+                       }
+               } else {
+                       phba->fc_myDID = phba->fc_pref_DID;
+                       phba->fc_flag |= FC_LBIT;
+               }
+
+               lpfc_linkup(phba);
+               if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) {
+                       lpfc_read_sparam(phba, mbox);
+                       mbox->mbox_cmpl = lpfc_mbx_cmpl_read_sparam;
+                       lpfc_sli_issue_mbox
+                           (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB));
+               }
+
+               if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) {
+                       phba->hba_state = LPFC_LOCAL_CFG_LINK;
+                       lpfc_config_link(phba, mbox);
+                       mbox->mbox_cmpl = lpfc_mbx_cmpl_config_link;
+                       lpfc_sli_issue_mbox
+                           (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB));
+               }
+       } else {
+               phba->fc_stat.LinkDown++;
+               /* Link Down Event <eventTag> received */
+               lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
+                               "%d:1305 Link Down Event x%x received "
+                               "Data: x%x x%x x%x\n",
+                               phba->brd_no, la->eventTag, phba->fc_eventTag,
+                               phba->hba_state, phba->fc_flag);
+
+               lpfc_linkdown(phba);
+
+               /* turn on Link Attention interrupts - no CLEAR_LA needed */
+               psli->sliinit.sli_flag |= LPFC_PROCESS_LA;
+               control = readl(phba->HCregaddr);
+               control |= HC_LAINT_ENA;
+               writel(control, phba->HCregaddr);
+               readl(phba->HCregaddr); /* flush */
+       }
+
+       pmb->context1 = NULL;
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       mempool_free( pmb, phba->mbox_mem_pool);
+       return;
+}
+
+/*
+ * This routine handles processing a REG_LOGIN mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       struct lpfc_sli *psli;
+       MAILBOX_t *mb;
+       struct lpfc_dmabuf *mp;
+       struct lpfc_nodelist *ndlp;
+
+       psli = &phba->sli;
+       mb = &pmb->mb;
+
+       ndlp = (struct lpfc_nodelist *) pmb->context2;
+       mp = (struct lpfc_dmabuf *) (pmb->context1);
+
+       /* The mailbox was populated by the HBA.  Flush it to main store for the
+        * driver.  Note that all context buffers are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE,
+                       PCI_DMA_FROMDEVICE);
+
+       pmb->context1 = NULL;
+
+       /* Good status, call state machine */
+       lpfc_disc_state_machine(phba, ndlp, pmb, NLP_EVT_CMPL_REG_LOGIN);
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       mempool_free( pmb, phba->mbox_mem_pool);
+
+       return;
+}
+
+/*
+ * This routine handles processing a Fabric REG_LOGIN mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       struct lpfc_sli *psli;
+       MAILBOX_t *mb;
+       struct lpfc_dmabuf *mp;
+       struct lpfc_nodelist *ndlp;
+       struct lpfc_nodelist *ndlp_fdmi;
+
+
+       psli = &phba->sli;
+       mb = &pmb->mb;
+
+       ndlp = (struct lpfc_nodelist *) pmb->context2;
+       mp = (struct lpfc_dmabuf *) (pmb->context1);
+
+       if (mb->mbxStatus) {
+               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+               kfree(mp);
+               mempool_free( pmb, phba->mbox_mem_pool);
+               mempool_free( ndlp, phba->nlp_mem_pool);
+
+               /* FLOGI failed, so just use loop map to make discovery list */
+               lpfc_disc_list_loopmap(phba);
+
+               /* Start discovery */
+               lpfc_disc_start(phba);
+               return;
+       }
+
+       /* The mailbox was populated by the HBA.  Flush it to main store for the
+        * driver.  Note that all context buffers are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE,
+                       PCI_DMA_FROMDEVICE);
+
+       pmb->context1 = NULL;
+
+       if (ndlp->nlp_rpi != 0)
+               lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+       ndlp->nlp_rpi = mb->un.varWords[0];
+       lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi);
+       ndlp->nlp_type |= NLP_FABRIC;
+       ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+
+       if (phba->hba_state == LPFC_FABRIC_CFG_LINK) {
+               /* This NPort has been assigned an NPort_ID by the fabric as a
+                * result of the completed fabric login.  Issue a State Change
+                * Registration (SCR) ELS request to the fabric controller
+                * (SCR_DID) so that this NPort gets RSCN events from the
+                * fabric.
+                */
+               lpfc_issue_els_scr(phba, SCR_DID, 0);
+
+               /* Allocate a new node instance.  If the pool is empty, just
+                * start the discovery process and skip the Nameserver login
+                * process.  This is attempted again later on.  Otherwise, issue
+                * a Port Login (PLOGI) to the NameServer
+                */
+               if ((ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC))
+                   == 0) {
+                       lpfc_disc_start(phba);
+               } else {
+                       lpfc_nlp_init(phba, ndlp, NameServer_DID);
+                       ndlp->nlp_type |= NLP_FABRIC;
+                       ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+                       lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+                       lpfc_issue_els_plogi(phba, ndlp, 0);
+                       if (phba->cfg_fdmi_on) {
+                               if ((ndlp_fdmi = mempool_alloc(
+                                                      phba->nlp_mem_pool,
+                                                      GFP_ATOMIC))) {
+                                       lpfc_nlp_init(phba, ndlp_fdmi,
+                                               FDMI_DID);
+                                       ndlp_fdmi->nlp_type |= NLP_FABRIC;
+                                       ndlp_fdmi->nlp_state =
+                                           NLP_STE_PLOGI_ISSUE;
+                                       lpfc_issue_els_plogi(phba, ndlp_fdmi,
+                                                            0);
+                               }
+                       }
+               }
+       }
+
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       mempool_free( pmb, phba->mbox_mem_pool);
+
+       return;
+}
+
+/*
+ * This routine handles processing a NameServer REG_LOGIN mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       struct lpfc_sli *psli;
+       MAILBOX_t *mb;
+       struct lpfc_dmabuf *mp;
+       struct lpfc_nodelist *ndlp;
+
+       psli = &phba->sli;
+       mb = &pmb->mb;
+
+       ndlp = (struct lpfc_nodelist *) pmb->context2;
+       mp = (struct lpfc_dmabuf *) (pmb->context1);
+
+       if (mb->mbxStatus) {
+               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+               kfree(mp);
+               mempool_free( pmb, phba->mbox_mem_pool);
+               lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+
+               /* RegLogin failed, so just use loop map to make discovery
+                  list */
+               lpfc_disc_list_loopmap(phba);
+
+               /* Start discovery */
+               lpfc_disc_start(phba);
+               return;
+       }
+
+       /* The mailbox was populated by the HBA.  Flush it to main store for the
+        * driver.  Note that all context buffers are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE,
+                       PCI_DMA_FROMDEVICE);
+
+       pmb->context1 = NULL;
+
+       if (ndlp->nlp_rpi != 0)
+               lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+       ndlp->nlp_rpi = mb->un.varWords[0];
+       lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi);
+       ndlp->nlp_type |= NLP_FABRIC;
+       ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+
+       if (phba->hba_state < LPFC_HBA_READY) {
+               /* Link up discovery requires Fabrib registration. */
+               lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RNN_ID);
+               lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RSNN_NN);
+               lpfc_ns_cmd(phba, ndlp, SLI_CTNS_RFT_ID);
+       }
+
+       phba->fc_ns_retry = 0;
+       /* Good status, issue CT Request to NameServer */
+       if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT)) {
+               /* Cannot issue NameServer Query, so finish up discovery */
+               lpfc_disc_start(phba);
+       }
+
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       mempool_free( pmb, phba->mbox_mem_pool);
+
+       return;
+}
+
+/* Put blp on the bind list */
+int
+lpfc_consistent_bind_save(struct lpfc_hba * phba, struct lpfc_bindlist * blp)
+{
+       /* Put it at the end of the bind list */
+       list_add_tail(&blp->nlp_listp, &phba->fc_nlpbind_list);
+       phba->fc_bind_cnt++;
+
+       /* Add scsiid <sid> to BIND list */
+       lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                       "%d:0903 Add scsiid %d to BIND list "
+                       "Data: x%x x%x x%x x%p\n",
+                       phba->brd_no, blp->nlp_sid, phba->fc_bind_cnt,
+                       blp->nlp_DID, blp->nlp_bind_type, blp);
+
+       return (0);
+}
+
+int
+lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list)
+{
+       struct lpfc_bindlist *blp;
+       struct lpfc_target   *targetp;
+       struct lpfc_sli      *psli;
+       psli = &phba->sli;
+
+       /* Sanity check to ensure we are not moving to / from the same list */
+       if((nlp->nlp_flag & NLP_LIST_MASK) == list) {
+               if(list != NLP_NO_LIST)
+                       return(0);
+       }
+
+       blp = nlp->nlp_listp_bind;
+
+       switch(nlp->nlp_flag & NLP_LIST_MASK) {
+       case NLP_NO_LIST: /* Not on any list */
+               break;
+       case NLP_UNUSED_LIST:
+               phba->fc_unused_cnt--;
+               list_del(&nlp->nlp_listp);
+               nlp->nlp_flag &= ~NLP_LIST_MASK;
+               break;
+       case NLP_PLOGI_LIST:
+               phba->fc_plogi_cnt--;
+               list_del(&nlp->nlp_listp);
+               nlp->nlp_flag &= ~NLP_LIST_MASK;
+               break;
+       case NLP_ADISC_LIST:
+               phba->fc_adisc_cnt--;
+               list_del(&nlp->nlp_listp);
+               nlp->nlp_flag &= ~NLP_LIST_MASK;
+               break;
+       case NLP_REGLOGIN_LIST:
+               phba->fc_reglogin_cnt--;
+               list_del(&nlp->nlp_listp);
+               nlp->nlp_flag &= ~NLP_LIST_MASK;
+               break;
+       case NLP_PRLI_LIST:
+               phba->fc_prli_cnt--;
+               list_del(&nlp->nlp_listp);
+               nlp->nlp_flag &= ~NLP_LIST_MASK;
+               break;
+       case NLP_UNMAPPED_LIST:
+               phba->fc_unmap_cnt--;
+               list_del(&nlp->nlp_listp);
+               nlp->nlp_flag &= ~NLP_LIST_MASK;
+               nlp->nlp_flag &= ~NLP_TGT_NO_SCSIID;
+               nlp->nlp_type &= ~NLP_FC_NODE;
+               phba->nport_event_cnt++;
+               break;
+       case NLP_MAPPED_LIST:
+               phba->fc_map_cnt--;
+               list_del(&nlp->nlp_listp);
+               nlp->nlp_flag &= ~NLP_LIST_MASK;
+               phba->nport_event_cnt++;
+               lpfc_set_failmask(phba, nlp, LPFC_DEV_DISAPPEARED,
+                         LPFC_SET_BITMASK);
+               nlp->nlp_type &= ~NLP_FCP_TARGET;
+               targetp = nlp->nlp_Target;
+               if (targetp && (list != NLP_MAPPED_LIST)) {
+                       nlp->nlp_Target = NULL;
+#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH)
+                       /*
+                        * Do not block the target if the driver has just reset
+                        * its interface to the hardware.
+                        */
+                       if (phba->hba_state != LPFC_INIT_START)
+                               lpfc_target_block(phba, targetp);
+#endif
+               }
+
+               break;
+       case NLP_NPR_LIST:
+               phba->fc_npr_cnt--;
+               list_del(&nlp->nlp_listp);
+               nlp->nlp_flag &= ~NLP_LIST_MASK;
+               /* Stop delay tmo if taking node off NPR list */
+               if ((nlp->nlp_flag & NLP_DELAY_TMO) &&
+                  (list != NLP_NPR_LIST)) {
+                       nlp->nlp_flag &= ~NLP_DELAY_TMO;
+                       del_timer_sync(&nlp->nlp_delayfunc);
+               }
+               break;
+       }
+
+       /* Add NPort <did> to <num> list */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_NODE,
+                       "%d:0904 Add NPort x%x to %d list Data: x%x x%p\n",
+                       phba->brd_no,
+                       nlp->nlp_DID, list, nlp->nlp_flag, blp);
+
+       nlp->nlp_listp_bind = NULL;
+
+       switch(list) {
+       case NLP_NO_LIST: /* No list, just remove it */
+               lpfc_nlp_remove(phba, nlp);
+               break;
+       case NLP_UNUSED_LIST:
+               nlp->nlp_flag |= list;
+               /* Put it at the end of the unused list */
+               list_add_tail(&nlp->nlp_listp, &phba->fc_unused_list);
+               phba->fc_unused_cnt++;
+               break;
+       case NLP_PLOGI_LIST:
+               nlp->nlp_flag |= list;
+               /* Put it at the end of the plogi list */
+               list_add_tail(&nlp->nlp_listp, &phba->fc_plogi_list);
+               phba->fc_plogi_cnt++;
+               break;
+       case NLP_ADISC_LIST:
+               nlp->nlp_flag |= list;
+               /* Put it at the end of the adisc list */
+               list_add_tail(&nlp->nlp_listp, &phba->fc_adisc_list);
+               phba->fc_adisc_cnt++;
+               break;
+       case NLP_REGLOGIN_LIST:
+               nlp->nlp_flag |= list;
+               /* Put it at the end of the reglogin list */
+               list_add_tail(&nlp->nlp_listp, &phba->fc_reglogin_list);
+               phba->fc_reglogin_cnt++;
+               break;
+       case NLP_PRLI_LIST:
+               nlp->nlp_flag |= list;
+               /* Put it at the end of the prli list */
+               list_add_tail(&nlp->nlp_listp, &phba->fc_prli_list);
+               phba->fc_prli_cnt++;
+               break;
+       case NLP_UNMAPPED_LIST:
+               nlp->nlp_flag |= list;
+               /* Put it at the end of the unmap list */
+               list_add_tail(&nlp->nlp_listp, &phba->fc_nlpunmap_list);
+               phba->fc_unmap_cnt++;
+               phba->nport_event_cnt++;
+               /* stop nodev tmo if running */
+               if (nlp->nlp_flag & NLP_NODEV_TMO) {
+                       del_timer_sync(&nlp->nlp_tmofunc);
+               }
+               nlp->nlp_flag &= ~NLP_NODEV_TMO;
+               nlp->nlp_type |= NLP_FC_NODE;
+               lpfc_set_failmask(phba, nlp, LPFC_DEV_DISCOVERY_INP,
+                                 LPFC_CLR_BITMASK);
+               break;
+       case NLP_MAPPED_LIST:
+               nlp->nlp_flag |= list;
+               /* Put it at the end of the map list */
+               list_add_tail(&nlp->nlp_listp, &phba->fc_nlpmap_list);
+               phba->fc_map_cnt++;
+               phba->nport_event_cnt++;
+               /* stop nodev tmo if running */
+               if (nlp->nlp_flag & NLP_NODEV_TMO) {
+                       del_timer_sync(&nlp->nlp_tmofunc);
+               }
+               nlp->nlp_flag &= ~NLP_NODEV_TMO;
+               nlp->nlp_type |= NLP_FCP_TARGET;
+               lpfc_set_failmask(phba, nlp, LPFC_DEV_DISAPPEARED,
+                         LPFC_CLR_BITMASK);
+               lpfc_set_failmask(phba, nlp, LPFC_DEV_DISCOVERY_INP,
+                                 LPFC_CLR_BITMASK);
+
+               targetp = phba->device_queue_hash[nlp->nlp_sid];
+               if (targetp && targetp->pnode) {
+                       nlp->nlp_Target = targetp;
+#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH)
+                       /* Unblock I/Os on target */
+                       if(targetp->blocked)
+                               lpfc_target_unblock(phba, targetp);
+#endif
+               }
+               break;
+       case NLP_NPR_LIST:
+               nlp->nlp_flag |= list;
+               /* Put it at the end of the npr list */
+               list_add_tail(&nlp->nlp_listp, &phba->fc_npr_list);
+               phba->fc_npr_cnt++;
+
+               /*
+                * Sanity check for Fabric entity.
+                * Set nodev_tmo for NPR state, for Fabric use 1 sec.
+                */
+               if (nlp->nlp_type & NLP_FABRIC) {
+                       mod_timer(&nlp->nlp_tmofunc, jiffies + HZ);
+               }
+               else {
+                       mod_timer(&nlp->nlp_tmofunc,
+                           jiffies + HZ * phba->cfg_nodev_tmo);
+               }
+               nlp->nlp_flag |= NLP_NODEV_TMO;
+               nlp->nlp_flag &= ~NLP_RCV_PLOGI;
+               break;
+       case NLP_JUST_DQ:
+               break;
+       }
+
+       if (blp) {
+               nlp->nlp_sid = 0;
+               nlp->nlp_flag &= ~NLP_SEED_MASK;
+               nlp->nlp_Target = NULL;
+               lpfc_consistent_bind_save(phba, blp);
+       }
+       return (0);
+}
+
+/*
+ * Start / ReStart rescue timer for Discovery / RSCN handling
+ */
+void
+lpfc_set_disctmo(struct lpfc_hba * phba)
+{
+       uint32_t tmo;
+
+       tmo = ((phba->fc_ratov * 2) + 1);
+
+       mod_timer(&phba->fc_disctmo, jiffies + HZ * tmo);
+       phba->fc_flag |= FC_DISC_TMO;
+
+       /* Start Discovery Timer state <hba_state> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                       "%d:0247 Start Discovery Timer state x%x "
+                       "Data: x%x x%lx x%x x%x\n",
+                       phba->brd_no,
+                       phba->hba_state, tmo, (unsigned long)&phba->fc_disctmo,
+                       phba->fc_plogi_cnt, phba->fc_adisc_cnt);
+
+       return;
+}
+
+/*
+ * Cancel rescue timer for Discovery / RSCN handling
+ */
+int
+lpfc_can_disctmo(struct lpfc_hba * phba)
+{
+       /* Turn off discovery timer if its running */
+       if(phba->fc_flag & FC_DISC_TMO) {
+               del_timer_sync(&phba->fc_disctmo);
+       }
+       phba->fc_flag &= ~FC_DISC_TMO;
+
+       /* Cancel Discovery Timer state <hba_state> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                       "%d:0248 Cancel Discovery Timer state x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, phba->hba_state, phba->fc_flag,
+                       phba->fc_plogi_cnt, phba->fc_adisc_cnt);
+
+       return (0);
+}
+
+/*
+ * Check specified ring for outstanding IOCB on the SLI queue
+ * Return true if iocb matches the specified nport
+ */
+int
+lpfc_check_sli_ndlp(struct lpfc_hba * phba,
+                   struct lpfc_sli_ring * pring,
+                   struct lpfc_iocbq * iocb, struct lpfc_nodelist * ndlp)
+{
+       struct lpfc_sli *psli;
+       IOCB_t *icmd;
+
+       psli = &phba->sli;
+       icmd = &iocb->iocb;
+       if (pring->ringno == LPFC_ELS_RING) {
+               switch (icmd->ulpCommand) {
+               case CMD_GEN_REQUEST64_CR:
+                       if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi)
+                               return (1);
+               case CMD_ELS_REQUEST64_CR:
+               case CMD_XMIT_ELS_RSP64_CX:
+                       if (iocb->context1 == (uint8_t *) ndlp)
+                               return (1);
+               }
+       } else if (pring->ringno == psli->ip_ring) {
+
+       } else if (pring->ringno == psli->fcp_ring) {
+               /* Skip match check if waiting to relogin to FCP target */
+               if ((ndlp->nlp_type & NLP_FCP_TARGET) &&
+                 (ndlp->nlp_flag & NLP_DELAY_TMO)) {
+                       return (0);
+               }
+               if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) {
+                       return (1);
+               }
+       } else if (pring->ringno == psli->next_ring) {
+
+       }
+       return (0);
+}
+
+/*
+ * Free resources / clean up outstanding I/Os
+ * associated with nlp_rpi in the LPFC_NODELIST entry.
+ */
+static int
+lpfc_no_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       IOCB_t *icmd;
+       uint32_t rpi, i;
+
+       psli = &phba->sli;
+       rpi = ndlp->nlp_rpi;
+       if (rpi) {
+               /* Now process each ring */
+               for (i = 0; i < psli->sliinit.num_rings; i++) {
+                       pring = &psli->ring[i];
+
+                       list_for_each_entry_safe(iocb, next_iocb, &pring->txq,
+                                               list) {
+                               /*
+                                * Check to see if iocb matches the nport we are
+                                * looking for
+                                */
+                               if ((lpfc_check_sli_ndlp
+                                    (phba, pring, iocb, ndlp))) {
+                                       /* It matches, so deque and call compl
+                                          with an error */
+                                       list_del(&iocb->list);
+                                       pring->txq_cnt--;
+                                       if (iocb->iocb_cmpl) {
+                                               icmd = &iocb->iocb;
+                                               icmd->ulpStatus =
+                                                   IOSTAT_LOCAL_REJECT;
+                                               icmd->un.ulpWord[4] =
+                                                   IOERR_SLI_ABORTED;
+                                               (iocb->iocb_cmpl) (phba,
+                                                                  iocb, iocb);
+                                       } else {
+                                               mempool_free(iocb,
+                                                    phba->iocb_mem_pool);
+                                       }
+                               }
+                       }
+                       /* Everything that matches on txcmplq will be returned
+                        * by firmware with a no rpi error.
+                        */
+               }
+       }
+       return (0);
+}
+
+/*
+ * Free rpi associated with LPFC_NODELIST entry.
+ * This routine is called from lpfc_freenode(), when we are removing
+ * a LPFC_NODELIST entry. It is also called if the driver initiates a
+ * LOGO that completes successfully, and we are waiting to PLOGI back
+ * to the remote NPort. In addition, it is called after we receive
+ * and unsolicated ELS cmd, send back a rsp, the rsp completes and
+ * we are waiting to PLOGI back to the remote NPort.
+ */
+int
+lpfc_unreg_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+       LPFC_MBOXQ_t *mbox;
+
+       if (ndlp->nlp_rpi) {
+               if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) {
+                       lpfc_unreg_login(phba, ndlp->nlp_rpi, mbox);
+                       if (lpfc_sli_issue_mbox
+                           (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB))
+                           == MBX_NOT_FINISHED) {
+                               mempool_free( mbox, phba->mbox_mem_pool);
+                       }
+               }
+               lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+               lpfc_no_rpi(phba, ndlp);
+               ndlp->nlp_rpi = 0;
+               lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED,
+                                 LPFC_SET_BITMASK);
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Free resources associated with LPFC_NODELIST entry
+ * so it can be freed.
+ */
+static int
+lpfc_freenode(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+       struct lpfc_target *targetp;
+       struct lpfc_sli *psli;
+       int scsid;
+
+       /* The psli variable gets rid of the long pointer deference. */
+       psli = &phba->sli;
+
+       /* Cleanup node for NPort <nlp_DID> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                       "%d:0900 Cleanup node for NPort x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag,
+                       ndlp->nlp_state, ndlp->nlp_rpi);
+
+       lpfc_nlp_list(phba, ndlp, NLP_JUST_DQ);
+
+       if(ndlp->nlp_flag & NLP_NODEV_TMO) {
+               del_timer_sync(&ndlp->nlp_tmofunc);
+       }
+       ndlp->nlp_flag &= ~NLP_NODEV_TMO;
+
+       if(ndlp->nlp_flag & NLP_DELAY_TMO) {
+               del_timer_sync(&ndlp->nlp_delayfunc);
+       }
+       ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+
+       lpfc_unreg_rpi(phba, ndlp);
+
+       for(scsid=0;scsid<MAX_FCP_TARGET;scsid++) {
+               targetp = phba->device_queue_hash[scsid];
+               /* First see if the SCSI ID has an allocated struct
+                  lpfc_target */
+               if (targetp) {
+                       if (targetp->pnode == ndlp) {
+                               targetp->pnode = NULL;
+                               ndlp->nlp_Target = NULL;
+#ifdef FC_TRANS_VER1
+                               /*
+                                * This code does not apply to SLES9 since there
+                                * is no starget defined in the midlayer.
+                                * Additionally, dynamic target discovery to the
+                                * midlayer is not supported yet.
+                                */
+                               if (targetp->starget) {
+                                       /* Remove SCSI target / SCSI Hotplug */
+                                       lpfc_target_remove(phba, targetp);
+                               }
+#endif
+                               break;
+                       }
+               }
+       }
+       return (0);
+}
+
+/*
+ * Check to see if we can free the nlp back to the freelist.
+ * If we are in the middle of using the nlp in the discovery state
+ * machine, defer the free till we reach the end of the state machine.
+ */
+int
+lpfc_nlp_remove(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+       if(ndlp->nlp_flag & NLP_NODEV_TMO) {
+               del_timer_sync(&ndlp->nlp_tmofunc);
+       }
+       ndlp->nlp_flag &= ~NLP_NODEV_TMO;
+
+       if(ndlp->nlp_flag & NLP_DELAY_TMO) {
+               del_timer_sync(&ndlp->nlp_delayfunc);
+       }
+       ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+
+       if (ndlp->nlp_disc_refcnt) {
+               ndlp->nlp_flag |= NLP_DELAY_REMOVE;
+       }
+       else {
+               lpfc_freenode(phba, ndlp);
+               mempool_free( ndlp, phba->nlp_mem_pool);
+       }
+       return(0);
+}
+
+static int
+lpfc_matchdid(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, uint32_t did)
+{
+       D_ID mydid;
+       D_ID ndlpdid;
+       D_ID matchdid;
+
+       if (did == Bcast_DID)
+               return (0);
+
+       if (ndlp->nlp_DID == 0) {
+               return (0);
+       }
+
+       /* First check for Direct match */
+       if (ndlp->nlp_DID == did)
+               return (1);
+
+       /* Next check for area/domain identically equals 0 match */
+       mydid.un.word = phba->fc_myDID;
+       if ((mydid.un.b.domain == 0) && (mydid.un.b.area == 0)) {
+               return (0);
+       }
+
+       matchdid.un.word = did;
+       ndlpdid.un.word = ndlp->nlp_DID;
+       if (matchdid.un.b.id == ndlpdid.un.b.id) {
+               if ((mydid.un.b.domain == matchdid.un.b.domain) &&
+                   (mydid.un.b.area == matchdid.un.b.area)) {
+                       if ((ndlpdid.un.b.domain == 0) &&
+                           (ndlpdid.un.b.area == 0)) {
+                               if (ndlpdid.un.b.id)
+                                       return (1);
+                       }
+                       return (0);
+               }
+
+               matchdid.un.word = ndlp->nlp_DID;
+               if ((mydid.un.b.domain == ndlpdid.un.b.domain) &&
+                   (mydid.un.b.area == ndlpdid.un.b.area)) {
+                       if ((matchdid.un.b.domain == 0) &&
+                           (matchdid.un.b.area == 0)) {
+                               if (matchdid.un.b.id)
+                                       return (1);
+                       }
+               }
+       }
+       return (0);
+}
+
+/* Search for a nodelist entry on a specific list */
+struct lpfc_nodelist *
+lpfc_findnode_wwpn(struct lpfc_hba * phba, uint32_t order,
+                  struct lpfc_name * wwpn)
+{
+       struct lpfc_nodelist *ndlp, *next_ndlp;
+       uint32_t data1;
+
+       if (order & NLP_SEARCH_UNMAPPED) {
+               list_for_each_entry_safe(ndlp, next_ndlp,
+                                        &phba->fc_nlpunmap_list, nlp_listp) {
+                       if (memcmp(&ndlp->nlp_portname, wwpn,
+                                  sizeof(struct lpfc_name)) == 0) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* FIND node DID unmapped */
+                               lpfc_printf_log(phba,
+                                               KERN_INFO,
+                                               LOG_NODE,
+                                               "%d:0911 FIND node DID unmapped"
+                                               " Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       if (order & NLP_SEARCH_MAPPED) {
+               list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list,
+                                       nlp_listp) {
+                       if (memcmp(&ndlp->nlp_portname, wwpn,
+                                  sizeof(struct lpfc_name)) == 0) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* FIND node DID mapped */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0901 FIND node DID mapped "
+                                               "Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       /* no match found */
+       return ((struct lpfc_nodelist *) 0);
+}
+/* Search for a nodelist entry on a specific list */
+struct lpfc_nodelist *
+lpfc_findnode_wwnn(struct lpfc_hba * phba, uint32_t order,
+                  struct lpfc_name * wwnn)
+{
+       struct lpfc_nodelist *ndlp, *next_ndlp;
+       uint32_t data1;
+
+       if (order & NLP_SEARCH_UNMAPPED) {
+               list_for_each_entry_safe(ndlp, next_ndlp,
+                                        &phba->fc_nlpunmap_list, nlp_listp) {
+                       if (memcmp(&ndlp->nlp_nodename, wwnn,
+                                  sizeof(struct lpfc_name)) ==  0) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* FIND node DID unmapped */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0910 FIND node DID unmapped"
+                                               "Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       if (order & NLP_SEARCH_MAPPED) {
+               list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list,
+                                        nlp_listp) {
+                       if (memcmp(&ndlp->nlp_nodename, wwnn,
+                                  sizeof(struct lpfc_name)) == 0) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* FIND node did mapped */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0902 FIND node DID mapped "
+                                               "Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       /* no match found */
+       return ((struct lpfc_nodelist *) 0);
+}
+/* Search for a nodelist entry on a specific list */
+struct lpfc_nodelist *
+lpfc_findnode_did(struct lpfc_hba * phba, uint32_t order, uint32_t did)
+{
+       struct lpfc_nodelist *ndlp, *next_ndlp;
+       uint32_t data1;
+
+       if (order & NLP_SEARCH_UNMAPPED) {
+               list_for_each_entry_safe(ndlp, next_ndlp,
+                                        &phba->fc_nlpunmap_list, nlp_listp) {
+                       if (lpfc_matchdid(phba, ndlp, did)) {
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* FIND node DID unmapped */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0929 FIND node DID unmapped"
+                                               " Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       if (order & NLP_SEARCH_MAPPED) {
+               list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list,
+                                       nlp_listp) {
+                       if (lpfc_matchdid(phba, ndlp, did)) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* FIND node DID mapped */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0930 FIND node DID mapped "
+                                               "Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       if (order & NLP_SEARCH_PLOGI) {
+               list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list,
+                                       nlp_listp) {
+                       if (lpfc_matchdid(phba, ndlp, did)) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* LOG change to PLOGI */
+                               /* FIND node DID plogi */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0908 FIND node DID plogi "
+                                               "Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       if (order & NLP_SEARCH_ADISC) {
+               list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list,
+                                       nlp_listp) {
+                       if (lpfc_matchdid(phba, ndlp, did)) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* LOG change to ADISC */
+                               /* FIND node DID adisc */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0931 FIND node DID adisc "
+                                               "Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       if (order & NLP_SEARCH_REGLOGIN) {
+               list_for_each_entry_safe(ndlp, next_ndlp,
+                                        &phba->fc_reglogin_list, nlp_listp) {
+                       if (lpfc_matchdid(phba, ndlp, did)) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* LOG change to REGLOGIN */
+                               /* FIND node DID reglogin */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0931 FIND node DID reglogin"
+                                               " Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       if (order & NLP_SEARCH_PRLI) {
+               list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list,
+                                       nlp_listp) {
+                       if (lpfc_matchdid(phba, ndlp, did)) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* LOG change to PRLI */
+                               /* FIND node DID prli */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0931 FIND node DID prli "
+                                               "Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       if (order & NLP_SEARCH_NPR) {
+               list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+                                       nlp_listp) {
+                       if (lpfc_matchdid(phba, ndlp, did)) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* LOG change to NPR */
+                               /* FIND node DID npr */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0931 FIND node DID npr "
+                                               "Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       if (order & NLP_SEARCH_UNUSED) {
+               list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list,
+                                       nlp_listp) {
+                       if (lpfc_matchdid(phba, ndlp, did)) {
+
+                               data1 = (((uint32_t) ndlp->nlp_state << 24) |
+                                        ((uint32_t) ndlp->nlp_xri << 16) |
+                                        ((uint32_t) ndlp->nlp_type << 8) |
+                                        ((uint32_t) ndlp->nlp_rpi & 0xff));
+                               /* LOG change to UNUSED */
+                               /* FIND node DID unused */
+                               lpfc_printf_log(phba, KERN_INFO, LOG_NODE,
+                                               "%d:0931 FIND node DID unused "
+                                               "Data: x%p x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               ndlp, ndlp->nlp_DID,
+                                               ndlp->nlp_flag, data1);
+                               return (ndlp);
+                       }
+               }
+       }
+
+       /* FIND node did <did> NOT FOUND */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_NODE,
+                       "%d:0932 FIND node did x%x NOT FOUND Data: x%x\n",
+                       phba->brd_no, did, order);
+
+       /* no match found */
+       return ((struct lpfc_nodelist *) 0);
+}
+
+struct lpfc_nodelist *
+lpfc_setup_disc_node(struct lpfc_hba * phba, uint32_t did)
+{
+       struct lpfc_nodelist *ndlp;
+       uint32_t flg;
+
+       if((ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, did)) == 0) {
+               if ((phba->hba_state == LPFC_HBA_READY) &&
+                  ((lpfc_rscn_payload_check(phba, did) == 0)))
+                       return NULL;
+               ndlp = (struct lpfc_nodelist *)
+                    mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC);
+               if (!ndlp)
+                       return NULL;
+               lpfc_nlp_init(phba, ndlp, did);
+               ndlp->nlp_state = NLP_STE_NPR_NODE;
+               lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+               ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+               return ndlp;
+       }
+       if ((phba->hba_state == LPFC_HBA_READY) &&
+           (phba->fc_flag & FC_RSCN_MODE)) {
+               if(lpfc_rscn_payload_check(phba, did)) {
+                       ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+               }
+               else {
+                       ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+                       ndlp = NULL;
+               }
+       }
+       else {
+               flg = ndlp->nlp_flag & NLP_LIST_MASK;
+               if ((flg == NLP_ADISC_LIST) ||
+               (flg == NLP_PLOGI_LIST)) {
+                       return NULL;
+               }
+               ndlp->nlp_state = NLP_STE_NPR_NODE;
+               lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+               ndlp->nlp_flag |= NLP_NPR_2B_DISC;
+       }
+       return ndlp;
+}
+
+/* Build a list of nodes to discover based on the loopmap */
+void
+lpfc_disc_list_loopmap(struct lpfc_hba * phba)
+{
+       int j;
+       uint32_t alpa, index;
+
+       if (phba->hba_state <= LPFC_LINK_DOWN) {
+               return;
+       }
+       if (phba->fc_topology != TOPOLOGY_LOOP) {
+               return;
+       }
+
+       /* Check for loop map present or not */
+       if (phba->alpa_map[0]) {
+               for (j = 1; j <= phba->alpa_map[0]; j++) {
+                       alpa = phba->alpa_map[j];
+
+                       if (((phba->fc_myDID & 0xff) == alpa) || (alpa == 0)) {
+                               continue;
+                       }
+                       lpfc_setup_disc_node(phba, alpa);
+               }
+       } else {
+               /* No alpamap, so try all alpa's */
+               for (j = 0; j < FC_MAXLOOP; j++) {
+                       /* If cfg_scan_down is set, start from highest
+                        * ALPA (0xef) to lowest (0x1).
+                        */
+                       if (phba->cfg_scan_down)
+                               index = j;
+                       else
+                               index = FC_MAXLOOP - j - 1;
+                       alpa = lpfcAlpaArray[index];
+                       if ((phba->fc_myDID & 0xff) == alpa) {
+                               continue;
+                       }
+
+                       lpfc_setup_disc_node(phba, alpa);
+               }
+       }
+       return;
+}
+
+/* Start Link up / RSCN discovery on NPR list */
+void
+lpfc_disc_start(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       LPFC_MBOXQ_t *mbox;
+       struct lpfc_nodelist *ndlp, *next_ndlp;
+       uint32_t did_changed, num_sent;
+       uint32_t clear_la_pending;
+
+       psli = &phba->sli;
+
+       if (phba->hba_state <= LPFC_LINK_DOWN) {
+               return;
+       }
+       if (phba->hba_state == LPFC_CLEAR_LA)
+               clear_la_pending = 1;
+       else
+               clear_la_pending = 0;
+
+       if (phba->hba_state < LPFC_HBA_READY) {
+               phba->hba_state = LPFC_DISC_AUTH;
+       }
+       lpfc_set_disctmo(phba);
+
+       if (phba->fc_prevDID == phba->fc_myDID) {
+               did_changed = 0;
+       } else {
+               did_changed = 1;
+       }
+       phba->fc_prevDID = phba->fc_myDID;
+       phba->num_disc_nodes = 0;
+
+       /* Start Discovery state <hba_state> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                       "%d:0202 Start Discovery hba state x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, phba->hba_state, phba->fc_flag,
+                       phba->fc_plogi_cnt, phba->fc_adisc_cnt);
+
+       /* If our did changed, we MUST do PLOGI */
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+                               nlp_listp) {
+               if(ndlp->nlp_flag & NLP_NPR_2B_DISC) {
+                       if(did_changed)
+                               ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+               }
+       }
+
+       /* First do ADISCs - if any */
+       num_sent = lpfc_els_disc_adisc(phba);
+
+       if(num_sent)
+               return;
+
+       if ((phba->hba_state < LPFC_HBA_READY) && (!clear_la_pending)) {
+               /* If we get here, there is nothing to ADISC */
+               if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) {
+                       phba->hba_state = LPFC_CLEAR_LA;
+                       lpfc_clear_la(phba, mbox);
+                       mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+                       if (lpfc_sli_issue_mbox
+                           (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB))
+                           == MBX_NOT_FINISHED) {
+                               mempool_free( mbox, phba->mbox_mem_pool);
+                               lpfc_disc_flush_list(phba);
+                               psli->ring[(psli->ip_ring)].flag &=
+                                   ~LPFC_STOP_IOCB_EVENT;
+                               psli->ring[(psli->fcp_ring)].flag &=
+                                   ~LPFC_STOP_IOCB_EVENT;
+                               psli->ring[(psli->next_ring)].flag &=
+                                   ~LPFC_STOP_IOCB_EVENT;
+                               phba->hba_state = LPFC_HBA_READY;
+                       }
+               }
+       } else {
+               /* Next do PLOGIs - if any */
+               num_sent = lpfc_els_disc_plogi(phba);
+
+               if(num_sent)
+                       return;
+
+               if (phba->fc_flag & FC_RSCN_MODE) {
+                       /* Check to see if more RSCNs came in while we
+                        * were processing this one.
+                        */
+                       if ((phba->fc_rscn_id_cnt == 0) &&
+                           (!(phba->fc_flag & FC_RSCN_DISCOVERY))) {
+                               lpfc_els_flush_rscn(phba);
+                       } else {
+                               lpfc_els_handle_rscn(phba);
+                       }
+               }
+       }
+       return;
+}
+
+/*
+ *  Ignore completion for all IOCBs on tx and txcmpl queue for ELS
+ *  ring the match the sppecified nodelist.
+ */
+static void
+lpfc_free_tx(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+       struct lpfc_sli *psli;
+       IOCB_t     *icmd;
+       struct lpfc_iocbq    *iocb, *next_iocb;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_dmabuf   *mp;
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];
+
+       /* Error matching iocb on txq or txcmplq
+        * First check the txq.
+        */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+               if (iocb->context1 != ndlp) {
+                       continue;
+               }
+               icmd = &iocb->iocb;
+               if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) ||
+                   (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) {
+
+                       list_del(&iocb->list);
+                       pring->txq_cnt--;
+                       lpfc_els_free_iocb(phba, iocb);
+               }
+       }
+
+       /* Next check the txcmplq */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+               if (iocb->context1 != ndlp) {
+                       continue;
+               }
+               icmd = &iocb->iocb;
+               if ((icmd->ulpCommand == CMD_ELS_REQUEST64_CR) ||
+                   (icmd->ulpCommand == CMD_XMIT_ELS_RSP64_CX)) {
+
+                       iocb->iocb_cmpl = NULL;
+                       /* context2 = cmd, context2->next = rsp, context3 =
+                          bpl */
+                       if (iocb->context2) {
+                               /* Free the response IOCB before handling the
+                                  command. */
+
+                               mp = (struct lpfc_dmabuf *)
+                                    (((struct lpfc_dmabuf *) (iocb->context2))
+                                   ->list.next);
+                               if (mp) {
+                                       /* Delay before releasing rsp buffer to
+                                        * give UNREG mbox a chance to take
+                                        * effect.
+                                        */
+                                       list_add(&mp->list,
+                                               &phba->freebufList);
+                               }
+                               lpfc_mbuf_free(phba,
+                                              ((struct lpfc_dmabuf *)
+                                               iocb->context2)->virt,
+                                              ((struct lpfc_dmabuf *)
+                                               iocb->context2)->phys);
+                               kfree(iocb->context2);
+                       }
+
+                       if (iocb->context3) {
+                               lpfc_mbuf_free(phba,
+                                              ((struct lpfc_dmabuf *)
+                                               iocb->context3)->virt,
+                                              ((struct lpfc_dmabuf *)
+                                               iocb->context3)->phys);
+                               kfree(iocb->context3);
+                       }
+               }
+       }
+
+       return;
+}
+
+void
+lpfc_disc_flush_list(struct lpfc_hba * phba)
+{
+       struct lpfc_nodelist *ndlp, *next_ndlp;
+
+       if (phba->fc_plogi_cnt) {
+               list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list,
+                                       nlp_listp) {
+                       lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED,
+                                         LPFC_SET_BITMASK);
+                       lpfc_free_tx(phba, ndlp);
+                       lpfc_nlp_remove(phba, ndlp);
+               }
+       }
+       if (phba->fc_adisc_cnt) {
+               list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list,
+                                       nlp_listp) {
+                       lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCONNECTED,
+                                         LPFC_SET_BITMASK);
+                       lpfc_free_tx(phba, ndlp);
+                       lpfc_nlp_remove(phba, ndlp);
+               }
+       }
+       return;
+}
+
+/*****************************************************************************/
+/*
+ * NAME:     lpfc_disc_timeout
+ *
+ * FUNCTION: Fibre Channel driver discovery timeout routine.
+ *
+ * EXECUTION ENVIRONMENT: interrupt only
+ *
+ * CALLED FROM:
+ *      Timer function
+ *
+ * RETURNS:
+ *      none
+ */
+/*****************************************************************************/
+void
+lpfc_disc_timeout(unsigned long ptr)
+{
+       struct lpfc_hba *phba;
+       struct lpfc_sli *psli;
+       struct lpfc_nodelist *ndlp;
+       LPFC_MBOXQ_t *mbox;
+       unsigned long iflag;
+
+       phba = (struct lpfc_hba *)ptr;
+       if (!phba) {
+               return;
+       }
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+
+       psli = &phba->sli;
+       phba->fc_flag &= ~FC_DISC_TMO;
+
+       /* hba_state is identically LPFC_LOCAL_CFG_LINK while waiting for FAN */
+       if (phba->hba_state == LPFC_LOCAL_CFG_LINK) {
+               /* FAN timeout */
+               lpfc_printf_log(phba,
+                                KERN_WARNING,
+                                LOG_DISCOVERY,
+                                "%d:0221 FAN timeout\n",
+                                phba->brd_no);
+
+               /* Forget about FAN, Start discovery by sending a FLOGI
+                * hba_state is identically LPFC_FLOGI while waiting for FLOGI
+                * cmpl
+                */
+               phba->hba_state = LPFC_FLOGI;
+               lpfc_set_disctmo(phba);
+               lpfc_initial_flogi(phba);
+               goto out;
+       }
+
+       /* hba_state is identically LPFC_FLOGI while waiting for FLOGI cmpl */
+       if (phba->hba_state == LPFC_FLOGI) {
+               /* Initial FLOGI timeout */
+               lpfc_printf_log(phba,
+                                KERN_ERR,
+                                LOG_DISCOVERY,
+                                "%d:0222 Initial FLOGI timeout\n",
+                                phba->brd_no);
+
+               /* Assume no Fabric and go on with discovery.
+                * Check for outstanding ELS FLOGI to abort.
+                */
+
+               /* FLOGI failed, so just use loop map to make discovery list */
+               lpfc_disc_list_loopmap(phba);
+
+               /* Start discovery */
+               lpfc_disc_start(phba);
+               goto out;
+       }
+
+       /* hba_state is identically LPFC_FABRIC_CFG_LINK while waiting for
+          NameServer login */
+       if (phba->hba_state == LPFC_FABRIC_CFG_LINK) {
+               /* Timeout while waiting for NameServer login */
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+                               "%d:0223 Timeout while waiting for NameServer "
+                               "login\n", phba->brd_no);
+
+               /* Next look for NameServer ndlp */
+               if ((ndlp = lpfc_findnode_did(phba,
+                                      NLP_SEARCH_ALL, NameServer_DID))) {
+                       lpfc_nlp_remove(phba, ndlp);
+               }
+               /* Start discovery */
+               lpfc_disc_start(phba);
+               goto out;
+       }
+
+       /* Check for wait for NameServer Rsp timeout */
+       if (phba->hba_state == LPFC_NS_QRY) {
+               /* NameServer Query timeout */
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+                               "%d:0224 NameServer Query timeout "
+                               "Data: x%x x%x\n",
+                               phba->brd_no,
+                               phba->fc_ns_retry, LPFC_MAX_NS_RETRY);
+
+               if ((ndlp =
+                    lpfc_findnode_did(phba, NLP_SEARCH_UNMAPPED,
+                                      NameServer_DID))) {
+                       if (phba->fc_ns_retry < LPFC_MAX_NS_RETRY) {
+                               /* Try it one more time */
+                               if (lpfc_ns_cmd(phba, ndlp, SLI_CTNS_GID_FT) ==
+                                   0) {
+                                       goto out;
+                               }
+                       }
+                       phba->fc_ns_retry = 0;
+               }
+
+               /* Nothing to authenticate, so CLEAR_LA right now */
+               if (phba->hba_state != LPFC_CLEAR_LA) {
+                       if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+                                                 GFP_ATOMIC))) {
+                               phba->hba_state = LPFC_CLEAR_LA;
+                               lpfc_clear_la(phba, mbox);
+                               mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+                               if (lpfc_sli_issue_mbox
+                                   (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB))
+                                   == MBX_NOT_FINISHED) {
+                                       mempool_free(mbox, phba->mbox_mem_pool);
+                                       goto clrlaerr;
+                               }
+                       } else {
+                               /* Device Discovery completion error */
+                               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+                                               "%d:0226 Device Discovery "
+                                               "completion error\n",
+                                               phba->brd_no);
+                               phba->hba_state = LPFC_HBA_ERROR;
+                       }
+               }
+               if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC))) {
+                       /* Setup and issue mailbox INITIALIZE LINK command */
+                       lpfc_linkdown(phba);
+                       lpfc_init_link(phba, mbox,
+                                      phba->cfg_topology,
+                                      phba->cfg_link_speed);
+                       mbox->mb.un.varInitLnk.lipsr_AL_PA = 0;
+                       if (lpfc_sli_issue_mbox
+                           (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB))
+                           == MBX_NOT_FINISHED) {
+                               mempool_free( mbox, phba->mbox_mem_pool);
+                       }
+               }
+               goto out;
+       }
+
+       if (phba->hba_state == LPFC_DISC_AUTH) {
+               /* Node Authentication timeout */
+               lpfc_printf_log(phba,
+                                KERN_ERR,
+                                LOG_DISCOVERY,
+                                "%d:0227 Node Authentication timeout\n",
+                                phba->brd_no);
+               lpfc_disc_flush_list(phba);
+               if (phba->hba_state != LPFC_CLEAR_LA) {
+                       if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+                                                 GFP_ATOMIC))) {
+                               phba->hba_state = LPFC_CLEAR_LA;
+                               lpfc_clear_la(phba, mbox);
+                               mbox->mbox_cmpl = lpfc_mbx_cmpl_clear_la;
+                               if (lpfc_sli_issue_mbox
+                                   (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB))
+                                   == MBX_NOT_FINISHED) {
+                                       mempool_free(mbox, phba->mbox_mem_pool);
+                                       goto clrlaerr;
+                               }
+                       }
+               }
+               goto out;
+       }
+
+       if (phba->hba_state == LPFC_CLEAR_LA) {
+               /* CLEAR LA timeout */
+               lpfc_printf_log(phba,
+                                KERN_ERR,
+                                LOG_DISCOVERY,
+                                "%d:0228 CLEAR LA timeout\n",
+                                phba->brd_no);
+clrlaerr:
+               lpfc_disc_flush_list(phba);
+               psli->ring[(psli->ip_ring)].flag &= ~LPFC_STOP_IOCB_EVENT;
+               psli->ring[(psli->fcp_ring)].flag &= ~LPFC_STOP_IOCB_EVENT;
+               psli->ring[(psli->next_ring)].flag &= ~LPFC_STOP_IOCB_EVENT;
+               phba->hba_state = LPFC_HBA_READY;
+               goto out;
+       }
+
+       if ((phba->hba_state == LPFC_HBA_READY) &&
+           (phba->fc_flag & FC_RSCN_MODE)) {
+               /* RSCN timeout */
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_DISCOVERY,
+                               "%d:0231 RSCN timeout Data: x%x x%x\n",
+                               phba->brd_no,
+                               phba->fc_ns_retry, LPFC_MAX_NS_RETRY);
+
+               /* Cleanup any outstanding ELS commands */
+               lpfc_els_flush_cmd(phba);
+
+               lpfc_els_flush_rscn(phba);
+               lpfc_disc_flush_list(phba);
+               goto out;
+       }
+
+out:
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+       return;
+}
+
+/*****************************************************************************/
+/*
+ * NAME:     lpfc_scan_timeout
+ *
+ * FUNCTION: Fibre Channel driver scsi_scan_host timeout routine.
+ *
+ * EXECUTION ENVIRONMENT: interrupt only
+ *
+ * CALLED FROM:
+ *      Timer function
+ *
+ * RETURNS:
+ *      none
+ */
+/*****************************************************************************/
+void
+lpfc_scan_timeout(unsigned long ptr)
+{
+       struct lpfc_hba *phba;
+       unsigned long iflag;
+
+       phba = (struct lpfc_hba *)ptr;
+       if (!phba) {
+               return;
+       }
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+       phba->fc_flag &= ~FC_SCSI_SCAN_TMO;
+       lpfc_discq_post_event(phba, NULL, NULL, LPFC_EVT_SCAN);
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+       return;
+}
+
+static void
+lpfc_nodev_timeout(unsigned long ptr)
+{
+       struct lpfc_hba *phba;
+       struct lpfc_nodelist *ndlp;
+       unsigned long iflag;
+
+       ndlp = (struct lpfc_nodelist *)ptr;
+       phba = ndlp->nlp_phba;
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+
+       /* All nodev timeouts are posted to discovery tasklet */
+       lpfc_discq_post_event(phba, ndlp, NULL, LPFC_EVT_NODEV_TMO);
+
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+       return;
+}
+
+/*****************************************************************************/
+/*
+ * NAME:     lpfc_find_target
+ *
+ * FUNCTION: Fibre Channel bus/target/LUN to struct lpfc_target lookup
+ *
+ * EXECUTION ENVIRONMENT:
+ *
+ * RETURNS:
+ *      ptr to desired struct lpfc_target
+ */
+/*****************************************************************************/
+struct lpfc_target *
+lpfc_find_target(struct lpfc_hba * phba, uint32_t tgt,
+       struct lpfc_nodelist *nlp)
+{
+       struct lpfc_target *targetp;
+
+       /* search the mapped list for this target ID */
+       if(!nlp) {
+               list_for_each_entry(nlp, &phba->fc_nlpmap_list, nlp_listp) {
+                       if (tgt == nlp->nlp_sid)
+                               break;
+               }
+
+               if (&(nlp->nlp_listp) == &(phba->fc_nlpmap_list))
+                       return NULL;
+       }
+
+       targetp = phba->device_queue_hash[tgt];
+
+       /* First see if the SCSI ID has an allocated struct lpfc_target */
+       if (!targetp) {
+               targetp = kmalloc(sizeof (struct lpfc_target), GFP_ATOMIC);
+               if (!targetp)
+                       return NULL;
+
+               memset(targetp, 0, sizeof (struct lpfc_target));
+               phba->device_queue_hash[tgt] = targetp;
+               targetp->scsi_id = tgt;
+
+               /* Create SCSI Target <tgt> */
+               lpfc_printf_log(phba,
+                               KERN_INFO,
+                               LOG_DISCOVERY | LOG_FCP,
+                               "%d:0204 Create SCSI Target %d\n",
+                               phba->brd_no, tgt);
+       }
+
+       if (targetp->pnode == NULL) {
+               targetp->pnode = nlp;
+               nlp->nlp_Target = targetp;
+#ifdef FC_TRANS_VER1
+               /*
+                * This code does not apply to SLES9 since there is no
+                * starget defined in the midlayer.  Additionally,
+                * dynamic target discovery to the midlayer is not
+                * supported yet.
+                */
+               if(!(phba->fc_flag & FC_LOADING)) {
+                       /* Add SCSI target / SCSI Hotplug if called
+                        * after initial driver load.
+                        */
+                       lpfc_target_add(phba, targetp);
+               }
+#endif
+       }
+       else {
+               if(targetp->pnode != nlp) {
+                       /*
+                        * Get rid of the old nlp before updating
+                        * targetp with the new one.
+                        */
+                       lpfc_nlp_list(phba, targetp->pnode, NLP_NO_LIST);
+                       targetp->pnode = nlp;
+               }
+       }
+       nlp->nlp_Target = targetp;
+       return (targetp);
+}
+
+/*
+ *   lpfc_set_failmask
+ *   Set, or clear, failMask bits in struct lpfc_nodelist
+ */
+void
+lpfc_set_failmask(struct lpfc_hba * phba,
+                 struct lpfc_nodelist * ndlp, uint32_t bitmask, uint32_t flag)
+{
+       uint32_t oldmask;
+       uint32_t changed;
+
+       /* Failmask change on NPort <nlp_DID> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                       "%d:0208 Failmask change on NPort x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no,
+                       ndlp->nlp_DID, ndlp->nlp_failMask, bitmask, flag);
+
+       if (flag == LPFC_SET_BITMASK) {
+               oldmask = ndlp->nlp_failMask;
+               /* Set failMask event */
+               ndlp->nlp_failMask |= bitmask;
+               if (oldmask != ndlp->nlp_failMask) {
+                       changed = 1;
+               } else {
+                       changed = 0;
+               }
+
+       } else {
+               /* Clear failMask event */
+               ndlp->nlp_failMask &= ~bitmask;
+               changed = 1;
+       }
+       return;
+}
+
+/*
+ * This routine handles processing a NameServer REG_LOGIN mailbox
+ * command upon completion. It is setup in the LPFC_MBOXQ
+ * as the completion routine when the command is
+ * handed off to the SLI layer.
+ */
+void
+lpfc_mbx_cmpl_fdmi_reg_login(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       struct lpfc_sli *psli;
+       MAILBOX_t *mb;
+       struct lpfc_dmabuf *mp;
+       struct lpfc_nodelist *ndlp;
+
+       psli = &phba->sli;
+       mb = &pmb->mb;
+
+       ndlp = (struct lpfc_nodelist *) pmb->context2;
+       mp = (struct lpfc_dmabuf *) (pmb->context1);
+
+       /* The mailbox was populated by the HBA.  Flush it to main store for the
+        * driver.  Note that all context buffers are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE.
+        */
+       pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE,
+                       PCI_DMA_FROMDEVICE);
+
+       pmb->context1 = NULL;
+
+       if (ndlp->nlp_rpi != 0)
+               lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+       ndlp->nlp_rpi = mb->un.varWords[0];
+       lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi);
+       ndlp->nlp_type |= NLP_FABRIC;
+       ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+
+       /* Start issuing Fabric-Device Management Interface (FDMI)
+        * command to 0xfffffa (FDMI well known port)
+        */
+       if (phba->cfg_fdmi_on == 1) {
+               lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA);
+       } else {
+               /*
+                * Delay issuing FDMI command if fdmi-on=2
+                * (supporting RPA/hostnmae)
+                */
+               mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60);
+       }
+
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       mempool_free( pmb, phba->mbox_mem_pool);
+
+       return;
+}
+
+/*
+ * This routine looks up the ndlp hash
+ * table for the given RPI. If rpi found
+ * it return the node list pointer
+ * else return 0.
+ */
+struct lpfc_nodelist *
+lpfc_findnode_rpi(struct lpfc_hba * phba, uint16_t rpi)
+{
+       struct lpfc_nodelist *ret;
+
+       ret = phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)];
+       while ((ret != 0) && (ret->nlp_rpi != rpi)) {
+               ret = ret->nlp_rpi_hash_next;
+       }
+       return ret;
+}
+
+/*
+ * This routine looks up the ndlp hash table for the
+ * given RPI. If rpi found it return the node list
+ * pointer else return 0 after deleting the entry
+ * from hash table.
+ */
+struct lpfc_nodelist *
+lpfc_findnode_remove_rpi(struct lpfc_hba * phba, uint16_t rpi)
+{
+       struct lpfc_nodelist *ret, *temp;;
+
+       ret = phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)];
+       if (ret == 0)
+               return NULL;
+
+       if (ret->nlp_rpi == rpi) {
+               phba->fc_nlplookup[LPFC_RPI_HASH_FUNC(rpi)] =
+                   ret->nlp_rpi_hash_next;
+               ret->nlp_rpi_hash_next = NULL;
+               return ret;
+       }
+
+       while ((ret->nlp_rpi_hash_next != 0) &&
+              (ret->nlp_rpi_hash_next->nlp_rpi != rpi)) {
+               ret = ret->nlp_rpi_hash_next;
+       }
+
+       if (ret->nlp_rpi_hash_next != 0) {
+               temp = ret->nlp_rpi_hash_next;
+               ret->nlp_rpi_hash_next = temp->nlp_rpi_hash_next;
+               temp->nlp_rpi_hash_next = NULL;
+               return temp;
+       } else {
+               return NULL;
+       }
+}
+
+/*
+ * This routine adds the node list entry to the
+ * ndlp hash table.
+ */
+void
+lpfc_addnode_rpi(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+                uint16_t rpi)
+{
+
+       uint32_t index;
+
+       index = LPFC_RPI_HASH_FUNC(rpi);
+       ndlp->nlp_rpi_hash_next = phba->fc_nlplookup[index];
+       phba->fc_nlplookup[index] = ndlp;
+       return;
+}
+
+void
+lpfc_nlp_init(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+                uint32_t did)
+{
+       memset(ndlp, 0, sizeof (struct lpfc_nodelist));
+       init_timer(&ndlp->nlp_tmofunc);
+       ndlp->nlp_tmofunc.function = lpfc_nodev_timeout;
+       ndlp->nlp_tmofunc.data = (unsigned long)ndlp;
+       init_timer(&ndlp->nlp_delayfunc);
+       ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
+       ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
+       ndlp->nlp_DID = did;
+       ndlp->nlp_phba = phba;
+       return;
+}
+
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
new file mode 100644 (file)
index 0000000..b5c023a
--- /dev/null
@@ -0,0 +1,2688 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_hw.h 1.29 2004/11/18 17:28:05EST sf_support Exp  $
+ */
+
+#ifndef  _H_LPFC_HW
+#define _H_LPFC_HW
+
+#define FDMI_DID        ((uint32_t)0xfffffa)
+#define NameServer_DID  ((uint32_t)0xfffffc)
+#define SCR_DID         ((uint32_t)0xfffffd)
+#define Fabric_DID      ((uint32_t)0xfffffe)
+#define Bcast_DID       ((uint32_t)0xffffff)
+#define Mask_DID        ((uint32_t)0xffffff)
+#define CT_DID_MASK     ((uint32_t)0xffff00)
+#define Fabric_DID_MASK ((uint32_t)0xfff000)
+#define WELL_KNOWN_DID_MASK ((uint32_t)0xfffff0)
+
+#define PT2PT_LocalID   ((uint32_t)1)
+#define PT2PT_RemoteID  ((uint32_t)2)
+
+#define FF_DEF_EDTOV          2000     /* Default E_D_TOV (2000ms) */
+#define FF_DEF_ALTOV            15     /* Default AL_TIME (15ms) */
+#define FF_DEF_RATOV             2     /* Default RA_TOV (2s) */
+#define FF_DEF_ARBTOV         1900     /* Default ARB_TOV (1900ms) */
+
+#define LPFC_BUF_RING0        64       /* Number of buffers to post to RING
+                                          0 */
+
+#define FCELSSIZE             1024     /* maximum ELS transfer size */
+
+#define LPFC_FCP_RING            0     /* ring 2 for FCP initiator commands */
+#define LPFC_IP_RING             1     /* ring 1 for IP commands */
+#define LPFC_ELS_RING            2     /* ring 0 for ELS commands */
+#define LPFC_FCP_NEXT_RING       3
+
+#define SLI2_IOCB_CMD_R0_ENTRIES    172        /* SLI-2 FCP command ring entries */
+#define SLI2_IOCB_RSP_R0_ENTRIES    134        /* SLI-2 FCP response ring entries */
+#define SLI2_IOCB_CMD_R1_ENTRIES      4        /* SLI-2 IP command ring entries */
+#define SLI2_IOCB_RSP_R1_ENTRIES      4        /* SLI-2 IP response ring entries */
+#define SLI2_IOCB_CMD_R1XTRA_ENTRIES 36        /* SLI-2 extra FCP cmd ring entries */
+#define SLI2_IOCB_RSP_R1XTRA_ENTRIES 52        /* SLI-2 extra FCP rsp ring entries */
+#define SLI2_IOCB_CMD_R2_ENTRIES     20        /* SLI-2 ELS command ring entries */
+#define SLI2_IOCB_RSP_R2_ENTRIES     20        /* SLI-2 ELS response ring entries */
+#define SLI2_IOCB_CMD_R3_ENTRIES      0
+#define SLI2_IOCB_RSP_R3_ENTRIES      0
+#define SLI2_IOCB_CMD_R3XTRA_ENTRIES 24
+#define SLI2_IOCB_RSP_R3XTRA_ENTRIES 32
+
+/* Common Transport structures and definitions */
+
+union CtRevisionId {
+       /* Structure is in Big Endian format */
+       struct {
+               uint32_t Revision:8;
+               uint32_t InId:24;
+       } bits;
+       uint32_t word;
+};
+
+union CtCommandResponse {
+       /* Structure is in Big Endian format */
+       struct {
+               uint32_t CmdRsp:16;
+               uint32_t Size:16;
+       } bits;
+       uint32_t word;
+};
+
+struct lpfc_sli_ct_request {
+       /* Structure is in Big Endian format */
+       union CtRevisionId RevisionId;
+       uint8_t FsType;
+       uint8_t FsSubType;
+       uint8_t Options;
+       uint8_t Rsrvd1;
+       union CtCommandResponse CommandResponse;
+       uint8_t Rsrvd2;
+       uint8_t ReasonCode;
+       uint8_t Explanation;
+       uint8_t VendorUnique;
+
+       union {
+               uint32_t PortID;
+               struct gid {
+                       uint8_t PortType;       /* for GID_PT requests */
+                       uint8_t DomainScope;
+                       uint8_t AreaScope;
+                       uint8_t Fc4Type;        /* for GID_FT requests */
+               } gid;
+               struct rft {
+                       uint32_t PortId;        /* For RFT_ID requests */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+                       uint32_t rsvd0:16;
+                       uint32_t rsvd1:7;
+                       uint32_t fcpReg:1;      /* Type 8 */
+                       uint32_t rsvd2:2;
+                       uint32_t ipReg:1;       /* Type 5 */
+                       uint32_t rsvd3:5;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+                       uint32_t rsvd0:16;
+                       uint32_t fcpReg:1;      /* Type 8 */
+                       uint32_t rsvd1:7;
+                       uint32_t rsvd3:5;
+                       uint32_t ipReg:1;       /* Type 5 */
+                       uint32_t rsvd2:2;
+#endif
+
+                       uint32_t rsvd[7];
+               } rft;
+               struct rnn {
+                       uint32_t PortId;        /* For RNN_ID requests */
+                       uint8_t wwnn[8];
+               } rnn;
+               struct rsnn {   /* For RSNN_ID requests */
+                       uint8_t wwnn[8];
+                       uint8_t len;
+                       uint8_t symbname[255];
+               } rsnn;
+       } un;
+};
+
+#define  SLI_CT_REVISION        1
+#define  GID_REQUEST_SZ         (sizeof(struct lpfc_sli_ct_request) - 260)
+#define  RFT_REQUEST_SZ         (sizeof(struct lpfc_sli_ct_request) - 228)
+#define  RNN_REQUEST_SZ         (sizeof(struct lpfc_sli_ct_request) - 252)
+#define  RSNN_REQUEST_SZ        (sizeof(struct lpfc_sli_ct_request))
+
+/*
+ * FsType Definitions
+ */
+
+#define  SLI_CT_MANAGEMENT_SERVICE        0xFA
+#define  SLI_CT_TIME_SERVICE              0xFB
+#define  SLI_CT_DIRECTORY_SERVICE         0xFC
+#define  SLI_CT_FABRIC_CONTROLLER_SERVICE 0xFD
+
+/*
+ * Directory Service Subtypes
+ */
+
+#define  SLI_CT_DIRECTORY_NAME_SERVER     0x02
+
+/*
+ * Response Codes
+ */
+
+#define  SLI_CT_RESPONSE_FS_RJT           0x8001
+#define  SLI_CT_RESPONSE_FS_ACC           0x8002
+
+/*
+ * Reason Codes
+ */
+
+#define  SLI_CT_NO_ADDITIONAL_EXPL       0x0
+#define  SLI_CT_INVALID_COMMAND           0x01
+#define  SLI_CT_INVALID_VERSION           0x02
+#define  SLI_CT_LOGICAL_ERROR             0x03
+#define  SLI_CT_INVALID_IU_SIZE           0x04
+#define  SLI_CT_LOGICAL_BUSY              0x05
+#define  SLI_CT_PROTOCOL_ERROR            0x07
+#define  SLI_CT_UNABLE_TO_PERFORM_REQ     0x09
+#define  SLI_CT_REQ_NOT_SUPPORTED         0x0b
+#define  SLI_CT_HBA_INFO_NOT_REGISTERED          0x10
+#define  SLI_CT_MULTIPLE_HBA_ATTR_OF_SAME_TYPE  0x11
+#define  SLI_CT_INVALID_HBA_ATTR_BLOCK_LEN      0x12
+#define  SLI_CT_HBA_ATTR_NOT_PRESENT     0x13
+#define  SLI_CT_PORT_INFO_NOT_REGISTERED  0x20
+#define  SLI_CT_MULTIPLE_PORT_ATTR_OF_SAME_TYPE 0x21
+#define  SLI_CT_INVALID_PORT_ATTR_BLOCK_LEN     0x22
+#define  SLI_CT_VENDOR_UNIQUE             0xff
+
+/*
+ * Name Server SLI_CT_UNABLE_TO_PERFORM_REQ Explanations
+ */
+
+#define  SLI_CT_NO_PORT_ID                0x01
+#define  SLI_CT_NO_PORT_NAME              0x02
+#define  SLI_CT_NO_NODE_NAME              0x03
+#define  SLI_CT_NO_CLASS_OF_SERVICE       0x04
+#define  SLI_CT_NO_IP_ADDRESS             0x05
+#define  SLI_CT_NO_IPA                    0x06
+#define  SLI_CT_NO_FC4_TYPES              0x07
+#define  SLI_CT_NO_SYMBOLIC_PORT_NAME     0x08
+#define  SLI_CT_NO_SYMBOLIC_NODE_NAME     0x09
+#define  SLI_CT_NO_PORT_TYPE              0x0A
+#define  SLI_CT_ACCESS_DENIED             0x10
+#define  SLI_CT_INVALID_PORT_ID           0x11
+#define  SLI_CT_DATABASE_EMPTY            0x12
+
+/*
+ * Name Server Command Codes
+ */
+
+#define  SLI_CTNS_GA_NXT      0x0100
+#define  SLI_CTNS_GPN_ID      0x0112
+#define  SLI_CTNS_GNN_ID      0x0113
+#define  SLI_CTNS_GCS_ID      0x0114
+#define  SLI_CTNS_GFT_ID      0x0117
+#define  SLI_CTNS_GSPN_ID     0x0118
+#define  SLI_CTNS_GPT_ID      0x011A
+#define  SLI_CTNS_GID_PN      0x0121
+#define  SLI_CTNS_GID_NN      0x0131
+#define  SLI_CTNS_GIP_NN      0x0135
+#define  SLI_CTNS_GIPA_NN     0x0136
+#define  SLI_CTNS_GSNN_NN     0x0139
+#define  SLI_CTNS_GNN_IP      0x0153
+#define  SLI_CTNS_GIPA_IP     0x0156
+#define  SLI_CTNS_GID_FT      0x0171
+#define  SLI_CTNS_GID_PT      0x01A1
+#define  SLI_CTNS_RPN_ID      0x0212
+#define  SLI_CTNS_RNN_ID      0x0213
+#define  SLI_CTNS_RCS_ID      0x0214
+#define  SLI_CTNS_RFT_ID      0x0217
+#define  SLI_CTNS_RSPN_ID     0x0218
+#define  SLI_CTNS_RPT_ID      0x021A
+#define  SLI_CTNS_RIP_NN      0x0235
+#define  SLI_CTNS_RIPA_NN     0x0236
+#define  SLI_CTNS_RSNN_NN     0x0239
+#define  SLI_CTNS_DA_ID       0x0300
+
+/*
+ * Port Types
+ */
+
+#define  SLI_CTPT_N_PORT      0x01
+#define  SLI_CTPT_NL_PORT     0x02
+#define  SLI_CTPT_FNL_PORT    0x03
+#define  SLI_CTPT_IP          0x04
+#define  SLI_CTPT_FCP         0x08
+#define  SLI_CTPT_NX_PORT     0x7F
+#define  SLI_CTPT_F_PORT      0x81
+#define  SLI_CTPT_FL_PORT     0x82
+#define  SLI_CTPT_E_PORT      0x84
+
+#define SLI_CT_LAST_ENTRY     0x80000000
+
+/* Fibre Channel Service Parameter definitions */
+
+#define FC_PH_4_0   6          /* FC-PH version 4.0 */
+#define FC_PH_4_1   7          /* FC-PH version 4.1 */
+#define FC_PH_4_2   8          /* FC-PH version 4.2 */
+#define FC_PH_4_3   9          /* FC-PH version 4.3 */
+
+#define FC_PH_LOW   8          /* Lowest supported FC-PH version */
+#define FC_PH_HIGH  9          /* Highest supported FC-PH version */
+#define FC_PH3   0x20          /* FC-PH-3 version */
+
+#define FF_FRAME_SIZE     2048
+
+struct lpfc_name {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t nameType:4;     /* FC Word 0, bit 28:31 */
+       uint8_t IEEEextMsn:4;   /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t IEEEextMsn:4;   /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */
+       uint8_t nameType:4;     /* FC Word 0, bit 28:31 */
+#endif
+
+#define NAME_IEEE           0x1        /* IEEE name - nameType */
+#define NAME_IEEE_EXT       0x2        /* IEEE extended name */
+#define NAME_FC_TYPE        0x3        /* FC native name type */
+#define NAME_IP_TYPE        0x4        /* IP address */
+#define NAME_CCITT_TYPE     0xC
+#define NAME_CCITT_GR_TYPE  0xE
+       uint8_t IEEEextLsb;     /* FC Word 0, bit 16:23, IEEE extended Lsb */
+       uint8_t IEEE[6];        /* FC IEEE address */
+};
+
+struct csp {
+       uint8_t fcphHigh;       /* FC Word 0, byte 0 */
+       uint8_t fcphLow;
+       uint8_t bbCreditMsb;
+       uint8_t bbCreditlsb;    /* FC Word 0, byte 3 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t increasingOffset:1;    /* FC Word 1, bit 31 */
+       uint16_t randomOffset:1;        /* FC Word 1, bit 30 */
+       uint16_t word1Reserved2:1;      /* FC Word 1, bit 29 */
+       uint16_t fPort:1;       /* FC Word 1, bit 28 */
+       uint16_t altBbCredit:1; /* FC Word 1, bit 27 */
+       uint16_t edtovResolution:1;     /* FC Word 1, bit 26 */
+       uint16_t multicast:1;   /* FC Word 1, bit 25 */
+       uint16_t broadcast:1;   /* FC Word 1, bit 24 */
+
+       uint16_t huntgroup:1;   /* FC Word 1, bit 23 */
+       uint16_t simplex:1;     /* FC Word 1, bit 22 */
+       uint16_t word1Reserved1:3;      /* FC Word 1, bit 21:19 */
+       uint16_t dhd:1;         /* FC Word 1, bit 18 */
+       uint16_t contIncSeqCnt:1;       /* FC Word 1, bit 17 */
+       uint16_t payloadlength:1;       /* FC Word 1, bit 16 */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t broadcast:1;   /* FC Word 1, bit 24 */
+       uint16_t multicast:1;   /* FC Word 1, bit 25 */
+       uint16_t edtovResolution:1;     /* FC Word 1, bit 26 */
+       uint16_t altBbCredit:1; /* FC Word 1, bit 27 */
+       uint16_t fPort:1;       /* FC Word 1, bit 28 */
+       uint16_t word1Reserved2:1;      /* FC Word 1, bit 29 */
+       uint16_t randomOffset:1;        /* FC Word 1, bit 30 */
+       uint16_t increasingOffset:1;    /* FC Word 1, bit 31 */
+
+       uint16_t payloadlength:1;       /* FC Word 1, bit 16 */
+       uint16_t contIncSeqCnt:1;       /* FC Word 1, bit 17 */
+       uint16_t dhd:1;         /* FC Word 1, bit 18 */
+       uint16_t word1Reserved1:3;      /* FC Word 1, bit 21:19 */
+       uint16_t simplex:1;     /* FC Word 1, bit 22 */
+       uint16_t huntgroup:1;   /* FC Word 1, bit 23 */
+#endif
+
+       uint8_t bbRcvSizeMsb;   /* Upper nibble is reserved */
+       uint8_t bbRcvSizeLsb;   /* FC Word 1, byte 3 */
+       union {
+               struct {
+                       uint8_t word2Reserved1; /* FC Word 2 byte 0 */
+
+                       uint8_t totalConcurrSeq;        /* FC Word 2 byte 1 */
+                       uint8_t roByCategoryMsb;        /* FC Word 2 byte 2 */
+
+                       uint8_t roByCategoryLsb;        /* FC Word 2 byte 3 */
+               } nPort;
+               uint32_t r_a_tov;       /* R_A_TOV must be in B.E. format */
+       } w2;
+
+       uint32_t e_d_tov;       /* E_D_TOV must be in B.E. format */
+};
+
+struct class_parms {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t classValid:1;   /* FC Word 0, bit 31 */
+       uint8_t intermix:1;     /* FC Word 0, bit 30 */
+       uint8_t stackedXparent:1;       /* FC Word 0, bit 29 */
+       uint8_t stackedLockDown:1;      /* FC Word 0, bit 28 */
+       uint8_t seqDelivery:1;  /* FC Word 0, bit 27 */
+       uint8_t word0Reserved1:3;       /* FC Word 0, bit 24:26 */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t word0Reserved1:3;       /* FC Word 0, bit 24:26 */
+       uint8_t seqDelivery:1;  /* FC Word 0, bit 27 */
+       uint8_t stackedLockDown:1;      /* FC Word 0, bit 28 */
+       uint8_t stackedXparent:1;       /* FC Word 0, bit 29 */
+       uint8_t intermix:1;     /* FC Word 0, bit 30 */
+       uint8_t classValid:1;   /* FC Word 0, bit 31 */
+
+#endif
+
+       uint8_t word0Reserved2; /* FC Word 0, bit 16:23 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t iCtlXidReAssgn:2;       /* FC Word 0, Bit 14:15 */
+       uint8_t iCtlInitialPa:2;        /* FC Word 0, bit 12:13 */
+       uint8_t iCtlAck0capable:1;      /* FC Word 0, bit 11 */
+       uint8_t iCtlAckNcapable:1;      /* FC Word 0, bit 10 */
+       uint8_t word0Reserved3:2;       /* FC Word 0, bit  8: 9 */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t word0Reserved3:2;       /* FC Word 0, bit  8: 9 */
+       uint8_t iCtlAckNcapable:1;      /* FC Word 0, bit 10 */
+       uint8_t iCtlAck0capable:1;      /* FC Word 0, bit 11 */
+       uint8_t iCtlInitialPa:2;        /* FC Word 0, bit 12:13 */
+       uint8_t iCtlXidReAssgn:2;       /* FC Word 0, Bit 14:15 */
+#endif
+
+       uint8_t word0Reserved4; /* FC Word 0, bit  0: 7 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t rCtlAck0capable:1;      /* FC Word 1, bit 31 */
+       uint8_t rCtlAckNcapable:1;      /* FC Word 1, bit 30 */
+       uint8_t rCtlXidInterlck:1;      /* FC Word 1, bit 29 */
+       uint8_t rCtlErrorPolicy:2;      /* FC Word 1, bit 27:28 */
+       uint8_t word1Reserved1:1;       /* FC Word 1, bit 26 */
+       uint8_t rCtlCatPerSeq:2;        /* FC Word 1, bit 24:25 */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t rCtlCatPerSeq:2;        /* FC Word 1, bit 24:25 */
+       uint8_t word1Reserved1:1;       /* FC Word 1, bit 26 */
+       uint8_t rCtlErrorPolicy:2;      /* FC Word 1, bit 27:28 */
+       uint8_t rCtlXidInterlck:1;      /* FC Word 1, bit 29 */
+       uint8_t rCtlAckNcapable:1;      /* FC Word 1, bit 30 */
+       uint8_t rCtlAck0capable:1;      /* FC Word 1, bit 31 */
+#endif
+
+       uint8_t word1Reserved2; /* FC Word 1, bit 16:23 */
+       uint8_t rcvDataSizeMsb; /* FC Word 1, bit  8:15 */
+       uint8_t rcvDataSizeLsb; /* FC Word 1, bit  0: 7 */
+
+       uint8_t concurrentSeqMsb;       /* FC Word 2, bit 24:31 */
+       uint8_t concurrentSeqLsb;       /* FC Word 2, bit 16:23 */
+       uint8_t EeCreditSeqMsb; /* FC Word 2, bit  8:15 */
+       uint8_t EeCreditSeqLsb; /* FC Word 2, bit  0: 7 */
+
+       uint8_t openSeqPerXchgMsb;      /* FC Word 3, bit 24:31 */
+       uint8_t openSeqPerXchgLsb;      /* FC Word 3, bit 16:23 */
+       uint8_t word3Reserved1; /* Fc Word 3, bit  8:15 */
+       uint8_t word3Reserved2; /* Fc Word 3, bit  0: 7 */
+};
+
+struct serv_parm {     /* Structure is in Big Endian format */
+       struct csp cmn;
+       struct lpfc_name portName;
+       struct lpfc_name nodeName;
+       struct class_parms cls1;
+       struct class_parms cls2;
+       struct class_parms cls3;
+       struct class_parms cls4;
+       uint8_t vendorVersion[16];
+};
+
+/*
+ *  Extended Link Service LS_COMMAND codes (Payload Word 0)
+ */
+#ifdef __BIG_ENDIAN_BITFIELD
+#define ELS_CMD_MASK      0xffff0000
+#define ELS_RSP_MASK      0xff000000
+#define ELS_CMD_LS_RJT    0x01000000
+#define ELS_CMD_ACC       0x02000000
+#define ELS_CMD_PLOGI     0x03000000
+#define ELS_CMD_FLOGI     0x04000000
+#define ELS_CMD_LOGO      0x05000000
+#define ELS_CMD_ABTX      0x06000000
+#define ELS_CMD_RCS       0x07000000
+#define ELS_CMD_RES       0x08000000
+#define ELS_CMD_RSS       0x09000000
+#define ELS_CMD_RSI       0x0A000000
+#define ELS_CMD_ESTS      0x0B000000
+#define ELS_CMD_ESTC      0x0C000000
+#define ELS_CMD_ADVC      0x0D000000
+#define ELS_CMD_RTV       0x0E000000
+#define ELS_CMD_RLS       0x0F000000
+#define ELS_CMD_ECHO      0x10000000
+#define ELS_CMD_TEST      0x11000000
+#define ELS_CMD_RRQ       0x12000000
+#define ELS_CMD_PRLI      0x20100014
+#define ELS_CMD_PRLO      0x21100014
+#define ELS_CMD_PDISC     0x50000000
+#define ELS_CMD_FDISC     0x51000000
+#define ELS_CMD_ADISC     0x52000000
+#define ELS_CMD_FARP      0x54000000
+#define ELS_CMD_FARPR     0x55000000
+#define ELS_CMD_FAN       0x60000000
+#define ELS_CMD_RSCN      0x61040000
+#define ELS_CMD_SCR       0x62000000
+#define ELS_CMD_RNID      0x78000000
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+#define ELS_CMD_MASK      0xffff
+#define ELS_RSP_MASK      0xff
+#define ELS_CMD_LS_RJT    0x01
+#define ELS_CMD_ACC       0x02
+#define ELS_CMD_PLOGI     0x03
+#define ELS_CMD_FLOGI     0x04
+#define ELS_CMD_LOGO      0x05
+#define ELS_CMD_ABTX      0x06
+#define ELS_CMD_RCS       0x07
+#define ELS_CMD_RES       0x08
+#define ELS_CMD_RSS       0x09
+#define ELS_CMD_RSI       0x0A
+#define ELS_CMD_ESTS      0x0B
+#define ELS_CMD_ESTC      0x0C
+#define ELS_CMD_ADVC      0x0D
+#define ELS_CMD_RTV       0x0E
+#define ELS_CMD_RLS       0x0F
+#define ELS_CMD_ECHO      0x10
+#define ELS_CMD_TEST      0x11
+#define ELS_CMD_RRQ       0x12
+#define ELS_CMD_PRLI      0x14001020
+#define ELS_CMD_PRLO      0x14001021
+#define ELS_CMD_PDISC     0x50
+#define ELS_CMD_FDISC     0x51
+#define ELS_CMD_ADISC     0x52
+#define ELS_CMD_FARP      0x54
+#define ELS_CMD_FARPR     0x55
+#define ELS_CMD_FAN       0x60
+#define ELS_CMD_RSCN      0x0461
+#define ELS_CMD_SCR       0x62
+#define ELS_CMD_RNID      0x78
+#endif
+
+/*
+ *  LS_RJT Payload Definition
+ */
+
+struct ls_rjt {        /* Structure is in Big Endian format */
+       union {
+               uint32_t lsRjtError;
+               struct {
+                       uint8_t lsRjtRsvd0;     /* FC Word 0, bit 24:31 */
+
+                       uint8_t lsRjtRsnCode;   /* FC Word 0, bit 16:23 */
+                       /* LS_RJT reason codes */
+#define LSRJT_INVALID_CMD     0x01
+#define LSRJT_LOGICAL_ERR     0x03
+#define LSRJT_LOGICAL_BSY     0x05
+#define LSRJT_PROTOCOL_ERR    0x07
+#define LSRJT_UNABLE_TPC      0x09     /* Unable to perform command */
+#define LSRJT_CMD_UNSUPPORTED 0x0B
+#define LSRJT_VENDOR_UNIQUE   0xFF     /* See Byte 3 */
+
+                       uint8_t lsRjtRsnCodeExp; /* FC Word 0, bit 8:15 */
+                       /* LS_RJT reason explanation */
+#define LSEXP_NOTHING_MORE      0x00
+#define LSEXP_SPARM_OPTIONS     0x01
+#define LSEXP_SPARM_ICTL        0x03
+#define LSEXP_SPARM_RCTL        0x05
+#define LSEXP_SPARM_RCV_SIZE    0x07
+#define LSEXP_SPARM_CONCUR_SEQ  0x09
+#define LSEXP_SPARM_CREDIT      0x0B
+#define LSEXP_INVALID_PNAME     0x0D
+#define LSEXP_INVALID_NNAME     0x0E
+#define LSEXP_INVALID_CSP       0x0F
+#define LSEXP_INVALID_ASSOC_HDR 0x11
+#define LSEXP_ASSOC_HDR_REQ     0x13
+#define LSEXP_INVALID_O_SID     0x15
+#define LSEXP_INVALID_OX_RX     0x17
+#define LSEXP_CMD_IN_PROGRESS   0x19
+#define LSEXP_INVALID_NPORT_ID  0x1F
+#define LSEXP_INVALID_SEQ_ID    0x21
+#define LSEXP_INVALID_XCHG      0x23
+#define LSEXP_INACTIVE_XCHG     0x25
+#define LSEXP_RQ_REQUIRED       0x27
+#define LSEXP_OUT_OF_RESOURCE   0x29
+#define LSEXP_CANT_GIVE_DATA    0x2A
+#define LSEXP_REQ_UNSUPPORTED   0x2C
+                       uint8_t vendorUnique;   /* FC Word 0, bit  0: 7 */
+               } b;
+       } un;
+};
+
+/*
+ *  N_Port Login (FLOGO/PLOGO Request) Payload Definition
+ */
+
+typedef struct _LOGO {         /* Structure is in Big Endian format */
+       union {
+               uint32_t nPortId32;     /* Access nPortId as a word */
+               struct {
+                       uint8_t word1Reserved1; /* FC Word 1, bit 31:24 */
+                       uint8_t nPortIdByte0;   /* N_port  ID bit 16:23 */
+                       uint8_t nPortIdByte1;   /* N_port  ID bit  8:15 */
+                       uint8_t nPortIdByte2;   /* N_port  ID bit  0: 7 */
+               } b;
+       } un;
+       struct lpfc_name portName;      /* N_port name field */
+} LOGO;
+
+/*
+ *  FCP Login (PRLI Request / ACC) Payload Definition
+ */
+
+#define PRLX_PAGE_LEN   0x10
+#define TPRLO_PAGE_LEN  0x14
+
+typedef struct _PRLI {         /* Structure is in Big Endian format */
+       uint8_t prliType;       /* FC Parm Word 0, bit 24:31 */
+
+#define PRLI_FCP_TYPE 0x08
+       uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t origProcAssocV:1;       /* FC Parm Word 0, bit 15 */
+       uint8_t respProcAssocV:1;       /* FC Parm Word 0, bit 14 */
+       uint8_t estabImagePair:1;       /* FC Parm Word 0, bit 13 */
+
+       /*    ACC = imagePairEstablished */
+       uint8_t word0Reserved2:1;       /* FC Parm Word 0, bit 12 */
+       uint8_t acceptRspCode:4;        /* FC Parm Word 0, bit 8:11, ACC ONLY */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t acceptRspCode:4;        /* FC Parm Word 0, bit 8:11, ACC ONLY */
+       uint8_t word0Reserved2:1;       /* FC Parm Word 0, bit 12 */
+       uint8_t estabImagePair:1;       /* FC Parm Word 0, bit 13 */
+       uint8_t respProcAssocV:1;       /* FC Parm Word 0, bit 14 */
+       uint8_t origProcAssocV:1;       /* FC Parm Word 0, bit 15 */
+       /*    ACC = imagePairEstablished */
+#endif
+
+#define PRLI_REQ_EXECUTED     0x1      /* acceptRspCode */
+#define PRLI_NO_RESOURCES     0x2
+#define PRLI_INIT_INCOMPLETE  0x3
+#define PRLI_NO_SUCH_PA       0x4
+#define PRLI_PREDEF_CONFIG    0x5
+#define PRLI_PARTIAL_SUCCESS  0x6
+#define PRLI_INVALID_PAGE_CNT 0x7
+       uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */
+
+       uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */
+
+       uint32_t respProcAssoc; /* FC Parm Word 2, bit 0:31 */
+
+       uint8_t word3Reserved1; /* FC Parm Word 3, bit 24:31 */
+       uint8_t word3Reserved2; /* FC Parm Word 3, bit 16:23 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t Word3bit15Resved:1;    /* FC Parm Word 3, bit 15 */
+       uint16_t Word3bit14Resved:1;    /* FC Parm Word 3, bit 14 */
+       uint16_t Word3bit13Resved:1;    /* FC Parm Word 3, bit 13 */
+       uint16_t Word3bit12Resved:1;    /* FC Parm Word 3, bit 12 */
+       uint16_t Word3bit11Resved:1;    /* FC Parm Word 3, bit 11 */
+       uint16_t Word3bit10Resved:1;    /* FC Parm Word 3, bit 10 */
+       uint16_t TaskRetryIdReq:1;      /* FC Parm Word 3, bit  9 */
+       uint16_t Retry:1;       /* FC Parm Word 3, bit  8 */
+       uint16_t ConfmComplAllowed:1;   /* FC Parm Word 3, bit  7 */
+       uint16_t dataOverLay:1; /* FC Parm Word 3, bit  6 */
+       uint16_t initiatorFunc:1;       /* FC Parm Word 3, bit  5 */
+       uint16_t targetFunc:1;  /* FC Parm Word 3, bit  4 */
+       uint16_t cmdDataMixEna:1;       /* FC Parm Word 3, bit  3 */
+       uint16_t dataRspMixEna:1;       /* FC Parm Word 3, bit  2 */
+       uint16_t readXferRdyDis:1;      /* FC Parm Word 3, bit  1 */
+       uint16_t writeXferRdyDis:1;     /* FC Parm Word 3, bit  0 */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t Retry:1;       /* FC Parm Word 3, bit  8 */
+       uint16_t TaskRetryIdReq:1;      /* FC Parm Word 3, bit  9 */
+       uint16_t Word3bit10Resved:1;    /* FC Parm Word 3, bit 10 */
+       uint16_t Word3bit11Resved:1;    /* FC Parm Word 3, bit 11 */
+       uint16_t Word3bit12Resved:1;    /* FC Parm Word 3, bit 12 */
+       uint16_t Word3bit13Resved:1;    /* FC Parm Word 3, bit 13 */
+       uint16_t Word3bit14Resved:1;    /* FC Parm Word 3, bit 14 */
+       uint16_t Word3bit15Resved:1;    /* FC Parm Word 3, bit 15 */
+       uint16_t writeXferRdyDis:1;     /* FC Parm Word 3, bit  0 */
+       uint16_t readXferRdyDis:1;      /* FC Parm Word 3, bit  1 */
+       uint16_t dataRspMixEna:1;       /* FC Parm Word 3, bit  2 */
+       uint16_t cmdDataMixEna:1;       /* FC Parm Word 3, bit  3 */
+       uint16_t targetFunc:1;  /* FC Parm Word 3, bit  4 */
+       uint16_t initiatorFunc:1;       /* FC Parm Word 3, bit  5 */
+       uint16_t dataOverLay:1; /* FC Parm Word 3, bit  6 */
+       uint16_t ConfmComplAllowed:1;   /* FC Parm Word 3, bit  7 */
+#endif
+} PRLI;
+
+/*
+ *  FCP Logout (PRLO Request / ACC) Payload Definition
+ */
+
+typedef struct _PRLO {         /* Structure is in Big Endian format */
+       uint8_t prloType;       /* FC Parm Word 0, bit 24:31 */
+
+#define PRLO_FCP_TYPE  0x08
+       uint8_t word0Reserved1; /* FC Parm Word 0, bit 16:23 */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t origProcAssocV:1;       /* FC Parm Word 0, bit 15 */
+       uint8_t respProcAssocV:1;       /* FC Parm Word 0, bit 14 */
+       uint8_t word0Reserved2:2;       /* FC Parm Word 0, bit 12:13 */
+       uint8_t acceptRspCode:4;        /* FC Parm Word 0, bit 8:11, ACC ONLY */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t acceptRspCode:4;        /* FC Parm Word 0, bit 8:11, ACC ONLY */
+       uint8_t word0Reserved2:2;       /* FC Parm Word 0, bit 12:13 */
+       uint8_t respProcAssocV:1;       /* FC Parm Word 0, bit 14 */
+       uint8_t origProcAssocV:1;       /* FC Parm Word 0, bit 15 */
+#endif
+
+#define PRLO_REQ_EXECUTED     0x1      /* acceptRspCode */
+#define PRLO_NO_SUCH_IMAGE    0x4
+#define PRLO_INVALID_PAGE_CNT 0x7
+
+       uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */
+
+       uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */
+
+       uint32_t respProcAssoc; /* FC Parm Word 2, bit 0:31 */
+
+       uint32_t word3Reserved1;        /* FC Parm Word 3, bit 0:31 */
+} PRLO;
+
+typedef struct _ADISC {                /* Structure is in Big Endian format */
+       uint32_t hardAL_PA;
+       struct lpfc_name portName;
+       struct lpfc_name nodeName;
+       uint32_t DID;
+} ADISC;
+
+typedef struct _FARP {         /* Structure is in Big Endian format */
+       uint32_t Mflags:8;
+       uint32_t Odid:24;
+#define FARP_NO_ACTION          0      /* FARP information enclosed, no
+                                          action */
+#define FARP_MATCH_PORT         0x1    /* Match on Responder Port Name */
+#define FARP_MATCH_NODE         0x2    /* Match on Responder Node Name */
+#define FARP_MATCH_IP           0x4    /* Match on IP address, not supported */
+#define FARP_MATCH_IPV4         0x5    /* Match on IPV4 address, not
+                                          supported */
+#define FARP_MATCH_IPV6         0x6    /* Match on IPV6 address, not
+                                          supported */
+       uint32_t Rflags:8;
+       uint32_t Rdid:24;
+#define FARP_REQUEST_PLOGI      0x1    /* Request for PLOGI */
+#define FARP_REQUEST_FARPR      0x2    /* Request for FARP Response */
+       struct lpfc_name OportName;
+       struct lpfc_name OnodeName;
+       struct lpfc_name RportName;
+       struct lpfc_name RnodeName;
+       uint8_t Oipaddr[16];
+       uint8_t Ripaddr[16];
+} FARP;
+
+typedef struct _FAN {          /* Structure is in Big Endian format */
+       uint32_t Fdid;
+       struct lpfc_name FportName;
+       struct lpfc_name FnodeName;
+} FAN;
+
+typedef struct _SCR {          /* Structure is in Big Endian format */
+       uint8_t resvd1;
+       uint8_t resvd2;
+       uint8_t resvd3;
+       uint8_t Function;
+#define  SCR_FUNC_FABRIC     0x01
+#define  SCR_FUNC_NPORT      0x02
+#define  SCR_FUNC_FULL       0x03
+#define  SCR_CLEAR           0xff
+} SCR;
+
+typedef struct _RNID_TOP_DISC {
+       struct lpfc_name portName;
+       uint8_t resvd[8];
+       uint32_t unitType;
+#define RNID_HBA            0x7
+#define RNID_HOST           0xa
+#define RNID_DRIVER         0xd
+       uint32_t physPort;
+       uint32_t attachedNodes;
+       uint16_t ipVersion;
+#define RNID_IPV4           0x1
+#define RNID_IPV6           0x2
+       uint16_t UDPport;
+       uint8_t ipAddr[16];
+       uint16_t resvd1;
+       uint16_t flags;
+#define RNID_TD_SUPPORT     0x1
+#define RNID_LP_VALID       0x2
+} RNID_TOP_DISC;
+
+typedef struct _RNID {         /* Structure is in Big Endian format */
+       uint8_t Format;
+#define RNID_TOPOLOGY_DISC  0xdf
+       uint8_t CommonLen;
+       uint8_t resvd1;
+       uint8_t SpecificLen;
+       struct lpfc_name portName;
+       struct lpfc_name nodeName;
+       union {
+               RNID_TOP_DISC topologyDisc;     /* topology disc (0xdf) */
+       } un;
+} RNID;
+
+typedef struct _RRQ {          /* Structure is in Big Endian format */
+       uint32_t SID;
+       uint16_t Oxid;
+       uint16_t Rxid;
+       uint8_t resv[32];       /* optional association hdr */
+} RRQ;
+
+/* This is used for RSCN command */
+typedef struct _D_ID {         /* Structure is in Big Endian format */
+       union {
+               uint32_t word;
+               struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+                       uint8_t resv;
+                       uint8_t domain;
+                       uint8_t area;
+                       uint8_t id;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+                       uint8_t id;
+                       uint8_t area;
+                       uint8_t domain;
+                       uint8_t resv;
+#endif
+               } b;
+       } un;
+} D_ID;
+
+/*
+ *  Structure to define all ELS Payload types
+ */
+
+typedef struct _ELS_PKT {      /* Structure is in Big Endian format */
+       uint8_t elsCode;        /* FC Word 0, bit 24:31 */
+       uint8_t elsByte1;
+       uint8_t elsByte2;
+       uint8_t elsByte3;
+       union {
+               struct ls_rjt lsRjt;    /* Payload for LS_RJT ELS response */
+               struct serv_parm logi;  /* Payload for PLOGI/FLOGI/PDISC/ACC */
+               LOGO logo;      /* Payload for PLOGO/FLOGO/ACC */
+               PRLI prli;      /* Payload for PRLI/ACC */
+               PRLO prlo;      /* Payload for PRLO/ACC */
+               ADISC adisc;    /* Payload for ADISC/ACC */
+               FARP farp;      /* Payload for FARP/ACC */
+               FAN fan;        /* Payload for FAN */
+               SCR scr;        /* Payload for SCR/ACC */
+               RRQ rrq;        /* Payload for RRQ */
+               RNID rnid;      /* Payload for RNID */
+               uint8_t pad[128 - 4];   /* Pad out to payload of 128 bytes */
+       } un;
+} ELS_PKT;
+
+/*
+ * FDMI
+ * HBA MAnagement Operations Command Codes
+ */
+#define  SLI_MGMT_GRHL     0x100       /* Get registered HBA list */
+#define  SLI_MGMT_GHAT     0x101       /* Get HBA attributes */
+#define  SLI_MGMT_GRPL     0x102       /* Get registered Port list */
+#define  SLI_MGMT_GPAT     0x110       /* Get Port attributes */
+#define  SLI_MGMT_RHBA     0x200       /* Register HBA */
+#define  SLI_MGMT_RHAT     0x201       /* Register HBA atttributes */
+#define  SLI_MGMT_RPRT     0x210       /* Register Port */
+#define  SLI_MGMT_RPA      0x211       /* Register Port attributes */
+#define  SLI_MGMT_DHBA     0x300       /* De-register HBA */
+#define  SLI_MGMT_DPRT     0x310       /* De-register Port */
+
+/*
+ * Management Service Subtypes
+ */
+#define  SLI_CT_FDMI_Subtypes     0x10
+
+/*
+ * HBA Management Service Reject Code
+ */
+#define  REJECT_CODE             0x9   /* Unable to perform command request */
+
+/*
+ * HBA Management Service Reject Reason Code
+ * Please refer to the Reason Codes above
+ */
+
+/*
+ * HBA Attribute Types
+ */
+#define  NODE_NAME               0x1
+#define  MANUFACTURER            0x2
+#define  SERIAL_NUMBER           0x3
+#define  MODEL                   0x4
+#define  MODEL_DESCRIPTION       0x5
+#define  HARDWARE_VERSION        0x6
+#define  DRIVER_VERSION          0x7
+#define  OPTION_ROM_VERSION      0x8
+#define  FIRMWARE_VERSION        0x9
+#define  OS_NAME_VERSION        0xa
+#define  MAX_CT_PAYLOAD_LEN     0xb
+
+/*
+ * Port Attrubute Types
+ */
+#define  SUPPORTED_FC4_TYPES     0x1
+#define  SUPPORTED_SPEED         0x2
+#define  PORT_SPEED              0x3
+#define  MAX_FRAME_SIZE          0x4
+#define  OS_DEVICE_NAME          0x5
+#define  HOST_NAME               0x6
+
+union AttributesDef {
+       /* Structure is in Big Endian format */
+       struct {
+               uint32_t AttrType:16;
+               uint32_t AttrLen:16;
+       } bits;
+       uint32_t word;
+};
+
+
+/*
+ * HBA Attribute Entry (8 - 260 bytes)
+ */
+typedef struct {
+       union AttributesDef ad;
+       union {
+               uint32_t VendorSpecific;
+               uint8_t Manufacturer[64];
+               uint8_t SerialNumber[64];
+               uint8_t Model[256];
+               uint8_t ModelDescription[256];
+               uint8_t HardwareVersion[256];
+               uint8_t DriverVersion[256];
+               uint8_t OptionROMVersion[256];
+               uint8_t FirmwareVersion[256];
+               struct lpfc_name NodeName;
+               uint8_t SupportFC4Types[32];
+               uint32_t SupportSpeed;
+               uint32_t PortSpeed;
+               uint32_t MaxFrameSize;
+               uint8_t OsDeviceName[256];
+               uint8_t OsNameVersion[256];
+               uint32_t MaxCTPayloadLen;
+               uint8_t HostName[256];
+       } un;
+} ATTRIBUTE_ENTRY;
+
+/*
+ * HBA Attribute Block
+ */
+typedef struct {
+       uint32_t EntryCnt;      /* Number of HBA attribute entries */
+       ATTRIBUTE_ENTRY Entry;  /* Variable-length array */
+} ATTRIBUTE_BLOCK;
+
+/*
+ * Port Entry
+ */
+typedef struct {
+       struct lpfc_name PortName;
+} PORT_ENTRY;
+
+/*
+ * HBA Identifier
+ */
+typedef struct {
+       struct lpfc_name PortName;
+} HBA_IDENTIFIER;
+
+/*
+ * Registered Port List Format
+ */
+typedef struct {
+       uint32_t EntryCnt;
+       PORT_ENTRY pe;          /* Variable-length array */
+} REG_PORT_LIST;
+
+/*
+ * Register HBA(RHBA)
+ */
+typedef struct {
+       HBA_IDENTIFIER hi;
+       REG_PORT_LIST rpl;      /* variable-length array */
+/* ATTRIBUTE_BLOCK   ab; */
+} REG_HBA;
+
+/*
+ * Register HBA Attributes (RHAT)
+ */
+typedef struct {
+       struct lpfc_name HBA_PortName;
+       ATTRIBUTE_BLOCK ab;
+} REG_HBA_ATTRIBUTE;
+
+/*
+ * Register Port Attributes (RPA)
+ */
+typedef struct {
+       struct lpfc_name PortName;
+       ATTRIBUTE_BLOCK ab;
+} REG_PORT_ATTRIBUTE;
+
+/*
+ * Get Registered HBA List (GRHL) Accept Payload Format
+ */
+typedef struct {
+       uint32_t HBA__Entry_Cnt; /* Number of Registered HBA Identifiers */
+       struct lpfc_name HBA_PortName;  /* Variable-length array */
+} GRHL_ACC_PAYLOAD;
+
+/*
+ * Get Registered Port List (GRPL) Accept Payload Format
+ */
+typedef struct {
+       uint32_t RPL_Entry_Cnt; /* Number of Registered Port Entries */
+       PORT_ENTRY Reg_Port_Entry[1];   /* Variable-length array */
+} GRPL_ACC_PAYLOAD;
+
+/*
+ * Get Port Attributes (GPAT) Accept Payload Format
+ */
+
+typedef struct {
+       ATTRIBUTE_BLOCK pab;
+} GPAT_ACC_PAYLOAD;
+
+
+/*
+ *  Begin HBA configuration parameters.
+ *  The PCI configuration register BAR assignments are:
+ *  BAR0, offset 0x10 - SLIM base memory address
+ *  BAR1, offset 0x14 - SLIM base memory high address
+ *  BAR2, offset 0x18 - REGISTER base memory address
+ *  BAR3, offset 0x1c - REGISTER base memory high address
+ *  BAR4, offset 0x20 - BIU I/O registers
+ *  BAR5, offset 0x24 - REGISTER base io high address
+ */
+
+/* Number of rings currently used and available. */
+#define MAX_CONFIGURED_RINGS     3
+#define MAX_RINGS                4
+
+/* IOCB / Mailbox is owned by FireFly */
+#define OWN_CHIP        1
+
+/* IOCB / Mailbox is owned by Host */
+#define OWN_HOST        0
+
+/* Number of 4-byte words in an IOCB. */
+#define IOCB_WORD_SZ    8
+
+/* defines for type field in fc header */
+#define FC_ELS_DATA     0x1
+#define FC_LLC_SNAP     0x5
+#define FC_FCP_DATA     0x8
+#define FC_COMMON_TRANSPORT_ULP 0x20
+
+/* defines for rctl field in fc header */
+#define FC_DEV_DATA     0x0
+#define FC_UNSOL_CTL    0x2
+#define FC_SOL_CTL      0x3
+#define FC_UNSOL_DATA   0x4
+#define FC_FCP_CMND     0x6
+#define FC_ELS_REQ      0x22
+#define FC_ELS_RSP      0x23
+
+/* network headers for Dfctl field */
+#define FC_NET_HDR      0x20
+
+/* Start FireFly Register definitions */
+#define PCI_VENDOR_ID_EMULEX        0x10df
+#define PCI_DEVICE_ID_FIREFLY      0x1ae5
+#define PCI_DEVICE_ID_SUPERFLY      0xf700
+#define PCI_DEVICE_ID_DRAGONFLY     0xf800
+#define PCI_DEVICE_ID_RFLY          0xf095
+#define PCI_DEVICE_ID_PFLY          0xf098
+#define PCI_DEVICE_ID_TFLY          0xf0a5
+#define PCI_DEVICE_ID_CENTAUR       0xf900
+#define PCI_DEVICE_ID_PEGASUS       0xf980
+#define PCI_DEVICE_ID_THOR          0xfa00
+#define PCI_DEVICE_ID_VIPER         0xfb00
+#define PCI_DEVICE_ID_HELIOS        0xfd00
+#define PCI_DEVICE_ID_BMID          0xf0d5
+#define PCI_DEVICE_ID_BSMB          0xf0d1
+#define PCI_DEVICE_ID_ZEPHYR        0xfe00
+#define PCI_DEVICE_ID_ZMID          0xf0e5
+#define PCI_DEVICE_ID_ZSMB          0xf0e1
+#define PCI_DEVICE_ID_LP101        0xf0a1
+
+#define JEDEC_ID_ADDRESS            0x0080001c
+#define FIREFLY_JEDEC_ID            0x1ACC
+#define SUPERFLY_JEDEC_ID           0x0020
+#define DRAGONFLY_JEDEC_ID          0x0021
+#define DRAGONFLY_V2_JEDEC_ID       0x0025
+#define CENTAUR_2G_JEDEC_ID         0x0026
+#define CENTAUR_1G_JEDEC_ID         0x0028
+#define PEGASUS_ORION_JEDEC_ID      0x0036
+#define PEGASUS_JEDEC_ID            0x0038
+#define THOR_JEDEC_ID               0x0012
+#define HELIOS_JEDEC_ID             0x0364
+#define ZEPHYR_JEDEC_ID             0x0577
+#define VIPER_JEDEC_ID              0x4838
+
+#define JEDEC_ID_MASK               0x0FFFF000
+#define JEDEC_ID_SHIFT              12
+#define FC_JEDEC_ID(id)             ((id & JEDEC_ID_MASK) >> JEDEC_ID_SHIFT)
+
+typedef struct {               /* FireFly BIU registers */
+       uint32_t hostAtt;       /* See definitions for Host Attention
+                                  register */
+       uint32_t chipAtt;       /* See definitions for Chip Attention
+                                  register */
+       uint32_t hostStatus;    /* See definitions for Host Status register */
+       uint32_t hostControl;   /* See definitions for Host Control register */
+       uint32_t buiConfig;     /* See definitions for BIU configuration
+                                  register */
+} FF_REGS;
+
+/* IO Register size in bytes */
+#define FF_REG_AREA_SIZE       256
+
+/* Host Attention Register */
+
+#define HA_REG_OFFSET  0       /* Word offset from register base address */
+
+#define HA_R0RE_REQ    0x00000001      /* Bit  0 */
+#define HA_R0CE_RSP    0x00000002      /* Bit  1 */
+#define HA_R0ATT       0x00000008      /* Bit  3 */
+#define HA_R1RE_REQ    0x00000010      /* Bit  4 */
+#define HA_R1CE_RSP    0x00000020      /* Bit  5 */
+#define HA_R1ATT       0x00000080      /* Bit  7 */
+#define HA_R2RE_REQ    0x00000100      /* Bit  8 */
+#define HA_R2CE_RSP    0x00000200      /* Bit  9 */
+#define HA_R2ATT       0x00000800      /* Bit 11 */
+#define HA_R3RE_REQ    0x00001000      /* Bit 12 */
+#define HA_R3CE_RSP    0x00002000      /* Bit 13 */
+#define HA_R3ATT       0x00008000      /* Bit 15 */
+#define HA_LATT        0x20000000      /* Bit 29 */
+#define HA_MBATT       0x40000000      /* Bit 30 */
+#define HA_ERATT       0x80000000      /* Bit 31 */
+
+#define HA_RXRE_REQ    0x00000001      /* Bit  0 */
+#define HA_RXCE_RSP    0x00000002      /* Bit  1 */
+#define HA_RXATT       0x00000008      /* Bit  3 */
+#define HA_RXMASK      0x0000000f
+
+/* Chip Attention Register */
+
+#define CA_REG_OFFSET  1       /* Word offset from register base address */
+
+#define CA_R0CE_REQ    0x00000001      /* Bit  0 */
+#define CA_R0RE_RSP    0x00000002      /* Bit  1 */
+#define CA_R0ATT       0x00000008      /* Bit  3 */
+#define CA_R1CE_REQ    0x00000010      /* Bit  4 */
+#define CA_R1RE_RSP    0x00000020      /* Bit  5 */
+#define CA_R1ATT       0x00000080      /* Bit  7 */
+#define CA_R2CE_REQ    0x00000100      /* Bit  8 */
+#define CA_R2RE_RSP    0x00000200      /* Bit  9 */
+#define CA_R2ATT       0x00000800      /* Bit 11 */
+#define CA_R3CE_REQ    0x00001000      /* Bit 12 */
+#define CA_R3RE_RSP    0x00002000      /* Bit 13 */
+#define CA_R3ATT       0x00008000      /* Bit 15 */
+#define CA_MBATT       0x40000000      /* Bit 30 */
+
+/* Host Status Register */
+
+#define HS_REG_OFFSET  2       /* Word offset from register base address */
+
+#define HS_MBRDY       0x00400000      /* Bit 22 */
+#define HS_FFRDY       0x00800000      /* Bit 23 */
+#define HS_FFER8       0x01000000      /* Bit 24 */
+#define HS_FFER7       0x02000000      /* Bit 25 */
+#define HS_FFER6       0x04000000      /* Bit 26 */
+#define HS_FFER5       0x08000000      /* Bit 27 */
+#define HS_FFER4       0x10000000      /* Bit 28 */
+#define HS_FFER3       0x20000000      /* Bit 29 */
+#define HS_FFER2       0x40000000      /* Bit 30 */
+#define HS_FFER1       0x80000000      /* Bit 31 */
+#define HS_FFERM       0xFF000000      /* Mask for error bits 31:24 */
+
+/* Host Control Register */
+
+#define HC_REG_OFFSET  3       /* Word offset from register base address */
+
+#define HC_MBINT_ENA   0x00000001      /* Bit  0 */
+#define HC_R0INT_ENA   0x00000002      /* Bit  1 */
+#define HC_R1INT_ENA   0x00000004      /* Bit  2 */
+#define HC_R2INT_ENA   0x00000008      /* Bit  3 */
+#define HC_R3INT_ENA   0x00000010      /* Bit  4 */
+#define HC_INITHBI     0x02000000      /* Bit 25 */
+#define HC_INITMB      0x04000000      /* Bit 26 */
+#define HC_INITFF      0x08000000      /* Bit 27 */
+#define HC_LAINT_ENA   0x20000000      /* Bit 29 */
+#define HC_ERINT_ENA   0x80000000      /* Bit 31 */
+
+/* Mailbox Commands */
+#define MBX_SHUTDOWN        0x00       /* terminate testing */
+#define MBX_LOAD_SM         0x01
+#define MBX_READ_NV         0x02
+#define MBX_WRITE_NV        0x03
+#define MBX_RUN_BIU_DIAG    0x04
+#define MBX_INIT_LINK       0x05
+#define MBX_DOWN_LINK       0x06
+#define MBX_CONFIG_LINK     0x07
+#define MBX_CONFIG_RING     0x09
+#define MBX_RESET_RING      0x0A
+#define MBX_READ_CONFIG     0x0B
+#define MBX_READ_RCONFIG    0x0C
+#define MBX_READ_SPARM      0x0D
+#define MBX_READ_STATUS     0x0E
+#define MBX_READ_RPI        0x0F
+#define MBX_READ_XRI        0x10
+#define MBX_READ_REV        0x11
+#define MBX_READ_LNK_STAT   0x12
+#define MBX_REG_LOGIN       0x13
+#define MBX_UNREG_LOGIN     0x14
+#define MBX_READ_LA         0x15
+#define MBX_CLEAR_LA        0x16
+#define MBX_DUMP_MEMORY     0x17
+#define MBX_DUMP_CONTEXT    0x18
+#define MBX_RUN_DIAGS       0x19
+#define MBX_RESTART         0x1A
+#define MBX_UPDATE_CFG      0x1B
+#define MBX_DOWN_LOAD       0x1C
+#define MBX_DEL_LD_ENTRY    0x1D
+#define MBX_RUN_PROGRAM     0x1E
+#define MBX_SET_MASK        0x20
+#define MBX_SET_SLIM        0x21
+#define MBX_UNREG_D_ID      0x23
+#define MBX_CONFIG_FARP     0x25
+
+#define MBX_LOAD_AREA       0x81
+#define MBX_RUN_BIU_DIAG64  0x84
+#define MBX_CONFIG_PORT     0x88
+#define MBX_READ_SPARM64    0x8D
+#define MBX_READ_RPI64      0x8F
+#define MBX_REG_LOGIN64     0x93
+#define MBX_READ_LA64       0x95
+
+#define MBX_FLASH_WR_ULA    0x98
+#define MBX_SET_DEBUG       0x99
+#define MBX_LOAD_EXP_ROM    0x9C
+
+#define MBX_MAX_CMDS        0x9D
+#define MBX_SLI2_CMD_MASK   0x80
+
+/* IOCB Commands */
+
+#define CMD_RCV_SEQUENCE_CX     0x01
+#define CMD_XMIT_SEQUENCE_CR    0x02
+#define CMD_XMIT_SEQUENCE_CX    0x03
+#define CMD_XMIT_BCAST_CN       0x04
+#define CMD_XMIT_BCAST_CX       0x05
+#define CMD_QUE_RING_BUF_CN     0x06
+#define CMD_QUE_XRI_BUF_CX      0x07
+#define CMD_IOCB_CONTINUE_CN    0x08
+#define CMD_RET_XRI_BUF_CX      0x09
+#define CMD_ELS_REQUEST_CR      0x0A
+#define CMD_ELS_REQUEST_CX      0x0B
+#define CMD_RCV_ELS_REQ_CX      0x0D
+#define CMD_ABORT_XRI_CN        0x0E
+#define CMD_ABORT_XRI_CX        0x0F
+#define CMD_CLOSE_XRI_CN        0x10
+#define CMD_CLOSE_XRI_CX        0x11
+#define CMD_CREATE_XRI_CR       0x12
+#define CMD_CREATE_XRI_CX       0x13
+#define CMD_GET_RPI_CN          0x14
+#define CMD_XMIT_ELS_RSP_CX     0x15
+#define CMD_GET_RPI_CR          0x16
+#define CMD_XRI_ABORTED_CX      0x17
+#define CMD_FCP_IWRITE_CR       0x18
+#define CMD_FCP_IWRITE_CX       0x19
+#define CMD_FCP_IREAD_CR        0x1A
+#define CMD_FCP_IREAD_CX        0x1B
+#define CMD_FCP_ICMND_CR        0x1C
+#define CMD_FCP_ICMND_CX        0x1D
+
+#define CMD_ADAPTER_MSG         0x20
+#define CMD_ADAPTER_DUMP        0x22
+
+/*  SLI_2 IOCB Command Set */
+
+#define CMD_RCV_SEQUENCE64_CX   0x81
+#define CMD_XMIT_SEQUENCE64_CR  0x82
+#define CMD_XMIT_SEQUENCE64_CX  0x83
+#define CMD_XMIT_BCAST64_CN     0x84
+#define CMD_XMIT_BCAST64_CX     0x85
+#define CMD_QUE_RING_BUF64_CN   0x86
+#define CMD_QUE_XRI_BUF64_CX    0x87
+#define CMD_IOCB_CONTINUE64_CN  0x88
+#define CMD_RET_XRI_BUF64_CX    0x89
+#define CMD_ELS_REQUEST64_CR    0x8A
+#define CMD_ELS_REQUEST64_CX    0x8B
+#define CMD_ABORT_MXRI64_CN     0x8C
+#define CMD_RCV_ELS_REQ64_CX    0x8D
+#define CMD_XMIT_ELS_RSP64_CX   0x95
+#define CMD_FCP_IWRITE64_CR     0x98
+#define CMD_FCP_IWRITE64_CX     0x99
+#define CMD_FCP_IREAD64_CR      0x9A
+#define CMD_FCP_IREAD64_CX      0x9B
+#define CMD_FCP_ICMND64_CR      0x9C
+#define CMD_FCP_ICMND64_CX      0x9D
+
+#define CMD_GEN_REQUEST64_CR    0xC2
+#define CMD_GEN_REQUEST64_CX    0xC3
+
+#define CMD_MAX_IOCB_CMD        0xE6
+#define CMD_IOCB_MASK           0xff
+
+#define MAX_MSG_DATA            28     /* max msg data in CMD_ADAPTER_MSG
+                                          iocb */
+#define LPFC_MAX_ADPTMSG         32    /* max msg data */
+/*
+ *  Define Status
+ */
+#define MBX_SUCCESS                 0
+#define MBXERR_NUM_RINGS            1
+#define MBXERR_NUM_IOCBS            2
+#define MBXERR_IOCBS_EXCEEDED       3
+#define MBXERR_BAD_RING_NUMBER      4
+#define MBXERR_MASK_ENTRIES_RANGE   5
+#define MBXERR_MASKS_EXCEEDED       6
+#define MBXERR_BAD_PROFILE          7
+#define MBXERR_BAD_DEF_CLASS        8
+#define MBXERR_BAD_MAX_RESPONDER    9
+#define MBXERR_BAD_MAX_ORIGINATOR   10
+#define MBXERR_RPI_REGISTERED       11
+#define MBXERR_RPI_FULL             12
+#define MBXERR_NO_RESOURCES         13
+#define MBXERR_BAD_RCV_LENGTH       14
+#define MBXERR_DMA_ERROR            15
+#define MBXERR_ERROR                16
+#define MBX_NOT_FINISHED           255
+
+#define MBX_BUSY                   0xffffff /* Attempted cmd to busy Mailbox */
+#define MBX_TIMEOUT                0xfffffe /* time-out expired waiting for */
+
+/*
+ *    Begin Structure Definitions for Mailbox Commands
+ */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t tval;
+       uint8_t tmask;
+       uint8_t rval;
+       uint8_t rmask;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t rmask;
+       uint8_t rval;
+       uint8_t tmask;
+       uint8_t tval;
+#endif
+} RR_REG;
+
+struct ulp_bde {
+       uint32_t bdeAddress;
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t bdeReserved:4;
+       uint32_t bdeAddrHigh:4;
+       uint32_t bdeSize:24;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t bdeSize:24;
+       uint32_t bdeAddrHigh:4;
+       uint32_t bdeReserved:4;
+#endif
+};
+
+struct ulp_bde64 {     /* SLI-2 */
+       union ULP_BDE_TUS {
+               uint32_t w;
+               struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+                       uint32_t bdeFlags:8;    /* BDE Flags 0 IS A SUPPORTED
+                                                  VALUE !! */
+                       uint32_t bdeSize:24;    /* Size of buffer (in bytes) */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+                       uint32_t bdeSize:24;    /* Size of buffer (in bytes) */
+                       uint32_t bdeFlags:8;    /* BDE Flags 0 IS A SUPPORTED
+                                                  VALUE !! */
+#endif
+
+#define BUFF_USE_RSVD       0x01       /* bdeFlags */
+#define BUFF_USE_INTRPT     0x02       /* Not Implemented with LP6000 */
+#define BUFF_USE_CMND       0x04       /* Optional, 1=cmd/rsp 0=data buffer */
+#define BUFF_USE_RCV        0x08       /*  "" "", 1=rcv buffer, 0=xmit
+                                           buffer */
+#define BUFF_TYPE_32BIT     0x10       /*  "" "", 1=32 bit addr 0=64 bit
+                                           addr */
+#define BUFF_TYPE_SPECIAL   0x20       /* Not Implemented with LP6000  */
+#define BUFF_TYPE_BDL       0x40       /* Optional,  may be set in BDL */
+#define BUFF_TYPE_INVALID   0x80       /*  ""  "" */
+               } f;
+       } tus;
+       uint32_t addrLow;
+       uint32_t addrHigh;
+};
+#define BDE64_SIZE_WORD 0
+#define BPL64_SIZE_WORD 0x40
+
+typedef struct ULP_BDL {       /* SLI-2 */
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t bdeFlags:8;    /* BDL Flags */
+       uint32_t bdeSize:24;    /* Size of BDL array in host memory (bytes) */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t bdeSize:24;    /* Size of BDL array in host memory (bytes) */
+       uint32_t bdeFlags:8;    /* BDL Flags */
+#endif
+
+       uint32_t addrLow;       /* Address 0:31 */
+       uint32_t addrHigh;      /* Address 32:63 */
+       uint32_t ulpIoTag32;    /* Can be used for 32 bit I/O Tag */
+} ULP_BDL;
+
+/* Structure for MB Command LOAD_SM and DOWN_LOAD */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t rsvd2:25;
+       uint32_t acknowledgment:1;
+       uint32_t version:1;
+       uint32_t erase_or_prog:1;
+       uint32_t update_flash:1;
+       uint32_t update_ram:1;
+       uint32_t method:1;
+       uint32_t load_cmplt:1;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t load_cmplt:1;
+       uint32_t method:1;
+       uint32_t update_ram:1;
+       uint32_t update_flash:1;
+       uint32_t erase_or_prog:1;
+       uint32_t version:1;
+       uint32_t acknowledgment:1;
+       uint32_t rsvd2:25;
+#endif
+
+       uint32_t dl_to_adr_low;
+       uint32_t dl_to_adr_high;
+       uint32_t dl_len;
+       union {
+               uint32_t dl_from_mbx_offset;
+               struct ulp_bde dl_from_bde;
+               struct ulp_bde64 dl_from_bde64;
+       } un;
+
+} LOAD_SM_VAR;
+
+/* Structure for MB Command READ_NVPARM (02) */
+
+typedef struct {
+       uint32_t rsvd1[3];      /* Read as all one's */
+       uint32_t rsvd2;         /* Read as all zero's */
+       uint32_t portname[2];   /* N_PORT name */
+       uint32_t nodename[2];   /* NODE name */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t pref_DID:24;
+       uint32_t hardAL_PA:8;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t hardAL_PA:8;
+       uint32_t pref_DID:24;
+#endif
+
+       uint32_t rsvd3[21];     /* Read as all one's */
+} READ_NV_VAR;
+
+/* Structure for MB Command WRITE_NVPARMS (03) */
+
+typedef struct {
+       uint32_t rsvd1[3];      /* Must be all one's */
+       uint32_t rsvd2;         /* Must be all zero's */
+       uint32_t portname[2];   /* N_PORT name */
+       uint32_t nodename[2];   /* NODE name */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t pref_DID:24;
+       uint32_t hardAL_PA:8;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t hardAL_PA:8;
+       uint32_t pref_DID:24;
+#endif
+
+       uint32_t rsvd3[21];     /* Must be all one's */
+} WRITE_NV_VAR;
+
+/* Structure for MB Command RUN_BIU_DIAG (04) */
+/* Structure for MB Command RUN_BIU_DIAG64 (0x84) */
+
+typedef struct {
+       uint32_t rsvd1;
+       union {
+               struct {
+                       struct ulp_bde xmit_bde;
+                       struct ulp_bde rcv_bde;
+               } s1;
+               struct {
+                       struct ulp_bde64 xmit_bde64;
+                       struct ulp_bde64 rcv_bde64;
+               } s2;
+       } un;
+} BIU_DIAG_VAR;
+
+/* Structure for MB Command INIT_LINK (05) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t rsvd1:24;
+       uint32_t lipsr_AL_PA:8; /* AL_PA to issue Lip Selective Reset to */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t lipsr_AL_PA:8; /* AL_PA to issue Lip Selective Reset to */
+       uint32_t rsvd1:24;
+#endif
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t fabric_AL_PA;   /* If using a Fabric Assigned AL_PA */
+       uint8_t rsvd2;
+       uint16_t link_flags;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t link_flags;
+       uint8_t rsvd2;
+       uint8_t fabric_AL_PA;   /* If using a Fabric Assigned AL_PA */
+#endif
+
+#define FLAGS_LOCAL_LB               0x01 /* link_flags (=1) ENDEC loopback */
+#define FLAGS_TOPOLOGY_MODE_LOOP_PT  0x00 /* Attempt loop then pt-pt */
+#define FLAGS_TOPOLOGY_MODE_PT_PT    0x02 /* Attempt pt-pt only */
+#define FLAGS_TOPOLOGY_MODE_LOOP     0x04 /* Attempt loop only */
+#define FLAGS_TOPOLOGY_MODE_PT_LOOP  0x06 /* Attempt pt-pt then loop */
+#define FLAGS_LIRP_LILP              0x80 /* LIRP / LILP is disabled */
+
+#define FLAGS_TOPOLOGY_FAILOVER      0x0400    /* Bit 10 */
+#define FLAGS_LINK_SPEED             0x0800    /* Bit 11 */
+
+       uint32_t link_speed;
+#define LINK_SPEED_AUTO 0       /* Auto selection */
+#define LINK_SPEED_1G   1       /* 1 Gigabaud */
+#define LINK_SPEED_2G   2       /* 2 Gigabaud */
+#define LINK_SPEED_4G   4       /* 4 Gigabaud */
+#define LINK_SPEED_8G   8       /* 4 Gigabaud */
+#define LINK_SPEED_10G   16      /* 10 Gigabaud */
+
+} INIT_LINK_VAR;
+
+/* Structure for MB Command DOWN_LINK (06) */
+
+typedef struct {
+       uint32_t rsvd1;
+} DOWN_LINK_VAR;
+
+/* Structure for MB Command CONFIG_LINK (07) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t cr:1;
+       uint32_t ci:1;
+       uint32_t cr_delay:6;
+       uint32_t cr_count:8;
+       uint32_t rsvd1:8;
+       uint32_t MaxBBC:8;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t MaxBBC:8;
+       uint32_t rsvd1:8;
+       uint32_t cr_count:8;
+       uint32_t cr_delay:6;
+       uint32_t ci:1;
+       uint32_t cr:1;
+#endif
+
+       uint32_t myId;
+       uint32_t rsvd2;
+       uint32_t edtov;
+       uint32_t arbtov;
+       uint32_t ratov;
+       uint32_t rttov;
+       uint32_t altov;
+       uint32_t crtov;
+       uint32_t citov;
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t rrq_enable:1;
+       uint32_t rrq_immed:1;
+       uint32_t rsvd4:29;
+       uint32_t ack0_enable:1;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t ack0_enable:1;
+       uint32_t rsvd4:29;
+       uint32_t rrq_immed:1;
+       uint32_t rrq_enable:1;
+#endif
+} CONFIG_LINK;
+
+/* Structure for MB Command PART_SLIM (08)
+ * will be removed since SLI1 is no longer supported!
+ */
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t offCiocb;
+       uint16_t numCiocb;
+       uint16_t offRiocb;
+       uint16_t numRiocb;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t numCiocb;
+       uint16_t offCiocb;
+       uint16_t numRiocb;
+       uint16_t offRiocb;
+#endif
+} RING_DEF;
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t unused1:24;
+       uint32_t numRing:8;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t numRing:8;
+       uint32_t unused1:24;
+#endif
+
+       RING_DEF ringdef[4];
+       uint32_t hbainit;
+} PART_SLIM_VAR;
+
+/* Structure for MB Command CONFIG_RING (09) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t unused2:6;
+       uint32_t recvSeq:1;
+       uint32_t recvNotify:1;
+       uint32_t numMask:8;
+       uint32_t profile:8;
+       uint32_t unused1:4;
+       uint32_t ring:4;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t ring:4;
+       uint32_t unused1:4;
+       uint32_t profile:8;
+       uint32_t numMask:8;
+       uint32_t recvNotify:1;
+       uint32_t recvSeq:1;
+       uint32_t unused2:6;
+#endif
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t maxRespXchg;
+       uint16_t maxOrigXchg;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t maxOrigXchg;
+       uint16_t maxRespXchg;
+#endif
+
+       RR_REG rrRegs[6];
+} CONFIG_RING_VAR;
+
+/* Structure for MB Command RESET_RING (10) */
+
+typedef struct {
+       uint32_t ring_no;
+} RESET_RING_VAR;
+
+/* Structure for MB Command READ_CONFIG (11) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t cr:1;
+       uint32_t ci:1;
+       uint32_t cr_delay:6;
+       uint32_t cr_count:8;
+       uint32_t InitBBC:8;
+       uint32_t MaxBBC:8;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t MaxBBC:8;
+       uint32_t InitBBC:8;
+       uint32_t cr_count:8;
+       uint32_t cr_delay:6;
+       uint32_t ci:1;
+       uint32_t cr:1;
+#endif
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t topology:8;
+       uint32_t myDid:24;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t myDid:24;
+       uint32_t topology:8;
+#endif
+
+       /* Defines for topology (defined previously) */
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t AR:1;
+       uint32_t IR:1;
+       uint32_t rsvd1:29;
+       uint32_t ack0:1;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t ack0:1;
+       uint32_t rsvd1:29;
+       uint32_t IR:1;
+       uint32_t AR:1;
+#endif
+
+       uint32_t edtov;
+       uint32_t arbtov;
+       uint32_t ratov;
+       uint32_t rttov;
+       uint32_t altov;
+       uint32_t lmt;
+#define LMT_RESERVED    0x0    /* Not used */
+#define LMT_266_10bit   0x1    /* 265.625 Mbaud 10 bit iface  */
+#define LMT_532_10bit   0x2    /* 531.25  Mbaud 10 bit iface  */
+#define LMT_1063_20bit  0x3    /* 1062.5   Mbaud 20 bit iface */
+#define LMT_1063_10bit  0x4    /* 1062.5   Mbaud 10 bit iface */
+#define LMT_2125_10bit  0x8    /* 2125     Mbaud 10 bit iface */
+#define LMT_4250_10bit  0x40   /* 4250     Mbaud 10 bit iface */
+
+       uint32_t rsvd2;
+       uint32_t rsvd3;
+       uint32_t max_xri;
+       uint32_t max_iocb;
+       uint32_t max_rpi;
+       uint32_t avail_xri;
+       uint32_t avail_iocb;
+       uint32_t avail_rpi;
+       uint32_t default_rpi;
+} READ_CONFIG_VAR;
+
+/* Structure for MB Command READ_RCONFIG (12) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t rsvd2:7;
+       uint32_t recvNotify:1;
+       uint32_t numMask:8;
+       uint32_t profile:8;
+       uint32_t rsvd1:4;
+       uint32_t ring:4;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t ring:4;
+       uint32_t rsvd1:4;
+       uint32_t profile:8;
+       uint32_t numMask:8;
+       uint32_t recvNotify:1;
+       uint32_t rsvd2:7;
+#endif
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t maxResp;
+       uint16_t maxOrig;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t maxOrig;
+       uint16_t maxResp;
+#endif
+
+       RR_REG rrRegs[6];
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t cmdRingOffset;
+       uint16_t cmdEntryCnt;
+       uint16_t rspRingOffset;
+       uint16_t rspEntryCnt;
+       uint16_t nextCmdOffset;
+       uint16_t rsvd3;
+       uint16_t nextRspOffset;
+       uint16_t rsvd4;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t cmdEntryCnt;
+       uint16_t cmdRingOffset;
+       uint16_t rspEntryCnt;
+       uint16_t rspRingOffset;
+       uint16_t rsvd3;
+       uint16_t nextCmdOffset;
+       uint16_t rsvd4;
+       uint16_t nextRspOffset;
+#endif
+} READ_RCONF_VAR;
+
+/* Structure for MB Command READ_SPARM (13) */
+/* Structure for MB Command READ_SPARM64 (0x8D) */
+
+typedef struct {
+       uint32_t rsvd1;
+       uint32_t rsvd2;
+       union {
+               struct ulp_bde sp; /* This BDE points to struct serv_parm
+                                     structure */
+               struct ulp_bde64 sp64;
+       } un;
+} READ_SPARM_VAR;
+
+/* Structure for MB Command READ_STATUS (14) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t rsvd1:31;
+       uint32_t clrCounters:1;
+       uint16_t activeXriCnt;
+       uint16_t activeRpiCnt;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t clrCounters:1;
+       uint32_t rsvd1:31;
+       uint16_t activeRpiCnt;
+       uint16_t activeXriCnt;
+#endif
+
+       uint32_t xmitByteCnt;
+       uint32_t rcvByteCnt;
+       uint32_t xmitFrameCnt;
+       uint32_t rcvFrameCnt;
+       uint32_t xmitSeqCnt;
+       uint32_t rcvSeqCnt;
+       uint32_t totalOrigExchanges;
+       uint32_t totalRespExchanges;
+       uint32_t rcvPbsyCnt;
+       uint32_t rcvFbsyCnt;
+} READ_STATUS_VAR;
+
+/* Structure for MB Command READ_RPI (15) */
+/* Structure for MB Command READ_RPI64 (0x8F) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t nextRpi;
+       uint16_t reqRpi;
+       uint32_t rsvd2:8;
+       uint32_t DID:24;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t reqRpi;
+       uint16_t nextRpi;
+       uint32_t DID:24;
+       uint32_t rsvd2:8;
+#endif
+
+       union {
+               struct ulp_bde sp;
+               struct ulp_bde64 sp64;
+       } un;
+
+} READ_RPI_VAR;
+
+/* Structure for MB Command READ_XRI (16) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t nextXri;
+       uint16_t reqXri;
+       uint16_t rsvd1;
+       uint16_t rpi;
+       uint32_t rsvd2:8;
+       uint32_t DID:24;
+       uint32_t rsvd3:8;
+       uint32_t SID:24;
+       uint32_t rsvd4;
+       uint8_t seqId;
+       uint8_t rsvd5;
+       uint16_t seqCount;
+       uint16_t oxId;
+       uint16_t rxId;
+       uint32_t rsvd6:30;
+       uint32_t si:1;
+       uint32_t exchOrig:1;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t reqXri;
+       uint16_t nextXri;
+       uint16_t rpi;
+       uint16_t rsvd1;
+       uint32_t DID:24;
+       uint32_t rsvd2:8;
+       uint32_t SID:24;
+       uint32_t rsvd3:8;
+       uint32_t rsvd4;
+       uint16_t seqCount;
+       uint8_t rsvd5;
+       uint8_t seqId;
+       uint16_t rxId;
+       uint16_t oxId;
+       uint32_t exchOrig:1;
+       uint32_t si:1;
+       uint32_t rsvd6:30;
+#endif
+} READ_XRI_VAR;
+
+/* Structure for MB Command READ_REV (17) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t cv:1;
+       uint32_t rr:1;
+       uint32_t rsvd1:29;
+       uint32_t rv:1;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t rv:1;
+       uint32_t rsvd1:29;
+       uint32_t rr:1;
+       uint32_t cv:1;
+#endif
+
+       uint32_t biuRev;
+       uint32_t smRev;
+       union {
+               uint32_t smFwRev;
+               struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+                       uint8_t ProgType;
+                       uint8_t ProgId;
+                       uint16_t ProgVer:4;
+                       uint16_t ProgRev:4;
+                       uint16_t ProgFixLvl:2;
+                       uint16_t ProgDistType:2;
+                       uint16_t DistCnt:4;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+                       uint16_t DistCnt:4;
+                       uint16_t ProgDistType:2;
+                       uint16_t ProgFixLvl:2;
+                       uint16_t ProgRev:4;
+                       uint16_t ProgVer:4;
+                       uint8_t ProgId;
+                       uint8_t ProgType;
+#endif
+
+               } b;
+       } un;
+       uint32_t endecRev;
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t feaLevelHigh;
+       uint8_t feaLevelLow;
+       uint8_t fcphHigh;
+       uint8_t fcphLow;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t fcphLow;
+       uint8_t fcphHigh;
+       uint8_t feaLevelLow;
+       uint8_t feaLevelHigh;
+#endif
+
+       uint32_t postKernRev;
+       uint32_t opFwRev;
+       uint8_t opFwName[16];
+       uint32_t sli1FwRev;
+       uint8_t sli1FwName[16];
+       uint32_t sli2FwRev;
+       uint8_t sli2FwName[16];
+       uint32_t rsvd2;
+       uint32_t RandomData[7];
+} READ_REV_VAR;
+
+/* Structure for MB Command READ_LINK_STAT (18) */
+
+typedef struct {
+       uint32_t rsvd1;
+       uint32_t linkFailureCnt;
+       uint32_t lossSyncCnt;
+
+       uint32_t lossSignalCnt;
+       uint32_t primSeqErrCnt;
+       uint32_t invalidXmitWord;
+       uint32_t crcCnt;
+       uint32_t primSeqTimeout;
+       uint32_t elasticOverrun;
+       uint32_t arbTimeout;
+} READ_LNK_VAR;
+
+/* Structure for MB Command REG_LOGIN (19) */
+/* Structure for MB Command REG_LOGIN64 (0x93) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t rsvd1;
+       uint16_t rpi;
+       uint32_t rsvd2:8;
+       uint32_t did:24;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t rpi;
+       uint16_t rsvd1;
+       uint32_t did:24;
+       uint32_t rsvd2:8;
+#endif
+
+       union {
+               struct ulp_bde sp;
+               struct ulp_bde64 sp64;
+       } un;
+
+} REG_LOGIN_VAR;
+
+/* Word 30 contents for REG_LOGIN */
+typedef union {
+       struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+               uint16_t rsvd1:12;
+               uint16_t wd30_class:4;
+               uint16_t xri;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+               uint16_t xri;
+               uint16_t wd30_class:4;
+               uint16_t rsvd1:12;
+#endif
+       } f;
+       uint32_t word;
+} REG_WD30;
+
+/* Structure for MB Command UNREG_LOGIN (20) */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t rsvd1;
+       uint16_t rpi;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t rpi;
+       uint16_t rsvd1;
+#endif
+} UNREG_LOGIN_VAR;
+
+/* Structure for MB Command UNREG_D_ID (0x23) */
+
+typedef struct {
+       uint32_t did;
+} UNREG_D_ID_VAR;
+
+/* Structure for MB Command READ_LA (21) */
+/* Structure for MB Command READ_LA64 (0x95) */
+
+typedef struct {
+       uint32_t eventTag;      /* Event tag */
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t rsvd1:22;
+       uint32_t pb:1;
+       uint32_t il:1;
+       uint32_t attType:8;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t attType:8;
+       uint32_t il:1;
+       uint32_t pb:1;
+       uint32_t rsvd1:22;
+#endif
+
+#define AT_RESERVED    0x00    /* Reserved - attType */
+#define AT_LINK_UP     0x01    /* Link is up */
+#define AT_LINK_DOWN   0x02    /* Link is down */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t granted_AL_PA;
+       uint8_t lipAlPs;
+       uint8_t lipType;
+       uint8_t topology;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t topology;
+       uint8_t lipType;
+       uint8_t lipAlPs;
+       uint8_t granted_AL_PA;
+#endif
+
+#define TOPOLOGY_PT_PT 0x01    /* Topology is pt-pt / pt-fabric */
+#define TOPOLOGY_LOOP  0x02    /* Topology is FC-AL */
+
+       union {
+               struct ulp_bde lilpBde; /* This BDE points to a 128 byte buffer
+                                          to */
+               /* store the LILP AL_PA position map into */
+               struct ulp_bde64 lilpBde64;
+       } un;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t Dlu:1;
+       uint32_t Dtf:1;
+       uint32_t Drsvd2:14;
+       uint32_t DlnkSpeed:8;
+       uint32_t DnlPort:4;
+       uint32_t Dtx:2;
+       uint32_t Drx:2;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t Drx:2;
+       uint32_t Dtx:2;
+       uint32_t DnlPort:4;
+       uint32_t DlnkSpeed:8;
+       uint32_t Drsvd2:14;
+       uint32_t Dtf:1;
+       uint32_t Dlu:1;
+#endif
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t Ulu:1;
+       uint32_t Utf:1;
+       uint32_t Ursvd2:14;
+       uint32_t UlnkSpeed:8;
+       uint32_t UnlPort:4;
+       uint32_t Utx:2;
+       uint32_t Urx:2;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t Urx:2;
+       uint32_t Utx:2;
+       uint32_t UnlPort:4;
+       uint32_t UlnkSpeed:8;
+       uint32_t Ursvd2:14;
+       uint32_t Utf:1;
+       uint32_t Ulu:1;
+#endif
+
+#define LA_UNKNW_LINK  0x0    /* lnkSpeed */
+#define LA_1GHZ_LINK   0x04   /* lnkSpeed */
+#define LA_2GHZ_LINK   0x08   /* lnkSpeed */
+#define LA_4GHZ_LINK   0x10   /* lnkSpeed */
+#define LA_8GHZ_LINK   0x20   /* lnkSpeed */
+#define LA_10GHZ_LINK  0x40   /* lnkSpeed */
+
+} READ_LA_VAR;
+
+/* Structure for MB Command CLEAR_LA (22) */
+
+typedef struct {
+       uint32_t eventTag;      /* Event tag */
+       uint32_t rsvd1;
+} CLEAR_LA_VAR;
+
+/* Structure for MB Command DUMP */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t rsvd:25;
+       uint32_t ra:1;
+       uint32_t co:1;
+       uint32_t cv:1;
+       uint32_t type:4;
+       uint32_t entry_index:16;
+       uint32_t region_id:16;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t type:4;
+       uint32_t cv:1;
+       uint32_t co:1;
+       uint32_t ra:1;
+       uint32_t rsvd:25;
+       uint32_t region_id:16;
+       uint32_t entry_index:16;
+#endif
+
+       uint32_t rsvd1;
+       uint32_t word_cnt;
+       uint32_t resp_offset;
+} DUMP_VAR;
+
+#define  DMP_MEM_REG             0x1
+#define  DMP_NV_PARAMS           0x2
+
+#define  DMP_REGION_VPD          0xe
+#define  DMP_VPD_SIZE            0x100
+
+/* Structure for MB Command CONFIG_PORT (0x88) */
+
+typedef struct {
+       uint32_t pcbLen;
+       uint32_t pcbLow;       /* bit 31:0  of memory based port config block */
+       uint32_t pcbHigh;      /* bit 63:32 of memory based port config block */
+       uint32_t hbainit[5];
+} CONFIG_PORT_VAR;
+
+/* SLI-2 Port Control Block */
+
+/* SLIM POINTER */
+#define SLIMOFF 0x30           /* WORD */
+
+typedef struct _SLI2_RDSC {
+       uint32_t cmdEntries;
+       uint32_t cmdAddrLow;
+       uint32_t cmdAddrHigh;
+
+       uint32_t rspEntries;
+       uint32_t rspAddrLow;
+       uint32_t rspAddrHigh;
+} SLI2_RDSC;
+
+typedef struct _PCB {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t type:8;
+#define TYPE_NATIVE_SLI2       0x01;
+       uint32_t feature:8;
+#define FEATURE_INITIAL_SLI2   0x01;
+       uint32_t rsvd:12;
+       uint32_t maxRing:4;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t maxRing:4;
+       uint32_t rsvd:12;
+       uint32_t feature:8;
+#define FEATURE_INITIAL_SLI2   0x01;
+       uint32_t type:8;
+#define TYPE_NATIVE_SLI2       0x01;
+#endif
+
+       uint32_t mailBoxSize;
+       uint32_t mbAddrLow;
+       uint32_t mbAddrHigh;
+
+       uint32_t hgpAddrLow;
+       uint32_t hgpAddrHigh;
+
+       uint32_t pgpAddrLow;
+       uint32_t pgpAddrHigh;
+       SLI2_RDSC rdsc[MAX_RINGS];
+} PCB_t;
+
+/* NEW_FEATURE */
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t rsvd0:27;
+       uint32_t discardFarp:1;
+       uint32_t IPEnable:1;
+       uint32_t nodeName:1;
+       uint32_t portName:1;
+       uint32_t filterEnable:1;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t filterEnable:1;
+       uint32_t portName:1;
+       uint32_t nodeName:1;
+       uint32_t IPEnable:1;
+       uint32_t discardFarp:1;
+       uint32_t rsvd:27;
+#endif
+
+       uint8_t portname[8];    /* Used to be struct lpfc_name */
+       uint8_t nodename[8];
+       uint32_t rsvd1;
+       uint32_t rsvd2;
+       uint32_t rsvd3;
+       uint32_t IPAddress;
+} CONFIG_FARP_VAR;
+
+/* Union of all Mailbox Command types */
+#define MAILBOX_CMD_WSIZE 32
+
+typedef union {
+       uint32_t varWords[MAILBOX_CMD_WSIZE - 1];
+       LOAD_SM_VAR varLdSM;    /* cmd =  1 (LOAD_SM)        */
+       READ_NV_VAR varRDnvp;   /* cmd =  2 (READ_NVPARMS)   */
+       WRITE_NV_VAR varWTnvp;  /* cmd =  3 (WRITE_NVPARMS)  */
+       BIU_DIAG_VAR varBIUdiag;        /* cmd =  4 (RUN_BIU_DIAG)   */
+       INIT_LINK_VAR varInitLnk;       /* cmd =  5 (INIT_LINK)      */
+       DOWN_LINK_VAR varDwnLnk;        /* cmd =  6 (DOWN_LINK)      */
+       CONFIG_LINK varCfgLnk;  /* cmd =  7 (CONFIG_LINK)    */
+       PART_SLIM_VAR varSlim;  /* cmd =  8 (PART_SLIM)      */
+       CONFIG_RING_VAR varCfgRing;     /* cmd =  9 (CONFIG_RING)    */
+       RESET_RING_VAR varRstRing;      /* cmd = 10 (RESET_RING)     */
+       READ_CONFIG_VAR varRdConfig;    /* cmd = 11 (READ_CONFIG)    */
+       READ_RCONF_VAR varRdRConfig;    /* cmd = 12 (READ_RCONFIG)   */
+       READ_SPARM_VAR varRdSparm;      /* cmd = 13 (READ_SPARM(64)) */
+       READ_STATUS_VAR varRdStatus;    /* cmd = 14 (READ_STATUS)    */
+       READ_RPI_VAR varRdRPI;  /* cmd = 15 (READ_RPI(64))   */
+       READ_XRI_VAR varRdXRI;  /* cmd = 16 (READ_XRI)       */
+       READ_REV_VAR varRdRev;  /* cmd = 17 (READ_REV)       */
+       READ_LNK_VAR varRdLnk;  /* cmd = 18 (READ_LNK_STAT)  */
+       REG_LOGIN_VAR varRegLogin;      /* cmd = 19 (REG_LOGIN(64))  */
+       UNREG_LOGIN_VAR varUnregLogin;  /* cmd = 20 (UNREG_LOGIN)    */
+       READ_LA_VAR varReadLA;  /* cmd = 21 (READ_LA(64))    */
+       CLEAR_LA_VAR varClearLA;        /* cmd = 22 (CLEAR_LA)       */
+       DUMP_VAR varDmp;        /* Warm Start DUMP mbx cmd   */
+       UNREG_D_ID_VAR varUnregDID; /* cmd = 0x23 (UNREG_D_ID)   */
+       CONFIG_FARP_VAR varCfgFarp; /* cmd = 0x25 (CONFIG_FARP)  NEW_FEATURE */
+       CONFIG_PORT_VAR varCfgPort; /* cmd = 0x88 (CONFIG_PORT)  */
+} MAILVARIANTS;
+
+/*
+ * SLI-2 specific structures
+ */
+
+typedef struct {
+       uint32_t cmdPutInx;
+       uint32_t rspGetInx;
+} HGP;
+
+typedef struct {
+       uint32_t cmdGetInx;
+       uint32_t rspPutInx;
+} PGP;
+
+typedef struct _SLI2_DESC {
+       HGP host[MAX_RINGS];
+       uint32_t unused1[16];
+       PGP port[MAX_RINGS];
+} SLI2_DESC;
+
+typedef union {
+       SLI2_DESC s2;
+} SLI_VAR;
+
+typedef volatile struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t mbxStatus;
+       uint8_t mbxCommand;
+       uint8_t mbxReserved:6;
+       uint8_t mbxHc:1;
+       uint8_t mbxOwner:1;     /* Low order bit first word */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t mbxOwner:1;     /* Low order bit first word */
+       uint8_t mbxHc:1;
+       uint8_t mbxReserved:6;
+       uint8_t mbxCommand;
+       uint16_t mbxStatus;
+#endif
+
+       MAILVARIANTS un;
+       SLI_VAR us;
+} MAILBOX_t;
+
+/*
+ *    Begin Structure Definitions for IOCB Commands
+ */
+
+typedef struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint8_t statAction;
+       uint8_t statRsn;
+       uint8_t statBaExp;
+       uint8_t statLocalError;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint8_t statLocalError;
+       uint8_t statBaExp;
+       uint8_t statRsn;
+       uint8_t statAction;
+#endif
+       /* statRsn  P/F_RJT reason codes */
+#define RJT_BAD_D_ID       0x01        /* Invalid D_ID field */
+#define RJT_BAD_S_ID       0x02        /* Invalid S_ID field */
+#define RJT_UNAVAIL_TEMP   0x03        /* N_Port unavailable temp. */
+#define RJT_UNAVAIL_PERM   0x04        /* N_Port unavailable perm. */
+#define RJT_UNSUP_CLASS    0x05        /* Class not supported */
+#define RJT_DELIM_ERR      0x06        /* Delimiter usage error */
+#define RJT_UNSUP_TYPE     0x07        /* Type not supported */
+#define RJT_BAD_CONTROL    0x08        /* Invalid link conrtol */
+#define RJT_BAD_RCTL       0x09        /* R_CTL invalid */
+#define RJT_BAD_FCTL       0x0A        /* F_CTL invalid */
+#define RJT_BAD_OXID       0x0B        /* OX_ID invalid */
+#define RJT_BAD_RXID       0x0C        /* RX_ID invalid */
+#define RJT_BAD_SEQID      0x0D        /* SEQ_ID invalid */
+#define RJT_BAD_DFCTL      0x0E        /* DF_CTL invalid */
+#define RJT_BAD_SEQCNT     0x0F        /* SEQ_CNT invalid */
+#define RJT_BAD_PARM       0x10        /* Param. field invalid */
+#define RJT_XCHG_ERR       0x11        /* Exchange error */
+#define RJT_PROT_ERR       0x12        /* Protocol error */
+#define RJT_BAD_LENGTH     0x13        /* Invalid Length */
+#define RJT_UNEXPECTED_ACK 0x14        /* Unexpected ACK */
+#define RJT_LOGIN_REQUIRED 0x16        /* Login required */
+#define RJT_TOO_MANY_SEQ   0x17        /* Excessive sequences */
+#define RJT_XCHG_NOT_STRT  0x18        /* Exchange not started */
+#define RJT_UNSUP_SEC_HDR  0x19        /* Security hdr not supported */
+#define RJT_UNAVAIL_PATH   0x1A        /* Fabric Path not available */
+#define RJT_VENDOR_UNIQUE  0xFF        /* Vendor unique error */
+
+#define IOERR_SUCCESS                 0x00     /* statLocalError */
+#define IOERR_MISSING_CONTINUE        0x01
+#define IOERR_SEQUENCE_TIMEOUT        0x02
+#define IOERR_INTERNAL_ERROR          0x03
+#define IOERR_INVALID_RPI             0x04
+#define IOERR_NO_XRI                  0x05
+#define IOERR_ILLEGAL_COMMAND         0x06
+#define IOERR_XCHG_DROPPED            0x07
+#define IOERR_ILLEGAL_FIELD           0x08
+#define IOERR_BAD_CONTINUE            0x09
+#define IOERR_TOO_MANY_BUFFERS        0x0A
+#define IOERR_RCV_BUFFER_WAITING      0x0B
+#define IOERR_NO_CONNECTION           0x0C
+#define IOERR_TX_DMA_FAILED           0x0D
+#define IOERR_RX_DMA_FAILED           0x0E
+#define IOERR_ILLEGAL_FRAME           0x0F
+#define IOERR_EXTRA_DATA              0x10
+#define IOERR_NO_RESOURCES            0x11
+#define IOERR_RESERVED                0x12
+#define IOERR_ILLEGAL_LENGTH          0x13
+#define IOERR_UNSUPPORTED_FEATURE     0x14
+#define IOERR_ABORT_IN_PROGRESS       0x15
+#define IOERR_ABORT_REQUESTED         0x16
+#define IOERR_RECEIVE_BUFFER_TIMEOUT  0x17
+#define IOERR_LOOP_OPEN_FAILURE       0x18
+#define IOERR_RING_RESET              0x19
+#define IOERR_LINK_DOWN               0x1A
+#define IOERR_CORRUPTED_DATA          0x1B
+#define IOERR_CORRUPTED_RPI           0x1C
+#define IOERR_OUT_OF_ORDER_DATA       0x1D
+#define IOERR_OUT_OF_ORDER_ACK        0x1E
+#define IOERR_DUP_FRAME               0x1F
+#define IOERR_LINK_CONTROL_FRAME      0x20     /* ACK_N received */
+#define IOERR_BAD_HOST_ADDRESS        0x21
+#define IOERR_RCV_HDRBUF_WAITING      0x22
+#define IOERR_MISSING_HDR_BUFFER      0x23
+#define IOERR_MSEQ_CHAIN_CORRUPTED    0x24
+#define IOERR_ABORTMULT_REQUESTED     0x25
+#define IOERR_BUFFER_SHORTAGE         0x28
+#define IOERR_DEFAULT                 0x29
+#define IOERR_CNT                     0x2A
+
+#define IOERR_DRVR_MASK               0x100
+#define IOERR_SLI_DOWN                0x101  /* ulpStatus  - Driver defined */
+#define IOERR_SLI_BRESET              0x102
+#define IOERR_SLI_ABORTED             0x103
+} PARM_ERR;
+
+typedef union {
+       struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+               uint8_t Rctl;   /* R_CTL field */
+               uint8_t Type;   /* TYPE field */
+               uint8_t Dfctl;  /* DF_CTL field */
+               uint8_t Fctl;   /* Bits 0-7 of IOCB word 5 */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+               uint8_t Fctl;   /* Bits 0-7 of IOCB word 5 */
+               uint8_t Dfctl;  /* DF_CTL field */
+               uint8_t Type;   /* TYPE field */
+               uint8_t Rctl;   /* R_CTL field */
+#endif
+
+#define BC      0x02           /* Broadcast Received  - Fctl */
+#define SI      0x04           /* Sequence Initiative */
+#define LA      0x08           /* Ignore Link Attention state */
+#define LS      0x80           /* Last Sequence */
+       } hcsw;
+       uint32_t reserved;
+} WORD5;
+
+/* IOCB Command template for a generic response */
+typedef struct {
+       uint32_t reserved[4];
+       PARM_ERR perr;
+} GENERIC_RSP;
+
+/* IOCB Command template for XMIT / XMIT_BCAST / RCV_SEQUENCE / XMIT_ELS */
+typedef struct {
+       struct ulp_bde xrsqbde[2];
+       uint32_t xrsqRo;        /* Starting Relative Offset */
+       WORD5 w5;               /* Header control/status word */
+} XR_SEQ_FIELDS;
+
+/* IOCB Command template for ELS_REQUEST */
+typedef struct {
+       struct ulp_bde elsReq;
+       struct ulp_bde elsRsp;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t word4Rsvd:7;
+       uint32_t fl:1;
+       uint32_t myID:24;
+       uint32_t word5Rsvd:8;
+       uint32_t remoteID:24;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t myID:24;
+       uint32_t fl:1;
+       uint32_t word4Rsvd:7;
+       uint32_t remoteID:24;
+       uint32_t word5Rsvd:8;
+#endif
+} ELS_REQUEST;
+
+/* IOCB Command template for RCV_ELS_REQ */
+typedef struct {
+       struct ulp_bde elsReq[2];
+       uint32_t parmRo;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t word5Rsvd:8;
+       uint32_t remoteID:24;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t remoteID:24;
+       uint32_t word5Rsvd:8;
+#endif
+} RCV_ELS_REQ;
+
+/* IOCB Command template for ABORT / CLOSE_XRI */
+typedef struct {
+       uint32_t rsvd[3];
+       uint32_t abortType;
+#define ABORT_TYPE_ABTX  0x00000000
+#define ABORT_TYPE_ABTS  0x00000001
+       uint32_t parm;
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t abortContextTag; /* ulpContext from command to abort/close */
+       uint16_t abortIoTag;    /* ulpIoTag from command to abort/close */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint16_t abortIoTag;    /* ulpIoTag from command to abort/close */
+       uint16_t abortContextTag; /* ulpContext from command to abort/close */
+#endif
+} AC_XRI;
+
+/* IOCB Command template for ABORT_MXRI64 */
+typedef struct {
+       uint32_t rsvd[3];
+       uint32_t abortType;
+       uint32_t parm;
+       uint32_t iotag32;
+} A_MXRI64;
+
+/* IOCB Command template for GET_RPI */
+typedef struct {
+       uint32_t rsvd[4];
+       uint32_t parmRo;
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t word5Rsvd:8;
+       uint32_t remoteID:24;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t remoteID:24;
+       uint32_t word5Rsvd:8;
+#endif
+} GET_RPI;
+
+/* IOCB Command template for all FCP Initiator commands */
+typedef struct {
+       struct ulp_bde fcpi_cmnd;       /* FCP_CMND payload descriptor */
+       struct ulp_bde fcpi_rsp;        /* Rcv buffer */
+       uint32_t fcpi_parm;
+       uint32_t fcpi_XRdy;     /* transfer ready for IWRITE */
+} FCPI_FIELDS;
+
+/* IOCB Command template for all FCP Target commands */
+typedef struct {
+       struct ulp_bde fcpt_Buffer[2];  /* FCP_CMND payload descriptor */
+       uint32_t fcpt_Offset;
+       uint32_t fcpt_Length;   /* transfer ready for IWRITE */
+} FCPT_FIELDS;
+
+/* SLI-2 IOCB structure definitions */
+
+/* IOCB Command template for 64 bit XMIT / XMIT_BCAST / XMIT_ELS */
+typedef struct {
+       ULP_BDL bdl;
+       uint32_t xrsqRo;        /* Starting Relative Offset */
+       WORD5 w5;               /* Header control/status word */
+} XMT_SEQ_FIELDS64;
+
+/* IOCB Command template for 64 bit RCV_SEQUENCE64 */
+typedef struct {
+       struct ulp_bde64 rcvBde;
+       uint32_t rsvd1;
+       uint32_t xrsqRo;        /* Starting Relative Offset */
+       WORD5 w5;               /* Header control/status word */
+} RCV_SEQ_FIELDS64;
+
+/* IOCB Command template for ELS_REQUEST64 */
+typedef struct {
+       ULP_BDL bdl;
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t word4Rsvd:7;
+       uint32_t fl:1;
+       uint32_t myID:24;
+       uint32_t word5Rsvd:8;
+       uint32_t remoteID:24;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t myID:24;
+       uint32_t fl:1;
+       uint32_t word4Rsvd:7;
+       uint32_t remoteID:24;
+       uint32_t word5Rsvd:8;
+#endif
+} ELS_REQUEST64;
+
+/* IOCB Command template for GEN_REQUEST64 */
+typedef struct {
+       ULP_BDL bdl;
+       uint32_t xrsqRo;        /* Starting Relative Offset */
+       WORD5 w5;               /* Header control/status word */
+} GEN_REQUEST64;
+
+/* IOCB Command template for RCV_ELS_REQ64 */
+typedef struct {
+       struct ulp_bde64 elsReq;
+       uint32_t rcvd1;
+       uint32_t parmRo;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t word5Rsvd:8;
+       uint32_t remoteID:24;
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t remoteID:24;
+       uint32_t word5Rsvd:8;
+#endif
+} RCV_ELS_REQ64;
+
+/* IOCB Command template for all 64 bit FCP Initiator commands */
+typedef struct {
+       ULP_BDL bdl;
+       uint32_t fcpi_parm;
+       uint32_t fcpi_XRdy;     /* transfer ready for IWRITE */
+} FCPI_FIELDS64;
+
+/* IOCB Command template for all 64 bit FCP Target commands */
+typedef struct {
+       ULP_BDL bdl;
+       uint32_t fcpt_Offset;
+       uint32_t fcpt_Length;   /* transfer ready for IWRITE */
+} FCPT_FIELDS64;
+
+typedef volatile struct _IOCB {        /* IOCB structure */
+       union {
+               GENERIC_RSP grsp;       /* Generic response */
+               XR_SEQ_FIELDS xrseq;    /* XMIT / BCAST / RCV_SEQUENCE cmd */
+               struct ulp_bde cont[3]; /* up to 3 continuation bdes */
+               RCV_ELS_REQ rcvels;     /* RCV_ELS_REQ template */
+               AC_XRI acxri;   /* ABORT / CLOSE_XRI template */
+               A_MXRI64 amxri; /* abort multiple xri command overlay */
+               GET_RPI getrpi; /* GET_RPI template */
+               FCPI_FIELDS fcpi;       /* FCP Initiator template */
+               FCPT_FIELDS fcpt;       /* FCP target template */
+
+               /* SLI-2 structures */
+
+               struct ulp_bde64 cont64[2];     /* up to 2 64 bit continuation
+                                          bde_64s */
+               ELS_REQUEST64 elsreq64; /* ELS_REQUEST template */
+               GEN_REQUEST64 genreq64; /* GEN_REQUEST template */
+               RCV_ELS_REQ64 rcvels64; /* RCV_ELS_REQ template */
+               XMT_SEQ_FIELDS64 xseq64;        /* XMIT / BCAST cmd */
+               FCPI_FIELDS64 fcpi64;   /* FCP 64 bit Initiator template */
+               FCPT_FIELDS64 fcpt64;   /* FCP 64 bit target template */
+
+               uint32_t ulpWord[IOCB_WORD_SZ - 2];     /* generic 6 'words' */
+       } un;
+       union {
+               struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+                       uint16_t ulpContext;    /* High order bits word 6 */
+                       uint16_t ulpIoTag;      /* Low  order bits word 6 */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+                       uint16_t ulpIoTag;      /* Low  order bits word 6 */
+                       uint16_t ulpContext;    /* High order bits word 6 */
+#endif
+               } t1;
+               struct {
+#ifdef __BIG_ENDIAN_BITFIELD
+                       uint16_t ulpContext;    /* High order bits word 6 */
+                       uint16_t ulpIoTag1:2;   /* Low  order bits word 6 */
+                       uint16_t ulpIoTag0:14;  /* Low  order bits word 6 */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+                       uint16_t ulpIoTag0:14;  /* Low  order bits word 6 */
+                       uint16_t ulpIoTag1:2;   /* Low  order bits word 6 */
+                       uint16_t ulpContext;    /* High order bits word 6 */
+#endif
+               } t2;
+       } un1;
+#define ulpContext un1.t1.ulpContext
+#define ulpIoTag   un1.t1.ulpIoTag
+#define ulpIoTag0  un1.t2.ulpIoTag0
+
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint32_t ulpTimeout:8;
+       uint32_t ulpXS:1;
+       uint32_t ulpFCP2Rcvy:1;
+       uint32_t ulpPU:2;
+       uint32_t ulpIr:1;
+       uint32_t ulpClass:3;
+       uint32_t ulpCommand:8;
+       uint32_t ulpStatus:4;
+       uint32_t ulpBdeCount:2;
+       uint32_t ulpLe:1;
+       uint32_t ulpOwner:1;    /* Low order bit word 7 */
+#else  /*  __LITTLE_ENDIAN_BITFIELD */
+       uint32_t ulpOwner:1;    /* Low order bit word 7 */
+       uint32_t ulpLe:1;
+       uint32_t ulpBdeCount:2;
+       uint32_t ulpStatus:4;
+       uint32_t ulpCommand:8;
+       uint32_t ulpClass:3;
+       uint32_t ulpIr:1;
+       uint32_t ulpPU:2;
+       uint32_t ulpFCP2Rcvy:1;
+       uint32_t ulpXS:1;
+       uint32_t ulpTimeout:8;
+#endif
+
+#define PARM_UNUSED        0   /* PU field (Word 4) not used */
+#define PARM_REL_OFF       1   /* PU field (Word 4) = R. O. */
+#define PARM_READ_CHECK    2   /* PU field (Word 4) = Data Transfer Length */
+#define CLASS1             0   /* Class 1 */
+#define CLASS2             1   /* Class 2 */
+#define CLASS3             2   /* Class 3 */
+#define CLASS_FCP_INTERMIX 7   /* FCP Data->Cls 1, all else->Cls 2 */
+
+#define IOSTAT_SUCCESS         0x0     /* ulpStatus  - HBA defined */
+#define IOSTAT_FCP_RSP_ERROR   0x1
+#define IOSTAT_REMOTE_STOP     0x2
+#define IOSTAT_LOCAL_REJECT    0x3
+#define IOSTAT_NPORT_RJT       0x4
+#define IOSTAT_FABRIC_RJT      0x5
+#define IOSTAT_NPORT_BSY       0x6
+#define IOSTAT_FABRIC_BSY      0x7
+#define IOSTAT_INTERMED_RSP    0x8
+#define IOSTAT_LS_RJT          0x9
+#define IOSTAT_BA_RJT          0xA
+#define IOSTAT_RSVD1           0xB
+#define IOSTAT_RSVD2           0xC
+#define IOSTAT_RSVD3           0xD
+#define IOSTAT_RSVD4           0xE
+#define IOSTAT_RSVD5           0xF
+#define IOSTAT_DRIVER_REJECT   0x10   /* ulpStatus  - Driver defined */
+#define IOSTAT_DEFAULT         0xF    /* Same as rsvd5 for now */
+#define IOSTAT_CNT             0x11
+
+} IOCB_t;
+
+
+#define SLI1_SLIM_SIZE   (4 * 1024)
+
+/* Up to 498 IOCBs will fit into 16k
+ * 256 (MAILBOX_t) + 140 (PCB_t) + ( 32 (IOCB_t) * 498 ) = < 16384
+ */
+#define SLI2_SLIM_SIZE   (16 * 1024)
+
+/* Maximum IOCBs that will fit in SLI2 slim */
+#define MAX_SLI2_IOCB    498
+
+struct lpfc_sli2_slim {
+       MAILBOX_t mbx;
+       PCB_t pcb;
+       IOCB_t IOCBs[MAX_SLI2_IOCB];
+};
+
+/*******************************************************************
+This macro check PCI device to allow special handling for LC HBAs.
+
+Parameters:
+device : struct pci_dev 's device field
+
+return 1 => TRUE
+       0 => FALSE
+ *******************************************************************/
+static inline int
+lpfc_is_LC_HBA(unsigned short device)
+{
+       if ((device == PCI_DEVICE_ID_TFLY) ||
+           (device == PCI_DEVICE_ID_PFLY) ||
+           (device == PCI_DEVICE_ID_LP101) ||
+           (device == PCI_DEVICE_ID_BMID) ||
+           (device == PCI_DEVICE_ID_BSMB) ||
+           (device == PCI_DEVICE_ID_ZMID) ||
+           (device == PCI_DEVICE_ID_ZSMB) ||
+           (device == PCI_DEVICE_ID_RFLY))
+               return 1;
+       else
+               return 0;
+}
+
+#endif                         /* _H_LPFC_HW */
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
new file mode 100644 (file)
index 0000000..e7f9278
--- /dev/null
@@ -0,0 +1,1345 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_init.c 1.168 2004/11/15 11:01:33EST sf_support Exp  $
+ */
+
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/ctype.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+#include "lpfc_hw.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_mem.h"
+#include "lpfc_version.h"
+#include "lpfc_compat.h"
+
+static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *);
+static int lpfc_post_rcv_buf(struct lpfc_hba *);
+static int lpfc_rdrev_wd30 = 0;
+
+/************************************************************************/
+/*                                                                      */
+/*    lpfc_config_port_prep                                             */
+/*    This routine will do LPFC initialization prior to the             */
+/*    CONFIG_PORT mailbox command. This will be initialized             */
+/*    as a SLI layer callback routine.                                  */
+/*    This routine returns 0 on success or -ERESTART if it wants        */
+/*    the SLI layer to reset the HBA and try again. Any                 */
+/*    other return value indicates an error.                            */
+/*                                                                      */
+/************************************************************************/
+int
+lpfc_config_port_prep(struct lpfc_hba * phba)
+{
+       lpfc_vpd_t *vp = &phba->vpd;
+       int i = 0;
+       LPFC_MBOXQ_t *pmb;
+       MAILBOX_t *mb;
+
+
+       /* Get a Mailbox buffer to setup mailbox commands for HBA
+          initialization */
+       pmb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC);
+       if (!pmb) {
+               phba->hba_state = LPFC_HBA_ERROR;
+               return -ENOMEM;
+       }
+
+       mb = &pmb->mb;
+       phba->hba_state = LPFC_INIT_MBX_CMDS;
+
+       /* special handling for LC HBAs */
+       if (lpfc_is_LC_HBA(phba->pcidev->device)) {
+               char licensed[56] =
+                   "key unlock for use with gnu public licensed code only\0";
+               uint32_t *ptext = (uint32_t *) licensed;
+
+               for (i = 0; i < 56; i += sizeof (uint32_t), ptext++)
+                       *ptext = cpu_to_be32(*ptext);
+
+               /* Setup and issue mailbox READ NVPARAMS command */
+               lpfc_read_nv(phba, pmb);
+               memset((char*)mb->un.varRDnvp.rsvd3, 0,
+                       sizeof (mb->un.varRDnvp.rsvd3));
+               memcpy((char*)mb->un.varRDnvp.rsvd3, licensed,
+                        sizeof (licensed));
+
+               if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+                       /* Adapter initialization error, mbxCmd <cmd>
+                          READ_NVPARM, mbxStatus <status> */
+                       lpfc_printf_log(phba,
+                                       KERN_ERR,
+                                       LOG_MBOX,
+                                       "%d:0324 Config Port initialization "
+                                       "error, mbxCmd x%x READ_NVPARM, "
+                                       "mbxStatus x%x\n",
+                                       phba->brd_no,
+                                       mb->mbxCommand, mb->mbxStatus);
+                       return -ERESTART;
+               }
+               memcpy(phba->wwnn, (char *)mb->un.varRDnvp.nodename,
+                      sizeof (mb->un.varRDnvp.nodename));
+       }
+
+       /* Setup and issue mailbox READ REV command */
+       lpfc_read_rev(phba, pmb);
+       if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+               /* Adapter failed to init, mbxCmd <mbxCmd> READ_REV, mbxStatus
+                  <status> */
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_INIT,
+                               "%d:0439 Adapter failed to init, mbxCmd x%x "
+                               "READ_REV, mbxStatus x%x\n",
+                               phba->brd_no,
+                               mb->mbxCommand, mb->mbxStatus);
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return -ERESTART;
+       }
+
+       /* The HBA's current state is provided by the ProgType and rr fields.
+        * Read and check the value of these fields before continuing to config
+        * this port.
+        */
+       if (mb->un.varRdRev.rr == 0 || mb->un.varRdRev.un.b.ProgType != 2) {
+               /* Old firmware */
+               vp->rev.rBit = 0;
+               /* Adapter failed to init, mbxCmd <cmd> READ_REV detected
+                  outdated firmware */
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_INIT,
+                               "%d:0440 Adapter failed to init, mbxCmd x%x "
+                               "READ_REV detected outdated firmware"
+                               "Data: x%x\n",
+                               phba->brd_no,
+                               mb->mbxCommand, 0);
+               mempool_free(pmb, phba->mbox_mem_pool);
+               return -ERESTART;
+       } else {
+               vp->rev.rBit = 1;
+               vp->rev.sli1FwRev = mb->un.varRdRev.sli1FwRev;
+               memcpy(vp->rev.sli1FwName,
+                       (char*)mb->un.varRdRev.sli1FwName, 16);
+               vp->rev.sli2FwRev = mb->un.varRdRev.sli2FwRev;
+               memcpy(vp->rev.sli2FwName,
+                       (char *)mb->un.varRdRev.sli2FwName, 16);
+       }
+
+       /* Save information as VPD data */
+       vp->rev.biuRev = mb->un.varRdRev.biuRev;
+       vp->rev.smRev = mb->un.varRdRev.smRev;
+       vp->rev.smFwRev = mb->un.varRdRev.un.smFwRev;
+       vp->rev.endecRev = mb->un.varRdRev.endecRev;
+       vp->rev.fcphHigh = mb->un.varRdRev.fcphHigh;
+       vp->rev.fcphLow = mb->un.varRdRev.fcphLow;
+       vp->rev.feaLevelHigh = mb->un.varRdRev.feaLevelHigh;
+       vp->rev.feaLevelLow = mb->un.varRdRev.feaLevelLow;
+       vp->rev.postKernRev = mb->un.varRdRev.postKernRev;
+       vp->rev.opFwRev = mb->un.varRdRev.opFwRev;
+       lpfc_rdrev_wd30 = mb->un.varWords[30];
+
+       if (lpfc_is_LC_HBA(phba->pcidev->device))
+               memcpy(phba->RandomData, (char *)&mb->un.varWords[24],
+                       sizeof (phba->RandomData));
+
+       /* Get adapter VPD information */
+       lpfc_dump_mem(phba, pmb);
+       if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+               /* Let it go through even if failed. */
+               /* Adapter failed to init, mbxCmd <cmd> DUMP VPD,
+                  mbxStatus <status> */
+               lpfc_printf_log(phba,
+                               KERN_INFO,
+                               LOG_INIT,
+                               "%d:0441 VPD not present on adapter, mbxCmd "
+                               "x%x DUMP VPD, mbxStatus x%x\n",
+                               phba->brd_no,
+                               mb->mbxCommand, mb->mbxStatus);
+       } else if (mb->un.varDmp.ra == 1) {
+               lpfc_parse_vpd(phba, (uint8_t *)&mb->un.varDmp.resp_offset);
+       }
+       mempool_free(pmb, phba->mbox_mem_pool);
+       return 0;
+}
+
+/************************************************************************/
+/*                                                                      */
+/*    lpfc_config_port_post                                             */
+/*    This routine will do LPFC initialization after the                */
+/*    CONFIG_PORT mailbox command. This will be initialized             */
+/*    as a SLI layer callback routine.                                  */
+/*    This routine returns 0 on success. Any other return value         */
+/*    indicates an error.                                               */
+/*                                                                      */
+/************************************************************************/
+int
+lpfc_config_port_post(struct lpfc_hba * phba)
+{
+       LPFC_MBOXQ_t *pmb;
+       MAILBOX_t *mb;
+       struct lpfc_dmabuf *mp;
+       struct lpfc_sli *psli = &phba->sli;
+       uint32_t status, timeout;
+       int i, j, flogi_sent;
+       unsigned long isr_cnt, clk_cnt;
+
+
+       /* Get a Mailbox buffer to setup mailbox commands for HBA
+          initialization */
+       pmb = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC);
+       if (!pmb) {
+               phba->hba_state = LPFC_HBA_ERROR;
+               return -ENOMEM;
+       }
+       mb = &pmb->mb;
+
+       /* Setup link timers */
+       lpfc_config_link(phba, pmb);
+       if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_INIT,
+                               "%d:0447 Adapter failed init, mbxCmd x%x "
+                               "CONFIG_LINK mbxStatus x%x\n",
+                               phba->brd_no,
+                               mb->mbxCommand, mb->mbxStatus);
+               phba->hba_state = LPFC_HBA_ERROR;
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return -EIO;
+       }
+
+       /* Get login parameters for NID.  */
+       lpfc_read_sparam(phba, pmb);
+       if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_INIT,
+                               "%d:0448 Adapter failed init, mbxCmd x%x "
+                               "READ_SPARM mbxStatus x%x\n",
+                               phba->brd_no,
+                               mb->mbxCommand, mb->mbxStatus);
+               phba->hba_state = LPFC_HBA_ERROR;
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return -EIO;
+       }
+
+       mp = (struct lpfc_dmabuf *) pmb->context1;
+
+       /* The mailbox was populated by the HBA.  Flush it to main store for the
+        * driver.  Note that all context buffers are from the driver's
+        * dma pool and have length LPFC_BPL_SIZE.
+        */
+        pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys, LPFC_BPL_SIZE,
+               PCI_DMA_FROMDEVICE);
+
+       memcpy(&phba->fc_sparam, mp->virt, sizeof (struct serv_parm));
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       pmb->context1 = NULL;
+
+       memcpy(&phba->fc_nodename, &phba->fc_sparam.nodeName,
+              sizeof (struct lpfc_name));
+       memcpy(&phba->fc_portname, &phba->fc_sparam.portName,
+              sizeof (struct lpfc_name));
+       /* If no serial number in VPD data, use low 6 bytes of WWNN */
+       /* This should be consolidated into parse_vpd ? - mr */
+       if (phba->SerialNumber[0] == 0) {
+               uint8_t *outptr;
+
+               outptr = (uint8_t *) & phba->fc_nodename.IEEE[0];
+               for (i = 0; i < 12; i++) {
+                       status = *outptr++;
+                       j = ((status & 0xf0) >> 4);
+                       if (j <= 9)
+                               phba->SerialNumber[i] =
+                                   (char)((uint8_t) 0x30 + (uint8_t) j);
+                       else
+                               phba->SerialNumber[i] =
+                                   (char)((uint8_t) 0x61 + (uint8_t) (j - 10));
+                       i++;
+                       j = (status & 0xf);
+                       if (j <= 9)
+                               phba->SerialNumber[i] =
+                                   (char)((uint8_t) 0x30 + (uint8_t) j);
+                       else
+                               phba->SerialNumber[i] =
+                                   (char)((uint8_t) 0x61 + (uint8_t) (j - 10));
+               }
+       }
+
+       /* This should turn on DELAYED ABTS for ELS timeouts */
+       lpfc_set_slim(phba, pmb, 0x052198, 0x1);
+       if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+               phba->hba_state = LPFC_HBA_ERROR;
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return -EIO;
+       }
+
+
+       lpfc_read_config(phba, pmb);
+       if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_INIT,
+                               "%d:0453 Adapter failed to init, mbxCmd x%x "
+                               "READ_CONFIG, mbxStatus x%x\n",
+                               phba->brd_no,
+                               mb->mbxCommand, mb->mbxStatus);
+               phba->hba_state = LPFC_HBA_ERROR;
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return -EIO;
+       }
+
+       /* Reset the DFT_HBA_Q_DEPTH to the max xri  */
+       if (phba->cfg_hba_queue_depth > (mb->un.varRdConfig.max_xri+1))
+               phba->cfg_hba_queue_depth =
+                       mb->un.varRdConfig.max_xri + 1;
+
+       phba->lmt = mb->un.varRdConfig.lmt;
+       /* HBA is not 4GB capable, or HBA is not 2GB capable,
+       don't let link speed ask for it */
+       if ((((phba->lmt & LMT_4250_10bit) != LMT_4250_10bit) &&
+               (phba->cfg_link_speed > LINK_SPEED_2G)) ||
+               (((phba->lmt & LMT_2125_10bit) != LMT_2125_10bit) && 
+               (phba->cfg_link_speed > LINK_SPEED_1G))) {
+               /* Reset link speed to auto. 1G/2GB HBA cfg'd for 4G */
+               lpfc_printf_log(phba,
+                       KERN_WARNING,
+                       LOG_LINK_EVENT,
+                       "%d:1302 Invalid speed for this board: "
+                       "Reset link speed to auto: x%x\n",
+                       phba->brd_no,
+                       phba->cfg_link_speed);
+                       phba->cfg_link_speed = LINK_SPEED_AUTO;
+       }
+
+       if (!phba->intr_inited) {
+               /* Add our interrupt routine to kernel's interrupt chain &
+                  enable it */
+
+               if (request_irq(phba->pcidev->irq,
+                               lpfc_intr_handler,
+                               SA_SHIRQ,
+                               LPFC_DRIVER_NAME,
+                               phba) != 0) {
+                       /* Enable interrupt handler failed */
+                       lpfc_printf_log(phba,
+                                       KERN_ERR,
+                                       LOG_INIT,
+                                       "%d:0451 Enable interrupt handler "
+                                       "failed\n",
+                                       phba->brd_no);
+                       phba->hba_state = LPFC_HBA_ERROR;
+                       mempool_free(pmb, phba->mbox_mem_pool);
+                       return -EIO;
+               }
+               phba->intr_inited =
+                       (HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA);
+       }
+
+       phba->hba_state = LPFC_LINK_DOWN;
+
+       /* Only process IOCBs on ring 0 till hba_state is READY */
+       if (psli->ring[psli->ip_ring].cmdringaddr)
+               psli->ring[psli->ip_ring].flag |= LPFC_STOP_IOCB_EVENT;
+       if (psli->ring[psli->fcp_ring].cmdringaddr)
+               psli->ring[psli->fcp_ring].flag |= LPFC_STOP_IOCB_EVENT;
+       if (psli->ring[psli->next_ring].cmdringaddr)
+               psli->ring[psli->next_ring].flag |= LPFC_STOP_IOCB_EVENT;
+
+       /* Post receive buffers for desired rings */
+       lpfc_post_rcv_buf(phba);
+
+       /* Enable appropriate host interrupts */
+       status = readl(phba->HCregaddr);
+       status |= phba->intr_inited;
+       if (psli->sliinit.num_rings > 0)
+               status |= HC_R0INT_ENA;
+       if (psli->sliinit.num_rings > 1)
+               status |= HC_R1INT_ENA;
+       if (psli->sliinit.num_rings > 2)
+               status |= HC_R2INT_ENA;
+       if (psli->sliinit.num_rings > 3)
+               status |= HC_R3INT_ENA;
+
+       writel(status, phba->HCregaddr);
+       readl(phba->HCregaddr); /* flush */
+
+       /* Setup and issue mailbox INITIALIZE LINK command */
+       lpfc_init_link(phba, pmb, phba->cfg_topology,
+                      phba->cfg_link_speed);
+
+       isr_cnt = psli->slistat.sliIntr;
+       clk_cnt = jiffies;
+
+       if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) != MBX_SUCCESS) {
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_INIT,
+                               "%d:0454 Adapter failed to init, mbxCmd x%x "
+                               "INIT_LINK, mbxStatus x%x\n",
+                               phba->brd_no,
+                               mb->mbxCommand, mb->mbxStatus);
+
+               /* Clear all interrupt enable conditions */
+               writel(0, phba->HCregaddr);
+               readl(phba->HCregaddr); /* flush */
+               /* Clear all pending interrupts */
+               writel(0xffffffff, phba->HAregaddr);
+               readl(phba->HAregaddr); /* flush */
+
+               free_irq(phba->pcidev->irq, phba);
+               phba->hba_state = LPFC_HBA_ERROR;
+               mempool_free(pmb, phba->mbox_mem_pool);
+               return -EIO;
+       }
+       /* MBOX buffer will be freed in mbox compl */
+
+       /*
+        * Setup the ring 0 (els)  timeout handler
+        */
+       timeout = phba->fc_ratov << 1;
+
+       phba->els_tmofunc.expires = jiffies + HZ * timeout;
+       add_timer(&phba->els_tmofunc);
+
+       phba->fc_prevDID = Mask_DID;
+       flogi_sent = 0;
+       i = 0;
+       while ((phba->hba_state != LPFC_HBA_READY) ||
+              (phba->num_disc_nodes) || (phba->fc_prli_sent) ||
+              ((phba->fc_map_cnt == 0) && (i<2)) ||
+              (psli->sliinit.sli_flag & LPFC_SLI_MBOX_ACTIVE)) {
+               /* Check every second for 30 retries. */
+               i++;
+               if (i > 30) {
+                       break;
+               }
+               if ((i >= 15) && (phba->hba_state <= LPFC_LINK_DOWN)) {
+                       /* The link is down.  Set linkdown timeout */
+                       break;
+               }
+
+               /* Delay for 1 second to give discovery time to complete. */
+               for (j = 0; j < 20; j++) {
+                       /* On some systems, the driver's attach/detect routines
+                        * are uninterruptible.  Since the driver cannot predict
+                        * when this is true, just manually call the ISR every
+                        * 50 ms to service any interrupts.
+                        */
+                       msleep(50);
+                       if (isr_cnt == psli->slistat.sliIntr) {
+                               lpfc_sli_intr(phba);
+                               isr_cnt = psli->slistat.sliIntr;
+                       }
+               }
+               isr_cnt = psli->slistat.sliIntr;
+
+               if (clk_cnt == jiffies) {
+                       /* REMOVE: IF THIS HAPPENS, SYSTEM CLOCK IS NOT RUNNING.
+                        * WE HAVE TO MANUALLY CALL OUR TIMEOUT ROUTINES.
+                        */
+                       clk_cnt = jiffies;
+               }
+       }
+
+       /* Since num_disc_nodes keys off of PLOGI, delay a bit to let
+        * any potential PRLIs to flush thru the SLI sub-system.
+        */
+       msleep(50);
+       if (isr_cnt == psli->slistat.sliIntr) {
+               lpfc_sli_intr(phba);
+       }
+
+       return (0);
+}
+
+/************************************************************************/
+/*                                                                      */
+/*    lpfc_hba_down_prep                                                */
+/*    This routine will do LPFC uninitialization before the             */
+/*    HBA is reset when bringing down the SLI Layer. This will be       */
+/*    initialized as a SLI layer callback routine.                      */
+/*    This routine returns 0 on success. Any other return value         */
+/*    indicates an error.                                               */
+/*                                                                      */
+/************************************************************************/
+int
+lpfc_hba_down_prep(struct lpfc_hba * phba)
+{
+       /* Disable interrupts */
+       writel(0, phba->HCregaddr);
+       readl(phba->HCregaddr); /* flush */
+
+       /* Cleanup potential discovery resources */
+       lpfc_els_flush_rscn(phba);
+       lpfc_els_flush_cmd(phba);
+       lpfc_disc_flush_list(phba);
+
+       return (0);
+}
+
+/************************************************************************/
+/*                                                                      */
+/*    lpfc_handle_eratt                                                 */
+/*    This routine will handle processing a Host Attention              */
+/*    Error Status event. This will be initialized                      */
+/*    as a SLI layer callback routine.                                  */
+/*                                                                      */
+/************************************************************************/
+void
+lpfc_handle_eratt(struct lpfc_hba * phba, uint32_t status)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring  *pring;
+       struct lpfc_iocbq     *iocb, *next_iocb;
+       IOCB_t          *icmd = NULL, *cmd = NULL;
+       struct lpfc_scsi_buf  *lpfc_cmd;
+       volatile uint32_t status1, status2;
+       void *from_slim;
+       unsigned long iflag;
+
+       psli = &phba->sli;
+       from_slim = ((uint8_t *)phba->MBslimaddr + 0xa8);
+       status1 = readl( from_slim);
+       from_slim =  ((uint8_t *)phba->MBslimaddr + 0xac);
+       status2 = readl( from_slim);
+
+       if (status & HS_FFER6) {
+               /* Re-establishing Link */
+               spin_lock_irqsave(phba->host->host_lock, iflag);
+               lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
+                               "%d:1301 Re-establishing Link "
+                               "Data: x%x x%x x%x\n",
+                               phba->brd_no, status, status1, status2);
+               phba->fc_flag |= FC_ESTABLISH_LINK;
+
+               /*
+               * Firmware stops when it triggled erratt with HS_FFER6.
+               * That could cause the I/Os dropped by the firmware.
+               * Error iocb (I/O) on txcmplq and let the SCSI layer
+               * retry it after re-establishing link.
+               */
+               pring = &psli->ring[psli->fcp_ring];
+
+               list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq,
+                                        list) {
+                       cmd = &iocb->iocb;
+
+                       /* Must be a FCP command */
+                       if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                               (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                               (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                               continue;
+                               }
+
+                       /* context1 MUST be a struct lpfc_scsi_buf */
+                       lpfc_cmd = (struct lpfc_scsi_buf *)(iocb->context1);
+                       if (lpfc_cmd == 0) {
+                               continue;
+                       }
+
+                       list_del(&iocb->list);
+                       pring->txcmplq_cnt--;
+
+                       if (iocb->iocb_cmpl) {
+                               icmd = &iocb->iocb;
+                               icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                               icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                               (iocb->iocb_cmpl)(phba, iocb, iocb);
+                       } else {
+                               mempool_free( iocb, phba->iocb_mem_pool);
+                       }
+               }
+
+               /*
+                * There was a firmware error.  Take the hba offline and then
+                * attempt to restart it.
+                */
+               spin_unlock_irqrestore(phba->host->host_lock, iflag);
+               lpfc_offline(phba);
+               if (lpfc_online(phba) == 0) {   /* Initialize the HBA */
+                       mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60);
+                       return;
+               }
+       } else {
+               /* The if clause above forces this code path when the status
+                * failure is a value other than FFER6.  Do not call the offline
+                *  twice. This is the adapter hardware error path.
+                */
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "%d:0457 Adapter Hardware Error "
+                               "Data: x%x x%x x%x\n",
+                               phba->brd_no, status, status1, status2);
+
+               lpfc_offline(phba);
+
+               /*
+                * Restart all traffic to this host.  Since the fc_transport
+                * block functions (future) were not called in lpfc_offline,
+                * don't call them here.
+                */
+               scsi_unblock_requests(phba->host);
+       }
+       return;
+}
+
+/************************************************************************/
+/*                                                                      */
+/*    lpfc_handle_latt                                                  */
+/*    This routine will handle processing a Host Attention              */
+/*    Link Status event. This will be initialized                       */
+/*    as a SLI layer callback routine.                                  */
+/*                                                                      */
+/************************************************************************/
+void
+lpfc_handle_latt(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       LPFC_MBOXQ_t *pmb;
+       volatile uint32_t control;
+       unsigned long iflag;
+
+
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+
+       /* called from host_interrupt, to process LATT */
+       psli = &phba->sli;
+       psli->slistat.linkEvent++;
+
+       /* Get a buffer which will be used for mailbox commands */
+       if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool,
+                                                 GFP_ATOMIC))) {
+               if (lpfc_read_la(phba, pmb) == 0) {
+                       pmb->mbox_cmpl = lpfc_mbx_cmpl_read_la;
+                       if (lpfc_sli_issue_mbox
+                           (phba, pmb, (MBX_NOWAIT | MBX_STOP_IOCB))
+                           != MBX_NOT_FINISHED) {
+                               /* Turn off Link Attention interrupts until
+                                  CLEAR_LA done */
+                               psli->sliinit.sli_flag &= ~LPFC_PROCESS_LA;
+                               control = readl(phba->HCregaddr);
+                               control &= ~HC_LAINT_ENA;
+                               writel(control, phba->HCregaddr);
+                               readl(phba->HCregaddr); /* flush */
+
+                               /* Clear Link Attention in HA REG */
+                               writel(HA_LATT, phba->HAregaddr);
+                               readl(phba->HAregaddr); /* flush */
+                               spin_unlock_irqrestore(phba->host->host_lock,
+                                                      iflag);
+                               return;
+                       } else {
+                               mempool_free(pmb, phba->mbox_mem_pool);
+                       }
+               } else {
+                       mempool_free(pmb, phba->mbox_mem_pool);
+               }
+       }
+
+       /* Clear Link Attention in HA REG */
+       writel(HA_LATT, phba->HAregaddr);
+       readl(phba->HAregaddr); /* flush */
+       lpfc_linkdown(phba);
+       phba->hba_state = LPFC_HBA_ERROR;
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+       return;
+}
+
+/************************************************************************/
+/*                                                                      */
+/*   lpfc_parse_vpd                                                     */
+/*   This routine will parse the VPD data                               */
+/*                                                                      */
+/************************************************************************/
+static int
+lpfc_parse_vpd(struct lpfc_hba * phba, uint8_t * vpd)
+{
+       uint8_t lenlo, lenhi;
+       uint8_t *Length;
+       int i, j;
+       int finished = 0;
+       int index = 0;
+
+       /* Vital Product */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_INIT,
+                       "%d:0455 Vital Product Data: x%x x%x x%x x%x\n",
+                       phba->brd_no,
+                       (uint32_t) vpd[0], (uint32_t) vpd[1], (uint32_t) vpd[2],
+                       (uint32_t) vpd[3]);
+       do {
+               switch (vpd[index]) {
+               case 0x82:
+                       index += 1;
+                       lenlo = vpd[index];
+                       index += 1;
+                       lenhi = vpd[index];
+                       index += 1;
+                       i = ((((unsigned short)lenhi) << 8) + lenlo);
+                       index += i;
+                       break;
+               case 0x90:
+                       index += 1;
+                       lenlo = vpd[index];
+                       index += 1;
+                       lenhi = vpd[index];
+                       index += 1;
+                       i = ((((unsigned short)lenhi) << 8) + lenlo);
+                       do {
+                               /* Look for Serial Number */
+                               if ((vpd[index] == 'S')
+                                   && (vpd[index + 1] == 'N')) {
+                                       index += 2;
+                                       Length = &vpd[index];
+                                       index += 1;
+                                       i = *Length;
+                                       j = 0;
+                                       while (i--) {
+                                               phba->SerialNumber[j++] =
+                                                   vpd[index++];
+                                               if (j == 31)
+                                                       break;
+                                       }
+                                       phba->SerialNumber[j] = 0;
+                                       return (1);
+                               } else {
+                                       index += 2;
+                                       Length = &vpd[index];
+                                       index += 1;
+                                       j = (int)(*Length);
+                                       index += j;
+                                       i -= (3 + j);
+                               }
+                       } while (i > 0);
+                       finished = 0;
+                       break;
+               case 0x78:
+                       finished = 1;
+                       break;
+               default:
+                       return (0);
+               }
+       } while (!finished);
+       return (1);
+}
+
+/**************************************************/
+/*   lpfc_post_buffer                             */
+/*                                                */
+/*   This routine will post count buffers to the  */
+/*   ring with the QUE_RING_BUF_CN command. This  */
+/*   allows 3 buffers / command to be posted.     */
+/*   Returns the number of buffers NOT posted.    */
+/**************************************************/
+int
+lpfc_post_buffer(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, int cnt,
+                int type)
+{
+       IOCB_t *icmd;
+       struct lpfc_iocbq *iocb;
+       struct lpfc_dmabuf *mp1, *mp2;
+
+       cnt += pring->missbufcnt;
+
+       /* While there are buffers to post */
+       while (cnt > 0) {
+               /* Allocate buffer for  command iocb */
+               if ((iocb = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC))
+                   == 0) {
+                       pring->missbufcnt = cnt;
+                       return (cnt);
+               }
+               memset(iocb, 0, sizeof (struct lpfc_iocbq));
+               icmd = &iocb->iocb;
+
+               /* 2 buffers can be posted per command */
+               /* Allocate buffer to post */
+               mp1 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC);
+               if (mp1)
+                   mp1->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
+                                               &mp1->phys);
+               if (mp1 == 0 || mp1->virt == 0) {
+                       if (mp1)
+                               kfree(mp1);
+
+                       mempool_free( iocb, phba->iocb_mem_pool);
+                       pring->missbufcnt = cnt;
+                       return (cnt);
+               }
+
+               INIT_LIST_HEAD(&mp1->list);
+               /* Allocate buffer to post */
+               if (cnt > 1) {
+                       mp2 = kmalloc(sizeof (struct lpfc_dmabuf), GFP_KERNEL);
+                       if (mp2)
+                               mp2->virt = lpfc_mbuf_alloc(phba, MEM_PRI,
+                                                           &mp2->phys);
+                       if (mp2 == 0 || mp2->virt == 0) {
+                               if (mp2)
+                                       kfree(mp2);
+                               lpfc_mbuf_free(phba, mp1->virt, mp1->phys);
+                               kfree(mp1);
+                               mempool_free( iocb, phba->iocb_mem_pool);
+                               pring->missbufcnt = cnt;
+                               return (cnt);
+                       }
+
+                       INIT_LIST_HEAD(&mp2->list);
+               } else {
+                       mp2 = NULL;
+               }
+
+               icmd->un.cont64[0].addrHigh = putPaddrHigh(mp1->phys);
+               icmd->un.cont64[0].addrLow = putPaddrLow(mp1->phys);
+               icmd->un.cont64[0].tus.f.bdeSize = FCELSSIZE;
+               icmd->ulpBdeCount = 1;
+               cnt--;
+               if (mp2) {
+                       icmd->un.cont64[1].addrHigh = putPaddrHigh(mp2->phys);
+                       icmd->un.cont64[1].addrLow = putPaddrLow(mp2->phys);
+                       icmd->un.cont64[1].tus.f.bdeSize = FCELSSIZE;
+                       cnt--;
+                       icmd->ulpBdeCount = 2;
+               }
+
+               icmd->ulpCommand = CMD_QUE_RING_BUF64_CN;
+               icmd->ulpIoTag = lpfc_sli_next_iotag(phba, pring);
+               icmd->ulpLe = 1;
+
+               if (lpfc_sli_issue_iocb(phba, pring, iocb, 0) == IOCB_ERROR) {
+                       lpfc_mbuf_free(phba, mp1->virt, mp1->phys);
+                       kfree(mp1);
+                       if (mp2) {
+                               lpfc_mbuf_free(phba, mp2->virt, mp2->phys);
+                               kfree(mp2);
+                       }
+                       mempool_free( iocb, phba->iocb_mem_pool);
+                       pring->missbufcnt = cnt;
+                       return (cnt);
+               }
+               lpfc_sli_ringpostbuf_put(phba, pring, mp1);
+               if (mp2) {
+                       lpfc_sli_ringpostbuf_put(phba, pring, mp2);
+               }
+       }
+       pring->missbufcnt = 0;
+       return (0);
+}
+
+/************************************************************************/
+/*                                                                      */
+/*   lpfc_post_rcv_buf                                                  */
+/*   This routine post initial rcv buffers to the configured rings      */
+/*                                                                      */
+/************************************************************************/
+static int
+lpfc_post_rcv_buf(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+
+       /* Ring 0, ELS / CT buffers */
+       lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0, 1);
+       /* Ring 2 - FCP no buffers needed */
+
+       return 0;
+}
+
+#define S(N,V) (((V)<<(N))|((V)>>(32-(N))))
+
+/************************************************************************/
+/*                                                                      */
+/*   lpfc_sha_init                                                      */
+/*                                                                      */
+/************************************************************************/
+static void
+lpfc_sha_init(uint32_t * HashResultPointer)
+{
+       HashResultPointer[0] = 0x67452301;
+       HashResultPointer[1] = 0xEFCDAB89;
+       HashResultPointer[2] = 0x98BADCFE;
+       HashResultPointer[3] = 0x10325476;
+       HashResultPointer[4] = 0xC3D2E1F0;
+}
+
+/************************************************************************/
+/*                                                                      */
+/*   lpfc_sha_iterate                                                   */
+/*                                                                      */
+/************************************************************************/
+static void
+lpfc_sha_iterate(uint32_t * HashResultPointer, uint32_t * HashWorkingPointer)
+{
+       int t;
+       uint32_t TEMP;
+       uint32_t A, B, C, D, E;
+       t = 16;
+       do {
+               HashWorkingPointer[t] =
+                   S(1,
+                     HashWorkingPointer[t - 3] ^ HashWorkingPointer[t -
+                                                                    8] ^
+                     HashWorkingPointer[t - 14] ^ HashWorkingPointer[t - 16]);
+       } while (++t <= 79);
+       t = 0;
+       A = HashResultPointer[0];
+       B = HashResultPointer[1];
+       C = HashResultPointer[2];
+       D = HashResultPointer[3];
+       E = HashResultPointer[4];
+
+       do {
+               if (t < 20) {
+                       TEMP = ((B & C) | ((~B) & D)) + 0x5A827999;
+               } else if (t < 40) {
+                       TEMP = (B ^ C ^ D) + 0x6ED9EBA1;
+               } else if (t < 60) {
+                       TEMP = ((B & C) | (B & D) | (C & D)) + 0x8F1BBCDC;
+               } else {
+                       TEMP = (B ^ C ^ D) + 0xCA62C1D6;
+               }
+               TEMP += S(5, A) + E + HashWorkingPointer[t];
+               E = D;
+               D = C;
+               C = S(30, B);
+               B = A;
+               A = TEMP;
+       } while (++t <= 79);
+
+       HashResultPointer[0] += A;
+       HashResultPointer[1] += B;
+       HashResultPointer[2] += C;
+       HashResultPointer[3] += D;
+       HashResultPointer[4] += E;
+
+}
+
+/************************************************************************/
+/*                                                                      */
+/*   lpfc_challenge_key                                                 */
+/*                                                                      */
+/************************************************************************/
+static void
+lpfc_challenge_key(uint32_t * RandomChallenge, uint32_t * HashWorking)
+{
+       *HashWorking = (*RandomChallenge ^ *HashWorking);
+}
+
+/************************************************************************/
+/*                                                                      */
+/*   lpfc_hba_init                                                      */
+/*                                                                      */
+/************************************************************************/
+void
+lpfc_hba_init(struct lpfc_hba *phba, uint32_t *hbainit)
+{
+       int t;
+       uint32_t *HashWorking;
+       uint32_t *pwwnn = phba->wwnn;
+
+       HashWorking = kmalloc(80 * sizeof(uint32_t), GFP_ATOMIC);
+       if (!HashWorking)
+               return;
+       
+       memset(HashWorking, 0, (80 * sizeof(uint32_t)));
+       HashWorking[0] = HashWorking[78] = *pwwnn++;
+       HashWorking[1] = HashWorking[79] = *pwwnn;
+
+       for (t = 0; t < 7; t++)
+               lpfc_challenge_key(phba->RandomData + t, HashWorking + t);
+
+       lpfc_sha_init(hbainit);
+       lpfc_sha_iterate(hbainit, HashWorking);
+       kfree(HashWorking);
+}
+
+static void
+lpfc_consistent_bind_cleanup(struct lpfc_hba * phba)
+{
+       struct lpfc_bindlist *bdlp, *next_bdlp;
+
+       list_for_each_entry_safe(bdlp, next_bdlp,
+                                &phba->fc_nlpbind_list, nlp_listp) {
+               list_del(&bdlp->nlp_listp);
+               mempool_free( bdlp, phba->bind_mem_pool);
+       }
+       phba->fc_bind_cnt = 0;
+}
+
+void
+lpfc_cleanup(struct lpfc_hba * phba, uint32_t save_bind)
+{
+       struct lpfc_nodelist *ndlp, *next_ndlp;
+
+       /* clean up phba - lpfc specific */
+       lpfc_can_disctmo(phba);
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpunmap_list,
+                               nlp_listp) {
+               lpfc_nlp_remove(phba, ndlp);
+       }
+
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_nlpmap_list,
+                                nlp_listp) {
+               lpfc_nlp_remove(phba, ndlp);
+       }
+
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_unused_list,
+                               nlp_listp) {
+               lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       }
+
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_plogi_list,
+                               nlp_listp) {
+               lpfc_nlp_remove(phba, ndlp);
+       }
+
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_adisc_list,
+                               nlp_listp) {
+               lpfc_nlp_remove(phba, ndlp);
+       }
+
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_reglogin_list,
+                               nlp_listp) {
+               lpfc_nlp_remove(phba, ndlp);
+       }
+
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_prli_list,
+                               nlp_listp) {
+               lpfc_nlp_remove(phba, ndlp);
+       }
+
+       list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
+                               nlp_listp) {
+               lpfc_nlp_remove(phba, ndlp);
+       }
+
+       if (save_bind == 0) {
+               lpfc_consistent_bind_cleanup(phba);
+       }
+
+       INIT_LIST_HEAD(&phba->fc_nlpmap_list);
+       INIT_LIST_HEAD(&phba->fc_nlpunmap_list);
+       INIT_LIST_HEAD(&phba->fc_unused_list);
+       INIT_LIST_HEAD(&phba->fc_plogi_list);
+       INIT_LIST_HEAD(&phba->fc_adisc_list);
+       INIT_LIST_HEAD(&phba->fc_reglogin_list);
+       INIT_LIST_HEAD(&phba->fc_prli_list);
+       INIT_LIST_HEAD(&phba->fc_npr_list);
+
+       phba->fc_map_cnt   = 0;
+       phba->fc_unmap_cnt = 0;
+       phba->fc_plogi_cnt = 0;
+       phba->fc_adisc_cnt = 0;
+       phba->fc_reglogin_cnt = 0;
+       phba->fc_prli_cnt  = 0;
+       phba->fc_npr_cnt   = 0;
+       phba->fc_unused_cnt= 0;
+       return;
+}
+
+void
+lpfc_establish_link_tmo(unsigned long ptr)
+{
+       struct lpfc_hba *phba = (struct lpfc_hba *)ptr;
+       unsigned long iflag;
+
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+
+       /* Re-establishing Link, timer expired */
+       lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
+                       "%d:1300 Re-establishing Link, timer expired "
+                       "Data: x%x x%x\n",
+                       phba->brd_no, phba->fc_flag, phba->hba_state);
+       phba->fc_flag &= ~FC_ESTABLISH_LINK;
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+}
+
+int
+lpfc_online(struct lpfc_hba * phba)
+{
+       if (!phba)
+               return 0;
+
+       if (!(phba->fc_flag & FC_OFFLINE_MODE))
+               return 0;
+
+       lpfc_printf_log(phba,
+                      KERN_WARNING,
+                      LOG_INIT,
+                      "%d:0458 Bring Adapter online\n",
+                      phba->brd_no);
+
+       if (!lpfc_sli_queue_setup(phba))
+               return 1;
+
+       if (lpfc_sli_hba_setup(phba))   /* Initialize the HBA */
+               return 1;
+
+       phba->fc_flag &= ~FC_OFFLINE_MODE;
+
+       /*
+        * Restart all traffic to this host.  Since the fc_transport block
+        * functions (future) were not called in lpfc_offline, don't call them
+        * here.
+        */
+       scsi_unblock_requests(phba->host);
+       return 0;
+}
+
+int
+lpfc_offline(struct lpfc_hba * phba)
+{
+       struct lpfc_sli_ring *pring;
+       struct lpfc_sli *psli;
+       unsigned long iflag;
+       int i = 0;
+
+       if (!phba)
+               return 0;
+
+       if (phba->fc_flag & FC_OFFLINE_MODE)
+               return 0;
+
+       /*
+        * Don't call the fc_transport block api (future).  The device is
+        * going offline and causing a timer to fire in the midlayer is
+        * unproductive.  Just block all new requests until the driver
+        * comes back online.
+        */
+       scsi_block_requests(phba->host);
+       psli = &phba->sli;
+       pring = &psli->ring[psli->fcp_ring];
+
+       lpfc_linkdown(phba);
+
+       /* The linkdown event takes 30 seconds to timeout. */
+       while (pring->txcmplq_cnt) {
+               mdelay(10);
+               if (i++ > 3000)
+                       break;
+       }
+
+       /* stop all timers associated with this hba */
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+       lpfc_stop_timer(phba);
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+
+       lpfc_printf_log(phba,
+                      KERN_WARNING,
+                      LOG_INIT,
+                      "%d:0460 Bring Adapter offline\n",
+                      phba->brd_no);
+
+       /* Bring down the SLI Layer and cleanup.  The HBA is offline
+          now.  */
+       lpfc_sli_hba_down(phba);
+       lpfc_cleanup(phba, 1);
+       phba->fc_flag |= FC_OFFLINE_MODE;
+       return 0;
+}
+
+/******************************************************************************
+* Function name : lpfc_scsi_free
+*
+* Description   : Called from fc_detach to free scsi tgt / lun resources
+*
+******************************************************************************/
+int
+lpfc_scsi_free(struct lpfc_hba * phba)
+{
+       struct lpfc_target *targetp;
+       int i;
+
+       for (i = 0; i < MAX_FCP_TARGET; i++) {
+               targetp = phba->device_queue_hash[i];
+               if (targetp) {
+                       kfree(targetp);
+                       phba->device_queue_hash[i] = NULL;
+               }
+       }
+       return 0;
+}
+
+static void
+lpfc_wakeup_event(struct lpfc_hba * phba, fcEVTHDR_t * ep)
+{
+       ep->e_mode &= ~E_SLEEPING_MODE;
+       switch (ep->e_mask) {
+       case FC_REG_LINK_EVENT:
+               wake_up_interruptible(&phba->linkevtwq);
+               break;
+       case FC_REG_RSCN_EVENT:
+               wake_up_interruptible(&phba->rscnevtwq);
+               break;
+       case FC_REG_CT_EVENT:
+               wake_up_interruptible(&phba->ctevtwq);
+               break;
+       }
+       return;
+}
+
+int
+lpfc_put_event(struct lpfc_hba * phba, uint32_t evcode, uint32_t evdata0,
+              void * evdata1, uint32_t evdata2, uint32_t evdata3)
+{
+       fcEVT_t *ep;
+       fcEVTHDR_t *ehp = phba->fc_evt_head;
+       int found = 0;
+       void *fstype = NULL;
+       struct lpfc_dmabuf *mp;
+       struct lpfc_sli_ct_request *ctp;
+       struct lpfc_hba_event *rec;
+       uint32_t evtype;
+
+       switch (evcode) {
+               case HBA_EVENT_RSCN:
+                       evtype = FC_REG_RSCN_EVENT;
+                       break;
+               case HBA_EVENT_LINK_DOWN:
+               case HBA_EVENT_LINK_UP:
+                       evtype = FC_REG_LINK_EVENT;
+                       break;
+               default:
+                       evtype = FC_REG_CT_EVENT;
+       }
+
+       if (evtype == FC_REG_RSCN_EVENT || evtype == FC_REG_LINK_EVENT) {
+               rec = &phba->hbaevt[phba->hba_event_put];
+               rec->fc_eventcode = evcode;
+               rec->fc_evdata1 = evdata0;
+               rec->fc_evdata2 = (uint32_t)(unsigned long)evdata1;
+               rec->fc_evdata3 = evdata2;
+               rec->fc_evdata4 = evdata3;
+
+               phba->hba_event_put++;
+               if (phba->hba_event_put >= MAX_HBAEVT)
+                       phba->hba_event_put = 0;
+
+               if (phba->hba_event_put == phba->hba_event_get) {
+                       phba->hba_event_missed++;
+                       phba->hba_event_get++;
+                       if (phba->hba_event_get >= MAX_HBAEVT)
+                               phba->hba_event_get = 0;
+               }
+       }
+
+       if (evtype == FC_REG_CT_EVENT) {
+               mp = (struct lpfc_dmabuf *) evdata1;
+               ctp = (struct lpfc_sli_ct_request *) mp->virt;
+               fstype = (void *)(ulong) (ctp->FsType);
+       }
+
+       while (ehp && ((ehp->e_mask != evtype) || (ehp->e_type != fstype)))
+               ehp = (fcEVTHDR_t *) ehp->e_next_header;
+
+       if (!ehp)
+               return (0);
+
+       ep = ehp->e_head;
+
+       while (ep && !(found)) {
+               if (ep->evt_sleep) {
+                       switch (evtype) {
+                       case FC_REG_CT_EVENT:
+                               if ((ep->evt_type ==
+                                    (void *)(ulong) FC_FSTYPE_ALL)
+                                   || (ep->evt_type == fstype)) {
+                                       found++;
+                                       ep->evt_data0 = evdata0; /* tag */
+                                       ep->evt_data1 = evdata1; /* buffer
+                                                                   ptr */
+                                       ep->evt_data2 = evdata2; /* count */
+                                       ep->evt_sleep = 0;
+                                       if (ehp->e_mode & E_SLEEPING_MODE) {
+                                               ehp->e_flag |=
+                                                   E_GET_EVENT_ACTIVE;
+                                               lpfc_wakeup_event(phba, ehp);
+                                       }
+                                       /* For FC_REG_CT_EVENT just give it to
+                                          first one found */
+                               }
+                               break;
+                       default:
+                               found++;
+                               ep->evt_data0 = evdata0;
+                               ep->evt_data1 = evdata1;
+                               ep->evt_data2 = evdata2;
+                               ep->evt_sleep = 0;
+                               if ((ehp->e_mode & E_SLEEPING_MODE)
+                                   && !(ehp->e_flag & E_GET_EVENT_ACTIVE)) {
+                                       ehp->e_flag |= E_GET_EVENT_ACTIVE;
+                                       lpfc_wakeup_event(phba, ehp);
+                               }
+                               /* For all other events, give it to every one
+                                  waiting */
+                               break;
+                       }
+               }
+               ep = ep->evt_next;
+       }
+       if (evtype == FC_REG_LINK_EVENT)
+               phba->nport_event_cnt++;
+
+       return (found);
+}
+
+int
+lpfc_stop_timer(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+
+       /* Instead of a timer, this has been converted to a
+        * deferred procedding list.
+        */
+       while (!list_empty(&phba->freebufList)) {
+               struct lpfc_dmabuf *mp;
+
+               mp = (struct lpfc_dmabuf *)(phba->freebufList.next);
+               if (mp) {
+                       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+                       list_del(&mp->list);
+                       kfree(mp);
+               }
+       }
+
+       del_timer_sync(&phba->fc_estabtmo);
+       del_timer_sync(&phba->fc_disctmo);
+       del_timer_sync(&phba->fc_scantmo);
+       del_timer_sync(&phba->fc_fdmitmo);
+       del_timer_sync(&phba->els_tmofunc);
+       psli = &phba->sli;
+       del_timer_sync(&psli->mbox_tmo);
+       return(1);
+}
diff --git a/drivers/scsi/lpfc/lpfc_logmsg.h b/drivers/scsi/lpfc/lpfc_logmsg.h
new file mode 100644 (file)
index 0000000..5540e23
--- /dev/null
@@ -0,0 +1,46 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_logmsg.h 1.31 2004/08/26 19:17:52EDT sf_support Exp  $
+ */
+
+#ifndef _H_LPFC_LOGMSG
+#define _H_LPFC_LOGMSG
+
+#define LOG_ELS                       0x1      /* ELS events */
+#define LOG_DISCOVERY                 0x2      /* Link discovery events */
+#define LOG_MBOX                      0x4      /* Mailbox events */
+#define LOG_INIT                      0x8      /* Initialization events */
+#define LOG_LINK_EVENT                0x10     /* Link events */
+#define LOG_IP                        0x20     /* IP traffic history */
+#define LOG_FCP                       0x40     /* FCP traffic history */
+#define LOG_NODE                      0x80     /* Node table events */
+#define LOG_MISC                      0x400    /* Miscellaneous events */
+#define LOG_SLI                       0x800    /* SLI events */
+#define LOG_CHK_COND                  0x1000   /* FCP Check condition flag */
+#define LOG_LIBDFC                    0x2000   /* Libdfc events */
+#define LOG_ALL_MSG                   0xffff   /* LOG all messages */
+
+#define lpfc_printf_log(phba, level, mask, fmt, arg...) \
+       { if (((mask) &(phba)->cfg_log_verbose) || (level[1] <= '3')) \
+               dev_printk(level, &((phba)->pcidev)->dev, fmt, ##arg); }
+#endif
+
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
new file mode 100644 (file)
index 0000000..436d1b7
--- /dev/null
@@ -0,0 +1,672 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_mbox.c 1.70 2004/11/18 17:33:04EST sf_support Exp  $
+ */
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <scsi/scsi_device.h>
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+#include "lpfc_hw.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_mem.h"
+#include "lpfc_compat.h"
+
+/**********************************************/
+
+/*                mailbox command             */
+/**********************************************/
+void
+lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       MAILBOX_t *mb;
+
+       mb = &pmb->mb;
+       /* Setup to dump VPD region */
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+       mb->mbxCommand = MBX_DUMP_MEMORY;
+       mb->un.varDmp.cv = 1;
+       mb->un.varDmp.type = DMP_NV_PARAMS;
+       mb->un.varDmp.region_id = DMP_REGION_VPD;
+       mb->un.varDmp.word_cnt = (DMP_VPD_SIZE / sizeof (uint32_t));
+
+       mb->un.varDmp.co = 0;
+       mb->un.varDmp.resp_offset = 0;
+       mb->mbxOwner = OWN_HOST;
+       return;
+}
+
+/**********************************************/
+/*  lpfc_read_nv  Issue a READ NVPARAM        */
+/*                mailbox command             */
+/**********************************************/
+void
+lpfc_read_nv(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       MAILBOX_t *mb;
+
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+       mb->mbxCommand = MBX_READ_NV;
+       mb->mbxOwner = OWN_HOST;
+       return;
+}
+
+/**********************************************/
+/*  lpfc_read_la  Issue a READ LA             */
+/*                mailbox command             */
+/**********************************************/
+int
+lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       MAILBOX_t *mb;
+       struct lpfc_dmabuf *mp;
+       struct lpfc_sli *psli;
+
+       psli = &phba->sli;
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       /* Get a buffer to hold the loop map */
+       if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) ||
+           ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) {
+               if (mp)
+                       kfree(mp);
+               mb->mbxCommand = MBX_READ_LA64;
+               /* READ_LA: no buffers */
+               lpfc_printf_log(phba,
+                              KERN_WARNING,
+                              LOG_MBOX,
+                              "%d:0300 READ_LA: no buffers\n",
+                              phba->brd_no);
+               return (1);
+       }
+       INIT_LIST_HEAD(&mp->list);
+       mb->mbxCommand = MBX_READ_LA64;
+       mb->un.varReadLA.un.lilpBde64.tus.f.bdeSize = 128;
+       mb->un.varReadLA.un.lilpBde64.addrHigh = putPaddrHigh(mp->phys);
+       mb->un.varReadLA.un.lilpBde64.addrLow = putPaddrLow(mp->phys);
+
+       /* Sync the mailbox data with its PCI memory address now. */
+       pci_dma_sync_single_for_device(phba->pcidev, mp->phys, LPFC_BPL_SIZE,
+                       PCI_DMA_TODEVICE);
+
+       /* Save address for later completion and set the owner to host so that
+        * the FW knows this mailbox is available for processing.
+        */
+       pmb->context1 = (uint8_t *) mp;
+       mb->mbxOwner = OWN_HOST;
+       return (0);
+}
+
+/**********************************************/
+/*  lpfc_clear_la  Issue a CLEAR LA           */
+/*                 mailbox command            */
+/**********************************************/
+void
+lpfc_clear_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       MAILBOX_t *mb;
+
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       mb->un.varClearLA.eventTag = phba->fc_eventTag;
+       mb->mbxCommand = MBX_CLEAR_LA;
+       mb->mbxOwner = OWN_HOST;
+       return;
+}
+
+/**************************************************/
+/*  lpfc_config_link  Issue a CONFIG LINK         */
+/*                    mailbox command             */
+/**************************************************/
+void
+lpfc_config_link(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       MAILBOX_t *mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       /* NEW_FEATURE
+        * SLI-2, Coalescing Response Feature.
+        */
+       if (phba->cfg_cr_delay) {
+               mb->un.varCfgLnk.cr = 1;
+               mb->un.varCfgLnk.ci = 1;
+               mb->un.varCfgLnk.cr_delay = phba->cfg_cr_delay;
+               mb->un.varCfgLnk.cr_count = phba->cfg_cr_count;
+       }
+
+       mb->un.varCfgLnk.myId = phba->fc_myDID;
+       mb->un.varCfgLnk.edtov = phba->fc_edtov;
+       mb->un.varCfgLnk.arbtov = phba->fc_arbtov;
+       mb->un.varCfgLnk.ratov = phba->fc_ratov;
+       mb->un.varCfgLnk.rttov = phba->fc_rttov;
+       mb->un.varCfgLnk.altov = phba->fc_altov;
+       mb->un.varCfgLnk.crtov = phba->fc_crtov;
+       mb->un.varCfgLnk.citov = phba->fc_citov;
+
+       if (phba->cfg_ack0)
+               mb->un.varCfgLnk.ack0_enable = 1;
+
+       mb->mbxCommand = MBX_CONFIG_LINK;
+       mb->mbxOwner = OWN_HOST;
+       return;
+}
+
+/**********************************************/
+/*  lpfc_init_link  Issue an INIT LINK        */
+/*                  mailbox command           */
+/**********************************************/
+void
+lpfc_init_link(struct lpfc_hba * phba,
+              LPFC_MBOXQ_t * pmb, uint32_t topology, uint32_t linkspeed)
+{
+       lpfc_vpd_t *vpd;
+       struct lpfc_sli *psli;
+       MAILBOX_t *mb;
+
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       psli = &phba->sli;
+       switch (topology) {
+       case FLAGS_TOPOLOGY_MODE_LOOP_PT:
+               mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP;
+               mb->un.varInitLnk.link_flags |= FLAGS_TOPOLOGY_FAILOVER;
+               break;
+       case FLAGS_TOPOLOGY_MODE_PT_PT:
+               mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT;
+               break;
+       case FLAGS_TOPOLOGY_MODE_LOOP:
+               mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_LOOP;
+               break;
+       case FLAGS_TOPOLOGY_MODE_PT_LOOP:
+               mb->un.varInitLnk.link_flags = FLAGS_TOPOLOGY_MODE_PT_PT;
+               mb->un.varInitLnk.link_flags |= FLAGS_TOPOLOGY_FAILOVER;
+               break;
+       }
+
+       /* NEW_FEATURE
+        * Setting up the link speed
+        */
+       vpd = &phba->vpd;
+       if (vpd->rev.feaLevelHigh >= 0x02){
+               switch(linkspeed){
+                       case LINK_SPEED_1G:
+                       case LINK_SPEED_2G:
+                       case LINK_SPEED_4G:
+                               mb->un.varInitLnk.link_flags |= 
+                                                       FLAGS_LINK_SPEED;
+                               mb->un.varInitLnk.link_speed = linkspeed;
+                       break;
+                       case LINK_SPEED_AUTO:
+                       default:
+                               mb->un.varInitLnk.link_speed = 
+                                                       LINK_SPEED_AUTO;
+                       break;
+               }
+               
+       }
+       else
+               mb->un.varInitLnk.link_speed = LINK_SPEED_AUTO;
+
+       mb->mbxCommand = (volatile uint8_t)MBX_INIT_LINK;
+       mb->mbxOwner = OWN_HOST;
+       mb->un.varInitLnk.fabric_AL_PA = phba->fc_pref_ALPA;
+       return;
+}
+
+/**********************************************/
+/*  lpfc_read_sparam  Issue a READ SPARAM     */
+/*                    mailbox command         */
+/**********************************************/
+int
+lpfc_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       struct lpfc_dmabuf *mp;
+       MAILBOX_t *mb;
+       struct lpfc_sli *psli;
+
+       psli = &phba->sli;
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       mb->mbxOwner = OWN_HOST;
+
+       /* Get a buffer to hold the HBAs Service Parameters */
+
+       if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) ||
+           ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) {
+               if (mp)
+                       kfree(mp);
+               mb->mbxCommand = MBX_READ_SPARM64;
+               /* READ_SPARAM: no buffers */
+               lpfc_printf_log(phba,
+                               KERN_WARNING,
+                               LOG_MBOX,
+                               "%d:0301 READ_SPARAM: no buffers\n",
+                               phba->brd_no);
+               return (1);
+       }
+       INIT_LIST_HEAD(&mp->list);
+       mb->mbxCommand = MBX_READ_SPARM64;
+       mb->un.varRdSparm.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm);
+       mb->un.varRdSparm.un.sp64.addrHigh = putPaddrHigh(mp->phys);
+       mb->un.varRdSparm.un.sp64.addrLow = putPaddrLow(mp->phys);
+
+       pci_dma_sync_single_for_device(phba->pcidev, mp->phys, LPFC_BPL_SIZE,
+                       PCI_DMA_TODEVICE);
+
+       /* save address for completion */
+       pmb->context1 = mp;
+
+       return (0);
+}
+
+/********************************************/
+/*  lpfc_unreg_did  Issue a UNREG_DID       */
+/*                  mailbox command         */
+/********************************************/
+void
+lpfc_unreg_did(struct lpfc_hba * phba, uint32_t did, LPFC_MBOXQ_t * pmb)
+{
+       MAILBOX_t *mb;
+
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       mb->un.varUnregDID.did = did;
+
+       mb->mbxCommand = MBX_UNREG_D_ID;
+       mb->mbxOwner = OWN_HOST;
+       return;
+}
+
+/***********************************************/
+
+/*                  command to write slim      */
+/***********************************************/
+void
+lpfc_set_slim(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint32_t addr,
+             uint32_t value)
+{
+       MAILBOX_t *mb;
+
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       /* addr = 0x090597 is AUTO ABTS disable for ELS commands */
+       /* addr = 0x052198 is DELAYED ABTS enable for ELS commands */
+
+       /*
+        * Always turn on DELAYED ABTS for ELS timeouts
+        */
+       if ((addr == 0x052198) && (value == 0))
+               value = 1;
+
+       mb->un.varWords[0] = addr;
+       mb->un.varWords[1] = value;
+
+       mb->mbxCommand = MBX_SET_SLIM;
+       mb->mbxOwner = OWN_HOST;
+       return;
+}
+
+/**********************************************/
+/*  lpfc_read_nv  Issue a READ CONFIG         */
+/*                mailbox command             */
+/**********************************************/
+void
+lpfc_read_config(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       MAILBOX_t *mb;
+
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       mb->mbxCommand = MBX_READ_CONFIG;
+       mb->mbxOwner = OWN_HOST;
+       return;
+}
+
+/********************************************/
+/*  lpfc_reg_login  Issue a REG_LOGIN       */
+/*                  mailbox command         */
+/********************************************/
+int
+lpfc_reg_login(struct lpfc_hba * phba,
+              uint32_t did, uint8_t * param, LPFC_MBOXQ_t * pmb, uint32_t flag)
+{
+       uint8_t *sparam;
+       struct lpfc_dmabuf *mp;
+       MAILBOX_t *mb;
+       struct lpfc_sli *psli;
+
+       psli = &phba->sli;
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       mb->un.varRegLogin.rpi = 0;
+       mb->un.varRegLogin.did = did;
+       mb->un.varWords[30] = flag;     /* Set flag to issue action on cmpl */
+
+       mb->mbxOwner = OWN_HOST;
+
+       /* Get a buffer to hold NPorts Service Parameters */
+       if (((mp = kmalloc(sizeof (struct lpfc_dmabuf), GFP_ATOMIC)) == 0) ||
+           ((mp->virt = lpfc_mbuf_alloc(phba, 0, &(mp->phys))) == 0)) {
+               if (mp)
+                       kfree(mp);
+
+               mb->mbxCommand = MBX_REG_LOGIN64;
+               /* REG_LOGIN: no buffers */
+               lpfc_printf_log(phba,
+                              KERN_WARNING,
+                              LOG_MBOX,
+                              "%d:0302 REG_LOGIN: no buffers Data x%x x%x\n",
+                              phba->brd_no,
+                              (uint32_t) did, (uint32_t) flag);
+               return (1);
+       }
+       INIT_LIST_HEAD(&mp->list);
+       sparam = mp->virt;
+
+       /* Copy param's into a new buffer */
+       memcpy(sparam, param, sizeof (struct serv_parm));
+
+       /* Sync the mailbox data with its PCI memory address now. */
+       pci_dma_sync_single_for_device(phba->pcidev, mp->phys, LPFC_BPL_SIZE,
+                       PCI_DMA_TODEVICE);
+
+       /* save address for completion */
+       pmb->context1 = (uint8_t *) mp;
+
+       mb->mbxCommand = MBX_REG_LOGIN64;
+       mb->un.varRegLogin.un.sp64.tus.f.bdeSize = sizeof (struct serv_parm);
+       mb->un.varRegLogin.un.sp64.addrHigh = putPaddrHigh(mp->phys);
+       mb->un.varRegLogin.un.sp64.addrLow = putPaddrLow(mp->phys);
+
+       return (0);
+}
+
+/**********************************************/
+/*  lpfc_unreg_login  Issue a UNREG_LOGIN     */
+/*                    mailbox command         */
+/**********************************************/
+void
+lpfc_unreg_login(struct lpfc_hba * phba, uint32_t rpi, LPFC_MBOXQ_t * pmb)
+{
+       MAILBOX_t *mb;
+
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       mb->un.varUnregLogin.rpi = (uint16_t) rpi;
+       mb->un.varUnregLogin.rsvd1 = 0;
+
+       mb->mbxCommand = MBX_UNREG_LOGIN;
+       mb->mbxOwner = OWN_HOST;
+       return;
+}
+
+static void
+lpfc_config_pcb_setup(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_sli_ring *pring;
+       PCB_t *pcbp = &phba->slim2p->pcb;
+       LPFC_RING_INIT_t *pringinit;
+       dma_addr_t pdma_addr;
+       uint32_t offset;
+       uint32_t iocbCnt;
+       int i;
+
+       psli->MBhostaddr = (uint32_t *)&phba->slim2p->mbx;
+       pcbp->maxRing = (psli->sliinit.num_rings - 1);
+
+       iocbCnt = 0;
+       for (i = 0; i < psli->sliinit.num_rings; i++) {
+               pringinit = &psli->sliinit.ringinit[i];
+               pring = &psli->ring[i];
+               /* A ring MUST have both cmd and rsp entries defined to be
+                  valid */
+               if ((pringinit->numCiocb == 0) || (pringinit->numRiocb == 0)) {
+                       pcbp->rdsc[i].cmdEntries = 0;
+                       pcbp->rdsc[i].rspEntries = 0;
+                       pcbp->rdsc[i].cmdAddrHigh = 0;
+                       pcbp->rdsc[i].rspAddrHigh = 0;
+                       pcbp->rdsc[i].cmdAddrLow = 0;
+                       pcbp->rdsc[i].rspAddrLow = 0;
+                       pring->cmdringaddr = NULL;
+                       pring->rspringaddr = NULL;
+                       continue;
+               }
+               /* Command ring setup for ring */
+               pring->cmdringaddr =
+                   (void *)&phba->slim2p->IOCBs[iocbCnt];
+               pcbp->rdsc[i].cmdEntries = pringinit->numCiocb;
+
+               offset = (uint8_t *)&phba->slim2p->IOCBs[iocbCnt] -
+                        (uint8_t *)phba->slim2p;
+               pdma_addr = phba->slim2p_mapping + offset;
+               pcbp->rdsc[i].cmdAddrHigh = putPaddrHigh(pdma_addr);
+               pcbp->rdsc[i].cmdAddrLow = putPaddrLow(pdma_addr);
+               iocbCnt += pringinit->numCiocb;
+
+               /* Response ring setup for ring */
+               pring->rspringaddr =
+                   (void *)&phba->slim2p->IOCBs[iocbCnt];
+
+               pcbp->rdsc[i].rspEntries = pringinit->numRiocb;
+               offset = (uint8_t *)&phba->slim2p->IOCBs[iocbCnt] -
+                        (uint8_t *)phba->slim2p;
+               pdma_addr = phba->slim2p_mapping + offset;
+               pcbp->rdsc[i].rspAddrHigh = putPaddrHigh(pdma_addr);
+               pcbp->rdsc[i].rspAddrLow = putPaddrLow(pdma_addr);
+               iocbCnt += pringinit->numRiocb;
+       }
+}
+
+void
+lpfc_read_rev(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       MAILBOX_t *mb;
+
+       mb = &pmb->mb;
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+       mb->un.varRdRev.cv = 1;
+       mb->mbxCommand = MBX_READ_REV;
+       mb->mbxOwner = OWN_HOST;
+       return;
+}
+
+void
+lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb)
+{
+       int i;
+       MAILBOX_t *mb = &pmb->mb;
+       struct lpfc_sli *psli;
+       LPFC_RING_INIT_t *pring;
+
+       memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
+
+       mb->un.varCfgRing.ring = ring;
+       mb->un.varCfgRing.maxOrigXchg = 0;
+       mb->un.varCfgRing.maxRespXchg = 0;
+       mb->un.varCfgRing.recvNotify = 1;
+
+       psli = &phba->sli;
+       pring = &psli->sliinit.ringinit[ring];
+       mb->un.varCfgRing.numMask = pring->num_mask;
+       mb->mbxCommand = MBX_CONFIG_RING;
+       mb->mbxOwner = OWN_HOST;
+
+       /* Is this ring configured for a specific profile */
+       if (pring->prt[0].profile) {
+               mb->un.varCfgRing.profile = pring->prt[0].profile;
+               return;
+       }
+
+       /* Otherwise we setup specific rctl / type masks for this ring */
+       for (i = 0; i < pring->num_mask; i++) {
+               mb->un.varCfgRing.rrRegs[i].rval = pring->prt[i].rctl;
+               if (mb->un.varCfgRing.rrRegs[i].rval != FC_ELS_REQ)
+                       mb->un.varCfgRing.rrRegs[i].rmask = 0xff;
+               else
+                       mb->un.varCfgRing.rrRegs[i].rmask = 0xfe;
+               mb->un.varCfgRing.rrRegs[i].tval = pring->prt[i].type;
+               mb->un.varCfgRing.rrRegs[i].tmask = 0xff;
+       }
+
+       return;
+}
+
+void
+lpfc_config_port(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
+{
+       MAILBOX_t *mb = &pmb->mb;
+       dma_addr_t pdma_addr;
+       uint32_t bar_low, bar_high;
+       size_t offset;
+       HGP hgp;
+       void *to_slim;
+
+       memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
+       mb->mbxCommand = MBX_CONFIG_PORT;
+       mb->mbxOwner = OWN_HOST;
+
+       mb->un.varCfgPort.pcbLen = sizeof(PCB_t);
+       offset = (uint8_t *)&phba->slim2p->pcb - (uint8_t *)phba->slim2p;
+       pdma_addr = phba->slim2p_mapping + offset;
+       mb->un.varCfgPort.pcbLow = putPaddrLow(pdma_addr);
+       mb->un.varCfgPort.pcbHigh = putPaddrHigh(pdma_addr);
+
+       /* Now setup pcb */
+       phba->slim2p->pcb.type = TYPE_NATIVE_SLI2;
+       phba->slim2p->pcb.feature = FEATURE_INITIAL_SLI2;
+
+       /* Setup Mailbox pointers */
+       phba->slim2p->pcb.mailBoxSize = sizeof(MAILBOX_t);
+       offset = (uint8_t *)&phba->slim2p->mbx - (uint8_t *)phba->slim2p;
+       pdma_addr = phba->slim2p_mapping + offset;
+       phba->slim2p->pcb.mbAddrHigh = putPaddrHigh(pdma_addr);
+       phba->slim2p->pcb.mbAddrLow = putPaddrLow(pdma_addr);
+
+       /*
+        * Setup Host Group ring pointer.
+        *
+        * For efficiency reasons, the ring get/put pointers can be
+        * placed in adapter memory (SLIM) rather than in host memory.
+        * This allows firmware to avoid PCI reads/writes when updating
+        * and checking pointers.
+        *
+        * The firmware recognizes the use of SLIM memory by comparing
+        * the address of the get/put pointers structure with that of
+        * the SLIM BAR (BAR0).
+        *
+        * Caution: be sure to use the PCI config space value of BAR0/BAR1
+        * (the hardware's view of the base address), not the OS's
+        * value of pci_resource_start() as the OS value may be a cookie
+        * for ioremap/iomap.
+        */
+       
+
+       pci_read_config_dword(phba->pcidev, PCI_BASE_ADDRESS_0, &bar_low);
+       pci_read_config_dword(phba->pcidev, PCI_BASE_ADDRESS_1, &bar_high);
+
+
+       /* mask off BAR0's flag bits 0 - 3 */
+       phba->slim2p->pcb.hgpAddrLow = (bar_low & PCI_BASE_ADDRESS_MEM_MASK) +
+                                       (SLIMOFF*sizeof(uint32_t));
+       if (bar_low & PCI_BASE_ADDRESS_MEM_TYPE_64)
+               phba->slim2p->pcb.hgpAddrHigh = bar_high;
+       else 
+               phba->slim2p->pcb.hgpAddrHigh = 0;
+       /* write HGP data to SLIM at the required longword offset */
+       memset(&hgp, 0, sizeof(HGP));
+       to_slim = (uint8_t *)phba->MBslimaddr + (SLIMOFF*sizeof (uint32_t));
+       lpfc_memcpy_to_slim(to_slim, &hgp, sizeof (HGP));
+
+       /* Setup Port Group ring pointer */
+       offset = (uint8_t *)&phba->slim2p->mbx.us.s2.port -
+                (uint8_t *)phba->slim2p;
+       pdma_addr = phba->slim2p_mapping + offset;
+       phba->slim2p->pcb.pgpAddrHigh = putPaddrHigh(pdma_addr);
+       phba->slim2p->pcb.pgpAddrLow = putPaddrLow(pdma_addr);
+
+       /* Use callback routine to setp rings in the pcb */
+       lpfc_config_pcb_setup(phba);
+
+       /* special handling for LC HBAs */
+       if (lpfc_is_LC_HBA(phba->pcidev->device)) {
+               uint32_t hbainit[5];
+
+               lpfc_hba_init(phba, hbainit);
+
+               memcpy(&mb->un.varCfgPort.hbainit, hbainit, 20);
+       }
+
+       /* Swap PCB if needed */
+       lpfc_sli_pcimem_bcopy((uint32_t *)&phba->slim2p->pcb,
+                             (uint32_t *)&phba->slim2p->pcb,
+                             sizeof (PCB_t));
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                       "%d:0405 Service Level Interface (SLI) 2 selected\n",
+                       phba->brd_no);
+}
+
+void
+lpfc_mbox_put(struct lpfc_hba * phba, LPFC_MBOXQ_t * mbq)
+{
+       struct lpfc_sli *psli;
+
+       psli = &phba->sli;
+
+       list_add_tail(&mbq->list, &psli->mboxq);
+
+       psli->mboxq_cnt++;
+
+       return;
+}
+
+LPFC_MBOXQ_t *
+lpfc_mbox_get(struct lpfc_hba * phba)
+{
+       LPFC_MBOXQ_t *mbq = NULL;
+       struct lpfc_sli *psli = &phba->sli;
+
+       if (!list_empty(&psli->mboxq)) {
+               mbq = list_entry(psli->mboxq.next, LPFC_MBOXQ_t, list);
+               list_del_init(&mbq->list);
+               psli->mboxq_cnt--;
+       }
+
+       return mbq;
+}
diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c
new file mode 100644 (file)
index 0000000..16e35d0
--- /dev/null
@@ -0,0 +1,192 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_mem.c 1.69 2004/09/28 07:54:24EDT sf_support Exp  $
+ */
+
+#include <linux/mempool.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <scsi/scsi_device.h>
+
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+#include "lpfc_mem.h"
+
+static void *
+lpfc_pool_kmalloc(int gfp_flags, void *data)
+{
+       return kmalloc((unsigned long)data, gfp_flags);
+}
+
+static void
+lpfc_pool_kfree(void *obj, void *data)
+{
+       kfree(obj);
+}
+
+int
+lpfc_mem_alloc(struct lpfc_hba * phba)
+{
+       struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
+       int i;
+
+       phba->lpfc_scsi_dma_ext_pool = pci_pool_create("lpfc_scsi_dma_ext_pool",
+                               phba->pcidev, LPFC_SCSI_DMA_EXT_SIZE, 8, 0);
+       if (!phba->lpfc_scsi_dma_ext_pool)
+               goto fail;
+
+       phba->lpfc_mbuf_pool = pci_pool_create("lpfc_mbuf_pool", phba->pcidev,
+                                                       LPFC_BPL_SIZE, 8,0);
+       if (!phba->lpfc_mbuf_pool)
+               goto fail_free_dma_ext_pool;
+
+       pool->elements = kmalloc(sizeof(struct lpfc_dmabuf) *
+                                        LPFC_MBUF_POOL_SIZE, GFP_KERNEL);
+       pool->max_count = 0;
+       pool->current_count = 0;
+       for ( i = 0; i < LPFC_MBUF_POOL_SIZE; i++) {
+               pool->elements[i].virt = pci_pool_alloc(phba->lpfc_mbuf_pool,
+                                      GFP_KERNEL, &pool->elements[i].phys);
+               if (!pool->elements[i].virt)
+                       goto fail_free_mbuf_pool;
+               pool->max_count++;
+               pool->current_count++;
+       }
+
+       phba->iocb_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE,
+                       lpfc_pool_kmalloc, lpfc_pool_kfree,
+                       (void *)(unsigned long)sizeof(struct lpfc_iocbq));
+       if (!phba->iocb_mem_pool)
+               goto fail_free_mbuf_pool;
+
+       phba->scsibuf_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE,
+                       lpfc_pool_kmalloc, lpfc_pool_kfree,
+                       (void *)(unsigned long)sizeof(struct lpfc_scsi_buf));
+       if (!phba->scsibuf_mem_pool)
+               goto fail_free_iocb_pool;
+
+       phba->mbox_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE,
+                               lpfc_pool_kmalloc, lpfc_pool_kfree,
+                               (void *)(unsigned long)sizeof(LPFC_MBOXQ_t));
+       if (!phba->mbox_mem_pool)
+               goto fail_free_scsibuf_pool;
+
+       phba->nlp_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE,
+                       lpfc_pool_kmalloc, lpfc_pool_kfree,
+                       (void *)(unsigned long)sizeof(struct lpfc_nodelist));
+       if (!phba->nlp_mem_pool)
+               goto fail_free_mbox_pool;
+
+       phba->bind_mem_pool = mempool_create(LPFC_MEM_POOL_SIZE,
+                       lpfc_pool_kmalloc, lpfc_pool_kfree,
+                       (void *)(unsigned long)sizeof(struct lpfc_bindlist));
+       if (!phba->bind_mem_pool)
+               goto fail_free_nlp_pool;
+
+       return 0;
+
+ fail_free_nlp_pool:
+       mempool_destroy(phba->nlp_mem_pool);
+ fail_free_mbox_pool:
+       mempool_destroy(phba->mbox_mem_pool);
+ fail_free_scsibuf_pool:
+       mempool_destroy(phba->scsibuf_mem_pool);
+ fail_free_iocb_pool:
+       mempool_destroy(phba->iocb_mem_pool);
+ fail_free_mbuf_pool:
+       while (--i)
+               pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt,
+                                                pool->elements[i].phys);
+       kfree(pool->elements);
+       pci_pool_destroy(phba->lpfc_mbuf_pool);
+ fail_free_dma_ext_pool:
+       pci_pool_destroy(phba->lpfc_scsi_dma_ext_pool);
+ fail:
+       return -ENOMEM;
+}
+
+void
+lpfc_mem_free(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
+       LPFC_MBOXQ_t *mbox, *next_mbox;
+       int i;
+
+       list_for_each_entry_safe(mbox, next_mbox, &psli->mboxq, list) {
+               list_del(&mbox->list);
+               mempool_free(mbox, phba->mbox_mem_pool);
+       }
+
+       psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+       if (psli->mbox_active) {
+               mempool_free(psli->mbox_active, phba->mbox_mem_pool);
+               psli->mbox_active = NULL;
+       }
+
+       for (i = 0; i < pool->current_count; i++)
+               pci_pool_free(phba->lpfc_mbuf_pool, pool->elements[i].virt,
+                                                pool->elements[i].phys);
+       kfree(pool->elements);
+       mempool_destroy(phba->bind_mem_pool);
+       mempool_destroy(phba->nlp_mem_pool);
+       mempool_destroy(phba->mbox_mem_pool);
+       mempool_destroy(phba->scsibuf_mem_pool);
+       mempool_destroy(phba->iocb_mem_pool);
+
+       pci_pool_destroy(phba->lpfc_scsi_dma_ext_pool);
+       pci_pool_destroy(phba->lpfc_mbuf_pool);
+}
+
+void *
+lpfc_mbuf_alloc(struct lpfc_hba *phba, int mem_flags, dma_addr_t *handle)
+{
+       struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
+       void *ret;
+
+       ret = pci_pool_alloc(phba->lpfc_mbuf_pool, GFP_ATOMIC, handle);
+
+       if (!ret && ( mem_flags & MEM_PRI) && pool->current_count) {
+               pool->current_count--;
+               ret = pool->elements[pool->current_count].virt;
+               *handle = pool->elements[pool->current_count].phys;
+       }
+       return ret;
+}
+
+void
+lpfc_mbuf_free(struct lpfc_hba * phba, void *virt, dma_addr_t dma)
+{
+       struct lpfc_dma_pool *pool = &phba->lpfc_mbuf_safety_pool;
+
+       if (pool->current_count < pool->max_count) {
+               pool->elements[pool->current_count].virt = virt;
+               pool->elements[pool->current_count].phys = dma;
+               pool->current_count++;
+       } else {
+               pci_pool_free(phba->lpfc_mbuf_pool, virt, dma);
+       }
+       return;
+}
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
new file mode 100644 (file)
index 0000000..c3f431b
--- /dev/null
@@ -0,0 +1,2145 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_nportdisc.c 1.146 2004/11/18 14:53:54EST sf_support Exp  $
+ */
+
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <scsi/scsi_device.h>
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+#include "lpfc_hw.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_mem.h"
+
+extern uint8_t lpfcAlpaArray[];
+
+
+/* Called to verify a rcv'ed ADISC was intended for us. */
+static int
+lpfc_check_adisc(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+                struct lpfc_name * nn, struct lpfc_name * pn)
+{
+       /* Compare the ADISC rsp WWNN / WWPN matches our internal node
+        * table entry for that node.
+        */
+       if (memcmp(nn, &ndlp->nlp_nodename, sizeof (struct lpfc_name)) != 0)
+               return (0);
+
+       if (memcmp(pn, &ndlp->nlp_portname, sizeof (struct lpfc_name)) != 0)
+               return (0);
+
+       /* we match, return success */
+       return (1);
+}
+
+
+int
+lpfc_check_sparm(struct lpfc_hba * phba,
+                struct lpfc_nodelist * ndlp, struct serv_parm * sp,
+                uint32_t class)
+{
+       volatile struct serv_parm *hsp = &phba->fc_sparam;
+       /* First check for supported version */
+
+       /* Next check for class validity */
+       if (sp->cls1.classValid) {
+
+               if (sp->cls1.rcvDataSizeMsb > hsp->cls1.rcvDataSizeMsb)
+                       sp->cls1.rcvDataSizeMsb = hsp->cls1.rcvDataSizeMsb;
+               if (sp->cls1.rcvDataSizeLsb > hsp->cls1.rcvDataSizeLsb)
+                       sp->cls1.rcvDataSizeLsb = hsp->cls1.rcvDataSizeLsb;
+       } else if (class == CLASS1) {
+               return (0);
+       }
+
+       if (sp->cls2.classValid) {
+
+               if (sp->cls2.rcvDataSizeMsb > hsp->cls2.rcvDataSizeMsb)
+                       sp->cls2.rcvDataSizeMsb = hsp->cls2.rcvDataSizeMsb;
+               if (sp->cls2.rcvDataSizeLsb > hsp->cls2.rcvDataSizeLsb)
+                       sp->cls2.rcvDataSizeLsb = hsp->cls2.rcvDataSizeLsb;
+       } else if (class == CLASS2) {
+               return (0);
+       }
+
+       if (sp->cls3.classValid) {
+
+               if (sp->cls3.rcvDataSizeMsb > hsp->cls3.rcvDataSizeMsb)
+                       sp->cls3.rcvDataSizeMsb = hsp->cls3.rcvDataSizeMsb;
+               if (sp->cls3.rcvDataSizeLsb > hsp->cls3.rcvDataSizeLsb)
+                       sp->cls3.rcvDataSizeLsb = hsp->cls3.rcvDataSizeLsb;
+       } else if (class == CLASS3) {
+               return (0);
+       }
+
+       if (sp->cmn.bbRcvSizeMsb > hsp->cmn.bbRcvSizeMsb)
+               sp->cmn.bbRcvSizeMsb = hsp->cmn.bbRcvSizeMsb;
+       if (sp->cmn.bbRcvSizeLsb > hsp->cmn.bbRcvSizeLsb)
+               sp->cmn.bbRcvSizeLsb = hsp->cmn.bbRcvSizeLsb;
+
+       /* If check is good, copy wwpn wwnn into ndlp */
+       memcpy(&ndlp->nlp_nodename, &sp->nodeName, sizeof (struct lpfc_name));
+       memcpy(&ndlp->nlp_portname, &sp->portName, sizeof (struct lpfc_name));
+       return (1);
+}
+
+static void *
+lpfc_check_elscmpl_iocb(struct lpfc_hba * phba,
+                     struct lpfc_iocbq *cmdiocb,
+                     struct lpfc_iocbq *rspiocb)
+{
+       struct lpfc_dmabuf *pcmd, *prsp;
+       uint32_t *lp;
+       void     *ptr;
+       IOCB_t   *irsp;
+
+       irsp = &rspiocb->iocb;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       /* For lpfc_els_abort, context2 could be zero'ed to delay
+        * freeing associated memory till after ABTS completes.
+        */
+       if (pcmd) {
+               prsp = (struct lpfc_dmabuf *) pcmd->list.next;
+               lp = (uint32_t *) prsp->virt;
+
+               pci_dma_sync_single_for_cpu(phba->pcidev, prsp->phys,
+                       LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+               ptr = (void *)((uint8_t *)lp + sizeof(uint32_t));
+       }
+       else {
+               /* Force ulpStatus error since we are returning NULL ptr */
+               if (!(irsp->ulpStatus)) {
+                       irsp->ulpStatus = IOSTAT_LOCAL_REJECT;
+                       irsp->un.ulpWord[4] = IOERR_SLI_ABORTED;
+               }
+               ptr = NULL;
+       }
+       return (ptr);
+}
+
+
+/*
+ * Free resources / clean up outstanding I/Os
+ * associated with a LPFC_NODELIST entry. This
+ * routine effectively results in a "software abort".
+ */
+static int
+lpfc_els_abort(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
+       int send_abts)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       IOCB_t *icmd;
+
+       /* Abort outstanding I/O on NPort <nlp_DID> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
+                       "%d:0201 Abort outstanding I/O on NPort x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, ndlp->nlp_DID, ndlp->nlp_flag,
+                       ndlp->nlp_state, ndlp->nlp_rpi);
+
+       psli = &phba->sli;
+       pring = &psli->ring[LPFC_ELS_RING];
+
+       /* First check the txq */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+               /* Check to see if iocb matches the nport we are looking for */
+               if ((lpfc_check_sli_ndlp(phba, pring, iocb, ndlp))) {
+                       /* It matches, so deque and call compl with an error */
+                       list_del(&iocb->list);
+                       pring->txq_cnt--;
+                       if (iocb->iocb_cmpl) {
+                               icmd = &iocb->iocb;
+                               icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                               icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                               (iocb->iocb_cmpl) (phba, iocb, iocb);
+                       } else {
+                               mempool_free(iocb, phba->iocb_mem_pool);
+                       }
+               }
+       }
+
+       /* Everything on txcmplq will be returned by firmware
+       * with a no rpi / linkdown / abort error.  For ring 0,
+       * ELS discovery, we want to get rid of it right here.
+       */
+       /* Next check the txcmplq */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+               /* Check to see if iocb matches the nport we are looking for */
+               if ((lpfc_check_sli_ndlp (phba, pring, iocb, ndlp))) {
+                       /* It matches, so deque and call compl with an error */
+                       list_del(&iocb->list);
+                       pring->txcmplq_cnt--;
+
+                       icmd = &iocb->iocb;
+                       /* If the driver is completing an ELS
+                        * command early, flush it out of the firmware.
+                        */
+                       if (send_abts &&
+                          (icmd->ulpCommand == CMD_ELS_REQUEST64_CR) &&
+                          (icmd->un.elsreq64.bdl.ulpIoTag32)) {
+                               lpfc_sli_issue_abort_iotag32(phba, pring, iocb);
+                       }
+                       if (iocb->iocb_cmpl) {
+                               icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                               icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                               (iocb->iocb_cmpl) (phba, iocb, iocb);
+                       } else {
+                               mempool_free(iocb, phba->iocb_mem_pool);
+                       }
+               }
+       }
+
+       /* If we are delaying issuing an ELS command, cancel it */
+       if(ndlp->nlp_flag & NLP_DELAY_TMO) {
+               ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+               del_timer_sync(&ndlp->nlp_delayfunc);
+       }
+       return (0);
+}
+
+static int
+lpfc_rcv_plogi(struct lpfc_hba * phba,
+                     struct lpfc_nodelist * ndlp,
+                     struct lpfc_iocbq *cmdiocb)
+{
+       struct lpfc_dmabuf *pcmd;
+       uint32_t *lp;
+       IOCB_t *icmd;
+       struct serv_parm *sp;
+       LPFC_MBOXQ_t *mbox;
+       struct ls_rjt stat;
+
+       memset(&stat, 0, sizeof (struct ls_rjt));
+       if (phba->hba_state <= LPFC_FLOGI) {
+               /* Before responding to PLOGI, check for pt2pt mode.
+                * If we are pt2pt, with an outstanding FLOGI, abort
+                * the FLOGI and resend it first.
+                */
+               if (phba->fc_flag & FC_PT2PT) {
+                       lpfc_els_abort_flogi(phba);
+                       if(!(phba->fc_flag & FC_PT2PT_PLOGI)) {
+                               /* If the other side is supposed to initiate
+                                * the PLOGI anyway, just ACC it now and
+                                * move on with discovery.
+                                */
+                               phba->fc_edtov = FF_DEF_EDTOV;
+                               phba->fc_ratov = FF_DEF_RATOV;
+                               /* Start discovery - this should just do
+                                  CLEAR_LA */
+                               lpfc_disc_start(phba);
+                       }
+                       else {
+                               lpfc_initial_flogi(phba);
+                       }
+               }
+               else {
+                       stat.un.b.lsRjtRsnCode = LSRJT_LOGICAL_BSY;
+                       stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
+                       goto out;
+               }
+       }
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       lp = (uint32_t *) pcmd->virt;
+       sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));
+       if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3) == 0)) {
+               /* Reject this request because invalid parameters */
+               stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+               stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;
+               goto out;
+       }
+       icmd = &cmdiocb->iocb;
+
+       /* PLOGI chkparm OK */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_ELS,
+                       "%d:0114 PLOGI chkparm OK Data: x%x x%x x%x x%x\n",
+                       phba->brd_no,
+                       ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag,
+                       ndlp->nlp_rpi);
+
+       if ((phba->cfg_fcp_class == 2) &&
+           (sp->cls2.classValid)) {
+               ndlp->nlp_fcp_info |= CLASS2;
+       } else {
+               ndlp->nlp_fcp_info |= CLASS3;
+       }
+
+       /* no need to reg_login if we are already in one of these states */
+       switch(ndlp->nlp_state) {
+       case  NLP_STE_REG_LOGIN_ISSUE:
+       case  NLP_STE_PRLI_ISSUE:
+       case  NLP_STE_UNMAPPED_NODE:
+       case  NLP_STE_MAPPED_NODE:
+               lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, NULL, 0);
+               return (1);
+       }
+
+       if ((mbox = mempool_alloc(phba->mbox_mem_pool, GFP_ATOMIC)) == 0) {
+               stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+               stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE;
+               goto out;
+       }
+
+       if ((phba->fc_flag & FC_PT2PT)
+           && !(phba->fc_flag & FC_PT2PT_PLOGI)) {
+               /* rcv'ed PLOGI decides what our NPortId will be */
+               phba->fc_myDID = icmd->un.rcvels.parmRo;
+               lpfc_config_link(phba, mbox);
+               if (lpfc_sli_issue_mbox
+                   (phba, mbox, (MBX_NOWAIT | MBX_STOP_IOCB))
+                   == MBX_NOT_FINISHED) {
+                       mempool_free( mbox, phba->mbox_mem_pool);
+                       stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+                       stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE;
+                       goto out;
+               }
+               if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+                       GFP_ATOMIC)) == 0) {
+                       stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+                       stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE;
+                       goto out;
+               }
+               lpfc_can_disctmo(phba);
+       }
+
+       if(lpfc_reg_login(phba, icmd->un.rcvels.remoteID,
+                          (uint8_t *) sp, mbox, 0)) {
+               mempool_free( mbox, phba->mbox_mem_pool);
+               stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+               stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE;
+out:
+               lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+               return (0);
+       }
+
+       /* ACC PLOGI rsp command needs to execute first,
+        * queue this mbox command to be processed later.
+        */
+       mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login;
+       mbox->context2  = ndlp;
+       ndlp->nlp_flag |= NLP_ACC_REGLOGIN;
+
+       /* If there is an outstanding PLOGI issued, abort it before
+        * sending ACC rsp to PLOGI recieved.
+        */
+       if(ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) {
+               /* software abort outstanding PLOGI */
+               lpfc_els_abort(phba, ndlp, 1);
+       }
+       ndlp->nlp_flag |= NLP_RCV_PLOGI;
+       lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox, 0);
+       return (1);
+}
+
+static int
+lpfc_rcv_padisc(struct lpfc_hba * phba,
+               struct lpfc_nodelist * ndlp,
+               struct lpfc_iocbq *cmdiocb)
+{
+       struct lpfc_dmabuf *pcmd;
+       struct serv_parm *sp;
+       struct lpfc_name *pnn, *ppn;
+       struct ls_rjt stat;
+       ADISC *ap;
+       IOCB_t *icmd;
+       uint32_t *lp;
+       uint32_t cmd;
+
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       lp = (uint32_t *) pcmd->virt;
+
+       cmd = *lp++;
+       if (cmd == ELS_CMD_ADISC) {
+               ap = (ADISC *) lp;
+               pnn = (struct lpfc_name *) & ap->nodeName;
+               ppn = (struct lpfc_name *) & ap->portName;
+       } else {
+               sp = (struct serv_parm *) lp;
+               pnn = (struct lpfc_name *) & sp->nodeName;
+               ppn = (struct lpfc_name *) & sp->portName;
+       }
+
+       icmd = &cmdiocb->iocb;
+       if ((icmd->ulpStatus == 0) &&
+           (lpfc_check_adisc(phba, ndlp, pnn, ppn))) {
+               if (cmd == ELS_CMD_ADISC) {
+                       lpfc_els_rsp_adisc_acc(phba, cmdiocb, ndlp);
+               }
+               else {
+                       lpfc_els_rsp_acc(phba, ELS_CMD_PLOGI, cmdiocb, ndlp,
+                               NULL, 0);
+               }
+               return (1);
+       }
+       /* Reject this request because invalid parameters */
+       stat.un.b.lsRjtRsvd0 = 0;
+       stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+       stat.un.b.lsRjtRsnCodeExp = LSEXP_SPARM_OPTIONS;
+       stat.un.b.vendorUnique = 0;
+       lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+
+       ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;
+       /* 1 sec timeout */
+       mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+
+       ndlp->nlp_flag |= NLP_DELAY_TMO;
+       ndlp->nlp_state = NLP_STE_NPR_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+       return (0);
+}
+
+static int
+lpfc_rcv_logo(struct lpfc_hba * phba,
+                     struct lpfc_nodelist * ndlp,
+                     struct lpfc_iocbq *cmdiocb)
+{
+       /* Put ndlp on NPR list with 1 sec timeout for plogi, ACC logo */
+       /* Only call LOGO ACC for first LOGO, this avoids sending unnecessary
+        * PLOGIs during LOGO storms from a device.
+        */
+       ndlp->nlp_flag |= NLP_LOGO_ACC;
+       lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+
+       if (!(ndlp->nlp_type & NLP_FABRIC)) {
+               /* Only try to re-login if this is NOT a Fabric Node */
+               ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;
+               mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
+               ndlp->nlp_flag |= NLP_DELAY_TMO;
+       }
+
+       ndlp->nlp_state = NLP_STE_NPR_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+
+       ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+       /* The driver has to wait until the ACC completes before it continues
+        * processing the LOGO.  The action will resume in
+        * lpfc_cmpl_els_logo_acc routine. Since part of processing includes an
+        * unreg_login, the driver waits so the ACC does not get aborted.
+        */
+       return (0);
+}
+
+static int
+lpfc_binding_found(struct lpfc_bindlist * blp, struct lpfc_nodelist * ndlp)
+{
+       uint16_t bindtype = blp->nlp_bind_type;
+
+       if ((bindtype & FCP_SEED_DID) &&
+                  (ndlp->nlp_DID == be32_to_cpu(blp->nlp_DID))) {
+               return (1);
+       } else if ((bindtype & FCP_SEED_WWPN) &&
+                  (memcmp(&ndlp->nlp_portname, &blp->nlp_portname,
+                          sizeof (struct lpfc_name)) == 0)) {
+               return (1);
+       } else if ((bindtype & FCP_SEED_WWNN) &&
+                  (memcmp(&ndlp->nlp_nodename, &blp->nlp_nodename,
+                           sizeof (struct lpfc_name)) == 0)) {
+               return (1);
+       }
+       return (0);
+}
+
+static int
+lpfc_binding_useid(struct lpfc_hba * phba, uint32_t sid)
+{
+       struct lpfc_bindlist *blp;
+
+       list_for_each_entry(blp, &phba->fc_nlpbind_list, nlp_listp) {
+               if (blp->nlp_sid == sid) {
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+static int
+lpfc_mapping_useid(struct lpfc_hba * phba, uint32_t sid)
+{
+       struct lpfc_nodelist *mapnode;
+       struct lpfc_bindlist *blp;
+
+       list_for_each_entry(mapnode, &phba->fc_nlpmap_list, nlp_listp) {
+               blp = mapnode->nlp_listp_bind;
+               if (blp->nlp_sid == sid) {
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+static struct lpfc_bindlist *
+lpfc_create_binding(struct lpfc_hba * phba,
+                   struct lpfc_nodelist * ndlp, uint16_t index,
+                   uint16_t bindtype)
+{
+       struct lpfc_bindlist *blp;
+
+       if ((blp = mempool_alloc(phba->bind_mem_pool, GFP_ATOMIC))) {
+               memset(blp, 0, sizeof (struct lpfc_bindlist));
+               switch (bindtype) {
+               case FCP_SEED_WWPN:
+                       blp->nlp_bind_type = FCP_SEED_WWPN;
+                       break;
+               case FCP_SEED_WWNN:
+                       blp->nlp_bind_type = FCP_SEED_WWNN;
+                       break;
+               case FCP_SEED_DID:
+                       blp->nlp_bind_type = FCP_SEED_DID;
+                       break;
+               }
+               blp->nlp_sid = index;
+               blp->nlp_DID = ndlp->nlp_DID;
+               memcpy(&blp->nlp_nodename, &ndlp->nlp_nodename,
+                      sizeof (struct lpfc_name));
+               memcpy(&blp->nlp_portname, &ndlp->nlp_portname,
+                      sizeof (struct lpfc_name));
+
+               return (blp);
+       }
+       return NULL;
+}
+
+
+static struct lpfc_bindlist *
+lpfc_consistent_bind_get(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
+{
+       struct lpfc_bindlist *blp, *next_blp;
+       uint16_t index;
+
+       /* check binding list */
+       list_for_each_entry_safe(blp, next_blp, &phba->fc_nlpbind_list,
+                               nlp_listp) {
+               if (lpfc_binding_found(blp, ndlp)) {
+
+                       /* take it off the binding list */
+                       phba->fc_bind_cnt--;
+                       list_del_init(&blp->nlp_listp);
+
+                       /* Reassign scsi id <sid> to NPort <nlp_DID> */
+                       lpfc_printf_log(phba,
+                                       KERN_INFO,
+                                       LOG_DISCOVERY | LOG_FCP,
+                                       "%d:0213 Reassign scsi id x%x to "
+                                       "NPort x%x Data: x%x x%x x%x x%x\n",
+                                       phba->brd_no,
+                                       blp->nlp_sid, ndlp->nlp_DID,
+                                       blp->nlp_bind_type, ndlp->nlp_flag,
+                                       ndlp->nlp_state, ndlp->nlp_rpi);
+
+                       return (blp);
+               }
+       }
+
+       /* NOTE: if scan-down = 2 and we have private loop, then we use
+        * AlpaArray to determine sid.
+        */
+       if ((phba->cfg_fcp_bind_method == 4) &&
+           ((phba->fc_flag & (FC_PUBLIC_LOOP | FC_FABRIC)) ||
+            (phba->fc_topology != TOPOLOGY_LOOP))) {
+               /* Log message: ALPA based binding used on a non loop
+                  topology */
+               lpfc_printf_log(phba,
+                               KERN_WARNING,
+                               LOG_DISCOVERY,
+                               "%d:0245 ALPA based bind method used on an HBA "
+                               "which is in a nonloop topology Data: x%x\n",
+                               phba->brd_no,
+                               phba->fc_topology);
+       }
+
+       if ((phba->cfg_fcp_bind_method == 4) &&
+           !(phba->fc_flag & (FC_PUBLIC_LOOP | FC_FABRIC)) &&
+           (phba->fc_topology == TOPOLOGY_LOOP)) {
+               for (index = 0; index < FC_MAXLOOP; index++) {
+                       if (ndlp->nlp_DID == (uint32_t) lpfcAlpaArray[index]) {
+                               if ((blp =
+                                    lpfc_create_binding(phba, ndlp, index,
+                                                        FCP_SEED_DID))) {
+                                       return (blp);
+                               }
+                               goto errid;
+                       }
+               }
+       }
+
+       if (phba->cfg_automap) {
+               while (1) {
+                       if ((lpfc_binding_useid(phba, phba->sid_cnt))
+                            || (lpfc_mapping_useid (phba, phba->sid_cnt))) {
+
+                               phba->sid_cnt++;
+                       } else {
+                               if ((blp =
+                                    lpfc_create_binding(phba, ndlp,
+                                                        phba->sid_cnt,
+                                                        phba->fcp_mapping))) {
+                                       blp->nlp_bind_type |= FCP_SEED_AUTO;
+
+                                       phba->sid_cnt++;
+                                       return (blp);
+                               }
+                               goto errid;
+                       }
+               }
+       }
+       /* if automap on */
+errid:
+       /* Cannot assign scsi id on NPort <nlp_DID> */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_DISCOVERY | LOG_FCP,
+                       "%d:0230 Cannot assign scsi ID on NPort x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no,
+                       ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
+                       ndlp->nlp_rpi);
+
+       return NULL;
+}
+
+static uint32_t
+lpfc_assign_binding(struct lpfc_hba * phba,
+               struct lpfc_nodelist * ndlp, struct lpfc_bindlist *blp)
+{
+       struct lpfc_target *targetp;
+
+       targetp = lpfc_find_target(phba, blp->nlp_sid, ndlp);
+       if(!targetp) {
+               /* Cannot assign scsi id <sid> to NPort <nlp_DID> */
+               lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_DISCOVERY | LOG_FCP,
+                       "%d:0229 Cannot assign scsi id x%x to NPort x%x "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, blp->nlp_sid,
+                       ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
+                       ndlp->nlp_rpi);
+               return(0);
+       }
+       ndlp->nlp_sid = blp->nlp_sid;
+       ndlp->nlp_flag &= ~NLP_SEED_MASK;
+       switch ((blp->nlp_bind_type & FCP_SEED_MASK)) {
+       case FCP_SEED_WWPN:
+               ndlp->nlp_flag |= NLP_SEED_WWPN;
+               break;
+       case FCP_SEED_WWNN:
+               ndlp->nlp_flag |= NLP_SEED_WWNN;
+               break;
+       case FCP_SEED_DID:
+               ndlp->nlp_flag |= NLP_SEED_DID;
+               break;
+       }
+       if (blp->nlp_bind_type & FCP_SEED_AUTO) {
+               ndlp->nlp_flag |= NLP_AUTOMAP;
+       }
+       /* Assign scsi id <sid> to NPort <nlp_DID> */
+       lpfc_printf_log(phba,
+               KERN_INFO,
+               LOG_DISCOVERY | LOG_FCP,
+               "%d:0216 Assign scsi "
+               "id x%x to NPort x%x "
+               "Data: x%x x%x x%x x%x\n",
+               phba->brd_no,
+               ndlp->nlp_sid, ndlp->nlp_DID,
+               blp->nlp_bind_type,
+               ndlp->nlp_flag, ndlp->nlp_state,
+               ndlp->nlp_rpi);
+       return(1);
+}
+
+static uint32_t
+lpfc_disc_set_adisc(struct lpfc_hba * phba,
+                     struct lpfc_nodelist * ndlp)
+{
+       /* Check config parameter use-adisc or FCP-2 */
+       if ((phba->cfg_use_adisc == 0) &&
+               !(phba->fc_flag & FC_RSCN_MODE)) {
+               return (0);
+       }
+       ndlp->nlp_flag |= NLP_NPR_ADISC;
+       return (1);
+}
+
+static uint32_t
+lpfc_disc_noop(struct lpfc_hba * phba,
+               struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       /* This routine does nothing, just return the current state */
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_disc_illegal(struct lpfc_hba * phba,
+                  struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       lpfc_printf_log(phba,
+                       KERN_ERR,
+                       LOG_DISCOVERY,
+                       "%d:0253 Illegal State Transition: node x%x event x%x, "
+                       "state x%x Data: x%x x%x\n",
+                       phba->brd_no,
+                       ndlp->nlp_DID, evt, ndlp->nlp_state, ndlp->nlp_rpi,
+                       ndlp->nlp_flag);
+       return (ndlp->nlp_state);
+}
+
+/* Start of Discovery State Machine routines */
+
+static uint32_t
+lpfc_rcv_plogi_unused_node(struct lpfc_hba * phba,
+                          struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) {
+               ndlp->nlp_state = NLP_STE_UNUSED_NODE;
+               lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST);
+               return (ndlp->nlp_state);
+       }
+       lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_rcv_els_unused_node(struct lpfc_hba * phba,
+                        struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       lpfc_issue_els_logo(phba, ndlp, 0);
+       lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_unused_node(struct lpfc_hba * phba,
+                         struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq     *cmdiocb;
+       struct lpfc_dmabuf    *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       ndlp->nlp_flag |= NLP_LOGO_ACC;
+       lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+       lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST);
+
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_logo_unused_node(struct lpfc_hba * phba,
+                         struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_rm_unused_node(struct lpfc_hba * phba,
+                          struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_rcv_plogi_plogi_issue(struct lpfc_hba * phba,
+                          struct lpfc_nodelist * ndlp,
+                          struct lpfc_iocbq *cmdiocb, uint32_t evt)
+{
+       struct lpfc_dmabuf *pcmd;
+       struct serv_parm *sp;
+       uint32_t *lp;
+       struct ls_rjt stat;
+       int port_cmp;
+
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       lp = (uint32_t *) pcmd->virt;
+       sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       memset(&stat, 0, sizeof (struct ls_rjt));
+
+       /* For a PLOGI, we only accept if our portname is less
+        * than the remote portname.
+        */
+       phba->fc_stat.elsLogiCol++;
+       port_cmp = memcmp(&phba->fc_portname, &sp->portName,
+                         sizeof (struct lpfc_name));
+
+       if (port_cmp >= 0) {
+               /* Reject this request because the remote node will accept
+                  ours */
+               stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+               stat.un.b.lsRjtRsnCodeExp = LSEXP_CMD_IN_PROGRESS;
+               lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+       }
+       else {
+               lpfc_rcv_plogi(phba, ndlp, cmdiocb);
+       } /* if our portname was less */
+
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_els_plogi_issue(struct lpfc_hba * phba,
+                         struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq     *cmdiocb;
+       struct lpfc_dmabuf    *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       /* software abort outstanding PLOGI */
+       lpfc_els_abort(phba, ndlp, 1);
+       mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
+       ndlp->nlp_flag |= NLP_DELAY_TMO;
+
+       if(evt == NLP_EVT_RCV_LOGO) {
+               lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+       }
+       else {
+               lpfc_issue_els_logo(phba, ndlp, 0);
+       }
+
+       /* Put ndlp in npr list set plogi timer for 1 sec */
+       ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;
+       ndlp->nlp_state = NLP_STE_NPR_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_plogi_plogi_issue(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb, *rspiocb;
+       struct lpfc_dmabuf *pcmd, *prsp;
+       uint32_t *lp;
+       IOCB_t *irsp;
+       struct serv_parm *sp;
+       LPFC_MBOXQ_t *mbox;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       rspiocb = cmdiocb->context_un.rsp_iocb;
+
+       if (ndlp->nlp_flag & NLP_ACC_REGLOGIN) {
+               return (ndlp->nlp_state);
+       }
+
+       irsp = &rspiocb->iocb;
+
+       if (irsp->ulpStatus == 0) {
+               pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+               prsp = (struct lpfc_dmabuf *) pcmd->list.next;
+               lp = (uint32_t *) prsp->virt;
+
+               pci_dma_sync_single_for_cpu(phba->pcidev, prsp->phys,
+                       LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+               sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t));
+               if ((lpfc_check_sparm(phba, ndlp, sp, CLASS3))) {
+                       /* PLOGI chkparm OK */
+                       lpfc_printf_log(phba,
+                                       KERN_INFO,
+                                       LOG_ELS,
+                                       "%d:0121 PLOGI chkparm OK "
+                                       "Data: x%x x%x x%x x%x\n",
+                                       phba->brd_no,
+                                       ndlp->nlp_DID, ndlp->nlp_state,
+                                       ndlp->nlp_flag, ndlp->nlp_rpi);
+
+                       if ((phba->cfg_fcp_class == 2) &&
+                           (sp->cls2.classValid)) {
+                               ndlp->nlp_fcp_info |= CLASS2;
+                       } else {
+                               ndlp->nlp_fcp_info |= CLASS3;
+                       }
+
+                       if ((mbox = mempool_alloc(phba->mbox_mem_pool,
+                                                 GFP_ATOMIC))) {
+                               lpfc_unreg_rpi(phba, ndlp);
+                               if (lpfc_reg_login
+                                   (phba, irsp->un.elsreq64.remoteID,
+                                    (uint8_t *) sp, mbox, 0) == 0) {
+                                       /* set_slim mailbox command needs to
+                                        * execute first, queue this command to
+                                        * be processed later.
+                                        */
+                                       switch(ndlp->nlp_DID) {
+                                       case NameServer_DID:
+                                               mbox->mbox_cmpl =
+                                                   lpfc_mbx_cmpl_ns_reg_login;
+                                               break;
+                                       case FDMI_DID:
+                                               mbox->mbox_cmpl =
+                                                   lpfc_mbx_cmpl_fdmi_reg_login;
+                                               break;
+                                       default:
+                                               mbox->mbox_cmpl =
+                                                   lpfc_mbx_cmpl_reg_login;
+                                       }
+                                       mbox->context2 = ndlp;
+                                       if (lpfc_sli_issue_mbox(phba, mbox,
+                                            (MBX_NOWAIT | MBX_STOP_IOCB))
+                                           != MBX_NOT_FINISHED) {
+                                               ndlp->nlp_state =
+                                               NLP_STE_REG_LOGIN_ISSUE;
+                                               lpfc_nlp_list(phba, ndlp,
+                                                     NLP_REGLOGIN_LIST);
+                                               return (ndlp->nlp_state);
+                                       }
+                                       mempool_free(mbox, phba->mbox_mem_pool);
+                               } else {
+                                       mempool_free(mbox, phba->mbox_mem_pool);
+                               }
+                       }
+               }
+       }
+
+       /* Free this node since the driver cannot login or has the wrong
+          sparm */
+       lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_rm_plogi_issue(struct lpfc_hba * phba,
+                          struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       /* software abort outstanding PLOGI */
+       lpfc_els_abort(phba, ndlp, 1);
+
+       lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_recov_plogi_issue(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       /* software abort outstanding PLOGI */
+       lpfc_els_abort(phba, ndlp, 1);
+
+       ndlp->nlp_state = NLP_STE_NPR_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+       ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_adisc_issue(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       /* software abort outstanding ADISC */
+       lpfc_els_abort(phba, ndlp, 1);
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) {
+               return (ndlp->nlp_state);
+       }
+       ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+       lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+       lpfc_issue_els_plogi(phba, ndlp, 0);
+
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_adisc_issue(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_adisc_issue(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       /* software abort outstanding ADISC */
+       lpfc_els_abort(phba, ndlp, 0);
+
+       lpfc_rcv_logo(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_adisc_issue(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prlo_adisc_issue(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       /* Treat like rcv logo */
+       lpfc_rcv_logo(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_adisc_adisc_issue(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb, *rspiocb;
+       struct lpfc_bindlist *blp;
+       IOCB_t *irsp;
+       ADISC *ap;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       rspiocb = cmdiocb->context_un.rsp_iocb;
+
+       ap = (ADISC *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb);
+       irsp = &rspiocb->iocb;
+
+       if ((irsp->ulpStatus) ||
+               (!lpfc_check_adisc(phba, ndlp, &ap->nodeName, &ap->portName))) {
+               ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;
+               /* 1 sec timeout */
+               mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+               ndlp->nlp_flag |= NLP_DELAY_TMO;
+
+               memset(&ndlp->nlp_nodename, 0, sizeof (struct lpfc_name));
+               memset(&ndlp->nlp_portname, 0, sizeof (struct lpfc_name));
+
+               ndlp->nlp_state = NLP_STE_NPR_NODE;
+               lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+               lpfc_unreg_rpi(phba, ndlp);
+               return (ndlp->nlp_state);
+       }
+       /* move to mapped / unmapped list accordingly */
+       /* Can we assign a SCSI Id to this NPort */
+       if ((blp = lpfc_consistent_bind_get(phba, ndlp))) {
+               /* Next 4 lines MUST be in this order */
+               if(lpfc_assign_binding(phba, ndlp, blp)) {
+                       ndlp->nlp_state = NLP_STE_MAPPED_NODE;
+                       lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST);
+                       ndlp->nlp_listp_bind = blp;
+
+                       lpfc_set_failmask(phba, ndlp,
+                               (LPFC_DEV_DISCOVERY_INP|LPFC_DEV_DISCONNECTED),
+                               LPFC_CLR_BITMASK);
+
+                       return (ndlp->nlp_state);
+               }
+       }
+       ndlp->nlp_flag |= NLP_TGT_NO_SCSIID;
+       ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+
+       lpfc_set_failmask(phba, ndlp,
+               (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED),
+               LPFC_CLR_BITMASK);
+
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_device_rm_adisc_issue(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       /* software abort outstanding ADISC */
+       lpfc_els_abort(phba, ndlp, 1);
+
+       lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_recov_adisc_issue(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       /* software abort outstanding ADISC */
+       lpfc_els_abort(phba, ndlp, 1);
+
+       ndlp->nlp_state = NLP_STE_NPR_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+       ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+
+       lpfc_disc_set_adisc(phba, ndlp);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_reglogin_issue(struct lpfc_hba * phba,
+                             struct lpfc_nodelist * ndlp, void *arg,
+                             uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_plogi(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_reglogin_issue(struct lpfc_hba * phba,
+                            struct lpfc_nodelist * ndlp, void *arg,
+                            uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_reglogin_issue(struct lpfc_hba * phba,
+                            struct lpfc_nodelist * ndlp, void *arg,
+                            uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_logo(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_reglogin_issue(struct lpfc_hba * phba,
+                              struct lpfc_nodelist * ndlp, void *arg,
+                              uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prlo_reglogin_issue(struct lpfc_hba * phba,
+                            struct lpfc_nodelist * ndlp, void *arg,
+                            uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_hba * phba,
+                                 struct lpfc_nodelist * ndlp,
+                                 void *arg, uint32_t evt)
+{
+       LPFC_MBOXQ_t *pmb;
+       MAILBOX_t *mb;
+       uint32_t did;
+
+       pmb = (LPFC_MBOXQ_t *) arg;
+       mb = &pmb->mb;
+       did = mb->un.varWords[1];
+       if (mb->mbxStatus) {
+               /* RegLogin failed */
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_DISCOVERY,
+                               "%d:0246 RegLogin failed Data: x%x x%x x%x\n",
+                               phba->brd_no,
+                               did, mb->mbxStatus, phba->hba_state);
+
+               mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
+               ndlp->nlp_flag |= NLP_DELAY_TMO;
+
+               lpfc_issue_els_logo(phba, ndlp, 0);
+               /* Put ndlp in npr list set plogi timer for 1 sec */
+               ndlp->nlp_last_elscmd = (unsigned long)ELS_CMD_PLOGI;
+               ndlp->nlp_state = NLP_STE_NPR_NODE;
+               lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+               return (ndlp->nlp_state);
+       }
+
+       if (ndlp->nlp_rpi != 0)
+               lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+
+       ndlp->nlp_rpi = mb->un.varWords[0];
+       lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi);
+
+       /* Only if we are not a fabric nport do we issue PRLI */
+       if (!(ndlp->nlp_type & NLP_FABRIC)) {
+               ndlp->nlp_state = NLP_STE_PRLI_ISSUE;
+               lpfc_nlp_list(phba, ndlp, NLP_PRLI_LIST);
+               lpfc_issue_els_prli(phba, ndlp, 0);
+       } else {
+               ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+               lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+       }
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_device_rm_reglogin_issue(struct lpfc_hba * phba,
+                             struct lpfc_nodelist * ndlp, void *arg,
+                             uint32_t evt)
+{
+       lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_recov_reglogin_issue(struct lpfc_hba * phba,
+                              struct lpfc_nodelist * ndlp, void *arg,
+                              uint32_t evt)
+{
+       ndlp->nlp_state = NLP_STE_NPR_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+       ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_prli_issue(struct lpfc_hba * phba,
+                         struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_plogi(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_prli_issue(struct lpfc_hba * phba,
+                        struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_prli_issue(struct lpfc_hba * phba,
+                        struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       /* Software abort outstanding PRLI before sending acc */
+       lpfc_els_abort(phba, ndlp, 1);
+
+       lpfc_rcv_logo(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_prli_issue(struct lpfc_hba * phba,
+                          struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+/* This routine is envoked when we rcv a PRLO request from a nport
+ * we are logged into.  We should send back a PRLO rsp setting the
+ * appropriate bits.
+ * NEXT STATE = PRLI_ISSUE
+ */
+static uint32_t
+lpfc_rcv_prlo_prli_issue(struct lpfc_hba * phba,
+                        struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_prli_prli_issue(struct lpfc_hba * phba,
+                         struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb, *rspiocb;
+       IOCB_t *irsp;
+       PRLI *npr;
+       struct lpfc_bindlist *blp;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       rspiocb = cmdiocb->context_un.rsp_iocb;
+       npr = (PRLI *)lpfc_check_elscmpl_iocb(phba, cmdiocb, rspiocb);
+
+       irsp = &rspiocb->iocb;
+       if (irsp->ulpStatus) {
+               ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+               lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+               lpfc_set_failmask(phba, ndlp, LPFC_DEV_DISCOVERY_INP,
+                                 LPFC_CLR_BITMASK);
+               return (ndlp->nlp_state);
+       }
+
+       /* Check out PRLI rsp */
+       if ((npr->acceptRspCode != PRLI_REQ_EXECUTED) ||
+           (npr->prliType != PRLI_FCP_TYPE) || (npr->targetFunc != 1)) {
+               ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+               lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+               lpfc_set_failmask(phba, ndlp,
+                       (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED),
+                       LPFC_CLR_BITMASK);
+               return (ndlp->nlp_state);
+       }
+       if (npr->Retry == 1) {
+               ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE;
+       }
+
+       /* Can we assign a SCSI Id to this NPort */
+       if ((blp = lpfc_consistent_bind_get(phba, ndlp))) {
+               /* Next 4 lines MUST be in this order */
+               if(lpfc_assign_binding(phba, ndlp, blp)) {
+                       ndlp->nlp_state = NLP_STE_MAPPED_NODE;
+                       lpfc_nlp_list(phba, ndlp, NLP_MAPPED_LIST);
+                       ndlp->nlp_listp_bind = blp;
+
+                       lpfc_set_failmask(phba, ndlp,
+                               (LPFC_DEV_DISCOVERY_INP|LPFC_DEV_DISCONNECTED),
+                               LPFC_CLR_BITMASK);
+                       return (ndlp->nlp_state);
+               }
+       }
+       ndlp->nlp_flag |= NLP_TGT_NO_SCSIID;
+       ndlp->nlp_state = NLP_STE_UNMAPPED_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_UNMAPPED_LIST);
+
+       lpfc_set_failmask(phba, ndlp,
+               (LPFC_DEV_DISCOVERY_INP | LPFC_DEV_DISCONNECTED),
+               LPFC_CLR_BITMASK);
+       return (ndlp->nlp_state);
+}
+
+/*! lpfc_device_rm_prli_issue
+  *
+  * \pre
+  * \post
+  * \param   phba
+  * \param   ndlp
+  * \param   arg
+  * \param   evt
+  * \return  uint32_t
+  *
+  * \b Description:
+  *    This routine is envoked when we a request to remove a nport we are in the
+  *    process of PRLIing. We should software abort outstanding prli, unreg
+  *    login, send a logout. We will change node state to UNUSED_NODE, put it
+  *    on plogi list so it can be freed when LOGO completes.
+  *
+  */
+static uint32_t
+lpfc_device_rm_prli_issue(struct lpfc_hba * phba,
+                         struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       /* software abort outstanding PRLI */
+       lpfc_els_abort(phba, ndlp, 1);
+
+       lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       return (NLP_STE_FREED_NODE);
+}
+
+
+/*! lpfc_device_recov_prli_issue
+  *
+  * \pre
+  * \post
+  * \param   phba
+  * \param   ndlp
+  * \param   arg
+  * \param   evt
+  * \return  uint32_t
+  *
+  * \b Description:
+  *    The routine is envoked when the state of a device is unknown, like
+  *    during a link down. We should remove the nodelist entry from the
+  *    unmapped list, issue a UNREG_LOGIN, do a software abort of the
+  *    outstanding PRLI command, then free the node entry.
+  */
+static uint32_t
+lpfc_device_recov_prli_issue(struct lpfc_hba * phba,
+                          struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       /* software abort outstanding PRLI */
+       lpfc_els_abort(phba, ndlp, 1);
+
+       ndlp->nlp_state = NLP_STE_NPR_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+       ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_unmap_node(struct lpfc_hba * phba,
+                         struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_plogi(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_unmap_node(struct lpfc_hba * phba,
+                        struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_unmap_node(struct lpfc_hba * phba,
+                        struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_logo(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_unmap_node(struct lpfc_hba * phba,
+                          struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prlo_unmap_node(struct lpfc_hba * phba,
+                        struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       /* Treat like rcv logo */
+       lpfc_rcv_logo(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_device_recov_unmap_node(struct lpfc_hba * phba,
+                          struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       ndlp->nlp_state = NLP_STE_NPR_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+       ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+       lpfc_disc_set_adisc(phba, ndlp);
+
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_mapped_node(struct lpfc_hba * phba,
+                          struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_plogi(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_mapped_node(struct lpfc_hba * phba,
+                         struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_els_rsp_prli_acc(phba, cmdiocb, ndlp);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_mapped_node(struct lpfc_hba * phba,
+                         struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_logo(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_mapped_node(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prlo_mapped_node(struct lpfc_hba * phba,
+                         struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       /* flush the target */
+       lpfc_sli_abort_iocb_tgt(phba,
+                              &phba->sli.ring[phba->sli.fcp_ring],
+                              ndlp->nlp_sid, LPFC_ABORT_ALLQ);
+
+       /* Treat like rcv logo */
+       lpfc_rcv_logo(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_device_recov_mapped_node(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       ndlp->nlp_state = NLP_STE_NPR_NODE;
+       lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST);
+       ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+       lpfc_disc_set_adisc(phba, ndlp);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_plogi_npr_node(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq *cmdiocb;
+       struct lpfc_dmabuf *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       /* Ignore PLOGI if we have an outstanding LOGO */
+       if (ndlp->nlp_flag & NLP_LOGO_SND) {
+               return (ndlp->nlp_state);
+       }
+
+       if(lpfc_rcv_plogi(phba, ndlp, cmdiocb)) {
+               ndlp->nlp_flag &= ~(NLP_NPR_ADISC | NLP_NPR_2B_DISC);
+               return (ndlp->nlp_state);
+       }
+
+       /* send PLOGI immediately, move to PLOGI issue state */
+       if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
+                       ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+                       lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+                       lpfc_issue_els_plogi(phba, ndlp, 0);
+       }
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prli_npr_node(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq     *cmdiocb;
+       struct lpfc_dmabuf    *pcmd;
+       struct ls_rjt          stat;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       memset(&stat, 0, sizeof (struct ls_rjt));
+       stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+       stat.un.b.lsRjtRsnCodeExp = LSEXP_NOTHING_MORE;
+       lpfc_els_rsp_reject(phba, stat.un.lsRjtError, cmdiocb, ndlp);
+
+       if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
+               if (ndlp->nlp_flag & NLP_NPR_ADISC) {
+                       ndlp->nlp_state = NLP_STE_ADISC_ISSUE;
+                       lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST);
+                       lpfc_issue_els_adisc(phba, ndlp, 0);
+               } else {
+                       ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+                       lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+                       lpfc_issue_els_plogi(phba, ndlp, 0);
+               }
+       }
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_logo_npr_node(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq     *cmdiocb;
+       struct lpfc_dmabuf         *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_logo(phba, ndlp, cmdiocb);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_padisc_npr_node(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq     *cmdiocb;
+       struct lpfc_dmabuf    *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_rcv_padisc(phba, ndlp, cmdiocb);
+
+       if(!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
+               if (ndlp->nlp_flag & NLP_NPR_ADISC) {
+                       ndlp->nlp_state = NLP_STE_ADISC_ISSUE;
+                       lpfc_nlp_list(phba, ndlp, NLP_ADISC_LIST);
+                       lpfc_issue_els_adisc(phba, ndlp, 0);
+               } else {
+                       ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+                       lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+                       lpfc_issue_els_plogi(phba, ndlp, 0);
+               }
+       }
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_rcv_prlo_npr_node(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       struct lpfc_iocbq     *cmdiocb;
+       struct lpfc_dmabuf         *pcmd;
+
+       cmdiocb = (struct lpfc_iocbq *) arg;
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+       pci_dma_sync_single_for_cpu(phba->pcidev, pcmd->phys,
+               LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+
+       lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0);
+
+       if(ndlp->nlp_flag & NLP_DELAY_TMO) {
+               if (ndlp->nlp_last_elscmd == (unsigned long)ELS_CMD_PLOGI) {
+                       return (ndlp->nlp_state);
+               } else {
+                       del_timer_sync(&ndlp->nlp_delayfunc);
+                       ndlp->nlp_flag &= ~NLP_DELAY_TMO;
+               }
+       }
+
+       ndlp->nlp_state = NLP_STE_PLOGI_ISSUE;
+       lpfc_nlp_list(phba, ndlp, NLP_PLOGI_LIST);
+       lpfc_issue_els_plogi(phba, ndlp, 0);
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_logo_npr_node(struct lpfc_hba * phba,
+               struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       lpfc_unreg_rpi(phba, ndlp);
+       /* This routine does nothing, just return the current state */
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_cmpl_reglogin_npr_node(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       LPFC_MBOXQ_t *pmb;
+       MAILBOX_t *mb;
+
+       pmb = (LPFC_MBOXQ_t *) arg;
+       mb = &pmb->mb;
+
+       /* save rpi */
+       if (ndlp->nlp_rpi != 0)
+               lpfc_findnode_remove_rpi(phba, ndlp->nlp_rpi);
+
+       ndlp->nlp_rpi = mb->un.varWords[0];
+       lpfc_addnode_rpi(phba, ndlp, ndlp->nlp_rpi);
+
+       return (ndlp->nlp_state);
+}
+
+static uint32_t
+lpfc_device_rm_npr_node(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       lpfc_nlp_list(phba, ndlp, NLP_NO_LIST);
+       return (NLP_STE_FREED_NODE);
+}
+
+static uint32_t
+lpfc_device_recov_npr_node(struct lpfc_hba * phba,
+                           struct lpfc_nodelist * ndlp, void *arg,
+                           uint32_t evt)
+{
+       ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+       return (ndlp->nlp_state);
+}
+
+
+/* This next section defines the NPort Discovery State Machine */
+
+/* There are 4 different double linked lists nodelist entries can reside on.
+ * The plogi list and adisc list are used when Link Up discovery or RSCN
+ * processing is needed. Each list holds the nodes that we will send PLOGI
+ * or ADISC on. These lists will keep track of what nodes will be effected
+ * by an RSCN, or a Link Up (Typically, all nodes are effected on Link Up).
+ * The unmapped_list will contain all nodes that we have successfully logged
+ * into at the Fibre Channel level. The mapped_list will contain all nodes
+ * that are mapped FCP targets.
+ */
+/*
+ * The bind list is a list of undiscovered (potentially non-existent) nodes
+ * that we have saved binding information on. This information is used when
+ * nodes transition from the unmapped to the mapped list.
+ */
+/* For UNUSED_NODE state, the node has just been allocated .
+ * For PLOGI_ISSUE and REG_LOGIN_ISSUE, the node is on
+ * the PLOGI list. For REG_LOGIN_COMPL, the node is taken off the PLOGI list
+ * and put on the unmapped list. For ADISC processing, the node is taken off
+ * the ADISC list and placed on either the mapped or unmapped list (depending
+ * on its previous state). Once on the unmapped list, a PRLI is issued and the
+ * state changed to PRLI_ISSUE. When the PRLI completion occurs, the state is
+ * changed to UNMAPPED_NODE. If the completion indicates a mapped
+ * node, the node is taken off the unmapped list. The binding list is checked
+ * for a valid binding, or a binding is automatically assigned. If binding
+ * assignment is unsuccessful, the node is left on the unmapped list. If
+ * binding assignment is successful, the associated binding list entry (if
+ * any) is removed, and the node is placed on the mapped list.
+ */
+/*
+ * For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped
+ * lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers
+ * expire, all effected nodes will receive a DEVICE_RM event.
+ */
+/*
+ * For a Link Up or RSCN, all nodes will move from the mapped / unmapped lists
+ * to either the ADISC or PLOGI list.  After a Nameserver query or ALPA loopmap
+ * check, additional nodes may be added or removed (via DEVICE_RM) to / from
+ * the PLOGI or ADISC lists. Once the PLOGI and ADISC lists are populated,
+ * we will first process the ADISC list.  32 entries are processed initially and
+ * ADISC is initited for each one.  Completions / Events for each node are
+ * funnelled thru the state machine.  As each node finishes ADISC processing, it
+ * starts ADISC for any nodes waiting for ADISC processing. If no nodes are
+ * waiting, and the ADISC list count is identically 0, then we are done. For
+ * Link Up discovery, since all nodes on the PLOGI list are UNREG_LOGIN'ed, we
+ * can issue a CLEAR_LA and reenable Link Events. Next we will process the PLOGI
+ * list.  32 entries are processed initially and PLOGI is initited for each one.
+ * Completions / Events for each node are funnelled thru the state machine.  As
+ * each node finishes PLOGI processing, it starts PLOGI for any nodes waiting
+ * for PLOGI processing. If no nodes are waiting, and the PLOGI list count is
+ * indentically 0, then we are done. We have now completed discovery / RSCN
+ * handling. Upon completion, ALL nodes should be on either the mapped or
+ * unmapped lists.
+ */
+
+static void *lpfc_disc_action[NLP_STE_MAX_STATE * NLP_EVT_MAX_EVENT] = {
+       /* Action routine                          Event       Current State  */
+       (void *)lpfc_rcv_plogi_unused_node,     /* RCV_PLOGI   UNUSED_NODE    */
+       (void *)lpfc_rcv_els_unused_node,       /* RCV_PRLI        */
+       (void *)lpfc_rcv_logo_unused_node,      /* RCV_LOGO        */
+       (void *)lpfc_rcv_els_unused_node,       /* RCV_ADISC       */
+       (void *)lpfc_rcv_els_unused_node,       /* RCV_PDISC       */
+       (void *)lpfc_rcv_els_unused_node,       /* RCV_PRLO        */
+       (void *)lpfc_disc_illegal,              /* CMPL_PLOGI      */
+       (void *)lpfc_disc_illegal,              /* CMPL_PRLI       */
+       (void *)lpfc_cmpl_logo_unused_node,     /* CMPL_LOGO       */
+       (void *)lpfc_disc_illegal,              /* CMPL_ADISC      */
+       (void *)lpfc_disc_illegal,              /* CMPL_REG_LOGIN  */
+       (void *)lpfc_device_rm_unused_node,     /* DEVICE_RM       */
+       (void *)lpfc_disc_illegal,              /* DEVICE_RECOVERY */
+
+       (void *)lpfc_rcv_plogi_plogi_issue,     /* RCV_PLOGI   PLOGI_ISSUE    */
+       (void *)lpfc_rcv_els_plogi_issue,       /* RCV_PRLI        */
+       (void *)lpfc_rcv_els_plogi_issue,       /* RCV_LOGO        */
+       (void *)lpfc_rcv_els_plogi_issue,       /* RCV_ADISC       */
+       (void *)lpfc_rcv_els_plogi_issue,       /* RCV_PDISC       */
+       (void *)lpfc_rcv_els_plogi_issue,       /* RCV_PRLO        */
+       (void *)lpfc_cmpl_plogi_plogi_issue,    /* CMPL_PLOGI      */
+       (void *)lpfc_disc_illegal,              /* CMPL_PRLI       */
+       (void *)lpfc_disc_illegal,              /* CMPL_LOGO       */
+       (void *)lpfc_disc_illegal,              /* CMPL_ADISC      */
+       (void *)lpfc_disc_illegal,              /* CMPL_REG_LOGIN  */
+       (void *)lpfc_device_rm_plogi_issue,     /* DEVICE_RM       */
+       (void *)lpfc_device_recov_plogi_issue,  /* DEVICE_RECOVERY */
+
+       (void *)lpfc_rcv_plogi_adisc_issue,     /* RCV_PLOGI   ADISC_ISSUE    */
+       (void *)lpfc_rcv_prli_adisc_issue,      /* RCV_PRLI        */
+       (void *)lpfc_rcv_logo_adisc_issue,      /* RCV_LOGO        */
+       (void *)lpfc_rcv_padisc_adisc_issue,    /* RCV_ADISC       */
+       (void *)lpfc_rcv_padisc_adisc_issue,    /* RCV_PDISC       */
+       (void *)lpfc_rcv_prlo_adisc_issue,      /* RCV_PRLO        */
+       (void *)lpfc_disc_illegal,              /* CMPL_PLOGI      */
+       (void *)lpfc_disc_illegal,              /* CMPL_PRLI       */
+       (void *)lpfc_disc_illegal,              /* CMPL_LOGO       */
+       (void *)lpfc_cmpl_adisc_adisc_issue,    /* CMPL_ADISC      */
+       (void *)lpfc_disc_illegal,              /* CMPL_REG_LOGIN  */
+       (void *)lpfc_device_rm_adisc_issue,     /* DEVICE_RM       */
+       (void *)lpfc_device_recov_adisc_issue,  /* DEVICE_RECOVERY */
+
+       (void *)lpfc_rcv_plogi_reglogin_issue,  /* RCV_PLOGI  REG_LOGIN_ISSUE */
+       (void *)lpfc_rcv_prli_reglogin_issue,   /* RCV_PLOGI       */
+       (void *)lpfc_rcv_logo_reglogin_issue,   /* RCV_LOGO        */
+       (void *)lpfc_rcv_padisc_reglogin_issue, /* RCV_ADISC       */
+       (void *)lpfc_rcv_padisc_reglogin_issue, /* RCV_PDISC       */
+       (void *)lpfc_rcv_prlo_reglogin_issue,   /* RCV_PRLO        */
+       (void *)lpfc_disc_illegal,              /* CMPL_PLOGI      */
+       (void *)lpfc_disc_illegal,              /* CMPL_PRLI       */
+       (void *)lpfc_disc_illegal,              /* CMPL_LOGO       */
+       (void *)lpfc_disc_illegal,              /* CMPL_ADISC      */
+       (void *)lpfc_cmpl_reglogin_reglogin_issue,/* CMPL_REG_LOGIN  */
+       (void *)lpfc_device_rm_reglogin_issue,  /* DEVICE_RM       */
+       (void *)lpfc_device_recov_reglogin_issue,/* DEVICE_RECOVERY */
+
+       (void *)lpfc_rcv_plogi_prli_issue,      /* RCV_PLOGI   PRLI_ISSUE     */
+       (void *)lpfc_rcv_prli_prli_issue,       /* RCV_PRLI        */
+       (void *)lpfc_rcv_logo_prli_issue,       /* RCV_LOGO        */
+       (void *)lpfc_rcv_padisc_prli_issue,     /* RCV_ADISC       */
+       (void *)lpfc_rcv_padisc_prli_issue,     /* RCV_PDISC       */
+       (void *)lpfc_rcv_prlo_prli_issue,       /* RCV_PRLO        */
+       (void *)lpfc_disc_illegal,              /* CMPL_PLOGI      */
+       (void *)lpfc_cmpl_prli_prli_issue,      /* CMPL_PRLI       */
+       (void *)lpfc_disc_illegal,              /* CMPL_LOGO       */
+       (void *)lpfc_disc_illegal,              /* CMPL_ADISC      */
+       (void *)lpfc_disc_illegal,              /* CMPL_REG_LOGIN  */
+       (void *)lpfc_device_rm_prli_issue,      /* DEVICE_RM       */
+       (void *)lpfc_device_recov_prli_issue,   /* DEVICE_RECOVERY */
+
+       (void *)lpfc_rcv_plogi_unmap_node,      /* RCV_PLOGI   UNMAPPED_NODE  */
+       (void *)lpfc_rcv_prli_unmap_node,       /* RCV_PRLI        */
+       (void *)lpfc_rcv_logo_unmap_node,       /* RCV_LOGO        */
+       (void *)lpfc_rcv_padisc_unmap_node,     /* RCV_ADISC       */
+       (void *)lpfc_rcv_padisc_unmap_node,     /* RCV_PDISC       */
+       (void *)lpfc_rcv_prlo_unmap_node,       /* RCV_PRLO        */
+       (void *)lpfc_disc_illegal,              /* CMPL_PLOGI      */
+       (void *)lpfc_disc_illegal,              /* CMPL_PRLI       */
+       (void *)lpfc_disc_illegal,              /* CMPL_LOGO       */
+       (void *)lpfc_disc_illegal,              /* CMPL_ADISC      */
+       (void *)lpfc_disc_illegal,              /* CMPL_REG_LOGIN  */
+       (void *)lpfc_disc_illegal,              /* DEVICE_RM       */
+       (void *)lpfc_device_recov_unmap_node,   /* DEVICE_RECOVERY */
+
+       (void *)lpfc_rcv_plogi_mapped_node,     /* RCV_PLOGI   MAPPED_NODE    */
+       (void *)lpfc_rcv_prli_mapped_node,      /* RCV_PRLI        */
+       (void *)lpfc_rcv_logo_mapped_node,      /* RCV_LOGO        */
+       (void *)lpfc_rcv_padisc_mapped_node,    /* RCV_ADISC       */
+       (void *)lpfc_rcv_padisc_mapped_node,    /* RCV_PDISC       */
+       (void *)lpfc_rcv_prlo_mapped_node,      /* RCV_PRLO        */
+       (void *)lpfc_disc_illegal,              /* CMPL_PLOGI      */
+       (void *)lpfc_disc_illegal,              /* CMPL_PRLI       */
+       (void *)lpfc_disc_illegal,              /* CMPL_LOGO       */
+       (void *)lpfc_disc_illegal,              /* CMPL_ADISC      */
+       (void *)lpfc_disc_illegal,              /* CMPL_REG_LOGIN  */
+       (void *)lpfc_disc_illegal,              /* DEVICE_RM       */
+       (void *)lpfc_device_recov_mapped_node,  /* DEVICE_RECOVERY */
+
+       (void *)lpfc_rcv_plogi_npr_node,        /* RCV_PLOGI   NPR_NODE    */
+       (void *)lpfc_rcv_prli_npr_node,         /* RCV_PRLI        */
+       (void *)lpfc_rcv_logo_npr_node,         /* RCV_LOGO        */
+       (void *)lpfc_rcv_padisc_npr_node,       /* RCV_ADISC       */
+       (void *)lpfc_rcv_padisc_npr_node,       /* RCV_PDISC       */
+       (void *)lpfc_rcv_prlo_npr_node,         /* RCV_PRLO        */
+       (void *)lpfc_disc_noop,                 /* CMPL_PLOGI      */
+       (void *)lpfc_disc_noop,                 /* CMPL_PRLI       */
+       (void *)lpfc_cmpl_logo_npr_node,        /* CMPL_LOGO       */
+       (void *)lpfc_disc_noop,                 /* CMPL_ADISC      */
+       (void *)lpfc_cmpl_reglogin_npr_node,    /* CMPL_REG_LOGIN  */
+       (void *)lpfc_device_rm_npr_node,        /* DEVICE_RM       */
+       (void *)lpfc_device_recov_npr_node,     /* DEVICE_RECOVERY */
+};
+
+int
+lpfc_disc_state_machine(struct lpfc_hba * phba,
+                       struct lpfc_nodelist * ndlp, void *arg, uint32_t evt)
+{
+       uint32_t cur_state, rc;
+       uint32_t(*func) (struct lpfc_hba *, struct lpfc_nodelist *, void *,
+                        uint32_t);
+
+       ndlp->nlp_disc_refcnt++;
+       cur_state = ndlp->nlp_state;
+
+       /* DSM in event <evt> on NPort <nlp_DID> in state <cur_state> */
+       lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_DISCOVERY,
+                       "%d:0211 DSM in event x%x on NPort x%x in state %d "
+                       "Data: x%x\n",
+                       phba->brd_no,
+                       evt, ndlp->nlp_DID, cur_state, ndlp->nlp_flag);
+
+       func = (uint32_t(*)(struct lpfc_hba *, struct lpfc_nodelist *, void *,
+                           uint32_t))
+           lpfc_disc_action[(cur_state * NLP_EVT_MAX_EVENT) + evt];
+       rc = (func) (phba, ndlp, arg, evt);
+
+       /* DSM out state <rc> on NPort <nlp_DID> */
+       lpfc_printf_log(phba,
+                      KERN_INFO,
+                      LOG_DISCOVERY,
+                      "%d:0212 DSM out state %d on NPort x%x Data: x%x\n",
+                      phba->brd_no,
+                      rc, ndlp->nlp_DID, ndlp->nlp_flag);
+
+       ndlp->nlp_disc_refcnt--;
+
+       /* Check to see if ndlp removal is deferred */
+       if ((ndlp->nlp_disc_refcnt == 0)
+           && (ndlp->nlp_flag & NLP_DELAY_REMOVE)) {
+
+               ndlp->nlp_flag &= ~NLP_DELAY_REMOVE;
+               lpfc_nlp_remove(phba, ndlp);
+               return (NLP_STE_FREED_NODE);
+       }
+       if (rc == NLP_STE_FREED_NODE)
+               return (NLP_STE_FREED_NODE);
+       ndlp->nlp_state = rc;
+       return (rc);
+}
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h
new file mode 100644 (file)
index 0000000..12412c2
--- /dev/null
@@ -0,0 +1,92 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_scsi.h 1.68 2004/11/10 11:40:43EST sf_support Exp  $
+ */
+
+#ifndef _H_LPFC_SCSI
+#define _H_LPFC_SCSI
+
+#include "lpfc_disc.h"
+#include "lpfc_mem.h"
+#include "lpfc_sli.h"
+
+struct lpfc_hba;
+
+
+struct lpfc_target {
+       struct lpfc_nodelist *pnode;    /* Pointer to the node structure. */
+       uint16_t  scsi_id;
+       uint32_t  qcmdcnt;
+       uint32_t  iodonecnt;
+       uint32_t  errorcnt;
+       uint32_t  slavecnt;
+#if defined(FC_TRANS_VER1) || defined(FC_TRANS_265_BLKPATCH)
+       uint16_t  blocked;
+#endif
+#ifdef FC_TRANS_VER1
+       struct scsi_target *starget;            /* Pointer to midlayer target
+                                                  structure. */
+#endif
+#if defined(FC_TRANS_265_BLKPATCH)
+       struct timer_list dev_loss_timer;
+#endif
+};
+
+struct lpfc_scsi_buf {
+       struct scsi_cmnd *pCmd;
+       struct lpfc_hba *scsi_hba;
+       struct lpfc_target *target;
+
+       uint32_t timeout;
+
+       uint16_t status;        /* From IOCB Word 7- ulpStatus */
+       uint32_t result;        /* From IOCB Word 4. */
+
+       uint32_t   seg_cnt;     /* Number of scatter-gather segments returned by
+                                * dma_map_sg.  The driver needs this for calls
+                                * to dma_unmap_sg. */
+       dma_addr_t nonsg_phys;  /* Non scatter-gather physical address. */
+
+       /* dma_ext has both virt, phys to dma-able buffer
+        * which contains fcp_cmd, fcp_rsp and scatter gather list fro upto
+        * 68 (LPFC_SCSI_BPL_SIZE) BDE entries,
+        * xfer length, cdb, data direction....
+        */
+       struct lpfc_dmabuf dma_ext;
+       struct fcp_cmnd *fcp_cmnd;
+       struct fcp_rsp *fcp_rsp;
+       struct ulp_bde64 *fcp_bpl;
+
+       /* cur_iocbq has phys of the dma-able buffer.
+        * Iotag is in here
+        */
+       struct lpfc_iocbq cur_iocbq;
+};
+
+#define LPFC_SCSI_INITIAL_BPL_SIZE  4  /* Number of scsi buf BDEs in fcp_bpl */
+
+#define LPFC_SCSI_DMA_EXT_SIZE 264
+#define LPFC_BPL_SIZE          1024
+
+#define MDAC_DIRECT_CMD                  0x22
+
+#endif                         /* _H_LPFC_SCSI */
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
new file mode 100644 (file)
index 0000000..e9068e7
--- /dev/null
@@ -0,0 +1,3349 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_sli.c 1.178 2004/11/23 16:57:11EST sf_support Exp  $
+ */
+
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_crtn.h"
+#include "lpfc_hw.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_mem.h"
+#include "lpfc_compat.h"
+
+static int lpfc_sli_reset_on_init = 1;
+
+/*
+ * Define macro to log: Mailbox command x%x cannot issue Data
+ * This allows multiple uses of lpfc_msgBlk0311
+ * w/o perturbing log msg utility.
+*/
+#define LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) \
+                       lpfc_printf_log(phba, \
+                               KERN_INFO, \
+                               LOG_MBOX | LOG_SLI, \
+                               "%d:0311 Mailbox command x%x cannot issue " \
+                               "Data: x%x x%x x%x\n", \
+                               phba->brd_no, \
+                               mb->mbxCommand,         \
+                               phba->hba_state,        \
+                               psli->sliinit.sli_flag, \
+                               flag);
+
+
+/* This will save a huge switch to determine if the IOCB cmd
+ * is unsolicited or solicited.
+ */
+#define LPFC_UNKNOWN_IOCB 0
+#define LPFC_UNSOL_IOCB   1
+#define LPFC_SOL_IOCB     2
+#define LPFC_ABORT_IOCB   3
+static uint8_t lpfc_sli_iocb_cmd_type[CMD_MAX_IOCB_CMD] = {
+       LPFC_UNKNOWN_IOCB,      /* 0x00 */
+       LPFC_UNSOL_IOCB,        /* CMD_RCV_SEQUENCE_CX     0x01 */
+       LPFC_SOL_IOCB,          /* CMD_XMIT_SEQUENCE_CR    0x02 */
+       LPFC_SOL_IOCB,          /* CMD_XMIT_SEQUENCE_CX    0x03 */
+       LPFC_SOL_IOCB,          /* CMD_XMIT_BCAST_CN       0x04 */
+       LPFC_SOL_IOCB,          /* CMD_XMIT_BCAST_CX       0x05 */
+       LPFC_UNKNOWN_IOCB,      /* CMD_QUE_RING_BUF_CN     0x06 */
+       LPFC_UNKNOWN_IOCB,      /* CMD_QUE_XRI_BUF_CX      0x07 */
+       LPFC_UNKNOWN_IOCB,      /* CMD_IOCB_CONTINUE_CN    0x08 */
+       LPFC_UNKNOWN_IOCB,      /* CMD_RET_XRI_BUF_CX      0x09 */
+       LPFC_SOL_IOCB,          /* CMD_ELS_REQUEST_CR      0x0A */
+       LPFC_SOL_IOCB,          /* CMD_ELS_REQUEST_CX      0x0B */
+       LPFC_UNKNOWN_IOCB,      /* 0x0C */
+       LPFC_UNSOL_IOCB,        /* CMD_RCV_ELS_REQ_CX      0x0D */
+       LPFC_ABORT_IOCB,        /* CMD_ABORT_XRI_CN        0x0E */
+       LPFC_ABORT_IOCB,        /* CMD_ABORT_XRI_CX        0x0F */
+       LPFC_ABORT_IOCB,        /* CMD_CLOSE_XRI_CR        0x10 */
+       LPFC_ABORT_IOCB,        /* CMD_CLOSE_XRI_CX        0x11 */
+       LPFC_SOL_IOCB,          /* CMD_CREATE_XRI_CR       0x12 */
+       LPFC_SOL_IOCB,          /* CMD_CREATE_XRI_CX       0x13 */
+       LPFC_SOL_IOCB,          /* CMD_GET_RPI_CN          0x14 */
+       LPFC_SOL_IOCB,          /* CMD_XMIT_ELS_RSP_CX     0x15 */
+       LPFC_SOL_IOCB,          /* CMD_GET_RPI_CR          0x16 */
+       LPFC_ABORT_IOCB,        /* CMD_XRI_ABORTED_CX      0x17 */
+       LPFC_SOL_IOCB,          /* CMD_FCP_IWRITE_CR       0x18 */
+       LPFC_SOL_IOCB,          /* CMD_FCP_IWRITE_CX       0x19 */
+       LPFC_SOL_IOCB,          /* CMD_FCP_IREAD_CR        0x1A */
+       LPFC_SOL_IOCB,          /* CMD_FCP_IREAD_CX        0x1B */
+       LPFC_SOL_IOCB,          /* CMD_FCP_ICMND_CR        0x1C */
+       LPFC_SOL_IOCB,          /* CMD_FCP_ICMND_CX        0x1D */
+       LPFC_UNKNOWN_IOCB,      /* 0x1E */
+       LPFC_SOL_IOCB,          /* CMD_FCP_TSEND_CX        0x1F */
+       LPFC_SOL_IOCB,          /* CMD_ADAPTER_MSG         0x20 */
+       LPFC_SOL_IOCB,          /* CMD_FCP_TRECEIVE_CX     0x21 */
+       LPFC_SOL_IOCB,          /* CMD_ADAPTER_DUMP        0x22 */
+       LPFC_SOL_IOCB,          /* CMD_FCP_TRSP_CX         0x23 */
+       /* 0x24 - 0x80 */
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       /* 0x30 */
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB,
+       /* 0x40 */
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB,
+       /* 0x50 */
+       LPFC_SOL_IOCB,
+       LPFC_SOL_IOCB,
+       LPFC_UNKNOWN_IOCB,
+       LPFC_SOL_IOCB,
+       LPFC_SOL_IOCB,
+       LPFC_UNSOL_IOCB,
+       LPFC_UNSOL_IOCB,
+       LPFC_SOL_IOCB,
+       LPFC_SOL_IOCB,
+
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB,
+       /* 0x60 */
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB,
+       /* 0x70 */
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB,
+       /* 0x80 */
+       LPFC_UNKNOWN_IOCB,
+       LPFC_UNSOL_IOCB,        /* CMD_RCV_SEQUENCE64_CX   0x81 */
+       LPFC_SOL_IOCB,          /* CMD_XMIT_SEQUENCE64_CR  0x82 */
+       LPFC_SOL_IOCB,          /* CMD_XMIT_SEQUENCE64_CX  0x83 */
+       LPFC_SOL_IOCB,          /* CMD_XMIT_BCAST64_CN     0x84 */
+       LPFC_SOL_IOCB,          /* CMD_XMIT_BCAST64_CX     0x85 */
+       LPFC_UNKNOWN_IOCB,      /* CMD_QUE_RING_BUF64_CN   0x86 */
+       LPFC_UNKNOWN_IOCB,      /* CMD_QUE_XRI_BUF64_CX    0x87 */
+       LPFC_UNKNOWN_IOCB,      /* CMD_IOCB_CONTINUE64_CN  0x88 */
+       LPFC_UNKNOWN_IOCB,      /* CMD_RET_XRI_BUF64_CX    0x89 */
+       LPFC_SOL_IOCB,          /* CMD_ELS_REQUEST64_CR    0x8A */
+       LPFC_SOL_IOCB,          /* CMD_ELS_REQUEST64_CX    0x8B */
+       LPFC_ABORT_IOCB,        /* CMD_ABORT_MXRI64_CN     0x8C */
+       LPFC_UNSOL_IOCB,        /* CMD_RCV_ELS_REQ64_CX    0x8D */
+       /* 0x8E - 0x94 */
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB,
+       LPFC_SOL_IOCB,          /* CMD_XMIT_ELS_RSP64_CX   0x95 */
+       LPFC_UNKNOWN_IOCB,      /* 0x96 */
+       LPFC_UNKNOWN_IOCB,      /* 0x97 */
+       LPFC_SOL_IOCB,          /* CMD_FCP_IWRITE64_CR     0x98 */
+       LPFC_SOL_IOCB,          /* CMD_FCP_IWRITE64_CX     0x99 */
+       LPFC_SOL_IOCB,          /* CMD_FCP_IREAD64_CR      0x9A */
+       LPFC_SOL_IOCB,          /* CMD_FCP_IREAD64_CX      0x9B */
+       LPFC_SOL_IOCB,          /* CMD_FCP_ICMND64_CR      0x9C */
+       LPFC_SOL_IOCB,          /* CMD_FCP_ICMND64_CX      0x9D */
+       LPFC_UNKNOWN_IOCB,      /* 0x9E */
+       LPFC_SOL_IOCB,          /* CMD_FCP_TSEND64_CX      0x9F */
+       LPFC_UNKNOWN_IOCB,      /* 0xA0 */
+       LPFC_SOL_IOCB,          /* CMD_FCP_TRECEIVE64_CX   0xA1 */
+       LPFC_UNKNOWN_IOCB,      /* 0xA2 */
+       LPFC_SOL_IOCB,          /* CMD_FCP_TRSP64_CX       0xA3 */
+       /* 0xA4 - 0xC1 */
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_SOL_IOCB,          /* CMD_GEN_REQUEST64_CR    0xC2 */
+       LPFC_SOL_IOCB,          /* CMD_GEN_REQUEST64_CX    0xC3 */
+       /* 0xC4 - 0xCF */
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+
+       LPFC_SOL_IOCB,
+       LPFC_SOL_IOCB,          /* CMD_SENDTEXT_CR              0xD1 */
+       LPFC_SOL_IOCB,          /* CMD_SENDTEXT_CX              0xD2 */
+       LPFC_SOL_IOCB,          /* CMD_RCV_LOGIN                0xD3 */
+       LPFC_SOL_IOCB,          /* CMD_ACCEPT_LOGIN             0xD4 */
+       LPFC_SOL_IOCB,          /* CMD_REJECT_LOGIN             0xD5 */
+       LPFC_UNSOL_IOCB,
+       /* 0xD7 - 0xDF */
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB, LPFC_UNKNOWN_IOCB,
+       /* 0xE0 */
+       LPFC_UNSOL_IOCB,
+       LPFC_SOL_IOCB,
+       LPFC_SOL_IOCB,
+       LPFC_SOL_IOCB,
+       LPFC_SOL_IOCB,
+       LPFC_UNSOL_IOCB
+};
+
+
+static int
+lpfc_sli_ring_map(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       LPFC_MBOXQ_t *pmb;
+       MAILBOX_t *pmbox;
+       int i;
+
+       psli = &phba->sli;
+
+       /* Get a Mailbox buffer to setup mailbox commands for HBA
+          initialization */
+       if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool,
+                                                 GFP_ATOMIC)) == 0) {
+               phba->hba_state = LPFC_HBA_ERROR;
+               return -ENOMEM;
+       }
+       pmbox = &pmb->mb;
+
+       /* Initialize the struct lpfc_sli_ring structure for each ring */
+       for (i = 0; i < psli->sliinit.num_rings; i++) {
+               /* Issue a CONFIG_RING mailbox command for each ring */
+               phba->hba_state = LPFC_INIT_MBX_CMDS;
+               lpfc_config_ring(phba, i, pmb);
+               if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+                       /* Adapter failed to init, mbxCmd <cmd> CFG_RING,
+                          mbxStatus <status>, ring <num> */
+                       lpfc_printf_log(phba,
+                                       KERN_ERR,
+                                       LOG_INIT,
+                                       "%d:0446 Adapter failed to init, "
+                                       "mbxCmd x%x CFG_RING, mbxStatus x%x, "
+                                       "ring %d\n",
+                                       phba->brd_no,
+                                       pmbox->mbxCommand,
+                                       pmbox->mbxStatus,
+                                       i);
+                       phba->hba_state = LPFC_HBA_ERROR;
+                       mempool_free( pmb, phba->mbox_mem_pool);
+                       return -ENXIO;
+               }
+       }
+       mempool_free( pmb, phba->mbox_mem_pool);
+       return 0;
+}
+
+static int
+lpfc_sli_ringtxcmpl_put(struct lpfc_hba * phba,
+                       struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocb)
+{
+       uint16_t iotag;
+
+       list_add_tail(&piocb->list, &pring->txcmplq);
+       pring->txcmplq_cnt++;
+
+       if (pring->fast_lookup) {
+               /* Setup fast lookup based on iotag for completion */
+               iotag = piocb->iocb.ulpIoTag;
+               if (iotag && (iotag
+                     < phba->sli.sliinit.ringinit[pring->ringno].fast_iotag))
+                       *(pring->fast_lookup + iotag) = piocb;
+               else {
+                       
+                       /* Cmd ring <ringno> put: iotag <iotag> greater then
+                          configured max <fast_iotag> wd0 <icmd> */
+                       lpfc_printf_log(phba,
+                                       KERN_ERR,
+                                       LOG_SLI,
+                                       "%d:0316 Cmd ring %d put: iotag x%x "
+                                       "greater then configured max x%x "
+                                       "wd0 x%x\n",
+                                       phba->brd_no,
+                                       pring->ringno, iotag, phba->sli.sliinit
+                                       .ringinit[pring->ringno].fast_iotag,
+                                       *(((uint32_t *)(&piocb->iocb)) + 7));
+               }
+       }
+       return (0);
+}
+
+static struct lpfc_iocbq *
+lpfc_sli_ringtx_get(struct lpfc_hba * phba, struct lpfc_sli_ring * pring)
+{
+       struct list_head *dlp;
+       struct lpfc_iocbq *cmd_iocb;
+       struct lpfc_iocbq *next_iocb;
+
+       dlp = &pring->txq;
+       cmd_iocb = NULL;
+       next_iocb = (struct lpfc_iocbq *) pring->txq.next;
+       if (next_iocb != (struct lpfc_iocbq *) & pring->txq) {
+               /* If the first ptr is not equal to the list header,
+                * deque the IOCBQ_t and return it.
+                */
+               cmd_iocb = next_iocb;
+               list_del(&cmd_iocb->list);
+               pring->txq_cnt--;
+       }
+       return (cmd_iocb);
+}
+
+static IOCB_t *
+lpfc_sli_next_iocb_slot (struct lpfc_hba *phba, struct lpfc_sli_ring *pring)
+{
+       MAILBOX_t *mbox = (MAILBOX_t *)phba->sli.MBhostaddr;
+       PGP *pgp = (PGP *)&mbox->us.s2.port[pring->ringno];
+       uint32_t  max_cmd_idx =
+               phba->sli.sliinit.ringinit[pring->ringno].numCiocb;
+       IOCB_t *iocb = NULL;
+       
+       if((pring->next_cmdidx == pring->cmdidx) &&
+          (++pring->next_cmdidx >= max_cmd_idx))
+               pring->next_cmdidx = 0;
+
+       if (unlikely(pring->local_getidx == pring->next_cmdidx)) {
+               
+               pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
+
+               if (unlikely(pring->local_getidx >= max_cmd_idx)) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                                       "%d:0315 Ring %d issue: portCmdGet %d "
+                                       "is bigger then cmd ring %d\n",
+                                       phba->brd_no, pring->ringno,
+                                       pring->local_getidx, max_cmd_idx);
+
+                       phba->hba_state = LPFC_HBA_ERROR;
+                       lpfc_handle_eratt(phba, HS_FFER3);
+                       
+                       return NULL;
+               }
+
+               if (pring->local_getidx == pring->next_cmdidx)
+                       return NULL;
+       }
+
+       iocb = IOCB_ENTRY(pring->cmdringaddr, pring->cmdidx);
+
+       return iocb;
+}
+
+static void
+lpfc_sli_submit_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+               IOCB_t *iocb, struct lpfc_iocbq *nextiocb)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       int ringno = pring->ringno;
+
+       /*
+        * Alloocate and set up an iotag
+        */
+       nextiocb->iocb.ulpIoTag =
+               lpfc_sli_next_iotag(phba, &psli->ring[psli->fcp_ring]);
+
+       /*
+        * Issue iocb command to adapter
+        */
+       lpfc_sli_pcimem_bcopy((uint32_t *)&nextiocb->iocb,
+                             (uint32_t *)(iocb), sizeof (IOCB_t));
+       wmb();
+       psli->slistat.iocbCmd[ringno]++;
+
+       /*
+        * If there is no completion routine to call, we can release the
+        * IOCB buffer back right now. For IOCBs, like QUE_RING_BUF,
+        * that have no rsp ring completion, iocb_cmpl MUST be NULL.
+        */
+       if (nextiocb->iocb_cmpl)
+               lpfc_sli_ringtxcmpl_put(phba, pring, nextiocb);
+       else
+               mempool_free(nextiocb, phba->iocb_mem_pool);
+
+       /*
+        * Let the HBA know what IOCB slot will be the next one the
+        * driver will put a command into.
+        */
+       pring->cmdidx = pring->next_cmdidx;
+       writeb(pring->cmdidx,
+              (u8 *)phba->MBslimaddr + (SLIMOFF + (ringno * 2)) * 4);
+
+       return;
+}
+
+static void
+lpfc_sli_update_full_ring(struct lpfc_hba * phba,
+                         struct lpfc_sli_ring *pring)
+{
+       int ringno = pring->ringno;
+
+       pring->flag |= LPFC_CALL_RING_AVAILABLE;
+
+       wmb();
+
+       /*
+        * Set ring 'ringno' to SET R0CE_REQ in Chip Att register.
+        * The HBA will tell us when an IOCB entry is available.
+        */
+       writel((CA_R0ATT|CA_R0CE_REQ) << (ringno*4), phba->CAregaddr);
+       readl(phba->CAregaddr); /* flush */
+
+       phba->sli.slistat.iocbCmdFull[ringno]++;
+}
+
+static void
+lpfc_sli_update_ring(struct lpfc_hba * phba,
+                    struct lpfc_sli_ring *pring)
+{
+       int ringno = pring->ringno;
+
+       /*
+        * Tell the HBA that there is work to do in this ring.
+        */
+       wmb();
+       writel(CA_R0ATT << (ringno * 4), phba->CAregaddr);
+       readl(phba->CAregaddr); /* flush */
+}
+
+static void
+lpfc_sli_resume_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       IOCB_t *iocb;
+       struct lpfc_iocbq *nextiocb;
+
+       /*
+        * Check to see if:
+        *  (a) there is anything on the txq to send
+        *  (b) link is up
+        *  (c) link attention events can be processed (fcp ring only)
+        *  (d) IOCB processing is not blocked by the outstanding mbox command.
+        */
+       if (pring->txq_cnt &&
+           (phba->hba_state > LPFC_LINK_DOWN) &&
+           (pring->ringno != psli->fcp_ring ||
+            psli->sliinit.sli_flag & LPFC_PROCESS_LA) &&
+           !(pring->flag & LPFC_STOP_IOCB_MBX)) {
+
+               while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) &&
+                      (nextiocb = lpfc_sli_ringtx_get(phba, pring)))
+                       lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb);
+               
+               if (iocb)
+                       lpfc_sli_update_ring(phba, pring);
+               else
+                       lpfc_sli_update_full_ring(phba, pring);
+       }
+
+       return;
+}
+
+/* lpfc_sli_turn_on_ring is only called by lpfc_sli_handle_mb_event below */
+static void
+lpfc_sli_turn_on_ring(struct lpfc_hba * phba, int ringno)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       PGP *pgp;
+
+       psli = &phba->sli;
+       pring = &psli->ring[ringno];
+       pgp = (PGP *) & (((MAILBOX_t *)psli->MBhostaddr)->us.s2.port[ringno]);
+
+       /* If the ring is active, flag it */
+       if (psli->ring[ringno].cmdringaddr) {
+               if (psli->ring[ringno].flag & LPFC_STOP_IOCB_MBX) {
+                       psli->ring[ringno].flag &= ~LPFC_STOP_IOCB_MBX;
+                       /*
+                        * Force update of the local copy of cmdGetInx
+                        */
+                       pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
+                       lpfc_sli_resume_iocb(phba, pring);
+               }
+       }
+}
+
+static int
+lpfc_sli_chk_mbx_command(uint8_t mbxCommand)
+{
+       uint8_t ret;
+
+       switch (mbxCommand) {
+       case MBX_LOAD_SM:
+       case MBX_READ_NV:
+       case MBX_WRITE_NV:
+       case MBX_RUN_BIU_DIAG:
+       case MBX_INIT_LINK:
+       case MBX_DOWN_LINK:
+       case MBX_CONFIG_LINK:
+       case MBX_CONFIG_RING:
+       case MBX_RESET_RING:
+       case MBX_READ_CONFIG:
+       case MBX_READ_RCONFIG:
+       case MBX_READ_SPARM:
+       case MBX_READ_STATUS:
+       case MBX_READ_RPI:
+       case MBX_READ_XRI:
+       case MBX_READ_REV:
+       case MBX_READ_LNK_STAT:
+       case MBX_REG_LOGIN:
+       case MBX_UNREG_LOGIN:
+       case MBX_READ_LA:
+       case MBX_CLEAR_LA:
+       case MBX_DUMP_MEMORY:
+       case MBX_DUMP_CONTEXT:
+       case MBX_RUN_DIAGS:
+       case MBX_RESTART:
+       case MBX_UPDATE_CFG:
+       case MBX_DOWN_LOAD:
+       case MBX_DEL_LD_ENTRY:
+       case MBX_RUN_PROGRAM:
+       case MBX_SET_MASK:
+       case MBX_SET_SLIM:
+       case MBX_UNREG_D_ID:
+       case MBX_CONFIG_FARP:
+       case MBX_LOAD_AREA:
+       case MBX_RUN_BIU_DIAG64:
+       case MBX_CONFIG_PORT:
+       case MBX_READ_SPARM64:
+       case MBX_READ_RPI64:
+       case MBX_REG_LOGIN64:
+       case MBX_READ_LA64:
+       case MBX_FLASH_WR_ULA:
+       case MBX_SET_DEBUG:
+       case MBX_LOAD_EXP_ROM:
+               ret = mbxCommand;
+               break;
+       default:
+               ret = MBX_SHUTDOWN;
+               break;
+       }
+       return (ret);
+}
+static int
+lpfc_sli_handle_mb_event(struct lpfc_hba * phba)
+{
+       MAILBOX_t *mbox;
+       MAILBOX_t *pmbox;
+       LPFC_MBOXQ_t *pmb;
+       struct lpfc_dmabuf *mp;
+       struct lpfc_sli *psli;
+       int i;
+       unsigned long iflag;
+       uint32_t process_next;
+
+
+       psli = &phba->sli;
+       /* We should only get here if we are in SLI2 mode */
+       if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) {
+               return (1);
+       }
+
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+
+       psli->slistat.mboxEvent++;
+
+       /* Get a Mailbox buffer to setup mailbox commands for callback */
+       if ((pmb = psli->mbox_active)) {
+               pmbox = &pmb->mb;
+               mbox = (MAILBOX_t *) psli->MBhostaddr;
+
+               /* First check out the status word */
+               lpfc_sli_pcimem_bcopy((uint32_t *) mbox, (uint32_t *) pmbox,
+                                    sizeof (uint32_t));
+
+               /* Sanity check to ensure the host owns the mailbox */
+               if (pmbox->mbxOwner != OWN_HOST) {
+                       /* Lets try for a while */
+                       for (i = 0; i < 10240; i++) {
+                               /* First copy command data */
+                               lpfc_sli_pcimem_bcopy((uint32_t *) mbox,
+                                                    (uint32_t *) pmbox,
+                                                    sizeof (uint32_t));
+                               if (pmbox->mbxOwner == OWN_HOST)
+                                       goto mbout;
+                       }
+                       /* Stray Mailbox Interrupt, mbxCommand <cmd> mbxStatus
+                          <status> */
+                       lpfc_printf_log(phba,
+                                       KERN_ERR,
+                                       LOG_MBOX | LOG_SLI,
+                                       "%d:0304 Stray Mailbox Interrupt "
+                                       "mbxCommand x%x mbxStatus x%x\n",
+                                       phba->brd_no,
+                                       pmbox->mbxCommand,
+                                       pmbox->mbxStatus);
+
+                       psli->sliinit.sli_flag |= LPFC_SLI_MBOX_ACTIVE;
+                       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+                       return (1);
+               }
+
+             mbout:
+               del_timer_sync(&psli->mbox_tmo);
+
+               /*
+                * It is a fatal error if unknown mbox command completion.
+                */
+               if (lpfc_sli_chk_mbx_command(pmbox->mbxCommand) ==
+                   MBX_SHUTDOWN) {
+
+                       /* Unknow mailbox command compl */
+                       lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_MBOX | LOG_SLI,
+                               "%d:0323 Unknown Mailbox command %x Cmpl\n",
+                               phba->brd_no,
+                               pmbox->mbxCommand);
+                       phba->hba_state = LPFC_HBA_ERROR;
+                       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+                       lpfc_handle_eratt(phba, HS_FFER3);
+                       return (0);
+               }
+
+               psli->mbox_active = NULL;
+               if (pmbox->mbxStatus) {
+                       psli->slistat.mboxStatErr++;
+                       if (pmbox->mbxStatus == MBXERR_NO_RESOURCES) {
+                               /* Mbox cmd cmpl error - RETRYing */
+                               lpfc_printf_log(phba,
+                                       KERN_INFO,
+                                       LOG_MBOX | LOG_SLI,
+                                       "%d:0305 Mbox cmd cmpl error - "
+                                       "RETRYing Data: x%x x%x x%x x%x\n",
+                                       phba->brd_no,
+                                       pmbox->mbxCommand,
+                                       pmbox->mbxStatus,
+                                       pmbox->un.varWords[0],
+                                       phba->hba_state);
+                               pmbox->mbxStatus = 0;
+                               pmbox->mbxOwner = OWN_HOST;
+                               psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+                               if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT)
+                                   == MBX_SUCCESS) {
+                                       spin_unlock_irqrestore(
+                                                      phba->host->host_lock,
+                                                      iflag);
+                                       return (0);
+                               }
+                       }
+               }
+
+               /* Mailbox cmd <cmd> Cmpl <cmpl> */
+               lpfc_printf_log(phba,
+                               KERN_INFO,
+                               LOG_MBOX | LOG_SLI,
+                               "%d:0307 Mailbox cmd x%x Cmpl x%p "
+                               "Data: x%x x%x x%x x%x x%x x%x x%x x%x x%x\n",
+                               phba->brd_no,
+                               pmbox->mbxCommand,
+                               pmb->mbox_cmpl,
+                               *((uint32_t *) pmbox),
+                               pmbox->un.varWords[0],
+                               pmbox->un.varWords[1],
+                               pmbox->un.varWords[2],
+                               pmbox->un.varWords[3],
+                               pmbox->un.varWords[4],
+                               pmbox->un.varWords[5],
+                               pmbox->un.varWords[6],
+                               pmbox->un.varWords[7]);
+
+               if (pmb->mbox_cmpl) {
+                       /* Copy entire mbox completion over buffer */
+                       lpfc_sli_pcimem_bcopy((uint32_t *) mbox,
+                                            (uint32_t *) pmbox,
+                                            (sizeof (uint32_t) *
+                                             (MAILBOX_CMD_WSIZE)));
+                       /* All mbox cmpls are posted to discovery tasklet */
+                       lpfc_discq_post_event(phba, pmb, NULL,
+                               LPFC_EVT_MBOX);
+               } else {
+                       mp = (struct lpfc_dmabuf *) (pmb->context1);
+                       if (mp) {
+                               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+                               kfree(mp);
+                       }
+                       mempool_free( pmb, phba->mbox_mem_pool);
+               }
+       }
+
+
+       do {
+               process_next = 0;       /* by default don't loop */
+               psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+
+               /* Process next mailbox command if there is one */
+               if ((pmb = lpfc_mbox_get(phba))) {
+                       if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) ==
+                           MBX_NOT_FINISHED) {
+                               mp = (struct lpfc_dmabuf *) (pmb->context1);
+                               if (mp) {
+                                       lpfc_mbuf_free(phba, mp->virt,
+                                                               mp->phys);
+                                       kfree(mp);
+                               }
+                               mempool_free( pmb, phba->mbox_mem_pool);
+                               process_next = 1;
+                               continue;       /* loop back */
+                       }
+               } else {
+                       /* Turn on IOCB processing */
+                       for (i = 0; i < psli->sliinit.num_rings; i++) {
+                               lpfc_sli_turn_on_ring(phba, i);
+                       }
+
+                       /* Free any lpfc_dmabuf's waiting for mbox cmd cmpls */
+                       while (!list_empty(&phba->freebufList)) {
+                               struct lpfc_dmabuf *mp;
+
+                               mp = (struct lpfc_dmabuf *)
+                                       (phba->freebufList.next);
+                               if (mp) {
+                                       lpfc_mbuf_free(phba, mp->virt,
+                                                      mp->phys);
+                                       list_del(&mp->list);
+                                       kfree(mp);
+                               }
+                       }
+               }
+
+       } while (process_next);
+
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+       return (0);
+}
+static int
+lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+                           struct lpfc_iocbq *saveq)
+{
+       struct lpfc_sli       * psli;
+       IOCB_t           * irsp;
+       LPFC_RING_INIT_t * pringinit;
+       WORD5            * w5p;
+       uint32_t           Rctl, Type;
+       uint32_t           match, ringno, i;
+
+       psli = &phba->sli;
+       match = 0;
+       ringno = pring->ringno;
+       irsp = &(saveq->iocb);
+       if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX)
+           || (irsp->ulpCommand == CMD_RCV_ELS_REQ_CX)) {
+               Rctl = FC_ELS_REQ;
+               Type = FC_ELS_DATA;
+       } else {
+               w5p =
+                   (WORD5 *) & (saveq->iocb.un.
+                                ulpWord[5]);
+               Rctl = w5p->hcsw.Rctl;
+               Type = w5p->hcsw.Type;
+       }
+       /* unSolicited Responses */
+       pringinit = &psli->sliinit.ringinit[ringno];
+       if (pringinit->prt[0].profile) {
+               /* If this ring has a profile set, just
+                  send it to prt[0] */
+               /* All unsol iocbs for LPFC_ELS_RING
+                * are posted to discovery tasklet.
+                */
+               if (ringno == LPFC_ELS_RING) {
+                       lpfc_discq_post_event(phba, (void *)&pringinit->prt[0],
+                       (void *)saveq,  LPFC_EVT_UNSOL_IOCB);
+               }
+               else {
+                       (pringinit->prt[0].
+                       lpfc_sli_rcv_unsol_event) (phba, pring, saveq);
+               }
+               match = 1;
+       } else {
+               /* We must search, based on rctl / type
+                  for the right routine */
+               for (i = 0; i < pringinit->num_mask;
+                    i++) {
+                       if ((pringinit->prt[i].rctl ==
+                            Rctl)
+                           && (pringinit->prt[i].
+                               type == Type)) {
+                               /* All unsol iocbs for LPFC_ELS_RING
+                                * are posted to discovery tasklet.
+                                */
+                               if (ringno == LPFC_ELS_RING) {
+                                       lpfc_discq_post_event(phba,
+                                       (void *)&pringinit->prt[i],
+                                       (void *)saveq,  LPFC_EVT_UNSOL_IOCB);
+                               }
+                               else {
+                                       (pringinit->prt[i].
+                                       lpfc_sli_rcv_unsol_event)
+                                       (phba, pring, saveq);
+                               }
+                               match = 1;
+                               break;
+                       }
+               }
+       }
+       if (match == 0) {
+               /* Unexpected Rctl / Type received */
+               /* Ring <ringno> handler: unexpected
+                  Rctl <Rctl> Type <Type> received */
+               lpfc_printf_log(phba,
+                               KERN_WARNING,
+                               LOG_SLI,
+                               "%d:0313 Ring %d handler: unexpected Rctl x%x "
+                               "Type x%x received \n",
+                               phba->brd_no,
+                               ringno,
+                               Rctl,
+                               Type);
+       }
+       return(1);
+}
+static struct lpfc_iocbq *
+lpfc_search_txcmpl(struct lpfc_sli_ring * pring, struct lpfc_iocbq * prspiocb)
+{
+       IOCB_t *icmd = NULL;
+       IOCB_t *irsp = NULL;
+       struct lpfc_iocbq *cmd_iocb;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       uint16_t iotag;
+
+       irsp = &prspiocb->iocb;
+       iotag = irsp->ulpIoTag;
+       cmd_iocb = NULL;
+
+       /* Search through txcmpl from the begining */
+       list_for_each_entry_safe(iocb, next_iocb, &(pring->txcmplq), list) {
+               icmd = &iocb->iocb;
+               if (iotag == icmd->ulpIoTag) {
+                       /* Found a match.  */
+                       cmd_iocb = iocb;
+                       list_del(&iocb->list);
+                       pring->txcmplq_cnt--;
+                       break;
+               }
+       }
+
+       return (cmd_iocb);
+}
+static struct lpfc_iocbq *
+lpfc_sli_ringtxcmpl_get(struct lpfc_hba * phba,
+                       struct lpfc_sli_ring * pring,
+                       struct lpfc_iocbq * prspiocb, uint32_t srch)
+{
+       struct list_head *dlp;
+       IOCB_t *irsp = NULL;
+       struct lpfc_iocbq *cmd_iocb;
+       struct lpfc_sli *psli;
+       uint16_t iotag;
+
+
+       dlp = &pring->txcmplq;
+
+       if (pring->fast_lookup && (srch == 0)) {
+               /*
+                *  Use fast lookup based on iotag for completion
+                */
+               psli = &phba->sli;
+               irsp = &prspiocb->iocb;
+               iotag = irsp->ulpIoTag;
+               if (iotag < psli->sliinit.ringinit[pring->ringno].fast_iotag) {
+                       cmd_iocb = *(pring->fast_lookup + iotag);
+                       *(pring->fast_lookup + iotag) = NULL;
+                       if (cmd_iocb) {
+                               list_del(&cmd_iocb->list);
+                               pring->txcmplq_cnt--;
+                               return cmd_iocb;
+                       }
+               } else {
+                       /*
+                        * Rsp ring <ringno> get: iotag <iotag> greater then
+                        *  configured max <fast_iotag> wd0 <irsp>
+                        */
+                       lpfc_printf_log(phba,
+                                       KERN_ERR,
+                                       LOG_SLI,
+                                       "%d:0317 Rsp ring %d get: iotag x%x "
+                                       "greater then configured max x%x "
+                                       "wd0 x%x\n",
+                                       phba->brd_no,
+                                       pring->ringno, iotag,
+                                       psli->sliinit.ringinit[pring->ringno]
+                                       .fast_iotag,
+                                       *(((uint32_t *) irsp) + 7));
+               }
+       }
+
+       cmd_iocb = lpfc_search_txcmpl(pring, prspiocb);
+
+       return cmd_iocb;
+}
+
+static int
+lpfc_sli_process_sol_iocb(struct lpfc_hba * phba, struct lpfc_sli_ring * pring,
+                         struct lpfc_iocbq *saveq)
+{
+       struct lpfc_iocbq * cmdiocbp;
+       int            ringno, rc;
+       unsigned long iflag;
+
+       rc = 1;
+       ringno = pring->ringno;
+       /* Solicited Responses */
+       /* Based on the iotag field, get the cmd IOCB
+          from the txcmplq */
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+       if ((cmdiocbp =
+            lpfc_sli_ringtxcmpl_get(phba, pring, saveq,
+                                    0))) {
+               /* Call the specified completion
+                  routine */
+               if (cmdiocbp->iocb_cmpl) {
+                       /* All iocb cmpls for LPFC_ELS_RING
+                        * are posted to discovery tasklet.
+                        */
+                       if (ringno == LPFC_ELS_RING) {
+                               lpfc_discq_post_event(phba, (void *)cmdiocbp,
+                                       (void *)saveq,  LPFC_EVT_SOL_IOCB);
+                       }
+                       else {
+                               if (cmdiocbp->iocb_flag & LPFC_IO_POLL) {
+                                       rc = 0;
+                               }
+
+                               spin_unlock_irqrestore(phba->host->host_lock,
+                                                      iflag);
+                               (cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq);
+                               spin_lock_irqsave(phba->host->host_lock, iflag);
+                       }
+               } else {
+                       mempool_free( cmdiocbp, phba->iocb_mem_pool);
+               }
+       } else {
+               /* Could not find the initiating command
+                * based of the response iotag.
+                * This is expected on ELS ring because of lpfc_els_abort().
+                */
+               if (ringno != LPFC_ELS_RING) {
+                       /* Ring <ringno> handler: unexpected
+                          completion IoTag <IoTag> */
+                       lpfc_printf_log(phba,
+                               KERN_WARNING,
+                               LOG_SLI,
+                               "%d:0322 Ring %d handler: unexpected "
+                               "completion IoTag x%x Data: x%x x%x x%x x%x\n",
+                               phba->brd_no,
+                               ringno,
+                               saveq->iocb.ulpIoTag,
+                               saveq->iocb.ulpStatus,
+                               saveq->iocb.un.ulpWord[4],
+                               saveq->iocb.ulpCommand,
+                               saveq->iocb.ulpContext);
+               }
+       }
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+       return(rc);
+}
+static int
+lpfc_sli_handle_ring_event(struct lpfc_hba * phba,
+                          struct lpfc_sli_ring * pring, uint32_t mask)
+{
+       struct lpfc_sli       * psli;
+       IOCB_t           * entry;
+       IOCB_t           * irsp;
+       struct lpfc_iocbq     * rspiocbp, *next_iocb;
+       struct lpfc_iocbq     * cmdiocbp;
+       struct lpfc_iocbq     * saveq;
+       HGP              * hgp;
+       PGP              * pgp;
+       MAILBOX_t        * mbox;
+       uint32_t           status, free_saveq;
+       uint32_t           portRspPut, portRspMax;
+       int                ringno, loopcnt, rc;
+       uint8_t            type;
+       unsigned long      iflag;
+       void *to_slim;
+
+       psli   = &phba->sli;
+       ringno = pring->ringno;
+       irsp   = NULL;
+       rc     = 1;
+
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+       psli->slistat.iocbEvent[ringno]++;
+
+       /* At this point we assume SLI-2 */
+       mbox = (MAILBOX_t *) psli->MBhostaddr;
+       pgp = (PGP *) & mbox->us.s2.port[ringno];
+       hgp = (HGP *) & mbox->us.s2.host[ringno];
+
+       /* portRspMax is the number of rsp ring entries for this specific
+          ring. */
+       portRspMax = psli->sliinit.ringinit[ringno].numRiocb;
+
+       rspiocbp = NULL;
+       loopcnt = 0;
+
+       /* Gather iocb entries off response ring.
+        * rspidx is the IOCB index of the next IOCB that the driver
+        * is going to process.
+        */
+       entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx);
+       portRspPut = le32_to_cpu(pgp->rspPutInx);
+
+       if (portRspPut >= portRspMax) {
+
+               /* Ring <ringno> handler: portRspPut <portRspPut> is bigger then
+                  rsp ring <portRspMax> */
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_SLI,
+                               "%d:0312 Ring %d handler: portRspPut %d "
+                               "is bigger then rsp ring %d\n",
+                               phba->brd_no,
+                               ringno, portRspPut, portRspMax);
+               /*
+                * Treat it as adapter hardware error.
+                */
+               phba->hba_state = LPFC_HBA_ERROR;
+               spin_unlock_irqrestore(phba->host->host_lock, iflag);
+               lpfc_handle_eratt(phba, HS_FFER3);
+               return (1);
+       }
+
+       rmb();
+
+       /* Get the next available response iocb.
+        * rspidx is the IOCB index of the next IOCB that the driver
+        * is going to process.
+        */
+       while (pring->rspidx != portRspPut) {
+               /* get an iocb buffer to copy entry into */
+               if ((rspiocbp = mempool_alloc(phba->iocb_mem_pool,
+                                             GFP_ATOMIC)) == 0) {
+                       break;
+               }
+
+               lpfc_sli_pcimem_bcopy((uint32_t *) entry,
+                                     (uint32_t *) & rspiocbp->iocb,
+                                     sizeof (IOCB_t));
+               irsp = &rspiocbp->iocb;
+
+               /* bump iocb available response index */
+               if (++pring->rspidx >= portRspMax) {
+                       pring->rspidx = 0;
+               }
+
+               /* Let the HBA know what IOCB slot will be the next one the
+                * driver will read a response from.
+                */
+               to_slim = (uint8_t *) phba->MBslimaddr +
+                       (SLIMOFF + (ringno * 2) + 1) * 4;
+               writeb( pring->rspidx, to_slim);
+
+               /* chain all iocb entries until LE is set */
+               if (list_empty(&(pring->iocb_continueq))) {
+                       list_add(&rspiocbp->list, &(pring->iocb_continueq));
+               } else {
+                       list_add_tail(&rspiocbp->list,
+                                     &(pring->iocb_continueq));
+               }
+               pring->iocb_continueq_cnt++;
+
+               /*
+                * When the ulpLe field is set, the entire Command has been
+                * received. Start by getting a pointer to the first iocb entry
+                * in the chain.
+                */
+               if (irsp->ulpLe) {
+                       /*
+                        * By default, the driver expects to free all resources
+                        * associated with this iocb completion.
+                        */
+                       free_saveq = 1;
+                       saveq = list_entry(pring->iocb_continueq.next,
+                                          struct lpfc_iocbq, list);
+                       irsp = &(saveq->iocb);
+                       list_del_init(&pring->iocb_continueq);
+                       pring->iocb_continueq_cnt = 0;
+
+                       psli->slistat.iocbRsp[ringno]++;
+
+                       if(irsp->ulpStatus) {
+                               /* Rsp ring <ringno> error: IOCB */
+                               lpfc_printf_log(phba,
+                                       KERN_WARNING,
+                                       LOG_SLI,
+                                       "%d:0324 Rsp Ring %d error: IOCB Data: "
+                                       "x%x x%x x%x x%x x%x x%x x%x x%x\n",
+                                       phba->brd_no,
+                                       ringno,
+                                       irsp->un.ulpWord[0],
+                                       irsp->un.ulpWord[1],
+                                       irsp->un.ulpWord[2],
+                                       irsp->un.ulpWord[3],
+                                       irsp->un.ulpWord[4],
+                                       irsp->un.ulpWord[5],
+                                       *(((uint32_t *) irsp) + 6),
+                                       *(((uint32_t *) irsp) + 7));
+                       }
+
+                       /* Determine if IOCB command is a solicited or
+                          unsolicited event */
+                       type =
+                           lpfc_sli_iocb_cmd_type[(irsp->
+                                                   ulpCommand &
+                                                   CMD_IOCB_MASK)];
+                       if (type == LPFC_SOL_IOCB) {
+                               spin_unlock_irqrestore(phba->host->host_lock,
+                                                      iflag);
+                               rc = lpfc_sli_process_sol_iocb(phba, pring,
+                                       saveq);
+                               spin_lock_irqsave(phba->host->host_lock, iflag);
+                               /*
+                                * If this solicted completion is an ELS
+                                * command, don't free the resources now because
+                                * the discoverytasklet does later.
+                                */
+                               if (pring->ringno == LPFC_ELS_RING)
+                                       free_saveq = 0;
+                               else
+                                       free_saveq = 1;
+
+                       } else if (type == LPFC_UNSOL_IOCB) {
+                               spin_unlock_irqrestore(phba->host->host_lock,
+                                                      iflag);
+                               rc = lpfc_sli_process_unsol_iocb(phba, pring,
+                                       saveq);
+                               spin_lock_irqsave(phba->host->host_lock, iflag);
+
+                               /*
+                                * If this unsolicted completion is an ELS
+                                * command, don't free the resources now because
+                                * the discoverytasklet does later.
+                                */
+                               if (pring->ringno == LPFC_ELS_RING)
+                                       free_saveq = 0;
+                               else
+                                       free_saveq = 1;
+
+                       } else if (type == LPFC_ABORT_IOCB) {
+                               /* Solicited ABORT Responses */
+                               /* Based on the iotag field, get the cmd IOCB
+                                  from the txcmplq */
+                               if ((irsp->ulpCommand != CMD_XRI_ABORTED_CX) &&
+                                   ((cmdiocbp =
+                                     lpfc_sli_ringtxcmpl_get(phba, pring,
+                                                             saveq, 0)))) {
+                                       /* Call the specified completion
+                                          routine */
+                                       if (cmdiocbp->iocb_cmpl) {
+                                               spin_unlock_irqrestore(
+                                                      phba->host->host_lock,
+                                                      iflag);
+                                               (cmdiocbp->iocb_cmpl) (phba,
+                                                            cmdiocbp, saveq);
+                                               spin_lock_irqsave(
+                                                         phba->host->host_lock,
+                                                         iflag);
+                                       } else {
+                                               mempool_free(cmdiocbp,
+                                                    phba->iocb_mem_pool);
+                                       }
+                               }
+                       } else if (type == LPFC_UNKNOWN_IOCB) {
+                               if (irsp->ulpCommand == CMD_ADAPTER_MSG) {
+
+                                       char adaptermsg[LPFC_MAX_ADPTMSG];
+
+                                       memset(adaptermsg, 0,
+                                              LPFC_MAX_ADPTMSG);
+                                       memcpy(&adaptermsg[0], (uint8_t *) irsp,
+                                              MAX_MSG_DATA);
+                                       dev_warn(&((phba->pcidev)->dev),
+                                                "lpfc%d: %s",
+                                                phba->brd_no, adaptermsg);
+                               } else {
+                                       /* Unknown IOCB command */
+                                       lpfc_printf_log(phba,
+                                               KERN_ERR,
+                                               LOG_SLI,
+                                               "%d:0321 Unknown IOCB command "
+                                               "Data: x%x x%x x%x x%x\n",
+                                               phba->brd_no,
+                                               irsp->ulpCommand,
+                                               irsp->ulpStatus,
+                                               irsp->ulpIoTag,
+                                               irsp->ulpContext);
+                               }
+                       }
+
+                       if (free_saveq) {
+                               /*
+                                * Free up iocb buffer chain for command just
+                                * processed
+                                */
+                               if (!list_empty(&pring->iocb_continueq)) {
+                                       list_for_each_entry_safe(rspiocbp,
+                                                next_iocb,
+                                                &pring->iocb_continueq, list) {
+                                       list_del_init(&rspiocbp->list);
+                                       mempool_free(rspiocbp,
+                                                    phba->iocb_mem_pool);
+                                       }
+                               }
+                       mempool_free( saveq, phba->iocb_mem_pool);
+                       }
+               }
+
+               /* Entire Command has been received */
+               entry = IOCB_ENTRY(pring->rspringaddr, pring->rspidx);
+
+               /* If the port response put pointer has not been updated, sync
+                * the pgp->rspPutInx in the MAILBOX_tand fetch the new port
+                * response put pointer.
+                */
+               if (pring->rspidx == portRspPut) {
+                       portRspPut = le32_to_cpu(pgp->rspPutInx);
+               }
+       }                       /* while (pring->rspidx != portRspPut) */
+
+       if ((rspiocbp != 0) && (mask & HA_R0RE_REQ)) {
+               /* At least one response entry has been freed */
+               psli->slistat.iocbRspFull[ringno]++;
+               /* SET RxRE_RSP in Chip Att register */
+               status = ((CA_R0ATT | CA_R0RE_RSP) << (ringno * 4));
+               writel(status, phba->CAregaddr);
+               readl(phba->CAregaddr); /* flush */
+       }
+       if ((mask & HA_R0CE_RSP) && (pring->flag & LPFC_CALL_RING_AVAILABLE)) {
+               pring->flag &= ~LPFC_CALL_RING_AVAILABLE;
+               psli->slistat.iocbCmdEmpty[ringno]++;
+               /*
+                * Force update of the local copy of cmdGetInx
+                */
+               pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
+               lpfc_sli_resume_iocb(phba, pring);
+               
+               if ((psli->sliinit.ringinit[ringno].lpfc_sli_cmd_available))
+                       (psli->sliinit.ringinit[ringno].
+                        lpfc_sli_cmd_available) (phba, pring);
+
+       }
+
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+       return (rc);
+}
+
+static uint32_t
+lpfc_intr_prep(struct lpfc_hba * phba)
+{
+       uint32_t ha_copy;
+
+       /* Ignore all interrupts during initialization. */
+       if (phba->hba_state < LPFC_LINK_DOWN)
+               return (0);
+
+       /* Read host attention register to determine interrupt source */
+       ha_copy = readl(phba->HAregaddr);
+
+       /* Clear Attention Sources, except ERATT (to preserve status) & LATT
+        *    (ha_copy & ~(HA_ERATT | HA_LATT));
+        */
+       writel((ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr);
+       readl(phba->HAregaddr); /* flush */
+       return (ha_copy);
+}                              /* lpfc_intr_prep */
+
+int
+lpfc_sli_intr(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       uint32_t ha_copy, status;
+       int i;
+
+       psli = &phba->sli;
+       psli->slistat.sliIntr++;
+
+       /*
+        * Call the HBA to see if it is interrupting.  If not, don't claim
+        * the interrupt
+        */
+       ha_copy = lpfc_intr_prep(phba);
+       if (!ha_copy) {
+               return (1);
+       }
+
+       if (ha_copy & HA_ERATT) {
+               /*
+                * There was a link/board error.  Read the status register to
+                * retrieve the error event and process it.
+                */
+               psli->slistat.errAttnEvent++;
+               status = readl(phba->HSregaddr);
+
+               /* Clear Chip error bit */
+               writel(HA_ERATT, phba->HAregaddr);
+               readl(phba->HAregaddr); /* flush */
+
+               lpfc_handle_eratt(phba, status);
+               return (0);
+       }
+
+       if (ha_copy & HA_MBATT) {
+               /* There was a Mailbox event. */
+               lpfc_sli_handle_mb_event(phba);
+       }
+
+       if (ha_copy & HA_LATT) {
+               /*
+                * There was a link attention event.  Provided the driver is in
+                * a state to handle link events, handle this event.
+                */
+               if (psli->sliinit.sli_flag & LPFC_PROCESS_LA) {
+                       lpfc_handle_latt(phba);
+               }
+       }
+
+       /* Process all events on each ring */
+       for (i = 0; i < psli->sliinit.num_rings; i++) {
+               pring = &psli->ring[i];
+               if ((ha_copy & HA_RXATT)
+                   || (pring->flag & LPFC_DEFERRED_RING_EVENT)) {
+                       if (pring->flag & LPFC_STOP_IOCB_MASK) {
+                               pring->flag |= LPFC_DEFERRED_RING_EVENT;
+                       } else {
+                               lpfc_sli_handle_ring_event(phba, pring,
+                                                          (ha_copy &
+                                                           HA_RXMASK));
+                               pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
+                       }
+               }
+               ha_copy = (ha_copy >> 4);
+       }
+
+       return (0);
+}
+
+static int
+lpfc_sli_abort_iocb_ring(struct lpfc_hba * phba, struct lpfc_sli_ring * pring,
+                        uint32_t flag)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       struct lpfc_iocbq *abtsiocbp;
+       IOCB_t *icmd = NULL, *cmd = NULL;
+       int errcnt;
+       uint16_t iotag;
+
+       psli = &phba->sli;
+       errcnt = 0;
+
+       /* Error everything on txq and txcmplq
+        * First do the txq.
+        */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+               list_del_init(&iocb->list);
+               if (iocb->iocb_cmpl) {
+                       icmd = &iocb->iocb;
+                       icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                       icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                       (iocb->iocb_cmpl) (phba, iocb, iocb);
+               } else {
+                       mempool_free( iocb, phba->iocb_mem_pool);
+               }
+       }
+
+       pring->txq_cnt = 0;
+       INIT_LIST_HEAD(&(pring->txq));
+
+       /* Next issue ABTS for everything on the txcmplq */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+               cmd = &iocb->iocb;
+
+               if (flag == LPFC_SLI_ABORT_IMED) {
+                       /*
+                        * Imediate abort of IOCB, clear fast_lookup entry,
+                        * if any, deque and call compl
+                        */
+                       iotag = cmd->ulpIoTag;
+                       if (pring->fast_lookup &&
+                           iotag &&
+                           (iotag <
+                            psli->sliinit.ringinit[pring->ringno].fast_iotag))
+                               *(pring->fast_lookup + iotag) = NULL;
+
+                       list_del_init(&iocb->list);
+                       pring->txcmplq_cnt--;
+
+                       if (iocb->iocb_cmpl) {
+                               cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                               cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                               (iocb->iocb_cmpl) (phba, iocb, iocb);
+                       } else {
+                               mempool_free( iocb, phba->iocb_mem_pool);
+                       }
+                       continue;
+               }
+
+               /* issue ABTS for this IOCB based on iotag */
+
+               if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool,
+                                              GFP_ATOMIC)) == 0) {
+                       errcnt++;
+                       continue;
+               }
+               memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq));
+               icmd = &abtsiocbp->iocb;
+
+               icmd->un.acxri.abortType = ABORT_TYPE_ABTS;
+               icmd->un.acxri.abortContextTag = cmd->ulpContext;
+               icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
+
+               icmd->ulpLe = 1;
+               icmd->ulpClass = cmd->ulpClass;
+               if (phba->hba_state >= LPFC_LINK_UP) {
+                       icmd->ulpCommand = CMD_ABORT_XRI_CN;
+               } else {
+                       icmd->ulpCommand = CMD_CLOSE_XRI_CN;
+
+               }
+
+               if (lpfc_sli_issue_iocb
+                   (phba, pring, abtsiocbp, 0) == IOCB_ERROR) {
+                       mempool_free(abtsiocbp, phba->iocb_mem_pool);
+                       errcnt++;
+                       continue;
+               }
+               /* The rsp ring completion will remove IOCB from txcmplq when
+                * abort is read by HBA.
+                */
+       }
+
+       if (flag == LPFC_SLI_ABORT_IMED) {
+               INIT_LIST_HEAD(&(pring->txcmplq));
+               pring->txcmplq_cnt = 0;
+       }
+
+       return (errcnt);
+}
+
+int
+lpfc_sli_brdreset(struct lpfc_hba * phba)
+{
+       MAILBOX_t *swpmb;
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       uint16_t cfg_value, skip_post;
+       volatile uint32_t word0;
+       int i;
+       void *to_slim;
+       struct lpfc_dmabuf *mp, *next_mp;
+
+       psli = &phba->sli;
+
+       /* A board reset must use REAL SLIM. */
+       psli->sliinit.sli_flag &= ~LPFC_SLI2_ACTIVE;
+
+       word0 = 0;
+       swpmb = (MAILBOX_t *) & word0;
+       swpmb->mbxCommand = MBX_RESTART;
+       swpmb->mbxHc = 1;
+
+       to_slim = phba->MBslimaddr;
+       writel(*(uint32_t *) swpmb, to_slim);
+       readl(to_slim); /* flush */
+
+       /* Only skip post after fc_ffinit is completed */
+       if (phba->hba_state) {
+               skip_post = 1;
+               word0 = 1;      /* This is really setting up word1 */
+       } else {
+               skip_post = 0;
+               word0 = 0;      /* This is really setting up word1 */
+       }
+       to_slim = (uint8_t *) phba->MBslimaddr + sizeof (uint32_t);
+       writel(*(uint32_t *) swpmb, to_slim);
+       readl(to_slim); /* flush */
+
+       /* Reset HBA */
+       lpfc_printf_log(phba,
+               KERN_INFO,
+               LOG_SLI,
+               "%d:0325 Reset HBA Data: x%x x%x\n",
+               phba->brd_no,
+               phba->hba_state,
+               psli->sliinit.sli_flag);
+
+       /* Turn off SERR, PERR in PCI cmd register */
+       phba->hba_state = LPFC_INIT_START;
+
+       /* perform board reset */
+       phba->fc_eventTag = 0;
+       phba->fc_myDID = 0;
+       phba->fc_prevDID = 0;
+
+       /* Turn off parity checking and serr during the physical reset */
+       pci_read_config_word(phba->pcidev, PCI_COMMAND, &cfg_value);
+       pci_write_config_word(phba->pcidev, PCI_COMMAND,
+                             (cfg_value &
+                              ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR)));
+
+       /* Now toggle INITFF bit in the Host Control Register */
+       writel(HC_INITFF, phba->HCregaddr);
+       mdelay(1);
+       readl(phba->HCregaddr); /* flush */
+       writel(0, phba->HCregaddr);
+       readl(phba->HCregaddr); /* flush */
+
+       /* Restore PCI cmd register */
+
+       pci_write_config_word(phba->pcidev, PCI_COMMAND, cfg_value);
+       phba->hba_state = LPFC_INIT_START;
+
+       /* Initialize relevant SLI info */
+       for (i = 0; i < psli->sliinit.num_rings; i++) {
+               pring = &psli->ring[i];
+               pring->flag = 0;
+               pring->rspidx = 0;
+               pring->next_cmdidx  = 0;
+               pring->local_getidx = 0;
+               pring->cmdidx = 0;
+               pring->missbufcnt = 0;
+       }
+
+       if (skip_post) {
+               mdelay(100);
+       } else {
+               mdelay(2000);
+       }
+
+       /* Cleanup preposted buffers on the ELS ring */
+       pring = &psli->ring[LPFC_ELS_RING];
+       list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) {
+               list_del(&mp->list);
+               pring->postbufq_cnt--;
+               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+               kfree(mp);
+       }
+
+       for (i = 0; i < psli->sliinit.num_rings; i++) {
+               pring = &psli->ring[i];
+               lpfc_sli_abort_iocb_ring(phba, pring, LPFC_SLI_ABORT_IMED);
+       }
+
+       return (0);
+}
+
+static void
+lpfc_setup_slim_access(struct lpfc_hba *phba)
+{
+       phba->MBslimaddr = phba->slim_memmap_p;
+       phba->HAregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) +
+               HA_REG_OFFSET;
+       phba->HCregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) +
+               HC_REG_OFFSET;
+       phba->CAregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) +
+               CA_REG_OFFSET;
+       phba->HSregaddr = (uint32_t *) (phba->ctrl_regs_memmap_p) +
+               HS_REG_OFFSET;
+       return;
+}
+
+int
+lpfc_sli_hba_setup(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       LPFC_MBOXQ_t *pmb;
+       int read_rev_reset, i, rc;
+       uint32_t status;
+
+       psli = &phba->sli;
+
+       /* Setep SLI interface for HBA register and HBA SLIM access */
+       lpfc_setup_slim_access(phba);
+
+       /* Set board state to initialization started */
+       phba->hba_state = LPFC_INIT_START;
+       read_rev_reset = 0;
+
+       /* On some platforms/OS's, the driver can't rely on the state the
+        * adapter may be in.  For this reason, the driver is allowed to reset
+        * the HBA before initialization.
+        */
+       if (lpfc_sli_reset_on_init) {
+               phba->hba_state = 0;    /* Don't skip post */
+               lpfc_sli_brdreset(phba);
+               phba->hba_state = LPFC_INIT_START;
+
+               /* Sleep for 2.5 sec */
+               msleep(2500);   
+       }
+
+top:
+       /* Read the HBA Host Status Register */
+       status = readl(phba->HSregaddr);
+
+       /* Check status register to see what current state is */
+       i = 0;
+       while ((status & (HS_FFRDY | HS_MBRDY)) != (HS_FFRDY | HS_MBRDY)) {
+
+               /* Check every 100ms for 5 retries, then every 500ms for 5, then
+                * every 2.5 sec for 5, then reset board and every 2.5 sec for
+                * 4.
+                */
+               if (i++ >= 20) {
+                       /* Adapter failed to init, timeout, status reg
+                          <status> */
+                       lpfc_printf_log(phba,
+                                       KERN_ERR,
+                                       LOG_INIT,
+                                       "%d:0436 Adapter failed to init, "
+                                       "timeout, status reg x%x\n",
+                                       phba->brd_no,
+                                       status);
+                       phba->hba_state = LPFC_HBA_ERROR;
+                       return -ETIMEDOUT;
+               }
+
+               /* Check to see if any errors occurred during init */
+               if (status & HS_FFERM) {
+                       /* ERROR: During chipset initialization */
+                       /* Adapter failed to init, chipset, status reg
+                          <status> */
+                       lpfc_printf_log(phba,
+                                       KERN_ERR,
+                                       LOG_INIT,
+                                       "%d:0437 Adapter failed to init, "
+                                       "chipset, status reg x%x\n",
+                                       phba->brd_no,
+                                       status);
+                       phba->hba_state = LPFC_HBA_ERROR;
+                       return -EIO;
+               }
+
+               if (i <= 5) {
+                       msleep(10);
+               } else if (i <= 10) {
+                       msleep(500);
+               } else {
+                       msleep(2500);
+               }
+
+               if (i == 15) {
+                       phba->hba_state = 0;    /* Don't skip post */
+                       lpfc_sli_brdreset(phba);
+                       phba->hba_state = LPFC_INIT_START;
+               }
+               /* Read the HBA Host Status Register */
+               status = readl(phba->HSregaddr);
+       }
+
+       /* Check to see if any errors occurred during init */
+       if (status & HS_FFERM) {
+               /* ERROR: During chipset initialization */
+               /* Adapter failed to init, chipset, status reg <status> */
+               lpfc_printf_log(phba,
+                               KERN_ERR,
+                               LOG_INIT,
+                               "%d:0438 Adapter failed to init, chipset, "
+                               "status reg x%x\n",
+                               phba->brd_no,
+                               status);
+               phba->hba_state = LPFC_HBA_ERROR;
+               return -EIO;
+       }
+
+       /* Clear all interrupt enable conditions */
+       writel(0, phba->HCregaddr);
+       readl(phba->HCregaddr); /* flush */
+
+       /* setup host attn register */
+       writel(0xffffffff, phba->HAregaddr);
+       readl(phba->HAregaddr); /* flush */
+
+       /* Get a Mailbox buffer to setup mailbox commands for HBA
+          initialization */
+       if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool,
+                                                 GFP_ATOMIC)) == 0) {
+               phba->hba_state = LPFC_HBA_ERROR;
+               return -ENOMEM;
+       }
+
+       /* Call pre CONFIG_PORT mailbox command initialization.  A value of 0
+        * means the call was successful.  Any other nonzero value is a failure,
+        * but if ERESTART is returned, the driver may reset the HBA and try
+        * again.
+        */
+       if ((rc = lpfc_config_port_prep(phba))) {
+               if ((rc == -ERESTART) && (read_rev_reset == 0)) {
+                       mempool_free( pmb, phba->mbox_mem_pool);
+                       phba->hba_state = 0;    /* Don't skip post */
+                       lpfc_sli_brdreset(phba);
+                       phba->hba_state = LPFC_INIT_START;
+                       msleep(500);
+                       read_rev_reset = 1;
+                       goto top;
+               }
+               phba->hba_state = LPFC_HBA_ERROR;
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return -ENXIO;
+       }
+
+       /* Setup and issue mailbox CONFIG_PORT command */
+       phba->hba_state = LPFC_INIT_MBX_CMDS;
+       lpfc_config_port(phba, pmb);
+       if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) {
+               /* Adapter failed to init, mbxCmd <cmd> CONFIG_PORT,
+                  mbxStatus <status> */
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "%d:0442 Adapter failed to init, mbxCmd x%x "
+                               "CONFIG_PORT, mbxStatus x%x Data: x%x\n",
+                               phba->brd_no, pmb->mb.mbxCommand,
+                               pmb->mb.mbxStatus, 0);
+
+               /* This clause gives the config_port call is given multiple
+                  chances to succeed. */
+               if (read_rev_reset == 0) {
+                       mempool_free( pmb, phba->mbox_mem_pool);
+                       phba->hba_state = 0;    /* Don't skip post */
+                       lpfc_sli_brdreset(phba);
+                       phba->hba_state = LPFC_INIT_START;
+                       msleep(2500);
+                       read_rev_reset = 1;
+                       goto top;
+               }
+
+               psli->sliinit.sli_flag &= ~LPFC_SLI2_ACTIVE;
+               phba->hba_state = LPFC_HBA_ERROR;
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return -ENXIO;
+       }
+
+       if ((rc = lpfc_sli_ring_map(phba))) {
+               phba->hba_state = LPFC_HBA_ERROR;
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return -ENXIO;
+       }
+       psli->sliinit.sli_flag |= LPFC_PROCESS_LA;
+
+       /* Call post CONFIG_PORT mailbox command initialization. */
+       if ((rc = lpfc_config_port_post(phba))) {
+               phba->hba_state = LPFC_HBA_ERROR;
+               mempool_free( pmb, phba->mbox_mem_pool);
+               return -ENXIO;
+       }
+       mempool_free( pmb, phba->mbox_mem_pool);
+       return 0;
+}
+
+
+
+
+
+
+
+static void
+lpfc_mbox_abort(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       LPFC_MBOXQ_t *pmbox;
+       MAILBOX_t *mb;
+       struct lpfc_dmabuf *mp;
+
+       psli = &phba->sli;
+
+       if (psli->mbox_active) {
+               del_timer_sync(&psli->mbox_tmo);
+               pmbox = psli->mbox_active;
+               mb = &pmbox->mb;
+               psli->mbox_active = NULL;
+               if (pmbox->mbox_cmpl) {
+                       mb->mbxStatus = MBX_NOT_FINISHED;
+                       (pmbox->mbox_cmpl) (phba, pmbox);
+               } else {
+                       mp = (struct lpfc_dmabuf *) (pmbox->context1);
+                       if (mp) {
+                               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+                               kfree(mp);
+                       }
+                       mempool_free( pmbox, phba->mbox_mem_pool);
+               }
+               psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+       }
+
+       /* Abort all the non active mailbox commands. */
+       pmbox = lpfc_mbox_get(phba);
+       while (pmbox) {
+               mb = &pmbox->mb;
+               if (pmbox->mbox_cmpl) {
+                       mb->mbxStatus = MBX_NOT_FINISHED;
+                       (pmbox->mbox_cmpl) (phba, pmbox);
+               } else {
+                       mp = (struct lpfc_dmabuf *) (pmbox->context1);
+                       if (mp) {
+                               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+                               kfree(mp);
+                       }
+                       mempool_free( pmbox, phba->mbox_mem_pool);
+               }
+               pmbox = lpfc_mbox_get(phba);
+       }
+       return;
+}
+/*! lpfc_mbox_timeout
+ *
+ * \pre
+ * \post
+ * \param hba Pointer to per struct lpfc_hba structure
+ * \param l1  Pointer to the driver's mailbox queue.
+ * \return
+ *   void
+ *
+ * \b Description:
+ *
+ * This routine handles mailbox timeout events at timer interrupt context.
+ */
+void
+lpfc_mbox_timeout(unsigned long ptr)
+{
+       struct lpfc_hba *phba;
+       struct lpfc_sli *psli;
+       LPFC_MBOXQ_t *pmbox;
+       MAILBOX_t *mb;
+       struct lpfc_dmabuf *mp;
+       unsigned long iflag;
+
+       phba = (struct lpfc_hba *)ptr;
+       psli = &phba->sli;
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+
+       pmbox = psli->mbox_active;
+       mb = &pmbox->mb;
+
+       /* Mbox cmd <mbxCommand> timeout */
+       lpfc_printf_log(phba,
+               KERN_ERR,
+               LOG_MBOX | LOG_SLI,
+               "%d:0310 Mailbox command x%x timeout Data: x%x x%x x%p\n",
+               phba->brd_no,
+               mb->mbxCommand,
+               phba->hba_state,
+               psli->sliinit.sli_flag,
+               psli->mbox_active);
+
+       if (psli->mbox_active == pmbox) {
+               psli->mbox_active = NULL;
+               if (pmbox->mbox_cmpl) {
+                       mb->mbxStatus = MBX_NOT_FINISHED;
+                       (pmbox->mbox_cmpl) (phba, pmbox);
+               } else {
+                       mp = (struct lpfc_dmabuf *) (pmbox->context1);
+                       if (mp) {
+                               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+                               kfree(mp);
+                       }
+                       mempool_free( pmbox, phba->mbox_mem_pool);
+               }
+               psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+       }
+
+       lpfc_mbox_abort(phba);
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+       return;
+}
+
+
+int
+lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag)
+{
+       MAILBOX_t *mbox;
+       MAILBOX_t *mb;
+       struct lpfc_sli *psli;
+       uint32_t status, evtctr;
+       uint32_t ha_copy;
+       int i;
+       unsigned long drvr_flag = 0;
+       volatile uint32_t word0, ldata;
+       void *to_slim;
+
+       psli = &phba->sli;
+       if (flag & MBX_POLL) {
+               spin_lock_irqsave(phba->host->host_lock, drvr_flag);
+       }
+
+       mb = &pmbox->mb;
+       status = MBX_SUCCESS;
+
+       if (psli->sliinit.sli_flag & LPFC_SLI_MBOX_ACTIVE) {
+               /* Polling for a mbox command when another one is already active
+                * is not allowed in SLI. Also, the driver must have established
+                * SLI2 mode to queue and process multiple mbox commands.
+                */
+
+               if (flag & MBX_POLL) {
+                       spin_unlock_irqrestore(phba->host->host_lock,
+                                              drvr_flag);
+
+                       /* Mbox command <mbxCommand> cannot issue */
+                       LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag)
+                       return (MBX_NOT_FINISHED);
+               }
+
+               if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) {
+
+                       /* Mbox command <mbxCommand> cannot issue */
+                       LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag)
+                       return (MBX_NOT_FINISHED);
+               }
+
+               /* Handle STOP IOCB processing flag. This is only meaningful
+                * if we are not polling for mbox completion.
+                */
+               if (flag & MBX_STOP_IOCB) {
+                       flag &= ~MBX_STOP_IOCB;
+                       /* Now flag each ring */
+                       for (i = 0; i < psli->sliinit.num_rings; i++) {
+                               /* If the ring is active, flag it */
+                               if (psli->ring[i].cmdringaddr) {
+                                       psli->ring[i].flag |=
+                                           LPFC_STOP_IOCB_MBX;
+                               }
+                       }
+               }
+
+               /* Another mailbox command is still being processed, queue this
+                * command to be processed later.
+                */
+               lpfc_mbox_put(phba, pmbox);
+
+               /* Mbox cmd issue - BUSY */
+               lpfc_printf_log(phba,
+                       KERN_INFO,
+                       LOG_MBOX | LOG_SLI,
+                       "%d:0308 Mbox cmd issue - BUSY Data: x%x x%x x%x x%x\n",
+                       phba->brd_no,
+                       mb->mbxCommand,
+                       phba->hba_state,
+                       psli->sliinit.sli_flag,
+                       flag);
+
+               psli->slistat.mboxBusy++;
+               if (flag == MBX_POLL) {
+                       spin_unlock_irqrestore(phba->host->host_lock,
+                                              drvr_flag);
+               }
+               return (MBX_BUSY);
+       }
+
+       /* Handle STOP IOCB processing flag. This is only meaningful
+        * if we are not polling for mbox completion.
+        */
+       if (flag & MBX_STOP_IOCB) {
+               flag &= ~MBX_STOP_IOCB;
+               if (flag == MBX_NOWAIT) {
+                       /* Now flag each ring */
+                       for (i = 0; i < psli->sliinit.num_rings; i++) {
+                               /* If the ring is active, flag it */
+                               if (psli->ring[i].cmdringaddr) {
+                                       psli->ring[i].flag |=
+                                           LPFC_STOP_IOCB_MBX;
+                               }
+                       }
+               }
+       }
+
+       psli->sliinit.sli_flag |= LPFC_SLI_MBOX_ACTIVE;
+
+       /* If we are not polling, we MUST be in SLI2 mode */
+       if (flag != MBX_POLL) {
+               if (!(psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE)) {
+                       psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+
+                       /* Mbox command <mbxCommand> cannot issue */
+                       LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag);
+                       return (MBX_NOT_FINISHED);
+               }
+               /* timeout active mbox command */
+               mod_timer(&psli->mbox_tmo, jiffies + HZ * LPFC_MBOX_TMO);
+       }
+
+       /* Mailbox cmd <cmd> issue */
+       lpfc_printf_log(phba,
+               KERN_INFO,
+               LOG_MBOX | LOG_SLI,
+               "%d:0309 Mailbox cmd x%x issue Data: x%x x%x x%x\n",
+               phba->brd_no,
+               mb->mbxCommand,
+               phba->hba_state,
+               psli->sliinit.sli_flag,
+               flag);
+
+       psli->slistat.mboxCmd++;
+       evtctr = psli->slistat.mboxEvent;
+
+       /* next set own bit for the adapter and copy over command word */
+       mb->mbxOwner = OWN_CHIP;
+
+       if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) {
+
+               /* First copy command data to host SLIM area */
+               mbox = (MAILBOX_t *) psli->MBhostaddr;
+               lpfc_sli_pcimem_bcopy((uint32_t *) mb, (uint32_t *) mbox,
+                                     (sizeof (uint32_t) *
+                                      (MAILBOX_CMD_WSIZE)));
+
+               pci_dma_sync_single_for_device(phba->pcidev,
+                                              phba->slim2p_mapping,
+                                              sizeof (MAILBOX_t),
+                                              PCI_DMA_TODEVICE);
+       } else {
+               if (mb->mbxCommand == MBX_CONFIG_PORT) {
+                       /* copy command data into host mbox for cmpl */
+                       mbox = (MAILBOX_t *) psli->MBhostaddr;
+                       lpfc_sli_pcimem_bcopy((uint32_t *) mb,
+                                             (uint32_t *) mbox,
+                                             (sizeof (uint32_t) *
+                                              (MAILBOX_CMD_WSIZE)));
+               }
+
+               /* First copy mbox command data to HBA SLIM, skip past first
+                  word */
+               to_slim = (uint8_t *) phba->MBslimaddr + sizeof (uint32_t);
+               lpfc_memcpy_to_slim(to_slim, (void *)&mb->un.varWords[0],
+                           (MAILBOX_CMD_WSIZE - 1) * sizeof (uint32_t));
+
+               /* Next copy over first word, with mbxOwner set */
+               ldata = *((volatile uint32_t *)mb);
+               to_slim = phba->MBslimaddr;
+               writel(ldata, to_slim);
+               readl(to_slim); /* flush */
+
+               if (mb->mbxCommand == MBX_CONFIG_PORT) {
+                       /* switch over to host mailbox */
+                       psli->sliinit.sli_flag |= LPFC_SLI2_ACTIVE;
+               }
+       }
+
+       wmb();
+       /* interrupt board to doit right away */
+       writel(CA_MBATT, phba->CAregaddr);
+       readl(phba->CAregaddr); /* flush */
+
+       switch (flag) {
+       case MBX_NOWAIT:
+               /* Don't wait for it to finish, just return */
+               psli->mbox_active = pmbox;
+               break;
+
+       case MBX_POLL:
+               i = 0;
+               psli->mbox_active = NULL;
+               if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) {
+                       /* First read mbox status word */
+                       mbox = (MAILBOX_t *) psli->MBhostaddr;
+                       word0 = *((volatile uint32_t *)mbox);
+                       word0 = le32_to_cpu(word0);
+               } else {
+                       /* First read mbox status word */
+                       word0 = readl(phba->MBslimaddr);
+               }
+
+               /* Read the HBA Host Attention Register */
+               ha_copy = readl(phba->HAregaddr);
+
+               /* Wait for command to complete */
+               while (((word0 & OWN_CHIP) == OWN_CHIP)
+                      || !(ha_copy & HA_MBATT)) {
+                       if (i++ >= 100) {
+                               psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+                               spin_unlock_irqrestore(phba->host->host_lock,
+                                                      drvr_flag);
+                               return (MBX_NOT_FINISHED);
+                       }
+
+                       /* Check if we took a mbox interrupt while we were
+                          polling */
+                       if (((word0 & OWN_CHIP) != OWN_CHIP)
+                           && (evtctr != psli->slistat.mboxEvent))
+                               break;
+
+                       spin_unlock_irqrestore(phba->host->host_lock,
+                                              drvr_flag);
+
+                       /* Can be in interrupt context, do not sleep */
+                       /* (or might be called with interrupts disabled) */
+                       mdelay(i);
+
+                       spin_lock_irqsave(phba->host->host_lock, drvr_flag);
+
+                       if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) {
+                               /* First copy command data */
+                               mbox = (MAILBOX_t *) psli->MBhostaddr;
+                               word0 = *((volatile uint32_t *)mbox);
+                               word0 = le32_to_cpu(word0);
+                               if (mb->mbxCommand == MBX_CONFIG_PORT) {
+                                       MAILBOX_t *slimmb;
+                                       volatile uint32_t slimword0;
+                                       /* Check real SLIM for any errors */
+                                       slimword0 = readl(phba->MBslimaddr);
+                                       slimmb = (MAILBOX_t *) & slimword0;
+                                       if (((slimword0 & OWN_CHIP) != OWN_CHIP)
+                                           && slimmb->mbxStatus) {
+                                               psli->sliinit.sli_flag &=
+                                                   ~LPFC_SLI2_ACTIVE;
+                                               word0 = slimword0;
+                                       }
+                               }
+                       } else {
+                               /* First copy command data */
+                               word0 = readl(phba->MBslimaddr);
+                       }
+                       /* Read the HBA Host Attention Register */
+                       ha_copy = readl(phba->HAregaddr);
+               }
+
+               if (psli->sliinit.sli_flag & LPFC_SLI2_ACTIVE) {
+                       /* First copy command data */
+                       mbox = (MAILBOX_t *) psli->MBhostaddr;
+                       /* copy results back to user */
+                       lpfc_sli_pcimem_bcopy((uint32_t *) mbox,
+                                             (uint32_t *) mb,
+                                             (sizeof (uint32_t) *
+                                              MAILBOX_CMD_WSIZE));
+               } else {
+                       /* First copy command data */
+                       lpfc_memcpy_from_slim((void *)mb,
+                                     phba->MBslimaddr,
+                                     sizeof (uint32_t) * (MAILBOX_CMD_WSIZE));
+               }
+
+               writel(HA_MBATT, phba->HAregaddr);
+               readl(phba->HAregaddr); /* flush */
+
+               psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+               status = mb->mbxStatus;
+       }
+
+       if (flag == MBX_POLL) {
+               spin_unlock_irqrestore(phba->host->host_lock, drvr_flag);
+       }
+       return (status);
+}
+
+static int
+lpfc_sli_ringtx_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring,
+                   struct lpfc_iocbq * piocb)
+{
+       /* Insert the caller's iocb in the txq tail for later processing. */
+       list_add_tail(&piocb->list, &pring->txq);
+       pring->txq_cnt++;
+       return (0);
+}
+
+static struct lpfc_iocbq *
+lpfc_sli_next_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, 
+                  struct lpfc_iocbq ** piocb)
+{
+       struct lpfc_iocbq * nextiocb;
+
+       nextiocb = lpfc_sli_ringtx_get(phba, pring);
+       if (!nextiocb) {
+               nextiocb = *piocb;
+               *piocb = NULL;
+       }
+
+       return nextiocb;
+}
+
+int
+lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+                   struct lpfc_iocbq *piocb, uint32_t flag)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       int ringno = pring->ringno;
+       struct lpfc_iocbq *nextiocb;
+       IOCB_t *iocb;
+
+       /*
+        * We should never get an IOCB if we are in a < LINK_DOWN state
+        */
+       if (unlikely(phba->hba_state < LPFC_LINK_DOWN))
+               return IOCB_ERROR;
+
+       /*
+        * Check to see if we are blocking IOCB processing because of a
+        * outstanding mbox command.
+        */
+       if (unlikely(pring->flag & LPFC_STOP_IOCB_MBX))
+               goto iocb_busy;
+
+       if (unlikely(phba->hba_state == LPFC_LINK_DOWN)) {
+               /*
+                * Only CREATE_XRI, CLOSE_XRI, ABORT_XRI, and QUE_RING_BUF
+                * can be issued if the link is not up.
+                */
+               switch (piocb->iocb.ulpCommand) {
+               case CMD_QUE_RING_BUF_CN:
+               case CMD_QUE_RING_BUF64_CN:
+               case CMD_CLOSE_XRI_CN:
+               case CMD_ABORT_XRI_CN:
+                       /*
+                        * For IOCBs, like QUE_RING_BUF, that have no rsp ring
+                        * completion, iocb_cmpl MUST be 0.
+                        */
+                       if (piocb->iocb_cmpl)
+                               piocb->iocb_cmpl = NULL;
+                       /*FALLTHROUGH*/
+               case CMD_CREATE_XRI_CR:
+                       break;
+               default:
+                       goto iocb_busy;
+               }
+
+       /*
+        * For FCP commands, we must be in a state where we can process link
+        * attention events.
+        */
+       } else if (unlikely(pring->ringno == psli->fcp_ring &&
+                  !(psli->sliinit.sli_flag & LPFC_PROCESS_LA)))
+               goto iocb_busy;
+
+       /*
+        * Check to see if this is a high priority command.
+        * If so bypass tx queue processing.
+        */
+       if (unlikely((flag & SLI_IOCB_HIGH_PRIORITY) &&
+                    (iocb = lpfc_sli_next_iocb_slot(phba, pring)))) {
+               lpfc_sli_submit_iocb(phba, pring, iocb, piocb);
+               piocb = NULL;
+       }
+
+       while ((iocb = lpfc_sli_next_iocb_slot(phba, pring)) &&
+              (nextiocb = lpfc_sli_next_iocb(phba, pring, &piocb)))
+               lpfc_sli_submit_iocb(phba, pring, iocb, nextiocb);
+       
+       if (iocb)
+               lpfc_sli_update_ring(phba, pring);
+       else
+               lpfc_sli_update_full_ring(phba, pring);
+
+       if (!piocb)
+               return IOCB_SUCCESS;
+
+       goto out_busy;
+
+ iocb_busy:
+       psli->slistat.iocbCmdDelay[ringno]++;
+
+ out_busy:
+
+       if (!(flag & SLI_IOCB_RET_IOCB)) {
+               lpfc_sli_ringtx_put(phba, pring, piocb);
+               return IOCB_SUCCESS;
+       }
+
+       return IOCB_BUSY;
+}
+
+int
+lpfc_sli_queue_setup(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       int i, cnt;
+
+       psli = &phba->sli;
+       INIT_LIST_HEAD(&psli->mboxq);
+       /* Initialize list headers for txq and txcmplq as double linked lists */
+       for (i = 0; i < psli->sliinit.num_rings; i++) {
+               pring = &psli->ring[i];
+               pring->ringno = i;
+               pring->next_cmdidx  = 0;
+               pring->local_getidx = 0;
+               pring->cmdidx = 0;
+               INIT_LIST_HEAD(&pring->txq);
+               INIT_LIST_HEAD(&pring->txcmplq);
+               INIT_LIST_HEAD(&pring->iocb_continueq);
+               INIT_LIST_HEAD(&pring->postbufq);
+               cnt = psli->sliinit.ringinit[i].fast_iotag;
+               if (cnt) {
+                       pring->fast_lookup =
+                               kmalloc(cnt * sizeof (struct lpfc_iocbq *),
+                                       GFP_KERNEL);
+                       if (pring->fast_lookup == 0) {
+                               return (0);
+                       }
+                       memset((char *)pring->fast_lookup, 0,
+                              cnt * sizeof (struct lpfc_iocbq *));
+               }
+       }
+       return (1);
+}
+
+int
+lpfc_sli_hba_down(struct lpfc_hba * phba)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_sli_ring *pring;
+       LPFC_MBOXQ_t *pmb;
+       struct lpfc_dmabuf *mp;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       IOCB_t *icmd = NULL;
+       int i;
+
+       psli = &phba->sli;
+       lpfc_hba_down_prep(phba);
+
+       for (i = 0; i < psli->sliinit.num_rings; i++) {
+               pring = &psli->ring[i];
+               pring->flag |= LPFC_DEFERRED_RING_EVENT;
+
+               /*
+                * Error everything on the txq since these iocbs have not been
+                * given to the FW yet. 
+                */
+               pring->txq_cnt = 0;
+
+               list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+                       list_del_init(&iocb->list);
+                       if (iocb->iocb_cmpl) {
+                               icmd = &iocb->iocb;
+                               icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                               icmd->un.ulpWord[4] = IOERR_SLI_DOWN;
+                               (iocb->iocb_cmpl) (phba, iocb, iocb);
+                       } else {
+                               mempool_free( iocb, phba->iocb_mem_pool);
+                       }
+               }
+
+               INIT_LIST_HEAD(&(pring->txq));
+
+               if (pring->fast_lookup) {
+                       kfree(pring->fast_lookup);
+                       pring->fast_lookup = NULL;
+               }
+
+       }
+
+       /* Return any active mbox cmds */
+       del_timer_sync(&psli->mbox_tmo);
+       if ((psli->mbox_active)) {
+                       pmb = psli->mbox_active;
+                       mp = (struct lpfc_dmabuf *) (pmb->context1);
+                       if (mp) {
+                               lpfc_mbuf_free(phba, mp->virt, mp->phys);
+                               kfree(mp);
+                       }
+               mempool_free(psli->mbox_active, phba->mbox_mem_pool);
+       }
+       psli->sliinit.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
+       psli->mbox_active = NULL;
+
+       /* Return any pending mbox cmds */
+       while ((pmb = lpfc_mbox_get(phba)) != NULL) {
+               mp = (struct lpfc_dmabuf *) (pmb->context1);
+               if (mp) {
+                       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+                       kfree(mp);
+               }
+               mempool_free(pmb, phba->mbox_mem_pool);
+       }
+
+       INIT_LIST_HEAD(&psli->mboxq);
+
+       /*
+        * Provided the hba is not in an error state, reset it.  It is not
+        * capable of IO anymore.
+        */
+       if (phba->hba_state != LPFC_HBA_ERROR) {
+               phba->hba_state = LPFC_INIT_START;
+               lpfc_sli_brdreset(phba);
+       }
+
+       return 1;
+}
+
+void
+lpfc_sli_pcimem_bcopy(uint32_t * src, uint32_t * dest, uint32_t cnt)
+{
+       uint32_t ldata;
+       int i;
+
+       for (i = 0; i < (int)cnt; i += sizeof (uint32_t)) {
+               ldata = *src++;
+               ldata = le32_to_cpu(ldata);
+               *dest++ = ldata;
+       }
+}
+
+int
+lpfc_sli_ringpostbuf_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring,
+                        struct lpfc_dmabuf * mp)
+{
+       /* Stick struct lpfc_dmabuf at end of postbufq so driver can look it up
+          later */
+       list_add_tail(&mp->list, &pring->postbufq);
+
+       pring->postbufq_cnt++;
+       return 0;
+}
+
+
+struct lpfc_dmabuf *
+lpfc_sli_ringpostbuf_get(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+                        dma_addr_t phys)
+{
+       struct lpfc_dmabuf *mp, *next_mp;
+       struct list_head *slp = &pring->postbufq;
+
+       /* Search postbufq, from the begining, looking for a match on phys */
+       list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) {
+               if (mp->phys == phys) {
+                       list_del_init(&mp->list);
+                       pring->postbufq_cnt--;
+                       pci_dma_sync_single_for_cpu(phba->pcidev, mp->phys,
+                                       LPFC_BPL_SIZE, PCI_DMA_FROMDEVICE);
+                       return mp;
+               }
+       }
+
+       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "%d:0410 Cannot find virtual addr for mapped buf on "
+                       "ring %d Data x%llx x%p x%p x%x\n",
+                       phba->brd_no, pring->ringno, (unsigned long long)phys,
+                       slp->next, slp->prev, pring->postbufq_cnt);
+       return NULL;
+}
+
+uint32_t
+lpfc_sli_next_iotag(struct lpfc_hba * phba, struct lpfc_sli_ring * pring)
+{
+       LPFC_RING_INIT_t *pringinit;
+       struct lpfc_sli *psli;
+       uint32_t search_start;
+
+       psli = &phba->sli;
+       pringinit = &psli->sliinit.ringinit[pring->ringno];
+
+       if (pring->fast_lookup == NULL) {
+               pringinit->iotag_ctr++;
+               if (pringinit->iotag_ctr >= pringinit->iotag_max)
+                       pringinit->iotag_ctr = 1;
+               return pringinit->iotag_ctr;
+       }
+
+       search_start = pringinit->iotag_ctr;
+
+       do {
+               pringinit->iotag_ctr++;
+               if (pringinit->iotag_ctr >= pringinit->fast_iotag)
+                       pringinit->iotag_ctr = 1;
+
+               if(*(pring->fast_lookup + pringinit->iotag_ctr) == NULL)
+                       return pringinit->iotag_ctr;
+
+       } while (pringinit->iotag_ctr != search_start);
+
+       /*
+        * Outstanding I/O count for ring <ringno> is at max <fast_iotag>
+        */
+       lpfc_printf_log(phba,
+               KERN_ERR,
+               LOG_SLI,
+               "%d:0318 Outstanding I/O count for ring %d is at max x%x\n",
+               phba->brd_no,
+               pring->ringno,
+               psli->sliinit.ringinit[pring->ringno].fast_iotag);
+       return (0);
+}
+
+static void
+lpfc_sli_abort_elsreq_cmpl(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb,
+                          struct lpfc_iocbq * rspiocb)
+{
+       struct lpfc_dmabuf *buf_ptr, *buf_ptr1;
+       /* Free the resources associated with the ELS_REQUEST64 IOCB the driver
+        * just aborted.
+        * In this case, context2  = cmd,  context2->next = rsp, context3 = bpl
+        */
+       if (cmdiocb->context2) {
+               buf_ptr1 = (struct lpfc_dmabuf *) cmdiocb->context2;
+
+               /* Free the response IOCB before completing the abort
+                  command.  */
+               if (!list_empty(&buf_ptr1->list)) {
+
+                       buf_ptr = list_entry(buf_ptr1->list.next,
+                                            struct lpfc_dmabuf, list);
+
+                       list_del(&buf_ptr->list);
+                       lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
+                       kfree(buf_ptr);
+               }
+               lpfc_mbuf_free(phba, buf_ptr1->virt, buf_ptr1->phys);
+               kfree(buf_ptr1);
+       }
+
+       if (cmdiocb->context3) {
+               buf_ptr = (struct lpfc_dmabuf *) cmdiocb->context3;
+               lpfc_mbuf_free(phba, buf_ptr->virt, buf_ptr->phys);
+               kfree(buf_ptr);
+       }
+       mempool_free( cmdiocb, phba->iocb_mem_pool);
+       return;
+}
+
+int
+lpfc_sli_issue_abort_iotag32(struct lpfc_hba * phba,
+                            struct lpfc_sli_ring * pring,
+                            struct lpfc_iocbq * cmdiocb)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_iocbq *abtsiocbp;
+       IOCB_t *icmd = NULL;
+       IOCB_t *iabt = NULL;
+       uint32_t iotag32;
+
+       psli = &phba->sli;
+
+       /* issue ABTS for this IOCB based on iotag */
+       if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool, GFP_ATOMIC)) == 0) {
+               return (0);
+       }
+       memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq));
+       iabt = &abtsiocbp->iocb;
+
+       icmd = &cmdiocb->iocb;
+       switch (icmd->ulpCommand) {
+       case CMD_ELS_REQUEST64_CR:
+               iotag32 = icmd->un.elsreq64.bdl.ulpIoTag32;
+               /* Even though we abort the ELS command, the firmware may access
+                * the BPL or other resources before it processes our
+                * ABORT_MXRI64. Thus we must delay reusing the cmdiocb
+                * resources till the actual abort request completes.
+                */
+               abtsiocbp->context1 = (void *)((unsigned long)icmd->ulpCommand);
+               abtsiocbp->context2 = cmdiocb->context2;
+               abtsiocbp->context3 = cmdiocb->context3;
+               cmdiocb->context2 = NULL;
+               cmdiocb->context3 = NULL;
+               abtsiocbp->iocb_cmpl = lpfc_sli_abort_elsreq_cmpl;
+               break;
+       default:
+               mempool_free( abtsiocbp, phba->iocb_mem_pool);
+               return (0);
+       }
+
+       iabt->un.amxri.abortType = ABORT_TYPE_ABTS;
+       iabt->un.amxri.iotag32 = iotag32;
+
+       iabt->ulpLe = 1;
+       iabt->ulpClass = CLASS3;
+       iabt->ulpCommand = CMD_ABORT_MXRI64_CN;
+
+       if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) == IOCB_ERROR) {
+               mempool_free( abtsiocbp, phba->iocb_mem_pool);
+               return (0);
+       }
+
+       return (1);
+}
+
+int
+lpfc_sli_abort_iocb_ctx(struct lpfc_hba * phba, struct lpfc_sli_ring * pring,
+                       uint32_t ctx)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       struct lpfc_iocbq *abtsiocbp;
+       IOCB_t *icmd = NULL, *cmd = NULL;
+       int errcnt;
+
+       psli = &phba->sli;
+       errcnt = 0;
+
+       /* Error matching iocb on txq or txcmplq
+        * First check the txq.
+        */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+               cmd = &iocb->iocb;
+               if (cmd->ulpContext != ctx) {
+                       continue;
+               }
+
+               list_del_init(&iocb->list);
+               pring->txq_cnt--;
+               if (iocb->iocb_cmpl) {
+                       icmd = &iocb->iocb;
+                       icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                       icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                       (iocb->iocb_cmpl) (phba, iocb, iocb);
+               } else {
+                       mempool_free( iocb, phba->iocb_mem_pool);
+               }
+       }
+
+       /* Next check the txcmplq */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+               cmd = &iocb->iocb;
+               if (cmd->ulpContext != ctx) {
+                       continue;
+               }
+
+               /* issue ABTS for this IOCB based on iotag */
+               if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool,
+                                              GFP_ATOMIC)) == 0) {
+                       errcnt++;
+                       continue;
+               }
+               memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq));
+               icmd = &abtsiocbp->iocb;
+
+               icmd->un.acxri.abortType = ABORT_TYPE_ABTS;
+               icmd->un.acxri.abortContextTag = cmd->ulpContext;
+               icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
+
+               icmd->ulpLe = 1;
+               icmd->ulpClass = cmd->ulpClass;
+               if (phba->hba_state >= LPFC_LINK_UP) {
+                       icmd->ulpCommand = CMD_ABORT_XRI_CN;
+               } else {
+                       icmd->ulpCommand = CMD_CLOSE_XRI_CN;
+               }
+
+               if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) ==
+                                                               IOCB_ERROR) {
+                       mempool_free( abtsiocbp, phba->iocb_mem_pool);
+                       errcnt++;
+                       continue;
+               }
+               /* The rsp ring completion will remove IOCB from txcmplq when
+                * abort is read by HBA.
+                */
+       }
+       return (errcnt);
+}
+
+int
+lpfc_sli_sum_iocb_host(struct lpfc_hba * phba,
+                       struct lpfc_sli_ring * pring)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       IOCB_t *cmd = NULL;
+       struct lpfc_scsi_buf *lpfc_cmd;
+       int sum;
+
+       psli = &phba->sli;
+       sum = 0;
+
+       /* Error matching iocb on txq or txcmplq
+        * First check the txq.
+        */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+               cmd = &iocb->iocb;
+
+               /* Must be a FCP command */
+               if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                   (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                   (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                       continue;
+               }
+
+               /* context1 MUST be a struct lpfc_scsi_buf */
+               lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+               if (lpfc_cmd == 0) {
+                       continue;
+               }
+               sum++;
+       }
+
+       /* Next check the txcmplq */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+               cmd = &iocb->iocb;
+
+               /* Must be a FCP command */
+               if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                   (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                   (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                       continue;
+               }
+
+               /* context1 MUST be a struct lpfc_scsi_buf */
+               lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+               if (lpfc_cmd == 0) {
+                       continue;
+               }
+               sum++;
+       }
+       return (sum);
+}
+
+int
+lpfc_sli_abort_iocb_host(struct lpfc_hba * phba,
+                       struct lpfc_sli_ring * pring, int flag)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       struct lpfc_iocbq *abtsiocbp;
+       IOCB_t *icmd = NULL, *cmd = NULL;
+       struct lpfc_scsi_buf *lpfc_cmd;
+       int errcnt;
+
+       psli = &phba->sli;
+       errcnt = 0;
+
+       /* Error matching iocb on txq or txcmplq
+        * First check the txq.
+        */
+       if(flag & LPFC_ABORT_TXQ) {
+               list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+                       cmd = &iocb->iocb;
+
+                       /* Must be a FCP command */
+                       if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                       (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                       (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                               continue;
+                       }
+
+                       /* context1 MUST be a struct lpfc_scsi_buf */
+                       lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+                       if (lpfc_cmd == 0) {
+                               continue;
+                       }
+
+                       list_del_init(&iocb->list);
+                       pring->txq_cnt--;
+                       if (iocb->iocb_cmpl) {
+                               icmd = &iocb->iocb;
+                               icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                               icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                               (iocb->iocb_cmpl) (phba, iocb, iocb);
+                       } else {
+                               mempool_free( iocb, phba->iocb_mem_pool);
+                       }
+               }
+       }
+
+       if(flag & LPFC_ABORT_TXCMPLQ) {
+               /* Next check the txcmplq */
+               list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq,
+                                        list) {
+                       cmd = &iocb->iocb;
+
+                       /* Must be a FCP command */
+                       if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                           (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                           (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                               continue;
+                       }
+
+                       /* context1 MUST be a struct lpfc_scsi_buf */
+                       lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+                       if (lpfc_cmd == 0) {
+                               continue;
+                       }
+
+                       /* issue ABTS for this IOCB based on iotag */
+                       if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool,
+                                                      GFP_ATOMIC)) == 0) {
+                               errcnt++;
+                               continue;
+                       }
+                       memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq));
+                       icmd = &abtsiocbp->iocb;
+
+                       icmd->un.acxri.abortType = ABORT_TYPE_ABTS;
+                       icmd->un.acxri.abortContextTag = cmd->ulpContext;
+                       icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
+
+                       icmd->ulpLe = 1;
+                       icmd->ulpClass = cmd->ulpClass;
+                       if (phba->hba_state >= LPFC_LINK_UP) {
+                               icmd->ulpCommand = CMD_ABORT_XRI_CN;
+                       } else {
+                               icmd->ulpCommand = CMD_CLOSE_XRI_CN;
+                       }
+
+                       if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) ==
+                                                               IOCB_ERROR) {
+                               mempool_free( abtsiocbp, phba->iocb_mem_pool);
+                               errcnt++;
+                               continue;
+                       }
+                       /* The rsp ring completion will remove IOCB from
+                        * tacmplq when abort is read by HBA.
+                        */
+               }
+       }
+       return (errcnt);
+}
+
+int
+lpfc_sli_sum_iocb_lun(struct lpfc_hba * phba,
+                       struct lpfc_sli_ring * pring,
+                       uint16_t scsi_target, uint64_t scsi_lun)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       IOCB_t *cmd = NULL;
+       struct lpfc_scsi_buf *lpfc_cmd;
+       int sum;
+
+       psli = &phba->sli;
+       sum = 0;
+
+       /* Error matching iocb on txq or txcmplq
+        * First check the txq.
+        */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+               cmd = &iocb->iocb;
+
+               /* Must be a FCP command */
+               if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                   (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                   (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                       continue;
+               }
+
+               /* context1 MUST be a struct lpfc_scsi_buf */
+               lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+               if ((lpfc_cmd == 0) ||
+                   (lpfc_cmd->pCmd->device->id != scsi_target) ||
+                   (lpfc_cmd->pCmd->device->lun != scsi_lun)) {
+                       continue;
+               }
+               sum++;
+       }
+
+       /* Next check the txcmplq */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+               cmd = &iocb->iocb;
+
+               /* Must be a FCP command */
+               if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                   (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                   (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                       continue;
+               }
+
+               /* context1 MUST be a struct lpfc_scsi_buf */
+               lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+               if ((lpfc_cmd == 0) ||
+                   (lpfc_cmd->pCmd->device->id != scsi_target) ||
+                   (lpfc_cmd->pCmd->device->lun != scsi_lun)) {
+                       continue;
+               }
+
+               sum++;
+       }
+       return (sum);
+}
+
+int
+lpfc_sli_abort_iocb_lun(struct lpfc_hba * phba,
+                       struct lpfc_sli_ring * pring,
+                       uint16_t scsi_target, uint64_t scsi_lun, int flag)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       struct lpfc_iocbq *abtsiocbp;
+       IOCB_t *icmd = NULL, *cmd = NULL;
+       struct lpfc_scsi_buf *lpfc_cmd;
+       int errcnt;
+
+       psli = &phba->sli;
+       errcnt = 0;
+
+       /* Error matching iocb on txq or txcmplq
+        * First check the txq.
+        */
+       if(flag & LPFC_ABORT_TXQ) {
+               list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+                       cmd = &iocb->iocb;
+
+                       /* Must be a FCP command */
+                       if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                       (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                       (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                               continue;
+                       }
+
+                       /* context1 MUST be a struct lpfc_scsi_buf */
+                       lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+                       if ((lpfc_cmd == 0) ||
+                           (lpfc_cmd->pCmd->device->id != scsi_target) ||
+                           (lpfc_cmd->pCmd->device->lun != scsi_lun)) {
+                               continue;
+                       }
+
+                       list_del_init(&iocb->list);
+                       pring->txq_cnt--;
+                       if (iocb->iocb_cmpl) {
+                               icmd = &iocb->iocb;
+                               icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                               icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                               (iocb->iocb_cmpl) (phba, iocb, iocb);
+                       } else {
+                               mempool_free( iocb, phba->iocb_mem_pool);
+                       }
+               }
+       }
+
+       if(flag & LPFC_ABORT_TXCMPLQ) {
+               /* Next check the txcmplq */
+               list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq,
+                                        list) {
+                       cmd = &iocb->iocb;
+
+                       /* Must be a FCP command */
+                       if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                           (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                           (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                               continue;
+                       }
+
+                       /* context1 MUST be a struct lpfc_scsi_buf */
+                       lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+                       if ((lpfc_cmd == 0) ||
+                           (lpfc_cmd->pCmd->device->id != scsi_target) ||
+                           (lpfc_cmd->pCmd->device->lun != scsi_lun)) {
+                               continue;
+                       }
+
+                       /* issue ABTS for this IOCB based on iotag */
+                       if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool,
+                                                      GFP_ATOMIC)) == 0) {
+                               errcnt++;
+                               continue;
+                       }
+                       memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq));
+                       icmd = &abtsiocbp->iocb;
+
+                       icmd->un.acxri.abortType = ABORT_TYPE_ABTS;
+                       icmd->un.acxri.abortContextTag = cmd->ulpContext;
+                       icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
+
+                       icmd->ulpLe = 1;
+                       icmd->ulpClass = cmd->ulpClass;
+                       if (phba->hba_state >= LPFC_LINK_UP) {
+                               icmd->ulpCommand = CMD_ABORT_XRI_CN;
+                       } else {
+                               icmd->ulpCommand = CMD_CLOSE_XRI_CN;
+                       }
+
+                       if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) ==
+                                                               IOCB_ERROR) {
+                               mempool_free( abtsiocbp, phba->iocb_mem_pool);
+                               errcnt++;
+                               continue;
+                       }
+                       /* The rsp ring completion will remove IOCB from
+                        * tacmplq when abort is read by HBA.
+                        */
+               }
+       }
+       return (errcnt);
+}
+
+int
+lpfc_sli_abort_iocb_tgt(struct lpfc_hba * phba,
+                       struct lpfc_sli_ring * pring,
+                       uint16_t scsi_target, int flag)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_iocbq *iocb, *next_iocb;
+       struct lpfc_iocbq *abtsiocbp;
+       IOCB_t *icmd = NULL, *cmd = NULL;
+       struct lpfc_scsi_buf *lpfc_cmd;
+       int errcnt;
+
+       psli = &phba->sli;
+       errcnt = 0;
+
+       /* Error matching iocb on txq or txcmplq
+        * First check the txq.
+        */
+       if(flag & LPFC_ABORT_TXQ) {
+               list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+                       cmd = &iocb->iocb;
+
+                       /* Must be a FCP command */
+                       if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                       (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                       (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                               continue;
+                       }
+
+                       /* context1 MUST be a struct lpfc_scsi_buf */
+                       lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+                       if ((lpfc_cmd == 0)
+                           || (lpfc_cmd->pCmd->device->id != scsi_target)) {
+                               continue;
+                       }
+
+                       list_del_init(&iocb->list);
+                       pring->txq_cnt--;
+                       if (iocb->iocb_cmpl) {
+                               icmd = &iocb->iocb;
+                               icmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                               icmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                               (iocb->iocb_cmpl) (phba, iocb, iocb);
+                       } else {
+                               mempool_free( iocb, phba->iocb_mem_pool);
+                       }
+               }
+       }
+
+       if(flag & LPFC_ABORT_TXCMPLQ) {
+               /* Next check the txcmplq */
+               list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq,
+                                        list) {
+                       cmd = &iocb->iocb;
+
+                       /* Must be a FCP command */
+                       if ((cmd->ulpCommand != CMD_FCP_ICMND64_CR) &&
+                           (cmd->ulpCommand != CMD_FCP_IWRITE64_CR) &&
+                           (cmd->ulpCommand != CMD_FCP_IREAD64_CR)) {
+                               continue;
+                       }
+
+                       /* context1 MUST be a struct lpfc_scsi_buf */
+                       lpfc_cmd = (struct lpfc_scsi_buf *) (iocb->context1);
+                       if ((lpfc_cmd == 0)
+                           || (lpfc_cmd->pCmd->device->id != scsi_target)) {
+                               continue;
+                       }
+
+                       /* issue ABTS for this IOCB based on iotag */
+                       if ((abtsiocbp = mempool_alloc(phba->iocb_mem_pool,
+                               GFP_ATOMIC)) == 0) {
+                               errcnt++;
+                               continue;
+                       }
+                       memset(abtsiocbp, 0, sizeof (struct lpfc_iocbq));
+                       icmd = &abtsiocbp->iocb;
+
+                       icmd->un.acxri.abortType = ABORT_TYPE_ABTS;
+                       icmd->un.acxri.abortContextTag = cmd->ulpContext;
+                       icmd->un.acxri.abortIoTag = cmd->ulpIoTag;
+
+                       icmd->ulpLe = 1;
+                       icmd->ulpClass = cmd->ulpClass;
+                       if (phba->hba_state >= LPFC_LINK_UP) {
+                               icmd->ulpCommand = CMD_ABORT_XRI_CN;
+                       } else {
+                               icmd->ulpCommand = CMD_CLOSE_XRI_CN;
+                       }
+
+                       if (lpfc_sli_issue_iocb(phba, pring, abtsiocbp, 0) ==
+                                                               IOCB_ERROR) {
+                               mempool_free( abtsiocbp, phba->iocb_mem_pool);
+                               errcnt++;
+                               continue;
+                       }
+                       /* The rsp ring completion will remove IOCB from
+                        * txcmplq when abort is read by HBA.
+                        */
+               }
+       }
+       return (errcnt);
+}
+
+
+
+void
+lpfc_sli_wake_iocb_high_priority(struct lpfc_hba * phba,
+                                struct lpfc_iocbq * queue1,
+                                struct lpfc_iocbq * queue2)
+{
+       if (queue1->context2 && queue2)
+               memcpy(queue1->context2, queue2, sizeof (struct lpfc_iocbq));
+
+       /* The waiter is looking for LPFC_IO_HIPRI bit to be set
+          as a signal to wake up */
+       queue1->iocb_flag |= LPFC_IO_HIPRI;
+       return;
+}
+
+int
+lpfc_sli_issue_iocb_wait_high_priority(struct lpfc_hba * phba,
+                                      struct lpfc_sli_ring * pring,
+                                      struct lpfc_iocbq * piocb,
+                                      uint32_t flag,
+                                      struct lpfc_iocbq * prspiocbq,
+                                      uint32_t timeout)
+{
+       int j, delay_time,  retval = IOCB_ERROR;
+
+       /* The caller must left context1 empty.  */
+       if (piocb->context_un.hipri_wait_queue != 0) {
+               return IOCB_ERROR;
+       }
+
+       /*
+        * If the caller has provided a response iocbq buffer, context2 must
+        * be NULL or its an error.
+        */
+       if (prspiocbq && piocb->context2) {
+               return IOCB_ERROR;
+       }
+
+       piocb->context2 = prspiocbq;
+
+       /* Setup callback routine and issue the command. */
+       piocb->iocb_cmpl = lpfc_sli_wake_iocb_high_priority;
+       retval = lpfc_sli_issue_iocb(phba, pring, piocb,
+                                       flag | SLI_IOCB_HIGH_PRIORITY);
+       if (retval != IOCB_SUCCESS) {
+               piocb->context2 = NULL;
+               return IOCB_ERROR;
+       }
+
+       /*
+        * This high-priority iocb was sent out-of-band.  Poll for its
+        * completion rather than wait for a signal.  Note that the host_lock
+        * is held by the midlayer and must be released here to allow the
+        * interrupt handlers to complete the IO and signal this routine via
+        * the iocb_flag.
+        * Also, the delay_time is computed to be one second longer than
+        * the scsi command timeout to give the FW time to abort on
+        * timeout rather than the driver just giving up.  Typically,
+        * the midlayer does not specify a time for this command so the
+        * driver is free to enforce its own timeout.
+        */
+
+       delay_time = ((timeout + 1) * 1000) >> 6;
+       retval = IOCB_ERROR;
+       spin_unlock_irq(phba->host->host_lock);
+       for (j = 0; j < 64; j++) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6)
+               mdelay(delay_time);
+#else
+               msleep(delay_time);
+#endif
+               if (piocb->iocb_flag & LPFC_IO_HIPRI) {
+                       piocb->iocb_flag &= ~LPFC_IO_HIPRI;
+                       retval = IOCB_SUCCESS;
+                       break;
+               }
+       }
+
+       spin_lock_irq(phba->host->host_lock);
+       piocb->context2 = NULL;
+       return retval;
+}
+static void
+lpfc_sli_wake_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
+{
+       wait_queue_head_t *pdone_q;
+
+       /*
+        * If pdone_q is empty, the driver thread gave up waiting and
+        * continued running.
+        */
+       pdone_q = (wait_queue_head_t *) pmboxq->context1;
+       if (pdone_q)
+               wake_up_interruptible(pdone_q);
+       return;
+}
+
+int
+lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq,
+                        uint32_t timeout)
+{
+       DECLARE_WAIT_QUEUE_HEAD(done_q);
+       DECLARE_WAITQUEUE(wq_entry, current);
+       uint32_t timeleft = 0;
+       int retval;
+
+       /* The caller must leave context1 empty. */
+       if (pmboxq->context1 != 0) {
+               return (MBX_NOT_FINISHED);
+       }
+
+       /* setup wake call as IOCB callback */
+       pmboxq->mbox_cmpl = lpfc_sli_wake_mbox_wait;
+       /* setup context field to pass wait_queue pointer to wake function  */
+       pmboxq->context1 = &done_q;
+
+       /* start to sleep before we wait, to avoid races */
+       set_current_state(TASK_INTERRUPTIBLE);
+       add_wait_queue(&done_q, &wq_entry);
+
+       /* now issue the command */
+       spin_lock_irq(phba->host->host_lock);
+       retval = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT);
+       spin_unlock_irq(phba->host->host_lock);
+
+       if (retval == MBX_BUSY || retval == MBX_SUCCESS) {
+               timeleft = schedule_timeout(timeout * HZ);
+               pmboxq->context1 = NULL;
+               /* if schedule_timeout returns 0, we timed out and were not
+                  woken up */
+               if (timeleft == 0) {
+                       retval = MBX_TIMEOUT;
+               } else {
+                       retval = MBX_SUCCESS;
+               }
+       }
+
+
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&done_q, &wq_entry);
+       return retval;
+}
+
+static void
+lpfc_sli_wake_iocb_wait(struct lpfc_hba * phba,
+                       struct lpfc_iocbq * queue1, struct lpfc_iocbq * queue2)
+{
+       wait_queue_head_t *pdone_q;
+
+       queue1->iocb_flag |= LPFC_IO_WAIT;
+       if (queue1->context2 && queue2)
+               memcpy(queue1->context2, queue2, sizeof (struct lpfc_iocbq));
+
+       /*
+        * If pdone_q is empty, the waiter gave up and returned and this
+        * call has nothing to do.
+        */
+       pdone_q = queue1->context_un.hipri_wait_queue;
+       if (pdone_q) {
+               wake_up(pdone_q);
+       }
+
+       return;
+}
+
+int
+lpfc_sli_issue_iocb_wait(struct lpfc_hba * phba,
+                        struct lpfc_sli_ring * pring,
+                        struct lpfc_iocbq * piocb,
+                        struct lpfc_iocbq * prspiocbq, uint32_t timeout)
+{
+       DECLARE_WAIT_QUEUE_HEAD(done_q);
+       DECLARE_WAITQUEUE(wq_entry, current);
+       uint32_t timeleft = 0;
+       int retval;
+
+       /* The caller must leave context1 empty for the driver. */
+       if (piocb->context_un.hipri_wait_queue != 0)
+               return (IOCB_ERROR);
+
+       /* If the caller has provided a response iocbq buffer, then context2
+        * is NULL or its an error.
+        */
+       if (prspiocbq) {
+               if (piocb->context2)
+                       return (IOCB_ERROR);
+               piocb->context2 = prspiocbq;
+       }
+
+       /* setup wake call as IOCB callback */
+       piocb->iocb_cmpl = lpfc_sli_wake_iocb_wait;
+       /* setup context field to pass wait_queue pointer to wake function  */
+       piocb->context_un.hipri_wait_queue = &done_q;
+
+       /* start to sleep before we wait, to avoid races */
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       add_wait_queue(&done_q, &wq_entry);
+
+       /* now issue the command */
+       retval = lpfc_sli_issue_iocb(phba, pring, piocb, 0);
+       if (retval == IOCB_SUCCESS) {
+               /* Give up thread time and wait for the iocb to complete or for
+                * the alloted time to expire.
+                */
+               timeleft = schedule_timeout(timeout * HZ);
+
+               piocb->context_un.hipri_wait_queue = NULL;
+               piocb->iocb_cmpl = NULL;
+               if (piocb->context2 == prspiocbq)
+                       piocb->context2 = NULL;
+
+               /*
+                * Catch the error cases.  A timeleft of zero is an error since
+                * the iocb should have completed.  The iocb_flag not have value
+                * LPFC_IO_WAIT is also an error since the wakeup callback sets
+                * this flag when it runs.  Handle each.
+                */
+               if (timeleft == 0) {
+                       printk(KERN_WARNING "lpfc driver detected iocb "
+                              "Timeout!\n");
+                       retval = IOCB_TIMEDOUT;
+               } else if (!(piocb->iocb_flag & LPFC_IO_WAIT)) {
+                       printk(KERN_WARNING "lpfc driver detected iocb "
+                              "flag = 0x%X\n", piocb->iocb_flag);
+                       retval = IOCB_TIMEDOUT;
+               }
+       }
+
+       remove_wait_queue(&done_q, &wq_entry);
+       set_current_state(TASK_RUNNING);
+       piocb->context2 = NULL;
+       return retval;
+}
+
+irqreturn_t
+lpfc_intr_handler(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct lpfc_hba *phba;
+       int intr_status;
+
+       /*
+        * Get the driver's phba structure from the dev_id and
+        * assume the HBA is not interrupting.
+        */
+       phba = (struct lpfc_hba *) dev_id;
+
+       if (phba) {
+               /* Call SLI to handle the interrupt event. */
+               intr_status = lpfc_sli_intr(phba);
+               if (intr_status == 0)
+                       return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+
+} /* lpfc_intr_handler */
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
new file mode 100644 (file)
index 0000000..917a278
--- /dev/null
@@ -0,0 +1,218 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_sli.h 1.36 2004/11/23 16:56:58EST sf_support Exp  $
+ */
+
+#ifndef _H_LPFC_SLI
+#define _H_LPFC_SLI
+
+#include "lpfc_hw.h"
+
+/* forward declaration for LPFC_IOCB_t's use */
+struct lpfc_hba;
+
+/* This structure is used to handle IOCB requests / responses */
+struct lpfc_iocbq {
+       /* lpfc_iocbqs are used in double linked lists */
+       struct list_head list;
+       IOCB_t iocb;            /* IOCB cmd */
+       uint8_t retry;          /* retry counter for IOCB cmd - if needed */
+       uint8_t iocb_flag;
+#define LPFC_IO_POLL   1       /* Polling mode iocb */
+#define LPFC_IO_LIBDFC 2       /* libdfc iocb */
+#define LPFC_IO_WAIT   4
+#define LPFC_IO_HIPRI  8       /* High Priority Queue signal flag */
+
+       uint8_t abort_count;
+       uint8_t rsvd2;
+       uint32_t drvrTimeout;   /* driver timeout in seconds */
+       void *context1;         /* caller context information */
+       void *context2;         /* caller context information */
+       void *context3;         /* caller context information */
+       union {
+               wait_queue_head_t *hipri_wait_queue; /* High Priority Queue wait
+                                                       queue */
+               struct lpfc_iocbq  *rsp_iocb;
+               struct lpfcMboxq   *mbox;
+       } context_un;
+
+       void (*iocb_cmpl) (struct lpfc_hba *, struct lpfc_iocbq *,
+                          struct lpfc_iocbq *);
+
+};
+
+#define SLI_IOCB_RET_IOCB      1       /* Return IOCB if cmd ring full */
+#define SLI_IOCB_HIGH_PRIORITY 2       /* High priority command */
+
+#define IOCB_SUCCESS        0
+#define IOCB_BUSY           1
+#define IOCB_ERROR          2
+#define IOCB_TIMEDOUT       3
+
+typedef struct lpfcMboxq {
+       /* MBOXQs are used in single linked lists */
+       struct list_head list;  /* ptr to next mailbox command */
+       MAILBOX_t mb;           /* Mailbox cmd */
+       void *context1;         /* caller context information */
+       void *context2;         /* caller context information */
+
+       void (*mbox_cmpl) (struct lpfc_hba *, struct lpfcMboxq *);
+
+} LPFC_MBOXQ_t;
+
+#define MBX_POLL        1      /* poll mailbox till command done, then
+                                  return */
+#define MBX_NOWAIT      2      /* issue command then return immediately */
+#define MBX_STOP_IOCB   4      /* Stop iocb processing till mbox cmds
+                                  complete */
+
+#define LPFC_MAX_RING_MASK  4  /* max num of rctl/type masks allowed per
+                                  ring */
+#define LPFC_MAX_RING       4  /* max num of SLI rings used by driver */
+
+/* Structure used to hold SLI ring information */
+struct lpfc_sli_ring {
+       uint16_t flag;          /* ring flags */
+#define LPFC_DEFERRED_RING_EVENT 0x001 /* Deferred processing a ring event */
+#define LPFC_CALL_RING_AVAILABLE 0x002 /* indicates cmd was full */
+#define LPFC_STOP_IOCB_MBX       0x010 /* Stop processing IOCB cmds mbox */
+#define LPFC_STOP_IOCB_EVENT     0x020 /* Stop processing IOCB cmds event */
+#define LPFC_STOP_IOCB_MASK      0x030 /* Stop processing IOCB cmds mask */
+       uint16_t abtsiotag;     /* tracks next iotag to use for ABTS */
+
+       uint32_t local_getidx;   /* last available cmd index (from cmdGetInx) */
+       uint32_t next_cmdidx;    /* next_cmd index */
+       uint8_t rsvd;
+       uint8_t ringno;         /* ring number */
+       uint8_t rspidx;         /* current index in response ring */
+       uint8_t cmdidx;         /* current index in command ring */
+       struct lpfc_iocbq ** fast_lookup; /* array of IOCB ptrs indexed by
+                                          iotag */
+       struct list_head txq;
+       uint16_t txq_cnt;       /* current length of queue */
+       uint16_t txq_max;       /* max length */
+       struct list_head txcmplq;
+       uint16_t txcmplq_cnt;   /* current length of queue */
+       uint16_t txcmplq_max;   /* max length */
+       volatile uint32_t *cmdringaddr; /* virtual address for cmd rings */
+       volatile uint32_t *rspringaddr; /* virtual address for rsp rings */
+       uint32_t missbufcnt;    /* keep track of buffers to post */
+       struct list_head postbufq;
+       uint16_t postbufq_cnt;  /* current length of queue */
+       uint16_t postbufq_max;  /* max length */
+       struct list_head iocb_continueq;
+       uint16_t iocb_continueq_cnt;    /* current length of queue */
+       uint16_t iocb_continueq_max;    /* max length */
+};
+
+typedef struct {
+       uint8_t profile;        /* profile associated with ring */
+       uint8_t rctl;   /* rctl / type pair configured for ring */
+       uint8_t type;   /* rctl / type pair configured for ring */
+       uint8_t rsvd;
+       /* rcv'd unsol event */
+       void (*lpfc_sli_rcv_unsol_event) (struct lpfc_hba *,
+                                        struct lpfc_sli_ring *,
+                                        struct lpfc_iocbq *);
+} LPFC_RING_MASK_t;
+
+/* Structure used for configuring rings to a specific profile or rctl / type */
+typedef struct {
+       LPFC_RING_MASK_t prt[LPFC_MAX_RING_MASK];
+       uint32_t num_mask;      /* number of mask entries in prt array */
+       uint32_t iotag_ctr;     /* keeps track of the next iotag to use */
+       uint32_t iotag_max;     /* max iotag value to use               */
+       uint32_t fast_iotag;    /* max fastlookup based iotag           */
+       uint16_t numCiocb;      /* number of command iocb's per ring */
+       uint16_t numRiocb;      /* number of rsp iocb's per ring */
+       /* cmd ring available */
+       void (*lpfc_sli_cmd_available) (struct lpfc_hba *,
+                                       struct lpfc_sli_ring *);
+} LPFC_RING_INIT_t;
+
+typedef struct {
+       LPFC_RING_INIT_t ringinit[LPFC_MAX_RING]; /* ring initialization info */
+       uint32_t num_rings;
+       uint32_t sli_flag;
+} LPFC_SLI_INIT_t;
+
+/* Structure used to hold SLI statistical counters and info */
+typedef struct {
+       uint64_t iocbEvent[LPFC_MAX_RING];      /* IOCB event counters */
+       uint64_t iocbCmd[LPFC_MAX_RING];        /* IOCB cmd issued */
+       uint64_t iocbRsp[LPFC_MAX_RING];        /* IOCB rsp received */
+       uint64_t iocbCmdDelay[LPFC_MAX_RING];   /* IOCB cmd ring delay */
+       uint64_t iocbCmdFull[LPFC_MAX_RING];    /* IOCB cmd ring full */
+       uint64_t iocbCmdEmpty[LPFC_MAX_RING];   /* IOCB cmd ring is now empty */
+       uint64_t iocbRspFull[LPFC_MAX_RING];    /* IOCB rsp ring full */
+       uint64_t mboxStatErr;   /* Mbox cmds completed status error */
+       uint64_t mboxCmd;       /* Mailbox commands issued */
+       uint64_t sliIntr;       /* Count of Host Attention interrupts */
+       uint32_t errAttnEvent;  /* Error Attn event counters */
+       uint32_t linkEvent;     /* Link event counters */
+       uint32_t mboxEvent;     /* Mailbox event counters */
+       uint32_t mboxBusy;      /* Mailbox cmd busy */
+} LPFC_SLI_STAT_t;
+
+/* Structure used to hold SLI information */
+struct lpfc_sli {
+       LPFC_SLI_INIT_t sliinit;        /* initialization info */
+       /* Additional sli_flags */
+#define LPFC_SLI_MBOX_ACTIVE      0x100        /* HBA mailbox is currently active */
+#define LPFC_SLI2_ACTIVE          0x200        /* SLI2 overlay in firmware is active */
+#define LPFC_PROCESS_LA           0x400        /* Able to process link attention */
+
+       struct lpfc_sli_ring ring[LPFC_MAX_RING];
+       int fcp_ring;           /* ring used for FCP initiator commands */
+       int next_ring;
+
+       int ip_ring;            /* ring used for IP network drv cmds */
+
+       LPFC_SLI_STAT_t slistat;        /* SLI statistical info */
+       struct list_head mboxq;
+       uint16_t mboxq_cnt;     /* current length of queue */
+       uint16_t mboxq_max;     /* max length */
+       LPFC_MBOXQ_t *mbox_active;      /* active mboxq information */
+
+       struct timer_list mbox_tmo;     /* Hold clk to timeout active mbox
+                                          cmd */
+
+       volatile uint32_t *MBhostaddr;  /* virtual address for mbox cmds */
+};
+
+/* Given a pointer to the start of the ring, and the slot number of
+ * the desired iocb entry, calc a pointer to that entry.
+ * (assume iocb entry size is 32 bytes, or 8 words)
+ */
+#define IOCB_ENTRY(ring,slot) ((IOCB_t *)(((char *)(ring)) + ((slot) * 32)))
+
+#define LPFC_SLI_ABORT_IMED    0       /* Immediate abort of IOCB, deque and
+                                          call compl routine immediately. */
+#define LPFC_MBOX_TMO           30     /* Sec tmo for outstanding mbox
+                                          command */
+
+/* Flags for aborting I/Os on tx and txcmpl queues */
+#define LPFC_ABORT_TXQ         1       /* Abort I/Os on txq */
+#define LPFC_ABORT_TXCMPLQ     2       /* Abort I/Os on txcmplq */
+#define LPFC_ABORT_ALLQ                3       /* Abort I/Os both txq and txcmplq */
+
+#endif                         /* _H_LPFC_SLI */
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
new file mode 100644 (file)
index 0000000..51c9dce
--- /dev/null
@@ -0,0 +1,37 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for         *
+ * Enterprise Fibre Channel Host Bus Adapters.                     *
+ * Refer to the README file included with this package for         *
+ * driver version and adapter support.                             *
+ * Copyright (C) 2004 Emulex Corporation.                          *
+ * www.emulex.com                                                  *
+ *                                                                 *
+ * This program is free software; you can redistribute it and/or   *
+ * modify it under the terms of the GNU General Public License     *
+ * as published by the Free Software Foundation; either version 2  *
+ * of the License, or (at your option) any later version.          *
+ *                                                                 *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of  *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *
+ * GNU General Public License for more details, a copy of which    *
+ * can be found in the file COPYING included with this package.    *
+ *******************************************************************/
+
+/*
+ * $Id: lpfc_version.h 1.36 2004/12/07 14:51:53EST sf_support Exp  $
+ */
+
+#ifndef _H_LPFC_VERSION
+#define _H_LPFC_VERSION
+
+#define LPFC_DRIVER_VERSION "8.0.16"
+
+#define LPFC_DRIVER_NAME "lpfc"
+
+#define LPFC_MODULE_DESC "Emulex LightPulse Fibre Channel SCSI driver " \
+               LPFC_DRIVER_VERSION
+
+#define DFC_API_VERSION "0.0.0"
+
+#endif
diff --git a/drivers/scsi/ql1040_fw.h b/drivers/scsi/ql1040_fw.h
new file mode 100644 (file)
index 0000000..89d8e09
--- /dev/null
@@ -0,0 +1,2099 @@
+/**************************************************************************
+ *                  QLOGIC LINUX SOFTWARE
+ *
+ * Copyright (C) 2004 QLogic Corporation
+ * (www.qlogic.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ *************************************************************************/
+
+/************************************************************************
+ *                                                                     *
+ *             --- ISP1040 Initiator/Target Firmware ---               *
+ *                         32 LUN Support                              *
+ *                                                                     *
+ ************************************************************************
+ */
+
+/*
+ *     Firmware Version 7.65.00 (14:17 Jul 20, 1999)
+ */
+
+static unsigned char firmware_version[] = {7,65,0};
+
+#define FW_VERSION_STRING "7.65.0"
+
+static unsigned short risc_code_addr01 = 0x1000 ;
+
+static unsigned short risc_code01[] = { 
+       0x0078, 0x103a, 0x0000, 0x4057, 0x0000, 0x2043, 0x4f50, 0x5952,
+       0x4947, 0x4854, 0x2031, 0x3939, 0x3520, 0x514c, 0x4f47, 0x4943,
+       0x2043, 0x4f52, 0x504f, 0x5241, 0x5449, 0x4f4e, 0x2049, 0x5350,
+       0x3130, 0x3230, 0x2049, 0x2f54, 0x2046, 0x6972, 0x6d77, 0x6172,
+       0x6520, 0x2056, 0x6572, 0x7369, 0x6f6e, 0x2030, 0x372e, 0x3635,
+       0x2020, 0x2043, 0x7573, 0x746f, 0x6d65, 0x7220, 0x4e6f, 0x2e20,
+       0x3030, 0x2050, 0x726f, 0x6475, 0x6374, 0x204e, 0x6f2e, 0x2020,
+       0x3031, 0x2024, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x0048,
+       0x1045, 0x0038, 0x104b, 0x0078, 0x1047, 0x0028, 0x104b, 0x20b9,
+       0x1212, 0x0078, 0x104d, 0x20b9, 0x2222, 0x20c1, 0x0008, 0x2071,
+       0x0010, 0x70c3, 0x0004, 0x20c9, 0x77ff, 0x2089, 0x1186, 0x70c7,
+       0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3, 0x0007, 0x3f00,
+       0x70d6, 0x20c1, 0x0008, 0x2019, 0x0000, 0x2009, 0xfeff, 0x2100,
+       0x200b, 0xa5a5, 0xa1ec, 0x7fff, 0x2d64, 0x206b, 0x0a0a, 0xaddc,
+       0x3fff, 0x2b54, 0x205b, 0x5050, 0x2114, 0xa286, 0xa5a5, 0x0040,
+       0x10bf, 0xa386, 0x000f, 0x0040, 0x1085, 0x2c6a, 0x2a5a, 0x20c1,
+       0x0000, 0x2019, 0x000f, 0x0078, 0x1065, 0x2c6a, 0x2a5a, 0x20c1,
+       0x0008, 0x2009, 0x7fff, 0x2148, 0x2944, 0x204b, 0x0a0a, 0xa9bc,
+       0x3fff, 0x2734, 0x203b, 0x5050, 0x2114, 0xa286, 0x0a0a, 0x0040,
+       0x10a9, 0x284a, 0x263a, 0x20c1, 0x0004, 0x2009, 0x3fff, 0x2134,
+       0x200b, 0x5050, 0x2114, 0xa286, 0x5050, 0x0040, 0x10aa, 0x0078,
+       0x118e, 0x284a, 0x263a, 0x98c0, 0xa188, 0x1000, 0x212c, 0x200b,
+       0xa5a5, 0x2114, 0xa286, 0xa5a5, 0x0040, 0x10bc, 0x250a, 0xa18a,
+       0x1000, 0x98c1, 0x0078, 0x10c1, 0x250a, 0x0078, 0x10c1, 0x2c6a,
+       0x2a5a, 0x2130, 0xa18a, 0x0040, 0x2128, 0xa1a2, 0x5100, 0x8424,
+       0x8424, 0x8424, 0x8424, 0x8424, 0x8424, 0xa192, 0x7800, 0x2009,
+       0x0000, 0x2001, 0x0031, 0x1078, 0x1cba, 0x2218, 0x2079, 0x5100,
+       0x2fa0, 0x2408, 0x2011, 0x0000, 0x20a9, 0x0040, 0x42a4, 0x8109,
+       0x00c0, 0x10dc, 0x7ef2, 0x8528, 0x7de6, 0x7cea, 0x7bee, 0x7883,
+       0x0000, 0x2031, 0x0030, 0x78cf, 0x0101, 0x780b, 0x0002, 0x780f,
+       0x0002, 0x784f, 0x0003, 0x2069, 0x5140, 0x2001, 0x04fd, 0x2004,
+       0xa082, 0x0005, 0x0048, 0x1104, 0x0038, 0x1100, 0x0078, 0x1108,
+       0x681b, 0x003c, 0x0078, 0x110a, 0x00a8, 0x1108, 0x681b, 0x003c,
+       0x681b, 0x0028, 0x6807, 0x0007, 0x680b, 0x00fa, 0x680f, 0x0008,
+       0x6813, 0x0005, 0x6823, 0x0000, 0x6827, 0x0006, 0x6817, 0x0008,
+       0x682b, 0x0000, 0x681f, 0x0019, 0x2069, 0x5380, 0x2011, 0x0020,
+       0x2009, 0x0010, 0x680b, 0x080c, 0x680f, 0x0019, 0x6803, 0xfd00,
+       0x6807, 0x0018, 0x6a1a, 0x2d00, 0xa0e8, 0x0008, 0xa290, 0x0004,
+       0x8109, 0x00c0, 0x1122, 0x2069, 0x5400, 0x2009, 0x0002, 0x20a9,
+       0x0100, 0x6837, 0x0000, 0x680b, 0x0040, 0x7bf0, 0xa386, 0xfeff,
+       0x00c0, 0x1148, 0x6817, 0x0100, 0x681f, 0x0064, 0x0078, 0x114c,
+       0x6817, 0x0064, 0x681f, 0x0002, 0xade8, 0x0010, 0x0070, 0x1152,
+       0x0078, 0x1139, 0x8109, 0x00c0, 0x1137, 0x1078, 0x220a, 0x1078,
+       0x482c, 0x1078, 0x1963, 0x1078, 0x4d22, 0x3200, 0xa085, 0x000d,
+       0x2090, 0x70c3, 0x0000, 0x0090, 0x116c, 0x70c0, 0xa086, 0x0002,
+       0x00c0, 0x116c, 0x1078, 0x1284, 0x1078, 0x1196, 0x78cc, 0xa005,
+       0x00c0, 0x117a, 0x1078, 0x1ce3, 0x0010, 0x1180, 0x0068, 0x1180,
+       0x1078, 0x20e9, 0x0010, 0x1180, 0x0068, 0x1180, 0x1078, 0x1a48,
+       0x00e0, 0x116c, 0x1078, 0x4ba9, 0x0078, 0x116c, 0x118e, 0x1190,
+       0x240b, 0x240b, 0x48ad, 0x48ad, 0x240b, 0x240b, 0x0078, 0x118e,
+       0x0078, 0x1190, 0x0078, 0x1192, 0x0078, 0x1194, 0x0068, 0x1201,
+       0x2061, 0x0000, 0x6018, 0xa084, 0x0001, 0x00c0, 0x1201, 0x7814,
+       0xa005, 0x00c0, 0x11a7, 0x0010, 0x1202, 0x0078, 0x1201, 0x2009,
+       0x515b, 0x2104, 0xa005, 0x00c0, 0x1201, 0x2009, 0x5164, 0x200b,
+       0x0000, 0x7914, 0xa186, 0x0042, 0x00c0, 0x11cc, 0x7816, 0x2009,
+       0x5162, 0x2164, 0x200b, 0x0000, 0x6018, 0x70c6, 0x6014, 0x70ca,
+       0x611c, 0xa18c, 0xff00, 0x6020, 0xa084, 0x00ff, 0xa105, 0x70ce,
+       0x1078, 0x1948, 0x0078, 0x11ff, 0x7814, 0xa086, 0x0018, 0x00c0,
+       0x11d3, 0x1078, 0x165a, 0x7817, 0x0000, 0x2009, 0x5162, 0x2104,
+       0xa065, 0x0040, 0x11ef, 0x0c7e, 0x609c, 0x2060, 0x1078, 0x19b3,
+       0x0c7f, 0x609f, 0x0000, 0x1078, 0x1730, 0x2009, 0x000c, 0x6007,
+       0x0103, 0x1078, 0x1924, 0x00c0, 0x11fb, 0x1078, 0x1948, 0x2009,
+       0x5162, 0x200b, 0x0000, 0x2009, 0x515c, 0x2104, 0x200b, 0x0000,
+       0xa005, 0x0040, 0x11ff, 0x2001, 0x4005, 0x0078, 0x1286, 0x0078,
+       0x1284, 0x007c, 0x70c3, 0x0000, 0x70c7, 0x0000, 0x70cb, 0x0000,
+       0x70cf, 0x0000, 0x70c0, 0xa0bc, 0xffc0, 0x00c0, 0x1252, 0x2038,
+       0x0079, 0x1212, 0x1284, 0x12e5, 0x12a9, 0x12fe, 0x130d, 0x1313,
+       0x12a0, 0x1748, 0x1317, 0x1298, 0x12ad, 0x12af, 0x12b1, 0x12b3,
+       0x174d, 0x1298, 0x1329, 0x1360, 0x1672, 0x1742, 0x12b5, 0x1591,
+       0x15ad, 0x15c9, 0x15f4, 0x154a, 0x1558, 0x156c, 0x1580, 0x13df,
+       0x1298, 0x138d, 0x1393, 0x1398, 0x139d, 0x13a3, 0x13a8, 0x13ad,
+       0x13b2, 0x13b7, 0x13bb, 0x13d0, 0x13dc, 0x1298, 0x1298, 0x1298,
+       0x1298, 0x13eb, 0x13f4, 0x1403, 0x1429, 0x1433, 0x143a, 0x1480,
+       0x148f, 0x149e, 0x14b0, 0x152a, 0x153a, 0x1298, 0x1298, 0x1298,
+       0x1298, 0x153f, 0xa0bc, 0xffa0, 0x00c0, 0x1298, 0x2038, 0xa084,
+       0x001f, 0x0079, 0x125b, 0x1786, 0x1789, 0x1799, 0x1298, 0x1298,
+       0x18df, 0x18fc, 0x1298, 0x1298, 0x1298, 0x1900, 0x1908, 0x1298,
+       0x1298, 0x1298, 0x1298, 0x12db, 0x12f4, 0x131f, 0x1356, 0x1668,
+       0x1764, 0x1778, 0x1298, 0x1829, 0x190e, 0x18bb, 0x18c5, 0x18c9,
+       0x18d7, 0x1298, 0x1298, 0x72ca, 0x71c6, 0x2001, 0x4006, 0x0078,
+       0x1286, 0x73ce, 0x72ca, 0x71c6, 0x2001, 0x4000, 0x70c2, 0x0068,
+       0x1287, 0x2061, 0x0000, 0x601b, 0x0001, 0x2091, 0x5000, 0x00e0,
+       0x128f, 0x00e0, 0x1291, 0x0068, 0x1291, 0x2091, 0x4080, 0x007c,
+       0x70c3, 0x4001, 0x0078, 0x1287, 0x70c3, 0x4006, 0x0078, 0x1287,
+       0x2099, 0x0041, 0x20a1, 0x0041, 0x20a9, 0x0005, 0x53a3, 0x0078,
+       0x1284, 0x70c4, 0x70c3, 0x0004, 0x007a, 0x0078, 0x1284, 0x0078,
+       0x1284, 0x0078, 0x1284, 0x0078, 0x1284, 0x2091, 0x8000, 0x70c3,
+       0x0000, 0x70c7, 0x4953, 0x70cb, 0x5020, 0x70cf, 0x2020, 0x70d3,
+       0x0007, 0x3f00, 0x70d6, 0x2079, 0x0000, 0x781b, 0x0001, 0x2031,
+       0x0030, 0x2059, 0x1000, 0x2029, 0x0457, 0x2051, 0x0470, 0x2061,
+       0x0472, 0x20b9, 0xffff, 0x20c1, 0x0000, 0x2091, 0x5000, 0x2091,
+       0x4080, 0x0078, 0x0455, 0x1078, 0x1b53, 0x00c0, 0x129c, 0x75d8,
+       0x74dc, 0x75da, 0x74de, 0x0078, 0x12e8, 0x2029, 0x0000, 0x2520,
+       0x71d0, 0x73c8, 0x72cc, 0x70c4, 0x1078, 0x1a8d, 0x0040, 0x1284,
+       0x70c3, 0x4002, 0x0078, 0x1284, 0x1078, 0x1b53, 0x00c0, 0x129c,
+       0x75d8, 0x74dc, 0x75da, 0x74de, 0x0078, 0x1301, 0x2029, 0x0000,
+       0x2520, 0x71d0, 0x73c8, 0x72cc, 0x70c4, 0x1078, 0x1aed, 0x0040,
+       0x1284, 0x70c3, 0x4002, 0x0078, 0x1284, 0x71c4, 0x70c8, 0x2114,
+       0x200a, 0x0078, 0x1282, 0x71c4, 0x2114, 0x0078, 0x1282, 0x70c7,
+       0x0007, 0x70cb, 0x0041, 0x70cf, 0x0000, 0x0078, 0x1284, 0x1078,
+       0x1b53, 0x00c0, 0x129c, 0x75d8, 0x76dc, 0x75da, 0x76de, 0x0078,
+       0x132c, 0x2029, 0x0000, 0x2530, 0x70c4, 0x72c8, 0x73cc, 0x74d0,
+       0x70c6, 0x72ca, 0x73ce, 0x74d2, 0xa005, 0x0040, 0x1350, 0x8001,
+       0x7892, 0xa084, 0xfc00, 0x0040, 0x1345, 0x78cc, 0xa085, 0x0001,
+       0x78ce, 0x2001, 0x4005, 0x0078, 0x1286, 0x7a9a, 0x7b9e, 0x7da2,
+       0x7ea6, 0x7c96, 0x78cc, 0xa084, 0xfffc, 0x78ce, 0x0078, 0x1354,
+       0x78cc, 0xa085, 0x0001, 0x78ce, 0x0078, 0x1284, 0x1078, 0x1b53,
+       0x00c0, 0x129c, 0x75d8, 0x76dc, 0x75da, 0x76de, 0x0078, 0x1363,
+       0x2029, 0x0000, 0x2530, 0x70c4, 0x72c8, 0x73cc, 0x74d4, 0x70c6,
+       0x72ca, 0x73ce, 0x74d6, 0xa005, 0x0040, 0x1387, 0x8001, 0x78ae,
+       0xa084, 0xfc00, 0x0040, 0x137c, 0x78cc, 0xa085, 0x0100, 0x78ce,
+       0x2001, 0x4005, 0x0078, 0x1286, 0x7ab6, 0x7bba, 0x7dbe, 0x7ec2,
+       0x7cb2, 0x78cc, 0xa084, 0xfcff, 0x78ce, 0x0078, 0x138b, 0x78cc,
+       0xa085, 0x0100, 0x78ce, 0x0078, 0x1284, 0x2009, 0x5161, 0x210c,
+       0x7aec, 0x0078, 0x1282, 0x2009, 0x5141, 0x210c, 0x0078, 0x1283,
+       0x2009, 0x5142, 0x210c, 0x0078, 0x1283, 0x2061, 0x5140, 0x610c,
+       0x6210, 0x0078, 0x1282, 0x2009, 0x5145, 0x210c, 0x0078, 0x1283,
+       0x2009, 0x5146, 0x210c, 0x0078, 0x1283, 0x2009, 0x5148, 0x210c,
+       0x0078, 0x1283, 0x2009, 0x5149, 0x210c, 0x0078, 0x1283, 0x7908,
+       0x7a0c, 0x0078, 0x1282, 0x71c4, 0x8107, 0xa084, 0x000f, 0x8003,
+       0x8003, 0x8003, 0xa0e8, 0x5380, 0x6a00, 0x6804, 0xa084, 0x0008,
+       0x0040, 0x13cd, 0x6b08, 0x0078, 0x13ce, 0x6b0c, 0x0078, 0x1281,
+       0x77c4, 0x1078, 0x1973, 0x2091, 0x8000, 0x6b1c, 0x6a14, 0x2091,
+       0x8001, 0x2708, 0x0078, 0x1281, 0x794c, 0x0078, 0x1283, 0x77c4,
+       0x1078, 0x1973, 0x2091, 0x8000, 0x6908, 0x6a18, 0x6b10, 0x2091,
+       0x8001, 0x0078, 0x1281, 0x71c4, 0xa182, 0x0010, 0x00c8, 0x127c,
+       0x1078, 0x22e2, 0x0078, 0x1281, 0x71c4, 0xa182, 0x0010, 0x00c8,
+       0x127c, 0x2011, 0x5141, 0x2204, 0x007e, 0x2112, 0x1078, 0x229b,
+       0x017f, 0x0078, 0x1283, 0x71c4, 0x2011, 0x1421, 0x20a9, 0x0008,
+       0x2204, 0xa106, 0x0040, 0x1413, 0x8210, 0x0070, 0x1411, 0x0078,
+       0x1408, 0x0078, 0x127c, 0xa292, 0x1421, 0x027e, 0x2011, 0x5142,
+       0x2204, 0x2112, 0x017f, 0x007e, 0x1078, 0x22a7, 0x017f, 0x0078,
+       0x1283, 0x03e8, 0x00fa, 0x01f4, 0x02ee, 0x0064, 0x0019, 0x0032,
+       0x004b, 0x2061, 0x5140, 0x610c, 0x6210, 0x70c4, 0x600e, 0x70c8,
+       0x6012, 0x0078, 0x1282, 0x2061, 0x5140, 0x6114, 0x70c4, 0x6016,
+       0x0078, 0x1283, 0x2061, 0x5140, 0x71c4, 0x2011, 0x0004, 0x601f,
+       0x0019, 0x2019, 0x1212, 0xa186, 0x0028, 0x0040, 0x145b, 0x2011,
+       0x0005, 0x601f, 0x0019, 0x2019, 0x1212, 0xa186, 0x0032, 0x0040,
+       0x145b, 0x2011, 0x0006, 0x601f, 0x000c, 0x2019, 0x2222, 0xa186,
+       0x003c, 0x00c0, 0x127c, 0x6018, 0x007e, 0x611a, 0x7800, 0xa084,
+       0x0001, 0x00c0, 0x1476, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005,
+       0x0048, 0x146e, 0x0038, 0x1472, 0x0078, 0x1476, 0x0028, 0x1472,
+       0x0078, 0x1476, 0x2019, 0x2222, 0x0078, 0x1478, 0x2019, 0x1212,
+       0x23b8, 0x1078, 0x22b8, 0x1078, 0x4d22, 0x017f, 0x0078, 0x1283,
+       0x71c4, 0xa184, 0xffcf, 0x00c0, 0x127c, 0x2011, 0x5148, 0x2204,
+       0x2112, 0x007e, 0x1078, 0x22da, 0x017f, 0x0078, 0x1283, 0x71c4,
+       0xa182, 0x0010, 0x00c8, 0x127c, 0x2011, 0x5149, 0x2204, 0x007e,
+       0x2112, 0x1078, 0x22c9, 0x017f, 0x0078, 0x1283, 0x71c4, 0x72c8,
+       0xa184, 0xfffd, 0x00c0, 0x127b, 0xa284, 0xfffd, 0x00c0, 0x127b,
+       0x2100, 0x7908, 0x780a, 0x2200, 0x7a0c, 0x780e, 0x0078, 0x1282,
+       0x71c4, 0x8107, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e8,
+       0x5380, 0x2019, 0x0000, 0x72c8, 0xa284, 0x0080, 0x0040, 0x14c6,
+       0x6c14, 0x84ff, 0x00c0, 0x14c6, 0x6817, 0x0040, 0xa284, 0x0040,
+       0x0040, 0x14d0, 0x6c10, 0x84ff, 0x00c0, 0x14d0, 0x6813, 0x0001,
+       0x6800, 0x007e, 0xa226, 0x0040, 0x14f3, 0x6a02, 0xa484, 0x2000,
+       0x0040, 0x14dc, 0xa39d, 0x0010, 0xa484, 0x1000, 0x0040, 0x14e2,
+       0xa39d, 0x0008, 0xa484, 0x4000, 0x0040, 0x14f3, 0x810f, 0xa284,
+       0x4000, 0x0040, 0x14ef, 0x1078, 0x22fc, 0x0078, 0x14f3, 0x1078,
+       0x22ee, 0x0078, 0x14f3, 0x72cc, 0x6808, 0xa206, 0x0040, 0x1522,
+       0xa2a4, 0x00ff, 0x2061, 0x5140, 0x6118, 0xa186, 0x0028, 0x0040,
+       0x1509, 0xa186, 0x0032, 0x0040, 0x150f, 0xa186, 0x003c, 0x0040,
+       0x1515, 0xa482, 0x0064, 0x0048, 0x151f, 0x0078, 0x1519, 0xa482,
+       0x0050, 0x0048, 0x151f, 0x0078, 0x1519, 0xa482, 0x0043, 0x0048,
+       0x151f, 0x71c4, 0x71c6, 0x027f, 0x72ca, 0x0078, 0x127d, 0x6a0a,
+       0xa39d, 0x000a, 0x6804, 0xa305, 0x6806, 0x027f, 0x6b0c, 0x71c4,
+       0x0078, 0x1281, 0x77c4, 0x1078, 0x1973, 0x2091, 0x8000, 0x6a14,
+       0x6b1c, 0x2091, 0x8001, 0x70c8, 0x6816, 0x70cc, 0x681e, 0x2708,
+       0x0078, 0x1281, 0x70c4, 0x794c, 0x784e, 0x0078, 0x1283, 0x71c4,
+       0x72c8, 0x73cc, 0xa182, 0x0010, 0x00c8, 0x127c, 0x1078, 0x230a,
+       0x0078, 0x1281, 0x77c4, 0x1078, 0x1973, 0x2091, 0x8000, 0x6a08,
+       0xa295, 0x0002, 0x6a0a, 0x2091, 0x8001, 0x2708, 0x0078, 0x1282,
+       0x77c4, 0x1078, 0x1973, 0x2091, 0x8000, 0x6a08, 0xa294, 0xfff9,
+       0x6a0a, 0x6804, 0xa005, 0x0040, 0x1567, 0x1078, 0x21d2, 0x2091,
+       0x8001, 0x2708, 0x0078, 0x1282, 0x77c4, 0x1078, 0x1973, 0x2091,
+       0x8000, 0x6a08, 0xa295, 0x0004, 0x6a0a, 0x6804, 0xa005, 0x0040,
+       0x157b, 0x1078, 0x21d2, 0x2091, 0x8001, 0x2708, 0x0078, 0x1282,
+       0x77c4, 0x2041, 0x0001, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091,
+       0x8000, 0x1078, 0x1980, 0x2091, 0x8001, 0x2708, 0x6a08, 0x0078,
+       0x1282, 0x77c4, 0x72c8, 0x73cc, 0x77c6, 0x72ca, 0x73ce, 0x1078,
+       0x19e1, 0x00c0, 0x15a9, 0x6818, 0xa005, 0x0040, 0x15a9, 0x2708,
+       0x1078, 0x231a, 0x00c0, 0x15a9, 0x7817, 0x0015, 0x2091, 0x8001,
+       0x007c, 0x2091, 0x8001, 0x0078, 0x1284, 0x77c4, 0x77c6, 0x2041,
+       0x0021, 0x2049, 0x0005, 0x2051, 0x0020, 0x2091, 0x8000, 0x1078,
+       0x1980, 0x2061, 0x5140, 0x606f, 0x0003, 0x6782, 0x6093, 0x000f,
+       0x6073, 0x0000, 0x7817, 0x0016, 0x1078, 0x21d2, 0x2091, 0x8001,
+       0x007c, 0x77c8, 0x77ca, 0x77c4, 0x77c6, 0xa7bc, 0xff00, 0x2091,
+       0x8000, 0x2061, 0x5140, 0x606f, 0x0002, 0x6073, 0x0000, 0x6782,
+       0x6093, 0x000f, 0x7817, 0x0017, 0x1078, 0x21d2, 0x2091, 0x8001,
+       0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0010, 0x2091, 0x8000,
+       0x1078, 0x1980, 0x70c8, 0x6836, 0x8738, 0xa784, 0x001f, 0x00c0,
+       0x15e8, 0x2091, 0x8001, 0x007c, 0x78cc, 0xa084, 0x0003, 0x00c0,
+       0x1618, 0x2039, 0x0000, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051,
+       0x0008, 0x1078, 0x1973, 0x2091, 0x8000, 0x6808, 0xa80d, 0x690a,
+       0x2091, 0x8001, 0x8738, 0xa784, 0x001f, 0x00c0, 0x1601, 0xa7bc,
+       0xff00, 0x873f, 0x8738, 0x873f, 0xa784, 0x0f00, 0x00c0, 0x1601,
+       0x2091, 0x8000, 0x2069, 0x0100, 0x6830, 0xa084, 0x0040, 0x0040,
+       0x1641, 0x684b, 0x0004, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0004,
+       0x0040, 0x162e, 0x0070, 0x162e, 0x0078, 0x1625, 0x684b, 0x0009,
+       0x20a9, 0x0014, 0x6848, 0xa084, 0x0001, 0x0040, 0x163b, 0x0070,
+       0x163b, 0x0078, 0x1632, 0x20a9, 0x00fa, 0x0070, 0x1641, 0x0078,
+       0x163d, 0x2079, 0x5100, 0x7817, 0x0018, 0x2061, 0x5140, 0x606f,
+       0x0001, 0x6073, 0x0000, 0x6093, 0x000f, 0x78cc, 0xa085, 0x0002,
+       0x78ce, 0x6808, 0xa084, 0xfffd, 0x680a, 0x681b, 0x0048, 0x2091,
+       0x8001, 0x007c, 0x78cc, 0xa084, 0xfffd, 0x78ce, 0xa084, 0x0001,
+       0x00c0, 0x1664, 0x1078, 0x1a2b, 0x71c4, 0x71c6, 0x794a, 0x007c,
+       0x1078, 0x1b53, 0x00c0, 0x129c, 0x75d8, 0x74dc, 0x75da, 0x74de,
+       0x0078, 0x1675, 0x2029, 0x0000, 0x2520, 0x71c4, 0x73c8, 0x72cc,
+       0x71c6, 0x73ca, 0x72ce, 0x2079, 0x5100, 0x2091, 0x8000, 0x1078,
+       0x192e, 0x2091, 0x8001, 0x0040, 0x172c, 0x20a9, 0x0005, 0x20a1,
+       0x5118, 0x2091, 0x8000, 0x41a1, 0x2091, 0x8001, 0x2009, 0x0020,
+       0x1078, 0x1929, 0x0040, 0x1698, 0x1078, 0x1948, 0x0078, 0x172c,
+       0x6004, 0xa084, 0xff00, 0x8007, 0x8009, 0x0040, 0x16fb, 0x0c7e,
+       0x2c68, 0x2091, 0x8000, 0x1078, 0x192e, 0x2091, 0x8001, 0x0040,
+       0x16cc, 0x2c00, 0x689e, 0x8109, 0x00c0, 0x16a0, 0x609f, 0x0000,
+       0x0c7f, 0x0c7e, 0x7218, 0x731c, 0x7420, 0x7524, 0x2c68, 0x689c,
+       0xa065, 0x0040, 0x16fa, 0x2009, 0x0020, 0x1078, 0x1929, 0x00c0,
+       0x16e3, 0x6004, 0xa084, 0x00ff, 0xa086, 0x0002, 0x00c0, 0x16cc,
+       0x2d00, 0x6002, 0x0078, 0x16b2, 0x0c7f, 0x0c7e, 0x609c, 0x2060,
+       0x1078, 0x19b3, 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1730, 0x2009,
+       0x000c, 0x6008, 0xa085, 0x0200, 0x600a, 0x1078, 0x1924, 0x1078,
+       0x1948, 0x0078, 0x172c, 0x0c7f, 0x0c7e, 0x609c, 0x2060, 0x1078,
+       0x19b3, 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1730, 0x2009, 0x000c,
+       0x6007, 0x0103, 0x601b, 0x0003, 0x1078, 0x1924, 0x1078, 0x1948,
+       0x0078, 0x172c, 0x0c7f, 0x74c4, 0x73c8, 0x72cc, 0x6014, 0x2091,
+       0x8000, 0x7817, 0x0012, 0x0e7e, 0x2071, 0x5140, 0x706f, 0x0005,
+       0x7073, 0x0000, 0x7376, 0x727a, 0x747e, 0x7082, 0x7087, 0x0000,
+       0x2c00, 0x708a, 0x708f, 0x0000, 0xa02e, 0x2530, 0x611c, 0x61a2,
+       0xa184, 0x0060, 0x0040, 0x171e, 0x1078, 0x47c2, 0x0e7f, 0x6596,
+       0x65a6, 0x669a, 0x66aa, 0x60af, 0x0000, 0x60b3, 0x0000, 0x1078,
+       0x21d2, 0x2091, 0x8001, 0x007c, 0x70c3, 0x4005, 0x0078, 0x1287,
+       0x20a9, 0x0005, 0x2099, 0x5118, 0x2091, 0x8000, 0x530a, 0x2091,
+       0x8001, 0x2100, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9,
+       0x0000, 0x007c, 0x71c4, 0x70c7, 0x0000, 0x7906, 0x0078, 0x1284,
+       0x71c4, 0x71c6, 0x2168, 0x0078, 0x174f, 0x2069, 0x1000, 0x690c,
+       0xa016, 0x2d04, 0xa210, 0x8d68, 0x8109, 0x00c0, 0x1751, 0xa285,
+       0x0000, 0x00c0, 0x175f, 0x70c3, 0x4000, 0x0078, 0x1761, 0x70c3,
+       0x4003, 0x70ca, 0x0078, 0x1287, 0x2011, 0x5167, 0x220c, 0x70c4,
+       0x8003, 0x0048, 0x1771, 0x1078, 0x3b7f, 0xa184, 0x7fff, 0x0078,
+       0x1775, 0x1078, 0x3b72, 0xa185, 0x8000, 0x2012, 0x0078, 0x1283,
+       0x71c4, 0x1078, 0x3b69, 0x6100, 0x2001, 0x5167, 0x2004, 0xa084,
+       0x8000, 0xa10d, 0x6204, 0x6308, 0x0078, 0x1281, 0x79e4, 0x0078,
+       0x1283, 0x71c4, 0x71c6, 0x2198, 0x20a1, 0x0042, 0x20a9, 0x0004,
+       0x53a3, 0x21a0, 0x2099, 0x0042, 0x20a9, 0x0004, 0x53a3, 0x0078,
+       0x1284, 0x70c4, 0x2068, 0x2079, 0x5100, 0x2091, 0x8000, 0x1078,
+       0x192e, 0x2091, 0x8001, 0x0040, 0x1825, 0x6007, 0x0001, 0x600b,
+       0x0000, 0x602b, 0x0000, 0x601b, 0x0006, 0x6a10, 0xa28c, 0x000f,
+       0xa284, 0x00f0, 0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0x6016,
+       0xa284, 0x0800, 0x0040, 0x17c0, 0x601b, 0x000a, 0x0078, 0x17c6,
+       0xa284, 0x1000, 0x0040, 0x17c6, 0x601b, 0x000c, 0xa284, 0x0300,
+       0x0040, 0x17cf, 0x602b, 0x0001, 0x8004, 0x8004, 0x8004, 0xa085,
+       0x0001, 0x601e, 0x6023, 0x0000, 0x6027, 0x0000, 0xa284, 0x0400,
+       0x0040, 0x17dc, 0x602b, 0x0000, 0x20a9, 0x0006, 0xac80, 0x000b,
+       0x20a0, 0xad80, 0x0005, 0x2098, 0x53a3, 0xa284, 0x0300, 0x00c0,
+       0x17f1, 0x6046, 0x604a, 0x604e, 0x6052, 0x6096, 0x609a, 0x0078,
+       0x17fb, 0x6800, 0x6046, 0x6804, 0x604a, 0x6e08, 0x664e, 0x6d0c,
+       0x6552, 0x6596, 0x669a, 0x6014, 0x2091, 0x8000, 0x7817, 0x0042,
+       0x2c08, 0x2061, 0x5140, 0x606f, 0x0005, 0x6073, 0x0000, 0x6077,
+       0x0000, 0x607b, 0x0000, 0x607f, 0x0000, 0x6082, 0x618a, 0xa284,
+       0x0400, 0x608e, 0x2091, 0x8001, 0x0e7e, 0x2071, 0x0020, 0x7007,
+       0x000a, 0x7007, 0x0002, 0x7003, 0x0000, 0x0e7f, 0x2091, 0x8000,
+       0x1078, 0x21d2, 0x2091, 0x8001, 0x007c, 0x70c3, 0x4005, 0x0078,
+       0x1287, 0x0c7e, 0x0d7e, 0x0e7e, 0x0f7e, 0x2091, 0x8000, 0x2071,
+       0x5140, 0x2079, 0x0100, 0x2061, 0x0010, 0x70a0, 0xa06d, 0x0040,
+       0x18b1, 0x6a04, 0xa294, 0x00ff, 0xa286, 0x0007, 0x0040, 0x1844,
+       0xa286, 0x000f, 0x00c0, 0x18b1, 0x691c, 0xa184, 0x0080, 0x00c0,
+       0x18b1, 0x6824, 0xa18c, 0xff00, 0xa085, 0x0019, 0x6826, 0x71b0,
+       0x81ff, 0x0040, 0x1867, 0x0d7e, 0x2069, 0x0020, 0x6807, 0x0010,
+       0x6908, 0x6808, 0xa106, 0x00c0, 0x1858, 0x690c, 0x680c, 0xa106,
+       0x00c0, 0x185d, 0xa184, 0x00ff, 0x00c0, 0x185d, 0x0d7f, 0x78b8,
+       0xa084, 0x801f, 0x00c0, 0x1867, 0x7848, 0xa085, 0x000c, 0x784a,
+       0x71b0, 0x81ff, 0x0040, 0x188a, 0x70b3, 0x0000, 0x0d7e, 0x2069,
+       0x0020, 0x6807, 0x0018, 0x6804, 0xa084, 0x0008, 0x00c0, 0x187b,
+       0x6807, 0x0008, 0x6804, 0xa084, 0x0008, 0x00c0, 0x1882, 0x6807,
+       0x0002, 0x0d7f, 0x61c4, 0x62c8, 0x63cc, 0x61c6, 0x62ca, 0x63ce,
+       0x0e7e, 0x2071, 0x5100, 0x7266, 0x736a, 0xae80, 0x0019, 0x0e7f,
+       0x7848, 0xa084, 0x000c, 0x00c0, 0x1898, 0x1078, 0x46db, 0x78a3,
+       0x0000, 0x7858, 0xa084, 0xedff, 0x785a, 0x70b4, 0xa080, 0x00df,
+       0x781a, 0x0f7f, 0x0e7f, 0x0d7f, 0x0c7f, 0x2091, 0x8001, 0x0078,
+       0x1284, 0x0f7f, 0x0e7f, 0x0d7f, 0x0c7f, 0x2091, 0x8001, 0x2001,
+       0x4005, 0x0078, 0x1286, 0x7980, 0x71c6, 0x71c4, 0xa182, 0x0003,
+       0x00c8, 0x127c, 0x7982, 0x0078, 0x1284, 0x7980, 0x71c6, 0x0078,
+       0x1284, 0x7974, 0x71c6, 0x71c4, 0x7976, 0x7978, 0x71ca, 0x71c8,
+       0x797a, 0x797c, 0x71ce, 0x71cc, 0x797e, 0x0078, 0x1284, 0x7974,
+       0x71c6, 0x7978, 0x71ca, 0x797c, 0x71ce, 0x0078, 0x1284, 0x7900,
+       0x71c6, 0x71c4, 0x7902, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005,
+       0x0048, 0x18ee, 0x0038, 0x18f0, 0x0078, 0x18fa, 0x00a8, 0x18fa,
+       0xa18c, 0x0001, 0x00c0, 0x18f8, 0x20b9, 0x2222, 0x0078, 0x18fa,
+       0x20b9, 0x1212, 0x0078, 0x1284, 0x7900, 0x71c6, 0x0078, 0x1284,
+       0x2009, 0x5174, 0x2104, 0x70c6, 0x70c4, 0x200a, 0x0078, 0x1284,
+       0x2009, 0x5174, 0x2104, 0x70c6, 0x0078, 0x1284, 0x71c4, 0x8107,
+       0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e8, 0x5380, 0x6a14,
+       0xd2b4, 0x0040, 0x191f, 0x2011, 0x0001, 0x0078, 0x1921, 0x2011,
+       0x0000, 0x6b0c, 0x0078, 0x1281, 0xac80, 0x0001, 0x1078, 0x1b0f,
+       0x007c, 0xac80, 0x0001, 0x1078, 0x1aaf, 0x007c, 0x7850, 0xa065,
+       0x0040, 0x1936, 0x2c04, 0x7852, 0x2063, 0x0000, 0x007c, 0x0f7e,
+       0x2079, 0x5100, 0x7850, 0xa06d, 0x0040, 0x1946, 0x2d04, 0x7852,
+       0x6803, 0x0000, 0x6807, 0x0000, 0x680b, 0x0000, 0x0f7f, 0x007c,
+       0x2091, 0x8000, 0x0f7e, 0x2079, 0x5100, 0x7850, 0x2062, 0x2c00,
+       0xa005, 0x00c0, 0x1955, 0x1078, 0x23eb, 0x7852, 0x0f7f, 0x2091,
+       0x8001, 0x007c, 0x0f7e, 0x2079, 0x5100, 0x7850, 0x206a, 0x2d00,
+       0x7852, 0x0f7f, 0x007c, 0x2011, 0x7800, 0x7a52, 0x7bec, 0x8319,
+       0x0040, 0x1970, 0xa280, 0x0031, 0x2012, 0x2010, 0x0078, 0x1967,
+       0x2013, 0x0000, 0x007c, 0xa784, 0x0f00, 0x800b, 0xa784, 0x001f,
+       0x8003, 0x8003, 0x8003, 0x8003, 0xa105, 0xa0e8, 0x5400, 0x007c,
+       0x1078, 0x1973, 0x2900, 0x682a, 0x2a00, 0x682e, 0x6808, 0xa084,
+       0xffef, 0xa80d, 0x690a, 0x2009, 0x5152, 0x210c, 0x6804, 0xa005,
+       0x0040, 0x19b2, 0xa116, 0x00c0, 0x199d, 0x2060, 0x6000, 0x6806,
+       0x017e, 0x200b, 0x0000, 0x0078, 0x19a0, 0x2009, 0x0000, 0x017e,
+       0x6804, 0xa065, 0x0040, 0x19af, 0x6000, 0x6806, 0x1078, 0x19c0,
+       0x1078, 0x1c5f, 0x6810, 0x8001, 0x6812, 0x00c0, 0x19a0, 0x017f,
+       0x6902, 0x6906, 0x007c, 0xa065, 0x0040, 0x19bf, 0x609c, 0x609f,
+       0x0000, 0x2008, 0x1078, 0x1948, 0x2100, 0x0078, 0x19b3, 0x007c,
+       0x6007, 0x0103, 0x608f, 0x0000, 0x20a9, 0x001c, 0xac80, 0x0005,
+       0x20a0, 0x2001, 0x0000, 0x40a4, 0x6828, 0x601a, 0x682c, 0x6022,
+       0x007c, 0x0e7e, 0x2071, 0x5140, 0x704c, 0xa08c, 0x0200, 0x00c0,
+       0x19df, 0xa088, 0x5180, 0x2d0a, 0x8000, 0x704e, 0xa006, 0x0e7f,
+       0x007c, 0x1078, 0x1973, 0x2091, 0x8000, 0x6804, 0x781e, 0xa065,
+       0x0040, 0x1a2a, 0x0078, 0x19f2, 0x2c00, 0x781e, 0x6000, 0xa065,
+       0x0040, 0x1a2a, 0x600c, 0xa306, 0x00c0, 0x19ec, 0x6010, 0xa206,
+       0x00c0, 0x19ec, 0x2c28, 0x2001, 0x5152, 0x2004, 0xac06, 0x00c0,
+       0x1a03, 0x0078, 0x1a28, 0x6804, 0xac06, 0x00c0, 0x1a10, 0x6000,
+       0xa065, 0x6806, 0x00c0, 0x1a1a, 0x6803, 0x0000, 0x0078, 0x1a1a,
+       0x6400, 0x781c, 0x2060, 0x6402, 0xa486, 0x0000, 0x00c0, 0x1a1a,
+       0x2c00, 0x6802, 0x2560, 0x1078, 0x19c0, 0x601b, 0x0005, 0x6023,
+       0x0020, 0x1078, 0x1c5f, 0x6810, 0x8001, 0x1050, 0x23eb, 0x6812,
+       0xa085, 0xffff, 0x007c, 0x2039, 0x0000, 0x2041, 0x0021, 0x2049,
+       0x0004, 0x2051, 0x0008, 0x2091, 0x8000, 0x1078, 0x1980, 0x8738,
+       0xa784, 0x001f, 0x00c0, 0x1a35, 0xa7bc, 0xff00, 0x873f, 0x8738,
+       0x873f, 0xa784, 0x0f00, 0x00c0, 0x1a35, 0x2091, 0x8001, 0x007c,
+       0x2061, 0x0000, 0x6018, 0xa084, 0x0001, 0x00c0, 0x1a59, 0x2091,
+       0x8000, 0x78e0, 0x78e3, 0x0000, 0x2091, 0x8001, 0xa005, 0x00c0,
+       0x1a5a, 0x007c, 0xa08c, 0xfff0, 0x0040, 0x1a60, 0x1078, 0x23eb,
+       0x0079, 0x1a62, 0x1a72, 0x1a75, 0x1a7b, 0x1a7f, 0x1a73, 0x1a83,
+       0x1a89, 0x1a73, 0x1a73, 0x1c29, 0x1c4d, 0x1c51, 0x1a73, 0x1a73,
+       0x1a73, 0x1a73, 0x007c, 0x1078, 0x23eb, 0x1078, 0x1a2b, 0x2001,
+       0x8001, 0x0078, 0x1c57, 0x2001, 0x8003, 0x0078, 0x1c57, 0x2001,
+       0x8004, 0x0078, 0x1c57, 0x1078, 0x1a2b, 0x2001, 0x8006, 0x0078,
+       0x1c57, 0x2001, 0x8007, 0x0078, 0x1c57, 0x2030, 0x2138, 0xa782,
+       0x0021, 0x0048, 0x1a95, 0x2009, 0x0020, 0x2600, 0x1078, 0x1aaf,
+       0x00c0, 0x1aae, 0xa7ba, 0x0020, 0x0048, 0x1aad, 0x0040, 0x1aad,
+       0x2708, 0xa6b0, 0x0020, 0xa290, 0x0040, 0xa399, 0x0000, 0xa4a1,
+       0x0000, 0xa5a9, 0x0000, 0x0078, 0x1a8f, 0xa006, 0x007c, 0x81ff,
+       0x0040, 0x1aea, 0x2099, 0x0030, 0x20a0, 0x700c, 0xa084, 0x00ff,
+       0x0040, 0x1ac1, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0,
+       0x1abc, 0x21a8, 0x7017, 0x0000, 0x810b, 0x7112, 0x721a, 0x731e,
+       0x7422, 0x7526, 0x780c, 0xa085, 0x0001, 0x7002, 0x7007, 0x0001,
+       0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x00c8, 0x1ade, 0x2009,
+       0x0022, 0x2104, 0xa084, 0x4000, 0x00c0, 0x1ad0, 0x7008, 0x800b,
+       0x00c8, 0x1ad0, 0x7007, 0x0002, 0xa08c, 0x01e0, 0x00c0, 0x1aea,
+       0x53a5, 0xa006, 0x7003, 0x0000, 0x007c, 0x2030, 0x2138, 0xa782,
+       0x0021, 0x0048, 0x1af5, 0x2009, 0x0020, 0x2600, 0x1078, 0x1b0f,
+       0x00c0, 0x1b0e, 0xa7ba, 0x0020, 0x0048, 0x1b0d, 0x0040, 0x1b0d,
+       0x2708, 0xa6b0, 0x0020, 0xa290, 0x0040, 0xa399, 0x0000, 0xa4a1,
+       0x0000, 0xa5a9, 0x0000, 0x0078, 0x1aef, 0xa006, 0x007c, 0x81ff,
+       0x0040, 0x1b50, 0x2098, 0x20a1, 0x0030, 0x700c, 0xa084, 0x00ff,
+       0x0040, 0x1b21, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0,
+       0x1b1c, 0x21a8, 0x7017, 0x0000, 0x810b, 0x7112, 0x721a, 0x731e,
+       0x7422, 0x7526, 0x780c, 0xa085, 0x0000, 0x7002, 0x53a6, 0x7007,
+       0x0001, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x00c8, 0x1b3f,
+       0x2009, 0x0022, 0x2104, 0xa084, 0x4000, 0x00c0, 0x1b31, 0x7010,
+       0xa084, 0xf000, 0x0040, 0x1b48, 0x7007, 0x0008, 0x0078, 0x1b4c,
+       0x7108, 0x8103, 0x00c8, 0x1b31, 0x7007, 0x0002, 0xa184, 0x01e0,
+       0x7003, 0x0000, 0x007c, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0004,
+       0x00c8, 0x1b5c, 0x0078, 0x1b5f, 0xa006, 0x0078, 0x1b61, 0xa085,
+       0x0001, 0x007c, 0x0e7e, 0x2071, 0x5100, 0x2d08, 0x7058, 0x6802,
+       0xa005, 0x00c0, 0x1b6c, 0x715e, 0x715a, 0x0e7f, 0x007c, 0x2c08,
+       0x7858, 0x6002, 0xa005, 0x00c0, 0x1b76, 0x795e, 0x795a, 0x007c,
+       0x2091, 0x8000, 0x6003, 0x0000, 0x2c08, 0x785c, 0xa065, 0x00c0,
+       0x1b84, 0x795a, 0x0078, 0x1b85, 0x6102, 0x795e, 0x2091, 0x8001,
+       0x1078, 0x21ef, 0x007c, 0x0e7e, 0x2071, 0x5100, 0x7058, 0xa06d,
+       0x0040, 0x1b99, 0x6800, 0x705a, 0xa005, 0x00c0, 0x1b98, 0x705e,
+       0x8dff, 0x0e7f, 0x007c, 0x0d7e, 0x0c7e, 0x0f7e, 0x2079, 0x5100,
+       0xaf80, 0x0016, 0x2060, 0x6000, 0xa005, 0x0040, 0x1bc9, 0x2068,
+       0x6814, 0xa306, 0x00c0, 0x1bb2, 0x6828, 0xa084, 0x00ff, 0xa406,
+       0x0040, 0x1bb5, 0x2d60, 0x0078, 0x1ba3, 0x6800, 0xa005, 0x6002,
+       0x00c0, 0x1bc1, 0xaf80, 0x0016, 0xac06, 0x0040, 0x1bc0, 0x2c00,
+       0x785e, 0x0d7e, 0x689c, 0xa005, 0x0040, 0x1bc8, 0x1078, 0x19b3,
+       0x007f, 0x0f7f, 0x0c7f, 0x0d7f, 0xa005, 0x007c, 0x0d7e, 0x0c7e,
+       0x0f7e, 0x2079, 0x5100, 0xaf80, 0x0016, 0x2060, 0x6000, 0xa005,
+       0x0040, 0x1bf8, 0x2068, 0x6814, 0xa084, 0x00ff, 0xa306, 0x0040,
+       0x1be4, 0x2d60, 0x0078, 0x1bd6, 0x6800, 0xa005, 0x6002, 0x00c0,
+       0x1bf0, 0xaf80, 0x0016, 0xac06, 0x0040, 0x1bef, 0x2c00, 0x785e,
+       0x0d7e, 0x689c, 0xa005, 0x0040, 0x1bf7, 0x1078, 0x19b3, 0x007f,
+       0x0f7f, 0x0c7f, 0x0d7f, 0xa005, 0x007c, 0x0d7e, 0x0c7e, 0x0f7e,
+       0x2079, 0x5100, 0xaf80, 0x0016, 0x2060, 0x6000, 0xa06d, 0x0040,
+       0x1c24, 0x6814, 0xa306, 0x0040, 0x1c10, 0x2d60, 0x0078, 0x1c05,
+       0x6800, 0xa005, 0x6002, 0x00c0, 0x1c1c, 0xaf80, 0x0016, 0xac06,
+       0x0040, 0x1c1b, 0x2c00, 0x785e, 0x0d7e, 0x689c, 0xa005, 0x0040,
+       0x1c23, 0x1078, 0x19b3, 0x007f, 0x0f7f, 0x0c7f, 0x0d7f, 0xa005,
+       0x007c, 0x2091, 0x8000, 0x2069, 0x5140, 0x6800, 0xa086, 0x0000,
+       0x0040, 0x1c37, 0x2091, 0x8001, 0x78e3, 0x0009, 0x007c, 0x6880,
+       0xa0bc, 0xff00, 0x2041, 0x0021, 0x2049, 0x0004, 0x2051, 0x0010,
+       0x1078, 0x1980, 0x8738, 0xa784, 0x001f, 0x00c0, 0x1c40, 0x2091,
+       0x8001, 0x2001, 0x800a, 0x0078, 0x1c57, 0x2001, 0x800c, 0x0078,
+       0x1c57, 0x1078, 0x1a2b, 0x2001, 0x800d, 0x0078, 0x1c57, 0x70c2,
+       0x2061, 0x0000, 0x601b, 0x0001, 0x2091, 0x4080, 0x007c, 0x6004,
+       0x2c08, 0x2063, 0x0000, 0x7884, 0x8000, 0x7886, 0x7888, 0xa005,
+       0x798a, 0x0040, 0x1c6e, 0x2c02, 0x0078, 0x1c6f, 0x798e, 0x007c,
+       0x6807, 0x0103, 0x0c7e, 0x2061, 0x5100, 0x2d08, 0x206b, 0x0000,
+       0x6084, 0x8000, 0x6086, 0x6088, 0xa005, 0x618a, 0x0040, 0x1c83,
+       0x2d02, 0x0078, 0x1c84, 0x618e, 0x0c7f, 0x007c, 0x1078, 0x1c97,
+       0x0040, 0x1c96, 0x0c7e, 0x609c, 0xa065, 0x0040, 0x1c91, 0x1078,
+       0x19b3, 0x0c7f, 0x609f, 0x0000, 0x1078, 0x1948, 0x007c, 0x788c,
+       0xa065, 0x0040, 0x1ca9, 0x2091, 0x8000, 0x7884, 0x8001, 0x7886,
+       0x2c04, 0x788e, 0xa005, 0x00c0, 0x1ca7, 0x788a, 0x8000, 0x2091,
+       0x8001, 0x007c, 0x20a9, 0x0010, 0xa006, 0x8004, 0x8086, 0x818e,
+       0x00c8, 0x1cb3, 0xa200, 0x0070, 0x1cb7, 0x0078, 0x1cae, 0x8086,
+       0x818e, 0x007c, 0x157e, 0x20a9, 0x0010, 0xa005, 0x0040, 0x1cdd,
+       0xa11a, 0x00c8, 0x1cdd, 0x8213, 0x818d, 0x0048, 0x1cce, 0xa11a,
+       0x00c8, 0x1ccf, 0x0070, 0x1cd5, 0x0078, 0x1cc3, 0xa11a, 0x2308,
+       0x8210, 0x0070, 0x1cd5, 0x0078, 0x1cc3, 0x007e, 0x3200, 0xa084,
+       0xf7ff, 0x2080, 0x007f, 0x157f, 0x007c, 0x007e, 0x3200, 0xa085,
+       0x0800, 0x0078, 0x1cd9, 0x7994, 0x70d0, 0xa106, 0x0040, 0x1d51,
+       0x2091, 0x8000, 0x2071, 0x0020, 0x7004, 0xa005, 0x00c0, 0x1d51,
+       0x7008, 0x7208, 0xa206, 0x00c0, 0x1d51, 0xa286, 0x0008, 0x00c0,
+       0x1d51, 0x2071, 0x0010, 0x1078, 0x192e, 0x0040, 0x1d51, 0x7a9c,
+       0x7b98, 0x7ca4, 0x7da0, 0xa184, 0xff00, 0x0040, 0x1d1f, 0x2031,
+       0x0000, 0x810b, 0x86b5, 0x810b, 0x86b5, 0x810b, 0x86b5, 0x810b,
+       0x86b5, 0x810b, 0x86b5, 0x810b, 0x86b5, 0x2100, 0xa210, 0x2600,
+       0xa319, 0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x0078, 0x1d29, 0x8107,
+       0x8004, 0x8004, 0xa210, 0xa399, 0x0000, 0xa4a1, 0x0000, 0xa5a9,
+       0x0000, 0x2009, 0x0020, 0x1078, 0x1929, 0x2091, 0x8001, 0x0040,
+       0x1d48, 0x1078, 0x1948, 0x78a8, 0x8000, 0x78aa, 0xa086, 0x0002,
+       0x00c0, 0x1d51, 0x2091, 0x8000, 0x78e3, 0x0002, 0x78ab, 0x0000,
+       0x78cc, 0xa085, 0x0003, 0x78ce, 0x2091, 0x8001, 0x0078, 0x1d51,
+       0x78ab, 0x0000, 0x1078, 0x20ac, 0x6004, 0xa084, 0x000f, 0x0079,
+       0x1d56, 0x2071, 0x0010, 0x2091, 0x8001, 0x007c, 0x1d66, 0x1d88,
+       0x1dae, 0x1d66, 0x1dcb, 0x1d75, 0x1f2c, 0x1f47, 0x1d66, 0x1d82,
+       0x1da8, 0x1e13, 0x1e82, 0x1ed2, 0x1ee4, 0x1f43, 0x2039, 0x0400,
+       0x78dc, 0xa705, 0x78de, 0x6008, 0xa705, 0x600a, 0x1078, 0x1fc7,
+       0x609c, 0x78da, 0x1078, 0x2094, 0x007c, 0x78dc, 0xa084, 0x0100,
+       0x0040, 0x1d7c, 0x0078, 0x1d66, 0x601c, 0xa085, 0x0080, 0x601e,
+       0x0078, 0x1d8f, 0x1078, 0x1b53, 0x00c0, 0x1d66, 0x1078, 0x20c6,
+       0x78dc, 0xa084, 0x0100, 0x0040, 0x1d8f, 0x0078, 0x1d66, 0x78df,
+       0x0000, 0x6004, 0x8007, 0xa084, 0x00ff, 0x78d2, 0x8001, 0x609f,
+       0x0000, 0x0040, 0x1da5, 0x1078, 0x1fc7, 0x0040, 0x1da5, 0x78dc,
+       0xa085, 0x0100, 0x78de, 0x0078, 0x1da7, 0x1078, 0x1feb, 0x007c,
+       0x1078, 0x1b53, 0x00c0, 0x1d66, 0x1078, 0x20c2, 0x78dc, 0xa08c,
+       0x0e00, 0x00c0, 0x1db7, 0xa084, 0x0100, 0x00c0, 0x1db9, 0x0078,
+       0x1d66, 0x1078, 0x1fc7, 0x00c0, 0x1dca, 0x6104, 0xa18c, 0x00ff,
+       0xa186, 0x0007, 0x0040, 0x1f84, 0xa186, 0x000f, 0x0040, 0x1f84,
+       0x1078, 0x1feb, 0x007c, 0x78dc, 0xa084, 0x0100, 0x0040, 0x1dd2,
+       0x0078, 0x1d66, 0x78df, 0x0000, 0x6714, 0x2011, 0x0001, 0x20a9,
+       0x0001, 0x6018, 0xa084, 0x00ff, 0xa005, 0x0040, 0x1df5, 0x2011,
+       0x0001, 0xa7bc, 0xff00, 0x20a9, 0x0020, 0xa08e, 0x0001, 0x0040,
+       0x1df5, 0x2039, 0x0000, 0x2011, 0x0002, 0x20a9, 0x0100, 0xa08e,
+       0x0002, 0x0040, 0x1df5, 0x0078, 0x1e10, 0x1078, 0x1973, 0x2091,
+       0x8000, 0x682b, 0x0000, 0x682f, 0x0000, 0x6808, 0xa084, 0xffde,
+       0x680a, 0xade8, 0x0010, 0x2091, 0x8001, 0x0070, 0x1e09, 0x0078,
+       0x1df7, 0x8211, 0x0040, 0x1e10, 0x20a9, 0x0100, 0x0078, 0x1df7,
+       0x1078, 0x1948, 0x007c, 0x2001, 0x5167, 0x2004, 0xa084, 0x8000,
+       0x0040, 0x1fac, 0x6114, 0x1078, 0x20e3, 0x6900, 0xa184, 0x0001,
+       0x0040, 0x1e34, 0x6028, 0xa084, 0x00ff, 0x00c0, 0x1fa4, 0x6800,
+       0xa084, 0x0001, 0x0040, 0x1fac, 0x6803, 0x0000, 0x680b, 0x0000,
+       0x6807, 0x0000, 0x0078, 0x1fb4, 0x2011, 0x0001, 0x6020, 0xd0f4,
+       0x0040, 0x1e3c, 0xa295, 0x0002, 0xd0c4, 0x0040, 0x1e41, 0xa295,
+       0x0008, 0xd0cc, 0x0040, 0x1e46, 0xa295, 0x0400, 0x601c, 0xa084,
+       0x0002, 0x0040, 0x1e4d, 0xa295, 0x0004, 0x602c, 0xa08c, 0x00ff,
+       0xa182, 0x0002, 0x0048, 0x1fb0, 0xa182, 0x001b, 0x00c8, 0x1fb0,
+       0x0040, 0x1fb0, 0x690e, 0x602c, 0x8007, 0xa08c, 0x00ff, 0xa182,
+       0x0002, 0x0048, 0x1fb0, 0xa182, 0x001b, 0x00c8, 0x1fb0, 0x0040,
+       0x1fb0, 0x6912, 0x6030, 0xa005, 0x00c0, 0x1e70, 0x2001, 0x001e,
+       0x8000, 0x6816, 0x6028, 0xa084, 0x00ff, 0x0040, 0x1fac, 0x6806,
+       0x6028, 0x8007, 0xa084, 0x00ff, 0x0040, 0x1fac, 0x680a, 0x6a02,
+       0x0078, 0x1fb4, 0x2001, 0x5167, 0x2004, 0xa084, 0x8000, 0x0040,
+       0x1fac, 0x6114, 0x1078, 0x20e3, 0x2091, 0x8000, 0x6a04, 0x6b08,
+       0x6418, 0xa484, 0x0003, 0x0040, 0x1ea8, 0x6128, 0xa18c, 0x00ff,
+       0x8001, 0x00c0, 0x1ea1, 0x2100, 0xa210, 0x0048, 0x1ece, 0x0078,
+       0x1ea8, 0x8001, 0x00c0, 0x1ece, 0x2100, 0xa212, 0x0048, 0x1ece,
+       0xa484, 0x000c, 0x0040, 0x1ec2, 0x6128, 0x810f, 0xa18c, 0x00ff,
+       0xa082, 0x0004, 0x00c0, 0x1eba, 0x2100, 0xa318, 0x0048, 0x1ece,
+       0x0078, 0x1ec2, 0xa082, 0x0004, 0x00c0, 0x1ece, 0x2100, 0xa31a,
+       0x0048, 0x1ece, 0x6030, 0xa005, 0x0040, 0x1ec8, 0x8000, 0x6816,
+       0x6a06, 0x6b0a, 0x2091, 0x8001, 0x0078, 0x1fb4, 0x2091, 0x8001,
+       0x0078, 0x1fb0, 0x6114, 0x1078, 0x20e3, 0x2091, 0x8000, 0x6b08,
+       0x8318, 0x0048, 0x1ee0, 0x6b0a, 0x2091, 0x8001, 0x0078, 0x1fc3,
+       0x2091, 0x8001, 0x0078, 0x1fb0, 0x6024, 0x8007, 0xa084, 0x00ff,
+       0x0040, 0x1f02, 0xa086, 0x0080, 0x00c0, 0x1f2a, 0x20a9, 0x0008,
+       0x2069, 0x7510, 0x2091, 0x8000, 0x6800, 0xa084, 0xfcff, 0x6802,
+       0xade8, 0x0008, 0x0070, 0x1efe, 0x0078, 0x1ef4, 0x2091, 0x8001,
+       0x0078, 0x1fb4, 0x6028, 0xa015, 0x0040, 0x1f2a, 0x6114, 0x1078,
+       0x20e3, 0x0d7e, 0xade8, 0x0007, 0x2091, 0x8000, 0x6800, 0xa00d,
+       0x0040, 0x1f27, 0xa206, 0x0040, 0x1f18, 0x2168, 0x0078, 0x1f0e,
+       0x0c7e, 0x2160, 0x6000, 0x6802, 0x1078, 0x1948, 0x0c7f, 0x0d7f,
+       0x6808, 0x8000, 0x680a, 0x2091, 0x8001, 0x0078, 0x1fc3, 0x2091,
+       0x8001, 0x0d7f, 0x0078, 0x1fac, 0x6114, 0x1078, 0x20e3, 0x6800,
+       0xa084, 0x0001, 0x0040, 0x1f9c, 0x2091, 0x8000, 0x6a04, 0x8210,
+       0x0048, 0x1f3f, 0x6a06, 0x2091, 0x8001, 0x0078, 0x1fc3, 0x2091,
+       0x8001, 0x0078, 0x1fb0, 0x1078, 0x1b53, 0x00c0, 0x1d66, 0x6114,
+       0x1078, 0x20e3, 0x60be, 0x60bb, 0x0000, 0x6900, 0xa184, 0x0008,
+       0x0040, 0x1f56, 0x6020, 0xa085, 0x0100, 0x6022, 0xa184, 0x0001,
+       0x0040, 0x1fac, 0xa184, 0x0100, 0x00c0, 0x1f98, 0xa184, 0x0200,
+       0x00c0, 0x1f94, 0x681c, 0xa005, 0x00c0, 0x1fa0, 0x6004, 0xa084,
+       0x00ff, 0xa086, 0x000f, 0x00c0, 0x1f6f, 0x1078, 0x20c6, 0x78df,
+       0x0000, 0x6004, 0x8007, 0xa084, 0x00ff, 0x78d2, 0x8001, 0x609f,
+       0x0000, 0x0040, 0x1f84, 0x1078, 0x1fc7, 0x0040, 0x1f84, 0x78dc,
+       0xa085, 0x0100, 0x78de, 0x007c, 0x78d7, 0x0000, 0x78db, 0x0000,
+       0x6024, 0xa084, 0xff00, 0x6026, 0x1078, 0x39de, 0x0040, 0x1ce3,
+       0x1078, 0x1b78, 0x0078, 0x1ce3, 0x2009, 0x0017, 0x0078, 0x1fb6,
+       0x2009, 0x000e, 0x0078, 0x1fb6, 0x2009, 0x0007, 0x0078, 0x1fb6,
+       0x2009, 0x0035, 0x0078, 0x1fb6, 0x2009, 0x003e, 0x0078, 0x1fb6,
+       0x2009, 0x0004, 0x0078, 0x1fb6, 0x2009, 0x0006, 0x0078, 0x1fb6,
+       0x2009, 0x0016, 0x0078, 0x1fb6, 0x2009, 0x0001, 0x6024, 0xa084,
+       0xff00, 0xa105, 0x6026, 0x2091, 0x8000, 0x1078, 0x1c5f, 0x2091,
+       0x8001, 0x0078, 0x1ce3, 0x1078, 0x1948, 0x0078, 0x1ce3, 0x78d4,
+       0xa06d, 0x00c0, 0x1fd2, 0x2c00, 0x78d6, 0x78da, 0x609f, 0x0000,
+       0x0078, 0x1fde, 0x2c00, 0x689e, 0x609f, 0x0000, 0x78d6, 0x2d00,
+       0x6002, 0x78d8, 0xad06, 0x00c0, 0x1fde, 0x6002, 0x78d0, 0x8001,
+       0x78d2, 0x00c0, 0x1fea, 0x78dc, 0xa084, 0xfeff, 0x78de, 0x78d8,
+       0x2060, 0xa006, 0x007c, 0xa02e, 0x2530, 0x611c, 0x61a2, 0xa184,
+       0xe1ff, 0x601e, 0xa184, 0x0060, 0x0040, 0x1ffa, 0x0e7e, 0x1078,
+       0x47c2, 0x0e7f, 0x6596, 0x65a6, 0x669a, 0x66aa, 0x60af, 0x0000,
+       0x60b3, 0x0000, 0x6714, 0x1078, 0x1973, 0x2091, 0x8000, 0x60a0,
+       0xa084, 0x8000, 0x00c0, 0x2021, 0x6808, 0xa084, 0x0001, 0x0040,
+       0x2021, 0x2091, 0x8001, 0x1078, 0x19c0, 0x2091, 0x8000, 0x1078,
+       0x1c5f, 0x2091, 0x8001, 0x78d7, 0x0000, 0x78db, 0x0000, 0x0078,
+       0x2093, 0x6024, 0xa096, 0x0001, 0x00c0, 0x2028, 0x8000, 0x6026,
+       0x6a10, 0x6814, 0x2091, 0x8001, 0xa202, 0x0048, 0x2037, 0x0040,
+       0x2037, 0x2039, 0x0200, 0x1078, 0x2094, 0x0078, 0x2093, 0x2c08,
+       0x2091, 0x8000, 0x60a0, 0xa084, 0x8000, 0x0040, 0x2064, 0x6800,
+       0xa065, 0x0040, 0x2069, 0x6a04, 0x0e7e, 0x2071, 0x5140, 0x7000,
+       0xa084, 0x0001, 0x0040, 0x205e, 0x7048, 0xa206, 0x00c0, 0x205e,
+       0x6b04, 0x231c, 0x2160, 0x6302, 0x2300, 0xa005, 0x00c0, 0x2059,
+       0x6902, 0x2260, 0x6102, 0x0e7f, 0x0078, 0x2070, 0x2160, 0x6202,
+       0x6906, 0x0e7f, 0x0078, 0x2070, 0x6800, 0xa065, 0x0040, 0x2069,
+       0x6102, 0x6902, 0x00c0, 0x206d, 0x6906, 0x2160, 0x6003, 0x0000,
+       0x2160, 0x60a0, 0xa084, 0x8000, 0x0040, 0x207a, 0x6808, 0xa084,
+       0xfffc, 0x680a, 0x6810, 0x8000, 0x6812, 0x2091, 0x8001, 0x6808,
+       0xa08c, 0x0040, 0x0040, 0x2089, 0xa086, 0x0040, 0x680a, 0x1078,
+       0x19d1, 0x2091, 0x8000, 0x1078, 0x21d2, 0x2091, 0x8001, 0x78db,
+       0x0000, 0x78d7, 0x0000, 0x007c, 0x6008, 0xa705, 0x600a, 0x2091,
+       0x8000, 0x1078, 0x1c5f, 0x2091, 0x8001, 0x78d8, 0xa065, 0x0040,
+       0x20a7, 0x609c, 0x78da, 0x609f, 0x0000, 0x0078, 0x2097, 0x78d7,
+       0x0000, 0x78db, 0x0000, 0x007c, 0x7990, 0x7894, 0x8000, 0xa10a,
+       0x00c8, 0x20b3, 0xa006, 0x7896, 0x70d2, 0x7804, 0xa005, 0x0040,
+       0x20c1, 0x8001, 0x7806, 0x00c0, 0x20c1, 0x0068, 0x20c1, 0x2091,
+       0x4080, 0x007c, 0x2039, 0x20da, 0x0078, 0x20c8, 0x2039, 0x20e0,
+       0x2704, 0xa005, 0x0040, 0x20d9, 0xac00, 0x2068, 0x6b08, 0x6c0c,
+       0x6910, 0x6a14, 0x690a, 0x6a0e, 0x6b12, 0x6c16, 0x8738, 0x0078,
+       0x20c8, 0x007c, 0x0003, 0x0009, 0x000f, 0x0015, 0x001b, 0x0000,
+       0x0015, 0x001b, 0x0000, 0x0c7e, 0x1078, 0x3b69, 0x2c68, 0x0c7f,
+       0x007c, 0x0010, 0x215a, 0x0068, 0x215a, 0x2029, 0x0000, 0x78cb,
+       0x0000, 0x788c, 0xa065, 0x0040, 0x2153, 0x2009, 0x5174, 0x2104,
+       0xa084, 0x0001, 0x0040, 0x2121, 0x6004, 0xa086, 0x0103, 0x00c0,
+       0x2121, 0x6018, 0xa005, 0x00c0, 0x2121, 0x6014, 0xa005, 0x00c0,
+       0x2121, 0x0d7e, 0x2069, 0x0000, 0x6818, 0xa084, 0x0001, 0x00c0,
+       0x2120, 0x600c, 0x70c6, 0x6010, 0x70ca, 0x70c3, 0x8020, 0x681b,
+       0x0001, 0x2091, 0x4080, 0x0d7f, 0x1078, 0x1c86, 0x0078, 0x2158,
+       0x0d7f, 0x1078, 0x215b, 0x0040, 0x2153, 0x6204, 0xa294, 0x00ff,
+       0xa296, 0x0003, 0x0040, 0x2133, 0x6204, 0xa296, 0x0110, 0x00c0,
+       0x2141, 0x78cb, 0x0001, 0x6204, 0xa294, 0xff00, 0x8217, 0x8211,
+       0x0040, 0x2141, 0x85ff, 0x00c0, 0x2153, 0x8210, 0xa202, 0x00c8,
+       0x2153, 0x057e, 0x1078, 0x216a, 0x057f, 0x0040, 0x214e, 0x78e0,
+       0xa086, 0x0003, 0x0040, 0x2153, 0x0078, 0x2141, 0x8528, 0x78c8,
+       0xa005, 0x0040, 0x20f1, 0x85ff, 0x0040, 0x215a, 0x2091, 0x4080,
+       0x78b0, 0x70d6, 0x007c, 0x7bac, 0x79b0, 0x70d4, 0xa102, 0x00c0,
+       0x2164, 0x2300, 0xa005, 0x007c, 0x0048, 0x2168, 0xa302, 0x007c,
+       0x8002, 0x007c, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005, 0x00c8,
+       0x2184, 0x2091, 0x8000, 0x2071, 0x0020, 0x7004, 0xa005, 0x00c0,
+       0x21b9, 0x7008, 0x7208, 0xa206, 0x00c0, 0x21b9, 0xa286, 0x0008,
+       0x00c0, 0x21b9, 0x2071, 0x0010, 0x1078, 0x21be, 0x2009, 0x0020,
+       0x6004, 0xa086, 0x0103, 0x00c0, 0x2193, 0x6028, 0xa005, 0x00c0,
+       0x2193, 0x2009, 0x000c, 0x1078, 0x1924, 0x0040, 0x21ac, 0x78c4,
+       0x8000, 0x78c6, 0xa086, 0x0002, 0x00c0, 0x21b9, 0x2091, 0x8000,
+       0x78e3, 0x0003, 0x78c7, 0x0000, 0x78cc, 0xa085, 0x0300, 0x78ce,
+       0x2091, 0x8001, 0x0078, 0x21b9, 0x78c7, 0x0000, 0x1078, 0x1c86,
+       0x79ac, 0x78b0, 0x8000, 0xa10a, 0x00c8, 0x21b7, 0xa006, 0x78b2,
+       0xa006, 0x2071, 0x0010, 0x2091, 0x8001, 0x007c, 0x8107, 0x8004,
+       0x8004, 0x7ab8, 0x7bb4, 0x7cc0, 0x7dbc, 0xa210, 0xa399, 0x0000,
+       0xa4a1, 0x0000, 0xa5a9, 0x0000, 0x007c, 0x2009, 0x515b, 0x2091,
+       0x8000, 0x200a, 0x0f7e, 0x0e7e, 0x2071, 0x5140, 0x7000, 0xa086,
+       0x0000, 0x00c0, 0x21ec, 0x2009, 0x5112, 0x2104, 0xa005, 0x00c0,
+       0x21ec, 0x2079, 0x0100, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x21ec,
+       0x0018, 0x21ec, 0x781b, 0x004b, 0x0e7f, 0x0f7f, 0x007c, 0x0f7e,
+       0x0e7e, 0x2071, 0x5140, 0x2091, 0x8000, 0x7000, 0xa086, 0x0000,
+       0x00c0, 0x2205, 0x2079, 0x0100, 0x7830, 0xa084, 0x00c0, 0x00c0,
+       0x2205, 0x0018, 0x2205, 0x781b, 0x004d, 0x2091, 0x8001, 0x0e7f,
+       0x0f7f, 0x007c, 0x127e, 0x2091, 0x2300, 0x2071, 0x5140, 0x2079,
+       0x0100, 0x784b, 0x000f, 0x0098, 0x2218, 0x7838, 0x0078, 0x2211,
+       0x20a9, 0x0040, 0x7800, 0xa082, 0x0004, 0x0048, 0x2221, 0x20a9,
+       0x0060, 0x789b, 0x0000, 0x78af, 0x0000, 0x78af, 0x0000, 0x0070,
+       0x222b, 0x0078, 0x2223, 0x7800, 0xa082, 0x0004, 0x0048, 0x223a,
+       0x70b7, 0x0096, 0x2019, 0x4ee7, 0x1078, 0x2276, 0x702f, 0x8001,
+       0x0078, 0x2246, 0x70b7, 0x0000, 0x2019, 0x4d5f, 0x1078, 0x2276,
+       0x2019, 0x4d9e, 0x1078, 0x2276, 0x702f, 0x8000, 0x7003, 0x0000,
+       0x1078, 0x237f, 0x7004, 0xa084, 0x000f, 0x017e, 0x2009, 0x04fd,
+       0x210c, 0xa18a, 0x0005, 0x0048, 0x225b, 0x0038, 0x2261, 0xa085,
+       0x6280, 0x0078, 0x2263, 0x0028, 0x2261, 0xa085, 0x6280, 0x0078,
+       0x2263, 0xa085, 0x62c0, 0x017f, 0x7806, 0x780f, 0xb204, 0x7843,
+       0x00d8, 0x7853, 0x0080, 0x780b, 0x0008, 0x7047, 0x0008, 0x7053,
+       0x517f, 0x704f, 0x0000, 0x127f, 0x2000, 0x007c, 0x137e, 0x147e,
+       0x157e, 0x047e, 0x20a1, 0x012b, 0x2304, 0xa005, 0x789a, 0x0040,
+       0x2296, 0x8318, 0x2324, 0x8318, 0x2398, 0x24a8, 0xa484, 0xff00,
+       0x0040, 0x228e, 0xa482, 0x0100, 0x20a9, 0x0100, 0x2020, 0x53a6,
+       0xa005, 0x00c0, 0x2285, 0x3318, 0x0078, 0x227c, 0x047f, 0x157f,
+       0x147f, 0x137f, 0x007c, 0xa18c, 0x000f, 0x2011, 0x0101, 0x2204,
+       0xa084, 0xfff0, 0xa105, 0x2012, 0x1078, 0x237f, 0x007c, 0x2011,
+       0x0101, 0x20a9, 0x0009, 0x810b, 0x0070, 0x22b0, 0x0078, 0x22ab,
+       0xa18c, 0x0e00, 0x2204, 0xa084, 0xf1ff, 0xa105, 0x2012, 0x007c,
+       0x2009, 0x0101, 0x20a9, 0x0005, 0x8213, 0x0070, 0x22c1, 0x0078,
+       0x22bc, 0xa294, 0x00e0, 0x2104, 0xa084, 0xff1f, 0xa205, 0x200a,
+       0x007c, 0x2011, 0x0101, 0x20a9, 0x000c, 0x810b, 0x0070, 0x22d2,
+       0x0078, 0x22cd, 0xa18c, 0xf000, 0x2204, 0xa084, 0x0fff, 0xa105,
+       0x2012, 0x007c, 0x2011, 0x0102, 0x2204, 0xa084, 0xffcf, 0xa105,
+       0x2012, 0x007c, 0x8103, 0x8003, 0xa080, 0x0020, 0x0c7e, 0x2061,
+       0x0100, 0x609a, 0x62ac, 0x63ac, 0x0c7f, 0x007c, 0x8103, 0x8003,
+       0xa080, 0x0022, 0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4, 0xa084,
+       0xffdf, 0x60ae, 0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080, 0x0022,
+       0x0c7e, 0x2061, 0x0100, 0x609a, 0x60a4, 0xa085, 0x0020, 0x60ae,
+       0x0c7f, 0x007c, 0x8103, 0x8003, 0xa080, 0x0020, 0x0c7e, 0x2061,
+       0x0100, 0x609a, 0x60a4, 0x62ae, 0x2010, 0x60a4, 0x63ae, 0x2018,
+       0x0c7f, 0x007c, 0x2091, 0x8000, 0x0c7e, 0x0e7e, 0x6818, 0xa005,
+       0x0040, 0x235d, 0x2061, 0x7500, 0x1078, 0x2365, 0x0040, 0x2349,
+       0x20a9, 0x0000, 0x2061, 0x7400, 0x0c7e, 0x1078, 0x2365, 0x0040,
+       0x2339, 0x0c7f, 0x8c60, 0x0070, 0x2337, 0x0078, 0x232c, 0x0078,
+       0x235d, 0x007f, 0xa082, 0x7400, 0x2071, 0x5140, 0x7086, 0x7182,
+       0x2001, 0x0004, 0x706e, 0x7093, 0x000f, 0x1078, 0x21cd, 0x0078,
+       0x2359, 0x60c0, 0xa005, 0x00c0, 0x235d, 0x2071, 0x5140, 0x7182,
+       0x2c00, 0x708a, 0x2001, 0x0006, 0x706e, 0x7093, 0x000f, 0x1078,
+       0x21cd, 0x2001, 0x0000, 0x0078, 0x235f, 0x2001, 0x0001, 0x2091,
+       0x8001, 0xa005, 0x0e7f, 0x0c7f, 0x007c, 0x2c04, 0xa005, 0x0040,
+       0x237c, 0x2060, 0x600c, 0xa306, 0x00c0, 0x2379, 0x6010, 0xa206,
+       0x00c0, 0x2379, 0x6014, 0xa106, 0x00c0, 0x2379, 0xa006, 0x0078,
+       0x237e, 0x6000, 0x0078, 0x2366, 0xa085, 0x0001, 0x007c, 0x2011,
+       0x5141, 0x220c, 0xa18c, 0x000f, 0x2011, 0x013b, 0x2204, 0xa084,
+       0x0100, 0x0040, 0x2395, 0x2021, 0xff04, 0x2122, 0x810b, 0x810b,
+       0x810b, 0x810b, 0xa18d, 0x0f00, 0x2104, 0x007c, 0x0e7e, 0x68e4,
+       0xa08c, 0x0020, 0x0040, 0x23e9, 0xa084, 0x0006, 0x00c0, 0x23e9,
+       0x6014, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0f0,
+       0x5380, 0x7004, 0xa084, 0x000a, 0x00c0, 0x23e9, 0x7108, 0xa194,
+       0xff00, 0x0040, 0x23e9, 0xa18c, 0x00ff, 0x2001, 0x000c, 0xa106,
+       0x0040, 0x23d0, 0x2001, 0x0012, 0xa106, 0x0040, 0x23d4, 0x2001,
+       0x0014, 0xa106, 0x0040, 0x23d8, 0x2001, 0x0019, 0xa106, 0x0040,
+       0x23dc, 0x2001, 0x0032, 0xa106, 0x0040, 0x23e0, 0x0078, 0x23e4,
+       0x2009, 0x0012, 0x0078, 0x23e6, 0x2009, 0x0014, 0x0078, 0x23e6,
+       0x2009, 0x0019, 0x0078, 0x23e6, 0x2009, 0x0020, 0x0078, 0x23e6,
+       0x2009, 0x003f, 0x0078, 0x23e6, 0x2011, 0x0000, 0x2100, 0xa205,
+       0x700a, 0x0e7f, 0x007c, 0x0068, 0x23eb, 0x2091, 0x8000, 0x2071,
+       0x0000, 0x007e, 0x7018, 0xa084, 0x0001, 0x00c0, 0x23f2, 0x007f,
+       0x2071, 0x0010, 0x70ca, 0x007f, 0x70c6, 0x70c3, 0x8002, 0x70db,
+       0x0741, 0x70df, 0x0000, 0x2071, 0x0000, 0x701b, 0x0001, 0x2091,
+       0x4080, 0x0078, 0x2409, 0x107e, 0x007e, 0x127e, 0x2091, 0x2300,
+       0x7f3c, 0x7e58, 0x7c30, 0x7d38, 0x77c2, 0x74c6, 0x76ca, 0x75ce,
+       0xa594, 0x003f, 0xa49c, 0x0003, 0xa484, 0x000f, 0x0079, 0x2420,
+       0x2432, 0x2432, 0x2432, 0x276c, 0x393b, 0x2430, 0x2461, 0x246b,
+       0x2430, 0x2430, 0x2430, 0x2430, 0x2430, 0x2430, 0x2430, 0x2430,
+       0x1078, 0x23eb, 0x8507, 0xa084, 0x001f, 0x0079, 0x2437, 0x2475,
+       0x276c, 0x2926, 0x2a23, 0x2a4b, 0x2ced, 0x2f98, 0x2fdb, 0x3026,
+       0x30ab, 0x3163, 0x320c, 0x2461, 0x2848, 0x2f6d, 0x2457, 0x3cc8,
+       0x3ce8, 0x3eae, 0x3eba, 0x3f8f, 0x2457, 0x2457, 0x4062, 0x4066,
+       0x3cc6, 0x2457, 0x3e19, 0x2457, 0x3b8c, 0x246b, 0x2457, 0x1078,
+       0x23eb, 0x0018, 0x2410, 0x127f, 0x2091, 0x8001, 0x007f, 0x107f,
+       0x007c, 0x2019, 0x4e3b, 0x1078, 0x2276, 0x702f, 0x0001, 0x781b,
+       0x004f, 0x0078, 0x2459, 0x2019, 0x4d9e, 0x1078, 0x2276, 0x702f,
+       0x8000, 0x781b, 0x00d0, 0x0078, 0x2459, 0x7242, 0x2009, 0x510f,
+       0x200b, 0x0000, 0xa584, 0x0001, 0x00c0, 0x3ba0, 0x0040, 0x2492,
+       0x1078, 0x23eb, 0x7003, 0x0000, 0x704b, 0x0000, 0x7043, 0x0000,
+       0x7037, 0x0000, 0x1078, 0x3912, 0x0018, 0x2410, 0x2009, 0x510f,
+       0x200b, 0x0000, 0x7068, 0xa005, 0x00c0, 0x255d, 0x706c, 0xa084,
+       0x0007, 0x0079, 0x249b, 0x2594, 0x24a3, 0x24af, 0x24cc, 0x24ee,
+       0x253b, 0x2514, 0x24a3, 0x1078, 0x38fa, 0x2009, 0x0048, 0x1078,
+       0x2e39, 0x00c0, 0x24ad, 0x7003, 0x0004, 0x0078, 0x2459, 0x1078,
+       0x38fa, 0x00c0, 0x24ca, 0x7080, 0x8007, 0x7882, 0x789b, 0x0010,
+       0x78ab, 0x000c, 0x789b, 0x0060, 0x78ab, 0x0001, 0x785b, 0x0004,
+       0x2009, 0x00e0, 0x1078, 0x2e2d, 0x00c0, 0x24ca, 0x7003, 0x0004,
+       0x7093, 0x000f, 0x0078, 0x2459, 0x1078, 0x38fa, 0x00c0, 0x24ec,
+       0x7180, 0x8107, 0x7882, 0x789b, 0x0010, 0xa18c, 0x001f, 0xa18d,
+       0x00c0, 0x79aa, 0x78ab, 0x0006, 0x789b, 0x0060, 0x78ab, 0x0002,
+       0x785b, 0x0004, 0x2009, 0x00e0, 0x1078, 0x2e2d, 0x00c0, 0x24ec,
+       0x7003, 0x0004, 0x7093, 0x000f, 0x0078, 0x2459, 0x1078, 0x38fa,
+       0x00c0, 0x2512, 0x7180, 0x8107, 0x7882, 0x789b, 0x0010, 0xa18c,
+       0x001f, 0xa18d, 0x00c0, 0x79aa, 0x78ab, 0x0020, 0x7184, 0x79aa,
+       0x78ab, 0x000d, 0x789b, 0x0060, 0x78ab, 0x0004, 0x785b, 0x0004,
+       0x2009, 0x00e0, 0x1078, 0x2e2d, 0x00c0, 0x2512, 0x7003, 0x0004,
+       0x7093, 0x000f, 0x0078, 0x2459, 0x1078, 0x38fa, 0x00c0, 0x2539,
+       0x7180, 0x8107, 0x7882, 0x789b, 0x0010, 0xa18c, 0x001f, 0xa18d,
+       0x00c0, 0x79aa, 0x78ab, 0x0006, 0x789b, 0x0060, 0x78ab, 0x0002,
+       0x785b, 0x0004, 0x2009, 0x00e0, 0x1078, 0x2e2d, 0x00c0, 0x2539,
+       0x7088, 0x708b, 0x0000, 0x2068, 0x704a, 0x7003, 0x0002, 0x7093,
+       0x000f, 0x0078, 0x2459, 0x1078, 0x38fa, 0x00c0, 0x2459, 0x7088,
+       0x2068, 0x6f14, 0x1078, 0x37ef, 0x2c50, 0x1078, 0x39ac, 0x789b,
+       0x0010, 0x6814, 0xa084, 0x001f, 0xa085, 0x0080, 0x78aa, 0x6e1c,
+       0x2041, 0x0001, 0x708c, 0xa084, 0x0400, 0x2001, 0x0004, 0x0040,
+       0x255b, 0x2001, 0x0006, 0x0078, 0x267c, 0x1078, 0x38fa, 0x00c0,
+       0x2459, 0x789b, 0x0010, 0x7068, 0x2068, 0x6f14, 0x1078, 0x37ef,
+       0x2c50, 0x1078, 0x39ac, 0x6008, 0xa085, 0x0010, 0x600a, 0x6824,
+       0xa005, 0x0040, 0x257b, 0xa082, 0x0006, 0x0048, 0x2579, 0x0078,
+       0x257b, 0x6827, 0x0005, 0x6b14, 0xa39c, 0x001f, 0xa39d, 0x00c0,
+       0x7058, 0xa084, 0x8000, 0x0040, 0x2589, 0xa684, 0x0001, 0x0040,
+       0x258b, 0xa39c, 0xffbf, 0x7baa, 0x2031, 0x0020, 0x2041, 0x0001,
+       0x2001, 0x0003, 0x0078, 0x267c, 0x0018, 0x2410, 0x744c, 0xa485,
+       0x0000, 0x0040, 0x25ae, 0xa080, 0x5180, 0x2030, 0x7150, 0x8108,
+       0xa12a, 0x0048, 0x25a5, 0x2009, 0x5180, 0x2164, 0x6504, 0x85ff,
+       0x00c0, 0x25bf, 0x8421, 0x00c0, 0x259f, 0x7152, 0x7003, 0x0000,
+       0x704b, 0x0000, 0x7040, 0xa005, 0x0040, 0x3ba0, 0x0078, 0x2459,
+       0x764c, 0xa6b0, 0x5180, 0x7150, 0x2600, 0x0078, 0x25aa, 0x7152,
+       0x2568, 0x2558, 0x754a, 0x2c50, 0x6034, 0xa085, 0x0000, 0x00c0,
+       0x25bc, 0x6708, 0x773a, 0xa784, 0x033f, 0x0040, 0x25f5, 0xa784,
+       0x0021, 0x00c0, 0x25bc, 0xa784, 0x0002, 0x0040, 0x25de, 0xa784,
+       0x0004, 0x0040, 0x25bc, 0xa7bc, 0xfffb, 0x670a, 0xa784, 0x0008,
+       0x00c0, 0x25bc, 0xa784, 0x0010, 0x00c0, 0x25bc, 0xa784, 0x0200,
+       0x00c0, 0x25bc, 0xa784, 0x0100, 0x0040, 0x25f5, 0x6018, 0xa005,
+       0x00c0, 0x25bc, 0xa7bc, 0xfeff, 0x670a, 0x6823, 0x0000, 0x6e1c,
+       0xa684, 0x000e, 0x6118, 0x0040, 0x2605, 0x601c, 0xa102, 0x0048,
+       0x2608, 0x0040, 0x2608, 0x0078, 0x25b8, 0x81ff, 0x00c0, 0x25b8,
+       0x68c3, 0x0000, 0xa784, 0x0080, 0x00c0, 0x2610, 0x700c, 0x6022,
+       0xa7bc, 0xff7f, 0x670a, 0x1078, 0x39ac, 0x0018, 0x2410, 0x789b,
+       0x0010, 0xa046, 0x1078, 0x38fa, 0x00c0, 0x2459, 0x6b14, 0xa39c,
+       0x001f, 0xa39d, 0x00c0, 0x7058, 0xa084, 0x8000, 0x0040, 0x262c,
+       0xa684, 0x0001, 0x0040, 0x262e, 0xa39c, 0xffbf, 0xa684, 0x0010,
+       0x0040, 0x2634, 0xa39d, 0x0020, 0x7baa, 0x8840, 0xa684, 0x000e,
+       0x00c0, 0x263f, 0xa7bd, 0x0010, 0x670a, 0x0078, 0x267a, 0x7158,
+       0xa18c, 0x0800, 0x0040, 0x3401, 0x2011, 0x0020, 0xa684, 0x0008,
+       0x00c0, 0x2650, 0x8210, 0xa684, 0x0002, 0x00c0, 0x2650, 0x8210,
+       0x7aaa, 0x8840, 0x1078, 0x3912, 0x6a14, 0x610c, 0x8108, 0xa18c,
+       0x00ff, 0xa1e0, 0x7400, 0x2c64, 0x8cff, 0x0040, 0x2671, 0x6014,
+       0xa206, 0x00c0, 0x265b, 0x60b8, 0x8001, 0x60ba, 0x00c0, 0x2656,
+       0x0c7e, 0x2a60, 0x6008, 0xa085, 0x0100, 0x600a, 0x0c7f, 0x0078,
+       0x2594, 0x1078, 0x38fa, 0x00c0, 0x2459, 0x2a60, 0x610e, 0x79aa,
+       0x8840, 0x7132, 0x2001, 0x0001, 0x007e, 0x715c, 0xa184, 0x0018,
+       0x0040, 0x2697, 0xa184, 0x0010, 0x0040, 0x268a, 0x1078, 0x3604,
+       0x00c0, 0x26ba, 0xa184, 0x0008, 0x0040, 0x2697, 0x69a0, 0xa184,
+       0x0600, 0x00c0, 0x2697, 0x1078, 0x34f1, 0x0078, 0x26ba, 0x69a0,
+       0xa184, 0x0800, 0x0040, 0x26ae, 0x0c7e, 0x027e, 0x2960, 0x6000,
+       0xa085, 0x2000, 0x6002, 0x6104, 0xa18d, 0x0010, 0x6106, 0x027f,
+       0x0c7f, 0x1078, 0x3604, 0x00c0, 0x26ba, 0x69a0, 0xa184, 0x0200,
+       0x0040, 0x26b6, 0x1078, 0x3540, 0x0078, 0x26ba, 0xa184, 0x0400,
+       0x00c0, 0x2693, 0x69a0, 0xa184, 0x1000, 0x0040, 0x26c5, 0x6914,
+       0xa18c, 0xff00, 0x810f, 0x1078, 0x22ee, 0x007f, 0x7002, 0xa68c,
+       0x00e0, 0xa684, 0x0060, 0x0040, 0x26d3, 0xa086, 0x0060, 0x00c0,
+       0x26d3, 0xa18d, 0x4000, 0x88ff, 0x0040, 0x26d8, 0xa18d, 0x0004,
+       0x795a, 0x69b6, 0x789b, 0x0060, 0x2800, 0x78aa, 0x789b, 0x0061,
+       0x6818, 0xa08d, 0x8000, 0xa084, 0x7fff, 0x691a, 0xa68c, 0x0080,
+       0x0040, 0x26f7, 0x7097, 0x0000, 0xa08a, 0x000d, 0x0050, 0x26f5,
+       0xa08a, 0x000c, 0x7196, 0x2001, 0x000c, 0x800c, 0x719a, 0x78aa,
+       0x8008, 0x810c, 0x0040, 0x3407, 0xa18c, 0x00f8, 0x00c0, 0x3407,
+       0x157e, 0x137e, 0x147e, 0x20a1, 0x012b, 0x789b, 0x0000, 0x8000,
+       0x80ac, 0xad80, 0x000b, 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f,
+       0x6814, 0x8007, 0x7882, 0x6d94, 0x7dd6, 0x7dde, 0x6e98, 0x7ed2,
+       0x7eda, 0x1078, 0x38fa, 0x00c0, 0x272e, 0x702c, 0x8003, 0x0048,
+       0x2727, 0x2019, 0x4d9e, 0x1078, 0x2276, 0x702f, 0x8000, 0x7830,
+       0xa084, 0x00c0, 0x00c0, 0x272e, 0x0098, 0x2736, 0x6008, 0xa084,
+       0xffef, 0x600a, 0x1078, 0x3912, 0x0078, 0x2482, 0x7200, 0xa284,
+       0x0007, 0xa086, 0x0001, 0x00c0, 0x2743, 0x781b, 0x004f, 0x1078,
+       0x3912, 0x0078, 0x2754, 0x6ab4, 0xa295, 0x2000, 0x7a5a, 0x781b,
+       0x004f, 0x1078, 0x3912, 0x7200, 0x2500, 0xa605, 0x0040, 0x2754,
+       0xa284, 0x0007, 0x1079, 0x2762, 0xad80, 0x0009, 0x7036, 0xa284,
+       0x0007, 0xa086, 0x0001, 0x00c0, 0x2459, 0x6018, 0x8000, 0x601a,
+       0x0078, 0x2459, 0x276a, 0x4a3a, 0x4a3a, 0x4a29, 0x4a3a, 0x276a,
+       0x4a29, 0x276a, 0x1078, 0x23eb, 0x1078, 0x38fa, 0x0f7e, 0x2079,
+       0x5100, 0x78cc, 0x0f7f, 0xa084, 0x0001, 0x0040, 0x2790, 0x706c,
+       0xa086, 0x0001, 0x00c0, 0x277f, 0x706e, 0x0078, 0x2823, 0x706c,
+       0xa086, 0x0005, 0x00c0, 0x278e, 0x7088, 0x2068, 0x681b, 0x0004,
+       0x6817, 0x0000, 0x6820, 0xa085, 0x0008, 0x6822, 0x706f, 0x0000,
+       0x2011, 0x0004, 0x716c, 0xa186, 0x0001, 0x0040, 0x27b1, 0xa186,
+       0x0007, 0x00c0, 0x27a1, 0x2009, 0x5138, 0x200b, 0x0005, 0x0078,
+       0x27b1, 0x2009, 0x5113, 0x2104, 0x2009, 0x5112, 0x200a, 0x2009,
+       0x5138, 0x200b, 0x0001, 0x706f, 0x0000, 0x7073, 0x0001, 0x0078,
+       0x27b3, 0x706f, 0x0000, 0x1078, 0x4776, 0x157e, 0x20a9, 0x0010,
+       0x2039, 0x0000, 0x1078, 0x36e2, 0xa7b8, 0x0100, 0x0070, 0x27c2,
+       0x0078, 0x27ba, 0x157f, 0x7000, 0x0079, 0x27c6, 0x27f4, 0x27db,
+       0x27db, 0x27ce, 0x27f4, 0x27f4, 0x27f4, 0x27f4, 0x2021, 0x515a,
+       0x2404, 0xa005, 0x0040, 0x27f4, 0xad06, 0x00c0, 0x27db, 0x6800,
+       0x2022, 0x0078, 0x27eb, 0x6820, 0xa084, 0x0001, 0x00c0, 0x27e7,
+       0x6f14, 0x1078, 0x37ef, 0x1078, 0x33d8, 0x0078, 0x27eb, 0x7060,
+       0x2060, 0x6800, 0x6002, 0x6a1a, 0x6817, 0x0000, 0x6820, 0xa085,
+       0x0008, 0x6822, 0x1078, 0x1c70, 0x2021, 0x7500, 0x1078, 0x2830,
+       0x2021, 0x515a, 0x1078, 0x2830, 0x157e, 0x20a9, 0x0000, 0x2021,
+       0x7400, 0x1078, 0x2830, 0x8420, 0x0070, 0x2808, 0x0078, 0x2801,
+       0x2061, 0x5400, 0x2021, 0x0002, 0x20a9, 0x0100, 0x6018, 0x6110,
+       0x81ff, 0x0040, 0x2817, 0xa102, 0x0050, 0x2817, 0x6012, 0x601b,
+       0x0000, 0xace0, 0x0010, 0x0070, 0x281f, 0x0078, 0x280e, 0x8421,
+       0x00c0, 0x280c, 0x157f, 0x709c, 0xa084, 0x8000, 0x0040, 0x282a,
+       0x1078, 0x3a00, 0x7003, 0x0000, 0x704b, 0x0000, 0x0078, 0x2459,
+       0x047e, 0x2404, 0xa005, 0x0040, 0x2844, 0x2068, 0x6800, 0x007e,
+       0x6a1a, 0x6817, 0x0000, 0x6820, 0xa085, 0x0008, 0x6822, 0x1078,
+       0x1c70, 0x007f, 0x0078, 0x2832, 0x047f, 0x2023, 0x0000, 0x007c,
+       0xa282, 0x0003, 0x0050, 0x284e, 0x1078, 0x23eb, 0x2300, 0x0079,
+       0x2851, 0x2854, 0x28c7, 0x28e4, 0xa282, 0x0002, 0x0040, 0x285a,
+       0x1078, 0x23eb, 0x706c, 0x706f, 0x0000, 0x7093, 0x0000, 0x0079,
+       0x2861, 0x2869, 0x2869, 0x286b, 0x289f, 0x340d, 0x2869, 0x289f,
+       0x2869, 0x1078, 0x23eb, 0x7780, 0x1078, 0x36e2, 0x7780, 0xa7bc,
+       0x0f00, 0x1078, 0x37ef, 0x6018, 0xa005, 0x0040, 0x2896, 0x2021,
+       0x7500, 0x2009, 0x0004, 0x2011, 0x0010, 0x1078, 0x28ff, 0x0040,
+       0x2896, 0x157e, 0x20a9, 0x0000, 0x2021, 0x7400, 0x047e, 0x2009,
+       0x0004, 0x2011, 0x0010, 0x1078, 0x28ff, 0x047f, 0x0040, 0x2895,
+       0x8420, 0x0070, 0x2895, 0x0078, 0x2886, 0x157f, 0x8738, 0xa784,
+       0x001f, 0x00c0, 0x2871, 0x0078, 0x2482, 0x0078, 0x2482, 0x7780,
+       0x1078, 0x37ef, 0x6018, 0xa005, 0x0040, 0x28c5, 0x2021, 0x7500,
+       0x2009, 0x0005, 0x2011, 0x0020, 0x1078, 0x28ff, 0x0040, 0x28c5,
+       0x157e, 0x20a9, 0x0000, 0x2021, 0x7400, 0x047e, 0x2009, 0x0005,
+       0x2011, 0x0020, 0x1078, 0x28ff, 0x047f, 0x0040, 0x28c4, 0x8420,
+       0x0070, 0x28c4, 0x0078, 0x28b5, 0x157f, 0x0078, 0x2482, 0x2200,
+       0x0079, 0x28ca, 0x28cd, 0x28cf, 0x28cf, 0x1078, 0x23eb, 0x2009,
+       0x0012, 0x706c, 0xa086, 0x0002, 0x0040, 0x28d8, 0x2009, 0x000e,
+       0x6818, 0xa084, 0x8000, 0x0040, 0x28de, 0x691a, 0x706f, 0x0000,
+       0x7073, 0x0001, 0x0078, 0x3888, 0x2200, 0x0079, 0x28e7, 0x28ec,
+       0x28cf, 0x28ea, 0x1078, 0x23eb, 0x1078, 0x4776, 0x7000, 0xa086,
+       0x0001, 0x00c0, 0x339d, 0x1078, 0x33ee, 0x6008, 0xa084, 0xffef,
+       0x600a, 0x1078, 0x3390, 0x0040, 0x339d, 0x0078, 0x2594, 0x2404,
+       0xa005, 0x0040, 0x2922, 0x2068, 0x2d04, 0x007e, 0x6814, 0xa706,
+       0x0040, 0x290e, 0x2d20, 0x007f, 0x0078, 0x2900, 0x007f, 0x2022,
+       0x691a, 0x6817, 0x0000, 0x6820, 0xa205, 0x6822, 0x1078, 0x1c70,
+       0x6010, 0x8001, 0x6012, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078,
+       0x33ee, 0x007c, 0xa085, 0x0001, 0x0078, 0x2921, 0x2300, 0x0079,
+       0x2929, 0x292e, 0x292c, 0x29c7, 0x1078, 0x23eb, 0x78ec, 0xa084,
+       0x0001, 0x00c0, 0x2942, 0x7000, 0xa086, 0x0004, 0x00c0, 0x293a,
+       0x0078, 0x2965, 0x1078, 0x33ee, 0x6008, 0xa084, 0xffef, 0x600a,
+       0x0078, 0x339d, 0x78e4, 0xa005, 0x00d0, 0x2965, 0x0018, 0x2459,
+       0x2008, 0xa084, 0x0030, 0x00c0, 0x2951, 0x781b, 0x004f, 0x0078,
+       0x2459, 0x78ec, 0xa084, 0x0003, 0x0040, 0x294d, 0x2100, 0xa084,
+       0x0007, 0x0079, 0x295b, 0x299e, 0x29a9, 0x298f, 0x2963, 0x38ed,
+       0x38ed, 0x2963, 0x29b8, 0x1078, 0x23eb, 0x7000, 0xa086, 0x0004,
+       0x00c0, 0x297f, 0x706c, 0xa086, 0x0002, 0x00c0, 0x2975, 0x2011,
+       0x0002, 0x2019, 0x0000, 0x0078, 0x2848, 0x706c, 0xa086, 0x0006,
+       0x0040, 0x296f, 0x706c, 0xa086, 0x0004, 0x0040, 0x296f, 0x79e4,
+       0xa184, 0x0030, 0x0040, 0x2989, 0x78ec, 0xa084, 0x0003, 0x00c0,
+       0x298b, 0x0078, 0x2f6d, 0x2001, 0x0003, 0x0078, 0x2d01, 0x6818,
+       0xa084, 0x8000, 0x0040, 0x2996, 0x681b, 0x001d, 0x1078, 0x36c1,
+       0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2459, 0x6818, 0xa084,
+       0x8000, 0x0040, 0x29a5, 0x681b, 0x001d, 0x1078, 0x36c1, 0x0078,
+       0x38b8, 0x6818, 0xa084, 0x8000, 0x0040, 0x29b0, 0x681b, 0x001d,
+       0x1078, 0x36c1, 0x782b, 0x3008, 0x781b, 0x00cd, 0x0078, 0x2459,
+       0x6818, 0xa084, 0x8000, 0x0040, 0x29bf, 0x681b, 0x001d, 0x1078,
+       0x36c1, 0x782b, 0x3008, 0x781b, 0x008e, 0x0078, 0x2459, 0xa584,
+       0x000f, 0x00c0, 0x29e4, 0x7000, 0x0079, 0x29ce, 0x2482, 0x29d8,
+       0x29d6, 0x339d, 0x339d, 0x339d, 0x339d, 0x29d6, 0x1078, 0x23eb,
+       0x1078, 0x33ee, 0x6008, 0xa084, 0xffef, 0x600a, 0x1078, 0x3390,
+       0x0040, 0x339d, 0x0078, 0x2594, 0x78e4, 0xa005, 0x00d0, 0x2965,
+       0x0018, 0x2965, 0x2008, 0xa084, 0x0030, 0x00c0, 0x29f3, 0x781b,
+       0x004f, 0x0078, 0x2459, 0x78ec, 0xa084, 0x0003, 0x0040, 0x29ef,
+       0x2100, 0xa184, 0x0007, 0x0079, 0x29fd, 0x2a0f, 0x2a13, 0x2a07,
+       0x2a05, 0x38ed, 0x38ed, 0x2a05, 0x38e3, 0x1078, 0x23eb, 0x1078,
+       0x36c9, 0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2459, 0x1078,
+       0x36c9, 0x0078, 0x38b8, 0x1078, 0x36c9, 0x782b, 0x3008, 0x781b,
+       0x00cd, 0x0078, 0x2459, 0x1078, 0x36c9, 0x782b, 0x3008, 0x781b,
+       0x008e, 0x0078, 0x2459, 0x2300, 0x0079, 0x2a26, 0x2a2b, 0x2a29,
+       0x2a2d, 0x1078, 0x23eb, 0x0078, 0x30ab, 0x681b, 0x0008, 0x78a3,
+       0x0000, 0x79e4, 0xa184, 0x0030, 0x0040, 0x30ab, 0x78ec, 0xa084,
+       0x0003, 0x0040, 0x30ab, 0xa184, 0x0007, 0x0079, 0x2a3f, 0x2a47,
+       0x2a13, 0x298f, 0x3888, 0x38ed, 0x38ed, 0x2a47, 0x38e3, 0x1078,
+       0x389c, 0x0078, 0x2459, 0xa282, 0x0005, 0x0050, 0x2a51, 0x1078,
+       0x23eb, 0x2300, 0x0079, 0x2a54, 0x2a57, 0x2cae, 0x2cbc, 0x2200,
+       0x0079, 0x2a5a, 0x2a74, 0x2a61, 0x2a74, 0x2a5f, 0x2c93, 0x1078,
+       0x23eb, 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff, 0xa082, 0x0020,
+       0x0048, 0x369d, 0xa08a, 0x0004, 0x00c8, 0x369d, 0x0079, 0x2a70,
+       0x369d, 0x369d, 0x369d, 0x364b, 0x789b, 0x0018, 0x79a8, 0xa184,
+       0x0080, 0x0040, 0x2a85, 0x0078, 0x369d, 0x7000, 0xa005, 0x00c0,
+       0x2a7b, 0x2011, 0x0004, 0x0078, 0x321f, 0xa184, 0x00ff, 0xa08a,
+       0x0010, 0x00c8, 0x369d, 0x0079, 0x2a8d, 0x2a9f, 0x2a9d, 0x2ab7,
+       0x2abb, 0x2b78, 0x369d, 0x369d, 0x2b7a, 0x369d, 0x369d, 0x2c8f,
+       0x2c8f, 0x369d, 0x369d, 0x369d, 0x2c91, 0x1078, 0x23eb, 0xa684,
+       0x1000, 0x0040, 0x2aac, 0x2001, 0x0500, 0x8000, 0x8000, 0x783a,
+       0x781b, 0x008c, 0x0078, 0x2459, 0x6818, 0xa084, 0x8000, 0x0040,
+       0x2ab5, 0x681b, 0x001d, 0x0078, 0x2aa3, 0x0078, 0x3888, 0x681b,
+       0x001d, 0x0078, 0x36ad, 0x6920, 0x6922, 0xa684, 0x1800, 0x00c0,
+       0x2afc, 0x6820, 0xa084, 0x0001, 0x00c0, 0x2b04, 0x6818, 0xa086,
+       0x0008, 0x00c0, 0x2acd, 0x681b, 0x0000, 0xa684, 0x0400, 0x0040,
+       0x2b74, 0xa684, 0x0080, 0x0040, 0x2af8, 0x7097, 0x0000, 0x6818,
+       0xa084, 0x003f, 0xa08a, 0x000d, 0x0050, 0x2af8, 0xa08a, 0x000c,
+       0x7196, 0x2001, 0x000c, 0x800c, 0x719a, 0x789b, 0x0061, 0x78aa,
+       0x157e, 0x137e, 0x147e, 0x20a1, 0x012b, 0x789b, 0x0000, 0x8000,
+       0x80ac, 0xad80, 0x000b, 0x2098, 0x53a6, 0x147f, 0x137f, 0x157f,
+       0x781b, 0x0058, 0x0078, 0x2459, 0xa684, 0x1000, 0x0040, 0x2b04,
+       0x781b, 0x0065, 0x0078, 0x2459, 0xa684, 0x0060, 0x0040, 0x2b70,
+       0xa684, 0x0800, 0x0040, 0x2b70, 0xa684, 0x8000, 0x00c0, 0x2b12,
+       0x0078, 0x2b2c, 0xa6b4, 0x7fff, 0x7e5a, 0x6eb6, 0x789b, 0x0076,
+       0x7aac, 0x79ac, 0x78ac, 0x801b, 0x00c8, 0x2b1f, 0x8000, 0xa084,
+       0x003f, 0xa108, 0xa291, 0x0000, 0x6b98, 0x2100, 0xa302, 0x68b2,
+       0x6b94, 0x2200, 0xa303, 0x68ae, 0xa684, 0x4000, 0x0040, 0x2b34,
+       0xa6b4, 0xbfff, 0x7e5a, 0x6eb6, 0x7000, 0xa086, 0x0003, 0x00c0,
+       0x2b41, 0x1078, 0x482c, 0x1078, 0x4a29, 0x781b, 0x0064, 0x0078,
+       0x2459, 0xa006, 0x1078, 0x4b30, 0x6ab0, 0x69ac, 0x6c98, 0x6b94,
+       0x2200, 0xa105, 0x0040, 0x2b50, 0x2200, 0xa422, 0x2100, 0xa31b,
+       0x6caa, 0x7cd2, 0x7cda, 0x6ba6, 0x7bd6, 0x7bde, 0x2300, 0xa405,
+       0x00c0, 0x2b62, 0xa6b5, 0x4000, 0x7e5a, 0x6eb6, 0x781b, 0x0064,
+       0x0078, 0x2459, 0x781b, 0x0064, 0x2200, 0xa115, 0x00c0, 0x2b6c,
+       0x1078, 0x4a3a, 0x0078, 0x2459, 0x1078, 0x4a85, 0x0078, 0x2459,
+       0x781b, 0x0065, 0x0078, 0x2459, 0x781b, 0x0058, 0x0078, 0x2459,
+       0x1078, 0x23eb, 0x0078, 0x2bdb, 0x6920, 0xa184, 0x0100, 0x0040,
+       0x2b92, 0xa18c, 0xfeff, 0x6922, 0x0c7e, 0x7054, 0x2060, 0x6000,
+       0xa084, 0xefff, 0x6002, 0x6004, 0xa084, 0xfff5, 0x6006, 0x0c7f,
+       0x0078, 0x2bca, 0xa184, 0x0200, 0x0040, 0x2bca, 0xa18c, 0xfdff,
+       0x6922, 0x0c7e, 0x7054, 0x2060, 0x6000, 0xa084, 0xdfff, 0x6002,
+       0x6004, 0xa084, 0xffef, 0x6006, 0x2008, 0x2c48, 0x0c7f, 0xa184,
+       0x0008, 0x0040, 0x2bca, 0x1078, 0x37eb, 0x1078, 0x34f1, 0x88ff,
+       0x0040, 0x2bca, 0x789b, 0x0060, 0x2800, 0x78aa, 0x7e58, 0xa6b5,
+       0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x2bc4, 0x782b, 0x3008,
+       0x781b, 0x0056, 0x0078, 0x2459, 0x782b, 0x3008, 0x781b, 0x0065,
+       0x0078, 0x2459, 0x7e58, 0xa684, 0x0400, 0x00c0, 0x2bd3, 0x781b,
+       0x0058, 0x0078, 0x2459, 0x781b, 0x0065, 0x0078, 0x2459, 0x0078,
+       0x36a5, 0x0078, 0x36a5, 0x2019, 0x0000, 0x7990, 0xa18c, 0x0007,
+       0x00c0, 0x2be9, 0x6820, 0xa084, 0x0100, 0x0040, 0x2bd9, 0x2009,
+       0x0008, 0x789b, 0x0010, 0x78a8, 0xa094, 0x00ff, 0xa286, 0x0001,
+       0x00c0, 0x2c20, 0x2300, 0x7ca8, 0xa400, 0x2018, 0xa102, 0x0040,
+       0x2c18, 0x0048, 0x2bfd, 0x0078, 0x2c1a, 0xa380, 0x0002, 0xa102,
+       0x00c8, 0x2c18, 0x6920, 0xa18c, 0xfcff, 0x6922, 0x0c7e, 0x7054,
+       0x2060, 0x6000, 0xa084, 0xefef, 0x6002, 0x6004, 0xa084, 0xffe5,
+       0x6006, 0x0c7f, 0x7e58, 0xa6b4, 0xfffb, 0x7e5a, 0x0078, 0x2bcb,
+       0x0078, 0x2b7c, 0x24a8, 0x7aa8, 0x00f0, 0x2c1a, 0x0078, 0x2beb,
+       0xa284, 0x00f0, 0xa086, 0x0020, 0x00c0, 0x2c80, 0x8318, 0x8318,
+       0x2300, 0xa102, 0x0040, 0x2c30, 0x0048, 0x2c30, 0x0078, 0x2c7d,
+       0xa286, 0x0023, 0x0040, 0x2bd9, 0x681c, 0xa084, 0xfff1, 0x681e,
+       0x7e58, 0xa684, 0xfff1, 0xa085, 0x0010, 0x2030, 0x7e5a, 0x6008,
+       0xa085, 0x0010, 0x600a, 0x0c7e, 0x7054, 0x2060, 0x6004, 0x2008,
+       0x2c48, 0x0c7f, 0xa184, 0x0010, 0x0040, 0x2c54, 0x1078, 0x37eb,
+       0x1078, 0x3604, 0x0078, 0x2c63, 0x0c7e, 0x7054, 0x2060, 0x6004,
+       0x2008, 0x2c48, 0x0c7f, 0xa184, 0x0008, 0x0040, 0x2bca, 0x1078,
+       0x37eb, 0x1078, 0x34f1, 0x88ff, 0x0040, 0x2bca, 0x789b, 0x0060,
+       0x2800, 0x78aa, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0,
+       0x2c77, 0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2459, 0x782b,
+       0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x7aa8, 0x0078, 0x2beb,
+       0x8318, 0x2300, 0xa102, 0x0040, 0x2c89, 0x0048, 0x2c89, 0x0078,
+       0x2beb, 0xa284, 0x0080, 0x00c0, 0x36ad, 0x0078, 0x36a5, 0x0078,
+       0x36ad, 0x0078, 0x369d, 0x789b, 0x0018, 0x78a8, 0xa084, 0x00ff,
+       0xa08e, 0x0001, 0x0040, 0x2c9e, 0x1078, 0x23eb, 0x7aa8, 0xa294,
+       0x00ff, 0x78a8, 0xa084, 0x00ff, 0xa08a, 0x0004, 0x00c8, 0x369d,
+       0x0079, 0x2caa, 0x369d, 0x343e, 0x369d, 0x3599, 0xa282, 0x0000,
+       0x00c0, 0x2cb4, 0x1078, 0x23eb, 0x1078, 0x36c1, 0x782b, 0x3008,
+       0x781b, 0x0065, 0x0078, 0x2459, 0xa282, 0x0003, 0x00c0, 0x2cc2,
+       0x1078, 0x23eb, 0xa484, 0x8000, 0x00c0, 0x2ce5, 0x706c, 0xa005,
+       0x0040, 0x2ccc, 0x1078, 0x23eb, 0x6f14, 0x7782, 0xa7bc, 0x0f00,
+       0x1078, 0x37ef, 0x6008, 0xa085, 0x0021, 0x600a, 0x8738, 0xa784,
+       0x001f, 0x00c0, 0x2cd0, 0x1078, 0x36c5, 0x706f, 0x0002, 0x2009,
+       0x5138, 0x200b, 0x0009, 0x0078, 0x2ce7, 0x1078, 0x36d1, 0x782b,
+       0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0xa282, 0x0004, 0x0050,
+       0x2cf3, 0x1078, 0x23eb, 0x2300, 0x0079, 0x2cf6, 0x2cf9, 0x2de2,
+       0x2e15, 0xa286, 0x0003, 0x0040, 0x2cff, 0x1078, 0x23eb, 0x2001,
+       0x0000, 0x007e, 0x68c0, 0xa005, 0x0040, 0x2d08, 0x7003, 0x0003,
+       0x68a0, 0xa084, 0x2000, 0x0040, 0x2d11, 0x6008, 0xa085, 0x0002,
+       0x600a, 0x007f, 0x703e, 0x7000, 0xa084, 0x0007, 0x0079, 0x2d18,
+       0x2482, 0x2d22, 0x2d22, 0x2f17, 0x2f53, 0x2482, 0x2f53, 0x2d20,
+       0x1078, 0x23eb, 0xa684, 0x1000, 0x00c0, 0x2d2a, 0x1078, 0x4776,
+       0x0040, 0x2dbc, 0x7868, 0xa08c, 0x00ff, 0x0040, 0x2d72, 0xa186,
+       0x0008, 0x00c0, 0x2d41, 0x1078, 0x33ee, 0x6008, 0xa084, 0xffef,
+       0x600a, 0x1078, 0x3390, 0x0040, 0x2d72, 0x1078, 0x4776, 0x0078,
+       0x2d59, 0xa186, 0x0028, 0x00c0, 0x2d72, 0x1078, 0x4776, 0x6008,
+       0xa084, 0xffef, 0x600a, 0x6018, 0xa005, 0x0040, 0x2d59, 0x8001,
+       0x601a, 0xa005, 0x0040, 0x2d59, 0x8001, 0xa005, 0x0040, 0x2d59,
+       0x601e, 0x6820, 0xa084, 0x0001, 0x0040, 0x2482, 0x6820, 0xa084,
+       0xfffe, 0x6822, 0x7060, 0x0c7e, 0x2060, 0x6800, 0x6002, 0x0c7f,
+       0x6004, 0x6802, 0xa005, 0x2d00, 0x00c0, 0x2d6f, 0x6002, 0x6006,
+       0x0078, 0x2482, 0x017e, 0x1078, 0x2e46, 0x017f, 0xa684, 0xdf00,
+       0x681e, 0x682b, 0x0000, 0x6f14, 0x81ff, 0x0040, 0x2dbc, 0xa186,
+       0x0002, 0x00c0, 0x2dbc, 0xa684, 0x0800, 0x00c0, 0x2d8f, 0xa684,
+       0x0060, 0x0040, 0x2d8f, 0x78d8, 0x7adc, 0x682e, 0x6a32, 0x6820,
+       0xa084, 0x0800, 0x00c0, 0x2dbc, 0x8717, 0xa294, 0x000f, 0x8213,
+       0x8213, 0x8213, 0xa290, 0x5380, 0xa290, 0x0000, 0x221c, 0xa384,
+       0x0100, 0x00c0, 0x2da5, 0x0078, 0x2dab, 0x8210, 0x2204, 0xa085,
+       0x0018, 0x2012, 0x8211, 0xa384, 0x0400, 0x0040, 0x2db8, 0x68a0,
+       0xa084, 0x0100, 0x00c0, 0x2db8, 0x1078, 0x2eca, 0x0078, 0x2482,
+       0x6008, 0xa085, 0x0002, 0x600a, 0x6916, 0x6818, 0xa084, 0x8000,
+       0x0040, 0x2dc4, 0x703c, 0x681a, 0xa68c, 0xdf00, 0x691e, 0x1078,
+       0x33df, 0x1078, 0x33ee, 0x00c0, 0x2dd1, 0x6008, 0xa084, 0xffef,
+       0x600a, 0x6820, 0xa084, 0x0001, 0x00c0, 0x2dda, 0x1078, 0x33d8,
+       0x0078, 0x2dde, 0x7060, 0x2060, 0x6800, 0x6002, 0x1078, 0x1c70,
+       0x0078, 0x2482, 0xa282, 0x0004, 0x0048, 0x2de8, 0x1078, 0x23eb,
+       0x2200, 0x0079, 0x2deb, 0x2de6, 0x2def, 0x2dfc, 0x2def, 0x7000,
+       0xa086, 0x0005, 0x0040, 0x2df8, 0x1078, 0x36c1, 0x782b, 0x3008,
+       0x781b, 0x0065, 0x0078, 0x2459, 0x7890, 0x8007, 0x8001, 0xa084,
+       0x0007, 0xa080, 0x0018, 0x789a, 0x79a8, 0xa18c, 0x00ff, 0xa186,
+       0x0003, 0x0040, 0x2e11, 0xa186, 0x0000, 0x0040, 0x2e11, 0x0078,
+       0x369d, 0x781b, 0x0065, 0x0078, 0x2459, 0x6820, 0xa085, 0x0004,
+       0x6822, 0x82ff, 0x00c0, 0x2e20, 0x1078, 0x36c1, 0x0078, 0x2e27,
+       0x8211, 0x0040, 0x2e25, 0x1078, 0x23eb, 0x1078, 0x36d1, 0x782b,
+       0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x702c, 0x8003, 0x0048,
+       0x2e37, 0x2019, 0x4d9e, 0x1078, 0x2276, 0x702f, 0x8000, 0x1078,
+       0x3912, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x2e43, 0x0018, 0x2e43,
+       0x791a, 0xa006, 0x007c, 0xa085, 0x0001, 0x007c, 0xa684, 0x0060,
+       0x00c0, 0x2e50, 0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x2ec9,
+       0xa684, 0x0800, 0x00c0, 0x2e72, 0x68b4, 0xa084, 0x4800, 0xa635,
+       0xa684, 0x0800, 0x00c0, 0x2e72, 0x6998, 0x6a94, 0x692e, 0x6a32,
+       0x703c, 0xa005, 0x00c0, 0x2e6a, 0x2200, 0xa105, 0x0040, 0x2e71,
+       0x703f, 0x0015, 0x7000, 0xa086, 0x0006, 0x0040, 0x2e71, 0x1078,
+       0x4776, 0x007c, 0xa684, 0x0020, 0x0040, 0x2e94, 0xa684, 0x4000,
+       0x0040, 0x2e80, 0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x2e6a,
+       0x68b4, 0xa084, 0x4800, 0xa635, 0xa684, 0x4000, 0x00c0, 0x2e7a,
+       0x703c, 0xa005, 0x00c0, 0x2e8e, 0x703f, 0x0015, 0x79d8, 0x7adc,
+       0x692e, 0x6a32, 0x0078, 0x2e6a, 0xa684, 0x4000, 0x0040, 0x2e9e,
+       0x682f, 0x0000, 0x6833, 0x0000, 0x0078, 0x2e6a, 0x68b4, 0xa084,
+       0x4800, 0xa635, 0xa684, 0x4000, 0x00c0, 0x2e98, 0x703c, 0xa005,
+       0x00c0, 0x2eac, 0x703f, 0x0015, 0x79d8, 0x7adc, 0x78d0, 0x80fb,
+       0x00c8, 0x2eb3, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000,
+       0x692e, 0x6a32, 0x2100, 0xa205, 0x00c0, 0x2ec0, 0x0078, 0x2e6a,
+       0x7000, 0xa086, 0x0006, 0x0040, 0x2ec9, 0x1078, 0x4b30, 0x0078,
+       0x2e6a, 0x007c, 0x6008, 0xa085, 0x0200, 0x600a, 0xa384, 0x0200,
+       0x0040, 0x2ed6, 0x6008, 0xa085, 0x0002, 0x600a, 0x681b, 0x0006,
+       0x688f, 0x0000, 0x6893, 0x0000, 0x6a30, 0x692c, 0x6a3e, 0x6942,
+       0x682f, 0x0003, 0x6833, 0x0000, 0x6837, 0x0020, 0x6897, 0x0000,
+       0x689b, 0x0020, 0x68b3, 0x0000, 0x68af, 0x0000, 0x7000, 0x0079,
+       0x2ef1, 0x2482, 0x2efb, 0x2f04, 0x2ef9, 0x2ef9, 0x2ef9, 0x2ef9,
+       0x2ef9, 0x1078, 0x23eb, 0x6820, 0xa084, 0x0001, 0x00c0, 0x2f04,
+       0x1078, 0x33d8, 0x0078, 0x2f0a, 0x7060, 0x2c50, 0x2060, 0x6800,
+       0x6002, 0x2a60, 0x2021, 0x515a, 0x2404, 0xa005, 0x0040, 0x2f13,
+       0x2020, 0x0078, 0x2f0c, 0x2d22, 0x206b, 0x0000, 0x007c, 0x1078,
+       0x33df, 0x1078, 0x33ee, 0x6008, 0xa084, 0xfdff, 0x600a, 0x682b,
+       0x0000, 0x789b, 0x000e, 0x6f14, 0x6817, 0x0002, 0x1078, 0x4b78,
+       0xa684, 0x0800, 0x0040, 0x2f30, 0x691c, 0xa18d, 0x2000, 0x691e,
+       0x6818, 0xa084, 0x8000, 0x0040, 0x2f40, 0x7868, 0xa08c, 0x00ff,
+       0x0040, 0x2f3e, 0x681b, 0x001e, 0x0078, 0x2f40, 0x681b, 0x0000,
+       0x2021, 0x515a, 0x2404, 0xad06, 0x0040, 0x2f47, 0x7460, 0x6800,
+       0x2022, 0x68c3, 0x0000, 0x6a3c, 0x6940, 0x6a32, 0x692e, 0x1078,
+       0x1c70, 0x0078, 0x2482, 0x1078, 0x2e46, 0x682b, 0x0000, 0x2001,
+       0x000e, 0x6f14, 0x1078, 0x3918, 0xa08c, 0x00ff, 0x6916, 0x6818,
+       0xa084, 0x8000, 0x0040, 0x2f66, 0x703c, 0x681a, 0xa68c, 0xdf00,
+       0x691e, 0x706f, 0x0000, 0x0078, 0x2482, 0x7000, 0xa005, 0x00c0,
+       0x2f73, 0x0078, 0x2482, 0xa006, 0x1078, 0x4776, 0x6817, 0x0000,
+       0x681b, 0x0014, 0xa68c, 0xdf00, 0x691e, 0x682b, 0x0000, 0x6820,
+       0xa085, 0x00ff, 0x6822, 0x7000, 0x0079, 0x2f86, 0x2482, 0x2f90,
+       0x2f90, 0x2f92, 0x2f92, 0x2f92, 0x2f92, 0x2f8e, 0x1078, 0x23eb,
+       0x1078, 0x33ee, 0x6008, 0xa084, 0xffef, 0x600a, 0x0078, 0x33a8,
+       0x2300, 0x0079, 0x2f9b, 0x2f9e, 0x2fa0, 0x2fd9, 0x1078, 0x23eb,
+       0x7000, 0x0079, 0x2fa3, 0x2482, 0x2fad, 0x2fad, 0x2fc8, 0x2fad,
+       0x2fd5, 0x2fc8, 0x2fab, 0x1078, 0x23eb, 0xa684, 0x0060, 0xa086,
+       0x0060, 0x00c0, 0x2fc4, 0xa6b4, 0xffdf, 0xa6b4, 0xbfff, 0xa6b5,
+       0x2000, 0x7e5a, 0x681c, 0xa084, 0xffdf, 0x681e, 0x1078, 0x4776,
+       0x1078, 0x4a3a, 0x0078, 0x3888, 0xa684, 0x2000, 0x0040, 0x2fb7,
+       0x6818, 0xa084, 0x8000, 0x0040, 0x2fd5, 0x681b, 0x0015, 0xa684,
+       0x4000, 0x0040, 0x2fd5, 0x681b, 0x0007, 0x1078, 0x389c, 0x0078,
+       0x2459, 0x1078, 0x23eb, 0x2300, 0x0079, 0x2fde, 0x2fe1, 0x2fe3,
+       0x3016, 0x1078, 0x23eb, 0x7000, 0x0079, 0x2fe6, 0x2482, 0x2ff0,
+       0x2ff0, 0x300b, 0x2ff0, 0x3012, 0x300b, 0x2fee, 0x1078, 0x23eb,
+       0xa684, 0x0060, 0xa086, 0x0060, 0x00c0, 0x3007, 0xa6b4, 0xffbf,
+       0xa6b4, 0xbfff, 0xa6b5, 0x2000, 0x7e5a, 0x681c, 0xa084, 0xffbf,
+       0x681e, 0x1078, 0x4776, 0x1078, 0x4a3a, 0x0078, 0x3888, 0xa684,
+       0x2000, 0x0040, 0x2ffa, 0x6818, 0xa084, 0x8000, 0x0040, 0x3012,
+       0x681b, 0x0007, 0x781b, 0x00cd, 0x0078, 0x2459, 0x6820, 0xa085,
+       0x0004, 0x6822, 0x1078, 0x3853, 0xa6b5, 0x0800, 0x1078, 0x36c1,
+       0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x2300, 0x0079,
+       0x3029, 0x302c, 0x302e, 0x3030, 0x1078, 0x23eb, 0x0078, 0x36ad,
+       0xa684, 0x0400, 0x00c0, 0x3059, 0x79e4, 0xa184, 0x0020, 0x0040,
+       0x3040, 0x78ec, 0xa084, 0x0003, 0x0040, 0x3040, 0x782b, 0x3009,
+       0x789b, 0x0060, 0x78ab, 0x0000, 0xa684, 0xfffb, 0x785a, 0x79e4,
+       0xa184, 0x0020, 0x0040, 0x3051, 0x78ec, 0xa084, 0x0003, 0x00c0,
+       0x3055, 0x2001, 0x0014, 0x0078, 0x2d01, 0xa184, 0x0007, 0x0079,
+       0x3091, 0x7a90, 0xa294, 0x0007, 0x789b, 0x0060, 0x79a8, 0x81ff,
+       0x0040, 0x308f, 0x789b, 0x0010, 0x7ba8, 0xa384, 0x0001, 0x00c0,
+       0x3080, 0x7ba8, 0x7ba8, 0xa386, 0x0001, 0x00c0, 0x3073, 0x2009,
+       0xfff7, 0x0078, 0x3079, 0xa386, 0x0003, 0x00c0, 0x3080, 0x2009,
+       0xffef, 0x0c7e, 0x7054, 0x2060, 0x6004, 0xa104, 0x6006, 0x0c7f,
+       0x789b, 0x0060, 0x78ab, 0x0000, 0xa684, 0xfffb, 0x785a, 0x782b,
+       0x3009, 0x6920, 0xa18c, 0xfdff, 0xa18c, 0xfeff, 0x6922, 0x0078,
+       0x3888, 0x299e, 0x29a9, 0x309b, 0x30a3, 0x3099, 0x3099, 0x3888,
+       0x3888, 0x1078, 0x23eb, 0x6920, 0xa18c, 0xfdff, 0xa18c, 0xfeff,
+       0x6922, 0x0078, 0x3892, 0x6920, 0xa18c, 0xfdff, 0xa18c, 0xfeff,
+       0x6922, 0x0078, 0x3888, 0x79e4, 0xa184, 0x0030, 0x0040, 0x30b5,
+       0x78ec, 0xa084, 0x0003, 0x00c0, 0x30dc, 0x7000, 0xa086, 0x0004,
+       0x00c0, 0x30cf, 0x706c, 0xa086, 0x0002, 0x00c0, 0x30c5, 0x2011,
+       0x0002, 0x2019, 0x0000, 0x0078, 0x2848, 0x706c, 0xa086, 0x0006,
+       0x0040, 0x30bf, 0x706c, 0xa086, 0x0004, 0x0040, 0x30bf, 0x7000,
+       0xa086, 0x0000, 0x0040, 0x2459, 0x6818, 0xa085, 0x8000, 0x681a,
+       0x2001, 0x0014, 0x0078, 0x2d01, 0xa184, 0x0007, 0x0079, 0x30e0,
+       0x3888, 0x3888, 0x30e8, 0x3888, 0x38ed, 0x38ed, 0x3888, 0x3888,
+       0xa684, 0x0080, 0x0040, 0x3117, 0x7194, 0x81ff, 0x0040, 0x3117,
+       0xa182, 0x000d, 0x00d0, 0x30f8, 0x7097, 0x0000, 0x0078, 0x30fd,
+       0xa182, 0x000c, 0x7096, 0x2009, 0x000c, 0x789b, 0x0061, 0x79aa,
+       0x157e, 0x137e, 0x147e, 0x7098, 0x8114, 0xa210, 0x729a, 0xa080,
+       0x000b, 0xad00, 0x2098, 0x20a1, 0x012b, 0x789b, 0x0000, 0x8108,
+       0x81ac, 0x53a6, 0x147f, 0x137f, 0x157f, 0x0078, 0x3892, 0xa684,
+       0x0400, 0x00c0, 0x3158, 0x6820, 0xa084, 0x0001, 0x0040, 0x3892,
+       0xa68c, 0x0060, 0xa684, 0x0060, 0x0040, 0x312c, 0xa086, 0x0060,
+       0x00c0, 0x312c, 0xa18d, 0x4000, 0xa18c, 0xfffb, 0x795a, 0x69b6,
+       0x789b, 0x0060, 0x78ab, 0x0000, 0x789b, 0x0061, 0x6818, 0xa085,
+       0x8000, 0x681a, 0x78aa, 0x8008, 0x810c, 0x0040, 0x3407, 0xa18c,
+       0x00f8, 0x00c0, 0x3407, 0x157e, 0x137e, 0x147e, 0x20a1, 0x012b,
+       0x789b, 0x0000, 0x8000, 0x80ac, 0xad80, 0x000b, 0x2098, 0x53a6,
+       0x147f, 0x137f, 0x157f, 0x6814, 0x8007, 0x7882, 0x0078, 0x3892,
+       0x6818, 0xa084, 0x8000, 0x0040, 0x315f, 0x681b, 0x0008, 0x781b,
+       0x00c3, 0x0078, 0x2459, 0x2300, 0x0079, 0x3166, 0x316b, 0x320a,
+       0x3169, 0x1078, 0x23eb, 0x7000, 0xa084, 0x0007, 0x0079, 0x3170,
+       0x2482, 0x317a, 0x31af, 0x3185, 0x3178, 0x2482, 0x3178, 0x3178,
+       0x1078, 0x23eb, 0x681c, 0xa084, 0x2000, 0x0040, 0x3193, 0x6008,
+       0xa085, 0x0002, 0x600a, 0x0078, 0x3193, 0x68c0, 0xa005, 0x00c0,
+       0x31af, 0x6920, 0xa18d, 0x0001, 0x6922, 0x68c3, 0x0001, 0x6800,
+       0x706a, 0x0078, 0x31a9, 0x6920, 0xa18d, 0x0001, 0x6922, 0x6800,
+       0x6006, 0xa005, 0x00c0, 0x319d, 0x6002, 0x681c, 0xa084, 0x000e,
+       0x0040, 0x31a9, 0x7014, 0x68ba, 0x7130, 0xa188, 0x7400, 0x0078,
+       0x31ab, 0x2009, 0x7500, 0x2104, 0x6802, 0x2d0a, 0x7162, 0x6eb6,
+       0xa684, 0x0060, 0x0040, 0x3208, 0xa684, 0x0800, 0x00c0, 0x31c3,
+       0xa684, 0x7fff, 0x68b6, 0x6894, 0x68a6, 0x6898, 0x68aa, 0x1078,
+       0x4776, 0x0078, 0x3208, 0xa684, 0x0020, 0x0040, 0x31d8, 0x68c0,
+       0xa005, 0x0040, 0x31cf, 0x1078, 0x4b78, 0x0078, 0x31d2, 0xa006,
+       0x1078, 0x4b30, 0x79d8, 0x7adc, 0x69aa, 0x6aa6, 0x0078, 0x31de,
+       0x1078, 0x37fc, 0x69aa, 0x6aa6, 0x1078, 0x4b30, 0xa684, 0x8000,
+       0x0040, 0x3208, 0xa684, 0x7fff, 0x68b6, 0x2001, 0x0076, 0x1078,
+       0x3918, 0x2010, 0x2001, 0x0078, 0x1078, 0x3918, 0x2008, 0xa684,
+       0x0020, 0x00c0, 0x3200, 0x2001, 0x007a, 0x1078, 0x3918, 0x801b,
+       0x00c8, 0x31fb, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000,
+       0x6b98, 0x2100, 0xa302, 0x68b2, 0x6b94, 0x2200, 0xa303, 0x68ae,
+       0x0078, 0x2482, 0x0078, 0x36ad, 0x7037, 0x0000, 0xa282, 0x0006,
+       0x0050, 0x3214, 0x1078, 0x23eb, 0x7000, 0xa084, 0x0007, 0x10c0,
+       0x39be, 0x2300, 0x0079, 0x321c, 0x321f, 0x3248, 0x325c, 0x2200,
+       0x0079, 0x3222, 0x3246, 0x36ad, 0x3228, 0x3246, 0x3278, 0x32ba,
+       0x7003, 0x0005, 0x2001, 0x7610, 0x2068, 0x704a, 0x157e, 0x20a9,
+       0x0031, 0x2003, 0x0000, 0x8000, 0x0070, 0x3238, 0x0078, 0x3231,
+       0x157f, 0xad80, 0x0009, 0x7036, 0x6817, 0x0000, 0x68b7, 0x0700,
+       0x6823, 0x0800, 0x6827, 0x0003, 0x0078, 0x369d, 0x1078, 0x23eb,
+       0x7003, 0x0005, 0x2001, 0x7610, 0x2068, 0x704a, 0xad80, 0x0009,
+       0x7036, 0x2200, 0x0079, 0x3254, 0x36ad, 0x325a, 0x325a, 0x3278,
+       0x325a, 0x36ad, 0x1078, 0x23eb, 0x7003, 0x0005, 0x2001, 0x7610,
+       0x2068, 0x704a, 0xad80, 0x0009, 0x7036, 0x2200, 0x0079, 0x3268,
+       0x3270, 0x326e, 0x326e, 0x3270, 0x326e, 0x3270, 0x1078, 0x23eb,
+       0x1078, 0x36d1, 0x782b, 0x3008, 0x781b, 0x0065, 0x0078, 0x2459,
+       0x7003, 0x0002, 0x7a80, 0xa294, 0x0f00, 0x789b, 0x0018, 0x7ca8,
+       0xa484, 0x001f, 0xa215, 0x2069, 0x7500, 0x2d04, 0x2d08, 0x7162,
+       0x2068, 0xa005, 0x0040, 0x3293, 0x6814, 0xa206, 0x0040, 0x32af,
+       0x6800, 0x0078, 0x3286, 0x7003, 0x0005, 0x2001, 0x7610, 0x2068,
+       0x704a, 0x7036, 0x157e, 0x20a9, 0x0031, 0x2003, 0x0000, 0x8000,
+       0x0070, 0x32a4, 0x0078, 0x329d, 0x157f, 0xad80, 0x0009, 0x7036,
+       0x6a16, 0x68b7, 0x0700, 0x6823, 0x0800, 0x6827, 0x0003, 0x6eb4,
+       0x7e5a, 0x6820, 0xa084, 0x0c00, 0x0040, 0x3309, 0x1078, 0x36c9,
+       0x0078, 0x3309, 0x7003, 0x0002, 0x7a80, 0xa294, 0x0f00, 0x789b,
+       0x0018, 0x7ca8, 0xa484, 0x001f, 0xa215, 0x79a8, 0x79a8, 0xa18c,
+       0x00ff, 0xa1e8, 0x7400, 0x2d04, 0x2d08, 0x7162, 0x2068, 0xa005,
+       0x0040, 0x32d9, 0x6814, 0xa206, 0x0040, 0x32f4, 0x6800, 0x0078,
+       0x32cc, 0x7003, 0x0005, 0x2001, 0x7610, 0x2068, 0x704a, 0x157e,
+       0x20a9, 0x0031, 0x2003, 0x0000, 0x8000, 0x0070, 0x32e9, 0x0078,
+       0x32e2, 0x157f, 0xad80, 0x0009, 0x7036, 0x6a16, 0x68b7, 0x0700,
+       0x6823, 0x0800, 0x6827, 0x0003, 0x6eb4, 0x7e5a, 0x6820, 0xa084,
+       0x0c00, 0x0040, 0x3309, 0xa084, 0x0800, 0x0040, 0x3303, 0x1078,
+       0x36cd, 0x0078, 0x3309, 0x1078, 0x36c9, 0x708b, 0x0000, 0x0078,
+       0x3309, 0x027e, 0x8207, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003,
+       0xa080, 0x5380, 0x2060, 0x7056, 0x6000, 0x705a, 0x6004, 0x705e,
+       0xa684, 0x0060, 0x0040, 0x3361, 0x6b98, 0x6c94, 0x69ac, 0x68b0,
+       0xa105, 0x00c0, 0x3343, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde, 0xa6b4,
+       0xb7ff, 0x7e5a, 0xa684, 0x0060, 0xa086, 0x0060, 0x0040, 0x3361,
+       0x68c0, 0xa005, 0x0040, 0x333c, 0x7003, 0x0003, 0x682b, 0x0000,
+       0x1078, 0x4a29, 0x0078, 0x333e, 0x1078, 0x4a3a, 0xa6b5, 0x2000,
+       0x7e5a, 0x0078, 0x3361, 0x68b0, 0xa31a, 0x2100, 0xa423, 0x2400,
+       0xa305, 0x0040, 0x3361, 0x7bd2, 0x7bda, 0x7cd6, 0x7cde, 0x68b0,
+       0xa6b4, 0xbfff, 0x7e5a, 0x007e, 0x68c0, 0xa005, 0x007f, 0x0040,
+       0x335f, 0x7003, 0x0003, 0x1078, 0x4a29, 0x0078, 0x3361, 0x1078,
+       0x4a85, 0x077f, 0x1078, 0x37ef, 0x2009, 0x0065, 0xa684, 0x0004,
+       0x0040, 0x3382, 0x78e4, 0xa084, 0x0030, 0x0040, 0x337a, 0x78ec,
+       0xa084, 0x0003, 0x0040, 0x337a, 0x782b, 0x3008, 0x2009, 0x0065,
+       0x0078, 0x3382, 0x0f7e, 0x2079, 0x5100, 0x1078, 0x4776, 0x0f7f,
+       0x0040, 0x2482, 0x791a, 0x2d00, 0x704a, 0x8207, 0xa084, 0x000f,
+       0x8003, 0x8003, 0x8003, 0xa080, 0x5380, 0x2048, 0x0078, 0x2459,
+       0x6020, 0xa005, 0x0040, 0x339c, 0x8001, 0x6022, 0x6008, 0xa085,
+       0x0008, 0x600a, 0x7010, 0x6026, 0x007c, 0xa006, 0x1078, 0x4776,
+       0x6817, 0x0000, 0x681b, 0x0001, 0x6823, 0x0040, 0x681f, 0x0100,
+       0x7000, 0xa084, 0x0007, 0x0079, 0x33ad, 0x2482, 0x33b7, 0x33b7,
+       0x33d4, 0x33bf, 0x33bd, 0x33bf, 0x33b5, 0x1078, 0x23eb, 0x1078,
+       0x33df, 0x1078, 0x33d8, 0x1078, 0x1c70, 0x0078, 0x2482, 0x706c,
+       0x706f, 0x0000, 0x7093, 0x0000, 0x0079, 0x33c6, 0x33d0, 0x33d0,
+       0x33ce, 0x33ce, 0x33ce, 0x33d0, 0x33ce, 0x33d0, 0x0079, 0x2861,
+       0x706f, 0x0000, 0x0078, 0x2482, 0x681b, 0x0000, 0x0078, 0x2f17,
+       0x6800, 0xa005, 0x00c0, 0x33dd, 0x6002, 0x6006, 0x007c, 0x6010,
+       0xa005, 0x0040, 0x33e8, 0x8001, 0x00d0, 0x33e8, 0x1078, 0x23eb,
+       0x6012, 0x6008, 0xa084, 0xffef, 0x600a, 0x007c, 0x6018, 0xa005,
+       0x0040, 0x33f4, 0x8001, 0x601a, 0x007c, 0x1078, 0x3912, 0x681b,
+       0x0018, 0x0078, 0x342b, 0x1078, 0x3912, 0x681b, 0x0019, 0x0078,
+       0x342b, 0x1078, 0x3912, 0x681b, 0x001a, 0x0078, 0x342b, 0x1078,
+       0x3912, 0x681b, 0x0003, 0x0078, 0x342b, 0x7780, 0x1078, 0x37ef,
+       0x7184, 0xa18c, 0x00ff, 0xa1e8, 0x7400, 0x2d04, 0x2d08, 0x2068,
+       0xa005, 0x00c0, 0x341d, 0x0078, 0x2482, 0x6814, 0x7280, 0xa206,
+       0x0040, 0x3425, 0x6800, 0x0078, 0x3416, 0x6800, 0x200a, 0x681b,
+       0x0005, 0x708b, 0x0000, 0x1078, 0x33df, 0x6820, 0xa084, 0x0001,
+       0x00c0, 0x3434, 0x1078, 0x33d8, 0x1078, 0x33ee, 0x681f, 0x0000,
+       0x6823, 0x0020, 0x1078, 0x1c70, 0x0078, 0x2482, 0xa282, 0x0003,
+       0x00c0, 0x369d, 0x7da8, 0xa5ac, 0x00ff, 0x7ca8, 0xa4a4, 0x00ff,
+       0x6920, 0xa18d, 0x0080, 0x6922, 0xa184, 0x0100, 0x0040, 0x34a2,
+       0xa18c, 0xfeff, 0x6922, 0xa4a4, 0x00ff, 0x0040, 0x348c, 0xa482,
+       0x000c, 0x0048, 0x345f, 0x0040, 0x345f, 0x2021, 0x000c, 0x852b,
+       0x852b, 0x1078, 0x3760, 0x0040, 0x3469, 0x1078, 0x355b, 0x0078,
+       0x3495, 0x1078, 0x371b, 0x0c7e, 0x2960, 0x6004, 0xa084, 0xfff5,
+       0x6006, 0x1078, 0x3586, 0x0c7f, 0x6920, 0xa18d, 0x0100, 0x6922,
+       0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0, 0x3486,
+       0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2459, 0x782b, 0x3008,
+       0x781b, 0x0065, 0x0078, 0x2459, 0x0c7e, 0x2960, 0x6004, 0xa084,
+       0xfff5, 0x6006, 0x1078, 0x3586, 0x0c7f, 0x7e58, 0xa684, 0x0400,
+       0x00c0, 0x349e, 0x781b, 0x0058, 0x0078, 0x2459, 0x781b, 0x0065,
+       0x0078, 0x2459, 0x0c7e, 0x7054, 0x2060, 0x6100, 0xa18c, 0x1000,
+       0x0040, 0x34e2, 0x6208, 0x8217, 0xa294, 0x00ff, 0xa282, 0x000c,
+       0x0048, 0x34b6, 0x0040, 0x34b6, 0x2011, 0x000c, 0x2400, 0xa202,
+       0x00c8, 0x34bb, 0x2220, 0x6208, 0xa294, 0x00ff, 0x7018, 0xa086,
+       0x0028, 0x00c0, 0x34cb, 0xa282, 0x0019, 0x00c8, 0x34d1, 0x2011,
+       0x0019, 0x0078, 0x34d1, 0xa282, 0x000c, 0x00c8, 0x34d1, 0x2011,
+       0x000c, 0x2200, 0xa502, 0x00c8, 0x34d6, 0x2228, 0x1078, 0x371f,
+       0x852b, 0x852b, 0x1078, 0x3760, 0x0040, 0x34e2, 0x1078, 0x355b,
+       0x0078, 0x34e6, 0x1078, 0x371b, 0x1078, 0x3586, 0x7858, 0xa085,
+       0x0004, 0x785a, 0x0c7f, 0x782b, 0x3008, 0x781b, 0x0065, 0x0078,
+       0x2459, 0x0c7e, 0x2960, 0x6000, 0xa084, 0x1000, 0x00c0, 0x3509,
+       0x6010, 0xa084, 0x000f, 0x00c0, 0x3503, 0x6104, 0xa18c, 0xfff5,
+       0x6106, 0x0c7f, 0x007c, 0x2011, 0x0032, 0x2019, 0x0000, 0x0078,
+       0x3530, 0x68a0, 0xa084, 0x0200, 0x00c0, 0x3503, 0x6208, 0xa294,
+       0x00ff, 0x7018, 0xa086, 0x0028, 0x00c0, 0x351e, 0xa282, 0x0019,
+       0x00c8, 0x3524, 0x2011, 0x0019, 0x0078, 0x3524, 0xa282, 0x000c,
+       0x00c8, 0x3524, 0x2011, 0x000c, 0x6308, 0x831f, 0xa39c, 0x00ff,
+       0xa382, 0x000c, 0x0048, 0x3530, 0x0040, 0x3530, 0x2019, 0x000c,
+       0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7aaa, 0x7baa,
+       0xa8c0, 0x0005, 0x6820, 0xa085, 0x0100, 0x6822, 0x0c7f, 0x007c,
+       0x0c7e, 0x2960, 0xa18c, 0xfff5, 0x6106, 0x2011, 0x0032, 0x2019,
+       0x0000, 0x0078, 0x354b, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab,
+       0x0001, 0x7aaa, 0x7baa, 0xa8c0, 0x0005, 0x6820, 0xa085, 0x0100,
+       0x6822, 0x0c7f, 0x007c, 0x0c7e, 0x7154, 0x2160, 0x1078, 0x3562,
+       0x0c7f, 0x007c, 0x2008, 0xa084, 0xfff0, 0xa425, 0x7c86, 0x6018,
+       0x789a, 0x7cae, 0x6412, 0x78a4, 0xa084, 0xfff8, 0xa18c, 0x0007,
+       0xa105, 0x78a6, 0x6016, 0x788a, 0xa4a4, 0x000f, 0x8427, 0x8204,
+       0x8004, 0xa084, 0x00ff, 0xa405, 0x600e, 0x78ec, 0xd08c, 0x00c0,
+       0x3585, 0x6004, 0xa084, 0xfff5, 0x6006, 0x007c, 0x0c7e, 0x7054,
+       0x2060, 0x1078, 0x358d, 0x0c7f, 0x007c, 0x6018, 0x789a, 0x78a4,
+       0xa084, 0xfff0, 0x78a6, 0x6012, 0x7884, 0xa084, 0xfff0, 0x7886,
+       0x007c, 0xa282, 0x0002, 0x00c0, 0x369d, 0x7aa8, 0x6920, 0xa18d,
+       0x0080, 0x6922, 0xa184, 0x0200, 0x0040, 0x35e2, 0xa18c, 0xfdff,
+       0x6922, 0xa294, 0x00ff, 0xa282, 0x0002, 0x00c8, 0x369d, 0x1078,
+       0x362b, 0x1078, 0x3586, 0xa980, 0x0001, 0x200c, 0x1078, 0x37eb,
+       0x1078, 0x34f1, 0x88ff, 0x0040, 0x35d5, 0x789b, 0x0060, 0x2800,
+       0x78aa, 0x7e58, 0xa6b5, 0x0004, 0x7e5a, 0xa684, 0x0400, 0x00c0,
+       0x35cf, 0x782b, 0x3008, 0x781b, 0x0056, 0x0078, 0x2459, 0x782b,
+       0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x7e58, 0xa684, 0x0400,
+       0x00c0, 0x35de, 0x781b, 0x0058, 0x0078, 0x2459, 0x781b, 0x0065,
+       0x0078, 0x2459, 0xa282, 0x0002, 0x00c8, 0x35ea, 0xa284, 0x0001,
+       0x0040, 0x35f4, 0x7154, 0xa188, 0x0000, 0x210c, 0xa18c, 0x2000,
+       0x00c0, 0x35f4, 0x2011, 0x0000, 0x1078, 0x370d, 0x1078, 0x362b,
+       0x1078, 0x3586, 0x7858, 0xa085, 0x0004, 0x785a, 0x782b, 0x3008,
+       0x781b, 0x0065, 0x0078, 0x2459, 0x0c7e, 0x027e, 0x2960, 0x6000,
+       0x2011, 0x0001, 0xa084, 0x2000, 0x00c0, 0x361b, 0x6014, 0xa084,
+       0x0040, 0x00c0, 0x3619, 0xa18c, 0xffef, 0x6106, 0xa006, 0x0078,
+       0x3628, 0x2011, 0x0000, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab,
+       0x0003, 0x7aaa, 0xa8c0, 0x0004, 0x6820, 0xa085, 0x0200, 0x6822,
+       0x027f, 0x0c7f, 0x007c, 0x0c7e, 0x7054, 0x2060, 0x1078, 0x3632,
+       0x0c7f, 0x007c, 0x82ff, 0x0040, 0x3637, 0x2011, 0x0040, 0x6018,
+       0xa080, 0x0002, 0x789a, 0x78a4, 0xa084, 0xffbf, 0xa205, 0x78a6,
+       0x788a, 0x6016, 0x78ec, 0xd08c, 0x00c0, 0x364a, 0x6004, 0xa084,
+       0xffef, 0x6006, 0x007c, 0x007e, 0x7000, 0xa086, 0x0003, 0x0040,
+       0x3654, 0x007f, 0x0078, 0x3657, 0x007f, 0x0078, 0x3699, 0xa684,
+       0x0020, 0x0040, 0x3699, 0x7888, 0xa084, 0x0040, 0x0040, 0x3699,
+       0x7bb8, 0xa384, 0x003f, 0x831b, 0x00c8, 0x3667, 0x8000, 0xa005,
+       0x0040, 0x367d, 0x831b, 0x00c8, 0x3670, 0x8001, 0x0040, 0x3695,
+       0xa684, 0x4000, 0x0040, 0x367d, 0x78b8, 0x801b, 0x00c8, 0x3679,
+       0x8000, 0xa084, 0x003f, 0x00c0, 0x3695, 0xa6b4, 0xbfff, 0x7e5a,
+       0x79d8, 0x7adc, 0x2001, 0x0001, 0xa108, 0x00c8, 0x3689, 0xa291,
+       0x0000, 0x79d2, 0x79da, 0x7ad6, 0x7ade, 0x1078, 0x4b30, 0x781b,
+       0x0064, 0x1078, 0x49b5, 0x0078, 0x2459, 0x781b, 0x0064, 0x0078,
+       0x2459, 0x781b, 0x0065, 0x0078, 0x2459, 0x1078, 0x36d5, 0x782b,
+       0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x1078, 0x36c1, 0x782b,
+       0x3008, 0x781b, 0x0065, 0x0078, 0x2459, 0x6827, 0x0002, 0x1078,
+       0x36c9, 0x78e4, 0xa084, 0x0030, 0x0040, 0x2482, 0x78ec, 0xa084,
+       0x0003, 0x0040, 0x2482, 0x782b, 0x3008, 0x781b, 0x0065, 0x0078,
+       0x2459, 0x2001, 0x0005, 0x0078, 0x36d7, 0x2001, 0x000c, 0x0078,
+       0x36d7, 0x2001, 0x0006, 0x0078, 0x36d7, 0x2001, 0x000d, 0x0078,
+       0x36d7, 0x2001, 0x0009, 0x0078, 0x36d7, 0x2001, 0x0007, 0x789b,
+       0x0010, 0x78aa, 0x789b, 0x0060, 0x78ab, 0x0001, 0xa6b5, 0x0004,
+       0x7e5a, 0x007c, 0x077e, 0x873f, 0xa7bc, 0x000f, 0x873b, 0x873b,
+       0x8703, 0xa0e0, 0x5380, 0xa7b8, 0x0020, 0x7f9a, 0x79a4, 0xa184,
+       0x000f, 0x0040, 0x36fb, 0xa184, 0xfff0, 0x78a6, 0x6012, 0x6004,
+       0xa085, 0x0008, 0x6006, 0x8738, 0x8738, 0x7f9a, 0x79a4, 0xa184,
+       0x0040, 0x0040, 0x370b, 0xa184, 0xffbf, 0x78a6, 0x6016, 0x6004,
+       0xa085, 0x0010, 0x6006, 0x077f, 0x007c, 0x789b, 0x0010, 0x78ab,
+       0x0001, 0x78ab, 0x0002, 0x78ab, 0x0003, 0x7aaa, 0x789b, 0x0060,
+       0x78ab, 0x0004, 0x007c, 0x2021, 0x0000, 0x2029, 0x0032, 0x789b,
+       0x0010, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab, 0x0001, 0x7daa,
+       0x7caa, 0x789b, 0x0060, 0x78ab, 0x0005, 0x007c, 0x157e, 0x8007,
+       0xa084, 0x00ff, 0x8003, 0x8003, 0xa080, 0x0020, 0x789a, 0x79a4,
+       0xa18c, 0xfff0, 0x2001, 0x5146, 0x2004, 0xa082, 0x0028, 0x0040,
+       0x3749, 0x2021, 0x37d2, 0x2019, 0x0014, 0x20a9, 0x000c, 0x0078,
+       0x374f, 0x2021, 0x37de, 0x2019, 0x0019, 0x20a9, 0x000d, 0x2011,
+       0x0064, 0x2404, 0xa084, 0xfff0, 0xa106, 0x0040, 0x375e, 0x8420,
+       0x2300, 0xa210, 0x0070, 0x375e, 0x0078, 0x3751, 0x157f, 0x007c,
+       0x157e, 0x2009, 0x5146, 0x210c, 0xa182, 0x0032, 0x0048, 0x3774,
+       0x0040, 0x3778, 0x2009, 0x37c4, 0x2019, 0x0011, 0x20a9, 0x000e,
+       0x2011, 0x0032, 0x0078, 0x378a, 0xa182, 0x0028, 0x0040, 0x3782,
+       0x2009, 0x37d2, 0x2019, 0x0014, 0x20a9, 0x000c, 0x2011, 0x0064,
+       0x0078, 0x378a, 0x2009, 0x37de, 0x2019, 0x0019, 0x20a9, 0x000d,
+       0x2011, 0x0064, 0x2200, 0xa502, 0x0040, 0x379a, 0x0048, 0x379a,
+       0x8108, 0x2300, 0xa210, 0x0070, 0x3797, 0x0078, 0x378a, 0x157f,
+       0xa006, 0x007c, 0x157f, 0xa582, 0x0064, 0x00c8, 0x37a9, 0x7808,
+       0xa085, 0x0070, 0x780a, 0x7044, 0xa085, 0x0070, 0x7046, 0x0078,
+       0x37a9, 0x78ec, 0xa084, 0x0300, 0x0040, 0x37b1, 0x2104, 0x0078,
+       0x37c2, 0x2104, 0xa09e, 0x1102, 0x00c0, 0x37c2, 0x2001, 0x04fd,
+       0x2004, 0xa082, 0x0005, 0x0048, 0x37c1, 0x2001, 0x1201, 0x0078,
+       0x37c2, 0x2104, 0xa005, 0x007c, 0x1102, 0x3002, 0x3202, 0x4203,
+       0x4403, 0x5404, 0x5604, 0x6605, 0x6805, 0x7806, 0x7a06, 0x0c07,
+       0x0c07, 0x0e07, 0x3202, 0x4202, 0x5202, 0x6202, 0x7202, 0x6605,
+       0x7605, 0x7805, 0x7a05, 0x7c05, 0x7e05, 0x7f05, 0x2202, 0x3202,
+       0x4202, 0x5202, 0x5404, 0x6404, 0x7404, 0x7604, 0x7804, 0x7a04,
+       0x7c04, 0x7e04, 0x7f04, 0x789b, 0x0010, 0xa046, 0x007c, 0xa784,
+       0x0f00, 0x800b, 0xa784, 0x001f, 0x8003, 0x8003, 0x8003, 0x8003,
+       0xa105, 0xa0e0, 0x5400, 0x007c, 0x79d8, 0x7adc, 0x78d0, 0x801b,
+       0x00c8, 0x3803, 0x8000, 0xa084, 0x003f, 0xa108, 0xa291, 0x0000,
+       0x007c, 0x0f7e, 0x2079, 0x0100, 0x2009, 0x5140, 0x2091, 0x8000,
+       0x2104, 0x0079, 0x3813, 0x3849, 0x381d, 0x381d, 0x381d, 0x381d,
+       0x381d, 0x381d, 0x384d, 0x1078, 0x23eb, 0x784b, 0x0004, 0x7848,
+       0xa084, 0x0004, 0x00c0, 0x381f, 0x784b, 0x0008, 0x7848, 0xa084,
+       0x0008, 0x00c0, 0x3826, 0x68b4, 0xa085, 0x4000, 0x68b6, 0x7858,
+       0xa085, 0x4000, 0x785a, 0x7830, 0xa084, 0x0080, 0x00c0, 0x3849,
+       0x0018, 0x3849, 0x681c, 0xa084, 0x0020, 0x00c0, 0x3847, 0x0e7e,
+       0x2071, 0x5140, 0x1078, 0x389c, 0x0e7f, 0x0078, 0x3849, 0x781b,
+       0x00cd, 0x2091, 0x8001, 0x0f7f, 0x007c, 0x70b3, 0x0000, 0x1078,
+       0x3a76, 0x0078, 0x3849, 0x0c7e, 0x6814, 0x8007, 0xa084, 0x000f,
+       0x8003, 0x8003, 0x8003, 0xa0e0, 0x5380, 0x6004, 0xa084, 0x000a,
+       0x00c0, 0x3886, 0x6108, 0xa194, 0xff00, 0x0040, 0x3886, 0xa18c,
+       0x00ff, 0x2001, 0x0019, 0xa106, 0x0040, 0x3875, 0x2001, 0x0032,
+       0xa106, 0x0040, 0x3879, 0x0078, 0x387d, 0x2009, 0x0020, 0x0078,
+       0x387f, 0x2009, 0x003f, 0x0078, 0x387f, 0x2011, 0x0000, 0x2100,
+       0xa205, 0x600a, 0x6004, 0xa085, 0x0002, 0x6006, 0x0c7f, 0x007c,
+       0x781b, 0x0065, 0x0078, 0x2459, 0x782b, 0x3008, 0x781b, 0x0065,
+       0x0078, 0x2459, 0x781b, 0x0058, 0x0078, 0x2459, 0x782b, 0x3008,
+       0x781b, 0x0056, 0x0078, 0x2459, 0x2009, 0x5120, 0x210c, 0xa186,
+       0x0000, 0x0040, 0x38b0, 0xa186, 0x0001, 0x0040, 0x38b3, 0x2009,
+       0x5138, 0x200b, 0x000b, 0x706f, 0x0001, 0x781b, 0x0048, 0x007c,
+       0x781b, 0x00c7, 0x007c, 0x2009, 0x5138, 0x200b, 0x000a, 0x007c,
+       0x2009, 0x5120, 0x210c, 0xa186, 0x0000, 0x0040, 0x38d3, 0xa186,
+       0x0001, 0x0040, 0x38cd, 0x2009, 0x5138, 0x200b, 0x000b, 0x706f,
+       0x0001, 0x781b, 0x0048, 0x0078, 0x2459, 0x2009, 0x5138, 0x200b,
+       0x000a, 0x0078, 0x2459, 0x782b, 0x3008, 0x781b, 0x00c7, 0x0078,
+       0x2459, 0x781b, 0x00cd, 0x0078, 0x2459, 0x782b, 0x3008, 0x781b,
+       0x00cd, 0x0078, 0x2459, 0x781b, 0x008e, 0x0078, 0x2459, 0x782b,
+       0x3008, 0x781b, 0x008e, 0x0078, 0x2459, 0x6818, 0xa084, 0x8000,
+       0x0040, 0x38f4, 0x681b, 0x001d, 0x706f, 0x0001, 0x781b, 0x0048,
+       0x0078, 0x2459, 0x007e, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x3910,
+       0x7808, 0xa084, 0xfffc, 0x780a, 0x0005, 0x0005, 0x0005, 0x0005,
+       0x78ec, 0xa084, 0x0021, 0x0040, 0x3910, 0x7044, 0x780a, 0xa005,
+       0x007f, 0x007c, 0x7044, 0xa085, 0x0002, 0x7046, 0x780a, 0x007c,
+       0x007e, 0x7830, 0xa084, 0x0040, 0x00c0, 0x3919, 0x0098, 0x3924,
+       0x007f, 0x789a, 0x78ac, 0x007c, 0x7808, 0xa084, 0xfffd, 0x780a,
+       0x0005, 0x0005, 0x0005, 0x0005, 0x78ec, 0xa084, 0x0021, 0x0040,
+       0x3933, 0x0098, 0x3931, 0x007f, 0x789a, 0x78ac, 0x007e, 0x7044,
+       0x780a, 0x007f, 0x007c, 0x78ec, 0xa084, 0x0002, 0x00c0, 0x4760,
+       0xa784, 0x007d, 0x00c0, 0x3947, 0x2700, 0x1078, 0x23eb, 0xa784,
+       0x0001, 0x00c0, 0x2f6d, 0xa784, 0x0070, 0x0040, 0x3957, 0x0c7e,
+       0x2d60, 0x2f68, 0x1078, 0x2396, 0x2d78, 0x2c68, 0x0c7f, 0xa784,
+       0x0008, 0x0040, 0x3964, 0x784b, 0x0008, 0x78ec, 0xa084, 0x0003,
+       0x0040, 0x2482, 0x0078, 0x3888, 0xa784, 0x0004, 0x0040, 0x3997,
+       0x78b8, 0xa084, 0x4001, 0x0040, 0x3997, 0x784b, 0x0008, 0x78ec,
+       0xa084, 0x0003, 0x0040, 0x2482, 0x78e4, 0xa084, 0x0007, 0xa086,
+       0x0001, 0x00c0, 0x3997, 0x78c0, 0xa085, 0x4800, 0x2030, 0x7e5a,
+       0x781b, 0x00cd, 0x0078, 0x2459, 0x784b, 0x0008, 0x6818, 0xa084,
+       0x8000, 0x0040, 0x3993, 0x681b, 0x0015, 0xa684, 0x4000, 0x0040,
+       0x3993, 0x681b, 0x0007, 0x1078, 0x389c, 0x0078, 0x2459, 0x681b,
+       0x0003, 0x7858, 0xa084, 0x3f00, 0x681e, 0x682f, 0x0000, 0x6833,
+       0x0000, 0x784b, 0x0008, 0x78ec, 0xa084, 0x0003, 0x0040, 0x2965,
+       0x0018, 0x2459, 0x0078, 0x36a5, 0x6b14, 0x8307, 0xa084, 0x000f,
+       0x8003, 0x8003, 0x8003, 0xa080, 0x5380, 0x2060, 0x2048, 0x7056,
+       0x6000, 0x705a, 0x6004, 0x705e, 0x2a60, 0x007c, 0x0079, 0x39c0,
+       0x39c8, 0x39c9, 0x39c8, 0x39cb, 0x39c8, 0x39c8, 0x39c8, 0x39d0,
+       0x007c, 0x1078, 0x33ee, 0x1078, 0x4776, 0x7038, 0x600a, 0x007c,
+       0x70a0, 0xa005, 0x0040, 0x39dd, 0x2068, 0x1078, 0x1b62, 0x1078,
+       0x46f8, 0x1078, 0x46ff, 0x70a3, 0x0000, 0x007c, 0x0e7e, 0x2091,
+       0x8000, 0x2071, 0x5140, 0x7000, 0xa086, 0x0007, 0x00c0, 0x39f4,
+       0x6110, 0x70bc, 0xa106, 0x00c0, 0x39f4, 0x0e7f, 0x1078, 0x1b6f,
+       0x1078, 0x39fa, 0xa006, 0x007c, 0x2091, 0x8001, 0x0e7f, 0xa085,
+       0x0001, 0x007c, 0x0f7e, 0x0e7e, 0x2071, 0x5140, 0x0078, 0x21fa,
+       0x785b, 0x0000, 0x70af, 0x000e, 0x2009, 0x0100, 0x017e, 0x70a0,
+       0xa06d, 0x0040, 0x3a0f, 0x70a3, 0x0000, 0x0078, 0x3a15, 0x70b3,
+       0x0000, 0x1078, 0x1b8b, 0x0040, 0x3a1b, 0x70ac, 0x6826, 0x1078,
+       0x3af8, 0x0078, 0x3a0f, 0x017f, 0x157e, 0x0c7e, 0x0d7e, 0x20a9,
+       0x0008, 0x2061, 0x7510, 0x6000, 0xa105, 0x6002, 0x601c, 0xa06d,
+       0x0040, 0x3a33, 0x6800, 0x601e, 0x1078, 0x195a, 0x6008, 0x8000,
+       0x600a, 0x0078, 0x3a26, 0x6018, 0xa06d, 0x0040, 0x3a3d, 0x6800,
+       0x601a, 0x1078, 0x195a, 0x0078, 0x3a33, 0xace0, 0x0008, 0x0070,
+       0x3a43, 0x0078, 0x3a23, 0x709c, 0xa084, 0x8000, 0x0040, 0x3a4a,
+       0x1078, 0x3b72, 0x0d7f, 0x0c7f, 0x157f, 0x007c, 0x127e, 0x2091,
+       0x2300, 0x6804, 0xa084, 0x000f, 0x0079, 0x3a56, 0x3a66, 0x3a66,
+       0x3a66, 0x3a66, 0x3a66, 0x3a66, 0x3a68, 0x3a6e, 0x3a66, 0x3a66,
+       0x3a66, 0x3a66, 0x3a66, 0x3a70, 0x3a66, 0x3a68, 0x1078, 0x23eb,
+       0x1078, 0x44d0, 0x1078, 0x195a, 0x0078, 0x3a74, 0x6827, 0x000b,
+       0x1078, 0x44d0, 0x1078, 0x3af8, 0x127f, 0x007c, 0x127e, 0x2091,
+       0x2300, 0x0098, 0x3a92, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x3a92,
+       0x0d7e, 0x1078, 0x4708, 0x2d00, 0x682e, 0x2009, 0x0004, 0x2001,
+       0x0000, 0x6827, 0x0084, 0x1078, 0x46c1, 0x1078, 0x3af8, 0x0d7f,
+       0x0078, 0x3ac6, 0x7948, 0xa185, 0x4000, 0x784a, 0x0098, 0x3a9b,
+       0x794a, 0x0078, 0x3a80, 0x7828, 0xa086, 0x1834, 0x00c0, 0x3aa4,
+       0xa185, 0x0004, 0x0078, 0x3aab, 0x7828, 0xa086, 0x1814, 0x00c0,
+       0x3a98, 0xa185, 0x000c, 0x784a, 0x789b, 0x000e, 0x78ab, 0x0002,
+       0x7858, 0xa084, 0x00ff, 0xa085, 0x0400, 0x785a, 0x70b4, 0xa080,
+       0x0091, 0x781a, 0x6827, 0x0284, 0x682c, 0x6836, 0x6830, 0x683a,
+       0x2009, 0x0004, 0x2001, 0x0000, 0x1078, 0x46c1, 0x127f, 0x007c,
+       0x0d7e, 0x6b14, 0x1078, 0x1bfd, 0x0040, 0x3ad5, 0x2068, 0x6827,
+       0x0002, 0x1078, 0x3af8, 0x0078, 0x3aca, 0x0d7f, 0x007c, 0x0d7e,
+       0x6b14, 0x6c28, 0xa4a4, 0x00ff, 0x1078, 0x1b9b, 0x0040, 0x3ae5,
+       0x2068, 0x6827, 0x0002, 0x1078, 0x3af8, 0x0d7f, 0x007c, 0x0d7e,
+       0x6b14, 0xa39c, 0x00ff, 0x1078, 0x1bce, 0x0040, 0x3af6, 0x2068,
+       0x6827, 0x0002, 0x1078, 0x3af8, 0x0078, 0x3aeb, 0x0d7f, 0x007c,
+       0x0c7e, 0x6914, 0x1078, 0x3b69, 0x6904, 0xa18c, 0x00ff, 0xa186,
+       0x0006, 0x0040, 0x3b13, 0xa186, 0x000d, 0x0040, 0x3b32, 0xa186,
+       0x0017, 0x00c0, 0x3b0f, 0x1078, 0x195a, 0x0078, 0x3b11, 0x1078,
+       0x1c72, 0x0c7f, 0x007c, 0x6004, 0x8001, 0x0048, 0x3b30, 0x6006,
+       0x2009, 0x0000, 0xa684, 0x0001, 0x00c0, 0x3b20, 0xa18d, 0x8000,
+       0xa684, 0x0004, 0x0040, 0x3b26, 0xa18d, 0x0002, 0x691e, 0x6823,
+       0x0000, 0x7104, 0x810f, 0x6818, 0xa105, 0x681a, 0x0078, 0x3b0f,
+       0x1078, 0x23eb, 0x6018, 0xa005, 0x00c0, 0x3b41, 0x6008, 0x8001,
+       0x0048, 0x3b41, 0x600a, 0x601c, 0x6802, 0x2d00, 0x601e, 0x0078,
+       0x3b57, 0xac88, 0x0006, 0x2104, 0xa005, 0x0040, 0x3b4a, 0x2008,
+       0x0078, 0x3b43, 0x6802, 0x2d0a, 0x6008, 0x8001, 0x0048, 0x3b11,
+       0x600a, 0x6018, 0x2068, 0x6800, 0x601a, 0x0078, 0x3b3b, 0x157e,
+       0x137e, 0x147e, 0x0c7e, 0x0d7e, 0x1078, 0x1937, 0x2da0, 0x137f,
+       0x20a9, 0x0031, 0x53a3, 0x0c7f, 0x147f, 0x137f, 0x157f, 0x0078,
+       0x3b0f, 0xa184, 0x001f, 0x8003, 0x8003, 0x8003, 0xa080, 0x7510,
+       0x2060, 0x007c, 0x2019, 0x5151, 0x2304, 0xa085, 0x0001, 0x201a,
+       0x2019, 0x0102, 0x2304, 0xa085, 0x0001, 0x201a, 0x007c, 0x2019,
+       0x5151, 0x2304, 0xa084, 0xfffe, 0x201a, 0x2019, 0x0102, 0x2304,
+       0xa084, 0xfffe, 0x201a, 0x007c, 0x7990, 0xa18c, 0xfff8, 0x7992,
+       0x70b4, 0xa080, 0x00dd, 0x781a, 0x0078, 0x2459, 0x70a3, 0x0000,
+       0x7003, 0x0000, 0x7043, 0x0001, 0x7037, 0x0000, 0x0018, 0x2410,
+       0x1078, 0x1b8b, 0x0040, 0x3bc7, 0x2009, 0x510f, 0x200b, 0x0000,
+       0x68bc, 0x2060, 0x6100, 0xa184, 0x0300, 0x0040, 0x3bbb, 0x6827,
+       0x000e, 0xa084, 0x0200, 0x0040, 0x3bb7, 0x6827, 0x0017, 0x1078,
+       0x3af8, 0x0078, 0x3b96, 0x7000, 0xa086, 0x0007, 0x00c0, 0x3c29,
+       0x2d00, 0x70a2, 0xad80, 0x000f, 0x7036, 0x0078, 0x3bce, 0x7040,
+       0xa086, 0x0001, 0x0040, 0x2492, 0x0078, 0x2459, 0x2031, 0x0000,
+       0x691c, 0xa184, 0x0002, 0x0040, 0x3bd7, 0xa6b5, 0x0004, 0xa184,
+       0x00c0, 0x8003, 0x8003, 0x8007, 0xa080, 0x3cc2, 0x2004, 0xa635,
+       0x6820, 0xa084, 0x0400, 0x0040, 0x3bef, 0x789b, 0x0018, 0x78ab,
+       0x0003, 0x789b, 0x0081, 0x78ab, 0x0001, 0xa6b5, 0x1000, 0x6820,
+       0xa084, 0x8000, 0x00c0, 0x3bfd, 0x681c, 0xa084, 0x8000, 0x00c0,
+       0x3c04, 0xa6b5, 0x0800, 0x0078, 0x3c04, 0xa6b5, 0x0400, 0x789b,
+       0x000e, 0x6824, 0x8007, 0x78aa, 0x6820, 0xa084, 0x0100, 0x0040,
+       0x3c0b, 0xa6b5, 0x4000, 0xa684, 0x0200, 0x0040, 0x3c25, 0x682c,
+       0x78d2, 0x6830, 0x78d6, 0xa684, 0x0100, 0x0040, 0x3c23, 0x682c,
+       0xa084, 0x0001, 0x0040, 0x3c23, 0x7888, 0xa084, 0x0040, 0x0040,
+       0x3c23, 0xa6b5, 0x8000, 0x1078, 0x46f0, 0x7e5a, 0x6eb6, 0x0078,
+       0x4727, 0x1078, 0x38fa, 0x00c0, 0x3cbc, 0x702c, 0x8004, 0x0048,
+       0x3c37, 0x2019, 0x4e3b, 0x1078, 0x2276, 0x702f, 0x0001, 0x2041,
+       0x0001, 0x2031, 0x1000, 0x789b, 0x0018, 0x6814, 0xa084, 0x001f,
+       0xa085, 0x0080, 0x78aa, 0x691c, 0xa184, 0x0002, 0x0040, 0x3c50,
+       0xa6b5, 0x0004, 0x78ab, 0x0020, 0x6828, 0x78aa, 0xa8c0, 0x0002,
+       0x681c, 0xd0f4, 0x0040, 0x3c59, 0x2c50, 0x1078, 0x39ac, 0x1078,
+       0x45ff, 0x6820, 0xa084, 0x8000, 0x0040, 0x3c67, 0xa6b5, 0x0400,
+       0x789b, 0x000e, 0x6824, 0x8007, 0x78aa, 0x0078, 0x3c6e, 0x681c,
+       0xa084, 0x8000, 0x00c0, 0x3c6e, 0xa6b5, 0x0800, 0x6820, 0xa084,
+       0x0100, 0x0040, 0x3c75, 0xa6b5, 0x4000, 0x681c, 0xa084, 0x00c0,
+       0x8003, 0x8003, 0x8007, 0xa080, 0x3cc2, 0x2004, 0xa635, 0xa684,
+       0x0100, 0x0040, 0x3c8f, 0x682c, 0xa084, 0x0001, 0x0040, 0x3c8f,
+       0x7888, 0xa084, 0x0040, 0x0040, 0x3c8f, 0xa6b5, 0x8000, 0x789b,
+       0x007e, 0x7eae, 0x6eb6, 0x6814, 0x8007, 0x78aa, 0x7882, 0x2810,
+       0x7aaa, 0x7830, 0xa084, 0x00c0, 0x00c0, 0x3cbc, 0x0018, 0x3cbc,
+       0x70b4, 0xa080, 0x00e2, 0x781a, 0x1078, 0x3912, 0xa684, 0x0200,
+       0x0040, 0x3cb0, 0x682c, 0x78d2, 0x6830, 0x78d6, 0x1078, 0x46f0,
+       0x2d00, 0x70a2, 0x704a, 0x6810, 0x70be, 0x7003, 0x0007, 0xad80,
+       0x000f, 0x7036, 0x0078, 0x2459, 0x1078, 0x1b62, 0x1078, 0x3912,
+       0x0078, 0x2459, 0x0000, 0x0300, 0x0200, 0x0000, 0x1078, 0x23eb,
+       0x2300, 0x0079, 0x3ccb, 0x3cce, 0x3cce, 0x3cd0, 0x1078, 0x23eb,
+       0x1078, 0x46ff, 0x6924, 0xa184, 0x00ff, 0xa086, 0x000a, 0x0040,
+       0x3ce2, 0xa184, 0xff00, 0xa085, 0x000a, 0x6826, 0x1078, 0x1b62,
+       0x0078, 0x3b96, 0x2001, 0x000a, 0x1078, 0x4691, 0x0078, 0x3b96,
+       0xa282, 0x0005, 0x0050, 0x3cee, 0x1078, 0x23eb, 0x7000, 0xa084,
+       0x0007, 0x10c0, 0x39be, 0x1078, 0x1937, 0x00c0, 0x3d0d, 0xa684,
+       0x0004, 0x0040, 0x3cff, 0x2001, 0x2800, 0x0078, 0x3d01, 0x2001,
+       0x0800, 0x71b4, 0xa188, 0x0091, 0x789b, 0x000e, 0x78aa, 0x2031,
+       0x0400, 0x7e5a, 0x791a, 0x0078, 0x2459, 0x6807, 0x0106, 0x680b,
+       0x0000, 0x689f, 0x0000, 0x6827, 0x0000, 0xa386, 0x0002, 0x00c0,
+       0x3d2e, 0xa286, 0x0002, 0x00c0, 0x3d2e, 0x78a0, 0xa005, 0x00c0,
+       0x3d2e, 0xa484, 0x8000, 0x00c0, 0x3d2e, 0x78e4, 0xa084, 0x0008,
+       0x0040, 0x3d2e, 0xa6b5, 0x0008, 0x2019, 0x0000, 0x1078, 0x411e,
+       0x2d00, 0x70a2, 0x704a, 0x7003, 0x0007, 0x7037, 0x0000, 0x6824,
+       0xa084, 0x0080, 0x0040, 0x3d40, 0x1078, 0x41d0, 0x0078, 0x2459,
+       0x2300, 0x0079, 0x3d43, 0x3d46, 0x3dc7, 0x3de6, 0x2200, 0x0079,
+       0x3d49, 0x3d4e, 0x3d5e, 0x3d84, 0x3d90, 0x3db3, 0x2029, 0x0001,
+       0xa026, 0x2011, 0x0000, 0x1078, 0x42f1, 0x0079, 0x3d57, 0x3d5c,
+       0x2459, 0x3b96, 0x3d5c, 0x3d5c, 0x1078, 0x23eb, 0x7990, 0xa18c,
+       0x0007, 0x00c0, 0x3d65, 0x2009, 0x0008, 0x2011, 0x0001, 0xa684,
+       0x0004, 0x0040, 0x3d6d, 0x2011, 0x0003, 0x2220, 0xa12a, 0x2011,
+       0x0001, 0x1078, 0x42f1, 0x0079, 0x3d75, 0x3d7a, 0x2459, 0x3b96,
+       0x3d82, 0x3d7c, 0x0078, 0x472d, 0x70ab, 0x3d80, 0x0078, 0x2459,
+       0x0078, 0x3d7a, 0x1078, 0x23eb, 0xa684, 0x0010, 0x0040, 0x3d8e,
+       0x1078, 0x419f, 0x0040, 0x3d8e, 0x0078, 0x2459, 0x0078, 0x420c,
+       0x6000, 0xa084, 0x0002, 0x0040, 0x3dad, 0x70b4, 0xa080, 0x00d2,
+       0x781a, 0x0d7e, 0x1078, 0x4708, 0x2d00, 0x682e, 0x6827, 0x0000,
+       0x1078, 0x3af8, 0x0d7f, 0x1078, 0x195a, 0x7003, 0x0000, 0x7037,
+       0x0000, 0x704b, 0x0000, 0x0078, 0x3b96, 0xa684, 0x0004, 0x00c0,
+       0x3db3, 0x0078, 0x472d, 0x6000, 0xa084, 0x0004, 0x00c0, 0x3dc5,
+       0x6000, 0xa084, 0x0001, 0x0040, 0x3dc5, 0x70ab, 0x3dc5, 0x2001,
+       0x0007, 0x1078, 0x4689, 0x0078, 0x4733, 0x0078, 0x472d, 0x2200,
+       0x0079, 0x3dca, 0x3dcf, 0x3dcf, 0x3dcf, 0x3dd1, 0x3dcf, 0x1078,
+       0x23eb, 0x70a7, 0x3dd5, 0x0078, 0x4739, 0x2011, 0x0018, 0x1078,
+       0x42eb, 0x0079, 0x3ddb, 0x3de0, 0x2459, 0x3b96, 0x3de2, 0x3de4,
+       0x1078, 0x23eb, 0x1078, 0x23eb, 0x1078, 0x23eb, 0x2200, 0x0079,
+       0x3de9, 0x3dee, 0x3df0, 0x3df0, 0x3dee, 0x3dee, 0x1078, 0x23eb,
+       0x78e4, 0xa084, 0x0008, 0x0040, 0x3e05, 0x70a7, 0x3df9, 0x0078,
+       0x4739, 0x2011, 0x0004, 0x1078, 0x42eb, 0x0079, 0x3dff, 0x3e05,
+       0x2459, 0x3b96, 0x3e05, 0x3e0f, 0x3e13, 0x70ab, 0x3e0d, 0x2001,
+       0x0003, 0x1078, 0x4689, 0x0078, 0x4733, 0x0078, 0x472d, 0x70ab,
+       0x3e05, 0x0078, 0x2459, 0x70ab, 0x3e17, 0x0078, 0x2459, 0x0078,
+       0x3e0d, 0xa282, 0x0003, 0x0050, 0x3e1f, 0x1078, 0x23eb, 0xa386,
+       0x0002, 0x00c0, 0x3e38, 0xa286, 0x0002, 0x00c0, 0x3e3e, 0x78a0,
+       0xa005, 0x00c0, 0x3e3e, 0xa484, 0x8000, 0x00c0, 0x3e3e, 0x78e4,
+       0xa084, 0x0008, 0x0040, 0x3e38, 0xa6b5, 0x0008, 0x2019, 0x0000,
+       0xa684, 0x0008, 0x0040, 0x3e3e, 0x1078, 0x417c, 0x6810, 0x70be,
+       0x7003, 0x0007, 0x2300, 0x0079, 0x3e45, 0x3e48, 0x3e75, 0x3e7d,
+       0x2200, 0x0079, 0x3e4b, 0x3e50, 0x3e4e, 0x3e69, 0x1078, 0x23eb,
+       0x7990, 0xa1ac, 0x0007, 0xa026, 0x2011, 0x0001, 0x1078, 0x42f1,
+       0x0079, 0x3e5a, 0x3e5f, 0x2459, 0x3b96, 0x3e67, 0x3e61, 0x0078,
+       0x472d, 0x70ab, 0x3e65, 0x0078, 0x2459, 0x0078, 0x3e5f, 0x1078,
+       0x23eb, 0xa684, 0x0010, 0x0040, 0x3e73, 0x1078, 0x419f, 0x0040,
+       0x3e73, 0x0078, 0x2459, 0x0078, 0x420c, 0x2200, 0x0079, 0x3e78,
+       0x3e7b, 0x3e7b, 0x3e7b, 0x1078, 0x23eb, 0x2200, 0x0079, 0x3e80,
+       0x3e83, 0x3e85, 0x3e85, 0x1078, 0x23eb, 0x78e4, 0xa084, 0x0008,
+       0x0040, 0x3e9a, 0x70a7, 0x3e8e, 0x0078, 0x4739, 0x2011, 0x0004,
+       0x1078, 0x42eb, 0x0079, 0x3e94, 0x3e9a, 0x2459, 0x3b96, 0x3e9a,
+       0x3ea4, 0x3ea8, 0x70ab, 0x3ea2, 0x2001, 0x0003, 0x1078, 0x4689,
+       0x0078, 0x4733, 0x0078, 0x472d, 0x70ab, 0x3e9a, 0x0078, 0x2459,
+       0x70ab, 0x3eac, 0x0078, 0x2459, 0x0078, 0x3ea2, 0x2300, 0x0079,
+       0x3eb1, 0x3eb6, 0x3eb8, 0x3eb4, 0x1078, 0x23eb, 0x70a4, 0x007a,
+       0x70a4, 0x007a, 0xa282, 0x0002, 0x0050, 0x3ec0, 0x1078, 0x23eb,
+       0xa684, 0x0200, 0x0040, 0x3eca, 0x1078, 0x46f8, 0x1078, 0x42d3,
+       0x1078, 0x46ff, 0x2300, 0x0079, 0x3ecd, 0x3ed0, 0x3ef4, 0x3f5a,
+       0xa286, 0x0001, 0x0040, 0x3ed6, 0x1078, 0x23eb, 0xa684, 0x0200,
+       0x0040, 0x3ede, 0x1078, 0x46f8, 0x1078, 0x46ff, 0x2001, 0x0001,
+       0x1078, 0x4691, 0x78b8, 0xa084, 0xc001, 0x0040, 0x3ef0, 0x7848,
+       0xa085, 0x0008, 0x784a, 0x7848, 0xa084, 0x0008, 0x00c0, 0x3eeb,
+       0x7003, 0x0000, 0x0078, 0x3b96, 0x2200, 0x0079, 0x3ef7, 0x3ef9,
+       0x3f2a, 0x70a7, 0x3efd, 0x0078, 0x4739, 0x2011, 0x000d, 0x1078,
+       0x42eb, 0x0079, 0x3f03, 0x3f0a, 0x2459, 0x3b96, 0x3f12, 0x3f1a,
+       0x3f20, 0x3f22, 0xa6b4, 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a,
+       0x0078, 0x4727, 0xa6b4, 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a,
+       0x0078, 0x4727, 0x70ab, 0x3f1e, 0x0078, 0x2459, 0x0078, 0x3f0a,
+       0x1078, 0x23eb, 0x70ab, 0x3f26, 0x0078, 0x2459, 0x1078, 0x473f,
+       0x0078, 0x2459, 0x70a7, 0x3f2e, 0x0078, 0x4739, 0x2011, 0x0012,
+       0x1078, 0x42eb, 0x0079, 0x3f34, 0x3f3a, 0x2459, 0x3b96, 0x3f46,
+       0x3f4e, 0x3f54, 0xa6b4, 0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a,
+       0x70b4, 0xa080, 0x00a6, 0x781a, 0x0078, 0x2459, 0xa6b4, 0x00ff,
+       0xa6b5, 0x0400, 0x6eb6, 0x7e5a, 0x0078, 0x4727, 0x70ab, 0x3f52,
+       0x0078, 0x2459, 0x0078, 0x3f3a, 0x70ab, 0x3f58, 0x0078, 0x2459,
+       0x0078, 0x3f46, 0xa286, 0x0001, 0x0040, 0x3f60, 0x1078, 0x23eb,
+       0x70a7, 0x3f64, 0x0078, 0x4739, 0x2011, 0x0015, 0x1078, 0x42eb,
+       0x0079, 0x3f6a, 0x3f6f, 0x2459, 0x3b96, 0x3f7d, 0x3f89, 0xa6b4,
+       0x00ff, 0xa6b5, 0x0400, 0x6eb6, 0x7e5a, 0x783b, 0x1301, 0x70b4,
+       0xa080, 0x00b4, 0x781a, 0x0078, 0x2459, 0xa6b4, 0x00ff, 0xa6b5,
+       0x0400, 0x6eb6, 0x7e5a, 0x70b4, 0xa080, 0x00a6, 0x781a, 0x0078,
+       0x2459, 0x70ab, 0x3f8d, 0x0078, 0x2459, 0x0078, 0x3f6f, 0xa282,
+       0x0003, 0x0050, 0x3f95, 0x1078, 0x23eb, 0x2300, 0x0079, 0x3f98,
+       0x3f9b, 0x3fd2, 0x402d, 0xa286, 0x0001, 0x0040, 0x3fa1, 0x1078,
+       0x23eb, 0x6804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0, 0x3fae,
+       0x1078, 0x3af8, 0x7003, 0x0000, 0x0078, 0x3b96, 0x683b, 0x0000,
+       0x6837, 0x0000, 0xa684, 0x0200, 0x0040, 0x3fbc, 0x1078, 0x46f8,
+       0x1078, 0x42d3, 0x1078, 0x46ff, 0x2001, 0x0001, 0x1078, 0x4691,
+       0x78b8, 0xa084, 0xc001, 0x0040, 0x3fce, 0x7848, 0xa085, 0x0008,
+       0x784a, 0x7848, 0xa084, 0x0008, 0x00c0, 0x3fc9, 0x7003, 0x0000,
+       0x0078, 0x3b96, 0x2200, 0x0079, 0x3fd5, 0x3fd7, 0x4008, 0x70a7,
+       0x3fdb, 0x0078, 0x4739, 0x2011, 0x000d, 0x1078, 0x42eb, 0x0079,
+       0x3fe1, 0x3fe8, 0x2459, 0x3b96, 0x3ff0, 0x3ff8, 0x3ffe, 0x4000,
+       0xa6b4, 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x4727,
+       0xa6b4, 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x4727,
+       0x70ab, 0x3ffc, 0x0078, 0x2459, 0x0078, 0x3fe8, 0x1078, 0x23eb,
+       0x70ab, 0x4004, 0x0078, 0x2459, 0x1078, 0x473f, 0x0078, 0x2459,
+       0x70a7, 0x400c, 0x0078, 0x4739, 0x2011, 0x0005, 0x1078, 0x42eb,
+       0x0079, 0x4012, 0x4017, 0x2459, 0x3b96, 0x401f, 0x4027, 0xa6b4,
+       0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x4727, 0xa6b4,
+       0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x4727, 0x70ab,
+       0x402b, 0x0078, 0x2459, 0x0078, 0x4017, 0xa286, 0x0001, 0x0040,
+       0x4033, 0x1078, 0x23eb, 0x70a7, 0x4037, 0x0078, 0x4739, 0x2011,
+       0x0006, 0x1078, 0x42eb, 0x0079, 0x403d, 0x4042, 0x2459, 0x3b96,
+       0x4048, 0x4052, 0xa6b5, 0x0800, 0x6eb6, 0x7e5a, 0x0078, 0x4727,
+       0xa6b4, 0x00ff, 0xa6b5, 0x0800, 0x6eb6, 0xa6b5, 0x4000, 0x7e5a,
+       0x0078, 0x4727, 0x70ab, 0x4056, 0x0078, 0x2459, 0x0078, 0x4042,
+       0x2300, 0x0079, 0x405b, 0x4060, 0x405e, 0x405e, 0x1078, 0x23eb,
+       0x1078, 0x23eb, 0x2300, 0x71a8, 0xa005, 0x017a, 0x6810, 0x70be,
+       0xa282, 0x0003, 0x0050, 0x406e, 0x1078, 0x23eb, 0x2300, 0x0079,
+       0x4071, 0x4074, 0x4082, 0x40a4, 0xa684, 0x0200, 0x0040, 0x407c,
+       0x1078, 0x46f8, 0x1078, 0x46ff, 0x2001, 0x0001, 0x1078, 0x4691,
+       0x0078, 0x2459, 0xa296, 0x0002, 0x0040, 0x408b, 0x82ff, 0x0040,
+       0x408b, 0x1078, 0x23eb, 0x70a7, 0x408f, 0x0078, 0x4739, 0x2011,
+       0x0018, 0x1078, 0x42eb, 0x0079, 0x4095, 0x409a, 0x2459, 0x3b96,
+       0x409c, 0x409e, 0x0078, 0x4727, 0x0078, 0x4727, 0x70ab, 0x40a2,
+       0x0078, 0x2459, 0x0078, 0x409a, 0x2200, 0x0079, 0x40a7, 0x40a9,
+       0x40c2, 0x70a7, 0x40ad, 0x0078, 0x4739, 0x2011, 0x0017, 0x1078,
+       0x42eb, 0x0079, 0x40b3, 0x40b8, 0x2459, 0x3b96, 0x40ba, 0x40bc,
+       0x0078, 0x4727, 0x0078, 0x4727, 0x70ab, 0x40c0, 0x0078, 0x2459,
+       0x0078, 0x40b8, 0xa484, 0x8000, 0x00c0, 0x410c, 0xa684, 0x0100,
+       0x0040, 0x40d6, 0x1078, 0x46f8, 0x1078, 0x42d3, 0x1078, 0x46ff,
+       0x7848, 0xa085, 0x000c, 0x784a, 0x0078, 0x40da, 0x78d8, 0x78d2,
+       0x78dc, 0x78d6, 0xa6b4, 0xefff, 0x7e5a, 0x70a7, 0x40e1, 0x0078,
+       0x4739, 0x2011, 0x000d, 0x1078, 0x42eb, 0x0079, 0x40e7, 0x40ee,
+       0x2459, 0x3b96, 0x40ee, 0x40fc, 0x4102, 0x4104, 0xa684, 0x0100,
+       0x0040, 0x40fa, 0x1078, 0x46b6, 0x682c, 0x78d2, 0x6830, 0x78d6,
+       0x1078, 0x46f0, 0x0078, 0x4727, 0x70ab, 0x4100, 0x0078, 0x2459,
+       0x0078, 0x40ee, 0x1078, 0x23eb, 0x70ab, 0x4108, 0x0078, 0x2459,
+       0x1078, 0x473f, 0x0078, 0x2459, 0x1078, 0x46ff, 0x70ab, 0x4116,
+       0x2001, 0x0003, 0x1078, 0x4689, 0x0078, 0x4733, 0x1078, 0x46f0,
+       0x682c, 0x78d2, 0x6830, 0x78d6, 0x0078, 0x4727, 0x70b8, 0x6812,
+       0x70be, 0x8000, 0x70ba, 0x681b, 0x0000, 0xa684, 0x0008, 0x0040,
+       0x4141, 0x157e, 0x137e, 0x147e, 0x7890, 0x8004, 0x8004, 0x8004,
+       0x8004, 0xa084, 0x000f, 0x681a, 0x80ac, 0x789b, 0x0000, 0xaf80,
+       0x002b, 0x2098, 0xad80, 0x000b, 0x20a0, 0x53a5, 0x147f, 0x137f,
+       0x157f, 0xa6c4, 0x0f00, 0xa684, 0x0002, 0x00c0, 0x4150, 0x692c,
+       0x810d, 0x810d, 0x810d, 0xa184, 0x0007, 0x2008, 0x0078, 0x415f,
+       0x789b, 0x0010, 0x79ac, 0xa184, 0x0020, 0x0040, 0x415f, 0x017e,
+       0x2009, 0x0005, 0x2001, 0x3d00, 0x1078, 0x46c1, 0x017f, 0xa184,
+       0x001f, 0xa805, 0x6816, 0x1078, 0x3b69, 0x68be, 0xa684, 0x0004,
+       0x0040, 0x4170, 0xa18c, 0xff00, 0x78a8, 0xa084, 0x00ff, 0xa105,
+       0x682a, 0xa6b4, 0x00ff, 0x6000, 0xa084, 0x0008, 0x0040, 0x417a,
+       0xa6b5, 0x4000, 0x6eb6, 0x007c, 0x157e, 0x137e, 0x147e, 0x6918,
+       0x7890, 0x8004, 0x8004, 0x8004, 0x8004, 0xa084, 0x000f, 0x007e,
+       0xa100, 0x681a, 0x007f, 0x8000, 0x8004, 0x0040, 0x419b, 0x20a8,
+       0x8104, 0xa080, 0x000b, 0xad00, 0x20a0, 0x789b, 0x0000, 0xaf80,
+       0x002b, 0x2098, 0x53a5, 0x147f, 0x137f, 0x157f, 0x007c, 0x682c,
+       0xa084, 0x0020, 0x00c0, 0x41a7, 0x620c, 0x0078, 0x41a8, 0x6210,
+       0x6b18, 0x2300, 0xa202, 0x0040, 0x41c8, 0x2018, 0xa382, 0x000e,
+       0x0048, 0x41b8, 0x0040, 0x41b8, 0x2019, 0x000e, 0x0078, 0x41bc,
+       0x7858, 0xa084, 0xffef, 0x785a, 0x783b, 0x1b01, 0x7893, 0x0000,
+       0x7ba2, 0x70b4, 0xa080, 0x008e, 0x781a, 0xa085, 0x0001, 0x007c,
+       0x7858, 0xa084, 0xffef, 0x785a, 0x7893, 0x0000, 0xa006, 0x007c,
+       0x6904, 0xa18c, 0x00ff, 0xa196, 0x0007, 0x0040, 0x41dd, 0xa196,
+       0x000f, 0x0040, 0x41dd, 0x6807, 0x0117, 0x6914, 0x1078, 0x3b69,
+       0x6100, 0x8104, 0x00c8, 0x41f8, 0x601c, 0xa005, 0x0040, 0x41ec,
+       0x2001, 0x0800, 0x0078, 0x41fa, 0x0d7e, 0x6824, 0x007e, 0x1078,
+       0x4708, 0x007f, 0x6826, 0x2d00, 0x682e, 0x1078, 0x3af8, 0x0d7f,
+       0x2001, 0x0200, 0x6826, 0x8007, 0x789b, 0x000e, 0x78aa, 0x6820,
+       0xa085, 0x8000, 0x6822, 0x2031, 0x0400, 0x6eb6, 0x7e5a, 0x71b4,
+       0xa188, 0x0091, 0x791a, 0x007c, 0xa6c4, 0x0f00, 0xa684, 0x0002,
+       0x00c0, 0x4220, 0x692c, 0x810d, 0x810d, 0x810d, 0xa184, 0x0007,
+       0x2008, 0xa805, 0x6816, 0x1078, 0x3b69, 0x68be, 0x0078, 0x4223,
+       0x6914, 0x1078, 0x3b69, 0x6100, 0x8104, 0x00c8, 0x4280, 0xa184,
+       0x0300, 0x0040, 0x422f, 0x6807, 0x0117, 0x0078, 0x424d, 0x6004,
+       0xa005, 0x00c0, 0x4256, 0x6807, 0x0117, 0x601c, 0xa005, 0x00c0,
+       0x4243, 0x0d7e, 0x1078, 0x4708, 0x6827, 0x0034, 0x2d00, 0x682e,
+       0x1078, 0x3af8, 0x0d7f, 0xa684, 0x0004, 0x0040, 0x424d, 0x2031,
+       0x0400, 0x2001, 0x2800, 0x0078, 0x4251, 0x2031, 0x0400, 0x2001,
+       0x0800, 0x71b4, 0xa188, 0x0091, 0x0078, 0x42ae, 0x6018, 0xa005,
+       0x00c0, 0x4243, 0x601c, 0xa005, 0x00c0, 0x4243, 0x689f, 0x0000,
+       0x6827, 0x003d, 0xa684, 0x0001, 0x0040, 0x42bc, 0xd694, 0x00c0,
+       0x4279, 0x6100, 0xd1d4, 0x0040, 0x4279, 0x692c, 0x81ff, 0x0040,
+       0x42bc, 0xa186, 0x0003, 0x0040, 0x42bc, 0xa186, 0x0012, 0x0040,
+       0x42bc, 0xa6b5, 0x0800, 0x71b4, 0xa188, 0x00af, 0x0078, 0x42b7,
+       0x6807, 0x0117, 0x2031, 0x0400, 0x692c, 0xa18c, 0x00ff, 0xa186,
+       0x0012, 0x00c0, 0x4291, 0x2001, 0x42c9, 0x2009, 0x0001, 0x0078,
+       0x42a2, 0xa186, 0x0003, 0x00c0, 0x429b, 0x2001, 0x42ca, 0x2009,
+       0x0012, 0x0078, 0x42a2, 0x2001, 0x0200, 0x71b4, 0xa188, 0x0091,
+       0x0078, 0x42ae, 0x1078, 0x46db, 0x78a3, 0x0000, 0x681c, 0xa085,
+       0x0040, 0x681e, 0x71b4, 0xa188, 0x00df, 0xa006, 0x6826, 0x8007,
+       0x789b, 0x000e, 0x78aa, 0x6820, 0xa085, 0x8000, 0x6822, 0x6eb6,
+       0x7e5a, 0x791a, 0x0078, 0x2459, 0x6eb6, 0x1078, 0x3af8, 0x6810,
+       0x70be, 0x7003, 0x0007, 0x70a3, 0x0000, 0x704b, 0x0000, 0x0078,
+       0x2459, 0x0023, 0x0070, 0x0005, 0x0000, 0x0a00, 0x0000, 0x0000,
+       0x0025, 0x0000, 0x0000, 0x683b, 0x0000, 0x6837, 0x0000, 0xa684,
+       0x0200, 0x0040, 0x42ea, 0x78b8, 0xa08c, 0x001f, 0xa084, 0x8000,
+       0x0040, 0x42e3, 0x8108, 0x78d8, 0xa100, 0x6836, 0x78dc, 0xa081,
+       0x0000, 0x683a, 0x007c, 0x7990, 0x810f, 0xa5ac, 0x0007, 0x2021,
+       0x0000, 0xa480, 0x0010, 0x789a, 0x79a8, 0xa18c, 0x00ff, 0xa184,
+       0x0080, 0x00c0, 0x4319, 0xa182, 0x0020, 0x00c8, 0x4337, 0xa182,
+       0x0012, 0x00c8, 0x467b, 0x2100, 0x1079, 0x4307, 0x007c, 0x467b,
+       0x44e8, 0x467b, 0x467b, 0x4344, 0x4347, 0x4381, 0x43b7, 0x43eb,
+       0x43ee, 0x467b, 0x467b, 0x43a2, 0x4412, 0x444c, 0x467b, 0x467b,
+       0x4473, 0xa184, 0x0020, 0x00c0, 0x44a7, 0xa18c, 0x001f, 0x6814,
+       0xa084, 0x001f, 0xa106, 0x0040, 0x4334, 0x70b4, 0xa080, 0x00d2,
+       0x781a, 0x2001, 0x0014, 0x1078, 0x4691, 0x1078, 0x46ff, 0x7003,
+       0x0000, 0x2001, 0x0002, 0x007c, 0x2001, 0x0000, 0x007c, 0xa182,
+       0x0024, 0x00c8, 0x467b, 0xa184, 0x0003, 0x1079, 0x4307, 0x007c,
+       0x467b, 0x467b, 0x467b, 0x467b, 0x1078, 0x467b, 0x007c, 0x2200,
+       0x0079, 0x434a, 0x4476, 0x4476, 0x436e, 0x436e, 0x436e, 0x436e,
+       0x436e, 0x436e, 0x436e, 0x436e, 0x436c, 0x436e, 0x4363, 0x436e,
+       0x436e, 0x436e, 0x436e, 0x436e, 0x4376, 0x4379, 0x4476, 0x4379,
+       0x436e, 0x436e, 0x436e, 0x0c7e, 0x077e, 0x6f14, 0x1078, 0x36e2,
+       0x077f, 0x0c7f, 0x0078, 0x436e, 0x1078, 0x458b, 0x6827, 0x02b3,
+       0x2009, 0x000b, 0x2001, 0x4800, 0x0078, 0x44aa, 0x1078, 0x4670,
+       0x007c, 0x6827, 0x0093, 0x2009, 0x000b, 0x2001, 0x4800, 0x0078,
+       0x4492, 0x2d58, 0x6804, 0xa084, 0x00ff, 0xa086, 0x0006, 0x00c0,
+       0x438b, 0x6807, 0x0117, 0x6827, 0x0002, 0x1078, 0x4708, 0x6827,
+       0x0036, 0x6932, 0x2d00, 0x682e, 0x0d7e, 0x1078, 0x3ac8, 0x1078,
+       0x44d0, 0x2b68, 0x1078, 0x3af8, 0x0d7f, 0x1078, 0x3af8, 0x2001,
+       0x0002, 0x007c, 0x1078, 0x44d0, 0x2001, 0x0017, 0x1078, 0x4691,
+       0x70a3, 0x0000, 0x2009, 0x5138, 0x200b, 0x0006, 0x70af, 0x0017,
+       0x2009, 0x0200, 0x1078, 0x3a06, 0x2001, 0x0001, 0x007c, 0x2200,
+       0x0079, 0x43ba, 0x4476, 0x44a7, 0x44a7, 0x44a7, 0x43db, 0x44b7,
+       0x43e3, 0x44b7, 0x44b7, 0x44ba, 0x44ba, 0x44bf, 0x44bf, 0x43d3,
+       0x43d3, 0x44a7, 0x44a7, 0x44b7, 0x44a7, 0x43e3, 0x4476, 0x43e3,
+       0x43e3, 0x43e3, 0x43e3, 0x6827, 0x0084, 0x2009, 0x000b, 0x2001,
+       0x4300, 0x0078, 0x44c9, 0x6827, 0x000d, 0x2009, 0x000b, 0x2001,
+       0x4300, 0x0078, 0x44aa, 0x6827, 0x0093, 0x2009, 0x000b, 0x2001,
+       0x4300, 0x0078, 0x4492, 0x2001, 0x0000, 0x007c, 0x2200, 0x0079,
+       0x43f1, 0x4476, 0x440a, 0x440a, 0x440a, 0x440a, 0x44b7, 0x44b7,
+       0x44b7, 0x44b7, 0x44b7, 0x44b7, 0x44b7, 0x44b7, 0x440a, 0x440a,
+       0x440a, 0x440a, 0x44b7, 0x440a, 0x440a, 0x44b7, 0x44b7, 0x44b7,
+       0x44b7, 0x4476, 0x6827, 0x0093, 0x2009, 0x000b, 0x2001, 0x4300,
+       0x0078, 0x4492, 0xa684, 0x0004, 0x00c0, 0x4426, 0x6804, 0xa084,
+       0x00ff, 0xa086, 0x0006, 0x00c0, 0x467b, 0x1078, 0x44d0, 0x6807,
+       0x0117, 0x1078, 0x3af8, 0x2001, 0x0002, 0x007c, 0x6000, 0xa084,
+       0x0004, 0x0040, 0x467b, 0x2d58, 0x6804, 0xa084, 0x00ff, 0xa086,
+       0x0006, 0x00c0, 0x4435, 0x6807, 0x0117, 0x6827, 0x0002, 0x1078,
+       0x4708, 0x6827, 0x0036, 0x6932, 0x2d00, 0x682e, 0x0d7e, 0x1078,
+       0x3ad7, 0x1078, 0x44d0, 0x2b68, 0x1078, 0x3af8, 0x0d7f, 0x1078,
+       0x3af8, 0x2001, 0x0002, 0x007c, 0x6000, 0xa084, 0x0004, 0x0040,
+       0x467b, 0x2d58, 0x6a04, 0xa294, 0x00ff, 0xa286, 0x0006, 0x00c0,
+       0x445b, 0x6807, 0x0117, 0x6827, 0x0002, 0x2d58, 0x1078, 0x4708,
+       0x6827, 0x0036, 0x6932, 0x2d00, 0x682e, 0x0d7e, 0x1078, 0x3ae7,
+       0x1078, 0x44d0, 0x2b68, 0x1078, 0x3af8, 0x0d7f, 0x1078, 0x3af8,
+       0x2001, 0x0002, 0x007c, 0x1078, 0x467b, 0x007c, 0x70b4, 0xa080,
+       0x00d2, 0x781a, 0x2001, 0x0001, 0x1078, 0x4691, 0x1078, 0x46ff,
+       0x7003, 0x0000, 0x2001, 0x0002, 0x007c, 0x1078, 0x46c1, 0x1078,
+       0x46f8, 0x1078, 0x42d3, 0x1078, 0x41d0, 0x1078, 0x46ff, 0x2001,
+       0x0001, 0x007c, 0x1078, 0x46c1, 0x1078, 0x46f8, 0x1078, 0x42d3,
+       0x70b4, 0xa080, 0x00d2, 0x781a, 0x2001, 0x0013, 0x1078, 0x4691,
+       0x1078, 0x46ff, 0x7003, 0x0000, 0x2001, 0x0002, 0x007c, 0x1078,
+       0x467b, 0x007c, 0x1078, 0x46c1, 0x1078, 0x46f8, 0x1078, 0x42d3,
+       0x1078, 0x41d0, 0x1078, 0x46ff, 0x2001, 0x0001, 0x007c, 0x2001,
+       0x0003, 0x007c, 0x1078, 0x458b, 0x2001, 0x0000, 0x007c, 0x0c7e,
+       0x077e, 0x6f14, 0x1078, 0x36e2, 0x077f, 0x0c7f, 0x2001, 0x0000,
+       0x007c, 0x1078, 0x46c1, 0x1078, 0x467b, 0x2001, 0x0006, 0x007c,
+       0x6904, 0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x44db, 0xa186,
+       0x000f, 0x00c0, 0x44df, 0x1078, 0x46f8, 0x1078, 0x42d3, 0x70b4,
+       0xa080, 0x00d2, 0x781a, 0x1078, 0x46ff, 0x7003, 0x0000, 0x007c,
+       0x7aa8, 0xa294, 0x00ff, 0x78a8, 0xa084, 0x00ff, 0xa08a, 0x0004,
+       0x00c8, 0x467b, 0x1079, 0x44f5, 0x007c, 0x467b, 0x44f9, 0x467b,
+       0x4592, 0xa282, 0x0003, 0x0040, 0x4500, 0x1078, 0x467b, 0x007c,
+       0x7da8, 0xa5ac, 0x00ff, 0x7ca8, 0xa4a4, 0x00ff, 0x69b8, 0xa184,
+       0x0100, 0x0040, 0x453f, 0xa18c, 0xfeff, 0x69ba, 0x78a0, 0xa005,
+       0x00c0, 0x453f, 0xa4a4, 0x00ff, 0x0040, 0x4533, 0xa482, 0x000c,
+       0x0040, 0x451c, 0x00c8, 0x4526, 0x852b, 0x852b, 0x1078, 0x3760,
+       0x0040, 0x4526, 0x1078, 0x355b, 0x0078, 0x4535, 0x1078, 0x465d,
+       0x1078, 0x3586, 0x69b8, 0xa18d, 0x0100, 0x69ba, 0xa6b5, 0x1000,
+       0x7e5a, 0x0078, 0x4538, 0x1078, 0x3586, 0xa6b4, 0xefff, 0x7e5a,
+       0x70b4, 0xa080, 0x0091, 0x781a, 0x2001, 0x0001, 0x007c, 0x0c7e,
+       0x1078, 0x457f, 0x6200, 0xd2e4, 0x0040, 0x4570, 0x6208, 0x8217,
+       0xa294, 0x00ff, 0xa282, 0x000c, 0x0048, 0x4552, 0x0040, 0x4552,
+       0x2011, 0x000c, 0x2400, 0xa202, 0x00c8, 0x4557, 0x2220, 0x6208,
+       0xa294, 0x00ff, 0x701c, 0xa202, 0x00c8, 0x455f, 0x721c, 0x2200,
+       0xa502, 0x00c8, 0x4564, 0x2228, 0x1078, 0x4661, 0x852b, 0x852b,
+       0x1078, 0x3760, 0x0040, 0x4570, 0x1078, 0x3562, 0x0078, 0x4574,
+       0x1078, 0x465d, 0x1078, 0x358d, 0xa6b5, 0x1000, 0x7e5a, 0x70b4,
+       0xa080, 0x00be, 0x781a, 0x2001, 0x0004, 0x0c7f, 0x007c, 0x007e,
+       0x6814, 0x8007, 0xa084, 0x000f, 0x8003, 0x8003, 0x8003, 0xa0e0,
+       0x5380, 0x007f, 0x007c, 0x0c7e, 0x1078, 0x457f, 0x1078, 0x358d,
+       0x0c7f, 0x007c, 0xa282, 0x0002, 0x00c0, 0x467b, 0x7aa8, 0xa294,
+       0x00ff, 0x69b8, 0xa184, 0x0200, 0x0040, 0x45c9, 0xa18c, 0xfdff,
+       0x69ba, 0x78a0, 0xa005, 0x00c0, 0x45c9, 0xa282, 0x0002, 0x00c8,
+       0x369d, 0x1078, 0x4627, 0x1078, 0x362b, 0x1078, 0x3586, 0xa684,
+       0x0100, 0x0040, 0x45bf, 0x682c, 0xa084, 0x0001, 0x0040, 0x45bf,
+       0xc6fc, 0x7888, 0xa084, 0x0040, 0x0040, 0x45bf, 0xc6fd, 0xa6b5,
+       0x1000, 0x7e5a, 0x70b4, 0xa080, 0x0091, 0x781a, 0x2001, 0x0001,
+       0x007c, 0x0c7e, 0x1078, 0x457f, 0xa284, 0xfffe, 0x0040, 0x45d4,
+       0x2011, 0x0001, 0x0078, 0x45d8, 0xa284, 0x0001, 0x0040, 0x45de,
+       0x6100, 0xd1ec, 0x00c0, 0x45de, 0x2011, 0x0000, 0x1078, 0x4619,
+       0x1078, 0x3632, 0x1078, 0x358d, 0xa684, 0x0100, 0x0040, 0x45f4,
+       0x682c, 0xa084, 0x0001, 0x0040, 0x45f4, 0xc6fc, 0x7888, 0xa084,
+       0x0040, 0x0040, 0x45f4, 0xc6fd, 0xa6b5, 0x1000, 0x7e5a, 0x70b4,
+       0xa080, 0x00be, 0x781a, 0x2001, 0x0004, 0x0c7f, 0x007c, 0x0c7e,
+       0x2960, 0x6000, 0x2011, 0x0001, 0xa084, 0x2000, 0x00c0, 0x460a,
+       0x2011, 0x0000, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab, 0x0003,
+       0x7aaa, 0xa8c0, 0x0004, 0x68b8, 0xa085, 0x0200, 0x68ba, 0x0c7f,
+       0x007c, 0x789b, 0x0018, 0x78ab, 0x0001, 0x78ab, 0x0002, 0x78ab,
+       0x0003, 0x7aaa, 0x789b, 0x0081, 0x78ab, 0x0004, 0x007c, 0x0c7e,
+       0x7054, 0x2060, 0x6000, 0xa084, 0x1000, 0x00c0, 0x4635, 0x2029,
+       0x0032, 0x2021, 0x0000, 0x0078, 0x4655, 0x6508, 0xa5ac, 0x00ff,
+       0x7018, 0xa086, 0x0028, 0x00c0, 0x4645, 0xa582, 0x0019, 0x00c8,
+       0x464b, 0x2029, 0x0019, 0x0078, 0x464b, 0xa582, 0x000c, 0x00c8,
+       0x464b, 0x2029, 0x000c, 0x6408, 0x8427, 0xa4a4, 0x00ff, 0xa482,
+       0x000c, 0x0048, 0x4655, 0x2021, 0x000c, 0x1078, 0x4661, 0x68b8,
+       0xa085, 0x0100, 0x68ba, 0x0c7f, 0x007c, 0x2021, 0x0000, 0x2029,
+       0x0032, 0x789b, 0x0018, 0x78ab, 0x0001, 0x78ab, 0x0003, 0x78ab,
+       0x0001, 0x7daa, 0x7caa, 0x789b, 0x0081, 0x78ab, 0x0005, 0x007c,
+       0x2001, 0x0003, 0x1078, 0x4689, 0x70b4, 0xa080, 0x00be, 0x781a,
+       0x2001, 0x0005, 0x007c, 0x2001, 0x0007, 0x1078, 0x4689, 0xa6b5,
+       0x1000, 0x7e5a, 0x70b4, 0xa080, 0x00be, 0x781a, 0x2001, 0x0004,
+       0x007c, 0x789b, 0x0018, 0x78aa, 0x789b, 0x0081, 0x78ab, 0x0001,
+       0x007c, 0x6904, 0xa18c, 0x00ff, 0xa196, 0x0007, 0x0040, 0x469f,
+       0xa196, 0x000f, 0x0040, 0x469f, 0x1078, 0x195a, 0x007c, 0x6924,
+       0xa194, 0x003f, 0x00c0, 0x46a8, 0xa18c, 0xffc0, 0xa105, 0x6826,
+       0x1078, 0x3af8, 0x691c, 0xa184, 0x0100, 0x0040, 0x46b5, 0x6914,
+       0x1078, 0x3b69, 0x6204, 0x8210, 0x6206, 0x007c, 0x692c, 0x6834,
+       0x682e, 0xa112, 0x6930, 0x6838, 0x6832, 0xa11b, 0xa200, 0xa301,
+       0x007c, 0x0c7e, 0xade0, 0x0018, 0x6003, 0x0070, 0x6106, 0x600b,
+       0x0000, 0x600f, 0x0a00, 0x6013, 0x0000, 0x6017, 0x0000, 0x8007,
+       0x601a, 0x601f, 0x0000, 0x6023, 0x0000, 0x0c7f, 0x6824, 0xa085,
+       0x0080, 0x6826, 0x007c, 0x157e, 0x137e, 0x147e, 0x2098, 0xaf80,
+       0x002d, 0x20a0, 0x81ac, 0x0040, 0x46e6, 0x53a6, 0xa184, 0x0001,
+       0x0040, 0x46ec, 0x3304, 0x78be, 0x147f, 0x137f, 0x157f, 0x007c,
+       0x70b0, 0xa005, 0x10c0, 0x23eb, 0x70b3, 0x8000, 0x0078, 0x4a3a,
+       0x71b0, 0x81ff, 0x0040, 0x46fe, 0x1078, 0x4b30, 0x007c, 0x71b0,
+       0x81ff, 0x0040, 0x4707, 0x70b3, 0x0000, 0x1078, 0x4776, 0x007c,
+       0x0c7e, 0x0d7e, 0x1078, 0x1937, 0x0c7f, 0x157e, 0x137e, 0x147e,
+       0x2da0, 0x2c98, 0x20a9, 0x0031, 0x53a3, 0x147f, 0x137f, 0x157f,
+       0x6807, 0x010d, 0x680b, 0x0000, 0x7004, 0x8007, 0x681a, 0x6823,
+       0x0000, 0x681f, 0x0000, 0x689f, 0x0000, 0x0c7f, 0x007c, 0x70b4,
+       0xa080, 0x0091, 0x781a, 0x0078, 0x2459, 0x70b4, 0xa080, 0x0081,
+       0x781a, 0x0078, 0x2459, 0x70b4, 0xa080, 0x00be, 0x781a, 0x0078,
+       0x2459, 0x70b4, 0xa080, 0x00c8, 0x781a, 0x0078, 0x2459, 0x6904,
+       0xa18c, 0x00ff, 0xa196, 0x0007, 0x0040, 0x474c, 0xa196, 0x000f,
+       0x0040, 0x474c, 0x6807, 0x0117, 0x2001, 0x0200, 0x6826, 0x8007,
+       0x789b, 0x000e, 0x78aa, 0x6820, 0xa085, 0x8000, 0x6822, 0x2031,
+       0x0400, 0x6eb6, 0x7e5a, 0x71b4, 0xa188, 0x0091, 0x791a, 0x007c,
+       0x1078, 0x46ff, 0x7848, 0xa085, 0x000c, 0x784a, 0x70b4, 0xa080,
+       0x00d2, 0x781a, 0x2009, 0x000b, 0x2001, 0x4400, 0x1078, 0x46c1,
+       0x2001, 0x0013, 0x1078, 0x4691, 0x0078, 0x3b96, 0x127e, 0x2091,
+       0x2200, 0x2049, 0x4776, 0x7000, 0x7204, 0xa205, 0x720c, 0xa215,
+       0x7008, 0xa084, 0xfff7, 0xa205, 0x0040, 0x4788, 0x0078, 0x478d,
+       0x7003, 0x0000, 0x127f, 0x2000, 0x007c, 0x7000, 0xa084, 0x0001,
+       0x00c0, 0x47bb, 0x7108, 0x8103, 0x00c8, 0x479a, 0x1078, 0x48bd,
+       0x0078, 0x4792, 0x700c, 0xa08c, 0x00ff, 0x0040, 0x47bb, 0x7004,
+       0x8004, 0x00c8, 0x47b2, 0x7014, 0xa005, 0x00c0, 0x47ae, 0x7010,
+       0xa005, 0x0040, 0x47b2, 0xa102, 0x00c8, 0x4792, 0x7007, 0x0010,
+       0x0078, 0x47bb, 0x8aff, 0x0040, 0x47bb, 0x1078, 0x4b07, 0x00c0,
+       0x47b5, 0x0040, 0x4792, 0x1078, 0x4846, 0x7003, 0x0000, 0x127f,
+       0x2000, 0x007c, 0x017e, 0x6104, 0xa18c, 0x00ff, 0xa186, 0x0007,
+       0x0040, 0x47ce, 0xa18e, 0x000f, 0x00c0, 0x47d1, 0x6040, 0x0078,
+       0x47d2, 0x6428, 0x017f, 0x84ff, 0x0040, 0x47fc, 0x2c70, 0x7004,
+       0xa0bc, 0x000f, 0xa7b8, 0x480c, 0x273c, 0x87fb, 0x00c0, 0x47ea,
+       0x0048, 0x47e4, 0x1078, 0x23eb, 0x609c, 0xa075, 0x0040, 0x47fc,
+       0x0078, 0x47d7, 0x2704, 0xae68, 0x6808, 0xa630, 0x680c, 0xa529,
+       0x8421, 0x0040, 0x47fc, 0x8738, 0x2704, 0xa005, 0x00c0, 0x47eb,
+       0x709c, 0xa075, 0x00c0, 0x47d7, 0x007c, 0x0000, 0x0005, 0x0009,
+       0x000d, 0x0011, 0x0015, 0x0019, 0x001d, 0x0000, 0x0003, 0x0009,
+       0x000f, 0x0015, 0x001b, 0x0000, 0x0000, 0x4801, 0x47fe, 0x0000,
+       0x0000, 0x8000, 0x0000, 0x4801, 0x0000, 0x4809, 0x4806, 0x0000,
+       0x0000, 0x0000, 0x0000, 0x4809, 0x0000, 0x4804, 0x4804, 0x0000,
+       0x0000, 0x8000, 0x0000, 0x4804, 0x0000, 0x480a, 0x480a, 0x0000,
+       0x0000, 0x0000, 0x0000, 0x480a, 0x127e, 0x2091, 0x2200, 0x2079,
+       0x5100, 0x2071, 0x0010, 0x7007, 0x000a, 0x7007, 0x0002, 0x7003,
+       0x0000, 0x2071, 0x0020, 0x7007, 0x000a, 0x7007, 0x0002, 0x7003,
+       0x0000, 0x2049, 0x0000, 0x127f, 0x2000, 0x007c, 0x2049, 0x4846,
+       0x2019, 0x0000, 0x7004, 0x8004, 0x00c8, 0x4899, 0x7007, 0x0012,
+       0x7108, 0x7008, 0xa106, 0x00c0, 0x4850, 0xa184, 0x01e0, 0x0040,
+       0x485b, 0x1078, 0x23eb, 0x2001, 0x04fd, 0x2004, 0xa082, 0x0005,
+       0x00c8, 0x4866, 0xa184, 0x4000, 0x00c0, 0x4850, 0xa19c, 0x300c,
+       0xa386, 0x2004, 0x0040, 0x4874, 0xa386, 0x0008, 0x0040, 0x487f,
+       0xa386, 0x200c, 0x00c0, 0x4850, 0x7200, 0x8204, 0x0048, 0x487f,
+       0x730c, 0xa384, 0x00ff, 0x0040, 0x487f, 0x1078, 0x23eb, 0x7007,
+       0x0012, 0x7000, 0xa084, 0x0001, 0x00c0, 0x4899, 0x7008, 0xa084,
+       0x01e0, 0x00c0, 0x4899, 0x7310, 0x7014, 0xa305, 0x0040, 0x4899,
+       0x710c, 0xa184, 0x0300, 0x00c0, 0x4899, 0xa184, 0x00ff, 0x00c0,
+       0x4846, 0x7007, 0x0012, 0x7007, 0x0008, 0x7004, 0xa084, 0x0008,
+       0x00c0, 0x489d, 0x7007, 0x0012, 0x7108, 0x8103, 0x0048, 0x48a2,
+       0x7003, 0x0000, 0x2049, 0x0000, 0x007c, 0x107e, 0x007e, 0x127e,
+       0x157e, 0x2091, 0x2200, 0x7108, 0x1078, 0x48bd, 0x157f, 0x127f,
+       0x2091, 0x8001, 0x007f, 0x107f, 0x007c, 0x7204, 0x7500, 0x730c,
+       0xa384, 0x0300, 0x00c0, 0x48e4, 0xa184, 0x01e0, 0x00c0, 0x4908,
+       0x7108, 0xa184, 0x01e0, 0x00c0, 0x4908, 0x2001, 0x04fd, 0x2004,
+       0xa082, 0x0005, 0x00c8, 0x48d8, 0xa184, 0x4000, 0x00c0, 0x48c8,
+       0xa184, 0x0007, 0x0079, 0x48dc, 0x48e6, 0x48f8, 0x48e4, 0x48f8,
+       0x48e4, 0x4944, 0x48e4, 0x4942, 0x1078, 0x23eb, 0x7004, 0xa084,
+       0x0010, 0xa085, 0x0002, 0x7006, 0x8aff, 0x00c0, 0x48f3, 0x2049,
+       0x0000, 0x0078, 0x48f7, 0x1078, 0x4b07, 0x00c0, 0x48f3, 0x007c,
+       0x7004, 0xa084, 0x0010, 0xa085, 0x0002, 0x7006, 0x8aff, 0x00c0,
+       0x4903, 0x0078, 0x4907, 0x1078, 0x4b07, 0x00c0, 0x4903, 0x007c,
+       0x7007, 0x0012, 0x7108, 0x00e0, 0x490b, 0x2091, 0x6000, 0x00e0,
+       0x490f, 0x2091, 0x6000, 0x7007, 0x0012, 0x7007, 0x0008, 0x7004,
+       0xa084, 0x0008, 0x00c0, 0x4917, 0x7007, 0x0012, 0x7108, 0x8103,
+       0x0048, 0x491c, 0x7003, 0x0000, 0x7000, 0xa005, 0x00c0, 0x4930,
+       0x7004, 0xa005, 0x00c0, 0x4930, 0x700c, 0xa005, 0x0040, 0x4932,
+       0x0078, 0x4913, 0x2049, 0x0000, 0x1078, 0x3809, 0x6818, 0xa084,
+       0x8000, 0x0040, 0x493d, 0x681b, 0x0002, 0x007c, 0x1078, 0x23eb,
+       0x1078, 0x23eb, 0x1078, 0x49a0, 0x7210, 0x7114, 0x700c, 0xa09c,
+       0x00ff, 0x2800, 0xa300, 0xa211, 0xa189, 0x0000, 0x1078, 0x49a0,
+       0x2704, 0x2c58, 0xac60, 0x6308, 0x2200, 0xa322, 0x630c, 0x2100,
+       0xa31b, 0x2400, 0xa305, 0x0040, 0x4967, 0x00c8, 0x4967, 0x8412,
+       0x8210, 0x830a, 0xa189, 0x0000, 0x2b60, 0x0078, 0x494e, 0x2b60,
+       0x8a07, 0x007e, 0x6004, 0xa084, 0x0008, 0x0040, 0x4973, 0xa7ba,
+       0x4806, 0x0078, 0x4975, 0xa7ba, 0x47fe, 0x007f, 0xa73d, 0x2c00,
+       0x6886, 0x6f8a, 0x6c92, 0x6b8e, 0x7007, 0x0012, 0x1078, 0x4846,
+       0x007c, 0x8738, 0x2704, 0xa005, 0x00c0, 0x4994, 0x609c, 0xa005,
+       0x0040, 0x499d, 0x2060, 0x6004, 0xa084, 0x000f, 0xa080, 0x480c,
+       0x203c, 0x87fb, 0x1040, 0x23eb, 0x8a51, 0x0040, 0x499c, 0x7008,
+       0xa084, 0x0003, 0xa086, 0x0003, 0x007c, 0x2051, 0x0000, 0x007c,
+       0x8a50, 0x8739, 0x2704, 0xa004, 0x00c0, 0x49b4, 0x6000, 0xa064,
+       0x00c0, 0x49ab, 0x2d60, 0x6004, 0xa084, 0x000f, 0xa080, 0x481c,
+       0x203c, 0x87fb, 0x1040, 0x23eb, 0x007c, 0x127e, 0x0d7e, 0x2091,
+       0x2200, 0x0d7f, 0x6884, 0x2060, 0x6888, 0x6b8c, 0x6c90, 0x8057,
+       0xaad4, 0x00ff, 0xa084, 0x00ff, 0x007e, 0x6804, 0xa084, 0x0008,
+       0x007f, 0x0040, 0x49cf, 0xa0b8, 0x4806, 0x0078, 0x49d1, 0xa0b8,
+       0x47fe, 0x7e08, 0xa6b5, 0x000c, 0x6904, 0xa18c, 0x00ff, 0xa186,
+       0x0007, 0x0040, 0x49df, 0xa18e, 0x000f, 0x00c0, 0x49e8, 0x681c,
+       0xa084, 0x0040, 0x0040, 0x49ef, 0xa6b5, 0x0001, 0x0078, 0x49ef,
+       0x681c, 0xa084, 0x0040, 0x0040, 0x49ef, 0xa6b5, 0x0001, 0x7007,
+       0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x49f1, 0x2400, 0xa305,
+       0x00c0, 0x49fc, 0x0078, 0x4a22, 0x2c58, 0x2704, 0x6104, 0xac60,
+       0x6000, 0xa400, 0x701a, 0x6004, 0xa301, 0x701e, 0xa184, 0x0008,
+       0x0040, 0x4a12, 0x6010, 0xa081, 0x0000, 0x7022, 0x6014, 0xa081,
+       0x0000, 0x7026, 0x6208, 0x2400, 0xa202, 0x7012, 0x620c, 0x2300,
+       0xa203, 0x7016, 0x7602, 0x7007, 0x0001, 0x2b60, 0x1078, 0x4981,
+       0x0078, 0x4a24, 0x1078, 0x4b07, 0x00c0, 0x4a22, 0x127f, 0x2000,
+       0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x7007, 0x0004,
+       0x7004, 0xa084, 0x0004, 0x00c0, 0x4a30, 0x7003, 0x0008, 0x127f,
+       0x2000, 0x007c, 0x127e, 0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x2049,
+       0x4a3a, 0x7007, 0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x4a43,
+       0x7e08, 0xa6b5, 0x000c, 0x6904, 0xa18c, 0x00ff, 0xa186, 0x0007,
+       0x0040, 0x4a56, 0xa18e, 0x000f, 0x00c0, 0x4a61, 0x681c, 0xa084,
+       0x0040, 0x0040, 0x4a5d, 0xa6b5, 0x0001, 0x6840, 0x2050, 0x0078,
+       0x4a6a, 0x681c, 0xa084, 0x0020, 0x00c0, 0x4a68, 0xa6b5, 0x0001,
+       0x6828, 0x2050, 0x2d60, 0x6004, 0xa0bc, 0x000f, 0xa7b8, 0x480c,
+       0x273c, 0x87fb, 0x00c0, 0x4a7e, 0x0048, 0x4a78, 0x1078, 0x23eb,
+       0x689c, 0xa065, 0x0040, 0x4a82, 0x0078, 0x4a6b, 0x1078, 0x4b07,
+       0x00c0, 0x4a7e, 0x127f, 0x2000, 0x007c, 0x127e, 0x007e, 0x017e,
+       0x0d7e, 0x2091, 0x2200, 0x0d7f, 0x037f, 0x047f, 0x7e08, 0xa6b5,
+       0x000c, 0x6904, 0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x4a9c,
+       0xa18e, 0x000f, 0x00c0, 0x4aa5, 0x681c, 0xa084, 0x0040, 0x0040,
+       0x4aac, 0xa6b5, 0x0001, 0x0078, 0x4aac, 0x681c, 0xa084, 0x0040,
+       0x0040, 0x4aac, 0xa6b5, 0x0001, 0x2049, 0x4a85, 0x017e, 0x6904,
+       0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x4aba, 0xa18e, 0x000f,
+       0x00c0, 0x4abd, 0x6840, 0x0078, 0x4abe, 0x6828, 0x017f, 0xa055,
+       0x0040, 0x4b04, 0x2d70, 0x2e60, 0x7004, 0xa0bc, 0x000f, 0xa7b8,
+       0x480c, 0x273c, 0x87fb, 0x00c0, 0x4ad8, 0x0048, 0x4ad1, 0x1078,
+       0x23eb, 0x709c, 0xa075, 0x2060, 0x0040, 0x4b04, 0x0078, 0x4ac4,
+       0x2704, 0xae68, 0x6808, 0xa422, 0x680c, 0xa31b, 0x0048, 0x4af1,
+       0x8a51, 0x00c0, 0x4ae5, 0x1078, 0x23eb, 0x8738, 0x2704, 0xa005,
+       0x00c0, 0x4ad9, 0x709c, 0xa075, 0x2060, 0x0040, 0x4b04, 0x0078,
+       0x4ac4, 0x8422, 0x8420, 0x831a, 0xa399, 0x0000, 0x6908, 0x2400,
+       0xa122, 0x690c, 0x2300, 0xa11b, 0x00c8, 0x4b00, 0x1078, 0x23eb,
+       0x2071, 0x0020, 0x0078, 0x49ef, 0x127f, 0x2000, 0x007c, 0x7008,
+       0xa084, 0x0003, 0xa086, 0x0003, 0x0040, 0x4b2f, 0x2704, 0xac08,
+       0x2104, 0x701a, 0x8108, 0x2104, 0x701e, 0x8108, 0x2104, 0x7012,
+       0x8108, 0x2104, 0x7016, 0x6004, 0xa084, 0x0008, 0x0040, 0x4b26,
+       0x8108, 0x2104, 0x7022, 0x8108, 0x2104, 0x7026, 0x7602, 0x7004,
+       0xa084, 0x0010, 0xa085, 0x0001, 0x7006, 0x1078, 0x4981, 0x007c,
+       0x127e, 0x007e, 0x0d7e, 0x2091, 0x2200, 0x2049, 0x4b30, 0x0d7f,
+       0x087f, 0x7108, 0xa184, 0x0003, 0x00c0, 0x4b5a, 0x017e, 0x6904,
+       0xa18c, 0x00ff, 0xa186, 0x0007, 0x0040, 0x4b4a, 0xa18e, 0x000f,
+       0x00c0, 0x4b4d, 0x6840, 0x0078, 0x4b4e, 0x6828, 0x017f, 0xa005,
+       0x0040, 0x4b68, 0x0078, 0x478d, 0x0020, 0x4b5a, 0x1078, 0x4944,
+       0x0078, 0x4b68, 0x00a0, 0x4b61, 0x7108, 0x1078, 0x48bd, 0x0078,
+       0x4b39, 0x7007, 0x0010, 0x00a0, 0x4b63, 0x7108, 0x1078, 0x48bd,
+       0x7008, 0xa086, 0x0008, 0x00c0, 0x4b39, 0x7000, 0xa005, 0x00c0,
+       0x4b39, 0x7003, 0x0000, 0x2049, 0x0000, 0x127f, 0x2000, 0x007c,
+       0x127e, 0x147e, 0x137e, 0x157e, 0x0c7e, 0x0d7e, 0x2091, 0x2200,
+       0x0d7f, 0x2049, 0x4b78, 0xad80, 0x0011, 0x20a0, 0x2099, 0x0031,
+       0x700c, 0xa084, 0x00ff, 0x682a, 0x7007, 0x0008, 0x7007, 0x0002,
+       0x7003, 0x0001, 0x0040, 0x4b97, 0x8000, 0x80ac, 0x53a5, 0x7007,
+       0x0004, 0x7004, 0xa084, 0x0004, 0x00c0, 0x4b99, 0x0c7f, 0x2049,
+       0x0000, 0x7003, 0x0000, 0x157f, 0x137f, 0x147f, 0x127f, 0x2000,
+       0x007c, 0x2091, 0x6000, 0x2091, 0x8000, 0x78cc, 0xa005, 0x0040,
+       0x4bc0, 0x7994, 0x70d0, 0xa106, 0x00c0, 0x4bc0, 0x7804, 0xa005,
+       0x0040, 0x4bc0, 0x7807, 0x0000, 0x0068, 0x4bc0, 0x2091, 0x4080,
+       0x7820, 0x8001, 0x7822, 0x00c0, 0x4c1b, 0x7824, 0x7822, 0x2069,
+       0x5140, 0x6800, 0xa084, 0x0007, 0x0040, 0x4bde, 0xa086, 0x0002,
+       0x0040, 0x4bde, 0x6834, 0xa00d, 0x0040, 0x4bde, 0x2104, 0xa005,
+       0x0040, 0x4bde, 0x8001, 0x200a, 0x0040, 0x4cc3, 0x7848, 0xa005,
+       0x0040, 0x4bec, 0x8001, 0x784a, 0x00c0, 0x4bec, 0x2009, 0x0102,
+       0x6844, 0x200a, 0x1078, 0x21d2, 0x6890, 0xa005, 0x0040, 0x4bf8,
+       0x8001, 0x6892, 0x00c0, 0x4bf8, 0x686f, 0x0000, 0x6873, 0x0001,
+       0x2061, 0x5400, 0x20a9, 0x0100, 0x2009, 0x0002, 0x6034, 0xa005,
+       0x0040, 0x4c0e, 0x8001, 0x6036, 0x00c0, 0x4c0e, 0x6010, 0xa005,
+       0x0040, 0x4c0e, 0x017e, 0x1078, 0x21d2, 0x017f, 0xace0, 0x0010,
+       0x0070, 0x4c14, 0x0078, 0x4bfe, 0x8109, 0x0040, 0x4c1b, 0x20a9,
+       0x0100, 0x0078, 0x4bfe, 0x1078, 0x4c28, 0x1078, 0x4c4d, 0x2009,
+       0x5151, 0x2104, 0x2009, 0x0102, 0x200a, 0x2091, 0x8001, 0x007c,
+       0x7834, 0x8001, 0x7836, 0x00c0, 0x4c4c, 0x7838, 0x7836, 0x2091,
+       0x8000, 0x7844, 0xa005, 0x00c0, 0x4c37, 0x2001, 0x0101, 0x8001,
+       0x7846, 0xa080, 0x7400, 0x2040, 0x2004, 0xa065, 0x0040, 0x4c4c,
+       0x6024, 0xa005, 0x0040, 0x4c48, 0x8001, 0x6026, 0x0040, 0x4c7c,
+       0x6000, 0x2c40, 0x0078, 0x4c3d, 0x007c, 0x7828, 0x8001, 0x782a,
+       0x00c0, 0x4c7b, 0x782c, 0x782a, 0x7830, 0xa005, 0x00c0, 0x4c5a,
+       0x2001, 0x0200, 0x8001, 0x7832, 0x8003, 0x8003, 0x8003, 0x8003,
+       0xa090, 0x5400, 0xa298, 0x0002, 0x2304, 0xa084, 0x0008, 0x0040,
+       0x4c7b, 0xa290, 0x0009, 0x2204, 0xa005, 0x0040, 0x4c73, 0x8001,
+       0x2012, 0x00c0, 0x4c7b, 0x2304, 0xa084, 0xfff7, 0xa085, 0x0080,
+       0x201a, 0x1078, 0x21d2, 0x007c, 0x2069, 0x5140, 0x6800, 0xa005,
+       0x0040, 0x4c86, 0x6848, 0xac06, 0x0040, 0x4cc3, 0x601b, 0x0006,
+       0x60b4, 0xa084, 0x3f00, 0x601e, 0x6020, 0xa084, 0x00ff, 0xa085,
+       0x0060, 0x6022, 0x6000, 0x2042, 0x6714, 0x6f82, 0x1078, 0x1973,
+       0x6818, 0xa005, 0x0040, 0x4c9e, 0x8001, 0x681a, 0x6808, 0xa084,
+       0xffef, 0x680a, 0x6810, 0x8001, 0x00d0, 0x4ca8, 0x1078, 0x23eb,
+       0x6812, 0x602f, 0x0000, 0x6033, 0x0000, 0x2c68, 0x1078, 0x1c70,
+       0x2069, 0x5140, 0x7944, 0xa184, 0x0100, 0x2001, 0x0006, 0x686e,
+       0x00c0, 0x4cbe, 0x6986, 0x2001, 0x0004, 0x686e, 0x1078, 0x21cd,
+       0x2091, 0x8001, 0x007c, 0x2069, 0x0100, 0x2009, 0x5140, 0x2104,
+       0xa084, 0x0007, 0x0040, 0x4d1f, 0xa086, 0x0007, 0x00c0, 0x4cd9,
+       0x0d7e, 0x2009, 0x5152, 0x216c, 0x1078, 0x3a4e, 0x0d7f, 0x0078,
+       0x4d1f, 0x2009, 0x5152, 0x2164, 0x1078, 0x2396, 0x601b, 0x0006,
+       0x6858, 0xa084, 0x3f00, 0x601e, 0x6020, 0xa084, 0x00ff, 0xa085,
+       0x0048, 0x6022, 0x602f, 0x0000, 0x6033, 0x0000, 0x6830, 0xa084,
+       0x0040, 0x0040, 0x4d13, 0x684b, 0x0004, 0x20a9, 0x0014, 0x6848,
+       0xa084, 0x0004, 0x0040, 0x4d00, 0x0070, 0x4d00, 0x0078, 0x4cf7,
+       0x684b, 0x0009, 0x20a9, 0x0014, 0x6848, 0xa084, 0x0001, 0x0040,
+       0x4d0d, 0x0070, 0x4d0d, 0x0078, 0x4d04, 0x20a9, 0x00fa, 0x0070,
+       0x4d13, 0x0078, 0x4d0f, 0x6808, 0xa084, 0xfffd, 0x680a, 0x681b,
+       0x0048, 0x2009, 0x515b, 0x200b, 0x0007, 0x784c, 0x784a, 0x2091,
+       0x8001, 0x007c, 0x2079, 0x5100, 0x1078, 0x4d4d, 0x1078, 0x4d31,
+       0x1078, 0x4d3f, 0x7833, 0x0000, 0x7847, 0x0000, 0x784b, 0x0000,
+       0x007c, 0x2019, 0x0003, 0x2011, 0x5146, 0x2204, 0xa086, 0x003c,
+       0x0040, 0x4d3c, 0x2019, 0x0002, 0x7b2a, 0x7b2e, 0x007c, 0x2019,
+       0x0039, 0x2011, 0x5146, 0x2204, 0xa086, 0x003c, 0x0040, 0x4d4a,
+       0x2019, 0x0027, 0x7b36, 0x7b3a, 0x007c, 0x2019, 0x3971, 0x2011,
+       0x5146, 0x2204, 0xa086, 0x003c, 0x0040, 0x4d58, 0x2019, 0x2626,
+       0x7b22, 0x7b26, 0x783f, 0x0000, 0x7843, 0x000a, 0x007c, 0x0020,
+       0x002b, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+       0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+       0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+       0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+       0x0020, 0x0000, 0x0014, 0x0014, 0x9849, 0x0014, 0x0014, 0x0014,
+       0x0014, 0x0014, 0x0014, 0x0014, 0x0080, 0x000f, 0x0000, 0x0201,
+       0x0604, 0x0c08, 0x2120, 0x4022, 0xf880, 0x0018, 0x300b, 0xa201,
+       0x0014, 0xa200, 0x0014, 0xa200, 0x0214, 0x0000, 0x006c, 0x0002,
+       0x0014, 0x98d0, 0x009e, 0x0096, 0xa202, 0x8838, 0x3806, 0x8839,
+       0x20c3, 0x0864, 0x9884, 0x28c1, 0x9cb1, 0xa203, 0x300c, 0x2846,
+       0x8161, 0x846a, 0x8300, 0x1856, 0x883a, 0x9865, 0x28f2, 0x9c90,
+       0x9858, 0x300c, 0x28e1, 0x9c90, 0x2802, 0xa206, 0x64c3, 0x282d,
+       0xa207, 0x64a0, 0x67a0, 0x6fc0, 0x1814, 0x883b, 0x7824, 0x68c1,
+       0x7864, 0x883e, 0x9878, 0x8576, 0x8677, 0x206b, 0x28c1, 0x9cb1,
+       0x2044, 0x2103, 0x20a2, 0x2081, 0x9865, 0xa209, 0x2901, 0x988c,
+       0x0014, 0xa205, 0xa300, 0x1872, 0x879a, 0x883c, 0x1fe2, 0xc601,
+       0xa20a, 0x856e, 0x0704, 0x9c90, 0x0014, 0xa204, 0xa300, 0x3009,
+       0x19e2, 0xf868, 0x8176, 0x86eb, 0x85eb, 0x872e, 0x87a9, 0x883f,
+       0x08e6, 0x9890, 0xf881, 0x988b, 0xc801, 0x0014, 0xf8c1, 0x0016,
+       0x85b2, 0x80f0, 0x9532, 0xfb02, 0x1de2, 0x0014, 0x8532, 0xf241,
+       0x0014, 0x1de2, 0x84a8, 0xd7a0, 0x1fe6, 0x0014, 0xa208, 0x6043,
+       0x8008, 0x1dc1, 0x0016, 0x8300, 0x8160, 0x842a, 0xf041, 0x3008,
+       0x84a8, 0x11d6, 0x7042, 0x20dd, 0x0011, 0x20d5, 0x8822, 0x0016,
+       0x8000, 0x2847, 0x1011, 0x98c3, 0x8000, 0xa000, 0x2802, 0x1011,
+       0x98c9, 0x9865, 0x283e, 0x1011, 0x98cd, 0xa20b, 0x0017, 0x300c,
+       0xa300, 0x1de2, 0xdb81, 0x0014, 0x0210, 0x98da, 0x0014, 0x26e0,
+       0x873a, 0xfb02, 0x19f2, 0x1fe2, 0x0014, 0xa20d, 0x3806, 0x0210,
+       0x9cb6, 0x0704, 0x0000, 0x006c, 0x0002, 0x984f, 0x0014, 0x009e,
+       0x00a5, 0x0017, 0x60ff, 0x300c, 0x8720, 0xa211, 0x9cd5, 0x8772,
+       0x8837, 0x2101, 0x987a, 0x10d2, 0x78e2, 0x9cd8, 0x9859, 0xd984,
+       0xf0e2, 0xf0a1, 0x98d2, 0x0014, 0x8831, 0xd166, 0x8830, 0x800f,
+       0x9401, 0xb520, 0xc802, 0x8820, 0x987a, 0x2301, 0x987a, 0x10d2,
+       0x78e4, 0x9cd8, 0x8821, 0x8820, 0x9859, 0xf123, 0xf142, 0xf101,
+       0x98cb, 0x10d2, 0x70f6, 0x8832, 0x8203, 0x870c, 0xd99e, 0x6001,
+       0x0014, 0x6845, 0x0214, 0xa21b, 0x9cd5, 0x2001, 0x98ca, 0x8201,
+       0x1852, 0xd184, 0xd163, 0x8834, 0x8001, 0x988d, 0x3027, 0x84a8,
+       0x1a56, 0x8833, 0x0014, 0xa218, 0x6981, 0x9cc1, 0x692a, 0x6902,
+       0x1834, 0x989d, 0x1a14, 0x8010, 0x8592, 0x8026, 0x84b9, 0x7021,
+       0x0014, 0xa300, 0x69e1, 0x9caa, 0x694c, 0xa213, 0x9cba, 0x1462,
+       0xa213, 0x8000, 0x16e1, 0x98b4, 0x8023, 0x16e1, 0x8001, 0x10f1,
+       0x0016, 0x6968, 0xa214, 0x9cba, 0x8004, 0x16e1, 0x0101, 0x300a,
+       0x8827, 0x0014, 0x9cba, 0x0014, 0x61c2, 0x8002, 0x14e1, 0x0016,
+       0xa217, 0x9cc1, 0x0014, 0xa300, 0x8181, 0x842a, 0x84a8, 0x1ce6,
+       0x882c, 0x0016, 0xa212, 0x9cd5, 0x10d2, 0x70e4, 0x0004, 0x8007,
+       0x9424, 0xcc1a, 0x9cd8, 0x98ca, 0x8827, 0x300a, 0x0013, 0x8000,
+       0x84a4, 0x0016, 0x11c2, 0x211e, 0x870e, 0xa21d, 0x0014, 0x878e,
+       0x0016, 0xa21c, 0x1035, 0x9891, 0xa210, 0xa000, 0x8010, 0x8592,
+       0x853b, 0xd044, 0x8022, 0x3807, 0x84bb, 0x98ef, 0x8021, 0x3807,
+       0x84b9, 0x300c, 0x817e, 0x872b, 0x8772, 0x9891, 0x0000, 0x0020,
+       0x002b, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+       0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+       0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+       0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000, 0x0020, 0x0000,
+       0x0020, 0x0000, 0x0014, 0x0014, 0x9849, 0x0014, 0x0014, 0x98e5,
+       0x98d0, 0x0014, 0x0014, 0x0014, 0x0080, 0x013f, 0x0000, 0x0201,
+       0x0604, 0x0c08, 0x2120, 0x4022, 0xf880, 0x0018, 0x300b, 0xa201,
+       0x0014, 0xa200, 0x0014, 0xa200, 0x0214, 0xa202, 0x8838, 0x3806,
+       0x8839, 0x20c3, 0x0864, 0xa82e, 0x28c1, 0x9cb1, 0xa203, 0x300c,
+       0x2846, 0x8161, 0x846a, 0x8300, 0x1856, 0x883a, 0xa804, 0x28f2,
+       0x9c90, 0xa8f4, 0x300c, 0x28e1, 0x9c90, 0x2802, 0xa206, 0x64c3,
+       0x282d, 0xa207, 0x64a0, 0x67a0, 0x6fc0, 0x1814, 0x883b, 0x7824,
+       0x68c1, 0x7864, 0x883e, 0xa802, 0x8576, 0x8677, 0x206b, 0x28c1,
+       0x9cb1, 0x2044, 0x2103, 0x20a2, 0x2081, 0xa8e5, 0xa209, 0x2901,
+       0xa809, 0x0014, 0xa205, 0xa300, 0x1872, 0x879a, 0x883c, 0x1fe2,
+       0xc601, 0xa20a, 0x856e, 0x0704, 0x9c90, 0x0014, 0xa204, 0xa300,
+       0x3009, 0x19e2, 0xf868, 0x8176, 0x86eb, 0x85eb, 0x872e, 0x87a9,
+       0x883f, 0x08e6, 0xa8f3, 0xf881, 0xa8ec, 0xc801, 0x0014, 0xf8c1,
+       0x0016, 0x85b2, 0x80f0, 0x9532, 0xfb02, 0x1de2, 0x0014, 0x8532,
+       0xf241, 0x0014, 0x1de2, 0x84a8, 0xd7a0, 0x1fe6, 0x0014, 0xa208,
+       0x6043, 0x8008, 0x1dc1, 0x0016, 0x8300, 0x8160, 0x842a, 0xf041,
+       0x3008, 0x84a8, 0x11d6, 0x7042, 0x20dd, 0x0011, 0x20d5, 0x8822,
+       0x0016, 0x8000, 0x2847, 0x1011, 0xa8fc, 0x8000, 0xa000, 0x2802,
+       0x1011, 0xa8fd, 0xa898, 0x283e, 0x1011, 0xa8fd, 0xa20b, 0x0017,
+       0x300c, 0xa300, 0x1de2, 0xdb81, 0x0014, 0x0210, 0xa801, 0x0014,
+       0x26e0, 0x873a, 0xfb02, 0x19f2, 0x1fe2, 0x0014, 0xa20d, 0x3806,
+       0x0210, 0x9cb6, 0x0704, 0x0017, 0x60ff, 0x300c, 0x8720, 0xa211,
+       0x9d6b, 0x8772, 0x8837, 0x2101, 0xa821, 0x10d2, 0x78e2, 0x9d6e,
+       0xa8fc, 0xd984, 0xf0e2, 0xf0a1, 0xa871, 0x0014, 0x8831, 0xd166,
+       0x8830, 0x800f, 0x9401, 0xb520, 0xc802, 0x8820, 0xa80f, 0x2301,
+       0xa80d, 0x10d2, 0x78e4, 0x9d6e, 0x8821, 0x8820, 0xa8e6, 0xf123,
+       0xf142, 0xf101, 0xa854, 0x10d2, 0x70f6, 0x8832, 0x8203, 0x870c,
+       0xd99e, 0x6001, 0x0014, 0x6845, 0x0214, 0xa21b, 0x9d6b, 0x2001,
+       0xa845, 0x8201, 0x1852, 0xd184, 0xd163, 0x8834, 0x8001, 0xa801,
+       0x3027, 0x84a8, 0x1a56, 0x8833, 0x0014, 0xa218, 0x6981, 0x9d57,
+       0x692a, 0x6902, 0x1834, 0xa805, 0x1a14, 0x8010, 0x8592, 0x8026,
+       0x84b9, 0x7021, 0x0014, 0xa300, 0x69e1, 0x9d40, 0x694c, 0xa213,
+       0x9d50, 0x1462, 0xa213, 0x8000, 0x16e1, 0xa80a, 0x8023, 0x16e1,
+       0x8001, 0x10f1, 0x0016, 0x6968, 0xa214, 0x9d50, 0x8004, 0x16e1,
+       0x0101, 0x300a, 0x8827, 0x0014, 0x9d50, 0x0014, 0x61c2, 0x8002,
+       0x14e1, 0x0016, 0xa217, 0x9d57, 0x0014, 0xa300, 0x8181, 0x842a,
+       0x84a8, 0x1ce6, 0x882c, 0x0016, 0xa212, 0x9d6b, 0x10d2, 0x70e4,
+       0x0004, 0x8007, 0x9424, 0xcc1a, 0x9d6e, 0xa8f8, 0x8827, 0x300a,
+       0x0013, 0x8000, 0x84a4, 0x0016, 0x11c2, 0x211e, 0x870e, 0xa21d,
+       0x0014, 0x878e, 0x0016, 0xa21c, 0x1035, 0xa8af, 0xa210, 0x3807,
+       0x300c, 0x817e, 0x872b, 0x8772, 0xa8a8, 0x0000, 0xdf21
+};
+static unsigned short   risc_code_length01 = 0x4057;
+
diff --git a/drivers/scsi/sata_qstor.c b/drivers/scsi/sata_qstor.c
new file mode 100644 (file)
index 0000000..0b6531d
--- /dev/null
@@ -0,0 +1,700 @@
+/*
+ *  sata_qstor.c - Pacific Digital Corporation QStor SATA
+ *
+ *  Maintained by:  Mark Lord <mlord@pobox.com>
+ *
+ *  Copyright 2005 Pacific Digital Corporation.
+ *  (OSL/GPL code release authorized by Jalil Fadavi).
+ *
+ *  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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <asm/io.h>
+#include <linux/libata.h>
+
+#define DRV_NAME       "sata_qstor"
+#define DRV_VERSION    "0.03"
+
+enum {
+       QS_PORTS                = 4,
+       QS_MAX_PRD              = LIBATA_MAX_PRD,
+       QS_CPB_ORDER            = 6,
+       QS_CPB_BYTES            = (1 << QS_CPB_ORDER),
+       QS_PRD_BYTES            = QS_MAX_PRD * 16,
+       QS_PKT_BYTES            = QS_CPB_BYTES + QS_PRD_BYTES,
+
+       QS_DMA_BOUNDARY         = ~0UL,
+
+       /* global register offsets */
+       QS_HCF_CNFG3            = 0x0003, /* host configuration offset */
+       QS_HID_HPHY             = 0x0004, /* host physical interface info */
+       QS_HCT_CTRL             = 0x00e4, /* global interrupt mask offset */
+       QS_HST_SFF              = 0x0100, /* host status fifo offset */
+       QS_HVS_SERD3            = 0x0393, /* PHY enable offset */
+
+       /* global control bits */
+       QS_HPHY_64BIT           = (1 << 1), /* 64-bit bus detected */
+       QS_CNFG3_GSRST          = 0x01,     /* global chip reset */
+       QS_SERD3_PHY_ENA        = 0xf0,     /* PHY detection ENAble*/
+
+       /* per-channel register offsets */
+       QS_CCF_CPBA             = 0x0710, /* chan CPB base address */
+       QS_CCF_CSEP             = 0x0718, /* chan CPB separation factor */
+       QS_CFC_HUFT             = 0x0800, /* host upstream fifo threshold */
+       QS_CFC_HDFT             = 0x0804, /* host downstream fifo threshold */
+       QS_CFC_DUFT             = 0x0808, /* dev upstream fifo threshold */
+       QS_CFC_DDFT             = 0x080c, /* dev downstream fifo threshold */
+       QS_CCT_CTR0             = 0x0900, /* chan control-0 offset */
+       QS_CCT_CTR1             = 0x0901, /* chan control-1 offset */
+       QS_CCT_CFF              = 0x0a00, /* chan command fifo offset */
+
+       /* channel control bits */
+       QS_CTR0_REG             = (1 << 1),   /* register mode (vs. pkt mode) */
+       QS_CTR0_CLER            = (1 << 2),   /* clear channel errors */
+       QS_CTR1_RDEV            = (1 << 1),   /* sata phy/comms reset */
+       QS_CTR1_RCHN            = (1 << 4),   /* reset channel logic */
+       QS_CCF_RUN_PKT          = 0x107,      /* RUN a new dma PKT */
+
+       /* pkt sub-field headers */
+       QS_HCB_HDR              = 0x01,   /* Host Control Block header */
+       QS_DCB_HDR              = 0x02,   /* Device Control Block header */
+
+       /* pkt HCB flag bits */
+       QS_HF_DIRO              = (1 << 0),   /* data DIRection Out */
+       QS_HF_DAT               = (1 << 3),   /* DATa pkt */
+       QS_HF_IEN               = (1 << 4),   /* Interrupt ENable */
+       QS_HF_VLD               = (1 << 5),   /* VaLiD pkt */
+
+       /* pkt DCB flag bits */
+       QS_DF_PORD              = (1 << 2),   /* Pio OR Dma */
+       QS_DF_ELBA              = (1 << 3),   /* Extended LBA (lba48) */
+
+       /* PCI device IDs */
+       board_2068_idx          = 0,    /* QStor 4-port SATA/RAID */
+};
+
+typedef enum { qs_state_idle, qs_state_pkt, qs_state_mmio } qs_state_t;
+
+struct qs_port_priv {
+       u8                      *pkt;
+       dma_addr_t              pkt_dma;
+       qs_state_t              state;
+};
+
+static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+static int qs_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static irqreturn_t qs_intr (int irq, void *dev_instance, struct pt_regs *regs);
+static int qs_port_start(struct ata_port *ap);
+static void qs_host_stop(struct ata_host_set *host_set);
+static void qs_port_stop(struct ata_port *ap);
+static void qs_phy_reset(struct ata_port *ap);
+static void qs_qc_prep(struct ata_queued_cmd *qc);
+static int qs_qc_issue(struct ata_queued_cmd *qc);
+static int qs_check_atapi_dma(struct ata_queued_cmd *qc);
+static void qs_bmdma_stop(struct ata_port *ap);
+static u8 qs_bmdma_status(struct ata_port *ap);
+static void qs_irq_clear(struct ata_port *ap);
+
+static Scsi_Host_Template qs_ata_sht = {
+       .module                 = THIS_MODULE,
+       .name                   = DRV_NAME,
+       .ioctl                  = ata_scsi_ioctl,
+       .queuecommand           = ata_scsi_queuecmd,
+       .eh_strategy_handler    = ata_scsi_error,
+       .can_queue              = ATA_DEF_QUEUE,
+       .this_id                = ATA_SHT_THIS_ID,
+       .sg_tablesize           = QS_MAX_PRD,
+       .max_sectors            = ATA_MAX_SECTORS,
+       .cmd_per_lun            = ATA_SHT_CMD_PER_LUN,
+       .emulated               = ATA_SHT_EMULATED,
+       //FIXME .use_clustering         = ATA_SHT_USE_CLUSTERING,
+       .use_clustering         = ENABLE_CLUSTERING,
+       .proc_name              = DRV_NAME,
+       .dma_boundary           = QS_DMA_BOUNDARY,
+       .slave_configure        = ata_scsi_slave_config,
+       .bios_param             = ata_std_bios_param,
+};
+
+static struct ata_port_operations qs_ata_ops = {
+       .port_disable           = ata_port_disable,
+       .tf_load                = ata_tf_load,
+       .tf_read                = ata_tf_read,
+       .check_status           = ata_check_status,
+       .check_atapi_dma        = qs_check_atapi_dma,
+       .exec_command           = ata_exec_command,
+       .dev_select             = ata_std_dev_select,
+       .phy_reset              = qs_phy_reset,
+       .qc_prep                = qs_qc_prep,
+       .qc_issue               = qs_qc_issue,
+       .eng_timeout            = ata_eng_timeout,
+       .irq_handler            = qs_intr,
+       .irq_clear              = qs_irq_clear,
+       .scr_read               = qs_scr_read,
+       .scr_write              = qs_scr_write,
+       .port_start             = qs_port_start,
+       .port_stop              = qs_port_stop,
+       .host_stop              = qs_host_stop,
+       .bmdma_stop             = qs_bmdma_stop,
+       .bmdma_status           = qs_bmdma_status,
+};
+
+static struct ata_port_info qs_port_info[] = {
+       /* board_2068_idx */
+       {
+               .sht            = &qs_ata_sht,
+               .host_flags     = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+                                 ATA_FLAG_SATA_RESET |
+                                 //FIXME ATA_FLAG_SRST |
+                                 ATA_FLAG_MMIO,
+               .pio_mask       = 0x10, /* pio4 */
+               .udma_mask      = 0x7f, /* udma0-6 */
+               .port_ops       = &qs_ata_ops,
+       },
+};
+
+static struct pci_device_id qs_ata_pci_tbl[] = {
+       { PCI_VENDOR_ID_PDC, 0x2068, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_2068_idx },
+
+       { }     /* terminate list */
+};
+
+static struct pci_driver qs_ata_pci_driver = {
+       .name                   = DRV_NAME,
+       .id_table               = qs_ata_pci_tbl,
+       .probe                  = qs_ata_init_one,
+       .remove                 = ata_pci_remove_one,
+};
+
+static int qs_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+       return 1;       /* ATAPI DMA not supported */
+}
+
+static void qs_bmdma_stop(struct ata_port *ap)
+{
+       /* nothing */
+}
+
+static u8 qs_bmdma_status(struct ata_port *ap)
+{
+       return 0;
+}
+
+static void qs_irq_clear(struct ata_port *ap)
+{
+       /* nothing */
+}
+
+static void qs_enter_reg_mode(struct ata_port *ap)
+{
+       u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000);
+
+       writeb(QS_CTR0_REG, chan + QS_CCT_CTR0);
+       readb(chan + QS_CCT_CTR0);        /* flush */
+}
+
+static void qs_phy_reset(struct ata_port *ap)
+{
+       u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000);
+       struct qs_port_priv *pp = ap->private_data;
+
+       pp->state = qs_state_idle;
+       writeb(QS_CTR1_RCHN, chan + QS_CCT_CTR1);
+       qs_enter_reg_mode(ap);
+       sata_phy_reset(ap);
+}
+
+static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+       if (sc_reg > SCR_CONTROL)
+               return ~0U;
+       return readl((void __iomem *)(ap->ioaddr.scr_addr + (sc_reg * 8)));
+}
+
+static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
+{
+       if (sc_reg > SCR_CONTROL)
+               return;
+       writel(val, (void __iomem *)(ap->ioaddr.scr_addr + (sc_reg * 8)));
+}
+
+static void qs_fill_sg(struct ata_queued_cmd *qc)
+{
+       struct scatterlist *sg = qc->sg;
+       struct ata_port *ap = qc->ap;
+       struct qs_port_priv *pp = ap->private_data;
+       unsigned int nelem;
+       u8 *prd = pp->pkt + QS_CPB_BYTES;
+
+       assert(sg != NULL);
+       assert(qc->n_elem > 0);
+
+       for (nelem = 0; nelem < qc->n_elem; nelem++,sg++) {
+               u64 addr;
+               u32 len;
+
+               addr = sg_dma_address(sg);
+               *(u64 *)prd = cpu_to_le64(addr);
+               prd += sizeof(u64);
+
+               len = sg_dma_len(sg);
+               *(u32 *)prd = cpu_to_le32(len);
+               prd += sizeof(u64);
+
+               VPRINTK("PRD[%u] = (0x%llX, 0x%X)\n", nelem,
+                                       (unsigned long long)addr, len);
+       }
+}
+
+static void qs_qc_prep(struct ata_queued_cmd *qc)
+{
+       struct qs_port_priv *pp = qc->ap->private_data;
+       u8 dflags = QS_DF_PORD, *buf = pp->pkt;
+       u8 hflags = QS_HF_DAT | QS_HF_IEN | QS_HF_VLD;
+       u64 addr;
+
+       VPRINTK("ENTER\n");
+
+       qs_enter_reg_mode(qc->ap);
+       if (qc->tf.protocol != ATA_PROT_DMA) {
+               ata_qc_prep(qc);
+               return;
+       }
+
+       qs_fill_sg(qc);
+
+       if ((qc->tf.flags & ATA_TFLAG_WRITE))
+               hflags |= QS_HF_DIRO;
+       if ((qc->tf.flags & ATA_TFLAG_LBA48))
+               dflags |= QS_DF_ELBA;
+
+       /* host control block (HCB) */
+       buf[ 0] = QS_HCB_HDR;
+       buf[ 1] = hflags;
+       *(u32 *)(&buf[ 4]) = cpu_to_le32(qc->nsect * ATA_SECT_SIZE);
+       *(u32 *)(&buf[ 8]) = cpu_to_le32(qc->n_elem);
+       addr = ((u64)pp->pkt_dma) + QS_CPB_BYTES;
+       *(u64 *)(&buf[16]) = cpu_to_le64(addr);
+
+       /* device control block (DCB) */
+       buf[24] = QS_DCB_HDR;
+       buf[28] = dflags;
+
+       /* frame information structure (FIS) */
+       ata_tf_to_fis(&qc->tf, &buf[32], 0);
+}
+
+static inline void qs_packet_start(struct ata_queued_cmd *qc)
+{
+       struct ata_port *ap = qc->ap;
+       u8 __iomem *chan = ap->host_set->mmio_base + (ap->port_no * 0x4000);
+
+       VPRINTK("ENTER, ap %p\n", ap);
+
+       writeb(QS_CTR0_CLER, chan + QS_CCT_CTR0);
+       wmb();                             /* flush PRDs and pkt to memory */
+       writel(QS_CCF_RUN_PKT, chan + QS_CCT_CFF);
+       readl(chan + QS_CCT_CFF);          /* flush */
+}
+
+static int qs_qc_issue(struct ata_queued_cmd *qc)
+{
+       struct qs_port_priv *pp = qc->ap->private_data;
+
+       switch (qc->tf.protocol) {
+       case ATA_PROT_DMA:
+
+               pp->state = qs_state_pkt;
+               qs_packet_start(qc);
+               return 0;
+
+       case ATA_PROT_ATAPI_DMA:
+               BUG();
+               break;
+
+       default:
+               break;
+       }
+
+       pp->state = qs_state_mmio;
+       return ata_qc_issue_prot(qc);
+}
+
+static inline unsigned int qs_intr_pkt(struct ata_host_set *host_set)
+{
+       unsigned int handled = 0;
+       u8 sFFE;
+       u8 __iomem *mmio_base = host_set->mmio_base;
+
+       do {
+               u32 sff0 = readl(mmio_base + QS_HST_SFF);
+               u32 sff1 = readl(mmio_base + QS_HST_SFF + 4);
+               u8 sEVLD = (sff1 >> 30) & 0x01; /* valid flag */
+               sFFE  = sff1 >> 31;             /* empty flag */
+
+               if (sEVLD) {
+                       u8 sDST = sff0 >> 16;   /* dev status */
+                       u8 sHST = sff1 & 0x3f;  /* host status */
+                       unsigned int port_no = (sff1 >> 8) & 0x03;
+                       struct ata_port *ap = host_set->ports[port_no];
+
+                       DPRINTK("SFF=%08x%08x: sCHAN=%u sHST=%d sDST=%02x\n",
+                                       sff1, sff0, port_no, sHST, sDST);
+                       handled = 1;
+                       if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+                               struct ata_queued_cmd *qc;
+                               struct qs_port_priv *pp = ap->private_data;
+                               if (!pp || pp->state != qs_state_pkt)
+                                       continue;
+                               qc = ata_qc_from_tag(ap, ap->active_tag);
+                               if (qc && (!(qc->tf.ctl & ATA_NIEN))) {
+                                       switch (sHST) {
+                                       case 0: /* sucessful CPB */
+                                       case 3: /* device error */
+                                               pp->state = qs_state_idle;
+                                               qs_enter_reg_mode(qc->ap);
+                                               ata_qc_complete(qc, sDST);
+                                               break;
+                                       default:
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       } while (!sFFE);
+       return handled;
+}
+
+static inline unsigned int qs_intr_mmio(struct ata_host_set *host_set)
+{
+       unsigned int handled = 0, port_no;
+
+       for (port_no = 0; port_no < host_set->n_ports; ++port_no) {
+               struct ata_port *ap;
+               ap = host_set->ports[port_no];
+               if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) {
+                       struct ata_queued_cmd *qc;
+                       struct qs_port_priv *pp = ap->private_data;
+                       if (!pp || pp->state != qs_state_mmio)
+                               continue;
+                       qc = ata_qc_from_tag(ap, ap->active_tag);
+                       if (qc && (!(qc->tf.ctl & ATA_NIEN))) {
+
+                               /* check main status, clearing INTRQ */
+                               u8 status = ata_chk_status(ap);
+                               if ((status & ATA_BUSY))
+                                       continue;
+                               DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n",
+                                       ap->id, qc->tf.protocol, status);
+               
+                               /* complete taskfile transaction */
+                               pp->state = qs_state_idle;
+                               ata_qc_complete(qc, status);
+                               handled = 1;
+                       }
+               }
+       }
+       return handled;
+}
+
+static irqreturn_t qs_intr(int irq, void *dev_instance, struct pt_regs *regs)
+{
+       struct ata_host_set *host_set = dev_instance;
+       unsigned int handled = 0;
+
+       VPRINTK("ENTER\n");
+
+       spin_lock(&host_set->lock);
+       handled  = qs_intr_pkt(host_set) | qs_intr_mmio(host_set);
+       spin_unlock(&host_set->lock);
+
+       VPRINTK("EXIT\n");
+
+       return IRQ_RETVAL(handled);
+}
+
+static void qs_ata_setup_port(struct ata_ioports *port, unsigned long base)
+{
+       port->cmd_addr          =
+       port->data_addr         = base + 0x400;
+       port->error_addr        =
+       port->feature_addr      = base + 0x408; /* hob_feature = 0x409 */
+       port->nsect_addr        = base + 0x410; /* hob_nsect   = 0x411 */
+       port->lbal_addr         = base + 0x418; /* hob_lbal    = 0x419 */
+       port->lbam_addr         = base + 0x420; /* hob_lbam    = 0x421 */
+       port->lbah_addr         = base + 0x428; /* hob_lbah    = 0x429 */
+       port->device_addr       = base + 0x430;
+       port->status_addr       =
+       port->command_addr      = base + 0x438;
+       port->altstatus_addr    =
+       port->ctl_addr          = base + 0x440;
+       port->scr_addr          = base + 0xc00;
+}
+
+static int qs_port_start(struct ata_port *ap)
+{
+       struct device *dev = ap->host_set->dev;
+       struct qs_port_priv *pp;
+       void __iomem *mmio_base = ap->host_set->mmio_base;
+       void __iomem *chan = mmio_base + (ap->port_no * 0x4000);
+       u64 addr;
+       int rc;
+
+       rc = ata_port_start(ap);
+       if (rc)
+               return rc;
+       qs_enter_reg_mode(ap);
+       pp = kcalloc(1, sizeof(*pp), GFP_KERNEL);
+       if (!pp) {
+               rc = -ENOMEM;
+               goto err_out;
+       }
+       pp->pkt = dma_alloc_coherent(dev, QS_PKT_BYTES, &pp->pkt_dma,
+                                                               GFP_KERNEL);
+       if (!pp->pkt) {
+               rc = -ENOMEM;
+               goto err_out_kfree;
+       }
+       memset(pp->pkt, 0, QS_PKT_BYTES);
+       ap->private_data = pp;
+
+       addr = (u64)pp->pkt_dma;
+       writel((u32) addr,        chan + QS_CCF_CPBA);
+       writel((u32)(addr >> 32), chan + QS_CCF_CPBA + 4);
+       return 0;
+
+err_out_kfree:
+       kfree(pp);
+err_out:
+       ata_port_stop(ap);
+       return rc;
+}
+
+static void qs_port_stop(struct ata_port *ap)
+{
+       struct device *dev = ap->host_set->dev;
+       struct qs_port_priv *pp = ap->private_data;
+
+       if (pp != NULL) {
+               ap->private_data = NULL;
+               if (pp->pkt != NULL)
+                       dma_free_coherent(dev, QS_PKT_BYTES, pp->pkt,
+                                                               pp->pkt_dma);
+               kfree(pp);
+       }
+       ata_port_stop(ap);
+}
+
+static void qs_host_stop(struct ata_host_set *host_set)
+{
+       void __iomem *mmio_base = host_set->mmio_base;
+
+       writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */
+       writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */
+}
+
+static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe)
+{
+       void __iomem *mmio_base = pe->mmio_base;
+       unsigned int port_no;
+
+       writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */
+       writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */
+
+       /* reset each channel in turn */
+       for (port_no = 0; port_no < pe->n_ports; ++port_no) {
+               u8 __iomem *chan = mmio_base + (port_no * 0x4000);
+               writeb(QS_CTR1_RDEV|QS_CTR1_RCHN, chan + QS_CCT_CTR1);
+               writeb(QS_CTR0_REG, chan + QS_CCT_CTR0);
+               readb(chan + QS_CCT_CTR0);        /* flush */
+       }
+       writeb(QS_SERD3_PHY_ENA, mmio_base + QS_HVS_SERD3); /* enable phy */
+
+       for (port_no = 0; port_no < pe->n_ports; ++port_no) {
+               u8 __iomem *chan = mmio_base + (port_no * 0x4000);
+               /* set FIFO depths to same settings as Windows driver */
+               writew(32, chan + QS_CFC_HUFT);
+               writew(32, chan + QS_CFC_HDFT);
+               writew(10, chan + QS_CFC_DUFT);
+               writew( 8, chan + QS_CFC_DDFT);
+               /* set CPB size in bytes, as a power of two */
+               writeb(QS_CPB_ORDER,    chan + QS_CCF_CSEP);
+       }
+       writeb(1, mmio_base + QS_HCT_CTRL); /* enable host interrupts */
+}
+
+/*
+ * The QStor understands 64-bit buses, and uses 64-bit fields
+ * for DMA pointers regardless of bus width.  We just have to
+ * make sure our DMA masks are set appropriately for whatever
+ * bridge lies between us and the QStor, and then the DMA mapping
+ * code will ensure we only ever "see" appropriate buffer addresses.
+ * If we're 32-bit limited somewhere, then our 64-bit fields will
+ * just end up with zeros in the upper 32-bits, without any special
+ * logic required outside of this routine (below).
+ */
+static int qs_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base)
+{
+       u32 bus_info = readl(mmio_base + QS_HID_HPHY);
+       int rc, have_64bit_bus = (bus_info & QS_HPHY_64BIT);
+
+       if (have_64bit_bus &&
+           !pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) {
+               rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
+               if (rc) {
+                       rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL);
+                       if (rc) {
+                               printk(KERN_ERR DRV_NAME
+                                       "(%s): 64-bit DMA enable failed\n",
+                                       pci_name(pdev));
+                               return rc;
+                       }
+               }
+       } else {
+               rc = pci_set_dma_mask(pdev, 0xffffffffULL);
+               if (rc) {
+                       printk(KERN_ERR DRV_NAME
+                               "(%s): 32-bit DMA enable failed\n",
+                               pci_name(pdev));
+                       return rc;
+               }
+               rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL);
+               if (rc) {
+                       printk(KERN_ERR DRV_NAME
+                               "(%s): 32-bit consistent DMA enable failed\n",
+                               pci_name(pdev));
+                       return rc;
+               }
+       }
+       return 0;
+}
+
+static int qs_ata_init_one(struct pci_dev *pdev,
+                               const struct pci_device_id *ent)
+{
+       static int printed_version;
+       struct ata_probe_ent *probe_ent = NULL;
+       void __iomem *mmio_base;
+       unsigned int board_idx = (unsigned int) ent->driver_data;
+       int rc, port_no;
+
+       if (!printed_version++)
+               printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+       rc = pci_enable_device(pdev);
+       if (rc)
+               return rc;
+
+       rc = pci_request_regions(pdev, DRV_NAME);
+       if (rc)
+               goto err_out;
+
+       if ((pci_resource_flags(pdev, 4) & IORESOURCE_MEM) == 0) {
+               rc = -ENODEV;
+               goto err_out_regions;
+       }
+
+       mmio_base = ioremap(pci_resource_start(pdev, 4),
+                           pci_resource_len(pdev, 4));
+       if (mmio_base == NULL) {
+               rc = -ENOMEM;
+               goto err_out_regions;
+       }
+
+       rc = qs_set_dma_masks(pdev, mmio_base);
+       if (rc)
+               goto err_out_iounmap;
+
+       probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+       if (probe_ent == NULL) {
+               rc = -ENOMEM;
+               goto err_out_iounmap;
+       }
+
+       memset(probe_ent, 0, sizeof(*probe_ent));
+       probe_ent->dev = pci_dev_to_dev(pdev);
+       INIT_LIST_HEAD(&probe_ent->node);
+
+       probe_ent->sht          = qs_port_info[board_idx].sht;
+       probe_ent->host_flags   = qs_port_info[board_idx].host_flags;
+       probe_ent->pio_mask     = qs_port_info[board_idx].pio_mask;
+       probe_ent->mwdma_mask   = qs_port_info[board_idx].mwdma_mask;
+       probe_ent->udma_mask    = qs_port_info[board_idx].udma_mask;
+       probe_ent->port_ops     = qs_port_info[board_idx].port_ops;
+
+       probe_ent->irq          = pdev->irq;
+       probe_ent->irq_flags    = SA_SHIRQ;
+       probe_ent->mmio_base    = mmio_base;
+       probe_ent->n_ports      = QS_PORTS;
+
+       for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) {
+               unsigned long chan = (unsigned long)mmio_base +
+                                                       (port_no * 0x4000);
+               qs_ata_setup_port(&probe_ent->port[port_no], chan);
+       }
+
+       pci_set_master(pdev);
+
+       /* initialize adapter */
+       qs_host_init(board_idx, probe_ent);
+
+       rc = ata_device_add(probe_ent);
+       kfree(probe_ent);
+       if (rc != QS_PORTS)
+               goto err_out_iounmap;
+       return 0;
+
+err_out_iounmap:
+       iounmap(mmio_base);
+err_out_regions:
+       pci_release_regions(pdev);
+err_out:
+       pci_disable_device(pdev);
+       return rc;
+}
+
+static int __init qs_ata_init(void)
+{
+       return pci_module_init(&qs_ata_pci_driver);
+}
+
+static void __exit qs_ata_exit(void)
+{
+       pci_unregister_driver(&qs_ata_pci_driver);
+}
+
+MODULE_AUTHOR("Mark Lord");
+MODULE_DESCRIPTION("Pacific Digital Corporation QStor SATA low-level driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, qs_ata_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(qs_ata_init);
+module_exit(qs_ata_exit);
diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c
new file mode 100644 (file)
index 0000000..1becb36
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ *  sata_uli.c - ULi Electronics SATA
+ *
+ *  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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME       "sata_uli"
+#define DRV_VERSION    "0.11"
+
+enum {
+       uli_5289                = 0,
+       uli_5287                = 1,
+
+       /* PCI configuration registers */
+       ULI_SCR_BASE            = 0x90, /* sata0 phy SCR registers */
+       ULI_SATA1_OFS           = 0x10, /* offset from sata0->sata1 phy regs */
+
+};
+
+static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+
+static struct pci_device_id uli_pci_tbl[] = {
+       { PCI_VENDOR_ID_AL, 0x5289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5289 },
+       { PCI_VENDOR_ID_AL, 0x5287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5287 },
+       { }     /* terminate list */
+};
+
+
+static struct pci_driver uli_pci_driver = {
+       .name                   = DRV_NAME,
+       .id_table               = uli_pci_tbl,
+       .probe                  = uli_init_one,
+       .remove                 = ata_pci_remove_one,
+};
+
+static Scsi_Host_Template uli_sht = {
+       .module                 = THIS_MODULE,
+       .name                   = DRV_NAME,
+       .ioctl                  = ata_scsi_ioctl,
+       .queuecommand           = ata_scsi_queuecmd,
+       .eh_strategy_handler    = ata_scsi_error,
+       .can_queue              = ATA_DEF_QUEUE,
+       .this_id                = ATA_SHT_THIS_ID,
+       .sg_tablesize           = LIBATA_MAX_PRD,
+       .max_sectors            = ATA_MAX_SECTORS,
+       .cmd_per_lun            = ATA_SHT_CMD_PER_LUN,
+       .emulated               = ATA_SHT_EMULATED,
+       .use_clustering         = ATA_SHT_USE_CLUSTERING,
+       .proc_name              = DRV_NAME,
+       .dma_boundary           = ATA_DMA_BOUNDARY,
+       .slave_configure        = ata_scsi_slave_config,
+       .bios_param             = ata_std_bios_param,
+};
+
+static struct ata_port_operations uli_ops = {
+       .port_disable           = ata_port_disable,
+
+       .tf_load                = ata_tf_load,
+       .tf_read                = ata_tf_read,
+       .check_status           = ata_check_status,
+       .exec_command           = ata_exec_command,
+       .dev_select             = ata_std_dev_select,
+
+       .phy_reset              = sata_phy_reset,
+
+       .bmdma_setup            = ata_bmdma_setup,
+       .bmdma_start            = ata_bmdma_start,
+       .qc_prep                = ata_qc_prep,
+       .qc_issue               = ata_qc_issue_prot,
+
+       .eng_timeout            = ata_eng_timeout,
+
+       .irq_handler            = ata_interrupt,
+       .irq_clear              = ata_bmdma_irq_clear,
+
+       .scr_read               = uli_scr_read,
+       .scr_write              = uli_scr_write,
+
+       .port_start             = ata_port_start,
+       .port_stop              = ata_port_stop,
+};
+
+static struct ata_port_info uli_port_info = {
+       .sht            = &uli_sht,
+       .host_flags     = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET |
+                         ATA_FLAG_NO_LEGACY,
+       .pio_mask       = 0x03,         //support pio mode 4 (FIXME)
+       .udma_mask      = 0x7f,         //support udma mode 6
+       .port_ops       = &uli_ops,
+};
+
+
+MODULE_AUTHOR("Peer Chen");
+MODULE_DESCRIPTION("low-level driver for ULi Electronics SATA controller");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, uli_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+static unsigned int get_scr_cfg_addr(unsigned int port_no, unsigned int sc_reg)
+{
+       unsigned int addr = ULI_SCR_BASE + (4 * sc_reg);
+
+       switch (port_no) {
+       case 0:
+               break;
+       case 1:
+               addr += ULI_SATA1_OFS;
+               break;
+       case 2:
+               addr += ULI_SATA1_OFS*4;
+               break;
+       case 3:
+               addr += ULI_SATA1_OFS*5;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return addr;
+}
+
+static u32 uli_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg)
+{
+       unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, sc_reg);
+       u32 val;
+
+       pci_read_config_dword(ap->host_set->pdev, cfg_addr, &val);
+       return val;
+}
+
+static void uli_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val)
+{
+       unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, scr);
+
+       pci_write_config_dword(ap->host_set->pdev, cfg_addr, val);
+}
+
+static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+       if (sc_reg > SCR_CONTROL)
+               return 0xffffffffU;
+
+       return uli_scr_cfg_read(ap, sc_reg);
+}
+
+static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
+{
+       if (sc_reg > SCR_CONTROL)       //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0
+               return;
+
+       uli_scr_cfg_write(ap, sc_reg, val);
+}
+
+/* move to PCI layer, integrate w/ MSI stuff */
+static void pci_enable_intx(struct pci_dev *pdev)
+{
+       u16 pci_command;
+
+       pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+       if (pci_command & PCI_COMMAND_INTX_DISABLE) {
+               pci_command &= ~PCI_COMMAND_INTX_DISABLE;
+               pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+       }
+}
+
+static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct ata_probe_ent *probe_ent;
+       struct ata_port_info *ppi;
+       int rc;
+       unsigned int board_idx = (unsigned int) ent->driver_data;
+
+       rc = pci_enable_device(pdev);
+       if (rc)
+               return rc;
+
+       rc = pci_request_regions(pdev, DRV_NAME);
+       if (rc)
+               goto err_out;
+
+       rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+       if (rc)
+               goto err_out_regions;
+       rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+       if (rc)
+               goto err_out_regions;
+
+       ppi = &uli_port_info;
+       probe_ent = ata_pci_init_native_mode(pdev, &ppi);
+       if (!probe_ent) {
+               rc = -ENOMEM;
+               goto err_out_regions;
+       }
+
+       switch (board_idx) {
+       case uli_5287:
+                       probe_ent->n_ports = 4;
+
+                       probe_ent->port[2].cmd_addr = pci_resource_start(pdev, 0) + 8;
+               probe_ent->port[2].altstatus_addr =
+               probe_ent->port[2].ctl_addr =
+                       (pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS) + 4;
+               probe_ent->port[2].bmdma_addr = pci_resource_start(pdev, 4) + 16;
+
+               probe_ent->port[3].cmd_addr = pci_resource_start(pdev, 2) + 8;
+               probe_ent->port[3].altstatus_addr =
+               probe_ent->port[3].ctl_addr =
+                       (pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS) + 4;
+               probe_ent->port[3].bmdma_addr = pci_resource_start(pdev, 4) + 24;
+
+               ata_std_ports(&probe_ent->port[2]);
+               ata_std_ports(&probe_ent->port[3]);
+               break;
+
+       case uli_5289:
+               /* do nothing; ata_pci_init_native_mode did it all */
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+
+       pci_set_master(pdev);
+       pci_enable_intx(pdev);
+
+       /* FIXME: check ata_device_add return value */
+       ata_device_add(probe_ent);
+       kfree(probe_ent);
+
+       return 0;
+
+err_out_regions:
+       pci_release_regions(pdev);
+
+err_out:
+       pci_disable_device(pdev);
+       return rc;
+
+}
+
+static int __init uli_init(void)
+{
+       return pci_module_init(&uli_pci_driver);
+}
+
+static void __exit uli_exit(void)
+{
+       pci_unregister_driver(&uli_pci_driver);
+}
+
+
+module_init(uli_init);
+module_exit(uli_exit);
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
new file mode 100644 (file)
index 0000000..b0aff94
--- /dev/null
@@ -0,0 +1,388 @@
+/* 
+ * iSCSI transport class definitions
+ *
+ * Copyright (C) IBM Corporation, 2004
+ * Copyright (C) Mike Christie, 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+#define ISCSI_SESSION_ATTRS 20
+#define ISCSI_HOST_ATTRS 2
+
+struct iscsi_internal {
+       struct scsi_transport_template t;
+       struct iscsi_function_template *fnt;
+       /*
+        * We do not have any private or other attrs.
+        */
+       struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
+       struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
+};
+
+#define to_iscsi_internal(tmpl) container_of(tmpl, struct iscsi_internal, t)
+
+static DECLARE_TRANSPORT_CLASS(iscsi_transport_class,
+                              "iscsi_transport",
+                              NULL,
+                              NULL,
+                              NULL);
+
+static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
+                              "iscsi_host",
+                              NULL,
+                              NULL,
+                              NULL);
+/*
+ * iSCSI target and session attrs
+ */
+#define iscsi_session_show_fn(field, format)                           \
+                                                                       \
+static ssize_t                                                         \
+show_session_##field(struct class_device *cdev, char *buf)             \
+{                                                                      \
+       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
+       struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
+                                                                       \
+       if (i->fnt->get_##field)                                        \
+               i->fnt->get_##field(starget);                           \
+       return snprintf(buf, 20, format"\n", iscsi_##field(starget));   \
+}
+
+#define iscsi_session_rd_attr(field, format)                           \
+       iscsi_session_show_fn(field, format)                            \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_##field, NULL);
+
+iscsi_session_rd_attr(tpgt, "%hu");
+iscsi_session_rd_attr(tsih, "%2x");
+iscsi_session_rd_attr(max_recv_data_segment_len, "%u");
+iscsi_session_rd_attr(max_burst_len, "%u");
+iscsi_session_rd_attr(first_burst_len, "%u");
+iscsi_session_rd_attr(def_time2wait, "%hu");
+iscsi_session_rd_attr(def_time2retain, "%hu");
+iscsi_session_rd_attr(max_outstanding_r2t, "%hu");
+iscsi_session_rd_attr(erl, "%d");
+
+
+#define iscsi_session_show_bool_fn(field)                              \
+                                                                       \
+static ssize_t                                                         \
+show_session_bool_##field(struct class_device *cdev, char *buf)                \
+{                                                                      \
+       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
+       struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
+                                                                       \
+       if (i->fnt->get_##field)                                        \
+               i->fnt->get_##field(starget);                           \
+                                                                       \
+       if (iscsi_##field(starget))                                     \
+               return sprintf(buf, "Yes\n");                           \
+       return sprintf(buf, "No\n");                                    \
+}
+
+#define iscsi_session_rd_bool_attr(field)                              \
+       iscsi_session_show_bool_fn(field)                               \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_bool_##field, NULL);
+
+iscsi_session_rd_bool_attr(initial_r2t);
+iscsi_session_rd_bool_attr(immediate_data);
+iscsi_session_rd_bool_attr(data_pdu_in_order);
+iscsi_session_rd_bool_attr(data_sequence_in_order);
+
+#define iscsi_session_show_digest_fn(field)                            \
+                                                                       \
+static ssize_t                                                         \
+show_##field(struct class_device *cdev, char *buf)                     \
+{                                                                      \
+       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
+       struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
+                                                                       \
+       if (i->fnt->get_##field)                                        \
+               i->fnt->get_##field(starget);                           \
+                                                                       \
+       if (iscsi_##field(starget))                                     \
+               return sprintf(buf, "CRC32C\n");                        \
+       return sprintf(buf, "None\n");                                  \
+}
+
+#define iscsi_session_rd_digest_attr(field)                            \
+       iscsi_session_show_digest_fn(field)                             \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+
+iscsi_session_rd_digest_attr(header_digest);
+iscsi_session_rd_digest_attr(data_digest);
+
+static ssize_t
+show_port(struct class_device *cdev, char *buf)
+{
+       struct scsi_target *starget = transport_class_to_starget(cdev);
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
+
+       if (i->fnt->get_port)
+               i->fnt->get_port(starget);
+
+       return snprintf(buf, 20, "%hu\n", ntohs(iscsi_port(starget)));
+}
+static CLASS_DEVICE_ATTR(port, S_IRUGO, show_port, NULL);
+
+static ssize_t
+show_ip_address(struct class_device *cdev, char *buf)
+{
+       struct scsi_target *starget = transport_class_to_starget(cdev);
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
+
+       if (i->fnt->get_ip_address)
+               i->fnt->get_ip_address(starget);
+
+       if (iscsi_addr_type(starget) == AF_INET)
+               return sprintf(buf, "%u.%u.%u.%u\n",
+                              NIPQUAD(iscsi_sin_addr(starget)));
+       else if(iscsi_addr_type(starget) == AF_INET6)
+               return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+                              NIP6(iscsi_sin6_addr(starget)));
+       return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(ip_address, S_IRUGO, show_ip_address, NULL);
+
+static ssize_t
+show_isid(struct class_device *cdev, char *buf)
+{
+       struct scsi_target *starget = transport_class_to_starget(cdev);
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
+
+       if (i->fnt->get_isid)
+               i->fnt->get_isid(starget);
+
+       return sprintf(buf, "%02x%02x%02x%02x%02x%02x\n",
+                      iscsi_isid(starget)[0], iscsi_isid(starget)[1],
+                      iscsi_isid(starget)[2], iscsi_isid(starget)[3],
+                      iscsi_isid(starget)[4], iscsi_isid(starget)[5]);
+}
+static CLASS_DEVICE_ATTR(isid, S_IRUGO, show_isid, NULL);
+
+/*
+ * This is used for iSCSI names. Normally, we follow
+ * the transport class convention of having the lld
+ * set the field, but in these cases the value is
+ * too large.
+ */
+#define iscsi_session_show_str_fn(field)                               \
+                                                                       \
+static ssize_t                                                         \
+show_session_str_##field(struct class_device *cdev, char *buf)         \
+{                                                                      \
+       ssize_t ret = 0;                                                \
+       struct scsi_target *starget = transport_class_to_starget(cdev); \
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
+       struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
+                                                                       \
+       if (i->fnt->get_##field)                                        \
+               ret = i->fnt->get_##field(starget, buf, PAGE_SIZE);     \
+       return ret;                                                     \
+}
+
+#define iscsi_session_rd_str_attr(field)                               \
+       iscsi_session_show_str_fn(field)                                \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, show_session_str_##field, NULL);
+
+iscsi_session_rd_str_attr(target_name);
+iscsi_session_rd_str_attr(target_alias);
+
+/*
+ * iSCSI host attrs
+ */
+
+/*
+ * Again, this is used for iSCSI names. Normally, we follow
+ * the transport class convention of having the lld set
+ * the field, but in these cases the value is too large.
+ */
+#define iscsi_host_show_str_fn(field)                                  \
+                                                                       \
+static ssize_t                                                         \
+show_host_str_##field(struct class_device *cdev, char *buf)            \
+{                                                                      \
+       int ret = 0;                                                    \
+       struct Scsi_Host *shost = transport_class_to_shost(cdev);       \
+       struct iscsi_internal *i = to_iscsi_internal(shost->transportt); \
+                                                                       \
+       if (i->fnt->get_##field)                                        \
+               ret = i->fnt->get_##field(shost, buf, PAGE_SIZE);       \
+       return ret;                                                     \
+}
+
+#define iscsi_host_rd_str_attr(field)                                  \
+       iscsi_host_show_str_fn(field)                                   \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, show_host_str_##field, NULL);
+
+iscsi_host_rd_str_attr(initiator_name);
+iscsi_host_rd_str_attr(initiator_alias);
+
+#define SETUP_SESSION_RD_ATTR(field)                                   \
+       if (i->fnt->show_##field) {                                     \
+               i->session_attrs[count] = &class_device_attr_##field;   \
+               count++;                                                \
+       }
+
+#define SETUP_HOST_RD_ATTR(field)                                      \
+       if (i->fnt->show_##field) {                                     \
+               i->host_attrs[count] = &class_device_attr_##field;      \
+               count++;                                                \
+       }
+
+static int iscsi_host_match(struct attribute_container *cont,
+                         struct device *dev)
+{
+       struct Scsi_Host *shost;
+       struct iscsi_internal *i;
+
+       if (!scsi_is_host_device(dev))
+               return 0;
+
+       shost = dev_to_shost(dev);
+       if (!shost->transportt  || shost->transportt->host_attrs.class
+           != &iscsi_host_class.class)
+               return 0;
+
+       i = to_iscsi_internal(shost->transportt);
+       
+       return &i->t.host_attrs == cont;
+}
+
+static int iscsi_target_match(struct attribute_container *cont,
+                           struct device *dev)
+{
+       struct Scsi_Host *shost;
+       struct iscsi_internal *i;
+
+       if (!scsi_is_target_device(dev))
+               return 0;
+
+       shost = dev_to_shost(dev->parent);
+       if (!shost->transportt  || shost->transportt->host_attrs.class
+           != &iscsi_host_class.class)
+               return 0;
+
+       i = to_iscsi_internal(shost->transportt);
+       
+       return &i->t.target_attrs == cont;
+}
+
+struct scsi_transport_template *
+iscsi_attach_transport(struct iscsi_function_template *fnt)
+{
+       struct iscsi_internal *i = kmalloc(sizeof(struct iscsi_internal),
+                                          GFP_KERNEL);
+       int count = 0;
+
+       if (unlikely(!i))
+               return NULL;
+
+       memset(i, 0, sizeof(struct iscsi_internal));
+       i->fnt = fnt;
+
+       i->t.target_attrs.attrs = &i->session_attrs[0];
+       i->t.target_attrs.class = &iscsi_transport_class.class;
+       i->t.target_attrs.match = iscsi_target_match;
+       attribute_container_register(&i->t.target_attrs);
+       i->t.target_size = sizeof(struct iscsi_class_session);
+
+       SETUP_SESSION_RD_ATTR(tsih);
+       SETUP_SESSION_RD_ATTR(isid);
+       SETUP_SESSION_RD_ATTR(header_digest);
+       SETUP_SESSION_RD_ATTR(data_digest);
+       SETUP_SESSION_RD_ATTR(target_name);
+       SETUP_SESSION_RD_ATTR(target_alias);
+       SETUP_SESSION_RD_ATTR(port);
+       SETUP_SESSION_RD_ATTR(tpgt);
+       SETUP_SESSION_RD_ATTR(ip_address);
+       SETUP_SESSION_RD_ATTR(initial_r2t);
+       SETUP_SESSION_RD_ATTR(immediate_data);
+       SETUP_SESSION_RD_ATTR(max_recv_data_segment_len);
+       SETUP_SESSION_RD_ATTR(max_burst_len);
+       SETUP_SESSION_RD_ATTR(first_burst_len);
+       SETUP_SESSION_RD_ATTR(def_time2wait);
+       SETUP_SESSION_RD_ATTR(def_time2retain);
+       SETUP_SESSION_RD_ATTR(max_outstanding_r2t);
+       SETUP_SESSION_RD_ATTR(data_pdu_in_order);
+       SETUP_SESSION_RD_ATTR(data_sequence_in_order);
+       SETUP_SESSION_RD_ATTR(erl);
+
+       BUG_ON(count > ISCSI_SESSION_ATTRS);
+       i->session_attrs[count] = NULL;
+
+       i->t.host_attrs.attrs = &i->host_attrs[0];
+       i->t.host_attrs.class = &iscsi_host_class.class;
+       i->t.host_attrs.match = iscsi_host_match;
+       attribute_container_register(&i->t.host_attrs);
+       i->t.host_size = 0;
+
+       count = 0;
+       SETUP_HOST_RD_ATTR(initiator_name);
+       SETUP_HOST_RD_ATTR(initiator_alias);
+
+       BUG_ON(count > ISCSI_HOST_ATTRS);
+       i->host_attrs[count] = NULL;
+
+       return &i->t;
+}
+
+EXPORT_SYMBOL(iscsi_attach_transport);
+
+void iscsi_release_transport(struct scsi_transport_template *t)
+{
+       struct iscsi_internal *i = to_iscsi_internal(t);
+
+       attribute_container_unregister(&i->t.target_attrs);
+       attribute_container_unregister(&i->t.host_attrs);
+
+       kfree(i);
+}
+
+EXPORT_SYMBOL(iscsi_release_transport);
+
+static __init int iscsi_transport_init(void)
+{
+       int err = transport_class_register(&iscsi_transport_class);
+
+       if (err)
+               return err;
+       return transport_class_register(&iscsi_host_class);
+}
+
+static void __exit iscsi_transport_exit(void)
+{
+       transport_class_unregister(&iscsi_host_class);
+       transport_class_unregister(&iscsi_transport_class);
+}
+
+module_init(iscsi_transport_init);
+module_exit(iscsi_transport_exit);
+
+MODULE_AUTHOR("Mike Christie");
+MODULE_DESCRIPTION("iSCSI Transport Attributes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/8250_early.c b/drivers/serial/8250_early.c
new file mode 100644 (file)
index 0000000..b7a5dd7
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Early serial console for 8250/16550 devices
+ *
+ * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
+ *     Bjorn Helgaas <bjorn.helgaas@hp.com>
+ *
+ * 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.
+ *
+ * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King,
+ * and on early_printk.c by Andi Kleen.
+ *
+ * This is for use before the serial driver has initialized, in
+ * particular, before the UARTs have been discovered and named.
+ * Instead of specifying the console device as, e.g., "ttyS0",
+ * we locate the device directly by its MMIO or I/O port address.
+ *
+ * The user can specify the device directly, e.g.,
+ *     console=uart,io,0x3f8,9600n8
+ *     console=uart,mmio,0xff5e0000,115200n8
+ * or platform code can call early_uart_console_init() to set
+ * the early UART device.
+ *
+ * After the normal serial driver starts, we try to locate the
+ * matching ttyS device and start a console there.
+ */
+
+#include <linux/tty.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/serial.h>
+#include <asm/io.h>
+#include <asm/serial.h>
+
+struct early_uart_device {
+       struct uart_port port;
+       char options[16];               /* e.g., 115200n8 */
+       unsigned int baud;
+};
+
+static struct early_uart_device early_device __initdata;
+static int early_uart_registered __initdata;
+
+static unsigned int __init serial_in(struct uart_port *port, int offset)
+{
+       if (port->iotype == UPIO_MEM)
+               return readb(port->membase + offset);
+       else
+               return inb(port->iobase + offset);
+}
+
+static void __init serial_out(struct uart_port *port, int offset, int value)
+{
+       if (port->iotype == UPIO_MEM)
+               writeb(value, port->membase + offset);
+       else
+               outb(value, port->iobase + offset);
+}
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+static void __init wait_for_xmitr(struct uart_port *port)
+{
+       unsigned int status;
+
+       for (;;) {
+               status = serial_in(port, UART_LSR);
+               if ((status & BOTH_EMPTY) == BOTH_EMPTY)
+                       return;
+               cpu_relax();
+       }
+}
+
+static void __init putc(struct uart_port *port, unsigned char c)
+{
+       wait_for_xmitr(port);
+       serial_out(port, UART_TX, c);
+}
+
+static void __init early_uart_write(struct console *console, const char *s, unsigned int count)
+{
+       struct uart_port *port = &early_device.port;
+       unsigned int ier;
+
+       /* Save the IER and disable interrupts */
+       ier = serial_in(port, UART_IER);
+       serial_out(port, UART_IER, 0);
+
+       while (*s && count-- > 0) {
+               putc(port, *s);
+               if (*s == '\n')
+                       putc(port, '\r');
+               s++;
+       }
+
+       /* Wait for transmitter to become empty and restore the IER */
+       wait_for_xmitr(port);
+       serial_out(port, UART_IER, ier);
+}
+
+static unsigned int __init probe_baud(struct uart_port *port)
+{
+       unsigned char lcr, dll, dlm;
+       unsigned int quot;
+
+       lcr = serial_in(port, UART_LCR);
+       serial_out(port, UART_LCR, lcr | UART_LCR_DLAB);
+       dll = serial_in(port, UART_DLL);
+       dlm = serial_in(port, UART_DLM);
+       serial_out(port, UART_LCR, lcr);
+
+       quot = (dlm << 8) | dll;
+       return (port->uartclk / 16) / quot;
+}
+
+static void __init init_port(struct early_uart_device *device)
+{
+       struct uart_port *port = &device->port;
+       unsigned int divisor;
+       unsigned char c;
+
+       serial_out(port, UART_LCR, 0x3);        /* 8n1 */
+       serial_out(port, UART_IER, 0);          /* no interrupt */
+       serial_out(port, UART_FCR, 0);          /* no fifo */
+       serial_out(port, UART_MCR, 0x3);        /* DTR + RTS */
+
+       divisor = port->uartclk / (16 * device->baud);
+       c = serial_in(port, UART_LCR);
+       serial_out(port, UART_LCR, c | UART_LCR_DLAB);
+       serial_out(port, UART_DLL, divisor & 0xff);
+       serial_out(port, UART_DLM, (divisor >> 8) & 0xff);
+       serial_out(port, UART_LCR, c & ~UART_LCR_DLAB);
+}
+
+static int __init parse_options(struct early_uart_device *device, char *options)
+{
+       struct uart_port *port = &device->port;
+       int mapsize = 64;
+       int mmio, length;
+
+       if (!options)
+               return -ENODEV;
+
+       port->uartclk = BASE_BAUD * 16;
+       if (!strncmp(options, "mmio,", 5)) {
+               port->iotype = UPIO_MEM;
+               port->mapbase = simple_strtoul(options + 5, &options, 0);
+               port->membase = ioremap(port->mapbase, mapsize);
+               if (!port->membase) {
+                       printk(KERN_ERR "%s: Couldn't ioremap 0x%lx\n",
+                               __FUNCTION__, port->mapbase);
+                       return -ENOMEM;
+               }
+               mmio = 1;
+       } else if (!strncmp(options, "io,", 3)) {
+               port->iotype = UPIO_PORT;
+               port->iobase = simple_strtoul(options + 3, &options, 0);
+               mmio = 0;
+       } else
+               return -EINVAL;
+
+       if ((options = strchr(options, ','))) {
+               options++;
+               device->baud = simple_strtoul(options, 0, 0);
+               length = min(strcspn(options, " "), sizeof(device->options));
+               strncpy(device->options, options, length);
+       } else {
+               device->baud = probe_baud(port);
+               snprintf(device->options, sizeof(device->options), "%u",
+                       device->baud);
+       }
+
+       printk(KERN_INFO "Early serial console at %s 0x%lx (options '%s')\n",
+               mmio ? "MMIO" : "I/O port",
+               mmio ? port->mapbase : (unsigned long) port->iobase,
+               device->options);
+       return 0;
+}
+
+static int __init early_uart_setup(struct console *console, char *options)
+{
+       struct early_uart_device *device = &early_device;
+       int err;
+
+       if (device->port.membase || device->port.iobase)
+               return 0;
+
+       if ((err = parse_options(device, options)) < 0)
+               return err;
+
+       init_port(device);
+       return 0;
+}
+
+static struct console early_uart_console __initdata = {
+       .name   = "uart",
+       .write  = early_uart_write,
+       .setup  = early_uart_setup,
+       .flags  = CON_PRINTBUFFER,
+       .index  = -1,
+};
+
+static int __init early_uart_console_init(void)
+{
+       if (!early_uart_registered) {
+               register_console(&early_uart_console);
+               early_uart_registered = 1;
+       }
+       return 0;
+}
+console_initcall(early_uart_console_init);
+
+int __init early_serial_console_init(char *cmdline)
+{
+       char *options;
+       int err;
+
+       options = strstr(cmdline, "console=uart,");
+       if (!options)
+               return -ENODEV;
+
+       options = strchr(cmdline, ',') + 1;
+       if ((err = early_uart_setup(NULL, options)) < 0)
+               return err;
+       return early_uart_console_init();
+}
+
+static int __init early_uart_console_switch(void)
+{
+       struct early_uart_device *device = &early_device;
+       struct uart_port *port = &device->port;
+       int mmio, line;
+
+       if (!(early_uart_console.flags & CON_ENABLED))
+               return 0;
+
+       /* Try to start the normal driver on a matching line.  */
+       mmio = (port->iotype == UPIO_MEM);
+       line = serial8250_start_console(port, device->options);
+       if (line < 0)
+               printk("No ttyS device at %s 0x%lx for console\n",
+                       mmio ? "MMIO" : "I/O port",
+                       mmio ? port->mapbase :
+                           (unsigned long) port->iobase);
+
+       unregister_console(&early_uart_console);
+       if (mmio)
+               iounmap(port->membase);
+
+       return 0;
+}
+late_initcall(early_uart_console_switch);
diff --git a/drivers/serial/8250_hp300.c b/drivers/serial/8250_hp300.c
new file mode 100644 (file)
index 0000000..b8d51eb
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Driver for the 98626/98644/internal serial interface on hp300/hp400
+ * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs)
+ *
+ * Ported from 2.2 and modified to use the normal 8250 driver
+ * by Kars de Jong <jongk@linux-m68k.org>, May 2004.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <linux/serial_core.h>
+#include <linux/delay.h>
+#include <linux/dio.h>
+#include <linux/console.h>
+#include <asm/io.h>
+
+#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI)
+#warning CONFIG_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure?
+#endif
+
+#ifdef CONFIG_HPAPCI
+struct hp300_port
+{
+       struct hp300_port *next;        /* next port */
+       int line;                       /* line (tty) number */
+};
+
+static struct hp300_port *hp300_ports;
+#endif
+
+#ifdef CONFIG_HPDCA
+
+static int __devinit hpdca_init_one(struct dio_dev *d,
+                                const struct dio_device_id *ent);
+static void __devexit hpdca_remove_one(struct dio_dev *d);
+
+static struct dio_device_id hpdca_dio_tbl[] = {
+       { DIO_ID_DCA0 },
+       { DIO_ID_DCA0REM },
+       { DIO_ID_DCA1 },
+       { DIO_ID_DCA1REM },
+       { 0 }
+};
+
+static struct dio_driver hpdca_driver = {
+       .name      = "hpdca",
+       .id_table  = hpdca_dio_tbl,
+       .probe     = hpdca_init_one,
+       .remove    = __devexit_p(hpdca_remove_one),
+};
+
+#endif
+
+extern int hp300_uart_scode;
+
+/* Offset to UART registers from base of DCA */
+#define UART_OFFSET    17
+
+#define DCA_ID         0x01    /* ID (read), reset (write) */
+#define DCA_IC         0x03    /* Interrupt control        */
+
+/* Interrupt control */
+#define DCA_IC_IE      0x80    /* Master interrupt enable  */
+
+#define HPDCA_BAUD_BASE 153600
+
+/* Base address of the Frodo part */
+#define FRODO_BASE     (0x41c000)
+
+/*
+ * Where we find the 8250-like APCI ports, and how far apart they are.
+ */
+#define FRODO_APCIBASE         0x0
+#define FRODO_APCISPACE                0x20
+#define FRODO_APCI_OFFSET(x)   (FRODO_APCIBASE + ((x) * FRODO_APCISPACE))
+
+#define HPAPCI_BAUD_BASE 500400
+
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+/*
+ * Parse the bootinfo to find descriptions for headless console and 
+ * debug serial ports and register them with the 8250 driver.
+ * This function should be called before serial_console_init() is called
+ * to make sure the serial console will be available for use. IA-64 kernel
+ * calls this function from setup_arch() after the EFI and ACPI tables have
+ * been parsed.
+ */
+int __init hp300_setup_serial_console(void)
+{
+       int scode;
+       struct uart_port port;
+
+       memset(&port, 0, sizeof(port));
+
+       if (hp300_uart_scode < 0 || hp300_uart_scode > DIO_SCMAX)
+               return 0;
+
+       if (DIO_SCINHOLE(hp300_uart_scode))
+               return 0;
+
+       scode = hp300_uart_scode;
+
+       /* Memory mapped I/O */
+       port.iotype = UPIO_MEM;
+       port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
+       port.type = PORT_UNKNOWN;
+
+       /* Check for APCI console */
+       if (scode == 256) {
+#ifdef CONFIG_HPAPCI
+               printk(KERN_INFO "Serial console is HP APCI 1\n");
+
+               port.uartclk = HPAPCI_BAUD_BASE * 16;
+               port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1));
+               port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
+               port.regshift = 2;
+               add_preferred_console("ttyS", port.line, "9600n8");
+#else
+               printk(KERN_WARNING "Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n");
+               return 0;
+#endif
+       }
+       else {
+#ifdef CONFIG_HPDCA
+               unsigned long pa = dio_scodetophysaddr(scode);
+               if (!pa) {
+                       return 0;
+               }
+
+               printk(KERN_INFO "Serial console is HP DCA at select code %d\n", scode);
+
+               port.uartclk = HPDCA_BAUD_BASE * 16;
+               port.mapbase = (pa + UART_OFFSET);
+               port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
+               port.regshift = 1;
+               port.irq = DIO_IPL(pa + DIO_VIRADDRBASE);
+
+               /* Enable board-interrupts */
+               out_8(pa + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE);
+
+               if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80) {
+                       add_preferred_console("ttyS", port.line, "9600n8");
+               }
+#else
+               printk(KERN_WARNING "Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n");
+               return 0;
+#endif
+       }
+
+       if (early_serial_setup(&port) < 0) {
+               printk(KERN_WARNING "hp300_setup_serial_console(): early_serial_setup() failed.\n");
+       }
+
+       return 0;
+}
+#endif /* CONFIG_SERIAL_8250_CONSOLE */
+
+#ifdef CONFIG_HPDCA
+static int __devinit hpdca_init_one(struct dio_dev *d,
+                                const struct dio_device_id *ent)
+{
+       struct serial_struct serial_req;
+       int line;
+
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+       if (hp300_uart_scode == d->scode) {
+               /* Already got it. */
+               return 0;
+       }
+#endif
+       memset(&serial_req, 0, sizeof(struct serial_struct));
+
+       /* Memory mapped I/O */
+       serial_req.io_type = SERIAL_IO_MEM;
+       serial_req.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
+       serial_req.irq = d->ipl;
+       serial_req.baud_base = HPDCA_BAUD_BASE;
+       serial_req.iomap_base = (d->resource.start + UART_OFFSET);
+       serial_req.iomem_base = (char *)(serial_req.iomap_base + DIO_VIRADDRBASE);
+       serial_req.iomem_reg_shift = 1;
+       line = register_serial(&serial_req);
+
+       if (line < 0) {
+               printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d"
+                      " irq %d failed\n", d->scode, serial_req.irq);
+               return -ENOMEM;
+       }
+
+       /* Enable board-interrupts */
+       out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE);
+       dio_set_drvdata(d, (void *)line);
+
+       /* Reset the DCA */
+       out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff);
+       udelay(100);
+
+       return 0;
+}
+#endif
+
+static int __init hp300_8250_init(void)
+{
+       static int called = 0;
+       int num_ports;
+#ifdef CONFIG_HPAPCI
+       int line;
+       unsigned long base;
+       struct serial_struct serial_req;
+       struct hp300_port *port;
+       int i;
+#endif
+       if (called)
+               return -ENODEV;
+       called = 1;
+
+       if (!MACH_IS_HP300)
+               return -ENODEV;
+
+       num_ports = 0;
+
+#ifdef CONFIG_HPDCA
+       if (dio_module_init(&hpdca_driver) == 0)
+               num_ports++;
+#endif
+#ifdef CONFIG_HPAPCI
+       if (hp300_model < HP_400) {
+               if (!num_ports)
+                       return -ENODEV;
+               return 0;
+       }
+       /* These models have the Frodo chip.
+        * Port 0 is reserved for the Apollo Domain keyboard.
+        * Port 1 is either the console or the DCA.
+        */
+       for (i = 1; i < 4; i++) {
+               /* Port 1 is the console on a 425e, on other machines it's mapped to
+                * DCA.
+                */
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+               if (i == 1) {
+                       continue;
+               }
+#endif
+
+               /* Create new serial device */
+               port = kmalloc(sizeof(struct hp300_port), GFP_KERNEL);
+               if (!port)
+                       return -ENOMEM;
+
+               memset(&serial_req, 0, sizeof(struct serial_struct));
+
+               base = (FRODO_BASE + FRODO_APCI_OFFSET(i));
+
+               /* Memory mapped I/O */
+               serial_req.io_type = SERIAL_IO_MEM;
+               serial_req.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
+               /* XXX - no interrupt support yet */
+               serial_req.irq = 0;
+               serial_req.baud_base = HPAPCI_BAUD_BASE;
+               serial_req.iomap_base = base;
+               serial_req.iomem_base = (char *)(serial_req.iomap_base + DIO_VIRADDRBASE);
+               serial_req.iomem_reg_shift = 2;
+
+               line = register_serial(&serial_req);
+
+               if (line < 0) {
+                       printk(KERN_NOTICE "8250_hp300: register_serial() APCI %d"
+                              " irq %d failed\n", i, serial_req.irq);
+                       kfree(port);
+                       continue;
+               }
+
+               port->line = line;
+               port->next = hp300_ports;
+               hp300_ports = port;
+
+               num_ports++;
+       }
+#endif
+
+       /* Any boards found? */
+       if (!num_ports)
+               return -ENODEV;
+
+       return 0;
+}
+
+#ifdef CONFIG_HPDCA
+static void __devexit hpdca_remove_one(struct dio_dev *d)
+{
+       int line;
+
+       line = (int) dio_get_drvdata(d);
+       if (d->resource.start) {
+               /* Disable board-interrupts */
+               out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, 0);
+       }
+       unregister_serial(line);
+}
+#endif
+
+static void __exit hp300_8250_exit(void)
+{
+#ifdef CONFIG_HPAPCI
+       struct hp300_port *port, *to_free;
+
+       for (port = hp300_ports; port; ) {
+               unregister_serial(port->line);
+               to_free = port;
+               port = port->next;
+               kfree(to_free);
+       }
+
+       hp300_ports = NULL;
+#endif
+#ifdef CONFIG_HPDCA
+       dio_unregister_driver(&hpdca_driver);
+#endif
+}
+
+module_init(hp300_8250_init);
+module_exit(hp300_8250_exit);
+MODULE_DESCRIPTION("HP DCA/APCI serial driver");
+MODULE_AUTHOR("Kars de Jong <jongk@linux-m68k.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c
new file mode 100644 (file)
index 0000000..676c423
--- /dev/null
@@ -0,0 +1,5061 @@
+/* $Id: serial.c,v 1.25 2004/09/29 10:33:49 starvik Exp $
+ *
+ * Serial port driver for the ETRAX 100LX chip
+ *
+ *    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Axis Communications AB
+ *
+ *    Many, many authors. Based once upon a time on serial.c for 16x50.
+ *
+ * $Log: serial.c,v $
+ * Revision 1.25  2004/09/29 10:33:49  starvik
+ * Resolved a dealock when printing debug from kernel.
+ *
+ * Revision 1.24  2004/08/27 23:25:59  johana
+ * rs_set_termios() must call change_speed() if c_iflag has changed or
+ * automatic XOFF handling will be enabled and transmitter will stop
+ * if 0x13 is received.
+ *
+ * Revision 1.23  2004/08/24 06:57:13  starvik
+ * More whitespace cleanup
+ *
+ * Revision 1.22  2004/08/24 06:12:20  starvik
+ * Whitespace cleanup
+ *
+ * Revision 1.20  2004/05/24 12:00:20  starvik
+ * Big merge of stuff from Linux 2.4 (e.g. manual mode for the serial port).
+ *
+ * Revision 1.19  2004/05/17 13:12:15  starvik
+ * Kernel console hook
+ * Big merge from Linux 2.4 still pending.
+ *
+ * Revision 1.18  2003/10/28 07:18:30  starvik
+ * Compiles with debug info
+ *
+ * Revision 1.17  2003/07/04 08:27:37  starvik
+ * Merge of Linux 2.5.74
+ *
+ * Revision 1.16  2003/06/13 10:05:19  johana
+ * Help the user to avoid trouble by:
+ * Forcing mixed mode for status/control lines if not all pins are used.
+ *
+ * Revision 1.15  2003/06/13 09:43:01  johana
+ * Merged in the following changes from os/linux/arch/cris/drivers/serial.c
+ * + some minor changes to reduce diff.
+ *
+ * Revision 1.49  2003/05/30 11:31:54  johana
+ * Merged in change-branch--serial9bit that adds CMSPAR support for sticky
+ * parity (mark/space)
+ *
+ * Revision 1.48  2003/05/30 11:03:57  johana
+ * Implemented rs_send_xchar() by disabling the DMA and writing manually.
+ * Added e100_disable_txdma_channel() and e100_enable_txdma_channel().
+ * Fixed rs_throttle() and rs_unthrottle() to properly call rs_send_xchar
+ * instead of setting info->x_char and check the CRTSCTS flag before
+ * controlling the rts pin.
+ *
+ * Revision 1.14  2003/04/09 08:12:44  pkj
+ * Corrected typo changes made upstream.
+ *
+ * Revision 1.13  2003/04/09 05:20:47  starvik
+ * Merge of Linux 2.5.67
+ *
+ * Revision 1.11  2003/01/22 06:48:37  starvik
+ * Fixed warnings issued by GCC 3.2.1
+ *
+ * Revision 1.9  2002/12/13 09:07:47  starvik
+ * Alert user that RX_TIMEOUT_TICKS==0 doesn't work
+ *
+ * Revision 1.8  2002/12/11 13:13:57  starvik
+ * Added arch/ to v10 specific includes
+ * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer)
+ *
+ * Revision 1.7  2002/12/06 07:13:57  starvik
+ * Corrected work queue stuff
+ * Removed CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
+ *
+ * Revision 1.6  2002/11/21 07:17:46  starvik
+ * Change static inline to extern inline where otherwise outlined with gcc-3.2
+ *
+ * Revision 1.5  2002/11/14 15:59:49  starvik
+ * Linux 2.5 port of the latest serial driver from 2.4. The work queue stuff
+ * probably doesn't work yet.
+ *
+ * Revision 1.42  2002/11/05 09:08:47  johana
+ * Better implementation of rs_stop() and rs_start() that uses the XOFF
+ * register to start/stop transmission.
+ * change_speed() also initilises XOFF register correctly so that
+ * auto_xoff is enabled when IXON flag is set by user.
+ * This gives fast XOFF response times.
+ *
+ * Revision 1.41  2002/11/04 18:40:57  johana
+ * Implemented rs_stop() and rs_start().
+ * Simple tests using hwtestserial indicates that this should be enough
+ * to make it work.
+ *
+ * Revision 1.40  2002/10/14 05:33:18  starvik
+ * RS-485 uses fast timers even if SERIAL_FAST_TIMER is disabled
+ *
+ * Revision 1.39  2002/09/30 21:00:57  johana
+ * Support for CONFIG_ETRAX_SERx_DTR_RI_DSR_CD_MIXED where the status and
+ * control pins can be mixed between PA and PB.
+ * If no serial port uses MIXED old solution is used
+ * (saves a few bytes and cycles).
+ * control_pins struct uses masks instead of bit numbers.
+ * Corrected dummy values and polarity in line_info() so
+ * /proc/tty/driver/serial is now correct.
+ * (the E100_xxx_GET() macros is really active low - perhaps not obvious)
+ *
+ * Revision 1.38  2002/08/23 11:01:36  starvik
+ * Check that serial port is enabled in all interrupt handlers to avoid
+ * restarts of DMA channels not assigned to serial ports
+ *
+ * Revision 1.37  2002/08/13 13:02:37  bjornw
+ * Removed some warnings because of unused code
+ *
+ * Revision 1.36  2002/08/08 12:50:01  starvik
+ * Serial interrupt is shared with synchronous serial port driver
+ *
+ * Revision 1.35  2002/06/03 10:40:49  starvik
+ * Increased RS-485 RTS toggle timer to 2 characters
+ *
+ * Revision 1.34  2002/05/28 18:59:36  johana
+ * Whitespace and comment fixing to be more like etrax100ser.c 1.71.
+ *
+ * Revision 1.33  2002/05/28 17:55:43  johana
+ * RS-485 uses FAST_TIMER if enabled, and starts a short (one char time)
+ * timer from tranismit_chars (interrupt context).
+ * The timer toggles RTS in interrupt context when expired giving minimum
+ * latencies.
+ *
+ * Revision 1.32  2002/05/22 13:58:00  johana
+ * Renamed rs_write() to raw_write() and made it inline.
+ * New rs_write() handles RS-485 if configured and enabled
+ * (moved code from e100_write_rs485()).
+ * RS-485 ioctl's uses copy_from_user() instead of verify_area().
+ *
+ * Revision 1.31  2002/04/22 11:20:03  johana
+ * Updated copyright years.
+ *
+ * Revision 1.30  2002/04/22 09:39:12  johana
+ * RS-485 support compiles.
+ *
+ * Revision 1.29  2002/01/14 16:10:01  pkj
+ * Allocate the receive buffers dynamically. The static 4kB buffer was
+ * too small for the peaks. This means that we can get rid of the extra
+ * buffer and the copying to it. It also means we require less memory
+ * under normal operations, but can use more when needed (there is a
+ * cap at 64kB for safety reasons). If there is no memory available
+ * we panic(), and die a horrible death...
+ *
+ * Revision 1.28  2001/12/18 15:04:53  johana
+ * Cleaned up write_rs485() - now it works correctly without padding extra
+ * char.
+ * Added sane default initialisation of rs485.
+ * Added #ifdef around dummy variables.
+ *
+ * Revision 1.27  2001/11/29 17:00:41  pkj
+ * 2kB seems to be too small a buffer when using 921600 bps,
+ * so increase it to 4kB (this was already done for the elinux
+ * version of the serial driver).
+ *
+ * Revision 1.26  2001/11/19 14:20:41  pkj
+ * Minor changes to comments and unused code.
+ *
+ * Revision 1.25  2001/11/12 20:03:43  pkj
+ * Fixed compiler warnings.
+ *
+ * Revision 1.24  2001/11/12 15:10:05  pkj
+ * Total redesign of the receiving part of the serial driver.
+ * Uses eight chained descriptors to write to a 4kB buffer.
+ * This data is then serialised into a 2kB buffer. From there it
+ * is copied into the TTY's flip buffers when they become available.
+ * A lot of copying, and the sizes of the buffers might need to be
+ * tweaked, but all in all it should work better than the previous
+ * version, without the need to modify the TTY code in any way.
+ * Also note that erroneous bytes are now correctly marked in the
+ * flag buffers (instead of always marking the first byte).
+ *
+ * Revision 1.23  2001/10/30 17:53:26  pkj
+ * * Set info->uses_dma to 0 when a port is closed.
+ * * Mark the timer1 interrupt as a fast one (SA_INTERRUPT).
+ * * Call start_flush_timer() in start_receive() if
+ *   CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST is defined.
+ *
+ * Revision 1.22  2001/10/30 17:44:03  pkj
+ * Use %lu for received and transmitted counters in line_info().
+ *
+ * Revision 1.21  2001/10/30 17:40:34  pkj
+ * Clean-up. The only change to functionality is that
+ * CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS(=5) is used instead of
+ * MAX_FLUSH_TIME(=8).
+ *
+ * Revision 1.20  2001/10/30 15:24:49  johana
+ * Added char_time stuff from 2.0 driver.
+ *
+ * Revision 1.19  2001/10/30 15:23:03  johana
+ * Merged with 1.13.2 branch + fixed indentation
+ * and changed CONFIG_ETRAX100_XYS to CONFIG_ETRAX_XYZ
+ *
+ * Revision 1.18  2001/09/24 09:27:22  pkj
+ * Completed ext_baud_table[] in cflag_to_baud() and cflag_to_etrax_baud().
+ *
+ * Revision 1.17  2001/08/24 11:32:49  ronny
+ * More fixes for the CONFIG_ETRAX_SERIAL_PORT0 define.
+ *
+ * Revision 1.16  2001/08/24 07:56:22  ronny
+ * Added config ifdefs around ser0 irq requests.
+ *
+ * Revision 1.15  2001/08/16 09:10:31  bjarne
+ * serial.c - corrected the initialization of rs_table, the wrong defines
+ *            where used.
+ *            Corrected a test in timed_flush_handler.
+ *            Changed configured to enabled.
+ * serial.h - Changed configured to enabled.
+ *
+ * Revision 1.14  2001/08/15 07:31:23  bjarne
+ * Introduced two new members to the e100_serial struct.
+ * configured - Will be set to 1 if the port has been configured in .config
+ * uses_dma   - Should be set to 1 if the port uses DMA. Currently it is set
+ *              to 1
+ *              when a port is opened. This is used to limit the DMA interrupt
+ *              routines to only manipulate DMA channels actually used by the
+ *              serial driver.
+ *
+ * Revision 1.13.2.2  2001/10/17 13:57:13  starvik
+ * Receiver was broken by the break fixes
+ *
+ * Revision 1.13.2.1  2001/07/20 13:57:39  ronny
+ * Merge with new stuff from etrax100ser.c. Works but haven't checked stuff
+ * like break handling.
+ *
+ * Revision 1.13  2001/05/09 12:40:31  johana
+ * Use DMA_NBR and IRQ_NBR defines from dma.h and irq.h
+ *
+ * Revision 1.12  2001/04/19 12:23:07  bjornw
+ * CONFIG_RS485 -> CONFIG_ETRAX_RS485
+ *
+ * Revision 1.11  2001/04/05 14:29:48  markusl
+ * Updated according to review remarks i.e.
+ * -Use correct types in port structure to avoid compiler warnings
+ * -Try to use IO_* macros whenever possible
+ * -Open should never return -EBUSY
+ *
+ * Revision 1.10  2001/03/05 13:14:07  bjornw
+ * Another spelling fix
+ *
+ * Revision 1.9  2001/02/23 13:46:38  bjornw
+ * Spellling check
+ *
+ * Revision 1.8  2001/01/23 14:56:35  markusl
+ * Made use of ser1 optional
+ * Needed by USB
+ *
+ * Revision 1.7  2001/01/19 16:14:48  perf
+ * Added kernel options for serial ports 234.
+ * Changed option names from CONFIG_ETRAX100_XYZ to CONFIG_ETRAX_XYZ.
+ *
+ * Revision 1.6  2000/11/22 16:36:09  bjornw
+ * Please marketing by using the correct case when spelling Etrax.
+ *
+ * Revision 1.5  2000/11/21 16:43:37  bjornw
+ * Fixed so it compiles under CONFIG_SVINTO_SIM
+ *
+ * Revision 1.4  2000/11/15 17:34:12  bjornw
+ * Added a timeout timer for flushing input channels. The interrupt-based
+ * fast flush system should be easy to merge with this later (works the same
+ * way, only with an irq instead of a system timer_list)
+ *
+ * Revision 1.3  2000/11/13 17:19:57  bjornw
+ * * Incredibly, this almost complete rewrite of serial.c worked (at least
+ *   for output) the first time.
+ *
+ *   Items worth noticing:
+ *
+ *      No Etrax100 port 1 workarounds (does only compile on 2.4 anyway now)
+ *      RS485 is not ported (why can't it be done in userspace as on x86 ?)
+ *      Statistics done through async_icount - if any more stats are needed,
+ *      that's the place to put them or in an arch-dep version of it.
+ *      timeout_interrupt and the other fast timeout stuff not ported yet
+ *      There be dragons in this 3k+ line driver
+ *
+ * Revision 1.2  2000/11/10 16:50:28  bjornw
+ * First shot at a 2.4 port, does not compile totally yet
+ *
+ * Revision 1.1  2000/11/10 16:47:32  bjornw
+ * Added verbatim copy of rev 1.49 etrax100ser.c from elinux
+ *
+ * Revision 1.49  2000/10/30 15:47:14  tobiasa
+ * Changed version number.
+ *
+ * Revision 1.48  2000/10/25 11:02:43  johana
+ * Changed %ul to %lu in printf's
+ *
+ * Revision 1.47  2000/10/18 15:06:53  pkj
+ * Compile correctly with CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST and
+ * CONFIG_ETRAX_SERIAL_PROC_ENTRY together.
+ * Some clean-up of the /proc/serial file.
+ *
+ * Revision 1.46  2000/10/16 12:59:40  johana
+ * Added CONFIG_ETRAX_SERIAL_PROC_ENTRY for statistics and debug info.
+ *
+ * Revision 1.45  2000/10/13 17:10:59  pkj
+ * Do not flush DMAs while flipping TTY buffers.
+ *
+ * Revision 1.44  2000/10/13 16:34:29  pkj
+ * Added a delay in ser_interrupt() for 2.3ms when an error is detected.
+ * We do not know why this delay is required yet, but without it the
+ * irmaflash program does not work (this was the program that needed
+ * the ser_interrupt() to be needed in the first place). This should not
+ * affect normal use of the serial ports.
+ *
+ * Revision 1.43  2000/10/13 16:30:44  pkj
+ * New version of the fast flush of serial buffers code. This time
+ * it is localized to the serial driver and uses a fast timer to
+ * do the work.
+ *
+ * Revision 1.42  2000/10/13 14:54:26  bennyo
+ * Fix for switching RTS when using rs485
+ *
+ * Revision 1.41  2000/10/12 11:43:44  pkj
+ * Cleaned up a number of comments.
+ *
+ * Revision 1.40  2000/10/10 11:58:39  johana
+ * Made RS485 support generic for all ports.
+ * Toggle rts in interrupt if no delay wanted.
+ * WARNING: No true transmitter empty check??
+ * Set d_wait bit when sending data so interrupt is delayed until
+ * fifo flushed. (Fix tcdrain() problem)
+ *
+ * Revision 1.39  2000/10/04 16:08:02  bjornw
+ * * Use virt_to_phys etc. for DMA addresses
+ * * Removed CONFIG_FLUSH_DMA_FAST hacks
+ * * Indentation fix
+ *
+ * Revision 1.38  2000/10/02 12:27:10  mattias
+ * * added variable used when using fast flush on serial dma.
+ *   (CONFIG_FLUSH_DMA_FAST)
+ *
+ * Revision 1.37  2000/09/27 09:44:24  pkj
+ * Uncomment definition of SERIAL_HANDLE_EARLY_ERRORS.
+ *
+ * Revision 1.36  2000/09/20 13:12:52  johana
+ * Support for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS:
+ *   Number of timer ticks between flush of receive fifo (1 tick = 10ms).
+ *   Try 0-3 for low latency applications. Approx 5 for high load
+ *   applications (e.g. PPP). Maybe this should be more adaptive some day...
+ *
+ * Revision 1.35  2000/09/20 10:36:08  johana
+ * Typo in get_lsr_info()
+ *
+ * Revision 1.34  2000/09/20 10:29:59  johana
+ * Let rs_chars_in_buffer() check fifo content as well.
+ * get_lsr_info() might work now (not tested).
+ * Easier to change the port to debug.
+ *
+ * Revision 1.33  2000/09/13 07:52:11  torbjore
+ * Support RS485
+ *
+ * Revision 1.32  2000/08/31 14:45:37  bjornw
+ * After sending a break we need to reset the transmit DMA channel
+ *
+ * Revision 1.31  2000/06/21 12:13:29  johana
+ * Fixed wait for all chars sent when closing port.
+ * (Used to always take 1 second!)
+ * Added shadows for directions of status/ctrl signals.
+ *
+ * Revision 1.30  2000/05/29 16:27:55  bjornw
+ * Simulator ifdef moved a bit
+ *
+ * Revision 1.29  2000/05/09 09:40:30  mattias
+ * * Added description of dma registers used in timeout_interrupt
+ * * Removed old code
+ *
+ * Revision 1.28  2000/05/08 16:38:58  mattias
+ * * Bugfix for flushing fifo in timeout_interrupt
+ *   Problem occurs when bluetooth stack waits for a small number of bytes
+ *   containing an event acknowledging free buffers in bluetooth HW
+ *   As before, data was stuck in fifo until more data came on uart and
+ *   flushed it up to the stack.
+ *
+ * Revision 1.27  2000/05/02 09:52:28  jonasd
+ * Added fix for peculiar etrax behaviour when eop is forced on an empty
+ * fifo. This is used when flashing the IRMA chip. Disabled by default.
+ *
+ * Revision 1.26  2000/03/29 15:32:02  bjornw
+ * 2.0.34 updates
+ *
+ * Revision 1.25  2000/02/16 16:59:36  bjornw
+ * * Receive DMA directly into the flip-buffer, eliminating an intermediary
+ *   receive buffer and a memcpy. Will avoid some overruns.
+ * * Error message on debug port if an overrun or flip buffer overrun occurs.
+ * * Just use the first byte in the flag flip buffer for errors.
+ * * Check for timeout on the serial ports only each 5/100 s, not 1/100.
+ *
+ * Revision 1.24  2000/02/09 18:02:28  bjornw
+ * * Clear serial errors (overrun, framing, parity) correctly. Before, the
+ *   receiver would get stuck if an error occurred and we did not restart
+ *   the input DMA.
+ * * Cosmetics (indentation, some code made into inlines)
+ * * Some more debug options
+ * * Actually shut down the serial port (DMA irq, DMA reset, receiver stop)
+ *   when the last open is closed. Corresponding fixes in startup().
+ * * rs_close() "tx FIFO wait" code moved into right place, bug & -> && fixed
+ *   and make a special case out of port 1 (R_DMA_CHx_STATUS is broken for that)
+ * * e100_disable_rx/enable_rx just disables/enables the receiver, not RTS
+ *
+ * Revision 1.23  2000/01/24 17:46:19  johana
+ * Wait for flush of DMA/FIFO when closing port.
+ *
+ * Revision 1.22  2000/01/20 18:10:23  johana
+ * Added TIOCMGET ioctl to return modem status.
+ * Implemented modem status/control that works with the extra signals
+ * (DTR, DSR, RI,CD) as well.
+ * 3 different modes supported:
+ * ser0 on PB (Bundy), ser1 on PB (Lisa) and ser2 on PA (Bundy)
+ * Fixed DEF_TX value that caused the serial transmitter pin (txd) to go to 0 when
+ * closing the last filehandle, NASTY!.
+ * Added break generation, not tested though!
+ * Use SA_SHIRQ when request_irq() for ser2 and ser3 (shared with) par0 and par1.
+ * You can't use them at the same time (yet..), but you can hopefully switch
+ * between ser2/par0, ser3/par1 with the same kernel config.
+ * Replaced some magic constants with defines
+ *
+ *
+ */
+
+static char *serial_version = "$Revision: 1.25 $";
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <linux/delay.h>
+
+#include <asm/arch/svinto.h>
+
+/* non-arch dependent serial structures are in linux/serial.h */
+#include <linux/serial.h>
+/* while we keep our own stuff (struct e100_serial) in a local .h file */
+#include "serial.h"
+#include <asm/fasttimer.h>
+
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+#ifndef CONFIG_ETRAX_FAST_TIMER
+#error "Enable FAST_TIMER to use SERIAL_FAST_TIMER"
+#endif
+#endif
+
+#if defined(CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS) && \
+           (CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS == 0)
+#error "RX_TIMEOUT_TICKS == 0 not allowed, use 1"
+#endif
+
+#if defined(CONFIG_ETRAX_RS485_ON_PA) && defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+#error "Disable either CONFIG_ETRAX_RS485_ON_PA or CONFIG_ETRAX_RS485_ON_PORT_G"
+#endif
+
+/*
+ * All of the compatibilty code so we can compile serial.c against
+ * older kernels is hidden in serial_compat.h
+ */
+#if defined(LOCAL_HEADERS)
+#include "serial_compat.h"
+#endif
+
+#define _INLINE_ inline
+
+struct tty_driver *serial_driver;
+
+/* serial subtype definitions */
+#ifndef SERIAL_TYPE_NORMAL
+#define SERIAL_TYPE_NORMAL     1
+#endif
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+//#define SERIAL_DEBUG_INTR
+//#define SERIAL_DEBUG_OPEN
+//#define SERIAL_DEBUG_FLOW
+//#define SERIAL_DEBUG_DATA
+//#define SERIAL_DEBUG_THROTTLE
+//#define SERIAL_DEBUG_IO  /* Debug for Extra control and status pins */
+//#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */
+
+/* Enable this to use serial interrupts to handle when you
+   expect the first received event on the serial port to
+   be an error, break or similar. Used to be able to flash IRMA
+   from eLinux */
+#define SERIAL_HANDLE_EARLY_ERRORS
+
+/* Defined and used in n_tty.c, but we need it here as well */
+#define TTY_THRESHOLD_THROTTLE 128
+
+/* Due to buffersizes and threshold values, our SERIAL_DESCR_BUF_SIZE
+ * must not be to high or flow control won't work if we leave it to the tty
+ * layer so we have our own throttling in flush_to_flip
+ * TTY_FLIPBUF_SIZE=512,
+ * TTY_THRESHOLD_THROTTLE/UNTHROTTLE=128
+ * BUF_SIZE can't be > 128
+ */
+/* Currently 16 descriptors x 128 bytes = 2048 bytes */
+#define SERIAL_DESCR_BUF_SIZE 256
+
+#define SERIAL_PRESCALE_BASE 3125000 /* 3.125MHz */
+#define DEF_BAUD_BASE SERIAL_PRESCALE_BASE
+
+/* We don't want to load the system with massive fast timer interrupt
+ * on high baudrates so limit it to 250 us (4kHz) */
+#define MIN_FLUSH_TIME_USEC 250
+
+/* Add an x here to log a lot of timer stuff */
+#define TIMERD(x)
+/* Debug details of interrupt handling */
+#define DINTR1(x)  /* irq on/off, errors */
+#define DINTR2(x)    /* tx and rx */
+/* Debug flip buffer stuff */
+#define DFLIP(x)
+/* Debug flow control and overview of data flow */
+#define DFLOW(x)
+#define DBAUD(x)
+#define DLOG_INT_TRIG(x)
+
+//#define DEBUG_LOG_INCLUDED
+#ifndef DEBUG_LOG_INCLUDED
+#define DEBUG_LOG(line, string, value)
+#else
+struct debug_log_info
+{
+       unsigned long time;
+       unsigned long timer_data;
+//  int line;
+       const char *string;
+       int value;
+};
+#define DEBUG_LOG_SIZE 4096
+
+struct debug_log_info debug_log[DEBUG_LOG_SIZE];
+int debug_log_pos = 0;
+
+#define DEBUG_LOG(_line, _string, _value) do { \
+  if ((_line) == SERIAL_DEBUG_LINE) {\
+    debug_log_func(_line, _string, _value); \
+  }\
+}while(0)
+
+void debug_log_func(int line, const char *string, int value)
+{
+       if (debug_log_pos < DEBUG_LOG_SIZE) {
+               debug_log[debug_log_pos].time = jiffies;
+               debug_log[debug_log_pos].timer_data = *R_TIMER_DATA;
+//    debug_log[debug_log_pos].line = line;
+               debug_log[debug_log_pos].string = string;
+               debug_log[debug_log_pos].value = value;
+               debug_log_pos++;
+       }
+       /*printk(string, value);*/
+}
+#endif
+
+#ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
+/* Default number of timer ticks before flushing rx fifo
+ * When using "little data, low latency applications: use 0
+ * When using "much data applications (PPP)" use ~5
+ */
+#define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5
+#endif
+
+unsigned long timer_data_to_ns(unsigned long timer_data);
+
+static void change_speed(struct e100_serial *info);
+static void rs_throttle(struct tty_struct * tty);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+static int rs_write(struct tty_struct * tty, int from_user,
+                    const unsigned char *buf, int count);
+extern _INLINE_ int rs_raw_write(struct tty_struct * tty, int from_user,
+                            const unsigned char *buf, int count);
+#ifdef CONFIG_ETRAX_RS485
+static int e100_write_rs485(struct tty_struct * tty, int from_user,
+                            const unsigned char *buf, int count);
+#endif
+static int get_lsr_info(struct e100_serial * info, unsigned int *value);
+
+
+#define DEF_BAUD 115200   /* 115.2 kbit/s */
+#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+#define DEF_RX 0x20  /* or SERIAL_CTRL_W >> 8 */
+/* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */
+#define DEF_TX 0x80  /* or SERIAL_CTRL_B */
+
+/* offsets from R_SERIALx_CTRL */
+
+#define REG_DATA 0
+#define REG_DATA_STATUS32 0 /* this is the 32 bit register R_SERIALx_READ */
+#define REG_TR_DATA 0
+#define REG_STATUS 1
+#define REG_TR_CTRL 1
+#define REG_REC_CTRL 2
+#define REG_BAUD 3
+#define REG_XOFF 4  /* this is a 32 bit register */
+
+/* The bitfields are the same for all serial ports */
+#define SER_RXD_MASK         IO_MASK(R_SERIAL0_STATUS, rxd)
+#define SER_DATA_AVAIL_MASK  IO_MASK(R_SERIAL0_STATUS, data_avail)
+#define SER_FRAMING_ERR_MASK IO_MASK(R_SERIAL0_STATUS, framing_err)
+#define SER_PAR_ERR_MASK     IO_MASK(R_SERIAL0_STATUS, par_err)
+#define SER_OVERRUN_MASK     IO_MASK(R_SERIAL0_STATUS, overrun)
+
+#define SER_ERROR_MASK (SER_OVERRUN_MASK | SER_PAR_ERR_MASK | SER_FRAMING_ERR_MASK)
+
+/* Values for info->errorcode */
+#define ERRCODE_SET_BREAK    (TTY_BREAK)
+#define ERRCODE_INSERT        0x100
+#define ERRCODE_INSERT_BREAK (ERRCODE_INSERT | TTY_BREAK)
+
+#define FORCE_EOP(info)  *R_SET_EOP = 1U << info->iseteop;
+
+/*
+ * General note regarding the use of IO_* macros in this file:
+ *
+ * We will use the bits defined for DMA channel 6 when using various
+ * IO_* macros (e.g. IO_STATE, IO_MASK, IO_EXTRACT) and _assume_ they are
+ * the same for all channels (which of course they are).
+ *
+ * We will also use the bits defined for serial port 0 when writing commands
+ * to the different ports, as these bits too are the same for all ports.
+ */
+
+
+/* Mask for the irqs possibly enabled in R_IRQ_MASK1_RD etc. */
+static const unsigned long e100_ser_int_mask = 0
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+| IO_MASK(R_IRQ_MASK1_RD, ser0_data) | IO_MASK(R_IRQ_MASK1_RD, ser0_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+| IO_MASK(R_IRQ_MASK1_RD, ser1_data) | IO_MASK(R_IRQ_MASK1_RD, ser1_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+| IO_MASK(R_IRQ_MASK1_RD, ser2_data) | IO_MASK(R_IRQ_MASK1_RD, ser2_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+| IO_MASK(R_IRQ_MASK1_RD, ser3_data) | IO_MASK(R_IRQ_MASK1_RD, ser3_ready)
+#endif
+;
+unsigned long r_alt_ser_baudrate_shadow = 0;
+
+/* this is the data for the four serial ports in the etrax100 */
+/*  DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */
+/* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */
+
+static struct e100_serial rs_table[] = {
+       { .baud        = DEF_BAUD,
+         .port        = (unsigned char *)R_SERIAL0_CTRL,
+         .irq         = 1U << 12, /* uses DMA 6 and 7 */
+         .oclrintradr = R_DMA_CH6_CLR_INTR,
+         .ofirstadr   = R_DMA_CH6_FIRST,
+         .ocmdadr     = R_DMA_CH6_CMD,
+         .ostatusadr  = R_DMA_CH6_STATUS,
+         .iclrintradr = R_DMA_CH7_CLR_INTR,
+         .ifirstadr   = R_DMA_CH7_FIRST,
+         .icmdadr     = R_DMA_CH7_CMD,
+         .idescradr   = R_DMA_CH7_DESCR,
+         .flags       = STD_FLAGS,
+         .rx_ctrl     = DEF_RX,
+         .tx_ctrl     = DEF_TX,
+         .iseteop     = 2,
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+         .dma_out_enabled = 1,
+#else
+         .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+         .dma_in_enabled = 1,
+#else
+         .dma_in_enabled = 0
+#endif
+#else
+          .enabled  = 0,
+         .dma_out_enabled = 0,
+         .dma_in_enabled = 0
+#endif
+
+},  /* ttyS0 */
+#ifndef CONFIG_SVINTO_SIM
+       { .baud        = DEF_BAUD,
+         .port        = (unsigned char *)R_SERIAL1_CTRL,
+         .irq         = 1U << 16, /* uses DMA 8 and 9 */
+         .oclrintradr = R_DMA_CH8_CLR_INTR,
+         .ofirstadr   = R_DMA_CH8_FIRST,
+         .ocmdadr     = R_DMA_CH8_CMD,
+         .ostatusadr  = R_DMA_CH8_STATUS,
+         .iclrintradr = R_DMA_CH9_CLR_INTR,
+         .ifirstadr   = R_DMA_CH9_FIRST,
+         .icmdadr     = R_DMA_CH9_CMD,
+         .idescradr   = R_DMA_CH9_DESCR,
+         .flags       = STD_FLAGS,
+         .rx_ctrl     = DEF_RX,
+         .tx_ctrl     = DEF_TX,
+         .iseteop     = 3,
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
+         .dma_out_enabled = 1,
+#else
+         .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
+         .dma_in_enabled = 1,
+#else
+         .dma_in_enabled = 0
+#endif
+#else
+          .enabled  = 0,
+         .dma_out_enabled = 0,
+         .dma_in_enabled = 0
+#endif
+},  /* ttyS1 */
+
+       { .baud        = DEF_BAUD,
+         .port        = (unsigned char *)R_SERIAL2_CTRL,
+         .irq         = 1U << 4,  /* uses DMA 2 and 3 */
+         .oclrintradr = R_DMA_CH2_CLR_INTR,
+         .ofirstadr   = R_DMA_CH2_FIRST,
+         .ocmdadr     = R_DMA_CH2_CMD,
+         .ostatusadr  = R_DMA_CH2_STATUS,
+         .iclrintradr = R_DMA_CH3_CLR_INTR,
+         .ifirstadr   = R_DMA_CH3_FIRST,
+         .icmdadr     = R_DMA_CH3_CMD,
+         .idescradr   = R_DMA_CH3_DESCR,
+         .flags       = STD_FLAGS,
+         .rx_ctrl     = DEF_RX,
+         .tx_ctrl     = DEF_TX,
+         .iseteop     = 0,
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+         .dma_out_enabled = 1,
+#else
+         .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+         .dma_in_enabled = 1,
+#else
+         .dma_in_enabled = 0
+#endif
+#else
+          .enabled  = 0,
+         .dma_out_enabled = 0,
+         .dma_in_enabled = 0
+#endif
+ },  /* ttyS2 */
+
+       { .baud        = DEF_BAUD,
+         .port        = (unsigned char *)R_SERIAL3_CTRL,
+         .irq         = 1U << 8,  /* uses DMA 4 and 5 */
+         .oclrintradr = R_DMA_CH4_CLR_INTR,
+         .ofirstadr   = R_DMA_CH4_FIRST,
+         .ocmdadr     = R_DMA_CH4_CMD,
+         .ostatusadr  = R_DMA_CH4_STATUS,
+         .iclrintradr = R_DMA_CH5_CLR_INTR,
+         .ifirstadr   = R_DMA_CH5_FIRST,
+         .icmdadr     = R_DMA_CH5_CMD,
+         .idescradr   = R_DMA_CH5_DESCR,
+         .flags       = STD_FLAGS,
+         .rx_ctrl     = DEF_RX,
+         .tx_ctrl     = DEF_TX,
+         .iseteop     = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+         .dma_out_enabled = 1,
+#else
+         .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
+         .dma_in_enabled = 1,
+#else
+         .dma_in_enabled = 0
+#endif
+#else
+          .enabled  = 0,
+         .dma_out_enabled = 0,
+         .dma_in_enabled = 0
+#endif
+ }   /* ttyS3 */
+#endif
+};
+
+
+#define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial))
+
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+static struct fast_timer fast_timers[NR_PORTS];
+#endif
+
+#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY
+#define PROCSTAT(x) x
+struct ser_statistics_type {
+       int overrun_cnt;
+       int early_errors_cnt;
+       int ser_ints_ok_cnt;
+       int errors_cnt;
+       unsigned long int processing_flip;
+       unsigned long processing_flip_still_room;
+       unsigned long int timeout_flush_cnt;
+       int rx_dma_ints;
+       int tx_dma_ints;
+       int rx_tot;
+       int tx_tot;
+};
+
+static struct ser_statistics_type ser_stat[NR_PORTS];
+
+#else
+
+#define PROCSTAT(x)
+
+#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */
+
+/* RS-485 */
+#if defined(CONFIG_ETRAX_RS485)
+#ifdef CONFIG_ETRAX_FAST_TIMER
+static struct fast_timer fast_timers_rs485[NR_PORTS];
+#endif
+#if defined(CONFIG_ETRAX_RS485_ON_PA)
+static int rs485_pa_bit = CONFIG_ETRAX_RS485_ON_PA_BIT;
+#endif
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+static int rs485_port_g_bit = CONFIG_ETRAX_RS485_ON_PORT_G_BIT;
+#endif
+#endif
+
+/* Info and macros needed for each ports extra control/status signals. */
+#define E100_STRUCT_PORT(line, pinname) \
+ ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
+               (R_PORT_PA_DATA): ( \
+ (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
+               (R_PORT_PB_DATA):&dummy_ser[line]))
+
+#define E100_STRUCT_SHADOW(line, pinname) \
+ ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
+               (&port_pa_data_shadow): ( \
+ (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
+               (&port_pb_data_shadow):&dummy_ser[line]))
+#define E100_STRUCT_MASK(line, pinname) \
+ ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
+               (1<<CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT): ( \
+ (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
+               (1<<CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT):DUMMY_##pinname##_MASK))
+
+#define DUMMY_DTR_MASK 1
+#define DUMMY_RI_MASK  2
+#define DUMMY_DSR_MASK 4
+#define DUMMY_CD_MASK  8
+static unsigned char dummy_ser[NR_PORTS] = {0xFF, 0xFF, 0xFF,0xFF};
+
+/* If not all status pins are used or disabled, use mixed mode */
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+
+#define SER0_PA_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PA_BIT+CONFIG_ETRAX_SER0_RI_ON_PA_BIT+CONFIG_ETRAX_SER0_DSR_ON_PA_BIT+CONFIG_ETRAX_SER0_CD_ON_PA_BIT)
+
+#if SER0_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER0_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER0_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER0_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER0_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER0_PB_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PB_BIT+CONFIG_ETRAX_SER0_RI_ON_PB_BIT+CONFIG_ETRAX_SER0_DSR_ON_PB_BIT+CONFIG_ETRAX_SER0_CD_ON_PB_BIT)
+
+#if SER0_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER0_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER0_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER0_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER0_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT0 */
+
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+
+#define SER1_PA_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PA_BIT+CONFIG_ETRAX_SER1_RI_ON_PA_BIT+CONFIG_ETRAX_SER1_DSR_ON_PA_BIT+CONFIG_ETRAX_SER1_CD_ON_PA_BIT)
+
+#if SER1_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER1_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER1_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER1_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER1_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER1_PB_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PB_BIT+CONFIG_ETRAX_SER1_RI_ON_PB_BIT+CONFIG_ETRAX_SER1_DSR_ON_PB_BIT+CONFIG_ETRAX_SER1_CD_ON_PB_BIT)
+
+#if SER1_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER1_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER1_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER1_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER1_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT1 */
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+
+#define SER2_PA_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PA_BIT+CONFIG_ETRAX_SER2_RI_ON_PA_BIT+CONFIG_ETRAX_SER2_DSR_ON_PA_BIT+CONFIG_ETRAX_SER2_CD_ON_PA_BIT)
+
+#if SER2_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER2_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER2_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER2_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER2_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER2_PB_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PB_BIT+CONFIG_ETRAX_SER2_RI_ON_PB_BIT+CONFIG_ETRAX_SER2_DSR_ON_PB_BIT+CONFIG_ETRAX_SER2_CD_ON_PB_BIT)
+
+#if SER2_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER2_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER2_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER2_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER2_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT2 */
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+
+#define SER3_PA_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PA_BIT+CONFIG_ETRAX_SER3_RI_ON_PA_BIT+CONFIG_ETRAX_SER3_DSR_ON_PA_BIT+CONFIG_ETRAX_SER3_CD_ON_PA_BIT)
+
+#if SER3_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER3_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER3_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER3_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER3_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER3_PB_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PB_BIT+CONFIG_ETRAX_SER3_RI_ON_PB_BIT+CONFIG_ETRAX_SER3_DSR_ON_PB_BIT+CONFIG_ETRAX_SER3_CD_ON_PB_BIT)
+
+#if SER3_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER3_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER3_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER3_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER3_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT3 */
+
+
+#if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED) || \
+    defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED) || \
+    defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED) || \
+    defined(CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED)
+#define CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED
+#endif
+
+#ifdef CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED
+/* The pins can be mixed on PA and PB */
+#define CONTROL_PINS_PORT_NOT_USED(line) \
+  &dummy_ser[line], &dummy_ser[line], \
+  &dummy_ser[line], &dummy_ser[line], \
+  &dummy_ser[line], &dummy_ser[line], \
+  &dummy_ser[line], &dummy_ser[line], \
+  DUMMY_DTR_MASK, DUMMY_RI_MASK, DUMMY_DSR_MASK, DUMMY_CD_MASK
+
+
+struct control_pins
+{
+       volatile unsigned char *dtr_port;
+       unsigned char          *dtr_shadow;
+       volatile unsigned char *ri_port;
+       unsigned char          *ri_shadow;
+       volatile unsigned char *dsr_port;
+       unsigned char          *dsr_shadow;
+       volatile unsigned char *cd_port;
+       unsigned char          *cd_shadow;
+
+       unsigned char dtr_mask;
+       unsigned char ri_mask;
+       unsigned char dsr_mask;
+       unsigned char cd_mask;
+};
+
+static const struct control_pins e100_modem_pins[NR_PORTS] =
+{
+       /* Ser 0 */
+       {
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+       E100_STRUCT_PORT(0,DTR), E100_STRUCT_SHADOW(0,DTR),
+       E100_STRUCT_PORT(0,RI),  E100_STRUCT_SHADOW(0,RI),
+       E100_STRUCT_PORT(0,DSR), E100_STRUCT_SHADOW(0,DSR),
+       E100_STRUCT_PORT(0,CD),  E100_STRUCT_SHADOW(0,CD),
+       E100_STRUCT_MASK(0,DTR),
+       E100_STRUCT_MASK(0,RI),
+       E100_STRUCT_MASK(0,DSR),
+       E100_STRUCT_MASK(0,CD)
+#else
+       CONTROL_PINS_PORT_NOT_USED(0)
+#endif
+       },
+
+       /* Ser 1 */
+       {
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+       E100_STRUCT_PORT(1,DTR), E100_STRUCT_SHADOW(1,DTR),
+       E100_STRUCT_PORT(1,RI),  E100_STRUCT_SHADOW(1,RI),
+       E100_STRUCT_PORT(1,DSR), E100_STRUCT_SHADOW(1,DSR),
+       E100_STRUCT_PORT(1,CD),  E100_STRUCT_SHADOW(1,CD),
+       E100_STRUCT_MASK(1,DTR),
+       E100_STRUCT_MASK(1,RI),
+       E100_STRUCT_MASK(1,DSR),
+       E100_STRUCT_MASK(1,CD)
+#else
+       CONTROL_PINS_PORT_NOT_USED(1)
+#endif
+       },
+
+       /* Ser 2 */
+       {
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+       E100_STRUCT_PORT(2,DTR), E100_STRUCT_SHADOW(2,DTR),
+       E100_STRUCT_PORT(2,RI),  E100_STRUCT_SHADOW(2,RI),
+       E100_STRUCT_PORT(2,DSR), E100_STRUCT_SHADOW(2,DSR),
+       E100_STRUCT_PORT(2,CD),  E100_STRUCT_SHADOW(2,CD),
+       E100_STRUCT_MASK(2,DTR),
+       E100_STRUCT_MASK(2,RI),
+       E100_STRUCT_MASK(2,DSR),
+       E100_STRUCT_MASK(2,CD)
+#else
+       CONTROL_PINS_PORT_NOT_USED(2)
+#endif
+       },
+
+       /* Ser 3 */
+       {
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+       E100_STRUCT_PORT(3,DTR), E100_STRUCT_SHADOW(3,DTR),
+       E100_STRUCT_PORT(3,RI),  E100_STRUCT_SHADOW(3,RI),
+       E100_STRUCT_PORT(3,DSR), E100_STRUCT_SHADOW(3,DSR),
+       E100_STRUCT_PORT(3,CD),  E100_STRUCT_SHADOW(3,CD),
+       E100_STRUCT_MASK(3,DTR),
+       E100_STRUCT_MASK(3,RI),
+       E100_STRUCT_MASK(3,DSR),
+       E100_STRUCT_MASK(3,CD)
+#else
+       CONTROL_PINS_PORT_NOT_USED(3)
+#endif
+       }
+};
+#else  /* CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED */
+
+/* All pins are on either PA or PB for each serial port */
+#define CONTROL_PINS_PORT_NOT_USED(line) \
+  &dummy_ser[line], &dummy_ser[line], \
+  DUMMY_DTR_MASK, DUMMY_RI_MASK, DUMMY_DSR_MASK, DUMMY_CD_MASK
+
+
+struct control_pins
+{
+       volatile unsigned char *port;
+       unsigned char          *shadow;
+
+       unsigned char dtr_mask;
+       unsigned char ri_mask;
+       unsigned char dsr_mask;
+       unsigned char cd_mask;
+};
+
+#define dtr_port port
+#define dtr_shadow shadow
+#define ri_port port
+#define ri_shadow shadow
+#define dsr_port port
+#define dsr_shadow shadow
+#define cd_port port
+#define cd_shadow shadow
+
+static const struct control_pins e100_modem_pins[NR_PORTS] =
+{
+       /* Ser 0 */
+       {
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+       E100_STRUCT_PORT(0,DTR), E100_STRUCT_SHADOW(0,DTR),
+       E100_STRUCT_MASK(0,DTR),
+       E100_STRUCT_MASK(0,RI),
+       E100_STRUCT_MASK(0,DSR),
+       E100_STRUCT_MASK(0,CD)
+#else
+       CONTROL_PINS_PORT_NOT_USED(0)
+#endif
+       },
+
+       /* Ser 1 */
+       {
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+       E100_STRUCT_PORT(1,DTR), E100_STRUCT_SHADOW(1,DTR),
+       E100_STRUCT_MASK(1,DTR),
+       E100_STRUCT_MASK(1,RI),
+       E100_STRUCT_MASK(1,DSR),
+       E100_STRUCT_MASK(1,CD)
+#else
+       CONTROL_PINS_PORT_NOT_USED(1)
+#endif
+       },
+
+       /* Ser 2 */
+       {
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+       E100_STRUCT_PORT(2,DTR), E100_STRUCT_SHADOW(2,DTR),
+       E100_STRUCT_MASK(2,DTR),
+       E100_STRUCT_MASK(2,RI),
+       E100_STRUCT_MASK(2,DSR),
+       E100_STRUCT_MASK(2,CD)
+#else
+       CONTROL_PINS_PORT_NOT_USED(2)
+#endif
+       },
+
+       /* Ser 3 */
+       {
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+       E100_STRUCT_PORT(3,DTR), E100_STRUCT_SHADOW(3,DTR),
+       E100_STRUCT_MASK(3,DTR),
+       E100_STRUCT_MASK(3,RI),
+       E100_STRUCT_MASK(3,DSR),
+       E100_STRUCT_MASK(3,CD)
+#else
+       CONTROL_PINS_PORT_NOT_USED(3)
+#endif
+       }
+};
+#endif /* !CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED */
+
+#define E100_RTS_MASK 0x20
+#define E100_CTS_MASK 0x40
+
+/* All serial port signals are active low:
+ * active   = 0 -> 3.3V to RS-232 driver -> -12V on RS-232 level
+ * inactive = 1 -> 0V   to RS-232 driver -> +12V on RS-232 level
+ *
+ * These macros returns the pin value: 0=0V, >=1 = 3.3V on ETRAX chip
+ */
+
+/* Output */
+#define E100_RTS_GET(info) ((info)->rx_ctrl & E100_RTS_MASK)
+/* Input */
+#define E100_CTS_GET(info) ((info)->port[REG_STATUS] & E100_CTS_MASK)
+
+/* These are typically PA or PB and 0 means 0V, 1 means 3.3V */
+/* Is an output */
+#define E100_DTR_GET(info) ((*e100_modem_pins[(info)->line].dtr_shadow) & e100_modem_pins[(info)->line].dtr_mask)
+
+/* Normally inputs */
+#define E100_RI_GET(info) ((*e100_modem_pins[(info)->line].ri_port) & e100_modem_pins[(info)->line].ri_mask)
+#define E100_CD_GET(info) ((*e100_modem_pins[(info)->line].cd_port) & e100_modem_pins[(info)->line].cd_mask)
+
+/* Input */
+#define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].dsr_port) & e100_modem_pins[(info)->line].dsr_mask)
+
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs 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;
+#ifdef DECLARE_MUTEX
+static DECLARE_MUTEX(tmp_buf_sem);
+#else
+static struct semaphore tmp_buf_sem = MUTEX;
+#endif
+
+/* Calculate the chartime depending on baudrate, numbor of bits etc. */
+static void update_char_time(struct e100_serial * info)
+{
+       tcflag_t cflags = info->tty->termios->c_cflag;
+       int bits;
+
+       /* calc. number of bits / data byte */
+       /* databits + startbit and 1 stopbit */
+       if ((cflags & CSIZE) == CS7)
+               bits = 9;
+       else
+               bits = 10;
+
+       if (cflags & CSTOPB)     /* 2 stopbits ? */
+               bits++;
+
+       if (cflags & PARENB)     /* parity bit ? */
+               bits++;
+
+       /* calc timeout */
+       info->char_time_usec = ((bits * 1000000) / info->baud) + 1;
+       info->flush_time_usec = 4*info->char_time_usec;
+       if (info->flush_time_usec < MIN_FLUSH_TIME_USEC)
+               info->flush_time_usec = MIN_FLUSH_TIME_USEC;
+
+}
+
+/*
+ * This function maps from the Bxxxx defines in asm/termbits.h into real
+ * baud rates.
+ */
+
+static int
+cflag_to_baud(unsigned int cflag)
+{
+       static int baud_table[] = {
+               0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
+               4800, 9600, 19200, 38400 };
+
+       static int ext_baud_table[] = {
+               0, 57600, 115200, 230400, 460800, 921600, 1843200, 6250000,
+                0, 0, 0, 0, 0, 0, 0, 0 };
+
+       if (cflag & CBAUDEX)
+               return ext_baud_table[(cflag & CBAUD) & ~CBAUDEX];
+       else
+               return baud_table[cflag & CBAUD];
+}
+
+/* and this maps to an etrax100 hardware baud constant */
+
+static unsigned char
+cflag_to_etrax_baud(unsigned int cflag)
+{
+       char retval;
+
+       static char baud_table[] = {
+               -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, -1, 3, 4, 5, 6, 7 };
+
+       static char ext_baud_table[] = {
+               -1, 8, 9, 10, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 };
+
+       if (cflag & CBAUDEX)
+               retval = ext_baud_table[(cflag & CBAUD) & ~CBAUDEX];
+       else
+               retval = baud_table[cflag & CBAUD];
+
+       if (retval < 0) {
+               printk(KERN_WARNING "serdriver tried setting invalid baud rate, flags %x.\n", cflag);
+               retval = 5; /* choose default 9600 instead */
+       }
+
+       return retval | (retval << 4); /* choose same for both TX and RX */
+}
+
+
+/* Various static support functions */
+
+/* Functions to set or clear DTR/RTS on the requested line */
+/* It is complicated by the fact that RTS is a serial port register, while
+ * DTR might not be implemented in the HW at all, and if it is, it can be on
+ * any general port.
+ */
+
+
+static inline void
+e100_dtr(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+       unsigned char mask = e100_modem_pins[info->line].dtr_mask;
+
+#ifdef SERIAL_DEBUG_IO
+       printk("ser%i dtr %i mask: 0x%02X\n", info->line, set, mask);
+       printk("ser%i shadow before 0x%02X get: %i\n",
+              info->line, *e100_modem_pins[info->line].dtr_shadow,
+              E100_DTR_GET(info));
+#endif
+       /* DTR is active low */
+       {
+               unsigned long flags;
+
+               save_flags(flags);
+               cli();
+               *e100_modem_pins[info->line].dtr_shadow &= ~mask;
+               *e100_modem_pins[info->line].dtr_shadow |= (set ? 0 : mask);
+               *e100_modem_pins[info->line].dtr_port = *e100_modem_pins[info->line].dtr_shadow;
+               restore_flags(flags);
+       }
+
+#ifdef SERIAL_DEBUG_IO
+       printk("ser%i shadow after 0x%02X get: %i\n",
+              info->line, *e100_modem_pins[info->line].dtr_shadow,
+              E100_DTR_GET(info));
+#endif
+#endif
+}
+
+/* set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive
+ *                                          0=0V    , 1=3.3V
+ */
+static inline void
+e100_rts(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+       unsigned long flags;
+       save_flags(flags);
+       cli();
+       info->rx_ctrl &= ~E100_RTS_MASK;
+       info->rx_ctrl |= (set ? 0 : E100_RTS_MASK);  /* RTS is active low */
+       info->port[REG_REC_CTRL] = info->rx_ctrl;
+       restore_flags(flags);
+#ifdef SERIAL_DEBUG_IO
+       printk("ser%i rts %i\n", info->line, set);
+#endif
+#endif
+}
+
+
+/* If this behaves as a modem, RI and CD is an output */
+static inline void
+e100_ri_out(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+       /* RI is active low */
+       {
+               unsigned char mask = e100_modem_pins[info->line].ri_mask;
+               unsigned long flags;
+
+               save_flags(flags);
+               cli();
+               *e100_modem_pins[info->line].ri_shadow &= ~mask;
+               *e100_modem_pins[info->line].ri_shadow |= (set ? 0 : mask);
+               *e100_modem_pins[info->line].ri_port = *e100_modem_pins[info->line].ri_shadow;
+               restore_flags(flags);
+       }
+#endif
+}
+static inline void
+e100_cd_out(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+       /* CD is active low */
+       {
+               unsigned char mask = e100_modem_pins[info->line].cd_mask;
+               unsigned long flags;
+
+               save_flags(flags);
+               cli();
+               *e100_modem_pins[info->line].cd_shadow &= ~mask;
+               *e100_modem_pins[info->line].cd_shadow |= (set ? 0 : mask);
+               *e100_modem_pins[info->line].cd_port = *e100_modem_pins[info->line].cd_shadow;
+               restore_flags(flags);
+       }
+#endif
+}
+
+static inline void
+e100_disable_rx(struct e100_serial *info)
+{
+#ifndef CONFIG_SVINTO_SIM
+       /* disable the receiver */
+       info->port[REG_REC_CTRL] =
+               (info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
+#endif
+}
+
+static inline void
+e100_enable_rx(struct e100_serial *info)
+{
+#ifndef CONFIG_SVINTO_SIM
+       /* enable the receiver */
+       info->port[REG_REC_CTRL] =
+               (info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
+#endif
+}
+
+/* the rx DMA uses both the dma_descr and the dma_eop interrupts */
+
+static inline void
+e100_disable_rxdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+       printk("rxdma_irq(%d): 0\n",info->line);
+#endif
+       DINTR1(DEBUG_LOG(info->line,"IRQ disable_rxdma_irq %i\n", info->line));
+       *R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3);
+}
+
+static inline void
+e100_enable_rxdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+       printk("rxdma_irq(%d): 1\n",info->line);
+#endif
+       DINTR1(DEBUG_LOG(info->line,"IRQ enable_rxdma_irq %i\n", info->line));
+       *R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3);
+}
+
+/* the tx DMA uses only dma_descr interrupt */
+
+static _INLINE_ void
+e100_disable_txdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+       printk("txdma_irq(%d): 0\n",info->line);
+#endif
+       DINTR1(DEBUG_LOG(info->line,"IRQ disable_txdma_irq %i\n", info->line));
+       *R_IRQ_MASK2_CLR = info->irq;
+}
+
+static _INLINE_ void
+e100_enable_txdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+       printk("txdma_irq(%d): 1\n",info->line);
+#endif
+       DINTR1(DEBUG_LOG(info->line,"IRQ enable_txdma_irq %i\n", info->line));
+       *R_IRQ_MASK2_SET = info->irq;
+}
+
+static _INLINE_ void
+e100_disable_txdma_channel(struct e100_serial *info)
+{
+       unsigned long flags;
+
+       /* Disable output DMA channel for the serial port in question
+        * ( set to something other then serialX)
+        */
+       save_flags(flags);
+       cli();
+       DFLOW(DEBUG_LOG(info->line, "disable_txdma_channel %i\n", info->line));
+       if (info->line == 0) {
+               if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma6)) ==
+                   IO_STATE(R_GEN_CONFIG, dma6, serial0)) {
+                       genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma6);
+                       genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
+               }
+       } else if (info->line == 1) {
+               if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma8)) ==
+                   IO_STATE(R_GEN_CONFIG, dma8, serial1)) {
+                       genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma8);
+                       genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
+               }
+       } else if (info->line == 2) {
+               if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma2)) ==
+                   IO_STATE(R_GEN_CONFIG, dma2, serial2)) {
+                       genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma2);
+                       genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
+               }
+       } else if (info->line == 3) {
+               if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma4)) ==
+                   IO_STATE(R_GEN_CONFIG, dma4, serial3)) {
+                       genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma4);
+                       genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1);
+               }
+       }
+       *R_GEN_CONFIG = genconfig_shadow;
+       restore_flags(flags);
+}
+
+
+static _INLINE_ void
+e100_enable_txdma_channel(struct e100_serial *info)
+{
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       DFLOW(DEBUG_LOG(info->line, "enable_txdma_channel %i\n", info->line));
+       /* Enable output DMA channel for the serial port in question */
+       if (info->line == 0) {
+               genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma6);
+               genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, serial0);
+       } else if (info->line == 1) {
+               genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma8);
+               genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, serial1);
+       } else if (info->line == 2) {
+               genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma2);
+               genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, serial2);
+       } else if (info->line == 3) {
+               genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma4);
+               genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, serial3);
+       }
+       *R_GEN_CONFIG = genconfig_shadow;
+       restore_flags(flags);
+}
+
+static _INLINE_ void
+e100_disable_rxdma_channel(struct e100_serial *info)
+{
+       unsigned long flags;
+
+       /* Disable input DMA channel for the serial port in question
+        * ( set to something other then serialX)
+        */
+       save_flags(flags);
+       cli();
+       if (info->line == 0) {
+               if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma7)) ==
+                   IO_STATE(R_GEN_CONFIG, dma7, serial0)) {
+                       genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma7);
+                       genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, unused);
+               }
+       } else if (info->line == 1) {
+               if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma9)) ==
+                   IO_STATE(R_GEN_CONFIG, dma9, serial1)) {
+                       genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma9);
+                       genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, usb);
+               }
+       } else if (info->line == 2) {
+               if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma3)) ==
+                   IO_STATE(R_GEN_CONFIG, dma3, serial2)) {
+                       genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma3);
+                       genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0);
+               }
+       } else if (info->line == 3) {
+               if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma5)) ==
+                   IO_STATE(R_GEN_CONFIG, dma5, serial3)) {
+                       genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma5);
+                       genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1);
+               }
+       }
+       *R_GEN_CONFIG = genconfig_shadow;
+       restore_flags(flags);
+}
+
+
+static _INLINE_ void
+e100_enable_rxdma_channel(struct e100_serial *info)
+{
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       /* Enable input DMA channel for the serial port in question */
+       if (info->line == 0) {
+               genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma7);
+               genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, serial0);
+       } else if (info->line == 1) {
+               genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma9);
+               genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, serial1);
+       } else if (info->line == 2) {
+               genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma3);
+               genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, serial2);
+       } else if (info->line == 3) {
+               genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma5);
+               genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, serial3);
+       }
+       *R_GEN_CONFIG = genconfig_shadow;
+       restore_flags(flags);
+}
+
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+/* in order to detect and fix errors on the first byte
+   we have to use the serial interrupts as well. */
+
+static inline void
+e100_disable_serial_data_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+       printk("ser_irq(%d): 0\n",info->line);
+#endif
+       DINTR1(DEBUG_LOG(info->line,"IRQ disable data_irq %i\n", info->line));
+       *R_IRQ_MASK1_CLR = (1U << (8+2*info->line));
+}
+
+static inline void
+e100_enable_serial_data_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+       printk("ser_irq(%d): 1\n",info->line);
+       printk("**** %d = %d\n",
+              (8+2*info->line),
+              (1U << (8+2*info->line)));
+#endif
+       DINTR1(DEBUG_LOG(info->line,"IRQ enable data_irq %i\n", info->line));
+       *R_IRQ_MASK1_SET = (1U << (8+2*info->line));
+}
+#endif
+
+static inline void
+e100_disable_serial_tx_ready_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+       printk("ser_tx_irq(%d): 0\n",info->line);
+#endif
+       DINTR1(DEBUG_LOG(info->line,"IRQ disable ready_irq %i\n", info->line));
+       *R_IRQ_MASK1_CLR = (1U << (8+1+2*info->line));
+}
+
+static inline void
+e100_enable_serial_tx_ready_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+       printk("ser_tx_irq(%d): 1\n",info->line);
+       printk("**** %d = %d\n",
+              (8+1+2*info->line),
+              (1U << (8+1+2*info->line)));
+#endif
+       DINTR2(DEBUG_LOG(info->line,"IRQ enable ready_irq %i\n", info->line));
+       *R_IRQ_MASK1_SET = (1U << (8+1+2*info->line));
+}
+
+static inline void e100_enable_rx_irq(struct e100_serial *info)
+{
+       if (info->uses_dma_in)
+               e100_enable_rxdma_irq(info);
+       else
+               e100_enable_serial_data_irq(info);
+}
+static inline void e100_disable_rx_irq(struct e100_serial *info)
+{
+       if (info->uses_dma_in)
+               e100_disable_rxdma_irq(info);
+       else
+               e100_disable_serial_data_irq(info);
+}
+
+#if defined(CONFIG_ETRAX_RS485)
+/* Enable RS-485 mode on selected port. This is UGLY. */
+static int
+e100_enable_rs485(struct tty_struct *tty,struct rs485_control *r)
+{
+       struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+
+#if defined(CONFIG_ETRAX_RS485_ON_PA)
+       *R_PORT_PA_DATA = port_pa_data_shadow |= (1 << rs485_pa_bit);
+#endif
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+       REG_SHADOW_SET(R_PORT_G_DATA,  port_g_data_shadow,
+                      rs485_port_g_bit, 1);
+#endif
+#if defined(CONFIG_ETRAX_RS485_LTC1387)
+       REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+                      CONFIG_ETRAX_RS485_LTC1387_DXEN_PORT_G_BIT, 1);
+       REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+                      CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 1);
+#endif
+
+       info->rs485.rts_on_send = 0x01 & r->rts_on_send;
+       info->rs485.rts_after_sent = 0x01 & r->rts_after_sent;
+       if (r->delay_rts_before_send >= 1000)
+               info->rs485.delay_rts_before_send = 1000;
+       else
+               info->rs485.delay_rts_before_send = r->delay_rts_before_send;
+       info->rs485.enabled = r->enabled;
+/*     printk("rts: on send = %i, after = %i, enabled = %i",
+                   info->rs485.rts_on_send,
+                   info->rs485.rts_after_sent,
+                   info->rs485.enabled
+       );
+*/
+       return 0;
+}
+
+static int
+e100_write_rs485(struct tty_struct *tty, int from_user,
+                 const unsigned char *buf, int count)
+{
+       struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+       int old_enabled = info->rs485.enabled;
+
+       /* rs485 is always implicitly enabled if we're using the ioctl()
+        * but it doesn't have to be set in the rs485_control
+        * (to be backward compatible with old apps)
+        * So we store, set and restore it.
+        */
+       info->rs485.enabled = 1;
+       /* rs_write now deals with RS485 if enabled */
+       count = rs_write(tty, from_user, buf, count);
+       info->rs485.enabled = old_enabled;
+       return count;
+}
+
+#ifdef CONFIG_ETRAX_FAST_TIMER
+/* Timer function to toggle RTS when using FAST_TIMER */
+static void rs485_toggle_rts_timer_function(unsigned long data)
+{
+       struct e100_serial *info = (struct e100_serial *)data;
+
+       fast_timers_rs485[info->line].function = NULL;
+       e100_rts(info, info->rs485.rts_after_sent);
+#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
+       e100_enable_rx(info);
+       e100_enable_rx_irq(info);
+#endif
+}
+#endif
+#endif /* CONFIG_ETRAX_RS485 */
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter using the XOFF registers, as necessary.
+ * ------------------------------------------------------------
+ */
+
+static void
+rs_stop(struct tty_struct *tty)
+{
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+       if (info) {
+               unsigned long flags;
+               unsigned long xoff;
+
+               save_flags(flags); cli();
+               DFLOW(DEBUG_LOG(info->line, "XOFF rs_stop xmit %i\n",
+                               CIRC_CNT(info->xmit.head,
+                                        info->xmit.tail,SERIAL_XMIT_SIZE)));
+
+               xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->tty));
+               xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop);
+               if (tty->termios->c_iflag & IXON ) {
+                       xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
+               }
+
+               *((unsigned long *)&info->port[REG_XOFF]) = xoff;
+               restore_flags(flags);
+       }
+}
+
+static void
+rs_start(struct tty_struct *tty)
+{
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+       if (info) {
+               unsigned long flags;
+               unsigned long xoff;
+
+               save_flags(flags); cli();
+               DFLOW(DEBUG_LOG(info->line, "XOFF rs_start xmit %i\n",
+                               CIRC_CNT(info->xmit.head,
+                                        info->xmit.tail,SERIAL_XMIT_SIZE)));
+               xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty));
+               xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
+               if (tty->termios->c_iflag & IXON ) {
+                       xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
+               }
+
+               *((unsigned long *)&info->port[REG_XOFF]) = xoff;
+               if (!info->uses_dma_out &&
+                   info->xmit.head != info->xmit.tail && info->xmit.buf)
+                       e100_enable_serial_tx_ready_irq(info);
+
+               restore_flags(flags);
+       }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_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 serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ *                             - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void
+rs_sched_event(struct e100_serial *info,
+                                   int event)
+{
+       if (info->event & (1 << event))
+               return;
+       info->event |= 1 << event;
+       schedule_work(&info->work);
+}
+
+/* The output DMA channel is free - use it to send as many chars as possible
+ * NOTES:
+ *   We don't pay attention to info->x_char, which means if the TTY wants to
+ *   use XON/XOFF it will set info->x_char but we won't send any X char!
+ *
+ *   To implement this, we'd just start a DMA send of 1 byte pointing at a
+ *   buffer containing the X char, and skip updating xmit. We'd also have to
+ *   check if the last sent char was the X char when we enter this function
+ *   the next time, to avoid updating xmit with the sent X value.
+ */
+
+static void
+transmit_chars_dma(struct e100_serial *info)
+{
+       unsigned int c, sentl;
+       struct etrax_dma_descr *descr;
+
+#ifdef CONFIG_SVINTO_SIM
+       /* This will output too little if tail is not 0 always since
+        * we don't reloop to send the other part. Anyway this SHOULD be a
+        * no-op - transmit_chars_dma would never really be called during sim
+        * since rs_write does not write into the xmit buffer then.
+        */
+       if (info->xmit.tail)
+               printk("Error in serial.c:transmit_chars-dma(), tail!=0\n");
+       if (info->xmit.head != info->xmit.tail) {
+               SIMCOUT(info->xmit.buf + info->xmit.tail,
+                       CIRC_CNT(info->xmit.head,
+                                info->xmit.tail,
+                                SERIAL_XMIT_SIZE));
+               info->xmit.head = info->xmit.tail;  /* move back head */
+               info->tr_running = 0;
+       }
+       return;
+#endif
+       /* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
+       *info->oclrintradr =
+               IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+               IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+
+#ifdef SERIAL_DEBUG_INTR
+       if (info->line == SERIAL_DEBUG_LINE)
+               printk("tc\n");
+#endif
+       if (!info->tr_running) {
+               /* weirdo... we shouldn't get here! */
+               printk(KERN_WARNING "Achtung: transmit_chars_dma with !tr_running\n");
+               return;
+       }
+
+       descr = &info->tr_descr;
+
+       /* first get the amount of bytes sent during the last DMA transfer,
+          and update xmit accordingly */
+
+       /* if the stop bit was not set, all data has been sent */
+       if (!(descr->status & d_stop)) {
+               sentl = descr->sw_len;
+       } else
+               /* otherwise we find the amount of data sent here */
+               sentl = descr->hw_len;
+
+       DFLOW(DEBUG_LOG(info->line, "TX %i done\n", sentl));
+
+       /* update stats */
+       info->icount.tx += sentl;
+
+       /* update xmit buffer */
+       info->xmit.tail = (info->xmit.tail + sentl) & (SERIAL_XMIT_SIZE - 1);
+
+       /* if there is only a few chars left in the buf, wake up the blocked
+          write if any */
+       if (CIRC_CNT(info->xmit.head,
+                    info->xmit.tail,
+                    SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+       /* find out the largest amount of consecutive bytes we want to send now */
+
+       c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+
+       /* Don't send all in one DMA transfer - divide it so we wake up
+        * application before all is sent
+        */
+
+       if (c >= 4*WAKEUP_CHARS)
+               c = c/2;
+
+       if (c <= 0) {
+               /* our job here is done, don't schedule any new DMA transfer */
+               info->tr_running = 0;
+
+#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER)
+               if (info->rs485.enabled) {
+                       /* Set a short timer to toggle RTS */
+                       start_one_shot_timer(&fast_timers_rs485[info->line],
+                                            rs485_toggle_rts_timer_function,
+                                            (unsigned long)info,
+                                            info->char_time_usec*2,
+                                            "RS-485");
+               }
+#endif /* RS485 */
+               return;
+       }
+
+       /* ok we can schedule a dma send of c chars starting at info->xmit.tail */
+       /* set up the descriptor correctly for output */
+       DFLOW(DEBUG_LOG(info->line, "TX %i\n", c));
+       descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */
+       descr->sw_len = c;
+       descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail);
+       descr->status = 0;
+
+       *info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */
+       *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
+
+       /* DMA is now running (hopefully) */
+} /* transmit_chars_dma */
+
+static void
+start_transmit(struct e100_serial *info)
+{
+#if 0
+       if (info->line == SERIAL_DEBUG_LINE)
+               printk("x\n");
+#endif
+
+       info->tr_descr.sw_len = 0;
+       info->tr_descr.hw_len = 0;
+       info->tr_descr.status = 0;
+       info->tr_running = 1;
+       if (info->uses_dma_out)
+               transmit_chars_dma(info);
+       else
+               e100_enable_serial_tx_ready_irq(info);
+} /* start_transmit */
+
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+static int serial_fast_timer_started = 0;
+static int serial_fast_timer_expired = 0;
+static void flush_timeout_function(unsigned long data);
+#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) {\
+  unsigned long timer_flags; \
+  save_flags(timer_flags); \
+  cli(); \
+  if (fast_timers[info->line].function == NULL) { \
+    serial_fast_timer_started++; \
+    TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
+    TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
+    start_one_shot_timer(&fast_timers[info->line], \
+                         flush_timeout_function, \
+                         (unsigned long)info, \
+                         (usec), \
+                         string); \
+  } \
+  else { \
+    TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \
+  } \
+  restore_flags(timer_flags); \
+}
+#define START_FLUSH_FAST_TIMER(info, string) START_FLUSH_FAST_TIMER_TIME(info, string, info->flush_time_usec)
+
+#else
+#define START_FLUSH_FAST_TIMER_TIME(info, string, usec)
+#define START_FLUSH_FAST_TIMER(info, string)
+#endif
+
+static struct etrax_recv_buffer *
+alloc_recv_buffer(unsigned int size)
+{
+       struct etrax_recv_buffer *buffer;
+
+       if (!(buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC)))
+               return NULL;
+
+       buffer->next = NULL;
+       buffer->length = 0;
+       buffer->error = TTY_NORMAL;
+
+       return buffer;
+}
+
+static void
+append_recv_buffer(struct e100_serial *info, struct etrax_recv_buffer *buffer)
+{
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+
+       if (!info->first_recv_buffer)
+               info->first_recv_buffer = buffer;
+       else
+               info->last_recv_buffer->next = buffer;
+
+       info->last_recv_buffer = buffer;
+
+       info->recv_cnt += buffer->length;
+       if (info->recv_cnt > info->max_recv_cnt)
+               info->max_recv_cnt = info->recv_cnt;
+
+       restore_flags(flags);
+}
+
+static int
+add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag)
+{
+       struct etrax_recv_buffer *buffer;
+       if (info->uses_dma_in) {
+               if (!(buffer = alloc_recv_buffer(4)))
+                       return 0;
+
+               buffer->length = 1;
+               buffer->error = flag;
+               buffer->buffer[0] = data;
+
+               append_recv_buffer(info, buffer);
+
+               info->icount.rx++;
+       } else {
+               struct tty_struct *tty = info->tty;
+               *tty->flip.char_buf_ptr = data;
+               *tty->flip.flag_buf_ptr = flag;
+               tty->flip.flag_buf_ptr++;
+               tty->flip.char_buf_ptr++;
+               tty->flip.count++;
+               info->icount.rx++;
+       }
+
+       return 1;
+}
+
+extern _INLINE_ unsigned int
+handle_descr_data(struct e100_serial *info, struct etrax_dma_descr *descr, unsigned int recvl)
+{
+       struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer;
+
+       if (info->recv_cnt + recvl > 65536) {
+               printk(KERN_CRIT
+                      "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __FUNCTION__, recvl);
+               return 0;
+       }
+
+       buffer->length = recvl;
+
+       if (info->errorcode == ERRCODE_SET_BREAK)
+               buffer->error = TTY_BREAK;
+       info->errorcode = 0;
+
+       append_recv_buffer(info, buffer);
+
+       if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
+               panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__);
+
+       descr->buf = virt_to_phys(buffer->buffer);
+
+       return recvl;
+}
+
+static _INLINE_ unsigned int
+handle_all_descr_data(struct e100_serial *info)
+{
+       struct etrax_dma_descr *descr;
+       unsigned int recvl;
+       unsigned int ret = 0;
+
+       while (1)
+       {
+               descr = &info->rec_descr[info->cur_rec_descr];
+
+               if (descr == phys_to_virt(*info->idescradr))
+                       break;
+
+               if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS)
+                       info->cur_rec_descr = 0;
+
+               /* find out how many bytes were read */
+
+               /* if the eop bit was not set, all data has been received */
+               if (!(descr->status & d_eop)) {
+                       recvl = descr->sw_len;
+               } else {
+                       /* otherwise we find the amount of data received here */
+                       recvl = descr->hw_len;
+               }
+
+               /* Reset the status information */
+               descr->status = 0;
+
+               DFLOW(  DEBUG_LOG(info->line, "RX %lu\n", recvl);
+                       if (info->tty->stopped) {
+                               unsigned char *buf = phys_to_virt(descr->buf);
+                               DEBUG_LOG(info->line, "rx 0x%02X\n", buf[0]);
+                               DEBUG_LOG(info->line, "rx 0x%02X\n", buf[1]);
+                               DEBUG_LOG(info->line, "rx 0x%02X\n", buf[2]);
+                       }
+                       );
+
+               /* update stats */
+               info->icount.rx += recvl;
+
+               ret += handle_descr_data(info, descr, recvl);
+       }
+
+       return ret;
+}
+
+static _INLINE_ void
+receive_chars_dma(struct e100_serial *info)
+{
+       struct tty_struct *tty;
+       unsigned char rstat;
+
+#ifdef CONFIG_SVINTO_SIM
+       /* No receive in the simulator.  Will probably be when the rest of
+        * the serial interface works, and this piece will just be removed.
+        */
+       return;
+#endif
+
+       /* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
+       *info->iclrintradr =
+               IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+               IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+
+       tty = info->tty;
+       if (!tty) /* Something wrong... */
+               return;
+
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+       if (info->uses_dma_in)
+               e100_enable_serial_data_irq(info);
+#endif
+
+       if (info->errorcode == ERRCODE_INSERT_BREAK)
+               add_char_and_flag(info, '\0', TTY_BREAK);
+
+       handle_all_descr_data(info);
+
+       /* Read the status register to detect errors */
+       rstat = info->port[REG_STATUS];
+       if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
+               DFLOW(DEBUG_LOG(info->line, "XOFF detect stat %x\n", rstat));
+       }
+
+       if (rstat & SER_ERROR_MASK) {
+               /* If we got an error, we must reset it by reading the
+                * data_in field
+                */
+               unsigned char data = info->port[REG_DATA];
+
+               PROCSTAT(ser_stat[info->line].errors_cnt++);
+               DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n",
+                         ((rstat & SER_ERROR_MASK) << 8) | data);
+
+               if (rstat & SER_PAR_ERR_MASK)
+                       add_char_and_flag(info, data, TTY_PARITY);
+               else if (rstat & SER_OVERRUN_MASK)
+                       add_char_and_flag(info, data, TTY_OVERRUN);
+               else if (rstat & SER_FRAMING_ERR_MASK)
+                       add_char_and_flag(info, data, TTY_FRAME);
+       }
+
+       START_FLUSH_FAST_TIMER(info, "receive_chars");
+
+       /* Restart the receiving DMA */
+       *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
+}
+
+static _INLINE_ int
+start_recv_dma(struct e100_serial *info)
+{
+       struct etrax_dma_descr *descr = info->rec_descr;
+       struct etrax_recv_buffer *buffer;
+        int i;
+
+       /* Set up the receiving descriptors */
+       for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
+               if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
+                       panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__);
+
+               descr[i].ctrl = d_int;
+               descr[i].buf = virt_to_phys(buffer->buffer);
+               descr[i].sw_len = SERIAL_DESCR_BUF_SIZE;
+               descr[i].hw_len = 0;
+               descr[i].status = 0;
+               descr[i].next = virt_to_phys(&descr[i+1]);
+       }
+
+       /* Link the last descriptor to the first */
+       descr[i-1].next = virt_to_phys(&descr[0]);
+
+       /* Start with the first descriptor in the list */
+       info->cur_rec_descr = 0;
+
+       /* Start the DMA */
+       *info->ifirstadr = virt_to_phys(&descr[info->cur_rec_descr]);
+       *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
+
+       /* Input DMA should be running now */
+       return 1;
+}
+
+static void
+start_receive(struct e100_serial *info)
+{
+#ifdef CONFIG_SVINTO_SIM
+       /* No receive in the simulator.  Will probably be when the rest of
+        * the serial interface works, and this piece will just be removed.
+        */
+       return;
+#endif
+       info->tty->flip.count = 0;
+       if (info->uses_dma_in) {
+               /* reset the input dma channel to be sure it works */
+
+               *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+               while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
+                      IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+
+               start_recv_dma(info);
+       }
+}
+
+
+static _INLINE_ void
+status_handle(struct e100_serial *info, unsigned short status)
+{
+}
+
+/* the bits in the MASK2 register are laid out like this:
+   DMAI_EOP DMAI_DESCR DMAO_EOP DMAO_DESCR
+   where I is the input channel and O is the output channel for the port.
+   info->irq is the bit number for the DMAO_DESCR so to check the others we
+   shift info->irq to the left.
+*/
+
+/* dma output channel interrupt handler
+   this interrupt is called from DMA2(ser2), DMA4(ser3), DMA6(ser0) or
+   DMA8(ser1) when they have finished a descriptor with the intr flag set.
+*/
+
+static irqreturn_t
+tr_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct e100_serial *info;
+       unsigned long ireg;
+       int i;
+       int handled = 0;
+
+#ifdef CONFIG_SVINTO_SIM
+       /* No receive in the simulator.  Will probably be when the rest of
+        * the serial interface works, and this piece will just be removed.
+        */
+       {
+               const char *s = "What? tr_interrupt in simulator??\n";
+               SIMCOUT(s,strlen(s));
+       }
+       return IRQ_HANDLED;
+#endif
+
+       /* find out the line that caused this irq and get it from rs_table */
+
+       ireg = *R_IRQ_MASK2_RD;  /* get the active irq bits for the dma channels */
+
+       for (i = 0; i < NR_PORTS; i++) {
+               info = rs_table + i;
+               if (!info->enabled || !info->uses_dma_out)
+                       continue;
+               /* check for dma_descr (don't need to check for dma_eop in output dma for serial */
+               if (ireg & info->irq) {
+                       handled = 1;
+                       /* we can send a new dma bunch. make it so. */
+                       DINTR2(DEBUG_LOG(info->line, "tr_interrupt %i\n", i));
+                       /* Read jiffies_usec first,
+                        * we want this time to be as late as possible
+                        */
+                       PROCSTAT(ser_stat[info->line].tx_dma_ints++);
+                       info->last_tx_active_usec = GET_JIFFIES_USEC();
+                       info->last_tx_active = jiffies;
+                       transmit_chars_dma(info);
+               }
+
+               /* FIXME: here we should really check for a change in the
+                  status lines and if so call status_handle(info) */
+       }
+       return IRQ_RETVAL(handled);
+} /* tr_interrupt */
+
+/* dma input channel interrupt handler */
+
+static irqreturn_t
+rec_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct e100_serial *info;
+       unsigned long ireg;
+       int i;
+       int handled = 0;
+
+#ifdef CONFIG_SVINTO_SIM
+       /* No receive in the simulator.  Will probably be when the rest of
+        * the serial interface works, and this piece will just be removed.
+        */
+       {
+               const char *s = "What? rec_interrupt in simulator??\n";
+               SIMCOUT(s,strlen(s));
+       }
+       return IRQ_HANDLED;
+#endif
+
+       /* find out the line that caused this irq and get it from rs_table */
+
+       ireg = *R_IRQ_MASK2_RD;  /* get the active irq bits for the dma channels */
+
+       for (i = 0; i < NR_PORTS; i++) {
+               info = rs_table + i;
+               if (!info->enabled || !info->uses_dma_in)
+                       continue;
+               /* check for both dma_eop and dma_descr for the input dma channel */
+               if (ireg & ((info->irq << 2) | (info->irq << 3))) {
+                       handled = 1;
+                       /* we have received something */
+                       receive_chars_dma(info);
+               }
+
+               /* FIXME: here we should really check for a change in the
+                  status lines and if so call status_handle(info) */
+       }
+       return IRQ_RETVAL(handled);
+} /* rec_interrupt */
+
+static _INLINE_ int
+force_eop_if_needed(struct e100_serial *info)
+{
+       /* We check data_avail bit to determine if data has
+        * arrived since last time
+        */
+       unsigned char rstat = info->port[REG_STATUS];
+
+       /* error or datavail? */
+       if (rstat & SER_ERROR_MASK) {
+               /* Some error has occurred. If there has been valid data, an
+                * EOP interrupt will be made automatically. If no data, the
+                * normal ser_interrupt should be enabled and handle it.
+                * So do nothing!
+                */
+               DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n",
+                         rstat | (info->line << 8));
+               return 0;
+       }
+
+       if (rstat & SER_DATA_AVAIL_MASK) {
+               /* Ok data, no error, count it */
+               TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n",
+                         rstat | (info->line << 8)));
+               /* Read data to clear status flags */
+               (void)info->port[REG_DATA];
+
+               info->forced_eop = 0;
+               START_FLUSH_FAST_TIMER(info, "magic");
+               return 0;
+       }
+
+       /* hit the timeout, force an EOP for the input
+        * dma channel if we haven't already
+        */
+       if (!info->forced_eop) {
+               info->forced_eop = 1;
+               PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
+               TIMERD(DEBUG_LOG(info->line, "timeout EOP %i\n", info->line));
+               FORCE_EOP(info);
+       }
+
+       return 1;
+}
+
+extern _INLINE_ void
+flush_to_flip_buffer(struct e100_serial *info)
+{
+       struct tty_struct *tty;
+       struct etrax_recv_buffer *buffer;
+       unsigned int length;
+       unsigned long flags;
+       int max_flip_size;
+
+       if (!info->first_recv_buffer)
+               return;
+
+       save_flags(flags);
+       cli();
+
+       if (!(tty = info->tty)) {
+               restore_flags(flags);
+               return;
+       }
+
+       length = tty->flip.count;
+       /* Don't flip more than the ldisc has room for.
+        * The return value from ldisc.receive_room(tty) - might not be up to
+        * date, the previous flip of up to TTY_FLIPBUF_SIZE might be on the
+        * processed and not accounted for yet.
+        * Since we use DMA, 1 SERIAL_DESCR_BUF_SIZE could be on the way.
+        * Lets buffer data here and let flow control take care of it.
+        * Since we normally flip large chunks, the ldisc don't react
+        * with throttle until too late if we flip to much.
+        */
+       max_flip_size = tty->ldisc.receive_room(tty);
+       if (max_flip_size < 0)
+               max_flip_size = 0;
+       if (max_flip_size <= (TTY_FLIPBUF_SIZE +         /* Maybe not accounted for */
+                             length + info->recv_cnt +  /* We have this queued */
+                             2*SERIAL_DESCR_BUF_SIZE +    /* This could be on the way */
+                             TTY_THRESHOLD_THROTTLE)) { /* Some slack */
+               /* check TTY_THROTTLED first so it indicates our state */
+               if (!test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
+                       DFLOW(DEBUG_LOG(info->line,"flush_to_flip throttles room %lu\n", max_flip_size));
+                       rs_throttle(tty);
+               }
+#if 0
+               else if (max_flip_size <= (TTY_FLIPBUF_SIZE +         /* Maybe not accounted for */
+                                          length + info->recv_cnt +  /* We have this queued */
+                                          SERIAL_DESCR_BUF_SIZE +    /* This could be on the way */
+                                          TTY_THRESHOLD_THROTTLE)) { /* Some slack */
+                       DFLOW(DEBUG_LOG(info->line,"flush_to_flip throttles again! %lu\n", max_flip_size));
+                       rs_throttle(tty);
+               }
+#endif
+       }
+
+       if (max_flip_size > TTY_FLIPBUF_SIZE)
+               max_flip_size = TTY_FLIPBUF_SIZE;
+
+       while ((buffer = info->first_recv_buffer) && length < max_flip_size) {
+               unsigned int count = buffer->length;
+
+               if (length + count > max_flip_size)
+                       count = max_flip_size - length;
+
+               memcpy(tty->flip.char_buf_ptr + length, buffer->buffer, count);
+               memset(tty->flip.flag_buf_ptr + length, TTY_NORMAL, count);
+               tty->flip.flag_buf_ptr[length] = buffer->error;
+
+               length += count;
+               info->recv_cnt -= count;
+               DFLIP(DEBUG_LOG(info->line,"flip: %i\n", length));
+
+               if (count == buffer->length) {
+                       info->first_recv_buffer = buffer->next;
+                       kfree(buffer);
+               } else {
+                       buffer->length -= count;
+                       memmove(buffer->buffer, buffer->buffer + count, buffer->length);
+                       buffer->error = TTY_NORMAL;
+               }
+       }
+
+       if (!info->first_recv_buffer)
+               info->last_recv_buffer = NULL;
+
+       tty->flip.count = length;
+       DFLIP(if (tty->ldisc.chars_in_buffer(tty) > 3500) {
+               DEBUG_LOG(info->line, "ldisc %lu\n",
+                         tty->ldisc.chars_in_buffer(tty));
+               DEBUG_LOG(info->line, "flip.count %lu\n",
+                         tty->flip.count);
+             }
+             );
+       restore_flags(flags);
+
+       DFLIP(
+         if (1) {
+
+                 if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
+                         DEBUG_LOG(info->line, "*** TTY_DONT_FLIP set flip.count %i ***\n", tty->flip.count);
+                         DEBUG_LOG(info->line, "*** recv_cnt %i\n", info->recv_cnt);
+                 } else {
+                 }
+                 DEBUG_LOG(info->line, "*** rxtot %i\n", info->icount.rx);
+                 DEBUG_LOG(info->line, "ldisc %lu\n", tty->ldisc.chars_in_buffer(tty));
+                 DEBUG_LOG(info->line, "room  %lu\n", tty->ldisc.receive_room(tty));
+         }
+
+       );
+
+       /* this includes a check for low-latency */
+       tty_flip_buffer_push(tty);
+}
+
+static _INLINE_ void
+check_flush_timeout(struct e100_serial *info)
+{
+       /* Flip what we've got (if we can) */
+       flush_to_flip_buffer(info);
+
+       /* We might need to flip later, but not to fast
+        * since the system is busy processing input... */
+       if (info->first_recv_buffer)
+               START_FLUSH_FAST_TIMER_TIME(info, "flip", 2000);
+
+       /* Force eop last, since data might have come while we're processing
+        * and if we started the slow timer above, we won't start a fast
+        * below.
+        */
+       force_eop_if_needed(info);
+}
+
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+static void flush_timeout_function(unsigned long data)
+{
+       struct e100_serial *info = (struct e100_serial *)data;
+
+       fast_timers[info->line].function = NULL;
+       serial_fast_timer_expired++;
+       TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line));
+       TIMERD(DEBUG_LOG(info->line, "num expired: %i\n", serial_fast_timer_expired));
+       check_flush_timeout(info);
+}
+
+#else
+
+/* dma fifo/buffer timeout handler
+   forces an end-of-packet for the dma input channel if no chars
+   have been received for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS/100 s.
+*/
+
+static struct timer_list flush_timer;
+
+static void
+timed_flush_handler(unsigned long ptr)
+{
+       struct e100_serial *info;
+       int i;
+
+#ifdef CONFIG_SVINTO_SIM
+       return;
+#endif
+
+       for (i = 0; i < NR_PORTS; i++) {
+               info = rs_table + i;
+               if (info->uses_dma_in)
+                       check_flush_timeout(info);
+       }
+
+       /* restart flush timer */
+       mod_timer(&flush_timer, jiffies + CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS);
+}
+#endif
+
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+
+/* If there is an error (ie break) when the DMA is running and
+ * there are no bytes in the fifo the DMA is stopped and we get no
+ * eop interrupt. Thus we have to monitor the first bytes on a DMA
+ * transfer, and if it is without error we can turn the serial
+ * interrupts off.
+ */
+
+/*
+BREAK handling on ETRAX 100:
+ETRAX will generate interrupt although there is no stop bit between the
+characters.
+
+Depending on how long the break sequence is, the end of the breaksequence
+will look differently:
+| indicates start/end of a character.
+
+B= Break character (0x00) with framing error.
+E= Error byte with parity error received after B characters.
+F= "Faked" valid byte received immediately after B characters.
+V= Valid byte
+
+1.
+    B          BL         ___________________________ V
+.._|__________|__________|                           |valid data |
+
+Multiple frame errors with data == 0x00 (B),
+the timing matches up "perfectly" so no extra ending char is detected.
+The RXD pin is 1 in the last interrupt, in that case
+we set info->errorcode = ERRCODE_INSERT_BREAK, but we can't really
+know if another byte will come and this really is case 2. below
+(e.g F=0xFF or 0xFE)
+If RXD pin is 0 we can expect another character (see 2. below).
+
+
+2.
+
+    B          B          E or F__________________..__ V
+.._|__________|__________|______    |                 |valid data
+                          "valid" or
+                          parity error
+
+Multiple frame errors with data == 0x00 (B),
+but the part of the break trigs is interpreted as a start bit (and possibly
+some 0 bits followed by a number of 1 bits and a stop bit).
+Depending on parity settings etc. this last character can be either
+a fake "valid" char (F) or have a parity error (E).
+
+If the character is valid it will be put in the buffer,
+we set info->errorcode = ERRCODE_SET_BREAK so the receive interrupt
+will set the flags so the tty will handle it,
+if it's an error byte it will not be put in the buffer
+and we set info->errorcode = ERRCODE_INSERT_BREAK.
+
+To distinguish a V byte in 1. from an F byte in 2. we keep a timestamp
+of the last faulty char (B) and compares it with the current time:
+If the time elapsed time is less then 2*char_time_usec we will assume
+it's a faked F char and not a Valid char and set
+info->errorcode = ERRCODE_SET_BREAK.
+
+Flaws in the above solution:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We use the timer to distinguish a F character from a V character,
+if a V character is to close after the break we might make the wrong decision.
+
+TODO: The break will be delayed until an F or V character is received.
+
+*/
+
+extern _INLINE_
+struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info)
+{
+       unsigned long data_read;
+       struct tty_struct *tty = info->tty;
+
+       if (!tty) {
+               printk("!NO TTY!\n");
+               return info;
+       }
+       if (tty->flip.count >= TTY_FLIPBUF_SIZE - TTY_THRESHOLD_THROTTLE) {
+               /* check TTY_THROTTLED first so it indicates our state */
+               if (!test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
+                       DFLOW(DEBUG_LOG(info->line, "rs_throttle flip.count: %i\n", tty->flip.count));
+                       rs_throttle(tty);
+               }
+       }
+       if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+               DEBUG_LOG(info->line, "force FLIP! %i\n", tty->flip.count);
+               tty->flip.work.func((void *) tty);
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                       DEBUG_LOG(info->line, "FLIP FULL! %i\n", tty->flip.count);
+                       return info;            /* if TTY_DONT_FLIP is set */
+               }
+       }
+       /* Read data and status at the same time */
+       data_read = *((unsigned long *)&info->port[REG_DATA_STATUS32]);
+more_data:
+       if (data_read & IO_MASK(R_SERIAL0_READ, xoff_detect) ) {
+               DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
+       }
+       DINTR2(DEBUG_LOG(info->line, "ser_rx   %c\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read)));
+
+       if (data_read & ( IO_MASK(R_SERIAL0_READ, framing_err) |
+                         IO_MASK(R_SERIAL0_READ, par_err) |
+                         IO_MASK(R_SERIAL0_READ, overrun) )) {
+               /* An error */
+               info->last_rx_active_usec = GET_JIFFIES_USEC();
+               info->last_rx_active = jiffies;
+               DINTR1(DEBUG_LOG(info->line, "ser_rx err stat_data %04X\n", data_read));
+               DLOG_INT_TRIG(
+               if (!log_int_trig1_pos) {
+                       log_int_trig1_pos = log_int_pos;
+                       log_int(rdpc(), 0, 0);
+               }
+               );
+
+
+               if ( ((data_read & IO_MASK(R_SERIAL0_READ, data_in)) == 0) &&
+                    (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) ) {
+                       /* Most likely a break, but we get interrupts over and
+                        * over again.
+                        */
+
+                       if (!info->break_detected_cnt) {
+                               DEBUG_LOG(info->line, "#BRK start\n", 0);
+                       }
+                       if (data_read & IO_MASK(R_SERIAL0_READ, rxd)) {
+                               /* The RX pin is high now, so the break
+                                * must be over, but....
+                                * we can't really know if we will get another
+                                * last byte ending the break or not.
+                                * And we don't know if the byte (if any) will
+                                * have an error or look valid.
+                                */
+                               DEBUG_LOG(info->line, "# BL BRK\n", 0);
+                               info->errorcode = ERRCODE_INSERT_BREAK;
+                       }
+                       info->break_detected_cnt++;
+               } else {
+                       /* The error does not look like a break, but could be
+                        * the end of one
+                        */
+                       if (info->break_detected_cnt) {
+                               DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
+                               info->errorcode = ERRCODE_INSERT_BREAK;
+                       } else {
+                               if (info->errorcode == ERRCODE_INSERT_BREAK) {
+                                       info->icount.brk++;
+                                       *tty->flip.char_buf_ptr = 0;
+                                       *tty->flip.flag_buf_ptr = TTY_BREAK;
+                                       tty->flip.flag_buf_ptr++;
+                                       tty->flip.char_buf_ptr++;
+                                       tty->flip.count++;
+                                       info->icount.rx++;
+                               }
+                               *tty->flip.char_buf_ptr = IO_EXTRACT(R_SERIAL0_READ, data_in, data_read);
+
+                               if (data_read & IO_MASK(R_SERIAL0_READ, par_err)) {
+                                       info->icount.parity++;
+                                       *tty->flip.flag_buf_ptr = TTY_PARITY;
+                               } else if (data_read & IO_MASK(R_SERIAL0_READ, overrun)) {
+                                       info->icount.overrun++;
+                                       *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+                               } else if (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) {
+                                       info->icount.frame++;
+                                       *tty->flip.flag_buf_ptr = TTY_FRAME;
+                               }
+                               info->errorcode = 0;
+                       }
+                       info->break_detected_cnt = 0;
+               }
+       } else if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
+               /* No error */
+               DLOG_INT_TRIG(
+               if (!log_int_trig1_pos) {
+                       if (log_int_pos >= log_int_size) {
+                               log_int_pos = 0;
+                       }
+                       log_int_trig0_pos = log_int_pos;
+                       log_int(rdpc(), 0, 0);
+               }
+               );
+               *tty->flip.char_buf_ptr = IO_EXTRACT(R_SERIAL0_READ, data_in, data_read);
+               *tty->flip.flag_buf_ptr = 0;
+       } else {
+               DEBUG_LOG(info->line, "ser_rx int but no data_avail  %08lX\n", data_read);
+       }
+
+
+       tty->flip.flag_buf_ptr++;
+       tty->flip.char_buf_ptr++;
+       tty->flip.count++;
+       info->icount.rx++;
+       data_read = *((unsigned long *)&info->port[REG_DATA_STATUS32]);
+       if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
+               DEBUG_LOG(info->line, "ser_rx   %c in loop\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read));
+               goto more_data;
+       }
+
+       tty_flip_buffer_push(info->tty);
+       return info;
+}
+
+extern _INLINE_
+struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info)
+{
+       unsigned char rstat;
+
+#ifdef SERIAL_DEBUG_INTR
+       printk("Interrupt from serport %d\n", i);
+#endif
+/*     DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
+       if (!info->uses_dma_in) {
+               return handle_ser_rx_interrupt_no_dma(info);
+       }
+       /* DMA is used */
+       rstat = info->port[REG_STATUS];
+       if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
+               DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
+       }
+
+       if (rstat & SER_ERROR_MASK) {
+               unsigned char data;
+
+               info->last_rx_active_usec = GET_JIFFIES_USEC();
+               info->last_rx_active = jiffies;
+               /* If we got an error, we must reset it by reading the
+                * data_in field
+                */
+               data = info->port[REG_DATA];
+               DINTR1(DEBUG_LOG(info->line, "ser_rx!  %c\n", data));
+               DINTR1(DEBUG_LOG(info->line, "ser_rx err stat %02X\n", rstat));
+               if (!data && (rstat & SER_FRAMING_ERR_MASK)) {
+                       /* Most likely a break, but we get interrupts over and
+                        * over again.
+                        */
+
+                       if (!info->break_detected_cnt) {
+                               DEBUG_LOG(info->line, "#BRK start\n", 0);
+                       }
+                       if (rstat & SER_RXD_MASK) {
+                               /* The RX pin is high now, so the break
+                                * must be over, but....
+                                * we can't really know if we will get another
+                                * last byte ending the break or not.
+                                * And we don't know if the byte (if any) will
+                                * have an error or look valid.
+                                */
+                               DEBUG_LOG(info->line, "# BL BRK\n", 0);
+                               info->errorcode = ERRCODE_INSERT_BREAK;
+                       }
+                       info->break_detected_cnt++;
+               } else {
+                       /* The error does not look like a break, but could be
+                        * the end of one
+                        */
+                       if (info->break_detected_cnt) {
+                               DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
+                               info->errorcode = ERRCODE_INSERT_BREAK;
+                       } else {
+                               if (info->errorcode == ERRCODE_INSERT_BREAK) {
+                                       info->icount.brk++;
+                                       add_char_and_flag(info, '\0', TTY_BREAK);
+                               }
+
+                               if (rstat & SER_PAR_ERR_MASK) {
+                                       info->icount.parity++;
+                                       add_char_and_flag(info, data, TTY_PARITY);
+                               } else if (rstat & SER_OVERRUN_MASK) {
+                                       info->icount.overrun++;
+                                       add_char_and_flag(info, data, TTY_OVERRUN);
+                               } else if (rstat & SER_FRAMING_ERR_MASK) {
+                                       info->icount.frame++;
+                                       add_char_and_flag(info, data, TTY_FRAME);
+                               }
+
+                               info->errorcode = 0;
+                       }
+                       info->break_detected_cnt = 0;
+                       DEBUG_LOG(info->line, "#iERR s d %04X\n",
+                                 ((rstat & SER_ERROR_MASK) << 8) | data);
+               }
+               PROCSTAT(ser_stat[info->line].early_errors_cnt++);
+       } else { /* It was a valid byte, now let the DMA do the rest */
+               unsigned long curr_time_u = GET_JIFFIES_USEC();
+               unsigned long curr_time = jiffies;
+
+               if (info->break_detected_cnt) {
+                       /* Detect if this character is a new valid char or the
+                        * last char in a break sequence: If LSBits are 0 and
+                        * MSBits are high AND the time is close to the
+                        * previous interrupt we should discard it.
+                        */
+                       long elapsed_usec =
+                         (curr_time - info->last_rx_active) * (1000000/HZ) +
+                         curr_time_u - info->last_rx_active_usec;
+                       if (elapsed_usec < 2*info->char_time_usec) {
+                               DEBUG_LOG(info->line, "FBRK %i\n", info->line);
+                               /* Report as BREAK (error) and let
+                                * receive_chars_dma() handle it
+                                */
+                               info->errorcode = ERRCODE_SET_BREAK;
+                       } else {
+                               DEBUG_LOG(info->line, "Not end of BRK (V)%i\n", info->line);
+                       }
+                       DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt);
+               }
+
+#ifdef SERIAL_DEBUG_INTR
+               printk("** OK, disabling ser_interrupts\n");
+#endif
+               e100_disable_serial_data_irq(info);
+               DINTR2(DEBUG_LOG(info->line, "ser_rx OK %d\n", info->line));
+               info->break_detected_cnt = 0;
+
+               PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
+       }
+       /* Restarting the DMA never hurts */
+       *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
+       START_FLUSH_FAST_TIMER(info, "ser_int");
+       return info;
+} /* handle_ser_rx_interrupt */
+
+extern _INLINE_ void handle_ser_tx_interrupt(struct e100_serial *info)
+{
+       unsigned long flags;
+
+       if (info->x_char) {
+               unsigned char rstat;
+               DFLOW(DEBUG_LOG(info->line, "tx_int: xchar 0x%02X\n", info->x_char));
+               save_flags(flags); cli();
+               rstat = info->port[REG_STATUS];
+               DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
+
+               info->port[REG_TR_DATA] = info->x_char;
+               info->icount.tx++;
+               info->x_char = 0;
+               /* We must enable since it is disabled in ser_interrupt */
+               e100_enable_serial_tx_ready_irq(info);
+               restore_flags(flags);
+               return;
+       }
+       if (info->uses_dma_out) {
+               unsigned char rstat;
+               int i;
+               /* We only use normal tx interrupt when sending x_char */
+               DFLOW(DEBUG_LOG(info->line, "tx_int: xchar sent\n", 0));
+               save_flags(flags); cli();
+               rstat = info->port[REG_STATUS];
+               DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
+               e100_disable_serial_tx_ready_irq(info);
+               if (info->tty->stopped)
+                       rs_stop(info->tty);
+               /* Enable the DMA channel and tell it to continue */
+               e100_enable_txdma_channel(info);
+               /* Wait 12 cycles before doing the DMA command */
+               for(i = 6;  i > 0; i--)
+                       nop();
+
+               *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, continue);
+               restore_flags(flags);
+               return;
+       }
+       /* Normal char-by-char interrupt */
+       if (info->xmit.head == info->xmit.tail
+           || info->tty->stopped
+           || info->tty->hw_stopped) {
+               DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n", info->tty->stopped));
+               e100_disable_serial_tx_ready_irq(info);
+               info->tr_running = 0;
+               return;
+       }
+       DINTR2(DEBUG_LOG(info->line, "tx_int %c\n", info->xmit.buf[info->xmit.tail]));
+       /* Send a byte, rs485 timing is critical so turn of ints */
+       save_flags(flags); cli();
+       info->port[REG_TR_DATA] = info->xmit.buf[info->xmit.tail];
+       info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
+       info->icount.tx++;
+       if (info->xmit.head == info->xmit.tail) {
+#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER)
+               if (info->rs485.enabled) {
+                       /* Set a short timer to toggle RTS */
+                       start_one_shot_timer(&fast_timers_rs485[info->line],
+                                            rs485_toggle_rts_timer_function,
+                                            (unsigned long)info,
+                                            info->char_time_usec*2,
+                                            "RS-485");
+               }
+#endif /* RS485 */
+               info->last_tx_active_usec = GET_JIFFIES_USEC();
+               info->last_tx_active = jiffies;
+               e100_disable_serial_tx_ready_irq(info);
+               info->tr_running = 0;
+               DFLOW(DEBUG_LOG(info->line, "tx_int: stop2\n", 0));
+       } else {
+               /* We must enable since it is disabled in ser_interrupt */
+               e100_enable_serial_tx_ready_irq(info);
+       }
+       restore_flags(flags);
+
+       if (CIRC_CNT(info->xmit.head,
+                    info->xmit.tail,
+                    SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+} /* handle_ser_tx_interrupt */
+
+/* result of time measurements:
+ * RX duration 54-60 us when doing something, otherwise 6-9 us
+ * ser_int duration: just sending: 8-15 us normally, up to 73 us
+ */
+static irqreturn_t
+ser_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       static volatile int tx_started = 0;
+       struct e100_serial *info;
+       int i;
+       unsigned long flags;
+       unsigned long irq_mask1_rd;
+       unsigned long data_mask = (1 << (8+2*0)); /* ser0 data_avail */
+       int handled = 0;
+       static volatile unsigned long reentered_ready_mask = 0;
+
+       save_flags(flags); cli();
+       irq_mask1_rd = *R_IRQ_MASK1_RD;
+       /* First handle all rx interrupts with ints disabled */
+       info = rs_table;
+       irq_mask1_rd &= e100_ser_int_mask;
+       for (i = 0; i < NR_PORTS; i++) {
+               /* Which line caused the data irq? */
+               if (irq_mask1_rd & data_mask) {
+                       handled = 1;
+                       handle_ser_rx_interrupt(info);
+               }
+               info += 1;
+               data_mask <<= 2;
+       }
+       /* Handle tx interrupts with interrupts enabled so we
+        * can take care of new data interrupts while transmitting
+        * We protect the tx part with the tx_started flag.
+        * We disable the tr_ready interrupts we are about to handle and
+        * unblock the serial interrupt so new serial interrupts may come.
+        *
+        * If we get a new interrupt:
+        *  - it migth be due to synchronous serial ports.
+        *  - serial irq will be blocked by general irq handler.
+        *  - async data will be handled above (sync will be ignored).
+        *  - tx_started flag will prevent us from trying to send again and
+        *    we will exit fast - no need to unblock serial irq.
+        *  - Next (sync) serial interrupt handler will be runned with
+        *    disabled interrupt due to restore_flags() at end of function,
+        *    so sync handler will not be preempted or reentered.
+        */
+       if (!tx_started) {
+               unsigned long ready_mask;
+               unsigned long
+               tx_started = 1;
+               /* Only the tr_ready interrupts left */
+               irq_mask1_rd &= (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
+                                IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
+                                IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
+                                IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
+               while (irq_mask1_rd) {
+                       /* Disable those we are about to handle */
+                       *R_IRQ_MASK1_CLR = irq_mask1_rd;
+                       /* Unblock the serial interrupt */
+                       *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set);
+
+                       sti();
+                       ready_mask = (1 << (8+1+2*0)); /* ser0 tr_ready */
+                       info = rs_table;
+                       for (i = 0; i < NR_PORTS; i++) {
+                               /* Which line caused the ready irq? */
+                               if (irq_mask1_rd & ready_mask) {
+                                       handled = 1;
+                                       handle_ser_tx_interrupt(info);
+                               }
+                               info += 1;
+                               ready_mask <<= 2;
+                       }
+                       /* handle_ser_tx_interrupt enables tr_ready interrupts */
+                       cli();
+                       /* Handle reentered TX interrupt */
+                       irq_mask1_rd = reentered_ready_mask;
+               }
+               cli();
+               tx_started = 0;
+       } else {
+               unsigned long ready_mask;
+               ready_mask = irq_mask1_rd & (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
+                                            IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
+                                            IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
+                                            IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
+               if (ready_mask) {
+                       reentered_ready_mask |= ready_mask;
+                       /* Disable those we are about to handle */
+                       *R_IRQ_MASK1_CLR = ready_mask;
+                       DFLOW(DEBUG_LOG(SERIAL_DEBUG_LINE, "ser_int reentered with TX %X\n", ready_mask));
+               }
+       }
+
+       restore_flags(flags);
+       return IRQ_RETVAL(handled);
+} /* ser_interrupt */
+#endif
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial 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_softint(void *private_)
+{
+       struct e100_serial      *info = (struct e100_serial *) private_;
+       struct tty_struct       *tty;
+
+       tty = info->tty;
+       if (!tty)
+               return;
+
+       if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+               if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                   tty->ldisc.write_wakeup)
+                       (tty->ldisc.write_wakeup)(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+}
+
+static int
+startup(struct e100_serial * info)
+{
+       unsigned long flags;
+       unsigned long xmit_page;
+       int i;
+
+       xmit_page = get_zeroed_page(GFP_KERNEL);
+       if (!xmit_page)
+               return -ENOMEM;
+
+       save_flags(flags);
+       cli();
+
+       /* if it was already initialized, skip this */
+
+       if (info->flags & ASYNC_INITIALIZED) {
+               restore_flags(flags);
+               free_page(xmit_page);
+               return 0;
+       }
+
+       if (info->xmit.buf)
+               free_page(xmit_page);
+       else
+               info->xmit.buf = (unsigned char *) xmit_page;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("starting up ttyS%d (xmit_buf 0x%p)...\n", info->line, info->xmit.buf);
+#endif
+
+#ifdef CONFIG_SVINTO_SIM
+       /* Bits and pieces collected from below.  Better to have them
+          in one ifdef:ed clause than to mix in a lot of ifdefs,
+          right? */
+       if (info->tty)
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->xmit.head = info->xmit.tail = 0;
+       info->first_recv_buffer = info->last_recv_buffer = NULL;
+       info->recv_cnt = info->max_recv_cnt = 0;
+
+       for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+               info->rec_descr[i].buf = NULL;
+
+       /* No real action in the simulator, but may set info important
+          to ioctl. */
+       change_speed(info);
+#else
+
+       /*
+        * Clear the FIFO buffers and disable them
+        * (they will be reenabled in change_speed())
+        */
+
+       /*
+        * Reset the DMA channels and make sure their interrupts are cleared
+        */
+
+       if (info->dma_in_enabled) {
+               info->uses_dma_in = 1;
+               e100_enable_rxdma_channel(info);
+
+               *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+
+               /* Wait until reset cycle is complete */
+               while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
+                      IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+
+               /* Make sure the irqs are cleared */
+               *info->iclrintradr =
+                       IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+                       IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+       } else {
+               e100_disable_rxdma_channel(info);
+       }
+
+       if (info->dma_out_enabled) {
+               info->uses_dma_out = 1;
+               e100_enable_txdma_channel(info);
+               *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+
+               while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
+                      IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+
+               /* Make sure the irqs are cleared */
+               *info->oclrintradr =
+                       IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+                       IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+       } else {
+               e100_disable_txdma_channel(info);
+       }
+
+       if (info->tty)
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->xmit.head = info->xmit.tail = 0;
+       info->first_recv_buffer = info->last_recv_buffer = NULL;
+       info->recv_cnt = info->max_recv_cnt = 0;
+
+       for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+               info->rec_descr[i].buf = 0;
+
+       /*
+        * and set the speed and other flags of the serial port
+        * this will start the rx/tx as well
+        */
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+       e100_enable_serial_data_irq(info);
+#endif
+       change_speed(info);
+
+       /* dummy read to reset any serial errors */
+
+       (void)info->port[REG_DATA];
+
+       /* enable the interrupts */
+       if (info->uses_dma_out)
+               e100_enable_txdma_irq(info);
+
+       e100_enable_rx_irq(info);
+
+       info->tr_running = 0; /* to be sure we don't lock up the transmitter */
+
+       /* setup the dma input descriptor and start dma */
+
+       start_receive(info);
+
+       /* for safety, make sure the descriptors last result is 0 bytes written */
+
+       info->tr_descr.sw_len = 0;
+       info->tr_descr.hw_len = 0;
+       info->tr_descr.status = 0;
+
+       /* enable RTS/DTR last */
+
+       e100_rts(info, 1);
+       e100_dtr(info, 1);
+
+#endif /* CONFIG_SVINTO_SIM */
+
+       info->flags |= ASYNC_INITIALIZED;
+
+       restore_flags(flags);
+       return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+shutdown(struct e100_serial * info)
+{
+       unsigned long flags;
+       struct etrax_dma_descr *descr = info->rec_descr;
+       struct etrax_recv_buffer *buffer;
+       int i;
+
+#ifndef CONFIG_SVINTO_SIM
+       /* shut down the transmitter and receiver */
+       DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line));
+       e100_disable_rx(info);
+       info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
+
+       /* disable interrupts, reset dma channels */
+       if (info->uses_dma_in) {
+               e100_disable_rxdma_irq(info);
+               *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+               info->uses_dma_in = 0;
+       } else {
+               e100_disable_serial_data_irq(info);
+       }
+
+       if (info->uses_dma_out) {
+               e100_disable_txdma_irq(info);
+               info->tr_running = 0;
+               *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+               info->uses_dma_out = 0;
+       } else {
+               e100_disable_serial_tx_ready_irq(info);
+               info->tr_running = 0;
+       }
+
+#endif /* CONFIG_SVINTO_SIM */
+
+       if (!(info->flags & ASYNC_INITIALIZED))
+               return;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("Shutting down serial port %d (irq %d)....\n", info->line,
+              info->irq);
+#endif
+
+       save_flags(flags);
+       cli(); /* Disable interrupts */
+
+       if (info->xmit.buf) {
+               free_page((unsigned long)info->xmit.buf);
+               info->xmit.buf = NULL;
+       }
+
+       for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+               if (descr[i].buf) {
+                       buffer = phys_to_virt(descr[i].buf) - sizeof *buffer;
+                       kfree(buffer);
+                       descr[i].buf = 0;
+               }
+
+       if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+               /* hang up DTR and RTS if HUPCL is enabled */
+               e100_dtr(info, 0);
+               e100_rts(info, 0); /* could check CRTSCTS before doing this */
+       }
+
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->flags &= ~ASYNC_INITIALIZED;
+       restore_flags(flags);
+}
+
+
+/* change baud rate and other assorted parameters */
+
+static void
+change_speed(struct e100_serial *info)
+{
+       unsigned int cflag;
+       unsigned long xoff;
+       unsigned long flags;
+       /* first some safety checks */
+
+       if (!info->tty || !info->tty->termios)
+               return;
+       if (!info->port)
+               return;
+
+       cflag = info->tty->termios->c_cflag;
+
+       /* possibly, the tx/rx should be disabled first to do this safely */
+
+       /* change baud-rate and write it to the hardware */
+       if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
+               /* Special baudrate */
+               u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
+               unsigned long alt_source =
+                               IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
+                               IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
+               /* R_ALT_SER_BAUDRATE selects the source */
+               DBAUD(printk("Custom baudrate: baud_base/divisor %lu/%i\n",
+                      (unsigned long)info->baud_base, info->custom_divisor));
+               if (info->baud_base == SERIAL_PRESCALE_BASE) {
+                       /* 0, 2-65535 (0=65536) */
+                       u16 divisor = info->custom_divisor;
+                       /* R_SERIAL_PRESCALE (upper 16 bits of R_CLOCK_PRESCALE) */
+                       /* baudrate is 3.125MHz/custom_divisor */
+                       alt_source =
+                               IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, prescale) |
+                               IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, prescale);
+                       alt_source = 0x11;
+                       DBAUD(printk("Writing SERIAL_PRESCALE: divisor %i\n", divisor));
+                       *R_SERIAL_PRESCALE = divisor;
+                       info->baud = SERIAL_PRESCALE_BASE/divisor;
+               }
+#ifdef CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+               else if ((info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8 &&
+                         info->custom_divisor == 1) ||
+                        (info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ &&
+                         info->custom_divisor == 8)) {
+                               /* ext_clk selected */
+                               alt_source =
+                                       IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, extern) |
+                                       IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, extern);
+                               DBAUD(printk("using external baudrate: %lu\n", CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8));
+                               info->baud = CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8;
+                       }
+               }
+#endif
+               else
+               {
+                       /* Bad baudbase, we don't support using timer0
+                        * for baudrate.
+                        */
+                       printk(KERN_WARNING "Bad baud_base/custom_divisor: %lu/%i\n",
+                              (unsigned long)info->baud_base, info->custom_divisor);
+               }
+               r_alt_ser_baudrate_shadow &= ~mask;
+               r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
+               *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
+       } else {
+               /* Normal baudrate */
+               /* Make sure we use normal baudrate */
+               u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
+               unsigned long alt_source =
+                       IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
+                       IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
+               r_alt_ser_baudrate_shadow &= ~mask;
+               r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
+#ifndef CONFIG_SVINTO_SIM
+               *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
+#endif /* CONFIG_SVINTO_SIM */
+
+               info->baud = cflag_to_baud(cflag);
+#ifndef CONFIG_SVINTO_SIM
+               info->port[REG_BAUD] = cflag_to_etrax_baud(cflag);
+#endif /* CONFIG_SVINTO_SIM */
+       }
+
+#ifndef CONFIG_SVINTO_SIM
+       /* start with default settings and then fill in changes */
+       save_flags(flags);
+       cli();
+       /* 8 bit, no/even parity */
+       info->rx_ctrl &= ~(IO_MASK(R_SERIAL0_REC_CTRL, rec_bitnr) |
+                          IO_MASK(R_SERIAL0_REC_CTRL, rec_par_en) |
+                          IO_MASK(R_SERIAL0_REC_CTRL, rec_par));
+
+       /* 8 bit, no/even parity, 1 stop bit, no cts */
+       info->tx_ctrl &= ~(IO_MASK(R_SERIAL0_TR_CTRL, tr_bitnr) |
+                          IO_MASK(R_SERIAL0_TR_CTRL, tr_par_en) |
+                          IO_MASK(R_SERIAL0_TR_CTRL, tr_par) |
+                          IO_MASK(R_SERIAL0_TR_CTRL, stop_bits) |
+                          IO_MASK(R_SERIAL0_TR_CTRL, auto_cts));
+
+       if ((cflag & CSIZE) == CS7) {
+               /* set 7 bit mode */
+               info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_7bit);
+               info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_7bit);
+       }
+
+       if (cflag & CSTOPB) {
+               /* set 2 stop bit mode */
+               info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, stop_bits, two_bits);
+       }
+
+       if (cflag & PARENB) {
+               /* enable parity */
+               info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable);
+               info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
+       }
+
+       if (cflag & CMSPAR) {
+               /* enable stick parity, PARODD mean Mark which matches ETRAX */
+               info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, stick);
+               info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, stick);
+       }
+       if (cflag & PARODD) {
+               /* set odd parity (or Mark if CMSPAR) */
+               info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
+               info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+       }
+
+       if (cflag & CRTSCTS) {
+               /* enable automatic CTS handling */
+               DFLOW(DEBUG_LOG(info->line, "FLOW auto_cts enabled\n", 0));
+               info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, active);
+       }
+
+       /* make sure the tx and rx are enabled */
+
+       info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_enable, enable);
+       info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
+
+       /* actually write the control regs to the hardware */
+
+       info->port[REG_TR_CTRL] = info->tx_ctrl;
+       info->port[REG_REC_CTRL] = info->rx_ctrl;
+       xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->tty));
+       xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
+       if (info->tty->termios->c_iflag & IXON ) {
+               DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n", STOP_CHAR(info->tty)));
+               xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
+       }
+
+       *((unsigned long *)&info->port[REG_XOFF]) = xoff;
+       restore_flags(flags);
+#endif /* !CONFIG_SVINTO_SIM */
+
+       update_char_time(info);
+
+} /* change_speed */
+
+/* start transmitting chars NOW */
+
+static void
+rs_flush_chars(struct tty_struct *tty)
+{
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+       unsigned long flags;
+
+       if (info->tr_running ||
+           info->xmit.head == info->xmit.tail ||
+           tty->stopped ||
+           tty->hw_stopped ||
+           !info->xmit.buf)
+               return;
+
+#ifdef SERIAL_DEBUG_FLOW
+       printk("rs_flush_chars\n");
+#endif
+
+       /* this protection might not exactly be necessary here */
+
+       save_flags(flags);
+       cli();
+       start_transmit(info);
+       restore_flags(flags);
+}
+
+extern _INLINE_ int
+rs_raw_write(struct tty_struct * tty, int from_user,
+         const unsigned char *buf, int count)
+{
+       int     c, ret = 0;
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+       unsigned long flags;
+
+       /* first some sanity checks */
+
+       if (!tty || !info->xmit.buf || !tmp_buf)
+               return 0;
+
+#ifdef SERIAL_DEBUG_DATA
+       if (info->line == SERIAL_DEBUG_LINE)
+               printk("rs_raw_write (%d), status %d\n",
+                      count, info->port[REG_STATUS]);
+#endif
+
+#ifdef CONFIG_SVINTO_SIM
+       /* Really simple.  The output is here and now. */
+       SIMCOUT(buf, count);
+       return count;
+#endif
+       save_flags(flags);
+       DFLOW(DEBUG_LOG(info->line, "write count %i ", count));
+       DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty)));
+
+
+       /* the cli/restore_flags pairs below are needed because the
+        * DMA interrupt handler moves the info->xmit values. the memcpy
+        * needs to be in the critical region unfortunately, because we
+        * need to read xmit values, memcpy, write xmit values in one
+        * atomic operation... this could perhaps be avoided by more clever
+        * design.
+        */
+       if (from_user) {
+               down(&tmp_buf_sem);
+               while (1) {
+                       int c1;
+                       c = CIRC_SPACE_TO_END(info->xmit.head,
+                                             info->xmit.tail,
+                                             SERIAL_XMIT_SIZE);
+                       if (count < c)
+                               c = count;
+                       if (c <= 0)
+                               break;
+
+                       c -= copy_from_user(tmp_buf, buf, c);
+                       if (!c) {
+                               if (!ret)
+                                       ret = -EFAULT;
+                               break;
+                       }
+                       cli();
+                       c1 = CIRC_SPACE_TO_END(info->xmit.head,
+                                              info->xmit.tail,
+                                              SERIAL_XMIT_SIZE);
+                       if (c1 < c)
+                               c = c1;
+                       memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
+                       info->xmit.head = ((info->xmit.head + c) &
+                                          (SERIAL_XMIT_SIZE-1));
+                       restore_flags(flags);
+                       buf += c;
+                       count -= c;
+                       ret += c;
+               }
+               up(&tmp_buf_sem);
+       } else {
+               cli();
+               while (count) {
+                       c = CIRC_SPACE_TO_END(info->xmit.head,
+                                             info->xmit.tail,
+                                             SERIAL_XMIT_SIZE);
+
+                       if (count < c)
+                               c = count;
+                       if (c <= 0)
+                               break;
+
+                       memcpy(info->xmit.buf + info->xmit.head, buf, c);
+                       info->xmit.head = (info->xmit.head + c) &
+                               (SERIAL_XMIT_SIZE-1);
+                       buf += c;
+                       count -= c;
+                       ret += c;
+               }
+               restore_flags(flags);
+       }
+
+       /* enable transmitter if not running, unless the tty is stopped
+        * this does not need IRQ protection since if tr_running == 0
+        * the IRQ's are not running anyway for this port.
+        */
+       DFLOW(DEBUG_LOG(info->line, "write ret %i\n", ret));
+
+       if (info->xmit.head != info->xmit.tail &&
+           !tty->stopped &&
+           !tty->hw_stopped &&
+           !info->tr_running) {
+               start_transmit(info);
+       }
+
+       return ret;
+} /* raw_raw_write() */
+
+static int
+rs_write(struct tty_struct * tty, int from_user,
+        const unsigned char *buf, int count)
+{
+#if defined(CONFIG_ETRAX_RS485)
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+       if (info->rs485.enabled)
+       {
+               /* If we are in RS-485 mode, we need to toggle RTS and disable
+                * the receiver before initiating a DMA transfer
+                */
+#ifdef CONFIG_ETRAX_FAST_TIMER
+               /* Abort any started timer */
+               fast_timers_rs485[info->line].function = NULL;
+               del_fast_timer(&fast_timers_rs485[info->line]);
+#endif
+               e100_rts(info, info->rs485.rts_on_send);
+#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
+               e100_disable_rx(info);
+               e100_enable_rx_irq(info);
+#endif
+
+               if (info->rs485.delay_rts_before_send > 0) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout((info->rs485.delay_rts_before_send * HZ)/1000);
+               }
+       }
+#endif /* CONFIG_ETRAX_RS485 */
+
+       count = rs_raw_write(tty, from_user, buf, count);
+
+#if defined(CONFIG_ETRAX_RS485)
+       if (info->rs485.enabled)
+       {
+               unsigned int val;
+               /* If we are in RS-485 mode the following has to be done:
+                * wait until DMA is ready
+                * wait on transmit shift register
+                * toggle RTS
+                * enable the receiver
+                */
+
+               /* Sleep until all sent */
+               tty_wait_until_sent(tty, 0);
+#ifdef CONFIG_ETRAX_FAST_TIMER
+               /* Now sleep a little more so that shift register is empty */
+               schedule_usleep(info->char_time_usec * 2);
+#endif
+               /* wait on transmit shift register */
+               do{
+                       get_lsr_info(info, &val);
+               }while (!(val & TIOCSER_TEMT));
+
+               e100_rts(info, info->rs485.rts_after_sent);
+
+#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
+               e100_enable_rx(info);
+               e100_enable_rxdma_irq(info);
+#endif
+       }
+#endif /* CONFIG_ETRAX_RS485 */
+
+       return count;
+} /* rs_write */
+
+
+/* how much space is available in the xmit buffer? */
+
+static int
+rs_write_room(struct tty_struct *tty)
+{
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+       return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+/* How many chars are in the xmit buffer?
+ * This does not include any chars in the transmitter FIFO.
+ * Use wait_until_sent for waiting for FIFO drain.
+ */
+
+static int
+rs_chars_in_buffer(struct tty_struct *tty)
+{
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+       return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+/* discard everything in the xmit buffer */
+
+static void
+rs_flush_buffer(struct tty_struct *tty)
+{
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       info->xmit.head = info->xmit.tail = 0;
+       restore_flags(flags);
+
+       wake_up_interruptible(&tty->write_wait);
+
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ *
+ * Since we use DMA we don't check for info->x_char in transmit_chars_dma(),
+ * but we do it in handle_ser_tx_interrupt().
+ * We disable DMA channel and enable tx ready interrupt and write the
+ * character when possible.
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+       unsigned long flags;
+       save_flags(flags); cli();
+       if (info->uses_dma_out) {
+               /* Put the DMA on hold and disable the channel */
+               *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, hold);
+               while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) !=
+                      IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, hold));
+               e100_disable_txdma_channel(info);
+       }
+
+       /* Must make sure transmitter is not stopped before we can transmit */
+       if (tty->stopped)
+               rs_start(tty);
+
+       /* Enable manual transmit interrupt and send from there */
+       DFLOW(DEBUG_LOG(info->line, "rs_send_xchar 0x%02X\n", ch));
+       info->x_char = ch;
+       e100_enable_serial_tx_ready_irq(info);
+       restore_flags(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void
+rs_throttle(struct tty_struct * tty)
+{
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+       char    buf[64];
+
+       printk("throttle %s: %lu....\n", tty_name(tty, buf),
+              (unsigned long)tty->ldisc.chars_in_buffer(tty));
+#endif
+       DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty)));
+
+       /* Do RTS before XOFF since XOFF might take some time */
+       if (tty->termios->c_cflag & CRTSCTS) {
+               /* Turn off RTS line */
+               e100_rts(info, 0);
+       }
+       if (I_IXOFF(tty))
+               rs_send_xchar(tty, STOP_CHAR(tty));
+
+}
+
+static void
+rs_unthrottle(struct tty_struct * tty)
+{
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+       char    buf[64];
+
+       printk("unthrottle %s: %lu....\n", tty_name(tty, buf),
+              (unsigned long)tty->ldisc.chars_in_buffer(tty));
+#endif
+       DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty)));
+       DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count));
+       /* Do RTS before XOFF since XOFF might take some time */
+       if (tty->termios->c_cflag & CRTSCTS) {
+               /* Assert RTS line  */
+               e100_rts(info, 1);
+       }
+
+       if (I_IXOFF(tty)) {
+               if (info->x_char)
+                       info->x_char = 0;
+               else
+                       rs_send_xchar(tty, START_CHAR(tty));
+       }
+
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int
+get_serial_info(struct e100_serial * info,
+               struct serial_struct * retinfo)
+{
+       struct serial_struct tmp;
+
+       /* this is all probably wrong, there are a lot of fields
+        * here that we don't have in e100_serial and maybe we
+        * should set them to something else than 0.
+        */
+
+       if (!retinfo)
+               return -EFAULT;
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.type = info->type;
+       tmp.line = info->line;
+       tmp.port = (int)info->port;
+       tmp.irq = info->irq;
+       tmp.flags = info->flags;
+       tmp.baud_base = info->baud_base;
+       tmp.close_delay = info->close_delay;
+       tmp.closing_wait = info->closing_wait;
+       tmp.custom_divisor = info->custom_divisor;
+       if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+               return -EFAULT;
+       return 0;
+}
+
+static int
+set_serial_info(struct e100_serial *info,
+               struct serial_struct *new_info)
+{
+       struct serial_struct new_serial;
+       struct e100_serial old_info;
+       int retval = 0;
+
+       if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+               return -EFAULT;
+
+       old_info = *info;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((new_serial.type != info->type) ||
+                   (new_serial.close_delay != info->close_delay) ||
+                   ((new_serial.flags & ~ASYNC_USR_MASK) !=
+                    (info->flags & ~ASYNC_USR_MASK)))
+                       return -EPERM;
+               info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+                              (new_serial.flags & ASYNC_USR_MASK));
+               goto check_and_exit;
+       }
+
+       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->flags = ((info->flags & ~ASYNC_FLAGS) |
+                      (new_serial.flags & ASYNC_FLAGS));
+       info->custom_divisor = new_serial.custom_divisor;
+       info->type = new_serial.type;
+       info->close_delay = new_serial.close_delay;
+       info->closing_wait = new_serial.closing_wait;
+       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+ check_and_exit:
+       if (info->flags & ASYNC_INITIALIZED) {
+               change_speed(info);
+       } else
+               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 e100_serial * info, unsigned int *value)
+{
+       unsigned int result = TIOCSER_TEMT;
+#ifndef CONFIG_SVINTO_SIM
+       unsigned long curr_time = jiffies;
+       unsigned long curr_time_usec = GET_JIFFIES_USEC();
+       unsigned long elapsed_usec =
+               (curr_time - info->last_tx_active) * 1000000/HZ +
+               curr_time_usec - info->last_tx_active_usec;
+
+       if (info->xmit.head != info->xmit.tail ||
+           elapsed_usec < 2*info->char_time_usec) {
+               result = 0;
+       }
+#endif
+
+       if (copy_to_user(value, &result, sizeof(int)))
+               return -EFAULT;
+       return 0;
+}
+
+#ifdef SERIAL_DEBUG_IO
+struct state_str
+{
+       int state;
+       const char *str;
+};
+
+const struct state_str control_state_str[] = {
+       {TIOCM_DTR, "DTR" },
+       {TIOCM_RTS, "RTS"},
+       {TIOCM_ST, "ST?" },
+       {TIOCM_SR, "SR?" },
+       {TIOCM_CTS, "CTS" },
+       {TIOCM_CD, "CD" },
+       {TIOCM_RI, "RI" },
+       {TIOCM_DSR, "DSR" },
+       {0, NULL }
+};
+
+char *get_control_state_str(int MLines, char *s)
+{
+       int i = 0;
+
+       s[0]='\0';
+       while (control_state_str[i].str != NULL) {
+               if (MLines & control_state_str[i].state) {
+                       if (s[0] != '\0') {
+                               strcat(s, ", ");
+                       }
+                       strcat(s, control_state_str[i].str);
+               }
+               i++;
+       }
+       return s;
+}
+#endif
+
+static int
+get_modem_info(struct e100_serial * info, unsigned int *value)
+{
+       unsigned int result;
+       /* Polarity isn't verified */
+#if 0 /*def SERIAL_DEBUG_IO  */
+
+       printk("get_modem_info: RTS: %i DTR: %i CD: %i RI: %i DSR: %i CTS: %i\n",
+              E100_RTS_GET(info),
+              E100_DTR_GET(info),
+              E100_CD_GET(info),
+              E100_RI_GET(info),
+              E100_DSR_GET(info),
+              E100_CTS_GET(info));
+#endif
+
+       result =
+               (!E100_RTS_GET(info) ? TIOCM_RTS : 0)
+               | (!E100_DTR_GET(info) ? TIOCM_DTR : 0)
+               | (!E100_RI_GET(info) ? TIOCM_RNG : 0)
+               | (!E100_DSR_GET(info) ? TIOCM_DSR : 0)
+               | (!E100_CD_GET(info) ? TIOCM_CAR : 0)
+               | (!E100_CTS_GET(info) ? TIOCM_CTS : 0);
+
+#ifdef SERIAL_DEBUG_IO
+       printk("e100ser: modem state: %i 0x%08X\n", result, result);
+       {
+               char s[100];
+
+               get_control_state_str(result, s);
+               printk("state: %s\n", s);
+       }
+#endif
+       if (copy_to_user(value, &result, sizeof(int)))
+               return -EFAULT;
+       return 0;
+}
+
+
+static int
+set_modem_info(struct e100_serial * info, unsigned int cmd,
+              unsigned int *value)
+{
+       unsigned int arg;
+
+       if (copy_from_user(&arg, value, sizeof(int)))
+               return -EFAULT;
+
+       switch (cmd) {
+       case TIOCMBIS:
+               if (arg & TIOCM_RTS) {
+                       e100_rts(info, 1);
+               }
+               if (arg & TIOCM_DTR) {
+                       e100_dtr(info, 1);
+               }
+               /* Handle FEMALE behaviour */
+               if (arg & TIOCM_RI) {
+                       e100_ri_out(info, 1);
+               }
+               if (arg & TIOCM_CD) {
+                       e100_cd_out(info, 1);
+               }
+               break;
+       case TIOCMBIC:
+               if (arg & TIOCM_RTS) {
+                       e100_rts(info, 0);
+               }
+               if (arg & TIOCM_DTR) {
+                       e100_dtr(info, 0);
+               }
+               /* Handle FEMALE behaviour */
+               if (arg & TIOCM_RI) {
+                       e100_ri_out(info, 0);
+               }
+               if (arg & TIOCM_CD) {
+                       e100_cd_out(info, 0);
+               }
+               break;
+       case TIOCMSET:
+               e100_rts(info, arg & TIOCM_RTS);
+               e100_dtr(info, arg & TIOCM_DTR);
+               /* Handle FEMALE behaviour */
+               e100_ri_out(info, arg & TIOCM_RI);
+               e100_cd_out(info, arg & TIOCM_CD);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+
+static void
+rs_break(struct tty_struct *tty, int break_state)
+{
+       struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+       unsigned long flags;
+
+       if (!info->port)
+               return;
+
+       save_flags(flags);
+       cli();
+       if (break_state == -1) {
+               /* Go to manual mode and set the txd pin to 0 */
+               info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */
+       } else {
+               info->tx_ctrl |= (0x80 | 0x40); /* Set bit 7 (txd) and 6 (tr_enable) */
+       }
+       info->port[REG_TR_CTRL] = info->tx_ctrl;
+       restore_flags(flags);
+}
+
+static int
+rs_ioctl(struct tty_struct *tty, struct file * file,
+        unsigned int cmd, unsigned long arg)
+{
+       struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+
+       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 TIOCMGET:
+                       return get_modem_info(info, (unsigned int *) arg);
+               case TIOCMBIS:
+               case TIOCMBIC:
+               case TIOCMSET:
+                       return set_modem_info(info, cmd, (unsigned int *) arg);
+               case TIOCGSERIAL:
+                       return get_serial_info(info,
+                                              (struct serial_struct *) arg);
+               case TIOCSSERIAL:
+                       return set_serial_info(info,
+                                              (struct serial_struct *) arg);
+               case TIOCSERGETLSR: /* Get line status register */
+                       return get_lsr_info(info, (unsigned int *) arg);
+
+               case TIOCSERGSTRUCT:
+                       if (copy_to_user((struct e100_serial *) arg,
+                                        info, sizeof(struct e100_serial)))
+                               return -EFAULT;
+                       return 0;
+
+#if defined(CONFIG_ETRAX_RS485)
+               case TIOCSERSETRS485:
+               {
+                       struct rs485_control rs485ctrl;
+                       if (copy_from_user(&rs485ctrl, (struct rs485_control*)arg, sizeof(rs485ctrl)))
+                               return -EFAULT;
+
+                       return e100_enable_rs485(tty, &rs485ctrl);
+               }
+
+               case TIOCSERWRRS485:
+               {
+                       struct rs485_write rs485wr;
+                       if (copy_from_user(&rs485wr, (struct rs485_write*)arg, sizeof(rs485wr)))
+                               return -EFAULT;
+
+                       return e100_write_rs485(tty, 1, rs485wr.outc, rs485wr.outc_size);
+               }
+#endif
+
+               default:
+                       return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+static void
+rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+       if (tty->termios->c_cflag == old_termios->c_cflag &&
+           tty->termios->c_iflag == old_termios->c_iflag)
+               return;
+
+       change_speed(info);
+
+       /* Handle turning off CRTSCTS */
+       if ((old_termios->c_cflag & CRTSCTS) &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               rs_start(tty);
+       }
+
+}
+
+/* In debugport.c - register a console write function that uses the normal
+ * serial driver
+ */
+typedef int (*debugport_write_function)(int i, const char *buf, unsigned int len);
+
+extern debugport_write_function debug_write_function;
+
+static int rs_debug_write_function(int i, const char *buf, unsigned int len)
+{
+       int cnt;
+       int written = 0;
+        struct tty_struct *tty;
+        static int recurse_cnt = 0;
+
+        tty = rs_table[i].tty;
+        if (tty)  {
+               unsigned long flags;
+               if (recurse_cnt > 5) /* We skip this debug output */
+                       return 1;
+
+               local_irq_save(flags);
+               recurse_cnt++;
+               local_irq_restore(flags);
+                do {
+                        cnt = rs_write(tty, 0, buf + written, len);
+                        if (cnt >= 0) {
+                               written += cnt;
+                                buf += cnt;
+                                len -= cnt;
+                        } else
+                                len = cnt;
+                } while(len > 0);
+               local_irq_save(flags);
+               recurse_cnt--;
+               local_irq_restore(flags);
+                return 1;
+        }
+        return 0;
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * S structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void
+rs_close(struct tty_struct *tty, struct file * filp)
+{
+       struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+       unsigned long flags;
+
+       if (!info)
+               return;
+
+       /* interrupts are disabled for this entire function */
+
+       save_flags(flags);
+       cli();
+
+       if (tty_hung_up_p(filp)) {
+               restore_flags(flags);
+               return;
+       }
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("[%d] rs_close ttyS%d, count = %d\n", current->pid,
+              info->line, info->count);
+#endif
+       if ((tty->count == 1) && (info->count != 1)) {
+               /*
+                * Uh, oh.  tty->count is 1, which means that the tty
+                * structure will be freed.  Info->count should always
+                * be one in these conditions.  If it's greater than
+                * one, we've got real problems, since it means the
+                * serial port won't be shutdown.
+                */
+               printk(KERN_CRIT
+                      "rs_close: bad serial port count; tty->count is 1, "
+                      "info->count is %d\n", info->count);
+               info->count = 1;
+       }
+       if (--info->count < 0) {
+               printk(KERN_CRIT "rs_close: bad serial port count for ttyS%d: %d\n",
+                      info->line, info->count);
+               info->count = 0;
+       }
+       if (info->count) {
+               restore_flags(flags);
+               return;
+       }
+       info->flags |= ASYNC_CLOSING;
+       /*
+        * Save the termios structure, since this port may have
+        * separate termios for callout and dialin.
+        */
+       if (info->flags & ASYNC_NORMAL_ACTIVE)
+               info->normal_termios = *tty->termios;
+       /*
+        * Now we wait for the transmit buffer to clear; and we notify
+        * the line discipline to only process XON/XOFF characters.
+        */
+       tty->closing = 1;
+       if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+               tty_wait_until_sent(tty, info->closing_wait);
+       /*
+        * At this point we stop accepting input.  To do this, we
+        * disable the serial receiver and the DMA receive interrupt.
+        */
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+       e100_disable_serial_data_irq(info);
+#endif
+
+#ifndef CONFIG_SVINTO_SIM
+       e100_disable_rx(info);
+       e100_disable_rx_irq(info);
+
+       if (info->flags & ASYNC_INITIALIZED) {
+               /*
+                * Before we drop DTR, make sure the UART transmitter
+                * has completely drained; this is especially
+                * important as we have a transmit FIFO!
+                */
+               rs_wait_until_sent(tty, HZ);
+       }
+#endif
+
+       shutdown(info);
+       if (tty->driver->flush_buffer)
+               tty->driver->flush_buffer(tty);
+       if (tty->ldisc.flush_buffer)
+               tty->ldisc.flush_buffer(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = 0;
+       if (info->blocked_open) {
+               if (info->close_delay) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(info->close_delay);
+               }
+               wake_up_interruptible(&info->open_wait);
+       }
+       info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
+       wake_up_interruptible(&info->close_wait);
+       restore_flags(flags);
+
+       /* port closed */
+
+#if defined(CONFIG_ETRAX_RS485)
+       if (info->rs485.enabled) {
+               info->rs485.enabled = 0;
+#if defined(CONFIG_ETRAX_RS485_ON_PA)
+               *R_PORT_PA_DATA = port_pa_data_shadow &= ~(1 << rs485_pa_bit);
+#endif
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+               REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+                              rs485_port_g_bit, 0);
+#endif
+#if defined(CONFIG_ETRAX_RS485_LTC1387)
+               REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+                              CONFIG_ETRAX_RS485_LTC1387_DXEN_PORT_G_BIT, 0);
+               REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+                              CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 0);
+#endif
+       }
+#endif
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       unsigned long orig_jiffies;
+       struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+       unsigned long curr_time = jiffies;
+       unsigned long curr_time_usec = GET_JIFFIES_USEC();
+       long elapsed_usec =
+               (curr_time - info->last_tx_active) * (1000000/HZ) +
+               curr_time_usec - info->last_tx_active_usec;
+
+       /*
+        * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO
+        * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k)
+        */
+       orig_jiffies = jiffies;
+       while (info->xmit.head != info->xmit.tail || /* More in send queue */
+              (*info->ostatusadr & 0x007f) ||  /* more in FIFO */
+              (elapsed_usec < 2*info->char_time_usec)) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(1);
+               if (signal_pending(current))
+                       break;
+               if (timeout && time_after(jiffies, orig_jiffies + timeout))
+                       break;
+               curr_time = jiffies;
+               curr_time_usec = GET_JIFFIES_USEC();
+               elapsed_usec =
+                       (curr_time - info->last_tx_active) * (1000000/HZ) +
+                       curr_time_usec - info->last_tx_active_usec;
+       }
+       set_current_state(TASK_RUNNING);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void
+rs_hangup(struct tty_struct *tty)
+{
+       struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+
+       rs_flush_buffer(tty);
+       shutdown(info);
+       info->event = 0;
+       info->count = 0;
+       info->flags &= ~ASYNC_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 e100_serial *info)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       unsigned long   flags;
+       int             retval;
+       int             do_clocal = 0, extra_count = 0;
+
+       /*
+        * If the device is in the middle of being closed, then block
+        * until it's done, and then try again.
+        */
+       if (tty_hung_up_p(filp) ||
+           (info->flags & ASYNC_CLOSING)) {
+               if (info->flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+               if (info->flags & ASYNC_HUP_NOTIFY)
+                       return -EAGAIN;
+               else
+                       return -ERESTARTSYS;
+#else
+               return -EAGAIN;
+#endif
+       }
+
+       /*
+        * 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 |= ASYNC_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
+        * rs_close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+       retval = 0;
+       add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready before block: ttyS%d, count = %d\n",
+              info->line, info->count);
+#endif
+       save_flags(flags);
+       cli();
+       if (!tty_hung_up_p(filp)) {
+               extra_count++;
+               info->count--;
+       }
+       restore_flags(flags);
+       info->blocked_open++;
+       while (1) {
+               save_flags(flags);
+               cli();
+               /* assert RTS and DTR */
+               e100_rts(info, 1);
+               e100_dtr(info, 1);
+               restore_flags(flags);
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (tty_hung_up_p(filp) ||
+                   !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+                       if (info->flags & ASYNC_HUP_NOTIFY)
+                               retval = -EAGAIN;
+                       else
+                               retval = -ERESTARTSYS;
+#else
+                       retval = -EAGAIN;
+#endif
+                       break;
+               }
+               if (!(info->flags & ASYNC_CLOSING) && do_clocal)
+                       /* && (do_clocal || DCD_IS_ASSERTED) */
+                       break;
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+#ifdef SERIAL_DEBUG_OPEN
+               printk("block_til_ready blocking: ttyS%d, count = %d\n",
+                      info->line, info->count);
+#endif
+               schedule();
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&info->open_wait, &wait);
+       if (extra_count)
+               info->count++;
+       info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready after blocking: ttyS%d, count = %d\n",
+              info->line, info->count);
+#endif
+       if (retval)
+               return retval;
+       info->flags |= ASYNC_NORMAL_ACTIVE;
+       return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.
+ * It performs the serial-specific initialization for the tty structure.
+ */
+static int
+rs_open(struct tty_struct *tty, struct file * filp)
+{
+       struct e100_serial      *info;
+       int                     retval, line;
+       unsigned long           page;
+
+       /* find which port we want to open */
+
+       line = tty->index;
+
+       if (line < 0 || line >= NR_PORTS)
+               return -ENODEV;
+
+       /* find the corresponding e100_serial struct in the table */
+       info = rs_table + line;
+
+       /* don't allow the opening of ports that are not enabled in the HW config */
+       if (!info->enabled)
+               return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+        printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name,
+              info->count);
+#endif
+
+       info->count++;
+       tty->driver_data = info;
+       info->tty = tty;
+
+       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+       if (!tmp_buf) {
+               page = get_zeroed_page(GFP_KERNEL);
+               if (!page) {
+                       return -ENOMEM;
+               }
+               if (tmp_buf)
+                       free_page(page);
+               else
+                       tmp_buf = (unsigned char *) page;
+       }
+
+       /*
+        * If the port is in the middle of closing, bail out now
+        */
+       if (tty_hung_up_p(filp) ||
+           (info->flags & ASYNC_CLOSING)) {
+               if (info->flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+               return ((info->flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+#else
+               return -EAGAIN;
+#endif
+       }
+
+       /*
+        * Start up the serial port
+        */
+
+       retval = startup(info);
+       if (retval)
+               return retval;
+
+       retval = block_til_ready(tty, filp, info);
+       if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+               printk("rs_open returning after block_til_ready with %d\n",
+                      retval);
+#endif
+               return retval;
+       }
+
+       if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+               *tty->termios = info->normal_termios;
+               change_speed(info);
+       }
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("rs_open ttyS%d successful...\n", info->line);
+#endif
+       DLOG_INT_TRIG( log_int_pos = 0);
+
+       DFLIP(  if (info->line == SERIAL_DEBUG_LINE) {
+                       info->icount.rx = 0;
+               } );
+
+       return 0;
+}
+
+/*
+ * /proc fs routines....
+ */
+
+extern _INLINE_ int line_info(char *buf, struct e100_serial *info)
+{
+       char    stat_buf[30];
+       int     ret;
+       unsigned long tmp;
+
+       ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d",
+                     info->line, (unsigned long)info->port, info->irq);
+
+       if (!info->port || (info->type == PORT_UNKNOWN)) {
+               ret += sprintf(buf+ret, "\n");
+               return ret;
+       }
+
+       stat_buf[0] = 0;
+       stat_buf[1] = 0;
+       if (!E100_RTS_GET(info))
+               strcat(stat_buf, "|RTS");
+       if (!E100_CTS_GET(info))
+               strcat(stat_buf, "|CTS");
+       if (!E100_DTR_GET(info))
+               strcat(stat_buf, "|DTR");
+       if (!E100_DSR_GET(info))
+               strcat(stat_buf, "|DSR");
+       if (!E100_CD_GET(info))
+               strcat(stat_buf, "|CD");
+       if (!E100_RI_GET(info))
+               strcat(stat_buf, "|RI");
+
+       ret += sprintf(buf+ret, " baud:%d", info->baud);
+
+       ret += sprintf(buf+ret, " tx:%lu rx:%lu",
+                      (unsigned long)info->icount.tx,
+                      (unsigned long)info->icount.rx);
+       tmp = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+       if (tmp) {
+               ret += sprintf(buf+ret, " tx_pend:%lu/%lu",
+                              (unsigned long)tmp,
+                              (unsigned long)SERIAL_XMIT_SIZE);
+       }
+
+       ret += sprintf(buf+ret, " rx_pend:%lu/%lu",
+                      (unsigned long)info->recv_cnt,
+                      (unsigned long)info->max_recv_cnt);
+
+#if 1
+       if (info->tty) {
+
+               if (info->tty->stopped)
+                       ret += sprintf(buf+ret, " stopped:%i",
+                                      (int)info->tty->stopped);
+               if (info->tty->hw_stopped)
+                       ret += sprintf(buf+ret, " hw_stopped:%i",
+                                      (int)info->tty->hw_stopped);
+       }
+
+       {
+               unsigned char rstat = info->port[REG_STATUS];
+               if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) )
+                       ret += sprintf(buf+ret, " xoff_detect:1");
+       }
+
+#endif
+
+
+
+
+       if (info->icount.frame)
+               ret += sprintf(buf+ret, " fe:%lu",
+                              (unsigned long)info->icount.frame);
+
+       if (info->icount.parity)
+               ret += sprintf(buf+ret, " pe:%lu",
+                              (unsigned long)info->icount.parity);
+
+       if (info->icount.brk)
+               ret += sprintf(buf+ret, " brk:%lu",
+                              (unsigned long)info->icount.brk);
+
+       if (info->icount.overrun)
+               ret += sprintf(buf+ret, " oe:%lu",
+                              (unsigned long)info->icount.overrun);
+
+       /*
+        * Last thing is the RS-232 status lines
+        */
+       ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+       return ret;
+}
+
+int rs_read_proc(char *page, char **start, off_t off, int count,
+                int *eof, void *data)
+{
+       int i, len = 0, l;
+       off_t   begin = 0;
+
+       len += sprintf(page, "serinfo:1.0 driver:%s\n",
+                      serial_version);
+       for (i = 0; i < NR_PORTS && len < 4000; i++) {
+               if (!rs_table[i].enabled)
+                       continue;
+               l = line_info(page + len, &rs_table[i]);
+               len += l;
+               if (len+begin > off+count)
+                       goto done;
+               if (len+begin < off) {
+                       begin += len;
+                       len = 0;
+               }
+       }
+#ifdef DEBUG_LOG_INCLUDED
+       for (i = 0; i < debug_log_pos; i++) {
+               len += sprintf(page + len, "%-4i %lu.%lu ", i, debug_log[i].time, timer_data_to_ns(debug_log[i].timer_data));
+               len += sprintf(page + len, debug_log[i].string, debug_log[i].value);
+               if (len+begin > off+count)
+                       goto done;
+               if (len+begin < off) {
+                       begin += len;
+                       len = 0;
+               }
+       }
+       len += sprintf(page + len, "debug_log %i/%i  %li bytes\n",
+                      i, DEBUG_LOG_SIZE, begin+len);
+       debug_log_pos = 0;
+#endif
+
+       *eof = 1;
+done:
+       if (off >= len+begin)
+               return 0;
+       *start = page + (off-begin);
+       return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void
+show_serial_version(void)
+{
+       printk(KERN_INFO
+              "ETRAX 100LX serial-driver %s, (c) 2000-2004 Axis Communications AB\r\n",
+              &serial_version[11]); /* "$Revision: x.yy" */
+}
+
+/* rs_init inits the driver at boot (using the module_init chain) */
+
+static struct tty_operations rs_ops = {
+       .open = rs_open,
+       .close = rs_close,
+       .write = rs_write,
+       .flush_chars = rs_flush_chars,
+       .write_room = rs_write_room,
+       .chars_in_buffer = rs_chars_in_buffer,
+       .flush_buffer = rs_flush_buffer,
+       .ioctl = rs_ioctl,
+       .throttle = rs_throttle,
+        .unthrottle = rs_unthrottle,
+       .set_termios = rs_set_termios,
+       .stop = rs_stop,
+       .start = rs_start,
+       .hangup = rs_hangup,
+       .break_ctl = rs_break,
+       .send_xchar = rs_send_xchar,
+       .wait_until_sent = rs_wait_until_sent,
+       .read_proc = rs_read_proc,
+};
+
+static int __init
+rs_init(void)
+{
+       int i;
+       struct e100_serial *info;
+       struct tty_driver *driver = alloc_tty_driver(NR_PORTS);
+
+       if (!driver)
+               return -ENOMEM;
+
+       show_serial_version();
+
+       /* Setup the timed flush handler system */
+
+#if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER)
+       init_timer(&flush_timer);
+       flush_timer.function = timed_flush_handler;
+       mod_timer(&flush_timer, jiffies + CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS);
+#endif
+
+       /* Initialize the tty_driver structure */
+
+       driver->driver_name = "serial";
+       driver->name = "ttyS";
+       driver->major = TTY_MAJOR;
+       driver->minor_start = 64;
+       driver->type = TTY_DRIVER_TYPE_SERIAL;
+       driver->subtype = SERIAL_TYPE_NORMAL;
+       driver->init_termios = tty_std_termios;
+       driver->init_termios.c_cflag =
+               B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
+       driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+       driver->termios = serial_termios;
+       driver->termios_locked = serial_termios_locked;
+
+       tty_set_operations(driver, &rs_ops);
+        serial_driver = driver;
+       if (tty_register_driver(driver))
+               panic("Couldn't register serial driver\n");
+       /* do some initializing for the separate ports */
+
+       for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
+               info->uses_dma_in = 0;
+               info->uses_dma_out = 0;
+               info->line = i;
+               info->tty = 0;
+               info->type = PORT_ETRAX;
+               info->tr_running = 0;
+               info->forced_eop = 0;
+               info->baud_base = DEF_BAUD_BASE;
+               info->custom_divisor = 0;
+               info->flags = 0;
+               info->close_delay = 5*HZ/10;
+               info->closing_wait = 30*HZ;
+               info->x_char = 0;
+               info->event = 0;
+               info->count = 0;
+               info->blocked_open = 0;
+               info->normal_termios = driver->init_termios;
+               init_waitqueue_head(&info->open_wait);
+               init_waitqueue_head(&info->close_wait);
+               info->xmit.buf = NULL;
+               info->xmit.tail = info->xmit.head = 0;
+               info->first_recv_buffer = info->last_recv_buffer = NULL;
+               info->recv_cnt = info->max_recv_cnt = 0;
+               info->last_tx_active_usec = 0;
+               info->last_tx_active = 0;
+
+#if defined(CONFIG_ETRAX_RS485)
+               /* Set sane defaults */
+               info->rs485.rts_on_send = 0;
+               info->rs485.rts_after_sent = 1;
+               info->rs485.delay_rts_before_send = 0;
+               info->rs485.enabled = 0;
+#endif
+               INIT_WORK(&info->work, do_softint, info);
+
+               if (info->enabled) {
+                       printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n",
+                              serial_driver->name, info->line, (unsigned int)info->port);
+               }
+       }
+#ifdef CONFIG_ETRAX_FAST_TIMER
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+       memset(fast_timers, 0, sizeof(fast_timers));
+#endif
+#ifdef CONFIG_ETRAX_RS485
+       memset(fast_timers_rs485, 0, sizeof(fast_timers_rs485));
+#endif
+       fast_timer_init();
+#endif
+
+#ifndef CONFIG_SVINTO_SIM
+       /* Not needed in simulator.  May only complicate stuff. */
+       /* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */
+
+       if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", NULL))
+               panic("irq8");
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+       if (request_irq(SER0_DMA_TX_IRQ_NBR, tr_interrupt, SA_INTERRUPT, "serial 0 dma tr", NULL))
+               panic("irq22");
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+       if (request_irq(SER0_DMA_RX_IRQ_NBR, rec_interrupt, SA_INTERRUPT, "serial 0 dma rec", NULL))
+               panic("irq23");
+#endif
+#endif
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
+       if (request_irq(SER1_DMA_TX_IRQ_NBR, tr_interrupt, SA_INTERRUPT, "serial 1 dma tr", NULL))
+               panic("irq24");
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
+       if (request_irq(SER1_DMA_RX_IRQ_NBR, rec_interrupt, SA_INTERRUPT, "serial 1 dma rec", NULL))
+               panic("irq25");
+#endif
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+       /* DMA Shared with par0 (and SCSI0 and ATA) */
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+       if (request_irq(SER2_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 2 dma tr", NULL))
+               panic("irq18");
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+       if (request_irq(SER2_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 2 dma rec", NULL))
+               panic("irq19");
+#endif
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+       /* DMA Shared with par1 (and SCSI1 and Extern DMA 0) */
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+       if (request_irq(SER3_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 3 dma tr", NULL))
+               panic("irq20");
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
+       if (request_irq(SER3_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 3 dma rec", NULL))
+               panic("irq21");
+#endif
+#endif
+
+#ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
+       if (request_irq(TIMER1_IRQ_NBR, timeout_interrupt, SA_SHIRQ | SA_INTERRUPT,
+                      "fast serial dma timeout", NULL)) {
+               printk(KERN_CRIT "err: timer1 irq\n");
+       }
+#endif
+#endif /* CONFIG_SVINTO_SIM */
+       debug_write_function = rs_debug_write_function;
+       return 0;
+}
+
+/* this makes sure that rs_init is called during kernel boot */
+
+module_init(rs_init);
+
+/*
+ * register_serial and unregister_serial allows for serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ */
+int
+register_serial(struct serial_struct *req)
+{
+       return -1;
+}
+
+void unregister_serial(int line)
+{
+}
diff --git a/drivers/serial/crisv10.h b/drivers/serial/crisv10.h
new file mode 100644 (file)
index 0000000..1800c0e
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * serial.h: Arch-dep definitions for the Etrax100 serial driver.
+ *
+ * Copyright (C) 1998, 1999, 2000 Axis Communications AB
+ */
+
+#ifndef _ETRAX_SERIAL_H
+#define _ETRAX_SERIAL_H
+
+#include <linux/config.h>
+#include <linux/circ_buf.h>
+#include <asm/termios.h>
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+#define SERIAL_RECV_DESCRIPTORS 8
+
+struct etrax_recv_buffer {
+       struct etrax_recv_buffer *next;
+       unsigned short length;
+       unsigned char error;
+       unsigned char pad;
+
+       unsigned char buffer[0];
+};
+
+struct e100_serial {
+       int                     baud;
+       volatile u8             *port; /* R_SERIALx_CTRL */
+       u32                     irq;  /* bitnr in R_IRQ_MASK2 for dmaX_descr */
+
+       /* Output registers */
+       volatile u8             *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
+       volatile u32            *ofirstadr;   /* adr to R_DMA_CHx_FIRST */
+       volatile u8             *ocmdadr;     /* adr to R_DMA_CHx_CMD */
+       const volatile u8       *ostatusadr;  /* adr to R_DMA_CHx_STATUS */
+
+       /* Input registers */
+       volatile u8             *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
+       volatile u32            *ifirstadr;   /* adr to R_DMA_CHx_FIRST */
+       volatile u8             *icmdadr;     /* adr to R_DMA_CHx_CMD */
+       volatile u32            *idescradr;   /* adr to R_DMA_CHx_DESCR */
+
+       int                     flags;  /* defined in tty.h */
+
+       u8                      rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
+       u8                      tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
+       u8                      iseteop; /* bit number for R_SET_EOP for the input dma */
+       int                     enabled; /* Set to 1 if the port is enabled in HW config */
+
+       u8              dma_out_enabled:1; /* Set to 1 if DMA should be used */
+       u8              dma_in_enabled:1;  /* Set to 1 if DMA should be used */
+
+       /* end of fields defined in rs_table[] in .c-file */
+       u8              uses_dma_in;  /* Set to 1 if DMA is used */
+       u8              uses_dma_out; /* Set to 1 if DMA is used */
+       u8              forced_eop;   /* a fifo eop has been forced */
+       int                     baud_base;     /* For special baudrates */
+       int                     custom_divisor; /* For special baudrates */
+       struct etrax_dma_descr  tr_descr;
+       struct etrax_dma_descr  rec_descr[SERIAL_RECV_DESCRIPTORS];
+       int                     cur_rec_descr;
+
+       volatile int            tr_running; /* 1 if output is running */
+
+       struct tty_struct       *tty;
+       int                     read_status_mask;
+       int                     ignore_status_mask;
+       int                     x_char; /* xon/xoff character */
+       int                     close_delay;
+       unsigned short          closing_wait;
+       unsigned short          closing_wait2;
+       unsigned long           event;
+       unsigned long           last_active;
+       int                     line;
+       int                     type;  /* PORT_ETRAX */
+       int                     count;      /* # of fd on device */
+       int                     blocked_open; /* # of blocked opens */
+       struct circ_buf         xmit;
+       struct etrax_recv_buffer *first_recv_buffer;
+       struct etrax_recv_buffer *last_recv_buffer;
+       unsigned int            recv_cnt;
+       unsigned int            max_recv_cnt;
+
+       struct work_struct      work;
+       struct async_icount     icount;   /* error-statistics etc.*/
+       struct termios          normal_termios;
+       struct termios          callout_termios;
+#ifdef DECLARE_WAITQUEUE
+       wait_queue_head_t       open_wait;
+       wait_queue_head_t       close_wait;
+#else
+       struct wait_queue       *open_wait;
+       struct wait_queue       *close_wait;
+#endif
+
+       unsigned long           char_time_usec;       /* The time for 1 char, in usecs */
+       unsigned long           flush_time_usec;      /* How often we should flush */
+       unsigned long           last_tx_active_usec;  /* Last tx usec in the jiffies */
+       unsigned long           last_tx_active;       /* Last tx time in jiffies */
+       unsigned long           last_rx_active_usec;  /* Last rx usec in the jiffies */
+       unsigned long           last_rx_active;       /* Last rx time in jiffies */
+
+       int                     break_detected_cnt;
+       int                     errorcode;
+
+#ifdef CONFIG_ETRAX_RS485
+       struct rs485_control    rs485;  /* RS-485 support */
+#endif
+};
+
+/* this PORT is not in the standard serial.h. it's not actually used for
+ * anything since we only have one type of async serial-port anyway in this
+ * system.
+ */
+
+#define PORT_ETRAX 1
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP  0
+
+#endif /* __KERNEL__ */
+
+#endif /* !_ETRAX_SERIAL_H */
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
new file mode 100644 (file)
index 0000000..fc2a8f0
--- /dev/null
@@ -0,0 +1,902 @@
+/*
+ *  linux/drivers/serial/imx.c
+ *
+ *  Driver for Motorola IMX serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Author: Sascha Hauer <sascha@saschahauer.de>
+ *  Copyright (C) 2004 Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <linux/config.h>
+
+#if defined(CONFIG_SERIAL_IMX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_IMX_MAJOR       204
+#define MINOR_START            41
+
+#define NR_PORTS               2
+
+#define IMX_ISR_PASS_LIMIT     256
+
+/*
+ * This is the size of our serial port register set.
+ */
+#define UART_PORT_SIZE 0x100
+
+/*
+ * This determines how often we check the modem status signals
+ * for any change.  They generally aren't connected to an IRQ
+ * so we have to poll them.  We also check immediately before
+ * filling the TX fifo incase CTS has been dropped.
+ */
+#define MCTRL_TIMEOUT  (250*HZ/1000)
+
+#define DRIVER_NAME "IMX-uart"
+
+struct imx_port {
+       struct uart_port        port;
+       struct timer_list       timer;
+       unsigned int            old_status;
+       int txirq,rxirq;
+};
+
+/*
+ * Handle any change of modem status signal since we were last called.
+ */
+static void imx_mctrl_check(struct imx_port *sport)
+{
+       unsigned int status, changed;
+
+       status = sport->port.ops->get_mctrl(&sport->port);
+       changed = status ^ sport->old_status;
+
+       if (changed == 0)
+               return;
+
+       sport->old_status = status;
+
+       if (changed & TIOCM_RI)
+               sport->port.icount.rng++;
+       if (changed & TIOCM_DSR)
+               sport->port.icount.dsr++;
+       if (changed & TIOCM_CAR)
+               uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
+       if (changed & TIOCM_CTS)
+               uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
+
+       wake_up_interruptible(&sport->port.info->delta_msr_wait);
+}
+
+/*
+ * This is our per-port timeout handler, for checking the
+ * modem status signals.
+ */
+static void imx_timeout(unsigned long data)
+{
+       struct imx_port *sport = (struct imx_port *)data;
+       unsigned long flags;
+
+       if (sport->port.info) {
+               spin_lock_irqsave(&sport->port.lock, flags);
+               imx_mctrl_check(sport);
+               spin_unlock_irqrestore(&sport->port.lock, flags);
+
+               mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
+       }
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void imx_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+       UCR1((u32)sport->port.membase) &= ~UCR1_TXMPTYEN;
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void imx_stop_rx(struct uart_port *port)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+       UCR2((u32)sport->port.membase) &= ~UCR2_RXEN;
+}
+
+/*
+ * Set the modem control timer to fire immediately.
+ */
+static void imx_enable_ms(struct uart_port *port)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+
+       mod_timer(&sport->timer, jiffies);
+}
+
+static inline void imx_transmit_buffer(struct imx_port *sport)
+{
+       struct circ_buf *xmit = &sport->port.info->xmit;
+
+       do {
+               /* send xmit->buf[xmit->tail]
+                * out the port here */
+               URTX0((u32)sport->port.membase) = xmit->buf[xmit->tail];
+               xmit->tail = (xmit->tail + 1) &
+                        (UART_XMIT_SIZE - 1);
+               sport->port.icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (!(UTS((u32)sport->port.membase) & UTS_TXFULL));
+
+       if (uart_circ_empty(xmit))
+               imx_stop_tx(&sport->port, 0);
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void imx_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+
+       UCR1((u32)sport->port.membase) |= UCR1_TXMPTYEN;
+
+       if(UTS((u32)sport->port.membase) & UTS_TXEMPTY)
+               imx_transmit_buffer(sport);
+}
+
+static irqreturn_t imx_txint(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct imx_port *sport = (struct imx_port *)dev_id;
+       struct circ_buf *xmit = &sport->port.info->xmit;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sport->port.lock,flags);
+       if (sport->port.x_char)
+       {
+               /* Send next char */
+               URTX0((u32)sport->port.membase) = sport->port.x_char;
+               goto out;
+       }
+
+       if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
+               imx_stop_tx(&sport->port, 0);
+               goto out;
+       }
+
+       imx_transmit_buffer(sport);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&sport->port);
+
+out:
+       spin_unlock_irqrestore(&sport->port.lock,flags);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t imx_rxint(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct imx_port *sport = dev_id;
+       unsigned int rx,flg,ignored = 0;
+       struct tty_struct *tty = sport->port.info->tty;
+       unsigned long flags;
+
+       rx = URXD0((u32)sport->port.membase);
+       spin_lock_irqsave(&sport->port.lock,flags);
+
+       do {
+               flg = TTY_NORMAL;
+               sport->port.icount.rx++;
+
+               if( USR2((u32)sport->port.membase) & USR2_BRCD ) {
+                       USR2((u32)sport->port.membase) |= USR2_BRCD;
+                       if(uart_handle_break(&sport->port))
+                               goto ignore_char;
+               }
+
+               if (uart_handle_sysrq_char
+                           (&sport->port, (unsigned char)rx, regs))
+                       goto ignore_char;
+
+               if( rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) )
+                       goto handle_error;
+
+       error_return:
+               tty_insert_flip_char(tty, rx, flg);
+
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                       goto out;
+
+       ignore_char:
+               rx = URXD0((u32)sport->port.membase);
+       } while(rx & URXD_CHARRDY);
+
+out:
+       spin_unlock_irqrestore(&sport->port.lock,flags);
+       tty_flip_buffer_push(tty);
+       return IRQ_HANDLED;
+
+handle_error:
+       if (rx & URXD_PRERR)
+               sport->port.icount.parity++;
+       else if (rx & URXD_FRMERR)
+               sport->port.icount.frame++;
+       if (rx & URXD_OVRRUN)
+               sport->port.icount.overrun++;
+
+       if (rx & sport->port.ignore_status_mask) {
+               if (++ignored > 100)
+                       goto out;
+               goto ignore_char;
+       }
+
+       rx &= sport->port.read_status_mask;
+
+       if (rx & URXD_PRERR)
+               flg = TTY_PARITY;
+       else if (rx & URXD_FRMERR)
+               flg = TTY_FRAME;
+       if (rx & URXD_OVRRUN)
+               flg = TTY_OVERRUN;
+
+#ifdef SUPPORT_SYSRQ
+       sport->port.sysrq = 0;
+#endif
+       goto error_return;
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int imx_tx_empty(struct uart_port *port)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+
+       return USR2((u32)sport->port.membase) & USR2_TXDC ?  TIOCSER_TEMT : 0;
+}
+
+static unsigned int imx_get_mctrl(struct uart_port *port)
+{
+       return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void imx_break_ctl(struct uart_port *port, int break_state)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+
+       if ( break_state != 0 )
+               UCR1((u32)sport->port.membase) |= UCR1_SNDBRK;
+       else
+               UCR1((u32)sport->port.membase) &= ~UCR1_SNDBRK;
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+#define TXTL 2 /* reset default */
+#define RXTL 1 /* reset default */
+
+static int imx_startup(struct uart_port *port)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+       int retval;
+       unsigned int val;
+       unsigned long flags;
+
+       /* set receiver / transmitter trigger level. We assume
+        * that RFDIV has been set by the arch setup or by the bootloader.
+        */
+       val = (UFCR((u32)sport->port.membase) & UFCR_RFDIV)  | TXTL<<10 | RXTL;
+       UFCR((u32)sport->port.membase) = val;
+
+       /* disable the DREN bit (Data Ready interrupt enable) before
+        * requesting IRQs
+        */
+       UCR4((u32)sport->port.membase) &= ~UCR4_DREN;
+
+       /*
+        * Allocate the IRQ
+        */
+       retval = request_irq(sport->rxirq, imx_rxint, 0,
+                            DRIVER_NAME, sport);
+       if (retval) goto error_out2;
+
+       retval = request_irq(sport->txirq, imx_txint, 0,
+                            "imx-uart", sport);
+       if (retval) goto error_out1;
+
+       /*
+        * Finally, clear and enable interrupts
+        */
+
+       UCR1((u32)sport->port.membase) |=
+                        (UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_UARTEN);
+
+       UCR2((u32)sport->port.membase) |= (UCR2_RXEN | UCR2_TXEN);
+       /*
+        * Enable modem status interrupts
+        */
+       spin_lock_irqsave(&sport->port.lock,flags);
+       imx_enable_ms(&sport->port);
+       spin_unlock_irqrestore(&sport->port.lock,flags);
+
+       return 0;
+
+error_out1:
+       free_irq(sport->rxirq, sport);
+error_out2:
+       free_irq(sport->txirq, sport);
+       return retval;
+}
+
+static void imx_shutdown(struct uart_port *port)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+
+       /*
+        * Stop our timer.
+        */
+       del_timer_sync(&sport->timer);
+
+       /*
+        * Free the interrupts
+        */
+       free_irq(sport->txirq, sport);
+       free_irq(sport->rxirq, sport);
+
+       /*
+        * Disable all interrupts, port and break condition.
+        */
+
+       UCR1((u32)sport->port.membase) &=
+                        ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_UARTEN);
+}
+
+static void
+imx_set_termios(struct uart_port *port, struct termios *termios,
+                  struct termios *old)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+       unsigned long flags;
+       unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
+       unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+
+       /*
+        * If we don't support modem control lines, don't allow
+        * these to be set.
+        */
+       if (0) {
+               termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
+               termios->c_cflag |= CLOCAL;
+       }
+
+       /*
+        * We only support CS7 and CS8.
+        */
+       while ((termios->c_cflag & CSIZE) != CS7 &&
+              (termios->c_cflag & CSIZE) != CS8) {
+               termios->c_cflag &= ~CSIZE;
+               termios->c_cflag |= old_csize;
+               old_csize = CS8;
+       }
+
+       if ((termios->c_cflag & CSIZE) == CS8)
+               ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS;
+       else
+               ucr2 = UCR2_SRST | UCR2_IRTS;
+
+       if (termios->c_cflag & CSTOPB)
+               ucr2 |= UCR2_STPB;
+       if (termios->c_cflag & PARENB) {
+               ucr2 |= UCR2_PREN;
+               if (!(termios->c_cflag & PARODD))
+                       ucr2 |= UCR2_PROE;
+       }
+
+       /*
+        * Ask the core to calculate the divisor for us.
+        */
+       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+       quot = uart_get_divisor(port, baud);
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+
+       sport->port.read_status_mask = 0;
+       if (termios->c_iflag & INPCK)
+               sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR);
+       if (termios->c_iflag & (BRKINT | PARMRK))
+               sport->port.read_status_mask |= URXD_BRK;
+
+       /*
+        * Characters to ignore
+        */
+       sport->port.ignore_status_mask = 0;
+       if (termios->c_iflag & IGNPAR)
+               sport->port.ignore_status_mask |= URXD_PRERR;
+       if (termios->c_iflag & IGNBRK) {
+               sport->port.ignore_status_mask |= URXD_BRK;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too (for real raw support).
+                */
+               if (termios->c_iflag & IGNPAR)
+                       sport->port.ignore_status_mask |= URXD_OVRRUN;
+       }
+
+       del_timer_sync(&sport->timer);
+
+       /*
+        * Update the per-port timeout.
+        */
+       uart_update_timeout(port, termios->c_cflag, baud);
+
+       /*
+        * disable interrupts and drain transmitter
+        */
+       old_ucr1 = UCR1((u32)sport->port.membase);
+       UCR1((u32)sport->port.membase) &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN);
+
+       while ( !(USR2((u32)sport->port.membase) & USR2_TXDC))
+               barrier();
+
+       /* then, disable everything */
+       old_txrxen = UCR2((u32)sport->port.membase) & ( UCR2_TXEN | UCR2_RXEN );
+       UCR2((u32)sport->port.membase) &= ~( UCR2_TXEN | UCR2_RXEN);
+
+       /* set the parity, stop bits and data size */
+       UCR2((u32)sport->port.membase) = ucr2;
+
+       /* set the baud rate. We assume uartclk = 16 MHz
+        *
+        * baud * 16   UBIR - 1
+        * --------- = --------
+        *  uartclk    UBMR - 1
+        */
+       UBIR((u32)sport->port.membase) = (baud / 100) - 1;
+       UBMR((u32)sport->port.membase) = 10000 - 1;
+
+       UCR1((u32)sport->port.membase) = old_ucr1;
+       UCR2((u32)sport->port.membase) |= old_txrxen;
+
+       if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
+               imx_enable_ms(&sport->port);
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static const char *imx_type(struct uart_port *port)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+
+       return sport->port.type == PORT_IMX ? "IMX" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void imx_release_port(struct uart_port *port)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+
+       release_mem_region(sport->port.mapbase, UART_PORT_SIZE);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int imx_request_port(struct uart_port *port)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+
+       return request_mem_region(sport->port.mapbase, UART_PORT_SIZE,
+                       "imx-uart") != NULL ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void imx_config_port(struct uart_port *port, int flags)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+
+       if (flags & UART_CONFIG_TYPE &&
+           imx_request_port(&sport->port) == 0)
+               sport->port.type = PORT_IMX;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_IMX and PORT_UNKNOWN
+ */
+static int
+imx_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+       int ret = 0;
+
+       if (ser->type != PORT_UNKNOWN && ser->type != PORT_IMX)
+               ret = -EINVAL;
+       if (sport->port.irq != ser->irq)
+               ret = -EINVAL;
+       if (ser->io_type != UPIO_MEM)
+               ret = -EINVAL;
+       if (sport->port.uartclk / 16 != ser->baud_base)
+               ret = -EINVAL;
+       if ((void *)sport->port.mapbase != ser->iomem_base)
+               ret = -EINVAL;
+       if (sport->port.iobase != ser->port)
+               ret = -EINVAL;
+       if (ser->hub6 != 0)
+               ret = -EINVAL;
+       return ret;
+}
+
+static struct uart_ops imx_pops = {
+       .tx_empty       = imx_tx_empty,
+       .set_mctrl      = imx_set_mctrl,
+       .get_mctrl      = imx_get_mctrl,
+       .stop_tx        = imx_stop_tx,
+       .start_tx       = imx_start_tx,
+       .stop_rx        = imx_stop_rx,
+       .enable_ms      = imx_enable_ms,
+       .break_ctl      = imx_break_ctl,
+       .startup        = imx_startup,
+       .shutdown       = imx_shutdown,
+       .set_termios    = imx_set_termios,
+       .type           = imx_type,
+       .release_port   = imx_release_port,
+       .request_port   = imx_request_port,
+       .config_port    = imx_config_port,
+       .verify_port    = imx_verify_port,
+};
+
+static struct imx_port imx_ports[] = {
+       {
+       .txirq  = UART1_MINT_TX,
+       .rxirq  = UART1_MINT_RX,
+       .port   = {
+               .type           = PORT_IMX,
+               .iotype         = SERIAL_IO_MEM,
+               .membase        = (void *)IMX_UART1_BASE,
+               .mapbase        = IMX_UART1_BASE, /* FIXME */
+               .irq            = UART1_MINT_RX,
+               .uartclk        = 16000000,
+               .fifosize       = 8,
+               .flags          = ASYNC_BOOT_AUTOCONF,
+               .ops            = &imx_pops,
+               .line           = 0,
+       },
+       }, {
+       .txirq  = UART2_MINT_TX,
+       .rxirq  = UART2_MINT_RX,
+       .port   = {
+               .type           = PORT_IMX,
+               .iotype         = SERIAL_IO_MEM,
+               .membase        = (void *)IMX_UART2_BASE,
+               .mapbase        = IMX_UART2_BASE, /* FIXME */
+               .irq            = UART2_MINT_RX,
+               .uartclk        = 16000000,
+               .fifosize       = 8,
+               .flags          = ASYNC_BOOT_AUTOCONF,
+               .ops            = &imx_pops,
+               .line           = 1,
+       },
+       }
+};
+
+/*
+ * Setup the IMX serial ports.
+ * Note also that we support "console=ttySMXx" where "x" is either 0 or 1.
+ * Which serial port this ends up being depends on the machine you're
+ * running this kernel on.  I'm not convinced that this is a good idea,
+ * but that's the way it traditionally works.
+ *
+ */
+static void __init imx_init_ports(void)
+{
+       static int first = 1;
+       int i;
+
+       if (!first)
+               return;
+       first = 0;
+
+       for (i = 0; i < ARRAY_SIZE(imx_ports); i++) {
+               init_timer(&imx_ports[i].timer);
+               imx_ports[i].timer.function = imx_timeout;
+               imx_ports[i].timer.data     = (unsigned long)&imx_ports[i];
+       }
+
+       imx_gpio_mode(PC9_PF_UART1_CTS);
+       imx_gpio_mode(PC10_PF_UART1_RTS);
+       imx_gpio_mode(PC11_PF_UART1_TXD);
+       imx_gpio_mode(PC12_PF_UART1_RXD);
+       imx_gpio_mode(PB28_PF_UART2_CTS);
+       imx_gpio_mode(PB29_PF_UART2_RTS);
+
+       imx_gpio_mode(PB30_PF_UART2_TXD);
+       imx_gpio_mode(PB31_PF_UART2_RXD);
+
+#if 0 /* We don't need these, on the mx1 the _modem_ side of the uart
+       * is implemented.
+       */
+       imx_gpio_mode(PD7_AF_UART2_DTR);
+       imx_gpio_mode(PD8_AF_UART2_DCD);
+       imx_gpio_mode(PD9_AF_UART2_RI);
+       imx_gpio_mode(PD10_AF_UART2_DSR);
+#endif
+
+
+}
+
+#ifdef CONFIG_SERIAL_IMX_CONSOLE
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+imx_console_write(struct console *co, const char *s, unsigned int count)
+{
+       struct imx_port *sport = &imx_ports[co->index];
+       unsigned int old_ucr1, old_ucr2, i;
+
+       /*
+        *      First, save UCR1/2 and then disable interrupts
+        */
+       old_ucr1 = UCR1((u32)sport->port.membase);
+       old_ucr2 = UCR2((u32)sport->port.membase);
+
+       UCR1((u32)sport->port.membase) =
+                          (old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN)
+                          & ~(UCR1_TXMPTYEN | UCR1_RRDYEN);
+       UCR2((u32)sport->port.membase) = old_ucr2 | UCR2_TXEN;
+
+       /*
+        *      Now, do each character
+        */
+       for (i = 0; i < count; i++) {
+
+               while ((UTS((u32)sport->port.membase) & UTS_TXFULL))
+                       barrier();
+
+               URTX0((u32)sport->port.membase) = s[i];
+
+               if (s[i] == '\n') {
+                       while ((UTS((u32)sport->port.membase) & UTS_TXFULL))
+                               barrier();
+                       URTX0((u32)sport->port.membase) = '\r';
+               }
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore UCR1/2
+        */
+       while (!(USR2((u32)sport->port.membase) & USR2_TXDC));
+
+       UCR1((u32)sport->port.membase) = old_ucr1;
+       UCR2((u32)sport->port.membase) = old_ucr2;
+}
+
+/*
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+imx_console_get_options(struct imx_port *sport, int *baud,
+                          int *parity, int *bits)
+{
+       if ( UCR1((u32)sport->port.membase) | UCR1_UARTEN ) {
+               /* ok, the port was enabled */
+               unsigned int ucr2, ubir,ubmr, uartclk;
+
+               ucr2 = UCR2((u32)sport->port.membase);
+
+               *parity = 'n';
+               if (ucr2 & UCR2_PREN) {
+                       if (ucr2 & UCR2_PROE)
+                               *parity = 'o';
+                       else
+                               *parity = 'e';
+               }
+
+               if (ucr2 & UCR2_WS)
+                       *bits = 8;
+               else
+                       *bits = 7;
+
+               ubir = UBIR((u32)sport->port.membase) & 0xffff;
+               ubmr = UBMR((u32)sport->port.membase) & 0xffff;
+               uartclk = sport->port.uartclk;
+
+               *baud = ((uartclk/16) * (ubir + 1)) / (ubmr + 1);
+       }
+}
+
+static int __init
+imx_console_setup(struct console *co, char *options)
+{
+       struct imx_port *sport;
+       int baud = 9600;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       /*
+        * Check whether an invalid uart number has been specified, and
+        * if so, search for the first available port that does have
+        * console support.
+        */
+       if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
+               co->index = 0;
+       sport = &imx_ports[co->index];
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+       else
+               imx_console_get_options(sport, &baud, &parity, &bits);
+
+       return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+}
+
+extern struct uart_driver imx_reg;
+static struct console imx_console = {
+       .name           = "ttySMX",
+       .write          = imx_console_write,
+       .device         = uart_console_device,
+       .setup          = imx_console_setup,
+       .flags          = CON_PRINTBUFFER,
+       .index          = -1,
+       .data           = &imx_reg,
+};
+
+static int __init imx_rs_console_init(void)
+{
+       imx_init_ports();
+       register_console(&imx_console);
+       return 0;
+}
+console_initcall(imx_rs_console_init);
+
+#define IMX_CONSOLE    &imx_console
+#else
+#define IMX_CONSOLE    NULL
+#endif
+
+static struct uart_driver imx_reg = {
+       .owner          = THIS_MODULE,
+       .driver_name    = DRIVER_NAME,
+       .dev_name       = "ttySMX",
+       .devfs_name     = "ttsmx/",
+       .major          = SERIAL_IMX_MAJOR,
+       .minor          = MINOR_START,
+       .nr             = ARRAY_SIZE(imx_ports),
+       .cons           = IMX_CONSOLE,
+};
+
+static int serial_imx_suspend(struct device *_dev, u32 state, u32 level)
+{
+        struct imx_port *sport = dev_get_drvdata(_dev);
+
+        if (sport && level == SUSPEND_DISABLE)
+                uart_suspend_port(&imx_reg, &sport->port);
+
+        return 0;
+}
+
+static int serial_imx_resume(struct device *_dev, u32 level)
+{
+        struct imx_port *sport = dev_get_drvdata(_dev);
+
+        if (sport && level == RESUME_ENABLE)
+                uart_resume_port(&imx_reg, &sport->port);
+
+        return 0;
+}
+
+static int serial_imx_probe(struct device *_dev)
+{
+       struct platform_device *dev = to_platform_device(_dev);
+
+       imx_ports[dev->id].port.dev = _dev;
+       uart_add_one_port(&imx_reg, &imx_ports[dev->id].port);
+       dev_set_drvdata(_dev, &imx_ports[dev->id]);
+       return 0;
+}
+
+static int serial_imx_remove(struct device *_dev)
+{
+       struct imx_port *sport = dev_get_drvdata(_dev);
+
+       dev_set_drvdata(_dev, NULL);
+
+       if (sport)
+               uart_remove_one_port(&imx_reg, &sport->port);
+
+       return 0;
+}
+
+static struct device_driver serial_imx_driver = {
+        .name           = "imx-uart",
+        .bus            = &platform_bus_type,
+        .probe          = serial_imx_probe,
+        .remove         = serial_imx_remove,
+
+       .suspend        = serial_imx_suspend,
+       .resume         = serial_imx_resume,
+};
+
+static int __init imx_serial_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "Serial: IMX driver\n");
+
+       imx_init_ports();
+
+       ret = uart_register_driver(&imx_reg);
+       if (ret)
+               return ret;
+
+       ret = driver_register(&serial_imx_driver);
+       if (ret != 0)
+               uart_unregister_driver(&imx_reg);
+
+       return 0;
+}
+
+static void __exit imx_serial_exit(void)
+{
+       uart_unregister_driver(&imx_reg);
+}
+
+module_init(imx_serial_init);
+module_exit(imx_serial_exit);
+
+MODULE_AUTHOR("Sascha Hauer");
+MODULE_DESCRIPTION("IMX generic serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/m32r_sio.c b/drivers/serial/m32r_sio.c
new file mode 100644 (file)
index 0000000..380c295
--- /dev/null
@@ -0,0 +1,1373 @@
+/*
+ *  m32r_sio.c
+ *
+ *  Driver for M32R serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *  Based on drivers/serial/8250.c.
+ *
+ *  Copyright (C) 2001  Russell King.
+ *  Copyright (C) 2004  Hirokazu Takata <takata at linux-m32r.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.
+ */
+
+/*
+ * A note about mapbase / membase
+ *
+ *  mapbase is the physical address of the IO port.  Currently, we don't
+ *  support this very well, and it may well be dropped from this driver
+ *  in future.  As such, mapbase should be NULL.
+ *
+ *  membase is an 'ioremapped' cookie.  This is compatible with the old
+ *  serial.c driver, and is currently the preferred form.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <linux/delay.h>
+
+#include <asm/m32r.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#if defined(CONFIG_SERIAL_M32R_SIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#define PORT_SIO       1
+#define PORT_MAX_SIO   1
+#define BAUD_RATE      115200
+
+#include <linux/serial_core.h>
+#include "m32r_sio.h"
+#include "m32r_sio_reg.h"
+
+/*
+ * Configuration:
+ *   share_irqs - whether we pass SA_SHIRQ to request_irq().  This option
+ *                is unsafe when used on edge-triggered interrupts.
+ */
+unsigned int share_irqs_sio = M32R_SIO_SHARE_IRQS;
+
+/*
+ * Debugging.
+ */
+#if 0
+#define DEBUG_AUTOCONF(fmt...) printk(fmt)
+#else
+#define DEBUG_AUTOCONF(fmt...) do { } while (0)
+#endif
+
+#if 0
+#define DEBUG_INTR(fmt...)     printk(fmt)
+#else
+#define DEBUG_INTR(fmt...)     do { } while (0)
+#endif
+
+#define PASS_LIMIT     256
+
+/*
+ * We default to IRQ0 for the "no irq" hack.   Some
+ * machine types want others as well - they're free
+ * to redefine this in their header file.
+ */
+#define is_real_interrupt(irq) ((irq) != 0)
+
+/*
+ * This converts from our new CONFIG_ symbols to the symbols
+ * that asm/serial.h expects.  You _NEED_ to comment out the
+ * linux/config.h include contained inside asm/serial.h for
+ * this to work.
+ */
+#undef CONFIG_SERIAL_MANY_PORTS
+#undef CONFIG_SERIAL_DETECT_IRQ
+#undef CONFIG_SERIAL_MULTIPORT
+#undef CONFIG_HUB6
+
+#ifdef CONFIG_SERIAL_M32R_SIO_DETECT_IRQ
+#define CONFIG_SERIAL_DETECT_IRQ 1
+#endif
+#ifdef CONFIG_SERIAL_M32R_SIO_MULTIPORT
+#define CONFIG_SERIAL_MULTIPORT 1
+#endif
+#ifdef CONFIG_SERIAL_M32R_SIO_MANY_PORTS
+#define CONFIG_SERIAL_MANY_PORTS 1
+#endif
+
+/*
+ * HUB6 is always on.  This will be removed once the header
+ * files have been cleaned.
+ */
+#define CONFIG_HUB6 1
+
+#include <asm/serial.h>
+
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+static struct old_serial_port old_serial_port[] = {
+       { 0, BASE_BAUD, ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV, STD_COM_FLAGS },
+};
+#else
+static struct old_serial_port old_serial_port[] = {
+       { 0, BASE_BAUD, M32R_SIO_OFFSET, M32R_IRQ_SIO0_R, STD_COM_FLAGS },
+};
+#endif
+
+#define UART_NR        ARRAY_SIZE(old_serial_port)
+
+struct uart_sio_port {
+       struct uart_port        port;
+       struct timer_list       timer;          /* "no irq" timer */
+       struct list_head        list;           /* ports on this IRQ */
+       unsigned short          rev;
+       unsigned char           acr;
+       unsigned char           ier;
+       unsigned char           lcr;
+       unsigned char           mcr_mask;       /* mask of user bits */
+       unsigned char           mcr_force;      /* mask of forced bits */
+       unsigned char           lsr_break_flag;
+
+       /*
+        * We provide a per-port pm hook.
+        */
+       void                    (*pm)(struct uart_port *port,
+                                     unsigned int state, unsigned int old);
+};
+
+struct irq_info {
+       spinlock_t              lock;
+       struct list_head        *head;
+};
+
+static struct irq_info irq_lists[NR_IRQS];
+
+/*
+ * Here we define the default xmit fifo size used for each type of UART.
+ */
+static const struct serial_uart_config uart_config[PORT_MAX_SIO+1] = {
+       { "unknown",    1,      0 },
+       { "M32RSIO",    1,      0 }
+};
+
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+
+#define __sio_in(x) inw((unsigned long)(x))
+#define __sio_out(v,x) outw((v),(unsigned long)(x))
+
+static inline void sio_set_baud_rate(unsigned long baud)
+{
+       unsigned short sbaud;
+       sbaud = (boot_cpu_data.bus_clock / (baud * 4))-1;
+       __sio_out(sbaud, PLD_ESIO0BAUR);
+}
+
+static void sio_reset(void)
+{
+       unsigned short tmp;
+
+       tmp = __sio_in(PLD_ESIO0RXB);
+       tmp = __sio_in(PLD_ESIO0RXB);
+       tmp = __sio_in(PLD_ESIO0CR);
+       sio_set_baud_rate(BAUD_RATE);
+       __sio_out(0x0300, PLD_ESIO0CR);
+       __sio_out(0x0003, PLD_ESIO0CR);
+}
+
+static void sio_init(void)
+{
+       unsigned short tmp;
+
+       tmp = __sio_in(PLD_ESIO0RXB);
+       tmp = __sio_in(PLD_ESIO0RXB);
+       tmp = __sio_in(PLD_ESIO0CR);
+       __sio_out(0x0300, PLD_ESIO0CR);
+       __sio_out(0x0003, PLD_ESIO0CR);
+}
+
+static void sio_error(int *status)
+{
+       printk("SIO0 error[%04x]\n", *status);
+       do {
+               sio_init();
+       } while ((*status = __sio_in(PLD_ESIO0CR)) != 3);
+}
+
+#else /* not CONFIG_SERIAL_M32R_PLDSIO */
+
+#define __sio_in(x) inl(x)
+#define __sio_out(v,x) outl((v),(x))
+
+static inline void sio_set_baud_rate(unsigned long baud)
+{
+       unsigned long i, j;
+
+       i = boot_cpu_data.bus_clock / (baud * 16);
+       j = (boot_cpu_data.bus_clock - (i * baud * 16)) / baud;
+       i -= 1;
+       j = (j + 1) >> 1;
+
+       __sio_out(i, M32R_SIO0_BAUR_PORTL);
+       __sio_out(j, M32R_SIO0_RBAUR_PORTL);
+}
+
+static void sio_reset(void)
+{
+       __sio_out(0x00000300, M32R_SIO0_CR_PORTL);      /* init status */
+       __sio_out(0x00000800, M32R_SIO0_MOD1_PORTL);    /* 8bit        */
+       __sio_out(0x00000080, M32R_SIO0_MOD0_PORTL);    /* 1stop non   */
+       sio_set_baud_rate(BAUD_RATE);
+       __sio_out(0x00000000, M32R_SIO0_TRCR_PORTL);
+       __sio_out(0x00000003, M32R_SIO0_CR_PORTL);      /* RXCEN */
+}
+
+static void sio_init(void)
+{
+       unsigned int tmp;
+
+       tmp = __sio_in(M32R_SIO0_RXB_PORTL);
+       tmp = __sio_in(M32R_SIO0_RXB_PORTL);
+       tmp = __sio_in(M32R_SIO0_STS_PORTL);
+       __sio_out(0x00000003, M32R_SIO0_CR_PORTL);
+}
+
+static void sio_error(int *status)
+{
+       printk("SIO0 error[%04x]\n", *status);
+       do {
+               sio_init();
+       } while ((*status = __sio_in(M32R_SIO0_CR_PORTL)) != 3);
+}
+
+#endif /* CONFIG_SERIAL_M32R_PLDSIO */
+
+static _INLINE_ unsigned int sio_in(struct uart_sio_port *up, int offset)
+{
+       return __sio_in(up->port.iobase + offset);
+}
+
+static _INLINE_ void sio_out(struct uart_sio_port *up, int offset, int value)
+{
+       __sio_out(value, up->port.iobase + offset);
+}
+
+static _INLINE_ unsigned int serial_in(struct uart_sio_port *up, int offset)
+{
+       if (!offset)
+               return 0;
+
+       return __sio_in(offset);
+}
+
+static _INLINE_ void
+serial_out(struct uart_sio_port *up, int offset, int value)
+{
+       if (!offset)
+               return;
+
+       __sio_out(value, offset);
+}
+
+static void m32r_sio_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+       if (up->ier & UART_IER_THRI) {
+               up->ier &= ~UART_IER_THRI;
+               serial_out(up, UART_IER, up->ier);
+       }
+}
+
+static void m32r_sio_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+       struct circ_buf *xmit = &up->port.info->xmit;
+
+       if (!(up->ier & UART_IER_THRI)) {
+               up->ier |= UART_IER_THRI;
+               serial_out(up, UART_IER, up->ier);
+               serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               up->port.icount.tx++;
+       }
+       while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY);
+#else
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+       if (!(up->ier & UART_IER_THRI)) {
+               up->ier |= UART_IER_THRI;
+               serial_out(up, UART_IER, up->ier);
+       }
+#endif
+}
+
+static void m32r_sio_stop_rx(struct uart_port *port)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+       up->ier &= ~UART_IER_RLSI;
+       up->port.read_status_mask &= ~UART_LSR_DR;
+       serial_out(up, UART_IER, up->ier);
+}
+
+static void m32r_sio_enable_ms(struct uart_port *port)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+       up->ier |= UART_IER_MSI;
+       serial_out(up, UART_IER, up->ier);
+}
+
+static _INLINE_ void receive_chars(struct uart_sio_port *up, int *status,
+       struct pt_regs *regs)
+{
+       struct tty_struct *tty = up->port.info->tty;
+       unsigned char ch;
+       int max_count = 256;
+
+       do {
+               if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
+                       tty->flip.work.func((void *)tty);
+                       if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                               return; // if TTY_DONT_FLIP is set
+               }
+               ch = sio_in(up, SIORXB);
+               *tty->flip.char_buf_ptr = ch;
+               *tty->flip.flag_buf_ptr = TTY_NORMAL;
+               up->port.icount.rx++;
+
+               if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+                                      UART_LSR_FE | UART_LSR_OE))) {
+                       /*
+                        * For statistics only
+                        */
+                       if (*status & UART_LSR_BI) {
+                               *status &= ~(UART_LSR_FE | UART_LSR_PE);
+                               up->port.icount.brk++;
+                               /*
+                                * We do the SysRQ and SAK checking
+                                * here because otherwise the break
+                                * may get masked by ignore_status_mask
+                                * or read_status_mask.
+                                */
+                               if (uart_handle_break(&up->port))
+                                       goto ignore_char;
+                       } else if (*status & UART_LSR_PE)
+                               up->port.icount.parity++;
+                       else if (*status & UART_LSR_FE)
+                               up->port.icount.frame++;
+                       if (*status & UART_LSR_OE)
+                               up->port.icount.overrun++;
+
+                       /*
+                        * Mask off conditions which should be ingored.
+                        */
+                       *status &= up->port.read_status_mask;
+
+                       if (up->port.line == up->port.cons->index) {
+                               /* Recover the break flag from console xmit */
+                               *status |= up->lsr_break_flag;
+                               up->lsr_break_flag = 0;
+                       }
+
+                       if (*status & UART_LSR_BI) {
+                               DEBUG_INTR("handling break....");
+                               *tty->flip.flag_buf_ptr = TTY_BREAK;
+                       } else if (*status & UART_LSR_PE)
+                               *tty->flip.flag_buf_ptr = TTY_PARITY;
+                       else if (*status & UART_LSR_FE)
+                               *tty->flip.flag_buf_ptr = TTY_FRAME;
+               }
+               if (uart_handle_sysrq_char(&up->port, ch, regs))
+                       goto ignore_char;
+               if ((*status & up->port.ignore_status_mask) == 0) {
+                       tty->flip.flag_buf_ptr++;
+                       tty->flip.char_buf_ptr++;
+                       tty->flip.count++;
+               }
+               if ((*status & UART_LSR_OE) &&
+                   tty->flip.count < TTY_FLIPBUF_SIZE) {
+                       /*
+                        * Overrun is special, since it's reported
+                        * immediately, and doesn't affect the current
+                        * character.
+                        */
+                       *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+                       tty->flip.flag_buf_ptr++;
+                       tty->flip.char_buf_ptr++;
+                       tty->flip.count++;
+               }
+       ignore_char:
+               *status = serial_in(up, UART_LSR);
+       } while ((*status & UART_LSR_DR) && (max_count-- > 0));
+       tty_flip_buffer_push(tty);
+}
+
+static _INLINE_ void transmit_chars(struct uart_sio_port *up)
+{
+       struct circ_buf *xmit = &up->port.info->xmit;
+       int count;
+
+       if (up->port.x_char) {
+#ifndef CONFIG_SERIAL_M32R_PLDSIO      /* XXX */
+               serial_out(up, UART_TX, up->port.x_char);
+#endif
+               up->port.icount.tx++;
+               up->port.x_char = 0;
+               return;
+       }
+       if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+               m32r_sio_stop_tx(&up->port, 0);
+               return;
+       }
+
+       count = up->port.fifosize;
+       do {
+               serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               up->port.icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+               while (!serial_in(up, UART_LSR) & UART_LSR_THRE);
+
+       } while (--count > 0);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&up->port);
+
+       DEBUG_INTR("THRE...");
+
+       if (uart_circ_empty(xmit))
+               m32r_sio_stop_tx(&up->port, 0);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline void m32r_sio_handle_port(struct uart_sio_port *up,
+       unsigned int status, struct pt_regs *regs)
+{
+       DEBUG_INTR("status = %x...", status);
+
+       if (status & 0x04)
+               receive_chars(up, &status, regs);
+       // check_modem_status(up);
+       if (status & 0x01)
+               transmit_chars(up);
+}
+
+/*
+ * This is the serial driver's interrupt routine.
+ *
+ * Arjan thinks the old way was overly complex, so it got simplified.
+ * Alan disagrees, saying that need the complexity to handle the weird
+ * nature of ISA shared interrupts.  (This is a special exception.)
+ *
+ * In order to handle ISA shared interrupts properly, we need to check
+ * that all ports have been serviced, and therefore the ISA interrupt
+ * line has been de-asserted.
+ *
+ * This means we need to loop through all ports. checking that they
+ * don't have an interrupt pending.
+ */
+static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id,
+       struct pt_regs *regs)
+{
+       struct irq_info *i = dev_id;
+       struct list_head *l, *end = NULL;
+       int pass_counter = 0;
+
+       DEBUG_INTR("m32r_sio_interrupt(%d)...", irq);
+
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+//     if (irq == PLD_IRQ_SIO0_SND)
+//             irq = PLD_IRQ_SIO0_RCV;
+#else
+       if (irq == M32R_IRQ_SIO0_S)
+               irq = M32R_IRQ_SIO0_R;
+#endif
+
+       spin_lock(&i->lock);
+
+       l = i->head;
+       do {
+               struct uart_sio_port *up;
+               unsigned int sts;
+
+               up = list_entry(l, struct uart_sio_port, list);
+
+               sts = sio_in(up, SIOSTS);
+               if (sts & 0x5) {
+                       spin_lock(&up->port.lock);
+                       m32r_sio_handle_port(up, sts, regs);
+                       spin_unlock(&up->port.lock);
+
+                       end = NULL;
+               } else if (end == NULL)
+                       end = l;
+
+               l = l->next;
+
+               if (l == i->head && pass_counter++ > PASS_LIMIT) {
+                       if (sts & 0xe0)
+                               sio_error(&sts);
+                       break;
+               }
+       } while (l != end);
+
+       spin_unlock(&i->lock);
+
+       DEBUG_INTR("end.\n");
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * To support ISA shared interrupts, we need to have one interrupt
+ * handler that ensures that the IRQ line has been deasserted
+ * before returning.  Failing to do this will result in the IRQ
+ * line being stuck active, and, since ISA irqs are edge triggered,
+ * no more IRQs will be seen.
+ */
+static void serial_do_unlink(struct irq_info *i, struct uart_sio_port *up)
+{
+       spin_lock_irq(&i->lock);
+
+       if (!list_empty(i->head)) {
+               if (i->head == &up->list)
+                       i->head = i->head->next;
+               list_del(&up->list);
+       } else {
+               BUG_ON(i->head != &up->list);
+               i->head = NULL;
+       }
+
+       spin_unlock_irq(&i->lock);
+}
+
+static int serial_link_irq_chain(struct uart_sio_port *up)
+{
+       struct irq_info *i = irq_lists + up->port.irq;
+       int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0;
+
+       spin_lock_irq(&i->lock);
+
+       if (i->head) {
+               list_add(&up->list, i->head);
+               spin_unlock_irq(&i->lock);
+
+               ret = 0;
+       } else {
+               INIT_LIST_HEAD(&up->list);
+               i->head = &up->list;
+               spin_unlock_irq(&i->lock);
+
+               ret = request_irq(up->port.irq, m32r_sio_interrupt,
+                                 irq_flags, "SIO0-RX", i);
+               ret |= request_irq(up->port.irq + 1, m32r_sio_interrupt,
+                                 irq_flags, "SIO0-TX", i);
+               if (ret < 0)
+                       serial_do_unlink(i, up);
+       }
+
+       return ret;
+}
+
+static void serial_unlink_irq_chain(struct uart_sio_port *up)
+{
+       struct irq_info *i = irq_lists + up->port.irq;
+
+       BUG_ON(i->head == NULL);
+
+       if (list_empty(i->head)) {
+               free_irq(up->port.irq, i);
+               free_irq(up->port.irq + 1, i);
+       }
+
+       serial_do_unlink(i, up);
+}
+
+/*
+ * This function is used to handle ports that do not have an
+ * interrupt.  This doesn't work very well for 16450's, but gives
+ * barely passable results for a 16550A.  (Although at the expense
+ * of much CPU overhead).
+ */
+static void m32r_sio_timeout(unsigned long data)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)data;
+       unsigned int timeout;
+       unsigned int sts;
+
+       sts = sio_in(up, SIOSTS);
+       if (sts & 0x5) {
+               spin_lock(&up->port.lock);
+               m32r_sio_handle_port(up, sts, NULL);
+               spin_unlock(&up->port.lock);
+       }
+
+       timeout = up->port.timeout;
+       timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+       mod_timer(&up->timer, jiffies + timeout);
+}
+
+static unsigned int m32r_sio_tx_empty(struct uart_port *port)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+       unsigned long flags;
+       unsigned int ret;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       return ret;
+}
+
+static unsigned int m32r_sio_get_mctrl(struct uart_port *port)
+{
+       return 0;
+}
+
+static void m32r_sio_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+
+}
+
+static void m32r_sio_break_ctl(struct uart_port *port, int break_state)
+{
+
+}
+
+static int m32r_sio_startup(struct uart_port *port)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+       int retval;
+
+       sio_init();
+
+       /*
+        * If the "interrupt" for this port doesn't correspond with any
+        * hardware interrupt, we use a timer-based system.  The original
+        * driver used to do this with IRQ0.
+        */
+       if (!is_real_interrupt(up->port.irq)) {
+               unsigned int timeout = up->port.timeout;
+
+               timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+
+               up->timer.data = (unsigned long)up;
+               mod_timer(&up->timer, jiffies + timeout);
+       } else {
+               retval = serial_link_irq_chain(up);
+               if (retval)
+                       return retval;
+       }
+
+       /*
+        * Finally, enable interrupts.  Note: Modem status interrupts
+        * are set via set_termios(), which will be occurring imminently
+        * anyway, so we don't enable them here.
+        * - M32R_SIO: 0x0c
+        * - M32R_PLDSIO: 0x04
+        */
+       up->ier = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+       sio_out(up, SIOTRCR, up->ier);
+
+       /*
+        * And clear the interrupt registers again for luck.
+        */
+       sio_reset();
+
+       return 0;
+}
+
+static void m32r_sio_shutdown(struct uart_port *port)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+       /*
+        * Disable interrupts from this port
+        */
+       up->ier = 0;
+       sio_out(up, SIOTRCR, 0);
+
+       /*
+        * Disable break condition and FIFOs
+        */
+
+       sio_init();
+
+       if (!is_real_interrupt(up->port.irq))
+               del_timer_sync(&up->timer);
+       else
+               serial_unlink_irq_chain(up);
+}
+
+static unsigned int m32r_sio_get_divisor(struct uart_port *port,
+       unsigned int baud)
+{
+       return uart_get_divisor(port, baud);
+}
+
+static void m32r_sio_set_termios(struct uart_port *port,
+       struct termios *termios, struct termios *old)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+       unsigned char cval = 0;
+       unsigned long flags;
+       unsigned int baud, quot;
+
+       switch (termios->c_cflag & CSIZE) {
+       case CS5:
+               cval = 0x00;
+               break;
+       case CS6:
+               cval = 0x01;
+               break;
+       case CS7:
+               cval = 0x02;
+               break;
+       default:
+       case CS8:
+               cval = 0x03;
+               break;
+       }
+
+       if (termios->c_cflag & CSTOPB)
+               cval |= 0x04;
+       if (termios->c_cflag & PARENB)
+               cval |= UART_LCR_PARITY;
+       if (!(termios->c_cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+       if (termios->c_cflag & CMSPAR)
+               cval |= UART_LCR_SPAR;
+#endif
+
+       /*
+        * Ask the core to calculate the divisor for us.
+        */
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4);
+#else
+       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+#endif
+       quot = m32r_sio_get_divisor(port, baud);
+
+       /*
+        * Ok, we're now changing the port state.  Do it with
+        * interrupts disabled.
+        */
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       sio_set_baud_rate(baud);
+
+       /*
+        * Update the per-port timeout.
+        */
+       uart_update_timeout(port, termios->c_cflag, baud);
+
+       up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+       if (termios->c_iflag & INPCK)
+               up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (termios->c_iflag & (BRKINT | PARMRK))
+               up->port.read_status_mask |= UART_LSR_BI;
+
+       /*
+        * Characteres to ignore
+        */
+       up->port.ignore_status_mask = 0;
+       if (termios->c_iflag & IGNPAR)
+               up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+       if (termios->c_iflag & IGNBRK) {
+               up->port.ignore_status_mask |= UART_LSR_BI;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too (for real raw support).
+                */
+               if (termios->c_iflag & IGNPAR)
+                       up->port.ignore_status_mask |= UART_LSR_OE;
+       }
+
+       /*
+        * ignore all characters if CREAD is not set
+        */
+       if ((termios->c_cflag & CREAD) == 0)
+               up->port.ignore_status_mask |= UART_LSR_DR;
+
+       /*
+        * CTS flow control flag and modem status interrupts
+        */
+       up->ier &= ~UART_IER_MSI;
+       if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+               up->ier |= UART_IER_MSI;
+
+       serial_out(up, UART_IER, up->ier);
+
+       up->lcr = cval;                                 /* Save LCR */
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void m32r_sio_pm(struct uart_port *port, unsigned int state,
+       unsigned int oldstate)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+       if (up->pm)
+               up->pm(port, state, oldstate);
+}
+
+/*
+ * Resource handling.  This is complicated by the fact that resources
+ * depend on the port type.  Maybe we should be claiming the standard
+ * 8250 ports, and then trying to get other resources as necessary?
+ */
+static int
+m32r_sio_request_std_resource(struct uart_sio_port *up, struct resource **res)
+{
+       unsigned int size = 8 << up->port.regshift;
+#ifndef CONFIG_SERIAL_M32R_PLDSIO
+       unsigned long start;
+#endif
+       int ret = 0;
+
+       switch (up->port.iotype) {
+       case SERIAL_IO_MEM:
+               if (up->port.mapbase) {
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+                       *res = request_mem_region(up->port.mapbase, size, "serial");
+#else
+                       start = up->port.mapbase;
+                       start += UART_RSA_BASE << up->port.regshift;
+                       *res = request_mem_region(start, size, "serial");
+#endif
+                       if (!*res)
+                               ret = -EBUSY;
+               }
+               break;
+
+       case SERIAL_IO_HUB6:
+       case SERIAL_IO_PORT:
+               *res = request_region(up->port.iobase, size, "serial");
+               if (!*res)
+                       ret = -EBUSY;
+               break;
+       }
+       return ret;
+}
+
+static int
+m32r_sio_request_rsa_resource(struct uart_sio_port *up, struct resource **res)
+{
+       unsigned int size = 8 << up->port.regshift;
+       unsigned long start;
+       int ret = 0;
+
+       switch (up->port.iotype) {
+       case SERIAL_IO_MEM:
+               if (up->port.mapbase) {
+                       start = up->port.mapbase;
+                       start += UART_RSA_BASE << up->port.regshift;
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+                       *res = request_mem_region(start, size, "serial-rsa");
+#else
+                       *res = request_mem_region(up->port.mapbase, size, "serial-rsa");
+#endif
+                       if (!*res)
+                               ret = -EBUSY;
+               }
+               break;
+
+       case SERIAL_IO_HUB6:
+       case SERIAL_IO_PORT:
+               start = up->port.iobase;
+               start += UART_RSA_BASE << up->port.regshift;
+               *res = request_region(up->port.iobase, size, "serial-rsa");
+               if (!*res)
+                       ret = -EBUSY;
+               break;
+       }
+
+       return ret;
+}
+
+static void m32r_sio_release_port(struct uart_port *port)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+       unsigned long start, offset = 0, size = 0;
+
+       if (up->port.type == PORT_RSA) {
+               offset = UART_RSA_BASE << up->port.regshift;
+               size = 8;
+       }
+
+       size <<= up->port.regshift;
+
+       switch (up->port.iotype) {
+       case SERIAL_IO_MEM:
+               if (up->port.mapbase) {
+                       /*
+                        * Unmap the area.
+                        */
+                       iounmap(up->port.membase);
+                       up->port.membase = NULL;
+
+                       start = up->port.mapbase;
+
+                       if (size)
+                               release_mem_region(start + offset, size);
+                       release_mem_region(start, 8 << up->port.regshift);
+               }
+               break;
+
+       case SERIAL_IO_HUB6:
+       case SERIAL_IO_PORT:
+               start = up->port.iobase;
+
+               if (size)
+                       release_region(start + offset, size);
+               release_region(start + offset, 8 << up->port.regshift);
+               break;
+
+       default:
+               break;
+       }
+}
+
+static int m32r_sio_request_port(struct uart_port *port)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+       struct resource *res = NULL, *res_rsa = NULL;
+       int ret = 0;
+
+       if (up->port.type == PORT_RSA){
+               ret = m32r_sio_request_rsa_resource(up, &res_rsa);
+               if (ret < 0)
+                       return ret;
+       }
+       ret = m32r_sio_request_std_resource(up, &res);
+
+       /*
+        * If we have a mapbase, then request that as well.
+        */
+       if (ret == 0 && up->port.flags & UPF_IOREMAP) {
+               int size = res->end - res->start + 1;
+
+               up->port.membase = ioremap(up->port.mapbase, size);
+               if (!up->port.membase)
+                       ret = -ENOMEM;
+       }
+
+       if (ret < 0) {
+               if (res_rsa)
+                       release_resource(res_rsa);
+               if (res)
+                       release_resource(res);
+       }
+       return ret;
+}
+
+static void m32r_sio_config_port(struct uart_port *port, int flags)
+{
+       struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       up->port.type = PORT_SIO;
+       up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int
+m32r_sio_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       if (ser->irq >= NR_IRQS || ser->irq < 0 ||
+           ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
+           ser->type > PORT_MAX_SIO || ser->type == PORT_CIRRUS ||
+           ser->type == PORT_STARTECH)
+               return -EINVAL;
+       return 0;
+}
+
+static const char *
+m32r_sio_type(struct uart_port *port)
+{
+       int type = port->type;
+
+       if (type >= ARRAY_SIZE(uart_config))
+               type = 0;
+       return uart_config[type].name;
+}
+
+static struct uart_ops m32r_sio_pops = {
+       .tx_empty       = m32r_sio_tx_empty,
+       .set_mctrl      = m32r_sio_set_mctrl,
+       .get_mctrl      = m32r_sio_get_mctrl,
+       .stop_tx        = m32r_sio_stop_tx,
+       .start_tx       = m32r_sio_start_tx,
+       .stop_rx        = m32r_sio_stop_rx,
+       .enable_ms      = m32r_sio_enable_ms,
+       .break_ctl      = m32r_sio_break_ctl,
+       .startup        = m32r_sio_startup,
+       .shutdown       = m32r_sio_shutdown,
+       .set_termios    = m32r_sio_set_termios,
+       .pm             = m32r_sio_pm,
+       .type           = m32r_sio_type,
+       .release_port   = m32r_sio_release_port,
+       .request_port   = m32r_sio_request_port,
+       .config_port    = m32r_sio_config_port,
+       .verify_port    = m32r_sio_verify_port,
+};
+
+static struct uart_sio_port m32r_sio_ports[UART_NR];
+
+static void __init m32r_sio_isa_init_ports(void)
+{
+       struct uart_sio_port *up;
+       static int first = 1;
+       int i;
+
+       if (!first)
+               return;
+       first = 0;
+
+       for (i = 0, up = m32r_sio_ports; i < ARRAY_SIZE(old_serial_port);
+            i++, up++) {
+               up->port.iobase   = old_serial_port[i].port;
+               up->port.irq      = irq_canonicalize(old_serial_port[i].irq);
+               up->port.uartclk  = old_serial_port[i].baud_base * 16;
+               up->port.flags    = old_serial_port[i].flags;
+               up->port.hub6     = old_serial_port[i].hub6;
+               up->port.membase  = old_serial_port[i].iomem_base;
+               up->port.iotype   = old_serial_port[i].io_type;
+               up->port.regshift = old_serial_port[i].iomem_reg_shift;
+               up->port.ops      = &m32r_sio_pops;
+               if (share_irqs_sio)
+                       up->port.flags |= UPF_SHARE_IRQ;
+       }
+}
+
+static void __init m32r_sio_register_ports(struct uart_driver *drv)
+{
+       int i;
+
+       m32r_sio_isa_init_ports();
+
+       for (i = 0; i < UART_NR; i++) {
+               struct uart_sio_port *up = &m32r_sio_ports[i];
+
+               up->port.line = i;
+               up->port.ops = &m32r_sio_pops;
+               init_timer(&up->timer);
+               up->timer.function = m32r_sio_timeout;
+
+               /*
+                * ALPHA_KLUDGE_MCR needs to be killed.
+                */
+               up->mcr_mask = ~ALPHA_KLUDGE_MCR;
+               up->mcr_force = ALPHA_KLUDGE_MCR;
+
+               uart_add_one_port(drv, &up->port);
+       }
+}
+
+#ifdef CONFIG_SERIAL_M32R_SIO_CONSOLE
+
+/*
+ *     Wait for transmitter & holding register to empty
+ */
+static inline void wait_for_xmitr(struct uart_sio_port *up)
+{
+       unsigned int status, tmout = 10000;
+
+       /* Wait up to 10ms for the character(s) to be sent. */
+       do {
+               status = sio_in(up, SIOSTS);
+
+               if (--tmout == 0)
+                       break;
+               udelay(1);
+       } while ((status & UART_EMPTY) != UART_EMPTY);
+
+       /* Wait up to 1s for flow control if necessary */
+       if (up->port.flags & UPF_CONS_FLOW) {
+               tmout = 1000000;
+               while (--tmout)
+                       udelay(1);
+       }
+}
+
+/*
+ *     Print a string to the serial port trying not to disturb
+ *     any possible real use of the port...
+ *
+ *     The console_lock must be held when we get here.
+ */
+static void m32r_sio_console_write(struct console *co, const char *s,
+       unsigned int count)
+{
+       struct uart_sio_port *up = &m32r_sio_ports[co->index];
+       unsigned int ier;
+       int i;
+
+       /*
+        *      First save the UER then disable the interrupts
+        */
+       ier = sio_in(up, SIOTRCR);
+       sio_out(up, SIOTRCR, 0);
+
+       /*
+        *      Now, do each character
+        */
+       for (i = 0; i < count; i++, s++) {
+               wait_for_xmitr(up);
+
+               /*
+                *      Send the character out.
+                *      If a LF, also do CR...
+                */
+               sio_out(up, SIOTXB, *s);
+
+               if (*s == 10) {
+                       wait_for_xmitr(up);
+                       sio_out(up, SIOTXB, 13);
+               }
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore the IER
+        */
+       wait_for_xmitr(up);
+       sio_out(up, SIOTRCR, ier);
+}
+
+static int __init m32r_sio_console_setup(struct console *co, char *options)
+{
+       struct uart_port *port;
+       int baud = 9600;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       /*
+        * Check whether an invalid uart number has been specified, and
+        * if so, search for the first available port that does have
+        * console support.
+        */
+       if (co->index >= UART_NR)
+               co->index = 0;
+       port = &m32r_sio_ports[co->index].port;
+
+       /*
+        * Temporary fix.
+        */
+       spin_lock_init(&port->lock);
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+       return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+extern struct uart_driver m32r_sio_reg;
+static struct console m32r_sio_console = {
+       .name           = "ttyS",
+       .write          = m32r_sio_console_write,
+       .device         = uart_console_device,
+       .setup          = m32r_sio_console_setup,
+       .flags          = CON_PRINTBUFFER,
+       .index          = -1,
+       .data           = &m32r_sio_reg,
+};
+
+static int __init m32r_sio_console_init(void)
+{
+       sio_reset();
+       sio_init();
+       m32r_sio_isa_init_ports();
+       register_console(&m32r_sio_console);
+       return 0;
+}
+console_initcall(m32r_sio_console_init);
+
+#define M32R_SIO_CONSOLE       &m32r_sio_console
+#else
+#define M32R_SIO_CONSOLE       NULL
+#endif
+
+static struct uart_driver m32r_sio_reg = {
+       .owner                  = THIS_MODULE,
+       .driver_name            = "sio",
+       .devfs_name             = "tts/",
+       .dev_name               = "ttyS",
+       .major                  = TTY_MAJOR,
+       .minor                  = 64,
+       .nr                     = UART_NR,
+       .cons                   = M32R_SIO_CONSOLE,
+};
+
+/*
+ * register_serial and unregister_serial allows for 16x50 serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ */
+
+static int __register_m32r_sio(struct serial_struct *req, int line)
+{
+       struct uart_port port;
+
+       port.iobase   = req->port;
+       port.membase  = req->iomem_base;
+       port.irq      = req->irq;
+       port.uartclk  = req->baud_base * 16;
+       port.fifosize = req->xmit_fifo_size;
+       port.regshift = req->iomem_reg_shift;
+       port.iotype   = req->io_type;
+       port.flags    = req->flags | UPF_BOOT_AUTOCONF;
+       port.mapbase  = req->iomap_base;
+       port.line     = line;
+
+       if (share_irqs_sio)
+               port.flags |= UPF_SHARE_IRQ;
+
+       if (HIGH_BITS_OFFSET)
+               port.iobase |= (long) req->port_high << HIGH_BITS_OFFSET;
+
+       /*
+        * If a clock rate wasn't specified by the low level
+        * driver, then default to the standard clock rate.
+        */
+       if (port.uartclk == 0)
+               port.uartclk = BASE_BAUD * 16;
+
+       return uart_register_port(&m32r_sio_reg, &port);
+}
+
+/**
+ *     register_serial - configure a 16x50 serial port at runtime
+ *     @req: request structure
+ *
+ *     Configure the serial port specified by the request. If the
+ *     port exists and is in use an error is returned. If the port
+ *     is not currently in the table it is added.
+ *
+ *     The port is then probed and if necessary the IRQ is autodetected
+ *     If this fails an error is returned.
+ *
+ *     On success the port is ready to use and the line number is returned.
+ */
+int register_m32r_sio(struct serial_struct *req)
+{
+       return __register_m32r_sio(req, -1);
+}
+
+int __init early_m32r_sio_setup(struct uart_port *port)
+{
+       m32r_sio_isa_init_ports();
+       m32r_sio_ports[port->line].port = *port;
+       m32r_sio_ports[port->line].port.ops = &m32r_sio_pops;
+
+       return 0;
+}
+
+/**
+ *     unregister_serial - remove a 16x50 serial port at runtime
+ *     @line: serial line number
+ *
+ *     Remove one serial port.  This may be called from interrupt
+ *     context.
+ */
+void unregister_m32r_sio(int line)
+{
+       uart_unregister_port(&m32r_sio_reg, line);
+}
+
+/*
+ * This is for ISAPNP only.
+ */
+void m32r_sio_get_irq_map(unsigned int *map)
+{
+       int i;
+
+       for (i = 0; i < UART_NR; i++) {
+               if (m32r_sio_ports[i].port.type != PORT_UNKNOWN &&
+                   m32r_sio_ports[i].port.irq < 16)
+                       *map |= 1 << m32r_sio_ports[i].port.irq;
+       }
+}
+
+/**
+ *     m32r_sio_suspend_port - suspend one serial port
+ *     @line: serial line number
+ *
+ *     Suspend one serial port.
+ */
+void m32r_sio_suspend_port(int line)
+{
+       uart_suspend_port(&m32r_sio_reg, &m32r_sio_ports[line].port);
+}
+
+/**
+ *     m32r_sio_resume_port - resume one serial port
+ *     @line: serial line number
+ *
+ *     Resume one serial port.
+ */
+void m32r_sio_resume_port(int line)
+{
+       uart_resume_port(&m32r_sio_reg, &m32r_sio_ports[line].port);
+}
+
+static int __init m32r_sio_init(void)
+{
+       int ret, i;
+
+       printk(KERN_INFO "Serial: M32R SIO driver $Revision: 1.9 $ "
+               "IRQ sharing %sabled\n", share_irqs_sio ? "en" : "dis");
+
+       for (i = 0; i < NR_IRQS; i++)
+               spin_lock_init(&irq_lists[i].lock);
+
+       ret = uart_register_driver(&m32r_sio_reg);
+       if (ret >= 0)
+               m32r_sio_register_ports(&m32r_sio_reg);
+
+       return ret;
+}
+
+static void __exit m32r_sio_exit(void)
+{
+       int i;
+
+       for (i = 0; i < UART_NR; i++)
+               uart_remove_one_port(&m32r_sio_reg, &m32r_sio_ports[i].port);
+
+       uart_unregister_driver(&m32r_sio_reg);
+}
+
+module_init(m32r_sio_init);
+module_exit(m32r_sio_exit);
+
+EXPORT_SYMBOL(register_m32r_sio);
+EXPORT_SYMBOL(unregister_m32r_sio);
+EXPORT_SYMBOL(m32r_sio_get_irq_map);
+EXPORT_SYMBOL(m32r_sio_suspend_port);
+EXPORT_SYMBOL(m32r_sio_resume_port);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic M32R SIO serial driver $Revision: 1.9 $");
+
+module_param(share_irqs_sio, bool, 0400);
+MODULE_PARM_DESC(share_irqs_sio, "Share IRQs with other non-M32R SIO devices"
+       " (unsafe)");
diff --git a/drivers/serial/m32r_sio.h b/drivers/serial/m32r_sio.h
new file mode 100644 (file)
index 0000000..f957c76
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *  m32r_sio.h
+ *
+ *  Driver for M32R serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *  Based on drivers/serial/8250.h.
+ *
+ *  Copyright (C) 2001  Russell King.
+ *  Copyright (C) 2004  Hirokazu Takata <takata at linux-m32r.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 <linux/config.h>
+
+struct m32r_sio_probe {
+       struct module   *owner;
+       int             (*pci_init_one)(struct pci_dev *dev);
+       void            (*pci_remove_one)(struct pci_dev *dev);
+       void            (*pnp_init)(void);
+};
+
+int m32r_sio_register_probe(struct m32r_sio_probe *probe);
+void m32r_sio_unregister_probe(struct m32r_sio_probe *probe);
+void m32r_sio_get_irq_map(unsigned int *map);
+void m32r_sio_suspend_port(int line);
+void m32r_sio_resume_port(int line);
+
+struct old_serial_port {
+       unsigned int uart;
+       unsigned int baud_base;
+       unsigned int port;
+       unsigned int irq;
+       unsigned int flags;
+       unsigned char hub6;
+       unsigned char io_type;
+       unsigned char *iomem_base;
+       unsigned short iomem_reg_shift;
+};
+
+#define _INLINE_ inline
+
+#define PROBE_RSA      (1 << 0)
+#define PROBE_ANY      (~0)
+
+#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
+
+#ifdef CONFIG_SERIAL_SIO_SHARE_IRQ
+#define M32R_SIO_SHARE_IRQS 1
+#else
+#define M32R_SIO_SHARE_IRQS 0
+#endif
diff --git a/drivers/serial/m32r_sio_reg.h b/drivers/serial/m32r_sio_reg.h
new file mode 100644 (file)
index 0000000..d940255
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * m32r_sio_reg.h
+ *
+ * Copyright (C) 1992, 1994 by Theodore Ts'o.
+ * Copyright (C) 2004  Hirokazu Takata <takata at linux-m32r.org>
+ *
+ * Redistribution of this file is permitted under the terms of the GNU
+ * Public License (GPL)
+ *
+ * These are the UART port assignments, expressed as offsets from the base
+ * register.  These assignments should hold for any serial port based on
+ * a 8250, 16450, or 16550(A).
+ */
+
+#ifndef _M32R_SIO_REG_H
+#define _M32R_SIO_REG_H
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+
+#define SIOCR          0x000
+#define SIOMOD0                0x002
+#define SIOMOD1                0x004
+#define SIOSTS         0x006
+#define SIOTRCR                0x008
+#define SIOBAUR                0x00a
+// #define SIORBAUR    0x018
+#define SIOTXB         0x00c
+#define SIORXB         0x00e
+
+#define UART_RX                ((unsigned long) PLD_ESIO0RXB)
+                               /* In:  Receive buffer (DLAB=0) */
+#define UART_TX                ((unsigned long) PLD_ESIO0TXB)
+                               /* Out: Transmit buffer (DLAB=0) */
+#define UART_DLL       0       /* Out: Divisor Latch Low (DLAB=1) */
+#define UART_TRG       0       /* (LCR=BF) FCTR bit 7 selects Rx or Tx
+                                * In: Fifo count
+                                * Out: Fifo custom trigger levels
+                                * XR16C85x only */
+
+#define UART_DLM       0       /* Out: Divisor Latch High (DLAB=1) */
+#define UART_IER       ((unsigned long) PLD_ESIO0INTCR)
+                               /* Out: Interrupt Enable Register */
+#define UART_FCTR      0       /* (LCR=BF) Feature Control Register
+                                * XR16C85x only */
+
+#define UART_IIR       0       /* In:  Interrupt ID Register */
+#define UART_FCR       0       /* Out: FIFO Control Register */
+#define UART_EFR       0       /* I/O: Extended Features Register */
+                               /* (DLAB=1, 16C660 only) */
+
+#define UART_LCR       0       /* Out: Line Control Register */
+#define UART_MCR       0       /* Out: Modem Control Register */
+#define UART_LSR       ((unsigned long) PLD_ESIO0STS)
+                               /* In:  Line Status Register */
+#define UART_MSR       0       /* In:  Modem Status Register */
+#define UART_SCR       0       /* I/O: Scratch Register */
+#define UART_EMSR      0       /* (LCR=BF) Extended Mode Select Register
+                                * FCTR bit 6 selects SCR or EMSR
+                                * XR16c85x only */
+
+#else /* not CONFIG_SERIAL_M32R_PLDSIO */
+
+#define SIOCR          0x000
+#define SIOMOD0                0x004
+#define SIOMOD1                0x008
+#define SIOSTS         0x00c
+#define SIOTRCR                0x010
+#define SIOBAUR                0x014
+#define SIORBAUR       0x018
+#define SIOTXB         0x01c
+#define SIORXB         0x020
+
+#define UART_RX                M32R_SIO0_RXB_PORTL     /* In:  Receive buffer (DLAB=0) */
+#define UART_TX                M32R_SIO0_TXB_PORTL     /* Out: Transmit buffer (DLAB=0) */
+#define UART_DLL       0       /* Out: Divisor Latch Low (DLAB=1) */
+#define UART_TRG       0       /* (LCR=BF) FCTR bit 7 selects Rx or Tx
+                                * In: Fifo count
+                                * Out: Fifo custom trigger levels
+                                * XR16C85x only */
+
+#define UART_DLM       0       /* Out: Divisor Latch High (DLAB=1) */
+#define UART_IER       M32R_SIO0_TRCR_PORTL    /* Out: Interrupt Enable Register */
+#define UART_FCTR      0       /* (LCR=BF) Feature Control Register
+                                * XR16C85x only */
+
+#define UART_IIR       0       /* In:  Interrupt ID Register */
+#define UART_FCR       0       /* Out: FIFO Control Register */
+#define UART_EFR       0       /* I/O: Extended Features Register */
+                               /* (DLAB=1, 16C660 only) */
+
+#define UART_LCR       0       /* Out: Line Control Register */
+#define UART_MCR       0       /* Out: Modem Control Register */
+#define UART_LSR       M32R_SIO0_STS_PORTL     /* In:  Line Status Register */
+#define UART_MSR       0       /* In:  Modem Status Register */
+#define UART_SCR       0       /* I/O: Scratch Register */
+#define UART_EMSR      0       /* (LCR=BF) Extended Mode Select Register
+                                * FCTR bit 6 selects SCR or EMSR
+                                * XR16c85x only */
+
+#endif /* CONFIG_SERIAL_M32R_PLDSIO */
+
+#define UART_EMPTY     (UART_LSR_TEMT | UART_LSR_THRE)
+
+/*
+ * These are the definitions for the FIFO Control Register
+ * (16650 only)
+ */
+#define UART_FCR_ENABLE_FIFO   0x01 /* Enable the FIFO */
+#define UART_FCR_CLEAR_RCVR    0x02 /* Clear the RCVR FIFO */
+#define UART_FCR_CLEAR_XMIT    0x04 /* Clear the XMIT FIFO */
+#define UART_FCR_DMA_SELECT    0x08 /* For DMA applications */
+#define UART_FCR_TRIGGER_MASK  0xC0 /* Mask for the FIFO trigger range */
+#define UART_FCR_TRIGGER_1     0x00 /* Mask for trigger set at 1 */
+#define UART_FCR_TRIGGER_4     0x40 /* Mask for trigger set at 4 */
+#define UART_FCR_TRIGGER_8     0x80 /* Mask for trigger set at 8 */
+#define UART_FCR_TRIGGER_14    0xC0 /* Mask for trigger set at 14 */
+/* 16650 redefinitions */
+#define UART_FCR6_R_TRIGGER_8  0x00 /* Mask for receive trigger set at 1 */
+#define UART_FCR6_R_TRIGGER_16 0x40 /* Mask for receive trigger set at 4 */
+#define UART_FCR6_R_TRIGGER_24  0x80 /* Mask for receive trigger set at 8 */
+#define UART_FCR6_R_TRIGGER_28 0xC0 /* Mask for receive trigger set at 14 */
+#define UART_FCR6_T_TRIGGER_16 0x00 /* Mask for transmit trigger set at 16 */
+#define UART_FCR6_T_TRIGGER_8  0x10 /* Mask for transmit trigger set at 8 */
+#define UART_FCR6_T_TRIGGER_24  0x20 /* Mask for transmit trigger set at 24 */
+#define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */
+/* TI 16750 definitions */
+#define UART_FCR7_64BYTE       0x20 /* Go into 64 byte mode */
+
+/*
+ * These are the definitions for the Line Control Register
+ *
+ * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting
+ * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits.
+ */
+#define UART_LCR_DLAB  0x80    /* Divisor latch access bit */
+#define UART_LCR_SBC   0x40    /* Set break control */
+#define UART_LCR_SPAR  0x20    /* Stick parity (?) */
+#define UART_LCR_EPAR  0x10    /* Even parity select */
+#define UART_LCR_PARITY        0x08    /* Parity Enable */
+#define UART_LCR_STOP  0x04    /* Stop bits: 0=1 stop bit, 1= 2 stop bits */
+#define UART_LCR_WLEN5  0x00   /* Wordlength: 5 bits */
+#define UART_LCR_WLEN6  0x01   /* Wordlength: 6 bits */
+#define UART_LCR_WLEN7  0x02   /* Wordlength: 7 bits */
+#define UART_LCR_WLEN8  0x03   /* Wordlength: 8 bits */
+
+/*
+ * These are the definitions for the Line Status Register
+ */
+#define UART_LSR_TEMT  0x02    /* Transmitter empty */
+#define UART_LSR_THRE  0x01    /* Transmit-hold-register empty */
+#define UART_LSR_BI    0x00    /* Break interrupt indicator */
+#define UART_LSR_FE    0x80    /* Frame error indicator */
+#define UART_LSR_PE    0x40    /* Parity error indicator */
+#define UART_LSR_OE    0x20    /* Overrun error indicator */
+#define UART_LSR_DR    0x04    /* Receiver data ready */
+
+/*
+ * These are the definitions for the Interrupt Identification Register
+ */
+#define UART_IIR_NO_INT        0x01    /* No interrupts pending */
+#define UART_IIR_ID    0x06    /* Mask for the interrupt ID */
+
+#define UART_IIR_MSI   0x00    /* Modem status interrupt */
+#define UART_IIR_THRI  0x02    /* Transmitter holding register empty */
+#define UART_IIR_RDI   0x04    /* Receiver data interrupt */
+#define UART_IIR_RLSI  0x06    /* Receiver line status interrupt */
+
+/*
+ * These are the definitions for the Interrupt Enable Register
+ */
+#define UART_IER_MSI   0x00    /* Enable Modem status interrupt */
+#define UART_IER_RLSI  0x08    /* Enable receiver line status interrupt */
+#define UART_IER_THRI  0x03    /* Enable Transmitter holding register int. */
+#define UART_IER_RDI   0x04    /* Enable receiver data interrupt */
+/*
+ * Sleep mode for ST16650 and TI16750.
+ * Note that for 16650, EFR-bit 4 must be selected as well.
+ */
+#define UART_IERX_SLEEP  0x10  /* Enable sleep mode */
+
+/*
+ * These are the definitions for the Modem Control Register
+ */
+#define UART_MCR_LOOP  0x10    /* Enable loopback test mode */
+#define UART_MCR_OUT2  0x08    /* Out2 complement */
+#define UART_MCR_OUT1  0x04    /* Out1 complement */
+#define UART_MCR_RTS   0x02    /* RTS complement */
+#define UART_MCR_DTR   0x01    /* DTR complement */
+
+/*
+ * These are the definitions for the Modem Status Register
+ */
+#define UART_MSR_DCD   0x80    /* Data Carrier Detect */
+#define UART_MSR_RI    0x40    /* Ring Indicator */
+#define UART_MSR_DSR   0x20    /* Data Set Ready */
+#define UART_MSR_CTS   0x10    /* Clear to Send */
+#define UART_MSR_DDCD  0x08    /* Delta DCD */
+#define UART_MSR_TERI  0x04    /* Trailing edge ring indicator */
+#define UART_MSR_DDSR  0x02    /* Delta DSR */
+#define UART_MSR_DCTS  0x01    /* Delta CTS */
+#define UART_MSR_ANY_DELTA 0x0F        /* Any of the delta bits! */
+
+/*
+ * These are the definitions for the Extended Features Register
+ * (StarTech 16C660 only, when DLAB=1)
+ */
+#define UART_EFR_CTS   0x80    /* CTS flow control */
+#define UART_EFR_RTS   0x40    /* RTS flow control */
+#define UART_EFR_SCD   0x20    /* Special character detect */
+#define UART_EFR_ECB   0x10    /* Enhanced control bit */
+/*
+ * the low four bits control software flow control
+ */
+
+/*
+ * These register definitions are for the 16C950
+ */
+#define UART_ASR       0x01    /* Additional Status Register */
+#define UART_RFL       0x03    /* Receiver FIFO level */
+#define UART_TFL       0x04    /* Transmitter FIFO level */
+#define UART_ICR       0x05    /* Index Control Register */
+
+/* The 16950 ICR registers */
+#define UART_ACR       0x00    /* Additional Control Register */
+#define UART_CPR       0x01    /* Clock Prescalar Register */
+#define UART_TCR       0x02    /* Times Clock Register */
+#define UART_CKS       0x03    /* Clock Select Register */
+#define UART_TTL       0x04    /* Transmitter Interrupt Trigger Level */
+#define UART_RTL       0x05    /* Receiver Interrupt Trigger Level */
+#define UART_FCL       0x06    /* Flow Control Level Lower */
+#define UART_FCH       0x07    /* Flow Control Level Higher */
+#define UART_ID1       0x08    /* ID #1 */
+#define UART_ID2       0x09    /* ID #2 */
+#define UART_ID3       0x0A    /* ID #3 */
+#define UART_REV       0x0B    /* Revision */
+#define UART_CSR       0x0C    /* Channel Software Reset */
+#define UART_NMR       0x0D    /* Nine-bit Mode Register */
+#define UART_CTR       0xFF
+
+/*
+ * The 16C950 Additional Control Reigster
+ */
+#define UART_ACR_RXDIS 0x01    /* Receiver disable */
+#define UART_ACR_TXDIS 0x02    /* Receiver disable */
+#define UART_ACR_DSRFC 0x04    /* DSR Flow Control */
+#define UART_ACR_TLENB 0x20    /* 950 trigger levels enable */
+#define UART_ACR_ICRRD 0x40    /* ICR Read enable */
+#define UART_ACR_ASREN 0x80    /* Additional status enable */
+
+/*
+ * These are the definitions for the Feature Control Register
+ * (XR16C85x only, when LCR=bf; doubles with the Interrupt Enable
+ * Register, UART register #1)
+ */
+#define UART_FCTR_RTS_NODELAY  0x00  /* RTS flow control delay */
+#define UART_FCTR_RTS_4DELAY   0x01
+#define UART_FCTR_RTS_6DELAY   0x02
+#define UART_FCTR_RTS_8DELAY   0x03
+#define UART_FCTR_IRDA 0x04  /* IrDa data encode select */
+#define UART_FCTR_TX_INT       0x08  /* Tx interrupt type select */
+#define UART_FCTR_TRGA 0x00  /* Tx/Rx 550 trigger table select */
+#define UART_FCTR_TRGB 0x10  /* Tx/Rx 650 trigger table select */
+#define UART_FCTR_TRGC 0x20  /* Tx/Rx 654 trigger table select */
+#define UART_FCTR_TRGD 0x30  /* Tx/Rx 850 programmable trigger select */
+#define UART_FCTR_SCR_SWAP     0x40  /* Scratch pad register swap */
+#define UART_FCTR_RX   0x00  /* Programmable trigger mode select */
+#define UART_FCTR_TX   0x80  /* Programmable trigger mode select */
+
+/*
+ * These are the definitions for the Enhanced Mode Select Register
+ * (XR16C85x only, when LCR=bf and FCTR bit 6=1; doubles with the
+ * Scratch register, UART register #7)
+ */
+#define UART_EMSR_FIFO_COUNT   0x01  /* Rx/Tx select */
+#define UART_EMSR_ALT_COUNT    0x02  /* Alternating count select */
+
+/*
+ * These are the definitions for the Programmable Trigger
+ * Register (XR16C85x only, when LCR=bf; doubles with the UART RX/TX
+ * register, UART register #0)
+ */
+#define UART_TRG_1     0x01
+#define UART_TRG_4     0x04
+#define UART_TRG_8     0x08
+#define UART_TRG_16    0x10
+#define UART_TRG_32    0x20
+#define UART_TRG_64    0x40
+#define UART_TRG_96    0x60
+#define UART_TRG_120   0x78
+#define UART_TRG_128   0x80
+
+/*
+ * These definitions are for the RSA-DV II/S card, from
+ *
+ * Kiyokazu SUTO <suto@ks-and-ks.ne.jp>
+ */
+
+#define UART_RSA_BASE (-8)
+
+#define UART_RSA_MSR ((UART_RSA_BASE) + 0) /* I/O: Mode Select Register */
+
+#define UART_RSA_MSR_SWAP (1 << 0) /* Swap low/high 8 bytes in I/O port addr */
+#define UART_RSA_MSR_FIFO (1 << 2) /* Enable the external FIFO */
+#define UART_RSA_MSR_FLOW (1 << 3) /* Enable the auto RTS/CTS flow control */
+#define UART_RSA_MSR_ITYP (1 << 4) /* Level (1) / Edge triger (0) */
+
+#define UART_RSA_IER ((UART_RSA_BASE) + 1) /* I/O: Interrupt Enable Register */
+
+#define UART_RSA_IER_Rx_FIFO_H (1 << 0) /* Enable Rx FIFO half full int. */
+#define UART_RSA_IER_Tx_FIFO_H (1 << 1) /* Enable Tx FIFO half full int. */
+#define UART_RSA_IER_Tx_FIFO_E (1 << 2) /* Enable Tx FIFO empty int. */
+#define UART_RSA_IER_Rx_TOUT (1 << 3) /* Enable char receive timeout int */
+#define UART_RSA_IER_TIMER (1 << 4) /* Enable timer interrupt */
+
+#define UART_RSA_SRR ((UART_RSA_BASE) + 2) /* IN: Status Read Register */
+
+#define UART_RSA_SRR_Tx_FIFO_NEMP (1 << 0) /* Tx FIFO is not empty (1) */
+#define UART_RSA_SRR_Tx_FIFO_NHFL (1 << 1) /* Tx FIFO is not half full (1) */
+#define UART_RSA_SRR_Tx_FIFO_NFUL (1 << 2) /* Tx FIFO is not full (1) */
+#define UART_RSA_SRR_Rx_FIFO_NEMP (1 << 3) /* Rx FIFO is not empty (1) */
+#define UART_RSA_SRR_Rx_FIFO_NHFL (1 << 4) /* Rx FIFO is not half full (1) */
+#define UART_RSA_SRR_Rx_FIFO_NFUL (1 << 5) /* Rx FIFO is not full (1) */
+#define UART_RSA_SRR_Rx_TOUT (1 << 6) /* Character reception timeout occurred (1) */
+#define UART_RSA_SRR_TIMER (1 << 7) /* Timer interrupt occurred */
+
+#define UART_RSA_FRR ((UART_RSA_BASE) + 2) /* OUT: FIFO Reset Register */
+
+#define UART_RSA_TIVSR ((UART_RSA_BASE) + 3) /* I/O: Timer Interval Value Set Register */
+
+#define UART_RSA_TCR ((UART_RSA_BASE) + 4) /* OUT: Timer Control Register */
+
+#define UART_RSA_TCR_SWITCH (1 << 0) /* Timer on */
+
+/*
+ * The RSA DSV/II board has two fixed clock frequencies.  One is the
+ * standard rate, and the other is 8 times faster.
+ */
+#define SERIAL_RSA_BAUD_BASE (921600)
+#define SERIAL_RSA_BAUD_BASE_LO (SERIAL_RSA_BAUD_BASE / 8)
+
+#endif /* _M32R_SIO_REG_H */
diff --git a/drivers/serial/mpsc.c b/drivers/serial/mpsc.c
new file mode 100644 (file)
index 0000000..d0dfc3c
--- /dev/null
@@ -0,0 +1,1832 @@
+/*
+ * drivers/serial/mpsc.c
+ *
+ * Generic driver for the MPSC (UART mode) on Marvell parts (e.g., GT64240,
+ * GT64260, MV64340, MV64360, GT96100, ... ).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * Based on an old MPSC driver that was in the linuxppc tree.  It appears to
+ * have been created by Chris Zankel (formerly of MontaVista) but there
+ * is no proper Copyright so I'm not sure.  Apparently, parts were also
+ * taken from PPCBoot (now U-Boot).  Also based on drivers/serial/8250.c
+ * by Russell King.
+ *
+ * 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.
+ */
+/*
+ * The MPSC interface is much like a typical network controller's interface.
+ * That is, you set up separate rings of descriptors for transmitting and
+ * receiving data.  There is also a pool of buffers with (one buffer per
+ * descriptor) that incoming data are dma'd into or outgoing data are dma'd
+ * out of.
+ *
+ * The MPSC requires two other controllers to be able to work.  The Baud Rate
+ * Generator (BRG) provides a clock at programmable frequencies which determines
+ * the baud rate.  The Serial DMA Controller (SDMA) takes incoming data from the
+ * MPSC and DMA's it into memory or DMA's outgoing data and passes it to the
+ * MPSC.  It is actually the SDMA interrupt that the driver uses to keep the
+ * transmit and receive "engines" going (i.e., indicate data has been
+ * transmitted or received).
+ *
+ * NOTES:
+ *
+ * 1) Some chips have an erratum where several regs cannot be
+ * read.  To work around that, we keep a local copy of those regs in
+ * 'mpsc_port_info'.
+ *
+ * 2) Some chips have an erratum where the ctlr will hang when the SDMA ctlr
+ * accesses system mem with coherency enabled.  For that reason, the driver
+ * assumes that coherency for that ctlr has been disabled.  This means
+ * that when in a cache coherent system, the driver has to manually manage
+ * the data cache on the areas that it touches because the dma_* macro are
+ * basically no-ops.
+ *
+ * 3) There is an erratum (on PPC) where you can't use the instruction to do
+ * a DMA_TO_DEVICE/cache clean so DMA_BIDIRECTIONAL/flushes are used in places
+ * where a DMA_TO_DEVICE/clean would have [otherwise] sufficed.
+ *
+ * 4) AFAICT, hardware flow control isn't supported by the controller --MAG.
+ */
+
+#include "mpsc.h"
+
+/*
+ * Define how this driver is known to the outside (we've been assigned a
+ * range on the "Low-density serial ports" major).
+ */
+#define MPSC_MAJOR             204
+#define MPSC_MINOR_START       44
+#define        MPSC_DRIVER_NAME        "MPSC"
+#define        MPSC_DEVFS_NAME         "ttymm/"
+#define        MPSC_DEV_NAME           "ttyMM"
+#define        MPSC_VERSION            "1.00"
+
+static struct mpsc_port_info mpsc_ports[MPSC_NUM_CTLRS];
+static struct mpsc_shared_regs mpsc_shared_regs;
+
+/*
+ ******************************************************************************
+ *
+ * Baud Rate Generator Routines (BRG)
+ *
+ ******************************************************************************
+ */
+static void
+mpsc_brg_init(struct mpsc_port_info *pi, u32 clk_src)
+{
+       u32     v;
+
+       v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
+       v = (v & ~(0xf << 18)) | ((clk_src & 0xf) << 18);
+
+       if (pi->brg_can_tune)
+               v &= ~(1 << 25);
+
+       if (pi->mirror_regs)
+               pi->BRG_BCR_m = v;
+       writel(v, pi->brg_base + BRG_BCR);
+
+       writel(readl(pi->brg_base + BRG_BTR) & 0xffff0000,
+               pi->brg_base + BRG_BTR);
+       return;
+}
+
+static void
+mpsc_brg_enable(struct mpsc_port_info *pi)
+{
+       u32     v;
+
+       v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
+       v |= (1 << 16);
+
+       if (pi->mirror_regs)
+               pi->BRG_BCR_m = v;
+       writel(v, pi->brg_base + BRG_BCR);
+       return;
+}
+
+static void
+mpsc_brg_disable(struct mpsc_port_info *pi)
+{
+       u32     v;
+
+       v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
+       v &= ~(1 << 16);
+
+       if (pi->mirror_regs)
+               pi->BRG_BCR_m = v;
+       writel(v, pi->brg_base + BRG_BCR);
+       return;
+}
+
+static inline void
+mpsc_set_baudrate(struct mpsc_port_info *pi, u32 baud)
+{
+       /*
+        * To set the baud, we adjust the CDV field in the BRG_BCR reg.
+        * From manual: Baud = clk / ((CDV+1)*2) ==> CDV = (clk / (baud*2)) - 1.
+        * However, the input clock is divided by 16 in the MPSC b/c of how
+        * 'MPSC_MMCRH' was set up so we have to divide the 'clk' used in our
+        * calculation by 16 to account for that.  So the real calculation
+        * that accounts for the way the mpsc is set up is:
+        * CDV = (clk / (baud*2*16)) - 1 ==> CDV = (clk / (baud << 5)) - 1.
+        */
+       u32     cdv = (pi->port.uartclk / (baud << 5)) - 1;
+       u32     v;
+
+       mpsc_brg_disable(pi);
+       v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
+       v = (v & 0xffff0000) | (cdv & 0xffff);
+
+       if (pi->mirror_regs)
+               pi->BRG_BCR_m = v;
+       writel(v, pi->brg_base + BRG_BCR);
+       mpsc_brg_enable(pi);
+
+       return;
+}
+
+/*
+ ******************************************************************************
+ *
+ * Serial DMA Routines (SDMA)
+ *
+ ******************************************************************************
+ */
+
+static void
+mpsc_sdma_burstsize(struct mpsc_port_info *pi, u32 burst_size)
+{
+       u32     v;
+
+       pr_debug("mpsc_sdma_burstsize[%d]: burst_size: %d\n",
+           pi->port.line, burst_size);
+
+       burst_size >>= 3; /* Divide by 8 b/c reg values are 8-byte chunks */
+
+       if (burst_size < 2)
+               v = 0x0;        /* 1 64-bit word */
+       else if (burst_size < 4)
+               v = 0x1;        /* 2 64-bit words */
+       else if (burst_size < 8)
+               v = 0x2;        /* 4 64-bit words */
+       else
+               v = 0x3;        /* 8 64-bit words */
+
+       writel((readl(pi->sdma_base + SDMA_SDC) & (0x3 << 12)) | (v << 12),
+               pi->sdma_base + SDMA_SDC);
+       return;
+}
+
+static void
+mpsc_sdma_init(struct mpsc_port_info *pi, u32 burst_size)
+{
+       pr_debug("mpsc_sdma_init[%d]: burst_size: %d\n", pi->port.line,
+               burst_size);
+
+       writel((readl(pi->sdma_base + SDMA_SDC) & 0x3ff) | 0x03f,
+               pi->sdma_base + SDMA_SDC);
+       mpsc_sdma_burstsize(pi, burst_size);
+       return;
+}
+
+static inline u32
+mpsc_sdma_intr_mask(struct mpsc_port_info *pi, u32 mask)
+{
+       u32     old, v;
+
+       pr_debug("mpsc_sdma_intr_mask[%d]: mask: 0x%x\n", pi->port.line, mask);
+
+       old = v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m :
+               readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
+
+       mask &= 0xf;
+       if (pi->port.line)
+               mask <<= 8;
+       v &= ~mask;
+
+       if (pi->mirror_regs)
+               pi->shared_regs->SDMA_INTR_MASK_m = v;
+       writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
+
+       if (pi->port.line)
+               old >>= 8;
+       return old & 0xf;
+}
+
+static inline void
+mpsc_sdma_intr_unmask(struct mpsc_port_info *pi, u32 mask)
+{
+       u32     v;
+
+       pr_debug("mpsc_sdma_intr_unmask[%d]: mask: 0x%x\n", pi->port.line,mask);
+
+       v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m :
+               readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
+
+       mask &= 0xf;
+       if (pi->port.line)
+               mask <<= 8;
+       v |= mask;
+
+       if (pi->mirror_regs)
+               pi->shared_regs->SDMA_INTR_MASK_m = v;
+       writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
+       return;
+}
+
+static inline void
+mpsc_sdma_intr_ack(struct mpsc_port_info *pi)
+{
+       pr_debug("mpsc_sdma_intr_ack[%d]: Acknowledging IRQ\n", pi->port.line);
+
+       if (pi->mirror_regs)
+               pi->shared_regs->SDMA_INTR_CAUSE_m = 0;
+       writel(0, pi->shared_regs->sdma_intr_base + SDMA_INTR_CAUSE);
+       return;
+}
+
+static inline void
+mpsc_sdma_set_rx_ring(struct mpsc_port_info *pi, struct mpsc_rx_desc *rxre_p)
+{
+       pr_debug("mpsc_sdma_set_rx_ring[%d]: rxre_p: 0x%x\n",
+               pi->port.line, (u32) rxre_p);
+
+       writel((u32)rxre_p, pi->sdma_base + SDMA_SCRDP);
+       return;
+}
+
+static inline void
+mpsc_sdma_set_tx_ring(struct mpsc_port_info *pi, struct mpsc_tx_desc *txre_p)
+{
+       writel((u32)txre_p, pi->sdma_base + SDMA_SFTDP);
+       writel((u32)txre_p, pi->sdma_base + SDMA_SCTDP);
+       return;
+}
+
+static inline void
+mpsc_sdma_cmd(struct mpsc_port_info *pi, u32 val)
+{
+       u32     v;
+
+       v = readl(pi->sdma_base + SDMA_SDCM);
+       if (val)
+               v |= val;
+       else
+               v = 0;
+       wmb();
+       writel(v, pi->sdma_base + SDMA_SDCM);
+       wmb();
+       return;
+}
+
+static inline uint
+mpsc_sdma_tx_active(struct mpsc_port_info *pi)
+{
+       return readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_TXD;
+}
+
+static inline void
+mpsc_sdma_start_tx(struct mpsc_port_info *pi)
+{
+       struct mpsc_tx_desc *txre, *txre_p;
+
+       /* If tx isn't running & there's a desc ready to go, start it */
+       if (!mpsc_sdma_tx_active(pi)) {
+               txre = (struct mpsc_tx_desc *)(pi->txr +
+                       (pi->txr_tail * MPSC_TXRE_SIZE));
+               dma_cache_sync((void *) txre, MPSC_TXRE_SIZE, DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+               if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+                       invalidate_dcache_range((ulong)txre,
+                               (ulong)txre + MPSC_TXRE_SIZE);
+#endif
+
+               if (be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O) {
+                       txre_p = (struct mpsc_tx_desc *)(pi->txr_p +
+                                                        (pi->txr_tail *
+                                                         MPSC_TXRE_SIZE));
+
+                       mpsc_sdma_set_tx_ring(pi, txre_p);
+                       mpsc_sdma_cmd(pi, SDMA_SDCM_STD | SDMA_SDCM_TXD);
+               }
+       }
+
+       return;
+}
+
+static inline void
+mpsc_sdma_stop(struct mpsc_port_info *pi)
+{
+       pr_debug("mpsc_sdma_stop[%d]: Stopping SDMA\n", pi->port.line);
+
+       /* Abort any SDMA transfers */
+       mpsc_sdma_cmd(pi, 0);
+       mpsc_sdma_cmd(pi, SDMA_SDCM_AR | SDMA_SDCM_AT);
+
+       /* Clear the SDMA current and first TX and RX pointers */
+       mpsc_sdma_set_tx_ring(pi, 0);
+       mpsc_sdma_set_rx_ring(pi, 0);
+
+       /* Disable interrupts */
+       mpsc_sdma_intr_mask(pi, 0xf);
+       mpsc_sdma_intr_ack(pi);
+
+       return;
+}
+
+/*
+ ******************************************************************************
+ *
+ * Multi-Protocol Serial Controller Routines (MPSC)
+ *
+ ******************************************************************************
+ */
+
+static void
+mpsc_hw_init(struct mpsc_port_info *pi)
+{
+       u32     v;
+
+       pr_debug("mpsc_hw_init[%d]: Initializing hardware\n", pi->port.line);
+
+       /* Set up clock routing */
+       if (pi->mirror_regs) {
+               v = pi->shared_regs->MPSC_MRR_m;
+               v &= ~0x1c7;
+               pi->shared_regs->MPSC_MRR_m = v;
+               writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR);
+
+               v = pi->shared_regs->MPSC_RCRR_m;
+               v = (v & ~0xf0f) | 0x100;
+               pi->shared_regs->MPSC_RCRR_m = v;
+               writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR);
+
+               v = pi->shared_regs->MPSC_TCRR_m;
+               v = (v & ~0xf0f) | 0x100;
+               pi->shared_regs->MPSC_TCRR_m = v;
+               writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR);
+       }
+       else {
+               v = readl(pi->shared_regs->mpsc_routing_base + MPSC_MRR);
+               v &= ~0x1c7;
+               writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR);
+
+               v = readl(pi->shared_regs->mpsc_routing_base + MPSC_RCRR);
+               v = (v & ~0xf0f) | 0x100;
+               writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR);
+
+               v = readl(pi->shared_regs->mpsc_routing_base + MPSC_TCRR);
+               v = (v & ~0xf0f) | 0x100;
+               writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR);
+       }
+
+       /* Put MPSC in UART mode & enabel Tx/Rx egines */
+       writel(0x000004c4, pi->mpsc_base + MPSC_MMCRL);
+
+       /* No preamble, 16x divider, low-latency,  */
+       writel(0x04400400, pi->mpsc_base + MPSC_MMCRH);
+
+       if (pi->mirror_regs) {
+               pi->MPSC_CHR_1_m = 0;
+               pi->MPSC_CHR_2_m = 0;
+       }
+       writel(0, pi->mpsc_base + MPSC_CHR_1);
+       writel(0, pi->mpsc_base + MPSC_CHR_2);
+       writel(pi->mpsc_max_idle, pi->mpsc_base + MPSC_CHR_3);
+       writel(0, pi->mpsc_base + MPSC_CHR_4);
+       writel(0, pi->mpsc_base + MPSC_CHR_5);
+       writel(0, pi->mpsc_base + MPSC_CHR_6);
+       writel(0, pi->mpsc_base + MPSC_CHR_7);
+       writel(0, pi->mpsc_base + MPSC_CHR_8);
+       writel(0, pi->mpsc_base + MPSC_CHR_9);
+       writel(0, pi->mpsc_base + MPSC_CHR_10);
+
+       return;
+}
+
+static inline void
+mpsc_enter_hunt(struct mpsc_port_info *pi)
+{
+       pr_debug("mpsc_enter_hunt[%d]: Hunting...\n", pi->port.line);
+
+       if (pi->mirror_regs) {
+               writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_EH,
+                       pi->mpsc_base + MPSC_CHR_2);
+               /* Erratum prevents reading CHR_2 so just delay for a while */
+               udelay(100);
+       }
+       else {
+               writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_EH,
+                       pi->mpsc_base + MPSC_CHR_2);
+
+               while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_EH)
+                       udelay(10);
+       }
+
+       return;
+}
+
+static inline void
+mpsc_freeze(struct mpsc_port_info *pi)
+{
+       u32     v;
+
+       pr_debug("mpsc_freeze[%d]: Freezing\n", pi->port.line);
+
+       v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
+               readl(pi->mpsc_base + MPSC_MPCR);
+       v |= MPSC_MPCR_FRZ;
+
+       if (pi->mirror_regs)
+               pi->MPSC_MPCR_m = v;
+       writel(v, pi->mpsc_base + MPSC_MPCR);
+       return;
+}
+
+static inline void
+mpsc_unfreeze(struct mpsc_port_info *pi)
+{
+       u32     v;
+
+       v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
+               readl(pi->mpsc_base + MPSC_MPCR);
+       v &= ~MPSC_MPCR_FRZ;
+
+       if (pi->mirror_regs)
+               pi->MPSC_MPCR_m = v;
+       writel(v, pi->mpsc_base + MPSC_MPCR);
+
+       pr_debug("mpsc_unfreeze[%d]: Unfrozen\n", pi->port.line);
+       return;
+}
+
+static inline void
+mpsc_set_char_length(struct mpsc_port_info *pi, u32 len)
+{
+       u32     v;
+
+       pr_debug("mpsc_set_char_length[%d]: char len: %d\n", pi->port.line,len);
+
+       v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
+               readl(pi->mpsc_base + MPSC_MPCR);
+       v = (v & ~(0x3 << 12)) | ((len & 0x3) << 12);
+
+       if (pi->mirror_regs)
+               pi->MPSC_MPCR_m = v;
+       writel(v, pi->mpsc_base + MPSC_MPCR);
+       return;
+}
+
+static inline void
+mpsc_set_stop_bit_length(struct mpsc_port_info *pi, u32 len)
+{
+       u32     v;
+
+       pr_debug("mpsc_set_stop_bit_length[%d]: stop bits: %d\n",
+               pi->port.line, len);
+
+       v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
+               readl(pi->mpsc_base + MPSC_MPCR);
+
+       v = (v & ~(1 << 14)) | ((len & 0x1) << 14);
+
+       if (pi->mirror_regs)
+               pi->MPSC_MPCR_m = v;
+       writel(v, pi->mpsc_base + MPSC_MPCR);
+       return;
+}
+
+static inline void
+mpsc_set_parity(struct mpsc_port_info *pi, u32 p)
+{
+       u32     v;
+
+       pr_debug("mpsc_set_parity[%d]: parity bits: 0x%x\n", pi->port.line, p);
+
+       v = (pi->mirror_regs) ? pi->MPSC_CHR_2_m :
+               readl(pi->mpsc_base + MPSC_CHR_2);
+
+       p &= 0x3;
+       v = (v & ~0xc000c) | (p << 18) | (p << 2);
+
+       if (pi->mirror_regs)
+               pi->MPSC_CHR_2_m = v;
+       writel(v, pi->mpsc_base + MPSC_CHR_2);
+       return;
+}
+
+/*
+ ******************************************************************************
+ *
+ * Driver Init Routines
+ *
+ ******************************************************************************
+ */
+
+static void
+mpsc_init_hw(struct mpsc_port_info *pi)
+{
+       pr_debug("mpsc_init_hw[%d]: Initializing\n", pi->port.line);
+
+       mpsc_brg_init(pi, pi->brg_clk_src);
+       mpsc_brg_enable(pi);
+       mpsc_sdma_init(pi, dma_get_cache_alignment());  /* burst a cacheline */
+       mpsc_sdma_stop(pi);
+       mpsc_hw_init(pi);
+
+       return;
+}
+
+static int
+mpsc_alloc_ring_mem(struct mpsc_port_info *pi)
+{
+       int rc = 0;
+       static void mpsc_free_ring_mem(struct mpsc_port_info *pi);
+
+       pr_debug("mpsc_alloc_ring_mem[%d]: Allocating ring mem\n",
+               pi->port.line);
+
+       if (!pi->dma_region) {
+               if (!dma_supported(pi->port.dev, 0xffffffff)) {
+                       printk(KERN_ERR "MPSC: Inadequate DMA support\n");
+                       rc = -ENXIO;
+               }
+               else if ((pi->dma_region = dma_alloc_noncoherent(pi->port.dev,
+                       MPSC_DMA_ALLOC_SIZE, &pi->dma_region_p, GFP_KERNEL))
+                       == NULL) {
+
+                       printk(KERN_ERR "MPSC: Can't alloc Desc region\n");
+                       rc = -ENOMEM;
+               }
+       }
+
+       return rc;
+}
+
+static void
+mpsc_free_ring_mem(struct mpsc_port_info *pi)
+{
+       pr_debug("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line);
+
+       if (pi->dma_region) {
+               dma_free_noncoherent(pi->port.dev, MPSC_DMA_ALLOC_SIZE,
+                         pi->dma_region, pi->dma_region_p);
+               pi->dma_region = NULL;
+               pi->dma_region_p = (dma_addr_t) NULL;
+       }
+
+       return;
+}
+
+static void
+mpsc_init_rings(struct mpsc_port_info *pi)
+{
+       struct mpsc_rx_desc *rxre;
+       struct mpsc_tx_desc *txre;
+       dma_addr_t dp, dp_p;
+       u8 *bp, *bp_p;
+       int i;
+
+       pr_debug("mpsc_init_rings[%d]: Initializing rings\n", pi->port.line);
+
+       BUG_ON(pi->dma_region == NULL);
+
+       memset(pi->dma_region, 0, MPSC_DMA_ALLOC_SIZE);
+
+       /*
+        * Descriptors & buffers are multiples of cacheline size and must be
+        * cacheline aligned.
+        */
+       dp = ALIGN((u32) pi->dma_region, dma_get_cache_alignment());
+       dp_p = ALIGN((u32) pi->dma_region_p, dma_get_cache_alignment());
+
+       /*
+        * Partition dma region into rx ring descriptor, rx buffers,
+        * tx ring descriptors, and tx buffers.
+        */
+       pi->rxr = dp;
+       pi->rxr_p = dp_p;
+       dp += MPSC_RXR_SIZE;
+       dp_p += MPSC_RXR_SIZE;
+
+       pi->rxb = (u8 *) dp;
+       pi->rxb_p = (u8 *) dp_p;
+       dp += MPSC_RXB_SIZE;
+       dp_p += MPSC_RXB_SIZE;
+
+       pi->rxr_posn = 0;
+
+       pi->txr = dp;
+       pi->txr_p = dp_p;
+       dp += MPSC_TXR_SIZE;
+       dp_p += MPSC_TXR_SIZE;
+
+       pi->txb = (u8 *) dp;
+       pi->txb_p = (u8 *) dp_p;
+
+       pi->txr_head = 0;
+       pi->txr_tail = 0;
+
+       /* Init rx ring descriptors */
+       dp = pi->rxr;
+       dp_p = pi->rxr_p;
+       bp = pi->rxb;
+       bp_p = pi->rxb_p;
+
+       for (i = 0; i < MPSC_RXR_ENTRIES; i++) {
+               rxre = (struct mpsc_rx_desc *)dp;
+
+               rxre->bufsize = cpu_to_be16(MPSC_RXBE_SIZE);
+               rxre->bytecnt = cpu_to_be16(0);
+               rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O |
+                                           SDMA_DESC_CMDSTAT_EI |
+                                           SDMA_DESC_CMDSTAT_F |
+                                           SDMA_DESC_CMDSTAT_L);
+               rxre->link = cpu_to_be32(dp_p + MPSC_RXRE_SIZE);
+               rxre->buf_ptr = cpu_to_be32(bp_p);
+
+               dp += MPSC_RXRE_SIZE;
+               dp_p += MPSC_RXRE_SIZE;
+               bp += MPSC_RXBE_SIZE;
+               bp_p += MPSC_RXBE_SIZE;
+       }
+       rxre->link = cpu_to_be32(pi->rxr_p);    /* Wrap last back to first */
+
+       /* Init tx ring descriptors */
+       dp = pi->txr;
+       dp_p = pi->txr_p;
+       bp = pi->txb;
+       bp_p = pi->txb_p;
+
+       for (i = 0; i < MPSC_TXR_ENTRIES; i++) {
+               txre = (struct mpsc_tx_desc *)dp;
+
+               txre->link = cpu_to_be32(dp_p + MPSC_TXRE_SIZE);
+               txre->buf_ptr = cpu_to_be32(bp_p);
+
+               dp += MPSC_TXRE_SIZE;
+               dp_p += MPSC_TXRE_SIZE;
+               bp += MPSC_TXBE_SIZE;
+               bp_p += MPSC_TXBE_SIZE;
+       }
+       txre->link = cpu_to_be32(pi->txr_p);    /* Wrap last back to first */
+
+       dma_cache_sync((void *) pi->dma_region, MPSC_DMA_ALLOC_SIZE,
+               DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+               if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+                       flush_dcache_range((ulong)pi->dma_region,
+                               (ulong)pi->dma_region + MPSC_DMA_ALLOC_SIZE);
+#endif
+
+       return;
+}
+
+static void
+mpsc_uninit_rings(struct mpsc_port_info *pi)
+{
+       pr_debug("mpsc_uninit_rings[%d]: Uninitializing rings\n",pi->port.line);
+
+       BUG_ON(pi->dma_region == NULL);
+
+       pi->rxr = 0;
+       pi->rxr_p = 0;
+       pi->rxb = NULL;
+       pi->rxb_p = NULL;
+       pi->rxr_posn = 0;
+
+       pi->txr = 0;
+       pi->txr_p = 0;
+       pi->txb = NULL;
+       pi->txb_p = NULL;
+       pi->txr_head = 0;
+       pi->txr_tail = 0;
+
+       return;
+}
+
+static int
+mpsc_make_ready(struct mpsc_port_info *pi)
+{
+       int rc;
+
+       pr_debug("mpsc_make_ready[%d]: Making cltr ready\n", pi->port.line);
+
+       if (!pi->ready) {
+               mpsc_init_hw(pi);
+               if ((rc = mpsc_alloc_ring_mem(pi)))
+                       return rc;
+               mpsc_init_rings(pi);
+               pi->ready = 1;
+       }
+
+       return 0;
+}
+
+/*
+ ******************************************************************************
+ *
+ * Interrupt Handling Routines
+ *
+ ******************************************************************************
+ */
+
+static inline int
+mpsc_rx_intr(struct mpsc_port_info *pi, struct pt_regs *regs)
+{
+       struct mpsc_rx_desc *rxre;
+       struct tty_struct *tty = pi->port.info->tty;
+       u32     cmdstat, bytes_in, i;
+       int     rc = 0;
+       u8      *bp;
+       char    flag = TTY_NORMAL;
+       static void mpsc_start_rx(struct mpsc_port_info *pi);
+
+       pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line);
+
+       rxre = (struct mpsc_rx_desc *)(pi->rxr + (pi->rxr_posn*MPSC_RXRE_SIZE));
+
+       dma_cache_sync((void *)rxre, MPSC_RXRE_SIZE, DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+       if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+               invalidate_dcache_range((ulong)rxre,
+                       (ulong)rxre + MPSC_RXRE_SIZE);
+#endif
+
+       /*
+        * Loop through Rx descriptors handling ones that have been completed.
+        */
+       while (!((cmdstat = be32_to_cpu(rxre->cmdstat)) & SDMA_DESC_CMDSTAT_O)){
+               bytes_in = be16_to_cpu(rxre->bytecnt);
+
+               /* Following use of tty struct directly is deprecated */
+               if (unlikely((tty->flip.count + bytes_in) >= TTY_FLIPBUF_SIZE)){
+                       if (tty->low_latency)
+                               tty_flip_buffer_push(tty);
+                       /*
+                        * If this failed then we will throw awa the bytes
+                        * but mst do so to clear interrupts.
+                        */
+               }
+
+               bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE);
+               dma_cache_sync((void *) bp, MPSC_RXBE_SIZE, DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+               if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+                       invalidate_dcache_range((ulong)bp,
+                               (ulong)bp + MPSC_RXBE_SIZE);
+#endif
+
+               /*
+                * Other than for parity error, the manual provides little
+                * info on what data will be in a frame flagged by any of
+                * these errors.  For parity error, it is the last byte in
+                * the buffer that had the error.  As for the rest, I guess
+                * we'll assume there is no data in the buffer.
+                * If there is...it gets lost.
+                */
+               if (unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR |
+                       SDMA_DESC_CMDSTAT_FR | SDMA_DESC_CMDSTAT_OR))) {
+
+                       pi->port.icount.rx++;
+
+                       if (cmdstat & SDMA_DESC_CMDSTAT_BR) {   /* Break */
+                               pi->port.icount.brk++;
+
+                               if (uart_handle_break(&pi->port))
+                                       goto next_frame;
+                       }
+                       else if (cmdstat & SDMA_DESC_CMDSTAT_FR)/* Framing */
+                               pi->port.icount.frame++;
+                       else if (cmdstat & SDMA_DESC_CMDSTAT_OR) /* Overrun */
+                               pi->port.icount.overrun++;
+
+                       cmdstat &= pi->port.read_status_mask;
+
+                       if (cmdstat & SDMA_DESC_CMDSTAT_BR)
+                               flag = TTY_BREAK;
+                       else if (cmdstat & SDMA_DESC_CMDSTAT_FR)
+                               flag = TTY_FRAME;
+                       else if (cmdstat & SDMA_DESC_CMDSTAT_OR)
+                               flag = TTY_OVERRUN;
+                       else if (cmdstat & SDMA_DESC_CMDSTAT_PE)
+                               flag = TTY_PARITY;
+               }
+
+               if (uart_handle_sysrq_char(&pi->port, *bp, regs)) {
+                       bp++;
+                       bytes_in--;
+                       goto next_frame;
+               }
+
+               if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR |
+                       SDMA_DESC_CMDSTAT_FR | SDMA_DESC_CMDSTAT_OR))) &&
+                       !(cmdstat & pi->port.ignore_status_mask))
+
+                       tty_insert_flip_char(tty, *bp, flag);
+               else {
+                       for (i=0; i<bytes_in; i++)
+                               tty_insert_flip_char(tty, *bp++, TTY_NORMAL);
+
+                       pi->port.icount.rx += bytes_in;
+               }
+
+next_frame:
+               rxre->bytecnt = cpu_to_be16(0);
+               wmb();
+               rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O |
+                                           SDMA_DESC_CMDSTAT_EI |
+                                           SDMA_DESC_CMDSTAT_F |
+                                           SDMA_DESC_CMDSTAT_L);
+               wmb();
+               dma_cache_sync((void *)rxre, MPSC_RXRE_SIZE, DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+               if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+                       flush_dcache_range((ulong)rxre,
+                               (ulong)rxre + MPSC_RXRE_SIZE);
+#endif
+
+               /* Advance to next descriptor */
+               pi->rxr_posn = (pi->rxr_posn + 1) & (MPSC_RXR_ENTRIES - 1);
+               rxre = (struct mpsc_rx_desc *)(pi->rxr +
+                       (pi->rxr_posn * MPSC_RXRE_SIZE));
+               dma_cache_sync((void *)rxre, MPSC_RXRE_SIZE, DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+               if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+                       invalidate_dcache_range((ulong)rxre,
+                               (ulong)rxre + MPSC_RXRE_SIZE);
+#endif
+
+               rc = 1;
+       }
+
+       /* Restart rx engine, if its stopped */
+       if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0)
+               mpsc_start_rx(pi);
+
+       tty_flip_buffer_push(tty);
+       return rc;
+}
+
+static inline void
+mpsc_setup_tx_desc(struct mpsc_port_info *pi, u32 count, u32 intr)
+{
+       struct mpsc_tx_desc *txre;
+
+       txre = (struct mpsc_tx_desc *)(pi->txr +
+               (pi->txr_head * MPSC_TXRE_SIZE));
+
+       txre->bytecnt = cpu_to_be16(count);
+       txre->shadow = txre->bytecnt;
+       wmb();                  /* ensure cmdstat is last field updated */
+       txre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | SDMA_DESC_CMDSTAT_F |
+                                   SDMA_DESC_CMDSTAT_L | ((intr) ?
+                                                          SDMA_DESC_CMDSTAT_EI
+                                                          : 0));
+       wmb();
+       dma_cache_sync((void *) txre, MPSC_TXRE_SIZE, DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+       if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+               flush_dcache_range((ulong)txre,
+                       (ulong)txre + MPSC_TXRE_SIZE);
+#endif
+
+       return;
+}
+
+static inline void
+mpsc_copy_tx_data(struct mpsc_port_info *pi)
+{
+       struct circ_buf *xmit = &pi->port.info->xmit;
+       u8 *bp;
+       u32 i;
+
+       /* Make sure the desc ring isn't full */
+       while (CIRC_CNT(pi->txr_head, pi->txr_tail, MPSC_TXR_ENTRIES) <
+              (MPSC_TXR_ENTRIES - 1)) {
+               if (pi->port.x_char) {
+                       /*
+                        * Ideally, we should use the TCS field in
+                        * CHR_1 to put the x_char out immediately but
+                        * errata prevents us from being able to read
+                        * CHR_2 to know that its safe to write to
+                        * CHR_1.  Instead, just put it in-band with
+                        * all the other Tx data.
+                        */
+                       bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE);
+                       *bp = pi->port.x_char;
+                       pi->port.x_char = 0;
+                       i = 1;
+               }
+               else if (!uart_circ_empty(xmit) && !uart_tx_stopped(&pi->port)){
+                       i = min((u32) MPSC_TXBE_SIZE,
+                               (u32) uart_circ_chars_pending(xmit));
+                       i = min(i, (u32) CIRC_CNT_TO_END(xmit->head, xmit->tail,
+                               UART_XMIT_SIZE));
+                       bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE);
+                       memcpy(bp, &xmit->buf[xmit->tail], i);
+                       xmit->tail = (xmit->tail + i) & (UART_XMIT_SIZE - 1);
+
+                       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+                               uart_write_wakeup(&pi->port);
+               }
+               else /* All tx data copied into ring bufs */
+                       return;
+
+               dma_cache_sync((void *) bp, MPSC_TXBE_SIZE, DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+               if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+                       flush_dcache_range((ulong)bp,
+                               (ulong)bp + MPSC_TXBE_SIZE);
+#endif
+               mpsc_setup_tx_desc(pi, i, 1);
+
+               /* Advance to next descriptor */
+               pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1);
+       }
+
+       return;
+}
+
+static inline int
+mpsc_tx_intr(struct mpsc_port_info *pi)
+{
+       struct mpsc_tx_desc *txre;
+       int rc = 0;
+
+       if (!mpsc_sdma_tx_active(pi)) {
+               txre = (struct mpsc_tx_desc *)(pi->txr +
+                       (pi->txr_tail * MPSC_TXRE_SIZE));
+
+               dma_cache_sync((void *) txre, MPSC_TXRE_SIZE, DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+               if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+                       invalidate_dcache_range((ulong)txre,
+                               (ulong)txre + MPSC_TXRE_SIZE);
+#endif
+
+               while (!(be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O)) {
+                       rc = 1;
+                       pi->port.icount.tx += be16_to_cpu(txre->bytecnt);
+                       pi->txr_tail = (pi->txr_tail+1) & (MPSC_TXR_ENTRIES-1);
+
+                       /* If no more data to tx, fall out of loop */
+                       if (pi->txr_head == pi->txr_tail)
+                               break;
+
+                       txre = (struct mpsc_tx_desc *)(pi->txr +
+                               (pi->txr_tail * MPSC_TXRE_SIZE));
+                       dma_cache_sync((void *) txre, MPSC_TXRE_SIZE,
+                               DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+                       if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+                               invalidate_dcache_range((ulong)txre,
+                                       (ulong)txre + MPSC_TXRE_SIZE);
+#endif
+               }
+
+               mpsc_copy_tx_data(pi);
+               mpsc_sdma_start_tx(pi); /* start next desc if ready */
+       }
+
+       return rc;
+}
+
+/*
+ * This is the driver's interrupt handler.  To avoid a race, we first clear
+ * the interrupt, then handle any completed Rx/Tx descriptors.  When done
+ * handling those descriptors, we restart the Rx/Tx engines if they're stopped.
+ */
+static irqreturn_t
+mpsc_sdma_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct mpsc_port_info *pi = dev_id;
+       ulong iflags;
+       int rc = IRQ_NONE;
+
+       pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Received\n",pi->port.line);
+
+       spin_lock_irqsave(&pi->port.lock, iflags);
+       mpsc_sdma_intr_ack(pi);
+       if (mpsc_rx_intr(pi, regs))
+               rc = IRQ_HANDLED;
+       if (mpsc_tx_intr(pi))
+               rc = IRQ_HANDLED;
+       spin_unlock_irqrestore(&pi->port.lock, iflags);
+
+       pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Handled\n", pi->port.line);
+       return rc;
+}
+
+/*
+ ******************************************************************************
+ *
+ * serial_core.c Interface routines
+ *
+ ******************************************************************************
+ */
+static uint
+mpsc_tx_empty(struct uart_port *port)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+       ulong iflags;
+       uint rc;
+
+       spin_lock_irqsave(&pi->port.lock, iflags);
+       rc = mpsc_sdma_tx_active(pi) ? 0 : TIOCSER_TEMT;
+       spin_unlock_irqrestore(&pi->port.lock, iflags);
+
+       return rc;
+}
+
+static void
+mpsc_set_mctrl(struct uart_port *port, uint mctrl)
+{
+       /* Have no way to set modem control lines AFAICT */
+       return;
+}
+
+static uint
+mpsc_get_mctrl(struct uart_port *port)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+       u32 mflags, status;
+       ulong iflags;
+
+       spin_lock_irqsave(&pi->port.lock, iflags);
+       status = (pi->mirror_regs) ? pi->MPSC_CHR_10_m :
+               readl(pi->mpsc_base + MPSC_CHR_10);
+       spin_unlock_irqrestore(&pi->port.lock, iflags);
+
+       mflags = 0;
+       if (status & 0x1)
+               mflags |= TIOCM_CTS;
+       if (status & 0x2)
+               mflags |= TIOCM_CAR;
+
+       return mflags | TIOCM_DSR;      /* No way to tell if DSR asserted */
+}
+
+static void
+mpsc_stop_tx(struct uart_port *port, uint tty_start)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+
+       pr_debug("mpsc_stop_tx[%d]: tty_start: %d\n", port->line, tty_start);
+
+       mpsc_freeze(pi);
+       return;
+}
+
+static void
+mpsc_start_tx(struct uart_port *port, uint tty_start)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+
+       mpsc_unfreeze(pi);
+       mpsc_copy_tx_data(pi);
+       mpsc_sdma_start_tx(pi);
+
+       pr_debug("mpsc_start_tx[%d]: tty_start: %d\n", port->line, tty_start);
+       return;
+}
+
+static void
+mpsc_start_rx(struct mpsc_port_info *pi)
+{
+       pr_debug("mpsc_start_rx[%d]: Starting...\n", pi->port.line);
+
+       if (pi->rcv_data) {
+               mpsc_enter_hunt(pi);
+               mpsc_sdma_cmd(pi, SDMA_SDCM_ERD);
+       }
+       return;
+}
+
+static void
+mpsc_stop_rx(struct uart_port *port)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+
+       pr_debug("mpsc_stop_rx[%d]: Stopping...\n", port->line);
+
+       mpsc_sdma_cmd(pi, SDMA_SDCM_AR);
+       return;
+}
+
+static void
+mpsc_enable_ms(struct uart_port *port)
+{
+       return;                 /* Not supported */
+}
+
+static void
+mpsc_break_ctl(struct uart_port *port, int ctl)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+       ulong   flags;
+       u32     v;
+
+       v = ctl ? 0x00ff0000 : 0;
+
+       spin_lock_irqsave(&pi->port.lock, flags);
+       if (pi->mirror_regs)
+               pi->MPSC_CHR_1_m = v;
+       writel(v, pi->mpsc_base + MPSC_CHR_1);
+       spin_unlock_irqrestore(&pi->port.lock, flags);
+
+       return;
+}
+
+static int
+mpsc_startup(struct uart_port *port)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+       u32 flag = 0;
+       int rc;
+
+       pr_debug("mpsc_startup[%d]: Starting up MPSC, irq: %d\n",
+               port->line, pi->port.irq);
+
+       if ((rc = mpsc_make_ready(pi)) == 0) {
+               /* Setup IRQ handler */
+               mpsc_sdma_intr_ack(pi);
+
+               /* If irq's are shared, need to set flag */
+               if (mpsc_ports[0].port.irq == mpsc_ports[1].port.irq)
+                       flag = SA_SHIRQ;
+
+               if (request_irq(pi->port.irq, mpsc_sdma_intr, flag,
+                               "mpsc/sdma", pi))
+                       printk(KERN_ERR "MPSC: Can't get SDMA IRQ %d\n",
+                              pi->port.irq);
+
+               mpsc_sdma_intr_unmask(pi, 0xf);
+               mpsc_sdma_set_rx_ring(pi, (struct mpsc_rx_desc *)(pi->rxr_p +
+                       (pi->rxr_posn * MPSC_RXRE_SIZE)));
+       }
+
+       return rc;
+}
+
+static void
+mpsc_shutdown(struct uart_port *port)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+       static void mpsc_release_port(struct uart_port *port);
+
+       pr_debug("mpsc_shutdown[%d]: Shutting down MPSC\n", port->line);
+
+       mpsc_sdma_stop(pi);
+       free_irq(pi->port.irq, pi);
+       return;
+}
+
+static void
+mpsc_set_termios(struct uart_port *port, struct termios *termios,
+                struct termios *old)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+       u32 baud;
+       ulong flags;
+       u32 chr_bits, stop_bits, par;
+
+       pi->c_iflag = termios->c_iflag;
+       pi->c_cflag = termios->c_cflag;
+
+       switch (termios->c_cflag & CSIZE) {
+       case CS5:
+               chr_bits = MPSC_MPCR_CL_5;
+               break;
+       case CS6:
+               chr_bits = MPSC_MPCR_CL_6;
+               break;
+       case CS7:
+               chr_bits = MPSC_MPCR_CL_7;
+               break;
+       case CS8:
+       default:
+               chr_bits = MPSC_MPCR_CL_8;
+               break;
+       }
+
+       if (termios->c_cflag & CSTOPB)
+               stop_bits = MPSC_MPCR_SBL_2;
+       else
+               stop_bits = MPSC_MPCR_SBL_1;
+
+       par = MPSC_CHR_2_PAR_EVEN;
+       if (termios->c_cflag & PARENB)
+               if (termios->c_cflag & PARODD)
+                       par = MPSC_CHR_2_PAR_ODD;
+#ifdef CMSPAR
+               if (termios->c_cflag & CMSPAR) {
+                       if (termios->c_cflag & PARODD)
+                               par = MPSC_CHR_2_PAR_MARK;
+                       else
+                               par = MPSC_CHR_2_PAR_SPACE;
+               }
+#endif
+
+       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk);
+
+       spin_lock_irqsave(&pi->port.lock, flags);
+
+       uart_update_timeout(port, termios->c_cflag, baud);
+
+       mpsc_set_char_length(pi, chr_bits);
+       mpsc_set_stop_bit_length(pi, stop_bits);
+       mpsc_set_parity(pi, par);
+       mpsc_set_baudrate(pi, baud);
+
+       /* Characters/events to read */
+       pi->rcv_data = 1;
+       pi->port.read_status_mask = SDMA_DESC_CMDSTAT_OR;
+
+       if (termios->c_iflag & INPCK)
+               pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE |
+                   SDMA_DESC_CMDSTAT_FR;
+
+       if (termios->c_iflag & (BRKINT | PARMRK))
+               pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR;
+
+       /* Characters/events to ignore */
+       pi->port.ignore_status_mask = 0;
+
+       if (termios->c_iflag & IGNPAR)
+               pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_PE |
+                   SDMA_DESC_CMDSTAT_FR;
+
+       if (termios->c_iflag & IGNBRK) {
+               pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_BR;
+
+               if (termios->c_iflag & IGNPAR)
+                       pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_OR;
+       }
+
+       /* Ignore all chars if CREAD not set */
+       if (!(termios->c_cflag & CREAD))
+               pi->rcv_data = 0;
+       else
+               mpsc_start_rx(pi);
+
+       spin_unlock_irqrestore(&pi->port.lock, flags);
+       return;
+}
+
+static const char *
+mpsc_type(struct uart_port *port)
+{
+       pr_debug("mpsc_type[%d]: port type: %s\n", port->line,MPSC_DRIVER_NAME);
+       return MPSC_DRIVER_NAME;
+}
+
+static int
+mpsc_request_port(struct uart_port *port)
+{
+       /* Should make chip/platform specific call */
+       return 0;
+}
+
+static void
+mpsc_release_port(struct uart_port *port)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+
+       if (pi->ready) {
+               mpsc_uninit_rings(pi);
+               mpsc_free_ring_mem(pi);
+               pi->ready = 0;
+       }
+
+       return;
+}
+
+static void
+mpsc_config_port(struct uart_port *port, int flags)
+{
+       return;
+}
+
+static int
+mpsc_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+       int rc = 0;
+
+       pr_debug("mpsc_verify_port[%d]: Verifying port data\n", pi->port.line);
+
+       if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPSC)
+               rc = -EINVAL;
+       else if (pi->port.irq != ser->irq)
+               rc = -EINVAL;
+       else if (ser->io_type != SERIAL_IO_MEM)
+               rc = -EINVAL;
+       else if (pi->port.uartclk / 16 != ser->baud_base) /* Not sure */
+               rc = -EINVAL;
+       else if ((void *)pi->port.mapbase != ser->iomem_base)
+               rc = -EINVAL;
+       else if (pi->port.iobase != ser->port)
+               rc = -EINVAL;
+       else if (ser->hub6 != 0)
+               rc = -EINVAL;
+
+       return rc;
+}
+
+static struct uart_ops mpsc_pops = {
+       .tx_empty     = mpsc_tx_empty,
+       .set_mctrl    = mpsc_set_mctrl,
+       .get_mctrl    = mpsc_get_mctrl,
+       .stop_tx      = mpsc_stop_tx,
+       .start_tx     = mpsc_start_tx,
+       .stop_rx      = mpsc_stop_rx,
+       .enable_ms    = mpsc_enable_ms,
+       .break_ctl    = mpsc_break_ctl,
+       .startup      = mpsc_startup,
+       .shutdown     = mpsc_shutdown,
+       .set_termios  = mpsc_set_termios,
+       .type         = mpsc_type,
+       .release_port = mpsc_release_port,
+       .request_port = mpsc_request_port,
+       .config_port  = mpsc_config_port,
+       .verify_port  = mpsc_verify_port,
+};
+
+/*
+ ******************************************************************************
+ *
+ * Console Interface Routines
+ *
+ ******************************************************************************
+ */
+
+#ifdef CONFIG_SERIAL_MPSC_CONSOLE
+static void
+mpsc_console_write(struct console *co, const char *s, uint count)
+{
+       struct mpsc_port_info *pi = &mpsc_ports[co->index];
+       u8 *bp, *dp, add_cr = 0;
+       int i;
+
+       while (mpsc_sdma_tx_active(pi))
+               udelay(100);
+
+       while (count > 0) {
+               bp = dp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE);
+
+               for (i = 0; i < MPSC_TXBE_SIZE; i++) {
+                       if (count == 0)
+                               break;
+
+                       if (add_cr) {
+                               *(dp++) = '\r';
+                               add_cr = 0;
+                       }
+                       else {
+                               *(dp++) = *s;
+
+                               if (*(s++) == '\n') { /* add '\r' after '\n' */
+                                       add_cr = 1;
+                                       count++;
+                               }
+                       }
+
+                       count--;
+               }
+
+               dma_cache_sync((void *) bp, MPSC_TXBE_SIZE, DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+               if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+                       flush_dcache_range((ulong)bp,
+                               (ulong)bp + MPSC_TXBE_SIZE);
+#endif
+               mpsc_setup_tx_desc(pi, i, 0);
+               pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1);
+               mpsc_sdma_start_tx(pi);
+
+               while (mpsc_sdma_tx_active(pi))
+                       udelay(100);
+
+               pi->txr_tail = (pi->txr_tail + 1) & (MPSC_TXR_ENTRIES - 1);
+       }
+
+       return;
+}
+
+static int __init
+mpsc_console_setup(struct console *co, char *options)
+{
+       struct mpsc_port_info *pi;
+       int baud, bits, parity, flow;
+
+       pr_debug("mpsc_console_setup[%d]: options: %s\n", co->index, options);
+
+       if (co->index >= MPSC_NUM_CTLRS)
+               co->index = 0;
+
+       pi = &mpsc_ports[co->index];
+
+       baud = pi->default_baud;
+       bits = pi->default_bits;
+       parity = pi->default_parity;
+       flow = pi->default_flow;
+
+       if (!pi->port.ops)
+               return -ENODEV;
+
+       spin_lock_init(&pi->port.lock); /* Temporary fix--copied from 8250.c */
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+       return uart_set_options(&pi->port, co, baud, parity, bits, flow);
+}
+
+extern struct uart_driver mpsc_reg;
+static struct console mpsc_console = {
+       .name   = MPSC_DEV_NAME,
+       .write  = mpsc_console_write,
+       .device = uart_console_device,
+       .setup  = mpsc_console_setup,
+       .flags  = CON_PRINTBUFFER,
+       .index  = -1,
+       .data   = &mpsc_reg,
+};
+
+static int __init
+mpsc_late_console_init(void)
+{
+       pr_debug("mpsc_late_console_init: Enter\n");
+
+       if (!(mpsc_console.flags & CON_ENABLED))
+               register_console(&mpsc_console);
+       return 0;
+}
+
+late_initcall(mpsc_late_console_init);
+
+#define MPSC_CONSOLE   &mpsc_console
+#else
+#define MPSC_CONSOLE   NULL
+#endif
+/*
+ ******************************************************************************
+ *
+ * Dummy Platform Driver to extract & map shared register regions
+ *
+ ******************************************************************************
+ */
+static void
+mpsc_resource_err(char *s)
+{
+       printk(KERN_WARNING "MPSC: Platform device resource error in %s\n", s);
+       return;
+}
+
+static int
+mpsc_shared_map_regs(struct platform_device *pd)
+{
+       struct resource *r;
+
+       if ((r = platform_get_resource(pd, IORESOURCE_MEM,
+               MPSC_ROUTING_BASE_ORDER)) && request_mem_region(r->start,
+               MPSC_ROUTING_REG_BLOCK_SIZE, "mpsc_routing_regs")) {
+
+               mpsc_shared_regs.mpsc_routing_base = ioremap(r->start,
+                       MPSC_ROUTING_REG_BLOCK_SIZE);
+               mpsc_shared_regs.mpsc_routing_base_p = r->start;
+       }
+       else {
+               mpsc_resource_err("MPSC routing base");
+               return -ENOMEM;
+       }
+
+       if ((r = platform_get_resource(pd, IORESOURCE_MEM,
+               MPSC_SDMA_INTR_BASE_ORDER)) && request_mem_region(r->start,
+               MPSC_SDMA_INTR_REG_BLOCK_SIZE, "sdma_intr_regs")) {
+
+               mpsc_shared_regs.sdma_intr_base = ioremap(r->start,
+                       MPSC_SDMA_INTR_REG_BLOCK_SIZE);
+               mpsc_shared_regs.sdma_intr_base_p = r->start;
+       }
+       else {
+               iounmap(mpsc_shared_regs.mpsc_routing_base);
+               release_mem_region(mpsc_shared_regs.mpsc_routing_base_p,
+                       MPSC_ROUTING_REG_BLOCK_SIZE);
+               mpsc_resource_err("SDMA intr base");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void
+mpsc_shared_unmap_regs(void)
+{
+       if (!mpsc_shared_regs.mpsc_routing_base) {
+               iounmap(mpsc_shared_regs.mpsc_routing_base);
+               release_mem_region(mpsc_shared_regs.mpsc_routing_base_p,
+                       MPSC_ROUTING_REG_BLOCK_SIZE);
+       }
+       if (!mpsc_shared_regs.sdma_intr_base) {
+               iounmap(mpsc_shared_regs.sdma_intr_base);
+               release_mem_region(mpsc_shared_regs.sdma_intr_base_p,
+                       MPSC_SDMA_INTR_REG_BLOCK_SIZE);
+       }
+
+       mpsc_shared_regs.mpsc_routing_base = 0;
+       mpsc_shared_regs.sdma_intr_base = 0;
+
+       mpsc_shared_regs.mpsc_routing_base_p = 0;
+       mpsc_shared_regs.sdma_intr_base_p = 0;
+
+       return;
+}
+
+static int
+mpsc_shared_drv_probe(struct device *dev)
+{
+       struct platform_device          *pd = to_platform_device(dev);
+       struct mpsc_shared_pdata        *pdata;
+       int                              rc = -ENODEV;
+
+       if (pd->id == 0) {
+               if (!(rc = mpsc_shared_map_regs(pd)))  {
+                       pdata = (struct mpsc_shared_pdata *)dev->platform_data;
+
+                       mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val;
+                       mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val;
+                       mpsc_shared_regs.MPSC_TCRR_m= pdata->tcrr_val;
+                       mpsc_shared_regs.SDMA_INTR_CAUSE_m =
+                               pdata->intr_cause_val;
+                       mpsc_shared_regs.SDMA_INTR_MASK_m =
+                               pdata->intr_mask_val;
+
+                       rc = 0;
+               }
+       }
+
+       return rc;
+}
+
+static int
+mpsc_shared_drv_remove(struct device *dev)
+{
+       struct platform_device  *pd = to_platform_device(dev);
+       int     rc = -ENODEV;
+
+       if (pd->id == 0) {
+               mpsc_shared_unmap_regs();
+               mpsc_shared_regs.MPSC_MRR_m = 0;
+               mpsc_shared_regs.MPSC_RCRR_m = 0;
+               mpsc_shared_regs.MPSC_TCRR_m = 0;
+               mpsc_shared_regs.SDMA_INTR_CAUSE_m = 0;
+               mpsc_shared_regs.SDMA_INTR_MASK_m = 0;
+               rc = 0;
+       }
+
+       return rc;
+}
+
+static struct device_driver mpsc_shared_driver = {
+       .name   = MPSC_SHARED_NAME,
+       .bus    = &platform_bus_type,
+       .probe  = mpsc_shared_drv_probe,
+       .remove = mpsc_shared_drv_remove,
+};
+
+/*
+ ******************************************************************************
+ *
+ * Driver Interface Routines
+ *
+ ******************************************************************************
+ */
+static struct uart_driver mpsc_reg = {
+       .owner       = THIS_MODULE,
+       .driver_name = MPSC_DRIVER_NAME,
+       .devfs_name  = MPSC_DEVFS_NAME,
+       .dev_name    = MPSC_DEV_NAME,
+       .major       = MPSC_MAJOR,
+       .minor       = MPSC_MINOR_START,
+       .nr          = MPSC_NUM_CTLRS,
+       .cons        = MPSC_CONSOLE,
+};
+
+static int
+mpsc_drv_map_regs(struct mpsc_port_info *pi, struct platform_device *pd)
+{
+       struct resource *r;
+
+       if ((r = platform_get_resource(pd, IORESOURCE_MEM, MPSC_BASE_ORDER)) &&
+               request_mem_region(r->start, MPSC_REG_BLOCK_SIZE, "mpsc_regs")){
+
+               pi->mpsc_base = ioremap(r->start, MPSC_REG_BLOCK_SIZE);
+               pi->mpsc_base_p = r->start;
+       }
+       else {
+               mpsc_resource_err("MPSC base");
+               return -ENOMEM;
+       }
+
+       if ((r = platform_get_resource(pd, IORESOURCE_MEM,
+               MPSC_SDMA_BASE_ORDER)) && request_mem_region(r->start,
+               MPSC_SDMA_REG_BLOCK_SIZE, "sdma_regs")) {
+
+               pi->sdma_base = ioremap(r->start,MPSC_SDMA_REG_BLOCK_SIZE);
+               pi->sdma_base_p = r->start;
+       }
+       else {
+               mpsc_resource_err("SDMA base");
+               return -ENOMEM;
+       }
+
+       if ((r = platform_get_resource(pd,IORESOURCE_MEM,MPSC_BRG_BASE_ORDER))
+               && request_mem_region(r->start, MPSC_BRG_REG_BLOCK_SIZE,
+               "brg_regs")) {
+
+               pi->brg_base = ioremap(r->start, MPSC_BRG_REG_BLOCK_SIZE);
+               pi->brg_base_p = r->start;
+       }
+       else {
+               mpsc_resource_err("BRG base");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void
+mpsc_drv_unmap_regs(struct mpsc_port_info *pi)
+{
+       if (!pi->mpsc_base) {
+               iounmap(pi->mpsc_base);
+               release_mem_region(pi->mpsc_base_p, MPSC_REG_BLOCK_SIZE);
+       }
+       if (!pi->sdma_base) {
+               iounmap(pi->sdma_base);
+               release_mem_region(pi->sdma_base_p, MPSC_SDMA_REG_BLOCK_SIZE);
+       }
+       if (!pi->brg_base) {
+               iounmap(pi->brg_base);
+               release_mem_region(pi->brg_base_p, MPSC_BRG_REG_BLOCK_SIZE);
+       }
+
+       pi->mpsc_base = 0;
+       pi->sdma_base = 0;
+       pi->brg_base = 0;
+
+       pi->mpsc_base_p = 0;
+       pi->sdma_base_p = 0;
+       pi->brg_base_p = 0;
+
+       return;
+}
+
+static void
+mpsc_drv_get_platform_data(struct mpsc_port_info *pi,
+       struct platform_device *pd, int num)
+{
+       struct mpsc_pdata       *pdata;
+
+       pdata = (struct mpsc_pdata *)pd->dev.platform_data;
+
+       pi->port.uartclk = pdata->brg_clk_freq;
+       pi->port.iotype = UPIO_MEM;
+       pi->port.line = num;
+       pi->port.type = PORT_MPSC;
+       pi->port.fifosize = MPSC_TXBE_SIZE;
+       pi->port.membase = pi->mpsc_base;
+       pi->port.mapbase = (ulong)pi->mpsc_base;
+       pi->port.ops = &mpsc_pops;
+
+       pi->mirror_regs = pdata->mirror_regs;
+       pi->cache_mgmt = pdata->cache_mgmt;
+       pi->brg_can_tune = pdata->brg_can_tune;
+       pi->brg_clk_src = pdata->brg_clk_src;
+       pi->mpsc_max_idle = pdata->max_idle;
+       pi->default_baud = pdata->default_baud;
+       pi->default_bits = pdata->default_bits;
+       pi->default_parity = pdata->default_parity;
+       pi->default_flow = pdata->default_flow;
+
+       /* Initial values of mirrored regs */
+       pi->MPSC_CHR_1_m = pdata->chr_1_val;
+       pi->MPSC_CHR_2_m = pdata->chr_2_val;
+       pi->MPSC_CHR_10_m = pdata->chr_10_val;
+       pi->MPSC_MPCR_m = pdata->mpcr_val;
+       pi->BRG_BCR_m = pdata->bcr_val;
+
+       pi->shared_regs = &mpsc_shared_regs;
+
+       pi->port.irq = platform_get_irq(pd, 0);
+
+       return;
+}
+
+static int
+mpsc_drv_probe(struct device *dev)
+{
+       struct platform_device  *pd = to_platform_device(dev);
+       struct mpsc_port_info   *pi;
+       int                     rc = -ENODEV;
+
+       pr_debug("mpsc_drv_probe: Adding MPSC %d\n", pd->id);
+
+       if (pd->id < MPSC_NUM_CTLRS) {
+               pi = &mpsc_ports[pd->id];
+
+               if (!(rc = mpsc_drv_map_regs(pi, pd))) {
+                       mpsc_drv_get_platform_data(pi, pd, pd->id);
+
+                       if (!(rc = mpsc_make_ready(pi)))
+                               if (!(rc = uart_add_one_port(&mpsc_reg,
+                                       &pi->port)))
+                                       rc = 0;
+                               else {
+                                       mpsc_release_port(
+                                               (struct uart_port *)pi);
+                                       mpsc_drv_unmap_regs(pi);
+                               }
+                       else
+                               mpsc_drv_unmap_regs(pi);
+               }
+       }
+
+       return rc;
+}
+
+static int
+mpsc_drv_remove(struct device *dev)
+{
+       struct platform_device  *pd = to_platform_device(dev);
+
+       pr_debug("mpsc_drv_exit: Removing MPSC %d\n", pd->id);
+
+       if (pd->id < MPSC_NUM_CTLRS) {
+               uart_remove_one_port(&mpsc_reg, &mpsc_ports[pd->id].port);
+               mpsc_release_port((struct uart_port *)&mpsc_ports[pd->id].port);
+               mpsc_drv_unmap_regs(&mpsc_ports[pd->id]);
+               return 0;
+       }
+       else
+               return -ENODEV;
+}
+
+static struct device_driver mpsc_driver = {
+       .name   = MPSC_CTLR_NAME,
+       .bus    = &platform_bus_type,
+       .probe  = mpsc_drv_probe,
+       .remove = mpsc_drv_remove,
+};
+
+static int __init
+mpsc_drv_init(void)
+{
+       int     rc;
+
+       printk(KERN_INFO "Serial: MPSC driver $Revision: 1.00 $\n");
+
+       memset(mpsc_ports, 0, sizeof(mpsc_ports));
+       memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs));
+
+       if (!(rc = uart_register_driver(&mpsc_reg))) {
+               if (!(rc = driver_register(&mpsc_shared_driver))) {
+                       if ((rc = driver_register(&mpsc_driver))) {
+                               driver_unregister(&mpsc_shared_driver);
+                               uart_unregister_driver(&mpsc_reg);
+                       }
+               }
+               else
+                       uart_unregister_driver(&mpsc_reg);
+       }
+
+       return rc;
+
+}
+
+static void __exit
+mpsc_drv_exit(void)
+{
+       driver_unregister(&mpsc_driver);
+       driver_unregister(&mpsc_shared_driver);
+       uart_unregister_driver(&mpsc_reg);
+       memset(mpsc_ports, 0, sizeof(mpsc_ports));
+       memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs));
+       return;
+}
+
+module_init(mpsc_drv_init);
+module_exit(mpsc_drv_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Generic Marvell MPSC serial/UART driver $Revision: 1.00 $");
+MODULE_VERSION(MPSC_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(MPSC_MAJOR);
diff --git a/drivers/serial/mpsc.h b/drivers/serial/mpsc.h
new file mode 100644 (file)
index 0000000..1f7294b
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * drivers/serial/mpsc.h
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 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.
+ */
+
+#ifndef        __MPSC_H__
+#define        __MPSC_H__
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mv643xx.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#if defined(CONFIG_SERIAL_MPSC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#define        MPSC_NUM_CTLRS          2
+
+/*
+ * Descriptors and buffers must be cache line aligned.
+ * Buffers lengths must be multiple of cache line size.
+ * Number of Tx & Rx descriptors must be powers of 2.
+ */
+#define        MPSC_RXR_ENTRIES        32
+#define        MPSC_RXRE_SIZE          dma_get_cache_alignment()
+#define        MPSC_RXR_SIZE           (MPSC_RXR_ENTRIES * MPSC_RXRE_SIZE)
+#define        MPSC_RXBE_SIZE          dma_get_cache_alignment()
+#define        MPSC_RXB_SIZE           (MPSC_RXR_ENTRIES * MPSC_RXBE_SIZE)
+
+#define        MPSC_TXR_ENTRIES        32
+#define        MPSC_TXRE_SIZE          dma_get_cache_alignment()
+#define        MPSC_TXR_SIZE           (MPSC_TXR_ENTRIES * MPSC_TXRE_SIZE)
+#define        MPSC_TXBE_SIZE          dma_get_cache_alignment()
+#define        MPSC_TXB_SIZE           (MPSC_TXR_ENTRIES * MPSC_TXBE_SIZE)
+
+#define        MPSC_DMA_ALLOC_SIZE     (MPSC_RXR_SIZE + MPSC_RXB_SIZE +        \
+                               MPSC_TXR_SIZE + MPSC_TXB_SIZE +         \
+                               dma_get_cache_alignment() /* for alignment */)
+
+/* Rx and Tx Ring entry descriptors -- assume entry size is <= cacheline size */
+struct mpsc_rx_desc {
+       u16 bufsize;
+       u16 bytecnt;
+       u32 cmdstat;
+       u32 link;
+       u32 buf_ptr;
+} __attribute((packed));
+
+struct mpsc_tx_desc {
+       u16 bytecnt;
+       u16 shadow;
+       u32 cmdstat;
+       u32 link;
+       u32 buf_ptr;
+} __attribute((packed));
+
+/*
+ * Some regs that have the erratum that you can't read them are are shared
+ * between the two MPSC controllers.  This struct contains those shared regs.
+ */
+struct mpsc_shared_regs {
+       phys_addr_t mpsc_routing_base_p;
+       phys_addr_t sdma_intr_base_p;
+
+       void *mpsc_routing_base;
+       void *sdma_intr_base;
+
+       u32 MPSC_MRR_m;
+       u32 MPSC_RCRR_m;
+       u32 MPSC_TCRR_m;
+       u32 SDMA_INTR_CAUSE_m;
+       u32 SDMA_INTR_MASK_m;
+};
+
+/* The main driver data structure */
+struct mpsc_port_info {
+       struct uart_port port;  /* Overlay uart_port structure */
+
+       /* Internal driver state for this ctlr */
+       u8 ready;
+       u8 rcv_data;
+       tcflag_t c_iflag;       /* save termios->c_iflag */
+       tcflag_t c_cflag;       /* save termios->c_cflag */
+
+       /* Info passed in from platform */
+       u8 mirror_regs;         /* Need to mirror regs? */
+       u8 cache_mgmt;          /* Need manual cache mgmt? */
+       u8 brg_can_tune;        /* BRG has baud tuning? */
+       u32 brg_clk_src;
+       u16 mpsc_max_idle;
+       int default_baud;
+       int default_bits;
+       int default_parity;
+       int default_flow;
+
+       /* Physical addresses of various blocks of registers (from platform) */
+       phys_addr_t mpsc_base_p;
+       phys_addr_t sdma_base_p;
+       phys_addr_t brg_base_p;
+
+       /* Virtual addresses of various blocks of registers (from platform) */
+       void *mpsc_base;
+       void *sdma_base;
+       void *brg_base;
+
+       /* Descriptor ring and buffer allocations */
+       void *dma_region;
+       dma_addr_t dma_region_p;
+
+       dma_addr_t rxr;         /* Rx descriptor ring */
+       dma_addr_t rxr_p;       /* Phys addr of rxr */
+       u8 *rxb;                /* Rx Ring I/O buf */
+       u8 *rxb_p;              /* Phys addr of rxb */
+       u32 rxr_posn;           /* First desc w/ Rx data */
+
+       dma_addr_t txr;         /* Tx descriptor ring */
+       dma_addr_t txr_p;       /* Phys addr of txr */
+       u8 *txb;                /* Tx Ring I/O buf */
+       u8 *txb_p;              /* Phys addr of txb */
+       int txr_head;           /* Where new data goes */
+       int txr_tail;           /* Where sent data comes off */
+
+       /* Mirrored values of regs we can't read (if 'mirror_regs' set) */
+       u32 MPSC_MPCR_m;
+       u32 MPSC_CHR_1_m;
+       u32 MPSC_CHR_2_m;
+       u32 MPSC_CHR_10_m;
+       u32 BRG_BCR_m;
+       struct mpsc_shared_regs *shared_regs;
+};
+
+/* Hooks to platform-specific code */
+int mpsc_platform_register_driver(void);
+void mpsc_platform_unregister_driver(void);
+
+/* Hooks back in to mpsc common to be called by platform-specific code */
+struct mpsc_port_info *mpsc_device_probe(int index);
+struct mpsc_port_info *mpsc_device_remove(int index);
+
+/*
+ *****************************************************************************
+ *
+ *     Multi-Protocol Serial Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+/* Main Configuratino Register Offsets */
+#define        MPSC_MMCRL                      0x0000
+#define        MPSC_MMCRH                      0x0004
+#define        MPSC_MPCR                       0x0008
+#define        MPSC_CHR_1                      0x000c
+#define        MPSC_CHR_2                      0x0010
+#define        MPSC_CHR_3                      0x0014
+#define        MPSC_CHR_4                      0x0018
+#define        MPSC_CHR_5                      0x001c
+#define        MPSC_CHR_6                      0x0020
+#define        MPSC_CHR_7                      0x0024
+#define        MPSC_CHR_8                      0x0028
+#define        MPSC_CHR_9                      0x002c
+#define        MPSC_CHR_10                     0x0030
+#define        MPSC_CHR_11                     0x0034
+
+#define        MPSC_MPCR_FRZ                   (1 << 9)
+#define        MPSC_MPCR_CL_5                  0
+#define        MPSC_MPCR_CL_6                  1
+#define        MPSC_MPCR_CL_7                  2
+#define        MPSC_MPCR_CL_8                  3
+#define        MPSC_MPCR_SBL_1                 0
+#define        MPSC_MPCR_SBL_2                 1
+
+#define        MPSC_CHR_2_TEV                  (1<<1)
+#define        MPSC_CHR_2_TA                   (1<<7)
+#define        MPSC_CHR_2_TTCS                 (1<<9)
+#define        MPSC_CHR_2_REV                  (1<<17)
+#define        MPSC_CHR_2_RA                   (1<<23)
+#define        MPSC_CHR_2_CRD                  (1<<25)
+#define        MPSC_CHR_2_EH                   (1<<31)
+#define        MPSC_CHR_2_PAR_ODD              0
+#define        MPSC_CHR_2_PAR_SPACE            1
+#define        MPSC_CHR_2_PAR_EVEN             2
+#define        MPSC_CHR_2_PAR_MARK             3
+
+/* MPSC Signal Routing */
+#define        MPSC_MRR                        0x0000
+#define        MPSC_RCRR                       0x0004
+#define        MPSC_TCRR                       0x0008
+
+/*
+ *****************************************************************************
+ *
+ *     Serial DMA Controller Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        SDMA_SDC                        0x0000
+#define        SDMA_SDCM                       0x0008
+#define        SDMA_RX_DESC                    0x0800
+#define        SDMA_RX_BUF_PTR                 0x0808
+#define        SDMA_SCRDP                      0x0810
+#define        SDMA_TX_DESC                    0x0c00
+#define        SDMA_SCTDP                      0x0c10
+#define        SDMA_SFTDP                      0x0c14
+
+#define        SDMA_DESC_CMDSTAT_PE            (1<<0)
+#define        SDMA_DESC_CMDSTAT_CDL           (1<<1)
+#define        SDMA_DESC_CMDSTAT_FR            (1<<3)
+#define        SDMA_DESC_CMDSTAT_OR            (1<<6)
+#define        SDMA_DESC_CMDSTAT_BR            (1<<9)
+#define        SDMA_DESC_CMDSTAT_MI            (1<<10)
+#define        SDMA_DESC_CMDSTAT_A             (1<<11)
+#define        SDMA_DESC_CMDSTAT_AM            (1<<12)
+#define        SDMA_DESC_CMDSTAT_CT            (1<<13)
+#define        SDMA_DESC_CMDSTAT_C             (1<<14)
+#define        SDMA_DESC_CMDSTAT_ES            (1<<15)
+#define        SDMA_DESC_CMDSTAT_L             (1<<16)
+#define        SDMA_DESC_CMDSTAT_F             (1<<17)
+#define        SDMA_DESC_CMDSTAT_P             (1<<18)
+#define        SDMA_DESC_CMDSTAT_EI            (1<<23)
+#define        SDMA_DESC_CMDSTAT_O             (1<<31)
+
+#define SDMA_DESC_DFLT                 (SDMA_DESC_CMDSTAT_O |  \
+                                       SDMA_DESC_CMDSTAT_EI)
+
+#define        SDMA_SDC_RFT                    (1<<0)
+#define        SDMA_SDC_SFM                    (1<<1)
+#define        SDMA_SDC_BLMR                   (1<<6)
+#define        SDMA_SDC_BLMT                   (1<<7)
+#define        SDMA_SDC_POVR                   (1<<8)
+#define        SDMA_SDC_RIFB                   (1<<9)
+
+#define        SDMA_SDCM_ERD                   (1<<7)
+#define        SDMA_SDCM_AR                    (1<<15)
+#define        SDMA_SDCM_STD                   (1<<16)
+#define        SDMA_SDCM_TXD                   (1<<23)
+#define        SDMA_SDCM_AT                    (1<<31)
+
+#define        SDMA_0_CAUSE_RXBUF              (1<<0)
+#define        SDMA_0_CAUSE_RXERR              (1<<1)
+#define        SDMA_0_CAUSE_TXBUF              (1<<2)
+#define        SDMA_0_CAUSE_TXEND              (1<<3)
+#define        SDMA_1_CAUSE_RXBUF              (1<<8)
+#define        SDMA_1_CAUSE_RXERR              (1<<9)
+#define        SDMA_1_CAUSE_TXBUF              (1<<10)
+#define        SDMA_1_CAUSE_TXEND              (1<<11)
+
+#define        SDMA_CAUSE_RX_MASK      (SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR | \
+       SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR)
+#define        SDMA_CAUSE_TX_MASK      (SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND | \
+       SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND)
+
+/* SDMA Interrupt registers */
+#define        SDMA_INTR_CAUSE                 0x0000
+#define        SDMA_INTR_MASK                  0x0080
+
+/*
+ *****************************************************************************
+ *
+ *     Baud Rate Generator Interface Registers
+ *
+ *****************************************************************************
+ */
+
+#define        BRG_BCR                         0x0000
+#define        BRG_BTR                         0x0004
+
+#endif                         /* __MPSC_H__ */
diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c
new file mode 100644 (file)
index 0000000..dfc9873
--- /dev/null
@@ -0,0 +1,1171 @@
+/*
+ *  drivers/serial/serial_txx9.c
+ *
+ * Derived from many drivers using generic_serial interface,
+ * especially serial_tx3912.c by Steven J. Hill and r39xx_serial.c
+ * (was in Linux/VR tree) by Jim Pick.
+ *
+ *  Copyright (C) 1999 Harald Koerfgen
+ *  Copyright (C) 2000 Jim Pick <jim@jimpick.com>
+ *  Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com)
+ *  Copyright (C) 2000-2002 Toshiba Corporation
+ *
+ * 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.
+ *
+ *  Serial driver for TX3927/TX4927/TX4925/TX4938 internal SIO controller
+ *
+ *  Revision History:
+ *     0.30    Initial revision. (Renamed from serial_txx927.c)
+ *     0.31    Use save_flags instead of local_irq_save.
+ *     0.32    Support SCLK.
+ *     0.33    Switch TXX9_TTY_NAME by CONFIG_SERIAL_TXX9_STDSERIAL.
+ *             Support TIOCSERGETLSR.
+ *     0.34    Support slow baudrate.
+ *     0.40    Merge codes from mainstream kernel (2.4.22).
+ *     0.41    Fix console checking in rs_shutdown_port().
+ *             Disable flow-control in serial_console_write().
+ *     0.42    Fix minor compiler warning.
+ *     1.00    Kernel 2.6.  Converted to new serial core (based on 8250.c).
+ *     1.01    Set fifosize to make tx_empry called properly.
+ *             Use standard uart_get_divisor.
+ *     1.02    Cleanup. (import 8250.c changes)
+ */
+#include <linux/config.h>
+
+#if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+static char *serial_version = "1.02";
+static char *serial_name = "TX39/49 Serial driver";
+
+#define PASS_LIMIT     256
+
+#if !defined(CONFIG_SERIAL_TXX9_STDSERIAL)
+/* "ttyS" is used for standard serial driver */
+#define TXX9_TTY_NAME "ttyTX"
+#define TXX9_TTY_DEVFS_NAME "tttx/"
+#define TXX9_TTY_MINOR_START   (64 + 64)       /* ttyTX0(128), ttyTX1(129) */
+#else
+/* acts like standard serial driver */
+#define TXX9_TTY_NAME "ttyS"
+#define TXX9_TTY_DEVFS_NAME "tts/"
+#define TXX9_TTY_MINOR_START   64
+#endif
+#define TXX9_TTY_MAJOR TTY_MAJOR
+
+/* flag aliases */
+#define UPF_TXX9_HAVE_CTS_LINE UPF_BUGGY_UART
+#define UPF_TXX9_USE_SCLK      UPF_MAGIC_MULTIPLIER
+
+#ifdef CONFIG_PCI
+/* support for Toshiba TC86C001 SIO */
+#define ENABLE_SERIAL_TXX9_PCI
+#endif
+
+/*
+ * Number of serial ports
+ */
+#ifdef ENABLE_SERIAL_TXX9_PCI
+#define NR_PCI_BOARDS  4
+#define UART_NR  (2 + NR_PCI_BOARDS)
+#else
+#define UART_NR  2
+#endif
+
+struct uart_txx9_port {
+       struct uart_port        port;
+
+       /*
+        * We provide a per-port pm hook.
+        */
+       void                    (*pm)(struct uart_port *port,
+                                     unsigned int state, unsigned int old);
+};
+
+#define TXX9_REGION_SIZE       0x24
+
+/* TXX9 Serial Registers */
+#define TXX9_SILCR     0x00
+#define TXX9_SIDICR    0x04
+#define TXX9_SIDISR    0x08
+#define TXX9_SICISR    0x0c
+#define TXX9_SIFCR     0x10
+#define TXX9_SIFLCR    0x14
+#define TXX9_SIBGR     0x18
+#define TXX9_SITFIFO   0x1c
+#define TXX9_SIRFIFO   0x20
+
+/* SILCR : Line Control */
+#define TXX9_SILCR_SCS_MASK    0x00000060
+#define TXX9_SILCR_SCS_IMCLK   0x00000000
+#define TXX9_SILCR_SCS_IMCLK_BG        0x00000020
+#define TXX9_SILCR_SCS_SCLK    0x00000040
+#define TXX9_SILCR_SCS_SCLK_BG 0x00000060
+#define TXX9_SILCR_UEPS        0x00000010
+#define TXX9_SILCR_UPEN        0x00000008
+#define TXX9_SILCR_USBL_MASK   0x00000004
+#define TXX9_SILCR_USBL_1BIT   0x00000000
+#define TXX9_SILCR_USBL_2BIT   0x00000004
+#define TXX9_SILCR_UMODE_MASK  0x00000003
+#define TXX9_SILCR_UMODE_8BIT  0x00000000
+#define TXX9_SILCR_UMODE_7BIT  0x00000001
+
+/* SIDICR : DMA/Int. Control */
+#define TXX9_SIDICR_TDE        0x00008000
+#define TXX9_SIDICR_RDE        0x00004000
+#define TXX9_SIDICR_TIE        0x00002000
+#define TXX9_SIDICR_RIE        0x00001000
+#define TXX9_SIDICR_SPIE       0x00000800
+#define TXX9_SIDICR_CTSAC      0x00000600
+#define TXX9_SIDICR_STIE_MASK  0x0000003f
+#define TXX9_SIDICR_STIE_OERS          0x00000020
+#define TXX9_SIDICR_STIE_CTSS          0x00000010
+#define TXX9_SIDICR_STIE_RBRKD 0x00000008
+#define TXX9_SIDICR_STIE_TRDY          0x00000004
+#define TXX9_SIDICR_STIE_TXALS 0x00000002
+#define TXX9_SIDICR_STIE_UBRKD 0x00000001
+
+/* SIDISR : DMA/Int. Status */
+#define TXX9_SIDISR_UBRK       0x00008000
+#define TXX9_SIDISR_UVALID     0x00004000
+#define TXX9_SIDISR_UFER       0x00002000
+#define TXX9_SIDISR_UPER       0x00001000
+#define TXX9_SIDISR_UOER       0x00000800
+#define TXX9_SIDISR_ERI        0x00000400
+#define TXX9_SIDISR_TOUT       0x00000200
+#define TXX9_SIDISR_TDIS       0x00000100
+#define TXX9_SIDISR_RDIS       0x00000080
+#define TXX9_SIDISR_STIS       0x00000040
+#define TXX9_SIDISR_RFDN_MASK  0x0000001f
+
+/* SICISR : Change Int. Status */
+#define TXX9_SICISR_OERS       0x00000020
+#define TXX9_SICISR_CTSS       0x00000010
+#define TXX9_SICISR_RBRKD      0x00000008
+#define TXX9_SICISR_TRDY       0x00000004
+#define TXX9_SICISR_TXALS      0x00000002
+#define TXX9_SICISR_UBRKD      0x00000001
+
+/* SIFCR : FIFO Control */
+#define TXX9_SIFCR_SWRST       0x00008000
+#define TXX9_SIFCR_RDIL_MASK   0x00000180
+#define TXX9_SIFCR_RDIL_1      0x00000000
+#define TXX9_SIFCR_RDIL_4      0x00000080
+#define TXX9_SIFCR_RDIL_8      0x00000100
+#define TXX9_SIFCR_RDIL_12     0x00000180
+#define TXX9_SIFCR_RDIL_MAX    0x00000180
+#define TXX9_SIFCR_TDIL_MASK   0x00000018
+#define TXX9_SIFCR_TDIL_MASK   0x00000018
+#define TXX9_SIFCR_TDIL_1      0x00000000
+#define TXX9_SIFCR_TDIL_4      0x00000001
+#define TXX9_SIFCR_TDIL_8      0x00000010
+#define TXX9_SIFCR_TDIL_MAX    0x00000010
+#define TXX9_SIFCR_TFRST       0x00000004
+#define TXX9_SIFCR_RFRST       0x00000002
+#define TXX9_SIFCR_FRSTE       0x00000001
+#define TXX9_SIO_TX_FIFO       8
+#define TXX9_SIO_RX_FIFO       16
+
+/* SIFLCR : Flow Control */
+#define TXX9_SIFLCR_RCS        0x00001000
+#define TXX9_SIFLCR_TES        0x00000800
+#define TXX9_SIFLCR_RTSSC      0x00000200
+#define TXX9_SIFLCR_RSDE       0x00000100
+#define TXX9_SIFLCR_TSDE       0x00000080
+#define TXX9_SIFLCR_RTSTL_MASK 0x0000001e
+#define TXX9_SIFLCR_RTSTL_MAX  0x0000001e
+#define TXX9_SIFLCR_TBRK       0x00000001
+
+/* SIBGR : Baudrate Control */
+#define TXX9_SIBGR_BCLK_MASK   0x00000300
+#define TXX9_SIBGR_BCLK_T0     0x00000000
+#define TXX9_SIBGR_BCLK_T2     0x00000100
+#define TXX9_SIBGR_BCLK_T4     0x00000200
+#define TXX9_SIBGR_BCLK_T6     0x00000300
+#define TXX9_SIBGR_BRD_MASK    0x000000ff
+
+static inline unsigned int sio_in(struct uart_txx9_port *up, int offset)
+{
+       switch (up->port.iotype) {
+       default:
+               return *(volatile u32 *)(up->port.membase + offset);
+       case UPIO_PORT:
+               return inl(up->port.iobase + offset);
+       }
+}
+
+static inline void
+sio_out(struct uart_txx9_port *up, int offset, int value)
+{
+       switch (up->port.iotype) {
+       default:
+               *(volatile u32 *)(up->port.membase + offset) = value;
+               break;
+       case UPIO_PORT:
+               outl(value, up->port.iobase + offset);
+               break;
+       }
+}
+
+static inline void
+sio_mask(struct uart_txx9_port *up, int offset, unsigned int value)
+{
+       sio_out(up, offset, sio_in(up, offset) & ~value);
+}
+static inline void
+sio_set(struct uart_txx9_port *up, int offset, unsigned int value)
+{
+       sio_out(up, offset, sio_in(up, offset) | value);
+}
+
+static inline void
+sio_quot_set(struct uart_txx9_port *up, int quot)
+{
+       quot >>= 1;
+       if (quot < 256)
+               sio_out(up, TXX9_SIBGR, quot | TXX9_SIBGR_BCLK_T0);
+       else if (quot < (256 << 2))
+               sio_out(up, TXX9_SIBGR, (quot >> 2) | TXX9_SIBGR_BCLK_T2);
+       else if (quot < (256 << 4))
+               sio_out(up, TXX9_SIBGR, (quot >> 4) | TXX9_SIBGR_BCLK_T4);
+       else if (quot < (256 << 6))
+               sio_out(up, TXX9_SIBGR, (quot >> 6) | TXX9_SIBGR_BCLK_T6);
+       else
+               sio_out(up, TXX9_SIBGR, 0xff | TXX9_SIBGR_BCLK_T6);
+}
+
+static void serial_txx9_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_TIE);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void serial_txx9_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       sio_set(up, TXX9_SIDICR, TXX9_SIDICR_TIE);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void serial_txx9_stop_rx(struct uart_port *port)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       up->port.read_status_mask &= ~TXX9_SIDISR_RDIS;
+#if 0
+       sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_RIE);
+#endif
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void serial_txx9_enable_ms(struct uart_port *port)
+{
+       /* TXX9-SIO can not control DTR... */
+}
+
+static inline void
+receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *regs)
+{
+       struct tty_struct *tty = up->port.info->tty;
+       unsigned char ch;
+       unsigned int disr = *status;
+       int max_count = 256;
+       char flag;
+
+       do {
+               /* The following is not allowed by the tty layer and
+                  unsafe. It should be fixed ASAP */
+               if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
+                       if(tty->low_latency)
+                               tty_flip_buffer_push(tty);
+                       /* If this failed then we will throw away the
+                          bytes but must do so to clear interrupts */
+               }
+               ch = sio_in(up, TXX9_SIRFIFO);
+               flag = TTY_NORMAL;
+               up->port.icount.rx++;
+
+               if (unlikely(disr & (TXX9_SIDISR_UBRK | TXX9_SIDISR_UPER |
+                                    TXX9_SIDISR_UFER | TXX9_SIDISR_UOER))) {
+                       /*
+                        * For statistics only
+                        */
+                       if (disr & TXX9_SIDISR_UBRK) {
+                               disr &= ~(TXX9_SIDISR_UFER | TXX9_SIDISR_UPER);
+                               up->port.icount.brk++;
+                               /*
+                                * We do the SysRQ and SAK checking
+                                * here because otherwise the break
+                                * may get masked by ignore_status_mask
+                                * or read_status_mask.
+                                */
+                               if (uart_handle_break(&up->port))
+                                       goto ignore_char;
+                       } else if (disr & TXX9_SIDISR_UPER)
+                               up->port.icount.parity++;
+                       else if (disr & TXX9_SIDISR_UFER)
+                               up->port.icount.frame++;
+                       if (disr & TXX9_SIDISR_UOER)
+                               up->port.icount.overrun++;
+
+                       /*
+                        * Mask off conditions which should be ingored.
+                        */
+                       disr &= up->port.read_status_mask;
+
+                       if (disr & TXX9_SIDISR_UBRK) {
+                               flag = TTY_BREAK;
+                       } else if (disr & TXX9_SIDISR_UPER)
+                               flag = TTY_PARITY;
+                       else if (disr & TXX9_SIDISR_UFER)
+                               flag = TTY_FRAME;
+               }
+               if (uart_handle_sysrq_char(&up->port, ch, regs))
+                       goto ignore_char;
+               if ((disr & up->port.ignore_status_mask) == 0) {
+                       tty_insert_flip_char(tty, ch, flag);
+               }
+               if ((disr & TXX9_SIDISR_UOER) &&
+                   tty->flip.count < TTY_FLIPBUF_SIZE) {
+                       /*
+                        * Overrun is special, since it's reported
+                        * immediately, and doesn't affect the current
+                        * character.
+                        */
+                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+               }
+       ignore_char:
+               disr = sio_in(up, TXX9_SIDISR);
+       } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0));
+       tty_flip_buffer_push(tty);
+       *status = disr;
+}
+
+static inline void transmit_chars(struct uart_txx9_port *up)
+{
+       struct circ_buf *xmit = &up->port.info->xmit;
+       int count;
+
+       if (up->port.x_char) {
+               sio_out(up, TXX9_SITFIFO, up->port.x_char);
+               up->port.icount.tx++;
+               up->port.x_char = 0;
+               return;
+       }
+       if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+               serial_txx9_stop_tx(&up->port, 0);
+               return;
+       }
+
+       count = TXX9_SIO_TX_FIFO;
+       do {
+               sio_out(up, TXX9_SITFIFO, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               up->port.icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (--count > 0);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&up->port);
+
+       if (uart_circ_empty(xmit))
+               serial_txx9_stop_tx(&up->port, 0);
+}
+
+static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       int pass_counter = 0;
+       struct uart_txx9_port *up = dev_id;
+       unsigned int status;
+
+       while (1) {
+               spin_lock(&up->port.lock);
+               status = sio_in(up, TXX9_SIDISR);
+               if (!(sio_in(up, TXX9_SIDICR) & TXX9_SIDICR_TIE))
+                       status &= ~TXX9_SIDISR_TDIS;
+               if (!(status & (TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS |
+                               TXX9_SIDISR_TOUT))) {
+                       spin_unlock(&up->port.lock);
+                       break;
+               }
+
+               if (status & TXX9_SIDISR_RDIS)
+                       receive_chars(up, &status, regs);
+               if (status & TXX9_SIDISR_TDIS)
+                       transmit_chars(up);
+               /* Clear TX/RX Int. Status */
+               sio_mask(up, TXX9_SIDISR,
+                        TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS |
+                        TXX9_SIDISR_TOUT);
+               spin_unlock(&up->port.lock);
+
+               if (pass_counter++ > PASS_LIMIT)
+                       break;
+       }
+
+       return pass_counter ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static unsigned int serial_txx9_tx_empty(struct uart_port *port)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+       unsigned int ret;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       ret = (sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS) ? TIOCSER_TEMT : 0;
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       return ret;
+}
+
+static unsigned int serial_txx9_get_mctrl(struct uart_port *port)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+       unsigned int ret;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       ret =  ((sio_in(up, TXX9_SIFLCR) & TXX9_SIFLCR_RTSSC) ? 0 : TIOCM_RTS)
+               | ((sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS) ? 0 : TIOCM_CTS);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       return ret;
+}
+
+static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       if (mctrl & TIOCM_RTS)
+               sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC);
+       else
+               sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void serial_txx9_break_ctl(struct uart_port *port, int break_state)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       if (break_state == -1)
+               sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK);
+       else
+               sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int serial_txx9_startup(struct uart_port *port)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+       int retval;
+
+       /*
+        * Clear the FIFO buffers and disable them.
+        * (they will be reeanbled in set_termios())
+        */
+       sio_set(up, TXX9_SIFCR,
+               TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE);
+       /* clear reset */
+       sio_mask(up, TXX9_SIFCR,
+                TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE);
+       sio_out(up, TXX9_SIDICR, 0);
+
+       /*
+        * Clear the interrupt registers.
+        */
+       sio_out(up, TXX9_SIDISR, 0);
+
+       retval = request_irq(up->port.irq, serial_txx9_interrupt,
+                            SA_SHIRQ, "serial_txx9", up);
+       if (retval)
+               return retval;
+
+       /*
+        * Now, initialize the UART
+        */
+       spin_lock_irqsave(&up->port.lock, flags);
+       serial_txx9_set_mctrl(&up->port, up->port.mctrl);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       /* Enable RX/TX */
+       sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE);
+
+       /*
+        * Finally, enable interrupts.
+        */
+       sio_set(up, TXX9_SIDICR, TXX9_SIDICR_RIE);
+
+       return 0;
+}
+
+static void serial_txx9_shutdown(struct uart_port *port)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+
+       /*
+        * Disable interrupts from this port
+        */
+       sio_out(up, TXX9_SIDICR, 0);    /* disable all intrs */
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       serial_txx9_set_mctrl(&up->port, up->port.mctrl);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       /*
+        * Disable break condition
+        */
+       sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK);
+
+#ifdef CONFIG_SERIAL_TXX9_CONSOLE
+       if (up->port.cons && up->port.line == up->port.cons->index) {
+               free_irq(up->port.irq, up);
+               return;
+       }
+#endif
+       /* reset FIFOs */
+       sio_set(up, TXX9_SIFCR,
+               TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE);
+       /* clear reset */
+       sio_mask(up, TXX9_SIFCR,
+                TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE);
+
+       /* Disable RX/TX */
+       sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE);
+
+       free_irq(up->port.irq, up);
+}
+
+static void
+serial_txx9_set_termios(struct uart_port *port, struct termios *termios,
+                      struct termios *old)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned int cval, fcr = 0;
+       unsigned long flags;
+       unsigned int baud, quot;
+
+       cval = sio_in(up, TXX9_SILCR);
+       /* byte size and parity */
+       cval &= ~TXX9_SILCR_UMODE_MASK;
+       switch (termios->c_cflag & CSIZE) {
+       case CS7:
+               cval |= TXX9_SILCR_UMODE_7BIT;
+               break;
+       default:
+       case CS5:       /* not supported */
+       case CS6:       /* not supported */
+       case CS8:
+               cval |= TXX9_SILCR_UMODE_8BIT;
+               break;
+       }
+
+       cval &= ~TXX9_SILCR_USBL_MASK;
+       if (termios->c_cflag & CSTOPB)
+               cval |= TXX9_SILCR_USBL_2BIT;
+       else
+               cval |= TXX9_SILCR_USBL_1BIT;
+       cval &= ~(TXX9_SILCR_UPEN | TXX9_SILCR_UEPS);
+       if (termios->c_cflag & PARENB)
+               cval |= TXX9_SILCR_UPEN;
+       if (!(termios->c_cflag & PARODD))
+               cval |= TXX9_SILCR_UEPS;
+
+       /*
+        * Ask the core to calculate the divisor for us.
+        */
+       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16/2);
+       quot = uart_get_divisor(port, baud);
+
+       /* Set up FIFOs */
+       /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */
+       fcr = TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1;
+
+       /*
+        * Ok, we're now changing the port state.  Do it with
+        * interrupts disabled.
+        */
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       /*
+        * Update the per-port timeout.
+        */
+       uart_update_timeout(port, termios->c_cflag, baud);
+
+       up->port.read_status_mask = TXX9_SIDISR_UOER |
+               TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS;
+       if (termios->c_iflag & INPCK)
+               up->port.read_status_mask |= TXX9_SIDISR_UFER | TXX9_SIDISR_UPER;
+       if (termios->c_iflag & (BRKINT | PARMRK))
+               up->port.read_status_mask |= TXX9_SIDISR_UBRK;
+
+       /*
+        * Characteres to ignore
+        */
+       up->port.ignore_status_mask = 0;
+       if (termios->c_iflag & IGNPAR)
+               up->port.ignore_status_mask |= TXX9_SIDISR_UPER | TXX9_SIDISR_UFER;
+       if (termios->c_iflag & IGNBRK) {
+               up->port.ignore_status_mask |= TXX9_SIDISR_UBRK;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too (for real raw support).
+                */
+               if (termios->c_iflag & IGNPAR)
+                       up->port.ignore_status_mask |= TXX9_SIDISR_UOER;
+       }
+
+       /*
+        * ignore all characters if CREAD is not set
+        */
+       if ((termios->c_cflag & CREAD) == 0)
+               up->port.ignore_status_mask |= TXX9_SIDISR_RDIS;
+
+       /* CTS flow control flag */
+       if ((termios->c_cflag & CRTSCTS) &&
+           (up->port.flags & UPF_TXX9_HAVE_CTS_LINE)) {
+               sio_set(up, TXX9_SIFLCR,
+                       TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES);
+       } else {
+               sio_mask(up, TXX9_SIFLCR,
+                        TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES);
+       }
+
+       sio_out(up, TXX9_SILCR, cval);
+       sio_quot_set(up, quot);
+       sio_out(up, TXX9_SIFCR, fcr);
+
+       serial_txx9_set_mctrl(&up->port, up->port.mctrl);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial_txx9_pm(struct uart_port *port, unsigned int state,
+             unsigned int oldstate)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       if (state) {
+               /* sleep */
+
+               if (up->pm)
+                       up->pm(port, state, oldstate);
+       } else {
+               /* wake */
+
+               if (up->pm)
+                       up->pm(port, state, oldstate);
+       }
+}
+
+static int serial_txx9_request_resource(struct uart_txx9_port *up)
+{
+       unsigned int size = TXX9_REGION_SIZE;
+       int ret = 0;
+
+       switch (up->port.iotype) {
+       default:
+               if (!up->port.mapbase)
+                       break;
+
+               if (!request_mem_region(up->port.mapbase, size, "serial_txx9")) {
+                       ret = -EBUSY;
+                       break;
+               }
+
+               if (up->port.flags & UPF_IOREMAP) {
+                       up->port.membase = ioremap(up->port.mapbase, size);
+                       if (!up->port.membase) {
+                               release_mem_region(up->port.mapbase, size);
+                               ret = -ENOMEM;
+                       }
+               }
+               break;
+
+       case UPIO_PORT:
+               if (!request_region(up->port.iobase, size, "serial_txx9"))
+                       ret = -EBUSY;
+               break;
+       }
+       return ret;
+}
+
+static void serial_txx9_release_resource(struct uart_txx9_port *up)
+{
+       unsigned int size = TXX9_REGION_SIZE;
+
+       switch (up->port.iotype) {
+       default:
+               if (!up->port.mapbase)
+                       break;
+
+               if (up->port.flags & UPF_IOREMAP) {
+                       iounmap(up->port.membase);
+                       up->port.membase = NULL;
+               }
+
+               release_mem_region(up->port.mapbase, size);
+               break;
+
+       case UPIO_PORT:
+               release_region(up->port.iobase, size);
+               break;
+       }
+}
+
+static void serial_txx9_release_port(struct uart_port *port)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       serial_txx9_release_resource(up);
+}
+
+static int serial_txx9_request_port(struct uart_port *port)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       return serial_txx9_request_resource(up);
+}
+
+static void serial_txx9_config_port(struct uart_port *port, int uflags)
+{
+       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+       int ret;
+
+       /*
+        * Find the region that we can probe for.  This in turn
+        * tells us whether we can probe for the type of port.
+        */
+       ret = serial_txx9_request_resource(up);
+       if (ret < 0)
+               return;
+       port->type = PORT_TXX9;
+       up->port.fifosize = TXX9_SIO_TX_FIFO;
+
+#ifdef CONFIG_SERIAL_TXX9_CONSOLE
+       if (up->port.line == up->port.cons->index)
+               return;
+#endif
+       spin_lock_irqsave(&up->port.lock, flags);
+       /*
+        * Reset the UART.
+        */
+       sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST);
+#ifdef CONFIG_CPU_TX49XX
+       /* TX4925 BUG WORKAROUND.  Accessing SIOC register
+        * immediately after soft reset causes bus error. */
+       iob();
+       udelay(1);
+#endif
+       while (sio_in(up, TXX9_SIFCR) & TXX9_SIFCR_SWRST)
+               ;
+       /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */
+       sio_set(up, TXX9_SIFCR,
+               TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1);
+       /* initial settings */
+       sio_out(up, TXX9_SILCR,
+               TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT |
+               ((up->port.flags & UPF_TXX9_USE_SCLK) ?
+                TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG));
+       sio_quot_set(up, uart_get_divisor(port, 9600));
+       sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int
+serial_txx9_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       if (ser->irq < 0 ||
+           ser->baud_base < 9600 || ser->type != PORT_TXX9)
+               return -EINVAL;
+       return 0;
+}
+
+static const char *
+serial_txx9_type(struct uart_port *port)
+{
+       return "txx9";
+}
+
+static struct uart_ops serial_txx9_pops = {
+       .tx_empty       = serial_txx9_tx_empty,
+       .set_mctrl      = serial_txx9_set_mctrl,
+       .get_mctrl      = serial_txx9_get_mctrl,
+       .stop_tx        = serial_txx9_stop_tx,
+       .start_tx       = serial_txx9_start_tx,
+       .stop_rx        = serial_txx9_stop_rx,
+       .enable_ms      = serial_txx9_enable_ms,
+       .break_ctl      = serial_txx9_break_ctl,
+       .startup        = serial_txx9_startup,
+       .shutdown       = serial_txx9_shutdown,
+       .set_termios    = serial_txx9_set_termios,
+       .pm             = serial_txx9_pm,
+       .type           = serial_txx9_type,
+       .release_port   = serial_txx9_release_port,
+       .request_port   = serial_txx9_request_port,
+       .config_port    = serial_txx9_config_port,
+       .verify_port    = serial_txx9_verify_port,
+};
+
+static struct uart_txx9_port serial_txx9_ports[UART_NR];
+
+static void __init serial_txx9_register_ports(struct uart_driver *drv)
+{
+       int i;
+
+       for (i = 0; i < UART_NR; i++) {
+               struct uart_txx9_port *up = &serial_txx9_ports[i];
+
+               up->port.line = i;
+               up->port.ops = &serial_txx9_pops;
+               uart_add_one_port(drv, &up->port);
+       }
+}
+
+#ifdef CONFIG_SERIAL_TXX9_CONSOLE
+
+/*
+ *     Wait for transmitter & holding register to empty
+ */
+static inline void wait_for_xmitr(struct uart_txx9_port *up)
+{
+       unsigned int tmout = 10000;
+
+       /* Wait up to 10ms for the character(s) to be sent. */
+       while (--tmout &&
+              !(sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS))
+               udelay(1);
+
+       /* Wait up to 1s for flow control if necessary */
+       if (up->port.flags & UPF_CONS_FLOW) {
+               tmout = 1000000;
+               while (--tmout &&
+                      (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS))
+                       udelay(1);
+       }
+}
+
+/*
+ *     Print a string to the serial port trying not to disturb
+ *     any possible real use of the port...
+ *
+ *     The console_lock must be held when we get here.
+ */
+static void
+serial_txx9_console_write(struct console *co, const char *s, unsigned int count)
+{
+       struct uart_txx9_port *up = &serial_txx9_ports[co->index];
+       unsigned int ier, flcr;
+       int i;
+
+       /*
+        *      First save the UER then disable the interrupts
+        */
+       ier = sio_in(up, TXX9_SIDICR);
+       sio_out(up, TXX9_SIDICR, 0);
+       /*
+        *      Disable flow-control if enabled (and unnecessary)
+        */
+       flcr = sio_in(up, TXX9_SIFLCR);
+       if (!(up->port.flags & UPF_CONS_FLOW) && (flcr & TXX9_SIFLCR_TES))
+               sio_out(up, TXX9_SIFLCR, flcr & ~TXX9_SIFLCR_TES);
+
+       /*
+        *      Now, do each character
+        */
+       for (i = 0; i < count; i++, s++) {
+               wait_for_xmitr(up);
+
+               /*
+                *      Send the character out.
+                *      If a LF, also do CR...
+                */
+               sio_out(up, TXX9_SITFIFO, *s);
+               if (*s == 10) {
+                       wait_for_xmitr(up);
+                       sio_out(up, TXX9_SITFIFO, 13);
+               }
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore the IER
+        */
+       wait_for_xmitr(up);
+       sio_out(up, TXX9_SIFLCR, flcr);
+       sio_out(up, TXX9_SIDICR, ier);
+}
+
+static int serial_txx9_console_setup(struct console *co, char *options)
+{
+       struct uart_port *port;
+       struct uart_txx9_port *up;
+       int baud = 9600;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       /*
+        * Check whether an invalid uart number has been specified, and
+        * if so, search for the first available port that does have
+        * console support.
+        */
+       if (co->index >= UART_NR)
+               co->index = 0;
+       up = &serial_txx9_ports[co->index];
+       port = &up->port;
+       if (!port->ops)
+               return -ENODEV;
+
+       /*
+        * Temporary fix.
+        */
+       spin_lock_init(&port->lock);
+
+       /*
+        *      Disable UART interrupts, set DTR and RTS high
+        *      and set speed.
+        */
+       sio_out(up, TXX9_SIDICR, 0);
+       /* initial settings */
+       sio_out(up, TXX9_SILCR,
+               TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT |
+               ((port->flags & UPF_TXX9_USE_SCLK) ?
+                TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG));
+       sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */);
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+       return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver serial_txx9_reg;
+static struct console serial_txx9_console = {
+       .name           = TXX9_TTY_NAME,
+       .write          = serial_txx9_console_write,
+       .device         = uart_console_device,
+       .setup          = serial_txx9_console_setup,
+       .flags          = CON_PRINTBUFFER,
+       .index          = -1,
+       .data           = &serial_txx9_reg,
+};
+
+static int __init serial_txx9_console_init(void)
+{
+       register_console(&serial_txx9_console);
+       return 0;
+}
+console_initcall(serial_txx9_console_init);
+
+static int __init serial_txx9_late_console_init(void)
+{
+       if (!(serial_txx9_console.flags & CON_ENABLED))
+               register_console(&serial_txx9_console);
+       return 0;
+}
+late_initcall(serial_txx9_late_console_init);
+
+#define SERIAL_TXX9_CONSOLE    &serial_txx9_console
+#else
+#define SERIAL_TXX9_CONSOLE    NULL
+#endif
+
+static struct uart_driver serial_txx9_reg = {
+       .owner                  = THIS_MODULE,
+       .driver_name            = "serial_txx9",
+       .devfs_name             = TXX9_TTY_DEVFS_NAME,
+       .dev_name               = TXX9_TTY_NAME,
+       .major                  = TXX9_TTY_MAJOR,
+       .minor                  = TXX9_TTY_MINOR_START,
+       .nr                     = UART_NR,
+       .cons                   = SERIAL_TXX9_CONSOLE,
+};
+
+int __init early_serial_txx9_setup(struct uart_port *port)
+{
+       if (port->line >= ARRAY_SIZE(serial_txx9_ports))
+               return -ENODEV;
+
+       serial_txx9_ports[port->line].port = *port;
+       serial_txx9_ports[port->line].port.ops = &serial_txx9_pops;
+       serial_txx9_ports[port->line].port.flags |= UPF_BOOT_AUTOCONF;
+       return 0;
+}
+
+#ifdef ENABLE_SERIAL_TXX9_PCI
+/**
+ *     serial_txx9_suspend_port - suspend one serial port
+ *     @line:  serial line number
+ *      @level: the level of port suspension, as per uart_suspend_port
+ *
+ *     Suspend one serial port.
+ */
+static void serial_txx9_suspend_port(int line)
+{
+       uart_suspend_port(&serial_txx9_reg, &serial_txx9_ports[line].port);
+}
+
+/**
+ *     serial_txx9_resume_port - resume one serial port
+ *     @line:  serial line number
+ *      @level: the level of port resumption, as per uart_resume_port
+ *
+ *     Resume one serial port.
+ */
+static void serial_txx9_resume_port(int line)
+{
+       uart_resume_port(&serial_txx9_reg, &serial_txx9_ports[line].port);
+}
+
+/*
+ * Probe one serial board.  Unfortunately, there is no rhyme nor reason
+ * to the arrangement of serial ports on a PCI card.
+ */
+static int __devinit
+pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+       struct uart_port port;
+       int line;
+       int rc;
+
+       rc = pci_enable_device(dev);
+       if (rc)
+               return rc;
+
+       memset(&port, 0, sizeof(port));
+       port.ops = &serial_txx9_pops;
+       port.flags |= UPF_BOOT_AUTOCONF; /* uart_ops.config_port will be called */
+       port.flags |= UPF_TXX9_HAVE_CTS_LINE;
+       port.uartclk = 66670000;
+       port.irq = dev->irq;
+       port.iotype = UPIO_PORT;
+       port.iobase = pci_resource_start(dev, 1);
+       line = uart_register_port(&serial_txx9_reg, &port);
+       if (line < 0) {
+               printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), line);
+       }
+       pci_set_drvdata(dev, (void *)(long)line);
+
+       return 0;
+}
+
+static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev)
+{
+       int line = (int)(long)pci_get_drvdata(dev);
+
+       pci_set_drvdata(dev, NULL);
+
+       if (line) {
+               uart_unregister_port(&serial_txx9_reg, line);
+               pci_disable_device(dev);
+       }
+}
+
+static int pciserial_txx9_suspend_one(struct pci_dev *dev, u32 state)
+{
+       int line = (int)(long)pci_get_drvdata(dev);
+
+       if (line)
+               serial_txx9_suspend_port(line);
+       return 0;
+}
+
+static int pciserial_txx9_resume_one(struct pci_dev *dev)
+{
+       int line = (int)(long)pci_get_drvdata(dev);
+
+       if (line)
+               serial_txx9_resume_port(line);
+       return 0;
+}
+
+static struct pci_device_id serial_txx9_pci_tbl[] = {
+       {       PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC,
+               PCI_ANY_ID, PCI_ANY_ID,
+               0, 0, 0 },
+       { 0, }
+};
+
+static struct pci_driver serial_txx9_pci_driver = {
+       .name           = "serial_txx9",
+       .probe          = pciserial_txx9_init_one,
+       .remove         = __devexit_p(pciserial_txx9_remove_one),
+       .suspend        = pciserial_txx9_suspend_one,
+       .resume         = pciserial_txx9_resume_one,
+       .id_table       = serial_txx9_pci_tbl,
+};
+
+MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl);
+#endif /* ENABLE_SERIAL_TXX9_PCI */
+
+static int __init serial_txx9_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+
+       ret = uart_register_driver(&serial_txx9_reg);
+       if (ret >= 0) {
+               serial_txx9_register_ports(&serial_txx9_reg);
+
+#ifdef ENABLE_SERIAL_TXX9_PCI
+               ret = pci_module_init(&serial_txx9_pci_driver);
+#endif
+       }
+       return ret;
+}
+
+static void __exit serial_txx9_exit(void)
+{
+       int i;
+
+#ifdef ENABLE_SERIAL_TXX9_PCI
+       pci_unregister_driver(&serial_txx9_pci_driver);
+#endif
+       for (i = 0; i < UART_NR; i++)
+               uart_remove_one_port(&serial_txx9_reg, &serial_txx9_ports[i].port);
+
+       uart_unregister_driver(&serial_txx9_reg);
+}
+
+module_init(serial_txx9_init);
+module_exit(serial_txx9_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TX39/49 serial driver");
+
+MODULE_ALIAS_CHARDEV_MAJOR(TXX9_TTY_MAJOR);
diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig
new file mode 100644 (file)
index 0000000..0d9f537
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# USB ATM driver configuration
+#
+comment "USB ATM/DSL drivers"
+       depends on USB
+
+config USB_ATM
+       tristate "Generic USB ATM/DSL core I/O support"
+       depends on USB && ATM
+       select CRC32
+       default n
+       help
+         This provides a library which is used for packet I/O by USB DSL
+         modems, such as the SpeedTouch driver below. 
+
+         To compile this driver as a module, choose M here: the
+         module will be called usb_atm.
+
+config USB_SPEEDTOUCH
+       tristate "Alcatel Speedtouch USB support"
+       depends on USB && ATM
+       select USB_ATM
+       help
+         Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330
+         modem.  In order to use your modem you will need to install the 
+         two parts of the firmware, extracted by the user space tools; see
+         <http://www.linux-usb.org/SpeedTouch/> for details.
+
+         To compile this driver as a module, choose M here: the
+         module will be called speedtch.
diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile
new file mode 100644 (file)
index 0000000..9213b8b
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the rest of the USB drivers
+# (the ones that don't fit into any other categories)
+#
+
+obj-$(CONFIG_USB_ATM)          += usb_atm.o
+obj-$(CONFIG_USB_SPEEDTOUCH)   += speedtch.o
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
new file mode 100644 (file)
index 0000000..c06a3c1
--- /dev/null
@@ -0,0 +1,863 @@
+/******************************************************************************
+ *  speedtch.c  -  Alcatel SpeedTouch USB xDSL modem driver
+ *
+ *  Copyright (C) 2001, Alcatel
+ *  Copyright (C) 2003, Duncan Sands
+ *  Copyright (C) 2004, David Woodhouse
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the Free
+ *  Software Foundation; either version 2 of the License, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ *  more details.
+ *
+ *  You should have received a copy of the GNU General Public License along with
+ *  this program; if not, write to the Free Software Foundation, Inc., 59
+ *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "usb_atm.h"
+
+/*
+#define DEBUG
+#define VERBOSE_DEBUG
+*/
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+#      define DEBUG
+#endif
+
+#include <linux/usb.h>
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+#      define USE_FW_LOADER
+#endif
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len);
+#define PACKETDEBUG(arg...)    udsl_print_packet (arg)
+#define vdbg(arg...)           dbg (arg)
+#else
+#define PACKETDEBUG(arg...)
+#define vdbg(arg...)
+#endif
+
+#define DRIVER_AUTHOR  "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
+#define DRIVER_VERSION "1.8"
+#define DRIVER_DESC    "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
+
+static const char speedtch_driver_name[] = "speedtch";
+
+#define SPEEDTOUCH_VENDORID            0x06b9
+#define SPEEDTOUCH_PRODUCTID           0x4061
+
+/* Timeout in jiffies */
+#define CTRL_TIMEOUT (2*HZ)
+#define DATA_TIMEOUT (2*HZ)
+
+#define OFFSET_7  0            /* size 1 */
+#define OFFSET_b  1            /* size 8 */
+#define OFFSET_d  9            /* size 4 */
+#define OFFSET_e 13            /* size 1 */
+#define OFFSET_f 14            /* size 1 */
+#define TOTAL    15
+
+#define SIZE_7 1
+#define SIZE_b 8
+#define SIZE_d 4
+#define SIZE_e 1
+#define SIZE_f 1
+
+static int dl_512_first = 0;
+static int sw_buffering = 0;
+
+module_param(dl_512_first, bool, 0444);
+MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware");
+
+module_param(sw_buffering, uint, 0444);
+MODULE_PARM_DESC(sw_buffering, "Enable software buffering");
+
+#define UDSL_IOCTL_LINE_UP             1
+#define UDSL_IOCTL_LINE_DOWN           2
+
+#define SPEEDTCH_ENDPOINT_INT          0x81
+#define SPEEDTCH_ENDPOINT_DATA         0x07
+#define SPEEDTCH_ENDPOINT_FIRMWARE     0x05
+
+#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
+
+static struct usb_device_id speedtch_usb_ids[] = {
+       {USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)},
+       {}
+};
+
+MODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
+
+struct speedtch_instance_data {
+       struct udsl_instance_data u;
+
+       /* Status */
+       struct urb *int_urb;
+       unsigned char int_data[16];
+       struct work_struct poll_work;
+       struct timer_list poll_timer;
+};
+/* USB */
+
+static int speedtch_usb_probe(struct usb_interface *intf,
+                             const struct usb_device_id *id);
+static void speedtch_usb_disconnect(struct usb_interface *intf);
+static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
+                             void *user_data);
+static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs);
+static void speedtch_poll_status(struct speedtch_instance_data *instance);
+
+static struct usb_driver speedtch_usb_driver = {
+       .owner          = THIS_MODULE,
+       .name           = speedtch_driver_name,
+       .probe          = speedtch_usb_probe,
+       .disconnect     = speedtch_usb_disconnect,
+       .ioctl          = speedtch_usb_ioctl,
+       .id_table       = speedtch_usb_ids,
+};
+
+/***************
+**  firmware  **
+***************/
+
+static void speedtch_got_firmware(struct speedtch_instance_data *instance,
+                                 int got_it)
+{
+       int err;
+       struct usb_interface *intf;
+
+       down(&instance->u.serialize);   /* vs self, speedtch_firmware_start */
+       if (instance->u.status == UDSL_LOADED_FIRMWARE)
+               goto out;
+       if (!got_it) {
+               instance->u.status = UDSL_NO_FIRMWARE;
+               goto out;
+       }
+       if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) {
+               dbg("speedtch_got_firmware: usb_set_interface returned %d!", err);
+               instance->u.status = UDSL_NO_FIRMWARE;
+               goto out;
+       }
+
+       /* Set up interrupt endpoint */
+       intf = usb_ifnum_to_if(instance->u.usb_dev, 0);
+       if (intf && !usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) {
+
+               instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (instance->int_urb) {
+
+                       usb_fill_int_urb(instance->int_urb, instance->u.usb_dev,
+                                        usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT),
+                                        instance->int_data,
+                                        sizeof(instance->int_data),
+                                        speedtch_handle_int, instance, 50);
+                       err = usb_submit_urb(instance->int_urb, GFP_KERNEL);
+                       if (err) {
+                               /* Doesn't matter; we'll poll anyway */
+                               dbg("speedtch_got_firmware: Submission of interrupt URB failed %d", err);
+                               usb_free_urb(instance->int_urb);
+                               instance->int_urb = NULL;
+                               usb_driver_release_interface(&speedtch_usb_driver, intf);
+                       }
+               }
+       }
+       /* Start status polling */
+       mod_timer(&instance->poll_timer, jiffies + (1 * HZ));
+
+       instance->u.status = UDSL_LOADED_FIRMWARE;
+       tasklet_schedule(&instance->u.receive_tasklet);
+ out:
+       up(&instance->u.serialize);
+       wake_up_interruptible(&instance->u.firmware_waiters);
+}
+
+static int speedtch_set_swbuff(struct speedtch_instance_data *instance,
+                              int state)
+{
+       struct usb_device *dev = instance->u.usb_dev;
+       int ret;
+
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0x32, 0x40, state ? 0x01 : 0x00,
+                             0x00, NULL, 0, 100);
+       if (ret < 0) {
+               printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n",
+                    state ? "En" : "Dis", ret);
+               return ret;
+       }
+
+       dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
+       return 0;
+}
+
+static void speedtch_test_sequence(struct speedtch_instance_data *instance)
+{
+       struct usb_device *dev = instance->u.usb_dev;
+       unsigned char buf[10];
+       int ret;
+
+       /* URB 147 */
+       buf[0] = 0x1c;
+       buf[1] = 0x50;
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0x01, 0x40, 0x0b, 0x00, buf, 2, 100);
+       if (ret < 0)
+               printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret);
+
+       /* URB 148 */
+       buf[0] = 0x32;
+       buf[1] = 0x00;
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0x01, 0x40, 0x02, 0x00, buf, 2, 100);
+       if (ret < 0)
+               printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret);
+
+       /* URB 149 */
+       buf[0] = 0x01;
+       buf[1] = 0x00;
+       buf[2] = 0x01;
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0x01, 0x40, 0x03, 0x00, buf, 3, 100);
+       if (ret < 0)
+               printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret);
+
+       /* URB 150 */
+       buf[0] = 0x01;
+       buf[1] = 0x00;
+       buf[2] = 0x01;
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0x01, 0x40, 0x04, 0x00, buf, 3, 100);
+       if (ret < 0)
+               printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret);
+}
+
+static int speedtch_start_synchro(struct speedtch_instance_data *instance)
+{
+       struct usb_device *dev = instance->u.usb_dev;
+       unsigned char buf[2];
+       int ret;
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x12, 0xc0, 0x04, 0x00,
+                             buf, sizeof(buf), CTRL_TIMEOUT);
+       if (ret < 0) {
+               printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret);
+               return ret;
+       }
+
+       dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]);
+       return 0;
+}
+
+static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs)
+{
+       struct speedtch_instance_data *instance = urb->context;
+       unsigned int count = urb->actual_length;
+       int ret;
+
+       /* The magic interrupt for "up state" */
+       const static unsigned char up_int[6]   = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 };
+       /* The magic interrupt for "down state" */
+       const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       switch (urb->status) {
+       case 0:
+               /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               /* this urb is terminated; clean up */
+               dbg("%s - urb shutting down with status: %d", __func__, urb->status);
+               return;
+       default:
+               dbg("%s - nonzero urb status received: %d", __func__, urb->status);
+               goto exit;
+       }
+
+       if (count < 6) {
+               dbg("%s - int packet too short", __func__);
+               goto exit;
+       }
+
+       if (!memcmp(up_int, instance->int_data, 6)) {
+               del_timer(&instance->poll_timer);
+               printk(KERN_NOTICE "DSL line goes up\n");
+       } else if (!memcmp(down_int, instance->int_data, 6)) {
+               printk(KERN_NOTICE "DSL line goes down\n");
+       } else {
+               int i;
+
+               printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count);
+               for (i = 0; i < count; i++)
+                       printk(" %02x", instance->int_data[i]);
+               printk("\n");
+       }
+       schedule_work(&instance->poll_work);
+
+ exit:
+       rmb();
+       if (!instance->int_urb)
+               return;
+
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret)
+               err("%s - usb_submit_urb failed with result %d", __func__, ret);
+}
+
+static int speedtch_get_status(struct speedtch_instance_data *instance,
+                              unsigned char *buf)
+{
+       struct usb_device *dev = instance->u.usb_dev;
+       int ret;
+
+       memset(buf, 0, TOTAL);
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
+                             CTRL_TIMEOUT);
+       if (ret < 0) {
+               dbg("MSG 7 failed");
+               return ret;
+       }
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b,
+                             CTRL_TIMEOUT);
+       if (ret < 0) {
+               dbg("MSG B failed");
+               return ret;
+       }
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d,
+                             CTRL_TIMEOUT);
+       if (ret < 0) {
+               dbg("MSG D failed");
+               return ret;
+       }
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e,
+                             CTRL_TIMEOUT);
+       if (ret < 0) {
+               dbg("MSG E failed");
+               return ret;
+       }
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f,
+                             CTRL_TIMEOUT);
+       if (ret < 0) {
+               dbg("MSG F failed");
+               return ret;
+       }
+
+       return 0;
+}
+
+static void speedtch_poll_status(struct speedtch_instance_data *instance)
+{
+       unsigned char buf[TOTAL];
+       int ret;
+
+       ret = speedtch_get_status(instance, buf);
+       if (ret) {
+               printk(KERN_WARNING
+                      "SpeedTouch: Error %d fetching device status\n", ret);
+               return;
+       }
+
+       dbg("Line state %02x", buf[OFFSET_7]);
+
+       switch (buf[OFFSET_7]) {
+       case 0:
+               if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
+                       instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+                       printk(KERN_NOTICE "ADSL line is down\n");
+               }
+               break;
+
+       case 0x08:
+               if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
+                       instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+                       printk(KERN_NOTICE "ADSL line is blocked?\n");
+               }
+               break;
+
+       case 0x10:
+               if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
+                       instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+                       printk(KERN_NOTICE "ADSL line is synchronising\n");
+               }
+               break;
+
+       case 0x20:
+               if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) {
+                       int down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8)
+                               | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24);
+                       int up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8)
+                               | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24);
+
+                       if (!(down_speed & 0x0000ffff) &&
+                           !(up_speed & 0x0000ffff)) {
+                               down_speed >>= 16;
+                               up_speed >>= 16;
+                       }
+                       instance->u.atm_dev->link_rate = down_speed * 1000 / 424;
+                       instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
+
+                       printk(KERN_NOTICE
+                              "ADSL line is up (%d Kib/s down | %d Kib/s up)\n",
+                              down_speed, up_speed);
+               }
+               break;
+
+       default:
+               if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
+                       instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+                       printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]);
+               }
+               break;
+       }
+}
+
+static void speedtch_timer_poll(unsigned long data)
+{
+       struct speedtch_instance_data *instance = (void *)data;
+
+       schedule_work(&instance->poll_work);
+       mod_timer(&instance->poll_timer, jiffies + (5 * HZ));
+}
+
+#ifdef USE_FW_LOADER
+static void speedtch_upload_firmware(struct speedtch_instance_data *instance,
+                                    const struct firmware *fw1,
+                                    const struct firmware *fw2)
+{
+       unsigned char *buffer;
+       struct usb_device *usb_dev = instance->u.usb_dev;
+       struct usb_interface *intf;
+       int actual_length, ret;
+       int offset;
+
+       dbg("speedtch_upload_firmware");
+
+       if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
+               dbg("speedtch_upload_firmware: interface not found!");
+               goto fail;
+       }
+
+       if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
+               dbg("speedtch_upload_firmware: no memory for buffer!");
+               goto fail;
+       }
+
+       /* A user-space firmware loader may already have claimed interface #2 */
+       if ((ret =
+            usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) {
+               dbg("speedtch_upload_firmware: interface in use (%d)!", ret);
+               goto fail_free;
+       }
+
+       /* URB 7 */
+       if (dl_512_first) {     /* some modems need a read before writing the firmware */
+               ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+                                  buffer, 0x200, &actual_length, 2 * HZ);
+
+               if (ret < 0 && ret != -ETIMEDOUT)
+                       dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret);
+               else
+                       dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret);
+       }
+
+       /* URB 8 : both leds are static green */
+       for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) {
+               int thislen = min_t(int, PAGE_SIZE, fw1->size - offset);
+               memcpy(buffer, fw1->data + offset, thislen);
+
+               ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+                                  buffer, thislen, &actual_length, DATA_TIMEOUT);
+
+               if (ret < 0) {
+                       dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret);
+                       goto fail_release;
+               }
+               dbg("speedtch_upload_firmware: BLOCK1 uploaded (%d bytes)", fw1->size);
+       }
+
+       /* USB led blinking green, ADSL led off */
+
+       /* URB 11 */
+       ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+                          buffer, 0x200, &actual_length, DATA_TIMEOUT);
+
+       if (ret < 0) {
+               dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret);
+               goto fail_release;
+       }
+       dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length);
+
+       /* URBs 12 to 139 - USB led blinking green, ADSL led off */
+       for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) {
+               int thislen = min_t(int, PAGE_SIZE, fw2->size - offset);
+               memcpy(buffer, fw2->data + offset, thislen);
+
+               ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+                                  buffer, thislen, &actual_length, DATA_TIMEOUT);
+
+               if (ret < 0) {
+                       dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret);
+                       goto fail_release;
+               }
+       }
+       dbg("speedtch_upload_firmware: BLOCK3 uploaded (%d bytes)", fw2->size);
+
+       /* USB led static green, ADSL led static red */
+
+       /* URB 142 */
+       ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+                          buffer, 0x200, &actual_length, DATA_TIMEOUT);
+
+       if (ret < 0) {
+               dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret);
+               goto fail_release;
+       }
+
+       /* success */
+       dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length);
+
+       /* Delay to allow firmware to start up. We can do this here
+          because we're in our own kernel thread anyway. */
+       msleep(1000);
+
+       /* Enable software buffering, if requested */
+       if (sw_buffering)
+               speedtch_set_swbuff(instance, 1);
+
+       /* Magic spell; don't ask us what this does */
+       speedtch_test_sequence(instance);
+
+       /* Start modem synchronisation */
+       if (speedtch_start_synchro(instance))
+               dbg("speedtch_start_synchro: failed");
+
+       speedtch_got_firmware(instance, 1);
+
+       free_page((unsigned long)buffer);
+       return;
+
+ fail_release:
+       /* Only release interface #2 if uploading failed; we don't release it
+          we succeeded.  This prevents the userspace tools from trying to load
+          the firmware themselves */
+       usb_driver_release_interface(&speedtch_usb_driver, intf);
+ fail_free:
+       free_page((unsigned long)buffer);
+ fail:
+       speedtch_got_firmware(instance, 0);
+}
+
+static int speedtch_find_firmware(struct speedtch_instance_data
+                                 *instance, int phase,
+                                 const struct firmware **fw_p)
+{
+       char buf[24];
+       const u16 bcdDevice = instance->u.usb_dev->descriptor.bcdDevice;
+       const u8 major_revision = bcdDevice >> 8;
+       const u8 minor_revision = bcdDevice & 0xff;
+
+       sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
+       dbg("speedtch_find_firmware: looking for %s", buf);
+
+       if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+               sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
+               dbg("speedtch_find_firmware: looking for %s", buf);
+
+               if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+                       sprintf(buf, "speedtch-%d.bin", phase);
+                       dbg("speedtch_find_firmware: looking for %s", buf);
+
+                       if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+                               dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase);
+                               return -ENOENT;
+                       }
+               }
+       }
+
+       dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf);
+
+       return 0;
+}
+
+static int speedtch_load_firmware(void *arg)
+{
+       const struct firmware *fw1, *fw2;
+       struct speedtch_instance_data *instance = arg;
+
+       BUG_ON(!instance);
+
+       daemonize("firmware/speedtch");
+
+       if (!speedtch_find_firmware(instance, 1, &fw1)) {
+               if (!speedtch_find_firmware(instance, 2, &fw2)) {
+                       speedtch_upload_firmware(instance, fw1, fw2);
+                       release_firmware(fw2);
+               }
+               release_firmware(fw1);
+       }
+
+       /* In case we failed, set state back to NO_FIRMWARE so that
+          another later attempt may work. Otherwise, we never actually
+          manage to recover if, for example, the firmware is on /usr and
+          we look for it too early. */
+       speedtch_got_firmware(instance, 0);
+
+       module_put(THIS_MODULE);
+       udsl_put_instance(&instance->u);
+       return 0;
+}
+#endif /* USE_FW_LOADER */
+
+static void speedtch_firmware_start(struct speedtch_instance_data *instance)
+{
+#ifdef USE_FW_LOADER
+       int ret;
+#endif
+
+       dbg("speedtch_firmware_start");
+
+       down(&instance->u.serialize);   /* vs self, speedtch_got_firmware */
+
+       if (instance->u.status >= UDSL_LOADING_FIRMWARE) {
+               up(&instance->u.serialize);
+               return;
+       }
+
+       instance->u.status = UDSL_LOADING_FIRMWARE;
+       up(&instance->u.serialize);
+
+#ifdef USE_FW_LOADER
+       udsl_get_instance(&instance->u);
+       try_module_get(THIS_MODULE);
+
+       ret = kernel_thread(speedtch_load_firmware, instance,
+                           CLONE_FS | CLONE_FILES);
+
+       if (ret >= 0)
+               return;         /* OK */
+
+       dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret);
+
+       module_put(THIS_MODULE);
+       udsl_put_instance(&instance->u);
+       /* Just pretend it never happened... hope modem_run happens */
+#endif                         /* USE_FW_LOADER */
+
+       speedtch_got_firmware(instance, 0);
+}
+
+static int speedtch_firmware_wait(struct udsl_instance_data *instance)
+{
+       speedtch_firmware_start((void *)instance);
+
+       if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0)
+               return -ERESTARTSYS;
+
+       return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN;
+}
+
+/**********
+**  USB  **
+**********/
+
+static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
+                             void *user_data)
+{
+       struct speedtch_instance_data *instance = usb_get_intfdata(intf);
+
+       dbg("speedtch_usb_ioctl entered");
+
+       if (!instance) {
+               dbg("speedtch_usb_ioctl: NULL instance!");
+               return -ENODEV;
+       }
+
+       switch (code) {
+       case UDSL_IOCTL_LINE_UP:
+               instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
+               speedtch_got_firmware(instance, 1);
+               return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO;
+       case UDSL_IOCTL_LINE_DOWN:
+               instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+               return 0;
+       default:
+               return -ENOTTY;
+       }
+}
+
+static int speedtch_usb_probe(struct usb_interface *intf,
+                             const struct usb_device_id *id)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+       int ifnum = intf->altsetting->desc.bInterfaceNumber;
+       struct speedtch_instance_data *instance;
+       unsigned char mac_str[13];
+       int ret, i;
+       char buf7[SIZE_7];
+
+       dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
+
+       if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) ||
+           (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) ||
+           (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1))
+               return -ENODEV;
+
+       dbg("speedtch_usb_probe: device accepted");
+
+       /* instance init */
+       instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+       if (!instance) {
+               dbg("speedtch_usb_probe: no memory for instance data!");
+               return -ENOMEM;
+       }
+
+       memset(instance, 0, sizeof(struct speedtch_instance_data));
+
+       if ((ret = usb_set_interface(dev, 0, 0)) < 0)
+               goto fail;
+
+       if ((ret = usb_set_interface(dev, 2, 0)) < 0)
+               goto fail;
+
+       instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA;
+       instance->u.firmware_wait = speedtch_firmware_wait;
+       instance->u.driver_name = speedtch_driver_name;
+
+       ret = udsl_instance_setup(dev, &instance->u);
+       if (ret)
+               goto fail;
+
+       init_timer(&instance->poll_timer);
+       instance->poll_timer.function = speedtch_timer_poll;
+       instance->poll_timer.data = (unsigned long)instance;
+
+       INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance);
+
+       /* set MAC address, it is stored in the serial number */
+       memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi));
+       if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
+               for (i = 0; i < 6; i++)
+                       instance->u.atm_dev->esi[i] =
+                               (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1]));
+       }
+
+       /* First check whether the modem already seems to be alive */
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ / 2);
+
+       if (ret == SIZE_7) {
+               dbg("firmware appears to be already loaded");
+               speedtch_got_firmware(instance, 1);
+               speedtch_poll_status(instance);
+       } else {
+               speedtch_firmware_start(instance);
+       }
+
+       usb_set_intfdata(intf, instance);
+
+       return 0;
+
+ fail:
+       kfree(instance);
+
+       return -ENOMEM;
+}
+
+static void speedtch_usb_disconnect(struct usb_interface *intf)
+{
+       struct speedtch_instance_data *instance = usb_get_intfdata(intf);
+
+       dbg("speedtch_usb_disconnect entered");
+
+       if (!instance) {
+               dbg("speedtch_usb_disconnect: NULL instance!");
+               return;
+       }
+
+       if (instance->int_urb) {
+               struct urb *int_urb = instance->int_urb;
+               instance->int_urb = NULL;
+               wmb();
+               usb_unlink_urb(int_urb);
+               usb_free_urb(int_urb);
+       }
+
+       instance->int_data[0] = 1;
+       del_timer_sync(&instance->poll_timer);
+       wmb();
+       flush_scheduled_work();
+
+       udsl_instance_disconnect(&instance->u);
+
+       /* clean up */
+       usb_set_intfdata(intf, NULL);
+       udsl_put_instance(&instance->u);
+}
+
+/***********
+**  init  **
+***********/
+
+static int __init speedtch_usb_init(void)
+{
+       dbg("speedtch_usb_init: driver version " DRIVER_VERSION);
+
+       return usb_register(&speedtch_usb_driver);
+}
+
+static void __exit speedtch_usb_cleanup(void)
+{
+       dbg("speedtch_usb_cleanup entered");
+
+       usb_deregister(&speedtch_usb_driver);
+}
+
+module_init(speedtch_usb_init);
+module_exit(speedtch_usb_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c
new file mode 100644 (file)
index 0000000..9180dda
--- /dev/null
@@ -0,0 +1,1201 @@
+/******************************************************************************
+ *  usb_atm.c - Generic USB xDSL driver core
+ *
+ *  Copyright (C) 2001, Alcatel
+ *  Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
+ *  Copyright (C) 2004, David Woodhouse
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+/*
+ *  Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr)
+ *
+ *  1.7+:      - See the check-in logs
+ *
+ *  1.6:       - No longer opens a connection if the firmware is not loaded
+ *             - Added support for the speedtouch 330
+ *             - Removed the limit on the number of devices
+ *             - Module now autoloads on device plugin
+ *             - Merged relevant parts of sarlib
+ *             - Replaced the kernel thread with a tasklet
+ *             - New packet transmission code
+ *             - Changed proc file contents
+ *             - Fixed all known SMP races
+ *             - Many fixes and cleanups
+ *             - Various fixes by Oliver Neukum (oliver@neukum.name)
+ *
+ *  1.5A:      - Version for inclusion in 2.5 series kernel
+ *             - Modifications by Richard Purdie (rpurdie@rpsys.net)
+ *             - made compatible with kernel 2.5.6 onwards by changing
+ *             udsl_usb_send_data_context->urb to a pointer and adding code
+ *             to alloc and free it
+ *             - remove_wait_queue() added to udsl_atm_processqueue_thread()
+ *
+ *  1.5:       - fixed memory leak when atmsar_decode_aal5 returned NULL.
+ *             (reported by stephen.robinson@zen.co.uk)
+ *
+ *  1.4:       - changed the spin_lock() under interrupt to spin_lock_irqsave()
+ *             - unlink all active send urbs of a vcc that is being closed.
+ *
+ *  1.3.1:     - added the version number
+ *
+ *  1.3:       - Added multiple send urb support
+ *             - fixed memory leak and vcc->tx_inuse starvation bug
+ *               when not enough memory left in vcc.
+ *
+ *  1.2:       - Fixed race condition in udsl_usb_send_data()
+ *  1.1:       - Turned off packet debugging
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "usb_atm.h"
+
+/*
+#define DEBUG
+#define VERBOSE_DEBUG
+*/
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+#      define DEBUG
+#endif
+
+#include <linux/usb.h>
+
+#ifdef DEBUG
+#define UDSL_ASSERT(x) BUG_ON(!(x))
+#else
+#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0)
+#endif
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len);
+#define PACKETDEBUG(arg...)    udsl_print_packet (arg)
+#define vdbg(arg...)           dbg (arg)
+#else
+#define PACKETDEBUG(arg...)
+#define vdbg(arg...)
+#endif
+
+#define DRIVER_AUTHOR  "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
+#define DRIVER_VERSION "1.8"
+#define DRIVER_DESC    "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
+
+static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
+static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
+static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS;
+static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS;
+static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
+static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
+
+module_param(num_rcv_urbs, uint, 0444);
+MODULE_PARM_DESC(num_rcv_urbs,
+                "Number of urbs used for reception (range: 0-"
+                __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")");
+
+module_param(num_snd_urbs, uint, 0444);
+MODULE_PARM_DESC(num_snd_urbs,
+                "Number of urbs used for transmission (range: 0-"
+                __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")");
+
+module_param(num_rcv_bufs, uint, 0444);
+MODULE_PARM_DESC(num_rcv_bufs,
+                "Number of buffers used for reception (range: 0-"
+                __MODULE_STRING(UDSL_MAX_RCV_BUFS) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_RCV_BUFS) ")");
+
+module_param(num_snd_bufs, uint, 0444);
+MODULE_PARM_DESC(num_snd_bufs,
+                "Number of buffers used for transmission (range: 0-"
+                __MODULE_STRING(UDSL_MAX_SND_BUFS) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_SND_BUFS) ")");
+
+module_param(rcv_buf_size, uint, 0444);
+MODULE_PARM_DESC(rcv_buf_size,
+                "Size of the buffers used for reception (range: 0-"
+                __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")");
+
+module_param(snd_buf_size, uint, 0444);
+MODULE_PARM_DESC(snd_buf_size,
+                "Size of the buffers used for transmission (range: 0-"
+                __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")");
+
+/* ATM */
+
+static void udsl_atm_dev_close(struct atm_dev *dev);
+static int udsl_atm_open(struct atm_vcc *vcc);
+static void udsl_atm_close(struct atm_vcc *vcc);
+static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
+static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb);
+static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page);
+
+static struct atmdev_ops udsl_atm_devops = {
+       .dev_close      = udsl_atm_dev_close,
+       .open           = udsl_atm_open,
+       .close          = udsl_atm_close,
+       .ioctl          = udsl_atm_ioctl,
+       .send           = udsl_atm_send,
+       .proc_read      = udsl_atm_proc_read,
+       .owner          = THIS_MODULE,
+};
+
+/***********
+**  misc  **
+***********/
+
+static inline void udsl_pop(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+       if (vcc->pop)
+               vcc->pop(vcc, skb);
+       else
+               dev_kfree_skb(skb);
+}
+
+/*************
+**  decode  **
+*************/
+
+static inline struct udsl_vcc_data *udsl_find_vcc(struct udsl_instance_data *instance,
+                                                 short vpi, int vci)
+{
+       struct udsl_vcc_data *vcc;
+
+       list_for_each_entry(vcc, &instance->vcc_list, list)
+               if ((vcc->vci == vci) && (vcc->vpi == vpi))
+                       return vcc;
+       return NULL;
+}
+
+static void udsl_extract_cells(struct udsl_instance_data *instance,
+                              unsigned char *source, unsigned int howmany)
+{
+       struct udsl_vcc_data *cached_vcc = NULL;
+       struct atm_vcc *vcc;
+       struct sk_buff *sarb;
+       struct udsl_vcc_data *vcc_data;
+       int cached_vci = 0;
+       unsigned int i;
+       int pti;
+       int vci;
+       short cached_vpi = 0;
+       short vpi;
+
+       for (i = 0; i < howmany;
+            i++, source += ATM_CELL_SIZE + instance->rcv_padding) {
+               vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4);
+               vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
+               pti = (source[3] & 0x2) != 0;
+
+               vdbg("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti);
+
+               if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi))
+                       vcc_data = cached_vcc;
+               else if ((vcc_data = udsl_find_vcc(instance, vpi, vci))) {
+                       cached_vcc = vcc_data;
+                       cached_vpi = vpi;
+                       cached_vci = vci;
+               } else {
+                       dbg("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci);
+                       continue;
+               }
+
+               vcc = vcc_data->vcc;
+               sarb = vcc_data->sarb;
+
+               if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
+                       dbg("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc);
+                       /* discard cells already received */
+                       skb_trim(sarb, 0);
+               }
+
+               memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+               __skb_put(sarb, ATM_CELL_PAYLOAD);
+
+               if (pti) {
+                       struct sk_buff *skb;
+                       unsigned int length;
+                       unsigned int pdu_length;
+
+                       length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5];
+
+                       /* guard against overflow */
+                       if (length > ATM_MAX_AAL5_PDU) {
+                               dbg("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc);
+                               atomic_inc(&vcc->stats->rx_err);
+                               goto out;
+                       }
+
+                       pdu_length = UDSL_NUM_CELLS(length) * ATM_CELL_PAYLOAD;
+
+                       if (sarb->len < pdu_length) {
+                               dbg("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc);
+                               atomic_inc(&vcc->stats->rx_err);
+                               goto out;
+                       }
+
+                       if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
+                               dbg("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc);
+                               atomic_inc(&vcc->stats->rx_err);
+                               goto out;
+                       }
+
+                       vdbg("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc);
+
+                       if (!(skb = dev_alloc_skb(length))) {
+                               dbg("udsl_extract_cells: no memory for skb (length: %u)!", length);
+                               atomic_inc(&vcc->stats->rx_drop);
+                               goto out;
+                       }
+
+                       vdbg("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize);
+
+                       if (!atm_charge(vcc, skb->truesize)) {
+                               dbg("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize);
+                               dev_kfree_skb(skb);
+                               goto out;       /* atm_charge increments rx_drop */
+                       }
+
+                       memcpy(skb->data, sarb->tail - pdu_length, length);
+                       __skb_put(skb, length);
+
+                       vdbg("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize);
+
+                       PACKETDEBUG(skb->data, skb->len);
+
+                       vcc->push(vcc, skb);
+
+                       atomic_inc(&vcc->stats->rx);
+               out:
+                       skb_trim(sarb, 0);
+               }
+       }
+}
+
+/*************
+**  encode  **
+*************/
+
+static const unsigned char zeros[ATM_CELL_PAYLOAD];
+
+static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+       struct udsl_control *ctrl = UDSL_SKB(skb);
+       unsigned int zero_padding;
+       u32 crc;
+
+       ctrl->atm_data.vcc = vcc;
+       ctrl->cell_header[0] = vcc->vpi >> 4;
+       ctrl->cell_header[1] = (vcc->vpi << 4) | (vcc->vci >> 12);
+       ctrl->cell_header[2] = vcc->vci >> 4;
+       ctrl->cell_header[3] = vcc->vci << 4;
+       ctrl->cell_header[4] = 0xec;
+
+       ctrl->num_cells = UDSL_NUM_CELLS(skb->len);
+       ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD;
+
+       zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER;
+
+       if (ctrl->num_entire + 1 < ctrl->num_cells)
+               ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
+       else
+               ctrl->pdu_padding = zero_padding;
+
+       ctrl->aal5_trailer[0] = 0;      /* UU = 0 */
+       ctrl->aal5_trailer[1] = 0;      /* CPI = 0 */
+       ctrl->aal5_trailer[2] = skb->len >> 8;
+       ctrl->aal5_trailer[3] = skb->len;
+
+       crc = crc32_be(~0, skb->data, skb->len);
+       crc = crc32_be(crc, zeros, zero_padding);
+       crc = crc32_be(crc, ctrl->aal5_trailer, 4);
+       crc = ~crc;
+
+       ctrl->aal5_trailer[4] = crc >> 24;
+       ctrl->aal5_trailer[5] = crc >> 16;
+       ctrl->aal5_trailer[6] = crc >> 8;
+       ctrl->aal5_trailer[7] = crc;
+}
+
+static unsigned int udsl_write_cells(struct udsl_instance_data *instance,
+                                    unsigned int howmany, struct sk_buff *skb,
+                                    unsigned char **target_p)
+{
+       struct udsl_control *ctrl = UDSL_SKB(skb);
+       unsigned char *target = *target_p;
+       unsigned int nc, ne, i;
+
+       vdbg("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding);
+
+       nc = ctrl->num_cells;
+       ne = min(howmany, ctrl->num_entire);
+
+       for (i = 0; i < ne; i++) {
+               memcpy(target, ctrl->cell_header, ATM_CELL_HEADER);
+               target += ATM_CELL_HEADER;
+               memcpy(target, skb->data, ATM_CELL_PAYLOAD);
+               target += ATM_CELL_PAYLOAD;
+               if (instance->snd_padding) {
+                       memset(target, 0, instance->snd_padding);
+                       target += instance->snd_padding;
+               }
+               __skb_pull(skb, ATM_CELL_PAYLOAD);
+       }
+
+       ctrl->num_entire -= ne;
+
+       if (!(ctrl->num_cells -= ne) || !(howmany -= ne))
+               goto out;
+
+       if (instance->snd_padding) {
+               memset(target, 0, instance->snd_padding);
+               target += instance->snd_padding;
+       }
+       memcpy(target, ctrl->cell_header, ATM_CELL_HEADER);
+       target += ATM_CELL_HEADER;
+       memcpy(target, skb->data, skb->len);
+       target += skb->len;
+       __skb_pull(skb, skb->len);
+       memset(target, 0, ctrl->pdu_padding);
+       target += ctrl->pdu_padding;
+
+       if (--ctrl->num_cells) {
+               if (!--howmany) {
+                       ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
+                       goto out;
+               }
+
+               memcpy(target, ctrl->cell_header, ATM_CELL_HEADER);
+               target += ATM_CELL_HEADER;
+               memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
+               target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
+
+               --ctrl->num_cells;
+               UDSL_ASSERT(!ctrl->num_cells);
+       }
+
+       memcpy(target, ctrl->aal5_trailer, ATM_AAL5_TRAILER);
+       target += ATM_AAL5_TRAILER;
+       /* set pti bit in last cell */
+       *(target + 3 - ATM_CELL_SIZE) |= 0x2;
+       if (instance->snd_padding) {
+               memset(target, 0, instance->snd_padding);
+               target += instance->snd_padding;
+       }
+ out:
+       *target_p = target;
+       return nc - ctrl->num_cells;
+}
+
+/**************
+**  receive  **
+**************/
+
+static void udsl_complete_receive(struct urb *urb, struct pt_regs *regs)
+{
+       struct udsl_receive_buffer *buf;
+       struct udsl_instance_data *instance;
+       struct udsl_receiver *rcv;
+       unsigned long flags;
+
+       if (!urb || !(rcv = urb->context)) {
+               dbg("udsl_complete_receive: bad urb!");
+               return;
+       }
+
+       instance = rcv->instance;
+       buf = rcv->buffer;
+
+       buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding);
+
+       vdbg("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf);
+
+       UDSL_ASSERT(buf->filled_cells <= rcv_buf_size);
+
+       /* may not be in_interrupt() */
+       spin_lock_irqsave(&instance->receive_lock, flags);
+       list_add(&rcv->list, &instance->spare_receivers);
+       list_add_tail(&buf->list, &instance->filled_receive_buffers);
+       if (likely(!urb->status))
+               tasklet_schedule(&instance->receive_tasklet);
+       spin_unlock_irqrestore(&instance->receive_lock, flags);
+}
+
+static void udsl_process_receive(unsigned long data)
+{
+       struct udsl_receive_buffer *buf;
+       struct udsl_instance_data *instance = (struct udsl_instance_data *)data;
+       struct udsl_receiver *rcv;
+       int err;
+
+ made_progress:
+       while (!list_empty(&instance->spare_receive_buffers)) {
+               spin_lock_irq(&instance->receive_lock);
+               if (list_empty(&instance->spare_receivers)) {
+                       spin_unlock_irq(&instance->receive_lock);
+                       break;
+               }
+               rcv = list_entry(instance->spare_receivers.next,
+                                struct udsl_receiver, list);
+               list_del(&rcv->list);
+               spin_unlock_irq(&instance->receive_lock);
+
+               buf = list_entry(instance->spare_receive_buffers.next,
+                                struct udsl_receive_buffer, list);
+               list_del(&buf->list);
+
+               rcv->buffer = buf;
+
+               usb_fill_bulk_urb(rcv->urb, instance->usb_dev,
+                                 usb_rcvbulkpipe(instance->usb_dev, instance->data_endpoint),
+                                 buf->base,
+                                 rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
+                                 udsl_complete_receive, rcv);
+
+               vdbg("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p",
+                    rcv->urb, rcv, buf);
+
+               if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) {
+                       dbg("udsl_process_receive: urb submission failed (%d)!", err);
+                       list_add(&buf->list, &instance->spare_receive_buffers);
+                       spin_lock_irq(&instance->receive_lock);
+                       list_add(&rcv->list, &instance->spare_receivers);
+                       spin_unlock_irq(&instance->receive_lock);
+                       break;
+               }
+       }
+
+       spin_lock_irq(&instance->receive_lock);
+       if (list_empty(&instance->filled_receive_buffers)) {
+               spin_unlock_irq(&instance->receive_lock);
+               return;         /* done - no more buffers */
+       }
+       buf = list_entry(instance->filled_receive_buffers.next,
+                        struct udsl_receive_buffer, list);
+       list_del(&buf->list);
+       spin_unlock_irq(&instance->receive_lock);
+
+       vdbg("udsl_process_receive: processing buf 0x%p", buf);
+       udsl_extract_cells(instance, buf->base, buf->filled_cells);
+       list_add(&buf->list, &instance->spare_receive_buffers);
+       goto made_progress;
+}
+
+/***********
+**  send  **
+***********/
+
+static void udsl_complete_send(struct urb *urb, struct pt_regs *regs)
+{
+       struct udsl_instance_data *instance;
+       struct udsl_sender *snd;
+       unsigned long flags;
+
+       if (!urb || !(snd = urb->context) || !(instance = snd->instance)) {
+               dbg("udsl_complete_send: bad urb!");
+               return;
+       }
+
+       vdbg("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb,
+            urb->status, snd, snd->buffer);
+
+       /* may not be in_interrupt() */
+       spin_lock_irqsave(&instance->send_lock, flags);
+       list_add(&snd->list, &instance->spare_senders);
+       list_add(&snd->buffer->list, &instance->spare_send_buffers);
+       tasklet_schedule(&instance->send_tasklet);
+       spin_unlock_irqrestore(&instance->send_lock, flags);
+}
+
+static void udsl_process_send(unsigned long data)
+{
+       struct udsl_send_buffer *buf;
+       struct udsl_instance_data *instance = (struct udsl_instance_data *)data;
+       struct sk_buff *skb;
+       struct udsl_sender *snd;
+       int err;
+       unsigned int num_written;
+
+ made_progress:
+       spin_lock_irq(&instance->send_lock);
+       while (!list_empty(&instance->spare_senders)) {
+               if (!list_empty(&instance->filled_send_buffers)) {
+                       buf = list_entry(instance->filled_send_buffers.next,
+                                        struct udsl_send_buffer, list);
+                       list_del(&buf->list);
+               } else if ((buf = instance->current_buffer)) {
+                       instance->current_buffer = NULL;
+               } else          /* all buffers empty */
+                       break;
+
+               snd = list_entry(instance->spare_senders.next,
+                                struct udsl_sender, list);
+               list_del(&snd->list);
+               spin_unlock_irq(&instance->send_lock);
+
+               snd->buffer = buf;
+               usb_fill_bulk_urb(snd->urb, instance->usb_dev,
+                                 usb_sndbulkpipe(instance->usb_dev, instance->data_endpoint),
+                                 buf->base,
+                                 (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding),
+                                 udsl_complete_send, snd);
+
+               vdbg("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p",
+                    snd->urb, snd_buf_size - buf->free_cells, snd, buf);
+
+               if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) {
+                       dbg("udsl_process_send: urb submission failed (%d)!", err);
+                       spin_lock_irq(&instance->send_lock);
+                       list_add(&snd->list, &instance->spare_senders);
+                       spin_unlock_irq(&instance->send_lock);
+                       list_add(&buf->list, &instance->filled_send_buffers);
+                       return; /* bail out */
+               }
+
+               spin_lock_irq(&instance->send_lock);
+       }                       /* while */
+       spin_unlock_irq(&instance->send_lock);
+
+       if (!instance->current_skb)
+               instance->current_skb = skb_dequeue(&instance->sndqueue);
+       if (!instance->current_skb)
+               return;         /* done - no more skbs */
+
+       skb = instance->current_skb;
+
+       if (!(buf = instance->current_buffer)) {
+               spin_lock_irq(&instance->send_lock);
+               if (list_empty(&instance->spare_send_buffers)) {
+                       instance->current_buffer = NULL;
+                       spin_unlock_irq(&instance->send_lock);
+                       return; /* done - no more buffers */
+               }
+               buf = list_entry(instance->spare_send_buffers.next,
+                              struct udsl_send_buffer, list);
+               list_del(&buf->list);
+               spin_unlock_irq(&instance->send_lock);
+
+               buf->free_start = buf->base;
+               buf->free_cells = snd_buf_size;
+
+               instance->current_buffer = buf;
+       }
+
+       num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start);
+
+       vdbg("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p",
+            num_written, skb, buf);
+
+       if (!(buf->free_cells -= num_written)) {
+               list_add_tail(&buf->list, &instance->filled_send_buffers);
+               instance->current_buffer = NULL;
+       }
+
+       vdbg("udsl_process_send: buffer contains %d cells, %d left",
+            snd_buf_size - buf->free_cells, buf->free_cells);
+
+       if (!UDSL_SKB(skb)->num_cells) {
+               struct atm_vcc *vcc = UDSL_SKB(skb)->atm_data.vcc;
+
+               udsl_pop(vcc, skb);
+               instance->current_skb = NULL;
+
+               atomic_inc(&vcc->stats->tx);
+       }
+
+       goto made_progress;
+}
+
+static void udsl_cancel_send(struct udsl_instance_data *instance,
+                            struct atm_vcc *vcc)
+{
+       struct sk_buff *skb, *n;
+
+       dbg("udsl_cancel_send entered");
+       spin_lock_irq(&instance->sndqueue.lock);
+       for (skb = instance->sndqueue.next, n = skb->next;
+            skb != (struct sk_buff *)&instance->sndqueue;
+            skb = n, n = skb->next)
+               if (UDSL_SKB(skb)->atm_data.vcc == vcc) {
+                       dbg("udsl_cancel_send: popping skb 0x%p", skb);
+                       __skb_unlink(skb, &instance->sndqueue);
+                       udsl_pop(vcc, skb);
+               }
+       spin_unlock_irq(&instance->sndqueue.lock);
+
+       tasklet_disable(&instance->send_tasklet);
+       if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm_data.vcc == vcc)) {
+               dbg("udsl_cancel_send: popping current skb (0x%p)", skb);
+               instance->current_skb = NULL;
+               udsl_pop(vcc, skb);
+       }
+       tasklet_enable(&instance->send_tasklet);
+       dbg("udsl_cancel_send done");
+}
+
+static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+       struct udsl_instance_data *instance = vcc->dev->dev_data;
+       int err;
+
+       vdbg("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len);
+
+       if (!instance) {
+               dbg("udsl_atm_send: NULL data!");
+               err = -ENODEV;
+               goto fail;
+       }
+
+       if (vcc->qos.aal != ATM_AAL5) {
+               dbg("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal);
+               err = -EINVAL;
+               goto fail;
+       }
+
+       if (skb->len > ATM_MAX_AAL5_PDU) {
+               dbg("udsl_atm_send: packet too long (%d vs %d)!", skb->len,
+                   ATM_MAX_AAL5_PDU);
+               err = -EINVAL;
+               goto fail;
+       }
+
+       PACKETDEBUG(skb->data, skb->len);
+
+       udsl_groom_skb(vcc, skb);
+       skb_queue_tail(&instance->sndqueue, skb);
+       tasklet_schedule(&instance->send_tasklet);
+
+       return 0;
+
+ fail:
+       udsl_pop(vcc, skb);
+       return err;
+}
+
+/********************
+**  bean counting  **
+********************/
+
+static void udsl_destroy_instance(struct kref *kref)
+{
+       struct udsl_instance_data *instance =
+           container_of(kref, struct udsl_instance_data, refcount);
+
+       tasklet_kill(&instance->receive_tasklet);
+       tasklet_kill(&instance->send_tasklet);
+       usb_put_dev(instance->usb_dev);
+       kfree(instance);
+}
+
+void udsl_get_instance(struct udsl_instance_data *instance)
+{
+       kref_get(&instance->refcount);
+}
+
+void udsl_put_instance(struct udsl_instance_data *instance)
+{
+       kref_put(&instance->refcount, udsl_destroy_instance);
+}
+
+/**********
+**  ATM  **
+**********/
+
+static void udsl_atm_dev_close(struct atm_dev *dev)
+{
+       struct udsl_instance_data *instance = dev->dev_data;
+
+       dev->dev_data = NULL;
+       udsl_put_instance(instance);
+}
+
+static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page)
+{
+       struct udsl_instance_data *instance = atm_dev->dev_data;
+       int left = *pos;
+
+       if (!instance) {
+               dbg("udsl_atm_proc_read: NULL instance!");
+               return -ENODEV;
+       }
+
+       if (!left--)
+               return sprintf(page, "%s\n", instance->description);
+
+       if (!left--)
+               return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+                              atm_dev->esi[0], atm_dev->esi[1],
+                              atm_dev->esi[2], atm_dev->esi[3],
+                              atm_dev->esi[4], atm_dev->esi[5]);
+
+       if (!left--)
+               return sprintf(page,
+                              "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
+                              atomic_read(&atm_dev->stats.aal5.tx),
+                              atomic_read(&atm_dev->stats.aal5.tx_err),
+                              atomic_read(&atm_dev->stats.aal5.rx),
+                              atomic_read(&atm_dev->stats.aal5.rx_err),
+                              atomic_read(&atm_dev->stats.aal5.rx_drop));
+
+       if (!left--) {
+               switch (atm_dev->signal) {
+               case ATM_PHY_SIG_FOUND:
+                       sprintf(page, "Line up");
+                       break;
+               case ATM_PHY_SIG_LOST:
+                       sprintf(page, "Line down");
+                       break;
+               default:
+                       sprintf(page, "Line state unknown");
+                       break;
+               }
+
+               if (instance->usb_dev->state == USB_STATE_NOTATTACHED)
+                       strcat(page, ", disconnected\n");
+               else {
+                       if (instance->status == UDSL_LOADED_FIRMWARE)
+                               strcat(page, ", firmware loaded\n");
+                       else if (instance->status == UDSL_LOADING_FIRMWARE)
+                               strcat(page, ", firmware loading\n");
+                       else
+                               strcat(page, ", no firmware\n");
+               }
+
+               return strlen(page);
+       }
+
+       return 0;
+}
+
+static int udsl_atm_open(struct atm_vcc *vcc)
+{
+       struct udsl_instance_data *instance = vcc->dev->dev_data;
+       struct udsl_vcc_data *new;
+       unsigned int max_pdu;
+       int vci = vcc->vci;
+       short vpi = vcc->vpi;
+       int err;
+
+       dbg("udsl_atm_open: vpi %hd, vci %d", vpi, vci);
+
+       if (!instance) {
+               dbg("udsl_atm_open: NULL data!");
+               return -ENODEV;
+       }
+
+       /* only support AAL5 */
+       if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0)
+           || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
+               dbg("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal);
+               return -EINVAL;
+       }
+
+       if (instance->firmware_wait &&
+           (err = instance->firmware_wait(instance)) < 0) {
+               dbg("udsl_atm_open: firmware not loaded (%d)!", err);
+               return err;
+       }
+
+       down(&instance->serialize);     /* vs self, udsl_atm_close */
+
+       if (udsl_find_vcc(instance, vpi, vci)) {
+               dbg("udsl_atm_open: %hd/%d already in use!", vpi, vci);
+               up(&instance->serialize);
+               return -EADDRINUSE;
+       }
+
+       if (!(new = kmalloc(sizeof(struct udsl_vcc_data), GFP_KERNEL))) {
+               dbg("udsl_atm_open: no memory for vcc_data!");
+               up(&instance->serialize);
+               return -ENOMEM;
+       }
+
+       memset(new, 0, sizeof(struct udsl_vcc_data));
+       new->vcc = vcc;
+       new->vpi = vpi;
+       new->vci = vci;
+
+       /* udsl_extract_cells requires at least one cell */
+       max_pdu = max(1, UDSL_NUM_CELLS(vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD;
+       if (!(new->sarb = alloc_skb(max_pdu, GFP_KERNEL))) {
+               dbg("udsl_atm_open: no memory for SAR buffer!");
+               kfree(new);
+               up(&instance->serialize);
+               return -ENOMEM;
+       }
+
+       vcc->dev_data = new;
+
+       tasklet_disable(&instance->receive_tasklet);
+       list_add(&new->list, &instance->vcc_list);
+       tasklet_enable(&instance->receive_tasklet);
+
+       set_bit(ATM_VF_ADDR, &vcc->flags);
+       set_bit(ATM_VF_PARTIAL, &vcc->flags);
+       set_bit(ATM_VF_READY, &vcc->flags);
+
+       up(&instance->serialize);
+
+       tasklet_schedule(&instance->receive_tasklet);
+
+       dbg("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu);
+
+       return 0;
+}
+
+static void udsl_atm_close(struct atm_vcc *vcc)
+{
+       struct udsl_instance_data *instance = vcc->dev->dev_data;
+       struct udsl_vcc_data *vcc_data = vcc->dev_data;
+
+       dbg("udsl_atm_close called");
+
+       if (!instance || !vcc_data) {
+               dbg("udsl_atm_close: NULL data!");
+               return;
+       }
+
+       dbg("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d",
+           vcc_data, vcc_data->vpi, vcc_data->vci);
+
+       udsl_cancel_send(instance, vcc);
+
+       down(&instance->serialize);     /* vs self, udsl_atm_open */
+
+       tasklet_disable(&instance->receive_tasklet);
+       list_del(&vcc_data->list);
+       tasklet_enable(&instance->receive_tasklet);
+
+       kfree_skb(vcc_data->sarb);
+       vcc_data->sarb = NULL;
+
+       kfree(vcc_data);
+       vcc->dev_data = NULL;
+
+       vcc->vpi = ATM_VPI_UNSPEC;
+       vcc->vci = ATM_VCI_UNSPEC;
+       clear_bit(ATM_VF_READY, &vcc->flags);
+       clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+       clear_bit(ATM_VF_ADDR, &vcc->flags);
+
+       up(&instance->serialize);
+
+       dbg("udsl_atm_close successful");
+}
+
+static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd,
+                         void __user * arg)
+{
+       switch (cmd) {
+       case ATM_QUERYLOOP:
+               return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0;
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+/**********
+**  USB  **
+**********/
+
+int udsl_instance_setup(struct usb_device *dev,
+                       struct udsl_instance_data *instance)
+{
+       char *buf;
+       int i, length;
+
+       kref_init(&instance->refcount); /* one for USB */
+       udsl_get_instance(instance);    /* one for ATM */
+
+       init_MUTEX(&instance->serialize);
+
+       instance->usb_dev = dev;
+
+       INIT_LIST_HEAD(&instance->vcc_list);
+
+       instance->status = UDSL_NO_FIRMWARE;
+       init_waitqueue_head(&instance->firmware_waiters);
+
+       spin_lock_init(&instance->receive_lock);
+       INIT_LIST_HEAD(&instance->spare_receivers);
+       INIT_LIST_HEAD(&instance->filled_receive_buffers);
+
+       tasklet_init(&instance->receive_tasklet, udsl_process_receive, (unsigned long)instance);
+       INIT_LIST_HEAD(&instance->spare_receive_buffers);
+
+       skb_queue_head_init(&instance->sndqueue);
+
+       spin_lock_init(&instance->send_lock);
+       INIT_LIST_HEAD(&instance->spare_senders);
+       INIT_LIST_HEAD(&instance->spare_send_buffers);
+
+       tasklet_init(&instance->send_tasklet, udsl_process_send,
+                    (unsigned long)instance);
+       INIT_LIST_HEAD(&instance->filled_send_buffers);
+
+       /* receive init */
+       for (i = 0; i < num_rcv_urbs; i++) {
+               struct udsl_receiver *rcv = &(instance->receivers[i]);
+
+               if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
+                       dbg("udsl_usb_probe: no memory for receive urb %d!", i);
+                       goto fail;
+               }
+
+               rcv->instance = instance;
+
+               list_add(&rcv->list, &instance->spare_receivers);
+       }
+
+       for (i = 0; i < num_rcv_bufs; i++) {
+               struct udsl_receive_buffer *buf =
+                   &(instance->receive_buffers[i]);
+
+               buf->base = kmalloc(rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
+                                   GFP_KERNEL);
+               if (!buf->base) {
+                       dbg("udsl_usb_probe: no memory for receive buffer %d!", i);
+                       goto fail;
+               }
+
+               list_add(&buf->list, &instance->spare_receive_buffers);
+       }
+
+       /* send init */
+       for (i = 0; i < num_snd_urbs; i++) {
+               struct udsl_sender *snd = &(instance->senders[i]);
+
+               if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
+                       dbg("udsl_usb_probe: no memory for send urb %d!", i);
+                       goto fail;
+               }
+
+               snd->instance = instance;
+
+               list_add(&snd->list, &instance->spare_senders);
+       }
+
+       for (i = 0; i < num_snd_bufs; i++) {
+               struct udsl_send_buffer *buf = &(instance->send_buffers[i]);
+
+               buf->base = kmalloc(snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding),
+                                   GFP_KERNEL);
+               if (!buf->base) {
+                       dbg("udsl_usb_probe: no memory for send buffer %d!", i);
+                       goto fail;
+               }
+
+               list_add(&buf->list, &instance->spare_send_buffers);
+       }
+
+       /* ATM init */
+       instance->atm_dev = atm_dev_register(instance->driver_name,
+                                            &udsl_atm_devops, -1, NULL);
+       if (!instance->atm_dev) {
+               dbg("udsl_usb_probe: failed to register ATM device!");
+               goto fail;
+       }
+
+       instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
+       instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX;
+       instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+
+       /* temp init ATM device, set to 128kbit */
+       instance->atm_dev->link_rate = 128 * 1000 / 424;
+
+       /* device description */
+       buf = instance->description;
+       length = sizeof(instance->description);
+
+       if ((i = usb_string(dev, dev->descriptor.iProduct, buf, length)) < 0)
+               goto finish;
+
+       buf += i;
+       length -= i;
+
+       i = scnprintf(buf, length, " (");
+       buf += i;
+       length -= i;
+
+       if (length <= 0 || (i = usb_make_path(dev, buf, length)) < 0)
+               goto finish;
+
+       buf += i;
+       length -= i;
+
+       snprintf(buf, length, ")");
+
+ finish:
+       /* ready for ATM callbacks */
+       wmb();
+       instance->atm_dev->dev_data = instance;
+
+       usb_get_dev(dev);
+
+       return 0;
+
+ fail:
+       for (i = 0; i < num_snd_bufs; i++)
+               kfree(instance->send_buffers[i].base);
+
+       for (i = 0; i < num_snd_urbs; i++)
+               usb_free_urb(instance->senders[i].urb);
+
+       for (i = 0; i < num_rcv_bufs; i++)
+               kfree(instance->receive_buffers[i].base);
+
+       for (i = 0; i < num_rcv_urbs; i++)
+               usb_free_urb(instance->receivers[i].urb);
+
+       return -ENOMEM;
+}
+
+void udsl_instance_disconnect(struct udsl_instance_data *instance)
+{
+       int i;
+
+       dbg("udsl_instance_disconnect entered");
+
+       if (!instance) {
+               dbg("udsl_instance_disconnect: NULL instance!");
+               return;
+       }
+
+       /* receive finalize */
+       tasklet_disable(&instance->receive_tasklet);
+
+       for (i = 0; i < num_rcv_urbs; i++)
+               usb_kill_urb(instance->receivers[i].urb);
+
+       /* no need to take the spinlock */
+       INIT_LIST_HEAD(&instance->filled_receive_buffers);
+       INIT_LIST_HEAD(&instance->spare_receive_buffers);
+
+       tasklet_enable(&instance->receive_tasklet);
+
+       for (i = 0; i < num_rcv_urbs; i++)
+               usb_free_urb(instance->receivers[i].urb);
+
+       for (i = 0; i < num_rcv_bufs; i++)
+               kfree(instance->receive_buffers[i].base);
+
+       /* send finalize */
+       tasklet_disable(&instance->send_tasklet);
+
+       for (i = 0; i < num_snd_urbs; i++)
+               usb_kill_urb(instance->senders[i].urb);
+
+       /* no need to take the spinlock */
+       INIT_LIST_HEAD(&instance->spare_senders);
+       INIT_LIST_HEAD(&instance->spare_send_buffers);
+       instance->current_buffer = NULL;
+
+       tasklet_enable(&instance->send_tasklet);
+
+       for (i = 0; i < num_snd_urbs; i++)
+               usb_free_urb(instance->senders[i].urb);
+
+       for (i = 0; i < num_snd_bufs; i++)
+               kfree(instance->send_buffers[i].base);
+
+       /* ATM finalize */
+       shutdown_atm_dev(instance->atm_dev);
+}
+
+EXPORT_SYMBOL_GPL(udsl_get_instance);
+EXPORT_SYMBOL_GPL(udsl_put_instance);
+EXPORT_SYMBOL_GPL(udsl_instance_setup);
+EXPORT_SYMBOL_GPL(udsl_instance_disconnect);
+
+/***********
+**  init  **
+***********/
+
+static int __init udsl_usb_init(void)
+{
+       dbg("udsl_usb_init: driver version " DRIVER_VERSION);
+
+       if (sizeof(struct udsl_control) > sizeof(((struct sk_buff *) 0)->cb)) {
+               printk(KERN_ERR __FILE__ ": unusable with this kernel!\n");
+               return -EIO;
+       }
+
+       if ((num_rcv_urbs > UDSL_MAX_RCV_URBS)
+           || (num_snd_urbs > UDSL_MAX_SND_URBS)
+           || (num_rcv_bufs > UDSL_MAX_RCV_BUFS)
+           || (num_snd_bufs > UDSL_MAX_SND_BUFS)
+           || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE)
+           || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
+               return -EINVAL;
+
+       return 0;
+}
+
+static void __exit udsl_usb_exit(void)
+{
+}
+
+module_init(udsl_usb_init);
+module_exit(udsl_usb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+/************
+**  debug  **
+************/
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len)
+{
+       unsigned char buffer[256];
+       int i = 0, j = 0;
+
+       for (i = 0; i < len;) {
+               buffer[0] = '\0';
+               sprintf(buffer, "%.3d :", i);
+               for (j = 0; (j < 16) && (i < len); j++, i++) {
+                       sprintf(buffer, "%s %2.2x", buffer, data[i]);
+               }
+               dbg("%s", buffer);
+       }
+       return i;
+}
+#endif
diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h
new file mode 100644 (file)
index 0000000..188e917
--- /dev/null
@@ -0,0 +1,160 @@
+/******************************************************************************
+ *  usb_atm.h - Generic USB xDSL driver core
+ *
+ *  Copyright (C) 2001, Alcatel
+ *  Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
+ *  Copyright (C) 2004, David Woodhouse
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the Free
+ *  Software Foundation; either version 2 of the License, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ *  more details.
+ *
+ *  You should have received a copy of the GNU General Public License along with
+ *  this program; if not, write to the Free Software Foundation, Inc., 59
+ *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ ******************************************************************************/
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/kref.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <asm/semaphore.h>
+
+#define UDSL_MAX_RCV_URBS              4
+#define UDSL_MAX_SND_URBS              4
+#define UDSL_MAX_RCV_BUFS              8
+#define UDSL_MAX_SND_BUFS              8
+#define UDSL_MAX_RCV_BUF_SIZE          1024    /* ATM cells */
+#define UDSL_MAX_SND_BUF_SIZE          1024    /* ATM cells */
+#define UDSL_DEFAULT_RCV_URBS          2
+#define UDSL_DEFAULT_SND_URBS          2
+#define UDSL_DEFAULT_RCV_BUFS          4
+#define UDSL_DEFAULT_SND_BUFS          4
+#define UDSL_DEFAULT_RCV_BUF_SIZE      64      /* ATM cells */
+#define UDSL_DEFAULT_SND_BUF_SIZE      64      /* ATM cells */
+
+#define ATM_CELL_HEADER                        (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
+#define UDSL_NUM_CELLS(x)              (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
+
+/* receive */
+
+struct udsl_receive_buffer {
+       struct list_head list;
+       unsigned char *base;
+       unsigned int filled_cells;
+};
+
+struct udsl_receiver {
+       struct list_head list;
+       struct udsl_receive_buffer *buffer;
+       struct urb *urb;
+       struct udsl_instance_data *instance;
+};
+
+struct udsl_vcc_data {
+       /* vpi/vci lookup */
+       struct list_head list;
+       short vpi;
+       int vci;
+       struct atm_vcc *vcc;
+
+       /* raw cell reassembly */
+       struct sk_buff *sarb;
+};
+
+/* send */
+
+struct udsl_send_buffer {
+       struct list_head list;
+       unsigned char *base;
+       unsigned char *free_start;
+       unsigned int free_cells;
+};
+
+struct udsl_sender {
+       struct list_head list;
+       struct udsl_send_buffer *buffer;
+       struct urb *urb;
+       struct udsl_instance_data *instance;
+};
+
+struct udsl_control {
+       struct atm_skb_data atm_data;
+       unsigned int num_cells;
+       unsigned int num_entire;
+       unsigned int pdu_padding;
+       unsigned char cell_header[ATM_CELL_HEADER];
+       unsigned char aal5_trailer[ATM_AAL5_TRAILER];
+};
+
+#define UDSL_SKB(x)            ((struct udsl_control *)(x)->cb)
+
+/* main driver data */
+
+enum udsl_status {
+       UDSL_NO_FIRMWARE,
+       UDSL_LOADING_FIRMWARE,
+       UDSL_LOADED_FIRMWARE
+};
+
+struct udsl_instance_data {
+       struct kref refcount;
+       struct semaphore serialize;
+
+       /* USB device part */
+       struct usb_device *usb_dev;
+       char description[64];
+       int data_endpoint;
+       int snd_padding;
+       int rcv_padding;
+       const char *driver_name;
+
+       /* ATM device part */
+       struct atm_dev *atm_dev;
+       struct list_head vcc_list;
+
+       /* firmware */
+       int (*firmware_wait) (struct udsl_instance_data *);
+       enum udsl_status status;
+       wait_queue_head_t firmware_waiters;
+
+       /* receive */
+       struct udsl_receiver receivers[UDSL_MAX_RCV_URBS];
+       struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS];
+
+       spinlock_t receive_lock;
+       struct list_head spare_receivers;
+       struct list_head filled_receive_buffers;
+
+       struct tasklet_struct receive_tasklet;
+       struct list_head spare_receive_buffers;
+
+       /* send */
+       struct udsl_sender senders[UDSL_MAX_SND_URBS];
+       struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS];
+
+       struct sk_buff_head sndqueue;
+
+       spinlock_t send_lock;
+       struct list_head spare_senders;
+       struct list_head spare_send_buffers;
+
+       struct tasklet_struct send_tasklet;
+       struct sk_buff *current_skb;                    /* being emptied */
+       struct udsl_send_buffer *current_buffer;        /* being filled */
+       struct list_head filled_send_buffers;
+};
+
+extern int udsl_instance_setup(struct usb_device *dev,
+                              struct udsl_instance_data *instance);
+extern void udsl_instance_disconnect(struct udsl_instance_data *instance);
+extern void udsl_get_instance(struct udsl_instance_data *instance);
+extern void udsl_put_instance(struct udsl_instance_data *instance);
diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c
new file mode 100644 (file)
index 0000000..770c870
--- /dev/null
@@ -0,0 +1,4574 @@
+/*
+ * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD)
+ *
+ * Copyright (c) 2002, 2003 Axis Communications AB.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/arch/svinto.h>
+
+#include <linux/usb.h>
+/* Ugly include because we don't live with the other host drivers. */
+#include <../drivers/usb/core/hcd.h>
+#include <../drivers/usb/core/usb.h>
+
+#include "hc_crisv10.h"
+
+#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR
+#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
+#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
+
+static const char *usb_hcd_version = "$Revision: 1.2 $";
+
+#undef KERN_DEBUG
+#define KERN_DEBUG ""
+
+
+#undef USB_DEBUG_RH
+#undef USB_DEBUG_EPID
+#undef USB_DEBUG_SB
+#undef USB_DEBUG_DESC
+#undef USB_DEBUG_URB
+#undef USB_DEBUG_TRACE
+#undef USB_DEBUG_BULK
+#undef USB_DEBUG_CTRL
+#undef USB_DEBUG_INTR
+#undef USB_DEBUG_ISOC
+
+#ifdef USB_DEBUG_RH
+#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg)
+#else
+#define dbg_rh(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_EPID
+#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg)
+#else
+#define dbg_epid(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_SB
+#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg)
+#else
+#define dbg_sb(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_CTRL
+#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg)
+#else
+#define dbg_ctrl(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_BULK
+#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg)
+#else
+#define dbg_bulk(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_INTR
+#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg)
+#else
+#define dbg_intr(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_ISOC
+#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg)
+#else
+#define dbg_isoc(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_TRACE
+#define DBFENTER (printk(": Entering: %s\n", __FUNCTION__))
+#define DBFEXIT  (printk(": Exiting:  %s\n", __FUNCTION__))
+#else
+#define DBFENTER do {} while (0)
+#define DBFEXIT  do {} while (0)
+#endif
+
+#define usb_pipeslow(pipe)     (((pipe) >> 26) & 1)
+
+/*-------------------------------------------------------------------
+ Virtual Root Hub
+ -------------------------------------------------------------------*/
+
+static __u8 root_hub_dev_des[] =
+{
+       0x12,  /*  __u8  bLength; */
+       0x01,  /*  __u8  bDescriptorType; Device */
+       0x00,  /*  __u16 bcdUSB; v1.0 */
+       0x01,
+       0x09,  /*  __u8  bDeviceClass; HUB_CLASSCODE */
+       0x00,  /*  __u8  bDeviceSubClass; */
+       0x00,  /*  __u8  bDeviceProtocol; */
+       0x08,  /*  __u8  bMaxPacketSize0; 8 Bytes */
+       0x00,  /*  __u16 idVendor; */
+       0x00,
+       0x00,  /*  __u16 idProduct; */
+       0x00,
+       0x00,  /*  __u16 bcdDevice; */
+       0x00,
+       0x00,  /*  __u8  iManufacturer; */
+       0x02,  /*  __u8  iProduct; */
+       0x01,  /*  __u8  iSerialNumber; */
+       0x01   /*  __u8  bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+       0x09,  /*  __u8  bLength; */
+       0x02,  /*  __u8  bDescriptorType; Configuration */
+       0x19,  /*  __u16 wTotalLength; */
+       0x00,
+       0x01,  /*  __u8  bNumInterfaces; */
+       0x01,  /*  __u8  bConfigurationValue; */
+       0x00,  /*  __u8  iConfiguration; */
+       0x40,  /*  __u8  bmAttributes; Bit 7: Bus-powered */
+       0x00,  /*  __u8  MaxPower; */
+
+     /* interface */
+       0x09,  /*  __u8  if_bLength; */
+       0x04,  /*  __u8  if_bDescriptorType; Interface */
+       0x00,  /*  __u8  if_bInterfaceNumber; */
+       0x00,  /*  __u8  if_bAlternateSetting; */
+       0x01,  /*  __u8  if_bNumEndpoints; */
+       0x09,  /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+       0x00,  /*  __u8  if_bInterfaceSubClass; */
+       0x00,  /*  __u8  if_bInterfaceProtocol; */
+       0x00,  /*  __u8  if_iInterface; */
+
+     /* endpoint */
+       0x07,  /*  __u8  ep_bLength; */
+       0x05,  /*  __u8  ep_bDescriptorType; Endpoint */
+       0x81,  /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+       0x03,  /*  __u8  ep_bmAttributes; Interrupt */
+       0x08,  /*  __u16 ep_wMaxPacketSize; 8 Bytes */
+       0x00,
+       0xff   /*  __u8  ep_bInterval; 255 ms */
+};
+
+static __u8 root_hub_hub_des[] =
+{
+       0x09,  /*  __u8  bLength; */
+       0x29,  /*  __u8  bDescriptorType; Hub-descriptor */
+       0x02,  /*  __u8  bNbrPorts; */
+       0x00,  /* __u16  wHubCharacteristics; */
+       0x00,
+       0x01,  /*  __u8  bPwrOn2pwrGood; 2ms */
+       0x00,  /*  __u8  bHubContrCurrent; 0 mA */
+       0x00,  /*  __u8  DeviceRemovable; *** 7 Ports max *** */
+       0xff   /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+static struct timer_list bulk_start_timer = TIMER_INITIALIZER(NULL, 0, 0);
+static struct timer_list bulk_eot_timer = TIMER_INITIALIZER(NULL, 0, 0);
+
+/* We want the start timer to expire before the eot timer, because the former might start
+   traffic, thus making it unnecessary for the latter to time out. */
+#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */
+#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */
+
+#define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break
+#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
+{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
+
+#define SLAB_FLAG     (in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG  (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
+
+/* Most helpful debugging aid */
+#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
+
+/* Alternative assert define which stops after a failed assert. */
+/*
+#define assert(expr)                                      \
+{                                                         \
+        if (!(expr)) {                                    \
+                err("assert failed at line %d",__LINE__); \
+                while (1);                                \
+        }                                                 \
+}
+*/
+
+
+/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically?
+   To adjust it dynamically we would have to get an interrupt when we reach the end
+   of the rx descriptor list, or when we get close to the end, and then allocate more
+   descriptors. */
+
+#define NBR_OF_RX_DESC     512
+#define RX_DESC_BUF_SIZE   1024
+#define RX_BUF_SIZE        (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE)
+
+/* The number of epids is, among other things, used for pre-allocating
+   ctrl, bulk and isoc EP descriptors (one for each epid).
+   Assumed to be > 1 when initiating the DMA lists. */
+#define NBR_OF_EPIDS       32
+
+/* Support interrupt traffic intervals up to 128 ms. */
+#define MAX_INTR_INTERVAL 128
+
+/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table
+   must be "invalid". By this we mean that we shouldn't care about epid attentions
+   for this epid, or at least handle them differently from epid attentions for "valid"
+   epids. This define determines which one to use (don't change it). */
+#define INVALID_EPID     31
+/* A special epid for the bulk dummys. */
+#define DUMMY_EPID       30
+
+/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */
+static __u32 epid_usage_bitmask;
+
+/* A bitfield to keep information on in/out traffic is needed to uniquely identify
+   an endpoint on a device, since the most significant bit which indicates traffic
+   direction is lacking in the ep_id field (ETRAX epids can handle both in and
+   out traffic on endpoints that are otherwise identical). The USB framework, however,
+   relies on them to be handled separately.  For example, bulk IN and OUT urbs cannot
+   be queued in the same list, since they would block each other. */
+static __u32 epid_out_traffic;
+
+/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line.
+   Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */
+static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32)));
+static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
+
+/* Pointers into RxDescList. */
+static volatile USB_IN_Desc_t *myNextRxDesc;
+static volatile USB_IN_Desc_t *myLastRxDesc;
+static volatile USB_IN_Desc_t *myPrevRxDesc;
+
+/* EP descriptors must be 32-bit aligned. */
+static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set,
+   causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which
+   gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the
+   EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors
+   in each frame. */
+static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4)));
+
+static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4)));
+
+static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
+static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
+
+/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting
+   this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0
+   results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point
+   it to this buffer. */
+static int zout_buffer[4] __attribute__ ((aligned (4)));
+
+/* Cache for allocating new EP and SB descriptors. */
+static kmem_cache_t *usb_desc_cache;
+
+/* Cache for the registers allocated in the top half. */
+static kmem_cache_t *top_half_reg_cache;
+
+/* Cache for the data allocated in the isoc descr top half. */
+static kmem_cache_t *isoc_compl_cache;
+
+static struct usb_bus *etrax_usb_bus;
+
+/* This is a circular (double-linked) list of the active urbs for each epid.
+   The head is never removed, and new urbs are linked onto the list as
+   urb_entry_t elements. Don't reference urb_list directly; use the wrapper
+   functions instead. Note that working with these lists might require spinlock
+   protection. */
+static struct list_head urb_list[NBR_OF_EPIDS];
+
+/* Read about the need and usage of this lock in submit_ctrl_urb. */
+static spinlock_t urb_list_lock;
+
+/* Used when unlinking asynchronously. */
+static struct list_head urb_unlink_list;
+
+/* for returning string descriptors in UTF-16LE */
+static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
+{
+       int retval;
+
+       for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
+               *utf++ = *ascii++ & 0x7f;
+               *utf++ = 0;
+       }
+       return retval;
+}
+
+static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
+{
+       char buf [30];
+
+       // assert (len > (2 * (sizeof (buf) + 1)));
+       // assert (strlen (type) <= 8);
+
+       // language ids
+       if (id == 0) {
+               *data++ = 4; *data++ = 3;       /* 4 bytes data */
+               *data++ = 0; *data++ = 0;       /* some language id */
+               return 4;
+
+       // serial number
+       } else if (id == 1) {
+               sprintf (buf, "%x", serial);
+
+       // product description
+       } else if (id == 2) {
+               sprintf (buf, "USB %s Root Hub", type);
+
+       // id 3 == vendor description
+
+       // unsupported IDs --> "stall"
+       } else
+           return 0;
+
+       data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
+       data [1] = 3;
+       return data [0];
+}
+
+/* Wrappers around the list functions (include/linux/list.h). */
+
+static inline int urb_list_empty(int epid)
+{
+       return list_empty(&urb_list[epid]);
+}
+
+/* Returns first urb for this epid, or NULL if list is empty. */
+static inline struct urb *urb_list_first(int epid)
+{
+       struct urb *first_urb = 0;
+
+       if (!urb_list_empty(epid)) {
+               /* Get the first urb (i.e. head->next). */
+               urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list);
+               first_urb = urb_entry->urb;
+       }
+       return first_urb;
+}
+
+/* Adds an urb_entry last in the list for this epid. */
+static inline void urb_list_add(struct urb *urb, int epid)
+{
+       urb_entry_t *urb_entry = (urb_entry_t *)kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
+       assert(urb_entry);
+
+       urb_entry->urb = urb;
+       list_add_tail(&urb_entry->list, &urb_list[epid]);
+}
+
+/* Search through the list for an element that contains this urb. (The list
+   is expected to be short and the one we are about to delete will often be
+   the first in the list.) */
+static inline urb_entry_t *__urb_list_entry(struct urb *urb, int epid)
+{
+       struct list_head *entry;
+       struct list_head *tmp;
+       urb_entry_t *urb_entry;
+
+       list_for_each_safe(entry, tmp, &urb_list[epid]) {
+               urb_entry = list_entry(entry, urb_entry_t, list);
+               assert(urb_entry);
+               assert(urb_entry->urb);
+
+               if (urb_entry->urb == urb) {
+                       return urb_entry;
+               }
+       }
+       return 0;
+}
+
+/* Delete an urb from the list. */
+static inline void urb_list_del(struct urb *urb, int epid)
+{
+       urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+       assert(urb_entry);
+
+       /* Delete entry and free. */
+       list_del(&urb_entry->list);
+       kfree(urb_entry);
+}
+
+/* Move an urb to the end of the list. */
+static inline void urb_list_move_last(struct urb *urb, int epid)
+{
+       urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+       assert(urb_entry);
+
+       list_del(&urb_entry->list);
+       list_add_tail(&urb_entry->list, &urb_list[epid]);
+}
+
+/* Get the next urb in the list. */
+static inline struct urb *urb_list_next(struct urb *urb, int epid)
+{
+       urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+
+       assert(urb_entry);
+
+       if (urb_entry->list.next != &urb_list[epid]) {
+               struct list_head *elem = urb_entry->list.next;
+               urb_entry = list_entry(elem, urb_entry_t, list);
+               return urb_entry->urb;
+       } else {
+               return NULL;
+       }
+}
+
+
+
+/* For debug purposes only. */
+static inline void urb_list_dump(int epid)
+{
+       struct list_head *entry;
+       struct list_head *tmp;
+       urb_entry_t *urb_entry;
+       int i = 0;
+
+       info("Dumping urb list for epid %d", epid);
+
+       list_for_each_safe(entry, tmp, &urb_list[epid]) {
+               urb_entry = list_entry(entry, urb_entry_t, list);
+               info("   entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb);
+       }
+}
+
+static void init_rx_buffers(void);
+static int etrax_rh_unlink_urb(struct urb *urb);
+static void etrax_rh_send_irq(struct urb *urb);
+static void etrax_rh_init_int_timer(struct urb *urb);
+static void etrax_rh_int_timer_do(unsigned long ptr);
+
+static int etrax_usb_setup_epid(struct urb *urb);
+static int etrax_usb_lookup_epid(struct urb *urb);
+static int etrax_usb_allocate_epid(void);
+static void etrax_usb_free_epid(int epid);
+
+static int etrax_remove_from_sb_list(struct urb *urb);
+
+static void* etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, int mem_flags, dma_addr_t *dma);
+static void etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma);
+
+static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid);
+static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid);
+static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid);
+static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid);
+
+static int etrax_usb_submit_bulk_urb(struct urb *urb);
+static int etrax_usb_submit_ctrl_urb(struct urb *urb);
+static int etrax_usb_submit_intr_urb(struct urb *urb);
+static int etrax_usb_submit_isoc_urb(struct urb *urb);
+
+static int etrax_usb_submit_urb(struct urb *urb, int mem_flags);
+static int etrax_usb_unlink_urb(struct urb *urb, int status);
+static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
+static int etrax_usb_allocate_dev(struct usb_device *usb_dev);
+static int etrax_usb_deallocate_dev(struct usb_device *usb_dev);
+
+static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs);
+static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs);
+static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs);
+static void etrax_usb_hc_interrupt_bottom_half(void *data);
+
+static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data);
+
+
+/* The following is a list of interrupt handlers for the host controller interrupts we use.
+   They are called from etrax_usb_hc_interrupt_bottom_half. */
+static void etrax_usb_hc_isoc_eof_interrupt(void);
+static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced);
+static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg);
+static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg);
+static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg);
+
+static int etrax_rh_submit_urb (struct urb *urb);
+
+/* Forward declaration needed because they are used in the rx interrupt routine. */
+static void etrax_usb_complete_urb(struct urb *urb, int status);
+static void etrax_usb_complete_bulk_urb(struct urb *urb, int status);
+static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status);
+static void etrax_usb_complete_intr_urb(struct urb *urb, int status);
+static void etrax_usb_complete_isoc_urb(struct urb *urb, int status);
+
+static int etrax_usb_hc_init(void);
+static void etrax_usb_hc_cleanup(void);
+
+static struct usb_operations etrax_usb_device_operations =
+{
+       .allocate = etrax_usb_allocate_dev,
+       .deallocate = etrax_usb_deallocate_dev,
+       .get_frame_number = etrax_usb_get_frame_number,
+       .submit_urb = etrax_usb_submit_urb,
+       .unlink_urb = etrax_usb_unlink_urb,
+        .buffer_alloc = etrax_usb_buffer_alloc,
+        .buffer_free = etrax_usb_buffer_free
+};
+
+/* Note that these functions are always available in their "__" variants, for use in
+   error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/
+   USB_DEBUG_URB macros. */
+static void __dump_urb(struct urb* purb)
+{
+       printk("\nurb                  :0x%08lx\n", (unsigned long)purb);
+       printk("dev                   :0x%08lx\n", (unsigned long)purb->dev);
+       printk("pipe                  :0x%08x\n", purb->pipe);
+       printk("status                :%d\n", purb->status);
+       printk("transfer_flags        :0x%08x\n", purb->transfer_flags);
+       printk("transfer_buffer       :0x%08lx\n", (unsigned long)purb->transfer_buffer);
+       printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
+       printk("actual_length         :%d\n", purb->actual_length);
+       printk("setup_packet          :0x%08lx\n", (unsigned long)purb->setup_packet);
+       printk("start_frame           :%d\n", purb->start_frame);
+       printk("number_of_packets     :%d\n", purb->number_of_packets);
+       printk("interval              :%d\n", purb->interval);
+       printk("error_count           :%d\n", purb->error_count);
+       printk("context               :0x%08lx\n", (unsigned long)purb->context);
+       printk("complete              :0x%08lx\n\n", (unsigned long)purb->complete);
+}
+
+static void __dump_in_desc(volatile USB_IN_Desc_t *in)
+{
+       printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in);
+       printk("  sw_len  : 0x%04x (%d)\n", in->sw_len, in->sw_len);
+       printk("  command : 0x%04x\n", in->command);
+       printk("  next    : 0x%08lx\n", in->next);
+       printk("  buf     : 0x%08lx\n", in->buf);
+       printk("  hw_len  : 0x%04x (%d)\n", in->hw_len, in->hw_len);
+       printk("  status  : 0x%04x\n\n", in->status);
+}
+
+static void __dump_sb_desc(volatile USB_SB_Desc_t *sb)
+{
+       char tt = (sb->command & 0x30) >> 4;
+       char *tt_string;
+
+       switch (tt) {
+       case 0:
+               tt_string = "zout";
+               break;
+       case 1:
+               tt_string = "in";
+               break;
+       case 2:
+               tt_string = "out";
+               break;
+       case 3:
+               tt_string = "setup";
+               break;
+       default:
+               tt_string = "unknown (weird)";
+       }
+
+       printk("\n   USB_SB_Desc at 0x%08lx\n", (unsigned long)sb);
+       printk("     command : 0x%04x\n", sb->command);
+       printk("        rem     : %d\n", (sb->command & 0x3f00) >> 8);
+       printk("        full    : %d\n", (sb->command & 0x40) >> 6);
+       printk("        tt      : %d (%s)\n", tt, tt_string);
+       printk("        intr    : %d\n", (sb->command & 0x8) >> 3);
+       printk("        eot     : %d\n", (sb->command & 0x2) >> 1);
+       printk("        eol     : %d\n", sb->command & 0x1);
+       printk("     sw_len  : 0x%04x (%d)\n", sb->sw_len, sb->sw_len);
+       printk("     next    : 0x%08lx\n", sb->next);
+       printk("     buf     : 0x%08lx\n\n", sb->buf);
+}
+
+
+static void __dump_ep_desc(volatile USB_EP_Desc_t *ep)
+{
+       printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep);
+       printk("  command : 0x%04x\n", ep->command);
+       printk("     ep_id   : %d\n", (ep->command & 0x1f00) >> 8);
+       printk("     enable  : %d\n", (ep->command & 0x10) >> 4);
+       printk("     intr    : %d\n", (ep->command & 0x8) >> 3);
+       printk("     eof     : %d\n", (ep->command & 0x2) >> 1);
+       printk("     eol     : %d\n", ep->command & 0x1);
+       printk("  hw_len  : 0x%04x (%d)\n", ep->hw_len, ep->hw_len);
+       printk("  next    : 0x%08lx\n", ep->next);
+       printk("  sub     : 0x%08lx\n\n", ep->sub);
+}
+
+static inline void __dump_ep_list(int pipe_type)
+{
+       volatile USB_EP_Desc_t *ep;
+       volatile USB_EP_Desc_t *first_ep;
+       volatile USB_SB_Desc_t *sb;
+
+       switch (pipe_type)
+       {
+       case PIPE_BULK:
+               first_ep = &TxBulkEPList[0];
+               break;
+       case PIPE_CONTROL:
+               first_ep = &TxCtrlEPList[0];
+               break;
+       case PIPE_INTERRUPT:
+               first_ep = &TxIntrEPList[0];
+               break;
+       case PIPE_ISOCHRONOUS:
+               first_ep = &TxIsocEPList[0];
+               break;
+       default:
+               warn("Cannot dump unknown traffic type");
+               return;
+       }
+       ep = first_ep;
+
+       printk("\n\nDumping EP list...\n\n");
+
+       do {
+               __dump_ep_desc(ep);
+               /* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */
+               sb = ep->sub ? phys_to_virt(ep->sub) : 0;
+               while (sb) {
+                       __dump_sb_desc(sb);
+                       sb = sb->next ? phys_to_virt(sb->next) : 0;
+               }
+               ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next));
+
+       } while (ep != first_ep);
+}
+
+static inline void __dump_ept_data(int epid)
+{
+       unsigned long flags;
+       __u32 r_usb_ept_data;
+
+       if (epid < 0 || epid > 31) {
+               printk("Cannot dump ept data for invalid epid %d\n", epid);
+               return;
+       }
+
+       save_flags(flags);
+       cli();
+       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+       nop();
+       r_usb_ept_data = *R_USB_EPT_DATA;
+       restore_flags(flags);
+
+       printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid);
+       if (r_usb_ept_data == 0) {
+               /* No need for more detailed printing. */
+               return;
+       }
+       printk("  valid           : %d\n", (r_usb_ept_data & 0x80000000) >> 31);
+       printk("  hold            : %d\n", (r_usb_ept_data & 0x40000000) >> 30);
+       printk("  error_count_in  : %d\n", (r_usb_ept_data & 0x30000000) >> 28);
+       printk("  t_in            : %d\n", (r_usb_ept_data & 0x08000000) >> 27);
+       printk("  low_speed       : %d\n", (r_usb_ept_data & 0x04000000) >> 26);
+       printk("  port            : %d\n", (r_usb_ept_data & 0x03000000) >> 24);
+       printk("  error_code      : %d\n", (r_usb_ept_data & 0x00c00000) >> 22);
+       printk("  t_out           : %d\n", (r_usb_ept_data & 0x00200000) >> 21);
+       printk("  error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19);
+       printk("  max_len         : %d\n", (r_usb_ept_data & 0x0003f800) >> 11);
+       printk("  ep              : %d\n", (r_usb_ept_data & 0x00000780) >> 7);
+       printk("  dev             : %d\n", (r_usb_ept_data & 0x0000003f));
+}
+
+static inline void __dump_ept_data_list(void)
+{
+       int i;
+
+       printk("Dumping the whole R_USB_EPT_DATA list\n");
+
+       for (i = 0; i < 32; i++) {
+               __dump_ept_data(i);
+       }
+}
+#ifdef USB_DEBUG_DESC
+#define dump_in_desc(...) __dump_in_desc(...)
+#define dump_sb_desc(...) __dump_sb_desc(...)
+#define dump_ep_desc(...) __dump_ep_desc(...)
+#else
+#define dump_in_desc(...) do {} while (0)
+#define dump_sb_desc(...) do {} while (0)
+#define dump_ep_desc(...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_URB
+#define dump_urb(x)     __dump_urb(x)
+#else
+#define dump_urb(x)     do {} while (0)
+#endif
+
+static void init_rx_buffers(void)
+{
+       int i;
+
+       DBFENTER;
+
+       for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
+               RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+               RxDescList[i].command = 0;
+               RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]);
+               RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
+               RxDescList[i].hw_len = 0;
+               RxDescList[i].status = 0;
+
+               /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc
+                  for the relevant fields.) */
+               prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]);
+
+       }
+
+       RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+       RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes);
+       RxDescList[i].next = virt_to_phys(&RxDescList[0]);
+       RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
+       RxDescList[i].hw_len = 0;
+       RxDescList[i].status = 0;
+
+       myNextRxDesc = &RxDescList[0];
+       myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+       myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+
+       *R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc);
+       *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start);
+
+       DBFEXIT;
+}
+
+static void init_tx_bulk_ep(void)
+{
+       int i;
+
+       DBFENTER;
+
+       for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+               CHECK_ALIGN(&TxBulkEPList[i]);
+               TxBulkEPList[i].hw_len = 0;
+               TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+               TxBulkEPList[i].sub = 0;
+               TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]);
+
+               /* Initiate two EPs, disabled and with the eol flag set. No need for any
+                  preserved epid. */
+
+               /* The first one has the intr flag set so we get an interrupt when the DMA
+                  channel is about to become disabled. */
+               CHECK_ALIGN(&TxBulkDummyEPList[i][0]);
+               TxBulkDummyEPList[i][0].hw_len = 0;
+               TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
+                                                  IO_STATE(USB_EP_command, eol, yes) |
+                                                  IO_STATE(USB_EP_command, intr, yes));
+               TxBulkDummyEPList[i][0].sub = 0;
+               TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]);
+
+               /* The second one. */
+               CHECK_ALIGN(&TxBulkDummyEPList[i][1]);
+               TxBulkDummyEPList[i][1].hw_len = 0;
+               TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
+                                                  IO_STATE(USB_EP_command, eol, yes));
+               TxBulkDummyEPList[i][1].sub = 0;
+               /* The last dummy's next pointer is the same as the current EP's next pointer. */
+               TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]);
+       }
+
+       /* Configure the last one. */
+       CHECK_ALIGN(&TxBulkEPList[i]);
+       TxBulkEPList[i].hw_len = 0;
+       TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
+                                  IO_FIELD(USB_EP_command, epid, i));
+       TxBulkEPList[i].sub = 0;
+       TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]);
+
+       /* No need configuring dummy EPs for the last one as it will never be used for
+          bulk traffic (i == INVALD_EPID at this point). */
+
+       /* Set up to start on the last EP so we will enable it when inserting traffic
+          for the first time (imitating the situation where the DMA has stopped
+          because there was no more traffic). */
+       *R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]);
+       /* No point in starting the bulk channel yet.
+        *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */
+       DBFEXIT;
+}
+
+static void init_tx_ctrl_ep(void)
+{
+       int i;
+
+       DBFENTER;
+
+       for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+               CHECK_ALIGN(&TxCtrlEPList[i]);
+               TxCtrlEPList[i].hw_len = 0;
+               TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+               TxCtrlEPList[i].sub = 0;
+               TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]);
+       }
+
+       CHECK_ALIGN(&TxCtrlEPList[i]);
+       TxCtrlEPList[i].hw_len = 0;
+       TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
+                                  IO_FIELD(USB_EP_command, epid, i));
+
+       TxCtrlEPList[i].sub = 0;
+       TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]);
+
+       *R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
+       *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
+
+       DBFEXIT;
+}
+
+
+static void init_tx_intr_ep(void)
+{
+       int i;
+
+       DBFENTER;
+
+       /* Read comment at zout_buffer declaration for an explanation to this. */
+       TxIntrSB_zout.sw_len = 1;
+       TxIntrSB_zout.next = 0;
+       TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]);
+       TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                IO_STATE(USB_SB_command, tt, zout) |
+                                IO_STATE(USB_SB_command, full, yes) |
+                                IO_STATE(USB_SB_command, eot, yes) |
+                                IO_STATE(USB_SB_command, eol, yes));
+
+       for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) {
+               CHECK_ALIGN(&TxIntrEPList[i]);
+               TxIntrEPList[i].hw_len = 0;
+               TxIntrEPList[i].command =
+                       (IO_STATE(USB_EP_command, eof, yes) |
+                        IO_STATE(USB_EP_command, enable, yes) |
+                        IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+               TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
+               TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]);
+       }
+
+       CHECK_ALIGN(&TxIntrEPList[i]);
+       TxIntrEPList[i].hw_len = 0;
+       TxIntrEPList[i].command =
+               (IO_STATE(USB_EP_command, eof, yes) |
+                IO_STATE(USB_EP_command, eol, yes) |
+                IO_STATE(USB_EP_command, enable, yes) |
+                IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+       TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
+       TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]);
+
+       *R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
+       *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+       DBFEXIT;
+}
+
+static void init_tx_isoc_ep(void)
+{
+       int i;
+
+       DBFENTER;
+
+       /* Read comment at zout_buffer declaration for an explanation to this. */
+       TxIsocSB_zout.sw_len = 1;
+       TxIsocSB_zout.next = 0;
+       TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]);
+       TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                IO_STATE(USB_SB_command, tt, zout) |
+                                IO_STATE(USB_SB_command, full, yes) |
+                                IO_STATE(USB_SB_command, eot, yes) |
+                                IO_STATE(USB_SB_command, eol, yes));
+
+       /* The last isochronous EP descriptor is a dummy. */
+
+       for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+               CHECK_ALIGN(&TxIsocEPList[i]);
+               TxIsocEPList[i].hw_len = 0;
+               TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+               TxIsocEPList[i].sub = 0;
+               TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]);
+       }
+
+       CHECK_ALIGN(&TxIsocEPList[i]);
+       TxIsocEPList[i].hw_len = 0;
+
+       /* Must enable the last EP descr to get eof interrupt. */
+       TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) |
+                                  IO_STATE(USB_EP_command, eof, yes) |
+                                  IO_STATE(USB_EP_command, eol, yes) |
+                                  IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+       TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout);
+       TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]);
+
+       *R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]);
+       *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
+
+       DBFEXIT;
+}
+
+static void etrax_usb_unlink_intr_urb(struct urb *urb)
+{
+       volatile USB_EP_Desc_t *first_ep;  /* First EP in the list. */
+       volatile USB_EP_Desc_t *curr_ep;   /* Current EP, the iterator. */
+       volatile USB_EP_Desc_t *next_ep;   /* The EP after current. */
+       volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */
+
+       int epid;
+
+       /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */
+
+       DBFENTER;
+
+       epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid;
+
+       first_ep = &TxIntrEPList[0];
+       curr_ep = first_ep;
+
+
+       /* Note that this loop removes all EP descriptors with this epid. This assumes
+          that all EP descriptors belong to the one and only urb for this epid. */
+
+       do {
+               next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next);
+
+               if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) {
+
+                       dbg_intr("Found EP to unlink for epid %d", epid);
+
+                       /* This is the one we should unlink. */
+                       unlink_ep = next_ep;
+
+                       /* Actually unlink the EP from the DMA list. */
+                       curr_ep->next = unlink_ep->next;
+
+                       /* Wait until the DMA is no longer at this descriptor. */
+                       while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep));
+
+                       /* Now we are free to remove it and its SB descriptor.
+                          Note that it is assumed here that there is only one sb in the
+                          sb list for this ep. */
+                       kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub));
+                       kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep);
+               }
+
+               curr_ep = phys_to_virt(curr_ep->next);
+
+       } while (curr_ep != first_ep);
+        urb->hcpriv = NULL;
+}
+
+void etrax_usb_do_intr_recover(int epid)
+{
+       USB_EP_Desc_t *first_ep, *tmp_ep;
+
+       DBFENTER;
+
+       first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
+       tmp_ep = first_ep;
+
+       /* What this does is simply to walk the list of interrupt
+          ep descriptors and enable those that are disabled. */
+
+       do {
+               if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
+                   !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
+                       tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
+               }
+
+               tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
+
+       } while (tmp_ep != first_ep);
+
+
+       DBFEXIT;
+}
+
+static int etrax_rh_unlink_urb (struct urb *urb)
+{
+       etrax_hc_t *hc;
+
+       DBFENTER;
+
+       hc = urb->dev->bus->hcpriv;
+
+       if (hc->rh.urb == urb) {
+               hc->rh.send = 0;
+               del_timer(&hc->rh.rh_int_timer);
+       }
+
+       DBFEXIT;
+       return 0;
+}
+
+static void etrax_rh_send_irq(struct urb *urb)
+{
+       __u16 data = 0;
+       etrax_hc_t *hc = urb->dev->bus->hcpriv;
+       DBFENTER;
+
+/*
+  dbg_rh("R_USB_FM_NUMBER   : 0x%08X", *R_USB_FM_NUMBER);
+  dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING);
+*/
+
+       data |= (hc->rh.wPortChange_1) ? (1 << 1) : 0;
+       data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0;
+
+       *((__u16 *)urb->transfer_buffer) = cpu_to_le16(data);
+       /* FIXME: Why is actual_length set to 1 when data is 2 bytes?
+          Since only 1 byte is used, why not declare data as __u8? */
+       urb->actual_length = 1;
+       urb->status = 0;
+
+       if (hc->rh.send && urb->complete) {
+               dbg_rh("wPortChange_1: 0x%04X", hc->rh.wPortChange_1);
+               dbg_rh("wPortChange_2: 0x%04X", hc->rh.wPortChange_2);
+
+               urb->complete(urb, NULL);
+       }
+
+       DBFEXIT;
+}
+
+static void etrax_rh_init_int_timer(struct urb *urb)
+{
+       etrax_hc_t *hc;
+
+       DBFENTER;
+
+       hc = urb->dev->bus->hcpriv;
+       hc->rh.interval = urb->interval;
+       init_timer(&hc->rh.rh_int_timer);
+       hc->rh.rh_int_timer.function = etrax_rh_int_timer_do;
+       hc->rh.rh_int_timer.data = (unsigned long)urb;
+       /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped
+          to 0, and the rest to the nearest lower 10 ms. */
+       hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000);
+       add_timer(&hc->rh.rh_int_timer);
+
+       DBFEXIT;
+}
+
+static void etrax_rh_int_timer_do(unsigned long ptr)
+{
+       struct urb *urb;
+       etrax_hc_t *hc;
+
+       DBFENTER;
+
+       urb = (struct urb*)ptr;
+       hc = urb->dev->bus->hcpriv;
+
+       if (hc->rh.send) {
+               etrax_rh_send_irq(urb);
+       }
+
+       DBFEXIT;
+}
+
+static int etrax_usb_setup_epid(struct urb *urb)
+{
+       int epid;
+       char devnum, endpoint, out_traffic, slow;
+       int maxlen;
+       unsigned long flags;
+
+       DBFENTER;
+
+       epid = etrax_usb_lookup_epid(urb);
+       if ((epid != -1)){
+               /* An epid that fits this urb has been found. */
+               DBFEXIT;
+               return epid;
+       }
+
+       /* We must find and initiate a new epid for this urb. */
+       epid = etrax_usb_allocate_epid();
+
+       if (epid == -1) {
+               /* Failed to allocate a new epid. */
+               DBFEXIT;
+               return epid;
+       }
+
+       /* We now have a new epid to use. Initiate it. */
+       set_bit(epid, (void *)&epid_usage_bitmask);
+
+       devnum = usb_pipedevice(urb->pipe);
+       endpoint = usb_pipeendpoint(urb->pipe);
+       slow = usb_pipeslow(urb->pipe);
+       maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+       if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+               /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+               out_traffic = 1;
+       } else {
+               out_traffic = usb_pipeout(urb->pipe);
+       }
+
+       save_flags(flags);
+       cli();
+
+       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+       nop();
+
+       if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+               *R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) |
+                       /* FIXME: Change any to the actual port? */
+                       IO_STATE(R_USB_EPT_DATA_ISO, port, any) |
+                       IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) |
+                       IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) |
+                       IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum);
+       } else {
+               *R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
+                       IO_FIELD(R_USB_EPT_DATA, low_speed, slow) |
+                       /* FIXME: Change any to the actual port? */
+                       IO_STATE(R_USB_EPT_DATA, port, any) |
+                       IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) |
+                       IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
+                       IO_FIELD(R_USB_EPT_DATA, dev, devnum);
+       }
+
+       restore_flags(flags);
+
+       if (out_traffic) {
+               set_bit(epid, (void *)&epid_out_traffic);
+       } else {
+               clear_bit(epid, (void *)&epid_out_traffic);
+       }
+
+       dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)",
+                epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN");
+
+       DBFEXIT;
+       return epid;
+}
+
+static void etrax_usb_free_epid(int epid)
+{
+       unsigned long flags;
+
+       DBFENTER;
+
+       if (!test_bit(epid, (void *)&epid_usage_bitmask)) {
+               warn("Trying to free unused epid %d", epid);
+               DBFEXIT;
+               return;
+       }
+
+       save_flags(flags);
+       cli();
+
+       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+       nop();
+       while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold));
+       /* This will, among other things, set the valid field to 0. */
+       *R_USB_EPT_DATA = 0;
+       restore_flags(flags);
+
+       clear_bit(epid, (void *)&epid_usage_bitmask);
+
+
+       dbg_epid("Freed epid %d", epid);
+
+       DBFEXIT;
+}
+
+static int etrax_usb_lookup_epid(struct urb *urb)
+{
+       int i;
+       __u32 data;
+       char devnum, endpoint, slow, out_traffic;
+       int maxlen;
+       unsigned long flags;
+
+       DBFENTER;
+
+       devnum = usb_pipedevice(urb->pipe);
+       endpoint = usb_pipeendpoint(urb->pipe);
+       slow = usb_pipeslow(urb->pipe);
+       maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+       if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+               /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+               out_traffic = 1;
+       } else {
+               out_traffic = usb_pipeout(urb->pipe);
+       }
+
+       /* Step through att epids. */
+       for (i = 0; i < NBR_OF_EPIDS; i++) {
+               if (test_bit(i, (void *)&epid_usage_bitmask) &&
+                   test_bit(i, (void *)&epid_out_traffic) == out_traffic) {
+
+                       save_flags(flags);
+                       cli();
+                       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
+                       nop();
+
+                       if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                               data = *R_USB_EPT_DATA_ISO;
+                               restore_flags(flags);
+
+                               if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) &&
+                                   (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) &&
+                                   (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) &&
+                                   (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) {
+                                       dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+                                                i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+                                       DBFEXIT;
+                                       return i;
+                               }
+                       } else {
+                               data = *R_USB_EPT_DATA;
+                               restore_flags(flags);
+
+                               if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
+                                   (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
+                                   (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
+                                   (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
+                                   (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) {
+                                       dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+                                                i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+                                       DBFEXIT;
+                                       return i;
+                               }
+                       }
+               }
+       }
+
+       DBFEXIT;
+       return -1;
+}
+
+static int etrax_usb_allocate_epid(void)
+{
+       int i;
+
+       DBFENTER;
+
+       for (i = 0; i < NBR_OF_EPIDS; i++) {
+               if (!test_bit(i, (void *)&epid_usage_bitmask)) {
+                       dbg_epid("Found free epid %d", i);
+                       DBFEXIT;
+                       return i;
+               }
+       }
+
+       dbg_epid("Found no free epids");
+       DBFEXIT;
+       return -1;
+}
+
+static int etrax_usb_submit_urb(struct urb *urb, int mem_flags)
+{
+       etrax_hc_t *hc;
+       int ret = -EINVAL;
+
+       DBFENTER;
+
+       if (!urb->dev || !urb->dev->bus) {
+               return -ENODEV;
+       }
+       if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) {
+               info("Submit urb to pipe with maxpacketlen 0, pipe 0x%X\n", urb->pipe);
+               return -EMSGSIZE;
+       }
+
+       if (urb->timeout) {
+               /* FIXME. */
+               warn("urb->timeout specified, ignoring.");
+       }
+
+       hc = (etrax_hc_t*)urb->dev->bus->hcpriv;
+
+       if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+               /* This request is for the Virtual Root Hub. */
+               ret = etrax_rh_submit_urb(urb);
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+               ret = etrax_usb_submit_bulk_urb(urb);
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+               ret = etrax_usb_submit_ctrl_urb(urb);
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+               int bustime;
+
+               if (urb->bandwidth == 0) {
+                       bustime = usb_check_bandwidth(urb->dev, urb);
+                       if (bustime < 0) {
+                               ret = bustime;
+                       } else {
+                               ret = etrax_usb_submit_intr_urb(urb);
+                               if (ret == 0)
+                                       usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+                       }
+               } else {
+                       /* Bandwidth already set. */
+                       ret = etrax_usb_submit_intr_urb(urb);
+               }
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+               int bustime;
+
+               if (urb->bandwidth == 0) {
+                       bustime = usb_check_bandwidth(urb->dev, urb);
+                       if (bustime < 0) {
+                               ret = bustime;
+                       } else {
+                               ret = etrax_usb_submit_isoc_urb(urb);
+                               if (ret == 0)
+                                       usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+                       }
+               } else {
+                       /* Bandwidth already set. */
+                       ret = etrax_usb_submit_isoc_urb(urb);
+               }
+       }
+
+       DBFEXIT;
+
+        if (ret != 0)
+          printk("Submit URB error %d\n", ret);
+
+       return ret;
+}
+
+static int etrax_usb_unlink_urb(struct urb *urb, int status)
+{
+       etrax_hc_t *hc;
+       etrax_urb_priv_t *urb_priv;
+       int epid;
+       unsigned int flags;
+
+       DBFENTER;
+
+       if (!urb) {
+               return -EINVAL;
+       }
+
+       /* Disable interrupts here since a descriptor interrupt for the isoc epid
+          will modify the sb list.  This could possibly be done more granular, but
+          unlink_urb should not be used frequently anyway.
+       */
+
+       save_flags(flags);
+       cli();
+
+       if (!urb->dev || !urb->dev->bus) {
+               restore_flags(flags);
+               return -ENODEV;
+       }
+       if (!urb->hcpriv) {
+               /* This happens if a device driver calls unlink on an urb that
+                  was never submitted (lazy driver) or if the urb was completed
+                  while unlink was being called. */
+               restore_flags(flags);
+               return 0;
+       }
+       if (urb->transfer_flags & URB_ASYNC_UNLINK) {
+               /* FIXME. */
+               /* If URB_ASYNC_UNLINK is set:
+                  unlink
+                  move to a separate urb list
+                  call complete at next sof with ECONNRESET
+
+                  If not:
+                  wait 1 ms
+                  unlink
+                  call complete with ENOENT
+               */
+               warn("URB_ASYNC_UNLINK set, ignoring.");
+       }
+
+       /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking,
+          but that doesn't work for interrupt and isochronous traffic since they are completed
+          repeatedly, and urb->status is set then. That may in itself be a bug though. */
+
+       hc = urb->dev->bus->hcpriv;
+       urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+       epid = urb_priv->epid;
+
+       /* Set the urb status (synchronous unlink). */
+       urb->status = -ENOENT;
+       urb_priv->urb_state = UNLINK;
+
+       if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+               int ret;
+               ret = etrax_rh_unlink_urb(urb);
+               DBFEXIT;
+               restore_flags(flags);
+               return ret;
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+               dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb);
+
+               if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                       /* The EP was enabled, disable it and wait. */
+                       TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+
+                       /* Ah, the luxury of busy-wait. */
+                       while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
+               }
+               /* Kicking dummy list out of the party. */
+               TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+               dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb);
+
+               if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                       /* The EP was enabled, disable it and wait. */
+                       TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+
+                       /* Ah, the luxury of busy-wait. */
+                       while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
+               }
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+
+               dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb);
+
+               /* Separate function because it's a tad more complicated. */
+               etrax_usb_unlink_intr_urb(urb);
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+
+               dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb);
+
+               if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                       /* The EP was enabled, disable it and wait. */
+                       TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+
+                       /* Ah, the luxury of busy-wait. */
+                       while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
+               }
+       }
+
+       /* Note that we need to remove the urb from the urb list *before* removing its SB
+          descriptors. (This means that the isoc eof handler might get a null urb when we
+          are unlinking the last urb.) */
+
+       if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+               urb_list_del(urb, epid);
+               TxBulkEPList[epid].sub = 0;
+               etrax_remove_from_sb_list(urb);
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+               urb_list_del(urb, epid);
+               TxCtrlEPList[epid].sub = 0;
+               etrax_remove_from_sb_list(urb);
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+
+               urb_list_del(urb, epid);
+               /* Sanity check (should never happen). */
+               assert(urb_list_empty(epid));
+
+               /* Release allocated bandwidth. */
+               usb_release_bandwidth(urb->dev, urb, 0);
+
+       } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+
+               if (usb_pipeout(urb->pipe)) {
+
+                       USB_SB_Desc_t *iter_sb, *prev_sb, *next_sb;
+
+                       if (__urb_list_entry(urb, epid)) {
+
+                               urb_list_del(urb, epid);
+                               iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
+                               prev_sb = 0;
+                               while (iter_sb && (iter_sb != urb_priv->first_sb)) {
+                                       prev_sb = iter_sb;
+                                       iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+                               }
+
+                               if (iter_sb == 0) {
+                                       /* Unlink of the URB currently being transmitted. */
+                                       prev_sb = 0;
+                                       iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
+                               }
+
+                               while (iter_sb && (iter_sb != urb_priv->last_sb)) {
+                                       iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+                               }
+                               if (iter_sb) {
+                                       next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+                               } else {
+                                       /* This should only happen if the DMA has completed
+                                          processing the SB list for this EP while interrupts
+                                          are disabled. */
+                                       dbg_isoc("Isoc urb not found, already sent?");
+                                       next_sb = 0;
+                               }
+                               if (prev_sb) {
+                                       prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0;
+                               } else {
+                                       TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0;
+                               }
+
+                               etrax_remove_from_sb_list(urb);
+                               if (urb_list_empty(epid)) {
+                                       TxIsocEPList[epid].sub = 0;
+                                       dbg_isoc("Last isoc out urb epid %d", epid);
+                               } else if (next_sb || prev_sb) {
+                                       dbg_isoc("Re-enable isoc out epid %d", epid);
+
+                                       TxIsocEPList[epid].hw_len = 0;
+                                       TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+                               } else {
+                                       TxIsocEPList[epid].sub = 0;
+                                       dbg_isoc("URB list non-empty and no SB list, EP disabled");
+                               }
+                       } else {
+                               dbg_isoc("Urb 0x%p not found, completed already?", urb);
+                       }
+               } else {
+
+                       urb_list_del(urb, epid);
+
+                       /* For in traffic there is only one SB descriptor for each EP even
+                          though there may be several urbs (all urbs point at the same SB). */
+                       if (urb_list_empty(epid)) {
+                               /* No more urbs, remove the SB. */
+                               TxIsocEPList[epid].sub = 0;
+                               etrax_remove_from_sb_list(urb);
+                       } else {
+                               TxIsocEPList[epid].hw_len = 0;
+                               TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+                       }
+               }
+               /* Release allocated bandwidth. */
+               usb_release_bandwidth(urb->dev, urb, 1);
+       }
+       /* Free the epid if urb list is empty. */
+       if (urb_list_empty(epid)) {
+               etrax_usb_free_epid(epid);
+       }
+       restore_flags(flags);
+
+       /* Must be done before calling completion handler. */
+       kfree(urb_priv);
+       urb->hcpriv = 0;
+
+       if (urb->complete) {
+               urb->complete(urb, NULL);
+       }
+
+       DBFEXIT;
+       return 0;
+}
+
+static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
+{
+       DBFENTER;
+       DBFEXIT;
+       return (*R_USB_FM_NUMBER & 0x7ff);
+}
+
+static int etrax_usb_allocate_dev(struct usb_device *usb_dev)
+{
+       DBFENTER;
+       DBFEXIT;
+       return 0;
+}
+
+static int etrax_usb_deallocate_dev(struct usb_device *usb_dev)
+{
+       DBFENTER;
+       DBFEXIT;
+       return 0;
+}
+
+static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+       DBFENTER;
+
+       /* This interrupt handler could be used when unlinking EP descriptors. */
+
+       if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
+               USB_EP_Desc_t *ep;
+
+               //dbg_bulk("dma8_sub0_descr (BULK) intr.");
+
+               /* It should be safe clearing the interrupt here, since we don't expect to get a new
+                  one until we restart the bulk channel. */
+               *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
+
+               /* Wait while the DMA is running (though we don't expect it to be). */
+               while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd));
+
+               /* Advance the DMA to the next EP descriptor. */
+               ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
+
+               //dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep);
+
+               /* ep->next is already a physical address; no need for a virt_to_phys. */
+               *R_DMA_CH8_SUB0_EP = ep->next;
+
+               /* Start the DMA bulk channel again. */
+               *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+       }
+       if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
+               struct urb *urb;
+               int epid;
+               etrax_urb_priv_t *urb_priv;
+               unsigned long int flags;
+
+               dbg_ctrl("dma8_sub1_descr (CTRL) intr.");
+               *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
+
+               /* The complete callback gets called so we cli. */
+               save_flags(flags);
+               cli();
+
+               for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+                       if ((TxCtrlEPList[epid].sub == 0) ||
+                           (epid == DUMMY_EPID) ||
+                           (epid == INVALID_EPID)) {
+                               /* Nothing here to see. */
+                               continue;
+                       }
+
+                       /* Get the first urb (if any). */
+                       urb = urb_list_first(epid);
+
+                       if (urb) {
+
+                               /* Sanity check. */
+                               assert(usb_pipetype(urb->pipe) == PIPE_CONTROL);
+
+                               urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+                               assert(urb_priv);
+
+                               if (urb_priv->urb_state == WAITING_FOR_DESCR_INTR) {
+                                       assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+
+                                       etrax_usb_complete_urb(urb, 0);
+                               }
+                       }
+               }
+               restore_flags(flags);
+       }
+       if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
+               dbg_intr("dma8_sub2_descr (INTR) intr.");
+               *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
+       }
+       if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
+               struct urb *urb;
+               int epid;
+               int epid_done;
+               etrax_urb_priv_t *urb_priv;
+               USB_SB_Desc_t *sb_desc;
+
+               usb_isoc_complete_data_t *comp_data = NULL;
+
+               /* One or more isoc out transfers are done. */
+               dbg_isoc("dma8_sub3_descr (ISOC) intr.");
+
+               /* For each isoc out EP search for the first sb_desc with the intr flag
+                  set.  This descriptor must be the last packet from an URB.  Then
+                  traverse the URB list for the EP until the URB with urb_priv->last_sb
+                  matching the intr-marked sb_desc is found.  All URBs before this have
+                  been sent.
+               */
+
+               for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+                       /* Skip past epids with no SB lists, epids used for in traffic,
+                          and special (dummy, invalid) epids. */
+                       if ((TxIsocEPList[epid].sub == 0) ||
+                           (test_bit(epid, (void *)&epid_out_traffic) == 0) ||
+                           (epid == DUMMY_EPID) ||
+                           (epid == INVALID_EPID)) {
+                               /* Nothing here to see. */
+                               continue;
+                       }
+                       sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
+
+                       /* Find the last descriptor of the currently active URB for this ep.
+                          This is the first descriptor in the sub list marked for a descriptor
+                          interrupt. */
+                       while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) {
+                               sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0;
+                       }
+                       assert(sb_desc);
+
+                       dbg_isoc("Check epid %d, sub 0x%p, SB 0x%p",
+                                epid,
+                                phys_to_virt(TxIsocEPList[epid].sub),
+                                sb_desc);
+
+                       epid_done = 0;
+
+                       /* Get the first urb (if any). */
+                       urb = urb_list_first(epid);
+                       assert(urb);
+
+                       while (urb && !epid_done) {
+
+                               /* Sanity check. */
+                               assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
+
+                               if (!usb_pipeout(urb->pipe)) {
+                                       /* descr interrupts are generated only for out pipes. */
+                                       epid_done = 1;
+                                       continue;
+                               }
+
+                               urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+                               assert(urb_priv);
+
+                               if (sb_desc != urb_priv->last_sb) {
+
+                                       /* This urb has been sent. */
+                                       dbg_isoc("out URB 0x%p sent", urb);
+
+                                       urb_priv->urb_state = TRANSFER_DONE;
+
+                               } else if ((sb_desc == urb_priv->last_sb) &&
+                                          !(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
+
+                                       assert((sb_desc->command & IO_MASK(USB_SB_command, eol)) == IO_STATE(USB_SB_command, eol, yes));
+                                       assert(sb_desc->next == 0);
+
+                                       dbg_isoc("out URB 0x%p last in list, epid disabled", urb);
+                                       TxIsocEPList[epid].sub = 0;
+                                       TxIsocEPList[epid].hw_len = 0;
+                                       urb_priv->urb_state = TRANSFER_DONE;
+
+                                       epid_done = 1;
+
+                               } else {
+                                       epid_done = 1;
+                               }
+                               if (!epid_done) {
+                                       urb = urb_list_next(urb, epid);
+                               }
+                       }
+
+               }
+
+               *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
+
+               comp_data = (usb_isoc_complete_data_t*)kmem_cache_alloc(isoc_compl_cache, SLAB_ATOMIC);
+               assert(comp_data != NULL);
+
+                INIT_WORK(&comp_data->usb_bh, etrax_usb_isoc_descr_interrupt_bottom_half, comp_data);
+                schedule_work(&comp_data->usb_bh);
+       }
+
+       DBFEXIT;
+        return IRQ_HANDLED;
+}
+
+static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data)
+{
+       usb_isoc_complete_data_t *comp_data = (usb_isoc_complete_data_t*)data;
+
+       struct urb *urb;
+       int epid;
+       int epid_done;
+       etrax_urb_priv_t *urb_priv;
+
+       DBFENTER;
+
+       dbg_isoc("dma8_sub3_descr (ISOC) bottom half.");
+
+       for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+               unsigned long flags;
+
+               save_flags(flags);
+               cli();
+
+               epid_done = 0;
+
+               /* The descriptor interrupt handler has marked all transmitted isoch. out
+                  URBs with TRANSFER_DONE.  Now we traverse all epids and for all that
+                  have isoch. out traffic traverse its URB list and complete the
+                  transmitted URB.
+               */
+
+               while (!epid_done) {
+
+                       /* Get the first urb (if any). */
+                       urb = urb_list_first(epid);
+                       if (urb == 0) {
+                               epid_done = 1;
+                               continue;
+                       }
+
+                       if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
+                                       epid_done = 1;
+                                       continue;
+                       }
+
+                       if (!usb_pipeout(urb->pipe)) {
+                               /* descr interrupts are generated only for out pipes. */
+                               epid_done = 1;
+                               continue;
+                       }
+
+                       dbg_isoc("Check epid %d, SB 0x%p", epid, (char*)TxIsocEPList[epid].sub);
+
+                       urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+                       assert(urb_priv);
+
+                       if (urb_priv->urb_state == TRANSFER_DONE) {
+                               int i;
+                               struct usb_iso_packet_descriptor *packet;
+
+                               /* This urb has been sent. */
+                               dbg_isoc("Completing isoc out URB 0x%p", urb);
+
+                               for (i = 0; i < urb->number_of_packets; i++) {
+                                       packet = &urb->iso_frame_desc[i];
+                                       packet->status = 0;
+                                       packet->actual_length = packet->length;
+                               }
+
+                               etrax_usb_complete_isoc_urb(urb, 0);
+
+                               if (urb_list_empty(epid)) {
+                                       etrax_usb_free_epid(epid);
+                                       epid_done = 1;
+                               }
+                       } else {
+                               epid_done = 1;
+                       }
+               }
+               restore_flags(flags);
+
+       }
+       kmem_cache_free(isoc_compl_cache, comp_data);
+
+       DBFEXIT;
+}
+
+
+
+static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+       struct urb *urb;
+       etrax_urb_priv_t *urb_priv;
+       int epid = 0;
+       unsigned long flags;
+
+       /* Isoc diagnostics. */
+       static int curr_fm = 0;
+       static int prev_fm = 0;
+
+       DBFENTER;
+
+       /* Clear this interrupt. */
+       *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
+
+       /* Note that this while loop assumes that all packets span only
+          one rx descriptor. */
+
+       /* The reason we cli here is that we call the driver's callback functions. */
+       save_flags(flags);
+       cli();
+
+       while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
+
+               epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
+               urb = urb_list_first(epid);
+
+               //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb);
+
+               if (!urb) {
+                       err("No urb for epid %d in rx interrupt", epid);
+                       __dump_ept_data(epid);
+                       goto skip_out;
+               }
+
+               /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since
+                  ctrl pipes are not. */
+
+               if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
+                       __u32 r_usb_ept_data;
+                       int no_error = 0;
+
+                       assert(test_bit(epid, (void *)&epid_usage_bitmask));
+
+                       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+                       nop();
+                       if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                               r_usb_ept_data = *R_USB_EPT_DATA_ISO;
+
+                               if ((r_usb_ept_data & IO_MASK(R_USB_EPT_DATA_ISO, valid)) &&
+                                   (IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data) == 0) &&
+                                   (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata))) {
+                                       /* Not an error, just a failure to receive an expected iso
+                                          in packet in this frame.  This is not documented
+                                          in the designers reference.
+                                       */
+                                       no_error++;
+                               } else {
+                                       warn("R_USB_EPT_DATA_ISO for epid %d = 0x%x", epid, r_usb_ept_data);
+                               }
+                       } else {
+                               r_usb_ept_data = *R_USB_EPT_DATA;
+                               warn("R_USB_EPT_DATA for epid %d = 0x%x", epid, r_usb_ept_data);
+                       }
+
+                       if (!no_error){
+                               warn("error in rx desc->status, epid %d, first urb = 0x%lx",
+                                    epid, (unsigned long)urb);
+                               __dump_in_desc(myNextRxDesc);
+
+                               warn("R_USB_STATUS = 0x%x", *R_USB_STATUS);
+
+                               /* Check that ept was disabled when error occurred. */
+                               switch (usb_pipetype(urb->pipe)) {
+                               case PIPE_BULK:
+                                       assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+                                       break;
+                               case PIPE_CONTROL:
+                                       assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+                                       break;
+                               case PIPE_INTERRUPT:
+                                       assert(!(TxIntrEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+                                       break;
+                               case PIPE_ISOCHRONOUS:
+                                       assert(!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+                                       break;
+                               default:
+                                       warn("etrax_usb_rx_interrupt: bad pipetype %d in urb 0x%p",
+                                            usb_pipetype(urb->pipe),
+                                            urb);
+                               }
+                               etrax_usb_complete_urb(urb, -EPROTO);
+                               goto skip_out;
+                       }
+               }
+
+               urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+               assert(urb_priv);
+
+               if ((usb_pipetype(urb->pipe) == PIPE_BULK) ||
+                   (usb_pipetype(urb->pipe) == PIPE_CONTROL) ||
+                   (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
+
+                       if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+                               /* We get nodata for empty data transactions, and the rx descriptor's
+                                  hw_len field is not valid in that case. No data to copy in other
+                                  words. */
+                       } else {
+                               /* Make sure the data fits in the buffer. */
+                               assert(urb_priv->rx_offset + myNextRxDesc->hw_len
+                                      <= urb->transfer_buffer_length);
+
+                               memcpy(urb->transfer_buffer + urb_priv->rx_offset,
+                                      phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);
+                               urb_priv->rx_offset += myNextRxDesc->hw_len;
+                       }
+
+                       if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
+                               if ((usb_pipetype(urb->pipe) == PIPE_CONTROL) &&
+                                   ((TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)) ==
+                                    IO_STATE(USB_EP_command, enable, yes))) {
+                                       /* The EP is still enabled, so the OUT packet used to ack
+                                          the in data is probably not processed yet.  If the EP
+                                          sub pointer has not moved beyond urb_priv->last_sb mark
+                                          it for a descriptor interrupt and complete the urb in
+                                          the descriptor interrupt handler.
+                                       */
+                                       USB_SB_Desc_t *sub = TxCtrlEPList[urb_priv->epid].sub ? phys_to_virt(TxCtrlEPList[urb_priv->epid].sub) : 0;
+
+                                       while ((sub != NULL) && (sub != urb_priv->last_sb)) {
+                                               sub = sub->next ? phys_to_virt(sub->next) : 0;
+                                       }
+                                       if (sub != NULL) {
+                                               /* The urb has not been fully processed. */
+                                               urb_priv->urb_state = WAITING_FOR_DESCR_INTR;
+                                       } else {
+                                               warn("(CTRL) epid enabled and urb (0x%p) processed, ep->sub=0x%p", urb, (char*)TxCtrlEPList[urb_priv->epid].sub);
+                                               etrax_usb_complete_urb(urb, 0);
+                                       }
+                               } else {
+                                       etrax_usb_complete_urb(urb, 0);
+                               }
+                       }
+
+               } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+
+                       struct usb_iso_packet_descriptor *packet;
+
+                       if (urb_priv->urb_state == UNLINK) {
+                               info("Ignoring rx data for urb being unlinked.");
+                               goto skip_out;
+                       } else if (urb_priv->urb_state == NOT_STARTED) {
+                               info("What? Got rx data for urb that isn't started?");
+                               goto skip_out;
+                       }
+
+                       packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter];
+                       packet->status = 0;
+
+                       if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+                               /* We get nodata for empty data transactions, and the rx descriptor's
+                                  hw_len field is not valid in that case. We copy 0 bytes however to
+                                  stay in synch. */
+                               packet->actual_length = 0;
+                       } else {
+                               packet->actual_length = myNextRxDesc->hw_len;
+                               /* Make sure the data fits in the buffer. */
+                               assert(packet->actual_length <= packet->length);
+                               memcpy(urb->transfer_buffer + packet->offset,
+                                      phys_to_virt(myNextRxDesc->buf), packet->actual_length);
+                       }
+
+                       /* Increment the packet counter. */
+                       urb_priv->isoc_packet_counter++;
+
+                       /* Note that we don't care about the eot field in the rx descriptor's status.
+                          It will always be set for isoc traffic. */
+                       if (urb->number_of_packets == urb_priv->isoc_packet_counter) {
+
+                               /* Out-of-synch diagnostics. */
+                               curr_fm = (*R_USB_FM_NUMBER & 0x7ff);
+                               if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) {
+                                       /* This test is wrong, if there is more than one isoc
+                                          in endpoint active it will always calculate wrong
+                                          since prev_fm is shared by all endpoints.
+
+                                          FIXME Make this check per URB using urb->start_frame.
+                                       */
+                                       dbg_isoc("Out of synch? Previous frame = %d, current frame = %d",
+                                                prev_fm, curr_fm);
+
+                               }
+                               prev_fm = curr_fm;
+
+                               /* Complete the urb with status OK. */
+                               etrax_usb_complete_isoc_urb(urb, 0);
+                       }
+               }
+
+       skip_out:
+
+               /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr
+                  has the same layout as USB_IN_Desc for the relevant fields.) */
+               prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc);
+
+               myPrevRxDesc = myNextRxDesc;
+               myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
+               myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
+               myLastRxDesc = myPrevRxDesc;
+
+               myNextRxDesc->status = 0;
+               myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+       }
+
+       restore_flags(flags);
+
+       DBFEXIT;
+
+        return IRQ_HANDLED;
+}
+
+
+/* This function will unlink the SB descriptors associated with this urb. */
+static int etrax_remove_from_sb_list(struct urb *urb)
+{
+       USB_SB_Desc_t *next_sb, *first_sb, *last_sb;
+       etrax_urb_priv_t *urb_priv;
+       int i = 0;
+
+       DBFENTER;
+
+       urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+       assert(urb_priv);
+
+       /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor
+          doesn't really need to be disabled, it's just that we expect it to be. */
+       if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+               assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+       } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+               assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+       }
+
+       first_sb = urb_priv->first_sb;
+       last_sb = urb_priv->last_sb;
+
+       assert(first_sb);
+       assert(last_sb);
+
+       while (first_sb != last_sb) {
+               next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next);
+               kmem_cache_free(usb_desc_cache, first_sb);
+               first_sb = next_sb;
+               i++;
+       }
+       kmem_cache_free(usb_desc_cache, last_sb);
+       i++;
+       dbg_sb("%d SB descriptors freed", i);
+       /* Compare i with urb->number_of_packets for Isoc traffic.
+          Should be same when calling unlink_urb */
+
+       DBFEXIT;
+
+       return i;
+}
+
+static int etrax_usb_submit_bulk_urb(struct urb *urb)
+{
+       int epid;
+       int empty;
+       unsigned long flags;
+       etrax_urb_priv_t *urb_priv;
+
+       DBFENTER;
+
+       /* Epid allocation, empty check and list add must be protected.
+          Read about this in etrax_usb_submit_ctrl_urb. */
+
+       spin_lock_irqsave(&urb_list_lock, flags);
+       epid = etrax_usb_setup_epid(urb);
+       if (epid == -1) {
+               DBFEXIT;
+               spin_unlock_irqrestore(&urb_list_lock, flags);
+               return -ENOMEM;
+       }
+       empty = urb_list_empty(epid);
+       urb_list_add(urb, epid);
+       spin_unlock_irqrestore(&urb_list_lock, flags);
+
+       dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d",
+                usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid);
+
+       /* Mark the urb as being in progress. */
+       urb->status = -EINPROGRESS;
+
+       /* Setup the hcpriv data. */
+       urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+       assert(urb_priv != NULL);
+       /* This sets rx_offset to 0. */
+       memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+       urb_priv->urb_state = NOT_STARTED;
+       urb->hcpriv = urb_priv;
+
+       if (empty) {
+               etrax_usb_add_to_bulk_sb_list(urb, epid);
+       }
+
+       DBFEXIT;
+
+       return 0;
+}
+
+static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid)
+{
+       USB_SB_Desc_t *sb_desc;
+       etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+       unsigned long flags;
+       char maxlen;
+
+       DBFENTER;
+
+       dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb);
+
+       maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+       sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+       assert(sb_desc != NULL);
+       memset(sb_desc, 0, sizeof(USB_SB_Desc_t));
+
+
+       if (usb_pipeout(urb->pipe)) {
+
+               dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+
+               /* This is probably a sanity check of the bulk transaction length
+                  not being larger than 64 kB. */
+               if (urb->transfer_buffer_length > 0xffff) {
+                       panic("urb->transfer_buffer_length > 0xffff");
+               }
+
+               sb_desc->sw_len = urb->transfer_buffer_length;
+
+               /* The rem field is don't care if it's not a full-length transfer, so setting
+                  it shouldn't hurt. Also, rem isn't used for OUT traffic. */
+               sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                   IO_STATE(USB_SB_command, tt, out) |
+                                   IO_STATE(USB_SB_command, eot, yes) |
+                                   IO_STATE(USB_SB_command, eol, yes));
+
+               /* The full field is set to yes, even if we don't actually check that this is
+                  a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0).
+                  Setting full prevents the USB controller from sending an empty packet in
+                  that case.  However, if URB_ZERO_PACKET was set we want that. */
+               if (!(urb->transfer_flags & URB_ZERO_PACKET)) {
+                       sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+               }
+
+               sb_desc->buf = virt_to_phys(urb->transfer_buffer);
+               sb_desc->next = 0;
+
+       } else if (usb_pipein(urb->pipe)) {
+
+               dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+
+               sb_desc->sw_len = urb->transfer_buffer_length ?
+                       (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+
+               /* The rem field is don't care if it's not a full-length transfer, so setting
+                  it shouldn't hurt. */
+               sb_desc->command =
+                       (IO_FIELD(USB_SB_command, rem,
+                                 urb->transfer_buffer_length % maxlen) |
+                        IO_STATE(USB_SB_command, tt, in) |
+                        IO_STATE(USB_SB_command, eot, yes) |
+                        IO_STATE(USB_SB_command, eol, yes));
+
+               sb_desc->buf = 0;
+               sb_desc->next = 0;
+       }
+
+       urb_priv->first_sb = sb_desc;
+       urb_priv->last_sb = sb_desc;
+       urb_priv->epid = epid;
+
+       urb->hcpriv = urb_priv;
+
+       /* Reset toggle bits and reset error count. */
+       save_flags(flags);
+       cli();
+
+       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+       nop();
+
+       /* FIXME: Is this a special case since the hold field is checked,
+          or should we check hold in a lot of other cases as well? */
+       if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
+               panic("Hold was set in %s", __FUNCTION__);
+       }
+
+       /* Reset error counters (regardless of which direction this traffic is). */
+       *R_USB_EPT_DATA &=
+               ~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
+                 IO_MASK(R_USB_EPT_DATA, error_count_out));
+
+       /* Software must preset the toggle bits. */
+       if (usb_pipeout(urb->pipe)) {
+               char toggle =
+                       usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+               *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out);
+               *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle);
+       } else {
+               char toggle =
+                       usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+               *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in);
+               *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle);
+       }
+
+       /* Assert that the EP descriptor is disabled. */
+       assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+
+       /* The reason we set the EP's sub pointer directly instead of
+          walking the SB list and linking it last in the list is that we only
+          have one active urb at a time (the rest are queued). */
+
+       /* Note that we cannot have interrupts running when we have set the SB descriptor
+          but the EP is not yet enabled.  If a bulk eot happens for another EP, we will
+          find this EP disabled and with a SB != 0, which will make us think that it's done. */
+       TxBulkEPList[epid].sub = virt_to_phys(sb_desc);
+       TxBulkEPList[epid].hw_len = 0;
+       /* Note that we don't have to fill in the ep_id field since this
+          was done when we allocated the EP descriptors in init_tx_bulk_ep. */
+
+       /* Check if the dummy list is already with us (if several urbs were queued). */
+       if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) {
+
+               dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d",
+                        (unsigned long)urb, epid);
+
+               /* The last EP in the dummy list already has its next pointer set to
+                  TxBulkEPList[epid].next. */
+
+               /* We don't need to check if the DMA is at this EP or not before changing the
+                  next pointer, since we will do it in one 32-bit write (EP descriptors are
+                  32-bit aligned). */
+               TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]);
+       }
+       /* Enable the EP descr. */
+       dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid);
+       TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+       /* Everything is set up, safe to enable interrupts again. */
+       restore_flags(flags);
+
+       /* If the DMA bulk channel isn't running, we need to restart it if it
+          has stopped at the last EP descriptor (DMA stopped because there was
+          no more traffic) or if it has stopped at a dummy EP with the intr flag
+          set (DMA stopped because we were too slow in inserting new traffic). */
+       if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
+
+               USB_EP_Desc_t *ep;
+               ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
+               dbg_bulk("DMA channel not running in add");
+               dbg_bulk("DMA is at 0x%lx", (unsigned long)ep);
+
+               if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) ||
+                   (ep->command & 0x8) >> 3) {
+                       *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+                       /* Update/restart the bulk start timer since we just started the channel. */
+                       mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+                       /* Update/restart the bulk eot timer since we just inserted traffic. */
+                       mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+               }
+       }
+
+       DBFEXIT;
+}
+
+static void etrax_usb_complete_bulk_urb(struct urb *urb, int status)
+{
+       etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+       int epid = urb_priv->epid;
+       unsigned long flags;
+
+       DBFENTER;
+
+       if (status)
+               warn("Completing bulk urb with status %d.", status);
+
+       dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid);
+
+       /* Update the urb list. */
+       urb_list_del(urb, epid);
+
+       /* For an IN pipe, we always set the actual length, regardless of whether there was
+          an error or not (which means the device driver can use the data if it wants to). */
+       if (usb_pipein(urb->pipe)) {
+               urb->actual_length = urb_priv->rx_offset;
+       } else {
+               /* Set actual_length for OUT urbs also; the USB mass storage driver seems
+                  to want that. We wouldn't know of any partial writes if there was an error. */
+               if (status == 0) {
+                       urb->actual_length = urb->transfer_buffer_length;
+               } else {
+                       urb->actual_length = 0;
+               }
+       }
+
+       /* FIXME: Is there something of the things below we shouldn't do if there was an error?
+          Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */
+
+       save_flags(flags);
+       cli();
+
+       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+       nop();
+
+       /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */
+       if (usb_pipeout(urb->pipe)) {
+               char toggle =
+                       IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
+               usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+                             usb_pipeout(urb->pipe), toggle);
+       } else {
+               char toggle =
+                       IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
+               usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+                             usb_pipeout(urb->pipe), toggle);
+       }
+       restore_flags(flags);
+
+       /* Remember to free the SBs. */
+       etrax_remove_from_sb_list(urb);
+       kfree(urb_priv);
+       urb->hcpriv = 0;
+
+       /* If there are any more urb's in the list we'd better start sending */
+       if (!urb_list_empty(epid)) {
+
+               struct urb *new_urb;
+
+               /* Get the first urb. */
+               new_urb = urb_list_first(epid);
+               assert(new_urb);
+
+               dbg_bulk("More bulk for epid %d", epid);
+
+               etrax_usb_add_to_bulk_sb_list(new_urb, epid);
+       }
+
+       urb->status = status;
+
+       /* We let any non-zero status from the layer above have precedence. */
+       if (status == 0) {
+               /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
+                  is to be treated as an error. */
+               if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+                       if (usb_pipein(urb->pipe) &&
+                           (urb->actual_length !=
+                            usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+                               urb->status = -EREMOTEIO;
+                       }
+               }
+       }
+
+       if (urb->complete) {
+               urb->complete(urb, NULL);
+       }
+
+       if (urb_list_empty(epid)) {
+               /* This means that this EP is now free, deconfigure it. */
+               etrax_usb_free_epid(epid);
+
+               /* No more traffic; time to clean up.
+                  Must set sub pointer to 0, since we look at the sub pointer when handling
+                  the bulk eot interrupt. */
+
+               dbg_bulk("No bulk for epid %d", epid);
+
+               TxBulkEPList[epid].sub = 0;
+
+               /* Unlink the dummy list. */
+
+               dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d",
+                        (unsigned long)urb, epid);
+
+               /* No need to wait for the DMA before changing the next pointer.
+                  The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use
+                  the last one (INVALID_EPID) for actual traffic. */
+               TxBulkEPList[epid].next =
+                       virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
+       }
+
+       DBFEXIT;
+}
+
+static int etrax_usb_submit_ctrl_urb(struct urb *urb)
+{
+       int epid;
+       int empty;
+       unsigned long flags;
+       etrax_urb_priv_t *urb_priv;
+
+       DBFENTER;
+
+       /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */
+
+       /* Epid allocation, empty check and list add must be protected.
+
+          Epid allocation because if we find an existing epid for this endpoint an urb might be
+          completed (emptying the list) before we add the new urb to the list, causing the epid
+          to be de-allocated. We would then start the transfer with an invalid epid -> epid attn.
+
+          Empty check and add because otherwise we might conclude that the list is not empty,
+          after which it becomes empty before we add the new urb to the list, causing us not to
+          insert the new traffic into the SB list. */
+
+       spin_lock_irqsave(&urb_list_lock, flags);
+       epid = etrax_usb_setup_epid(urb);
+       if (epid == -1) {
+               spin_unlock_irqrestore(&urb_list_lock, flags);
+               DBFEXIT;
+               return -ENOMEM;
+       }
+       empty = urb_list_empty(epid);
+       urb_list_add(urb, epid);
+       spin_unlock_irqrestore(&urb_list_lock, flags);
+
+       dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d",
+                (unsigned long)urb, empty ? "empty" : "", epid);
+
+       /* Mark the urb as being in progress. */
+       urb->status = -EINPROGRESS;
+
+       /* Setup the hcpriv data. */
+       urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+       assert(urb_priv != NULL);
+       /* This sets rx_offset to 0. */
+       memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+       urb_priv->urb_state = NOT_STARTED;
+       urb->hcpriv = urb_priv;
+
+       if (empty) {
+               etrax_usb_add_to_ctrl_sb_list(urb, epid);
+       }
+
+       DBFEXIT;
+
+       return 0;
+}
+
+static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid)
+{
+       USB_SB_Desc_t *sb_desc_setup;
+       USB_SB_Desc_t *sb_desc_data;
+       USB_SB_Desc_t *sb_desc_status;
+
+       etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+
+       unsigned long flags;
+       char maxlen;
+
+       DBFENTER;
+
+       maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+
+       sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+       assert(sb_desc_setup != NULL);
+       sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+       assert(sb_desc_status != NULL);
+
+       /* Initialize the mandatory setup SB descriptor (used only in control transfers) */
+       sb_desc_setup->sw_len = 8;
+       sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                 IO_STATE(USB_SB_command, tt, setup) |
+                                 IO_STATE(USB_SB_command, full, yes) |
+                                 IO_STATE(USB_SB_command, eot, yes));
+
+       sb_desc_setup->buf = virt_to_phys(urb->setup_packet);
+
+       if (usb_pipeout(urb->pipe)) {
+               dbg_ctrl("Transfer for epid %d is OUT", epid);
+
+               /* If this Control OUT transfer has an optional data stage we add an OUT token
+                  before the mandatory IN (status) token, hence the reordered SB list */
+
+               sb_desc_setup->next = virt_to_phys(sb_desc_status);
+               if (urb->transfer_buffer) {
+
+                       dbg_ctrl("This OUT transfer has an extra data stage");
+
+                       sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+                       assert(sb_desc_data != NULL);
+
+                       sb_desc_setup->next = virt_to_phys(sb_desc_data);
+
+                       sb_desc_data->sw_len = urb->transfer_buffer_length;
+                       sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) |
+                                                IO_STATE(USB_SB_command, full, yes) |
+                                                IO_STATE(USB_SB_command, eot, yes));
+                       sb_desc_data->buf = virt_to_phys(urb->transfer_buffer);
+                       sb_desc_data->next = virt_to_phys(sb_desc_status);
+               }
+
+               sb_desc_status->sw_len = 1;
+               sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                          IO_STATE(USB_SB_command, tt, in) |
+                                          IO_STATE(USB_SB_command, eot, yes) |
+                                          IO_STATE(USB_SB_command, intr, yes) |
+                                          IO_STATE(USB_SB_command, eol, yes));
+
+               sb_desc_status->buf = 0;
+               sb_desc_status->next = 0;
+
+       } else if (usb_pipein(urb->pipe)) {
+
+               dbg_ctrl("Transfer for epid %d is IN", epid);
+               dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length);
+               dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
+
+               sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+               assert(sb_desc_data != NULL);
+
+               sb_desc_setup->next = virt_to_phys(sb_desc_data);
+
+               sb_desc_data->sw_len = urb->transfer_buffer_length ?
+                       (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+               dbg_ctrl("sw_len got %d", sb_desc_data->sw_len);
+
+               sb_desc_data->command =
+                       (IO_FIELD(USB_SB_command, rem,
+                                 urb->transfer_buffer_length % maxlen) |
+                        IO_STATE(USB_SB_command, tt, in) |
+                        IO_STATE(USB_SB_command, eot, yes));
+
+               sb_desc_data->buf = 0;
+               sb_desc_data->next = virt_to_phys(sb_desc_status);
+
+               /* Read comment at zout_buffer declaration for an explanation to this. */
+               sb_desc_status->sw_len = 1;
+               sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                          IO_STATE(USB_SB_command, tt, zout) |
+                                          IO_STATE(USB_SB_command, full, yes) |
+                                          IO_STATE(USB_SB_command, eot, yes) |
+                                          IO_STATE(USB_SB_command, intr, yes) |
+                                          IO_STATE(USB_SB_command, eol, yes));
+
+               sb_desc_status->buf = virt_to_phys(&zout_buffer[0]);
+               sb_desc_status->next = 0;
+       }
+
+       urb_priv->first_sb = sb_desc_setup;
+       urb_priv->last_sb = sb_desc_status;
+       urb_priv->epid = epid;
+
+       urb_priv->urb_state = STARTED;
+
+       /* Reset toggle bits and reset error count, remember to di and ei */
+       /* Warning: it is possible that this locking doesn't work with bottom-halves */
+
+       save_flags(flags);
+       cli();
+
+       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+       nop();
+       if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
+               panic("Hold was set in %s", __FUNCTION__);
+       }
+
+
+       /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits
+          are set to a specific value. Why the difference? Read "Transfer and Toggle Bits
+          in Designer's Reference, p. 8 - 11. */
+       *R_USB_EPT_DATA &=
+               ~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
+                 IO_MASK(R_USB_EPT_DATA, error_count_out) |
+                 IO_MASK(R_USB_EPT_DATA, t_in) |
+                 IO_MASK(R_USB_EPT_DATA, t_out));
+
+       /* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now
+          (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */
+       restore_flags(flags);
+
+       /* Assert that the EP descriptor is disabled. */
+       assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+
+       /* Set up and enable the EP descriptor. */
+       TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup);
+       TxCtrlEPList[epid].hw_len = 0;
+       TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+       /* We start the DMA sub channel without checking if it's running or not, because:
+          1) If it's already running, issuing the start command is a nop.
+          2) We avoid a test-and-set race condition. */
+       *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
+
+       DBFEXIT;
+}
+
+static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status)
+{
+       etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+       int epid = urb_priv->epid;
+
+       DBFENTER;
+
+       if (status)
+               warn("Completing ctrl urb with status %d.", status);
+
+       dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb);
+
+       /* Remove this urb from the list. */
+       urb_list_del(urb, epid);
+
+       /* For an IN pipe, we always set the actual length, regardless of whether there was
+          an error or not (which means the device driver can use the data if it wants to). */
+       if (usb_pipein(urb->pipe)) {
+               urb->actual_length = urb_priv->rx_offset;
+       }
+
+       /* FIXME: Is there something of the things below we shouldn't do if there was an error?
+          Like, maybe we shouldn't insert more traffic. */
+
+       /* Remember to free the SBs. */
+       etrax_remove_from_sb_list(urb);
+       kfree(urb_priv);
+       urb->hcpriv = 0;
+
+       /* If there are any more urbs in the list we'd better start sending. */
+       if (!urb_list_empty(epid)) {
+               struct urb *new_urb;
+
+               /* Get the first urb. */
+               new_urb = urb_list_first(epid);
+               assert(new_urb);
+
+               dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb);
+
+               etrax_usb_add_to_ctrl_sb_list(new_urb, epid);
+       }
+
+       urb->status = status;
+
+       /* We let any non-zero status from the layer above have precedence. */
+       if (status == 0) {
+               /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
+                  is to be treated as an error. */
+               if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+                       if (usb_pipein(urb->pipe) &&
+                           (urb->actual_length !=
+                            usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+                               urb->status = -EREMOTEIO;
+                       }
+               }
+       }
+
+       if (urb->complete) {
+               urb->complete(urb, NULL);
+       }
+
+       if (urb_list_empty(epid)) {
+               /* No more traffic. Time to clean up. */
+               etrax_usb_free_epid(epid);
+               /* Must set sub pointer to 0. */
+               dbg_ctrl("No ctrl for epid %d", epid);
+               TxCtrlEPList[epid].sub = 0;
+       }
+
+       DBFEXIT;
+}
+
+static int etrax_usb_submit_intr_urb(struct urb *urb)
+{
+
+       int epid;
+
+       DBFENTER;
+
+       if (usb_pipeout(urb->pipe)) {
+               /* Unsupported transfer type.
+                  We don't support interrupt out traffic. (If we do, we can't support
+                  intervals for neither in or out traffic, but are forced to schedule all
+                  interrupt traffic in one frame.) */
+               return -EINVAL;
+       }
+
+       epid = etrax_usb_setup_epid(urb);
+       if (epid == -1) {
+               DBFEXIT;
+               return -ENOMEM;
+       }
+
+       if (!urb_list_empty(epid)) {
+               /* There is already a queued urb for this endpoint. */
+               etrax_usb_free_epid(epid);
+               return -ENXIO;
+       }
+
+       urb->status = -EINPROGRESS;
+
+       dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid);
+
+       urb_list_add(urb, epid);
+       etrax_usb_add_to_intr_sb_list(urb, epid);
+
+       return 0;
+
+       DBFEXIT;
+}
+
+static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid)
+{
+
+       volatile USB_EP_Desc_t *tmp_ep;
+       volatile USB_EP_Desc_t *first_ep;
+
+       char maxlen;
+       int interval;
+       int i;
+
+       etrax_urb_priv_t *urb_priv;
+
+       DBFENTER;
+
+       maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+       interval = urb->interval;
+
+       urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+       assert(urb_priv != NULL);
+       memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+       urb->hcpriv = urb_priv;
+
+       first_ep = &TxIntrEPList[0];
+
+       /* Round of the interval to 2^n, it is obvious that this code favours
+          smaller numbers, but that is actually a good thing */
+       /* FIXME: The "rounding error" for larger intervals will be quite
+          large. For in traffic this shouldn't be a problem since it will only
+          mean that we "poll" more often. */
+       for (i = 0; interval; i++) {
+               interval = interval >> 1;
+       }
+       interval = 1 << (i - 1);
+
+       dbg_intr("Interval rounded to %d", interval);
+
+       tmp_ep = first_ep;
+       i = 0;
+       do {
+               if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
+                       if ((i % interval) == 0) {
+                               /* Insert the traffic ep after tmp_ep */
+                               USB_EP_Desc_t *ep_desc;
+                               USB_SB_Desc_t *sb_desc;
+
+                               dbg_intr("Inserting EP for epid %d", epid);
+
+                               ep_desc = (USB_EP_Desc_t *)
+                                       kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+                               sb_desc = (USB_SB_Desc_t *)
+                                       kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+                               assert(ep_desc != NULL);
+                               CHECK_ALIGN(ep_desc);
+                               assert(sb_desc != NULL);
+
+                               ep_desc->sub = virt_to_phys(sb_desc);
+                               ep_desc->hw_len = 0;
+                               ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) |
+                                                   IO_STATE(USB_EP_command, enable, yes));
+
+
+                               /* Round upwards the number of packets of size maxlen
+                                  that this SB descriptor should receive. */
+                               sb_desc->sw_len = urb->transfer_buffer_length ?
+                                       (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+                               sb_desc->next = 0;
+                               sb_desc->buf = 0;
+                               sb_desc->command =
+                                       (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) |
+                                        IO_STATE(USB_SB_command, tt, in) |
+                                        IO_STATE(USB_SB_command, eot, yes) |
+                                        IO_STATE(USB_SB_command, eol, yes));
+
+                               ep_desc->next = tmp_ep->next;
+                               tmp_ep->next = virt_to_phys(ep_desc);
+                       }
+                       i++;
+               }
+               tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
+       } while (tmp_ep != first_ep);
+
+
+       /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */
+       urb_priv->epid = epid;
+
+       /* We start the DMA sub channel without checking if it's running or not, because:
+          1) If it's already running, issuing the start command is a nop.
+          2) We avoid a test-and-set race condition. */
+       *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+
+       DBFEXIT;
+}
+
+
+
+static void etrax_usb_complete_intr_urb(struct urb *urb, int status)
+{
+       etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+       int epid = urb_priv->epid;
+
+       DBFENTER;
+
+       if (status)
+               warn("Completing intr urb with status %d.", status);
+
+       dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb);
+
+       urb->status = status;
+       urb->actual_length = urb_priv->rx_offset;
+
+       dbg_intr("interrupt urb->actual_length = %d", urb->actual_length);
+
+       /* We let any non-zero status from the layer above have precedence. */
+       if (status == 0) {
+               /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
+                  is to be treated as an error. */
+               if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+                       if (urb->actual_length !=
+                           usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+                               urb->status = -EREMOTEIO;
+                       }
+               }
+       }
+
+       /* The driver will resubmit the URB so we need to remove it first */
+        etrax_usb_unlink_urb(urb, 0);
+       if (urb->complete) {
+               urb->complete(urb, NULL);
+       }
+
+       DBFEXIT;
+}
+
+
+static int etrax_usb_submit_isoc_urb(struct urb *urb)
+{
+       int epid;
+       unsigned long flags;
+
+       DBFENTER;
+
+       dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb);
+
+       /* Epid allocation, empty check and list add must be protected.
+          Read about this in etrax_usb_submit_ctrl_urb. */
+
+       spin_lock_irqsave(&urb_list_lock, flags);
+       /* Is there an active epid for this urb ? */
+       epid = etrax_usb_setup_epid(urb);
+       if (epid == -1) {
+               DBFEXIT;
+               spin_unlock_irqrestore(&urb_list_lock, flags);
+               return -ENOMEM;
+       }
+
+       /* Ok, now we got valid endpoint, lets insert some traffic */
+
+       urb->status = -EINPROGRESS;
+
+       /* Find the last urb in the URB_List and add this urb after that one.
+          Also add the traffic, that is do an etrax_usb_add_to_isoc_sb_list.  This
+          is important to make this in "real time" since isochronous traffic is
+          time sensitive. */
+
+       dbg_isoc("Adding isoc urb to (possibly empty) list");
+       urb_list_add(urb, epid);
+       etrax_usb_add_to_isoc_sb_list(urb, epid);
+       spin_unlock_irqrestore(&urb_list_lock, flags);
+
+       DBFEXIT;
+
+       return 0;
+}
+
+static void etrax_usb_check_error_isoc_ep(const int epid)
+{
+       unsigned long int flags;
+       int error_code;
+       __u32 r_usb_ept_data;
+
+       /* We can't read R_USB_EPID_ATTN here since it would clear the iso_eof,
+          bulk_eot and epid_attn interrupts.  So we just check the status of
+          the epid without testing if for it in R_USB_EPID_ATTN. */
+
+
+       save_flags(flags);
+       cli();
+       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+       nop();
+       /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
+          registers, they are located at the same address and are of the same size.
+          In other words, this read should be ok for isoc also. */
+       r_usb_ept_data = *R_USB_EPT_DATA;
+       restore_flags(flags);
+
+       error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
+
+       if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+               warn("Hold was set for epid %d.", epid);
+               return;
+       }
+
+       if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, no_error)) {
+
+               /* This indicates that the SB list of the ept was completed before
+                  new data was appended to it.  This is not an error, but indicates
+                  large system or USB load and could possibly cause trouble for
+                  very timing sensitive USB device drivers so we log it.
+               */
+               info("Isoc. epid %d disabled with no error", epid);
+               return;
+
+       } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, stall)) {
+               /* Not really a protocol error, just says that the endpoint gave
+                  a stall response. Note that error_code cannot be stall for isoc. */
+               panic("Isoc traffic cannot stall");
+
+       } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, bus_error)) {
+               /* Two devices responded to a transaction request. Must be resolved
+                  by software. FIXME: Reset ports? */
+               panic("Bus error for epid %d."
+                     " Two devices responded to transaction request",
+                     epid);
+
+       } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
+               /* DMA overrun or underrun. */
+               warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+
+               /* It seems that error_code = buffer_error in
+                  R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
+                  are the same error. */
+       }
+}
+
+
+static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid)
+{
+
+       int i = 0;
+
+       etrax_urb_priv_t *urb_priv;
+       USB_SB_Desc_t *prev_sb_desc,  *next_sb_desc, *temp_sb_desc;
+
+       DBFENTER;
+
+       prev_sb_desc = next_sb_desc = temp_sb_desc = NULL;
+
+       urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC);
+       assert(urb_priv != NULL);
+       memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+
+       urb->hcpriv = urb_priv;
+       urb_priv->epid = epid;
+
+       if (usb_pipeout(urb->pipe)) {
+
+               if (urb->number_of_packets == 0) panic("etrax_usb_add_to_isoc_sb_list 0 packets\n");
+
+               dbg_isoc("Transfer for epid %d is OUT", epid);
+               dbg_isoc("%d packets in URB", urb->number_of_packets);
+
+               /* Create one SB descriptor for each packet and link them together. */
+               for (i = 0; i < urb->number_of_packets; i++) {
+                       if (!urb->iso_frame_desc[i].length)
+                               continue;
+
+                       next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_ATOMIC);
+                       assert(next_sb_desc != NULL);
+
+                       if (urb->iso_frame_desc[i].length > 0) {
+
+                               next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) |
+                                                        IO_STATE(USB_SB_command, eot, yes));
+
+                               next_sb_desc->sw_len = urb->iso_frame_desc[i].length;
+                               next_sb_desc->buf = virt_to_phys((char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset);
+
+                               /* Check if full length transfer. */
+                               if (urb->iso_frame_desc[i].length ==
+                                   usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+                                       next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+                               }
+                       } else {
+                               dbg_isoc("zero len packet");
+                               next_sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                                        IO_STATE(USB_SB_command, tt, zout) |
+                                                        IO_STATE(USB_SB_command, eot, yes) |
+                                                        IO_STATE(USB_SB_command, full, yes));
+
+                               next_sb_desc->sw_len = 1;
+                               next_sb_desc->buf = virt_to_phys(&zout_buffer[0]);
+                       }
+
+                       /* First SB descriptor that belongs to this urb */
+                       if (i == 0)
+                               urb_priv->first_sb = next_sb_desc;
+                       else
+                               prev_sb_desc->next = virt_to_phys(next_sb_desc);
+
+                       prev_sb_desc = next_sb_desc;
+               }
+
+               next_sb_desc->command |= (IO_STATE(USB_SB_command, intr, yes) |
+                                         IO_STATE(USB_SB_command, eol, yes));
+               next_sb_desc->next = 0;
+               urb_priv->last_sb = next_sb_desc;
+
+       } else if (usb_pipein(urb->pipe)) {
+
+               dbg_isoc("Transfer for epid %d is IN", epid);
+               dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length);
+               dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length);
+
+               /* Note that in descriptors for periodic traffic are not consumed. This means that
+                  the USB controller never propagates in the SB list. In other words, if there already
+                  is an SB descriptor in the list for this EP we don't have to do anything. */
+               if (TxIsocEPList[epid].sub == 0) {
+                       dbg_isoc("Isoc traffic not already running, allocating SB");
+
+                       next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_ATOMIC);
+                       assert(next_sb_desc != NULL);
+
+                       next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) |
+                                                IO_STATE(USB_SB_command, eot, yes) |
+                                                IO_STATE(USB_SB_command, eol, yes));
+
+                       next_sb_desc->next = 0;
+                       next_sb_desc->sw_len = 1; /* Actual number of packets is not relevant
+                                                    for periodic in traffic as long as it is more
+                                                    than zero.  Set to 1 always. */
+                       next_sb_desc->buf = 0;
+
+                       /* The rem field is don't care for isoc traffic, so we don't set it. */
+
+                       /* Only one SB descriptor that belongs to this urb. */
+                       urb_priv->first_sb = next_sb_desc;
+                       urb_priv->last_sb = next_sb_desc;
+
+               } else {
+
+                       dbg_isoc("Isoc traffic already running, just setting first/last_sb");
+
+                       /* Each EP for isoc in will have only one SB descriptor, setup when submitting the
+                          already active urb. Note that even though we may have several first_sb/last_sb
+                          pointing at the same SB descriptor, they are freed only once (when the list has
+                          become empty). */
+                       urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub);
+                       urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub);
+                       return;
+               }
+
+       }
+
+       /* Find the spot to insert this urb and add it. */
+       if (TxIsocEPList[epid].sub == 0) {
+               /* First SB descriptor inserted in this list (in or out). */
+               dbg_isoc("Inserting SB desc first in list");
+               TxIsocEPList[epid].hw_len = 0;
+               TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
+
+       } else {
+               /* Isochronous traffic is already running, insert new traffic last (only out). */
+               dbg_isoc("Inserting SB desc last in list");
+               temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
+               while ((temp_sb_desc->command & IO_MASK(USB_SB_command, eol)) !=
+                      IO_STATE(USB_SB_command, eol, yes)) {
+                       assert(temp_sb_desc->next);
+                       temp_sb_desc = phys_to_virt(temp_sb_desc->next);
+               }
+               dbg_isoc("Appending list on desc 0x%p", temp_sb_desc);
+
+               /* Next pointer must be set before eol is removed. */
+               temp_sb_desc->next = virt_to_phys(urb_priv->first_sb);
+               /* Clear the previous end of list flag since there is a new in the
+                  added SB descriptor list. */
+               temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol);
+
+               if (!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
+                       /* 8.8.5 in Designer's Reference says we should check for and correct
+                          any errors in the EP here.  That should not be necessary if epid_attn
+                          is handled correctly, so we assume all is ok. */
+                       dbg_isoc("EP disabled");
+                       etrax_usb_check_error_isoc_ep(epid);
+
+                       /* The SB list was exhausted. */
+                       if (virt_to_phys(urb_priv->last_sb) != TxIsocEPList[epid].sub) {
+                               /* The new sublist did not get processed before the EP was
+                                  disabled.  Setup the EP again. */
+                               dbg_isoc("Set EP sub to new list");
+                               TxIsocEPList[epid].hw_len = 0;
+                               TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
+                       }
+               }
+       }
+
+       if (urb->transfer_flags & URB_ISO_ASAP) {
+               /* The isoc transfer should be started as soon as possible. The start_frame
+                  field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER
+                  with a USB Chief trace shows that the first isoc IN token is sent 2 frames
+                  later. I'm not sure how this affects usage of the start_frame field by the
+                  device driver, or how it affects things when USB_ISO_ASAP is not set, so
+                  therefore there's no compensation for the 2 frame "lag" here. */
+               urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff);
+               TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+               urb_priv->urb_state = STARTED;
+               dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame);
+       } else {
+               /* Not started yet. */
+               urb_priv->urb_state = NOT_STARTED;
+               dbg_isoc("urb_priv->urb_state set to NOT_STARTED");
+       }
+
+       /* We start the DMA sub channel without checking if it's running or not, because:
+         1) If it's already running, issuing the start command is a nop.
+         2) We avoid a test-and-set race condition. */
+       *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
+
+       DBFEXIT;
+}
+
+static void etrax_usb_complete_isoc_urb(struct urb *urb, int status)
+{
+       etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+       int epid = urb_priv->epid;
+       int auto_resubmit = 0;
+
+       DBFENTER;
+       dbg_isoc("complete urb 0x%p, status %d", urb, status);
+
+       if (status)
+               warn("Completing isoc urb with status %d.", status);
+
+       if (usb_pipein(urb->pipe)) {
+               int i;
+
+               /* Make that all isoc packets have status and length set before
+                  completing the urb. */
+               for (i = urb_priv->isoc_packet_counter; i < urb->number_of_packets; i++) {
+                       urb->iso_frame_desc[i].actual_length = 0;
+                       urb->iso_frame_desc[i].status = -EPROTO;
+               }
+
+               urb_list_del(urb, epid);
+
+               if (!list_empty(&urb_list[epid])) {
+                       ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
+               } else {
+                       unsigned long int flags;
+                       if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                               /* The EP was enabled, disable it and wait. */
+                               TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+
+                               /* Ah, the luxury of busy-wait. */
+                               while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
+                       }
+
+                       etrax_remove_from_sb_list(urb);
+                       TxIsocEPList[epid].sub = 0;
+                       TxIsocEPList[epid].hw_len = 0;
+
+                       save_flags(flags);
+                       cli();
+                       etrax_usb_free_epid(epid);
+                       restore_flags(flags);
+               }
+
+               urb->hcpriv = 0;
+               kfree(urb_priv);
+
+               /* Release allocated bandwidth. */
+               usb_release_bandwidth(urb->dev, urb, 0);
+       } else if (usb_pipeout(urb->pipe)) {
+               int freed_descr;
+
+               dbg_isoc("Isoc out urb complete 0x%p", urb);
+
+               /* Update the urb list. */
+               urb_list_del(urb, epid);
+
+               freed_descr = etrax_remove_from_sb_list(urb);
+               dbg_isoc("freed %d descriptors of %d packets", freed_descr, urb->number_of_packets);
+               assert(freed_descr == urb->number_of_packets);
+               urb->hcpriv = 0;
+               kfree(urb_priv);
+
+               /* Release allocated bandwidth. */
+               usb_release_bandwidth(urb->dev, urb, 0);
+       }
+
+       urb->status = status;
+       if (urb->complete) {
+               urb->complete(urb, NULL);
+       }
+
+       if (auto_resubmit) {
+               /* Check that urb was not unlinked by the complete callback. */
+               if (__urb_list_entry(urb, epid)) {
+                       /* Move this one down the list. */
+                       urb_list_move_last(urb, epid);
+
+                       /* Mark the now first urb as started (may already be). */
+                       ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
+
+                       /* Must set this to 0 since this urb is still active after
+                          completion. */
+                       urb_priv->isoc_packet_counter = 0;
+               } else {
+                       warn("(ISOC) automatic resubmit urb 0x%p removed by complete.", urb);
+               }
+       }
+
+       DBFEXIT;
+}
+
+static void etrax_usb_complete_urb(struct urb *urb, int status)
+{
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_BULK:
+               etrax_usb_complete_bulk_urb(urb, status);
+               break;
+       case PIPE_CONTROL:
+               etrax_usb_complete_ctrl_urb(urb, status);
+               break;
+       case PIPE_INTERRUPT:
+               etrax_usb_complete_intr_urb(urb, status);
+               break;
+       case PIPE_ISOCHRONOUS:
+               etrax_usb_complete_isoc_urb(urb, status);
+               break;
+       default:
+               err("Unknown pipetype");
+       }
+}
+
+
+
+static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs)
+{
+       usb_interrupt_registers_t *reg;
+       unsigned long flags;
+       __u32 irq_mask;
+       __u8 status;
+       __u32 epid_attn;
+       __u16 port_status_1;
+       __u16 port_status_2;
+       __u32 fm_number;
+
+       DBFENTER;
+
+       /* Read critical registers into local variables, do kmalloc afterwards. */
+       save_flags(flags);
+       cli();
+
+       irq_mask = *R_USB_IRQ_MASK_READ;
+       /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS
+          must be read before R_USB_EPID_ATTN since reading the latter clears the
+          ourun and perror fields of R_USB_STATUS. */
+       status = *R_USB_STATUS;
+
+       /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */
+       epid_attn = *R_USB_EPID_ATTN;
+
+       /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the
+          port_status interrupt. */
+       port_status_1 = *R_USB_RH_PORT_STATUS_1;
+       port_status_2 = *R_USB_RH_PORT_STATUS_2;
+
+       /* Reading R_USB_FM_NUMBER clears the sof interrupt. */
+       /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */
+       fm_number = *R_USB_FM_NUMBER;
+
+       restore_flags(flags);
+
+       reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, SLAB_ATOMIC);
+
+       assert(reg != NULL);
+
+       reg->hc = (etrax_hc_t *)vhc;
+
+       /* Now put register values into kmalloc'd area. */
+       reg->r_usb_irq_mask_read = irq_mask;
+       reg->r_usb_status = status;
+       reg->r_usb_epid_attn = epid_attn;
+       reg->r_usb_rh_port_status_1 = port_status_1;
+       reg->r_usb_rh_port_status_2 = port_status_2;
+       reg->r_usb_fm_number = fm_number;
+
+        INIT_WORK(&reg->usb_bh, etrax_usb_hc_interrupt_bottom_half, reg);
+        schedule_work(&reg->usb_bh);
+
+       DBFEXIT;
+
+        return IRQ_HANDLED;
+}
+
+static void etrax_usb_hc_interrupt_bottom_half(void *data)
+{
+       usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data;
+       __u32 irq_mask = reg->r_usb_irq_mask_read;
+
+       DBFENTER;
+
+       /* Interrupts are handled in order of priority. */
+       if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) {
+               etrax_usb_hc_epid_attn_interrupt(reg);
+       }
+       if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
+               etrax_usb_hc_port_status_interrupt(reg);
+       }
+       if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) {
+               etrax_usb_hc_ctl_status_interrupt(reg);
+       }
+       if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) {
+               etrax_usb_hc_isoc_eof_interrupt();
+       }
+       if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) {
+               /* Update/restart the bulk start timer since obviously the channel is running. */
+               mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+               /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */
+               mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+
+               etrax_usb_hc_bulk_eot_interrupt(0);
+       }
+
+       kmem_cache_free(top_half_reg_cache, reg);
+
+       DBFEXIT;
+}
+
+
+void etrax_usb_hc_isoc_eof_interrupt(void)
+{
+       struct urb *urb;
+       etrax_urb_priv_t *urb_priv;
+       int epid;
+       unsigned long flags;
+
+       DBFENTER;
+
+       /* Do not check the invalid epid (it has a valid sub pointer). */
+       for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+
+               /* Do not check the invalid epid (it has a valid sub pointer). */
+               if ((epid == DUMMY_EPID) || (epid == INVALID_EPID))
+                       continue;
+
+               /* Disable interrupts to block the isoc out descriptor interrupt handler
+                  from being called while the isoc EPID list is being checked.
+               */
+               save_flags(flags);
+               cli();
+
+               if (TxIsocEPList[epid].sub == 0) {
+                       /* Nothing here to see. */
+                       restore_flags(flags);
+                       continue;
+               }
+
+               /* Get the first urb (if any). */
+               urb = urb_list_first(epid);
+               if (urb == 0) {
+                       warn("Ignoring NULL urb");
+                       restore_flags(flags);
+                       continue;
+               }
+               if (usb_pipein(urb->pipe)) {
+
+                       /* Sanity check. */
+                       assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
+
+                       urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+                       assert(urb_priv);
+
+                       if (urb_priv->urb_state == NOT_STARTED) {
+
+                               /* If ASAP is not set and urb->start_frame is the current frame,
+                                  start the transfer. */
+                               if (!(urb->transfer_flags & URB_ISO_ASAP) &&
+                                   (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) {
+
+                                       dbg_isoc("Enabling isoc IN EP descr for epid %d", epid);
+                                       TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+                                       /* This urb is now active. */
+                                       urb_priv->urb_state = STARTED;
+                                       continue;
+                               }
+                       }
+               }
+               restore_flags(flags);
+       }
+
+       DBFEXIT;
+
+}
+
+void etrax_usb_hc_bulk_eot_interrupt(int timer_induced)
+{
+       int epid;
+
+       /* The technique is to run one urb at a time, wait for the eot interrupt at which
+          point the EP descriptor has been disabled. */
+
+       DBFENTER;
+       dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : "");
+
+       for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+
+               if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) &&
+                   (TxBulkEPList[epid].sub != 0)) {
+
+                       struct urb *urb;
+                       etrax_urb_priv_t *urb_priv;
+                       unsigned long flags;
+                       __u32 r_usb_ept_data;
+
+                       /* Found a disabled EP descriptor which has a non-null sub pointer.
+                          Verify that this ctrl EP descriptor got disabled no errors.
+                          FIXME: Necessary to check error_code? */
+                       dbg_bulk("for epid %d?", epid);
+
+                       /* Get the first urb. */
+                       urb = urb_list_first(epid);
+
+                       /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of
+                          wrong unlinking? */
+                       if (!urb) {
+                               warn("NULL urb for epid %d", epid);
+                               continue;
+                       }
+
+                       assert(urb);
+                       urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+                       assert(urb_priv);
+
+                       /* Sanity checks. */
+                       assert(usb_pipetype(urb->pipe) == PIPE_BULK);
+                       if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) {
+                               err("bulk endpoint got disabled before reaching last sb");
+                       }
+
+                       /* For bulk IN traffic, there seems to be a race condition between
+                          between the bulk eot and eop interrupts, or rather an uncertainty regarding
+                          the order in which they happen. Normally we expect the eop interrupt from
+                          DMA channel 9 to happen before the eot interrupt.
+
+                          Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */
+
+                       if (usb_pipein(urb->pipe)) {
+                               dbg_bulk("in urb, continuing");
+                               continue;
+                       }
+
+                       save_flags(flags);
+                       cli();
+                       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+                       nop();
+                       r_usb_ept_data = *R_USB_EPT_DATA;
+                       restore_flags(flags);
+
+                       if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
+                           IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+                               /* This means that the endpoint has no error, is disabled
+                                  and had inserted traffic, i.e. transfer successfully completed. */
+                               etrax_usb_complete_bulk_urb(urb, 0);
+                       } else {
+                               /* Shouldn't happen. We expect errors to be caught by epid attention. */
+                               err("Found disabled bulk EP desc, error_code != no_error");
+                       }
+               }
+       }
+
+       /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer.
+          However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may
+          not.  Also, we might find two disabled EPs when handling an eot interrupt, and then find
+          none the next time. */
+
+       DBFEXIT;
+
+}
+
+void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg)
+{
+       /* This function handles the epid attention interrupt.  There are a variety of reasons
+          for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details):
+
+          invalid ep_id  - Invalid epid in an EP (EP disabled).
+          stall          - Not strictly an error condition (EP disabled).
+          3rd error      - Three successive transaction errors  (EP disabled).
+          buffer ourun   - Buffer overrun or underrun (EP disabled).
+          past eof1      - Intr or isoc transaction proceeds past EOF1.
+          near eof       - Intr or isoc transaction would not fit inside the frame.
+          zout transfer  - If zout transfer for a bulk endpoint (EP disabled).
+          setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */
+
+       int epid;
+
+
+       DBFENTER;
+
+       assert(reg != NULL);
+
+       /* Note that we loop through all epids. We still want to catch errors for
+          the invalid one, even though we might handle them differently. */
+       for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+
+               if (test_bit(epid, (void *)&reg->r_usb_epid_attn)) {
+
+                       struct urb *urb;
+                       __u32 r_usb_ept_data;
+                       unsigned long flags;
+                       int error_code;
+
+                       save_flags(flags);
+                       cli();
+                       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+                       nop();
+                       /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
+                          registers, they are located at the same address and are of the same size.
+                          In other words, this read should be ok for isoc also. */
+                       r_usb_ept_data = *R_USB_EPT_DATA;
+                       restore_flags(flags);
+
+                       /* First some sanity checks. */
+                       if (epid == INVALID_EPID) {
+                               /* FIXME: What if it became disabled? Could seriously hurt interrupt
+                                  traffic. (Use do_intr_recover.) */
+                               warn("Got epid_attn for INVALID_EPID (%d).", epid);
+                               err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+                               err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+                               continue;
+                       } else  if (epid == DUMMY_EPID) {
+                               /* We definitely don't care about these ones. Besides, they are
+                                  always disabled, so any possible disabling caused by the
+                                  epid attention interrupt is irrelevant. */
+                               warn("Got epid_attn for DUMMY_EPID (%d).", epid);
+                               continue;
+                       }
+
+                       /* Get the first urb in the urb list for this epid. We blatantly assume
+                          that only the first urb could have caused the epid attention.
+                          (For bulk and ctrl, only one urb is active at any one time. For intr
+                          and isoc we remove them once they are completed.) */
+                       urb = urb_list_first(epid);
+
+                       if (urb == NULL) {
+                               err("Got epid_attn for epid %i with no urb.", epid);
+                               err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+                               err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+                               continue;
+                       }
+
+                       switch (usb_pipetype(urb->pipe)) {
+                       case PIPE_BULK:
+                               warn("Got epid attn for bulk endpoint, epid %d", epid);
+                               break;
+                       case PIPE_CONTROL:
+                               warn("Got epid attn for control endpoint, epid %d", epid);
+                               break;
+                       case PIPE_INTERRUPT:
+                               warn("Got epid attn for interrupt endpoint, epid %d", epid);
+                               break;
+                       case PIPE_ISOCHRONOUS:
+                               warn("Got epid attn for isochronous endpoint, epid %d", epid);
+                               break;
+                       }
+
+                       if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
+                               if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+                                       warn("Hold was set for epid %d.", epid);
+                                       continue;
+                               }
+                       }
+
+                       /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and
+                          R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */
+                       if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                               error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
+                       } else {
+                               error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data);
+                       }
+
+                       /* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */
+                       if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+
+                               /* Isoc traffic doesn't have error_count_in/error_count_out. */
+                               if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) &&
+                                   (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 ||
+                                    IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) {
+                                       /* 3rd error. */
+                                       warn("3rd error for epid %i", epid);
+                                       etrax_usb_complete_urb(urb, -EPROTO);
+
+                               } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+
+                                       warn("Perror for epid %d", epid);
+
+                                       if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
+                                               /* invalid ep_id */
+                                               panic("Perror because of invalid epid."
+                                                     " Deconfigured too early?");
+                                       } else {
+                                               /* past eof1, near eof, zout transfer, setup transfer */
+
+                                               /* Dump the urb and the relevant EP descriptor list. */
+
+                                               __dump_urb(urb);
+                                               __dump_ept_data(epid);
+                                               __dump_ep_list(usb_pipetype(urb->pipe));
+
+                                               panic("Something wrong with DMA descriptor contents."
+                                                     " Too much traffic inserted?");
+                                       }
+                               } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+                                       /* buffer ourun */
+                                       panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+                               }
+
+                       } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
+                               /* Not really a protocol error, just says that the endpoint gave
+                                  a stall response. Note that error_code cannot be stall for isoc. */
+                               if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                                       panic("Isoc traffic cannot stall");
+                               }
+
+                               warn("Stall for epid %d", epid);
+                               etrax_usb_complete_urb(urb, -EPIPE);
+
+                       } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
+                               /* Two devices responded to a transaction request. Must be resolved
+                                  by software. FIXME: Reset ports? */
+                               panic("Bus error for epid %d."
+                                     " Two devices responded to transaction request",
+                                     epid);
+
+                       } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
+                               /* DMA overrun or underrun. */
+                               warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+
+                               /* It seems that error_code = buffer_error in
+                                  R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
+                                  are the same error. */
+                               etrax_usb_complete_urb(urb, -EPROTO);
+                       }
+               }
+       }
+
+       DBFEXIT;
+
+}
+
+void etrax_usb_bulk_start_timer_func(unsigned long dummy)
+{
+
+       /* We might enable an EP descriptor behind the current DMA position when it's about
+          to decide that there are no more bulk traffic and it should stop the bulk channel.
+          Therefore we periodically check if the bulk channel is stopped and there is an
+          enabled bulk EP descriptor, in which case we start the bulk channel. */
+       dbg_bulk("bulk_start_timer timed out.");
+
+       if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
+               int epid;
+
+               dbg_bulk("Bulk DMA channel not running.");
+
+               for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+                       if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                               dbg_bulk("Found enabled EP for epid %d, starting bulk channel.\n",
+                                        epid);
+                               *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+
+                               /* Restart the bulk eot timer since we just started the bulk channel. */
+                               mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+
+                               /* No need to search any further. */
+                               break;
+                       }
+               }
+       } else {
+               dbg_bulk("Bulk DMA channel running.");
+       }
+}
+
+void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg)
+{
+       etrax_hc_t *hc = reg->hc;
+       __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
+       __u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
+
+       DBFENTER;
+
+       /* The Etrax RH does not include a wPortChange register, so this has to be handled in software
+          (by saving the old port status value for comparison when the port status interrupt happens).
+          See section 11.16.2.6.2 in the USB 1.1 spec for details. */
+
+       dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1);
+       dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2);
+       dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1);
+       dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2);
+
+       /* C_PORT_CONNECTION is set on any transition. */
+       hc->rh.wPortChange_1 |=
+               ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
+                (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
+               (1 << RH_PORT_CONNECTION) : 0;
+
+       hc->rh.wPortChange_2 |=
+               ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
+                (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
+               (1 << RH_PORT_CONNECTION) : 0;
+
+       /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when
+          the port is disabled, not when it's enabled. */
+       hc->rh.wPortChange_1 |=
+               ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
+                && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
+               (1 << RH_PORT_ENABLE) : 0;
+
+       hc->rh.wPortChange_2 |=
+               ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
+                && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
+               (1 << RH_PORT_ENABLE) : 0;
+
+       /* C_PORT_SUSPEND is set to one when the device has transitioned out
+          of the suspended state, i.e. when suspend goes from one to zero. */
+       hc->rh.wPortChange_1 |=
+               ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND))
+                && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ?
+               (1 << RH_PORT_SUSPEND) : 0;
+
+       hc->rh.wPortChange_2 |=
+               ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND))
+                && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ?
+               (1 << RH_PORT_SUSPEND) : 0;
+
+
+       /* C_PORT_RESET is set when reset processing on this port is complete. */
+       hc->rh.wPortChange_1 |=
+               ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
+                && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ?
+               (1 << RH_PORT_RESET) : 0;
+
+       hc->rh.wPortChange_2 |=
+               ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
+                && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ?
+               (1 << RH_PORT_RESET) : 0;
+
+       /* Save the new values for next port status change. */
+       hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
+       hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;
+
+       dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1);
+       dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2);
+
+       DBFEXIT;
+
+}
+
+void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg)
+{
+       DBFENTER;
+
+       /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB
+          list for the corresponding epid? */
+       if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+               panic("USB controller got ourun.");
+       }
+       if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+
+               /* Before, etrax_usb_do_intr_recover was called on this epid if it was
+                  an interrupt pipe. I don't see how re-enabling all EP descriptors
+                  will help if there was a programming error. */
+               panic("USB controller got perror.");
+       }
+
+       if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) {
+               /* We should never operate in device mode. */
+               panic("USB controller in device mode.");
+       }
+
+       /* These if-statements could probably be nested. */
+       if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) {
+               info("USB controller in host mode.");
+       }
+       if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) {
+               info("USB controller started.");
+       }
+       if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) {
+               info("USB controller running.");
+       }
+
+       DBFEXIT;
+
+}
+
+
+static int etrax_rh_submit_urb(struct urb *urb)
+{
+       struct usb_device *usb_dev = urb->dev;
+       etrax_hc_t *hc = usb_dev->bus->hcpriv;
+       unsigned int pipe = urb->pipe;
+       struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+       void *data = urb->transfer_buffer;
+       int leni = urb->transfer_buffer_length;
+       int len = 0;
+       int stat = 0;
+
+       __u16 bmRType_bReq;
+       __u16 wValue;
+       __u16 wIndex;
+       __u16 wLength;
+
+       DBFENTER;
+
+       /* FIXME: What is this interrupt urb that is sent to the root hub? */
+       if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
+               dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval);
+               hc->rh.urb = urb;
+               hc->rh.send = 1;
+               /* FIXME: We could probably remove this line since it's done
+                  in etrax_rh_init_int_timer. (Don't remove it from
+                  etrax_rh_init_int_timer though.) */
+               hc->rh.interval = urb->interval;
+               etrax_rh_init_int_timer(urb);
+               DBFEXIT;
+
+               return 0;
+       }
+
+       bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
+       wValue = le16_to_cpu(cmd->wValue);
+       wIndex = le16_to_cpu(cmd->wIndex);
+       wLength = le16_to_cpu(cmd->wLength);
+
+       dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq);
+       dbg_rh("wValue       : 0x%04x (%d)", wValue, wValue);
+       dbg_rh("wIndex       : 0x%04x (%d)", wIndex, wIndex);
+       dbg_rh("wLength      : 0x%04x (%d)", wLength, wLength);
+
+       switch (bmRType_bReq) {
+
+               /* Request Destination:
+                  without flags: Device,
+                  RH_INTERFACE: interface,
+                  RH_ENDPOINT: endpoint,
+                  RH_CLASS means HUB here,
+                  RH_OTHER | RH_CLASS  almost ever means HUB_PORT here
+                */
+
+       case RH_GET_STATUS:
+               *(__u16 *) data = cpu_to_le16 (1);
+               OK (2);
+
+       case RH_GET_STATUS | RH_INTERFACE:
+               *(__u16 *) data = cpu_to_le16 (0);
+               OK (2);
+
+       case RH_GET_STATUS | RH_ENDPOINT:
+               *(__u16 *) data = cpu_to_le16 (0);
+               OK (2);
+
+       case RH_GET_STATUS | RH_CLASS:
+               *(__u32 *) data = cpu_to_le32 (0);
+               OK (4);         /* hub power ** */
+
+       case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+               if (wIndex == 1) {
+                       *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1);
+                       *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1);
+               } else if (wIndex == 2) {
+                       *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2);
+                       *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2);
+               } else {
+                       dbg_rh("RH_GET_STATUS whith invalid wIndex!");
+                       OK(0);
+               }
+
+               OK(4);
+
+       case RH_CLEAR_FEATURE | RH_ENDPOINT:
+               switch (wValue) {
+               case (RH_ENDPOINT_STALL):
+                       OK (0);
+               }
+               break;
+
+       case RH_CLEAR_FEATURE | RH_CLASS:
+               switch (wValue) {
+               case (RH_C_HUB_OVER_CURRENT):
+                       OK (0); /* hub power over current ** */
+               }
+               break;
+
+       case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+               switch (wValue) {
+               case (RH_PORT_ENABLE):
+                       if (wIndex == 1) {
+
+                               dbg_rh("trying to do disable port 1");
+
+                               *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes);
+
+                               while (hc->rh.prev_wPortStatus_1 &
+                                      IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes));
+                               *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
+                               dbg_rh("Port 1 is disabled");
+
+                       } else if (wIndex == 2) {
+
+                               dbg_rh("trying to do disable port 2");
+
+                               *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes);
+
+                               while (hc->rh.prev_wPortStatus_2 &
+                                      IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes));
+                               *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
+                               dbg_rh("Port 2 is disabled");
+
+                       } else {
+                               dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE "
+                                      "with invalid wIndex == %d!", wIndex);
+                       }
+
+                       OK (0);
+               case (RH_PORT_SUSPEND):
+                       /* Opposite to suspend should be resume, so we'll do a resume. */
+                       /* FIXME: USB 1.1, 11.16.2.2 says:
+                          "Clearing the PORT_SUSPEND feature causes a host-initiated resume
+                          on the specified port. If the port is not in the Suspended state,
+                          the hub should treat this request as a functional no-operation."
+                          Shouldn't we check if the port is in a suspended state before
+                          resuming? */
+
+                       /* Make sure the controller isn't busy. */
+                       while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+                       if (wIndex == 1) {
+                               *R_USB_COMMAND =
+                                       IO_STATE(R_USB_COMMAND, port_sel, port1) |
+                                       IO_STATE(R_USB_COMMAND, port_cmd, resume) |
+                                       IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+                       } else if (wIndex == 2) {
+                               *R_USB_COMMAND =
+                                       IO_STATE(R_USB_COMMAND, port_sel, port2) |
+                                       IO_STATE(R_USB_COMMAND, port_cmd, resume) |
+                                       IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+                       } else {
+                               dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND "
+                                      "with invalid wIndex == %d!", wIndex);
+                       }
+
+                       OK (0);
+               case (RH_PORT_POWER):
+                       OK (0); /* port power ** */
+               case (RH_C_PORT_CONNECTION):
+                       if (wIndex == 1) {
+                               hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION);
+                       } else if (wIndex == 2) {
+                               hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION);
+                       } else {
+                               dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION "
+                                      "with invalid wIndex == %d!", wIndex);
+                       }
+
+                       OK (0);
+               case (RH_C_PORT_ENABLE):
+                       if (wIndex == 1) {
+                               hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE);
+                       } else if (wIndex == 2) {
+                               hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE);
+                       } else {
+                               dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE "
+                                      "with invalid wIndex == %d!", wIndex);
+                       }
+                       OK (0);
+               case (RH_C_PORT_SUSPEND):
+/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+                       OK (0);
+               case (RH_C_PORT_OVER_CURRENT):
+                       OK (0); /* port power over current ** */
+               case (RH_C_PORT_RESET):
+                       if (wIndex == 1) {
+                               hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET);
+                       } else if (wIndex == 2) {
+                               hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET);
+                       } else {
+                               dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET "
+                                      "with invalid index == %d!", wIndex);
+                       }
+
+                       OK (0);
+
+               }
+               break;
+
+       case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+               switch (wValue) {
+               case (RH_PORT_SUSPEND):
+
+                       /* Make sure the controller isn't busy. */
+                       while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+                       if (wIndex == 1) {
+                               *R_USB_COMMAND =
+                                       IO_STATE(R_USB_COMMAND, port_sel, port1) |
+                                       IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
+                                       IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+                       } else if (wIndex == 2) {
+                               *R_USB_COMMAND =
+                                       IO_STATE(R_USB_COMMAND, port_sel, port2) |
+                                       IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
+                                       IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+                       } else {
+                               dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND "
+                                      "with invalid wIndex == %d!", wIndex);
+                       }
+
+                       OK (0);
+               case (RH_PORT_RESET):
+                       if (wIndex == 1) {
+
+                       port_1_reset:
+                               dbg_rh("Doing reset of port 1");
+
+                               /* Make sure the controller isn't busy. */
+                               while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+                               *R_USB_COMMAND =
+                                       IO_STATE(R_USB_COMMAND, port_sel, port1) |
+                                       IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+                                       IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+
+                               /* We must wait at least 10 ms for the device to recover.
+                                  15 ms should be enough. */
+                               udelay(15000);
+
+                               /* Wait for reset bit to go low (should be done by now). */
+                               while (hc->rh.prev_wPortStatus_1 &
+                                      IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes));
+
+                               /* If the port status is
+                                  1) connected and enabled then there is a device and everything is fine
+                                  2) neither connected nor enabled then there is no device, also fine
+                                  3) connected and not enabled then we try again
+                                  (Yes, there are other port status combinations besides these.) */
+
+                               if ((hc->rh.prev_wPortStatus_1 &
+                                    IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+                                   (hc->rh.prev_wPortStatus_1 &
+                                    IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+                                       dbg_rh("Connected device on port 1, but port not enabled?"
+                                              " Trying reset again.");
+                                       goto port_2_reset;
+                               }
+
+                               /* Diagnostic printouts. */
+                               if ((hc->rh.prev_wPortStatus_1 &
+                                    IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) &&
+                                   (hc->rh.prev_wPortStatus_1 &
+                                    IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+                                       dbg_rh("No connected device on port 1");
+                               } else if ((hc->rh.prev_wPortStatus_1 &
+                                           IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+                                          (hc->rh.prev_wPortStatus_1 &
+                                           IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) {
+                                       dbg_rh("Connected device on port 1, port 1 enabled");
+                               }
+
+                       } else if (wIndex == 2) {
+
+                       port_2_reset:
+                               dbg_rh("Doing reset of port 2");
+
+                               /* Make sure the controller isn't busy. */
+                               while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+                               /* Issue the reset command. */
+                               *R_USB_COMMAND =
+                                       IO_STATE(R_USB_COMMAND, port_sel, port2) |
+                                       IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+                                       IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+
+                               /* We must wait at least 10 ms for the device to recover.
+                                  15 ms should be enough. */
+                               udelay(15000);
+
+                               /* Wait for reset bit to go low (should be done by now). */
+                               while (hc->rh.prev_wPortStatus_2 &
+                                      IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes));
+
+                               /* If the port status is
+                                  1) connected and enabled then there is a device and everything is fine
+                                  2) neither connected nor enabled then there is no device, also fine
+                                  3) connected and not enabled then we try again
+                                  (Yes, there are other port status combinations besides these.) */
+
+                               if ((hc->rh.prev_wPortStatus_2 &
+                                    IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+                                   (hc->rh.prev_wPortStatus_2 &
+                                    IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+                                       dbg_rh("Connected device on port 2, but port not enabled?"
+                                              " Trying reset again.");
+                                       goto port_2_reset;
+                               }
+
+                               /* Diagnostic printouts. */
+                               if ((hc->rh.prev_wPortStatus_2 &
+                                    IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) &&
+                                   (hc->rh.prev_wPortStatus_2 &
+                                    IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+                                       dbg_rh("No connected device on port 2");
+                               } else if ((hc->rh.prev_wPortStatus_2 &
+                                           IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+                                          (hc->rh.prev_wPortStatus_2 &
+                                           IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) {
+                                       dbg_rh("Connected device on port 2, port 2 enabled");
+                               }
+
+                       } else {
+                               dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex);
+                       }
+
+                       /* Make sure the controller isn't busy. */
+                       while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+                       /* If all enabled ports were disabled the host controller goes down into
+                          started mode, so we need to bring it back into the running state.
+                          (This is safe even if it's already in the running state.) */
+                       *R_USB_COMMAND =
+                               IO_STATE(R_USB_COMMAND, port_sel, nop) |
+                               IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+                               IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+
+                       dbg_rh("...Done");
+                       OK(0);
+
+               case (RH_PORT_POWER):
+                       OK (0); /* port power ** */
+               case (RH_PORT_ENABLE):
+                       /* There is no port enable command in the host controller, so if the
+                          port is already enabled, we do nothing. If not, we reset the port
+                          (with an ugly goto). */
+
+                       if (wIndex == 1) {
+                               if (hc->rh.prev_wPortStatus_1 &
+                                   IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) {
+                                       goto port_1_reset;
+                               }
+                       } else if (wIndex == 2) {
+                               if (hc->rh.prev_wPortStatus_2 &
+                                   IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) {
+                                       goto port_2_reset;
+                               }
+                       } else {
+                               dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex);
+                       }
+                       OK (0);
+               }
+               break;
+
+       case RH_SET_ADDRESS:
+               hc->rh.devnum = wValue;
+               dbg_rh("RH address set to: %d", hc->rh.devnum);
+               OK (0);
+
+       case RH_GET_DESCRIPTOR:
+               switch ((wValue & 0xff00) >> 8) {
+               case (0x01):    /* device descriptor */
+                       len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength));
+                       memcpy (data, root_hub_dev_des, len);
+                       OK (len);
+               case (0x02):    /* configuration descriptor */
+                       len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength));
+                       memcpy (data, root_hub_config_des, len);
+                       OK (len);
+               case (0x03):    /* string descriptors */
+                       len = usb_root_hub_string (wValue & 0xff,
+                                                  0xff, "ETRAX 100LX",
+                                                  data, wLength);
+                       if (len > 0) {
+                               OK(min(leni, len));
+                       } else {
+                               stat = -EPIPE;
+                       }
+
+               }
+               break;
+
+       case RH_GET_DESCRIPTOR | RH_CLASS:
+               root_hub_hub_des[2] = hc->rh.numports;
+               len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength));
+               memcpy (data, root_hub_hub_des, len);
+               OK (len);
+
+       case RH_GET_CONFIGURATION:
+               *(__u8 *) data = 0x01;
+               OK (1);
+
+       case RH_SET_CONFIGURATION:
+               OK (0);
+
+       default:
+               stat = -EPIPE;
+       }
+
+       urb->actual_length = len;
+       urb->status = stat;
+       urb->dev = NULL;
+       if (urb->complete) {
+               urb->complete(urb, NULL);
+       }
+       DBFEXIT;
+
+       return 0;
+}
+
+static void
+etrax_usb_bulk_eot_timer_func(unsigned long dummy)
+{
+       /* Because of a race condition in the top half, we might miss a bulk eot.
+          This timer "simulates" a bulk eot if we don't get one for a while, hopefully
+          correcting the situation. */
+       dbg_bulk("bulk_eot_timer timed out.");
+       etrax_usb_hc_bulk_eot_interrupt(1);
+}
+
+static void*
+etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, int mem_flags, dma_addr_t *dma)
+{
+  return kmalloc(size, mem_flags);
+}
+
+static void
+etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma)
+{
+  kfree(addr);
+}
+
+
+static struct device fake_device;
+
+static int __init etrax_usb_hc_init(void)
+{
+       static etrax_hc_t *hc;
+       struct usb_bus *bus;
+       struct usb_device *usb_rh;
+       int i;
+
+       DBFENTER;
+
+       info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version);
+
+       hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
+       assert(hc != NULL);
+
+       /* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */
+       /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate
+          SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) ==
+          sizeof(USB_SB_Desc_t). */
+
+       usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0,
+                                          SLAB_HWCACHE_ALIGN, 0, 0);
+       assert(usb_desc_cache != NULL);
+
+       top_half_reg_cache = kmem_cache_create("top_half_reg_cache",
+                                              sizeof(usb_interrupt_registers_t),
+                                              0, SLAB_HWCACHE_ALIGN, 0, 0);
+       assert(top_half_reg_cache != NULL);
+
+       isoc_compl_cache = kmem_cache_create("isoc_compl_cache",
+                                               sizeof(usb_isoc_complete_data_t),
+                                               0, SLAB_HWCACHE_ALIGN, 0, 0);
+       assert(isoc_compl_cache != NULL);
+
+       etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations);
+       hc->bus = bus;
+       bus->bus_name="ETRAX 100LX";
+       bus->hcpriv = hc;
+
+       /* Initalize RH to the default address.
+          And make sure that we have no status change indication */
+       hc->rh.numports = 2;  /* The RH has two ports */
+       hc->rh.devnum = 1;
+       hc->rh.wPortChange_1 = 0;
+       hc->rh.wPortChange_2 = 0;
+
+       /* Also initate the previous values to zero */
+       hc->rh.prev_wPortStatus_1 = 0;
+       hc->rh.prev_wPortStatus_2 = 0;
+
+       /* Initialize the intr-traffic flags */
+       /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */
+       hc->intr.sleeping = 0;
+       hc->intr.wq = NULL;
+
+       epid_usage_bitmask = 0;
+       epid_out_traffic = 0;
+
+       /* Mark the invalid epid as being used. */
+       set_bit(INVALID_EPID, (void *)&epid_usage_bitmask);
+       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID);
+       nop();
+       /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */
+       *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) |
+                          IO_FIELD(R_USB_EPT_DATA, max_len, 1));
+
+       /* Mark the dummy epid as being used. */
+       set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask);
+       *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID);
+       nop();
+       *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) |
+                          IO_FIELD(R_USB_EPT_DATA, max_len, 1));
+
+       /* Initialize the urb list by initiating a head for each list. */
+       for (i = 0; i < NBR_OF_EPIDS; i++) {
+               INIT_LIST_HEAD(&urb_list[i]);
+       }
+       spin_lock_init(&urb_list_lock);
+
+       INIT_LIST_HEAD(&urb_unlink_list);
+
+
+       /* Initiate the bulk start timer. */
+       init_timer(&bulk_start_timer);
+       bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL;
+       bulk_start_timer.function = etrax_usb_bulk_start_timer_func;
+       add_timer(&bulk_start_timer);
+
+
+       /* Initiate the bulk eot timer. */
+       init_timer(&bulk_eot_timer);
+       bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL;
+       bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func;
+       add_timer(&bulk_eot_timer);
+
+       /* Set up the data structures for USB traffic. Note that this must be done before
+          any interrupt that relies on sane DMA list occurrs. */
+       init_rx_buffers();
+       init_tx_bulk_ep();
+       init_tx_ctrl_ep();
+       init_tx_intr_ep();
+       init_tx_isoc_ep();
+
+        device_initialize(&fake_device);
+        kobject_set_name(&fake_device.kobj, "etrax_usb");
+        kobject_add(&fake_device.kobj);
+        hc->bus->controller = &fake_device;
+       usb_register_bus(hc->bus);
+
+       *R_IRQ_MASK2_SET =
+               /* Note that these interrupts are not used. */
+               IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) |
+               /* Sub channel 1 (ctrl) descr. interrupts are used. */
+               IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) |
+               IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) |
+               /* Sub channel 3 (isoc) descr. interrupts are used. */
+               IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set);
+
+       /* Note that the dma9_descr interrupt is not used. */
+       *R_IRQ_MASK2_SET =
+               IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
+               IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
+
+       /* FIXME: Enable iso_eof only when isoc traffic is running. */
+       *R_USB_IRQ_MASK_SET =
+               IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) |
+               IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) |
+               IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) |
+               IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) |
+               IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
+
+
+       if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0,
+                       "ETRAX 100LX built-in USB (HC)", hc)) {
+               err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ);
+               etrax_usb_hc_cleanup();
+               DBFEXIT;
+               return -1;
+       }
+
+       if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0,
+                       "ETRAX 100LX built-in USB (Rx)", hc)) {
+               err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ);
+               etrax_usb_hc_cleanup();
+               DBFEXIT;
+               return -1;
+       }
+
+       if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0,
+                       "ETRAX 100LX built-in USB (Tx)", hc)) {
+               err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ);
+               etrax_usb_hc_cleanup();
+               DBFEXIT;
+               return -1;
+       }
+
+       /* R_USB_COMMAND:
+          USB commands in host mode. The fields in this register should all be
+          written to in one write. Do not read-modify-write one field at a time. A
+          write to this register will trigger events in the USB controller and an
+          incomplete command may lead to unpredictable results, and in worst case
+          even to a deadlock in the controller.
+          (Note however that the busy field is read-only, so no need to write to it.) */
+
+       /* Check the busy bit before writing to R_USB_COMMAND. */
+
+       while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+       /* Reset the USB interface. */
+       *R_USB_COMMAND =
+               IO_STATE(R_USB_COMMAND, port_sel, nop) |
+               IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+               IO_STATE(R_USB_COMMAND, ctrl_cmd, reset);
+
+       /* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800),
+          to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may
+          allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation.
+
+          While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK
+          behaviour, it doesn't solve this problem. What happens is that a control transfer will not
+          be interrupted in its data stage when PSTART happens (the point at which periodic traffic
+          is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before
+          PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done,
+          there may be too little time left for an isochronous transfer, causing an epid attention
+          interrupt due to perror. The work-around for this is to let the control transfers run at the
+          end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't
+          fit into the frame. However, since there will *always* be a control transfer at the beginning
+          of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer
+          which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to
+          this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make
+          sure that the periodic transfers that are inserted will always fit in the frame.
+
+          The idea was suggested that a control transfer could be split up into several 8 byte transfers,
+          so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this
+          hasn't been implemented.
+
+          The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra
+          for possible bit stuffing. */
+
+       *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960);
+
+#ifdef CONFIG_ETRAX_USB_HOST_PORT1
+       *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
+#endif
+
+#ifdef CONFIG_ETRAX_USB_HOST_PORT2
+       *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
+#endif
+
+       while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+       /* Configure the USB interface as a host controller. */
+       *R_USB_COMMAND =
+               IO_STATE(R_USB_COMMAND, port_sel, nop) |
+               IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+               IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config);
+
+       /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled
+          sequence of resetting the ports. If we reset both ports now, and there are devices
+          on both ports, we will get a bus error because both devices will answer the set address
+          request. */
+
+       while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+       /* Start processing of USB traffic. */
+       *R_USB_COMMAND =
+               IO_STATE(R_USB_COMMAND, port_sel, nop) |
+               IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+               IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+
+       while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+       usb_rh = usb_alloc_dev(NULL, hc->bus, 0);
+       hc->bus->root_hub = usb_rh;
+        usb_rh->state = USB_STATE_ADDRESS;
+        usb_rh->speed = USB_SPEED_FULL;
+        usb_rh->devnum = 1;
+        hc->bus->devnum_next = 2;
+        usb_rh->epmaxpacketin[0] = usb_rh->epmaxpacketout[0] = 64;
+        usb_get_device_descriptor(usb_rh, USB_DT_DEVICE_SIZE);
+       usb_new_device(usb_rh);
+
+       DBFEXIT;
+
+       return 0;
+}
+
+static void etrax_usb_hc_cleanup(void)
+{
+       DBFENTER;
+
+       free_irq(ETRAX_USB_HC_IRQ, NULL);
+       free_irq(ETRAX_USB_RX_IRQ, NULL);
+       free_irq(ETRAX_USB_TX_IRQ, NULL);
+
+       usb_deregister_bus(etrax_usb_bus);
+
+       /* FIXME: call kmem_cache_destroy here? */
+
+       DBFEXIT;
+}
+
+module_init(etrax_usb_hc_init);
+module_exit(etrax_usb_hc_cleanup);
diff --git a/drivers/usb/host/hc_crisv10.h b/drivers/usb/host/hc_crisv10.h
new file mode 100644 (file)
index 0000000..62f7711
--- /dev/null
@@ -0,0 +1,289 @@
+#ifndef __LINUX_ETRAX_USB_H
+#define __LINUX_ETRAX_USB_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+typedef struct USB_IN_Desc {
+       volatile __u16 sw_len;
+       volatile __u16 command;
+       volatile unsigned long next;
+       volatile unsigned long buf;
+       volatile __u16 hw_len;
+       volatile __u16 status;
+} USB_IN_Desc_t;
+
+typedef struct USB_SB_Desc {
+       volatile __u16 sw_len;
+       volatile __u16 command;
+       volatile unsigned long next;
+       volatile unsigned long buf;
+       __u32 dummy;
+} USB_SB_Desc_t;
+
+typedef struct USB_EP_Desc {
+       volatile __u16 hw_len;
+       volatile __u16 command;
+       volatile unsigned long sub;
+       volatile unsigned long next;
+       __u32 dummy;
+} USB_EP_Desc_t;
+
+struct virt_root_hub {
+       int devnum;
+       void *urb;
+       void *int_addr;
+       int send;
+       int interval;
+       int numports;
+       struct timer_list rh_int_timer;
+       volatile __u16 wPortChange_1;
+       volatile __u16 wPortChange_2;
+       volatile __u16 prev_wPortStatus_1;
+       volatile __u16 prev_wPortStatus_2;
+};
+
+struct etrax_usb_intr_traffic {
+       int sleeping;
+       int error;
+       struct wait_queue *wq;
+};
+
+typedef struct etrax_usb_hc {
+       struct usb_bus *bus;
+       struct virt_root_hub rh;
+       struct etrax_usb_intr_traffic intr;
+} etrax_hc_t;
+
+typedef enum {
+       STARTED,
+       NOT_STARTED,
+       UNLINK,
+       TRANSFER_DONE,
+       WAITING_FOR_DESCR_INTR
+} etrax_usb_urb_state_t;
+
+
+
+typedef struct etrax_usb_urb_priv {
+       /* The first_sb field is used for freeing all SB descriptors belonging
+          to an urb. The corresponding ep descriptor's sub pointer cannot be
+          used for this since the DMA advances the sub pointer as it processes
+          the sb list. */
+       USB_SB_Desc_t *first_sb;
+       /* The last_sb field referes to the last SB descriptor that belongs to
+          this urb. This is important to know so we can free the SB descriptors
+          that ranges between first_sb and last_sb. */
+       USB_SB_Desc_t *last_sb;
+
+       /* The rx_offset field is used in ctrl and bulk traffic to keep track
+          of the offset in the urb's transfer_buffer where incoming data should be
+          copied to. */
+       __u32 rx_offset;
+
+       /* Counter used in isochronous transfers to keep track of the
+          number of packets received/transmitted.  */
+       __u32 isoc_packet_counter;
+
+       /* This field is used to pass information about the urb's current state between
+          the various interrupt handlers (thus marked volatile). */
+       volatile etrax_usb_urb_state_t urb_state;
+
+       /* Connection between the submitted urb and ETRAX epid number */
+       __u8 epid;
+
+       /* The rx_data_list field is used for periodic traffic, to hold
+          received data for later processing in the the complete_urb functions,
+          where the data us copied to the urb's transfer_buffer. Basically, we
+          use this intermediate storage because we don't know when it's safe to
+          reuse the transfer_buffer (FIXME?). */
+       struct list_head rx_data_list;
+} etrax_urb_priv_t;
+
+/* This struct is for passing data from the top half to the bottom half. */
+typedef struct usb_interrupt_registers
+{
+       etrax_hc_t *hc;
+       __u32 r_usb_epid_attn;
+       __u8 r_usb_status;
+       __u16 r_usb_rh_port_status_1;
+       __u16 r_usb_rh_port_status_2;
+       __u32 r_usb_irq_mask_read;
+       __u32 r_usb_fm_number;
+       struct work_struct usb_bh;
+} usb_interrupt_registers_t;
+
+/* This struct is for passing data from the isoc top half to the isoc bottom half. */
+typedef struct usb_isoc_complete_data
+{
+       struct urb *urb;
+       struct work_struct usb_bh;
+} usb_isoc_complete_data_t;
+
+/* This struct holds data we get from the rx descriptors for DMA channel 9
+   for periodic traffic (intr and isoc). */
+typedef struct rx_data
+{
+       void *data;
+       int length;
+       struct list_head list;
+} rx_data_t;
+
+typedef struct urb_entry
+{
+       struct urb *urb;
+       struct list_head list;
+} urb_entry_t;
+
+/* ---------------------------------------------------------------------------
+   Virtual Root HUB
+   ------------------------------------------------------------------------- */
+/* destination of request */
+#define RH_INTERFACE               0x01
+#define RH_ENDPOINT                0x02
+#define RH_OTHER                   0x03
+
+#define RH_CLASS                   0x20
+#define RH_VENDOR                  0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS           0x0080
+#define RH_CLEAR_FEATURE        0x0100
+#define RH_SET_FEATURE          0x0300
+#define RH_SET_ADDRESS         0x0500
+#define RH_GET_DESCRIPTOR      0x0680
+#define RH_SET_DESCRIPTOR       0x0700
+#define RH_GET_CONFIGURATION   0x0880
+#define RH_SET_CONFIGURATION   0x0900
+#define RH_GET_STATE            0x0280
+#define RH_GET_INTERFACE        0x0A80
+#define RH_SET_INTERFACE        0x0B00
+#define RH_SYNC_FRAME           0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP               0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION         0x00
+#define RH_PORT_ENABLE             0x01
+#define RH_PORT_SUSPEND            0x02
+#define RH_PORT_OVER_CURRENT       0x03
+#define RH_PORT_RESET              0x04
+#define RH_PORT_POWER              0x08
+#define RH_PORT_LOW_SPEED          0x09
+#define RH_C_PORT_CONNECTION       0x10
+#define RH_C_PORT_ENABLE           0x11
+#define RH_C_PORT_SUSPEND          0x12
+#define RH_C_PORT_OVER_CURRENT     0x13
+#define RH_C_PORT_RESET            0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER       0x00
+#define RH_C_HUB_OVER_CURRENT      0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP    0x00
+#define RH_ENDPOINT_STALL          0x01
+
+/* Our Vendor Specific feature */
+#define RH_REMOVE_EP               0x00
+
+
+#define RH_ACK                     0x01
+#define RH_REQ_ERR                 -1
+#define RH_NACK                    0x00
+
+/* Field definitions for */
+
+#define USB_IN_command__eol__BITNR      0 /* command macros */
+#define USB_IN_command__eol__WIDTH      1
+#define USB_IN_command__eol__no         0
+#define USB_IN_command__eol__yes        1
+
+#define USB_IN_command__intr__BITNR     3
+#define USB_IN_command__intr__WIDTH     1
+#define USB_IN_command__intr__no        0
+#define USB_IN_command__intr__yes       1
+
+#define USB_IN_status__eop__BITNR       1 /* status macros. */
+#define USB_IN_status__eop__WIDTH       1
+#define USB_IN_status__eop__no          0
+#define USB_IN_status__eop__yes         1
+
+#define USB_IN_status__eot__BITNR       5
+#define USB_IN_status__eot__WIDTH       1
+#define USB_IN_status__eot__no          0
+#define USB_IN_status__eot__yes         1
+
+#define USB_IN_status__error__BITNR     6
+#define USB_IN_status__error__WIDTH     1
+#define USB_IN_status__error__no        0
+#define USB_IN_status__error__yes       1
+
+#define USB_IN_status__nodata__BITNR    7
+#define USB_IN_status__nodata__WIDTH    1
+#define USB_IN_status__nodata__no       0
+#define USB_IN_status__nodata__yes      1
+
+#define USB_IN_status__epid__BITNR      8
+#define USB_IN_status__epid__WIDTH      5
+
+#define USB_EP_command__eol__BITNR      0
+#define USB_EP_command__eol__WIDTH      1
+#define USB_EP_command__eol__no         0
+#define USB_EP_command__eol__yes        1
+
+#define USB_EP_command__eof__BITNR      1
+#define USB_EP_command__eof__WIDTH      1
+#define USB_EP_command__eof__no         0
+#define USB_EP_command__eof__yes        1
+
+#define USB_EP_command__intr__BITNR     3
+#define USB_EP_command__intr__WIDTH     1
+#define USB_EP_command__intr__no        0
+#define USB_EP_command__intr__yes       1
+
+#define USB_EP_command__enable__BITNR   4
+#define USB_EP_command__enable__WIDTH   1
+#define USB_EP_command__enable__no      0
+#define USB_EP_command__enable__yes     1
+
+#define USB_EP_command__hw_valid__BITNR 5
+#define USB_EP_command__hw_valid__WIDTH 1
+#define USB_EP_command__hw_valid__no    0
+#define USB_EP_command__hw_valid__yes   1
+
+#define USB_EP_command__epid__BITNR     8
+#define USB_EP_command__epid__WIDTH     5
+
+#define USB_SB_command__eol__BITNR      0 /* command macros. */
+#define USB_SB_command__eol__WIDTH      1
+#define USB_SB_command__eol__no         0
+#define USB_SB_command__eol__yes        1
+
+#define USB_SB_command__eot__BITNR      1
+#define USB_SB_command__eot__WIDTH      1
+#define USB_SB_command__eot__no         0
+#define USB_SB_command__eot__yes        1
+
+#define USB_SB_command__intr__BITNR     3
+#define USB_SB_command__intr__WIDTH     1
+#define USB_SB_command__intr__no        0
+#define USB_SB_command__intr__yes       1
+
+#define USB_SB_command__tt__BITNR       4
+#define USB_SB_command__tt__WIDTH       2
+#define USB_SB_command__tt__zout        0
+#define USB_SB_command__tt__in          1
+#define USB_SB_command__tt__out         2
+#define USB_SB_command__tt__setup       3
+
+
+#define USB_SB_command__rem__BITNR      8
+#define USB_SB_command__rem__WIDTH      6
+
+#define USB_SB_command__full__BITNR     6
+#define USB_SB_command__full__WIDTH     1
+#define USB_SB_command__full__no        0
+#define USB_SB_command__full__yes       1
+
+#endif
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
new file mode 100644 (file)
index 0000000..ee8503a
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Bus Glue for AMD Alchemy Au1xxx
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Rusell King et al.
+ *
+ * Modified for LH7A404 from ohci-sa1111.c
+ *  by Durgesh Pattamatta <pattamattad@sharpsec.com>
+ * Modified for AMD Alchemy Au1xxx
+ *  by Matt Porter <mporter@kernel.crashing.org>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/mach-au1x00/au1000.h>
+
+#define USBH_ENABLE_BE (1<<0)
+#define USBH_ENABLE_C  (1<<1)
+#define USBH_ENABLE_E  (1<<2)
+#define USBH_ENABLE_CE (1<<3)
+#define USBH_ENABLE_RD (1<<4)
+
+#ifdef __LITTLE_ENDIAN
+#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
+#elif __BIG_ENDIAN
+#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE)
+#else
+#error not byte order defined
+#endif
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void au1xxx_start_hc(struct platform_device *dev)
+{
+       printk(KERN_DEBUG __FILE__
+               ": starting Au1xxx OHCI USB Controller\n");
+
+       /* enable host controller */
+       au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG);
+       udelay(1000);
+       au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG);
+       udelay(1000);
+
+       /* wait for reset complete (read register twice; see au1500 errata) */
+       while (au_readl(USB_HOST_CONFIG),
+               !(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD))
+               udelay(1000);
+
+       printk(KERN_DEBUG __FILE__
+       ": Clock to USB host has been enabled \n");
+}
+
+static void au1xxx_stop_hc(struct platform_device *dev)
+{
+       printk(KERN_DEBUG __FILE__
+              ": stopping Au1xxx OHCI USB Controller\n");
+
+       /* Disable clock */
+       au_writel(readl((void *)USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static irqreturn_t usb_hcd_au1xxx_hcim_irq (int irq, void *__hcd,
+                                            struct pt_regs * r)
+{
+       struct usb_hcd *hcd = __hcd;
+
+       return usb_hcd_irq(irq, hcd, r);
+}
+
+/*-------------------------------------------------------------------------*/
+
+void usb_hcd_au1xxx_remove (struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_au1xxx_probe - initialize Au1xxx-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_au1xxx_probe (const struct hc_driver *driver,
+                         struct usb_hcd **hcd_out,
+                         struct platform_device *dev)
+{
+       int retval;
+       struct usb_hcd *hcd = 0;
+
+       unsigned int *addr = NULL;
+
+       if (!request_mem_region(dev->resource[0].start,
+                               dev->resource[0].end
+                               - dev->resource[0].start + 1, hcd_name)) {
+               pr_debug("request_mem_region failed");
+               return -EBUSY;
+       }
+
+       au1xxx_start_hc(dev);
+
+       addr = ioremap(dev->resource[0].start,
+                      dev->resource[0].end
+                      - dev->resource[0].start + 1);
+       if (!addr) {
+               pr_debug("ioremap failed");
+               retval = -ENOMEM;
+               goto err1;
+       }
+
+       if(dev->resource[1].flags != IORESOURCE_IRQ) {
+               pr_debug ("resource[1] is not IORESOURCE_IRQ");
+               retval = -ENOMEM;
+               goto err1;
+       }
+
+       hcd = usb_create_hcd(driver);
+       if (hcd == NULL) {
+               pr_debug ("usb_create_hcd failed");
+               retval = -ENOMEM;
+               goto err1;
+       }
+       ohci_hcd_init(hcd_to_ohci(hcd));
+
+       hcd->irq = dev->resource[1].start;
+       hcd->regs = addr;
+       hcd->self.controller = &dev->dev;
+
+       retval = hcd_buffer_create (hcd);
+       if (retval != 0) {
+               pr_debug ("pool alloc fail");
+               goto err2;
+       }
+
+       retval = request_irq (hcd->irq, usb_hcd_au1xxx_hcim_irq, SA_INTERRUPT,
+                             hcd->driver->description, hcd);
+       if (retval != 0) {
+               pr_debug("request_irq failed");
+               retval = -EBUSY;
+               goto err3;
+       }
+
+       pr_debug ("%s (Au1xxx) at 0x%p, irq %d",
+            hcd->driver->description, hcd->regs, hcd->irq);
+
+       hcd->self.bus_name = "au1xxx";
+
+       usb_register_bus (&hcd->self);
+
+       if ((retval = driver->start (hcd)) < 0)
+       {
+               usb_hcd_au1xxx_remove(hcd, dev);
+               printk("bad driver->start\n");
+               return retval;
+       }
+
+       *hcd_out = hcd;
+       return 0;
+
+ err3:
+       hcd_buffer_destroy (hcd);
+ err2:
+       usb_put_hcd(hcd);
+ err1:
+       au1xxx_stop_hc(dev);
+       release_mem_region(dev->resource[0].start,
+                               dev->resource[0].end
+                          - dev->resource[0].start + 1);
+       return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_au1xxx_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_au1xxx_remove (struct usb_hcd *hcd, struct platform_device *dev)
+{
+       pr_debug ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
+
+       if (in_interrupt ())
+               BUG ();
+
+       hcd->state = USB_STATE_QUIESCING;
+
+       pr_debug ("%s: roothub graceful disconnect", hcd->self.bus_name);
+       usb_disconnect (&hcd->self.root_hub);
+
+       hcd->driver->stop (hcd);
+       hcd->state = USB_STATE_HALT;
+
+       free_irq (hcd->irq, hcd);
+       hcd_buffer_destroy (hcd);
+
+       usb_deregister_bus (&hcd->self);
+
+       au1xxx_stop_hc(dev);
+       release_mem_region(dev->resource[0].start,
+                          dev->resource[0].end
+                          - dev->resource[0].start + 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_au1xxx_start (struct usb_hcd *hcd)
+{
+       struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+       int             ret;
+
+       ohci_dbg (ohci, "ohci_au1xxx_start, ohci:%p", ohci);
+
+       if ((ret = ohci_init (ohci)) < 0)
+               return ret;
+
+       if ((ret = ohci_run (ohci)) < 0) {
+               err ("can't start %s", hcd->self.bus_name);
+               ohci_stop (hcd);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_au1xxx_hc_driver = {
+       .description =          hcd_name,
+       .product_desc =         "Au1xxx OHCI",
+       .hcd_priv_size =        sizeof(struct ohci_hcd),
+
+       /*
+        * generic hardware linkage
+        */
+       .irq =                  ohci_irq,
+       .flags =                HCD_USB11,
+
+       /*
+        * basic lifecycle operations
+        */
+       .start =                ohci_au1xxx_start,
+#ifdef CONFIG_PM
+       /* suspend:             ohci_au1xxx_suspend,  -- tbd */
+       /* resume:              ohci_au1xxx_resume,   -- tbd */
+#endif /*CONFIG_PM*/
+       .stop =                 ohci_stop,
+
+       /*
+        * managing i/o requests and associated device resources
+        */
+       .urb_enqueue =          ohci_urb_enqueue,
+       .urb_dequeue =          ohci_urb_dequeue,
+       .endpoint_disable =     ohci_endpoint_disable,
+
+       /*
+        * scheduling support
+        */
+       .get_frame_number =     ohci_get_frame,
+
+       /*
+        * root hub support
+        */
+       .hub_status_data =      ohci_hub_status_data,
+       .hub_control =          ohci_hub_control,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_au1xxx_drv_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct usb_hcd *hcd = NULL;
+       int ret;
+
+       pr_debug ("In ohci_hcd_au1xxx_drv_probe");
+
+       if (usb_disabled())
+               return -ENODEV;
+
+       ret = usb_hcd_au1xxx_probe(&ohci_au1xxx_hc_driver, &hcd, pdev);
+
+       if (ret == 0)
+               dev_set_drvdata(dev, hcd);
+
+       return ret;
+}
+
+static int ohci_hcd_au1xxx_drv_remove(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+       usb_hcd_au1xxx_remove(hcd, pdev);
+       dev_set_drvdata(dev, NULL);
+       return 0;
+}
+       /*TBD*/
+/*static int ohci_hcd_au1xxx_drv_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+       return 0;
+}
+static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+       return 0;
+}
+*/
+
+static struct device_driver ohci_hcd_au1xxx_driver = {
+       .name           = "au1xxx-ohci",
+       .bus            = &platform_bus_type,
+       .probe          = ohci_hcd_au1xxx_drv_probe,
+       .remove         = ohci_hcd_au1xxx_drv_remove,
+       /*.suspend      = ohci_hcd_au1xxx_drv_suspend, */
+       /*.resume       = ohci_hcd_au1xxx_drv_resume, */
+};
+
+static int __init ohci_hcd_au1xxx_init (void)
+{
+       pr_debug (DRIVER_INFO " (Au1xxx)");
+       pr_debug ("block sizes: ed %d td %d\n",
+               sizeof (struct ed), sizeof (struct td));
+
+       return driver_register(&ohci_hcd_au1xxx_driver);
+}
+
+static void __exit ohci_hcd_au1xxx_cleanup (void)
+{
+       driver_unregister(&ohci_hcd_au1xxx_driver);
+}
+
+module_init (ohci_hcd_au1xxx_init);
+module_exit (ohci_hcd_au1xxx_cleanup);
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
new file mode 100644 (file)
index 0000000..44582d0
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ *
+ * Bus Glue for pxa27x
+ *
+ * Written by Christopher Hoover <ch@hpl.hp.com>
+ * Based on fragments of previous driver by Russell King et al.
+ *
+ * Modified for LH7A404 from ohci-sa1111.c
+ *  by Durgesh Pattamatta <pattamattad@sharpsec.com>
+ *
+ * Modified for pxa27x from ohci-lh7a404.c
+ *  by Nick Bane <nick@cecomputing.co.uk> 26-8-2004
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <linux/device.h>
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+
+#define PMM_NPS_MODE           1
+#define PMM_GLOBAL_MODE        2
+#define PMM_PERPORT_MODE       3
+
+#define PXA_UHC_MAX_PORTNUM    3
+
+#define UHCRHPS(x)              __REG2( 0x4C000050, (x)<<2 )
+
+static int pxa27x_ohci_pmm_state;
+
+/*
+  PMM_NPS_MODE -- PMM Non-power switching mode
+      Ports are powered continuously.
+
+  PMM_GLOBAL_MODE -- PMM global switching mode
+      All ports are powered at the same time.
+
+  PMM_PERPORT_MODE -- PMM per port switching mode
+      Ports are powered individually.
+ */
+static int pxa27x_ohci_select_pmm( int mode )
+{
+       pxa27x_ohci_pmm_state = mode;
+
+       switch ( mode ) {
+       case PMM_NPS_MODE:
+               UHCRHDA |= RH_A_NPS;
+               break; 
+       case PMM_GLOBAL_MODE:
+               UHCRHDA &= ~(RH_A_NPS & RH_A_PSM);
+               break;
+       case PMM_PERPORT_MODE:
+               UHCRHDA &= ~(RH_A_NPS);
+               UHCRHDA |= RH_A_PSM;
+
+               /* Set port power control mask bits, only 3 ports. */
+               UHCRHDB |= (0x7<<17);
+               break;
+       default:
+               printk( KERN_ERR
+                       "Invalid mode %d, set to non-power switch mode.\n", 
+                       mode );
+
+               pxa27x_ohci_pmm_state = PMM_NPS_MODE;
+               UHCRHDA |= RH_A_NPS;
+       }
+
+       return 0;
+}
+
+/*
+  If you select PMM_PERPORT_MODE, you should set the port power
+ */
+static int pxa27x_ohci_set_port_power( int port )
+{
+       if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE)
+            && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
+               UHCRHPS(port) |= 0x100;
+               return 0;
+       }
+       return -1;
+}
+
+/*
+  If you select PMM_PERPORT_MODE, you should set the port power
+ */
+static int pxa27x_ohci_clear_port_power( int port )
+{
+       if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE) 
+            && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) {
+               UHCRHPS(port) |= 0x200;
+               return 0;
+       }
+        
+       return -1;
+}
+
+extern int usb_disabled(void);
+
+/*-------------------------------------------------------------------------*/
+
+static void pxa27x_start_hc(struct platform_device *dev)
+{
+       pxa_set_cken(CKEN10_USBHOST, 1);
+
+       UHCHR |= UHCHR_FHR;
+       udelay(11);
+       UHCHR &= ~UHCHR_FHR;
+
+       UHCHR |= UHCHR_FSBIR;
+       while (UHCHR & UHCHR_FSBIR)
+               cpu_relax();
+
+       /* This could be properly abstracted away through the
+          device data the day more machines are supported and
+          their differences can be figured out correctly. */
+       if (machine_is_mainstone()) {
+               /* setup Port1 GPIO pin. */
+               pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN);  /* USBHPWR1 */
+               pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */
+
+               /* Set the Power Control Polarity Low and Power Sense
+                  Polarity Low to active low. Supply power to USB ports. */
+               UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) &
+                       ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE);
+       }
+
+       UHCHR &= ~UHCHR_SSE;
+
+       UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE);
+}
+
+static void pxa27x_stop_hc(struct platform_device *dev)
+{
+       UHCHR |= UHCHR_FHR;
+       udelay(11);
+       UHCHR &= ~UHCHR_FHR;
+
+       UHCCOMS |= 1;
+       udelay(10);
+
+       pxa_set_cken(CKEN10_USBHOST, 0);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+void usb_hcd_pxa27x_remove (struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+
+/**
+ * usb_hcd_pxa27x_probe - initialize pxa27x-based HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ */
+int usb_hcd_pxa27x_probe (const struct hc_driver *driver,
+                         struct usb_hcd **hcd_out,
+                         struct platform_device *dev)
+{
+       int retval;
+       struct usb_hcd *hcd = 0;
+
+       unsigned int *addr = NULL;
+
+       if (!request_mem_region(dev->resource[0].start,
+                               dev->resource[0].end
+                               - dev->resource[0].start + 1, hcd_name)) {
+               pr_debug("request_mem_region failed");
+               return -EBUSY;
+       }
+
+       pxa27x_start_hc(dev);
+
+       /* Select Power Management Mode */
+       pxa27x_ohci_select_pmm( PMM_PERPORT_MODE );
+
+       /* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */
+       if (pxa27x_ohci_set_port_power(1) < 0)
+               printk(KERN_ERR "Setting port 1 power failed.\n");
+
+       if (pxa27x_ohci_clear_port_power(2) < 0)
+               printk(KERN_ERR "Setting port 2 power failed.\n");
+
+       if (pxa27x_ohci_clear_port_power(3) < 0)
+               printk(KERN_ERR "Setting port 3 power failed.\n");
+
+       addr = ioremap(dev->resource[0].start,
+                      dev->resource[0].end - dev->resource[0].start + 1);
+       if (!addr) {
+               pr_debug("ioremap failed");
+               retval = -ENOMEM;
+               goto err1;
+       }
+
+       hcd = driver->hcd_alloc ();
+       if (hcd == NULL){
+               pr_debug ("hcd_alloc failed");
+               retval = -ENOMEM;
+               goto err1;
+       }
+
+       if(dev->resource[1].flags != IORESOURCE_IRQ){
+               pr_debug ("resource[1] is not IORESOURCE_IRQ");
+               retval = -ENOMEM;
+               goto err1;
+       }
+
+       hcd->driver = (struct hc_driver *) driver;
+       hcd->description = driver->description;
+       hcd->irq = dev->resource[1].start;
+       hcd->regs = addr;
+       hcd->self.controller = &dev->dev;
+
+       retval = hcd_buffer_create (hcd);
+       if (retval != 0) {
+               pr_debug ("pool alloc fail");
+               goto err1;
+       }
+
+       retval = request_irq (hcd->irq, usb_hcd_irq, SA_INTERRUPT,
+                             hcd->description, hcd);
+       if (retval != 0) {
+               pr_debug("request_irq(%d) failed with retval %d\n",hcd->irq,retval);
+               retval = -EBUSY;
+               goto err2;
+       }
+
+       pr_debug ("%s (pxa27x) at 0x%p, irq %d",
+            hcd->description, hcd->regs, hcd->irq);
+
+       usb_bus_init (&hcd->self);
+       hcd->self.op = &usb_hcd_operations;
+       hcd->self.release = &usb_hcd_release;
+       hcd->self.hcpriv = (void *) hcd;
+       hcd->self.bus_name = "pxa27x";
+       hcd->product_desc = "PXA27x OHCI";
+
+       INIT_LIST_HEAD (&hcd->dev_list);
+
+       usb_register_bus (&hcd->self);
+
+       if ((retval = driver->start (hcd)) < 0) {
+               usb_hcd_pxa27x_remove(hcd, dev);
+               return retval;
+       }
+
+       *hcd_out = hcd;
+       return 0;
+
+ err2:
+       hcd_buffer_destroy (hcd);
+ err1:
+       kfree(hcd);
+       pxa27x_stop_hc(dev);
+       release_mem_region(dev->resource[0].start,
+                               dev->resource[0].end
+                          - dev->resource[0].start + 1);
+       return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_pxa27x_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *dev)
+{
+       pr_debug ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
+
+       if (in_interrupt ())
+               BUG ();
+
+       hcd->state = USB_STATE_QUIESCING;
+
+       pr_debug ("%s: roothub graceful disconnect", hcd->self.bus_name);
+       usb_disconnect (&hcd->self.root_hub);
+
+       hcd->driver->stop (hcd);
+       hcd->state = USB_STATE_HALT;
+
+       free_irq (hcd->irq, hcd);
+       hcd_buffer_destroy (hcd);
+
+       usb_deregister_bus (&hcd->self);
+
+       pxa27x_stop_hc(dev);
+       release_mem_region(dev->resource[0].start,
+                          dev->resource[0].end - dev->resource[0].start + 1);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_pxa27x_start (struct usb_hcd *hcd)
+{
+       struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+       int             ret;
+
+       ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
+
+       if ((ret = ohci_init(ohci)) < 0)
+               return ret;
+
+       if ((ret = ohci_run (ohci)) < 0) {
+               err ("can't start %s", ohci->hcd.self.bus_name);
+               ohci_stop (hcd);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_pxa27x_hc_driver = {
+       .description =          hcd_name,
+
+       /*
+        * generic hardware linkage
+        */
+       .irq =                  ohci_irq,
+       .flags =                HCD_USB11,
+
+       /*
+        * basic lifecycle operations
+        */
+       .start =                ohci_pxa27x_start,
+       .stop =                 ohci_stop,
+
+       /*
+        * memory lifecycle (except per-request)
+        */
+       .hcd_alloc =            ohci_hcd_alloc,
+
+       /*
+        * managing i/o requests and associated device resources
+        */
+       .urb_enqueue =          ohci_urb_enqueue,
+       .urb_dequeue =          ohci_urb_dequeue,
+       .endpoint_disable =     ohci_endpoint_disable,
+
+       /*
+        * scheduling support
+        */
+       .get_frame_number =     ohci_get_frame,
+
+       /*
+        * root hub support
+        */
+       .hub_status_data =      ohci_hub_status_data,
+       .hub_control =          ohci_hub_control,
+#ifdef  CONFIG_USB_SUSPEND
+       .hub_suspend =          ohci_hub_suspend,
+       .hub_resume =           ohci_hub_resume,
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hcd_pxa27x_drv_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct usb_hcd *hcd = NULL;
+       int ret;
+
+       pr_debug ("In ohci_hcd_pxa27x_drv_probe");
+
+       if (usb_disabled())
+               return -ENODEV;
+
+       ret = usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, &hcd, pdev);
+
+       if (ret == 0)
+               dev_set_drvdata(dev, hcd);
+
+       return ret;
+}
+
+static int ohci_hcd_pxa27x_drv_remove(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+       usb_hcd_pxa27x_remove(hcd, pdev);
+       dev_set_drvdata(dev, NULL);
+       return 0;
+}
+
+static int ohci_hcd_pxa27x_drv_suspend(struct device *dev, u32 state, u32 level)
+{
+//     struct platform_device *pdev = to_platform_device(dev);
+//     struct usb_hcd *hcd = dev_get_drvdata(dev);
+       printk("%s: not implemented yet\n", __FUNCTION__);
+
+       return 0;
+}
+
+static int ohci_hcd_pxa27x_drv_resume(struct device *dev, u32 state)
+{
+//     struct platform_device *pdev = to_platform_device(dev);
+//     struct usb_hcd *hcd = dev_get_drvdata(dev);
+       printk("%s: not implemented yet\n", __FUNCTION__);
+
+       return 0;
+}
+
+
+static struct device_driver ohci_hcd_pxa27x_driver = {
+       .name           = "pxa27x-ohci",
+       .bus            = &platform_bus_type,
+       .probe          = ohci_hcd_pxa27x_drv_probe,
+       .remove         = ohci_hcd_pxa27x_drv_remove,
+       .suspend        = ohci_hcd_pxa27x_drv_suspend, 
+       .resume         = ohci_hcd_pxa27x_drv_resume, 
+};
+
+static int __init ohci_hcd_pxa27x_init (void)
+{
+       pr_debug (DRIVER_INFO " (pxa27x)");
+       pr_debug ("block sizes: ed %d td %d\n",
+               sizeof (struct ed), sizeof (struct td));
+
+       return driver_register(&ohci_hcd_pxa27x_driver);
+}
+
+static void __exit ohci_hcd_pxa27x_cleanup (void)
+{
+       driver_unregister(&ohci_hcd_pxa27x_driver);
+}
+
+module_init (ohci_hcd_pxa27x_init);
+module_exit (ohci_hcd_pxa27x_cleanup);
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
new file mode 100644 (file)
index 0000000..ac8f899
--- /dev/null
@@ -0,0 +1,1905 @@
+/*
+ * SL811HS HCD (Host Controller Driver) for USB.
+ *
+ * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Copyright (C) 2004 David Brownell
+ * 
+ * Periodic scheduling is based on Roman's OHCI code
+ *     Copyright (C) 1999 Roman Weissgaerber
+ *
+ * The SL811HS controller handles host side USB (like the SL11H, but with
+ * another register set and SOF generation) as well as peripheral side USB
+ * (like the SL811S).  This driver version doesn't implement the Gadget API
+ * for the peripheral role; or OTG (that'd need much external circuitry).
+ *
+ * For documentation, see the SL811HS spec and the "SL811HS Embedded Host"
+ * document (providing significant pieces missing from that spec); plus
+ * the SL811S spec if you want peripheral side info.
+ */ 
+
+/*
+ * Status:  Passed basic stress testing, works with hubs, mice, keyboards,
+ * and usb-storage.
+ *
+ * TODO:
+ * - usb suspend/resume triggered by sl811 (with USB_SUSPEND)
+ * - various issues noted in the code
+ * - performance work; use both register banks; ...
+ * - use urb->iso_frame_desc[] with ISO transfers
+ */
+
+#undef VERBOSE
+#undef PACKET_TRACE
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+#      define DEBUG
+#else
+#      undef DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb_sl811.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+
+#include "../core/hcd.h"
+#include "sl811.h"
+
+
+MODULE_DESCRIPTION("SL811HS USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+
+#define DRIVER_VERSION "06 Dec 2004"
+
+
+#ifndef DEBUG
+#      define  STUB_DEBUG_FILE
+#endif
+
+/* for now, use only one transfer register bank */
+#undef USE_B
+
+/* this doesn't understand urb->iso_frame_desc[], but if you had a driver
+ * that just queued one ISO frame per URB then iso transfers "should" work
+ * using the normal urb status fields.
+ */
+#define        DISABLE_ISO
+
+// #define     QUIRK2
+#define        QUIRK3
+
+static const char hcd_name[] = "sl811-hcd";
+
+/*-------------------------------------------------------------------------*/
+
+static irqreturn_t sl811h_irq(int irq, void *_sl811, struct pt_regs *regs);
+
+static void port_power(struct sl811 *sl811, int is_on)
+{
+       /* hub is inactive unless the port is powered */
+       if (is_on) {
+               if (sl811->port1 & (1 << USB_PORT_FEAT_POWER))
+                       return;
+
+               sl811->port1 = (1 << USB_PORT_FEAT_POWER);
+               sl811->irq_enable = SL11H_INTMASK_INSRMV;
+               sl811->hcd.self.controller->power.power_state = PM_SUSPEND_ON;
+       } else {
+               sl811->port1 = 0;
+               sl811->irq_enable = 0;
+               sl811->hcd.state = USB_STATE_HALT;
+               sl811->hcd.self.controller->power.power_state = PM_SUSPEND_DISK;
+       }
+       sl811->ctrl1 = 0;
+       sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
+       sl811_write(sl811, SL11H_IRQ_STATUS, ~0);
+
+       if (sl811->board && sl811->board->port_power) {
+               /* switch VBUS, at 500mA unless hub power budget gets set */
+               DBG("power %s\n", is_on ? "on" : "off");
+               sl811->board->port_power(sl811->hcd.self.controller, is_on);
+       }
+
+       /* reset as thoroughly as we can */
+       if (sl811->board && sl811->board->reset)
+               sl811->board->reset(sl811->hcd.self.controller);
+
+       sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
+       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+       sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT);
+       sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+       // if !is_on, put into lowpower mode now
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* This is a PIO-only HCD.  Queueing appends URBs to the endpoint's queue,
+ * and may start I/O.  Endpoint queues are scanned during completion irq
+ * handlers (one per packet: ACK, NAK, faults, etc) and urb cancelation.
+ *
+ * Using an external DMA engine to copy a packet at a time could work,
+ * though setup/teardown costs may be too big to make it worthwhile.
+ */
+
+/* SETUP starts a new control request.  Devices are not allowed to
+ * STALL or NAK these; they must cancel any pending control requests.
+ */
+static void setup_packet(
+       struct sl811            *sl811,
+       struct sl811h_ep        *ep,
+       struct urb              *urb,
+       u8                      bank,
+       u8                      control
+)
+{
+       u8                      addr;
+       u8                      len;
+       void __iomem            *data_reg;
+
+       addr = SL811HS_PACKET_BUF(bank == 0);
+       len = sizeof(struct usb_ctrlrequest);
+       data_reg = sl811->data_reg;
+       sl811_write_buf(sl811, addr, urb->setup_packet, len);
+
+       /* autoincrementing */
+       sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+       writeb(len, data_reg);
+       writeb(SL_SETUP /* | ep->epnum */, data_reg);
+       writeb(usb_pipedevice(urb->pipe), data_reg);
+
+       /* always OUT/data0 */ ;
+       sl811_write(sl811, bank + SL11H_HOSTCTLREG,
+                       control | SL11H_HCTLMASK_OUT);
+       ep->length = 0;
+       PACKET("SETUP qh%p\n", ep);
+}
+
+/* STATUS finishes control requests, often after IN or OUT data packets */
+static void status_packet(
+       struct sl811            *sl811,
+       struct sl811h_ep        *ep,
+       struct urb              *urb,
+       u8                      bank,
+       u8                      control
+)
+{
+       int                     do_out;
+       void __iomem            *data_reg;
+
+       do_out = urb->transfer_buffer_length && usb_pipein(urb->pipe);
+       data_reg = sl811->data_reg;
+
+       /* autoincrementing */
+       sl811_write(sl811, bank + SL11H_BUFADDRREG, 0);
+       writeb(0, data_reg);
+       writeb((do_out ? SL_OUT : SL_IN) /* | ep->epnum */, data_reg);
+       writeb(usb_pipedevice(urb->pipe), data_reg);
+
+       /* always data1; sometimes IN */
+       control |= SL11H_HCTLMASK_TOGGLE;
+       if (do_out)
+               control |= SL11H_HCTLMASK_OUT;
+       sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
+       ep->length = 0;
+       PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "",
+                       do_out ? "out" : "in", ep);
+}
+
+/* IN packets can be used with any type of endpoint. here we just
+ * start the transfer, data from the peripheral may arrive later.
+ * urb->iso_frame_desc is currently ignored here...
+ */
+static void in_packet(
+       struct sl811            *sl811,
+       struct sl811h_ep        *ep,
+       struct urb              *urb,
+       u8                      bank,
+       u8                      control
+)
+{
+       u8                      addr;
+       u8                      len;
+       void __iomem            *data_reg;
+
+       /* avoid losing data on overflow */
+       len = ep->maxpacket;
+       addr = SL811HS_PACKET_BUF(bank == 0);
+       if (!(control & SL11H_HCTLMASK_ISOCH)
+                       && usb_gettoggle(urb->dev, ep->epnum, 0))
+               control |= SL11H_HCTLMASK_TOGGLE;
+       data_reg = sl811->data_reg;
+
+       /* autoincrementing */
+       sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+       writeb(len, data_reg);
+       writeb(SL_IN | ep->epnum, data_reg);
+       writeb(usb_pipedevice(urb->pipe), data_reg);
+
+       sl811_write(sl811, bank + SL11H_HOSTCTLREG, control);
+       ep->length = min((int)len,
+                       urb->transfer_buffer_length - urb->actual_length);
+       PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",
+                       !!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len);
+}
+
+/* OUT packets can be used with any type of endpoint.
+ * urb->iso_frame_desc is currently ignored here...
+ */
+static void out_packet(
+       struct sl811            *sl811,
+       struct sl811h_ep        *ep,
+       struct urb              *urb,
+       u8                      bank,
+       u8                      control
+)
+{
+       void                    *buf;
+       u8                      addr;
+       u8                      len;
+       void __iomem            *data_reg;
+
+       buf = urb->transfer_buffer + urb->actual_length;
+       prefetch(buf);
+
+       len = min((int)ep->maxpacket,
+                       urb->transfer_buffer_length - urb->actual_length);
+
+       if (!(control & SL11H_HCTLMASK_ISOCH)
+                       && usb_gettoggle(urb->dev, ep->epnum, 1))
+               control |= SL11H_HCTLMASK_TOGGLE;
+       addr = SL811HS_PACKET_BUF(bank == 0);
+       data_reg = sl811->data_reg;
+
+       sl811_write_buf(sl811, addr, buf, len);
+
+       /* autoincrementing */
+       sl811_write(sl811, bank + SL11H_BUFADDRREG, addr);
+       writeb(len, data_reg);
+       writeb(SL_OUT | ep->epnum, data_reg);
+       writeb(usb_pipedevice(urb->pipe), data_reg);
+
+       sl811_write(sl811, bank + SL11H_HOSTCTLREG,
+                       control | SL11H_HCTLMASK_OUT);
+       ep->length = len;
+       PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "",
+                       !!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* caller updates on-chip enables later */
+
+static inline void sofirq_on(struct sl811 *sl811)
+{
+       if (sl811->irq_enable & SL11H_INTMASK_SOFINTR)
+               return;
+       VDBG("sof irq on\n");
+       sl811->irq_enable |= SL11H_INTMASK_SOFINTR;
+}
+
+static inline void sofirq_off(struct sl811 *sl811)
+{
+       if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR))
+               return;
+       VDBG("sof irq off\n");
+       sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* pick the next endpoint for a transaction, and issue it.
+ * frames start with periodic transfers (after whatever is pending
+ * from the previous frame), and the rest of the time is async
+ * transfers, scheduled round-robin.
+ */
+static struct sl811h_ep        *start(struct sl811 *sl811, u8 bank)
+{
+       struct sl811h_ep        *ep;
+       struct sl811h_req       *req;
+       struct urb              *urb;
+       int                     fclock;
+       u8                      control;
+
+       /* use endpoint at schedule head */
+       if (sl811->next_periodic) {
+               ep = sl811->next_periodic;
+               sl811->next_periodic = ep->next;
+       } else {
+               if (sl811->next_async)
+                       ep = sl811->next_async;
+               else if (!list_empty(&sl811->async))
+                       ep = container_of(sl811->async.next,
+                                       struct sl811h_ep, schedule);
+               else {
+                       /* could set up the first fullspeed periodic
+                        * transfer for the next frame ...
+                        */
+                       return NULL;
+               }
+
+#ifdef USE_B
+               if ((bank && sl811->active_b == ep) || sl811->active_a == ep)
+                       return NULL;
+#endif
+
+               if (ep->schedule.next == &sl811->async)
+                       sl811->next_async = NULL;
+               else
+                       sl811->next_async = container_of(ep->schedule.next,
+                                       struct sl811h_ep, schedule);
+       }
+
+       if (unlikely(list_empty(&ep->queue))) {
+               DBG("empty %p queue?\n", ep);
+               return NULL;
+       }
+
+       req = container_of(ep->queue.next, struct sl811h_req, queue);
+       urb = req->urb;
+       control = ep->defctrl;
+
+       /* if this frame doesn't have enough time left to transfer this
+        * packet, wait till the next frame.  too-simple algorithm...
+        */
+       fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6;
+       fclock -= 100;          /* setup takes not much time */
+       if (urb->dev->speed == USB_SPEED_LOW) {
+               if (control & SL11H_HCTLMASK_PREAMBLE) {
+                       /* also note erratum 1: some hubs won't work */
+                       fclock -= 800;
+               }
+               fclock -= ep->maxpacket << 8;
+
+               /* erratum 2: AFTERSOF only works for fullspeed */
+               if (fclock < 0) {
+                       if (ep->period)
+                               sl811->stat_overrun++;
+                       sofirq_on(sl811);
+                       return NULL;
+               }
+       } else {
+               fclock -= 12000 / 19;   /* 19 64byte packets/msec */
+               if (fclock < 0) {
+                       if (ep->period)
+                               sl811->stat_overrun++;
+                       control |= SL11H_HCTLMASK_AFTERSOF;
+
+               /* throttle bulk/control irq noise */
+               } else if (ep->nak_count)
+                       control |= SL11H_HCTLMASK_AFTERSOF;
+       }
+
+
+       switch (ep->nextpid) {
+       case USB_PID_IN:
+               in_packet(sl811, ep, urb, bank, control);
+               break;
+       case USB_PID_OUT:
+               out_packet(sl811, ep, urb, bank, control);
+               break;
+       case USB_PID_SETUP:
+               setup_packet(sl811, ep, urb, bank, control);
+               break;
+       case USB_PID_ACK:               /* for control status */
+               status_packet(sl811, ep, urb, bank, control);
+               break;
+       default:
+               DBG("bad ep%p pid %02x\n", ep, ep->nextpid);
+               ep = NULL;
+       }
+       return ep;
+}
+
+#define MIN_JIFFIES    ((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2)
+
+static inline void start_transfer(struct sl811 *sl811)
+{
+       if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))
+               return;
+       if (sl811->active_a == NULL) {
+               sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF));
+               if (sl811->active_a != NULL)
+                       sl811->jiffies_a = jiffies + MIN_JIFFIES;
+       }
+#ifdef USE_B
+       if (sl811->active_b == NULL) {
+               sl811->active_b = start(sl811, SL811_EP_B(SL811_HOST_BUF));
+               if (sl811->active_b != NULL)
+                       sl811->jiffies_b = jiffies + MIN_JIFFIES;
+       }
+#endif
+}
+
+static void finish_request(
+       struct sl811            *sl811,
+       struct sl811h_ep        *ep,
+       struct sl811h_req       *req,
+       struct pt_regs          *regs,
+       int                     status
+) __releases(sl811->lock) __acquires(sl811->lock)
+{
+       unsigned                i;
+       struct urb              *urb = req->urb;
+
+       list_del(&req->queue);
+       kfree(req);
+       urb->hcpriv = NULL;
+
+       if (usb_pipecontrol(urb->pipe))
+               ep->nextpid = USB_PID_SETUP;
+
+       spin_lock(&urb->lock);
+       if (urb->status == -EINPROGRESS)
+               urb->status = status;
+       spin_unlock(&urb->lock);
+
+       spin_unlock(&sl811->lock);
+       usb_hcd_giveback_urb(&sl811->hcd, urb, regs);
+       spin_lock(&sl811->lock);
+
+       /* leave active endpoints in the schedule */
+       if (!list_empty(&ep->queue))
+               return;
+
+       /* async deschedule? */
+       if (!list_empty(&ep->schedule)) {
+               list_del_init(&ep->schedule);
+               if (ep == sl811->next_async)
+                       sl811->next_async = NULL;
+               return;
+       }
+
+       /* periodic deschedule */
+       DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+       for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+               struct sl811h_ep        *temp;
+               struct sl811h_ep        **prev = &sl811->periodic[i];
+
+               while (*prev && ((temp = *prev) != ep))
+                       prev = &temp->next;
+               if (*prev)
+                       *prev = ep->next;
+               sl811->load[i] -= ep->load;
+       }       
+       ep->branch = PERIODIC_SIZE;
+       sl811->periodic_count--;
+       hcd_to_bus(&sl811->hcd)->bandwidth_allocated
+               -= ep->load / ep->period;
+       if (ep == sl811->next_periodic)
+               sl811->next_periodic = ep->next;
+
+       /* we might turn SOFs back on again for the async schedule */
+       if (sl811->periodic_count == 0)
+               sofirq_off(sl811);
+}
+
+static void
+done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs)
+{
+       u8                      status;
+       struct sl811h_req       *req;
+       struct urb              *urb;
+       int                     urbstat = -EINPROGRESS;
+
+       if (unlikely(!ep))
+               return;
+
+       status = sl811_read(sl811, bank + SL11H_PKTSTATREG);
+
+       req = container_of(ep->queue.next, struct sl811h_req, queue);
+       urb = req->urb;
+
+       /* we can safely ignore NAKs */
+       if (status & SL11H_STATMASK_NAK) {
+               // PACKET("...NAK_%02x qh%p\n", bank, ep);
+               if (!ep->period)
+                       ep->nak_count++;
+               ep->error_count = 0;
+
+       /* ACK advances transfer, toggle, and maybe queue */
+       } else if (status & SL11H_STATMASK_ACK) {
+               struct usb_device       *udev = urb->dev;
+               int                     len;
+               unsigned char           *buf;
+
+               /* urb->iso_frame_desc is currently ignored here... */
+
+               ep->nak_count = ep->error_count = 0;
+               switch (ep->nextpid) {
+               case USB_PID_OUT:
+                       // PACKET("...ACK/out_%02x qh%p\n", bank, ep);
+                       urb->actual_length += ep->length;
+                       usb_dotoggle(udev, ep->epnum, 1);
+                       if (urb->actual_length
+                                       == urb->transfer_buffer_length) {
+                               if (usb_pipecontrol(urb->pipe))
+                                       ep->nextpid = USB_PID_ACK;
+
+                               /* some bulk protocols terminate OUT transfers
+                                * by a short packet, using ZLPs not padding.
+                                */
+                               else if (ep->length < ep->maxpacket
+                                               || !(urb->transfer_flags
+                                                       & URB_ZERO_PACKET))
+                                       urbstat = 0;
+                       }
+                       break;
+               case USB_PID_IN:
+                       // PACKET("...ACK/in_%02x qh%p\n", bank, ep);
+                       buf = urb->transfer_buffer + urb->actual_length;
+                       prefetchw(buf);
+                       len = ep->maxpacket - sl811_read(sl811,
+                                               bank + SL11H_XFERCNTREG);
+                       if (len > ep->length) {
+                               len = ep->length;
+                               urb->status = -EOVERFLOW;
+                       }
+                       urb->actual_length += len;
+                       sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0),
+                                       buf, len);
+                       usb_dotoggle(udev, ep->epnum, 0);
+                       if (urb->actual_length == urb->transfer_buffer_length)
+                               urbstat = 0;
+                       else if (len < ep->maxpacket) {
+                               if (urb->transfer_flags & URB_SHORT_NOT_OK)
+                                       urbstat = -EREMOTEIO;
+                               else
+                                       urbstat = 0;
+                       }
+                       if (usb_pipecontrol(urb->pipe)
+                                       && (urbstat == -EREMOTEIO
+                                               || urbstat == 0)) {
+
+                               /* NOTE if the status stage STALLs (why?),
+                                * this reports the wrong urb status.
+                                */
+                               spin_lock(&urb->lock);
+                               if (urb->status == -EINPROGRESS)
+                                       urb->status = urbstat;
+                               spin_unlock(&urb->lock);
+
+                               req = NULL;
+                               ep->nextpid = USB_PID_ACK;
+                       }
+                       break;
+               case USB_PID_SETUP:
+                       // PACKET("...ACK/setup_%02x qh%p\n", bank, ep);
+                       if (urb->transfer_buffer_length == urb->actual_length)
+                               ep->nextpid = USB_PID_ACK;
+                       else if (usb_pipeout(urb->pipe)) {
+                               usb_settoggle(udev, 0, 1, 1);
+                               ep->nextpid = USB_PID_OUT;
+                       } else {
+                               usb_settoggle(udev, 0, 0, 1);
+                               ep->nextpid = USB_PID_IN;
+                       }
+                       break;
+               case USB_PID_ACK:
+                       // PACKET("...ACK/status_%02x qh%p\n", bank, ep);
+                       urbstat = 0;
+                       break;
+               }
+
+       /* STALL stops all transfers */
+       } else if (status & SL11H_STATMASK_STALL) {
+               PACKET("...STALL_%02x qh%p\n", bank, ep);
+               ep->nak_count = ep->error_count = 0;
+               urbstat = -EPIPE;
+
+       /* error? retry, until "3 strikes" */
+       } else if (++ep->error_count >= 3) {
+               if (status & SL11H_STATMASK_TMOUT)
+                       urbstat = -ETIMEDOUT;
+               else if (status & SL11H_STATMASK_OVF)
+                       urbstat = -EOVERFLOW;
+               else
+                       urbstat = -EPROTO;
+               ep->error_count = 0;
+               PACKET("...3STRIKES_%02x %02x qh%p stat %d\n",
+                               bank, status, ep, urbstat);
+       }
+
+       if ((urbstat != -EINPROGRESS || urb->status != -EINPROGRESS)
+                       && req)
+               finish_request(sl811, ep, req, regs, urbstat);
+}
+
+static inline u8 checkdone(struct sl811 *sl811)
+{
+       u8      ctl;
+       u8      irqstat = 0;
+
+       if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) {
+               ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG));
+               if (ctl & SL11H_HCTLMASK_ARM)
+                       sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
+               DBG("%s DONE_A: ctrl %02x sts %02x\n",
+                       (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
+                       ctl,
+                       sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
+               irqstat |= SL11H_INTMASK_DONE_A;
+       }
+#ifdef USE_B
+       if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) {
+               ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG));
+               if (ctl & SL11H_HCTLMASK_ARM)
+                       sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
+               DBG("%s DONE_B: ctrl %02x sts %02x\n", ctl,
+                       (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost",
+                       ctl,
+                       sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
+               irqstat |= SL11H_INTMASK_DONE_A;
+       }
+#endif
+       return irqstat;
+}
+
+static irqreturn_t sl811h_irq(int irq, void *_sl811, struct pt_regs *regs)
+{
+       struct sl811    *sl811 = _sl811;
+       u8              irqstat;
+       irqreturn_t     ret = IRQ_NONE;
+       unsigned        retries = 5;
+
+       spin_lock(&sl811->lock);
+
+retry:
+       irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP;
+       if (irqstat) {
+               sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);
+               irqstat &= sl811->irq_enable;
+       }
+
+#ifdef QUIRK2
+       /* this may no longer be necessary ... */
+       if (irqstat == 0 && ret == IRQ_NONE) {
+               irqstat = checkdone(sl811);
+               if (irqstat && irq != ~0)
+                       sl811->stat_lost++;
+       }
+#endif
+
+       /* USB packets, not necessarily handled in the order they're
+        * issued ... that's fine if they're different endpoints.
+        */
+       if (irqstat & SL11H_INTMASK_DONE_A) {
+               done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF), regs);
+               sl811->active_a = NULL;
+               sl811->stat_a++;
+       }
+#ifdef USE_B
+       if (irqstat & SL11H_INTMASK_DONE_B) {
+               done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF), regs);
+               sl811->active_b = NULL;
+               sl811->stat_b++;
+       }
+#endif
+       if (irqstat & SL11H_INTMASK_SOFINTR) {
+               unsigned index;
+
+               index = sl811->frame++ % (PERIODIC_SIZE - 1);
+               sl811->stat_sof++;
+
+               /* be graceful about almost-inevitable periodic schedule
+                * overruns:  continue the previous frame's transfers iff
+                * this one has nothing scheduled.
+                */
+               if (sl811->next_periodic) {
+                       // ERR("overrun to slot %d\n", index);
+                       sl811->stat_overrun++;
+               }
+               if (sl811->periodic[index])
+                       sl811->next_periodic = sl811->periodic[index];
+       }
+
+       /* khubd manages debouncing and wakeup */
+       if (irqstat & SL11H_INTMASK_INSRMV) {
+               sl811->stat_insrmv++;
+
+               /* most stats are reset for each VBUS session */
+               sl811->stat_wake = 0;
+               sl811->stat_sof = 0;
+               sl811->stat_a = 0;
+               sl811->stat_b = 0;
+               sl811->stat_lost = 0;
+
+               sl811->ctrl1 = 0;
+               sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+               sl811->irq_enable = SL11H_INTMASK_INSRMV;
+               sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+               /* usbcore nukes other pending transactions on disconnect */
+               if (sl811->active_a) {
+                       sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
+                       finish_request(sl811, sl811->active_a,
+                               container_of(sl811->active_a->queue.next,
+                                       struct sl811h_req, queue),
+                               NULL, -ESHUTDOWN);
+                       sl811->active_a = NULL;
+               }
+#ifdef USE_B
+               if (sl811->active_b) {
+                       sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
+                       finish_request(sl811, sl811->active_b,
+                               container_of(sl811->active_b->queue.next,
+                                       struct sl811h_req, queue),
+                               NULL, -ESHUTDOWN);
+                       sl811->active_b = NULL;
+               }
+#endif
+
+               /* port status seems wierd until after reset, so
+                * force the reset and make khubd clean up later.
+                */
+               sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
+                               | (1 << USB_PORT_FEAT_CONNECTION);
+
+       } else if (irqstat & SL11H_INTMASK_RD) {
+               if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) {
+                       DBG("wakeup\n");
+                       sl811->port1 |= 1 << USB_PORT_FEAT_C_SUSPEND;
+                       sl811->stat_wake++;
+               } else
+                       irqstat &= ~SL11H_INTMASK_RD;
+       }
+
+       if (irqstat) {
+               if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
+                       start_transfer(sl811);
+               ret = IRQ_HANDLED;
+               sl811->hcd.saw_irq = 1;
+               if (retries--)
+                       goto retry;
+       }
+
+       if (sl811->periodic_count == 0 && list_empty(&sl811->async)) 
+               sofirq_off(sl811);
+       sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+
+       spin_unlock(&sl811->lock);
+
+       return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* usb 1.1 says max 90% of a frame is available for periodic transfers.
+ * this driver doesn't promise that much since it's got to handle an
+ * IRQ per packet; irq handling latencies also use up that time.
+ */
+#define        MAX_PERIODIC_LOAD       500     /* out of 1000 usec */
+
+static int balance(struct sl811 *sl811, u16 period, u16 load)
+{
+       int     i, branch = -ENOSPC;
+
+       /* search for the least loaded schedule branch of that period
+        * which has enough bandwidth left unreserved.
+        */
+       for (i = 0; i < period ; i++) {
+               if (branch < 0 || sl811->load[branch] > sl811->load[i]) {
+                       int     j;
+
+                       for (j = i; j < PERIODIC_SIZE; j += period) {
+                               if ((sl811->load[j] + load)
+                                               > MAX_PERIODIC_LOAD)
+                                       break;
+                       }
+                       if (j < PERIODIC_SIZE)
+                               continue;
+                       branch = i; 
+               }
+       }
+       return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int sl811h_urb_enqueue(
+       struct usb_hcd  *hcd,
+       struct urb      *urb,
+       int             mem_flags
+) {
+       struct sl811            *sl811 = hcd_to_sl811(hcd);
+       struct usb_device       *udev = urb->dev;
+       struct hcd_dev          *hdev = (struct hcd_dev *) udev->hcpriv;
+       unsigned int            pipe = urb->pipe;
+       int                     is_out = !usb_pipein(pipe);
+       int                     type = usb_pipetype(pipe);
+       int                     epnum = usb_pipeendpoint(pipe);
+       struct sl811h_ep        *ep = NULL;
+       struct sl811h_req       *req;
+       unsigned long           flags;
+       int                     i;
+       int                     retval = 0;
+
+#ifdef DISABLE_ISO
+       if (type == PIPE_ISOCHRONOUS)
+               return -ENOSPC;
+#endif
+
+       /* avoid all allocations within spinlocks: request or endpoint */
+       urb->hcpriv = req = kmalloc(sizeof *req, mem_flags);
+       if (!req)
+               return -ENOMEM;
+       req->urb = urb;
+
+       i = epnum << 1;
+       if (i && is_out)
+               i |= 1;
+       if (!hdev->ep[i])
+               ep = kcalloc(1, sizeof *ep, mem_flags);
+
+       spin_lock_irqsave(&sl811->lock, flags);
+
+       /* don't submit to a dead or disabled port */
+       if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
+                       || !HCD_IS_RUNNING(sl811->hcd.state)) {
+               retval = -ENODEV;
+               goto fail;
+       }
+
+       if (hdev->ep[i]) {
+               kfree(ep);
+               ep = hdev->ep[i];
+       } else if (!ep) {
+               retval = -ENOMEM;
+               goto fail;
+
+       } else {
+               INIT_LIST_HEAD(&ep->queue);
+               INIT_LIST_HEAD(&ep->schedule);
+               ep->udev = usb_get_dev(udev);
+               ep->epnum = epnum;
+               ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+               ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE;
+               usb_settoggle(udev, epnum, is_out, 0);
+
+               if (type == PIPE_CONTROL)
+                       ep->nextpid = USB_PID_SETUP;
+               else if (is_out)
+                       ep->nextpid = USB_PID_OUT;
+               else
+                       ep->nextpid = USB_PID_IN;
+
+               if (ep->maxpacket > H_MAXPACKET) {
+                       /* iso packets up to 240 bytes could work... */
+                       DBG("dev %d ep%d maxpacket %d\n",
+                               udev->devnum, epnum, ep->maxpacket);
+                       retval = -EINVAL;
+                       goto fail;
+               }
+
+               if (udev->speed == USB_SPEED_LOW) {
+                       /* send preamble for external hub? */
+                       if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD))
+                               ep->defctrl |= SL11H_HCTLMASK_PREAMBLE;
+               }
+               switch (type) {
+               case PIPE_ISOCHRONOUS:
+               case PIPE_INTERRUPT:
+                       if (urb->interval > PERIODIC_SIZE)
+                               urb->interval = PERIODIC_SIZE;
+                       ep->period = urb->interval;
+                       ep->branch = PERIODIC_SIZE;
+                       if (type == PIPE_ISOCHRONOUS)
+                               ep->defctrl |= SL11H_HCTLMASK_ISOCH;
+                       ep->load = usb_calc_bus_time(udev->speed, !is_out,
+                               (type == PIPE_ISOCHRONOUS),
+                               usb_maxpacket(udev, pipe, is_out))
+                                       / 1000;
+                       break;
+               }
+
+               hdev->ep[i] = ep;
+       }
+
+       /* maybe put endpoint into schedule */
+       switch (type) {
+       case PIPE_CONTROL:
+       case PIPE_BULK:
+               if (list_empty(&ep->schedule))
+                       list_add_tail(&ep->schedule, &sl811->async);
+               break;
+       case PIPE_ISOCHRONOUS:
+       case PIPE_INTERRUPT:
+               urb->interval = ep->period;
+               if (ep->branch < PERIODIC_SIZE)
+                       break;
+
+               retval = balance(sl811, ep->period, ep->load);
+               if (retval < 0)
+                       goto fail;
+               ep->branch = retval;
+               retval = 0;
+               urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))
+                                       + ep->branch;
+
+               /* sort each schedule branch by period (slow before fast)
+                * to share the faster parts of the tree without needing
+                * dummy/placeholder nodes
+                */
+               DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch);
+               for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) {
+                       struct sl811h_ep        **prev = &sl811->periodic[i];
+                       struct sl811h_ep        *here = *prev;
+
+                       while (here && ep != here) {
+                               if (ep->period > here->period)
+                                       break;
+                               prev = &here->next;
+                               here = *prev;
+                       }
+                       if (ep != here) {
+                               ep->next = here;
+                               *prev = ep;
+                       }
+                       sl811->load[i] += ep->load;
+               }
+               sl811->periodic_count++;
+               hcd_to_bus(&sl811->hcd)->bandwidth_allocated
+                               += ep->load / ep->period;
+               sofirq_on(sl811);
+       }
+
+       /* in case of unlink-during-submit */
+       spin_lock(&urb->lock);
+       if (urb->status != -EINPROGRESS) {
+               spin_unlock(&urb->lock);
+               finish_request(sl811, ep, req, NULL, 0);
+               req = NULL;
+               retval = 0;
+               goto fail;
+       }
+       list_add_tail(&req->queue, &ep->queue);
+       spin_unlock(&urb->lock);
+
+       start_transfer(sl811);
+       sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+fail:
+       spin_unlock_irqrestore(&sl811->lock, flags);
+       if (retval)
+               kfree(req);
+       return retval;
+}
+
+static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+       struct sl811            *sl811 = hcd_to_sl811(hcd);
+       struct usb_device       *udev = urb->dev;
+       struct hcd_dev          *hdev = (struct hcd_dev *) udev->hcpriv;
+       unsigned int            pipe = urb->pipe;
+       int                     is_out = !usb_pipein(pipe);
+       unsigned long           flags;
+       unsigned                i;
+       struct sl811h_ep        *ep;
+       struct sl811h_req       *req = urb->hcpriv;
+       int                     retval = 0;
+
+       i = usb_pipeendpoint(pipe) << 1;
+       if (i && is_out)
+               i |= 1;
+
+       spin_lock_irqsave(&sl811->lock, flags);
+       ep = hdev->ep[i];
+       if (ep) {
+               /* finish right away if this urb can't be active ...
+                * note that some drivers wrongly expect delays
+                */
+               if (ep->queue.next != &req->queue) {
+                       /* not front of queue?  never active */
+
+               /* for active transfers, we expect an IRQ */
+               } else if (sl811->active_a == ep) {
+                       if (time_before_eq(sl811->jiffies_a, jiffies)) {
+                               /* happens a lot with lowspeed?? */
+                               DBG("giveup on DONE_A: ctrl %02x sts %02x\n",
+                                       sl811_read(sl811,
+                                               SL811_EP_A(SL11H_HOSTCTLREG)),
+                                       sl811_read(sl811,
+                                               SL811_EP_A(SL11H_PKTSTATREG)));
+                               sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
+                                               0);
+                               sl811->active_a = NULL;
+                       } else
+                               req = NULL;
+#ifdef USE_B
+               } else if (sl811->active_b == ep) {
+                       if (time_before_eq(sl811->jiffies_a, jiffies)) {
+                               /* happens a lot with lowspeed?? */
+                               DBG("giveup on DONE_B: ctrl %02x sts %02x\n",
+                                       sl811_read(sl811,
+                                               SL811_EP_B(SL11H_HOSTCTLREG)),
+                                       sl811_read(sl811,
+                                               SL811_EP_B(SL11H_PKTSTATREG)));
+                               sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG),
+                                               0);
+                               sl811->active_b = NULL;
+                       } else
+                               req = NULL;
+#endif
+               } else {
+                       /* front of queue for inactive endpoint */
+               }
+
+               if (req)
+                       finish_request(sl811, ep, req, NULL, 0);
+               else
+                       VDBG("dequeue, urb %p active %s; wait4irq\n", urb,
+                               (sl811->active_a == ep) ? "A" : "B");
+       } else
+               retval = -EINVAL;
+       spin_unlock_irqrestore(&sl811->lock, flags);
+       return retval;
+}
+
+static void
+sl811h_endpoint_disable(struct usb_hcd *hcd, struct hcd_dev *hdev, int epnum)
+{
+       struct sl811            *sl811 = hcd_to_sl811(hcd);
+       struct sl811h_ep        *ep;
+       unsigned long           flags;
+       int                     i;
+
+       i = (epnum & 0xf) << 1;
+       if (epnum && !(epnum & USB_DIR_IN))
+               i |= 1;
+
+       spin_lock_irqsave(&sl811->lock, flags);
+       ep = hdev->ep[i];
+       hdev->ep[i] = NULL;
+       spin_unlock_irqrestore(&sl811->lock, flags);
+
+       if (ep) {
+               /* assume we'd just wait for the irq */
+               if (!list_empty(&ep->queue))
+                       msleep(3);
+               if (!list_empty(&ep->queue))
+                       WARN("ep %p not empty?\n", ep);
+
+               usb_put_dev(ep->udev);
+               kfree(ep);
+       }
+       return;
+}
+
+static int
+sl811h_get_frame(struct usb_hcd *hcd)
+{
+       struct sl811 *sl811 = hcd_to_sl811(hcd);
+
+       /* wrong except while periodic transfers are scheduled;
+        * never matches the on-the-wire frame;
+        * subject to overruns.
+        */
+       return sl811->frame;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* the virtual root hub timer IRQ checks for hub status */
+static int
+sl811h_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+       struct sl811 *sl811 = hcd_to_sl811(hcd);
+#ifdef QUIRK3
+       unsigned long flags;
+
+       /* non-SMP HACK: use root hub timer as i/o watchdog
+        * this seems essential when SOF IRQs aren't in use...
+        */
+       local_irq_save(flags);
+       if (!timer_pending(&sl811->timer)) {
+               if (sl811h_irq(~0, sl811, NULL) != IRQ_NONE)
+                       sl811->stat_lost++;
+       }
+       local_irq_restore(flags);
+#endif
+
+       if (!(sl811->port1 & (0xffff << 16)))
+               return 0;
+
+       /* tell khubd port 1 changed */
+       *buf = (1 << 1);
+       return 1;
+}
+
+static void
+sl811h_hub_descriptor (
+       struct sl811                    *sl811,
+       struct usb_hub_descriptor       *desc
+) {
+       u16             temp = 0;
+
+       desc->bDescriptorType = 0x29;
+       desc->bHubContrCurrent = 0;
+
+       desc->bNbrPorts = 1;
+       desc->bDescLength = 9;
+
+       /* per-port power switching (gang of one!), or none */
+       desc->bPwrOn2PwrGood = 0;
+       if (sl811->board && sl811->board->port_power) {
+               desc->bPwrOn2PwrGood = sl811->board->potpg;
+               if (!desc->bPwrOn2PwrGood)
+                       desc->bPwrOn2PwrGood = 10;
+               temp = 0x0001;
+       } else
+               temp = 0x0002;
+
+       /* no overcurrent errors detection/handling */
+       temp |= 0x0010;
+
+       desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp);
+
+       /* two bitmaps:  ports removable, and legacy PortPwrCtrlMask */
+       desc->bitmap[0] = 1 << 1;
+       desc->bitmap[1] = ~0;
+}
+
+static void
+sl811h_timer(unsigned long _sl811)
+{
+       struct sl811    *sl811 = (void *) _sl811;
+       unsigned long   flags;
+       u8              irqstat;
+       u8              signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE;
+       const u32       mask = (1 << USB_PORT_FEAT_CONNECTION)
+                               | (1 << USB_PORT_FEAT_ENABLE)
+                               | (1 << USB_PORT_FEAT_LOWSPEED);
+
+       spin_lock_irqsave(&sl811->lock, flags);
+
+       /* stop special signaling */
+       sl811->ctrl1 &= ~SL11H_CTL1MASK_FORCE;
+       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+       udelay(3);
+
+       irqstat = sl811_read(sl811, SL11H_IRQ_STATUS);
+
+       switch (signaling) {
+       case SL11H_CTL1MASK_SE0:
+               DBG("end reset\n");
+               sl811->port1 = (1 << USB_PORT_FEAT_C_RESET)
+                               | (1 << USB_PORT_FEAT_POWER);
+               sl811->ctrl1 = 0;
+               /* don't wrongly ack RD */
+               if (irqstat & SL11H_INTMASK_INSRMV)
+                       irqstat &= ~SL11H_INTMASK_RD;
+               break;
+       case SL11H_CTL1MASK_K:
+               DBG("end resume\n");
+               sl811->port1 &= ~(1 << USB_PORT_FEAT_SUSPEND);
+               break;
+       default:
+               DBG("odd timer signaling: %02x\n", signaling);
+               break;
+       }
+       sl811_write(sl811, SL11H_IRQ_STATUS, irqstat);
+
+       if (irqstat & SL11H_INTMASK_RD) {
+               /* usbcore nukes all pending transactions on disconnect */
+               if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION))
+                       sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
+                                       | (1 << USB_PORT_FEAT_C_ENABLE);
+               sl811->port1 &= ~mask;
+               sl811->irq_enable = SL11H_INTMASK_INSRMV;
+       } else {
+               sl811->port1 |= mask;
+               if (irqstat & SL11H_INTMASK_DP)
+                       sl811->port1 &= ~(1 << USB_PORT_FEAT_LOWSPEED);
+               sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD;
+       }
+
+       if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) {
+               u8      ctrl2 = SL811HS_CTL2_INIT;
+
+               sl811->irq_enable |= SL11H_INTMASK_DONE_A;
+#ifdef USE_B
+               sl811->irq_enable |= SL11H_INTMASK_DONE_B;
+#endif
+               if (sl811->port1 & (1 << USB_PORT_FEAT_LOWSPEED)) {
+                       sl811->ctrl1 |= SL11H_CTL1MASK_LSPD;
+                       ctrl2 |= SL811HS_CTL2MASK_DSWAP;
+               }
+
+               /* start SOFs flowing, kickstarting with A registers */
+               sl811->ctrl1 |= SL11H_CTL1MASK_SOF_ENA;
+               sl811_write(sl811, SL11H_SOFLOWREG, 0xe0);
+               sl811_write(sl811, SL811HS_CTLREG2, ctrl2);
+
+               /* autoincrementing */
+               sl811_write(sl811, SL811_EP_A(SL11H_BUFLNTHREG), 0);
+               writeb(SL_SOF, sl811->data_reg);
+               writeb(0, sl811->data_reg);
+               sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG),
+                               SL11H_HCTLMASK_ARM);
+
+               /* khubd provides debounce delay */
+       } else {
+               sl811->ctrl1 = 0;
+       }
+       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+       /* reenable irqs */
+       sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
+       spin_unlock_irqrestore(&sl811->lock, flags);
+}
+
+static int
+sl811h_hub_control(
+       struct usb_hcd  *hcd,
+       u16             typeReq,
+       u16             wValue,
+       u16             wIndex,
+       char            *buf,
+       u16             wLength
+) {
+       struct sl811    *sl811 = hcd_to_sl811(hcd);
+       int             retval = 0;
+       unsigned long   flags;
+
+       spin_lock_irqsave(&sl811->lock, flags);
+
+       switch (typeReq) {
+       case ClearHubFeature:
+       case SetHubFeature:
+               switch (wValue) {
+               case C_HUB_OVER_CURRENT:
+               case C_HUB_LOCAL_POWER:
+                       break;
+               default:
+                       goto error;
+               }
+               break;
+       case ClearPortFeature:
+               if (wIndex != 1 || wLength != 0)
+                       goto error;
+
+               switch (wValue) {
+               case USB_PORT_FEAT_ENABLE:
+                       sl811->port1 &= (1 << USB_PORT_FEAT_POWER);
+                       sl811->ctrl1 = 0;
+                       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+                       sl811->irq_enable = SL11H_INTMASK_INSRMV;
+                       sl811_write(sl811, SL11H_IRQ_ENABLE,
+                                               sl811->irq_enable);
+                       break;
+               case USB_PORT_FEAT_SUSPEND:
+                       if (!(sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)))
+                               break;
+
+                       /* 20 msec of resume/K signaling, other irqs blocked */
+                       DBG("start resume...\n");
+                       sl811->irq_enable = 0;
+                       sl811_write(sl811, SL11H_IRQ_ENABLE,
+                                               sl811->irq_enable);
+                       sl811->ctrl1 |= SL11H_CTL1MASK_K;
+                       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+
+                       mod_timer(&sl811->timer, jiffies
+                                       + msecs_to_jiffies(20));
+                       break;
+               case USB_PORT_FEAT_POWER:
+                       port_power(sl811, 0);
+                       break;
+               case USB_PORT_FEAT_C_ENABLE:
+               case USB_PORT_FEAT_C_SUSPEND:
+               case USB_PORT_FEAT_C_CONNECTION:
+               case USB_PORT_FEAT_C_OVER_CURRENT:
+               case USB_PORT_FEAT_C_RESET:
+                       break;
+               default:
+                       goto error;
+               }
+               sl811->port1 &= ~(1 << wValue);
+               break;
+       case GetHubDescriptor:
+               sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf);
+               break;
+       case GetHubStatus:
+               *(__le32 *) buf = cpu_to_le32(0);
+               break;
+       case GetPortStatus:
+               if (wIndex != 1)
+                       goto error;
+               *(__le32 *) buf = cpu_to_le32(sl811->port1);
+
+#ifndef        VERBOSE
+       if (*(u16*)(buf+2))     /* only if wPortChange is interesting */
+#endif
+               DBG("GetPortStatus %08x\n", sl811->port1);
+               break;
+       case SetPortFeature:
+               if (wIndex != 1 || wLength != 0)
+                       goto error;
+               switch (wValue) {
+               case USB_PORT_FEAT_SUSPEND:
+                       if (sl811->port1 & (1 << USB_PORT_FEAT_RESET))
+                               goto error;
+                       if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)))
+                               goto error;
+
+                       DBG("suspend...\n");
+                       sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA;
+                       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+                       break;
+               case USB_PORT_FEAT_POWER:
+                       port_power(sl811, 1);
+                       break;
+               case USB_PORT_FEAT_RESET:
+                       if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))
+                               goto error;
+                       if (!(sl811->port1 & (1 << USB_PORT_FEAT_POWER)))
+                               break;
+
+                       /* 50 msec of reset/SE0 signaling, irqs blocked */
+                       sl811->irq_enable = 0;
+                       sl811_write(sl811, SL11H_IRQ_ENABLE,
+                                               sl811->irq_enable);
+                       sl811->ctrl1 = SL11H_CTL1MASK_SE0;
+                       sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
+                       sl811->port1 |= (1 << USB_PORT_FEAT_RESET);
+                       mod_timer(&sl811->timer, jiffies
+                                       + msecs_to_jiffies(50));
+                       break;
+               default:
+                       goto error;
+               }
+               sl811->port1 |= 1 << wValue;
+               break;
+
+       default:
+error:
+               /* "protocol stall" on error */
+               retval = -EPIPE;
+       }
+
+       spin_unlock_irqrestore(&sl811->lock, flags);
+       return retval;
+}
+
+#ifdef CONFIG_PM
+
+static int
+sl811h_hub_suspend(struct usb_hcd *hcd)
+{
+       // SOFs off
+       DBG("%s\n", __FUNCTION__);
+       return 0;
+}
+
+static int
+sl811h_hub_resume(struct usb_hcd *hcd)
+{
+       // SOFs on
+       DBG("%s\n", __FUNCTION__);
+       return 0;
+}
+
+#else
+
+#define        sl811h_hub_suspend      NULL
+#define        sl811h_hub_resume       NULL
+
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILE
+
+static inline void create_debug_file(struct sl811 *sl811) { }
+static inline void remove_debug_file(struct sl811 *sl811) { }
+
+#else
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static void dump_irq(struct seq_file *s, char *label, u8 mask)
+{
+       seq_printf(s, "%s %02x%s%s%s%s%s%s\n", label, mask,
+               (mask & SL11H_INTMASK_DONE_A) ? " done_a" : "",
+               (mask & SL11H_INTMASK_DONE_B) ? " done_b" : "",
+               (mask & SL11H_INTMASK_SOFINTR) ? " sof" : "",
+               (mask & SL11H_INTMASK_INSRMV) ? " ins/rmv" : "",
+               (mask & SL11H_INTMASK_RD) ? " rd" : "",
+               (mask & SL11H_INTMASK_DP) ? " dp" : "");
+}
+
+static int proc_sl811h_show(struct seq_file *s, void *unused)
+{
+       struct sl811            *sl811 = s->private;
+       struct sl811h_ep        *ep;
+       unsigned                i;
+
+       seq_printf(s, "%s\n%s version %s\nportstatus[1] = %08x\n",
+               sl811->hcd.product_desc,
+               hcd_name, DRIVER_VERSION,
+               sl811->port1);
+
+       seq_printf(s, "insert/remove: %ld\n", sl811->stat_insrmv);
+       seq_printf(s, "current session:  done_a %ld done_b %ld "
+                       "wake %ld sof %ld overrun %ld lost %ld\n\n",
+               sl811->stat_a, sl811->stat_b,
+               sl811->stat_wake, sl811->stat_sof,
+               sl811->stat_overrun, sl811->stat_lost);
+
+       spin_lock_irq(&sl811->lock);
+
+       if (sl811->ctrl1 & SL11H_CTL1MASK_SUSPEND)
+               seq_printf(s, "(suspended)\n\n");
+       else {
+               u8      t = sl811_read(sl811, SL11H_CTLREG1);
+
+               seq_printf(s, "ctrl1 %02x%s%s%s%s\n", t,
+                       (t & SL11H_CTL1MASK_SOF_ENA) ? " sofgen" : "",
+                       ({char *s; switch (t & SL11H_CTL1MASK_FORCE) {
+                       case SL11H_CTL1MASK_NORMAL: s = ""; break;
+                       case SL11H_CTL1MASK_SE0: s = " se0/reset"; break;
+                       case SL11H_CTL1MASK_K: s = " k/resume"; break;
+                       default: s = "j"; break;
+                       }; s; }),
+                       (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "",
+                       (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : "");
+
+               dump_irq(s, "irq_enable",
+                               sl811_read(sl811, SL11H_IRQ_ENABLE));
+               dump_irq(s, "irq_status",
+                               sl811_read(sl811, SL11H_IRQ_STATUS));
+               seq_printf(s, "frame clocks remaining:  %d\n",
+                               sl811_read(sl811, SL11H_SOFTMRREG) << 6);
+       }
+
+       seq_printf(s, "A: qh%p ctl %02x sts %02x\n", sl811->active_a,
+               sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)),
+               sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG)));
+       seq_printf(s, "B: qh%p ctl %02x sts %02x\n", sl811->active_b,
+               sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)),
+               sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG)));
+       seq_printf(s, "\n");
+       list_for_each_entry (ep, &sl811->async, schedule) {
+               struct sl811h_req       *req;
+
+               seq_printf(s, "%s%sqh%p, ep%d%s, maxpacket %d"
+                                       " nak %d err %d\n",
+                       (ep == sl811->active_a) ? "(A) " : "",
+                       (ep == sl811->active_b) ? "(B) " : "",
+                       ep, ep->epnum,
+                       ({ char *s; switch (ep->nextpid) {
+                       case USB_PID_IN: s = "in"; break;
+                       case USB_PID_OUT: s = "out"; break;
+                       case USB_PID_SETUP: s = "setup"; break;
+                       case USB_PID_ACK: s = "status"; break;
+                       default: s = "?"; break;
+                       }; s;}),
+                       ep->maxpacket,
+                       ep->nak_count, ep->error_count);
+               list_for_each_entry (req, &ep->queue, queue) {
+                       seq_printf(s, "  urb%p, %d/%d\n", req->urb,
+                               req->urb->actual_length,
+                               req->urb->transfer_buffer_length);
+               }
+       }
+       if (!list_empty(&sl811->async))
+               seq_printf(s, "\n");
+
+       seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
+
+       for (i = 0; i < PERIODIC_SIZE; i++) {
+               ep = sl811->periodic[i];
+               if (!ep)
+                       continue;
+               seq_printf(s, "%2d [%3d]:\n", i, sl811->load[i]);
+
+               /* DUMB: prints shared entries multiple times */
+               do {
+                       seq_printf(s,
+                               "   %s%sqh%d/%p (%sdev%d ep%d%s max %d) "
+                                       "err %d\n",
+                               (ep == sl811->active_a) ? "(A) " : "",
+                               (ep == sl811->active_b) ? "(B) " : "",
+                               ep->period, ep,
+                               (ep->udev->speed == USB_SPEED_FULL)
+                                       ? "" : "ls ",
+                               ep->udev->devnum, ep->epnum,
+                               (ep->epnum == 0) ? ""
+                                       : ((ep->nextpid == USB_PID_IN)
+                                               ? "in"
+                                               : "out"),
+                               ep->maxpacket, ep->error_count);
+                       ep = ep->next;
+               } while (ep);
+       }
+
+       spin_unlock_irq(&sl811->lock);
+       seq_printf(s, "\n");
+
+       return 0;
+}
+
+static int proc_sl811h_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, proc_sl811h_show, PDE(inode)->data);
+}
+
+static struct file_operations proc_ops = {
+       .open           = proc_sl811h_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/* expect just one sl811 per system */
+static const char proc_filename[] = "driver/sl811h";
+
+static void create_debug_file(struct sl811 *sl811)
+{
+       struct proc_dir_entry *pde;
+
+       pde = create_proc_entry(proc_filename, 0, NULL);
+       if (pde == NULL)
+               return;
+
+       pde->proc_fops = &proc_ops;
+       pde->data = sl811;
+       sl811->pde = pde;
+}
+
+static void remove_debug_file(struct sl811 *sl811)
+{
+       if (sl811->pde)
+               remove_proc_entry(proc_filename, NULL);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void
+sl811h_stop(struct usb_hcd *hcd)
+{
+       struct sl811    *sl811 = hcd_to_sl811(hcd);
+       unsigned long   flags;
+
+       del_timer_sync(&sl811->hcd.rh_timer);
+
+       spin_lock_irqsave(&sl811->lock, flags);
+       port_power(sl811, 0);
+       spin_unlock_irqrestore(&sl811->lock, flags);
+}
+
+static int
+sl811h_start(struct usb_hcd *hcd)
+{
+       struct sl811            *sl811 = hcd_to_sl811(hcd);
+       struct usb_device       *udev;
+
+       /* chip has been reset, VBUS power is off */
+
+       udev = usb_alloc_dev(NULL, &sl811->hcd.self, 0);
+       if (!udev)
+               return -ENOMEM;
+
+       udev->speed = USB_SPEED_FULL;
+       hcd->state = USB_STATE_RUNNING;
+
+       if (sl811->board)
+               sl811->hcd.can_wakeup = sl811->board->can_wakeup;
+
+       if (hcd_register_root(udev, &sl811->hcd) != 0) {
+               usb_put_dev(udev);
+               sl811h_stop(hcd);
+               return -ENODEV;
+       }
+
+       if (sl811->board && sl811->board->power)
+               hub_set_power_budget(udev, sl811->board->power * 2);
+
+       return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct hc_driver sl811h_hc_driver = {
+       .description =          hcd_name,
+
+       /*
+        * generic hardware linkage
+        */
+       .flags =                HCD_USB11,
+
+       /*
+        * managing i/o requests and associated device resources
+        */
+       .urb_enqueue =          sl811h_urb_enqueue,
+       .urb_dequeue =          sl811h_urb_dequeue,
+       .endpoint_disable =     sl811h_endpoint_disable,
+
+       /*
+        * periodic schedule support
+        */
+       .get_frame_number =     sl811h_get_frame,
+
+       /*
+        * root hub support
+        */
+       .hub_status_data =      sl811h_hub_status_data,
+       .hub_control =          sl811h_hub_control,
+       .hub_suspend =          sl811h_hub_suspend,
+       .hub_resume =           sl811h_hub_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init_or_module
+sl811h_remove(struct device *dev)
+{
+       struct sl811            *sl811 = dev_get_drvdata(dev);
+       struct platform_device  *pdev;
+       struct resource         *res;
+
+       pdev = container_of(dev, struct platform_device, dev);
+
+       if (HCD_IS_RUNNING(sl811->hcd.state))
+               sl811->hcd.state = USB_STATE_QUIESCING;
+
+       usb_disconnect(&sl811->hcd.self.root_hub);
+       remove_debug_file(sl811);
+       sl811h_stop(&sl811->hcd);
+
+       if (!list_empty(&sl811->hcd.self.bus_list))
+               usb_deregister_bus(&sl811->hcd.self);
+
+       if (sl811->hcd.irq >= 0)
+               free_irq(sl811->hcd.irq, sl811);
+
+       if (sl811->data_reg)
+               iounmap(sl811->data_reg);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       release_mem_region(res->start, 1);
+
+       if (sl811->addr_reg) 
+               iounmap(sl811->addr_reg);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, 1);
+
+       kfree(sl811);
+       return 0;
+}
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+
+static int __init
+sl811h_probe(struct device *dev)
+{
+       struct sl811            *sl811;
+       struct platform_device  *pdev;
+       struct resource         *addr, *data;
+       int                     irq;
+       int                     status;
+       u8                      tmp;
+       unsigned long           flags;
+
+       /* basic sanity checks first.  board-specific init logic should
+        * have initialized these three resources and probably board
+        * specific platform_data.  we don't probe for IRQs, and do only
+        * minimal sanity checking.
+        */
+       pdev = container_of(dev, struct platform_device, dev);
+       if (pdev->num_resources < 3)
+               return -ENODEV;
+
+       addr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       data = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       irq = platform_get_irq(pdev, 0);
+       if (!addr || !data || irq < 0)
+               return -ENODEV;
+
+       /* refuse to confuse usbcore */
+       if (dev->dma_mask) {
+               DBG("no we won't dma\n");
+               return -EINVAL;
+       }
+
+       if (!request_mem_region(addr->start, 1, hcd_name))
+               return -EBUSY;
+       if (!request_mem_region(data->start, 1, hcd_name)) {
+               release_mem_region(addr->start, 1);
+               return -EBUSY;
+       }
+
+       /* allocate and initialize hcd */
+       sl811 = kcalloc(1, sizeof *sl811, GFP_KERNEL);
+       if (!sl811)
+               return 0;
+       dev_set_drvdata(dev, sl811);
+
+       usb_bus_init(&sl811->hcd.self);
+       sl811->hcd.self.controller = dev;
+       sl811->hcd.self.bus_name = dev->bus_id;
+       sl811->hcd.self.op = &usb_hcd_operations;
+       sl811->hcd.self.hcpriv = sl811;
+
+       // NOTE: 2.6.11 starts to change the hcd glue layer some more,
+       // eventually letting us eliminate struct sl811h_req and a
+       // lot of the boilerplate code here 
+
+       INIT_LIST_HEAD(&sl811->hcd.dev_list);
+       sl811->hcd.self.release = &usb_hcd_release;
+
+       sl811->hcd.description = sl811h_hc_driver.description;
+       init_timer(&sl811->hcd.rh_timer);
+       sl811->hcd.driver = &sl811h_hc_driver;
+       sl811->hcd.irq = -1;
+       sl811->hcd.state = USB_STATE_HALT;
+
+       spin_lock_init(&sl811->lock);
+       INIT_LIST_HEAD(&sl811->async);
+       sl811->board = dev->platform_data;
+       init_timer(&sl811->timer);
+       sl811->timer.function = sl811h_timer;
+       sl811->timer.data = (unsigned long) sl811;
+
+       sl811->addr_reg = ioremap(addr->start, resource_len(addr));
+       if (sl811->addr_reg == NULL) {
+               status = -ENOMEM;
+               goto fail;
+       }
+       sl811->data_reg = ioremap(data->start, resource_len(addr));
+       if (sl811->data_reg == NULL) {
+               status = -ENOMEM;
+               goto fail;
+       }
+
+       spin_lock_irqsave(&sl811->lock, flags);
+       port_power(sl811, 0);
+       spin_unlock_irqrestore(&sl811->lock, flags);
+       msleep(200);
+
+       tmp = sl811_read(sl811, SL11H_HWREVREG);
+       switch (tmp >> 4) {
+       case 1:
+               sl811->hcd.product_desc = "SL811HS v1.2";
+               break;
+       case 2:
+               sl811->hcd.product_desc = "SL811HS v1.5";
+               break;
+       default:
+               /* reject case 0, SL11S is less functional */
+               DBG("chiprev %02x\n", tmp);
+               status = -ENXIO;
+               goto fail;
+       }
+
+       /* sl811s would need a different handler for this irq */
+#ifdef CONFIG_ARM
+       /* Cypress docs say the IRQ is IRQT_HIGH ... */
+       set_irq_type(irq, IRQT_RISING);
+#endif
+       status = request_irq(irq, sl811h_irq, SA_INTERRUPT, hcd_name, sl811);
+       if (status < 0)
+               goto fail;
+       sl811->hcd.irq = irq;
+
+       INFO("%s, irq %d\n", sl811->hcd.product_desc, irq);
+
+       status = usb_register_bus(&sl811->hcd.self);
+       if (status < 0)
+               goto fail;
+       status = sl811h_start(&sl811->hcd);
+       if (status == 0) {
+               create_debug_file(sl811);
+               return 0;
+       }
+fail:
+       sl811h_remove(dev);
+       DBG("init error, %d\n", status);
+       return status;
+}
+
+#ifdef CONFIG_PM
+
+/* for this device there's no useful distinction between the controller
+ * and its root hub, except that the root hub only gets direct PM calls 
+ * when CONFIG_USB_SUSPEND is enabled.
+ */
+
+static int
+sl811h_suspend(struct device *dev, u32 state, u32 phase)
+{
+       struct sl811    *sl811 = dev_get_drvdata(dev);
+       int             retval = 0;
+
+       if (phase != SUSPEND_POWER_DOWN)
+               return retval;
+
+       if (state <= PM_SUSPEND_MEM)
+               retval = sl811h_hub_suspend(&sl811->hcd);
+       else
+               port_power(sl811, 0);
+       if (retval == 0)
+               dev->power.power_state = state;
+       return retval;
+}
+
+static int
+sl811h_resume(struct device *dev, u32 phase)
+{
+       struct sl811    *sl811 = dev_get_drvdata(dev);
+
+       if (phase != RESUME_POWER_ON)
+               return 0;
+
+       /* with no "check to see if VBUS is still powered" board hook,
+        * let's assume it'd only be powered to enable remote wakeup.
+        */
+       if (dev->power.power_state > PM_SUSPEND_MEM
+                       || !sl811->hcd.can_wakeup) {
+               sl811->port1 = 0;
+               port_power(sl811, 1);
+               return 0;
+       }
+
+       dev->power.power_state = PM_SUSPEND_ON;
+       return sl811h_hub_resume(&sl811->hcd);
+}
+
+#else
+
+#define        sl811h_suspend  NULL
+#define        sl811h_resume   NULL
+
+#endif
+
+
+static struct device_driver sl811h_driver = {
+       .name =         (char *) hcd_name,
+       .bus =          &platform_bus_type,
+
+       .probe =        sl811h_probe,
+       .remove =       sl811h_remove,
+
+       .suspend =      sl811h_suspend,
+       .resume =       sl811h_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+static int __init sl811h_init(void) 
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION);
+       return driver_register(&sl811h_driver);
+}
+module_init(sl811h_init);
+
+static void __exit sl811h_cleanup(void) 
+{      
+       driver_unregister(&sl811h_driver);
+}
+module_exit(sl811h_cleanup);
diff --git a/drivers/usb/host/sl811.h b/drivers/usb/host/sl811.h
new file mode 100644 (file)
index 0000000..1f5f0d4
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * SL811HS register declarations and HCD data structures
+ *
+ * Copyright (C) 2004 Psion Teklogix
+ * Copyright (C) 2004 David Brownell
+ * Copyright (C) 2001 Cypress Semiconductor Inc. 
+ */
+
+/*
+ * SL811HS has transfer registers, and control registers.  In host/master
+ * mode one set of registers is used; in peripheral/slave mode, another.
+ *  - SL11H only has some "A" transfer registers from 0x00-0x04
+ *  - SL811HS also has "B" registers from 0x08-0x0c
+ *  - SL811S (or HS in slave mode) has four A+B sets, at 00, 10, 20, 30
+ */
+
+#define SL811_EP_A(base)       ((base) + 0)
+#define SL811_EP_B(base)       ((base) + 8)
+
+#define SL811_HOST_BUF         0x00
+#define SL811_PERIPH_EP0       0x00
+#define SL811_PERIPH_EP1       0x10
+#define SL811_PERIPH_EP2       0x20
+#define SL811_PERIPH_EP3       0x30
+
+
+/* TRANSFER REGISTERS:  host and peripheral sides are similar
+ * except for the control models (master vs slave).
+ */
+#define SL11H_HOSTCTLREG       0
+#      define SL11H_HCTLMASK_ARM       0x01
+#      define SL11H_HCTLMASK_ENABLE    0x02
+#      define SL11H_HCTLMASK_IN        0x00
+#      define SL11H_HCTLMASK_OUT       0x04
+#      define SL11H_HCTLMASK_ISOCH     0x10
+#      define SL11H_HCTLMASK_AFTERSOF  0x20
+#      define SL11H_HCTLMASK_TOGGLE    0x40
+#      define SL11H_HCTLMASK_PREAMBLE  0x80
+#define SL11H_BUFADDRREG       1
+#define SL11H_BUFLNTHREG       2
+#define SL11H_PKTSTATREG       3       /* read */
+#      define SL11H_STATMASK_ACK       0x01
+#      define SL11H_STATMASK_ERROR     0x02
+#      define SL11H_STATMASK_TMOUT     0x04
+#      define SL11H_STATMASK_SEQ       0x08
+#      define SL11H_STATMASK_SETUP     0x10
+#      define SL11H_STATMASK_OVF       0x20
+#      define SL11H_STATMASK_NAK       0x40
+#      define SL11H_STATMASK_STALL     0x80
+#define SL11H_PIDEPREG         3       /* write */
+#      define  SL_SETUP        0xd0
+#      define  SL_IN           0x90
+#      define  SL_OUT          0x10
+#      define  SL_SOF          0x50
+#      define  SL_PREAMBLE     0xc0
+#      define  SL_NAK          0xa0
+#      define  SL_STALL        0xe0
+#      define  SL_DATA0        0x30
+#      define  SL_DATA1        0xb0
+#define SL11H_XFERCNTREG       4       /* read */
+#define SL11H_DEVADDRREG       4       /* write */
+
+
+/* CONTROL REGISTERS:  host and peripheral are very different.
+ */
+#define SL11H_CTLREG1          5
+#      define SL11H_CTL1MASK_SOF_ENA   0x01
+#      define SL11H_CTL1MASK_FORCE     0x18
+#              define SL11H_CTL1MASK_NORMAL    0x00
+#              define SL11H_CTL1MASK_SE0       0x08    /* reset */
+#              define SL11H_CTL1MASK_J         0x10
+#              define SL11H_CTL1MASK_K         0x18    /* resume */
+#      define SL11H_CTL1MASK_LSPD      0x20
+#      define SL11H_CTL1MASK_SUSPEND   0x40
+#define SL11H_IRQ_ENABLE       6
+#      define SL11H_INTMASK_DONE_A     0x01
+#      define SL11H_INTMASK_DONE_B     0x02
+#      define SL11H_INTMASK_SOFINTR    0x10
+#      define SL11H_INTMASK_INSRMV     0x20    /* to/from SE0 */
+#      define SL11H_INTMASK_RD         0x40
+#      define SL11H_INTMASK_DP         0x80    /* only in INTSTATREG */
+#define SL11S_ADDRESS          7
+
+/* 0x08-0x0c are for the B buffer (not in SL11) */
+
+#define SL11H_IRQ_STATUS       0x0D    /* write to ack */
+#define SL11H_HWREVREG         0x0E    /* read */
+#      define SL11H_HWRMASK_HWREV      0xF0
+#define SL11H_SOFLOWREG                0x0E    /* write */
+#define SL11H_SOFTMRREG                0x0F    /* read */
+
+/* a write to this register enables SL811HS features.
+ * HOST flag presumably overrides the chip input signal?
+ */
+#define SL811HS_CTLREG2                0x0F
+#      define SL811HS_CTL2MASK_SOF_MASK        0x3F
+#      define SL811HS_CTL2MASK_DSWAP           0x40
+#      define SL811HS_CTL2MASK_HOST            0x80
+
+#define SL811HS_CTL2_INIT      (SL811HS_CTL2MASK_HOST | 0x2e)
+
+
+/* DATA BUFFERS: registers from 0x10..0xff are for data buffers;
+ * that's 240 bytes, which we'll split evenly between A and B sides.
+ * Only ISO can use more than 64 bytes per packet.
+ * (The SL11S has 0x40..0xff for buffers.)
+ */
+#define H_MAXPACKET    120             /* bytes in A or B fifos */
+
+#define SL11H_DATA_START       0x10
+#define        SL811HS_PACKET_BUF(is_a)        ((is_a) \
+               ? SL11H_DATA_START \
+               : (SL11H_DATA_START + H_MAXPACKET))
+
+/*-------------------------------------------------------------------------*/
+
+#define        LOG2_PERIODIC_SIZE      5       /* arbitrary; this matches OHCI */
+#define        PERIODIC_SIZE           (1 << LOG2_PERIODIC_SIZE)
+
+struct sl811 {
+       struct usb_hcd          hcd;
+       spinlock_t              lock;
+       void __iomem            *addr_reg;
+       void __iomem            *data_reg;
+       struct sl811_platform_data      *board;
+       struct proc_dir_entry   *pde;
+
+       unsigned long           stat_insrmv;
+       unsigned long           stat_wake;
+       unsigned long           stat_sof;
+       unsigned long           stat_a;
+       unsigned long           stat_b;
+       unsigned long           stat_lost;
+       unsigned long           stat_overrun;
+
+       /* sw model */
+       struct timer_list       timer;
+       struct sl811h_ep        *next_periodic;
+       struct sl811h_ep        *next_async;
+
+       struct sl811h_ep        *active_a;
+       unsigned long           jiffies_a;
+       struct sl811h_ep        *active_b;
+       unsigned long           jiffies_b;
+
+       u32                     port1;
+       u8                      ctrl1, ctrl2, irq_enable;
+       u16                     frame;
+
+       /* async schedule: control, bulk */
+       struct list_head        async;
+
+       /* periodic schedule: interrupt, iso */
+       u16                     load[PERIODIC_SIZE];
+       struct sl811h_ep        *periodic[PERIODIC_SIZE];
+       unsigned                periodic_count;
+};
+
+static inline struct sl811 *hcd_to_sl811(struct usb_hcd *hcd)
+{
+       return container_of(hcd, struct sl811, hcd);
+}
+
+struct sl811h_ep {
+       struct list_head        queue;
+       struct usb_device       *udev;
+
+       u8                      defctrl;
+       u8                      maxpacket;
+       u8                      epnum;
+       u8                      nextpid;
+
+       u16                     error_count;
+       u16                     nak_count;
+       u16                     length;         /* of current packet */
+
+       /* periodic schedule */
+       u16                     period;
+       u16                     branch;
+       u16                     load;
+       struct sl811h_ep        *next;
+
+       /* async schedule */
+       struct list_head        schedule;
+};
+
+struct sl811h_req {
+       /* FIXME usbcore should maintain endpoints' urb queues
+        * directly in 'struct usb_host_endpoint'
+        */
+       struct urb              *urb;
+       struct list_head        queue;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* These register utilities should work for the SL811S register API too
+ * NOTE:  caller must hold sl811->lock.
+ */
+
+static inline u8 sl811_read(struct sl811 *sl811, int reg)
+{
+       writeb(reg, sl811->addr_reg);
+       return readb(sl811->data_reg);
+}
+
+static inline void sl811_write(struct sl811 *sl811, int reg, u8 val)
+{
+       writeb(reg, sl811->addr_reg);
+       writeb(val, sl811->data_reg);
+}
+
+static inline void
+sl811_write_buf(struct sl811 *sl811, int addr, const void *buf, size_t count)
+{
+       const u8        *data;
+       void __iomem    *data_reg;
+
+       if (!count)
+               return;
+       writeb(addr, sl811->addr_reg);
+
+       data = buf;
+       data_reg = sl811->data_reg;
+       do {
+               writeb(*data++, data_reg);
+       } while (--count);
+}
+
+static inline void
+sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count)
+{
+       u8              *data;
+       void __iomem    *data_reg;
+
+       if (!count)
+               return;
+       writeb(addr, sl811->addr_reg);
+
+       data = buf;
+       data_reg = sl811->data_reg;
+       do {
+               *data++ = readb(data_reg);
+       } while (--count);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+#define DBG(stuff...)          printk(KERN_DEBUG "sl811: " stuff)
+#else
+#define DBG(stuff...)          do{}while(0)
+#endif
+
+#ifdef VERBOSE
+#    define VDBG               DBG
+#else
+#    define VDBG(stuff...)     do{}while(0)
+#endif
+
+#ifdef PACKET_TRACE
+#    define PACKET             VDBG
+#else
+#    define PACKET(stuff...)   do{}while(0)
+#endif
+
+#define ERR(stuff...)          printk(KERN_ERR "sl811: " stuff)
+#define WARN(stuff...)         printk(KERN_WARNING "sl811: " stuff)
+#define INFO(stuff...)         printk(KERN_INFO "sl811: " stuff)
+
diff --git a/drivers/usb/media/pwc/Makefile b/drivers/usb/media/pwc/Makefile
new file mode 100644 (file)
index 0000000..44bcc76
--- /dev/null
@@ -0,0 +1,20 @@
+ifneq ($(KERNELRELEASE),)
+
+pwc-objs       := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o
+
+obj-m += pwc.o
+
+else
+
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+
+default:
+       $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
+
+endif
+
+clean:
+       rm -f *.[oas] .*.flags *.ko .*.cmd .*.d .*.tmp *.mod.c 
+       rm -rf .tmp_versions
+
diff --git a/drivers/usb/media/pwc/pwc-ctrl.c b/drivers/usb/media/pwc/pwc-ctrl.c
new file mode 100644 (file)
index 0000000..45c9323
--- /dev/null
@@ -0,0 +1,1630 @@
+/* Driver for Philips webcam
+   Functions that send various control messages to the webcam, including
+   video modes.
+   (C) 1999-2003 Nemosoft Unv.
+   (C) 2004      Luc Saillard (luc@saillard.org)
+
+   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+   driver and thus may have bugs that are not present in the original version.
+   Please send bug reports and support requests to <luc@saillard.org>.
+
+   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+   driver and thus may have bugs that are not present in the original version.
+   Please send bug reports and support requests to <luc@saillard.org>.
+   The decompression routines have been implemented by reverse-engineering the
+   Nemosoft binary pwcx module. Caveat emptor.
+
+   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
+*/
+
+/*
+   Changes
+   2001/08/03  Alvarado   Added methods for changing white balance and 
+                          red/green gains
+ */
+
+/* Control functions for the cam; brightness, contrast, video mode, etc. */
+
+#ifdef __KERNEL__
+#include <asm/uaccess.h> 
+#endif
+#include <asm/errno.h>
+#include <linux/version.h>
+#include "pwc.h"
+#include "pwc-ioctl.h"
+#include "pwc-uncompress.h"
+#include "pwc-kiara.h"
+#include "pwc-timon.h"
+#include "pwc-dec1.h"
+#include "pwc-dec23.h"
+
+/* Request types: video */
+#define SET_LUM_CTL                    0x01
+#define GET_LUM_CTL                    0x02
+#define SET_CHROM_CTL                  0x03
+#define GET_CHROM_CTL                  0x04
+#define SET_STATUS_CTL                 0x05
+#define GET_STATUS_CTL                 0x06
+#define SET_EP_STREAM_CTL              0x07
+#define GET_EP_STREAM_CTL              0x08
+#define SET_MPT_CTL                    0x0D
+#define GET_MPT_CTL                    0x0E
+
+/* Selectors for the Luminance controls [GS]ET_LUM_CTL */
+#define AGC_MODE_FORMATTER                     0x2000
+#define PRESET_AGC_FORMATTER                   0x2100
+#define SHUTTER_MODE_FORMATTER                 0x2200
+#define PRESET_SHUTTER_FORMATTER               0x2300
+#define PRESET_CONTOUR_FORMATTER               0x2400
+#define AUTO_CONTOUR_FORMATTER                 0x2500
+#define BACK_LIGHT_COMPENSATION_FORMATTER      0x2600
+#define CONTRAST_FORMATTER                     0x2700
+#define DYNAMIC_NOISE_CONTROL_FORMATTER                0x2800
+#define FLICKERLESS_MODE_FORMATTER             0x2900
+#define AE_CONTROL_SPEED                       0x2A00
+#define BRIGHTNESS_FORMATTER                   0x2B00
+#define GAMMA_FORMATTER                                0x2C00
+
+/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */
+#define WB_MODE_FORMATTER                      0x1000
+#define AWB_CONTROL_SPEED_FORMATTER            0x1100
+#define AWB_CONTROL_DELAY_FORMATTER            0x1200
+#define PRESET_MANUAL_RED_GAIN_FORMATTER       0x1300
+#define PRESET_MANUAL_BLUE_GAIN_FORMATTER      0x1400
+#define COLOUR_MODE_FORMATTER                  0x1500
+#define SATURATION_MODE_FORMATTER1             0x1600
+#define SATURATION_MODE_FORMATTER2             0x1700
+
+/* Selectors for the Status controls [GS]ET_STATUS_CTL */
+#define SAVE_USER_DEFAULTS_FORMATTER           0x0200
+#define RESTORE_USER_DEFAULTS_FORMATTER                0x0300
+#define RESTORE_FACTORY_DEFAULTS_FORMATTER     0x0400
+#define READ_AGC_FORMATTER                     0x0500
+#define READ_SHUTTER_FORMATTER                 0x0600
+#define READ_RED_GAIN_FORMATTER                        0x0700
+#define READ_BLUE_GAIN_FORMATTER               0x0800
+#define SENSOR_TYPE_FORMATTER1                 0x0C00
+#define READ_RAW_Y_MEAN_FORMATTER              0x3100
+#define SET_POWER_SAVE_MODE_FORMATTER          0x3200
+#define MIRROR_IMAGE_FORMATTER                 0x3300
+#define LED_FORMATTER                          0x3400
+#define SENSOR_TYPE_FORMATTER2                 0x3700
+
+/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */
+#define VIDEO_OUTPUT_CONTROL_FORMATTER         0x0100
+
+/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */
+#define PT_RELATIVE_CONTROL_FORMATTER          0x01
+#define PT_RESET_CONTROL_FORMATTER             0x02
+#define PT_STATUS_FORMATTER                    0x03
+
+static char *size2name[PSZ_MAX] =
+{
+       "subQCIF",
+       "QSIF",
+       "QCIF",
+       "SIF",
+       "CIF",
+       "VGA",
+};  
+
+/********/
+
+/* Entries for the Nala (645/646) camera; the Nala doesn't have compression 
+   preferences, so you either get compressed or non-compressed streams.
+   
+   An alternate value of 0 means this mode is not available at all.
+ */
+
+struct Nala_table_entry {
+       char alternate;                 /* USB alternate setting */
+       int compressed;                 /* Compressed yes/no */
+
+       unsigned char mode[3];          /* precomputed mode table */
+};
+
+static struct Nala_table_entry Nala_table[PSZ_MAX][8] =
+{
+#include "pwc-nala.h"
+};
+
+
+/****************************************************************************/
+
+
+#define SendControlMsg(request, value, buflen) \
+       usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \
+               request, \
+               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \
+               value, \
+               pdev->vcinterface, \
+               &buf, buflen, HZ / 2)
+
+#define RecvControlMsg(request, value, buflen) \
+       usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \
+               request, \
+               USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \
+               value, \
+               pdev->vcinterface, \
+               &buf, buflen, HZ / 2)
+
+
+#if PWC_DEBUG
+void pwc_hexdump(void *p, int len)
+{
+       int i;
+       unsigned char *s;
+       char buf[100], *d;
+
+       s = (unsigned char *)p;
+       d = buf;
+       *d = '\0';
+       Debug("Doing hexdump @ %p, %d bytes.\n", p, len);
+       for (i = 0; i < len; i++) {
+               d += sprintf(d, "%02X ", *s++);
+               if ((i & 0xF) == 0xF) {
+                       Debug("%s\n", buf);
+                       d = buf;
+                       *d = '\0';
+               }
+       }
+       if ((i & 0xF) != 0)
+               Debug("%s\n", buf);
+}
+#endif
+
+static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen)
+{
+       return usb_control_msg(udev,
+               usb_sndctrlpipe(udev, 0),
+               SET_EP_STREAM_CTL,
+               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+               VIDEO_OUTPUT_CONTROL_FORMATTER,
+               index,
+               buf, buflen, HZ);
+}
+
+
+
+static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames)
+{
+       unsigned char buf[3];
+       int ret, fps;
+       struct Nala_table_entry *pEntry;
+       int frames2frames[31] =
+       { /* closest match of framerate */
+          0,  0,  0,  0,  4,  /*  0-4  */
+          5,  5,  7,  7, 10,  /*  5-9  */
+          10, 10, 12, 12, 15,  /* 10-14 */
+          15, 15, 15, 20, 20,  /* 15-19 */
+          20, 20, 20, 24, 24,  /* 20-24 */
+          24, 24, 24, 24, 24,  /* 25-29 */
+          24                   /* 30    */
+       };
+       int frames2table[31] = 
+       { 0, 0, 0, 0, 0, /*  0-4  */
+         1, 1, 1, 2, 2, /*  5-9  */
+         3, 3, 4, 4, 4, /* 10-14 */
+         5, 5, 5, 5, 5, /* 15-19 */
+         6, 6, 6, 6, 7, /* 20-24 */
+         7, 7, 7, 7, 7, /* 25-29 */
+         7              /* 30    */
+       };
+       
+       if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25)
+               return -EINVAL;
+       frames = frames2frames[frames];
+       fps = frames2table[frames];
+       pEntry = &Nala_table[size][fps];
+       if (pEntry->alternate == 0)
+               return -EINVAL;
+
+       if (pEntry->compressed)
+               return -ENOENT; /* Not supported. */
+
+       memcpy(buf, pEntry->mode, 3);   
+       ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3);
+       if (ret < 0) {
+               Debug("Failed to send video command... %d\n", ret);
+               return ret;
+       }
+       if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW)
+        {
+          switch(pdev->type) {
+            case 645:
+            case 646:
+              pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data);
+              break;
+
+            case 675:
+            case 680:
+            case 690:
+            case 720:
+            case 730:
+            case 740:
+            case 750:
+              pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data);
+              break;
+          }
+       }
+       pdev->cmd_len = 3;
+       memcpy(pdev->cmd_buf, buf, 3);
+
+       /* Set various parameters */
+       pdev->vframes = frames;
+       pdev->vsize = size;
+       pdev->valternate = pEntry->alternate;
+       pdev->image = pwc_image_sizes[size];
+       pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2;
+       if (pEntry->compressed) {
+               if (pdev->release < 5) { /* 4 fold compression */
+                       pdev->vbandlength = 528;
+                       pdev->frame_size /= 4;
+               }
+               else {
+                       pdev->vbandlength = 704;
+                       pdev->frame_size /= 3;
+               }
+       }
+       else
+               pdev->vbandlength = 0;
+       return 0;
+}
+
+
+static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot)
+{
+       unsigned char buf[13];
+       const struct Timon_table_entry *pChoose;
+       int ret, fps;
+
+       if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
+               return -EINVAL;
+       if (size == PSZ_VGA && frames > 15)
+               return -EINVAL;
+       fps = (frames / 5) - 1;
+
+       /* Find a supported framerate with progressively higher compression ratios
+          if the preferred ratio is not available.
+       */
+       pChoose = NULL;
+       while (compression <= 3) {
+          pChoose = &Timon_table[size][fps][compression];
+          if (pChoose->alternate != 0)
+            break;
+          compression++;
+       }
+       if (pChoose == NULL || pChoose->alternate == 0)
+               return -ENOENT; /* Not supported. */
+
+       memcpy(buf, pChoose->mode, 13);
+       if (snapshot)
+               buf[0] |= 0x80;
+       ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13);
+       if (ret < 0)
+               return ret;
+
+       if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
+          pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data);
+
+       pdev->cmd_len = 13;
+       memcpy(pdev->cmd_buf, buf, 13);
+
+       /* Set various parameters */
+       pdev->vframes = frames;
+       pdev->vsize = size;
+       pdev->vsnapshot = snapshot;
+       pdev->valternate = pChoose->alternate;
+       pdev->image = pwc_image_sizes[size];
+       pdev->vbandlength = pChoose->bandlength;
+       if (pChoose->bandlength > 0)
+               pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4;
+       else
+               pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
+       return 0;
+}
+
+
+static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot)
+{
+       const struct Kiara_table_entry *pChoose = 0;
+       int fps, ret;
+       unsigned char buf[12];
+       struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}};
+
+       if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
+               return -EINVAL;
+       if (size == PSZ_VGA && frames > 15)
+               return -EINVAL;
+       fps = (frames / 5) - 1;
+
+       /* special case: VGA @ 5 fps and snapshot is raw bayer mode */
+       if (size == PSZ_VGA && frames == 5 && snapshot)
+       {
+               /* Only available in case the raw palette is selected or 
+                  we have the decompressor available. This mode is 
+                  only available in compressed form 
+               */
+               if (pdev->vpalette == VIDEO_PALETTE_RAW)
+               {
+                       Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette);
+                       pChoose = &RawEntry;
+               }
+               else
+               {
+                       Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n");
+               }
+       }
+       else
+       {
+               /* Find a supported framerate with progressively higher compression ratios
+                  if the preferred ratio is not available.
+                   Skip this step when using RAW modes.
+               */
+               while (compression <= 3) {
+                       pChoose = &Kiara_table[size][fps][compression];
+                       if (pChoose->alternate != 0)
+                               break;
+                       compression++;
+               }
+       }
+       if (pChoose == NULL || pChoose->alternate == 0)
+               return -ENOENT; /* Not supported. */
+
+       Debug("Using alternate setting %d.\n", pChoose->alternate);
+       
+       /* usb_control_msg won't take staticly allocated arrays as argument?? */
+       memcpy(buf, pChoose->mode, 12);
+       if (snapshot)
+               buf[0] |= 0x80;
+
+       /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */
+       ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12);
+       if (ret < 0)
+               return ret;
+
+       if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
+         pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data);
+
+       pdev->cmd_len = 12;
+       memcpy(pdev->cmd_buf, buf, 12);
+       /* All set and go */
+       pdev->vframes = frames;
+       pdev->vsize = size;
+       pdev->vsnapshot = snapshot;
+       pdev->valternate = pChoose->alternate;
+       pdev->image = pwc_image_sizes[size];
+       pdev->vbandlength = pChoose->bandlength;
+       if (pdev->vbandlength > 0)
+               pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4;
+       else
+               pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
+       return 0;
+}
+
+
+
+/**
+   @pdev: device structure
+   @width: viewport width
+   @height: viewport height
+   @frame: framerate, in fps
+   @compression: preferred compression ratio
+   @snapshot: snapshot mode or streaming
+ */
+int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot)
+{
+        int ret, size;
+
+        Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette);
+       size = pwc_decode_size(pdev, width, height);
+       if (size < 0) {
+               Debug("Could not find suitable size.\n");
+               return -ERANGE;
+       }
+       Debug("decode_size = %d.\n", size);
+
+        ret = -EINVAL;
+       switch(pdev->type) {
+       case 645:
+       case 646:
+               ret = set_video_mode_Nala(pdev, size, frames);
+               break;
+
+       case 675:
+       case 680:
+       case 690:
+               ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot);
+               break;
+       
+       case 720:
+       case 730:
+       case 740:
+       case 750:
+               ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot);
+               break;
+       }
+       if (ret < 0) {
+               if (ret == -ENOENT)
+                       Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames);
+               else {
+                       Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret);
+               }
+               return ret;
+       }
+       pdev->view.x = width;
+       pdev->view.y = height;
+       pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size;
+       pwc_set_image_buffer_size(pdev);
+       Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y);
+       return 0;
+}
+
+
+void pwc_set_image_buffer_size(struct pwc_device *pdev)
+{
+       int i, factor = 0, filler = 0;
+
+       /* for PALETTE_YUV420P */
+       switch(pdev->vpalette)
+       {
+       case VIDEO_PALETTE_YUV420P:
+               factor = 6;
+               filler = 128;
+               break;
+       case VIDEO_PALETTE_RAW:
+               factor = 6; /* can be uncompressed YUV420P */
+               filler = 0;
+               break;
+       }
+
+       /* Set sizes in bytes */
+       pdev->image.size = pdev->image.x * pdev->image.y * factor / 4;
+       pdev->view.size  = pdev->view.x  * pdev->view.y  * factor / 4;
+
+       /* Align offset, or you'll get some very weird results in
+          YUV420 mode... x must be multiple of 4 (to get the Y's in
+          place), and y even (or you'll mixup U & V). This is less of a
+          problem for YUV420P.
+        */
+       pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC;
+       pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE;
+
+       /* Fill buffers with gray or black */
+       for (i = 0; i < MAX_IMAGES; i++) {
+               if (pdev->image_ptr[i] != NULL)
+                       memset(pdev->image_ptr[i], filler, pdev->view.size);
+       }
+}
+
+
+
+/* BRIGHTNESS */
+
+int pwc_get_brightness(struct pwc_device *pdev)
+{
+       char buf;
+       int ret;
+
+       ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1);     
+       if (ret < 0)
+               return ret;
+       return buf << 9;
+}
+
+int pwc_set_brightness(struct pwc_device *pdev, int value)
+{
+       char buf;
+
+       if (value < 0)
+               value = 0;
+       if (value > 0xffff)
+               value = 0xffff;
+       buf = (value >> 9) & 0x7f;
+       return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1);
+}
+
+/* CONTRAST */
+
+int pwc_get_contrast(struct pwc_device *pdev)
+{
+       char buf;
+       int ret;
+
+       ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+       return buf << 10;
+}
+
+int pwc_set_contrast(struct pwc_device *pdev, int value)
+{
+       char buf;
+
+       if (value < 0)
+               value = 0;
+       if (value > 0xffff)
+               value = 0xffff;
+       buf = (value >> 10) & 0x3f;
+       return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1);
+}
+
+/* GAMMA */
+
+int pwc_get_gamma(struct pwc_device *pdev)
+{
+       char buf;
+       int ret;
+       
+       ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+       return buf << 11;
+}
+
+int pwc_set_gamma(struct pwc_device *pdev, int value)
+{
+       char buf;
+
+       if (value < 0)
+               value = 0;
+       if (value > 0xffff)
+               value = 0xffff;
+       buf = (value >> 11) & 0x1f;
+       return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1);
+}
+
+
+/* SATURATION */
+
+int pwc_get_saturation(struct pwc_device *pdev)
+{
+       char buf;
+       int ret;
+
+       if (pdev->type < 675)
+               return -1;
+       ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1);
+       if (ret < 0)
+               return ret;
+       return 32768 + buf * 327;
+}
+
+int pwc_set_saturation(struct pwc_device *pdev, int value)
+{
+       char buf;
+
+       if (pdev->type < 675)
+               return -EINVAL;
+       if (value < 0)
+               value = 0;
+       if (value > 0xffff)
+               value = 0xffff;
+       /* saturation ranges from -100 to +100 */
+       buf = (value - 32768) / 327;
+       return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1);
+}
+
+/* AGC */
+
+static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value)
+{
+       char buf;
+       int ret;
+       
+       if (mode)
+               buf = 0x0; /* auto */
+       else
+               buf = 0xff; /* fixed */
+
+       ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1);
+       
+       if (!mode && ret >= 0) {
+               if (value < 0)
+                       value = 0;
+               if (value > 0xffff)
+                       value = 0xffff;
+               buf = (value >> 10) & 0x3F;
+               ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1);
+       }
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static inline int pwc_get_agc(struct pwc_device *pdev, int *value)
+{
+       unsigned char buf;
+       int ret;
+       
+       ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+
+       if (buf != 0) { /* fixed */
+               ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1);
+               if (ret < 0)
+                       return ret;
+               if (buf > 0x3F)
+                       buf = 0x3F;
+               *value = (buf << 10);           
+       }
+       else { /* auto */
+               ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1);
+               if (ret < 0)
+                       return ret;
+               /* Gah... this value ranges from 0x00 ... 0x9F */
+               if (buf > 0x9F)
+                       buf = 0x9F;
+               *value = -(48 + buf * 409);
+       }
+
+       return 0;
+}
+
+static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value)
+{
+       char buf[2];
+       int speed, ret;
+
+
+       if (mode)
+               buf[0] = 0x0;   /* auto */
+       else
+               buf[0] = 0xff; /* fixed */
+       
+       ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1);
+
+       if (!mode && ret >= 0) {
+               if (value < 0)
+                       value = 0;
+               if (value > 0xffff)
+                       value = 0xffff;
+               switch(pdev->type) {
+               case 675:
+               case 680:
+               case 690:
+                       /* speed ranges from 0x0 to 0x290 (656) */
+                       speed = (value / 100);
+                       buf[1] = speed >> 8;
+                       buf[0] = speed & 0xff;
+                       break;
+               case 720:
+               case 730:
+               case 740:
+               case 750:
+                       /* speed seems to range from 0x0 to 0xff */
+                       buf[1] = 0;
+                       buf[0] = value >> 8;
+                       break;
+               }
+
+               ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2);
+       }
+       return ret;
+}      
+
+
+/* POWER */
+
+int pwc_camera_power(struct pwc_device *pdev, int power)
+{
+       char buf;
+
+       if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6))
+               return 0;       /* Not supported by Nala or Timon < release 6 */
+
+       if (power)
+               buf = 0x00; /* active */
+       else
+               buf = 0xFF; /* power save */
+       return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1);
+}
+
+
+
+/* private calls */
+
+static inline int pwc_restore_user(struct pwc_device *pdev)
+{
+       char buf; /* dummy */
+       return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0);
+}
+
+static inline int pwc_save_user(struct pwc_device *pdev)
+{
+       char buf; /* dummy */
+       return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0);
+}
+
+static inline int pwc_restore_factory(struct pwc_device *pdev)
+{
+       char buf; /* dummy */
+       return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0);
+}
+
+ /* ************************************************* */
+ /* Patch by Alvarado: (not in the original version   */
+
+ /*
+  * the camera recognizes modes from 0 to 4:
+  *
+  * 00: indoor (incandescant lighting)
+  * 01: outdoor (sunlight)
+  * 02: fluorescent lighting
+  * 03: manual
+  * 04: auto
+  */ 
+static inline int pwc_set_awb(struct pwc_device *pdev, int mode)
+{
+       char buf;
+       int ret;
+       
+       if (mode < 0)
+           mode = 0;
+       
+       if (mode > 4)
+           mode = 4;
+       
+       buf = mode & 0x07; /* just the lowest three bits */
+       
+       ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1);
+       
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static inline int pwc_get_awb(struct pwc_device *pdev)
+{
+       unsigned char buf;
+       int ret;
+       
+       ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1);
+
+       if (ret < 0) 
+               return ret;
+       return buf;
+}
+
+static inline int pwc_set_red_gain(struct pwc_device *pdev, int value)
+{
+        unsigned char buf;
+
+       if (value < 0)
+               value = 0;
+       if (value > 0xffff)
+               value = 0xffff;
+       /* only the msb is considered */
+       buf = value >> 8;
+       return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1);
+}
+
+static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value)
+{
+       unsigned char buf;
+       int ret;
+       
+       ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1);
+       if (ret < 0)
+           return ret;
+       *value = buf << 8;
+       return 0;
+}
+
+
+static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value)
+{
+       unsigned char buf;
+
+       if (value < 0)
+               value = 0;
+       if (value > 0xffff)
+               value = 0xffff;
+       /* only the msb is considered */
+       buf = value >> 8;
+       return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1);
+}
+
+static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value)
+{
+       unsigned char buf;
+       int ret;
+       
+       ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1);
+       if (ret < 0)
+           return ret;
+       *value = buf << 8;
+       return 0;
+}
+
+
+/* The following two functions are different, since they only read the
+   internal red/blue gains, which may be different from the manual 
+   gains set or read above.
+ */   
+static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value)
+{
+       unsigned char buf;
+       int ret;
+       
+       ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+       *value = buf << 8;
+       return 0;
+}
+
+static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value)
+{
+       unsigned char buf;
+       int ret;
+       
+       ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+       *value = buf << 8;
+       return 0;
+}
+
+
+static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed)
+{
+       unsigned char buf;
+       
+       /* useful range is 0x01..0x20 */
+       buf = speed / 0x7f0;
+       return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1);
+}
+
+static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value)
+{
+       unsigned char buf;
+       int ret;
+       
+       ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+       *value = buf * 0x7f0;
+       return 0;
+}
+
+
+static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay)
+{
+       unsigned char buf;
+       
+       /* useful range is 0x01..0x3F */
+       buf = (delay >> 10);
+       return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1);
+}
+
+static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value)
+{
+       unsigned char buf;
+       int ret;
+       
+       ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+       *value = buf << 10;
+       return 0;
+}
+
+
+int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
+{
+       unsigned char buf[2];
+
+       if (pdev->type < 730)
+               return 0;
+       on_value /= 100;
+       off_value /= 100;
+       if (on_value < 0)
+               on_value = 0;
+       if (on_value > 0xff)
+               on_value = 0xff;
+       if (off_value < 0)
+               off_value = 0;
+       if (off_value > 0xff)
+               off_value = 0xff;
+
+       buf[0] = on_value;
+       buf[1] = off_value;
+
+       return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2);
+}
+
+int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
+{
+       unsigned char buf[2];
+       int ret;
+       
+       if (pdev->type < 730) {
+               *on_value = -1;
+               *off_value = -1;
+               return 0;
+       }
+
+       ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2);
+       if (ret < 0)
+               return ret;
+       *on_value = buf[0] * 100;
+       *off_value = buf[1] * 100;
+       return 0;
+}
+
+static inline int pwc_set_contour(struct pwc_device *pdev, int contour)
+{
+       unsigned char buf;
+       int ret;
+       
+       if (contour < 0)
+               buf = 0xff; /* auto contour on */
+       else
+               buf = 0x0; /* auto contour off */
+       ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+       
+       if (contour < 0)
+               return 0;
+       if (contour > 0xffff)
+               contour = 0xffff;
+       
+       buf = (contour >> 10); /* contour preset is [0..3f] */
+       ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1);
+       if (ret < 0)    
+               return ret;     
+       return 0;
+}
+
+static inline int pwc_get_contour(struct pwc_device *pdev, int *contour)
+{
+       unsigned char buf;
+       int ret;
+       
+       ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+
+       if (buf == 0) {
+               /* auto mode off, query current preset value */
+               ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1);
+               if (ret < 0)    
+                       return ret;
+               *contour = buf << 10;
+       }
+       else
+               *contour = -1;
+       return 0;
+}
+
+
+static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight)
+{
+       unsigned char buf;
+       
+       if (backlight)
+               buf = 0xff;
+       else
+               buf = 0x0;
+       return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1);
+}
+
+static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight)
+{
+       int ret;
+       unsigned char buf;
+       
+       ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+       *backlight = buf;
+       return 0;
+}
+
+
+static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker)
+{
+       unsigned char buf;
+       
+       if (flicker)
+               buf = 0xff;
+       else
+               buf = 0x0;
+       return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
+}
+
+static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker)
+{
+       int ret;
+       unsigned char buf;
+       
+       ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+       *flicker = buf;
+       return 0;
+}
+
+
+static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise)
+{
+       unsigned char buf;
+
+       if (noise < 0)
+               noise = 0;
+       if (noise > 3)
+               noise = 3;
+       buf = noise;
+       return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
+}
+
+static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise)
+{
+       int ret;
+       unsigned char buf;
+       
+       ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
+       if (ret < 0)
+               return ret;
+       *noise = buf;
+       return 0;
+}
+
+int pwc_mpt_reset(struct pwc_device *pdev, int flags)
+{
+       unsigned char buf;
+       
+       buf = flags & 0x03; // only lower two bits are currently used
+       return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1);
+}
+
+static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt)
+{
+       unsigned char buf[4];
+       
+       /* set new relative angle; angles are expressed in degrees * 100,
+          but cam as .5 degree resolution, hence devide by 200. Also
+          the angle must be multiplied by 64 before it's send to
+          the cam (??)
+        */
+       pan  =  64 * pan  / 100;
+       tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */
+       buf[0] = pan & 0xFF;
+       buf[1] = (pan >> 8) & 0xFF;
+       buf[2] = tilt & 0xFF;
+       buf[3] = (tilt >> 8) & 0xFF;
+       return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4);
+}
+
+static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status)
+{
+       int ret;
+       unsigned char buf[5];
+       
+       ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5);
+       if (ret < 0)
+               return ret;
+       status->status = buf[0] & 0x7; // 3 bits are used for reporting
+       status->time_pan = (buf[1] << 8) + buf[2];
+       status->time_tilt = (buf[3] << 8) + buf[4];
+       return 0;
+}
+
+
+int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
+{
+       unsigned char buf;
+       int ret = -1, request;
+       
+       if (pdev->type < 675)
+               request = SENSOR_TYPE_FORMATTER1;
+       else if (pdev->type < 730)
+               return -1; /* The Vesta series doesn't have this call */
+       else
+               request = SENSOR_TYPE_FORMATTER2;
+       
+       ret = RecvControlMsg(GET_STATUS_CTL, request, 1);
+       if (ret < 0)
+               return ret;
+       if (pdev->type < 675)
+               *sensor = buf | 0x100;
+       else
+               *sensor = buf;
+       return 0;
+}
+
+
+ /* End of Add-Ons                                    */
+ /* ************************************************* */
+
+/* Linux 2.5.something and 2.6 pass direct pointers to arguments of
+   ioctl() calls. With 2.4, you have to do tedious copy_from_user()
+   and copy_to_user() calls. With these macros we circumvent this,
+   and let me maintain only one source file. The functionality is
+   exactly the same otherwise.
+ */   
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+
+/* define local variable for arg */
+#define ARG_DEF(ARG_type, ARG_name)\
+       ARG_type *ARG_name = arg;
+/* copy arg to local variable */       
+#define ARG_IN(ARG_name) /* nothing */
+/* argument itself (referenced) */
+#define ARGR(ARG_name) (*ARG_name)
+/* argument address */
+#define ARGA(ARG_name) ARG_name
+/* copy local variable to arg */
+#define ARG_OUT(ARG_name) /* nothing */
+
+#else
+
+#define ARG_DEF(ARG_type, ARG_name)\
+       ARG_type ARG_name;
+#define ARG_IN(ARG_name)\
+       if (copy_from_user(&ARG_name, arg, sizeof(ARG_name))) {\
+               ret = -EFAULT;\
+               break;\
+       }
+#define ARGR(ARG_name) ARG_name
+#define ARGA(ARG_name) &ARG_name
+#define ARG_OUT(ARG_name)\
+       if (copy_to_user(arg, &ARG_name, sizeof(ARG_name))) {\
+               ret = -EFAULT;\
+               break;\
+       }
+
+#endif
+
+int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
+{
+       int ret = 0;
+
+       switch(cmd) {
+       case VIDIOCPWCRUSER:
+       {
+               if (pwc_restore_user(pdev))
+                       ret = -EINVAL;
+               break;
+       }
+       
+       case VIDIOCPWCSUSER:
+       {
+               if (pwc_save_user(pdev))
+                       ret = -EINVAL;
+               break;
+       }
+               
+       case VIDIOCPWCFACTORY:
+       {
+               if (pwc_restore_factory(pdev))
+                       ret = -EINVAL;
+               break;
+       }
+       
+       case VIDIOCPWCSCQUAL:
+       {       
+               ARG_DEF(int, qual)
+
+               ARG_IN(qual)
+               if (ARGR(qual) < 0 || ARGR(qual) > 3)
+                       ret = -EINVAL;
+               else
+                       ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot);
+               if (ret >= 0)
+                       pdev->vcompression = ARGR(qual);
+               break;
+       }
+       
+       case VIDIOCPWCGCQUAL:
+       {
+               ARG_DEF(int, qual)
+               
+               ARGR(qual) = pdev->vcompression;
+               ARG_OUT(qual)
+               break;
+       }
+       
+       case VIDIOCPWCPROBE:
+       {
+               ARG_DEF(struct pwc_probe, probe)
+               
+               strcpy(ARGR(probe).name, pdev->vdev->name);
+               ARGR(probe).type = pdev->type;
+               ARG_OUT(probe)
+               break;
+       }
+
+       case VIDIOCPWCGSERIAL:
+       {
+               ARG_DEF(struct pwc_serial, serial)
+               
+               strcpy(ARGR(serial).serial, pdev->serial);
+               ARG_OUT(serial)
+               break;
+       }
+
+       case VIDIOCPWCSAGC:
+       {
+               ARG_DEF(int, agc)
+
+               ARG_IN(agc)
+               if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc)))
+                       ret = -EINVAL;
+               break;
+       }
+       
+       case VIDIOCPWCGAGC:
+       {
+               ARG_DEF(int, agc)
+               
+               if (pwc_get_agc(pdev, ARGA(agc)))
+                       ret = -EINVAL;
+               ARG_OUT(agc)
+               break;
+       }
+       
+       case VIDIOCPWCSSHUTTER:
+       {
+               ARG_DEF(int, shutter_speed)
+
+               ARG_IN(shutter_speed)
+               ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed));
+               break;
+       }
+       
+        case VIDIOCPWCSAWB:
+       {
+               ARG_DEF(struct pwc_whitebalance, wb)
+               
+               ARG_IN(wb)
+               ret = pwc_set_awb(pdev, ARGR(wb).mode);
+               if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) {
+                       pwc_set_red_gain(pdev, ARGR(wb).manual_red);
+                       pwc_set_blue_gain(pdev, ARGR(wb).manual_blue);
+               }
+               break;
+       }
+
+       case VIDIOCPWCGAWB:
+       {
+               ARG_DEF(struct pwc_whitebalance, wb)
+
+               memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance));
+               ARGR(wb).mode = pwc_get_awb(pdev);
+               if (ARGR(wb).mode < 0)
+                       ret = -EINVAL;
+               else {
+                       if (ARGR(wb).mode == PWC_WB_MANUAL) {
+                               ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red);
+                               if (ret < 0)
+                                       break;
+                               ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue);
+                               if (ret < 0)
+                                       break;
+                       }
+                       if (ARGR(wb).mode == PWC_WB_AUTO) {
+                               ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red);
+                               if (ret < 0)
+                                       break;
+                               ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue);
+                               if (ret < 0)
+                                       break;
+                       }
+               }
+               ARG_OUT(wb)
+               break;
+       }
+       
+       case VIDIOCPWCSAWBSPEED:
+       {
+               ARG_DEF(struct pwc_wb_speed, wbs)
+               
+               if (ARGR(wbs).control_speed > 0) {
+                       ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed);
+               }
+               if (ARGR(wbs).control_delay > 0) {
+                       ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay);
+               }
+               break;
+       }
+       
+       case VIDIOCPWCGAWBSPEED:
+       {
+               ARG_DEF(struct pwc_wb_speed, wbs)
+               
+               ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed);
+               if (ret < 0)
+                       break;
+               ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay);
+               if (ret < 0)
+                       break;
+               ARG_OUT(wbs)
+               break;
+       }
+
+        case VIDIOCPWCSLED:
+       {
+               ARG_DEF(struct pwc_leds, leds)
+
+               ARG_IN(leds)
+               ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off);
+               break;
+       }
+
+
+       case VIDIOCPWCGLED:
+       {
+               ARG_DEF(struct pwc_leds, leds)
+               
+               ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off);
+               ARG_OUT(leds)
+               break;
+       }
+
+       case VIDIOCPWCSCONTOUR:
+       {
+               ARG_DEF(int, contour)
+
+               ARG_IN(contour)
+               ret = pwc_set_contour(pdev, ARGR(contour));
+               break;
+       }
+                       
+       case VIDIOCPWCGCONTOUR:
+       {
+               ARG_DEF(int, contour)
+               
+               ret = pwc_get_contour(pdev, ARGA(contour));
+               ARG_OUT(contour)
+               break;
+       }
+       
+       case VIDIOCPWCSBACKLIGHT:
+       {
+               ARG_DEF(int, backlight)
+               
+               ARG_IN(backlight)
+               ret = pwc_set_backlight(pdev, ARGR(backlight));
+               break;
+       }
+
+       case VIDIOCPWCGBACKLIGHT:
+       {
+               ARG_DEF(int, backlight)
+               
+               ret = pwc_get_backlight(pdev, ARGA(backlight));
+               ARG_OUT(backlight)
+               break;
+       }
+       
+       case VIDIOCPWCSFLICKER:
+       {
+               ARG_DEF(int, flicker)
+               
+               ARG_IN(flicker)
+               ret = pwc_set_flicker(pdev, ARGR(flicker));
+               break;
+       }
+
+       case VIDIOCPWCGFLICKER:
+       {
+               ARG_DEF(int, flicker)
+               
+               ret = pwc_get_flicker(pdev, ARGA(flicker));
+               ARG_OUT(flicker)
+               break;
+       }
+       
+       case VIDIOCPWCSDYNNOISE:
+       {
+               ARG_DEF(int, dynnoise)
+               
+               ARG_IN(dynnoise)
+               ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise));
+               break;
+       }
+       
+       case VIDIOCPWCGDYNNOISE:
+       {
+               ARG_DEF(int, dynnoise)
+
+               ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise));
+               ARG_OUT(dynnoise);
+               break;
+       }
+
+       case VIDIOCPWCGREALSIZE:
+       {
+               ARG_DEF(struct pwc_imagesize, size)
+               
+               ARGR(size).width = pdev->image.x;
+               ARGR(size).height = pdev->image.y;
+               ARG_OUT(size)
+               break;
+       }
+       
+       case VIDIOCPWCMPTRESET:
+       {
+               if (pdev->features & FEATURE_MOTOR_PANTILT)
+               {
+                       ARG_DEF(int, flags)
+
+                       ARG_IN(flags)
+                       ret = pwc_mpt_reset(pdev, ARGR(flags));
+                       if (ret >= 0)
+                       {
+                               pdev->pan_angle = 0;
+                               pdev->tilt_angle = 0;
+                       }
+               }
+               else
+               {
+                       ret = -ENXIO;
+               }
+               break;          
+       }
+       
+       case VIDIOCPWCMPTGRANGE:
+       {
+               if (pdev->features & FEATURE_MOTOR_PANTILT)
+               {
+                       ARG_DEF(struct pwc_mpt_range, range)
+                       
+                       ARGR(range) = pdev->angle_range;
+                       ARG_OUT(range)
+               }
+               else
+               {       
+                       ret = -ENXIO;
+               }
+               break;
+       }
+       
+       case VIDIOCPWCMPTSANGLE:
+       {
+               int new_pan, new_tilt;
+               
+               if (pdev->features & FEATURE_MOTOR_PANTILT)
+               {
+                       ARG_DEF(struct pwc_mpt_angles, angles)
+
+                       ARG_IN(angles)
+                       /* The camera can only set relative angles, so
+                          do some calculations when getting an absolute angle .
+                        */
+                       if (ARGR(angles).absolute)
+                       {
+                               new_pan  = ARGR(angles).pan; 
+                               new_tilt = ARGR(angles).tilt;
+                       }
+                       else
+                       {
+                               new_pan  = pdev->pan_angle  + ARGR(angles).pan;
+                               new_tilt = pdev->tilt_angle + ARGR(angles).tilt;
+                       }
+                       /* check absolute ranges */
+                       if (new_pan  < pdev->angle_range.pan_min  ||
+                           new_pan  > pdev->angle_range.pan_max  ||
+                           new_tilt < pdev->angle_range.tilt_min ||
+                           new_tilt > pdev->angle_range.tilt_max)
+                       {
+                               ret = -ERANGE;
+                       }
+                       else
+                       {
+                               /* go to relative range, check again */
+                               new_pan  -= pdev->pan_angle;
+                               new_tilt -= pdev->tilt_angle;
+                               /* angles are specified in degrees * 100, thus the limit = 36000 */
+                               if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000)
+                                       ret = -ERANGE;
+                       }
+                       if (ret == 0) /* no errors so far */
+                       {
+                               ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt);
+                               if (ret >= 0)
+                               {
+                                       pdev->pan_angle  += new_pan;
+                                       pdev->tilt_angle += new_tilt;
+                               }
+                               if (ret == -EPIPE) /* stall -> out of range */
+                                       ret = -ERANGE;                          
+                       }
+               }
+               else
+               {
+                       ret = -ENXIO;
+               }
+               break;
+       }
+
+       case VIDIOCPWCMPTGANGLE:
+       {
+               
+               if (pdev->features & FEATURE_MOTOR_PANTILT)
+               {
+                       ARG_DEF(struct pwc_mpt_angles, angles)
+
+                       ARGR(angles).absolute = 1;
+                       ARGR(angles).pan  = pdev->pan_angle;
+                       ARGR(angles).tilt = pdev->tilt_angle;
+                       ARG_OUT(angles)
+               }
+               else
+               {
+                       ret = -ENXIO;
+               }
+               break;
+       }
+       case VIDIOCPWCMPTSTATUS:
+       {
+               if (pdev->features & FEATURE_MOTOR_PANTILT)
+               {
+                       ARG_DEF(struct pwc_mpt_status, status)
+                       
+                       ret = pwc_mpt_get_status(pdev, ARGA(status));
+                       ARG_OUT(status)
+               }
+               else
+               {
+                       ret = -ENXIO;
+               }
+               break;
+       }
+
+       case VIDIOCPWCGVIDCMD:
+       {
+               ARG_DEF(struct pwc_video_command, cmd);
+               
+                ARGR(cmd).type = pdev->type;
+               ARGR(cmd).release = pdev->release;
+               ARGR(cmd).command_len = pdev->cmd_len;
+               memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len);
+               ARGR(cmd).bandlength = pdev->vbandlength;
+               ARGR(cmd).frame_size = pdev->frame_size;
+               ARG_OUT(cmd)
+               break;
+       }
+       /*
+       case VIDIOCPWCGVIDTABLE:
+       {
+               ARG_DEF(struct pwc_table_init_buffer, table);
+               ARGR(table).len = pdev->cmd_len;
+               memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size);
+               ARG_OUT(table)
+               break;
+       }
+       */
+
+       default:
+               ret = -ENOIOCTLCMD;
+               break;
+       }
+       
+       if (ret > 0)
+               return 0;
+       return ret;
+}
+
+
+
diff --git a/drivers/usb/media/pwc/pwc-if.c b/drivers/usb/media/pwc/pwc-if.c
new file mode 100644 (file)
index 0000000..d5c4f24
--- /dev/null
@@ -0,0 +1,2211 @@
+/* Linux driver for Philips webcam
+   USB and Video4Linux interface part.
+   (C) 1999-2004 Nemosoft Unv.
+   (C) 2004      Luc Saillard (luc@saillard.org)
+
+   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+   driver and thus may have bugs that are not present in the original version.
+   Please send bug reports and support requests to <luc@saillard.org>.
+   The decompression routines have been implemented by reverse-engineering the
+   Nemosoft binary pwcx module. Caveat emptor.
+
+   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
+
+*/
+
+/*  
+   This code forms the interface between the USB layers and the Philips
+   specific stuff. Some adanved stuff of the driver falls under an
+   NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and
+   is thus not distributed in source form. The binary pwcx.o module 
+   contains the code that falls under the NDA.
+   
+   In case you're wondering: 'pwc' stands for "Philips WebCam", but 
+   I really didn't want to type 'philips_web_cam' every time (I'm lazy as
+   any Linux kernel hacker, but I don't like uncomprehensible abbreviations
+   without explanation).
+   
+   Oh yes, convention: to disctinguish between all the various pointers to
+   device-structures, I use these names for the pointer variables:
+   udev: struct usb_device *
+   vdev: struct video_device *
+   pdev: struct pwc_devive *
+*/
+
+/* Contributors:
+   - Alvarado: adding whitebalance code
+   - Alistar Moire: QuickCam 3000 Pro device/product ID
+   - Tony Hoyle: Creative Labs Webcam 5 device/product ID
+   - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
+   - Jk Fang: Sotec Afina Eye ID
+   - Xavier Roche: QuickCam Pro 4000 ID
+   - Jens Knudsen: QuickCam Zoom ID
+   - J. Debert: QuickCam for Notebooks ID
+*/
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+
+#include "pwc.h"
+#include "pwc-ioctl.h"
+#include "pwc-kiara.h"
+#include "pwc-timon.h"
+#include "pwc-dec23.h"
+#include "pwc-dec1.h"
+#include "pwc-uncompress.h"
+
+/* Function prototypes and driver templates */
+
+/* hotplug device table support */
+static struct usb_device_id pwc_device_table [] = {
+       { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
+       { USB_DEVICE(0x0471, 0x0303) },
+       { USB_DEVICE(0x0471, 0x0304) },
+       { USB_DEVICE(0x0471, 0x0307) },
+       { USB_DEVICE(0x0471, 0x0308) },
+       { USB_DEVICE(0x0471, 0x030C) },
+       { USB_DEVICE(0x0471, 0x0310) },
+       { USB_DEVICE(0x0471, 0x0311) },
+       { USB_DEVICE(0x0471, 0x0312) },
+       { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
+       { USB_DEVICE(0x069A, 0x0001) }, /* Askey */
+       { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
+       { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
+       { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */
+       { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
+       { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
+       { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
+       { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */
+       { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */
+       { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
+       { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */
+       { USB_DEVICE(0x055D, 0x9001) },
+       { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
+       { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
+       { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
+       { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */
+       { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
+       { USB_DEVICE(0x0d81, 0x1900) },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, pwc_device_table);
+
+static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
+static void usb_pwc_disconnect(struct usb_interface *intf);
+
+static struct usb_driver pwc_driver = {
+       .owner =                THIS_MODULE,
+       .name =                 "Philips webcam",       /* name */
+       .id_table =             pwc_device_table,
+       .probe =                usb_pwc_probe,          /* probe() */
+       .disconnect =           usb_pwc_disconnect,     /* disconnect() */
+};
+
+#define MAX_DEV_HINTS  20
+#define MAX_ISOC_ERRORS        20
+
+static int default_size = PSZ_QCIF;
+static int default_fps = 10;
+static int default_fbufs = 3;   /* Default number of frame buffers */
+static int default_mbufs = 2;  /* Default number of mmap() buffers */
+       int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX;
+static int power_save = 0;
+static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */
+       int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */
+static struct {
+       int type;
+       char serial_number[30];
+       int device_node;
+       struct pwc_device *pdev;
+} device_hint[MAX_DEV_HINTS];
+
+/***/
+
+static int pwc_video_open(struct inode *inode, struct file *file);
+static int pwc_video_close(struct inode *inode, struct file *file);
+static ssize_t pwc_video_read(struct file *file, char *buf,
+                         size_t count, loff_t *ppos);
+static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
+static int  pwc_video_ioctl(struct inode *inode, struct file *file,
+                           unsigned int ioctlnr, unsigned long arg);
+static int  pwc_video_mmap(struct file *file, struct vm_area_struct *vma);
+
+static struct file_operations pwc_fops = {
+       .owner =        THIS_MODULE,
+       .open =         pwc_video_open,
+       .release =      pwc_video_close,
+       .read =         pwc_video_read,
+       .poll =         pwc_video_poll,
+       .mmap =         pwc_video_mmap,
+       .ioctl =        pwc_video_ioctl,
+       .llseek =       no_llseek,
+};
+static struct video_device pwc_template = {
+       .owner =        THIS_MODULE,
+       .name =         "Philips Webcam",       /* Filled in later */
+       .type =         VID_TYPE_CAPTURE,
+       .hardware =     VID_HARDWARE_PWC,
+       .release =      video_device_release,
+       .fops =         &pwc_fops,
+       .minor =        -1,
+};
+
+/***************************************************************************/
+
+/* Okay, this is some magic that I worked out and the reasoning behind it...
+
+   The biggest problem with any USB device is of course: "what to do 
+   when the user unplugs the device while it is in use by an application?"
+   We have several options:
+   1) Curse them with the 7 plagues when they do (requires divine intervention)
+   2) Tell them not to (won't work: they'll do it anyway)
+   3) Oops the kernel (this will have a negative effect on a user's uptime)
+   4) Do something sensible.
+   
+   Of course, we go for option 4.
+
+   It happens that this device will be linked to two times, once from
+   usb_device and once from the video_device in their respective 'private'
+   pointers. This is done when the device is probed() and all initialization
+   succeeded. The pwc_device struct links back to both structures.
+
+   When a device is unplugged while in use it will be removed from the 
+   list of known USB devices; I also de-register it as a V4L device, but 
+   unfortunately I can't free the memory since the struct is still in use
+   by the file descriptor. This free-ing is then deferend until the first
+   opportunity. Crude, but it works.
+   
+   A small 'advantage' is that if a user unplugs the cam and plugs it back
+   in, it should get assigned the same video device minor, but unfortunately
+   it's non-trivial to re-link the cam back to the video device... (that 
+   would surely be magic! :))
+*/
+
+/***************************************************************************/
+/* Private functions */
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the area.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr) 
+{
+        unsigned long kva, ret;
+
+       kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
+       kva |= adr & (PAGE_SIZE-1); /* restore the offset */
+       ret = __pa(kva);
+        return ret;
+}
+
+static void * rvmalloc(unsigned long size)
+{
+       void * mem;
+       unsigned long adr;
+
+       size=PAGE_ALIGN(size);
+        mem=vmalloc_32(size);
+       if (mem) 
+       {
+               memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+               adr=(unsigned long) mem;
+               while (size > 0) 
+                {
+                       SetPageReserved(vmalloc_to_page((void *)adr));
+                       adr+=PAGE_SIZE;
+                       size-=PAGE_SIZE;
+               }
+       }
+       return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+        unsigned long adr;
+
+       if (mem) 
+       {
+               adr=(unsigned long) mem;
+               while ((long) size > 0) 
+                {
+                       ClearPageReserved(vmalloc_to_page((void *)adr));
+                       adr+=PAGE_SIZE;
+                       size-=PAGE_SIZE;
+               }
+               vfree(mem);
+       }
+}
+
+
+
+
+static int pwc_allocate_buffers(struct pwc_device *pdev)
+{
+       int i;
+       void *kbuf;
+
+       Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
+
+       if (pdev == NULL)
+               return -ENXIO;
+               
+#ifdef PWC_MAGIC
+       if (pdev->magic != PWC_MAGIC) {
+               Err("allocate_buffers(): magic failed.\n");
+               return -ENXIO;
+       }
+#endif 
+       /* Allocate Isochronuous pipe buffers */
+       for (i = 0; i < MAX_ISO_BUFS; i++) {
+               if (pdev->sbuf[i].data == NULL) {
+                       kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
+                       if (kbuf == NULL) {
+                               Err("Failed to allocate iso buffer %d.\n", i);
+                               return -ENOMEM;
+                       }
+                       Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf);
+                       pdev->sbuf[i].data = kbuf;
+                       memset(kbuf, 0, ISO_BUFFER_SIZE);
+               }
+       }
+
+       /* Allocate frame buffer structure */
+       if (pdev->fbuf == NULL) {
+               kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL);
+               if (kbuf == NULL) {
+                       Err("Failed to allocate frame buffer structure.\n");
+                       return -ENOMEM;
+               }
+               Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf);
+               pdev->fbuf = kbuf;
+               memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf));
+       }
+       /* create frame buffers, and make circular ring */
+       for (i = 0; i < default_fbufs; i++) {
+               if (pdev->fbuf[i].data == NULL) {
+                       kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */
+                       if (kbuf == NULL) {
+                               Err("Failed to allocate frame buffer %d.\n", i);
+                               return -ENOMEM;
+                       }
+                       Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf);
+                       pdev->fbuf[i].data = kbuf;
+                       memset(kbuf, 128, PWC_FRAME_SIZE);
+               }
+       }
+       
+       /* Allocate decompressor table space */
+       kbuf = NULL;
+       switch (pdev->type)
+        {
+         case 675:
+         case 680:
+         case 690:
+         case 720:
+         case 730:
+         case 740:
+         case 750:
+           Trace(TRACE_MEMORY,"private_data(%d)\n",sizeof(struct pwc_dec23_private));
+           kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL);       /* Timon & Kiara */
+           break;
+         case 645:
+         case 646:
+           /* TODO & FIXME */
+           kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL);
+           break;
+        }
+       if (kbuf == NULL) {
+          Err("Failed to allocate decompress table.\n");
+          return -ENOMEM;
+       }
+       pdev->decompress_data = kbuf;
+       
+       /* Allocate image buffer; double buffer for mmap() */
+       kbuf = rvmalloc(default_mbufs * pdev->len_per_image);
+       if (kbuf == NULL) {
+               Err("Failed to allocate image buffer(s). needed (%d)\n",default_mbufs * pdev->len_per_image);
+               return -ENOMEM;
+       }
+       Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf);
+       pdev->image_data = kbuf;
+       for (i = 0; i < default_mbufs; i++)
+               pdev->image_ptr[i] = kbuf + i * pdev->len_per_image;
+       for (; i < MAX_IMAGES; i++)
+               pdev->image_ptr[i] = NULL;
+
+       kbuf = NULL;
+         
+       Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n");
+       return 0;
+}
+
+static void pwc_free_buffers(struct pwc_device *pdev)
+{
+       int i;
+
+       Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev);
+
+       if (pdev == NULL)
+               return;
+#ifdef PWC_MAGIC
+       if (pdev->magic != PWC_MAGIC) {
+               Err("free_buffers(): magic failed.\n");
+               return;
+       }
+#endif 
+
+       /* Release Iso-pipe buffers */
+       for (i = 0; i < MAX_ISO_BUFS; i++)
+               if (pdev->sbuf[i].data != NULL) {
+                       Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data);
+                       kfree(pdev->sbuf[i].data);
+                       pdev->sbuf[i].data = NULL;
+               }
+
+       /* The same for frame buffers */
+       if (pdev->fbuf != NULL) {
+               for (i = 0; i < default_fbufs; i++) {
+                       if (pdev->fbuf[i].data != NULL) {
+                               Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data);
+                               vfree(pdev->fbuf[i].data);
+                               pdev->fbuf[i].data = NULL;
+                       }
+               }
+               kfree(pdev->fbuf);
+               pdev->fbuf = NULL;
+       }
+
+       /* Intermediate decompression buffer & tables */
+       if (pdev->decompress_data != NULL) {
+               Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data);
+               kfree(pdev->decompress_data);
+               pdev->decompress_data = NULL;
+       }
+       pdev->decompressor = NULL;
+
+       /* Release image buffers */
+       if (pdev->image_data != NULL) {
+               Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data);
+               rvfree(pdev->image_data, default_mbufs * pdev->len_per_image);
+       }
+       pdev->image_data = NULL;
+       
+       Trace(TRACE_MEMORY, "Leaving free_buffers().\n");
+}
+
+/* The frame & image buffer mess. 
+
+   Yes, this is a mess. Well, it used to be simple, but alas...  In this
+   module, 3 buffers schemes are used to get the data from the USB bus to
+   the user program. The first scheme involves the ISO buffers (called thus
+   since they transport ISO data from the USB controller), and not really
+   interesting. Suffices to say the data from this buffer is quickly 
+   gathered in an interrupt handler (pwc_isoc_handler) and placed into the
+   frame buffer.
+
+   The frame buffer is the second scheme, and is the central element here.
+   It collects the data from a single frame from the camera (hence, the
+   name). Frames are delimited by the USB camera with a short USB packet,
+   so that's easy to detect. The frame buffers form a list that is filled
+   by the camera+USB controller and drained by the user process through
+   either read() or mmap().
+
+   The image buffer is the third scheme, in which frames are decompressed
+   and converted into planar format. For mmap() there is more than
+   one image buffer available.
+
+   The frame buffers provide the image buffering. In case the user process
+   is a bit slow, this introduces lag and some undesired side-effects.
+   The problem arises when the frame buffer is full. I used to drop the last
+   frame, which makes the data in the queue stale very quickly. But dropping
+   the frame at the head of the queue proved to be a litte bit more difficult.
+   I tried a circular linked scheme, but this introduced more problems than
+   it solved.
+
+   Because filling and draining are completely asynchronous processes, this
+   requires some fiddling with pointers and mutexes.
+
+   Eventually, I came up with a system with 2 lists: an 'empty' frame list
+   and a 'full' frame list:
+     * Initially, all frame buffers but one are on the 'empty' list; the one
+       remaining buffer is our initial fill frame.
+     * If a frame is needed for filling, we try to take it from the 'empty' 
+       list, unless that list is empty, in which case we take the buffer at 
+       the head of the 'full' list.
+     * When our fill buffer has been filled, it is appended to the 'full'
+       list.
+     * If a frame is needed by read() or mmap(), it is taken from the head of
+       the 'full' list, handled, and then appended to the 'empty' list. If no
+       buffer is present on the 'full' list, we wait.
+   The advantage is that the buffer that is currently being decompressed/
+   converted, is on neither list, and thus not in our way (any other scheme
+   I tried had the problem of old data lingering in the queue).
+
+   Whatever strategy you choose, it always remains a tradeoff: with more
+   frame buffers the chances of a missed frame are reduced. On the other
+   hand, on slower machines it introduces lag because the queue will
+   always be full.
+ */
+
+/**
+  \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first.
+ */
+static inline int pwc_next_fill_frame(struct pwc_device *pdev)
+{
+       int ret;
+       unsigned long flags;
+
+       ret = 0;
+       spin_lock_irqsave(&pdev->ptrlock, flags);
+       if (pdev->fill_frame != NULL) {
+               /* append to 'full' list */
+               if (pdev->full_frames == NULL) {
+                       pdev->full_frames = pdev->fill_frame;
+                       pdev->full_frames_tail = pdev->full_frames;
+               }
+               else {
+                       pdev->full_frames_tail->next = pdev->fill_frame;
+                       pdev->full_frames_tail = pdev->fill_frame;
+               }
+       }
+       if (pdev->empty_frames != NULL) {
+               /* We have empty frames available. That's easy */
+               pdev->fill_frame = pdev->empty_frames;
+               pdev->empty_frames = pdev->empty_frames->next;
+       }
+       else {
+               /* Hmm. Take it from the full list */
+#if PWC_DEBUG
+               /* sanity check */
+               if (pdev->full_frames == NULL) {
+                       Err("Neither empty or full frames available!\n");
+                       spin_unlock_irqrestore(&pdev->ptrlock, flags);
+                       return -EINVAL;
+               }
+#endif
+               pdev->fill_frame = pdev->full_frames;
+               pdev->full_frames = pdev->full_frames->next;
+               ret = 1;
+       }
+       pdev->fill_frame->next = NULL;
+#if PWC_DEBUG
+       Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence);
+       pdev->fill_frame->sequence = pdev->sequence++;
+#endif
+       spin_unlock_irqrestore(&pdev->ptrlock, flags);
+       return ret;
+}
+
+
+/**
+  \brief Reset all buffers, pointers and lists, except for the image_used[] buffer.
+
+  If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble.
+ */
+static void pwc_reset_buffers(struct pwc_device *pdev)
+{
+       int i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pdev->ptrlock, flags);
+       pdev->full_frames = NULL;
+       pdev->full_frames_tail = NULL;
+       for (i = 0; i < default_fbufs; i++) {
+               pdev->fbuf[i].filled = 0;
+               if (i > 0)
+                       pdev->fbuf[i].next = &pdev->fbuf[i - 1];
+               else
+                       pdev->fbuf->next = NULL;
+       }
+       pdev->empty_frames = &pdev->fbuf[default_fbufs - 1];
+       pdev->empty_frames_tail = pdev->fbuf;
+       pdev->read_frame = NULL;
+       pdev->fill_frame = pdev->empty_frames;
+       pdev->empty_frames = pdev->empty_frames->next;
+
+       pdev->image_read_pos = 0;
+       pdev->fill_image = 0;
+       spin_unlock_irqrestore(&pdev->ptrlock, flags);
+}
+
+
+/**
+  \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers.
+ */
+static int pwc_handle_frame(struct pwc_device *pdev)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pdev->ptrlock, flags);
+       /* First grab our read_frame; this is removed from all lists, so
+          we can release the lock after this without problems */
+       if (pdev->read_frame != NULL) {
+               /* This can't theoretically happen */
+               Err("Huh? Read frame still in use?\n");
+       }
+       else {
+               if (pdev->full_frames == NULL) {
+                       Err("Woops. No frames ready.\n");
+               }
+               else {
+                       pdev->read_frame = pdev->full_frames;
+                       pdev->full_frames = pdev->full_frames->next;
+                       pdev->read_frame->next = NULL;
+               }
+
+               if (pdev->read_frame != NULL) {
+#if PWC_DEBUG
+                       Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence);
+#endif
+                       /* Decompression is a lenghty process, so it's outside of the lock.
+                          This gives the isoc_handler the opportunity to fill more frames
+                          in the mean time.
+                       */
+                       spin_unlock_irqrestore(&pdev->ptrlock, flags);
+                       ret = pwc_decompress(pdev);
+                       spin_lock_irqsave(&pdev->ptrlock, flags);
+
+                       /* We're done with read_buffer, tack it to the end of the empty buffer list */
+                       if (pdev->empty_frames == NULL) {
+                               pdev->empty_frames = pdev->read_frame;
+                               pdev->empty_frames_tail = pdev->empty_frames;
+                       }
+                       else {
+                               pdev->empty_frames_tail->next = pdev->read_frame;
+                               pdev->empty_frames_tail = pdev->read_frame;
+                       }
+                       pdev->read_frame = NULL;
+               }
+       }
+       spin_unlock_irqrestore(&pdev->ptrlock, flags);
+       return ret;
+}
+
+/**
+  \brief Advance pointers of image buffer (after each user request)
+*/
+static inline void pwc_next_image(struct pwc_device *pdev)
+{
+       pdev->image_used[pdev->fill_image] = 0;
+       pdev->fill_image = (pdev->fill_image + 1) % default_mbufs;
+}
+
+
+/* This gets called for the Isochronous pipe (video). This is done in
+ * interrupt time, so it has to be fast, not crash, and not stall. Neat.
+ */
+static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
+{
+       struct pwc_device *pdev;
+       int i, fst, flen;
+       int awake;
+       struct pwc_frame_buf *fbuf;
+       unsigned char *fillptr = 0, *iso_buf = 0;
+
+       awake = 0;
+       pdev = (struct pwc_device *)urb->context;
+       if (pdev == NULL) {
+               Err("isoc_handler() called with NULL device?!\n");
+               return;
+       }
+#ifdef PWC_MAGIC
+       if (pdev->magic != PWC_MAGIC) {
+               Err("isoc_handler() called with bad magic!\n");
+               return;
+       }
+#endif
+       if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
+               Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
+               return;
+       }
+       if (urb->status != -EINPROGRESS && urb->status != 0) {
+               const char *errmsg;
+
+               errmsg = "Unknown";
+               switch(urb->status) {
+                       case -ENOSR:            errmsg = "Buffer error (overrun)"; break;
+                       case -EPIPE:            errmsg = "Stalled (device not responding)"; break;
+                       case -EOVERFLOW:        errmsg = "Babble (bad cable?)"; break;
+                       case -EPROTO:           errmsg = "Bit-stuff error (bad cable?)"; break;
+                       case -EILSEQ:           errmsg = "CRC/Timeout (could be anything)"; break;
+                       case -ETIMEDOUT:        errmsg = "NAK (device does not respond)"; break;
+               }
+               Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
+               /* Give up after a number of contiguous errors on the USB bus. 
+                  Appearantly something is wrong so we simulate an unplug event.
+                */
+               if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
+               {
+                       Info("Too many ISOC errors, bailing out.\n");
+                       pdev->error_status = EIO;
+                       awake = 1;
+                       wake_up_interruptible(&pdev->frameq);
+               }
+               goto handler_end; // ugly, but practical
+       }
+
+       fbuf = pdev->fill_frame;
+       if (fbuf == NULL) {
+               Err("pwc_isoc_handler without valid fill frame.\n");
+               awake = 1;
+               goto handler_end;
+       }
+       else {
+               fillptr = fbuf->data + fbuf->filled;
+       }
+
+       /* Reset ISOC error counter. We did get here, after all. */
+       pdev->visoc_errors = 0;
+
+       /* vsync: 0 = don't copy data
+                 1 = sync-hunt
+                 2 = synched
+        */
+       /* Compact data */
+       for (i = 0; i < urb->number_of_packets; i++) {
+               fst  = urb->iso_frame_desc[i].status;
+               flen = urb->iso_frame_desc[i].actual_length;
+               iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+               if (fst == 0) {
+                       if (flen > 0) { /* if valid data... */
+                               if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */
+                                       pdev->vsync = 2;
+
+                                       /* ...copy data to frame buffer, if possible */
+                                       if (flen + fbuf->filled > pdev->frame_total_size) {
+                                               Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size);
+                                               pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */
+                                               pdev->vframes_error++;
+                                       }
+                                       else {
+                                               memmove(fillptr, iso_buf, flen);
+                                               fillptr += flen;
+                                       }
+                               }
+                               fbuf->filled += flen;
+                       } /* ..flen > 0 */
+
+                       if (flen < pdev->vlast_packet_size) {
+                               /* Shorter packet... We probably have the end of an image-frame; 
+                                  wake up read() process and let select()/poll() do something.
+                                  Decompression is done in user time over there.
+                                */
+                               if (pdev->vsync == 2) {
+                                       /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus 
+                                          frames on the USB wire after an exposure change. This conditition is 
+                                          however detected  in the cam and a bit is set in the header.
+                                        */
+                                       if (pdev->type == 730) {
+                                               unsigned char *ptr = (unsigned char *)fbuf->data;
+                                               
+                                               if (ptr[1] == 1 && ptr[0] & 0x10) {
+#if PWC_DEBUG
+                                                       Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence);
+#endif
+                                                       pdev->drop_frames += 2;
+                                                       pdev->vframes_error++;
+                                               }
+                                               if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+                                                       if (ptr[0] & 0x01)
+                                                               Info("Snapshot button pressed.\n");
+                                                       else
+                                                               Info("Snapshot button released.\n");
+                                               }
+                                               if ((ptr[0] ^ pdev->vmirror) & 0x02) {
+                                                       if (ptr[0] & 0x02)
+                                                               Info("Image is mirrored.\n");
+                                                       else
+                                                               Info("Image is normal.\n");
+                                               }
+                                               pdev->vmirror = ptr[0] & 0x03;
+                                               /* Sometimes the trailer of the 730 is still sent as a 4 byte packet 
+                                                  after a short frame; this condition is filtered out specifically. A 4 byte
+                                                  frame doesn't make sense anyway.
+                                                  So we get either this sequence: 
+                                                       drop_bit set -> 4 byte frame -> short frame -> good frame
+                                                  Or this one:
+                                                       drop_bit set -> short frame -> good frame
+                                                  So we drop either 3 or 2 frames in all!
+                                                */
+                                               if (fbuf->filled == 4)
+                                                       pdev->drop_frames++;
+                                       }
+
+                                       /* In case we were instructed to drop the frame, do so silently.
+                                          The buffer pointers are not updated either (but the counters are reset below).
+                                        */
+                                       if (pdev->drop_frames > 0)
+                                               pdev->drop_frames--;
+                                       else {
+                                               /* Check for underflow first */
+                                               if (fbuf->filled < pdev->frame_total_size) {
+                                                       Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled);
+                                                       pdev->vframes_error++;
+                                               }
+                                               else {
+                                                       /* Send only once per EOF */
+                                                       awake = 1; /* delay wake_ups */
+
+                                                       /* Find our next frame to fill. This will always succeed, since we
+                                                        * nick a frame from either empty or full list, but if we had to
+                                                        * take it from the full list, it means a frame got dropped.
+                                                        */
+                                                       if (pwc_next_fill_frame(pdev)) {
+                                                               pdev->vframes_dumped++;
+                                                               if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) {
+                                                                       if (pdev->vframes_dumped < 20)
+                                                                               Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count);
+                                                                       if (pdev->vframes_dumped == 20)
+                                                                               Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count);
+                                                               }
+                                                       }
+                                                       fbuf = pdev->fill_frame;
+                                               }
+                                       } /* !drop_frames */
+                                       pdev->vframe_count++;
+                               }
+                               fbuf->filled = 0;
+                               fillptr = fbuf->data;
+                               pdev->vsync = 1;
+                       } /* .. flen < last_packet_size */
+                       pdev->vlast_packet_size = flen;
+               } /* ..status == 0 */
+#if PWC_DEBUG
+               /* This is normally not interesting to the user, unless you are really debugging something */
+               else {
+                       static int iso_error = 0;
+                       iso_error++;
+                       if (iso_error < 20)
+                               Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);
+               }
+#endif
+       }
+
+handler_end:
+       if (awake)
+               wake_up_interruptible(&pdev->frameq);
+
+       urb->dev = pdev->udev;
+       i = usb_submit_urb(urb, GFP_ATOMIC);
+       if (i != 0)
+               Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
+}
+
+
+static int pwc_isoc_init(struct pwc_device *pdev)
+{
+       struct usb_device *udev;
+       struct urb *urb;
+       int i, j, ret;
+
+       struct usb_interface *intf;
+       struct usb_host_interface *idesc = NULL;
+
+       if (pdev == NULL)
+               return -EFAULT;
+       if (pdev->iso_init)
+               return 0;
+       pdev->vsync = 0;
+       udev = pdev->udev;
+
+       /* Get the current alternate interface, adjust packet size */
+       if (!udev->actconfig)
+               return -EFAULT;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)
+       idesc = &udev->actconfig->interface[0]->altsetting[pdev->valternate];
+#else
+       intf = usb_ifnum_to_if(udev, 0);
+       if (intf)
+               idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
+#endif
+               
+       if (!idesc)
+               return -EFAULT;
+
+       /* Search video endpoint */
+       pdev->vmax_packet_size = -1;
+       for (i = 0; i < idesc->desc.bNumEndpoints; i++)
+               if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
+                       pdev->vmax_packet_size = idesc->endpoint[i].desc.wMaxPacketSize;
+                       break;
+               }
+       
+       if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
+               Err("Failed to find packet size for video endpoint in current alternate setting.\n");
+               return -ENFILE; /* Odd error, that should be noticable */
+       }
+
+       /* Set alternate interface */
+       ret = 0;
+       Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate);
+       ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < MAX_ISO_BUFS; i++) {
+               urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
+               if (urb == NULL) {
+                       Err("Failed to allocate urb %d\n", i);
+                       ret = -ENOMEM;
+                       break;
+               }
+               pdev->sbuf[i].urb = urb;
+               Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb);
+       }
+       if (ret) {
+               /* De-allocate in reverse order */
+               while (i >= 0) {
+                       if (pdev->sbuf[i].urb != NULL)
+                               usb_free_urb(pdev->sbuf[i].urb);
+                       pdev->sbuf[i].urb = NULL;
+                       i--;
+               }
+               return ret;
+       }
+
+       /* init URB structure */        
+       for (i = 0; i < MAX_ISO_BUFS; i++) {
+               urb = pdev->sbuf[i].urb;
+
+               urb->interval = 1; // devik
+               urb->dev = udev;
+               urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
+               urb->transfer_flags = URB_ISO_ASAP;
+               urb->transfer_buffer = pdev->sbuf[i].data;
+               urb->transfer_buffer_length = ISO_BUFFER_SIZE;
+               urb->complete = pwc_isoc_handler;
+               urb->context = pdev;
+               urb->start_frame = 0;
+               urb->number_of_packets = ISO_FRAMES_PER_DESC;
+               for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
+                       urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
+                       urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
+               }
+       }
+
+       /* link */
+       for (i = 0; i < MAX_ISO_BUFS; i++) {
+               ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL);
+               if (ret)
+                       Err("isoc_init() submit_urb %d failed with error %d\n", i, ret);
+               else
+                       Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb);
+       }
+
+       /* All is done... */
+       pdev->iso_init = 1;
+       Trace(TRACE_OPEN, "<< pwc_isoc_init()\n");
+       return 0;
+}
+
+static void pwc_isoc_cleanup(struct pwc_device *pdev)
+{
+       int i;
+
+       Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n");
+       if (pdev == NULL)
+               return;
+
+       /* Unlinking ISOC buffers one by one */
+       for (i = 0; i < MAX_ISO_BUFS; i++) {
+               struct urb *urb;
+
+               urb = pdev->sbuf[i].urb;
+               if (urb != 0) {
+                       if (pdev->iso_init) {
+                               Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb);
+                               usb_unlink_urb(urb);
+                       }
+                       Trace(TRACE_MEMORY, "Freeing URB\n");
+                       usb_free_urb(urb);
+                       pdev->sbuf[i].urb = NULL;
+               }
+       }
+
+       /* Stop camera, but only if we are sure the camera is still there (unplug
+          is signalled by EPIPE) 
+        */
+       if (pdev->error_status && pdev->error_status != EPIPE) {
+               Trace(TRACE_OPEN, "Setting alternate interface 0.\n");
+               usb_set_interface(pdev->udev, 0, 0);
+       }
+
+       pdev->iso_init = 0;
+       Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n");
+}
+
+int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot)
+{
+       int ret, start;
+
+       /* Stop isoc stuff */
+       pwc_isoc_cleanup(pdev);
+       /* Reset parameters */
+       pwc_reset_buffers(pdev);
+       /* Try to set video mode... */
+       start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot);
+       if (ret) { 
+               Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n");
+               /* That failed... restore old mode (we know that worked) */
+               start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
+               if (start) {
+                       Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n");
+               }
+       }
+       if (start == 0)
+       {
+               if (pwc_isoc_init(pdev) < 0)
+               {
+                       Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n");
+                       ret = -EAGAIN; /* let's try again, who knows if it works a second time */
+               }
+       }
+       pdev->drop_frames++; /* try to avoid garbage during switch */
+       return ret; /* Return original error code */
+}
+
+
+/***************************************************************************/
+/* Video4Linux functions */
+
+static int pwc_video_open(struct inode *inode, struct file *file)
+{
+       int i;
+       struct video_device *vdev = video_devdata(file);
+       struct pwc_device *pdev;
+
+       Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev);
+       
+       pdev = (struct pwc_device *)vdev->priv;
+       if (pdev == NULL)
+               BUG();
+       if (pdev->vopen)
+               return -EBUSY;
+       
+       down(&pdev->modlock);
+       if (!pdev->usb_init) {
+               Trace(TRACE_OPEN, "Doing first time initialization.\n");
+               pdev->usb_init = 1;
+               
+               if (pwc_trace & TRACE_OPEN)
+               {
+                       /* Query sensor type */
+                       const char *sensor_type = NULL;
+                       int ret;
+
+                       ret = pwc_get_cmos_sensor(pdev, &i);
+                       if (ret >= 0)
+                       {
+                               switch(i) {
+                               case 0x00:  sensor_type = "Hyundai CMOS sensor"; break;
+                               case 0x20:  sensor_type = "Sony CCD sensor + TDA8787"; break;
+                               case 0x2E:  sensor_type = "Sony CCD sensor + Exas 98L59"; break;
+                               case 0x2F:  sensor_type = "Sony CCD sensor + ADI 9804"; break;
+                               case 0x30:  sensor_type = "Sharp CCD sensor + TDA8787"; break;
+                               case 0x3E:  sensor_type = "Sharp CCD sensor + Exas 98L59"; break;
+                               case 0x3F:  sensor_type = "Sharp CCD sensor + ADI 9804"; break;
+                               case 0x40:  sensor_type = "UPA 1021 sensor"; break;
+                               case 0x100: sensor_type = "VGA sensor"; break;
+                               case 0x101: sensor_type = "PAL MR sensor"; break;
+                               default:    sensor_type = "unknown type of sensor"; break;
+                               }
+                       }
+                       if (sensor_type != NULL)
+                               Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i);
+               }
+       }
+
+       /* Turn on camera */
+       if (power_save) {
+               i = pwc_camera_power(pdev, 1);
+               if (i < 0)
+                       Info("Failed to restore power to the camera! (%d)\n", i);
+       }
+       /* Set LED on/off time */
+       if (pwc_set_leds(pdev, led_on, led_off) < 0)
+               Info("Failed to set LED on/off time.\n");
+       
+       pwc_construct(pdev); /* set min/max sizes correct */
+
+       /* So far, so good. Allocate memory. */
+       i = pwc_allocate_buffers(pdev);
+       if (i < 0) {
+               Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n");
+               up(&pdev->modlock);
+               return i;
+       }
+       
+       /* Reset buffers & parameters */
+       pwc_reset_buffers(pdev);
+       for (i = 0; i < default_mbufs; i++)
+               pdev->image_used[i] = 0;
+       pdev->vframe_count = 0;
+       pdev->vframes_dumped = 0;
+       pdev->vframes_error = 0;
+       pdev->visoc_errors = 0;
+       pdev->error_status = 0;
+#if PWC_DEBUG
+       pdev->sequence = 0;
+#endif
+       pwc_construct(pdev); /* set min/max sizes correct */
+
+       /* Set some defaults */
+       pdev->vsnapshot = 0;
+
+       /* Start iso pipe for video; first try the last used video size
+          (or the default one); if that fails try QCIF/10 or QSIF/10;
+          it that fails too, give up.
+        */
+       i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0);
+       if (i)  {
+               Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n");
+               if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750)
+                       i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0);
+               else
+                       i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0);
+       }
+       if (i) {
+               Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n");
+               up(&pdev->modlock);
+               return i;
+       }
+       
+       i = pwc_isoc_init(pdev);
+       if (i) {
+               Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i);
+               up(&pdev->modlock);
+               return i;
+       }
+
+       pdev->vopen++;
+       file->private_data = vdev;
+       up(&pdev->modlock);
+       Trace(TRACE_OPEN, "<< video_open() returns 0.\n");
+       return 0;
+}
+
+/* Note that all cleanup is done in the reverse order as in _open */
+static int pwc_video_close(struct inode *inode, struct file *file)
+{
+       struct video_device *vdev = file->private_data;
+       struct pwc_device *pdev;
+       int i;
+
+       Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev);
+
+       pdev = (struct pwc_device *)vdev->priv;
+       if (pdev->vopen == 0)
+               Info("video_close() called on closed device?\n");
+
+       /* Dump statistics, but only if a reasonable amount of frames were
+          processed (to prevent endless log-entries in case of snap-shot
+          programs)
+        */
+       if (pdev->vframe_count > 20)
+               Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
+
+       switch (pdev->type)
+        {
+         case 675:
+         case 680:
+         case 690:
+         case 720:
+         case 730:
+         case 740:
+         case 750:
+           pwc_dec23_exit();   /* Timon & Kiara */
+           break;
+         case 645:
+         case 646:
+           pwc_dec1_exit();
+           break;
+        }
+
+       pwc_isoc_cleanup(pdev);
+       pwc_free_buffers(pdev);
+
+       /* Turn off LEDS and power down camera, but only when not unplugged */
+       if (pdev->error_status != EPIPE) {
+               /* Turn LEDs off */
+               if (pwc_set_leds(pdev, 0, 0) < 0)
+                       Info("Failed to set LED on/off time.\n");
+               if (power_save) {
+                       i = pwc_camera_power(pdev, 0);
+                       if (i < 0)
+                               Err("Failed to power down camera (%d)\n", i);
+               }
+       }
+       pdev->vopen = 0;
+       Trace(TRACE_OPEN, "<< video_close()\n");
+       return 0;
+}
+
+/*
+ *     FIXME: what about two parallel reads ????
+ *      ANSWER: Not supported. You can't open the device more than once,
+                despite what the V4L1 interface says. First, I don't see
+                the need, second there's no mechanism of alerting the
+                2nd/3rd/... process of events like changing image size.
+                And I don't see the point of blocking that for the
+                2nd/3rd/... process.
+                In multi-threaded environments reading parallel from any
+                device is tricky anyhow.
+ */
+
+static ssize_t pwc_video_read(struct file *file, char *buf,
+                         size_t count, loff_t *ppos)
+{
+       struct video_device *vdev = file->private_data;
+       struct pwc_device *pdev;
+       int noblock = file->f_flags & O_NONBLOCK;
+       DECLARE_WAITQUEUE(wait, current);
+        int bytes_to_read;
+
+       Trace(TRACE_READ, "video_read(0x%p, %p, %d) called.\n", vdev, buf, count);
+       if (vdev == NULL)
+               return -EFAULT;
+       pdev = vdev->priv;
+       if (pdev == NULL)
+               return -EFAULT;
+       if (pdev->error_status)
+               return -pdev->error_status; /* Something happened, report what. */
+
+       /* In case we're doing partial reads, we don't have to wait for a frame */
+       if (pdev->image_read_pos == 0) {
+               /* Do wait queueing according to the (doc)book */
+               add_wait_queue(&pdev->frameq, &wait);
+               while (pdev->full_frames == NULL) {
+                       /* Check for unplugged/etc. here */
+                       if (pdev->error_status) {
+                               remove_wait_queue(&pdev->frameq, &wait);
+                               set_current_state(TASK_RUNNING);
+                               return -pdev->error_status ;
+                       }
+                       if (noblock) {
+                               remove_wait_queue(&pdev->frameq, &wait);
+                               set_current_state(TASK_RUNNING);
+                               return -EWOULDBLOCK;
+                       }
+                       if (signal_pending(current)) {
+                               remove_wait_queue(&pdev->frameq, &wait);
+                               set_current_state(TASK_RUNNING);
+                               return -ERESTARTSYS;
+                       }
+                       schedule();
+                       set_current_state(TASK_INTERRUPTIBLE);
+               }
+               remove_wait_queue(&pdev->frameq, &wait);
+               set_current_state(TASK_RUNNING);
+                                                                                                                                                                                
+               /* Decompress and release frame */
+               if (pwc_handle_frame(pdev))
+                       return -EFAULT;
+       }
+
+       Trace(TRACE_READ, "Copying data to user space.\n");
+       if (pdev->vpalette == VIDEO_PALETTE_RAW)
+               bytes_to_read = pdev->frame_size;
+       else
+               bytes_to_read = pdev->view.size;
+
+       /* copy bytes to user space; we allow for partial reads */
+       if (count + pdev->image_read_pos > bytes_to_read)
+               count = bytes_to_read - pdev->image_read_pos;
+       if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count))
+               return -EFAULT;
+       pdev->image_read_pos += count;
+       if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */
+               pdev->image_read_pos = 0;
+               pwc_next_image(pdev);
+       }
+       return count;
+}
+
+static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
+{
+       struct video_device *vdev = file->private_data;
+       struct pwc_device *pdev;
+
+       if (vdev == NULL)
+               return -EFAULT;
+       pdev = vdev->priv;
+       if (pdev == NULL)
+               return -EFAULT;
+
+       poll_wait(file, &pdev->frameq, wait);
+       if (pdev->error_status)
+               return POLLERR;
+       if (pdev->full_frames != NULL) /* we have frames waiting */
+               return (POLLIN | POLLRDNORM);
+
+       return 0;
+}
+
+static int pwc_video_do_ioctl(struct inode *inode, struct file *file,
+                             unsigned int cmd, void *arg)
+{
+       struct video_device *vdev = file->private_data;
+       struct pwc_device *pdev;
+       DECLARE_WAITQUEUE(wait, current);
+
+       if (vdev == NULL)
+               return -EFAULT;
+       pdev = vdev->priv;
+       if (pdev == NULL)
+               return -EFAULT;
+
+       switch (cmd) {
+               /* Query cabapilities */
+               case VIDIOCGCAP:
+               {
+                       struct video_capability *caps = arg;
+
+                       strcpy(caps->name, vdev->name);
+                       caps->type = VID_TYPE_CAPTURE;
+                       caps->channels = 1;
+                       caps->audios = 1;
+                       caps->minwidth  = pdev->view_min.x;
+                       caps->minheight = pdev->view_min.y;
+                       caps->maxwidth  = pdev->view_max.x;
+                       caps->maxheight = pdev->view_max.y;
+                       break;
+               }
+
+               /* Channel functions (simulate 1 channel) */
+               case VIDIOCGCHAN:
+               {
+                       struct video_channel *v = arg;
+
+                       if (v->channel != 0)
+                               return -EINVAL;
+                       v->flags = 0;
+                       v->tuners = 0;
+                       v->type = VIDEO_TYPE_CAMERA;
+                       strcpy(v->name, "Webcam");
+                       return 0;
+               }
+
+               case VIDIOCSCHAN:
+               {
+                       /* The spec says the argument is an integer, but
+                          the bttv driver uses a video_channel arg, which
+                          makes sense becasue it also has the norm flag.
+                        */
+                       struct video_channel *v = arg;
+                       if (v->channel != 0)
+                               return -EINVAL;
+                       return 0;
+               }
+
+
+               /* Picture functions; contrast etc. */
+               case VIDIOCGPICT:
+               {
+                       struct video_picture *p = arg;
+                       int val;
+
+                       val = pwc_get_brightness(pdev);
+                       if (val >= 0)
+                               p->brightness = val;
+                       else
+                               p->brightness = 0xffff;
+                       val = pwc_get_contrast(pdev);
+                       if (val >= 0)
+                               p->contrast = val;
+                       else
+                               p->contrast = 0xffff;
+                       /* Gamma, Whiteness, what's the difference? :) */
+                       val = pwc_get_gamma(pdev);
+                       if (val >= 0)
+                               p->whiteness = val;
+                       else
+                               p->whiteness = 0xffff;
+                       val = pwc_get_saturation(pdev);
+                       if (val >= 0)
+                               p->colour = val;
+                       else
+                               p->colour = 0xffff;
+                       p->depth = 24;
+                       p->palette = pdev->vpalette;
+                       p->hue = 0xFFFF; /* N/A */
+                       break;
+               }
+
+               case VIDIOCSPICT:
+               {
+                       struct video_picture *p = arg;
+                       /*
+                        *      FIXME:  Suppose we are mid read
+                               ANSWER: No problem: the firmware of the camera
+                                       can handle brightness/contrast/etc
+                                       changes at _any_ time, and the palette
+                                       is used exactly once in the uncompress
+                                       routine.
+                        */
+                       pwc_set_brightness(pdev, p->brightness);
+                       pwc_set_contrast(pdev, p->contrast);
+                       pwc_set_gamma(pdev, p->whiteness);
+                       pwc_set_saturation(pdev, p->colour);
+                       if (p->palette && p->palette != pdev->vpalette) {
+                               switch (p->palette) {
+                                       case VIDEO_PALETTE_YUV420P:
+                                       case VIDEO_PALETTE_RAW:
+                                               pdev->vpalette = p->palette;
+                                               return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
+                                               break;
+                                       default:
+                                               return -EINVAL;
+                                               break;
+                               }
+                       }
+                       break;
+               }
+
+               /* Window/size parameters */            
+               case VIDIOCGWIN:
+               {
+                       struct video_window *vw = arg;
+                       
+                       vw->x = 0;
+                       vw->y = 0;
+                       vw->width = pdev->view.x;
+                       vw->height = pdev->view.y;
+                       vw->chromakey = 0;
+                       vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | 
+                                  (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0);
+                       break;
+               }
+               
+               case VIDIOCSWIN:
+               {
+                       struct video_window *vw = arg;
+                       int fps, snapshot, ret;
+
+                       fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
+                       snapshot = vw->flags & PWC_FPS_SNAPSHOT;
+                       if (fps == 0)
+                               fps = pdev->vframes;
+                       if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot)
+                               return 0;
+                       ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot);
+                       if (ret)
+                               return ret;
+                       break;          
+               }
+               
+               /* We don't have overlay support (yet) */
+               case VIDIOCGFBUF:
+               {
+                       struct video_buffer *vb = arg;
+
+                       memset(vb,0,sizeof(*vb));
+                       break;
+               }
+
+               /* mmap() functions */
+               case VIDIOCGMBUF:
+               {
+                       /* Tell the user program how much memory is needed for a mmap() */
+                       struct video_mbuf *vm = arg;
+                       int i;
+
+                       memset(vm, 0, sizeof(*vm));
+                       vm->size = default_mbufs * pdev->len_per_image;
+                       vm->frames = default_mbufs; /* double buffering should be enough for most applications */
+                       for (i = 0; i < default_mbufs; i++)
+                               vm->offsets[i] = i * pdev->len_per_image;
+                       break;
+               }
+
+               case VIDIOCMCAPTURE:
+               {
+                       /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */
+                       struct video_mmap *vm = arg;
+
+                       Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format);
+                       if (vm->frame < 0 || vm->frame >= default_mbufs)
+                               return -EINVAL;
+
+                       /* xawtv is nasty. It probes the available palettes
+                          by setting a very small image size and trying
+                          various palettes... The driver doesn't support
+                          such small images, so I'm working around it.
+                        */
+                       if (vm->format)
+                       {
+                               switch (vm->format)
+                               {
+                                       case VIDEO_PALETTE_YUV420P:
+                                       case VIDEO_PALETTE_RAW:
+                                               break;
+                                       default:
+                                               return -EINVAL;
+                                               break;
+                               }
+                       }
+
+                       if ((vm->width != pdev->view.x || vm->height != pdev->view.y) &&
+                           (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) {
+                               int ret;
+
+                               Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n");
+                               ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
+                               if (ret)
+                                       return ret;
+                       } /* ... size mismatch */
+
+                       /* FIXME: should we lock here? */
+                       if (pdev->image_used[vm->frame])
+                               return -EBUSY;  /* buffer wasn't available. Bummer */
+                       pdev->image_used[vm->frame] = 1;
+
+                       /* Okay, we're done here. In the SYNC call we wait until a 
+                          frame comes available, then expand image into the given 
+                          buffer.
+                          In contrast to the CPiA cam the Philips cams deliver a
+                          constant stream, almost like a grabber card. Also,
+                          we have separate buffers for the rawdata and the image,
+                          meaning we can nearly always expand into the requested buffer.
+                        */
+                       Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n");
+                       break;
+               }
+
+               case VIDIOCSYNC:
+               {
+                       /* The doc says: "Whenever a buffer is used it should
+                          call VIDIOCSYNC to free this frame up and continue."
+                          
+                          The only odd thing about this whole procedure is 
+                          that MCAPTURE flags the buffer as "in use", and
+                          SYNC immediately unmarks it, while it isn't 
+                          after SYNC that you know that the buffer actually
+                          got filled! So you better not start a CAPTURE in
+                          the same frame immediately (use double buffering). 
+                          This is not a problem for this cam, since it has 
+                          extra intermediate buffers, but a hardware 
+                          grabber card will then overwrite the buffer 
+                          you're working on.
+                        */
+                       int *mbuf = arg;
+                       int ret;
+
+                       Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf);
+
+                       /* bounds check */
+                       if (*mbuf < 0 || *mbuf >= default_mbufs)
+                               return -EINVAL;
+                       /* check if this buffer was requested anyway */
+                       if (pdev->image_used[*mbuf] == 0)
+                               return -EINVAL;
+
+                       /* Add ourselves to the frame wait-queue.
+                          
+                          FIXME: needs auditing for safety.
+                          QUESTION: In what respect? I think that using the
+                                    frameq is safe now.
+                        */
+                       add_wait_queue(&pdev->frameq, &wait);
+                       while (pdev->full_frames == NULL) {
+                               if (pdev->error_status) {
+                                       remove_wait_queue(&pdev->frameq, &wait);
+                                       set_current_state(TASK_RUNNING);
+                                       return -pdev->error_status;
+                               }
+                       
+                               if (signal_pending(current)) {
+                                       remove_wait_queue(&pdev->frameq, &wait);
+                                       set_current_state(TASK_RUNNING);
+                                       return -ERESTARTSYS;
+                               }
+                               schedule();
+                               set_current_state(TASK_INTERRUPTIBLE);
+                       }
+                       remove_wait_queue(&pdev->frameq, &wait);
+                       set_current_state(TASK_RUNNING);
+                               
+                       /* The frame is ready. Expand in the image buffer 
+                          requested by the user. I don't care if you 
+                          mmap() 5 buffers and request data in this order: 
+                          buffer 4 2 3 0 1 2 3 0 4 3 1 . . .
+                          Grabber hardware may not be so forgiving.
+                        */
+                       Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n");
+                       pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */
+                       /* Decompress, etc */
+                       ret = pwc_handle_frame(pdev);
+                       pdev->image_used[*mbuf] = 0;
+                       if (ret)
+                               return -EFAULT;
+                       break;
+               }
+               
+               case VIDIOCGAUDIO:
+               {
+                       struct video_audio *v = arg;
+                       
+                       strcpy(v->name, "Microphone");
+                       v->audio = -1; /* unknown audio minor */
+                       v->flags = 0;
+                       v->mode = VIDEO_SOUND_MONO;
+                       v->volume = 0;
+                       v->bass = 0;
+                       v->treble = 0;
+                       v->balance = 0x8000;
+                       v->step = 1;
+                       break;  
+               }
+               
+               case VIDIOCSAUDIO:
+               {
+                       /* Dummy: nothing can be set */
+                       break;
+               }
+               
+               case VIDIOCGUNIT:
+               {
+                       struct video_unit *vu = arg;
+                       
+                       vu->video = pdev->vdev->minor & 0x3F;
+                       vu->audio = -1; /* not known yet */
+                       vu->vbi = -1;
+                       vu->radio = -1;
+                       vu->teletext = -1;
+                       break;
+               }
+               default:
+                       return pwc_ioctl(pdev, cmd, arg);
+       } /* ..switch */
+       return 0;
+}      
+
+static int pwc_video_ioctl(struct inode *inode, struct file *file,
+                          unsigned int cmd, unsigned long arg)
+{
+       return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl);
+}
+
+
+static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct video_device *vdev = file->private_data;
+       struct pwc_device *pdev;
+       unsigned long start = vma->vm_start;
+       unsigned long size  = vma->vm_end-vma->vm_start;
+       unsigned long page, pos;
+       
+       Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size);
+       pdev = vdev->priv;
+       
+       vma->vm_flags |= VM_IO;
+
+       pos = (unsigned long)pdev->image_data;
+       while (size > 0) {
+               page = kvirt_to_pa(pos);
+               if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
+                       return -EAGAIN;
+
+               start += PAGE_SIZE;
+               pos += PAGE_SIZE;
+               if (size > PAGE_SIZE)
+                       size -= PAGE_SIZE;
+               else
+                       size = 0;
+       }
+
+       return 0;
+}
+
+/***************************************************************************/
+/* USB functions */
+
+/* This function gets called when a new device is plugged in or the usb core
+ * is loaded.
+ */
+
+static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct pwc_device *pdev = NULL;
+       int vendor_id, product_id, type_id;
+       int i, hint;
+       int features = 0;
+       int video_nr = -1; /* default: use next available device */
+       char serial_number[30], *name;
+
+       /* Check if we can handle this device */
+       Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", 
+               udev->descriptor.idVendor, udev->descriptor.idProduct, 
+               intf->altsetting->desc.bInterfaceNumber);
+
+       /* the interfaces are probed one by one. We are only interested in the
+          video interface (0) now.
+          Interface 1 is the Audio Control, and interface 2 Audio itself.
+        */
+       if (intf->altsetting->desc.bInterfaceNumber > 0)
+               return -ENODEV;
+
+       vendor_id = udev->descriptor.idVendor;
+       product_id = udev->descriptor.idProduct;
+
+       if (vendor_id == 0x0471) {
+               switch (product_id) {
+               case 0x0302:
+                       Info("Philips PCA645VC USB webcam detected.\n");
+                       name = "Philips 645 webcam";
+                       type_id = 645;
+                       break;
+               case 0x0303:
+                       Info("Philips PCA646VC USB webcam detected.\n");
+                       name = "Philips 646 webcam";
+                       type_id = 646;
+                       break;
+               case 0x0304:
+                       Info("Askey VC010 type 2 USB webcam detected.\n");
+                       name = "Askey VC010 webcam";
+                       type_id = 646;
+                       break;
+               case 0x0307:
+                       Info("Philips PCVC675K (Vesta) USB webcam detected.\n");
+                       name = "Philips 675 webcam";
+                       type_id = 675;
+                       break;
+               case 0x0308:
+                       Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
+                       name = "Philips 680 webcam";
+                       type_id = 680;
+                       break;
+               case 0x030C:
+                       Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
+                       name = "Philips 690 webcam";
+                       type_id = 690;
+                       break;
+               case 0x0310:
+                       Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
+                       name = "Philips 730 webcam";
+                       type_id = 730;
+                       break;
+               case 0x0311:
+                       Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
+                       name = "Philips 740 webcam";
+                       type_id = 740;
+                       break;
+               case 0x0312:
+                       Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
+                       name = "Philips 750 webcam";
+                       type_id = 750;
+                       break;
+               case 0x0313:
+                       Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
+                       name = "Philips 720K/40 webcam";
+                       type_id = 720;
+                       break;
+               default:
+                       return -ENODEV;
+                       break;
+               }
+       }
+       else if (vendor_id == 0x069A) {
+               switch(product_id) {
+               case 0x0001:
+                       Info("Askey VC010 type 1 USB webcam detected.\n");
+                       name = "Askey VC010 webcam";
+                       type_id = 645;
+                       break;
+               default:
+                       return -ENODEV;
+                       break;
+               }
+       }
+       else if (vendor_id == 0x046d) {
+               switch(product_id) {
+               case 0x08b0:
+                       Info("Logitech QuickCam Pro 3000 USB webcam detected.\n");
+                       name = "Logitech QuickCam Pro 3000";
+                       type_id = 740; /* CCD sensor */
+                       break;
+               case 0x08b1:
+                       Info("Logitech QuickCam Notebook Pro USB webcam detected.\n");
+                       name = "Logitech QuickCam Notebook Pro";
+                       type_id = 740; /* CCD sensor */
+                       break;
+               case 0x08b2:
+                       Info("Logitech QuickCam 4000 Pro USB webcam detected.\n");
+                       name = "Logitech QuickCam Pro 4000";
+                       type_id = 740; /* CCD sensor */
+                       break;
+               case 0x08b3:
+                       Info("Logitech QuickCam Zoom USB webcam detected.\n");
+                       name = "Logitech QuickCam Zoom";
+                       type_id = 740; /* CCD sensor */
+                       break;
+               case 0x08B4:
+                       Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
+                       name = "Logitech QuickCam Zoom";
+                       type_id = 740; /* CCD sensor */
+                       break;
+               case 0x08b5:
+                       Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
+                       name = "Logitech QuickCam Orbit";
+                       type_id = 740; /* CCD sensor */
+                       features |= FEATURE_MOTOR_PANTILT;
+                       break;
+               case 0x08b6:
+               case 0x08b7:
+               case 0x08b8:
+                       Info("Logitech QuickCam detected (reserved ID).\n");
+                       name = "Logitech QuickCam (res.)";
+                       type_id = 730; /* Assuming CMOS */
+                       break;
+               default:
+                       return -ENODEV;
+                       break;
+               }
+        }
+       else if (vendor_id == 0x055d) {
+               /* I don't know the difference between the C10 and the C30;
+                  I suppose the difference is the sensor, but both cameras
+                  work equally well with a type_id of 675
+                */
+               switch(product_id) {
+               case 0x9000:
+                       Info("Samsung MPC-C10 USB webcam detected.\n");
+                       name = "Samsung MPC-C10";
+                       type_id = 675;
+                       break;
+               case 0x9001:
+                       Info("Samsung MPC-C30 USB webcam detected.\n");
+                       name = "Samsung MPC-C30";
+                       type_id = 675;
+                       break;
+               default:
+                       return -ENODEV;
+                       break;
+               }
+       }
+       else if (vendor_id == 0x041e) {
+               switch(product_id) {
+               case 0x400c:
+                       Info("Creative Labs Webcam 5 detected.\n");
+                       name = "Creative Labs Webcam 5";
+                       type_id = 730;
+                       break;
+               case 0x4011:
+                       Info("Creative Labs Webcam Pro Ex detected.\n");
+                       name = "Creative Labs Webcam Pro Ex";
+                       type_id = 740;
+                       break;
+               default:
+                       return -ENODEV;
+                       break;
+               }
+       }
+       else if (vendor_id == 0x04cc) {
+               switch(product_id) {
+               case 0x8116:
+                       Info("Sotec Afina Eye USB webcam detected.\n");
+                       name = "Sotec Afina Eye";
+                       type_id = 730;
+                       break;
+               default:
+                       return -ENODEV;
+                       break;
+               }
+       }
+       else if (vendor_id == 0x06be) {
+               switch(product_id) {
+               case 0x8116:
+                       /* This is essentially the same cam as the Sotec Afina Eye */
+                       Info("AME Co. Afina Eye USB webcam detected.\n");
+                       name = "AME Co. Afina Eye";
+                       type_id = 750;
+                       break;
+               default:
+                       return -ENODEV;
+                       break;
+               }
+       
+       }
+       else if (vendor_id == 0x0d81) {
+               switch(product_id) {
+               case 0x1900:
+                       Info("Visionite VCS-UC300 USB webcam detected.\n");
+                       name = "Visionite VCS-UC300";
+                       type_id = 740; /* CCD sensor */
+                       break;
+               case 0x1910:
+                       Info("Visionite VCS-UM100 USB webcam detected.\n");
+                       name = "Visionite VCS-UM100";
+                       type_id = 730; /* CMOS sensor */
+                       break;
+               default:
+                       return -ENODEV;
+                       break;
+               }
+       }
+       else 
+               return -ENODEV; /* Not any of the know types; but the list keeps growing. */
+
+       memset(serial_number, 0, 30);
+       usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
+       Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number);
+
+       if (udev->descriptor.bNumConfigurations > 1)
+               Info("Warning: more than 1 configuration available.\n");
+
+       /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
+       pdev = kmalloc(sizeof(struct pwc_device), GFP_KERNEL);
+       if (pdev == NULL) {
+               Err("Oops, could not allocate memory for pwc_device.\n");
+               return -ENOMEM;
+       }
+       memset(pdev, 0, sizeof(struct pwc_device));
+       pdev->type = type_id;
+       pdev->vsize = default_size;
+       pdev->vframes = default_fps;
+       strcpy(pdev->serial, serial_number);
+       pdev->features = features;
+       if (vendor_id == 0x046D && product_id == 0x08B5)
+       {
+               /* Logitech QuickCam Orbit
+                  The ranges have been determined experimentally; they may differ from cam to cam.
+                  Also, the exact ranges left-right and up-down are different for my cam
+                 */
+               pdev->angle_range.pan_min  = -7000;
+               pdev->angle_range.pan_max  =  7000;
+               pdev->angle_range.tilt_min = -3000;
+               pdev->angle_range.tilt_max =  2500;
+       }
+
+       init_MUTEX(&pdev->modlock);
+       pdev->ptrlock = SPIN_LOCK_UNLOCKED;
+
+       pdev->udev = udev;
+       init_waitqueue_head(&pdev->frameq);
+       pdev->vcompression = pwc_preferred_compression;
+
+       /* Allocate video_device structure */
+       pdev->vdev = video_device_alloc();
+       if (pdev->vdev == 0)
+       {
+               Err("Err, cannot allocate video_device struture. Failing probe.");
+               kfree(pdev);
+               return -ENOMEM;
+       }
+       memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
+       strcpy(pdev->vdev->name, name);
+       pdev->vdev->owner = THIS_MODULE;
+       video_set_drvdata(pdev->vdev, pdev);
+
+       pdev->release = udev->descriptor.bcdDevice;
+       Trace(TRACE_PROBE, "Release: %04x\n", pdev->release);
+
+       /* Now search device_hint[] table for a match, so we can hint a node number. */
+       for (hint = 0; hint < MAX_DEV_HINTS; hint++) {
+               if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) &&
+                    (device_hint[hint].pdev == NULL)) {
+                       /* so far, so good... try serial number */
+                       if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) {
+                               /* match! */
+                               video_nr = device_hint[hint].device_node;
+                               Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr);
+                               break;
+                       }
+               }
+       }
+
+       pdev->vdev->release = video_device_release;
+       i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
+       if (i < 0) {
+               Err("Failed to register as video device (%d).\n", i);
+               video_device_release(pdev->vdev); /* Drip... drip... drip... */
+               kfree(pdev); /* Oops, no memory leaks please */
+               return -EIO;
+       }
+       else {
+               Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F);
+       }
+
+       /* occupy slot */
+       if (hint < MAX_DEV_HINTS) 
+               device_hint[hint].pdev = pdev;
+
+       Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev);
+       usb_set_intfdata (intf, pdev);
+       return 0;
+}
+
+/* The user janked out the cable... */
+static void usb_pwc_disconnect(struct usb_interface *intf)
+{
+       struct pwc_device *pdev;
+       int hint;
+
+       lock_kernel();
+       pdev = usb_get_intfdata (intf);
+       usb_set_intfdata (intf, NULL);
+       if (pdev == NULL) {
+               Err("pwc_disconnect() Called without private pointer.\n");
+               goto disconnect_out;
+       }
+       if (pdev->udev == NULL) {
+               Err("pwc_disconnect() already called for %p\n", pdev);
+               goto disconnect_out;
+       }
+       if (pdev->udev != interface_to_usbdev(intf)) {
+               Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
+               goto disconnect_out;
+       }
+#ifdef PWC_MAGIC       
+       if (pdev->magic != PWC_MAGIC) {
+               Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n");
+               goto disconnect_out;
+       }
+#endif
+       
+       /* We got unplugged; this is signalled by an EPIPE error code */
+       if (pdev->vopen) {
+               Info("Disconnected while webcam is in use!\n");
+               pdev->error_status = EPIPE;
+       }
+
+       /* Alert waiting processes */
+       wake_up_interruptible(&pdev->frameq);
+       /* Wait until device is closed */
+       while (pdev->vopen)
+               schedule();
+       /* Device is now closed, so we can safely unregister it */
+       Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n");
+       video_unregister_device(pdev->vdev);
+
+       /* Free memory (don't set pdev to 0 just yet) */
+       kfree(pdev);
+
+disconnect_out:
+       /* search device_hint[] table if we occupy a slot, by any chance */
+       for (hint = 0; hint < MAX_DEV_HINTS; hint++)
+               if (device_hint[hint].pdev == pdev)
+                       device_hint[hint].pdev = NULL;
+
+       unlock_kernel();
+}
+
+
+/* *grunt* We have to do atoi ourselves :-( */
+static int pwc_atoi(const char *s)
+{
+       int k = 0;
+
+       k = 0;
+       while (*s != '\0' && *s >= '0' && *s <= '9') {
+               k = 10 * k + (*s - '0');
+               s++;
+       }
+       return k;
+}
+
+
+/* 
+ * Initialization code & module stuff 
+ */
+
+static char *size = NULL;
+static int fps = 0;
+static int fbufs = 0;
+static int mbufs = 0;
+static int trace = -1;
+static int compression = -1;
+static int leds[2] = { -1, -1 };
+static char *dev_hint[MAX_DEV_HINTS] = { };
+
+MODULE_PARM(size, "s");
+MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga");
+MODULE_PARM(fps, "i");
+MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30");
+MODULE_PARM(fbufs, "i");
+MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve");
+MODULE_PARM(mbufs, "i");
+MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers");
+MODULE_PARM(trace, "i");
+MODULE_PARM_DESC(trace, "For debugging purposes");
+MODULE_PARM(power_save, "i");
+MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off");
+MODULE_PARM(compression, "i");
+MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)");
+MODULE_PARM(leds, "2i");
+MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
+MODULE_PARM(dev_hint, "0-20s");
+MODULE_PARM_DESC(dev_hint, "Device node hints");
+
+MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
+MODULE_AUTHOR("Luc Saillard <luc@saillard.org>");
+MODULE_LICENSE("GPL");
+
+static int __init usb_pwc_init(void)
+{
+       int i, sz;
+       char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
+
+       Info("Philips webcam module version " PWC_VERSION " loaded.\n");
+       Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n");
+       Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n");
+       Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
+
+       if (fps) {
+               if (fps < 4 || fps > 30) {
+                       Err("Framerate out of bounds (4-30).\n");
+                       return -EINVAL;
+               }
+               default_fps = fps;
+               Info("Default framerate set to %d.\n", default_fps);
+       }
+
+       if (size) {
+               /* string; try matching with array */
+               for (sz = 0; sz < PSZ_MAX; sz++) {
+                       if (!strcmp(sizenames[sz], size)) { /* Found! */
+                               default_size = sz;
+                               break;
+                       }
+               }
+               if (sz == PSZ_MAX) {
+                       Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n");
+                       return -EINVAL;
+               }
+               Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y);
+       }
+       if (mbufs) {
+               if (mbufs < 1 || mbufs > MAX_IMAGES) {
+                       Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES);
+                       return -EINVAL;
+               }
+               default_mbufs = mbufs;
+               Info("Number of image buffers set to %d.\n", default_mbufs);
+       }
+       if (fbufs) {
+               if (fbufs < 2 || fbufs > MAX_FRAMES) {
+                       Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES);
+                       return -EINVAL;
+               }
+               default_fbufs = fbufs;
+               Info("Number of frame buffers set to %d.\n", default_fbufs);
+       }
+       if (trace >= 0) {
+               Info("Trace options: 0x%04x\n", trace);
+               pwc_trace = trace;
+       }
+       if (compression >= 0) {
+               if (compression > 3) {
+                       Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n");
+                       return -EINVAL;
+               }
+               pwc_preferred_compression = compression;
+               Info("Preferred compression set to %d.\n", pwc_preferred_compression);
+       }
+       if (power_save)
+               Info("Enabling power save on open/close.\n");
+       if (leds[0] >= 0)
+               led_on = leds[0];
+       if (leds[1] >= 0)
+               led_off = leds[1];
+
+       /* Big device node whoopla. Basicly, it allows you to assign a
+          device node (/dev/videoX) to a camera, based on its type
+          & serial number. The format is [type[.serialnumber]:]node.
+
+          Any camera that isn't matched by these rules gets the next
+          available free device node.
+        */
+       for (i = 0; i < MAX_DEV_HINTS; i++) {
+               char *s, *colon, *dot;
+
+               /* This loop also initializes the array */
+               device_hint[i].pdev = NULL;
+               s = dev_hint[i];
+               if (s != NULL && *s != '\0') {
+                       device_hint[i].type = -1; /* wildcard */
+                       strcpy(device_hint[i].serial_number, "*");
+
+                       /* parse string: chop at ':' & '/' */
+                       colon = dot = s;
+                       while (*colon != '\0' && *colon != ':')
+                               colon++;
+                       while (*dot != '\0' && *dot != '.')
+                               dot++;
+                       /* Few sanity checks */
+                       if (*dot != '\0' && dot > colon) {
+                               Err("Malformed camera hint: the colon must be after the dot.\n");
+                               return -EINVAL;
+                       }
+
+                       if (*colon == '\0') {
+                               /* No colon */
+                               if (*dot != '\0') {
+                                       Err("Malformed camera hint: no colon + device node given.\n");
+                                       return -EINVAL;
+                               }
+                               else {
+                                       /* No type or serial number specified, just a number. */
+                                       device_hint[i].device_node = pwc_atoi(s);
+                               }
+                       }
+                       else {
+                               /* There's a colon, so we have at least a type and a device node */
+                               device_hint[i].type = pwc_atoi(s);
+                               device_hint[i].device_node = pwc_atoi(colon + 1);
+                               if (*dot != '\0') {
+                                       /* There's a serial number as well */
+                                       int k;
+                                       
+                                       dot++;
+                                       k = 0;
+                                       while (*dot != ':' && k < 29) {
+                                               device_hint[i].serial_number[k++] = *dot;
+                                               dot++;
+                                       }
+                                       device_hint[i].serial_number[k] = '\0';
+                               }
+                       }
+#if PWC_DEBUG          
+                       Debug("device_hint[%d]:\n", i);
+                       Debug("  type    : %d\n", device_hint[i].type);
+                       Debug("  serial# : %s\n", device_hint[i].serial_number);
+                       Debug("  node    : %d\n", device_hint[i].device_node);
+#endif                 
+               }
+               else
+                       device_hint[i].type = 0; /* not filled */
+       } /* ..for MAX_DEV_HINTS */
+
+       Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver);
+       return usb_register(&pwc_driver);
+}
+
+static void __exit usb_pwc_exit(void)
+{
+       Trace(TRACE_MODULE, "Deregistering driver.\n");
+       usb_deregister(&pwc_driver);
+       Info("Philips webcam module removed.\n");
+}
+
+module_init(usb_pwc_init);
+module_exit(usb_pwc_exit);
+
diff --git a/drivers/usb/media/pwc/pwc-ioctl.h b/drivers/usb/media/pwc/pwc-ioctl.h
new file mode 100644 (file)
index 0000000..65805ea
--- /dev/null
@@ -0,0 +1,292 @@
+#ifndef PWC_IOCTL_H
+#define PWC_IOCTL_H
+
+/* (C) 2001-2004 Nemosoft Unv.
+   (C) 2004      Luc Saillard (luc@saillard.org)
+
+   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+   driver and thus may have bugs that are not present in the original version.
+   Please send bug reports and support requests to <luc@saillard.org>.
+   The decompression routines have been implemented by reverse-engineering the
+   Nemosoft binary pwcx module. Caveat emptor.
+
+   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
+*/
+
+/* This is pwc-ioctl.h belonging to PWC 8.12.1
+   It contains structures and defines to communicate from user space
+   directly to the driver.
+ */
+
+/*
+   Changes
+   2001/08/03  Alvarado   Added ioctl constants to access methods for
+                          changing white balance and red/blue gains
+   2002/12/15  G. H. Fernandez-Toribio   VIDIOCGREALSIZE
+   2003/12/13  Nemosft Unv. Some modifications to make interfacing to
+               PWCX easier
+ */
+
+/* These are private ioctl() commands, specific for the Philips webcams.
+   They contain functions not found in other webcams, and settings not
+   specified in the Video4Linux API.
+
+   The #define names are built up like follows:
+   VIDIOC              VIDeo IOCtl prefix
+         PWC           Philps WebCam
+            G           optional: Get
+            S           optional: Set
+             ...       the function
+ */
+
+
+ /* Enumeration of image sizes */
+#define PSZ_SQCIF      0x00
+#define PSZ_QSIF       0x01
+#define PSZ_QCIF       0x02
+#define PSZ_SIF                0x03
+#define PSZ_CIF                0x04
+#define PSZ_VGA                0x05
+#define PSZ_MAX                6
+
+
+/* The frame rate is encoded in the video_window.flags parameter using
+   the upper 16 bits, since some flags are defined nowadays. The following
+   defines provide a mask and shift to filter out this value.
+
+   In 'Snapshot' mode the camera freezes its automatic exposure and colour
+   balance controls.
+ */
+#define PWC_FPS_SHIFT          16
+#define PWC_FPS_MASK           0x00FF0000
+#define PWC_FPS_FRMASK         0x003F0000
+#define PWC_FPS_SNAPSHOT       0x00400000
+
+
+/* structure for transfering x & y coordinates */
+struct pwc_coord
+{
+       int x, y;               /* guess what */
+       int size;               /* size, or offset */
+};
+
+
+/* Used with VIDIOCPWCPROBE */
+struct pwc_probe
+{
+       char name[32];
+       int type;
+};
+
+struct pwc_serial
+{
+       char serial[30];        /* String with serial number. Contains terminating 0 */
+};
+       
+/* pwc_whitebalance.mode values */
+#define PWC_WB_INDOOR          0
+#define PWC_WB_OUTDOOR         1
+#define PWC_WB_FL              2
+#define PWC_WB_MANUAL          3
+#define PWC_WB_AUTO            4
+
+/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). 
+   Set mode to one of the PWC_WB_* values above.
+   *red and *blue are the respective gains of these colour components inside 
+   the camera; range 0..65535
+   When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; 
+   otherwise undefined.
+   'read_red' and 'read_blue' are read-only.
+*/   
+struct pwc_whitebalance
+{
+       int mode;
+       int manual_red, manual_blue;    /* R/W */
+       int read_red, read_blue;        /* R/O */
+};
+
+/* 
+   'control_speed' and 'control_delay' are used in automatic whitebalance mode,
+   and tell the camera how fast it should react to changes in lighting, and 
+   with how much delay. Valid values are 0..65535.
+*/
+struct pwc_wb_speed
+{
+       int control_speed;
+       int control_delay;
+
+};
+
+/* Used with VIDIOCPWC[SG]LED */
+struct pwc_leds
+{
+       int led_on;                     /* Led on-time; range = 0..25000 */
+       int led_off;                    /* Led off-time; range = 0..25000  */
+};
+
+/* Image size (used with GREALSIZE) */
+struct pwc_imagesize
+{
+       int width;
+       int height;
+};
+
+/* Defines and structures for Motorized Pan & Tilt */
+#define PWC_MPT_PAN            0x01
+#define PWC_MPT_TILT           0x02
+#define PWC_MPT_TIMEOUT                0x04 /* for status */
+
+/* Set angles; when absolute != 0, the angle is absolute and the 
+   driver calculates the relative offset for you. This can only
+   be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns
+   absolute angles.
+ */   
+struct pwc_mpt_angles
+{
+       int absolute;           /* write-only */
+       int pan;                /* degrees * 100 */
+       int tilt;               /* degress * 100 */
+};
+
+/* Range of angles of the camera, both horizontally and vertically.
+ */
+struct pwc_mpt_range
+{
+       int pan_min, pan_max;           /* degrees * 100 */
+       int tilt_min, tilt_max;
+};
+
+struct pwc_mpt_status
+{
+       int status;
+       int time_pan;
+       int time_tilt;
+};
+
+
+/* This is used for out-of-kernel decompression. With it, you can get
+   all the necessary information to initialize and use the decompressor
+   routines in standalone applications.
+ */   
+struct pwc_video_command
+{
+       int type;               /* camera type (645, 675, 730, etc.) */
+       int release;            /* release number */
+
+        int size;              /* one of PSZ_* */
+        int alternate;
+       int command_len;        /* length of USB video command */
+       unsigned char command_buf[13];  /* Actual USB video command */
+       int bandlength;         /* >0 = compressed */
+       int frame_size;         /* Size of one (un)compressed frame */
+};
+
+/* Flags for PWCX subroutines. Not all modules honour all flags. */
+#define PWCX_FLAG_PLANAR       0x0001
+#define PWCX_FLAG_BAYER                0x0008
+
+
+/* IOCTL definitions */
+
+ /* Restore user settings */
+#define VIDIOCPWCRUSER         _IO('v', 192)
+ /* Save user settings */
+#define VIDIOCPWCSUSER         _IO('v', 193)
+ /* Restore factory settings */
+#define VIDIOCPWCFACTORY       _IO('v', 194)
+
+ /* You can manipulate the compression factor. A compression preference of 0
+    means use uncompressed modes when available; 1 is low compression, 2 is
+    medium and 3 is high compression preferred. Of course, the higher the
+    compression, the lower the bandwidth used but more chance of artefacts
+    in the image. The driver automatically chooses a higher compression when
+    the preferred mode is not available.
+  */
+ /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */
+#define VIDIOCPWCSCQUAL                _IOW('v', 195, int)
+ /* Get preferred compression quality */
+#define VIDIOCPWCGCQUAL                _IOR('v', 195, int)
+
+
+/* Retrieve serial number of camera */
+#define VIDIOCPWCGSERIAL       _IOR('v', 198, struct pwc_serial)
+
+ /* This is a probe function; since so many devices are supported, it
+    becomes difficult to include all the names in programs that want to
+    check for the enhanced Philips stuff. So in stead, try this PROBE;
+    it returns a structure with the original name, and the corresponding
+    Philips type.
+    To use, fill the structure with zeroes, call PROBE and if that succeeds,
+    compare the name with that returned from VIDIOCGCAP; they should be the
+    same. If so, you can be assured it is a Philips (OEM) cam and the type
+    is valid.
+ */
+#define VIDIOCPWCPROBE         _IOR('v', 199, struct pwc_probe)
+
+ /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */
+#define VIDIOCPWCSAGC          _IOW('v', 200, int)
+ /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */
+#define VIDIOCPWCGAGC          _IOR('v', 200, int)
+ /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */
+#define VIDIOCPWCSSHUTTER      _IOW('v', 201, int)
+
+ /* Color compensation (Auto White Balance) */
+#define VIDIOCPWCSAWB           _IOW('v', 202, struct pwc_whitebalance)
+#define VIDIOCPWCGAWB           _IOR('v', 202, struct pwc_whitebalance)
+
+ /* Auto WB speed */
+#define VIDIOCPWCSAWBSPEED     _IOW('v', 203, struct pwc_wb_speed)
+#define VIDIOCPWCGAWBSPEED     _IOR('v', 203, struct pwc_wb_speed)
+
+ /* LEDs on/off/blink; int range 0..65535 */
+#define VIDIOCPWCSLED           _IOW('v', 205, struct pwc_leds)
+#define VIDIOCPWCGLED           _IOR('v', 205, struct pwc_leds)
+
+  /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */
+#define VIDIOCPWCSCONTOUR      _IOW('v', 206, int)
+#define VIDIOCPWCGCONTOUR      _IOR('v', 206, int)
+
+  /* Backlight compensation; 0 = off, otherwise on */
+#define VIDIOCPWCSBACKLIGHT    _IOW('v', 207, int)
+#define VIDIOCPWCGBACKLIGHT    _IOR('v', 207, int)
+
+  /* Flickerless mode; = 0 off, otherwise on */
+#define VIDIOCPWCSFLICKER      _IOW('v', 208, int)
+#define VIDIOCPWCGFLICKER      _IOR('v', 208, int)  
+
+  /* Dynamic noise reduction; 0 off, 3 = high noise reduction */
+#define VIDIOCPWCSDYNNOISE     _IOW('v', 209, int)
+#define VIDIOCPWCGDYNNOISE     _IOR('v', 209, int)
+
+ /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */
+#define VIDIOCPWCGREALSIZE     _IOR('v', 210, struct pwc_imagesize)
+
+ /* Motorized pan & tilt functions */ 
+#define VIDIOCPWCMPTRESET      _IOW('v', 211, int)
+#define VIDIOCPWCMPTGRANGE     _IOR('v', 211, struct pwc_mpt_range)
+#define VIDIOCPWCMPTSANGLE     _IOW('v', 212, struct pwc_mpt_angles)
+#define VIDIOCPWCMPTGANGLE     _IOR('v', 212, struct pwc_mpt_angles)
+#define VIDIOCPWCMPTSTATUS     _IOR('v', 213, struct pwc_mpt_status)
+
+ /* Get the USB set-video command; needed for initializing libpwcx */
+#define VIDIOCPWCGVIDCMD       _IOR('v', 215, struct pwc_video_command)
+struct pwc_table_init_buffer {
+   int len;
+   char *buffer;
+
+};
+#define VIDIOCPWCGVIDTABLE     _IOR('v', 216, struct pwc_table_init_buffer)
+
+#endif
diff --git a/drivers/usb/media/pwc/pwc-kiara.c b/drivers/usb/media/pwc/pwc-kiara.c
new file mode 100644 (file)
index 0000000..5485800
--- /dev/null
@@ -0,0 +1,891 @@
+/* Linux driver for Philips webcam
+   (C) 2004      Luc Saillard (luc@saillard.org)
+
+   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+   driver and thus may have bugs that are not present in the original version.
+   Please send bug reports and support requests to <luc@saillard.org>.
+   The decompression routines have been implemented by reverse-engineering the
+   Nemosoft binary pwcx module. Caveat emptor.
+
+   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
+*/
+
+
+/* This tables contains entries for the 730/740/750 (Kiara) camera, with
+   4 different qualities (no compression, low, medium, high).
+   It lists the bandwidth requirements for said mode by its alternate interface
+   number. An alternate of 0 means that the mode is unavailable.
+
+   There are 6 * 4 * 4 entries:
+     6 different resolutions subqcif, qsif, qcif, sif, cif, vga
+     6 framerates: 5, 10, 15, 20, 25, 30
+     4 compression modi: none, low, medium, high
+
+   When an uncompressed mode is not available, the next available compressed mode
+   will be chosen (unless the decompressor is absent). Sometimes there are only
+   1 or 2 compressed modes available; in that case entries are duplicated.
+*/
+
+
+#include "pwc-kiara.h"
+#include "pwc-uncompress.h"
+
+const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] =
+{
+   /* SQCIF */
+   {
+      /* 5 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 10 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 15 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 20 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 25 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 30 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+   },
+   /* QSIF */
+   {
+      /* 5 fps */
+      {
+         {1, 146,    0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+         {1, 146,    0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+         {1, 146,    0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+         {1, 146,    0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}},
+      },
+      /* 10 fps */
+      {
+         {2, 291,    0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}},
+         {1, 192,  630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
+         {1, 192,  630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
+         {1, 192,  630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}},
+      },
+      /* 15 fps */
+      {
+         {3, 437,    0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}},
+         {2, 292,  640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}},
+         {2, 292,  640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}},
+         {1, 192,  420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}},
+      },
+      /* 20 fps */
+      {
+         {4, 589,    0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}},
+         {3, 448,  730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}},
+         {2, 292,  476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}},
+         {1, 192,  312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}},
+      },
+      /* 25 fps */
+      {
+         {5, 703,    0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}},
+         {3, 447,  610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}},
+         {2, 292,  398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}},
+         {1, 193,  262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}},
+      },
+      /* 30 fps */
+      {
+         {8, 874,    0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}},
+         {5, 704,  730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}},
+         {3, 448,  492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}},
+         {2, 292,  320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}},
+      },
+   },
+   /* QCIF */
+   {
+      /* 5 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 10 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 15 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 20 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 25 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 30 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+   },
+   /* SIF */
+   {
+      /* 5 fps */
+      {
+         {4, 582,    0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}},
+         {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}},
+         {2, 291,  960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}},
+         {1, 191,  630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}},
+      },
+      /* 10 fps */
+      {
+         {0, },
+         {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}},
+         {3, 447,  736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}},
+         {2, 292,  480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}},
+      },
+      /* 15 fps */
+      {
+         {0, },
+         {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}},
+         {4, 592,  650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}},
+         {3, 448,  492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}},
+      },
+      /* 20 fps */
+      {
+         {0, },
+         {9, 958,  782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}},
+         {5, 703,  574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}},
+         {3, 446,  364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}},
+      },
+      /* 25 fps */
+      {
+         {0, },
+         {9, 958,  654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}},
+         {6, 776,  530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}},
+         {4, 592,  404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}},
+      },
+      /* 30 fps */
+      {
+         {0, },
+         {9, 957,  526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}},
+         {6, 775,  426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}},
+         {4, 590,  324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}},
+      },
+   },
+   /* CIF */
+   {
+      /* 5 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 10 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 15 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 20 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 25 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 30 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+   },
+   /* VGA */
+   {
+      /* 5 fps */
+      {
+         {0, },
+         {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}},
+         {4, 592,  976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}},
+         {3, 448,  738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}},
+      },
+      /* 10 fps */
+      {
+         {0, },
+         {9, 956,  788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}},
+         {6, 776,  640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}},
+         {4, 592,  488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}},
+      },
+      /* 15 fps */
+      {
+         {0, },
+         {9, 957,  526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}},
+         {9, 957,  526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}},
+         {8, 895,  492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}},
+      },
+      /* 20 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 25 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 30 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+   },
+};
+
+
+/*
+ * Rom table for kiara chips
+ *
+ * 32 roms tables (one for each resolution ?)
+ *  2 tables per roms (one for each passes) (Y, and U&V)
+ * 128 bytes per passes
+ */
+
+const unsigned int KiaraRomTable [8][2][16][8] =  
+{
+ { /* version 0 */
+  { /* version 0, passes 0 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000001,0x00000001},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000009,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000009,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000249,0x0000024a,0x00000049},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000249,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000249,
+    0x00000249,0x0000124a,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000249,
+    0x0000124a,0x00009252,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00009252,0x00009292,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009292,0x00009292,0x00009493,0x000124db},
+   {0x00000000,0x00000000,0x00000249,0x0000924a,
+    0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x0000a493,0x000124db,0x000124db,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x000124db,0x000126dc,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000124db,0x000136e4,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b925,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 0, passes 1 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000},
+   {0x00000000,0x00000000,0x00000001,0x00000009,
+    0x00000009,0x00000009,0x00000009,0x00000001},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000249,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00001252},
+   {0x00000000,0x00000000,0x00000049,0x00001249,
+    0x0000124a,0x0000124a,0x00001252,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009252,0x00009252,0x00009292,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x0000924a,
+    0x00009292,0x00009292,0x00009292,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x00009292,
+    0x00009492,0x00009493,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x0000a493,0x000124db,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00009252,0x00009493,
+    0x000126dc,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000136e4,0x000136e4,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 1 */
+  { /* version 1, passes 0 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000001},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000009,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00001252},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x0000124a,0x00009252,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009252,0x00009292,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x0000924a,
+    0x00009252,0x00009493,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x0000924a,
+    0x00009292,0x00009493,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x00009252,
+    0x00009492,0x00009493,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x000124db,0x000124db,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000126dc,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 1, passes 1 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000},
+   {0x00000000,0x00000000,0x00000049,0x00000009,
+    0x00000049,0x00000009,0x00000001,0x00000000},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000000},
+   {0x00000000,0x00000000,0x00000249,0x00000049,
+    0x00000249,0x00000049,0x0000024a,0x00000001},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00000001},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00000001},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x0000024a,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x0000024a,0x00000009},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00009252,0x00001252,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00009292,0x00001252,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00009292,0x00001252,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009292,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x0000924a,0x0000924a,
+    0x00009492,0x00009493,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 2 */
+  { /* version 2, passes 0 */
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x0000124a,0x00001252,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x00009252,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x0000124a,0x00009292,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009252,0x00009493,0x00009493,0x0000a49b},
+   {0x00000000,0x00000000,0x00000249,0x0000924a,
+    0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009292,0x00009493,0x0000a49b,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x000124db,0x000124db,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x0000a493,0x000124db,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x000136e4},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0001249b,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000136e4,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x00009252,0x000124db,
+    0x000126dc,0x0001b724,0x0001b725,0x0001b925},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 2, passes 1 */
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00000249,
+    0x0000124a,0x0000124a,0x00001252,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x00009292,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x0000a49b,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x0000a49b,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x0000a49b,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x0000a49b,0x0000a49b,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x00009252,0x0000a49b,
+    0x0001249b,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 3 */
+  { /* version 3, passes 0 */
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x000124db,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000126dc,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000136e4,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000136e4,0x0001b725,0x0001b925},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x000136e4,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b92d,0x0001c92d},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000126dc,0x0001b724,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x000136e4,0x0001b925,0x00025bb6,0x00024b77},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 3, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00000249,
+    0x0000124a,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x00009493,0x0000a49b,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x000126dc,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00009492,0x0000a49b,
+    0x000136e4,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x0001b724,0x0001b724,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 4 */
+  { /* version 4, passes 0 */
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000249,0x00000049,
+    0x00000249,0x00000249,0x0000024a,0x00000049},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x00009252,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009493,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009292,0x00009493,0x00009493,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0001249b,0x000126dc,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00009252,0x00009493,
+    0x000124db,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009252,0x0000a49b,
+    0x000124db,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00009492,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x000136e4,0x0001b925,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 4, passes 1 */
+   {0x00000000,0x00000000,0x00000249,0x00000049,
+    0x00000009,0x00000009,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000049,0x00000049,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00000249,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x0000124a,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009252,0x0000124a,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x00009252,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x00009292,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x00009292,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x00009493,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000124db,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009252,0x000124db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 5 */
+  { /* version 5, passes 0 */
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000126dc,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b725,0x000136e4},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+   {0x00000000,0x00000000,0x00009492,0x0000a49b,
+    0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b925,0x0001c96e,0x0001b925},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x0001b724,0x0001b925,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001c924,0x0002496d,0x00025bb6,0x00024b77},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 5, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009252,0x00009252,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000124db,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000124db,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000126dc,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009292,0x000124db,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 6 */
+  { /* version 6, passes 0 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000126dc,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b725,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000136e4,0x0001b724,0x0001b92d,0x000136e4},
+   {0x00000000,0x00000000,0x00009492,0x0000a49b,
+    0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b925,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x0001b724,0x0001b925,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x0001b724,0x0001c92d,0x00024b76,0x0002496e},
+   {0x00000000,0x00000000,0x00012492,0x000126db,
+    0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 6, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x00009252,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x00009292,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x0000a49b,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000124db,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000126dc,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001c924,0x0001b724,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 7 */
+  { /* version 7, passes 0 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x0000a49b,
+    0x0001249b,0x000126dc,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x0001b725,0x000124db},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000136e4,0x0001b724,0x0001b725,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x000124db,
+    0x000136e4,0x0001b724,0x0001b725,0x000126dc},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b724,0x0001c96e,0x000136e4},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001c92d,0x0001c96e,0x0001b724},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x000136e4,0x0001c92d,0x0001c96e,0x0001b724},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x0001b724,0x0001c92d,0x0001c96e,0x0001b925},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001b724,0x0001c92d,0x00024b76,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001b924,0x0001c92d,0x00024b76,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001b924,0x0001c92d,0x00024b76,0x0002496e},
+   {0x00000000,0x00000000,0x00012492,0x000136db,
+    0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 7, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x00009492,0x00009292,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x0000a49b,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000124db,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000136db,
+    0x0001b724,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000136db,
+    0x0001b724,0x000126dc,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00009292,0x000136db,
+    0x0001b724,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009492,0x000136db,
+    0x0001b724,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00012492,0x0001b6db,
+    0x0001c924,0x0001b724,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ }
+};
+
diff --git a/drivers/usb/media/pwc/pwc-timon.c b/drivers/usb/media/pwc/pwc-timon.c
new file mode 100644 (file)
index 0000000..f950a4e
--- /dev/null
@@ -0,0 +1,1446 @@
+/* Linux driver for Philips webcam
+   (C) 2004      Luc Saillard (luc@saillard.org)
+
+   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+   driver and thus may have bugs that are not present in the original version.
+   Please send bug reports and support requests to <luc@saillard.org>.
+   The decompression routines have been implemented by reverse-engineering the
+   Nemosoft binary pwcx module. Caveat emptor.
+
+   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
+*/
+
+
+/* This tables contains entries for the 675/680/690 (Timon) camera, with
+   4 different qualities (no compression, low, medium, high).
+   It lists the bandwidth requirements for said mode by its alternate interface
+   number. An alternate of 0 means that the mode is unavailable.
+
+   There are 6 * 4 * 4 entries:
+     6 different resolutions subqcif, qsif, qcif, sif, cif, vga
+     6 framerates: 5, 10, 15, 20, 25, 30
+     4 compression modi: none, low, medium, high
+
+   When an uncompressed mode is not available, the next available compressed mode
+   will be chosen (unless the decompressor is absent). Sometimes there are only
+   1 or 2 compressed modes available; in that case entries are duplicated.
+*/
+
+#include "pwc-timon.h"
+
+const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] =
+{
+   /* SQCIF */
+   {
+      /* 5 fps */
+      {
+         {1, 140,    0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+         {1, 140,    0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+         {1, 140,    0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+         {1, 140,    0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}},
+      },
+      /* 10 fps */
+      {
+         {2, 280,    0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+         {2, 280,    0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+         {2, 280,    0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+         {2, 280,    0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}},
+      },
+      /* 15 fps */
+      {
+         {3, 410,    0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+         {3, 410,    0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+         {3, 410,    0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+         {3, 410,    0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}},
+      },
+      /* 20 fps */
+      {
+         {4, 559,    0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+         {4, 559,    0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+         {4, 559,    0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+         {4, 559,    0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}},
+      },
+      /* 25 fps */
+      {
+         {5, 659,    0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+         {5, 659,    0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+         {5, 659,    0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+         {5, 659,    0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}},
+      },
+      /* 30 fps */
+      {
+         {7, 838,    0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+         {7, 838,    0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+         {7, 838,    0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+         {7, 838,    0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}},
+      },
+   },
+   /* QSIF */
+   {
+      /* 5 fps */
+      {
+         {1, 146,    0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+         {1, 146,    0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+         {1, 146,    0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+         {1, 146,    0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}},
+      },
+      /* 10 fps */
+      {
+         {2, 291,    0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}},
+         {1, 191,  630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+         {1, 191,  630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+         {1, 191,  630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+      },
+      /* 15 fps */
+      {
+         {3, 437,    0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}},
+         {2, 291,  640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+         {2, 291,  640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+         {1, 191,  420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}},
+      },
+      /* 20 fps */
+      {
+         {4, 588,    0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}},
+         {3, 447,  730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+         {2, 292,  476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
+         {1, 192,  312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}},
+      },
+      /* 25 fps */
+      {
+         {5, 703,    0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}},
+         {3, 447,  610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+         {2, 292,  398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
+         {1, 192,  262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}},
+      },
+      /* 30 fps */
+      {
+         {8, 873,    0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}},
+         {5, 704,  774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}},
+         {3, 448,  492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}},
+         {2, 291,  320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}},
+      },
+   },
+   /* QCIF */
+   {
+      /* 5 fps */
+      {
+         {1, 193,    0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+         {1, 193,    0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+         {1, 193,    0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+         {1, 193,    0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}},
+      },
+      /* 10 fps */
+      {
+         {3, 385,    0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}},
+         {2, 291,  800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+         {2, 291,  800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}},
+         {1, 194,  532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}},
+      },
+      /* 15 fps */
+      {
+         {4, 577,    0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}},
+         {3, 447,  818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}},
+         {2, 292,  534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}},
+         {1, 195,  356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}},
+      },
+      /* 20 fps */
+      {
+         {6, 776,    0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}},
+         {4, 591,  804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}},
+         {3, 447,  608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+         {2, 291,  396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}},
+      },
+      /* 25 fps */
+      {
+         {9, 928,    0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}},
+         {5, 703,  800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}},
+         {3, 447,  508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}},
+         {2, 292,  332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}},
+      },
+      /* 30 fps */
+      {
+         {0, },
+         {9, 956,  876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}},
+         {4, 592,  542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}},
+         {2, 291,  266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}},
+      },
+   },
+   /* SIF */
+   {
+      /* 5 fps */
+      {
+         {4, 582,    0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}},
+         {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}},
+         {2, 291,  960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}},
+         {1, 191,  630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}},
+      },
+      /* 10 fps */
+      {
+         {0, },
+         {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}},
+         {3, 447,  736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}},
+         {2, 291,  480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}},
+      },
+      /* 15 fps */
+      {
+         {0, },
+         {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}},
+         {4, 591,  650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}},
+         {3, 448,  492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}},
+      },
+      /* 20 fps */
+      {
+         {0, },
+         {9, 958,  782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}},
+         {5, 703,  574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}},
+         {3, 446,  364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}},
+      },
+      /* 25 fps */
+      {
+         {0, },
+         {9, 958,  654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}},
+         {6, 776,  530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}},
+         {4, 592,  404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}},
+      },
+      /* 30 fps */
+      {
+         {0, },
+         {9, 957,  526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}},
+         {6, 775,  426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}},
+         {4, 590,  324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}},
+      },
+   },
+   /* CIF */
+   {
+      /* 5 fps */
+      {
+         {6, 771,    0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}},
+         {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}},
+         {2, 291,  800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}},
+         {1, 193,  528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}},
+      },
+      /* 10 fps */
+      {
+         {0, },
+         {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}},
+         {4, 591,  812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}},
+         {2, 291,  400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}},
+      },
+      /* 15 fps */
+      {
+         {0, },
+         {9, 956,  876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}},
+         {5, 703,  644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}},
+         {3, 448,  410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}},
+      },
+      /* 20 fps */
+      {
+         {0, },
+         {9, 956,  650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}},
+         {6, 776,  528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}},
+         {4, 591,  402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}},
+      },
+      /* 25 fps */
+      {
+         {0, },
+         {9, 956,  544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}},
+         {7, 840,  478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}},
+         {5, 703,  400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}},
+      },
+      /* 30 fps */
+      {
+         {0, },
+         {9, 956,  438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}},
+         {7, 838,  384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}},
+         {6, 773,  354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}},
+      },
+   },
+   /* VGA */
+   {
+      /* 5 fps */
+      {
+         {0, },
+         {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}},
+         {4, 592,  976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}},
+         {3, 448,  738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}},
+      },
+      /* 10 fps */
+      {
+         {0, },
+         {9, 956,  788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}},
+         {6, 776,  640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}},
+         {4, 592,  488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}},
+      },
+      /* 15 fps */
+      {
+         {0, },
+         {9, 957,  526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}},
+         {9, 957,  526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}},
+         {8, 895,  492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}},
+      },
+      /* 20 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 25 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+      /* 30 fps */
+      {
+         {0, },
+         {0, },
+         {0, },
+         {0, },
+      },
+   },
+};
+
+/*
+ * 16 versions:
+ *   2 tables  (one for Y, and one for U&V)
+ *   16 levels of details per tables
+ *   8 blocs
+ */
+
+const unsigned int TimonRomTable [16][2][16][8] =  
+{
+ { /* version 0 */
+  { /* version 0, passes 0 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000001},
+   {0x00000000,0x00000000,0x00000001,0x00000001,
+    0x00000001,0x00000001,0x00000001,0x00000001},
+   {0x00000000,0x00000000,0x00000001,0x00000001,
+    0x00000001,0x00000009,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000009,0x00000001,
+    0x00000009,0x00000009,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000009,0x00000049,0x00000009},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000009,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000249,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000249,0x00000249,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000249,0x0000124a,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000249,
+    0x00000249,0x0000124a,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x0000124a,0x00009252,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 0, passes 1 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000},
+   {0x00000000,0x00000000,0x00000001,0x00000001,
+    0x00000001,0x00000001,0x00000000,0x00000000},
+   {0x00000000,0x00000000,0x00000009,0x00000001,
+    0x00000001,0x00000009,0x00000000,0x00000000},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000009,0x00000000,0x00000000},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000009,0x00000001,0x00000000},
+   {0x00000000,0x00000000,0x00000049,0x00000009,
+    0x00000009,0x00000049,0x00000001,0x00000001},
+   {0x00000000,0x00000000,0x00000049,0x00000009,
+    0x00000009,0x00000049,0x00000001,0x00000001},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000009,0x00000001},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000009,0x00000001},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000009,0x00000001},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000249,0x00000049,0x00000009},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000249,0x00000049,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000049,
+    0x00000249,0x00000249,0x00000049,0x00000009},
+   {0x00000000,0x00000000,0x00001249,0x00000249,
+    0x0000124a,0x0000124a,0x0000024a,0x00000049},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 1 */
+  { /* version 1, passes 0 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000001},
+   {0x00000000,0x00000000,0x00000001,0x00000001,
+    0x00000001,0x00000009,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000009,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000009,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000249,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00001252},
+   {0x00000000,0x00000000,0x00000049,0x00000249,
+    0x00000249,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00000049,0x00000249,
+    0x0000124a,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x0000124a,0x00009252,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009252,0x00009252,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x0000924a,
+    0x00009292,0x00009493,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009492,0x0000a49b,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 1, passes 1 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000001,0x00000001,0x00000000},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000009,0x00000001,0x00000000},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000009,0x00000001,0x00000000},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000001,0x00000001},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000009,0x00000001},
+   {0x00000000,0x00000000,0x00000249,0x00000049,
+    0x00000049,0x00000249,0x00000009,0x00000001},
+   {0x00000000,0x00000000,0x00000249,0x00000049,
+    0x00000249,0x00000249,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x00000049,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x0000124a,0x00000049,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x0000124a,0x00000049,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x0000124a,0x0000024a,0x00000049},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x0000024a,0x00000049},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x0000024a,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009252,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 2 */
+  { /* version 2, passes 0 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000001},
+   {0x00000000,0x00000000,0x00000009,0x00000009,
+    0x00000009,0x00000009,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000049,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00001252},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x0000124a,0x00009252,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009252,0x00009292,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x0000924a,
+    0x00009252,0x00009493,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x0000924a,
+    0x00009292,0x00009493,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x00009252,
+    0x00009492,0x00009493,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x000124db,0x000124db,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000126dc,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 2, passes 1 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000},
+   {0x00000000,0x00000000,0x00000049,0x00000009,
+    0x00000049,0x00000009,0x00000001,0x00000000},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000000},
+   {0x00000000,0x00000000,0x00000249,0x00000049,
+    0x00000249,0x00000049,0x0000024a,0x00000001},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00000001},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00000001},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x0000024a,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x0000024a,0x00000009},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00009252,0x00001252,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00009292,0x00001252,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00009292,0x00001252,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009292,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x0000924a,0x0000924a,
+    0x00009492,0x00009493,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 3 */
+  { /* version 3, passes 0 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000001},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000049,0x00000249,
+    0x00000249,0x00000249,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x00009252,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x0000124a,0x00009292,0x00009292,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009252,0x00009292,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009292,0x00009493,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x00009252,
+    0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009292,0x0000a49b,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009492,0x0000a49b,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x000124db,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x0000a493,0x0000a49b,0x000124db,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0001249b,0x000126dc,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000136e4,0x0001b725,0x000136e4},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 3, passes 1 */
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000},
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000001,0x00000000},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x00000049,0x00000001},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x0000124a,0x00001252,0x00000001},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x00001252,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x0000124a,0x00009252,0x00009292,0x00000009},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00009252,0x00009292,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009252,0x00009292,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009493,0x00009292,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009493,0x00009292,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009493,0x00009493,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009292,0x00009493,0x00009493,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x00009493,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009492,0x0000a49b,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x00009292,
+    0x0000a493,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 4 */
+  { /* version 4, passes 0 */
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x0000124a,0x00001252,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x00009252,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x0000124a,0x00009292,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009252,0x00009493,0x00009493,0x0000a49b},
+   {0x00000000,0x00000000,0x00000249,0x0000924a,
+    0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009292,0x00009493,0x0000a49b,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x000124db,0x000124db,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x0000a493,0x000124db,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x000136e4},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0001249b,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000136e4,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x00009252,0x000124db,
+    0x000126dc,0x0001b724,0x0001b725,0x0001b925},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 4, passes 1 */
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00000249,
+    0x0000124a,0x0000124a,0x00001252,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x00009292,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x0000a49b,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x0000a49b,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x0000a49b,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x0000a49b,0x0000a49b,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x00009252,0x0000a49b,
+    0x0001249b,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 5 */
+  { /* version 5, passes 0 */
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x0000124a,0x00001252,0x00009292},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x0000124a,0x00009292,0x00009292,0x00009493},
+   {0x00000000,0x00000000,0x00000249,0x0000924a,
+    0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x000124db,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x0000a493,0x000124db,0x000124db,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0001249b,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0001249b,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0001249b,0x000126dc,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000126dc,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x000136e4,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b724,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 5, passes 1 */
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x00009493,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x00009493,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x0000a49b,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x00009493,0x000124db,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x00009493,0x000124db,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x000124db,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x000124db,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009252,0x000124db,
+    0x000126dc,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 6 */
+  { /* version 6, passes 0 */
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x0000124a,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x0000a49b,0x0000a49b,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x000124db,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000126dc,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000136e4,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000136e4,0x0001b725,0x0001b925},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x000136e4,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b92d,0x0001c92d},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000126dc,0x0001b724,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x000136e4,0x0001b925,0x00025bb6,0x00024b77},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 6, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00000249,
+    0x0000124a,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x00009493,0x0000a49b,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x000126dc,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00009492,0x0000a49b,
+    0x000136e4,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x0001b724,0x0001b724,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 7 */
+  { /* version 7, passes 0 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x0000a49b,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x0000a493,0x0000a49b,0x000124db,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x000136e4},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0001249b,0x000126dc,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00001249,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000136e4,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x0001b725,0x0001b925},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x00009292,0x000124db,
+    0x000126dc,0x0001b724,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b724,0x0001c96e,0x0002496e},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x000136e4,0x0001b925,0x0001c96e,0x0002496e},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x0002496d,0x00025bb6,0x00025bbf},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 7, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x00009493,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x000124db,0x000136e4,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x000124db,0x000136e4,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000124db,0x000136e4,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x000136e4,0x0001b724,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00012492,0x000126db,
+    0x0001b724,0x0001b925,0x0001b725,0x000136e4},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 8 */
+  { /* version 8, passes 0 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009292,0x00009493,0x0000a49b,0x000124db},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x0000a493,0x000124db,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x000136e4},
+   {0x00000000,0x00000000,0x00001249,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000136e4,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x0001b725,0x0001b925},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b92d,0x0001c92d},
+   {0x00000000,0x00000000,0x00009252,0x000124db,
+    0x000126dc,0x0001b724,0x0001b92d,0x0001c92d},
+   {0x00000000,0x00000000,0x00009292,0x000124db,
+    0x000126dc,0x0001b925,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b925,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b925,0x00024b76,0x00024b77},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x000136e4,0x0001b925,0x00024b76,0x00025bbf},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x000136e4,0x0001c92d,0x00024b76,0x00025bbf},
+   {0x00000000,0x00000000,0x00012492,0x000136db,
+    0x0001b724,0x00024b6d,0x0002ddb6,0x0002efff},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 8, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009493,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x000124db,0x000136e4,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000126dc,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000136e4,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00009292,0x000124db,
+    0x000136e4,0x0001b724,0x0001b725,0x000136e4},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x000136e4,0x0001b925,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x000136e4,0x0001b925,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x0002496d,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 9 */
+  { /* version 9, passes 0 */
+   {0x00000000,0x00000000,0x00000049,0x00000049,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00000249,0x00000049,
+    0x00000249,0x00000249,0x0000024a,0x00000049},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x0000124a,0x00009252,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009493,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009292,0x00009493,0x00009493,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0001249b,0x000126dc,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00009252,0x00009493,
+    0x000124db,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009252,0x0000a49b,
+    0x000124db,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00009492,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x000136e4,0x0001b925,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 9, passes 1 */
+   {0x00000000,0x00000000,0x00000249,0x00000049,
+    0x00000009,0x00000009,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000049,0x00000049,0x00000009,0x00000009},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00000249,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x0000124a,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009252,0x0000124a,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x00009252,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x00009292,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x00009292,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x00009493,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000124db,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009252,0x000124db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 10 */
+  { /* version 10, passes 0 */
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00000249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x00009493,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x000124db,0x000124db,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0001249b,0x000126dc,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000126dc,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009252,0x0000a49b,
+    0x000124db,0x000136e4,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x00009492,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b92d,0x0001b724},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000126dc,0x0001b925,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x000136e4,0x0002496d,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 10, passes 1 */
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000049,0x00000049,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00000249,0x00000049,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x00009252,0x0000024a,0x00000049},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009493,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00009252,
+    0x00009492,0x00009493,0x00001252,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x00009493,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x00009492,0x00009493,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x00009493,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009252,0x000126db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 11 */
+  { /* version 11, passes 0 */
+   {0x00000000,0x00000000,0x00000249,0x00000249,
+    0x00000249,0x00000249,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009492,0x0000a49b,0x0000a49b,0x00009292},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x000124db,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000126dc,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b725,0x000136e4},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+   {0x00000000,0x00000000,0x00009492,0x0000a49b,
+    0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b925,0x0001c96e,0x0001b925},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x0001b724,0x0001b925,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001c924,0x0002496d,0x00025bb6,0x00024b77},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 11, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00000249,
+    0x00000249,0x00000249,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009252,0x00009252,0x0000024a,0x0000024a},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x0000a49b,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x0000a49b,0x00009292,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000124db,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000124db,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000126dc,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009292,0x000124db,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 12 */
+  { /* version 12, passes 0 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x0000a493,0x0000a49b,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x000126dc,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b725,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000136e4,0x0001b724,0x0001b92d,0x000136e4},
+   {0x00000000,0x00000000,0x00009492,0x0000a49b,
+    0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b724,0x0001b92d,0x0001b724},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b925,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x0001b724,0x0001b925,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x0001b724,0x0001c92d,0x0001c96e,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x0001b724,0x0001c92d,0x00024b76,0x0002496e},
+   {0x00000000,0x00000000,0x00012492,0x000126db,
+    0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 12, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x00001249,0x00009292,
+    0x00009492,0x00009252,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x00009292,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x0000a49b,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000124db,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000124db,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000126dc,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x000136e4,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00009492,0x000126db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001c924,0x0001b724,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 13 */
+  { /* version 13, passes 0 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x00009252,0x00009292,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x000124db,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x0000a49b,
+    0x0001249b,0x000126dc,0x000126dc,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x000136e4,0x0001b725,0x000124db},
+   {0x00000000,0x00000000,0x00009292,0x0000a49b,
+    0x000136e4,0x0001b724,0x0001b725,0x000126dc},
+   {0x00000000,0x00000000,0x00009292,0x000124db,
+    0x000136e4,0x0001b724,0x0001b725,0x000126dc},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b724,0x0001c96e,0x000136e4},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001c92d,0x0001c96e,0x0001b724},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x000136e4,0x0001c92d,0x0001c96e,0x0001b724},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x0001b724,0x0001c92d,0x0001c96e,0x0001b925},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001b724,0x0001c92d,0x00024b76,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001b924,0x0001c92d,0x00024b76,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001b924,0x0001c92d,0x00024b76,0x0002496e},
+   {0x00000000,0x00000000,0x00012492,0x000136db,
+    0x00024924,0x00024b6d,0x0002ddb6,0x00025bbf},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 13, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x00009492,0x00009292,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x0000a49b,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000124db,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000136db,
+    0x0001b724,0x000124db,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000136db,
+    0x0001b724,0x000126dc,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00009292,0x000136db,
+    0x0001b724,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x00009492,0x000136db,
+    0x0001b724,0x000126dc,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00012492,0x0001b6db,
+    0x0001c924,0x0001b724,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 14 */
+  { /* version 14, passes 0 */
+   {0x00000000,0x00000000,0x00001249,0x0000924a,
+    0x00009292,0x00009493,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00001249,0x0000a49b,
+    0x0000a493,0x000124db,0x000126dc,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x0000a49b},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x000136e4,0x0001b725,0x000124db},
+   {0x00000000,0x00000000,0x00009292,0x000124db,
+    0x000126dc,0x0001b724,0x0001b92d,0x000126dc},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b724,0x0001b92d,0x000126dc},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001c92d,0x0001c96e,0x000136e4},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x0001b724,0x0001c92d,0x0001c96e,0x0001b724},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x0001b724,0x0001c92d,0x00024b76,0x0001b925},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001b724,0x0001c92d,0x00024b76,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001b724,0x0001c92d,0x00024b76,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x0001c92d,0x00024b76,0x0002496e},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b924,0x0002496d,0x00024b76,0x00024b77},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b924,0x00024b6d,0x0002ddb6,0x00025bbf},
+   {0x00000000,0x00000000,0x00012492,0x0001b6db,
+    0x00024924,0x0002db6d,0x00036db6,0x0002efff},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 14, passes 1 */
+   {0x00000000,0x00000000,0x00001249,0x00001249,
+    0x0000124a,0x0000124a,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x00009493,
+    0x0000a493,0x00009292,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x0000a49b,0x00001252,0x00001252},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000136e4,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000136e4,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x000136e4,0x00009493,0x00009292},
+   {0x00000000,0x00000000,0x00009492,0x000136db,
+    0x0001b724,0x000136e4,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x00009492,0x000136db,
+    0x0001b724,0x000136e4,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x00009492,0x000136db,
+    0x0001b724,0x000136e4,0x0000a49b,0x00009493},
+   {0x00000000,0x00000000,0x00009492,0x000136db,
+    0x0001b724,0x000136e4,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x000136e4,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x000136e4,0x000124db,0x0000a49b},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b724,0x000136e4,0x000126dc,0x000124db},
+   {0x00000000,0x00000000,0x00012492,0x0001b6db,
+    0x0001c924,0x0001b724,0x000136e4,0x000126dc},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ },
+ { /* version 15 */
+  { /* version 15, passes 0 */
+   {0x00000000,0x00000000,0x00001249,0x00009493,
+    0x0000a493,0x0000a49b,0x000124db,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0001249b,0x000126dc,0x000136e4,0x000124db},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x000126dc,0x0001b724,0x0001b725,0x000126dc},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x0001b724,0x0001b92d,0x000126dc},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x000136e4,0x0001b925,0x0001c96e,0x000136e4},
+   {0x00000000,0x00000000,0x00009492,0x000124db,
+    0x0001b724,0x0001c92d,0x0001c96e,0x0001b724},
+   {0x00000000,0x00000000,0x0000a492,0x000124db,
+    0x0001b724,0x0001c92d,0x0001c96e,0x0001b724},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001b724,0x0001c92d,0x0001c96e,0x0001b925},
+   {0x00000000,0x00000000,0x0000a492,0x000126db,
+    0x0001b924,0x0001c92d,0x00024b76,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b924,0x0001c92d,0x00024b76,0x0001c92d},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001b924,0x0002496d,0x00024b76,0x0002496e},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001c924,0x0002496d,0x00025bb6,0x00024b77},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001c924,0x00024b6d,0x00025bb6,0x00024b77},
+   {0x00000000,0x00000000,0x00012492,0x000136db,
+    0x0001c924,0x00024b6d,0x0002ddb6,0x00025bbf},
+   {0x00000000,0x00000000,0x00012492,0x0001b6db,
+    0x00024924,0x0002db6d,0x00036db6,0x0002efff},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  },
+  { /* version 15, passes 1 */
+   {0x00000000,0x00000000,0x0000924a,0x0000924a,
+    0x00009292,0x00009292,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x0000a49b,
+    0x0000a493,0x000124db,0x00009292,0x00009292},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000124db,0x0001b724,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000126dc,0x0001b724,0x00009493,0x00009493},
+   {0x00000000,0x00000000,0x0000924a,0x000124db,
+    0x000136e4,0x0001b724,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00009292,0x000136db,
+    0x0001b724,0x0001b724,0x0000a49b,0x0000a49b},
+   {0x00000000,0x00000000,0x00009492,0x000136db,
+    0x0001c924,0x0001b724,0x000124db,0x000124db},
+   {0x00000000,0x00000000,0x00009492,0x000136db,
+    0x0001c924,0x0001b724,0x000124db,0x000124db},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001c924,0x0001b724,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001c924,0x0001b925,0x000126dc,0x000126dc},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001c924,0x0001b925,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001c924,0x0001b925,0x000136e4,0x000136e4},
+   {0x00000000,0x00000000,0x0000a492,0x000136db,
+    0x0001c924,0x0001b925,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x00012492,0x000136db,
+    0x0001c924,0x0001b925,0x0001b725,0x0001b724},
+   {0x00000000,0x00000000,0x00012492,0x0001b6db,
+    0x00024924,0x0002496d,0x0001b92d,0x0001b925},
+   {0x00000000,0x00000000,0x00000000,0x00000000,
+    0x00000000,0x00000000,0x00000000,0x00000000}
+  }
+ }
+};
diff --git a/drivers/usb/media/pwc/pwc-uncompress.c b/drivers/usb/media/pwc/pwc-uncompress.c
new file mode 100644 (file)
index 0000000..c062e43
--- /dev/null
@@ -0,0 +1,147 @@
+/* Linux driver for Philips webcam
+   Decompression frontend.
+   (C) 1999-2003 Nemosoft Unv.
+   (C) 2004      Luc Saillard (luc@saillard.org)
+
+   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+   driver and thus may have bugs that are not present in the original version.
+   Please send bug reports and support requests to <luc@saillard.org>.
+   The decompression routines have been implemented by reverse-engineering the
+   Nemosoft binary pwcx module. Caveat emptor.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <asm/current.h>
+#include <asm/types.h>
+
+#include "pwc.h"
+#include "pwc-uncompress.h"
+#include "pwc-dec1.h"
+#include "pwc-dec23.h"
+
+int pwc_decompress(struct pwc_device *pdev)
+{
+       struct pwc_frame_buf *fbuf;
+       int n, line, col, stride;
+       void *yuv, *image;
+       u16 *src;
+       u16 *dsty, *dstu, *dstv;
+
+       if (pdev == NULL)
+               return -EFAULT;
+#if defined(__KERNEL__) && defined(PWC_MAGIC)
+       if (pdev->magic != PWC_MAGIC) {
+               Err("pwc_decompress(): magic failed.\n");
+               return -EFAULT;
+       }
+#endif
+
+       fbuf = pdev->read_frame;
+       if (fbuf == NULL)
+               return -EFAULT;
+       image = pdev->image_ptr[pdev->fill_image];
+       if (!image)
+               return -EFAULT;
+
+       yuv = fbuf->data + pdev->frame_header_size;  /* Skip header */
+
+       /* Raw format; that's easy... */
+       if (pdev->vpalette == VIDEO_PALETTE_RAW)
+       {
+               memcpy(image, yuv, pdev->frame_size);
+               return 0;
+       }
+
+       if (pdev->vbandlength == 0) {
+               /* Uncompressed mode. We copy the data into the output buffer,
+                  using the viewport size (which may be larger than the image
+                  size). Unfortunately we have to do a bit of byte stuffing
+                  to get the desired output format/size.
+                */
+                       /*
+                        * We do some byte shuffling here to go from the
+                        * native format to YUV420P.
+                        */
+                       src = (u16 *)yuv;
+                       n = pdev->view.x * pdev->view.y;
+
+                       /* offset in Y plane */
+                       stride = pdev->view.x * pdev->offset.y + pdev->offset.x;
+                       dsty = (u16 *)(image + stride);
+
+                       /* offsets in U/V planes */
+                       stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2;
+                       dstu = (u16 *)(image + n +         stride);
+                       dstv = (u16 *)(image + n + n / 4 + stride);
+
+                       /* increment after each line */
+                       stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */
+
+                       for (line = 0; line < pdev->image.y; line++) {
+                               for (col = 0; col < pdev->image.x; col += 4) {
+                                       *dsty++ = *src++;
+                                       *dsty++ = *src++;
+                                       if (line & 1)
+                                               *dstv++ = *src++;
+                                       else
+                                               *dstu++ = *src++;
+                               }
+                               dsty += stride;
+                               if (line & 1)
+                                       dstv += (stride >> 1);
+                               else
+                                       dstu += (stride >> 1);
+                       }
+       }
+       else {
+               /* Compressed; the decompressor routines will write the data
+                  in planar format immediately.
+                */
+               int flags;
+                
+                flags = PWCX_FLAG_PLANAR;
+                if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot)
+                {
+                  printk(KERN_ERR "pwc: Mode Bayer is not supported for now\n");
+                  flags |= PWCX_FLAG_BAYER;
+                  return -ENXIO; /* No such device or address: missing decompressor */
+                }
+
+               switch (pdev->type)
+                {
+                 case 675:
+                 case 680:
+                 case 690:
+                 case 720:
+                 case 730:
+                 case 740:
+                 case 750:
+                   pwc_dec23_decompress(&pdev->image, &pdev->view, &pdev->offset,
+                               yuv, image,
+                               flags,
+                               pdev->decompress_data, pdev->vbandlength);
+                   break;
+                 case 645:
+                 case 646:
+                   /* TODO & FIXME */
+                   return -ENXIO; /* No such device or address: missing decompressor */
+                   break;
+                }
+       }
+       return 0;
+}
+
+
diff --git a/drivers/usb/media/pwc/pwc.h b/drivers/usb/media/pwc/pwc.h
new file mode 100644 (file)
index 0000000..53b516d
--- /dev/null
@@ -0,0 +1,278 @@
+/* (C) 1999-2003 Nemosoft Unv.
+   (C) 2004      Luc Saillard (luc@saillard.org)
+
+   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+   driver and thus may have bugs that are not present in the original version.
+   Please send bug reports and support requests to <luc@saillard.org>.
+   The decompression routines have been implemented by reverse-engineering the
+   Nemosoft binary pwcx module. Caveat emptor.
+
+   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 PWC_H
+#define PWC_H
+
+#include <linux/version.h>
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/spinlock.h>
+#include <linux/videodev.h>
+#include <linux/wait.h>
+#include <linux/smp_lock.h>
+#include <asm/semaphore.h>
+#include <asm/errno.h>
+
+#include "pwc-uncompress.h"
+#include "pwc-ioctl.h"
+
+/* Defines and structures for the Philips webcam */
+/* Used for checking memory corruption/pointer validation */
+#define PWC_MAGIC 0x89DC10ABUL
+#undef PWC_MAGIC
+
+/* Turn some debugging options on/off */
+#define PWC_DEBUG 0
+
+/* Trace certain actions in the driver */
+#define TRACE_MODULE   0x0001
+#define TRACE_PROBE    0x0002
+#define TRACE_OPEN     0x0004
+#define TRACE_READ     0x0008
+#define TRACE_MEMORY   0x0010
+#define TRACE_FLOW     0x0020
+#define TRACE_SIZE     0x0040
+#define TRACE_PWCX     0x0080
+#define TRACE_SEQUENCE 0x1000
+
+#define Trace(R, A...) if (pwc_trace & R) printk(KERN_DEBUG PWC_NAME " " A)
+#define Debug(A...) printk(KERN_DEBUG PWC_NAME " " A)
+#define Info(A...)  printk(KERN_INFO  PWC_NAME " " A)
+#define Err(A...)   printk(KERN_ERR   PWC_NAME " " A)
+
+
+/* Defines for ToUCam cameras */
+#define TOUCAM_HEADER_SIZE             8
+#define TOUCAM_TRAILER_SIZE            4
+
+#define FEATURE_MOTOR_PANTILT          0x0001
+
+/* Version block */
+#define PWC_MAJOR      9
+#define PWC_MINOR      0
+#define PWC_VERSION    "9.0.2-unofficial"
+#define PWC_NAME       "pwc"
+
+/* Turn certain features on/off */
+#define PWC_INT_PIPE 0
+
+/* Ignore errors in the first N frames, to allow for startup delays */
+#define FRAME_LOWMARK 5
+
+/* Size and number of buffers for the ISO pipe. */
+#define MAX_ISO_BUFS           2
+#define ISO_FRAMES_PER_DESC    10
+#define ISO_MAX_FRAME_SIZE     960
+#define ISO_BUFFER_SIZE        (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
+
+/* Frame buffers: contains compressed or uncompressed video data. */
+#define MAX_FRAMES             5
+/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */
+#define PWC_FRAME_SIZE                 (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE)
+
+/* Absolute maximum number of buffers available for mmap() */
+#define MAX_IMAGES             10
+
+/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */
+struct pwc_iso_buf
+{
+       void *data;
+       int  length;
+       int  read;
+       struct urb *urb;
+};
+
+/* intermediate buffers with raw data from the USB cam */
+struct pwc_frame_buf
+{
+   void *data;
+   volatile int filled;                /* number of bytes filled */
+   struct pwc_frame_buf *next; /* list */
+#if PWC_DEBUG
+   int sequence;               /* Sequence number */
+#endif
+};
+
+struct pwc_device
+{
+   struct video_device *vdev;
+#ifdef PWC_MAGIC
+   int magic;
+#endif
+   /* Pointer to our usb_device */
+   struct usb_device *udev;
+   
+   int type;                    /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
+   int release;                        /* release number */
+   int features;               /* feature bits */
+   char serial[30];            /* serial number (string) */
+   int error_status;           /* set when something goes wrong with the cam (unplugged, USB errors) */
+   int usb_init;               /* set when the cam has been initialized over USB */
+
+   /*** Video data ***/
+   int vopen;                  /* flag */
+   int vendpoint;              /* video isoc endpoint */
+   int vcinterface;            /* video control interface */
+   int valternate;             /* alternate interface needed */
+   int vframes, vsize;         /* frames-per-second & size (see PSZ_*) */
+   int vpalette;               /* palette: 420P, RAW or RGBBAYER */
+   int vframe_count;           /* received frames */
+   int vframes_dumped;                 /* counter for dumped frames */
+   int vframes_error;          /* frames received in error */
+   int vmax_packet_size;       /* USB maxpacket size */
+   int vlast_packet_size;      /* for frame synchronisation */
+   int visoc_errors;           /* number of contiguous ISOC errors */
+   int vcompression;           /* desired compression factor */
+   int vbandlength;            /* compressed band length; 0 is uncompressed */
+   char vsnapshot;             /* snapshot mode */
+   char vsync;                 /* used by isoc handler */
+   char vmirror;               /* for ToUCaM series */
+   
+   int cmd_len;
+   unsigned char cmd_buf[13];
+
+   /* The image acquisition requires 3 to 4 steps:
+      1. data is gathered in short packets from the USB controller
+      2. data is synchronized and packed into a frame buffer
+      3a. in case data is compressed, decompress it directly into image buffer
+      3b. in case data is uncompressed, copy into image buffer with viewport
+      4. data is transferred to the user process
+
+      Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES....
+      We have in effect a back-to-back-double-buffer system.
+    */
+   /* 1: isoc */
+   struct pwc_iso_buf sbuf[MAX_ISO_BUFS];
+   char iso_init;
+
+   /* 2: frame */
+   struct pwc_frame_buf *fbuf; /* all frames */
+   struct pwc_frame_buf *empty_frames, *empty_frames_tail;     /* all empty frames */
+   struct pwc_frame_buf *full_frames, *full_frames_tail;       /* all filled frames */
+   struct pwc_frame_buf *fill_frame;   /* frame currently being filled */
+   struct pwc_frame_buf *read_frame;   /* frame currently read by user process */
+   int frame_header_size, frame_trailer_size;
+   int frame_size;
+   int frame_total_size; /* including header & trailer */
+   int drop_frames;
+#if PWC_DEBUG
+   int sequence;                       /* Debugging aid */
+#endif
+
+   /* 3: decompression */
+   struct pwc_decompressor *decompressor;      /* function block with decompression routines */
+   void *decompress_data;              /* private data for decompression engine */
+
+   /* 4: image */
+   /* We have an 'image' and a 'view', where 'image' is the fixed-size image
+      as delivered by the camera, and 'view' is the size requested by the
+      program. The camera image is centered in this viewport, laced with
+      a gray or black border. view_min <= image <= view <= view_max;
+    */
+   int image_mask;                     /* bitmask of supported sizes */
+   struct pwc_coord view_min, view_max;        /* minimum and maximum viewable sizes */
+   struct pwc_coord abs_max;            /* maximum supported size with compression */
+   struct pwc_coord image, view;       /* image and viewport size */
+   struct pwc_coord offset;            /* offset within the viewport */
+
+   void *image_data;                   /* total buffer, which is subdivided into ... */
+   void *image_ptr[MAX_IMAGES];                /* ...several images... */
+   int fill_image;                     /* ...which are rotated. */
+   int len_per_image;                  /* length per image */
+   int image_read_pos;                 /* In case we read data in pieces, keep track of were we are in the imagebuffer */
+   int image_used[MAX_IMAGES];         /* For MCAPTURE and SYNC */
+
+   struct semaphore modlock;           /* to prevent races in video_open(), etc */
+   spinlock_t ptrlock;                 /* for manipulating the buffer pointers */
+
+   /*** motorized pan/tilt feature */
+   struct pwc_mpt_range angle_range;
+   int pan_angle;                      /* in degrees * 100 */
+   int tilt_angle;                     /* absolute angle; 0,0 is home position */
+
+   /*** Misc. data ***/
+   wait_queue_head_t frameq;           /* When waiting for a frame to finish... */
+#if PWC_INT_PIPE
+   void *usb_int_handler;              /* for the interrupt endpoint */
+#endif
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Global variables */
+extern int pwc_trace;
+extern int pwc_preferred_compression;
+
+/** functions in pwc-if.c */
+int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot);
+
+/** Functions in pwc-misc.c */
+/* sizes in pixels */
+extern struct pwc_coord pwc_image_sizes[PSZ_MAX];
+
+int pwc_decode_size(struct pwc_device *pdev, int width, int height);
+void pwc_construct(struct pwc_device *pdev);
+
+/** Functions in pwc-ctrl.c */
+/* Request a certain video mode. Returns < 0 if not possible */
+extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot);
+/* Calculate the number of bytes per image (not frame) */
+extern void pwc_set_image_buffer_size(struct pwc_device *pdev);
+
+/* Various controls; should be obvious. Value 0..65535, or < 0 on error */
+extern int pwc_get_brightness(struct pwc_device *pdev);
+extern int pwc_set_brightness(struct pwc_device *pdev, int value);
+extern int pwc_get_contrast(struct pwc_device *pdev);
+extern int pwc_set_contrast(struct pwc_device *pdev, int value);
+extern int pwc_get_gamma(struct pwc_device *pdev);
+extern int pwc_set_gamma(struct pwc_device *pdev, int value);
+extern int pwc_get_saturation(struct pwc_device *pdev);
+extern int pwc_set_saturation(struct pwc_device *pdev, int value);
+extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value);
+extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value);
+extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor);
+
+/* Power down or up the camera; not supported by all models */
+extern int pwc_camera_power(struct pwc_device *pdev, int power);
+
+/* Private ioctl()s; see pwc-ioctl.h */
+extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg);
+
+
+/** pwc-uncompress.c */
+/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */
+extern int pwc_decompress(struct pwc_device *pdev);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/drivers/usb/media/sn9c102_hv7131d.c b/drivers/usb/media/sn9c102_hv7131d.c
new file mode 100644 (file)
index 0000000..18070d5
--- /dev/null
@@ -0,0 +1,271 @@
+/***************************************************************************
+ * Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera     *
+ * Controllers                                                             *
+ *                                                                         *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ *                                                                         *
+ * 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 "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor hv7131d;
+
+
+static int hv7131d_init(struct sn9c102_device* cam)
+{
+       int err = 0;
+
+       err += sn9c102_write_reg(cam, 0x00, 0x10);
+       err += sn9c102_write_reg(cam, 0x00, 0x11);
+       err += sn9c102_write_reg(cam, 0x00, 0x14);
+       err += sn9c102_write_reg(cam, 0x60, 0x17);
+       err += sn9c102_write_reg(cam, 0x0e, 0x18);
+       err += sn9c102_write_reg(cam, 0xf2, 0x19);
+
+       err += sn9c102_i2c_write(cam, 0x01, 0x04);
+       err += sn9c102_i2c_write(cam, 0x02, 0x00);
+       err += sn9c102_i2c_write(cam, 0x28, 0x00);
+
+       return err;
+}
+
+
+static int hv7131d_get_ctrl(struct sn9c102_device* cam, 
+                            struct v4l2_control* ctrl)
+{
+       switch (ctrl->id) {
+       case V4L2_CID_EXPOSURE:
+               {
+                       int r1 = sn9c102_i2c_read(cam, 0x26),
+                           r2 = sn9c102_i2c_read(cam, 0x27);
+                       if (r1 < 0 || r2 < 0)
+                               return -EIO;
+                       ctrl->value = (r1 << 8) | (r2 & 0xff);
+               }
+               return 0;
+       case V4L2_CID_RED_BALANCE:
+               if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0)
+                       return -EIO;
+               ctrl->value = 0x3f - (ctrl->value & 0x3f);
+               return 0;
+       case V4L2_CID_BLUE_BALANCE:
+               if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0)
+                       return -EIO;
+               ctrl->value = 0x3f - (ctrl->value & 0x3f);
+               return 0;
+       case SN9C102_V4L2_CID_GREEN_BALANCE:
+               if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0)
+                       return -EIO;
+               ctrl->value = 0x3f - (ctrl->value & 0x3f);
+               return 0;
+       case SN9C102_V4L2_CID_RESET_LEVEL:
+               if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0)
+                       return -EIO;
+               ctrl->value &= 0x3f;
+               return 0;
+       case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE:
+               if ((ctrl->value = sn9c102_i2c_read(cam, 0x34)) < 0)
+                       return -EIO;
+               ctrl->value &= 0x07;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+
+static int hv7131d_set_ctrl(struct sn9c102_device* cam, 
+                            const struct v4l2_control* ctrl)
+{
+       int err = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_EXPOSURE:
+               err += sn9c102_i2c_write(cam, 0x26, ctrl->value >> 8);
+               err += sn9c102_i2c_write(cam, 0x27, ctrl->value & 0xff);
+               break;
+       case V4L2_CID_RED_BALANCE:
+               err += sn9c102_i2c_write(cam, 0x31, 0x3f - ctrl->value);
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               err += sn9c102_i2c_write(cam, 0x33, 0x3f - ctrl->value);
+               break;
+       case SN9C102_V4L2_CID_GREEN_BALANCE:
+               err += sn9c102_i2c_write(cam, 0x32, 0x3f - ctrl->value);
+               break;
+       case SN9C102_V4L2_CID_RESET_LEVEL:
+               err += sn9c102_i2c_write(cam, 0x30, ctrl->value);
+               break;
+       case SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE:
+               err += sn9c102_i2c_write(cam, 0x34, ctrl->value);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return err ? -EIO : 0;
+}
+
+
+static int hv7131d_set_crop(struct sn9c102_device* cam, 
+                            const struct v4l2_rect* rect)
+{
+       struct sn9c102_sensor* s = &hv7131d;
+       int err = 0;
+       u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2,
+          v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2;
+
+       err += sn9c102_write_reg(cam, h_start, 0x12);
+       err += sn9c102_write_reg(cam, v_start, 0x13);
+
+       return err;
+}
+
+
+static int hv7131d_set_pix_format(struct sn9c102_device* cam, 
+                                  const struct v4l2_pix_format* pix)
+{
+       int err = 0;
+
+       if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+               err += sn9c102_write_reg(cam, 0x42, 0x19);
+       else
+               err += sn9c102_write_reg(cam, 0xf2, 0x19);
+
+       return err;
+}
+
+
+static struct sn9c102_sensor hv7131d = {
+       .name = "HV7131D",
+       .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+       .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
+       .frequency = SN9C102_I2C_100KHZ,
+       .interface = SN9C102_I2C_2WIRES,
+       .i2c_slave_id = 0x11,
+       .init = &hv7131d_init,
+       .qctrl = {
+               {
+                       .id = V4L2_CID_EXPOSURE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "exposure",
+                       .minimum = 0x0250,
+                       .maximum = 0xffff,
+                       .step = 0x0001,
+                       .default_value = 0x0250,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_RED_BALANCE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "red balance",
+                       .minimum = 0x00,
+                       .maximum = 0x3f,
+                       .step = 0x01,
+                       .default_value = 0x00,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_BLUE_BALANCE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "blue balance",
+                       .minimum = 0x00,
+                       .maximum = 0x3f,
+                       .step = 0x01,
+                       .default_value = 0x20,
+                       .flags = 0,
+               },
+               {
+                       .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "green balance",
+                       .minimum = 0x00,
+                       .maximum = 0x3f,
+                       .step = 0x01,
+                       .default_value = 0x1e,
+                       .flags = 0,
+               },
+               {
+                       .id = SN9C102_V4L2_CID_RESET_LEVEL,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "reset level",
+                       .minimum = 0x19,
+                       .maximum = 0x3f,
+                       .step = 0x01,
+                       .default_value = 0x30,
+                       .flags = 0,
+               },
+               {
+                       .id = SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "pixel bias voltage",
+                       .minimum = 0x00,
+                       .maximum = 0x07,
+                       .step = 0x01,
+                       .default_value = 0x02,
+                       .flags = 0,
+               },
+       },
+       .get_ctrl = &hv7131d_get_ctrl,
+       .set_ctrl = &hv7131d_set_ctrl,
+       .cropcap = {
+               .bounds = {
+                       .left = 0,
+                       .top = 0,
+                       .width = 640,
+                       .height = 480,
+               },
+               .defrect = {
+                       .left = 0,
+                       .top = 0,
+                       .width = 640,
+                       .height = 480,
+               },
+       },
+       .set_crop = &hv7131d_set_crop,
+       .pix_format = {
+               .width = 640,
+               .height = 480,
+               .pixelformat = V4L2_PIX_FMT_SBGGR8,
+               .priv = 8,
+       },
+       .set_pix_format = &hv7131d_set_pix_format
+};
+
+
+int sn9c102_probe_hv7131d(struct sn9c102_device* cam)
+{
+       int r0 = 0, r1 = 0, err = 0;
+
+       err += sn9c102_write_reg(cam, 0x01, 0x01);
+       err += sn9c102_write_reg(cam, 0x00, 0x01);
+       err += sn9c102_write_reg(cam, 0x28, 0x17);
+       if (err)
+               return -EIO;
+
+       r0 = sn9c102_i2c_try_read(cam, &hv7131d, 0x00);
+       r1 = sn9c102_i2c_try_read(cam, &hv7131d, 0x01);
+       if (r0 < 0 || r1 < 0)
+               return -EIO;
+
+       if (r0 != 0x00 && r1 != 0x04)
+               return -ENODEV;
+
+       sn9c102_attach_sensor(cam, &hv7131d);
+
+       return 0;
+}
diff --git a/drivers/usb/media/sn9c102_mi0343.c b/drivers/usb/media/sn9c102_mi0343.c
new file mode 100644 (file)
index 0000000..86676ab
--- /dev/null
@@ -0,0 +1,363 @@
+/***************************************************************************
+ * Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera     *
+ * Controllers                                                             *
+ *                                                                         *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
+ *                                                                         *
+ * 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 "sn9c102_sensor.h"
+
+
+static struct sn9c102_sensor mi0343;
+static u8 mi0343_i2c_data[5+1];
+
+
+static int mi0343_init(struct sn9c102_device* cam)
+{
+       int err = 0;
+
+       err += sn9c102_write_reg(cam, 0x00, 0x10);
+       err += sn9c102_write_reg(cam, 0x00, 0x11);
+       err += sn9c102_write_reg(cam, 0x0a, 0x14);
+       err += sn9c102_write_reg(cam, 0x40, 0x01);
+       err += sn9c102_write_reg(cam, 0x20, 0x17);
+       err += sn9c102_write_reg(cam, 0x07, 0x18);
+       err += sn9c102_write_reg(cam, 0xa0, 0x19);
+
+       err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+                                        0x0d, 0x00, 0x01, 0, 0);
+       err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+                                        0x0d, 0x00, 0x00, 0, 0);
+       err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+                                        0x03, 0x01, 0xe1, 0, 0);
+       err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+                                        0x04, 0x02, 0x81, 0, 0);
+       err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+                                        0x05, 0x00, 0x17, 0, 0);
+       err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+                                        0x06, 0x00, 0x11, 0, 0);
+       err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id,
+                                        0x62, 0x04, 0x9a, 0, 0);
+
+       return err;
+}
+
+
+static int mi0343_get_ctrl(struct sn9c102_device* cam, 
+                           struct v4l2_control* ctrl)
+{
+       switch (ctrl->id) {
+       case V4L2_CID_EXPOSURE:
+               if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+                                            0x09, 2+1, mi0343_i2c_data) < 0)
+                       return -EIO;
+               ctrl->value = mi0343_i2c_data[2];
+               return 0;
+       case V4L2_CID_GAIN:
+               if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+                                            0x35, 2+1, mi0343_i2c_data) < 0)
+                       return -EIO;
+               break;
+       case V4L2_CID_HFLIP:
+               if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+                                            0x20, 2+1, mi0343_i2c_data) < 0)
+                       return -EIO;
+               ctrl->value = mi0343_i2c_data[3] & 0x20 ? 1 : 0;
+               return 0;
+       case V4L2_CID_VFLIP:
+               if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+                                            0x20, 2+1, mi0343_i2c_data) < 0)
+                       return -EIO;
+               ctrl->value = mi0343_i2c_data[3] & 0x80 ? 1 : 0;
+               return 0;
+       case V4L2_CID_RED_BALANCE:
+               if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+                                            0x2d, 2+1, mi0343_i2c_data) < 0)
+                       return -EIO;
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+                                            0x2c, 2+1, mi0343_i2c_data) < 0)
+                       return -EIO;
+               break;
+       case SN9C102_V4L2_CID_GREEN_BALANCE:
+               if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id,
+                                            0x2e, 2+1, mi0343_i2c_data) < 0)
+                       return -EIO;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (ctrl->id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_RED_BALANCE:
+       case V4L2_CID_BLUE_BALANCE:
+       case SN9C102_V4L2_CID_GREEN_BALANCE:
+               ctrl->value = mi0343_i2c_data[3] | (mi0343_i2c_data[2] << 8);
+               if (ctrl->value >= 0x10 && ctrl->value <= 0x3f)
+                       ctrl->value -= 0x10;
+               else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f)
+                       ctrl->value -= 0x60;
+               else if (ctrl->value >= 0xe0 && ctrl->value <= 0xff)
+                       ctrl->value -= 0xe0;
+       }
+
+       return 0;
+}
+
+
+static int mi0343_set_ctrl(struct sn9c102_device* cam, 
+                           const struct v4l2_control* ctrl)
+{
+       u16 reg = 0;
+       int err = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_GAIN:
+       case V4L2_CID_RED_BALANCE:
+       case V4L2_CID_BLUE_BALANCE:
+       case SN9C102_V4L2_CID_GREEN_BALANCE:
+               if (ctrl->value <= (0x3f-0x10))
+                       reg = 0x10 + ctrl->value;
+               else if (ctrl->value <= ((0x3f-0x10) + (0x7f-0x60)))
+                       reg = 0x60 + (ctrl->value - (0x3f-0x10));
+               else
+                       reg = 0xe0 + (ctrl->value - (0x3f-0x10) - (0x7f-0x60));
+               break;
+       }
+
+       switch (ctrl->id) {
+       case V4L2_CID_EXPOSURE:
+               err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+                                                mi0343.i2c_slave_id,
+                                                0x09, ctrl->value, 0x00,
+                                                0, 0);
+               break;
+       case V4L2_CID_GAIN:
+               err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+                                                mi0343.i2c_slave_id,
+                                                0x35, reg >> 8, reg & 0xff,
+                                                0, 0);
+               break;
+       case V4L2_CID_HFLIP:
+               err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+                                                mi0343.i2c_slave_id,
+                                                0x20, ctrl->value ? 0x40:0x00,
+                                                ctrl->value ? 0x20:0x00,
+                                                0, 0);
+               break;
+       case V4L2_CID_VFLIP:
+               err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+                                                mi0343.i2c_slave_id,
+                                                0x20, ctrl->value ? 0x80:0x00,
+                                                ctrl->value ? 0x80:0x00,
+                                                0, 0);
+               break;
+       case V4L2_CID_RED_BALANCE:
+               err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+                                                mi0343.i2c_slave_id,
+                                                0x2d, reg >> 8, reg & 0xff,
+                                                0, 0);
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+                                                mi0343.i2c_slave_id,
+                                                0x2c, reg >> 8, reg & 0xff,
+                                                0, 0);
+               break;
+       case SN9C102_V4L2_CID_GREEN_BALANCE:
+               err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+                                                mi0343.i2c_slave_id,
+                                                0x2b, reg >> 8, reg & 0xff,
+                                                0, 0);
+               err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+                                                mi0343.i2c_slave_id,
+                                                0x2e, reg >> 8, reg & 0xff,
+                                                0, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return err ? -EIO : 0;
+}
+
+
+static int mi0343_set_crop(struct sn9c102_device* cam, 
+                            const struct v4l2_rect* rect)
+{
+       struct sn9c102_sensor* s = &mi0343;
+       int err = 0;
+       u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0,
+          v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2;
+
+       err += sn9c102_write_reg(cam, h_start, 0x12);
+       err += sn9c102_write_reg(cam, v_start, 0x13);
+
+       return err;
+}
+
+
+static int mi0343_set_pix_format(struct sn9c102_device* cam, 
+                                 const struct v4l2_pix_format* pix)
+{
+       int err = 0;
+
+       if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) {
+               err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+                                                mi0343.i2c_slave_id,
+                                                0x0a, 0x00, 0x03, 0, 0);
+               err += sn9c102_write_reg(cam, 0x20, 0x19);
+       } else {
+               err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4,
+                                                mi0343.i2c_slave_id,
+                                                0x0a, 0x00, 0x05, 0, 0);
+               err += sn9c102_write_reg(cam, 0xa0, 0x19);
+       }
+
+       return err;
+}
+
+
+static struct sn9c102_sensor mi0343 = {
+       .name = "MI-0343",
+       .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
+       .frequency = SN9C102_I2C_100KHZ,
+       .interface = SN9C102_I2C_2WIRES,
+       .i2c_slave_id = 0x5d,
+       .init = &mi0343_init,
+       .qctrl = {
+               {
+                       .id = V4L2_CID_EXPOSURE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "exposure",
+                       .minimum = 0x00,
+                       .maximum = 0x0f,
+                       .step = 0x01,
+                       .default_value = 0x06,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_GAIN,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "global gain",
+                       .minimum = 0x00,
+                       .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),/*0x6d*/
+                       .step = 0x01,
+                       .default_value = 0x00,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_HFLIP,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "horizontal mirror",
+                       .minimum = 0,
+                       .maximum = 1,
+                       .step = 1,
+                       .default_value = 0,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_VFLIP,
+                       .type = V4L2_CTRL_TYPE_BOOLEAN,
+                       .name = "vertical mirror",
+                       .minimum = 0,
+                       .maximum = 1,
+                       .step = 1,
+                       .default_value = 0,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_RED_BALANCE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "red balance",
+                       .minimum = 0x00,
+                       .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),
+                       .step = 0x01,
+                       .default_value = 0x00,
+                       .flags = 0,
+               },
+               {
+                       .id = V4L2_CID_BLUE_BALANCE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "blue balance",
+                       .minimum = 0x00,
+                       .maximum = (0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0),
+                       .step = 0x01,
+                       .default_value = 0x00,
+                       .flags = 0,
+               },
+               {
+                       .id = SN9C102_V4L2_CID_GREEN_BALANCE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "green balance",
+                       .minimum = 0x00,
+                       .maximum = ((0x3f-0x10)+(0x7f-0x60)+(0xff-0xe0)),
+                       .step = 0x01,
+                       .default_value = 0x00,
+                       .flags = 0,
+               },
+       },
+       .get_ctrl = &mi0343_get_ctrl,
+       .set_ctrl = &mi0343_set_ctrl,
+       .cropcap = {
+               .bounds = {
+                       .left = 0,
+                       .top = 0,
+                       .width = 640,
+                       .height = 480,
+               },
+               .defrect = {
+                       .left = 0,
+                       .top = 0,
+                       .width = 640,
+                       .height = 480,
+               },
+       },
+       .set_crop = &mi0343_set_crop,
+       .pix_format = {
+               .width = 640,
+               .height = 480,
+               .pixelformat = V4L2_PIX_FMT_SBGGR8,
+               .priv = 8,
+       },
+       .set_pix_format = &mi0343_set_pix_format
+};
+
+
+int sn9c102_probe_mi0343(struct sn9c102_device* cam)
+{
+       int err = 0;
+
+       err += sn9c102_write_reg(cam, 0x01, 0x01);
+       err += sn9c102_write_reg(cam, 0x00, 0x01);
+       err += sn9c102_write_reg(cam, 0x28, 0x17);
+       if (err)
+               return -EIO;
+
+       if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00,
+                                    2, mi0343_i2c_data) < 0)
+               return -EIO;
+
+       if (mi0343_i2c_data[4] != 0x32 && mi0343_i2c_data[3] != 0xe3)
+               return -ENODEV;
+
+       sn9c102_attach_sensor(cam, &mi0343);
+
+       return 0;
+}
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
new file mode 100644 (file)
index 0000000..04f0912
--- /dev/null
@@ -0,0 +1,443 @@
+/* Siemens ID Mouse driver v0.5
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of
+  the License, or (at your option) any later version.
+
+  Copyright (C) 2004-5 by Florian 'Floe' Echtler  <echtler@fs.tum.de>
+                      and Andreas  'ad'  Deresch <aderesch@fs.tum.de>
+
+  Derived from the USB Skeleton driver 1.1,
+  Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
+
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+#define WIDTH 225
+#define HEIGHT 288
+#define HEADER "P5 225 288 255 "
+#define IMGSIZE ((WIDTH * HEIGHT) + sizeof(HEADER)-1)
+
+/* Version Information */
+#define DRIVER_VERSION "0.5"
+#define DRIVER_SHORT   "idmouse"
+#define DRIVER_AUTHOR  "Florian 'Floe' Echtler <echtler@fs.tum.de>"
+#define DRIVER_DESC    "Siemens ID Mouse FingerTIP Sensor Driver"
+
+/* Siemens ID Mouse */
+#define USB_IDMOUSE_VENDOR_ID  0x0681
+#define USB_IDMOUSE_PRODUCT_ID 0x0005
+
+/* we still need a minor number */
+#define USB_IDMOUSE_MINOR_BASE 132
+
+static struct usb_device_id idmouse_table[] = {
+       {USB_DEVICE(USB_IDMOUSE_VENDOR_ID, USB_IDMOUSE_PRODUCT_ID)},
+       {} /* null entry at the end */
+};
+
+MODULE_DEVICE_TABLE(usb, idmouse_table);
+
+/* structure to hold all of our device specific stuff */
+struct usb_idmouse {
+
+       struct usb_device *udev; /* save off the usb device pointer */
+       struct usb_interface *interface; /* the interface for this device */
+
+       unsigned char *bulk_in_buffer; /* the buffer to receive data */
+       size_t bulk_in_size; /* the size of the receive buffer */
+       __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
+
+       int open; /* if the port is open or not */
+       int present; /* if the device is not disconnected */
+       struct semaphore sem; /* locks this structure */
+
+};
+
+/* local function prototypes */
+static ssize_t idmouse_read(struct file *file, char __user *buffer,
+                               size_t count, loff_t * ppos);
+
+static int idmouse_open(struct inode *inode, struct file *file);
+static int idmouse_release(struct inode *inode, struct file *file);
+
+static int idmouse_probe(struct usb_interface *interface,
+                               const struct usb_device_id *id);
+
+static void idmouse_disconnect(struct usb_interface *interface);
+
+/* file operation pointers */
+static struct file_operations idmouse_fops = {
+       .owner = THIS_MODULE,
+       .read = idmouse_read,
+       .open = idmouse_open,
+       .release = idmouse_release,
+};
+
+/* class driver information for devfs */
+static struct usb_class_driver idmouse_class = {
+       .name = "usb/idmouse%d",
+       .fops = &idmouse_fops,
+       .mode = S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH, /* filemode (char, 444) */
+       .minor_base = USB_IDMOUSE_MINOR_BASE,
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver idmouse_driver = {
+       .owner = THIS_MODULE,
+       .name = DRIVER_SHORT,
+       .probe = idmouse_probe,
+       .disconnect = idmouse_disconnect,
+       .id_table = idmouse_table,
+};
+
+// prevent races between open() and disconnect()
+static DECLARE_MUTEX(disconnect_sem);
+
+static int idmouse_create_image(struct usb_idmouse *dev)
+{
+       int bytes_read = 0;
+       int bulk_read = 0;
+       int result = 0;
+
+       if (dev->bulk_in_size < sizeof(HEADER))
+               return -ENOMEM;
+
+       memcpy(dev->bulk_in_buffer,HEADER,sizeof(HEADER)-1);
+       bytes_read += sizeof(HEADER)-1;
+
+       /* Dump the setup packets. Yes, they are uncommented, simply 
+          because they were sniffed under Windows using SnoopyPro.
+          I _guess_ that 0x22 is a kind of reset command and 0x21 
+          means init..
+       */
+       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+                               0x21, 0x42, 0x0001, 0x0002, NULL, 0, HZ);
+       if (result < 0)
+               return result;
+       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+                               0x20, 0x42, 0x0001, 0x0002, NULL, 0, HZ);
+       if (result < 0)
+               return result;
+       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+                               0x22, 0x42, 0x0000, 0x0002, NULL, 0, HZ);
+       if (result < 0)
+               return result;
+
+       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+                               0x21, 0x42, 0x0001, 0x0002, NULL, 0, HZ);
+       if (result < 0)
+               return result;
+       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+                               0x20, 0x42, 0x0001, 0x0002, NULL, 0, HZ);
+       if (result < 0)
+               return result;
+       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+                               0x20, 0x42, 0x0000, 0x0002, NULL, 0, HZ);
+       if (result < 0)
+               return result;
+
+       /* loop over a blocking bulk read to get data from the device */
+       while (bytes_read < IMGSIZE) {
+               result = usb_bulk_msg (dev->udev,
+                               usb_rcvbulkpipe (dev->udev, dev->bulk_in_endpointAddr),
+                               dev->bulk_in_buffer + bytes_read,
+                               dev->bulk_in_size, &bulk_read, HZ * 5);
+               if (result < 0)
+                       return result;
+               if (signal_pending(current))
+                       return -EINTR;
+               bytes_read += bulk_read;
+       }
+
+       /* reset the device */
+       result = usb_control_msg (dev->udev, usb_sndctrlpipe (dev->udev, 0),
+                               0x22, 0x42, 0x0000, 0x0002, NULL, 0, HZ);
+       if (result < 0)
+               return result;
+
+       /* should be IMGSIZE == 64815 */
+       dbg("read %d bytes fingerprint data", bytes_read);
+       return 0;
+}
+
+static inline void idmouse_delete(struct usb_idmouse *dev)
+{
+       kfree(dev->bulk_in_buffer);
+       kfree(dev);
+}
+
+static int idmouse_open(struct inode *inode, struct file *file)
+{
+       struct usb_idmouse *dev = NULL;
+       struct usb_interface *interface;
+       int result = 0;
+
+       /* prevent disconnects */
+       down(&disconnect_sem);
+
+       /* get the interface from minor number and driver information */
+       interface = usb_find_interface (&idmouse_driver, iminor (inode));
+       if (!interface) {
+               up(&disconnect_sem);
+               return -ENODEV;
+       }
+       /* get the device information block from the interface */
+       dev = usb_get_intfdata(interface);
+       if (!dev) {
+               up(&disconnect_sem);
+               return -ENODEV;
+       }
+
+       /* lock this device */
+       down(&dev->sem);
+
+       /* check if already open */
+       if (dev->open) {
+
+               /* already open, so fail */
+               result = -EBUSY;
+
+       } else {
+
+               /* create a new image and check for success */
+               result = idmouse_create_image (dev);
+               if (result)
+                       goto error;
+
+               /* increment our usage count for the driver */
+               ++dev->open;
+
+               /* save our object in the file's private structure */
+               file->private_data = dev;
+
+       } 
+
+error:
+
+       /* unlock this device */
+       up(&dev->sem);
+
+       /* unlock the disconnect semaphore */
+       up(&disconnect_sem);
+       return result;
+}
+
+static int idmouse_release(struct inode *inode, struct file *file)
+{
+       struct usb_idmouse *dev;
+
+       /* prevent a race condition with open() */
+       down(&disconnect_sem);
+
+       dev = (struct usb_idmouse *) file->private_data;
+
+       if (dev == NULL) {
+               up(&disconnect_sem);
+               return -ENODEV;
+       }
+
+       /* lock our device */
+       down(&dev->sem);
+
+       /* are we really open? */
+       if (dev->open <= 0) {
+               up(&dev->sem);
+               up(&disconnect_sem);
+               return -ENODEV;
+       }
+
+       --dev->open;
+
+       if (!dev->present) {
+               /* the device was unplugged before the file was released */
+               up(&dev->sem);
+               idmouse_delete(dev);
+               up(&disconnect_sem);
+               return 0;
+       }
+
+       up(&dev->sem);
+       up(&disconnect_sem);
+       return 0;
+}
+
+static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count,
+                               loff_t * ppos)
+{
+       struct usb_idmouse *dev;
+       int result = 0;
+
+       dev = (struct usb_idmouse *) file->private_data;
+
+       // lock this object
+       down (&dev->sem);
+
+       // verify that the device wasn't unplugged
+       if (!dev->present) {
+               up (&dev->sem);
+               return -ENODEV;
+       }
+
+       if (*ppos >= IMGSIZE) {
+               up (&dev->sem);
+               return 0;
+       }
+
+       if (count > IMGSIZE - *ppos)
+               count = IMGSIZE - *ppos;
+
+       if (copy_to_user (buffer, dev->bulk_in_buffer + *ppos, count)) {
+               result = -EFAULT;
+       } else {
+               result = count;
+               *ppos += count;
+       }
+
+       // unlock the device 
+       up(&dev->sem);
+       return result;
+}
+
+static int idmouse_probe(struct usb_interface *interface,
+                               const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(interface);
+       struct usb_idmouse *dev = NULL;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       size_t buffer_size;
+       int result;
+
+       /* check if we have gotten the data or the hid interface */
+       iface_desc = &interface->altsetting[0];
+       if (iface_desc->desc.bInterfaceClass != 0x0A)
+               return -ENODEV;
+
+       /* allocate memory for our device state and initialize it */
+       dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+       if (dev == NULL)
+               return -ENOMEM;
+       memset(dev, 0x00, sizeof(*dev));
+
+       init_MUTEX(&dev->sem);
+       dev->udev = udev;
+       dev->interface = interface;
+
+       /* set up the endpoint information - use only the first bulk-in endpoint */
+       endpoint = &iface_desc->endpoint[0].desc;
+       if (!dev->bulk_in_endpointAddr
+               && (endpoint->bEndpointAddress & USB_DIR_IN)
+               && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+               USB_ENDPOINT_XFER_BULK)) {
+
+               /* we found a bulk in endpoint */
+               buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+               dev->bulk_in_size = buffer_size;
+               dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+               dev->bulk_in_buffer =
+                       kmalloc(IMGSIZE + buffer_size, GFP_KERNEL);
+
+               if (!dev->bulk_in_buffer) {
+                       err("Unable to allocate input buffer.");
+                       idmouse_delete(dev);
+                       return -ENOMEM;
+               }
+       }
+
+       if (!(dev->bulk_in_endpointAddr)) {
+               err("Unable to find bulk-in endpoint.");
+               idmouse_delete(dev);
+               return -ENODEV;
+       }
+       /* allow device read, write and ioctl */
+       dev->present = 1;
+
+       /* we can register the device now, as it is ready */
+       usb_set_intfdata(interface, dev);
+       result = usb_register_dev(interface, &idmouse_class);
+       if (result) {
+               /* something prevented us from registering this device */
+               err("Unble to allocate minor number.");
+               usb_set_intfdata(interface, NULL);
+               idmouse_delete(dev);
+               return result;
+       }
+
+       /* be noisy */
+       dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC);
+
+       return 0;
+}
+
+static void idmouse_disconnect(struct usb_interface *interface)
+{
+       struct usb_idmouse *dev;
+
+       /* prevent races with open() */
+       down(&disconnect_sem);
+
+       /* get device structure */
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+
+       /* lock it */
+       down(&dev->sem);
+
+       /* give back our minor */
+       usb_deregister_dev(interface, &idmouse_class);
+
+       /* prevent device read, write and ioctl */
+       dev->present = 0;
+
+       /* unlock */
+       up(&dev->sem);
+
+       /* if the device is opened, idmouse_release will clean this up */
+       if (!dev->open)
+               idmouse_delete(dev);
+
+       up(&disconnect_sem);
+
+       info("%s disconnected", DRIVER_DESC);
+}
+
+static int __init usb_idmouse_init(void)
+{
+       int result;
+
+       info(DRIVER_DESC " " DRIVER_VERSION);
+
+       /* register this driver with the USB subsystem */
+       result = usb_register(&idmouse_driver);
+       if (result)
+               err("Unable to register device (error %d).", result);
+
+       return result;
+}
+
+static void __exit usb_idmouse_exit(void)
+{
+       /* deregister this driver with the USB subsystem */
+       usb_deregister(&idmouse_driver);
+}
+
+module_init(usb_idmouse_init);
+module_exit(usb_idmouse_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c
new file mode 100644 (file)
index 0000000..ace44a7
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * USB PhidgetInterfaceKit driver 1.0
+ *
+ * Copyright (C) 2004 Sean Young <sean@mess.org>
+ * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is a driver for the USB PhidgetInterfaceKit.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
+#define DRIVER_DESC "USB PhidgetInterfaceKit Driver"
+
+#define USB_VENDOR_ID_GLAB             0x06c2
+#define USB_DEVICE_ID_INTERFACEKIT004  0x0040
+#define USB_DEVICE_ID_INTERFACEKIT888  0x0045
+#define USB_DEVICE_ID_INTERFACEKIT047  0x0051
+#define USB_DEVICE_ID_INTERFACEKIT088  0x0053
+
+#define USB_VENDOR_ID_WISEGROUP                0x0925
+#define USB_DEVICE_ID_INTERFACEKIT884  0x8201
+
+#define MAX_INTERFACES                 8
+
+struct driver_interfacekit {
+       int sensors;
+       int inputs;
+       int outputs;
+       int has_lcd;
+};
+#define ifkit(_sensors, _inputs, _outputs, _lcd)                       \
+static struct driver_interfacekit ph_##_sensors##_inputs##_outputs = { \
+       .sensors        = _sensors,                                     \
+       .inputs         = _inputs,                                      \
+       .outputs        = _outputs,                                     \
+       .has_lcd        = _lcd,                                         \
+};
+ifkit(0, 0, 4, 0);
+ifkit(8, 8, 8, 0);
+ifkit(0, 4, 7, 1);
+ifkit(8, 8, 4, 0);
+ifkit(0, 8, 8, 1);
+
+struct phidget_interfacekit {
+       struct usb_device *udev;
+       struct usb_interface *intf;
+       struct driver_interfacekit *ifkit;
+       int outputs[MAX_INTERFACES];
+       int inputs[MAX_INTERFACES];
+       int sensors[MAX_INTERFACES];
+       u8 lcd_files_on;
+
+       struct urb *irq;
+       unsigned char *data;
+       dma_addr_t data_dma;
+};
+
+static struct usb_device_id id_table[] = {
+       {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT004),
+               .driver_info = (kernel_ulong_t)&ph_004},
+       {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888),
+               .driver_info = (kernel_ulong_t)&ph_888},
+       {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT047),
+               .driver_info = (kernel_ulong_t)&ph_047},
+       {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT088),
+               .driver_info = (kernel_ulong_t)&ph_088},
+       {USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_INTERFACEKIT884),
+               .driver_info = (kernel_ulong_t)&ph_884},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int change_outputs(struct phidget_interfacekit *kit, int output_num, int enable)
+{
+       unsigned char *buffer;
+       int retval;
+       int n;
+
+       buffer = kmalloc(4, GFP_KERNEL);
+       if (!buffer) {
+               dev_err(&kit->udev->dev, "%s - out of memory\n",
+                       __FUNCTION__);
+               return -ENOMEM;
+       }
+
+       kit->outputs[output_num] = enable;
+       memset(buffer, 0, 4);
+       for (n=0; n<8; n++) {
+               if (kit->outputs[n]) {
+                       buffer[0] |= 1 << n;
+               }
+       }
+
+       dev_dbg(&kit->udev->dev, "data: %02x %02x\n", buffer[0], buffer[1]);
+
+       retval = usb_control_msg(kit->udev,
+                        usb_sndctrlpipe(kit->udev, 0),
+                        0x09, 0x21, 0x0200, 0x0000, buffer, 4, 2 * HZ);
+
+       if (retval != 4)
+               dev_err(&kit->udev->dev, "retval = %d\n", retval);
+       kfree(buffer);
+
+       return retval < 0 ? retval : 0;
+}
+
+static int change_string(struct phidget_interfacekit *kit, const char *display, unsigned char row)
+{
+       unsigned char *buffer;
+        unsigned char *form_buffer;
+       int retval = -ENOMEM;
+       int i,j, len, buf_ptr;
+       
+       buffer = kmalloc(8, GFP_KERNEL);
+       form_buffer = kmalloc(30, GFP_KERNEL);
+       if ((!buffer) || (!form_buffer)) {
+               dev_err(&kit->udev->dev, "%s - out of memory\n", __FUNCTION__);
+               goto exit;
+       }
+
+       len = strlen(display);
+       if (len > 20)
+               len = 20;
+
+       dev_dbg(&kit->udev->dev, "Setting LCD line %d to %s\n", row, display);
+
+       form_buffer[0] = row * 0x40 + 0x80;
+       form_buffer[1] = 0x02;
+       buf_ptr = 2;
+       for (i = 0; i<len; i++)
+               form_buffer[buf_ptr++] = display[i];
+
+       for (i = 0; i < (20 - len); i++)
+               form_buffer[buf_ptr++] = 0x20;
+       form_buffer[buf_ptr++] = 0x01;
+       form_buffer[buf_ptr++] = row * 0x40 + 0x80 + strlen(display);
+
+       for (i = 0; i < buf_ptr; i += 7) {
+               if ((buf_ptr - i) > 7)
+                       len = 7;
+               else
+                       len = (buf_ptr - i);
+               for (j = 0; j < len; j++)
+                       buffer[j] = form_buffer[i + j];
+               buffer[7] = len;
+
+               retval = usb_control_msg(kit->udev,
+                                usb_sndctrlpipe(kit->udev, 0),
+                                0x09, 0x21, 0x0200, 0x0000, buffer, 8, 2 * HZ);
+               if (retval < 0)
+                       goto exit;
+       }
+
+       retval = 0;
+exit:
+       kfree(buffer);
+       kfree(form_buffer);
+
+       return retval;
+}
+
+#define set_lcd_line(number)   \
+static ssize_t lcd_line_##number(struct device *dev, const char *buf, size_t count)    \
+{                                                                                      \
+       struct usb_interface *intf = to_usb_interface(dev);                             \
+       struct phidget_interfacekit *kit = usb_get_intfdata(intf);                      \
+       change_string(kit, buf, number - 1);                                            \
+       return count;                                                                   \
+}                                                                                      \
+static DEVICE_ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number);
+set_lcd_line(1);
+set_lcd_line(2);
+
+static ssize_t set_backlight(struct device *dev, const char *buf, size_t count)
+{
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct phidget_interfacekit *kit = usb_get_intfdata(intf);
+       int enabled;
+       unsigned char *buffer;
+       int retval = -ENOMEM;
+       
+       buffer = kmalloc(8, GFP_KERNEL);
+       if (!buffer) {
+               dev_err(&kit->udev->dev, "%s - out of memory\n", __FUNCTION__);
+               goto exit;
+       }
+
+       if (sscanf(buf, "%d", &enabled) < 1) {
+               retval = -EINVAL;
+               goto exit;
+       }
+       memset(buffer, 0x00, 8);
+       if (enabled)
+               buffer[0] = 0x01;
+       buffer[7] = 0x11;
+
+       dev_dbg(&kit->udev->dev, "Setting backlight to %s\n", enabled ? "on" : "off");
+       
+       retval = usb_control_msg(kit->udev,
+                        usb_sndctrlpipe(kit->udev, 0),
+                        0x09, 0x21, 0x0200, 0x0000, buffer, 8, 2 * HZ);
+       if (retval < 0)
+               goto exit;
+
+       retval = count;
+exit:
+       kfree(buffer);
+       return retval;
+}
+static DEVICE_ATTR(backlight, S_IWUGO, NULL, set_backlight);
+
+static void remove_lcd_files(struct phidget_interfacekit *kit)
+{
+       if (kit->lcd_files_on) {
+               dev_dbg(&kit->udev->dev, "Removing lcd files\n");
+               device_remove_file(&kit->intf->dev, &dev_attr_lcd_line_1);
+               device_remove_file(&kit->intf->dev, &dev_attr_lcd_line_2);
+               device_remove_file(&kit->intf->dev, &dev_attr_backlight);
+       }
+}
+
+static ssize_t enable_lcd_files(struct device *dev, const char *buf, size_t count)
+{
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct phidget_interfacekit *kit = usb_get_intfdata(intf);
+       int enable;
+       
+       if (kit->ifkit->has_lcd == 0)
+               return -ENODEV;
+
+       if (sscanf(buf, "%d", &enable) < 1)
+               return -EINVAL;
+
+       if (enable) {
+               if (!kit->lcd_files_on) {
+                       dev_dbg(&kit->udev->dev, "Adding lcd files\n");
+                       device_create_file(&kit->intf->dev, &dev_attr_lcd_line_1);
+                       device_create_file(&kit->intf->dev, &dev_attr_lcd_line_2);
+                       device_create_file(&kit->intf->dev, &dev_attr_backlight);
+                       kit->lcd_files_on = 1;
+               }
+       } else {
+               if (kit->lcd_files_on) {
+                       remove_lcd_files(kit);
+                       kit->lcd_files_on = 0;
+               }
+       }
+       
+       return count;
+}
+static DEVICE_ATTR(lcd, S_IWUGO, NULL, enable_lcd_files);
+
+static void interfacekit_irq(struct urb *urb, struct pt_regs *regs)
+{
+       struct phidget_interfacekit *kit = urb->context;
+       unsigned char *buffer = kit->data;
+       int status;
+       int n;
+
+       switch (urb->status) {
+       case 0:                 /* success */
+               break;
+       case -ECONNRESET:       /* unlink */
+       case -ENOENT:
+       case -ESHUTDOWN:
+               return;
+       /* -EPIPE:  should clear the halt */
+       default:                /* error */
+               goto resubmit;
+       }
+
+       for (n=0; n<8; n++) {
+               kit->inputs[n] = buffer[1] & (1 << n) ? 1 : 0;
+       }
+
+       if (buffer[0] & 1) {
+               kit->sensors[4] = buffer[2] + (buffer[3] & 0x0f) * 256;
+               kit->sensors[5] = buffer[4] + (buffer[3] & 0xf0) * 16;
+               kit->sensors[6] = buffer[5] + (buffer[6] & 0x0f) * 256;
+               kit->sensors[7] = buffer[7] + (buffer[6] & 0xf0) * 16;
+       } else {
+               kit->sensors[0] = buffer[2] + (buffer[3] & 0x0f) * 256;
+               kit->sensors[1] = buffer[4] + (buffer[3] & 0xf0) * 16;
+               kit->sensors[2] = buffer[5] + (buffer[6] & 0x0f) * 256;
+               kit->sensors[3] = buffer[7] + (buffer[6] & 0xf0) * 16;
+       }
+
+resubmit:
+       status = usb_submit_urb(urb, SLAB_ATOMIC);
+       if (status)
+               err("can't resubmit intr, %s-%s/interfacekit0, status %d",
+                       kit->udev->bus->bus_name,
+                       kit->udev->devpath, status);
+}
+
+#define show_set_output(value)         \
+static ssize_t set_output##value(struct device *dev, const char *buf,  \
+                                                       size_t count)   \
+{                                                                      \
+       struct usb_interface *intf = to_usb_interface(dev);             \
+       struct phidget_interfacekit *kit = usb_get_intfdata(intf);      \
+       int enabled;                                                    \
+       int retval;                                                     \
+                                                                       \
+       if (sscanf(buf, "%d", &enabled) < 1) {                          \
+               return -EINVAL;                                         \
+       }                                                               \
+                                                                       \
+       retval = change_outputs(kit, value - 1, enabled ? 1 : 0);       \
+                                                                       \
+       return retval ? retval : count;                                 \
+}                                                                      \
+                                                                       \
+static ssize_t show_output##value(struct device *dev, char *buf)       \
+{                                                                      \
+       struct usb_interface *intf = to_usb_interface(dev);             \
+       struct phidget_interfacekit *kit = usb_get_intfdata(intf);      \
+                                                                       \
+       return sprintf(buf, "%d\n", kit->outputs[value - 1 ]);          \
+}                                                                      \
+static DEVICE_ATTR(output##value, S_IWUGO | S_IRUGO,                   \
+               show_output##value, set_output##value);
+show_set_output(1);
+show_set_output(2);
+show_set_output(3);
+show_set_output(4);
+show_set_output(5);
+show_set_output(6);
+show_set_output(7);
+show_set_output(8);    /* should be MAX_INTERFACES - 1 */
+
+#define show_input(value)      \
+static ssize_t show_input##value(struct device *dev, char *buf)        \
+{                                                                      \
+       struct usb_interface *intf = to_usb_interface(dev);             \
+       struct phidget_interfacekit *kit = usb_get_intfdata(intf);      \
+                                                                       \
+       return sprintf(buf, "%d\n", kit->inputs[value - 1]);            \
+}                                                                      \
+static DEVICE_ATTR(input##value, S_IRUGO, show_input##value, NULL);
+
+show_input(1);
+show_input(2);
+show_input(3);
+show_input(4);
+show_input(5);
+show_input(6);
+show_input(7);
+show_input(8);         /* should be MAX_INTERFACES - 1 */
+
+#define show_sensor(value)     \
+static ssize_t show_sensor##value(struct device *dev, char *buf)       \
+{                                                                      \
+       struct usb_interface *intf = to_usb_interface(dev);             \
+       struct phidget_interfacekit *kit = usb_get_intfdata(intf);      \
+                                                                       \
+       return sprintf(buf, "%d\n", kit->sensors[value - 1]);           \
+}                                                                      \
+static DEVICE_ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL);
+
+show_sensor(1);
+show_sensor(2);
+show_sensor(3);
+show_sensor(4);
+show_sensor(5);
+show_sensor(6);
+show_sensor(7);
+show_sensor(8);                /* should be MAX_INTERFACES - 1 */
+
+static int interfacekit_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct usb_host_interface *interface;
+       struct usb_endpoint_descriptor *endpoint;
+       struct phidget_interfacekit *kit;
+       struct driver_interfacekit *ifkit;
+       int pipe, maxp;
+
+       ifkit = (struct driver_interfacekit *)id->driver_info;
+       if (!ifkit)
+               return -ENODEV;
+
+       interface = intf->cur_altsetting;
+       if (interface->desc.bNumEndpoints != 1)
+               return -ENODEV;
+
+       endpoint = &interface->endpoint[0].desc;
+       if (!(endpoint->bEndpointAddress & 0x80)) 
+               return -ENODEV;
+       /*
+        * bmAttributes
+        */
+       pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+       maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+       
+       kit = kmalloc(sizeof(*kit), GFP_KERNEL);
+       if (kit  == NULL) {
+               dev_err(&intf->dev, "%s - out of memory\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+       memset(kit, 0, sizeof(*kit));
+       kit->ifkit = ifkit;
+
+       kit->data = usb_buffer_alloc(dev, 8, SLAB_ATOMIC, &kit->data_dma);
+       if (!kit->data) {
+               kfree(kit);
+               return -ENOMEM;
+       }
+
+       kit->irq = usb_alloc_urb(0, GFP_KERNEL);
+       if (!kit->irq) {
+               usb_buffer_free(dev, 8, kit->data, kit->data_dma);
+               kfree(kit);
+               return -ENOMEM;
+       }
+
+       kit->udev = usb_get_dev(dev);
+       kit->intf = intf;
+       usb_fill_int_urb(kit->irq, kit->udev, pipe, kit->data,
+                       (maxp > 8 ? 8 : maxp),
+                       interfacekit_irq, kit, endpoint->bInterval);
+       kit->irq->transfer_dma = kit->data_dma;
+       kit->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       usb_set_intfdata(intf, kit);
+
+       if (usb_submit_urb(kit->irq, GFP_KERNEL)) {
+               return -EIO;
+       }
+
+       if (ifkit->outputs == 8) {
+               device_create_file(&intf->dev, &dev_attr_output1);
+               device_create_file(&intf->dev, &dev_attr_output2);
+               device_create_file(&intf->dev, &dev_attr_output3);
+               device_create_file(&intf->dev, &dev_attr_output4);
+               device_create_file(&intf->dev, &dev_attr_output5);
+               device_create_file(&intf->dev, &dev_attr_output6);
+               device_create_file(&intf->dev, &dev_attr_output7);
+               device_create_file(&intf->dev, &dev_attr_output8);
+       } 
+
+       if (ifkit->inputs >= 4) {
+               device_create_file(&intf->dev, &dev_attr_input1);
+               device_create_file(&intf->dev, &dev_attr_input2);
+               device_create_file(&intf->dev, &dev_attr_input3);
+               device_create_file(&intf->dev, &dev_attr_input4);
+       }
+       if (ifkit->inputs == 8) {
+               device_create_file(&intf->dev, &dev_attr_input5);
+               device_create_file(&intf->dev, &dev_attr_input6);
+               device_create_file(&intf->dev, &dev_attr_input7);
+               device_create_file(&intf->dev, &dev_attr_input8);
+       }
+
+       if (ifkit->sensors >= 4) {
+               device_create_file(&intf->dev, &dev_attr_sensor1);
+               device_create_file(&intf->dev, &dev_attr_sensor2);
+               device_create_file(&intf->dev, &dev_attr_sensor3);
+               device_create_file(&intf->dev, &dev_attr_sensor4);
+       }
+       if (ifkit->sensors >= 7) {
+               device_create_file(&intf->dev, &dev_attr_sensor5);
+               device_create_file(&intf->dev, &dev_attr_sensor6);
+               device_create_file(&intf->dev, &dev_attr_sensor7);
+       }
+       if (ifkit->sensors == 8) {
+               device_create_file(&intf->dev, &dev_attr_sensor8);
+       }
+
+       if (ifkit->has_lcd)
+               device_create_file(&intf->dev, &dev_attr_lcd);
+
+       dev_info(&intf->dev, "USB PhidgetInterfaceKit %d/%d/%d attached\n",
+                       ifkit->inputs, ifkit->outputs, ifkit->sensors);
+
+       return 0;
+}
+
+static void interfacekit_disconnect(struct usb_interface *interface)
+{
+       struct phidget_interfacekit *kit;
+
+       kit = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+       if (!kit)
+               return;
+
+       if (kit->ifkit->outputs == MAX_INTERFACES) {
+               device_remove_file(&interface->dev, &dev_attr_output1);
+               device_remove_file(&interface->dev, &dev_attr_output2);
+               device_remove_file(&interface->dev, &dev_attr_output3);
+               device_remove_file(&interface->dev, &dev_attr_output4);
+               device_remove_file(&interface->dev, &dev_attr_output5);
+               device_remove_file(&interface->dev, &dev_attr_output6);
+               device_remove_file(&interface->dev, &dev_attr_output7);
+               device_remove_file(&interface->dev, &dev_attr_output7);
+       }
+
+       if (kit->ifkit->inputs >= 4) {
+               device_remove_file(&interface->dev, &dev_attr_input1);
+               device_remove_file(&interface->dev, &dev_attr_input2);
+               device_remove_file(&interface->dev, &dev_attr_input3);
+               device_remove_file(&interface->dev, &dev_attr_input4);
+       }
+       if (kit->ifkit->inputs == 8) {
+               device_remove_file(&interface->dev, &dev_attr_input5);
+               device_remove_file(&interface->dev, &dev_attr_input6);
+               device_remove_file(&interface->dev, &dev_attr_input7);
+               device_remove_file(&interface->dev, &dev_attr_input8);
+       }
+
+       if (kit->ifkit->sensors >= 4) {
+               device_remove_file(&interface->dev, &dev_attr_sensor1);
+               device_remove_file(&interface->dev, &dev_attr_sensor2);
+               device_remove_file(&interface->dev, &dev_attr_sensor3);
+               device_remove_file(&interface->dev, &dev_attr_sensor4);
+       }
+       if (kit->ifkit->sensors >= 7) {
+               device_remove_file(&interface->dev, &dev_attr_sensor5);
+               device_remove_file(&interface->dev, &dev_attr_sensor6);
+               device_remove_file(&interface->dev, &dev_attr_sensor7);
+       }
+       if (kit->ifkit->sensors == 8) {
+               device_remove_file(&interface->dev, &dev_attr_sensor8);
+       }
+       if (kit->ifkit->has_lcd)
+               device_create_file(&interface->dev, &dev_attr_lcd);
+
+       dev_info(&interface->dev, "USB PhidgetInterfaceKit %d/%d/%d detached\n",
+               kit->ifkit->inputs, kit->ifkit->outputs, kit->ifkit->sensors);
+
+       usb_kill_urb(kit->irq);
+       usb_free_urb(kit->irq);
+       usb_buffer_free(kit->udev, 8, kit->data, kit->data_dma);
+
+       usb_put_dev(kit->udev);
+       kfree(kit);
+}
+
+static struct usb_driver interfacekit_driver = {
+       .owner = THIS_MODULE,
+       .name = "phidgetkit",
+       .probe = interfacekit_probe,
+       .disconnect = interfacekit_disconnect,
+       .id_table = id_table
+};
+
+static int __init interfacekit_init(void)
+{
+       int retval = 0;
+
+       retval = usb_register(&interfacekit_driver);
+       if (retval)
+               err("usb_register failed. Error number %d", retval);
+
+       return retval;
+}
+
+static void __exit interfacekit_exit(void)
+{
+       usb_deregister(&interfacekit_driver);
+}
+
+module_init(interfacekit_init);
+module_exit(interfacekit_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
new file mode 100644 (file)
index 0000000..0a51a11
--- /dev/null
@@ -0,0 +1,1222 @@
+/*
+ * USB Cypress M8 driver
+ *
+ *     Copyright (C) 2004
+ *         Lonnie Mendez (dignome@gmail.com) 
+ *     Copyright (C) 2003,2004
+ *         Neil Whelchel (koyama@firstlight.net)
+ *
+ *     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.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * See http://geocities.com/i0xox0i for information on this driver and the
+ * earthmate usb device.
+ *
+ *
+ *  Lonnie Mendez <dignome@gmail.com>
+ *  04-10-2004
+ *     Driver modified to support dynamic line settings.  Various improvments
+ *      and features.
+ *
+ *  Neil Whelchel
+ *  10-2003
+ *     Driver first released.
+ *
+ *
+ * Long Term TODO:
+ *     Improve transfer speeds - both read/write are somewhat slow
+ *   at this point.
+ */
+
+/* Neil Whelchel wrote the cypress m8 implementation */
+/* Thanks to cypress for providing references for the hid reports. */
+/* Thanks to Jiang Zhang for providing links and for general help. */
+/* Code originates and was built up from ftdi_sio, belkin, pl2303 and others. */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+#include <linux/serial.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+       static int debug = 1;
+#else
+       static int debug;
+#endif
+
+static int stats;
+
+#include "usb-serial.h"
+#include "cypress_m8.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.06"
+#define DRIVER_AUTHOR "Lonnie Mendez <dignome@gmail.com>, Neil Whelchel <koyama@firstlight.net>"
+#define DRIVER_DESC "Cypress USB to Serial Driver"
+
+static struct usb_device_id id_table_earthmate [] = {
+       { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
+       { }                                             /* Terminating entry */
+};
+
+static struct usb_device_id id_table_cyphidcomrs232 [] = {
+       { USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
+       { }                                             /* Terminating entry */
+};
+
+static struct usb_device_id id_table_combined [] = {
+       { USB_DEVICE(VENDOR_ID_DELORME, PRODUCT_ID_EARTHMATEUSB) },
+       { USB_DEVICE(VENDOR_ID_CYPRESS, PRODUCT_ID_CYPHIDCOM) },
+       { }                                             /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, id_table_combined);
+
+static struct usb_driver cypress_driver = {
+       .name =         "cypress",
+       .probe =        usb_serial_probe,
+       .disconnect =   usb_serial_disconnect,
+       .id_table =     id_table_combined,
+};
+
+struct cypress_private {
+       spinlock_t lock;                   /* private lock */
+       int chiptype;                      /* identifier of device, for quirks/etc */
+       int bytes_in;                      /* used for statistics */
+       int bytes_out;                     /* used for statistics */
+       int cmd_count;                     /* used for statistics */
+       int cmd_ctrl;                      /* always set this to 1 before issuing a command */
+       int termios_initialized;
+       __u8 line_control;                 /* holds dtr / rts value */
+       __u8 current_status;               /* received from last read - info on dsr,cts,cd,ri,etc */
+       __u8 current_config;               /* stores the current configuration byte */
+       __u8 rx_flags;                     /* throttling - used from whiteheat/ftdi_sio */
+       int baud_rate;                     /* stores current baud rate in integer form */
+       int cbr_mask;                      /* stores current baud rate in masked form */
+       int isthrottled;                   /* if throttled, discard reads */
+       wait_queue_head_t delta_msr_wait;  /* used for TIOCMIWAIT */
+       char prev_status, diff_status;     /* used for TIOCMIWAIT */
+       /* we pass a pointer to this as the arguement sent to cypress_set_termios old_termios */
+       struct termios tmp_termios;        /* stores the old termios settings */
+       int write_interval;                /* interrupt out write interval, as obtained from interrupt_out_urb */
+       int writepipe;                     /* used for clear halt, if necessary */
+};
+
+/* function prototypes for the Cypress USB to serial device */
+static int  cypress_earthmate_startup  (struct usb_serial *serial);
+static int  cypress_hidcom_startup     (struct usb_serial *serial);
+static void cypress_shutdown           (struct usb_serial *serial);
+static int  cypress_open               (struct usb_serial_port *port, struct file *filp);
+static void cypress_close              (struct usb_serial_port *port, struct file *filp);
+static int  cypress_write              (struct usb_serial_port *port, const unsigned char *buf, int count);
+static int  cypress_write_room         (struct usb_serial_port *port);
+static int  cypress_ioctl              (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
+static void cypress_set_termios                (struct usb_serial_port *port, struct termios * old);
+static int  cypress_tiocmget           (struct usb_serial_port *port, struct file *file);
+static int  cypress_tiocmset           (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear);
+static int  cypress_chars_in_buffer    (struct usb_serial_port *port);
+static void cypress_throttle           (struct usb_serial_port *port);
+static void cypress_unthrottle         (struct usb_serial_port *port);
+static void cypress_read_int_callback  (struct urb *urb, struct pt_regs *regs);
+static void cypress_write_int_callback (struct urb *urb, struct pt_regs *regs);
+static int  mask_to_rate               (unsigned mask);
+static unsigned rate_to_mask           (int rate);
+
+static struct usb_serial_device_type cypress_earthmate_device = {
+       .owner =                        THIS_MODULE,
+       .name =                         "DeLorme Earthmate USB",
+       .short_name =                   "earthmate",
+       .id_table =                     id_table_earthmate,
+       .num_interrupt_in =             1,
+       .num_interrupt_out =            1,
+       .num_bulk_in =                  NUM_DONT_CARE,
+       .num_bulk_out =                 NUM_DONT_CARE,
+       .num_ports =                    1,
+       .attach =                       cypress_earthmate_startup,
+       .shutdown =                     cypress_shutdown,
+       .open =                         cypress_open,
+       .close =                        cypress_close,
+       .write =                        cypress_write,
+       .write_room =                   cypress_write_room,
+       .ioctl =                        cypress_ioctl,
+       .set_termios =                  cypress_set_termios,
+       .tiocmget =                     cypress_tiocmget,
+       .tiocmset =                     cypress_tiocmset,
+       .chars_in_buffer =              cypress_chars_in_buffer,
+       .throttle =                     cypress_throttle,
+       .unthrottle =                   cypress_unthrottle,
+       .read_int_callback =            cypress_read_int_callback,
+       .write_int_callback =           cypress_write_int_callback,
+};
+
+static struct usb_serial_device_type cypress_hidcom_device = {
+       .owner =                        THIS_MODULE,
+       .name =                         "HID->COM RS232 Adapter",
+       .short_name =                   "cyphidcom",
+       .id_table =                     id_table_cyphidcomrs232,
+       .num_interrupt_in =             1,
+       .num_interrupt_out =            1,
+       .num_bulk_in =                  NUM_DONT_CARE,
+       .num_bulk_out =                 NUM_DONT_CARE,
+       .num_ports =                    1,
+       .attach =                       cypress_hidcom_startup,
+       .shutdown =                     cypress_shutdown,
+       .open =                         cypress_open,
+       .close =                        cypress_close,
+       .write =                        cypress_write,
+       .write_room =                   cypress_write_room,
+       .ioctl =                        cypress_ioctl,
+       .set_termios =                  cypress_set_termios,
+       .tiocmget =                     cypress_tiocmget,
+       .tiocmset =                     cypress_tiocmset,
+       .chars_in_buffer =              cypress_chars_in_buffer,
+       .throttle =                     cypress_throttle,
+       .unthrottle =                   cypress_unthrottle,
+       .read_int_callback =            cypress_read_int_callback,
+       .write_int_callback =           cypress_write_int_callback,
+};
+
+
+/*****************************************************************************
+ * Cypress serial helper functions
+ *****************************************************************************/
+
+
+/* This function can either set or retreive the current serial line settings */
+static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_mask, int data_bits, int stop_bits,
+                                  int parity_enable, int parity_type, int reset, int cypress_request_type)
+{
+       int i, n_baud_rate = 0, retval = 0;
+       struct cypress_private *priv;
+       __u8 feature_buffer[5];
+       __u8 config;
+       unsigned long flags;
+
+       dbg("%s", __FUNCTION__);
+       
+       priv = usb_get_serial_port_data(port);
+
+       switch(cypress_request_type) {
+               case CYPRESS_SET_CONFIG:
+
+                       /*
+                        * The general purpose firmware for the Cypress M8 allows for a maximum speed
+                        * of 57600bps (I have no idea whether DeLorme chose to use the general purpose
+                        * firmware or not), if you need to modify this speed setting for your own
+                        * project please add your own chiptype and modify the code likewise.  The
+                        * Cypress HID->COM device will work successfully up to 115200bps.
+                        */
+                       if (baud_mask != priv->cbr_mask) {
+                               dbg("%s - baud rate is changing", __FUNCTION__);
+                               if ( priv->chiptype == CT_EARTHMATE ) {
+                                       /* 300 and 600 baud rates are supported under the generic firmware,
+                                        * but are not used with NMEA and SiRF protocols */
+                                       
+                                       if ( (baud_mask == B300) || (baud_mask == B600) ) {
+                                               err("%s - failed setting baud rate, unsupported speed (default to 4800)",
+                                                   __FUNCTION__);
+                                               n_baud_rate = 4800;
+                                       } else if ( (n_baud_rate = mask_to_rate(baud_mask)) == -1) {
+                                               err("%s - failed setting baud rate, unsupported speed (default to 4800)",
+                                                   __FUNCTION__);
+                                               n_baud_rate = 4800;
+                                       }
+                               } else if (priv->chiptype == CT_CYPHIDCOM) {
+                                       if ( (n_baud_rate = mask_to_rate(baud_mask)) == -1) {
+                                               err("%s - failed setting baud rate, unsupported speed (default to 4800)",
+                                                   __FUNCTION__);
+                                               n_baud_rate = 4800;
+                                       }
+                               } else if (priv->chiptype == CT_GENERIC) {
+                                       if ( (n_baud_rate = mask_to_rate(baud_mask)) == -1) {
+                                               err("%s - failed setting baud rate, unsupported speed (default to 4800)",
+                                                   __FUNCTION__);
+                                               n_baud_rate = 4800;
+                                       }
+                               } else {
+                                       info("%s - please define your chiptype, using 4800bps default", __FUNCTION__);
+                                       n_baud_rate = 4800;
+                               }
+                       } else {  /* baud rate not changing, keep the old */
+                               n_baud_rate = priv->baud_rate;
+                       }
+                       dbg("%s - baud rate is being sent as %d", __FUNCTION__, n_baud_rate);
+
+                       
+                       /*
+                        * This algorithm accredited to Jiang Jay Zhang... thanks for all the help!
+                        */
+                       for (i = 0; i < 4; ++i) {
+                               feature_buffer[i] = ( n_baud_rate >> (i*8) & 0xFF );
+                       }
+
+                       config = 0;                      // reset config byte
+                       config |= data_bits;             // assign data bits in 2 bit space ( max 3 )
+                       /* 1 bit gap */
+                       config |= (stop_bits << 3);      // assign stop bits in 1 bit space
+                       config |= (parity_enable << 4);  // assign parity flag in 1 bit space
+                       config |= (parity_type << 5);    // assign parity type in 1 bit space
+                       /* 1 bit gap */
+                       config |= (reset << 7);          // assign reset at end of byte, 1 bit space
+
+                       feature_buffer[4] = config;
+                               
+                       dbg("%s - device is being sent this feature report:", __FUNCTION__);
+                       dbg("%s - %02X - %02X - %02X - %02X - %02X", __FUNCTION__, feature_buffer[0], feature_buffer[1],
+                           feature_buffer[2], feature_buffer[3], feature_buffer[4]);
+                       
+                       retval = usb_control_msg (port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
+                                                 HID_REQ_SET_REPORT, USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
+                                                 0x0300, 0, feature_buffer, 5, 500);
+
+                       if (retval != 5)
+                               err("%s - failed sending serial line settings - %d", __FUNCTION__, retval);
+                       else {
+                               spin_lock_irqsave(&priv->lock, flags);
+                               priv->baud_rate = n_baud_rate;
+                               priv->cbr_mask = baud_mask;
+                               priv->current_config = config;
+                               ++priv->cmd_count;
+                               spin_unlock_irqrestore(&priv->lock, flags);
+                       }
+               break;
+               case CYPRESS_GET_CONFIG:
+                       dbg("%s - retreiving serial line settings", __FUNCTION__);
+                       /* reset values in feature buffer */
+                       memset(feature_buffer, 0, 5);
+
+                       retval = usb_control_msg (port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0),
+                                                 HID_REQ_GET_REPORT, USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
+                                                 0x0300, 0, feature_buffer, 5, 500);
+                       if (retval != 5) {
+                               err("%s - failed to retreive serial line settings - %d", __FUNCTION__, retval);
+                               return retval;
+                       } else {
+                               spin_lock_irqsave(&priv->lock, flags);
+                               /* store the config in one byte, and later use bit masks to check values */
+                               priv->current_config = feature_buffer[4];
+                               /* reverse the process above to get the baud_mask value */
+                               n_baud_rate = 0; // reset bits
+                               for (i = 0; i < 4; ++i) {
+                                       n_baud_rate |= ( feature_buffer[i] << (i*8) );
+                               }
+                               
+                               priv->baud_rate = n_baud_rate;
+                               if ( (priv->cbr_mask = rate_to_mask(n_baud_rate)) == 0x40)
+                                       dbg("%s - failed setting the baud mask (not defined)", __FUNCTION__);
+                               ++priv->cmd_count;
+                               spin_unlock_irqrestore(&priv->lock, flags);
+                       }
+                       break;
+               default:
+                       err("%s - unsupported serial control command issued", __FUNCTION__);
+       }
+       return retval;
+} /* cypress_serial_control */
+
+
+/* given a baud mask, it will return speed on success */
+static int mask_to_rate (unsigned mask)
+{
+       int rate;
+
+       switch (mask) {
+               case B0: rate = 0; break;
+               case B300: rate = 300; break;
+               case B600: rate = 600; break;
+               case B1200: rate = 1200; break;
+               case B2400: rate = 2400; break;
+               case B4800: rate = 4800; break;
+               case B9600: rate = 9600; break;
+               case B19200: rate = 19200; break;
+               case B38400: rate = 38400; break;
+               case B57600: rate = 57600; break;
+               case B115200: rate = 115200; break;
+               default: rate = -1;
+       }
+
+       return rate;
+}
+
+
+static unsigned rate_to_mask (int rate)
+{
+       unsigned mask;
+
+       switch (rate) {
+               case 0: mask = B0; break;
+               case 300: mask = B300; break;
+               case 600: mask = B600; break;
+               case 1200: mask = B1200; break;
+               case 2400: mask = B2400; break;
+               case 4800: mask = B4800; break;
+               case 9600: mask = B9600; break;
+               case 19200: mask = B19200; break;
+               case 38400: mask = B38400; break;
+               case 57600: mask = B57600; break;
+               case 115200: mask = B115200; break;
+               default: mask = 0x40;
+       }
+
+       return mask;
+}
+/*****************************************************************************
+ * Cypress serial driver functions
+ *****************************************************************************/
+
+
+static int generic_startup (struct usb_serial *serial)
+{
+       struct cypress_private *priv;
+
+       dbg("%s - port %d", __FUNCTION__, serial->port[0]->number);
+
+       priv = kmalloc(sizeof (struct cypress_private), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       memset(priv, 0x00, sizeof (struct cypress_private));
+       spin_lock_init(&priv->lock);
+       init_waitqueue_head(&priv->delta_msr_wait);
+       priv->writepipe = serial->port[0]->interrupt_out_urb->pipe;
+       
+       /* free up interrupt_out buffer / urb allocated by usbserial
+        * for this port as we use our own urbs for writing */
+       if (serial->port[0]->interrupt_out_buffer) {
+               kfree(serial->port[0]->interrupt_out_buffer);
+               serial->port[0]->interrupt_out_buffer = NULL;
+       }
+       if (serial->port[0]->interrupt_out_urb) {
+               priv->write_interval = serial->port[0]->interrupt_out_urb->interval;
+               usb_free_urb(serial->port[0]->interrupt_out_urb);
+               serial->port[0]->interrupt_out_urb = NULL;
+       } else /* still need a write interval */
+               priv->write_interval = 10;
+
+       priv->cmd_ctrl = 0;
+       priv->line_control = 0;
+       priv->termios_initialized = 0;
+       priv->rx_flags = 0;
+       usb_set_serial_port_data(serial->port[0], priv);
+       
+       return (0);     
+}      
+
+
+static int cypress_earthmate_startup (struct usb_serial *serial)
+{
+       struct cypress_private *priv;
+
+       dbg("%s", __FUNCTION__);
+
+       if (generic_startup(serial)) {
+               dbg("%s - Failed setting up port %d", __FUNCTION__, serial->port[0]->number);
+               return 1;
+       }
+
+       priv = usb_get_serial_port_data(serial->port[0]);
+       priv->chiptype = CT_EARTHMATE;
+       
+       return (0);     
+} /* cypress_earthmate_startup */
+
+
+static int cypress_hidcom_startup (struct usb_serial *serial)
+{
+       struct cypress_private *priv;
+
+       dbg("%s", __FUNCTION__);
+
+       if (generic_startup(serial)) {
+               dbg("%s - Failed setting up port %d", __FUNCTION__, serial->port[0]->number);
+               return 1;
+       }
+
+       priv = usb_get_serial_port_data(serial->port[0]);
+       priv->chiptype = CT_CYPHIDCOM;
+       
+       return (0);     
+} /* cypress_hidcom_startup */
+
+
+static void cypress_shutdown (struct usb_serial *serial)
+{
+       struct cypress_private *priv;
+
+       dbg ("%s - port %d", __FUNCTION__, serial->port[0]->number);
+
+       /* all open ports are closed at this point */
+
+       priv = usb_get_serial_port_data(serial->port[0]);
+
+       if (priv) {
+               kfree(priv);
+               usb_set_serial_port_data(serial->port[0], NULL);
+       }
+}
+
+
+static int cypress_open (struct usb_serial_port *port, struct file *filp)
+{
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+       struct usb_serial *serial = port->serial;
+       unsigned long flags;
+       int result = 0;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       /* reset read/write statistics */
+       priv->bytes_in = 0;
+       priv->bytes_out = 0;
+       priv->cmd_count = 0;
+
+       /* turn on dtr / rts since we are not flow controlling by default */
+       priv->line_control = CONTROL_DTR | CONTROL_RTS; /* sent in status byte */
+       spin_unlock_irqrestore(&priv->lock, flags);
+       priv->cmd_ctrl = 1;
+       result = cypress_write(port, NULL, 0);
+       
+       port->tty->low_latency = 1;
+
+       /* termios defaults are set by usb_serial_init */
+       
+       cypress_set_termios(port, &priv->tmp_termios);
+
+       if (result) {
+               dev_err(&port->dev, "%s - failed setting the control lines - error %d\n", __FUNCTION__, result);
+               return result;
+       } else
+               dbg("%s - success setting the control lines", __FUNCTION__);
+
+       /* throttling off */
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->rx_flags = 0;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* setup the port and
+        * start reading from the device */
+       if(!port->interrupt_in_urb){
+               err("%s - interrupt_in_urb is empty!", __FUNCTION__);
+               return(-1);
+       }
+
+       usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
+               usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
+               port->interrupt_in_urb->transfer_buffer, port->interrupt_in_urb->transfer_buffer_length,
+               cypress_read_int_callback, port, port->interrupt_in_urb->interval);
+       result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+
+       if (result){
+               dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
+       }
+
+       return result;
+} /* cypress_open */
+
+
+static void cypress_close(struct usb_serial_port *port, struct file * filp)
+{
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+       unsigned int c_cflag;
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (port->tty) {
+               c_cflag = port->tty->termios->c_cflag;
+               if (c_cflag & HUPCL) {
+                       /* drop dtr and rts */
+                       priv = usb_get_serial_port_data(port);
+                       spin_lock_irqsave(&priv->lock, flags);
+                       priv->line_control = 0;
+                       priv->cmd_ctrl = 1;
+                       spin_unlock_irqrestore(&priv->lock, flags);
+                       cypress_write(port, NULL, 0);
+               }
+       }
+
+       if (port->interrupt_in_urb) {
+               dbg("%s - stopping read urb", __FUNCTION__);
+               usb_kill_urb (port->interrupt_in_urb);
+       }
+
+       if (stats)
+               dev_info (&port->dev, "Statistics: %d Bytes In | %d Bytes Out | %d Commands Issued\n",
+                         priv->bytes_in, priv->bytes_out, priv->cmd_count);
+} /* cypress_close */
+
+
+static int cypress_write(struct usb_serial_port *port, const unsigned char *buf, int count)
+{
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+       struct urb *urb;
+       int status, s_pos = 0;
+       __u8 transfer_size = 0;
+       __u8 *buffer;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (count == 0 && !priv->cmd_ctrl) {
+               spin_unlock_irqrestore(&priv->lock, flags);
+               dbg("%s - write request of 0 bytes", __FUNCTION__);
+               return 0;
+       }
+
+       if (priv->cmd_ctrl)
+               ++priv->cmd_count;
+       priv->cmd_ctrl = 0;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       dbg("%s - interrupt out size is %d", __FUNCTION__, port->interrupt_out_size);
+       dbg("%s - count is %d", __FUNCTION__, count);
+
+       /* Allocate buffer and urb */
+       buffer = kmalloc (port->interrupt_out_size, GFP_ATOMIC);
+       if (!buffer) {
+               dev_err(&port->dev, "ran out of memory for buffer\n");
+               return -ENOMEM;
+       }
+
+       urb = usb_alloc_urb (0, GFP_ATOMIC);
+       if (!urb) {
+               dev_err(&port->dev, "failed allocating write urb\n");
+               kfree (buffer);
+               return -ENOMEM;
+       }
+
+       memset(buffer, 0, port->interrupt_out_size); // test if this is needed... probably not since loop removed
+
+       spin_lock_irqsave(&priv->lock, flags);
+       switch (port->interrupt_out_size) {
+               case 32:
+                       // this is for the CY7C64013...
+                       transfer_size = min (count, 30);
+                       buffer[0] = priv->line_control;
+                       buffer[1] = transfer_size;
+                       s_pos = 2;
+                       break;
+               case 8:
+                       // this is for the CY7C63743...
+                       transfer_size = min (count, 7);
+                       buffer[0] = priv->line_control | transfer_size;
+                       s_pos = 1;
+                       break;
+               default:
+                       dbg("%s - wrong packet size", __FUNCTION__);
+                       spin_unlock_irqrestore(&priv->lock, flags);
+                       kfree (buffer);
+                       usb_free_urb (urb);
+                       return -1;
+       }
+
+       if (priv->line_control & CONTROL_RESET)
+               priv->line_control &= ~CONTROL_RESET;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* copy data to offset position in urb transfer buffer */
+       memcpy (&buffer[s_pos], buf, transfer_size);
+
+       usb_serial_debug_data (debug, &port->dev, __FUNCTION__, port->interrupt_out_size, buffer);
+
+       /* build up the urb */
+       usb_fill_int_urb (urb, port->serial->dev,
+                         usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
+                         buffer, port->interrupt_out_size,
+                         cypress_write_int_callback, port, priv->write_interval);
+
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+
+       if (status) {
+               dev_err(&port->dev, "%s - usb_submit_urb (write interrupt) failed with status %d\n",
+                       __FUNCTION__, status);
+               transfer_size = status;
+               kfree (buffer);
+               goto exit;
+       }
+
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->bytes_out += transfer_size;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+exit:
+       /* buffer free'd in callback */
+       usb_free_urb (urb);
+
+       return transfer_size;
+
+} /* cypress_write */
+
+
+static int cypress_write_room(struct usb_serial_port *port)
+{
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /*
+        * We really can take anything the user throw at us
+        * but let's pick a nice big number to tell the tty
+        * layer that we have lots of free space
+        */     
+
+       return 2048;
+}
+
+
+static int cypress_tiocmget (struct usb_serial_port *port, struct file *file)
+{
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+       __u8 status, control;
+       unsigned int result = 0;
+       unsigned long flags;
+       
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       control = priv->line_control;
+       status = priv->current_status;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       result = ((control & CONTROL_DTR)        ? TIOCM_DTR : 0)
+               | ((control & CONTROL_RTS)       ? TIOCM_RTS : 0)
+               | ((status & UART_CTS)        ? TIOCM_CTS : 0)
+               | ((status & UART_DSR)        ? TIOCM_DSR : 0)
+               | ((status & UART_RI)         ? TIOCM_RI  : 0)
+               | ((status & UART_CD)         ? TIOCM_CD  : 0);
+
+       dbg("%s - result = %x", __FUNCTION__, result);
+
+       return result;
+}
+
+
+static int cypress_tiocmset (struct usb_serial_port *port, struct file *file,
+                              unsigned int set, unsigned int clear)
+{
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+       
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (set & TIOCM_RTS)
+               priv->line_control |= CONTROL_RTS;
+       if (set & TIOCM_DTR)
+               priv->line_control |= CONTROL_DTR;
+       if (clear & TIOCM_RTS)
+               priv->line_control &= ~CONTROL_RTS;
+       if (clear & TIOCM_DTR)
+               priv->line_control &= ~CONTROL_DTR;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       priv->cmd_ctrl = 1;
+       return cypress_write(port, NULL, 0);
+}
+
+
+static int cypress_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
+{
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+
+       dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
+
+       switch (cmd) {
+               case TIOCGSERIAL:
+                       if (copy_to_user((void __user *)arg, port->tty->termios, sizeof(struct termios))) {
+                               return -EFAULT;
+                       }
+                       return (0);
+                       break;
+               case TIOCSSERIAL:
+                       if (copy_from_user(port->tty->termios, (void __user *)arg, sizeof(struct termios))) {
+                               return -EFAULT;
+                       }
+                       /* here we need to call cypress_set_termios to invoke the new settings */
+                       cypress_set_termios(port, &priv->tmp_termios);
+                       return (0);
+                       break;
+               /* these are called when setting baud rate from gpsd */
+               case TCGETS:
+                       if (copy_to_user((void __user *)arg, port->tty->termios, sizeof(struct termios))) {
+                               return -EFAULT;
+                       }
+                       return (0);
+                       break;
+               case TCSETS:
+                       if (copy_from_user(port->tty->termios, (void __user *)arg, sizeof(struct termios))) {
+                               return -EFAULT;
+                       }
+                       /* here we need to call cypress_set_termios to invoke the new settings */
+                       cypress_set_termios(port, &priv->tmp_termios);
+                       return (0);
+                       break;
+               /* This code comes from drivers/char/serial.c and ftdi_sio.c */
+               case TIOCMIWAIT:
+                       while (priv != NULL) {
+                               interruptible_sleep_on(&priv->delta_msr_wait);
+                               /* see if a signal did it */
+                               if (signal_pending(current))
+                                       return -ERESTARTSYS;
+                               else {
+                                       char diff = priv->diff_status;
+
+                                       if (diff == 0) {
+                                               return -EIO; /* no change => error */
+                                       }
+                                       
+                                       /* consume all events */
+                                       priv->diff_status = 0;
+
+                                       /* return 0 if caller wanted to know about these bits */
+                                       if ( ((arg & TIOCM_RNG) && (diff & UART_RI)) ||
+                                            ((arg & TIOCM_DSR) && (diff & UART_DSR)) ||
+                                            ((arg & TIOCM_CD) && (diff & UART_CD)) ||
+                                            ((arg & TIOCM_CTS) && (diff & UART_CTS)) ) {
+                                               return 0;
+                                       }
+                                       /* otherwise caller can't care less about what happened,
+                                        * and so we continue to wait for more events.
+                                        */
+                               }
+                       }
+                       return 0;
+                       break;
+               default:
+                       break;
+       }
+
+       dbg("%s - arg not supported - it was 0x%04x - check include/asm/ioctls.h", __FUNCTION__, cmd);
+
+       return -ENOIOCTLCMD;
+} /* cypress_ioctl */
+
+
+static void cypress_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+{
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+       struct tty_struct *tty;
+       int data_bits, stop_bits, parity_type, parity_enable;
+       unsigned cflag, iflag, baud_mask;
+       unsigned long flags;
+       
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       tty = port->tty;
+       if ((!tty) || (!tty->termios)) {
+               dbg("%s - no tty structures", __FUNCTION__);
+               return;
+       }
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (!priv->termios_initialized) {
+               if (priv->chiptype == CT_EARTHMATE) {
+                       *(tty->termios) = tty_std_termios;
+                       tty->termios->c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL;
+               } else if (priv->chiptype == CT_CYPHIDCOM) {
+                       *(tty->termios) = tty_std_termios;
+                       tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+               }
+               priv->termios_initialized = 1;
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       cflag = tty->termios->c_cflag;
+       iflag = tty->termios->c_iflag;
+
+       /* check if there are new settings */
+       if (old_termios) {
+               if ((cflag != old_termios->c_cflag) ||
+                   (RELEVANT_IFLAG(iflag) != RELEVANT_IFLAG(old_termios->c_iflag))) {
+                       dbg("%s - attempting to set new termios settings", __FUNCTION__);
+                       /* should make a copy of this in case something goes wrong in the function, we can restore it */
+                       spin_lock_irqsave(&priv->lock, flags);
+                       priv->tmp_termios = *(tty->termios);
+                       spin_unlock_irqrestore(&priv->lock, flags); 
+               } else {
+                       dbg("%s - nothing to do, exiting", __FUNCTION__);
+                       return;
+               }
+       } else
+               return;
+
+       /* set number of data bits, parity, stop bits */
+       /* when parity is disabled the parity type bit is ignored */
+
+       stop_bits = cflag & CSTOPB ? 1 : 0; /* 1 means 2 stop bits, 0 means 1 stop bit */
+       
+       if (cflag & PARENB) {
+               parity_enable = 1;
+               parity_type = cflag & PARODD ? 1 : 0; /* 1 means odd parity, 0 means even parity */
+       } else
+               parity_enable = parity_type = 0;
+
+       if (cflag & CSIZE) {
+               switch (cflag & CSIZE) {
+                       case CS5: data_bits = 0; break;
+                       case CS6: data_bits = 1; break;
+                       case CS7: data_bits = 2; break;
+                       case CS8: data_bits = 3; break;
+                       default: err("%s - CSIZE was set, but not CS5-CS8", __FUNCTION__); data_bits = 3;
+               }
+       } else
+               data_bits = 3;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if ((cflag & CBAUD) == B0) {
+               /* drop dtr and rts */
+               dbg("%s - dropping the lines, baud rate 0bps", __FUNCTION__);
+               baud_mask = B0;
+               priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
+       } else {
+               baud_mask = (cflag & CBAUD);
+               switch(baud_mask) {
+                       case B300: dbg("%s - setting baud 300bps", __FUNCTION__); break;
+                       case B600: dbg("%s - setting baud 600bps", __FUNCTION__); break;
+                       case B1200: dbg("%s - setting baud 1200bps", __FUNCTION__); break;
+                       case B2400: dbg("%s - setting baud 2400bps", __FUNCTION__); break;
+                       case B4800: dbg("%s - setting baud 4800bps", __FUNCTION__); break;
+                       case B9600: dbg("%s - setting baud 9600bps", __FUNCTION__); break;
+                       case B19200: dbg("%s - setting baud 19200bps", __FUNCTION__); break;
+                       case B38400: dbg("%s - setting baud 38400bps", __FUNCTION__); break;
+                       case B57600: dbg("%s - setting baud 57600bps", __FUNCTION__); break;
+                       case B115200: dbg("%s - setting baud 115200bps", __FUNCTION__); break;
+                       default: dbg("%s - unknown masked baud rate", __FUNCTION__);
+               }
+               priv->line_control |= CONTROL_DTR;
+               
+               /* this is probably not what I think it is... check into it */
+               if (cflag & CRTSCTS)
+                       priv->line_control |= CONTROL_RTS;
+               else
+                       priv->line_control &= ~CONTROL_RTS;
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+       
+       dbg("%s - sending %d stop_bits, %d parity_enable, %d parity_type, %d data_bits (+5)", __FUNCTION__,
+           stop_bits, parity_enable, parity_type, data_bits);
+
+       cypress_serial_control(port, baud_mask, data_bits, stop_bits, parity_enable,
+                              parity_type, 0, CYPRESS_SET_CONFIG);
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(50*HZ/1000); /* give some time between change and read (50ms) */ 
+
+       /* we perform a CYPRESS_GET_CONFIG so that the current settings are filled into the private structure
+         * this should confirm that all is working if it returns what we just set */
+       cypress_serial_control(port, 0, 0, 0, 0, 0, 0, CYPRESS_GET_CONFIG);
+
+       /* Here we can define custom tty settings for devices
+         *
+         * the main tty base comes from empeg.c
+         */
+
+       spin_lock_irqsave(&priv->lock, flags);  
+       if ( (priv->chiptype == CT_EARTHMATE) && (priv->baud_rate == 4800) ) {
+
+               dbg("Using custom termios settings for a baud rate of 4800bps.");
+               /* define custom termios settings for NMEA protocol */
+
+               
+               tty->termios->c_iflag /* input modes - */
+                       &= ~(IGNBRK             /* disable ignore break */
+                       | BRKINT                /* disable break causes interrupt */
+                       | PARMRK                /* disable mark parity errors */
+                       | ISTRIP                /* disable clear high bit of input characters */
+                       | INLCR                 /* disable translate NL to CR */
+                       | IGNCR                 /* disable ignore CR */
+                       | ICRNL                 /* disable translate CR to NL */
+                       | IXON);                /* disable enable XON/XOFF flow control */
+               
+               tty->termios->c_oflag /* output modes */
+                       &= ~OPOST;              /* disable postprocess output characters */
+               
+               tty->termios->c_lflag /* line discipline modes */
+                       &= ~(ECHO               /* disable echo input characters */
+                       | ECHONL                /* disable echo new line */
+                       | ICANON                /* disable erase, kill, werase, and rprnt special characters */
+                       | ISIG                  /* disable interrupt, quit, and suspend special characters */
+                       | IEXTEN);              /* disable non-POSIX special characters */
+
+       } else if (priv->chiptype == CT_CYPHIDCOM) {
+
+               // Software app handling it for device...       
+
+       } else {
+               
+               /* do something here */
+
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* set lines */
+       priv->cmd_ctrl = 1;
+       cypress_write(port, NULL, 0);
+       
+       return;
+} /* cypress_set_termios */
+
+
+static int cypress_chars_in_buffer(struct usb_serial_port *port)
+{
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /*
+        * We can't really account for how much data we
+        * have sent out, but hasn't made it through to the
+        * device, so just tell the tty layer that everything
+        * is flushed.
+        */
+
+       return 0;
+}
+
+
+static void cypress_throttle (struct usb_serial_port *port)
+{
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->rx_flags = THROTTLED;
+       spin_unlock_irqrestore(&priv->lock, flags);        
+}
+
+
+static void cypress_unthrottle (struct usb_serial_port *port)
+{
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+       int actually_throttled, result;
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
+       priv->rx_flags = 0;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       if (actually_throttled) {
+               port->interrupt_in_urb->dev = port->serial->dev;
+
+               result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+               if (result)
+                       dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
+       }
+}
+
+
+static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+       struct cypress_private *priv = usb_get_serial_port_data(port);
+       struct tty_struct *tty;
+       unsigned char *data = urb->transfer_buffer;
+       unsigned long flags;
+       char tty_flag = TTY_NORMAL;
+       int bytes=0;
+       int result;
+       int i=0;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (urb->status) {
+               dbg("%s - nonzero read status received: %d", __FUNCTION__, urb->status);
+               return;
+       }
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (priv->rx_flags & THROTTLED) {
+               priv->rx_flags |= ACTUALLY_THROTTLED;
+               spin_unlock_irqrestore(&priv->lock, flags);
+               return;
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       tty = port->tty;
+       if (!tty) {
+               dbg("%s - bad tty pointer - exiting", __FUNCTION__);
+               return;
+       }
+
+       usb_serial_debug_data (debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+       
+       spin_lock_irqsave(&priv->lock, flags);
+       switch(urb->actual_length) {
+               case 32:
+                       // This is for the CY7C64013...
+                       priv->current_status = data[0] & 0xF8;
+                       bytes = data[1]+2;
+                       i=2;
+                       break;
+               case 8:
+                       // This is for the CY7C63743...
+                       priv->current_status = data[0] & 0xF8;
+                       bytes = (data[0] & 0x07)+1;
+                       i=1;
+                       break;
+               default:
+                       dbg("%s - wrong packet size - received %d bytes", __FUNCTION__, urb->actual_length);
+                       spin_unlock_irqrestore(&priv->lock, flags);
+                       goto continue_read;
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       /* check to see if status has changed */
+       if (priv != NULL) {
+               if (priv->current_status != priv->prev_status) {
+                       priv->diff_status |= priv->current_status ^ priv->prev_status;
+                       wake_up_interruptible(&priv->delta_msr_wait);
+                       priv->prev_status = priv->current_status;
+               }
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);     
+
+       /* hangup, as defined in acm.c... this might be a bad place for it though */
+       if (tty && !(tty->termios->c_cflag & CLOCAL) && !(priv->current_status & UART_CD)) {
+               dbg("%s - calling hangup", __FUNCTION__);
+               tty_hangup(tty);
+               goto continue_read;
+       }
+
+       /* There is one error bit... I'm assuming it is a parity error indicator
+        * as the generic firmware will set this bit to 1 if a parity error occurs.
+        * I can not find reference to any other error events.
+        *
+        */
+       spin_lock_irqsave(&priv->lock, flags);
+       if (priv->current_status & CYP_ERROR) {
+               spin_unlock_irqrestore(&priv->lock, flags);
+               tty_flag = TTY_PARITY;
+               dbg("%s - Parity Error detected", __FUNCTION__);
+       } else
+               spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* process read if there is data other than line status */
+       if (tty && (bytes > i)) {
+               for (; i < bytes ; ++i) {
+                       dbg("pushing byte number %d - %d",i,data[i]);
+                       if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                               tty_flip_buffer_push(tty);
+                       }
+                       tty_insert_flip_char(tty, data[i], tty_flag);
+               }
+               tty_flip_buffer_push(port->tty);
+       }
+
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->bytes_in += bytes;  /* control and status byte(s) are also counted */
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+continue_read:
+       
+       /* Continue trying to always read... unless the port has closed.  */
+
+       if (port->open_count > 0) {
+       usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
+               usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress),
+               port->interrupt_in_urb->transfer_buffer,
+               port->interrupt_in_urb->transfer_buffer_length,
+               cypress_read_int_callback, port,
+               port->interrupt_in_urb->interval);
+       result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
+       if (result)
+               dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+       }
+       
+       return;
+} /* cypress_read_int_callback */
+
+
+static void cypress_write_int_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+
+       /* free up the transfer buffer, as usb_free_urb() does not do this */
+       kfree (urb->transfer_buffer);
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+       
+       if (urb->status) {
+               dbg("%s - nonzero write status received: %d", __FUNCTION__, urb->status);
+               return;
+       }
+
+       schedule_work(&port->work);
+}
+
+
+/*****************************************************************************
+ * Module functions
+ *****************************************************************************/
+
+static int __init cypress_init(void)
+{
+       int retval;
+       
+       dbg("%s", __FUNCTION__);
+       
+       retval = usb_serial_register(&cypress_earthmate_device);
+       if (retval)
+               goto failed_em_register;
+       retval = usb_serial_register(&cypress_hidcom_device);
+       if (retval)
+               goto failed_hidcom_register;
+       retval = usb_register(&cypress_driver);
+       if (retval)
+               goto failed_usb_register;
+
+       info(DRIVER_DESC " " DRIVER_VERSION);
+       return 0;
+failed_usb_register:
+       usb_deregister(&cypress_driver);
+failed_hidcom_register:
+       usb_serial_deregister(&cypress_hidcom_device);
+failed_em_register:
+       usb_serial_deregister(&cypress_earthmate_device);
+
+       return retval;
+}
+
+
+static void __exit cypress_exit (void)
+{
+       dbg("%s", __FUNCTION__);
+
+       usb_deregister (&cypress_driver);
+       usb_serial_deregister (&cypress_earthmate_device);
+       usb_serial_deregister (&cypress_hidcom_device);
+}
+
+
+module_init(cypress_init);
+module_exit(cypress_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+module_param(stats, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(stats, "Enable statistics or not");
diff --git a/drivers/usb/serial/cypress_m8.h b/drivers/usb/serial/cypress_m8.h
new file mode 100644 (file)
index 0000000..1012ee6
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef CYPRESS_M8_H
+#define CYPRESS_M8_H
+
+/* definitions and function prototypes used for the cypress USB to Serial controller */
+
+/* For sending our feature buffer - controlling serial communication states */
+/* Linux HID has no support for serial devices so we do this through the driver */
+#define HID_REQ_GET_REPORT 0x01
+#define HID_REQ_SET_REPORT 0x09
+
+/* List other cypress USB to Serial devices here, and add them to the id_table */
+
+/* DeLorme Earthmate USB - a GPS device */
+#define        VENDOR_ID_DELORME                0x1163
+#define PRODUCT_ID_EARTHMATEUSB                 0x0100
+
+/* Cypress HID->COM RS232 Adapter */
+#define VENDOR_ID_CYPRESS               0x04b4
+#define PRODUCT_ID_CYPHIDCOM            0x5500
+/* End of device listing */
+
+/* Used for setting / requesting serial line settings */
+#define CYPRESS_SET_CONFIG 0x01
+#define CYPRESS_GET_CONFIG 0x02
+
+/* Used for throttle control */
+#define THROTTLED 0x1
+#define ACTUALLY_THROTTLED 0x2
+
+/* chiptypes - used in case firmware differs from the generic form ... offering
+ *     different baud speeds/etc.
+ */
+
+#define CT_EARTHMATE   0x01
+#define CT_CYPHIDCOM   0x02
+#define CT_GENERIC     0x0F
+/* End of chiptype definitions */
+
+/* RS-232 serial data communication protocol definitions */
+/* these are sent / read at byte 0 of the input/output hid reports */
+/* You can find these values defined in the CY4601 USB to Serial design notes */
+
+#define CONTROL_DTR    0x20    /* data terminal ready - flow control - host to device */
+#define UART_DSR       0x20    /* data set ready - flow control - device to host */
+#define CONTROL_RTS    0x10    /* request to send - flow control - host to device */
+#define UART_CTS       0x10    /* clear to send - flow control - device to host */
+#define        UART_RI         0x10    /* ring indicator - modem - device to host */ 
+#define UART_CD                0x40    /* carrier detect - modem - device to host */
+#define CYP_ERROR      0x08    /* received from input report - device to host */
+/* Note - the below has nothing to to with the "feature report" reset */
+#define CONTROL_RESET  0x08    /* sent with output report - host to device */
+
+/* End of RS-232 protocol definitions */
+
+#endif /* CYPRESS_M8_H */
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
new file mode 100644 (file)
index 0000000..1655085
--- /dev/null
@@ -0,0 +1,1541 @@
+/*
+ * Garmin GPS driver
+ *
+ * Copyright (C) 2004 Hermann Kneissel herkne@users.sourceforge.net
+ *
+ * The latest version of the driver can be found at
+ * http://sourceforge.net/projects/garmin-gps/
+ *
+ * This driver has been derived from v2.1 of the visor driver.
+ *
+ * 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 USA
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+/* the mode to be set when the port ist opened */
+static int initial_mode = 1;
+
+/* debug flag */
+static int debug = 0;
+
+#include "usb-serial.h"
+
+#define GARMIN_VENDOR_ID             0x091E
+
+/*
+ * Version Information
+ */
+
+#define VERSION_MAJOR  0
+#define VERSION_MINOR  23
+
+#define _STR(s) #s
+#define _DRIVER_VERSION(a,b) "v" _STR(a) "." _STR(b)
+#define DRIVER_VERSION _DRIVER_VERSION(VERSION_MAJOR, VERSION_MINOR)
+#define DRIVER_AUTHOR "hermann kneissel"
+#define DRIVER_DESC "garmin gps driver"
+
+/* error codes returned by the driver */
+#define EINVPKT        1000    /* invalid packet structure */
+
+
+// size of the header of a packet using the usb protocol
+#define GARMIN_PKTHDR_LENGTH   12
+
+// max. possible size of a packet using the serial protocol 
+#define MAX_SERIAL_PKT_SIZ (3+255+3)
+
+// max. possible size of a packet with worst case stuffing
+#define MAX_SERIAL_PKT_SIZ_STUFFED MAX_SERIAL_PKT_SIZ+256
+
+// size of a buffer able to hold a complete (no stuffing) packet
+// (the document protocol does not contain packets with a larger
+//  size, but in theory a packet may be 64k+12 bytes - if in
+//  later protocol versions larger packet sizes occur, this value
+//  should be increased accordingly, so the input buffer is always 
+//  large enough the store a complete packet inclusive header)
+#define GPS_IN_BUFSIZ  (GARMIN_PKTHDR_LENGTH+MAX_SERIAL_PKT_SIZ) 
+
+// size of a buffer able to hold a complete (incl. stuffing) packet
+#define GPS_OUT_BUFSIZ (GARMIN_PKTHDR_LENGTH+MAX_SERIAL_PKT_SIZ_STUFFED) 
+
+// where to place the packet id of a serial packet, so we can
+// prepend the usb-packet header without the need to move the
+// packets data
+#define GSP_INITIAL_OFFSET (GARMIN_PKTHDR_LENGTH-2)
+
+// max. size of incoming private packets (header+1 param)
+#define PRIVPKTSIZ (GARMIN_PKTHDR_LENGTH+4)
+
+#define GARMIN_LAYERID_TRANSPORT  0
+#define GARMIN_LAYERID_APPL      20
+// our own layer-id to use for some control mechanisms
+#define GARMIN_LAYERID_PRIVATE 0x01106E4B
+
+#define GARMIN_PKTID_PVT_DATA  51
+#define GARMIN_PKTID_L001_COMMAND_DATA 10
+
+#define CMND_ABORT_TRANSFER 0
+
+// packet ids used in private layer
+#define PRIV_PKTID_SET_DEBUG   1
+#define PRIV_PKTID_SET_MODE    2
+#define PRIV_PKTID_INFO_REQ    3
+#define PRIV_PKTID_INFO_RESP   4
+#define PRIV_PKTID_RESET_REQ   5
+#define PRIV_PKTID_SET_DEF_MODE        6
+
+
+#define ETX    0x03
+#define DLE    0x10
+#define ACK    0x06
+#define NAK    0x15
+
+/* structure used to queue incoming packets */
+struct garmin_packet {
+       struct list_head  list;
+       int               seq;
+       int               size; // the real size of the data array, always > 0
+       __u8              data[1];
+};
+
+/* structure used to keep the current state of the driver */
+struct garmin_data {
+       __u8   state;
+       __u16  flags;
+       __u8   mode;
+       __u8   ignorePkts;
+       __u8   count;
+       __u8   pkt_id;
+       __u32  serial_num;
+       struct timer_list timer;
+       struct usb_serial_port *port;
+       int    seq_counter;
+       int    insize;
+       int    outsize;
+       __u8   inbuffer [GPS_IN_BUFSIZ];  /* tty -> usb */
+       __u8   outbuffer[GPS_OUT_BUFSIZ]; /* usb -> tty */
+       __u8   privpkt[4*6];
+       spinlock_t lock;
+       struct list_head pktlist;
+};
+
+
+#define STATE_NEW            0
+#define STATE_INITIAL_DELAY  1
+#define STATE_TIMEOUT        2
+#define STATE_SESSION_REQ1   3
+#define STATE_SESSION_REQ2   4
+#define STATE_ACTIVE         5
+
+#define STATE_RESET         8
+#define STATE_DISCONNECTED   9
+#define STATE_WAIT_TTY_ACK  10
+#define STATE_GSP_WAIT_DATA 11
+
+#define MODE_NATIVE          0
+#define MODE_GARMIN_SERIAL   1
+
+// Flags used in garmin_data.flags:
+#define FLAGS_SESSION_REPLY_MASK  0x00C0
+#define FLAGS_SESSION_REPLY1_SEEN 0x0080
+#define FLAGS_SESSION_REPLY2_SEEN 0x0040
+#define FLAGS_BULK_IN_ACTIVE      0x0020
+#define FLAGS_THROTTLED           0x0010
+#define CLEAR_HALT_REQUIRED       0x0001
+
+#define FLAGS_QUEUING             0x0100
+#define FLAGS_APP_RESP_SEEN       0x0200
+#define FLAGS_APP_REQ_SEEN        0x0400
+#define FLAGS_DROP_DATA           0x0800
+
+#define FLAGS_GSP_SKIP            0x1000
+#define FLAGS_GSP_DLESEEN         0x2000
+
+
+
+
+
+
+/* function prototypes */
+static void gsp_next_packet(struct garmin_data * garmin_data_p);
+static int  garmin_write_bulk(struct usb_serial_port *port,
+                            const unsigned char *buf, int count);
+
+/* some special packets to be send or received */
+static unsigned char const GARMIN_START_SESSION_REQ[]
+       = { 0, 0, 0, 0,  5, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char const GARMIN_START_SESSION_REQ2[]
+       = { 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char const GARMIN_START_SESSION_REPLY[]
+       = { 0, 0, 0, 0,  6, 0, 0, 0, 4, 0, 0, 0 };
+static unsigned char const GARMIN_SESSION_ACTIVE_REPLY[]
+       = { 0, 0, 0, 0, 17, 0, 0, 0, 4, 0, 0, 0, 0, 16, 0, 0 };
+static unsigned char const GARMIN_BULK_IN_AVAIL_REPLY[]
+       = { 0, 0, 0, 0,  2, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char const GARMIN_APP_LAYER_REPLY[]
+       = { 0x14, 0, 0, 0 };
+static unsigned char const GARMIN_START_PVT_REQ[]
+       = { 20, 0, 0, 0,  10, 0, 0, 0, 2, 0, 0, 0, 49, 0 };
+static unsigned char const GARMIN_STOP_PVT_REQ[]
+       = { 20, 0, 0, 0,  10, 0, 0, 0, 2, 0, 0, 0, 50, 0 };
+static unsigned char const GARMIN_STOP_TRANSFER_REQ[]
+       = { 20, 0, 0, 0,  10, 0, 0, 0, 2, 0, 0, 0, 0, 0 };
+static unsigned char const GARMIN_STOP_TRANSFER_REQ_V2[]
+       = { 20, 0, 0, 0,  10, 0, 0, 0, 1, 0, 0, 0, 0 };
+static unsigned char const PRIVATE_REQ[]
+       =    { 0x4B, 0x6E, 0x10, 0x01,  0xFF, 0, 0, 0, 0xFF, 0, 0, 0 };
+
+
+
+static struct usb_device_id id_table [] = {
+       /* the same device id seems to be used by all usb enabled gps devices */
+       { USB_DEVICE(GARMIN_VENDOR_ID, 3 ) },
+       { }                                     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, id_table);
+
+static struct usb_driver garmin_driver = {
+       .owner =        THIS_MODULE,
+       .name =         "garmin_gps",
+       .probe =        usb_serial_probe,
+       .disconnect =   usb_serial_disconnect,
+       .id_table =     id_table,
+};
+
+
+static inline int noResponseFromAppLayer(struct garmin_data * garmin_data_p)
+{
+       return ((garmin_data_p->flags
+                               & (FLAGS_APP_REQ_SEEN|FLAGS_APP_RESP_SEEN))
+               == FLAGS_APP_REQ_SEEN);
+}
+
+
+static inline int getLayerId(const __u8 *usbPacket)
+{
+       return __le32_to_cpup((__le32 *)(usbPacket));
+}
+
+static inline int getPacketId(const __u8 *usbPacket)
+{
+       return __le32_to_cpup((__le32 *)(usbPacket+4));
+}
+
+static inline int getDataLength(const __u8 *usbPacket)
+{
+       return __le32_to_cpup((__le32 *)(usbPacket+8));
+}
+
+
+/*
+ * check if the usb-packet in buf contains an abort-transfer command.
+ * (if yes, all queued data will be dropped)
+ */
+static inline int isAbortTrfCmnd(const unsigned char *buf)
+{
+       if (0 == memcmp(buf, GARMIN_STOP_TRANSFER_REQ, 
+                       sizeof(GARMIN_STOP_TRANSFER_REQ)) ||
+           0 == memcmp(buf, GARMIN_STOP_TRANSFER_REQ_V2, 
+                       sizeof(GARMIN_STOP_TRANSFER_REQ_V2)))
+               return 1;
+       else
+               return 0;
+}
+
+
+
+static void send_to_tty(struct usb_serial_port *port,
+                        char *data, unsigned int actual_length)
+{
+       struct tty_struct *tty = port->tty;
+       int i;
+
+       if (tty && actual_length) {
+
+               usb_serial_debug_data(debug, &port->dev, 
+                                       __FUNCTION__, actual_length, data);
+
+               for (i = 0; i < actual_length ; ++i) {
+                       /* if we insert more than TTY_FLIPBUF_SIZE characters,
+                          we drop them. */
+                       if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                               tty_flip_buffer_push(tty);
+                       }
+                       /* this doesn't actually push the data through unless
+                          tty->low_latency is set */
+                       tty_insert_flip_char(tty, data[i], 0);
+               }
+               tty_flip_buffer_push(tty);
+       }
+}
+
+
+/******************************************************************************
+ * packet queue handling
+ ******************************************************************************/
+
+/*
+ * queue a received (usb-)packet for later processing
+ */
+static int pkt_add(struct garmin_data * garmin_data_p,
+                   unsigned char *data, unsigned int data_length)
+{
+       int result = 0;
+       unsigned long flags;
+       struct garmin_packet *pkt;
+
+       /* process only packets containg data ... */
+       if (data_length) {
+               garmin_data_p->flags |= FLAGS_QUEUING;
+               pkt = kmalloc(sizeof(struct garmin_packet)+data_length,
+                             GFP_ATOMIC);
+               if (pkt == NULL) {
+                       dev_err(&garmin_data_p->port->dev, "out of memory\n");
+                       return 0;
+               }
+               pkt->size = data_length;
+               memcpy(pkt->data, data, data_length);
+
+               spin_lock_irqsave(&garmin_data_p->lock, flags);
+               result = list_empty(&garmin_data_p->pktlist);
+               pkt->seq = garmin_data_p->seq_counter++;
+               list_add_tail(&pkt->list, &garmin_data_p->pktlist);
+               spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+               /* in serial mode, if someone is waiting for data from
+                  the device, iconvert and send the next packet to tty. */
+               if (result && (garmin_data_p->state == STATE_GSP_WAIT_DATA)) {
+                       gsp_next_packet(garmin_data_p);
+               }
+       }
+       return result;
+}
+
+
+/* get the next pending packet */
+static struct garmin_packet *pkt_pop(struct garmin_data * garmin_data_p)
+{
+       unsigned long flags;
+       struct garmin_packet *result = NULL;
+
+       spin_lock_irqsave(&garmin_data_p->lock, flags);
+       if (!list_empty(&garmin_data_p->pktlist)) {
+               result = (struct garmin_packet *)garmin_data_p->pktlist.next;
+               list_del(&result->list);
+       }
+       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+       return result;
+}
+
+
+/* free up all queued data */
+static void pkt_clear(struct garmin_data * garmin_data_p)
+{
+       unsigned long flags;
+       struct garmin_packet *result = NULL;
+
+       dbg("%s", __FUNCTION__);
+
+       spin_lock_irqsave(&garmin_data_p->lock, flags);
+       while (!list_empty(&garmin_data_p->pktlist)) {
+               result = (struct garmin_packet *)garmin_data_p->pktlist.next;
+               list_del(&result->list);
+               kfree(result);
+       }
+       spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+}
+
+
+/******************************************************************************
+ * garmin serial protocol handling handling
+ ******************************************************************************/
+
+/* send an ack packet back to the tty */
+static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id)
+{
+       __u8 pkt[10];
+        __u8 cksum = 0;
+        __u8 *ptr = pkt;
+        unsigned  l = 0;
+
+       dbg("%s - pkt-id: 0x%X.", __FUNCTION__, 0xFF & pkt_id);
+
+       *ptr++ = DLE;
+       *ptr++ = ACK;
+       cksum += ACK;
+
+       *ptr++ = 2;
+       cksum += 2;
+
+       *ptr++ = pkt_id;
+       cksum += pkt_id;
+
+       if (pkt_id == DLE) {
+               *ptr++ = DLE;
+       }
+
+       *ptr++ = 0;
+       *ptr++ = 0xFF & (-cksum);
+       *ptr++ = DLE;
+       *ptr++ = ETX;
+
+       l = ptr-pkt;
+
+       send_to_tty(garmin_data_p->port, pkt, l);
+       return 0;
+}
+
+
+
+/*
+ * called for a complete packet received from tty layer
+ *
+ * the complete packet (pkzid ... cksum) is in garmin_data_p->inbuf starting
+ * at GSP_INITIAL_OFFSET.
+ *
+ * count - number of bytes in the input buffer including space reserved for
+ *         the usb header: GSP_INITIAL_OFFSET + number of bytes in packet 
+ *         (including pkt-id, data-length a. cksum)
+ */
+static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
+{
+       const __u8* recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET;
+        __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer;
+
+       int cksum = 0;
+       int n = 0;
+       int pktid = recpkt[0];
+       int size = recpkt[1];
+
+       usb_serial_debug_data(debug, &garmin_data_p->port->dev,
+                              __FUNCTION__, count-GSP_INITIAL_OFFSET, recpkt);
+
+       if (size != (count-GSP_INITIAL_OFFSET-3)) {
+               dbg("%s - invalid size, expected %d bytes, got %d",
+                       __FUNCTION__, size, (count-GSP_INITIAL_OFFSET-3));
+               return -EINVPKT;
+       }
+
+       cksum += *recpkt++;
+       cksum += *recpkt++;
+
+       // sanity check, remove after test ...
+       if ((__u8*)&(usbdata[3]) != recpkt) {
+               dbg("%s - ptr mismatch %p - %p",
+                       __FUNCTION__, &(usbdata[4]), recpkt);
+               return -EINVPKT;
+       }
+
+       while (n < size) {
+               cksum += *recpkt++;
+               n++;
+       }
+
+       if ((0xff & (cksum + *recpkt)) != 0) {
+                dbg("%s - invalid checksum, expected %02x, got %02x",
+                        __FUNCTION__, 0xff & -cksum, 0xff & *recpkt);
+                return -EINVPKT;
+        }
+
+       usbdata[0] = __cpu_to_le32(GARMIN_LAYERID_APPL);
+       usbdata[1] = __cpu_to_le32(pktid);
+       usbdata[2] = __cpu_to_le32(size);
+
+       garmin_write_bulk (garmin_data_p->port, garmin_data_p->inbuffer,
+                          GARMIN_PKTHDR_LENGTH+size);
+
+       /* if this was an abort-transfer command, flush all
+          queued data. */
+       if (isAbortTrfCmnd(garmin_data_p->inbuffer)) {
+               garmin_data_p->flags |= FLAGS_DROP_DATA;
+               pkt_clear(garmin_data_p);
+       }
+
+       return count;
+}
+
+
+
+/*
+ * Called for data received from tty
+ *
+ * buf contains the data read, it may span more than one packet or even
+ * incomplete packets
+ *
+ * input record should be a serial-record, but it may not be complete.
+ * Copy it into our local buffer, until an etx is seen (or an error
+ * occurs).
+ * Once the record is complete, convert into a usb packet and send it
+ * to the bulk pipe, send an ack back to the tty.
+ *
+ * If the input is an ack, just send the last queued packet to the
+ * tty layer.
+ *
+ * if the input is an abort command, drop all queued data.
+ */
+
+static int gsp_receive(struct garmin_data * garmin_data_p,
+                       const unsigned char *buf, int count)
+{
+       int offs = 0;
+       int ack_or_nak_seen = 0;
+       int i = 0;
+       __u8 *dest = garmin_data_p->inbuffer;
+       int size = garmin_data_p->insize;
+       // dleSeen: set if last byte read was a DLE
+       int dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN;
+       // skip: if set, skip incoming data until possible start of
+       //       new packet
+       int skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
+       __u8 data;
+
+       dbg("%s - dle=%d skip=%d size=%d count=%d",
+               __FUNCTION__, dleSeen, skip, size, count);
+
+       if (size == 0) {
+               size = GSP_INITIAL_OFFSET;
+       }
+
+       while (offs < count) {
+
+               data = *(buf+offs);
+               offs ++;
+
+               if (data == DLE) {
+                       if (skip) { /* start of a new pkt */
+                               skip = 0;
+                               size = GSP_INITIAL_OFFSET;
+                               dleSeen = 1;
+                       } else if (dleSeen) {
+                               dest[size++] = data;
+                               dleSeen = 0;
+                       } else {
+                               dleSeen = 1;
+                       }
+               } else if (data == ETX) {
+                       if (dleSeen) {
+                               /* packet complete */
+
+                               data = dest[GSP_INITIAL_OFFSET];
+
+                               if (data == ACK) {
+                                       ack_or_nak_seen = ACK;
+                                       dbg("ACK packet complete.");
+                               } else if (data == NAK) {
+                                       ack_or_nak_seen = NAK;
+                                       dbg("NAK packet complete.");
+                               } else {
+                                       dbg("packet complete "
+                                                       "- id=0x%X.",
+                                                       0xFF & data);
+                                       gsp_rec_packet(garmin_data_p, size);
+                               }
+
+                               skip = 1;
+                               size = GSP_INITIAL_OFFSET;
+                               dleSeen = 0;
+                       } else {
+                               dest[size++] = data;
+                       }
+               } else if (!skip) {
+
+                       if (dleSeen) {
+                               dbg("non-masked DLE at %d - restarting", i);
+                               size = GSP_INITIAL_OFFSET;
+                               dleSeen = 0;
+                       }
+
+                       dest[size++] = data;
+               }
+
+               if (size >= GPS_IN_BUFSIZ) {
+                       dbg("%s - packet too large.", __FUNCTION__);
+                       skip = 1;
+                       size = GSP_INITIAL_OFFSET;
+                       dleSeen = 0;
+               }
+       }
+
+       garmin_data_p->insize = size;
+
+       // copy flags back to structure
+       if (skip)
+               garmin_data_p->flags |= FLAGS_GSP_SKIP;
+       else
+               garmin_data_p->flags &= ~FLAGS_GSP_SKIP;
+
+       if (dleSeen)
+               garmin_data_p->flags |= FLAGS_GSP_DLESEEN;
+       else
+               garmin_data_p->flags &= ~FLAGS_GSP_DLESEEN;
+
+       if (ack_or_nak_seen) {
+               garmin_data_p->state = STATE_GSP_WAIT_DATA;
+               gsp_next_packet(garmin_data_p);
+       }
+
+       return count;
+}
+
+
+
+
+/*
+ * Sends a usb packet to the tty
+ *
+ * Assumes, that all packages and at an usb-packet boundary.
+ *
+ * return <0 on error, 0 if packet is incomplete or > 0 if packet was sent
+ */
+int gsp_send(struct garmin_data * garmin_data_p, const unsigned char *buf,
+              int count)
+{
+       const unsigned char *src;
+       unsigned char *dst;
+       int pktid = 0;
+       int datalen = 0;
+       int cksum = 0;
+       int i=0;
+       int k;
+
+       dbg("%s - state %d - %d bytes.", __FUNCTION__,
+                garmin_data_p->state, count);
+
+       k = garmin_data_p->outsize;
+       if ((k+count) > GPS_OUT_BUFSIZ) {
+               dbg("packet too large");
+               garmin_data_p->outsize = 0;
+               return -4;
+       }
+
+       memcpy(garmin_data_p->outbuffer+k, buf, count);
+       k += count;
+       garmin_data_p->outsize = k;
+
+       if (k >= GARMIN_PKTHDR_LENGTH) {
+               pktid  = getPacketId(garmin_data_p->outbuffer);
+               datalen= getDataLength(garmin_data_p->outbuffer);
+               i = GARMIN_PKTHDR_LENGTH + datalen;
+               if (k < i)
+                       return 0;
+       } else {
+               return 0;
+       }
+
+       dbg("%s - %d bytes in buffer, %d bytes in pkt.", __FUNCTION__,
+                k, i);
+
+       /* garmin_data_p->outbuffer now contains a complete packet */
+
+       usb_serial_debug_data(debug, &garmin_data_p->port->dev,
+                                  __FUNCTION__, k, garmin_data_p->outbuffer);
+
+       garmin_data_p->outsize = 0;
+
+       if (GARMIN_LAYERID_APPL != getLayerId(garmin_data_p->outbuffer)) {
+               dbg("not an application packet (%d)", 
+                       getLayerId(garmin_data_p->outbuffer));
+               return -1;
+       }
+
+       if (pktid > 255) {
+               dbg("packet-id %d too large", pktid);
+               return -2;
+       }
+
+       if (datalen > 255) {
+               dbg("packet-size %d too large", datalen);
+               return -3;
+       }
+
+       /* the serial protocol should be able to handle this packet */
+
+       k = 0;
+       src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH;
+       for (i=0; i<datalen; i++) {
+               if (*src++ == DLE)
+                       k++;
+       }
+
+       src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH;
+       if (k > (GARMIN_PKTHDR_LENGTH-2)) {
+               /* can't add stuffing DLEs in place, move data to end 
+                   of buffer ... */
+               dst = garmin_data_p->outbuffer+GPS_OUT_BUFSIZ-datalen;
+               memcpy(dst, src, datalen);
+               src = dst;
+       }
+
+       dst = garmin_data_p->outbuffer;
+
+       *dst++ = DLE;
+       *dst++ = pktid;
+       cksum += pktid;
+       *dst++ = datalen;
+       cksum += datalen;
+       if (datalen == DLE)
+               *dst++ = DLE;
+
+       for (i=0; i<datalen; i++) {
+               __u8 c = *src++;
+               *dst++ = c;
+               cksum += c;
+               if (c == DLE)
+                       *dst++ = DLE;
+       }
+               
+       cksum = 0xFF & -cksum;
+       *dst++ = cksum;
+       if (cksum == DLE)
+               *dst++ = DLE;
+       *dst++ = DLE;
+       *dst++ = ETX;
+
+       i = dst-garmin_data_p->outbuffer;
+
+       send_to_tty(garmin_data_p->port, garmin_data_p->outbuffer, i);
+
+       garmin_data_p->pkt_id = pktid;
+       garmin_data_p->state  = STATE_WAIT_TTY_ACK;
+
+       return i;
+}
+
+
+
+
+
+/*
+ * Process the next pending data packet - if there is one
+ */
+static void gsp_next_packet(struct garmin_data * garmin_data_p)
+{
+       struct garmin_packet *pkt = NULL;
+
+       while ((pkt = pkt_pop(garmin_data_p)) != NULL) {
+               dbg("%s - next pkt: %d", __FUNCTION__, pkt->seq);
+               if (gsp_send(garmin_data_p, pkt->data, pkt->size) > 0) {
+                       kfree(pkt);
+                       return;
+               }
+               kfree(pkt);
+       }
+}
+
+
+
+
+/******************************************************************************
+ * garmin native mode
+ ******************************************************************************/
+
+
+/*
+ * Called for data received from tty
+ *
+ * The input data is expected to be in garmin usb-packet format.
+ *
+ * buf contains the data read, it may span more than one packet
+ * or even incomplete packets
+ */
+static int nat_receive(struct garmin_data * garmin_data_p,
+                       const unsigned char *buf, int count)
+{
+       __u8 * dest;
+       int offs = 0;
+       int result = count;
+       int len;
+
+       while (offs < count) {
+               // if buffer contains header, copy rest of data
+               if (garmin_data_p->insize >= GARMIN_PKTHDR_LENGTH)
+                       len = GARMIN_PKTHDR_LENGTH
+                             +getDataLength(garmin_data_p->inbuffer);
+               else
+                       len = GARMIN_PKTHDR_LENGTH;
+
+               if (len >= GPS_IN_BUFSIZ) {
+                       /* seem to be an invalid packet, ignore rest of input */
+                       dbg("%s - packet size too large: %d",
+                               __FUNCTION__, len);
+                       garmin_data_p->insize = 0;
+                       count = 0;
+                       result = -EINVPKT;
+               } else {
+                       len -= garmin_data_p->insize;
+                       if (len > (count-offs))
+                               len = (count-offs);
+                       if (len > 0) {
+                               dest = garmin_data_p->inbuffer
+                                       +garmin_data_p->insize;
+                               memcpy(dest, buf+offs, len);
+                               garmin_data_p->insize += len;
+                               offs += len;
+                       }
+               }
+
+               /* do we have a complete packet ? */
+               if (garmin_data_p->insize >= GARMIN_PKTHDR_LENGTH) {
+                       len = GARMIN_PKTHDR_LENGTH+
+                          getDataLength(garmin_data_p->inbuffer);
+                       if (garmin_data_p->insize >= len) {
+                               garmin_write_bulk (garmin_data_p->port,
+                                                  garmin_data_p->inbuffer,
+                                                  len);
+                               garmin_data_p->insize = 0;
+
+                               /* if this was an abort-transfer command,
+                                  flush all queued data. */
+                               if (isAbortTrfCmnd(garmin_data_p->inbuffer)) {
+                                       garmin_data_p->flags |= FLAGS_DROP_DATA;
+                                       pkt_clear(garmin_data_p);
+                               }
+                       }
+               }
+       }
+       return result;
+}
+
+
+/******************************************************************************
+ * private packets
+ ******************************************************************************/
+
+static void priv_status_resp(struct usb_serial_port *port)
+{
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+       __le32 *pkt = (__le32 *)garmin_data_p->privpkt;
+
+       pkt[0] = __cpu_to_le32(GARMIN_LAYERID_PRIVATE);
+       pkt[1] = __cpu_to_le32(PRIV_PKTID_INFO_RESP);
+       pkt[2] = __cpu_to_le32(12);
+       pkt[3] = __cpu_to_le32(VERSION_MAJOR << 16 | VERSION_MINOR);
+       pkt[4] = __cpu_to_le32(garmin_data_p->mode);
+       pkt[5] = __cpu_to_le32(garmin_data_p->serial_num);
+
+       send_to_tty(port, (__u8*)pkt, 6*4);
+}
+
+
+/******************************************************************************
+ * Garmin specific driver functions
+ ******************************************************************************/
+
+static int process_resetdev_request(struct usb_serial_port *port)
+{
+       int status;
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+
+       garmin_data_p->flags &= ~(CLEAR_HALT_REQUIRED);
+       garmin_data_p->state = STATE_RESET;
+       garmin_data_p->serial_num = 0;
+
+       usb_kill_urb (port->interrupt_in_urb);
+       dbg("%s - usb_reset_device", __FUNCTION__ );
+       status = usb_reset_device(port->serial->dev);
+       if (status)
+               dbg("%s - usb_reset_device failed: %d",
+                       __FUNCTION__, status);
+       return status;
+}
+
+
+
+/*
+ * clear all cached data
+ */
+static int garmin_clear(struct garmin_data * garmin_data_p)
+{
+       int status = 0;
+
+       struct usb_serial_port *port = garmin_data_p->port;
+
+       if (port != NULL && garmin_data_p->flags & FLAGS_APP_RESP_SEEN) {
+               /* send a terminate command */
+               status = garmin_write_bulk(port, GARMIN_STOP_TRANSFER_REQ,
+                                          sizeof(GARMIN_STOP_TRANSFER_REQ));
+       }
+
+       /* flush all queued data */
+       pkt_clear(garmin_data_p);
+
+       garmin_data_p->insize = 0;
+       garmin_data_p->outsize = 0;
+
+       return status;
+}
+
+
+
+
+
+
+static int garmin_init_session(struct usb_serial_port *port)
+{
+       struct usb_serial *serial = port->serial;
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+       int status = 0;
+
+       if (status == 0) {
+               usb_kill_urb (port->interrupt_in_urb);
+
+               dbg("%s - adding interrupt input", __FUNCTION__);
+               port->interrupt_in_urb->dev = serial->dev;
+               status = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+               if (status)
+                       dev_err(&serial->dev->dev,
+                               "%s - failed submitting interrupt urb,"
+                               " error %d\n",
+                               __FUNCTION__, status);
+       }
+
+       if (status == 0) {
+               dbg("%s - starting session ...", __FUNCTION__);
+               garmin_data_p->state = STATE_ACTIVE;
+               status = garmin_write_bulk(port, GARMIN_START_SESSION_REQ,
+                                          sizeof(GARMIN_START_SESSION_REQ));
+
+               if (status >= 0) {
+
+                       garmin_data_p->ignorePkts++;
+
+                       /* not needed, but the win32 driver does it too ... */
+                       status = garmin_write_bulk(port,
+                                                  GARMIN_START_SESSION_REQ2,
+                                                  sizeof(GARMIN_START_SESSION_REQ2));
+                       if (status >= 0) {
+                               status = 0;
+                               garmin_data_p->ignorePkts++;
+                       }
+               }
+       }
+
+       return status;
+}
+
+
+
+
+
+static int garmin_open (struct usb_serial_port *port, struct file *filp)
+{
+       int status = 0;
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /*
+        * Force low_latency on so that our tty_push actually forces the data
+        * through, otherwise it is scheduled, and with high data rates (like
+        * with OHCI) data can get lost.
+        */
+       if (port->tty)
+               port->tty->low_latency = 1;
+
+       garmin_data_p->mode  = initial_mode;
+       garmin_data_p->count = 0;
+       garmin_data_p->flags = 0;
+
+       /* shutdown any bulk reads that might be going on */
+       usb_kill_urb (port->write_urb);
+       usb_kill_urb (port->read_urb);
+
+       if (garmin_data_p->state == STATE_RESET) {
+               status = garmin_init_session(port);
+       }
+
+       garmin_data_p->state = STATE_ACTIVE;
+
+       return status;
+}
+
+
+static void garmin_close (struct usb_serial_port *port, struct file * filp)
+{
+       struct usb_serial *serial = port->serial;
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+
+       dbg("%s - port %d - mode=%d state=%d flags=0x%X", __FUNCTION__,
+               port->number, garmin_data_p->mode,
+               garmin_data_p->state, garmin_data_p->flags);
+
+       if (!serial)
+               return;
+
+       garmin_clear(garmin_data_p);
+
+       /* shutdown our urbs */
+       usb_kill_urb (port->read_urb);
+       usb_kill_urb (port->write_urb);
+
+       if (noResponseFromAppLayer(garmin_data_p) ||
+           ((garmin_data_p->flags & CLEAR_HALT_REQUIRED) != 0)) {
+               process_resetdev_request(port);
+               garmin_data_p->state = STATE_RESET;
+       } else {
+               garmin_data_p->state = STATE_DISCONNECTED;
+       }
+}
+
+
+static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+
+       /* free up the transfer buffer, as usb_free_urb() does not do this */
+       kfree (urb->transfer_buffer);
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (urb->status) {
+               dbg("%s - nonzero write bulk status received: %d",
+                       __FUNCTION__, urb->status);
+               garmin_data_p->flags |= CLEAR_HALT_REQUIRED;
+       }
+
+       schedule_work(&port->work);
+}
+
+
+static int garmin_write_bulk (struct usb_serial_port *port,
+                              const unsigned char *buf, int count)
+{
+       struct usb_serial *serial = port->serial;
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+       struct urb *urb;
+       unsigned char *buffer;
+       int status;
+
+       dbg("%s - port %d, state %d", __FUNCTION__, port->number,
+               garmin_data_p->state);
+
+       garmin_data_p->flags &= ~FLAGS_DROP_DATA;
+
+       buffer = kmalloc (count, GFP_ATOMIC);
+       if (!buffer) {
+               dev_err(&port->dev, "out of memory\n");
+               return -ENOMEM;
+       }
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               dev_err(&port->dev, "no more free urbs\n");
+               kfree (buffer);
+               return -ENOMEM;
+       }
+
+       memcpy (buffer, buf, count);
+
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buffer);
+
+       usb_fill_bulk_urb (urb, serial->dev,
+                               usb_sndbulkpipe (serial->dev,
+                               port->bulk_out_endpointAddress),
+                               buffer, count,
+                               garmin_write_bulk_callback, port);
+       urb->transfer_flags |= URB_ZERO_PACKET;
+
+       if (GARMIN_LAYERID_APPL == getLayerId(buffer)) {
+               garmin_data_p->flags |= FLAGS_APP_REQ_SEEN;
+               if (garmin_data_p->mode == MODE_GARMIN_SERIAL)  {
+                       pkt_clear(garmin_data_p);
+                       garmin_data_p->state = STATE_GSP_WAIT_DATA;
+               }
+       }
+
+       /* send it down the pipe */
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+       if (status) {
+               dev_err(&port->dev,
+                       "%s - usb_submit_urb(write bulk) "
+                       "failed with status = %d\n",
+                               __FUNCTION__, status);
+               count = status;
+       } else {
+
+               if (GARMIN_LAYERID_APPL == getLayerId(buffer)
+                   && (garmin_data_p->mode == MODE_GARMIN_SERIAL))  {
+
+                       gsp_send_ack(garmin_data_p, buffer[4]);
+               }
+       }
+
+       /* we are done with this urb, so let the host driver
+        * really free it when it is finished with it */
+       usb_free_urb (urb);
+
+       return count;
+}
+
+
+
+static int garmin_write (struct usb_serial_port *port,
+                         const unsigned char *buf, int count)
+{
+       int pktid, pktsiz, len;
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+       __le32 *privpkt = (__le32 *)garmin_data_p->privpkt;
+
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buf);
+
+       /* check for our private packets */
+       if (count >= GARMIN_PKTHDR_LENGTH) {
+
+               len = PRIVPKTSIZ;
+               if (count < len)
+                       len = count;
+
+               memcpy(garmin_data_p->privpkt, buf, len);
+
+               pktsiz = getDataLength(garmin_data_p->privpkt);
+               pktid  = getPacketId(garmin_data_p->privpkt);
+
+               if (count == (GARMIN_PKTHDR_LENGTH+pktsiz)
+                   && GARMIN_LAYERID_PRIVATE == getLayerId(garmin_data_p->privpkt)) {
+
+                       dbg("%s - processing private request %d",
+                               __FUNCTION__, pktid);
+
+                       // drop all unfinished transfers
+                       garmin_clear(garmin_data_p);
+
+                       switch(pktid) {
+
+                       case PRIV_PKTID_SET_DEBUG:
+                               if (pktsiz != 4)
+                                       return -EINVPKT;
+                               debug = __le32_to_cpu(privpkt[3]);
+                               dbg("%s - debug level set to 0x%X",
+                                       __FUNCTION__, debug);
+                               break;
+
+                       case PRIV_PKTID_SET_MODE:
+                               if (pktsiz != 4)
+                                       return -EINVPKT;
+                               garmin_data_p->mode = __le32_to_cpu(privpkt[3]);
+                               dbg("%s - mode set to %d",
+                                       __FUNCTION__, garmin_data_p->mode);
+                               break;
+
+                       case PRIV_PKTID_INFO_REQ:
+                               priv_status_resp(port);
+                               break;
+
+                       case PRIV_PKTID_RESET_REQ:
+                               garmin_data_p->flags |= FLAGS_APP_REQ_SEEN;
+                               break;
+
+                       case PRIV_PKTID_SET_DEF_MODE:
+                               if (pktsiz != 4)
+                                       return -EINVPKT;
+                               initial_mode = __le32_to_cpu(privpkt[3]);
+                               dbg("%s - initial_mode set to %d",
+                                       __FUNCTION__,
+                                       garmin_data_p->mode);
+                               break;
+                       }
+                       return count;
+               }
+       }
+
+       if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
+               return gsp_receive(garmin_data_p, buf, count);
+       } else {        /* MODE_NATIVE */
+               return nat_receive(garmin_data_p, buf, count);
+       }
+}
+
+
+static int garmin_write_room (struct usb_serial_port *port)
+{
+       /*
+        * Report back the bytes currently available in the output buffer.
+        */
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+       return GPS_OUT_BUFSIZ-garmin_data_p->outsize;
+}
+
+
+static int garmin_chars_in_buffer (struct usb_serial_port *port)
+{
+       /*
+        * Report back the number of bytes currently in our input buffer.
+         * Will this lock up the driver - the buffer contains an incomplete
+         * package which will not be written to the device until it
+         * has been completed ?
+         */
+       //struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+       //return garmin_data_p->insize;
+       return 0;
+}
+
+
+static void garmin_read_process(struct garmin_data * garmin_data_p,
+                                unsigned char *data, unsigned data_length)
+{
+       if (garmin_data_p->flags & FLAGS_DROP_DATA) {
+               /* abort-transfer cmd is actice */
+               dbg("%s - pkt dropped", __FUNCTION__);
+       } else if (garmin_data_p->state != STATE_DISCONNECTED &&
+                  garmin_data_p->state != STATE_RESET ) {
+
+               /* remember any appl.layer packets, so we know
+                  if a reset is required or not when closing
+                  the device */
+               if (0 == memcmp(data, GARMIN_APP_LAYER_REPLY,
+                               sizeof(GARMIN_APP_LAYER_REPLY)))
+                       garmin_data_p->flags |= FLAGS_APP_RESP_SEEN;
+
+               /* if throttling is active or postprecessing is required
+                  put the received data in th input queue, otherwise
+                  send it directly to the tty port */
+               if (garmin_data_p->flags & FLAGS_QUEUING) {
+                       pkt_add(garmin_data_p, data, data_length);
+               } else if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
+                       if (getLayerId(data) == GARMIN_LAYERID_APPL) {
+                               pkt_add(garmin_data_p, data, data_length);
+                       }
+               } else {
+                       send_to_tty(garmin_data_p->port, data, data_length);
+               }
+       }
+}
+
+
+static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+       struct usb_serial *serial =  port->serial;
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+       unsigned char *data = urb->transfer_buffer;
+       int status;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (!serial) {
+               dbg("%s - bad serial pointer, exiting", __FUNCTION__);
+               return;
+       }
+
+       if (urb->status) {
+               dbg("%s - nonzero read bulk status received: %d",
+                       __FUNCTION__, urb->status);
+               return;
+       }
+
+       usb_serial_debug_data(debug, &port->dev, 
+                               __FUNCTION__, urb->actual_length, data);
+
+       garmin_read_process(garmin_data_p, data, urb->actual_length);
+
+       /* Continue trying to read until nothing more is received  */
+       if (urb->actual_length > 0) {
+               usb_fill_bulk_urb (port->read_urb, serial->dev,
+                          usb_rcvbulkpipe (serial->dev,
+                                           port->bulk_in_endpointAddress),
+                          port->read_urb->transfer_buffer,
+                          port->read_urb->transfer_buffer_length,
+                          garmin_read_bulk_callback, port);
+               status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+               if (status)
+                       dev_err(&port->dev,
+                               "%s - failed resubmitting read urb, error %d\n",
+                               __FUNCTION__, status);
+       }
+       return;
+}
+
+
+static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
+{
+       int status;
+       struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+       struct usb_serial *serial = port->serial;
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+       unsigned char *data = urb->transfer_buffer;
+
+       switch (urb->status) {
+       case 0:
+               /* success */
+               break;
+       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);
+               return;
+       }
+
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, 
+                               urb->actual_length, urb->transfer_buffer);
+
+       if (urb->actual_length == sizeof(GARMIN_BULK_IN_AVAIL_REPLY) &&
+           0 == memcmp(data, GARMIN_BULK_IN_AVAIL_REPLY,
+                       sizeof(GARMIN_BULK_IN_AVAIL_REPLY))) {
+
+               dbg("%s - bulk data available.", __FUNCTION__);
+
+               /* bulk data available */
+               usb_fill_bulk_urb (port->read_urb, serial->dev,
+                               usb_rcvbulkpipe (serial->dev,
+                               port->bulk_in_endpointAddress),
+                               port->read_urb->transfer_buffer,
+                               port->read_urb->transfer_buffer_length,
+                               garmin_read_bulk_callback, port);
+               status = usb_submit_urb(port->read_urb, GFP_KERNEL);
+               if (status) {
+                       dev_err(&port->dev,
+                               "%s - failed submitting read urb, error %d\n",
+                       __FUNCTION__, status);
+               }
+
+       } else if (urb->actual_length == (4+sizeof(GARMIN_START_SESSION_REPLY))
+                        && 0 == memcmp(data, GARMIN_START_SESSION_REPLY,
+                                       sizeof(GARMIN_START_SESSION_REPLY))) {
+
+               garmin_data_p->flags |= FLAGS_SESSION_REPLY1_SEEN;
+
+               /* save the serial number */
+               garmin_data_p->serial_num 
+                       = __le32_to_cpup((__le32*)(data+GARMIN_PKTHDR_LENGTH));
+
+               dbg("%s - start-of-session reply seen - serial %u.",
+                       __FUNCTION__, garmin_data_p->serial_num);
+       }
+
+       if (garmin_data_p->ignorePkts) {
+               /* this reply belongs to a request generated by the driver,
+                  ignore it. */
+               dbg("%s - pkt ignored (%d)",
+                       __FUNCTION__, garmin_data_p->ignorePkts);
+               garmin_data_p->ignorePkts--;
+       } else {
+               garmin_read_process(garmin_data_p, data, urb->actual_length);
+       }
+
+       port->interrupt_in_urb->dev = port->serial->dev;
+       status = usb_submit_urb (urb, GFP_ATOMIC);
+       if (status)
+               dev_err(&urb->dev->dev,
+                       "%s - Error %d submitting interrupt urb\n",
+                       __FUNCTION__, status);
+}
+
+
+/*
+ * Sends the next queued packt to the tty port (garmin native mode only)
+ * and then sets a timer to call itself again until all queued data
+ * is sent.
+ */
+static int garmin_flush_queue(struct garmin_data * garmin_data_p)
+{
+       struct garmin_packet *pkt;
+
+       if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) {
+               pkt = pkt_pop(garmin_data_p);
+               if (pkt != NULL) {
+
+                       send_to_tty(garmin_data_p->port, pkt->data, pkt->size);
+                       kfree(pkt);
+                       mod_timer(&garmin_data_p->timer, (1)+jiffies);
+
+               } else {
+                       garmin_data_p->flags &= ~FLAGS_QUEUING;
+               }
+       }
+       return 0;
+}
+
+
+static void garmin_throttle (struct usb_serial_port *port)
+{
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+       /* set flag, data received will be put into a queue
+          for later processing */
+       garmin_data_p->flags |= FLAGS_QUEUING|FLAGS_THROTTLED;
+}
+
+
+static void garmin_unthrottle (struct usb_serial_port *port)
+{
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+       garmin_data_p->flags &= ~FLAGS_THROTTLED;
+
+       /* in native mode send queued data to tty, in
+          serial mode nothing needs to be done here */
+       if (garmin_data_p->mode == MODE_NATIVE)
+               garmin_flush_queue(garmin_data_p);
+}
+
+
+
+/*
+ * The timer is currently only used to send queued packets to
+ * the tty in cases where the protocol provides no own handshaking
+ * to initiate the transfer.
+ */
+static void timeout_handler(unsigned long data)
+{
+       struct garmin_data *garmin_data_p = (struct garmin_data *) data;
+
+       /* send the next queued packet to the tty port */
+       if (garmin_data_p->mode == MODE_NATIVE)
+               if (garmin_data_p->flags & FLAGS_QUEUING)
+                       garmin_flush_queue(garmin_data_p);
+}
+
+
+
+static int garmin_attach (struct usb_serial *serial)
+{
+       int status = 0;
+       struct usb_serial_port *port = serial->port[0];
+       struct garmin_data * garmin_data_p = NULL;
+
+       dbg("%s", __FUNCTION__);
+
+       garmin_data_p = kmalloc (sizeof(struct garmin_data), GFP_KERNEL);
+       if (garmin_data_p == NULL) {
+               dev_err(&port->dev, "%s - Out of memory\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+       memset (garmin_data_p, 0, sizeof(struct garmin_data));
+       init_timer(&garmin_data_p->timer);
+       spin_lock_init(&garmin_data_p->lock);
+       INIT_LIST_HEAD(&garmin_data_p->pktlist);
+       //garmin_data_p->timer.expires = jiffies + session_timeout;
+       garmin_data_p->timer.data = (unsigned long)garmin_data_p;
+       garmin_data_p->timer.function = timeout_handler;
+       garmin_data_p->port = port;
+       garmin_data_p->state = 0;
+       garmin_data_p->count = 0;
+       usb_set_serial_port_data(port, garmin_data_p);
+
+       status = garmin_init_session(port);
+
+       return status;
+}
+
+
+static void garmin_shutdown (struct usb_serial *serial)
+{
+       struct usb_serial_port *port = serial->port[0];
+       struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+
+       dbg("%s", __FUNCTION__);
+
+       usb_kill_urb (port->interrupt_in_urb);
+       del_timer_sync(&garmin_data_p->timer);
+       kfree (garmin_data_p);
+       usb_set_serial_port_data(port, NULL);
+}
+
+
+
+
+
+
+
+/* All of the device info needed */
+static struct usb_serial_device_type garmin_device = {
+       .owner               = THIS_MODULE,
+       .name                = "Garmin GPS usb/tty",
+       .short_name          = "garmin_gps",
+       .id_table            = id_table,
+       .num_interrupt_in    = 1,
+       .num_bulk_in         = 1,
+       .num_bulk_out        = 1,
+       .num_ports           = 1,
+       .open                = garmin_open,
+       .close               = garmin_close,
+       .throttle            = garmin_throttle,
+       .unthrottle          = garmin_unthrottle,
+       .attach              = garmin_attach,
+       .shutdown            = garmin_shutdown,
+       .write               = garmin_write,
+       .write_room          = garmin_write_room,
+       .chars_in_buffer     = garmin_chars_in_buffer,
+       .write_bulk_callback = garmin_write_bulk_callback,
+       .read_bulk_callback  = garmin_read_bulk_callback,
+       .read_int_callback   = garmin_read_int_callback,
+};
+
+
+static int __init garmin_init (void)
+{
+       int retval;
+
+       retval = usb_serial_register(&garmin_device);
+       if (retval)
+               goto failed_garmin_register;
+       retval = usb_register(&garmin_driver);
+       if (retval)
+               goto failed_usb_register;
+       info(DRIVER_DESC " " DRIVER_VERSION);
+
+       return 0;
+failed_usb_register:
+       usb_serial_deregister(&garmin_device);
+failed_garmin_register:
+       return retval;
+}
+
+
+static void __exit garmin_exit (void)
+{
+       usb_deregister (&garmin_driver);
+       usb_serial_deregister (&garmin_device);
+}
+
+
+
+
+module_init(garmin_init);
+module_exit(garmin_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+module_param(initial_mode, int, S_IRUGO);
+MODULE_PARM_DESC(initial_mode, "Initial mode");
+
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
new file mode 100644 (file)
index 0000000..2097793
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * IPWireless 3G UMTS TDD Modem driver (USB connected)
+ *
+ *   Copyright (C) 2004 Roelf Diedericks <roelfd@inet.co.za>
+ *   Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ * All information about the device was acquired using SnoopyPro
+ * on MSFT's O/S, and examing the MSFT drivers' debug output 
+ * (insanely left _on_ in the enduser version)
+ *
+ * It was written out of frustration with the IPWireless USB modem
+ * supplied by Axity3G/Sentech South Africa not supporting
+ * Linux whatsoever.
+ *
+ * Nobody provided any proprietary information that was not already 
+ * available for this device.
+ * 
+ * The modem adheres to the "3GPP TS  27.007 AT command set for 3G 
+ * User Equipment (UE)" standard, available from 
+ * http://www.3gpp.org/ftp/Specs/html-info/27007.htm
+ *
+ * The code was only tested the IPWireless handheld modem distributed
+ * in South Africa by Sentech.
+ * 
+ * It may work for Woosh Inc in .nz too, as it appears they use the
+ * same kit.
+ *
+ * There is still some work to be done in terms of handling 
+ * DCD, DTR, RTS, CTS which are currently faked.
+ * It's good enough for PPP at this point. It's based off all kinds of
+ * code found in usb/serial and usb/class
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+#include "usb-serial.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.3"
+#define DRIVER_AUTHOR  "Roelf Diedericks"
+#define DRIVER_DESC    "IPWireless tty driver"
+
+#define IPW_TTY_MAJOR  240     /* real device node major id, experimental range */
+#define IPW_TTY_MINORS 256     /* we support 256 devices, dunno why, it'd be insane :) */
+
+#define USB_IPW_MAGIC  0x6d02  /* magic number for ipw struct */
+
+
+/* Message sizes */
+#define EVENT_BUFFER_SIZE      0xFF
+#define CHAR2INT16(c1,c0)      (((u32)((c1) & 0xff) << 8) + (u32)((c0) & 0xff))
+#define NUM_BULK_URBS          24
+#define NUM_CONTROL_URBS       16
+
+/* vendor/product pairs that are known work with this driver*/
+#define IPW_VID                0x0bc3
+#define IPW_PID                0x0001
+
+
+/* Vendor commands: */
+
+/* baud rates */
+enum {
+       ipw_sio_b256000 = 0x000e,
+       ipw_sio_b128000 = 0x001d,
+       ipw_sio_b115200 = 0x0020,
+       ipw_sio_b57600  = 0x0040,
+       ipw_sio_b56000  = 0x0042,
+       ipw_sio_b38400  = 0x0060,
+       ipw_sio_b19200  = 0x00c0,
+       ipw_sio_b14400  = 0x0100,
+       ipw_sio_b9600   = 0x0180,
+       ipw_sio_b4800   = 0x0300,
+       ipw_sio_b2400   = 0x0600,
+       ipw_sio_b1200   = 0x0c00,
+       ipw_sio_b600    = 0x1800
+};
+
+/* data bits */
+#define ipw_dtb_7              0x700
+#define ipw_dtb_8              0x810   // ok so the define is misleading, I know, but forces 8,n,1
+                                       // I mean, is there a point to any other setting these days? :) 
+
+/* usb control request types : */
+#define IPW_SIO_RXCTL          0x00    // control bulk rx channel transmissions, value=1/0 (on/off)
+#define IPW_SIO_SET_BAUD       0x01    // set baud, value=requested ipw_sio_bxxxx
+#define IPW_SIO_SET_LINE       0x03    // set databits, parity. value=ipw_dtb_x
+#define IPW_SIO_SET_PIN                0x03    // set/clear dtr/rts value=ipw_pin_xxx
+#define IPW_SIO_POLL           0x08    // get serial port status byte, call with value=0
+#define IPW_SIO_INIT           0x11    // initializes ? value=0 (appears as first thing todo on open)
+#define IPW_SIO_PURGE          0x12    // purge all transmissions?, call with value=numchar_to_purge
+#define IPW_SIO_HANDFLOW       0x13    // set xon/xoff limits value=0, and a buffer of 0x10 bytes
+#define IPW_SIO_SETCHARS       0x13    // set the flowcontrol special chars, value=0, buf=6 bytes, 
+                                       // last 2 bytes contain flowcontrol chars e.g. 00 00 00 00 11 13
+
+/* values used for request IPW_SIO_SET_PIN */
+#define IPW_PIN_SETDTR         0x101
+#define IPW_PIN_SETRTS         0x202
+#define IPW_PIN_CLRDTR         0x100
+#define IPW_PIN_CLRRTS         0x200 // unconfirmed
+
+/* values used for request IPW_SIO_RXCTL */
+#define IPW_RXBULK_ON          1
+#define IPW_RXBULK_OFF         0
+
+/* various 16 byte hardcoded transferbuffers used by flow control */
+#define IPW_BYTES_FLOWINIT     { 0x01, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+/* Interpretation of modem status lines */
+/* These need sorting out by individually connecting pins and checking
+ * results. FIXME!
+ * When data is being sent we see 0x30 in the lower byte; this must
+ * contain DSR and CTS ...
+ */
+#define IPW_DSR                        ((1<<4) | (1<<5))
+#define IPW_CTS                        ((1<<5) | (1<<4))
+
+#define IPW_WANTS_TO_SEND      0x30
+//#define IPW_DTR                      /* Data Terminal Ready */
+//#define IPW_CTS                      /* Clear To Send */
+//#define IPW_CD                       /* Carrier Detect */
+//#define IPW_DSR                      /* Data Set Ready */
+//#define IPW_RxD                      /* Receive pin */
+
+//#define IPW_LE
+//#define IPW_RTS              
+//#define IPW_ST               
+//#define IPW_SR               
+//#define IPW_RI                       /* Ring Indicator */
+
+static struct usb_device_id usb_ipw_ids[] = {
+       { USB_DEVICE(IPW_VID, IPW_PID) },
+       { },
+};
+
+MODULE_DEVICE_TABLE(usb, usb_ipw_ids);
+
+static struct usb_driver usb_ipw_driver = {
+       .owner =        THIS_MODULE,
+       .name =         "ipwtty",
+       .probe =        usb_serial_probe,
+       .disconnect =   usb_serial_disconnect,
+       .id_table =     usb_ipw_ids,
+};
+
+static int debug;
+
+static void ipw_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port = urb->context;
+       unsigned char *data = urb->transfer_buffer;
+       struct tty_struct *tty;
+       int i;
+       int result;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (urb->status) {
+               dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+               return;
+       }
+
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+
+       tty = port->tty;
+       if (tty && urb->actual_length) {
+               for (i = 0; i < urb->actual_length ; ++i) {
+                       /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+                       if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                               tty_flip_buffer_push(tty);
+                       }
+                       /* this doesn't actually push the data through unless tty->low_latency is set */
+                       tty_insert_flip_char(tty, data[i], 0);
+               }
+               tty_flip_buffer_push(tty);
+       }
+
+       /* Continue trying to always read  */
+       usb_fill_bulk_urb (port->read_urb, port->serial->dev,
+                          usb_rcvbulkpipe(port->serial->dev,
+                                          port->bulk_in_endpointAddress),
+                          port->read_urb->transfer_buffer,
+                          port->read_urb->transfer_buffer_length,
+                          ipw_read_bulk_callback, port);
+       result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+       if (result)
+               dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+       return;
+}
+
+static int ipw_open(struct usb_serial_port *port, struct file *filp)
+{
+       struct usb_device *dev = port->serial->dev;
+       u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT;
+       u8 *buf_flow_init;
+       int result;
+
+       dbg("%s", __FUNCTION__);
+
+       buf_flow_init = kmalloc(16, GFP_KERNEL);
+       if (!buf_flow_init)
+               return -ENOMEM;
+       memcpy(buf_flow_init, buf_flow_static, 16);
+
+       if (port->tty)
+               port->tty->low_latency = 1;
+
+       /* --1: Tell the modem to initialize (we think) From sniffs this is always the
+        * first thing that gets sent to the modem during opening of the device */
+       dbg("%s: Sending SIO_INIT (we guess)",__FUNCTION__);
+       result = usb_control_msg(dev, usb_sndctrlpipe(dev,0),
+                                IPW_SIO_INIT,
+                                USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                0,
+                                0, /* index */
+                                NULL,
+                                0,
+                                100*HZ);
+       if (result < 0)
+               dev_err(&port->dev, "Init of modem failed (error = %d)", result);
+
+       /* reset the bulk pipes */
+       usb_clear_halt(dev, usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress));
+       usb_clear_halt(dev, usb_sndbulkpipe(dev, port->bulk_out_endpointAddress));
+
+       /*--2: Start reading from the device */ 
+       dbg("%s: setting up bulk read callback",__FUNCTION__);
+       usb_fill_bulk_urb(port->read_urb, dev,
+                         usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress),
+                         port->bulk_in_buffer,
+                         port->bulk_in_size,
+                         ipw_read_bulk_callback, port);
+       result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+       if (result < 0)
+               dbg("%s - usb_submit_urb(read bulk) failed with status %d", __FUNCTION__, result);
+
+       /*--3: Tell the modem to open the floodgates on the rx bulk channel */
+       dbg("%s:asking modem for RxRead (RXBULK_ON)",__FUNCTION__);
+       result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                                IPW_SIO_RXCTL,
+                                USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                IPW_RXBULK_ON,
+                                0, /* index */
+                                NULL,
+                                0,
+                                100*HZ);
+       if (result < 0) 
+               dev_err(&port->dev, "Enabling bulk RxRead failed (error = %d)", result);
+
+       /*--4: setup the initial flowcontrol */
+       dbg("%s:setting init flowcontrol (%s)",__FUNCTION__,buf_flow_init);
+       result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                                IPW_SIO_HANDFLOW,
+                                USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                0,
+                                0,
+                                buf_flow_init,
+                                0x10,
+                                200*HZ);
+       if (result < 0)
+               dev_err(&port->dev, "initial flowcontrol failed (error = %d)", result);
+
+
+       /*--5: raise the dtr */
+       dbg("%s:raising dtr",__FUNCTION__);
+       result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                                IPW_SIO_SET_PIN,
+                                USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                IPW_PIN_SETDTR,
+                                0,
+                                NULL,
+                                0,
+                                200*HZ);
+       if (result < 0)
+               dev_err(&port->dev, "setting dtr failed (error = %d)", result);
+
+       /*--6: raise the rts */
+       dbg("%s:raising rts",__FUNCTION__);
+       result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                                IPW_SIO_SET_PIN,
+                                USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                IPW_PIN_SETRTS,
+                                0,
+                                NULL,
+                                0,
+                                200*HZ);
+       if (result < 0)
+               dev_err(&port->dev, "setting dtr failed (error = %d)", result);
+       
+       kfree(buf_flow_init);
+       return 0;
+}
+
+static void ipw_close(struct usb_serial_port *port, struct file * filp)
+{
+       struct usb_device *dev = port->serial->dev;
+       int result;
+
+       if (tty_hung_up_p(filp)) {
+               dbg("%s: tty_hung_up_p ...", __FUNCTION__);
+               return;
+       }
+
+       /*--1: drop the dtr */
+       dbg("%s:dropping dtr",__FUNCTION__);
+       result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                                IPW_SIO_SET_PIN,
+                                USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                IPW_PIN_CLRDTR,
+                                0,
+                                NULL,
+                                0,
+                                200*HZ);
+       if (result < 0)
+               dev_err(&port->dev, "dropping dtr failed (error = %d)", result);
+
+       /*--2: drop the rts */
+       dbg("%s:dropping rts",__FUNCTION__);
+       result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                                IPW_SIO_SET_PIN, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                IPW_PIN_CLRRTS,
+                                0,
+                                NULL,
+                                0,
+                                200*HZ);
+       if (result < 0)
+               dev_err(&port->dev, "dropping rts failed (error = %d)", result);
+
+
+       /*--3: purge */
+       dbg("%s:sending purge",__FUNCTION__);
+       result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                                IPW_SIO_PURGE, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                0x03,
+                                0,
+                                NULL,
+                                0,
+                                200*HZ);
+       if (result < 0)
+               dev_err(&port->dev, "purge failed (error = %d)", result);
+
+
+       /* send RXBULK_off (tell modem to stop transmitting bulk data on rx chan) */
+       result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                                IPW_SIO_RXCTL,
+                                USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                IPW_RXBULK_OFF,
+                                0, /* index */
+                                NULL,
+                                0,
+                                100*HZ);
+
+       if (result < 0)
+               dev_err(&port->dev, "Disabling bulk RxRead failed (error = %d)", result);
+
+       /* shutdown any in-flight urbs that we know about */
+       usb_kill_urb(port->read_urb);
+       usb_kill_urb(port->write_urb);
+}
+
+static void ipw_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port = urb->context;
+
+       dbg("%s", __FUNCTION__);
+
+       if (urb->status)
+               dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+
+       schedule_work(&port->work);
+}
+
+static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int count)
+{
+       struct usb_device *dev = port->serial->dev;
+       int ret;
+
+       dbg("%s: TOP: count=%d, in_interrupt=%ld", __FUNCTION__,
+               count, in_interrupt() );
+
+       if (count == 0) {
+               dbg("%s - write request of 0 bytes", __FUNCTION__);
+               return 0;
+       }
+       
+       /* Racy and broken, FIXME properly! */
+       if (port->write_urb->status == -EINPROGRESS)
+               return 0;
+
+       count = min(count, port->bulk_out_size);
+       memcpy(port->bulk_out_buffer, buf, count);
+
+       dbg("%s count now:%d", __FUNCTION__, count);
+       
+       usb_fill_bulk_urb(port->write_urb, dev,
+                         usb_sndbulkpipe(dev, port->bulk_out_endpointAddress),
+                         port->write_urb->transfer_buffer,
+                         count,
+                         ipw_write_bulk_callback,
+                         port);
+
+       ret = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+       if (ret != 0) {
+               dbg("%s - usb_submit_urb(write bulk) failed with error = %d", __FUNCTION__, ret);
+               return ret;
+       }
+
+       dbg("%s returning %d", __FUNCTION__, count);
+       return count;
+} 
+
+static int ipw_probe(struct usb_serial_port *port)
+{
+       return 0;
+}
+
+static int ipw_disconnect(struct usb_serial_port *port)
+{
+       usb_set_serial_port_data(port, NULL);
+       return 0;
+}
+
+static struct usb_serial_device_type ipw_device = {
+       .owner =                THIS_MODULE,
+       .name =                 "IPWireless converter",
+       .short_name =           "ipw",
+       .id_table =             usb_ipw_ids,
+       .num_interrupt_in =     NUM_DONT_CARE,
+       .num_bulk_in =          1,
+       .num_bulk_out =         1,
+       .num_ports =            1,
+       .open =                 ipw_open,
+       .close =                ipw_close,
+       .port_probe =           ipw_probe,
+       .port_remove =          ipw_disconnect,
+       .write =                ipw_write,
+       .write_bulk_callback =  ipw_write_bulk_callback,
+       .read_bulk_callback =   ipw_read_bulk_callback,
+};
+
+
+
+int usb_ipw_init(void)
+{
+       int retval;
+
+       retval = usb_serial_register(&ipw_device);
+       if (retval)
+               return retval;
+       retval = usb_register(&usb_ipw_driver);
+       if (retval) {
+               usb_serial_deregister(&ipw_device);
+               return retval;
+       }
+       info(DRIVER_DESC " " DRIVER_VERSION);
+       return 0;
+}
+
+void usb_ipw_exit(void)
+{
+       usb_deregister(&usb_ipw_driver);
+       usb_serial_deregister(&ipw_device);
+}
+
+module_init(usb_ipw_init);
+module_exit(usb_ipw_exit);
+
+/* Module information */
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/ti_fw_3410.h b/drivers/usb/serial/ti_fw_3410.h
new file mode 100644 (file)
index 0000000..71e8857
--- /dev/null
@@ -0,0 +1,885 @@
+/* vi: ts=8 sw=8
+ *
+ * TI 3410 USB Serial Driver Firmware Header
+ *
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * 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 _TI_FW_3410_H_
+#define _TI_FW_3410_H_
+
+/* firmware 9/10/04 FW3410_Special_StartWdogOnStartPort */
+
+static unsigned char ti_fw_3410[] = {
+0xC2, 0x35,    /* firmware image length excluding header, little endian */
+0x00,          /* placeholder for checksum */
+
+0x02,0x00,0x1e,0x02,0x1a,0xdb,0xff,0xff,0xff,0xff,0xff,0x02,0x32,0xcb,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x02,0x33,0x76,0x75,0x81,
+0xce,0x90,0xfd,0xe8,0x85,0x83,0xa0,0x12,0x34,0xea,0xec,0x4d,0x60,0x6a,0x78,0xab,
+0x80,0x03,0x76,0x00,0x18,0xb8,0x9c,0xfa,0x78,0x7f,0x80,0x03,0x76,0x00,0x18,0xb8,
+0x65,0xfa,0x78,0x20,0x80,0x03,0x76,0x00,0x18,0xb8,0x20,0xfa,0x90,0xfd,0xdd,0xae,
+0x83,0xaf,0x82,0x90,0xfb,0xf8,0x12,0x00,0xa1,0x60,0x05,0xe4,0xf0,0xa3,0x80,0xf6,
+0x90,0xfd,0xe8,0xa8,0x82,0x90,0xfd,0xe8,0xa9,0x82,0xe8,0xc3,0x99,0x50,0x05,0x76,
+0x00,0x08,0x80,0xf6,0x90,0x00,0xff,0x12,0x00,0xaa,0x90,0x01,0x03,0x12,0x00,0xaa,
+0x90,0x01,0x07,0x12,0x00,0xaa,0x90,0x01,0x0b,0x12,0x00,0xc8,0x90,0x01,0x11,0x12,
+0x00,0xc8,0x90,0x01,0x17,0x12,0x00,0xc8,0x75,0xd0,0x00,0x12,0x33,0xc8,0x02,0x01,
+0x1d,0xef,0x65,0x82,0x70,0x03,0xee,0x65,0x83,0x22,0xe4,0x93,0xf8,0x74,0x01,0x93,
+0xf9,0x74,0x02,0x93,0xfe,0x74,0x03,0x93,0xf5,0x82,0x8e,0x83,0xe8,0x69,0x70,0x01,
+0x22,0xe4,0x93,0xf6,0xa3,0x08,0x80,0xf4,0xe4,0x93,0xfc,0x74,0x01,0x93,0xfd,0x74,
+0x02,0x93,0xfe,0x74,0x03,0x93,0xff,0x74,0x04,0x93,0xf8,0x74,0x05,0x93,0xf5,0x82,
+0x88,0x83,0x12,0x00,0xa1,0x70,0x01,0x22,0xe4,0x93,0xa3,0xa8,0x83,0xa9,0x82,0x8c,
+0x83,0x8d,0x82,0xf0,0xa3,0xac,0x83,0xad,0x82,0x88,0x83,0x89,0x82,0x80,0xe3,0x21,
+0x21,0x04,0x92,0x80,0x80,0x04,0x92,0xac,0xae,0x04,0x92,0xfd,0xe8,0x04,0x94,0x04,
+0x94,0xfb,0xf3,0x04,0x99,0x04,0x94,0xfb,0xf3,0x04,0xf9,0x04,0xf9,0x80,0xfe,0xd0,
+0xf0,0x30,0xf0,0x09,0x20,0xf3,0x03,0xf6,0x80,0x10,0xf7,0x80,0x0d,0x30,0xf1,0x09,
+0x20,0xf3,0x03,0xf2,0x80,0x04,0xf3,0x80,0x01,0xf0,0x20,0xf4,0x04,0xfc,0xd0,0xe0,
+0xcc,0x22,0xcc,0xc0,0xe0,0x12,0x01,0x5a,0x02,0x01,0x4b,0xbc,0x00,0x05,0xd0,0xf0,
+0xac,0xf0,0x22,0xc3,0x13,0xdc,0xfc,0x02,0x01,0x21,0xbf,0x00,0x09,0xed,0x25,0x82,
+0x75,0xf0,0x01,0xf8,0xe6,0x22,0xbf,0x01,0x0f,0xed,0x25,0x82,0xf5,0x82,0xee,0x35,
+0x83,0xf5,0x83,0x75,0xf0,0x04,0xe0,0x22,0xed,0x25,0x82,0x75,0xf0,0x02,0xf8,0xe2,
+0x22,0xd0,0x83,0xd0,0x82,0xf5,0xf0,0xc3,0xe4,0x93,0xa3,0xc5,0xf0,0x95,0xf0,0xc0,
+0xe0,0xc3,0xd0,0xf0,0xe4,0x93,0xa3,0x95,0xf0,0x40,0x12,0xa3,0xa3,0xc3,0xe5,0xf0,
+0x33,0x50,0x02,0x05,0x83,0x25,0x82,0xf5,0x82,0x50,0x02,0x05,0x83,0x74,0x01,0x93,
+0xc0,0xe0,0xe4,0x93,0xc0,0xe0,0x22,0xd0,0x83,0xd0,0x82,0xf5,0xf0,0xe4,0x93,0x70,
+0x09,0x74,0x01,0x93,0x70,0x04,0xa3,0xa3,0x80,0x0c,0x74,0x02,0x93,0x65,0xf0,0x60,
+0x05,0xa3,0xa3,0xa3,0x80,0xe7,0x74,0x01,0x93,0xc0,0xe0,0xe4,0x93,0xc0,0xe0,0x22,
+0x12,0x02,0x5b,0x02,0x01,0xf2,0x12,0x02,0xaf,0x02,0x01,0xf2,0x12,0x02,0xd3,0x02,
+0x01,0xf2,0x30,0xe0,0x07,0x20,0xe3,0x02,0xe6,0x22,0xe7,0x22,0x30,0xe1,0x07,0x20,
+0xe3,0x02,0xe2,0x22,0xe3,0x22,0x30,0xe2,0x02,0xe0,0x22,0xe4,0x93,0x22,0x12,0x02,
+0xd3,0x02,0x02,0x1a,0x12,0x02,0xaf,0x02,0x02,0x1a,0xab,0xf0,0x12,0x02,0x24,0xcb,
+0xc5,0xf0,0xcb,0x22,0x30,0xe0,0x10,0x20,0xe3,0x06,0xe6,0xf5,0xf0,0x08,0xe6,0x22,
+0xe7,0xf5,0xf0,0x09,0xe7,0x19,0x22,0x30,0xe1,0x10,0x20,0xe3,0x06,0xe2,0xf5,0xf0,
+0x08,0xe2,0x22,0xe3,0xf5,0xf0,0x09,0xe3,0x19,0x22,0x30,0xe2,0x06,0xe0,0xf5,0xf0,
+0xa3,0xe0,0x22,0xe4,0x93,0xf5,0xf0,0x74,0x01,0x93,0x22,0xbb,0x00,0x03,0x74,0x09,
+0x22,0xbb,0x01,0x07,0x89,0x82,0x8a,0x83,0x74,0x04,0x22,0xbb,0x02,0x07,0x89,0x82,
+0x8a,0x83,0x74,0x10,0x22,0x74,0x0a,0x22,0x02,0x02,0x7b,0xbb,0x00,0x07,0xe9,0x25,
+0x82,0xf8,0x74,0x01,0x22,0xbb,0x01,0x0d,0xe9,0x25,0x82,0xf5,0x82,0xea,0x35,0x83,
+0xf5,0x83,0x74,0x04,0x22,0xbb,0x02,0x0d,0xe9,0x25,0x82,0xf5,0x82,0xea,0x35,0x83,
+0xf5,0x83,0x74,0x10,0x22,0xe9,0x25,0x82,0xf8,0x74,0x02,0x22,0x02,0x02,0xaf,0xbf,
+0x00,0x05,0xed,0xf8,0x74,0x01,0x22,0xbf,0x01,0x07,0x8d,0x82,0x8e,0x83,0x74,0x04,
+0x22,0xbf,0x02,0x07,0x8d,0x82,0x8e,0x83,0x74,0x10,0x22,0xed,0xf8,0x74,0x02,0x22,
+0x02,0x02,0xd3,0xbf,0x00,0x07,0xed,0x25,0x82,0xf8,0x74,0x01,0x22,0xbf,0x01,0x0d,
+0xed,0x25,0x82,0xf5,0x82,0xee,0x35,0x83,0xf5,0x83,0x74,0x04,0x22,0xbf,0x02,0x0d,
+0xed,0x25,0x82,0xf5,0x82,0xee,0x35,0x83,0xf5,0x83,0x74,0x10,0x22,0xed,0x25,0x82,
+0xf8,0x74,0x02,0x22,0x02,0x03,0x07,0xc0,0xe0,0x12,0x02,0x5b,0x02,0x03,0x1f,0xc0,
+0xe0,0x12,0x02,0xaf,0x02,0x03,0x1f,0xc0,0xe0,0x12,0x02,0xd3,0x02,0x03,0x1f,0x30,
+0xe0,0x0b,0x20,0xe3,0x04,0xd0,0xe0,0xf6,0x22,0xd0,0xe0,0xf7,0x22,0x30,0xe1,0x0b,
+0x20,0xe3,0x04,0xd0,0xe0,0xf2,0x22,0xd0,0xe0,0xf3,0x22,0xd0,0xe0,0xf0,0x22,0xc9,
+0xcd,0xc9,0xca,0xce,0xca,0xcb,0xcf,0xcb,0x12,0x03,0x52,0xed,0xf9,0xee,0xfa,0xef,
+0xfb,0x22,0xbb,0x00,0x2f,0xbf,0x00,0x0a,0xfa,0xed,0xf8,0xe7,0xf6,0x08,0x09,0xda,
+0xfa,0x22,0xbf,0x01,0x12,0x8d,0x82,0x8e,0x83,0xf8,0x02,0x03,0x6f,0x09,0xa3,0xe7,
+0xf0,0xd8,0xfa,0x22,0x02,0x03,0x7a,0xfa,0xed,0xf8,0xe7,0xf2,0x08,0x09,0xda,0xfa,
+0x22,0x02,0x03,0x84,0xbb,0x01,0x4d,0xbf,0x00,0x14,0x89,0x82,0x8a,0x83,0xf9,0xed,
+0xf8,0x02,0x03,0x96,0x08,0xa3,0xe0,0xf6,0xd9,0xfa,0x22,0x02,0x03,0xa7,0xbf,0x01,
+0x22,0x8d,0x82,0x8e,0x83,0xfb,0x08,0xc9,0xc5,0x82,0xc9,0xca,0xc5,0x83,0xca,0xe0,
+0xa3,0xc9,0xc5,0x82,0xc9,0xca,0xc5,0x83,0xca,0xf0,0xa3,0xdb,0xea,0xd8,0xe8,0x22,
+0x02,0x03,0xca,0x8d,0x82,0x8e,0x83,0xf9,0xed,0xf8,0xe0,0xf2,0x08,0xa3,0xd9,0xfa,
+0x22,0x02,0x03,0xd4,0xbb,0x02,0x4d,0xbf,0x00,0x12,0x89,0x82,0x8a,0x83,0xf9,0xed,
+0xf8,0x02,0x03,0xe6,0x08,0xa3,0xe4,0x93,0xf6,0xd9,0xf9,0x22,0xbf,0x01,0x23,0x8d,
+0x82,0x8e,0x83,0xfb,0x08,0xc9,0xc5,0x82,0xc9,0xca,0xc5,0x83,0xca,0xe4,0x93,0xa3,
+0xc9,0xc5,0x82,0xc9,0xca,0xc5,0x83,0xca,0xf0,0xa3,0xdb,0xe9,0xd8,0xe7,0x22,0x02,
+0x04,0x19,0x89,0x82,0x8a,0x83,0xf9,0xed,0xf8,0xe4,0x93,0xf2,0x08,0xa3,0xd9,0xf9,
+0x22,0x02,0x04,0x2a,0xbf,0x00,0x0d,0xfa,0xed,0xf8,0xe3,0xf6,0x08,0x09,0xda,0xfa,
+0x22,0x02,0x04,0x34,0xbf,0x01,0x12,0x8d,0x82,0x8e,0x83,0xf8,0x02,0x04,0x41,0x09,
+0xa3,0xe3,0xf0,0xd8,0xfa,0x22,0x02,0x04,0x4c,0xfa,0xed,0xf8,0xe3,0xf2,0x08,0x09,
+0xda,0xfa,0x22,0x02,0x04,0x56,0xe6,0xfb,0x08,0xe6,0xfa,0x08,0xe6,0xf9,0x04,0xf6,
+0x18,0x70,0x01,0x06,0x22,0xe6,0xff,0x08,0xe6,0xfe,0x08,0xe6,0xfd,0x22,0xef,0xf0,
+0xa3,0xee,0xf0,0xa3,0xed,0xf0,0x22,0xeb,0xf0,0xa3,0xea,0xf0,0xa3,0xe9,0xf0,0x22,
+0xe0,0xff,0xa3,0xe0,0xfe,0xa3,0xe0,0xfd,0x22,0xe0,0xfb,0xa3,0xe0,0xfa,0xa3,0xe0,
+0xf9,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xf9,0x00,0x61,0x05,0x68,0x00,
+0x26,0x05,0x8f,0x00,0x33,0x0a,0x00,0x00,0x61,0x0a,0x6c,0x00,0x66,0x15,0x1d,0x00,
+0x61,0x0c,0xf0,0x00,0x61,0x09,0xa0,0x00,0x61,0x09,0xd7,0x00,0x61,0x0d,0xb7,0x00,
+0x61,0x0b,0xe8,0x00,0x61,0x0a,0x13,0x00,0x61,0x0a,0x48,0x00,0x61,0x17,0x15,0x00,
+0x33,0x17,0x28,0x00,0x34,0x1d,0xf6,0x00,0x43,0x1e,0xa1,0x00,0x44,0x20,0x0e,0x00,
+0x44,0x1f,0xfc,0x00,0x47,0x1e,0xc8,0x00,0x47,0x1f,0x6d,0x00,0x4d,0x1f,0xbe,0x00,
+0x4f,0x1e,0xea,0x00,0x58,0x32,0x56,0x00,0x61,0x7c,0xcc,0x7d,0xff,0x12,0x1c,0xa7,
+0x22,0x90,0xff,0xfc,0xe0,0x20,0xe7,0x2d,0xc2,0xaf,0xae,0x59,0xaf,0x58,0x75,0x5a,
+0x20,0xe5,0x5a,0x14,0xc5,0x5a,0x60,0x19,0xe4,0xfe,0x7f,0x05,0xee,0x4f,0xce,0x24,
+0xff,0xce,0xcf,0x34,0xff,0xcf,0x60,0x07,0xe4,0x90,0xff,0x92,0xf0,0x80,0xed,0x80,
+0xe0,0x8e,0x59,0x8f,0x58,0x22,0x12,0x05,0x01,0x7d,0x07,0x7c,0xb7,0x12,0x32,0x72,
+0x7d,0x0f,0x7c,0x6e,0x12,0x32,0x8c,0x78,0x9d,0x7a,0x06,0xe4,0xf6,0x08,0xda,0xfc,
+0x7a,0x06,0x12,0x05,0xc4,0x7c,0x03,0x12,0x0e,0x4c,0x12,0x21,0x4a,0xe4,0xfe,0xff,
+0x7c,0x0f,0x12,0x31,0xfb,0xd2,0xa8,0x22,0x12,0x30,0xe6,0xe4,0x90,0xfc,0x38,0xf0,
+0x90,0xff,0xf0,0xe0,0x30,0xe4,0x08,0x74,0x01,0x90,0xfc,0x39,0xf0,0x80,0x05,0xe4,
+0x90,0xfc,0x39,0xf0,0x7d,0x0a,0x7c,0x00,0x12,0x25,0x26,0x12,0x31,0x69,0x22,0x12,
+0x30,0xe6,0x90,0xfc,0x39,0xe0,0x14,0x70,0x0e,0x90,0xff,0xf0,0xe0,0x44,0x10,0xf0,
+0x7c,0x00,0x12,0x25,0xbf,0x80,0x19,0x90,0xfc,0x39,0xe0,0x70,0x0e,0x90,0xff,0xf0,
+0xe0,0x54,0xef,0xf0,0x7c,0x00,0x12,0x25,0xbf,0x80,0x05,0x7c,0x17,0x12,0x25,0xbf,
+0x12,0x31,0x69,0x22,0x90,0xff,0xf0,0xe0,0x54,0xab,0xf0,0x90,0xff,0xf0,0xe0,0x44,
+0x20,0xf0,0x22,0x8c,0x37,0x8d,0x36,0x78,0x82,0xed,0xf6,0x08,0xec,0xf6,0xed,0xfe,
+0xec,0xfd,0x7f,0x01,0x90,0x00,0x05,0x12,0x01,0xec,0x78,0x80,0xf6,0x78,0x82,0xe6,
+0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x04,0x12,0x01,0xec,
+0x54,0x0f,0xfc,0x7d,0x80,0x12,0x17,0x46,0x78,0x80,0xe6,0x70,0x0d,0xad,0x3a,0xae,
+0x39,0xaf,0x38,0xe4,0x12,0x03,0x0f,0x7c,0x08,0x22,0x90,0xff,0xf0,0xe0,0x54,0xfe,
+0xf0,0x90,0xff,0xf0,0xe0,0x54,0xfd,0xf0,0x80,0x1e,0x78,0x82,0xe6,0xfd,0x08,0xe6,
+0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x08,0x12,0x02,0x0e,0x25,0xe0,0x44,
+0x01,0x90,0xff,0xf3,0xf0,0x02,0x06,0xd0,0x78,0x82,0xe6,0xfd,0x08,0xe6,0xfc,0xed,
+0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x06,0x12,0x02,0x0e,0x54,0xfe,0x90,0xff,0xf3,
+0xf0,0x80,0x2b,0x78,0x82,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,
+0x90,0x00,0x08,0x12,0x02,0x0e,0xfa,0xeb,0x90,0xff,0xf1,0xf0,0x12,0x08,0xbf,0x40,
+0x0d,0xad,0x3a,0xae,0x39,0xaf,0x38,0xe4,0x12,0x03,0x0f,0x7c,0x18,0x22,0x78,0x82,
+0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x08,0x12,0x02,
+0x0e,0x90,0xff,0xf1,0xf0,0x12,0x08,0xbf,0x40,0x0d,0xad,0x3a,0xae,0x39,0xaf,0x38,
+0xe4,0x12,0x03,0x0f,0x7c,0x18,0x22,0x78,0x82,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,
+0xec,0xfd,0x7f,0x01,0x90,0x00,0x06,0x12,0x02,0x0e,0x44,0x01,0x90,0xff,0xf3,0xf0,
+0x78,0x83,0xe6,0x24,0x03,0xf6,0x18,0xe6,0x34,0x00,0xf6,0x78,0x80,0xe6,0x24,0xfe,
+0x50,0x09,0x90,0xff,0xf0,0xe0,0x54,0xfd,0xf0,0x80,0x07,0x90,0xff,0xf0,0xe0,0x44,
+0x02,0xf0,0xe4,0x90,0xff,0xf1,0xf0,0x78,0x81,0x76,0x00,0x78,0x80,0xe6,0x24,0xff,
+0xfc,0xe4,0x34,0xff,0xfd,0x78,0x81,0xe6,0x7f,0x00,0xfe,0xec,0xd3,0x9e,0xef,0x64,
+0x80,0xcd,0x64,0x80,0x9d,0x40,0x2f,0x12,0x08,0xa4,0x40,0x0f,0x78,0x81,0xe6,0xad,
+0x3a,0xae,0x39,0xaf,0x38,0x12,0x03,0x0f,0x7c,0x18,0x22,0x90,0xff,0xf2,0xe0,0xfc,
+0x78,0x82,0x86,0x83,0x08,0x86,0x82,0xec,0xf0,0x78,0x81,0x06,0xa3,0x78,0x82,0xa6,
+0x83,0x08,0xa6,0x82,0x80,0xb5,0x12,0x08,0xa4,0x40,0x0f,0x78,0x81,0xe6,0xad,0x3a,
+0xae,0x39,0xaf,0x38,0x12,0x03,0x0f,0x7c,0x18,0x22,0x90,0xff,0xf2,0xe0,0xfc,0x78,
+0x82,0x86,0x83,0x08,0x86,0x82,0xec,0xf0,0x78,0x80,0xe6,0xad,0x3a,0xae,0x39,0xaf,
+0x38,0x12,0x03,0x0f,0x7c,0x00,0x22,0x8c,0x37,0x8d,0x36,0x78,0x82,0xed,0xf6,0x08,
+0xec,0xf6,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x05,0x12,0x01,0xec,0x78,0x81,
+0xf6,0x78,0x82,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,
+0x04,0x12,0x01,0xec,0x54,0x0f,0xfc,0x7d,0x81,0x12,0x17,0x46,0x78,0x81,0xe6,0x70,
+0x03,0x7c,0x08,0x22,0x90,0xff,0xf0,0xe0,0x54,0xfe,0xf0,0x90,0xff,0xf0,0xe0,0x54,
+0xfd,0xf0,0x80,0x1b,0x78,0x82,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,
+0x01,0x90,0x00,0x08,0x12,0x02,0x0e,0x25,0xe0,0x90,0xff,0xf3,0xf0,0x80,0x5b,0x78,
+0x82,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x06,0x12,
+0x02,0x0e,0x54,0xfe,0x90,0xff,0xf3,0xf0,0x80,0x21,0x78,0x82,0xe6,0xfd,0x08,0xe6,
+0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x08,0x12,0x02,0x0e,0xfa,0xeb,0x90,
+0xff,0xf1,0xf0,0x12,0x08,0xbf,0x40,0x03,0x7c,0x18,0x22,0x78,0x82,0xe6,0xfd,0x08,
+0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x08,0x12,0x02,0x0e,0x90,0xff,
+0xf1,0xf0,0x12,0x08,0xbf,0x40,0x03,0x7c,0x18,0x22,0x78,0x83,0xe6,0x24,0x0a,0xf6,
+0x18,0xe6,0x34,0x00,0xf6,0x78,0x80,0x76,0x00,0x78,0x81,0xe6,0x24,0xff,0xfc,0xe4,
+0x34,0xff,0xfd,0x78,0x80,0xe6,0x7f,0x00,0xfe,0xec,0xd3,0x9e,0xef,0x64,0x80,0xcd,
+0x64,0x80,0x9d,0x40,0x21,0x78,0x82,0x86,0x83,0x08,0x86,0x82,0xe0,0x90,0xff,0xf1,
+0xf0,0x12,0x08,0xbf,0x40,0x03,0x7c,0x18,0x22,0x78,0x80,0x06,0x78,0x83,0x06,0xe6,
+0x18,0x70,0x01,0x06,0x80,0xc3,0x90,0xff,0xf0,0xe0,0x44,0x01,0xf0,0x78,0x82,0x86,
+0x83,0x08,0x86,0x82,0xe0,0x90,0xff,0xf1,0xf0,0x12,0x08,0xbf,0x40,0x03,0x7c,0x18,
+0x22,0x7c,0x00,0x22,0x90,0xff,0xf0,0xe0,0x20,0xe7,0x12,0x90,0xff,0xf0,0xe0,0x30,
+0xe5,0x09,0x90,0xff,0xf0,0xe0,0x44,0x20,0xf0,0xc3,0x22,0x80,0xe7,0xd3,0x22,0x90,
+0xff,0xf0,0xe0,0x20,0xe3,0x12,0x90,0xff,0xf0,0xe0,0x30,0xe5,0x09,0x90,0xff,0xf0,
+0xe0,0x44,0x20,0xf0,0xc3,0x22,0x80,0xe7,0xd3,0x22,0x8c,0x42,0x8d,0x41,0x7c,0x00,
+0xed,0x54,0xf0,0xfd,0xec,0x70,0x03,0xed,0x64,0x30,0x70,0x05,0x75,0x3e,0x03,0x80,
+0x03,0x75,0x3e,0x04,0xac,0x3e,0x12,0x0f,0x69,0x75,0x83,0x00,0x85,0x83,0x40,0xe5,
+0x41,0x54,0x0f,0xf5,0x3f,0xe5,0x40,0x70,0x04,0xe5,0x3f,0x64,0x03,0x70,0x35,0xe5,
+0x3e,0x24,0xfd,0x75,0xf0,0x0a,0xa4,0x24,0x02,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,
+0xe0,0x30,0xe6,0x05,0x12,0x10,0x4b,0x80,0x19,0xe5,0x3e,0x24,0x9d,0xf8,0xc6,0x54,
+0xfb,0xf6,0x78,0xa9,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0x74,
+0x0f,0xf0,0x80,0x59,0xe5,0x40,0x70,0x04,0xe5,0x3f,0x64,0x04,0x70,0x48,0xe5,0x3e,
+0x24,0xfd,0x75,0xf0,0x0a,0xa4,0x24,0x02,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xe0,
+0x30,0xe5,0x07,0xac,0x42,0xad,0x41,0x12,0x1c,0x3c,0xe5,0x42,0x30,0xe2,0x15,0x78,
+0xad,0xe6,0x30,0xe0,0x0f,0x78,0xad,0xe6,0x30,0xe1,0x09,0xe4,0xff,0x04,0xfe,0x7c,
+0x04,0x12,0x31,0xfb,0x78,0xa9,0xe6,0x24,0x06,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0x74,0x0f,0xf0,0x80,0x07,0xe4,0xfc,0x7d,0xee,0x12,0x1c,0x3c,0xc2,0x03,0x22,
+0x12,0x30,0xe6,0x12,0x0f,0x69,0x78,0xa9,0xe6,0x24,0x06,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xe0,0x90,0xfc,0x38,0xf0,0x78,0xa9,0xe6,0x24,0x05,0xf5,0x82,0x18,
+0xe6,0x34,0x00,0xf5,0x83,0xe0,0x90,0xfc,0x39,0xf0,0xc2,0x03,0x7d,0x02,0x7c,0x00,
+0x12,0x25,0x26,0x12,0x31,0x69,0x22,0x12,0x30,0xe6,0x78,0x95,0xec,0xf6,0xec,0x24,
+0x9d,0xf8,0xe6,0x30,0xe1,0x07,0x7c,0x13,0x12,0x25,0xbf,0x80,0x0f,0x90,0xfc,0x39,
+0xe0,0xfd,0x78,0x95,0xe6,0xfc,0x12,0x13,0xc8,0x12,0x25,0xbf,0x12,0x31,0x69,0x22,
+0x12,0x30,0xe6,0x78,0x95,0xec,0xf6,0x7d,0x00,0x12,0x0f,0x09,0x12,0x25,0xbf,0x12,
+0x31,0x69,0x22,0x12,0x30,0xe6,0x78,0x95,0xec,0xf6,0xec,0x24,0x9d,0xf8,0xe6,0x30,
+0xe2,0x07,0x7c,0x13,0x12,0x25,0xbf,0x80,0x1b,0x78,0x95,0xe6,0x24,0x9d,0xf8,0xe6,
+0x20,0xe1,0x07,0x7c,0x12,0x12,0x25,0xbf,0x80,0x0a,0x78,0x95,0xe6,0xfc,0x12,0x13,
+0xec,0x12,0x25,0xbf,0x12,0x31,0x69,0x22,0x12,0x30,0xe6,0x78,0x95,0xec,0xf6,0xec,
+0x24,0x9d,0xf8,0xe6,0x20,0xe2,0x07,0x7c,0x11,0x12,0x25,0xbf,0x80,0x0a,0x78,0x95,
+0xe6,0xfc,0x12,0x14,0xed,0x12,0x25,0xbf,0x12,0x31,0x69,0x22,0x12,0x30,0xe6,0x78,
+0x95,0xec,0xf6,0x12,0x0f,0x69,0x78,0xa9,0xe6,0x24,0x09,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xe0,0x90,0xfc,0x3f,0xf0,0x78,0xa9,0xe6,0x24,0x0a,0xf5,0x82,0x18,
+0xe6,0x34,0x00,0xf5,0x83,0xe0,0x90,0xfc,0x40,0xf0,0x78,0xa9,0xe6,0x24,0x03,0xf5,
+0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xfc,0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xf5,0x62,0x78,0xa9,0xe6,0x24,0x02,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xf5,0x63,0x8c,0x61,0xe4,0xec,0x33,0x33,0x54,
+0x01,0x78,0x95,0xf6,0x60,0x08,0xe5,0x62,0x30,0xe1,0x03,0x78,0x95,0x06,0x78,0x95,
+0xe6,0x90,0xfc,0x41,0xf0,0x78,0xa7,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0xfd,0xa3,0xe0,0x54,0x0c,0xfc,0xed,0x54,0xe6,0x8c,0x65,0xf5,0x64,
+0xe5,0x61,0x30,0xe5,0x03,0x43,0x65,0x01,0xe5,0x62,0x20,0xe5,0x0e,0xe5,0x61,0x54,
+0x7f,0x70,0x08,0xe5,0x61,0x20,0xe7,0x03,0x43,0x65,0x02,0xe5,0x61,0x30,0xe3,0x03,
+0x43,0x65,0x10,0xe5,0x61,0x30,0xe2,0x03,0x43,0x65,0x20,0xe5,0x61,0x54,0x03,0x60,
+0x03,0x43,0x65,0x40,0xe5,0x61,0x30,0xe1,0x03,0x43,0x65,0x80,0xe5,0x61,0x30,0xe4,
+0x03,0x43,0x64,0x01,0xe5,0x61,0x30,0xe6,0x03,0x43,0x64,0x08,0xe5,0x62,0x20,0xe4,
+0x0e,0xe5,0x61,0x54,0x7f,0x70,0x08,0xe5,0x61,0x20,0xe7,0x03,0x43,0x64,0x10,0x53,
+0x65,0xfb,0x53,0x64,0x79,0xad,0x64,0xe5,0x65,0x90,0xfc,0x3a,0xcd,0xf0,0xa3,0xcd,
+0xf0,0xe5,0x63,0x30,0xe3,0x0d,0xe5,0x63,0x54,0x30,0xc4,0x54,0x0f,0x90,0xfc,0x3d,
+0xf0,0x80,0x05,0xe4,0x90,0xfc,0x3d,0xf0,0xe5,0x63,0x54,0x03,0x90,0xfc,0x3c,0xf0,
+0xe5,0x63,0x54,0x04,0xc3,0x13,0x90,0xfc,0x3e,0xf0,0x90,0xfc,0x3c,0xe0,0x70,0x0e,
+0x7d,0x35,0x7e,0xfc,0x7f,0x01,0x74,0x01,0x90,0x00,0x09,0x12,0x01,0x42,0x78,0xa9,
+0xe6,0x24,0x08,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x7c,0x00,0xfd,0x78,
+0xa9,0xe6,0x24,0x07,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x7f,0x00,0x4c,
+0xfe,0xef,0x4d,0x90,0xfc,0x38,0xf0,0xa3,0xce,0xf0,0xce,0xc2,0x03,0x7d,0x0a,0x7c,
+0x00,0x12,0x25,0x26,0x12,0x31,0x69,0x22,0x12,0x30,0xe6,0x78,0x95,0xec,0xf6,0x78,
+0x9a,0x76,0x01,0x08,0x76,0xfc,0x08,0x76,0x38,0x78,0x97,0x76,0x0c,0x78,0x9a,0x12,
+0x04,0x65,0x12,0x02,0x14,0x78,0x98,0xcb,0xf6,0xcb,0x08,0xf6,0x7f,0x00,0xef,0x24,
+0xea,0x40,0x1f,0xe4,0xef,0x25,0xe0,0x90,0x35,0x2c,0xfd,0x93,0xcd,0x04,0x93,0x78,
+0x99,0x66,0x70,0x03,0xed,0x18,0x66,0x70,0x06,0x78,0x97,0x76,0x00,0x80,0x03,0x0f,
+0x80,0xdc,0x78,0x96,0xef,0xf6,0x78,0x9a,0x12,0x04,0x65,0x90,0x00,0x02,0x12,0x02,
+0x0e,0x78,0x98,0xcb,0xf6,0xcb,0x08,0xf6,0x54,0x04,0xcb,0x54,0x86,0x4b,0x60,0x04,
+0x78,0x97,0x76,0x0b,0x78,0x99,0xe6,0x30,0xe3,0x13,0x78,0x9a,0x12,0x04,0x65,0x90,
+0x00,0x05,0x12,0x01,0xec,0x24,0xfb,0x50,0x04,0x78,0x97,0x76,0x0d,0x78,0x99,0xe6,
+0x54,0xc0,0x7d,0x00,0x64,0xc0,0x4d,0x70,0x04,0x78,0x97,0x76,0x0b,0x78,0x9a,0x12,
+0x04,0x65,0x90,0x00,0x04,0x12,0x01,0xec,0x24,0xfc,0x50,0x04,0x78,0x97,0x76,0x0f,
+0x78,0x9a,0x12,0x04,0x65,0x90,0x00,0x06,0x12,0x01,0xec,0x24,0xfd,0x50,0x04,0x78,
+0x97,0x76,0x0e,0x78,0x9a,0x12,0x04,0x65,0x90,0x00,0x09,0x12,0x01,0xec,0x24,0xfd,
+0x50,0x04,0x78,0x97,0x76,0x0a,0x78,0x97,0xe6,0x70,0x2a,0x78,0x95,0xe6,0xfc,0x12,
+0x0f,0x69,0x78,0x9a,0x12,0x04,0x65,0x78,0xa7,0xe6,0xf9,0x78,0xa6,0xe6,0xfa,0x7b,
+0x01,0x74,0x0a,0x78,0x00,0x12,0x03,0x3f,0xc2,0x03,0x78,0x95,0xe6,0xfc,0x12,0x11,
+0x07,0x78,0x97,0xec,0xf6,0x78,0x97,0xe6,0xfc,0x12,0x25,0xbf,0x12,0x31,0x69,0x22,
+0x12,0x30,0xe6,0x78,0x95,0xec,0xf6,0x12,0x0f,0x69,0x78,0x95,0xe6,0x24,0xfd,0x75,
+0xf0,0x0a,0xa4,0x24,0x14,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xac,0x82,0xad,0x83,
+0x78,0xa6,0x86,0x83,0x08,0x86,0x82,0xec,0xf9,0xed,0xfa,0x7b,0x0a,0x78,0x01,0x12,
+0x03,0xa7,0xc2,0x03,0x78,0x95,0xe6,0xfc,0x12,0x11,0x07,0x12,0x31,0x69,0x22,0x8d,
+0x2b,0x8c,0x2a,0xed,0x60,0x40,0x75,0x27,0x01,0x75,0x29,0x48,0x75,0x28,0xff,0xe5,
+0x2a,0x24,0xfd,0xfc,0xe4,0x34,0xff,0xfd,0xec,0x7c,0x03,0x25,0xe0,0xcd,0x33,0xcd,
+0xdc,0xf9,0xfc,0xe5,0x29,0x2c,0xf5,0x29,0xe5,0x28,0x3d,0xf5,0x28,0xad,0x29,0xae,
+0x28,0xaf,0x27,0x74,0x80,0x90,0x00,0x06,0x12,0x03,0x17,0x74,0x80,0x90,0x00,0x02,
+0x12,0x03,0x17,0x12,0x0f,0xb7,0xe5,0x2b,0x14,0x60,0x3b,0x75,0x27,0x01,0x75,0x29,
+0x08,0x75,0x28,0xff,0xe5,0x2a,0x24,0xfd,0xfc,0xe4,0x34,0xff,0xfd,0xec,0x7c,0x03,
+0x25,0xe0,0xcd,0x33,0xcd,0xdc,0xf9,0xfc,0xe5,0x29,0x2c,0xf5,0x29,0xe5,0x28,0x3d,
+0xf5,0x28,0xad,0x29,0xae,0x28,0xaf,0x27,0xe4,0x90,0x00,0x06,0x12,0x03,0x17,0xe4,
+0x90,0x00,0x02,0x12,0x03,0x17,0x22,0x12,0x30,0xe6,0x78,0x95,0xec,0xf6,0xec,0x24,
+0x9d,0xf8,0xe6,0x30,0xe2,0x09,0x78,0x95,0xe6,0xfc,0x12,0x14,0xed,0xd2,0x00,0x78,
+0x95,0xe6,0xfc,0x12,0x0f,0x69,0x78,0x96,0x76,0x00,0x90,0xfc,0x39,0xe0,0x30,0xe7,
+0x04,0x78,0x96,0x76,0x01,0x78,0x96,0xe6,0xfd,0x78,0x95,0xe6,0xfc,0x12,0x0d,0x2f,
+0xc2,0x03,0x30,0x00,0x07,0x78,0x95,0xe6,0xfc,0x12,0x13,0xec,0x7c,0x00,0x12,0x25,
+0xbf,0x12,0x31,0x69,0x22,0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x44,0x01,0xf0,0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xe0,0x30,0xe0,0x02,0x80,0xed,0x78,0xa9,0xe6,0x24,0x0b,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0xf8,0xf0,0x78,0xa9,0xe6,0x24,0x02,0xf5,
+0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x44,0x80,0xf0,0x22,0xc2,0x03,0x8c,0x58,
+0x12,0x0f,0x69,0x78,0xa6,0x86,0x83,0x08,0x86,0x82,0x79,0x5d,0x7a,0x35,0x7b,0x0a,
+0x78,0x01,0x12,0x03,0xf5,0x12,0x0e,0x05,0xac,0x58,0x7d,0x02,0x12,0x0d,0x2f,0xc2,
+0x03,0xac,0x58,0x12,0x11,0x07,0x22,0x8d,0x53,0x8e,0x52,0x8f,0x51,0x8c,0x50,0x12,
+0x0f,0x69,0x75,0x4f,0x00,0x78,0xa9,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x20,0xe4,0x1f,0xe5,0x4f,0x24,0xf6,0x40,0x19,0x05,0x4f,0xc2,0x03,
+0x7c,0x18,0x12,0x32,0xa9,0x90,0xff,0x93,0xe0,0x44,0x01,0xf0,0xb2,0xb3,0xac,0x50,
+0x12,0x0f,0x69,0x80,0xd0,0x78,0xa9,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x20,0xe4,0x05,0xc2,0x03,0x7c,0x02,0x22,0x78,0xa9,0xe6,0x24,0x05,
+0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0x0f,0x60,0x16,0x78,0xa9,0xe6,
+0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0x0f,0xf0,0xc2,0x03,
+0x7c,0x01,0x22,0x78,0xa8,0x86,0x83,0x08,0x86,0x82,0xe0,0xad,0x53,0xae,0x52,0xaf,
+0x51,0x12,0x03,0x0f,0xc2,0x03,0x7c,0x00,0x22,0x8d,0x31,0x8c,0x30,0x12,0x14,0xed,
+0xe5,0x31,0x60,0x0f,0xe5,0x30,0xb4,0x03,0x0a,0x7c,0x01,0x12,0x24,0xee,0x7c,0x81,
+0x12,0x24,0xee,0xac,0x30,0x12,0x0f,0x69,0xe5,0x31,0x60,0x1a,0x78,0xaa,0x86,0x83,
+0x08,0x86,0x82,0xe0,0x54,0xe7,0xf0,0xa3,0xa3,0xa3,0xa3,0xe0,0x54,0xe7,0xf0,0xac,
+0x30,0x7d,0x02,0x12,0x0d,0x2f,0x78,0xa6,0x86,0x83,0x08,0x86,0x82,0x79,0x67,0x7a,
+0x35,0x7b,0x0a,0x78,0x01,0x12,0x03,0xf5,0xc2,0x03,0xe5,0x30,0x24,0x9d,0xf8,0xc6,
+0x54,0xfd,0xf6,0xac,0x30,0x12,0x11,0x07,0x22,0x8c,0x26,0x30,0x03,0x05,0x12,0x32,
+0x48,0x80,0xf8,0x7c,0x0a,0x12,0x31,0x5b,0xd2,0x03,0xe5,0x26,0x24,0xfd,0x78,0xa3,
+0xf6,0x70,0x07,0x78,0xaa,0x76,0xff,0x08,0x76,0xe0,0x78,0xa3,0xe6,0x75,0xf0,0x10,
+0xa4,0xad,0xf0,0xfc,0x24,0xa0,0x78,0xa9,0xf6,0xed,0x34,0xff,0x18,0xf6,0x78,0xa3,
+0xe6,0x75,0xf0,0x0a,0xa4,0x24,0x00,0xfc,0xe4,0x34,0xfc,0xfd,0x78,0xa6,0xed,0xf6,
+0x08,0xec,0xf6,0x12,0x31,0xf4,0x22,0x78,0xa9,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xe0,0x30,0xe7,0x22,0x78,0xa9,0xe6,0x24,0x02,0xf5,0x82,0x18,
+0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0x7f,0xf0,0x78,0xa9,0xe6,0x24,0x02,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x44,0x80,0xf0,0x22,0x78,0xaa,0x86,0x83,0x08,
+0x86,0x82,0xe0,0x54,0x7f,0xf0,0xad,0x83,0xe5,0x82,0x24,0x04,0xfc,0xe4,0x3d,0x8c,
+0x82,0xf5,0x83,0xe0,0x54,0x7f,0xf0,0x78,0xa9,0xe6,0x24,0x0b,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xe0,0x54,0xf8,0xf0,0x78,0xab,0xe6,0x24,0x01,0xf5,0x82,0x18,
+0xe6,0x34,0x00,0xf5,0x83,0xe0,0x44,0x03,0xf0,0x78,0xab,0xe6,0x24,0x05,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x44,0x03,0xf0,0x78,0xa9,0xe6,0x24,0x05,0xf5,
+0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0x74,0x0f,0xf0,0x22,0x78,0xaa,0x86,0x83,0x08,
+0x86,0x82,0xe0,0x54,0x3f,0xf0,0xad,0x83,0xe5,0x82,0x24,0x04,0xfc,0xe4,0x3d,0x8c,
+0x82,0xf5,0x83,0xe0,0x54,0x3f,0xf0,0x78,0xa3,0xe6,0x24,0xa4,0xf8,0xe6,0xfc,0x78,
+0xab,0xe6,0x24,0x01,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0x78,0xa3,
+0xe6,0x24,0xa4,0xf8,0xe6,0xfc,0x78,0xab,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xec,0xf0,0x78,0xa9,0xe6,0x24,0x0b,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x54,0xfb,0x44,0x02,0xf5,0x26,0x78,0xa7,0xe6,0x24,0x02,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x30,0xe5,0x03,0x43,0x26,0x01,0x78,0xa9,0xe6,
+0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x30,0xe0,0x03,0x12,0x0f,
+0xb7,0xe5,0x26,0xfc,0x78,0xa9,0xe6,0x24,0x0b,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xec,0xf0,0x78,0xa9,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,
+0x74,0x0f,0xf0,0x78,0xaa,0x86,0x83,0x08,0x86,0x82,0xe0,0x44,0x80,0xf0,0xa3,0xa3,
+0xa3,0xa3,0xe0,0x44,0x80,0xf0,0x22,0x8c,0x2a,0x12,0x0f,0x69,0x78,0xa7,0xe6,0x24,
+0x08,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xfc,0x78,0xa9,0xe6,0x24,0x0a,
+0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0x78,0xa7,0xe6,0x24,0x07,0xf5,
+0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xfc,0x78,0xa9,0xe6,0x24,0x09,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0x78,0xa6,0x86,0x83,0x08,0x86,0x82,0xe0,
+0xfd,0xa3,0xe0,0xfc,0xed,0xfe,0x78,0xa9,0xe6,0x24,0x08,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xee,0xf0,0xec,0xfe,0x78,0xa9,0xe6,0x24,0x07,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xee,0xf0,0x8c,0x29,0x8d,0x28,0xc3,0xec,0x94,0x05,0xed,0x94,
+0x0c,0x40,0x05,0x75,0x27,0x7c,0x80,0x33,0xd3,0xe5,0x29,0x94,0x01,0xe5,0x28,0x94,
+0x03,0x40,0x05,0x75,0x27,0x3c,0x80,0x23,0xd3,0xe5,0x29,0x94,0x81,0xe5,0x28,0x94,
+0x01,0x40,0x05,0x75,0x27,0x18,0x80,0x13,0xd3,0xe5,0x29,0x94,0x60,0xe5,0x28,0x94,
+0x00,0x40,0x05,0x75,0x27,0x0c,0x80,0x03,0x75,0x27,0x08,0xaf,0x27,0xe4,0xef,0x54,
+0x7c,0x44,0x83,0xff,0x8f,0x27,0xe5,0x27,0xfc,0x78,0xab,0xe6,0x24,0x01,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0xe5,0x27,0xfc,0x78,0xab,0xe6,0x24,0x05,
+0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0xe5,0x27,0xfc,0x78,0xa3,0xe6,
+0x24,0xa4,0xf8,0xec,0xf6,0x78,0xa9,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0xf5,0x27,0x78,0xa7,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xa3,0xe0,0x30,0xe3,0x17,0x53,0x27,0xc7,0x78,0xa7,0xe6,0x24,0x05,0xf5,
+0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x90,0x35,0x58,0x93,0x42,0x27,0x53,0x27,
+0xfb,0x78,0xa7,0xe6,0x24,0x06,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x60,
+0x03,0x43,0x27,0x04,0x53,0x27,0xfc,0x78,0xa7,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xe0,0x42,0x27,0x43,0x27,0x80,0xe5,0x27,0xfc,0x78,0xa9,0xe6,
+0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0x78,0xa9,0xe6,0x24,
+0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xf5,0x27,0x78,0xa7,0xe6,0x24,
+0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xa3,0xe0,0x30,0xe1,0x05,0x53,0x27,
+0xdf,0x80,0x03,0x43,0x27,0x20,0x78,0xa7,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xe0,0x30,0xe4,0x05,0x53,0x27,0xef,0x80,0x03,0x43,0x27,0x10,0x78,
+0xa7,0xe6,0x24,0x09,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xb4,0x02,0x03,
+0x43,0x27,0x02,0xe5,0x27,0xfc,0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xec,0xf0,0x78,0xa9,0xe6,0x24,0x03,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0xf5,0x27,0x78,0xa7,0xe6,0x24,0x09,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x70,0x05,0x53,0x27,0x7f,0x80,0x03,0x43,0x27,0x80,0x78,0xa7,0xe6,
+0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xa3,0xe0,0x30,0xe0,0x05,0x43,
+0x27,0x20,0x80,0x03,0x53,0x27,0xdf,0x78,0xa7,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xe0,0x30,0xe3,0x05,0x43,0x27,0x40,0x80,0x03,0x53,0x27,0xbf,
+0x78,0xa7,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x30,0xe0,
+0x05,0x43,0x27,0x10,0x80,0x03,0x53,0x27,0xef,0x78,0xa7,0xe6,0x24,0x02,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xa3,0xe0,0x30,0xe4,0x05,0x43,0x27,0x08,0x80,0x03,
+0x53,0x27,0xf7,0x78,0xa7,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,
+0xa3,0xe0,0x30,0xe5,0x05,0x43,0x27,0x04,0x80,0x03,0x53,0x27,0xfb,0x78,0xa7,0xe6,
+0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xa3,0xe0,0x30,0xe6,0x05,0x43,
+0x27,0x01,0x80,0x03,0x53,0x27,0xfe,0x78,0xa7,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xa3,0xe0,0x30,0xe7,0x05,0x43,0x27,0x02,0x80,0x03,0x53,0x27,
+0xfd,0xe5,0x27,0xfc,0x78,0xa9,0xe6,0x24,0x03,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xec,0xf0,0xc2,0x03,0x7c,0x00,0x22,0x8d,0x27,0x8c,0x26,0xed,0x54,0x03,0x14,
+0x60,0x03,0x7c,0x10,0x22,0xe5,0x27,0x54,0x7c,0x24,0xfc,0x40,0x03,0x7c,0x0b,0x22,
+0xe5,0x26,0x24,0x9d,0xf8,0xc6,0x44,0x02,0xf6,0x7c,0x00,0x22,0x8c,0x30,0x12,0x0f,
+0x69,0xe5,0x30,0x24,0x9d,0xf8,0xe6,0x20,0xe2,0x4f,0xac,0x30,0x7d,0x02,0x12,0x0d,
+0x2f,0xe5,0x30,0x24,0xfe,0x44,0x28,0xfc,0x78,0xaa,0x86,0x83,0x08,0x86,0x82,0xec,
+0xf0,0xaf,0x83,0xe5,0x82,0x24,0x04,0xfe,0xe4,0x3f,0xff,0xec,0x8e,0x82,0x8f,0x83,
+0xf0,0x7c,0x03,0x8c,0x2c,0xe5,0x2c,0xfc,0x78,0xab,0xe6,0x24,0x01,0xf5,0x82,0x18,
+0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0xe5,0x2c,0xfc,0x78,0xab,0xe6,0x24,0x05,0xf5,
+0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0x75,0x2d,0x01,0x75,0x2f,0x48,0x75,
+0x2e,0xff,0xe5,0x30,0x24,0xfd,0xfc,0xe4,0x34,0xff,0xfd,0xec,0x7c,0x03,0x25,0xe0,
+0xcd,0x33,0xcd,0xdc,0xf9,0xfc,0xe5,0x2f,0x2c,0xf5,0x2f,0xe5,0x2e,0x3d,0xf5,0x2e,
+0x78,0xab,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0xe7,
+0xf5,0x2c,0xad,0x2f,0xae,0x2e,0xaf,0x2d,0xe4,0x90,0x00,0x02,0x12,0x03,0x17,0xe4,
+0x90,0x00,0x06,0x12,0x03,0x17,0x12,0x01,0xe6,0x30,0xe5,0x03,0x43,0x2c,0x10,0xe5,
+0x2c,0xfc,0x78,0xab,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,
+0xf0,0x12,0x10,0x4b,0x78,0xa9,0xe6,0x24,0x06,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xe0,0xc2,0x03,0xfc,0xe5,0x30,0x24,0x9d,0xf8,0xc6,0x44,0x04,0xf6,0x8c,0x2c,
+0xe5,0x30,0x54,0x0f,0xc4,0x54,0xf0,0x7e,0x00,0xff,0xee,0xef,0x44,0x04,0x7d,0x00,
+0xff,0xec,0x4e,0xfc,0xed,0x4f,0xfd,0x12,0x1c,0xa7,0x7c,0x00,0x22,0x8c,0x2f,0x12,
+0x0f,0x69,0x12,0x0f,0xeb,0x78,0xaa,0x86,0x83,0x08,0x86,0x82,0xe0,0x54,0x08,0xf0,
+0xa3,0xa3,0xa3,0xa3,0xe0,0x54,0x08,0xf0,0xac,0x2f,0x7d,0x02,0x12,0x0d,0x2f,0xc2,
+0x03,0xe5,0x2f,0x24,0x9d,0xf8,0xc6,0x54,0xfb,0xf6,0x7c,0x00,0x22,0x12,0x30,0xe6,
+0x78,0x96,0xec,0xf6,0xec,0x24,0x9d,0xf8,0xe6,0x30,0xe1,0x0a,0x7d,0x00,0x7c,0x13,
+0x12,0x25,0x26,0x12,0x31,0x69,0x78,0x96,0xe6,0x24,0x9d,0xf8,0xc6,0x44,0x01,0xf6,
+0x78,0x96,0xe6,0xfc,0x12,0x0f,0x69,0x78,0x96,0xe6,0x24,0xfd,0x75,0xf0,0x0a,0xa4,
+0x24,0x14,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0x78,0xa6,0xe6,0xfa,0x08,0xe6,0xf9,
+0x7b,0x0a,0x78,0x01,0x12,0x03,0xa7,0x78,0xa6,0x86,0x83,0x08,0x86,0x82,0x79,0x67,
+0x7a,0x35,0x7b,0x0a,0x78,0x01,0x12,0x03,0xf5,0x12,0x0f,0xb7,0xc2,0x03,0x78,0x96,
+0xe6,0xfc,0x12,0x11,0x07,0x78,0x95,0xec,0xf6,0xec,0x60,0x0a,0x7d,0x00,0x7c,0x08,
+0x12,0x25,0x26,0x12,0x31,0x69,0x78,0x96,0xe6,0xfc,0x12,0x0f,0x69,0x78,0xa9,0xe6,
+0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x44,0x10,0x54,0xdf,0xfc,
+0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0x78,
+0x95,0xec,0xf6,0xc2,0x03,0x7c,0xc8,0x12,0x32,0xa9,0x78,0x96,0xe6,0xfc,0x12,0x0f,
+0x69,0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,
+0xef,0xf0,0xc2,0x03,0x7c,0xc8,0x12,0x32,0xa9,0x78,0x96,0xe6,0xfc,0x12,0x0f,0x69,
+0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x44,0x10,
+0xf0,0xc2,0x03,0x7c,0xc8,0x12,0x32,0xa9,0x78,0x96,0xe6,0xfc,0x12,0x0f,0x69,0x78,
+0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x44,0x20,0xf0,
+0xc2,0x03,0x7c,0xf0,0x12,0x32,0xa9,0x78,0x96,0xe6,0xfc,0x12,0x0f,0x69,0x78,0xa9,
+0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x30,0xe4,0x15,0xc2,
+0x03,0x78,0x96,0xe6,0x44,0x10,0x7f,0x00,0xfe,0x7c,0x07,0x12,0x31,0xfb,0x12,0x31,
+0x69,0x02,0x17,0x14,0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xe0,0x54,0xcf,0xf0,0xc2,0x03,0x7c,0xc8,0x12,0x32,0xa9,0x78,0x96,0xe6,0xfc,
+0x12,0x0f,0x69,0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,
+0xe0,0x44,0x30,0xf0,0xc2,0x03,0x7c,0xf0,0x12,0x32,0xa9,0x78,0x96,0xe6,0xfc,0x12,
+0x0f,0x69,0x78,0xa9,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,
+0x30,0xe4,0x14,0xc2,0x03,0x78,0x96,0xe6,0x44,0x10,0x7f,0x00,0xfe,0x7c,0x07,0x12,
+0x31,0xfb,0x12,0x31,0x69,0x80,0x5d,0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xe0,0x54,0xef,0xf0,0x78,0xa9,0xe6,0x24,0x04,0xf5,0x82,0x18,
+0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0xdf,0xf0,0x78,0x96,0xe6,0x24,0xfd,0x75,0xf0,
+0x0a,0xa4,0x24,0x14,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xac,0x82,0xad,0x83,0x78,
+0xa6,0x86,0x83,0x08,0x86,0x82,0xec,0xf9,0xed,0xfa,0x7b,0x0a,0x78,0x01,0x12,0x03,
+0xa7,0xc2,0x03,0x78,0x96,0xe6,0xfc,0x12,0x11,0x07,0x7d,0x00,0x7c,0x0b,0x12,0x25,
+0x26,0x12,0x31,0x69,0x22,0x12,0x30,0xe6,0xe4,0x90,0xfc,0x39,0xf0,0x7d,0x02,0x7c,
+0x00,0x12,0x25,0x26,0x12,0x31,0x69,0x22,0x12,0x30,0xe6,0x7c,0x00,0x12,0x25,0xbf,
+0x12,0x31,0x69,0x22,0x74,0x3c,0x90,0xfb,0xe0,0xf0,0x74,0x3e,0x90,0xfb,0xe0,0xf0,
+0xe4,0x90,0xfc,0x28,0xf0,0x22,0x8d,0x35,0x8c,0x34,0xec,0xb4,0x01,0x02,0x80,0x03,
+0xd3,0x40,0x02,0x80,0x28,0xb4,0x02,0x02,0x80,0x03,0xd3,0x40,0x08,0xa8,0x35,0xc6,
+0x25,0xe0,0xf6,0x80,0x18,0xb4,0x04,0x02,0x80,0x03,0xd3,0x40,0x0a,0xa8,0x35,0xc6,
+0x25,0xe0,0x25,0xe0,0xf6,0x80,0x06,0xa8,0x35,0x76,0x00,0x80,0x00,0x22,0x8c,0x3c,
+0x8d,0x3b,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x75,0x66,0x06,0x75,0x67,0x00,0x90,0xfc,
+0x29,0x12,0x04,0x6e,0x12,0x01,0xe6,0xb4,0x80,0x02,0x80,0x06,0xd3,0x50,0x03,0x02,
+0x18,0x47,0x90,0xfc,0x29,0x12,0x04,0x80,0x90,0x00,0x03,0x12,0x01,0xec,0x54,0xf0,
+0xb4,0x30,0x02,0x80,0x03,0xd3,0x40,0x5f,0x90,0xfc,0x29,0x12,0x04,0x80,0x90,0x00,
+0x08,0x12,0x02,0x0e,0xfa,0xfd,0xeb,0xfe,0x7f,0x01,0x90,0xfc,0x2c,0x12,0x04,0x6e,
+0xee,0xcd,0x90,0x35,0x71,0xfc,0xe4,0x93,0xff,0x74,0x01,0x93,0xfe,0xf9,0xef,0xfa,
+0x7b,0x01,0xea,0xff,0xe9,0xfe,0xec,0xc3,0x9e,0xed,0x9f,0x40,0x25,0x90,0x35,0x73,
+0xe4,0x93,0xfd,0x74,0x01,0x93,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0xee,0xcd,0xfc,
+0x90,0xfc,0x2e,0xe0,0xd3,0x9c,0x90,0xfc,0x2d,0xe0,0x9d,0x50,0x05,0x75,0x66,0x80,
+0x80,0x33,0x12,0x19,0x65,0x80,0x2e,0xb4,0x60,0x02,0x80,0x03,0xd3,0x40,0x0b,0xac,
+0x3c,0xad,0x3b,0x12,0x07,0x77,0x8c,0x66,0x80,0x1b,0xb4,0x10,0x03,0xb3,0x40,0x10,
+0xc3,0xb4,0x20,0x03,0xb3,0x40,0x09,0xc3,0xb4,0x40,0x02,0x80,0x03,0xd3,0x40,0x00,
+0x75,0x66,0x81,0x80,0x00,0x80,0x75,0xb4,0x81,0x02,0x80,0x03,0xd3,0x40,0x6b,0x90,
+0xfc,0x29,0x12,0x04,0x80,0x90,0x00,0x03,0x12,0x01,0xec,0x54,0xf0,0xb4,0x30,0x02,
+0x80,0x03,0xd3,0x40,0x1d,0x90,0xfc,0x29,0x12,0x04,0x80,0x90,0x00,0x08,0x12,0x02,
+0x0e,0xfa,0xfd,0xeb,0xfe,0x7f,0x01,0x90,0xfc,0x2f,0x12,0x04,0x6e,0x12,0x18,0xcf,
+0x80,0x36,0xb4,0x60,0x02,0x80,0x03,0xd3,0x40,0x13,0x75,0x3a,0x67,0xe4,0xf5,0x39,
+0xf5,0x38,0xac,0x3c,0xad,0x3b,0x12,0x05,0xd3,0x8c,0x66,0x80,0x1b,0xb4,0x10,0x03,
+0xb3,0x40,0x10,0xc3,0xb4,0x20,0x03,0xb3,0x40,0x09,0xc3,0xb4,0x40,0x02,0x80,0x03,
+0xd3,0x40,0x00,0x75,0x66,0x81,0x80,0x00,0x80,0x02,0x80,0x00,0xe5,0x66,0xfc,0x90,
+0xfc,0x29,0x12,0x04,0x80,0xec,0x90,0x00,0x02,0x12,0x03,0x17,0xac,0x67,0x22,0x90,
+0xfc,0x29,0x12,0x04,0x80,0x90,0x00,0x04,0x12,0x01,0xec,0x60,0x04,0x74,0x01,0x80,
+0x01,0xe4,0xa2,0xe0,0x92,0x01,0x90,0xfc,0x29,0x12,0x04,0x80,0xed,0x24,0x03,0xfd,
+0x50,0x01,0x0e,0x90,0xfc,0x2c,0x12,0x04,0x6e,0x90,0xfc,0x29,0x12,0x04,0x80,0x90,
+0x00,0x05,0x12,0x01,0xec,0xf5,0x67,0x90,0x00,0x04,0x12,0x01,0xec,0x54,0x0f,0xfc,
+0x7d,0x67,0x12,0x17,0x46,0xe5,0x67,0x70,0x04,0x75,0x66,0x08,0x22,0x75,0x66,0x00,
+0x78,0x84,0x76,0x00,0x78,0x84,0xe6,0xc3,0x95,0x67,0x50,0x38,0x90,0xfc,0x2f,0x12,
+0x04,0x80,0x12,0x01,0xe6,0xfc,0x90,0xfc,0x2c,0x12,0x04,0x80,0xec,0x12,0x03,0x0f,
+0x30,0x01,0x0e,0x90,0xfc,0x31,0xe0,0x04,0xf0,0x90,0xfc,0x30,0x70,0x03,0xe0,0x04,
+0xf0,0x78,0x84,0x06,0x90,0xfc,0x2e,0xe0,0x04,0xf0,0x90,0xfc,0x2d,0x70,0x03,0xe0,
+0x04,0xf0,0x80,0xc0,0x22,0x90,0xfc,0x2a,0xe0,0xfd,0xa3,0xe0,0xfc,0xed,0xfe,0xec,
+0xfd,0x7f,0x01,0xed,0x24,0x0a,0xfd,0x50,0x01,0x0e,0x90,0xfc,0x32,0x12,0x04,0x6e,
+0x90,0xfc,0x29,0x12,0x04,0x80,0x90,0x00,0x04,0x12,0x01,0xec,0x54,0x0f,0xb4,0x01,
+0x02,0x80,0x03,0xd3,0x40,0x17,0x90,0xfc,0x32,0x12,0x04,0x80,0x0d,0xed,0x70,0x01,
+0x0e,0x90,0xfc,0x2f,0x12,0x04,0x6e,0x78,0x88,0x76,0x01,0x80,0x4e,0xb4,0x02,0x02,
+0x80,0x03,0xd3,0x40,0x19,0x90,0xfc,0x32,0x12,0x04,0x80,0xed,0x24,0x02,0xfd,0x50,
+0x01,0x0e,0x90,0xfc,0x2f,0x12,0x04,0x6e,0x78,0x88,0x76,0x02,0x80,0x2d,0xb4,0x04,
+0x02,0x80,0x03,0xd3,0x40,0x19,0x90,0xfc,0x32,0x12,0x04,0x80,0xed,0x24,0x04,0xfd,
+0x50,0x01,0x0e,0x90,0xfc,0x2f,0x12,0x04,0x6e,0x78,0x88,0x76,0x04,0x80,0x0c,0xb4,
+0x00,0x02,0x80,0x03,0xd3,0x40,0x00,0x75,0x66,0x08,0x22,0x90,0xfc,0x29,0x12,0x04,
+0x80,0x90,0x00,0x05,0x12,0x01,0xec,0xf5,0x67,0x78,0x85,0x76,0x00,0x78,0x85,0xe6,
+0xc3,0x95,0x67,0x40,0x03,0x02,0x1a,0xcd,0x78,0x86,0x76,0x00,0x78,0x86,0xe6,0xc3,
+0x78,0x88,0x96,0x50,0x76,0x90,0xfc,0x2c,0x12,0x04,0x80,0x12,0x01,0xe6,0xfc,0x90,
+0xfc,0x32,0x12,0x04,0x89,0x12,0x01,0xe0,0xf4,0x5c,0xfc,0x12,0x01,0xe0,0xf8,0x90,
+0xfc,0x2f,0x12,0x04,0x80,0xe8,0xc0,0xe0,0x12,0x01,0xe6,0xc8,0xd0,0xe0,0xc8,0x58,
+0x4c,0xfc,0x90,0xfc,0x2c,0x12,0x04,0x80,0xec,0x12,0x03,0x0f,0x78,0x87,0xec,0xf6,
+0x90,0xfc,0x31,0xe0,0x04,0xf0,0x90,0xfc,0x30,0x70,0x03,0xe0,0x04,0xf0,0x09,0xe9,
+0x70,0x01,0x0a,0x90,0xfc,0x32,0x12,0x04,0x77,0x90,0xfc,0x29,0x12,0x04,0x80,0x90,
+0x00,0x04,0x12,0x01,0xec,0x30,0xe4,0x0e,0x90,0xfc,0x2e,0xe0,0x04,0xf0,0x90,0xfc,
+0x2d,0x70,0x03,0xe0,0x04,0xf0,0x78,0x86,0x06,0x80,0x81,0x78,0x88,0xe6,0xfd,0xe4,
+0xfe,0xff,0xee,0xcd,0xfc,0x90,0xfc,0x31,0xe0,0x2c,0xf0,0x90,0xfc,0x30,0xe0,0x3d,
+0xf0,0x78,0x88,0xe6,0xfd,0xe4,0xfe,0xff,0xee,0xcd,0xfc,0x90,0xfc,0x34,0xe0,0x2c,
+0xf0,0x90,0xfc,0x33,0xe0,0x3d,0xf0,0x78,0x85,0x06,0x02,0x1a,0x0d,0x75,0x66,0x00,
+0x22,0xe5,0x3d,0x05,0x3d,0x04,0x70,0x02,0xb2,0xb0,0x22,0xc0,0xe0,0xc0,0xf0,0xc0,
+0x82,0xc0,0x83,0xc0,0xd0,0xe8,0xc0,0xe0,0xe9,0xc0,0xe0,0xea,0xc0,0xe0,0xeb,0xc0,
+0xe0,0xec,0xc0,0xe0,0xed,0xc0,0xe0,0xee,0xc0,0xe0,0xef,0xc0,0xe0,0x90,0xff,0x92,
+0xe0,0x12,0x01,0xb7,0x1b,0x29,0x30,0x1b,0x29,0x32,0x1b,0x38,0x38,0x1b,0x4a,0x3a,
+0x1b,0x5c,0x3e,0x1b,0x74,0x44,0x1b,0x68,0x46,0x1b,0x80,0x50,0x1b,0xc2,0x52,0x1b,
+0xa1,0x54,0x1b,0xe3,0x56,0x00,0x00,0x1c,0x04,0x90,0xff,0x92,0xe0,0x7f,0x00,0xfe,
+0x7c,0x01,0x12,0x31,0xfb,0x02,0x1c,0x14,0xe4,0xff,0x04,0xfe,0x7c,0x03,0x12,0x31,
+0xfb,0x74,0x20,0x90,0xff,0xfe,0xf0,0x02,0x1c,0x14,0xe4,0xff,0x04,0xfe,0x7c,0x02,
+0x12,0x31,0xfb,0x74,0x40,0x90,0xff,0xfe,0xf0,0x02,0x1c,0x14,0xe4,0xff,0x04,0xfe,
+0x7c,0x04,0x12,0x31,0xfb,0x02,0x1c,0x14,0xe4,0xff,0x04,0xfe,0x7c,0x05,0x12,0x31,
+0xfb,0x02,0x1c,0x14,0xe4,0xff,0x04,0xfe,0x7c,0x06,0x12,0x31,0xfb,0x02,0x1c,0x14,
+0x90,0xff,0xa5,0xe0,0x7d,0x00,0x90,0xfb,0xf8,0xcd,0xf0,0xa3,0xcd,0xf0,0x90,0xfb,
+0xf9,0xe0,0xfc,0xf5,0x83,0x90,0xfb,0xf8,0xe0,0x44,0x33,0xfd,0x12,0x1c,0xa7,0x80,
+0x73,0x90,0xff,0xb5,0xe0,0x7d,0x00,0x90,0xfb,0xfa,0xcd,0xf0,0xa3,0xcd,0xf0,0x90,
+0xfb,0xfb,0xe0,0xfc,0xf5,0x83,0x90,0xfb,0xfa,0xe0,0x44,0x43,0xfd,0x12,0x1c,0xa7,
+0x80,0x52,0x90,0xff,0xa6,0xe0,0x7d,0x00,0x90,0xfb,0xfc,0xcd,0xf0,0xa3,0xcd,0xf0,
+0x90,0xfb,0xfd,0xe0,0xfc,0xf5,0x83,0x90,0xfb,0xfc,0xe0,0x44,0x34,0xfd,0x12,0x1c,
+0xa7,0x80,0x31,0x90,0xff,0xb6,0xe0,0x7d,0x00,0x90,0xfb,0xfe,0xcd,0xf0,0xa3,0xcd,
+0xf0,0x90,0xfb,0xff,0xe0,0xfc,0xf5,0x83,0x90,0xfb,0xfe,0xe0,0x44,0x44,0xfd,0x12,
+0x1c,0xa7,0x80,0x10,0x90,0xff,0x92,0xe0,0x7d,0x00,0xfc,0xed,0x44,0xaa,0xfd,0x12,
+0x1c,0xa7,0x80,0x00,0xe4,0x90,0xff,0x92,0xf0,0xd0,0xe0,0xff,0xd0,0xe0,0xfe,0xd0,
+0xe0,0xfd,0xd0,0xe0,0xfc,0xd0,0xe0,0xfb,0xd0,0xe0,0xfa,0xd0,0xe0,0xf9,0xd0,0xe0,
+0xf8,0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32,0x05,0x81,0x05,0x81,
+0x05,0x81,0x05,0x81,0xa8,0x81,0x18,0x18,0x18,0xed,0xf6,0x08,0xec,0xf6,0x90,0xff,
+0x5a,0xe0,0x20,0xe7,0x02,0x80,0xf7,0x90,0xff,0x59,0xe0,0x7d,0x00,0xa8,0x81,0x18,
+0xcd,0xf6,0xcd,0x08,0xf6,0x7d,0x03,0xa8,0x81,0xe6,0x18,0xfc,0xe6,0xcc,0x25,0xe0,
+0xcc,0x33,0xcc,0xdd,0xf9,0xcc,0xf6,0xcc,0x08,0xf6,0xa8,0x81,0x18,0xe6,0x44,0xf8,
+0xf6,0xa8,0x81,0x18,0x18,0x18,0xe6,0xfd,0x08,0xe6,0xfc,0xa8,0x81,0x18,0x86,0x83,
+0x08,0x86,0x82,0xed,0xf0,0xa3,0xec,0xf0,0x74,0x02,0x90,0xff,0x5a,0xf0,0x15,0x81,
+0x15,0x81,0x15,0x81,0x15,0x81,0x22,0xe5,0x81,0x24,0x05,0xf5,0x81,0xe4,0xa8,0x81,
+0x18,0xf6,0xa8,0x81,0x18,0x18,0x18,0x18,0xed,0xf6,0x08,0xec,0xf6,0x90,0xfb,0xf5,
+0xe0,0x24,0xf8,0x50,0x03,0x02,0x1d,0xc8,0xe4,0xa8,0x81,0x18,0x18,0xf6,0xa8,0x81,
+0x18,0xe6,0xfe,0xa8,0x81,0x18,0x18,0x18,0x18,0xe6,0xfd,0x08,0xe6,0xfc,0x7f,0x00,
+0xef,0x24,0xf8,0x40,0x4d,0xe4,0xef,0x25,0xe0,0x24,0x7d,0xf5,0x82,0xe4,0x34,0xfc,
+0xf5,0x83,0xe0,0xfb,0xa3,0xe0,0x6c,0x70,0x03,0xfa,0xeb,0x6d,0x70,0x09,0x74,0x01,
+0xa8,0x81,0x18,0x18,0xf6,0x80,0x2b,0xe4,0xef,0x25,0xe0,0x24,0x7d,0xf5,0x82,0xe4,
+0x34,0xfc,0xf5,0x83,0x7a,0x00,0xe0,0x54,0xf0,0xcc,0xf8,0xcc,0xcd,0xf9,0xcd,0xfb,
+0x78,0x00,0xe9,0x54,0xf0,0xf9,0xea,0x68,0x70,0x02,0xeb,0x69,0x70,0x01,0x0e,0x0f,
+0x80,0xae,0xa8,0x81,0x18,0xee,0xf6,0xa8,0x81,0x18,0x18,0x18,0x18,0xed,0xf6,0x08,
+0xec,0xf6,0xa8,0x81,0xef,0xf6,0xa8,0x81,0x18,0x18,0xe6,0x70,0x79,0xa8,0x81,0x18,
+0xe6,0x24,0xf7,0x40,0x71,0xa8,0x81,0x18,0x18,0x18,0x18,0xe6,0x54,0x0f,0xa8,0x81,
+0xf6,0x64,0x04,0x60,0x17,0xa8,0x81,0xe6,0x64,0x03,0x60,0x10,0xa8,0x81,0x18,0x18,
+0x18,0x18,0xe6,0xfd,0x08,0xe6,0xfc,0x12,0x1c,0x3c,0x80,0x4a,0x7c,0x0a,0x12,0x31,
+0x5b,0xa8,0x81,0x18,0x18,0x18,0x18,0xe6,0xfd,0x08,0xe6,0xfc,0x90,0xfb,0xf4,0xe0,
+0x25,0xe0,0x24,0x7d,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xed,0xf0,0xa3,0xec,0xf0,
+0x90,0xfb,0xf4,0xe0,0xff,0xe4,0xef,0x04,0x54,0x07,0xff,0x90,0xfb,0xf4,0xf0,0x90,
+0xfb,0xf5,0xe0,0x04,0xf0,0x12,0x31,0xf4,0x90,0xfb,0xf6,0xe0,0x70,0x08,0xe4,0xfe,
+0xff,0x7c,0x0f,0x12,0x31,0xfb,0x80,0x27,0x90,0xfb,0xf7,0xe0,0x04,0xf0,0x54,0x3f,
+0x70,0x1d,0x90,0xfb,0xf7,0xe0,0x44,0xfe,0x7d,0x00,0xfc,0x90,0xfb,0xf4,0xe0,0x25,
+0xe0,0x24,0x7d,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xed,0xf0,0xa3,0xec,0xf0,0xe5,
+0x81,0x24,0xfb,0xf5,0x81,0x22,0x78,0x8b,0x76,0x00,0x78,0x8c,0x76,0x00,0x74,0x01,
+0x90,0xfb,0xf6,0xf0,0x12,0x30,0xe6,0x90,0xfb,0xf5,0xe0,0x60,0x57,0x7c,0x0a,0x12,
+0x31,0x5b,0x90,0xfb,0xf3,0xe0,0x25,0xe0,0x24,0x7d,0xf5,0x82,0xe4,0x34,0xfc,0xf5,
+0x83,0xe0,0xfd,0xa3,0xe0,0xfc,0x90,0xfb,0xf3,0xe0,0x25,0xe0,0x24,0x7d,0xf5,0x82,
+0xe4,0x34,0xfc,0xf5,0x83,0xe4,0xf0,0xa3,0xf0,0x90,0xfb,0xf3,0xe0,0xff,0xe4,0xef,
+0x04,0x54,0x07,0xff,0x90,0xfb,0xf3,0xf0,0x90,0xfb,0xf5,0xe0,0x14,0xf0,0x78,0x89,
+0xed,0xf6,0x08,0xec,0xf6,0x12,0x31,0xf4,0x78,0x89,0xe6,0xfd,0x08,0xe6,0xfc,0x12,
+0x08,0xda,0x80,0xa3,0x12,0x32,0x48,0x90,0xff,0x93,0xe0,0x44,0x01,0xf0,0xb2,0xb3,
+0x78,0x8b,0x06,0xb6,0x00,0x11,0x78,0x8b,0x76,0x00,0x78,0x8c,0xe6,0xf4,0x04,0x04,
+0xa2,0xe0,0x92,0xb4,0x78,0x8c,0xf6,0x02,0x1e,0x07,0xe4,0x90,0xfb,0xf6,0xf0,0x90,
+0xfb,0xf5,0xe0,0x7d,0x00,0xfc,0xed,0x44,0xcf,0xfd,0x12,0x1c,0x3c,0x12,0x31,0x69,
+0x22,0x12,0x30,0xe6,0xe5,0x70,0x64,0x49,0x45,0x6f,0x60,0x15,0x90,0xff,0x83,0xe0,
+0x54,0x0f,0x7d,0x00,0xd3,0x95,0x70,0xed,0x95,0x6f,0x50,0x05,0x12,0x2f,0x2f,0x80,
+0x03,0x12,0x2f,0xff,0x12,0x31,0x69,0x22,0x12,0x30,0xe6,0xe5,0x70,0x64,0x49,0x45,
+0x6f,0x60,0x05,0x12,0x30,0x39,0x80,0x0e,0x90,0xff,0x80,0xe0,0x44,0x08,0xf0,0x90,
+0xff,0x83,0xe0,0x54,0x7f,0xf0,0x12,0x31,0x69,0x22,0x12,0x30,0xe6,0x8c,0x54,0xec,
+0x54,0xf0,0xb4,0x10,0x15,0x75,0x6a,0x35,0x75,0x69,0xfc,0x75,0x68,0x01,0xe5,0x6a,
+0x24,0x03,0xf5,0x6a,0xe5,0x69,0x34,0x00,0xf5,0x69,0xe4,0xf5,0x57,0xf5,0x56,0xe5,
+0x56,0xc3,0x94,0x01,0x50,0x27,0xe5,0x54,0x54,0x0f,0xfc,0xad,0x6a,0xae,0x69,0xaf,
+0x68,0x12,0x0e,0x77,0x8c,0x55,0xec,0x60,0x02,0x80,0x12,0x05,0x6a,0xe5,0x6a,0x70,
+0x02,0x05,0x69,0x05,0x57,0xe5,0x57,0x70,0x02,0x05,0x56,0x80,0xd2,0xe5,0x54,0x54,
+0x0f,0x24,0x9d,0xf8,0xc6,0x54,0xfe,0xf6,0xe5,0x54,0x54,0x0f,0x7f,0x00,0xfe,0x7c,
+0x12,0x12,0x31,0xfb,0xe5,0x55,0x14,0x70,0x09,0x7d,0x00,0x7c,0x09,0x12,0x25,0x26,
+0x80,0x07,0xad,0x57,0x7c,0x00,0x12,0x25,0x26,0x12,0x31,0x69,0x22,0x12,0x30,0xe6,
+0x90,0xff,0xfc,0xe0,0x44,0x02,0xf0,0x90,0xff,0x00,0xe0,0x30,0xe7,0x13,0x90,0xff,
+0x83,0xe0,0x44,0x80,0xf0,0x43,0x6d,0x80,0x90,0xff,0xfc,0xe0,0x44,0x01,0xf0,0x80,
+0x11,0x90,0xff,0x82,0xe0,0x44,0x08,0xf0,0x53,0x6d,0x7f,0x90,0xff,0xfc,0xe0,0x54,
+0xfe,0xf0,0x90,0xff,0x81,0xe0,0x44,0x80,0xf0,0x12,0x25,0xd9,0x90,0xff,0xfe,0xe0,
+0x44,0x05,0xf0,0x90,0xff,0xfc,0xe0,0x54,0xfd,0xf0,0x12,0x31,0x69,0x22,0x12,0x30,
+0xe6,0x7c,0x01,0x12,0x32,0xa9,0x78,0xad,0xe6,0x44,0x02,0xf6,0x74,0xfe,0xfc,0x04,
+0xfd,0x12,0x1c,0xa7,0x90,0xff,0x5a,0xe0,0x30,0xe7,0x02,0x80,0xf7,0xe4,0xf5,0x4e,
+0x75,0x4d,0x10,0xac,0x4e,0xad,0x4d,0xe5,0x4e,0x15,0x4e,0x70,0x02,0x15,0x4d,0xec,
+0x4d,0x60,0x02,0x80,0xee,0x43,0x87,0x01,0x12,0x31,0x69,0x22,0x12,0x30,0xe6,0x7c,
+0x02,0x12,0x31,0x75,0x78,0xad,0xe6,0x54,0xfd,0xf6,0x12,0x31,0x69,0x22,0x12,0x30,
+0xe6,0x78,0xad,0xe6,0x30,0xe0,0x2c,0x78,0xad,0xe6,0x30,0xe1,0x26,0x78,0xad,0xe6,
+0xfc,0xf5,0x83,0x18,0xe6,0x44,0xf0,0xfd,0x12,0x1c,0x3c,0x90,0xff,0xfc,0xe0,0x44,
+0x20,0xf0,0x7c,0x02,0x12,0x32,0xa9,0x78,0xad,0xe6,0x54,0xfd,0xf6,0x74,0x1a,0x90,
+0xff,0xfe,0xf0,0x78,0xad,0xe6,0xfc,0xf5,0x83,0x18,0xe6,0x44,0xf1,0xfd,0x12,0x1c,
+0x3c,0x12,0x31,0x69,0x22,0x75,0x6d,0x00,0x90,0xff,0xff,0xe0,0x60,0x03,0x43,0x6d,
+0x01,0x75,0x6e,0x00,0xe4,0xf5,0x6c,0xf5,0x6b,0xe4,0xf5,0x6f,0x75,0x70,0x49,0x74,
+0x84,0x90,0xff,0x82,0xf0,0x74,0x84,0x90,0xff,0x80,0xf0,0x74,0x80,0x90,0xff,0x58,
+0xf0,0x74,0x80,0x90,0xff,0x5a,0xf0,0xad,0x46,0xaf,0x45,0x7e,0x00,0xee,0x24,0xfe,
+0x50,0x03,0x02,0x21,0x24,0xe4,0xee,0x75,0xf0,0x07,0xa4,0x24,0x7f,0xf5,0x82,0xe4,
+0x34,0xf8,0xf5,0x83,0xe0,0xff,0xe4,0xef,0x54,0x80,0xfd,0xe4,0xef,0x54,0x0f,0x14,
+0xff,0xed,0x60,0x38,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x48,0xf5,0x82,0xe4,0x34,
+0xff,0xf5,0x83,0x74,0x90,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x4a,0xf5,0x82,
+0xe4,0x34,0xff,0xf5,0x83,0x74,0x80,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x4e,
+0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0x74,0x80,0xf0,0x80,0x34,0xe4,0xef,0x75,0xf0,
+0x08,0xa4,0x24,0x08,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0x74,0x90,0xf0,0xe4,0xef,
+0x75,0xf0,0x08,0xa4,0x24,0x0a,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0xe4,
+0xef,0x75,0xf0,0x08,0xa4,0x24,0x0e,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,
+0x0e,0x02,0x20,0x8d,0x8d,0x46,0x8e,0x44,0x8f,0x45,0x74,0x7f,0x90,0xff,0xfd,0xf0,
+0x74,0x90,0x90,0xff,0xfc,0xf0,0x22,0x8c,0x58,0xec,0x24,0xf6,0x50,0x06,0xe5,0x58,
+0x24,0x37,0xfc,0x22,0xe5,0x58,0x24,0x30,0xfc,0x22,0x12,0x25,0x23,0xec,0x70,0x03,
+0x02,0x22,0x5e,0x75,0x5c,0x03,0xae,0x5b,0x7f,0x00,0xe5,0x5c,0x15,0x5c,0x64,0x80,
+0x24,0x7f,0x50,0x35,0xef,0x24,0x00,0xf5,0x82,0xe4,0x34,0xfb,0xf5,0x83,0xe0,0xfe,
+0x24,0xfe,0x50,0x1e,0xef,0x7d,0x00,0xfc,0xe4,0xfb,0x74,0x74,0xc3,0x9c,0xfa,0xeb,
+0x9d,0xfb,0xee,0x7d,0x00,0xfc,0xea,0xc3,0x9c,0xed,0x64,0x80,0xcb,0x64,0x80,0x9b,
+0x50,0x02,0x80,0x05,0xef,0x2e,0xff,0x80,0xc1,0x8e,0x5b,0x8f,0x5a,0xe5,0x5c,0x64,
+0x80,0x24,0x7f,0x50,0x03,0x02,0x22,0x5e,0xe5,0x5a,0x24,0x8e,0x50,0x03,0x02,0x22,
+0x5e,0x85,0x5a,0x5d,0x75,0x5b,0x00,0xae,0x5a,0xaf,0x5b,0x90,0x35,0x9c,0xe4,0x93,
+0xf5,0x5c,0xe5,0x5c,0x15,0x5c,0x64,0x80,0x24,0x7f,0x50,0x18,0xee,0x24,0x00,0xf5,
+0x82,0xe4,0x34,0xfb,0xf5,0x83,0xe0,0xfc,0xef,0x90,0x35,0x9c,0x93,0x6c,0x70,0x04,
+0x0e,0x0f,0x80,0xde,0x8e,0x5a,0x8f,0x5b,0xe5,0x5c,0x64,0x80,0x24,0x7f,0x40,0x6e,
+0x75,0x5e,0x01,0x75,0x60,0xe8,0x75,0x5f,0xff,0xe5,0x5d,0x24,0x02,0xf5,0x5a,0x75,
+0x5c,0x07,0xe5,0x5c,0x33,0x40,0x57,0xad,0x60,0xae,0x5f,0xaf,0x5e,0xe5,0x5c,0xf5,
+0x82,0x33,0x95,0xe0,0xf5,0x83,0x12,0x01,0xec,0xc4,0x54,0x0f,0xfc,0x12,0x21,0x37,
+0xe5,0x5a,0x24,0x00,0xf5,0x82,0xe4,0x34,0xfb,0xf5,0x83,0xec,0xf0,0x05,0x5a,0x05,
+0x5a,0xad,0x60,0xae,0x5f,0xaf,0x5e,0xe5,0x5c,0xf5,0x82,0x33,0x95,0xe0,0xf5,0x83,
+0x12,0x01,0xec,0x54,0x0f,0xfc,0x12,0x21,0x37,0xe5,0x5a,0x24,0x00,0xf5,0x82,0xe4,
+0x34,0xfb,0xf5,0x83,0xec,0xf0,0x05,0x5a,0x05,0x5a,0x15,0x5c,0x80,0xa4,0x74,0x02,
+0x90,0xf8,0x51,0xf0,0x90,0xf8,0x6b,0x79,0x75,0x7a,0x35,0x7b,0x27,0x78,0x01,0x12,
+0x03,0xf5,0x75,0x6a,0x35,0x75,0x69,0xfc,0x75,0x68,0x01,0xe4,0x90,0xff,0x83,0xf0,
+0x74,0x80,0x90,0xff,0x81,0xf0,0x75,0x59,0x02,0xe5,0x59,0x75,0xf0,0x07,0xa4,0x24,
+0x7f,0xf5,0x82,0xe4,0x34,0xf8,0xf5,0x83,0xe0,0x78,0x8f,0xf6,0xfc,0x54,0x0f,0x14,
+0xfc,0x78,0x8f,0xec,0xf6,0xe5,0x59,0x75,0xf0,0x07,0xa4,0x24,0x81,0xf5,0x82,0xe4,
+0x34,0xf8,0xf5,0x83,0xe0,0x78,0x92,0x76,0xfd,0x08,0x76,0xe8,0xfc,0x78,0x8f,0xe6,
+0x75,0xf0,0x08,0xa4,0x24,0x48,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0x78,
+0x8f,0xe6,0x75,0xf0,0x08,0xa4,0x24,0x4f,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xec,
+0xf0,0x78,0x92,0xe6,0xff,0x08,0xe6,0x7e,0x03,0xcf,0xc3,0x13,0xcf,0x13,0xde,0xf9,
+0xfe,0x78,0x8f,0xe6,0x75,0xf0,0x08,0xa4,0x24,0x49,0xf5,0x82,0xe4,0x34,0xff,0xf5,
+0x83,0xee,0xf0,0x78,0x8f,0xe6,0x75,0xf0,0x08,0xa4,0x24,0x4a,0xf5,0x82,0xe4,0x34,
+0xff,0xf5,0x83,0x74,0x80,0xf0,0x78,0x90,0xec,0xf6,0x7d,0x00,0x78,0x93,0xe6,0x2c,
+0xf6,0x18,0xe6,0x3d,0xf6,0x78,0x92,0xe6,0xfd,0x08,0xe6,0x7c,0x03,0xcd,0xc3,0x13,
+0xcd,0x13,0xdc,0xf9,0xfc,0x78,0x8f,0xe6,0x75,0xf0,0x08,0xa4,0x24,0x4d,0xf5,0x82,
+0xe4,0x34,0xff,0xf5,0x83,0xec,0xf0,0x78,0x8f,0xe6,0x75,0xf0,0x08,0xa4,0x24,0x4e,
+0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0x78,0x92,0xe6,0xfd,0x08,0xe6,0xfc,
+0x78,0x8f,0xe6,0xff,0x7e,0x00,0xee,0x24,0xfe,0x50,0x03,0x02,0x24,0xdd,0xe4,0xee,
+0x75,0xf0,0x07,0xa4,0x24,0x7f,0xf5,0x82,0xe4,0x34,0xf8,0xf5,0x83,0xe0,0xff,0xe4,
+0xef,0x54,0x80,0xfa,0xe4,0xef,0x54,0x0f,0x14,0xff,0xe4,0xee,0x75,0xf0,0x07,0xa4,
+0x24,0x81,0xf5,0x82,0xe4,0x34,0xf8,0xf5,0x83,0xe0,0x78,0x90,0xf6,0xe4,0xee,0x13,
+0x13,0x54,0x80,0x24,0xf0,0xf8,0xe4,0x34,0xfd,0xf9,0xe8,0xfc,0xe9,0xfd,0x8a,0x5a,
+0xea,0x70,0x03,0x02,0x24,0x4a,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x48,0xf5,0x82,
+0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0x78,0x90,0xe6,0xfa,0xe4,0xef,0x75,0xf0,0x08,
+0xa4,0x24,0x4f,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xea,0xf0,0xed,0xfb,0xec,0x7a,
+0x03,0xcb,0xc3,0x13,0xcb,0x13,0xda,0xf9,0xfa,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,
+0x49,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xea,0xf0,0x78,0x90,0xe6,0x7b,0x00,0xfa,
+0xec,0x2a,0xfc,0xed,0x3b,0xfd,0xfb,0xec,0x7a,0x03,0xcb,0xc3,0x13,0xcb,0x13,0xda,
+0xf9,0xfa,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x4d,0xf5,0x82,0xe4,0x34,0xff,0xf5,
+0x83,0xea,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x4a,0xf5,0x82,0xe4,0x34,0xff,
+0xf5,0x83,0x74,0x80,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x4e,0xf5,0x82,0xe4,
+0x34,0xff,0xf5,0x83,0x74,0x80,0xf0,0x02,0x24,0xd9,0xe4,0xef,0x75,0xf0,0x08,0xa4,
+0x24,0x08,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0x78,0x90,0xe6,0xfa,0xe4,
+0xef,0x75,0xf0,0x08,0xa4,0x24,0x0f,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xea,0xf0,
+0xed,0xfb,0xec,0x7a,0x03,0xcb,0xc3,0x13,0xcb,0x13,0xda,0xf9,0xfa,0xe4,0xef,0x75,
+0xf0,0x08,0xa4,0x24,0x09,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xea,0xf0,0x78,0x90,
+0xe6,0x7b,0x00,0xfa,0xec,0x2a,0xfc,0xed,0x3b,0xfd,0xfb,0xec,0x7a,0x03,0xcb,0xc3,
+0x13,0xcb,0x13,0xda,0xf9,0xfa,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x0d,0xf5,0x82,
+0xe4,0x34,0xff,0xf5,0x83,0xea,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x0a,0xf5,
+0x82,0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x0e,
+0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0x0e,0x02,0x23,0x66,0x8e,0x59,0x78,
+0x92,0xed,0xf6,0x08,0xec,0xf6,0x78,0x8f,0xef,0xf6,0x12,0x20,0x55,0x22,0x8c,0x26,
+0xec,0x30,0xe7,0x18,0xe5,0x26,0x54,0x0f,0x14,0x75,0xf0,0x08,0xa4,0x24,0x48,0xf5,
+0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x54,0xdf,0xf0,0x80,0x16,0xe5,0x26,0x54,0x0f,
+0x14,0x75,0xf0,0x08,0xa4,0x24,0x08,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x54,
+0xdf,0xf0,0x22,0x7c,0x00,0x22,0xec,0x90,0xfc,0x37,0xf0,0x8c,0x24,0xed,0x24,0x03,
+0xf5,0x25,0x7d,0x00,0xd3,0x95,0x72,0xed,0x95,0x71,0x40,0x03,0x85,0x72,0x25,0xe5,
+0x25,0x24,0xb7,0x50,0x09,0x75,0x25,0x03,0x74,0x02,0x90,0xfc,0x37,0xf0,0xac,0x25,
+0x12,0x30,0x24,0x22,0xe4,0xf5,0x6c,0xf5,0x6b,0x12,0x25,0x5d,0x22,0x90,0xfc,0x35,
+0xe0,0x65,0x73,0x60,0x0e,0x74,0x04,0x90,0xfc,0x37,0xf0,0xe4,0xf5,0x6b,0x75,0x6c,
+0x03,0x80,0x46,0x7d,0x73,0xe4,0xfe,0xff,0x79,0x35,0x7a,0xfc,0x7b,0x01,0x74,0x05,
+0x78,0x00,0x12,0x03,0x3f,0xe5,0x6c,0x24,0x03,0xf5,0x6c,0xe5,0x6b,0x34,0x00,0xf5,
+0x6b,0xe5,0x6c,0xd3,0x95,0x72,0xe5,0x6b,0x95,0x71,0x40,0x06,0x85,0x72,0x6c,0x85,
+0x71,0x6b,0xd3,0xe5,0x6c,0x94,0x48,0xe5,0x6b,0x94,0x00,0x40,0x0c,0x74,0x02,0x90,
+0xfc,0x37,0xf0,0xe4,0xf5,0x6b,0x75,0x6c,0x03,0xac,0x6c,0x12,0x30,0x24,0x22,0xec,
+0x90,0xfc,0x37,0xf0,0xe4,0xf5,0x6c,0xf5,0x6b,0x8c,0x32,0xec,0x60,0x05,0x12,0x30,
+0x15,0x80,0x05,0x7c,0x00,0x12,0x30,0x24,0x22,0x90,0xff,0x93,0xe0,0x44,0x01,0xf0,
+0xb2,0xb3,0x90,0xff,0x04,0xe0,0xf5,0x4a,0x90,0xff,0x06,0xe0,0xfd,0xa3,0xe0,0xed,
+0x7d,0x00,0xfc,0x7d,0x00,0xfc,0x90,0xff,0x06,0xe0,0xff,0xa3,0xe0,0x7e,0x00,0xff,
+0xe4,0xfe,0xec,0x4e,0xfc,0xed,0x4f,0xfd,0xc3,0xec,0x94,0x48,0xed,0x94,0x00,0x50,
+0x22,0x90,0xff,0x06,0xe0,0xfd,0xa3,0xe0,0xed,0x7d,0x00,0xfc,0x7d,0x00,0xfc,0x90,
+0xff,0x06,0xe0,0xff,0xa3,0xe0,0x7e,0x00,0xff,0xe4,0xfe,0xec,0x4e,0xfc,0xed,0x4f,
+0xfd,0x80,0x04,0xe4,0xfd,0x7c,0x48,0x8c,0x72,0x8d,0x71,0x90,0xff,0x02,0xe0,0xfd,
+0xa3,0xe0,0xed,0x7d,0x00,0xfc,0x7d,0x00,0xfc,0x90,0xff,0x02,0xe0,0xff,0xa3,0xe0,
+0x7e,0x00,0xff,0xe4,0xfe,0xec,0x4e,0xf5,0x4c,0xed,0x4f,0xf5,0x4b,0x75,0x6a,0x35,
+0x75,0x69,0xfc,0x75,0x68,0x01,0x7d,0x35,0x7e,0xfc,0x7f,0x01,0x79,0x73,0xe4,0xfa,
+0xfb,0x74,0x05,0x78,0x00,0x12,0x03,0x3f,0x75,0x49,0x00,0xe5,0x49,0x24,0xfe,0x40,
+0x19,0xad,0x6a,0xae,0x69,0xaf,0x68,0xe4,0x12,0x03,0x0f,0x05,0x49,0x0d,0xed,0x70,
+0x01,0x0e,0x8d,0x6a,0x8e,0x69,0x8f,0x68,0x80,0xe1,0x75,0x6a,0x35,0x75,0x69,0xfc,
+0x75,0x68,0x01,0x90,0xff,0x00,0xe0,0x54,0x60,0xb4,0x00,0x02,0x80,0x06,0xd3,0x50,
+0x03,0x02,0x2c,0x6d,0xe5,0x4a,0x54,0x0f,0xf5,0x49,0xe5,0x4a,0x54,0x80,0xa2,0xe0,
+0x92,0x02,0x90,0xff,0x01,0xe0,0x12,0x01,0x81,0x00,0x0b,0x2c,0x68,0x26,0xe5,0x28,
+0x03,0x2c,0x68,0x29,0x0f,0x2c,0x68,0x29,0xf2,0x2a,0x26,0x2b,0x8d,0x2b,0x90,0x2b,
+0xd0,0x2c,0x11,0x2c,0x3f,0xe5,0x6d,0x30,0xe7,0x0e,0xe5,0x4c,0x45,0x4b,0x70,0x08,
+0xe5,0x72,0x64,0x02,0x45,0x71,0x60,0x03,0x02,0x2c,0x6a,0x90,0xff,0x00,0xe0,0x54,
+0x1f,0xb4,0x00,0x02,0x80,0x03,0xd3,0x40,0x29,0xe5,0x4a,0x60,0x03,0x02,0x28,0x00,
+0xad,0x6a,0xae,0x69,0xaf,0x68,0x74,0x01,0x12,0x03,0x0f,0x78,0xad,0xe6,0x30,0xe0,
+0x0b,0xad,0x6a,0xae,0x69,0xaf,0x68,0x74,0x02,0x12,0x03,0x0f,0x7c,0x02,0x12,0x30,
+0x24,0x22,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x1b,0xe5,0x6d,0x20,0xe1,0x07,0xe5,
+0x4a,0x60,0x03,0x02,0x28,0x00,0xe5,0x4a,0x24,0xfe,0x50,0x03,0x02,0x28,0x00,0x7c,
+0x02,0x12,0x30,0x24,0x22,0xb4,0x02,0x02,0x80,0x06,0xd3,0x50,0x03,0x02,0x27,0xfe,
+0xe5,0x6d,0x20,0xe1,0x0d,0xe5,0x4a,0x60,0x09,0xe5,0x4a,0x64,0x80,0x60,0x03,0x02,
+0x28,0x00,0xac,0x4a,0x12,0x30,0xab,0x40,0x03,0x02,0x28,0x00,0xe5,0x49,0x70,0x25,
+0x30,0x02,0x11,0x90,0xff,0x80,0xe0,0x54,0x08,0xad,0x6a,0xae,0x69,0xaf,0x68,0x12,
+0x03,0x0f,0x80,0x0f,0x90,0xff,0x82,0xe0,0x54,0x08,0xad,0x6a,0xae,0x69,0xaf,0x68,
+0x12,0x03,0x0f,0x80,0x3d,0x15,0x49,0x30,0x02,0x1d,0xe5,0x49,0x75,0xf0,0x08,0xa4,
+0x24,0x48,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x54,0x08,0xad,0x6a,0xae,0x69,
+0xaf,0x68,0x12,0x03,0x0f,0x80,0x1b,0xe5,0x49,0x75,0xf0,0x08,0xa4,0x24,0x08,0xf5,
+0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x54,0x08,0xad,0x6a,0xae,0x69,0xaf,0x68,0x12,
+0x03,0x0f,0xad,0x6a,0xae,0x69,0xaf,0x68,0x12,0x01,0xe6,0x60,0x0b,0xad,0x6a,0xae,
+0x69,0xaf,0x68,0x74,0x01,0x12,0x03,0x0f,0x7c,0x02,0x12,0x30,0x24,0x22,0x80,0x00,
+0x02,0x2c,0x6a,0xe5,0x6d,0x20,0xe7,0x06,0xe5,0x72,0x45,0x71,0x60,0x03,0x02,0x2c,
+0x6a,0x90,0xff,0x00,0xe0,0x54,0x1f,0xb4,0x00,0x02,0x80,0x03,0xd3,0x40,0x1a,0xe5,
+0x4c,0x14,0x45,0x4b,0x70,0x04,0xe5,0x4a,0x60,0x03,0x02,0x29,0x0c,0x78,0xad,0xe6,
+0x54,0xfe,0xf6,0x7c,0x00,0x12,0x30,0x24,0x22,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,
+0x2a,0xe5,0x6d,0x20,0xe1,0x08,0xe5,0x6d,0x20,0xe0,0x03,0x02,0x29,0x0c,0xe5,0x6d,
+0x30,0xe0,0x04,0xe5,0x4a,0x70,0x0b,0xe5,0x6d,0x30,0xe1,0x09,0xe5,0x4a,0x24,0xfe,
+0x50,0x03,0x02,0x29,0x0c,0x7c,0x00,0x12,0x30,0x24,0x22,0xb4,0x02,0x02,0x80,0x06,
+0xd3,0x50,0x03,0x02,0x29,0x0a,0xe5,0x4c,0x45,0x4b,0x60,0x03,0x02,0x29,0x0c,0xac,
+0x4a,0x12,0x30,0xab,0x40,0x03,0x02,0x29,0x0c,0xe5,0x6d,0x20,0xe1,0x07,0xe5,0x6d,
+0x20,0xe0,0x02,0x80,0x77,0xe5,0x6d,0x30,0xe0,0x06,0xe5,0x49,0x60,0x02,0x80,0x6c,
+0xe5,0x49,0x70,0x0f,0x90,0xff,0x82,0xe0,0x54,0xf7,0xf0,0x90,0xff,0x80,0xe0,0x54,
+0xf7,0xf0,0x22,0xe5,0x49,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x09,0x7d,0x01,0x7c,
+0x03,0x12,0x0f,0x09,0x80,0x11,0xb4,0x02,0x02,0x80,0x03,0xd3,0x40,0x09,0x7d,0x01,
+0x7c,0x04,0x12,0x0f,0x09,0x80,0x00,0x15,0x49,0x30,0x02,0x15,0xe5,0x49,0x75,0xf0,
+0x08,0xa4,0x24,0x48,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x54,0xf7,0xf0,0x80,
+0x13,0xe5,0x49,0x75,0xf0,0x08,0xa4,0x24,0x08,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,
+0xe0,0x54,0xf7,0xf0,0x7c,0x00,0x12,0x30,0x24,0x22,0x80,0x00,0x02,0x2c,0x6a,0xe5,
+0x6d,0x20,0xe7,0x06,0xe5,0x72,0x45,0x71,0x60,0x03,0x02,0x2c,0x6a,0x90,0xff,0x00,
+0xe0,0x54,0x1f,0xb4,0x00,0x02,0x80,0x03,0xd3,0x40,0x1a,0xe5,0x4c,0x14,0x45,0x4b,
+0x70,0x04,0xe5,0x4a,0x60,0x03,0x02,0x29,0xef,0x78,0xad,0xe6,0x44,0x01,0xf6,0x7c,
+0x00,0x12,0x30,0x24,0x22,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x29,0xe5,0x6d,0x20,
+0xe1,0x08,0xe5,0x6d,0x20,0xe0,0x03,0x02,0x29,0xef,0xe5,0x6d,0x30,0xe0,0x04,0xe5,
+0x49,0x70,0x0b,0xe5,0x6d,0x30,0xe1,0x08,0xe5,0x49,0x24,0xfe,0x50,0x02,0x80,0x7f,
+0x7c,0x00,0x12,0x30,0x24,0x22,0xb4,0x02,0x02,0x80,0x03,0xd3,0x40,0x6f,0xe5,0x4c,
+0x45,0x4b,0x60,0x02,0x80,0x69,0xac,0x4a,0x12,0x30,0xab,0x40,0x02,0x80,0x60,0xe5,
+0x6d,0x20,0xe1,0x07,0xe5,0x6d,0x20,0xe0,0x02,0x80,0x54,0xe5,0x49,0x70,0x14,0x30,
+0x02,0x09,0x90,0xff,0x80,0xe0,0x44,0x08,0xf0,0x80,0x07,0x90,0xff,0x82,0xe0,0x44,
+0x08,0xf0,0x22,0xe5,0x6d,0x30,0xe1,0x33,0x15,0x49,0x30,0x02,0x15,0xe5,0x49,0x75,
+0xf0,0x08,0xa4,0x24,0x48,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x44,0x08,0xf0,
+0x80,0x13,0xe5,0x49,0x75,0xf0,0x08,0xa4,0x24,0x08,0xf5,0x82,0xe4,0x34,0xff,0xf5,
+0x83,0xe0,0x44,0x08,0xf0,0x7c,0x00,0x12,0x30,0x24,0x22,0x80,0x02,0x80,0x00,0x02,
+0x2c,0x6a,0xe5,0x6d,0x20,0xe7,0x12,0xe5,0x72,0x45,0x71,0x70,0x0c,0xe5,0x4a,0x70,
+0x08,0x90,0xff,0x00,0xe0,0x54,0x1f,0x60,0x03,0x02,0x2c,0x6a,0xe5,0x4c,0x90,0xff,
+0xff,0xf0,0x90,0xff,0xff,0xe0,0x60,0x05,0x43,0x6d,0x01,0x80,0x03,0x53,0x6d,0xfe,
+0x7c,0x00,0x12,0x30,0x24,0x22,0xe5,0x6d,0x30,0xe7,0x0e,0xe5,0x72,0x45,0x71,0x60,
+0x08,0x90,0xff,0x00,0xe0,0x54,0x1f,0x60,0x03,0x02,0x2c,0x6a,0xad,0x4b,0xe5,0x4c,
+0xed,0x7d,0x00,0xfc,0x7d,0x00,0xfc,0xbd,0x00,0x02,0x80,0x03,0x02,0x2b,0x88,0xb4,
+0x01,0x02,0x80,0x03,0xd3,0x40,0x32,0xe5,0x4a,0x70,0x05,0xe5,0x4c,0xfc,0x60,0x03,
+0x02,0x2b,0x8a,0x75,0x6a,0x40,0x75,0x69,0xf8,0x75,0x68,0x01,0xd3,0xe5,0x72,0x94,
+0x12,0xe5,0x71,0x94,0x00,0x40,0x06,0xe4,0xfd,0x7c,0x12,0x80,0x04,0xac,0x72,0xad,
+0x71,0x8c,0x70,0x8d,0x6f,0x12,0x30,0x39,0x22,0xb4,0x02,0x02,0x80,0x03,0xd3,0x40,
+0x59,0xe5,0x4a,0x60,0x03,0x02,0x2b,0x8a,0xe5,0x4c,0xfc,0x70,0x27,0x75,0x6a,0x52,
+0x75,0x69,0xf8,0x75,0x68,0x01,0xd3,0xe5,0x72,0x94,0x19,0xe5,0x71,0x94,0x00,0x40,
+0x06,0xe4,0xfd,0x7c,0x19,0x80,0x04,0xac,0x72,0xad,0x71,0x8c,0x70,0x8d,0x6f,0x12,
+0x30,0x39,0x80,0x25,0x75,0x6a,0x6b,0x75,0x69,0xf8,0x75,0x68,0x01,0xd3,0xe5,0x72,
+0x94,0x27,0xe5,0x71,0x94,0x00,0x40,0x06,0xe4,0xfd,0x7c,0x27,0x80,0x04,0xac,0x72,
+0xad,0x71,0x8c,0x70,0x8d,0x6f,0x12,0x30,0x39,0x22,0xb4,0x03,0x02,0x80,0x06,0xd3,
+0x50,0x03,0x02,0x2b,0x88,0xe5,0x4c,0xf5,0x49,0x70,0x0f,0x90,0xff,0x04,0xe0,0xfd,
+0xa3,0xe0,0x4d,0x60,0x03,0x02,0x2b,0x8a,0x80,0x18,0x90,0xfb,0x02,0xe0,0xfd,0xa3,
+0xe0,0xfc,0x90,0xff,0x05,0xe0,0x6c,0x70,0x07,0x90,0xff,0x04,0xe0,0x6d,0x60,0x02,
+0x80,0x68,0xe4,0xf5,0x70,0xf5,0x6f,0x7f,0x00,0xe5,0x49,0x14,0xc5,0x49,0x60,0x0f,
+0xef,0x24,0x00,0xf5,0x82,0xe4,0x34,0xfb,0xf5,0x83,0xe0,0x2f,0xff,0x80,0xea,0x8f,
+0x4a,0xe5,0x4a,0x24,0x00,0xf5,0x82,0xe4,0x34,0xfb,0xf5,0x83,0xe0,0x7d,0x00,0xd3,
+0x95,0x72,0xed,0x95,0x71,0x40,0x06,0xac,0x72,0xad,0x71,0x80,0x0f,0xe5,0x4a,0x24,
+0x00,0xf5,0x82,0xe4,0x34,0xfb,0xf5,0x83,0xe0,0x7d,0x00,0xfc,0x8c,0x70,0x8d,0x6f,
+0xe5,0x4a,0x24,0x00,0xfc,0xe4,0x34,0xfb,0xfd,0xfe,0xec,0xfd,0x7f,0x01,0x8d,0x6a,
+0x8e,0x69,0x8f,0x68,0x12,0x30,0x39,0x22,0x80,0x00,0x02,0x2c,0x6a,0x02,0x2c,0x6a,
+0xe5,0x6d,0x30,0xe7,0x19,0xe5,0x72,0x14,0x45,0x71,0x70,0x12,0xe5,0x4a,0x70,0x0e,
+0xe5,0x4c,0x45,0x4b,0x70,0x08,0x90,0xff,0x00,0xe0,0x54,0x1f,0x60,0x03,0x02,0x2c,
+0x6a,0xe5,0x6d,0x20,0xe0,0x08,0xe5,0x6d,0x20,0xe1,0x03,0x02,0x2c,0x6a,0x75,0x6a,
+0x6e,0xe4,0xf5,0x69,0xf5,0x68,0xe4,0xf5,0x6f,0x04,0xf5,0x70,0x12,0x30,0x39,0x22,
+0xe5,0x6d,0x20,0xe7,0x12,0xe5,0x72,0x45,0x71,0x70,0x0c,0xe5,0x4a,0x70,0x08,0x90,
+0xff,0x00,0xe0,0x54,0x1f,0x60,0x03,0x02,0x2c,0x6a,0xe5,0x6d,0x20,0xe0,0x07,0xe5,
+0x6d,0x20,0xe1,0x02,0x80,0x74,0x85,0x4c,0x6e,0xe5,0x6e,0x70,0x08,0x43,0x6d,0x01,
+0x53,0x6d,0xfd,0x80,0x06,0x53,0x6d,0xfe,0x43,0x6d,0x02,0x7c,0x00,0x12,0x30,0x24,
+0x22,0xe5,0x6d,0x30,0xe7,0x1a,0xe5,0x72,0x14,0x45,0x71,0x70,0x13,0xe5,0x4a,0x70,
+0x0f,0xe5,0x4c,0x45,0x4b,0x70,0x09,0x90,0xff,0x00,0xe0,0x54,0x1f,0x14,0x60,0x02,
+0x80,0x38,0xe5,0x6d,0x20,0xe1,0x02,0x80,0x31,0x7c,0x01,0x12,0x30,0x24,0x22,0xe5,
+0x6d,0x20,0xe7,0x15,0xe5,0x72,0x45,0x71,0x70,0x0f,0xe5,0x4c,0x45,0x4b,0x70,0x09,
+0x90,0xff,0x00,0xe0,0x54,0x1f,0x14,0x60,0x02,0x80,0x0f,0xe5,0x6d,0x20,0xe1,0x02,
+0x80,0x08,0x7c,0x00,0x12,0x30,0x24,0x22,0x80,0x00,0x02,0x2f,0x2b,0xb4,0x40,0x02,
+0x80,0x06,0xd3,0x50,0x03,0x02,0x2f,0x21,0x90,0xff,0x01,0xe0,0x90,0xfc,0x35,0xf0,
+0xe5,0x4a,0x90,0xfc,0x36,0xf0,0xe4,0x90,0xfc,0x37,0xf0,0xe5,0x6a,0x24,0x03,0xf5,
+0x6a,0xe5,0x69,0x34,0x00,0xf5,0x69,0xad,0x4b,0xe5,0x4c,0x85,0x6a,0x82,0x85,0x69,
+0x83,0xcd,0xf0,0xa3,0xcd,0xf0,0x90,0xff,0x01,0xe0,0x12,0x01,0xb7,0x2c,0xd8,0x01,
+0x2c,0xfe,0x02,0x2d,0x28,0x03,0x2d,0x52,0x04,0x2d,0xa0,0x05,0x2d,0xdd,0x06,0x2e,
+0x03,0x07,0x2e,0x29,0x08,0x2e,0x55,0x09,0x2e,0x7b,0x0b,0x2e,0xa1,0x0c,0x2e,0xb0,
+0x80,0x2e,0xb0,0x81,0x00,0x00,0x2f,0x0e,0xe5,0x6d,0x20,0xe7,0x06,0x7c,0x05,0x12,
+0x25,0xbf,0x22,0x7d,0x24,0x7e,0x35,0x7f,0x02,0x79,0x38,0x7a,0xfc,0x7b,0x01,0x74,
+0x08,0x78,0x00,0x12,0x03,0x3f,0x7d,0x08,0x7c,0x00,0x12,0x25,0x26,0x22,0xe5,0x6d,
+0x20,0xe7,0x06,0x7c,0x05,0x12,0x25,0xbf,0x22,0xe5,0x4a,0xb4,0x03,0x00,0x40,0x10,
+0xb4,0x05,0x00,0x50,0x0b,0xe5,0x4a,0x7f,0x00,0xfe,0x7c,0x10,0x12,0x31,0xfb,0x22,
+0x7d,0x00,0x7c,0x07,0x12,0x25,0x26,0x22,0xe5,0x6d,0x20,0xe7,0x06,0x7c,0x05,0x12,
+0x25,0xbf,0x22,0xe5,0x4a,0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,
+0x4a,0x7f,0x00,0xfe,0x7c,0x11,0x12,0x31,0xfb,0x22,0x7d,0x00,0x7c,0x07,0x12,0x25,
+0x26,0x22,0xe5,0x6d,0x20,0xe7,0x06,0x7c,0x05,0x12,0x25,0xbf,0x22,0xe5,0x4a,0xb4,
+0x05,0x02,0x80,0x03,0xd3,0x40,0x0a,0xe4,0xff,0x04,0xfe,0x7c,0x0a,0x12,0x31,0xfb,
+0x22,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x0a,0xe4,0xff,0x04,0xfe,0x7c,0x08,0x12,
+0x31,0xfb,0x22,0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,0x4a,0x7f,
+0x00,0xfe,0x7c,0x13,0x12,0x31,0xfb,0x22,0x7d,0x00,0x7c,0x07,0x12,0x25,0x26,0x22,
+0xe5,0x6d,0x20,0xe7,0x34,0xd3,0xe5,0x72,0x94,0x48,0xe5,0x71,0x94,0x00,0x50,0x06,
+0xe5,0x72,0x45,0x71,0x70,0x06,0x7c,0x02,0x12,0x25,0xbf,0x22,0xe5,0x4a,0xb4,0x01,
+0x03,0xb3,0x40,0x0b,0xc3,0xb4,0x03,0x00,0x40,0x09,0xb4,0x06,0x00,0x50,0x04,0x12,
+0x30,0xd1,0x22,0x7c,0x07,0x12,0x25,0xbf,0x22,0x12,0x25,0x5d,0x22,0xe5,0x6d,0x20,
+0xe7,0x1d,0xe5,0x4a,0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,0x4a,
+0x7f,0x00,0xfe,0x7c,0x16,0x12,0x31,0xfb,0x22,0x7c,0x07,0x12,0x25,0xbf,0x22,0x12,
+0x25,0x5d,0x22,0xe5,0x6d,0x20,0xe7,0x1d,0xe5,0x4a,0xb4,0x03,0x00,0x40,0x10,0xb4,
+0x05,0x00,0x50,0x0b,0xe5,0x4a,0x7f,0x00,0xfe,0x7c,0x19,0x12,0x31,0xfb,0x22,0x7c,
+0x07,0x12,0x25,0xbf,0x22,0x12,0x25,0x5d,0x22,0xe5,0x6d,0x20,0xe7,0x23,0x74,0x81,
+0x90,0xff,0x93,0xf0,0xe5,0x4a,0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,
+0xe5,0x4a,0x7f,0x00,0xfe,0x7c,0x17,0x12,0x31,0xfb,0x22,0x7c,0x07,0x12,0x25,0xbf,
+0x22,0x12,0x25,0x5d,0x22,0xe5,0x6d,0x20,0xe7,0x1d,0xe5,0x4a,0xb4,0x03,0x00,0x40,
+0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,0x4a,0x7f,0x00,0xfe,0x7c,0x18,0x12,0x31,0xfb,
+0x22,0x7c,0x07,0x12,0x25,0xbf,0x22,0x12,0x25,0x5d,0x22,0xe5,0x6d,0x20,0xe7,0x1d,
+0xe5,0x4a,0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,0x4a,0x7f,0x00,
+0xfe,0x7c,0x15,0x12,0x31,0xfb,0x22,0x7c,0x07,0x12,0x25,0xbf,0x22,0x12,0x25,0x5d,
+0x22,0xe5,0x6d,0x20,0xe7,0x06,0x7c,0x07,0x12,0x25,0xbf,0x22,0x12,0x25,0x5d,0x22,
+0xe5,0x6d,0x30,0xe7,0x20,0x90,0xff,0x00,0xe0,0x54,0x1f,0x70,0x10,0x90,0xff,0x01,
+0xe0,0xb4,0x80,0x05,0x12,0x25,0x54,0x80,0x03,0x12,0x25,0x5d,0x22,0x7d,0x00,0x7c,
+0x05,0x12,0x25,0x26,0x22,0x90,0xff,0x00,0xe0,0x54,0x1f,0x60,0x06,0x7c,0x05,0x12,
+0x25,0xbf,0x22,0xd3,0xe5,0x72,0x94,0x48,0xe5,0x71,0x94,0x00,0x50,0x0b,0xc3,0xe5,
+0x72,0x94,0x07,0xe5,0x71,0x94,0x00,0x50,0x06,0x7c,0x03,0x12,0x25,0xbf,0x22,0xe5,
+0x4a,0xb4,0x05,0x04,0x12,0x30,0xd1,0x22,0x7c,0x07,0x12,0x25,0xbf,0x22,0xe5,0x6d,
+0x30,0xe7,0x08,0x7d,0x00,0x7c,0x05,0x12,0x25,0x26,0x22,0x7c,0x05,0x12,0x25,0xbf,
+0x22,0xb4,0x20,0x02,0x80,0x03,0xd3,0x40,0x00,0x80,0x00,0x12,0x2f,0xff,0x22,0x75,
+0x43,0x00,0x90,0xff,0x83,0xe0,0x54,0x0f,0xd3,0x95,0x43,0x40,0x24,0xe5,0x43,0x24,
+0xf0,0xf5,0x82,0xe4,0x34,0xfe,0xf5,0x83,0xe0,0xad,0x6a,0xae,0x69,0xaf,0x68,0x12,
+0x03,0x0f,0x05,0x43,0x0d,0xed,0x70,0x01,0x0e,0x8d,0x6a,0x8e,0x69,0x8f,0x68,0x80,
+0xd1,0xe5,0x43,0x7d,0x00,0xfc,0xc3,0xe5,0x70,0x9c,0xf5,0x70,0xe5,0x6f,0x9d,0xf5,
+0x6f,0xe5,0x70,0x45,0x6f,0x60,0x06,0xe4,0x90,0xff,0x83,0xf0,0x22,0x90,0xff,0x82,
+0xe0,0x44,0x08,0xf0,0xe4,0xf5,0x6f,0x75,0x70,0x49,0x90,0xfc,0x35,0xe0,0xb4,0x05,
+0x02,0x80,0x03,0xd3,0x40,0x40,0x90,0xfc,0x36,0xe0,0xf5,0x43,0xb4,0x05,0x02,0x80,
+0x03,0xd3,0x40,0x0a,0xe4,0xff,0x04,0xfe,0x7c,0x0b,0x12,0x31,0xfb,0x22,0xb4,0x01,
+0x02,0x80,0x03,0xd3,0x40,0x0a,0xe4,0xff,0x04,0xfe,0x7c,0x09,0x12,0x31,0xfb,0x22,
+0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,0x43,0x7f,0x00,0xfe,0x7c,
+0x14,0x12,0x31,0xfb,0x22,0x22,0xb4,0x80,0x00,0x40,0x23,0xb4,0x82,0x00,0x50,0x1e,
+0x7c,0x35,0x7d,0xfc,0x12,0x17,0x7e,0x7d,0x00,0x8c,0x6c,0x8d,0x6b,0x90,0xfc,0x37,
+0xe0,0x60,0x05,0x12,0x2f,0xff,0x80,0x05,0x7c,0x00,0x12,0x30,0x24,0x22,0x22,0x90,
+0xff,0x83,0xe0,0x54,0x7f,0xf0,0x90,0xff,0x82,0xe0,0x44,0x08,0xf0,0x90,0xff,0x80,
+0xe0,0x44,0x08,0xf0,0x22,0x90,0xff,0x82,0xe0,0x44,0x08,0xf0,0x90,0xff,0x80,0xe0,
+0x44,0x08,0xf0,0x22,0x8c,0x23,0x7d,0x00,0x8c,0x70,0x8d,0x6f,0x75,0x6a,0x35,0x75,
+0x69,0xfc,0x75,0x68,0x01,0x12,0x30,0x39,0x22,0x90,0xff,0x83,0xe0,0x54,0x7f,0xf0,
+0xe5,0x70,0x64,0x49,0x45,0x6f,0x70,0x01,0x22,0xc3,0xe5,0x70,0x94,0x08,0xe5,0x6f,
+0x94,0x00,0x40,0x15,0x75,0x21,0x08,0xe5,0x21,0x7d,0x00,0xfc,0xc3,0xe5,0x70,0x9c,
+0xf5,0x70,0xe5,0x6f,0x9d,0xf5,0x6f,0x80,0x09,0x85,0x70,0x21,0xe4,0xf5,0x6f,0x75,
+0x70,0x49,0x75,0x22,0x00,0xe5,0x22,0xc3,0x95,0x21,0x50,0x26,0xad,0x6a,0xae,0x69,
+0xaf,0x68,0x12,0x01,0xe6,0xfc,0xe5,0x22,0x24,0xf8,0xf5,0x82,0xe4,0x34,0xfe,0xf5,
+0x83,0xec,0xf0,0x05,0x22,0x0d,0xed,0x70,0x01,0x0e,0x8d,0x6a,0x8e,0x69,0x8f,0x68,
+0x80,0xd3,0xe5,0x21,0x54,0x7f,0x90,0xff,0x81,0xf0,0x22,0x8c,0x48,0x7f,0x00,0xef,
+0x24,0xfd,0x40,0x19,0xe4,0xef,0x75,0xf0,0x07,0xa4,0x24,0x7f,0xf5,0x82,0xe4,0x34,
+0xf8,0xf5,0x83,0xe0,0x65,0x48,0x70,0x02,0xd3,0x22,0x0f,0x80,0xe2,0x8f,0x47,0xc3,
+0x22,0x85,0x72,0x70,0x85,0x71,0x6f,0x90,0xff,0x82,0xe0,0x54,0xf7,0xf0,0x90,0xff,
+0x83,0xe0,0x54,0x7f,0xf0,0x22,0xc0,0x00,0xc0,0x01,0xc0,0x02,0xc0,0x06,0xc0,0x07,
+0xe5,0x78,0x24,0x08,0xf8,0x86,0x06,0x53,0x06,0x7f,0x7c,0xff,0x12,0x31,0x5b,0x7c,
+0x00,0x7d,0x00,0xe5,0x7b,0x60,0x46,0xff,0x90,0xfd,0x95,0xe0,0x54,0x7f,0x6e,0x70,
+0x0f,0xc0,0x83,0xc0,0x82,0xa3,0xe0,0xfd,0xa3,0xe0,0xfc,0xa3,0x15,0x7b,0x80,0x07,
+0xa3,0xa3,0xa3,0xdf,0xe6,0x80,0x26,0xdf,0x06,0xd0,0x82,0xd0,0x83,0x80,0x1e,0xe0,
+0xf8,0xa3,0xe0,0xf9,0xa3,0xe0,0xfa,0xd0,0x82,0xd0,0x83,0xe8,0xf0,0xa3,0xe9,0xf0,
+0xa3,0xea,0xf0,0xa3,0xc0,0x83,0xc0,0x82,0xa3,0xa3,0xa3,0x80,0xda,0x12,0x31,0xf4,
+0xd0,0x07,0xd0,0x06,0xd0,0x02,0xd0,0x01,0xd0,0x00,0x22,0x85,0xa8,0x7a,0x75,0xa8,
+0x88,0xec,0x70,0x02,0x7c,0x3f,0x8c,0x79,0x22,0xe5,0x78,0x24,0x08,0xf8,0x76,0x00,
+0x12,0x32,0x48,0x80,0xfb,0xc0,0x00,0xc0,0x01,0xc0,0x02,0xc0,0x06,0xc0,0x07,0xae,
+0x04,0x7c,0xff,0x12,0x31,0x5b,0xe5,0x7b,0x60,0x42,0xff,0x90,0xfd,0x95,0xe0,0x54,
+0x7f,0x6e,0x70,0x0b,0xc0,0x83,0xc0,0x82,0xa3,0xa3,0xa3,0x15,0x7b,0x80,0x07,0xa3,
+0xa3,0xa3,0xdf,0xea,0x80,0x26,0xdf,0x06,0xd0,0x82,0xd0,0x83,0x80,0xd8,0xe0,0xf8,
+0xa3,0xe0,0xf9,0xa3,0xe0,0xfa,0xd0,0x82,0xd0,0x83,0xe8,0xf0,0xa3,0xe9,0xf0,0xa3,
+0xea,0xf0,0xa3,0xc0,0x83,0xc0,0x82,0xa3,0xa3,0xa3,0x80,0xda,0x78,0x08,0x08,0x79,
+0x18,0x09,0x7c,0x01,0xe6,0x54,0x7f,0x6e,0x70,0x06,0x76,0x00,0x77,0x00,0x80,0x06,
+0x08,0x09,0x0c,0xbc,0x08,0xee,0x12,0x31,0xf4,0xd0,0x07,0xd0,0x06,0xd0,0x02,0xd0,
+0x01,0xd0,0x00,0x22,0x75,0x79,0x00,0x85,0x7a,0xa8,0x22,0xc0,0xf0,0xc0,0x82,0xc0,
+0x83,0xc3,0xe5,0x7b,0x24,0xe8,0x50,0x05,0x12,0x32,0x48,0x80,0xf4,0xec,0x60,0x31,
+0x90,0x35,0x23,0xe4,0x93,0xc3,0x9c,0x40,0x28,0xc0,0x04,0x7c,0xff,0x12,0x31,0x5b,
+0xd0,0x04,0x43,0x04,0x80,0xe5,0x7b,0x75,0xf0,0x03,0xa4,0x24,0x95,0xf5,0x82,0xe4,
+0x34,0xfd,0xf5,0x83,0xec,0xf0,0xef,0xa3,0xf0,0xee,0xa3,0xf0,0x05,0x7b,0x12,0x31,
+0xf4,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0x22,0xc0,0x04,0x7c,0x20,0xd2,0x8c,0xd2,0x8d,
+0xd5,0x04,0xfd,0xd0,0x04,0x22,0x75,0xa8,0x00,0x75,0x88,0x00,0x75,0xb8,0x00,0x75,
+0xf0,0x00,0x75,0xd0,0x00,0xe4,0xf8,0x90,0x00,0x00,0xf6,0x08,0xb8,0x00,0xfb,0x02,
+0x00,0x00,0xc3,0xed,0x94,0x02,0x50,0x04,0x7d,0x03,0x7c,0xe8,0xec,0xf4,0xfc,0xed,
+0xf4,0xfd,0x0c,0xbc,0x00,0x01,0x0d,0x8c,0x7f,0x8d,0x7e,0x22,0xc3,0xec,0x94,0xbc,
+0xed,0x94,0x02,0x50,0x04,0x7d,0x07,0x7c,0xd0,0xec,0xf4,0xfc,0xed,0xf4,0xfd,0x0c,
+0xbc,0x00,0x01,0x0d,0x8c,0x7d,0x8d,0x7c,0x22,0xec,0x70,0x01,0x22,0xc0,0x00,0xe5,
+0x78,0x24,0x18,0xf8,0xa6,0x04,0xe5,0x78,0x24,0x08,0xf8,0xc6,0x54,0x7f,0xf6,0xe6,
+0x30,0xe7,0x03,0xd0,0x00,0x22,0x12,0x32,0x48,0x80,0xf4,0xc2,0x8c,0x85,0x7c,0x8c,
+0x85,0x7d,0x8a,0xd2,0x8c,0xc0,0xe0,0xc0,0xd0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,
+0x00,0xc0,0x01,0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0xc0,0x06,0xc0,0x07,0x12,
+0x1a,0xd1,0xe5,0x78,0x24,0x08,0xf8,0xe6,0x60,0x24,0xe5,0x78,0x24,0x10,0xf8,0xa6,
+0x81,0xe5,0x78,0x75,0xf0,0x21,0xa4,0x24,0x8d,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,
+0x78,0xae,0xe5,0x81,0x04,0xc3,0x98,0xf9,0xe6,0xf0,0x08,0xa3,0xd9,0xfa,0x74,0x08,
+0x25,0x78,0xf8,0x05,0x78,0x08,0xe6,0x54,0x80,0x70,0x0c,0xe5,0x78,0xb4,0x07,0xf3,
+0x78,0x08,0x75,0x78,0x00,0x80,0xef,0xe5,0x78,0x24,0x10,0xf8,0x86,0x81,0xe5,0x78,
+0x75,0xf0,0x21,0xa4,0x24,0x8d,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0x78,0xae,0xe5,
+0x81,0x04,0xc3,0x98,0xf9,0xe0,0xf6,0x08,0xa3,0xd9,0xfa,0xd0,0x07,0xd0,0x06,0xd0,
+0x05,0xd0,0x04,0xd0,0x03,0xd0,0x02,0xd0,0x01,0xd0,0x00,0xd0,0x83,0xd0,0x82,0xd0,
+0xf0,0xd0,0xd0,0xd0,0xe0,0x32,0xc0,0xe0,0xc0,0xd0,0xc0,0x00,0xc0,0x01,0xc0,0x02,
+0xc2,0x8e,0x85,0x7e,0x8d,0x85,0x7f,0x8b,0xd2,0x8e,0x78,0x19,0x79,0x09,0x7a,0x07,
+0xe7,0x70,0x04,0xa6,0x00,0x80,0x0b,0xe6,0x60,0x08,0x16,0xe6,0x70,0x04,0xe7,0x44,
+0x80,0xf7,0x08,0x09,0xda,0xea,0xe5,0x79,0x60,0x13,0x14,0xf5,0x79,0x70,0x0e,0xe5,
+0x78,0x24,0x08,0xf8,0x76,0x00,0x12,0x31,0xf4,0xd2,0x8c,0xd2,0x8d,0xd0,0x02,0xd0,
+0x01,0xd0,0x00,0xd0,0xd0,0xd0,0xe0,0x32,0x75,0x81,0xad,0x74,0x2a,0x90,0xff,0x93,
+0xf0,0x75,0x7f,0x30,0x75,0x7e,0xf8,0x75,0x7d,0x60,0x75,0x7c,0xf0,0x12,0x05,0x36,
+0x12,0x34,0x7c,0x12,0x17,0x34,0x90,0xff,0x93,0xe0,0x44,0x01,0xf0,0xb2,0xb3,0x12,
+0x34,0xa6,0x12,0x32,0x56,0x80,0xda,0x22,0xc0,0x00,0x7c,0x01,0xec,0x24,0x08,0xf8,
+0xe6,0x60,0x09,0x0c,0xbc,0x08,0xf5,0x12,0x32,0x48,0x80,0xee,0xd0,0x00,0x22,0xc0,
+0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x00,0xc0,0x06,0xc0,0x07,0xed,0x24,0x10,0xf8,0x76,
+0xbc,0xed,0x75,0xf0,0x21,0xa4,0x24,0x8d,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xc0,
+0x82,0xc0,0x83,0xa3,0xa3,0xe4,0x78,0x0d,0xf0,0xa3,0xd8,0xfc,0xec,0x54,0x7f,0x75,
+0xf0,0x02,0xa4,0x24,0xef,0xf5,0x82,0xe5,0xf0,0x34,0x34,0xf5,0x83,0xe4,0x93,0xfe,
+0x74,0x01,0x93,0xf5,0x82,0x8e,0x83,0xe4,0x93,0xfe,0x74,0x01,0x93,0xff,0xd0,0x83,
+0xd0,0x82,0xef,0xf0,0xa3,0xee,0xf0,0xed,0x24,0x08,0xf8,0xec,0x44,0x80,0xf6,0xd0,
+0x07,0xd0,0x06,0xd0,0x00,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0x22,0x75,0x78,0x00,0x75,
+0x7b,0x00,0x7a,0x08,0x79,0x18,0x78,0x08,0x76,0x00,0x77,0x00,0x08,0x09,0xda,0xf8,
+0xe4,0x78,0x08,0x74,0x80,0x44,0x7f,0xf6,0x74,0x01,0x44,0x10,0xf5,0x89,0x75,0xb8,
+0x08,0xd2,0xab,0xd2,0xa9,0x22,0x75,0x81,0xad,0xd2,0x8e,0xd2,0x8c,0xd2,0xaf,0xe5,
+0x7b,0x60,0x32,0xff,0x90,0xfd,0x95,0xe0,0x54,0x80,0x60,0x24,0x78,0x08,0x79,0x08,
+0xe0,0x54,0x7f,0xfa,0x7b,0x00,0xe6,0x54,0x7f,0xb5,0x02,0x02,0x7b,0xff,0x08,0xd9,
+0xf5,0xeb,0x70,0x0c,0xea,0xf0,0x12,0x33,0xf8,0xad,0x04,0xac,0x02,0x12,0x34,0x0f,
+0xa3,0xa3,0xa3,0xdf,0xd2,0x12,0x32,0x48,0x80,0xc5,0x7c,0x01,0x7d,0x00,0x22,0x04,
+0xf5,0x04,0xe9,0x04,0xed,0x04,0xe1,0x04,0xdd,0x04,0xd9,0x04,0xe5,0x04,0xf1,0x04,
+0x9d,0x04,0xa1,0x04,0xcd,0x04,0xd1,0x04,0x99,0x04,0x99,0x04,0x99,0x04,0xd5,0x04,
+0xb5,0x04,0xad,0x04,0xb1,0x04,0xa9,0x04,0xc1,0x04,0xbd,0x04,0xb9,0x04,0xc5,0x04,
+0xc9,0x04,0xa5,0x19,0x01,0x03,0x00,0x22,0x00,0x48,0x02,0x00,0x48,0x0e,0x30,0x14,
+0x20,0xc8,0x1a,0xd0,0x18,0x0a,0x0c,0x05,0x06,0x02,0x03,0x01,0x02,0x00,0x01,0xce,
+0x01,0x81,0x01,0x00,0x00,0xc0,0x00,0x80,0x00,0x60,0x00,0x30,0x00,0x18,0x00,0x10,
+0x00,0x08,0x00,0x04,0x00,0x02,0x00,0x01,0x00,0x08,0x18,0x38,0x28,0x0c,0x05,0x10,
+0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x03,0x01,0x10,0x0a,0x02,0x00,0x00,0x00,0x00,
+0x00,0xfb,0xe0,0xfb,0xf2,0x09,0x02,0x27,0x00,0x01,0x02,0x00,0xa0,0x32,0x09,0x04,
+0x00,0x00,0x03,0xff,0x00,0x00,0x00,0x07,0x05,0x81,0x02,0x40,0x00,0x00,0x07,0x05,
+0x01,0x02,0x40,0x00,0x00,0x07,0x05,0x83,0x03,0x02,0x00,0x01,0x22,0x03,0x54,0x00,
+0x55,0x00,0x53,0x00,0x42,0x00,0x33,0x00,0x34,0x00,0x31,0x00,0x30,0x00,0x20,0x00,
+0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x00,0x00,
+0x00,0x00,
+};
+
+#endif /* ifndef _TI_FW_3410_H_ */
diff --git a/drivers/usb/serial/ti_fw_5052.h b/drivers/usb/serial/ti_fw_5052.h
new file mode 100644 (file)
index 0000000..6ccf40a
--- /dev/null
@@ -0,0 +1,885 @@
+/* vi: ts=8 sw=8
+ *
+ * TI 5052 USB Serial Driver Firmware Header
+ *
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * 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 _TI_FW_5052_H_
+#define _TI_FW_5052_H_
+
+/* firmware 9/18/04 */
+
+static unsigned char ti_fw_5052[] = {
+0xC1, 0x35,    /* firmware image length excluding header, little endian */
+0x00,          /* placeholder for checksum */
+
+0x02,0x00,0x1e,0x02,0x1b,0x32,0xff,0xff,0xff,0xff,0xff,0x02,0x32,0x6a,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x02,0x33,0x15,0x75,0x81,
+0xc8,0x90,0xfe,0xf0,0x85,0x83,0xa0,0x12,0x34,0x7d,0xec,0x4d,0x60,0x6a,0x78,0xa5,
+0x80,0x03,0x76,0x00,0x18,0xb8,0x96,0xfa,0x78,0x79,0x80,0x03,0x76,0x00,0x18,0xb8,
+0x5f,0xfa,0x78,0x20,0x80,0x03,0x76,0x00,0x18,0xb8,0x20,0xfa,0x90,0xfe,0xe5,0xae,
+0x83,0xaf,0x82,0x90,0xfd,0x00,0x12,0x00,0xa1,0x60,0x05,0xe4,0xf0,0xa3,0x80,0xf6,
+0x90,0xfe,0xf0,0xa8,0x82,0x90,0xfe,0xf0,0xa9,0x82,0xe8,0xc3,0x99,0x50,0x05,0x76,
+0x00,0x08,0x80,0xf6,0x90,0x00,0xff,0x12,0x00,0xaa,0x90,0x01,0x03,0x12,0x00,0xaa,
+0x90,0x01,0x07,0x12,0x00,0xaa,0x90,0x01,0x0b,0x12,0x00,0xc8,0x90,0x01,0x11,0x12,
+0x00,0xc8,0x90,0x01,0x17,0x12,0x00,0xc8,0x75,0xd0,0x00,0x12,0x33,0x67,0x02,0x01,
+0x1d,0xef,0x65,0x82,0x70,0x03,0xee,0x65,0x83,0x22,0xe4,0x93,0xf8,0x74,0x01,0x93,
+0xf9,0x74,0x02,0x93,0xfe,0x74,0x03,0x93,0xf5,0x82,0x8e,0x83,0xe8,0x69,0x70,0x01,
+0x22,0xe4,0x93,0xf6,0xa3,0x08,0x80,0xf4,0xe4,0x93,0xfc,0x74,0x01,0x93,0xfd,0x74,
+0x02,0x93,0xfe,0x74,0x03,0x93,0xff,0x74,0x04,0x93,0xf8,0x74,0x05,0x93,0xf5,0x82,
+0x88,0x83,0x12,0x00,0xa1,0x70,0x01,0x22,0xe4,0x93,0xa3,0xa8,0x83,0xa9,0x82,0x8c,
+0x83,0x8d,0x82,0xf0,0xa3,0xac,0x83,0xad,0x82,0x88,0x83,0x89,0x82,0x80,0xe3,0x21,
+0x21,0x04,0x92,0x7a,0x7a,0x04,0x92,0xa6,0xa8,0x04,0x92,0xfe,0xf0,0x04,0x94,0x04,
+0x94,0xfb,0xfb,0x04,0x99,0x04,0x94,0xfb,0xfb,0x04,0xf9,0x04,0xf9,0x80,0xfe,0xd0,
+0xf0,0x30,0xf0,0x09,0x20,0xf3,0x03,0xf6,0x80,0x10,0xf7,0x80,0x0d,0x30,0xf1,0x09,
+0x20,0xf3,0x03,0xf2,0x80,0x04,0xf3,0x80,0x01,0xf0,0x20,0xf4,0x04,0xfc,0xd0,0xe0,
+0xcc,0x22,0xcc,0xc0,0xe0,0x12,0x01,0x5a,0x02,0x01,0x4b,0xbc,0x00,0x05,0xd0,0xf0,
+0xac,0xf0,0x22,0xc3,0x13,0xdc,0xfc,0x02,0x01,0x21,0xbf,0x00,0x09,0xed,0x25,0x82,
+0x75,0xf0,0x01,0xf8,0xe6,0x22,0xbf,0x01,0x0f,0xed,0x25,0x82,0xf5,0x82,0xee,0x35,
+0x83,0xf5,0x83,0x75,0xf0,0x04,0xe0,0x22,0xed,0x25,0x82,0x75,0xf0,0x02,0xf8,0xe2,
+0x22,0xd0,0x83,0xd0,0x82,0xf5,0xf0,0xc3,0xe4,0x93,0xa3,0xc5,0xf0,0x95,0xf0,0xc0,
+0xe0,0xc3,0xd0,0xf0,0xe4,0x93,0xa3,0x95,0xf0,0x40,0x12,0xa3,0xa3,0xc3,0xe5,0xf0,
+0x33,0x50,0x02,0x05,0x83,0x25,0x82,0xf5,0x82,0x50,0x02,0x05,0x83,0x74,0x01,0x93,
+0xc0,0xe0,0xe4,0x93,0xc0,0xe0,0x22,0xd0,0x83,0xd0,0x82,0xf5,0xf0,0xe4,0x93,0x70,
+0x09,0x74,0x01,0x93,0x70,0x04,0xa3,0xa3,0x80,0x0c,0x74,0x02,0x93,0x65,0xf0,0x60,
+0x05,0xa3,0xa3,0xa3,0x80,0xe7,0x74,0x01,0x93,0xc0,0xe0,0xe4,0x93,0xc0,0xe0,0x22,
+0x12,0x02,0x5b,0x02,0x01,0xf2,0x12,0x02,0xaf,0x02,0x01,0xf2,0x12,0x02,0xd3,0x02,
+0x01,0xf2,0x30,0xe0,0x07,0x20,0xe3,0x02,0xe6,0x22,0xe7,0x22,0x30,0xe1,0x07,0x20,
+0xe3,0x02,0xe2,0x22,0xe3,0x22,0x30,0xe2,0x02,0xe0,0x22,0xe4,0x93,0x22,0x12,0x02,
+0xd3,0x02,0x02,0x1a,0x12,0x02,0xaf,0x02,0x02,0x1a,0xab,0xf0,0x12,0x02,0x24,0xcb,
+0xc5,0xf0,0xcb,0x22,0x30,0xe0,0x10,0x20,0xe3,0x06,0xe6,0xf5,0xf0,0x08,0xe6,0x22,
+0xe7,0xf5,0xf0,0x09,0xe7,0x19,0x22,0x30,0xe1,0x10,0x20,0xe3,0x06,0xe2,0xf5,0xf0,
+0x08,0xe2,0x22,0xe3,0xf5,0xf0,0x09,0xe3,0x19,0x22,0x30,0xe2,0x06,0xe0,0xf5,0xf0,
+0xa3,0xe0,0x22,0xe4,0x93,0xf5,0xf0,0x74,0x01,0x93,0x22,0xbb,0x00,0x03,0x74,0x09,
+0x22,0xbb,0x01,0x07,0x89,0x82,0x8a,0x83,0x74,0x04,0x22,0xbb,0x02,0x07,0x89,0x82,
+0x8a,0x83,0x74,0x10,0x22,0x74,0x0a,0x22,0x02,0x02,0x7b,0xbb,0x00,0x07,0xe9,0x25,
+0x82,0xf8,0x74,0x01,0x22,0xbb,0x01,0x0d,0xe9,0x25,0x82,0xf5,0x82,0xea,0x35,0x83,
+0xf5,0x83,0x74,0x04,0x22,0xbb,0x02,0x0d,0xe9,0x25,0x82,0xf5,0x82,0xea,0x35,0x83,
+0xf5,0x83,0x74,0x10,0x22,0xe9,0x25,0x82,0xf8,0x74,0x02,0x22,0x02,0x02,0xaf,0xbf,
+0x00,0x05,0xed,0xf8,0x74,0x01,0x22,0xbf,0x01,0x07,0x8d,0x82,0x8e,0x83,0x74,0x04,
+0x22,0xbf,0x02,0x07,0x8d,0x82,0x8e,0x83,0x74,0x10,0x22,0xed,0xf8,0x74,0x02,0x22,
+0x02,0x02,0xd3,0xbf,0x00,0x07,0xed,0x25,0x82,0xf8,0x74,0x01,0x22,0xbf,0x01,0x0d,
+0xed,0x25,0x82,0xf5,0x82,0xee,0x35,0x83,0xf5,0x83,0x74,0x04,0x22,0xbf,0x02,0x0d,
+0xed,0x25,0x82,0xf5,0x82,0xee,0x35,0x83,0xf5,0x83,0x74,0x10,0x22,0xed,0x25,0x82,
+0xf8,0x74,0x02,0x22,0x02,0x03,0x07,0xc0,0xe0,0x12,0x02,0x5b,0x02,0x03,0x1f,0xc0,
+0xe0,0x12,0x02,0xaf,0x02,0x03,0x1f,0xc0,0xe0,0x12,0x02,0xd3,0x02,0x03,0x1f,0x30,
+0xe0,0x0b,0x20,0xe3,0x04,0xd0,0xe0,0xf6,0x22,0xd0,0xe0,0xf7,0x22,0x30,0xe1,0x0b,
+0x20,0xe3,0x04,0xd0,0xe0,0xf2,0x22,0xd0,0xe0,0xf3,0x22,0xd0,0xe0,0xf0,0x22,0xc9,
+0xcd,0xc9,0xca,0xce,0xca,0xcb,0xcf,0xcb,0x12,0x03,0x52,0xed,0xf9,0xee,0xfa,0xef,
+0xfb,0x22,0xbb,0x00,0x2f,0xbf,0x00,0x0a,0xfa,0xed,0xf8,0xe7,0xf6,0x08,0x09,0xda,
+0xfa,0x22,0xbf,0x01,0x12,0x8d,0x82,0x8e,0x83,0xf8,0x02,0x03,0x6f,0x09,0xa3,0xe7,
+0xf0,0xd8,0xfa,0x22,0x02,0x03,0x7a,0xfa,0xed,0xf8,0xe7,0xf2,0x08,0x09,0xda,0xfa,
+0x22,0x02,0x03,0x84,0xbb,0x01,0x4d,0xbf,0x00,0x14,0x89,0x82,0x8a,0x83,0xf9,0xed,
+0xf8,0x02,0x03,0x96,0x08,0xa3,0xe0,0xf6,0xd9,0xfa,0x22,0x02,0x03,0xa7,0xbf,0x01,
+0x22,0x8d,0x82,0x8e,0x83,0xfb,0x08,0xc9,0xc5,0x82,0xc9,0xca,0xc5,0x83,0xca,0xe0,
+0xa3,0xc9,0xc5,0x82,0xc9,0xca,0xc5,0x83,0xca,0xf0,0xa3,0xdb,0xea,0xd8,0xe8,0x22,
+0x02,0x03,0xca,0x8d,0x82,0x8e,0x83,0xf9,0xed,0xf8,0xe0,0xf2,0x08,0xa3,0xd9,0xfa,
+0x22,0x02,0x03,0xd4,0xbb,0x02,0x4d,0xbf,0x00,0x12,0x89,0x82,0x8a,0x83,0xf9,0xed,
+0xf8,0x02,0x03,0xe6,0x08,0xa3,0xe4,0x93,0xf6,0xd9,0xf9,0x22,0xbf,0x01,0x23,0x8d,
+0x82,0x8e,0x83,0xfb,0x08,0xc9,0xc5,0x82,0xc9,0xca,0xc5,0x83,0xca,0xe4,0x93,0xa3,
+0xc9,0xc5,0x82,0xc9,0xca,0xc5,0x83,0xca,0xf0,0xa3,0xdb,0xe9,0xd8,0xe7,0x22,0x02,
+0x04,0x19,0x89,0x82,0x8a,0x83,0xf9,0xed,0xf8,0xe4,0x93,0xf2,0x08,0xa3,0xd9,0xf9,
+0x22,0x02,0x04,0x2a,0xbf,0x00,0x0d,0xfa,0xed,0xf8,0xe3,0xf6,0x08,0x09,0xda,0xfa,
+0x22,0x02,0x04,0x34,0xbf,0x01,0x12,0x8d,0x82,0x8e,0x83,0xf8,0x02,0x04,0x41,0x09,
+0xa3,0xe3,0xf0,0xd8,0xfa,0x22,0x02,0x04,0x4c,0xfa,0xed,0xf8,0xe3,0xf2,0x08,0x09,
+0xda,0xfa,0x22,0x02,0x04,0x56,0xe6,0xfb,0x08,0xe6,0xfa,0x08,0xe6,0xf9,0x04,0xf6,
+0x18,0x70,0x01,0x06,0x22,0xe6,0xff,0x08,0xe6,0xfe,0x08,0xe6,0xfd,0x22,0xef,0xf0,
+0xa3,0xee,0xf0,0xa3,0xed,0xf0,0x22,0xeb,0xf0,0xa3,0xea,0xf0,0xa3,0xe9,0xf0,0x22,
+0xe0,0xff,0xa3,0xe0,0xfe,0xa3,0xe0,0xfd,0x22,0xe0,0xfb,0xa3,0xe0,0xfa,0xa3,0xe0,
+0xf9,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0xf9,0x00,0x5b,0x05,0x73,0x00,
+0x26,0x05,0x9a,0x00,0x33,0x0a,0x0b,0x00,0x5b,0x0a,0x77,0x00,0x60,0x15,0x52,0x00,
+0x5b,0x0c,0xfb,0x00,0x5b,0x09,0xab,0x00,0x5b,0x09,0xe2,0x00,0x5b,0x0d,0xc2,0x00,
+0x5b,0x0b,0xf3,0x00,0x5b,0x0a,0x1e,0x00,0x5b,0x0a,0x53,0x00,0x5b,0x17,0x4a,0x00,
+0x33,0x17,0x60,0x00,0x34,0x1e,0x4d,0x00,0x43,0x1e,0xf0,0x00,0x44,0x20,0x5d,0x00,
+0x44,0x20,0x4b,0x00,0x47,0x1f,0x17,0x00,0x47,0x1f,0xbc,0x00,0x4d,0x20,0x0d,0x00,
+0x4f,0x1f,0x39,0x00,0x58,0x31,0xf5,0x00,0x5b,0x7c,0xcc,0x7d,0xff,0x12,0x1c,0xfe,
+0x22,0x74,0x90,0x90,0xff,0x91,0xf0,0x90,0xff,0xfc,0xe0,0x20,0xe7,0x2d,0xc2,0xaf,
+0xae,0x59,0xaf,0x58,0x75,0x5a,0x20,0xe5,0x5a,0x14,0xc5,0x5a,0x60,0x19,0xe4,0xfe,
+0x7f,0x05,0xee,0x4f,0xce,0x24,0xff,0xce,0xcf,0x34,0xff,0xcf,0x60,0x07,0xe4,0x90,
+0xff,0x92,0xf0,0x80,0xed,0x80,0xe0,0x8e,0x59,0x8f,0x58,0x22,0x12,0x05,0x01,0x7d,
+0x07,0x7c,0xb7,0x12,0x32,0x11,0x7d,0x0f,0x7c,0x6e,0x12,0x32,0x2b,0x78,0x97,0x7a,
+0x06,0xe4,0xf6,0x08,0xda,0xfc,0x7a,0x06,0x12,0x05,0xcf,0x7c,0x03,0x12,0x0e,0x57,
+0x7c,0x04,0x12,0x0e,0x57,0x12,0x21,0x8b,0xe4,0xfe,0xff,0x7c,0x0f,0x12,0x31,0x9a,
+0xd2,0xa8,0x22,0x12,0x30,0x85,0xe4,0x90,0xfd,0x40,0xf0,0x90,0xff,0xf0,0xe0,0x30,
+0xe4,0x08,0x74,0x01,0x90,0xfd,0x41,0xf0,0x80,0x05,0xe4,0x90,0xfd,0x41,0xf0,0x7d,
+0x0a,0x7c,0x00,0x12,0x24,0xb1,0x12,0x31,0x08,0x22,0x12,0x30,0x85,0x90,0xfd,0x41,
+0xe0,0x14,0x70,0x0e,0x90,0xff,0xf0,0xe0,0x44,0x10,0xf0,0x7c,0x00,0x12,0x25,0x4a,
+0x80,0x19,0x90,0xfd,0x41,0xe0,0x70,0x0e,0x90,0xff,0xf0,0xe0,0x54,0xef,0xf0,0x7c,
+0x00,0x12,0x25,0x4a,0x80,0x05,0x7c,0x17,0x12,0x25,0x4a,0x12,0x31,0x08,0x22,0x90,
+0xff,0xf0,0xe0,0x54,0xab,0xf0,0x90,0xff,0xf0,0xe0,0x44,0x20,0xf0,0x22,0x8c,0x37,
+0x8d,0x36,0x78,0x7c,0xed,0xf6,0x08,0xec,0xf6,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,
+0x00,0x05,0x12,0x01,0xec,0x78,0x7a,0xf6,0x78,0x7c,0xe6,0xfd,0x08,0xe6,0xfc,0xed,
+0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x04,0x12,0x01,0xec,0x54,0x0f,0xfc,0x7d,0x7a,
+0x12,0x17,0x9d,0x78,0x7a,0xe6,0x70,0x0d,0xad,0x3a,0xae,0x39,0xaf,0x38,0xe4,0x12,
+0x03,0x0f,0x7c,0x08,0x22,0x90,0xff,0xf0,0xe0,0x54,0xfe,0xf0,0x90,0xff,0xf0,0xe0,
+0x54,0xfd,0xf0,0x80,0x1e,0x78,0x7c,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,
+0x7f,0x01,0x90,0x00,0x08,0x12,0x02,0x0e,0x25,0xe0,0x44,0x01,0x90,0xff,0xf3,0xf0,
+0x02,0x06,0xdb,0x78,0x7c,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,
+0x90,0x00,0x06,0x12,0x02,0x0e,0x54,0xfe,0x90,0xff,0xf3,0xf0,0x80,0x2b,0x78,0x7c,
+0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x08,0x12,0x02,
+0x0e,0xfa,0xeb,0x90,0xff,0xf1,0xf0,0x12,0x08,0xca,0x40,0x0d,0xad,0x3a,0xae,0x39,
+0xaf,0x38,0xe4,0x12,0x03,0x0f,0x7c,0x18,0x22,0x78,0x7c,0xe6,0xfd,0x08,0xe6,0xfc,
+0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x08,0x12,0x02,0x0e,0x90,0xff,0xf1,0xf0,
+0x12,0x08,0xca,0x40,0x0d,0xad,0x3a,0xae,0x39,0xaf,0x38,0xe4,0x12,0x03,0x0f,0x7c,
+0x18,0x22,0x78,0x7c,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,
+0x00,0x06,0x12,0x02,0x0e,0x44,0x01,0x90,0xff,0xf3,0xf0,0x78,0x7d,0xe6,0x24,0x03,
+0xf6,0x18,0xe6,0x34,0x00,0xf6,0x78,0x7a,0xe6,0x24,0xfe,0x50,0x09,0x90,0xff,0xf0,
+0xe0,0x54,0xfd,0xf0,0x80,0x07,0x90,0xff,0xf0,0xe0,0x44,0x02,0xf0,0xe4,0x90,0xff,
+0xf1,0xf0,0x78,0x7b,0x76,0x00,0x78,0x7a,0xe6,0x24,0xff,0xfc,0xe4,0x34,0xff,0xfd,
+0x78,0x7b,0xe6,0x7f,0x00,0xfe,0xec,0xd3,0x9e,0xef,0x64,0x80,0xcd,0x64,0x80,0x9d,
+0x40,0x2f,0x12,0x08,0xaf,0x40,0x0f,0x78,0x7b,0xe6,0xad,0x3a,0xae,0x39,0xaf,0x38,
+0x12,0x03,0x0f,0x7c,0x18,0x22,0x90,0xff,0xf2,0xe0,0xfc,0x78,0x7c,0x86,0x83,0x08,
+0x86,0x82,0xec,0xf0,0x78,0x7b,0x06,0xa3,0x78,0x7c,0xa6,0x83,0x08,0xa6,0x82,0x80,
+0xb5,0x12,0x08,0xaf,0x40,0x0f,0x78,0x7b,0xe6,0xad,0x3a,0xae,0x39,0xaf,0x38,0x12,
+0x03,0x0f,0x7c,0x18,0x22,0x90,0xff,0xf2,0xe0,0xfc,0x78,0x7c,0x86,0x83,0x08,0x86,
+0x82,0xec,0xf0,0x78,0x7a,0xe6,0xad,0x3a,0xae,0x39,0xaf,0x38,0x12,0x03,0x0f,0x7c,
+0x00,0x22,0x8c,0x37,0x8d,0x36,0x78,0x7c,0xed,0xf6,0x08,0xec,0xf6,0xed,0xfe,0xec,
+0xfd,0x7f,0x01,0x90,0x00,0x05,0x12,0x01,0xec,0x78,0x7b,0xf6,0x78,0x7c,0xe6,0xfd,
+0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x04,0x12,0x01,0xec,0x54,
+0x0f,0xfc,0x7d,0x7b,0x12,0x17,0x9d,0x78,0x7b,0xe6,0x70,0x03,0x7c,0x08,0x22,0x90,
+0xff,0xf0,0xe0,0x54,0xfe,0xf0,0x90,0xff,0xf0,0xe0,0x54,0xfd,0xf0,0x80,0x1b,0x78,
+0x7c,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x08,0x12,
+0x02,0x0e,0x25,0xe0,0x90,0xff,0xf3,0xf0,0x80,0x5b,0x78,0x7c,0xe6,0xfd,0x08,0xe6,
+0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x90,0x00,0x06,0x12,0x02,0x0e,0x54,0xfe,0x90,
+0xff,0xf3,0xf0,0x80,0x21,0x78,0x7c,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,0xfd,
+0x7f,0x01,0x90,0x00,0x08,0x12,0x02,0x0e,0xfa,0xeb,0x90,0xff,0xf1,0xf0,0x12,0x08,
+0xca,0x40,0x03,0x7c,0x18,0x22,0x78,0x7c,0xe6,0xfd,0x08,0xe6,0xfc,0xed,0xfe,0xec,
+0xfd,0x7f,0x01,0x90,0x00,0x08,0x12,0x02,0x0e,0x90,0xff,0xf1,0xf0,0x12,0x08,0xca,
+0x40,0x03,0x7c,0x18,0x22,0x78,0x7d,0xe6,0x24,0x0a,0xf6,0x18,0xe6,0x34,0x00,0xf6,
+0x78,0x7a,0x76,0x00,0x78,0x7b,0xe6,0x24,0xff,0xfc,0xe4,0x34,0xff,0xfd,0x78,0x7a,
+0xe6,0x7f,0x00,0xfe,0xec,0xd3,0x9e,0xef,0x64,0x80,0xcd,0x64,0x80,0x9d,0x40,0x21,
+0x78,0x7c,0x86,0x83,0x08,0x86,0x82,0xe0,0x90,0xff,0xf1,0xf0,0x12,0x08,0xca,0x40,
+0x03,0x7c,0x18,0x22,0x78,0x7a,0x06,0x78,0x7d,0x06,0xe6,0x18,0x70,0x01,0x06,0x80,
+0xc3,0x90,0xff,0xf0,0xe0,0x44,0x01,0xf0,0x78,0x7c,0x86,0x83,0x08,0x86,0x82,0xe0,
+0x90,0xff,0xf1,0xf0,0x12,0x08,0xca,0x40,0x03,0x7c,0x18,0x22,0x7c,0x00,0x22,0x90,
+0xff,0xf0,0xe0,0x20,0xe7,0x12,0x90,0xff,0xf0,0xe0,0x30,0xe5,0x09,0x90,0xff,0xf0,
+0xe0,0x44,0x20,0xf0,0xc3,0x22,0x80,0xe7,0xd3,0x22,0x90,0xff,0xf0,0xe0,0x20,0xe3,
+0x12,0x90,0xff,0xf0,0xe0,0x30,0xe5,0x09,0x90,0xff,0xf0,0xe0,0x44,0x20,0xf0,0xc3,
+0x22,0x80,0xe7,0xd3,0x22,0x8c,0x42,0x8d,0x41,0x7c,0x00,0xed,0x54,0xf0,0xfd,0xec,
+0x70,0x03,0xed,0x64,0x30,0x70,0x05,0x75,0x3e,0x03,0x80,0x03,0x75,0x3e,0x04,0xac,
+0x3e,0x12,0x0f,0x7c,0x75,0x83,0x00,0x85,0x83,0x40,0xe5,0x41,0x54,0x0f,0xf5,0x3f,
+0xe5,0x40,0x70,0x04,0xe5,0x3f,0x64,0x03,0x70,0x35,0xe5,0x3e,0x24,0xfd,0x75,0xf0,
+0x0a,0xa4,0x24,0x0a,0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,0xe0,0x30,0xe6,0x05,0x12,
+0x10,0x67,0x80,0x19,0xe5,0x3e,0x24,0x97,0xf8,0xc6,0x54,0xfb,0xf6,0x78,0xa3,0xe6,
+0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0x74,0x0f,0xf0,0x80,0x59,0xe5,
+0x40,0x70,0x04,0xe5,0x3f,0x64,0x04,0x70,0x48,0xe5,0x3e,0x24,0xfd,0x75,0xf0,0x0a,
+0xa4,0x24,0x0a,0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,0xe0,0x30,0xe5,0x07,0xac,0x42,
+0xad,0x41,0x12,0x1c,0x93,0xe5,0x42,0x30,0xe2,0x15,0x78,0xa7,0xe6,0x30,0xe0,0x0f,
+0x78,0xa7,0xe6,0x30,0xe1,0x09,0xe4,0xff,0x04,0xfe,0x7c,0x04,0x12,0x31,0x9a,0x78,
+0xa3,0xe6,0x24,0x06,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0x74,0x0f,0xf0,0x80,
+0x07,0xe4,0xfc,0x7d,0xee,0x12,0x1c,0x93,0xc2,0x03,0x22,0x12,0x30,0x85,0x12,0x0f,
+0x7c,0x78,0xa3,0xe6,0x24,0x06,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x90,
+0xfd,0x40,0xf0,0x78,0xa3,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,
+0xe0,0x90,0xfd,0x41,0xf0,0xc2,0x03,0x7d,0x02,0x7c,0x00,0x12,0x24,0xb1,0x12,0x31,
+0x08,0x22,0x12,0x30,0x85,0x78,0x8f,0xec,0xf6,0xec,0x24,0x97,0xf8,0xe6,0x30,0xe1,
+0x07,0x7c,0x13,0x12,0x25,0x4a,0x80,0x0f,0x90,0xfd,0x41,0xe0,0xfd,0x78,0x8f,0xe6,
+0xfc,0x12,0x13,0xfd,0x12,0x25,0x4a,0x12,0x31,0x08,0x22,0x12,0x30,0x85,0x78,0x8f,
+0xec,0xf6,0x7d,0x00,0x12,0x0f,0x0b,0x12,0x25,0x4a,0x12,0x31,0x08,0x22,0x12,0x30,
+0x85,0x78,0x8f,0xec,0xf6,0xec,0x24,0x97,0xf8,0xe6,0x30,0xe2,0x07,0x7c,0x13,0x12,
+0x25,0x4a,0x80,0x1b,0x78,0x8f,0xe6,0x24,0x97,0xf8,0xe6,0x20,0xe1,0x07,0x7c,0x12,
+0x12,0x25,0x4a,0x80,0x0a,0x78,0x8f,0xe6,0xfc,0x12,0x14,0x21,0x12,0x25,0x4a,0x12,
+0x31,0x08,0x22,0x12,0x30,0x85,0x78,0x8f,0xec,0xf6,0xec,0x24,0x97,0xf8,0xe6,0x20,
+0xe2,0x07,0x7c,0x11,0x12,0x25,0x4a,0x80,0x0a,0x78,0x8f,0xe6,0xfc,0x12,0x15,0x22,
+0x12,0x25,0x4a,0x12,0x31,0x08,0x22,0x12,0x30,0x85,0x78,0x8f,0xec,0xf6,0x12,0x0f,
+0x7c,0x78,0xa3,0xe6,0x24,0x09,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x90,
+0xfd,0x47,0xf0,0x78,0xa3,0xe6,0x24,0x0a,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,
+0xe0,0x90,0xfd,0x48,0xf0,0x78,0xa3,0xe6,0x24,0x03,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0xfc,0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xe0,0xf5,0x5c,0x78,0xa3,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xe0,0xf5,0x5d,0x8c,0x5b,0xe4,0xec,0x33,0x33,0x54,0x01,0x78,0x8f,0xf6,0x60,
+0x08,0xe5,0x5c,0x30,0xe1,0x03,0x78,0x8f,0x06,0x78,0x8f,0xe6,0x90,0xfd,0x49,0xf0,
+0x78,0xa1,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xfd,0xa3,
+0xe0,0x54,0x0c,0xfc,0xed,0x54,0xe6,0x8c,0x5f,0xf5,0x5e,0xe5,0x5b,0x30,0xe5,0x03,
+0x43,0x5f,0x01,0xe5,0x5c,0x20,0xe5,0x0e,0xe5,0x5b,0x54,0x7f,0x70,0x08,0xe5,0x5b,
+0x20,0xe7,0x03,0x43,0x5f,0x02,0xe5,0x5b,0x30,0xe3,0x03,0x43,0x5f,0x10,0xe5,0x5b,
+0x30,0xe2,0x03,0x43,0x5f,0x20,0xe5,0x5b,0x54,0x03,0x60,0x03,0x43,0x5f,0x40,0xe5,
+0x5b,0x30,0xe1,0x03,0x43,0x5f,0x80,0xe5,0x5b,0x30,0xe4,0x03,0x43,0x5e,0x01,0xe5,
+0x5b,0x30,0xe6,0x03,0x43,0x5e,0x08,0xe5,0x5c,0x20,0xe4,0x0e,0xe5,0x5b,0x54,0x7f,
+0x70,0x08,0xe5,0x5b,0x20,0xe7,0x03,0x43,0x5e,0x10,0x53,0x5f,0xfb,0x53,0x5e,0xf9,
+0xad,0x5e,0xe5,0x5f,0x90,0xfd,0x42,0xcd,0xf0,0xa3,0xcd,0xf0,0xe5,0x5d,0x30,0xe3,
+0x0d,0xe5,0x5d,0x54,0x30,0xc4,0x54,0x0f,0x90,0xfd,0x45,0xf0,0x80,0x05,0xe4,0x90,
+0xfd,0x45,0xf0,0xe5,0x5d,0x54,0x03,0x90,0xfd,0x44,0xf0,0xe5,0x5d,0x54,0x04,0xc3,
+0x13,0x90,0xfd,0x46,0xf0,0x90,0xfd,0x44,0xe0,0x70,0x0e,0x7d,0x3d,0x7e,0xfd,0x7f,
+0x01,0x74,0x01,0x90,0x00,0x09,0x12,0x01,0x42,0x78,0xa3,0xe6,0x24,0x08,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x7c,0x00,0xfd,0x78,0xa3,0xe6,0x24,0x07,0xf5,
+0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x7f,0x00,0x4c,0xfe,0xef,0x4d,0x90,0xfd,
+0x40,0xf0,0xa3,0xce,0xf0,0xce,0xc2,0x03,0x7d,0x0a,0x7c,0x00,0x12,0x24,0xb1,0x12,
+0x31,0x08,0x22,0x12,0x30,0x85,0x78,0x8f,0xec,0xf6,0x78,0x94,0x76,0x01,0x08,0x76,
+0xfd,0x08,0x76,0x40,0x78,0x91,0x76,0x0c,0x78,0x94,0x12,0x04,0x65,0x12,0x02,0x14,
+0x78,0x92,0xcb,0xf6,0xcb,0x08,0xf6,0x7f,0x00,0xef,0x24,0xeb,0x40,0x1f,0xe4,0xef,
+0x25,0xe0,0x90,0x34,0xbf,0xfd,0x93,0xcd,0x04,0x93,0x78,0x93,0x66,0x70,0x03,0xed,
+0x18,0x66,0x70,0x06,0x78,0x91,0x76,0x00,0x80,0x03,0x0f,0x80,0xdc,0x78,0x90,0xef,
+0xf6,0x78,0x94,0x12,0x04,0x65,0x90,0x00,0x02,0x12,0x02,0x0e,0x78,0x92,0xcb,0xf6,
+0xcb,0x08,0xf6,0x54,0x04,0xcb,0x54,0x06,0x4b,0x60,0x04,0x78,0x91,0x76,0x0b,0x78,
+0x93,0xe6,0x30,0xe3,0x13,0x78,0x94,0x12,0x04,0x65,0x90,0x00,0x05,0x12,0x01,0xec,
+0x24,0xfb,0x50,0x04,0x78,0x91,0x76,0x0d,0x78,0x93,0xe6,0x54,0xc0,0x7d,0x00,0x64,
+0xc0,0x4d,0x70,0x04,0x78,0x91,0x76,0x0b,0x78,0x94,0x12,0x04,0x65,0x90,0x00,0x04,
+0x12,0x01,0xec,0x24,0xfc,0x50,0x04,0x78,0x91,0x76,0x0f,0x78,0x94,0x12,0x04,0x65,
+0x90,0x00,0x06,0x12,0x01,0xec,0x24,0xfd,0x50,0x04,0x78,0x91,0x76,0x0e,0x78,0x94,
+0x12,0x04,0x65,0x90,0x00,0x09,0x12,0x01,0xec,0x24,0xfd,0x50,0x04,0x78,0x91,0x76,
+0x0a,0x78,0x91,0xe6,0x70,0x2a,0x78,0x8f,0xe6,0xfc,0x12,0x0f,0x7c,0x78,0x94,0x12,
+0x04,0x65,0x78,0xa1,0xe6,0xf9,0x78,0xa0,0xe6,0xfa,0x7b,0x01,0x74,0x0a,0x78,0x00,
+0x12,0x03,0x3f,0xc2,0x03,0x78,0x8f,0xe6,0xfc,0x12,0x11,0x23,0x78,0x91,0xec,0xf6,
+0x78,0x91,0xe6,0xfc,0x12,0x25,0x4a,0x12,0x31,0x08,0x22,0x12,0x30,0x85,0x78,0x8f,
+0xec,0xf6,0x12,0x0f,0x7c,0x78,0x8f,0xe6,0x24,0xfd,0x75,0xf0,0x0a,0xa4,0x24,0x1c,
+0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,0xac,0x82,0xad,0x83,0x78,0xa0,0x86,0x83,0x08,
+0x86,0x82,0xec,0xf9,0xed,0xfa,0x7b,0x0a,0x78,0x01,0x12,0x03,0xa7,0xc2,0x03,0x78,
+0x8f,0xe6,0xfc,0x12,0x11,0x23,0x12,0x31,0x08,0x22,0x8d,0x2b,0x8c,0x2a,0xed,0x60,
+0x40,0x75,0x27,0x01,0x75,0x29,0x48,0x75,0x28,0xff,0xe5,0x2a,0x24,0xfd,0xfc,0xe4,
+0x34,0xff,0xfd,0xec,0x7c,0x03,0x25,0xe0,0xcd,0x33,0xcd,0xdc,0xf9,0xfc,0xe5,0x29,
+0x2c,0xf5,0x29,0xe5,0x28,0x3d,0xf5,0x28,0xad,0x29,0xae,0x28,0xaf,0x27,0x74,0x80,
+0x90,0x00,0x06,0x12,0x03,0x17,0x74,0x80,0x90,0x00,0x02,0x12,0x03,0x17,0x12,0x0f,
+0xd3,0xe5,0x2b,0x14,0x60,0x3b,0x75,0x27,0x01,0x75,0x29,0x08,0x75,0x28,0xff,0xe5,
+0x2a,0x24,0xfd,0xfc,0xe4,0x34,0xff,0xfd,0xec,0x7c,0x03,0x25,0xe0,0xcd,0x33,0xcd,
+0xdc,0xf9,0xfc,0xe5,0x29,0x2c,0xf5,0x29,0xe5,0x28,0x3d,0xf5,0x28,0xad,0x29,0xae,
+0x28,0xaf,0x27,0xe4,0x90,0x00,0x06,0x12,0x03,0x17,0xe4,0x90,0x00,0x02,0x12,0x03,
+0x17,0x22,0x12,0x30,0x85,0x78,0x8f,0xec,0xf6,0xec,0x24,0x97,0xf8,0xe6,0x30,0xe2,
+0x09,0x78,0x8f,0xe6,0xfc,0x12,0x15,0x22,0xd2,0x00,0x78,0x8f,0xe6,0xfc,0x12,0x0f,
+0x7c,0x78,0x90,0x76,0x00,0x90,0xfd,0x41,0xe0,0x30,0xe7,0x04,0x78,0x90,0x76,0x01,
+0x78,0x90,0xe6,0xfd,0x78,0x8f,0xe6,0xfc,0x12,0x0d,0x3a,0xc2,0x03,0x30,0x00,0x07,
+0x78,0x8f,0xe6,0xfc,0x12,0x14,0x21,0x7c,0x00,0x12,0x25,0x4a,0x12,0x31,0x08,0x22,
+0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x44,0x01,
+0xf0,0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x30,
+0xe0,0x02,0x80,0xed,0x78,0xa3,0xe6,0x24,0x0b,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xe0,0x54,0xf8,0xf0,0x78,0xa3,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x44,0x80,0xf0,0x22,0xc2,0x03,0x8c,0x58,0x12,0x0f,0x7c,0x78,0xa0,
+0x86,0x83,0x08,0x86,0x82,0x79,0xee,0x7a,0x34,0x7b,0x0a,0x78,0x01,0x12,0x03,0xf5,
+0x12,0x0e,0x10,0xac,0x58,0x7d,0x02,0x12,0x0d,0x3a,0xc2,0x03,0xac,0x58,0x12,0x11,
+0x23,0x22,0x8d,0x53,0x8e,0x52,0x8f,0x51,0x8c,0x50,0x12,0x0f,0x7c,0x75,0x4f,0x00,
+0x78,0xa3,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x20,0xe4,
+0x16,0xe5,0x4f,0x24,0xf6,0x40,0x10,0x05,0x4f,0xc2,0x03,0x7c,0x18,0x12,0x32,0x48,
+0xac,0x50,0x12,0x0f,0x7c,0x80,0xd9,0x78,0xa3,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xe0,0x20,0xe4,0x05,0xc2,0x03,0x7c,0x02,0x22,0x78,0xa3,0xe6,
+0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0x0f,0x60,0x16,0x78,
+0xa3,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0x0f,0xf0,
+0xc2,0x03,0x7c,0x01,0x22,0x78,0xa2,0x86,0x83,0x08,0x86,0x82,0xe0,0xad,0x53,0xae,
+0x52,0xaf,0x51,0x12,0x03,0x0f,0xc2,0x03,0x7c,0x00,0x22,0x8d,0x31,0x8c,0x30,0x12,
+0x15,0x22,0xe5,0x31,0x60,0x20,0xe5,0x30,0xb4,0x03,0x0c,0x7c,0x01,0x12,0x24,0x7c,
+0x7c,0x81,0x12,0x24,0x7c,0x80,0x0f,0xe5,0x30,0xb4,0x04,0x0a,0x7c,0x02,0x12,0x24,
+0x7c,0x7c,0x82,0x12,0x24,0x7c,0xac,0x30,0x12,0x0f,0x7c,0xe5,0x31,0x60,0x1a,0x78,
+0xa4,0x86,0x83,0x08,0x86,0x82,0xe0,0x54,0xe7,0xf0,0xa3,0xa3,0xa3,0xa3,0xe0,0x54,
+0xe7,0xf0,0xac,0x30,0x7d,0x02,0x12,0x0d,0x3a,0x78,0xa0,0x86,0x83,0x08,0x86,0x82,
+0x79,0xf8,0x7a,0x34,0x7b,0x0a,0x78,0x01,0x12,0x03,0xf5,0xc2,0x03,0xe5,0x30,0x24,
+0x97,0xf8,0xc6,0x54,0xfd,0xf6,0xac,0x30,0x12,0x11,0x23,0x22,0x8c,0x26,0x30,0x03,
+0x05,0x12,0x31,0xe7,0x80,0xf8,0x7c,0x0a,0x12,0x30,0xfa,0xd2,0x03,0xe5,0x26,0x24,
+0xfd,0x78,0x9d,0xf6,0x70,0x09,0x78,0xa4,0x76,0xff,0x08,0x76,0xe0,0x80,0x07,0x78,
+0xa4,0x76,0xff,0x08,0x76,0xe2,0x78,0x9d,0xe6,0x75,0xf0,0x10,0xa4,0xad,0xf0,0xfc,
+0x24,0xa0,0x78,0xa3,0xf6,0xed,0x34,0xff,0x18,0xf6,0x78,0x9d,0xe6,0x75,0xf0,0x0a,
+0xa4,0x24,0x08,0xfc,0xe4,0x34,0xfd,0xfd,0x78,0xa0,0xed,0xf6,0x08,0xec,0xf6,0x12,
+0x31,0x93,0x22,0x78,0xa3,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,
+0xe0,0x30,0xe7,0x22,0x78,0xa3,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xe0,0x54,0x7f,0xf0,0x78,0xa3,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x44,0x80,0xf0,0x22,0x78,0xa4,0x86,0x83,0x08,0x86,0x82,0xe0,0x54,
+0x7f,0xf0,0xad,0x83,0xe5,0x82,0x24,0x04,0xfc,0xe4,0x3d,0x8c,0x82,0xf5,0x83,0xe0,
+0x54,0x7f,0xf0,0x78,0xa3,0xe6,0x24,0x0b,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,
+0xe0,0x54,0xf8,0xf0,0x78,0xa5,0xe6,0x24,0x01,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xe0,0x44,0x03,0xf0,0x78,0xa5,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x44,0x03,0xf0,0x78,0xa3,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0x74,0x0f,0xf0,0x22,0x78,0xa4,0x86,0x83,0x08,0x86,0x82,0xe0,0x54,
+0x3f,0xf0,0xad,0x83,0xe5,0x82,0x24,0x04,0xfc,0xe4,0x3d,0x8c,0x82,0xf5,0x83,0xe0,
+0x54,0x3f,0xf0,0x78,0x9d,0xe6,0x24,0x9e,0xf8,0xe6,0xfc,0x78,0xa5,0xe6,0x24,0x01,
+0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0x78,0x9d,0xe6,0x24,0x9e,0xf8,
+0xe6,0xfc,0x78,0xa5,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,
+0xf0,0x78,0xa3,0xe6,0x24,0x0b,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,
+0xfb,0x44,0x02,0xf5,0x26,0x78,0xa1,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x30,0xe5,0x03,0x43,0x26,0x01,0x78,0xa3,0xe6,0x24,0x05,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x30,0xe0,0x03,0x12,0x0f,0xd3,0xe5,0x26,0xfc,
+0x78,0xa3,0xe6,0x24,0x0b,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0x78,
+0xa3,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0x74,0x0f,0xf0,0x78,
+0xa4,0x86,0x83,0x08,0x86,0x82,0xe0,0x44,0x80,0xf0,0xa3,0xa3,0xa3,0xa3,0xe0,0x44,
+0x80,0xf0,0x22,0x8c,0x2a,0x12,0x0f,0x7c,0x78,0xa1,0xe6,0x24,0x08,0xf5,0x82,0x18,
+0xe6,0x34,0x00,0xf5,0x83,0xe0,0xfc,0x78,0xa3,0xe6,0x24,0x0a,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xec,0xf0,0x78,0xa1,0xe6,0x24,0x07,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xe0,0xfc,0x78,0xa3,0xe6,0x24,0x09,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xec,0xf0,0x78,0xa0,0x86,0x83,0x08,0x86,0x82,0xe0,0xfd,0xa3,0xe0,0xfc,
+0xed,0xfe,0x78,0xa3,0xe6,0x24,0x08,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xee,
+0xf0,0xec,0xfe,0x78,0xa3,0xe6,0x24,0x07,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,
+0xee,0xf0,0x8c,0x29,0x8d,0x28,0xc3,0xec,0x94,0x02,0xed,0x94,0x06,0x40,0x05,0x75,
+0x27,0x7c,0x80,0x33,0xd3,0xe5,0x29,0x94,0x81,0xe5,0x28,0x94,0x01,0x40,0x05,0x75,
+0x27,0x3c,0x80,0x23,0xd3,0xe5,0x29,0x94,0xc0,0xe5,0x28,0x94,0x00,0x40,0x05,0x75,
+0x27,0x18,0x80,0x13,0xd3,0xe5,0x29,0x94,0x30,0xe5,0x28,0x94,0x00,0x40,0x05,0x75,
+0x27,0x0c,0x80,0x03,0x75,0x27,0x08,0xaf,0x27,0xe4,0xef,0x54,0x7c,0x44,0x83,0xff,
+0x8f,0x27,0xe5,0x27,0xfc,0x78,0xa5,0xe6,0x24,0x01,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xec,0xf0,0xe5,0x27,0xfc,0x78,0xa5,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xec,0xf0,0xe5,0x27,0xfc,0x78,0x9d,0xe6,0x24,0x9e,0xf8,0xec,
+0xf6,0x78,0xa3,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xf5,
+0x27,0x78,0xa1,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xa3,0xe0,
+0x30,0xe3,0x17,0x53,0x27,0xc7,0x78,0xa1,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xe0,0x90,0x34,0xe9,0x93,0x42,0x27,0x78,0xa1,0xe6,0x24,0x02,0xf5,
+0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x30,0xe7,0x05,0x43,0x27,0x40,0x80,0x03,
+0x53,0x27,0xbf,0x53,0x27,0xfb,0x78,0xa1,0xe6,0x24,0x06,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xe0,0x60,0x03,0x43,0x27,0x04,0x53,0x27,0xfc,0x78,0xa1,0xe6,0x24,
+0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x42,0x27,0x43,0x27,0x80,0xe5,
+0x27,0xfc,0x78,0xa3,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,
+0xf0,0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xf5,
+0x27,0x78,0xa1,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xa3,0xe0,
+0x30,0xe1,0x05,0x53,0x27,0xdf,0x80,0x03,0x43,0x27,0x20,0x78,0xa1,0xe6,0x24,0x02,
+0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x30,0xe4,0x05,0x53,0x27,0xef,0x80,
+0x03,0x43,0x27,0x10,0x78,0xa1,0xe6,0x24,0x09,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xe0,0xb4,0x02,0x03,0x43,0x27,0x02,0xe5,0x27,0xfc,0x78,0xa3,0xe6,0x24,0x04,
+0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0x78,0xa3,0xe6,0x24,0x03,0xf5,
+0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xf5,0x27,0x78,0xa1,0xe6,0x24,0x09,0xf5,
+0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x70,0x05,0x53,0x27,0x7f,0x80,0x03,0x43,
+0x27,0x80,0x78,0xa1,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xa3,
+0xe0,0x30,0xe0,0x05,0x43,0x27,0x20,0x80,0x03,0x53,0x27,0xdf,0x78,0xa1,0xe6,0x24,
+0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x30,0xe3,0x05,0x43,0x27,0x40,
+0x80,0x03,0x53,0x27,0xbf,0x78,0xa1,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x30,0xe0,0x05,0x43,0x27,0x10,0x80,0x03,0x53,0x27,0xef,0x78,0xa1,
+0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xa3,0xe0,0x30,0xe4,0x05,
+0x43,0x27,0x08,0x80,0x03,0x53,0x27,0xf7,0x78,0xa1,0xe6,0x24,0x02,0xf5,0x82,0x18,
+0xe6,0x34,0x00,0xf5,0x83,0xa3,0xe0,0x30,0xe5,0x05,0x43,0x27,0x04,0x80,0x03,0x53,
+0x27,0xfb,0x78,0xa1,0xe6,0x24,0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xa3,
+0xe0,0x30,0xe6,0x05,0x43,0x27,0x01,0x80,0x03,0x53,0x27,0xfe,0x78,0xa1,0xe6,0x24,
+0x02,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xa3,0xe0,0x30,0xe7,0x05,0x43,0x27,
+0x02,0x80,0x03,0x53,0x27,0xfd,0xe5,0x27,0xfc,0x78,0xa3,0xe6,0x24,0x03,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0xc2,0x03,0x7c,0x00,0x22,0x8d,0x27,0x8c,
+0x26,0xed,0x54,0x03,0x14,0x60,0x03,0x7c,0x10,0x22,0xe5,0x27,0x54,0x7c,0x24,0xfc,
+0x40,0x03,0x7c,0x0b,0x22,0xe5,0x26,0x24,0x97,0xf8,0xc6,0x44,0x02,0xf6,0x7c,0x00,
+0x22,0x8c,0x30,0x12,0x0f,0x7c,0xe5,0x30,0x24,0x97,0xf8,0xe6,0x20,0xe2,0x4f,0xac,
+0x30,0x7d,0x02,0x12,0x0d,0x3a,0xe5,0x30,0x24,0xfe,0x44,0x28,0xfc,0x78,0xa4,0x86,
+0x83,0x08,0x86,0x82,0xec,0xf0,0xaf,0x83,0xe5,0x82,0x24,0x04,0xfe,0xe4,0x3f,0xff,
+0xec,0x8e,0x82,0x8f,0x83,0xf0,0x7c,0x03,0x8c,0x2c,0xe5,0x2c,0xfc,0x78,0xa5,0xe6,
+0x24,0x01,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0xe5,0x2c,0xfc,0x78,
+0xa5,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xec,0xf0,0x75,0x2d,
+0x01,0x75,0x2f,0x48,0x75,0x2e,0xff,0xe5,0x30,0x24,0xfd,0xfc,0xe4,0x34,0xff,0xfd,
+0xec,0x7c,0x03,0x25,0xe0,0xcd,0x33,0xcd,0xdc,0xf9,0xfc,0xe5,0x2f,0x2c,0xf5,0x2f,
+0xe5,0x2e,0x3d,0xf5,0x2e,0x78,0xa5,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x54,0xe7,0xf5,0x2c,0xad,0x2f,0xae,0x2e,0xaf,0x2d,0xe4,0x90,0x00,
+0x02,0x12,0x03,0x17,0xe4,0x90,0x00,0x06,0x12,0x03,0x17,0x12,0x01,0xe6,0x30,0xe5,
+0x03,0x43,0x2c,0x10,0xe5,0x2c,0xfc,0x78,0xa5,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xec,0xf0,0x12,0x10,0x67,0x78,0xa3,0xe6,0x24,0x06,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0xc2,0x03,0xfc,0xe5,0x30,0x24,0x97,0xf8,0xc6,
+0x44,0x04,0xf6,0x8c,0x2c,0xe5,0x30,0x54,0x0f,0xc4,0x54,0xf0,0x7e,0x00,0xff,0xee,
+0xef,0x44,0x04,0x7d,0x00,0xff,0xec,0x4e,0xfc,0xed,0x4f,0xfd,0x12,0x1c,0xfe,0x7c,
+0x00,0x22,0x8c,0x2f,0x12,0x0f,0x7c,0x12,0x10,0x07,0x78,0xa4,0x86,0x83,0x08,0x86,
+0x82,0xe0,0x54,0x08,0xf0,0xa3,0xa3,0xa3,0xa3,0xe0,0x54,0x08,0xf0,0xac,0x2f,0x7d,
+0x02,0x12,0x0d,0x3a,0xc2,0x03,0xe5,0x2f,0x24,0x97,0xf8,0xc6,0x54,0xfb,0xf6,0x7c,
+0x00,0x22,0x12,0x30,0x85,0x78,0x90,0xec,0xf6,0xec,0x24,0x97,0xf8,0xe6,0x30,0xe1,
+0x0a,0x7d,0x00,0x7c,0x13,0x12,0x24,0xb1,0x12,0x31,0x08,0x78,0x90,0xe6,0x24,0x97,
+0xf8,0xc6,0x44,0x01,0xf6,0x78,0x90,0xe6,0xfc,0x12,0x0f,0x7c,0x78,0x90,0xe6,0x24,
+0xfd,0x75,0xf0,0x0a,0xa4,0x24,0x1c,0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,0x78,0xa0,
+0xe6,0xfa,0x08,0xe6,0xf9,0x7b,0x0a,0x78,0x01,0x12,0x03,0xa7,0x78,0xa0,0x86,0x83,
+0x08,0x86,0x82,0x79,0xf8,0x7a,0x34,0x7b,0x0a,0x78,0x01,0x12,0x03,0xf5,0x12,0x0f,
+0xd3,0xc2,0x03,0x78,0x90,0xe6,0xfc,0x12,0x11,0x23,0x78,0x8f,0xec,0xf6,0xec,0x60,
+0x0a,0x7d,0x00,0x7c,0x08,0x12,0x24,0xb1,0x12,0x31,0x08,0x78,0x90,0xe6,0xfc,0x12,
+0x0f,0x7c,0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,
+0x44,0x10,0x54,0xdf,0xfc,0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xec,0xf0,0x78,0x8f,0xec,0xf6,0xc2,0x03,0x7c,0xc8,0x12,0x32,0x48,0x78,
+0x90,0xe6,0xfc,0x12,0x0f,0x7c,0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,
+0x00,0xf5,0x83,0xe0,0x54,0xef,0xf0,0xc2,0x03,0x7c,0xc8,0x12,0x32,0x48,0x78,0x90,
+0xe6,0xfc,0x12,0x0f,0x7c,0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,
+0xf5,0x83,0xe0,0x44,0x10,0xf0,0xc2,0x03,0x7c,0xc8,0x12,0x32,0x48,0x78,0x90,0xe6,
+0xfc,0x12,0x0f,0x7c,0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,
+0x83,0xe0,0x44,0x20,0xf0,0xc2,0x03,0x7c,0xf0,0x12,0x32,0x48,0x78,0x90,0xe6,0xfc,
+0x12,0x0f,0x7c,0x78,0xa3,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,
+0xe0,0x30,0xe4,0x15,0xc2,0x03,0x78,0x90,0xe6,0x44,0x10,0x7f,0x00,0xfe,0x7c,0x07,
+0x12,0x31,0x9a,0x12,0x31,0x08,0x02,0x17,0x49,0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,
+0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0xcf,0xf0,0xc2,0x03,0x7c,0xc8,0x12,0x32,
+0x48,0x78,0x90,0xe6,0xfc,0x12,0x0f,0x7c,0x78,0xa3,0xe6,0x24,0x04,0xf5,0x82,0x18,
+0xe6,0x34,0x00,0xf5,0x83,0xe0,0x44,0x30,0xf0,0xc2,0x03,0x7c,0xf0,0x12,0x32,0x48,
+0x78,0x90,0xe6,0xfc,0x12,0x0f,0x7c,0x78,0xa3,0xe6,0x24,0x05,0xf5,0x82,0x18,0xe6,
+0x34,0x00,0xf5,0x83,0xe0,0x30,0xe4,0x14,0xc2,0x03,0x78,0x90,0xe6,0x44,0x10,0x7f,
+0x00,0xfe,0x7c,0x07,0x12,0x31,0x9a,0x12,0x31,0x08,0x80,0x5d,0x78,0xa3,0xe6,0x24,
+0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0xef,0xf0,0x78,0xa3,0xe6,
+0x24,0x04,0xf5,0x82,0x18,0xe6,0x34,0x00,0xf5,0x83,0xe0,0x54,0xdf,0xf0,0x78,0x90,
+0xe6,0x24,0xfd,0x75,0xf0,0x0a,0xa4,0x24,0x1c,0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,
+0xac,0x82,0xad,0x83,0x78,0xa0,0x86,0x83,0x08,0x86,0x82,0xec,0xf9,0xed,0xfa,0x7b,
+0x0a,0x78,0x01,0x12,0x03,0xa7,0xc2,0x03,0x78,0x90,0xe6,0xfc,0x12,0x11,0x23,0x7d,
+0x00,0x7c,0x0b,0x12,0x24,0xb1,0x12,0x31,0x08,0x22,0x12,0x30,0x85,0x90,0xff,0x91,
+0xe0,0x90,0xfd,0x41,0xf0,0x7d,0x02,0x7c,0x00,0x12,0x24,0xb1,0x12,0x31,0x08,0x22,
+0x12,0x30,0x85,0x90,0xfd,0x40,0xe0,0xf4,0xfc,0x90,0xff,0x91,0xe0,0x5c,0xf5,0x33,
+0x90,0xfd,0x41,0xe0,0xfc,0x90,0xfd,0x40,0xe0,0x5c,0x42,0x33,0xe5,0x33,0x90,0xff,
+0x91,0xf0,0x7c,0x00,0x12,0x25,0x4a,0x12,0x31,0x08,0x22,0x74,0x3c,0x90,0xfb,0xe8,
+0xf0,0x74,0x3e,0x90,0xfb,0xe8,0xf0,0xe4,0x90,0xfd,0x30,0xf0,0x22,0x8d,0x35,0x8c,
+0x34,0xec,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x02,0x80,0x28,0xb4,0x02,0x02,0x80,
+0x03,0xd3,0x40,0x08,0xa8,0x35,0xc6,0x25,0xe0,0xf6,0x80,0x18,0xb4,0x04,0x02,0x80,
+0x03,0xd3,0x40,0x0a,0xa8,0x35,0xc6,0x25,0xe0,0x25,0xe0,0xf6,0x80,0x06,0xa8,0x35,
+0x76,0x00,0x80,0x00,0x22,0x8c,0x3c,0x8d,0x3b,0xed,0xfe,0xec,0xfd,0x7f,0x01,0x75,
+0x60,0x06,0x75,0x61,0x00,0x90,0xfd,0x31,0x12,0x04,0x6e,0x12,0x01,0xe6,0xb4,0x80,
+0x02,0x80,0x06,0xd3,0x50,0x03,0x02,0x18,0x9e,0x90,0xfd,0x31,0x12,0x04,0x80,0x90,
+0x00,0x03,0x12,0x01,0xec,0x54,0xf0,0xb4,0x30,0x02,0x80,0x03,0xd3,0x40,0x5f,0x90,
+0xfd,0x31,0x12,0x04,0x80,0x90,0x00,0x08,0x12,0x02,0x0e,0xfa,0xfd,0xeb,0xfe,0x7f,
+0x01,0x90,0xfd,0x34,0x12,0x04,0x6e,0xee,0xcd,0x90,0x35,0x02,0xfc,0xe4,0x93,0xff,
+0x74,0x01,0x93,0xfe,0xf9,0xef,0xfa,0x7b,0x01,0xea,0xff,0xe9,0xfe,0xec,0xc3,0x9e,
+0xed,0x9f,0x40,0x25,0x90,0x35,0x04,0xe4,0x93,0xfd,0x74,0x01,0x93,0xfc,0xed,0xfe,
+0xec,0xfd,0x7f,0x01,0xee,0xcd,0xfc,0x90,0xfd,0x36,0xe0,0xd3,0x9c,0x90,0xfd,0x35,
+0xe0,0x9d,0x50,0x05,0x75,0x60,0x80,0x80,0x33,0x12,0x19,0xbc,0x80,0x2e,0xb4,0x60,
+0x02,0x80,0x03,0xd3,0x40,0x0b,0xac,0x3c,0xad,0x3b,0x12,0x07,0x82,0x8c,0x60,0x80,
+0x1b,0xb4,0x10,0x03,0xb3,0x40,0x10,0xc3,0xb4,0x20,0x03,0xb3,0x40,0x09,0xc3,0xb4,
+0x40,0x02,0x80,0x03,0xd3,0x40,0x00,0x75,0x60,0x81,0x80,0x00,0x80,0x75,0xb4,0x81,
+0x02,0x80,0x03,0xd3,0x40,0x6b,0x90,0xfd,0x31,0x12,0x04,0x80,0x90,0x00,0x03,0x12,
+0x01,0xec,0x54,0xf0,0xb4,0x30,0x02,0x80,0x03,0xd3,0x40,0x1d,0x90,0xfd,0x31,0x12,
+0x04,0x80,0x90,0x00,0x08,0x12,0x02,0x0e,0xfa,0xfd,0xeb,0xfe,0x7f,0x01,0x90,0xfd,
+0x37,0x12,0x04,0x6e,0x12,0x19,0x26,0x80,0x36,0xb4,0x60,0x02,0x80,0x03,0xd3,0x40,
+0x13,0x75,0x3a,0x61,0xe4,0xf5,0x39,0xf5,0x38,0xac,0x3c,0xad,0x3b,0x12,0x05,0xde,
+0x8c,0x60,0x80,0x1b,0xb4,0x10,0x03,0xb3,0x40,0x10,0xc3,0xb4,0x20,0x03,0xb3,0x40,
+0x09,0xc3,0xb4,0x40,0x02,0x80,0x03,0xd3,0x40,0x00,0x75,0x60,0x81,0x80,0x00,0x80,
+0x02,0x80,0x00,0xe5,0x60,0xfc,0x90,0xfd,0x31,0x12,0x04,0x80,0xec,0x90,0x00,0x02,
+0x12,0x03,0x17,0xac,0x61,0x22,0x90,0xfd,0x31,0x12,0x04,0x80,0x90,0x00,0x04,0x12,
+0x01,0xec,0x60,0x04,0x74,0x01,0x80,0x01,0xe4,0xa2,0xe0,0x92,0x01,0x90,0xfd,0x31,
+0x12,0x04,0x80,0xed,0x24,0x03,0xfd,0x50,0x01,0x0e,0x90,0xfd,0x34,0x12,0x04,0x6e,
+0x90,0xfd,0x31,0x12,0x04,0x80,0x90,0x00,0x05,0x12,0x01,0xec,0xf5,0x61,0x90,0x00,
+0x04,0x12,0x01,0xec,0x54,0x0f,0xfc,0x7d,0x61,0x12,0x17,0x9d,0xe5,0x61,0x70,0x04,
+0x75,0x60,0x08,0x22,0x75,0x60,0x00,0x78,0x7e,0x76,0x00,0x78,0x7e,0xe6,0xc3,0x95,
+0x61,0x50,0x38,0x90,0xfd,0x37,0x12,0x04,0x80,0x12,0x01,0xe6,0xfc,0x90,0xfd,0x34,
+0x12,0x04,0x80,0xec,0x12,0x03,0x0f,0x30,0x01,0x0e,0x90,0xfd,0x39,0xe0,0x04,0xf0,
+0x90,0xfd,0x38,0x70,0x03,0xe0,0x04,0xf0,0x78,0x7e,0x06,0x90,0xfd,0x36,0xe0,0x04,
+0xf0,0x90,0xfd,0x35,0x70,0x03,0xe0,0x04,0xf0,0x80,0xc0,0x22,0x90,0xfd,0x32,0xe0,
+0xfd,0xa3,0xe0,0xfc,0xed,0xfe,0xec,0xfd,0x7f,0x01,0xed,0x24,0x0a,0xfd,0x50,0x01,
+0x0e,0x90,0xfd,0x3a,0x12,0x04,0x6e,0x90,0xfd,0x31,0x12,0x04,0x80,0x90,0x00,0x04,
+0x12,0x01,0xec,0x54,0x0f,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x17,0x90,0xfd,0x3a,
+0x12,0x04,0x80,0x0d,0xed,0x70,0x01,0x0e,0x90,0xfd,0x37,0x12,0x04,0x6e,0x78,0x82,
+0x76,0x01,0x80,0x4e,0xb4,0x02,0x02,0x80,0x03,0xd3,0x40,0x19,0x90,0xfd,0x3a,0x12,
+0x04,0x80,0xed,0x24,0x02,0xfd,0x50,0x01,0x0e,0x90,0xfd,0x37,0x12,0x04,0x6e,0x78,
+0x82,0x76,0x02,0x80,0x2d,0xb4,0x04,0x02,0x80,0x03,0xd3,0x40,0x19,0x90,0xfd,0x3a,
+0x12,0x04,0x80,0xed,0x24,0x04,0xfd,0x50,0x01,0x0e,0x90,0xfd,0x37,0x12,0x04,0x6e,
+0x78,0x82,0x76,0x04,0x80,0x0c,0xb4,0x00,0x02,0x80,0x03,0xd3,0x40,0x00,0x75,0x60,
+0x08,0x22,0x90,0xfd,0x31,0x12,0x04,0x80,0x90,0x00,0x05,0x12,0x01,0xec,0xf5,0x61,
+0x78,0x7f,0x76,0x00,0x78,0x7f,0xe6,0xc3,0x95,0x61,0x40,0x03,0x02,0x1b,0x24,0x78,
+0x80,0x76,0x00,0x78,0x80,0xe6,0xc3,0x78,0x82,0x96,0x50,0x76,0x90,0xfd,0x34,0x12,
+0x04,0x80,0x12,0x01,0xe6,0xfc,0x90,0xfd,0x3a,0x12,0x04,0x89,0x12,0x01,0xe0,0xf4,
+0x5c,0xfc,0x12,0x01,0xe0,0xf8,0x90,0xfd,0x37,0x12,0x04,0x80,0xe8,0xc0,0xe0,0x12,
+0x01,0xe6,0xc8,0xd0,0xe0,0xc8,0x58,0x4c,0xfc,0x90,0xfd,0x34,0x12,0x04,0x80,0xec,
+0x12,0x03,0x0f,0x78,0x81,0xec,0xf6,0x90,0xfd,0x39,0xe0,0x04,0xf0,0x90,0xfd,0x38,
+0x70,0x03,0xe0,0x04,0xf0,0x09,0xe9,0x70,0x01,0x0a,0x90,0xfd,0x3a,0x12,0x04,0x77,
+0x90,0xfd,0x31,0x12,0x04,0x80,0x90,0x00,0x04,0x12,0x01,0xec,0x30,0xe4,0x0e,0x90,
+0xfd,0x36,0xe0,0x04,0xf0,0x90,0xfd,0x35,0x70,0x03,0xe0,0x04,0xf0,0x78,0x80,0x06,
+0x80,0x81,0x78,0x82,0xe6,0xfd,0xe4,0xfe,0xff,0xee,0xcd,0xfc,0x90,0xfd,0x39,0xe0,
+0x2c,0xf0,0x90,0xfd,0x38,0xe0,0x3d,0xf0,0x78,0x82,0xe6,0xfd,0xe4,0xfe,0xff,0xee,
+0xcd,0xfc,0x90,0xfd,0x3c,0xe0,0x2c,0xf0,0x90,0xfd,0x3b,0xe0,0x3d,0xf0,0x78,0x7f,
+0x06,0x02,0x1a,0x64,0x75,0x60,0x00,0x22,0xe5,0x3d,0x05,0x3d,0x04,0x70,0x02,0xb2,
+0xb0,0x22,0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0xe8,0xc0,0xe0,0xe9,
+0xc0,0xe0,0xea,0xc0,0xe0,0xeb,0xc0,0xe0,0xec,0xc0,0xe0,0xed,0xc0,0xe0,0xee,0xc0,
+0xe0,0xef,0xc0,0xe0,0x90,0xff,0x92,0xe0,0x12,0x01,0xb7,0x1b,0x80,0x30,0x1b,0x80,
+0x32,0x1b,0x8f,0x38,0x1b,0xa1,0x3a,0x1b,0xb3,0x3e,0x1b,0xcb,0x44,0x1b,0xbf,0x46,
+0x1b,0xd7,0x50,0x1c,0x19,0x52,0x1b,0xf8,0x54,0x1c,0x3a,0x56,0x00,0x00,0x1c,0x5b,
+0x90,0xff,0x92,0xe0,0x7f,0x00,0xfe,0x7c,0x01,0x12,0x31,0x9a,0x02,0x1c,0x6b,0xe4,
+0xff,0x04,0xfe,0x7c,0x03,0x12,0x31,0x9a,0x74,0x20,0x90,0xff,0xfe,0xf0,0x02,0x1c,
+0x6b,0xe4,0xff,0x04,0xfe,0x7c,0x02,0x12,0x31,0x9a,0x74,0x40,0x90,0xff,0xfe,0xf0,
+0x02,0x1c,0x6b,0xe4,0xff,0x04,0xfe,0x7c,0x04,0x12,0x31,0x9a,0x02,0x1c,0x6b,0xe4,
+0xff,0x04,0xfe,0x7c,0x05,0x12,0x31,0x9a,0x02,0x1c,0x6b,0xe4,0xff,0x04,0xfe,0x7c,
+0x06,0x12,0x31,0x9a,0x02,0x1c,0x6b,0x90,0xff,0xa5,0xe0,0x7d,0x00,0x90,0xfd,0x00,
+0xcd,0xf0,0xa3,0xcd,0xf0,0x90,0xfd,0x01,0xe0,0xfc,0xf5,0x83,0x90,0xfd,0x00,0xe0,
+0x44,0x33,0xfd,0x12,0x1c,0xfe,0x80,0x73,0x90,0xff,0xb5,0xe0,0x7d,0x00,0x90,0xfd,
+0x02,0xcd,0xf0,0xa3,0xcd,0xf0,0x90,0xfd,0x03,0xe0,0xfc,0xf5,0x83,0x90,0xfd,0x02,
+0xe0,0x44,0x43,0xfd,0x12,0x1c,0xfe,0x80,0x52,0x90,0xff,0xa6,0xe0,0x7d,0x00,0x90,
+0xfd,0x04,0xcd,0xf0,0xa3,0xcd,0xf0,0x90,0xfd,0x05,0xe0,0xfc,0xf5,0x83,0x90,0xfd,
+0x04,0xe0,0x44,0x34,0xfd,0x12,0x1c,0xfe,0x80,0x31,0x90,0xff,0xb6,0xe0,0x7d,0x00,
+0x90,0xfd,0x06,0xcd,0xf0,0xa3,0xcd,0xf0,0x90,0xfd,0x07,0xe0,0xfc,0xf5,0x83,0x90,
+0xfd,0x06,0xe0,0x44,0x44,0xfd,0x12,0x1c,0xfe,0x80,0x10,0x90,0xff,0x92,0xe0,0x7d,
+0x00,0xfc,0xed,0x44,0xaa,0xfd,0x12,0x1c,0xfe,0x80,0x00,0xe4,0x90,0xff,0x92,0xf0,
+0xd0,0xe0,0xff,0xd0,0xe0,0xfe,0xd0,0xe0,0xfd,0xd0,0xe0,0xfc,0xd0,0xe0,0xfb,0xd0,
+0xe0,0xfa,0xd0,0xe0,0xf9,0xd0,0xe0,0xf8,0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,
+0xd0,0xe0,0x32,0x05,0x81,0x05,0x81,0x05,0x81,0x05,0x81,0xa8,0x81,0x18,0x18,0x18,
+0xed,0xf6,0x08,0xec,0xf6,0x90,0xff,0x6a,0xe0,0x20,0xe7,0x02,0x80,0xf7,0x90,0xff,
+0x69,0xe0,0x7d,0x00,0xa8,0x81,0x18,0xcd,0xf6,0xcd,0x08,0xf6,0x7d,0x03,0xa8,0x81,
+0xe6,0x18,0xfc,0xe6,0xcc,0x25,0xe0,0xcc,0x33,0xcc,0xdd,0xf9,0xcc,0xf6,0xcc,0x08,
+0xf6,0xa8,0x81,0x18,0xe6,0x44,0xf8,0xf6,0xa8,0x81,0x18,0x18,0x18,0xe6,0xfd,0x08,
+0xe6,0xfc,0xa8,0x81,0x18,0x86,0x83,0x08,0x86,0x82,0xed,0xf0,0xa3,0xec,0xf0,0x74,
+0x02,0x90,0xff,0x6a,0xf0,0x15,0x81,0x15,0x81,0x15,0x81,0x15,0x81,0x22,0xe5,0x81,
+0x24,0x05,0xf5,0x81,0xe4,0xa8,0x81,0x18,0xf6,0xa8,0x81,0x18,0x18,0x18,0x18,0xed,
+0xf6,0x08,0xec,0xf6,0x90,0xfb,0xfd,0xe0,0x24,0xf8,0x50,0x03,0x02,0x1e,0x1f,0xe4,
+0xa8,0x81,0x18,0x18,0xf6,0xa8,0x81,0x18,0xe6,0xfe,0xa8,0x81,0x18,0x18,0x18,0x18,
+0xe6,0xfd,0x08,0xe6,0xfc,0x7f,0x00,0xef,0x24,0xf8,0x40,0x4d,0xe4,0xef,0x25,0xe0,
+0x24,0x85,0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,0xe0,0xfb,0xa3,0xe0,0x6c,0x70,0x03,
+0xfa,0xeb,0x6d,0x70,0x09,0x74,0x01,0xa8,0x81,0x18,0x18,0xf6,0x80,0x2b,0xe4,0xef,
+0x25,0xe0,0x24,0x85,0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,0x7a,0x00,0xe0,0x54,0xf0,
+0xcc,0xf8,0xcc,0xcd,0xf9,0xcd,0xfb,0x78,0x00,0xe9,0x54,0xf0,0xf9,0xea,0x68,0x70,
+0x02,0xeb,0x69,0x70,0x01,0x0e,0x0f,0x80,0xae,0xa8,0x81,0x18,0xee,0xf6,0xa8,0x81,
+0x18,0x18,0x18,0x18,0xed,0xf6,0x08,0xec,0xf6,0xa8,0x81,0xef,0xf6,0xa8,0x81,0x18,
+0x18,0xe6,0x70,0x79,0xa8,0x81,0x18,0xe6,0x24,0xf7,0x40,0x71,0xa8,0x81,0x18,0x18,
+0x18,0x18,0xe6,0x54,0x0f,0xa8,0x81,0xf6,0x64,0x04,0x60,0x17,0xa8,0x81,0xe6,0x64,
+0x03,0x60,0x10,0xa8,0x81,0x18,0x18,0x18,0x18,0xe6,0xfd,0x08,0xe6,0xfc,0x12,0x1c,
+0x93,0x80,0x4a,0x7c,0x0a,0x12,0x30,0xfa,0xa8,0x81,0x18,0x18,0x18,0x18,0xe6,0xfd,
+0x08,0xe6,0xfc,0x90,0xfb,0xfc,0xe0,0x25,0xe0,0x24,0x85,0xf5,0x82,0xe4,0x34,0xfd,
+0xf5,0x83,0xed,0xf0,0xa3,0xec,0xf0,0x90,0xfb,0xfc,0xe0,0xff,0xe4,0xef,0x04,0x54,
+0x07,0xff,0x90,0xfb,0xfc,0xf0,0x90,0xfb,0xfd,0xe0,0x04,0xf0,0x12,0x31,0x93,0x90,
+0xfb,0xfe,0xe0,0x70,0x08,0xe4,0xfe,0xff,0x7c,0x0f,0x12,0x31,0x9a,0x80,0x27,0x90,
+0xfb,0xff,0xe0,0x04,0xf0,0x54,0x3f,0x70,0x1d,0x90,0xfb,0xff,0xe0,0x44,0xfe,0x7d,
+0x00,0xfc,0x90,0xfb,0xfc,0xe0,0x25,0xe0,0x24,0x85,0xf5,0x82,0xe4,0x34,0xfd,0xf5,
+0x83,0xed,0xf0,0xa3,0xec,0xf0,0xe5,0x81,0x24,0xfb,0xf5,0x81,0x22,0x78,0x85,0x76,
+0x00,0x78,0x86,0x76,0x00,0x74,0x01,0x90,0xfb,0xfe,0xf0,0x12,0x30,0x85,0x90,0xfb,
+0xfd,0xe0,0x60,0x59,0x7c,0x0a,0x12,0x30,0xfa,0x90,0xfb,0xfb,0xe0,0x25,0xe0,0x24,
+0x85,0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,0xe0,0xfd,0xa3,0xe0,0xfc,0x90,0xfb,0xfb,
+0xe0,0x25,0xe0,0x24,0x85,0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,0xe4,0xf0,0xa3,0xf0,
+0x90,0xfb,0xfb,0xe0,0xff,0xe4,0xef,0x04,0x54,0x07,0xff,0x90,0xfb,0xfb,0xf0,0x90,
+0xfb,0xfd,0xe0,0x14,0xf0,0x78,0x83,0xed,0xf6,0x08,0xec,0xf6,0x12,0x31,0x93,0xb2,
+0xb3,0x78,0x83,0xe6,0xfd,0x08,0xe6,0xfc,0x12,0x08,0xe5,0x80,0xa1,0x12,0x31,0xe7,
+0x78,0x85,0x06,0xb6,0x00,0x11,0x78,0x85,0x76,0x00,0x78,0x86,0xe6,0xf4,0x04,0x04,
+0xa2,0xe0,0x92,0xb4,0x78,0x86,0xf6,0x80,0x85,0xe4,0x90,0xfb,0xfe,0xf0,0x90,0xfb,
+0xfd,0xe0,0x7d,0x00,0xfc,0xed,0x44,0xcf,0xfd,0x12,0x1c,0x93,0x12,0x31,0x08,0x22,
+0x12,0x30,0x85,0xe5,0x6a,0x64,0x49,0x45,0x69,0x60,0x15,0x90,0xff,0x83,0xe0,0x54,
+0x0f,0x7d,0x00,0xd3,0x95,0x6a,0xed,0x95,0x69,0x50,0x05,0x12,0x2e,0xce,0x80,0x03,
+0x12,0x2f,0x9e,0x12,0x31,0x08,0x22,0x12,0x30,0x85,0xe5,0x6a,0x64,0x49,0x45,0x69,
+0x60,0x05,0x12,0x2f,0xd8,0x80,0x0e,0x90,0xff,0x80,0xe0,0x44,0x08,0xf0,0x90,0xff,
+0x83,0xe0,0x54,0x7f,0xf0,0x12,0x31,0x08,0x22,0x12,0x30,0x85,0x8c,0x54,0xec,0x54,
+0xf0,0xb4,0x10,0x15,0x75,0x64,0x3d,0x75,0x63,0xfd,0x75,0x62,0x01,0xe5,0x64,0x24,
+0x03,0xf5,0x64,0xe5,0x63,0x34,0x00,0xf5,0x63,0xe4,0xf5,0x57,0xf5,0x56,0xe5,0x56,
+0xc3,0x94,0x01,0x50,0x27,0xe5,0x54,0x54,0x0f,0xfc,0xad,0x64,0xae,0x63,0xaf,0x62,
+0x12,0x0e,0x82,0x8c,0x55,0xec,0x60,0x02,0x80,0x12,0x05,0x64,0xe5,0x64,0x70,0x02,
+0x05,0x63,0x05,0x57,0xe5,0x57,0x70,0x02,0x05,0x56,0x80,0xd2,0xe5,0x54,0x54,0x0f,
+0x24,0x97,0xf8,0xc6,0x54,0xfe,0xf6,0xe5,0x54,0x54,0x0f,0x7f,0x00,0xfe,0x7c,0x12,
+0x12,0x31,0x9a,0xe5,0x55,0x14,0x70,0x09,0x7d,0x00,0x7c,0x09,0x12,0x24,0xb1,0x80,
+0x07,0xad,0x57,0x7c,0x00,0x12,0x24,0xb1,0x12,0x31,0x08,0x22,0x12,0x30,0x85,0x90,
+0xff,0xfc,0xe0,0x44,0x02,0xf0,0x90,0xff,0x00,0xe0,0x30,0xe7,0x13,0x90,0xff,0x83,
+0xe0,0x44,0x80,0xf0,0x43,0x67,0x80,0x90,0xff,0xfc,0xe0,0x44,0x01,0xf0,0x80,0x11,
+0x90,0xff,0x82,0xe0,0x44,0x08,0xf0,0x53,0x67,0x7f,0x90,0xff,0xfc,0xe0,0x54,0xfe,
+0xf0,0x90,0xff,0x81,0xe0,0x44,0x80,0xf0,0x12,0x25,0x64,0x90,0xff,0xfe,0xe0,0x44,
+0x05,0xf0,0x90,0xff,0xfc,0xe0,0x54,0xfd,0xf0,0x12,0x31,0x08,0x22,0x12,0x30,0x85,
+0x7c,0x01,0x12,0x32,0x48,0x78,0xa7,0xe6,0x44,0x02,0xf6,0x74,0xfe,0xfc,0x04,0xfd,
+0x12,0x1c,0xfe,0x90,0xff,0x6a,0xe0,0x30,0xe7,0x02,0x80,0xf7,0xe4,0xf5,0x4e,0x75,
+0x4d,0x10,0xac,0x4e,0xad,0x4d,0xe5,0x4e,0x15,0x4e,0x70,0x02,0x15,0x4d,0xec,0x4d,
+0x60,0x02,0x80,0xee,0x43,0x87,0x01,0x12,0x31,0x08,0x22,0x12,0x30,0x85,0x7c,0x02,
+0x12,0x31,0x14,0x78,0xa7,0xe6,0x54,0xfd,0xf6,0x12,0x31,0x08,0x22,0x12,0x30,0x85,
+0x78,0xa7,0xe6,0x30,0xe0,0x2c,0x78,0xa7,0xe6,0x30,0xe1,0x26,0x78,0xa7,0xe6,0xfc,
+0xf5,0x83,0x18,0xe6,0x44,0xf0,0xfd,0x12,0x1c,0x93,0x90,0xff,0xfc,0xe0,0x44,0x20,
+0xf0,0x7c,0x02,0x12,0x32,0x48,0x78,0xa7,0xe6,0x54,0xfd,0xf6,0x74,0x1a,0x90,0xff,
+0xfe,0xf0,0x78,0xa7,0xe6,0xfc,0xf5,0x83,0x18,0xe6,0x44,0xf1,0xfd,0x12,0x1c,0x93,
+0x12,0x31,0x08,0x22,0x75,0x67,0x00,0x75,0x68,0x00,0xe4,0xf5,0x66,0xf5,0x65,0xe4,
+0xf5,0x69,0x75,0x6a,0x49,0x74,0x84,0x90,0xff,0x82,0xf0,0x74,0x84,0x90,0xff,0x80,
+0xf0,0x74,0x80,0x90,0xff,0x68,0xf0,0x74,0x80,0x90,0xff,0x6a,0xf0,0xad,0x46,0xaf,
+0x45,0x7e,0x00,0xee,0x24,0xfc,0x50,0x03,0x02,0x21,0x6a,0xe4,0xee,0x75,0xf0,0x07,
+0xa4,0x24,0x3f,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xe0,0xff,0xe4,0xef,0x54,0x80,
+0xfd,0xe4,0xef,0x54,0x0f,0x14,0xff,0xed,0x60,0x38,0xe4,0xef,0x75,0xf0,0x08,0xa4,
+0x24,0x48,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0x74,0x90,0xf0,0xe4,0xef,0x75,0xf0,
+0x08,0xa4,0x24,0x4a,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0x74,0x80,0xf0,0xe4,0xef,
+0x75,0xf0,0x08,0xa4,0x24,0x4e,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0x74,0x80,0xf0,
+0x80,0x34,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x08,0xf5,0x82,0xe4,0x34,0xff,0xf5,
+0x83,0x74,0x90,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x0a,0xf5,0x82,0xe4,0x34,
+0xff,0xf5,0x83,0xe4,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x0e,0xf5,0x82,0xe4,
+0x34,0xff,0xf5,0x83,0xe4,0xf0,0x0e,0x02,0x20,0xd3,0x8d,0x46,0x8e,0x44,0x8f,0x45,
+0x74,0x7f,0x90,0xff,0xfd,0xf0,0x74,0x90,0x90,0xff,0xfc,0xf0,0x90,0xfc,0x19,0xe0,
+0x30,0xe6,0x07,0x90,0xff,0xfc,0xe0,0x44,0x04,0xf0,0x22,0x90,0xfc,0x0d,0xe0,0x14,
+0x70,0x04,0x90,0xfc,0x0c,0xe0,0x70,0x39,0x90,0xfc,0x00,0x79,0x06,0x7a,0x35,0x7b,
+0x12,0x78,0x01,0x12,0x03,0xf5,0x7f,0x00,0xef,0x33,0x40,0x15,0xef,0x90,0x35,0x4d,
+0x93,0xfc,0xef,0x24,0x80,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xec,0xf0,0x0f,0x80,
+0xe7,0x8f,0x59,0x90,0xfc,0x2b,0x79,0x18,0x7a,0x35,0x7b,0x35,0x78,0x01,0x12,0x03,
+0xf5,0xe4,0x90,0xff,0xff,0xf0,0x74,0x51,0x90,0xff,0xfa,0xf0,0x74,0x04,0x90,0xff,
+0xfb,0xf0,0x74,0x53,0x90,0xff,0xf8,0xf0,0x74,0x51,0x90,0xff,0xf9,0xf0,0x74,0x55,
+0x90,0xff,0xf7,0xf0,0x74,0x93,0x90,0xff,0xf6,0xf0,0x74,0x32,0x90,0xff,0xf5,0xf0,
+0x75,0x64,0x3d,0x75,0x63,0xfd,0x75,0x62,0x01,0xe4,0x90,0xff,0x83,0xf0,0x74,0x80,
+0x90,0xff,0x81,0xf0,0x75,0x58,0x04,0xe5,0x58,0x75,0xf0,0x07,0xa4,0x24,0x3f,0xf5,
+0x82,0xe4,0x34,0xfc,0xf5,0x83,0xe0,0x78,0x89,0xf6,0xfc,0x54,0x0f,0x14,0xfc,0x78,
+0x89,0xec,0xf6,0xe5,0x58,0x75,0xf0,0x07,0xa4,0x24,0x41,0xf5,0x82,0xe4,0x34,0xfc,
+0xf5,0x83,0xe0,0x78,0x8c,0x76,0xf8,0x08,0x76,0x00,0xfc,0x78,0x89,0xe6,0x75,0xf0,
+0x08,0xa4,0x24,0x48,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0x78,0x89,0xe6,
+0x75,0xf0,0x08,0xa4,0x24,0x4f,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xec,0xf0,0x78,
+0x8c,0xe6,0xff,0x08,0xe6,0x7e,0x03,0xcf,0xc3,0x13,0xcf,0x13,0xde,0xf9,0xfe,0x78,
+0x89,0xe6,0x75,0xf0,0x08,0xa4,0x24,0x49,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xee,
+0xf0,0x78,0x89,0xe6,0x75,0xf0,0x08,0xa4,0x24,0x4a,0xf5,0x82,0xe4,0x34,0xff,0xf5,
+0x83,0x74,0x80,0xf0,0x78,0x8a,0xec,0xf6,0x7d,0x00,0x78,0x8d,0xe6,0x2c,0xf6,0x18,
+0xe6,0x3d,0xf6,0x78,0x8c,0xe6,0xfd,0x08,0xe6,0x7c,0x03,0xcd,0xc3,0x13,0xcd,0x13,
+0xdc,0xf9,0xfc,0x78,0x89,0xe6,0x75,0xf0,0x08,0xa4,0x24,0x4d,0xf5,0x82,0xe4,0x34,
+0xff,0xf5,0x83,0xec,0xf0,0x78,0x89,0xe6,0x75,0xf0,0x08,0xa4,0x24,0x4e,0xf5,0x82,
+0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0x78,0x8c,0xe6,0xfd,0x08,0xe6,0xfc,0x78,0x89,
+0xe6,0xff,0x7e,0x00,0xee,0x24,0xfc,0x50,0x03,0x02,0x24,0x6b,0xe4,0xee,0x75,0xf0,
+0x07,0xa4,0x24,0x3f,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xe0,0xff,0xe4,0xef,0x54,
+0x80,0xfa,0xe4,0xef,0x54,0x0f,0x14,0xff,0xe4,0xee,0x75,0xf0,0x07,0xa4,0x24,0x41,
+0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xe0,0x78,0x8a,0xf6,0xee,0x75,0xf0,0x80,0xa4,
+0x24,0x08,0xf8,0xe5,0xf0,0x34,0xf8,0xf9,0xe8,0xfc,0xe9,0xfd,0x8a,0x59,0xea,0x70,
+0x03,0x02,0x23,0xd8,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x48,0xf5,0x82,0xe4,0x34,
+0xff,0xf5,0x83,0xe4,0xf0,0x78,0x8a,0xe6,0xfa,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,
+0x4f,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xea,0xf0,0xed,0xfb,0xec,0x7a,0x03,0xcb,
+0xc3,0x13,0xcb,0x13,0xda,0xf9,0xfa,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x49,0xf5,
+0x82,0xe4,0x34,0xff,0xf5,0x83,0xea,0xf0,0x78,0x8a,0xe6,0x7b,0x00,0xfa,0xec,0x2a,
+0xfc,0xed,0x3b,0xfd,0xfb,0xec,0x7a,0x03,0xcb,0xc3,0x13,0xcb,0x13,0xda,0xf9,0xfa,
+0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x4d,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xea,
+0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x4a,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,
+0x74,0x80,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x4e,0xf5,0x82,0xe4,0x34,0xff,
+0xf5,0x83,0x74,0x80,0xf0,0x02,0x24,0x67,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x08,
+0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0x78,0x8a,0xe6,0xfa,0xe4,0xef,0x75,
+0xf0,0x08,0xa4,0x24,0x0f,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xea,0xf0,0xed,0xfb,
+0xec,0x7a,0x03,0xcb,0xc3,0x13,0xcb,0x13,0xda,0xf9,0xfa,0xe4,0xef,0x75,0xf0,0x08,
+0xa4,0x24,0x09,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xea,0xf0,0x78,0x8a,0xe6,0x7b,
+0x00,0xfa,0xec,0x2a,0xfc,0xed,0x3b,0xfd,0xfb,0xec,0x7a,0x03,0xcb,0xc3,0x13,0xcb,
+0x13,0xda,0xf9,0xfa,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x0d,0xf5,0x82,0xe4,0x34,
+0xff,0xf5,0x83,0xea,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x0a,0xf5,0x82,0xe4,
+0x34,0xff,0xf5,0x83,0xe4,0xf0,0xe4,0xef,0x75,0xf0,0x08,0xa4,0x24,0x0e,0xf5,0x82,
+0xe4,0x34,0xff,0xf5,0x83,0xe4,0xf0,0x0e,0x02,0x22,0xf4,0x8e,0x58,0x78,0x8c,0xed,
+0xf6,0x08,0xec,0xf6,0x78,0x89,0xef,0xf6,0x12,0x20,0xa4,0x22,0x8c,0x26,0xec,0x30,
+0xe7,0x18,0xe5,0x26,0x54,0x0f,0x14,0x75,0xf0,0x08,0xa4,0x24,0x48,0xf5,0x82,0xe4,
+0x34,0xff,0xf5,0x83,0xe0,0x54,0xdf,0xf0,0x80,0x16,0xe5,0x26,0x54,0x0f,0x14,0x75,
+0xf0,0x08,0xa4,0x24,0x08,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x54,0xdf,0xf0,
+0x22,0xec,0x90,0xfd,0x3f,0xf0,0x8c,0x24,0xed,0x24,0x03,0xf5,0x25,0x7d,0x00,0xd3,
+0x95,0x6c,0xed,0x95,0x6b,0x40,0x03,0x85,0x6c,0x25,0xe5,0x25,0x24,0xb7,0x50,0x09,
+0x75,0x25,0x03,0x74,0x02,0x90,0xfd,0x3f,0xf0,0xac,0x25,0x12,0x2f,0xc3,0x22,0xe4,
+0xf5,0x66,0xf5,0x65,0x12,0x24,0xe8,0x22,0x90,0xfd,0x3d,0xe0,0x65,0x6d,0x60,0x0e,
+0x74,0x04,0x90,0xfd,0x3f,0xf0,0xe4,0xf5,0x65,0x75,0x66,0x03,0x80,0x46,0x7d,0x6d,
+0xe4,0xfe,0xff,0x79,0x3d,0x7a,0xfd,0x7b,0x01,0x74,0x05,0x78,0x00,0x12,0x03,0x3f,
+0xe5,0x66,0x24,0x03,0xf5,0x66,0xe5,0x65,0x34,0x00,0xf5,0x65,0xe5,0x66,0xd3,0x95,
+0x6c,0xe5,0x65,0x95,0x6b,0x40,0x06,0x85,0x6c,0x66,0x85,0x6b,0x65,0xd3,0xe5,0x66,
+0x94,0x48,0xe5,0x65,0x94,0x00,0x40,0x0c,0x74,0x02,0x90,0xfd,0x3f,0xf0,0xe4,0xf5,
+0x65,0x75,0x66,0x03,0xac,0x66,0x12,0x2f,0xc3,0x22,0xec,0x90,0xfd,0x3f,0xf0,0xe4,
+0xf5,0x66,0xf5,0x65,0x8c,0x32,0xec,0x60,0x05,0x12,0x2f,0xb4,0x80,0x05,0x7c,0x00,
+0x12,0x2f,0xc3,0x22,0x90,0xff,0x04,0xe0,0xf5,0x4a,0x90,0xff,0x06,0xe0,0xfd,0xa3,
+0xe0,0xed,0x7d,0x00,0xfc,0x7d,0x00,0xfc,0x90,0xff,0x06,0xe0,0xff,0xa3,0xe0,0x7e,
+0x00,0xff,0xe4,0xfe,0xec,0x4e,0xfc,0xed,0x4f,0xfd,0xc3,0xec,0x94,0x48,0xed,0x94,
+0x00,0x50,0x22,0x90,0xff,0x06,0xe0,0xfd,0xa3,0xe0,0xed,0x7d,0x00,0xfc,0x7d,0x00,
+0xfc,0x90,0xff,0x06,0xe0,0xff,0xa3,0xe0,0x7e,0x00,0xff,0xe4,0xfe,0xec,0x4e,0xfc,
+0xed,0x4f,0xfd,0x80,0x04,0xe4,0xfd,0x7c,0x48,0x8c,0x6c,0x8d,0x6b,0x90,0xff,0x02,
+0xe0,0xfd,0xa3,0xe0,0xed,0x7d,0x00,0xfc,0x7d,0x00,0xfc,0x90,0xff,0x02,0xe0,0xff,
+0xa3,0xe0,0x7e,0x00,0xff,0xe4,0xfe,0xec,0x4e,0xf5,0x4c,0xed,0x4f,0xf5,0x4b,0x75,
+0x64,0x3d,0x75,0x63,0xfd,0x75,0x62,0x01,0x7d,0x3d,0x7e,0xfd,0x7f,0x01,0x79,0x6d,
+0xe4,0xfa,0xfb,0x74,0x05,0x78,0x00,0x12,0x03,0x3f,0x75,0x49,0x00,0xe5,0x49,0x24,
+0xfe,0x40,0x19,0xad,0x64,0xae,0x63,0xaf,0x62,0xe4,0x12,0x03,0x0f,0x05,0x49,0x0d,
+0xed,0x70,0x01,0x0e,0x8d,0x64,0x8e,0x63,0x8f,0x62,0x80,0xe1,0x75,0x64,0x3d,0x75,
+0x63,0xfd,0x75,0x62,0x01,0x90,0xff,0x00,0xe0,0x54,0x60,0xb4,0x00,0x02,0x80,0x06,
+0xd3,0x50,0x03,0x02,0x2c,0x12,0xe5,0x4a,0x54,0x0f,0xf5,0x49,0xe5,0x4a,0x54,0x80,
+0xa2,0xe0,0x92,0x02,0x90,0xff,0x01,0xe0,0x12,0x01,0x81,0x00,0x0b,0x2c,0x0d,0x26,
+0x67,0x27,0x85,0x2c,0x0d,0x28,0x91,0x2c,0x0d,0x29,0x74,0x29,0xa8,0x2b,0x0f,0x2b,
+0x12,0x2b,0x52,0x2b,0xb6,0x2b,0xe4,0xe5,0x67,0x30,0xe7,0x0e,0xe5,0x4c,0x45,0x4b,
+0x70,0x08,0xe5,0x6c,0x64,0x02,0x45,0x6b,0x60,0x03,0x02,0x2c,0x0f,0x90,0xff,0x00,
+0xe0,0x54,0x1f,0xb4,0x00,0x02,0x80,0x03,0xd3,0x40,0x29,0xe5,0x4a,0x60,0x03,0x02,
+0x27,0x82,0xad,0x64,0xae,0x63,0xaf,0x62,0x74,0x01,0x12,0x03,0x0f,0x78,0xa7,0xe6,
+0x30,0xe0,0x0b,0xad,0x64,0xae,0x63,0xaf,0x62,0x74,0x02,0x12,0x03,0x0f,0x7c,0x02,
+0x12,0x2f,0xc3,0x22,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x1b,0xe5,0x67,0x20,0xe1,
+0x07,0xe5,0x4a,0x60,0x03,0x02,0x27,0x82,0xe5,0x4a,0x24,0xfe,0x50,0x03,0x02,0x27,
+0x82,0x7c,0x02,0x12,0x2f,0xc3,0x22,0xb4,0x02,0x02,0x80,0x06,0xd3,0x50,0x03,0x02,
+0x27,0x80,0xe5,0x67,0x20,0xe1,0x0d,0xe5,0x4a,0x60,0x09,0xe5,0x4a,0x64,0x80,0x60,
+0x03,0x02,0x27,0x82,0xac,0x4a,0x12,0x30,0x4a,0x40,0x03,0x02,0x27,0x82,0xe5,0x49,
+0x70,0x25,0x30,0x02,0x11,0x90,0xff,0x80,0xe0,0x54,0x08,0xad,0x64,0xae,0x63,0xaf,
+0x62,0x12,0x03,0x0f,0x80,0x0f,0x90,0xff,0x82,0xe0,0x54,0x08,0xad,0x64,0xae,0x63,
+0xaf,0x62,0x12,0x03,0x0f,0x80,0x3d,0x15,0x49,0x30,0x02,0x1d,0xe5,0x49,0x75,0xf0,
+0x08,0xa4,0x24,0x48,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x54,0x08,0xad,0x64,
+0xae,0x63,0xaf,0x62,0x12,0x03,0x0f,0x80,0x1b,0xe5,0x49,0x75,0xf0,0x08,0xa4,0x24,
+0x08,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x54,0x08,0xad,0x64,0xae,0x63,0xaf,
+0x62,0x12,0x03,0x0f,0xad,0x64,0xae,0x63,0xaf,0x62,0x12,0x01,0xe6,0x60,0x0b,0xad,
+0x64,0xae,0x63,0xaf,0x62,0x74,0x01,0x12,0x03,0x0f,0x7c,0x02,0x12,0x2f,0xc3,0x22,
+0x80,0x00,0x02,0x2c,0x0f,0xe5,0x67,0x20,0xe7,0x06,0xe5,0x6c,0x45,0x6b,0x60,0x03,
+0x02,0x2c,0x0f,0x90,0xff,0x00,0xe0,0x54,0x1f,0xb4,0x00,0x02,0x80,0x03,0xd3,0x40,
+0x1a,0xe5,0x4c,0x14,0x45,0x4b,0x70,0x04,0xe5,0x4a,0x60,0x03,0x02,0x28,0x8e,0x78,
+0xa7,0xe6,0x54,0xfe,0xf6,0x7c,0x00,0x12,0x2f,0xc3,0x22,0xb4,0x01,0x02,0x80,0x03,
+0xd3,0x40,0x2a,0xe5,0x67,0x20,0xe1,0x08,0xe5,0x67,0x20,0xe0,0x03,0x02,0x28,0x8e,
+0xe5,0x67,0x30,0xe0,0x04,0xe5,0x4a,0x70,0x0b,0xe5,0x67,0x30,0xe1,0x09,0xe5,0x4a,
+0x24,0xfe,0x50,0x03,0x02,0x28,0x8e,0x7c,0x00,0x12,0x2f,0xc3,0x22,0xb4,0x02,0x02,
+0x80,0x06,0xd3,0x50,0x03,0x02,0x28,0x8c,0xe5,0x4c,0x45,0x4b,0x60,0x03,0x02,0x28,
+0x8e,0xac,0x4a,0x12,0x30,0x4a,0x40,0x03,0x02,0x28,0x8e,0xe5,0x67,0x20,0xe1,0x07,
+0xe5,0x67,0x20,0xe0,0x02,0x80,0x77,0xe5,0x67,0x30,0xe0,0x06,0xe5,0x49,0x60,0x02,
+0x80,0x6c,0xe5,0x49,0x70,0x0f,0x90,0xff,0x82,0xe0,0x54,0xf7,0xf0,0x90,0xff,0x80,
+0xe0,0x54,0xf7,0xf0,0x22,0xe5,0x49,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x09,0x7d,
+0x01,0x7c,0x03,0x12,0x0f,0x0b,0x80,0x11,0xb4,0x02,0x02,0x80,0x03,0xd3,0x40,0x09,
+0x7d,0x01,0x7c,0x04,0x12,0x0f,0x0b,0x80,0x00,0x15,0x49,0x30,0x02,0x15,0xe5,0x49,
+0x75,0xf0,0x08,0xa4,0x24,0x48,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x54,0xf7,
+0xf0,0x80,0x13,0xe5,0x49,0x75,0xf0,0x08,0xa4,0x24,0x08,0xf5,0x82,0xe4,0x34,0xff,
+0xf5,0x83,0xe0,0x54,0xf7,0xf0,0x7c,0x00,0x12,0x2f,0xc3,0x22,0x80,0x00,0x02,0x2c,
+0x0f,0xe5,0x67,0x20,0xe7,0x06,0xe5,0x6c,0x45,0x6b,0x60,0x03,0x02,0x2c,0x0f,0x90,
+0xff,0x00,0xe0,0x54,0x1f,0xb4,0x00,0x02,0x80,0x03,0xd3,0x40,0x1a,0xe5,0x4c,0x14,
+0x45,0x4b,0x70,0x04,0xe5,0x4a,0x60,0x03,0x02,0x29,0x71,0x78,0xa7,0xe6,0x44,0x01,
+0xf6,0x7c,0x00,0x12,0x2f,0xc3,0x22,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x29,0xe5,
+0x67,0x20,0xe1,0x08,0xe5,0x67,0x20,0xe0,0x03,0x02,0x29,0x71,0xe5,0x67,0x30,0xe0,
+0x04,0xe5,0x49,0x70,0x0b,0xe5,0x67,0x30,0xe1,0x08,0xe5,0x49,0x24,0xfe,0x50,0x02,
+0x80,0x7f,0x7c,0x00,0x12,0x2f,0xc3,0x22,0xb4,0x02,0x02,0x80,0x03,0xd3,0x40,0x6f,
+0xe5,0x4c,0x45,0x4b,0x60,0x02,0x80,0x69,0xac,0x4a,0x12,0x30,0x4a,0x40,0x02,0x80,
+0x60,0xe5,0x67,0x20,0xe1,0x07,0xe5,0x67,0x20,0xe0,0x02,0x80,0x54,0xe5,0x49,0x70,
+0x14,0x30,0x02,0x09,0x90,0xff,0x80,0xe0,0x44,0x08,0xf0,0x80,0x07,0x90,0xff,0x82,
+0xe0,0x44,0x08,0xf0,0x22,0xe5,0x67,0x30,0xe1,0x33,0x15,0x49,0x30,0x02,0x15,0xe5,
+0x49,0x75,0xf0,0x08,0xa4,0x24,0x48,0xf5,0x82,0xe4,0x34,0xff,0xf5,0x83,0xe0,0x44,
+0x08,0xf0,0x80,0x13,0xe5,0x49,0x75,0xf0,0x08,0xa4,0x24,0x08,0xf5,0x82,0xe4,0x34,
+0xff,0xf5,0x83,0xe0,0x44,0x08,0xf0,0x7c,0x00,0x12,0x2f,0xc3,0x22,0x80,0x02,0x80,
+0x00,0x02,0x2c,0x0f,0xe5,0x67,0x20,0xe7,0x12,0xe5,0x6c,0x45,0x6b,0x70,0x0c,0xe5,
+0x4a,0x70,0x08,0x90,0xff,0x00,0xe0,0x54,0x1f,0x60,0x03,0x02,0x2c,0x0f,0xe5,0x4c,
+0x90,0xff,0xff,0xf0,0x90,0xff,0xff,0xe0,0x60,0x05,0x43,0x67,0x01,0x80,0x03,0x53,
+0x67,0xfe,0x7c,0x00,0x12,0x2f,0xc3,0x22,0xe5,0x67,0x30,0xe7,0x0e,0xe5,0x6c,0x45,
+0x6b,0x60,0x08,0x90,0xff,0x00,0xe0,0x54,0x1f,0x60,0x03,0x02,0x2c,0x0f,0xad,0x4b,
+0xe5,0x4c,0xed,0x7d,0x00,0xfc,0x7d,0x00,0xfc,0xbd,0x00,0x02,0x80,0x03,0x02,0x2b,
+0x0a,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x32,0xe5,0x4a,0x70,0x05,0xe5,0x4c,0xfc,
+0x60,0x03,0x02,0x2b,0x0c,0x75,0x64,0x00,0x75,0x63,0xfc,0x75,0x62,0x01,0xd3,0xe5,
+0x6c,0x94,0x12,0xe5,0x6b,0x94,0x00,0x40,0x06,0xe4,0xfd,0x7c,0x12,0x80,0x04,0xac,
+0x6c,0xad,0x6b,0x8c,0x6a,0x8d,0x69,0x12,0x2f,0xd8,0x22,0xb4,0x02,0x02,0x80,0x03,
+0xd3,0x40,0x59,0xe5,0x4a,0x60,0x03,0x02,0x2b,0x0c,0xe5,0x4c,0xfc,0x70,0x27,0x75,
+0x64,0x12,0x75,0x63,0xfc,0x75,0x62,0x01,0xd3,0xe5,0x6c,0x94,0x19,0xe5,0x6b,0x94,
+0x00,0x40,0x06,0xe4,0xfd,0x7c,0x19,0x80,0x04,0xac,0x6c,0xad,0x6b,0x8c,0x6a,0x8d,
+0x69,0x12,0x2f,0xd8,0x80,0x25,0x75,0x64,0x2b,0x75,0x63,0xfc,0x75,0x62,0x01,0xd3,
+0xe5,0x6c,0x94,0x35,0xe5,0x6b,0x94,0x00,0x40,0x06,0xe4,0xfd,0x7c,0x35,0x80,0x04,
+0xac,0x6c,0xad,0x6b,0x8c,0x6a,0x8d,0x69,0x12,0x2f,0xd8,0x22,0xb4,0x03,0x02,0x80,
+0x06,0xd3,0x50,0x03,0x02,0x2b,0x0a,0xe5,0x4c,0xf5,0x49,0x70,0x0f,0x90,0xff,0x04,
+0xe0,0xfd,0xa3,0xe0,0x4d,0x60,0x03,0x02,0x2b,0x0c,0x80,0x18,0x90,0xfc,0x82,0xe0,
+0xfd,0xa3,0xe0,0xfc,0x90,0xff,0x05,0xe0,0x6c,0x70,0x07,0x90,0xff,0x04,0xe0,0x6d,
+0x60,0x02,0x80,0x68,0xe4,0xf5,0x6a,0xf5,0x69,0x7f,0x00,0xe5,0x49,0x14,0xc5,0x49,
+0x60,0x0f,0xef,0x24,0x80,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xe0,0x2f,0xff,0x80,
+0xea,0x8f,0x4a,0xe5,0x4a,0x24,0x80,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xe0,0x7d,
+0x00,0xd3,0x95,0x6c,0xed,0x95,0x6b,0x40,0x06,0xac,0x6c,0xad,0x6b,0x80,0x0f,0xe5,
+0x4a,0x24,0x80,0xf5,0x82,0xe4,0x34,0xfc,0xf5,0x83,0xe0,0x7d,0x00,0xfc,0x8c,0x6a,
+0x8d,0x69,0xe5,0x4a,0x24,0x80,0xfc,0xe4,0x34,0xfc,0xfd,0xfe,0xec,0xfd,0x7f,0x01,
+0x8d,0x64,0x8e,0x63,0x8f,0x62,0x12,0x2f,0xd8,0x22,0x80,0x00,0x02,0x2c,0x0f,0x02,
+0x2c,0x0f,0xe5,0x67,0x30,0xe7,0x19,0xe5,0x6c,0x14,0x45,0x6b,0x70,0x12,0xe5,0x4a,
+0x70,0x0e,0xe5,0x4c,0x45,0x4b,0x70,0x08,0x90,0xff,0x00,0xe0,0x54,0x1f,0x60,0x03,
+0x02,0x2c,0x0f,0xe5,0x67,0x20,0xe0,0x08,0xe5,0x67,0x20,0xe1,0x03,0x02,0x2c,0x0f,
+0x75,0x64,0x68,0xe4,0xf5,0x63,0xf5,0x62,0xe4,0xf5,0x69,0x04,0xf5,0x6a,0x12,0x2f,
+0xd8,0x22,0xe5,0x67,0x20,0xe7,0x27,0xe5,0x6c,0x45,0x6b,0x70,0x21,0xe5,0x4a,0x70,
+0x1d,0xe5,0x4c,0x64,0x02,0x45,0x4b,0x60,0x0d,0xe5,0x4c,0x14,0x45,0x4b,0x60,0x06,
+0xe5,0x4c,0x45,0x4b,0x70,0x08,0x90,0xff,0x00,0xe0,0x54,0x1f,0x60,0x03,0x02,0x2c,
+0x0f,0xe5,0x67,0x20,0xe0,0x08,0xe5,0x67,0x20,0xe1,0x03,0x02,0x2c,0x0f,0x85,0x4c,
+0x68,0xe5,0x68,0x70,0x08,0x43,0x67,0x01,0x53,0x67,0xfd,0x80,0x13,0xe5,0x68,0x64,
+0x02,0x60,0x07,0xe5,0x68,0x14,0x60,0x02,0x80,0x65,0x53,0x67,0xfe,0x43,0x67,0x02,
+0x7c,0x00,0x12,0x2f,0xc3,0x22,0xe5,0x67,0x30,0xe7,0x1a,0xe5,0x6c,0x14,0x45,0x6b,
+0x70,0x13,0xe5,0x4a,0x70,0x0f,0xe5,0x4c,0x45,0x4b,0x70,0x09,0x90,0xff,0x00,0xe0,
+0x54,0x1f,0x14,0x60,0x02,0x80,0x38,0xe5,0x67,0x20,0xe1,0x02,0x80,0x31,0x7c,0x01,
+0x12,0x2f,0xc3,0x22,0xe5,0x67,0x20,0xe7,0x15,0xe5,0x6c,0x45,0x6b,0x70,0x0f,0xe5,
+0x4c,0x45,0x4b,0x70,0x09,0x90,0xff,0x00,0xe0,0x54,0x1f,0x14,0x60,0x02,0x80,0x0f,
+0xe5,0x67,0x20,0xe1,0x02,0x80,0x08,0x7c,0x00,0x12,0x2f,0xc3,0x22,0x80,0x00,0x02,
+0x2e,0xca,0xb4,0x40,0x02,0x80,0x06,0xd3,0x50,0x03,0x02,0x2e,0xc0,0x90,0xff,0x01,
+0xe0,0x90,0xfd,0x3d,0xf0,0xe5,0x4a,0x90,0xfd,0x3e,0xf0,0xe4,0x90,0xfd,0x3f,0xf0,
+0xe5,0x64,0x24,0x03,0xf5,0x64,0xe5,0x63,0x34,0x00,0xf5,0x63,0xad,0x4b,0xe5,0x4c,
+0x85,0x64,0x82,0x85,0x63,0x83,0xcd,0xf0,0xa3,0xcd,0xf0,0x90,0xff,0x01,0xe0,0x12,
+0x01,0xb7,0x2c,0x7d,0x01,0x2c,0xa3,0x02,0x2c,0xcd,0x03,0x2c,0xf7,0x04,0x2d,0x45,
+0x05,0x2d,0x82,0x06,0x2d,0xa8,0x07,0x2d,0xce,0x08,0x2d,0xf4,0x09,0x2e,0x1a,0x0b,
+0x2e,0x40,0x0c,0x2e,0x4f,0x80,0x2e,0x4f,0x81,0x00,0x00,0x2e,0xad,0xe5,0x67,0x20,
+0xe7,0x06,0x7c,0x05,0x12,0x25,0x4a,0x22,0x7d,0xb7,0x7e,0x34,0x7f,0x02,0x79,0x40,
+0x7a,0xfd,0x7b,0x01,0x74,0x08,0x78,0x00,0x12,0x03,0x3f,0x7d,0x08,0x7c,0x00,0x12,
+0x24,0xb1,0x22,0xe5,0x67,0x20,0xe7,0x06,0x7c,0x05,0x12,0x25,0x4a,0x22,0xe5,0x4a,
+0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,0x4a,0x7f,0x00,0xfe,0x7c,
+0x10,0x12,0x31,0x9a,0x22,0x7d,0x00,0x7c,0x07,0x12,0x24,0xb1,0x22,0xe5,0x67,0x20,
+0xe7,0x06,0x7c,0x05,0x12,0x25,0x4a,0x22,0xe5,0x4a,0xb4,0x03,0x00,0x40,0x10,0xb4,
+0x05,0x00,0x50,0x0b,0xe5,0x4a,0x7f,0x00,0xfe,0x7c,0x11,0x12,0x31,0x9a,0x22,0x7d,
+0x00,0x7c,0x07,0x12,0x24,0xb1,0x22,0xe5,0x67,0x20,0xe7,0x06,0x7c,0x05,0x12,0x25,
+0x4a,0x22,0xe5,0x4a,0xb4,0x05,0x02,0x80,0x03,0xd3,0x40,0x0a,0xe4,0xff,0x04,0xfe,
+0x7c,0x0a,0x12,0x31,0x9a,0x22,0xb4,0x01,0x02,0x80,0x03,0xd3,0x40,0x0a,0xe4,0xff,
+0x04,0xfe,0x7c,0x08,0x12,0x31,0x9a,0x22,0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,0x00,
+0x50,0x0b,0xe5,0x4a,0x7f,0x00,0xfe,0x7c,0x13,0x12,0x31,0x9a,0x22,0x7d,0x00,0x7c,
+0x07,0x12,0x24,0xb1,0x22,0xe5,0x67,0x20,0xe7,0x34,0xd3,0xe5,0x6c,0x94,0x48,0xe5,
+0x6b,0x94,0x00,0x50,0x06,0xe5,0x6c,0x45,0x6b,0x70,0x06,0x7c,0x02,0x12,0x25,0x4a,
+0x22,0xe5,0x4a,0xb4,0x01,0x03,0xb3,0x40,0x0b,0xc3,0xb4,0x03,0x00,0x40,0x09,0xb4,
+0x06,0x00,0x50,0x04,0x12,0x30,0x70,0x22,0x7c,0x07,0x12,0x25,0x4a,0x22,0x12,0x24,
+0xe8,0x22,0xe5,0x67,0x20,0xe7,0x1d,0xe5,0x4a,0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,
+0x00,0x50,0x0b,0xe5,0x4a,0x7f,0x00,0xfe,0x7c,0x16,0x12,0x31,0x9a,0x22,0x7c,0x07,
+0x12,0x25,0x4a,0x22,0x12,0x24,0xe8,0x22,0xe5,0x67,0x20,0xe7,0x1d,0xe5,0x4a,0xb4,
+0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,0x4a,0x7f,0x00,0xfe,0x7c,0x19,
+0x12,0x31,0x9a,0x22,0x7c,0x07,0x12,0x25,0x4a,0x22,0x12,0x24,0xe8,0x22,0xe5,0x67,
+0x20,0xe7,0x1d,0xe5,0x4a,0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,
+0x4a,0x7f,0x00,0xfe,0x7c,0x17,0x12,0x31,0x9a,0x22,0x7c,0x07,0x12,0x25,0x4a,0x22,
+0x12,0x24,0xe8,0x22,0xe5,0x67,0x20,0xe7,0x1d,0xe5,0x4a,0xb4,0x03,0x00,0x40,0x10,
+0xb4,0x05,0x00,0x50,0x0b,0xe5,0x4a,0x7f,0x00,0xfe,0x7c,0x18,0x12,0x31,0x9a,0x22,
+0x7c,0x07,0x12,0x25,0x4a,0x22,0x12,0x24,0xe8,0x22,0xe5,0x67,0x20,0xe7,0x1d,0xe5,
+0x4a,0xb4,0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,0x4a,0x7f,0x00,0xfe,
+0x7c,0x15,0x12,0x31,0x9a,0x22,0x7c,0x07,0x12,0x25,0x4a,0x22,0x12,0x24,0xe8,0x22,
+0xe5,0x67,0x20,0xe7,0x06,0x7c,0x07,0x12,0x25,0x4a,0x22,0x12,0x24,0xe8,0x22,0xe5,
+0x67,0x30,0xe7,0x20,0x90,0xff,0x00,0xe0,0x54,0x1f,0x70,0x10,0x90,0xff,0x01,0xe0,
+0xb4,0x80,0x05,0x12,0x24,0xdf,0x80,0x03,0x12,0x24,0xe8,0x22,0x7d,0x00,0x7c,0x05,
+0x12,0x24,0xb1,0x22,0x90,0xff,0x00,0xe0,0x54,0x1f,0x60,0x06,0x7c,0x05,0x12,0x25,
+0x4a,0x22,0xd3,0xe5,0x6c,0x94,0x48,0xe5,0x6b,0x94,0x00,0x50,0x0b,0xc3,0xe5,0x6c,
+0x94,0x07,0xe5,0x6b,0x94,0x00,0x50,0x06,0x7c,0x03,0x12,0x25,0x4a,0x22,0xe5,0x4a,
+0xb4,0x05,0x04,0x12,0x30,0x70,0x22,0x7c,0x07,0x12,0x25,0x4a,0x22,0xe5,0x67,0x30,
+0xe7,0x08,0x7d,0x00,0x7c,0x05,0x12,0x24,0xb1,0x22,0x7c,0x05,0x12,0x25,0x4a,0x22,
+0xb4,0x20,0x02,0x80,0x03,0xd3,0x40,0x00,0x80,0x00,0x12,0x2f,0x9e,0x22,0x75,0x43,
+0x00,0x90,0xff,0x83,0xe0,0x54,0x0f,0xd3,0x95,0x43,0x40,0x24,0xe5,0x43,0x24,0xf0,
+0xf5,0x82,0xe4,0x34,0xfe,0xf5,0x83,0xe0,0xad,0x64,0xae,0x63,0xaf,0x62,0x12,0x03,
+0x0f,0x05,0x43,0x0d,0xed,0x70,0x01,0x0e,0x8d,0x64,0x8e,0x63,0x8f,0x62,0x80,0xd1,
+0xe5,0x43,0x7d,0x00,0xfc,0xc3,0xe5,0x6a,0x9c,0xf5,0x6a,0xe5,0x69,0x9d,0xf5,0x69,
+0xe5,0x6a,0x45,0x69,0x60,0x06,0xe4,0x90,0xff,0x83,0xf0,0x22,0x90,0xff,0x82,0xe0,
+0x44,0x08,0xf0,0xe4,0xf5,0x69,0x75,0x6a,0x49,0x90,0xfd,0x3d,0xe0,0xb4,0x05,0x02,
+0x80,0x03,0xd3,0x40,0x40,0x90,0xfd,0x3e,0xe0,0xf5,0x43,0xb4,0x05,0x02,0x80,0x03,
+0xd3,0x40,0x0a,0xe4,0xff,0x04,0xfe,0x7c,0x0b,0x12,0x31,0x9a,0x22,0xb4,0x01,0x02,
+0x80,0x03,0xd3,0x40,0x0a,0xe4,0xff,0x04,0xfe,0x7c,0x09,0x12,0x31,0x9a,0x22,0xb4,
+0x03,0x00,0x40,0x10,0xb4,0x05,0x00,0x50,0x0b,0xe5,0x43,0x7f,0x00,0xfe,0x7c,0x14,
+0x12,0x31,0x9a,0x22,0x22,0xb4,0x80,0x00,0x40,0x23,0xb4,0x82,0x00,0x50,0x1e,0x7c,
+0x3d,0x7d,0xfd,0x12,0x17,0xd5,0x7d,0x00,0x8c,0x66,0x8d,0x65,0x90,0xfd,0x3f,0xe0,
+0x60,0x05,0x12,0x2f,0x9e,0x80,0x05,0x7c,0x00,0x12,0x2f,0xc3,0x22,0x22,0x90,0xff,
+0x83,0xe0,0x54,0x7f,0xf0,0x90,0xff,0x82,0xe0,0x44,0x08,0xf0,0x90,0xff,0x80,0xe0,
+0x44,0x08,0xf0,0x22,0x90,0xff,0x82,0xe0,0x44,0x08,0xf0,0x90,0xff,0x80,0xe0,0x44,
+0x08,0xf0,0x22,0x8c,0x23,0x7d,0x00,0x8c,0x6a,0x8d,0x69,0x75,0x64,0x3d,0x75,0x63,
+0xfd,0x75,0x62,0x01,0x12,0x2f,0xd8,0x22,0x90,0xff,0x83,0xe0,0x54,0x7f,0xf0,0xe5,
+0x6a,0x64,0x49,0x45,0x69,0x70,0x01,0x22,0xc3,0xe5,0x6a,0x94,0x08,0xe5,0x69,0x94,
+0x00,0x40,0x15,0x75,0x21,0x08,0xe5,0x21,0x7d,0x00,0xfc,0xc3,0xe5,0x6a,0x9c,0xf5,
+0x6a,0xe5,0x69,0x9d,0xf5,0x69,0x80,0x09,0x85,0x6a,0x21,0xe4,0xf5,0x69,0x75,0x6a,
+0x49,0x75,0x22,0x00,0xe5,0x22,0xc3,0x95,0x21,0x50,0x26,0xad,0x64,0xae,0x63,0xaf,
+0x62,0x12,0x01,0xe6,0xfc,0xe5,0x22,0x24,0xf8,0xf5,0x82,0xe4,0x34,0xfe,0xf5,0x83,
+0xec,0xf0,0x05,0x22,0x0d,0xed,0x70,0x01,0x0e,0x8d,0x64,0x8e,0x63,0x8f,0x62,0x80,
+0xd3,0xe5,0x21,0x54,0x7f,0x90,0xff,0x81,0xf0,0x22,0x8c,0x48,0x7f,0x00,0xef,0x24,
+0xfb,0x40,0x19,0xe4,0xef,0x75,0xf0,0x07,0xa4,0x24,0x3f,0xf5,0x82,0xe4,0x34,0xfc,
+0xf5,0x83,0xe0,0x65,0x48,0x70,0x02,0xd3,0x22,0x0f,0x80,0xe2,0x8f,0x47,0xc3,0x22,
+0x85,0x6c,0x6a,0x85,0x6b,0x69,0x90,0xff,0x82,0xe0,0x54,0xf7,0xf0,0x90,0xff,0x83,
+0xe0,0x54,0x7f,0xf0,0x22,0xc0,0x00,0xc0,0x01,0xc0,0x02,0xc0,0x06,0xc0,0x07,0xe5,
+0x72,0x24,0x08,0xf8,0x86,0x06,0x53,0x06,0x7f,0x7c,0xff,0x12,0x30,0xfa,0x7c,0x00,
+0x7d,0x00,0xe5,0x75,0x60,0x46,0xff,0x90,0xfe,0x9d,0xe0,0x54,0x7f,0x6e,0x70,0x0f,
+0xc0,0x83,0xc0,0x82,0xa3,0xe0,0xfd,0xa3,0xe0,0xfc,0xa3,0x15,0x75,0x80,0x07,0xa3,
+0xa3,0xa3,0xdf,0xe6,0x80,0x26,0xdf,0x06,0xd0,0x82,0xd0,0x83,0x80,0x1e,0xe0,0xf8,
+0xa3,0xe0,0xf9,0xa3,0xe0,0xfa,0xd0,0x82,0xd0,0x83,0xe8,0xf0,0xa3,0xe9,0xf0,0xa3,
+0xea,0xf0,0xa3,0xc0,0x83,0xc0,0x82,0xa3,0xa3,0xa3,0x80,0xda,0x12,0x31,0x93,0xd0,
+0x07,0xd0,0x06,0xd0,0x02,0xd0,0x01,0xd0,0x00,0x22,0x85,0xa8,0x74,0x75,0xa8,0x88,
+0xec,0x70,0x02,0x7c,0x3f,0x8c,0x73,0x22,0xe5,0x72,0x24,0x08,0xf8,0x76,0x00,0x12,
+0x31,0xe7,0x80,0xfb,0xc0,0x00,0xc0,0x01,0xc0,0x02,0xc0,0x06,0xc0,0x07,0xae,0x04,
+0x7c,0xff,0x12,0x30,0xfa,0xe5,0x75,0x60,0x42,0xff,0x90,0xfe,0x9d,0xe0,0x54,0x7f,
+0x6e,0x70,0x0b,0xc0,0x83,0xc0,0x82,0xa3,0xa3,0xa3,0x15,0x75,0x80,0x07,0xa3,0xa3,
+0xa3,0xdf,0xea,0x80,0x26,0xdf,0x06,0xd0,0x82,0xd0,0x83,0x80,0xd8,0xe0,0xf8,0xa3,
+0xe0,0xf9,0xa3,0xe0,0xfa,0xd0,0x82,0xd0,0x83,0xe8,0xf0,0xa3,0xe9,0xf0,0xa3,0xea,
+0xf0,0xa3,0xc0,0x83,0xc0,0x82,0xa3,0xa3,0xa3,0x80,0xda,0x78,0x08,0x08,0x79,0x18,
+0x09,0x7c,0x01,0xe6,0x54,0x7f,0x6e,0x70,0x06,0x76,0x00,0x77,0x00,0x80,0x06,0x08,
+0x09,0x0c,0xbc,0x08,0xee,0x12,0x31,0x93,0xd0,0x07,0xd0,0x06,0xd0,0x02,0xd0,0x01,
+0xd0,0x00,0x22,0x75,0x73,0x00,0x85,0x74,0xa8,0x22,0xc0,0xf0,0xc0,0x82,0xc0,0x83,
+0xc3,0xe5,0x75,0x24,0xe8,0x50,0x05,0x12,0x31,0xe7,0x80,0xf4,0xec,0x60,0x31,0x90,
+0x34,0xb6,0xe4,0x93,0xc3,0x9c,0x40,0x28,0xc0,0x04,0x7c,0xff,0x12,0x30,0xfa,0xd0,
+0x04,0x43,0x04,0x80,0xe5,0x75,0x75,0xf0,0x03,0xa4,0x24,0x9d,0xf5,0x82,0xe4,0x34,
+0xfe,0xf5,0x83,0xec,0xf0,0xef,0xa3,0xf0,0xee,0xa3,0xf0,0x05,0x75,0x12,0x31,0x93,
+0xd0,0x83,0xd0,0x82,0xd0,0xf0,0x22,0xc0,0x04,0x7c,0x20,0xd2,0x8c,0xd2,0x8d,0xd5,
+0x04,0xfd,0xd0,0x04,0x22,0x75,0xa8,0x00,0x75,0x88,0x00,0x75,0xb8,0x00,0x75,0xf0,
+0x00,0x75,0xd0,0x00,0xe4,0xf8,0x90,0x00,0x00,0xf6,0x08,0xb8,0x00,0xfb,0x02,0x00,
+0x00,0xc3,0xed,0x94,0x02,0x50,0x04,0x7d,0x03,0x7c,0xe8,0xec,0xf4,0xfc,0xed,0xf4,
+0xfd,0x0c,0xbc,0x00,0x01,0x0d,0x8c,0x79,0x8d,0x78,0x22,0xc3,0xec,0x94,0xbc,0xed,
+0x94,0x02,0x50,0x04,0x7d,0x07,0x7c,0xd0,0xec,0xf4,0xfc,0xed,0xf4,0xfd,0x0c,0xbc,
+0x00,0x01,0x0d,0x8c,0x77,0x8d,0x76,0x22,0xec,0x70,0x01,0x22,0xc0,0x00,0xe5,0x72,
+0x24,0x18,0xf8,0xa6,0x04,0xe5,0x72,0x24,0x08,0xf8,0xc6,0x54,0x7f,0xf6,0xe6,0x30,
+0xe7,0x03,0xd0,0x00,0x22,0x12,0x31,0xe7,0x80,0xf4,0xc2,0x8c,0x85,0x76,0x8c,0x85,
+0x77,0x8a,0xd2,0x8c,0xc0,0xe0,0xc0,0xd0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x00,
+0xc0,0x01,0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0xc0,0x06,0xc0,0x07,0x12,0x1b,
+0x28,0xe5,0x72,0x24,0x08,0xf8,0xe6,0x60,0x24,0xe5,0x72,0x24,0x10,0xf8,0xa6,0x81,
+0xe5,0x72,0x75,0xf0,0x21,0xa4,0x24,0x95,0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,0x78,
+0xa8,0xe5,0x81,0x04,0xc3,0x98,0xf9,0xe6,0xf0,0x08,0xa3,0xd9,0xfa,0x74,0x08,0x25,
+0x72,0xf8,0x05,0x72,0x08,0xe6,0x54,0x80,0x70,0x0c,0xe5,0x72,0xb4,0x07,0xf3,0x78,
+0x08,0x75,0x72,0x00,0x80,0xef,0xe5,0x72,0x24,0x10,0xf8,0x86,0x81,0xe5,0x72,0x75,
+0xf0,0x21,0xa4,0x24,0x95,0xf5,0x82,0xe4,0x34,0xfd,0xf5,0x83,0x78,0xa8,0xe5,0x81,
+0x04,0xc3,0x98,0xf9,0xe0,0xf6,0x08,0xa3,0xd9,0xfa,0xd0,0x07,0xd0,0x06,0xd0,0x05,
+0xd0,0x04,0xd0,0x03,0xd0,0x02,0xd0,0x01,0xd0,0x00,0xd0,0x83,0xd0,0x82,0xd0,0xf0,
+0xd0,0xd0,0xd0,0xe0,0x32,0xc0,0xe0,0xc0,0xd0,0xc0,0x00,0xc0,0x01,0xc0,0x02,0xc2,
+0x8e,0x85,0x78,0x8d,0x85,0x79,0x8b,0xd2,0x8e,0x78,0x19,0x79,0x09,0x7a,0x07,0xe7,
+0x70,0x04,0xa6,0x00,0x80,0x0b,0xe6,0x60,0x08,0x16,0xe6,0x70,0x04,0xe7,0x44,0x80,
+0xf7,0x08,0x09,0xda,0xea,0xe5,0x73,0x60,0x13,0x14,0xf5,0x73,0x70,0x0e,0xe5,0x72,
+0x24,0x08,0xf8,0x76,0x00,0x12,0x31,0x93,0xd2,0x8c,0xd2,0x8d,0xd0,0x02,0xd0,0x01,
+0xd0,0x00,0xd0,0xd0,0xd0,0xe0,0x32,0x75,0x81,0xa7,0x75,0x90,0x00,0x75,0x79,0x30,
+0x75,0x78,0xf8,0x75,0x77,0x60,0x75,0x76,0xf0,0x12,0x05,0x3c,0x12,0x34,0x0f,0x12,
+0x17,0x8b,0x12,0x34,0x39,0x12,0x31,0xf5,0x80,0xe3,0x22,0xc0,0x00,0x7c,0x01,0xec,
+0x24,0x08,0xf8,0xe6,0x60,0x09,0x0c,0xbc,0x08,0xf5,0x12,0x31,0xe7,0x80,0xee,0xd0,
+0x00,0x22,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x00,0xc0,0x06,0xc0,0x07,0xed,0x24,
+0x10,0xf8,0x76,0xb6,0xed,0x75,0xf0,0x21,0xa4,0x24,0x95,0xf5,0x82,0xe4,0x34,0xfd,
+0xf5,0x83,0xc0,0x82,0xc0,0x83,0xa3,0xa3,0xe4,0x78,0x0d,0xf0,0xa3,0xd8,0xfc,0xec,
+0x54,0x7f,0x75,0xf0,0x02,0xa4,0x24,0x82,0xf5,0x82,0xe5,0xf0,0x34,0x34,0xf5,0x83,
+0xe4,0x93,0xfe,0x74,0x01,0x93,0xf5,0x82,0x8e,0x83,0xe4,0x93,0xfe,0x74,0x01,0x93,
+0xff,0xd0,0x83,0xd0,0x82,0xef,0xf0,0xa3,0xee,0xf0,0xed,0x24,0x08,0xf8,0xec,0x44,
+0x80,0xf6,0xd0,0x07,0xd0,0x06,0xd0,0x00,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0x22,0x75,
+0x72,0x00,0x75,0x75,0x00,0x7a,0x08,0x79,0x18,0x78,0x08,0x76,0x00,0x77,0x00,0x08,
+0x09,0xda,0xf8,0xe4,0x78,0x08,0x74,0x80,0x44,0x7f,0xf6,0x74,0x01,0x44,0x10,0xf5,
+0x89,0x75,0xb8,0x08,0xd2,0xab,0xd2,0xa9,0x22,0x75,0x81,0xa7,0xd2,0x8e,0xd2,0x8c,
+0xd2,0xaf,0xe5,0x75,0x60,0x32,0xff,0x90,0xfe,0x9d,0xe0,0x54,0x80,0x60,0x24,0x78,
+0x08,0x79,0x08,0xe0,0x54,0x7f,0xfa,0x7b,0x00,0xe6,0x54,0x7f,0xb5,0x02,0x02,0x7b,
+0xff,0x08,0xd9,0xf5,0xeb,0x70,0x0c,0xea,0xf0,0x12,0x33,0x8b,0xad,0x04,0xac,0x02,
+0x12,0x33,0xa2,0xa3,0xa3,0xa3,0xdf,0xd2,0x12,0x31,0xe7,0x80,0xc5,0x7c,0x01,0x7d,
+0x00,0x22,0x04,0xf5,0x04,0xe9,0x04,0xed,0x04,0xe1,0x04,0xdd,0x04,0xd9,0x04,0xe5,
+0x04,0xf1,0x04,0x9d,0x04,0xa1,0x04,0xcd,0x04,0xd1,0x04,0x99,0x04,0x99,0x04,0x99,
+0x04,0xd5,0x04,0xb5,0x04,0xad,0x04,0xb1,0x04,0xa9,0x04,0xc1,0x04,0xbd,0x04,0xb9,
+0x04,0xc5,0x04,0xc9,0x04,0xa5,0x19,0x01,0x03,0x00,0x22,0x00,0x48,0x02,0x00,0x24,
+0x0f,0x18,0x0a,0x10,0x64,0x0d,0x68,0x0c,0x05,0x06,0x02,0x03,0x01,0x01,0x81,0x01,
+0x00,0x00,0xe7,0x00,0xc0,0x00,0x80,0x00,0x60,0x00,0x40,0x00,0x30,0x00,0x18,0x00,
+0x0c,0x00,0x08,0x00,0x04,0x00,0x02,0x00,0x01,0x00,0x08,0x18,0x38,0x28,0x06,0x02,
+0x10,0x0a,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x81,0x10,0x0a,0x02,0x00,0x00,0x00,
+0x00,0x00,0xfb,0xe8,0xfb,0xfa,0x12,0x01,0x10,0x01,0xff,0x00,0x00,0x08,0x51,0x04,
+0x5f,0x50,0x16,0x01,0x01,0x02,0x00,0x02,0x09,0x02,0x35,0x00,0x01,0x02,0x00,0xe0,
+0x00,0x09,0x04,0x00,0x00,0x05,0xff,0x00,0x00,0x00,0x07,0x05,0x81,0x02,0x40,0x00,
+0x00,0x07,0x05,0x01,0x02,0x40,0x00,0x00,0x07,0x05,0x82,0x02,0x40,0x00,0x00,0x07,
+0x05,0x02,0x02,0x40,0x00,0x00,0x07,0x05,0x85,0x03,0x02,0x00,0x01,0x04,0x03,0x09,
+0x04,0x24,0x03,0x54,0x00,0x65,0x00,0x78,0x00,0x61,0x00,0x73,0x00,0x20,0x00,0x49,
+0x00,0x6e,0x00,0x73,0x00,0x74,0x00,0x72,0x00,0x75,0x00,0x6d,0x00,0x65,0x00,0x6e,
+0x00,0x74,0x00,0x73,0x00,0x2a,0x03,0x54,0x00,0x55,0x00,0x53,0x00,0x42,0x00,0x35,
+0x00,0x30,0x00,0x35,0x00,0x32,0x00,0x20,0x00,0x53,0x00,0x65,0x00,0x72,0x00,0x69,
+0x00,0x61,0x00,0x6c,0x00,0x20,0x00,0x50,0x00,0x6f,0x00,0x72,0x00,0x74,0x00,0x22,
+0x03,0x54,0x00,0x55,0x00,0x53,0x00,0x42,0x00,0x35,0x00,0x30,0x00,0x35,0x00,0x32,
+0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0x20,
+0x00,
+};
+
+#endif /* ifndef _TI_FW_5052_H_ */
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
new file mode 100644 (file)
index 0000000..dc18427
--- /dev/null
@@ -0,0 +1,1842 @@
+/* vi: ts=8 sw=8
+ *
+ * TI 3410/5052 USB Serial Driver
+ *
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * This driver is based on the Linux io_ti driver, which is
+ *   Copyright (C) 2000-2002 Inside Out Networks
+ *   Copyright (C) 2001-2002 Greg Kroah-Hartman
+ *
+ * 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.
+ *
+ * For questions or problems with this driver, contact Texas Instruments
+ * technical support, or Al Borchers <alborchers@steinerpoint.com>, or
+ * Peter Berger <pberger@brimson.com>.
+ * 
+ * This driver needs this hotplug script in /etc/hotplug/usb/ti_usb_3410_5052
+ * or in /etc/hotplug.d/usb/ti_usb_3410_5052.hotplug to set the device
+ * configuration.
+ *
+ * #!/bin/bash
+ *
+ * BOOT_CONFIG=1
+ * ACTIVE_CONFIG=2
+ *
+ * if [[ "$ACTION" != "add" ]]
+ * then
+ *     exit
+ * fi
+ *
+ * CONFIG_PATH=/sys${DEVPATH%/?*}/bConfigurationValue
+ *
+ * if [[ 0`cat $CONFIG_PATH` -ne $BOOT_CONFIG ]]
+ * then
+ *     exit
+ * fi
+ *
+ * PRODUCT=${PRODUCT%/?*}              # delete version
+ * VENDOR_ID=`printf "%d" 0x${PRODUCT%/?*}`
+ * PRODUCT_ID=`printf "%d" 0x${PRODUCT#*?/}`
+ *
+ * PARAM_PATH=/sys/module/ti_usb_3410_5052/parameters
+ *
+ * function scan() {
+ *     s=$1
+ *     shift
+ *     for i
+ *     do
+ *             if [[ $s -eq $i ]]
+ *             then
+ *                     return 0
+ *             fi
+ *     done
+ *     return 1
+ * }
+ *
+ * IFS=$IFS,
+ *
+ * if (scan $VENDOR_ID 1105 `cat $PARAM_PATH/vendor_3410` &&
+ * scan $PRODUCT_ID 13328 `cat $PARAM_PATH/product_3410`) ||
+ * (scan $VENDOR_ID 1105 `cat $PARAM_PATH/vendor_5052` &&
+ * scan $PRODUCT_ID 20562 20818 20570 20575 `cat $PARAM_PATH/product_5052`)
+ * then
+ *     echo $ACTIVE_CONFIG > $CONFIG_PATH
+ * fi
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/ioctl.h>
+#include <linux/serial.h>
+#include <linux/circ_buf.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <linux/usb.h>
+
+#include "usb-serial.h"
+#include "ti_usb_3410_5052.h"
+#include "ti_fw_3410.h"                /* firmware image for 3410 */
+#include "ti_fw_5052.h"                /* firmware image for 5052 */
+
+
+/* Defines */
+
+#define TI_DRIVER_VERSION      "v0.9"
+#define TI_DRIVER_AUTHOR       "Al Borchers <alborchers@steinerpoint.com>"
+#define TI_DRIVER_DESC         "TI USB 3410/5052 Serial Driver"
+
+#define TI_FIRMWARE_BUF_SIZE   16284
+
+#define TI_WRITE_BUF_SIZE      1024
+
+#define TI_TRANSFER_TIMEOUT    2
+
+#define TI_DEFAULT_LOW_LATENCY 0
+#define TI_DEFAULT_CLOSING_WAIT        4000            /* in .01 secs */
+
+/* supported setserial flags */
+#define TI_SET_SERIAL_FLAGS    (ASYNC_LOW_LATENCY)
+
+/* read urb states */
+#define TI_READ_URB_RUNNING    0
+#define TI_READ_URB_STOPPING   1
+#define TI_READ_URB_STOPPED    2
+
+#define TI_EXTRA_VID_PID_COUNT 5
+
+
+/* Structures */
+
+struct ti_port {
+       int                     tp_is_open;
+       __u8                    tp_msr;
+       __u8                    tp_lsr;
+       __u8                    tp_shadow_mcr;
+       __u8                    tp_uart_mode;   /* 232 or 485 modes */
+       unsigned int            tp_uart_base_addr;
+       int                     tp_flags;
+       int                     tp_closing_wait;/* in .01 secs */
+       struct async_icount     tp_icount;
+       wait_queue_head_t       tp_msr_wait;    /* wait for msr change */
+       wait_queue_head_t       tp_write_wait;
+       struct ti_device        *tp_tdev;
+       struct usb_serial_port  *tp_port;
+       spinlock_t              tp_lock;
+       int                     tp_read_urb_state;
+       int                     tp_write_urb_in_use;
+       struct circ_buf         *tp_write_buf;
+};
+
+struct ti_device {
+       struct semaphore        td_open_close_sem;
+       int                     td_open_port_count;
+       struct usb_serial       *td_serial;
+       int                     td_is_3410;
+       int                     td_urb_error;
+};
+
+
+/* Function Declarations */
+
+static int ti_startup(struct usb_serial *serial);
+static void ti_shutdown(struct usb_serial *serial);
+static int ti_open(struct usb_serial_port *port, struct file *file);
+static void ti_close(struct usb_serial_port *port, struct file *file);
+static int ti_write(struct usb_serial_port *port, const unsigned char *data,
+       int count);
+static int ti_write_room(struct usb_serial_port *port);
+static int ti_chars_in_buffer(struct usb_serial_port *port);
+static void ti_throttle(struct usb_serial_port *port);
+static void ti_unthrottle(struct usb_serial_port *port);
+static int ti_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg);
+static void ti_set_termios(struct usb_serial_port *port,
+       struct termios *old_termios);
+static int ti_tiocmget(struct usb_serial_port *port, struct file *file);
+static int ti_tiocmset(struct usb_serial_port *port, struct file *file,
+       unsigned int set, unsigned int clear);
+static void ti_break(struct usb_serial_port *port, int break_state);
+static void ti_interrupt_callback(struct urb *urb, struct pt_regs *regs);
+static void ti_bulk_in_callback(struct urb *urb, struct pt_regs *regs);
+static void ti_bulk_out_callback(struct urb *urb, struct pt_regs *regs);
+
+static void ti_recv(struct device *dev, struct tty_struct *tty,
+       unsigned char *data, int length);
+static void ti_send(struct ti_port *tport);
+static int ti_set_mcr(struct ti_port *tport, unsigned int mcr);
+static int ti_get_lsr(struct ti_port *tport);
+static int ti_get_serial_info(struct ti_port *tport,
+       struct serial_struct __user *ret_arg);
+static int ti_set_serial_info(struct ti_port *tport,
+       struct serial_struct __user *new_arg);
+static void ti_handle_new_msr(struct ti_port *tport, __u8 msr);
+
+static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush);
+
+static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty);
+static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty);
+
+static int ti_command_out_sync(struct ti_device *tdev, __u8 command,
+       __u16 moduleid, __u16 value, __u8 *data, int size);
+static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
+       __u16 moduleid, __u16 value, __u8 *data, int size);
+
+static int ti_write_byte(struct ti_device *tdev, unsigned long addr,
+       __u8 mask, __u8 byte);
+
+static int ti_download_firmware(struct ti_device *tdev,
+       unsigned char *firmware, unsigned int firmware_size);
+
+/* circular buffer */
+static struct circ_buf *ti_buf_alloc(void);
+static void ti_buf_free(struct circ_buf *cb);
+static void ti_buf_clear(struct circ_buf *cb);
+static int ti_buf_data_avail(struct circ_buf *cb);
+static int ti_buf_space_avail(struct circ_buf *cb);
+static int ti_buf_put(struct circ_buf *cb, const char *buf, int count);
+static int ti_buf_get(struct circ_buf *cb, char *buf, int count);
+
+
+/* Data */
+
+/* module parameters */
+static int debug;
+static int low_latency = TI_DEFAULT_LOW_LATENCY;
+static int closing_wait = TI_DEFAULT_CLOSING_WAIT;
+static ushort vendor_3410[TI_EXTRA_VID_PID_COUNT];
+static int vendor_3410_count;
+static ushort product_3410[TI_EXTRA_VID_PID_COUNT];
+static int product_3410_count;
+static ushort vendor_5052[TI_EXTRA_VID_PID_COUNT];
+static int vendor_5052_count;
+static ushort product_5052[TI_EXTRA_VID_PID_COUNT];
+static int product_5052_count;
+
+/* supported devices */
+/* the array dimension is the number of default entries plus */
+/* TI_EXTRA_VID_PID_COUNT user defined entries plus 1 terminating */
+/* null entry */
+static struct usb_device_id ti_id_table_3410[1+TI_EXTRA_VID_PID_COUNT+1] = {
+       { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) },
+};
+
+static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = {
+       { USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) },
+       { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
+       { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
+       { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
+};
+
+static struct usb_device_id ti_id_table_combined[] = {
+       { USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) },
+       { USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) },
+       { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
+       { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
+       { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
+       { }
+};
+
+static struct usb_driver ti_usb_driver = {
+       .owner                  = THIS_MODULE,
+       .name                   = "ti_usb_3410_5052",
+       .probe                  = usb_serial_probe,
+       .disconnect             = usb_serial_disconnect,
+       .id_table               = ti_id_table_combined,
+};
+
+static struct usb_serial_device_type ti_1port_device = {
+       .owner                  = THIS_MODULE,
+       .name                   = "TI USB 3410 1 port adapter",
+       .id_table               = ti_id_table_3410,
+       .num_interrupt_in       = 1,
+       .num_bulk_in            = 1,
+       .num_bulk_out           = 1,
+       .num_ports              = 1,
+       .attach                 = ti_startup,
+       .shutdown               = ti_shutdown,
+       .open                   = ti_open,
+       .close                  = ti_close,
+       .write                  = ti_write,
+       .write_room             = ti_write_room,
+       .chars_in_buffer        = ti_chars_in_buffer,
+       .throttle               = ti_throttle,
+       .unthrottle             = ti_unthrottle,
+       .ioctl                  = ti_ioctl,
+       .set_termios            = ti_set_termios,
+       .tiocmget               = ti_tiocmget,
+       .tiocmset               = ti_tiocmset,
+       .break_ctl              = ti_break,
+       .read_int_callback      = ti_interrupt_callback,
+       .read_bulk_callback     = ti_bulk_in_callback,
+       .write_bulk_callback    = ti_bulk_out_callback,
+};
+
+static struct usb_serial_device_type ti_2port_device = {
+       .owner                  = THIS_MODULE,
+       .name                   = "TI USB 5052 2 port adapter",
+       .id_table               = ti_id_table_5052,
+       .num_interrupt_in       = 1,
+       .num_bulk_in            = 2,
+       .num_bulk_out           = 2,
+       .num_ports              = 2,
+       .attach                 = ti_startup,
+       .shutdown               = ti_shutdown,
+       .open                   = ti_open,
+       .close                  = ti_close,
+       .write                  = ti_write,
+       .write_room             = ti_write_room,
+       .chars_in_buffer        = ti_chars_in_buffer,
+       .throttle               = ti_throttle,
+       .unthrottle             = ti_unthrottle,
+       .ioctl                  = ti_ioctl,
+       .set_termios            = ti_set_termios,
+       .tiocmget               = ti_tiocmget,
+       .tiocmset               = ti_tiocmset,
+       .break_ctl              = ti_break,
+       .read_int_callback      = ti_interrupt_callback,
+       .read_bulk_callback     = ti_bulk_in_callback,
+       .write_bulk_callback    = ti_bulk_out_callback,
+};
+
+
+/* Module */
+
+MODULE_AUTHOR(TI_DRIVER_AUTHOR);
+MODULE_DESCRIPTION(TI_DRIVER_DESC);
+MODULE_VERSION(TI_DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging, 0=no, 1=yes");
+
+module_param(low_latency, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(low_latency, "TTY low_latency flag, 0=off, 1=on, default is off");
+
+module_param(closing_wait, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain in close, in .01 secs, default is 4000");
+
+module_param_array(vendor_3410, ushort, &vendor_3410_count, S_IRUGO);
+MODULE_PARM_DESC(vendor_3410, "Vendor ids for 3410 based devices, 1-5 short integers");
+module_param_array(product_3410, ushort, &product_3410_count, S_IRUGO);
+MODULE_PARM_DESC(product_3410, "Product ids for 3410 based devices, 1-5 short integers");
+module_param_array(vendor_5052, ushort, &vendor_5052_count, S_IRUGO);
+MODULE_PARM_DESC(vendor_5052, "Vendor ids for 5052 based devices, 1-5 short integers");
+module_param_array(product_5052, ushort, &product_5052_count, S_IRUGO);
+MODULE_PARM_DESC(product_5052, "Product ids for 5052 based devices, 1-5 short integers");
+
+MODULE_DEVICE_TABLE(usb, ti_id_table_combined);
+
+
+/* Functions */
+
+static int __init ti_init(void)
+{
+       int i,j;
+       int ret;
+
+
+       /* insert extra vendor and product ids */
+       j = sizeof(ti_id_table_3410)/sizeof(struct usb_device_id)
+               - TI_EXTRA_VID_PID_COUNT - 1;
+       for (i=0; i<min(vendor_3410_count,product_3410_count); i++,j++) {
+               ti_id_table_3410[j].idVendor = vendor_3410[i];
+               ti_id_table_3410[j].idProduct = product_3410[i];
+               ti_id_table_3410[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+       }
+       j = sizeof(ti_id_table_5052)/sizeof(struct usb_device_id)
+               - TI_EXTRA_VID_PID_COUNT - 1;
+       for (i=0; i<min(vendor_5052_count,product_5052_count); i++,j++) {
+               ti_id_table_5052[j].idVendor = vendor_5052[i];
+               ti_id_table_5052[j].idProduct = product_5052[i];
+               ti_id_table_5052[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+       }
+
+       ret = usb_serial_register(&ti_1port_device);
+       if (ret)
+               goto failed_1port;
+       ret = usb_serial_register(&ti_2port_device);
+       if (ret)
+               goto failed_2port;
+
+       ret = usb_register(&ti_usb_driver);
+       if (ret)
+               goto failed_usb;
+
+       info(TI_DRIVER_DESC " " TI_DRIVER_VERSION);
+
+       return 0;
+
+failed_usb:
+       usb_serial_deregister(&ti_2port_device);
+failed_2port:
+       usb_serial_deregister(&ti_1port_device);
+failed_1port:
+       return ret;
+}
+
+
+static void __exit ti_exit(void)
+{
+       usb_serial_deregister(&ti_1port_device);
+       usb_serial_deregister(&ti_2port_device);
+       usb_deregister(&ti_usb_driver);
+}
+
+
+module_init(ti_init);
+module_exit(ti_exit);
+
+
+static int ti_startup(struct usb_serial *serial)
+{
+       struct ti_device *tdev;
+       struct ti_port *tport;
+       struct usb_device *dev = serial->dev;
+       int status;
+       int i;
+
+
+       dbg("%s - product 0x%4X, num configurations %d, configuration value %d",
+           __FUNCTION__, le16_to_cpu(dev->descriptor.idProduct),
+           dev->descriptor.bNumConfigurations,
+           dev->actconfig->desc.bConfigurationValue);
+
+       /* create device structure */
+       tdev = kmalloc(sizeof(struct ti_device), GFP_KERNEL);
+       if (tdev == NULL) {
+               dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+       memset(tdev, 0, sizeof(struct ti_device));
+       sema_init(&tdev->td_open_close_sem, 1);
+       tdev->td_serial = serial;
+       usb_set_serial_data(serial, tdev);
+
+       /* determine device type */
+       if (usb_match_id(serial->interface, ti_id_table_3410))
+               tdev->td_is_3410 = 1;
+       dbg("%s - device type is %s", __FUNCTION__, tdev->td_is_3410 ? "3410" : "5052");
+
+       /* if we have only 1 configuration, download firmware */
+       if (dev->descriptor.bNumConfigurations == 1) {
+
+               if (tdev->td_is_3410)
+                       status = ti_download_firmware(tdev, ti_fw_3410,
+                               sizeof(ti_fw_3410));
+               else
+                       status = ti_download_firmware(tdev, ti_fw_5052,
+                               sizeof(ti_fw_5052));
+               if (status)
+                       goto free_tdev;
+
+               /* 3410 must be reset, 5052 resets itself */
+               if (tdev->td_is_3410) {
+                       msleep_interruptible(100);
+                       usb_reset_device(dev);
+               }
+
+               status = -ENODEV;
+               goto free_tdev;
+       } 
+
+       /* the second configuration must be set (in sysfs by hotplug script) */
+       if (dev->actconfig->desc.bConfigurationValue == TI_BOOT_CONFIG) {
+               status = -ENODEV;
+               goto free_tdev;
+       }
+
+       /* set up port structures */
+       for (i = 0; i < serial->num_ports; ++i) {
+               tport = kmalloc(sizeof(struct ti_port), GFP_KERNEL);
+               if (tport == NULL) {
+                       dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
+                       status = -ENOMEM;
+                       goto free_tports;
+               }
+               memset(tport, 0, sizeof(struct ti_port));
+               spin_lock_init(&tport->tp_lock);
+               tport->tp_uart_base_addr = (i == 0 ? TI_UART1_BASE_ADDR : TI_UART2_BASE_ADDR);
+               tport->tp_flags = low_latency ? ASYNC_LOW_LATENCY : 0;
+               tport->tp_closing_wait = closing_wait;
+               init_waitqueue_head(&tport->tp_msr_wait);
+               init_waitqueue_head(&tport->tp_write_wait);
+               tport->tp_write_buf = ti_buf_alloc();
+               if (tport->tp_write_buf == NULL) {
+                       dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
+                       kfree(tport);
+                       status = -ENOMEM;
+                       goto free_tports;
+               }
+               tport->tp_port = serial->port[i];
+               tport->tp_tdev = tdev;
+               usb_set_serial_port_data(serial->port[i], tport);
+               tport->tp_uart_mode = 0;        /* default is RS232 */
+       }
+       
+       return 0;
+
+free_tports:
+       for (--i; i>=0; --i) {
+               tport = usb_get_serial_port_data(serial->port[i]);
+               ti_buf_free(tport->tp_write_buf);
+               kfree(tport);
+               usb_set_serial_port_data(serial->port[i], NULL);
+       }
+free_tdev:
+       kfree(tdev);
+       usb_set_serial_data(serial, NULL);
+       return status;
+}
+
+
+static void ti_shutdown(struct usb_serial *serial)
+{
+       int i;
+       struct ti_device *tdev = usb_get_serial_data(serial);
+       struct ti_port *tport;
+
+       dbg("%s", __FUNCTION__);
+
+       for (i=0; i < serial->num_ports; ++i) {
+               tport = usb_get_serial_port_data(serial->port[i]);
+               if (tport) {
+                       ti_buf_free(tport->tp_write_buf);
+                       kfree(tport);
+                       usb_set_serial_port_data(serial->port[i], NULL);
+               }
+       }
+
+       if (tdev)
+               kfree(tdev);
+       usb_set_serial_data(serial, NULL);
+}
+
+
+static int ti_open(struct usb_serial_port *port, struct file *file)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       struct ti_device *tdev;
+       struct usb_device *dev;
+       struct urb *urb;
+       int port_number;
+       int status;
+       __u16 open_settings = (__u8)(TI_PIPE_MODE_CONTINOUS | 
+                            TI_PIPE_TIMEOUT_ENABLE | 
+                            (TI_TRANSFER_TIMEOUT << 2));
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (tport == NULL)
+               return -ENODEV;
+
+       dev = port->serial->dev;
+       tdev = tport->tp_tdev;
+
+       /* only one open on any port on a device at a time */
+       if (down_interruptible(&tdev->td_open_close_sem))
+               return -ERESTARTSYS;
+
+       if (port->tty)
+               port->tty->low_latency = 
+                       (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+       port_number = port->number - port->serial->minor;
+
+       memset(&(tport->tp_icount), 0x00, sizeof(tport->tp_icount));
+
+       tport->tp_msr = 0;
+       tport->tp_shadow_mcr |= (TI_MCR_RTS | TI_MCR_DTR);
+
+       /* start interrupt urb the first time a port is opened on this device */
+       if (tdev->td_open_port_count == 0) {
+               dbg("%s - start interrupt in urb", __FUNCTION__);
+               urb = tdev->td_serial->port[0]->interrupt_in_urb;
+               if (!urb) {
+                       dev_err(&port->dev, "%s - no interrupt urb\n", __FUNCTION__);
+                       status = -EINVAL;
+                       goto up_sem;
+               }
+               urb->complete = ti_interrupt_callback;
+               urb->context = tdev;
+               urb->dev = dev;
+               status = usb_submit_urb(urb, GFP_KERNEL);
+               if (status) {
+                       dev_err(&port->dev, "%s - submit interrupt urb failed, %d\n", __FUNCTION__, status);
+                       goto up_sem;
+               }
+       }
+
+       ti_set_termios(port, NULL);
+
+       dbg("%s - sending TI_OPEN_PORT", __FUNCTION__);
+       status = ti_command_out_sync(tdev, TI_OPEN_PORT,
+               (__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0);
+       if (status) {
+               dev_err(&port->dev, "%s - cannot send open command, %d\n", __FUNCTION__, status);
+               goto unlink_int_urb;
+       }
+
+       dbg("%s - sending TI_START_PORT", __FUNCTION__);
+       status = ti_command_out_sync(tdev, TI_START_PORT,
+               (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
+       if (status) {
+               dev_err(&port->dev, "%s - cannot send start command, %d\n", __FUNCTION__, status);
+               goto unlink_int_urb;
+       }
+
+       dbg("%s - sending TI_PURGE_PORT", __FUNCTION__);
+       status = ti_command_out_sync(tdev, TI_PURGE_PORT,
+               (__u8)(TI_UART1_PORT + port_number), TI_PURGE_INPUT, NULL, 0);
+       if (status) {
+               dev_err(&port->dev, "%s - cannot clear input buffers, %d\n", __FUNCTION__, status);
+               goto unlink_int_urb;
+       }
+       status = ti_command_out_sync(tdev, TI_PURGE_PORT,
+               (__u8)(TI_UART1_PORT + port_number), TI_PURGE_OUTPUT, NULL, 0);
+       if (status) {
+               dev_err(&port->dev, "%s - cannot clear output buffers, %d\n", __FUNCTION__, status);
+               goto unlink_int_urb;
+       }
+
+       /* reset the data toggle on the bulk endpoints to work around bug in
+        * host controllers where things get out of sync some times */
+       usb_clear_halt(dev, port->write_urb->pipe);
+       usb_clear_halt(dev, port->read_urb->pipe);
+
+       ti_set_termios(port, NULL);
+
+       dbg("%s - sending TI_OPEN_PORT (2)", __FUNCTION__);
+       status = ti_command_out_sync(tdev, TI_OPEN_PORT,
+               (__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0);
+       if (status) {
+               dev_err(&port->dev, "%s - cannot send open command (2), %d\n", __FUNCTION__, status);
+               goto unlink_int_urb;
+       }
+
+       dbg("%s - sending TI_START_PORT (2)", __FUNCTION__);
+       status = ti_command_out_sync(tdev, TI_START_PORT,
+               (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
+       if (status) {
+               dev_err(&port->dev, "%s - cannot send start command (2), %d\n", __FUNCTION__, status);
+               goto unlink_int_urb;
+       }
+
+       /* start read urb */
+       dbg("%s - start read urb", __FUNCTION__);
+       urb = port->read_urb;
+       if (!urb) {
+               dev_err(&port->dev, "%s - no read urb\n", __FUNCTION__);
+               status = -EINVAL;
+               goto unlink_int_urb;
+       }
+       tport->tp_read_urb_state = TI_READ_URB_RUNNING;
+       urb->complete = ti_bulk_in_callback;
+       urb->context = tport;
+       urb->dev = dev;
+       status = usb_submit_urb(urb, GFP_KERNEL);
+       if (status) {
+               dev_err(&port->dev, "%s - submit read urb failed, %d\n", __FUNCTION__, status);
+               goto unlink_int_urb;
+       }
+
+       tport->tp_is_open = 1;
+       ++tdev->td_open_port_count;
+
+       goto up_sem;
+
+unlink_int_urb:
+       if (tdev->td_open_port_count == 0)
+               usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
+up_sem:
+       up(&tdev->td_open_close_sem);
+       dbg("%s - exit %d", __FUNCTION__, status);
+       return status;
+}
+
+
+static void ti_close(struct usb_serial_port *port, struct file *file)
+{
+       struct ti_device *tdev;
+       struct ti_port *tport;
+       int port_number;
+       int status;
+       int do_up;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+                        
+       tdev = usb_get_serial_data(port->serial);
+       tport = usb_get_serial_port_data(port);
+       if (tdev == NULL || tport == NULL)
+               return;
+
+       tport->tp_is_open = 0;
+
+       ti_drain(tport, (tport->tp_closing_wait*HZ)/100, 1);
+
+       usb_kill_urb(port->read_urb);
+       usb_kill_urb(port->write_urb);
+       tport->tp_write_urb_in_use = 0;
+
+       port_number = port->number - port->serial->minor;
+
+       dbg("%s - sending TI_CLOSE_PORT", __FUNCTION__);
+       status = ti_command_out_sync(tdev, TI_CLOSE_PORT,
+                    (__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
+       if (status)
+               dev_err(&port->dev, "%s - cannot send close port command, %d\n" , __FUNCTION__, status);
+
+       /* if down is interrupted, continue anyway */
+       do_up = !down_interruptible(&tdev->td_open_close_sem);
+       --tport->tp_tdev->td_open_port_count;
+       if (tport->tp_tdev->td_open_port_count <= 0) {
+               /* last port is closed, shut down interrupt urb */
+               usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
+               tport->tp_tdev->td_open_port_count = 0;
+       }
+       if (do_up)
+               up(&tdev->td_open_close_sem);
+
+       dbg("%s - exit", __FUNCTION__);
+}
+
+
+static int ti_write(struct usb_serial_port *port, const unsigned char *data,
+       int count)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (count == 0) {
+               dbg("%s - write request of 0 bytes", __FUNCTION__);
+               return 0;
+       }
+
+       if (tport == NULL || !tport->tp_is_open)
+               return -ENODEV;
+
+       spin_lock_irqsave(&tport->tp_lock, flags);
+       count = ti_buf_put(tport->tp_write_buf, data, count);
+       spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+       ti_send(tport);
+
+       return count;
+}
+
+
+static int ti_write_room(struct usb_serial_port *port)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       int room = 0;
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (tport == NULL)
+               return -ENODEV;
+       
+       spin_lock_irqsave(&tport->tp_lock, flags);
+       room = ti_buf_space_avail(tport->tp_write_buf);
+       spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+       dbg("%s - returns %d", __FUNCTION__, room);
+       return room;
+}
+
+
+static int ti_chars_in_buffer(struct usb_serial_port *port)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       int chars = 0;
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (tport == NULL)
+               return -ENODEV;
+
+       spin_lock_irqsave(&tport->tp_lock, flags);
+       chars = ti_buf_data_avail(tport->tp_write_buf);
+       spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+       dbg("%s - returns %d", __FUNCTION__, chars);
+       return chars;
+}
+
+
+static void ti_throttle(struct usb_serial_port *port)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       struct tty_struct *tty;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (tport == NULL)
+               return;
+
+       tty = port->tty;
+       if (!tty) {
+               dbg("%s - no tty", __FUNCTION__);
+               return;
+       }
+
+       if (I_IXOFF(tty) || C_CRTSCTS(tty))
+               ti_stop_read(tport, tty);
+
+}
+
+
+static void ti_unthrottle(struct usb_serial_port *port)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       struct tty_struct *tty;
+       int status;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (tport == NULL)
+               return;
+
+       tty = port->tty;
+       if (!tty) {
+               dbg("%s - no tty", __FUNCTION__);
+               return;
+       }
+
+       if (I_IXOFF(tty) || C_CRTSCTS(tty)) {
+               status = ti_restart_read(tport, tty);
+               if (status)
+                       dev_err(&port->dev, "%s - cannot restart read, %d\n", __FUNCTION__, status);
+       }
+}
+
+
+static int ti_ioctl(struct usb_serial_port *port, struct file *file,
+       unsigned int cmd, unsigned long arg)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       struct async_icount cnow;
+       struct async_icount cprev;
+
+       dbg("%s - port %d, cmd = 0x%04X", __FUNCTION__, port->number, cmd);
+
+       if (tport == NULL)
+               return -ENODEV;
+
+       switch (cmd) {
+               case TIOCGSERIAL:
+                       dbg("%s - (%d) TIOCGSERIAL", __FUNCTION__, port->number);
+                       return ti_get_serial_info(tport, (struct serial_struct __user *)arg);
+                       break;
+
+               case TIOCSSERIAL:
+                       dbg("%s - (%d) TIOCSSERIAL", __FUNCTION__, port->number);
+                       return ti_set_serial_info(tport, (struct serial_struct __user *)arg);
+                       break;
+
+               case TIOCMIWAIT:
+                       dbg("%s - (%d) TIOCMIWAIT", __FUNCTION__, port->number);
+                       cprev = tport->tp_icount;
+                       while (1) {
+                               interruptible_sleep_on(&tport->tp_msr_wait);
+                               if (signal_pending(current))
+                                       return -ERESTARTSYS;
+                               cnow = tport->tp_icount;
+                               if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+                                   cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+                                       return -EIO; /* no change => error */
+                               if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+                                   ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+                                   ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+                                   ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+                                       return 0;
+                               }
+                               cprev = cnow;
+                       }
+                       break;
+
+               case TIOCGICOUNT:
+                       dbg("%s - (%d) TIOCGICOUNT RX=%d, TX=%d", __FUNCTION__, port->number, tport->tp_icount.rx, tport->tp_icount.tx);
+                       if (copy_to_user((void __user *)arg, &tport->tp_icount, sizeof(tport->tp_icount)))
+                               return -EFAULT;
+                       return 0;
+       }
+
+       return -ENOIOCTLCMD;
+}
+
+
+static void ti_set_termios(struct usb_serial_port *port,
+       struct termios *old_termios)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       struct tty_struct *tty = port->tty;
+       struct ti_uart_config *config;
+       tcflag_t cflag,iflag;
+       int baud;
+       int status;
+       int port_number = port->number - port->serial->minor;
+       unsigned int mcr;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (!tty || !tty->termios) {
+               dbg("%s - no tty or termios", __FUNCTION__);
+               return;
+       }
+
+       cflag = tty->termios->c_cflag;
+       iflag = tty->termios->c_iflag;
+
+       if (old_termios && cflag == old_termios->c_cflag
+       && iflag == old_termios->c_iflag) {
+               dbg("%s - nothing to change", __FUNCTION__);
+               return;
+       }
+
+       dbg("%s - clfag %08x, iflag %08x", __FUNCTION__, cflag, iflag);
+
+       if (old_termios)
+               dbg("%s - old clfag %08x, old iflag %08x", __FUNCTION__, old_termios->c_cflag, old_termios->c_iflag);
+
+       if (tport == NULL)
+               return;
+
+       config = kmalloc(sizeof(*config), GFP_KERNEL);
+       if (!config) {
+               dev_err(&port->dev, "%s - out of memory\n", __FUNCTION__);
+               return;
+       }
+
+       config->wFlags = 0;
+
+       /* these flags must be set */
+       config->wFlags |= TI_UART_ENABLE_MS_INTS;
+       config->wFlags |= TI_UART_ENABLE_AUTO_START_DMA;
+       config->bUartMode = (__u8)(tport->tp_uart_mode);
+
+       switch (cflag & CSIZE) {
+               case CS5:
+                           config->bDataBits = TI_UART_5_DATA_BITS;
+                           break;
+               case CS6:
+                           config->bDataBits = TI_UART_6_DATA_BITS;
+                           break;
+               case CS7:
+                           config->bDataBits = TI_UART_7_DATA_BITS;
+                           break;
+               default:
+               case CS8:
+                           config->bDataBits = TI_UART_8_DATA_BITS;
+                           break;
+       }
+
+       if (cflag & PARENB) {
+               if (cflag & PARODD) {
+                       config->wFlags |= TI_UART_ENABLE_PARITY_CHECKING;
+                       config->bParity = TI_UART_ODD_PARITY;
+               } else {
+                       config->wFlags |= TI_UART_ENABLE_PARITY_CHECKING;
+                       config->bParity = TI_UART_EVEN_PARITY;
+               }
+       } else {
+               config->wFlags &= ~TI_UART_ENABLE_PARITY_CHECKING;
+               config->bParity = TI_UART_NO_PARITY;    
+       }
+
+       if (cflag & CSTOPB)
+               config->bStopBits = TI_UART_2_STOP_BITS;
+       else
+               config->bStopBits = TI_UART_1_STOP_BITS;
+
+       if (cflag & CRTSCTS) {
+               /* RTS flow control must be off to drop RTS for baud rate B0 */
+               if ((cflag & CBAUD) != B0)
+                       config->wFlags |= TI_UART_ENABLE_RTS_IN;
+               config->wFlags |= TI_UART_ENABLE_CTS_OUT;
+       } else {
+               tty->hw_stopped = 0;
+               ti_restart_read(tport, tty);
+       }
+
+       if (I_IXOFF(tty) || I_IXON(tty)) {
+               config->cXon  = START_CHAR(tty);
+               config->cXoff = STOP_CHAR(tty);
+
+               if (I_IXOFF(tty))
+                       config->wFlags |= TI_UART_ENABLE_X_IN;
+               else
+                       ti_restart_read(tport, tty);
+
+               if (I_IXON(tty))
+                       config->wFlags |= TI_UART_ENABLE_X_OUT;
+       }
+
+       baud = tty_get_baud_rate(tty);
+       if (!baud) baud = 9600;
+       if (tport->tp_tdev->td_is_3410)
+               config->wBaudRate = (__u16)((923077 + baud/2) / baud);
+       else
+               config->wBaudRate = (__u16)((461538 + baud/2) / baud);
+
+       dbg("%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d",
+       __FUNCTION__, baud, config->wBaudRate, config->wFlags, config->bDataBits, config->bParity, config->bStopBits, config->cXon, config->cXoff, config->bUartMode);
+
+       cpu_to_be16s(&config->wBaudRate);
+       cpu_to_be16s(&config->wFlags);
+
+       status = ti_command_out_sync(tport->tp_tdev, TI_SET_CONFIG,
+               (__u8)(TI_UART1_PORT + port_number), 0, (__u8 *)config,
+               sizeof(*config));
+       if (status)
+               dev_err(&port->dev, "%s - cannot set config on port %d, %d\n", __FUNCTION__, port_number, status);
+
+       /* SET_CONFIG asserts RTS and DTR, reset them correctly */
+       mcr = tport->tp_shadow_mcr;
+       /* if baud rate is B0, clear RTS and DTR */
+       if ((cflag & CBAUD) == B0)
+               mcr &= ~(TI_MCR_DTR | TI_MCR_RTS);
+       status = ti_set_mcr(tport, mcr);
+       if (status)
+               dev_err(&port->dev, "%s - cannot set modem control on port %d, %d\n", __FUNCTION__, port_number, status);
+
+       kfree(config);
+}
+
+
+static int ti_tiocmget(struct usb_serial_port *port, struct file *file)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       unsigned int result;
+       unsigned int msr;
+       unsigned int mcr;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (tport == NULL)
+               return -ENODEV;
+
+       msr = tport->tp_msr;
+       mcr = tport->tp_shadow_mcr;
+
+       result = ((mcr & TI_MCR_DTR) ? TIOCM_DTR : 0)
+               | ((mcr & TI_MCR_RTS) ? TIOCM_RTS : 0)
+               | ((mcr & TI_MCR_LOOP) ? TIOCM_LOOP : 0)
+               | ((msr & TI_MSR_CTS) ? TIOCM_CTS : 0)
+               | ((msr & TI_MSR_CD) ? TIOCM_CAR : 0)
+               | ((msr & TI_MSR_RI) ? TIOCM_RI : 0)
+               | ((msr & TI_MSR_DSR) ? TIOCM_DSR : 0);
+
+       dbg("%s - 0x%04X", __FUNCTION__, result);
+
+       return result;
+}
+
+
+static int ti_tiocmset(struct usb_serial_port *port, struct file *file,
+       unsigned int set, unsigned int clear)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       unsigned int mcr;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (tport == NULL)
+               return -ENODEV;
+
+       mcr = tport->tp_shadow_mcr;
+
+       if (set & TIOCM_RTS)
+               mcr |= TI_MCR_RTS;
+       if (set & TIOCM_DTR)
+               mcr |= TI_MCR_DTR;
+       if (set & TIOCM_LOOP)
+               mcr |= TI_MCR_LOOP;
+
+       if (clear & TIOCM_RTS)
+               mcr &= ~TI_MCR_RTS;
+       if (clear & TIOCM_DTR)
+               mcr &= ~TI_MCR_DTR;
+       if (clear & TIOCM_LOOP)
+               mcr &= ~TI_MCR_LOOP;
+
+       return ti_set_mcr(tport, mcr);
+}
+
+
+static void ti_break(struct usb_serial_port *port, int break_state)
+{
+       struct ti_port *tport = usb_get_serial_port_data(port);
+       int status;
+
+       dbg("%s - state = %d", __FUNCTION__, break_state);
+
+       if (tport == NULL)
+               return;
+
+       ti_drain(tport, (tport->tp_closing_wait*HZ)/100, 0);
+
+       status = ti_write_byte(tport->tp_tdev,
+               tport->tp_uart_base_addr + TI_UART_OFFSET_LCR,
+               TI_LCR_BREAK, break_state == -1 ? TI_LCR_BREAK : 0);
+
+       if (status)
+               dbg("%s - error setting break, %d", __FUNCTION__, status);
+}
+
+
+static void ti_interrupt_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct ti_device *tdev = (struct ti_device *)urb->context;
+       struct usb_serial_port *port;
+       struct usb_serial *serial = tdev->td_serial;
+       struct ti_port *tport;
+       struct device *dev = &urb->dev->dev;
+       unsigned char *data = urb->transfer_buffer;
+       int length = urb->actual_length;
+       int port_number;
+       int function;
+       int status;
+       __u8 msr;
+
+       dbg("%s", __FUNCTION__);
+
+       switch (urb->status) {
+       case 0:
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status);
+               tdev->td_urb_error = 1;
+               return;
+       default:
+               dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status);
+               tdev->td_urb_error = 1;
+               goto exit;
+       }
+
+       if (length != 2) {
+               dbg("%s - bad packet size, %d", __FUNCTION__, length);
+               goto exit;
+       }
+
+       if (data[0] == TI_CODE_HARDWARE_ERROR) {
+               dev_err(dev, "%s - hardware error, %d\n", __FUNCTION__, data[1]);
+               goto exit;
+       }
+
+       port_number = TI_GET_PORT_FROM_CODE(data[0]);
+       function = TI_GET_FUNC_FROM_CODE(data[0]);
+
+       dbg("%s - port_number %d, function %d, data 0x%02X", __FUNCTION__, port_number, function, data[1]);
+
+       if (port_number >= serial->num_ports) {
+               dev_err(dev, "%s - bad port number, %d\n", __FUNCTION__, port_number);
+               goto exit;
+       }
+
+       port = serial->port[port_number];
+
+       tport = usb_get_serial_port_data(port);
+       if (!tport)
+               goto exit;
+
+       switch (function) {
+       case TI_CODE_DATA_ERROR:
+               dev_err(dev, "%s - DATA ERROR, port %d, data 0x%02X\n", __FUNCTION__, port_number, data[1]);
+               break;
+
+       case TI_CODE_MODEM_STATUS:
+               msr = data[1];
+               dbg("%s - port %d, msr 0x%02X", __FUNCTION__, port_number, msr);
+               ti_handle_new_msr(tport, msr);
+               break;
+
+       default:
+               dev_err(dev, "%s - unknown interrupt code, 0x%02X\n", __FUNCTION__, data[1]);
+               break;
+       }
+
+exit:
+       status = usb_submit_urb(urb, GFP_ATOMIC);
+       if (status)
+               dev_err(dev, "%s - resubmit interrupt urb failed, %d\n", __FUNCTION__, status);
+}
+
+
+static void ti_bulk_in_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct ti_port *tport = (struct ti_port *)urb->context;
+       struct usb_serial_port *port = tport->tp_port;
+       struct device *dev = &urb->dev->dev;
+       int status = 0;
+
+       dbg("%s", __FUNCTION__);
+
+       switch (urb->status) {
+       case 0:
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status);
+               tport->tp_tdev->td_urb_error = 1;
+               wake_up_interruptible(&tport->tp_write_wait);
+               return;
+       default:
+               dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status );
+               tport->tp_tdev->td_urb_error = 1;
+               wake_up_interruptible(&tport->tp_write_wait);
+       }
+
+       if (urb->status == -EPIPE)
+               goto exit;
+
+       if (urb->status) {
+               dev_err(dev, "%s - stopping read!\n", __FUNCTION__);
+               return;
+       }
+
+       if (port->tty && urb->actual_length) {
+               usb_serial_debug_data(debug, dev, __FUNCTION__,
+                       urb->actual_length, urb->transfer_buffer);
+
+               if (!tport->tp_is_open)
+                       dbg("%s - port closed, dropping data", __FUNCTION__);
+               else
+                       ti_recv(&urb->dev->dev, port->tty, urb->transfer_buffer,
+                               urb->actual_length);
+
+               spin_lock(&tport->tp_lock);
+               tport->tp_icount.rx += urb->actual_length;
+               spin_unlock(&tport->tp_lock);
+       }
+
+exit:
+       /* continue to read unless stopping */
+       spin_lock(&tport->tp_lock);
+       if (tport->tp_read_urb_state == TI_READ_URB_RUNNING) {
+               urb->dev = port->serial->dev;
+               status = usb_submit_urb(urb, GFP_ATOMIC);
+       } else if (tport->tp_read_urb_state == TI_READ_URB_STOPPING) {
+               tport->tp_read_urb_state = TI_READ_URB_STOPPED;
+       }
+       spin_unlock(&tport->tp_lock);
+       if (status)
+               dev_err(dev, "%s - resubmit read urb failed, %d\n", __FUNCTION__, status);
+}
+
+
+static void ti_bulk_out_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct ti_port *tport = (struct ti_port *)urb->context;
+       struct usb_serial_port *port = tport->tp_port;
+       struct device *dev = &urb->dev->dev;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       tport->tp_write_urb_in_use = 0;
+
+       switch (urb->status) {
+       case 0:
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status);
+               tport->tp_tdev->td_urb_error = 1;
+               wake_up_interruptible(&tport->tp_write_wait);
+               return;
+       default:
+               dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status);
+               tport->tp_tdev->td_urb_error = 1;
+               wake_up_interruptible(&tport->tp_write_wait);
+       }
+
+       /* send any buffered data */
+       ti_send(tport);
+}
+
+
+static void ti_recv(struct device *dev, struct tty_struct *tty,
+       unsigned char *data, int length)
+{
+       int cnt;
+
+       do {
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                       tty_flip_buffer_push(tty);
+                       if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                               dev_err(dev, "%s - dropping data, %d bytes lost\n", __FUNCTION__, length);
+                               return;
+                       }
+               }
+               cnt = min(length, TTY_FLIPBUF_SIZE - tty->flip.count);
+               memcpy(tty->flip.char_buf_ptr, data, cnt);
+               memset(tty->flip.flag_buf_ptr, 0, cnt);
+               tty->flip.char_buf_ptr += cnt;
+               tty->flip.flag_buf_ptr += cnt;
+               tty->flip.count += cnt;
+               data += cnt;
+               length -= cnt;
+       } while (length > 0);
+
+       tty_flip_buffer_push(tty);
+}
+
+
+static void ti_send(struct ti_port *tport)
+{
+       int count, result;
+       struct usb_serial_port *port = tport->tp_port;
+       struct tty_struct *tty = port->tty;
+       unsigned long flags;
+
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&tport->tp_lock, flags);
+
+       if (tport->tp_write_urb_in_use) {
+               spin_unlock_irqrestore(&tport->tp_lock, flags);
+               return;
+       }
+
+       count = ti_buf_get(tport->tp_write_buf,
+                               port->write_urb->transfer_buffer,
+                               port->bulk_out_size);
+
+       if (count == 0) {
+               spin_unlock_irqrestore(&tport->tp_lock, flags);
+               return;
+       }
+
+       tport->tp_write_urb_in_use = 1;
+
+       spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer);
+
+       usb_fill_bulk_urb(port->write_urb, port->serial->dev,
+                          usb_sndbulkpipe(port->serial->dev,
+                                           port->bulk_out_endpointAddress),
+                          port->write_urb->transfer_buffer, count,
+                          ti_bulk_out_callback, tport);
+
+       result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+       if (result) {
+               dev_err(&port->dev, "%s - submit write urb failed, %d\n", __FUNCTION__, result);
+               tport->tp_write_urb_in_use = 0; 
+               /* TODO: reschedule ti_send */
+       } else {
+               spin_lock_irqsave(&tport->tp_lock, flags);
+               tport->tp_icount.tx += count;
+               spin_unlock_irqrestore(&tport->tp_lock, flags);
+       }
+
+       /* more room in the buffer for new writes, wakeup */
+       if (tty)
+               tty_wakeup(tty);
+       wake_up_interruptible(&tport->tp_write_wait);
+}
+
+
+static int ti_set_mcr(struct ti_port *tport, unsigned int mcr)
+{
+       int status;
+
+       status = ti_write_byte(tport->tp_tdev,
+               tport->tp_uart_base_addr + TI_UART_OFFSET_MCR,
+               TI_MCR_RTS | TI_MCR_DTR | TI_MCR_LOOP, mcr);
+
+       if (!status)
+               tport->tp_shadow_mcr = mcr;
+
+       return status;
+}
+
+
+static int ti_get_lsr(struct ti_port *tport)
+{
+       int size,status;
+       struct ti_device *tdev = tport->tp_tdev;
+       struct usb_serial_port *port = tport->tp_port;
+       int port_number = port->number - port->serial->minor;
+       struct ti_port_status *data;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       size = sizeof(struct ti_port_status);
+       data = kmalloc(size, GFP_KERNEL);
+       if (!data) {
+               dev_err(&port->dev, "%s - out of memory\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+
+       status = ti_command_in_sync(tdev, TI_GET_PORT_STATUS,
+               (__u8)(TI_UART1_PORT+port_number), 0, (__u8 *)data, size);
+       if (status) {
+               dev_err(&port->dev, "%s - get port status command failed, %d\n", __FUNCTION__, status);
+               goto free_data;
+       }
+
+       dbg("%s - lsr 0x%02X", __FUNCTION__, data->bLSR);
+
+       tport->tp_lsr = data->bLSR;
+
+free_data:
+       kfree(data);
+       return status;
+}
+
+
+static int ti_get_serial_info(struct ti_port *tport,
+       struct serial_struct __user *ret_arg)
+{
+       struct usb_serial_port *port = tport->tp_port;
+       struct serial_struct ret_serial;
+
+       if (!ret_arg)
+               return -EFAULT;
+
+       memset(&ret_serial, 0, sizeof(ret_serial));
+
+       ret_serial.type = PORT_16550A;
+       ret_serial.line = port->serial->minor;
+       ret_serial.port = port->number - port->serial->minor;
+       ret_serial.flags = tport->tp_flags;
+       ret_serial.xmit_fifo_size = TI_WRITE_BUF_SIZE;
+       ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800;
+       ret_serial.closing_wait = tport->tp_closing_wait;
+
+       if (copy_to_user(ret_arg, &ret_serial, sizeof(*ret_arg)))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+static int ti_set_serial_info(struct ti_port *tport,
+       struct serial_struct __user *new_arg)
+{
+       struct usb_serial_port *port = tport->tp_port;
+       struct serial_struct new_serial;
+
+       if (copy_from_user(&new_serial, new_arg, sizeof(new_serial)))
+               return -EFAULT;
+
+       tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS;
+       if (port->tty)
+               port->tty->low_latency =
+                       (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+       tport->tp_closing_wait = new_serial.closing_wait;
+
+       return 0;
+}
+
+
+static void ti_handle_new_msr(struct ti_port *tport, __u8 msr)
+{
+       struct async_icount *icount;
+       struct tty_struct *tty;
+       unsigned long flags;
+
+       dbg("%s - msr 0x%02X", __FUNCTION__, msr);
+
+       if (msr & TI_MSR_DELTA_MASK) {
+               spin_lock_irqsave(&tport->tp_lock, flags);
+               icount = &tport->tp_icount;
+               if (msr & TI_MSR_DELTA_CTS)
+                       icount->cts++;
+               if (msr & TI_MSR_DELTA_DSR)
+                       icount->dsr++;
+               if (msr & TI_MSR_DELTA_CD)
+                       icount->dcd++;
+               if (msr & TI_MSR_DELTA_RI)
+                       icount->rng++;
+               wake_up_interruptible(&tport->tp_msr_wait);
+               spin_unlock_irqrestore(&tport->tp_lock, flags);
+       }
+
+       tport->tp_msr = msr & TI_MSR_MASK;
+
+       /* handle CTS flow control */
+       tty = tport->tp_port->tty;
+       if (tty && C_CRTSCTS(tty)) {
+               if (msr & TI_MSR_CTS) {
+                       tty->hw_stopped = 0;
+                       tty_wakeup(tty);
+               } else {
+                       tty->hw_stopped = 1;
+               }
+       }
+}
+
+
+static void ti_drain(struct ti_port *tport, unsigned long timeout, int flush)
+{
+       struct ti_device *tdev = tport->tp_tdev;
+       struct usb_serial_port *port = tport->tp_port;
+       wait_queue_t wait;
+       unsigned long flags;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&tport->tp_lock, flags);
+
+       /* wait for data to drain from the buffer */
+       tdev->td_urb_error = 0;
+       init_waitqueue_entry(&wait, current);
+       add_wait_queue(&tport->tp_write_wait, &wait);
+       for (;;) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (ti_buf_data_avail(tport->tp_write_buf) == 0
+               || timeout == 0 || signal_pending(current)
+               || tdev->td_urb_error
+               || !usb_get_intfdata(port->serial->interface))  /* disconnect */
+                       break;
+               spin_unlock_irqrestore(&tport->tp_lock, flags);
+               timeout = schedule_timeout(timeout);
+               spin_lock_irqsave(&tport->tp_lock, flags);
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&tport->tp_write_wait, &wait);
+
+       /* flush any remaining data in the buffer */
+       if (flush)
+               ti_buf_clear(tport->tp_write_buf);
+
+       spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+       /* wait for data to drain from the device */
+       /* wait for empty tx register, plus 20 ms */
+       timeout += jiffies;
+       tport->tp_lsr &= ~TI_LSR_TX_EMPTY;
+       while ((long)(jiffies - timeout) < 0 && !signal_pending(current)
+       && !(tport->tp_lsr&TI_LSR_TX_EMPTY) && !tdev->td_urb_error
+       && usb_get_intfdata(port->serial->interface)) {  /* not disconnected */
+               if (ti_get_lsr(tport))
+                       break;
+               msleep_interruptible(20);
+       }
+}
+
+
+static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&tport->tp_lock, flags);
+
+       if (tport->tp_read_urb_state == TI_READ_URB_RUNNING)
+               tport->tp_read_urb_state = TI_READ_URB_STOPPING;
+
+       spin_unlock_irqrestore(&tport->tp_lock, flags);
+}
+
+
+static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty)
+{
+       struct urb *urb;
+       int status = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&tport->tp_lock, flags);
+
+       if (tport->tp_read_urb_state == TI_READ_URB_STOPPED) {
+               urb = tport->tp_port->read_urb;
+               urb->complete = ti_bulk_in_callback;
+               urb->context = tport;
+               urb->dev = tport->tp_port->serial->dev;
+               status = usb_submit_urb(urb, GFP_KERNEL);
+       }
+       tport->tp_read_urb_state = TI_READ_URB_RUNNING;
+
+       spin_unlock_irqrestore(&tport->tp_lock, flags);
+
+       return status;
+}
+
+
+static int ti_command_out_sync(struct ti_device *tdev, __u8 command,
+       __u16 moduleid, __u16 value, __u8 *data, int size)
+{
+       int status;
+
+       status = usb_control_msg(tdev->td_serial->dev,
+               usb_sndctrlpipe(tdev->td_serial->dev, 0), command,
+               (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
+               value, moduleid, data, size, HZ);
+
+       if (status == size)
+               status = 0;
+
+       if (status > 0)
+               status = -ECOMM;
+
+       return status;
+}
+
+
+static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
+       __u16 moduleid, __u16 value, __u8 *data, int size)
+{
+       int status;
+
+       status = usb_control_msg(tdev->td_serial->dev,
+               usb_rcvctrlpipe(tdev->td_serial->dev, 0), command,
+               (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN),
+               value, moduleid, data, size, HZ);
+
+       if (status == size)
+               status = 0;
+
+       if (status > 0)
+               status = -ECOMM;
+
+       return status;
+}
+
+
+static int ti_write_byte(struct ti_device *tdev, unsigned long addr,
+       __u8 mask, __u8 byte)
+{
+       int status;
+       unsigned int size;
+       struct ti_write_data_bytes *data;
+       struct device *dev = &tdev->td_serial->dev->dev;
+
+       dbg("%s - addr 0x%08lX, mask 0x%02X, byte 0x%02X", __FUNCTION__, addr, mask, byte);
+
+       size = sizeof(struct ti_write_data_bytes) + 2;
+       data = kmalloc(size, GFP_KERNEL);
+       if (!data) {
+               dev_err(dev, "%s - out of memory\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+
+       data->bAddrType = TI_RW_DATA_ADDR_XDATA;
+       data->bDataType = TI_RW_DATA_BYTE;
+       data->bDataCounter = 1;
+       data->wBaseAddrHi = cpu_to_be16(addr>>16);
+       data->wBaseAddrLo = cpu_to_be16(addr);
+       data->bData[0] = mask;
+       data->bData[1] = byte;
+
+       status = ti_command_out_sync(tdev, TI_WRITE_DATA, TI_RAM_PORT, 0,
+               (__u8 *)data, size);
+
+       if (status < 0)
+               dev_err(dev, "%s - failed, %d\n", __FUNCTION__, status);
+
+       kfree(data);
+
+       return status;
+}
+
+
+static int ti_download_firmware(struct ti_device *tdev,
+       unsigned char *firmware, unsigned int firmware_size)
+{
+       int status = 0;
+       int buffer_size;
+       int pos;
+       int len;
+       int done;
+       __u8 cs = 0;
+       __u8 *buffer;
+       struct usb_device *dev = tdev->td_serial->dev;
+       struct ti_firmware_header *header;
+       unsigned int pipe = usb_sndbulkpipe(dev,
+               tdev->td_serial->port[0]->bulk_out_endpointAddress);
+
+
+       buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header);
+       buffer = kmalloc(buffer_size, GFP_KERNEL);
+       if (!buffer) {
+               dev_err(&dev->dev, "%s - out of memory\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+
+       memcpy(buffer, firmware, firmware_size);
+       memset(buffer+firmware_size, 0xff, buffer_size-firmware_size);
+
+       for(pos = sizeof(struct ti_firmware_header); pos < buffer_size; pos++)
+               cs = (__u8)(cs + buffer[pos]);
+
+       header = (struct ti_firmware_header *)buffer;
+       header->wLength = cpu_to_le16((__u16)(buffer_size - sizeof(struct ti_firmware_header)));
+       header->bCheckSum = cs;
+
+       dbg("%s - downloading firmware", __FUNCTION__);
+       for (pos = 0; pos < buffer_size; pos += done) {
+               len = min(buffer_size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE);
+               status = usb_bulk_msg(dev, pipe, buffer+pos, len, &done, HZ);
+               if (status)
+                       break;
+       }
+
+       kfree(buffer);
+
+       if (status) {
+               dev_err(&dev->dev, "%s - error downloading firmware, %d\n", __FUNCTION__, status);
+               return status;
+       }
+
+       dbg("%s - download successful", __FUNCTION__);
+
+       return 0;
+}
+
+
+/* Circular Buffer Functions */
+
+/*
+ * ti_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+
+static struct circ_buf *ti_buf_alloc(void)
+{
+       struct circ_buf *cb;
+
+       cb = (struct circ_buf *)kmalloc(sizeof(struct circ_buf), GFP_KERNEL);
+       if (cb == NULL)
+               return NULL;
+
+       cb->buf = kmalloc(TI_WRITE_BUF_SIZE, GFP_KERNEL);
+       if (cb->buf == NULL) {
+               kfree(cb);
+               return NULL;
+       }
+
+       ti_buf_clear(cb);
+
+       return cb;
+}
+
+
+/*
+ * ti_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+
+static void ti_buf_free(struct circ_buf *cb)
+{
+       kfree(cb->buf);
+       kfree(cb);
+}
+
+
+/*
+ * ti_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+
+static void ti_buf_clear(struct circ_buf *cb)
+{
+       cb->head = cb->tail = 0;
+}
+
+
+/*
+ * ti_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+
+static int ti_buf_data_avail(struct circ_buf *cb)
+{
+       return CIRC_CNT(cb->head,cb->tail,TI_WRITE_BUF_SIZE);
+}
+
+
+/*
+ * ti_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+
+static int ti_buf_space_avail(struct circ_buf *cb)
+{
+       return CIRC_SPACE(cb->head,cb->tail,TI_WRITE_BUF_SIZE);
+}
+
+
+/*
+ * ti_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+
+static int ti_buf_put(struct circ_buf *cb, const char *buf, int count)
+{
+       int c, ret = 0;
+
+       while (1) {
+               c = CIRC_SPACE_TO_END(cb->head, cb->tail, TI_WRITE_BUF_SIZE);
+               if (count < c)
+                       c = count;
+               if (c <= 0)
+                       break;
+               memcpy(cb->buf + cb->head, buf, c);
+               cb->head = (cb->head + c) & (TI_WRITE_BUF_SIZE-1);
+               buf += c;
+               count -= c;
+               ret += c;
+       }
+
+       return ret;
+}
+
+
+/*
+ * ti_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+
+static int ti_buf_get(struct circ_buf *cb, char *buf, int count)
+{
+       int c, ret = 0;
+
+       while (1) {
+               c = CIRC_CNT_TO_END(cb->head, cb->tail, TI_WRITE_BUF_SIZE);
+               if (count < c)
+                       c = count;
+               if (c <= 0)
+                       break;
+               memcpy(buf, cb->buf + cb->tail, c);
+               cb->tail = (cb->tail + c) & (TI_WRITE_BUF_SIZE-1);
+               buf += c;
+               count -= c;
+               ret += c;
+       }
+
+       return ret;
+}
diff --git a/drivers/usb/serial/ti_usb_3410_5052.h b/drivers/usb/serial/ti_usb_3410_5052.h
new file mode 100644 (file)
index 0000000..02c1aeb
--- /dev/null
@@ -0,0 +1,224 @@
+/* vi: ts=8 sw=8
+ *
+ * TI 3410/5052 USB Serial Driver Header
+ *
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * This driver is based on the Linux io_ti driver, which is
+ *   Copyright (C) 2000-2002 Inside Out Networks
+ *   Copyright (C) 2001-2002 Greg Kroah-Hartman
+ *
+ * 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.
+ *
+ * For questions or problems with this driver, contact Texas Instruments
+ * technical support, or Al Borchers <alborchers@steinerpoint.com>, or
+ * Peter Berger <pberger@brimson.com>.
+ */
+
+#ifndef _TI_3410_5052_H_
+#define _TI_3410_5052_H_
+
+/* Configuration ids */
+#define TI_BOOT_CONFIG                 1
+#define TI_ACTIVE_CONFIG               2
+
+/* Vendor and product ids */
+#define TI_VENDOR_ID                   0x0451
+#define TI_3410_PRODUCT_ID             0x3410
+#define TI_5052_BOOT_PRODUCT_ID                0x5052  /* no EEPROM, no firmware */
+#define TI_5152_BOOT_PRODUCT_ID                0x5152  /* no EEPROM, no firmware */
+#define TI_5052_EEPROM_PRODUCT_ID      0x505A  /* EEPROM, no firmware */
+#define TI_5052_FIRMWARE_PRODUCT_ID    0x505F  /* firmware is running */
+
+/* Commands */
+#define TI_GET_VERSION                 0x01
+#define TI_GET_PORT_STATUS             0x02
+#define TI_GET_PORT_DEV_INFO           0x03
+#define TI_GET_CONFIG                  0x04
+#define TI_SET_CONFIG                  0x05
+#define TI_OPEN_PORT                   0x06
+#define TI_CLOSE_PORT                  0x07
+#define TI_START_PORT                  0x08
+#define TI_STOP_PORT                   0x09
+#define TI_TEST_PORT                   0x0A
+#define TI_PURGE_PORT                  0x0B
+#define TI_RESET_EXT_DEVICE            0x0C
+#define TI_WRITE_DATA                  0x80
+#define TI_READ_DATA                   0x81
+#define TI_REQ_TYPE_CLASS              0x82
+
+/* Module identifiers */
+#define TI_I2C_PORT                    0x01
+#define TI_IEEE1284_PORT               0x02
+#define TI_UART1_PORT                  0x03
+#define TI_UART2_PORT                  0x04
+#define TI_RAM_PORT                    0x05
+
+/* Modem status */
+#define TI_MSR_DELTA_CTS               0x01
+#define TI_MSR_DELTA_DSR               0x02
+#define TI_MSR_DELTA_RI                        0x04
+#define TI_MSR_DELTA_CD                        0x08
+#define TI_MSR_CTS                     0x10
+#define TI_MSR_DSR                     0x20
+#define TI_MSR_RI                      0x40
+#define TI_MSR_CD                      0x80
+#define TI_MSR_DELTA_MASK              0x0F
+#define TI_MSR_MASK                    0xF0
+
+/* Line status */
+#define TI_LSR_OVERRUN_ERROR           0x01
+#define TI_LSR_PARITY_ERROR            0x02
+#define TI_LSR_FRAMING_ERROR           0x04
+#define TI_LSR_BREAK                   0x08
+#define TI_LSR_ERROR                   0x0F
+#define TI_LSR_RX_FULL                 0x10
+#define TI_LSR_TX_EMPTY                        0x20
+
+/* Line control */
+#define TI_LCR_BREAK                   0x40
+
+/* Modem control */
+#define TI_MCR_LOOP                    0x04
+#define TI_MCR_DTR                     0x10
+#define TI_MCR_RTS                     0x20
+
+/* Mask settings */
+#define TI_UART_ENABLE_RTS_IN          0x0001
+#define TI_UART_DISABLE_RTS            0x0002
+#define TI_UART_ENABLE_PARITY_CHECKING 0x0008
+#define TI_UART_ENABLE_DSR_OUT         0x0010
+#define TI_UART_ENABLE_CTS_OUT         0x0020
+#define TI_UART_ENABLE_X_OUT           0x0040
+#define TI_UART_ENABLE_XA_OUT          0x0080
+#define TI_UART_ENABLE_X_IN            0x0100
+#define TI_UART_ENABLE_DTR_IN          0x0800
+#define TI_UART_DISABLE_DTR            0x1000
+#define TI_UART_ENABLE_MS_INTS         0x2000
+#define TI_UART_ENABLE_AUTO_START_DMA  0x4000
+
+/* Parity */
+#define TI_UART_NO_PARITY              0x00
+#define TI_UART_ODD_PARITY             0x01
+#define TI_UART_EVEN_PARITY            0x02
+#define TI_UART_MARK_PARITY            0x03
+#define TI_UART_SPACE_PARITY           0x04
+
+/* Stop bits */
+#define TI_UART_1_STOP_BITS            0x00
+#define TI_UART_1_5_STOP_BITS          0x01
+#define TI_UART_2_STOP_BITS            0x02
+
+/* Bits per character */
+#define TI_UART_5_DATA_BITS            0x00
+#define TI_UART_6_DATA_BITS            0x01
+#define TI_UART_7_DATA_BITS            0x02
+#define TI_UART_8_DATA_BITS            0x03
+
+/* 232/485 modes */
+#define TI_UART_232                    0x00
+#define TI_UART_485_RECEIVER_DISABLED  0x01
+#define TI_UART_485_RECEIVER_ENABLED   0x02
+
+/* Pipe transfer mode and timeout */
+#define TI_PIPE_MODE_CONTINOUS         0x01
+#define TI_PIPE_MODE_MASK              0x03
+#define TI_PIPE_TIMEOUT_MASK           0x7C
+#define TI_PIPE_TIMEOUT_ENABLE         0x80
+
+/* Config struct */
+struct ti_uart_config {
+       __u16   wBaudRate;
+       __u16   wFlags;
+       __u8    bDataBits;
+       __u8    bParity;
+       __u8    bStopBits;
+       char    cXon;
+       char    cXoff;
+       __u8    bUartMode;
+} __attribute__((packed));
+
+/* Get port status */
+struct ti_port_status {
+       __u8    bCmdCode;
+       __u8    bModuleId;
+       __u8    bErrorCode;
+       __u8    bMSR;
+       __u8    bLSR;
+} __attribute__((packed));
+
+/* Purge modes */
+#define TI_PURGE_OUTPUT                        0x00
+#define TI_PURGE_INPUT                 0x80
+
+/* Read/Write data */
+#define TI_RW_DATA_ADDR_SFR            0x10
+#define TI_RW_DATA_ADDR_IDATA          0x20
+#define TI_RW_DATA_ADDR_XDATA          0x30
+#define TI_RW_DATA_ADDR_CODE           0x40
+#define TI_RW_DATA_ADDR_GPIO           0x50
+#define TI_RW_DATA_ADDR_I2C            0x60
+#define TI_RW_DATA_ADDR_FLASH          0x70
+#define TI_RW_DATA_ADDR_DSP            0x80
+
+#define TI_RW_DATA_UNSPECIFIED         0x00
+#define TI_RW_DATA_BYTE                        0x01
+#define TI_RW_DATA_WORD                        0x02
+#define TI_RW_DATA_DOUBLE_WORD         0x04
+
+struct ti_write_data_bytes {
+       __u8    bAddrType;
+       __u8    bDataType;
+       __u8    bDataCounter;
+       __be16  wBaseAddrHi;
+       __be16  wBaseAddrLo;
+       __u8    bData[0];
+} __attribute__((packed));
+
+struct ti_read_data_request {
+       __u8    bAddrType;
+       __u8    bDataType;
+       __u8    bDataCounter;
+       __be16  wBaseAddrHi;
+       __be16  wBaseAddrLo;
+} __attribute__((packed));
+
+struct ti_read_data_bytes {
+       __u8    bCmdCode;
+       __u8    bModuleId;
+       __u8    bErrorCode;
+       __u8    bData[0];
+} __attribute__((packed));
+
+/* Interrupt struct */
+struct ti_interrupt {
+       __u8    bICode;
+       __u8    bIInfo;
+} __attribute__((packed));
+
+/* Interrupt codes */
+#define TI_GET_PORT_FROM_CODE(c)       (((c) >> 4) - 3)
+#define TI_GET_FUNC_FROM_CODE(c)       ((c) & 0x0f)
+#define TI_CODE_HARDWARE_ERROR         0xFF
+#define TI_CODE_DATA_ERROR             0x03
+#define TI_CODE_MODEM_STATUS           0x04
+
+/* Download firmware max packet size */
+#define TI_DOWNLOAD_MAX_PACKET_SIZE    64
+
+/* Firmware image header */
+struct ti_firmware_header {
+       __le16  wLength;
+       __u8    bCheckSum;
+} __attribute__((packed));
+
+/* UART addresses */
+#define TI_UART1_BASE_ADDR             0xFFA0  /* UART 1 base address */
+#define TI_UART2_BASE_ADDR             0xFFB0  /* UART 2 base address */
+#define TI_UART_OFFSET_LCR             0x0002  /* UART MCR register offset */
+#define TI_UART_OFFSET_MCR             0x0004  /* UART MCR register offset */
+
+#endif /* _TI_3410_5052_H_ */
diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c
new file mode 100644 (file)
index 0000000..1aa9112
--- /dev/null
@@ -0,0 +1,676 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *     Au1100 LCD Driver.
+ *
+ * Copyright 2002 MontaVista Software
+ * Author: MontaVista Software, Inc.
+ *             ppopov@mvista.com or source@mvista.com
+ *
+ * Copyright 2002 Alchemy Semiconductor
+ * Author: Alchemy Semiconductor
+ *
+ * Based on:
+ * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device
+ *  Created 28 Dec 1997 by Geert Uytterhoeven
+ *
+ *  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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/au1000.h>
+#include <asm/pb1100.h>
+#include "au1100fb.h"
+
+#include <video/fbcon.h>
+#include <video/fbcon-mfb.h>
+#include <video/fbcon-cfb2.h>
+#include <video/fbcon-cfb4.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+
+/*
+ * Sanity check. If this is a new Au1100 based board, search for
+ * the PB1100 ifdefs to make sure you modify the code accordingly.
+ */
+#if defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100) || defined(CONFIG_MIPS_HYDROGEN3)
+#else
+error Unknown Au1100 board
+#endif
+
+#define CMAPSIZE 16
+
+static int my_lcd_index; /* default is zero */
+struct known_lcd_panels *p_lcd;
+AU1100_LCD *p_lcd_reg = (AU1100_LCD *)AU1100_LCD_ADDR;
+
+struct au1100fb_info {
+       struct fb_info_gen gen;
+       unsigned long fb_virt_start;
+       unsigned long fb_size;
+       unsigned long fb_phys;
+       int mmaped;
+       int nohwcursor;
+
+       struct { unsigned red, green, blue, pad; } palette[256];
+
+#if defined(FBCON_HAS_CFB16)
+       u16 fbcon_cmap16[16];
+#endif
+};
+
+
+struct au1100fb_par {
+        struct fb_var_screeninfo var;
+
+       int line_length;  // in bytes
+       int cmap_len;     // color-map length
+};
+
+
+static struct au1100fb_info fb_info;
+static struct au1100fb_par current_par;
+static struct display disp;
+
+int au1100fb_init(void);
+void au1100fb_setup(char *options, int *ints);
+static int au1100fb_mmap(struct fb_info *fb, struct file *file,
+               struct vm_area_struct *vma);
+static int au1100_blank(int blank_mode, struct fb_info_gen *info);
+static int au1100fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+                         u_long arg, int con, struct fb_info *info);
+
+void au1100_nocursor(struct display *p, int mode, int xx, int yy){};
+
+static struct fb_ops au1100fb_ops = {
+       owner:          THIS_MODULE,
+       fb_get_fix:     fbgen_get_fix,
+       fb_get_var:     fbgen_get_var,
+       fb_set_var:     fbgen_set_var,
+       fb_get_cmap:    fbgen_get_cmap,
+       fb_set_cmap:    fbgen_set_cmap,
+       fb_pan_display: fbgen_pan_display,
+        fb_ioctl:       au1100fb_ioctl,
+       fb_mmap:        au1100fb_mmap,
+};
+
+static void au1100_detect(void)
+{
+       /*
+        *  This function should detect the current video mode settings
+        *  and store it as the default video mode
+        */
+
+       /*
+        * Yeh, well, we're not going to change any settings so we're
+        * always stuck with the default ...
+        */
+
+}
+
+static int au1100_encode_fix(struct fb_fix_screeninfo *fix,
+               const void *_par, struct fb_info_gen *_info)
+{
+        struct au1100fb_info *info = (struct au1100fb_info *) _info;
+        struct au1100fb_par *par = (struct au1100fb_par *) _par;
+       struct fb_var_screeninfo *var = &par->var;
+
+       memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+
+       fix->smem_start = info->fb_phys;
+       fix->smem_len = info->fb_size;
+       fix->type = FB_TYPE_PACKED_PIXELS;
+       fix->type_aux = 0;
+        fix->visual = (var->bits_per_pixel == 8) ?
+               FB_VISUAL_PSEUDOCOLOR   : FB_VISUAL_TRUECOLOR;
+       fix->ywrapstep = 0;
+       fix->xpanstep = 1;
+       fix->ypanstep = 1;
+       fix->line_length = current_par.line_length;
+       return 0;
+}
+
+static void set_color_bitfields(struct fb_var_screeninfo *var)
+{
+       switch (var->bits_per_pixel) {
+       case 8:
+               var->red.offset = 0;
+               var->red.length = 8;
+               var->green.offset = 0;
+               var->green.length = 8;
+               var->blue.offset = 0;
+               var->blue.length = 8;
+               var->transp.offset = 0;
+               var->transp.length = 0;
+               break;
+       case 16:        /* RGB 565 */
+               var->red.offset = 11;
+               var->red.length = 5;
+               var->green.offset = 5;
+               var->green.length = 6;
+               var->blue.offset = 0;
+               var->blue.length = 5;
+               var->transp.offset = 0;
+               var->transp.length = 0;
+               break;
+       }
+
+       var->red.msb_right = 0;
+       var->green.msb_right = 0;
+       var->blue.msb_right = 0;
+       var->transp.msb_right = 0;
+}
+
+static int au1100_decode_var(const struct fb_var_screeninfo *var,
+               void *_par, struct fb_info_gen *_info)
+{
+
+       struct au1100fb_par *par = (struct au1100fb_par *)_par;
+
+       /*
+        * Don't allow setting any of these yet: xres and yres don't
+        * make sense for LCD panels.
+        */
+       if (var->xres != p_lcd->xres ||
+           var->yres != p_lcd->yres ||
+           var->xres != p_lcd->xres ||
+           var->yres != p_lcd->yres) {
+               return -EINVAL;
+       }
+       if(var->bits_per_pixel != p_lcd->bpp) {
+               return -EINVAL;
+       }
+
+       memset(par, 0, sizeof(struct au1100fb_par));
+       par->var = *var;
+
+       /* FIXME */
+       switch (var->bits_per_pixel) {
+               case 8:
+                       par->var.bits_per_pixel = 8;
+                       break;
+               case 16:
+                       par->var.bits_per_pixel = 16;
+                       break;
+               default:
+                       printk("color depth %d bpp not supported\n",
+                                       var->bits_per_pixel);
+                       return -EINVAL;
+
+       }
+       set_color_bitfields(&par->var);
+       par->cmap_len = (par->var.bits_per_pixel == 8) ? 256 : 16;
+       return 0;
+}
+
+static int au1100_encode_var(struct fb_var_screeninfo *var,
+               const void *par, struct fb_info_gen *_info)
+{
+
+       *var = ((struct au1100fb_par *)par)->var;
+       return 0;
+}
+
+static void
+au1100_get_par(void *_par, struct fb_info_gen *_info)
+{
+       *(struct au1100fb_par *)_par = current_par;
+}
+
+static void au1100_set_par(const void *par, struct fb_info_gen *info)
+{
+       /* nothing to do: we don't change any settings */
+}
+
+static int au1100_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+                        unsigned *blue, unsigned *transp,
+                        struct fb_info *info)
+{
+
+       struct au1100fb_info* i = (struct au1100fb_info*)info;
+
+       if (regno > 255)
+               return 1;
+
+       *red    = i->palette[regno].red;
+       *green  = i->palette[regno].green;
+       *blue   = i->palette[regno].blue;
+       *transp = 0;
+
+       return 0;
+}
+
+static int au1100_setcolreg(unsigned regno, unsigned red, unsigned green,
+                        unsigned blue, unsigned transp,
+                        struct fb_info *info)
+{
+       struct au1100fb_info* i = (struct au1100fb_info *)info;
+       u32 rgbcol;
+
+       if (regno > 255)
+               return 1;
+
+       i->palette[regno].red    = red;
+       i->palette[regno].green  = green;
+       i->palette[regno].blue   = blue;
+
+       switch(p_lcd->bpp) {
+#ifdef FBCON_HAS_CFB8
+       case 8:
+               red >>= 10;
+               green >>= 10;
+               blue >>= 10;
+               p_lcd_reg->lcd_pallettebase[regno] = (blue&0x1f) |
+                       ((green&0x3f)<<5) | ((red&0x1f)<<11);
+               break;
+#endif
+#ifdef FBCON_HAS_CFB16
+       case 16:
+               i->fbcon_cmap16[regno] =
+                       ((red & 0xf800) >> 0) |
+                       ((green & 0xfc00) >> 5) |
+                       ((blue & 0xf800) >> 11);
+               break;
+#endif
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+
+static int  au1100_blank(int blank_mode, struct fb_info_gen *_info)
+{
+
+       switch (blank_mode) {
+       case VESA_NO_BLANKING:
+               /* turn on panel */
+               //printk("turn on panel\n");
+#ifdef CONFIG_MIPS_PB1100
+               p_lcd_reg->lcd_control |= LCD_CONTROL_GO;
+               au_writew(au_readw(PB1100_G_CONTROL) | p_lcd->mode_backlight,
+                       PB1100_G_CONTROL);
+#endif
+#ifdef CONFIG_MIPS_HYDROGEN3
+               /*  Turn controller & power supply on,  GPIO213 */
+               au_writel(0x20002000, 0xB1700008);
+               au_writel(0x00040000, 0xB1900108);
+               au_writel(0x01000100, 0xB1700008);
+#endif
+               au_sync();
+               break;
+
+       case VESA_VSYNC_SUSPEND:
+       case VESA_HSYNC_SUSPEND:
+       case VESA_POWERDOWN:
+               /* turn off panel */
+               //printk("turn off panel\n");
+#ifdef CONFIG_MIPS_PB1100
+               au_writew(au_readw(PB1100_G_CONTROL) & ~p_lcd->mode_backlight,
+                       PB1100_G_CONTROL);
+               p_lcd_reg->lcd_control &= ~LCD_CONTROL_GO;
+#endif
+               au_sync();
+               break;
+       default:
+               break;
+
+       }
+       return 0;
+}
+
+static void au1100_set_disp(const void *unused, struct display *disp,
+                        struct fb_info_gen *info)
+{
+       disp->screen_base = (char *)fb_info.fb_virt_start;
+
+       switch (disp->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+       case 8:
+               disp->dispsw = &fbcon_cfb8;
+               if (fb_info.nohwcursor)
+                       fbcon_cfb8.cursor = au1100_nocursor;
+               break;
+#endif
+#ifdef FBCON_HAS_CFB16
+       case 16:
+               disp->dispsw = &fbcon_cfb16;
+               disp->dispsw_data = fb_info.fbcon_cmap16;
+               if (fb_info.nohwcursor)
+                       fbcon_cfb16.cursor = au1100_nocursor;
+               break;
+#endif
+       default:
+               disp->dispsw = &fbcon_dummy;
+               disp->dispsw_data = NULL;
+               break;
+       }
+}
+
+static int
+au1100fb_mmap(struct fb_info *_fb,
+            struct file *file,
+            struct vm_area_struct *vma)
+{
+       unsigned int len;
+       unsigned long start=0, off;
+       struct au1100fb_info *fb = (struct au1100fb_info *)_fb;
+
+       if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
+               return -EINVAL;
+       }
+
+       start = fb_info.fb_phys & PAGE_MASK;
+       len = PAGE_ALIGN((start & ~PAGE_MASK) + fb_info.fb_size);
+
+       off = vma->vm_pgoff << PAGE_SHIFT;
+
+       if ((vma->vm_end - vma->vm_start + off) > len) {
+               return -EINVAL;
+       }
+
+       off += start;
+       vma->vm_pgoff = off >> PAGE_SHIFT;
+
+       pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK;
+       //pgprot_val(vma->vm_page_prot) |= _CACHE_CACHABLE_NONCOHERENT;
+       pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6
+
+       /* This is an IO map - tell maydump to skip this VMA */
+       vma->vm_flags |= VM_IO;
+
+       if (io_remap_page_range(vma, vma->vm_start, off,
+                               vma->vm_end - vma->vm_start,
+                               vma->vm_page_prot)) {
+               return -EAGAIN;
+       }
+
+       fb->mmaped = 1;
+       return 0;
+}
+
+int au1100_pan_display(const struct fb_var_screeninfo *var,
+                      struct fb_info_gen *info)
+{
+       return 0;
+}
+
+static int au1100fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+                         u_long arg, int con, struct fb_info *info)
+{
+       /* nothing to do yet */
+       return -EINVAL;
+}
+
+static struct fbgen_hwswitch au1100_switch = {
+       au1100_detect,
+       au1100_encode_fix,
+       au1100_decode_var,
+       au1100_encode_var,
+       au1100_get_par,
+       au1100_set_par,
+       au1100_getcolreg,
+       au1100_setcolreg,
+       au1100_pan_display,
+       au1100_blank,
+       au1100_set_disp
+};
+
+
+int au1100_setmode(void)
+{
+       int words;
+
+       /* FIXME Need to accomodate for swivel mode and 12bpp, <8bpp*/
+       switch (p_lcd->mode_control & LCD_CONTROL_SM)
+       {
+               case LCD_CONTROL_SM_0:
+               case LCD_CONTROL_SM_180:
+               words = (p_lcd->xres * p_lcd->yres * p_lcd->bpp) / 32;
+                       break;
+               case LCD_CONTROL_SM_90:
+               case LCD_CONTROL_SM_270:
+                       /* is this correct? */
+               words = (p_lcd->xres * p_lcd->bpp) / 8;
+                       break;
+               default:
+                       printk("mode_control reg not initialized\n");
+                       return -EINVAL;
+       }
+
+       /*
+        * Setup LCD controller
+        */
+
+       p_lcd_reg->lcd_control = p_lcd->mode_control;
+       p_lcd_reg->lcd_intstatus = 0;
+       p_lcd_reg->lcd_intenable = 0;
+       p_lcd_reg->lcd_horztiming = p_lcd->mode_horztiming;
+       p_lcd_reg->lcd_verttiming = p_lcd->mode_verttiming;
+       p_lcd_reg->lcd_clkcontrol = p_lcd->mode_clkcontrol;
+       p_lcd_reg->lcd_words = words - 1;
+       p_lcd_reg->lcd_dmaaddr0 = fb_info.fb_phys;
+
+       /* turn on panel */
+#ifdef CONFIG_MIPS_PB1100
+       au_writew(au_readw(PB1100_G_CONTROL) | p_lcd->mode_backlight,
+                       PB1100_G_CONTROL);
+#endif
+#ifdef CONFIG_MIPS_HYDROGEN3
+       /*  Turn controller & power supply on,  GPIO213 */
+       au_writel(0x20002000, 0xB1700008);
+       au_writel(0x00040000, 0xB1900108);
+       au_writel(0x01000100, 0xB1700008);
+#endif
+
+       p_lcd_reg->lcd_control |= LCD_CONTROL_GO;
+
+       return 0;
+}
+
+
+int __init au1100fb_init(void)
+{
+       uint32 sys_clksrc;
+       unsigned long page;
+
+       /*
+       * Get the panel information/display mode and update the registry
+       */
+       p_lcd = &panels[my_lcd_index];
+
+       switch (p_lcd->mode_control & LCD_CONTROL_SM)
+       {
+               case LCD_CONTROL_SM_0:
+               case LCD_CONTROL_SM_180:
+               p_lcd->xres =
+                       (p_lcd->mode_horztiming & LCD_HORZTIMING_PPL) + 1;
+               p_lcd->yres =
+                       (p_lcd->mode_verttiming & LCD_VERTTIMING_LPP) + 1;
+                       break;
+               case LCD_CONTROL_SM_90:
+               case LCD_CONTROL_SM_270:
+               p_lcd->yres =
+                       (p_lcd->mode_horztiming & LCD_HORZTIMING_PPL) + 1;
+               p_lcd->xres =
+                       (p_lcd->mode_verttiming & LCD_VERTTIMING_LPP) + 1;
+                       break;
+       }
+
+       /*
+        * Panel dimensions x bpp must be divisible by 32
+        */
+       if (((p_lcd->yres * p_lcd->bpp) % 32) != 0)
+               printk("VERT %% 32\n");
+       if (((p_lcd->xres * p_lcd->bpp) % 32) != 0)
+               printk("HORZ %% 32\n");
+
+       /*
+        * Allocate LCD framebuffer from system memory
+        */
+       fb_info.fb_size = (p_lcd->xres * p_lcd->yres * p_lcd->bpp) / 8;
+
+       current_par.var.xres = p_lcd->xres;
+       current_par.var.xres_virtual = p_lcd->xres;
+       current_par.var.yres = p_lcd->yres;
+       current_par.var.yres_virtual = p_lcd->yres;
+       current_par.var.bits_per_pixel = p_lcd->bpp;
+
+       /* FIX!!! only works for 8/16 bpp */
+       current_par.line_length = p_lcd->xres * p_lcd->bpp / 8; /* in bytes */
+       fb_info.fb_virt_start = (unsigned long )
+               __get_free_pages(GFP_ATOMIC | GFP_DMA,
+                               get_order(fb_info.fb_size + 0x1000));
+       if (!fb_info.fb_virt_start) {
+               printk("Unable to allocate fb memory\n");
+               return -ENOMEM;
+       }
+       fb_info.fb_phys = virt_to_bus((void *)fb_info.fb_virt_start);
+
+       /*
+        * Set page reserved so that mmap will work. This is necessary
+        * since we'll be remapping normal memory.
+        */
+       for (page = fb_info.fb_virt_start;
+            page < PAGE_ALIGN(fb_info.fb_virt_start + fb_info.fb_size);
+            page += PAGE_SIZE) {
+               SetPageReserved(virt_to_page(page));
+       }
+
+       memset((void *)fb_info.fb_virt_start, 0, fb_info.fb_size);
+
+       /* set freqctrl now to allow more time to stabilize */
+       /* zero-out out LCD bits */
+       sys_clksrc = au_readl(SYS_CLKSRC) & ~0x000003e0;
+       sys_clksrc |= p_lcd->mode_toyclksrc;
+       au_writel(sys_clksrc, SYS_CLKSRC);
+
+       /* FIXME add check to make sure auxpll is what is expected! */
+       au1100_setmode();
+
+       fb_info.gen.parsize = sizeof(struct au1100fb_par);
+       fb_info.gen.fbhw = &au1100_switch;
+
+       strcpy(fb_info.gen.info.modename, "Au1100 LCD");
+       fb_info.gen.info.changevar = NULL;
+       fb_info.gen.info.node = -1;
+
+       fb_info.gen.info.fbops = &au1100fb_ops;
+       fb_info.gen.info.disp = &disp;
+       fb_info.gen.info.switch_con = &fbgen_switch;
+       fb_info.gen.info.updatevar = &fbgen_update_var;
+       fb_info.gen.info.blank = &fbgen_blank;
+       fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
+
+       /* This should give a reasonable default video mode */
+       fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
+       fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
+       fbgen_set_disp(-1, &fb_info.gen);
+       fbgen_install_cmap(0, &fb_info.gen);
+       if (register_framebuffer(&fb_info.gen.info) < 0)
+               return -EINVAL;
+       printk(KERN_INFO "fb%d: %s frame buffer device\n",
+                       GET_FB_IDX(fb_info.gen.info.node),
+                       fb_info.gen.info.modename);
+
+       return 0;
+}
+
+
+void au1100fb_cleanup(struct fb_info *info)
+{
+       unregister_framebuffer(info);
+}
+
+
+void au1100fb_setup(char *options, int *ints)
+{
+       char* this_opt;
+       int i;
+       int num_panels = sizeof(panels)/sizeof(struct known_lcd_panels);
+
+
+       if (!options || !*options)
+               return;
+
+       for(this_opt=strtok(options, ","); this_opt;
+           this_opt=strtok(NULL, ",")) {
+               if (!strncmp(this_opt, "panel:", 6)) {
+#if defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_DB1100)
+                       /* Read Pb1100 Switch S10 ? */
+                       if (!strncmp(this_opt+6, "s10", 3))
+                       {
+                               int panel;
+                               panel = *(volatile int *)0xAE000008; /* BCSR SWITCHES */
+                               panel >>= 8;
+                               panel &= 0x0F;
+                               if (panel >= num_panels) panel = 0;
+                               my_lcd_index = panel;
+                       }
+                       else
+#endif
+                       /* Get the panel name, everything else if fixed */
+                       for (i=0; i<num_panels; i++) {
+                               if (!strncmp(this_opt+6, panels[i].panel_name,
+                                                       strlen(this_opt))) {
+                                       my_lcd_index = i;
+                                       break;
+                               }
+                       }
+               }
+               else if (!strncmp(this_opt, "nohwcursor", 10)) {
+                       printk("nohwcursor\n");
+                       fb_info.nohwcursor = 1;
+               }
+       }
+
+       printk("au1100fb: Panel %d %s\n", my_lcd_index,
+               panels[my_lcd_index].panel_name);
+}
+
+
+
+#ifdef MODULE
+MODULE_LICENSE("GPL");
+int init_module(void)
+{
+       return au1100fb_init();
+}
+
+void cleanup_module(void)
+{
+       au1100fb_cleanup(void);
+}
+
+MODULE_AUTHOR("Pete Popov <ppopov@mvista.com>");
+MODULE_DESCRIPTION("Au1100 LCD framebuffer device driver");
+#endif /* MODULE */
diff --git a/drivers/video/au1100fb.h b/drivers/video/au1100fb.h
new file mode 100644 (file)
index 0000000..657c560
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *     Hardware definitions for the Au1100 LCD controller
+ *
+ * Copyright 2002 MontaVista Software
+ * Copyright 2002 Alchemy Semiconductor
+ * Author:     Alchemy Semiconductor, MontaVista Software
+ *
+ *  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.
+ */
+
+#ifndef _AU1100LCD_H
+#define _AU1100LCD_H
+
+/********************************************************************/
+#define uint32 unsigned long
+typedef volatile struct
+{
+       uint32  lcd_control;
+       uint32  lcd_intstatus;
+       uint32  lcd_intenable;
+       uint32  lcd_horztiming;
+       uint32  lcd_verttiming;
+       uint32  lcd_clkcontrol;
+       uint32  lcd_dmaaddr0;
+       uint32  lcd_dmaaddr1;
+       uint32  lcd_words;
+       uint32  lcd_pwmdiv;
+       uint32  lcd_pwmhi;
+       uint32  reserved[(0x0400-0x002C)/4];
+       uint32  lcd_pallettebase[256];
+
+} AU1100_LCD;
+
+/********************************************************************/
+
+#define AU1100_LCD_ADDR                0xB5000000
+
+/*
+ * Register bit definitions
+ */
+
+/* lcd_control */
+#define LCD_CONTROL_SBPPF              (7<<18)
+#define LCD_CONTROL_SBPPF_655  (0<<18)
+#define LCD_CONTROL_SBPPF_565  (1<<18)
+#define LCD_CONTROL_SBPPF_556  (2<<18)
+#define LCD_CONTROL_SBPPF_1555 (3<<18)
+#define LCD_CONTROL_SBPPF_5551 (4<<18)
+#define LCD_CONTROL_WP                 (1<<17)
+#define LCD_CONTROL_WD                 (1<<16)
+#define LCD_CONTROL_C                  (1<<15)
+#define LCD_CONTROL_SM                 (3<<13)
+#define LCD_CONTROL_SM_0               (0<<13)
+#define LCD_CONTROL_SM_90              (1<<13)
+#define LCD_CONTROL_SM_180             (2<<13)
+#define LCD_CONTROL_SM_270             (3<<13)
+#define LCD_CONTROL_DB                 (1<<12)
+#define LCD_CONTROL_CCO                        (1<<11)
+#define LCD_CONTROL_DP                 (1<<10)
+#define LCD_CONTROL_PO                 (3<<8)
+#define LCD_CONTROL_PO_00              (0<<8)
+#define LCD_CONTROL_PO_01              (1<<8)
+#define LCD_CONTROL_PO_10              (2<<8)
+#define LCD_CONTROL_PO_11              (3<<8)
+#define LCD_CONTROL_MPI                        (1<<7)
+#define LCD_CONTROL_PT                 (1<<6)
+#define LCD_CONTROL_PC                 (1<<5)
+#define LCD_CONTROL_BPP                        (7<<1)
+#define LCD_CONTROL_BPP_1              (0<<1)
+#define LCD_CONTROL_BPP_2              (1<<1)
+#define LCD_CONTROL_BPP_4              (2<<1)
+#define LCD_CONTROL_BPP_8              (3<<1)
+#define LCD_CONTROL_BPP_12             (4<<1)
+#define LCD_CONTROL_BPP_16             (5<<1)
+#define LCD_CONTROL_GO                 (1<<0)
+
+/* lcd_intstatus, lcd_intenable */
+#define LCD_INT_SD                             (1<<7)
+#define LCD_INT_OF                             (1<<6)
+#define LCD_INT_UF                             (1<<5)
+#define LCD_INT_SA                             (1<<3)
+#define LCD_INT_SS                             (1<<2)
+#define LCD_INT_S1                             (1<<1)
+#define LCD_INT_S0                             (1<<0)
+
+/* lcd_horztiming */
+#define LCD_HORZTIMING_HN2             (255<<24)
+#define LCD_HORZTIMING_HN2_N(N)        (((N)-1)<<24)
+#define LCD_HORZTIMING_HN1             (255<<16)
+#define LCD_HORZTIMING_HN1_N(N)        (((N)-1)<<16)
+#define LCD_HORZTIMING_HPW             (63<<10)
+#define LCD_HORZTIMING_HPW_N(N)        (((N)-1)<<10)
+#define LCD_HORZTIMING_PPL             (1023<<0)
+#define LCD_HORZTIMING_PPL_N(N)        (((N)-1)<<0)
+
+/* lcd_verttiming */
+#define LCD_VERTTIMING_VN2             (255<<24)
+#define LCD_VERTTIMING_VN2_N(N)        (((N)-1)<<24)
+#define LCD_VERTTIMING_VN1             (255<<16)
+#define LCD_VERTTIMING_VN1_N(N)        (((N)-1)<<16)
+#define LCD_VERTTIMING_VPW             (63<<10)
+#define LCD_VERTTIMING_VPW_N(N)        (((N)-1)<<10)
+#define LCD_VERTTIMING_LPP             (1023<<0)
+#define LCD_VERTTIMING_LPP_N(N)        (((N)-1)<<0)
+
+/* lcd_clkcontrol */
+#define LCD_CLKCONTROL_IB              (1<<18)
+#define LCD_CLKCONTROL_IC              (1<<17)
+#define LCD_CLKCONTROL_IH              (1<<16)
+#define LCD_CLKCONTROL_IV              (1<<15)
+#define LCD_CLKCONTROL_BF              (31<<10)
+#define LCD_CLKCONTROL_BF_N(N) (((N)-1)<<10)
+#define LCD_CLKCONTROL_PCD             (1023<<0)
+#define LCD_CLKCONTROL_PCD_N(N)        ((N)<<0)
+
+/* lcd_pwmdiv */
+#define LCD_PWMDIV_EN                  (1<<12)
+#define LCD_PWMDIV_PWMDIV              (2047<<0)
+#define LCD_PWMDIV_PWMDIV_N(N) (((N)-1)<<0)
+
+/* lcd_pwmhi */
+#define LCD_PWMHI_PWMHI1               (2047<<12)
+#define LCD_PWMHI_PWMHI1_N(N)  ((N)<<12)
+#define LCD_PWMHI_PWMHI0               (2047<<0)
+#define LCD_PWMHI_PWMHI0_N(N)  ((N)<<0)
+
+/* lcd_pallettebase - MONOCHROME */
+#define LCD_PALLETTE_MONO_MI           (15<<0)
+#define LCD_PALLETTE_MONO_MI_N(N)      ((N)<<0)
+
+/* lcd_pallettebase - COLOR */
+#define LCD_PALLETTE_COLOR_BI          (15<<8)
+#define LCD_PALLETTE_COLOR_BI_N(N)     ((N)<<8)
+#define LCD_PALLETTE_COLOR_GI          (15<<4)
+#define LCD_PALLETTE_COLOR_GI_N(N)     ((N)<<4)
+#define LCD_PALLETTE_COLOR_RI          (15<<0)
+#define LCD_PALLETTE_COLOR_RI_N(N)     ((N)<<0)
+
+/* lcd_palletebase - COLOR TFT PALLETIZED */
+#define LCD_PALLETTE_TFT_DC                    (65535<<0)
+#define LCD_PALLETTE_TFT_DC_N(N)       ((N)<<0)
+
+/********************************************************************/
+
+struct known_lcd_panels
+{
+       uint32 xres;
+       uint32 yres;
+       uint32 bpp;
+       unsigned char  panel_name[256];
+       uint32 mode_control;
+       uint32 mode_horztiming;
+       uint32 mode_verttiming;
+       uint32 mode_clkcontrol;
+       uint32 mode_pwmdiv;
+       uint32 mode_pwmhi;
+       uint32 mode_toyclksrc;
+       uint32 mode_backlight;
+
+};
+
+#if defined(__BIG_ENDIAN)
+#define LCD_DEFAULT_PIX_FORMAT LCD_CONTROL_PO_11
+#else
+#define LCD_DEFAULT_PIX_FORMAT LCD_CONTROL_PO_00
+#endif
+
+/*
+ * The fb driver assumes that AUX PLL is at 48MHz.  That can
+ * cover up to 800x600 resolution; if you need higher resolution,
+ * you should modify the driver as needed, not just this structure.
+ */
+struct known_lcd_panels panels[] =
+{
+       { /* 0: Pb1100 LCDA: Sharp 320x240 TFT panel */
+               320, /* xres */
+               240, /* yres */
+               16,  /* bpp  */
+
+               "Sharp_320x240_16",
+               /* mode_control */
+               ( LCD_CONTROL_SBPPF_565
+               /*LCD_CONTROL_WP*/
+               /*LCD_CONTROL_WD*/
+               | LCD_CONTROL_C
+               | LCD_CONTROL_SM_0
+               /*LCD_CONTROL_DB*/
+               /*LCD_CONTROL_CCO*/
+               /*LCD_CONTROL_DP*/
+               | LCD_DEFAULT_PIX_FORMAT
+               /*LCD_CONTROL_MPI*/
+               | LCD_CONTROL_PT
+               | LCD_CONTROL_PC
+               | LCD_CONTROL_BPP_16 ),
+
+               /* mode_horztiming */
+               ( LCD_HORZTIMING_HN2_N(8)
+               | LCD_HORZTIMING_HN1_N(60)
+               | LCD_HORZTIMING_HPW_N(12)
+               | LCD_HORZTIMING_PPL_N(320) ),
+
+               /* mode_verttiming */
+               ( LCD_VERTTIMING_VN2_N(5)
+               | LCD_VERTTIMING_VN1_N(17)
+               | LCD_VERTTIMING_VPW_N(1)
+               | LCD_VERTTIMING_LPP_N(240) ),
+
+               /* mode_clkcontrol */
+               ( 0
+               /*LCD_CLKCONTROL_IB*/
+               /*LCD_CLKCONTROL_IC*/
+               /*LCD_CLKCONTROL_IH*/
+               /*LCD_CLKCONTROL_IV*/
+               | LCD_CLKCONTROL_PCD_N(1) ),
+
+               /* mode_pwmdiv */
+               0,
+
+               /* mode_pwmhi */
+               0,
+
+               /* mode_toyclksrc */
+               ((1<<7) | (1<<6) | (1<<5)),
+
+               /* mode_backlight */
+               6
+       },
+
+       { /* 1: Pb1100 LCDC 640x480 TFT panel */
+               640, /* xres */
+               480, /* yres */
+               16,  /* bpp  */
+
+               "Generic_640x480_16",
+
+               /* mode_control */
+               0x004806a | LCD_DEFAULT_PIX_FORMAT,
+
+               /* mode_horztiming */
+               0x3434d67f,
+
+               /* mode_verttiming */
+               0x0e0e39df,
+
+               /* mode_clkcontrol */
+               ( 0
+               /*LCD_CLKCONTROL_IB*/
+               /*LCD_CLKCONTROL_IC*/
+               /*LCD_CLKCONTROL_IH*/
+               /*LCD_CLKCONTROL_IV*/
+               | LCD_CLKCONTROL_PCD_N(1) ),
+
+               /* mode_pwmdiv */
+               0,
+
+               /* mode_pwmhi */
+               0,
+
+               /* mode_toyclksrc */
+               ((1<<7) | (1<<6) | (0<<5)),
+
+               /* mode_backlight */
+               7
+       },
+
+       { /* 2: Pb1100 LCDB 640x480 PrimeView TFT panel */
+               640, /* xres */
+               480, /* yres */
+               16,  /* bpp  */
+
+               "PrimeView_640x480_16",
+
+               /* mode_control */
+               0x0004886a | LCD_DEFAULT_PIX_FORMAT,
+
+               /* mode_horztiming */
+               0x0e4bfe7f,
+
+               /* mode_verttiming */
+               0x210805df,
+
+               /* mode_clkcontrol */
+               0x00038001,
+
+               /* mode_pwmdiv */
+               0,
+
+               /* mode_pwmhi */
+               0,
+
+               /* mode_toyclksrc */
+               ((1<<7) | (1<<6) | (0<<5)),
+
+               /* mode_backlight */
+               7
+       },
+
+       { /* 3: Pb1100 800x600x16bpp NEON CRT */
+               800, /* xres */
+               600, /* yres */
+               16,  /* bpp */
+
+               "NEON_800x600_16",
+
+               /* mode_control */
+               0x0004886A | LCD_DEFAULT_PIX_FORMAT,
+
+               /* mode_horztiming */
+               0x005AFF1F,
+
+               /* mode_verttiming */
+               0x16000E57,
+
+               /* mode_clkcontrol */
+               0x00020000,
+
+               /* mode_pwmdiv */
+               0,
+
+               /* mode_pwmhi */
+               0,
+
+               /* mode_toyclksrc */
+               ((1<<7) | (1<<6) | (0<<5)),
+
+               /* mode_backlight */
+               7
+       },
+
+       { /* 4: Pb1100 640x480x16bpp NEON CRT */
+               640, /* xres */
+               480, /* yres */
+               16,  /* bpp */
+
+               "NEON_640x480_16",
+
+               /* mode_control */
+               0x0004886A | LCD_DEFAULT_PIX_FORMAT,
+
+               /* mode_horztiming */
+               0x0052E27F,
+
+               /* mode_verttiming */
+               0x18000DDF,
+
+               /* mode_clkcontrol */
+               0x00020000,
+
+               /* mode_pwmdiv */
+               0,
+
+               /* mode_pwmhi */
+               0,
+
+               /* mode_toyclksrc */
+               ((1<<7) | (1<<6) | (0<<5)),
+
+               /* mode_backlight */
+               7
+       },
+};
+#endif /* _AU1100LCD_H */
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
new file mode 100644 (file)
index 0000000..996d543
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# Backlight & LCD drivers configuration
+#
+
+menuconfig BACKLIGHT_LCD_SUPPORT
+       bool "Backlight & LCD device support"
+       help
+         Enable this to be able to choose the drivers for controlling the
+         backlight and the LCD panel on some platforms, for example on PDAs.
+
+config BACKLIGHT_CLASS_DEVICE
+        tristate "Lowlevel Backlight controls"
+       depends on BACKLIGHT_LCD_SUPPORT
+       default m
+       help
+         This framework adds support for low-level control of the LCD
+          backlight. This includes support for brightness and power.
+
+         To have support for your specific LCD panel you will have to
+         select the proper drivers which depend on this option.
+
+config BACKLIGHT_DEVICE
+       bool
+       depends on BACKLIGHT_CLASS_DEVICE
+       default y
+
+config LCD_CLASS_DEVICE
+        tristate "Lowlevel LCD controls"
+       depends on BACKLIGHT_LCD_SUPPORT
+       default m
+       help
+         This framework adds support for low-level control of LCD.
+         Some framebuffer devices connect to platform-specific LCD modules
+         in order to have a platform-specific way to control the flat panel
+         (contrast and applying power to the LCD (not to the backlight!)).
+
+         To have support for your specific LCD panel you will have to
+         select the proper drivers which depend on this option.
+
+config LCD_DEVICE
+       bool
+       depends on LCD_CLASS_DEVICE
+       default y
+
+config BACKLIGHT_CORGI
+       tristate "Sharp Corgi Backlight Driver (SL-C7xx Series)"
+       depends on BACKLIGHT_DEVICE && PXA_SHARPSL
+       default y
+       help
+         If you have a Sharp Zaurus SL-C7xx, say y to enable the
+         backlight driver.
+
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
new file mode 100644 (file)
index 0000000..9aae884
--- /dev/null
@@ -0,0 +1,5 @@
+# Backlight & LCD drivers
+
+obj-$(CONFIG_LCD_CLASS_DEVICE)     += lcd.o
+obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
+obj-$(CONFIG_BACKLIGHT_CORGI)  += corgi_bl.o
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
new file mode 100644 (file)
index 0000000..23bd9ee
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Backlight Lowlevel Control Abstraction
+ *
+ * Copyright (C) 2003,2004 Hewlett-Packard Company
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/backlight.h>
+#include <linux/notifier.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <asm/bug.h>
+
+static ssize_t backlight_show_power(struct class_device *cdev, char *buf)
+{
+       int rc;
+       struct backlight_device *bd = to_backlight_device(cdev);
+
+       down(&bd->sem);
+       if (likely(bd->props && bd->props->get_power))
+               rc = sprintf(buf, "%d\n", bd->props->get_power(bd));
+       else
+               rc = -ENXIO;
+       up(&bd->sem);
+
+       return rc;
+}
+
+static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count)
+{
+       int rc, power;
+       char *endp;
+       struct backlight_device *bd = to_backlight_device(cdev);
+
+       power = simple_strtoul(buf, &endp, 0);
+       if (*endp && !isspace(*endp))
+               return -EINVAL;
+
+       down(&bd->sem);
+       if (likely(bd->props && bd->props->set_power)) {
+               pr_debug("backlight: set power to %d\n", power);
+               bd->props->set_power(bd, power);
+               rc = count;
+       } else
+               rc = -ENXIO;
+       up(&bd->sem);
+
+       return rc;
+}
+
+static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf)
+{
+       int rc;
+       struct backlight_device *bd = to_backlight_device(cdev);
+
+       down(&bd->sem);
+       if (likely(bd->props && bd->props->get_brightness))
+               rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd));
+       else
+               rc = -ENXIO;
+       up(&bd->sem);
+
+       return rc;
+}
+
+static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count)
+{
+       int rc, brightness;
+       char *endp;
+       struct backlight_device *bd = to_backlight_device(cdev);
+
+       brightness = simple_strtoul(buf, &endp, 0);
+       if (*endp && !isspace(*endp))
+               return -EINVAL;
+
+       down(&bd->sem);
+       if (likely(bd->props && bd->props->set_brightness)) {
+               pr_debug("backlight: set brightness to %d\n", brightness);
+               bd->props->set_brightness(bd, brightness);
+               rc = count;
+       } else
+               rc = -ENXIO;
+       up(&bd->sem);
+
+       return rc;
+}
+
+static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf)
+{
+       int rc;
+       struct backlight_device *bd = to_backlight_device(cdev);
+
+       down(&bd->sem);
+       if (likely(bd->props))
+               rc = sprintf(buf, "%d\n", bd->props->max_brightness);
+       else
+               rc = -ENXIO;
+       up(&bd->sem);
+
+       return rc;
+}
+
+static void backlight_class_release(struct class_device *dev)
+{
+       struct backlight_device *bd = to_backlight_device(dev);
+       kfree(bd);
+}
+
+struct class backlight_class = {
+       .name = "backlight",
+       .release = backlight_class_release,
+};
+
+#define DECLARE_ATTR(_name,_mode,_show,_store)                 \
+{                                                              \
+       .attr   = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE },  \
+       .show   = _show,                                        \
+       .store  = _store,                                       \
+}
+
+static struct class_device_attribute bl_class_device_attributes[] = {
+       DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power),
+       DECLARE_ATTR(brightness, 0644, backlight_show_brightness, backlight_store_brightness),
+       DECLARE_ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
+};
+
+/* This callback gets called when something important happens inside a
+ * framebuffer driver. We're looking if that important event is blanking,
+ * and if it is, we're switching backlight power as well ...
+ */
+static int fb_notifier_callback(struct notifier_block *self,
+                               unsigned long event, void *data)
+{
+       struct backlight_device *bd;
+       struct fb_event *evdata =(struct fb_event *)data;
+
+       /* If we aren't interested in this event, skip it immediately ... */
+       if (event != FB_EVENT_BLANK)
+               return 0;
+
+       bd = container_of(self, struct backlight_device, fb_notif);
+       down(&bd->sem);
+       if (bd->props)
+               if (!bd->props->check_fb || bd->props->check_fb(evdata->info))
+                       bd->props->set_power(bd, *(int *)evdata->data);
+       up(&bd->sem);
+       return 0;
+}
+
+/**
+ * backlight_device_register - create and register a new object of
+ *   backlight_device class.
+ * @name: the name of the new object(must be the same as the name of the
+ *   respective framebuffer device).
+ * @devdata: an optional pointer to be stored in the class_device. The
+ *   methods may retrieve it by using class_get_devdata(&bd->class_dev).
+ * @bp: the backlight properties structure.
+ *
+ * Creates and registers new backlight class_device. Returns either an
+ * ERR_PTR() or a pointer to the newly allocated device.
+ */
+struct backlight_device *backlight_device_register(const char *name, void *devdata,
+                                                  struct backlight_properties *bp)
+{
+       int i, rc;
+       struct backlight_device *new_bd;
+
+       pr_debug("backlight_device_alloc: name=%s\n", name);
+
+       new_bd = kmalloc(sizeof(struct backlight_device), GFP_KERNEL);
+       if (unlikely(!new_bd))
+               return ERR_PTR(ENOMEM);
+
+       init_MUTEX(&new_bd->sem);
+       new_bd->props = bp;
+       memset(&new_bd->class_dev, 0, sizeof(new_bd->class_dev));
+       new_bd->class_dev.class = &backlight_class;
+       strlcpy(new_bd->class_dev.class_id, name, KOBJ_NAME_LEN);
+       class_set_devdata(&new_bd->class_dev, devdata);
+
+       rc = class_device_register(&new_bd->class_dev);
+       if (unlikely(rc)) {
+error:         kfree(new_bd);
+               return ERR_PTR(rc);
+       }
+
+       memset(&new_bd->fb_notif, 0, sizeof(new_bd->fb_notif));
+       new_bd->fb_notif.notifier_call = fb_notifier_callback;
+
+       rc = fb_register_client(&new_bd->fb_notif);
+       if (unlikely(rc))
+               goto error;
+
+       for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) {
+               rc = class_device_create_file(&new_bd->class_dev,
+                                             &bl_class_device_attributes[i]);
+               if (unlikely(rc)) {
+                       while (--i >= 0)
+                               class_device_remove_file(&new_bd->class_dev,
+                                                        &bl_class_device_attributes[i]);
+                       class_device_unregister(&new_bd->class_dev);
+                       /* No need to kfree(new_bd) since release() method was called */
+                       return ERR_PTR(rc);
+               }
+       }
+
+       return new_bd;
+}
+EXPORT_SYMBOL(backlight_device_register);
+
+/**
+ * backlight_device_unregister - unregisters a backlight device object.
+ * @bd: the backlight device object to be unregistered and freed.
+ *
+ * Unregisters a previously registered via backlight_device_register object.
+ */
+void backlight_device_unregister(struct backlight_device *bd)
+{
+       int i;
+
+       if (!bd)
+               return;
+
+       pr_debug("backlight_device_unregister: name=%s\n", bd->class_dev.class_id);
+
+       for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++)
+               class_device_remove_file(&bd->class_dev,
+                                        &bl_class_device_attributes[i]);
+
+       down(&bd->sem);
+       bd->props = NULL;
+       up(&bd->sem);
+
+       fb_unregister_client(&bd->fb_notif);
+
+       class_device_unregister(&bd->class_dev);
+}
+EXPORT_SYMBOL(backlight_device_unregister);
+
+static void __exit backlight_class_exit(void)
+{
+       class_unregister(&backlight_class);
+}
+
+static int __init backlight_class_init(void)
+{
+       return class_register(&backlight_class);
+}
+
+/*
+ * if this is compiled into the kernel, we need to ensure that the
+ * class is registered before users of the class try to register lcd's
+ */
+postcore_initcall(backlight_class_init);
+module_exit(backlight_class_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
+MODULE_DESCRIPTION("Backlight Lowlevel Control Abstraction");
diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c
new file mode 100644 (file)
index 0000000..b54ec44
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ *  Backlight Driver for Sharp Corgi
+ *
+ *  Copyright (c) 2004-2005 Richard Purdie
+ *
+ *  Based on Sharp's 2.4 Backlight Driver
+ *
+ *  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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
+#include <asm/arch-pxa/corgi.h>
+#include <asm/hardware/scoop.h>
+
+#define CORGI_MAX_INTENSITY            0x3e
+#define CORGI_DEFAULT_INTENSITY                0x1f
+#define CORGI_LIMIT_MASK                       0x0b
+
+static int corgibl_powermode = FB_BLANK_UNBLANK;
+static int current_intensity = 0;
+static int corgibl_limit = 0;
+static spinlock_t bl_lock = SPIN_LOCK_UNLOCKED;
+
+static void corgibl_send_intensity(int intensity)
+{
+       unsigned long flags;
+       void (*corgi_kick_batt)(void);
+
+       if (corgibl_powermode != FB_BLANK_UNBLANK) {
+               intensity = 0;
+       } else {
+               if (corgibl_limit)
+                       intensity &= CORGI_LIMIT_MASK;
+       }
+
+       /* Skip 0x20 as it will blank the display */
+       if (intensity >= 0x20)
+               intensity++;
+
+       spin_lock_irqsave(&bl_lock, flags);
+       /* Bits 0-4 are accessed via the SSP interface */
+       corgi_ssp_blduty_set(intensity & 0x1f);
+       /* Bit 5 is via SCOOP */
+       if (intensity & 0x0020)
+               set_scoop_gpio(CORGI_SCP_BACKLIGHT_CONT);
+       else
+               reset_scoop_gpio(CORGI_SCP_BACKLIGHT_CONT);
+       spin_unlock_irqrestore(&bl_lock, flags);
+}
+
+static void corgibl_blank(int blank)
+{
+       switch(blank) {
+
+       case FB_BLANK_NORMAL:
+       case FB_BLANK_VSYNC_SUSPEND:
+       case FB_BLANK_HSYNC_SUSPEND:
+       case FB_BLANK_POWERDOWN:
+               if (corgibl_powermode == FB_BLANK_UNBLANK) {
+                       corgibl_send_intensity(0);
+                       corgibl_powermode = blank;
+               }
+               break;
+       case FB_BLANK_UNBLANK:
+               if (corgibl_powermode != FB_BLANK_UNBLANK) {
+                       corgibl_powermode = blank;
+                       corgibl_send_intensity(current_intensity);
+               }
+               break;
+       }
+}
+
+#ifdef CONFIG_PM
+static int corgibl_suspend(struct device *dev, u32 state, u32 level)
+{
+       if (level == SUSPEND_POWER_DOWN)
+               corgibl_blank(FB_BLANK_POWERDOWN);
+       return 0;
+}
+
+static int corgibl_resume(struct device *dev, u32 level)
+{
+       if (level == RESUME_POWER_ON)
+               corgibl_blank(FB_BLANK_UNBLANK);
+       return 0;
+}
+#else
+#define corgibl_suspend        NULL
+#define corgibl_resume NULL
+#endif
+
+
+static int corgibl_set_power(struct backlight_device *bd, int state)
+{
+       corgibl_blank(state);
+       return 0;
+}
+
+static int corgibl_get_power(struct backlight_device *bd)
+{
+       return corgibl_powermode;
+}
+
+static int corgibl_set_intensity(struct backlight_device *bd, int intensity)
+{
+       if (intensity > CORGI_MAX_INTENSITY)
+               intensity = CORGI_MAX_INTENSITY;
+       corgibl_send_intensity(intensity);
+       current_intensity=intensity;
+       return 0;
+}
+
+static int corgibl_get_intensity(struct backlight_device *bd)
+{
+       return current_intensity;
+}
+
+/*
+ * Called when the battery is low to limit the backlight intensity.
+ * If limit==0 clear any limit, otherwise limit the intensity
+ */
+void corgibl_limit_intensity(int limit)
+{
+       corgibl_limit = (limit ? 1 : 0);
+       corgibl_send_intensity(current_intensity);
+}
+EXPORT_SYMBOL(corgibl_limit_intensity);
+
+
+static struct backlight_properties corgibl_data = {
+       .owner          = THIS_MODULE,
+       .get_power      = corgibl_get_power,
+       .set_power      = corgibl_set_power,
+       .max_brightness = CORGI_MAX_INTENSITY,
+       .get_brightness = corgibl_get_intensity,
+       .set_brightness = corgibl_set_intensity,
+};
+
+static struct backlight_device *corgi_backlight_device;
+
+static int __init corgibl_probe(struct device *dev)
+{
+       corgi_backlight_device = backlight_device_register ("corgi-bl",
+               NULL, &corgibl_data);
+       if (IS_ERR (corgi_backlight_device))
+               return PTR_ERR (corgi_backlight_device);
+
+       corgibl_set_intensity(NULL, CORGI_DEFAULT_INTENSITY);
+
+       printk("Corgi Backlight Driver Initialized.\n");
+       return 0;
+}
+
+static int corgibl_remove(struct device *dev)
+{
+       backlight_device_unregister(corgi_backlight_device);
+
+       corgibl_set_intensity(NULL, 0);
+
+       printk("Corgi Backlight Driver Unloaded\n");
+       return 0;
+}
+
+static struct device_driver corgibl_driver = {
+       .name           = "corgi-bl",
+       .bus            = &platform_bus_type,
+       .probe          = corgibl_probe,
+       .remove         = corgibl_remove,
+       .suspend        = corgibl_suspend,
+       .resume         = corgibl_resume,
+};
+
+static int __init corgibl_init(void)
+{
+       return driver_register(&corgibl_driver);
+}
+
+static void __exit corgibl_exit(void)
+{
+       driver_unregister(&corgibl_driver);
+}
+
+module_init(corgibl_init);
+module_exit(corgibl_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Corgi Backlight Driver");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c
new file mode 100644 (file)
index 0000000..4f29824
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * LCD Lowlevel Control Abstraction
+ *
+ * Copyright (C) 2003,2004 Hewlett-Packard Company
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/lcd.h>
+#include <linux/notifier.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <asm/bug.h>
+
+static ssize_t lcd_show_power(struct class_device *cdev, char *buf)
+{
+       int rc;
+       struct lcd_device *ld = to_lcd_device(cdev);
+
+       down(&ld->sem);
+       if (likely(ld->props && ld->props->get_power))
+               rc = sprintf(buf, "%d\n", ld->props->get_power(ld));
+       else
+               rc = -ENXIO;
+       up(&ld->sem);
+
+       return rc;
+}
+
+static ssize_t lcd_store_power(struct class_device *cdev, const char *buf, size_t count)
+{
+       int rc, power;
+       char *endp;
+       struct lcd_device *ld = to_lcd_device(cdev);
+
+       power = simple_strtoul(buf, &endp, 0);
+       if (*endp && !isspace(*endp))
+               return -EINVAL;
+
+       down(&ld->sem);
+       if (likely(ld->props && ld->props->set_power)) {
+               pr_debug("lcd: set power to %d\n", power);
+               ld->props->set_power(ld, power);
+               rc = count;
+       } else
+               rc = -ENXIO;
+       up(&ld->sem);
+
+       return rc;
+}
+
+static ssize_t lcd_show_contrast(struct class_device *cdev, char *buf)
+{
+       int rc;
+       struct lcd_device *ld = to_lcd_device(cdev);
+
+       down(&ld->sem);
+       if (likely(ld->props && ld->props->get_contrast))
+               rc = sprintf(buf, "%d\n", ld->props->get_contrast(ld));
+       else
+               rc = -ENXIO;
+       up(&ld->sem);
+
+       return rc;
+}
+
+static ssize_t lcd_store_contrast(struct class_device *cdev, const char *buf, size_t count)
+{
+       int rc, contrast;
+       char *endp;
+       struct lcd_device *ld = to_lcd_device(cdev);
+
+       contrast = simple_strtoul(buf, &endp, 0);
+       if (*endp && !isspace(*endp))
+               return -EINVAL;
+
+       down(&ld->sem);
+       if (likely(ld->props && ld->props->set_contrast)) {
+               pr_debug("lcd: set contrast to %d\n", contrast);
+               ld->props->set_contrast(ld, contrast);
+               rc = count;
+       } else
+               rc = -ENXIO;
+       up(&ld->sem);
+
+       return rc;
+}
+
+static ssize_t lcd_show_max_contrast(struct class_device *cdev, char *buf)
+{
+       int rc;
+       struct lcd_device *ld = to_lcd_device(cdev);
+
+       down(&ld->sem);
+       if (likely(ld->props))
+               rc = sprintf(buf, "%d\n", ld->props->max_contrast);
+       else
+               rc = -ENXIO;
+       up(&ld->sem);
+
+       return rc;
+}
+
+static void lcd_class_release(struct class_device *dev)
+{
+       struct lcd_device *ld = to_lcd_device(dev);
+       kfree(ld);
+}
+
+struct class lcd_class = {
+       .name = "lcd",
+       .release = lcd_class_release,
+};
+
+#define DECLARE_ATTR(_name,_mode,_show,_store)                 \
+{                                                              \
+       .attr   = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE },  \
+       .show   = _show,                                        \
+       .store  = _store,                                       \
+}
+
+static struct class_device_attribute lcd_class_device_attributes[] = {
+       DECLARE_ATTR(power, 0644, lcd_show_power, lcd_store_power),
+       DECLARE_ATTR(contrast, 0644, lcd_show_contrast, lcd_store_contrast),
+       DECLARE_ATTR(max_contrast, 0444, lcd_show_max_contrast, NULL),
+};
+
+/* This callback gets called when something important happens inside a
+ * framebuffer driver. We're looking if that important event is blanking,
+ * and if it is, we're switching lcd power as well ...
+ */
+static int fb_notifier_callback(struct notifier_block *self,
+                                unsigned long event, void *data)
+{
+       struct lcd_device *ld;
+       struct fb_event *evdata =(struct fb_event *)data;
+
+       /* If we aren't interested in this event, skip it immediately ... */
+       if (event != FB_EVENT_BLANK)
+               return 0;
+
+       ld = container_of(self, struct lcd_device, fb_notif);
+       down(&ld->sem);
+       if (ld->props)
+               if (!ld->props->check_fb || ld->props->check_fb(evdata->info))
+                       ld->props->set_power(ld, *(int *)evdata->data);
+       up(&ld->sem);
+       return 0;
+}
+
+/**
+ * lcd_device_register - register a new object of lcd_device class.
+ * @name: the name of the new object(must be the same as the name of the
+ *   respective framebuffer device).
+ * @devdata: an optional pointer to be stored in the class_device. The
+ *   methods may retrieve it by using class_get_devdata(ld->class_dev).
+ * @lp: the lcd properties structure.
+ *
+ * Creates and registers a new lcd class_device. Returns either an ERR_PTR()
+ * or a pointer to the newly allocated device.
+ */
+struct lcd_device *lcd_device_register(const char *name, void *devdata,
+                                      struct lcd_properties *lp)
+{
+       int i, rc;
+       struct lcd_device *new_ld;
+
+       pr_debug("lcd_device_register: name=%s\n", name);
+
+       new_ld = kmalloc(sizeof(struct lcd_device), GFP_KERNEL);
+       if (unlikely(!new_ld))
+               return ERR_PTR(ENOMEM);
+
+       init_MUTEX(&new_ld->sem);
+       new_ld->props = lp;
+       memset(&new_ld->class_dev, 0, sizeof(new_ld->class_dev));
+       new_ld->class_dev.class = &lcd_class;
+       strlcpy(new_ld->class_dev.class_id, name, KOBJ_NAME_LEN);
+       class_set_devdata(&new_ld->class_dev, devdata);
+
+       rc = class_device_register(&new_ld->class_dev);
+       if (unlikely(rc)) {
+error:         kfree(new_ld);
+               return ERR_PTR(rc);
+       }
+
+       memset(&new_ld->fb_notif, 0, sizeof(new_ld->fb_notif));
+       new_ld->fb_notif.notifier_call = fb_notifier_callback;
+
+       rc = fb_register_client(&new_ld->fb_notif);
+       if (unlikely(rc))
+               goto error;
+
+       for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) {
+               rc = class_device_create_file(&new_ld->class_dev,
+                                             &lcd_class_device_attributes[i]);
+               if (unlikely(rc)) {
+                       while (--i >= 0)
+                               class_device_remove_file(&new_ld->class_dev,
+                                                        &lcd_class_device_attributes[i]);
+                       class_device_unregister(&new_ld->class_dev);
+                       /* No need to kfree(new_ld) since release() method was called */
+                       return ERR_PTR(rc);
+               }
+       }
+
+       return new_ld;
+}
+EXPORT_SYMBOL(lcd_device_register);
+
+/**
+ * lcd_device_unregister - unregisters a object of lcd_device class.
+ * @ld: the lcd device object to be unregistered and freed.
+ *
+ * Unregisters a previously registered via lcd_device_register object.
+ */
+void lcd_device_unregister(struct lcd_device *ld)
+{
+       int i;
+
+       if (!ld)
+               return;
+
+       pr_debug("lcd_device_unregister: name=%s\n", ld->class_dev.class_id);
+
+       for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++)
+               class_device_remove_file(&ld->class_dev,
+                                        &lcd_class_device_attributes[i]);
+
+       down(&ld->sem);
+       ld->props = NULL;
+       up(&ld->sem);
+
+       fb_unregister_client(&ld->fb_notif);
+
+       class_device_unregister(&ld->class_dev);
+}
+EXPORT_SYMBOL(lcd_device_unregister);
+
+static void __exit lcd_class_exit(void)
+{
+       class_unregister(&lcd_class);
+}
+
+static int __init lcd_class_init(void)
+{
+       return class_register(&lcd_class);
+}
+
+/*
+ * if this is compiled into the kernel, we need to ensure that the
+ * class is registered before users of the class try to register lcd's
+ */
+postcore_initcall(lcd_class_init);
+module_exit(lcd_class_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jamey Hicks <jamey.hicks@hp.com>, Andrew Zabolotny <zap@homelink.ru>");
+MODULE_DESCRIPTION("LCD Lowlevel Control Abstraction");
diff --git a/drivers/video/bt431.h b/drivers/video/bt431.h
new file mode 100644 (file)
index 0000000..c826f27
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ *     linux/drivers/video/bt431.h
+ *
+ *     Copyright 2003  Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
+ *
+ *     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 <linux/types.h>
+#include <asm/system.h>
+
+/*
+ * Bt431 cursor generator registers, 32-bit aligned.
+ * Two twin Bt431 are used on the DECstation's PMAG-AA.
+ */
+struct bt431_regs {
+       volatile u16 addr_lo;
+       u16 pad0;
+       volatile u16 addr_hi;
+       u16 pad1;
+       volatile u16 addr_cmap;
+       u16 pad2;
+       volatile u16 addr_reg;
+       u16 pad3;
+};
+
+static inline u16 bt431_set_value(u8 val)
+{
+       return ((val << 8) | (val & 0xff)) & 0xffff;
+}
+
+static inline u8 bt431_get_value(u16 val)
+{
+       return val & 0xff;
+}
+
+/*
+ * Additional registers addressed indirectly.
+ */
+#define BT431_REG_CMD          0x0000
+#define BT431_REG_CXLO         0x0001
+#define BT431_REG_CXHI         0x0002
+#define BT431_REG_CYLO         0x0003
+#define BT431_REG_CYHI         0x0004
+#define BT431_REG_WXLO         0x0005
+#define BT431_REG_WXHI         0x0006
+#define BT431_REG_WYLO         0x0007
+#define BT431_REG_WYHI         0x0008
+#define BT431_REG_WWLO         0x0009
+#define BT431_REG_WWHI         0x000a
+#define BT431_REG_WHLO         0x000b
+#define BT431_REG_WHHI         0x000c
+
+#define BT431_REG_CRAM_BASE    0x0000
+#define BT431_REG_CRAM_END     0x01ff
+
+/*
+ * Command register.
+ */
+#define BT431_CMD_CURS_ENABLE  0x40
+#define BT431_CMD_XHAIR_ENABLE 0x20
+#define BT431_CMD_OR_CURSORS   0x10
+#define BT431_CMD_AND_CURSORS  0x00
+#define BT431_CMD_1_1_MUX      0x00
+#define BT431_CMD_4_1_MUX      0x04
+#define BT431_CMD_5_1_MUX      0x08
+#define BT431_CMD_xxx_MUX      0x0c
+#define BT431_CMD_THICK_1      0x00
+#define BT431_CMD_THICK_3      0x01
+#define BT431_CMD_THICK_5      0x02
+#define BT431_CMD_THICK_7      0x03
+
+static inline void bt431_select_reg(struct bt431_regs *regs, int ir)
+{
+       /*
+        * The compiler splits the write in two bytes without these
+        * helper variables.
+        */
+       volatile u16 *lo = &(regs->addr_lo);
+       volatile u16 *hi = &(regs->addr_hi);
+
+       mb();
+       *lo = bt431_set_value(ir & 0xff);
+       wmb();
+       *hi = bt431_set_value((ir >> 8) & 0xff);
+}
+
+/* Autoincrement read/write. */
+static inline u8 bt431_read_reg_inc(struct bt431_regs *regs)
+{
+       /*
+        * The compiler splits the write in two bytes without the
+        * helper variable.
+        */
+       volatile u16 *r = &(regs->addr_reg);
+
+       mb();
+       return bt431_get_value(*r);
+}
+
+static inline void bt431_write_reg_inc(struct bt431_regs *regs, u8 value)
+{
+       /*
+        * The compiler splits the write in two bytes without the
+        * helper variable.
+        */
+       volatile u16 *r = &(regs->addr_reg);
+
+       mb();
+       *r = bt431_set_value(value);
+}
+
+static inline u8 bt431_read_reg(struct bt431_regs *regs, int ir)
+{
+       bt431_select_reg(regs, ir);
+       return bt431_read_reg_inc(regs);
+}
+
+static inline void bt431_write_reg(struct bt431_regs *regs, int ir, u8 value)
+{
+       bt431_select_reg(regs, ir);
+       bt431_write_reg_inc(regs, value);
+}
+
+/* Autoincremented read/write for the cursor map. */
+static inline u16 bt431_read_cmap_inc(struct bt431_regs *regs)
+{
+       /*
+        * The compiler splits the write in two bytes without the
+        * helper variable.
+        */
+       volatile u16 *r = &(regs->addr_cmap);
+
+       mb();
+       return *r;
+}
+
+static inline void bt431_write_cmap_inc(struct bt431_regs *regs, u16 value)
+{
+       /*
+        * The compiler splits the write in two bytes without the
+        * helper variable.
+        */
+       volatile u16 *r = &(regs->addr_cmap);
+
+       mb();
+       *r = value;
+}
+
+static inline u16 bt431_read_cmap(struct bt431_regs *regs, int cr)
+{
+       bt431_select_reg(regs, cr);
+       return bt431_read_cmap_inc(regs);
+}
+
+static inline void bt431_write_cmap(struct bt431_regs *regs, int cr, u16 value)
+{
+       bt431_select_reg(regs, cr);
+       bt431_write_cmap_inc(regs, value);
+}
+
+static inline void bt431_enable_cursor(struct bt431_regs *regs)
+{
+       bt431_write_reg(regs, BT431_REG_CMD,
+                       BT431_CMD_CURS_ENABLE | BT431_CMD_OR_CURSORS
+                       | BT431_CMD_4_1_MUX | BT431_CMD_THICK_1);
+}
+
+static inline void bt431_erase_cursor(struct bt431_regs *regs)
+{
+       bt431_write_reg(regs, BT431_REG_CMD, BT431_CMD_4_1_MUX);
+}
+
+static inline void bt431_position_cursor(struct bt431_regs *regs, u16 x, u16 y)
+{
+       /*
+        * Magic from the MACH sources.
+        *
+        * Cx = x + D + H - P
+        *  P = 37 if 1:1, 52 if 4:1, 57 if 5:1
+        *  D = pixel skew between outdata and external data
+        *  H = pixels between HSYNCH falling and active video
+        *
+        * Cy = y + V - 32
+        *  V = scanlines between HSYNCH falling, two or more
+        *      clocks after VSYNCH falling, and active video
+        */
+       x += 412 - 52;
+       y += 68 - 32;
+
+       /* Use autoincrement. */
+       bt431_select_reg(regs, BT431_REG_CXLO);
+       bt431_write_reg_inc(regs, x & 0xff); /* BT431_REG_CXLO */
+       bt431_write_reg_inc(regs, (x >> 8) & 0x0f); /* BT431_REG_CXHI */
+       bt431_write_reg_inc(regs, y & 0xff); /* BT431_REG_CYLO */
+       bt431_write_reg_inc(regs, (y >> 8) & 0x0f); /* BT431_REG_CYHI */
+}
+
+static inline void bt431_set_font(struct bt431_regs *regs, u8 fgc,
+                                 u16 width, u16 height)
+{
+       int i;
+       u16 fgp = fgc ? 0xffff : 0x0000;
+       u16 bgp = fgc ? 0x0000 : 0xffff;
+
+       bt431_select_reg(regs, BT431_REG_CRAM_BASE);
+       for (i = BT431_REG_CRAM_BASE; i <= BT431_REG_CRAM_END; i++) {
+               u16 value;
+
+               if (height << 6 <= i << 3)
+                       value = bgp;
+               else if (width <= i % 8 << 3)
+                       value = bgp;
+               else if (((width >> 3) & 0xffff) > i % 8)
+                       value = fgp;
+               else
+                       value = fgp & ~(bgp << (width % 8 << 1));
+
+               bt431_write_cmap_inc(regs, value);
+       }
+}
+
+static inline void bt431_init_cursor(struct bt431_regs *regs)
+{
+       /* no crosshair window */
+       bt431_select_reg(regs, BT431_REG_WXLO);
+       bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WXLO */
+       bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WXHI */
+       bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WYLO */
+       bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WYHI */
+       bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WWLO */
+       bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WWHI */
+       bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WHLO */
+       bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WHHI */
+}
diff --git a/drivers/video/bt455.h b/drivers/video/bt455.h
new file mode 100644 (file)
index 0000000..b7591fe
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *     linux/drivers/video/bt455.h
+ *
+ *     Copyright 2003  Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
+ *
+ *     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 <linux/types.h>
+#include <asm/system.h>
+
+/*
+ * Bt455 byte-wide registers, 32-bit aligned.
+ */
+struct bt455_regs {
+       volatile u8 addr_cmap;
+       u8 pad0[3];
+       volatile u8 addr_cmap_data;
+       u8 pad1[3];
+       volatile u8 addr_clr;
+       u8 pad2[3];
+       volatile u8 addr_ovly;
+       u8 pad3[3];
+};
+
+static inline void bt455_select_reg(struct bt455_regs *regs, int ir)
+{
+       mb();
+       regs->addr_cmap = ir & 0x0f;
+}
+
+/*
+ * Read/write to a Bt455 color map register.
+ */
+static inline void bt455_read_cmap_entry(struct bt455_regs *regs, int cr,
+                                        u8* red, u8* green, u8* blue)
+{
+       bt455_select_reg(regs, cr);
+       mb();
+       *red = regs->addr_cmap_data & 0x0f;
+       rmb();
+       *green = regs->addr_cmap_data & 0x0f;
+       rmb();
+       *blue = regs->addr_cmap_data & 0x0f;
+}
+
+static inline void bt455_write_cmap_entry(struct bt455_regs *regs, int cr,
+                                         u8 red, u8 green, u8 blue)
+{
+       bt455_select_reg(regs, cr);
+       wmb();
+       regs->addr_cmap_data = red & 0x0f;
+       wmb();
+       regs->addr_cmap_data = green & 0x0f;
+       wmb();
+       regs->addr_cmap_data = blue & 0x0f;
+}
+
+static inline void bt455_write_ovly_entry(struct bt455_regs *regs, int cr,
+                                         u8 red, u8 green, u8 blue)
+{
+       bt455_select_reg(regs, cr);
+       wmb();
+       regs->addr_ovly = red & 0x0f;
+       wmb();
+       regs->addr_ovly = green & 0x0f;
+       wmb();
+       regs->addr_ovly = blue & 0x0f;
+}
+
+static inline void bt455_set_cursor(struct bt455_regs *regs)
+{
+       mb();
+       regs->addr_ovly = 0x0f;
+       wmb();
+       regs->addr_ovly = 0x0f;
+       wmb();
+       regs->addr_ovly = 0x0f;
+}
+
+static inline void bt455_erase_cursor(struct bt455_regs *regs)
+{
+       /* bt455_write_cmap_entry(regs, 8, 0x00, 0x00, 0x00); */
+       /* bt455_write_cmap_entry(regs, 9, 0x00, 0x00, 0x00); */
+       bt455_write_ovly_entry(regs, 8, 0x03, 0x03, 0x03);
+       bt455_write_ovly_entry(regs, 9, 0x07, 0x07, 0x07);
+
+       wmb();
+       regs->addr_ovly = 0x09;
+       wmb();
+       regs->addr_ovly = 0x09;
+       wmb();
+       regs->addr_ovly = 0x09;
+}
diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
new file mode 100644 (file)
index 0000000..2ab37d6
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ *  linux/drivers/video/console/bitblit.c -- BitBlitting Operation
+ *
+ *  Originally from the 'accel_*' routines in drivers/video/console/fbcon.c
+ *
+ *      Copyright (C) 2004 Antonino Daplas <adaplas @pol.net>
+ *
+ *  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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/console.h>
+#include <asm/types.h>
+#include "fbcon.h"
+
+/*
+ * Accelerated handlers.
+ */
+#define FBCON_ATTRIBUTE_UNDERLINE 1
+#define FBCON_ATTRIBUTE_REVERSE   2
+#define FBCON_ATTRIBUTE_BOLD      4
+
+static inline int real_y(struct display *p, int ypos)
+{
+       int rows = p->vrows;
+
+       ypos += p->yscroll;
+       return ypos < rows ? ypos : ypos - rows;
+}
+
+
+static inline int get_attribute(struct fb_info *info, u16 c)
+{
+       int attribute = 0;
+
+       if (fb_get_color_depth(info) == 1) {
+               if (attr_underline(c))
+                       attribute |= FBCON_ATTRIBUTE_UNDERLINE;
+               if (attr_reverse(c))
+                       attribute |= FBCON_ATTRIBUTE_REVERSE;
+               if (attr_bold(c))
+                       attribute |= FBCON_ATTRIBUTE_BOLD;
+       }
+
+       return attribute;
+}
+
+static inline void update_attr(u8 *dst, u8 *src, int attribute,
+                              struct vc_data *vc)
+{
+       int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
+       int width = (vc->vc_font.width + 7) >> 3;
+       unsigned int cellsize = vc->vc_font.height * width;
+       u8 c;
+
+       offset = cellsize - (offset * width);
+       for (i = 0; i < cellsize; i++) {
+               c = src[i];
+               if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset)
+                       c = 0xff;
+               if (attribute & FBCON_ATTRIBUTE_BOLD)
+                       c |= c >> 1;
+               if (attribute & FBCON_ATTRIBUTE_REVERSE)
+                       c = ~c;
+               dst[i] = c;
+       }
+}
+
+static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy,
+                     int sx, int dy, int dx, int height, int width)
+{
+       struct fb_copyarea area;
+
+       area.sx = sx * vc->vc_font.width;
+       area.sy = sy * vc->vc_font.height;
+       area.dx = dx * vc->vc_font.width;
+       area.dy = dy * vc->vc_font.height;
+       area.height = height * vc->vc_font.height;
+       area.width = width * vc->vc_font.width;
+
+       info->fbops->fb_copyarea(info, &area);
+}
+
+static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy,
+                     int sx, int height, int width)
+{
+       int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
+       struct fb_fillrect region;
+
+       region.color = attr_bgcol_ec(bgshift, vc);
+       region.dx = sx * vc->vc_font.width;
+       region.dy = sy * vc->vc_font.height;
+       region.width = width * vc->vc_font.width;
+       region.height = height * vc->vc_font.height;
+       region.rop = ROP_COPY;
+
+       info->fbops->fb_fillrect(info, &region);
+}
+
+static void bit_putcs(struct vc_data *vc, struct fb_info *info,
+                     const unsigned short *s, int count, int yy, int xx,
+                     int fg, int bg)
+{
+       void (*move_unaligned)(struct fb_info *info, struct fb_pixmap *buf,
+                              u8 *dst, u32 d_pitch, u8 *src, u32 idx,
+                              u32 height, u32 shift_high, u32 shift_low,
+                              u32 mod);
+       void (*move_aligned)(struct fb_info *info, struct fb_pixmap *buf,
+                            u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch,
+                            u32 height);
+       unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+       unsigned int width = (vc->vc_font.width + 7) >> 3;
+       unsigned int cellsize = vc->vc_font.height * width;
+       unsigned int maxcnt = info->pixmap.size/cellsize;
+       unsigned int scan_align = info->pixmap.scan_align - 1;
+       unsigned int buf_align = info->pixmap.buf_align - 1;
+       unsigned int shift_low = 0, mod = vc->vc_font.width % 8;
+       unsigned int shift_high = 8, pitch, cnt, size, k;
+       unsigned int idx = vc->vc_font.width >> 3;
+       unsigned int attribute = get_attribute(info, scr_readw(s));
+       struct fb_image image;
+       u8 *src, *dst, *buf = NULL;
+
+       if (attribute) {
+               buf = kmalloc(cellsize, GFP_KERNEL);
+               if (!buf)
+                       return;
+       }
+
+       image.fg_color = fg;
+       image.bg_color = bg;
+
+       image.dx = xx * vc->vc_font.width;
+       image.dy = yy * vc->vc_font.height;
+       image.height = vc->vc_font.height;
+       image.depth = 1;
+
+       if (info->pixmap.outbuf && info->pixmap.inbuf) {
+               move_aligned = fb_iomove_buf_aligned;
+               move_unaligned = fb_iomove_buf_unaligned;
+       } else {
+               move_aligned = fb_sysmove_buf_aligned;
+               move_unaligned = fb_sysmove_buf_unaligned;
+       }
+       while (count) {
+               if (count > maxcnt)
+                       cnt = k = maxcnt;
+               else
+                       cnt = k = count;
+
+               image.width = vc->vc_font.width * cnt;
+               pitch = ((image.width + 7) >> 3) + scan_align;
+               pitch &= ~scan_align;
+               size = pitch * image.height + buf_align;
+               size &= ~buf_align;
+               dst = fb_get_buffer_offset(info, &info->pixmap, size);
+               image.data = dst;
+               if (mod) {
+                       while (k--) {
+                               src = vc->vc_font.data + (scr_readw(s++)&
+                                                         charmask)*cellsize;
+
+                               if (attribute) {
+                                       update_attr(buf, src, attribute, vc);
+                                       src = buf;
+                               }
+
+                               move_unaligned(info, &info->pixmap, dst, pitch,
+                                              src, idx, image.height,
+                                              shift_high, shift_low, mod);
+                               shift_low += mod;
+                               dst += (shift_low >= 8) ? width : width - 1;
+                               shift_low &= 7;
+                               shift_high = 8 - shift_low;
+                       }
+               } else {
+                       while (k--) {
+                               src = vc->vc_font.data + (scr_readw(s++)&
+                                                         charmask)*cellsize;
+
+                               if (attribute) {
+                                       update_attr(buf, src, attribute, vc);
+                                       src = buf;
+                               }
+
+                               move_aligned(info, &info->pixmap, dst, pitch,
+                                            src, idx, image.height);
+                               dst += width;
+                       }
+               }
+               info->fbops->fb_imageblit(info, &image);
+               image.dx += cnt * vc->vc_font.width;
+               count -= cnt;
+       }
+
+       if (buf)
+               kfree(buf);
+}
+
+static void bit_clear_margins(struct vc_data *vc, struct fb_info *info,
+                             int bottom_only)
+{
+       int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
+       unsigned int cw = vc->vc_font.width;
+       unsigned int ch = vc->vc_font.height;
+       unsigned int rw = info->var.xres - (vc->vc_cols*cw);
+       unsigned int bh = info->var.yres - (vc->vc_rows*ch);
+       unsigned int rs = info->var.xres - rw;
+       unsigned int bs = info->var.yres - bh;
+       struct fb_fillrect region;
+
+       region.color = attr_bgcol_ec(bgshift, vc);
+       region.rop = ROP_COPY;
+
+       if (rw && !bottom_only) {
+               region.dx = info->var.xoffset + rs;
+               region.dy = 0;
+               region.width = rw;
+               region.height = info->var.yres_virtual;
+               info->fbops->fb_fillrect(info, &region);
+       }
+
+       if (bh) {
+               region.dx = info->var.xoffset;
+               region.dy = info->var.yoffset + bs;
+               region.width = rs;
+               region.height = bh;
+               info->fbops->fb_fillrect(info, &region);
+       }
+}
+
+static void bit_cursor(struct vc_data *vc, struct fb_info *info,
+                      struct display *p, int mode, int fg, int bg)
+{
+       struct fb_cursor cursor;
+       struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par;
+       unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+       int w = (vc->vc_font.width + 7) >> 3, c;
+       int y = real_y(p, vc->vc_y);
+       int attribute, use_sw = (vc->vc_cursor_type & 0x10);
+       char *src;
+
+       cursor.set = 0;
+
+       c = scr_readw((u16 *) vc->vc_pos);
+       attribute = get_attribute(info, c);
+       src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
+
+       if (ops->cursor_state.image.data != src ||
+           ops->cursor_reset) {
+           ops->cursor_state.image.data = src;
+           cursor.set |= FB_CUR_SETIMAGE;
+       }
+
+       if (attribute) {
+               u8 *dst;
+
+               dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC);
+               if (!dst)
+                       return;
+               if (ops->cursor_data)
+                       kfree(ops->cursor_data);
+               ops->cursor_data = dst;
+               update_attr(dst, src, attribute, vc);
+               src = dst;
+       }
+
+       if (ops->cursor_state.image.fg_color != fg ||
+           ops->cursor_state.image.bg_color != bg ||
+           ops->cursor_reset) {
+               ops->cursor_state.image.fg_color = fg;
+               ops->cursor_state.image.bg_color = bg;
+               cursor.set |= FB_CUR_SETCMAP;
+       }
+
+       if ((ops->cursor_state.image.dx != (vc->vc_font.width * vc->vc_x)) ||
+           (ops->cursor_state.image.dy != (vc->vc_font.height * y)) ||
+           ops->cursor_reset) {
+               ops->cursor_state.image.dx = vc->vc_font.width * vc->vc_x;
+               ops->cursor_state.image.dy = vc->vc_font.height * y;
+               cursor.set |= FB_CUR_SETPOS;
+       }
+
+       if (ops->cursor_state.image.height != vc->vc_font.height ||
+           ops->cursor_state.image.width != vc->vc_font.width ||
+           ops->cursor_reset) {
+               ops->cursor_state.image.height = vc->vc_font.height;
+               ops->cursor_state.image.width = vc->vc_font.width;
+               cursor.set |= FB_CUR_SETSIZE;
+       }
+
+       if (ops->cursor_state.hot.x || ops->cursor_state.hot.y ||
+           ops->cursor_reset) {
+               ops->cursor_state.hot.x = cursor.hot.y = 0;
+               cursor.set |= FB_CUR_SETHOT;
+       }
+
+       if (cursor.set & FB_CUR_SETSIZE ||
+           vc->vc_cursor_type != p->cursor_shape ||
+           ops->cursor_state.mask == NULL ||
+           ops->cursor_reset) {
+               char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC);
+               int cur_height, size, i = 0;
+               u8 msk = 0xff;
+
+               if (!mask)
+                       return;
+
+               if (ops->cursor_state.mask)
+                       kfree(ops->cursor_state.mask);
+               ops->cursor_state.mask = mask;
+
+               p->cursor_shape = vc->vc_cursor_type;
+               cursor.set |= FB_CUR_SETSHAPE;
+
+               switch (p->cursor_shape & CUR_HWMASK) {
+               case CUR_NONE:
+                       cur_height = 0;
+                       break;
+               case CUR_UNDERLINE:
+                       cur_height = (vc->vc_font.height < 10) ? 1 : 2;
+                       break;
+               case CUR_LOWER_THIRD:
+                       cur_height = vc->vc_font.height/3;
+                       break;
+               case CUR_LOWER_HALF:
+                       cur_height = vc->vc_font.height >> 1;
+                       break;
+               case CUR_TWO_THIRDS:
+                       cur_height = (vc->vc_font.height << 1)/3;
+                       break;
+               case CUR_BLOCK:
+               default:
+                       cur_height = vc->vc_font.height;
+                       break;
+               }
+               size = (vc->vc_font.height - cur_height) * w;
+               while (size--)
+                       mask[i++] = ~msk;
+               size = cur_height * w;
+               while (size--)
+                       mask[i++] = msk;
+       }
+
+       switch (mode) {
+       case CM_ERASE:
+               ops->cursor_state.enable = 0;
+               break;
+       case CM_DRAW:
+       case CM_MOVE:
+       default:
+               ops->cursor_state.enable = (use_sw) ? 0 : 1;
+               break;
+       }
+
+       cursor.image.data = src;
+       cursor.image.fg_color = ops->cursor_state.image.fg_color;
+       cursor.image.bg_color = ops->cursor_state.image.bg_color;
+       cursor.image.dx = ops->cursor_state.image.dx;
+       cursor.image.dy = ops->cursor_state.image.dy;
+       cursor.image.height = ops->cursor_state.image.height;
+       cursor.image.width = ops->cursor_state.image.width;
+       cursor.hot.x = ops->cursor_state.hot.x;
+       cursor.hot.y = ops->cursor_state.hot.y;
+       cursor.mask = ops->cursor_state.mask;
+       cursor.enable = ops->cursor_state.enable;
+       cursor.image.depth = 1;
+       cursor.rop = ROP_XOR;
+
+       info->fbops->fb_cursor(info, &cursor);
+
+       ops->cursor_reset = 0;
+}
+
+void fbcon_set_bitops(struct fbcon_ops *ops)
+{
+       ops->bmove = bit_bmove;
+       ops->clear = bit_clear;
+       ops->putcs = bit_putcs;
+       ops->clear_margins = bit_clear_margins;
+       ops->cursor = bit_cursor;
+}
+
+EXPORT_SYMBOL(fbcon_set_bitops);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("Bit Blitting Operation");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/video/console/tileblit.c b/drivers/video/console/tileblit.c
new file mode 100644 (file)
index 0000000..0984adc
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ *  linux/drivers/video/console/tileblit.c -- Tile Blitting Operation
+ *
+ *      Copyright (C) 2004 Antonino Daplas <adaplas @pol.net>
+ *
+ *  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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/console.h>
+#include <asm/types.h>
+#include "fbcon.h"
+
+static void tile_bmove(struct vc_data *vc, struct fb_info *info, int sy,
+                      int sx, int dy, int dx, int height, int width)
+{
+       struct fb_tilearea area;
+
+       area.sx = sx;
+       area.sy = sy;
+       area.dx = dx;
+       area.dy = dy;
+       area.height = height;
+       area.width = width;
+
+       info->tileops->fb_tilecopy(info, &area);
+}
+
+static void tile_clear(struct vc_data *vc, struct fb_info *info, int sy,
+                      int sx, int height, int width)
+{
+       struct fb_tilerect rect;
+       int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
+       int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
+
+       rect.index = vc->vc_video_erase_char &
+               ((vc->vc_hi_font_mask) ? 0x1ff : 0xff);
+       rect.fg = attr_fgcol_ec(fgshift, vc);
+       rect.bg = attr_bgcol_ec(bgshift, vc);
+       rect.sx = sx;
+       rect.sy = sy;
+       rect.width = width;
+       rect.height = height;
+       rect.rop = ROP_COPY;
+
+       info->tileops->fb_tilefill(info, &rect);
+}
+
+static void tile_putcs(struct vc_data *vc, struct fb_info *info,
+                      const unsigned short *s, int count, int yy, int xx,
+                      int fg, int bg)
+{
+       struct fb_tileblit blit;
+       unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+       int size = sizeof(u32) * count, i;
+
+       blit.sx = xx;
+       blit.sy = yy;
+       blit.width = count;
+       blit.height = 1;
+       blit.fg = fg;
+       blit.bg = bg;
+       blit.length = count;
+       blit.indices = (u32 *) fb_get_buffer_offset(info, &info->pixmap, size);
+       for (i = 0; i < count; i++)
+               blit.indices[i] = (u32)(scr_readw(s++) & charmask);
+
+       info->tileops->fb_tileblit(info, &blit);
+}
+
+static void tile_clear_margins(struct vc_data *vc, struct fb_info *info,
+                              int bottom_only)
+{
+       return;
+}
+
+static void tile_cursor(struct vc_data *vc, struct fb_info *info,
+                       struct display *p, int mode, int fg, int bg)
+{
+       struct fb_tilecursor cursor;
+       int use_sw = (vc->vc_cursor_type & 0x01);
+
+       cursor.sx = vc->vc_x;
+       cursor.sy = vc->vc_y;
+       cursor.mode = (mode == CM_ERASE || use_sw) ? 0 : 1;
+       cursor.fg = fg;
+       cursor.bg = bg;
+
+       switch (vc->vc_cursor_type & 0x0f) {
+       case CUR_NONE:
+               cursor.shape = FB_TILE_CURSOR_NONE;
+               break;
+       case CUR_UNDERLINE:
+               cursor.shape = FB_TILE_CURSOR_UNDERLINE;
+               break;
+       case CUR_LOWER_THIRD:
+               cursor.shape = FB_TILE_CURSOR_LOWER_THIRD;
+               break;
+       case CUR_LOWER_HALF:
+               cursor.shape = FB_TILE_CURSOR_LOWER_HALF;
+               break;
+       case CUR_TWO_THIRDS:
+               cursor.shape = FB_TILE_CURSOR_TWO_THIRDS;
+               break;
+       case CUR_BLOCK:
+       default:
+               cursor.shape = FB_TILE_CURSOR_BLOCK;
+               break;
+       }
+
+       info->tileops->fb_tilecursor(info, &cursor);
+}
+
+void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info,
+                      struct display *p, struct fbcon_ops *ops)
+{
+       struct fb_tilemap map;
+
+       ops->bmove = tile_bmove;
+       ops->clear = tile_clear;
+       ops->putcs = tile_putcs;
+       ops->clear_margins = tile_clear_margins;
+       ops->cursor = tile_cursor;
+
+       if (p) {
+               map.width = vc->vc_font.width;
+               map.height = vc->vc_font.height;
+               map.depth = 1;
+               map.length = (p->userfont) ?
+                       FNTCHARCNT(p->fontdata) : 256;
+               map.data = p->fontdata;
+               info->tileops->fb_settile(info, &map);
+       }
+}
+
+EXPORT_SYMBOL(fbcon_set_tileops);
+
+MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>");
+MODULE_DESCRIPTION("Tile Blitting Operation");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/video/intelfb/Makefile b/drivers/video/intelfb/Makefile
new file mode 100644 (file)
index 0000000..722d21d
--- /dev/null
@@ -0,0 +1,8 @@
+obj-$(CONFIG_FB_INTEL) += intelfb.o
+
+intelfb-objs := intelfbdrv.o intelfbhw.o
+
+ifdef CONFIG_FB_INTEL_DEBUG
+#EXTRA_CFLAGS += -DDEBUG -DVERBOSE -DREGDUMP
+EXTRA_CFLAGS += -DDEBUG -DREGDUMP
+endif
diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h
new file mode 100644 (file)
index 0000000..1c5b472
--- /dev/null
@@ -0,0 +1,278 @@
+#ifndef _INTELFB_H
+#define _INTELFB_H
+
+/* $DHD: intelfb/intelfb.h,v 1.40 2003/06/27 15:06:25 dawes Exp $ */
+
+#include <linux/agp_backend.h>
+#include <linux/fb.h>
+
+
+/*** Version/name ***/
+#define INTELFB_VERSION                        "0.9.2"
+#define INTELFB_MODULE_NAME            "intelfb"
+#define SUPPORTED_CHIPSETS             "830M/845G/852GM/855GM/865G"
+
+
+/*** Debug/feature defines ***/
+
+#ifndef DEBUG
+#define DEBUG                          0
+#endif
+
+#ifndef VERBOSE
+#define VERBOSE                                0
+#endif
+
+#ifndef REGDUMP
+#define REGDUMP                                0
+#endif
+
+#ifndef DETECT_VGA_CLASS_ONLY
+#define DETECT_VGA_CLASS_ONLY          1
+#endif
+
+#ifndef ALLOCATE_FOR_PANNING
+#define ALLOCATE_FOR_PANNING           1
+#endif
+
+#ifndef PREFERRED_MODE
+#define PREFERRED_MODE                 "1024x768-16@60"
+#endif
+
+/*** hw-related values ***/
+
+/* PCI ids for supported devices */
+#define PCI_DEVICE_ID_INTEL_830M       0x3577
+#define PCI_DEVICE_ID_INTEL_845G       0x2562
+#define PCI_DEVICE_ID_INTEL_85XGM      0x3582
+#define PCI_DEVICE_ID_INTEL_865G       0x2572
+
+/* Size of MMIO region */
+#define INTEL_REG_SIZE                 0x80000
+
+#define STRIDE_ALIGNMENT               16
+
+#define PALETTE_8_ENTRIES              256
+
+
+/*** Macros ***/
+
+/* basic arithmetic */
+#define KB(x)                  ((x) * 1024)
+#define MB(x)                  ((x) * 1024 * 1024)
+#define BtoKB(x)               ((x) / 1024)
+#define BtoMB(x)               ((x) / 1024 / 1024)
+
+#define GTT_PAGE_SIZE           KB(4)
+
+#define ROUND_UP_TO(x, y)      (((x) + (y) - 1) / (y) * (y))
+#define ROUND_DOWN_TO(x, y)    ((x) / (y) * (y))
+#define ROUND_UP_TO_PAGE(x)    ROUND_UP_TO((x), GTT_PAGE_SIZE)
+#define ROUND_DOWN_TO_PAGE(x)  ROUND_DOWN_TO((x), GTT_PAGE_SIZE)
+
+/* messages */
+#define PFX                    INTELFB_MODULE_NAME ": "
+
+#define ERR_MSG(fmt, args...)  printk(KERN_ERR PFX fmt, ## args)
+#define WRN_MSG(fmt, args...)  printk(KERN_WARNING PFX fmt, ## args)
+#define NOT_MSG(fmt, args...)  printk(KERN_NOTICE PFX fmt, ## args)
+#define INF_MSG(fmt, args...)  printk(KERN_INFO PFX fmt, ## args)
+#if DEBUG
+#define DBG_MSG(fmt, args...)  printk(KERN_DEBUG PFX fmt, ## args)
+#else
+#define DBG_MSG(fmt, args...)  while (0) printk(fmt, ## args)
+#endif
+
+/* get commonly used pointers */
+#define GET_DINFO(info)                (info)->par
+
+/* misc macros */
+#define ACCEL(d, i)                                                     \
+       ((d)->accel && !(d)->ring_lockup &&                             \
+        ((i)->var.accel_flags & FB_ACCELF_TEXT))
+
+/*#define NOACCEL_CHIPSET(d)                                           \
+       ((d)->chipset != INTEL_865G)*/
+#define NOACCEL_CHIPSET(d)                                             \
+       (0)
+
+#define FIXED_MODE(d) ((d)->fixed_mode)
+
+/*** Driver paramters ***/
+
+#define RINGBUFFER_SIZE                KB(64)
+#define HW_CURSOR_SIZE         KB(4)
+
+/* Intel agpgart driver */
+#define AGP_PHYSICAL_MEMORY     2
+
+/*** Data Types ***/
+
+/* supported chipsets */
+enum intel_chips {
+       INTEL_830M,
+       INTEL_845G,
+       INTEL_85XGM,
+       INTEL_852GM,
+       INTEL_852GME,
+       INTEL_855GM,
+       INTEL_855GME,
+       INTEL_865G
+};
+
+struct intelfb_hwstate {
+       u32 vga0_divisor;
+       u32 vga1_divisor;
+       u32 vga_pd;
+       u32 dpll_a;
+       u32 dpll_b;
+       u32 fpa0;
+       u32 fpa1;
+       u32 fpb0;
+       u32 fpb1;
+       u32 palette_a[PALETTE_8_ENTRIES];
+       u32 palette_b[PALETTE_8_ENTRIES];
+       u32 htotal_a;
+       u32 hblank_a;
+       u32 hsync_a;
+       u32 vtotal_a;
+       u32 vblank_a;
+       u32 vsync_a;
+       u32 src_size_a;
+       u32 bclrpat_a;
+       u32 htotal_b;
+       u32 hblank_b;
+       u32 hsync_b;
+       u32 vtotal_b;
+       u32 vblank_b;
+       u32 vsync_b;
+       u32 src_size_b;
+       u32 bclrpat_b;
+       u32 adpa;
+       u32 dvoa;
+       u32 dvob;
+       u32 dvoc;
+       u32 dvoa_srcdim;
+       u32 dvob_srcdim;
+       u32 dvoc_srcdim;
+       u32 lvds;
+       u32 pipe_a_conf;
+       u32 pipe_b_conf;
+       u32 disp_arb;
+       u32 cursor_a_control;
+       u32 cursor_b_control;
+       u32 cursor_a_base;
+       u32 cursor_b_base;
+       u32 cursor_size;
+       u32 disp_a_ctrl;
+       u32 disp_b_ctrl;
+       u32 disp_a_base;
+       u32 disp_b_base;
+       u32 cursor_a_palette[4];
+       u32 cursor_b_palette[4];
+       u32 disp_a_stride;
+       u32 disp_b_stride;
+       u32 vgacntrl;
+       u32 add_id;
+       u32 swf0x[7];
+       u32 swf1x[7];
+       u32 swf3x[3];
+       u32 fence[8];
+       u32 instpm;
+       u32 mem_mode;
+       u32 fw_blc_0;
+       u32 fw_blc_1;
+};
+
+struct intelfb_heap_data {
+       u32 physical;
+       u8 __iomem *virtual;
+       u32 offset;  // in GATT pages
+       u32 size;    // in bytes
+};
+
+struct intelfb_info {
+       struct fb_info *info;
+       struct fb_ops  *fbops;
+       struct pci_dev *pdev;
+
+       struct intelfb_hwstate save_state;
+
+       /* agpgart structs */
+       struct agp_memory *gtt_fb_mem;     // use all stolen memory or vram
+       struct agp_memory *gtt_ring_mem;   // ring buffer
+       struct agp_memory *gtt_cursor_mem; // hw cursor
+
+       /* use a gart reserved fb mem */
+       u8 fbmem_gart;
+
+       /* mtrr support */
+       u32 mtrr_reg;
+       u32 has_mtrr;
+
+       /* heap data */
+       struct intelfb_heap_data aperture;
+       struct intelfb_heap_data fb;
+       struct intelfb_heap_data ring;
+       struct intelfb_heap_data cursor;
+
+       /* mmio regs */
+       u32 mmio_base_phys;
+       u8 __iomem *mmio_base;
+
+       /* fb start offset (in bytes) */
+       u32 fb_start;
+
+       /* ring buffer */
+       u8 __iomem *ring_head;
+       u32 ring_tail;
+       u32 ring_tail_mask;
+       u32 ring_space;
+       u32 ring_lockup;
+
+       /* palette */
+       u32 pseudo_palette[17];
+       struct { u8 red, green, blue, pad; } palette[256];
+
+       /* chip info */
+       int pci_chipset;
+       int chipset;
+       const char *name;
+       int mobile;
+
+       /* current mode */
+       int bpp, depth;
+       u32 visual;
+       int xres, yres, pitch;
+       int pixclock;
+
+       /* current pipe */
+       int pipe;
+
+       /* some flags */
+       int accel;
+       int hwcursor;
+       int fixed_mode;
+       int ring_active;
+
+       /* hw cursor */
+       int cursor_on;
+       int cursor_blanked;
+       u8  cursor_src[64];
+
+       /* initial parameters */
+       int initial_vga;
+       struct fb_var_screeninfo initial_var;
+       u32 initial_fb_base;
+       u32 initial_video_ram;
+       u32 initial_pitch;
+
+       /* driver registered */
+       int registered;
+};
+
+/*** function prototypes ***/
+
+extern int intelfb_var_to_depth(const struct fb_var_screeninfo *var);
+
+#endif /* _INTELFB_H */
diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c
new file mode 100644 (file)
index 0000000..a506cb4
--- /dev/null
@@ -0,0 +1,1557 @@
+/*
+ * intelfb
+ *
+ * Linux framebuffer driver for Intel(R) 830M/845G/852GM/855GM/865G
+ * integrated graphics chips.
+ *
+ * Copyright Â© 2002, 2003 David Dawes <dawes@xfree86.org>
+ *                   2004 Sylvain Meyer
+ *
+ * This driver consists of two parts.  The first part (intelfbdrv.c) provides
+ * the basic fbdev interfaces, is derived in part from the radeonfb and
+ * vesafb drivers, and is covered by the GPL.  The second part (intelfbhw.c)
+ * provides the code to program the hardware.  Most of it is derived from
+ * the i810/i830 XFree86 driver.  The HW-specific code is covered here
+ * under a dual license (GPL and MIT/XFree86 license).
+ *
+ * Author: David Dawes
+ *
+ */
+
+/* $DHD: intelfb/intelfbdrv.c,v 1.20 2003/06/27 15:17:40 dawes Exp $ */
+
+/*
+ * Changes:
+ *    01/2003 - Initial driver (0.1.0), no mode switching, no acceleration.
+ *             This initial version is a basic core that works a lot like
+ *             the vesafb driver.  It must be built-in to the kernel,
+ *             and the initial video mode must be set with vga=XXX at
+ *             boot time.  (David Dawes)
+ *
+ *    01/2003 - Version 0.2.0: Mode switching added, colormap support
+ *             implemented, Y panning, and soft screen blanking implemented.
+ *             No acceleration yet.  (David Dawes)
+ *
+ *    01/2003 - Version 0.3.0: fbcon acceleration support added.  Module
+ *             option handling added.  (David Dawes)
+ *
+ *    01/2003 - Version 0.4.0: fbcon HW cursor support added.  (David Dawes)
+ *
+ *    01/2003 - Version 0.4.1: Add auto-generation of built-in modes.
+ *             (David Dawes)
+ *
+ *    02/2003 - Version 0.4.2: Add check for active non-CRT devices, and
+ *             mode validation checks.  (David Dawes)
+ *
+ *    02/2003 - Version 0.4.3: Check when the VC is in graphics mode so that
+ *             acceleration is disabled while an XFree86 server is running.
+ *             (David Dawes)
+ *
+ *    02/2003 - Version 0.4.4: Monitor DPMS support.  (David Dawes)
+ *
+ *    02/2003 - Version 0.4.5: Basic XFree86 + fbdev working.  (David Dawes)
+ *
+ *    02/2003 - Version 0.5.0: Modify to work with the 2.5.32 kernel as well
+ *             as 2.4.x kernels.  (David Dawes)
+ *
+ *    02/2003 - Version 0.6.0: Split out HW-specifics into a separate file.
+ *             (David Dawes)
+ *
+ *    02/2003 - Version 0.7.0: Test on 852GM/855GM.  Acceleration and HW
+ *             cursor are disabled on this platform.  (David Dawes)
+ *
+ *    02/2003 - Version 0.7.1: Test on 845G.  Acceleration is disabled
+ *             on this platform.  (David Dawes)
+ *
+ *    02/2003 - Version 0.7.2: Test on 830M.  Acceleration and HW
+ *             cursor are disabled on this platform.  (David Dawes)
+ *
+ *    02/2003 - Version 0.7.3: Fix 8-bit modes for mobile platforms
+ *             (David Dawes)
+ *
+ *    02/2003 - Version 0.7.4: Add checks for FB and FBCON_HAS_CFB* configured
+ *             in the kernel, and add mode bpp verification and default
+ *             bpp selection based on which FBCON_HAS_CFB* are configured.
+ *             (David Dawes)
+ *
+ *    02/2003 - Version 0.7.5: Add basic package/install scripts based on the
+ *             DRI packaging scripts.  (David Dawes)
+ *
+ *    04/2003 - Version 0.7.6: Fix typo that affects builds with SMP-enabled
+ *             kernels.  (David Dawes, reported by Anupam).
+ *
+ *    06/2003 - Version 0.7.7:
+ *              Fix Makefile.kernel build problem (Tsutomu Yasuda).
+ *             Fix mis-placed #endif (2.4.21 kernel).
+ *
+ *    09/2004 - Version 0.9.0 - by Sylvain Meyer
+ *              Port to linux 2.6 kernel fbdev
+ *              Fix HW accel and HW cursor on i845G
+ *              Use of agpgart for fb memory reservation
+ *              Add mtrr support
+ *
+ *    10/2004 - Version 0.9.1
+ *              Use module_param instead of old MODULE_PARM
+ *              Some cleanup
+ *
+ *    11/2004 - Version 0.9.2
+ *              Add vram option to reserve more memory than stolen by BIOS
+ *              Fix intelfbhw_pan_display typo
+ *              Add __initdata annotations
+ *
+ * TODO:
+ *
+ *
+ * Wish List:
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/selection.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/kd.h>
+#include <linux/vt_kern.h>
+#include <linux/pagemap.h>
+#include <linux/version.h>
+
+#include <asm/io.h>
+
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include "intelfb.h"
+#include "intelfbdrv.h"
+#include "intelfbhw.h"
+
+/*
+ * Limiting the class to PCI_CLASS_DISPLAY_VGA prevents function 1 of the
+ * mobile chipsets from being registered.
+ */
+#if DETECT_VGA_CLASS_ONLY
+#define INTELFB_CLASS_MASK ~0 << 8
+#else
+#define INTELFB_CLASS_MASK 0
+#endif
+
+static struct pci_device_id intelfb_pci_table[] __devinitdata = {
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_830M, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_830M },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_845G, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_845G },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_85XGM, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_85XGM },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_865G, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, INTELFB_CLASS_MASK, INTEL_865G },
+       { 0, }
+};
+
+/* Global data */
+static int num_registered = 0;
+
+/* fb ops */
+static struct fb_ops intel_fb_ops = {
+       .owner =                THIS_MODULE,
+       .fb_check_var =         intelfb_check_var,
+       .fb_set_par =           intelfb_set_par,
+       .fb_setcolreg =         intelfb_setcolreg,
+       .fb_blank =             intelfb_blank,
+       .fb_pan_display =       intelfb_pan_display,
+       .fb_fillrect  =         intelfb_fillrect,
+       .fb_copyarea  =         intelfb_copyarea,
+       .fb_imageblit =         intelfb_imageblit,
+       .fb_cursor =            intelfb_cursor,
+       .fb_sync =              intelfb_sync,
+       .fb_ioctl =             intelfb_ioctl
+};
+
+/* PCI driver module table */
+static struct pci_driver intelfb_driver = {
+       .name =         "Intel(R) " SUPPORTED_CHIPSETS " Framebuffer Driver",
+       .id_table =     intelfb_pci_table,
+       .probe =        intelfb_pci_register,
+       .remove =       __devexit_p(intelfb_pci_unregister)
+};
+
+/* Module description/parameters */
+MODULE_AUTHOR("David Dawes <dawes@tungstengraphics.com>, "
+             "Sylvain Meyer <sylvain.meyer@worldonline.fr>");
+MODULE_DESCRIPTION(
+       "Framebuffer driver for Intel(R) " SUPPORTED_CHIPSETS " chipsets");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DEVICE_TABLE(pci, intelfb_pci_table);
+
+static int accel        __initdata = 1;
+static int vram         __initdata = 4;
+static int hwcursor     __initdata = 1;
+static int mtrr         __initdata = 1;
+static int fixed        __initdata = 0;
+static int noinit       __initdata = 0;
+static int noregister   __initdata = 0;
+static int probeonly    __initdata = 0;
+static int idonly       __initdata = 0;
+static int bailearly    __initdata = 0;
+static char *mode       __initdata = NULL;
+
+module_param(accel, bool, S_IRUGO);
+MODULE_PARM_DESC(accel, "Enable console acceleration");
+module_param(vram, int, S_IRUGO);
+MODULE_PARM_DESC(vram, "System RAM to allocate to framebuffer in MiB");
+module_param(hwcursor, bool, S_IRUGO);
+MODULE_PARM_DESC(hwcursor, "Enable HW cursor");
+module_param(mtrr, bool, S_IRUGO);
+MODULE_PARM_DESC(mtrr, "Enable MTRR support");
+module_param(fixed, bool, S_IRUGO);
+MODULE_PARM_DESC(fixed, "Disable mode switching");
+module_param(noinit, bool, 0);
+MODULE_PARM_DESC(noinit, "Don't initialise graphics mode when loading");
+module_param(noregister, bool, 0);
+MODULE_PARM_DESC(noregister, "Don't register, just probe and exit (debug)");
+module_param(probeonly, bool, 0);
+MODULE_PARM_DESC(probeonly, "Do a minimal probe (debug)");
+module_param(idonly, bool, 0);
+MODULE_PARM_DESC(idonly, "Just identify without doing anything else (debug)");
+module_param(bailearly, bool, 0);
+MODULE_PARM_DESC(bailearly, "Bail out early, depending on value (debug)");
+module_param(mode, charp, S_IRUGO);
+MODULE_PARM_DESC(mode,
+                "Initial video mode \"<xres>x<yres>[-<depth>][@<refresh>]\"");
+/***************************************************************
+ *                     modules entry points                    *
+ ***************************************************************/
+
+/* module load/unload entry points */
+int __init
+intelfb_init(void)
+{
+#ifndef MODULE
+       char *option = NULL;
+#endif
+
+       DBG_MSG("intelfb_init\n");
+
+       INF_MSG("Framebuffer driver for "
+               "Intel(R) " SUPPORTED_CHIPSETS " chipsets\n");
+       INF_MSG("Version " INTELFB_VERSION "\n");
+
+       if (idonly)
+               return -ENODEV;
+
+#ifndef MODULE
+       if (fb_get_options("intelfb", &option))
+               return -ENODEV;
+       intelfb_setup(option);
+#endif
+
+       return pci_module_init(&intelfb_driver);
+}
+
+static void __exit
+intelfb_exit(void)
+{
+       DBG_MSG("intelfb_exit\n");
+       pci_unregister_driver(&intelfb_driver);
+}
+
+#ifndef MODULE
+#define OPT_EQUAL(opt, name) (!strncmp(opt, name, strlen(name)))
+#define OPT_INTVAL(opt, name) simple_strtoul(opt + strlen(name), NULL, 0)
+#define OPT_STRVAL(opt, name) (opt + strlen(name))
+
+static __inline__ char *
+get_opt_string(const char *this_opt, const char *name)
+{
+       const char *p;
+       int i;
+       char *ret;
+
+       p = OPT_STRVAL(this_opt, name);
+       i = 0;
+       while (p[i] && p[i] != ' ' && p[i] != ',')
+               i++;
+       ret = kmalloc(i + 1, GFP_KERNEL);
+       if (ret) {
+               strncpy(ret, p, i);
+               ret[i] = '\0';
+       }
+       return ret;
+}
+
+static __inline__ int
+get_opt_int(const char *this_opt, const char *name, int *ret)
+{
+       if (!ret)
+               return 0;
+
+       if (!OPT_EQUAL(this_opt, name))
+               return 0;
+
+       *ret = OPT_INTVAL(this_opt, name);
+       return 1;
+}
+
+static __inline__ int
+get_opt_bool(const char *this_opt, const char *name, int *ret)
+{
+       if (!ret)
+               return 0;
+
+       if (OPT_EQUAL(this_opt, name)) {
+               if (this_opt[strlen(name)] == '=')
+                       *ret = simple_strtoul(this_opt + strlen(name) + 1,
+                                             NULL, 0);
+               else
+                       *ret = 1;
+       } else {
+               if (OPT_EQUAL(this_opt, "no") && OPT_EQUAL(this_opt + 2, name))
+                       *ret = 0;
+               else
+                       return 0;
+       }
+       return 1;
+}
+
+int __init
+intelfb_setup(char *options)
+{
+       char *this_opt;
+
+       DBG_MSG("intelfb_setup\n");
+
+       if (!options || !*options) {
+               DBG_MSG("no options\n");
+               return 0;
+       } else
+               DBG_MSG("options: %s\n", options);
+
+       /*
+        * These are the built-in options analogous to the module parameters
+        * defined above.
+        *
+        * The syntax is:
+        *
+        *    video=intelfb:[mode][,<param>=<val>] ...
+        *
+        * e.g.,
+        *
+        *    video=intelfb:1024x768-16@75,accel=0
+        */
+
+       while ((this_opt = strsep(&options, ","))) {
+               if (!*this_opt)
+                       continue;
+               if (get_opt_bool(this_opt, "accel", &accel))
+                       ;
+               else if (get_opt_int(this_opt, "vram", &vram))
+                       ;
+               else if (get_opt_bool(this_opt, "hwcursor", &hwcursor))
+                       ;
+               else if (get_opt_bool(this_opt, "mtrr", &mtrr))
+                       ;
+               else if (get_opt_bool(this_opt, "fixed", &fixed))
+                       ;
+               else if (get_opt_bool(this_opt, "init", &noinit))
+                       noinit = !noinit;
+               else if (OPT_EQUAL(this_opt, "mode="))
+                       mode = get_opt_string(this_opt, "mode=");
+               else
+                       mode = this_opt;
+       }
+
+       return 0;
+}
+
+#endif
+
+module_init(intelfb_init);
+
+#ifdef MODULE
+module_exit(intelfb_exit);
+#endif
+
+/***************************************************************
+ *                     mtrr support functions                  *
+ ***************************************************************/
+
+#ifdef CONFIG_MTRR
+static inline void __devinit set_mtrr(struct intelfb_info *dinfo)
+{
+       dinfo->mtrr_reg = mtrr_add(dinfo->aperture.physical,
+                                  dinfo->aperture.size, MTRR_TYPE_WRCOMB, 1);
+       if (dinfo->mtrr_reg < 0) {
+               ERR_MSG("unable to set MTRR\n");
+               return;
+       }
+       dinfo->has_mtrr = 1;
+}
+static inline void unset_mtrr(struct intelfb_info *dinfo)
+{
+       if (dinfo->has_mtrr)
+               mtrr_del(dinfo->mtrr_reg, dinfo->aperture.physical,
+                        dinfo->aperture.size);
+}
+#else
+#define set_mtrr(x) WRN_MSG("MTRR is disabled in the kernel\n")
+
+#define unset_mtrr(x) do { } while (0)
+#endif /* CONFIG_MTRR */
+
+/***************************************************************
+ *                        driver init / cleanup                *
+ ***************************************************************/
+
+static void
+cleanup(struct intelfb_info *dinfo)
+{
+       DBG_MSG("cleanup\n");
+
+       if (!dinfo)
+               return;
+
+       fb_dealloc_cmap(&dinfo->info->cmap);
+       kfree(dinfo->info->pixmap.addr);
+
+       if (dinfo->registered)
+               unregister_framebuffer(dinfo->info);
+
+       unset_mtrr(dinfo);
+
+       if (dinfo->fbmem_gart && dinfo->gtt_fb_mem) {
+               agp_unbind_memory(dinfo->gtt_fb_mem);
+               agp_free_memory(dinfo->gtt_fb_mem);
+       }
+       if (dinfo->gtt_cursor_mem) {
+               agp_unbind_memory(dinfo->gtt_cursor_mem);
+               agp_free_memory(dinfo->gtt_cursor_mem);
+       }
+       if (dinfo->gtt_ring_mem) {
+               agp_unbind_memory(dinfo->gtt_ring_mem);
+               agp_free_memory(dinfo->gtt_ring_mem);
+       }
+
+       if (dinfo->mmio_base)
+               iounmap((void __iomem *)dinfo->mmio_base);
+       if (dinfo->aperture.virtual)
+               iounmap((void __iomem *)dinfo->aperture.virtual);
+
+       if (dinfo->mmio_base_phys)
+               release_mem_region(dinfo->mmio_base_phys, INTEL_REG_SIZE);
+       if (dinfo->aperture.physical)
+               release_mem_region(dinfo->aperture.physical,
+                                  dinfo->aperture.size);
+       framebuffer_release(dinfo->info);
+}
+
+#define bailout(dinfo) do {                                            \
+       DBG_MSG("bailout\n");                                           \
+       cleanup(dinfo);                                                 \
+       INF_MSG("Not going to register framebuffer, exiting...\n");     \
+       return -ENODEV;                                                 \
+} while (0)
+
+
+static int __devinit
+intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct fb_info *info;
+       struct intelfb_info *dinfo;
+       int i, j, err, dvo;
+       int aperture_size, stolen_size;
+       struct agp_kern_info gtt_info;
+       int agp_memtype;
+       const char *s;
+
+       DBG_MSG("intelfb_pci_register\n");
+
+       num_registered++;
+       if (num_registered != 1) {
+               ERR_MSG("Attempted to register %d devices "
+                       "(should be only 1).\n", num_registered);
+               return -ENODEV;
+       }
+
+       info = framebuffer_alloc(sizeof(struct intelfb_info), &pdev->dev);
+       if (!info) {
+               ERR_MSG("Could not allocate memory for intelfb_info.\n");
+               return -ENODEV;
+       }
+       if (fb_alloc_cmap(&info->cmap, 256, 1) < 0) {
+               ERR_MSG("Could not allocate cmap for intelfb_info.\n");
+               goto err_out_cmap;
+               return -ENODEV;
+       }
+
+       dinfo = info->par;
+       dinfo->info  = info;
+       dinfo->fbops = &intel_fb_ops;
+       dinfo->pdev  = pdev;
+
+       /* Reserve pixmap space. */
+       info->pixmap.addr = kmalloc(64 * 1024, GFP_KERNEL);
+       if (info->pixmap.addr == NULL) {
+               ERR_MSG("Cannot reserve pixmap memory.\n");
+               goto err_out_pixmap;
+       }
+       memset(info->pixmap.addr, 0, 64 * 1024);
+
+       /* set early this option because it could be changed by tv encoder
+          driver */
+       dinfo->fixed_mode = fixed;
+
+       /* Enable device. */
+       if ((err = pci_enable_device(pdev))) {
+               ERR_MSG("Cannot enable device.\n");
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+
+       /* Set base addresses. */
+       dinfo->aperture.physical = pci_resource_start(pdev, 0);
+       dinfo->aperture.size     = pci_resource_len(pdev, 0);
+       dinfo->mmio_base_phys    = pci_resource_start(pdev, 1);
+
+       DBG_MSG("fb aperture: 0x%lx/0x%lx, MMIO region: 0x%lx/0x%lx\n",
+               pci_resource_start(pdev, 0), pci_resource_len(pdev, 0),
+               pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
+
+       /* Reserve the fb and MMIO regions */
+       if (!request_mem_region(dinfo->aperture.physical, dinfo->aperture.size,
+                               INTELFB_MODULE_NAME)) {
+               ERR_MSG("Cannot reserve FB region.\n");
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+       if (!request_mem_region(dinfo->mmio_base_phys,
+                               INTEL_REG_SIZE,
+                               INTELFB_MODULE_NAME)) {
+               ERR_MSG("Cannot reserve MMIO region.\n");
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+
+       /* Map the fb and MMIO regions */
+       dinfo->aperture.virtual = (u8 __iomem *)ioremap_nocache
+               (dinfo->aperture.physical, dinfo->aperture.size);
+       if (!dinfo->aperture.virtual) {
+               ERR_MSG("Cannot remap FB region.\n");
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+       dinfo->mmio_base =
+               (u8 __iomem *)ioremap_nocache(dinfo->mmio_base_phys,
+                                              INTEL_REG_SIZE);
+       if (!dinfo->mmio_base) {
+               ERR_MSG("Cannot remap MMIO region.\n");
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+
+       /* Get the chipset info. */
+       dinfo->pci_chipset = pdev->device;
+
+       if (intelfbhw_get_chipset(pdev, &dinfo->name, &dinfo->chipset,
+                                 &dinfo->mobile)) {
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+
+       if (intelfbhw_get_memory(pdev, &aperture_size,&stolen_size)) {
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+
+       INF_MSG("%02x:%02x.%d: %s, aperture size %dMB, "
+               "stolen memory %dkB\n",
+               pdev->bus->number, PCI_SLOT(pdev->devfn),
+               PCI_FUNC(pdev->devfn), dinfo->name,
+               BtoMB(aperture_size), BtoKB(stolen_size));
+
+       /* Set these from the options. */
+       dinfo->accel    = accel;
+       dinfo->hwcursor = hwcursor;
+
+       if (NOACCEL_CHIPSET(dinfo) && dinfo->accel == 1) {
+               INF_MSG("Acceleration is not supported for the %s chipset.\n",
+                       dinfo->name);
+               dinfo->accel = 0;
+       }
+
+       /* Framebuffer parameters - Use all the stolen memory if >= vram */
+       if (ROUND_UP_TO_PAGE(stolen_size) >= MB(vram)) {
+               dinfo->fb.size = ROUND_UP_TO_PAGE(stolen_size);
+               dinfo->fbmem_gart = 0;
+       } else {
+               dinfo->fb.size =  MB(vram);
+               dinfo->fbmem_gart = 1;
+       }
+
+       /* Allocate space for the ring buffer and HW cursor if enabled. */
+       if (dinfo->accel) {
+               dinfo->ring.size = RINGBUFFER_SIZE;
+               dinfo->ring_tail_mask = dinfo->ring.size - 1;
+       }
+       if (dinfo->hwcursor) {
+               dinfo->cursor.size = HW_CURSOR_SIZE;
+       }
+
+       /* Use agpgart to manage the GATT */
+       if (agp_backend_acquire()) {
+               ERR_MSG("cannot acquire agp\n");
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+
+       /* get the current gatt info */
+       if (agp_copy_info(&gtt_info)) {
+               ERR_MSG("cannot get agp info\n");
+               agp_backend_release();
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+
+       /* set the mem offsets - set them after the already used pages */
+       if (dinfo->accel) {
+               dinfo->ring.offset = (stolen_size >> 12)
+                       + gtt_info.current_memory;
+       }
+       if (dinfo->hwcursor) {
+               dinfo->cursor.offset = (stolen_size >> 12) +
+                       + gtt_info.current_memory + (dinfo->ring.size >> 12);
+       }
+       if (dinfo->fbmem_gart) {
+               dinfo->fb.offset = (stolen_size >> 12) +
+                       + gtt_info.current_memory + (dinfo->ring.size >> 12)
+                       + (dinfo->cursor.size >> 12);
+       }
+
+       /* Allocate memories (which aren't stolen) */
+       if (dinfo->accel) {
+               if (!(dinfo->gtt_ring_mem =
+                     agp_allocate_memory(dinfo->ring.size >> 12,
+                                         AGP_NORMAL_MEMORY))) {
+                       ERR_MSG("cannot allocate ring buffer memory\n");
+                       agp_backend_release();
+                       cleanup(dinfo);
+                       return -ENOMEM;
+               }
+               if (agp_bind_memory(dinfo->gtt_ring_mem,
+                                   dinfo->ring.offset)) {
+                       ERR_MSG("cannot bind ring buffer memory\n");
+                       agp_backend_release();
+                       cleanup(dinfo);
+                       return -EBUSY;
+               }
+               dinfo->ring.physical = dinfo->aperture.physical
+                       + (dinfo->ring.offset << 12);
+               dinfo->ring.virtual  = dinfo->aperture.virtual
+                       + (dinfo->ring.offset << 12);
+               dinfo->ring_head = dinfo->ring.virtual;
+       }
+       if (dinfo->hwcursor) {
+               agp_memtype = dinfo->mobile ? AGP_PHYSICAL_MEMORY
+                       : AGP_NORMAL_MEMORY;
+               if (!(dinfo->gtt_cursor_mem =
+                     agp_allocate_memory(dinfo->cursor.size >> 12,
+                                         agp_memtype))) {
+                       ERR_MSG("cannot allocate cursor memory\n");
+                       agp_backend_release();
+                       cleanup(dinfo);
+                       return -ENOMEM;
+               }
+               if (agp_bind_memory(dinfo->gtt_cursor_mem,
+                                   dinfo->cursor.offset)) {
+                       ERR_MSG("cannot bind cursor memory\n");
+                       agp_backend_release();
+                       cleanup(dinfo);
+                       return -EBUSY;
+               }
+               if (dinfo->mobile)
+                       dinfo->cursor.physical
+                               = dinfo->gtt_cursor_mem->physical;
+               else
+                       dinfo->cursor.physical = dinfo->aperture.physical
+                               + (dinfo->cursor.offset << 12);
+               dinfo->cursor.virtual = dinfo->aperture.virtual
+                       + (dinfo->cursor.offset << 12);
+       }
+       if (dinfo->fbmem_gart) {
+               if (!(dinfo->gtt_fb_mem =
+                     agp_allocate_memory(dinfo->fb.size >> 12,
+                                         AGP_NORMAL_MEMORY))) {
+                       WRN_MSG("cannot allocate framebuffer memory - use "
+                               "the stolen one\n");
+                       dinfo->fbmem_gart = 0;
+               }
+               if (agp_bind_memory(dinfo->gtt_fb_mem,
+                                   dinfo->fb.offset)) {
+                       WRN_MSG("cannot bind framebuffer memory - use "
+                               "the stolen one\n");
+                       dinfo->fbmem_gart = 0;
+               }
+       }
+
+       /* update framebuffer memory parameters */
+       if (!dinfo->fbmem_gart)
+               dinfo->fb.offset = 0;   /* starts at offset 0 */
+       dinfo->fb.physical = dinfo->aperture.physical
+               + (dinfo->fb.offset << 12);
+       dinfo->fb.virtual = dinfo->aperture.virtual + (dinfo->fb.offset << 12);
+       dinfo->fb_start = dinfo->fb.offset << 12;
+
+       /* release agpgart */
+       agp_backend_release();
+
+       if (mtrr)
+               set_mtrr(dinfo);
+
+       DBG_MSG("fb: 0x%x(+ 0x%x)/0x%x (0x%x)\n",
+               dinfo->fb.physical, dinfo->fb.offset, dinfo->fb.size,
+               (u32 __iomem ) dinfo->fb.virtual);
+       DBG_MSG("MMIO: 0x%x/0x%x (0x%x)\n",
+               dinfo->mmio_base_phys, INTEL_REG_SIZE,
+               (u32 __iomem) dinfo->mmio_base);
+       DBG_MSG("ring buffer: 0x%x/0x%x (0x%x)\n",
+               dinfo->ring.physical, dinfo->ring.size,
+               (u32 __iomem ) dinfo->ring.virtual);
+       DBG_MSG("HW cursor: 0x%x/0x%x (0x%x) (offset 0x%x) (phys 0x%x)\n",
+               dinfo->cursor.physical, dinfo->cursor.size,
+               (u32 __iomem ) dinfo->cursor.virtual, dinfo->cursor.offset,
+               dinfo->cursor.physical);
+
+       DBG_MSG("options: vram = %d, accel = %d, hwcursor = %d, fixed = %d, "
+               "noinit = %d\n", vram, accel, hwcursor, fixed, noinit);
+       DBG_MSG("options: mode = \"%s\"\n", mode ? mode : "");
+
+       if (probeonly)
+               bailout(dinfo);
+
+       /*
+        * Check if the LVDS port or any DVO ports are enabled.  If so,
+        * don't allow mode switching
+        */
+       dvo = intelfbhw_check_non_crt(dinfo);
+       if (dvo) {
+               dinfo->fixed_mode = 1;
+               WRN_MSG("Non-CRT device is enabled ( ");
+               i = 0;
+               while (dvo) {
+                       if (dvo & 1) {
+                               s = intelfbhw_dvo_to_string(1 << i);
+                               if (s)
+                                       printk("%s ", s);
+                       }
+                       dvo >>= 1;
+                       ++i;
+               }
+               printk(").  Disabling mode switching.\n");
+       }
+
+       if (bailearly == 1)
+               bailout(dinfo);
+
+       if (FIXED_MODE(dinfo) && ORIG_VIDEO_ISVGA != VIDEO_TYPE_VLFB) {
+               ERR_MSG("Video mode must be programmed at boot time.\n");
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+
+       if (bailearly == 2)
+               bailout(dinfo);
+
+       /* Initialise dinfo and related data. */
+       /* If an initial mode was programmed at boot time, get its details. */
+       if (ORIG_VIDEO_ISVGA == VIDEO_TYPE_VLFB)
+               get_initial_mode(dinfo);
+
+       if (bailearly == 3)
+               bailout(dinfo);
+
+       if (FIXED_MODE(dinfo)) {
+               /* remap fb address */
+               update_dinfo(dinfo, &dinfo->initial_var);
+       }
+
+       if (bailearly == 4)
+               bailout(dinfo);
+
+
+       if (intelfb_set_fbinfo(dinfo)) {
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+
+       if (bailearly == 5)
+               bailout(dinfo);
+
+       for (i = 0; i < 16; i++) {
+               j = color_table[i];
+               dinfo->palette[i].red = default_red[j];
+               dinfo->palette[i].green = default_grn[j];
+               dinfo->palette[i].blue = default_blu[j];
+       }
+
+       if (bailearly == 6)
+               bailout(dinfo);
+
+       pci_set_drvdata(pdev, dinfo);
+
+       /* Save the initial register state. */
+       i = intelfbhw_read_hw_state(dinfo, &dinfo->save_state,
+                                   bailearly > 6 ? bailearly - 6 : 0);
+       if (i != 0) {
+               DBG_MSG("intelfbhw_read_hw_state returned %d\n", i);
+               bailout(dinfo);
+       }
+
+       intelfbhw_print_hw_state(dinfo, &dinfo->save_state);
+
+       if (bailearly == 18)
+               bailout(dinfo);
+
+       /* Cursor initialisation */
+       if (dinfo->hwcursor) {
+               intelfbhw_cursor_init(dinfo);
+               intelfbhw_cursor_reset(dinfo);
+       }
+
+       if (bailearly == 19)
+               bailout(dinfo);
+
+       /* 2d acceleration init */
+       if (dinfo->accel)
+               intelfbhw_2d_start(dinfo);
+
+       if (bailearly == 20)
+               bailout(dinfo);
+
+       if (noregister)
+               bailout(dinfo);
+
+       if (register_framebuffer(dinfo->info) < 0) {
+               ERR_MSG("Cannot register framebuffer.\n");
+               cleanup(dinfo);
+               return -ENODEV;
+       }
+
+       dinfo->registered = 1;
+
+       return 0;
+
+err_out_pixmap:
+       fb_dealloc_cmap(&info->cmap);
+err_out_cmap:
+       framebuffer_release(info);
+       return -ENODEV;
+}
+
+static void __devexit
+intelfb_pci_unregister(struct pci_dev *pdev)
+{
+       struct intelfb_info *dinfo = pci_get_drvdata(pdev);
+
+       DBG_MSG("intelfb_pci_unregister\n");
+
+       if (!dinfo)
+               return;
+
+       cleanup(dinfo);
+
+       pci_set_drvdata(pdev, NULL);
+}
+
+/***************************************************************
+ *                       helper functions                      *
+ ***************************************************************/
+
+int __inline__
+intelfb_var_to_depth(const struct fb_var_screeninfo *var)
+{
+       DBG_MSG("intelfb_var_to_depth: bpp: %d, green.length is %d\n",
+               var->bits_per_pixel, var->green.length);
+
+       switch (var->bits_per_pixel) {
+       case 16:
+               return (var->green.length == 6) ? 16 : 15;
+       case 32:
+               return 24;
+       default:
+               return var->bits_per_pixel;
+       }
+}
+
+
+static __inline__ int
+var_to_refresh(const struct fb_var_screeninfo *var)
+{
+       int xtot = var->xres + var->left_margin + var->right_margin +
+                  var->hsync_len;
+       int ytot = var->yres + var->upper_margin + var->lower_margin +
+                  var->vsync_len;
+
+       return (1000000000 / var->pixclock * 1000 + 500) / xtot / ytot;
+}
+
+/***************************************************************
+ *                Various intialisation functions              *
+ ***************************************************************/
+
+static void __devinit
+get_initial_mode(struct intelfb_info *dinfo)
+{
+       struct fb_var_screeninfo *var;
+       int xtot, ytot;
+
+       DBG_MSG("get_initial_mode\n");
+
+       dinfo->initial_vga = 1;
+       dinfo->initial_fb_base = screen_info.lfb_base;
+       dinfo->initial_video_ram = screen_info.lfb_size * KB(64);
+       dinfo->initial_pitch = screen_info.lfb_linelength;
+
+       var = &dinfo->initial_var;
+       memset(var, 0, sizeof(*var));
+       var->xres = screen_info.lfb_width;
+       var->yres = screen_info.lfb_height;
+       var->bits_per_pixel = screen_info.lfb_depth;
+       switch (screen_info.lfb_depth) {
+       case 15:
+               var->bits_per_pixel = 16;
+               break;
+       case 24:
+               var->bits_per_pixel = 32;
+               break;
+       }
+
+       DBG_MSG("Initial info: FB is 0x%x/0x%x (%d kByte)\n",
+               dinfo->initial_fb_base, dinfo->initial_video_ram,
+               BtoKB(dinfo->initial_video_ram));
+
+       DBG_MSG("Initial info: mode is %dx%d-%d (%d)\n",
+               var->xres, var->yres, var->bits_per_pixel,
+               dinfo->initial_pitch);
+
+       /* Dummy timing values (assume 60Hz) */
+       var->left_margin = (var->xres / 8) & 0xf8;
+       var->right_margin = 32;
+       var->upper_margin = 16;
+       var->lower_margin = 4;
+       var->hsync_len = (var->xres / 8) & 0xf8;
+       var->vsync_len = 4;
+
+       xtot = var->xres + var->left_margin +
+               var->right_margin + var->hsync_len;
+       ytot = var->yres + var->upper_margin +
+               var->lower_margin + var->vsync_len;
+       var->pixclock = 10000000 / xtot * 1000 / ytot * 100 / 60;
+
+       var->height = -1;
+       var->width = -1;
+
+       if (var->bits_per_pixel > 8) {
+               var->red.offset = screen_info.red_pos;
+               var->red.length = screen_info.red_size;
+               var->green.offset = screen_info.green_pos;
+               var->green.length = screen_info.green_size;
+               var->blue.offset = screen_info.blue_pos;
+               var->blue.length = screen_info.blue_size;
+               var->transp.offset = screen_info.rsvd_pos;
+               var->transp.length = screen_info.rsvd_size;
+       } else {
+               var->red.length = 8;
+               var->green.length = 8;
+               var->blue.length = 8;
+       }
+}
+
+static int __devinit
+intelfb_init_var(struct intelfb_info *dinfo)
+{
+       struct fb_var_screeninfo *var;
+       int msrc = 0;
+
+       DBG_MSG("intelfb_init_var\n");
+
+       var = &dinfo->info->var;
+       if (FIXED_MODE(dinfo)) {
+               memcpy(var, &dinfo->initial_var,
+                      sizeof(struct fb_var_screeninfo));
+               msrc = 5;
+       } else {
+               if (mode) {
+                       msrc = fb_find_mode(var, dinfo->info, mode,
+                                           NULL, 0, NULL, 0);
+                       if (msrc)
+                               msrc |= 8;
+               }
+               if (!msrc) {
+                       msrc = fb_find_mode(var, dinfo->info, PREFERRED_MODE,
+                                           NULL, 0, NULL, 0);
+               }
+       }
+
+       if (!msrc) {
+               ERR_MSG("Cannot find a suitable video mode.\n");
+               return 1;
+       }
+
+       INF_MSG("Initial video mode is %dx%d-%d@%d.\n", var->xres, var->yres,
+               var->bits_per_pixel, var_to_refresh(var));
+
+       DBG_MSG("Initial video mode is from %d.\n", msrc);
+
+#if ALLOCATE_FOR_PANNING
+       /* Allow use of half of the video ram for panning */
+       var->xres_virtual = var->xres;
+       var->yres_virtual =
+               dinfo->fb.size / 2 / (var->bits_per_pixel * var->xres);
+       if (var->yres_virtual < var->yres)
+               var->yres_virtual = var->yres;
+#else
+       var->yres_virtual = var->yres;
+#endif
+
+       if (dinfo->accel)
+               var->accel_flags |= FB_ACCELF_TEXT;
+       else
+               var->accel_flags &= ~FB_ACCELF_TEXT;
+
+       return 0;
+}
+
+static int __devinit
+intelfb_set_fbinfo(struct intelfb_info *dinfo)
+{
+       struct fb_info *info = dinfo->info;
+
+       DBG_MSG("intelfb_set_fbinfo\n");
+
+       info->flags = FBINFO_FLAG_DEFAULT;
+       info->fbops = &intel_fb_ops;
+       info->pseudo_palette = dinfo->pseudo_palette;
+
+       info->pixmap.size = 64*1024;
+       info->pixmap.buf_align = 8;
+       info->pixmap.flags = FB_PIXMAP_SYSTEM;
+
+       if (intelfb_init_var(dinfo))
+               return 1;
+
+       info->pixmap.scan_align = 1;
+
+       update_dinfo(dinfo, &info->var);
+
+       return 0;
+}
+
+/* Update dinfo to match the active video mode. */
+static void
+update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var)
+{
+       DBG_MSG("update_dinfo\n");
+
+       dinfo->bpp = var->bits_per_pixel;
+       dinfo->depth = intelfb_var_to_depth(var);
+       dinfo->xres = var->xres;
+       dinfo->yres = var->xres;
+       dinfo->pixclock = var->pixclock;
+
+       intelfb_get_fix(&dinfo->info->fix, dinfo->info);
+
+       switch (dinfo->bpp) {
+       case 8:
+               dinfo->visual = FB_VISUAL_PSEUDOCOLOR;
+               dinfo->pitch = var->xres_virtual;
+               break;
+       case 16:
+               dinfo->visual = FB_VISUAL_TRUECOLOR;
+               dinfo->pitch = var->xres_virtual * 2;
+               break;
+       case 32:
+               dinfo->visual = FB_VISUAL_TRUECOLOR;
+               dinfo->pitch = var->xres_virtual * 4;
+               break;
+       }
+
+       /* Make sure the line length is a aligned correctly. */
+       dinfo->pitch = ROUND_UP_TO(dinfo->pitch, STRIDE_ALIGNMENT);
+
+       if (FIXED_MODE(dinfo))
+               dinfo->pitch = dinfo->initial_pitch;
+
+       dinfo->info->screen_base = (char __iomem *)dinfo->fb.virtual;
+       dinfo->info->fix.line_length = dinfo->pitch;
+       dinfo->info->fix.visual = dinfo->visual;
+}
+
+/* fbops functions */
+
+static int
+intelfb_get_fix(struct fb_fix_screeninfo *fix, struct fb_info *info)
+{
+       struct intelfb_info *dinfo = GET_DINFO(info);
+
+       DBG_MSG("intelfb_get_fix\n");
+
+       memset(fix, 0, sizeof(*fix));
+       strcpy(fix->id, dinfo->name);
+       fix->smem_start = dinfo->fb.physical;
+       fix->smem_len = dinfo->fb.size;
+       fix->type = FB_TYPE_PACKED_PIXELS;
+       fix->type_aux = 0;
+       fix->visual = dinfo->visual;
+       fix->xpanstep = 8;
+       fix->ypanstep = 1;
+       fix->ywrapstep = 0;
+       fix->line_length = dinfo->pitch;
+       fix->mmio_start = dinfo->mmio_base_phys;
+       fix->mmio_len = INTEL_REG_SIZE;
+       fix->accel = FB_ACCEL_I830;
+       return 0;
+}
+
+/***************************************************************
+ *                       fbdev interface                       *
+ ***************************************************************/
+
+static int
+intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       int change_var = 0;
+       struct fb_var_screeninfo v;
+       struct intelfb_info *dinfo;
+       static int first = 1;
+
+       DBG_MSG("intelfb_check_var: accel_flags is %d\n", var->accel_flags);
+
+       dinfo = GET_DINFO(info);
+
+       if (intelfbhw_validate_mode(dinfo, var) != 0)
+               return -EINVAL;
+
+       v = *var;
+
+       /* Check for a supported bpp. */
+       if (v.bits_per_pixel <= 8) {
+               v.bits_per_pixel = 8;
+       } else if (v.bits_per_pixel <= 16) {
+               if (v.bits_per_pixel == 16)
+                       v.green.length = 6;
+               v.bits_per_pixel = 16;
+       } else if (v.bits_per_pixel <= 32) {
+               v.bits_per_pixel = 32;
+       } else
+               return -EINVAL;
+
+       change_var = ((info->var.xres != var->xres) ||
+                     (info->var.yres != var->yres) ||
+                     (info->var.xres_virtual != var->xres_virtual) ||
+                     (info->var.yres_virtual != var->yres_virtual) ||
+                     (info->var.bits_per_pixel != var->bits_per_pixel) ||
+                     memcmp(&info->var.red, &var->red, sizeof(var->red)) ||
+                     memcmp(&info->var.green, &var->green,
+                            sizeof(var->green)) ||
+                     memcmp(&info->var.blue, &var->blue, sizeof(var->blue)));
+
+       if (FIXED_MODE(dinfo) &&
+           (change_var ||
+            var->yres_virtual > dinfo->initial_var.yres_virtual ||
+            var->yres_virtual < dinfo->initial_var.yres ||
+            var->xoffset || var->nonstd)) {
+               if (first) {
+                       ERR_MSG("Changing the video mode is not supported.\n");
+                       first = 0;
+               }
+               return -EINVAL;
+       }
+
+       switch (intelfb_var_to_depth(&v)) {
+       case 8:
+               v.red.offset = v.green.offset = v.blue.offset = 0;
+               v.red.length = v.green.length = v.blue.length = 8;
+               v.transp.offset = v.transp.length = 0;
+               break;
+       case 15:
+               v.red.offset = 10;
+               v.green.offset = 5;
+               v.blue.offset = 0;
+               v.red.length = v.green.length = v.blue.length = 5;
+               v.transp.offset = v.transp.length = 0;
+               break;
+       case 16:
+               v.red.offset = 11;
+               v.green.offset = 5;
+               v.blue.offset = 0;
+               v.red.length = 5;
+               v.green.length = 6;
+               v.blue.length = 5;
+               v.transp.offset = v.transp.length = 0;
+               break;
+       case 24:
+               v.red.offset = 16;
+               v.green.offset = 8;
+               v.blue.offset = 0;
+               v.red.length = v.green.length = v.blue.length = 8;
+               v.transp.offset = v.transp.length = 0;
+               break;
+       case 32:
+               v.red.offset = 16;
+               v.green.offset = 8;
+               v.blue.offset = 0;
+               v.red.length = v.green.length = v.blue.length = 8;
+               v.transp.offset = 24;
+               v.transp.length = 8;
+               break;
+       }
+
+       if (v.xoffset < 0)
+               v.xoffset = 0;
+       if (v.yoffset < 0)
+               v.yoffset = 0;
+
+       if (v.xoffset > v.xres_virtual - v.xres)
+               v.xoffset = v.xres_virtual - v.xres;
+       if (v.yoffset > v.yres_virtual - v.yres)
+               v.yoffset = v.yres_virtual - v.yres;
+
+       v.red.msb_right = v.green.msb_right = v.blue.msb_right =
+                         v.transp.msb_right = 0;
+
+        *var = v;
+
+       return 0;
+}
+
+static int
+intelfb_set_par(struct fb_info *info)
+{
+       struct intelfb_hwstate hw;
+
+        struct intelfb_info *dinfo = GET_DINFO(info);
+
+       if (FIXED_MODE(dinfo)) {
+               ERR_MSG("Changing the video mode is not supported.\n");
+               return -EINVAL;
+       }
+
+       DBG_MSG("intelfb_set_par (%dx%d-%d)\n", info->var.xres,
+               info->var.yres, info->var.bits_per_pixel);
+
+       intelfb_blank(FB_BLANK_POWERDOWN, info);
+
+       if (dinfo->accel)
+               intelfbhw_2d_stop(dinfo);
+
+       hw = dinfo->save_state;
+       if (intelfbhw_mode_to_hw(dinfo, &hw, &info->var))
+               return -EINVAL;
+       if (intelfbhw_program_mode(dinfo, &hw, 0))
+               return -EINVAL;
+
+#if REGDUMP > 0
+       intelfbhw_read_hw_state(dinfo, &hw, 0);
+       intelfbhw_print_hw_state(dinfo, &hw);
+#endif
+
+       update_dinfo(dinfo, &info->var);
+
+       if (dinfo->accel)
+               intelfbhw_2d_start(dinfo);
+
+       intelfb_pan_display(&info->var, info);
+
+       intelfb_blank(FB_BLANK_UNBLANK, info);
+
+       if (ACCEL(dinfo, info)) {
+               info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN |
+               FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT |
+               FBINFO_HWACCEL_IMAGEBLIT;
+       } else {
+               info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
+       }
+       return 0;
+}
+
+static int
+intelfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+                 unsigned blue, unsigned transp, struct fb_info *info)
+{
+       struct intelfb_info *dinfo = GET_DINFO(info);
+
+#if VERBOSE > 0
+       DBG_MSG("intelfb_setcolreg: regno %d, depth %d\n", regno, dinfo->depth);
+#endif
+
+       if (regno > 255)
+               return 1;
+
+       switch (dinfo->depth) {
+       case 8:
+               {
+                       red >>= 8;
+                       green >>= 8;
+                       blue >>= 8;
+
+                       dinfo->palette[regno].red = red;
+                       dinfo->palette[regno].green = green;
+                       dinfo->palette[regno].blue = blue;
+
+                       intelfbhw_setcolreg(dinfo, regno, red, green, blue,
+                                           transp);
+               }
+               break;
+       case 15:
+               dinfo->pseudo_palette[regno] = ((red & 0xf800) >>  1) |
+                                              ((green & 0xf800) >>  6) |
+                                              ((blue & 0xf800) >> 11);
+               break;
+       case 16:
+               dinfo->pseudo_palette[regno] = (red & 0xf800) |
+                                              ((green & 0xfc00) >>  5) |
+                                              ((blue  & 0xf800) >> 11);
+               break;
+       case 24:
+               dinfo->pseudo_palette[regno] = ((red & 0xff00) << 8) |
+                                              (green & 0xff00) |
+                                              ((blue  & 0xff00) >> 8);
+               break;
+       }
+       return 0;
+}
+
+static int
+intelfb_blank(int blank, struct fb_info *info)
+{
+       intelfbhw_do_blank(blank, info);
+       return 0;
+}
+
+static int
+intelfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       intelfbhw_pan_display(var, info);
+       return 0;
+}
+
+/* When/if we have our own ioctls. */
+static int
+intelfb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+             unsigned long arg, struct fb_info *info)
+{
+       int retval = 0;
+
+       return retval;
+}
+
+static void
+intelfb_fillrect (struct fb_info *info, const struct fb_fillrect *rect)
+{
+        struct intelfb_info *dinfo = GET_DINFO(info);
+       u32 rop, color;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfb_fillrect\n");
+#endif
+
+       if (!ACCEL(dinfo, info) || dinfo->depth == 4)
+               return cfb_fillrect(info, rect);
+
+       if (rect->rop == ROP_COPY)
+               rop = PAT_ROP_GXCOPY;
+       else // ROP_XOR
+               rop = PAT_ROP_GXXOR;
+
+       if (dinfo->depth != 8)
+               color = dinfo->pseudo_palette[rect->color];
+       else
+               color = rect->color;
+
+       intelfbhw_do_fillrect(dinfo, rect->dx, rect->dy,
+                             rect->width, rect->height, color,
+                             dinfo->pitch, info->var.bits_per_pixel,
+                             rop);
+}
+
+static void
+intelfb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
+{
+        struct intelfb_info *dinfo = GET_DINFO(info);
+
+#if VERBOSE > 0
+       DBG_MSG("intelfb_copyarea\n");
+#endif
+
+       if (!ACCEL(dinfo, info) || dinfo->depth == 4)
+               return cfb_copyarea(info, region);
+
+       intelfbhw_do_bitblt(dinfo, region->sx, region->sy, region->dx,
+                           region->dy, region->width, region->height,
+                           dinfo->pitch, info->var.bits_per_pixel);
+}
+
+static void
+intelfb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+        struct intelfb_info *dinfo = GET_DINFO(info);
+       u32 fgcolor, bgcolor;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfb_imageblit\n");
+#endif
+
+       if (!ACCEL(dinfo, info) || dinfo->depth == 4
+           || image->depth != 1)
+               return cfb_imageblit(info, image);
+
+       if (dinfo->depth != 8) {
+               fgcolor = dinfo->pseudo_palette[image->fg_color];
+               bgcolor = dinfo->pseudo_palette[image->bg_color];
+       } else {
+               fgcolor = image->fg_color;
+               bgcolor = image->bg_color;
+       }
+
+       if (!intelfbhw_do_drawglyph(dinfo, fgcolor, bgcolor, image->width,
+                                   image->height, image->data,
+                                   image->dx, image->dy,
+                                   dinfo->pitch, info->var.bits_per_pixel))
+               return cfb_imageblit(info, image);
+}
+
+static int
+intelfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+        struct intelfb_info *dinfo = GET_DINFO(info);
+
+#if VERBOSE > 0
+       DBG_MSG("intelfb_cursor\n");
+#endif
+
+       if (!dinfo->hwcursor)
+               return soft_cursor(info, cursor);
+
+       intelfbhw_cursor_hide(dinfo);
+
+       /* If XFree killed the cursor - restore it */
+       if (INREG(CURSOR_A_BASEADDR) != dinfo->cursor.offset << 12) {
+               u32 fg, bg;
+
+               DBG_MSG("the cursor was killed - restore it !!\n");
+               DBG_MSG("size %d, %d   pos %d, %d\n",
+                       cursor->image.width, cursor->image.height,
+                       cursor->image.dx, cursor->image.dy);
+
+               intelfbhw_cursor_init(dinfo);
+               intelfbhw_cursor_reset(dinfo);
+               intelfbhw_cursor_setpos(dinfo, cursor->image.dx,
+                                       cursor->image.dy);
+
+               if (dinfo->depth != 8) {
+                       fg =dinfo->pseudo_palette[cursor->image.fg_color];
+                       bg =dinfo->pseudo_palette[cursor->image.bg_color];
+               } else {
+                       fg = cursor->image.fg_color;
+                       bg = cursor->image.bg_color;
+               }
+               intelfbhw_cursor_setcolor(dinfo, bg, fg);
+               intelfbhw_cursor_load(dinfo, cursor->image.width,
+                                     cursor->image.height,
+                                     dinfo->cursor_src);
+
+               if (cursor->enable)
+                       intelfbhw_cursor_show(dinfo);
+               return 0;
+       }
+
+       if (cursor->set & FB_CUR_SETPOS) {
+               u32 dx, dy;
+
+               dx = cursor->image.dx - info->var.xoffset;
+               dy = cursor->image.dy - info->var.yoffset;
+
+               intelfbhw_cursor_setpos(dinfo, dx, dy);
+       }
+
+       if (cursor->set & FB_CUR_SETSIZE) {
+               if (cursor->image.width > 64 || cursor->image.height > 64)
+                       return -ENXIO;
+
+               intelfbhw_cursor_reset(dinfo);
+       }
+
+       if (cursor->set & FB_CUR_SETCMAP) {
+               u32 fg, bg;
+
+               if (dinfo->depth != 8) {
+                       fg = dinfo->pseudo_palette[cursor->image.fg_color];
+                       bg = dinfo->pseudo_palette[cursor->image.bg_color];
+               } else {
+                       fg = cursor->image.fg_color;
+                       bg = cursor->image.bg_color;
+               }
+
+               intelfbhw_cursor_setcolor(dinfo, bg, fg);
+       }
+
+       if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
+               u32 s_pitch = (ROUND_UP_TO(cursor->image.width, 8) / 8);
+               u32 size = s_pitch * cursor->image.height;
+               u8 *dat = (u8 *) cursor->image.data;
+               u8 *msk = (u8 *) cursor->mask;
+               u8 src[64];
+               u32 i;
+
+               if (cursor->image.depth != 1)
+                       return -ENXIO;
+
+               switch (cursor->rop) {
+               case ROP_XOR:
+                       for (i = 0; i < size; i++)
+                               src[i] = dat[i] ^ msk[i];
+                       break;
+               case ROP_COPY:
+               default:
+                       for (i = 0; i < size; i++)
+                               src[i] = dat[i] & msk[i];
+                       break;
+               }
+
+               /* save the bitmap to restore it when XFree will
+                  make the cursor dirty */
+               memcpy(dinfo->cursor_src, src, size);
+
+               intelfbhw_cursor_load(dinfo, cursor->image.width,
+                                     cursor->image.height, src);
+       }
+
+       if (cursor->enable)
+               intelfbhw_cursor_show(dinfo);
+
+       return 0;
+}
+
+static int
+intelfb_sync(struct fb_info *info)
+{
+        struct intelfb_info *dinfo = GET_DINFO(info);
+
+#if VERBOSE > 0
+       DBG_MSG("intelfb_sync\n");
+#endif
+
+       if (dinfo->ring_lockup)
+               return 0;
+
+       intelfbhw_do_sync(dinfo);
+       return 0;
+}
+
diff --git a/drivers/video/intelfb/intelfbdrv.h b/drivers/video/intelfb/intelfbdrv.h
new file mode 100644 (file)
index 0000000..f05dffa
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef _INTELFBDRV_H
+#define _INTELFBDRV_H
+
+/*
+ ******************************************************************************
+ * intelfb
+ *
+ * Linux framebuffer driver for Intel(R) 830M/845G/852GM/855GM/865G
+ * integrated graphics chips.
+ *
+ * Copyright Â© 2004 Sylvain Meyer
+ *
+ * Author: Sylvain Meyer
+ *
+ ******************************************************************************
+ *    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.
+*/
+
+int __init intelfb_setup(char *options);
+static void __devinit get_initial_mode(struct intelfb_info *dinfo);
+static void update_dinfo(struct intelfb_info *dinfo,
+                        struct fb_var_screeninfo *var);
+static int intelfb_get_fix(struct fb_fix_screeninfo *fix,
+                          struct fb_info *info);
+
+static int intelfb_check_var(struct fb_var_screeninfo *var,
+                            struct fb_info *info);
+static int intelfb_set_par(struct fb_info *info);
+static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+                            unsigned blue, unsigned transp,
+                            struct fb_info *info);
+
+static int intelfb_blank(int blank, struct fb_info *info);
+static int intelfb_pan_display(struct fb_var_screeninfo *var,
+                              struct fb_info *info);
+
+static void intelfb_fillrect(struct fb_info *info,
+                            const struct fb_fillrect *rect);
+static void intelfb_copyarea(struct fb_info *info,
+                            const struct fb_copyarea *region);
+static void intelfb_imageblit(struct fb_info *info,
+                             const struct fb_image *image);
+static int intelfb_cursor(struct fb_info *info,
+                          struct fb_cursor *cursor);
+
+static int intelfb_sync(struct fb_info *info);
+
+static int intelfb_ioctl(struct inode *inode, struct file *file,
+                        unsigned int cmd, unsigned long arg,
+                        struct fb_info *info);
+
+static int __devinit intelfb_pci_register(struct pci_dev *pdev,
+                                         const struct pci_device_id *ent);
+static void __devexit intelfb_pci_unregister(struct pci_dev *pdev);
+static int __devinit intelfb_set_fbinfo(struct intelfb_info *dinfo);
+
+#endif
diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c
new file mode 100644 (file)
index 0000000..5ce1c82
--- /dev/null
@@ -0,0 +1,1753 @@
+/*
+ * intelfb
+ *
+ * Linux framebuffer driver for Intel(R) 865G integrated graphics chips.
+ *
+ * Copyright Â© 2002, 2003 David Dawes <dawes@xfree86.org>
+ *                   2004 Sylvain Meyer
+ *
+ * This driver consists of two parts.  The first part (intelfbdrv.c) provides
+ * the basic fbdev interfaces, is derived in part from the radeonfb and
+ * vesafb drivers, and is covered by the GPL.  The second part (intelfbhw.c)
+ * provides the code to program the hardware.  Most of it is derived from
+ * the i810/i830 XFree86 driver.  The HW-specific code is covered here
+ * under a dual license (GPL and MIT/XFree86 license).
+ *
+ * Author: David Dawes
+ *
+ */
+
+/* $DHD: intelfb/intelfbhw.c,v 1.9 2003/06/27 15:06:25 dawes Exp $ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/selection.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/kd.h>
+#include <linux/vt_kern.h>
+#include <linux/pagemap.h>
+#include <linux/version.h>
+
+#include <asm/io.h>
+
+#include "intelfb.h"
+#include "intelfbhw.h"
+
+int
+intelfbhw_get_chipset(struct pci_dev *pdev, const char **name, int *chipset,
+                     int *mobile)
+{
+       u32 tmp;
+
+       if (!pdev || !name || !chipset || !mobile)
+               return 1;
+
+       switch (pdev->device) {
+       case PCI_DEVICE_ID_INTEL_830M:
+               *name = "Intel(R) 830M";
+               *chipset = INTEL_830M;
+               *mobile = 1;
+               return 0;
+       case PCI_DEVICE_ID_INTEL_845G:
+               *name = "Intel(R) 845G";
+               *chipset = INTEL_845G;
+               *mobile = 0;
+               return 0;
+       case PCI_DEVICE_ID_INTEL_85XGM:
+               tmp = 0;
+               *mobile = 1;
+               pci_read_config_dword(pdev, INTEL_85X_CAPID, &tmp);
+               switch ((tmp >> INTEL_85X_VARIANT_SHIFT) &
+                       INTEL_85X_VARIANT_MASK) {
+               case INTEL_VAR_855GME:
+                       *name = "Intel(R) 855GME";
+                       *chipset = INTEL_855GME;
+                       return 0;
+               case INTEL_VAR_855GM:
+                       *name = "Intel(R) 855GM";
+                       *chipset = INTEL_855GM;
+                       return 0;
+               case INTEL_VAR_852GME:
+                       *name = "Intel(R) 852GME";
+                       *chipset = INTEL_852GME;
+                       return 0;
+               case INTEL_VAR_852GM:
+                       *name = "Intel(R) 852GM";
+                       *chipset = INTEL_852GM;
+                       return 0;
+               default:
+                       *name = "Intel(R) 852GM/855GM";
+                       *chipset = INTEL_85XGM;
+                       return 0;
+               }
+               break;
+       case PCI_DEVICE_ID_INTEL_865G:
+               *name = "Intel(R) 865G";
+               *chipset = INTEL_865G;
+               *mobile = 0;
+               return 0;
+       default:
+               return 1;
+       }
+}
+
+int
+intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size,
+                    int *stolen_size)
+{
+       struct pci_dev *bridge_dev;
+       u16 tmp;
+
+       if (!pdev || !aperture_size || !stolen_size)
+               return 1;
+
+       /* Find the bridge device.  It is always 0:0.0 */
+       if (!(bridge_dev = pci_find_slot(0, PCI_DEVFN(0, 0)))) {
+               ERR_MSG("cannot find bridge device\n");
+               return 1;
+       }
+
+       /* Get the fb aperture size and "stolen" memory amount. */
+       tmp = 0;
+       pci_read_config_word(bridge_dev, INTEL_GMCH_CTRL, &tmp);
+       switch (pdev->device) {
+       case PCI_DEVICE_ID_INTEL_830M:
+       case PCI_DEVICE_ID_INTEL_845G:
+               if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M)
+                       *aperture_size = MB(64);
+               else
+                       *aperture_size = MB(128);
+               switch (tmp & INTEL_830_GMCH_GMS_MASK) {
+               case INTEL_830_GMCH_GMS_STOLEN_512:
+                       *stolen_size = KB(512) - KB(132);
+                       return 0;
+               case INTEL_830_GMCH_GMS_STOLEN_1024:
+                       *stolen_size = MB(1) - KB(132);
+                       return 0;
+               case INTEL_830_GMCH_GMS_STOLEN_8192:
+                       *stolen_size = MB(8) - KB(132);
+                       return 0;
+               case INTEL_830_GMCH_GMS_LOCAL:
+                       ERR_MSG("only local memory found\n");
+                       return 1;
+               case INTEL_830_GMCH_GMS_DISABLED:
+                       ERR_MSG("video memory is disabled\n");
+                       return 1;
+               default:
+                       ERR_MSG("unexpected GMCH_GMS value: 0x%02x\n",
+                               tmp & INTEL_830_GMCH_GMS_MASK);
+                       return 1;
+               }
+               break;
+       default:
+               *aperture_size = MB(128);
+               switch (tmp & INTEL_855_GMCH_GMS_MASK) {
+               case INTEL_855_GMCH_GMS_STOLEN_1M:
+                       *stolen_size = MB(1) - KB(132);
+                       return 0;
+               case INTEL_855_GMCH_GMS_STOLEN_4M:
+                       *stolen_size = MB(4) - KB(132);
+                       return 0;
+               case INTEL_855_GMCH_GMS_STOLEN_8M:
+                       *stolen_size = MB(8) - KB(132);
+                       return 0;
+               case INTEL_855_GMCH_GMS_STOLEN_16M:
+                       *stolen_size = MB(16) - KB(132);
+                       return 0;
+               case INTEL_855_GMCH_GMS_STOLEN_32M:
+                       *stolen_size = MB(32) - KB(132);
+                       return 0;
+               case INTEL_855_GMCH_GMS_DISABLED:
+                       ERR_MSG("video memory is disabled\n");
+                       return 0;
+               default:
+                       ERR_MSG("unexpected GMCH_GMS value: 0x%02x\n",
+                               tmp & INTEL_855_GMCH_GMS_MASK);
+                       return 1;
+               }
+       }
+}
+
+int
+intelfbhw_check_non_crt(struct intelfb_info *dinfo)
+{
+       int dvo = 0;
+
+       if (INREG(LVDS) & PORT_ENABLE)
+               dvo |= LVDS_PORT;
+       if (INREG(DVOA) & PORT_ENABLE)
+               dvo |= DVOA_PORT;
+       if (INREG(DVOB) & PORT_ENABLE)
+               dvo |= DVOB_PORT;
+       if (INREG(DVOC) & PORT_ENABLE)
+               dvo |= DVOC_PORT;
+
+       return dvo;
+}
+
+const char *
+intelfbhw_dvo_to_string(int dvo)
+{
+       if (dvo & DVOA_PORT)
+               return "DVO port A";
+       else if (dvo & DVOB_PORT)
+               return "DVO port B";
+       else if (dvo & DVOC_PORT)
+               return "DVO port C";
+       else if (dvo & LVDS_PORT)
+               return "LVDS port";
+       else
+               return NULL;
+}
+
+
+int
+intelfbhw_validate_mode(struct intelfb_info *dinfo,
+                       struct fb_var_screeninfo *var)
+{
+       int bytes_per_pixel;
+       int tmp;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_validate_mode\n");
+#endif
+
+       bytes_per_pixel = var->bits_per_pixel / 8;
+       if (bytes_per_pixel == 3)
+               bytes_per_pixel = 4;
+
+       /* Check if enough video memory. */
+       tmp = var->yres_virtual * var->xres_virtual * bytes_per_pixel;
+       if (tmp > dinfo->fb.size) {
+               WRN_MSG("Not enough video ram for mode "
+                       "(%d KByte vs %d KByte).\n",
+                       BtoKB(tmp), BtoKB(dinfo->fb.size));
+               return 1;
+       }
+
+       /* Check if x/y limits are OK. */
+       if (var->xres - 1 > HACTIVE_MASK) {
+               WRN_MSG("X resolution too large (%d vs %d).\n",
+                       var->xres, HACTIVE_MASK + 1);
+               return 1;
+       }
+       if (var->yres - 1 > VACTIVE_MASK) {
+               WRN_MSG("Y resolution too large (%d vs %d).\n",
+                       var->yres, VACTIVE_MASK + 1);
+               return 1;
+       }
+
+       /* Check for interlaced/doublescan modes. */
+       if (var->vmode & FB_VMODE_INTERLACED) {
+               WRN_MSG("Mode is interlaced.\n");
+               return 1;
+       }
+       if (var->vmode & FB_VMODE_DOUBLE) {
+               WRN_MSG("Mode is double-scan.\n");
+               return 1;
+       }
+
+       /* Check if clock is OK. */
+       tmp = 1000000000 / var->pixclock;
+       if (tmp < MIN_CLOCK) {
+               WRN_MSG("Pixel clock is too low (%d MHz vs %d MHz).\n",
+                       (tmp + 500) / 1000, MIN_CLOCK / 1000);
+               return 1;
+       }
+       if (tmp > MAX_CLOCK) {
+               WRN_MSG("Pixel clock is too high (%d MHz vs %d MHz).\n",
+                       (tmp + 500) / 1000, MAX_CLOCK / 1000);
+               return 1;
+       }
+
+       return 0;
+}
+
+int
+intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       struct intelfb_info *dinfo = GET_DINFO(info);
+       u32 offset, xoffset, yoffset;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_pan_display\n");
+#endif
+
+       xoffset = ROUND_DOWN_TO(var->xoffset, 8);
+       yoffset = var->yoffset;
+
+       if ((xoffset + var->xres > var->xres_virtual) ||
+           (yoffset + var->yres > var->yres_virtual))
+               return -EINVAL;
+
+       offset = (yoffset * dinfo->pitch) +
+                (xoffset * var->bits_per_pixel) / 8;
+
+       offset += dinfo->fb.offset << 12;
+
+       OUTREG(DSPABASE, offset);
+
+       return 0;
+}
+
+/* Blank the screen. */
+void
+intelfbhw_do_blank(int blank, struct fb_info *info)
+{
+       struct intelfb_info *dinfo = GET_DINFO(info);
+       u32 tmp;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_do_blank: blank is %d\n", blank);
+#endif
+
+       /* Turn plane A on or off */
+       tmp = INREG(DSPACNTR);
+       if (blank)
+               tmp &= ~DISPPLANE_PLANE_ENABLE;
+       else
+               tmp |= DISPPLANE_PLANE_ENABLE;
+       OUTREG(DSPACNTR, tmp);
+       /* Flush */
+       tmp = INREG(DSPABASE);
+       OUTREG(DSPABASE, tmp);
+
+       /* Turn off/on the HW cursor */
+#if VERBOSE > 0
+       DBG_MSG("cursor_on is %d\n", dinfo->cursor_on);
+#endif
+       if (dinfo->cursor_on) {
+               if (blank) {
+                       intelfbhw_cursor_hide(dinfo);
+               } else {
+                       intelfbhw_cursor_show(dinfo);
+               }
+               dinfo->cursor_on = 1;
+       }
+       dinfo->cursor_blanked = blank;
+
+       /* Set DPMS level */
+       tmp = INREG(ADPA) & ~ADPA_DPMS_CONTROL_MASK;
+       switch (blank) {
+       case FB_BLANK_UNBLANK:
+       case FB_BLANK_NORMAL:
+               tmp |= ADPA_DPMS_D0;
+               break;
+       case FB_BLANK_VSYNC_SUSPEND:
+               tmp |= ADPA_DPMS_D1;
+               break;
+       case FB_BLANK_HSYNC_SUSPEND:
+               tmp |= ADPA_DPMS_D2;
+               break;
+       case FB_BLANK_POWERDOWN:
+               tmp |= ADPA_DPMS_D3;
+               break;
+       }
+       OUTREG(ADPA, tmp);
+
+       return;
+}
+
+
+void
+intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno,
+                   unsigned red, unsigned green, unsigned blue,
+                   unsigned transp)
+{
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_setcolreg: %d: (%d, %d, %d)\n",
+               regno, red, green, blue);
+#endif
+
+       u32 palette_reg = (dinfo->pipe == PIPE_A) ?
+                         PALETTE_A : PALETTE_B;
+
+       OUTREG(palette_reg + (regno << 2),
+              (red << PALETTE_8_RED_SHIFT) |
+              (green << PALETTE_8_GREEN_SHIFT) |
+              (blue << PALETTE_8_BLUE_SHIFT));
+}
+
+
+int
+intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw,
+                       int flag)
+{
+       int i;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_read_hw_state\n");
+#endif
+
+       if (!hw || !dinfo)
+               return -1;
+
+       /* Read in as much of the HW state as possible. */
+       hw->vga0_divisor = INREG(VGA0_DIVISOR);
+       hw->vga1_divisor = INREG(VGA1_DIVISOR);
+       hw->vga_pd = INREG(VGAPD);
+       hw->dpll_a = INREG(DPLL_A);
+       hw->dpll_b = INREG(DPLL_B);
+       hw->fpa0 = INREG(FPA0);
+       hw->fpa1 = INREG(FPA1);
+       hw->fpb0 = INREG(FPB0);
+       hw->fpb1 = INREG(FPB1);
+
+       if (flag == 1)
+               return flag;
+
+#if 0
+       /* This seems to be a problem with the 852GM/855GM */
+       for (i = 0; i < PALETTE_8_ENTRIES; i++) {
+               hw->palette_a[i] = INREG(PALETTE_A + (i << 2));
+               hw->palette_b[i] = INREG(PALETTE_B + (i << 2));
+       }
+#endif
+
+       if (flag == 2)
+               return flag;
+
+       hw->htotal_a = INREG(HTOTAL_A);
+       hw->hblank_a = INREG(HBLANK_A);
+       hw->hsync_a = INREG(HSYNC_A);
+       hw->vtotal_a = INREG(VTOTAL_A);
+       hw->vblank_a = INREG(VBLANK_A);
+       hw->vsync_a = INREG(VSYNC_A);
+       hw->src_size_a = INREG(SRC_SIZE_A);
+       hw->bclrpat_a = INREG(BCLRPAT_A);
+       hw->htotal_b = INREG(HTOTAL_B);
+       hw->hblank_b = INREG(HBLANK_B);
+       hw->hsync_b = INREG(HSYNC_B);
+       hw->vtotal_b = INREG(VTOTAL_B);
+       hw->vblank_b = INREG(VBLANK_B);
+       hw->vsync_b = INREG(VSYNC_B);
+       hw->src_size_b = INREG(SRC_SIZE_B);
+       hw->bclrpat_b = INREG(BCLRPAT_B);
+
+       if (flag == 3)
+               return flag;
+
+       hw->adpa = INREG(ADPA);
+       hw->dvoa = INREG(DVOA);
+       hw->dvob = INREG(DVOB);
+       hw->dvoc = INREG(DVOC);
+       hw->dvoa_srcdim = INREG(DVOA_SRCDIM);
+       hw->dvob_srcdim = INREG(DVOB_SRCDIM);
+       hw->dvoc_srcdim = INREG(DVOC_SRCDIM);
+       hw->lvds = INREG(LVDS);
+
+       if (flag == 4)
+               return flag;
+
+       hw->pipe_a_conf = INREG(PIPEACONF);
+       hw->pipe_b_conf = INREG(PIPEBCONF);
+       hw->disp_arb = INREG(DISPARB);
+
+       if (flag == 5)
+               return flag;
+
+       hw->cursor_a_control = INREG(CURSOR_A_CONTROL);
+       hw->cursor_b_control = INREG(CURSOR_B_CONTROL);
+       hw->cursor_a_base = INREG(CURSOR_A_BASEADDR);
+       hw->cursor_b_base = INREG(CURSOR_B_BASEADDR);
+
+       if (flag == 6)
+               return flag;
+
+       for (i = 0; i < 4; i++) {
+               hw->cursor_a_palette[i] = INREG(CURSOR_A_PALETTE0 + (i << 2));
+               hw->cursor_b_palette[i] = INREG(CURSOR_B_PALETTE0 + (i << 2));
+       }
+
+       if (flag == 7)
+               return flag;
+
+       hw->cursor_size = INREG(CURSOR_SIZE);
+
+       if (flag == 8)
+               return flag;
+
+       hw->disp_a_ctrl = INREG(DSPACNTR);
+       hw->disp_b_ctrl = INREG(DSPBCNTR);
+       hw->disp_a_base = INREG(DSPABASE);
+       hw->disp_b_base = INREG(DSPBBASE);
+       hw->disp_a_stride = INREG(DSPASTRIDE);
+       hw->disp_b_stride = INREG(DSPBSTRIDE);
+
+       if (flag == 9)
+               return flag;
+
+       hw->vgacntrl = INREG(VGACNTRL);
+
+       if (flag == 10)
+               return flag;
+
+       hw->add_id = INREG(ADD_ID);
+
+       if (flag == 11)
+               return flag;
+
+       for (i = 0; i < 7; i++) {
+               hw->swf0x[i] = INREG(SWF00 + (i << 2));
+               hw->swf1x[i] = INREG(SWF10 + (i << 2));
+               if (i < 3)
+                       hw->swf3x[i] = INREG(SWF30 + (i << 2));
+       }
+
+       for (i = 0; i < 8; i++)
+               hw->fence[i] = INREG(FENCE + (i << 2));
+
+       hw->instpm = INREG(INSTPM);
+       hw->mem_mode = INREG(MEM_MODE);
+       hw->fw_blc_0 = INREG(FW_BLC_0);
+       hw->fw_blc_1 = INREG(FW_BLC_1);
+
+       return 0;
+}
+
+
+void
+intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw)
+{
+#if REGDUMP
+       int i, m1, m2, n, p1, p2;
+
+       DBG_MSG("intelfbhw_print_hw_state\n");
+
+       if (!hw || !dinfo)
+               return;
+       /* Read in as much of the HW state as possible. */
+       printk("hw state dump start\n");
+       printk("        VGA0_DIVISOR:           0x%08x\n", hw->vga0_divisor);
+       printk("        VGA1_DIVISOR:           0x%08x\n", hw->vga1_divisor);
+       printk("        VGAPD:                  0x%08x\n", hw->vga_pd);
+       n = (hw->vga0_divisor >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       m1 = (hw->vga0_divisor >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       m2 = (hw->vga0_divisor >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       if (hw->vga_pd & VGAPD_0_P1_FORCE_DIV2)
+               p1 = 0;
+       else
+               p1 = (hw->vga_pd >> VGAPD_0_P1_SHIFT) & DPLL_P1_MASK;
+       p2 = (hw->vga_pd >> VGAPD_0_P2_SHIFT) & DPLL_P2_MASK;
+       printk("        VGA0: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n",
+               m1, m2, n, p1, p2);
+       printk("        VGA0: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2));
+
+       n = (hw->vga1_divisor >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       m1 = (hw->vga1_divisor >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       m2 = (hw->vga1_divisor >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       if (hw->vga_pd & VGAPD_1_P1_FORCE_DIV2)
+               p1 = 0;
+       else
+               p1 = (hw->vga_pd >> VGAPD_1_P1_SHIFT) & DPLL_P1_MASK;
+       p2 = (hw->vga_pd >> VGAPD_1_P2_SHIFT) & DPLL_P2_MASK;
+       printk("        VGA1: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n",
+               m1, m2, n, p1, p2);
+       printk("        VGA1: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2));
+
+       printk("        DPLL_A:                 0x%08x\n", hw->dpll_a);
+       printk("        DPLL_B:                 0x%08x\n", hw->dpll_b);
+       printk("        FPA0:                   0x%08x\n", hw->fpa0);
+       printk("        FPA1:                   0x%08x\n", hw->fpa1);
+       printk("        FPB0:                   0x%08x\n", hw->fpb0);
+       printk("        FPB1:                   0x%08x\n", hw->fpb1);
+
+       n = (hw->fpa0 >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       m1 = (hw->fpa0 >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       m2 = (hw->fpa0 >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       if (hw->dpll_a & DPLL_P1_FORCE_DIV2)
+               p1 = 0;
+       else
+               p1 = (hw->dpll_a >> DPLL_P1_SHIFT) & DPLL_P1_MASK;
+       p2 = (hw->dpll_a >> DPLL_P2_SHIFT) & DPLL_P2_MASK;
+       printk("        PLLA0: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n",
+               m1, m2, n, p1, p2);
+       printk("        PLLA0: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2));
+
+       n = (hw->fpa1 >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       m1 = (hw->fpa1 >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       m2 = (hw->fpa1 >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK;
+       if (hw->dpll_a & DPLL_P1_FORCE_DIV2)
+               p1 = 0;
+       else
+               p1 = (hw->dpll_a >> DPLL_P1_SHIFT) & DPLL_P1_MASK;
+       p2 = (hw->dpll_a >> DPLL_P2_SHIFT) & DPLL_P2_MASK;
+       printk("        PLLA1: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n",
+               m1, m2, n, p1, p2);
+       printk("        PLLA1: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2));
+
+#if 0
+       printk("        PALETTE_A:\n");
+       for (i = 0; i < PALETTE_8_ENTRIES)
+               printk("        %3d:    0x%08x\n", i, hw->palette_a[i];
+       printk("        PALETTE_B:\n");
+       for (i = 0; i < PALETTE_8_ENTRIES)
+               printk("        %3d:    0x%08x\n", i, hw->palette_b[i];
+#endif
+
+       printk("        HTOTAL_A:               0x%08x\n", hw->htotal_a);
+       printk("        HBLANK_A:               0x%08x\n", hw->hblank_a);
+       printk("        HSYNC_A:                0x%08x\n", hw->hsync_a);
+       printk("        VTOTAL_A:               0x%08x\n", hw->vtotal_a);
+       printk("        VBLANK_A:               0x%08x\n", hw->vblank_a);
+       printk("        VSYNC_A:                0x%08x\n", hw->vsync_a);
+       printk("        SRC_SIZE_A:             0x%08x\n", hw->src_size_a);
+       printk("        BCLRPAT_A:              0x%08x\n", hw->bclrpat_a);
+       printk("        HTOTAL_B:               0x%08x\n", hw->htotal_b);
+       printk("        HBLANK_B:               0x%08x\n", hw->hblank_b);
+       printk("        HSYNC_B:                0x%08x\n", hw->hsync_b);
+       printk("        VTOTAL_B:               0x%08x\n", hw->vtotal_b);
+       printk("        VBLANK_B:               0x%08x\n", hw->vblank_b);
+       printk("        VSYNC_B:                0x%08x\n", hw->vsync_b);
+       printk("        SRC_SIZE_B:             0x%08x\n", hw->src_size_b);
+       printk("        BCLRPAT_B:              0x%08x\n", hw->bclrpat_b);
+
+       printk("        ADPA:                   0x%08x\n", hw->adpa);
+       printk("        DVOA:                   0x%08x\n", hw->dvoa);
+       printk("        DVOB:                   0x%08x\n", hw->dvob);
+       printk("        DVOC:                   0x%08x\n", hw->dvoc);
+       printk("        DVOA_SRCDIM:            0x%08x\n", hw->dvoa_srcdim);
+       printk("        DVOB_SRCDIM:            0x%08x\n", hw->dvob_srcdim);
+       printk("        DVOC_SRCDIM:            0x%08x\n", hw->dvoc_srcdim);
+       printk("        LVDS:                   0x%08x\n", hw->lvds);
+
+       printk("        PIPEACONF:              0x%08x\n", hw->pipe_a_conf);
+       printk("        PIPEBCONF:              0x%08x\n", hw->pipe_b_conf);
+       printk("        DISPARB:                0x%08x\n", hw->disp_arb);
+
+       printk("        CURSOR_A_CONTROL:       0x%08x\n", hw->cursor_a_control);
+       printk("        CURSOR_B_CONTROL:       0x%08x\n", hw->cursor_b_control);
+       printk("        CURSOR_A_BASEADDR:      0x%08x\n", hw->cursor_a_base);
+       printk("        CURSOR_B_BASEADDR:      0x%08x\n", hw->cursor_b_base);
+
+       printk("        CURSOR_A_PALETTE:       ");
+       for (i = 0; i < 4; i++) {
+               printk("0x%08x", hw->cursor_a_palette[i]);
+               if (i < 3)
+                       printk(", ");
+       }
+       printk("\n");
+       printk("        CURSOR_B_PALETTE:       ");
+       for (i = 0; i < 4; i++) {
+               printk("0x%08x", hw->cursor_b_palette[i]);
+               if (i < 3)
+                       printk(", ");
+       }
+       printk("\n");
+
+       printk("        CURSOR_SIZE:            0x%08x\n", hw->cursor_size);
+
+       printk("        DSPACNTR:               0x%08x\n", hw->disp_a_ctrl);
+       printk("        DSPBCNTR:               0x%08x\n", hw->disp_b_ctrl);
+       printk("        DSPABASE:               0x%08x\n", hw->disp_a_base);
+       printk("        DSPBBASE:               0x%08x\n", hw->disp_b_base);
+       printk("        DSPASTRIDE:             0x%08x\n", hw->disp_a_stride);
+       printk("        DSPBSTRIDE:             0x%08x\n", hw->disp_b_stride);
+
+       printk("        VGACNTRL:               0x%08x\n", hw->vgacntrl);
+       printk("        ADD_ID:                 0x%08x\n", hw->add_id);
+
+       for (i = 0; i < 7; i++) {
+               printk("        SWF0%d                  0x%08x\n", i,
+                       hw->swf0x[i]);
+       }
+       for (i = 0; i < 7; i++) {
+               printk("        SWF1%d                  0x%08x\n", i,
+                       hw->swf1x[i]);
+       }
+       for (i = 0; i < 3; i++) {
+               printk("        SWF3%d                  0x%08x\n", i,
+                       hw->swf3x[i]);
+       }
+       for (i = 0; i < 8; i++)
+               printk("        FENCE%d                 0x%08x\n", i,
+                       hw->fence[i]);
+
+       printk("        INSTPM                  0x%08x\n", hw->instpm);
+       printk("        MEM_MODE                0x%08x\n", hw->mem_mode);
+       printk("        FW_BLC_0                0x%08x\n", hw->fw_blc_0);
+       printk("        FW_BLC_1                0x%08x\n", hw->fw_blc_1);
+
+       printk("hw state dump end\n");
+#endif
+}
+
+/* Split the M parameter into M1 and M2. */
+static int
+splitm(unsigned int m, unsigned int *retm1, unsigned int *retm2)
+{
+       int m1, m2;
+
+       m1 = (m - 2 - (MIN_M2 + MAX_M2) / 2) / 5 - 2;
+       if (m1 < MIN_M1)
+               m1 = MIN_M1;
+       if (m1 > MAX_M1)
+               m1 = MAX_M1;
+       m2 = m - 5 * (m1 + 2) - 2;
+       if (m2 < MIN_M2 || m2 > MAX_M2 || m2 >= m1) {
+               return 1;
+       } else {
+               *retm1 = (unsigned int)m1;
+               *retm2 = (unsigned int)m2;
+               return 0;
+       }
+}
+
+/* Split the P parameter into P1 and P2. */
+static int
+splitp(unsigned int p, unsigned int *retp1, unsigned int *retp2)
+{
+       int p1, p2;
+
+       if (p % 4 == 0)
+               p2 = 1;
+       else
+               p2 = 0;
+       p1 = (p / (1 << (p2 + 1))) - 2;
+       if (p % 4 == 0 && p1 < MIN_P1) {
+               p2 = 0;
+               p1 = (p / (1 << (p2 + 1))) - 2;
+       }
+       if (p1  < MIN_P1 || p1 > MAX_P1 || (p1 + 2) * (1 << (p2 + 1)) != p) {
+               return 1;
+       } else {
+               *retp1 = (unsigned int)p1;
+               *retp2 = (unsigned int)p2;
+               return 0;
+       }
+}
+
+static int
+calc_pll_params(int clock, u32 *retm1, u32 *retm2, u32 *retn, u32 *retp1,
+               u32 *retp2, u32 *retclock)
+{
+       u32 m1, m2, n, p1, p2, n1;
+       u32 f_vco, p, p_best = 0, m, f_out;
+       u32 err_max, err_target, err_best = 10000000;
+       u32 n_best = 0, m_best = 0, f_best, f_err;
+       u32 p_min, p_max, p_inc, div_min, div_max;
+
+       /* Accept 0.5% difference, but aim for 0.1% */
+       err_max = 5 * clock / 1000;
+       err_target = clock / 1000;
+
+       DBG_MSG("Clock is %d\n", clock);
+
+       div_max = MAX_VCO_FREQ / clock;
+       div_min = ROUND_UP_TO(MIN_VCO_FREQ, clock) / clock;
+
+       if (clock <= P_TRANSITION_CLOCK)
+               p_inc = 4;
+       else
+               p_inc = 2;
+       p_min = ROUND_UP_TO(div_min, p_inc);
+       p_max = ROUND_DOWN_TO(div_max, p_inc);
+       if (p_min < MIN_P)
+               p_min = 4;
+       if (p_max > MAX_P)
+               p_max = 128;
+
+       DBG_MSG("p range is %d-%d (%d)\n", p_min, p_max, p_inc);
+
+       p = p_min;
+       do {
+               if (splitp(p, &p1, &p2)) {
+                       WRN_MSG("cannot split p = %d\n", p);
+                       p += p_inc;
+                       continue;
+               }
+               n = MIN_N;
+               f_vco = clock * p;
+
+               do {
+                       m = ROUND_UP_TO(f_vco * n, PLL_REFCLK) / PLL_REFCLK;
+                       if (m < MIN_M)
+                               m = MIN_M;
+                       if (m > MAX_M)
+                               m = MAX_M;
+                       f_out = CALC_VCLOCK3(m, n, p);
+                       if (splitm(m, &m1, &m2)) {
+                               WRN_MSG("cannot split m = %d\n", m);
+                               n++;
+                               continue;
+                       }
+                       if (clock > f_out)
+                               f_err = clock - f_out;
+                       else
+                               f_err = f_out - clock;
+
+                       if (f_err < err_best) {
+                               m_best = m;
+                               n_best = n;
+                               p_best = p;
+                               f_best = f_out;
+                               err_best = f_err;
+                       }
+                       n++;
+               } while ((n <= MAX_N) && (f_out >= clock));
+               p += p_inc;
+       } while ((p <= p_max));
+
+       if (!m_best) {
+               WRN_MSG("cannot find parameters for clock %d\n", clock);
+               return 1;
+       }
+       m = m_best;
+       n = n_best;
+       p = p_best;
+       splitm(m, &m1, &m2);
+       splitp(p, &p1, &p2);
+       n1 = n - 2;
+
+       DBG_MSG("m, n, p: %d (%d,%d), %d (%d), %d (%d,%d), "
+               "f: %d (%d), VCO: %d\n",
+               m, m1, m2, n, n1, p, p1, p2,
+               CALC_VCLOCK3(m, n, p), CALC_VCLOCK(m1, m2, n1, p1, p2),
+               CALC_VCLOCK3(m, n, p) * p);
+       *retm1 = m1;
+       *retm2 = m2;
+       *retn = n1;
+       *retp1 = p1;
+       *retp2 = p2;
+       *retclock = CALC_VCLOCK(m1, m2, n1, p1, p2);
+
+       return 0;
+}
+
+static __inline__ int
+check_overflow(u32 value, u32 limit, const char *description)
+{
+       if (value > limit) {
+               WRN_MSG("%s value %d exceeds limit %d\n",
+                       description, value, limit);
+               return 1;
+       }
+       return 0;
+}
+
+/* It is assumed that hw is filled in with the initial state information. */
+int
+intelfbhw_mode_to_hw(struct intelfb_info *dinfo, struct intelfb_hwstate *hw,
+                    struct fb_var_screeninfo *var)
+{
+       int pipe = PIPE_A;
+       u32 *dpll, *fp0, *fp1;
+       u32 m1, m2, n, p1, p2, clock_target, clock;
+       u32 hsync_start, hsync_end, hblank_start, hblank_end, htotal, hactive;
+       u32 vsync_start, vsync_end, vblank_start, vblank_end, vtotal, vactive;
+       u32 vsync_pol, hsync_pol;
+       u32 *vs, *vb, *vt, *hs, *hb, *ht, *ss, *pipe_conf;
+
+       DBG_MSG("intelfbhw_mode_to_hw\n");
+
+       /* Disable VGA */
+       hw->vgacntrl |= VGA_DISABLE;
+
+       /* Check whether pipe A or pipe B is enabled. */
+       if (hw->pipe_a_conf & PIPECONF_ENABLE)
+               pipe = PIPE_A;
+       else if (hw->pipe_b_conf & PIPECONF_ENABLE)
+               pipe = PIPE_B;
+
+       /* Set which pipe's registers will be set. */
+       if (pipe == PIPE_B) {
+               dpll = &hw->dpll_b;
+               fp0 = &hw->fpb0;
+               fp1 = &hw->fpb1;
+               hs = &hw->hsync_b;
+               hb = &hw->hblank_b;
+               ht = &hw->htotal_b;
+               vs = &hw->vsync_b;
+               vb = &hw->vblank_b;
+               vt = &hw->vtotal_b;
+               ss = &hw->src_size_b;
+               pipe_conf = &hw->pipe_b_conf;
+       } else {
+               dpll = &hw->dpll_a;
+               fp0 = &hw->fpa0;
+               fp1 = &hw->fpa1;
+               hs = &hw->hsync_a;
+               hb = &hw->hblank_a;
+               ht = &hw->htotal_a;
+               vs = &hw->vsync_a;
+               vb = &hw->vblank_a;
+               vt = &hw->vtotal_a;
+               ss = &hw->src_size_a;
+               pipe_conf = &hw->pipe_a_conf;
+       }
+
+       /* Use ADPA register for sync control. */
+       hw->adpa &= ~ADPA_USE_VGA_HVPOLARITY;
+
+       /* sync polarity */
+       hsync_pol = (var->sync & FB_SYNC_HOR_HIGH_ACT) ?
+                       ADPA_SYNC_ACTIVE_HIGH : ADPA_SYNC_ACTIVE_LOW;
+       vsync_pol = (var->sync & FB_SYNC_VERT_HIGH_ACT) ?
+                       ADPA_SYNC_ACTIVE_HIGH : ADPA_SYNC_ACTIVE_LOW;
+       hw->adpa &= ~((ADPA_SYNC_ACTIVE_MASK << ADPA_VSYNC_ACTIVE_SHIFT) |
+                     (ADPA_SYNC_ACTIVE_MASK << ADPA_HSYNC_ACTIVE_SHIFT));
+       hw->adpa |= (hsync_pol << ADPA_HSYNC_ACTIVE_SHIFT) |
+                   (vsync_pol << ADPA_VSYNC_ACTIVE_SHIFT);
+
+       /* Connect correct pipe to the analog port DAC */
+       hw->adpa &= ~(PIPE_MASK << ADPA_PIPE_SELECT_SHIFT);
+       hw->adpa |= (pipe << ADPA_PIPE_SELECT_SHIFT);
+
+       /* Set DPMS state to D0 (on) */
+       hw->adpa &= ~ADPA_DPMS_CONTROL_MASK;
+       hw->adpa |= ADPA_DPMS_D0;
+
+       hw->adpa |= ADPA_DAC_ENABLE;
+
+       *dpll |= (DPLL_VCO_ENABLE | DPLL_VGA_MODE_DISABLE);
+       *dpll &= ~(DPLL_RATE_SELECT_MASK | DPLL_REFERENCE_SELECT_MASK);
+       *dpll |= (DPLL_REFERENCE_DEFAULT | DPLL_RATE_SELECT_FP0);
+
+       /* Desired clock in kHz */
+       clock_target = 1000000000 / var->pixclock;
+
+       if (calc_pll_params(clock_target, &m1, &m2, &n, &p1, &p2, &clock)) {
+               WRN_MSG("calc_pll_params failed\n");
+               return 1;
+       }
+
+       /* Check for overflow. */
+       if (check_overflow(p1, DPLL_P1_MASK, "PLL P1 parameter"))
+               return 1;
+       if (check_overflow(p2, DPLL_P2_MASK, "PLL P2 parameter"))
+               return 1;
+       if (check_overflow(m1, FP_DIVISOR_MASK, "PLL M1 parameter"))
+               return 1;
+       if (check_overflow(m2, FP_DIVISOR_MASK, "PLL M2 parameter"))
+               return 1;
+       if (check_overflow(n, FP_DIVISOR_MASK, "PLL N parameter"))
+               return 1;
+
+       *dpll &= ~DPLL_P1_FORCE_DIV2;
+       *dpll &= ~((DPLL_P2_MASK << DPLL_P2_SHIFT) |
+                  (DPLL_P1_MASK << DPLL_P1_SHIFT));
+       *dpll |= (p2 << DPLL_P2_SHIFT) | (p1 << DPLL_P1_SHIFT);
+       *fp0 = (n << FP_N_DIVISOR_SHIFT) |
+              (m1 << FP_M1_DIVISOR_SHIFT) |
+              (m2 << FP_M2_DIVISOR_SHIFT);
+       *fp1 = *fp0;
+
+       hw->dvob &= ~PORT_ENABLE;
+       hw->dvoc &= ~PORT_ENABLE;
+
+       /* Use display plane A. */
+       hw->disp_a_ctrl |= DISPPLANE_PLANE_ENABLE;
+       hw->disp_a_ctrl &= ~DISPPLANE_GAMMA_ENABLE;
+       hw->disp_a_ctrl &= ~DISPPLANE_PIXFORMAT_MASK;
+       switch (intelfb_var_to_depth(var)) {
+       case 8:
+               hw->disp_a_ctrl |= DISPPLANE_8BPP | DISPPLANE_GAMMA_ENABLE;
+               break;
+       case 15:
+               hw->disp_a_ctrl |= DISPPLANE_15_16BPP;
+               break;
+       case 16:
+               hw->disp_a_ctrl |= DISPPLANE_16BPP;
+               break;
+       case 24:
+               hw->disp_a_ctrl |= DISPPLANE_32BPP_NO_ALPHA;
+               break;
+       }
+       hw->disp_a_ctrl &= ~(PIPE_MASK << DISPPLANE_SEL_PIPE_SHIFT);
+       hw->disp_a_ctrl |= (pipe << DISPPLANE_SEL_PIPE_SHIFT);
+
+       /* Set CRTC registers. */
+       hactive = var->xres;
+       hsync_start = hactive + var->right_margin;
+       hsync_end = hsync_start + var->hsync_len;
+       htotal = hsync_end + var->left_margin;
+       hblank_start = hactive;
+       hblank_end = htotal;
+
+       DBG_MSG("H: act %d, ss %d, se %d, tot %d bs %d, be %d\n",
+               hactive, hsync_start, hsync_end, htotal, hblank_start,
+               hblank_end);
+
+       vactive = var->yres;
+       vsync_start = vactive + var->lower_margin;
+       vsync_end = vsync_start + var->vsync_len;
+       vtotal = vsync_end + var->upper_margin;
+       vblank_start = vactive;
+       vblank_end = vtotal;
+       vblank_end = vsync_end + 1;
+
+       DBG_MSG("V: act %d, ss %d, se %d, tot %d bs %d, be %d\n",
+               vactive, vsync_start, vsync_end, vtotal, vblank_start,
+               vblank_end);
+
+       /* Adjust for register values, and check for overflow. */
+       hactive--;
+       if (check_overflow(hactive, HACTIVE_MASK, "CRTC hactive"))
+               return 1;
+       hsync_start--;
+       if (check_overflow(hsync_start, HSYNCSTART_MASK, "CRTC hsync_start"))
+               return 1;
+       hsync_end--;
+       if (check_overflow(hsync_end, HSYNCEND_MASK, "CRTC hsync_end"))
+               return 1;
+       htotal--;
+       if (check_overflow(htotal, HTOTAL_MASK, "CRTC htotal"))
+               return 1;
+       hblank_start--;
+       if (check_overflow(hblank_start, HBLANKSTART_MASK, "CRTC hblank_start"))
+               return 1;
+       hblank_end--;
+       if (check_overflow(hblank_end, HBLANKEND_MASK, "CRTC hblank_end"))
+               return 1;
+
+       vactive--;
+       if (check_overflow(vactive, VACTIVE_MASK, "CRTC vactive"))
+               return 1;
+       vsync_start--;
+       if (check_overflow(vsync_start, VSYNCSTART_MASK, "CRTC vsync_start"))
+               return 1;
+       vsync_end--;
+       if (check_overflow(vsync_end, VSYNCEND_MASK, "CRTC vsync_end"))
+               return 1;
+       vtotal--;
+       if (check_overflow(vtotal, VTOTAL_MASK, "CRTC vtotal"))
+               return 1;
+       vblank_start--;
+       if (check_overflow(vblank_start, VBLANKSTART_MASK, "CRTC vblank_start"))
+               return 1;
+       vblank_end--;
+       if (check_overflow(vblank_end, VBLANKEND_MASK, "CRTC vblank_end"))
+               return 1;
+
+       *ht = (htotal << HTOTAL_SHIFT) | (hactive << HACTIVE_SHIFT);
+       *hb = (hblank_start << HBLANKSTART_SHIFT) |
+             (hblank_end << HSYNCEND_SHIFT);
+       *hs = (hsync_start << HSYNCSTART_SHIFT) | (hsync_end << HSYNCEND_SHIFT);
+
+       *vt = (vtotal << VTOTAL_SHIFT) | (vactive << VACTIVE_SHIFT);
+       *vb = (vblank_start << VBLANKSTART_SHIFT) |
+             (vblank_end << VSYNCEND_SHIFT);
+       *vs = (vsync_start << VSYNCSTART_SHIFT) | (vsync_end << VSYNCEND_SHIFT);
+       *ss = (hactive << SRC_SIZE_HORIZ_SHIFT) |
+             (vactive << SRC_SIZE_VERT_SHIFT);
+
+       hw->disp_a_stride = var->xres_virtual * var->bits_per_pixel / 8;
+       DBG_MSG("pitch is %d\n", hw->disp_a_stride);
+
+       hw->disp_a_base = hw->disp_a_stride * var->yoffset +
+                         var->xoffset * var->bits_per_pixel / 8;
+
+       hw->disp_a_base += dinfo->fb.offset << 12;
+
+       /* Check stride alignment. */
+       if (hw->disp_a_stride % STRIDE_ALIGNMENT != 0) {
+               WRN_MSG("display stride %d has bad alignment %d\n",
+                       hw->disp_a_stride, STRIDE_ALIGNMENT);
+               return 1;
+       }
+
+       /* Set the palette to 8-bit mode. */
+       *pipe_conf &= ~PIPECONF_GAMMA;
+       return 0;
+}
+
+/* Program a (non-VGA) video mode. */
+int
+intelfbhw_program_mode(struct intelfb_info *dinfo,
+                    const struct intelfb_hwstate *hw, int blank)
+{
+       int pipe = PIPE_A;
+       u32 tmp;
+       const u32 *dpll, *fp0, *fp1, *pipe_conf;
+       const u32 *hs, *ht, *hb, *vs, *vt, *vb, *ss;
+       u32 dpll_reg, fp0_reg, fp1_reg, pipe_conf_reg;
+       u32 hsync_reg, htotal_reg, hblank_reg;
+       u32 vsync_reg, vtotal_reg, vblank_reg;
+       u32 src_size_reg;
+
+       /* Assume single pipe, display plane A, analog CRT. */
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_program_mode\n");
+#endif
+
+       /* Disable VGA */
+       tmp = INREG(VGACNTRL);
+       tmp |= VGA_DISABLE;
+       OUTREG(VGACNTRL, tmp);
+
+       /* Check whether pipe A or pipe B is enabled. */
+       if (hw->pipe_a_conf & PIPECONF_ENABLE)
+               pipe = PIPE_A;
+       else if (hw->pipe_b_conf & PIPECONF_ENABLE)
+               pipe = PIPE_B;
+
+       dinfo->pipe = pipe;
+
+       if (pipe == PIPE_B) {
+               dpll = &hw->dpll_b;
+               fp0 = &hw->fpb0;
+               fp1 = &hw->fpb1;
+               pipe_conf = &hw->pipe_b_conf;
+               hs = &hw->hsync_b;
+               hb = &hw->hblank_b;
+               ht = &hw->htotal_b;
+               vs = &hw->vsync_b;
+               vb = &hw->vblank_b;
+               vt = &hw->vtotal_b;
+               ss = &hw->src_size_b;
+               dpll_reg = DPLL_B;
+               fp0_reg = FPB0;
+               fp1_reg = FPB1;
+               pipe_conf_reg = PIPEBCONF;
+               hsync_reg = HSYNC_B;
+               htotal_reg = HTOTAL_B;
+               hblank_reg = HBLANK_B;
+               vsync_reg = VSYNC_B;
+               vtotal_reg = VTOTAL_B;
+               vblank_reg = VBLANK_B;
+               src_size_reg = SRC_SIZE_B;
+       } else {
+               dpll = &hw->dpll_a;
+               fp0 = &hw->fpa0;
+               fp1 = &hw->fpa1;
+               pipe_conf = &hw->pipe_a_conf;
+               hs = &hw->hsync_a;
+               hb = &hw->hblank_a;
+               ht = &hw->htotal_a;
+               vs = &hw->vsync_a;
+               vb = &hw->vblank_a;
+               vt = &hw->vtotal_a;
+               ss = &hw->src_size_a;
+               dpll_reg = DPLL_A;
+               fp0_reg = FPA0;
+               fp1_reg = FPA1;
+               pipe_conf_reg = PIPEACONF;
+               hsync_reg = HSYNC_A;
+               htotal_reg = HTOTAL_A;
+               hblank_reg = HBLANK_A;
+               vsync_reg = VSYNC_A;
+               vtotal_reg = VTOTAL_A;
+               vblank_reg = VBLANK_A;
+               src_size_reg = SRC_SIZE_A;
+       }
+
+       /* Disable planes A and B. */
+       tmp = INREG(DSPACNTR);
+       tmp &= ~DISPPLANE_PLANE_ENABLE;
+       OUTREG(DSPACNTR, tmp);
+       tmp = INREG(DSPBCNTR);
+       tmp &= ~DISPPLANE_PLANE_ENABLE;
+       OUTREG(DSPBCNTR, tmp);
+
+       /* Wait for vblank.  For now, just wait for a 50Hz cycle (20ms)) */
+       mdelay(20);
+
+       /* Disable Sync */
+       tmp = INREG(ADPA);
+       tmp &= ~ADPA_DPMS_CONTROL_MASK;
+       tmp |= ADPA_DPMS_D3;
+       OUTREG(ADPA, tmp);
+
+       /* turn off pipe */
+       tmp = INREG(pipe_conf_reg);
+       tmp &= ~PIPECONF_ENABLE;
+       OUTREG(pipe_conf_reg, tmp);
+
+       /* turn off PLL */
+       tmp = INREG(dpll_reg);
+       dpll_reg &= ~DPLL_VCO_ENABLE;
+       OUTREG(dpll_reg, tmp);
+
+       /* Set PLL parameters */
+       OUTREG(dpll_reg, *dpll & ~DPLL_VCO_ENABLE);
+       OUTREG(fp0_reg, *fp0);
+       OUTREG(fp1_reg, *fp1);
+
+       /* Set pipe parameters */
+       OUTREG(hsync_reg, *hs);
+       OUTREG(hblank_reg, *hb);
+       OUTREG(htotal_reg, *ht);
+       OUTREG(vsync_reg, *vs);
+       OUTREG(vblank_reg, *vb);
+       OUTREG(vtotal_reg, *vt);
+       OUTREG(src_size_reg, *ss);
+
+       /* Set DVOs B/C */
+       OUTREG(DVOB, hw->dvob);
+       OUTREG(DVOC, hw->dvoc);
+
+       /* Set ADPA */
+       OUTREG(ADPA, (hw->adpa & ~(ADPA_DPMS_CONTROL_MASK)) | ADPA_DPMS_D3);
+
+       /* Enable PLL */
+       tmp = INREG(dpll_reg);
+       tmp |= DPLL_VCO_ENABLE;
+       OUTREG(dpll_reg, tmp);
+
+       /* Enable pipe */
+       OUTREG(pipe_conf_reg, *pipe_conf | PIPECONF_ENABLE);
+
+       /* Enable sync */
+       tmp = INREG(ADPA);
+       tmp &= ~ADPA_DPMS_CONTROL_MASK;
+       tmp |= ADPA_DPMS_D0;
+       OUTREG(ADPA, tmp);
+
+       /* setup display plane */
+       OUTREG(DSPACNTR, hw->disp_a_ctrl & ~DISPPLANE_PLANE_ENABLE);
+       OUTREG(DSPASTRIDE, hw->disp_a_stride);
+       OUTREG(DSPABASE, hw->disp_a_base);
+
+       /* Enable plane */
+       if (!blank) {
+               tmp = INREG(DSPACNTR);
+               tmp |= DISPPLANE_PLANE_ENABLE;
+               OUTREG(DSPACNTR, tmp);
+               OUTREG(DSPABASE, hw->disp_a_base);
+       }
+
+       return 0;
+}
+
+/* forward declarations */
+static void refresh_ring(struct intelfb_info *dinfo);
+static void reset_state(struct intelfb_info *dinfo);
+static void do_flush(struct intelfb_info *dinfo);
+
+static int
+wait_ring(struct intelfb_info *dinfo, int n)
+{
+       int i = 0;
+       unsigned long end;
+       u32 last_head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK;
+
+#if VERBOSE > 0
+       DBG_MSG("wait_ring: %d\n", n);
+#endif
+
+       end = jiffies + (HZ * 3);
+       while (dinfo->ring_space < n) {
+               dinfo->ring_head = (u8 __iomem *)(INREG(PRI_RING_HEAD) &
+                                                  RING_HEAD_MASK);
+               if (dinfo->ring_tail + RING_MIN_FREE <
+                   (u32 __iomem) dinfo->ring_head)
+                       dinfo->ring_space = (u32 __iomem) dinfo->ring_head
+                               - (dinfo->ring_tail + RING_MIN_FREE);
+               else
+                       dinfo->ring_space = (dinfo->ring.size +
+                                            (u32 __iomem) dinfo->ring_head)
+                               - (dinfo->ring_tail + RING_MIN_FREE);
+               if ((u32 __iomem) dinfo->ring_head != last_head) {
+                       end = jiffies + (HZ * 3);
+                       last_head = (u32 __iomem) dinfo->ring_head;
+               }
+               i++;
+               if (time_before(end, jiffies)) {
+                       if (!i) {
+                               /* Try again */
+                               reset_state(dinfo);
+                               refresh_ring(dinfo);
+                               do_flush(dinfo);
+                               end = jiffies + (HZ * 3);
+                               i = 1;
+                       } else {
+                               WRN_MSG("ring buffer : space: %d wanted %d\n",
+                                       dinfo->ring_space, n);
+                               WRN_MSG("lockup - turning off hardware "
+                                       "acceleration\n");
+                               dinfo->ring_lockup = 1;
+                               break;
+                       }
+               }
+               udelay(1);
+       }
+       return i;
+}
+
+static void
+do_flush(struct intelfb_info *dinfo) {
+       START_RING(2);
+       OUT_RING(MI_FLUSH | MI_WRITE_DIRTY_STATE | MI_INVALIDATE_MAP_CACHE);
+       OUT_RING(MI_NOOP);
+       ADVANCE_RING();
+}
+
+void
+intelfbhw_do_sync(struct intelfb_info *dinfo)
+{
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_do_sync\n");
+#endif
+
+       if (!dinfo->accel)
+               return;
+
+       /*
+        * Send a flush, then wait until the ring is empty.  This is what
+        * the XFree86 driver does, and actually it doesn't seem a lot worse
+        * than the recommended method (both have problems).
+        */
+       do_flush(dinfo);
+       wait_ring(dinfo, dinfo->ring.size - RING_MIN_FREE);
+       dinfo->ring_space = dinfo->ring.size - RING_MIN_FREE;
+}
+
+static void
+refresh_ring(struct intelfb_info *dinfo)
+{
+#if VERBOSE > 0
+       DBG_MSG("refresh_ring\n");
+#endif
+
+       dinfo->ring_head = (u8 __iomem *) (INREG(PRI_RING_HEAD) &
+                                          RING_HEAD_MASK);
+       dinfo->ring_tail = INREG(PRI_RING_TAIL) & RING_TAIL_MASK;
+       if (dinfo->ring_tail + RING_MIN_FREE < (u32 __iomem)dinfo->ring_head)
+               dinfo->ring_space = (u32 __iomem) dinfo->ring_head
+                       - (dinfo->ring_tail + RING_MIN_FREE);
+       else
+               dinfo->ring_space = (dinfo->ring.size +
+                                    (u32 __iomem) dinfo->ring_head)
+                       - (dinfo->ring_tail + RING_MIN_FREE);
+}
+
+static void
+reset_state(struct intelfb_info *dinfo)
+{
+       int i;
+       u32 tmp;
+
+#if VERBOSE > 0
+       DBG_MSG("reset_state\n");
+#endif
+
+       for (i = 0; i < FENCE_NUM; i++)
+               OUTREG(FENCE + (i << 2), 0);
+
+       /* Flush the ring buffer if it's enabled. */
+       tmp = INREG(PRI_RING_LENGTH);
+       if (tmp & RING_ENABLE) {
+#if VERBOSE > 0
+               DBG_MSG("reset_state: ring was enabled\n");
+#endif
+               refresh_ring(dinfo);
+               intelfbhw_do_sync(dinfo);
+               DO_RING_IDLE();
+       }
+
+       OUTREG(PRI_RING_LENGTH, 0);
+       OUTREG(PRI_RING_HEAD, 0);
+       OUTREG(PRI_RING_TAIL, 0);
+       OUTREG(PRI_RING_START, 0);
+}
+
+/* Stop the 2D engine, and turn off the ring buffer. */
+void
+intelfbhw_2d_stop(struct intelfb_info *dinfo)
+{
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_2d_stop: accel: %d, ring_active: %d\n", dinfo->accel,
+               dinfo->ring_active);
+#endif
+
+       if (!dinfo->accel)
+               return;
+
+       dinfo->ring_active = 0;
+       reset_state(dinfo);
+}
+
+/*
+ * Enable the ring buffer, and initialise the 2D engine.
+ * It is assumed that the graphics engine has been stopped by previously
+ * calling intelfb_2d_stop().
+ */
+void
+intelfbhw_2d_start(struct intelfb_info *dinfo)
+{
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_2d_start: accel: %d, ring_active: %d\n",
+               dinfo->accel, dinfo->ring_active);
+#endif
+
+       if (!dinfo->accel)
+               return;
+
+       /* Initialise the primary ring buffer. */
+       OUTREG(PRI_RING_LENGTH, 0);
+       OUTREG(PRI_RING_TAIL, 0);
+       OUTREG(PRI_RING_HEAD, 0);
+
+       OUTREG(PRI_RING_START, dinfo->ring.physical & RING_START_MASK);
+       OUTREG(PRI_RING_LENGTH,
+               ((dinfo->ring.size - GTT_PAGE_SIZE) & RING_LENGTH_MASK) |
+               RING_NO_REPORT | RING_ENABLE);
+       refresh_ring(dinfo);
+       dinfo->ring_active = 1;
+}
+
+/* 2D fillrect (solid fill or invert) */
+void
+intelfbhw_do_fillrect(struct intelfb_info *dinfo, u32 x, u32 y, u32 w, u32 h,
+                     u32 color, u32 pitch, u32 bpp, u32 rop)
+{
+       u32 br00, br09, br13, br14, br16;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_do_fillrect: (%d,%d) %dx%d, c 0x%06x, p %d bpp %d, "
+               "rop 0x%02x\n", x, y, w, h, color, pitch, bpp, rop);
+#endif
+
+       br00 = COLOR_BLT_CMD;
+       br09 = dinfo->fb_start + (y * pitch + x * (bpp / 8));
+       br13 = (rop << ROP_SHIFT) | pitch;
+       br14 = (h << HEIGHT_SHIFT) | ((w * (bpp / 8)) << WIDTH_SHIFT);
+       br16 = color;
+
+       switch (bpp) {
+       case 8:
+               br13 |= COLOR_DEPTH_8;
+               break;
+       case 16:
+               br13 |= COLOR_DEPTH_16;
+               break;
+       case 32:
+               br13 |= COLOR_DEPTH_32;
+               br00 |= WRITE_ALPHA | WRITE_RGB;
+               break;
+       }
+
+       START_RING(6);
+       OUT_RING(br00);
+       OUT_RING(br13);
+       OUT_RING(br14);
+       OUT_RING(br09);
+       OUT_RING(br16);
+       OUT_RING(MI_NOOP);
+       ADVANCE_RING();
+
+#if VERBOSE > 0
+       DBG_MSG("ring = 0x%08x, 0x%08x (%d)\n", dinfo->ring_head,
+               dinfo->ring_tail, dinfo->ring_space);
+#endif
+}
+
+void
+intelfbhw_do_bitblt(struct intelfb_info *dinfo, u32 curx, u32 cury,
+                   u32 dstx, u32 dsty, u32 w, u32 h, u32 pitch, u32 bpp)
+{
+       u32 br00, br09, br11, br12, br13, br22, br23, br26;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_do_bitblt: (%d,%d)->(%d,%d) %dx%d, p %d bpp %d\n",
+               curx, cury, dstx, dsty, w, h, pitch, bpp);
+#endif
+
+       br00 = XY_SRC_COPY_BLT_CMD;
+       br09 = dinfo->fb_start;
+       br11 = (pitch << PITCH_SHIFT);
+       br12 = dinfo->fb_start;
+       br13 = (SRC_ROP_GXCOPY << ROP_SHIFT) | (pitch << PITCH_SHIFT);
+       br22 = (dstx << WIDTH_SHIFT) | (dsty << HEIGHT_SHIFT);
+       br23 = ((dstx + w) << WIDTH_SHIFT) |
+              ((dsty + h) << HEIGHT_SHIFT);
+       br26 = (curx << WIDTH_SHIFT) | (cury << HEIGHT_SHIFT);
+
+       switch (bpp) {
+       case 8:
+               br13 |= COLOR_DEPTH_8;
+               break;
+       case 16:
+               br13 |= COLOR_DEPTH_16;
+               break;
+       case 32:
+               br13 |= COLOR_DEPTH_32;
+               br00 |= WRITE_ALPHA | WRITE_RGB;
+               break;
+       }
+
+       START_RING(8);
+       OUT_RING(br00);
+       OUT_RING(br13);
+       OUT_RING(br22);
+       OUT_RING(br23);
+       OUT_RING(br09);
+       OUT_RING(br26);
+       OUT_RING(br11);
+       OUT_RING(br12);
+       ADVANCE_RING();
+}
+
+int
+intelfbhw_do_drawglyph(struct intelfb_info *dinfo, u32 fg, u32 bg, u32 w,
+                      u32 h, const u8* cdat, u32 x, u32 y, u32 pitch, u32 bpp)
+{
+       int nbytes, ndwords, pad, tmp;
+       u32 br00, br09, br13, br18, br19, br22, br23;
+       int dat, ix, iy, iw;
+       int i, j;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_do_drawglyph: (%d,%d) %dx%d\n", x, y, w, h);
+#endif
+
+       /* size in bytes of a padded scanline */
+       nbytes = ROUND_UP_TO(w, 16) / 8;
+
+       /* Total bytes of padded scanline data to write out. */
+       nbytes = nbytes * h;
+
+       /*
+        * Check if the glyph data exceeds the immediate mode limit.
+        * It would take a large font (1K pixels) to hit this limit.
+        */
+       if (nbytes > MAX_MONO_IMM_SIZE)
+               return 0;
+
+       /* Src data is packaged a dword (32-bit) at a time. */
+       ndwords = ROUND_UP_TO(nbytes, 4) / 4;
+
+       /*
+        * Ring has to be padded to a quad word. But because the command starts
+          with 7 bytes, pad only if there is an even number of ndwords
+        */
+       pad = !(ndwords % 2);
+
+       tmp = (XY_MONO_SRC_IMM_BLT_CMD & DW_LENGTH_MASK) + ndwords;
+       br00 = (XY_MONO_SRC_IMM_BLT_CMD & ~DW_LENGTH_MASK) | tmp;
+       br09 = dinfo->fb_start;
+       br13 = (SRC_ROP_GXCOPY << ROP_SHIFT) | (pitch << PITCH_SHIFT);
+       br18 = bg;
+       br19 = fg;
+       br22 = (x << WIDTH_SHIFT) | (y << HEIGHT_SHIFT);
+       br23 = ((x + w) << WIDTH_SHIFT) | ((y + h) << HEIGHT_SHIFT);
+
+       switch (bpp) {
+       case 8:
+               br13 |= COLOR_DEPTH_8;
+               break;
+       case 16:
+               br13 |= COLOR_DEPTH_16;
+               break;
+       case 32:
+               br13 |= COLOR_DEPTH_32;
+               br00 |= WRITE_ALPHA | WRITE_RGB;
+               break;
+       }
+
+       START_RING(8 + ndwords);
+       OUT_RING(br00);
+       OUT_RING(br13);
+       OUT_RING(br22);
+       OUT_RING(br23);
+       OUT_RING(br09);
+       OUT_RING(br18);
+       OUT_RING(br19);
+       ix = iy = 0;
+       iw = ROUND_UP_TO(w, 8) / 8;
+       while (ndwords--) {
+               dat = 0;
+               for (j = 0; j < 2; ++j) {
+                       for (i = 0; i < 2; ++i) {
+                               if (ix != iw || i == 0)
+                                       dat |= cdat[iy*iw + ix++] << (i+j*2)*8;
+                       }
+                       if (ix == iw && iy != (h-1)) {
+                               ix = 0;
+                               ++iy;
+                       }
+               }
+               OUT_RING(dat);
+       }
+       if (pad)
+               OUT_RING(MI_NOOP);
+       ADVANCE_RING();
+
+       return 1;
+}
+
+/* HW cursor functions. */
+void
+intelfbhw_cursor_init(struct intelfb_info *dinfo)
+{
+       u32 tmp;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_cursor_init\n");
+#endif
+
+       if (dinfo->mobile) {
+               if (!dinfo->cursor.physical)
+                       return;
+               tmp = INREG(CURSOR_A_CONTROL);
+               tmp &= ~(CURSOR_MODE_MASK | CURSOR_MOBILE_GAMMA_ENABLE |
+                        CURSOR_MEM_TYPE_LOCAL |
+                        (1 << CURSOR_PIPE_SELECT_SHIFT));
+               tmp |= CURSOR_MODE_DISABLE;
+               OUTREG(CURSOR_A_CONTROL, tmp);
+               OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical);
+       } else {
+               tmp = INREG(CURSOR_CONTROL);
+               tmp &= ~(CURSOR_FORMAT_MASK | CURSOR_GAMMA_ENABLE |
+                        CURSOR_ENABLE | CURSOR_STRIDE_MASK);
+               tmp = CURSOR_FORMAT_3C;
+               OUTREG(CURSOR_CONTROL, tmp);
+               OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.offset << 12);
+               tmp = (64 << CURSOR_SIZE_H_SHIFT) |
+                     (64 << CURSOR_SIZE_V_SHIFT);
+               OUTREG(CURSOR_SIZE, tmp);
+       }
+}
+
+void
+intelfbhw_cursor_hide(struct intelfb_info *dinfo)
+{
+       u32 tmp;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_cursor_hide\n");
+#endif
+
+       dinfo->cursor_on = 0;
+       if (dinfo->mobile) {
+               if (!dinfo->cursor.physical)
+                       return;
+               tmp = INREG(CURSOR_A_CONTROL);
+               tmp &= ~CURSOR_MODE_MASK;
+               tmp |= CURSOR_MODE_DISABLE;
+               OUTREG(CURSOR_A_CONTROL, tmp);
+               /* Flush changes */
+               OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical);
+       } else {
+               tmp = INREG(CURSOR_CONTROL);
+               tmp &= ~CURSOR_ENABLE;
+               OUTREG(CURSOR_CONTROL, tmp);
+       }
+}
+
+void
+intelfbhw_cursor_show(struct intelfb_info *dinfo)
+{
+       u32 tmp;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_cursor_show\n");
+#endif
+
+       dinfo->cursor_on = 1;
+
+       if (dinfo->cursor_blanked)
+               return;
+
+       if (dinfo->mobile) {
+               if (!dinfo->cursor.physical)
+                       return;
+               tmp = INREG(CURSOR_A_CONTROL);
+               tmp &= ~CURSOR_MODE_MASK;
+               tmp |= CURSOR_MODE_64_4C_AX;
+               OUTREG(CURSOR_A_CONTROL, tmp);
+               /* Flush changes */
+               OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical);
+       } else {
+               tmp = INREG(CURSOR_CONTROL);
+               tmp |= CURSOR_ENABLE;
+               OUTREG(CURSOR_CONTROL, tmp);
+       }
+}
+
+void
+intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y)
+{
+       u32 tmp;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_cursor_setpos: (%d, %d)\n", x, y);
+#endif
+
+       /*
+        * Sets the position.  The coordinates are assumed to already
+        * have any offset adjusted.  Assume that the cursor is never
+        * completely off-screen, and that x, y are always >= 0.
+        */
+
+       tmp = ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT) |
+             ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT);
+       OUTREG(CURSOR_A_POSITION, tmp);
+}
+
+void
+intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, u32 fg)
+{
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_cursor_setcolor\n");
+#endif
+
+       OUTREG(CURSOR_A_PALETTE0, bg & CURSOR_PALETTE_MASK);
+       OUTREG(CURSOR_A_PALETTE1, fg & CURSOR_PALETTE_MASK);
+       OUTREG(CURSOR_A_PALETTE2, fg & CURSOR_PALETTE_MASK);
+       OUTREG(CURSOR_A_PALETTE3, bg & CURSOR_PALETTE_MASK);
+}
+
+void
+intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, int height,
+                     u8 *data)
+{
+       u8 __iomem *addr = (u8 __iomem *)dinfo->cursor.virtual;
+       int i, j, w = width / 8;
+       int mod = width % 8, t_mask, d_mask;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_cursor_load\n");
+#endif
+
+       if (!dinfo->cursor.virtual)
+               return;
+
+       t_mask = 0xff >> mod;
+       d_mask = ~(0xff >> mod);
+       for (i = height; i--; ) {
+               for (j = 0; j < w; j++) {
+                       writeb(0x00, addr + j);
+                       writeb(*(data++), addr + j+8);
+               }
+               if (mod) {
+                       writeb(t_mask, addr + j);
+                       writeb(*(data++) & d_mask, addr + j+8);
+               }
+               addr += 16;
+       }
+}
+
+void
+intelfbhw_cursor_reset(struct intelfb_info *dinfo) {
+       u8 __iomem *addr = (u8 __iomem *)dinfo->cursor.virtual;
+       int i, j;
+
+#if VERBOSE > 0
+       DBG_MSG("intelfbhw_cursor_reset\n");
+#endif
+
+       if (!dinfo->cursor.virtual)
+               return;
+
+       for (i = 64; i--; ) {
+               for (j = 0; j < 8; j++) {
+                       writeb(0xff, addr + j+0);
+                       writeb(0x00, addr + j+8);
+               }
+               addr += 16;
+       }
+}
diff --git a/drivers/video/intelfb/intelfbhw.h b/drivers/video/intelfb/intelfbhw.h
new file mode 100644 (file)
index 0000000..d4336cc
--- /dev/null
@@ -0,0 +1,567 @@
+#ifndef _INTELFBHW_H
+#define _INTELFBHW_H
+
+/* $DHD: intelfb/intelfbhw.h,v 1.5 2003/06/27 15:06:25 dawes Exp $ */
+
+
+/*** HW-specific data ***/
+
+/* Information about the 852GM/855GM variants */
+#define INTEL_85X_CAPID                0x44
+#define INTEL_85X_VARIANT_MASK         0x7
+#define INTEL_85X_VARIANT_SHIFT                5
+#define INTEL_VAR_855GME               0x0
+#define INTEL_VAR_855GM                        0x4
+#define INTEL_VAR_852GME               0x2
+#define INTEL_VAR_852GM                        0x5
+
+/* Information about DVO/LVDS Ports */
+#define DVOA_PORT  0x1
+#define DVOB_PORT  0x2
+#define DVOC_PORT  0x4
+#define LVDS_PORT  0x8
+
+/*
+ * The Bridge device's PCI config space has information about the
+ * fb aperture size and the amount of pre-reserved memory.
+ */
+#define INTEL_GMCH_CTRL                0x52
+#define INTEL_GMCH_ENABLED             0x4
+#define INTEL_GMCH_MEM_MASK            0x1
+#define INTEL_GMCH_MEM_64M             0x1
+#define INTEL_GMCH_MEM_128M            0
+
+#define INTEL_830_GMCH_GMS_MASK                (0x7 << 4)
+#define INTEL_830_GMCH_GMS_DISABLED    (0x0 << 4)
+#define INTEL_830_GMCH_GMS_LOCAL       (0x1 << 4)
+#define INTEL_830_GMCH_GMS_STOLEN_512  (0x2 << 4)
+#define INTEL_830_GMCH_GMS_STOLEN_1024 (0x3 << 4)
+#define INTEL_830_GMCH_GMS_STOLEN_8192 (0x4 << 4)
+
+#define INTEL_855_GMCH_GMS_MASK                (0x7 << 4)
+#define INTEL_855_GMCH_GMS_DISABLED    (0x0 << 4)
+#define INTEL_855_GMCH_GMS_STOLEN_1M   (0x1 << 4)
+#define INTEL_855_GMCH_GMS_STOLEN_4M   (0x2 << 4)
+#define INTEL_855_GMCH_GMS_STOLEN_8M   (0x3 << 4)
+#define INTEL_855_GMCH_GMS_STOLEN_16M  (0x4 << 4)
+#define INTEL_855_GMCH_GMS_STOLEN_32M  (0x5 << 4)
+
+/* HW registers */
+
+/* Fence registers */
+#define FENCE                  0x2000
+#define FENCE_NUM                      8
+
+/* Primary ring buffer */
+#define PRI_RING_TAIL          0x2030
+#define RING_TAIL_MASK                 0x001ffff8
+#define RING_INUSE                     0x1
+
+#define PRI_RING_HEAD          0x2034
+#define RING_HEAD_WRAP_MASK            0x7ff
+#define RING_HEAD_WRAP_SHIFT           21
+#define RING_HEAD_MASK                 0x001ffffc
+
+#define PRI_RING_START         0x2038
+#define RING_START_MASK                        0xfffff000
+
+#define PRI_RING_LENGTH                0x203c
+#define RING_LENGTH_MASK               0x001ff000
+#define RING_REPORT_MASK               (0x3 << 1)
+#define RING_NO_REPORT                 (0x0 << 1)
+#define RING_REPORT_64K                        (0x1 << 1)
+#define RING_REPORT_4K                 (0x2 << 1)
+#define RING_REPORT_128K               (0x3 << 1)
+#define RING_ENABLE                    0x1
+
+/*
+ * Tail can't wrap to any closer than RING_MIN_FREE bytes of the head,
+ * and the last RING_MIN_FREE bytes need to be padded with MI_NOOP
+ */
+#define RING_MIN_FREE                  64
+
+#define IPEHR                  0x2088
+
+#define INSTDONE               0x2090
+#define PRI_RING_EMPTY                 1
+
+#define INSTPM                 0x20c0
+#define SYNC_FLUSH_ENABLE              (1 << 5)
+
+#define INSTPS                 0x20c4
+
+#define MEM_MODE               0x20cc
+
+#define MASK_SHIFT                     16
+
+#define FW_BLC_0               0x20d8
+#define FW_DISPA_WM_SHIFT              0
+#define FW_DISPA_WM_MASK               0x3f
+#define FW_DISPA_BL_SHIFT              8
+#define FW_DISPA_BL_MASK               0xf
+#define FW_DISPB_WM_SHIFT              16
+#define FW_DISPB_WM_MASK               0x1f
+#define FW_DISPB_BL_SHIFT              24
+#define FW_DISPB_BL_MASK               0x7
+
+#define FW_BLC_1               0x20dc
+#define FW_DISPC_WM_SHIFT              0
+#define FW_DISPC_WM_MASK               0x1f
+#define FW_DISPC_BL_SHIFT              8
+#define FW_DISPC_BL_MASK               0x7
+
+
+/* PLL registers */
+#define VGA0_DIVISOR           0x06000
+#define VGA1_DIVISOR           0x06004
+#define VGAPD                  0x06010
+#define VGAPD_0_P1_SHIFT               0
+#define VGAPD_0_P1_FORCE_DIV2          (1 << 5)
+#define VGAPD_0_P2_SHIFT               7
+#define VGAPD_1_P1_SHIFT               8
+#define VGAPD_1_P1_FORCE_DIV2          (1 << 13)
+#define VGAPD_1_P2_SHIFT               15
+
+#define DPLL_A                 0x06014
+#define DPLL_B                 0x06018
+#define DPLL_VCO_ENABLE                        (1 << 31)
+#define DPLL_2X_CLOCK_ENABLE           (1 << 30)
+#define DPLL_SYNCLOCK_ENABLE           (1 << 29)
+#define DPLL_VGA_MODE_DISABLE          (1 << 28)
+#define DPLL_P2_MASK                   1
+#define DPLL_P2_SHIFT                  23
+#define DPLL_P1_FORCE_DIV2             (1 << 21)
+#define DPLL_P1_MASK                   0x1f
+#define DPLL_P1_SHIFT                  16
+#define DPLL_REFERENCE_SELECT_MASK     (0x3 << 13)
+#define DPLL_REFERENCE_DEFAULT         (0x0 << 13)
+#define DPLL_REFERENCE_TVCLK           (0x2 << 13)
+#define DPLL_RATE_SELECT_MASK          (1 << 8)
+#define DPLL_RATE_SELECT_FP0           (0 << 8)
+#define DPLL_RATE_SELECT_FP1           (1 << 8)
+
+#define FPA0                   0x06040
+#define FPA1                   0x06044
+#define FPB0                   0x06048
+#define FPB1                   0x0604c
+#define FP_DIVISOR_MASK                        0x3f
+#define FP_N_DIVISOR_SHIFT             16
+#define FP_M1_DIVISOR_SHIFT            8
+#define FP_M2_DIVISOR_SHIFT            0
+
+/* PLL parameters (these are for 852GM/855GM/865G, check earlier chips). */
+/* Clock values are in units of kHz */
+#define PLL_REFCLK             48000
+#define MIN_VCO_FREQ           930000
+#define MAX_VCO_FREQ           1400000
+#define MIN_CLOCK              25000
+#define MAX_CLOCK              350000
+#define P_TRANSITION_CLOCK     165000
+#define MIN_M                  108
+#define MAX_M                  140
+#define MIN_M1                 18
+#define MAX_M1                 26
+#define MIN_M2                 6
+#define MAX_M2                 16
+#define MIN_P                  4
+#define MAX_P                  128
+#define MIN_P1                 0
+#define MAX_P1                 31
+#define MIN_N                  3
+#define MAX_N                  16
+
+#define CALC_VCLOCK(m1, m2, n, p1, p2) \
+        ((PLL_REFCLK * (5 * ((m1) + 2) + ((m2) + 2)) / ((n) + 2)) / \
+        (((p1) + 2) * (1 << (p2 + 1))))
+
+#define CALC_VCLOCK3(m, n, p)  ((PLL_REFCLK * (m) / (n)) / (p))
+
+/* Two pipes */
+#define PIPE_A                 0
+#define PIPE_B                 1
+#define PIPE_MASK              1
+
+/* palette registers */
+#define PALETTE_A              0x0a000
+#define PALETTE_B              0x0a800
+#ifndef PALETTE_8_ENTRIES
+#define PALETTE_8_ENTRIES              256
+#endif
+#define PALETTE_8_SIZE                 (PALETTE_8_ENTRIES * 4)
+#define PALETTE_10_ENTRIES             128
+#define PALETTE_10_SIZE                        (PALETTE_10_ENTRIES * 8)
+#define PALETTE_8_MASK                 0xff
+#define PALETTE_8_RED_SHIFT            16
+#define PALETTE_8_GREEN_SHIFT          8
+#define PALETTE_8_BLUE_SHIFT           0
+
+/* CRTC registers */
+#define HTOTAL_A               0x60000
+#define HBLANK_A               0x60004
+#define HSYNC_A                        0x60008
+#define VTOTAL_A               0x6000c
+#define VBLANK_A               0x60010
+#define VSYNC_A                        0x60014
+#define SRC_SIZE_A             0x6001c
+#define BCLRPAT_A              0x60020
+
+#define HTOTAL_B               0x61000
+#define HBLANK_B               0x61004
+#define HSYNC_B                        0x61008
+#define VTOTAL_B               0x6100c
+#define VBLANK_B               0x61010
+#define VSYNC_B                        0x61014
+#define SRC_SIZE_B             0x6101c
+#define BCLRPAT_B              0x61020
+
+#define HTOTAL_MASK                    0xfff
+#define HTOTAL_SHIFT                   16
+#define HACTIVE_MASK                   0x7ff
+#define HACTIVE_SHIFT                  0
+#define HBLANKEND_MASK                 0xfff
+#define HBLANKEND_SHIFT                        16
+#define HBLANKSTART_MASK               0xfff
+#define HBLANKSTART_SHIFT              0
+#define HSYNCEND_MASK                  0xfff
+#define HSYNCEND_SHIFT                 16
+#define HSYNCSTART_MASK                        0xfff
+#define HSYNCSTART_SHIFT               0
+#define VTOTAL_MASK                    0xfff
+#define VTOTAL_SHIFT                   16
+#define VACTIVE_MASK                   0x7ff
+#define VACTIVE_SHIFT                  0
+#define VBLANKEND_MASK                 0xfff
+#define VBLANKEND_SHIFT                        16
+#define VBLANKSTART_MASK               0xfff
+#define VBLANKSTART_SHIFT              0
+#define VSYNCEND_MASK                  0xfff
+#define VSYNCEND_SHIFT                 16
+#define VSYNCSTART_MASK                        0xfff
+#define VSYNCSTART_SHIFT               0
+#define SRC_SIZE_HORIZ_MASK            0x7ff
+#define SRC_SIZE_HORIZ_SHIFT           16
+#define SRC_SIZE_VERT_MASK             0x7ff
+#define SRC_SIZE_VERT_SHIFT            0
+
+#define ADPA                   0x61100
+#define ADPA_DAC_ENABLE                        (1 << 31)
+#define ADPA_DAC_DISABLE               0
+#define ADPA_PIPE_SELECT_SHIFT         30
+#define ADPA_USE_VGA_HVPOLARITY                (1 << 15)
+#define ADPA_SETS_HVPOLARITY           0
+#define ADPA_DPMS_CONTROL_MASK         (0x3 << 10)
+#define ADPA_DPMS_D0                   (0x0 << 10)
+#define ADPA_DPMS_D2                   (0x1 << 10)
+#define ADPA_DPMS_D1                   (0x2 << 10)
+#define ADPA_DPMS_D3                   (0x3 << 10)
+#define ADPA_VSYNC_ACTIVE_SHIFT                4
+#define ADPA_HSYNC_ACTIVE_SHIFT                3
+#define ADPA_SYNC_ACTIVE_MASK          1
+#define ADPA_SYNC_ACTIVE_HIGH          1
+#define ADPA_SYNC_ACTIVE_LOW           0
+
+#define DVOA                   0x61120
+#define DVOB                   0x61140
+#define DVOC                   0x61160
+#define LVDS                   0x61180
+#define PORT_ENABLE                    (1 << 31)
+#define PORT_PIPE_SELECT_SHIFT         30
+#define PORT_TV_FLAGS_MASK              0xFF
+#define PORT_TV_FLAGS                   0xC4  // ripped from my BIOS
+                                              // to understand and correct
+
+#define DVOA_SRCDIM            0x61124
+#define DVOB_SRCDIM            0x61144
+#define DVOC_SRCDIM            0x61164
+
+#define PIPEACONF              0x70008
+#define PIPEBCONF              0x71008
+#define PIPECONF_ENABLE                        (1 << 31)
+#define PIPECONF_DISABLE               0
+#define PIPECONF_DOUBLE_WIDE           (1 << 30)
+#define PIPECONF_SINGLE_WIDE           0
+#define PIPECONF_LOCKED                        (1 << 25)
+#define PIPECONF_UNLOCKED              0
+#define PIPECONF_GAMMA                 (1 << 24)
+#define PIPECONF_PALETTE               0
+
+#define DISPARB                        0x70030
+#define DISPARB_AEND_MASK              0x1ff
+#define DISPARB_AEND_SHIFT             0
+#define DISPARB_BEND_MASK              0x3ff
+#define DISPARB_BEND_SHIFT             9
+
+/* Desktop HW cursor */
+#define CURSOR_CONTROL         0x70080
+#define CURSOR_ENABLE                  (1 << 31)
+#define CURSOR_GAMMA_ENABLE            (1 << 30)
+#define CURSOR_STRIDE_MASK             (0x3 << 28)
+#define CURSOR_STRIDE_256              (0x0 << 28)
+#define CURSOR_STRIDE_512              (0x1 << 28)
+#define CURSOR_STRIDE_1K               (0x2 << 28)
+#define CURSOR_STRIDE_2K               (0x3 << 28)
+#define CURSOR_FORMAT_MASK             (0x7 << 24)
+#define CURSOR_FORMAT_2C               (0x0 << 24)
+#define CURSOR_FORMAT_3C               (0x1 << 24)
+#define CURSOR_FORMAT_4C               (0x2 << 24)
+#define CURSOR_FORMAT_ARGB             (0x4 << 24)
+#define CURSOR_FORMAT_XRGB             (0x5 << 24)
+
+/* Mobile HW cursor (and i810) */
+#define CURSOR_A_CONTROL       CURSOR_CONTROL
+#define CURSOR_B_CONTROL       0x700c0
+#define CURSOR_MODE_MASK               0x27
+#define CURSOR_MODE_DISABLE            0
+#define CURSOR_MODE_64_3C              0x04
+#define CURSOR_MODE_64_4C_AX           0x05
+#define CURSOR_MODE_64_4C              0x06
+#define CURSOR_MODE_64_32B_AX          0x07
+#define CURSOR_MODE_64_ARGB_AX         0x27
+#define CURSOR_PIPE_SELECT_SHIFT       28
+#define CURSOR_MOBILE_GAMMA_ENABLE     (1 << 26)
+#define CURSOR_MEM_TYPE_LOCAL          (1 << 25)
+
+/* All platforms (desktop has no pipe B) */
+#define CURSOR_A_BASEADDR      0x70084
+#define CURSOR_B_BASEADDR      0x700c4
+#define CURSOR_BASE_MASK               0xffffff00
+
+#define CURSOR_A_POSITION      0x70088
+#define CURSOR_B_POSITION      0x700c8
+#define CURSOR_POS_SIGN                        (1 << 15)
+#define CURSOR_POS_MASK                        0x7ff
+#define CURSOR_X_SHIFT                 0
+#define CURSOR_Y_SHIFT                 16
+
+#define CURSOR_A_PALETTE0      0x70090
+#define CURSOR_A_PALETTE1      0x70094
+#define CURSOR_A_PALETTE2      0x70098
+#define CURSOR_A_PALETTE3      0x7009c
+#define CURSOR_B_PALETTE0      0x700d0
+#define CURSOR_B_PALETTE1      0x700d4
+#define CURSOR_B_PALETTE2      0x700d8
+#define CURSOR_B_PALETTE3      0x700dc
+#define CURSOR_COLOR_MASK                      0xff
+#define CURSOR_RED_SHIFT                       16
+#define CURSOR_GREEN_SHIFT                     8
+#define CURSOR_BLUE_SHIFT                      0
+#define CURSOR_PALETTE_MASK                    0xffffff
+
+/* Desktop only */
+#define CURSOR_SIZE            0x700a0
+#define CURSOR_SIZE_MASK               0x3ff
+#define CURSOR_SIZE_H_SHIFT            0
+#define CURSOR_SIZE_V_SHIFT            12
+
+#define DSPACNTR               0x70180
+#define DSPBCNTR               0x71180
+#define DISPPLANE_PLANE_ENABLE         (1 << 31)
+#define DISPPLANE_PLANE_DISABLE                0
+#define DISPPLANE_GAMMA_ENABLE         (1<<30)
+#define DISPPLANE_GAMMA_DISABLE                0
+#define DISPPLANE_PIXFORMAT_MASK       (0xf<<26)
+#define DISPPLANE_8BPP                 (0x2<<26)
+#define DISPPLANE_15_16BPP             (0x4<<26)
+#define DISPPLANE_16BPP                        (0x5<<26)
+#define DISPPLANE_32BPP_NO_ALPHA       (0x6<<26)
+#define DISPPLANE_32BPP                        (0x7<<26)
+#define DISPPLANE_STEREO_ENABLE                (1<<25)
+#define DISPPLANE_STEREO_DISABLE       0
+#define DISPPLANE_SEL_PIPE_SHIFT       24
+#define DISPPLANE_SRC_KEY_ENABLE       (1<<22)
+#define DISPPLANE_SRC_KEY_DISABLE      0
+#define DISPPLANE_LINE_DOUBLE          (1<<20)
+#define DISPPLANE_NO_LINE_DOUBLE       0
+#define DISPPLANE_STEREO_POLARITY_FIRST        0
+#define DISPPLANE_STEREO_POLARITY_SECOND (1<<18)
+/* plane B only */
+#define DISPPLANE_ALPHA_TRANS_ENABLE   (1<<15)
+#define DISPPLANE_ALPHA_TRANS_DISABLE  0
+#define DISPPLANE_SPRITE_ABOVE_DISPLAYA        0
+#define DISPPLANE_SPRITE_ABOVE_OVERLAY 1
+
+#define DSPABASE               0x70184
+#define DSPASTRIDE             0x70188
+
+#define DSPBBASE               0x71184
+#define DSPBSTRIDE             0x71188
+
+#define VGACNTRL               0x71400
+#define VGA_DISABLE                    (1 << 31)
+#define VGA_ENABLE                     0
+#define VGA_PIPE_SELECT_SHIFT          29
+#define VGA_PALETTE_READ_SELECT                23
+#define VGA_PALETTE_A_WRITE_DISABLE    (1 << 22)
+#define VGA_PALETTE_B_WRITE_DISABLE    (1 << 21)
+#define VGA_LEGACY_PALETTE             (1 << 20)
+#define VGA_6BIT_DAC                   0
+#define VGA_8BIT_DAC                   (1 << 20)
+
+#define ADD_ID                 0x71408
+#define ADD_ID_MASK                    0xff
+
+/* BIOS scratch area registers (830M and 845G). */
+#define SWF0                   0x71410
+#define SWF1                   0x71414
+#define SWF2                   0x71418
+#define SWF3                   0x7141c
+#define SWF4                   0x71420
+#define SWF5                   0x71424
+#define SWF6                   0x71428
+
+/* BIOS scratch area registers (852GM, 855GM, 865G). */
+#define SWF00                  0x70410
+#define SWF01                  0x70414
+#define SWF02                  0x70418
+#define SWF03                  0x7041c
+#define SWF04                  0x70420
+#define SWF05                  0x70424
+#define SWF06                  0x70428
+
+#define SWF10                  SWF0
+#define SWF11                  SWF1
+#define SWF12                  SWF2
+#define SWF13                  SWF3
+#define SWF14                  SWF4
+#define SWF15                  SWF5
+#define SWF16                  SWF6
+
+#define SWF30                  0x72414
+#define SWF31                  0x72418
+#define SWF32                  0x7241c
+
+/* Memory Commands */
+#define MI_NOOP                        (0x00 << 23)
+#define MI_NOOP_WRITE_ID               (1 << 22)
+#define MI_NOOP_ID_MASK                        ((1 << 22) - 1)
+
+#define MI_FLUSH               (0x04 << 23)
+#define MI_WRITE_DIRTY_STATE           (1 << 4)
+#define MI_END_SCENE                   (1 << 3)
+#define MI_INHIBIT_RENDER_CACHE_FLUSH  (1 << 2)
+#define MI_INVALIDATE_MAP_CACHE                (1 << 0)
+
+#define MI_STORE_DWORD_IMM     ((0x20 << 23) | 1)
+
+/* 2D Commands */
+#define COLOR_BLT_CMD          ((2 << 29) | (0x40 << 22) | 3)
+#define XY_COLOR_BLT_CMD       ((2 << 29) | (0x50 << 22) | 4)
+#define XY_SETUP_CLIP_BLT_CMD  ((2 << 29) | (0x03 << 22) | 1)
+#define XY_SRC_COPY_BLT_CMD    ((2 << 29) | (0x53 << 22) | 6)
+#define SRC_COPY_BLT_CMD       ((2 << 29) | (0x43 << 22) | 4)
+#define XY_MONO_PAT_BLT_CMD    ((2 << 29) | (0x52 << 22) | 7)
+#define XY_MONO_SRC_BLT_CMD    ((2 << 29) | (0x54 << 22) | 6)
+#define XY_MONO_SRC_IMM_BLT_CMD        ((2 << 29) | (0x71 << 22) | 5)
+#define TXT_IMM_BLT_CMD                ((2 << 29) | (0x30 << 22) | 2)
+#define SETUP_BLT_CMD          ((2 << 29) | (0x00 << 22) | 6)
+
+#define DW_LENGTH_MASK                 0xff
+
+#define WRITE_ALPHA                    (1 << 21)
+#define WRITE_RGB                      (1 << 20)
+#define VERT_SEED                      (3 << 8)
+#define HORIZ_SEED                     (3 << 12)
+
+#define COLOR_DEPTH_8                  (0 << 24)
+#define COLOR_DEPTH_16                 (1 << 24)
+#define COLOR_DEPTH_32                 (3 << 24)
+
+#define SRC_ROP_GXCOPY                 0xcc
+#define SRC_ROP_GXXOR                  0x66
+
+#define PAT_ROP_GXCOPY                  0xf0
+#define PAT_ROP_GXXOR                   0x5a
+
+#define PITCH_SHIFT                    0
+#define ROP_SHIFT                      16
+#define WIDTH_SHIFT                    0
+#define HEIGHT_SHIFT                   16
+
+/* in bytes */
+#define MAX_MONO_IMM_SIZE              128
+
+
+/*** Macros ***/
+
+/* I/O macros */
+#define INREG8(addr)         readb((u8 __iomem *)(dinfo->mmio_base + (addr)))
+#define INREG(addr)          readl((u32 __iomem *)(dinfo->mmio_base + (addr)))
+#define OUTREG8(addr, val)    writeb((val),(u8 __iomem *)(dinfo->mmio_base + \
+                                                          (addr)))
+#define OUTREG(addr, val)     writel((val),(u32 __iomem *)(dinfo->mmio_base + \
+                                     (addr)))
+
+/* Ring buffer macros */
+#define OUT_RING(n)    do {                                            \
+       writel((n), (u32 __iomem *)(dinfo->ring.virtual + dinfo->ring_tail));\
+       dinfo->ring_tail += 4;                                          \
+       dinfo->ring_tail &= dinfo->ring_tail_mask;                      \
+} while (0)
+
+#define START_RING(n)  do {                                            \
+       if (dinfo->ring_space < (n) * 4)                                \
+               wait_ring(dinfo,(n) * 4);                               \
+       dinfo->ring_space -= (n) * 4;                                   \
+} while (0)
+
+#define ADVANCE_RING() do {                                            \
+       OUTREG(PRI_RING_TAIL, dinfo->ring_tail);                        \
+} while (0)
+
+#define DO_RING_IDLE() do {                                            \
+       u32 head, tail;                                                 \
+       do {                                                            \
+               head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK;           \
+               tail = INREG(PRI_RING_TAIL) & RING_TAIL_MASK;           \
+               udelay(10);                                             \
+       } while (head != tail);                                         \
+} while (0)
+
+
+/* function protoypes */
+extern int intelfbhw_get_chipset(struct pci_dev *pdev, const char **name,
+                                int *chipset, int *mobile);
+extern int intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size,
+                               int *stolen_size);
+extern int intelfbhw_check_non_crt(struct intelfb_info *dinfo);
+extern const char *intelfbhw_dvo_to_string(int dvo);
+extern int intelfbhw_validate_mode(struct intelfb_info *dinfo,
+                                  struct fb_var_screeninfo *var);
+extern int intelfbhw_pan_display(struct fb_var_screeninfo *var,
+                                struct fb_info *info);
+extern void intelfbhw_do_blank(int blank, struct fb_info *info);
+extern void intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno,
+                               unsigned red, unsigned green, unsigned blue,
+                               unsigned transp);
+extern int intelfbhw_read_hw_state(struct intelfb_info *dinfo,
+                                  struct intelfb_hwstate *hw, int flag);
+extern void intelfbhw_print_hw_state(struct intelfb_info *dinfo,
+                                    struct intelfb_hwstate *hw);
+extern int intelfbhw_mode_to_hw(struct intelfb_info *dinfo,
+                               struct intelfb_hwstate *hw,
+                               struct fb_var_screeninfo *var);
+extern int intelfbhw_program_mode(struct intelfb_info *dinfo,
+                                 const struct intelfb_hwstate *hw, int blank);
+extern void intelfbhw_do_sync(struct intelfb_info *dinfo);
+extern void intelfbhw_2d_stop(struct intelfb_info *dinfo);
+extern void intelfbhw_2d_start(struct intelfb_info *dinfo);
+extern void intelfbhw_do_fillrect(struct intelfb_info *dinfo, u32 x, u32 y,
+                                 u32 w, u32 h, u32 color, u32 pitch, u32 bpp,
+                                 u32 rop);
+extern void intelfbhw_do_bitblt(struct intelfb_info *dinfo, u32 curx, u32 cury,
+                               u32 dstx, u32 dsty, u32 w, u32 h, u32 pitch,
+                               u32 bpp);
+extern int intelfbhw_do_drawglyph(struct intelfb_info *dinfo, u32 fg, u32 bg,
+                                 u32 w, u32 h, const u8* cdat, u32 x, u32 y,
+                                 u32 pitch, u32 bpp);
+extern void intelfbhw_cursor_init(struct intelfb_info *dinfo);
+extern void intelfbhw_cursor_hide(struct intelfb_info *dinfo);
+extern void intelfbhw_cursor_show(struct intelfb_info *dinfo);
+extern void intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y);
+extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg,
+                                     u32 fg);
+extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width,
+                                 int height, u8 *data);
+extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo);
+
+#endif /* _INTELFBHW_H */
diff --git a/drivers/video/pmag-aa-fb.c b/drivers/video/pmag-aa-fb.c
new file mode 100644 (file)
index 0000000..3e00ad7
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ *     linux/drivers/video/pmag-aa-fb.c
+ *     Copyright 2002 Karsten Merker <merker@debian.org>
+ *
+ *     PMAG-AA TurboChannel framebuffer card support ... derived from
+ *     pmag-ba-fb.c, which is Copyright (C) 1999, 2000, 2001 by
+ *     Michael Engel <engel@unix-ag.org>, Karsten Merker <merker@debian.org>
+ *     and Harald Koerfgen <hkoerfg@web.de>, which itself is derived from
+ *     "HP300 Topcat framebuffer support (derived from macfb of all things)
+ *     Phil Blundell <philb@gnu.org> 1998"
+ *
+ *     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.
+ *
+ *     2002-09-28  Karsten Merker <merker@linuxtag.org>
+ *             Version 0.01: First try to get a PMAG-AA running.
+ *
+ *     2003-02-24  Thiemo Seufer  <seufer@csv.ica.uni-stuttgart.de>
+ *             Version 0.02: Major code cleanup.
+ *
+ *     2003-09-21  Thiemo Seufer  <seufer@csv.ica.uni-stuttgart.de>
+ *             Hardware cursor support.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+
+#include <asm/bootinfo.h>
+#include <asm/dec/machtype.h>
+#include <asm/dec/tc.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-cfb8.h>
+
+#include "bt455.h"
+#include "bt431.h"
+
+/* Version information */
+#define DRIVER_VERSION "0.02"
+#define DRIVER_AUTHOR "Karsten Merker <merker@linuxtag.org>"
+#define DRIVER_DESCRIPTION "PMAG-AA Framebuffer Driver"
+
+/* Prototypes */
+static int aafb_set_var(struct fb_var_screeninfo *var, int con,
+                       struct fb_info *info);
+
+/*
+ * Bt455 RAM DAC register base offset (rel. to TC slot base address).
+ */
+#define PMAG_AA_BT455_OFFSET           0x100000
+
+/*
+ * Bt431 cursor generator offset (rel. to TC slot base address).
+ */
+#define PMAG_AA_BT431_OFFSET           0x180000
+
+/*
+ * Begin of PMAG-AA framebuffer memory relative to TC slot address,
+ * resolution is 1280x1024x1 (8 bits deep, but only LSB is used).
+ */
+#define PMAG_AA_ONBOARD_FBMEM_OFFSET   0x200000
+
+struct aafb_cursor {
+       struct timer_list timer;
+       int enable;
+       int on;
+       int vbl_cnt;
+       int blink_rate;
+       u16 x, y, width, height;
+};
+
+#define CURSOR_TIMER_FREQ      (HZ / 50)
+#define CURSOR_BLINK_RATE      (20)
+#define CURSOR_DRAW_DELAY      (2)
+
+struct aafb_info {
+       struct fb_info info;
+       struct display disp;
+       struct aafb_cursor cursor;
+       struct bt455_regs *bt455;
+       struct bt431_regs *bt431;
+       unsigned long fb_start;
+       unsigned long fb_size;
+       unsigned long fb_line_length;
+};
+
+/*
+ * Max 3 TURBOchannel slots -> max 3 PMAG-AA.
+ */
+static struct aafb_info my_fb_info[3];
+
+static struct aafb_par {
+} current_par;
+
+static int currcon = -1;
+
+static void aafb_set_cursor(struct aafb_info *info, int on)
+{
+       struct aafb_cursor *c = &info->cursor;
+
+       if (on) {
+               bt431_position_cursor(info->bt431, c->x, c->y);
+               bt431_enable_cursor(info->bt431);
+       } else
+               bt431_erase_cursor(info->bt431);
+}
+
+static void aafbcon_cursor(struct display *disp, int mode, int x, int y)
+{
+       struct aafb_info *info = (struct aafb_info *)disp->fb_info;
+       struct aafb_cursor *c = &info->cursor;
+
+       x *= fontwidth(disp);
+       y *= fontheight(disp);
+
+       if (c->x == x && c->y == y && (mode == CM_ERASE) == !c->enable)
+               return;
+
+       c->enable = 0;
+       if (c->on)
+               aafb_set_cursor(info, 0);
+       c->x = x - disp->var.xoffset;
+       c->y = y - disp->var.yoffset;
+
+       switch (mode) {
+               case CM_ERASE:
+                       c->on = 0;
+                       break;
+               case CM_DRAW:
+               case CM_MOVE:
+                       if (c->on)
+                               aafb_set_cursor(info, c->on);
+                       else
+                               c->vbl_cnt = CURSOR_DRAW_DELAY;
+                       c->enable = 1;
+                       break;
+       }
+}
+
+static int aafbcon_set_font(struct display *disp, int width, int height)
+{
+       struct aafb_info *info = (struct aafb_info *)disp->fb_info;
+       struct aafb_cursor *c = &info->cursor;
+       u8 fgc = ~attr_bgcol_ec(disp, disp->conp);
+
+       if (width > 64 || height > 64 || width < 0 || height < 0)
+               return -EINVAL;
+
+       c->height = height;
+       c->width = width;
+
+       bt431_set_font(info->bt431, fgc, width, height);
+
+       return 1;
+}
+
+static void aafb_cursor_timer_handler(unsigned long data)
+{
+       struct aafb_info *info = (struct aafb_info *)data;
+       struct aafb_cursor *c = &info->cursor;
+
+       if (!c->enable)
+               goto out;
+
+       if (c->vbl_cnt && --c->vbl_cnt == 0) {
+               c->on ^= 1;
+               aafb_set_cursor(info, c->on);
+               c->vbl_cnt = c->blink_rate;
+       }
+
+out:
+       c->timer.expires = jiffies + CURSOR_TIMER_FREQ;
+       add_timer(&c->timer);
+}
+
+static void __init aafb_cursor_init(struct aafb_info *info)
+{
+       struct aafb_cursor *c = &info->cursor;
+
+       c->enable = 1;
+       c->on = 1;
+       c->x = c->y = 0;
+       c->width = c->height = 0;
+       c->vbl_cnt = CURSOR_DRAW_DELAY;
+       c->blink_rate = CURSOR_BLINK_RATE;
+
+       init_timer(&c->timer);
+       c->timer.data = (unsigned long)info;
+       c->timer.function = aafb_cursor_timer_handler;
+       mod_timer(&c->timer, jiffies + CURSOR_TIMER_FREQ);
+}
+
+static void __exit aafb_cursor_exit(struct aafb_info *info)
+{
+       struct aafb_cursor *c = &info->cursor;
+
+       del_timer_sync(&c->timer);
+}
+
+static struct display_switch aafb_switch8 = {
+       .setup = fbcon_cfb8_setup,
+       .bmove = fbcon_cfb8_bmove,
+       .clear = fbcon_cfb8_clear,
+       .putc = fbcon_cfb8_putc,
+       .putcs = fbcon_cfb8_putcs,
+       .revc = fbcon_cfb8_revc,
+       .cursor = aafbcon_cursor,
+       .set_font = aafbcon_set_font,
+       .clear_margins = fbcon_cfb8_clear_margins,
+       .fontwidthmask = FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
+};
+
+static void aafb_get_par(struct aafb_par *par)
+{
+       *par = current_par;
+}
+
+static int aafb_get_fix(struct fb_fix_screeninfo *fix, int con,
+                       struct fb_info *info)
+{
+       struct aafb_info *ip = (struct aafb_info *)info;
+
+       memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+       strcpy(fix->id, "PMAG-AA");
+       fix->smem_start = ip->fb_start;
+       fix->smem_len = ip->fb_size;
+       fix->type = FB_TYPE_PACKED_PIXELS;
+       fix->ypanstep = 1;
+       fix->ywrapstep = 1;
+       fix->visual = FB_VISUAL_MONO10;
+       fix->line_length = 1280;
+       fix->accel = FB_ACCEL_NONE;
+
+       return 0;
+}
+
+static void aafb_set_disp(struct display *disp, int con,
+                         struct aafb_info *info)
+{
+       struct fb_fix_screeninfo fix;
+
+       disp->fb_info = &info->info;
+       aafb_set_var(&disp->var, con, &info->info);
+       if (disp->conp && disp->conp->vc_sw && disp->conp->vc_sw->con_cursor)
+               disp->conp->vc_sw->con_cursor(disp->conp, CM_ERASE);
+       disp->dispsw = &aafb_switch8;
+       disp->dispsw_data = 0;
+
+       aafb_get_fix(&fix, con, &info->info);
+       disp->screen_base = (u8 *) fix.smem_start;
+       disp->visual = fix.visual;
+       disp->type = fix.type;
+       disp->type_aux = fix.type_aux;
+       disp->ypanstep = fix.ypanstep;
+       disp->ywrapstep = fix.ywrapstep;
+       disp->line_length = fix.line_length;
+       disp->next_line = 2048;
+       disp->can_soft_blank = 1;
+       disp->inverse = 0;
+       disp->scrollmode = SCROLL_YREDRAW;
+
+       aafbcon_set_font(disp, fontwidth(disp), fontheight(disp));
+}
+
+static int aafb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+                        struct fb_info *info)
+{
+       static u16 color[2] = {0x0000, 0x000f};
+       static struct fb_cmap aafb_cmap = {0, 2, color, color, color, NULL};
+
+       fb_copy_cmap(&aafb_cmap, cmap, kspc ? 0 : 2);
+       return 0;
+}
+
+static int aafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+                        struct fb_info *info)
+{
+       u16 color[2] = {0x0000, 0x000f};
+
+       if (cmap->start == 0
+           && cmap->len == 2
+           && memcmp(cmap->red, color, sizeof(color)) == 0
+           && memcmp(cmap->green, color, sizeof(color)) == 0
+           && memcmp(cmap->blue, color, sizeof(color)) == 0
+           && cmap->transp == NULL)
+               return 0;
+       else
+               return -EINVAL;
+}
+
+static int aafb_ioctl(struct inode *inode, struct file *file, u32 cmd,
+                     unsigned long arg, int con, struct fb_info *info)
+{
+       /* TODO: Not yet implemented */
+       return -ENOIOCTLCMD;
+}
+
+static int aafb_switch(int con, struct fb_info *info)
+{
+       struct aafb_info *ip = (struct aafb_info *)info;
+       struct display *old = (currcon < 0) ? &ip->disp : (fb_display + currcon);
+       struct display *new = (con < 0) ? &ip->disp : (fb_display + con);
+
+       if (old->conp && old->conp->vc_sw && old->conp->vc_sw->con_cursor)
+               old->conp->vc_sw->con_cursor(old->conp, CM_ERASE);
+
+       /* Set the current console. */
+       currcon = con;
+       aafb_set_disp(new, con, ip);
+
+       return 0;
+}
+
+static void aafb_encode_var(struct fb_var_screeninfo *var,
+                           struct aafb_par *par)
+{
+       var->xres = 1280;
+       var->yres = 1024;
+       var->xres_virtual = 2048;
+       var->yres_virtual = 1024;
+       var->xoffset = 0;
+       var->yoffset = 0;
+       var->bits_per_pixel = 8;
+       var->grayscale = 1;
+       var->red.offset = 0;
+       var->red.length = 0;
+       var->red.msb_right = 0;
+       var->green.offset = 0;
+       var->green.length = 1;
+       var->green.msb_right = 0;
+       var->blue.offset = 0;
+       var->blue.length = 0;
+       var->blue.msb_right = 0;
+       var->transp.offset = 0;
+       var->transp.length = 0;
+       var->transp.msb_right = 0;
+       var->nonstd = 0;
+       var->activate &= ~FB_ACTIVATE_MASK & FB_ACTIVATE_NOW;
+       var->accel_flags = 0;
+       var->sync = FB_SYNC_ON_GREEN;
+       var->vmode &= ~FB_VMODE_MASK & FB_VMODE_NONINTERLACED;
+}
+
+static int aafb_get_var(struct fb_var_screeninfo *var, int con,
+                       struct fb_info *info)
+{
+       if (con < 0) {
+               struct aafb_par par;
+
+               memset(var, 0, sizeof(struct fb_var_screeninfo));
+               aafb_get_par(&par);
+               aafb_encode_var(var, &par);
+       } else
+               *var = info->var;
+
+       return 0;
+}
+
+static int aafb_set_var(struct fb_var_screeninfo *var, int con,
+                       struct fb_info *info)
+{
+       struct aafb_par par;
+
+       aafb_get_par(&par);
+       aafb_encode_var(var, &par);
+       info->var = *var;
+
+       return 0;
+}
+
+static int aafb_update_var(int con, struct fb_info *info)
+{
+       struct aafb_info *ip = (struct aafb_info *)info;
+       struct display *disp = (con < 0) ? &ip->disp : (fb_display + con);
+
+       if (con == currcon)
+               aafbcon_cursor(disp, CM_ERASE, ip->cursor.x, ip->cursor.y);
+
+       return 0;
+}
+
+/* 0 unblanks, any other blanks. */
+
+static void aafb_blank(int blank, struct fb_info *info)
+{
+       struct aafb_info *ip = (struct aafb_info *)info;
+       u8 val = blank ? 0x00 : 0x0f;
+
+       bt455_write_cmap_entry(ip->bt455, 1, val, val, val);
+       aafbcon_cursor(&ip->disp, CM_ERASE, ip->cursor.x, ip->cursor.y);
+}
+
+static struct fb_ops aafb_ops = {
+       .owner = THIS_MODULE,
+       .fb_get_fix = aafb_get_fix,
+       .fb_get_var = aafb_get_var,
+       .fb_set_var = aafb_set_var,
+       .fb_get_cmap = aafb_get_cmap,
+       .fb_set_cmap = aafb_set_cmap,
+       .fb_ioctl = aafb_ioctl
+};
+
+static int __init init_one(int slot)
+{
+       unsigned long base_addr = get_tc_base_addr(slot);
+       struct aafb_info *ip = &my_fb_info[slot];
+
+       memset(ip, 0, sizeof(struct aafb_info));
+
+       /*
+        * Framebuffer display memory base address and friends.
+        */
+       ip->bt455 = (struct bt455_regs *) (base_addr + PMAG_AA_BT455_OFFSET);
+       ip->bt431 = (struct bt431_regs *) (base_addr + PMAG_AA_BT431_OFFSET);
+       ip->fb_start = base_addr + PMAG_AA_ONBOARD_FBMEM_OFFSET;
+       ip->fb_size = 2048 * 1024; /* fb_fix_screeninfo.smem_length
+                                     seems to be physical */
+       ip->fb_line_length = 2048;
+
+       /*
+        * Let there be consoles..
+        */
+       strcpy(ip->info.modename, "PMAG-AA");
+       ip->info.node = -1;
+       ip->info.flags = FBINFO_FLAG_DEFAULT;
+       ip->info.fbops = &aafb_ops;
+       ip->info.disp = &ip->disp;
+       ip->info.changevar = NULL;
+       ip->info.switch_con = &aafb_switch;
+       ip->info.updatevar = &aafb_update_var;
+       ip->info.blank = &aafb_blank;
+
+       aafb_set_disp(&ip->disp, currcon, ip);
+
+       /*
+        * Configure the RAM DACs.
+        */
+       bt455_erase_cursor(ip->bt455);
+
+       /* Init colormap. */
+       bt455_write_cmap_entry(ip->bt455, 0, 0x00, 0x00, 0x00);
+       bt455_write_cmap_entry(ip->bt455, 1, 0x0f, 0x0f, 0x0f);
+
+       /* Init hardware cursor. */
+       bt431_init_cursor(ip->bt431);
+       aafb_cursor_init(ip);
+
+       /* Clear the screen. */
+       memset ((void *)ip->fb_start, 0, ip->fb_size);
+
+       if (register_framebuffer(&ip->info) < 0)
+               return -EINVAL;
+
+       printk(KERN_INFO "fb%d: %s frame buffer in TC slot %d\n",
+              GET_FB_IDX(ip->info.node), ip->info.modename, slot);
+
+       return 0;
+}
+
+static int __exit exit_one(int slot)
+{
+       struct aafb_info *ip = &my_fb_info[slot];
+
+       if (unregister_framebuffer(&ip->info) < 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+/*
+ * Initialise the framebuffer.
+ */
+int __init pmagaafb_init(void)
+{
+       int sid;
+       int found = 0;
+
+       while ((sid = search_tc_card("PMAG-AA")) >= 0) {
+               found = 1;
+               claim_tc_card(sid);
+               init_one(sid);
+       }
+
+       return found ? 0 : -ENXIO;
+}
+
+static void __exit pmagaafb_exit(void)
+{
+       int sid;
+
+       while ((sid = search_tc_card("PMAG-AA")) >= 0) {
+               exit_one(sid);
+               release_tc_card(sid);
+       }
+}
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
+MODULE_LICENSE("GPL");
+#ifdef MODULE
+module_init(pmagaafb_init);
+module_exit(pmagaafb_exit);
+#endif
diff --git a/drivers/video/savage/Makefile b/drivers/video/savage/Makefile
new file mode 100644 (file)
index 0000000..4859115
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for the S3 Savage framebuffer driver
+#
+
+obj-$(CONFIG_FB_SAVAGE)                += savagefb.o
+obj-$(CONFIG_FB_SAVAGE_I2C)     += savagefb-i2c.o
+obj-$(CONFIG_FB_SAVAGE_ACCEL)   += savagefb_accel.o
+
diff --git a/drivers/video/savage/savagefb-i2c.c b/drivers/video/savage/savagefb-i2c.c
new file mode 100644 (file)
index 0000000..2037dad
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * linux/drivers/video/savage/savagefb-i2c.c - S3 Savage DDC2
+ *
+ * Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
+ *
+ * Based partly on rivafb-i2c.c
+ *
+ * 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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+
+#include <asm/io.h>
+#include "savagefb.h"
+
+#define SAVAGE_DDC     0x50
+
+#define VGA_CR_IX      0x3d4
+#define VGA_CR_DATA    0x3d5
+
+#define CR_SERIAL1     0xa0    /* I2C serial communications interface */
+#define MM_SERIAL1     0xff20
+#define CR_SERIAL2     0xb1    /* DDC2 monitor communications interface */
+
+/* based on vt8365 documentation */
+#define PROSAVAGE_I2C_ENAB     0x10
+#define PROSAVAGE_I2C_SCL_OUT  0x01
+#define PROSAVAGE_I2C_SDA_OUT  0x02
+#define PROSAVAGE_I2C_SCL_IN   0x04
+#define PROSAVAGE_I2C_SDA_IN   0x08
+
+#define SAVAGE4_I2C_ENAB       0x00000020
+#define SAVAGE4_I2C_SCL_OUT    0x00000001
+#define SAVAGE4_I2C_SDA_OUT    0x00000002
+#define SAVAGE4_I2C_SCL_IN     0x00000008
+#define SAVAGE4_I2C_SDA_IN     0x00000010
+
+#define SET_CR_IX(base, val)   writeb((val), base + 0x8000 + VGA_CR_IX)
+#define SET_CR_DATA(base, val) writeb((val), base + 0x8000 + VGA_CR_DATA)
+#define GET_CR_DATA(base)      readb(base + 0x8000 + VGA_CR_DATA)
+
+static void savage4_gpio_setscl(void *data, int val)
+{
+       struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data;
+       unsigned int r;
+
+       r = readl(chan->ioaddr + chan->reg);
+       if(val)
+               r |= SAVAGE4_I2C_SCL_OUT;
+       else
+               r &= ~SAVAGE4_I2C_SCL_OUT;
+       writel(r, chan->ioaddr + chan->reg);
+       readl(chan->ioaddr + chan->reg);        /* flush posted write */
+}
+
+static void savage4_gpio_setsda(void *data, int val)
+{
+       struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data;
+
+       unsigned int r;
+       r = readl(chan->ioaddr + chan->reg);
+       if(val)
+               r |= SAVAGE4_I2C_SDA_OUT;
+       else
+               r &= ~SAVAGE4_I2C_SDA_OUT;
+       writel(r, chan->ioaddr + chan->reg);
+       readl(chan->ioaddr + chan->reg);        /* flush posted write */
+}
+
+static int savage4_gpio_getscl(void *data)
+{
+       struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data;
+
+       return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SCL_IN));
+}
+
+static int savage4_gpio_getsda(void *data)
+{
+       struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data;
+
+       return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SDA_IN));
+}
+
+static void prosavage_gpio_setscl(void* data, int val)
+{
+       struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data;
+       u32                       r;
+
+       SET_CR_IX(chan->ioaddr, chan->reg);
+       r = GET_CR_DATA(chan->ioaddr);
+       r |= PROSAVAGE_I2C_ENAB;
+       if (val) {
+               r |= PROSAVAGE_I2C_SCL_OUT;
+       } else {
+               r &= ~PROSAVAGE_I2C_SCL_OUT;
+       }
+       SET_CR_DATA(chan->ioaddr, r);
+}
+
+static void prosavage_gpio_setsda(void* data, int val)
+{
+       struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data;
+       unsigned int r;
+
+       SET_CR_IX(chan->ioaddr, chan->reg);
+       r = GET_CR_DATA(chan->ioaddr);
+       r |= PROSAVAGE_I2C_ENAB;
+       if (val) {
+               r |= PROSAVAGE_I2C_SDA_OUT;
+       } else {
+               r &= ~PROSAVAGE_I2C_SDA_OUT;
+       }
+       SET_CR_DATA(chan->ioaddr, r);
+}
+
+static int prosavage_gpio_getscl(void* data)
+{
+       struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data;
+
+       SET_CR_IX(chan->ioaddr, chan->reg);
+       return (0 != (GET_CR_DATA(chan->ioaddr) & PROSAVAGE_I2C_SCL_IN));
+}
+
+static int prosavage_gpio_getsda(void* data)
+{
+       struct savagefb_i2c_chan *chan = (struct savagefb_i2c_chan *)data;
+
+       SET_CR_IX(chan->ioaddr, chan->reg);
+       return (0 != (GET_CR_DATA(chan->ioaddr) & PROSAVAGE_I2C_SDA_IN));
+}
+
+#define I2C_ALGO_SAVAGE   0x0f0000
+static int savage_setup_i2c_bus(struct savagefb_i2c_chan *chan,
+                               const char *name)
+{
+       int (*add_bus)(struct i2c_adapter *) = symbol_get(i2c_bit_add_bus);
+       int rc = 0;
+
+       if (add_bus && chan->par) {
+               strcpy(chan->adapter.name, name);
+               chan->adapter.owner             = THIS_MODULE;
+               chan->adapter.id                = I2C_ALGO_SAVAGE;
+               chan->adapter.algo_data         = &chan->algo;
+               chan->adapter.dev.parent        = &chan->par->pcidev->dev;
+               chan->algo.udelay               = 40;
+               chan->algo.mdelay               = 5;
+               chan->algo.timeout              = 20;
+               chan->algo.data                 = chan;
+
+               i2c_set_adapdata(&chan->adapter, chan);
+
+               /* Raise SCL and SDA */
+               chan->algo.setsda(chan, 1);
+               chan->algo.setscl(chan, 1);
+               udelay(20);
+
+               rc = add_bus(&chan->adapter);
+               if (rc == 0)
+                       dev_dbg(&chan->par->pcidev->dev,
+                               "I2C bus %s registered.\n", name);
+               else
+                       dev_warn(&chan->par->pcidev->dev,
+                                "Failed to register I2C bus %s.\n", name);
+       } else
+               chan->par = NULL;
+
+       return rc;
+}
+
+void savagefb_create_i2c_busses(struct fb_info *info)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       par->chan.par   = par;
+
+       switch(info->fix.accel) {
+       case FB_ACCEL_PROSAVAGE_DDRK:
+       case FB_ACCEL_PROSAVAGE_PM:
+               par->chan.reg         = CR_SERIAL2;
+               par->chan.ioaddr      = par->mmio.vbase;
+               par->chan.algo.setsda = prosavage_gpio_setsda;
+               par->chan.algo.setscl = prosavage_gpio_setscl;
+               par->chan.algo.getsda = prosavage_gpio_getsda;
+               par->chan.algo.getscl = prosavage_gpio_getscl;
+               break;
+       case FB_ACCEL_SAVAGE4:
+               par->chan.reg         = 0xff20;
+               par->chan.ioaddr      = par->mmio.vbase;
+               par->chan.algo.setsda = savage4_gpio_setsda;
+               par->chan.algo.setscl = savage4_gpio_setscl;
+               par->chan.algo.getsda = savage4_gpio_getsda;
+               par->chan.algo.getscl = savage4_gpio_getscl;
+               break;
+       default:
+               par->chan.par = NULL;
+       }
+
+       savage_setup_i2c_bus(&par->chan, "SAVAGE DDC2");
+}
+EXPORT_SYMBOL(savagefb_create_i2c_busses);
+
+void savagefb_delete_i2c_busses(struct fb_info *info)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       int (*del_bus)(struct i2c_adapter *) =
+               symbol_get(i2c_bit_del_bus);
+
+       if (del_bus && par->chan.par)
+               del_bus(&par->chan.adapter);
+
+       par->chan.par = NULL;
+}
+EXPORT_SYMBOL(savagefb_delete_i2c_busses);
+
+static u8 *savage_do_probe_i2c_edid(struct savagefb_i2c_chan *chan)
+{
+       u8 start = 0x0;
+       int (*transfer)(struct i2c_adapter *, struct i2c_msg *, int) =
+               symbol_get(i2c_transfer);
+       struct i2c_msg msgs[] = {
+               {
+                       .addr   = SAVAGE_DDC,
+                       .len    = 1,
+                       .buf    = &start,
+               }, {
+                       .addr   = SAVAGE_DDC,
+                       .flags  = I2C_M_RD,
+                       .len    = EDID_LENGTH,
+               },
+       };
+       u8 *buf = NULL;
+
+       if (transfer && chan->par) {
+               buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+               if (buf) {
+                       msgs[1].buf = buf;
+
+                       if (transfer(&chan->adapter, msgs, 2) != 2) {
+                               dev_dbg(&chan->par->pcidev->dev,
+                                       "Unable to read EDID block.\n");
+                               kfree(buf);
+                               buf = NULL;
+                       }
+               }
+       }
+
+       return buf;
+}
+
+int savagefb_probe_i2c_connector(struct savagefb_par *par, u8 **out_edid)
+{
+       u8 *edid = NULL;
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               /* Do the real work */
+               edid = savage_do_probe_i2c_edid(&par->chan);
+               if (edid)
+                       break;
+       }
+       if (out_edid)
+               *out_edid = edid;
+       if (!edid)
+               return 1;
+
+       return 0;
+}
+EXPORT_SYMBOL(savagefb_probe_i2c_connector);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/savage/savagefb.c b/drivers/video/savage/savagefb.c
new file mode 100644 (file)
index 0000000..4c5edfa
--- /dev/null
@@ -0,0 +1,2276 @@
+/*
+ * linux/drivers/video/savagefb.c -- S3 Savage Framebuffer Driver
+ *
+ * Copyright (c) 2001-2002  Denis Oliver Kropp <dok@directfb.org>
+ *                          Sven Neumann <neo@directfb.org>
+ *
+ *
+ * Card specific code is based on XFree86's savage driver.
+ * Framebuffer framework code is based on code of cyber2000fb and tdfxfb.
+ *
+ * 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.
+ *
+ * 0.4.0 (neo)
+ *  - hardware accelerated clear and move
+ *
+ * 0.3.2 (dok)
+ *  - wait for vertical retrace before writing to cr67
+ *    at the beginning of savagefb_set_par
+ *  - use synchronization registers cr23 and cr26
+ *
+ * 0.3.1 (dok)
+ *  - reset 3D engine
+ *  - don't return alpha bits for 32bit format
+ *
+ * 0.3.0 (dok)
+ *  - added WaitIdle functions for all Savage types
+ *  - do WaitIdle before mode switching
+ *  - code cleanup
+ *
+ * 0.2.0 (dok)
+ *  - first working version
+ *
+ *
+ * TODO
+ * - clock validations in decode_var
+ *
+ * BUGS
+ * - white margin on bootup
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/console.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include "savagefb.h"
+
+
+#define SAVAGEFB_VERSION "0.4.0_2.6"
+
+/* --------------------------------------------------------------------- */
+
+
+static char *mode_option __initdata = NULL;
+static int   paletteEnabled = 0;
+
+#ifdef MODULE
+
+MODULE_AUTHOR("(c) 2001-2002  Denis Oliver Kropp <dok@directfb.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("FBDev driver for S3 Savage PCI/AGP Chips");
+
+#endif
+
+
+/* --------------------------------------------------------------------- */
+
+static void vgaHWSeqReset (struct savagefb_par *par, int start)
+{
+       if (start)
+               VGAwSEQ (0x00, 0x01);           /* Synchronous Reset */
+       else
+               VGAwSEQ (0x00, 0x03);           /* End Reset */
+}
+
+static void vgaHWProtect (struct savagefb_par *par, int on)
+{
+       unsigned char tmp;
+
+       if (on) {
+               /*
+                * Turn off screen and disable sequencer.
+                */
+               tmp = VGArSEQ (0x01);
+
+               vgaHWSeqReset (par, 1);         /* start synchronous reset */
+               VGAwSEQ (0x01, tmp | 0x20);     /* disable the display */
+
+               VGAenablePalette();
+       } else {
+               /*
+                * Reenable sequencer, then turn on screen.
+                */
+
+               tmp = VGArSEQ (0x01);
+
+               VGAwSEQ (0x01, tmp & ~0x20);    /* reenable display */
+               vgaHWSeqReset (par, 0);         /* clear synchronous reset */
+
+               VGAdisablePalette();
+       }
+}
+
+static void vgaHWRestore (struct savagefb_par  *par)
+{
+       int i;
+
+       VGAwMISC (par->MiscOutReg);
+
+       for (i = 1; i < 5; i++)
+               VGAwSEQ (i, par->Sequencer[i]);
+
+       /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or
+          CRTC[17] */
+       VGAwCR (17, par->CRTC[17] & ~0x80);
+
+       for (i = 0; i < 25; i++)
+               VGAwCR (i, par->CRTC[i]);
+
+       for (i = 0; i < 9; i++)
+               VGAwGR (i, par->Graphics[i]);
+
+       VGAenablePalette();
+
+       for (i = 0; i < 21; i++)
+               VGAwATTR (i, par->Attribute[i]);
+
+       VGAdisablePalette();
+}
+
+static void vgaHWInit (struct fb_var_screeninfo *var,
+                      struct savagefb_par            *par,
+                      struct xtimings                *timings)
+{
+       par->MiscOutReg = 0x23;
+
+       if (!(timings->sync & FB_SYNC_HOR_HIGH_ACT))
+               par->MiscOutReg |= 0x40;
+
+       if (!(timings->sync & FB_SYNC_VERT_HIGH_ACT))
+               par->MiscOutReg |= 0x80;
+
+       /*
+        * Time Sequencer
+        */
+       par->Sequencer[0x00] = 0x00;
+       par->Sequencer[0x01] = 0x01;
+       par->Sequencer[0x02] = 0x0F;
+       par->Sequencer[0x03] = 0x00;          /* Font select */
+       par->Sequencer[0x04] = 0x0E;          /* Misc */
+
+       /*
+        * CRTC Controller
+        */
+       par->CRTC[0x00] = (timings->HTotal >> 3) - 5;
+       par->CRTC[0x01] = (timings->HDisplay >> 3) - 1;
+       par->CRTC[0x02] = (timings->HSyncStart >> 3) - 1;
+       par->CRTC[0x03] = (((timings->HSyncEnd >> 3)  - 1) & 0x1f) | 0x80;
+       par->CRTC[0x04] = (timings->HSyncStart >> 3);
+       par->CRTC[0x05] = ((((timings->HSyncEnd >> 3) - 1) & 0x20) << 2) |
+               (((timings->HSyncEnd >> 3)) & 0x1f);
+       par->CRTC[0x06] = (timings->VTotal - 2) & 0xFF;
+       par->CRTC[0x07] = (((timings->VTotal - 2) & 0x100) >> 8) |
+               (((timings->VDisplay - 1) & 0x100) >> 7) |
+               ((timings->VSyncStart & 0x100) >> 6) |
+               (((timings->VSyncStart - 1) & 0x100) >> 5) |
+               0x10 |
+               (((timings->VTotal - 2) & 0x200) >> 4) |
+               (((timings->VDisplay - 1) & 0x200) >> 3) |
+               ((timings->VSyncStart & 0x200) >> 2);
+       par->CRTC[0x08] = 0x00;
+       par->CRTC[0x09] = (((timings->VSyncStart - 1) & 0x200) >> 4) | 0x40;
+
+       if (timings->dblscan)
+               par->CRTC[0x09] |= 0x80;
+
+       par->CRTC[0x0a] = 0x00;
+       par->CRTC[0x0b] = 0x00;
+       par->CRTC[0x0c] = 0x00;
+       par->CRTC[0x0d] = 0x00;
+       par->CRTC[0x0e] = 0x00;
+       par->CRTC[0x0f] = 0x00;
+       par->CRTC[0x10] = timings->VSyncStart & 0xff;
+       par->CRTC[0x11] = (timings->VSyncEnd & 0x0f) | 0x20;
+       par->CRTC[0x12] = (timings->VDisplay - 1) & 0xff;
+       par->CRTC[0x13] = var->xres_virtual >> 4;
+       par->CRTC[0x14] = 0x00;
+       par->CRTC[0x15] = (timings->VSyncStart - 1) & 0xff;
+       par->CRTC[0x16] = (timings->VSyncEnd - 1) & 0xff;
+       par->CRTC[0x17] = 0xc3;
+       par->CRTC[0x18] = 0xff;
+
+       /*
+        * are these unnecessary?
+        * vgaHWHBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO);
+        * vgaHWVBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO);
+        */
+
+       /*
+        * Graphics Display Controller
+        */
+       par->Graphics[0x00] = 0x00;
+       par->Graphics[0x01] = 0x00;
+       par->Graphics[0x02] = 0x00;
+       par->Graphics[0x03] = 0x00;
+       par->Graphics[0x04] = 0x00;
+       par->Graphics[0x05] = 0x40;
+       par->Graphics[0x06] = 0x05;   /* only map 64k VGA memory !!!! */
+       par->Graphics[0x07] = 0x0F;
+       par->Graphics[0x08] = 0xFF;
+
+
+       par->Attribute[0x00]  = 0x00; /* standard colormap translation */
+       par->Attribute[0x01]  = 0x01;
+       par->Attribute[0x02]  = 0x02;
+       par->Attribute[0x03]  = 0x03;
+       par->Attribute[0x04]  = 0x04;
+       par->Attribute[0x05]  = 0x05;
+       par->Attribute[0x06]  = 0x06;
+       par->Attribute[0x07]  = 0x07;
+       par->Attribute[0x08]  = 0x08;
+       par->Attribute[0x09]  = 0x09;
+       par->Attribute[0x0a] = 0x0A;
+       par->Attribute[0x0b] = 0x0B;
+       par->Attribute[0x0c] = 0x0C;
+       par->Attribute[0x0d] = 0x0D;
+       par->Attribute[0x0e] = 0x0E;
+       par->Attribute[0x0f] = 0x0F;
+       par->Attribute[0x10] = 0x41;
+       par->Attribute[0x11] = 0xFF;
+       par->Attribute[0x12] = 0x0F;
+       par->Attribute[0x13] = 0x00;
+       par->Attribute[0x14] = 0x00;
+}
+
+/* -------------------- Hardware specific routines ------------------------- */
+
+/*
+ * Hardware Acceleration for SavageFB
+ */
+
+/* Wait for fifo space */
+static void
+savage3D_waitfifo(struct savagefb_par *par, int space)
+{
+       int slots = MAXFIFO - space;
+
+       while ((savage_in32(0x48C00) & 0x0000ffff) > slots);
+}
+
+static void
+savage4_waitfifo(struct savagefb_par *par, int space)
+{
+       int slots = MAXFIFO - space;
+
+       while ((savage_in32(0x48C60) & 0x001fffff) > slots);
+}
+
+static void
+savage2000_waitfifo(struct savagefb_par *par, int space)
+{
+       int slots = MAXFIFO - space;
+
+       while ((savage_in32(0x48C60) & 0x0000ffff) > slots);
+}
+
+/* Wait for idle accelerator */
+static void
+savage3D_waitidle(struct savagefb_par *par)
+{
+       while ((savage_in32(0x48C00) & 0x0008ffff) != 0x80000);
+}
+
+static void
+savage4_waitidle(struct savagefb_par *par)
+{
+       while ((savage_in32(0x48C60) & 0x00a00000) != 0x00a00000);
+}
+
+static void
+savage2000_waitidle(struct savagefb_par *par)
+{
+       while ((savage_in32(0x48C60) & 0x009fffff));
+}
+
+
+static void
+SavageSetup2DEngine (struct savagefb_par  *par)
+{
+       unsigned long GlobalBitmapDescriptor;
+
+       GlobalBitmapDescriptor = 1 | 8 | BCI_BD_BW_DISABLE;
+       BCI_BD_SET_BPP (GlobalBitmapDescriptor, par->depth);
+       BCI_BD_SET_STRIDE (GlobalBitmapDescriptor, par->vwidth);
+
+       switch(par->chip) {
+       case S3_SAVAGE3D:
+       case S3_SAVAGE_MX:
+               /* Disable BCI */
+               savage_out32(0x48C18, savage_in32(0x48C18) & 0x3FF0);
+               /* Setup BCI command overflow buffer */
+               savage_out32(0x48C14, (par->cob_offset >> 11) | (par->cob_index << 29));
+               /* Program shadow status update. */
+               savage_out32(0x48C10, 0x78207220);
+               savage_out32(0x48C0C, 0);
+               /* Enable BCI and command overflow buffer */
+               savage_out32(0x48C18, savage_in32(0x48C18) | 0x0C);
+               break;
+       case S3_SAVAGE4:
+       case S3_PROSAVAGE:
+       case S3_SUPERSAVAGE:
+               /* Disable BCI */
+               savage_out32(0x48C18, savage_in32(0x48C18) & 0x3FF0);
+               /* Program shadow status update */
+               savage_out32(0x48C10, 0x00700040);
+               savage_out32(0x48C0C, 0);
+               /* Enable BCI without the COB */
+               savage_out32(0x48C18, savage_in32(0x48C18) | 0x08);
+               break;
+       case S3_SAVAGE2000:
+               /* Disable BCI */
+               savage_out32(0x48C18, 0);
+               /* Setup BCI command overflow buffer */
+               savage_out32(0x48C18, (par->cob_offset >> 7) | (par->cob_index));
+               /* Disable shadow status update */
+               savage_out32(0x48A30, 0);
+               /* Enable BCI and command overflow buffer */
+               savage_out32(0x48C18, savage_in32(0x48C18) | 0x00280000 );
+               break;
+           default:
+               break;
+       }
+       /* Turn on 16-bit register access. */
+       vga_out8(0x3d4, 0x31);
+       vga_out8(0x3d5, 0x0c);
+
+       /* Set stride to use GBD. */
+       vga_out8 (0x3d4, 0x50);
+       vga_out8 (0x3d5, vga_in8 (0x3d5 ) | 0xC1);
+
+       /* Enable 2D engine. */
+       vga_out8 (0x3d4, 0x40 );
+       vga_out8 (0x3d5, 0x01 );
+
+       savage_out32 (MONO_PAT_0, ~0);
+       savage_out32 (MONO_PAT_1, ~0);
+
+       /* Setup plane masks */
+       savage_out32 (0x8128, ~0 ); /* enable all write planes */
+       savage_out32 (0x812C, ~0 ); /* enable all read planes */
+       savage_out16 (0x8134, 0x27 );
+       savage_out16 (0x8136, 0x07 );
+
+       /* Now set the GBD */
+       par->bci_ptr = 0;
+       par->SavageWaitFifo (par, 4);
+
+       BCI_SEND( BCI_CMD_SETREG | (1 << 16) | BCI_GBD1 );
+       BCI_SEND( 0 );
+       BCI_SEND( BCI_CMD_SETREG | (1 << 16) | BCI_GBD2 );
+       BCI_SEND( GlobalBitmapDescriptor );
+}
+
+
+static void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1,
+                           int min_n2, int max_n2, long freq_min,
+                           long freq_max, unsigned int *mdiv,
+                           unsigned int *ndiv, unsigned int *r)
+{
+       long diff, best_diff;
+       unsigned int m;
+       unsigned char n1, n2, best_n1=16+2, best_n2=2, best_m=125+2;
+
+       if (freq < freq_min / (1 << max_n2)) {
+               printk (KERN_ERR "invalid frequency %ld Khz\n", freq);
+               freq = freq_min / (1 << max_n2);
+       }
+       if (freq > freq_max / (1 << min_n2)) {
+               printk (KERN_ERR "invalid frequency %ld Khz\n", freq);
+               freq = freq_max / (1 << min_n2);
+       }
+
+       /* work out suitable timings */
+       best_diff = freq;
+
+       for (n2=min_n2; n2<=max_n2; n2++) {
+               for (n1=min_n1+2; n1<=max_n1+2; n1++) {
+                       m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) /
+                               BASE_FREQ;
+                       if (m < min_m+2 || m > 127+2)
+                               continue;
+                       if ((m * BASE_FREQ >= freq_min * n1) &&
+                           (m * BASE_FREQ <= freq_max * n1)) {
+                               diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
+                               if (diff < 0)
+                                       diff = -diff;
+                               if (diff < best_diff) {
+                                       best_diff = diff;
+                                       best_m = m;
+                                       best_n1 = n1;
+                                       best_n2 = n2;
+                               }
+                       }
+               }
+       }
+
+       *ndiv = best_n1 - 2;
+       *r = best_n2;
+       *mdiv = best_m - 2;
+}
+
+static int common_calc_clock(long freq, int min_m, int min_n1, int max_n1,
+                            int min_n2, int max_n2, long freq_min,
+                            long freq_max, unsigned char *mdiv,
+                            unsigned char *ndiv)
+{
+       long diff, best_diff;
+       unsigned int m;
+       unsigned char n1, n2;
+       unsigned char best_n1 = 16+2, best_n2 = 2, best_m = 125+2;
+
+       best_diff = freq;
+
+       for (n2 = min_n2; n2 <= max_n2; n2++) {
+               for (n1 = min_n1+2; n1 <= max_n1+2; n1++) {
+                       m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) /
+                               BASE_FREQ;
+                       if (m < min_m + 2 || m > 127+2)
+                               continue;
+                       if((m * BASE_FREQ >= freq_min * n1) &&
+                          (m * BASE_FREQ <= freq_max * n1)) {
+                               diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
+                               if(diff < 0)
+                                       diff = -diff;
+                               if(diff < best_diff) {
+                                       best_diff = diff;
+                                       best_m = m;
+                                       best_n1 = n1;
+                                       best_n2 = n2;
+                               }
+                       }
+               }
+       }
+
+       if(max_n1 == 63)
+               *ndiv = (best_n1 - 2) | (best_n2 << 6);
+       else
+               *ndiv = (best_n1 - 2) | (best_n2 << 5);
+
+       *mdiv = best_m - 2;
+
+       return 0;
+}
+
+#ifdef SAVAGEFB_DEBUG
+/* This function is used to debug, it prints out the contents of s3 regs */
+
+static void SavagePrintRegs(void)
+{
+       unsigned char i;
+       int vgaCRIndex = 0x3d4;
+       int vgaCRReg = 0x3d5;
+
+       printk(KERN_DEBUG "SR    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE "
+              "xF" );
+
+       for( i = 0; i < 0x70; i++ ) {
+               if( !(i % 16) )
+                       printk(KERN_DEBUG "\nSR%xx ", i >> 4 );
+               vga_out8( 0x3c4, i );
+               printk(KERN_DEBUG " %02x", vga_in8(0x3c5) );
+       }
+
+       printk(KERN_DEBUG "\n\nCR    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC "
+              "xD xE xF" );
+
+       for( i = 0; i < 0xB7; i++ ) {
+               if( !(i % 16) )
+                       printk(KERN_DEBUG "\nCR%xx ", i >> 4 );
+               vga_out8( vgaCRIndex, i );
+               printk(KERN_DEBUG " %02x", vga_in8(vgaCRReg) );
+       }
+
+       printk(KERN_DEBUG "\n\n");
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static void savage_get_default_par(struct savagefb_par *par)
+{
+       unsigned char cr3a, cr53, cr66;
+
+       vga_out16 (0x3d4, 0x4838);
+       vga_out16 (0x3d4, 0xa039);
+       vga_out16 (0x3c4, 0x0608);
+
+       vga_out8 (0x3d4, 0x66);
+       cr66 = vga_in8 (0x3d5);
+       vga_out8 (0x3d5, cr66 | 0x80);
+       vga_out8 (0x3d4, 0x3a);
+       cr3a = vga_in8 (0x3d5);
+       vga_out8 (0x3d5, cr3a | 0x80);
+       vga_out8 (0x3d4, 0x53);
+       cr53 = vga_in8 (0x3d5);
+       vga_out8 (0x3d5, cr53 & 0x7f);
+
+       vga_out8 (0x3d4, 0x66);
+       vga_out8 (0x3d5, cr66);
+       vga_out8 (0x3d4, 0x3a);
+       vga_out8 (0x3d5, cr3a);
+
+       vga_out8 (0x3d4, 0x66);
+       vga_out8 (0x3d5, cr66);
+       vga_out8 (0x3d4, 0x3a);
+       vga_out8 (0x3d5, cr3a);
+
+       /* unlock extended seq regs */
+       vga_out8 (0x3c4, 0x08);
+       par->SR08 = vga_in8 (0x3c5);
+       vga_out8 (0x3c5, 0x06);
+
+       /* now save all the extended regs we need */
+       vga_out8 (0x3d4, 0x31);
+       par->CR31 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x32);
+       par->CR32 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x34);
+       par->CR34 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x36);
+       par->CR36 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x3a);
+       par->CR3A = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x40);
+       par->CR40 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x42);
+       par->CR42 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x45);
+       par->CR45 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x50);
+       par->CR50 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x51);
+       par->CR51 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x53);
+       par->CR53 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x58);
+       par->CR58 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x60);
+       par->CR60 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x66);
+       par->CR66 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x67);
+       par->CR67 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x68);
+       par->CR68 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x69);
+       par->CR69 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x6f);
+       par->CR6F = vga_in8 (0x3d5);
+
+       vga_out8 (0x3d4, 0x33);
+       par->CR33 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x86);
+       par->CR86 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x88);
+       par->CR88 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x90);
+       par->CR90 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x91);
+       par->CR91 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0xb0);
+       par->CRB0 = vga_in8 (0x3d5) | 0x80;
+
+       /* extended mode timing regs */
+       vga_out8 (0x3d4, 0x3b);
+       par->CR3B = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x3c);
+       par->CR3C = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x43);
+       par->CR43 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x5d);
+       par->CR5D = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x5e);
+       par->CR5E = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x65);
+       par->CR65 = vga_in8 (0x3d5);
+
+       /* save seq extended regs for DCLK PLL programming */
+       vga_out8 (0x3c4, 0x0e);
+       par->SR0E = vga_in8 (0x3c5);
+       vga_out8 (0x3c4, 0x0f);
+       par->SR0F = vga_in8 (0x3c5);
+       vga_out8 (0x3c4, 0x10);
+       par->SR10 = vga_in8 (0x3c5);
+       vga_out8 (0x3c4, 0x11);
+       par->SR11 = vga_in8 (0x3c5);
+       vga_out8 (0x3c4, 0x12);
+       par->SR12 = vga_in8 (0x3c5);
+       vga_out8 (0x3c4, 0x13);
+       par->SR13 = vga_in8 (0x3c5);
+       vga_out8 (0x3c4, 0x29);
+       par->SR29 = vga_in8 (0x3c5);
+
+       vga_out8 (0x3c4, 0x15);
+       par->SR15 = vga_in8 (0x3c5);
+       vga_out8 (0x3c4, 0x30);
+       par->SR30 = vga_in8 (0x3c5);
+       vga_out8 (0x3c4, 0x18);
+       par->SR18 = vga_in8 (0x3c5);
+
+       /* Save flat panel expansion regsters. */
+       if (par->chip == S3_SAVAGE_MX) {
+               int i;
+
+               for (i = 0; i < 8; i++) {
+                       vga_out8 (0x3c4, 0x54+i);
+                       par->SR54[i] = vga_in8 (0x3c5);
+               }
+       }
+
+       vga_out8 (0x3d4, 0x66);
+       cr66 = vga_in8 (0x3d5);
+       vga_out8 (0x3d5, cr66 | 0x80);
+       vga_out8 (0x3d4, 0x3a);
+       cr3a = vga_in8 (0x3d5);
+       vga_out8 (0x3d5, cr3a | 0x80);
+
+       /* now save MIU regs */
+       if (par->chip != S3_SAVAGE_MX) {
+               par->MMPR0 = savage_in32(FIFO_CONTROL_REG);
+               par->MMPR1 = savage_in32(MIU_CONTROL_REG);
+               par->MMPR2 = savage_in32(STREAMS_TIMEOUT_REG);
+               par->MMPR3 = savage_in32(MISC_TIMEOUT_REG);
+       }
+
+       vga_out8 (0x3d4, 0x3a);
+       vga_out8 (0x3d5, cr3a);
+       vga_out8 (0x3d4, 0x66);
+       vga_out8 (0x3d5, cr66);
+}
+
+static void savage_update_var(struct fb_var_screeninfo *var, struct fb_videomode *modedb)
+{
+       var->xres = var->xres_virtual = modedb->xres;
+       var->yres = modedb->yres;
+        if (var->yres_virtual < var->yres)
+           var->yres_virtual = var->yres;
+        var->xoffset = var->yoffset = 0;
+        var->pixclock = modedb->pixclock;
+        var->left_margin = modedb->left_margin;
+        var->right_margin = modedb->right_margin;
+        var->upper_margin = modedb->upper_margin;
+        var->lower_margin = modedb->lower_margin;
+        var->hsync_len = modedb->hsync_len;
+        var->vsync_len = modedb->vsync_len;
+        var->sync = modedb->sync;
+        var->vmode = modedb->vmode;
+}
+
+static int savagefb_check_var (struct fb_var_screeninfo   *var,
+                              struct fb_info *info)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       int memlen, vramlen, mode_valid = 0;
+
+       DBG("savagefb_check_var");
+
+       var->transp.offset = 0;
+       var->transp.length = 0;
+       switch (var->bits_per_pixel) {
+       case 8:
+               var->red.offset = var->green.offset =
+                       var->blue.offset = 0;
+               var->red.length = var->green.length =
+                       var->blue.length = var->bits_per_pixel;
+               break;
+       case 16:
+               var->red.offset = 11;
+               var->red.length = 5;
+               var->green.offset = 5;
+               var->green.length = 6;
+               var->blue.offset = 0;
+               var->blue.length = 5;
+               break;
+       case 32:
+               var->transp.offset = 24;
+               var->transp.length = 8;
+               var->red.offset = 16;
+               var->red.length = 8;
+               var->green.offset = 8;
+               var->green.length = 8;
+               var->blue.offset = 0;
+               var->blue.length = 8;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
+           !info->monspecs.dclkmax || !fb_validate_mode(var, info))
+               mode_valid = 1;
+
+       /* calculate modeline if supported by monitor */
+       if (!mode_valid && info->monspecs.gtf) {
+               if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
+                       mode_valid = 1;
+       }
+
+       if (!mode_valid) {
+               struct fb_videomode *mode;
+
+               mode = fb_find_best_mode(var, &info->modelist);
+               if (mode) {
+                       savage_update_var(var, mode);
+                       mode_valid = 1;
+               }
+       }
+
+       if (!mode_valid && info->monspecs.modedb_len)
+               return -EINVAL;
+
+       /* Is the mode larger than the LCD panel? */
+       if (par->SavagePanelWidth &&
+           (var->xres > par->SavagePanelWidth ||
+            var->yres > par->SavagePanelHeight)) {
+               printk (KERN_INFO "Mode (%dx%d) larger than the LCD panel "
+                       "(%dx%d)\n", var->xres,  var->yres,
+                       par->SavagePanelWidth,
+                       par->SavagePanelHeight);
+               return -1;
+       }
+
+       if (var->yres_virtual < var->yres)
+               var->yres_virtual = var->yres;
+       if (var->xres_virtual < var->xres)
+               var->xres_virtual = var->xres;
+
+       vramlen = info->fix.smem_len;
+
+       memlen = var->xres_virtual * var->bits_per_pixel *
+               var->yres_virtual / 8;
+       if (memlen > vramlen) {
+               var->yres_virtual = vramlen * 8 /
+                       (var->xres_virtual * var->bits_per_pixel);
+               memlen = var->xres_virtual * var->bits_per_pixel *
+                       var->yres_virtual / 8;
+       }
+
+       /* we must round yres/xres down, we already rounded y/xres_virtual up
+          if it was possible. We should return -EINVAL, but I disagree */
+       if (var->yres_virtual < var->yres)
+               var->yres = var->yres_virtual;
+       if (var->xres_virtual < var->xres)
+               var->xres = var->xres_virtual;
+       if (var->xoffset + var->xres > var->xres_virtual)
+               var->xoffset = var->xres_virtual - var->xres;
+       if (var->yoffset + var->yres > var->yres_virtual)
+               var->yoffset = var->yres_virtual - var->yres;
+
+       return 0;
+}
+
+
+static int savagefb_decode_var (struct fb_var_screeninfo   *var,
+                               struct savagefb_par        *par)
+{
+       struct xtimings timings;
+       int width, dclk, i, j; /*, refresh; */
+       unsigned int m, n, r;
+       unsigned char tmp = 0;
+       unsigned int pixclock = var->pixclock;
+
+       DBG("savagefb_decode_var");
+
+       memset (&timings, 0, sizeof(timings));
+
+       if (!pixclock) pixclock = 10000;        /* 10ns = 100MHz */
+       timings.Clock = 1000000000 / pixclock;
+       if (timings.Clock < 1) timings.Clock = 1;
+       timings.dblscan = var->vmode & FB_VMODE_DOUBLE;
+       timings.interlaced = var->vmode & FB_VMODE_INTERLACED;
+       timings.HDisplay = var->xres;
+       timings.HSyncStart = timings.HDisplay + var->right_margin;
+       timings.HSyncEnd = timings.HSyncStart + var->hsync_len;
+       timings.HTotal = timings.HSyncEnd + var->left_margin;
+       timings.VDisplay = var->yres;
+       timings.VSyncStart = timings.VDisplay + var->lower_margin;
+       timings.VSyncEnd = timings.VSyncStart + var->vsync_len;
+       timings.VTotal = timings.VSyncEnd + var->upper_margin;
+       timings.sync = var->sync;
+
+
+       par->depth  = var->bits_per_pixel;
+       par->vwidth = var->xres_virtual;
+
+       if (var->bits_per_pixel == 16  &&  par->chip == S3_SAVAGE3D) {
+               timings.HDisplay *= 2;
+               timings.HSyncStart *= 2;
+               timings.HSyncEnd *= 2;
+               timings.HTotal *= 2;
+       }
+
+       /*
+        * This will allocate the datastructure and initialize all of the
+        * generic VGA registers.
+        */
+       vgaHWInit (var, par, &timings);
+
+       /* We need to set CR67 whether or not we use the BIOS. */
+
+       dclk = timings.Clock;
+       par->CR67 = 0x00;
+
+       switch( var->bits_per_pixel ) {
+       case 8:
+               if( (par->chip == S3_SAVAGE2000) && (dclk >= 230000) )
+                       par->CR67 = 0x10;       /* 8bpp, 2 pixels/clock */
+               else
+                       par->CR67 = 0x00;       /* 8bpp, 1 pixel/clock */
+               break;
+       case 15:
+               if ( S3_SAVAGE_MOBILE_SERIES(par->chip) ||
+                    ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)) )
+                       par->CR67 = 0x30;       /* 15bpp, 2 pixel/clock */
+               else
+                       par->CR67 = 0x20;       /* 15bpp, 1 pixels/clock */
+               break;
+       case 16:
+               if( S3_SAVAGE_MOBILE_SERIES(par->chip) ||
+                   ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)) )
+                       par->CR67 = 0x50;       /* 16bpp, 2 pixel/clock */
+               else
+                       par->CR67 = 0x40;       /* 16bpp, 1 pixels/clock */
+               break;
+       case 24:
+               par->CR67 = 0x70;
+               break;
+       case 32:
+               par->CR67 = 0xd0;
+               break;
+       }
+
+       /*
+        * Either BIOS use is disabled, or we failed to find a suitable
+        * match.  Fall back to traditional register-crunching.
+        */
+
+       vga_out8 (0x3d4, 0x3a);
+       tmp = vga_in8 (0x3d5);
+       if (1 /*FIXME:psav->pci_burst*/)
+               par->CR3A = (tmp & 0x7f) | 0x15;
+       else
+               par->CR3A = tmp | 0x95;
+
+       par->CR53 = 0x00;
+       par->CR31 = 0x8c;
+       par->CR66 = 0x89;
+
+       vga_out8 (0x3d4, 0x58);
+       par->CR58 = vga_in8 (0x3d5) & 0x80;
+       par->CR58 |= 0x13;
+
+       par->SR15 = 0x03 | 0x80;
+       par->SR18 = 0x00;
+       par->CR43 = par->CR45 = par->CR65 = 0x00;
+
+       vga_out8 (0x3d4, 0x40);
+       par->CR40 = vga_in8 (0x3d5) & ~0x01;
+
+       par->MMPR0 = 0x010400;
+       par->MMPR1 = 0x00;
+       par->MMPR2 = 0x0808;
+       par->MMPR3 = 0x08080810;
+
+       SavageCalcClock (dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r);
+       /* m = 107; n = 4; r = 2; */
+
+       if (par->MCLK <= 0) {
+               par->SR10 = 255;
+               par->SR11 = 255;
+       } else {
+               common_calc_clock (par->MCLK, 1, 1, 31, 0, 3, 135000, 270000,
+                                  &par->SR11, &par->SR10);
+               /*      par->SR10 = 80; // MCLK == 286000 */
+               /*      par->SR11 = 125; */
+       }
+
+       par->SR12 = (r << 6) | (n & 0x3f);
+       par->SR13 = m & 0xff;
+       par->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2;
+
+       if (var->bits_per_pixel < 24)
+               par->MMPR0 -= 0x8000;
+       else
+               par->MMPR0 -= 0x4000;
+
+       if (timings.interlaced)
+               par->CR42 = 0x20;
+       else
+               par->CR42 = 0x00;
+
+       par->CR34 = 0x10; /* display fifo */
+
+       i = ((((timings.HTotal >> 3) - 5) & 0x100) >> 8) |
+               ((((timings.HDisplay >> 3) - 1) & 0x100) >> 7) |
+               ((((timings.HSyncStart >> 3) - 1) & 0x100) >> 6) |
+               ((timings.HSyncStart & 0x800) >> 7);
+
+       if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 64)
+               i |= 0x08;
+       if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 32)
+               i |= 0x20;
+
+       j = (par->CRTC[0] + ((i & 0x01) << 8) +
+            par->CRTC[4] + ((i & 0x10) << 4) + 1) / 2;
+
+       if (j - (par->CRTC[4] + ((i & 0x10) << 4)) < 4) {
+               if (par->CRTC[4] + ((i & 0x10) << 4) + 4 <=
+                   par->CRTC[0] + ((i & 0x01) << 8))
+                       j = par->CRTC[4] + ((i & 0x10) << 4) + 4;
+               else
+                       j = par->CRTC[0] + ((i & 0x01) << 8) + 1;
+       }
+
+       par->CR3B = j & 0xff;
+       i |= (j & 0x100) >> 2;
+       par->CR3C = (par->CRTC[0] + ((i & 0x01) << 8)) / 2;
+       par->CR5D = i;
+       par->CR5E = (((timings.VTotal - 2) & 0x400) >> 10) |
+               (((timings.VDisplay - 1) & 0x400) >> 9) |
+               (((timings.VSyncStart) & 0x400) >> 8) |
+               (((timings.VSyncStart) & 0x400) >> 6) | 0x40;
+       width = (var->xres_virtual * ((var->bits_per_pixel+7) / 8)) >> 3;
+       par->CR91 = par->CRTC[19] = 0xff & width;
+       par->CR51 = (0x300 & width) >> 4;
+       par->CR90 = 0x80 | (width >> 8);
+       par->MiscOutReg |= 0x0c;
+
+       /* Set frame buffer description. */
+
+       if (var->bits_per_pixel <= 8)
+               par->CR50 = 0;
+       else if (var->bits_per_pixel <= 16)
+               par->CR50 = 0x10;
+       else
+               par->CR50 = 0x30;
+
+       if (var->xres_virtual <= 640)
+               par->CR50 |= 0x40;
+       else if (var->xres_virtual == 800)
+               par->CR50 |= 0x80;
+       else if (var->xres_virtual == 1024)
+               par->CR50 |= 0x00;
+       else if (var->xres_virtual == 1152)
+               par->CR50 |= 0x01;
+       else if (var->xres_virtual == 1280)
+               par->CR50 |= 0xc0;
+       else if (var->xres_virtual == 1600)
+               par->CR50 |= 0x81;
+       else
+               par->CR50 |= 0xc1;      /* Use GBD */
+
+       if( par->chip == S3_SAVAGE2000 )
+               par->CR33 = 0x08;
+       else
+               par->CR33 = 0x20;
+
+       par->CRTC[0x17] = 0xeb;
+
+       par->CR67 |= 1;
+
+       vga_out8(0x3d4, 0x36);
+       par->CR36 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x68);
+       par->CR68 = vga_in8 (0x3d5);
+       par->CR69 = 0;
+       vga_out8 (0x3d4, 0x6f);
+       par->CR6F = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x86);
+       par->CR86 = vga_in8 (0x3d5);
+       vga_out8 (0x3d4, 0x88);
+       par->CR88 = vga_in8 (0x3d5) | 0x08;
+       vga_out8 (0x3d4, 0xb0);
+       par->CRB0 = vga_in8 (0x3d5) | 0x80;
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ *    Set a single color register. Return != 0 for invalid regno.
+ */
+static int savagefb_setcolreg(unsigned        regno,
+                             unsigned        red,
+                             unsigned        green,
+                             unsigned        blue,
+                             unsigned        transp,
+                             struct fb_info *info)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+
+       if (regno >= NR_PALETTE)
+               return -EINVAL;
+
+       par->palette[regno].red    = red;
+       par->palette[regno].green  = green;
+       par->palette[regno].blue   = blue;
+       par->palette[regno].transp = transp;
+
+       switch (info->var.bits_per_pixel) {
+       case 8:
+               vga_out8 (0x3c8, regno);
+
+               vga_out8 (0x3c9, red   >> 10);
+               vga_out8 (0x3c9, green >> 10);
+               vga_out8 (0x3c9, blue  >> 10);
+               break;
+
+       case 16:
+               if (regno < 16)
+                       ((u32 *)info->pseudo_palette)[regno] =
+                               ((red   & 0xf800)      ) |
+                               ((green & 0xfc00) >>  5) |
+                               ((blue  & 0xf800) >> 11);
+               break;
+
+       case 24:
+               if (regno < 16)
+                       ((u32 *)info->pseudo_palette)[regno] =
+                               ((red    & 0xff00) <<  8) |
+                               ((green  & 0xff00)      ) |
+                               ((blue   & 0xff00) >>  8);
+               break;
+       case 32:
+               if (regno < 16)
+                       ((u32 *)info->pseudo_palette)[regno] =
+                               ((transp & 0xff00) << 16) |
+                               ((red    & 0xff00) <<  8) |
+                               ((green  & 0xff00)      ) |
+                               ((blue   & 0xff00) >>  8);
+               break;
+
+       default:
+               return 1;
+       }
+
+       return 0;
+}
+
+static void savagefb_set_par_int (struct savagefb_par  *par)
+{
+       unsigned char tmp, cr3a, cr66, cr67;
+
+       DBG ("savagefb_set_par_int");
+
+       par->SavageWaitIdle (par);
+
+       vga_out8 (0x3c2, 0x23);
+
+       vga_out16 (0x3d4, 0x4838);
+       vga_out16 (0x3d4, 0xa539);
+       vga_out16 (0x3c4, 0x0608);
+
+       vgaHWProtect (par, 1);
+
+       /*
+        * Some Savage/MX and /IX systems go nuts when trying to exit the
+        * server after WindowMaker has displayed a gradient background.  I
+        * haven't been able to find what causes it, but a non-destructive
+        * switch to mode 3 here seems to eliminate the issue.
+        */
+
+       VerticalRetraceWait();
+       vga_out8 (0x3d4, 0x67);
+       cr67 = vga_in8 (0x3d5);
+       vga_out8 (0x3d5, cr67/*par->CR67*/ & ~0x0c); /* no STREAMS yet */
+
+       vga_out8 (0x3d4, 0x23);
+       vga_out8 (0x3d5, 0x00);
+       vga_out8 (0x3d4, 0x26);
+       vga_out8 (0x3d5, 0x00);
+
+       /* restore extended regs */
+       vga_out8 (0x3d4, 0x66);
+       vga_out8 (0x3d5, par->CR66);
+       vga_out8 (0x3d4, 0x3a);
+       vga_out8 (0x3d5, par->CR3A);
+       vga_out8 (0x3d4, 0x31);
+       vga_out8 (0x3d5, par->CR31);
+       vga_out8 (0x3d4, 0x32);
+       vga_out8 (0x3d5, par->CR32);
+       vga_out8 (0x3d4, 0x58);
+       vga_out8 (0x3d5, par->CR58);
+       vga_out8 (0x3d4, 0x53);
+       vga_out8 (0x3d5, par->CR53 & 0x7f);
+
+       vga_out16 (0x3c4, 0x0608);
+
+       /* Restore DCLK registers. */
+
+       vga_out8 (0x3c4, 0x0e);
+       vga_out8 (0x3c5, par->SR0E);
+       vga_out8 (0x3c4, 0x0f);
+       vga_out8 (0x3c5, par->SR0F);
+       vga_out8 (0x3c4, 0x29);
+       vga_out8 (0x3c5, par->SR29);
+       vga_out8 (0x3c4, 0x15);
+       vga_out8 (0x3c5, par->SR15);
+
+       /* Restore flat panel expansion regsters. */
+       if( par->chip == S3_SAVAGE_MX ) {
+               int i;
+
+               for( i = 0; i < 8; i++ ) {
+                       vga_out8 (0x3c4, 0x54+i);
+                       vga_out8 (0x3c5, par->SR54[i]);
+               }
+       }
+
+       vgaHWRestore (par);
+
+       /* extended mode timing registers */
+       vga_out8 (0x3d4, 0x53);
+       vga_out8 (0x3d5, par->CR53);
+       vga_out8 (0x3d4, 0x5d);
+       vga_out8 (0x3d5, par->CR5D);
+       vga_out8 (0x3d4, 0x5e);
+       vga_out8 (0x3d5, par->CR5E);
+       vga_out8 (0x3d4, 0x3b);
+       vga_out8 (0x3d5, par->CR3B);
+       vga_out8 (0x3d4, 0x3c);
+       vga_out8 (0x3d5, par->CR3C);
+       vga_out8 (0x3d4, 0x43);
+       vga_out8 (0x3d5, par->CR43);
+       vga_out8 (0x3d4, 0x65);
+       vga_out8 (0x3d5, par->CR65);
+
+       /* restore the desired video mode with cr67 */
+       vga_out8 (0x3d4, 0x67);
+       /* following part not present in X11 driver */
+       cr67 = vga_in8 (0x3d5) & 0xf;
+       vga_out8 (0x3d5, 0x50 | cr67);
+       udelay (10000);
+       vga_out8 (0x3d4, 0x67);
+       /* end of part */
+       vga_out8 (0x3d5, par->CR67 & ~0x0c);
+
+       /* other mode timing and extended regs */
+       vga_out8 (0x3d4, 0x34);
+       vga_out8 (0x3d5, par->CR34);
+       vga_out8 (0x3d4, 0x40);
+       vga_out8 (0x3d5, par->CR40);
+       vga_out8 (0x3d4, 0x42);
+       vga_out8 (0x3d5, par->CR42);
+       vga_out8 (0x3d4, 0x45);
+       vga_out8 (0x3d5, par->CR45);
+       vga_out8 (0x3d4, 0x50);
+       vga_out8 (0x3d5, par->CR50);
+       vga_out8 (0x3d4, 0x51);
+       vga_out8 (0x3d5, par->CR51);
+
+       /* memory timings */
+       vga_out8 (0x3d4, 0x36);
+       vga_out8 (0x3d5, par->CR36);
+       vga_out8 (0x3d4, 0x60);
+       vga_out8 (0x3d5, par->CR60);
+       vga_out8 (0x3d4, 0x68);
+       vga_out8 (0x3d5, par->CR68);
+       vga_out8 (0x3d4, 0x69);
+       vga_out8 (0x3d5, par->CR69);
+       vga_out8 (0x3d4, 0x6f);
+       vga_out8 (0x3d5, par->CR6F);
+
+       vga_out8 (0x3d4, 0x33);
+       vga_out8 (0x3d5, par->CR33);
+       vga_out8 (0x3d4, 0x86);
+       vga_out8 (0x3d5, par->CR86);
+       vga_out8 (0x3d4, 0x88);
+       vga_out8 (0x3d5, par->CR88);
+       vga_out8 (0x3d4, 0x90);
+       vga_out8 (0x3d5, par->CR90);
+       vga_out8 (0x3d4, 0x91);
+       vga_out8 (0x3d5, par->CR91);
+
+       if (par->chip == S3_SAVAGE4) {
+               vga_out8 (0x3d4, 0xb0);
+               vga_out8 (0x3d5, par->CRB0);
+       }
+
+       vga_out8 (0x3d4, 0x32);
+       vga_out8 (0x3d5, par->CR32);
+
+       /* unlock extended seq regs */
+       vga_out8 (0x3c4, 0x08);
+       vga_out8 (0x3c5, 0x06);
+
+       /* Restore extended sequencer regs for MCLK. SR10 == 255 indicates
+        * that we should leave the default SR10 and SR11 values there.
+        */
+       if (par->SR10 != 255) {
+               vga_out8 (0x3c4, 0x10);
+               vga_out8 (0x3c5, par->SR10);
+               vga_out8 (0x3c4, 0x11);
+               vga_out8 (0x3c5, par->SR11);
+       }
+
+       /* restore extended seq regs for dclk */
+       vga_out8 (0x3c4, 0x0e);
+       vga_out8 (0x3c5, par->SR0E);
+       vga_out8 (0x3c4, 0x0f);
+       vga_out8 (0x3c5, par->SR0F);
+       vga_out8 (0x3c4, 0x12);
+       vga_out8 (0x3c5, par->SR12);
+       vga_out8 (0x3c4, 0x13);
+       vga_out8 (0x3c5, par->SR13);
+       vga_out8 (0x3c4, 0x29);
+       vga_out8 (0x3c5, par->SR29);
+
+       vga_out8 (0x3c4, 0x18);
+       vga_out8 (0x3c5, par->SR18);
+
+       /* load new m, n pll values for dclk & mclk */
+       vga_out8 (0x3c4, 0x15);
+       tmp = vga_in8 (0x3c5) & ~0x21;
+
+       vga_out8 (0x3c5, tmp | 0x03);
+       vga_out8 (0x3c5, tmp | 0x23);
+       vga_out8 (0x3c5, tmp | 0x03);
+       vga_out8 (0x3c5, par->SR15);
+       udelay (100);
+
+       vga_out8 (0x3c4, 0x30);
+       vga_out8 (0x3c5, par->SR30);
+       vga_out8 (0x3c4, 0x08);
+       vga_out8 (0x3c5, par->SR08);
+
+       /* now write out cr67 in full, possibly starting STREAMS */
+       VerticalRetraceWait();
+       vga_out8 (0x3d4, 0x67);
+       vga_out8 (0x3d5, par->CR67);
+
+       vga_out8 (0x3d4, 0x66);
+       cr66 = vga_in8 (0x3d5);
+       vga_out8 (0x3d5, cr66 | 0x80);
+       vga_out8 (0x3d4, 0x3a);
+       cr3a = vga_in8 (0x3d5);
+       vga_out8 (0x3d5, cr3a | 0x80);
+
+       if (par->chip != S3_SAVAGE_MX) {
+               VerticalRetraceWait();
+               savage_out32 (FIFO_CONTROL_REG, par->MMPR0);
+               par->SavageWaitIdle (par);
+               savage_out32 (MIU_CONTROL_REG, par->MMPR1);
+               par->SavageWaitIdle (par);
+               savage_out32 (STREAMS_TIMEOUT_REG, par->MMPR2);
+               par->SavageWaitIdle (par);
+               savage_out32 (MISC_TIMEOUT_REG, par->MMPR3);
+       }
+
+       vga_out8 (0x3d4, 0x66);
+       vga_out8 (0x3d5, cr66);
+       vga_out8 (0x3d4, 0x3a);
+       vga_out8 (0x3d5, cr3a);
+
+       SavageSetup2DEngine (par);
+       vgaHWProtect (par, 0);
+}
+
+static void savagefb_update_start (struct savagefb_par      *par,
+                                  struct fb_var_screeninfo *var)
+{
+       int base;
+
+       base = ((var->yoffset * var->xres_virtual + (var->xoffset & ~1))
+               * ((var->bits_per_pixel+7) / 8)) >> 2;
+
+       /* now program the start address registers */
+       vga_out16(0x3d4, (base & 0x00ff00) | 0x0c);
+       vga_out16(0x3d4, ((base & 0x00ff) << 8) | 0x0d);
+       vga_out8 (0x3d4, 0x69);
+       vga_out8 (0x3d5, (base & 0x7f0000) >> 16);
+}
+
+
+static void savagefb_set_fix(struct fb_info *info)
+{
+       info->fix.line_length = info->var.xres_virtual *
+               info->var.bits_per_pixel / 8;
+
+       if (info->var.bits_per_pixel == 8)
+               info->fix.visual      = FB_VISUAL_PSEUDOCOLOR;
+       else
+               info->fix.visual      = FB_VISUAL_TRUECOLOR;
+}
+
+#if defined(CONFIG_FB_SAVAGE_ACCEL)
+static void savagefb_set_clip(struct fb_info *info)
+{
+    struct savagefb_par *par = (struct savagefb_par *)info->par;
+    int cmd;
+
+    cmd = BCI_CMD_NOP | BCI_CMD_CLIP_NEW;
+    par->bci_ptr = 0;
+    par->SavageWaitFifo(par,3);
+    BCI_SEND(cmd);
+    BCI_SEND(BCI_CLIP_TL(0, 0));
+    BCI_SEND(BCI_CLIP_BR(0xfff, 0xfff));
+}
+#endif
+
+static int savagefb_set_par (struct fb_info *info)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       struct fb_var_screeninfo *var = &info->var;
+       int err;
+
+       DBG("savagefb_set_par");
+       err = savagefb_decode_var (var, par);
+       if (err)
+               return err;
+
+       if (par->dacSpeedBpp <= 0) {
+               if (var->bits_per_pixel > 24)
+                       par->dacSpeedBpp = par->clock[3];
+               else if (var->bits_per_pixel >= 24)
+                       par->dacSpeedBpp = par->clock[2];
+               else if ((var->bits_per_pixel > 8) && (var->bits_per_pixel < 24))
+                       par->dacSpeedBpp = par->clock[1];
+               else if (var->bits_per_pixel <= 8)
+                       par->dacSpeedBpp = par->clock[0];
+       }
+
+       /* Set ramdac limits */
+       par->maxClock = par->dacSpeedBpp;
+       par->minClock = 10000;
+
+       savagefb_set_par_int (par);
+       savagefb_update_start (par, var);
+       fb_set_cmap (&info->cmap, info);
+       savagefb_set_fix(info);
+       savagefb_set_clip(info);
+
+       SavagePrintRegs();
+       return 0;
+}
+
+/*
+ *    Pan or Wrap the Display
+ */
+static int savagefb_pan_display (struct fb_var_screeninfo *var,
+                                struct fb_info           *info)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       u_int y_bottom;
+
+       y_bottom = var->yoffset;
+
+       if (!(var->vmode & FB_VMODE_YWRAP))
+               y_bottom += var->yres;
+
+       if (var->xoffset > (var->xres_virtual - var->xres))
+               return -EINVAL;
+       if (y_bottom > info->var.yres_virtual)
+               return -EINVAL;
+
+       savagefb_update_start (par, var);
+
+       info->var.xoffset = var->xoffset;
+       info->var.yoffset = var->yoffset;
+
+       if (var->vmode & FB_VMODE_YWRAP)
+               info->var.vmode |= FB_VMODE_YWRAP;
+       else
+               info->var.vmode &= ~FB_VMODE_YWRAP;
+
+       return 0;
+}
+
+
+static struct fb_ops savagefb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_check_var   = savagefb_check_var,
+       .fb_set_par     = savagefb_set_par,
+       .fb_setcolreg   = savagefb_setcolreg,
+       .fb_pan_display = savagefb_pan_display,
+#if defined(CONFIG_FB_SAVAGE_ACCEL)
+       .fb_fillrect    = savagefb_fillrect,
+       .fb_copyarea    = savagefb_copyarea,
+       .fb_imageblit   = savagefb_imageblit,
+       .fb_sync        = savagefb_sync,
+#else
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+#endif
+       .fb_cursor      = soft_cursor,
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct fb_var_screeninfo __devinitdata savagefb_var800x600x8 = {
+       .accel_flags =  FB_ACCELF_TEXT,
+       .xres =         800,
+       .yres =         600,
+       .xres_virtual =  800,
+       .yres_virtual =  600,
+       .bits_per_pixel = 8,
+       .pixclock =     25000,
+       .left_margin =  88,
+       .right_margin = 40,
+       .upper_margin = 23,
+       .lower_margin = 1,
+       .hsync_len =    128,
+       .vsync_len =    4,
+       .sync =         FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+       .vmode =        FB_VMODE_NONINTERLACED
+};
+
+static void savage_enable_mmio (struct savagefb_par *par)
+{
+       unsigned char val;
+
+       DBG ("savage_enable_mmio\n");
+
+       val = vga_in8 (0x3c3);
+       vga_out8 (0x3c3, val | 0x01);
+       val = vga_in8 (0x3cc);
+       vga_out8 (0x3c2, val | 0x01);
+
+       if (par->chip >= S3_SAVAGE4) {
+               vga_out8 (0x3d4, 0x40);
+               val = vga_in8 (0x3d5);
+               vga_out8 (0x3d5, val | 1);
+       }
+}
+
+
+void savage_disable_mmio (struct savagefb_par *par)
+{
+       unsigned char val;
+
+       DBG ("savage_disable_mmio\n");
+
+       if(par->chip >= S3_SAVAGE4 ) {
+               vga_out8 (0x3d4, 0x40);
+               val = vga_in8 (0x3d5);
+               vga_out8 (0x3d5, val | 1);
+       }
+}
+
+
+static int __devinit savage_map_mmio (struct fb_info *info)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       DBG ("savage_map_mmio");
+
+       if (S3_SAVAGE3D_SERIES (par->chip))
+               par->mmio.pbase = pci_resource_start (par->pcidev, 0) +
+                       SAVAGE_NEWMMIO_REGBASE_S3;
+       else
+               par->mmio.pbase = pci_resource_start (par->pcidev, 0) +
+                       SAVAGE_NEWMMIO_REGBASE_S4;
+
+       par->mmio.len = SAVAGE_NEWMMIO_REGSIZE;
+
+       par->mmio.vbase = ioremap (par->mmio.pbase, par->mmio.len);
+       if (!par->mmio.vbase) {
+               printk ("savagefb: unable to map memory mapped IO\n");
+               return -ENOMEM;
+       } else
+               printk (KERN_INFO "savagefb: mapped io at %p\n",
+                       par->mmio.vbase);
+
+       info->fix.mmio_start = par->mmio.pbase;
+       info->fix.mmio_len   = par->mmio.len;
+
+       par->bci_base = (u32 __iomem *)(par->mmio.vbase + BCI_BUFFER_OFFSET);
+       par->bci_ptr  = 0;
+
+       savage_enable_mmio (par);
+
+       return 0;
+}
+
+static void __devinit savage_unmap_mmio (struct fb_info *info)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       DBG ("savage_unmap_mmio");
+
+       savage_disable_mmio(par);
+
+       if (par->mmio.vbase) {
+               iounmap ((void __iomem *)par->mmio.vbase);
+               par->mmio.vbase = NULL;
+       }
+}
+
+static int __devinit savage_map_video (struct fb_info *info,
+                                      int video_len)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       int resource;
+
+       DBG("savage_map_video");
+
+       if (S3_SAVAGE3D_SERIES (par->chip))
+               resource = 0;
+       else
+               resource = 1;
+
+       par->video.pbase = pci_resource_start (par->pcidev, resource);
+       par->video.len   = video_len;
+       par->video.vbase = ioremap (par->video.pbase, par->video.len);
+
+       if (!par->video.vbase) {
+               printk ("savagefb: unable to map screen memory\n");
+               return -ENOMEM;
+       } else
+               printk (KERN_INFO "savagefb: mapped framebuffer at %p, "
+                       "pbase == %x\n", par->video.vbase, par->video.pbase);
+
+       info->fix.smem_start = par->video.pbase;
+       info->fix.smem_len   = par->video.len - par->cob_size;
+       info->screen_base    = par->video.vbase;
+
+#ifdef CONFIG_MTRR
+       par->video.mtrr = mtrr_add (par->video.pbase, video_len,
+                                    MTRR_TYPE_WRCOMB, 1);
+#endif
+       return 0;
+}
+
+static void __devinit savage_unmap_video (struct fb_info *info)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+
+       DBG("savage_unmap_video");
+
+       if (par->video.vbase) {
+#ifdef CONFIG_MTRR
+               mtrr_del (par->video.mtrr, par->video.pbase, par->video.len);
+#endif
+
+               iounmap (par->video.vbase);
+               par->video.vbase = NULL;
+               info->screen_base = NULL;
+       }
+}
+
+static int __devinit savage_init_hw (struct savagefb_par *par)
+{
+       unsigned char config1, m, n, n1, n2, sr8, cr3f, cr66 = 0, tmp;
+
+       static unsigned char RamSavage3D[] = { 8, 4, 4, 2 };
+       static unsigned char RamSavage4[] =  { 2, 4, 8, 12, 16, 32, 64, 32 };
+       static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 };
+       static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 2, 2 };
+
+       int videoRam, videoRambytes;
+
+       DBG("savage_init_hw");
+
+       /* unprotect CRTC[0-7] */
+       vga_out8(0x3d4, 0x11);
+       tmp = vga_in8(0x3d5);
+       vga_out8(0x3d5, tmp & 0x7f);
+
+       /* unlock extended regs */
+       vga_out16(0x3d4, 0x4838);
+       vga_out16(0x3d4, 0xa039);
+       vga_out16(0x3c4, 0x0608);
+
+       vga_out8(0x3d4, 0x40);
+       tmp = vga_in8(0x3d5);
+       vga_out8(0x3d5, tmp & ~0x01);
+
+       /* unlock sys regs */
+       vga_out8(0x3d4, 0x38);
+       vga_out8(0x3d5, 0x48);
+
+       /* Unlock system registers. */
+       vga_out16(0x3d4, 0x4838);
+
+       /* Next go on to detect amount of installed ram */
+
+       vga_out8(0x3d4, 0x36);            /* for register CR36 (CONFG_REG1), */
+       config1 = vga_in8(0x3d5);           /* get amount of vram installed */
+
+       /* Compute the amount of video memory and offscreen memory. */
+
+       switch  (par->chip) {
+       case S3_SAVAGE3D:
+               videoRam = RamSavage3D[ (config1 & 0xC0) >> 6 ] * 1024;
+               break;
+
+       case S3_SAVAGE4:
+               /*
+                * The Savage4 has one ugly special case to consider.  On
+                * systems with 4 banks of 2Mx32 SDRAM, the BIOS says 4MB
+                * when it really means 8MB.  Why do it the same when you
+                * can do it different...
+                */
+               vga_out8(0x3d4, 0x68);  /* memory control 1 */
+               if( (vga_in8(0x3d5) & 0xC0) == (0x01 << 6) )
+                       RamSavage4[1] = 8;
+
+               /*FALLTHROUGH*/
+
+       case S3_SAVAGE2000:
+               videoRam = RamSavage4[ (config1 & 0xE0) >> 5 ] * 1024;
+               break;
+
+       case S3_SAVAGE_MX:
+       case S3_SUPERSAVAGE:
+               videoRam = RamSavageMX[ (config1 & 0x0E) >> 1 ] * 1024;
+               break;
+
+       case S3_PROSAVAGE:
+               videoRam = RamSavageNB[ (config1 & 0xE0) >> 5 ] * 1024;
+               break;
+
+       default:
+               /* How did we get here? */
+               videoRam = 0;
+               break;
+       }
+
+       videoRambytes = videoRam * 1024;
+
+       printk (KERN_INFO "savagefb: probed videoram:  %dk\n", videoRam);
+
+       /* reset graphics engine to avoid memory corruption */
+       vga_out8 (0x3d4, 0x66);
+       cr66 = vga_in8 (0x3d5);
+       vga_out8 (0x3d5, cr66 | 0x02);
+       udelay (10000);
+
+       vga_out8 (0x3d4, 0x66);
+       vga_out8 (0x3d5, cr66 & ~0x02); /* clear reset flag */
+       udelay (10000);
+
+
+       /*
+        * reset memory interface, 3D engine, AGP master, PCI master,
+        * master engine unit, motion compensation/LPB
+        */
+       vga_out8 (0x3d4, 0x3f);
+       cr3f = vga_in8 (0x3d5);
+       vga_out8 (0x3d5, cr3f | 0x08);
+       udelay (10000);
+
+       vga_out8 (0x3d4, 0x3f);
+       vga_out8 (0x3d5, cr3f & ~0x08); /* clear reset flags */
+       udelay (10000);
+
+       /* Savage ramdac speeds */
+       par->numClocks = 4;
+       par->clock[0] = 250000;
+       par->clock[1] = 250000;
+       par->clock[2] = 220000;
+       par->clock[3] = 220000;
+
+       /* detect current mclk */
+       vga_out8(0x3c4, 0x08);
+       sr8 = vga_in8(0x3c5);
+       vga_out8(0x3c5, 0x06);
+       vga_out8(0x3c4, 0x10);
+       n = vga_in8(0x3c5);
+       vga_out8(0x3c4, 0x11);
+       m = vga_in8(0x3c5);
+       vga_out8(0x3c4, 0x08);
+       vga_out8(0x3c5, sr8);
+       m &= 0x7f;
+       n1 = n & 0x1f;
+       n2 = (n >> 5) & 0x03;
+       par->MCLK = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;
+       printk (KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n",
+               par->MCLK);
+
+       /* Check LCD panel parrmation */
+
+       if (par->chip == S3_SAVAGE_MX) {
+               unsigned char cr6b = VGArCR( 0x6b );
+
+               int panelX = (VGArSEQ (0x61) +
+                             ((VGArSEQ (0x66) & 0x02) << 7) + 1) * 8;
+               int panelY = (VGArSEQ (0x69) +
+                             ((VGArSEQ (0x6e) & 0x70) << 4) + 1);
+
+               char * sTechnology = "Unknown";
+
+               /* OK, I admit it.  I don't know how to limit the max dot clock
+                * for LCD panels of various sizes.  I thought I copied the
+                * formula from the BIOS, but many users have parrmed me of
+                * my folly.
+                *
+                * Instead, I'll abandon any attempt to automatically limit the
+                * clock, and add an LCDClock option to XF86Config.  Some day,
+                * I should come back to this.
+                */
+
+               enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */
+                       ActiveCRT = 0x01,
+                       ActiveLCD = 0x02,
+                       ActiveTV = 0x04,
+                       ActiveCRT2 = 0x20,
+                       ActiveDUO = 0x80
+               };
+
+               if ((VGArSEQ (0x39) & 0x03) == 0) {
+                       sTechnology = "TFT";
+               } else if ((VGArSEQ (0x30) & 0x01) == 0) {
+                       sTechnology = "DSTN";
+               } else  {
+                       sTechnology = "STN";
+               }
+
+               printk (KERN_INFO "savagefb: %dx%d %s LCD panel detected %s\n",
+                       panelX, panelY, sTechnology,
+                       cr6b & ActiveLCD ? "and active" : "but not active");
+
+               if( cr6b & ActiveLCD )  {
+                       /*
+                        * If the LCD is active and panel expansion is enabled,
+                        * we probably want to kill the HW cursor.
+                        */
+
+                       printk (KERN_INFO "savagefb: Limiting video mode to "
+                               "%dx%d\n", panelX, panelY );
+
+                       par->SavagePanelWidth = panelX;
+                       par->SavagePanelHeight = panelY;
+
+               }
+       }
+
+       savage_get_default_par (par);
+
+       if( S3_SAVAGE4_SERIES(par->chip) ) {
+               /*
+                * The Savage4 and ProSavage have COB coherency bugs which
+                * render the buffer useless.  We disable it.
+                */
+               par->cob_index = 2;
+               par->cob_size = 0x8000 << par->cob_index;
+               par->cob_offset = videoRambytes;
+       } else {
+               /* We use 128kB for the COB on all chips. */
+
+               par->cob_index  = 7;
+               par->cob_size   = 0x400 << par->cob_index;
+               par->cob_offset = videoRambytes - par->cob_size;
+       }
+
+       return videoRambytes;
+}
+
+static int __devinit savage_init_fb_info (struct fb_info *info,
+                                         struct pci_dev *dev,
+                                         const struct pci_device_id *id)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       int err = 0;
+
+       par->pcidev  = dev;
+
+       info->fix.type     = FB_TYPE_PACKED_PIXELS;
+       info->fix.type_aux         = 0;
+       info->fix.xpanstep         = 2;
+       info->fix.ypanstep         = 1;
+       info->fix.ywrapstep   = 0;
+       info->fix.accel       = id->driver_data;
+
+       switch (info->fix.accel) {
+       case FB_ACCEL_SUPERSAVAGE:
+               par->chip = S3_SUPERSAVAGE;
+               snprintf (info->fix.id, 16, "SuperSavage");
+               break;
+       case FB_ACCEL_SAVAGE4:
+               par->chip = S3_SAVAGE4;
+               snprintf (info->fix.id, 16, "Savage4");
+               break;
+       case FB_ACCEL_SAVAGE3D:
+               par->chip = S3_SAVAGE3D;
+               snprintf (info->fix.id, 16, "Savage3D");
+               break;
+       case FB_ACCEL_SAVAGE3D_MV:
+               par->chip = S3_SAVAGE3D;
+               snprintf (info->fix.id, 16, "Savage3D-MV");
+               break;
+       case FB_ACCEL_SAVAGE2000:
+               par->chip = S3_SAVAGE2000;
+               snprintf (info->fix.id, 16, "Savage2000");
+               break;
+       case FB_ACCEL_SAVAGE_MX_MV:
+               par->chip = S3_SAVAGE_MX;
+               snprintf (info->fix.id, 16, "Savage/MX-MV");
+               break;
+       case FB_ACCEL_SAVAGE_MX:
+               par->chip = S3_SAVAGE_MX;
+               snprintf (info->fix.id, 16, "Savage/MX");
+               break;
+       case FB_ACCEL_SAVAGE_IX_MV:
+               par->chip = S3_SAVAGE_MX;
+               snprintf (info->fix.id, 16, "Savage/IX-MV");
+               break;
+       case FB_ACCEL_SAVAGE_IX:
+               par->chip = S3_SAVAGE_MX;
+               snprintf (info->fix.id, 16, "Savage/IX");
+               break;
+       case FB_ACCEL_PROSAVAGE_PM:
+               par->chip = S3_PROSAVAGE;
+               snprintf (info->fix.id, 16, "ProSavagePM");
+               break;
+       case FB_ACCEL_PROSAVAGE_KM:
+               par->chip = S3_PROSAVAGE;
+               snprintf (info->fix.id, 16, "ProSavageKM");
+               break;
+       case FB_ACCEL_S3TWISTER_P:
+               par->chip = S3_PROSAVAGE;
+               snprintf (info->fix.id, 16, "TwisterP");
+               break;
+       case FB_ACCEL_S3TWISTER_K:
+               par->chip = S3_PROSAVAGE;
+               snprintf (info->fix.id, 16, "TwisterK");
+               break;
+       case FB_ACCEL_PROSAVAGE_DDR:
+               par->chip = S3_PROSAVAGE;
+               snprintf (info->fix.id, 16, "ProSavageDDR");
+               break;
+       case FB_ACCEL_PROSAVAGE_DDRK:
+               par->chip = S3_PROSAVAGE;
+               snprintf (info->fix.id, 16, "ProSavage8");
+               break;
+       }
+
+       if (S3_SAVAGE3D_SERIES(par->chip)) {
+               par->SavageWaitIdle = savage3D_waitidle;
+               par->SavageWaitFifo = savage3D_waitfifo;
+       } else if (S3_SAVAGE4_SERIES(par->chip) ||
+                  S3_SUPERSAVAGE == par->chip) {
+               par->SavageWaitIdle = savage4_waitidle;
+               par->SavageWaitFifo = savage4_waitfifo;
+       } else {
+               par->SavageWaitIdle = savage2000_waitidle;
+               par->SavageWaitFifo = savage2000_waitfifo;
+       }
+
+       info->var.nonstd      = 0;
+       info->var.activate    = FB_ACTIVATE_NOW;
+       info->var.width       = -1;
+       info->var.height      = -1;
+       info->var.accel_flags = 0;
+
+       info->fbops          = &savagefb_ops;
+       info->flags          = FBINFO_DEFAULT |
+                              FBINFO_HWACCEL_YPAN |
+                              FBINFO_HWACCEL_XPAN |
+                              FBINFO_MISC_MODESWITCHLATE;
+
+       info->pseudo_palette = par->pseudo_palette;
+
+#if defined(CONFIG_FB_SAVAGE_ACCEL)
+       /* FIFO size + padding for commands */
+       info->pixmap.addr = kmalloc(8*1024, GFP_KERNEL);
+
+       err = -ENOMEM;
+       if (info->pixmap.addr) {
+               memset(info->pixmap.addr, 0, 8*1024);
+               info->pixmap.size = 8*1024;
+               info->pixmap.scan_align = 4;
+               info->pixmap.buf_align = 4;
+               info->pixmap.access_align = 4;
+
+               fb_alloc_cmap (&info->cmap, NR_PALETTE, 0);
+               info->flags |= FBINFO_HWACCEL_COPYAREA |
+                              FBINFO_HWACCEL_FILLRECT |
+                              FBINFO_HWACCEL_IMAGEBLIT;
+
+               err = 0;
+       }
+#endif
+       return err;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int __devinit savagefb_probe (struct pci_dev* dev,
+                                    const struct pci_device_id* id)
+{
+       struct fb_info *info;
+       struct savagefb_par *par;
+       u_int h_sync, v_sync;
+       int err, lpitch;
+       int video_len;
+
+       DBG("savagefb_probe");
+       SavagePrintRegs();
+
+       info = framebuffer_alloc(sizeof(struct savagefb_par), &dev->dev);
+       if (!info)
+               return -ENOMEM;
+       par = info->par;
+       err = pci_enable_device(dev);
+       if (err)
+               goto failed_enable;
+
+       if (pci_request_regions(dev, "savagefb")) {
+               printk(KERN_ERR "cannot request PCI regions\n");
+               goto failed_enable;
+       }
+
+       err = -ENOMEM;
+
+       if (savage_init_fb_info(info, dev, id))
+               goto failed_init;
+
+       err = savage_map_mmio(info);
+       if (err)
+               goto failed_mmio;
+
+       video_len = savage_init_hw(par);
+       if (video_len < 0) {
+               err = video_len;
+               goto failed_mmio;
+       }
+
+       err = savage_map_video(info, video_len);
+       if (err)
+               goto failed_video;
+
+       INIT_LIST_HEAD(&info->modelist);
+#if defined(CONFIG_FB_SAVAGE_I2C)
+       savagefb_create_i2c_busses(info);
+       savagefb_probe_i2c_connector(par, &par->edid);
+       fb_edid_to_monspecs(par->edid, &info->monspecs);
+       fb_videomode_to_modelist(info->monspecs.modedb,
+                                info->monspecs.modedb_len,
+                                &info->modelist);
+#endif
+       info->var = savagefb_var800x600x8;
+
+       if (mode_option) {
+               fb_find_mode(&info->var, info, mode_option,
+                            info->monspecs.modedb, info->monspecs.modedb_len,
+                            NULL, 8);
+       } else if (info->monspecs.modedb != NULL) {
+               struct fb_monspecs *specs = &info->monspecs;
+               struct fb_videomode modedb;
+
+               if (info->monspecs.misc & FB_MISC_1ST_DETAIL) {
+                       int i;
+
+                       for (i = 0; i < specs->modedb_len; i++) {
+                               if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
+                                       modedb = specs->modedb[i];
+                                       break;
+                               }
+                       }
+               } else {
+                       /* otherwise, get first mode in database */
+                       modedb = specs->modedb[0];
+               }
+
+               savage_update_var(&info->var, &modedb);
+       }
+
+       /* maximize virtual vertical length */
+       lpitch = info->var.xres_virtual*((info->var.bits_per_pixel + 7) >> 3);
+       info->var.yres_virtual = info->fix.smem_len/lpitch;
+
+       if (info->var.yres_virtual < info->var.yres)
+               goto failed;
+
+#if defined(CONFIG_FB_SAVAGE_ACCEL)
+       /*
+        * The clipping coordinates are masked with 0xFFF, so limit our
+        * virtual resolutions to these sizes.
+        */
+       if (info->var.yres_virtual > 0x1000)
+               info->var.yres_virtual = 0x1000;
+
+       if (info->var.xres_virtual > 0x1000)
+               info->var.xres_virtual = 0x1000;
+#endif
+       savagefb_check_var(&info->var, info);
+       savagefb_set_fix(info);
+
+       /*
+        * Calculate the hsync and vsync frequencies.  Note that
+        * we split the 1e12 constant up so that we can preserve
+        * the precision and fit the results into 32-bit registers.
+        *  (1953125000 * 512 = 1e12)
+        */
+       h_sync = 1953125000 / info->var.pixclock;
+       h_sync = h_sync * 512 / (info->var.xres + info->var.left_margin +
+                                info->var.right_margin +
+                                info->var.hsync_len);
+       v_sync = h_sync / (info->var.yres + info->var.upper_margin +
+                          info->var.lower_margin + info->var.vsync_len);
+
+       printk(KERN_INFO "savagefb v" SAVAGEFB_VERSION ": "
+              "%dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
+              info->fix.smem_len >> 10,
+              info->var.xres, info->var.yres,
+              h_sync / 1000, h_sync % 1000, v_sync);
+
+
+       fb_destroy_modedb(info->monspecs.modedb);
+       info->monspecs.modedb = NULL;
+
+       err = register_framebuffer (info);
+       if (err < 0)
+               goto failed;
+
+       printk (KERN_INFO "fb: S3 %s frame buffer device\n",
+               info->fix.id);
+
+       /*
+        * Our driver data
+        */
+       pci_set_drvdata(dev, info);
+
+       return 0;
+
+ failed:
+#ifdef CONFIG_FB_SAVAGE_I2C
+       savagefb_delete_i2c_busses(info);
+#endif
+       fb_alloc_cmap (&info->cmap, 0, 0);
+       savage_unmap_video(info);
+ failed_video:
+       savage_unmap_mmio (info);
+ failed_mmio:
+       kfree(info->pixmap.addr);
+ failed_init:
+       pci_release_regions(dev);
+ failed_enable:
+       framebuffer_release(info);
+
+       return err;
+}
+
+static void __devexit savagefb_remove (struct pci_dev *dev)
+{
+       struct fb_info *info =
+               (struct fb_info *)pci_get_drvdata(dev);
+
+       DBG("savagefb_remove");
+
+       if (info) {
+               /*
+                * If unregister_framebuffer fails, then
+                * we will be leaving hooks that could cause
+                * oopsen laying around.
+                */
+               if (unregister_framebuffer (info))
+                       printk (KERN_WARNING "savagefb: danger danger! "
+                               "Oopsen imminent!\n");
+
+#ifdef CONFIG_FB_SAVAGE_I2C
+               savagefb_delete_i2c_busses(info);
+#endif
+               fb_alloc_cmap (&info->cmap, 0, 0);
+               savage_unmap_video (info);
+               savage_unmap_mmio (info);
+               kfree(info->pixmap.addr);
+               pci_release_regions(dev);
+               framebuffer_release(info);
+
+               /*
+                * Ensure that the driver data is no longer
+                * valid.
+                */
+               pci_set_drvdata(dev, NULL);
+       }
+}
+
+static int savagefb_suspend (struct pci_dev* dev, u32 state)
+{
+       struct fb_info *info =
+               (struct fb_info *)pci_get_drvdata(dev);
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+
+       DBG("savagefb_suspend");
+       printk(KERN_DEBUG "state: %u\n", state);
+
+       acquire_console_sem();
+       fb_set_suspend(info, state);
+       savage_disable_mmio(par);
+       release_console_sem();
+
+       pci_disable_device(dev);
+       pci_set_power_state(dev, state);
+
+       return 0;
+}
+
+static int savagefb_resume (struct pci_dev* dev)
+{
+       struct fb_info *info =
+               (struct fb_info *)pci_get_drvdata(dev);
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+
+       DBG("savage_resume");
+
+       pci_set_power_state(dev, 0);
+       pci_restore_state(dev);
+       if(pci_enable_device(dev))
+               DBG("err");
+
+       SavagePrintRegs();
+
+       acquire_console_sem();
+
+       savage_enable_mmio(par);
+       savage_init_hw(par);
+       savagefb_set_par (info);
+
+       fb_set_suspend (info, 0);
+       release_console_sem();
+
+       return 0;
+}
+
+
+static struct pci_device_id savagefb_devices[] __devinitdata = {
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX128,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64C,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128SDR,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128DDR,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64SDR,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64DDR,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCSDR,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCDDR,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE4},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D_MV,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D_MV},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE2000},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX_MV,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX_MV},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX_MV,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX_MV},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_PM,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_PM},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_KM,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_KM},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_P,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_P},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_K,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_K},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDR,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDR},
+
+       {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDRK,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDRK},
+
+       {0, 0, 0, 0, 0, 0, 0}
+};
+
+MODULE_DEVICE_TABLE(pci, savagefb_devices);
+
+static struct pci_driver savagefb_driver = {
+       .name =     "savagefb",
+       .id_table = savagefb_devices,
+       .probe =    savagefb_probe,
+       .suspend =  savagefb_suspend,
+       .resume =   savagefb_resume,
+       .remove =   __devexit_p(savagefb_remove)
+};
+
+/* **************************** exit-time only **************************** */
+
+static void __exit savage_done (void)
+{
+       DBG("savage_done");
+       pci_unregister_driver (&savagefb_driver);
+}
+
+
+/* ************************* init in-kernel code ************************** */
+
+int __init savagefb_setup(char *options)
+{
+#ifndef MODULE
+       char *this_opt;
+
+       if (!options || !*options)
+               return 0;
+
+       while ((this_opt = strsep(&options, ",")) != NULL) {
+               mode_option = this_opt;
+       }
+#endif /* !MODULE */
+       return 0;
+}
+
+int __init savagefb_init(void)
+{
+       char *option;
+
+       DBG("savagefb_init");
+
+       if (fb_get_options("savagefb", &option))
+               return -ENODEV;
+
+       savagefb_setup(option);
+       return pci_register_driver (&savagefb_driver);
+
+}
+
+module_init(savagefb_init);
+module_exit(savage_done);
diff --git a/drivers/video/savage/savagefb.h b/drivers/video/savage/savagefb.h
new file mode 100644 (file)
index 0000000..8594b1e
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * linux/drivers/video/savagefb.h -- S3 Savage Framebuffer Driver
+ *
+ * Copyright (c) 2001  Denis Oliver Kropp <dok@convergence.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License.  See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+
+#ifndef __SAVAGEFB_H__
+#define __SAVAGEFB_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "../edid.h"
+
+#ifdef SAVAGEFB_DEBUG
+# define DBG(x)                printk (KERN_DEBUG "savagefb: %s\n", (x));
+#else
+# define DBG(x)
+# define SavagePrintRegs(...)
+#endif
+
+
+#define PCI_CHIP_SAVAGE4      0x8a22
+#define PCI_CHIP_SAVAGE3D     0x8a20
+#define PCI_CHIP_SAVAGE3D_MV  0x8a21
+#define PCI_CHIP_SAVAGE2000   0x9102
+#define PCI_CHIP_SAVAGE_MX_MV 0x8c10
+#define PCI_CHIP_SAVAGE_MX    0x8c11
+#define PCI_CHIP_SAVAGE_IX_MV 0x8c12
+#define PCI_CHIP_SAVAGE_IX    0x8c13
+#define PCI_CHIP_PROSAVAGE_PM 0x8a25
+#define PCI_CHIP_PROSAVAGE_KM 0x8a26
+ /* Twister is a code name; hope I get the real name soon. */
+#define PCI_CHIP_S3TWISTER_P  0x8d01
+#define PCI_CHIP_S3TWISTER_K  0x8d02
+#define PCI_CHIP_PROSAVAGE_DDR          0x8d03
+#define PCI_CHIP_PROSAVAGE_DDRK         0x8d04
+#define PCI_CHIP_SUPSAV_MX128          0x8c22
+#define PCI_CHIP_SUPSAV_MX64           0x8c24
+#define PCI_CHIP_SUPSAV_MX64C          0x8c26
+#define PCI_CHIP_SUPSAV_IX128SDR       0x8c2a
+#define PCI_CHIP_SUPSAV_IX128DDR       0x8c2b
+#define PCI_CHIP_SUPSAV_IX64SDR                0x8c2c
+#define PCI_CHIP_SUPSAV_IX64DDR                0x8c2d
+#define PCI_CHIP_SUPSAV_IXCSDR         0x8c2e
+#define PCI_CHIP_SUPSAV_IXCDDR         0x8c2f
+
+
+
+#define S3_SAVAGE3D_SERIES(chip)  ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE_MX))
+
+#define S3_SAVAGE4_SERIES(chip)   ((chip==S3_SAVAGE4) || (chip==S3_PROSAVAGE))
+
+#define S3_SAVAGE_MOBILE_SERIES(chip)  ((chip==S3_SAVAGE_MX) || (chip==S3_SUPERSAVAGE))
+
+#define S3_SAVAGE_SERIES(chip)    ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE2000))
+
+
+/* Chip tags.  These are used to group the adapters into
+ * related families.
+ */
+
+typedef enum {
+  S3_UNKNOWN = 0,
+  S3_SAVAGE3D,
+  S3_SAVAGE_MX,
+  S3_SAVAGE4,
+  S3_PROSAVAGE,
+  S3_SUPERSAVAGE,
+  S3_SAVAGE2000,
+  S3_LAST
+} savage_chipset;
+
+#define BIOS_BSIZE                  1024
+#define BIOS_BASE                   0xc0000
+
+#define SAVAGE_NEWMMIO_REGBASE_S3    0x1000000  /* 16MB */
+#define SAVAGE_NEWMMIO_REGBASE_S4    0x0000000
+#define SAVAGE_NEWMMIO_REGSIZE      0x0080000  /* 512kb */
+#define SAVAGE_NEWMMIO_VGABASE      0x8000
+
+#define BASE_FREQ                   14318
+#define HALF_BASE_FREQ               7159
+
+#define FIFO_CONTROL_REG            0x8200
+#define MIU_CONTROL_REG                     0x8204
+#define STREAMS_TIMEOUT_REG         0x8208
+#define MISC_TIMEOUT_REG            0x820c
+
+#define MONO_PAT_0                   0xa4e8
+#define MONO_PAT_1                   0xa4ec
+
+#define MAXFIFO                      0x7f00
+
+#define BCI_CMD_NOP                  0x40000000
+#define BCI_CMD_SETREG               0x96000000
+#define BCI_CMD_RECT                 0x48000000
+#define BCI_CMD_RECT_XP              0x01000000
+#define BCI_CMD_RECT_YP              0x02000000
+#define BCI_CMD_SEND_COLOR           0x00008000
+#define BCI_CMD_DEST_GBD             0x00000000
+#define BCI_CMD_SRC_GBD              0x00000020
+#define BCI_CMD_SRC_SOLID            0x00000000
+#define BCI_CMD_SRC_MONO             0x00000060
+#define BCI_CMD_CLIP_NEW             0x00006000
+#define BCI_CMD_CLIP_LR              0x00004000
+
+#define BCI_CLIP_LR(l, r)            ((((r) << 16) | (l)) & 0x0FFF0FFF)
+#define BCI_CLIP_TL(t, l)            ((((t) << 16) | (l)) & 0x0FFF0FFF)
+#define BCI_CLIP_BR(b, r)            ((((b) << 16) | (r)) & 0x0FFF0FFF)
+#define BCI_W_H(w, h)                (((h) << 16) | ((w) & 0xFFF))
+#define BCI_X_Y(x, y)                (((y) << 16) | ((x) & 0xFFF))
+
+#define BCI_GBD1                     0xE0
+#define BCI_GBD2                     0xE1
+
+#define BCI_BUFFER_OFFSET            0x10000
+#define BCI_SIZE                     0x4000
+
+#define BCI_SEND(dw)                 writel(dw, par->bci_base + par->bci_ptr++)
+
+#define BCI_CMD_GET_ROP(cmd)         (((cmd) >> 16) & 0xFF)
+#define BCI_CMD_SET_ROP(cmd, rop)    ((cmd) |= ((rop & 0xFF) << 16))
+#define BCI_CMD_SEND_COLOR           0x00008000
+
+struct xtimings {
+       unsigned int Clock;
+       unsigned int HDisplay;
+       unsigned int HSyncStart;
+       unsigned int HSyncEnd;
+       unsigned int HTotal;
+       unsigned int HAdjusted;
+       unsigned int VDisplay;
+       unsigned int VSyncStart;
+       unsigned int VSyncEnd;
+       unsigned int VTotal;
+       unsigned int sync;
+       int            dblscan;
+       int            interlaced;
+};
+
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PALETTE     256
+
+
+struct savagefb_par;
+
+struct savagefb_i2c_chan {
+       struct savagefb_par *par;
+       struct i2c_adapter adapter;
+       struct i2c_algo_bit_data algo;
+       volatile u8 __iomem *ioaddr;
+       u32   reg;
+};
+
+struct savagefb_par {
+       struct pci_dev *pcidev;
+       savage_chipset  chip;
+       struct savagefb_i2c_chan chan;
+       unsigned char   *edid;
+       u32 pseudo_palette[16];
+       int dacSpeedBpp;
+       int maxClock;
+       int minClock;
+       int numClocks;
+       int clock[4];
+       struct {
+               u8   __iomem *vbase;
+               u32    pbase;
+               u32    len;
+#ifdef CONFIG_MTRR
+               int    mtrr;
+#endif
+       } video;
+
+       struct {
+               volatile u8  __iomem *vbase;
+               u32           pbase;
+               u32           len;
+       } mmio;
+
+       volatile u32  __iomem *bci_base;
+       unsigned int  bci_ptr;
+
+       u32           cob_offset;
+       u32           cob_size;
+       int           cob_index;
+
+       void (*SavageWaitIdle) (struct savagefb_par *par);
+       void (*SavageWaitFifo) (struct savagefb_par *par, int space);
+
+       int MCLK, REFCLK, LCDclk;
+       int HorizScaleFactor;
+
+       /* Panels size */
+       int SavagePanelWidth;
+       int SavagePanelHeight;
+
+       struct {
+               u16 red, green, blue, transp;
+       } palette[NR_PALETTE];
+
+       int depth;
+       int vwidth;
+
+       unsigned char MiscOutReg;     /* Misc */
+       unsigned char CRTC[25];       /* Crtc Controller */
+       unsigned char Sequencer[5];   /* Video Sequencer */
+       unsigned char Graphics[9];    /* Video Graphics */
+       unsigned char Attribute[21];  /* Video Atribute */
+
+       unsigned int mode, refresh;
+       unsigned char SR08, SR0E, SR0F;
+       unsigned char SR10, SR11, SR12, SR13, SR15, SR18, SR29, SR30;
+       unsigned char SR54[8];
+       unsigned char Clock;
+       unsigned char CR31, CR32, CR33, CR34, CR36, CR3A, CR3B, CR3C;
+       unsigned char CR40, CR41, CR42, CR43, CR45;
+       unsigned char CR50, CR51, CR53, CR55, CR58, CR5B, CR5D, CR5E;
+       unsigned char CR60, CR63, CR65, CR66, CR67, CR68, CR69, CR6D, CR6F;
+       unsigned char CR86, CR88;
+       unsigned char CR90, CR91, CRB0;
+       unsigned int  STREAMS[22];      /* yuck, streams regs */
+       unsigned int  MMPR0, MMPR1, MMPR2, MMPR3;
+};
+
+#define BCI_BD_BW_DISABLE            0x10000000
+#define BCI_BD_SET_BPP(bd, bpp)      ((bd) |= (((bpp) & 0xFF) << 16))
+#define BCI_BD_SET_STRIDE(bd, st)    ((bd) |= ((st) & 0xFFFF))
+
+
+/* IO functions */
+
+#define  vga_in8(addr)         (inb (addr))
+#define vga_in16(addr)         (inw (addr))
+#define vga_in32(addr)         (inl (addr))
+
+#define  vga_out8(addr,val)    (outb ((val), (addr)))
+#define vga_out16(addr,val)    (outw ((val), (addr)))
+#define vga_out32(addr,val)    (outl ((val), (addr)))
+
+#define savage_in16(addr)      readw(par->mmio.vbase + (addr))
+#define savage_in32(addr)      readl(par->mmio.vbase + (addr))
+
+#define savage_out16(addr,val) writew((val), par->mmio.vbase + (addr))
+#define savage_out32(addr,val) writel((val), par->mmio.vbase + (addr))
+
+static inline u8 VGArCR (u8 index)
+{
+  outb (index, 0x3d4);
+  return inb (0x3d5);
+}
+
+static inline u8 VGArGR (u8 index)
+{
+  outb (index, 0x3ce);
+  return inb (0x3cf);
+}
+
+static inline u8 VGArSEQ (u8 index)
+{
+  outb (index, 0x3c4);
+  return inb (0x3c5);
+}
+
+#define VGAwCR(index, val) \
+do {                       \
+  vga_out8 (0x3d4, index); \
+  vga_out8 (0x3d5, val);   \
+} while (0)
+
+#define VGAwGR(index, val) \
+do {                       \
+  vga_out8 (0x3ce, index); \
+  vga_out8 (0x3cf, val);   \
+} while (0)
+
+#define VGAwSEQ(index, val) \
+do {                        \
+  vga_out8 (0x3c4, index);  \
+  vga_out8 (0x3c5, val);    \
+} while (0)
+
+#define VGAenablePalette() \
+do {                       \
+  u8 tmp;                  \
+                           \
+  tmp = vga_in8 (0x3da);   \
+  vga_out8 (0x3c0, 0x00);  \
+  paletteEnabled = 1;      \
+} while (0)
+
+#define VGAdisablePalette() \
+do {                        \
+  u8 tmp;                   \
+                            \
+  tmp = vga_in8 (0x3da);    \
+  vga_out8 (0x3c0, 0x20);   \
+  paletteEnabled = 0;       \
+} while (0)
+
+#define VGAwATTR(index, value) \
+do {                           \
+  u8 tmp;                      \
+                               \
+  if (paletteEnabled)          \
+    index &= ~0x20;            \
+  else                         \
+    index |= 0x20;             \
+                               \
+  tmp = vga_in8 (0x3da);       \
+  vga_out8 (0x3c0, index);     \
+  vga_out8 (0x3c0, value);     \
+} while (0)
+
+#define VGAwMISC(value)    \
+do {                       \
+  vga_out8 (0x3c2, value); \
+} while (0)
+
+#ifndef CONFIG_FB_SAVAGE_ACCEL
+#define savagefb_set_clip(x)
+#endif
+
+#define VerticalRetraceWait() \
+{ \
+       vga_out8 (0x3d4, 0x17); \
+       if (vga_in8 (0x3d5) & 0x80) { \
+               while ((vga_in8(0x3da) & 0x08) == 0x08) ; \
+               while ((vga_in8(0x3da) & 0x08) == 0x00) ; \
+       } \
+}
+
+extern int savagefb_probe_i2c_connector(struct savagefb_par *par,
+                                       u8 **out_edid);
+extern void savagefb_create_i2c_busses(struct fb_info *info);
+extern void savagefb_delete_i2c_busses(struct fb_info *info);
+extern int  savagefb_sync(struct fb_info *info);
+extern void savagefb_copyarea(struct fb_info *info,
+                             const struct fb_copyarea *region);
+extern void savagefb_fillrect(struct fb_info *info,
+                             const struct fb_fillrect *rect);
+extern void savagefb_imageblit(struct fb_info *info,
+                              const struct fb_image *image);
+
+
+#endif /* __SAVAGEFB_H__ */
diff --git a/drivers/video/savage/savagefb_accel.c b/drivers/video/savage/savagefb_accel.c
new file mode 100644 (file)
index 0000000..da5fb8f
--- /dev/null
@@ -0,0 +1,140 @@
+/*-*- linux-c -*-
+ *  linux/drivers/video/savage/savage_accel.c -- Hardware Acceleration
+ *
+ *      Copyright (C) 2004 Antonino Daplas<adaplas@pol.net>
+ *      All Rights Reserved
+ *
+ *  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 <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+
+#include "savagefb.h"
+
+static u32 savagefb_rop[] = {
+       0xCC, /* ROP_COPY */
+       0x5A  /* ROP_XOR  */
+};
+
+int savagefb_sync(struct fb_info *info)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+
+       par->SavageWaitIdle(par);
+       return 0;
+}
+EXPORT_SYMBOL(savagefb_sync);
+
+void savagefb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       int sx = region->sx, dx = region->dx;
+       int sy = region->sy, dy = region->dy;
+       int cmd;
+
+       if (!region->width || !region->height)
+               return;
+       par->bci_ptr = 0;
+       cmd = BCI_CMD_RECT | BCI_CMD_DEST_GBD | BCI_CMD_SRC_GBD;
+       BCI_CMD_SET_ROP(cmd, savagefb_rop[0]);
+
+       if (dx <= sx) {
+               cmd |= BCI_CMD_RECT_XP;
+       } else {
+               sx += region->width - 1;
+               dx += region->width - 1;
+       }
+
+       if (dy <= sy) {
+               cmd |= BCI_CMD_RECT_YP;
+       } else {
+               sy += region->height - 1;
+               dy += region->height - 1;
+       }
+
+       par->SavageWaitFifo(par,4);
+       BCI_SEND(cmd);
+       BCI_SEND(BCI_X_Y(sx, sy));
+       BCI_SEND(BCI_X_Y(dx, dy));
+       BCI_SEND(BCI_W_H(region->width, region->height));
+}
+EXPORT_SYMBOL(savagefb_copyarea);
+
+void savagefb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       int cmd, color;
+
+       if (!rect->width || !rect->height)
+               return;
+
+       if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR)
+               color = rect->color;
+       else
+               color = ((u32 *)info->pseudo_palette)[rect->color];
+
+       cmd = BCI_CMD_RECT | BCI_CMD_RECT_XP | BCI_CMD_RECT_YP |
+             BCI_CMD_DEST_GBD | BCI_CMD_SRC_SOLID |
+             BCI_CMD_SEND_COLOR;
+
+       par->bci_ptr = 0;
+       BCI_CMD_SET_ROP(cmd, savagefb_rop[rect->rop]);
+
+       par->SavageWaitFifo(par,4);
+       BCI_SEND(cmd);
+       BCI_SEND(color);
+       BCI_SEND( BCI_X_Y(rect->dx, rect->dy) );
+       BCI_SEND( BCI_W_H(rect->width, rect->height) );
+}
+EXPORT_SYMBOL(savagefb_fillrect);
+
+void savagefb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+       struct savagefb_par *par = (struct savagefb_par *)info->par;
+       int fg, bg, size, i, width;
+       int cmd;
+       u32 *src = (u32 *) image->data;
+
+       if (!image->width || !image->height)
+               return;
+
+       if (image->depth != 1) {
+               cfb_imageblit(info, image);
+               return;
+       }
+
+       if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
+               fg = image->fg_color;
+               bg = image->bg_color;
+       } else {
+               fg = ((u32 *)info->pseudo_palette)[image->fg_color];
+               bg = ((u32 *)info->pseudo_palette)[image->bg_color];
+       }
+
+       cmd = BCI_CMD_RECT | BCI_CMD_RECT_XP | BCI_CMD_RECT_YP |
+             BCI_CMD_CLIP_LR | BCI_CMD_DEST_GBD | BCI_CMD_SRC_MONO |
+             BCI_CMD_SEND_COLOR;
+
+       par->bci_ptr = 0;
+       BCI_CMD_SET_ROP(cmd, savagefb_rop[0]);
+
+       width = (image->width + 31) & ~31;
+       size = (width * image->height)/8;
+       size >>= 2;
+
+       par->SavageWaitFifo(par, size + 5);
+       BCI_SEND(cmd);
+       BCI_SEND(BCI_CLIP_LR(image->dx, image->dx + image->width - 1));
+       BCI_SEND(fg);
+       BCI_SEND(bg);
+       BCI_SEND(BCI_X_Y(image->dx, image->dy));
+       BCI_SEND(BCI_W_H(width, image->height));
+       for (i = 0; i < size; i++)
+               BCI_SEND(src[i]);
+}
+EXPORT_SYMBOL(savagefb_imageblit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c
new file mode 100644 (file)
index 0000000..e118285
--- /dev/null
@@ -0,0 +1,1864 @@
+/*
+ * linux/drivers/video/w100fb.c
+ *
+ * Frame Buffer Device for ATI Imageon w100 (Wallaby)
+ *
+ * Copyright (C) 2002, ATI Corp.
+ * Copyright (C) 2004-2005 Richard Purdie
+ *
+ * Rewritten for 2.6 by Richard Purdie <rpurdie@rpsys.net>
+ *
+ * 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 <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <video/w100fb.h>
+#include "w100fb.h"
+
+/*
+ * Prototypes
+ */
+static void w100fb_save_buffer(void);
+static void w100fb_clear_buffer(void);
+static void w100fb_restore_buffer(void);
+static void w100fb_clear_screen(u32 mode, long int offset);
+static void w100_resume(void);
+static void w100_suspend(u32 mode);
+static void w100_init_qvga_rotation(u16 deg);
+static void w100_init_vga_rotation(u16 deg);
+static void w100_vsync(void);
+static void w100_init_sharp_lcd(u32 mode);
+static void w100_pwm_setup(void);
+static void w100_InitExtMem(u32 mode);
+static void w100_hw_init(void);
+static u16 w100_set_fastsysclk(u16 Freq);
+
+static void lcdtg_hw_init(u32 mode);
+static void lcdtg_lcd_change(u32 mode);
+static void lcdtg_resume(void);
+static void lcdtg_suspend(void);
+
+
+/* Register offsets & lengths */
+#define REMAPPED_FB_LEN   0x15ffff
+
+#define BITS_PER_PIXEL    16
+
+/* Pseudo palette size */
+#define MAX_PALETTES      16
+
+/* for resolution change */
+#define LCD_MODE_INIT (-1)
+#define LCD_MODE_480    0
+#define LCD_MODE_320    1
+#define LCD_MODE_240    2
+#define LCD_MODE_640    3
+
+#define LCD_SHARP_QVGA 0
+#define LCD_SHARP_VGA  1
+
+#define LCD_MODE_PORTRAIT      0
+#define LCD_MODE_LANDSCAPE     1
+
+#define W100_SUSPEND_EXTMEM 0
+#define W100_SUSPEND_ALL    1
+
+/* General frame buffer data structures */
+struct w100fb_par {
+       u32 xres;
+       u32 yres;
+       int fastsysclk_mode;
+       int lcdMode;
+       int rotation_flag;
+       int blanking_flag;
+       int comadj;
+       int phadadj;
+};
+
+static struct w100fb_par *current_par;
+
+static u16 *gSaveImagePtr = NULL;
+
+/* Remapped addresses for base cfg, memmapped regs and the frame buffer itself */
+static void *remapped_base;
+static void *remapped_regs;
+static void *remapped_fbuf;
+
+/* External Function */
+static void(*w100fb_ssp_send)(u8 adrs, u8 data);
+
+/*
+ * Sysfs functions
+ */
+
+static ssize_t rotation_show(struct device *dev, char *buf)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct w100fb_par *par=info->par;
+
+       return sprintf(buf, "%d\n",par->rotation_flag);
+}
+
+static ssize_t rotation_store(struct device *dev, const char *buf, size_t count)
+{
+       unsigned int rotate;
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct w100fb_par *par=info->par;
+
+       rotate = simple_strtoul(buf, NULL, 10);
+
+       if (rotate > 0) par->rotation_flag = 1;
+       else par->rotation_flag = 0;
+
+       if (par->lcdMode == LCD_MODE_320)
+               w100_init_qvga_rotation(par->rotation_flag ? 270 : 90);
+       else if (par->lcdMode == LCD_MODE_240)
+               w100_init_qvga_rotation(par->rotation_flag ? 180 : 0);
+       else if (par->lcdMode == LCD_MODE_640)
+               w100_init_vga_rotation(par->rotation_flag ? 270 : 90);
+       else if (par->lcdMode == LCD_MODE_480)
+               w100_init_vga_rotation(par->rotation_flag ? 180 : 0);
+
+       return count;
+}
+
+static DEVICE_ATTR(rotation, 0644, rotation_show, rotation_store);
+
+static ssize_t w100fb_reg_read(struct device *dev, const char *buf, size_t count)
+{
+       unsigned long param;
+       unsigned long regs;
+       regs = simple_strtoul(buf, NULL, 16);
+       param = readl(remapped_regs + regs);
+       printk("Read Register 0x%08lX: 0x%08lX\n", regs, param);
+       return count;
+}
+
+static DEVICE_ATTR(reg_read, 0200, NULL, w100fb_reg_read);
+
+static ssize_t w100fb_reg_write(struct device *dev, const char *buf, size_t count)
+{
+       unsigned long regs;
+       unsigned long param;
+       sscanf(buf, "%lx %lx", &regs, &param);
+
+       if (regs <= 0x2000) {
+               printk("Write Register 0x%08lX: 0x%08lX\n", regs, param);
+               writel(param, remapped_regs + regs);
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR(reg_write, 0200, NULL, w100fb_reg_write);
+
+
+static ssize_t fastsysclk_show(struct device *dev, char *buf)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct w100fb_par *par=info->par;
+
+       return sprintf(buf, "%d\n",par->fastsysclk_mode);
+}
+
+static ssize_t fastsysclk_store(struct device *dev, const char *buf, size_t count)
+{
+       int param;
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct w100fb_par *par=info->par;
+
+       param = simple_strtoul(buf, NULL, 10);
+
+       if (param == 75) {
+               printk("Set fastsysclk %d\n", param);
+               par->fastsysclk_mode = param;
+               w100_set_fastsysclk(par->fastsysclk_mode);
+       } else if (param == 100) {
+               printk("Set fastsysclk %d\n", param);
+               par->fastsysclk_mode = param;
+               w100_set_fastsysclk(par->fastsysclk_mode);
+       }
+       return count;
+}
+
+static DEVICE_ATTR(fastsysclk, 0644, fastsysclk_show, fastsysclk_store);
+
+/*
+ * The touchscreen on this device needs certain information
+ * from the video driver to function correctly. We export it here.
+ */
+int w100fb_get_xres(void) {
+       return current_par->xres;
+}
+
+int w100fb_get_blanking(void) {
+       return current_par->blanking_flag;
+}
+
+int w100fb_get_fastsysclk(void) {
+       return current_par->fastsysclk_mode;
+}
+EXPORT_SYMBOL(w100fb_get_xres);
+EXPORT_SYMBOL(w100fb_get_blanking);
+EXPORT_SYMBOL(w100fb_get_fastsysclk);
+
+
+/*
+ * Set a palette value from rgb components
+ */
+static int w100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                            u_int trans, struct fb_info *info)
+{
+       unsigned int val;
+       int ret = 1;
+
+       /*
+        * If greyscale is true, then we convert the RGB value
+        * to greyscale no matter what visual we are using.
+        */
+       if (info->var.grayscale)
+               red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+       /*
+        * 16-bit True Colour.  We encode the RGB value
+        * according to the RGB bitfield information.
+        */
+       if (regno < MAX_PALETTES) {
+
+               u32 *pal = info->pseudo_palette;
+
+               val = (red & 0xf800) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
+               pal[regno] = val;
+               ret = 0;
+       }
+       return ret;
+}
+
+
+/*
+ * Blank the display based on value in blank_mode
+ */
+static int w100fb_blank(int blank_mode, struct fb_info *info)
+{
+       struct w100fb_par *par;
+       par=info->par;
+
+       switch(blank_mode) {
+
+       case FB_BLANK_NORMAL: /* Normal blanking */
+       case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
+       case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
+       case FB_BLANK_POWERDOWN: /* Poweroff */
+               if (par->blanking_flag == 0) {
+                       w100fb_save_buffer();
+                       lcdtg_suspend();
+                       par->blanking_flag = 1;
+               }
+               break;
+
+       case FB_BLANK_UNBLANK: /* Unblanking */
+               if (par->blanking_flag != 0) {
+                       w100fb_restore_buffer();
+                       lcdtg_resume();
+                       par->blanking_flag = 0;
+               }
+               break;
+       }
+       return 0;
+}
+
+/*
+ *  Change the resolution by calling the appropriate hardware functions
+ */
+static void w100fb_changeres(int rotate_mode, u32 mode)
+{
+       u16 rotation=0;
+
+       switch(rotate_mode) {
+       case LCD_MODE_LANDSCAPE:
+               rotation=(current_par->rotation_flag ? 270 : 90);
+               break;
+       case LCD_MODE_PORTRAIT:
+               rotation=(current_par->rotation_flag ? 180 : 0);
+               break;
+       }
+
+       w100_pwm_setup();
+       switch(mode) {
+       case LCD_SHARP_QVGA:
+               w100_vsync();
+               w100_suspend(W100_SUSPEND_EXTMEM);
+               w100_init_sharp_lcd(LCD_SHARP_QVGA);
+               w100_init_qvga_rotation(rotation);
+               w100_InitExtMem(LCD_SHARP_QVGA);
+               w100fb_clear_screen(LCD_SHARP_QVGA, 0);
+               lcdtg_lcd_change(LCD_SHARP_QVGA);
+               break;
+       case LCD_SHARP_VGA:
+               w100fb_clear_screen(LCD_SHARP_QVGA, 0);
+               writel(0xBFFFA000, remapped_regs + mmMC_EXT_MEM_LOCATION);
+               w100_InitExtMem(LCD_SHARP_VGA);
+               w100fb_clear_screen(LCD_SHARP_VGA, 0x200000);
+               w100_vsync();
+               w100_init_sharp_lcd(LCD_SHARP_VGA);
+               if (rotation != 0)
+                       w100_init_vga_rotation(rotation);
+               lcdtg_lcd_change(LCD_SHARP_VGA);
+               break;
+       }
+}
+
+/*
+ * Set up the display for the fb subsystem
+ */
+static void w100fb_activate_var(struct fb_info *info)
+{
+       u32 temp32;
+       struct w100fb_par *par=info->par;
+       struct fb_var_screeninfo *var = &info->var;
+
+       /* Set the hardware to 565 */
+       temp32 = readl(remapped_regs + mmDISP_DEBUG2);
+       temp32 &= 0xff7fffff;
+       temp32 |= 0x00800000;
+       writel(temp32, remapped_regs + mmDISP_DEBUG2);
+
+       if (par->lcdMode == LCD_MODE_INIT) {
+               w100_init_sharp_lcd(LCD_SHARP_VGA);
+               w100_init_vga_rotation(par->rotation_flag ? 270 : 90);
+               par->lcdMode = LCD_MODE_640;
+               lcdtg_hw_init(LCD_SHARP_VGA);
+       } else if (var->xres == 320 && var->yres == 240) {
+               if (par->lcdMode != LCD_MODE_320) {
+                       w100fb_changeres(LCD_MODE_LANDSCAPE, LCD_SHARP_QVGA);
+                       par->lcdMode = LCD_MODE_320;
+               }
+       } else if (var->xres == 240 && var->yres == 320) {
+               if (par->lcdMode != LCD_MODE_240) {
+                       w100fb_changeres(LCD_MODE_PORTRAIT, LCD_SHARP_QVGA);
+                       par->lcdMode = LCD_MODE_240;
+               }
+       } else if (var->xres == 640 && var->yres == 480) {
+               if (par->lcdMode != LCD_MODE_640) {
+                       w100fb_changeres(LCD_MODE_LANDSCAPE, LCD_SHARP_VGA);
+                       par->lcdMode = LCD_MODE_640;
+               }
+       } else if (var->xres == 480 && var->yres == 640) {
+               if (par->lcdMode != LCD_MODE_480) {
+                       w100fb_changeres(LCD_MODE_PORTRAIT, LCD_SHARP_VGA);
+                       par->lcdMode = LCD_MODE_480;
+               }
+       } else printk(KERN_ERR "W100FB: Resolution error!\n");
+}
+
+
+/*
+ *  w100fb_check_var():
+ *  Get the video params out of 'var'. If a value doesn't fit, round it up,
+ *  if it's too big, return -EINVAL.
+ *
+ */
+static int w100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       if (var->xres < var->yres) { /* Portrait mode */
+               if ((var->xres > 480) || (var->yres > 640)) {
+                       return -EINVAL;
+               } else if ((var->xres > 240) || (var->yres > 320)) {
+                       var->xres = 480;
+                       var->yres = 640;
+               } else {
+                       var->xres = 240;
+                       var->yres = 320;
+               }
+       } else { /* Landscape mode */
+               if ((var->xres > 640) || (var->yres > 480)) {
+                       return -EINVAL;
+               } else if ((var->xres > 320) || (var->yres > 240)) {
+                       var->xres = 640;
+                       var->yres = 480;
+               } else {
+                       var->xres = 320;
+                       var->yres = 240;
+               }
+       }
+
+       var->xres_virtual = max(var->xres_virtual, var->xres);
+       var->yres_virtual = max(var->yres_virtual, var->yres);
+
+       if (var->bits_per_pixel > BITS_PER_PIXEL)
+               return -EINVAL;
+       else
+               var->bits_per_pixel = BITS_PER_PIXEL;
+
+       var->red.offset = 11;
+       var->red.length = 5;
+       var->green.offset = 5;
+       var->green.length = 6;
+       var->blue.offset = 0;
+       var->blue.length = 5;
+       var->transp.offset = var->transp.length = 0;
+
+       var->nonstd = 0;
+
+       var->height = -1;
+       var->width = -1;
+       var->vmode = FB_VMODE_NONINTERLACED;
+
+       var->sync = 0;
+       var->pixclock = 0x04;   /* 171521; */
+
+       return 0;
+}
+
+
+/*
+ * w100fb_set_par():
+ *     Set the user defined part of the display for the specified console
+ *  by looking at the values in info.var
+ */
+static int w100fb_set_par(struct fb_info *info)
+{
+       struct w100fb_par *par=info->par;
+
+       par->xres = info->var.xres;
+       par->yres = info->var.yres;
+
+       info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+       info->fix.ypanstep = 0;
+       info->fix.ywrapstep = 0;
+
+       if (par->blanking_flag)
+               w100fb_clear_buffer();
+
+       w100fb_activate_var(info);
+
+       if (par->lcdMode == LCD_MODE_480) {
+               info->fix.line_length = (480 * BITS_PER_PIXEL) / 8;
+               info->fix.smem_len = 0x200000;
+       } else if (par->lcdMode == LCD_MODE_320) {
+               info->fix.line_length = (320 * BITS_PER_PIXEL) / 8;
+               info->fix.smem_len = 0x60000;
+       } else if (par->lcdMode == LCD_MODE_240) {
+               info->fix.line_length = (240 * BITS_PER_PIXEL) / 8;
+               info->fix.smem_len = 0x60000;
+       } else if (par->lcdMode == LCD_MODE_INIT || par->lcdMode == LCD_MODE_640) {
+               info->fix.line_length = (640 * BITS_PER_PIXEL) / 8;
+               info->fix.smem_len = 0x200000;
+       }
+
+       return 0;
+}
+
+
+/*
+ *      Frame buffer operations
+ */
+static struct fb_ops w100fb_ops = {
+       .owner = THIS_MODULE,
+       .fb_check_var = w100fb_check_var,
+       .fb_set_par = w100fb_set_par,
+       .fb_setcolreg = w100fb_setcolreg,
+       .fb_blank = w100fb_blank,
+       .fb_fillrect = cfb_fillrect,
+       .fb_copyarea = cfb_copyarea,
+       .fb_imageblit = cfb_imageblit,
+       .fb_cursor = soft_cursor,
+};
+
+
+static void w100fb_clear_screen(u32 mode, long int offset)
+{
+       int i, numPix = 0;
+
+       if (mode == LCD_SHARP_VGA)
+               numPix = 640 * 480;
+       else if (mode == LCD_SHARP_QVGA)
+               numPix = 320 * 240;
+
+       for (i = 0; i < numPix; i++)
+               writew(0xffff, remapped_fbuf + offset + (2*i));
+}
+
+
+static void w100fb_save_buffer(void)
+{
+       int i;
+
+       if (gSaveImagePtr != NULL) {
+               vfree(gSaveImagePtr);
+               gSaveImagePtr = NULL;
+       }
+       gSaveImagePtr = vmalloc(current_par->xres * current_par->yres * BITS_PER_PIXEL / 8);
+       if (gSaveImagePtr != NULL) {
+               for (i = 0; i < (current_par->xres * current_par->yres); i++)
+                       *(gSaveImagePtr + i) = readw(remapped_fbuf + (2*i));
+       } else {
+               printk(KERN_WARNING "can't alloc pre-off image buffer\n");
+       }
+}
+
+
+static void w100fb_restore_buffer(void)
+{
+       int i;
+
+       if (gSaveImagePtr != NULL) {
+               for (i = 0; i < (current_par->xres * current_par->yres); i++) {
+                               writew(*(gSaveImagePtr + i),remapped_fbuf + (2*i));
+                       }
+               vfree(gSaveImagePtr);
+               gSaveImagePtr = NULL;
+       }
+}
+
+static void w100fb_clear_buffer(void)
+{
+       if (gSaveImagePtr != NULL) {
+               vfree(gSaveImagePtr);
+               gSaveImagePtr = NULL;
+       }
+}
+
+
+#ifdef CONFIG_PM
+static int w100fb_suspend(struct device *dev, u32 state, u32 level)
+{
+       if (level == SUSPEND_POWER_DOWN) {
+               struct fb_info *info = dev_get_drvdata(dev);
+               struct w100fb_par *par=info->par;
+
+               w100fb_save_buffer();
+               lcdtg_suspend();
+               w100_suspend(W100_SUSPEND_ALL);
+               par->blanking_flag = 1;
+       }
+       return 0;
+}
+
+static int w100fb_resume(struct device *dev, u32 level)
+{
+       if (level == RESUME_POWER_ON) {
+               struct fb_info *info = dev_get_drvdata(dev);
+               struct w100fb_par *par=info->par;
+
+               w100_resume();
+               w100fb_restore_buffer();
+               lcdtg_resume();
+               par->blanking_flag = 0;
+       }
+       return 0;
+}
+#else
+#define w100fb_suspend NULL
+#define w100fb_resume  NULL
+#endif
+
+
+int __init w100fb_probe(struct device *dev)
+{
+       struct w100fb_mach_info *inf;
+       struct fb_info *info;
+       struct w100fb_par *par;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       if (!mem)
+               return -EINVAL;
+
+       /* remap the areas we're going to use */
+       remapped_base = ioremap_nocache(mem->start+W100_CFG_BASE, W100_CFG_LEN);
+       if (remapped_base == NULL)
+               return -EIO;
+
+       remapped_regs = ioremap_nocache(mem->start+W100_REG_BASE, W100_REG_LEN);
+       if (remapped_regs == NULL) {
+               iounmap(remapped_base);
+               return -EIO;
+       }
+
+       remapped_fbuf = ioremap_nocache(mem->start+MEM_EXT_BASE_VALUE, REMAPPED_FB_LEN);
+       if (remapped_fbuf == NULL) {
+               iounmap(remapped_base);
+               iounmap(remapped_regs);
+               return -EIO;
+       }
+
+       info=framebuffer_alloc(sizeof(struct w100fb_par), dev);
+       if (!info) {
+               iounmap(remapped_base);
+               iounmap(remapped_regs);
+               iounmap(remapped_fbuf);
+               return -ENOMEM;
+       }
+
+       info->device=dev;
+       par = info->par;
+       current_par=info->par;
+       dev_set_drvdata(dev, info);
+
+       inf = dev->platform_data;
+       par->phadadj = inf->phadadj;
+       par->comadj = inf->comadj;
+       par->fastsysclk_mode = 75;
+       par->lcdMode = LCD_MODE_INIT;
+       par->rotation_flag=0;
+       par->blanking_flag=0;
+       w100fb_ssp_send = inf->w100fb_ssp_send;
+
+       w100_hw_init();
+       w100_pwm_setup();
+
+       info->pseudo_palette = kmalloc(sizeof (u32) * MAX_PALETTES, GFP_KERNEL);
+       if (!info->pseudo_palette) {
+               iounmap(remapped_base);
+               iounmap(remapped_regs);
+               iounmap(remapped_fbuf);
+               return -ENOMEM;
+       }
+
+       info->fbops = &w100fb_ops;
+       info->flags = FBINFO_DEFAULT;
+       info->node = -1;
+       info->screen_base = remapped_fbuf;
+       info->screen_size = REMAPPED_FB_LEN;
+
+       info->var.xres = 640;
+       info->var.xres_virtual = info->var.xres;
+       info->var.yres = 480;
+       info->var.yres_virtual = info->var.yres;
+       info->var.pixclock = 0x04;      /* 171521; */
+       info->var.sync = 0;
+       info->var.grayscale = 0;
+       info->var.xoffset = info->var.yoffset = 0;
+       info->var.accel_flags = 0;
+       info->var.activate = FB_ACTIVATE_NOW;
+
+       strcpy(info->fix.id, "w100fb");
+       info->fix.type = FB_TYPE_PACKED_PIXELS;
+       info->fix.type_aux = 0;
+       info->fix.accel = FB_ACCEL_NONE;
+       info->fix.smem_start = mem->start+MEM_EXT_BASE_VALUE;
+       info->fix.mmio_start = mem->start+W100_REG_BASE;
+       info->fix.mmio_len = W100_REG_LEN;
+
+       w100fb_check_var(&info->var, info);
+       w100fb_set_par(info);
+
+       if (register_framebuffer(info) < 0) {
+               kfree(info->pseudo_palette);
+               iounmap(remapped_base);
+               iounmap(remapped_regs);
+               iounmap(remapped_fbuf);
+               return -EINVAL;
+       }
+
+       device_create_file(dev, &dev_attr_fastsysclk);
+       device_create_file(dev, &dev_attr_reg_read);
+       device_create_file(dev, &dev_attr_reg_write);
+       device_create_file(dev, &dev_attr_rotation);
+
+       printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
+       return 0;
+}
+
+
+static int w100fb_remove(struct device *dev)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+
+       device_remove_file(dev, &dev_attr_fastsysclk);
+       device_remove_file(dev, &dev_attr_reg_read);
+       device_remove_file(dev, &dev_attr_reg_write);
+       device_remove_file(dev, &dev_attr_rotation);
+
+       unregister_framebuffer(info);
+
+       w100fb_clear_buffer();
+       kfree(info->pseudo_palette);
+
+       iounmap(remapped_base);
+       iounmap(remapped_regs);
+       iounmap(remapped_fbuf);
+
+       framebuffer_release(info);
+
+       return 0;
+}
+
+
+/* ------------------- chipset specific functions -------------------------- */
+
+
+static void w100_soft_reset(void)
+{
+       u16 val = readw((u16 *) remapped_base + cfgSTATUS);
+       writew(val | 0x08, (u16 *) remapped_base + cfgSTATUS);
+       udelay(100);
+       writew(0x00, (u16 *) remapped_base + cfgSTATUS);
+       udelay(100);
+}
+
+/*
+ * Initialization of critical w100 hardware
+ */
+static void w100_hw_init(void)
+{
+       u32 temp32;
+       union cif_cntl_u cif_cntl;
+       union intf_cntl_u intf_cntl;
+       union cfgreg_base_u cfgreg_base;
+       union wrap_top_dir_u wrap_top_dir;
+       union cif_read_dbg_u cif_read_dbg;
+       union cpu_defaults_u cpu_default;
+       union cif_write_dbg_u cif_write_dbg;
+       union wrap_start_dir_u wrap_start_dir;
+       union mc_ext_mem_location_u mc_ext_mem_loc;
+       union cif_io_u cif_io;
+
+       w100_soft_reset();
+
+       /* This is what the fpga_init code does on reset. May be wrong
+          but there is little info available */
+       writel(0x31, remapped_regs + mmSCRATCH_UMSK);
+       for (temp32 = 0; temp32 < 10000; temp32++)
+               readl(remapped_regs + mmSCRATCH_UMSK);
+       writel(0x30, remapped_regs + mmSCRATCH_UMSK);
+
+       /* Set up CIF */
+       cif_io.val = defCIF_IO;
+       writel((u32)(cif_io.val), remapped_regs + mmCIF_IO);
+
+       cif_write_dbg.val = readl(remapped_regs + mmCIF_WRITE_DBG);
+       cif_write_dbg.f.dis_packer_ful_during_rbbm_timeout = 0;
+       cif_write_dbg.f.en_dword_split_to_rbbm = 1;
+       cif_write_dbg.f.dis_timeout_during_rbbm = 1;
+       writel((u32) (cif_write_dbg.val), remapped_regs + mmCIF_WRITE_DBG);
+
+       cif_read_dbg.val = readl(remapped_regs + mmCIF_READ_DBG);
+       cif_read_dbg.f.dis_rd_same_byte_to_trig_fetch = 1;
+       writel((u32) (cif_read_dbg.val), remapped_regs + mmCIF_READ_DBG);
+
+       cif_cntl.val = readl(remapped_regs + mmCIF_CNTL);
+       cif_cntl.f.dis_system_bits = 1;
+       cif_cntl.f.dis_mr = 1;
+       cif_cntl.f.en_wait_to_compensate_dq_prop_dly = 0;
+       cif_cntl.f.intb_oe = 1;
+       cif_cntl.f.interrupt_active_high = 1;
+       writel((u32) (cif_cntl.val), remapped_regs + mmCIF_CNTL);
+
+       /* Setup cfgINTF_CNTL and cfgCPU defaults */
+       intf_cntl.val = defINTF_CNTL;
+       intf_cntl.f.ad_inc_a = 1;
+       intf_cntl.f.ad_inc_b = 1;
+       intf_cntl.f.rd_data_rdy_a = 0;
+       intf_cntl.f.rd_data_rdy_b = 0;
+       writeb((u8) (intf_cntl.val), remapped_base + cfgINTF_CNTL);
+
+       cpu_default.val = defCPU_DEFAULTS;
+       cpu_default.f.access_ind_addr_a = 1;
+       cpu_default.f.access_ind_addr_b = 1;
+       cpu_default.f.access_scratch_reg = 1;
+       cpu_default.f.transition_size = 0;
+       writeb((u8) (cpu_default.val), remapped_base + cfgCPU_DEFAULTS);
+
+       /* set up the apertures */
+       writeb((u8) (W100_REG_BASE >> 16), remapped_base + cfgREG_BASE);
+
+       cfgreg_base.val = defCFGREG_BASE;
+       cfgreg_base.f.cfgreg_base = W100_CFG_BASE;
+       writel((u32) (cfgreg_base.val), remapped_regs + mmCFGREG_BASE);
+
+       /* This location is relative to internal w100 addresses */
+       writel(0x15FF1000, remapped_regs + mmMC_FB_LOCATION);
+
+       mc_ext_mem_loc.val = defMC_EXT_MEM_LOCATION;
+       mc_ext_mem_loc.f.mc_ext_mem_start = MEM_EXT_BASE_VALUE >> 8;
+       mc_ext_mem_loc.f.mc_ext_mem_top = MEM_EXT_TOP_VALUE >> 8;
+       writel((u32) (mc_ext_mem_loc.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
+
+       if ((current_par->lcdMode == LCD_MODE_240) || (current_par->lcdMode == LCD_MODE_320))
+               w100_InitExtMem(LCD_SHARP_QVGA);
+       else
+               w100_InitExtMem(LCD_SHARP_VGA);
+
+       wrap_start_dir.val = defWRAP_START_DIR;
+       wrap_start_dir.f.start_addr = WRAP_BUF_BASE_VALUE >> 1;
+       writel((u32) (wrap_start_dir.val), remapped_regs + mmWRAP_START_DIR);
+
+       wrap_top_dir.val = defWRAP_TOP_DIR;
+       wrap_top_dir.f.top_addr = WRAP_BUF_TOP_VALUE >> 1;
+       writel((u32) (wrap_top_dir.val), remapped_regs + mmWRAP_TOP_DIR);
+
+       writel((u32) 0x2440, remapped_regs + mmRBBM_CNTL);
+}
+
+
+/*
+ * Types
+ */
+
+struct pll_parm {
+       u16 freq;               /* desired Fout for PLL */
+       u8 M;
+       u8 N_int;
+       u8 N_fac;
+       u8 tfgoal;
+       u8 lock_time;
+};
+
+struct power_state {
+       union clk_pin_cntl_u clk_pin_cntl;
+       union pll_ref_fb_div_u pll_ref_fb_div;
+       union pll_cntl_u pll_cntl;
+       union sclk_cntl_u sclk_cntl;
+       union pclk_cntl_u pclk_cntl;
+       union clk_test_cntl_u clk_test_cntl;
+       union pwrmgt_cntl_u pwrmgt_cntl;
+       u32 freq;               /* Fout for PLL calibration */
+       u8 tf100;               /* for pll calibration */
+       u8 tf80;                /* for pll calibration */
+       u8 tf20;                /* for pll calibration */
+       u8 M;                   /* for pll calibration */
+       u8 N_int;               /* for pll calibration */
+       u8 N_fac;               /* for pll calibration */
+       u8 lock_time;   /* for pll calibration */
+       u8 tfgoal;              /* for pll calibration */
+       u8 auto_mode;   /* hardware auto switch? */
+       u8 pwm_mode;            /* 0 fast, 1 normal/slow */
+       u16 fast_sclk;  /* fast clk freq */
+       u16 norm_sclk;  /* slow clk freq */
+};
+
+
+/*
+ * Global state variables
+ */
+
+static struct power_state w100_pwr_state;
+
+/* This table is specific for 12.5MHz ref crystal.  */
+static struct pll_parm gPLLTable[] = {
+    /*freq     M   N_int    N_fac  tfgoal  lock_time */
+    { 50,      0,   1,       0,     0xE0,        56}, /*  50.00 MHz */
+    { 75,      0,   5,       0,     0xDE,           37}, /*  75.00 MHz */
+    {100,      0,   7,       0,     0xE0,        28}, /* 100.00 MHz */
+    {125,      0,   9,       0,     0xE0,        22}, /* 125.00 MHz */
+    {150,      0,   11,      0,     0xE0,        17}, /* 150.00 MHz */
+    {  0,      0,   0,       0,        0,         0}  /* Terminator */
+};
+
+
+static u8 w100_pll_get_testcount(u8 testclk_sel)
+{
+       udelay(5);
+
+       w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x0;
+       w100_pwr_state.clk_test_cntl.f.testclk_sel = testclk_sel;
+       w100_pwr_state.clk_test_cntl.f.tstcount_rst = 0x1;      /*reset test count */
+       writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
+       w100_pwr_state.clk_test_cntl.f.tstcount_rst = 0x0;
+       writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
+
+       w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x1;
+       writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
+
+       udelay(20);
+
+       w100_pwr_state.clk_test_cntl.val = readl(remapped_regs + mmCLK_TEST_CNTL);
+       w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x0;
+       writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
+
+       return w100_pwr_state.clk_test_cntl.f.test_count;
+}
+
+
+static u8 w100_pll_adjust(void)
+{
+       do {
+               /* Wai Ming 80 percent of VDD 1.3V gives 1.04V, minimum operating voltage is 1.08V
+                * therefore, commented out the following lines
+                * tf80 meant tf100
+                * set VCO input = 0.8 * VDD
+                */
+               w100_pwr_state.pll_cntl.f.pll_dactal = 0xd;
+               writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+               w100_pwr_state.tf80 = w100_pll_get_testcount(0x1);      /* PLLCLK */
+               if (w100_pwr_state.tf80 >= (w100_pwr_state.tfgoal)) {
+                       /* set VCO input = 0.2 * VDD */
+                       w100_pwr_state.pll_cntl.f.pll_dactal = 0x7;
+                       writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+                       w100_pwr_state.tf20 = w100_pll_get_testcount(0x1);      /* PLLCLK */
+                       if (w100_pwr_state.tf20 <= (w100_pwr_state.tfgoal))
+                               return 1; // Success
+
+                       if ((w100_pwr_state.pll_cntl.f.pll_vcofr == 0x0) &&
+                           ((w100_pwr_state.pll_cntl.f.pll_pvg == 0x7) ||
+                            (w100_pwr_state.pll_cntl.f.pll_ioffset == 0x0))) {
+                               /* slow VCO config */
+                               w100_pwr_state.pll_cntl.f.pll_vcofr = 0x1;
+                               w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
+                               w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
+                               writel((u32) (w100_pwr_state.pll_cntl.val),
+                                       remapped_regs + mmPLL_CNTL);
+                               continue;
+                       }
+               }
+               if ((w100_pwr_state.pll_cntl.f.pll_ioffset) < 0x3) {
+                       w100_pwr_state.pll_cntl.f.pll_ioffset += 0x1;
+                       writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+                       continue;
+               }
+               if ((w100_pwr_state.pll_cntl.f.pll_pvg) < 0x7) {
+                       w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
+                       w100_pwr_state.pll_cntl.f.pll_pvg += 0x1;
+                       writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+                       continue;
+               }
+               return 0; // error
+       } while(1);
+}
+
+
+/*
+ * w100_pll_calibration
+ *                freq = target frequency of the PLL
+ *                (note: crystal = 14.3MHz)
+ */
+static u8 w100_pll_calibration(u32 freq)
+{
+       u8 status;
+
+       /* initial setting */
+       w100_pwr_state.pll_cntl.f.pll_pwdn = 0x0;               /* power down */
+       w100_pwr_state.pll_cntl.f.pll_reset = 0x0;              /* not reset */
+       w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x1;     /* Hi-Z */
+       w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;                /* VCO gain = 0 */
+       w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;              /* VCO frequency range control = off */
+       w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;    /* current offset inside VCO = 0 */
+       w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
+       writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+       /* check for (tf80 >= tfgoal) && (tf20 =< tfgoal) */
+       if ((w100_pwr_state.tf80 < w100_pwr_state.tfgoal) || (w100_pwr_state.tf20 > w100_pwr_state.tfgoal)) {
+               status=w100_pll_adjust();
+       }
+       /* PLL Reset And Lock */
+
+       /* set VCO input = 0.5 * VDD */
+       w100_pwr_state.pll_cntl.f.pll_dactal = 0xa;
+       writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+       /* reset time */
+       udelay(1);
+
+       /* enable charge pump */
+       w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;     /* normal */
+       writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+       /* set VCO input = Hi-Z */
+       /* disable DAC */
+       w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;
+       writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+       /* lock time */
+       udelay(400);    /* delay 400 us */
+
+       /* PLL locked */
+
+       w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x1;  /* PLL clock */
+       writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
+
+       w100_pwr_state.tf100 = w100_pll_get_testcount(0x1);     /* PLLCLK */
+
+       return status;
+}
+
+
+static u8 w100_pll_set_clk(void)
+{
+       u8 status;
+
+       if (w100_pwr_state.auto_mode == 1)      /* auto mode */
+       {
+               w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0; /* disable fast to normal */
+               w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0; /* disable normal to fast */
+               writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
+       }
+
+       w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x0;  /* crystal clock */
+       writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
+
+       w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = w100_pwr_state.M;
+       w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = w100_pwr_state.N_int;
+       w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = w100_pwr_state.N_fac;
+       w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = w100_pwr_state.lock_time;
+       writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
+
+       w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0;
+       writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
+
+       status = w100_pll_calibration (w100_pwr_state.freq);
+
+       if (w100_pwr_state.auto_mode == 1)      /* auto mode */
+       {
+               w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x1; /* reenable fast to normal */
+               w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x1; /* reenable normal to fast  */
+               writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
+       }
+       return status;
+}
+
+
+/* assume reference crystal clk is 12.5MHz,
+ * and that doubling is not enabled.
+ *
+ * Freq = 12 == 12.5MHz.
+ */
+static u16 w100_set_slowsysclk(u16 freq)
+{
+       if (w100_pwr_state.norm_sclk == freq)
+               return freq;
+
+       if (w100_pwr_state.auto_mode == 1)      /* auto mode */
+               return 0;
+
+       if (freq == 12) {
+               w100_pwr_state.norm_sclk = freq;
+               w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0;    /* Pslow = 1 */
+               w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x0;  /* crystal src */
+
+               writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
+
+               w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x1;
+               writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
+
+               w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x1;
+               w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1;
+               writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
+               w100_pwr_state.pwm_mode = 1;    /* normal mode */
+               return freq;
+       } else
+               return 0;
+}
+
+
+static u16 w100_set_fastsysclk(u16 freq)
+{
+       u16 pll_freq;
+       int i;
+
+       while(1) {
+               pll_freq = (u16) (freq * (w100_pwr_state.sclk_cntl.f.sclk_post_div_fast + 1));
+               i = 0;
+               do {
+                       if (pll_freq == gPLLTable[i].freq) {
+                               w100_pwr_state.freq = gPLLTable[i].freq * 1000000;
+                               w100_pwr_state.M = gPLLTable[i].M;
+                               w100_pwr_state.N_int = gPLLTable[i].N_int;
+                               w100_pwr_state.N_fac = gPLLTable[i].N_fac;
+                               w100_pwr_state.tfgoal = gPLLTable[i].tfgoal;
+                               w100_pwr_state.lock_time = gPLLTable[i].lock_time;
+                               w100_pwr_state.tf20 = 0xff;     /* set highest */
+                               w100_pwr_state.tf80 = 0x00;     /* set lowest */
+
+                               w100_pll_set_clk();
+                               w100_pwr_state.pwm_mode = 0;    /* fast mode */
+                               w100_pwr_state.fast_sclk = freq;
+                               return freq;
+                       }
+                       i++;
+               } while(gPLLTable[i].freq);
+
+               if (w100_pwr_state.auto_mode == 1)
+                       break;
+
+               if (w100_pwr_state.sclk_cntl.f.sclk_post_div_fast == 0)
+                       break;
+
+               w100_pwr_state.sclk_cntl.f.sclk_post_div_fast -= 1;
+               writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
+       }
+       return 0;
+}
+
+
+/* Set up an initial state.  Some values/fields set
+   here will be overwritten. */
+static void w100_pwm_setup(void)
+{
+       w100_pwr_state.clk_pin_cntl.f.osc_en = 0x1;
+       w100_pwr_state.clk_pin_cntl.f.osc_gain = 0x1f;
+       w100_pwr_state.clk_pin_cntl.f.dont_use_xtalin = 0x0;
+       w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x0;
+       w100_pwr_state.clk_pin_cntl.f.xtalin_dbl_en = 0x0;      /* no freq doubling */
+       w100_pwr_state.clk_pin_cntl.f.cg_debug = 0x0;
+       writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
+
+       w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x0;  /* Crystal Clk */
+       w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = 0x0;    /* Pfast = 1 */
+       w100_pwr_state.sclk_cntl.f.sclk_clkon_hys = 0x3;
+       w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0;    /* Pslow = 1 */
+       w100_pwr_state.sclk_cntl.f.disp_cg_ok2switch_en = 0x0;
+       w100_pwr_state.sclk_cntl.f.sclk_force_reg = 0x0;        /* Dynamic */
+       w100_pwr_state.sclk_cntl.f.sclk_force_disp = 0x0;       /* Dynamic */
+       w100_pwr_state.sclk_cntl.f.sclk_force_mc = 0x0; /* Dynamic */
+       w100_pwr_state.sclk_cntl.f.sclk_force_extmc = 0x0;      /* Dynamic */
+       w100_pwr_state.sclk_cntl.f.sclk_force_cp = 0x0; /* Dynamic */
+       w100_pwr_state.sclk_cntl.f.sclk_force_e2 = 0x0; /* Dynamic */
+       w100_pwr_state.sclk_cntl.f.sclk_force_e3 = 0x0; /* Dynamic */
+       w100_pwr_state.sclk_cntl.f.sclk_force_idct = 0x0;       /* Dynamic */
+       w100_pwr_state.sclk_cntl.f.sclk_force_bist = 0x0;       /* Dynamic */
+       w100_pwr_state.sclk_cntl.f.busy_extend_cp = 0x0;
+       w100_pwr_state.sclk_cntl.f.busy_extend_e2 = 0x0;
+       w100_pwr_state.sclk_cntl.f.busy_extend_e3 = 0x0;
+       w100_pwr_state.sclk_cntl.f.busy_extend_idct = 0x0;
+       writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
+
+       w100_pwr_state.pclk_cntl.f.pclk_src_sel = 0x0;  /* Crystal Clk */
+       w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x1; /* P = 2 */
+       w100_pwr_state.pclk_cntl.f.pclk_force_disp = 0x0;       /* Dynamic */
+       writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
+
+       w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = 0x0;      /* M = 1 */
+       w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = 0x0;   /* N = 1.0 */
+       w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = 0x0;
+       w100_pwr_state.pll_ref_fb_div.f.pll_reset_time = 0x5;
+       w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = 0xff;
+       writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
+
+       w100_pwr_state.pll_cntl.f.pll_pwdn = 0x1;
+       w100_pwr_state.pll_cntl.f.pll_reset = 0x1;
+       w100_pwr_state.pll_cntl.f.pll_pm_en = 0x0;
+       w100_pwr_state.pll_cntl.f.pll_mode = 0x0;       /* uses VCO clock */
+       w100_pwr_state.pll_cntl.f.pll_refclk_sel = 0x0;
+       w100_pwr_state.pll_cntl.f.pll_fbclk_sel = 0x0;
+       w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0;
+       w100_pwr_state.pll_cntl.f.pll_pcp = 0x4;
+       w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
+       w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0;
+       w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
+       w100_pwr_state.pll_cntl.f.pll_pecc_mode = 0x0;
+       w100_pwr_state.pll_cntl.f.pll_pecc_scon = 0x0;
+       w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;     /* Hi-Z */
+       w100_pwr_state.pll_cntl.f.pll_cp_clip = 0x3;
+       w100_pwr_state.pll_cntl.f.pll_conf = 0x2;
+       w100_pwr_state.pll_cntl.f.pll_mbctrl = 0x2;
+       w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
+       writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+       w100_pwr_state.clk_test_cntl.f.testclk_sel = 0x1;       /* PLLCLK (for testing) */
+       w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x0;
+       w100_pwr_state.clk_test_cntl.f.tstcount_rst = 0x0;
+       writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
+
+       w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x0;
+       w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1;        /* normal mode (0, 1, 3) */
+       w100_pwr_state.pwrmgt_cntl.f.pwm_wakeup_cond = 0x0;
+       w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0;
+       w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0;
+       w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_cond = 0x1;  /* PM4,ENG */
+       w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_cond = 0x1;  /* PM4,ENG */
+       w100_pwr_state.pwrmgt_cntl.f.pwm_idle_timer = 0xFF;
+       w100_pwr_state.pwrmgt_cntl.f.pwm_busy_timer = 0xFF;
+       writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
+
+       w100_pwr_state.auto_mode = 0;   /* manual mode */
+       w100_pwr_state.pwm_mode = 1;    /* normal mode (0, 1, 2) */
+       w100_pwr_state.freq = 50000000; /* 50 MHz */
+       w100_pwr_state.M = 3;   /* M = 4 */
+       w100_pwr_state.N_int = 6;       /* N = 7.0 */
+       w100_pwr_state.N_fac = 0;
+       w100_pwr_state.tfgoal = 0xE0;
+       w100_pwr_state.lock_time = 56;
+       w100_pwr_state.tf20 = 0xff;     /* set highest */
+       w100_pwr_state.tf80 = 0x00;     /* set lowest */
+       w100_pwr_state.tf100 = 0x00;    /* set lowest */
+       w100_pwr_state.fast_sclk = 50;  /* 50.0 MHz */
+       w100_pwr_state.norm_sclk = 12;  /* 12.5 MHz */
+}
+
+
+static void w100_init_sharp_lcd(u32 mode)
+{
+       u32 temp32;
+       union disp_db_buf_cntl_wr_u disp_db_buf_wr_cntl;
+
+       /* Prevent display updates */
+       disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
+       disp_db_buf_wr_cntl.f.update_db_buf = 0;
+       disp_db_buf_wr_cntl.f.en_db_buf = 0;
+       writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
+
+       switch(mode) {
+       case LCD_SHARP_QVGA:
+               w100_set_slowsysclk(12);        /* use crystal -- 12.5MHz */
+               /* not use PLL */
+
+               writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
+               writel(0x85FF8000, remapped_regs + mmMC_FB_LOCATION);
+               writel(0x00000003, remapped_regs + mmLCD_FORMAT);
+               writel(0x00CF1C06, remapped_regs + mmGRAPHIC_CTRL);
+               writel(0x01410145, remapped_regs + mmCRTC_TOTAL);
+               writel(0x01170027, remapped_regs + mmACTIVE_H_DISP);
+               writel(0x01410001, remapped_regs + mmACTIVE_V_DISP);
+               writel(0x01170027, remapped_regs + mmGRAPHIC_H_DISP);
+               writel(0x01410001, remapped_regs + mmGRAPHIC_V_DISP);
+               writel(0x81170027, remapped_regs + mmCRTC_SS);
+               writel(0xA0140000, remapped_regs + mmCRTC_LS);
+               writel(0x00400008, remapped_regs + mmCRTC_REV);
+               writel(0xA0000000, remapped_regs + mmCRTC_DCLK);
+               writel(0xC0140014, remapped_regs + mmCRTC_GS);
+               writel(0x00010141, remapped_regs + mmCRTC_VPOS_GS);
+               writel(0x8015010F, remapped_regs + mmCRTC_GCLK);
+               writel(0x80100110, remapped_regs + mmCRTC_GOE);
+               writel(0x00000000, remapped_regs + mmCRTC_FRAME);
+               writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
+               writel(0x01CC0000, remapped_regs + mmLCDD_CNTL1);
+               writel(0x0003FFFF, remapped_regs + mmLCDD_CNTL2);
+               writel(0x00FFFF0D, remapped_regs + mmGENLCD_CNTL1);
+               writel(0x003F3003, remapped_regs + mmGENLCD_CNTL2);
+               writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
+               writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
+               writel(0x000102aa, remapped_regs + mmGENLCD_CNTL3);
+               writel(0x00800000, remapped_regs + mmGRAPHIC_OFFSET);
+               writel(0x000001e0, remapped_regs + mmGRAPHIC_PITCH);
+               writel(0x000000bf, remapped_regs + mmGPIO_DATA);
+               writel(0x03c0feff, remapped_regs + mmGPIO_CNTL2);
+               writel(0x00000000, remapped_regs + mmGPIO_CNTL1);
+               writel(0x41060010, remapped_regs + mmCRTC_PS1_ACTIVE);
+               break;
+       case LCD_SHARP_VGA:
+               w100_set_slowsysclk(12);        /* use crystal -- 12.5MHz */
+               w100_set_fastsysclk(current_par->fastsysclk_mode);      /* use PLL -- 75.0MHz */
+               w100_pwr_state.pclk_cntl.f.pclk_src_sel = 0x1;
+               w100_pwr_state.pclk_cntl.f.pclk_post_div = 0x2;
+               writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
+               writel(0x15FF1000, remapped_regs + mmMC_FB_LOCATION);
+               writel(0x9FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
+               writel(0x00000003, remapped_regs + mmLCD_FORMAT);
+               writel(0x00DE1D66, remapped_regs + mmGRAPHIC_CTRL);
+
+               writel(0x0283028B, remapped_regs + mmCRTC_TOTAL);
+               writel(0x02360056, remapped_regs + mmACTIVE_H_DISP);
+               writel(0x02830003, remapped_regs + mmACTIVE_V_DISP);
+               writel(0x02360056, remapped_regs + mmGRAPHIC_H_DISP);
+               writel(0x02830003, remapped_regs + mmGRAPHIC_V_DISP);
+               writel(0x82360056, remapped_regs + mmCRTC_SS);
+               writel(0xA0280000, remapped_regs + mmCRTC_LS);
+               writel(0x00400008, remapped_regs + mmCRTC_REV);
+               writel(0xA0000000, remapped_regs + mmCRTC_DCLK);
+               writel(0x80280028, remapped_regs + mmCRTC_GS);
+               writel(0x02830002, remapped_regs + mmCRTC_VPOS_GS);
+               writel(0x8015010F, remapped_regs + mmCRTC_GCLK);
+               writel(0x80100110, remapped_regs + mmCRTC_GOE);
+               writel(0x00000000, remapped_regs + mmCRTC_FRAME);
+               writel(0x00000000, remapped_regs + mmCRTC_FRAME_VPOS);
+               writel(0x01CC0000, remapped_regs + mmLCDD_CNTL1);
+               writel(0x0003FFFF, remapped_regs + mmLCDD_CNTL2);
+               writel(0x00FFFF0D, remapped_regs + mmGENLCD_CNTL1);
+               writel(0x003F3003, remapped_regs + mmGENLCD_CNTL2);
+               writel(0x00000000, remapped_regs + mmCRTC_DEFAULT_COUNT);
+               writel(0x0000FF00, remapped_regs + mmLCD_BACKGROUND_COLOR);
+               writel(0x000102aa, remapped_regs + mmGENLCD_CNTL3);
+               writel(0x00800000, remapped_regs + mmGRAPHIC_OFFSET);
+               writel(0x000003C0, remapped_regs + mmGRAPHIC_PITCH);
+               writel(0x000000bf, remapped_regs + mmGPIO_DATA);
+               writel(0x03c0feff, remapped_regs + mmGPIO_CNTL2);
+               writel(0x00000000, remapped_regs + mmGPIO_CNTL1);
+               writel(0x41060010, remapped_regs + mmCRTC_PS1_ACTIVE);
+               break;
+       default:
+               break;
+       }
+
+       /* Hack for overlay in ext memory */
+       temp32 = readl(remapped_regs + mmDISP_DEBUG2);
+       temp32 |= 0xc0000000;
+       writel(temp32, remapped_regs + mmDISP_DEBUG2);
+
+       /* Re-enable display updates */
+       disp_db_buf_wr_cntl.f.db_buf_cntl = 0x1e;
+       disp_db_buf_wr_cntl.f.update_db_buf = 1;
+       disp_db_buf_wr_cntl.f.en_db_buf = 1;
+       writel((u32) (disp_db_buf_wr_cntl.val), remapped_regs + mmDISP_DB_BUF_CNTL);
+}
+
+
+static void w100_set_vga_rotation_regs(u16 divider, unsigned long ctrl, unsigned long offset, unsigned long pitch)
+{
+       w100_pwr_state.pclk_cntl.f.pclk_src_sel = 0x1;
+       w100_pwr_state.pclk_cntl.f.pclk_post_div = divider;
+       writel((u32) (w100_pwr_state.pclk_cntl.val), remapped_regs + mmPCLK_CNTL);
+
+       writel(ctrl, remapped_regs + mmGRAPHIC_CTRL);
+       writel(offset, remapped_regs + mmGRAPHIC_OFFSET);
+       writel(pitch, remapped_regs + mmGRAPHIC_PITCH);
+
+       /* Re-enable display updates */
+       writel(0x0000007b, remapped_regs + mmDISP_DB_BUF_CNTL);
+}
+
+
+static void w100_init_vga_rotation(u16 deg)
+{
+       switch(deg) {
+       case 0:
+               w100_set_vga_rotation_regs(0x02, 0x00DE1D66, 0x00800000, 0x000003c0);
+               break;
+       case 90:
+               w100_set_vga_rotation_regs(0x06, 0x00DE1D0e, 0x00895b00, 0x00000500);
+               break;
+       case 180:
+               w100_set_vga_rotation_regs(0x02, 0x00DE1D7e, 0x00895ffc, 0x000003c0);
+               break;
+       case 270:
+               w100_set_vga_rotation_regs(0x06, 0x00DE1D16, 0x008004fc, 0x00000500);
+               break;
+       default:
+               /* not-support */
+               break;
+       }
+}
+
+
+static void w100_set_qvga_rotation_regs(unsigned long ctrl, unsigned long offset, unsigned long pitch)
+{
+       writel(ctrl, remapped_regs + mmGRAPHIC_CTRL);
+       writel(offset, remapped_regs + mmGRAPHIC_OFFSET);
+       writel(pitch, remapped_regs + mmGRAPHIC_PITCH);
+
+       /* Re-enable display updates */
+       writel(0x0000007b, remapped_regs + mmDISP_DB_BUF_CNTL);
+}
+
+
+static void w100_init_qvga_rotation(u16 deg)
+{
+       switch(deg) {
+       case 0:
+               w100_set_qvga_rotation_regs(0x00d41c06, 0x00800000, 0x000001e0);
+               break;
+       case 90:
+               w100_set_qvga_rotation_regs(0x00d41c0E, 0x00825580, 0x00000280);
+               break;
+       case 180:
+               w100_set_qvga_rotation_regs(0x00d41c1e, 0x008257fc, 0x000001e0);
+               break;
+       case 270:
+               w100_set_qvga_rotation_regs(0x00d41c16, 0x0080027c, 0x00000280);
+               break;
+       default:
+               /* not-support */
+               break;
+       }
+}
+
+
+static void w100_suspend(u32 mode)
+{
+       u32 val;
+
+       writel(0x7FFF8000, remapped_regs + mmMC_EXT_MEM_LOCATION);
+       writel(0x00FF0000, remapped_regs + mmMC_PERF_MON_CNTL);
+
+       val = readl(remapped_regs + mmMEM_EXT_TIMING_CNTL);
+       val &= ~(0x00100000);   /* bit20=0 */
+       val |= 0xFF000000;      /* bit31:24=0xff */
+       writel(val, remapped_regs + mmMEM_EXT_TIMING_CNTL);
+
+       val = readl(remapped_regs + mmMEM_EXT_CNTL);
+       val &= ~(0x00040000);   /* bit18=0 */
+       val |= 0x00080000;      /* bit19=1 */
+       writel(val, remapped_regs + mmMEM_EXT_CNTL);
+
+       udelay(1);              /* wait 1us */
+
+       if (mode == W100_SUSPEND_EXTMEM) {
+
+               /* CKE: Tri-State */
+               val = readl(remapped_regs + mmMEM_EXT_CNTL);
+               val |= 0x40000000;      /* bit30=1 */
+               writel(val, remapped_regs + mmMEM_EXT_CNTL);
+
+               /* CLK: Stop */
+               val = readl(remapped_regs + mmMEM_EXT_CNTL);
+               val &= ~(0x00000001);   /* bit0=0 */
+               writel(val, remapped_regs + mmMEM_EXT_CNTL);
+       } else {
+
+               writel(0x00000000, remapped_regs + mmSCLK_CNTL);
+               writel(0x000000BF, remapped_regs + mmCLK_PIN_CNTL);
+               writel(0x00000015, remapped_regs + mmPWRMGT_CNTL);
+
+               udelay(5);
+
+               val = readl(remapped_regs + mmPLL_CNTL);
+               val |= 0x00000004;      /* bit2=1 */
+               writel(val, remapped_regs + mmPLL_CNTL);
+               writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
+       }
+}
+
+
+static void w100_resume(void)
+{
+       u32 temp32;
+
+       w100_hw_init();
+       w100_pwm_setup();
+
+       temp32 = readl(remapped_regs + mmDISP_DEBUG2);
+       temp32 &= 0xff7fffff;
+       temp32 |= 0x00800000;
+       writel(temp32, remapped_regs + mmDISP_DEBUG2);
+
+       if (current_par->lcdMode == LCD_MODE_480 || current_par->lcdMode == LCD_MODE_640) {
+               w100_init_sharp_lcd(LCD_SHARP_VGA);
+               if (current_par->lcdMode == LCD_MODE_640) {
+                       w100_init_vga_rotation(current_par->rotation_flag ? 270 : 90);
+               }
+       } else {
+               w100_init_sharp_lcd(LCD_SHARP_QVGA);
+               if (current_par->lcdMode == LCD_MODE_320) {
+                       w100_init_qvga_rotation(current_par->rotation_flag ? 270 : 90);
+               }
+       }
+}
+
+
+static void w100_vsync(void)
+{
+       u32 tmp;
+       int timeout = 30000; /* VSync timeout = 30[ms] > 16.8[ms] */
+
+       tmp = readl(remapped_regs + mmACTIVE_V_DISP);
+
+       /* set vline pos  */
+       writel((tmp >> 16) & 0x3ff, remapped_regs + mmDISP_INT_CNTL);
+
+       /* disable vline irq */
+       tmp = readl(remapped_regs + mmGEN_INT_CNTL);
+
+       tmp &= ~0x00000002;
+       writel(tmp, remapped_regs + mmGEN_INT_CNTL);
+
+       /* clear vline irq status */
+       writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
+
+       /* enable vline irq */
+       writel((tmp | 0x00000002), remapped_regs + mmGEN_INT_CNTL);
+
+       /* clear vline irq status */
+       writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
+
+       while(timeout > 0) {
+               if (readl(remapped_regs + mmGEN_INT_STATUS) & 0x00000002)
+                       break;
+               udelay(1);
+               timeout--;
+       }
+
+       /* disable vline irq */
+       writel(tmp, remapped_regs + mmGEN_INT_CNTL);
+
+       /* clear vline irq status */
+       writel(0x00000002, remapped_regs + mmGEN_INT_STATUS);
+}
+
+
+static void w100_InitExtMem(u32 mode)
+{
+       switch(mode) {
+       case LCD_SHARP_QVGA:
+               /* QVGA doesn't use external memory
+                  nothing to do, really. */
+               break;
+       case LCD_SHARP_VGA:
+               writel(0x00007800, remapped_regs + mmMC_BIST_CTRL);
+               writel(0x00040003, remapped_regs + mmMEM_EXT_CNTL);
+               writel(0x00200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
+               udelay(100);
+               writel(0x80200021, remapped_regs + mmMEM_SDRAM_MODE_REG);
+               udelay(100);
+               writel(0x00650021, remapped_regs + mmMEM_SDRAM_MODE_REG);
+               udelay(100);
+               writel(0x10002a4a, remapped_regs + mmMEM_EXT_TIMING_CNTL);
+               writel(0x7ff87012, remapped_regs + mmMEM_IO_CNTL);
+               break;
+       default:
+               break;
+       }
+}
+
+
+#define RESCTL_ADRS     0x00
+#define PHACTRL_ADRS   0x01
+#define DUTYCTRL_ADRS  0x02
+#define POWERREG0_ADRS 0x03
+#define POWERREG1_ADRS 0x04
+#define GPOR3_ADRS             0x05
+#define PICTRL_ADRS     0x06
+#define POLCTRL_ADRS    0x07
+
+#define RESCTL_QVGA     0x01
+#define RESCTL_VGA      0x00
+
+#define POWER1_VW_ON   0x01    /* VW Supply FET ON */
+#define POWER1_GVSS_ON 0x02    /* GVSS(-8V) Power Supply ON */
+#define POWER1_VDD_ON  0x04    /* VDD(8V),SVSS(-4V) Power Supply ON */
+
+#define POWER1_VW_OFF  0x00    /* VW Supply FET OFF */
+#define POWER1_GVSS_OFF        0x00    /* GVSS(-8V) Power Supply OFF */
+#define POWER1_VDD_OFF 0x00    /* VDD(8V),SVSS(-4V) Power Supply OFF */
+
+#define POWER0_COM_DCLK        0x01    /* COM Voltage DC Bias DAC Serial Data Clock */
+#define POWER0_COM_DOUT        0x02    /* COM Voltage DC Bias DAC Serial Data Out */
+#define POWER0_DAC_ON  0x04    /* DAC Power Supply ON */
+#define POWER0_COM_ON  0x08    /* COM Powewr Supply ON */
+#define POWER0_VCC5_ON 0x10    /* VCC5 Power Supply ON */
+
+#define POWER0_DAC_OFF 0x00    /* DAC Power Supply OFF */
+#define POWER0_COM_OFF 0x00    /* COM Powewr Supply OFF */
+#define POWER0_VCC5_OFF        0x00    /* VCC5 Power Supply OFF */
+
+#define PICTRL_INIT_STATE      0x01
+#define PICTRL_INIOFF          0x02
+#define PICTRL_POWER_DOWN      0x04
+#define PICTRL_COM_SIGNAL_OFF  0x08
+#define PICTRL_DAC_SIGNAL_OFF  0x10
+
+#define PICTRL_POWER_ACTIVE    (0)
+
+#define POLCTRL_SYNC_POL_FALL  0x01
+#define POLCTRL_EN_POL_FALL    0x02
+#define POLCTRL_DATA_POL_FALL  0x04
+#define POLCTRL_SYNC_ACT_H     0x08
+#define POLCTRL_EN_ACT_L       0x10
+
+#define POLCTRL_SYNC_POL_RISE  0x00
+#define POLCTRL_EN_POL_RISE    0x00
+#define POLCTRL_DATA_POL_RISE  0x00
+#define POLCTRL_SYNC_ACT_L     0x00
+#define POLCTRL_EN_ACT_H       0x00
+
+#define PHACTRL_PHASE_MANUAL   0x01
+
+#define PHAD_QVGA_DEFAULT_VAL (9)
+#define COMADJ_DEFAULT        (125)
+
+static void lcdtg_ssp_send(u8 adrs, u8 data)
+{
+       w100fb_ssp_send(adrs,data);
+}
+
+/*
+ * This is only a psuedo I2C interface. We can't use the standard kernel
+ * routines as the interface is write only. We just assume the data is acked...
+ */
+static void lcdtg_ssp_i2c_send(u8 data)
+{
+       lcdtg_ssp_send(POWERREG0_ADRS, data);
+       udelay(10);
+}
+
+static void lcdtg_i2c_send_bit(u8 data)
+{
+       lcdtg_ssp_i2c_send(data);
+       lcdtg_ssp_i2c_send(data | POWER0_COM_DCLK);
+       lcdtg_ssp_i2c_send(data);
+}
+
+static void lcdtg_i2c_send_start(u8 base)
+{
+       lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK | POWER0_COM_DOUT);
+       lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK);
+       lcdtg_ssp_i2c_send(base);
+}
+
+static void lcdtg_i2c_send_stop(u8 base)
+{
+       lcdtg_ssp_i2c_send(base);
+       lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK);
+       lcdtg_ssp_i2c_send(base | POWER0_COM_DCLK | POWER0_COM_DOUT);
+}
+
+static void lcdtg_i2c_send_byte(u8 base, u8 data)
+{
+       int i;
+       for (i = 0; i < 8; i++) {
+               if (data & 0x80)
+                       lcdtg_i2c_send_bit(base | POWER0_COM_DOUT);
+               else
+                       lcdtg_i2c_send_bit(base);
+               data <<= 1;
+       }
+}
+
+static void lcdtg_i2c_wait_ack(u8 base)
+{
+       lcdtg_i2c_send_bit(base);
+}
+
+static void lcdtg_set_common_voltage(u8 base_data, u8 data)
+{
+       /* Set Common Voltage to M62332FP via I2C */
+       lcdtg_i2c_send_start(base_data);
+       lcdtg_i2c_send_byte(base_data, 0x9c);
+       lcdtg_i2c_wait_ack(base_data);
+       lcdtg_i2c_send_byte(base_data, 0x00);
+       lcdtg_i2c_wait_ack(base_data);
+       lcdtg_i2c_send_byte(base_data, data);
+       lcdtg_i2c_wait_ack(base_data);
+       lcdtg_i2c_send_stop(base_data);
+}
+
+static struct lcdtg_register_setting {
+       u8 adrs;
+       u8 data;
+       u32 wait;
+} lcdtg_power_on_table[] = {
+
+    /* Initialize Internal Logic & Port */
+    { PICTRL_ADRS,
+      PICTRL_POWER_DOWN | PICTRL_INIOFF | PICTRL_INIT_STATE |
+      PICTRL_COM_SIGNAL_OFF | PICTRL_DAC_SIGNAL_OFF,
+      0 },
+
+    { POWERREG0_ADRS,
+      POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF | POWER0_COM_OFF |
+      POWER0_VCC5_OFF,
+      0 },
+
+    { POWERREG1_ADRS,
+      POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF,
+      0 },
+
+    /* VDD(+8V),SVSS(-4V) ON */
+    { POWERREG1_ADRS,
+      POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON /* VDD ON */,
+      3000 },
+
+    /* DAC ON */
+    { POWERREG0_ADRS,
+      POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON /* DAC ON */ |
+      POWER0_COM_OFF | POWER0_VCC5_OFF,
+      0 },
+
+    /* INIB = H, INI = L  */
+    { PICTRL_ADRS,
+      /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */
+      PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF,
+      0 },
+
+    /* Set Common Voltage */
+    { 0xfe, 0, 0 },
+
+    /* VCC5 ON */
+    { POWERREG0_ADRS,
+      POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON /* DAC ON */ |
+      POWER0_COM_OFF | POWER0_VCC5_ON /* VCC5 ON */,
+      0 },
+
+    /* GVSS(-8V) ON */
+    { POWERREG1_ADRS,
+      POWER1_VW_OFF | POWER1_GVSS_ON /* GVSS ON */ |
+      POWER1_VDD_ON /* VDD ON */,
+      2000 },
+
+    /* COM SIGNAL ON (PICTL[3] = L) */
+    { PICTRL_ADRS,
+      PICTRL_INIT_STATE,
+      0 },
+
+    /* COM ON */
+    { POWERREG0_ADRS,
+      POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON /* DAC ON */ |
+      POWER0_COM_ON /* COM ON */ | POWER0_VCC5_ON /* VCC5_ON */,
+      0 },
+
+    /* VW ON */
+    { POWERREG1_ADRS,
+      POWER1_VW_ON /* VW ON */ | POWER1_GVSS_ON /* GVSS ON */ |
+      POWER1_VDD_ON /* VDD ON */,
+      0 /* Wait 100ms */ },
+
+    /* Signals output enable */
+    { PICTRL_ADRS,
+      0 /* Signals output enable */,
+      0 },
+
+    { PHACTRL_ADRS,
+      PHACTRL_PHASE_MANUAL,
+      0 },
+
+    /* Initialize for Input Signals from ATI */
+    { POLCTRL_ADRS,
+      POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE | POLCTRL_DATA_POL_RISE |
+      POLCTRL_SYNC_ACT_L | POLCTRL_EN_ACT_H,
+      1000 /*100000*/ /* Wait 100ms */ },
+
+    /* end mark */
+    { 0xff, 0, 0 }
+};
+
+static void lcdtg_resume(void)
+{
+       if (current_par->lcdMode == LCD_MODE_480 || current_par->lcdMode == LCD_MODE_640) {
+               lcdtg_hw_init(LCD_SHARP_VGA);
+       } else {
+               lcdtg_hw_init(LCD_SHARP_QVGA);
+       }
+}
+
+static void lcdtg_suspend(void)
+{
+       int i;
+
+       for (i = 0; i < (current_par->xres * current_par->yres); i++) {
+               writew(0xffff, remapped_fbuf + (2*i));
+       }
+
+       /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */
+       mdelay(34);
+
+       /* (1)VW OFF */
+       lcdtg_ssp_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON);
+
+       /* (2)COM OFF */
+       lcdtg_ssp_send(PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF);
+       lcdtg_ssp_send(POWERREG0_ADRS, POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON);
+
+       /* (3)Set Common Voltage Bias 0V */
+       lcdtg_set_common_voltage(POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON, 0);
+
+       /* (4)GVSS OFF */
+       lcdtg_ssp_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON);
+
+       /* (5)VCC5 OFF */
+       lcdtg_ssp_send(POWERREG0_ADRS, POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF);
+
+       /* (6)Set PDWN, INIOFF, DACOFF */
+       lcdtg_ssp_send(PICTRL_ADRS, PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF |
+                       PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF);
+
+       /* (7)DAC OFF */
+       lcdtg_ssp_send(POWERREG0_ADRS, POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF);
+
+       /* (8)VDD OFF */
+       lcdtg_ssp_send(POWERREG1_ADRS, POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF);
+
+}
+
+static void lcdtg_set_phadadj(u32 mode)
+{
+       int adj;
+
+       if (mode == LCD_SHARP_VGA) {
+               /* Setting for VGA */
+               adj = current_par->phadadj;
+               if (adj < 0) {
+                       adj = PHACTRL_PHASE_MANUAL;
+               } else {
+                       adj = ((adj & 0x0f) << 1) | PHACTRL_PHASE_MANUAL;
+               }
+       } else {
+               /* Setting for QVGA */
+               adj = (PHAD_QVGA_DEFAULT_VAL << 1) | PHACTRL_PHASE_MANUAL;
+       }
+       lcdtg_ssp_send(PHACTRL_ADRS, adj);
+}
+
+static void lcdtg_hw_init(u32 mode)
+{
+       int i;
+       int comadj;
+
+       i = 0;
+       while(lcdtg_power_on_table[i].adrs != 0xff) {
+               if (lcdtg_power_on_table[i].adrs == 0xfe) {
+                       /* Set Common Voltage */
+                       comadj = current_par->comadj;
+                       if (comadj < 0) {
+                               comadj = COMADJ_DEFAULT;
+                       }
+                       lcdtg_set_common_voltage((POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF), comadj);
+               } else if (lcdtg_power_on_table[i].adrs == PHACTRL_ADRS) {
+                       /* Set Phase Adjuct */
+                       lcdtg_set_phadadj(mode);
+               } else {
+                       /* Other */
+                       lcdtg_ssp_send(lcdtg_power_on_table[i].adrs, lcdtg_power_on_table[i].data);
+               }
+               if (lcdtg_power_on_table[i].wait != 0)
+                       udelay(lcdtg_power_on_table[i].wait);
+               i++;
+       }
+
+       switch(mode) {
+       case LCD_SHARP_QVGA:
+               /* Set Lcd Resolution (QVGA) */
+               lcdtg_ssp_send(RESCTL_ADRS, RESCTL_QVGA);
+               break;
+       case LCD_SHARP_VGA:
+               /* Set Lcd Resolution (VGA) */
+               lcdtg_ssp_send(RESCTL_ADRS, RESCTL_VGA);
+               break;
+       default:
+               break;
+       }
+}
+
+static void lcdtg_lcd_change(u32 mode)
+{
+       /* Set Phase Adjuct */
+       lcdtg_set_phadadj(mode);
+
+       if (mode == LCD_SHARP_VGA)
+               /* Set Lcd Resolution (VGA) */
+               lcdtg_ssp_send(RESCTL_ADRS, RESCTL_VGA);
+       else if (mode == LCD_SHARP_QVGA)
+               /* Set Lcd Resolution (QVGA) */
+               lcdtg_ssp_send(RESCTL_ADRS, RESCTL_QVGA);
+}
+
+
+static struct device_driver w100fb_driver = {
+       .name           = "w100fb",
+       .bus            = &platform_bus_type,
+       .probe          = w100fb_probe,
+       .remove         = w100fb_remove,
+       .suspend        = w100fb_suspend,
+       .resume         = w100fb_resume,
+};
+
+int __devinit w100fb_init(void)
+{
+       return driver_register(&w100fb_driver);
+}
+
+void __exit w100fb_cleanup(void)
+{
+       driver_unregister(&w100fb_driver);
+}
+
+module_init(w100fb_init);
+module_exit(w100fb_cleanup);
+
+MODULE_DESCRIPTION("ATI Imageon w100 framebuffer driver");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/video/w100fb.h b/drivers/video/w100fb.h
new file mode 100644 (file)
index 0000000..41624f9
--- /dev/null
@@ -0,0 +1,615 @@
+/*
+ * linux/drivers/video/w100fb.h
+ *
+ * Frame Buffer Device for ATI w100 (Wallaby)
+ *
+ * Copyright (C) 2002, ATI Corp.
+ * Copyright (C) 2004-2005 Richard Purdie
+ *
+ * Modified to work with 2.6 by Richard Purdie <rpurdie@rpsys.net>
+ *
+ * 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.
+ *
+ */
+
+#if !defined (_W100FB_H)
+#define _W100FB_H
+
+/* Block CIF Start: */
+#define mmCHIP_ID           0x0000
+#define mmREVISION_ID          0x0004
+#define mmWRAP_BUF_A        0x0008
+#define mmWRAP_BUF_B        0x000C
+#define mmWRAP_TOP_DIR      0x0010
+#define mmWRAP_START_DIR    0x0014
+#define mmCIF_CNTL          0x0018
+#define mmCFGREG_BASE       0x001C
+#define mmCIF_IO            0x0020
+#define mmCIF_READ_DBG      0x0024
+#define mmCIF_WRITE_DBG     0x0028
+#define cfgIND_ADDR_A_0     0x0000
+#define cfgIND_ADDR_A_1     0x0001
+#define cfgIND_ADDR_A_2     0x0002
+#define cfgIND_DATA_A       0x0003
+#define cfgREG_BASE         0x0004
+#define cfgINTF_CNTL        0x0005
+#define cfgSTATUS           0x0006
+#define cfgCPU_DEFAULTS     0x0007
+#define cfgIND_ADDR_B_0     0x0008
+#define cfgIND_ADDR_B_1     0x0009
+#define cfgIND_ADDR_B_2     0x000A
+#define cfgIND_DATA_B       0x000B
+#define cfgPM4_RPTR         0x000C
+#define cfgSCRATCH          0x000D
+#define cfgPM4_WRPTR_0      0x000E
+#define cfgPM4_WRPTR_1      0x000F
+/* Block CIF End: */
+
+/* Block CP Start: */
+#define mmSCRATCH_UMSK      0x0280
+#define mmSCRATCH_ADDR      0x0284
+#define mmGEN_INT_CNTL      0x0200
+#define mmGEN_INT_STATUS    0x0204
+/* Block CP End: */
+
+/* Block DISPLAY Start: */
+#define mmLCD_FORMAT        0x0410
+#define mmGRAPHIC_CTRL      0x0414
+#define mmGRAPHIC_OFFSET    0x0418
+#define mmGRAPHIC_PITCH     0x041C
+#define mmCRTC_TOTAL        0x0420
+#define mmACTIVE_H_DISP     0x0424
+#define mmACTIVE_V_DISP     0x0428
+#define mmGRAPHIC_H_DISP    0x042C
+#define mmGRAPHIC_V_DISP    0x0430
+#define mmVIDEO_CTRL        0x0434
+#define mmGRAPHIC_KEY       0x0438
+#define mmBRIGHTNESS_CNTL   0x045C
+#define mmDISP_INT_CNTL     0x0488
+#define mmCRTC_SS           0x048C
+#define mmCRTC_LS           0x0490
+#define mmCRTC_REV          0x0494
+#define mmCRTC_DCLK         0x049C
+#define mmCRTC_GS           0x04A0
+#define mmCRTC_VPOS_GS      0x04A4
+#define mmCRTC_GCLK         0x04A8
+#define mmCRTC_GOE          0x04AC
+#define mmCRTC_FRAME        0x04B0
+#define mmCRTC_FRAME_VPOS   0x04B4
+#define mmGPIO_DATA         0x04B8
+#define mmGPIO_CNTL1        0x04BC
+#define mmGPIO_CNTL2        0x04C0
+#define mmLCDD_CNTL1        0x04C4
+#define mmLCDD_CNTL2        0x04C8
+#define mmGENLCD_CNTL1      0x04CC
+#define mmGENLCD_CNTL2      0x04D0
+#define mmDISP_DEBUG        0x04D4
+#define mmDISP_DB_BUF_CNTL  0x04D8
+#define mmDISP_CRC_SIG      0x04DC
+#define mmCRTC_DEFAULT_COUNT   0x04E0
+#define mmLCD_BACKGROUND_COLOR  0x04E4
+#define mmCRTC_PS2          0x04E8
+#define mmCRTC_PS2_VPOS     0x04EC
+#define mmCRTC_PS1_ACTIVE   0x04F0
+#define mmCRTC_PS1_NACTIVE  0x04F4
+#define mmCRTC_GCLK_EXT     0x04F8
+#define mmCRTC_ALW          0x04FC
+#define mmCRTC_ALW_VPOS     0x0500
+#define mmCRTC_PSK          0x0504
+#define mmCRTC_PSK_HPOS     0x0508
+#define mmCRTC_CV4_START    0x050C
+#define mmCRTC_CV4_END      0x0510
+#define mmCRTC_CV4_HPOS     0x0514
+#define mmCRTC_ECK          0x051C
+#define mmREFRESH_CNTL      0x0520
+#define mmGENLCD_CNTL3      0x0524
+#define mmGPIO_DATA2        0x0528
+#define mmGPIO_CNTL3        0x052C
+#define mmGPIO_CNTL4        0x0530
+#define mmCHIP_STRAP        0x0534
+#define mmDISP_DEBUG2       0x0538
+#define mmDEBUG_BUS_CNTL    0x053C
+#define mmGAMMA_VALUE1      0x0540
+#define mmGAMMA_VALUE2      0x0544
+#define mmGAMMA_SLOPE       0x0548
+#define mmGEN_STATUS        0x054C
+#define mmHW_INT            0x0550
+/* Block DISPLAY End: */
+
+/* Block GFX Start: */
+#define mmBRUSH_OFFSET      0x108C
+#define mmBRUSH_Y_X         0x1074
+#define mmDEFAULT_PITCH_OFFSET         0x10A0
+#define mmDEFAULT_SC_BOTTOM_RIGHT      0x10A8
+#define mmDEFAULT2_SC_BOTTOM_RIGHT     0x10AC
+#define mmGLOBAL_ALPHA      0x1210
+#define mmFILTER_COEF       0x1214
+#define mmMVC_CNTL_START    0x11E0
+#define mmE2_ARITHMETIC_CNTL   0x1220
+#define mmENG_CNTL          0x13E8
+#define mmENG_PERF_CNT      0x13F0
+/* Block GFX End: */
+
+/* Block IDCT Start: */
+#define mmIDCT_RUNS         0x0C00
+#define mmIDCT_LEVELS       0x0C04
+#define mmIDCT_CONTROL      0x0C3C
+#define mmIDCT_AUTH_CONTROL 0x0C08
+#define mmIDCT_AUTH         0x0C0C
+/* Block IDCT End: */
+
+/* Block MC Start: */
+#define mmMEM_CNTL          0x0180
+#define mmMEM_ARB           0x0184
+#define mmMC_FB_LOCATION    0x0188
+#define mmMEM_EXT_CNTL      0x018C
+#define mmMC_EXT_MEM_LOCATION   0x0190
+#define mmMEM_EXT_TIMING_CNTL   0x0194
+#define mmMEM_SDRAM_MODE_REG   0x0198
+#define mmMEM_IO_CNTL       0x019C
+#define mmMC_DEBUG          0x01A0
+#define mmMC_BIST_CTRL      0x01A4
+#define mmMC_BIST_COLLAR_READ          0x01A8
+#define mmTC_MISMATCH       0x01AC
+#define mmMC_PERF_MON_CNTL  0x01B0
+#define mmMC_PERF_COUNTERS  0x01B4
+/* Block MC End: */
+
+/* Block RBBM Start: */
+#define mmWAIT_UNTIL        0x1400
+#define mmISYNC_CNTL        0x1404
+#define mmRBBM_CNTL         0x0144
+#define mmNQWAIT_UNTIL      0x0150
+/* Block RBBM End: */
+
+/* Block CG Start: */
+#define mmCLK_PIN_CNTL      0x0080
+#define mmPLL_REF_FB_DIV    0x0084
+#define mmPLL_CNTL          0x0088
+#define mmSCLK_CNTL         0x008C
+#define mmPCLK_CNTL         0x0090
+#define mmCLK_TEST_CNTL     0x0094
+#define mmPWRMGT_CNTL       0x0098
+#define mmPWRMGT_STATUS     0x009C
+/* Block CG End: */
+
+/* default value definitions */
+#define defWRAP_TOP_DIR     0x00000000
+#define defWRAP_START_DIR      0x00000000
+#define defCFGREG_BASE      0x00000000
+#define defCIF_IO           0x000C0902
+#define defINTF_CNTL        0x00000011
+#define defCPU_DEFAULTS     0x00000006
+#define defHW_INT           0x00000000
+#define defMC_EXT_MEM_LOCATION            0x07ff0000
+#define defTC_MISMATCH      0x00000000
+
+#define W100_CFG_BASE          0x0
+#define W100_CFG_LEN           0x10
+#define W100_REG_BASE          0x10000
+#define W100_REG_LEN           0x2000
+#define MEM_INT_BASE_VALUE     0x100000
+#define MEM_INT_TOP_VALUE_W100 0x15ffff
+#define MEM_EXT_BASE_VALUE     0x800000
+#define MEM_EXT_TOP_VALUE      0x9fffff
+#define WRAP_BUF_BASE_VALUE    0x80000
+#define WRAP_BUF_TOP_VALUE     0xbffff
+
+
+/* data structure definitions */
+
+struct wrap_top_dir_t {
+     unsigned long top_addr         : 23;
+     unsigned long                             : 9;
+} __attribute__((packed));
+
+union wrap_top_dir_u {
+     unsigned long val : 32;
+     struct wrap_top_dir_t f;
+} __attribute__((packed));
+
+struct wrap_start_dir_t {
+     unsigned long start_addr       : 23;
+     unsigned long                             : 9;
+} __attribute__((packed));
+
+union wrap_start_dir_u {
+     unsigned long val : 32;
+     struct wrap_start_dir_t f;
+} __attribute__((packed));
+
+struct cif_cntl_t {
+     unsigned long swap_reg                    : 2;
+     unsigned long swap_fbuf_1                 : 2;
+     unsigned long swap_fbuf_2                 : 2;
+     unsigned long swap_fbuf_3                 : 2;
+     unsigned long pmi_int_disable             : 1;
+     unsigned long pmi_schmen_disable       : 1;
+     unsigned long intb_oe                     : 1;
+     unsigned long en_wait_to_compensate_dq_prop_dly : 1;
+     unsigned long compensate_wait_rd_size  : 2;
+     unsigned long wait_asserted_timeout_val      : 2;
+     unsigned long wait_masked_val             : 2;
+     unsigned long en_wait_timeout             : 1;
+     unsigned long en_one_clk_setup_before_wait   : 1;
+     unsigned long interrupt_active_high    : 1;
+     unsigned long en_overwrite_straps      : 1;
+     unsigned long strap_wait_active_hi     : 1;
+     unsigned long lat_busy_count              : 2;
+     unsigned long lat_rd_pm4_sclk_busy     : 1;
+     unsigned long dis_system_bits             : 1;
+     unsigned long dis_mr                      : 1;
+     unsigned long cif_spare_1                 : 4;
+} __attribute__((packed));
+
+union cif_cntl_u {
+     unsigned long val : 32;
+     struct cif_cntl_t f;
+} __attribute__((packed));
+
+struct cfgreg_base_t {
+     unsigned long cfgreg_base      : 24;
+     unsigned long                             : 8;
+} __attribute__((packed));
+
+union cfgreg_base_u {
+     unsigned long val : 32;
+     struct cfgreg_base_t f;
+} __attribute__((packed));
+
+struct cif_io_t {
+     unsigned long dq_srp           : 1;
+     unsigned long dq_srn           : 1;
+     unsigned long dq_sp            : 4;
+     unsigned long dq_sn            : 4;
+     unsigned long waitb_srp        : 1;
+     unsigned long waitb_srn        : 1;
+     unsigned long waitb_sp         : 4;
+     unsigned long waitb_sn         : 4;
+     unsigned long intb_srp         : 1;
+     unsigned long intb_srn         : 1;
+     unsigned long intb_sp          : 4;
+     unsigned long intb_sn          : 4;
+     unsigned long                             : 2;
+} __attribute__((packed));
+
+union cif_io_u {
+     unsigned long val : 32;
+     struct cif_io_t f;
+} __attribute__((packed));
+
+struct cif_read_dbg_t {
+     unsigned long unpacker_pre_fetch_trig_gen  : 2;
+     unsigned long dly_second_rd_fetch_trig     : 1;
+     unsigned long rst_rd_burst_id                     : 1;
+     unsigned long dis_rd_burst_id                     : 1;
+     unsigned long en_block_rd_when_packer_is_not_emp : 1;
+     unsigned long dis_pre_fetch_cntl_sm        : 1;
+     unsigned long rbbm_chrncy_dis                     : 1;
+     unsigned long rbbm_rd_after_wr_lat         : 2;
+     unsigned long dis_be_during_rd                    : 1;
+     unsigned long one_clk_invalidate_pulse     : 1;
+     unsigned long dis_chnl_priority                   : 1;
+     unsigned long rst_read_path_a_pls          : 1;
+     unsigned long rst_read_path_b_pls          : 1;
+     unsigned long dis_reg_rd_fetch_trig        : 1;
+     unsigned long dis_rd_fetch_trig_from_ind_addr : 1;
+     unsigned long dis_rd_same_byte_to_trig_fetch : 1;
+     unsigned long dis_dir_wrap                        : 1;
+     unsigned long dis_ring_buf_to_force_dec    : 1;
+     unsigned long dis_addr_comp_in_16bit       : 1;
+     unsigned long clr_w                               : 1;
+     unsigned long err_rd_tag_is_3                     : 1;
+     unsigned long err_load_when_ful_a          : 1;
+     unsigned long err_load_when_ful_b          : 1;
+     unsigned long                                                     : 7;
+} __attribute__((packed));
+
+union cif_read_dbg_u {
+     unsigned long val : 32;
+     struct cif_read_dbg_t f;
+} __attribute__((packed));
+
+struct cif_write_dbg_t {
+     unsigned long packer_timeout_count           : 2;
+     unsigned long en_upper_load_cond             : 1;
+     unsigned long en_chnl_change_cond            : 1;
+     unsigned long dis_addr_comp_cond             : 1;
+     unsigned long dis_load_same_byte_addr_cond   : 1;
+     unsigned long dis_timeout_cond                      : 1;
+     unsigned long dis_timeout_during_rbbm        : 1;
+     unsigned long dis_packer_ful_during_rbbm_timeout : 1;
+     unsigned long en_dword_split_to_rbbm         : 1;
+     unsigned long en_dummy_val                          : 1;
+     unsigned long dummy_val_sel                         : 1;
+     unsigned long mask_pm4_wrptr_dec             : 1;
+     unsigned long dis_mc_clean_cond                     : 1;
+     unsigned long err_two_reqi_during_ful        : 1;
+     unsigned long err_reqi_during_idle_clk       : 1;
+     unsigned long err_global                            : 1;
+     unsigned long en_wr_buf_dbg_load             : 1;
+     unsigned long en_wr_buf_dbg_path             : 1;
+     unsigned long sel_wr_buf_byte                       : 3;
+     unsigned long dis_rd_flush_wr                       : 1;
+     unsigned long dis_packer_ful_cond            : 1;
+     unsigned long dis_invalidate_by_ops_chnl     : 1;
+     unsigned long en_halt_when_reqi_err          : 1;
+     unsigned long cif_spare_2                           : 5;
+     unsigned long                                                       : 1;
+} __attribute__((packed));
+
+union cif_write_dbg_u {
+     unsigned long val : 32;
+     struct cif_write_dbg_t f;
+} __attribute__((packed));
+
+
+struct intf_cntl_t {
+     unsigned char ad_inc_a            : 1;
+     unsigned char ring_buf_a          : 1;
+     unsigned char rd_fetch_trigger_a  : 1;
+     unsigned char rd_data_rdy_a       : 1;
+     unsigned char ad_inc_b            : 1;
+     unsigned char ring_buf_b          : 1;
+     unsigned char rd_fetch_trigger_b  : 1;
+     unsigned char rd_data_rdy_b       : 1;
+} __attribute__((packed));
+
+union intf_cntl_u {
+     unsigned char val : 8;
+     struct intf_cntl_t f;
+} __attribute__((packed));
+
+struct cpu_defaults_t {
+     unsigned char unpack_rd_data   : 1;
+     unsigned char access_ind_addr_a: 1;
+     unsigned char access_ind_addr_b: 1;
+     unsigned char access_scratch_reg             : 1;
+     unsigned char pack_wr_data     : 1;
+     unsigned char transition_size  : 1;
+     unsigned char en_read_buf_mode : 1;
+     unsigned char rd_fetch_scratch : 1;
+} __attribute__((packed));
+
+union cpu_defaults_u {
+     unsigned char val : 8;
+     struct cpu_defaults_t f;
+} __attribute__((packed));
+
+struct video_ctrl_t {
+     unsigned long video_mode       : 1;
+     unsigned long keyer_en         : 1;
+     unsigned long en_video_req     : 1;
+     unsigned long en_graphic_req_video           : 1;
+     unsigned long en_video_crtc    : 1;
+     unsigned long video_hor_exp    : 2;
+     unsigned long video_ver_exp    : 2;
+     unsigned long uv_combine       : 1;
+     unsigned long total_req_video  : 9;
+     unsigned long video_ch_sel     : 1;
+     unsigned long video_portrait   : 2;
+     unsigned long yuv2rgb_en       : 1;
+     unsigned long yuv2rgb_option   : 1;
+     unsigned long video_inv_hor    : 1;
+     unsigned long video_inv_ver    : 1;
+     unsigned long gamma_sel        : 2;
+     unsigned long dis_limit        : 1;
+     unsigned long en_uv_hblend     : 1;
+     unsigned long rgb_gamma_sel    : 2;
+} __attribute__((packed));
+
+union video_ctrl_u {
+     unsigned long val : 32;
+     struct video_ctrl_t f;
+} __attribute__((packed));
+
+struct disp_db_buf_cntl_rd_t {
+     unsigned long en_db_buf           : 1;
+     unsigned long update_db_buf_done   : 1;
+     unsigned long db_buf_cntl         : 6;
+     unsigned long                                     : 24;
+} __attribute__((packed));
+
+union disp_db_buf_cntl_rd_u {
+     unsigned long val : 32;
+     struct disp_db_buf_cntl_rd_t f;
+} __attribute__((packed));
+
+struct disp_db_buf_cntl_wr_t {
+     unsigned long en_db_buf        : 1;
+     unsigned long update_db_buf    : 1;
+     unsigned long db_buf_cntl      : 6;
+     unsigned long    : 24;
+} __attribute__((packed));
+
+union disp_db_buf_cntl_wr_u {
+     unsigned long val : 32;
+     struct disp_db_buf_cntl_wr_t f;
+} __attribute__((packed));
+
+struct gamma_value1_t {
+     unsigned long gamma1           : 8;
+     unsigned long gamma2           : 8;
+     unsigned long gamma3           : 8;
+     unsigned long gamma4           : 8;
+} __attribute__((packed));
+
+union gamma_value1_u {
+     unsigned long val : 32;
+     struct gamma_value1_t f;
+} __attribute__((packed));
+
+struct gamma_value2_t {
+     unsigned long gamma5           : 8;
+     unsigned long gamma6           : 8;
+     unsigned long gamma7           : 8;
+     unsigned long gamma8           : 8;
+} __attribute__((packed));
+
+union gamma_value2_u {
+     unsigned long val : 32;
+     struct gamma_value2_t f;
+} __attribute__((packed));
+
+struct gamma_slope_t {
+     unsigned long slope1           : 3;
+     unsigned long slope2           : 3;
+     unsigned long slope3           : 3;
+     unsigned long slope4           : 3;
+     unsigned long slope5           : 3;
+     unsigned long slope6           : 3;
+     unsigned long slope7           : 3;
+     unsigned long slope8           : 3;
+     unsigned long                             : 8;
+} __attribute__((packed));
+
+union gamma_slope_u {
+     unsigned long val : 32;
+     struct gamma_slope_t f;
+} __attribute__((packed));
+
+struct mc_ext_mem_location_t {
+     unsigned long mc_ext_mem_start : 16;
+     unsigned long mc_ext_mem_top   : 16;
+} __attribute__((packed));
+
+union mc_ext_mem_location_u {
+     unsigned long val : 32;
+     struct mc_ext_mem_location_t f;
+} __attribute__((packed));
+
+struct clk_pin_cntl_t {
+     unsigned long osc_en           : 1;
+     unsigned long osc_gain         : 5;
+     unsigned long dont_use_xtalin  : 1;
+     unsigned long xtalin_pm_en     : 1;
+     unsigned long xtalin_dbl_en    : 1;
+     unsigned long                             : 7;
+     unsigned long cg_debug         : 16;
+} __attribute__((packed));
+
+union clk_pin_cntl_u {
+     unsigned long val : 32;
+     struct clk_pin_cntl_t f;
+} __attribute__((packed));
+
+struct pll_ref_fb_div_t {
+     unsigned long pll_ref_div      : 4;
+     unsigned long                             : 4;
+     unsigned long pll_fb_div_int   : 6;
+     unsigned long                             : 2;
+     unsigned long pll_fb_div_frac  : 3;
+     unsigned long                             : 1;
+     unsigned long pll_reset_time   : 4;
+     unsigned long pll_lock_time    : 8;
+} __attribute__((packed));
+
+union pll_ref_fb_div_u {
+     unsigned long val : 32;
+     struct pll_ref_fb_div_t f;
+} __attribute__((packed));
+
+struct pll_cntl_t {
+     unsigned long pll_pwdn         : 1;
+     unsigned long pll_reset        : 1;
+     unsigned long pll_pm_en        : 1;
+     unsigned long pll_mode         : 1;
+     unsigned long pll_refclk_sel   : 1;
+     unsigned long pll_fbclk_sel    : 1;
+     unsigned long pll_tcpoff       : 1;
+     unsigned long pll_pcp          : 3;
+     unsigned long pll_pvg          : 3;
+     unsigned long pll_vcofr        : 1;
+     unsigned long pll_ioffset      : 2;
+     unsigned long pll_pecc_mode    : 2;
+     unsigned long pll_pecc_scon    : 2;
+     unsigned long pll_dactal       : 4;
+     unsigned long pll_cp_clip      : 2;
+     unsigned long pll_conf         : 3;
+     unsigned long pll_mbctrl       : 2;
+     unsigned long pll_ring_off     : 1;
+} __attribute__((packed));
+
+union pll_cntl_u {
+     unsigned long val : 32;
+     struct pll_cntl_t f;
+} __attribute__((packed));
+
+struct sclk_cntl_t {
+     unsigned long sclk_src_sel        : 2;
+     unsigned long                                     : 2;
+     unsigned long sclk_post_div_fast   : 4;
+     unsigned long sclk_clkon_hys      : 3;
+     unsigned long sclk_post_div_slow   : 4;
+     unsigned long disp_cg_ok2switch_en : 1;
+     unsigned long sclk_force_reg      : 1;
+     unsigned long sclk_force_disp     : 1;
+     unsigned long sclk_force_mc       : 1;
+     unsigned long sclk_force_extmc    : 1;
+     unsigned long sclk_force_cp       : 1;
+     unsigned long sclk_force_e2       : 1;
+     unsigned long sclk_force_e3       : 1;
+     unsigned long sclk_force_idct     : 1;
+     unsigned long sclk_force_bist     : 1;
+     unsigned long busy_extend_cp      : 1;
+     unsigned long busy_extend_e2      : 1;
+     unsigned long busy_extend_e3      : 1;
+     unsigned long busy_extend_idct    : 1;
+     unsigned long                                     : 3;
+} __attribute__((packed));
+
+union sclk_cntl_u {
+     unsigned long val : 32;
+     struct sclk_cntl_t f;
+} __attribute__((packed));
+
+struct pclk_cntl_t {
+     unsigned long pclk_src_sel     : 2;
+     unsigned long                             : 2;
+     unsigned long pclk_post_div    : 4;
+     unsigned long                             : 8;
+     unsigned long pclk_force_disp  : 1;
+     unsigned long                             : 15;
+} __attribute__((packed));
+
+union pclk_cntl_u {
+     unsigned long val : 32;
+     struct pclk_cntl_t f;
+} __attribute__((packed));
+
+struct clk_test_cntl_t {
+     unsigned long testclk_sel      : 4;
+     unsigned long                             : 3;
+     unsigned long start_check_freq : 1;
+     unsigned long tstcount_rst     : 1;
+     unsigned long                             : 15;
+     unsigned long test_count       : 8;
+} __attribute__((packed));
+
+union clk_test_cntl_u {
+     unsigned long val : 32;
+     struct clk_test_cntl_t f;
+} __attribute__((packed));
+
+struct pwrmgt_cntl_t {
+     unsigned long pwm_enable          : 1;
+     unsigned long                                     : 1;
+     unsigned long pwm_mode_req         : 2;
+     unsigned long pwm_wakeup_cond      : 2;
+     unsigned long pwm_fast_noml_hw_en  : 1;
+     unsigned long pwm_noml_fast_hw_en  : 1;
+     unsigned long pwm_fast_noml_cond   : 4;
+     unsigned long pwm_noml_fast_cond   : 4;
+     unsigned long pwm_idle_timer      : 8;
+     unsigned long pwm_busy_timer      : 8;
+} __attribute__((packed));
+
+union pwrmgt_cntl_u {
+     unsigned long val : 32;
+     struct pwrmgt_cntl_t f;
+} __attribute__((packed));
+
+#endif
+
diff --git a/fs/affs/affs.h b/fs/affs/affs.h
new file mode 100644 (file)
index 0000000..0c6799f
--- /dev/null
@@ -0,0 +1,304 @@
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/affs_fs.h>
+#include <linux/amigaffs.h>
+
+/* AmigaOS allows file names with up to 30 characters length.
+ * Names longer than that will be silently truncated. If you
+ * want to disallow this, comment out the following #define.
+ * Creating filesystem objects with longer names will then
+ * result in an error (ENAMETOOLONG).
+ */
+/*#define AFFS_NO_TRUNCATE */
+
+/* Ugly macros make the code more pretty. */
+
+#define GET_END_PTR(st,p,sz)            ((st *)((char *)(p)+((sz)-sizeof(st))))
+#define AFFS_GET_HASHENTRY(data,hashkey) be32_to_cpu(((struct dir_front *)data)->hashtable[hashkey])
+#define AFFS_BLOCK(sb, bh, blk)                (AFFS_HEAD(bh)->table[AFFS_SB(sb)->s_hashsize-1-(blk)])
+
+#ifdef __LITTLE_ENDIAN
+#define BO_EXBITS      0x18UL
+#elif defined(__BIG_ENDIAN)
+#define BO_EXBITS      0x00UL
+#else
+#error Endianness must be known for affs to work.
+#endif
+
+#define AFFS_HEAD(bh)          ((struct affs_head *)(bh)->b_data)
+#define AFFS_TAIL(sb, bh)      ((struct affs_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_tail)))
+#define AFFS_ROOT_HEAD(bh)     ((struct affs_root_head *)(bh)->b_data)
+#define AFFS_ROOT_TAIL(sb, bh) ((struct affs_root_tail *)((bh)->b_data+(sb)->s_blocksize-sizeof(struct affs_root_tail)))
+#define AFFS_DATA_HEAD(bh)     ((struct affs_data_head *)(bh)->b_data)
+#define AFFS_DATA(bh)          (((struct affs_data_head *)(bh)->b_data)->data)
+
+#define AFFS_CACHE_SIZE                PAGE_SIZE
+
+#define AFFS_MAX_PREALLOC      32
+#define AFFS_LC_SIZE           (AFFS_CACHE_SIZE/sizeof(u32)/2)
+#define AFFS_AC_SIZE           (AFFS_CACHE_SIZE/sizeof(struct affs_ext_key)/2)
+#define AFFS_AC_MASK           (AFFS_AC_SIZE-1)
+
+struct affs_ext_key {
+       u32     ext;                            /* idx of the extended block */
+       u32     key;                            /* block number */
+};
+
+/*
+ * affs fs inode data in memory
+ */
+struct affs_inode_info {
+       u32      i_opencnt;
+       struct semaphore i_link_lock;           /* Protects internal inode access. */
+       struct semaphore i_ext_lock;            /* Protects internal inode access. */
+#define i_hash_lock i_ext_lock
+       u32      i_blkcnt;                      /* block count */
+       u32      i_extcnt;                      /* extended block count */
+       u32     *i_lc;                          /* linear cache of extended blocks */
+       u32      i_lc_size;
+       u32      i_lc_shift;
+       u32      i_lc_mask;
+       struct affs_ext_key *i_ac;              /* associative cache of extended blocks */
+       u32      i_ext_last;                    /* last accessed extended block */
+       struct buffer_head *i_ext_bh;           /* bh of last extended block */
+       loff_t   mmu_private;
+       u32      i_protect;                     /* unused attribute bits */
+       u32      i_lastalloc;                   /* last allocated block */
+       int      i_pa_cnt;                      /* number of preallocated blocks */
+       struct inode vfs_inode;
+};
+
+/* short cut to get to the affs specific inode data */
+static inline struct affs_inode_info *AFFS_I(struct inode *inode)
+{
+       return list_entry(inode, struct affs_inode_info, vfs_inode);
+}
+
+/*
+ * super-block data in memory
+ *
+ * Block numbers are adjusted for their actual size
+ *
+ */
+
+struct affs_bm_info {
+       u32 bm_key;                     /* Disk block number */
+       u32 bm_free;                    /* Free blocks in here */
+};
+
+struct affs_sb_info {
+       int s_partition_size;           /* Partition size in blocks. */
+       int s_reserved;                 /* Number of reserved blocks. */
+       //u32 s_blksize;                        /* Initial device blksize */
+       u32 s_data_blksize;             /* size of the data block w/o header */
+       u32 s_root_block;               /* FFS root block number. */
+       int s_hashsize;                 /* Size of hash table. */
+       unsigned long s_flags;          /* See below. */
+       uid_t s_uid;                    /* uid to override */
+       gid_t s_gid;                    /* gid to override */
+       umode_t s_mode;                 /* mode to override */
+       struct buffer_head *s_root_bh;  /* Cached root block. */
+       struct semaphore s_bmlock;      /* Protects bitmap access. */
+       struct affs_bm_info *s_bitmap;  /* Bitmap infos. */
+       u32 s_bmap_count;               /* # of bitmap blocks. */
+       u32 s_bmap_bits;                /* # of bits in one bitmap blocks */
+       u32 s_last_bmap;
+       struct buffer_head *s_bmap_bh;
+       char *s_prefix;                 /* Prefix for volumes and assigns. */
+       int s_prefix_len;               /* Length of prefix. */
+       char s_volume[32];              /* Volume prefix for absolute symlinks. */
+};
+
+#define SF_INTL                0x0001          /* International filesystem. */
+#define SF_BM_VALID    0x0002          /* Bitmap is valid. */
+#define SF_IMMUTABLE   0x0004          /* Protection bits cannot be changed */
+#define SF_QUIET       0x0008          /* chmod errors will be not reported */
+#define SF_SETUID      0x0010          /* Ignore Amiga uid */
+#define SF_SETGID      0x0020          /* Ignore Amiga gid */
+#define SF_SETMODE     0x0040          /* Ignore Amiga protection bits */
+#define SF_MUFS                0x0100          /* Use MUFS uid/gid mapping */
+#define SF_OFS         0x0200          /* Old filesystem */
+#define SF_PREFIX      0x0400          /* Buffer for prefix is allocated */
+#define SF_VERBOSE     0x0800          /* Talk about fs when mounting */
+
+/* short cut to get to the affs specific sb data */
+static inline struct affs_sb_info *AFFS_SB(struct super_block *sb)
+{
+       return sb->s_fs_info;
+}
+
+/* amigaffs.c */
+
+extern int     affs_insert_hash(struct inode *inode, struct buffer_head *bh);
+extern int     affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh);
+extern int     affs_remove_header(struct dentry *dentry);
+extern u32     affs_checksum_block(struct super_block *sb, struct buffer_head *bh);
+extern void    affs_fix_checksum(struct super_block *sb, struct buffer_head *bh);
+extern void    secs_to_datestamp(time_t secs, struct affs_date *ds);
+extern mode_t  prot_to_mode(u32 prot);
+extern void    mode_to_prot(struct inode *inode);
+extern void    affs_error(struct super_block *sb, const char *function, const char *fmt, ...);
+extern void    affs_warning(struct super_block *sb, const char *function, const char *fmt, ...);
+extern int     affs_check_name(const unsigned char *name, int len);
+extern int     affs_copy_name(unsigned char *bstr, struct dentry *dentry);
+
+/* bitmap. c */
+
+extern u32     affs_count_free_blocks(struct super_block *s);
+extern void    affs_free_block(struct super_block *sb, u32 block);
+extern u32     affs_alloc_block(struct inode *inode, u32 goal);
+extern int     affs_init_bitmap(struct super_block *sb, int *flags);
+extern void    affs_free_bitmap(struct super_block *sb);
+
+/* namei.c */
+
+extern int     affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len);
+extern struct dentry *affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *);
+extern int     affs_unlink(struct inode *dir, struct dentry *dentry);
+extern int     affs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *);
+extern int     affs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+extern int     affs_rmdir(struct inode *dir, struct dentry *dentry);
+extern int     affs_link(struct dentry *olddentry, struct inode *dir,
+                         struct dentry *dentry);
+extern int     affs_symlink(struct inode *dir, struct dentry *dentry,
+                            const char *symname);
+extern int     affs_rename(struct inode *old_dir, struct dentry *old_dentry,
+                           struct inode *new_dir, struct dentry *new_dentry);
+
+/* inode.c */
+
+extern unsigned long            affs_parent_ino(struct inode *dir);
+extern struct inode            *affs_new_inode(struct inode *dir);
+extern int                      affs_notify_change(struct dentry *dentry, struct iattr *attr);
+extern void                     affs_put_inode(struct inode *inode);
+extern void                     affs_delete_inode(struct inode *inode);
+extern void                     affs_clear_inode(struct inode *inode);
+extern void                     affs_read_inode(struct inode *inode);
+extern int                      affs_write_inode(struct inode *inode, int);
+extern int                      affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type);
+
+/* file.c */
+
+void           affs_free_prealloc(struct inode *inode);
+extern void    affs_truncate(struct inode *);
+
+/* dir.c */
+
+extern void   affs_dir_truncate(struct inode *);
+
+/* jump tables */
+
+extern struct inode_operations  affs_file_inode_operations;
+extern struct inode_operations  affs_dir_inode_operations;
+extern struct inode_operations   affs_symlink_inode_operations;
+extern struct file_operations   affs_file_operations;
+extern struct file_operations   affs_file_operations_ofs;
+extern struct file_operations   affs_dir_operations;
+extern struct address_space_operations  affs_symlink_aops;
+extern struct address_space_operations  affs_aops;
+extern struct address_space_operations  affs_aops_ofs;
+
+extern struct dentry_operations         affs_dentry_operations;
+extern struct dentry_operations         affs_dentry_operations_intl;
+
+static inline void
+affs_set_blocksize(struct super_block *sb, int size)
+{
+       sb_set_blocksize(sb, size);
+}
+static inline struct buffer_head *
+affs_bread(struct super_block *sb, int block)
+{
+       pr_debug("affs_bread: %d\n", block);
+       if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size)
+               return sb_bread(sb, block);
+       return NULL;
+}
+static inline struct buffer_head *
+affs_getblk(struct super_block *sb, int block)
+{
+       pr_debug("affs_getblk: %d\n", block);
+       if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size)
+               return sb_getblk(sb, block);
+       return NULL;
+}
+static inline struct buffer_head *
+affs_getzeroblk(struct super_block *sb, int block)
+{
+       struct buffer_head *bh;
+       pr_debug("affs_getzeroblk: %d\n", block);
+       if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) {
+               bh = sb_getblk(sb, block);
+               lock_buffer(bh);
+               memset(bh->b_data, 0 , sb->s_blocksize);
+               set_buffer_uptodate(bh);
+               unlock_buffer(bh);
+               return bh;
+       }
+       return NULL;
+}
+static inline struct buffer_head *
+affs_getemptyblk(struct super_block *sb, int block)
+{
+       struct buffer_head *bh;
+       pr_debug("affs_getemptyblk: %d\n", block);
+       if (block >= AFFS_SB(sb)->s_reserved && block < AFFS_SB(sb)->s_partition_size) {
+               bh = sb_getblk(sb, block);
+               wait_on_buffer(bh);
+               set_buffer_uptodate(bh);
+               return bh;
+       }
+       return NULL;
+}
+static inline void
+affs_brelse(struct buffer_head *bh)
+{
+       if (bh)
+               pr_debug("affs_brelse: %lld\n", (long long) bh->b_blocknr);
+       brelse(bh);
+}
+
+static inline void
+affs_adjust_checksum(struct buffer_head *bh, u32 val)
+{
+       u32 tmp = be32_to_cpu(((__be32 *)bh->b_data)[5]);
+       ((__be32 *)bh->b_data)[5] = cpu_to_be32(tmp - val);
+}
+static inline void
+affs_adjust_bitmapchecksum(struct buffer_head *bh, u32 val)
+{
+       u32 tmp = be32_to_cpu(((__be32 *)bh->b_data)[0]);
+       ((__be32 *)bh->b_data)[0] = cpu_to_be32(tmp - val);
+}
+
+static inline void
+affs_lock_link(struct inode *inode)
+{
+       down(&AFFS_I(inode)->i_link_lock);
+}
+static inline void
+affs_unlock_link(struct inode *inode)
+{
+       up(&AFFS_I(inode)->i_link_lock);
+}
+static inline void
+affs_lock_dir(struct inode *inode)
+{
+       down(&AFFS_I(inode)->i_hash_lock);
+}
+static inline void
+affs_unlock_dir(struct inode *inode)
+{
+       up(&AFFS_I(inode)->i_hash_lock);
+}
+static inline void
+affs_lock_ext(struct inode *inode)
+{
+       down(&AFFS_I(inode)->i_ext_lock);
+}
+static inline void
+affs_unlock_ext(struct inode *inode)
+{
+       up(&AFFS_I(inode)->i_ext_lock);
+}
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
new file mode 100644 (file)
index 0000000..745e9b8
--- /dev/null
@@ -0,0 +1,1101 @@
+/* binfmt_elf_fdpic.c: FDPIC ELF binary format
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * Derived from binfmt_elf.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.
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/binfmts.h>
+#include <linux/string.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/highmem.h>
+#include <linux/personality.h>
+#include <linux/ptrace.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/elf.h>
+#include <linux/elf-fdpic.h>
+#include <linux/elfcore.h>
+
+#include <asm/uaccess.h>
+#include <asm/param.h>
+#include <asm/pgalloc.h>
+
+typedef char *elf_caddr_t;
+#ifndef elf_addr_t
+#define elf_addr_t unsigned long
+#endif
+
+#if 0
+#define kdebug(fmt, ...) printk("FDPIC "fmt"\n" ,##__VA_ARGS__ )
+#else
+#define kdebug(fmt, ...) do {} while(0)
+#endif
+
+MODULE_LICENSE("GPL");
+
+static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs);
+//static int load_elf_fdpic_library(struct file *);
+static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *file);
+static int elf_fdpic_map_file(struct elf_fdpic_params *params,
+                             struct file *file,
+                             struct mm_struct *mm,
+                             const char *what);
+
+static int create_elf_fdpic_tables(struct linux_binprm *bprm,
+                                  struct mm_struct *mm,
+                                  struct elf_fdpic_params *exec_params,
+                                  struct elf_fdpic_params *interp_params);
+
+#ifndef CONFIG_MMU
+static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm, unsigned long *_sp);
+static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *params,
+                                                  struct file *file,
+                                                  struct mm_struct *mm);
+#endif
+
+static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
+                                            struct file *file,
+                                            struct mm_struct *mm);
+
+static struct linux_binfmt elf_fdpic_format = {
+       .module         = THIS_MODULE,
+       .load_binary    = load_elf_fdpic_binary,
+//     .load_shlib     = load_elf_fdpic_library,
+//     .core_dump      = elf_fdpic_core_dump,
+       .min_coredump   = ELF_EXEC_PAGESIZE,
+};
+
+static int __init init_elf_fdpic_binfmt(void)  { return register_binfmt(&elf_fdpic_format); }
+static void __exit exit_elf_fdpic_binfmt(void) { unregister_binfmt(&elf_fdpic_format); }
+
+module_init(init_elf_fdpic_binfmt)
+module_exit(exit_elf_fdpic_binfmt)
+
+static int is_elf_fdpic(struct elfhdr *hdr, struct file *file)
+{
+       if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0)
+               return 0;
+       if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN)
+               return 0;
+       if (!elf_check_arch(hdr) || !elf_check_fdpic(hdr))
+               return 0;
+       if (!file->f_op || !file->f_op->mmap)
+               return 0;
+       return 1;
+}
+
+/*****************************************************************************/
+/*
+ * read the program headers table into memory
+ */
+static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *file)
+{
+       struct elf32_phdr *phdr;
+       unsigned long size;
+       int retval, loop;
+
+       if (params->hdr.e_phentsize != sizeof(struct elf_phdr))
+               return -ENOMEM;
+       if (params->hdr.e_phnum > 65536U / sizeof(struct elf_phdr))
+               return -ENOMEM;
+
+       size = params->hdr.e_phnum * sizeof(struct elf_phdr);
+       params->phdrs = kmalloc(size, GFP_KERNEL);
+       if (!params->phdrs)
+               return -ENOMEM;
+
+       retval = kernel_read(file, params->hdr.e_phoff, (char *) params->phdrs, size);
+       if (retval < 0)
+               return retval;
+
+       /* determine stack size for this binary */
+       phdr = params->phdrs;
+       for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
+               if (phdr->p_type != PT_GNU_STACK)
+                       continue;
+
+               if (phdr->p_flags & PF_X)
+                       params->flags |= ELF_FDPIC_FLAG_EXEC_STACK;
+               else
+                       params->flags |= ELF_FDPIC_FLAG_NOEXEC_STACK;
+
+               params->stack_size = phdr->p_memsz;
+               break;
+       }
+
+       return 0;
+} /* end elf_fdpic_fetch_phdrs() */
+
+/*****************************************************************************/
+/*
+ * load an fdpic binary into various bits of memory
+ */
+static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+{
+       struct elf_fdpic_params exec_params, interp_params;
+       struct elf_phdr *phdr;
+       unsigned long stack_size;
+       struct file *interpreter = NULL; /* to shut gcc up */
+       char *interpreter_name = NULL;
+       int executable_stack;
+       int retval, i;
+
+       memset(&exec_params, 0, sizeof(exec_params));
+       memset(&interp_params, 0, sizeof(interp_params));
+
+       exec_params.hdr = *(struct elfhdr *) bprm->buf;
+       exec_params.flags = ELF_FDPIC_FLAG_PRESENT | ELF_FDPIC_FLAG_EXECUTABLE;
+
+       /* check that this is a binary we know how to deal with */
+       retval = -ENOEXEC;
+       if (!is_elf_fdpic(&exec_params.hdr, bprm->file))
+               goto error;
+
+       /* read the program header table */
+       retval = elf_fdpic_fetch_phdrs(&exec_params, bprm->file);
+       if (retval < 0)
+               goto error;
+
+       /* scan for a program header that specifies an interpreter */
+       phdr = exec_params.phdrs;
+
+       for (i = 0; i < exec_params.hdr.e_phnum; i++, phdr++) {
+               switch (phdr->p_type) {
+               case PT_INTERP:
+                       retval = -ENOMEM;
+                       if (phdr->p_filesz > PATH_MAX)
+                               goto error;
+                       retval = -ENOENT;
+                       if (phdr->p_filesz < 2)
+                               goto error;
+
+                       /* read the name of the interpreter into memory */
+                       interpreter_name = (char *) kmalloc(phdr->p_filesz, GFP_KERNEL);
+                       if (!interpreter_name)
+                               goto error;
+
+                       retval = kernel_read(bprm->file,
+                                            phdr->p_offset,
+                                            interpreter_name,
+                                            phdr->p_filesz);
+                       if (retval < 0)
+                               goto error;
+
+                       retval = -ENOENT;
+                       if (interpreter_name[phdr->p_filesz - 1] != '\0')
+                               goto error;
+
+                       kdebug("Using ELF interpreter %s", interpreter_name);
+
+                       /* replace the program with the interpreter */
+                       interpreter = open_exec(interpreter_name);
+                       retval = PTR_ERR(interpreter);
+                       if (IS_ERR(interpreter)) {
+                               interpreter = NULL;
+                               goto error;
+                       }
+
+                       retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE);
+                       if (retval < 0)
+                               goto error;
+
+                       interp_params.hdr = *((struct elfhdr *) bprm->buf);
+                       break;
+
+               case PT_LOAD:
+#ifdef CONFIG_MMU
+                       if (exec_params.load_addr == 0)
+                               exec_params.load_addr = phdr->p_vaddr;
+#endif
+                       break;
+               }
+
+       }
+
+       if (elf_check_const_displacement(&exec_params.hdr))
+               exec_params.flags |= ELF_FDPIC_FLAG_CONSTDISP;
+
+       /* perform insanity checks on the interpreter */
+       if (interpreter_name) {
+               retval = -ELIBBAD;
+               if (!is_elf_fdpic(&interp_params.hdr, interpreter))
+                       goto error;
+
+               interp_params.flags = ELF_FDPIC_FLAG_PRESENT;
+
+               /* read the interpreter's program header table */
+               retval = elf_fdpic_fetch_phdrs(&interp_params, interpreter);
+               if (retval < 0)
+                       goto error;
+       }
+
+       stack_size = exec_params.stack_size;
+       if (stack_size < interp_params.stack_size)
+               stack_size = interp_params.stack_size;
+
+       if (exec_params.flags & ELF_FDPIC_FLAG_EXEC_STACK)
+               executable_stack = EXSTACK_ENABLE_X;
+       else if (exec_params.flags & ELF_FDPIC_FLAG_NOEXEC_STACK)
+               executable_stack = EXSTACK_DISABLE_X;
+       else if (interp_params.flags & ELF_FDPIC_FLAG_EXEC_STACK)
+               executable_stack = EXSTACK_ENABLE_X;
+       else if (interp_params.flags & ELF_FDPIC_FLAG_NOEXEC_STACK)
+               executable_stack = EXSTACK_DISABLE_X;
+       else
+               executable_stack = EXSTACK_DEFAULT;
+
+       retval = -ENOEXEC;
+       if (stack_size == 0)
+               goto error;
+
+       if (elf_check_const_displacement(&interp_params.hdr))
+               interp_params.flags |= ELF_FDPIC_FLAG_CONSTDISP;
+
+       /* flush all traces of the currently running executable */
+       retval = flush_old_exec(bprm);
+       if (retval)
+               goto error;
+
+       /* there's now no turning back... the old userspace image is dead,
+        * defunct, deceased, etc. after this point we have to exit via
+        * error_kill */
+       set_personality(PER_LINUX_FDPIC);
+       set_binfmt(&elf_fdpic_format);
+
+       current->mm->start_code = 0;
+       current->mm->end_code = 0;
+       current->mm->start_stack = 0;
+       current->mm->start_data = 0;
+       current->mm->end_data = 0;
+       current->mm->context.exec_fdpic_loadmap = 0;
+       current->mm->context.interp_fdpic_loadmap = 0;
+
+       current->flags &= ~PF_FORKNOEXEC;
+
+#ifdef CONFIG_MMU
+       elf_fdpic_arch_lay_out_mm(&exec_params,
+                                 &interp_params,
+                                 &current->mm->start_stack,
+                                 &current->mm->start_brk);
+#endif
+
+       /* do this so that we can load the interpreter, if need be
+        * - we will change some of these later
+        */
+       current->mm->rss = 0;
+
+#ifdef CONFIG_MMU
+       retval = setup_arg_pages(bprm, current->mm->start_stack, executable_stack);
+       if (retval < 0) {
+               send_sig(SIGKILL, current, 0);
+               goto error_kill;
+       }
+#endif
+
+       /* load the executable and interpreter into memory */
+       retval = elf_fdpic_map_file(&exec_params, bprm->file, current->mm, "executable");
+       if (retval < 0)
+               goto error_kill;
+
+       if (interpreter_name) {
+               retval = elf_fdpic_map_file(&interp_params, interpreter,
+                                           current->mm, "interpreter");
+               if (retval < 0) {
+                       printk(KERN_ERR "Unable to load interpreter\n");
+                       goto error_kill;
+               }
+
+               allow_write_access(interpreter);
+               fput(interpreter);
+               interpreter = NULL;
+       }
+
+#ifdef CONFIG_MMU
+       if (!current->mm->start_brk)
+               current->mm->start_brk = current->mm->end_data;
+
+       current->mm->brk = current->mm->start_brk = PAGE_ALIGN(current->mm->start_brk);
+
+#else
+       /* create a stack and brk area big enough for everyone
+        * - the brk heap starts at the bottom and works up
+        * - the stack starts at the top and works down
+        */
+       stack_size = (stack_size + PAGE_SIZE - 1) & PAGE_MASK;
+       if (stack_size < PAGE_SIZE * 2)
+               stack_size = PAGE_SIZE * 2;
+
+       down_write(&current->mm->mmap_sem);
+       current->mm->start_brk = do_mmap(NULL,
+                                        0,
+                                        stack_size,
+                                        PROT_READ | PROT_WRITE | PROT_EXEC,
+                                        MAP_PRIVATE | MAP_ANON | MAP_GROWSDOWN,
+                                        0);
+
+       if (IS_ERR((void *) current->mm->start_brk)) {
+               up_write(&current->mm->mmap_sem);
+               retval = current->mm->start_brk;
+               current->mm->start_brk = 0;
+               goto error_kill;
+       }
+
+       if (do_mremap(current->mm->start_brk,
+                     stack_size,
+                     ksize((char *) current->mm->start_brk),
+                     0, 0
+                     ) == current->mm->start_brk
+           )
+               stack_size = ksize((char *) current->mm->start_brk);
+       up_write(&current->mm->mmap_sem);
+
+       current->mm->brk = current->mm->start_brk;
+       current->mm->context.end_brk = current->mm->start_brk;
+       current->mm->context.end_brk += (stack_size > PAGE_SIZE) ? (stack_size - PAGE_SIZE) : 0;
+       current->mm->start_stack = current->mm->start_brk + stack_size;
+#endif
+
+       compute_creds(bprm);
+       current->flags &= ~PF_FORKNOEXEC;
+       if (create_elf_fdpic_tables(bprm, current->mm, &exec_params, &interp_params) < 0)
+               goto error_kill;
+
+       kdebug("- start_code  %lx",     (long) current->mm->start_code);
+       kdebug("- end_code    %lx",     (long) current->mm->end_code);
+       kdebug("- start_data  %lx",     (long) current->mm->start_data);
+       kdebug("- end_data    %lx",     (long) current->mm->end_data);
+       kdebug("- start_brk   %lx",     (long) current->mm->start_brk);
+       kdebug("- brk         %lx",     (long) current->mm->brk);
+       kdebug("- start_stack %lx",     (long) current->mm->start_stack);
+
+#ifdef ELF_FDPIC_PLAT_INIT
+       /*
+        * The ABI may specify that certain registers be set up in special
+        * ways (on i386 %edx is the address of a DT_FINI function, for
+        * example.  This macro performs whatever initialization to
+        * the regs structure is required.
+        */
+       ELF_FDPIC_PLAT_INIT(regs,
+                           exec_params.map_addr,
+                           interp_params.map_addr,
+                           interp_params.dynamic_addr ?: exec_params.dynamic_addr
+                           );
+#endif
+
+       /* everything is now ready... get the userspace context ready to roll */
+       start_thread(regs,
+                    interp_params.entry_addr ?: exec_params.entry_addr,
+                    current->mm->start_stack);
+
+       if (unlikely(current->ptrace & PT_PTRACED)) {
+               if (current->ptrace & PT_TRACE_EXEC)
+                       ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP);
+               else
+                       send_sig(SIGTRAP, current, 0);
+       }
+
+       retval = 0;
+
+error:
+       if (interpreter) {
+               allow_write_access(interpreter);
+               fput(interpreter);
+       }
+       if (interpreter_name)
+               kfree(interpreter_name);
+       if (exec_params.phdrs)
+               kfree(exec_params.phdrs);
+       if (exec_params.loadmap)
+               kfree(exec_params.loadmap);
+       if (interp_params.phdrs)
+               kfree(interp_params.phdrs);
+       if (interp_params.loadmap)
+               kfree(interp_params.loadmap);
+       return retval;
+
+       /* unrecoverable error - kill the process */
+ error_kill:
+       send_sig(SIGSEGV, current, 0);
+       goto error;
+
+} /* end load_elf_fdpic_binary() */
+
+/*****************************************************************************/
+/*
+ * present useful information to the program
+ */
+static int create_elf_fdpic_tables(struct linux_binprm *bprm,
+                                  struct mm_struct *mm,
+                                  struct elf_fdpic_params *exec_params,
+                                  struct elf_fdpic_params *interp_params)
+{
+       unsigned long sp, csp, nitems;
+       elf_caddr_t *argv, *envp;
+       size_t platform_len = 0, len;
+       char *k_platform, *u_platform, *p;
+       long hwcap;
+       int loop;
+
+       /* we're going to shovel a whole load of stuff onto the stack */
+#ifdef CONFIG_MMU
+       sp = bprm->p;
+#else
+       sp = mm->start_stack;
+
+       /* stack the program arguments and environment */
+       if (elf_fdpic_transfer_args_to_stack(bprm, &sp) < 0)
+               return -EFAULT;
+#endif
+
+       /* get hold of platform and hardware capabilities masks for the machine
+        * we are running on.  In some cases (Sparc), this info is impossible
+        * to get, in others (i386) it is merely difficult.
+        */
+       hwcap = ELF_HWCAP;
+       k_platform = ELF_PLATFORM;
+
+       if (k_platform) {
+               platform_len = strlen(k_platform) + 1;
+               sp -= platform_len;
+               if (__copy_to_user(u_platform, k_platform, platform_len) != 0)
+                       return -EFAULT;
+       }
+
+       u_platform = (char *) sp;
+
+#if defined(__i386__) && defined(CONFIG_SMP)
+       /* in some cases (e.g. Hyper-Threading), we want to avoid L1 evictions
+        * by the processes running on the same package. One thing we can do
+        * is to shuffle the initial stack for them.
+        *
+        * the conditionals here are unneeded, but kept in to make the
+        * code behaviour the same as pre change unless we have hyperthreaded
+        * processors. This keeps Mr Marcelo Person happier but should be
+        * removed for 2.5
+        */
+       if (smp_num_siblings > 1)
+               sp = sp - ((current->pid % 64) << 7);
+#endif
+
+       sp &= ~7UL;
+
+       /* stack the load map(s) */
+       len = sizeof(struct elf32_fdpic_loadmap);
+       len += sizeof(struct elf32_fdpic_loadseg) * exec_params->loadmap->nsegs;
+       sp = (sp - len) & ~7UL;
+       exec_params->map_addr = sp;
+
+       if (copy_to_user((void *) sp, exec_params->loadmap, len) != 0)
+               return -EFAULT;
+
+       current->mm->context.exec_fdpic_loadmap = (unsigned long) sp;
+
+       if (interp_params->loadmap) {
+               len = sizeof(struct elf32_fdpic_loadmap);
+               len += sizeof(struct elf32_fdpic_loadseg) * interp_params->loadmap->nsegs;
+               sp = (sp - len) & ~7UL;
+               interp_params->map_addr = sp;
+
+               if (copy_to_user((void *) sp, interp_params->loadmap, len) != 0)
+                       return -EFAULT;
+
+               current->mm->context.interp_fdpic_loadmap = (unsigned long) sp;
+       }
+
+       /* force 16 byte _final_ alignment here for generality */
+#define DLINFO_ITEMS 13
+
+       nitems = 1 + DLINFO_ITEMS + (k_platform ? 1 : 0);
+#ifdef DLINFO_ARCH_ITEMS
+       nitems += DLINFO_ARCH_ITEMS;
+#endif
+
+       csp = sp;
+       sp -= nitems * 2 * sizeof(unsigned long);
+       sp -= (bprm->envc + 1) * sizeof(char *);        /* envv[] */
+       sp -= (bprm->argc + 1) * sizeof(char *);        /* argv[] */
+       sp -= 1 * sizeof(unsigned long);                /* argc */
+
+       csp -= sp & 15UL;
+       sp -= sp & 15UL;
+
+       /* put the ELF interpreter info on the stack */
+#define NEW_AUX_ENT(nr, id, val)                                               \
+       do {                                                                    \
+               struct { unsigned long _id, _val; } *ent = (void *) csp;        \
+               __put_user((id), &ent[nr]._id);                                 \
+               __put_user((val), &ent[nr]._val);                               \
+       } while (0)
+
+       csp -= 2 * sizeof(unsigned long);
+       NEW_AUX_ENT(0, AT_NULL, 0);
+       if (k_platform) {
+               csp -= 2 * sizeof(unsigned long);
+               NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform);
+       }
+
+       csp -= DLINFO_ITEMS * 2 * sizeof(unsigned long);
+       NEW_AUX_ENT( 0, AT_HWCAP,               hwcap);
+       NEW_AUX_ENT( 1, AT_PAGESZ,              PAGE_SIZE);
+       NEW_AUX_ENT( 2, AT_CLKTCK,              CLOCKS_PER_SEC);
+       NEW_AUX_ENT( 3, AT_PHDR,                exec_params->ph_addr);
+       NEW_AUX_ENT( 4, AT_PHENT,               sizeof(struct elf_phdr));
+       NEW_AUX_ENT( 5, AT_PHNUM,               exec_params->hdr.e_phnum);
+       NEW_AUX_ENT( 6, AT_BASE,                interp_params->elfhdr_addr);
+       NEW_AUX_ENT( 7, AT_FLAGS,               0);
+       NEW_AUX_ENT( 8, AT_ENTRY,               exec_params->entry_addr);
+       NEW_AUX_ENT( 9, AT_UID,                 (elf_addr_t) current->uid);
+       NEW_AUX_ENT(10, AT_EUID,                (elf_addr_t) current->euid);
+       NEW_AUX_ENT(11, AT_GID,                 (elf_addr_t) current->gid);
+       NEW_AUX_ENT(12, AT_EGID,                (elf_addr_t) current->egid);
+
+#ifdef ARCH_DLINFO
+       /* ARCH_DLINFO must come last so platform specific code can enforce
+        * special alignment requirements on the AUXV if necessary (eg. PPC).
+        */
+       ARCH_DLINFO;
+#endif
+#undef NEW_AUX_ENT
+
+       /* allocate room for argv[] and envv[] */
+       csp -= (bprm->envc + 1) * sizeof(elf_caddr_t);
+       envp = (elf_caddr_t *) csp;
+       csp -= (bprm->argc + 1) * sizeof(elf_caddr_t);
+       argv = (elf_caddr_t *) csp;
+
+       /* stack argc */
+       csp -= sizeof(unsigned long);
+       __put_user(bprm->argc, (unsigned long *) csp);
+
+       if (csp != sp)
+               BUG();
+
+       /* fill in the argv[] array */
+#ifdef CONFIG_MMU
+       current->mm->arg_start = bprm->p;
+#else
+       current->mm->arg_start = current->mm->start_stack - (MAX_ARG_PAGES * PAGE_SIZE - bprm->p);
+#endif
+
+       p = (char *) current->mm->arg_start;
+       for (loop = bprm->argc; loop > 0; loop--) {
+               __put_user((elf_caddr_t) p, argv++);
+               len = strnlen_user(p, PAGE_SIZE * MAX_ARG_PAGES);
+               if (!len || len > PAGE_SIZE * MAX_ARG_PAGES)
+                       return -EINVAL;
+               p += len;
+       }
+       __put_user(NULL, argv);
+       current->mm->arg_end = (unsigned long) p;
+
+       /* fill in the envv[] array */
+       current->mm->env_start = (unsigned long) p;
+       for (loop = bprm->envc; loop > 0; loop--) {
+               __put_user((elf_caddr_t)(unsigned long) p, envp++);
+               len = strnlen_user(p, PAGE_SIZE * MAX_ARG_PAGES);
+               if (!len || len > PAGE_SIZE * MAX_ARG_PAGES)
+                       return -EINVAL;
+               p += len;
+       }
+       __put_user(NULL, envp);
+       current->mm->env_end = (unsigned long) p;
+
+       mm->start_stack = (unsigned long) sp;
+       return 0;
+} /* end create_elf_fdpic_tables() */
+
+/*****************************************************************************/
+/*
+ * transfer the program arguments and environment from the holding pages onto
+ * the stack
+ */
+#ifndef CONFIG_MMU
+static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm, unsigned long *_sp)
+{
+       unsigned long index, stop, sp;
+       char *src;
+       int ret = 0;
+
+       stop = bprm->p >> PAGE_SHIFT;
+       sp = *_sp;
+
+       for (index = MAX_ARG_PAGES - 1; index >= stop; index--) {
+               src = kmap(bprm->page[index]);
+               sp -= PAGE_SIZE;
+               if (copy_to_user((void *) sp, src, PAGE_SIZE) != 0)
+                       ret = -EFAULT;
+               kunmap(bprm->page[index]);
+               if (ret < 0)
+                       goto out;
+       }
+
+       *_sp = (*_sp - (MAX_ARG_PAGES * PAGE_SIZE - bprm->p)) & ~15;
+
+ out:
+       return ret;
+} /* end elf_fdpic_transfer_args_to_stack() */
+#endif
+
+/*****************************************************************************/
+/*
+ * load the appropriate binary image (executable or interpreter) into memory
+ * - we assume no MMU is available
+ * - if no other PIC bits are set in params->hdr->e_flags
+ *   - we assume that the LOADable segments in the binary are independently relocatable
+ *   - we assume R/O executable segments are shareable
+ * - else
+ *   - we assume the loadable parts of the image to require fixed displacement
+ *   - the image is not shareable
+ */
+static int elf_fdpic_map_file(struct elf_fdpic_params *params,
+                             struct file *file,
+                             struct mm_struct *mm,
+                             const char *what)
+{
+       struct elf32_fdpic_loadmap *loadmap;
+#ifdef CONFIG_MMU
+       struct elf32_fdpic_loadseg *mseg;
+#endif
+       struct elf32_fdpic_loadseg *seg;
+       struct elf32_phdr *phdr;
+       unsigned long load_addr, stop;
+       unsigned nloads, tmp;
+       size_t size;
+       int loop, ret;
+
+       /* allocate a load map table */
+       nloads = 0;
+       for (loop = 0; loop < params->hdr.e_phnum; loop++)
+               if (params->phdrs[loop].p_type == PT_LOAD)
+                       nloads++;
+
+       if (nloads == 0)
+               return -ELIBBAD;
+
+       size = sizeof(*loadmap) + nloads * sizeof(*seg);
+       loadmap = kmalloc(size, GFP_KERNEL);
+       if (!loadmap)
+               return -ENOMEM;
+
+       params->loadmap = loadmap;
+       memset(loadmap, 0, size);
+
+       loadmap->version = ELF32_FDPIC_LOADMAP_VERSION;
+       loadmap->nsegs = nloads;
+
+       load_addr = params->load_addr;
+       seg = loadmap->segs;
+
+       /* map the requested LOADs into the memory space */
+       switch (params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) {
+       case ELF_FDPIC_FLAG_CONSTDISP:
+       case ELF_FDPIC_FLAG_CONTIGUOUS:
+#ifndef CONFIG_MMU
+               ret = elf_fdpic_map_file_constdisp_on_uclinux(params, file, mm);
+               if (ret < 0)
+                       return ret;
+               break;
+#endif
+       default:
+               ret = elf_fdpic_map_file_by_direct_mmap(params, file, mm);
+               if (ret < 0)
+                       return ret;
+               break;
+       }
+
+       /* map the entry point */
+       if (params->hdr.e_entry) {
+               seg = loadmap->segs;
+               for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
+                       if (params->hdr.e_entry >= seg->p_vaddr &&
+                           params->hdr.e_entry < seg->p_vaddr + seg->p_memsz
+                           ) {
+                               params->entry_addr =
+                                       (params->hdr.e_entry - seg->p_vaddr) + seg->addr;
+                               break;
+                       }
+               }
+       }
+
+       /* determine where the program header table has wound up if mapped */
+       stop = params->hdr.e_phoff + params->hdr.e_phnum * sizeof (struct elf_phdr);
+       phdr = params->phdrs;
+
+       for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
+               if (phdr->p_type != PT_LOAD)
+                       continue;
+
+               if (phdr->p_offset > params->hdr.e_phoff ||
+                   phdr->p_offset + phdr->p_filesz < stop)
+                       continue;
+
+               seg = loadmap->segs;
+               for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
+                       if (phdr->p_vaddr >= seg->p_vaddr &&
+                           phdr->p_vaddr + phdr->p_filesz <= seg->p_vaddr + seg->p_memsz
+                           ) {
+                               params->ph_addr = (phdr->p_vaddr - seg->p_vaddr) + seg->addr +
+                                       params->hdr.e_phoff - phdr->p_offset;
+                               break;
+                       }
+               }
+               break;
+       }
+
+       /* determine where the dynamic section has wound up if there is one */
+       phdr = params->phdrs;
+       for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
+               if (phdr->p_type != PT_DYNAMIC)
+                       continue;
+
+               seg = loadmap->segs;
+               for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
+                       if (phdr->p_vaddr >= seg->p_vaddr &&
+                           phdr->p_vaddr + phdr->p_memsz <= seg->p_vaddr + seg->p_memsz
+                           ) {
+                               params->dynamic_addr = (phdr->p_vaddr - seg->p_vaddr) + seg->addr;
+
+                               /* check the dynamic section contains at least one item, and that
+                                * the last item is a NULL entry */
+                               if (phdr->p_memsz == 0 ||
+                                   phdr->p_memsz % sizeof(Elf32_Dyn) != 0)
+                                       goto dynamic_error;
+
+                               tmp = phdr->p_memsz / sizeof(Elf32_Dyn);
+                               if (((Elf32_Dyn *) params->dynamic_addr)[tmp - 1].d_tag != 0)
+                                       goto dynamic_error;
+                               break;
+                       }
+               }
+               break;
+       }
+
+       /* now elide adjacent segments in the load map on MMU linux
+        * - on uClinux the holes between may actually be filled with system stuff or stuff from
+        *   other processes
+        */
+#ifdef CONFIG_MMU
+       nloads = loadmap->nsegs;
+       mseg = loadmap->segs;
+       seg = mseg + 1;
+       for (loop = 1; loop < nloads; loop++) {
+               /* see if we have a candidate for merging */
+               if (seg->p_vaddr - mseg->p_vaddr == seg->addr - mseg->addr) {
+                       load_addr = PAGE_ALIGN(mseg->addr + mseg->p_memsz);
+                       if (load_addr == (seg->addr & PAGE_MASK)) {
+                               mseg->p_memsz += load_addr - (mseg->addr + mseg->p_memsz);
+                               mseg->p_memsz += seg->addr & ~PAGE_MASK;
+                               mseg->p_memsz += seg->p_memsz;
+                               loadmap->nsegs--;
+                               continue;
+                       }
+               }
+
+               mseg++;
+               if (mseg != seg)
+                       *mseg = *seg;
+       }
+#endif
+
+       kdebug("Mapped Object [%s]:", what);
+       kdebug("- elfhdr   : %lx", params->elfhdr_addr);
+       kdebug("- entry    : %lx", params->entry_addr);
+       kdebug("- PHDR[]   : %lx", params->ph_addr);
+       kdebug("- DYNAMIC[]: %lx", params->dynamic_addr);
+       seg = loadmap->segs;
+       for (loop = 0; loop < loadmap->nsegs; loop++, seg++)
+               kdebug("- LOAD[%d] : %08x-%08x [va=%x ms=%x]",
+                      loop,
+                      seg->addr, seg->addr + seg->p_memsz - 1,
+                      seg->p_vaddr, seg->p_memsz);
+
+       return 0;
+
+ dynamic_error:
+       printk("ELF FDPIC %s with invalid DYNAMIC section (inode=%lu)\n",
+              what, file->f_dentry->d_inode->i_ino);
+       return -ELIBBAD;
+} /* end elf_fdpic_map_file() */
+
+/*****************************************************************************/
+/*
+ * map a file with constant displacement under uClinux
+ */
+#ifndef CONFIG_MMU
+static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *params,
+                                                  struct file *file,
+                                                  struct mm_struct *mm)
+{
+       struct elf32_fdpic_loadseg *seg;
+       struct elf32_phdr *phdr;
+       unsigned long load_addr, base = ULONG_MAX, top = 0, maddr = 0, mflags;
+       loff_t fpos;
+       int loop, ret;
+
+       load_addr = params->load_addr;
+       seg = params->loadmap->segs;
+
+       /* determine the bounds of the contiguous overall allocation we must make */
+       phdr = params->phdrs;
+       for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
+               if (params->phdrs[loop].p_type != PT_LOAD)
+                       continue;
+
+               if (base > phdr->p_vaddr)
+                       base = phdr->p_vaddr;
+               if (top < phdr->p_vaddr + phdr->p_memsz)
+                       top = phdr->p_vaddr + phdr->p_memsz;
+       }
+
+       /* allocate one big anon block for everything */
+       mflags = MAP_PRIVATE;
+       if (params->flags & ELF_FDPIC_FLAG_EXECUTABLE)
+               mflags |= MAP_EXECUTABLE;
+
+       down_write(&mm->mmap_sem);
+       maddr = do_mmap(NULL, load_addr, top - base,
+                       PROT_READ | PROT_WRITE | PROT_EXEC, mflags, 0);
+       up_write(&mm->mmap_sem);
+       if (IS_ERR((void *) maddr))
+               return (int) maddr;
+
+       if (load_addr != 0)
+               load_addr += PAGE_ALIGN(top - base);
+
+       /* and then load the file segments into it */
+       phdr = params->phdrs;
+       for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
+               if (params->phdrs[loop].p_type != PT_LOAD)
+                       continue;
+
+               fpos = phdr->p_offset;
+
+               seg->addr = maddr + (phdr->p_vaddr - base);
+               seg->p_vaddr = phdr->p_vaddr;
+               seg->p_memsz = phdr->p_memsz;
+
+               ret = file->f_op->read(file, (void *) seg->addr, phdr->p_filesz, &fpos);
+               if (ret < 0)
+                       return ret;
+
+               /* map the ELF header address if in this segment */
+               if (phdr->p_offset == 0)
+                       params->elfhdr_addr = seg->addr;
+
+               /* clear any space allocated but not loaded */
+               if (phdr->p_filesz < phdr->p_memsz)
+                       clear_user((void *) (seg->addr + phdr->p_filesz),
+                                  phdr->p_memsz - phdr->p_filesz);
+
+               if (mm) {
+                       if (phdr->p_flags & PF_X) {
+                               mm->start_code = seg->addr;
+                               mm->end_code = seg->addr + phdr->p_memsz;
+                       }
+                       else if (!mm->start_data) {
+                               mm->start_data = seg->addr;
+#ifndef CONFIG_MMU
+                               mm->end_data = seg->addr + phdr->p_memsz;
+#endif
+                       }
+
+#ifdef CONFIG_MMU
+                       if (seg->addr + phdr->p_memsz > mm->end_data)
+                               mm->end_data = seg->addr + phdr->p_memsz;
+#endif
+               }
+
+               seg++;
+       }
+
+       return 0;
+} /* end elf_fdpic_map_file_constdisp_on_uclinux() */
+#endif
+
+/*****************************************************************************/
+/*
+ * map a binary by direct mmap() of the individual PT_LOAD segments
+ */
+static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
+                                            struct file *file,
+                                            struct mm_struct *mm)
+{
+       struct elf32_fdpic_loadseg *seg;
+       struct elf32_phdr *phdr;
+       unsigned long load_addr, delta_vaddr;
+       int loop, dvset;
+
+       load_addr = params->load_addr;
+       delta_vaddr = 0;
+       dvset = 0;
+
+       seg = params->loadmap->segs;
+
+       /* deal with each load segment separately */
+       phdr = params->phdrs;
+       for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
+               unsigned long maddr, disp, excess, excess1;
+               int prot = 0, flags;
+
+               if (phdr->p_type != PT_LOAD)
+                       continue;
+
+               kdebug("[LOAD] va=%lx of=%lx fs=%lx ms=%lx",
+                      (unsigned long) phdr->p_vaddr,
+                      (unsigned long) phdr->p_offset,
+                      (unsigned long) phdr->p_filesz,
+                      (unsigned long) phdr->p_memsz);
+
+               /* determine the mapping parameters */
+               if (phdr->p_flags & PF_R) prot |= PROT_READ;
+               if (phdr->p_flags & PF_W) prot |= PROT_WRITE;
+               if (phdr->p_flags & PF_X) prot |= PROT_EXEC;
+
+               flags = MAP_PRIVATE | MAP_DENYWRITE;
+               if (params->flags & ELF_FDPIC_FLAG_EXECUTABLE)
+                       flags |= MAP_EXECUTABLE;
+
+               maddr = 0;
+
+               switch (params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) {
+               case ELF_FDPIC_FLAG_INDEPENDENT:
+                       /* PT_LOADs are independently locatable */
+                       break;
+
+               case ELF_FDPIC_FLAG_HONOURVADDR:
+                       /* the specified virtual address must be honoured */
+                       maddr = phdr->p_vaddr;
+                       flags |= MAP_FIXED;
+                       break;
+
+               case ELF_FDPIC_FLAG_CONSTDISP:
+                       /* constant displacement
+                        * - can be mapped anywhere, but must be mapped as a unit
+                        */
+                       if (!dvset) {
+                               maddr = load_addr;
+                               delta_vaddr = phdr->p_vaddr;
+                               dvset = 1;
+                       }
+                       else {
+                               maddr = load_addr + phdr->p_vaddr - delta_vaddr;
+                               flags |= MAP_FIXED;
+                       }
+                       break;
+
+               case ELF_FDPIC_FLAG_CONTIGUOUS:
+                       /* contiguity handled later */
+                       break;
+
+               default:
+                       BUG();
+               }
+
+               maddr &= PAGE_MASK;
+
+               /* create the mapping */
+               disp = phdr->p_vaddr & ~PAGE_MASK;
+               down_write(&mm->mmap_sem);
+               maddr = do_mmap(file, maddr, phdr->p_memsz + disp, prot, flags,
+                               phdr->p_offset - disp);
+               up_write(&mm->mmap_sem);
+
+               kdebug("mmap[%d] <file> sz=%lx pr=%x fl=%x of=%lx --> %08lx",
+                      loop, phdr->p_memsz + disp, prot, flags, phdr->p_offset - disp,
+                      maddr);
+
+               if (IS_ERR((void *) maddr))
+                       return (int) maddr;
+
+               if ((params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) == ELF_FDPIC_FLAG_CONTIGUOUS)
+                       load_addr += PAGE_ALIGN(phdr->p_memsz + disp);
+
+               seg->addr = maddr + disp;
+               seg->p_vaddr = phdr->p_vaddr;
+               seg->p_memsz = phdr->p_memsz;
+
+               /* map the ELF header address if in this segment */
+               if (phdr->p_offset == 0)
+                       params->elfhdr_addr = seg->addr;
+
+               /* clear the bit between beginning of mapping and beginning of PT_LOAD */
+               if (prot & PROT_WRITE && disp > 0) {
+                       kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr, disp);
+                       clear_user((void *) maddr, disp);
+                       maddr += disp;
+               }
+
+               /* clear any space allocated but not loaded
+                * - on uClinux we can just clear the lot
+                * - on MMU linux we'll get a SIGBUS beyond the last page
+                *   extant in the file
+                */
+               excess = phdr->p_memsz - phdr->p_filesz;
+               excess1 = PAGE_SIZE - ((maddr + phdr->p_filesz) & ~PAGE_MASK);
+
+#ifdef CONFIG_MMU
+
+               if (excess > excess1) {
+                       unsigned long xaddr = maddr + phdr->p_filesz + excess1;
+                       unsigned long xmaddr;
+
+                       flags |= MAP_FIXED | MAP_ANONYMOUS;
+                       down_write(&mm->mmap_sem);
+                       xmaddr = do_mmap(NULL, xaddr, excess - excess1, prot, flags, 0);
+                       up_write(&mm->mmap_sem);
+
+                       kdebug("mmap[%d] <anon>"
+                              " ad=%lx sz=%lx pr=%x fl=%x of=0 --> %08lx",
+                              loop, xaddr, excess - excess1, prot, flags, xmaddr);
+
+                       if (xmaddr != xaddr)
+                               return -ENOMEM;
+               }
+
+               if (prot & PROT_WRITE && excess1 > 0) {
+                       kdebug("clear[%d] ad=%lx sz=%lx",
+                              loop, maddr + phdr->p_filesz, excess1);
+                       clear_user((void *) maddr + phdr->p_filesz, excess1);
+               }
+
+#else
+               if (excess > 0) {
+                       kdebug("clear[%d] ad=%lx sz=%lx",
+                              loop, maddr + phdr->p_filesz, excess);
+                       clear_user((void *) maddr + phdr->p_filesz, excess);
+               }
+#endif
+
+               if (mm) {
+                       if (phdr->p_flags & PF_X) {
+                               mm->start_code = maddr;
+                               mm->end_code = maddr + phdr->p_memsz;
+                       }
+                       else if (!mm->start_data) {
+                               mm->start_data = maddr;
+                               mm->end_data = maddr + phdr->p_memsz;
+                       }
+               }
+
+               seg++;
+       }
+
+       return 0;
+} /* end elf_fdpic_map_file_by_direct_mmap() */
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
new file mode 100644 (file)
index 0000000..0fe23d2
--- /dev/null
@@ -0,0 +1,648 @@
+/*
+ *   fs/cifs/readdir.c
+ *
+ *   Directory search handling
+ * 
+ *   Copyright (C) International Business Machines  Corp., 2004
+ *   Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library 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 Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/smp_lock.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+
+extern int CIFSFindFirst2(const int xid, struct cifsTconInfo *tcon,
+            const char *searchName, const struct nls_table *nls_codepage,
+            __u16 *searchHandle, struct cifs_search_info * psrch_inf);
+
+extern int CIFSFindNext2(const int xid, struct cifsTconInfo *tcon,
+            __u16 searchHandle, struct cifs_search_info * psrch_inf);
+
+extern int construct_dentry(struct qstr *qstring, struct file *file,
+                struct inode **ptmp_inode, struct dentry **pnew_dentry);
+
+extern void fill_in_inode(struct inode *tmp_inode,
+             FILE_DIRECTORY_INFO * pfindData, int *pobject_type);
+
+extern void unix_fill_in_inode(struct inode *tmp_inode,
+             FILE_UNIX_INFO * pfindData, int *pobject_type);
+
+
+/* BB fixme - add debug wrappers around this function to disable it fixme BB */
+/* static void dump_cifs_file_struct(struct file * file, char * label)
+{
+       struct cifsFileInfo * cf;
+
+       if(file) {
+               cf = (struct cifsFileInfo *)file->private_data;
+               if(cf == NULL) {
+                       cFYI(1,("empty cifs private file data"));
+                       return;
+               }
+               if(cf->invalidHandle) {
+                       cFYI(1,("invalid handle"));
+               }
+               if(cf->srch_inf.endOfSearch) {
+                       cFYI(1,("end of search"));
+               }
+               if(cf->srch_inf.emptyDir) {
+                       cFYI(1,("empty dir"));
+               }
+               
+       }
+} */
+
+static int initiate_cifs_search(const int xid, struct file * file)
+{
+       int rc = 0;
+       char * full_path;
+       struct cifsFileInfo * cifsFile;
+       struct cifs_sb_info *cifs_sb;
+       struct cifsTconInfo *pTcon;
+
+       if(file->private_data == NULL) {
+               file->private_data = 
+                       kmalloc(sizeof(struct cifsFileInfo),GFP_KERNEL);
+       }
+
+       if(file->private_data == NULL) {
+               return -ENOMEM;
+       } else {
+               memset(file->private_data,0,sizeof(struct cifsFileInfo));
+       }
+       cifsFile = (struct cifsFileInfo *)file->private_data;
+       cifsFile->invalidHandle = TRUE;
+       cifsFile->srch_inf.endOfSearch = FALSE;
+
+       cifs_sb = CIFS_SB(file->f_dentry->d_sb);
+       if(cifs_sb == NULL)
+               return -EINVAL;
+
+       pTcon = cifs_sb->tcon;
+       if(pTcon == NULL)
+               return -EINVAL;
+
+       if(file->f_dentry == NULL)
+               return -ENOENT;
+
+       down(&file->f_dentry->d_sb->s_vfs_rename_sem);
+       full_path = build_wildcard_path_from_dentry(file->f_dentry);
+       up(&file->f_dentry->d_sb->s_vfs_rename_sem);
+
+       if(full_path == NULL) {
+               return -ENOMEM;
+       }
+
+       cFYI(1, ("Full path: %s start at: %lld ", full_path, file->f_pos));
+
+       /* test for Unix extensions */
+       if (pTcon->ses->capabilities & CAP_UNIX) {
+               cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
+       } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
+               cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
+       } else /* not srvinos - BB fixme add check for backlevel? */ {
+               cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
+       }
+
+       rc = CIFSFindFirst2(xid, pTcon,full_path,cifs_sb->local_nls, 
+               &cifsFile->netfid, &cifsFile->srch_inf); 
+       if(rc == 0)
+               cifsFile->invalidHandle = FALSE;
+       if(full_path)
+               kfree(full_path);
+       return rc;
+}
+
+/* return length of unicode string in bytes */
+static int cifs_unicode_bytelen(char * str)
+{
+       int len;
+       __le16 * ustr = (__le16 *)str;
+
+       for(len=0;len <= PATH_MAX;len++) {
+               if(ustr[len] == 0)
+                       return len << 1;
+       }
+       cFYI(1,("Unicode string longer than PATH_MAX found"));
+       return len << 1;
+}
+
+static char * nxt_dir_entry(char * old_entry, char * end_of_smb)
+{
+       char * new_entry;
+       FILE_DIRECTORY_INFO * pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
+
+       new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset);
+       cFYI(1,("new entry %p old entry %p",new_entry,old_entry));
+       /* validate that new_entry is not past end of SMB */
+       if(new_entry >= end_of_smb) {
+               cFYI(1,("search entry %p began after end of SMB %p old entry %p",
+                       new_entry,end_of_smb,old_entry)); 
+               return NULL;
+       } else
+               return new_entry;
+
+}
+
+#define UNICODE_DOT cpu_to_le16(0x2e)
+
+/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
+static int cifs_entry_is_dot(char * current_entry, struct cifsFileInfo * cfile)
+{
+       int rc = 0;
+       char * filename = NULL;
+       int len = 0; 
+
+       if(cfile->srch_inf.info_level == 0x202) {
+               FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               if(cfile->srch_inf.unicode) {
+                       len = cifs_unicode_bytelen(filename);
+               } else {
+                       /* BB should we make this strnlen of PATH_MAX? */
+                       len = strnlen(filename, 5);
+               }
+       } else if(cfile->srch_inf.info_level == 0x101) {
+               FILE_DIRECTORY_INFO * pFindData = 
+                       (FILE_DIRECTORY_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+       } else if(cfile->srch_inf.info_level == 0x102) {
+               FILE_FULL_DIRECTORY_INFO * pFindData = 
+                       (FILE_FULL_DIRECTORY_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+       } else if(cfile->srch_inf.info_level == 0x105) {
+               SEARCH_ID_FULL_DIR_INFO * pFindData = 
+                       (SEARCH_ID_FULL_DIR_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+       } else if(cfile->srch_inf.info_level == 0x104) {
+               FILE_BOTH_DIRECTORY_INFO * pFindData = 
+                       (FILE_BOTH_DIRECTORY_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+       } else {
+               cFYI(1,("Unknown findfirst level %d",cfile->srch_inf.info_level));
+       }
+
+       if(filename) {
+               if(cfile->srch_inf.unicode) {
+                       __le16 *ufilename = (__le16 *)filename;
+                       if(len == 2) {
+                               /* check for . */
+                               if(ufilename[0] == UNICODE_DOT)
+                                       rc = 1;
+                       } else if(len == 4) {
+                               /* check for .. */
+                               if((ufilename[0] == UNICODE_DOT)
+                                  &&(ufilename[1] == UNICODE_DOT))
+                                       rc = 2;
+                       }
+               } else /* ASCII */ {
+                       if(len == 1) {
+                               if(filename[0] == '.') 
+                                       rc = 1;
+                       } else if(len == 2) {
+                               if((filename[0] == '.') && (filename[1] == '.')) 
+                                       rc = 2;
+                       }
+               }
+       }
+
+       return rc;
+}
+
+/* find the corresponding entry in the search */
+/* Note that the SMB server returns search entries for . and .. which
+   complicates logic here if we choose to parse for them and we do not
+   assume that they are located in the findfirst return buffer.*/
+/* We start counting in the buffer with entry 2 and increment for every
+   entry (do not increment for . or .. entry) */
+static int find_cifs_entry(const int xid, struct cifsTconInfo * pTcon, 
+               struct file * file, char ** ppCurrentEntry,int * num_to_ret) 
+{
+       int rc = 0;
+       int pos_in_buf = 0;
+       loff_t first_entry_in_buffer;
+       loff_t index_to_find = file->f_pos;
+       struct cifsFileInfo * cifsFile = (struct cifsFileInfo *)file->private_data;
+       /* check if index in the buffer */
+       
+       if((cifsFile == NULL) || (ppCurrentEntry == NULL) || (num_to_ret == NULL))
+               return -ENOENT;
+       
+       *ppCurrentEntry = NULL;
+       first_entry_in_buffer = 
+               cifsFile->srch_inf.index_of_last_entry - 
+                       cifsFile->srch_inf.entries_in_buffer;
+/*     dump_cifs_file_struct(file, "In fce ");*/
+       if(index_to_find < first_entry_in_buffer) {
+               /* close and restart search */
+               cFYI(1,("search backing up - close and restart search"));
+               cifsFile->invalidHandle = TRUE;
+               CIFSFindClose(xid, pTcon, cifsFile->netfid);
+               if(cifsFile->search_resume_name) {
+                       kfree(cifsFile->search_resume_name);
+                       cifsFile->search_resume_name = NULL;
+               }
+               if(cifsFile->srch_inf.ntwrk_buf_start) {
+                       cFYI(1,("freeing SMB ff cache buf on search rewind")); 
+                       cifs_buf_release(cifsFile->srch_inf.ntwrk_buf_start);
+               }
+               rc = initiate_cifs_search(xid,file);
+               if(rc) {
+                       cFYI(1,("error %d reinitiating a search on rewind",rc));
+                       return rc;
+               }
+       }
+
+       while((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && 
+             (rc == 0) && (cifsFile->srch_inf.endOfSearch == FALSE)){
+               cFYI(1,("calling findnext2"));
+               rc = CIFSFindNext2(xid,pTcon,cifsFile->netfid, &cifsFile->srch_inf);
+               if(rc)
+                       return -ENOENT;
+       }
+       if(index_to_find < cifsFile->srch_inf.index_of_last_entry) {
+               /* we found the buffer that contains the entry */
+               /* scan and find it */
+               int i;
+               char * current_entry;
+               char * end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + 
+                       smbCalcSize((struct smb_hdr *)cifsFile->srch_inf.ntwrk_buf_start);
+/*     dump_cifs_file_struct(file,"found entry in fce "); */
+               first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry -
+                       cifsFile->srch_inf.entries_in_buffer;
+               pos_in_buf = index_to_find - first_entry_in_buffer;
+               cFYI(1,("found entry - pos_in_buf %d",pos_in_buf)); 
+               current_entry = cifsFile->srch_inf.srch_entries_start;
+               for(i=0;(i<(pos_in_buf)) && (current_entry != NULL);i++) {
+                       /* go entry to next entry figuring out which we need to start with */
+                       /* if( . or ..)
+                               skip */
+                       rc = cifs_entry_is_dot(current_entry,cifsFile);
+                       if(rc == 1) /* is . or .. so skip */ {
+                               cFYI(1,("Entry is .")); /* BB removeme BB */
+                               /* continue; */
+                       } else if (rc == 2 ) {
+                               cFYI(1,("Entry is ..")); /* BB removeme BB */
+                               /* continue; */
+                       }
+                       current_entry = nxt_dir_entry(current_entry,end_of_smb);
+               }
+               if((current_entry == NULL) && (i < pos_in_buf)) {
+                       cERROR(1,("reached end of buf searching for pos in buf %d index to find %lld rc %d",pos_in_buf,index_to_find,rc)); /* BB removeme BB */
+               }
+               rc = 0;
+               *ppCurrentEntry = current_entry;
+       } else {
+               cFYI(1,("index not in buffer - could not findnext into it"));
+               return 0;
+       }
+
+       if(pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) {
+               cFYI(1,("can not return entries when pos_in_buf beyond last entry"));
+               *num_to_ret = 0;
+       } else
+               *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf;
+/*     dump_cifs_file_struct(file, "end fce ");*/
+
+       return rc;
+}
+
+/* inode num, inode type and filename returned */
+static int cifs_get_name_from_search_buf(struct qstr * pqst,char * current_entry,
+                       __u16 level,unsigned int unicode,struct nls_table * nlt,
+                       ino_t * pinum)
+{
+       int rc = 0;
+       unsigned int len = 0;
+       char * filename;
+
+       *pinum = 0;
+
+       if(level == SMB_FIND_FILE_UNIX) {
+               FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry;
+
+               filename = &pFindData->FileName[0];
+               if(unicode) {
+                       len = cifs_unicode_bytelen(filename);
+               } else {
+                       /* BB should we make this strnlen of PATH_MAX? */
+                       len = strnlen(filename, PATH_MAX);
+               }
+
+               /* BB fixme - hash low and high 32 bits if not 64 bit arch BB fixme */
+               *pinum = pFindData->UniqueId;
+       } else if(level == SMB_FIND_FILE_DIRECTORY_INFO) {
+               FILE_DIRECTORY_INFO * pFindData = 
+                       (FILE_DIRECTORY_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+       } else if(level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
+               FILE_FULL_DIRECTORY_INFO * pFindData = 
+                       (FILE_FULL_DIRECTORY_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+       } else if(level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
+               SEARCH_ID_FULL_DIR_INFO * pFindData = 
+                       (SEARCH_ID_FULL_DIR_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+               *pinum = pFindData->UniqueId;
+       } else if(level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
+               FILE_BOTH_DIRECTORY_INFO * pFindData = 
+                       (FILE_BOTH_DIRECTORY_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+       } else {
+               cFYI(1,("Unknown findfirst level %d",level));
+               return -EINVAL;
+       }
+       if(unicode) {
+               /* BB fixme - test with long names */
+               /* Note converted filename can be longer than in unicode */
+               pqst->len = cifs_strfromUCS_le((char *)pqst->name,(wchar_t *)filename,len/2,nlt);
+       } else {
+               pqst->name = filename;
+               pqst->len = len;
+       }
+       pqst->hash = full_name_hash(pqst->name,pqst->len);
+/*     cFYI(1,("filldir on %s",pqst->name));  */
+       return rc;
+}
+
+
+static int
+cifs_filldir2(char * pfindEntry, struct file *file, 
+                         filldir_t filldir, void *direntry,char * scratch_buf)
+{
+       int rc = 0;
+       struct qstr qstring;
+       struct cifsFileInfo * pCifsF;
+       unsigned obj_type;
+       ino_t  inum;
+       struct cifs_sb_info * cifs_sb;
+       struct inode *tmp_inode;
+       struct dentry *tmp_dentry;
+
+       /* get filename and len into qstring */
+       /* get dentry */
+       /* decide whether to create and populate ionde */
+       if((direntry == NULL) || (file == NULL))
+               return -EINVAL;
+
+       pCifsF = file->private_data;
+       
+       if((scratch_buf == NULL) || (pfindEntry == NULL) || (pCifsF == NULL))
+               return -ENOENT;
+
+       if(file->f_dentry == NULL)
+               return -ENOENT;
+
+       cifs_sb = CIFS_SB(file->f_dentry->d_sb);
+
+       qstring.name = scratch_buf;
+       rc = cifs_get_name_from_search_buf(&qstring,pfindEntry,
+                       pCifsF->srch_inf.info_level,
+                       pCifsF->srch_inf.unicode,cifs_sb->local_nls,
+                       &inum /* returned */);
+
+       if(rc)
+               return rc;
+
+       rc = construct_dentry(&qstring,file,&tmp_inode, &tmp_dentry);
+       if((tmp_inode == NULL) || (tmp_dentry == NULL))
+               return -ENOMEM;
+
+       if(rc) {
+               /* inode created, we need to hash it with right inode number */
+               if(inum != 0) {
+                       /* BB fixme - hash the 2 32 quantities bits together if necessary BB */
+                       tmp_inode->i_ino = inum;
+               }
+               insert_inode_hash(tmp_inode);
+       }
+
+       if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
+               unix_fill_in_inode(tmp_inode,(FILE_UNIX_INFO *)pfindEntry,&obj_type);
+       } else {
+               fill_in_inode(tmp_inode,(FILE_DIRECTORY_INFO *)pfindEntry,&obj_type);
+       }
+       
+       rc = filldir(direntry,qstring.name,qstring.len,file->f_pos,tmp_inode->i_ino,obj_type);
+       if(rc) {
+               cFYI(1,("filldir rc = %d",rc));
+       }
+
+       dput(tmp_dentry);
+       return rc;
+}
+
+int cifs_save_resume_key(const char * current_entry,struct cifsFileInfo * cifsFile)
+{
+       int rc = 0;
+       unsigned int len = 0;
+       __u16 level;
+       char * filename;
+
+       if((cifsFile == NULL) || (current_entry == NULL))
+               return -EINVAL;
+
+       level = cifsFile->srch_inf.info_level;
+
+       if(level == SMB_FIND_FILE_UNIX) {
+               FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry;
+
+               filename = &pFindData->FileName[0];
+               if(cifsFile->srch_inf.unicode) {
+                       len = cifs_unicode_bytelen(filename);
+               } else {
+                       /* BB should we make this strnlen of PATH_MAX? */
+                       len = strnlen(filename, PATH_MAX);
+               }
+               cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
+       } else if(level == SMB_FIND_FILE_DIRECTORY_INFO) {
+               FILE_DIRECTORY_INFO * pFindData = 
+                       (FILE_DIRECTORY_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+               cifsFile->srch_inf.resume_key = pFindData->FileIndex;
+       } else if(level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
+               FILE_FULL_DIRECTORY_INFO * pFindData = 
+                       (FILE_FULL_DIRECTORY_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+               cifsFile->srch_inf.resume_key = pFindData->FileIndex;
+       } else if(level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
+               SEARCH_ID_FULL_DIR_INFO * pFindData = 
+                       (SEARCH_ID_FULL_DIR_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+               cifsFile->srch_inf.resume_key = pFindData->FileIndex;
+       } else if(level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
+               FILE_BOTH_DIRECTORY_INFO * pFindData = 
+                       (FILE_BOTH_DIRECTORY_INFO *)current_entry;
+               filename = &pFindData->FileName[0];
+               len = le32_to_cpu(pFindData->FileNameLength);
+               cifsFile->srch_inf.resume_key = pFindData->FileIndex;
+       } else {
+               cFYI(1,("Unknown findfirst level %d",level));
+               return -EINVAL;
+       }
+       cifsFile->srch_inf.resume_name_len = len;
+       cifsFile->srch_inf.presume_name = filename;
+       return rc;
+}
+
+int cifs_readdir2(struct file *file, void *direntry, filldir_t filldir)
+{
+       int rc = 0;
+       int xid,i;
+       struct cifs_sb_info *cifs_sb;
+       struct cifsTconInfo *pTcon;
+       struct cifsFileInfo *cifsFile = NULL;
+       char * current_entry;
+       int num_to_fill = 0;
+       char * tmp_buf = NULL;
+       char * end_of_smb;
+
+       xid = GetXid();
+
+       if(file->f_dentry == NULL) {
+               FreeXid(xid);
+               return -EIO;
+       }
+/*     dump_cifs_file_struct(file, "Begin rdir "); */
+
+       cifs_sb = CIFS_SB(file->f_dentry->d_sb);
+       pTcon = cifs_sb->tcon;
+       if(pTcon == NULL)
+               return -EINVAL;
+
+/*     cFYI(1,("readdir2 pos: %lld",file->f_pos)); */
+
+       switch ((int) file->f_pos) {
+       case 0:
+               /*if (filldir(direntry, ".", 1, file->f_pos,
+                    file->f_dentry->d_inode->i_ino, DT_DIR) < 0) {
+                       cERROR(1, ("Filldir for current dir failed "));
+                       rc = -ENOMEM;
+                       break;
+               }
+               file->f_pos++; */
+       case 1:
+               /* if (filldir(direntry, "..", 2, file->f_pos,
+                    file->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) {
+                       cERROR(1, ("Filldir for parent dir failed "));
+                       rc = -ENOMEM;
+                       break;
+               }
+               file->f_pos++; */
+       case 2:
+               /* 1) If search is active, 
+                       is in current search buffer? 
+                       if it before then restart search
+                       if after then keep searching till find it */
+
+               if(file->private_data == NULL) {
+                       rc = initiate_cifs_search(xid,file);
+                       cFYI(1,("initiate cifs search rc %d",rc));
+                       if(rc) {
+                               FreeXid(xid);
+                               return rc;
+                       }
+               }
+       default:
+               if(file->private_data == NULL) {
+                       rc = -EINVAL;
+                       FreeXid(xid);
+                       return rc;
+               }
+               cifsFile = (struct cifsFileInfo *) file->private_data;
+               if (cifsFile->srch_inf.endOfSearch) {
+                       if(cifsFile->srch_inf.emptyDir) {
+                               cFYI(1, ("End of search, empty dir"));
+                               rc = 0;
+                               break;
+                       }
+               } /* else {
+                       cifsFile->invalidHandle = TRUE;
+                       CIFSFindClose(xid, pTcon, cifsFile->netfid);
+               } 
+               if(cifsFile->search_resume_name) {
+                       kfree(cifsFile->search_resume_name);
+                       cifsFile->search_resume_name = NULL;
+               } */
+/* BB account for . and .. in f_pos */
+               /* dump_cifs_file_struct(file, "rdir after default ");*/
+
+               rc = find_cifs_entry(xid,pTcon, file,
+                               &current_entry,&num_to_fill);
+               if(rc) {
+                       cFYI(1,("fce error %d",rc)); 
+                       goto rddir2_exit;
+               } else if (current_entry != NULL) {
+                       cFYI(1,("entry %lld found",file->f_pos));
+               } else {
+                       cFYI(1,("could not find entry"));
+                       goto rddir2_exit;
+               }
+               cFYI(1,("loop through %d times filling dir for net buf %p",
+                       num_to_fill,cifsFile->srch_inf.ntwrk_buf_start)); 
+               end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + 
+                       smbCalcSize((struct smb_hdr *)cifsFile->srch_inf.ntwrk_buf_start);
+               tmp_buf = kmalloc(NAME_MAX+1,GFP_KERNEL);
+               for(i=0;(i<num_to_fill) && (rc == 0);i++) {
+                       if(current_entry == NULL) {
+                               cERROR(1,("beyond end of smb with num to fill %d i %d",num_to_fill,i)); /* BB removeme BB */
+                               break;
+                       }
+/*                     if((!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) || 
+                          (cifsFile->srch_inf.info_level != something that supports server inodes)) {
+                               create dentry
+                               create inode
+                               fill in inode new_inode (which makes number locally)
+                       }
+                       also create local inode for per reasons unless new mount parm says otherwise */
+                       rc = cifs_filldir2(current_entry, file, 
+                                       filldir, direntry,tmp_buf);
+                       file->f_pos++;
+                       if(file->f_pos == cifsFile->srch_inf.index_of_last_entry) {
+                               cFYI(1,("last entry in buf at pos %lld %s",file->f_pos,tmp_buf)); /* BB removeme BB */
+                               cifs_save_resume_key(current_entry,cifsFile);
+                               break;
+                       } else 
+                               current_entry = nxt_dir_entry(current_entry,end_of_smb);
+               }
+               if(tmp_buf != NULL)
+                       kfree(tmp_buf);
+               break;
+       } /* end switch */
+
+rddir2_exit:
+       /* dump_cifs_file_struct(file, "end rdir ");  */
+       FreeXid(xid);
+       return rc;
+}
+
diff --git a/fs/debugfs/Makefile b/fs/debugfs/Makefile
new file mode 100644 (file)
index 0000000..840c456
--- /dev/null
@@ -0,0 +1,4 @@
+debugfs-objs   := inode.o file.o
+
+obj-$(CONFIG_DEBUG_FS) += debugfs.o
+
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
new file mode 100644 (file)
index 0000000..1e105f9
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ *  file.c - part of debugfs, a tiny little debug file system
+ *
+ *  Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *  Copyright (C) 2004 IBM 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.
+ *
+ *  debugfs is for people to use instead of /proc or /sys.
+ *  See Documentation/DocBook/kernel-api for more details.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/debugfs.h>
+
+static ssize_t default_read_file(struct file *file, char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+       return 0;
+}
+
+static ssize_t default_write_file(struct file *file, const char __user *buf,
+                                  size_t count, loff_t *ppos)
+{
+       return count;
+}
+
+static int default_open(struct inode *inode, struct file *file)
+{
+       if (inode->u.generic_ip)
+               file->private_data = inode->u.generic_ip;
+
+       return 0;
+}
+
+struct file_operations debugfs_file_operations = {
+       .read =         default_read_file,
+       .write =        default_write_file,
+       .open =         default_open,
+};
+
+#define simple_type(type, format, temptype, strtolfn)                          \
+static ssize_t read_file_##type(struct file *file, char __user *user_buf,      \
+                               size_t count, loff_t *ppos)                     \
+{                                                                              \
+       char buf[32];                                                           \
+       type *val = file->private_data;                                         \
+                                                                               \
+       snprintf(buf, sizeof(buf), format, *val);                               \
+       return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));\
+}                                                                              \
+static ssize_t write_file_##type(struct file *file, const char __user *user_buf,\
+                                size_t count, loff_t *ppos)                    \
+{                                                                              \
+       char *endp;                                                             \
+       char buf[32];                                                           \
+       int buf_size;                                                           \
+       type *val = file->private_data;                                         \
+       temptype tmp;                                                           \
+                                                                               \
+       memset(buf, 0x00, sizeof(buf));                                         \
+       buf_size = min(count, (sizeof(buf)-1));                                 \
+       if (copy_from_user(buf, user_buf, buf_size))                            \
+               return -EFAULT;                                                 \
+                                                                               \
+       tmp = strtolfn(buf, &endp, 0);                                          \
+       if ((endp == buf) || ((type)tmp != tmp))                                \
+               return -EINVAL;                                                 \
+       *val = tmp;                                                             \
+       return count;                                                           \
+}                                                                              \
+static struct file_operations fops_##type = {                                  \
+       .read =         read_file_##type,                                       \
+       .write =        write_file_##type,                                      \
+       .open =         default_open,                                           \
+};
+simple_type(u8, "%c", unsigned long, simple_strtoul);
+simple_type(u16, "%hi", unsigned long, simple_strtoul);
+simple_type(u32, "%i", unsigned long, simple_strtoul);
+
+/**
+ * debugfs_create_u8 - create a file in the debugfs filesystem that is used to read and write a unsigned 8 bit value.
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this paramater is NULL, then the
+ *          file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ *         from.
+ *
+ * This function creates a file in debugfs with the given name that
+ * contains the value of the variable @value.  If the @mode variable is so
+ * set, it can be read from, and written to.
+ *
+ * This function will return a pointer to a dentry if it succeeds.  This
+ * pointer must be passed to the debugfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here.)  If an error occurs, NULL will be returned.
+ *
+ * If debugfs is not enabled in the kernel, the value -ENODEV will be
+ * returned.  It is not wise to check for this value, but rather, check for
+ * NULL or !NULL instead as to eliminate the need for #ifdef in the calling
+ * code.
+ */
+struct dentry *debugfs_create_u8(const char *name, mode_t mode,
+                                struct dentry *parent, u8 *value)
+{
+       return debugfs_create_file(name, mode, parent, value, &fops_u8);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_u8);
+
+/**
+ * debugfs_create_u16 - create a file in the debugfs filesystem that is used to read and write a unsigned 8 bit value.
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this paramater is NULL, then the
+ *          file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ *         from.
+ *
+ * This function creates a file in debugfs with the given name that
+ * contains the value of the variable @value.  If the @mode variable is so
+ * set, it can be read from, and written to.
+ *
+ * This function will return a pointer to a dentry if it succeeds.  This
+ * pointer must be passed to the debugfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here.)  If an error occurs, NULL will be returned.
+ *
+ * If debugfs is not enabled in the kernel, the value -ENODEV will be
+ * returned.  It is not wise to check for this value, but rather, check for
+ * NULL or !NULL instead as to eliminate the need for #ifdef in the calling
+ * code.
+ */
+struct dentry *debugfs_create_u16(const char *name, mode_t mode,
+                                 struct dentry *parent, u16 *value)
+{
+       return debugfs_create_file(name, mode, parent, value, &fops_u16);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_u16);
+
+/**
+ * debugfs_create_u32 - create a file in the debugfs filesystem that is used to read and write a unsigned 8 bit value.
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this paramater is NULL, then the
+ *          file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ *         from.
+ *
+ * This function creates a file in debugfs with the given name that
+ * contains the value of the variable @value.  If the @mode variable is so
+ * set, it can be read from, and written to.
+ *
+ * This function will return a pointer to a dentry if it succeeds.  This
+ * pointer must be passed to the debugfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here.)  If an error occurs, NULL will be returned.
+ *
+ * If debugfs is not enabled in the kernel, the value -ENODEV will be
+ * returned.  It is not wise to check for this value, but rather, check for
+ * NULL or !NULL instead as to eliminate the need for #ifdef in the calling
+ * code.
+ */
+struct dentry *debugfs_create_u32(const char *name, mode_t mode,
+                                struct dentry *parent, u32 *value)
+{
+       return debugfs_create_file(name, mode, parent, value, &fops_u32);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_u32);
+
+static ssize_t read_file_bool(struct file *file, char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       char buf[3];
+       u32 *val = file->private_data;
+       
+       if (val)
+               buf[0] = 'Y';
+       else
+               buf[0] = 'N';
+       buf[1] = '\n';
+       buf[2] = 0x00;
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t write_file_bool(struct file *file, const char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       char buf[32];
+       int buf_size;
+       u32 *val = file->private_data;
+
+       buf_size = min(count, (sizeof(buf)-1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       switch (buf[0]) {
+       case 'y':
+       case 'Y':
+       case '1':
+               *val = 1;
+               break;
+       case 'n':
+       case 'N':
+       case '0':
+               *val = 0;
+               break;
+       }
+       
+       return count;
+}
+
+static struct file_operations fops_bool = {
+       .read =         read_file_bool,
+       .write =        write_file_bool,
+       .open =         default_open,
+};
+
+/**
+ * debugfs_create_bool - create a file in the debugfs filesystem that is used to read and write a boolean value.
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this paramater is NULL, then the
+ *          file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ *         from.
+ *
+ * This function creates a file in debugfs with the given name that
+ * contains the value of the variable @value.  If the @mode variable is so
+ * set, it can be read from, and written to.
+ *
+ * This function will return a pointer to a dentry if it succeeds.  This
+ * pointer must be passed to the debugfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here.)  If an error occurs, NULL will be returned.
+ *
+ * If debugfs is not enabled in the kernel, the value -ENODEV will be
+ * returned.  It is not wise to check for this value, but rather, check for
+ * NULL or !NULL instead as to eliminate the need for #ifdef in the calling
+ * code.
+ */
+struct dentry *debugfs_create_bool(const char *name, mode_t mode,
+                                  struct dentry *parent, u32 *value)
+{
+       return debugfs_create_file(name, mode, parent, value, &fops_bool);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_bool);
+
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
new file mode 100644 (file)
index 0000000..b529786
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ *  file.c - part of debugfs, a tiny little debug file system
+ *
+ *  Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *  Copyright (C) 2004 IBM 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.
+ *
+ *  debugfs is for people to use instead of /proc or /sys.
+ *  See Documentation/DocBook/kernel-api for more details.
+ *
+ */
+
+/* uncomment to get debug messages from the debug filesystem, ah the irony. */
+/* #define DEBUG */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/namei.h>
+#include <linux/debugfs.h>
+
+#define DEBUGFS_MAGIC  0x64626720
+
+/* declared over in file.c */
+extern struct file_operations debugfs_file_operations;
+
+static struct vfsmount *debugfs_mount;
+static int debugfs_mount_count;
+
+static struct inode *debugfs_get_inode(struct super_block *sb, int mode, dev_t dev)
+{
+       struct inode *inode = new_inode(sb);
+
+       if (inode) {
+               inode->i_mode = mode;
+               inode->i_uid = 0;
+               inode->i_gid = 0;
+               inode->i_blksize = PAGE_CACHE_SIZE;
+               inode->i_blocks = 0;
+               inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+               switch (mode & S_IFMT) {
+               default:
+                       init_special_inode(inode, mode, dev);
+                       break;
+               case S_IFREG:
+                       inode->i_fop = &debugfs_file_operations;
+                       break;
+               case S_IFDIR:
+                       inode->i_op = &simple_dir_inode_operations;
+                       inode->i_fop = &simple_dir_operations;
+
+                       /* directory inodes start off with i_nlink == 2 (for "." entry) */
+                       inode->i_nlink++;
+                       break;
+               }
+       }
+       return inode; 
+}
+
+/* SMP-safe */
+static int debugfs_mknod(struct inode *dir, struct dentry *dentry,
+                        int mode, dev_t dev)
+{
+       struct inode *inode = debugfs_get_inode(dir->i_sb, mode, dev);
+       int error = -EPERM;
+
+       if (dentry->d_inode)
+               return -EEXIST;
+
+       if (inode) {
+               d_instantiate(dentry, inode);
+               dget(dentry);
+               error = 0;
+       }
+       return error;
+}
+
+static int debugfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+       int res;
+
+       mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
+       res = debugfs_mknod(dir, dentry, mode, 0);
+       if (!res)
+               dir->i_nlink++;
+       return res;
+}
+
+static int debugfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+       mode = (mode & S_IALLUGO) | S_IFREG;
+       return debugfs_mknod(dir, dentry, mode, 0);
+}
+
+static inline int debugfs_positive(struct dentry *dentry)
+{
+       return dentry->d_inode && !d_unhashed(dentry);
+}
+
+static int debug_fill_super(struct super_block *sb, void *data, int silent)
+{
+       static struct tree_descr debug_files[] = {{""}};
+
+       return simple_fill_super(sb, DEBUGFS_MAGIC, debug_files);
+}
+
+static struct dentry * get_dentry(struct dentry *parent, const char *name)
+{               
+       struct qstr qstr;
+
+       qstr.name = name;
+       qstr.len = strlen(name);
+       qstr.hash = full_name_hash(name,qstr.len);
+       return lookup_hash(&qstr,parent);
+}               
+
+static struct super_block *debug_get_sb(struct file_system_type *fs_type,
+                                       int flags, const char *dev_name,
+                                       void *data)
+{
+       return get_sb_single(fs_type, flags, data, debug_fill_super);
+}
+
+static struct file_system_type debug_fs_type = {
+       .owner =        THIS_MODULE,
+       .name =         "debugfs",
+       .get_sb =       debug_get_sb,
+       .kill_sb =      kill_litter_super,
+};
+
+static int debugfs_create_by_name(const char *name, mode_t mode,
+                                 struct dentry *parent,
+                                 struct dentry **dentry)
+{
+       int error = 0;
+
+       /* If the parent is not specified, we create it in the root.
+        * We need the root dentry to do this, which is in the super 
+        * block. A pointer to that is in the struct vfsmount that we
+        * have around.
+        */
+       if (!parent ) {
+               if (debugfs_mount && debugfs_mount->mnt_sb) {
+                       parent = debugfs_mount->mnt_sb->s_root;
+               }
+       }
+       if (!parent) {
+               pr_debug("debugfs: Ah! can not find a parent!\n");
+               return -EFAULT;
+       }
+
+       *dentry = NULL;
+       down(&parent->d_inode->i_sem);
+       *dentry = get_dentry (parent, name);
+       if (!IS_ERR(dentry)) {
+               if ((mode & S_IFMT) == S_IFDIR)
+                       error = debugfs_mkdir(parent->d_inode, *dentry, mode);
+               else 
+                       error = debugfs_create(parent->d_inode, *dentry, mode);
+       } else
+               error = PTR_ERR(dentry);
+       up(&parent->d_inode->i_sem);
+
+       return error;
+}
+
+/**
+ * debugfs_create_file - create a file in the debugfs filesystem
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this paramater is NULL, then the
+ *          file will be created in the root of the debugfs filesystem.
+ * @data: a pointer to something that the caller will want to get to later
+ *        on.  The inode.u.generic_ip pointer will point to this value on
+ *        the open() call.
+ * @fops: a pointer to a struct file_operations that should be used for
+ *        this file.
+ *
+ * This is the basic "create a file" function for debugfs.  It allows for a
+ * wide range of flexibility in createing a file, or a directory (if you
+ * want to create a directory, the debugfs_create_dir() function is
+ * recommended to be used instead.)
+ *
+ * This function will return a pointer to a dentry if it succeeds.  This
+ * pointer must be passed to the debugfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here.)  If an error occurs, NULL will be returned.
+ *
+ * If debugfs is not enabled in the kernel, the value -ENODEV will be
+ * returned.  It is not wise to check for this value, but rather, check for
+ * NULL or !NULL instead as to eliminate the need for #ifdef in the calling
+ * code.
+ */
+struct dentry *debugfs_create_file(const char *name, mode_t mode,
+                                  struct dentry *parent, void *data,
+                                  struct file_operations *fops)
+{
+       struct dentry *dentry = NULL;
+       int error;
+
+       pr_debug("debugfs: creating file '%s'\n",name);
+
+       error = simple_pin_fs("debugfs", &debugfs_mount, &debugfs_mount_count);
+       if (error)
+               goto exit;
+
+       error = debugfs_create_by_name(name, mode, parent, &dentry);
+       if (error) {
+               dentry = NULL;
+               goto exit;
+       }
+
+       if (dentry->d_inode) {
+               if (data)
+                       dentry->d_inode->u.generic_ip = data;
+               if (fops)
+                       dentry->d_inode->i_fop = fops;
+       }
+exit:
+       return dentry;
+}
+EXPORT_SYMBOL_GPL(debugfs_create_file);
+
+/**
+ * debugfs_create_dir - create a directory in the debugfs filesystem
+ *
+ * @name: a pointer to a string containing the name of the directory to
+ *        create.
+ * @parent: a pointer to the parent dentry for this file.  This should be a
+ *          directory dentry if set.  If this paramater is NULL, then the
+ *          directory will be created in the root of the debugfs filesystem.
+ *
+ * This function creates a directory in debugfs with the given name.
+ *
+ * This function will return a pointer to a dentry if it succeeds.  This
+ * pointer must be passed to the debugfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here.)  If an error occurs, NULL will be returned.
+ *
+ * If debugfs is not enabled in the kernel, the value -ENODEV will be
+ * returned.  It is not wise to check for this value, but rather, check for
+ * NULL or !NULL instead as to eliminate the need for #ifdef in the calling
+ * code.
+ */
+struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
+{
+       return debugfs_create_file(name, 
+                                  S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
+                                  parent, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_dir);
+
+/**
+ * debugfs_remove - removes a file or directory from the debugfs filesystem
+ *
+ * @dentry: a pointer to a the dentry of the file or directory to be
+ *          removed.
+ *
+ * This function removes a file or directory in debugfs that was previously
+ * created with a call to another debugfs function (like
+ * debufs_create_file() or variants thereof.)
+ *
+ * This function is required to be called in order for the file to be
+ * removed, no automatic cleanup of files will happen when a module is
+ * removed, you are responsible here.
+ */
+void debugfs_remove(struct dentry *dentry)
+{
+       struct dentry *parent;
+       
+       if (!dentry)
+               return;
+
+       parent = dentry->d_parent;
+       if (!parent || !parent->d_inode)
+               return;
+
+       down(&parent->d_inode->i_sem);
+       if (debugfs_positive(dentry)) {
+               if (dentry->d_inode) {
+                       if (S_ISDIR(dentry->d_inode->i_mode))
+                               simple_rmdir(parent->d_inode, dentry);
+                       else
+                               simple_unlink(parent->d_inode, dentry);
+               dput(dentry);
+               }
+       }
+       up(&parent->d_inode->i_sem);
+       simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+}
+EXPORT_SYMBOL_GPL(debugfs_remove);
+
+static decl_subsys(debug, NULL, NULL);
+
+static int __init debugfs_init(void)
+{
+       int retval;
+
+       kset_set_kset_s(&debug_subsys, kernel_subsys);
+       retval = subsystem_register(&debug_subsys);
+       if (retval)
+               return retval;
+
+       retval = register_filesystem(&debug_fs_type);
+       if (retval)
+               subsystem_unregister(&debug_subsys);
+       return retval;
+}
+
+static void __exit debugfs_exit(void)
+{
+       simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+       unregister_filesystem(&debug_fs_type);
+       subsystem_unregister(&debug_subsys);
+}
+
+core_initcall(debugfs_init);
+module_exit(debugfs_exit);
+MODULE_LICENSE("GPL");
+
diff --git a/fs/hfs/attr.c b/fs/hfs/attr.c
new file mode 100644 (file)
index 0000000..e057ec5
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *  linux/fs/hfs/attr.c
+ *
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Export hfs data via xattr
+ */
+
+
+#include <linux/fs.h>
+#include <linux/xattr.h>
+
+#include "hfs_fs.h"
+#include "btree.h"
+
+int hfs_setxattr(struct dentry *dentry, const char *name,
+                const void *value, size_t size, int flags)
+{
+       struct inode *inode = dentry->d_inode;
+       struct hfs_find_data fd;
+       hfs_cat_rec rec;
+       struct hfs_cat_file *file;
+       int res;
+
+       if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode))
+               return -EOPNOTSUPP;
+
+       res = hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd);
+       if (res)
+               return res;
+       fd.search_key->cat = HFS_I(inode)->cat_key;
+       res = hfs_brec_find(&fd);
+       if (res)
+               goto out;
+       hfs_bnode_read(fd.bnode, &rec, fd.entryoffset,
+                       sizeof(struct hfs_cat_file));
+       file = &rec.file;
+
+       if (!strcmp(name, "hfs.type")) {
+               if (size == 4)
+                       memcpy(&file->UsrWds.fdType, value, 4);
+               else
+                       res = -ERANGE;
+       } else if (!strcmp(name, "hfs.creator")) {
+               if (size == 4)
+                       memcpy(&file->UsrWds.fdCreator, value, 4);
+               else
+                       res = -ERANGE;
+       } else
+               res = -EOPNOTSUPP;
+       if (!res)
+               hfs_bnode_write(fd.bnode, &rec, fd.entryoffset,
+                               sizeof(struct hfs_cat_file));
+out:
+       hfs_find_exit(&fd);
+       return res;
+}
+
+ssize_t hfs_getxattr(struct dentry *dentry, const char *name,
+                        void *value, size_t size)
+{
+       struct inode *inode = dentry->d_inode;
+       struct hfs_find_data fd;
+       hfs_cat_rec rec;
+       struct hfs_cat_file *file;
+       ssize_t res = 0;
+
+       if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode))
+               return -EOPNOTSUPP;
+
+       if (size) {
+               res = hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd);
+               if (res)
+                       return res;
+               fd.search_key->cat = HFS_I(inode)->cat_key;
+               res = hfs_brec_find(&fd);
+               if (res)
+                       goto out;
+               hfs_bnode_read(fd.bnode, &rec, fd.entryoffset,
+                               sizeof(struct hfs_cat_file));
+       }
+       file = &rec.file;
+
+       if (!strcmp(name, "hfs.type")) {
+               if (size >= 4) {
+                       memcpy(value, &file->UsrWds.fdType, 4);
+                       res = 4;
+               } else
+                       res = size ? -ERANGE : 4;
+       } else if (!strcmp(name, "hfs.creator")) {
+               if (size >= 4) {
+                       memcpy(value, &file->UsrWds.fdCreator, 4);
+                       res = 4;
+               } else
+                       res = size ? -ERANGE : 4;
+       } else
+               res = -ENODATA;
+out:
+       if (size)
+               hfs_find_exit(&fd);
+       return res;
+}
+
+#define HFS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
+
+ssize_t hfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
+{
+       struct inode *inode = dentry->d_inode;
+
+       if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode))
+               return -EOPNOTSUPP;
+
+       if (!buffer || !size)
+               return HFS_ATTRLIST_SIZE;
+       if (size < HFS_ATTRLIST_SIZE)
+               return -ERANGE;
+       strcpy(buffer, "hfs.type");
+       strcpy(buffer + sizeof("hfs.type"), "hfs.creator");
+
+       return HFS_ATTRLIST_SIZE;
+}
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
new file mode 100644 (file)
index 0000000..1fe7f53
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+ *  linux/fs/nfsd/nfs4callback.c
+ *
+ *  Copyright (c) 2001 The Regents of the University of Michigan.
+ *  All rights reserved.
+ *
+ *  Kendrick Smith <kmsmith@umich.edu>
+ *  Andy Adamson <andros@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  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 REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/inet.h>
+#include <linux/errno.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/state.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/nfs4.h>
+
+#define NFSDDBG_FACILITY                NFSDDBG_PROC
+
+#define NFSPROC4_CB_NULL 0
+#define NFSPROC4_CB_COMPOUND 1
+
+/* declarations */
+static void nfs4_cb_null(struct rpc_task *task);
+extern spinlock_t recall_lock;
+
+/* Index of predefined Linux callback client operations */
+
+enum {
+        NFSPROC4_CLNT_CB_NULL = 0,
+       NFSPROC4_CLNT_CB_RECALL,
+};
+
+enum nfs_cb_opnum4 {
+       OP_CB_RECALL            = 4,
+};
+
+#define NFS4_MAXTAGLEN         20
+
+#define NFS4_enc_cb_null_sz            0
+#define NFS4_dec_cb_null_sz            0
+#define cb_compound_enc_hdr_sz         4
+#define cb_compound_dec_hdr_sz         (3 + (NFS4_MAXTAGLEN >> 2))
+#define op_enc_sz                      1
+#define op_dec_sz                      2
+#define enc_nfs4_fh_sz                 (1 + (NFS4_FHSIZE >> 2))
+#define enc_stateid_sz                 16
+#define NFS4_enc_cb_recall_sz          (cb_compound_enc_hdr_sz +       \
+                                       1 + enc_stateid_sz +            \
+                                       enc_nfs4_fh_sz)
+
+#define NFS4_dec_cb_recall_sz          (cb_compound_dec_hdr_sz  +      \
+                                       op_dec_sz)
+
+/*
+* Generic encode routines from fs/nfs/nfs4xdr.c
+*/
+static inline u32 *
+xdr_writemem(u32 *p, const void *ptr, int nbytes)
+{
+       int tmp = XDR_QUADLEN(nbytes);
+       if (!tmp)
+               return p;
+       p[tmp-1] = 0;
+       memcpy(p, ptr, nbytes);
+       return p + tmp;
+}
+
+#define WRITE32(n)               *p++ = htonl(n)
+#define WRITEMEM(ptr,nbytes)     do {                           \
+       p = xdr_writemem(p, ptr, nbytes);                       \
+} while (0)
+#define RESERVE_SPACE(nbytes)   do {                            \
+       p = xdr_reserve_space(xdr, nbytes);                     \
+       if (!p) dprintk("NFSD: RESERVE_SPACE(%d) failed in function %s\n", (int) (nbytes), __FUNCTION__); \
+       BUG_ON(!p);                                             \
+} while (0)
+
+/*
+ * Generic decode routines from fs/nfs/nfs4xdr.c
+ */
+#define DECODE_TAIL                             \
+       status = 0;                             \
+out:                                            \
+       return status;                          \
+xdr_error:                                      \
+       dprintk("NFSD: xdr error! (%s:%d)\n", __FILE__, __LINE__); \
+       status = -EIO;                          \
+       goto out
+
+#define READ32(x)         (x) = ntohl(*p++)
+#define READ64(x)         do {                  \
+       (x) = (u64)ntohl(*p++) << 32;           \
+       (x) |= ntohl(*p++);                     \
+} while (0)
+#define READTIME(x)       do {                  \
+       p++;                                    \
+       (x.tv_sec) = ntohl(*p++);               \
+       (x.tv_nsec) = ntohl(*p++);              \
+} while (0)
+#define READ_BUF(nbytes)  do { \
+       p = xdr_inline_decode(xdr, nbytes); \
+       if (!p) { \
+               dprintk("NFSD: %s: reply buffer overflowed in line %d.", \
+                       __FUNCTION__, __LINE__); \
+               return -EIO; \
+       } \
+} while (0)
+
+struct nfs4_cb_compound_hdr {
+       int             status;
+       u32             ident;
+       u32             nops;
+       u32             taglen;
+       char *          tag;
+};
+
+static struct {
+int stat;
+int errno;
+} nfs_cb_errtbl[] = {
+       { NFS4_OK,              0               },
+       { NFS4ERR_PERM,         EPERM           },
+       { NFS4ERR_NOENT,        ENOENT          },
+       { NFS4ERR_IO,           EIO             },
+       { NFS4ERR_NXIO,         ENXIO           },
+       { NFS4ERR_ACCESS,       EACCES          },
+       { NFS4ERR_EXIST,        EEXIST          },
+       { NFS4ERR_XDEV,         EXDEV           },
+       { NFS4ERR_NOTDIR,       ENOTDIR         },
+       { NFS4ERR_ISDIR,        EISDIR          },
+       { NFS4ERR_INVAL,        EINVAL          },
+       { NFS4ERR_FBIG,         EFBIG           },
+       { NFS4ERR_NOSPC,        ENOSPC          },
+       { NFS4ERR_ROFS,         EROFS           },
+       { NFS4ERR_MLINK,        EMLINK          },
+       { NFS4ERR_NAMETOOLONG,  ENAMETOOLONG    },
+       { NFS4ERR_NOTEMPTY,     ENOTEMPTY       },
+       { NFS4ERR_DQUOT,        EDQUOT          },
+       { NFS4ERR_STALE,        ESTALE          },
+       { NFS4ERR_BADHANDLE,    EBADHANDLE      },
+       { NFS4ERR_BAD_COOKIE,   EBADCOOKIE      },
+       { NFS4ERR_NOTSUPP,      ENOTSUPP        },
+       { NFS4ERR_TOOSMALL,     ETOOSMALL       },
+       { NFS4ERR_SERVERFAULT,  ESERVERFAULT    },
+       { NFS4ERR_BADTYPE,      EBADTYPE        },
+       { NFS4ERR_LOCKED,       EAGAIN          },
+       { NFS4ERR_RESOURCE,     EREMOTEIO       },
+       { NFS4ERR_SYMLINK,      ELOOP           },
+       { NFS4ERR_OP_ILLEGAL,   EOPNOTSUPP      },
+       { NFS4ERR_DEADLOCK,     EDEADLK         },
+       { -1,                   EIO             }
+};
+
+static int
+nfs_cb_stat_to_errno(int stat)
+{
+       int i;
+       for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) {
+               if (nfs_cb_errtbl[i].stat == stat)
+                       return nfs_cb_errtbl[i].errno;
+       }
+       /* If we cannot translate the error, the recovery routines should
+       * handle it.
+       * Note: remaining NFSv4 error codes have values > 10000, so should
+       * not conflict with native Linux error codes.
+       */
+       return stat;
+}
+
+/*
+ * XDR encode
+ */
+
+static int
+encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr)
+{
+       u32 * p;
+
+       RESERVE_SPACE(16);
+       WRITE32(0);            /* tag length is always 0 */
+       WRITE32(NFS4_MINOR_VERSION);
+       WRITE32(hdr->ident);
+       WRITE32(hdr->nops);
+       return 0;
+}
+
+static int
+encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec)
+{
+       u32 *p;
+       int len = cb_rec->cbr_fhlen;
+
+       RESERVE_SPACE(12+sizeof(cb_rec->cbr_stateid) + len);
+       WRITE32(OP_CB_RECALL);
+       WRITEMEM(&cb_rec->cbr_stateid, sizeof(stateid_t));
+       WRITE32(cb_rec->cbr_trunc);
+       WRITE32(len);
+       WRITEMEM(cb_rec->cbr_fhval, len);
+       return 0;
+}
+
+static int
+nfs4_xdr_enc_cb_null(struct rpc_rqst *req, u32 *p)
+{
+       struct xdr_stream xdrs, *xdr = &xdrs;
+
+       xdr_init_encode(&xdrs, &req->rq_snd_buf, p);
+        RESERVE_SPACE(0);
+       return 0;
+}
+
+static int
+nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, u32 *p, struct nfs4_cb_recall *args)
+{
+       struct xdr_stream xdr;
+       struct nfs4_cb_compound_hdr hdr = {
+               .nops   = 1,
+       };
+
+       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+       encode_cb_compound_hdr(&xdr, &hdr);
+       return (encode_cb_recall(&xdr, args));
+}
+
+
+static int
+decode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr){
+        u32 *p;
+
+        READ_BUF(8);
+        READ32(hdr->status);
+        READ32(hdr->taglen);
+        READ_BUF(hdr->taglen + 4);
+        hdr->tag = (char *)p;
+        p += XDR_QUADLEN(hdr->taglen);
+        READ32(hdr->nops);
+        return 0;
+}
+
+static int
+decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
+{
+       u32 *p;
+       u32 op;
+       int32_t nfserr;
+
+       READ_BUF(8);
+       READ32(op);
+       if (op != expected) {
+               dprintk("NFSD: decode_cb_op_hdr: Callback server returned "
+                        " operation %d but we issued a request for %d\n",
+                        op, expected);
+               return -EIO;
+       }
+       READ32(nfserr);
+       if (nfserr != NFS_OK)
+               return -nfs_cb_stat_to_errno(nfserr);
+       return 0;
+}
+
+static int
+nfs4_xdr_dec_cb_null(struct rpc_rqst *req, u32 *p)
+{
+       return 0;
+}
+
+static int
+nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, u32 *p)
+{
+       struct xdr_stream xdr;
+       struct nfs4_cb_compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_cb_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_cb_op_hdr(&xdr, OP_CB_RECALL);
+out    :
+       return status;
+}
+
+/*
+ * RPC procedure tables
+ */
+#ifndef MAX
+# define MAX(a, b)      (((a) > (b))? (a) : (b))
+#endif
+
+#define PROC(proc, call, argtype, restype)                              \
+[NFSPROC4_CLNT_##proc] = {                                             \
+        .p_proc   = NFSPROC4_CB_##call,                                        \
+        .p_encode = (kxdrproc_t) nfs4_xdr_##argtype,                    \
+        .p_decode = (kxdrproc_t) nfs4_xdr_##restype,                    \
+        .p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2,  \
+}
+
+struct rpc_procinfo     nfs4_cb_procedures[] = {
+    PROC(CB_NULL,      NULL,     enc_cb_null,     dec_cb_null),
+    PROC(CB_RECALL,    COMPOUND,   enc_cb_recall,      dec_cb_recall),
+};
+
+struct rpc_version              nfs_cb_version4 = {
+        .number                 = 1,
+        .nrprocs                = sizeof(nfs4_cb_procedures)/sizeof(nfs4_cb_procedures[0]),
+        .procs                  = nfs4_cb_procedures
+};
+
+static struct rpc_version *    nfs_cb_version[] = {
+       NULL,
+       &nfs_cb_version4,
+};
+
+/*
+ * Use the SETCLIENTID credential
+ */
+struct rpc_cred *
+nfsd4_lookupcred(struct nfs4_client *clp, int taskflags)
+{
+        struct auth_cred acred;
+       struct rpc_clnt *clnt = clp->cl_callback.cb_client;
+        struct rpc_cred *ret = NULL;
+
+       if (!clnt)
+               goto out;
+        get_group_info(clp->cl_cred.cr_group_info);
+        acred.uid = clp->cl_cred.cr_uid;
+        acred.gid = clp->cl_cred.cr_gid;
+        acred.group_info = clp->cl_cred.cr_group_info;
+
+        dprintk("NFSD:     looking up %s cred\n",
+                clnt->cl_auth->au_ops->au_name);
+        ret = rpcauth_lookup_credcache(clnt->cl_auth, &acred, taskflags);
+        put_group_info(clp->cl_cred.cr_group_info);
+out:
+        return ret;
+}
+
+/*
+ * Set up the callback client and put a NFSPROC4_CB_NULL on the wire...
+ */
+void
+nfsd4_probe_callback(struct nfs4_client *clp)
+{
+       struct sockaddr_in      addr;
+       struct nfs4_callback    *cb = &clp->cl_callback;
+       struct rpc_timeout      timeparms;
+       struct rpc_xprt *       xprt;
+       struct rpc_program *    program = &cb->cb_program;
+       struct rpc_stat *       stat = &cb->cb_stat;
+       struct rpc_clnt *       clnt;
+       struct rpc_message msg = {
+               .rpc_proc       = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
+               .rpc_argp       = clp,
+       };
+       char                    hostname[32];
+       int status;
+
+       dprintk("NFSD: probe_callback. cb_parsed %d cb_set %d\n",
+                       cb->cb_parsed, atomic_read(&cb->cb_set));
+       if (!cb->cb_parsed || atomic_read(&cb->cb_set))
+               return;
+
+       /* Initialize address */
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(cb->cb_port);
+       addr.sin_addr.s_addr = htonl(cb->cb_addr);
+
+       /* Initialize timeout */
+       timeparms.to_initval = (NFSD_LEASE_TIME/4) * HZ;
+       timeparms.to_retries = 5;
+       timeparms.to_maxval = (NFSD_LEASE_TIME/2) * HZ;
+       timeparms.to_exponential = 1;
+
+       /* Create RPC transport */
+       if (!(xprt = xprt_create_proto(IPPROTO_TCP, &addr, &timeparms))) {
+               dprintk("NFSD: couldn't create callback transport!\n");
+               goto out_err;
+       }
+
+       /* Initialize rpc_program */
+       program->name = "nfs4_cb";
+       program->number = cb->cb_prog;
+       program->nrvers = sizeof(nfs_cb_version)/sizeof(nfs_cb_version[0]);
+       program->version = nfs_cb_version;
+       program->stats = stat;
+
+       /* Initialize rpc_stat */
+       memset(stat, 0, sizeof(struct rpc_stat));
+       stat->program = program;
+
+       /* Create RPC client
+        *
+        * XXX AUTH_UNIX only - need AUTH_GSS....
+        */
+       sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr.sin_addr.s_addr));
+       if (!(clnt = rpc_create_client(xprt, hostname, program, 1, RPC_AUTH_UNIX))) {
+               dprintk("NFSD: couldn't create callback client\n");
+               goto out_xprt;
+       }
+       clnt->cl_intr = 1;
+       clnt->cl_softrtry = 1;
+       clnt->cl_chatty = 1;
+       cb->cb_client = clnt;
+
+       /* Kick rpciod, put the call on the wire. */
+
+       if (rpciod_up() != 0) {
+               dprintk("nfsd: couldn't start rpciod for callbacks!\n");
+               goto out_clnt;
+       }
+
+       /* the task holds a reference to the nfs4_client struct */
+       atomic_inc(&clp->cl_count);
+
+       msg.rpc_cred = nfsd4_lookupcred(clp,0);
+       status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, nfs4_cb_null, NULL);
+
+       if (status != 0) {
+               dprintk("NFSD: asynchronous NFSPROC4_CB_NULL failed!\n");
+               goto out_rpciod;
+       }
+       return;
+
+out_rpciod:
+       rpciod_down();
+out_clnt:
+       rpc_shutdown_client(clnt);
+       goto out_err;
+out_xprt:
+       xprt_destroy(xprt);
+out_err:
+       dprintk("NFSD: warning: no callback path to client %.*s\n",
+               (int)clp->cl_name.len, clp->cl_name.data);
+       cb->cb_client = NULL;
+}
+
+static void
+nfs4_cb_null(struct rpc_task *task)
+{
+       struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp;
+       struct nfs4_callback *cb = &clp->cl_callback;
+       u32 addr = htonl(cb->cb_addr);
+
+       dprintk("NFSD: nfs4_cb_null task->tk_status %d\n", task->tk_status);
+
+       if (task->tk_status < 0) {
+               dprintk("NFSD: callback establishment to client %.*s failed\n",
+                       (int)clp->cl_name.len, clp->cl_name.data);
+               goto out;
+       }
+       atomic_set(&cb->cb_set, 1);
+       dprintk("NFSD: callback set to client %u.%u.%u.%u\n", NIPQUAD(addr));
+out:
+       put_nfs4_client(clp);
+}
+
+/*
+ *  Called with dp->dl_count incremented
+ */
+static void
+nfs4_cb_recall_done(struct rpc_task *task)
+{
+       struct nfs4_cb_recall *cbr = (struct nfs4_cb_recall *)task->tk_calldata;
+       struct nfs4_delegation *dp = cbr->cbr_dp;
+       int status;
+
+       /* all is well... */
+       if (task->tk_status == 0)
+               goto out;
+
+       /* network partition, retry nfsd4_cb_recall once.  */
+       if (task->tk_status == -EIO) {
+               if (atomic_read(&dp->dl_recall_cnt) == 0)
+                       goto retry;
+               else
+                       /* callback channel no longer available */
+                       atomic_set(&dp->dl_client->cl_callback.cb_set, 0);
+       }
+
+       /* Race: a recall occurred miliseconds after a delegation was granted.
+       * Client may have received recall prior to delegation. retry recall
+       * once.
+       */
+       if ((task->tk_status == -EBADHANDLE) || (task->tk_status == -NFS4ERR_BAD_STATEID)){
+               if (atomic_read(&dp->dl_recall_cnt) == 0)
+                       goto retry;
+       }
+       atomic_set(&dp->dl_state, NFS4_RECALL_COMPLETE);
+
+out:
+       if (atomic_dec_and_test(&dp->dl_count))
+               atomic_set(&dp->dl_state, NFS4_REAP_DELEG);
+       BUG_ON(atomic_read(&dp->dl_count) < 0);
+       dprintk("NFSD: nfs4_cb_recall_done: dp %p dl_flock %p dl_count %d\n",dp, dp->dl_flock, atomic_read(&dp->dl_count));
+       return;
+
+retry:
+       atomic_inc(&dp->dl_recall_cnt);
+       /* sleep 2 seconds before retrying recall */
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(2*HZ);
+       status = nfsd4_cb_recall(dp);
+       dprintk("NFSD: nfs4_cb_recall_done: retry status: %d  dp %p dl_flock %p\n",status,dp, dp->dl_flock);
+}
+
+/*
+ * called with dp->dl_count inc'ed.
+ * nfs4_lock_state() may or may not have been called.
+ */
+int
+nfsd4_cb_recall(struct nfs4_delegation *dp)
+{
+       struct nfs4_client *clp;
+       struct rpc_clnt *clnt;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
+       };
+       struct nfs4_cb_recall *cbr = &dp->dl_recall;
+       int status;
+
+       dprintk("NFSD: nfsd4_cb_recall NFS4_enc_cb_recall_sz %d NFS4_dec_cb_recall_sz %d \n",NFS4_enc_cb_recall_sz,NFS4_dec_cb_recall_sz);
+
+       clp = dp->dl_client;
+       clnt = clp->cl_callback.cb_client;
+       status = EIO;
+       if ((!atomic_read(&clp->cl_callback.cb_set)) || !clnt)
+               goto out_free;
+
+       msg.rpc_argp = cbr;
+       msg.rpc_resp = cbr;
+       msg.rpc_cred = nfsd4_lookupcred(clp,0);
+
+       cbr->cbr_trunc = 0; /* XXX need to implement truncate optimization */
+       cbr->cbr_dp = dp;
+
+       if ((status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT,
+               nfs4_cb_recall_done, cbr ))) {
+               dprintk("NFSD: recall_delegation: rpc_call_async failed %d\n",
+                       status);
+               goto out_fail;
+       }
+out:
+       return status;
+out_fail:
+       status = nfserrno(status);
+       out_free:
+       kfree(cbr);
+       goto out;
+}
diff --git a/fs/ntfs/aops.h b/fs/ntfs/aops.h
new file mode 100644 (file)
index 0000000..3b74e66
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+ * aops.h - Defines for NTFS kernel address space operations and page cache
+ *         handling.  Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001-2004 Anton Altaparmakov
+ * Copyright (c) 2002 Richard Russon
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_AOPS_H
+#define _LINUX_NTFS_AOPS_H
+
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+
+#include "inode.h"
+
+/**
+ * ntfs_unmap_page - release a page that was mapped using ntfs_map_page()
+ * @page:      the page to release
+ *
+ * Unpin, unmap and release a page that was obtained from ntfs_map_page().
+ */
+static inline void ntfs_unmap_page(struct page *page)
+{
+       kunmap(page);
+       page_cache_release(page);
+}
+
+/**
+ * ntfs_map_page - map a page into accessible memory, reading it if necessary
+ * @mapping:   address space for which to obtain the page
+ * @index:     index into the page cache for @mapping of the page to map
+ *
+ * Read a page from the page cache of the address space @mapping at position
+ * @index, where @index is in units of PAGE_CACHE_SIZE, and not in bytes.
+ *
+ * If the page is not in memory it is loaded from disk first using the readpage
+ * method defined in the address space operations of @mapping and the page is
+ * added to the page cache of @mapping in the process.
+ *
+ * If the page belongs to an mst protected attribute and it is marked as such
+ * in its ntfs inode (NInoMstProtected()) the mst fixups are applied but no
+ * error checking is performed.  This means the caller has to verify whether
+ * the ntfs record(s) contained in the page are valid or not using one of the
+ * ntfs_is_XXXX_record{,p}() macros, where XXXX is the record type you are
+ * expecting to see.  (For details of the macros, see fs/ntfs/layout.h.)
+ *
+ * If the page is in high memory it is mapped into memory directly addressible
+ * by the kernel.
+ *
+ * Finally the page count is incremented, thus pinning the page into place.
+ *
+ * The above means that page_address(page) can be used on all pages obtained
+ * with ntfs_map_page() to get the kernel virtual address of the page.
+ *
+ * When finished with the page, the caller has to call ntfs_unmap_page() to
+ * unpin, unmap and release the page.
+ *
+ * Note this does not grant exclusive access. If such is desired, the caller
+ * must provide it independently of the ntfs_{un}map_page() calls by using
+ * a {rw_}semaphore or other means of serialization. A spin lock cannot be
+ * used as ntfs_map_page() can block.
+ *
+ * The unlocked and uptodate page is returned on success or an encoded error
+ * on failure. Caller has to test for error using the IS_ERR() macro on the
+ * return value. If that evaluates to TRUE, the negative error code can be
+ * obtained using PTR_ERR() on the return value of ntfs_map_page().
+ */
+static inline struct page *ntfs_map_page(struct address_space *mapping,
+               unsigned long index)
+{
+       struct page *page = read_cache_page(mapping, index,
+                       (filler_t*)mapping->a_ops->readpage, NULL);
+
+       if (!IS_ERR(page)) {
+               wait_on_page_locked(page);
+               kmap(page);
+               if (PageUptodate(page) && !PageError(page))
+                       return page;
+               ntfs_unmap_page(page);
+               return ERR_PTR(-EIO);
+       }
+       return page;
+}
+
+#ifdef NTFS_RW
+
+extern void mark_ntfs_record_dirty(struct page *page, const unsigned int ofs);
+
+#endif /* NTFS_RW */
+
+#endif /* _LINUX_NTFS_AOPS_H */
diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c
new file mode 100644 (file)
index 0000000..8438fb1
--- /dev/null
@@ -0,0 +1,1438 @@
+/**
+ * runlist.c - NTFS runlist handling code.  Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001-2004 Anton Altaparmakov
+ * Copyright (c) 2002 Richard Russon
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "debug.h"
+#include "dir.h"
+#include "endian.h"
+#include "malloc.h"
+#include "ntfs.h"
+
+/**
+ * ntfs_rl_mm - runlist memmove
+ *
+ * It is up to the caller to serialize access to the runlist @base.
+ */
+static inline void ntfs_rl_mm(runlist_element *base, int dst, int src,
+               int size)
+{
+       if (likely((dst != src) && (size > 0)))
+               memmove(base + dst, base + src, size * sizeof (*base));
+}
+
+/**
+ * ntfs_rl_mc - runlist memory copy
+ *
+ * It is up to the caller to serialize access to the runlists @dstbase and
+ * @srcbase.
+ */
+static inline void ntfs_rl_mc(runlist_element *dstbase, int dst,
+               runlist_element *srcbase, int src, int size)
+{
+       if (likely(size > 0))
+               memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase));
+}
+
+/**
+ * ntfs_rl_realloc - Reallocate memory for runlists
+ * @rl:                original runlist
+ * @old_size:  number of runlist elements in the original runlist @rl
+ * @new_size:  number of runlist elements we need space for
+ *
+ * As the runlists grow, more memory will be required.  To prevent the
+ * kernel having to allocate and reallocate large numbers of small bits of
+ * memory, this function returns and entire page of memory.
+ *
+ * It is up to the caller to serialize access to the runlist @rl.
+ *
+ * N.B.  If the new allocation doesn't require a different number of pages in
+ *       memory, the function will return the original pointer.
+ *
+ * On success, return a pointer to the newly allocated, or recycled, memory.
+ * On error, return -errno. The following error codes are defined:
+ *     -ENOMEM - Not enough memory to allocate runlist array.
+ *     -EINVAL - Invalid parameters were passed in.
+ */
+static inline runlist_element *ntfs_rl_realloc(runlist_element *rl,
+               int old_size, int new_size)
+{
+       runlist_element *new_rl;
+
+       old_size = PAGE_ALIGN(old_size * sizeof(*rl));
+       new_size = PAGE_ALIGN(new_size * sizeof(*rl));
+       if (old_size == new_size)
+               return rl;
+
+       new_rl = ntfs_malloc_nofs(new_size);
+       if (unlikely(!new_rl))
+               return ERR_PTR(-ENOMEM);
+
+       if (likely(rl != NULL)) {
+               if (unlikely(old_size > new_size))
+                       old_size = new_size;
+               memcpy(new_rl, rl, old_size);
+               ntfs_free(rl);
+       }
+       return new_rl;
+}
+
+/**
+ * ntfs_are_rl_mergeable - test if two runlists can be joined together
+ * @dst:       original runlist
+ * @src:       new runlist to test for mergeability with @dst
+ *
+ * Test if two runlists can be joined together. For this, their VCNs and LCNs
+ * must be adjacent.
+ *
+ * It is up to the caller to serialize access to the runlists @dst and @src.
+ *
+ * Return: TRUE   Success, the runlists can be merged.
+ *        FALSE  Failure, the runlists cannot be merged.
+ */
+static inline BOOL ntfs_are_rl_mergeable(runlist_element *dst,
+               runlist_element *src)
+{
+       BUG_ON(!dst);
+       BUG_ON(!src);
+
+       if ((dst->lcn < 0) || (src->lcn < 0))     /* Are we merging holes? */
+               return FALSE;
+       if ((dst->lcn + dst->length) != src->lcn) /* Are the runs contiguous? */
+               return FALSE;
+       if ((dst->vcn + dst->length) != src->vcn) /* Are the runs misaligned? */
+               return FALSE;
+
+       return TRUE;
+}
+
+/**
+ * __ntfs_rl_merge - merge two runlists without testing if they can be merged
+ * @dst:       original, destination runlist
+ * @src:       new runlist to merge with @dst
+ *
+ * Merge the two runlists, writing into the destination runlist @dst. The
+ * caller must make sure the runlists can be merged or this will corrupt the
+ * destination runlist.
+ *
+ * It is up to the caller to serialize access to the runlists @dst and @src.
+ */
+static inline void __ntfs_rl_merge(runlist_element *dst, runlist_element *src)
+{
+       dst->length += src->length;
+}
+
+/**
+ * ntfs_rl_append - append a runlist after a given element
+ * @dst:       original runlist to be worked on
+ * @dsize:     number of elements in @dst (including end marker)
+ * @src:       runlist to be inserted into @dst
+ * @ssize:     number of elements in @src (excluding end marker)
+ * @loc:       append the new runlist @src after this element in @dst
+ *
+ * Append the runlist @src after element @loc in @dst.  Merge the right end of
+ * the new runlist, if necessary. Adjust the size of the hole before the
+ * appended runlist.
+ *
+ * It is up to the caller to serialize access to the runlists @dst and @src.
+ *
+ * On success, return a pointer to the new, combined, runlist. Note, both
+ * runlists @dst and @src are deallocated before returning so you cannot use
+ * the pointers for anything any more. (Strictly speaking the returned runlist
+ * may be the same as @dst but this is irrelevant.)
+ *
+ * On error, return -errno. Both runlists are left unmodified. The following
+ * error codes are defined:
+ *     -ENOMEM - Not enough memory to allocate runlist array.
+ *     -EINVAL - Invalid parameters were passed in.
+ */
+static inline runlist_element *ntfs_rl_append(runlist_element *dst,
+               int dsize, runlist_element *src, int ssize, int loc)
+{
+       BOOL right;
+       int magic;
+
+       BUG_ON(!dst);
+       BUG_ON(!src);
+
+       /* First, check if the right hand end needs merging. */
+       right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1);
+
+       /* Space required: @dst size + @src size, less one if we merged. */
+       dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right);
+       if (IS_ERR(dst))
+               return dst;
+       /*
+        * We are guaranteed to succeed from here so can start modifying the
+        * original runlists.
+        */
+
+       /* First, merge the right hand end, if necessary. */
+       if (right)
+               __ntfs_rl_merge(src + ssize - 1, dst + loc + 1);
+
+       magic = loc + ssize;
+
+       /* Move the tail of @dst out of the way, then copy in @src. */
+       ntfs_rl_mm(dst, magic + 1, loc + 1 + right, dsize - loc - 1 - right);
+       ntfs_rl_mc(dst, loc + 1, src, 0, ssize);
+
+       /* Adjust the size of the preceding hole. */
+       dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn;
+
+       /* We may have changed the length of the file, so fix the end marker */
+       if (dst[magic + 1].lcn == LCN_ENOENT)
+               dst[magic + 1].vcn = dst[magic].vcn + dst[magic].length;
+
+       return dst;
+}
+
+/**
+ * ntfs_rl_insert - insert a runlist into another
+ * @dst:       original runlist to be worked on
+ * @dsize:     number of elements in @dst (including end marker)
+ * @src:       new runlist to be inserted
+ * @ssize:     number of elements in @src (excluding end marker)
+ * @loc:       insert the new runlist @src before this element in @dst
+ *
+ * Insert the runlist @src before element @loc in the runlist @dst. Merge the
+ * left end of the new runlist, if necessary. Adjust the size of the hole
+ * after the inserted runlist.
+ *
+ * It is up to the caller to serialize access to the runlists @dst and @src.
+ *
+ * On success, return a pointer to the new, combined, runlist. Note, both
+ * runlists @dst and @src are deallocated before returning so you cannot use
+ * the pointers for anything any more. (Strictly speaking the returned runlist
+ * may be the same as @dst but this is irrelevant.)
+ *
+ * On error, return -errno. Both runlists are left unmodified. The following
+ * error codes are defined:
+ *     -ENOMEM - Not enough memory to allocate runlist array.
+ *     -EINVAL - Invalid parameters were passed in.
+ */
+static inline runlist_element *ntfs_rl_insert(runlist_element *dst,
+               int dsize, runlist_element *src, int ssize, int loc)
+{
+       BOOL left = FALSE;
+       BOOL disc = FALSE;      /* Discontinuity */
+       BOOL hole = FALSE;      /* Following a hole */
+       int magic;
+
+       BUG_ON(!dst);
+       BUG_ON(!src);
+
+       /* disc => Discontinuity between the end of @dst and the start of @src.
+        *         This means we might need to insert a hole.
+        * hole => @dst ends with a hole or an unmapped region which we can
+        *         extend to match the discontinuity. */
+       if (loc == 0)
+               disc = (src[0].vcn > 0);
+       else {
+               s64 merged_length;
+
+               left = ntfs_are_rl_mergeable(dst + loc - 1, src);
+
+               merged_length = dst[loc - 1].length;
+               if (left)
+                       merged_length += src->length;
+
+               disc = (src[0].vcn > dst[loc - 1].vcn + merged_length);
+               if (disc)
+                       hole = (dst[loc - 1].lcn == LCN_HOLE);
+       }
+
+       /* Space required: @dst size + @src size, less one if we merged, plus
+        * one if there was a discontinuity, less one for a trailing hole. */
+       dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc - hole);
+       if (IS_ERR(dst))
+               return dst;
+       /*
+        * We are guaranteed to succeed from here so can start modifying the
+        * original runlist.
+        */
+
+       if (left)
+               __ntfs_rl_merge(dst + loc - 1, src);
+
+       magic = loc + ssize - left + disc - hole;
+
+       /* Move the tail of @dst out of the way, then copy in @src. */
+       ntfs_rl_mm(dst, magic, loc, dsize - loc);
+       ntfs_rl_mc(dst, loc + disc - hole, src, left, ssize - left);
+
+       /* Adjust the VCN of the last run ... */
+       if (dst[magic].lcn <= LCN_HOLE)
+               dst[magic].vcn = dst[magic - 1].vcn + dst[magic - 1].length;
+       /* ... and the length. */
+       if (dst[magic].lcn == LCN_HOLE || dst[magic].lcn == LCN_RL_NOT_MAPPED)
+               dst[magic].length = dst[magic + 1].vcn - dst[magic].vcn;
+
+       /* Writing beyond the end of the file and there's a discontinuity. */
+       if (disc) {
+               if (hole)
+                       dst[loc - 1].length = dst[loc].vcn - dst[loc - 1].vcn;
+               else {
+                       if (loc > 0) {
+                               dst[loc].vcn = dst[loc - 1].vcn +
+                                               dst[loc - 1].length;
+                               dst[loc].length = dst[loc + 1].vcn -
+                                               dst[loc].vcn;
+                       } else {
+                               dst[loc].vcn = 0;
+                               dst[loc].length = dst[loc + 1].vcn;
+                       }
+                       dst[loc].lcn = LCN_RL_NOT_MAPPED;
+               }
+
+               magic += hole;
+
+               if (dst[magic].lcn == LCN_ENOENT)
+                       dst[magic].vcn = dst[magic - 1].vcn +
+                                       dst[magic - 1].length;
+       }
+       return dst;
+}
+
+/**
+ * ntfs_rl_replace - overwrite a runlist element with another runlist
+ * @dst:       original runlist to be worked on
+ * @dsize:     number of elements in @dst (including end marker)
+ * @src:       new runlist to be inserted
+ * @ssize:     number of elements in @src (excluding end marker)
+ * @loc:       index in runlist @dst to overwrite with @src
+ *
+ * Replace the runlist element @dst at @loc with @src. Merge the left and
+ * right ends of the inserted runlist, if necessary.
+ *
+ * It is up to the caller to serialize access to the runlists @dst and @src.
+ *
+ * On success, return a pointer to the new, combined, runlist. Note, both
+ * runlists @dst and @src are deallocated before returning so you cannot use
+ * the pointers for anything any more. (Strictly speaking the returned runlist
+ * may be the same as @dst but this is irrelevant.)
+ *
+ * On error, return -errno. Both runlists are left unmodified. The following
+ * error codes are defined:
+ *     -ENOMEM - Not enough memory to allocate runlist array.
+ *     -EINVAL - Invalid parameters were passed in.
+ */
+static inline runlist_element *ntfs_rl_replace(runlist_element *dst,
+               int dsize, runlist_element *src, int ssize, int loc)
+{
+       BOOL left = FALSE;
+       BOOL right;
+       int magic;
+
+       BUG_ON(!dst);
+       BUG_ON(!src);
+
+       /* First, merge the left and right ends, if necessary. */
+       right = ntfs_are_rl_mergeable(src + ssize - 1, dst + loc + 1);
+       if (loc > 0)
+               left = ntfs_are_rl_mergeable(dst + loc - 1, src);
+
+       /* Allocate some space. We'll need less if the left, right, or both
+        * ends were merged. */
+       dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left - right);
+       if (IS_ERR(dst))
+               return dst;
+       /*
+        * We are guaranteed to succeed from here so can start modifying the
+        * original runlists.
+        */
+       if (right)
+               __ntfs_rl_merge(src + ssize - 1, dst + loc + 1);
+       if (left)
+               __ntfs_rl_merge(dst + loc - 1, src);
+
+       /* FIXME: What does this mean? (AIA) */
+       magic = loc + ssize - left;
+
+       /* Move the tail of @dst out of the way, then copy in @src. */
+       ntfs_rl_mm(dst, magic, loc + right + 1, dsize - loc - right - 1);
+       ntfs_rl_mc(dst, loc, src, left, ssize - left);
+
+       /* We may have changed the length of the file, so fix the end marker */
+       if (dst[magic].lcn == LCN_ENOENT)
+               dst[magic].vcn = dst[magic - 1].vcn + dst[magic - 1].length;
+       return dst;
+}
+
+/**
+ * ntfs_rl_split - insert a runlist into the centre of a hole
+ * @dst:       original runlist to be worked on
+ * @dsize:     number of elements in @dst (including end marker)
+ * @src:       new runlist to be inserted
+ * @ssize:     number of elements in @src (excluding end marker)
+ * @loc:       index in runlist @dst at which to split and insert @src
+ *
+ * Split the runlist @dst at @loc into two and insert @new in between the two
+ * fragments. No merging of runlists is necessary. Adjust the size of the
+ * holes either side.
+ *
+ * It is up to the caller to serialize access to the runlists @dst and @src.
+ *
+ * On success, return a pointer to the new, combined, runlist. Note, both
+ * runlists @dst and @src are deallocated before returning so you cannot use
+ * the pointers for anything any more. (Strictly speaking the returned runlist
+ * may be the same as @dst but this is irrelevant.)
+ *
+ * On error, return -errno. Both runlists are left unmodified. The following
+ * error codes are defined:
+ *     -ENOMEM - Not enough memory to allocate runlist array.
+ *     -EINVAL - Invalid parameters were passed in.
+ */
+static inline runlist_element *ntfs_rl_split(runlist_element *dst, int dsize,
+               runlist_element *src, int ssize, int loc)
+{
+       BUG_ON(!dst);
+       BUG_ON(!src);
+
+       /* Space required: @dst size + @src size + one new hole. */
+       dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1);
+       if (IS_ERR(dst))
+               return dst;
+       /*
+        * We are guaranteed to succeed from here so can start modifying the
+        * original runlists.
+        */
+
+       /* Move the tail of @dst out of the way, then copy in @src. */
+       ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc);
+       ntfs_rl_mc(dst, loc + 1, src, 0, ssize);
+
+       /* Adjust the size of the holes either size of @src. */
+       dst[loc].length         = dst[loc+1].vcn       - dst[loc].vcn;
+       dst[loc+ssize+1].vcn    = dst[loc+ssize].vcn   + dst[loc+ssize].length;
+       dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn;
+
+       return dst;
+}
+
+/**
+ * ntfs_runlists_merge - merge two runlists into one
+ * @drl:       original runlist to be worked on
+ * @srl:       new runlist to be merged into @drl
+ *
+ * First we sanity check the two runlists @srl and @drl to make sure that they
+ * are sensible and can be merged. The runlist @srl must be either after the
+ * runlist @drl or completely within a hole (or unmapped region) in @drl.
+ *
+ * It is up to the caller to serialize access to the runlists @drl and @srl.
+ *
+ * Merging of runlists is necessary in two cases:
+ *   1. When attribute lists are used and a further extent is being mapped.
+ *   2. When new clusters are allocated to fill a hole or extend a file.
+ *
+ * There are four possible ways @srl can be merged. It can:
+ *     - be inserted at the beginning of a hole,
+ *     - split the hole in two and be inserted between the two fragments,
+ *     - be appended at the end of a hole, or it can
+ *     - replace the whole hole.
+ * It can also be appended to the end of the runlist, which is just a variant
+ * of the insert case.
+ *
+ * On success, return a pointer to the new, combined, runlist. Note, both
+ * runlists @drl and @srl are deallocated before returning so you cannot use
+ * the pointers for anything any more. (Strictly speaking the returned runlist
+ * may be the same as @dst but this is irrelevant.)
+ *
+ * On error, return -errno. Both runlists are left unmodified. The following
+ * error codes are defined:
+ *     -ENOMEM - Not enough memory to allocate runlist array.
+ *     -EINVAL - Invalid parameters were passed in.
+ *     -ERANGE - The runlists overlap and cannot be merged.
+ */
+runlist_element *ntfs_runlists_merge(runlist_element *drl,
+               runlist_element *srl)
+{
+       int di, si;             /* Current index into @[ds]rl. */
+       int sstart;             /* First index with lcn > LCN_RL_NOT_MAPPED. */
+       int dins;               /* Index into @drl at which to insert @srl. */
+       int dend, send;         /* Last index into @[ds]rl. */
+       int dfinal, sfinal;     /* The last index into @[ds]rl with
+                                  lcn >= LCN_HOLE. */
+       int marker = 0;
+       VCN marker_vcn = 0;
+
+#ifdef DEBUG
+       ntfs_debug("dst:");
+       ntfs_debug_dump_runlist(drl);
+       ntfs_debug("src:");
+       ntfs_debug_dump_runlist(srl);
+#endif
+
+       /* Check for silly calling... */
+       if (unlikely(!srl))
+               return drl;
+       if (IS_ERR(srl) || IS_ERR(drl))
+               return ERR_PTR(-EINVAL);
+
+       /* Check for the case where the first mapping is being done now. */
+       if (unlikely(!drl)) {
+               drl = srl;
+               /* Complete the source runlist if necessary. */
+               if (unlikely(drl[0].vcn)) {
+                       /* Scan to the end of the source runlist. */
+                       for (dend = 0; likely(drl[dend].length); dend++)
+                               ;
+                       drl = ntfs_rl_realloc(drl, dend, dend + 1);
+                       if (IS_ERR(drl))
+                               return drl;
+                       /* Insert start element at the front of the runlist. */
+                       ntfs_rl_mm(drl, 1, 0, dend);
+                       drl[0].vcn = 0;
+                       drl[0].lcn = LCN_RL_NOT_MAPPED;
+                       drl[0].length = drl[1].vcn;
+               }
+               goto finished;
+       }
+
+       si = di = 0;
+
+       /* Skip any unmapped start element(s) in the source runlist. */
+       while (srl[si].length && srl[si].lcn < LCN_HOLE)
+               si++;
+
+       /* Can't have an entirely unmapped source runlist. */
+       BUG_ON(!srl[si].length);
+
+       /* Record the starting points. */
+       sstart = si;
+
+       /*
+        * Skip forward in @drl until we reach the position where @srl needs to
+        * be inserted. If we reach the end of @drl, @srl just needs to be
+        * appended to @drl.
+        */
+       for (; drl[di].length; di++) {
+               if (drl[di].vcn + drl[di].length > srl[sstart].vcn)
+                       break;
+       }
+       dins = di;
+
+       /* Sanity check for illegal overlaps. */
+       if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) &&
+                       (srl[si].lcn >= 0)) {
+               ntfs_error(NULL, "Run lists overlap. Cannot merge!");
+               return ERR_PTR(-ERANGE);
+       }
+
+       /* Scan to the end of both runlists in order to know their sizes. */
+       for (send = si; srl[send].length; send++)
+               ;
+       for (dend = di; drl[dend].length; dend++)
+               ;
+
+       if (srl[send].lcn == LCN_ENOENT)
+               marker_vcn = srl[marker = send].vcn;
+
+       /* Scan to the last element with lcn >= LCN_HOLE. */
+       for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--)
+               ;
+       for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--)
+               ;
+
+       {
+       BOOL start;
+       BOOL finish;
+       int ds = dend + 1;              /* Number of elements in drl & srl */
+       int ss = sfinal - sstart + 1;
+
+       start  = ((drl[dins].lcn <  LCN_RL_NOT_MAPPED) ||    /* End of file   */
+                 (drl[dins].vcn == srl[sstart].vcn));       /* Start of hole */
+       finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) &&    /* End of file   */
+                ((drl[dins].vcn + drl[dins].length) <=      /* End of hole   */
+                 (srl[send - 1].vcn + srl[send - 1].length)));
+
+       /* Or we'll lose an end marker */
+       if (start && finish && (drl[dins].length == 0))
+               ss++;
+       if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn))
+               finish = FALSE;
+#if 0
+       ntfs_debug("dfinal = %i, dend = %i", dfinal, dend);
+       ntfs_debug("sstart = %i, sfinal = %i, send = %i", sstart, sfinal, send);
+       ntfs_debug("start = %i, finish = %i", start, finish);
+       ntfs_debug("ds = %i, ss = %i, dins = %i", ds, ss, dins);
+#endif
+       if (start) {
+               if (finish)
+                       drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins);
+               else
+                       drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins);
+       } else {
+               if (finish)
+                       drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins);
+               else
+                       drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins);
+       }
+       if (IS_ERR(drl)) {
+               ntfs_error(NULL, "Merge failed.");
+               return drl;
+       }
+       ntfs_free(srl);
+       if (marker) {
+               ntfs_debug("Triggering marker code.");
+               for (ds = dend; drl[ds].length; ds++)
+                       ;
+               /* We only need to care if @srl ended after @drl. */
+               if (drl[ds].vcn <= marker_vcn) {
+                       int slots = 0;
+
+                       if (drl[ds].vcn == marker_vcn) {
+                               ntfs_debug("Old marker = 0x%llx, replacing "
+                                               "with LCN_ENOENT.",
+                                               (unsigned long long)
+                                               drl[ds].lcn);
+                               drl[ds].lcn = LCN_ENOENT;
+                               goto finished;
+                       }
+                       /*
+                        * We need to create an unmapped runlist element in
+                        * @drl or extend an existing one before adding the
+                        * ENOENT terminator.
+                        */
+                       if (drl[ds].lcn == LCN_ENOENT) {
+                               ds--;
+                               slots = 1;
+                       }
+                       if (drl[ds].lcn != LCN_RL_NOT_MAPPED) {
+                               /* Add an unmapped runlist element. */
+                               if (!slots) {
+                                       /* FIXME/TODO: We need to have the
+                                        * extra memory already! (AIA) */
+                                       drl = ntfs_rl_realloc(drl, ds, ds + 2);
+                                       if (!drl)
+                                               goto critical_error;
+                                       slots = 2;
+                               }
+                               ds++;
+                               /* Need to set vcn if it isn't set already. */
+                               if (slots != 1)
+                                       drl[ds].vcn = drl[ds - 1].vcn +
+                                                       drl[ds - 1].length;
+                               drl[ds].lcn = LCN_RL_NOT_MAPPED;
+                               /* We now used up a slot. */
+                               slots--;
+                       }
+                       drl[ds].length = marker_vcn - drl[ds].vcn;
+                       /* Finally add the ENOENT terminator. */
+                       ds++;
+                       if (!slots) {
+                               /* FIXME/TODO: We need to have the extra
+                                * memory already! (AIA) */
+                               drl = ntfs_rl_realloc(drl, ds, ds + 1);
+                               if (!drl)
+                                       goto critical_error;
+                       }
+                       drl[ds].vcn = marker_vcn;
+                       drl[ds].lcn = LCN_ENOENT;
+                       drl[ds].length = (s64)0;
+               }
+       }
+       }
+
+finished:
+       /* The merge was completed successfully. */
+       ntfs_debug("Merged runlist:");
+       ntfs_debug_dump_runlist(drl);
+       return drl;
+
+critical_error:
+       /* Critical error! We cannot afford to fail here. */
+       ntfs_error(NULL, "Critical error! Not enough memory.");
+       panic("NTFS: Cannot continue.");
+}
+
+/**
+ * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist
+ * @vol:       ntfs volume on which the attribute resides
+ * @attr:      attribute record whose mapping pairs array to decompress
+ * @old_rl:    optional runlist in which to insert @attr's runlist
+ *
+ * It is up to the caller to serialize access to the runlist @old_rl.
+ *
+ * Decompress the attribute @attr's mapping pairs array into a runlist. On
+ * success, return the decompressed runlist.
+ *
+ * If @old_rl is not NULL, decompressed runlist is inserted into the
+ * appropriate place in @old_rl and the resultant, combined runlist is
+ * returned. The original @old_rl is deallocated.
+ *
+ * On error, return -errno. @old_rl is left unmodified in that case.
+ *
+ * The following error codes are defined:
+ *     -ENOMEM - Not enough memory to allocate runlist array.
+ *     -EIO    - Corrupt runlist.
+ *     -EINVAL - Invalid parameters were passed in.
+ *     -ERANGE - The two runlists overlap.
+ *
+ * FIXME: For now we take the conceptionally simplest approach of creating the
+ * new runlist disregarding the already existing one and then splicing the
+ * two into one, if that is possible (we check for overlap and discard the new
+ * runlist if overlap present before returning ERR_PTR(-ERANGE)).
+ */
+runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
+               const ATTR_RECORD *attr, runlist_element *old_rl)
+{
+       VCN vcn;                /* Current vcn. */
+       LCN lcn;                /* Current lcn. */
+       s64 deltaxcn;           /* Change in [vl]cn. */
+       runlist_element *rl;    /* The output runlist. */
+       u8 *buf;                /* Current position in mapping pairs array. */
+       u8 *attr_end;           /* End of attribute. */
+       int rlsize;             /* Size of runlist buffer. */
+       u16 rlpos;              /* Current runlist position in units of
+                                  runlist_elements. */
+       u8 b;                   /* Current byte offset in buf. */
+
+#ifdef DEBUG
+       /* Make sure attr exists and is non-resident. */
+       if (!attr || !attr->non_resident || sle64_to_cpu(
+                       attr->data.non_resident.lowest_vcn) < (VCN)0) {
+               ntfs_error(vol->sb, "Invalid arguments.");
+               return ERR_PTR(-EINVAL);
+       }
+#endif
+       /* Start at vcn = lowest_vcn and lcn 0. */
+       vcn = sle64_to_cpu(attr->data.non_resident.lowest_vcn);
+       lcn = 0;
+       /* Get start of the mapping pairs array. */
+       buf = (u8*)attr + le16_to_cpu(
+                       attr->data.non_resident.mapping_pairs_offset);
+       attr_end = (u8*)attr + le32_to_cpu(attr->length);
+       if (unlikely(buf < (u8*)attr || buf > attr_end)) {
+               ntfs_error(vol->sb, "Corrupt attribute.");
+               return ERR_PTR(-EIO);
+       }
+       /* Current position in runlist array. */
+       rlpos = 0;
+       /* Allocate first page and set current runlist size to one page. */
+       rl = ntfs_malloc_nofs(rlsize = PAGE_SIZE);
+       if (unlikely(!rl))
+               return ERR_PTR(-ENOMEM);
+       /* Insert unmapped starting element if necessary. */
+       if (vcn) {
+               rl->vcn = 0;
+               rl->lcn = LCN_RL_NOT_MAPPED;
+               rl->length = vcn;
+               rlpos++;
+       }
+       while (buf < attr_end && *buf) {
+               /*
+                * Allocate more memory if needed, including space for the
+                * not-mapped and terminator elements. ntfs_malloc_nofs()
+                * operates on whole pages only.
+                */
+               if (((rlpos + 3) * sizeof(*old_rl)) > rlsize) {
+                       runlist_element *rl2;
+
+                       rl2 = ntfs_malloc_nofs(rlsize + (int)PAGE_SIZE);
+                       if (unlikely(!rl2)) {
+                               ntfs_free(rl);
+                               return ERR_PTR(-ENOMEM);
+                       }
+                       memcpy(rl2, rl, rlsize);
+                       ntfs_free(rl);
+                       rl = rl2;
+                       rlsize += PAGE_SIZE;
+               }
+               /* Enter the current vcn into the current runlist element. */
+               rl[rlpos].vcn = vcn;
+               /*
+                * Get the change in vcn, i.e. the run length in clusters.
+                * Doing it this way ensures that we signextend negative values.
+                * A negative run length doesn't make any sense, but hey, I
+                * didn't make up the NTFS specs and Windows NT4 treats the run
+                * length as a signed value so that's how it is...
+                */
+               b = *buf & 0xf;
+               if (b) {
+                       if (unlikely(buf + b > attr_end))
+                               goto io_error;
+                       for (deltaxcn = (s8)buf[b--]; b; b--)
+                               deltaxcn = (deltaxcn << 8) + buf[b];
+               } else { /* The length entry is compulsory. */
+                       ntfs_error(vol->sb, "Missing length entry in mapping "
+                                       "pairs array.");
+                       deltaxcn = (s64)-1;
+               }
+               /*
+                * Assume a negative length to indicate data corruption and
+                * hence clean-up and return NULL.
+                */
+               if (unlikely(deltaxcn < 0)) {
+                       ntfs_error(vol->sb, "Invalid length in mapping pairs "
+                                       "array.");
+                       goto err_out;
+               }
+               /*
+                * Enter the current run length into the current runlist
+                * element.
+                */
+               rl[rlpos].length = deltaxcn;
+               /* Increment the current vcn by the current run length. */
+               vcn += deltaxcn;
+               /*
+                * There might be no lcn change at all, as is the case for
+                * sparse clusters on NTFS 3.0+, in which case we set the lcn
+                * to LCN_HOLE.
+                */
+               if (!(*buf & 0xf0))
+                       rl[rlpos].lcn = LCN_HOLE;
+               else {
+                       /* Get the lcn change which really can be negative. */
+                       u8 b2 = *buf & 0xf;
+                       b = b2 + ((*buf >> 4) & 0xf);
+                       if (buf + b > attr_end)
+                               goto io_error;
+                       for (deltaxcn = (s8)buf[b--]; b > b2; b--)
+                               deltaxcn = (deltaxcn << 8) + buf[b];
+                       /* Change the current lcn to its new value. */
+                       lcn += deltaxcn;
+#ifdef DEBUG
+                       /*
+                        * On NTFS 1.2-, apparently can have lcn == -1 to
+                        * indicate a hole. But we haven't verified ourselves
+                        * whether it is really the lcn or the deltaxcn that is
+                        * -1. So if either is found give us a message so we
+                        * can investigate it further!
+                        */
+                       if (vol->major_ver < 3) {
+                               if (unlikely(deltaxcn == (LCN)-1))
+                                       ntfs_error(vol->sb, "lcn delta == -1");
+                               if (unlikely(lcn == (LCN)-1))
+                                       ntfs_error(vol->sb, "lcn == -1");
+                       }
+#endif
+                       /* Check lcn is not below -1. */
+                       if (unlikely(lcn < (LCN)-1)) {
+                               ntfs_error(vol->sb, "Invalid LCN < -1 in "
+                                               "mapping pairs array.");
+                               goto err_out;
+                       }
+                       /* Enter the current lcn into the runlist element. */
+                       rl[rlpos].lcn = lcn;
+               }
+               /* Get to the next runlist element. */
+               rlpos++;
+               /* Increment the buffer position to the next mapping pair. */
+               buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1;
+       }
+       if (unlikely(buf >= attr_end))
+               goto io_error;
+       /*
+        * If there is a highest_vcn specified, it must be equal to the final
+        * vcn in the runlist - 1, or something has gone badly wrong.
+        */
+       deltaxcn = sle64_to_cpu(attr->data.non_resident.highest_vcn);
+       if (unlikely(deltaxcn && vcn - 1 != deltaxcn)) {
+mpa_err:
+               ntfs_error(vol->sb, "Corrupt mapping pairs array in "
+                               "non-resident attribute.");
+               goto err_out;
+       }
+       /* Setup not mapped runlist element if this is the base extent. */
+       if (!attr->data.non_resident.lowest_vcn) {
+               VCN max_cluster;
+
+               max_cluster = (sle64_to_cpu(
+                               attr->data.non_resident.allocated_size) +
+                               vol->cluster_size - 1) >>
+                               vol->cluster_size_bits;
+               /*
+                * If there is a difference between the highest_vcn and the
+                * highest cluster, the runlist is either corrupt or, more
+                * likely, there are more extents following this one.
+                */
+               if (deltaxcn < --max_cluster) {
+                       ntfs_debug("More extents to follow; deltaxcn = 0x%llx, "
+                                       "max_cluster = 0x%llx",
+                                       (unsigned long long)deltaxcn,
+                                       (unsigned long long)max_cluster);
+                       rl[rlpos].vcn = vcn;
+                       vcn += rl[rlpos].length = max_cluster - deltaxcn;
+                       rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
+                       rlpos++;
+               } else if (unlikely(deltaxcn > max_cluster)) {
+                       ntfs_error(vol->sb, "Corrupt attribute. deltaxcn = "
+                                       "0x%llx, max_cluster = 0x%llx",
+                                       (unsigned long long)deltaxcn,
+                                       (unsigned long long)max_cluster);
+                       goto mpa_err;
+               }
+               rl[rlpos].lcn = LCN_ENOENT;
+       } else /* Not the base extent. There may be more extents to follow. */
+               rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
+
+       /* Setup terminating runlist element. */
+       rl[rlpos].vcn = vcn;
+       rl[rlpos].length = (s64)0;
+       /* If no existing runlist was specified, we are done. */
+       if (!old_rl) {
+               ntfs_debug("Mapping pairs array successfully decompressed:");
+               ntfs_debug_dump_runlist(rl);
+               return rl;
+       }
+       /* Now combine the new and old runlists checking for overlaps. */
+       old_rl = ntfs_runlists_merge(old_rl, rl);
+       if (likely(!IS_ERR(old_rl)))
+               return old_rl;
+       ntfs_free(rl);
+       ntfs_error(vol->sb, "Failed to merge runlists.");
+       return old_rl;
+io_error:
+       ntfs_error(vol->sb, "Corrupt attribute.");
+err_out:
+       ntfs_free(rl);
+       return ERR_PTR(-EIO);
+}
+
+/**
+ * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist
+ * @rl:                runlist to use for conversion
+ * @vcn:       vcn to convert
+ *
+ * Convert the virtual cluster number @vcn of an attribute into a logical
+ * cluster number (lcn) of a device using the runlist @rl to map vcns to their
+ * corresponding lcns.
+ *
+ * It is up to the caller to serialize access to the runlist @rl.
+ *
+ * Since lcns must be >= 0, we use negative return values with special meaning:
+ *
+ * Return value                        Meaning / Description
+ * ==================================================
+ *  -1 = LCN_HOLE              Hole / not allocated on disk.
+ *  -2 = LCN_RL_NOT_MAPPED     This is part of the runlist which has not been
+ *                             inserted into the runlist yet.
+ *  -3 = LCN_ENOENT            There is no such vcn in the attribute.
+ *
+ * Locking: - The caller must have locked the runlist (for reading or writing).
+ *         - This function does not touch the lock.
+ */
+LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn)
+{
+       int i;
+
+       BUG_ON(vcn < 0);
+       /*
+        * If rl is NULL, assume that we have found an unmapped runlist. The
+        * caller can then attempt to map it and fail appropriately if
+        * necessary.
+        */
+       if (unlikely(!rl))
+               return LCN_RL_NOT_MAPPED;
+
+       /* Catch out of lower bounds vcn. */
+       if (unlikely(vcn < rl[0].vcn))
+               return LCN_ENOENT;
+
+       for (i = 0; likely(rl[i].length); i++) {
+               if (unlikely(vcn < rl[i+1].vcn)) {
+                       if (likely(rl[i].lcn >= (LCN)0))
+                               return rl[i].lcn + (vcn - rl[i].vcn);
+                       return rl[i].lcn;
+               }
+       }
+       /*
+        * The terminator element is setup to the correct value, i.e. one of
+        * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT.
+        */
+       if (likely(rl[i].lcn < (LCN)0))
+               return rl[i].lcn;
+       /* Just in case... We could replace this with BUG() some day. */
+       return LCN_ENOENT;
+}
+
+/**
+ * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number
+ * @n:         number for which to get the number of bytes for
+ *
+ * Return the number of bytes required to store @n unambiguously as
+ * a signed number.
+ *
+ * This is used in the context of the mapping pairs array to determine how
+ * many bytes will be needed in the array to store a given logical cluster
+ * number (lcn) or a specific run length.
+ *
+ * Return the number of bytes written.  This function cannot fail.
+ */
+static inline int ntfs_get_nr_significant_bytes(const s64 n)
+{
+       s64 l = n;
+       int i;
+       s8 j;
+
+       i = 0;
+       do {
+               l >>= 8;
+               i++;
+       } while (l != 0 && l != -1);
+       j = (n >> 8 * (i - 1)) & 0xff;
+       /* If the sign bit is wrong, we need an extra byte. */
+       if ((n < 0 && j >= 0) || (n > 0 && j < 0))
+               i++;
+       return i;
+}
+
+/**
+ * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array
+ * @vol:       ntfs volume (needed for the ntfs version)
+ * @rl:                locked runlist to determine the size of the mapping pairs of
+ * @start_vcn: vcn at which to start the mapping pairs array
+ *
+ * Walk the locked runlist @rl and calculate the size in bytes of the mapping
+ * pairs array corresponding to the runlist @rl, starting at vcn @start_vcn.
+ * This for example allows us to allocate a buffer of the right size when
+ * building the mapping pairs array.
+ *
+ * If @rl is NULL, just return 1 (for the single terminator byte).
+ *
+ * Return the calculated size in bytes on success.  On error, return -errno.
+ * The following error codes are defined:
+ *     -EINVAL - Run list contains unmapped elements.  Make sure to only pass
+ *               fully mapped runlists to this function.
+ *     -EIO    - The runlist is corrupt.
+ *
+ * Locking: @rl must be locked on entry (either for reading or writing), it
+ *         remains locked throughout, and is left locked upon return.
+ */
+int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
+               const runlist_element *rl, const VCN start_vcn)
+{
+       LCN prev_lcn;
+       int rls;
+
+       BUG_ON(start_vcn < 0);
+       if (!rl) {
+               BUG_ON(start_vcn);
+               return 1;
+       }
+       /* Skip to runlist element containing @start_vcn. */
+       while (rl->length && start_vcn >= rl[1].vcn)
+               rl++;
+       if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn)
+               return -EINVAL;
+       prev_lcn = 0;
+       /* Always need the termining zero byte. */
+       rls = 1;
+       /* Do the first partial run if present. */
+       if (start_vcn > rl->vcn) {
+               s64 delta;
+
+               /* We know rl->length != 0 already. */
+               if (rl->length < 0 || rl->lcn < LCN_HOLE)
+                       goto err_out;
+               delta = start_vcn - rl->vcn;
+               /* Header byte + length. */
+               rls += 1 + ntfs_get_nr_significant_bytes(rl->length - delta);
+               /*
+                * If the logical cluster number (lcn) denotes a hole and we
+                * are on NTFS 3.0+, we don't store it at all, i.e. we need
+                * zero space.  On earlier NTFS versions we just store the lcn.
+                * Note: this assumes that on NTFS 1.2-, holes are stored with
+                * an lcn of -1 and not a delta_lcn of -1 (unless both are -1).
+                */
+               if (rl->lcn >= 0 || vol->major_ver < 3) {
+                       prev_lcn = rl->lcn;
+                       if (rl->lcn >= 0)
+                               prev_lcn += delta;
+                       /* Change in lcn. */
+                       rls += ntfs_get_nr_significant_bytes(prev_lcn);
+               }
+               /* Go to next runlist element. */
+               rl++;
+       }
+       /* Do the full runs. */
+       for (; rl->length; rl++) {
+               if (rl->length < 0 || rl->lcn < LCN_HOLE)
+                       goto err_out;
+               /* Header byte + length. */
+               rls += 1 + ntfs_get_nr_significant_bytes(rl->length);
+               /*
+                * If the logical cluster number (lcn) denotes a hole and we
+                * are on NTFS 3.0+, we don't store it at all, i.e. we need
+                * zero space.  On earlier NTFS versions we just store the lcn.
+                * Note: this assumes that on NTFS 1.2-, holes are stored with
+                * an lcn of -1 and not a delta_lcn of -1 (unless both are -1).
+                */
+               if (rl->lcn >= 0 || vol->major_ver < 3) {
+                       /* Change in lcn. */
+                       rls += ntfs_get_nr_significant_bytes(rl->lcn -
+                                       prev_lcn);
+                       prev_lcn = rl->lcn;
+               }
+       }
+       return rls;
+err_out:
+       if (rl->lcn == LCN_RL_NOT_MAPPED)
+               rls = -EINVAL;
+       else
+               rls = -EIO;
+       return rls;
+}
+
+/**
+ * ntfs_write_significant_bytes - write the significant bytes of a number
+ * @dst:       destination buffer to write to
+ * @dst_max:   pointer to last byte of destination buffer for bounds checking
+ * @n:         number whose significant bytes to write
+ *
+ * Store in @dst, the minimum bytes of the number @n which are required to
+ * identify @n unambiguously as a signed number, taking care not to exceed
+ * @dest_max, the maximum position within @dst to which we are allowed to
+ * write.
+ *
+ * This is used when building the mapping pairs array of a runlist to compress
+ * a given logical cluster number (lcn) or a specific run length to the minumum
+ * size possible.
+ *
+ * Return the number of bytes written on success.  On error, i.e. the
+ * destination buffer @dst is too small, return -ENOSPC.
+ */
+static inline int ntfs_write_significant_bytes(s8 *dst, const s8 *dst_max,
+               const s64 n)
+{
+       s64 l = n;
+       int i;
+       s8 j;
+
+       i = 0;
+       do {
+               if (dst > dst_max)
+                       goto err_out;
+               *dst++ = l & 0xffll;
+               l >>= 8;
+               i++;
+       } while (l != 0 && l != -1);
+       j = (n >> 8 * (i - 1)) & 0xff;
+       /* If the sign bit is wrong, we need an extra byte. */
+       if (n < 0 && j >= 0) {
+               if (dst > dst_max)
+                       goto err_out;
+               i++;
+               *dst = (s8)-1;
+       } else if (n > 0 && j < 0) {
+               if (dst > dst_max)
+                       goto err_out;
+               i++;
+               *dst = (s8)0;
+       }
+       return i;
+err_out:
+       return -ENOSPC;
+}
+
+/**
+ * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist
+ * @vol:       ntfs volume (needed for the ntfs version)
+ * @dst:       destination buffer to which to write the mapping pairs array
+ * @dst_len:   size of destination buffer @dst in bytes
+ * @rl:                locked runlist for which to build the mapping pairs array
+ * @start_vcn: vcn at which to start the mapping pairs array
+ * @stop_vcn:  first vcn outside destination buffer on success or -ENOSPC
+ *
+ * Create the mapping pairs array from the locked runlist @rl, starting at vcn
+ * @start_vcn and save the array in @dst.  @dst_len is the size of @dst in
+ * bytes and it should be at least equal to the value obtained by calling
+ * ntfs_get_size_for_mapping_pairs().
+ *
+ * If @rl is NULL, just write a single terminator byte to @dst.
+ *
+ * On success or -ENOSPC error, if @stop_vcn is not NULL, *@stop_vcn is set to
+ * the first vcn outside the destination buffer.  Note that on error, @dst has
+ * been filled with all the mapping pairs that will fit, thus it can be treated
+ * as partial success, in that a new attribute extent needs to be created or
+ * the next extent has to be used and the mapping pairs build has to be
+ * continued with @start_vcn set to *@stop_vcn.
+ *
+ * Return 0 on success and -errno on error.  The following error codes are
+ * defined:
+ *     -EINVAL - Run list contains unmapped elements.  Make sure to only pass
+ *               fully mapped runlists to this function.
+ *     -EIO    - The runlist is corrupt.
+ *     -ENOSPC - The destination buffer is too small.
+ *
+ * Locking: @rl must be locked on entry (either for reading or writing), it
+ *         remains locked throughout, and is left locked upon return.
+ */
+int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
+               const int dst_len, const runlist_element *rl,
+               const VCN start_vcn, VCN *const stop_vcn)
+{
+       LCN prev_lcn;
+       s8 *dst_max, *dst_next;
+       int err = -ENOSPC;
+       s8 len_len, lcn_len;
+
+       BUG_ON(start_vcn < 0);
+       BUG_ON(dst_len < 1);
+       if (!rl) {
+               BUG_ON(start_vcn);
+               if (stop_vcn)
+                       *stop_vcn = 0;
+               /* Terminator byte. */
+               *dst = 0;
+               return 0;
+       }
+       /* Skip to runlist element containing @start_vcn. */
+       while (rl->length && start_vcn >= rl[1].vcn)
+               rl++;
+       if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn)
+               return -EINVAL;
+       /*
+        * @dst_max is used for bounds checking in
+        * ntfs_write_significant_bytes().
+        */
+       dst_max = dst + dst_len - 1;
+       prev_lcn = 0;
+       /* Do the first partial run if present. */
+       if (start_vcn > rl->vcn) {
+               s64 delta;
+
+               /* We know rl->length != 0 already. */
+               if (rl->length < 0 || rl->lcn < LCN_HOLE)
+                       goto err_out;
+               delta = start_vcn - rl->vcn;
+               /* Write length. */
+               len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
+                               rl->length - delta);
+               if (len_len < 0)
+                       goto size_err;
+               /*
+                * If the logical cluster number (lcn) denotes a hole and we
+                * are on NTFS 3.0+, we don't store it at all, i.e. we need
+                * zero space.  On earlier NTFS versions we just write the lcn
+                * change.  FIXME: Do we need to write the lcn change or just
+                * the lcn in that case?  Not sure as I have never seen this
+                * case on NT4. - We assume that we just need to write the lcn
+                * change until someone tells us otherwise... (AIA)
+                */
+               if (rl->lcn >= 0 || vol->major_ver < 3) {
+                       prev_lcn = rl->lcn;
+                       if (rl->lcn >= 0)
+                               prev_lcn += delta;
+                       /* Write change in lcn. */
+                       lcn_len = ntfs_write_significant_bytes(dst + 1 +
+                                       len_len, dst_max, prev_lcn);
+                       if (lcn_len < 0)
+                               goto size_err;
+               } else
+                       lcn_len = 0;
+               dst_next = dst + len_len + lcn_len + 1;
+               if (dst_next > dst_max)
+                       goto size_err;
+               /* Update header byte. */
+               *dst = lcn_len << 4 | len_len;
+               /* Position at next mapping pairs array element. */
+               dst = dst_next;
+               /* Go to next runlist element. */
+               rl++;
+       }
+       /* Do the full runs. */
+       for (; rl->length; rl++) {
+               if (rl->length < 0 || rl->lcn < LCN_HOLE)
+                       goto err_out;
+               /* Write length. */
+               len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
+                               rl->length);
+               if (len_len < 0)
+                       goto size_err;
+               /*
+                * If the logical cluster number (lcn) denotes a hole and we
+                * are on NTFS 3.0+, we don't store it at all, i.e. we need
+                * zero space.  On earlier NTFS versions we just write the lcn
+                * change.  FIXME: Do we need to write the lcn change or just
+                * the lcn in that case?  Not sure as I have never seen this
+                * case on NT4. - We assume that we just need to write the lcn
+                * change until someone tells us otherwise... (AIA)
+                */
+               if (rl->lcn >= 0 || vol->major_ver < 3) {
+                       /* Write change in lcn. */
+                       lcn_len = ntfs_write_significant_bytes(dst + 1 +
+                                       len_len, dst_max, rl->lcn - prev_lcn);
+                       if (lcn_len < 0)
+                               goto size_err;
+                       prev_lcn = rl->lcn;
+               } else
+                       lcn_len = 0;
+               dst_next = dst + len_len + lcn_len + 1;
+               if (dst_next > dst_max)
+                       goto size_err;
+               /* Update header byte. */
+               *dst = lcn_len << 4 | len_len;
+               /* Position at next mapping pairs array element. */
+               dst = dst_next;
+       }
+       /* Success. */
+       err = 0;
+size_err:
+       /* Set stop vcn. */
+       if (stop_vcn)
+               *stop_vcn = rl->vcn;
+       /* Add terminator byte. */
+       *dst = 0;
+       return err;
+err_out:
+       if (rl->lcn == LCN_RL_NOT_MAPPED)
+               err = -EINVAL;
+       else
+               err = -EIO;
+       return err;
+}
+
+/**
+ * ntfs_rl_truncate_nolock - truncate a runlist starting at a specified vcn
+ * @runlist:   runlist to truncate
+ * @new_length:        the new length of the runlist in VCNs
+ *
+ * Truncate the runlist described by @runlist as well as the memory buffer
+ * holding the runlist elements to a length of @new_length VCNs.
+ *
+ * If @new_length lies within the runlist, the runlist elements with VCNs of
+ * @new_length and above are discarded.
+ *
+ * If @new_length lies beyond the runlist, a sparse runlist element is added to
+ * the end of the runlist @runlist or if the last runlist element is a sparse
+ * one already, this is extended.
+ *
+ * Return 0 on success and -errno on error.
+ *
+ * Locking: The caller must hold @runlist->lock for writing.
+ */
+int ntfs_rl_truncate_nolock(const ntfs_volume *vol, runlist *const runlist,
+               const s64 new_length)
+{
+       runlist_element *rl;
+       int old_size;
+
+       ntfs_debug("Entering for new_length 0x%llx.", (long long)new_length);
+       BUG_ON(!runlist);
+       BUG_ON(new_length < 0);
+       rl = runlist->rl;
+       if (unlikely(!rl)) {
+               /*
+                * Create a runlist consisting of a sparse runlist element of
+                * length @new_length followed by a terminator runlist element.
+                */
+               rl = ntfs_malloc_nofs(PAGE_SIZE);
+               if (unlikely(!rl)) {
+                       ntfs_error(vol->sb, "Not enough memory to allocate "
+                                       "runlist element buffer.");
+                       return -ENOMEM;
+               }
+               runlist->rl = rl;
+               rl[1].length = rl->vcn = 0;
+               rl->lcn = LCN_HOLE;
+               rl[1].vcn = rl->length = new_length;
+               rl[1].lcn = LCN_ENOENT;
+               return 0;
+       }
+       BUG_ON(new_length < rl->vcn);
+       /* Find @new_length in the runlist. */
+       while (likely(rl->length && new_length >= rl[1].vcn))
+               rl++;
+       /*
+        * If not at the end of the runlist we need to shrink it.
+        * If at the end of the runlist we need to expand it.
+        */
+       if (rl->length) {
+               runlist_element *trl;
+               BOOL is_end;
+
+               ntfs_debug("Shrinking runlist.");
+               /* Determine the runlist size. */
+               trl = rl + 1;
+               while (likely(trl->length))
+                       trl++;
+               old_size = trl - runlist->rl + 1;
+               /* Truncate the run. */
+               rl->length = new_length - rl->vcn;
+               /*
+                * If a run was partially truncated, make the following runlist
+                * element a terminator.
+                */
+               is_end = FALSE;
+               if (rl->length) {
+                       rl++;
+                       if (!rl->length)
+                               is_end = TRUE;
+                       rl->vcn = new_length;
+                       rl->length = 0;
+               }
+               rl->lcn = LCN_ENOENT;
+               /* Reallocate memory if necessary. */
+               if (!is_end) {
+                       int new_size = rl - runlist->rl + 1;
+                       rl = ntfs_rl_realloc(runlist->rl, old_size, new_size);
+                       if (IS_ERR(rl))
+                               ntfs_warning(vol->sb, "Failed to shrink "
+                                               "runlist buffer.  This just "
+                                               "wastes a bit of memory "
+                                               "temporarily so we ignore it "
+                                               "and return success.");
+                       else
+                               runlist->rl = rl;
+               }
+       } else if (likely(/* !rl->length && */ new_length > rl->vcn)) {
+               ntfs_debug("Expanding runlist.");
+               /*
+                * If there is a previous runlist element and it is a sparse
+                * one, extend it.  Otherwise need to add a new, sparse runlist
+                * element.
+                */
+               if ((rl > runlist->rl) && ((rl - 1)->lcn == LCN_HOLE))
+                       (rl - 1)->length = new_length - (rl - 1)->vcn;
+               else {
+                       /* Determine the runlist size. */
+                       old_size = rl - runlist->rl + 1;
+                       /* Reallocate memory if necessary. */
+                       rl = ntfs_rl_realloc(runlist->rl, old_size,
+                                       old_size + 1);
+                       if (IS_ERR(rl)) {
+                               ntfs_error(vol->sb, "Failed to expand runlist "
+                                               "buffer, aborting.");
+                               return PTR_ERR(rl);
+                       }
+                       runlist->rl = rl;
+                       /*
+                        * Set @rl to the same runlist element in the new
+                        * runlist as before in the old runlist.
+                        */
+                       rl += old_size - 1;
+                       /* Add a new, sparse runlist element. */
+                       rl->lcn = LCN_HOLE;
+                       rl->length = new_length - rl->vcn;
+                       /* Add a new terminator runlist element. */
+                       rl++;
+                       rl->length = 0;
+               }
+               rl->vcn = new_length;
+               rl->lcn = LCN_ENOENT;
+       } else /* if (unlikely(!rl->length && new_length == rl->vcn)) */ {
+               /* Runlist already has same size as requested. */
+               rl->lcn = LCN_ENOENT;
+       }
+       ntfs_debug("Done.");
+       return 0;
+}
diff --git a/fs/ntfs/runlist.h b/fs/ntfs/runlist.h
new file mode 100644 (file)
index 0000000..7107fde
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * runlist.h - Defines for runlist handling in NTFS Linux kernel driver.
+ *            Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001-2004 Anton Altaparmakov
+ * Copyright (c) 2002 Richard Russon
+ *
+ * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _LINUX_NTFS_RUNLIST_H
+#define _LINUX_NTFS_RUNLIST_H
+
+#include "types.h"
+#include "layout.h"
+#include "volume.h"
+
+/**
+ * runlist_element - in memory vcn to lcn mapping array element
+ * @vcn:       starting vcn of the current array element
+ * @lcn:       starting lcn of the current array element
+ * @length:    length in clusters of the current array element
+ *
+ * The last vcn (in fact the last vcn + 1) is reached when length == 0.
+ *
+ * When lcn == -1 this means that the count vcns starting at vcn are not
+ * physically allocated (i.e. this is a hole / data is sparse).
+ */
+typedef struct {       /* In memory vcn to lcn mapping structure element. */
+       VCN vcn;        /* vcn = Starting virtual cluster number. */
+       LCN lcn;        /* lcn = Starting logical cluster number. */
+       s64 length;     /* Run length in clusters. */
+} runlist_element;
+
+/**
+ * runlist - in memory vcn to lcn mapping array including a read/write lock
+ * @rl:                pointer to an array of runlist elements
+ * @lock:      read/write spinlock for serializing access to @rl
+ *
+ */
+typedef struct {
+       runlist_element *rl;
+       struct rw_semaphore lock;
+} runlist;
+
+static inline void ntfs_init_runlist(runlist *rl)
+{
+       rl->rl = NULL;
+       init_rwsem(&rl->lock);
+}
+
+typedef enum {
+       LCN_HOLE                = -1,   /* Keep this as highest value or die! */
+       LCN_RL_NOT_MAPPED       = -2,
+       LCN_ENOENT              = -3,
+} LCN_SPECIAL_VALUES;
+
+extern runlist_element *ntfs_runlists_merge(runlist_element *drl,
+               runlist_element *srl);
+
+extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
+               const ATTR_RECORD *attr, runlist_element *old_rl);
+
+extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn);
+
+extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
+               const runlist_element *rl, const VCN start_vcn);
+
+extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, s8 *dst,
+               const int dst_len, const runlist_element *rl,
+               const VCN start_vcn, VCN *const stop_vcn);
+
+extern int ntfs_rl_truncate_nolock(const ntfs_volume *vol,
+               runlist *const runlist, const s64 new_length);
+
+#endif /* _LINUX_NTFS_RUNLIST_H */
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
new file mode 100644 (file)
index 0000000..3e55198
--- /dev/null
@@ -0,0 +1,48 @@
+/* internal.h: internal procfs definitions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/proc_fs.h>
+
+struct vmalloc_info {
+       unsigned long   used;
+       unsigned long   largest_chunk;
+};
+
+#ifdef CONFIG_MMU
+#define VMALLOC_TOTAL (VMALLOC_END - VMALLOC_START)
+extern void get_vmalloc_info(struct vmalloc_info *vmi);
+#else
+
+#define VMALLOC_TOTAL 0UL
+#define get_vmalloc_info(vmi)                  \
+do {                                           \
+       (vmi)->used = 0;                        \
+       (vmi)->largest_chunk = 0;               \
+} while(0)
+
+#endif
+
+extern void create_seq_entry(char *name, mode_t mode, struct file_operations *f);
+extern int proc_exe_link(struct inode *, struct dentry **, struct vfsmount **);
+extern int proc_tid_stat(struct task_struct *,  char *);
+extern int proc_tgid_stat(struct task_struct *, char *);
+extern int proc_pid_status(struct task_struct *, char *);
+extern int proc_pid_statm(struct task_struct *, char *);
+
+static inline struct task_struct *proc_task(struct inode *inode)
+{
+       return PROC_I(inode)->task;
+}
+
+static inline int proc_type(struct inode *inode)
+{
+       return PROC_I(inode)->type;
+}
diff --git a/fs/proc/mmu.c b/fs/proc/mmu.c
new file mode 100644 (file)
index 0000000..a704103
--- /dev/null
@@ -0,0 +1,67 @@
+/* mmu.c: mmu memory info files
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mman.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/mmzone.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/seq_file.h>
+#include <linux/hugetlb.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/tlb.h>
+#include <asm/div64.h>
+#include "internal.h"
+
+void get_vmalloc_info(struct vmalloc_info *vmi)
+{
+       struct vm_struct *vma;
+       unsigned long free_area_size;
+       unsigned long prev_end;
+
+       vmi->used = 0;
+
+       if (!vmlist) {
+               vmi->largest_chunk = VMALLOC_TOTAL;
+       }
+       else {
+               vmi->largest_chunk = 0;
+
+               prev_end = VMALLOC_START;
+
+               read_lock(&vmlist_lock);
+
+               for (vma = vmlist; vma; vma = vma->next) {
+                       vmi->used += vma->size;
+
+                       free_area_size = (unsigned long) vma->addr - prev_end;
+                       if (vmi->largest_chunk < free_area_size)
+                               vmi->largest_chunk = free_area_size;
+
+                       prev_end = vma->size + (unsigned long) vma->addr;
+               }
+
+               if (VMALLOC_END - prev_end > vmi->largest_chunk)
+                       vmi->largest_chunk = VMALLOC_END - prev_end;
+
+               read_unlock(&vmlist_lock);
+       }
+}
diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c
new file mode 100644 (file)
index 0000000..f3bf016
--- /dev/null
@@ -0,0 +1,135 @@
+/* nommu.c: mmu-less memory info files
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mman.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/mmzone.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/seq_file.h>
+#include <linux/hugetlb.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/tlb.h>
+#include <asm/div64.h>
+#include "internal.h"
+
+/*
+ * display a list of all the VMAs the kernel knows about
+ * - nommu kernals have a single flat list
+ */
+static int nommu_vma_list_show(struct seq_file *m, void *v)
+{
+       struct vm_area_struct *vma;
+       unsigned long ino = 0;
+       struct file *file;
+       dev_t dev = 0;
+       int flags, len;
+
+       vma = rb_entry((struct rb_node *) v, struct vm_area_struct, vm_rb);
+
+       flags = vma->vm_flags;
+       file = vma->vm_file;
+
+       if (file) {
+               struct inode *inode = vma->vm_file->f_dentry->d_inode;
+               dev = inode->i_sb->s_dev;
+               ino = inode->i_ino;
+       }
+
+       seq_printf(m,
+                  "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n",
+                  vma->vm_start,
+                  vma->vm_end,
+                  flags & VM_READ ? 'r' : '-',
+                  flags & VM_WRITE ? 'w' : '-',
+                  flags & VM_EXEC ? 'x' : '-',
+                  flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p',
+                  vma->vm_pgoff << PAGE_SHIFT,
+                  MAJOR(dev), MINOR(dev), ino, &len);
+
+       if (file) {
+               len = 25 + sizeof(void *) * 6 - len;
+               if (len < 1)
+                       len = 1;
+               seq_printf(m, "%*c", len, ' ');
+               seq_path(m, file->f_vfsmnt, file->f_dentry, "");
+       }
+
+       seq_putc(m, '\n');
+       return 0;
+}
+
+static void *nommu_vma_list_start(struct seq_file *m, loff_t *_pos)
+{
+       struct rb_node *_rb;
+       loff_t pos = *_pos;
+       void *next = NULL;
+
+       down_read(&nommu_vma_sem);
+
+       for (_rb = rb_first(&nommu_vma_tree); _rb; _rb = rb_next(_rb)) {
+               if (pos == 0) {
+                       next = _rb;
+                       break;
+               }
+       }
+
+       return next;
+}
+
+static void nommu_vma_list_stop(struct seq_file *m, void *v)
+{
+       up_read(&nommu_vma_sem);
+}
+
+static void *nommu_vma_list_next(struct seq_file *m, void *v, loff_t *pos)
+{
+       (*pos)++;
+       return rb_next((struct rb_node *) v);
+}
+
+static struct seq_operations proc_nommu_vma_list_seqop = {
+       .start  = nommu_vma_list_start,
+       .next   = nommu_vma_list_next,
+       .stop   = nommu_vma_list_stop,
+       .show   = nommu_vma_list_show
+};
+
+static int proc_nommu_vma_list_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &proc_nommu_vma_list_seqop);
+}
+
+static struct file_operations proc_nommu_vma_list_operations = {
+       .open    = proc_nommu_vma_list_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = seq_release,
+};
+
+static int __init proc_nommu_init(void)
+{
+       create_seq_entry("maps", S_IRUGO, &proc_nommu_vma_list_operations);
+       return 0;
+}
+
+module_init(proc_nommu_init);
diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
new file mode 100644 (file)
index 0000000..0c057dc
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ *     fs/proc/vmcore.c Interface for accessing the crash
+ *                              dump from the system's previous life.
+ *     Heavily borrowed from fs/proc/kcore.c
+ *     Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
+ *     Copyright (C) IBM Corporation, 2004. All rights reserved
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/elf.h>
+#include <linux/elfcore.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/highmem.h>
+#include <linux/bootmem.h>
+#include <linux/init.h>
+#include <linux/crash_dump.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+/* This is to re-use the kcore header creation code */
+static struct kcore_list vmcore_mem;
+
+static int open_vmcore(struct inode * inode, struct file * filp)
+{
+       return 0;
+}
+
+static ssize_t read_vmcore(struct file *,char __user *,size_t, loff_t *);
+
+#define BACKUP_START CRASH_BACKUP_BASE
+#define BACKUP_END CRASH_BACKUP_BASE + CRASH_BACKUP_SIZE
+#define REG_SIZE sizeof(elf_gregset_t)
+
+struct file_operations proc_vmcore_operations = {
+       .read           = read_vmcore,
+       .open           = open_vmcore,
+};
+
+struct proc_dir_entry *proc_vmcore;
+
+struct memelfnote
+{
+       const char *name;
+       int type;
+       unsigned int datasz;
+       void *data;
+};
+
+static size_t get_vmcore_size(int *nphdr, size_t *elf_buflen)
+{
+       size_t size;
+
+       /* We need 1 PT_LOAD segment headers
+        * In addition, we need one PT_NOTE header
+        */
+       *nphdr = 2;
+       size = (size_t)(saved_max_pfn << PAGE_SHIFT);
+
+       *elf_buflen =   sizeof(struct elfhdr) +
+                       (*nphdr + 2)*sizeof(struct elf_phdr) +
+                       3 * sizeof(struct memelfnote) +
+                       sizeof(struct elf_prstatus) +
+                       sizeof(struct elf_prpsinfo) +
+                       sizeof(struct task_struct);
+       *elf_buflen = PAGE_ALIGN(*elf_buflen);
+       return size + *elf_buflen;
+}
+
+/*
+ * Reads a page from the oldmem device from given offset.
+ */
+static ssize_t read_from_oldmem(char *buf, size_t count,
+                            loff_t *ppos, int userbuf)
+{
+       unsigned long pfn;
+       size_t read = 0;
+
+       pfn = (unsigned long)(*ppos / PAGE_SIZE);
+
+       if (pfn > saved_max_pfn) {
+               read = -EINVAL;
+               goto done;
+       }
+
+       count = (count > PAGE_SIZE) ? PAGE_SIZE : count;
+
+       if (copy_oldmem_page(pfn, buf, count, userbuf)) {
+               read = -EFAULT;
+               goto done;
+       }
+
+       *ppos += count;
+done:
+       return read;
+}
+
+/*
+ * store an ELF crash dump header in the supplied buffer
+ * nphdr is the number of elf_phdr to insert
+ */
+static void elf_vmcore_store_hdr(char *bufp, int nphdr, int dataoff)
+{
+       struct elf_prstatus prstatus;   /* NT_PRSTATUS */
+       struct memelfnote notes[1];
+       char reg_buf[REG_SIZE];
+       loff_t reg_ppos;
+       char *buf = bufp;
+
+       vmcore_mem.addr = (unsigned long)__va(0);
+       vmcore_mem.size = saved_max_pfn << PAGE_SHIFT;
+       vmcore_mem.next = NULL;
+
+       /* Re-use the kcore code */
+       elf_kcore_store_hdr(bufp, nphdr, dataoff, &vmcore_mem);
+       buf += sizeof(struct elfhdr) + 2*sizeof(struct elf_phdr);
+
+       /* set up the process status */
+       notes[0].name = "CORE";
+       notes[0].type = NT_PRSTATUS;
+       notes[0].datasz = sizeof(struct elf_prstatus);
+       notes[0].data = &prstatus;
+
+       memset(&prstatus, 0, sizeof(struct elf_prstatus));
+
+       /* 1 - Get the registers from the reserved memory area */
+       reg_ppos = BACKUP_END + CRASH_RELOCATE_SIZE;
+       read_from_oldmem(reg_buf, REG_SIZE, &reg_ppos, 0);
+       elf_core_copy_regs(&prstatus.pr_reg, (struct pt_regs *)reg_buf);
+       buf = storenote(&notes[0], buf);
+}
+
+/*
+ * read from the ELF header and then the crash dump
+ */
+static ssize_t read_vmcore(
+struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
+{
+       ssize_t acc = 0;
+       size_t size, tsz;
+       size_t elf_buflen;
+       int nphdr;
+       unsigned long start;
+
+       tsz =  get_vmcore_size(&nphdr, &elf_buflen);
+       proc_vmcore->size = size = tsz + elf_buflen;
+       if (buflen == 0 || *fpos >= size) {
+               goto done;
+       }
+
+       /* trim buflen to not go beyond EOF */
+       if (buflen > size - *fpos)
+               buflen = size - *fpos;
+
+       /* construct an ELF core header if we'll need some of it */
+       if (*fpos < elf_buflen) {
+               char * elf_buf;
+
+               tsz = elf_buflen - *fpos;
+               if (buflen < tsz)
+                       tsz = buflen;
+               elf_buf = kmalloc(elf_buflen, GFP_ATOMIC);
+               if (!elf_buf) {
+                       acc = -ENOMEM;
+                       goto done;
+               }
+               memset(elf_buf, 0, elf_buflen);
+               elf_vmcore_store_hdr(elf_buf, nphdr, elf_buflen);
+               if (copy_to_user(buffer, elf_buf + *fpos, tsz)) {
+                       kfree(elf_buf);
+                       acc = -EFAULT;
+                       goto done;
+               }
+               kfree(elf_buf);
+               buflen -= tsz;
+               *fpos += tsz;
+               buffer += tsz;
+               acc += tsz;
+
+               /* leave now if filled buffer already */
+               if (buflen == 0) {
+                       goto done;
+               }
+       }
+
+       start = *fpos - elf_buflen;
+       if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
+               tsz = buflen;
+
+       while (buflen) {
+               unsigned long p;
+               loff_t pdup;
+
+               if ((start < 0) || (start >= size))
+                       if (clear_user(buffer, tsz)) {
+                               acc = -EFAULT;
+                               goto done;
+                       }
+
+               /* tsz contains actual len of dump to be read.
+                * buflen is the total len that was requested.
+                * This may contain part of ELF header. start
+                * is the fpos for the oldmem region
+                * If the file position corresponds to the second
+                * kernel's memory, we just return zeroes
+                */
+               p = start;
+               if ((p >= BACKUP_START) && (p < BACKUP_END)) {
+                       if (clear_user(buffer, tsz)) {
+                               acc = -EFAULT;
+                               goto done;
+                       }
+
+                       goto read_done;
+               } else if (p < CRASH_RELOCATE_SIZE)
+                       p += BACKUP_END;
+
+               pdup = p;
+               if (read_from_oldmem(buffer, tsz, &pdup, 1)) {
+                       acc = -EINVAL;
+                       goto done;
+               }
+
+read_done:
+               buflen -= tsz;
+               *fpos += tsz;
+               buffer += tsz;
+               acc += tsz;
+               start += tsz;
+               tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen);
+       }
+
+done:
+       return acc;
+}
diff --git a/fs/xfs/Kconfig b/fs/xfs/Kconfig
new file mode 100644 (file)
index 0000000..c92306f
--- /dev/null
@@ -0,0 +1,85 @@
+menu "XFS support"
+
+config XFS_FS
+       tristate "XFS filesystem support"
+       select EXPORTFS if NFSD!=n
+       help
+         XFS is a high performance journaling filesystem which originated
+         on the SGI IRIX platform.  It is completely multi-threaded, can
+         support large files and large filesystems, extended attributes,
+         variable block sizes, is extent based, and makes extensive use of
+         Btrees (directories, extents, free space) to aid both performance
+         and scalability.
+
+         Refer to the documentation at <http://oss.sgi.com/projects/xfs/>
+         for complete details.  This implementation is on-disk compatible
+         with the IRIX version of XFS.
+
+         To compile this file system support as a module, choose M here: the
+         module will be called xfs.  Be aware, however, that if the file
+         system of your root partition is compiled as a module, you'll need
+         to use an initial ramdisk (initrd) to boot.
+
+config XFS_EXPORT
+       bool
+       default y if XFS_FS && EXPORTFS
+
+config XFS_RT
+       bool "Realtime support (EXPERIMENTAL)"
+       depends on XFS_FS && EXPERIMENTAL
+       help
+         If you say Y here you will be able to mount and use XFS filesystems
+         which contain a realtime subvolume. The realtime subvolume is a
+         separate area of disk space where only file data is stored. The
+         realtime subvolume is designed to provide very deterministic
+         data rates suitable for media streaming applications.
+
+         See the xfs man page in section 5 for a bit more information.
+
+         This feature is unsupported at this time, is not yet fully
+         functional, and may cause serious problems.
+
+         If unsure, say N.
+
+config XFS_QUOTA
+       bool "Quota support"
+       depends on XFS_FS
+       help
+         If you say Y here, you will be able to set limits for disk usage on
+         a per user and/or a per group basis under XFS.  XFS considers quota
+         information as filesystem metadata and uses journaling to provide a
+         higher level guarantee of consistency.  The on-disk data format for
+         quota is also compatible with the IRIX version of XFS, allowing a
+         filesystem to be migrated between Linux and IRIX without any need
+         for conversion.
+
+         If unsure, say N.  More comprehensive documentation can be found in
+         README.quota in the xfsprogs package.  XFS quota can be used either
+         with or without the generic quota support enabled (CONFIG_QUOTA) -
+         they are completely independent subsystems.
+
+config XFS_SECURITY
+       bool "Security Label support"
+       depends on XFS_FS
+       help
+         Security labels support alternative access control models
+         implemented by security modules like SELinux.  This option
+         enables an extended attribute namespace for inode security
+         labels in the XFS filesystem.
+
+         If you are not using a security module that requires using
+         extended attributes for inode security labels, say N.
+
+config XFS_POSIX_ACL
+       bool "POSIX ACL support"
+       depends on XFS_FS
+       help
+         POSIX Access Control Lists (ACLs) support permissions for users and
+         groups beyond the owner/group/world scheme.
+
+         To learn more about Access Control Lists, visit the POSIX ACLs for
+         Linux website <http://acl.bestbits.at/>.
+
+         If you don't know what Access Control Lists are, say N.
+
+endmenu
diff --git a/fs/xfs/linux-2.6/xfs_export.c b/fs/xfs/linux-2.6/xfs_export.c
new file mode 100644 (file)
index 0000000..772d216
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2004-2005 Silicon Graphics, Inc.  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.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * 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: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+
+STATIC struct dentry *
+linvfs_decode_fh(
+       struct super_block      *sb,
+       __u32                   *fh,
+       int                     fh_len,
+       int                     fileid_type,
+       int (*acceptable)(
+               void            *context,
+               struct dentry   *de),
+       void                    *context)
+{
+       __u32 parent[2];
+       parent[0] = parent[1] = 0;
+       
+       if (fh_len < 2 || fileid_type > 2)
+               return NULL;
+       
+       if (fileid_type == 2 && fh_len > 2) {
+               if (fh_len == 3) {
+                       printk(KERN_WARNING
+                              "XFS: detected filehandle without "
+                              "parent inode generation information.");
+                       return ERR_PTR(-ESTALE);
+               }
+                       
+               parent[0] = fh[2];
+               parent[1] = fh[3];
+       }
+       
+       return find_exported_dentry(sb, fh, parent, acceptable, context);
+
+}
+
+STATIC struct dentry *
+linvfs_get_dentry(
+       struct super_block      *sb,
+       void                    *data)
+{
+       vnode_t                 *vp;
+       struct inode            *inode;
+       struct dentry           *result;
+       xfs_fid2_t              xfid;
+       vfs_t                   *vfsp = LINVFS_GET_VFS(sb);
+       int                     error;
+
+       xfid.fid_len = sizeof(xfs_fid2_t) - sizeof(xfid.fid_len);
+       xfid.fid_pad = 0;
+       xfid.fid_gen = ((__u32 *)data)[1];
+       xfid.fid_ino = ((__u32 *)data)[0];
+
+       VFS_VGET(vfsp, &vp, (fid_t *)&xfid, error);
+       if (error || vp == NULL)
+               return ERR_PTR(-ESTALE) ;
+
+       inode = LINVFS_GET_IP(vp);
+       result = d_alloc_anon(inode);
+        if (!result) {
+               iput(inode);
+               return ERR_PTR(-ENOMEM);
+       }
+       return result;
+}
+
+STATIC struct dentry *
+linvfs_get_parent(
+       struct dentry           *child)
+{
+       int                     error;
+       vnode_t                 *vp, *cvp;
+       struct dentry           *parent;
+       struct dentry           dotdot;
+
+       dotdot.d_name.name = "..";
+       dotdot.d_name.len = 2;
+       dotdot.d_inode = NULL;
+
+       cvp = NULL;
+       vp = LINVFS_GET_VP(child->d_inode);
+       VOP_LOOKUP(vp, &dotdot, &cvp, 0, NULL, NULL, error);
+       if (unlikely(error))
+               return ERR_PTR(-error);
+
+       parent = d_alloc_anon(LINVFS_GET_IP(cvp));
+       if (unlikely(!parent)) {
+               VN_RELE(cvp);
+               return ERR_PTR(-ENOMEM);
+       }
+       return parent;
+}
+
+struct export_operations linvfs_export_ops = {
+       .decode_fh              = linvfs_decode_fh,
+       .get_parent             = linvfs_get_parent,
+       .get_dentry             = linvfs_get_dentry,
+};
diff --git a/include/acpi/container.h b/include/acpi/container.h
new file mode 100644 (file)
index 0000000..d716df0
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __ACPI_CONTAINER_H
+#define __ACPI_CONTAINER_H
+
+#include <linux/kernel.h>
+
+struct acpi_container {
+       acpi_handle handle;
+       unsigned long sun;
+       int state;
+};
+
+#endif /* __ACPI_CONTAINER_H */
+
diff --git a/include/asm-alpha/cputime.h b/include/asm-alpha/cputime.h
new file mode 100644 (file)
index 0000000..19577fd
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __ALPHA_CPUTIME_H
+#define __ALPHA_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __ALPHA_CPUTIME_H */
diff --git a/include/asm-arm/arch-cl7500/debug-macro.S b/include/asm-arm/arch-cl7500/debug-macro.S
new file mode 100644 (file)
index 0000000..a5d489d
--- /dev/null
@@ -0,0 +1,31 @@
+/* linux/include/asm-arm/arch-cl7500/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .macro  addruart,rx
+               mov     \rx, #0xe0000000
+               orr     \rx, \rx, #0x00010000
+               orr     \rx, \rx, #0x00000be0
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx]
+               .endm
+
+               .macro  busyuart,rd,rx
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldrb    \rd, [\rx, #0x14]
+               tst     \rd, #0x20
+               beq     1001b
+               .endm
diff --git a/include/asm-arm/arch-cl7500/entry-macro.S b/include/asm-arm/arch-cl7500/entry-macro.S
new file mode 100644 (file)
index 0000000..686f413
--- /dev/null
@@ -0,0 +1,3 @@
+
+#include <asm/hardware/entry-macro-iomd.S>
+
diff --git a/include/asm-arm/arch-clps711x/debug-macro.S b/include/asm-arm/arch-clps711x/debug-macro.S
new file mode 100644 (file)
index 0000000..bc0a576
--- /dev/null
@@ -0,0 +1,46 @@
+/* linux/include/asm-arm/arch-clps711x/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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 <asm/hardware/clps7111.h>
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx, #CLPS7111_PHYS_BASE
+               movne   \rx, #CLPS7111_VIRT_BASE
+#ifndef CONFIG_DEBUG_CLPS711X_UART2
+               add     \rx, \rx, #0x0000       @ UART1
+#else
+               add     \rx, \rx, #0x1000       @ UART2
+#endif
+               .endm
+
+               .macro  senduart,rd,rx
+               str     \rd, [\rx, #0x0480]     @ UARTDR
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldr     \rd, [\rx, #0x0140]     @ SYSFLGx
+               tst     \rd, #1 << 11           @ UBUSYx
+               bne     1001b
+               .endm
+
+               .macro  busyuart,rd,rx
+               tst     \rx, #0x1000            @ UART2 does not have CTS here
+               bne     1002f
+1001:          ldr     \rd, [\rx, #0x0140]     @ SYSFLGx
+               tst     \rd, #1 << 8            @ CTS
+               bne     1001b
+1002:
+               .endm
+
diff --git a/include/asm-arm/arch-clps711x/entry-macro.S b/include/asm-arm/arch-clps711x/entry-macro.S
new file mode 100644 (file)
index 0000000..b31079a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * include/asm-arm/arch-CLPS711x/entry-macro.S
+ *
+ * Low-level IRQ helper macros for CLPS711X-based platforms
+ *
+ * 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 <asm/hardware/clps7111.h>
+
+               .macro  disable_fiq
+               .endm
+
+#if (INTSR2 - INTSR1) != (INTMR2 - INTMR1)
+#error INTSR stride != INTMR stride
+#endif
+
+               .macro  get_irqnr_and_base, irqnr, stat, base, mask
+               mov     \base, #CLPS7111_BASE
+               ldr     \stat, [\base, #INTSR1]
+               ldr     \mask, [\base, #INTMR1]
+               mov     \irqnr, #4
+               mov     \mask, \mask, lsl #16
+               and     \stat, \stat, \mask, lsr #16
+               movs    \stat, \stat, lsr #4
+               bne     1001f
+
+               add     \base, \base, #INTSR2 - INTSR1
+               ldr     \stat, [\base, #INTSR1]
+               ldr     \mask, [\base, #INTMR1]
+               mov     \irqnr, #16
+               mov     \mask, \mask, lsl #16
+               and     \stat, \stat, \mask, lsr #16
+
+1001:          tst     \stat, #255
+               addeq   \irqnr, \irqnr, #8
+               moveq   \stat, \stat, lsr #8
+               tst     \stat, #15
+               addeq   \irqnr, \irqnr, #4
+               moveq   \stat, \stat, lsr #4
+               tst     \stat, #3
+               addeq   \irqnr, \irqnr, #2
+               moveq   \stat, \stat, lsr #2
+               tst     \stat, #1
+               addeq   \irqnr, \irqnr, #1
+               moveq   \stat, \stat, lsr #1
+               tst     \stat, #1                       @ bit 0 should be set
+               .endm
+
+
diff --git a/include/asm-arm/arch-ebsa110/debug-macro.S b/include/asm-arm/arch-ebsa110/debug-macro.S
new file mode 100644 (file)
index 0000000..dcd03a4
--- /dev/null
@@ -0,0 +1,34 @@
+/* linux/include/asm-arm/arch-ebsa110/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+**/
+
+               .macro  addruart,rx
+               mov     \rx, #0xf0000000
+               orr     \rx, \rx, #0x00000be0
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx]
+               .endm
+
+               .macro  busyuart,rd,rx
+1002:          ldrb    \rd, [\rx, #0x14]
+               and     \rd, \rd, #0x60
+               teq     \rd, #0x60
+               bne     1002b
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldrb    \rd, [\rx, #0x18]
+               tst     \rd, #0x10
+               beq     1001b
+               .endm
diff --git a/include/asm-arm/arch-ebsa110/entry-macro.S b/include/asm-arm/arch-ebsa110/entry-macro.S
new file mode 100644 (file)
index 0000000..b12ca04
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * include/asm-arm/arch-ebsa110/entry-macro.S
+ *
+ * Low-level IRQ helper macros for ebsa110 platform.
+ *
+ * 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.
+ */
+
+
+
+#define IRQ_STAT               0xff000000      /* read */
+
+       .macro  disable_fiq
+       .endm
+
+       .macro  get_irqnr_and_base, irqnr, stat, base, tmp
+       mov     \base, #IRQ_STAT
+       ldrb    \stat, [\base]                  @ get interrupts
+       mov     \irqnr, #0
+       tst     \stat, #15
+       addeq   \irqnr, \irqnr, #4
+       moveq   \stat, \stat, lsr #4
+       tst     \stat, #3
+       addeq   \irqnr, \irqnr, #2
+       moveq   \stat, \stat, lsr #2
+       tst     \stat, #1
+       addeq   \irqnr, \irqnr, #1
+       moveq   \stat, \stat, lsr #1
+       tst     \stat, #1                       @ bit 0 should be set
+       .endm
+
diff --git a/include/asm-arm/arch-ebsa285/debug-macro.S b/include/asm-arm/arch-ebsa285/debug-macro.S
new file mode 100644 (file)
index 0000000..237853d
--- /dev/null
@@ -0,0 +1,66 @@
+/* linux/include/asm-arm/arch-ebsa285/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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 <asm/hardware/dec21285.h>
+
+#ifndef CONFIG_DEBUG_DC21285_PORT
+       /* For NetWinder debugging */
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx, #0x7c000000        @ physical
+               movne   \rx, #0xff000000        @ virtual
+               orr     \rx, \rx, #0x000003f8
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx]
+               .endm
+
+               .macro  busyuart,rd,rx
+1002:          ldrb    \rd, [\rx, #0x5]
+               and     \rd, \rd, #0x60
+               teq     \rd, #0x60
+               bne     1002b
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldrb    \rd, [\rx, #0x6]
+               tst     \rd, #0x10
+               beq     1001b
+               .endm
+#else
+       /* For EBSA285 debugging */
+               .equ    dc21285_high, ARMCSR_BASE & 0xff000000
+               .equ    dc21285_low,  ARMCSR_BASE & 0x00ffffff
+
+               .macro  addruart,rx
+               mov     \rx, #dc21285_high
+               .if     dc21285_low
+               orr     \rx, \rx, #dc21285_low
+               .endif
+               .endm
+
+               .macro  senduart,rd,rx
+               str     \rd, [\rx, #0x160]      @ UARTDR
+               .endm
+
+               .macro  busyuart,rd,rx
+1001:          ldr     \rd, [\rx, #0x178]      @ UARTFLG
+               tst     \rd, #1 << 3
+               bne     1001b
+               .endm
+
+               .macro  waituart,rd,rx
+               .endm
+#endif
diff --git a/include/asm-arm/arch-ebsa285/entry-macro.S b/include/asm-arm/arch-ebsa285/entry-macro.S
new file mode 100644 (file)
index 0000000..db5729f
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * include/asm-arm/arch-footbridge/entry-macro.S
+ *
+ * Low-level IRQ helper macros for footbridge-based platforms
+ *
+ * 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 <asm/hardware/dec21285.h>
+
+               .macro  disable_fiq
+               .endm
+
+               .equ    dc21285_high, ARMCSR_BASE & 0xff000000
+               .equ    dc21285_low, ARMCSR_BASE & 0x00ffffff
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               mov     r4, #dc21285_high
+               .if     dc21285_low
+               orr     r4, r4, #dc21285_low
+               .endif
+               ldr     \irqstat, [r4, #0x180]          @ get interrupts
+
+               mov     \irqnr, #IRQ_SDRAMPARITY
+               tst     \irqstat, #IRQ_MASK_SDRAMPARITY
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_UART_RX
+               movne   \irqnr, #IRQ_CONRX
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_DMA1
+               movne   \irqnr, #IRQ_DMA1
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_DMA2
+               movne   \irqnr, #IRQ_DMA2
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_IN0
+               movne   \irqnr, #IRQ_IN0
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_IN1
+               movne   \irqnr, #IRQ_IN1
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_IN2
+               movne   \irqnr, #IRQ_IN2
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_IN3
+               movne   \irqnr, #IRQ_IN3
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_PCI
+               movne   \irqnr, #IRQ_PCI
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_DOORBELLHOST
+               movne   \irqnr, #IRQ_DOORBELLHOST
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_I2OINPOST
+               movne   \irqnr, #IRQ_I2OINPOST
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_TIMER1
+               movne   \irqnr, #IRQ_TIMER1
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_TIMER2
+               movne   \irqnr, #IRQ_TIMER2
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_TIMER3
+               movne   \irqnr, #IRQ_TIMER3
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_UART_TX
+               movne   \irqnr, #IRQ_CONTX
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_PCI_ABORT
+               movne   \irqnr, #IRQ_PCI_ABORT
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_PCI_SERR
+               movne   \irqnr, #IRQ_PCI_SERR
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_DISCARD_TIMER
+               movne   \irqnr, #IRQ_DISCARD_TIMER
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_PCI_DPERR
+               movne   \irqnr, #IRQ_PCI_DPERR
+               bne     1001f
+
+               tst     \irqstat, #IRQ_MASK_PCI_PERR
+               movne   \irqnr, #IRQ_PCI_PERR
+1001:
+               .endm
+
diff --git a/include/asm-arm/arch-epxa10db/debug-macro.S b/include/asm-arm/arch-epxa10db/debug-macro.S
new file mode 100644 (file)
index 0000000..1d11c51
--- /dev/null
@@ -0,0 +1,41 @@
+/* linux/include/asm-arm/arch-epxa10db/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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 <asm/arch/excalibur.h>
+#define UART00_TYPE
+#include <asm/arch/uart00.h>
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               ldr     \rx, =EXC_UART00_BASE   @ physical base address
+               orrne   \rx, \rx, #0xff000000   @ virtual base
+               orrne   \rx, \rx, #0x00f00000
+               .endm
+
+               .macro  senduart,rd,rx
+               str     \rd, [\rx, #UART_TD(0)]
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldr     \rd, [\rx, #UART_TSR(0)]
+               and     \rd, \rd,  #UART_TSR_TX_LEVEL_MSK
+               cmp     \rd, #15
+               beq     1001b
+               .endm
+
+               .macro  busyuart,rd,rx
+1001:          ldr     \rd, [\rx, #UART_TSR(0)]
+               ands    \rd, \rd,  #UART_TSR_TX_LEVEL_MSK
+               bne     1001b
+               .endm
diff --git a/include/asm-arm/arch-epxa10db/entry-macro.S b/include/asm-arm/arch-epxa10db/entry-macro.S
new file mode 100644 (file)
index 0000000..de6ae08
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * include/asm-arm/arch-epxa10db/entry-macro.S
+ *
+ * Low-level IRQ helper macros for epxa10db platform
+ *
+ * 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 <asm/arch/platform.h>
+#undef IRQ_MODE /* same name defined in asm/proc/ptrace.h */
+#include <asm/arch/int_ctrl00.h>
+
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+
+               ldr     \irqstat, =INT_ID(IO_ADDRESS(EXC_INT_CTRL00_BASE))
+               ldr     \irqnr,[\irqstat]
+               cmp     \irqnr,#0
+               subne   \irqnr,\irqnr,#1
+
+               .endm
+
diff --git a/include/asm-arm/arch-h720x/debug-macro.S b/include/asm-arm/arch-h720x/debug-macro.S
new file mode 100644 (file)
index 0000000..82822d3
--- /dev/null
@@ -0,0 +1,40 @@
+/* linux/include/asm-arm/arch-h720x/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .equ    io_virt, IO_BASE
+               .equ    io_phys, IO_START
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                @ MMU enabled?
+               moveq   \rx, #io_phys          @ physical base address
+               movne   \rx, #io_virt          @ virtual address
+               add     \rx, \rx, #0x00020000   @ UART1
+               .endm
+
+               .macro  senduart,rd,rx
+               str     \rd, [\rx, #0x0]        @ UARTDR
+
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldr     \rd, [\rx, #0x18]       @ UARTFLG
+               tst     \rd, #1 << 5           @ UARTFLGUTXFF - 1 when full
+               bne     1001b
+               .endm
+
+               .macro  busyuart,rd,rx
+1001:          ldr     \rd, [\rx, #0x18]       @ UARTFLG
+               tst     \rd, #1 << 3           @ UARTFLGUBUSY - 1 when busy
+               bne     1001b
+               .endm
diff --git a/include/asm-arm/arch-h720x/entry-macro.S b/include/asm-arm/arch-h720x/entry-macro.S
new file mode 100644 (file)
index 0000000..8f16564
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * include/asm-arm/arch-h720x/entry-macro.S
+ *
+ * Low-level IRQ helper macros for Hynix HMS720x based platforms
+ *
+ * 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.
+ */
+
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+#if defined (CONFIG_CPU_H7201) || defined (CONFIG_CPU_H7202)
+               @ we could use the id register on H7202, but this is not
+               @ properly updated when we come back from asm_do_irq
+               @ without a previous return from interrupt
+               @ (see loops below in irq_svc, irq_usr)
+               @ We see unmasked pending ints only, as the masked pending ints
+               @ are not visible here
+
+               mov     \base, #0xf0000000             @ base register
+               orr     \base, \base, #0x24000         @ irqbase
+               ldr     \irqstat, [\base, #0x04]        @ get interrupt status
+#if defined (CONFIG_CPU_H7201)
+               ldr     \tmp, =0x001fffff
+#else
+               mvn     \tmp, #0xc0000000
+#endif
+               and     \irqstat, \irqstat, \tmp        @ mask out unused ints
+               mov     \irqnr, #0
+
+               mov     \tmp, #0xff00
+               orr     \tmp, \tmp, #0xff
+               tst     \irqstat, \tmp
+               addeq   \irqnr, \irqnr, #16
+               moveq   \irqstat, \irqstat, lsr #16
+               tst     \irqstat, #255
+               addeq   \irqnr, \irqnr, #8
+               moveq   \irqstat, \irqstat, lsr #8
+               tst     \irqstat, #15
+               addeq   \irqnr, \irqnr, #4
+               moveq   \irqstat, \irqstat, lsr #4
+               tst     \irqstat, #3
+               addeq   \irqnr, \irqnr, #2
+               moveq   \irqstat, \irqstat, lsr #2
+               tst     \irqstat, #1
+               addeq   \irqnr, \irqnr, #1
+               moveq   \irqstat, \irqstat, lsr #1
+               tst     \irqstat, #1                   @ bit 0 should be set
+               .endm
+
+               .macro  irq_prio_table
+               .endm
+
+#else
+#error hynix processor selection missmatch
+#endif
+
diff --git a/include/asm-arm/arch-imx/debug-macro.S b/include/asm-arm/arch-imx/debug-macro.S
new file mode 100644 (file)
index 0000000..83f552f
--- /dev/null
@@ -0,0 +1,34 @@
+/* linux/include/asm-arm/arch-imx/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx, #0x00000000        @ physical
+               movne   \rx, #0xe0000000        @ virtual
+               orr     \rx, \rx, #0x00200000
+               orr     \rx, \rx, #0x00006000   @ UART1 offset
+               .endm
+
+               .macro  senduart,rd,rx
+               str     \rd, [\rx, #0x40]       @ TXDATA
+               .endm
+
+               .macro  waituart,rd,rx
+               .endm
+
+               .macro  busyuart,rd,rx
+1002:          ldr     \rd, [\rx, #0x98]       @ SR2
+               tst     \rd, #1 << 3            @ TXDC
+               beq     1002b                   @ wait until transmit done
+               .endm
diff --git a/include/asm-arm/arch-imx/entry-macro.S b/include/asm-arm/arch-imx/entry-macro.S
new file mode 100644 (file)
index 0000000..b40ea7c
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * include/asm-arm/arch-imx/entry-macro.S
+ *
+ * Low-level IRQ helper macros for iMX-based platforms
+ *
+ * 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.
+ */
+               .macro  disable_fiq
+               .endm
+#define AITC_NIVECSR   0x40
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               ldr     \irqstat, =IO_ADDRESS(IMX_AITC_BASE)
+               @ Load offset & priority of the highest priority
+               @ interrupt pending.
+               ldr     \irqnr, [\irqstat, #AITC_NIVECSR]
+               @ Shift off the priority leaving the offset or
+               @ "interrupt number"
+               mov     \irqnr, \irqnr, lsr #16
+               ldr     \irqstat, =1    @ dummy compare
+               ldr     \base, =0xFFFF          // invalid interrupt
+               cmp     \irqnr, \base
+               bne     1001f
+               ldr     \irqstat, =0
+1001:
+               tst     \irqstat, #1    @ to make the condition code = TRUE
+               .endm
+
diff --git a/include/asm-arm/arch-integrator/debug-macro.S b/include/asm-arm/arch-integrator/debug-macro.S
new file mode 100644 (file)
index 0000000..484a1aa
--- /dev/null
@@ -0,0 +1,38 @@
+/* linux/include/asm-arm/arch-integrator/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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 <asm/hardware/amba_serial.h>
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx, #0x16000000        @ physical base address
+               movne   \rx, #0xf0000000        @ virtual base
+               addne   \rx, \rx, #0x16000000 >> 4
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx, #UART01x_DR]
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldr     \rd, [\rx, #0x18]       @ UARTFLG
+               tst     \rd, #1 << 5            @ UARTFLGUTXFF - 1 when full
+               bne     1001b
+               .endm
+
+               .macro  busyuart,rd,rx
+1001:          ldr     \rd, [\rx, #0x18]       @ UARTFLG
+               tst     \rd, #1 << 3            @ UARTFLGUBUSY - 1 when busy
+               bne     1001b
+               .endm
diff --git a/include/asm-arm/arch-integrator/entry-macro.S b/include/asm-arm/arch-integrator/entry-macro.S
new file mode 100644 (file)
index 0000000..44f7ee6
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * include/asm-arm/arch-integrator/entry-macro.S
+ *
+ * Low-level IRQ helper macros for Integrator platforms
+ *
+ * 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.
+ */
+
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+/* FIXME: should not be using soo many LDRs here */
+               ldr     \base, =IO_ADDRESS(INTEGRATOR_IC_BASE)
+               mov     \irqnr, #IRQ_PIC_START
+               ldr     \irqstat, [\base, #IRQ_STATUS]          @ get masked status
+               ldr     \base, =IO_ADDRESS(INTEGRATOR_HDR_BASE)
+               teq     \irqstat, #0
+               ldreq   \irqstat, [\base, #(INTEGRATOR_HDR_IC_OFFSET+IRQ_STATUS)]
+               moveq   \irqnr, #IRQ_CIC_START
+
+1001:          tst     \irqstat, #15
+               bne     1002f
+               add     \irqnr, \irqnr, #4
+               movs    \irqstat, \irqstat, lsr #4
+               bne     1001b
+1002:          tst     \irqstat, #1
+               bne     1003f
+               add     \irqnr, \irqnr, #1
+               movs    \irqstat, \irqstat, lsr #1
+               bne     1002b
+1003:          /* EQ will be set if no irqs pending */
+               .endm
+
diff --git a/include/asm-arm/arch-iop3xx/debug-macro.S b/include/asm-arm/arch-iop3xx/debug-macro.S
new file mode 100644 (file)
index 0000000..cc15f80
--- /dev/null
@@ -0,0 +1,48 @@
+/* linux/include/asm-arm/arch-iop3xx/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .macro  addruart,rx
+               mov     \rx, #0xfe000000        @ physical
+#if defined(CONFIG_ARCH_IQ80321) || defined(CONFIG_ARCH_IQ31244)
+               orr     \rx, \rx, #0x00800000   @ location of the UART
+#elif defined(CONFIG_ARCH_IOP331)
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx, #0x000fe000        @ Physical Base
+               movne   \rx, #0
+               orr     \rx, \rx, #0xfe000000
+               orr     \rx, \rx, #0x00f00000   @ Virtual Base
+               orr     \rx, \rx, #0x00001700   @ location of the UART
+#else
+#error Unknown IOP3XX implementation
+#endif
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx]
+               .endm
+
+               .macro  busyuart,rd,rx
+1002:          ldrb    \rd, [\rx, #0x5]
+               and     \rd, \rd, #0x60
+               teq     \rd, #0x60
+               bne     1002b
+               .endm
+
+               .macro  waituart,rd,rx
+#if !defined(CONFIG_ARCH_IQ80321) || !defined(CONFIG_ARCH_IQ31244) || !defined(CONFIG_ARCH_IQ80331)
+1001:          ldrb    \rd, [\rx, #0x6]
+               tst     \rd, #0x10
+               beq     1001b
+#endif
+               .endm
diff --git a/include/asm-arm/arch-iop3xx/entry-macro.S b/include/asm-arm/arch-iop3xx/entry-macro.S
new file mode 100644 (file)
index 0000000..e2ce7f5
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * include/asm-arm/arch-iop3xx/entry-macro.S
+ *
+ * Low-level IRQ helper macros for IOP3xx-based platforms
+ *
+ * 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.
+ */
+
+#if defined(CONFIG_ARCH_IOP321)
+               .macro  disable_fiq
+               .endm
+
+               /*
+                * Note: only deal with normal interrupts, not FIQ
+                */
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               mov     \irqnr, #0
+               mrc     p6, 0, \irqstat, c8, c0, 0      @ Read IINTSRC
+               cmp     \irqstat, #0
+               beq     1001f
+               clz     \irqnr, \irqstat
+               mov     \base, #31
+               subs    \irqnr,\base,\irqnr
+               add     \irqnr,\irqnr,#IRQ_IOP321_DMA0_EOT
+1001:
+               .endm
+
+#elif defined(CONFIG_ARCH_IOP331)
+               .macro  disable_fiq
+               .endm
+
+               /*
+                * Note: only deal with normal interrupts, not FIQ
+                */
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               mov     \irqnr, #0
+               mrc     p6, 0, \irqstat, c4, c0, 0      @ Read IINTSRC0
+               cmp     \irqstat, #0
+               bne     1002f
+               mrc     p6, 0, \irqstat, c5, c0, 0      @ Read IINTSRC1
+               cmp     \irqstat, #0
+               beq     1001f
+               clz     \irqnr, \irqstat
+               rsbs    \irqnr,\irqnr,#31   @ recommend by RMK
+               add     \irqnr,\irqnr,#IRQ_IOP331_XINT8
+               b       1001f
+1002:  clz     \irqnr, \irqstat
+               rsbs    \irqnr,\irqnr,#31   @ recommend by RMK
+               add     \irqnr,\irqnr,#IRQ_IOP331_DMA0_EOT
+1001:
+               .endm
+
+#endif
+
diff --git a/include/asm-arm/arch-iop3xx/iq80332.h b/include/asm-arm/arch-iop3xx/iq80332.h
new file mode 100644 (file)
index 0000000..e5fff17
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * linux/include/asm/arch-iop3xx/iq80332.h
+ *
+ * Intel IQ80332 evaluation board registers
+ */
+
+#ifndef _IQ80332_H_
+#define _IQ80332_H_
+
+#define        IQ80332_FLASHBASE       0xc0000000      /* Flash */
+#define        IQ80332_FLASHSIZE       0x00800000
+#define        IQ80332_FLASHWIDTH      1
+
+#define IQ80332_7SEG_1         0xce840000      /* 7-Segment MSB */
+#define IQ80332_7SEG_0         0xce850000      /* 7-Segment LSB (WO) */
+#define IQ80332_ROTARY_SW      0xce8d0000      /* Rotary Switch */
+#define IQ80332_BATT_STAT      0xce8f0000      /* Battery Status */
+
+#ifndef __ASSEMBLY__
+extern void iq80332_map_io(void);
+#endif
+
+#endif // _IQ80332_H_
diff --git a/include/asm-arm/arch-ixp2000/debug-macro.S b/include/asm-arm/arch-ixp2000/debug-macro.S
new file mode 100644 (file)
index 0000000..5631e08
--- /dev/null
@@ -0,0 +1,40 @@
+/* linux/include/asm-arm/arch-ixp2000/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx, #0xc0000000        @ Physical base
+               movne   \rx, #0xfe000000        @ virtual base
+               orrne   \rx, \rx, #0x00f00000
+               orr     \rx, \rx, #0x00030000
+#ifdef __ARMEB__
+               orr     \rx, \rx, #0x00000003
+#endif
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx]
+               .endm
+
+               .macro  busyuart,rd,rx
+1002:          ldrb    \rd, [\rx, #0x14]
+               tst     \rd, #0x20
+               beq     1002b
+               .endm
+
+               .macro  waituart,rd,rx
+               nop
+               nop
+               nop
+               .endm
diff --git a/include/asm-arm/arch-ixp2000/entry-macro.S b/include/asm-arm/arch-ixp2000/entry-macro.S
new file mode 100644 (file)
index 0000000..44db57c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * include/asm-arm/arch-ixp2000/entry-macro.S
+ *
+ * Low-level IRQ helper macros for IXP2000-based platforms
+ *
+ * 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.
+ */
+
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+
+               mov     \irqnr, #0x0              @clear out irqnr as default
+                mov    \base, #0xfe000000
+               orr     \base, \base, #0x00ff0000
+               orr     \base, \base, #0x0000a000
+               orr     \base, \base, #0x08
+               ldr     \irqstat, [\base]         @ get interrupts
+
+               cmp     \irqstat, #0
+               beq     1001f
+
+               clz     \irqnr, \irqstat
+               mov     \base, #31
+               subs    \irqnr, \base, \irqnr
+
+               /*
+                * We handle PCIA and PCIB here so we don't have an
+                * extra layer of code just to check these two bits.
+                */
+               cmp     \irqnr, #IRQ_IXP2000_PCI
+               bne     1001f
+
+               mov     \base, #0xfe000000
+               orr     \base, \base, #0x00fd0000
+               orr     \base, \base, #0x0000e100
+               orr     \base, \base, #0x00000058
+               ldr     \irqstat, [\base]
+
+               mov     \tmp, #(1<<26)
+               tst     \irqstat, \tmp
+               movne   \irqnr, #IRQ_IXP2000_PCIA
+               bne     1001f
+
+               mov     \tmp, #(1<<27)
+               tst     \irqstat, \tmp
+               movne   \irqnr, #IRQ_IXP2000_PCIB
+
+1001:
+               .endm
+
diff --git a/include/asm-arm/arch-ixp4xx/debug-macro.S b/include/asm-arm/arch-ixp4xx/debug-macro.S
new file mode 100644 (file)
index 0000000..4499ae8
--- /dev/null
@@ -0,0 +1,34 @@
+/* linux/include/asm-arm/arch-ixp4xx/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+*/
+
+                .macro  addruart,rx
+                mrc     p15, 0, \rx, c1, c0
+                tst     \rx, #1                 @ MMU enabled?
+                moveq   \rx, #0xc8000000
+                movne   \rx, #0xff000000
+                add     \rx,\rx,#3              @ Uart regs are at off set of 3 if
+                                               @ byte writes used - Big Endian.
+                .endm
+
+               .macro  senduart,rd,rx
+                strb    \rd, [\rx]
+                .endm
+
+                .macro  waituart,rd,rx
+1002:           ldrb    \rd, [\rx, #0x14]
+                and     \rd, \rd, #0x60                @ check THRE and TEMT bits
+                teq     \rd, #0x60
+                bne     1002b
+                .endm
+
+                .macro  busyuart,rd,rx
+                .endm
diff --git a/include/asm-arm/arch-ixp4xx/entry-macro.S b/include/asm-arm/arch-ixp4xx/entry-macro.S
new file mode 100644 (file)
index 0000000..455da64
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * include/asm-arm/arch-ixp4xx/entry-macro.S
+ *
+ * Low-level IRQ helper macros for IXP4xx-based platforms
+ *
+ * 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.
+ */
+
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               ldr     \irqstat, =(IXP4XX_INTC_BASE_VIRT+IXP4XX_ICIP_OFFSET)
+               ldr     \irqstat, [\irqstat]            @ get interrupts
+               cmp     \irqstat, #0
+               beq     1001f
+               clz     \irqnr, \irqstat
+               mov     \base, #31
+               subs    \irqnr, \base, \irqnr
+
+1001:
+               /*
+                * IXP465 has an upper IRQ status register
+                */
+#if defined(CONFIG_CPU_IXP46X)
+               bne     1002f
+               ldr     \irqstat, =(IXP4XX_INTC_BASE_VIRT+IXP4XX_ICIP2_OFFSET)
+               ldr     \irqstat, [\irqstat]            @ get upper interrupts
+               mov     \irqnr, #63
+               clz     \irqstat, \irqstat
+               cmp     \irqstat, #32
+               subne   \irqnr, \irqnr, \irqstat
+1002:
+#endif
+               .endm
+
+
diff --git a/include/asm-arm/arch-ixp4xx/gtwx5715.h b/include/asm-arm/arch-ixp4xx/gtwx5715.h
new file mode 100644 (file)
index 0000000..fc460af
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * include/asm-arm/arch-ixp4xx/gtwx5715.h
+ *
+ * Gemtek GTWX5715 Gateway (Linksys WRV54G)
+ *
+ * Copyright 2004 (c) George T. Joseph
+ *
+ * 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 __ASM_ARCH_HARDWARE_H__
+#error "Do not include this directly, instead #include <asm/hardware.h>"
+#endif
+#include "irqs.h"
+
+#define GTWX5715_GPIO0 0
+#define GTWX5715_GPIO1 1
+#define GTWX5715_GPIO2 2
+#define GTWX5715_GPIO3 3
+#define GTWX5715_GPIO4 4
+#define GTWX5715_GPIO5 5
+#define GTWX5715_GPIO6 6
+#define GTWX5715_GPIO7 7
+#define GTWX5715_GPIO8 8
+#define GTWX5715_GPIO9 9
+#define GTWX5715_GPIO10        10
+#define GTWX5715_GPIO11        11
+#define GTWX5715_GPIO12        12
+#define GTWX5715_GPIO13        13
+#define GTWX5715_GPIO14        14
+
+#define GTWX5715_GPIO0_IRQ                     IRQ_IXP4XX_GPIO0
+#define GTWX5715_GPIO1_IRQ                     IRQ_IXP4XX_GPIO1
+#define GTWX5715_GPIO2_IRQ                     IRQ_IXP4XX_GPIO2
+#define GTWX5715_GPIO3_IRQ                     IRQ_IXP4XX_GPIO3
+#define GTWX5715_GPIO4_IRQ                     IRQ_IXP4XX_GPIO4
+#define GTWX5715_GPIO5_IRQ                     IRQ_IXP4XX_GPIO5
+#define GTWX5715_GPIO6_IRQ                     IRQ_IXP4XX_GPIO6
+#define GTWX5715_GPIO7_IRQ                     IRQ_IXP4XX_GPIO7
+#define GTWX5715_GPIO8_IRQ                     IRQ_IXP4XX_GPIO8
+#define GTWX5715_GPIO9_IRQ                     IRQ_IXP4XX_GPIO9
+#define GTWX5715_GPIO10_IRQ            IRQ_IXP4XX_GPIO10
+#define GTWX5715_GPIO11_IRQ            IRQ_IXP4XX_GPIO11
+#define GTWX5715_GPIO12_IRQ            IRQ_IXP4XX_GPIO12
+#define GTWX5715_GPIO13_IRQ            IRQ_IXP4XX_SW_INT1
+#define GTWX5715_GPIO14_IRQ            IRQ_IXP4XX_SW_INT2
+
+
+#define        GTWX5715_FLASH_BASE     IXP4XX_EXP_BUS_CS0_BASE_PHYS
+#define        GTWX5715_FLASH_SIZE     (0x00800000)
+
+/* PCI controller GPIO to IRQ pin mappings
+
+                       INTA    INTB
+SLOT 0 10              11
+SLOT 1 11              10
+
+*/
+
+#define        GTWX5715_PCI_SLOT0_DEVID        0
+#define        GTWX5715_PCI_SLOT0_INTA_GPIO    GTWX5715_GPIO10
+#define        GTWX5715_PCI_SLOT0_INTB_GPIO    GTWX5715_GPIO11
+#define        GTWX5715_PCI_SLOT0_INTA_IRQ     GTWX5715_GPIO10_IRQ
+#define        GTWX5715_PCI_SLOT0_INTB_IRQ     GTWX5715_GPIO11_IRQ
+
+#define        GTWX5715_PCI_SLOT1_DEVID        1
+#define        GTWX5715_PCI_SLOT1_INTA_GPIO    GTWX5715_GPIO11
+#define        GTWX5715_PCI_SLOT1_INTB_GPIO    GTWX5715_GPIO10
+#define        GTWX5715_PCI_SLOT1_INTA_IRQ     GTWX5715_GPIO11_IRQ
+#define        GTWX5715_PCI_SLOT1_INTB_IRQ     GTWX5715_GPIO10_IRQ
+
+#define GTWX5715_PCI_SLOT_COUNT                        2
+#define GTWX5715_PCI_INT_PIN_COUNT             2
+
+/*
+ * GPIO 5,6,7 and12 are hard wired to the Kendin KS8995M Switch
+ * and operate as an SPI type interface.  The details of the interface
+ * are available on Kendin/Micrel's web site.
+ */
+
+#define GTWX5715_KSSPI_SELECT  GTWX5715_GPIO5
+#define GTWX5715_KSSPI_TXD             GTWX5715_GPIO6
+#define GTWX5715_KSSPI_CLOCK   GTWX5715_GPIO7
+#define GTWX5715_KSSPI_RXD             GTWX5715_GPIO12
+
+/*
+ * The "reset" button is wired to GPIO 3.
+ * The GPIO is brought "low" when the button is pushed.
+ */
+
+#define GTWX5715_BUTTON_GPIO   GTWX5715_GPIO3
+#define GTWX5715_BUTTON_IRQ    GTWX5715_GPIO3_IRQ
+
+/*
+ *  Board Label      Front Label
+ *  LED1             Power
+ *  LED2             Wireless-G
+ *  LED3             not populated but could be
+ *  LED4             Internet
+ *  LED5 - LED8      Controlled by KS8995M Switch
+ *  LED9             DMZ
+ */
+
+#define GTWX5715_LED1_GPIO             GTWX5715_GPIO2
+#define GTWX5715_LED2_GPIO             GTWX5715_GPIO9
+#define GTWX5715_LED3_GPIO             GTWX5715_GPIO8
+#define GTWX5715_LED4_GPIO             GTWX5715_GPIO1
+#define GTWX5715_LED9_GPIO             GTWX5715_GPIO4
diff --git a/include/asm-arm/arch-l7200/debug-macro.S b/include/asm-arm/arch-l7200/debug-macro.S
new file mode 100644 (file)
index 0000000..8464733
--- /dev/null
@@ -0,0 +1,40 @@
+/* linux/include/asm-arm/arch-l7200/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .equ    io_virt, IO_BASE
+               .equ    io_phys, IO_START
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx, #io_phys           @ physical base address
+               movne   \rx, #io_virt           @ virtual address
+               add     \rx, \rx, #0x00044000   @ UART1
+@              add     \rx, \rx, #0x00045000   @ UART2
+               .endm
+
+               .macro  senduart,rd,rx
+               str     \rd, [\rx, #0x0]        @ UARTDR
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldr     \rd, [\rx, #0x18]       @ UARTFLG
+               tst     \rd, #1 << 5            @ UARTFLGUTXFF - 1 when full
+               bne     1001b
+               .endm
+
+               .macro  busyuart,rd,rx
+1001:          ldr     \rd, [\rx, #0x18]       @ UARTFLG
+               tst     \rd, #1 << 3            @ UARTFLGUBUSY - 1 when busy
+               bne     1001b
+               .endm
diff --git a/include/asm-arm/arch-l7200/entry-macro.S b/include/asm-arm/arch-l7200/entry-macro.S
new file mode 100644 (file)
index 0000000..8b6342d
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * include/asm-arm/arch-l7200/entry-macro.S
+ *
+ * Low-level IRQ helper macros for L7200-based platforms
+ *
+ * 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 <asm/hardware.h>
+
+               .equ    irq_base_addr,  IO_BASE_2
+
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               mov     \irqstat, #irq_base_addr                @ Virt addr IRQ regs
+               add     \irqstat, \irqstat, #0x00001000         @ Status reg
+               ldr     \irqstat, [\irqstat, #0]                @ get interrupts
+               mov     \irqnr, #0
+1001:          tst     \irqstat, #1
+               addeq   \irqnr, \irqnr, #1
+               moveq   \irqstat, \irqstat, lsr #1
+               tsteq   \irqnr, #32
+               beq     1001b
+               teq     \irqnr, #32
+               .endm
+
diff --git a/include/asm-arm/arch-lh7a40x/debug-macro.S b/include/asm-arm/arch-lh7a40x/debug-macro.S
new file mode 100644 (file)
index 0000000..421dcd6
--- /dev/null
@@ -0,0 +1,39 @@
+/* linux/include/asm-arm/arch-lh7a40x/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+       @ It is not known if this will be appropriate for every 40x
+       @ board.
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               mov     \rx, #0x00000700        @ offset from base
+               orreq   \rx, \rx, #0x80000000   @ physical base
+               orrne   \rx, \rx, #0xf8000000   @ virtual base
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx]              @ DATA
+               .endm
+
+               .macro  busyuart,rd,rx          @ spin while busy
+1001:          ldr     \rd, [\rx, #0x10]       @ STATUS
+               tst     \rd, #1 << 3            @ BUSY (TX FIFO not empty)
+               bne     1001b                   @ yes, spin
+               .endm
+
+               .macro  waituart,rd,rx          @ wait for Tx FIFO room
+1001:          ldrb    \rd, [\rx, #0x10]       @ STATUS
+               tst     \rd, #1 << 5            @ TXFF (TX FIFO full)
+               bne     1001b                   @ yes, spin
+               .endm
diff --git a/include/asm-arm/arch-lh7a40x/entry-macro.S b/include/asm-arm/arch-lh7a40x/entry-macro.S
new file mode 100644 (file)
index 0000000..865f396
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * include/asm-arm/arch-lh7a40x/entry-macro.S
+ *
+ * Low-level IRQ helper macros for LH7A40x platforms
+ *
+ * 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.
+ */
+
+# if defined (CONFIG_ARCH_LH7A400) && defined (CONFIG_ARCH_LH7A404)
+#  error "LH7A400 and LH7A404 are mutually exclusive"
+# endif
+
+# if defined (CONFIG_ARCH_LH7A400)
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               mov     \irqnr, #0
+               mov     \base, #io_p2v(0x80000000)      @ APB registers
+               ldr     \irqstat, [\base, #0x500]       @ PIC INTSR
+
+1001:          movs    \irqstat, \irqstat, lsr #1      @ Shift into carry
+               bcs     1008f                           @ Bit set; irq found
+               add     \irqnr, \irqnr, #1
+               bne     1001b                           @ Until no bits
+               b       1009f                           @ Nothing?  Hmm.
+1008:          movs    \irqstat, #1                    @ Force !Z
+1009:
+               .endm
+
+#elif defined(CONFIG_ARCH_LH7A404)
+
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               mov     \irqnr, #0                      @ VIC1 irq base
+               mov     \base, #io_p2v(0x80000000)      @ APB registers
+               add     \base, \base, #0x8000
+               ldr     \tmp, [\base, #0x0030]          @ VIC1_VECTADDR
+               tst     \tmp, #VA_VECTORED              @ Direct vectored
+               bne     1002f
+               tst     \tmp, #VA_VIC1DEFAULT           @ Default vectored VIC1
+               ldrne   \irqstat, [\base, #0]           @ VIC1_IRQSTATUS
+               bne     1001f
+               add     \base, \base, #(0xa000 - 0x8000)
+               ldr     \tmp, [\base, #0x0030]          @ VIC2_VECTADDR
+               tst     \tmp, #VA_VECTORED              @ Direct vectored
+               bne     1002f
+               ldr     \irqstat, [\base, #0]           @ VIC2_IRQSTATUS
+               mov     \irqnr, #32                     @ VIC2 irq base
+
+1001:          movs    \irqstat, \irqstat, lsr #1      @ Shift into carry
+               bcs     1008f                           @ Bit set; irq found
+               add     \irqnr, \irqnr, #1
+               bne     1001b                           @ Until no bits
+               b       1009f                           @ Nothing?  Hmm.
+1002:          and     \irqnr, \tmp, #0x3f             @ Mask for valid bits
+1008:          movs    \irqstat, #1                    @ Force !Z
+               str     \tmp, [\base, #0x0030]          @ Clear vector
+1009:
+               .endm
+#endif
+
+
diff --git a/include/asm-arm/arch-omap/cpu.h b/include/asm-arm/arch-omap/cpu.h
new file mode 100644 (file)
index 0000000..e878671
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * linux/include/asm-arm/arch-omap/cpu.h
+ *
+ * OMAP cpu type detection
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ *
+ * Written by Tony Lindgren <tony.lindgren@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __ASM_ARCH_OMAP_CPU_H
+#define __ASM_ARCH_OMAP_CPU_H
+
+extern unsigned int system_rev;
+
+#define OMAP_DIE_ID_0          0xfffe1800
+#define OMAP_DIE_ID_1          0xfffe1804
+#define OMAP_PRODUCTION_ID_0   0xfffe2000
+#define OMAP_PRODUCTION_ID_1   0xfffe2004
+#define OMAP32_ID_0            0xfffed400
+#define OMAP32_ID_1            0xfffed404
+
+/*
+ * Test if multicore OMAP support is needed
+ */
+#undef MULTI_OMAP
+#undef OMAP_NAME
+
+#ifdef CONFIG_ARCH_OMAP730
+# ifdef OMAP_NAME
+#  undef  MULTI_OMAP
+#  define MULTI_OMAP
+# else
+#  define OMAP_NAME omap730
+# endif
+#endif
+#ifdef CONFIG_ARCH_OMAP1510
+# ifdef OMAP_NAME
+#  undef  MULTI_OMAP
+#  define MULTI_OMAP
+# else
+#  define OMAP_NAME omap1510
+# endif
+#endif
+#ifdef CONFIG_ARCH_OMAP16XX
+# ifdef OMAP_NAME
+#  undef  MULTI_OMAP
+#  define MULTI_OMAP
+# else
+#  define OMAP_NAME omap1610
+# endif
+#endif
+#ifdef CONFIG_ARCH_OMAP16XX
+# ifdef OMAP_NAME
+#  undef  MULTI_OMAP
+#  define MULTI_OMAP
+# else
+#  define OMAP_NAME omap1710
+# endif
+#endif
+
+/*
+ * Generate various OMAP cpu specific macros, and cpu class
+ * specific macros
+ */
+#define GET_OMAP_TYPE  ((system_rev >> 24) & 0xff)
+#define GET_OMAP_CLASS (system_rev & 0xff)
+
+#define IS_OMAP_TYPE(type, id)                         \
+static inline int is_omap ##type (void)                        \
+{                                                      \
+       return (GET_OMAP_TYPE == (id)) ? 1 : 0;         \
+}
+
+#define IS_OMAP_CLASS(class, id)                       \
+static inline int is_omap ##class (void)               \
+{                                                      \
+       return (GET_OMAP_CLASS == (id)) ? 1 : 0;        \
+}
+
+IS_OMAP_TYPE(730, 0x07)
+IS_OMAP_TYPE(1510, 0x15)
+IS_OMAP_TYPE(1610, 0x16)
+IS_OMAP_TYPE(5912, 0x16)
+IS_OMAP_TYPE(1710, 0x17)
+IS_OMAP_TYPE(2420, 0x24)
+
+IS_OMAP_CLASS(7xx, 0x07)
+IS_OMAP_CLASS(15xx, 0x15)
+IS_OMAP_CLASS(16xx, 0x16)
+IS_OMAP_CLASS(24xx, 0x24)
+
+/*
+ * Macros to group OMAP types into cpu classes.
+ * These can be used in most places.
+ * cpu_is_omap15xx():  True for 1510 and 5910
+ * cpu_is_omap16xx():  True for 1610, 5912 and 1710
+ */
+#if defined(MULTI_OMAP)
+# define cpu_is_omap7xx()              is_omap7xx()
+# define cpu_is_omap15xx()             is_omap15xx()
+# if !(defined(CONFIG_ARCH_OMAP1510) || defined(CONFIG_ARCH_OMAP730))
+#  define cpu_is_omap16xx()            1
+# else
+#  define cpu_is_omap16xx()            is_omap16xx()
+# endif
+#else
+# if defined(CONFIG_ARCH_OMAP730)
+#  define cpu_is_omap7xx()             1
+# else
+#  define cpu_is_omap7xx()             0
+# endif
+# if defined(CONFIG_ARCH_OMAP1510)
+#  define cpu_is_omap15xx()            1
+# else
+#  define cpu_is_omap15xx()            0
+# endif
+# if defined(CONFIG_ARCH_OMAP16XX)
+#  define cpu_is_omap16xx()            1
+# else
+#  define cpu_is_omap16xx()            0
+# endif
+#endif
+
+#if defined(MULTI_OMAP)
+# define cpu_is_omap730()              is_omap730()
+# define cpu_is_omap1510()             is_omap1510()
+# define cpu_is_omap1610()             is_omap1610()
+# define cpu_is_omap5912()             is_omap5912()
+# define cpu_is_omap1710()             is_omap1710()
+#else
+# if defined(CONFIG_ARCH_OMAP730)
+#  define cpu_is_omap730()             1
+# else
+#  define cpu_is_omap730()             0
+# endif
+# if defined(CONFIG_ARCH_OMAP1510)
+#  define cpu_is_omap1510()            1
+# else
+#  define cpu_is_omap1510()            0
+# endif
+# if defined(CONFIG_ARCH_OMAP16XX)
+#  define cpu_is_omap1610()            1
+# else
+#  define cpu_is_omap1610()            0
+# endif
+# if defined(CONFIG_ARCH_OMAP16XX)
+#  define cpu_is_omap5912()            1
+# else
+#  define cpu_is_omap5912()            0
+# endif
+# if defined(CONFIG_ARCH_OMAP16XX)
+# define cpu_is_omap1610()             is_omap1610()
+# define cpu_is_omap5912()             is_omap5912()
+# define cpu_is_omap1710()             is_omap1710()
+# else
+# define cpu_is_omap1610()             0
+# define cpu_is_omap5912()             0
+# define cpu_is_omap1710()             0
+# endif
+# if defined(CONFIG_ARCH_OMAP2420)
+#  define cpu_is_omap2420()            1
+# else
+#  define cpu_is_omap2420()            0
+# endif
+#endif
+
+#endif
diff --git a/include/asm-arm/arch-omap/debug-macro.S b/include/asm-arm/arch-omap/debug-macro.S
new file mode 100644 (file)
index 0000000..83bb458
--- /dev/null
@@ -0,0 +1,45 @@
+/* linux/include/asm-arm/arch-omap/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx, #0xff000000        @ physical base address
+               movne   \rx, #0xfe000000        @ virtual base
+               orr     \rx, \rx, #0x00fb0000
+#ifdef CONFIG_OMAP_LL_DEBUG_UART3
+               orr     \rx, \rx, #0x00009000   @ UART 3
+#endif
+#if defined(CONFIG_OMAP_LL_DEBUG_UART2) || defined(CONFIG_OMAP_LL_DEBUG_UART3)
+               orr     \rx, \rx, #0x00000800   @ UART 2 & 3
+#endif
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx]
+               .endm
+
+               .macro  busyuart,rd,rx
+1001:          ldrb    \rd, [\rx, #(0x5 << 2)] @ OMAP-1510 and friends
+               and     \rd, \rd, #0x60
+               teq     \rd, #0x60
+               beq     1002f
+               ldrb    \rd, [\rx, #(0x5 << 0)] @ OMAP-730 only
+               and     \rd, \rd, #0x60
+               teq     \rd, #0x60
+               bne     1001b
+1002:
+               .endm
+
+               .macro  waituart,rd,rx
+               .endm
diff --git a/include/asm-arm/arch-omap/entry-macro.S b/include/asm-arm/arch-omap/entry-macro.S
new file mode 100644 (file)
index 0000000..57b1268
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * include/asm-arm/arch-omap/entry-macro.S
+ *
+ * Low-level IRQ helper macros for OMAP-based platforms
+ *
+ * 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.
+ */
+
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               ldr     \base, =IO_ADDRESS(OMAP_IH1_BASE)
+               ldr     \irqnr, [\base, #IRQ_ITR_REG_OFFSET]
+               ldr     \tmp, [\base, #IRQ_MIR_REG_OFFSET]
+               mov     \irqstat, #0xffffffff
+               bic     \tmp, \irqstat, \tmp
+               tst     \irqnr, \tmp
+               beq     1510f
+
+               ldr     \irqnr, [\base, #IRQ_SIR_FIQ_REG_OFFSET]
+               cmp     \irqnr, #0
+               ldreq   \irqnr, [\base, #IRQ_SIR_IRQ_REG_OFFSET]
+               cmpeq   \irqnr, #INT_IH2_IRQ
+               ldreq   \base, =IO_ADDRESS(OMAP_IH2_BASE)
+               ldreq   \irqnr, [\base, #IRQ_SIR_IRQ_REG_OFFSET]
+               addeqs  \irqnr, \irqnr, #32
+1510:
+               .endm
+
diff --git a/include/asm-arm/arch-omap/omap16xx.h b/include/asm-arm/arch-omap/omap16xx.h
new file mode 100644 (file)
index 0000000..d6c4a94
--- /dev/null
@@ -0,0 +1,179 @@
+/* linux/include/asm-arm/arch-omap/omap16xx.h
+ *
+ * Hardware definitions for TI OMAP1610/5912/1710 processors.
+ *
+ * Cleanup for Linux-2.6 by Dirk Behme <dirk.behme@de.bosch.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS 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.
+ */
+
+#ifndef __ASM_ARCH_OMAP16XX_H
+#define __ASM_ARCH_OMAP16XX_H
+
+/*
+ * ----------------------------------------------------------------------------
+ * Base addresses
+ * ----------------------------------------------------------------------------
+ */
+
+/* Syntax: XX_BASE = Virtual base address, XX_START = Physical base address */
+
+#define OMAP16XX_SRAM_BASE     0xD0000000
+#define OMAP1610_SRAM_SIZE     (SZ_16K)
+#define OMAP5912_SRAM_SIZE     0x3E800
+#define OMAP16XX_SRAM_START    0x20000000
+
+#define OMAP16XX_DSP_BASE      0xE0000000
+#define OMAP16XX_DSP_SIZE      0x28000
+#define OMAP16XX_DSP_START     0xE0000000
+
+#define OMAP16XX_DSPREG_BASE   0xE1000000
+#define OMAP16XX_DSPREG_SIZE   SZ_128K
+#define OMAP16XX_DSPREG_START  0xE1000000
+
+/*
+ * ----------------------------------------------------------------------------
+ * Memory used by power management
+ * ----------------------------------------------------------------------------
+ */
+
+#define OMAP1610_SRAM_IDLE_SUSPEND     (OMAP16XX_SRAM_BASE + OMAP1610_SRAM_SIZE - 0x200)
+#define OMAP5912_SRAM_IDLE_SUSPEND     (OMAP16XX_SRAM_BASE + OMAP5912_SRAM_SIZE - 0x200)
+#define OMAP1610_SRAM_API_SUSPEND      (OMAP1610_SRAM_IDLE_SUSPEND + 0x100)
+
+/*
+ * ---------------------------------------------------------------------------
+ * Interrupts
+ * ---------------------------------------------------------------------------
+ */
+#define OMAP_IH2_0_BASE                (0xfffe0000)
+#define OMAP_IH2_1_BASE                (0xfffe0100)
+#define OMAP_IH2_2_BASE                (0xfffe0200)
+#define OMAP_IH2_3_BASE                (0xfffe0300)
+
+#define OMAP_IH2_0_ITR         (OMAP_IH2_0_BASE + 0x00)
+#define OMAP_IH2_0_MIR         (OMAP_IH2_0_BASE + 0x04)
+#define OMAP_IH2_0_SIR_IRQ     (OMAP_IH2_0_BASE + 0x10)
+#define OMAP_IH2_0_SIR_FIQ     (OMAP_IH2_0_BASE + 0x14)
+#define OMAP_IH2_0_CONTROL     (OMAP_IH2_0_BASE + 0x18)
+#define OMAP_IH2_0_ILR0                (OMAP_IH2_0_BASE + 0x1c)
+#define OMAP_IH2_0_ISR         (OMAP_IH2_0_BASE + 0x9c)
+
+#define OMAP_IH2_1_ITR         (OMAP_IH2_1_BASE + 0x00)
+#define OMAP_IH2_1_MIR         (OMAP_IH2_1_BASE + 0x04)
+#define OMAP_IH2_1_SIR_IRQ     (OMAP_IH2_1_BASE + 0x10)
+#define OMAP_IH2_1_SIR_FIQ     (OMAP_IH2_1_BASE + 0x14)
+#define OMAP_IH2_1_CONTROL     (OMAP_IH2_1_BASE + 0x18)
+#define OMAP_IH2_1_ILR1                (OMAP_IH2_1_BASE + 0x1c)
+#define OMAP_IH2_1_ISR         (OMAP_IH2_1_BASE + 0x9c)
+
+#define OMAP_IH2_2_ITR         (OMAP_IH2_2_BASE + 0x00)
+#define OMAP_IH2_2_MIR         (OMAP_IH2_2_BASE + 0x04)
+#define OMAP_IH2_2_SIR_IRQ     (OMAP_IH2_2_BASE + 0x10)
+#define OMAP_IH2_2_SIR_FIQ     (OMAP_IH2_2_BASE + 0x14)
+#define OMAP_IH2_2_CONTROL     (OMAP_IH2_2_BASE + 0x18)
+#define OMAP_IH2_2_ILR2                (OMAP_IH2_2_BASE + 0x1c)
+#define OMAP_IH2_2_ISR         (OMAP_IH2_2_BASE + 0x9c)
+
+#define OMAP_IH2_3_ITR         (OMAP_IH2_3_BASE + 0x00)
+#define OMAP_IH2_3_MIR         (OMAP_IH2_3_BASE + 0x04)
+#define OMAP_IH2_3_SIR_IRQ     (OMAP_IH2_3_BASE + 0x10)
+#define OMAP_IH2_3_SIR_FIQ     (OMAP_IH2_3_BASE + 0x14)
+#define OMAP_IH2_3_CONTROL     (OMAP_IH2_3_BASE + 0x18)
+#define OMAP_IH2_3_ILR3                (OMAP_IH2_3_BASE + 0x1c)
+#define OMAP_IH2_3_ISR         (OMAP_IH2_3_BASE + 0x9c)
+
+/*
+ * ----------------------------------------------------------------------------
+ * Clocks
+ * ----------------------------------------------------------------------------
+ */
+#define OMAP16XX_ARM_IDLECT3   (CLKGEN_REG_BASE + 0x24)
+
+/*
+ * ----------------------------------------------------------------------------
+ * Pin configuration registers
+ * ----------------------------------------------------------------------------
+ */
+#define OMAP16XX_CONF_VOLTAGE_VDDSHV6  (1 << 8)
+#define OMAP16XX_CONF_VOLTAGE_VDDSHV7  (1 << 9)
+#define OMAP16XX_CONF_VOLTAGE_VDDSHV8  (1 << 10)
+#define OMAP16XX_CONF_VOLTAGE_VDDSHV9  (1 << 11)
+#define OMAP16XX_SUBLVDS_CONF_VALID    (1 << 13)
+
+/*
+ * ---------------------------------------------------------------------------
+ * TIPB bus interface
+ * ---------------------------------------------------------------------------
+ */
+#define TIPB_SWITCH_BASE                (0xfffbc800)
+#define OMAP16XX_MMCSD2_SSW_MPU_CONF   (TIPB_SWITCH_BASE + 0x160)
+
+/* UART3 Registers Maping through MPU bus */
+#define UART3_RHR               (OMAP_UART3_BASE + 0)
+#define UART3_THR               (OMAP_UART3_BASE + 0)
+#define UART3_DLL               (OMAP_UART3_BASE + 0)
+#define UART3_IER               (OMAP_UART3_BASE + 4)
+#define UART3_DLH               (OMAP_UART3_BASE + 4)
+#define UART3_IIR               (OMAP_UART3_BASE + 8)
+#define UART3_FCR               (OMAP_UART3_BASE + 8)
+#define UART3_EFR               (OMAP_UART3_BASE + 8)
+#define UART3_LCR               (OMAP_UART3_BASE + 0x0C)
+#define UART3_MCR               (OMAP_UART3_BASE + 0x10)
+#define UART3_XON1_ADDR1        (OMAP_UART3_BASE + 0x10)
+#define UART3_XON2_ADDR2        (OMAP_UART3_BASE + 0x14)
+#define UART3_LSR               (OMAP_UART3_BASE + 0x14)
+#define UART3_TCR               (OMAP_UART3_BASE + 0x18)
+#define UART3_MSR               (OMAP_UART3_BASE + 0x18)
+#define UART3_XOFF1             (OMAP_UART3_BASE + 0x18)
+#define UART3_XOFF2             (OMAP_UART3_BASE + 0x1C)
+#define UART3_SPR               (OMAP_UART3_BASE + 0x1C)
+#define UART3_TLR               (OMAP_UART3_BASE + 0x1C)
+#define UART3_MDR1              (OMAP_UART3_BASE + 0x20)
+#define UART3_MDR2              (OMAP_UART3_BASE + 0x24)
+#define UART3_SFLSR             (OMAP_UART3_BASE + 0x28)
+#define UART3_TXFLL             (OMAP_UART3_BASE + 0x28)
+#define UART3_RESUME            (OMAP_UART3_BASE + 0x2C)
+#define UART3_TXFLH             (OMAP_UART3_BASE + 0x2C)
+#define UART3_SFREGL            (OMAP_UART3_BASE + 0x30)
+#define UART3_RXFLL             (OMAP_UART3_BASE + 0x30)
+#define UART3_SFREGH            (OMAP_UART3_BASE + 0x34)
+#define UART3_RXFLH             (OMAP_UART3_BASE + 0x34)
+#define UART3_BLR               (OMAP_UART3_BASE + 0x38)
+#define UART3_ACREG             (OMAP_UART3_BASE + 0x3C)
+#define UART3_DIV16             (OMAP_UART3_BASE + 0x3C)
+#define UART3_SCR               (OMAP_UART3_BASE + 0x40)
+#define UART3_SSR               (OMAP_UART3_BASE + 0x44)
+#define UART3_EBLR              (OMAP_UART3_BASE + 0x48)
+#define UART3_OSC_12M_SEL       (OMAP_UART3_BASE + 0x4C)
+#define UART3_MVR               (OMAP_UART3_BASE + 0x50)
+
+/*
+ * ----------------------------------------------------------------------------
+ * Pulse-Width Light
+ * ----------------------------------------------------------------------------
+ */
+#define OMAP16XX_PWL_BASE      (0xfffb5800)
+#define OMAP16XX_PWL_ENABLE    (OMAP16XX_PWL_BASE + 0x00)
+#define OMAP16XX_PWL_CLK_ENABLE        (OMAP16XX_PWL_BASE + 0x04)
+
+#endif /*  __ASM_ARCH_OMAP16XX_H */
+
diff --git a/include/asm-arm/arch-omap/tc.h b/include/asm-arm/arch-omap/tc.h
new file mode 100644 (file)
index 0000000..a1d2e5a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * linux/include/asm-arm/arch-omap/tc.h
+ *
+ * OMAP Traffic Controller
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __ASM_ARCH_TC_H
+#define __ASM_ARCH_TC_H
+
+#define OMAP_TC_OCPT1_PRIOR    0xFFFECC00
+#define OMAP_TC_EMIFS_PRIOR    0xFFFECC04
+#define OMAP_TC_EMIFF_PRIOR    0xFFFECC08
+#define OMAP_TC_OCPT2_PRIOR    0xFFFECCD0
+
+
+/* EMIF Slow Interface Configuration Register */
+#define        OMAP_EMIFS_CONFIG_REG   __REG32(EMIFS_CONFIG)
+
+#define OMAP_EMIFS_CONFIG_FR           (1 << 4)
+#define OMAP_EMIFS_CONFIG_PDE          (1 << 3)
+#define OMAP_EMIFS_CONFIG_PWD_EN       (1 << 2)
+#define OMAP_EMIFS_CONFIG_BM           (1 << 1)
+#define OMAP_EMIFS_CONFIG_WP           (1 << 0)
+
+/* external EMIFS chipselect regions */
+#define        OMAP_CS1_PHYS           0x04000000
+#define        OMAP_CS1_SIZE           SZ_64M
+
+#define        OMAP_CS1A_PHYS          OMAP_CS1_PHYS
+#define        OMAP_CS1A_SIZE          SZ_32M
+
+#define        OMAP_CS1B_PHYS          (OMAP_CS1A_PHYS + OMAP_CS1A_SIZE)
+#define        OMAP_CS1B_SIZE          SZ_32M
+
+#define        OMAP_CS2_PHYS           0x08000000
+#define        OMAP_CS2_SIZE           SZ_64M
+
+#define        OMAP_CS2A_PHYS          OMAP_CS2_PHYS
+#define        OMAP_CS2A_SIZE          SZ_32M
+
+#define        OMAP_CS2B_PHYS          (OMAP_CS2A_PHYS + OMAP_CS2A_SIZE)
+#define        OMAP_CS2B_SIZE          SZ_32M
+
+#define        OMAP_CS3_PHYS           0x0c000000
+#define        OMAP_CS3_SIZE           SZ_64M
+
+#endif /* __ASM_ARCH_TC_H */
diff --git a/include/asm-arm/arch-pxa/audio.h b/include/asm-arm/arch-pxa/audio.h
new file mode 100644 (file)
index 0000000..60976f8
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __ASM_ARCH_AUDIO_H__
+#define __ASM_ARCH_AUDIO_H__
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+typedef struct {
+       int (*startup)(snd_pcm_substream_t *, void *);
+       void (*shutdown)(snd_pcm_substream_t *, void *);
+       void (*suspend)(void *);
+       void (*resume)(void *);
+       void *priv;
+} pxa2xx_audio_ops_t;
+
+#endif
diff --git a/include/asm-arm/arch-pxa/corgi.h b/include/asm-arm/arch-pxa/corgi.h
new file mode 100644 (file)
index 0000000..950c937
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Hardware specific definitions for SL-C7xx series of PDAs
+ *
+ * Copyright (c) 2004-2005 Richard Purdie
+ *
+ * Based on Sharp's 2.4 kernel patches
+ *
+ * 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.
+ *
+ */
+#ifndef __ASM_ARCH_CORGI_H
+#define __ASM_ARCH_CORGI_H  1
+
+
+/*
+ * Corgi (Non Standard) GPIO Definitions
+ */
+#define CORGI_GPIO_KEY_INT                     (0)     /* Keyboard Interrupt */
+#define CORGI_GPIO_AC_IN                       (1) /* Charger Detection */
+#define CORGI_GPIO_WAKEUP                      (3) /* System wakeup notification? */
+#define CORGI_GPIO_AK_INT                      (4)     /* Headphone Jack Control Interrupt */
+#define CORGI_GPIO_TP_INT                      (5)     /* Touch Panel Interrupt */
+#define CORGI_GPIO_nSD_WP                      (7) /* SD Write Protect? */
+#define CORGI_GPIO_nSD_DETECT          (9) /* MMC/SD Card Detect */
+#define CORGI_GPIO_nSD_INT                     (10) /* SD Interrupt for SDIO? */
+#define CORGI_GPIO_MAIN_BAT_LOW                (11) /* Main Battery Low Notification */
+#define CORGI_GPIO_BAT_COVER           (11) /* Battery Cover Detect */
+#define CORGI_GPIO_LED_ORANGE          (13) /* Orange LED Control */
+#define CORGI_GPIO_CF_CD                       (14) /* Compact Flash Card Detect */
+#define CORGI_GPIO_CHRG_FULL           (16) /* Charging Complete Notification */
+#define CORGI_GPIO_CF_IRQ                      (17) /* Compact Flash Interrupt */
+#define CORGI_GPIO_LCDCON_CS           (19) /* LCD Control Chip Select */
+#define CORGI_GPIO_MAX1111_CS          (20) /* MAX1111 Chip Select */
+#define CORGI_GPIO_ADC_TEMP_ON         (21) /* Select battery voltage or temperature */
+#define CORGI_GPIO_IR_ON                       (22) /* Enable IR Transciever */
+#define CORGI_GPIO_ADS7846_CS          (24) /* ADS7846 Chip Select */
+#define CORGI_GPIO_SD_PWR                      (33) /* MMC/SD Power */
+#define CORGI_GPIO_CHRG_ON                     (38) /* Enable battery Charging */
+#define CORGI_GPIO_DISCHARGE_ON                (42) /* Enable battery Discharge */
+#define CORGI_GPIO_CHRG_UKN                    (43) /* Unknown Charging (Bypass Control?) */
+#define CORGI_GPIO_HSYNC                       (44) /* LCD HSync Pulse */
+#define CORGI_GPIO_USB_PULLUP          (45) /* USB show presence to host */
+
+
+/*
+ * Corgi Keyboard Definitions
+ */
+#define CORGI_KEY_STROBE_NUM           (12)
+#define CORGI_KEY_SENSE_NUM                    (8)
+#define CORGI_GPIO_ALL_STROBE_BIT      (0x00003ffc)
+#define CORGI_GPIO_HIGH_SENSE_BIT      (0xfc000000)
+#define CORGI_GPIO_HIGH_SENSE_RSHIFT   (26)
+#define CORGI_GPIO_LOW_SENSE_BIT       (0x00000003)
+#define CORGI_GPIO_LOW_SENSE_LSHIFT    (6)
+#define CORGI_GPIO_STROBE_BIT(a)       GPIO_bit(66+(a))
+#define CORGI_GPIO_SENSE_BIT(a)                GPIO_bit(58+(a))
+#define CORGI_GAFR_ALL_STROBE_BIT      (0x0ffffff0)
+#define CORGI_GAFR_HIGH_SENSE_BIT      (0xfff00000)
+#define CORGI_GAFR_LOW_SENSE_BIT       (0x0000000f)
+#define CORGI_GPIO_KEY_SENSE(a)                (58+(a))
+#define CORGI_GPIO_KEY_STROBE(a)       (66+(a))
+
+
+/*
+ * Corgi Interrupts
+ */
+#define CORGI_IRQ_GPIO_KEY_INT         IRQ_GPIO(0)
+#define CORGI_IRQ_GPIO_AC_IN           IRQ_GPIO(1)
+#define CORGI_IRQ_GPIO_WAKEUP          IRQ_GPIO(3)
+#define CORGI_IRQ_GPIO_AK_INT          IRQ_GPIO(4)
+#define CORGI_IRQ_GPIO_TP_INT          IRQ_GPIO(5)
+#define CORGI_IRQ_GPIO_nSD_DETECT      IRQ_GPIO(9)
+#define CORGI_IRQ_GPIO_nSD_INT         IRQ_GPIO(10)
+#define CORGI_IRQ_GPIO_MAIN_BAT_LOW    IRQ_GPIO(11)
+#define CORGI_IRQ_GPIO_CF_CD           IRQ_GPIO(14)
+#define CORGI_IRQ_GPIO_CHRG_FULL       IRQ_GPIO(16)    /* Battery fully charged */
+#define CORGI_IRQ_GPIO_CF_IRQ          IRQ_GPIO(17)
+#define CORGI_IRQ_GPIO_KEY_SENSE(a)    IRQ_GPIO(58+(a))        /* Keyboard Sense lines */
+
+
+/*
+ * Corgi SCOOP GPIOs and Config
+ */
+#define CORGI_SCP_LED_GREEN            SCOOP_GPCR_PA11
+#define CORGI_SCP_SWA                  SCOOP_GPCR_PA12  /* Hinge Switch A */
+#define CORGI_SCP_SWB                  SCOOP_GPCR_PA13  /* Hinge Switch B */
+#define CORGI_SCP_MUTE_L               SCOOP_GPCR_PA14
+#define CORGI_SCP_MUTE_R               SCOOP_GPCR_PA15
+#define CORGI_SCP_AKIN_PULLUP  SCOOP_GPCR_PA16
+#define CORGI_SCP_APM_ON               SCOOP_GPCR_PA17
+#define CORGI_SCP_BACKLIGHT_CONT       SCOOP_GPCR_PA18
+#define CORGI_SCP_MIC_BIAS             SCOOP_GPCR_PA19
+
+#define CORGI_SCOOP_IO_DIR     ( CORGI_SCP_LED_GREEN | CORGI_SCP_MUTE_L | CORGI_SCP_MUTE_R | \
+                       CORGI_SCP_AKIN_PULLUP | CORGI_SCP_APM_ON | CORGI_SCP_BACKLIGHT_CONT | \
+                       CORGI_SCP_MIC_BIAS )
+#define CORGI_SCOOP_IO_OUT     ( CORGI_SCP_MUTE_L | CORGI_SCP_MUTE_R )
+
+
+/*
+ * Corgi Parameter Area Definitions
+ */
+#define FLASH_MEM_BASE 0xa0000a00
+#define FLASH_MAGIC_CHG(a,b,c,d) ( ( d << 24 ) | ( c << 16 )  | ( b << 8 ) | a )
+
+#define FLASH_COMADJ_MAJIC     FLASH_MAGIC_CHG('C','M','A','D')
+#define        FLASH_COMADJ_MAGIC_ADR  0x00
+#define        FLASH_COMADJ_DATA_ADR   0x04
+
+#define FLASH_PHAD_MAJIC       FLASH_MAGIC_CHG('P','H','A','D')
+#define        FLASH_PHAD_MAGIC_ADR    0x38
+#define        FLASH_PHAD_DATA_ADR     0x3C
+
+struct sharpsl_flash_param_info {
+  unsigned int comadj_keyword;
+  unsigned int comadj;
+
+  unsigned int uuid_keyword;
+  unsigned char uuid[16];
+
+  unsigned int touch_keyword;
+  unsigned int touch1;
+  unsigned int touch2;
+  unsigned int touch3;
+  unsigned int touch4;
+
+  unsigned int adadj_keyword;
+  unsigned int adadj;
+
+  unsigned int phad_keyword;
+  unsigned int phadadj;
+};
+
+
+/*
+ * External Functions
+ */
+extern unsigned long corgi_ssp_ads7846_putget(unsigned long);
+extern unsigned long corgi_ssp_ads7846_get(void);
+extern void corgi_ssp_ads7846_put(ulong data);
+extern void corgi_ssp_ads7846_lock(void);
+extern void corgi_ssp_ads7846_unlock(void);
+extern void corgi_ssp_lcdtg_send (u8 adrs, u8 data);
+extern void corgi_ssp_blduty_set(int duty);
+extern int corgi_ssp_max1111_get(ulong data);
+
+#endif /* __ASM_ARCH_CORGI_H  */
+
diff --git a/include/asm-arm/arch-pxa/debug-macro.S b/include/asm-arm/arch-pxa/debug-macro.S
new file mode 100644 (file)
index 0000000..f288e74
--- /dev/null
@@ -0,0 +1,36 @@
+/* linux/include/asm-arm/arch-pxa/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx, #0x40000000                @ physical
+               movne   \rx, #io_p2v(0x40000000)        @ virtual
+               orr     \rx, \rx, #0x00100000
+               .endm
+
+               .macro  senduart,rd,rx
+               str     \rd, [\rx, #0]
+               .endm
+
+               .macro  busyuart,rd,rx
+1002:          ldr     \rd, [\rx, #0x14]
+               tst     \rd, #(1 << 6)
+               beq     1002b
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldr     \rd, [\rx, #0x14]
+               tst     \rd, #(1 << 5)
+               beq     1001b
+               .endm
diff --git a/include/asm-arm/arch-pxa/entry-macro.S b/include/asm-arm/arch-pxa/entry-macro.S
new file mode 100644 (file)
index 0000000..2abfc8b
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * include/asm-arm/arch-pxa/entry-macro.S
+ *
+ * Low-level IRQ helper macros for PXA-based platforms
+ *
+ * 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.
+ */
+
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+#ifdef CONFIG_PXA27x
+               mrc     p6, 0, \irqstat, c0, c0, 0              @ ICIP
+               mrc     p6, 0, \irqnr, c1, c0, 0                @ ICMR
+#else
+               mov     \base, #io_p2v(0x40000000)      @ IIR Ctl = 0x40d00000
+               add     \base, \base, #0x00d00000
+               ldr     \irqstat, [\base, #0]           @ ICIP
+               ldr     \irqnr, [\base, #4]             @ ICMR
+#endif
+               ands    \irqnr, \irqstat, \irqnr
+               beq     1001f
+               rsb     \irqstat, \irqnr, #0
+               and     \irqstat, \irqstat, \irqnr
+               clz     \irqnr, \irqstat
+               rsb     \irqnr, \irqnr, #(31 - PXA_IRQ_SKIP)
+1001:
+               .endm
diff --git a/include/asm-arm/arch-pxa/ssp.h b/include/asm-arm/arch-pxa/ssp.h
new file mode 100644 (file)
index 0000000..12fc8bf
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  ssp.h
+ *
+ *  Copyright (C) 2003 Russell King, 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This driver supports the following PXA CPU/SSP ports:-
+ *
+ *       PXA250     SSP
+ *       PXA255     SSP, NSSP
+ *       PXA26x     SSP, NSSP, ASSP
+ *       PXA27x     SSP1, SSP2, SSP3
+ */
+
+#ifndef SSP_H
+#define SSP_H
+
+struct ssp_state {
+       u32     cr0;
+       u32 cr1;
+       u32 to;
+       u32 psp;
+};
+
+struct ssp_dev {
+       u32 port;
+       u32 mode;
+       u32 flags;
+       u32 psp_flags;
+       u32 speed;
+};
+
+int ssp_write_word(struct ssp_dev *dev, u32 data);
+int ssp_read_word(struct ssp_dev *dev);
+void ssp_flush(struct ssp_dev *dev);
+void ssp_enable(struct ssp_dev *dev);
+void ssp_disable(struct ssp_dev *dev);
+void ssp_save_state(struct ssp_dev *dev, struct ssp_state *ssp);
+void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *ssp);
+int ssp_init(struct ssp_dev *dev, u32 port, u32 mode, u32 flags, u32 psp_flags,
+                                               u32 speed);
+void ssp_exit(struct ssp_dev *dev);
+
+#endif
diff --git a/include/asm-arm/arch-rpc/debug-macro.S b/include/asm-arm/arch-rpc/debug-macro.S
new file mode 100644 (file)
index 0000000..0711828
--- /dev/null
@@ -0,0 +1,35 @@
+/* linux/include/asm-arm/arch-rpc/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .macro  addruart,rx
+               mov     \rx, #0xe0000000
+               orr     \rx, \rx, #0x00010000
+               orr     \rx, \rx, #0x00000fe0
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx]
+               .endm
+
+               .macro  busyuart,rd,rx
+1001:          ldrb    \rd, [\rx, #0x14]
+               and     \rd, \rd, #0x60
+               teq     \rd, #0x60
+               bne     1001b
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldrb    \rd, [\rx, #0x18]
+               tst     \rd, #0x10
+               beq     1001b
+               .endm
diff --git a/include/asm-arm/arch-rpc/entry-macro.S b/include/asm-arm/arch-rpc/entry-macro.S
new file mode 100644 (file)
index 0000000..686f413
--- /dev/null
@@ -0,0 +1,3 @@
+
+#include <asm/hardware/entry-macro-iomd.S>
+
diff --git a/include/asm-arm/arch-s3c2410/bast-pmu.h b/include/asm-arm/arch-s3c2410/bast-pmu.h
new file mode 100644 (file)
index 0000000..758c5c5
--- /dev/null
@@ -0,0 +1,43 @@
+/* linux/include/asm-arm/arch-s3c2410/bast-pmu.h
+ *
+ * (c) 2003,2004 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *     Vincent Sanders <vince@simtec.co.uk>
+ *
+ * Machine BAST - Power Management chip
+ *
+ * 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.
+ *
+ * Changelog:
+ *     08-Oct-2003     BJD     Initial creation
+*/
+
+#ifndef __ASM_ARCH_BASTPMU_H
+#define __ASM_ARCH_BASTPMU_H "08_OCT_2004"
+
+#define BASTPMU_REG_IDENT      (0x00)
+#define BASTPMU_REG_VERSION    (0x01)
+#define BASTPMU_REG_DDCCTRL    (0x02)
+#define BASTPMU_REG_POWER      (0x03)
+#define BASTPMU_REG_RESET      (0x04)
+#define BASTPMU_REG_GWO                (0x05)
+#define BASTPMU_REG_WOL                (0x06)
+#define BASTPMU_REG_WOR                (0x07)
+#define BASTPMU_REG_UID                (0x09)
+
+#define BASTPMU_EEPROM         (0xC0)
+
+#define BASTPMU_EEP_UID                (BASTPMU_EEPROM + 0)
+#define BASTPMU_EEP_WOL                (BASTPMU_EEPROM + 8)
+#define BASTPMU_EEP_WOR                (BASTPMU_EEPROM + 9)
+
+#define BASTPMU_IDENT_0                0x53
+#define BASTPMU_IDENT_1                0x42
+#define BASTPMU_IDENT_2                0x50
+#define BASTPMU_IDENT_3                0x4d
+
+#define BASTPMU_RESET_GUARD    (0x55)
+
+#endif /* __ASM_ARCH_BASTPMU_H */
diff --git a/include/asm-arm/arch-s3c2410/debug-macro.S b/include/asm-arm/arch-s3c2410/debug-macro.S
new file mode 100644 (file)
index 0000000..eab2d12
--- /dev/null
@@ -0,0 +1,97 @@
+/* linux/include/asm-arm/arch-s3c2410/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Copyright (C) 2005 Simtec Electronics
+ *
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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 <asm/arch/map.h>
+#include <asm/arch/regs-serial.h>
+#include <asm/arch/regs-gpio.h>
+
+#define S3C2410_UART1_OFF (0x4000)
+#define SHIFT_2440TXF (14-9)
+
+               .macro addruart, rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1
+               ldreq   \rx, = S3C2410_PA_UART
+               ldrne   \rx, = S3C2410_VA_UART
+#if CONFIG_DEBUG_S3C2410_UART != 0
+               add     \rx, \rx, #(S3C2410_UART1_OFF * CONFIG_DEBUG_S3C2410_UART)
+#endif
+               .endm
+
+               .macro  senduart,rd,rx
+               str     \rd, [\rx, # S3C2410_UTXH ]
+               .endm
+
+               .macro  busyuart, rd, rx
+               ldr     \rd, [ \rx, # S3C2410_UFCON ]
+               tst     \rd, #S3C2410_UFCON_FIFOMODE    @ fifo enabled?
+               beq     1001f                           @
+               @ FIFO enabled...
+1003:
+               mrc     p15, 0, \rd, c1, c0
+               tst     \rd, #1
+               addeq   \rd, \rx, #(S3C2410_PA_GPIO - S3C2410_PA_UART)
+               addne   \rd, \rx, #(S3C2410_VA_GPIO - S3C2410_VA_UART)
+               bic     \rd, \rd, #0xff000
+               ldr     \rd, [ \rd, # S3C2410_GSTATUS1 - S3C2410_GPIOREG(0) ]
+               and     \rd, \rd, #0x00ff0000
+               teq     \rd, #0x00440000                @ is it 2440?
+
+               ldr     \rd, [ \rx, # S3C2410_UFSTAT ]
+               moveq   \rd, \rd, lsr #SHIFT_2440TXF
+               tst     \rd, #S3C2410_UFSTAT_TXFULL
+               bne     1003b
+               b       1002f
+
+1001:
+               @ busy waiting for non fifo
+               ldr     \rd, [ \rx, # S3C2410_UTRSTAT ]
+               tst     \rd, #S3C2410_UTRSTAT_TXFE
+               beq     1001b
+
+1002:          @ exit busyuart
+               .endm
+
+               .macro  waituart,rd,rx
+
+               ldr     \rd, [ \rx, # S3C2410_UFCON ]
+               tst     \rd, #S3C2410_UFCON_FIFOMODE    @ fifo enabled?
+               beq     1001f                           @
+               @ FIFO enabled...
+1003:
+               mrc     p15, 0, \rd, c1, c0
+               tst     \rd, #1
+               addeq   \rd, \rx, #(S3C2410_PA_GPIO - S3C2410_PA_UART)
+               addne   \rd, \rx, #(S3C2410_VA_GPIO - S3C2410_VA_UART)
+               bic     \rd, \rd, #0xff000
+               ldr     \rd, [ \rd, # S3C2410_GSTATUS1 - S3C2410_GPIOREG(0) ]
+               and     \rd, \rd, #0x00ff0000
+               teq     \rd, #0x00440000                @ is it 2440?
+
+               ldr     \rd, [ \rx, # S3C2410_UFSTAT ]
+               andne   \rd, \rd, #S3C2410_UFSTAT_TXMASK
+               andeq   \rd, \rd, #S3C2440_UFSTAT_TXMASK
+               teq     \rd, #0
+               bne     1003b
+               b       1002f
+
+1001:
+               @ idle waiting for non fifo
+               ldr     \rd, [ \rx, # S3C2410_UTRSTAT ]
+               tst     \rd, #S3C2410_UTRSTAT_TXFE
+               beq     1001b
+
+1002:          @ exit busyuart
+               .endm
diff --git a/include/asm-arm/arch-s3c2410/entry-macro.S b/include/asm-arm/arch-s3c2410/entry-macro.S
new file mode 100644 (file)
index 0000000..4cc886e
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * include/asm-arm/arch-s3c2410/entry-macro.S
+ *
+ * Low-level IRQ helper macros for S3C2410-based platforms
+ *
+ * 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.
+ */
+
+
+       .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+
+               mov     \tmp, #S3C2410_VA_IRQ
+               ldr     \irqnr, [ \tmp, #0x14 ]         @ get irq no
+30000:
+               teq     \irqnr, #4
+               teqne   \irqnr, #5
+               beq     1002f                           @ external irq reg
+
+               @ debug check to see if interrupt reported is the same
+               @ as the offset....
+
+               teq     \irqnr, #0
+               beq     20002f
+               ldr     \irqstat, [ \tmp, #0x10 ]       @ INTPND
+               mov     \irqstat, \irqstat, lsr \irqnr
+               tst     \irqstat, #1
+               bne     20002f
+
+               /* debug/warning if we get an invalud response from the
+                * INTOFFSET register */
+#if 1
+               stmfd   r13!, { r0 - r4 , r8-r12, r14 }
+               ldr     r1,     [ \tmp, #0x14 ]         @ INTOFFSET
+               ldr     r2,     [ \tmp, #0x10 ]         @ INTPND
+               ldr     r3,     [ \tmp, #0x00 ]         @ SRCPND
+               adr     r0, 20003f
+               bl      printk
+               b       20004f
+
+20003:
+               .ascii  "<7>irq: err - bad offset %d, intpnd=%08x, srcpnd=%08x\n"
+               .byte   0
+               .align  4
+20004:
+               mov     r1, #1
+               mov     \tmp, #S3C2410_VA_IRQ
+               ldmfd   r13!, { r0 - r4 , r8-r12, r14 }
+#endif
+
+               @ try working out interrupt number for ourselves
+               mov     \irqnr, #0
+               ldr     \irqstat, [ \tmp, #0x10 ]       @ INTPND
+10021:
+               movs    \irqstat, \irqstat, lsr#1
+               bcs     30000b          @ try and re-start the proccess
+               add     \irqnr, \irqnr, #1
+               cmp     \irqnr, #32
+               ble     10021b
+
+               @ found no interrupt, set Z flag and leave
+               movs    \irqnr, #0
+               b       1001f
+
+20005:
+20002:         @ exit
+               @ we base the s3c2410x interrupts at 16 and above to allow
+               @ isa peripherals to have their standard interrupts, also
+               @ ensure that Z flag is un-set on exit
+
+               @ note, we cannot be sure if we get IRQ_EINT0 (0) that
+               @ there is simply no interrupt pending, so in all other
+               @ cases we jump to say we have found something, otherwise
+               @ we check to see if the interrupt really is assrted
+               adds    \irqnr, \irqnr, #IRQ_EINT0
+               teq     \irqnr, #IRQ_EINT0
+               bne     1001f                           @ exit
+               ldr     \irqstat, [ \tmp, #0x10 ]       @ INTPND
+               teq     \irqstat, #0
+               moveq   \irqnr, #0
+               b       1001f
+
+               @ we get here from no main or external interrupts pending
+1002:
+               add     \tmp, \tmp, #S3C2410_VA_GPIO - S3C2410_VA_IRQ
+               ldr     \irqstat, [ \tmp, # 0xa8 ]      @ EXTINTPEND
+               ldr     \irqnr, [ \tmp, # 0xa4 ]        @ EXTINTMASK
+
+               bic     \irqstat, \irqstat, \irqnr      @ clear masked irqs
+
+               mov     \irqnr, #IRQ_EINT4              @ start extint nos
+               mov     \irqstat, \irqstat, lsr#4       @ ignore bottom 4 bits
+10021:
+               movs    \irqstat, \irqstat, lsr#1
+               bcs     1004f
+               add     \irqnr, \irqnr, #1
+               cmp     \irqnr, #IRQ_EINT23
+               ble     10021b
+
+               @ found no interrupt, set Z flag and leave
+               movs    \irqnr, #0
+
+1004:          @ ensure Z flag clear in case our MOVS shifted out the last bit
+               teq     \irqnr, #0
+1001:
+               @ exit irq routine
+               .endm
+
+
+               /* currently don't need an disable_fiq macro */
+
+               .macro  disable_fiq
+               .endm
+
+
diff --git a/include/asm-arm/arch-s3c2410/idle.h b/include/asm-arm/arch-s3c2410/idle.h
new file mode 100644 (file)
index 0000000..749227c
--- /dev/null
@@ -0,0 +1,28 @@
+/* linux/include/asm-arm/arch-s3c2410/idle.h
+ *
+ * Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk>
+ *             http://www.simtec.co.uk/products/SWLINUX/
+ *
+ * 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.
+ *
+ * S3C2410 CPU Idle controls
+ *
+ *  Changelog:
+ *     28-Oct-2004  BJD  Initial version
+ *
+*/
+
+#ifndef __ASM_ARCH_IDLE_H
+#define __ASM_ARCH_IDLE_H __FILE__
+
+/* This allows the over-ride of the default idle code, in case there
+ * is any other things to be done over idle (like DVS)
+*/
+
+extern void (*s3c24xx_idle)(void);
+
+extern void s3c24xx_default_idle(void);
+
+#endif /* __ASM_ARCH_IDLE_H */
diff --git a/include/asm-arm/arch-s3c2410/iic.h b/include/asm-arm/arch-s3c2410/iic.h
new file mode 100644 (file)
index 0000000..518547f
--- /dev/null
@@ -0,0 +1,36 @@
+/* linux/include/asm-arm/arch-s3c2410/iic.h
+ *
+ * (c) 2004 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 - I2C Controller platfrom_device info
+ *
+ * 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.
+ *
+ * Changelog:
+ *     05-Oct-2004 BJD  Created file
+ *     19-Oct-2004 BJD  Updated for s3c2440
+*/
+
+#ifndef __ASM_ARCH_IIC_H
+#define __ASM_ARCH_IIC_H __FILE__
+
+#define S3C_IICFLG_FILTER      (1<<0)  /* enable s3c2440 filter */
+
+/* Notes:
+ *     1) All frequencies are expressed in Hz
+ *     2) A value of zero is `do not care`
+*/
+
+struct s3c2410_platform_i2c {
+       unsigned int    flags;
+       unsigned int    slave_addr;     /* slave address for controller */
+       unsigned long   bus_freq;       /* standard bus frequency */
+       unsigned long   max_freq;       /* max frequency for the bus */
+       unsigned long   min_freq;       /* min frequency for the bus */
+       unsigned int    sda_delay;      /* pclks (s3c2440 only) */
+};
+
+#endif /* __ASM_ARCH_IIC_H */
diff --git a/include/asm-arm/arch-sa1100/debug-macro.S b/include/asm-arm/arch-sa1100/debug-macro.S
new file mode 100644 (file)
index 0000000..755fa34
--- /dev/null
@@ -0,0 +1,57 @@
+/* linux/include/asm-arm/arch-sa1100/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx, #0x80000000        @ physical base address
+               movne   \rx, #0xf8000000        @ virtual address
+
+               @ We probe for the active serial port here, coherently with
+               @ the comment in include/asm-arm/arch-sa1100/uncompress.h.
+               @ We assume r1 can be clobbered.
+
+               @ see if Ser3 is active
+               add     \rx, \rx, #0x00050000
+               ldr     r1, [\rx, #UTCR3]
+               tst     r1, #UTCR3_TXE
+
+               @ if Ser3 is inactive, then try Ser1
+               addeq   \rx, \rx, #(0x00010000 - 0x00050000)
+               ldreq   r1, [\rx, #UTCR3]
+               tsteq   r1, #UTCR3_TXE
+
+               @ if Ser1 is inactive, then try Ser2
+               addeq   \rx, \rx, #(0x00030000 - 0x00010000)
+               ldreq   r1, [\rx, #UTCR3]
+               tsteq   r1, #UTCR3_TXE
+
+               @ if all ports are inactive, then there is nothing we can do
+               moveq   pc, lr
+               .endm
+
+               .macro  senduart,rd,rx
+               str     \rd, [\rx, #UTDR]
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldr     \rd, [\rx, #UTSR1]
+               tst     \rd, #UTSR1_TNF
+               beq     1001b
+               .endm
+
+               .macro  busyuart,rd,rx
+1001:          ldr     \rd, [\rx, #UTSR1]
+               tst     \rd, #UTSR1_TBY
+               bne     1001b
+               .endm
diff --git a/include/asm-arm/arch-sa1100/entry-macro.S b/include/asm-arm/arch-sa1100/entry-macro.S
new file mode 100644 (file)
index 0000000..51fb50c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * include/asm-arm/arch-sa1100/entry-macro.S
+ *
+ * Low-level IRQ helper macros for SA1100-based platforms
+ *
+ * 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.
+ */
+
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               mov     r4, #0xfa000000                 @ ICIP = 0xfa050000
+               add     r4, r4, #0x00050000
+               ldr     \irqstat, [r4]                  @ get irqs
+               ldr     \irqnr, [r4, #4]                @ ICMR = 0xfa050004
+               ands    \irqstat, \irqstat, \irqnr
+               mov     \irqnr, #0
+               beq     1001f
+               tst     \irqstat, #0xff
+               moveq   \irqstat, \irqstat, lsr #8
+               addeq   \irqnr, \irqnr, #8
+               tsteq   \irqstat, #0xff
+               moveq   \irqstat, \irqstat, lsr #8
+               addeq   \irqnr, \irqnr, #8
+               tsteq   \irqstat, #0xff
+               moveq   \irqstat, \irqstat, lsr #8
+               addeq   \irqnr, \irqnr, #8
+               tst     \irqstat, #0x0f
+               moveq   \irqstat, \irqstat, lsr #4
+               addeq   \irqnr, \irqnr, #4
+               tst     \irqstat, #0x03
+               moveq   \irqstat, \irqstat, lsr #2
+               addeq   \irqnr, \irqnr, #2
+               tst     \irqstat, #0x01
+               addeqs  \irqnr, \irqnr, #1
+1001:
+               .endm
+
diff --git a/include/asm-arm/arch-shark/debug-macro.S b/include/asm-arm/arch-shark/debug-macro.S
new file mode 100644 (file)
index 0000000..7cb37f7
--- /dev/null
@@ -0,0 +1,31 @@
+/* linux/include/asm-arm/arch-shark/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+               .macro  addruart,rx
+               mov     \rx, #0xe0000000
+               orr     \rx, \rx, #0x000003f8
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx]
+               .endm
+
+               .macro  busyuart,rd,rx
+               mov     \rd, #0
+1001:          add     \rd, \rd, #1
+               teq     \rd, #0x10000
+               bne     1001b
+               .endm
+
+               .macro  waituart,rd,rx
+               .endm
diff --git a/include/asm-arm/arch-shark/entry-macro.S b/include/asm-arm/arch-shark/entry-macro.S
new file mode 100644 (file)
index 0000000..a924f27
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * include/asm-arm/arch-shark/entry-macro.S
+ *
+ * Low-level IRQ helper macros for Shark platform
+ *
+ * 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.
+ */
+               .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               mov     r4, #0xe0000000
+
+               mov     \irqstat, #0x0C
+               strb    \irqstat, [r4, #0x20]           @outb(0x0C, 0x20) /* Poll command */
+               ldrb    \irqnr, [r4, #0x20]             @irq = inb(0x20) & 7
+               and     \irqstat, \irqnr, #0x80
+               teq     \irqstat, #0
+               beq     43f
+               and     \irqnr, \irqnr, #7
+               teq     \irqnr, #2
+               bne     44f
+43:            mov     \irqstat, #0x0C
+               strb    \irqstat, [r4, #0xa0]           @outb(0x0C, 0xA0) /* Poll command */
+               ldrb    \irqnr, [r4, #0xa0]             @irq = (inb(0xA0) & 7) + 8
+               and     \irqstat, \irqnr, #0x80
+               teq     \irqstat, #0
+               beq     44f
+               and     \irqnr, \irqnr, #7
+               add     \irqnr, \irqnr, #8
+44:            teq     \irqstat, #0
+               .endm
+
diff --git a/include/asm-arm/arch-versatile/debug-macro.S b/include/asm-arm/arch-versatile/debug-macro.S
new file mode 100644 (file)
index 0000000..89e38ac
--- /dev/null
@@ -0,0 +1,39 @@
+/* linux/include/asm-arm/arch-versatile/debug-macro.S
+ *
+ * Debugging macro include header
+ *
+ *  Copyright (C) 1994-1999 Russell King
+ *  Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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 <asm/hardware/amba_serial.h>
+
+               .macro  addruart,rx
+               mrc     p15, 0, \rx, c1, c0
+               tst     \rx, #1                 @ MMU enabled?
+               moveq   \rx,      #0x10000000
+               movne   \rx,      #0xf1000000   @ virtual base
+               orr     \rx, \rx, #0x001F0000
+               orr     \rx, \rx, #0x00001000
+               .endm
+
+               .macro  senduart,rd,rx
+               strb    \rd, [\rx, #UART01x_DR]
+               .endm
+
+               .macro  waituart,rd,rx
+1001:          ldr     \rd, [\rx, #0x18]       @ UARTFLG
+               tst     \rd, #1 << 5            @ UARTFLGUTXFF - 1 when full
+               bne     1001b
+               .endm
+
+               .macro  busyuart,rd,rx
+1001:          ldr     \rd, [\rx, #0x18]       @ UARTFLG
+               tst     \rd, #1 << 3            @ UARTFLGUBUSY - 1 when busy
+               bne     1001b
+               .endm
diff --git a/include/asm-arm/arch-versatile/entry-macro.S b/include/asm-arm/arch-versatile/entry-macro.S
new file mode 100644 (file)
index 0000000..90e4e97
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * include/asm-arm/arch-versatile/entry-macro.S
+ *
+ * Low-level IRQ helper macros for Versatile platforms
+ *
+ * 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.
+ */
+                       .macro  disable_fiq
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               ldr     \base, =IO_ADDRESS(VERSATILE_VIC_BASE)
+               ldr     \irqstat, [\base, #VIC_IRQ_STATUS]      @ get masked status
+               mov     \irqnr, #0
+               teq     \irqstat, #0
+               beq     1003f
+
+1001:          tst     \irqstat, #15
+               bne     1002f
+               add     \irqnr, \irqnr, #4
+               movs    \irqstat, \irqstat, lsr #4
+               bne     1001b
+1002:          tst     \irqstat, #1
+               bne     1003f
+               add     \irqnr, \irqnr, #1
+               movs    \irqstat, \irqstat, lsr #1
+               bne     1002b
+1003:          /* EQ will be set if no irqs pending */
+
+@              clz     \irqnr, \irqstat
+@1003:         /* EQ will be set if we reach MAXIRQNUM */
+               .endm
+
diff --git a/include/asm-arm/cpu.h b/include/asm-arm/cpu.h
new file mode 100644 (file)
index 0000000..fcbdd40
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *  linux/include/asm-arm/cpu.h
+ *
+ *  Copyright (C) 2004-2005 ARM Ltd.
+ *
+ * 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.
+ */
+#ifndef __ASM_ARM_CPU_H
+#define __ASM_ARM_CPU_H
+
+#include <linux/config.h>
+#include <linux/percpu.h>
+
+struct cpuinfo_arm {
+       struct cpu      cpu;
+#ifdef CONFIG_SMP
+       unsigned int    loops_per_jiffy;
+#endif
+};
+
+DECLARE_PER_CPU(struct cpuinfo_arm, cpu_data);
+
+#endif
diff --git a/include/asm-arm/cputime.h b/include/asm-arm/cputime.h
new file mode 100644 (file)
index 0000000..3a8002a
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __ARM_CPUTIME_H
+#define __ARM_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __ARM_CPUTIME_H */
diff --git a/include/asm-arm/hardware/entry-macro-iomd.S b/include/asm-arm/hardware/entry-macro-iomd.S
new file mode 100644 (file)
index 0000000..30c7b92
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * arch/arm/commond/entry-macro-iomd.S
+ *
+ * Low-level IRQ helper macros for IOC/IOMD based platforms
+ *
+ * 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.
+ */
+
+/* IOC / IOMD based hardware */
+#include <asm/hardware/iomd.h>
+
+               .equ    ioc_base_high, IOC_BASE & 0xff000000
+               .equ    ioc_base_low, IOC_BASE & 0x00ff0000
+               .macro  disable_fiq
+               mov     r12, #ioc_base_high
+               .if     ioc_base_low
+               orr     r12, r12, #ioc_base_low
+               .endif
+               strb    r12, [r12, #0x38]       @ Disable FIQ register
+               .endm
+
+               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
+               mov     r4, #ioc_base_high              @ point at IOC
+               .if     ioc_base_low
+               orr     r4, r4, #ioc_base_low
+               .endif
+               ldrb    \irqstat, [r4, #IOMD_IRQREQB]   @ get high priority first
+               ldr     \base, =irq_prio_h
+               teq     \irqstat, #0
+#ifdef IOMD_BASE
+               ldreqb  \irqstat, [r4, #IOMD_DMAREQ]    @ get dma
+               addeq   \base, \base, #256              @ irq_prio_h table size
+               teqeq   \irqstat, #0
+               bne     2406f
+#endif
+               ldreqb  \irqstat, [r4, #IOMD_IRQREQA]   @ get low priority
+               addeq   \base, \base, #256              @ irq_prio_d table size
+               teqeq   \irqstat, #0
+#ifdef IOMD_IRQREQC
+               ldreqb  \irqstat, [r4, #IOMD_IRQREQC]
+               addeq   \base, \base, #256              @ irq_prio_l table size
+               teqeq   \irqstat, #0
+#endif
+#ifdef IOMD_IRQREQD
+               ldreqb  \irqstat, [r4, #IOMD_IRQREQD]
+               addeq   \base, \base, #256              @ irq_prio_lc table size
+               teqeq   \irqstat, #0
+#endif
+2406:          ldrneb  \irqnr, [\base, \irqstat]       @ get IRQ number
+               .endm
+
+/*
+ * Interrupt table (incorporates priority).  Please note that we
+ * rely on the order of these tables (see above code).
+ */
+               .align  5
+irq_prio_h:    .byte    0, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   12, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   14,14,14,14,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   15,15,15,15,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+               .byte   13,13,13,13,10,10,10,10,11,11,11,11,10,10,10,10
+#ifdef IOMD_BASE
+irq_prio_d:    .byte    0,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   20,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   21,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   21,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   22,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   22,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   21,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   21,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   23,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   23,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   21,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   21,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   22,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   22,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   21,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+               .byte   21,16,17,16,18,16,17,16,19,16,17,16,18,16,17,16
+#endif
+irq_prio_l:    .byte    0, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3
+               .byte    4, 0, 1, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3
+               .byte    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+               .byte    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+               .byte    6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3
+               .byte    6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3
+               .byte    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+               .byte    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5
+               .byte    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+               .byte    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+               .byte    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+               .byte    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+               .byte    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+               .byte    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+               .byte    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+               .byte    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+#ifdef IOMD_IRQREQC
+irq_prio_lc:   .byte   24,24,25,24,26,26,26,26,27,27,27,27,27,27,27,27
+               .byte   28,24,25,24,26,26,26,26,27,27,27,27,27,27,27,27
+               .byte   29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29
+               .byte   29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29
+               .byte   30,30,30,30,30,30,30,30,27,27,27,27,27,27,27,27
+               .byte   30,30,30,30,30,30,30,30,27,27,27,27,27,27,27,27
+               .byte   29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29
+               .byte   29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29
+               .byte   31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+               .byte   31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+               .byte   31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+               .byte   31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+               .byte   31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+               .byte   31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+               .byte   31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+               .byte   31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31
+#endif
+#ifdef IOMD_IRQREQD
+irq_prio_ld:   .byte   40,40,41,40,42,42,42,42,43,43,43,43,43,43,43,43
+               .byte   44,40,41,40,42,42,42,42,43,43,43,43,43,43,43,43
+               .byte   45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45
+               .byte   45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45
+               .byte   46,46,46,46,46,46,46,46,43,43,43,43,43,43,43,43
+               .byte   46,46,46,46,46,46,46,46,43,43,43,43,43,43,43,43
+               .byte   45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45
+               .byte   45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45
+               .byte   47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+               .byte   47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+               .byte   47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+               .byte   47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+               .byte   47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+               .byte   47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+               .byte   47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+               .byte   47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47
+#endif
+
diff --git a/include/asm-arm/hardware/icst307.h b/include/asm-arm/hardware/icst307.h
new file mode 100644 (file)
index 0000000..ff8618a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  linux/include/asm-arm/hardware/icst307.h
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, 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 version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Support functions for calculating clocks/divisors for the ICS307
+ *  clock generators.  See http://www.icst.com/ for more information
+ *  on these devices.
+ *
+ *  This file is similar to the icst525.h file
+ */
+#ifndef ASMARM_HARDWARE_ICST307_H
+#define ASMARM_HARDWARE_ICST307_H
+
+struct icst307_params {
+       unsigned long   ref;
+       unsigned long   vco_max;        /* inclusive */
+       unsigned short  vd_min;         /* inclusive */
+       unsigned short  vd_max;         /* inclusive */
+       unsigned char   rd_min;         /* inclusive */
+       unsigned char   rd_max;         /* inclusive */
+};
+
+struct icst307_vco {
+       unsigned short  v;
+       unsigned char   r;
+       unsigned char   s;
+};
+
+unsigned long icst307_khz(const struct icst307_params *p, struct icst307_vco vco);
+struct icst307_vco icst307_khz_to_vco(const struct icst307_params *p, unsigned long freq);
+struct icst307_vco icst307_ps_to_vco(const struct icst307_params *p, unsigned long period);
+
+#endif
diff --git a/include/asm-arm/hardware/scoop.h b/include/asm-arm/hardware/scoop.h
new file mode 100644 (file)
index 0000000..669b7df
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  Definitions for the SCOOP interface found on various Sharp PDAs
+ *
+ *  Copyright (c) 2004 Richard Purdie
+ *
+ *  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.
+ *
+ */
+
+#define SCOOP_MCR  0x00
+#define SCOOP_CDR  0x04
+#define SCOOP_CSR  0x08
+#define SCOOP_CPR  0x0C
+#define SCOOP_CCR  0x10
+#define SCOOP_IRR  0x14
+#define SCOOP_IRM  0x14
+#define SCOOP_IMR  0x18
+#define SCOOP_ISR  0x1C
+#define SCOOP_GPCR 0x20
+#define SCOOP_GPWR 0x24
+#define SCOOP_GPRR 0x28
+
+#define SCOOP_GPCR_PA22        ( 1 << 12 )
+#define SCOOP_GPCR_PA21        ( 1 << 11 )
+#define SCOOP_GPCR_PA20        ( 1 << 10 )
+#define SCOOP_GPCR_PA19        ( 1 << 9 )
+#define SCOOP_GPCR_PA18        ( 1 << 8 )
+#define SCOOP_GPCR_PA17        ( 1 << 7 )
+#define SCOOP_GPCR_PA16        ( 1 << 6 )
+#define SCOOP_GPCR_PA15        ( 1 << 5 )
+#define SCOOP_GPCR_PA14        ( 1 << 4 )
+#define SCOOP_GPCR_PA13        ( 1 << 3 )
+#define SCOOP_GPCR_PA12        ( 1 << 2 )
+#define SCOOP_GPCR_PA11        ( 1 << 1 )
+
+struct scoop_config {
+       unsigned short io_out;
+       unsigned short io_dir;
+};
+
+void reset_scoop(void);
+unsigned short set_scoop_gpio(unsigned short bit);
+unsigned short reset_scoop_gpio(unsigned short bit);
+unsigned short read_scoop_reg(unsigned short reg);
+void write_scoop_reg(unsigned short reg, unsigned short data);
diff --git a/include/asm-arm/mach/irda.h b/include/asm-arm/mach/irda.h
new file mode 100644 (file)
index 0000000..58984d9
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ *  linux/include/asm-arm/mach/irda.h
+ *
+ *  Copyright (C) 2004 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.
+ */
+#ifndef __ASM_ARM_MACH_IRDA_H
+#define __ASM_ARM_MACH_IRDA_H
+
+struct irda_platform_data {
+       int (*startup)(struct device *);
+       void (*shutdown)(struct device *);
+       int (*set_power)(struct device *, unsigned int state);
+       void (*set_speed)(struct device *, unsigned int speed);
+};
+
+#endif
diff --git a/include/asm-arm/rtc.h b/include/asm-arm/rtc.h
new file mode 100644 (file)
index 0000000..c3e330d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *  linux/include/asm-arm/rtc.h
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions Ltd.
+ *
+ * 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.
+ */
+#ifndef ASMARM_RTC_H
+#define ASMARM_RTC_H
+
+struct module;
+
+struct rtc_ops {
+       struct module   *owner;
+       int             (*open)(void);
+       void            (*release)(void);
+       int             (*ioctl)(unsigned int, unsigned long);
+
+       void            (*read_time)(struct rtc_time *);
+       int             (*set_time)(struct rtc_time *);
+       void            (*read_alarm)(struct rtc_wkalrm *);
+       int             (*set_alarm)(struct rtc_wkalrm *);
+       int             (*proc)(char *buf);
+};
+
+void rtc_time_to_tm(unsigned long, struct rtc_time *);
+int rtc_tm_to_time(struct rtc_time *, unsigned long *);
+void rtc_next_alarm_time(struct rtc_time *, struct rtc_time *, struct rtc_time *);
+void rtc_update(unsigned long, unsigned long);
+int register_rtc(struct rtc_ops *);
+void unregister_rtc(struct rtc_ops *);
+
+static inline int rtc_periodic_alarm(struct rtc_time *tm)
+{
+       return  (tm->tm_year == -1) ||
+               ((unsigned)tm->tm_mon >= 12) ||
+               ((unsigned)(tm->tm_mday - 1) >= 31) ||
+               ((unsigned)tm->tm_hour > 23) ||
+               ((unsigned)tm->tm_min > 59) ||
+               ((unsigned)tm->tm_sec > 59);
+}
+
+#endif
diff --git a/include/asm-arm26/cputime.h b/include/asm-arm26/cputime.h
new file mode 100644 (file)
index 0000000..d2783a9
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __ARM26_CPUTIME_H
+#define __ARM26_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __ARM26_CPUTIME_H */
diff --git a/include/asm-arm26/dma-mapping.h b/include/asm-arm26/dma-mapping.h
new file mode 100644 (file)
index 0000000..a95eae0
--- /dev/null
@@ -0,0 +1,2 @@
+#include <asm-generic/dma-mapping-broken.h>
+
diff --git a/include/asm-cris/cputime.h b/include/asm-cris/cputime.h
new file mode 100644 (file)
index 0000000..4446a65
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __CRIS_CPUTIME_H
+#define __CRIS_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __CRIS_CPUTIME_H */
diff --git a/include/asm-frv/a.out.h b/include/asm-frv/a.out.h
new file mode 100644 (file)
index 0000000..dd3b7e5
--- /dev/null
@@ -0,0 +1,5 @@
+/*
+ * FRV doesn't do AOUT format. This header file should be removed as
+ * soon as fs/exec.c and fs/proc/kcore.c and the archs that require
+ * them to include linux/a.out.h are fixed.
+ */
diff --git a/include/asm-frv/atomic.h b/include/asm-frv/atomic.h
new file mode 100644 (file)
index 0000000..e759684
--- /dev/null
@@ -0,0 +1,417 @@
+/* atomic.h: atomic operation emulation for FR-V
+ *
+ * For an explanation of how atomic ops work in this arch, see:
+ *   Documentation/fujitsu/frv/atomic-ops.txt
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ASM_ATOMIC_H
+#define _ASM_ATOMIC_H
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <asm/spr-regs.h>
+
+#ifdef CONFIG_SMP
+#error not SMP safe
+#endif
+
+/*
+ * Atomic operations that C can't guarantee us.  Useful for
+ * resource counting etc..
+ *
+ * We do not have SMP systems, so we don't have to deal with that.
+ */
+
+/* Atomic operations are already serializing */
+#define smp_mb__before_atomic_dec()    barrier()
+#define smp_mb__after_atomic_dec()     barrier()
+#define smp_mb__before_atomic_inc()    barrier()
+#define smp_mb__after_atomic_inc()     barrier()
+
+typedef struct {
+       int counter;
+} atomic_t;
+
+#define ATOMIC_INIT(i)         { (i) }
+#define atomic_read(v)         ((v)->counter)
+#define atomic_set(v, i)       (((v)->counter) = (i))
+
+#ifndef CONFIG_FRV_OUTOFLINE_ATOMIC_OPS
+static inline int atomic_add_return(int i, atomic_t *v)
+{
+       unsigned long val;
+
+       asm("0:                                         \n"
+           "   orcc            gr0,gr0,gr0,icc3        \n"     /* set ICC3.Z */
+           "   ckeq            icc3,cc7                \n"
+           "   ld.p            %M0,%1                  \n"     /* LD.P/ORCR must be atomic */
+           "   orcr            cc7,cc7,cc3             \n"     /* set CC3 to true */
+           "   add%I2          %1,%2,%1                \n"
+           "   cst.p           %1,%M0          ,cc3,#1 \n"
+           "   corcc           gr29,gr29,gr0   ,cc3,#1 \n"     /* clear ICC3.Z if store happens */
+           "   beq             icc3,#0,0b              \n"
+           : "+U"(v->counter), "=&r"(val)
+           : "NPr"(i)
+           : "memory", "cc7", "cc3", "icc3"
+           );
+
+       return val;
+}
+
+static inline int atomic_sub_return(int i, atomic_t *v)
+{
+       unsigned long val;
+
+       asm("0:                                         \n"
+           "   orcc            gr0,gr0,gr0,icc3        \n"     /* set ICC3.Z */
+           "   ckeq            icc3,cc7                \n"
+           "   ld.p            %M0,%1                  \n"     /* LD.P/ORCR must be atomic */
+           "   orcr            cc7,cc7,cc3             \n"     /* set CC3 to true */
+           "   sub%I2          %1,%2,%1                \n"
+           "   cst.p           %1,%M0          ,cc3,#1 \n"
+           "   corcc           gr29,gr29,gr0   ,cc3,#1 \n"     /* clear ICC3.Z if store happens */
+           "   beq             icc3,#0,0b              \n"
+           : "+U"(v->counter), "=&r"(val)
+           : "NPr"(i)
+           : "memory", "cc7", "cc3", "icc3"
+           );
+
+       return val;
+}
+
+#else
+
+extern int atomic_add_return(int i, atomic_t *v);
+extern int atomic_sub_return(int i, atomic_t *v);
+
+#endif
+
+static inline int atomic_add_negative(int i, atomic_t *v)
+{
+       return atomic_add_return(i, v) < 0;
+}
+
+static inline void atomic_add(int i, atomic_t *v)
+{
+       atomic_add_return(i, v);
+}
+
+static inline void atomic_sub(int i, atomic_t *v)
+{
+       atomic_sub_return(i, v);
+}
+
+static inline void atomic_inc(atomic_t *v)
+{
+       atomic_add_return(1, v);
+}
+
+static inline void atomic_dec(atomic_t *v)
+{
+       atomic_sub_return(1, v);
+}
+
+#define atomic_dec_return(v)           atomic_sub_return(1, (v))
+#define atomic_inc_return(v)           atomic_add_return(1, (v))
+
+#define atomic_sub_and_test(i,v)       (atomic_sub_return((i), (v)) == 0)
+#define atomic_dec_and_test(v)         (atomic_sub_return(1, (v)) == 0)
+#define atomic_inc_and_test(v)         (atomic_add_return(1, (v)) == 0)
+
+#ifndef CONFIG_FRV_OUTOFLINE_ATOMIC_OPS
+static inline
+unsigned long atomic_test_and_ANDNOT_mask(unsigned long mask, volatile unsigned long *v)
+{
+       unsigned long old, tmp;
+
+       asm volatile(
+               "0:                                             \n"
+               "       orcc            gr0,gr0,gr0,icc3        \n"     /* set ICC3.Z */
+               "       ckeq            icc3,cc7                \n"
+               "       ld.p            %M0,%1                  \n"     /* LD.P/ORCR are atomic */
+               "       orcr            cc7,cc7,cc3             \n"     /* set CC3 to true */
+               "       and%I3          %1,%3,%2                \n"
+               "       cst.p           %2,%M0          ,cc3,#1 \n"     /* if store happens... */
+               "       corcc           gr29,gr29,gr0   ,cc3,#1 \n"     /* ... clear ICC3.Z */
+               "       beq             icc3,#0,0b              \n"
+               : "+U"(*v), "=&r"(old), "=r"(tmp)
+               : "NPr"(~mask)
+               : "memory", "cc7", "cc3", "icc3"
+               );
+
+       return old;
+}
+
+static inline
+unsigned long atomic_test_and_OR_mask(unsigned long mask, volatile unsigned long *v)
+{
+       unsigned long old, tmp;
+
+       asm volatile(
+               "0:                                             \n"
+               "       orcc            gr0,gr0,gr0,icc3        \n"     /* set ICC3.Z */
+               "       ckeq            icc3,cc7                \n"
+               "       ld.p            %M0,%1                  \n"     /* LD.P/ORCR are atomic */
+               "       orcr            cc7,cc7,cc3             \n"     /* set CC3 to true */
+               "       or%I3           %1,%3,%2                \n"
+               "       cst.p           %2,%M0          ,cc3,#1 \n"     /* if store happens... */
+               "       corcc           gr29,gr29,gr0   ,cc3,#1 \n"     /* ... clear ICC3.Z */
+               "       beq             icc3,#0,0b              \n"
+               : "+U"(*v), "=&r"(old), "=r"(tmp)
+               : "NPr"(mask)
+               : "memory", "cc7", "cc3", "icc3"
+               );
+
+       return old;
+}
+
+static inline
+unsigned long atomic_test_and_XOR_mask(unsigned long mask, volatile unsigned long *v)
+{
+       unsigned long old, tmp;
+
+       asm volatile(
+               "0:                                             \n"
+               "       orcc            gr0,gr0,gr0,icc3        \n"     /* set ICC3.Z */
+               "       ckeq            icc3,cc7                \n"
+               "       ld.p            %M0,%1                  \n"     /* LD.P/ORCR are atomic */
+               "       orcr            cc7,cc7,cc3             \n"     /* set CC3 to true */
+               "       xor%I3          %1,%3,%2                \n"
+               "       cst.p           %2,%M0          ,cc3,#1 \n"     /* if store happens... */
+               "       corcc           gr29,gr29,gr0   ,cc3,#1 \n"     /* ... clear ICC3.Z */
+               "       beq             icc3,#0,0b              \n"
+               : "+U"(*v), "=&r"(old), "=r"(tmp)
+               : "NPr"(mask)
+               : "memory", "cc7", "cc3", "icc3"
+               );
+
+       return old;
+}
+
+#else
+
+extern unsigned long atomic_test_and_ANDNOT_mask(unsigned long mask, volatile unsigned long *v);
+extern unsigned long atomic_test_and_OR_mask(unsigned long mask, volatile unsigned long *v);
+extern unsigned long atomic_test_and_XOR_mask(unsigned long mask, volatile unsigned long *v);
+
+#endif
+
+#define atomic_clear_mask(mask, v)     atomic_test_and_ANDNOT_mask((mask), (v))
+#define atomic_set_mask(mask, v)       atomic_test_and_OR_mask((mask), (v))
+
+/*****************************************************************************/
+/*
+ * exchange value with memory
+ */
+#ifndef CONFIG_FRV_OUTOFLINE_ATOMIC_OPS
+
+#define xchg(ptr, x)                                                           \
+({                                                                             \
+       __typeof__(ptr) __xg_ptr = (ptr);                                       \
+       __typeof__(*(ptr)) __xg_orig;                                           \
+                                                                               \
+       switch (sizeof(__xg_orig)) {                                            \
+       case 1:                                                                 \
+               asm volatile(                                                   \
+                       "0:                                             \n"     \
+                       "       orcc            gr0,gr0,gr0,icc3        \n"     \
+                       "       ckeq            icc3,cc7                \n"     \
+                       "       ldub.p          %M0,%1                  \n"     \
+                       "       orcr            cc7,cc7,cc3             \n"     \
+                       "       cstb.p          %2,%M0          ,cc3,#1 \n"     \
+                       "       corcc           gr29,gr29,gr0   ,cc3,#1 \n"     \
+                       "       beq             icc3,#0,0b              \n"     \
+                       : "+U"(*__xg_ptr), "=&r"(__xg_orig)                     \
+                       : "r"(x)                                                \
+                       : "memory", "cc7", "cc3", "icc3"                        \
+                       );                                                      \
+               break;                                                          \
+                                                                               \
+       case 2:                                                                 \
+               asm volatile(                                                   \
+                       "0:                                             \n"     \
+                       "       orcc            gr0,gr0,gr0,icc3        \n"     \
+                       "       ckeq            icc3,cc7                \n"     \
+                       "       lduh.p          %M0,%1                  \n"     \
+                       "       orcr            cc7,cc7,cc3             \n"     \
+                       "       csth.p          %2,%M0          ,cc3,#1 \n"     \
+                       "       corcc           gr29,gr29,gr0   ,cc3,#1 \n"     \
+                       "       beq             icc3,#0,0b              \n"     \
+                       : "+U"(*__xg_ptr), "=&r"(__xg_orig)                     \
+                       : "r"(x)                                                \
+                       : "memory", "cc7", "cc3", "icc3"                        \
+                       );                                                      \
+               break;                                                          \
+                                                                               \
+       case 4:                                                                 \
+               asm volatile(                                                   \
+                       "0:                                             \n"     \
+                       "       orcc            gr0,gr0,gr0,icc3        \n"     \
+                       "       ckeq            icc3,cc7                \n"     \
+                       "       ld.p            %M0,%1                  \n"     \
+                       "       orcr            cc7,cc7,cc3             \n"     \
+                       "       cst.p           %2,%M0          ,cc3,#1 \n"     \
+                       "       corcc           gr29,gr29,gr0   ,cc3,#1 \n"     \
+                       "       beq             icc3,#0,0b              \n"     \
+                       : "+U"(*__xg_ptr), "=&r"(__xg_orig)                     \
+                       : "r"(x)                                                \
+                       : "memory", "cc7", "cc3", "icc3"                        \
+                       );                                                      \
+               break;                                                          \
+                                                                               \
+       default:                                                                \
+               __xg_orig = 0;                                                  \
+               asm volatile("break");                                          \
+               break;                                                          \
+       }                                                                       \
+                                                                               \
+       __xg_orig;                                                              \
+})
+
+#else
+
+extern uint8_t  __xchg_8 (uint8_t i,  volatile void *v);
+extern uint16_t __xchg_16(uint16_t i, volatile void *v);
+extern uint32_t __xchg_32(uint32_t i, volatile void *v);
+
+#define xchg(ptr, x)                                                                           \
+({                                                                                             \
+       __typeof__(ptr) __xg_ptr = (ptr);                                                       \
+       __typeof__(*(ptr)) __xg_orig;                                                           \
+                                                                                               \
+       switch (sizeof(__xg_orig)) {                                                            \
+       case 1: __xg_orig = (__typeof__(*(ptr))) __xchg_8 ((uint8_t)  x, __xg_ptr);     break;  \
+       case 2: __xg_orig = (__typeof__(*(ptr))) __xchg_16((uint16_t) x, __xg_ptr);     break;  \
+       case 4: __xg_orig = (__typeof__(*(ptr))) __xchg_32((uint32_t) x, __xg_ptr);     break;  \
+       default:                                                                                \
+               __xg_orig = 0;                                                                  \
+               asm volatile("break");                                                          \
+               break;                                                                          \
+       }                                                                                       \
+       __xg_orig;                                                                              \
+})
+
+#endif
+
+#define tas(ptr) (xchg((ptr), 1))
+
+/*****************************************************************************/
+/*
+ * compare and conditionally exchange value with memory
+ * - if (*ptr == test) then orig = *ptr; *ptr = test;
+ * - if (*ptr != test) then orig = *ptr;
+ */
+#ifndef CONFIG_FRV_OUTOFLINE_ATOMIC_OPS
+
+#define cmpxchg(ptr, test, new)                                                        \
+({                                                                             \
+       __typeof__(ptr) __xg_ptr = (ptr);                                       \
+       __typeof__(*(ptr)) __xg_orig, __xg_tmp;                                 \
+       __typeof__(*(ptr)) __xg_test = (test);                                  \
+       __typeof__(*(ptr)) __xg_new = (new);                                    \
+                                                                               \
+       switch (sizeof(__xg_orig)) {                                            \
+       case 1:                                                                 \
+               asm volatile(                                                   \
+                       "0:                                             \n"     \
+                       "       orcc            gr0,gr0,gr0,icc3        \n"     \
+                       "       ckeq            icc3,cc7                \n"     \
+                       "       ldub.p          %M0,%1                  \n"     \
+                       "       orcr            cc7,cc7,cc3             \n"     \
+                       "       sub%I4          %1,%4,%2                \n"     \
+                       "       sllcc           %2,#24,gr0,icc0         \n"     \
+                       "       bne             icc0,#0,1f              \n"     \
+                       "       cstb.p          %3,%M0          ,cc3,#1 \n"     \
+                       "       corcc           gr29,gr29,gr0   ,cc3,#1 \n"     \
+                       "       beq             icc3,#0,0b              \n"     \
+                       "1:                                             \n"     \
+                       : "+U"(*__xg_ptr), "=&r"(__xg_orig), "=&r"(__xg_tmp)    \
+                       : "r"(__xg_new), "NPr"(__xg_test)                       \
+                       : "memory", "cc7", "cc3", "icc3", "icc0"                \
+                       );                                                      \
+               break;                                                          \
+                                                                               \
+       case 2:                                                                 \
+               asm volatile(                                                   \
+                       "0:                                             \n"     \
+                       "       orcc            gr0,gr0,gr0,icc3        \n"     \
+                       "       ckeq            icc3,cc7                \n"     \
+                       "       lduh.p          %M0,%1                  \n"     \
+                       "       orcr            cc7,cc7,cc3             \n"     \
+                       "       sub%I4          %1,%4,%2                \n"     \
+                       "       sllcc           %2,#16,gr0,icc0         \n"     \
+                       "       bne             icc0,#0,1f              \n"     \
+                       "       csth.p          %3,%M0          ,cc3,#1 \n"     \
+                       "       corcc           gr29,gr29,gr0   ,cc3,#1 \n"     \
+                       "       beq             icc3,#0,0b              \n"     \
+                       "1:                                             \n"     \
+                       : "+U"(*__xg_ptr), "=&r"(__xg_orig), "=&r"(__xg_tmp)    \
+                       : "r"(__xg_new), "NPr"(__xg_test)                       \
+                       : "memory", "cc7", "cc3", "icc3", "icc0"                \
+                       );                                                      \
+               break;                                                          \
+                                                                               \
+       case 4:                                                                 \
+               asm volatile(                                                   \
+                       "0:                                             \n"     \
+                       "       orcc            gr0,gr0,gr0,icc3        \n"     \
+                       "       ckeq            icc3,cc7                \n"     \
+                       "       ld.p            %M0,%1                  \n"     \
+                       "       orcr            cc7,cc7,cc3             \n"     \
+                       "       sub%I4cc        %1,%4,%2,icc0           \n"     \
+                       "       bne             icc0,#0,1f              \n"     \
+                       "       cst.p           %3,%M0          ,cc3,#1 \n"     \
+                       "       corcc           gr29,gr29,gr0   ,cc3,#1 \n"     \
+                       "       beq             icc3,#0,0b              \n"     \
+                       "1:                                             \n"     \
+                       : "+U"(*__xg_ptr), "=&r"(__xg_orig), "=&r"(__xg_tmp)    \
+                       : "r"(__xg_new), "NPr"(__xg_test)                       \
+                       : "memory", "cc7", "cc3", "icc3", "icc0"                \
+                       );                                                      \
+               break;                                                          \
+                                                                               \
+       default:                                                                \
+               __xg_orig = 0;                                                  \
+               asm volatile("break");                                          \
+               break;                                                          \
+       }                                                                       \
+                                                                               \
+       __xg_orig;                                                              \
+})
+
+#else
+
+extern uint8_t  __cmpxchg_8 (uint8_t *v,  uint8_t test,  uint8_t new);
+extern uint16_t __cmpxchg_16(uint16_t *v, uint16_t test, uint16_t new);
+extern uint32_t __cmpxchg_32(uint32_t *v, uint32_t test, uint32_t new);
+
+#define cmpxchg(ptr, test, new)                                                        \
+({                                                                             \
+       __typeof__(ptr) __xg_ptr = (ptr);                                       \
+       __typeof__(*(ptr)) __xg_orig;                                           \
+       __typeof__(*(ptr)) __xg_test = (test);                                  \
+       __typeof__(*(ptr)) __xg_new = (new);                                    \
+                                                                               \
+       switch (sizeof(__xg_orig)) {                                            \
+       case 1: __xg_orig = __cmpxchg_8 (__xg_ptr, __xg_test, __xg_new); break; \
+       case 2: __xg_orig = __cmpxchg_16(__xg_ptr, __xg_test, __xg_new); break; \
+       case 4: __xg_orig = __cmpxchg_32(__xg_ptr, __xg_test, __xg_new); break; \
+       default:                                                                \
+               __xg_orig = 0;                                                  \
+               asm volatile("break");                                          \
+               break;                                                          \
+       }                                                                       \
+                                                                               \
+       __xg_orig;                                                              \
+})
+
+#endif
+
+#endif /* _ASM_ATOMIC_H */
diff --git a/include/asm-frv/ax88796.h b/include/asm-frv/ax88796.h
new file mode 100644 (file)
index 0000000..637e980
--- /dev/null
@@ -0,0 +1,22 @@
+/* ax88796.h: access points to the driver for the AX88796 NE2000 clone
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_AX88796_H
+#define _ASM_AX88796_H
+
+#include <asm/mb-regs.h>
+
+#define AX88796_IOADDR         (__region_CS1 + 0x200)
+#define AX88796_IRQ            IRQ_CPU_EXTERNAL7
+#define AX88796_FULL_DUPLEX    0                       /* force full duplex */
+#define AX88796_BUS_INFO       "CS1#+0x200"            /* bus info for ethtool */
+
+#endif /* _ASM_AX88796_H */
diff --git a/include/asm-frv/bitops.h b/include/asm-frv/bitops.h
new file mode 100644 (file)
index 0000000..b664bd5
--- /dev/null
@@ -0,0 +1,341 @@
+/* bitops.h: bit operations for the Fujitsu FR-V CPUs
+ *
+ * For an explanation of how atomic ops work in this arch, see:
+ *   Documentation/fujitsu/frv/atomic-ops.txt
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ASM_BITOPS_H
+#define _ASM_BITOPS_H
+
+#include <linux/config.h>
+#include <linux/compiler.h>
+#include <asm/byteorder.h>
+#include <asm/system.h>
+#include <asm/atomic.h>
+
+#ifdef __KERNEL__
+
+/*
+ * ffz = Find First Zero in word. Undefined if no zero exists,
+ * so code should check against ~0UL first..
+ */
+static inline unsigned long ffz(unsigned long word)
+{
+       unsigned long result = 0;
+
+       while (word & 1) {
+               result++;
+               word >>= 1;
+       }
+       return result;
+}
+
+/*
+ * clear_bit() doesn't provide any barrier for the compiler.
+ */
+#define smp_mb__before_clear_bit()     barrier()
+#define smp_mb__after_clear_bit()      barrier()
+
+static inline int test_and_clear_bit(int nr, volatile void *addr)
+{
+       volatile unsigned long *ptr = addr;
+       unsigned long mask = 1UL << (nr & 31);
+       ptr += nr >> 5;
+       return (atomic_test_and_ANDNOT_mask(mask, ptr) & mask) != 0;
+}
+
+static inline int test_and_set_bit(int nr, volatile void *addr)
+{
+       volatile unsigned long *ptr = addr;
+       unsigned long mask = 1UL << (nr & 31);
+       ptr += nr >> 5;
+       return (atomic_test_and_OR_mask(mask, ptr) & mask) != 0;
+}
+
+static inline int test_and_change_bit(int nr, volatile void *addr)
+{
+       volatile unsigned long *ptr = addr;
+       unsigned long mask = 1UL << (nr & 31);
+       ptr += nr >> 5;
+       return (atomic_test_and_XOR_mask(mask, ptr) & mask) != 0;
+}
+
+static inline void clear_bit(int nr, volatile void *addr)
+{
+       test_and_clear_bit(nr, addr);
+}
+
+static inline void set_bit(int nr, volatile void *addr)
+{
+       test_and_set_bit(nr, addr);
+}
+
+static inline void change_bit(int nr, volatile void * addr)
+{
+       test_and_change_bit(nr, addr);
+}
+
+static inline void __clear_bit(int nr, volatile void * addr)
+{
+       volatile unsigned long *a = addr;
+       int mask;
+
+       a += nr >> 5;
+       mask = 1 << (nr & 31);
+       *a &= ~mask;
+}
+
+static inline void __set_bit(int nr, volatile void * addr)
+{
+       volatile unsigned long *a = addr;
+       int mask;
+
+       a += nr >> 5;
+       mask = 1 << (nr & 31);
+       *a |= mask;
+}
+
+static inline void __change_bit(int nr, volatile void *addr)
+{
+       volatile unsigned long *a = addr;
+       int mask;
+
+       a += nr >> 5;
+       mask = 1 << (nr & 31);
+       *a ^= mask;
+}
+
+static inline int __test_and_clear_bit(int nr, volatile void * addr)
+{
+       volatile unsigned long *a = addr;
+       int mask, retval;
+
+       a += nr >> 5;
+       mask = 1 << (nr & 31);
+       retval = (mask & *a) != 0;
+       *a &= ~mask;
+       return retval;
+}
+
+static inline int __test_and_set_bit(int nr, volatile void * addr)
+{
+       volatile unsigned long *a = addr;
+       int mask, retval;
+
+       a += nr >> 5;
+       mask = 1 << (nr & 31);
+       retval = (mask & *a) != 0;
+       *a |= mask;
+       return retval;
+}
+
+static inline int __test_and_change_bit(int nr, volatile void * addr)
+{
+       volatile unsigned long *a = addr;
+       int mask, retval;
+
+       a += nr >> 5;
+       mask = 1 << (nr & 31);
+       retval = (mask & *a) != 0;
+       *a ^= mask;
+       return retval;
+}
+
+/*
+ * This routine doesn't need to be atomic.
+ */
+static inline int __constant_test_bit(int nr, const volatile void * addr)
+{
+       return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0;
+}
+
+static inline int __test_bit(int nr, const volatile void * addr)
+{
+       int     * a = (int *) addr;
+       int     mask;
+
+       a += nr >> 5;
+       mask = 1 << (nr & 0x1f);
+       return ((mask & *a) != 0);
+}
+
+#define test_bit(nr,addr) \
+(__builtin_constant_p(nr) ? \
+ __constant_test_bit((nr),(addr)) : \
+ __test_bit((nr),(addr)))
+
+extern int find_next_bit(const unsigned long *addr, int size, int offset);
+
+#define find_first_bit(addr, size) find_next_bit(addr, size, 0)
+
+#define find_first_zero_bit(addr, size) \
+        find_next_zero_bit((addr), (size), 0)
+
+static inline int find_next_zero_bit(const void *addr, int size, int offset)
+{
+       const unsigned long *p = ((const unsigned long *) addr) + (offset >> 5);
+       unsigned long result = offset & ~31UL;
+       unsigned long tmp;
+
+       if (offset >= size)
+               return size;
+       size -= result;
+       offset &= 31UL;
+       if (offset) {
+               tmp = *(p++);
+               tmp |= ~0UL >> (32-offset);
+               if (size < 32)
+                       goto found_first;
+               if (~tmp)
+                       goto found_middle;
+               size -= 32;
+               result += 32;
+       }
+       while (size & ~31UL) {
+               if (~(tmp = *(p++)))
+                       goto found_middle;
+               result += 32;
+               size -= 32;
+       }
+       if (!size)
+               return result;
+       tmp = *p;
+
+found_first:
+       tmp |= ~0UL >> size;
+found_middle:
+       return result + ffz(tmp);
+}
+
+#define ffs(x) generic_ffs(x)
+#define __ffs(x) (ffs(x) - 1)
+
+/*
+ * fls: find last bit set.
+ */
+#define fls(x)                                         \
+({                                                     \
+       int bit;                                        \
+                                                       \
+       asm("scan %1,gr0,%0" : "=r"(bit) : "r"(x));     \
+                                                       \
+       bit ? 33 - bit : bit;                           \
+})
+
+/*
+ * Every architecture must define this function. It's the fastest
+ * way of searching a 140-bit bitmap where the first 100 bits are
+ * unlikely to be set. It's guaranteed that at least one of the 140
+ * bits is cleared.
+ */
+static inline int sched_find_first_bit(const unsigned long *b)
+{
+       if (unlikely(b[0]))
+               return __ffs(b[0]);
+       if (unlikely(b[1]))
+               return __ffs(b[1]) + 32;
+       if (unlikely(b[2]))
+               return __ffs(b[2]) + 64;
+       if (b[3])
+               return __ffs(b[3]) + 96;
+       return __ffs(b[4]) + 128;
+}
+
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+#define hweight32(x) generic_hweight32(x)
+#define hweight16(x) generic_hweight16(x)
+#define hweight8(x) generic_hweight8(x)
+
+#define ext2_set_bit(nr, addr)         test_and_set_bit  ((nr) ^ 0x18, (addr))
+#define ext2_clear_bit(nr, addr)       test_and_clear_bit((nr) ^ 0x18, (addr))
+
+#define ext2_set_bit_atomic(lock,nr,addr)      ext2_set_bit((nr), addr)
+#define ext2_clear_bit_atomic(lock,nr,addr)    ext2_clear_bit((nr), addr)
+
+static inline int ext2_test_bit(int nr, const volatile void * addr)
+{
+       const volatile unsigned char *ADDR = (const unsigned char *) addr;
+       int mask;
+
+       ADDR += nr >> 3;
+       mask = 1 << (nr & 0x07);
+       return ((mask & *ADDR) != 0);
+}
+
+#define ext2_find_first_zero_bit(addr, size) \
+        ext2_find_next_zero_bit((addr), (size), 0)
+
+static inline unsigned long ext2_find_next_zero_bit(const void *addr,
+                                                   unsigned long size,
+                                                   unsigned long offset)
+{
+       const unsigned long *p = ((const unsigned long *) addr) + (offset >> 5);
+       unsigned long result = offset & ~31UL;
+       unsigned long tmp;
+
+       if (offset >= size)
+               return size;
+       size -= result;
+       offset &= 31UL;
+       if(offset) {
+               /* We hold the little endian value in tmp, but then the
+                * shift is illegal. So we could keep a big endian value
+                * in tmp, like this:
+                *
+                * tmp = __swab32(*(p++));
+                * tmp |= ~0UL >> (32-offset);
+                *
+                * but this would decrease preformance, so we change the
+                * shift:
+                */
+               tmp = *(p++);
+               tmp |= __swab32(~0UL >> (32-offset));
+               if(size < 32)
+                       goto found_first;
+               if(~tmp)
+                       goto found_middle;
+               size -= 32;
+               result += 32;
+       }
+       while(size & ~31UL) {
+               if(~(tmp = *(p++)))
+                       goto found_middle;
+               result += 32;
+               size -= 32;
+       }
+       if(!size)
+               return result;
+       tmp = *p;
+
+found_first:
+       /* tmp is little endian, so we would have to swab the shift,
+        * see above. But then we have to swab tmp below for ffz, so
+        * we might as well do this here.
+        */
+       return result + ffz(__swab32(tmp) | (~0UL << size));
+found_middle:
+       return result + ffz(__swab32(tmp));
+}
+
+/* Bitmap functions for the minix filesystem.  */
+#define minix_test_and_set_bit(nr,addr)                ext2_set_bit(nr,addr)
+#define minix_set_bit(nr,addr)                 ext2_set_bit(nr,addr)
+#define minix_test_and_clear_bit(nr,addr)      ext2_clear_bit(nr,addr)
+#define minix_test_bit(nr,addr)                        ext2_test_bit(nr,addr)
+#define minix_find_first_zero_bit(addr,size)   ext2_find_first_zero_bit(addr,size)
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_BITOPS_H */
diff --git a/include/asm-frv/bug.h b/include/asm-frv/bug.h
new file mode 100644 (file)
index 0000000..011860b
--- /dev/null
@@ -0,0 +1,51 @@
+/* bug.h: FRV bug trapping
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ASM_BUG_H
+#define _ASM_BUG_H
+
+#include <linux/config.h>
+
+/*
+ * Tell the user there is some problem.
+ */
+extern asmlinkage void __debug_bug_trap(int signr);
+
+#ifdef CONFIG_NO_KERNEL_MSG
+#define        _debug_bug_printk()
+#else
+extern void __debug_bug_printk(const char *file, unsigned line);
+#define        _debug_bug_printk() __debug_bug_printk(__FILE__, __LINE__)
+#endif
+
+#define _debug_bug_trap(signr)                 \
+do {                                           \
+       __debug_bug_trap(signr);                \
+       asm volatile("nop");                    \
+} while(0)
+
+#define HAVE_ARCH_BUG
+#define BUG()                                  \
+do {                                           \
+       _debug_bug_printk();                    \
+       _debug_bug_trap(6 /*SIGABRT*/);         \
+} while (0)
+
+#ifdef CONFIG_GDBSTUB
+#define HAVE_ARCH_KGDB_RAISE
+#define kgdb_raise(signr) do { _debug_bug_trap(signr); } while(0)
+
+#define HAVE_ARCH_KGDB_BAD_PAGE
+#define kgdb_bad_page(page) do { kgdb_raise(SIGABRT); } while(0)
+#endif
+
+#include <asm-generic/bug.h>
+
+#endif
diff --git a/include/asm-frv/bugs.h b/include/asm-frv/bugs.h
new file mode 100644 (file)
index 0000000..f2382be
--- /dev/null
@@ -0,0 +1,14 @@
+/* bugs.h: arch bug checking entry
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+static inline void check_bugs(void)
+{
+}
diff --git a/include/asm-frv/busctl-regs.h b/include/asm-frv/busctl-regs.h
new file mode 100644 (file)
index 0000000..bb0ff48
--- /dev/null
@@ -0,0 +1,41 @@
+/* busctl-regs.h: FR400-series CPU bus controller registers
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_BUSCTL_REGS_H
+#define _ASM_BUSCTL_REGS_H
+
+/* bus controller registers */
+#define __get_LGCR()   ({ *(volatile unsigned long *)(0xfe000010); })
+#define __get_LMAICR() ({ *(volatile unsigned long *)(0xfe000030); })
+#define __get_LEMBR()  ({ *(volatile unsigned long *)(0xfe000040); })
+#define __get_LEMAM()  ({ *(volatile unsigned long *)(0xfe000048); })
+#define __get_LCR(R)   ({ *(volatile unsigned long *)(0xfe000100 + 8*(R)); })
+#define __get_LSBR(R)  ({ *(volatile unsigned long *)(0xfe000c00 + 8*(R)); })
+#define __get_LSAM(R)  ({ *(volatile unsigned long *)(0xfe000d00 + 8*(R)); })
+
+#define __set_LGCR(V)  do { *(volatile unsigned long *)(0xfe000010) = (V); } while(0)
+#define __set_LMAICR(V)        do { *(volatile unsigned long *)(0xfe000030) = (V); } while(0)
+#define __set_LEMBR(V) do { *(volatile unsigned long *)(0xfe000040) = (V); } while(0)
+#define __set_LEMAM(V) do { *(volatile unsigned long *)(0xfe000048) = (V); } while(0)
+#define __set_LCR(R,V) do { *(volatile unsigned long *)(0xfe000100 + 8*(R)) = (V); } while(0)
+#define __set_LSBR(R,V)        do { *(volatile unsigned long *)(0xfe000c00 + 8*(R)) = (V); } while(0)
+#define __set_LSAM(R,V)        do { *(volatile unsigned long *)(0xfe000d00 + 8*(R)) = (V); } while(0)
+
+/* FR401 SDRAM controller registers */
+#define __get_DBR(R)   ({ *(volatile unsigned long *)(0xfe000e00 + 8*(R)); })
+#define __get_DAM(R)   ({ *(volatile unsigned long *)(0xfe000f00 + 8*(R)); })
+
+/* FR551 SDRAM controller registers */
+#define __get_DARS(R)  ({ *(volatile unsigned long *)(0xfeff0100 + 8*(R)); })
+#define __get_DAMK(R)  ({ *(volatile unsigned long *)(0xfeff0110 + 8*(R)); })
+
+
+#endif /* _ASM_BUSCTL_REGS_H */
diff --git a/include/asm-frv/byteorder.h b/include/asm-frv/byteorder.h
new file mode 100644 (file)
index 0000000..411bec3
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _ASM_BYTEORDER_H
+#define _ASM_BYTEORDER_H
+
+#include <asm/types.h>
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__)
+#  define __BYTEORDER_HAS_U64__
+#  define __SWAB_64_THRU_32__
+#endif
+
+#include <linux/byteorder/big_endian.h>
+
+#endif /* _ASM_BYTEORDER_H */
diff --git a/include/asm-frv/cache.h b/include/asm-frv/cache.h
new file mode 100644 (file)
index 0000000..cf69b63
--- /dev/null
@@ -0,0 +1,24 @@
+/* cache.h: FRV cache definitions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef __ASM_CACHE_H
+#define __ASM_CACHE_H
+
+#include <linux/config.h>
+
+/* bytes per L1 cache line */
+#define L1_CACHE_SHIFT         (CONFIG_FRV_L1_CACHE_SHIFT)
+#define L1_CACHE_BYTES         (1 << L1_CACHE_SHIFT)
+
+#define __cacheline_aligned    __attribute__((aligned(L1_CACHE_BYTES)))
+#define ____cacheline_aligned  __attribute__((aligned(L1_CACHE_BYTES)))
+
+#endif
diff --git a/include/asm-frv/cacheflush.h b/include/asm-frv/cacheflush.h
new file mode 100644 (file)
index 0000000..85adcf7
--- /dev/null
@@ -0,0 +1,91 @@
+/* cacheflush.h: FRV cache flushing routines
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_CACHEFLUSH_H
+#define _ASM_CACHEFLUSH_H
+
+/* Keep includes the same across arches.  */
+#include <linux/mm.h>
+
+/*
+ * virtually-indexed cache management (our cache is physically indexed)
+ */
+#define flush_cache_all()                      do {} while(0)
+#define flush_cache_mm(mm)                     do {} while(0)
+#define flush_cache_range(mm, start, end)      do {} while(0)
+#define flush_cache_page(vma, vmaddr)          do {} while(0)
+#define flush_cache_vmap(start, end)           do {} while(0)
+#define flush_cache_vunmap(start, end)         do {} while(0)
+#define flush_dcache_mmap_lock(mapping)                do {} while(0)
+#define flush_dcache_mmap_unlock(mapping)      do {} while(0)
+
+/*
+ * physically-indexed cache managment
+ * - see arch/frv/lib/cache.S
+ */
+extern void frv_dcache_writeback(unsigned long start, unsigned long size);
+extern void frv_cache_invalidate(unsigned long start, unsigned long size);
+extern void frv_icache_invalidate(unsigned long start, unsigned long size);
+extern void frv_cache_wback_inv(unsigned long start, unsigned long size);
+
+static inline void __flush_cache_all(void)
+{
+       asm volatile("  dcef    @(gr0,gr0),#1   \n"
+                    "  icei    @(gr0,gr0),#1   \n"
+                    "  membar                  \n"
+                    : : : "memory"
+                    );
+}
+
+/* dcache/icache coherency... */
+#ifdef CONFIG_MMU
+extern void flush_dcache_page(struct page *page);
+#else
+static inline void flush_dcache_page(struct page *page)
+{
+       unsigned long addr = page_to_phys(page);
+       frv_dcache_writeback(addr, addr + PAGE_SIZE);
+}
+#endif
+
+static inline void flush_page_to_ram(struct page *page)
+{
+       flush_dcache_page(page);
+}
+
+static inline void flush_icache(void)
+{
+       __flush_cache_all();
+}
+
+static inline void flush_icache_range(unsigned long start, unsigned long end)
+{
+       frv_cache_wback_inv(start, end);
+}
+
+#ifdef CONFIG_MMU
+extern void flush_icache_user_range(struct vm_area_struct *vma, struct page *page,
+                                   unsigned long start, unsigned long len);
+#else
+static inline void flush_icache_user_range(struct vm_area_struct *vma, struct page *page,
+                                          unsigned long start, unsigned long len)
+{
+       frv_cache_wback_inv(start, start + len);
+}
+#endif
+
+static inline void flush_icache_page(struct vm_area_struct *vma, struct page *page)
+{
+       flush_icache_user_range(vma, page, page_to_phys(page), PAGE_SIZE);
+}
+
+
+#endif /* _ASM_CACHEFLUSH_H */
diff --git a/include/asm-frv/checksum.h b/include/asm-frv/checksum.h
new file mode 100644 (file)
index 0000000..10236f6
--- /dev/null
@@ -0,0 +1,183 @@
+/* checksum.h: FRV checksumming
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_CHECKSUM_H
+#define _ASM_CHECKSUM_H
+
+#include <linux/in6.h>
+
+/*
+ * computes the checksum of a memory block at buff, length len,
+ * and adds in "sum" (32-bit)
+ *
+ * returns a 32-bit number suitable for feeding into itself
+ * or csum_tcpudp_magic
+ *
+ * this function must be called with even lengths, except
+ * for the last fragment, which may be odd
+ *
+ * it's best to have buff aligned on a 32-bit boundary
+ */
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum);
+
+/*
+ * the same as csum_partial, but copies from src while it
+ * checksums
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+unsigned int csum_partial_copy(const char *src, char *dst, int len, int sum);
+
+/*
+ * the same as csum_partial_copy, but copies from user space.
+ *
+ * here even more important to align src and dst on a 32-bit (or even
+ * better 64-bit) boundary
+ */
+extern unsigned int csum_partial_copy_from_user(const char *src, char *dst,
+                                               int len, int sum, int *csum_err);
+
+#define csum_partial_copy_nocheck(src, dst, len, sum)  \
+       csum_partial_copy((src), (dst), (len), (sum))
+
+/*
+ *     This is a version of ip_compute_csum() optimized for IP headers,
+ *     which always checksum on 4 octet boundaries.
+ *
+ */
+static inline
+unsigned short ip_fast_csum(unsigned char *iph, unsigned int ihl)
+{
+       unsigned int tmp, inc, sum = 0;
+
+       asm("   addcc           gr0,gr0,gr0,icc0\n" /* clear icc0.C */
+           "   subi            %1,#4,%1        \n"
+           "0:                                 \n"
+           "   ldu.p           @(%1,%3),%4     \n"
+           "   subicc          %2,#1,%2,icc1   \n"
+           "   addxcc.p        %4,%0,%0,icc0   \n"
+           "   bhi             icc1,#2,0b      \n"
+
+           /* fold the 33-bit result into 16-bits */
+           "   addxcc          gr0,%0,%0,icc0  \n"
+           "   srli            %0,#16,%1       \n"
+           "   sethi           #0,%0           \n"
+           "   add             %1,%0,%0        \n"
+           "   srli            %0,#16,%1       \n"
+           "   add             %1,%0,%0        \n"
+
+           : "=r" (sum), "=r" (iph), "=r" (ihl), "=r" (inc), "=&r"(tmp)
+           : "0" (sum), "1" (iph), "2" (ihl), "3" (4),
+           "m"(*(volatile struct { int _[100]; } *)iph)
+           : "icc0", "icc1"
+           );
+
+       return ~sum;
+}
+
+/*
+ *     Fold a partial checksum
+ */
+static inline unsigned int csum_fold(unsigned int sum)
+{
+       unsigned int tmp;
+
+       asm("   srli            %0,#16,%1       \n"
+           "   sethi           #0,%0           \n"
+           "   add             %1,%0,%0        \n"
+           "   srli            %0,#16,%1       \n"
+           "   add             %1,%0,%0        \n"
+           : "=r"(sum), "=&r"(tmp)
+           : "0"(sum)
+           );
+
+       return ~sum;
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+static inline unsigned int
+csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr, unsigned short len,
+                 unsigned short proto, unsigned int sum)
+{
+       asm("   addcc           %1,%0,%0,icc0   \n"
+           "   addxcc          %2,%0,%0,icc0   \n"
+           "   addxcc          %3,%0,%0,icc0   \n"
+           "   addxcc          gr0,%0,%0,icc0  \n"
+           : "=r" (sum)
+           : "r" (daddr), "r" (saddr), "r" (len + proto), "0"(sum)
+           : "icc0"
+           );
+       return sum;
+}
+
+static inline unsigned short int
+csum_tcpudp_magic(unsigned long saddr, unsigned long daddr, unsigned short len,
+                 unsigned short proto, unsigned int sum)
+{
+       return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum));
+}
+
+/*
+ * this routine is used for miscellaneous IP-like checksums, mainly
+ * in icmp.c
+ */
+extern unsigned short ip_compute_csum(const unsigned char * buff, int len);
+
+#define _HAVE_ARCH_IPV6_CSUM
+static inline unsigned short int
+csum_ipv6_magic(struct in6_addr *saddr, struct in6_addr *daddr,
+               __u32 len, unsigned short proto, unsigned int sum)
+{
+       unsigned long tmp, tmp2;
+
+       asm("   addcc           %2,%0,%0,icc0   \n"
+
+           /* add up the source addr */
+           "   ldi             @(%3,0),%1      \n"
+           "   addxcc          %1,%0,%0,icc0   \n"
+           "   ldi             @(%3,4),%2      \n"
+           "   addxcc          %2,%0,%0,icc0   \n"
+           "   ldi             @(%3,8),%1      \n"
+           "   addxcc          %1,%0,%0,icc0   \n"
+           "   ldi             @(%3,12),%2     \n"
+           "   addxcc          %2,%0,%0,icc0   \n"
+
+           /* add up the dest addr */
+           "   ldi             @(%4,0),%1      \n"
+           "   addxcc          %1,%0,%0,icc0   \n"
+           "   ldi             @(%4,4),%2      \n"
+           "   addxcc          %2,%0,%0,icc0   \n"
+           "   ldi             @(%4,8),%1      \n"
+           "   addxcc          %1,%0,%0,icc0   \n"
+           "   ldi             @(%4,12),%2     \n"
+           "   addxcc          %2,%0,%0,icc0   \n"
+
+           /* fold the 33-bit result into 16-bits */
+           "   addxcc          gr0,%0,%0,icc0  \n"
+           "   srli            %0,#16,%1       \n"
+           "   sethi           #0,%0           \n"
+           "   add             %1,%0,%0        \n"
+           "   srli            %0,#16,%1       \n"
+           "   add             %1,%0,%0        \n"
+
+           : "=r" (sum), "=&r" (tmp), "=r" (tmp2)
+           : "r" (saddr), "r" (daddr), "0" (sum), "2" (len + proto)
+           : "icc0"
+           );
+
+       return ~sum;
+}
+
+#endif /* _ASM_CHECKSUM_H */
diff --git a/include/asm-frv/cpu-irqs.h b/include/asm-frv/cpu-irqs.h
new file mode 100644 (file)
index 0000000..5cd691e
--- /dev/null
@@ -0,0 +1,87 @@
+/* cpu-irqs.h: on-CPU peripheral irqs
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_CPU_IRQS_H
+#define _ASM_CPU_IRQS_H
+
+#ifndef __ASSEMBLY__
+
+#include <asm/irq-routing.h>
+
+#define IRQ_BASE_CPU           (NR_IRQ_ACTIONS_PER_GROUP * 0)
+
+/* IRQ IDs presented to drivers */
+enum {
+       IRQ_CPU__UNUSED = IRQ_BASE_CPU,
+       IRQ_CPU_UART0,
+       IRQ_CPU_UART1,
+       IRQ_CPU_TIMER0,
+       IRQ_CPU_TIMER1,
+       IRQ_CPU_TIMER2,
+       IRQ_CPU_DMA0,
+       IRQ_CPU_DMA1,
+       IRQ_CPU_DMA2,
+       IRQ_CPU_DMA3,
+       IRQ_CPU_DMA4,
+       IRQ_CPU_DMA5,
+       IRQ_CPU_DMA6,
+       IRQ_CPU_DMA7,
+       IRQ_CPU_EXTERNAL0,
+       IRQ_CPU_EXTERNAL1,
+       IRQ_CPU_EXTERNAL2,
+       IRQ_CPU_EXTERNAL3,
+       IRQ_CPU_EXTERNAL4,
+       IRQ_CPU_EXTERNAL5,
+       IRQ_CPU_EXTERNAL6,
+       IRQ_CPU_EXTERNAL7,
+};
+
+/* IRQ to level mappings */
+#define IRQ_GDBSTUB_LEVEL      15
+#define IRQ_UART_LEVEL         13
+
+#ifdef CONFIG_GDBSTUB_UART0
+#define IRQ_UART0_LEVEL                IRQ_GDBSTUB_LEVEL
+#else
+#define IRQ_UART0_LEVEL                IRQ_UART_LEVEL
+#endif
+
+#ifdef CONFIG_GDBSTUB_UART1
+#define IRQ_UART1_LEVEL                IRQ_GDBSTUB_LEVEL
+#else
+#define IRQ_UART1_LEVEL                IRQ_UART_LEVEL
+#endif
+
+#define IRQ_DMA0_LEVEL         14
+#define IRQ_DMA1_LEVEL         14
+#define IRQ_DMA2_LEVEL         14
+#define IRQ_DMA3_LEVEL         14
+#define IRQ_DMA4_LEVEL         14
+#define IRQ_DMA5_LEVEL         14
+#define IRQ_DMA6_LEVEL         14
+#define IRQ_DMA7_LEVEL         14
+
+#define IRQ_TIMER0_LEVEL       12
+#define IRQ_TIMER1_LEVEL       11
+#define IRQ_TIMER2_LEVEL       10
+
+#define IRQ_XIRQ0_LEVEL                1
+#define IRQ_XIRQ1_LEVEL                2
+#define IRQ_XIRQ2_LEVEL                3
+#define IRQ_XIRQ3_LEVEL                4
+#define IRQ_XIRQ4_LEVEL                5
+#define IRQ_XIRQ5_LEVEL                6
+#define IRQ_XIRQ6_LEVEL                7
+#define IRQ_XIRQ7_LEVEL                8
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* _ASM_CPU_IRQS_H */
diff --git a/include/asm-frv/cpumask.h b/include/asm-frv/cpumask.h
new file mode 100644 (file)
index 0000000..d999c20
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _ASM_CPUMASK_H
+#define _ASM_CPUMASK_H
+
+#include <asm-generic/cpumask.h>
+
+#endif /* _ASM_CPUMASK_H */
diff --git a/include/asm-frv/cputime.h b/include/asm-frv/cputime.h
new file mode 100644 (file)
index 0000000..f6c373a
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _ASM_CPUTIME_H
+#define _ASM_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* _ASM_CPUTIME_H */
diff --git a/include/asm-frv/current.h b/include/asm-frv/current.h
new file mode 100644 (file)
index 0000000..86b0274
--- /dev/null
@@ -0,0 +1,30 @@
+/* current.h: FRV current task pointer
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_CURRENT_H
+#define _ASM_CURRENT_H
+
+#ifndef __ASSEMBLY__
+
+/*
+ * dedicate GR29 to keeping the current task pointer
+ */
+register struct task_struct *current asm("gr29");
+
+#define get_current() current
+
+#else
+
+#define CURRENT gr29
+
+#endif
+
+#endif /* _ASM_CURRENT_H */
diff --git a/include/asm-frv/delay.h b/include/asm-frv/delay.h
new file mode 100644 (file)
index 0000000..597b4eb
--- /dev/null
@@ -0,0 +1,50 @@
+/* delay.h: FRV delay code
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_DELAY_H
+#define _ASM_DELAY_H
+
+#include <asm/param.h>
+#include <asm/timer-regs.h>
+
+/*
+ * delay loop - runs at __core_clock_speed_HZ / 2 [there are 2 insns in the loop]
+ */
+extern unsigned long __delay_loops_MHz;
+
+static inline void __delay(unsigned long loops)
+{
+       asm volatile("1:        subicc  %0,#1,%0,icc0   \n"
+                    "          bnc     icc0,#2,1b      \n"
+                    : "=r" (loops)
+                    : "0" (loops)
+                    : "icc0"
+                    );
+}
+
+/*
+ * Use only for very small delays ( < 1 msec).  Should probably use a
+ * lookup table, really, as the multiplications take much too long with
+ * short delays.  This is a "reasonable" implementation, though (and the
+ * first constant multiplications gets optimized away if the delay is
+ * a constant)
+ */
+
+extern unsigned long loops_per_jiffy;
+
+static inline void udelay(unsigned long usecs)
+{
+       __delay(usecs * __delay_loops_MHz);
+}
+
+#define ndelay(n)      udelay((n) * 5)
+
+#endif /* _ASM_DELAY_H */
diff --git a/include/asm-frv/div64.h b/include/asm-frv/div64.h
new file mode 100644 (file)
index 0000000..6cd978c
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/div64.h>
diff --git a/include/asm-frv/dm9000.h b/include/asm-frv/dm9000.h
new file mode 100644 (file)
index 0000000..f6f48fd
--- /dev/null
@@ -0,0 +1,37 @@
+/* dm9000.h: Davicom DM9000 adapter configuration
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_DM9000_H
+#define _ASM_DM9000_H
+
+#include <asm/mb-regs.h>
+
+#define DM9000_ARCH_IOBASE     (__region_CS6 + 0x300)
+#define DM9000_ARCH_IRQ                IRQ_CPU_EXTERNAL3       /* XIRQ #3 (shared with FPGA) */
+#undef DM9000_ARCH_IRQ_ACTLOW                          /* IRQ pin active high */
+#define DM9000_ARCH_BUS_INFO   "CS6#+0x300"            /* bus info for ethtool */
+
+#undef __is_PCI_IO
+#define __is_PCI_IO(addr)      0       /* not PCI */
+
+#undef inl
+#define inl(addr)                                                                              \
+({                                                                                             \
+       unsigned long __ioaddr = (unsigned long) addr;                                          \
+       uint32_t x = readl(__ioaddr);                                                           \
+       ((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x >> 8) & 0xff00) | ((x >> 24) & 0xff);    \
+})
+
+#undef insl
+#define insl(a,b,l)    __insl(a,b,l,0) /* don't byte-swap */
+
+
+#endif /* _ASM_DM9000_H */
diff --git a/include/asm-frv/dma-mapping.h b/include/asm-frv/dma-mapping.h
new file mode 100644 (file)
index 0000000..0206ab3
--- /dev/null
@@ -0,0 +1,184 @@
+#ifndef _ASM_DMA_MAPPING_H
+#define _ASM_DMA_MAPPING_H
+
+#include <linux/device.h>
+#include <asm/cache.h>
+#include <asm/cacheflush.h>
+#include <asm/scatterlist.h>
+#include <asm/io.h>
+
+#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
+#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
+
+extern unsigned long __nongprelbss dma_coherent_mem_start;
+extern unsigned long __nongprelbss dma_coherent_mem_end;
+
+void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int gfp);
+void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle);
+
+/*
+ * These macros should be used after a pci_map_sg call has been done
+ * to get bus addresses of each of the SG entries and their lengths.
+ * You should only work with the number of sg entries pci_map_sg
+ * returns, or alternatively stop on the first sg_dma_len(sg) which
+ * is 0.
+ */
+#define sg_dma_address(sg)     ((unsigned long) (page_to_phys((sg)->page) + (sg)->offset))
+#define sg_dma_len(sg)         ((sg)->length)
+
+/*
+ * Map a single buffer of the indicated size for DMA in streaming mode.
+ * The 32-bit bus address to use is returned.
+ *
+ * Once the device is given the dma address, the device owns this memory
+ * until either pci_unmap_single or pci_dma_sync_single is performed.
+ */
+extern dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
+                                enum dma_data_direction direction);
+
+/*
+ * Unmap a single streaming mode DMA translation.  The dma_addr and size
+ * must match what was provided for in a previous pci_map_single call.  All
+ * other usages are undefined.
+ *
+ * After this call, reads by the cpu to the buffer are guarenteed to see
+ * whatever the device wrote there.
+ */
+static inline
+void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+                     enum dma_data_direction direction)
+{
+       BUG_ON(direction == DMA_NONE);
+}
+
+/*
+ * Map a set of buffers described by scatterlist in streaming
+ * mode for DMA.  This is the scather-gather version of the
+ * above pci_map_single interface.  Here the scatter gather list
+ * elements are each tagged with the appropriate dma address
+ * and length.  They are obtained via sg_dma_{address,length}(SG).
+ *
+ * NOTE: An implementation may be able to use a smaller number of
+ *       DMA address/length pairs than there are SG table elements.
+ *       (for example via virtual mapping capabilities)
+ *       The routine returns the number of addr/length pairs actually
+ *       used, at most nents.
+ *
+ * Device ownership issues as mentioned above for pci_map_single are
+ * the same here.
+ */
+extern int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+                     enum dma_data_direction direction);
+
+/*
+ * Unmap a set of streaming mode DMA translations.
+ * Again, cpu read rules concerning calls here are the same as for
+ * pci_unmap_single() above.
+ */
+static inline
+void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries,
+            enum dma_data_direction direction)
+{
+       BUG_ON(direction == DMA_NONE);
+}
+
+extern
+dma_addr_t dma_map_page(struct device *dev, struct page *page, unsigned long offset,
+                       size_t size, enum dma_data_direction direction);
+
+static inline
+void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
+                   enum dma_data_direction direction)
+{
+       BUG_ON(direction == DMA_NONE);
+}
+
+
+static inline
+void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size,
+                            enum dma_data_direction direction)
+{
+}
+
+static inline
+void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size,
+                               enum dma_data_direction direction)
+{
+       flush_write_buffers();
+}
+
+static inline
+void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle,
+                                  unsigned long offset, size_t size,
+                                  enum dma_data_direction direction)
+{
+}
+
+static inline
+void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle,
+                                     unsigned long offset, size_t size,
+                                     enum dma_data_direction direction)
+{
+       flush_write_buffers();
+}
+
+static inline
+void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
+                        enum dma_data_direction direction)
+{
+}
+
+static inline
+void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems,
+                           enum dma_data_direction direction)
+{
+       flush_write_buffers();
+}
+
+static inline
+int dma_mapping_error(dma_addr_t dma_addr)
+{
+       return 0;
+}
+
+static inline
+int dma_supported(struct device *dev, u64 mask)
+{
+        /*
+         * we fall back to GFP_DMA when the mask isn't all 1s,
+         * so we can't guarantee allocations that must be
+         * within a tighter range than GFP_DMA..
+         */
+        if (mask < 0x00ffffff)
+                return 0;
+
+       return 1;
+}
+
+static inline
+int dma_set_mask(struct device *dev, u64 mask)
+{
+       if (!dev->dma_mask || !dma_supported(dev, mask))
+               return -EIO;
+
+       *dev->dma_mask = mask;
+
+       return 0;
+}
+
+static inline
+int dma_get_cache_alignment(void)
+{
+       return 1 << L1_CACHE_SHIFT;
+}
+
+#define dma_is_consistent(d)   (1)
+
+static inline
+void dma_cache_sync(void *vaddr, size_t size,
+                   enum dma_data_direction direction)
+{
+       flush_write_buffers();
+}
+
+#endif  /* _ASM_DMA_MAPPING_H */
diff --git a/include/asm-frv/dma.h b/include/asm-frv/dma.h
new file mode 100644 (file)
index 0000000..d8f9a2f
--- /dev/null
@@ -0,0 +1,129 @@
+/* dma.h: FRV DMA controller management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_DMA_H
+#define _ASM_DMA_H
+
+//#define DMA_DEBUG 1
+
+#include <linux/config.h>
+#include <linux/interrupt.h>
+
+#undef MAX_DMA_CHANNELS                /* don't use kernel/dma.c */
+
+/* under 2.4 this is actually needed by the new bootmem allocator */
+#define MAX_DMA_ADDRESS                PAGE_OFFSET
+
+/*
+ * FRV DMA controller management
+ */
+struct pt_regs;
+
+typedef irqreturn_t (*dma_irq_handler_t)(int dmachan, unsigned long cstr, void *data,
+                                        struct pt_regs *regs);
+
+extern void frv_dma_init(void);
+
+extern int frv_dma_open(const char *devname,
+                       unsigned long dmamask,
+                       int dmacap,
+                       dma_irq_handler_t handler,
+                       unsigned long irq_flags,
+                       void *data);
+
+/* channels required */
+#define FRV_DMA_MASK_ANY       ULONG_MAX       /* any channel */
+
+/* capabilities required */
+#define FRV_DMA_CAP_DREQ       0x01            /* DMA request pin */
+#define FRV_DMA_CAP_DACK       0x02            /* DMA ACK pin */
+#define FRV_DMA_CAP_DONE       0x04            /* DMA done pin */
+
+extern void frv_dma_close(int dma);
+
+extern void frv_dma_config(int dma, unsigned long ccfr, unsigned long cctr, unsigned long apr);
+
+extern void frv_dma_start(int dma,
+                         unsigned long sba, unsigned long dba,
+                         unsigned long pix, unsigned long six, unsigned long bcl);
+
+extern void frv_dma_restart_circular(int dma, unsigned long six);
+
+extern void frv_dma_stop(int dma);
+
+extern int is_frv_dma_interrupting(int dma);
+
+extern void frv_dma_dump(int dma);
+
+extern void frv_dma_status_clear(int dma);
+
+#define FRV_DMA_NCHANS 8
+#define FRV_DMA_4CHANS 4
+#define FRV_DMA_8CHANS 8
+
+#define DMAC_CCFRx             0x00    /* channel configuration reg */
+#define DMAC_CCFRx_CM_SHIFT    16
+#define DMAC_CCFRx_CM_DA       0x00000000
+#define DMAC_CCFRx_CM_SCA      0x00010000
+#define DMAC_CCFRx_CM_DCA      0x00020000
+#define DMAC_CCFRx_CM_2D       0x00030000
+#define DMAC_CCFRx_ATS_SHIFT   8
+#define DMAC_CCFRx_RS_INTERN   0x00000000
+#define DMAC_CCFRx_RS_EXTERN   0x00000001
+#define DMAC_CCFRx_RS_SHIFT    0
+
+#define DMAC_CSTRx             0x08    /* channel status reg */
+#define DMAC_CSTRx_FS          0x0000003f
+#define DMAC_CSTRx_NE          0x00000100
+#define DMAC_CSTRx_FED         0x00000200
+#define DMAC_CSTRx_WER         0x00000800
+#define DMAC_CSTRx_RER         0x00001000
+#define DMAC_CSTRx_CE          0x00002000
+#define DMAC_CSTRx_INT         0x00800000
+#define DMAC_CSTRx_BUSY                0x80000000
+
+#define DMAC_CCTRx             0x10    /* channel control reg */
+#define DMAC_CCTRx_DSIZ_1      0x00000000
+#define DMAC_CCTRx_DSIZ_2      0x00000001
+#define DMAC_CCTRx_DSIZ_4      0x00000002
+#define DMAC_CCTRx_DSIZ_32     0x00000005
+#define DMAC_CCTRx_DAU_HOLD    0x00000000
+#define DMAC_CCTRx_DAU_INC     0x00000010
+#define DMAC_CCTRx_DAU_DEC     0x00000020
+#define DMAC_CCTRx_SSIZ_1      0x00000000
+#define DMAC_CCTRx_SSIZ_2      0x00000100
+#define DMAC_CCTRx_SSIZ_4      0x00000200
+#define DMAC_CCTRx_SSIZ_32     0x00000500
+#define DMAC_CCTRx_SAU_HOLD    0x00000000
+#define DMAC_CCTRx_SAU_INC     0x00001000
+#define DMAC_CCTRx_SAU_DEC     0x00002000
+#define DMAC_CCTRx_FC          0x08000000
+#define DMAC_CCTRx_ICE         0x10000000
+#define DMAC_CCTRx_IE          0x40000000
+#define DMAC_CCTRx_ACT         0x80000000
+
+#define DMAC_SBAx              0x18    /* source base address reg */
+#define DMAC_DBAx              0x20    /* data base address reg */
+#define DMAC_PIXx              0x28    /* primary index reg */
+#define DMAC_SIXx              0x30    /* secondary index reg */
+#define DMAC_BCLx              0x38    /* byte count limit reg */
+#define DMAC_APRx              0x40    /* alternate pointer reg */
+
+/*
+ * required for PCI + MODULES
+ */
+#ifdef CONFIG_PCI
+extern int isa_dma_bridge_buggy;
+#else
+#define isa_dma_bridge_buggy   (0)
+#endif
+
+#endif /* _ASM_DMA_H */
diff --git a/include/asm-frv/elf.h b/include/asm-frv/elf.h
new file mode 100644 (file)
index 0000000..7d2098f
--- /dev/null
@@ -0,0 +1,147 @@
+/* elf.h: FR-V ELF definitions
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from include/asm-m68knommu/elf.h
+ *
+ * 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 __ASM_ELF_H
+#define __ASM_ELF_H
+
+#include <linux/config.h>
+#include <asm/ptrace.h>
+#include <asm/user.h>
+
+struct elf32_hdr;
+
+/*
+ * ELF header e_flags defines.
+ */
+#define EF_FRV_GPR_MASK         0x00000003 /* mask for # of gprs */
+#define EF_FRV_GPR32           0x00000001 /* Only uses GR on 32-register */
+#define EF_FRV_GPR64           0x00000002 /* Only uses GR on 64-register */
+#define EF_FRV_FPR_MASK         0x0000000c /* mask for # of fprs */
+#define EF_FRV_FPR32           0x00000004 /* Only uses FR on 32-register */
+#define EF_FRV_FPR64           0x00000008 /* Only uses FR on 64-register */
+#define EF_FRV_FPR_NONE                0x0000000C /* Uses software floating-point */
+#define EF_FRV_DWORD_MASK       0x00000030 /* mask for dword support */
+#define EF_FRV_DWORD_YES       0x00000010 /* Assumes stack aligned to 8-byte boundaries. */
+#define EF_FRV_DWORD_NO                0x00000020 /* Assumes stack aligned to 4-byte boundaries. */
+#define EF_FRV_DOUBLE          0x00000040 /* Uses double instructions. */
+#define EF_FRV_MEDIA           0x00000080 /* Uses media instructions. */
+#define EF_FRV_PIC             0x00000100 /* Uses position independent code. */
+#define EF_FRV_NON_PIC_RELOCS  0x00000200 /* Does not use position Independent code. */
+#define EF_FRV_MULADD           0x00000400 /* -mmuladd */
+#define EF_FRV_BIGPIC           0x00000800 /* -fPIC */
+#define EF_FRV_LIBPIC           0x00001000 /* -mlibrary-pic */
+#define EF_FRV_G0               0x00002000 /* -G 0, no small data ptr */
+#define EF_FRV_NOPACK           0x00004000 /* -mnopack */
+#define EF_FRV_FDPIC            0x00008000 /* -mfdpic */
+#define EF_FRV_CPU_MASK         0xff000000 /* specific cpu bits */
+#define EF_FRV_CPU_GENERIC     0x00000000 /* Set CPU type is FR-V */
+#define EF_FRV_CPU_FR500       0x01000000 /* Set CPU type is FR500 */
+#define EF_FRV_CPU_FR300       0x02000000 /* Set CPU type is FR300 */
+#define EF_FRV_CPU_SIMPLE       0x03000000 /* SIMPLE */
+#define EF_FRV_CPU_TOMCAT       0x04000000 /* Tomcat, FR500 prototype */
+#define EF_FRV_CPU_FR400       0x05000000 /* Set CPU type is FR400 */
+#define EF_FRV_CPU_FR550        0x06000000 /* Set CPU type is FR550 */
+#define EF_FRV_CPU_FR405       0x07000000 /* Set CPU type is FR405 */
+#define EF_FRV_CPU_FR450       0x08000000 /* Set CPU type is FR450 */
+
+/*
+ * FR-V ELF relocation types
+ */
+
+
+/*
+ * ELF register definitions..
+ */
+typedef unsigned long elf_greg_t;
+
+#define ELF_NGREG (sizeof(struct pt_regs) / sizeof(elf_greg_t))
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+
+typedef struct fpmedia_struct elf_fpregset_t;
+
+/*
+ * This is used to ensure we don't load something for the wrong architecture.
+ */
+extern int elf_check_arch(const struct elf32_hdr *hdr);
+
+#define elf_check_fdpic(x) ((x)->e_flags & EF_FRV_FDPIC && !((x)->e_flags & EF_FRV_NON_PIC_RELOCS))
+#define elf_check_const_displacement(x) ((x)->e_flags & EF_FRV_PIC)
+
+/*
+ * These are used to set parameters in the core dumps.
+ */
+#define ELF_CLASS      ELFCLASS32
+#define ELF_DATA       ELFDATA2MSB
+#define ELF_ARCH       EM_FRV
+
+#define ELF_PLAT_INIT(_r)                      \
+do {                                           \
+       __kernel_frame0_ptr->gr16       = 0;    \
+       __kernel_frame0_ptr->gr17       = 0;    \
+       __kernel_frame0_ptr->gr18       = 0;    \
+       __kernel_frame0_ptr->gr19       = 0;    \
+       __kernel_frame0_ptr->gr20       = 0;    \
+       __kernel_frame0_ptr->gr21       = 0;    \
+       __kernel_frame0_ptr->gr22       = 0;    \
+       __kernel_frame0_ptr->gr23       = 0;    \
+       __kernel_frame0_ptr->gr24       = 0;    \
+       __kernel_frame0_ptr->gr25       = 0;    \
+       __kernel_frame0_ptr->gr26       = 0;    \
+       __kernel_frame0_ptr->gr27       = 0;    \
+       __kernel_frame0_ptr->gr29       = 0;    \
+} while(0)
+
+#define ELF_FDPIC_PLAT_INIT(_regs, _exec_map_addr, _interp_map_addr, _dynamic_addr)    \
+do {                                                                                   \
+       __kernel_frame0_ptr->gr16       = _exec_map_addr;                               \
+       __kernel_frame0_ptr->gr17       = _interp_map_addr;                             \
+       __kernel_frame0_ptr->gr18       = _dynamic_addr;                                \
+       __kernel_frame0_ptr->gr19       = 0;                                            \
+       __kernel_frame0_ptr->gr20       = 0;                                            \
+       __kernel_frame0_ptr->gr21       = 0;                                            \
+       __kernel_frame0_ptr->gr22       = 0;                                            \
+       __kernel_frame0_ptr->gr23       = 0;                                            \
+       __kernel_frame0_ptr->gr24       = 0;                                            \
+       __kernel_frame0_ptr->gr25       = 0;                                            \
+       __kernel_frame0_ptr->gr26       = 0;                                            \
+       __kernel_frame0_ptr->gr27       = 0;                                            \
+       __kernel_frame0_ptr->gr29       = 0;                                            \
+} while(0)
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE      16384
+
+/* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
+   use of this is to invoke "./ld.so someprog" to test out a new version of
+   the loader.  We need to make sure that it is out of the way of the program
+   that it will "exec", and that there is sufficient room for the brk.  */
+
+#define ELF_ET_DYN_BASE         0x08000000UL
+
+#define ELF_CORE_COPY_REGS(pr_reg, regs)                               \
+       memcpy(&pr_reg[0], &regs->sp, 31 * sizeof(uint32_t));
+
+/* This yields a mask that user programs can use to figure out what
+   instruction set this cpu supports.  */
+
+#define ELF_HWCAP      (0)
+
+/* This yields a string that ld.so will use to load implementation
+   specific libraries for optimization.  This is more specific in
+   intent than poking at uname or /proc/cpuinfo.  */
+
+#define ELF_PLATFORM  (NULL)
+
+#ifdef __KERNEL__
+#define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX)
+#endif
+
+#endif
diff --git a/include/asm-frv/errno.h b/include/asm-frv/errno.h
new file mode 100644 (file)
index 0000000..d010795
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _ASM_ERRNO_H
+#define _ASM_ERRNO_H
+
+#include <asm-generic/errno.h>
+
+#endif /* _ASM_ERRNO_H */
+
diff --git a/include/asm-frv/fcntl.h b/include/asm-frv/fcntl.h
new file mode 100644 (file)
index 0000000..d61b999
--- /dev/null
@@ -0,0 +1,88 @@
+#ifndef _ASM_FCNTL_H
+#define _ASM_FCNTL_H
+
+/* open/fcntl - O_SYNC is only implemented on blocks devices and on files
+   located on an ext2 file system */
+#define O_ACCMODE         0003
+#define O_RDONLY            00
+#define O_WRONLY            01
+#define O_RDWR              02
+#define O_CREAT                   0100 /* not fcntl */
+#define O_EXCL            0200 /* not fcntl */
+#define O_NOCTTY          0400 /* not fcntl */
+#define O_TRUNC                  01000 /* not fcntl */
+#define O_APPEND         02000
+#define O_NONBLOCK       04000
+#define O_NDELAY       O_NONBLOCK
+#define O_SYNC          010000
+#define FASYNC          020000 /* fcntl, for BSD compatibility */
+#define O_DIRECT        040000 /* direct disk access hint */
+#define O_LARGEFILE    0100000
+#define O_DIRECTORY    0200000 /* must be a directory */
+#define O_NOFOLLOW     0400000 /* don't follow links */
+#define O_NOATIME      01000000
+
+#define F_DUPFD                0       /* dup */
+#define F_GETFD                1       /* get close_on_exec */
+#define F_SETFD                2       /* set/clear close_on_exec */
+#define F_GETFL                3       /* get file->f_flags */
+#define F_SETFL                4       /* set file->f_flags */
+#define F_GETLK                5
+#define F_SETLK                6
+#define F_SETLKW       7
+
+#define F_SETOWN       8       /*  for sockets. */
+#define F_GETOWN       9       /*  for sockets. */
+#define F_SETSIG       10      /*  for sockets. */
+#define F_GETSIG       11      /*  for sockets. */
+
+#define F_GETLK64      12      /*  using 'struct flock64' */
+#define F_SETLK64      13
+#define F_SETLKW64     14
+
+/* for F_[GET|SET]FL */
+#define FD_CLOEXEC     1       /* actually anything with low bit set goes */
+
+/* for posix fcntl() and lockf() */
+#define F_RDLCK                0
+#define F_WRLCK                1
+#define F_UNLCK                2
+
+/* for old implementation of bsd flock () */
+#define F_EXLCK                4       /* or 3 */
+#define F_SHLCK                8       /* or 4 */
+
+/* for leases */
+#define F_INPROGRESS   16
+
+/* operations for bsd flock(), also used by the kernel implementation */
+#define LOCK_SH                1       /* shared lock */
+#define LOCK_EX                2       /* exclusive lock */
+#define LOCK_NB                4       /* or'd with one of the above to prevent
+                                  blocking */
+#define LOCK_UN                8       /* remove lock */
+
+#define LOCK_MAND      32      /* This is a mandatory flock */
+#define LOCK_READ      64      /* ... Which allows concurrent read operations */
+#define LOCK_WRITE     128     /* ... Which allows concurrent write operations */
+#define LOCK_RW                192     /* ... Which allows concurrent read & write ops */
+
+struct flock {
+       short l_type;
+       short l_whence;
+       off_t l_start;
+       off_t l_len;
+       pid_t l_pid;
+};
+
+struct flock64 {
+       short  l_type;
+       short  l_whence;
+       loff_t l_start;
+       loff_t l_len;
+       pid_t  l_pid;
+};
+
+#define F_LINUX_SPECIFIC_BASE  1024
+#endif /* _ASM_FCNTL_H */
+
diff --git a/include/asm-frv/fpu.h b/include/asm-frv/fpu.h
new file mode 100644 (file)
index 0000000..b1178f8
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __ASM_FPU_H
+#define __ASM_FPU_H
+
+#include <linux/config.h>
+
+/*
+ * MAX floating point unit state size (FSAVE/FRESTORE)
+ */
+
+#define kernel_fpu_end() do { asm volatile("bar":::"memory"); preempt_enable(); } while(0)
+
+#endif /* __ASM_FPU_H */
diff --git a/include/asm-frv/gdb-stub.h b/include/asm-frv/gdb-stub.h
new file mode 100644 (file)
index 0000000..c58479a
--- /dev/null
@@ -0,0 +1,118 @@
+/* gdb-stub.h: FRV GDB stub
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from asm-mips/gdb-stub.h (c) 1995 Andreas Busse
+ *
+ * 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 __ASM_GDB_STUB_H
+#define __ASM_GDB_STUB_H
+
+#undef GDBSTUB_DEBUG_PROTOCOL
+
+#include <asm/ptrace.h>
+
+/*
+ * important register numbers in GDB protocol
+ * - GR0,  GR1,  GR2,  GR3,  GR4,  GR5,  GR6,  GR7,
+ * - GR8,  GR9,  GR10, GR11, GR12, GR13, GR14, GR15,
+ * - GR16, GR17, GR18, GR19, GR20, GR21, GR22, GR23,
+ * - GR24, GR25, GR26, GR27, GR28, GR29, GR30, GR31,
+ * - GR32, GR33, GR34, GR35, GR36, GR37, GR38, GR39,
+ * - GR40, GR41, GR42, GR43, GR44, GR45, GR46, GR47,
+ * - GR48, GR49, GR50, GR51, GR52, GR53, GR54, GR55,
+ * - GR56, GR57, GR58, GR59, GR60, GR61, GR62, GR63,
+ * - FR0,  FR1,  FR2,  FR3,  FR4,  FR5,  FR6,  FR7,
+ * - FR8,  FR9,  FR10, FR11, FR12, FR13, FR14, FR15,
+ * - FR16, FR17, FR18, FR19, FR20, FR21, FR22, FR23,
+ * - FR24, FR25, FR26, FR27, FR28, FR29, FR30, FR31,
+ * - FR32, FR33, FR34, FR35, FR36, FR37, FR38, FR39,
+ * - FR40, FR41, FR42, FR43, FR44, FR45, FR46, FR47,
+ * - FR48, FR49, FR50, FR51, FR52, FR53, FR54, FR55,
+ * - FR56, FR57, FR58, FR59, FR60, FR61, FR62, FR63,
+ * - PC, PSR, CCR, CCCR,
+ * - _X132, _X133, _X134
+ * - TBR, BRR, DBAR0, DBAR1, DBAR2, DBAR3,
+ * - SCR0, SCR1, SCR2, SCR3,
+ * - LR, LCR,
+ * - IACC0H, IACC0L,
+ * - FSR0,
+ * - ACC0, ACC1, ACC2, ACC3, ACC4, ACC5, ACC6, ACC7,
+ * - ACCG0123, ACCG4567,
+ * - MSR0, MSR1,
+ * - GNER0, GNER1,
+ * - FNER0, FNER1,
+ */
+#define GDB_REG_GR(N)  (N)
+#define GDB_REG_FR(N)  (64+(N))
+#define GDB_REG_PC     128
+#define GDB_REG_PSR    129
+#define GDB_REG_CCR    130
+#define GDB_REG_CCCR   131
+#define GDB_REG_TBR    135
+#define GDB_REG_BRR    136
+#define GDB_REG_DBAR(N)        (137+(N))
+#define GDB_REG_SCR(N) (141+(N))
+#define GDB_REG_LR     145
+#define GDB_REG_LCR    146
+#define GDB_REG_FSR0   149
+#define GDB_REG_ACC(N) (150+(N))
+#define GDB_REG_ACCG(N)        (158+(N)/4)
+#define GDB_REG_MSR(N) (160+(N))
+#define GDB_REG_GNER(N)        (162+(N))
+#define GDB_REG_FNER(N)        (164+(N))
+
+#define GDB_REG_SP     GDB_REG_GR(1)
+#define GDB_REG_FP     GDB_REG_GR(2)
+
+#ifndef _LANGUAGE_ASSEMBLY
+
+/*
+ * Prototypes
+ */
+extern void show_registers_only(struct pt_regs *regs);
+
+extern void gdbstub_init(void);
+extern void gdbstub(int type);
+extern void gdbstub_exit(int status);
+
+extern void gdbstub_io_init(void);
+extern void gdbstub_set_baud(unsigned baud);
+extern int gdbstub_rx_char(unsigned char *_ch, int nonblock);
+extern void gdbstub_tx_char(unsigned char ch);
+extern void gdbstub_tx_flush(void);
+extern void gdbstub_do_rx(void);
+
+extern asmlinkage void __debug_stub_init_break(void);
+extern asmlinkage void __break_hijack_kernel_event(void);
+extern asmlinkage void start_kernel(void);
+
+extern asmlinkage void gdbstub_rx_handler(void);
+extern asmlinkage void gdbstub_rx_irq(void);
+extern asmlinkage void gdbstub_intercept(void);
+
+extern uint32_t __entry_usertrap_table[];
+extern uint32_t __entry_kerneltrap_table[];
+
+extern volatile u8     gdbstub_rx_buffer[PAGE_SIZE];
+extern volatile u32    gdbstub_rx_inp;
+extern volatile u32    gdbstub_rx_outp;
+extern volatile u8     gdbstub_rx_overflow;
+extern u8              gdbstub_rx_unget;
+
+extern void gdbstub_printk(const char *fmt, ...);
+extern void debug_to_serial(const char *p, int n);
+extern void console_set_baud(unsigned baud);
+
+#ifdef GDBSTUB_DEBUG_PROTOCOL
+#define gdbstub_proto(FMT,...) gdbstub_printk(FMT,##__VA_ARGS__)
+#else
+#define gdbstub_proto(FMT,...) ({ 0; })
+#endif
+
+#endif /* _LANGUAGE_ASSEMBLY */
+#endif /* __ASM_GDB_STUB_H */
diff --git a/include/asm-frv/gpio-regs.h b/include/asm-frv/gpio-regs.h
new file mode 100644 (file)
index 0000000..9edf5d5
--- /dev/null
@@ -0,0 +1,116 @@
+/* gpio-regs.h: on-chip general purpose I/O registers
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_GPIO_REGS
+#define _ASM_GPIO_REGS
+
+#define __reg(ADDR) (*(volatile unsigned long *)(ADDR))
+
+#define __get_PDR()    ({ __reg(0xfeff0400); })
+#define __set_PDR(V)   do { __reg(0xfeff0400) = (V); mb(); } while(0)
+
+#define __get_GPDR()   ({ __reg(0xfeff0408); })
+#define __set_GPDR(V)  do { __reg(0xfeff0408) = (V); mb(); } while(0)
+
+#define __get_SIR()    ({ __reg(0xfeff0410); })
+#define __set_SIR(V)   do { __reg(0xfeff0410) = (V); mb(); } while(0)
+
+#define __get_SOR()    ({ __reg(0xfeff0418); })
+#define __set_SOR(V)   do { __reg(0xfeff0418) = (V); mb(); } while(0)
+
+#define __set_PDSR(V)  do { __reg(0xfeff0420) = (V); mb(); } while(0)
+
+#define __set_PDCR(V)  do { __reg(0xfeff0428) = (V); mb(); } while(0)
+
+#define __get_RSTR()   ({ __reg(0xfeff0500); })
+#define __set_RSTR(V)  do { __reg(0xfeff0500) = (V); mb(); } while(0)
+
+
+
+/* PDR definitions */
+#define PDR_GPIO_DATA(X)       (1 << (X))
+
+/* GPDR definitions */
+#define GPDR_INPUT             0
+#define GPDR_OUTPUT            1
+#define GPDR_DREQ0_BIT         0x00001000
+#define GPDR_DREQ1_BIT         0x00008000
+#define GPDR_DREQ2_BIT         0x00040000
+#define GPDR_DREQ3_BIT         0x00080000
+#define GPDR_DREQ4_BIT         0x00004000
+#define GPDR_DREQ5_BIT         0x00020000
+#define GPDR_DREQ6_BIT         0x00100000
+#define GPDR_DREQ7_BIT         0x00200000
+#define GPDR_DACK0_BIT         0x00002000
+#define GPDR_DACK1_BIT         0x00010000
+#define GPDR_DACK2_BIT         0x00100000
+#define GPDR_DACK3_BIT         0x00200000
+#define GPDR_DONE0_BIT         0x00004000
+#define GPDR_DONE1_BIT         0x00020000
+#define GPDR_GPIO_DIR(X,D)     ((D) << (X))
+
+/* SIR definitions */
+#define SIR_GPIO_INPUT         0
+#define SIR_DREQ7_INPUT                0x00200000
+#define SIR_DREQ6_INPUT                0x00100000
+#define SIR_DREQ3_INPUT                0x00080000
+#define SIR_DREQ2_INPUT                0x00040000
+#define SIR_DREQ5_INPUT                0x00020000
+#define SIR_DREQ1_INPUT                0x00008000
+#define SIR_DREQ4_INPUT                0x00004000
+#define SIR_DREQ0_INPUT                0x00001000
+#define SIR_RXD1_INPUT         0x00000400
+#define SIR_CTS0_INPUT         0x00000100
+#define SIR_RXD0_INPUT         0x00000040
+#define SIR_GATE1_INPUT                0x00000020
+#define SIR_GATE0_INPUT                0x00000010
+#define SIR_IRQ3_INPUT         0x00000008
+#define SIR_IRQ2_INPUT         0x00000004
+#define SIR_IRQ1_INPUT         0x00000002
+#define SIR_IRQ0_INPUT         0x00000001
+#define SIR_DREQ_BITS          (SIR_DREQ0_INPUT | SIR_DREQ1_INPUT | \
+                                SIR_DREQ2_INPUT | SIR_DREQ3_INPUT | \
+                                SIR_DREQ4_INPUT | SIR_DREQ5_INPUT | \
+                                SIR_DREQ6_INPUT | SIR_DREQ7_INPUT)
+
+/* SOR definitions */
+#define SOR_GPIO_OUTPUT                0
+#define SOR_DACK3_OUTPUT       0x00200000
+#define SOR_DACK2_OUTPUT       0x00100000
+#define SOR_DONE1_OUTPUT       0x00020000
+#define SOR_DACK1_OUTPUT       0x00010000
+#define SOR_DONE0_OUTPUT       0x00004000
+#define SOR_DACK0_OUTPUT       0x00002000
+#define SOR_TXD1_OUTPUT                0x00000800
+#define SOR_RTS0_OUTPUT                0x00000200
+#define SOR_TXD0_OUTPUT                0x00000080
+#define SOR_TOUT1_OUTPUT       0x00000020
+#define SOR_TOUT0_OUTPUT       0x00000010
+#define SOR_DONE_BITS          (SOR_DONE0_OUTPUT | SOR_DONE1_OUTPUT)
+#define SOR_DACK_BITS          (SOR_DACK0_OUTPUT | SOR_DACK1_OUTPUT | \
+                                SOR_DACK2_OUTPUT | SOR_DACK3_OUTPUT)
+
+/* PDSR definitions */
+#define PDSR_UNCHANGED         0
+#define PDSR_SET_BIT(X)                (1 << (X))
+
+/* PDCR definitions */
+#define PDCR_UNCHANGED         0
+#define PDCR_CLEAR_BIT(X)      (1 << (X))
+
+/* RSTR definitions */
+/* Read Only */
+#define RSTR_POWERON           0x00000400
+#define RSTR_SOFTRESET_STATUS  0x00000100
+/* Write Only */
+#define RSTR_SOFTRESET         0x00000001
+
+#endif /* _ASM_GPIO_REGS */
diff --git a/include/asm-frv/hardirq.h b/include/asm-frv/hardirq.h
new file mode 100644 (file)
index 0000000..5248ca0
--- /dev/null
@@ -0,0 +1,30 @@
+/* hardirq.h: FRV hardware IRQ management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef __ASM_HARDIRQ_H
+#define __ASM_HARDIRQ_H
+
+#include <linux/config.h>
+#include <linux/threads.h>
+
+typedef struct {
+       unsigned int __softirq_pending;
+       unsigned long idle_timestamp;
+} ____cacheline_aligned irq_cpustat_t;
+
+#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
+
+#ifdef CONFIG_SMP
+#error SMP not available on FR-V
+#endif /* CONFIG_SMP */
+
+
+#endif
diff --git a/include/asm-frv/highmem.h b/include/asm-frv/highmem.h
new file mode 100644 (file)
index 0000000..6203b50
--- /dev/null
@@ -0,0 +1,183 @@
+/* highmem.h: virtual kernel memory mappings for high memory
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from include/asm-i386/highmem.h
+ *
+ * See Documentation/fujitsu/frv/mmu-layout.txt for more information.
+ *
+ * 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 _ASM_HIGHMEM_H
+#define _ASM_HIGHMEM_H
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/mem-layout.h>
+#include <asm/spr-regs.h>
+#include <asm/mb-regs.h>
+
+#define NR_TLB_LINES           64      /* number of lines in the TLB */
+
+#ifndef __ASSEMBLY__
+
+#include <linux/interrupt.h>
+#include <asm/kmap_types.h>
+#include <asm/pgtable.h>
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+#define HIGHMEM_DEBUG 1
+#else
+#define HIGHMEM_DEBUG 0
+#endif
+
+/* declarations for highmem.c */
+extern unsigned long highstart_pfn, highend_pfn;
+
+#define kmap_prot PAGE_KERNEL
+#define kmap_pte ______kmap_pte_in_TLB
+extern pte_t *pkmap_page_table;
+
+extern void kmap_init(void);
+
+#define flush_cache_kmaps()  do { } while (0)
+
+/*
+ * Right now we initialize only a single pte table. It can be extended
+ * easily, subsequent pte tables have to be allocated in one physical
+ * chunk of RAM.
+ */
+#define LAST_PKMAP     PTRS_PER_PTE
+#define LAST_PKMAP_MASK        (LAST_PKMAP - 1)
+#define PKMAP_NR(virt) ((virt - PKMAP_BASE) >> PAGE_SHIFT)
+#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT))
+
+extern void *kmap_high(struct page *page);
+extern void kunmap_high(struct page *page);
+
+extern void *kmap(struct page *page);
+extern void kunmap(struct page *page);
+
+extern struct page *kmap_atomic_to_page(void *ptr);
+
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
+ * gives a more generic (and caching) interface. But kmap_atomic can
+ * be used in IRQ contexts, so in some (very limited) cases we need
+ * it.
+ */
+#define KMAP_ATOMIC_CACHE_DAMR         8
+
+#ifndef __ASSEMBLY__
+
+#define __kmap_atomic_primary(type, paddr, ampr)                                               \
+({                                                                                             \
+       unsigned long damlr, dampr;                                                             \
+                                                                                               \
+       dampr = paddr | xAMPRx_L | xAMPRx_M | xAMPRx_S | xAMPRx_SS_16Kb | xAMPRx_V;             \
+                                                                                               \
+       if (type != __KM_CACHE)                                                                 \
+               asm volatile("movgs %0,dampr"#ampr :: "r"(dampr));                              \
+       else                                                                                    \
+               asm volatile("movgs %0,iampr"#ampr"\n"                                          \
+                            "movgs %0,dampr"#ampr"\n"                                          \
+                            :: "r"(dampr)                                                      \
+                            );                                                                 \
+                                                                                               \
+       asm("movsg damlr"#ampr",%0" : "=r"(damlr));                                             \
+                                                                                               \
+       /*printk("DAMR"#ampr": PRIM sl=%d L=%08lx P=%08lx\n", type, damlr, dampr);*/            \
+                                                                                               \
+       (void *) damlr;                                                                         \
+})
+
+#define __kmap_atomic_secondary(slot, paddr)                                                     \
+({                                                                                               \
+       unsigned long damlr = KMAP_ATOMIC_SECONDARY_FRAME + (slot) * PAGE_SIZE;                   \
+       unsigned long dampr = paddr | xAMPRx_L | xAMPRx_M | xAMPRx_S | xAMPRx_SS_16Kb | xAMPRx_V; \
+                                                                                                 \
+       asm volatile("movgs %0,tplr \n"                                                           \
+                    "movgs %1,tppr \n"                                                           \
+                    "tlbpr %0,gr0,#2,#1"                                                         \
+                    : : "r"(damlr), "r"(dampr));                                                 \
+                                                                                                 \
+       /*printk("TLB: SECN sl=%d L=%08lx P=%08lx\n", slot, damlr, dampr);*/                      \
+                                                                                                 \
+       (void *) damlr;                                                                           \
+})
+
+static inline void *kmap_atomic(struct page *page, enum km_type type)
+{
+       unsigned long paddr;
+
+       preempt_disable();
+       paddr = page_to_phys(page);
+
+       switch (type) {
+        case 0:                return __kmap_atomic_primary(0, paddr, 2);
+        case 1:                return __kmap_atomic_primary(1, paddr, 3);
+        case 2:                return __kmap_atomic_primary(2, paddr, 4);
+        case 3:                return __kmap_atomic_primary(3, paddr, 5);
+        case 4:                return __kmap_atomic_primary(4, paddr, 6);
+        case 5:                return __kmap_atomic_primary(5, paddr, 7);
+        case 6:                return __kmap_atomic_primary(6, paddr, 8);
+        case 7:                return __kmap_atomic_primary(7, paddr, 9);
+        case 8:                return __kmap_atomic_primary(8, paddr, 10);
+
+       case 9 ... 9 + NR_TLB_LINES - 1:
+               return __kmap_atomic_secondary(type - 9, paddr);
+
+       default:
+               BUG();
+               return 0;
+       }
+}
+
+#define __kunmap_atomic_primary(type, ampr)                    \
+do {                                                           \
+       asm volatile("movgs gr0,dampr"#ampr"\n");               \
+       if (type == __KM_CACHE)                                 \
+               asm volatile("movgs gr0,iampr"#ampr"\n");       \
+} while(0)
+
+#define __kunmap_atomic_secondary(slot, vaddr)                 \
+do {                                                           \
+       asm volatile("tlbpr %0,gr0,#4,#1" : : "r"(vaddr));      \
+} while(0)
+
+static inline void kunmap_atomic(void *kvaddr, enum km_type type)
+{
+       switch (type) {
+        case 0:                __kunmap_atomic_primary(0, 2);  break;
+        case 1:                __kunmap_atomic_primary(1, 3);  break;
+        case 2:                __kunmap_atomic_primary(2, 4);  break;
+        case 3:                __kunmap_atomic_primary(3, 5);  break;
+        case 4:                __kunmap_atomic_primary(4, 6);  break;
+        case 5:                __kunmap_atomic_primary(5, 7);  break;
+        case 6:                __kunmap_atomic_primary(6, 8);  break;
+        case 7:                __kunmap_atomic_primary(7, 9);  break;
+        case 8:                __kunmap_atomic_primary(8, 10); break;
+
+       case 9 ... 9 + NR_TLB_LINES - 1:
+               __kunmap_atomic_secondary(type - 9, kvaddr);
+               break;
+
+       default:
+               BUG();
+       }
+       preempt_enable();
+}
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_HIGHMEM_H */
diff --git a/include/asm-frv/hw_irq.h b/include/asm-frv/hw_irq.h
new file mode 100644 (file)
index 0000000..522ad37
--- /dev/null
@@ -0,0 +1,16 @@
+/* hw_irq.h: FR-V specific h/w IRQ stuff
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_HW_IRQ_H
+#define _ASM_HW_IRQ_H
+
+
+#endif /* _ASM_HW_IRQ_H */
diff --git a/include/asm-frv/ide.h b/include/asm-frv/ide.h
new file mode 100644 (file)
index 0000000..f9caecf
--- /dev/null
@@ -0,0 +1,43 @@
+/* ide.h: FRV IDE declarations
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_IDE_H
+#define _ASM_IDE_H
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <asm/setup.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#undef SUPPORT_SLOW_DATA_PORTS
+#define SUPPORT_SLOW_DATA_PORTS 0
+
+#undef SUPPORT_VLB_SYNC
+#define SUPPORT_VLB_SYNC 0
+
+#ifndef MAX_HWIFS
+#define MAX_HWIFS 8
+#endif
+
+/****************************************************************************/
+/*
+ * some bits needed for parts of the IDE subsystem to compile
+ */
+#define __ide_mm_insw(port, addr, n)   insw(port, addr, n)
+#define __ide_mm_insl(port, addr, n)   insl(port, addr, n)
+#define __ide_mm_outsw(port, addr, n)  outsw(port, addr, n)
+#define __ide_mm_outsl(port, addr, n)  outsl(port, addr, n)
+
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_IDE_H */
diff --git a/include/asm-frv/init.h b/include/asm-frv/init.h
new file mode 100644 (file)
index 0000000..8b15838
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _ASM_INIT_H
+#define _ASM_INIT_H
+
+#define __init __attribute__ ((__section__ (".text.init")))
+#define __initdata __attribute__ ((__section__ (".data.init")))
+/* For assembly routines */
+#define __INIT         .section        ".text.init",#alloc,#execinstr
+#define __FINIT                .previous
+#define __INITDATA     .section        ".data.init",#alloc,#write
+
+#endif
+
diff --git a/include/asm-frv/io.h b/include/asm-frv/io.h
new file mode 100644 (file)
index 0000000..48829f7
--- /dev/null
@@ -0,0 +1,290 @@
+/* io.h: FRV I/O operations
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This gets interesting when talking to the PCI bus - the CPU is in big endian
+ * mode, the PCI bus is little endian and the hardware in the middle can do
+ * byte swapping
+ */
+#ifndef _ASM_IO_H
+#define _ASM_IO_H
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <asm/virtconvert.h>
+#include <asm/string.h>
+#include <asm/mb-regs.h>
+#include <linux/delay.h>
+
+/*
+ * swap functions are sometimes needed to interface little-endian hardware
+ */
+
+static inline unsigned short _swapw(unsigned short v)
+{
+    return ((v << 8) | (v >> 8));
+}
+
+static inline unsigned long _swapl(unsigned long v)
+{
+    return ((v << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | (v >> 24));
+}
+
+//#define __iormb() asm volatile("membar")
+//#define __iowmb() asm volatile("membar")
+
+#define __raw_readb(addr) __builtin_read8((void *) (addr))
+#define __raw_readw(addr) __builtin_read16((void *) (addr))
+#define __raw_readl(addr) __builtin_read32((void *) (addr))
+
+#define __raw_writeb(datum, addr) __builtin_write8((void *) (addr), datum)
+#define __raw_writew(datum, addr) __builtin_write16((void *) (addr), datum)
+#define __raw_writel(datum, addr) __builtin_write32((void *) (addr), datum)
+
+static inline void io_outsb(unsigned int addr, const void *buf, int len)
+{
+       unsigned long __ioaddr = (unsigned long) addr;
+       const uint8_t *bp = buf;
+
+       while (len--)
+               __builtin_write8((volatile void __iomem *) __ioaddr, *bp++);
+}
+
+static inline void io_outsw(unsigned int addr, const void *buf, int len)
+{
+       unsigned long __ioaddr = (unsigned long) addr;
+       const uint16_t *bp = buf;
+
+       while (len--)
+               __builtin_write16((volatile void __iomem *) __ioaddr, (*bp++));
+}
+
+extern void __outsl_ns(unsigned int addr, const void *buf, int len);
+extern void __outsl_sw(unsigned int addr, const void *buf, int len);
+static inline void __outsl(unsigned int addr, const void *buf, int len, int swap)
+{
+       unsigned long __ioaddr = (unsigned long) addr;
+
+       if (!swap)
+               __outsl_ns(__ioaddr, buf, len);
+       else
+               __outsl_sw(__ioaddr, buf, len);
+}
+
+static inline void io_insb(unsigned long addr, void *buf, int len)
+{
+       uint8_t *bp = buf;
+
+       while (len--)
+               *bp++ = __builtin_read8((volatile void __iomem *) addr);
+}
+
+static inline void io_insw(unsigned long addr, void *buf, int len)
+{
+       uint16_t *bp = buf;
+
+       while (len--)
+               *bp++ = __builtin_read16((volatile void __iomem *) addr);
+}
+
+extern void __insl_ns(unsigned long addr, void *buf, int len);
+extern void __insl_sw(unsigned long addr, void *buf, int len);
+static inline void __insl(unsigned long addr, void *buf, int len, int swap)
+{
+       if (!swap)
+               __insl_ns(addr, buf, len);
+       else
+               __insl_sw(addr, buf, len);
+}
+
+/*
+ *     make the short names macros so specific devices
+ *     can override them as required
+ */
+
+static inline void memset_io(volatile void __iomem *addr, unsigned char val, int count)
+{
+       memset((void __force *) addr, val, count);
+}
+
+static inline void memcpy_fromio(void *dst, volatile void __iomem *src, int count)
+{
+       memcpy(dst, (void __force *) src, count);
+}
+
+static inline void memcpy_toio(volatile void __iomem *dst, const void *src, int count)
+{
+       memcpy((void __force *) dst, src, count);
+}
+
+static inline uint8_t inb(unsigned long addr)
+{
+       return __builtin_read8((void *)addr);
+}
+
+static inline uint16_t inw(unsigned long addr)
+{
+       uint16_t ret = __builtin_read16((void *)addr);
+
+       if (__is_PCI_IO(addr))
+               ret = _swapw(ret);
+
+       return ret;
+}
+
+static inline uint32_t inl(unsigned long addr)
+{
+       uint32_t ret = __builtin_read32((void *)addr);
+
+       if (__is_PCI_IO(addr))
+               ret = _swapl(ret);
+
+       return ret;
+}
+
+static inline void outb(uint8_t datum, unsigned long addr)
+{
+       __builtin_write8((void *)addr, datum);
+}
+
+static inline void outw(uint16_t datum, unsigned long addr)
+{
+       if (__is_PCI_IO(addr))
+               datum = _swapw(datum);
+       __builtin_write16((void *)addr, datum);
+}
+
+static inline void outl(uint32_t datum, unsigned long addr)
+{
+       if (__is_PCI_IO(addr))
+               datum = _swapl(datum);
+       __builtin_write32((void *)addr, datum);
+}
+
+#define inb_p(addr)    inb(addr)
+#define inw_p(addr)    inw(addr)
+#define inl_p(addr)    inl(addr)
+#define outb_p(x,addr) outb(x,addr)
+#define outw_p(x,addr) outw(x,addr)
+#define outl_p(x,addr) outl(x,addr)
+
+#define outsb(a,b,l)   io_outsb(a,b,l)
+#define outsw(a,b,l)   io_outsw(a,b,l)
+#define outsl(a,b,l)   __outsl(a,b,l,0)
+
+#define insb(a,b,l)    io_insb(a,b,l)
+#define insw(a,b,l)    io_insw(a,b,l)
+#define insl(a,b,l)    __insl(a,b,l,0)
+
+#define IO_SPACE_LIMIT 0xffffffff
+
+static inline uint8_t readb(const volatile void __iomem *addr)
+{
+       return __builtin_read8((volatile uint8_t __force *) addr);
+}
+
+static inline uint16_t readw(const volatile void __iomem *addr)
+{
+       uint16_t ret =  __builtin_read16((volatile uint16_t __force *)addr);
+
+       if (__is_PCI_MEM(addr))
+               ret = _swapw(ret);
+       return ret;
+}
+
+static inline uint32_t readl(const volatile void __iomem *addr)
+{
+       uint32_t ret =  __builtin_read32((volatile uint32_t __force *)addr);
+
+       if (__is_PCI_MEM(addr))
+               ret = _swapl(ret);
+
+       return ret;
+}
+
+static inline void writeb(uint8_t datum, volatile void __iomem *addr)
+{
+       __builtin_write8((volatile uint8_t __force *) addr, datum);
+       if (__is_PCI_MEM(addr))
+               __flush_PCI_writes();
+}
+
+static inline void writew(uint16_t datum, volatile void __iomem *addr)
+{
+       if (__is_PCI_MEM(addr))
+               datum = _swapw(datum);
+
+       __builtin_write16((volatile uint16_t __force *) addr, datum);
+       if (__is_PCI_MEM(addr))
+               __flush_PCI_writes();
+}
+
+static inline void writel(uint32_t datum, volatile void __iomem *addr)
+{
+       if (__is_PCI_MEM(addr))
+               datum = _swapl(datum);
+
+       __builtin_write32((volatile uint32_t __force *) addr, datum);
+       if (__is_PCI_MEM(addr))
+               __flush_PCI_writes();
+}
+
+
+/* Values for nocacheflag and cmode */
+#define IOMAP_FULL_CACHING             0
+#define IOMAP_NOCACHE_SER              1
+#define IOMAP_NOCACHE_NONSER           2
+#define IOMAP_WRITETHROUGH             3
+
+extern void __iomem *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag);
+extern void __iounmap(void __iomem *addr, unsigned long size);
+
+static inline void __iomem *ioremap(unsigned long physaddr, unsigned long size)
+{
+       return __ioremap(physaddr, size, IOMAP_NOCACHE_SER);
+}
+
+static inline void __iomem *ioremap_nocache(unsigned long physaddr, unsigned long size)
+{
+       return __ioremap(physaddr, size, IOMAP_NOCACHE_SER);
+}
+
+static inline void __iomem *ioremap_writethrough(unsigned long physaddr, unsigned long size)
+{
+       return __ioremap(physaddr, size, IOMAP_WRITETHROUGH);
+}
+
+static inline void __iomem *ioremap_fullcache(unsigned long physaddr, unsigned long size)
+{
+       return __ioremap(physaddr, size, IOMAP_FULL_CACHING);
+}
+
+extern void iounmap(void __iomem *addr);
+
+static inline void flush_write_buffers(void)
+{
+       __asm__ __volatile__ ("membar" : : :"memory");
+}
+
+
+/*
+ * Convert a physical pointer to a virtual kernel pointer for /dev/mem
+ * access
+ */
+#define xlate_dev_mem_ptr(p)   __va(p)
+
+/*
+ * Convert a virtual cached pointer to an uncached pointer
+ */
+#define xlate_dev_kmem_ptr(p)  p
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_IO_H */
diff --git a/include/asm-frv/ioctl.h b/include/asm-frv/ioctl.h
new file mode 100644 (file)
index 0000000..8aee769
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * linux/ioctl.h for Linux by H.H. Bergman.
+ */
+
+#ifndef _ASM_IOCTL_H
+#define _ASM_IOCTL_H
+
+/* ioctl command encoding: 32 bits total, command in lower 16 bits,
+ * size of the parameter structure in the lower 14 bits of the
+ * upper 16 bits.
+ * Encoding the size of the parameter structure in the ioctl request
+ * is useful for catching programs compiled with old versions
+ * and to avoid overwriting user space outside the user buffer area.
+ * The highest 2 bits are reserved for indicating the ``access mode''.
+ * NOTE: This limits the max parameter size to 16kB -1 !
+ */
+
+/*
+ * I don't really have any idea about what this should look like, so
+ * for the time being, this is heavily based on the PC definitions.
+ */
+
+/*
+ * The following is for compatibility across the various Linux
+ * platforms.  The i386 ioctl numbering scheme doesn't really enforce
+ * a type field.  De facto, however, the top 8 bits of the lower 16
+ * bits are indeed used as a type field, so we might just as well make
+ * this explicit here.  Please be sure to use the decoding macros
+ * below from now on.
+ */
+#define _IOC_NRBITS    8
+#define _IOC_TYPEBITS  8
+#define _IOC_SIZEBITS  14
+#define _IOC_DIRBITS   2
+
+#define _IOC_NRMASK    ((1 << _IOC_NRBITS)-1)
+#define _IOC_TYPEMASK  ((1 << _IOC_TYPEBITS)-1)
+#define _IOC_SIZEMASK  ((1 << _IOC_SIZEBITS)-1)
+#define _IOC_DIRMASK   ((1 << _IOC_DIRBITS)-1)
+
+#define _IOC_NRSHIFT   0
+#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
+#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
+#define _IOC_DIRSHIFT  (_IOC_SIZESHIFT+_IOC_SIZEBITS)
+
+/*
+ * Direction bits.
+ */
+#define _IOC_NONE      0U
+#define _IOC_WRITE     1U
+#define _IOC_READ      2U
+
+#define _IOC(dir,type,nr,size) \
+       (((dir)  << _IOC_DIRSHIFT) | \
+        ((type) << _IOC_TYPESHIFT) | \
+        ((nr)   << _IOC_NRSHIFT) | \
+        ((size) << _IOC_SIZESHIFT))
+
+/* used to create numbers */
+#define _IO(type,nr)           _IOC(_IOC_NONE,(type),(nr),0)
+#define _IOR(type,nr,size)     _IOC(_IOC_READ,(type),(nr),sizeof(size))
+#define _IOW(type,nr,size)     _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
+#define _IOWR(type,nr,size)    _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
+
+/* used to decode ioctl numbers.. */
+#define _IOC_DIR(nr)           (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
+#define _IOC_TYPE(nr)          (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
+#define _IOC_NR(nr)            (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
+#define _IOC_SIZE(nr)          (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
+
+/* ...and for the drivers/sound files... */
+
+#define IOC_IN         (_IOC_WRITE << _IOC_DIRSHIFT)
+#define IOC_OUT                (_IOC_READ << _IOC_DIRSHIFT)
+#define IOC_INOUT      ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
+#define IOCSIZE_MASK   (_IOC_SIZEMASK << _IOC_SIZESHIFT)
+#define IOCSIZE_SHIFT  (_IOC_SIZESHIFT)
+
+#endif /* _ASM_IOCTL_H */
+
diff --git a/include/asm-frv/ioctls.h b/include/asm-frv/ioctls.h
new file mode 100644 (file)
index 0000000..341c7dd
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef __ASM_IOCTLS_H__
+#define __ASM_IOCTLS_H__
+
+#include <asm/ioctl.h>
+
+/* 0x54 is just a magic number to make these relatively unique ('T') */
+
+#define TCGETS         0x5401
+#define TCSETS         0x5402
+#define TCSETSW                0x5403
+#define TCSETSF                0x5404
+#define TCGETA         0x5405
+#define TCSETA         0x5406
+#define TCSETAW                0x5407
+#define TCSETAF                0x5408
+#define TCSBRK         0x5409
+#define TCXONC         0x540A
+#define TCFLSH         0x540B
+#define TIOCEXCL       0x540C
+#define TIOCNXCL       0x540D
+#define TIOCSCTTY      0x540E
+#define TIOCGPGRP      0x540F
+#define TIOCSPGRP      0x5410
+#define TIOCOUTQ       0x5411
+#define TIOCSTI                0x5412
+#define TIOCGWINSZ     0x5413
+#define TIOCSWINSZ     0x5414
+#define TIOCMGET       0x5415
+#define TIOCMBIS       0x5416
+#define TIOCMBIC       0x5417
+#define TIOCMSET       0x5418
+#define TIOCGSOFTCAR   0x5419
+#define TIOCSSOFTCAR   0x541A
+#define FIONREAD       0x541B
+#define TIOCINQ                FIONREAD
+#define TIOCLINUX      0x541C
+#define TIOCCONS       0x541D
+#define TIOCGSERIAL    0x541E
+#define TIOCSSERIAL    0x541F
+#define TIOCPKT                0x5420
+#define FIONBIO                0x5421
+#define TIOCNOTTY      0x5422
+#define TIOCSETD       0x5423
+#define TIOCGETD       0x5424
+#define TCSBRKP                0x5425  /* Needed for POSIX tcsendbreak() */
+#define TIOCTTYGSTRUCT 0x5426  /* For debugging only */
+#define TIOCSBRK       0x5427  /* BSD compatibility */
+#define TIOCCBRK       0x5428  /* BSD compatibility */
+#define TIOCGSID       0x5429  /* Return the session ID of FD */
+#define TIOCGPTN       _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TIOCSPTLCK     _IOW('T',0x31, int)  /* Lock/unlock Pty */
+
+#define FIONCLEX       0x5450  /* these numbers need to be adjusted. */
+#define FIOCLEX                0x5451
+#define FIOASYNC       0x5452
+#define TIOCSERCONFIG  0x5453
+#define TIOCSERGWILD   0x5454
+#define TIOCSERSWILD   0x5455
+#define TIOCGLCKTRMIOS 0x5456
+#define TIOCSLCKTRMIOS 0x5457
+#define TIOCSERGSTRUCT 0x5458 /* For debugging only */
+#define TIOCSERGETLSR   0x5459 /* Get line status register */
+#define TIOCSERGETMULTI 0x545A /* Get multiport config  */
+#define TIOCSERSETMULTI 0x545B /* Set multiport config */
+
+#define TIOCMIWAIT     0x545C  /* wait for a change on serial input line(s) */
+#define TIOCGICOUNT    0x545D  /* read serial port inline interrupt counts */
+#define FIOQSIZE       0x545E
+
+/* Used for packet mode */
+#define TIOCPKT_DATA            0
+#define TIOCPKT_FLUSHREAD       1
+#define TIOCPKT_FLUSHWRITE      2
+#define TIOCPKT_STOP            4
+#define TIOCPKT_START           8
+#define TIOCPKT_NOSTOP         16
+#define TIOCPKT_DOSTOP         32
+
+#define TIOCSER_TEMT    0x01   /* Transmitter physically empty */
+
+#endif /* __ASM_IOCTLS_H__ */
+
diff --git a/include/asm-frv/ipc.h b/include/asm-frv/ipc.h
new file mode 100644 (file)
index 0000000..2f72065
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __ASM_IPC_H__
+#define __ASM_IPC_H__
+
+/*
+ * These are used to wrap system calls on FR-V
+ *
+ * See arch/frv/kernel/sys_frv.c for ugly details..
+ */
+struct ipc_kludge {
+       struct msgbuf __user *msgp;
+       long msgtyp;
+};
+
+#define SEMOP           1
+#define SEMGET          2
+#define SEMCTL          3
+#define SEMTIMEDOP      4
+#define MSGSND         11
+#define MSGRCV         12
+#define MSGGET         13
+#define MSGCTL         14
+#define SHMAT          21
+#define SHMDT          22
+#define SHMGET         23
+#define SHMCTL         24
+
+/* Used by the DIPC package, try and avoid reusing it */
+#define DIPC           25
+
+#define IPCCALL(version,op)    ((version)<<16 | (op))
+
+#endif
+
diff --git a/include/asm-frv/ipcbuf.h b/include/asm-frv/ipcbuf.h
new file mode 100644 (file)
index 0000000..b546f67
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef __ASM_IPCBUF_H__
+#define __ASM_IPCBUF_H__
+
+/*
+ * The user_ipc_perm structure for FR-V architecture.
+ * Note extra padding because this structure is passed back and forth
+ * between kernel and user space.
+ *
+ * Pad space is left for:
+ * - 32-bit mode_t and seq
+ * - 2 miscellaneous 32-bit values
+ */
+
+struct ipc64_perm
+{
+       __kernel_key_t          key;
+       __kernel_uid32_t        uid;
+       __kernel_gid32_t        gid;
+       __kernel_uid32_t        cuid;
+       __kernel_gid32_t        cgid;
+       __kernel_mode_t         mode;
+       unsigned short          __pad1;
+       unsigned short          seq;
+       unsigned short          __pad2;
+       unsigned long           __unused1;
+       unsigned long           __unused2;
+};
+
+#endif /* __ASM_IPCBUF_H__ */
+
diff --git a/include/asm-frv/irc-regs.h b/include/asm-frv/irc-regs.h
new file mode 100644 (file)
index 0000000..afa30ae
--- /dev/null
@@ -0,0 +1,53 @@
+/* irc-regs.h: on-chip interrupt controller registers
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_IRC_REGS
+#define _ASM_IRC_REGS
+
+#define __reg(ADDR) (*(volatile unsigned long *)(ADDR))
+
+#define __get_TM0()    ({ __reg(0xfeff9800); })
+#define __get_TM1()    ({ __reg(0xfeff9808); })
+#define __set_TM1(V)   do { __reg(0xfeff9808) = (V); mb(); } while(0)
+
+#define __set_TM1x(XI,V)                       \
+do {                                           \
+       int shift = (XI) * 2 + 16;              \
+       unsigned long tm1 = __reg(0xfeff9808);  \
+       tm1 &= ~(0x3 << shift);                 \
+       tm1 |= (V) << shift;                    \
+       __reg(0xfeff9808) = tm1;                \
+       mb();                                   \
+} while(0)
+
+#define __get_RS(C)    ({ (__reg(0xfeff9810) >> ((C)+16)) & 1; })
+
+#define __clr_RC(C)    do { __reg(0xfeff9818) = 1 << ((C)+16); mb(); } while(0)
+
+#define __get_MASK(C)  ({ (__reg(0xfeff9820) >> ((C)+16)) & 1; })
+#define __set_MASK(C)  do { __reg(0xfeff9820) |=  1 << ((C)+16); mb(); } while(0)
+#define __clr_MASK(C)  do { __reg(0xfeff9820) &=  ~(1 << ((C)+16)); mb(); } while(0)
+
+#define __get_MASK_all() __get_MASK(0)
+#define __set_MASK_all() __set_MASK(0)
+#define __clr_MASK_all() __clr_MASK(0)
+
+#define __get_IRL()    ({ (__reg(0xfeff9828) >> 16) & 0xf; })
+#define __clr_IRL()    do { __reg(0xfeff9828) = 0x100000; mb(); } while(0)
+
+#define __get_IRR(N)   ({ __reg(0xfeff9840 + (N) * 8); })
+#define __set_IRR(N,V) do { __reg(0xfeff9840 + (N) * 8) = (V); } while(0)
+
+#define __get_IITMR(N) ({ __reg(0xfeff9880 + (N) * 8); })
+#define __set_IITMR(N,V) do { __reg(0xfeff9880 + (N) * 8) = (V); } while(0)
+
+
+#endif /* _ASM_IRC_REGS */
diff --git a/include/asm-frv/irq-routing.h b/include/asm-frv/irq-routing.h
new file mode 100644 (file)
index 0000000..686fb2b
--- /dev/null
@@ -0,0 +1,70 @@
+/* irq-routing.h: multiplexed IRQ routing
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_IRQ_ROUTING_H
+#define _ASM_IRQ_ROUTING_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/spinlock.h>
+#include <asm/irq.h>
+
+struct irq_source;
+struct irq_level;
+
+/*
+ * IRQ action distribution sets
+ */
+struct irq_group {
+       int                     first_irq;      /* first IRQ distributed here */
+       void (*control)(struct irq_group *group, int index, int on);
+
+       struct irqaction        *actions[NR_IRQ_ACTIONS_PER_GROUP];     /* IRQ action chains */
+       struct irq_source       *sources[NR_IRQ_ACTIONS_PER_GROUP];     /* IRQ sources */
+       int                     disable_cnt[NR_IRQ_ACTIONS_PER_GROUP];  /* disable counts */
+};
+
+/*
+ * IRQ source manager
+ */
+struct irq_source {
+       struct irq_source       *next;
+       struct irq_level        *level;
+       const char              *muxname;
+       volatile void __iomem   *muxdata;
+       unsigned long           irqmask;
+
+       void (*doirq)(struct irq_source *source);
+};
+
+/*
+ * IRQ level management (per CPU IRQ priority / entry vector)
+ */
+struct irq_level {
+       int                     usage;
+       int                     disable_count;
+       unsigned long           flags;          /* current SA_INTERRUPT and SA_SHIRQ settings */
+       spinlock_t              lock;
+       struct irq_source       *sources;
+};
+
+extern struct irq_level frv_irq_levels[16];
+extern struct irq_group *irq_groups[NR_IRQ_GROUPS];
+
+extern void frv_irq_route(struct irq_source *source, int irqlevel);
+extern void frv_irq_route_external(struct irq_source *source, int irq);
+extern void frv_irq_set_group(struct irq_group *group);
+extern void distribute_irqs(struct irq_group *group, unsigned long irqmask);
+extern void route_cpu_irqs(void);
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* _ASM_IRQ_ROUTING_H */
diff --git a/include/asm-frv/irq.h b/include/asm-frv/irq.h
new file mode 100644 (file)
index 0000000..2c16d8d
--- /dev/null
@@ -0,0 +1,44 @@
+/* irq.h: FRV IRQ definitions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_IRQ_H_
+#define _ASM_IRQ_H_
+
+#include <linux/config.h>
+
+/*
+ * the system has an on-CPU PIC and another PIC on the FPGA and other PICs on other peripherals,
+ * so we do some routing in irq-routing.[ch] to reduce the number of false-positives seen by
+ * drivers
+ */
+
+/* this number is used when no interrupt has been assigned */
+#define NO_IRQ                         (-1)
+
+#define NR_IRQ_LOG2_ACTIONS_PER_GROUP  5
+#define NR_IRQ_ACTIONS_PER_GROUP       (1 << NR_IRQ_LOG2_ACTIONS_PER_GROUP)
+#define NR_IRQ_GROUPS                  4
+#define NR_IRQS                                (NR_IRQ_ACTIONS_PER_GROUP * NR_IRQ_GROUPS)
+
+/* probe returns a 32-bit IRQ mask:-/ */
+#define MIN_PROBE_IRQ  (NR_IRQS - 32)
+
+static inline int irq_canonicalize(int irq)
+{
+       return irq;
+}
+
+extern void disable_irq_nosync(unsigned int irq);
+extern void disable_irq(unsigned int irq);
+extern void enable_irq(unsigned int irq);
+
+
+#endif /* _ASM_IRQ_H_ */
diff --git a/include/asm-frv/kmap_types.h b/include/asm-frv/kmap_types.h
new file mode 100644 (file)
index 0000000..f8e16b2
--- /dev/null
@@ -0,0 +1,29 @@
+
+#ifndef _ASM_KMAP_TYPES_H
+#define _ASM_KMAP_TYPES_H
+
+enum km_type {
+       /* arch specific kmaps - change the numbers attached to these at your peril */
+       __KM_CACHE,             /* cache flush page attachment point */
+       __KM_PGD,               /* current page directory */
+       __KM_ITLB_PTD,          /* current instruction TLB miss page table lookup */
+       __KM_DTLB_PTD,          /* current data TLB miss page table lookup */
+
+       /* general kmaps */
+        KM_BOUNCE_READ,
+        KM_SKB_SUNRPC_DATA,
+        KM_SKB_DATA_SOFTIRQ,
+        KM_USER0,
+        KM_USER1,
+       KM_BIO_SRC_IRQ,
+       KM_BIO_DST_IRQ,
+       KM_PTE0,
+       KM_PTE1,
+       KM_IRQ0,
+       KM_IRQ1,
+       KM_SOFTIRQ0,
+       KM_SOFTIRQ1,
+       KM_TYPE_NR
+};
+
+#endif
diff --git a/include/asm-frv/linkage.h b/include/asm-frv/linkage.h
new file mode 100644 (file)
index 0000000..636c1bc
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __ASM_LINKAGE_H
+#define __ASM_LINKAGE_H
+
+#define __ALIGN                .align 4
+#define __ALIGN_STR    ".align 4"
+
+#endif
diff --git a/include/asm-frv/local.h b/include/asm-frv/local.h
new file mode 100644 (file)
index 0000000..c27bdf0
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _ASM_LOCAL_H
+#define _ASM_LOCAL_H
+
+#include <asm-generic/local.h>
+
+#endif /* _ASM_LOCAL_H */
diff --git a/include/asm-frv/math-emu.h b/include/asm-frv/math-emu.h
new file mode 100644 (file)
index 0000000..0c8f731
--- /dev/null
@@ -0,0 +1,301 @@
+#ifndef _ASM_MATH_EMU_H
+#define _ASM_MATH_EMU_H
+
+#include <asm/setup.h>
+#include <linux/linkage.h>
+
+/* Status Register bits */
+
+/* accrued exception bits */
+#define FPSR_AEXC_INEX 3
+#define FPSR_AEXC_DZ   4
+#define FPSR_AEXC_UNFL 5
+#define FPSR_AEXC_OVFL 6
+#define FPSR_AEXC_IOP  7
+
+/* exception status bits */
+#define FPSR_EXC_INEX1 8
+#define FPSR_EXC_INEX2 9
+#define FPSR_EXC_DZ    10
+#define FPSR_EXC_UNFL  11
+#define FPSR_EXC_OVFL  12
+#define FPSR_EXC_OPERR 13
+#define FPSR_EXC_SNAN  14
+#define FPSR_EXC_BSUN  15
+
+/* quotient byte, assumes big-endian, of course */
+#define FPSR_QUOTIENT(fpsr) (*((signed char *) &(fpsr) + 1))
+
+/* condition code bits */
+#define FPSR_CC_NAN    24
+#define FPSR_CC_INF    25
+#define FPSR_CC_Z      26
+#define FPSR_CC_NEG    27
+
+
+/* Control register bits */
+
+/* rounding mode */
+#define        FPCR_ROUND_RN   0               /* round to nearest/even */
+#define FPCR_ROUND_RZ  1               /* round to zero */
+#define FPCR_ROUND_RM  2               /* minus infinity */
+#define FPCR_ROUND_RP  3               /* plus infinity */
+
+/* rounding precision */
+#define FPCR_PRECISION_X       0       /* long double */
+#define FPCR_PRECISION_S       1       /* double */
+#define FPCR_PRECISION_D       2       /* float */
+
+
+/* Flags to select the debugging output */
+#define PDECODE                0
+#define PEXECUTE       1
+#define PCONV          2
+#define PNORM          3
+#define PREGISTER      4
+#define PINSTR         5
+#define PUNIMPL                6
+#define PMOVEM         7
+
+#define PMDECODE       (1<<PDECODE)
+#define PMEXECUTE      (1<<PEXECUTE)
+#define PMCONV         (1<<PCONV)
+#define PMNORM         (1<<PNORM)
+#define PMREGISTER     (1<<PREGISTER)
+#define PMINSTR                (1<<PINSTR)
+#define PMUNIMPL       (1<<PUNIMPL)
+#define PMMOVEM                (1<<PMOVEM)
+
+#ifndef __ASSEMBLY__
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+union fp_mant64 {
+       unsigned long long m64;
+       unsigned long m32[2];
+};
+
+union fp_mant128 {
+       unsigned long long m64[2];
+       unsigned long m32[4];
+};
+
+/* internal representation of extended fp numbers */
+struct fp_ext {
+       unsigned char lowmant;
+       unsigned char sign;
+       unsigned short exp;
+       union fp_mant64 mant;
+};
+
+/* C representation of FPU registers */
+/* NOTE: if you change this, you have to change the assembler offsets
+   below and the size in <asm/fpu.h>, too */
+struct fp_data {
+       struct fp_ext fpreg[8];
+       unsigned int fpcr;
+       unsigned int fpsr;
+       unsigned int fpiar;
+       unsigned short prec;
+       unsigned short rnd;
+       struct fp_ext temp[2];
+};
+
+#if FPU_EMU_DEBUG
+extern unsigned int fp_debugprint;
+
+#define dprint(bit, fmt, args...) ({                   \
+       if (fp_debugprint & (1 << (bit)))               \
+               printk(fmt, ## args);                   \
+})
+#else
+#define dprint(bit, fmt, args...)
+#endif
+
+#define uprint(str) ({                                 \
+       static int __count = 3;                         \
+                                                       \
+       if (__count > 0) {                              \
+               printk("You just hit an unimplemented " \
+                      "fpu instruction (%s)\n", str);  \
+               printk("Please report this to ....\n"); \
+               __count--;                              \
+       }                                               \
+})
+
+#define FPDATA         ((struct fp_data *)current->thread.fp)
+
+#else  /* __ASSEMBLY__ */
+
+#define FPDATA         %a2
+
+/* offsets from the base register to the floating point data in the task struct */
+#define FPD_FPREG      (TASK_THREAD+THREAD_FPREG+0)
+#define FPD_FPCR       (TASK_THREAD+THREAD_FPREG+96)
+#define FPD_FPSR       (TASK_THREAD+THREAD_FPREG+100)
+#define FPD_FPIAR      (TASK_THREAD+THREAD_FPREG+104)
+#define FPD_PREC       (TASK_THREAD+THREAD_FPREG+108)
+#define FPD_RND                (TASK_THREAD+THREAD_FPREG+110)
+#define FPD_TEMPFP1    (TASK_THREAD+THREAD_FPREG+112)
+#define FPD_TEMPFP2    (TASK_THREAD+THREAD_FPREG+124)
+#define FPD_SIZEOF     (TASK_THREAD+THREAD_FPREG+136)
+
+/* offsets on the stack to access saved registers,
+ * these are only used during instruction decoding
+ * where we always know how deep we're on the stack.
+ */
+#define FPS_DO         (PT_D0)
+#define FPS_D1         (PT_D1)
+#define FPS_D2         (PT_D2)
+#define FPS_A0         (PT_A0)
+#define FPS_A1         (PT_A1)
+#define FPS_A2         (PT_A2)
+#define FPS_SR         (PT_SR)
+#define FPS_PC         (PT_PC)
+#define FPS_EA         (PT_PC+6)
+#define FPS_PC2                (PT_PC+10)
+
+.macro fp_get_fp_reg
+       lea     (FPD_FPREG,FPDATA,%d0.w*4),%a0
+       lea     (%a0,%d0.w*8),%a0
+.endm
+
+/* Macros used to get/put the current program counter.
+ * 020/030 use a different stack frame then 040/060, for the
+ * 040/060 the return pc points already to the next location,
+ * so this only needs to be modified for jump instructions.
+ */
+.macro fp_get_pc dest
+       move.l  (FPS_PC+4,%sp),\dest
+.endm
+
+.macro fp_put_pc src,jump=0
+       move.l  \src,(FPS_PC+4,%sp)
+.endm
+
+.macro fp_get_instr_data       f,s,dest,label
+       getuser \f,%sp@(FPS_PC+4)@(0),\dest,\label,%sp@(FPS_PC+4)
+       addq.l  #\s,%sp@(FPS_PC+4)
+.endm
+
+.macro fp_get_instr_word       dest,label,addr
+       fp_get_instr_data       w,2,\dest,\label,\addr
+.endm
+
+.macro fp_get_instr_long       dest,label,addr
+       fp_get_instr_data       l,4,\dest,\label,\addr
+.endm
+
+/* These macros are used to read from/write to user space
+ * on error we jump to the fixup section, load the fault
+ * address into %a0 and jump to the exit.
+ * (derived from <asm/uaccess.h>)
+ */
+.macro getuser size,src,dest,label,addr
+|      printf  ,"[\size<%08x]",1,\addr
+.Lu1\@:        moves\size      \src,\dest
+
+       .section .fixup,"ax"
+       .even
+.Lu2\@:        move.l  \addr,%a0
+       jra     \label
+       .previous
+
+       .section __ex_table,"a"
+       .align  4
+       .long   .Lu1\@,.Lu2\@
+       .previous
+.endm
+
+.macro putuser size,src,dest,label,addr
+|      printf  ,"[\size>%08x]",1,\addr
+.Lu1\@:        moves\size      \src,\dest
+.Lu2\@:
+
+       .section .fixup,"ax"
+       .even
+.Lu3\@:        move.l  \addr,%a0
+       jra     \label
+       .previous
+
+       .section __ex_table,"a"
+       .align  4
+       .long   .Lu1\@,.Lu3\@
+       .long   .Lu2\@,.Lu3\@
+       .previous
+.endm
+
+
+.macro movestack       nr,arg1,arg2,arg3,arg4,arg5
+       .if     \nr
+       movestack       (\nr-1),\arg2,\arg3,\arg4,\arg5
+       move.l  \arg1,-(%sp)
+       .endif
+.endm
+
+.macro printf  bit=-1,string,nr=0,arg1,arg2,arg3,arg4,arg5
+#ifdef FPU_EMU_DEBUG
+       .data
+.Lpdata\@:
+       .string "\string"
+       .previous
+
+       movem.l %d0/%d1/%a0/%a1,-(%sp)
+       .if     \bit+1
+#if 0
+       moveq   #\bit,%d0
+       andw    #7,%d0
+       btst    %d0,fp_debugprint+((31-\bit)/8)
+#else
+       btst    #\bit,fp_debugprint+((31-\bit)/8)
+#endif
+       jeq     .Lpskip\@
+       .endif
+       movestack       \nr,\arg1,\arg2,\arg3,\arg4,\arg5
+       pea     .Lpdata\@
+       jsr     printk
+       lea     ((\nr+1)*4,%sp),%sp
+.Lpskip\@:
+       movem.l (%sp)+,%d0/%d1/%a0/%a1
+#endif
+.endm
+
+.macro printx  bit,fp
+#ifdef FPU_EMU_DEBUG
+       movem.l %d0/%a0,-(%sp)
+       lea     \fp,%a0
+#if 0
+       moveq   #'+',%d0
+       tst.w   (%a0)
+       jeq     .Lx1\@
+       moveq   #'-',%d0
+.Lx1\@:        printf  \bit," %c",1,%d0
+       move.l  (4,%a0),%d0
+       bclr    #31,%d0
+       jne     .Lx2\@
+       printf  \bit,"0."
+       jra     .Lx3\@
+.Lx2\@:        printf  \bit,"1."
+.Lx3\@:        printf  \bit,"%08x%08x",2,%d0,%a0@(8)
+       move.w  (2,%a0),%d0
+       ext.l   %d0
+       printf  \bit,"E%04x",1,%d0
+#else
+       printf  \bit," %08x%08x%08x",3,%a0@,%a0@(4),%a0@(8)
+#endif
+       movem.l (%sp)+,%d0/%a0
+#endif
+.endm
+
+.macro debug   instr,args
+#ifdef FPU_EMU_DEBUG
+       \instr  \args
+#endif
+.endm
+
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_FRV_MATH_EMU_H */
+
diff --git a/include/asm-frv/mb-regs.h b/include/asm-frv/mb-regs.h
new file mode 100644 (file)
index 0000000..c8f575f
--- /dev/null
@@ -0,0 +1,185 @@
+/* mb-regs.h: motherboard registers
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_MB_REGS_H
+#define _ASM_MB_REGS_H
+
+#include <asm/cpu-irqs.h>
+#include <asm/sections.h>
+#include <asm/mem-layout.h>
+
+#define __region_IO    KERNEL_IO_START /* the region from 0xe0000000 to 0xffffffff has suitable
+                                        * protection laid over the top for use in memory-mapped
+                                        * I/O
+                                        */
+
+#define __region_CS0   0xff000000      /* Boot ROMs area */
+
+#ifdef CONFIG_MB93091_VDK
+/*
+ * VDK motherboard and CPU card specific stuff
+ */
+
+#include <asm/mb93091-fpga-irqs.h>
+
+#define IRQ_CPU_MB93493_0      IRQ_CPU_EXTERNAL0
+#define IRQ_CPU_MB93493_1      IRQ_CPU_EXTERNAL1
+
+#define __region_CS2   0xe0000000      /* SLBUS/PCI I/O space */
+#define __region_CS2_M         0x0fffffff /* mask */
+#define __region_CS2_C         0x00000000 /* control */
+#define __region_CS5   0xf0000000      /* MB93493 CSC area (DAV daughter board) */
+#define __region_CS5_M         0x00ffffff
+#define __region_CS5_C         0x00010000
+#define __region_CS7   0xf1000000      /* CB70 CPU-card PCMCIA port I/O space */
+#define __region_CS7_M         0x00ffffff
+#define __region_CS7_C         0x00410701
+#define __region_CS1   0xfc000000      /* SLBUS/PCI bridge control registers */
+#define __region_CS1_M         0x000fffff
+#define __region_CS1_C         0x00000000
+#define __region_CS6   0xfc100000      /* CB70 CPU-card DM9000 LAN I/O space */
+#define __region_CS6_M         0x000fffff
+#define __region_CS6_C         0x00400707
+#define __region_CS3   0xfc200000      /* MB93493 CSR area (DAV daughter board) */
+#define __region_CS3_M         0x000fffff
+#define __region_CS3_C         0xc8100000
+#define __region_CS4   0xfd000000      /* CB70 CPU-card extra flash space */
+#define __region_CS4_M         0x00ffffff
+#define __region_CS4_C         0x00000f07
+
+#define __region_PCI_IO                (__region_CS2 + 0x04000000UL)
+#define __region_PCI_MEM       (__region_CS2 + 0x08000000UL)
+#define __flush_PCI_writes()                                           \
+do {                                                                   \
+       __builtin_write8((volatile void *) __region_PCI_MEM, 0);        \
+} while(0)
+
+#define __is_PCI_IO(addr) \
+       (((unsigned long)(addr) >> 24) - (__region_PCI_IO >> 24)  < (0x04000000UL >> 24))
+
+#define __is_PCI_MEM(addr) \
+       ((unsigned long)(addr) - __region_PCI_MEM < 0x08000000UL)
+
+#define __get_CLKSW()  ({ *(volatile unsigned long *)(__region_CS2 + 0x0130000cUL) & 0xffUL; })
+#define __get_CLKIN()  (__get_CLKSW() * 125U * 100000U / 24U)
+
+#ifndef __ASSEMBLY__
+extern int __nongprelbss mb93090_mb00_detected;
+#endif
+
+#define __addr_LEDS()          (__region_CS2 + 0x01200004UL)
+#ifdef CONFIG_MB93090_MB00
+#define __set_LEDS(X)                                                  \
+do {                                                                   \
+       if (mb93090_mb00_detected)                                      \
+               __builtin_write32((void *) __addr_LEDS(), ~(X));        \
+} while (0)
+#else
+#define __set_LEDS(X)
+#endif
+
+#define __addr_LCD()           (__region_CS2 + 0x01200008UL)
+#define __get_LCD(B)           __builtin_read32((volatile void *) (B))
+#define __set_LCD(B,X)         __builtin_write32((volatile void *) (B), (X))
+
+#define LCD_D                  0x000000ff              /* LCD data bus */
+#define LCD_RW                 0x00000100              /* LCD R/W signal */
+#define LCD_RS                 0x00000200              /* LCD Register Select */
+#define LCD_E                  0x00000400              /* LCD Start Enable Signal */
+
+#define LCD_CMD_CLEAR          (LCD_E|0x001)
+#define LCD_CMD_HOME           (LCD_E|0x002)
+#define LCD_CMD_CURSOR_INC     (LCD_E|0x004)
+#define LCD_CMD_SCROLL_INC     (LCD_E|0x005)
+#define LCD_CMD_CURSOR_DEC     (LCD_E|0x006)
+#define LCD_CMD_SCROLL_DEC     (LCD_E|0x007)
+#define LCD_CMD_OFF            (LCD_E|0x008)
+#define LCD_CMD_ON(CRSR,BLINK) (LCD_E|0x00c|(CRSR<<1)|BLINK)
+#define LCD_CMD_CURSOR_MOVE_L  (LCD_E|0x010)
+#define LCD_CMD_CURSOR_MOVE_R  (LCD_E|0x014)
+#define LCD_CMD_DISPLAY_SHIFT_L        (LCD_E|0x018)
+#define LCD_CMD_DISPLAY_SHIFT_R        (LCD_E|0x01c)
+#define LCD_CMD_FUNCSET(DL,N,F)        (LCD_E|0x020|(DL<<4)|(N<<3)|(F<<2))
+#define LCD_CMD_SET_CG_ADDR(X) (LCD_E|0x040|X)
+#define LCD_CMD_SET_DD_ADDR(X) (LCD_E|0x080|X)
+#define LCD_CMD_READ_BUSY      (LCD_E|LCD_RW)
+#define LCD_DATA_WRITE(X)      (LCD_E|LCD_RS|(X))
+#define LCD_DATA_READ          (LCD_E|LCD_RS|LCD_RW)
+
+#else
+/*
+ * PDK unit specific stuff
+ */
+
+#include <asm/mb93093-fpga-irqs.h>
+
+#define IRQ_CPU_MB93493_0      IRQ_CPU_EXTERNAL0
+#define IRQ_CPU_MB93493_1      IRQ_CPU_EXTERNAL1
+
+#define __region_CS5   0xf0000000      /* MB93493 CSC area (DAV daughter board) */
+#define __region_CS5_M         0x00ffffff /* mask */
+#define __region_CS5_C         0x00010000 /* control */
+#define __region_CS2   0x20000000      /* FPGA registers */
+#define __region_CS2_M         0x000fffff
+#define __region_CS2_C         0x00000000
+#define __region_CS1   0xfc100000      /* LAN registers */
+#define __region_CS1_M         0x000fffff
+#define __region_CS1_C         0x00010404
+#define __region_CS3   0xfc200000      /* MB93493 CSR area (DAV daughter board) */
+#define __region_CS3_M         0x000fffff
+#define __region_CS3_C         0xc8000000
+#define __region_CS4   0xfd000000      /* extra ROMs area */
+#define __region_CS4_M         0x00ffffff
+#define __region_CS4_C         0x00000f07
+
+#define __region_CS6   0xfe000000      /* not used - hide behind CPU resource I/O regs */
+#define __region_CS6_M         0x000fffff
+#define __region_CS6_C         0x00000f07
+#define __region_CS7   0xfe000000      /* not used - hide behind CPU resource I/O regs */
+#define __region_CS7_M         0x000fffff
+#define __region_CS7_C         0x00000f07
+
+#define __is_PCI_IO(addr)      0       /* no PCI */
+#define __is_PCI_MEM(addr)     0
+#define __region_PCI_IO                0
+#define __region_PCI_MEM       0
+#define __flush_PCI_writes()   do { } while(0)
+
+#define __get_CLKSW()          0UL
+#define __get_CLKIN()          66000000UL
+
+#define __addr_LEDS()          (__region_CS2 + 0x00000023UL)
+#define __set_LEDS(X)          __builtin_write8((volatile void *) __addr_LEDS(), (X))
+
+#define __addr_FPGATR()                (__region_CS2 + 0x00000030UL)
+#define __set_FPGATR(X)                __builtin_write32((volatile void *) __addr_FPGATR(), (X))
+#define __get_FPGATR()         __builtin_read32((volatile void *) __addr_FPGATR())
+
+#define MB93093_FPGA_FPGATR_AUDIO_CLK  0x00000003
+
+#define __set_FPGATR_AUDIO_CLK(V) \
+       __set_FPGATR((__get_FPGATR() & ~MB93093_FPGA_FPGATR_AUDIO_CLK) | (V))
+
+#define MB93093_FPGA_FPGATR_AUDIO_CLK_OFF      0x0
+#define MB93093_FPGA_FPGATR_AUDIO_CLK_11MHz    0x1
+#define MB93093_FPGA_FPGATR_AUDIO_CLK_12MHz    0x2
+#define MB93093_FPGA_FPGATR_AUDIO_CLK_02MHz    0x3
+
+#define MB93093_FPGA_SWR_PUSHSWMASK    (0x1F<<26)
+#define MB93093_FPGA_SWR_PUSHSW4       (1<<29)
+
+#define __addr_FPGA_SWR                ((volatile void *)(__region_CS2 + 0x28UL))
+#define __get_FPGA_PUSHSW1_5() (__builtin_read32(__addr_FPGA_SWR) & MB93093_FPGA_SWR_PUSHSWMASK)
+
+
+#endif
+
+#endif /* _ASM_MB_REGS_H */
diff --git a/include/asm-frv/mb86943a.h b/include/asm-frv/mb86943a.h
new file mode 100644 (file)
index 0000000..b89fd0b
--- /dev/null
@@ -0,0 +1,39 @@
+/* mb86943a.h: MB86943 SPARClite <-> PCI bridge registers
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_MB86943A_H
+#define _ASM_MB86943A_H
+
+#include <asm/mb-regs.h>
+
+#define __reg_MB86943_sl_ctl           *(volatile uint32_t *) (__region_CS1 + 0x00)
+
+#define MB86943_SL_CTL_BUS_WIDTH_64    0x00000001
+#define MB86943_SL_CTL_AS_HOST         0x00000002
+#define MB86943_SL_CTL_DRCT_MASTER_SWAP        0x00000004
+#define MB86943_SL_CTL_DRCT_SLAVE_SWAP 0x00000008
+#define MB86943_SL_CTL_PCI_CONFIG_SWAP 0x00000010
+#define MB86943_SL_CTL_ECS0_ENABLE     0x00000020
+#define MB86943_SL_CTL_ECS1_ENABLE     0x00000040
+#define MB86943_SL_CTL_ECS2_ENABLE     0x00000080
+
+#define __reg_MB86943_ecs_ctl(N)       *(volatile uint32_t *) (__region_CS1 + 0x08 + (0x08*(N)))
+#define __reg_MB86943_ecs_range(N)     *(volatile uint32_t *) (__region_CS1 + 0x20 + (0x10*(N)))
+#define __reg_MB86943_ecs_base(N)      *(volatile uint32_t *) (__region_CS1 + 0x28 + (0x10*(N)))
+
+#define __reg_MB86943_sl_pci_io_range  *(volatile uint32_t *) (__region_CS1 + 0x50)
+#define __reg_MB86943_sl_pci_io_base   *(volatile uint32_t *) (__region_CS1 + 0x58)
+#define __reg_MB86943_sl_pci_mem_range *(volatile uint32_t *) (__region_CS1 + 0x60)
+#define __reg_MB86943_sl_pci_mem_base  *(volatile uint32_t *) (__region_CS1 + 0x68)
+#define __reg_MB86943_pci_sl_io_base   *(volatile uint32_t *) (__region_CS1 + 0x70)
+#define __reg_MB86943_pci_sl_mem_base  *(volatile uint32_t *) (__region_CS1 + 0x78)
+
+#endif /* _ASM_MB86943A_H */
diff --git a/include/asm-frv/mb93091-fpga-irqs.h b/include/asm-frv/mb93091-fpga-irqs.h
new file mode 100644 (file)
index 0000000..341bfc5
--- /dev/null
@@ -0,0 +1,44 @@
+/* mb93091-fpga-irqs.h: MB93091 CPU board FPGA IRQs
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_MB93091_FPGA_IRQS_H
+#define _ASM_MB93091_FPGA_IRQS_H
+
+#ifndef __ASSEMBLY__
+
+#include <asm/irq-routing.h>
+
+#define IRQ_BASE_FPGA          (NR_IRQ_ACTIONS_PER_GROUP * 1)
+
+/* IRQ IDs presented to drivers */
+enum {
+       IRQ_FPGA__UNUSED                        = IRQ_BASE_FPGA,
+       IRQ_FPGA_SYSINT_BUS_EXPANSION_1,
+       IRQ_FPGA_SL_BUS_EXPANSION_2,
+       IRQ_FPGA_PCI_INTD,
+       IRQ_FPGA_PCI_INTC,
+       IRQ_FPGA_PCI_INTB,
+       IRQ_FPGA_PCI_INTA,
+       IRQ_FPGA_SL_BUS_EXPANSION_7,
+       IRQ_FPGA_SYSINT_BUS_EXPANSION_8,
+       IRQ_FPGA_SL_BUS_EXPANSION_9,
+       IRQ_FPGA_MB86943_PCI_INTA,
+       IRQ_FPGA_MB86943_SLBUS_SIDE,
+       IRQ_FPGA_RTL8029_INTA,
+       IRQ_FPGA_SYSINT_BUS_EXPANSION_13,
+       IRQ_FPGA_SL_BUS_EXPANSION_14,
+       IRQ_FPGA_NMI,
+};
+
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* _ASM_MB93091_FPGA_IRQS_H */
diff --git a/include/asm-frv/mb93093-fpga-irqs.h b/include/asm-frv/mb93093-fpga-irqs.h
new file mode 100644 (file)
index 0000000..1e0f11c
--- /dev/null
@@ -0,0 +1,31 @@
+/* mb93093-fpga-irqs.h: MB93093 CPU board FPGA IRQs
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_MB93093_FPGA_IRQS_H
+#define _ASM_MB93093_FPGA_IRQS_H
+
+#ifndef __ASSEMBLY__
+
+#include <asm/irq-routing.h>
+
+#define IRQ_BASE_FPGA          (NR_IRQ_ACTIONS_PER_GROUP * 1)
+
+/* IRQ IDs presented to drivers */
+enum {
+       IRQ_FPGA_PUSH_BUTTON_SW1_5              = IRQ_BASE_FPGA + 8,
+       IRQ_FPGA_ROCKER_C_SW8                   = IRQ_BASE_FPGA + 9,
+       IRQ_FPGA_ROCKER_C_SW9                   = IRQ_BASE_FPGA + 10,
+};
+
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* _ASM_MB93093_FPGA_IRQS_H */
diff --git a/include/asm-frv/mb93493-irqs.h b/include/asm-frv/mb93493-irqs.h
new file mode 100644 (file)
index 0000000..15096e7
--- /dev/null
@@ -0,0 +1,52 @@
+/* mb93493-irqs.h: MB93493 companion chip IRQs
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_MB93493_IRQS_H
+#define _ASM_MB93493_IRQS_H
+
+#ifndef __ASSEMBLY__
+
+#include <asm/irq-routing.h>
+
+#define IRQ_BASE_MB93493       (NR_IRQ_ACTIONS_PER_GROUP * 2)
+
+/* IRQ IDs presented to drivers */
+enum {
+       IRQ_MB93493_VDC                 = IRQ_BASE_MB93493 + 0,
+       IRQ_MB93493_VCC                 = IRQ_BASE_MB93493 + 1,
+       IRQ_MB93493_AUDIO_OUT           = IRQ_BASE_MB93493 + 2,
+       IRQ_MB93493_I2C_0               = IRQ_BASE_MB93493 + 3,
+       IRQ_MB93493_I2C_1               = IRQ_BASE_MB93493 + 4,
+       IRQ_MB93493_USB                 = IRQ_BASE_MB93493 + 5,
+       IRQ_MB93493_LOCAL_BUS           = IRQ_BASE_MB93493 + 7,
+       IRQ_MB93493_PCMCIA              = IRQ_BASE_MB93493 + 8,
+       IRQ_MB93493_GPIO                = IRQ_BASE_MB93493 + 9,
+       IRQ_MB93493_AUDIO_IN            = IRQ_BASE_MB93493 + 10,
+};
+
+/* IRQ multiplexor mappings */
+#define ROUTE_VIA_IRQ0 0       /* route IRQ by way of CPU external IRQ 0 */
+#define ROUTE_VIA_IRQ1 1       /* route IRQ by way of CPU external IRQ 1 */
+
+#define IRQ_MB93493_VDC_ROUTE          ROUTE_VIA_IRQ0
+#define IRQ_MB93493_VCC_ROUTE          ROUTE_VIA_IRQ1
+#define IRQ_MB93493_AUDIO_OUT_ROUTE    ROUTE_VIA_IRQ1
+#define IRQ_MB93493_I2C_0_ROUTE                ROUTE_VIA_IRQ1
+#define IRQ_MB93493_I2C_1_ROUTE                ROUTE_VIA_IRQ1
+#define IRQ_MB93493_USB_ROUTE          ROUTE_VIA_IRQ1
+#define IRQ_MB93493_LOCAL_BUS_ROUTE    ROUTE_VIA_IRQ1
+#define IRQ_MB93493_PCMCIA_ROUTE       ROUTE_VIA_IRQ1
+#define IRQ_MB93493_GPIO_ROUTE         ROUTE_VIA_IRQ1
+#define IRQ_MB93493_AUDIO_IN_ROUTE     ROUTE_VIA_IRQ1
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* _ASM_MB93493_IRQS_H */
diff --git a/include/asm-frv/mb93493-regs.h b/include/asm-frv/mb93493-regs.h
new file mode 100644 (file)
index 0000000..c54aa9d
--- /dev/null
@@ -0,0 +1,279 @@
+/* mb93493-regs.h: MB93493 companion chip registers
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_MB93493_REGS_H
+#define _ASM_MB93493_REGS_H
+
+#include <asm/mb-regs.h>
+#include <asm/mb93493-irqs.h>
+
+#define __get_MB93493(X)       ({ *(volatile unsigned long *)(__region_CS3 + (X)); })
+
+#define __set_MB93493(X,V)                                             \
+do {                                                                   \
+       *(volatile unsigned long *)(__region_CS3 + (X)) = (V); mb();    \
+} while(0)
+
+#define __get_MB93493_STSR(X)  __get_MB93493(0x3c0 + (X) * 4)
+#define __set_MB93493_STSR(X,V)        __set_MB93493(0x3c0 + (X) * 4, (V))
+#define MB93493_STSR_EN
+
+#define __get_MB93493_IQSR(X)  __get_MB93493(0x3d0 + (X) * 4)
+#define __set_MB93493_IQSR(X,V)        __set_MB93493(0x3d0 + (X) * 4, (V))
+
+#define __get_MB93493_DQSR(X)  __get_MB93493(0x3e0 + (X) * 4)
+#define __set_MB93493_DQSR(X,V)        __set_MB93493(0x3e0 + (X) * 4, (V))
+
+#define __get_MB93493_LBSER()  __get_MB93493(0x3f0)
+#define __set_MB93493_LBSER(V) __set_MB93493(0x3f0, (V))
+
+#define MB93493_LBSER_VDC      0x00010000
+#define MB93493_LBSER_VCC      0x00020000
+#define MB93493_LBSER_AUDIO    0x00040000
+#define MB93493_LBSER_I2C_0    0x00080000
+#define MB93493_LBSER_I2C_1    0x00100000
+#define MB93493_LBSER_USB      0x00200000
+#define MB93493_LBSER_GPIO     0x00800000
+#define MB93493_LBSER_PCMCIA   0x01000000
+
+#define __get_MB93493_LBSR()   __get_MB93493(0x3fc)
+#define __set_MB93493_LBSR(V)  __set_MB93493(0x3fc, (V))
+
+/*
+ * video display controller
+ */
+#define __get_MB93493_VDC(X)   __get_MB93493(MB93493_VDC_##X)
+#define __set_MB93493_VDC(X,V) __set_MB93493(MB93493_VDC_##X, (V))
+
+#define MB93493_VDC_RCURSOR    0x140   /* cursor position */
+#define MB93493_VDC_RCT1       0x144   /* cursor colour 1 */
+#define MB93493_VDC_RCT2       0x148   /* cursor colour 2 */
+#define MB93493_VDC_RHDC       0x150   /* horizontal display period */
+#define MB93493_VDC_RH_MARGINS 0x154   /* horizontal margin sizes */
+#define MB93493_VDC_RVDC       0x158   /* vertical display period */
+#define MB93493_VDC_RV_MARGINS 0x15c   /* vertical margin sizes */
+#define MB93493_VDC_RC         0x170   /* VDC control */
+#define MB93493_VDC_RCLOCK     0x174   /* clock divider, DMA req delay */
+#define MB93493_VDC_RBLACK     0x178   /* black insert sizes */
+#define MB93493_VDC_RS         0x17c   /* VDC status */
+
+#define __addr_MB93493_VDC_BCI(X)  ({ (volatile unsigned long *)(__region_CS3 + 0x000 + (X)); })
+#define __addr_MB93493_VDC_TPO(X)  (__region_CS3 + 0x1c0 + (X))
+
+#define VDC_TPO_WIDTH          32
+
+#define VDC_RC_DSR             0x00000080      /* VDC master reset */
+
+#define VDC_RS_IT              0x00060000      /* interrupt indicators */
+#define VDC_RS_IT_UNDERFLOW    0x00040000      /* - underflow event */
+#define VDC_RS_IT_VSYNC                0x00020000      /* - VSYNC event */
+#define VDC_RS_DFI             0x00010000      /* current interlace field number */
+#define VDC_RS_DFI_TOP         0x00000000      /* - top field */
+#define VDC_RS_DFI_BOTTOM      0x00010000      /* - bottom field */
+#define VDC_RS_DCSR            0x00000010      /* cursor state */
+#define VDC_RS_DCM             0x00000003      /* display mode */
+#define VDC_RS_DCM_DISABLED    0x00000000      /* - display disabled */
+#define VDC_RS_DCM_STOPPED     0x00000001      /* - VDC stopped */
+#define VDC_RS_DCM_FREERUNNING 0x00000002      /* - VDC free-running */
+#define VDC_RS_DCM_TRANSFERRING        0x00000003      /* - data being transferred to VDC */
+
+/*
+ * video capture controller
+ */
+#define __get_MB93493_VCC(X)   __get_MB93493(MB93493_VCC_##X)
+#define __set_MB93493_VCC(X,V) __set_MB93493(MB93493_VCC_##X, (V))
+
+#define MB93493_VCC_RREDUCT    0x104   /* reduction rate */
+#define MB93493_VCC_RHY                0x108   /* horizontal brightness filter coefficients */
+#define MB93493_VCC_RHC                0x10c   /* horizontal colour-difference filter coefficients */
+#define MB93493_VCC_RHSIZE     0x110   /* horizontal cycle sizes */
+#define MB93493_VCC_RHBC       0x114   /* horizontal back porch size */
+#define MB93493_VCC_RVCC       0x118   /* vertical capture period */
+#define MB93493_VCC_RVBC       0x11c   /* vertical back porch period */
+#define MB93493_VCC_RV         0x120   /* vertical filter coefficients */
+#define MB93493_VCC_RDTS       0x128   /* DMA transfer size */
+#define MB93493_VCC_RDTS_4B    0x01000000      /* 4-byte transfer */
+#define MB93493_VCC_RDTS_32B   0x03000000      /* 32-byte transfer */
+#define MB93493_VCC_RDTS_SHIFT 24
+#define MB93493_VCC_RCC                0x130   /* VCC control */
+#define MB93493_VCC_RIS                0x134   /* VCC interrupt status */
+
+#define __addr_MB93493_VCC_TPI(X)  (__region_CS3 + 0x180 + (X))
+
+#define VCC_RHSIZE_RHCC                0x000007ff
+#define VCC_RHSIZE_RHCC_SHIFT  0
+#define VCC_RHSIZE_RHTCC       0x0fff0000
+#define VCC_RHSIZE_RHTCC_SHIFT 16
+
+#define VCC_RVBC_RVBC          0x00003f00
+#define VCC_RVBC_RVBC_SHIFT    8
+
+#define VCC_RREDUCT_RHR                0x07ff0000
+#define VCC_RREDUCT_RHR_SHIFT  16
+#define VCC_RREDUCT_RVR                0x000007ff
+#define VCC_RREDUCT_RVR_SHIFT  0
+
+#define VCC_RCC_CE             0x00000001      /* VCC enable */
+#define VCC_RCC_CS             0x00000002      /* request video capture start */
+#define VCC_RCC_CPF            0x0000000c      /* pixel format */
+#define VCC_RCC_CPF_YCBCR_16   0x00000000      /* - YCbCr 4:2:2 16-bit format */
+#define VCC_RCC_CPF_RGB                0x00000004      /* - RGB 4:4:4 format */
+#define VCC_RCC_CPF_YCBCR_24   0x00000008      /* - YCbCr 4:2:2 24-bit format */
+#define VCC_RCC_CPF_BT656      0x0000000c      /* - ITU R-BT.656 format */
+#define VCC_RCC_CPF_SHIFT      2
+#define VCC_RCC_CSR            0x00000080      /* request reset */
+#define VCC_RCC_HSIP           0x00000100      /* HSYNC polarity */
+#define VCC_RCC_HSIP_LOACT     0x00000000      /* - low active */
+#define VCC_RCC_HSIP_HIACT     0x00000100      /* - high active */
+#define VCC_RCC_VSIP           0x00000200      /* VSYNC polarity */
+#define VCC_RCC_VSIP_LOACT     0x00000000      /* - low active */
+#define VCC_RCC_VSIP_HIACT     0x00000200      /* - high active */
+#define VCC_RCC_CIE            0x00000800      /* interrupt enable */
+#define VCC_RCC_CFP            0x00001000      /* RGB pixel packing */
+#define VCC_RCC_CFP_4TO3       0x00000000      /* - pack 4 pixels into 3 words */
+#define VCC_RCC_CFP_1TO1       0x00001000      /* - pack 1 pixel into 1 words */
+#define VCC_RCC_CSM            0x00006000      /* interlace specification */
+#define VCC_RCC_CSM_ONEPASS    0x00002000      /* - non-interlaced */
+#define VCC_RCC_CSM_INTERLACE  0x00004000      /* - interlaced */
+#define VCC_RCC_CSM_SHIFT      13
+#define VCC_RCC_ES             0x00008000      /* capture start polarity */
+#define VCC_RCC_ES_NEG         0x00000000      /* - negative edge */
+#define VCC_RCC_ES_POS         0x00008000      /* - positive edge */
+#define VCC_RCC_IFI            0x00080000      /* inferlace field evaluation reverse */
+#define VCC_RCC_FDTS           0x00300000      /* interlace field start */
+#define VCC_RCC_FDTS_3_8       0x00000000      /* - 3/8 of horizontal entire cycle */
+#define VCC_RCC_FDTS_1_4       0x00100000      /* - 1/4 of horizontal entire cycle */
+#define VCC_RCC_FDTS_7_16      0x00200000      /* - 7/16 of horizontal entire cycle */
+#define VCC_RCC_FDTS_SHIFT     20
+#define VCC_RCC_MOV            0x00400000      /* test bit - always set to 1 */
+#define VCC_RCC_STP            0x00800000      /* request video capture stop */
+#define VCC_RCC_TO             0x01000000      /* input during top-field only */
+
+#define VCC_RIS_VSYNC          0x01000000      /* VSYNC interrupt */
+#define VCC_RIS_OV             0x02000000      /* overflow interrupt */
+#define VCC_RIS_BOTTOM         0x08000000      /* interlace bottom field */
+#define VCC_RIS_STARTED                0x10000000      /* capture started */
+
+/*
+ * I2C
+ */
+#define MB93493_I2C_BSR        0x340           /* bus status */
+#define MB93493_I2C_BCR                0x344           /* bus control */
+#define MB93493_I2C_CCR                0x348           /* clock control */
+#define MB93493_I2C_ADR                0x34c           /* address */
+#define MB93493_I2C_DTR                0x350           /* data */
+#define MB93493_I2C_BC2R       0x35c           /* bus control 2 */
+
+#define __addr_MB93493_I2C(port,X)   (__region_CS3 + MB93493_I2C_##X + ((port)*0x20))
+#define __get_MB93493_I2C(port,X)    __get_MB93493(MB93493_I2C_##X + ((port)*0x20))
+#define __set_MB93493_I2C(port,X,V)  __set_MB93493(MB93493_I2C_##X + ((port)*0x20), (V))
+
+#define I2C_BSR_BB     (1 << 7)
+
+/*
+ * audio controller (I2S) registers
+ */
+#define __get_MB93493_I2S(X)   __get_MB93493(MB93493_I2S_##X)
+#define __set_MB93493_I2S(X,V) __set_MB93493(MB93493_I2S_##X, (V))
+
+#define MB93493_I2S_ALDR       0x300           /* L-channel data */
+#define MB93493_I2S_ARDR       0x304           /* R-channel data */
+#define MB93493_I2S_APDR       0x308           /* 16-bit packed data */
+#define MB93493_I2S_AISTR      0x310           /* status */
+#define MB93493_I2S_AICR       0x314           /* control */
+
+#define __addr_MB93493_I2S_ALDR(X)     (__region_CS3 + MB93493_I2S_ALDR + (X))
+#define __addr_MB93493_I2S_ARDR(X)     (__region_CS3 + MB93493_I2S_ARDR + (X))
+#define __addr_MB93493_I2S_APDR(X)     (__region_CS3 + MB93493_I2S_APDR + (X))
+#define __addr_MB93493_I2S_ADR(X)      (__region_CS3 + 0x320 + (X))
+
+#define I2S_AISTR_OTST         0x00000003      /* status of output data transfer */
+#define I2S_AISTR_OTR          0x00000010      /* output transfer request pending */
+#define I2S_AISTR_OUR          0x00000020      /* output FIFO underrun detected */
+#define I2S_AISTR_OOR          0x00000040      /* output FIFO overrun detected */
+#define I2S_AISTR_ODS          0x00000100      /* output DMA transfer size */
+#define I2S_AISTR_ODE          0x00000400      /* output DMA transfer request enable */
+#define I2S_AISTR_OTRIE                0x00001000      /* output transfer request interrupt enable */
+#define I2S_AISTR_OURIE                0x00002000      /* output FIFO underrun interrupt enable */
+#define I2S_AISTR_OORIE                0x00004000      /* output FIFO overrun interrupt enable */
+#define I2S_AISTR__OUT_MASK    0x00007570
+#define I2S_AISTR_ITST         0x00030000      /* status of input data transfer */
+#define I2S_AISTR_ITST_SHIFT   16
+#define I2S_AISTR_ITR          0x00100000      /* input transfer request pending */
+#define I2S_AISTR_IUR          0x00200000      /* input FIFO underrun detected */
+#define I2S_AISTR_IOR          0x00400000      /* input FIFO overrun detected */
+#define I2S_AISTR_IDS          0x01000000      /* input DMA transfer size */
+#define I2S_AISTR_IDE          0x04000000      /* input DMA transfer request enable */
+#define I2S_AISTR_ITRIE                0x10000000      /* input transfer request interrupt enable */
+#define I2S_AISTR_IURIE                0x20000000      /* input FIFO underrun interrupt enable */
+#define I2S_AISTR_IORIE                0x40000000      /* input FIFO overrun interrupt enable */
+#define I2S_AISTR__IN_MASK     0x75700000
+
+#define I2S_AICR_MI            0x00000001      /* mono input requested */
+#define I2S_AICR_AMI           0x00000002      /* relation between LRCKI/FS1 and SDI */
+#define I2S_AICR_LRI           0x00000004      /* function of LRCKI pin */
+#define I2S_AICR_SDMI          0x00000070      /* format of input audio data */
+#define I2S_AICR_SDMI_SHIFT    4
+#define I2S_AICR_CLI           0x00000080      /* input FIFO clearing control */
+#define I2S_AICR_IM            0x00000300      /* input state control */
+#define I2S_AICR_IM_SHIFT      8
+#define I2S_AICR__IN_MASK      0x000003f7
+#define I2S_AICR_MO            0x00001000      /* mono output requested */
+#define I2S_AICR_AMO           0x00002000      /* relation between LRCKO/FS0 and SDO */
+#define I2S_AICR_AMO_SHIFT     13
+#define I2S_AICR_LRO           0x00004000      /* function of LRCKO pin */
+#define I2S_AICR_SDMO          0x00070000      /* format of output audio data */
+#define I2S_AICR_SDMO_SHIFT    16
+#define I2S_AICR_CLO           0x00080000      /* output FIFO clearing control */
+#define I2S_AICR_OM            0x00100000      /* output state control */
+#define I2S_AICR__OUT_MASK     0x001f7000
+#define I2S_AICR_DIV           0x03000000      /* frequency division rate */
+#define I2S_AICR_DIV_SHIFT     24
+#define I2S_AICR_FL            0x20000000      /* frame length */
+#define I2S_AICR_FS            0x40000000      /* frame sync method */
+#define I2S_AICR_ME            0x80000000      /* master enable */
+
+/*
+ * PCMCIA
+ */
+#define __addr_MB93493_PCMCIA(X)  ((volatile unsigned long *)(__region_CS5 + (X)))
+
+/*
+ * GPIO
+ */
+#define __get_MB93493_GPIO_PDR(X)      __get_MB93493(0x380 + (X) * 0xc0)
+#define __set_MB93493_GPIO_PDR(X,V)    __set_MB93493(0x380 + (X) * 0xc0, (V))
+
+#define __get_MB93493_GPIO_GPDR(X)     __get_MB93493(0x384 + (X) * 0xc0)
+#define __set_MB93493_GPIO_GPDR(X,V)   __set_MB93493(0x384 + (X) * 0xc0, (V))
+
+#define __get_MB93493_GPIO_SIR(X)      __get_MB93493(0x388 + (X) * 0xc0)
+#define __set_MB93493_GPIO_SIR(X,V)    __set_MB93493(0x388 + (X) * 0xc0, (V))
+
+#define __get_MB93493_GPIO_SOR(X)      __get_MB93493(0x38c + (X) * 0xc0)
+#define __set_MB93493_GPIO_SOR(X,V)    __set_MB93493(0x38c + (X) * 0xc0, (V))
+
+#define __get_MB93493_GPIO_PDSR(X)     __get_MB93493(0x390 + (X) * 0xc0)
+#define __set_MB93493_GPIO_PDSR(X,V)   __set_MB93493(0x390 + (X) * 0xc0, (V))
+
+#define __get_MB93493_GPIO_PDCR(X)     __get_MB93493(0x394 + (X) * 0xc0)
+#define __set_MB93493_GPIO_PDCR(X,V)   __set_MB93493(0x394 + (X) * 0xc0, (V))
+
+#define __get_MB93493_GPIO_INTST(X)    __get_MB93493(0x398 + (X) * 0xc0)
+#define __set_MB93493_GPIO_INTST(X,V)  __set_MB93493(0x398 + (X) * 0xc0, (V))
+
+#define __get_MB93493_GPIO_IEHL(X)     __get_MB93493(0x39c + (X) * 0xc0)
+#define __set_MB93493_GPIO_IEHL(X,V)   __set_MB93493(0x39c + (X) * 0xc0, (V))
+
+#define __get_MB93493_GPIO_IELH(X)     __get_MB93493(0x3a0 + (X) * 0xc0)
+#define __set_MB93493_GPIO_IELH(X,V)   __set_MB93493(0x3a0 + (X) * 0xc0, (V))
+
+#endif /* _ASM_MB93493_REGS_H */
diff --git a/include/asm-frv/mem-layout.h b/include/asm-frv/mem-layout.h
new file mode 100644 (file)
index 0000000..a025dd4
--- /dev/null
@@ -0,0 +1,78 @@
+/* mem-layout.h: memory layout
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_MEM_LAYOUT_H
+#define _ASM_MEM_LAYOUT_H
+
+#ifndef __ASSEMBLY__
+#define __UL(X)        ((unsigned long) (X))
+#else
+#define __UL(X)        (X)
+#endif
+
+/*
+ * PAGE_SHIFT determines the page size
+ */
+#define PAGE_SHIFT                     14
+
+#ifndef __ASSEMBLY__
+#define PAGE_SIZE                      (1UL << PAGE_SHIFT)
+#else
+#define PAGE_SIZE                      (1 << PAGE_SHIFT)
+#endif
+
+#define PAGE_MASK                      (~(PAGE_SIZE-1))
+
+/*****************************************************************************/
+/*
+ * virtual memory layout from kernel's point of view
+ */
+#define PAGE_OFFSET                    ((unsigned long) &__page_offset)
+
+#ifdef CONFIG_MMU
+
+/* see Documentation/fujitsu/frv/mmu-layout.txt */
+#define KERNEL_LOWMEM_START            __UL(0xc0000000)
+#define KERNEL_LOWMEM_END              __UL(0xd0000000)
+#define VMALLOC_START                  __UL(0xd0000000)
+#define VMALLOC_END                    __UL(0xd8000000)
+#define PKMAP_BASE                     __UL(0xd8000000)
+#define PKMAP_END                      __UL(0xdc000000)
+#define KMAP_ATOMIC_SECONDARY_FRAME    __UL(0xdc000000)
+#define KMAP_ATOMIC_PRIMARY_FRAME      __UL(0xdd000000)
+
+#endif
+
+#define KERNEL_IO_START                        __UL(0xe0000000)
+
+
+/*****************************************************************************/
+/*
+ * memory layout from userspace's point of view
+ */
+#define BRK_BASE                       __UL(2 * 1024 * 1024 + PAGE_SIZE)
+#define STACK_TOP                      __UL(2 * 1024 * 1024)
+
+/* userspace process size */
+#ifdef CONFIG_MMU
+#define TASK_SIZE                      (PAGE_OFFSET)
+#else
+#define TASK_SIZE                      __UL(0xFFFFFFFFUL)
+#endif
+
+/* base of area at which unspecified mmaps will start */
+#ifdef CONFIG_BINFMT_ELF_FDPIC
+#define TASK_UNMAPPED_BASE             __UL(16 * 1024 * 1024)
+#else
+#define TASK_UNMAPPED_BASE             __UL(TASK_SIZE / 3)
+#endif
+
+#endif /* _ASM_MEM_LAYOUT_H */
diff --git a/include/asm-frv/mman.h b/include/asm-frv/mman.h
new file mode 100644 (file)
index 0000000..c684720
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef __ASM_MMAN_H__
+#define __ASM_MMAN_H__
+
+#define PROT_READ      0x1             /* page can be read */
+#define PROT_WRITE     0x2             /* page can be written */
+#define PROT_EXEC      0x4             /* page can be executed */
+#define PROT_SEM       0x8             /* page may be used for atomic ops */
+#define PROT_NONE      0x0             /* page can not be accessed */
+#define PROT_GROWSDOWN 0x01000000      /* mprotect flag: extend change to start of growsdown vma */
+#define PROT_GROWSUP   0x02000000      /* mprotect flag: extend change to end of growsup vma */
+
+#define MAP_SHARED     0x01            /* Share changes */
+#define MAP_PRIVATE    0x02            /* Changes are private */
+#define MAP_TYPE       0x0f            /* Mask for type of mapping */
+#define MAP_FIXED      0x10            /* Interpret addr exactly */
+#define MAP_ANONYMOUS  0x20            /* don't use a file */
+
+#define MAP_GROWSDOWN  0x0100          /* stack-like segment */
+#define MAP_DENYWRITE  0x0800          /* ETXTBSY */
+#define MAP_EXECUTABLE 0x1000          /* mark it as an executable */
+#define MAP_LOCKED     0x2000          /* pages are locked */
+#define MAP_NORESERVE  0x4000          /* don't check for reservations */
+#define MAP_POPULATE   0x8000          /* populate (prefault) pagetables */
+#define MAP_NONBLOCK   0x10000         /* do not block on IO */
+
+#define MS_ASYNC       1               /* sync memory asynchronously */
+#define MS_INVALIDATE  2               /* invalidate the caches */
+#define MS_SYNC                4               /* synchronous memory sync */
+
+#define MCL_CURRENT    1               /* lock all current mappings */
+#define MCL_FUTURE     2               /* lock all future mappings */
+
+#define MADV_NORMAL    0x0             /* default page-in behavior */
+#define MADV_RANDOM    0x1             /* page-in minimum required */
+#define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
+#define MADV_WILLNEED  0x3             /* pre-fault pages */
+#define MADV_DONTNEED  0x4             /* discard these pages */
+
+/* compatibility flags */
+#define MAP_ANON       MAP_ANONYMOUS
+#define MAP_FILE       0
+
+#endif /* __ASM_MMAN_H__ */
+
diff --git a/include/asm-frv/mmu.h b/include/asm-frv/mmu.h
new file mode 100644 (file)
index 0000000..22c0371
--- /dev/null
@@ -0,0 +1,42 @@
+/* mmu.h: memory management context for FR-V with or without MMU support
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ASM_MMU_H
+#define _ASM_MMU_H
+
+typedef struct {
+#ifdef CONFIG_MMU
+       struct list_head id_link;               /* link in list of context ID owners */
+       unsigned short  id;                     /* MMU context ID */
+       unsigned short  id_busy;                /* true if ID is in CXNR */
+       unsigned long   itlb_cached_pge;        /* [SCR0] PGE cached for insn TLB handler */
+       unsigned long   itlb_ptd_mapping;       /* [DAMR4] PTD mapping for itlb cached PGE */
+       unsigned long   dtlb_cached_pge;        /* [SCR1] PGE cached for data TLB handler */
+       unsigned long   dtlb_ptd_mapping;       /* [DAMR5] PTD mapping for dtlb cached PGE */
+
+#else
+       struct vm_list_struct   *vmlist;
+       unsigned long           end_brk;
+
+#endif
+
+#ifdef CONFIG_BINFMT_ELF_FDPIC
+       unsigned long   exec_fdpic_loadmap;
+       unsigned long   interp_fdpic_loadmap;
+#endif
+
+} mm_context_t;
+
+#ifdef CONFIG_MMU
+extern int __nongpreldata cxn_pinned;
+extern int cxn_pin_by_pid(pid_t pid);
+#endif
+
+#endif /* _ASM_MMU_H */
diff --git a/include/asm-frv/mmu_context.h b/include/asm-frv/mmu_context.h
new file mode 100644 (file)
index 0000000..4fb9ea3
--- /dev/null
@@ -0,0 +1,50 @@
+/* mmu_context.h: MMU context management routines
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_MMU_CONTEXT_H
+#define _ASM_MMU_CONTEXT_H
+
+#include <linux/config.h>
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+
+static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
+{
+}
+
+#ifdef CONFIG_MMU
+extern int init_new_context(struct task_struct *tsk, struct mm_struct *mm);
+extern void change_mm_context(mm_context_t *old, mm_context_t *ctx, pgd_t *_pgd);
+extern void destroy_context(struct mm_struct *mm);
+
+#else
+#define init_new_context(tsk, mm)              ({ 0; })
+#define change_mm_context(old, ctx, _pml4)     do {} while(0)
+#define destroy_context(mm)                    do {} while(0)
+#endif
+
+#define switch_mm(prev, next, tsk)                                             \
+do {                                                                           \
+       if (prev != next)                                                       \
+               change_mm_context(&prev->context, &next->context, next->pgd);   \
+} while(0)
+
+#define activate_mm(prev, next)                                                \
+do {                                                                   \
+       change_mm_context(&prev->context, &next->context, next->pgd);   \
+} while(0)
+
+#define deactivate_mm(tsk, mm)                 \
+do {                                           \
+} while(0)
+
+#endif
diff --git a/include/asm-frv/module.h b/include/asm-frv/module.h
new file mode 100644 (file)
index 0000000..3223cfa
--- /dev/null
@@ -0,0 +1,20 @@
+/* module.h: FRV module stuff
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ASM_MODULE_H
+#define _ASM_MODULE_H
+
+#define module_map(x)          vmalloc(x)
+#define module_unmap(x)                vfree(x)
+#define module_arch_init(x)    (0)
+#define arch_init_modules(x)   do { } while (0)
+
+#endif /* _ASM_MODULE_H */
+
diff --git a/include/asm-frv/msgbuf.h b/include/asm-frv/msgbuf.h
new file mode 100644 (file)
index 0000000..97ceb55
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _ASM_MSGBUF_H
+#define _ASM_MSGBUF_H
+
+/*
+ * The msqid64_ds structure for FR-V architecture.
+ * Note extra padding because this structure is passed back and forth
+ * between kernel and user space.
+ *
+ * Pad space is left for:
+ * - 64-bit time_t to solve y2038 problem
+ * - 2 miscellaneous 32-bit values
+ */
+
+struct msqid64_ds {
+       struct ipc64_perm       msg_perm;
+       __kernel_time_t         msg_stime;      /* last msgsnd time */
+       unsigned long           __unused1;
+       __kernel_time_t         msg_rtime;      /* last msgrcv time */
+       unsigned long           __unused2;
+       __kernel_time_t         msg_ctime;      /* last change time */
+       unsigned long           __unused3;
+       unsigned long           msg_cbytes;     /* current number of bytes on queue */
+       unsigned long           msg_qnum;       /* number of messages in queue */
+       unsigned long           msg_qbytes;     /* max number of bytes on queue */
+       __kernel_pid_t          msg_lspid;      /* pid of last msgsnd */
+       __kernel_pid_t          msg_lrpid;      /* last receive pid */
+       unsigned long           __unused4;
+       unsigned long           __unused5;
+};
+
+#endif /* _ASM_MSGBUF_H */
+
diff --git a/include/asm-frv/namei.h b/include/asm-frv/namei.h
new file mode 100644 (file)
index 0000000..84ddd64
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * asm/namei.h
+ *
+ * Included from linux/fs/namei.c
+ */
+
+#ifndef __ASM_NAMEI_H
+#define __ASM_NAMEI_H
+
+/* This dummy routine maybe changed to something useful
+ * for /usr/gnemul/ emulation stuff.
+ * Look at asm-sparc/namei.h for details.
+ */
+
+#define __emul_prefix() NULL
+
+#endif
+
diff --git a/include/asm-frv/page.h b/include/asm-frv/page.h
new file mode 100644 (file)
index 0000000..f7914f1
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef _ASM_PAGE_H
+#define _ASM_PAGE_H
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <asm/virtconvert.h>
+#include <asm/mem-layout.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+
+#ifndef __ASSEMBLY__
+
+#define get_user_page(vaddr)                   __get_free_page(GFP_KERNEL)
+#define free_user_page(page, addr)             free_page(addr)
+
+#define clear_page(pgaddr)                     memset((pgaddr), 0, PAGE_SIZE)
+#define copy_page(to,from)                     memcpy((to), (from), PAGE_SIZE)
+
+#define clear_user_page(pgaddr, vaddr, page)   memset((pgaddr), 0, PAGE_SIZE)
+#define copy_user_page(vto, vfrom, vaddr, topg)        memcpy((vto), (vfrom), PAGE_SIZE)
+
+/*
+ * These are used to make use of C type-checking..
+ */
+typedef struct { unsigned long pte;    } pte_t;
+typedef struct { unsigned long ste[64];} pmd_t;
+typedef struct { pmd_t         pue[1]; } pud_t;
+typedef struct { pud_t         pge[1]; } pgd_t;
+typedef struct { unsigned long pgprot; } pgprot_t;
+
+#define pte_val(x)     ((x).pte)
+#define pmd_val(x)     ((x).ste[0])
+#define pud_val(x)     ((x).pue[0])
+#define pgd_val(x)     ((x).pge[0])
+#define pgprot_val(x)  ((x).pgprot)
+
+#define __pte(x)       ((pte_t) { (x) } )
+#define __pmd(x)       ((pmd_t) { (x) } )
+#define __pud(x)       ((pud_t) { (x) } )
+#define __pgd(x)       ((pgd_t) { (x) } )
+#define __pgprot(x)    ((pgprot_t) { (x) } )
+#define PTE_MASK       PAGE_MASK
+
+/* to align the pointer to the (next) page boundary */
+#define PAGE_ALIGN(addr)       (((addr) + PAGE_SIZE - 1) & PAGE_MASK)
+
+/* Pure 2^n version of get_order */
+static inline int get_order(unsigned long size) __attribute_const__;
+static inline int get_order(unsigned long size)
+{
+       int order;
+
+       size = (size - 1) >> (PAGE_SHIFT - 1);
+       order = -1;
+       do {
+               size >>= 1;
+               order++;
+       } while (size);
+       return order;
+}
+
+#define devmem_is_allowed(pfn) 1
+
+#define __pa(vaddr)            virt_to_phys((void *) vaddr)
+#define __va(paddr)            phys_to_virt((unsigned long) paddr)
+
+#define pfn_to_kaddr(pfn)      __va((pfn) << PAGE_SHIFT)
+
+extern unsigned long max_low_pfn;
+extern unsigned long min_low_pfn;
+extern unsigned long max_pfn;
+
+#ifdef CONFIG_MMU
+#define pfn_to_page(pfn)       (mem_map + (pfn))
+#define page_to_pfn(page)      ((unsigned long) ((page) - mem_map))
+#define pfn_valid(pfn)         ((pfn) < max_mapnr)
+
+#else
+#define pfn_to_page(pfn)       (&mem_map[(pfn) - (PAGE_OFFSET >> PAGE_SHIFT)])
+#define page_to_pfn(page)      ((PAGE_OFFSET >> PAGE_SHIFT) + (unsigned long) ((page) - mem_map))
+#define pfn_valid(pfn)         ((pfn) >= min_low_pfn && (pfn) < max_low_pfn)
+
+#endif
+
+#define virt_to_page(kaddr)    pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
+#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
+
+
+#ifdef CONFIG_MMU
+#define VM_DATA_DEFAULT_FLAGS \
+       (VM_READ | VM_WRITE | \
+       ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0 ) | \
+                VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+#endif
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __KERNEL__ */
+
+#ifdef CONFIG_CONTIGUOUS_PAGE_ALLOC
+#define WANT_PAGE_VIRTUAL      1
+#endif
+
+#endif /* _ASM_PAGE_H */
diff --git a/include/asm-frv/param.h b/include/asm-frv/param.h
new file mode 100644 (file)
index 0000000..168381e
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _ASM_PARAM_H
+#define _ASM_PARAM_H
+
+#ifdef __KERNEL__
+#define HZ             1000            /* Internal kernel timer frequency */
+#define USER_HZ                100             /* .. some user interfaces are in "ticks" */
+#define CLOCKS_PER_SEC (USER_HZ)       /* like times() */
+#endif
+
+#ifndef HZ
+#define HZ 100
+#endif
+
+#define EXEC_PAGESIZE  16384
+
+#ifndef NOGROUP
+#define NOGROUP                (-1)
+#endif
+
+#define MAXHOSTNAMELEN         64      /* max length of hostname */
+#define COMMAND_LINE_SIZE      512
+
+#endif /* _ASM_PARAM_H */
diff --git a/include/asm-frv/pci.h b/include/asm-frv/pci.h
new file mode 100644 (file)
index 0000000..a6a4692
--- /dev/null
@@ -0,0 +1,108 @@
+/* pci.h: FR-V specific PCI declarations
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from include/asm-m68k/pci.h
+ *
+ * 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 ASM_PCI_H
+#define        ASM_PCI_H
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <asm/scatterlist.h>
+#include <asm-generic/pci-dma-compat.h>
+#include <asm-generic/pci.h>
+
+struct pci_dev;
+
+#define pcibios_assign_all_busses()    0
+
+static inline void pcibios_add_platform_entries(struct pci_dev *dev)
+{
+}
+
+extern void pcibios_set_master(struct pci_dev *dev);
+
+extern void pcibios_penalize_isa_irq(int irq);
+
+#ifdef CONFIG_MMU
+extern void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle);
+extern void consistent_free(void *vaddr);
+extern void consistent_sync(void *vaddr, size_t size, int direction);
+extern void consistent_sync_page(struct page *page, unsigned long offset,
+                                size_t size, int direction);
+#endif
+
+extern void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
+                                 dma_addr_t *dma_handle);
+
+extern void pci_free_consistent(struct pci_dev *hwdev, size_t size,
+                               void *vaddr, dma_addr_t dma_handle);
+
+/* This is always fine. */
+#define pci_dac_dma_supported(pci_dev, mask)   (1)
+
+/* Return the index of the PCI controller for device PDEV. */
+#define pci_controller_num(PDEV)       (0)
+
+/* The PCI address space does equal the physical memory
+ * address space.  The networking and block device layers use
+ * this boolean for bounce buffer decisions.
+ */
+#define PCI_DMA_BUS_IS_PHYS    (1)
+
+/*
+ *     These are pretty much arbitary with the CoMEM implementation.
+ *     We have the whole address space to ourselves.
+ */
+#define PCIBIOS_MIN_IO         0x100
+#define PCIBIOS_MIN_MEM                0x00010000
+
+/* Make physical memory consistent for a single
+ * streaming mode DMA translation after a transfer.
+ *
+ * If you perform a pci_map_single() but wish to interrogate the
+ * buffer using the cpu, yet do not wish to teardown the PCI dma
+ * mapping, you must call this function before doing so.  At the
+ * next point you give the PCI dma address back to the card, the
+ * device again owns the buffer.
+ */
+static inline void pci_dma_sync_single(struct pci_dev *hwdev,
+                                      dma_addr_t dma_handle,
+                                      size_t size, int direction)
+{
+       if (direction == PCI_DMA_NONE)
+                BUG();
+
+       frv_cache_wback_inv((unsigned long)bus_to_virt(dma_handle),
+                           (unsigned long)bus_to_virt(dma_handle) + size);
+}
+
+/* Make physical memory consistent for a set of streaming
+ * mode DMA translations after a transfer.
+ *
+ * The same as pci_dma_sync_single but for a scatter-gather list,
+ * same rules and usage.
+ */
+static inline void pci_dma_sync_sg(struct pci_dev *hwdev,
+                                  struct scatterlist *sg,
+                                  int nelems, int direction)
+{
+       int i;
+
+       if (direction == PCI_DMA_NONE)
+                BUG();
+
+       for (i = 0; i < nelems; i++)
+               frv_cache_wback_inv(sg_dma_address(&sg[i]),
+                                   sg_dma_address(&sg[i])+sg_dma_len(&sg[i]));
+}
+
+
+#endif
diff --git a/include/asm-frv/percpu.h b/include/asm-frv/percpu.h
new file mode 100644 (file)
index 0000000..2cad3f8
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __ASM_PERCPU_H
+#define __ASM_PERCPU_H
+
+#include <asm-generic/percpu.h>
+
+#endif /* __ASM_PERCPU_H */
diff --git a/include/asm-frv/pgalloc.h b/include/asm-frv/pgalloc.h
new file mode 100644 (file)
index 0000000..1bd28f4
--- /dev/null
@@ -0,0 +1,64 @@
+/* pgalloc.h: Page allocation routines for FRV
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Derived from:
+ *     include/asm-m68knommu/pgalloc.h
+ *     include/asm-i386/pgalloc.h
+ */
+#ifndef _ASM_PGALLOC_H
+#define _ASM_PGALLOC_H
+
+#include <linux/config.h>
+#include <asm/setup.h>
+#include <asm/virtconvert.h>
+
+#ifdef CONFIG_MMU
+
+#define pmd_populate_kernel(mm, pmd, pte) __set_pmd(pmd, __pa(pte) | _PAGE_TABLE)
+#define pmd_populate(MM, PMD, PAGE)                                            \
+do {                                                                           \
+       __set_pmd((PMD), page_to_pfn(PAGE) << PAGE_SHIFT | _PAGE_TABLE);        \
+} while(0)
+
+/*
+ * Allocate and free page tables.
+ */
+
+extern pgd_t *pgd_alloc(struct mm_struct *);
+extern void pgd_free(pgd_t *);
+
+extern pte_t *pte_alloc_one_kernel(struct mm_struct *, unsigned long);
+
+extern struct page *pte_alloc_one(struct mm_struct *, unsigned long);
+
+static inline void pte_free_kernel(pte_t *pte)
+{
+       free_page((unsigned long)pte);
+}
+
+static inline void pte_free(struct page *pte)
+{
+       __free_page(pte);
+}
+
+#define __pte_free_tlb(tlb,pte)                tlb_remove_page((tlb),(pte))
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pgd, so has no extra memory associated with it.
+ * (In the PAE case we free the pmds as part of the pgd.)
+ */
+#define pmd_alloc_one(mm, addr)                ({ BUG(); ((pmd_t *) 2); })
+#define pmd_free(x)                    do { } while (0)
+#define __pmd_free_tlb(tlb,x)          do { } while (0)
+
+#endif /* CONFIG_MMU */
+
+#endif /* _ASM_PGALLOC_H */
diff --git a/include/asm-frv/pgtable.h b/include/asm-frv/pgtable.h
new file mode 100644 (file)
index 0000000..eb56c87
--- /dev/null
@@ -0,0 +1,554 @@
+/* pgtable.h: FR-V page table mangling
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Derived from:
+ *     include/asm-m68knommu/pgtable.h
+ *     include/asm-i386/pgtable.h
+ */
+
+#ifndef _ASM_PGTABLE_H
+#define _ASM_PGTABLE_H
+
+#include <linux/config.h>
+#include <asm/mem-layout.h>
+#include <asm/setup.h>
+#include <asm/processor.h>
+
+#ifndef __ASSEMBLY__
+#include <linux/threads.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#endif
+
+#ifndef __ASSEMBLY__
+#if defined(CONFIG_HIGHPTE)
+typedef unsigned long pte_addr_t;
+#else
+typedef pte_t *pte_addr_t;
+#endif
+#endif
+
+/*****************************************************************************/
+/*
+ * MMU-less operation case first
+ */
+#ifndef CONFIG_MMU
+
+#define pgd_present(pgd)       (1)             /* pages are always present on NO_MM */
+#define pgd_none(pgd)          (0)
+#define pgd_bad(pgd)           (0)
+#define pgd_clear(pgdp)
+#define kern_addr_valid(addr)  (1)
+#define        pmd_offset(a, b)        ((void *) 0)
+
+#define PAGE_NONE              __pgprot(0)     /* these mean nothing to NO_MM */
+#define PAGE_SHARED            __pgprot(0)     /* these mean nothing to NO_MM */
+#define PAGE_COPY              __pgprot(0)     /* these mean nothing to NO_MM */
+#define PAGE_READONLY          __pgprot(0)     /* these mean nothing to NO_MM */
+#define PAGE_KERNEL            __pgprot(0)     /* these mean nothing to NO_MM */
+
+#define __swp_type(x)          (0)
+#define __swp_offset(x)                (0)
+#define __swp_entry(typ,off)   ((swp_entry_t) { ((typ) | ((off) << 7)) })
+#define __pte_to_swp_entry(pte)        ((swp_entry_t) { pte_val(pte) })
+#define __swp_entry_to_pte(x)  ((pte_t) { (x).val })
+
+#ifndef __ASSEMBLY__
+static inline int pte_file(pte_t pte) { return 0; }
+#endif
+
+#define ZERO_PAGE(vaddr)       ({ BUG(); NULL; })
+
+#define swapper_pg_dir         ((pgd_t *) NULL)
+
+#define pgtable_cache_init()   do {} while(0)
+
+#else /* !CONFIG_MMU */
+/*****************************************************************************/
+/*
+ * then MMU operation
+ */
+
+/*
+ * ZERO_PAGE is a global shared page that is always zero: used
+ * for zero-mapped memory areas etc..
+ */
+#ifndef __ASSEMBLY__
+extern unsigned long empty_zero_page;
+#define ZERO_PAGE(vaddr)       virt_to_page(empty_zero_page)
+#endif
+
+/*
+ * we use 2-level page tables, folding the PMD (mid-level table) into the PGE (top-level entry)
+ * [see Documentation/fujitsu/frv/mmu-layout.txt]
+ *
+ * Page Directory:
+ *  - Size: 16KB
+ *  - 64 PGEs per PGD
+ *  - Each PGE holds 1 PUD and covers 64MB
+ *
+ * Page Upper Directory:
+ *  - Size: 256B
+ *  - 1 PUE per PUD
+ *  - Each PUE holds 1 PMD and covers 64MB
+ *
+ * Page Mid-Level Directory
+ *  - Size: 256B
+ *  - 1 PME per PMD
+ *  - Each PME holds 64 STEs, all of which point to separate chunks of the same Page Table
+ *  - All STEs are instantiated at the same time
+ *
+ * Page Table
+ *  - Size: 16KB
+ *  - 4096 PTEs per PT
+ *  - Each Linux PT is subdivided into 64 FR451 PT's, each of which holds 64 entries
+ *
+ * Pages
+ *  - Size: 4KB
+ *
+ * total PTEs
+ *     = 1 PML4E * 64 PGEs * 1 PUEs * 1 PMEs * 4096 PTEs
+ *     = 1 PML4E * 64 PGEs * 64 STEs * 64 PTEs/FR451-PT
+ *     = 262144 (or 256 * 1024)
+ */
+#define PGDIR_SHIFT            26
+#define PGDIR_SIZE             (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK             (~(PGDIR_SIZE - 1))
+#define PTRS_PER_PGD           64
+
+#define PUD_SHIFT              26
+#define PTRS_PER_PUD           1
+#define PUD_SIZE               (1UL << PUD_SHIFT)
+#define PUD_MASK               (~(PUD_SIZE - 1))
+#define PUE_SIZE               256
+
+#define PMD_SHIFT              26
+#define PMD_SIZE               (1UL << PMD_SHIFT)
+#define PMD_MASK               (~(PMD_SIZE - 1))
+#define PTRS_PER_PMD           1
+#define PME_SIZE               256
+
+#define __frv_PT_SIZE          256
+
+#define PTRS_PER_PTE           4096
+
+#define USER_PGDS_IN_LAST_PML4 (TASK_SIZE / PGDIR_SIZE)
+#define FIRST_USER_PGD_NR      0
+
+#define USER_PGD_PTRS          (PAGE_OFFSET >> PGDIR_SHIFT)
+#define KERNEL_PGD_PTRS                (PTRS_PER_PGD - USER_PGD_PTRS)
+
+#define TWOLEVEL_PGDIR_SHIFT   26
+#define BOOT_USER_PGD_PTRS     (__PAGE_OFFSET >> TWOLEVEL_PGDIR_SHIFT)
+#define BOOT_KERNEL_PGD_PTRS   (PTRS_PER_PGD - BOOT_USER_PGD_PTRS)
+
+#ifndef __ASSEMBLY__
+
+extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
+
+#define pte_ERROR(e) \
+       printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, (e).pte)
+#define pmd_ERROR(e) \
+       printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
+#define pud_ERROR(e) \
+       printk("%s:%d: bad pud %08lx.\n", __FILE__, __LINE__, pmd_val(pud_val(e)))
+#define pgd_ERROR(e) \
+       printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pmd_val(pud_val(pgd_val(e))))
+
+/*
+ * Certain architectures need to do special things when PTEs
+ * within a page table are directly modified.  Thus, the following
+ * hook is made available.
+ */
+#define set_pte(pteptr, pteval)                                \
+do {                                                   \
+       *(pteptr) = (pteval);                           \
+       asm volatile("dcf %M0" :: "U"(*pteptr));        \
+} while(0)
+
+#define set_pte_atomic(pteptr, pteval)         set_pte((pteptr), (pteval))
+
+/*
+ * pgd_offset() returns a (pgd_t *)
+ * pgd_index() is used get the offset into the pgd page's array of pgd_t's;
+ */
+#define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address))
+
+/*
+ * a shortcut which implies the use of the kernel's pgd, instead
+ * of a process's
+ */
+#define pgd_offset_k(address) pgd_offset(&init_mm, address)
+
+/*
+ * The "pgd_xxx()" functions here are trivial for a folded two-level
+ * setup: the pud is never bad, and a pud always exists (as it's folded
+ * into the pgd entry)
+ */
+static inline int pgd_none(pgd_t pgd)          { return 0; }
+static inline int pgd_bad(pgd_t pgd)           { return 0; }
+static inline int pgd_present(pgd_t pgd)       { return 1; }
+static inline void pgd_clear(pgd_t *pgd)       { }
+
+#define pgd_populate(mm, pgd, pud)             do { } while (0)
+/*
+ * (puds are folded into pgds so this doesn't get actually called,
+ * but the define is needed for a generic inline function.)
+ */
+#define set_pgd(pgdptr, pgdval)                                \
+do {                                                   \
+       memcpy((pgdptr), &(pgdval), sizeof(pgd_t));     \
+       asm volatile("dcf %M0" :: "U"(*(pgdptr)));      \
+} while(0)
+
+static inline pud_t *pud_offset(pgd_t *pgd, unsigned long address)
+{
+       return (pud_t *) pgd;
+}
+
+#define pgd_page(pgd)                          (pud_page((pud_t){ pgd }))
+#define pgd_page_kernel(pgd)                   (pud_page_kernel((pud_t){ pgd }))
+
+/*
+ * allocating and freeing a pud is trivial: the 1-entry pud is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+#define pud_alloc_one(mm, address)             NULL
+#define pud_free(x)                            do { } while (0)
+#define __pud_free_tlb(tlb, x)                 do { } while (0)
+
+/*
+ * The "pud_xxx()" functions here are trivial for a folded two-level
+ * setup: the pmd is never bad, and a pmd always exists (as it's folded
+ * into the pud entry)
+ */
+static inline int pud_none(pud_t pud)          { return 0; }
+static inline int pud_bad(pud_t pud)           { return 0; }
+static inline int pud_present(pud_t pud)       { return 1; }
+static inline void pud_clear(pud_t *pud)       { }
+
+#define pud_populate(mm, pmd, pte)             do { } while (0)
+
+/*
+ * (pmds are folded into puds so this doesn't get actually called,
+ * but the define is needed for a generic inline function.)
+ */
+#define set_pud(pudptr, pudval)                        set_pmd((pmd_t *)(pudptr), (pmd_t) { pudval })
+
+#define pud_page(pud)                          (pmd_page((pmd_t){ pud }))
+#define pud_page_kernel(pud)                   (pmd_page_kernel((pmd_t){ pud }))
+
+/*
+ * (pmds are folded into pgds so this doesn't get actually called,
+ * but the define is needed for a generic inline function.)
+ */
+extern void __set_pmd(pmd_t *pmdptr, unsigned long __pmd);
+
+#define set_pmd(pmdptr, pmdval)                        \
+do {                                           \
+       __set_pmd((pmdptr), (pmdval).ste[0]);   \
+} while(0)
+
+#define __pmd_index(address)                   0
+
+static inline pmd_t *pmd_offset(pud_t *dir, unsigned long address)
+{
+       return (pmd_t *) dir + __pmd_index(address);
+}
+
+#define pte_same(a, b)         ((a).pte == (b).pte)
+#define pte_page(x)            (mem_map + ((unsigned long)(((x).pte >> PAGE_SHIFT))))
+#define pte_none(x)            (!(x).pte)
+#define pte_pfn(x)             ((unsigned long)(((x).pte >> PAGE_SHIFT)))
+#define pfn_pte(pfn, prot)     __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
+#define pfn_pmd(pfn, prot)     __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
+
+#define VMALLOC_VMADDR(x)      ((unsigned long) (x))
+
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * control flags in AMPR registers and TLB entries
+ */
+#define _PAGE_BIT_PRESENT      xAMPRx_V_BIT
+#define _PAGE_BIT_WP           DAMPRx_WP_BIT
+#define _PAGE_BIT_NOCACHE      xAMPRx_C_BIT
+#define _PAGE_BIT_SUPER                xAMPRx_S_BIT
+#define _PAGE_BIT_ACCESSED     xAMPRx_RESERVED8_BIT
+#define _PAGE_BIT_DIRTY                xAMPRx_M_BIT
+#define _PAGE_BIT_NOTGLOBAL    xAMPRx_NG_BIT
+
+#define _PAGE_PRESENT          xAMPRx_V
+#define _PAGE_WP               DAMPRx_WP
+#define _PAGE_NOCACHE          xAMPRx_C
+#define _PAGE_SUPER            xAMPRx_S
+#define _PAGE_ACCESSED         xAMPRx_RESERVED8        /* accessed if set */
+#define _PAGE_DIRTY            xAMPRx_M
+#define _PAGE_NOTGLOBAL                xAMPRx_NG
+
+#define _PAGE_RESERVED_MASK    (xAMPRx_RESERVED8 | xAMPRx_RESERVED13)
+
+#define _PAGE_FILE             0x002   /* set:pagecache unset:swap */
+#define _PAGE_PROTNONE         0x000   /* If not present */
+
+#define _PAGE_CHG_MASK         (PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
+
+#define __PGPROT_BASE \
+       (_PAGE_PRESENT | xAMPRx_SS_16Kb | xAMPRx_D | _PAGE_NOTGLOBAL | _PAGE_ACCESSED)
+
+#define PAGE_NONE      __pgprot(_PAGE_PROTNONE | _PAGE_ACCESSED)
+#define PAGE_SHARED    __pgprot(__PGPROT_BASE)
+#define PAGE_COPY      __pgprot(__PGPROT_BASE | _PAGE_WP)
+#define PAGE_READONLY  __pgprot(__PGPROT_BASE | _PAGE_WP)
+
+#define __PAGE_KERNEL          (__PGPROT_BASE | _PAGE_SUPER | _PAGE_DIRTY)
+#define __PAGE_KERNEL_NOCACHE  (__PGPROT_BASE | _PAGE_SUPER | _PAGE_DIRTY | _PAGE_NOCACHE)
+#define __PAGE_KERNEL_RO       (__PGPROT_BASE | _PAGE_SUPER | _PAGE_DIRTY | _PAGE_WP)
+
+#define MAKE_GLOBAL(x) __pgprot((x) & ~_PAGE_NOTGLOBAL)
+
+#define PAGE_KERNEL            MAKE_GLOBAL(__PAGE_KERNEL)
+#define PAGE_KERNEL_RO         MAKE_GLOBAL(__PAGE_KERNEL_RO)
+#define PAGE_KERNEL_NOCACHE    MAKE_GLOBAL(__PAGE_KERNEL_NOCACHE)
+
+#define _PAGE_TABLE            (_PAGE_PRESENT | xAMPRx_SS_16Kb)
+
+#ifndef __ASSEMBLY__
+
+/*
+ * The FR451 can do execute protection by virtue of having separate TLB miss handlers for
+ * instruction access and for data access. However, we don't have enough reserved bits to say
+ * "execute only", so we don't bother. If you can read it, you can execute it and vice versa.
+ */
+#define __P000 PAGE_NONE
+#define __P001 PAGE_READONLY
+#define __P010 PAGE_COPY
+#define __P011 PAGE_COPY
+#define __P100 PAGE_READONLY
+#define __P101 PAGE_READONLY
+#define __P110 PAGE_COPY
+#define __P111 PAGE_COPY
+
+#define __S000 PAGE_NONE
+#define __S001 PAGE_READONLY
+#define __S010 PAGE_SHARED
+#define __S011 PAGE_SHARED
+#define __S100 PAGE_READONLY
+#define __S101 PAGE_READONLY
+#define __S110 PAGE_SHARED
+#define __S111 PAGE_SHARED
+
+/*
+ * Define this to warn about kernel memory accesses that are
+ * done without a 'verify_area(VERIFY_WRITE,..)'
+ */
+#undef TEST_VERIFY_AREA
+
+#define pte_present(x) (pte_val(x) & _PAGE_PRESENT)
+#define pte_clear(xp)  do { set_pte(xp, __pte(0)); } while (0)
+
+#define pmd_none(x)    (!pmd_val(x))
+#define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT)
+#define        pmd_bad(x)      (pmd_val(x) & xAMPRx_SS)
+#define pmd_clear(xp)  do { __set_pmd(xp, 0); } while(0)
+
+#define pmd_page_kernel(pmd) \
+       ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK))
+
+#ifndef CONFIG_DISCONTIGMEM
+#define pmd_page(pmd)  (pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT))
+#endif
+
+#define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT))
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+static inline int pte_read(pte_t pte)          { return !((pte).pte & _PAGE_SUPER); }
+static inline int pte_exec(pte_t pte)          { return !((pte).pte & _PAGE_SUPER); }
+static inline int pte_dirty(pte_t pte)         { return (pte).pte & _PAGE_DIRTY; }
+static inline int pte_young(pte_t pte)         { return (pte).pte & _PAGE_ACCESSED; }
+static inline int pte_write(pte_t pte)         { return !((pte).pte & _PAGE_WP); }
+
+static inline pte_t pte_rdprotect(pte_t pte)   { (pte).pte |= _PAGE_SUPER; return pte; }
+static inline pte_t pte_exprotect(pte_t pte)   { (pte).pte |= _PAGE_SUPER; return pte; }
+static inline pte_t pte_mkclean(pte_t pte)     { (pte).pte &= ~_PAGE_DIRTY; return pte; }
+static inline pte_t pte_mkold(pte_t pte)       { (pte).pte &= ~_PAGE_ACCESSED; return pte; }
+static inline pte_t pte_wrprotect(pte_t pte)   { (pte).pte |= _PAGE_WP; return pte; }
+static inline pte_t pte_mkread(pte_t pte)      { (pte).pte &= ~_PAGE_SUPER; return pte; }
+static inline pte_t pte_mkexec(pte_t pte)      { (pte).pte &= ~_PAGE_SUPER; return pte; }
+static inline pte_t pte_mkdirty(pte_t pte)     { (pte).pte |= _PAGE_DIRTY; return pte; }
+static inline pte_t pte_mkyoung(pte_t pte)     { (pte).pte |= _PAGE_ACCESSED; return pte; }
+static inline pte_t pte_mkwrite(pte_t pte)     { (pte).pte &= ~_PAGE_WP; return pte; }
+
+static inline int ptep_test_and_clear_dirty(pte_t *ptep)
+{
+       int i = test_and_clear_bit(_PAGE_BIT_DIRTY, ptep);
+       asm volatile("dcf %M0" :: "U"(*ptep));
+       return i;
+}
+
+static inline int ptep_test_and_clear_young(pte_t *ptep)
+{
+       int i = test_and_clear_bit(_PAGE_BIT_ACCESSED, ptep);
+       asm volatile("dcf %M0" :: "U"(*ptep));
+       return i;
+}
+
+static inline pte_t ptep_get_and_clear(pte_t *ptep)
+{
+       unsigned long x = xchg(&ptep->pte, 0);
+       asm volatile("dcf %M0" :: "U"(*ptep));
+       return __pte(x);
+}
+
+static inline void ptep_set_wrprotect(pte_t *ptep)
+{
+       set_bit(_PAGE_BIT_WP, ptep);
+       asm volatile("dcf %M0" :: "U"(*ptep));
+}
+
+static inline void ptep_mkdirty(pte_t *ptep)
+{
+       set_bit(_PAGE_BIT_DIRTY, ptep);
+       asm volatile("dcf %M0" :: "U"(*ptep));
+}
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+
+#define mk_pte(page, pgprot)   pfn_pte(page_to_pfn(page), (pgprot))
+#define mk_pte_huge(entry)     ((entry).pte_low |= _PAGE_PRESENT | _PAGE_PSE)
+
+/* This takes a physical page address that is used by the remapping functions */
+#define mk_pte_phys(physpage, pgprot)  pfn_pte((physpage) >> PAGE_SHIFT, pgprot)
+
+static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{
+       pte.pte &= _PAGE_CHG_MASK;
+       pte.pte |= pgprot_val(newprot);
+       return pte;
+}
+
+#define page_pte(page) page_pte_prot((page), __pgprot(0))
+
+/* to find an entry in a page-table-directory. */
+#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
+#define pgd_index_k(addr) pgd_index(addr)
+
+/* Find an entry in the bottom-level page table.. */
+#define __pte_index(address) (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
+
+/*
+ * the pte page can be thought of an array like this: pte_t[PTRS_PER_PTE]
+ *
+ * this macro returns the index of the entry in the pte page which would
+ * control the given virtual address
+ */
+#define pte_index(address) \
+               (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
+#define pte_offset_kernel(dir, address) \
+       ((pte_t *) pmd_page_kernel(*(dir)) +  pte_index(address))
+
+#if defined(CONFIG_HIGHPTE)
+#define pte_offset_map(dir, address) \
+       ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE0) + pte_index(address))
+#define pte_offset_map_nested(dir, address) \
+       ((pte_t *)kmap_atomic(pmd_page(*(dir)),KM_PTE1) + pte_index(address))
+#define pte_unmap(pte) kunmap_atomic(pte, KM_PTE0)
+#define pte_unmap_nested(pte) kunmap_atomic((pte), KM_PTE1)
+#else
+#define pte_offset_map(dir, address) \
+       ((pte_t *)page_address(pmd_page(*(dir))) + pte_index(address))
+#define pte_offset_map_nested(dir, address) pte_offset_map((dir), (address))
+#define pte_unmap(pte) do { } while (0)
+#define pte_unmap_nested(pte) do { } while (0)
+#endif
+
+/*
+ * Handle swap and file entries
+ * - the PTE is encoded in the following format:
+ *     bit 0:          Must be 0 (!_PAGE_PRESENT)
+ *     bit 1:          Type: 0 for swap, 1 for file (_PAGE_FILE)
+ *     bits 2-7:       Swap type
+ *     bits 8-31:      Swap offset
+ *     bits 2-31:      File pgoff
+ */
+#define __swp_type(x)                  (((x).val >> 2) & 0x1f)
+#define __swp_offset(x)                        ((x).val >> 8)
+#define __swp_entry(type, offset)      ((swp_entry_t) { ((type) << 2) | ((offset) << 8) })
+#define __pte_to_swp_entry(pte)                ((swp_entry_t) { (pte).pte })
+#define __swp_entry_to_pte(x)          ((pte_t) { (x).val })
+
+static inline int pte_file(pte_t pte)
+{
+       return pte.pte & _PAGE_FILE;
+}
+
+#define PTE_FILE_MAX_BITS      29
+
+#define pte_to_pgoff(PTE)      ((PTE).pte >> 2)
+#define pgoff_to_pte(off)      __pte((off) << 2 | _PAGE_FILE)
+
+/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
+#define PageSkip(page)         (0)
+#define kern_addr_valid(addr)  (1)
+
+#define io_remap_page_range(vma, vaddr, paddr, size, prot)             \
+               remap_pfn_range(vma, vaddr, (paddr) >> PAGE_SHIFT, size, prot)
+
+#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
+#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY
+#define __HAVE_ARCH_PTEP_GET_AND_CLEAR
+#define __HAVE_ARCH_PTEP_SET_WRPROTECT
+#define __HAVE_ARCH_PTEP_MKDIRTY
+#define __HAVE_ARCH_PTE_SAME
+#include <asm-generic/pgtable.h>
+
+/*
+ * preload information about a newly instantiated PTE into the SCR0/SCR1 PGE cache
+ */
+static inline void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+{
+       unsigned long ampr;
+       pgd_t *pge = pgd_offset(current->mm, address);
+       pud_t *pue = pud_offset(pge, address);
+       pmd_t *pme = pmd_offset(pue, address);
+
+       ampr = pme->ste[0] & 0xffffff00;
+       ampr |= xAMPRx_L | xAMPRx_SS_16Kb | xAMPRx_S | xAMPRx_C | xAMPRx_V;
+
+       asm volatile("movgs %0,scr0\n"
+                    "movgs %0,scr1\n"
+                    "movgs %1,dampr4\n"
+                    "movgs %1,dampr5\n"
+                    :
+                    : "r"(address), "r"(ampr)
+                    );
+}
+
+#ifdef CONFIG_PROC_FS
+extern char *proc_pid_status_frv_cxnr(struct mm_struct *mm, char *buffer);
+#endif
+
+extern void __init pgtable_cache_init(void);
+
+#endif /* !__ASSEMBLY__ */
+#endif /* !CONFIG_MMU */
+
+#ifndef __ASSEMBLY__
+extern void __init paging_init(void);
+#endif /* !__ASSEMBLY__ */
+
+#endif /* _ASM_PGTABLE_H */
diff --git a/include/asm-frv/poll.h b/include/asm-frv/poll.h
new file mode 100644 (file)
index 0000000..8cbcd60
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _ASM_POLL_H
+#define _ASM_POLL_H
+
+#define POLLIN           1
+#define POLLPRI                  2
+#define POLLOUT                  4
+#define POLLERR                  8
+#define POLLHUP                 16
+#define POLLNVAL        32
+#define POLLRDNORM      64
+#define POLLWRNORM     POLLOUT
+#define POLLRDBAND     128
+#define POLLWRBAND     256
+#define POLLMSG                0x0400
+
+struct pollfd {
+       int fd;
+       short events;
+       short revents;
+};
+
+#endif
+
diff --git a/include/asm-frv/posix_types.h b/include/asm-frv/posix_types.h
new file mode 100644 (file)
index 0000000..73c2ba8
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef _ASM_POSIX_TYPES_H
+#define _ASM_POSIX_TYPES_H
+
+/*
+ * This file is generally used by user-level software, so you need to
+ * be a little careful about namespace pollution etc.  Also, we cannot
+ * assume GCC is being used.
+ */
+
+typedef unsigned long  __kernel_ino_t;
+typedef unsigned short __kernel_mode_t;
+typedef unsigned short __kernel_nlink_t;
+typedef long           __kernel_off_t;
+typedef int            __kernel_pid_t;
+typedef unsigned short __kernel_ipc_pid_t;
+typedef unsigned short __kernel_uid_t;
+typedef unsigned short __kernel_gid_t;
+typedef unsigned int   __kernel_size_t;
+typedef int            __kernel_ssize_t;
+typedef int            __kernel_ptrdiff_t;
+typedef long           __kernel_time_t;
+typedef long           __kernel_suseconds_t;
+typedef long           __kernel_clock_t;
+typedef int            __kernel_timer_t;
+typedef int            __kernel_clockid_t;
+typedef int            __kernel_daddr_t;
+typedef char *         __kernel_caddr_t;
+typedef unsigned short __kernel_uid16_t;
+typedef unsigned short __kernel_gid16_t;
+typedef unsigned int   __kernel_uid32_t;
+typedef unsigned int   __kernel_gid32_t;
+
+typedef unsigned short __kernel_old_uid_t;
+typedef unsigned short __kernel_old_gid_t;
+typedef unsigned short __kernel_old_dev_t;
+
+#ifdef __GNUC__
+typedef long long      __kernel_loff_t;
+#endif
+
+typedef struct {
+#if defined(__KERNEL__) || defined(__USE_ALL)
+       int     val[2];
+#else /* !defined(__KERNEL__) && !defined(__USE_ALL) */
+       int     __val[2];
+#endif /* !defined(__KERNEL__) && !defined(__USE_ALL) */
+} __kernel_fsid_t;
+
+#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
+
+#undef __FD_SET
+#define        __FD_SET(d, set)        ((set)->fds_bits[__FDELT(d)] |= __FDMASK(d))
+
+#undef __FD_CLR
+#define        __FD_CLR(d, set)        ((set)->fds_bits[__FDELT(d)] &= ~__FDMASK(d))
+
+#undef __FD_ISSET
+#define        __FD_ISSET(d, set)      (!!((set)->fds_bits[__FDELT(d)] & __FDMASK(d)))
+
+#undef __FD_ZERO
+#define __FD_ZERO(fdsetp) (memset (fdsetp, 0, sizeof(*(fd_set *)fdsetp)))
+
+#endif /* defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) */
+
+#endif
+
diff --git a/include/asm-frv/processor.h b/include/asm-frv/processor.h
new file mode 100644 (file)
index 0000000..5228c18
--- /dev/null
@@ -0,0 +1,153 @@
+/* processor.h: FRV processor definitions
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_PROCESSOR_H
+#define _ASM_PROCESSOR_H
+
+#include <linux/config.h>
+#include <asm/mem-layout.h>
+
+#ifndef __ASSEMBLY__
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
+#include <linux/linkage.h>
+#include <asm/sections.h>
+#include <asm/segment.h>
+#include <asm/fpu.h>
+#include <asm/registers.h>
+#include <asm/ptrace.h>
+#include <asm/current.h>
+#include <asm/cache.h>
+
+/* Forward declaration, a strange C thing */
+struct task_struct;
+
+/*
+ *  CPU type and hardware bug flags. Kept separately for each CPU.
+ */
+struct cpuinfo_frv {
+#ifdef CONFIG_MMU
+       unsigned long   *pgd_quick;
+       unsigned long   *pte_quick;
+       unsigned long   pgtable_cache_sz;
+#endif
+} __cacheline_aligned;
+
+extern struct cpuinfo_frv __nongprelbss boot_cpu_data;
+
+#define cpu_data               (&boot_cpu_data)
+#define current_cpu_data       boot_cpu_data
+
+/*
+ * Bus types
+ */
+#define EISA_bus 0
+#define MCA_bus 0
+
+struct thread_struct {
+       struct pt_regs          *frame;         /* [GR28] exception frame ptr for this thread */
+       struct task_struct      *curr;          /* [GR29] current pointer for this thread */
+       unsigned long           sp;             /* [GR1 ] kernel stack pointer */
+       unsigned long           fp;             /* [GR2 ] kernel frame pointer */
+       unsigned long           lr;             /* link register */
+       unsigned long           pc;             /* program counter */
+       unsigned long           gr[12];         /* [GR16-GR27] */
+       unsigned long           sched_lr;       /* LR from schedule() */
+
+       union {
+               struct pt_regs          *frame0;        /* top (user) stack frame */
+               struct user_context     *user;          /* userspace context */
+       };
+} __attribute__((aligned(8)));
+
+extern struct pt_regs *__kernel_frame0_ptr;
+extern struct task_struct *__kernel_current_task;
+
+#endif
+
+#ifndef __ASSEMBLY__
+#define INIT_THREAD_FRAME0 \
+       ((struct pt_regs *) \
+       (sizeof(init_stack) + (unsigned long) init_stack - sizeof(struct user_context)))
+
+#define INIT_THREAD {                          \
+       NULL,                                   \
+       (struct task_struct *) init_stack,      \
+       0, 0, 0, 0,                             \
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
+       0,                                      \
+       { INIT_THREAD_FRAME0 },                 \
+}
+
+/*
+ * do necessary setup to start up a newly executed thread.
+ * - need to discard the frame stacked by init() invoking the execve syscall
+ */
+#define start_thread(_regs, _pc, _usp)                 \
+do {                                                   \
+       set_fs(USER_DS); /* reads from user space */    \
+       __frame = __kernel_frame0_ptr;                  \
+       __frame->pc     = (_pc);                        \
+       __frame->psr    &= ~PSR_S;                      \
+       __frame->sp     = (_usp);                       \
+} while(0)
+
+extern void prepare_to_copy(struct task_struct *tsk);
+
+/* Free all resources held by a thread. */
+static inline void release_thread(struct task_struct *dead_task)
+{
+}
+
+extern asmlinkage int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
+extern asmlinkage void save_user_regs(struct user_context *target);
+extern asmlinkage void *restore_user_regs(const struct user_context *target, ...);
+
+#define copy_segments(tsk, mm)         do { } while (0)
+#define release_segments(mm)           do { } while (0)
+#define forget_segments()              do { } while (0)
+
+/*
+ * Free current thread data structures etc..
+ */
+static inline void exit_thread(void)
+{
+}
+
+/*
+ * Return saved PC of a blocked thread.
+ */
+extern unsigned long thread_saved_pc(struct task_struct *tsk);
+
+unsigned long get_wchan(struct task_struct *p);
+
+#define        KSTK_EIP(tsk)   ((tsk)->thread.frame0->pc)
+#define        KSTK_ESP(tsk)   ((tsk)->thread.frame0->sp)
+
+/* Allocation and freeing of basic task resources. */
+extern struct task_struct *alloc_task_struct(void);
+extern void free_task_struct(struct task_struct *p);
+
+#define cpu_relax()    do { } while (0)
+
+/* data cache prefetch */
+#define ARCH_HAS_PREFETCH
+static inline void prefetch(const void *x)
+{
+       asm volatile("dcpl %0,gr0,#0" : : "r"(x));
+}
+
+#endif /* __ASSEMBLY__ */
+#endif /* _ASM_PROCESSOR_H */
diff --git a/include/asm-frv/ptrace.h b/include/asm-frv/ptrace.h
new file mode 100644 (file)
index 0000000..b2cce07
--- /dev/null
@@ -0,0 +1,86 @@
+/* ptrace.h: ptrace() relevant definitions
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ASM_PTRACE_H
+#define _ASM_PTRACE_H
+
+#include <asm/registers.h>
+
+#define in_syscall(regs) (((regs)->tbr & TBR_TT) == TBR_TT_TRAP0)
+
+
+#define PT_PSR         0
+#define        PT_ISR          1
+#define PT_CCR         2
+#define PT_CCCR                3
+#define PT_LR          4
+#define PT_LCR         5
+#define PT_PC          6
+
+#define PT__STATUS     7       /* exception status */
+#define PT_SYSCALLNO   8       /* syscall number or -1 */
+#define PT_ORIG_GR8    9       /* saved GR8 for signal handling */
+#define PT_GNER0       10
+#define PT_GNER1       11
+#define PT_IACC0H      12
+#define PT_IACC0L      13
+
+#define PT_GR(j)       ( 14 + (j))     /* GRj for 0<=j<=63 */
+#define PT_FR(j)       ( 78 + (j))     /* FRj for 0<=j<=63 */
+#define PT_FNER(j)     (142 + (j))     /* FNERj for 0<=j<=1 */
+#define PT_MSR(j)      (144 + (j))     /* MSRj for 0<=j<=2 */
+#define PT_ACC(j)      (146 + (j))     /* ACCj for 0<=j<=7 */
+#define PT_ACCG(jklm)  (154 + (jklm))  /* ACCGjklm for 0<=jklm<=1 (reads four regs per slot) */
+#define PT_FSR(j)      (156 + (j))     /* FSRj for 0<=j<=0 */
+#define PT__GPEND      78
+#define PT__END                157
+
+#define PT_TBR         PT_GR(0)
+#define PT_SP          PT_GR(1)
+#define PT_FP          PT_GR(2)
+#define PT_PREV_FRAME  PT_GR(28)       /* previous exception frame pointer (old gr28 value) */
+#define PT_CURR_TASK   PT_GR(29)       /* current task */
+
+
+/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */
+#define PTRACE_GETREGS         12
+#define PTRACE_SETREGS         13
+#define PTRACE_GETFPREGS       14
+#define PTRACE_SETFPREGS       15
+#define PTRACE_GETFDPIC                31      /* get the ELF fdpic loadmap address */
+
+#define PTRACE_GETFDPIC_EXEC   0       /* [addr] request the executable loadmap */
+#define PTRACE_GETFDPIC_INTERP 1       /* [addr] request the interpreter loadmap */
+
+#ifndef __ASSEMBLY__
+
+/*
+ * dedicate GR28; to keeping the a pointer to the current exception frame
+ */
+register struct pt_regs *__frame asm("gr28");
+register struct pt_regs *__debug_frame asm("gr31");
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({                     \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+#define __debug_regs container_of(__debug_frame, struct pt_debug_regs, normal_regs)
+
+#define user_mode(regs)                        (!((regs)->psr & PSR_S))
+#define instruction_pointer(regs)      ((regs)->pc)
+
+extern unsigned long user_stack(const struct pt_regs *);
+extern void show_regs(struct pt_regs *);
+#define profile_pc(regs) ((regs)->pc)
+
+#endif /* !__ASSEMBLY__ */
+#endif /* _ASM_PTRACE_H */
diff --git a/include/asm-frv/registers.h b/include/asm-frv/registers.h
new file mode 100644 (file)
index 0000000..fccfd95
--- /dev/null
@@ -0,0 +1,255 @@
+/* registers.h: register frame declarations
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * notes:
+ *
+ * (1) that the members of all these structures are carefully aligned to permit
+ *     usage of STD/STDF instructions
+ *
+ * (2) if you change these structures, you must change the code in
+ *     arch/frvnommu/kernel/{break.S,entry.S,switch_to.S,gdb-stub.c}
+ *
+ *
+ * the kernel stack space block looks like this:
+ *
+ *     +0x2000 +----------------------
+ *             | union {
+ *             |       struct user_context
+ *             |       struct pt_regs [user exception]
+ *             | }
+ *             +---------------------- <-- __kernel_frame0_ptr (maybe GR28)
+ *             |
+ *             | kernel stack
+ *             |
+ *             |......................
+ *             | struct pt_regs [kernel exception]
+ *             |...................... <-- __kernel_frame0_ptr (maybe GR28)
+ *             |
+ *             | kernel stack
+ *             |
+ *             |...................... <-- stack pointer (GR1)
+ *             |
+ *             | unused stack space
+ *             |
+ *             +----------------------
+ *             | struct thread_info
+ *     +0x0000 +---------------------- <-- __current_thread_info (GR15);
+ *
+ * note that GR28 points to the current exception frame
+ */
+
+#ifndef _ASM_REGISTERS_H
+#define _ASM_REGISTERS_H
+
+#ifndef __ASSEMBLY__
+#define __OFFSET(X)    (X)
+#define __OFFSETC(X,N) xxxxxxxxxxxxxxxxxxxxxxxx
+#else
+#define __OFFSET(X)    ((X)*4)
+#define __OFFSETC(X,N) ((X)*4+(N))
+#endif
+
+/*****************************************************************************/
+/*
+ * Exception/Interrupt frame
+ * - held on kernel stack
+ * - 8-byte aligned on stack (old SP is saved in frame)
+ * - GR0 is fixed 0, so we don't save it
+ */
+#ifndef __ASSEMBLY__
+
+struct pt_regs {
+       unsigned long           psr;            /* Processor Status Register */
+       unsigned long           isr;            /* Integer Status Register */
+       unsigned long           ccr;            /* Condition Code Register */
+       unsigned long           cccr;           /* Condition Code for Conditional Insns Register */
+       unsigned long           lr;             /* Link Register */
+       unsigned long           lcr;            /* Loop Count Register */
+       unsigned long           pc;             /* Program Counter Register */
+       unsigned long           __status;       /* exception status */
+       unsigned long           syscallno;      /* syscall number or -1 */
+       unsigned long           orig_gr8;       /* original syscall arg #1 */
+       unsigned long           gner0;
+       unsigned long           gner1;
+       unsigned long long      iacc0;
+       unsigned long           tbr;            /* GR0 is fixed zero, so we use this for TBR */
+       unsigned long           sp;             /* GR1: USP/KSP */
+       unsigned long           fp;             /* GR2: FP */
+       unsigned long           gr3;
+       unsigned long           gr4;
+       unsigned long           gr5;
+       unsigned long           gr6;
+       unsigned long           gr7;            /* syscall number */
+       unsigned long           gr8;            /* 1st syscall param; syscall return */
+       unsigned long           gr9;            /* 2nd syscall param */
+       unsigned long           gr10;           /* 3rd syscall param */
+       unsigned long           gr11;           /* 4th syscall param */
+       unsigned long           gr12;           /* 5th syscall param */
+       unsigned long           gr13;           /* 6th syscall param */
+       unsigned long           gr14;
+       unsigned long           gr15;
+       unsigned long           gr16;           /* GP pointer */
+       unsigned long           gr17;           /* small data */
+       unsigned long           gr18;           /* PIC/PID */
+       unsigned long           gr19;
+       unsigned long           gr20;
+       unsigned long           gr21;
+       unsigned long           gr22;
+       unsigned long           gr23;
+       unsigned long           gr24;
+       unsigned long           gr25;
+       unsigned long           gr26;
+       unsigned long           gr27;
+       struct pt_regs          *next_frame;    /* GR28 - next exception frame */
+       unsigned long           gr29;           /* GR29 - OS reserved */
+       unsigned long           gr30;           /* GR30 - OS reserved */
+       unsigned long           gr31;           /* GR31 - OS reserved */
+} __attribute__((aligned(8)));
+
+#endif
+
+#define REG_PSR                __OFFSET( 0)    /* Processor Status Register */
+#define REG_ISR                __OFFSET( 1)    /* Integer Status Register */
+#define REG_CCR                __OFFSET( 2)    /* Condition Code Register */
+#define REG_CCCR       __OFFSET( 3)    /* Condition Code for Conditional Insns Register */
+#define REG_LR         __OFFSET( 4)    /* Link Register */
+#define REG_LCR                __OFFSET( 5)    /* Loop Count Register */
+#define REG_PC         __OFFSET( 6)    /* Program Counter */
+
+#define REG__STATUS    __OFFSET( 7)    /* exception status */
+#define REG__STATUS_STEP       0x00000001      /* - reenable single stepping on return */
+#define REG__STATUS_STEPPED    0x00000002      /* - single step caused exception */
+#define REG__STATUS_BROKE      0x00000004      /* - BREAK insn caused exception */
+#define REG__STATUS_SYSC_ENTRY 0x40000000      /* - T on syscall entry (ptrace.c only) */
+#define REG__STATUS_SYSC_EXIT  0x80000000      /* - T on syscall exit (ptrace.c only) */
+
+#define REG_SYSCALLNO  __OFFSET( 8)    /* syscall number or -1 */
+#define REG_ORIG_GR8   __OFFSET( 9)    /* saved GR8 for signal handling */
+#define REG_GNER0      __OFFSET(10)
+#define REG_GNER1      __OFFSET(11)
+#define REG_IACC0      __OFFSET(12)
+
+#define REG_TBR                __OFFSET(14)    /* Trap Vector Register */
+#define REG_GR(R)      __OFFSET((14+(R)))
+#define REG__END       REG_GR(32)
+
+#define REG_SP         REG_GR(1)
+#define REG_FP         REG_GR(2)
+#define REG_PREV_FRAME REG_GR(28)      /* previous exception frame pointer (old gr28 value) */
+#define REG_CURR_TASK  REG_GR(29)      /* current task */
+
+/*****************************************************************************/
+/*
+ * extension tacked in front of the exception frame in debug mode
+ */
+#ifndef __ASSEMBLY__
+
+struct pt_debug_regs
+{
+       unsigned long           bpsr;
+       unsigned long           dcr;
+       unsigned long           brr;
+       unsigned long           nmar;
+       struct pt_regs          normal_regs;
+} __attribute__((aligned(8)));
+
+#endif
+
+#define REG_NMAR               __OFFSET(-1)
+#define REG_BRR                        __OFFSET(-2)
+#define REG_DCR                        __OFFSET(-3)
+#define REG_BPSR               __OFFSET(-4)
+#define REG__DEBUG_XTRA                __OFFSET(4)
+
+/*****************************************************************************/
+/*
+ * userspace registers
+ */
+#ifndef __ASSEMBLY__
+
+struct user_int_regs
+{
+       /* integer registers
+        * - up to gr[31] mirror pt_regs
+        * - total size must be multiple of 8 bytes
+        */
+       unsigned long           psr;            /* Processor Status Register */
+       unsigned long           isr;            /* Integer Status Register */
+       unsigned long           ccr;            /* Condition Code Register */
+       unsigned long           cccr;           /* Condition Code for Conditional Insns Register */
+       unsigned long           lr;             /* Link Register */
+       unsigned long           lcr;            /* Loop Count Register */
+       unsigned long           pc;             /* Program Counter Register */
+       unsigned long           __status;       /* exception status */
+       unsigned long           syscallno;      /* syscall number or -1 */
+       unsigned long           orig_gr8;       /* original syscall arg #1 */
+       unsigned long           gner[2];
+       unsigned long long      iacc[1];
+
+       union {
+               unsigned long   tbr;
+               unsigned long   gr[64];
+       };
+};
+
+struct user_fpmedia_regs
+{
+       /* FP/Media registers */
+       unsigned long   fr[64];
+       unsigned long   fner[2];
+       unsigned long   msr[2];
+       unsigned long   acc[8];
+       unsigned char   accg[8];
+       unsigned long   fsr[1];
+};
+
+struct user_context
+{
+       struct user_int_regs            i;
+       struct user_fpmedia_regs        f;
+
+       /* we provide a context extension so that we can save the regs for CPUs that
+        * implement many more of Fujitsu's lavish register spec
+        */
+       void *extension;
+} __attribute__((aligned(8)));
+
+#endif
+
+#define NR_USER_INT_REGS       (14 + 64)
+#define NR_USER_FPMEDIA_REGS   (64 + 2 + 2 + 8 + 8/4 + 1)
+#define NR_USER_CONTEXT                (NR_USER_INT_REGS + NR_USER_FPMEDIA_REGS + 1)
+
+#define USER_CONTEXT_SIZE      (((NR_USER_CONTEXT + 1) & ~1) * 4)
+
+#define __THREAD_FRAME         __OFFSET(0)
+#define __THREAD_CURR          __OFFSET(1)
+#define __THREAD_SP            __OFFSET(2)
+#define __THREAD_FP            __OFFSET(3)
+#define __THREAD_LR            __OFFSET(4)
+#define __THREAD_PC            __OFFSET(5)
+#define __THREAD_GR(R)         __OFFSET(6 + (R) - 16)
+#define __THREAD_FRAME0                __OFFSET(19)
+#define __THREAD_USER          __OFFSET(19)
+
+#define __USER_INT             __OFFSET(0)
+#define __INT_GR(R)            __OFFSET(14 + (R))
+
+#define __USER_FPMEDIA         __OFFSET(NR_USER_INT_REGS)
+#define __FPMEDIA_FR(R)                __OFFSET(NR_USER_INT_REGS + (R))
+#define __FPMEDIA_FNER(R)      __OFFSET(NR_USER_INT_REGS + 64 + (R))
+#define __FPMEDIA_MSR(R)       __OFFSET(NR_USER_INT_REGS + 66 + (R))
+#define __FPMEDIA_ACC(R)       __OFFSET(NR_USER_INT_REGS + 68 + (R))
+#define __FPMEDIA_ACCG(R)      __OFFSETC(NR_USER_INT_REGS + 76, (R))
+#define __FPMEDIA_FSR(R)       __OFFSET(NR_USER_INT_REGS + 78 + (R))
+
+#endif /* _ASM_REGISTERS_H */
diff --git a/include/asm-frv/resource.h b/include/asm-frv/resource.h
new file mode 100644 (file)
index 0000000..5fc6054
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _ASM_RESOURCE_H
+#define _ASM_RESOURCE_H
+
+#include <asm-generic/resource.h>
+
+#endif /* _ASM_RESOURCE_H */
+
diff --git a/include/asm-frv/scatterlist.h b/include/asm-frv/scatterlist.h
new file mode 100644 (file)
index 0000000..fb38fd3
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _ASM_SCATTERLIST_H
+#define _ASM_SCATTERLIST_H
+
+/*
+ * Drivers must set either ->address or (preferred) ->page and ->offset
+ * to indicate where data must be transferred to/from.
+ *
+ * Using ->page is recommended since it handles highmem data as well as
+ * low mem. ->address is restricted to data which has a virtual mapping, and
+ * it will go away in the future. Updating to ->page can be automated very
+ * easily -- something like
+ *
+ * sg->address = some_ptr;
+ *
+ * can be rewritten as
+ *
+ * sg->page = virt_to_page(some_ptr);
+ * sg->offset = (unsigned long) some_ptr & ~PAGE_MASK;
+ *
+ * and that's it. There's no excuse for not highmem enabling YOUR driver. /jens
+ */
+struct scatterlist {
+       struct page     *page;          /* Location for highmem page, if any */
+       unsigned int    offset;         /* for highmem, page offset */
+
+       dma_addr_t      dma_address;
+       unsigned int    length;
+};
+
+#define ISA_DMA_THRESHOLD (0xffffffffUL)
+
+#endif /* !_ASM_SCATTERLIST_H */
diff --git a/include/asm-frv/sections.h b/include/asm-frv/sections.h
new file mode 100644 (file)
index 0000000..17d0fb1
--- /dev/null
@@ -0,0 +1,46 @@
+/* sections.h: linkage layout variables
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_SECTIONS_H
+#define _ASM_SECTIONS_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+#include <asm-generic/sections.h>
+
+#ifdef __KERNEL__
+
+/*
+ * we don't want to put variables in the GP-REL section if they're not used very much - that would
+ * be waste since GP-REL addressing is limited to GP16+/-2048
+ */
+#define __nongpreldata __attribute__((section(".data")))
+#define __nongprelbss  __attribute__((section(".bss")))
+
+/*
+ * linker symbols
+ */
+extern const void __kernel_image_start, __kernel_image_end, __page_offset;
+
+extern unsigned long __nongprelbss memory_start;
+extern unsigned long __nongprelbss memory_end;
+extern unsigned long __nongprelbss rom_length;
+
+/* determine if we're running from ROM */
+static inline int is_in_rom(unsigned long addr)
+{
+       return 0; /* default case: not in ROM */
+}
+
+#endif
+#endif
+#endif /* _ASM_SECTIONS_H */
diff --git a/include/asm-frv/segment.h b/include/asm-frv/segment.h
new file mode 100644 (file)
index 0000000..61222f0
--- /dev/null
@@ -0,0 +1,46 @@
+/* segment.h: MMU segment settings
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_SEGMENT_H
+#define _ASM_SEGMENT_H
+
+#include <linux/config.h>
+
+#ifndef __ASSEMBLY__
+
+typedef struct {
+       unsigned long seg;
+} mm_segment_t;
+
+#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
+
+#define KERNEL_DS              MAKE_MM_SEG(0xdfffffffUL)
+
+#ifdef CONFIG_MMU
+#define USER_DS                        MAKE_MM_SEG(TASK_SIZE - 1)
+#else
+#define USER_DS                        KERNEL_DS
+#endif
+
+#define get_ds()               (KERNEL_DS)
+#define get_fs()               (__current_thread_info->addr_limit)
+#define segment_eq(a,b)                ((a).seg == (b).seg)
+#define __kernel_ds_p()                segment_eq(get_fs(), KERNEL_DS)
+#define get_addr_limit()       (get_fs().seg)
+
+#define set_fs(_x)                                     \
+do {                                                   \
+       __current_thread_info->addr_limit = (_x);       \
+} while(0)
+
+
+#endif /* __ASSEMBLY__ */
+#endif /* _ASM_SEGMENT_H */
diff --git a/include/asm-frv/semaphore.h b/include/asm-frv/semaphore.h
new file mode 100644 (file)
index 0000000..3935456
--- /dev/null
@@ -0,0 +1,161 @@
+/* semaphore.h: semaphores for the FR-V
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ASM_SEMAPHORE_H
+#define _ASM_SEMAPHORE_H
+
+#define RW_LOCK_BIAS            0x01000000
+
+#ifndef __ASSEMBLY__
+
+#include <linux/linkage.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/rwsem.h>
+
+#define SEMAPHORE_DEBUG                WAITQUEUE_DEBUG
+
+/*
+ * the semaphore definition
+ * - if counter is >0 then there are tokens available on the semaphore for down to collect
+ * - if counter is <=0 then there are no spare tokens, and anyone that wants one must wait
+ * - if wait_list is not empty, then there are processes waiting for the semaphore
+ */
+struct semaphore {
+       unsigned                counter;
+       spinlock_t              wait_lock;
+       struct list_head        wait_list;
+#if SEMAPHORE_DEBUG
+       unsigned                __magic;
+#endif
+};
+
+#if SEMAPHORE_DEBUG
+# define __SEM_DEBUG_INIT(name) , (long)&(name).__magic
+#else
+# define __SEM_DEBUG_INIT(name)
+#endif
+
+
+#define __SEMAPHORE_INITIALIZER(name,count) \
+{ count, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) __SEM_DEBUG_INIT(name) }
+
+#define __MUTEX_INITIALIZER(name) \
+       __SEMAPHORE_INITIALIZER(name,1)
+
+#define __DECLARE_SEMAPHORE_GENERIC(name,count) \
+       struct semaphore name = __SEMAPHORE_INITIALIZER(name,count)
+
+#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1)
+#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0)
+
+static inline void sema_init (struct semaphore *sem, int val)
+{
+       *sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
+}
+
+static inline void init_MUTEX (struct semaphore *sem)
+{
+       sema_init(sem, 1);
+}
+
+static inline void init_MUTEX_LOCKED (struct semaphore *sem)
+{
+       sema_init(sem, 0);
+}
+
+extern void __down(struct semaphore *sem, unsigned long flags);
+extern int  __down_interruptible(struct semaphore *sem, unsigned long flags);
+extern void __up(struct semaphore *sem);
+
+static inline void down(struct semaphore *sem)
+{
+       unsigned long flags;
+
+#if SEMAPHORE_DEBUG
+       CHECK_MAGIC(sem->__magic);
+#endif
+
+       spin_lock_irqsave(&sem->wait_lock, flags);
+       if (likely(sem->counter > 0)) {
+               sem->counter--;
+               spin_unlock_irqrestore(&sem->wait_lock, flags);
+       }
+       else {
+               __down(sem, flags);
+       }
+}
+
+static inline int down_interruptible(struct semaphore *sem)
+{
+       unsigned long flags;
+       int ret = 0;
+
+#if SEMAPHORE_DEBUG
+       CHECK_MAGIC(sem->__magic);
+#endif
+
+       spin_lock_irqsave(&sem->wait_lock, flags);
+       if (likely(sem->counter > 0)) {
+               sem->counter--;
+               spin_unlock_irqrestore(&sem->wait_lock, flags);
+       }
+       else {
+               ret = __down_interruptible(sem, flags);
+       }
+       return ret;
+}
+
+/*
+ * non-blockingly attempt to down() a semaphore.
+ * - returns zero if we acquired it
+ */
+static inline int down_trylock(struct semaphore *sem)
+{
+       unsigned long flags;
+       int success = 0;
+
+#if SEMAPHORE_DEBUG
+       CHECK_MAGIC(sem->__magic);
+#endif
+
+       spin_lock_irqsave(&sem->wait_lock, flags);
+       if (sem->counter > 0) {
+               sem->counter--;
+               success = 1;
+       }
+       spin_unlock_irqrestore(&sem->wait_lock, flags);
+       return !success;
+}
+
+static inline void up(struct semaphore *sem)
+{
+       unsigned long flags;
+
+#if SEMAPHORE_DEBUG
+       CHECK_MAGIC(sem->__magic);
+#endif
+
+       spin_lock_irqsave(&sem->wait_lock, flags);
+       if (!list_empty(&sem->wait_list))
+               __up(sem);
+       else
+               sem->counter++;
+       spin_unlock_irqrestore(&sem->wait_lock, flags);
+}
+
+static inline int sem_getcount(struct semaphore *sem)
+{
+       return sem->counter;
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif
diff --git a/include/asm-frv/sembuf.h b/include/asm-frv/sembuf.h
new file mode 100644 (file)
index 0000000..164b127
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef _ASM_SEMBUF_H
+#define _ASM_SEMBUF_H
+
+/*
+ * The semid64_ds structure for FR-V architecture.
+ * Note extra padding because this structure is passed back and forth
+ * between kernel and user space.
+ *
+ * Pad space is left for:
+ * - 64-bit time_t to solve y2038 problem
+ * - 2 miscellaneous 32-bit values
+ */
+
+struct semid64_ds {
+       struct ipc64_perm       sem_perm;       /* permissions .. see ipc.h */
+       __kernel_time_t         sem_otime;      /* last semop time */
+       unsigned long           __unused1;
+       __kernel_time_t         sem_ctime;      /* last change time */
+       unsigned long           __unused2;
+       unsigned long           sem_nsems;      /* no. of semaphores in array */
+       unsigned long           __unused3;
+       unsigned long           __unused4;
+};
+
+#endif /* _ASM_SEMBUF_H */
+
diff --git a/include/asm-frv/serial-regs.h b/include/asm-frv/serial-regs.h
new file mode 100644 (file)
index 0000000..e1286bd
--- /dev/null
@@ -0,0 +1,44 @@
+/* serial-regs.h: serial port registers
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_SERIAL_REGS_H
+#define _ASM_SERIAL_REGS_H
+
+#include <linux/serial_reg.h>
+#include <asm/irc-regs.h>
+
+#define SERIAL_ICLK    33333333        /* the target serial input clock */
+#define UART0_BASE     0xfeff9c00
+#define UART1_BASE     0xfeff9c40
+
+#define __get_UART0(R) ({ __reg(UART0_BASE + (R) * 8) >> 24; })
+#define __get_UART1(R) ({ __reg(UART1_BASE + (R) * 8) >> 24; })
+#define __set_UART0(R,V) do { __reg(UART0_BASE + (R) * 8) = (V) << 24; } while(0)
+#define __set_UART1(R,V) do { __reg(UART1_BASE + (R) * 8) = (V) << 24; } while(0)
+
+#define __get_UART0_LSR() ({ __get_UART0(UART_LSR); })
+#define __get_UART1_LSR() ({ __get_UART1(UART_LSR); })
+
+#define __set_UART0_IER(V) __set_UART0(UART_IER,(V))
+#define __set_UART1_IER(V) __set_UART1(UART_IER,(V))
+
+/* serial prescaler select register */
+#define __get_UCPSR()  ({ *(volatile unsigned long *)(0xfeff9c90); })
+#define __set_UCPSR(V) do { *(volatile unsigned long *)(0xfeff9c90) = (V); } while(0)
+#define UCPSR_SELECT0  0x07000000
+#define UCPSR_SELECT1  0x38000000
+
+/* serial prescaler base value register */
+#define __get_UCPVR()  ({ *(volatile unsigned long *)(0xfeff9c98); mb(); })
+#define __set_UCPVR(V) do { *(volatile unsigned long *)(0xfeff9c98) = (V) << 24; mb(); } while(0)
+
+
+#endif /* _ASM_SERIAL_REGS_H */
diff --git a/include/asm-frv/serial.h b/include/asm-frv/serial.h
new file mode 100644 (file)
index 0000000..6917d55
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * serial.h
+ *
+ * Copyright (C) 2003 Develer S.r.l. (http://www.develer.com/)
+ * Author: Bernardo Innocenti <bernie@codewiz.org>
+ *
+ * Based on linux/include/asm-i386/serial.h
+ */
+#include <linux/config.h>
+#include <asm/serial-regs.h>
+
+/*
+ * the base baud is derived from the clock speed and so is variable
+ */
+#define BASE_BAUD 0
+
+#define STD_COM_FLAGS          ASYNC_BOOT_AUTOCONF
+
+#define SERIAL_PORT_DFNS
diff --git a/include/asm-frv/setup.h b/include/asm-frv/setup.h
new file mode 100644 (file)
index 0000000..0d293b9
--- /dev/null
@@ -0,0 +1,25 @@
+/* setup.h: setup stuff
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_SETUP_H
+#define _ASM_SETUP_H
+
+#include <linux/init.h>
+
+#ifndef __ASSEMBLY__
+
+#ifdef CONFIG_MMU
+extern unsigned long __initdata num_mappedpages;
+#endif
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* _ASM_SETUP_H */
diff --git a/include/asm-frv/shmbuf.h b/include/asm-frv/shmbuf.h
new file mode 100644 (file)
index 0000000..4c6e711
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef _ASM_SHMBUF_H
+#define _ASM_SHMBUF_H
+
+/*
+ * The shmid64_ds structure for FR-V architecture.
+ * Note extra padding because this structure is passed back and forth
+ * between kernel and user space.
+ *
+ * Pad space is left for:
+ * - 64-bit time_t to solve y2038 problem
+ * - 2 miscellaneous 32-bit values
+ */
+
+struct shmid64_ds {
+       struct ipc64_perm       shm_perm;       /* operation perms */
+       size_t                  shm_segsz;      /* size of segment (bytes) */
+       __kernel_time_t         shm_atime;      /* last attach time */
+       unsigned long           __unused1;
+       __kernel_time_t         shm_dtime;      /* last detach time */
+       unsigned long           __unused2;
+       __kernel_time_t         shm_ctime;      /* last change time */
+       unsigned long           __unused3;
+       __kernel_pid_t          shm_cpid;       /* pid of creator */
+       __kernel_pid_t          shm_lpid;       /* pid of last operator */
+       unsigned long           shm_nattch;     /* no. of current attaches */
+       unsigned long           __unused4;
+       unsigned long           __unused5;
+};
+
+struct shminfo64 {
+       unsigned long   shmmax;
+       unsigned long   shmmin;
+       unsigned long   shmmni;
+       unsigned long   shmseg;
+       unsigned long   shmall;
+       unsigned long   __unused1;
+       unsigned long   __unused2;
+       unsigned long   __unused3;
+       unsigned long   __unused4;
+};
+
+#endif /* _ASM_SHMBUF_H */
+
diff --git a/include/asm-frv/shmparam.h b/include/asm-frv/shmparam.h
new file mode 100644 (file)
index 0000000..ab71100
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _ASM_SHMPARAM_H
+#define _ASM_SHMPARAM_H
+
+#define        SHMLBA PAGE_SIZE                 /* attach addr a multiple of this */
+
+#endif /* _ASM_SHMPARAM_H */
+
diff --git a/include/asm-frv/sigcontext.h b/include/asm-frv/sigcontext.h
new file mode 100644 (file)
index 0000000..3b263f3
--- /dev/null
@@ -0,0 +1,26 @@
+/* sigcontext.h: FRV signal context
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ASM_SIGCONTEXT_H
+#define _ASM_SIGCONTEXT_H
+
+#include <asm/registers.h>
+
+/*
+ * Signal context structure - contains all info to do with the state
+ * before the signal handler was invoked.  Note: only add new entries
+ * to the end of the structure.
+ */
+struct sigcontext {
+       struct user_context     sc_context;
+       unsigned long           sc_oldmask;     /* old sigmask */
+} __attribute__((aligned(8)));
+
+#endif
diff --git a/include/asm-frv/siginfo.h b/include/asm-frv/siginfo.h
new file mode 100644 (file)
index 0000000..d3fd1ca
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _ASM_SIGINFO_H
+#define _ASM_SIGINFO_H
+
+#include <linux/types.h>
+#include <asm-generic/siginfo.h>
+
+#define FPE_MDAOVF     (__SI_FAULT|9)  /* media overflow */
+#undef NSIGFPE
+#define NSIGFPE                9
+
+#endif
+
diff --git a/include/asm-frv/signal.h b/include/asm-frv/signal.h
new file mode 100644 (file)
index 0000000..f18952f
--- /dev/null
@@ -0,0 +1,187 @@
+#ifndef _ASM_SIGNAL_H
+#define _ASM_SIGNAL_H
+
+#include <linux/types.h>
+
+/* Avoid too many header ordering problems.  */
+struct siginfo;
+
+#ifdef __KERNEL__
+/* Most things should be clean enough to redefine this at will, if care
+   is taken to make libc match.  */
+
+#define _NSIG          64
+#define _NSIG_BPW      32
+#define _NSIG_WORDS    (_NSIG / _NSIG_BPW)
+
+typedef unsigned long old_sigset_t;            /* at least 32 bits */
+
+typedef struct {
+       unsigned long sig[_NSIG_WORDS];
+} sigset_t;
+
+#else
+/* Here we must cater to libcs that poke about in kernel headers.  */
+
+#define NSIG           32
+typedef unsigned long sigset_t;
+
+#endif /* __KERNEL__ */
+
+#define SIGHUP          1
+#define SIGINT          2
+#define SIGQUIT                 3
+#define SIGILL          4
+#define SIGTRAP                 5
+#define SIGABRT                 6
+#define SIGIOT          6
+#define SIGBUS          7
+#define SIGFPE          8
+#define SIGKILL                 9
+#define SIGUSR1                10
+#define SIGSEGV                11
+#define SIGUSR2                12
+#define SIGPIPE                13
+#define SIGALRM                14
+#define SIGTERM                15
+#define SIGSTKFLT      16
+#define SIGCHLD                17
+#define SIGCONT                18
+#define SIGSTOP                19
+#define SIGTSTP                20
+#define SIGTTIN                21
+#define SIGTTOU                22
+#define SIGURG         23
+#define SIGXCPU                24
+#define SIGXFSZ                25
+#define SIGVTALRM      26
+#define SIGPROF                27
+#define SIGWINCH       28
+#define SIGIO          29
+#define SIGPOLL                SIGIO
+/*
+#define SIGLOST                29
+*/
+#define SIGPWR         30
+#define SIGSYS         31
+#define        SIGUNUSED       31
+
+/* These should not be considered constants from userland.  */
+#define SIGRTMIN       32
+#define SIGRTMAX       (_NSIG-1)
+
+/*
+ * SA_FLAGS values:
+ *
+ * SA_ONSTACK indicates that a registered stack_t will be used.
+ * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the
+ * SA_RESTART flag to get restarting signals (which were the default long ago)
+ * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop.
+ * SA_RESETHAND clears the handler when the signal is delivered.
+ * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies.
+ * SA_NODEFER prevents the current signal from being masked in the handler.
+ *
+ * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single
+ * Unix names RESETHAND and NODEFER respectively.
+ */
+#define SA_NOCLDSTOP   0x00000001
+#define SA_NOCLDWAIT   0x00000002 /* not supported yet */
+#define SA_SIGINFO     0x00000004
+#define SA_ONSTACK     0x08000000
+#define SA_RESTART     0x10000000
+#define SA_NODEFER     0x40000000
+#define SA_RESETHAND   0x80000000
+
+#define SA_NOMASK      SA_NODEFER
+#define SA_ONESHOT     SA_RESETHAND
+#define SA_INTERRUPT   0x20000000 /* dummy -- ignored */
+
+#define SA_RESTORER    0x04000000
+
+/*
+ * sigaltstack controls
+ */
+#define SS_ONSTACK     1
+#define SS_DISABLE     2
+
+#define MINSIGSTKSZ    2048
+#define SIGSTKSZ       8192
+
+#ifdef __KERNEL__
+
+/*
+ * These values of sa_flags are used only by the kernel as part of the
+ * irq handling routines.
+ *
+ * SA_INTERRUPT is also used by the irq handling routines.
+ * SA_SHIRQ is for shared interrupt support on PCI and EISA.
+ */
+#define SA_PROBE               SA_ONESHOT
+#define SA_SAMPLE_RANDOM       SA_RESTART
+#define SA_SHIRQ               0x04000000
+#endif
+
+#define SIG_BLOCK          0   /* for blocking signals */
+#define SIG_UNBLOCK        1   /* for unblocking signals */
+#define SIG_SETMASK        2   /* for setting the signal mask */
+
+/* Type of a signal handler.  */
+typedef void (*__sighandler_t)(int);
+
+#define SIG_DFL        ((__sighandler_t)0)     /* default signal handling */
+#define SIG_IGN        ((__sighandler_t)1)     /* ignore signal */
+#define SIG_ERR        ((__sighandler_t)-1)    /* error return from signal */
+
+#ifdef __KERNEL__
+struct old_sigaction {
+       __sighandler_t sa_handler;
+       old_sigset_t sa_mask;
+       unsigned long sa_flags;
+       void (*sa_restorer)(void);
+};
+
+struct sigaction {
+       __sighandler_t sa_handler;
+       unsigned long sa_flags;
+       void (*sa_restorer)(void);
+       sigset_t sa_mask;               /* mask last for extensibility */
+};
+
+struct k_sigaction {
+       struct sigaction sa;
+};
+#else
+/* Here we must cater to libcs that poke about in kernel headers.  */
+
+struct sigaction {
+       union {
+         __sighandler_t _sa_handler;
+         void (*_sa_sigaction)(int, struct siginfo *, void *);
+       } _u;
+       sigset_t sa_mask;
+       unsigned long sa_flags;
+       void (*sa_restorer)(void);
+};
+
+#define sa_handler     _u._sa_handler
+#define sa_sigaction   _u._sa_sigaction
+
+#endif /* __KERNEL__ */
+
+typedef struct sigaltstack {
+       void *ss_sp;
+       int ss_flags;
+       size_t ss_size;
+} stack_t;
+
+extern int do_signal(struct pt_regs *regs, sigset_t *oldset);
+#define ptrace_signal_deliver(regs, cookie) do { } while (0)
+
+#ifdef __KERNEL__
+
+#include <asm/sigcontext.h>
+#undef __HAVE_ARCH_SIG_BITOPS
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_SIGNAL_H */
diff --git a/include/asm-frv/smp.h b/include/asm-frv/smp.h
new file mode 100644 (file)
index 0000000..5ca7716
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __ASM_SMP_H
+#define __ASM_SMP_H
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SMP
+#error SMP not supported
+#endif
+
+#endif
diff --git a/include/asm-frv/socket.h b/include/asm-frv/socket.h
new file mode 100644 (file)
index 0000000..c3be17c
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _ASM_SOCKET_H
+#define _ASM_SOCKET_H
+
+#include <asm/sockios.h>
+
+/* For setsockopt(2) */
+#define SOL_SOCKET     1
+
+#define SO_DEBUG       1
+#define SO_REUSEADDR   2
+#define SO_TYPE                3
+#define SO_ERROR       4
+#define SO_DONTROUTE   5
+#define SO_BROADCAST   6
+#define SO_SNDBUF      7
+#define SO_RCVBUF      8
+#define SO_KEEPALIVE   9
+#define SO_OOBINLINE   10
+#define SO_NO_CHECK    11
+#define SO_PRIORITY    12
+#define SO_LINGER      13
+#define SO_BSDCOMPAT   14
+/* To add :#define SO_REUSEPORT 15 */
+#define SO_PASSCRED    16
+#define SO_PEERCRED    17
+#define SO_RCVLOWAT    18
+#define SO_SNDLOWAT    19
+#define SO_RCVTIMEO    20
+#define SO_SNDTIMEO    21
+
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define SO_SECURITY_AUTHENTICATION             22
+#define SO_SECURITY_ENCRYPTION_TRANSPORT       23
+#define SO_SECURITY_ENCRYPTION_NETWORK         24
+
+#define SO_BINDTODEVICE        25
+
+/* Socket filtering */
+#define SO_ATTACH_FILTER        26
+#define SO_DETACH_FILTER        27
+
+#define SO_PEERNAME             28
+#define SO_TIMESTAMP           29
+#define SCM_TIMESTAMP          SO_TIMESTAMP
+
+#define SO_ACCEPTCONN          30
+
+#define SO_PEERSEC             31
+
+#endif /* _ASM_SOCKET_H */
+
diff --git a/include/asm-frv/sockios.h b/include/asm-frv/sockios.h
new file mode 100644 (file)
index 0000000..8a6e4b2
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _ASM_SOCKIOS__
+#define _ASM_SOCKIOS__
+
+/* Socket-level I/O control calls. */
+#define FIOSETOWN      0x8901
+#define SIOCSPGRP      0x8902
+#define FIOGETOWN      0x8903
+#define SIOCGPGRP      0x8904
+#define SIOCATMARK     0x8905
+#define SIOCGSTAMP     0x8906          /* Get stamp */
+
+#endif /* _ASM_SOCKIOS__ */
+
diff --git a/include/asm-frv/spinlock.h b/include/asm-frv/spinlock.h
new file mode 100644 (file)
index 0000000..fe385f4
--- /dev/null
@@ -0,0 +1,17 @@
+/* spinlock.h: spinlocks for FR-V
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_SPINLOCK_H
+#define _ASM_SPINLOCK_H
+
+#error no spinlocks for FR-V yet
+
+#endif /* _ASM_SPINLOCK_H */
diff --git a/include/asm-frv/spr-regs.h b/include/asm-frv/spr-regs.h
new file mode 100644 (file)
index 0000000..ef472f0
--- /dev/null
@@ -0,0 +1,401 @@
+/* spr-regs.h: special-purpose registers on the FRV
+ *
+ * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_SPR_REGS_H
+#define _ASM_SPR_REGS_H
+
+/*
+ * PSR - Processor Status Register
+ */
+#define PSR_ET                 0x00000001      /* enable interrupts/exceptions flag */
+#define PSR_PS                 0x00000002      /* previous supervisor mode flag */
+#define PSR_S                  0x00000004      /* supervisor mode flag */
+#define PSR_PIL                        0x00000078      /* processor external interrupt level */
+#define PSR_PIL_0              0x00000000      /* - no interrupt in progress */
+#define PSR_PIL_13             0x00000068      /* - debugging only */
+#define PSR_PIL_14             0x00000070      /* - debugging in progress */
+#define PSR_PIL_15             0x00000078      /* - NMI in progress */
+#define PSR_EM                 0x00000080      /* enable media operation */
+#define PSR_EF                 0x00000100      /* enable FPU operation */
+#define PSR_BE                 0x00001000      /* endianness mode */
+#define PSR_BE_LE              0x00000000      /* - little endian mode */
+#define PSR_BE_BE              0x00001000      /* - big endian mode */
+#define PSR_CM                 0x00002000      /* conditional mode */
+#define PSR_NEM                        0x00004000      /* non-excepting mode */
+#define PSR_ICE                        0x00010000      /* in-circuit emulation mode */
+#define PSR_VERSION_SHIFT      24              /* CPU silicon ID */
+#define PSR_IMPLE_SHIFT                28              /* CPU core ID */
+
+#define PSR_VERSION(psr)       (((psr) >> PSR_VERSION_SHIFT) & 0xf)
+#define PSR_IMPLE(psr)         (((psr) >> PSR_IMPLE_SHIFT) & 0xf)
+
+#define PSR_IMPLE_FR401                0x2
+#define PSR_VERSION_FR401_MB93401      0x0
+#define PSR_VERSION_FR401_MB93401A     0x1
+#define PSR_VERSION_FR401_MB93403      0x2
+
+#define PSR_IMPLE_FR405                0x4
+#define PSR_VERSION_FR405_MB93405      0x0
+
+#define PSR_IMPLE_FR451                0x5
+#define PSR_VERSION_FR451_MB93451      0x0
+
+#define PSR_IMPLE_FR501                0x1
+#define PSR_VERSION_FR501_MB93501      0x1
+#define PSR_VERSION_FR501_MB93501A     0x2
+
+#define PSR_IMPLE_FR551                0x3
+#define PSR_VERSION_FR551_MB93555      0x1
+
+#define __get_PSR()    ({ unsigned long x; asm volatile("movsg psr,%0" : "=r"(x)); x; })
+#define __set_PSR(V)   do { asm volatile("movgs %0,psr" : : "r"(V)); } while(0)
+
+/*
+ * TBR - Trap Base Register
+ */
+#define TBR_TT                 0x00000ff0
+#define TBR_TT_INSTR_MMU_MISS  (0x01 << 4)
+#define TBR_TT_INSTR_ACC_ERROR (0x02 << 4)
+#define TBR_TT_INSTR_ACC_EXCEP (0x03 << 4)
+#define TBR_TT_PRIV_INSTR      (0x06 << 4)
+#define TBR_TT_ILLEGAL_INSTR   (0x07 << 4)
+#define TBR_TT_FP_EXCEPTION    (0x0d << 4)
+#define TBR_TT_MP_EXCEPTION    (0x0e << 4)
+#define TBR_TT_DATA_ACC_ERROR  (0x11 << 4)
+#define TBR_TT_DATA_MMU_MISS   (0x12 << 4)
+#define TBR_TT_DATA_ACC_EXCEP  (0x13 << 4)
+#define TBR_TT_DATA_STR_ERROR  (0x14 << 4)
+#define TBR_TT_DIVISION_EXCEP  (0x17 << 4)
+#define TBR_TT_COMMIT_EXCEP    (0x19 << 4)
+#define TBR_TT_INSTR_TLB_MISS  (0x1a << 4)
+#define TBR_TT_DATA_TLB_MISS   (0x1b << 4)
+#define TBR_TT_DATA_DAT_EXCEP  (0x1d << 4)
+#define TBR_TT_DECREMENT_TIMER (0x1f << 4)
+#define TBR_TT_COMPOUND_EXCEP  (0x20 << 4)
+#define TBR_TT_INTERRUPT_1     (0x21 << 4)
+#define TBR_TT_INTERRUPT_2     (0x22 << 4)
+#define TBR_TT_INTERRUPT_3     (0x23 << 4)
+#define TBR_TT_INTERRUPT_4     (0x24 << 4)
+#define TBR_TT_INTERRUPT_5     (0x25 << 4)
+#define TBR_TT_INTERRUPT_6     (0x26 << 4)
+#define TBR_TT_INTERRUPT_7     (0x27 << 4)
+#define TBR_TT_INTERRUPT_8     (0x28 << 4)
+#define TBR_TT_INTERRUPT_9     (0x29 << 4)
+#define TBR_TT_INTERRUPT_10    (0x2a << 4)
+#define TBR_TT_INTERRUPT_11    (0x2b << 4)
+#define TBR_TT_INTERRUPT_12    (0x2c << 4)
+#define TBR_TT_INTERRUPT_13    (0x2d << 4)
+#define TBR_TT_INTERRUPT_14    (0x2e << 4)
+#define TBR_TT_INTERRUPT_15    (0x2f << 4)
+#define TBR_TT_TRAP0           (0x80 << 4)
+#define TBR_TT_TRAP1           (0x81 << 4)
+#define TBR_TT_TRAP2           (0x82 << 4)
+#define TBR_TT_TRAP126         (0xfe << 4)
+#define TBR_TT_BREAK           (0xff << 4)
+
+#define __get_TBR()    ({ unsigned long x; asm volatile("movsg tbr,%0" : "=r"(x)); x; })
+
+/*
+ * HSR0 - Hardware Status Register 0
+ */
+#define HSR0_PDM               0x00000007      /* power down mode */
+#define HSR0_PDM_NORMAL                0x00000000      /* - normal mode */
+#define HSR0_PDM_CORE_SLEEP    0x00000001      /* - CPU core sleep mode */
+#define HSR0_PDM_BUS_SLEEP     0x00000003      /* - bus sleep mode */
+#define HSR0_PDM_PLL_RUN       0x00000005      /* - PLL run */
+#define HSR0_PDM_PLL_STOP      0x00000007      /* - PLL stop */
+#define HSR0_GRLE              0x00000040      /* GR lower register set enable */
+#define HSR0_GRHE              0x00000080      /* GR higher register set enable */
+#define HSR0_FRLE              0x00000100      /* FR lower register set enable */
+#define HSR0_FRHE              0x00000200      /* FR higher register set enable */
+#define HSR0_GRN               0x00000400      /* GR quantity */
+#define HSR0_GRN_64            0x00000000      /* - 64 GR registers */
+#define HSR0_GRN_32            0x00000400      /* - 32 GR registers */
+#define HSR0_FRN               0x00000800      /* FR quantity */
+#define HSR0_FRN_64            0x00000000      /* - 64 FR registers */
+#define HSR0_FRN_32            0x00000800      /* - 32 FR registers */
+#define HSR0_SA                        0x00001000      /* start address (RAMBOOT#) */
+#define HSR0_ETMI              0x00008000      /* enable TIMERI (64-bit up timer) */
+#define HSR0_ETMD              0x00004000      /* enable TIMERD (32-bit down timer) */
+#define HSR0_PEDAT             0x00010000      /* previous DAT mode */
+#define HSR0_XEDAT             0x00020000      /* exception DAT mode */
+#define HSR0_EDAT              0x00080000      /* enable DAT mode */
+#define HSR0_RME               0x00400000      /* enable RAM mode */
+#define HSR0_EMEM              0x00800000      /* enable MMU_Miss mask */
+#define HSR0_EXMMU             0x01000000      /* enable extended MMU mode */
+#define HSR0_EDMMU             0x02000000      /* enable data MMU */
+#define HSR0_EIMMU             0x04000000      /* enable instruction MMU */
+#define HSR0_CBM               0x08000000      /* copy back mode */
+#define HSR0_CBM_WRITE_THRU    0x00000000      /* - write through */
+#define HSR0_CBM_COPY_BACK     0x08000000      /* - copy back */
+#define HSR0_NWA               0x10000000      /* no write allocate */
+#define HSR0_DCE               0x40000000      /* data cache enable */
+#define HSR0_ICE               0x80000000      /* instruction cache enable */
+
+#define __get_HSR(R)   ({ unsigned long x; asm volatile("movsg hsr"#R",%0" : "=r"(x)); x; })
+#define __set_HSR(R,V) do { asm volatile("movgs %0,hsr"#R : : "r"(V)); } while(0)
+
+/*
+ * CCR - Condition Codes Register
+ */
+#define CCR_FCC0               0x0000000f      /* FP/Media condition 0 (fcc0 reg) */
+#define CCR_FCC1               0x000000f0      /* FP/Media condition 1 (fcc1 reg) */
+#define CCR_FCC2               0x00000f00      /* FP/Media condition 2 (fcc2 reg) */
+#define CCR_FCC3               0x0000f000      /* FP/Media condition 3 (fcc3 reg) */
+#define CCR_ICC0               0x000f0000      /* Integer condition 0 (icc0 reg) */
+#define CCR_ICC0_C             0x00010000      /* - Carry flag */
+#define CCR_ICC0_V             0x00020000      /* - Overflow flag */
+#define CCR_ICC0_Z             0x00040000      /* - Zero flag */
+#define CCR_ICC0_N             0x00080000      /* - Negative flag */
+#define CCR_ICC1               0x00f00000      /* Integer condition 1 (icc1 reg) */
+#define CCR_ICC2               0x0f000000      /* Integer condition 2 (icc2 reg) */
+#define CCR_ICC3               0xf0000000      /* Integer condition 3 (icc3 reg) */
+
+/*
+ * CCCR - Condition Codes for Conditional Instructions Register
+ */
+#define CCCR_CC0               0x00000003      /* condition 0 (cc0 reg) */
+#define CCCR_CC0_FALSE         0x00000002      /* - condition is false */
+#define CCCR_CC0_TRUE          0x00000003      /* - condition is true */
+#define CCCR_CC1               0x0000000c      /* condition 1 (cc1 reg) */
+#define CCCR_CC2               0x00000030      /* condition 2 (cc2 reg) */
+#define CCCR_CC3               0x000000c0      /* condition 3 (cc3 reg) */
+#define CCCR_CC4               0x00000300      /* condition 4 (cc4 reg) */
+#define CCCR_CC5               0x00000c00      /* condition 5 (cc5 reg) */
+#define CCCR_CC6               0x00003000      /* condition 6 (cc6 reg) */
+#define CCCR_CC7               0x0000c000      /* condition 7 (cc7 reg) */
+
+/*
+ * ISR - Integer Status Register
+ */
+#define ISR_EMAM               0x00000001      /* memory misaligned access handling */
+#define ISR_EMAM_EXCEPTION     0x00000000      /* - generate exception */
+#define ISR_EMAM_FUDGE         0x00000001      /* - mask out invalid address bits */
+#define ISR_AEXC               0x00000004      /* accrued [overflow] exception */
+#define ISR_DTT                        0x00000018      /* division type trap */
+#define ISR_DTT_IGNORE         0x00000000      /* - ignore division error */
+#define ISR_DTT_DIVBYZERO      0x00000008      /* - generate exception */
+#define ISR_DTT_OVERFLOW       0x00000010      /* - record overflow */
+#define ISR_EDE                        0x00000020      /* enable division exception */
+#define ISR_PLI                        0x20000000      /* pre-load instruction information */
+#define ISR_QI                 0x80000000      /* quad data implementation information */
+
+/*
+ * EPCR0 - Exception PC Register
+ */
+#define EPCR0_V                        0x00000001      /* register content validity indicator */
+#define EPCR0_PC               0xfffffffc      /* faulting instruction address */
+
+/*
+ * ESR0/14/15 - Exception Status Register
+ */
+#define ESRx_VALID             0x00000001      /* register content validity indicator */
+#define ESRx_EC                        0x0000003e      /* exception type */
+#define ESRx_EC_DATA_STORE     0x00000000      /* - data_store_error */
+#define ESRx_EC_INSN_ACCESS    0x00000006      /* - instruction_access_error */
+#define ESRx_EC_PRIV_INSN      0x00000008      /* - privileged_instruction */
+#define ESRx_EC_ILL_INSN       0x0000000a      /* - illegal_instruction */
+#define ESRx_EC_MP_EXCEP       0x0000001c      /* - mp_exception */
+#define ESRx_EC_DATA_ACCESS    0x00000020      /* - data_access_error */
+#define ESRx_EC_DIVISION       0x00000026      /* - division_exception */
+#define ESRx_EC_ITLB_MISS      0x00000034      /* - instruction_access_TLB_miss */
+#define ESRx_EC_DTLB_MISS      0x00000036      /* - data_access_TLB_miss */
+#define ESRx_EC_DATA_ACCESS_DAT        0x0000003a      /* - data_access_DAT_exception */
+
+#define ESR0_IAEC              0x00000100      /* info for instruction-access-exception */
+#define ESR0_IAEC_RESV         0x00000000      /* - reserved */
+#define ESR0_IAEC_PROT_VIOL    0x00000100      /* - protection violation */
+
+#define ESR0_ATXC              0x00f00000      /* address translation exception code */
+#define ESR0_ATXC_MMU_MISS     0x00000000      /* - MMU miss exception and more (?) */
+#define ESR0_ATXC_MULTI_DAT    0x00800000      /* - multiple DAT entry hit */
+#define ESR0_ATXC_MULTI_SAT    0x00900000      /* - multiple SAT entry hit */
+#define ESR0_ATXC_AMRTLB_MISS  0x00a00000      /* - MMU/TLB miss exception */
+#define ESR0_ATXC_PRIV_EXCEP   0x00c00000      /* - privilege protection fault */
+#define ESR0_ATXC_WP_EXCEP     0x00d00000      /* - write protection fault */
+
+#define ESR0_EAV               0x00000800      /* true if EAR0 register valid */
+#define ESR15_EAV              0x00000800      /* true if EAR15 register valid */
+
+/*
+ * ESFR1 - Exception Status Valid Flag Register
+ */
+#define ESFR1_ESR0             0x00000001      /* true if ESR0 is valid */
+#define ESFR1_ESR14            0x00004000      /* true if ESR14 is valid */
+#define ESFR1_ESR15            0x00008000      /* true if ESR15 is valid */
+
+/*
+ * MSR - Media Status Register
+ */
+#define MSR0_AOVF              0x00000001      /* overflow exception accrued */
+#define MSRx_OVF               0x00000002      /* overflow exception detected */
+#define MSRx_SIE               0x0000003c      /* last SIMD instruction exception detected */
+#define MSRx_SIE_NONE          0x00000000      /* - none detected */
+#define MSRx_SIE_FRkHI_ACCk    0x00000020      /* - exception at FRkHI or ACCk */
+#define MSRx_SIE_FRkLO_ACCk1   0x00000010      /* - exception at FRkLO or ACCk+1 */
+#define MSRx_SIE_FRk1HI_ACCk2  0x00000008      /* - exception at FRk+1HI or ACCk+2 */
+#define MSRx_SIE_FRk1LO_ACCk3  0x00000004      /* - exception at FRk+1LO or ACCk+3 */
+#define MSR0_MTT               0x00007000      /* type of last media trap detected */
+#define MSR0_MTT_NONE          0x00000000      /* - none detected */
+#define MSR0_MTT_OVERFLOW      0x00001000      /* - overflow detected */
+#define MSR0_HI                        0x00c00000      /* hardware implementation */
+#define MSR0_HI_ROUNDING       0x00000000      /* - rounding mode */
+#define MSR0_HI_NONROUNDING    0x00c00000      /* - non-rounding mode */
+#define MSR0_EMCI              0x01000000      /* enable media custom instructions */
+#define MSR0_SRDAV             0x10000000      /* select rounding mode of MAVEH */
+#define MSR0_SRDAV_RDAV                0x00000000      /* - controlled by MSR.RDAV */
+#define MSR0_SRDAV_RD          0x10000000      /* - controlled by MSR.RD */
+#define MSR0_RDAV              0x20000000      /* rounding mode of MAVEH */
+#define MSR0_RDAV_NEAREST_MI   0x00000000      /* - round to nearest minus */
+#define MSR0_RDAV_NEAREST_PL   0x20000000      /* - round to nearest plus */
+#define MSR0_RD                        0xc0000000      /* rounding mode */
+#define MSR0_RD_NEAREST                0x00000000      /* - nearest */
+#define MSR0_RD_ZERO           0x40000000      /* - zero */
+#define MSR0_RD_POS_INF                0x80000000      /* - postive infinity */
+#define MSR0_RD_NEG_INF                0xc0000000      /* - negative infinity */
+
+/*
+ * IAMPR0-7 - Instruction Address Mapping Register
+ * DAMPR0-7 - Data Address Mapping Register
+ */
+#define xAMPRx_V               0x00000001      /* register content validity indicator */
+#define DAMPRx_WP              0x00000002      /* write protect */
+#define DAMPRx_WP_RW           0x00000000      /* - read/write */
+#define DAMPRx_WP_RO           0x00000002      /* - read-only */
+#define xAMPRx_C               0x00000004      /* cached/uncached */
+#define xAMPRx_C_CACHED                0x00000000      /* - cached */
+#define xAMPRx_C_UNCACHED      0x00000004      /* - uncached */
+#define xAMPRx_S               0x00000008      /* supervisor only */
+#define xAMPRx_S_USER          0x00000000      /* - userspace can access */
+#define xAMPRx_S_KERNEL                0x00000008      /* - kernel only */
+#define xAMPRx_SS              0x000000f0      /* segment size */
+#define xAMPRx_SS_16Kb         0x00000000      /* - 16 kilobytes */
+#define xAMPRx_SS_64Kb         0x00000010      /* - 64 kilobytes */
+#define xAMPRx_SS_256Kb                0x00000020      /* - 256 kilobytes */
+#define xAMPRx_SS_1Mb          0x00000030      /* - 1 megabyte */
+#define xAMPRx_SS_2Mb          0x00000040      /* - 2 megabytes */
+#define xAMPRx_SS_4Mb          0x00000050      /* - 4 megabytes */
+#define xAMPRx_SS_8Mb          0x00000060      /* - 8 megabytes */
+#define xAMPRx_SS_16Mb         0x00000070      /* - 16 megabytes */
+#define xAMPRx_SS_32Mb         0x00000080      /* - 32 megabytes */
+#define xAMPRx_SS_64Mb         0x00000090      /* - 64 megabytes */
+#define xAMPRx_SS_128Mb                0x000000a0      /* - 128 megabytes */
+#define xAMPRx_SS_256Mb                0x000000b0      /* - 256 megabytes */
+#define xAMPRx_SS_512Mb                0x000000c0      /* - 512 megabytes */
+#define xAMPRx_RESERVED8       0x00000100      /* reserved bit */
+#define xAMPRx_NG              0x00000200      /* non-global */
+#define xAMPRx_L               0x00000400      /* locked */
+#define xAMPRx_M               0x00000800      /* modified */
+#define xAMPRx_D               0x00001000      /* DAT entry */
+#define xAMPRx_RESERVED13      0x00002000      /* reserved bit */
+#define xAMPRx_PPFN            0xfff00000      /* physical page frame number */
+
+#define xAMPRx_V_BIT           0
+#define DAMPRx_WP_BIT          1
+#define xAMPRx_C_BIT           2
+#define xAMPRx_S_BIT           3
+#define xAMPRx_RESERVED8_BIT   8
+#define xAMPRx_NG_BIT          9
+#define xAMPRx_L_BIT           10
+#define xAMPRx_M_BIT           11
+#define xAMPRx_D_BIT           12
+#define xAMPRx_RESERVED13_BIT  13
+
+#define __get_IAMPR(R) ({ unsigned long x; asm volatile("movsg iampr"#R",%0" : "=r"(x)); x; })
+#define __get_DAMPR(R) ({ unsigned long x; asm volatile("movsg dampr"#R",%0" : "=r"(x)); x; })
+
+#define __get_IAMLR(R) ({ unsigned long x; asm volatile("movsg iamlr"#R",%0" : "=r"(x)); x; })
+#define __get_DAMLR(R) ({ unsigned long x; asm volatile("movsg damlr"#R",%0" : "=r"(x)); x; })
+
+#define __set_IAMPR(R,V)       do { asm volatile("movgs %0,iampr"#R : : "r"(V)); } while(0)
+#define __set_DAMPR(R,V)       do { asm volatile("movgs %0,dampr"#R : : "r"(V)); } while(0)
+
+#define __set_IAMLR(R,V)       do { asm volatile("movgs %0,iamlr"#R : : "r"(V)); } while(0)
+#define __set_DAMLR(R,V)       do { asm volatile("movgs %0,damlr"#R : : "r"(V)); } while(0)
+
+#define save_dampr(R, _dampr)                                  \
+do {                                                           \
+       asm volatile("movsg dampr"R",%0" : "=r"(_dampr));       \
+} while(0)
+
+#define restore_dampr(R, _dampr)                       \
+do {                                                   \
+       asm volatile("movgs %0,dampr"R :: "r"(_dampr)); \
+} while(0)
+
+/*
+ * AMCR - Address Mapping Control Register
+ */
+#define AMCR_IAMRN             0x000000ff      /* quantity of IAMPR registers */
+#define AMCR_DAMRN             0x0000ff00      /* quantity of DAMPR registers */
+
+/*
+ * TTBR - Address Translation Table Base Register
+ */
+#define __get_TTBR()           ({ unsigned long x; asm volatile("movsg ttbr,%0" : "=r"(x)); x; })
+
+/*
+ * TPXR - TLB Probe Extend Register
+ */
+#define TPXR_E                 0x00000001
+#define TPXR_LMAX_SHIFT                20
+#define TPXR_LMAX_SMASK                0xf
+#define TPXR_WMAX_SHIFT                24
+#define TPXR_WMAX_SMASK                0xf
+#define TPXR_WAY_SHIFT         28
+#define TPXR_WAY_SMASK         0xf
+
+/*
+ * DCR - Debug Control Register
+ */
+#define DCR_IBCE3              0x00000001      /* break on conditional insn pointed to by IBAR3 */
+#define DCR_IBE3               0x00000002      /* break on insn pointed to by IBAR3 */
+#define DCR_IBCE1              0x00000004      /* break on conditional insn pointed to by IBAR2 */
+#define DCR_IBE1               0x00000008      /* break on insn pointed to by IBAR2 */
+#define DCR_IBCE2              0x00000010      /* break on conditional insn pointed to by IBAR1 */
+#define DCR_IBE2               0x00000020      /* break on insn pointed to by IBAR1 */
+#define DCR_IBCE0              0x00000040      /* break on conditional insn pointed to by IBAR0 */
+#define DCR_IBE0               0x00000080      /* break on insn pointed to by IBAR0 */
+
+#define DCR_DDBE1              0x00004000      /* use DBDR1x when checking DBAR1 */
+#define DCR_DWBE1              0x00008000      /* break on store to address in DBAR1/DBMR1x */
+#define DCR_DRBE1              0x00010000      /* break on load from address in DBAR1/DBMR1x */
+#define DCR_DDBE0              0x00020000      /* use DBDR0x when checking DBAR0 */
+#define DCR_DWBE0              0x00040000      /* break on store to address in DBAR0/DBMR0x */
+#define DCR_DRBE0              0x00080000      /* break on load from address in DBAR0/DBMR0x */
+
+#define DCR_EIM                        0x0c000000      /* external interrupt disable */
+#define DCR_IBM                        0x10000000      /* instruction break disable */
+#define DCR_SE                 0x20000000      /* single step enable */
+#define DCR_EBE                        0x40000000      /* exception break enable */
+
+/*
+ * BRR - Break Interrupt Request Register
+ */
+#define BRR_ST                 0x00000001      /* single-step detected */
+#define BRR_SB                 0x00000002      /* break instruction detected */
+#define BRR_BB                 0x00000004      /* branch with hint detected */
+#define BRR_CBB                        0x00000008      /* branch to LR detected */
+#define BRR_IBx                        0x000000f0      /* hardware breakpoint detected */
+#define BRR_DBx                        0x00000f00      /* hardware watchpoint detected */
+#define BRR_DBNEx              0x0000f000      /* ? */
+#define BRR_EBTT               0x00ff0000      /* trap type of exception break */
+#define BRR_TB                 0x10000000      /* external break request detected */
+#define BRR_CB                 0x20000000      /* ICE break command detected */
+#define BRR_EB                 0x40000000      /* exception break detected */
+
+/*
+ * BPSR - Break PSR Save Register
+ */
+#define BPSR_BET               0x00000001      /* former PSR.ET */
+#define BPSR_BS                        0x00001000      /* former PSR.S */
+
+#endif /* _ASM_SPR_REGS_H */
diff --git a/include/asm-frv/stat.h b/include/asm-frv/stat.h
new file mode 100644 (file)
index 0000000..ce56de9
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef _ASM_STAT_H
+#define _ASM_STAT_H
+
+struct __old_kernel_stat {
+       unsigned short st_dev;
+       unsigned short st_ino;
+       unsigned short st_mode;
+       unsigned short st_nlink;
+       unsigned short st_uid;
+       unsigned short st_gid;
+       unsigned short st_rdev;
+       unsigned long  st_size;
+       unsigned long  st_atime;
+       unsigned long  st_mtime;
+       unsigned long  st_ctime;
+};
+
+/* This matches struct stat in uClibc/glibc.  */
+struct stat {
+       unsigned char __pad1[6];
+       unsigned short st_dev;
+
+       unsigned long __pad2;
+       unsigned long st_ino;
+
+       unsigned short __pad3;
+       unsigned short st_mode;
+       unsigned short __pad4;
+       unsigned short st_nlink;
+
+       unsigned short __pad5;
+       unsigned short st_uid;
+       unsigned short __pad6;
+       unsigned short st_gid;
+
+       unsigned char __pad7[6];
+       unsigned short st_rdev;
+
+       unsigned long __pad8;
+       unsigned long st_size;
+
+       unsigned long __pad9;           /* align 64-bit st_blocks to 2-word */
+       unsigned long st_blksize;
+
+       unsigned long __pad10;  /* future possible st_blocks high bits */
+       unsigned long st_blocks;        /* Number 512-byte blocks allocated. */
+
+       unsigned long __unused1;
+       unsigned long st_atime;
+
+       unsigned long __unused2;
+       unsigned long st_mtime;
+
+       unsigned long __unused3;
+       unsigned long st_ctime;
+
+       unsigned long long __unused4;
+};
+
+/* This matches struct stat64 in uClibc/glibc.  The layout is exactly
+   the same as that of struct stat above, with 64-bit types taking up
+   space that was formerly used by padding.  stat syscalls are still
+   different from stat64, though, in that the former tests for
+   overflow.  */
+struct stat64 {
+       unsigned char __pad1[6];
+       unsigned short st_dev;
+
+       unsigned long long st_ino;
+
+       unsigned int st_mode;
+       unsigned int st_nlink;
+
+       unsigned long st_uid;
+       unsigned long st_gid;
+
+       unsigned char __pad2[6];
+       unsigned short st_rdev;
+
+       long long st_size;
+
+       unsigned long __pad3;           /* align 64-bit st_blocks to 2-word */
+       unsigned long st_blksize;
+
+       unsigned long __pad4;           /* future possible st_blocks high bits */
+       unsigned long st_blocks;        /* Number 512-byte blocks allocated. */
+
+       unsigned long st_atime_nsec;
+       unsigned long st_atime;
+
+       unsigned int st_mtime_nsec;
+       unsigned long st_mtime;
+
+       unsigned long st_ctime_nsec;
+       unsigned long st_ctime;
+
+       unsigned long long __unused4;
+};
+
+#endif /* _ASM_STAT_H */
diff --git a/include/asm-frv/statfs.h b/include/asm-frv/statfs.h
new file mode 100644 (file)
index 0000000..741f586
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _ASM_STATFS_H
+#define _ASM_STATFS_H
+
+#include <asm-generic/statfs.h>
+
+#endif /* _ASM_STATFS_H */
+
diff --git a/include/asm-frv/string.h b/include/asm-frv/string.h
new file mode 100644 (file)
index 0000000..5ed310f
--- /dev/null
@@ -0,0 +1,51 @@
+/* string.h: FRV string handling
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_STRING_H_
+#define _ASM_STRING_H_
+
+#ifdef __KERNEL__ /* only set these up for kernel code */
+
+#define __HAVE_ARCH_MEMSET 1
+#define __HAVE_ARCH_MEMCPY 1
+
+extern void *memset(void *, int, __kernel_size_t);
+extern void *memcpy(void *, const void *, __kernel_size_t);
+
+#else /* KERNEL */
+
+/*
+ *     let user libraries deal with these,
+ *     IMHO the kernel has no place defining these functions for user apps
+ */
+
+#define __HAVE_ARCH_STRCPY 1
+#define __HAVE_ARCH_STRNCPY 1
+#define __HAVE_ARCH_STRCAT 1
+#define __HAVE_ARCH_STRNCAT 1
+#define __HAVE_ARCH_STRCMP 1
+#define __HAVE_ARCH_STRNCMP 1
+#define __HAVE_ARCH_STRNICMP 1
+#define __HAVE_ARCH_STRCHR 1
+#define __HAVE_ARCH_STRRCHR 1
+#define __HAVE_ARCH_STRSTR 1
+#define __HAVE_ARCH_STRLEN 1
+#define __HAVE_ARCH_STRNLEN 1
+#define __HAVE_ARCH_MEMSET 1
+#define __HAVE_ARCH_MEMCPY 1
+#define __HAVE_ARCH_MEMMOVE 1
+#define __HAVE_ARCH_MEMSCAN 1
+#define __HAVE_ARCH_MEMCMP 1
+#define __HAVE_ARCH_MEMCHR 1
+#define __HAVE_ARCH_STRTOK 1
+
+#endif /* KERNEL */
+#endif /* _ASM_STRING_H_ */
diff --git a/include/asm-frv/suspend.h b/include/asm-frv/suspend.h
new file mode 100644 (file)
index 0000000..5fa7b5a
--- /dev/null
@@ -0,0 +1,20 @@
+/* suspend.h: suspension stuff
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_SUSPEND_H
+#define _ASM_SUSPEND_H
+
+static inline int arch_prepare_suspend(void)
+{
+       return 0;
+}
+
+#endif /* _ASM_SUSPEND_H */
diff --git a/include/asm-frv/system.h b/include/asm-frv/system.h
new file mode 100644 (file)
index 0000000..29cfa21
--- /dev/null
@@ -0,0 +1,126 @@
+/* system.h: FR-V CPU control definitions
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_SYSTEM_H
+#define _ASM_SYSTEM_H
+
+#include <linux/config.h> /* get configuration macros */
+#include <linux/linkage.h>
+#include <asm/atomic.h>
+
+struct thread_struct;
+
+#define prepare_to_switch()    do { } while(0)
+
+/*
+ * switch_to(prev, next) should switch from task `prev' to `next'
+ * `prev' will never be the same as `next'.
+ * The `mb' is to tell GCC not to cache `current' across this call.
+ */
+extern asmlinkage
+struct task_struct *__switch_to(struct thread_struct *prev_thread,
+                               struct thread_struct *next_thread,
+                               struct task_struct *prev);
+
+#define switch_to(prev, next, last)                                    \
+do {                                                                   \
+       (prev)->thread.sched_lr =                                       \
+               (unsigned long) __builtin_return_address(0);            \
+       (last) = __switch_to(&(prev)->thread, &(next)->thread, (prev)); \
+       mb();                                                           \
+} while(0)
+
+/*
+ * interrupt flag manipulation
+ */
+#define local_irq_disable()                            \
+do {                                                   \
+       unsigned long psr;                              \
+       asm volatile("  movsg   psr,%0          \n"     \
+                    "  andi    %0,%2,%0        \n"     \
+                    "  ori     %0,%1,%0        \n"     \
+                    "  movgs   %0,psr          \n"     \
+                    : "=r"(psr)                        \
+                    : "i" (PSR_PIL_14), "i" (~PSR_PIL) \
+                    : "memory");                       \
+} while(0)
+
+#define local_irq_enable()                             \
+do {                                                   \
+       unsigned long psr;                              \
+       asm volatile("  movsg   psr,%0          \n"     \
+                    "  andi    %0,%1,%0        \n"     \
+                    "  movgs   %0,psr          \n"     \
+                    : "=r"(psr)                        \
+                    : "i" (~PSR_PIL)                   \
+                    : "memory");                       \
+} while(0)
+
+#define local_save_flags(flags)                        \
+do {                                           \
+       typecheck(unsigned long, flags);        \
+       asm("movsg psr,%0"                      \
+           : "=r"(flags)                       \
+           :                                   \
+           : "memory");                        \
+} while(0)
+
+#define        local_irq_save(flags)                           \
+do {                                                   \
+       unsigned long npsr;                             \
+       typecheck(unsigned long, flags);                \
+       asm volatile("  movsg   psr,%0          \n"     \
+                    "  andi    %0,%3,%1        \n"     \
+                    "  ori     %1,%2,%1        \n"     \
+                    "  movgs   %1,psr          \n"     \
+                    : "=r"(flags), "=r"(npsr)          \
+                    : "i" (PSR_PIL_14), "i" (~PSR_PIL) \
+                    : "memory");                       \
+} while(0)
+
+#define        local_irq_restore(flags)                        \
+do {                                                   \
+       typecheck(unsigned long, flags);                \
+       asm volatile("  movgs   %0,psr          \n"     \
+                    :                                  \
+                    : "r" (flags)                      \
+                    : "memory");                       \
+} while(0)
+
+#define irqs_disabled() \
+       ((__get_PSR() & PSR_PIL) >= PSR_PIL_14)
+
+/*
+ * Force strict CPU ordering.
+ */
+#define nop()                  asm volatile ("nop"::)
+#define mb()                   asm volatile ("membar" : : :"memory")
+#define rmb()                  asm volatile ("membar" : : :"memory")
+#define wmb()                  asm volatile ("membar" : : :"memory")
+#define set_mb(var, value)     do { var = value; mb(); } while (0)
+#define set_wmb(var, value)    do { var = value; wmb(); } while (0)
+
+#define smp_mb()               mb()
+#define smp_rmb()              rmb()
+#define smp_wmb()              wmb()
+
+#define read_barrier_depends()         do {} while(0)
+#define smp_read_barrier_depends()     read_barrier_depends()
+
+#define HARD_RESET_NOW()                       \
+do {                                           \
+       cli();                                  \
+} while(1)
+
+extern void die_if_kernel(const char *, ...) __attribute__((format(printf, 1, 2)));
+extern void free_initmem(void);
+
+#endif /* _ASM_SYSTEM_H */
diff --git a/include/asm-frv/termbits.h b/include/asm-frv/termbits.h
new file mode 100644 (file)
index 0000000..74f20d6
--- /dev/null
@@ -0,0 +1,177 @@
+#ifndef _ASM_TERMBITS_H__
+#define _ASM_TERMBITS_H__
+
+#include <linux/posix_types.h>
+
+typedef unsigned char  cc_t;
+typedef unsigned int   speed_t;
+typedef unsigned int   tcflag_t;
+
+#define NCCS 19
+struct termios {
+       tcflag_t c_iflag;               /* input mode flags */
+       tcflag_t c_oflag;               /* output mode flags */
+       tcflag_t c_cflag;               /* control mode flags */
+       tcflag_t c_lflag;               /* local mode flags */
+       cc_t c_line;                    /* line discipline */
+       cc_t c_cc[NCCS];                /* control characters */
+};
+
+/* c_cc characters */
+#define VINTR 0
+#define VQUIT 1
+#define VERASE 2
+#define VKILL 3
+#define VEOF 4
+#define VTIME 5
+#define VMIN 6
+#define VSWTC 7
+#define VSTART 8
+#define VSTOP 9
+#define VSUSP 10
+#define VEOL 11
+#define VREPRINT 12
+#define VDISCARD 13
+#define VWERASE 14
+#define VLNEXT 15
+#define VEOL2 16
+
+
+/* c_iflag bits */
+#define IGNBRK 0000001
+#define BRKINT 0000002
+#define IGNPAR 0000004
+#define PARMRK 0000010
+#define INPCK  0000020
+#define ISTRIP 0000040
+#define INLCR  0000100
+#define IGNCR  0000200
+#define ICRNL  0000400
+#define IUCLC  0001000
+#define IXON   0002000
+#define IXANY  0004000
+#define IXOFF  0010000
+#define IMAXBEL        0020000
+#define IUTF8  0040000
+
+/* c_oflag bits */
+#define OPOST  0000001
+#define OLCUC  0000002
+#define ONLCR  0000004
+#define OCRNL  0000010
+#define ONOCR  0000020
+#define ONLRET 0000040
+#define OFILL  0000100
+#define OFDEL  0000200
+#define NLDLY  0000400
+#define   NL0  0000000
+#define   NL1  0000400
+#define CRDLY  0003000
+#define   CR0  0000000
+#define   CR1  0001000
+#define   CR2  0002000
+#define   CR3  0003000
+#define TABDLY 0014000
+#define   TAB0 0000000
+#define   TAB1 0004000
+#define   TAB2 0010000
+#define   TAB3 0014000
+#define   XTABS        0014000
+#define BSDLY  0020000
+#define   BS0  0000000
+#define   BS1  0020000
+#define VTDLY  0040000
+#define   VT0  0000000
+#define   VT1  0040000
+#define FFDLY  0100000
+#define   FF0  0000000
+#define   FF1  0100000
+
+/* c_cflag bit meaning */
+#define CBAUD  0010017
+#define  B0    0000000         /* hang up */
+#define  B50   0000001
+#define  B75   0000002
+#define  B110  0000003
+#define  B134  0000004
+#define  B150  0000005
+#define  B200  0000006
+#define  B300  0000007
+#define  B600  0000010
+#define  B1200 0000011
+#define  B1800 0000012
+#define  B2400 0000013
+#define  B4800 0000014
+#define  B9600 0000015
+#define  B19200        0000016
+#define  B38400        0000017
+#define EXTA B19200
+#define EXTB B38400
+#define CSIZE  0000060
+#define   CS5  0000000
+#define   CS6  0000020
+#define   CS7  0000040
+#define   CS8  0000060
+#define CSTOPB 0000100
+#define CREAD  0000200
+#define PARENB 0000400
+#define PARODD 0001000
+#define HUPCL  0002000
+#define CLOCAL 0004000
+#define CBAUDEX 0010000
+#define    B57600 0010001
+#define   B115200 0010002
+#define   B230400 0010003
+#define   B460800 0010004
+#define   B500000 0010005
+#define   B576000 0010006
+#define   B921600 0010007
+#define  B1000000 0010010
+#define  B1152000 0010011
+#define  B1500000 0010012
+#define  B2000000 0010013
+#define  B2500000 0010014
+#define  B3000000 0010015
+#define  B3500000 0010016
+#define  B4000000 0010017
+#define CIBAUD   002003600000  /* input baud rate (not used) */
+#define CTVB     004000000000          /* VisioBraille Terminal flow control */
+#define CMSPAR   010000000000          /* mark or space (stick) parity */
+#define CRTSCTS          020000000000          /* flow control */
+
+/* c_lflag bits */
+#define ISIG   0000001
+#define ICANON 0000002
+#define XCASE  0000004
+#define ECHO   0000010
+#define ECHOE  0000020
+#define ECHOK  0000040
+#define ECHONL 0000100
+#define NOFLSH 0000200
+#define TOSTOP 0000400
+#define ECHOCTL        0001000
+#define ECHOPRT        0002000
+#define ECHOKE 0004000
+#define FLUSHO 0010000
+#define PENDIN 0040000
+#define IEXTEN 0100000
+
+
+/* tcflow() and TCXONC use these */
+#define        TCOOFF          0
+#define        TCOON           1
+#define        TCIOFF          2
+#define        TCION           3
+
+/* tcflush() and TCFLSH use these */
+#define        TCIFLUSH        0
+#define        TCOFLUSH        1
+#define        TCIOFLUSH       2
+
+/* tcsetattr uses these */
+#define        TCSANOW         0
+#define        TCSADRAIN       1
+#define        TCSAFLUSH       2
+
+#endif /* _ASM_TERMBITS_H__ */
+
diff --git a/include/asm-frv/termios.h b/include/asm-frv/termios.h
new file mode 100644 (file)
index 0000000..b4a664e
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef _ASM_TERMIOS_H
+#define _ASM_TERMIOS_H
+
+#include <asm/termbits.h>
+#include <asm/ioctls.h>
+
+struct winsize {
+       unsigned short ws_row;
+       unsigned short ws_col;
+       unsigned short ws_xpixel;
+       unsigned short ws_ypixel;
+};
+
+#define NCC 8
+struct termio {
+       unsigned short c_iflag;         /* input mode flags */
+       unsigned short c_oflag;         /* output mode flags */
+       unsigned short c_cflag;         /* control mode flags */
+       unsigned short c_lflag;         /* local mode flags */
+       unsigned char c_line;           /* line discipline */
+       unsigned char c_cc[NCC];        /* control characters */
+};
+
+#ifdef __KERNEL__
+/*     intr=^C         quit=^|         erase=del       kill=^U
+       eof=^D          vtime=\0        vmin=\1         sxtc=\0
+       start=^Q        stop=^S         susp=^Z         eol=\0
+       reprint=^R      discard=^U      werase=^W       lnext=^V
+       eol2=\0
+*/
+#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
+#endif
+
+/* modem lines */
+#define TIOCM_LE       0x001
+#define TIOCM_DTR      0x002
+#define TIOCM_RTS      0x004
+#define TIOCM_ST       0x008
+#define TIOCM_SR       0x010
+#define TIOCM_CTS      0x020
+#define TIOCM_CAR      0x040
+#define TIOCM_RNG      0x080
+#define TIOCM_DSR      0x100
+#define TIOCM_CD       TIOCM_CAR
+#define TIOCM_RI       TIOCM_RNG
+#define TIOCM_OUT1     0x2000
+#define TIOCM_OUT2     0x4000
+#define TIOCM_LOOP     0x8000
+
+#define TIOCM_MODEM_BITS       TIOCM_OUT2      /* IRDA support */
+
+/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+
+/* line disciplines */
+#define N_TTY          0
+#define N_SLIP         1
+#define N_MOUSE                2
+#define N_PPP          3
+#define N_STRIP                4
+#define N_AX25         5
+#define N_X25          6       /* X.25 async */
+#define N_6PACK                7
+#define N_MASC         8       /* Reserved for Mobitex module <kaz@cafe.net> */
+#define N_R3964                9       /* Reserved for Simatic R3964 module */
+#define N_PROFIBUS_FDL 10      /* Reserved for Profibus <Dave@mvhi.com> */
+#define N_IRDA         11      /* Linux IrDa - http://irda.sourceforge.net/ */
+#define N_SMSBLOCK     12      /* SMS block mode - for talking to GSM data cards about SMS messages */
+#define N_HDLC         13      /* synchronous HDLC */
+#define N_SYNC_PPP     14
+#define N_HCI          15  /* Bluetooth HCI UART */
+
+#include <asm-generic/termios.h>
+
+#endif /* _ASM_TERMIOS_H */
diff --git a/include/asm-frv/thread_info.h b/include/asm-frv/thread_info.h
new file mode 100644 (file)
index 0000000..b80a97f
--- /dev/null
@@ -0,0 +1,159 @@
+/* thread_info.h: description
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * Derived from include/asm-i386/thread_info.h
+ *
+ * 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 _ASM_THREAD_INFO_H
+#define _ASM_THREAD_INFO_H
+
+#ifdef __KERNEL__
+
+#ifndef __ASSEMBLY__
+#include <asm/processor.h>
+#endif
+
+/*
+ * low level task data that entry.S needs immediate access to
+ * - this struct should fit entirely inside of one cache line
+ * - this struct shares the supervisor stack pages
+ * - if the contents of this structure are changed, the assembly constants must also be changed
+ */
+#ifndef __ASSEMBLY__
+
+struct thread_info {
+       struct task_struct      *task;          /* main task structure */
+       struct exec_domain      *exec_domain;   /* execution domain */
+       unsigned long           flags;          /* low level flags */
+       unsigned long           status;         /* thread-synchronous flags */
+       __u32                   cpu;            /* current CPU */
+       __s32                   preempt_count;  /* 0 => preemptable, <0 => BUG */
+
+       mm_segment_t            addr_limit;     /* thread address space:
+                                                  0-0xBFFFFFFF for user-thead
+                                                  0-0xFFFFFFFF for kernel-thread
+                                               */
+       struct restart_block    restart_block;
+
+       __u8                    supervisor_stack[0];
+};
+
+#else /* !__ASSEMBLY__ */
+
+/* offsets into the thread_info struct for assembly code access */
+#define TI_TASK                        0x00000000
+#define TI_EXEC_DOMAIN         0x00000004
+#define TI_FLAGS               0x00000008
+#define TI_STATUS              0x0000000C
+#define TI_CPU                 0x00000010
+#define TI_PRE_COUNT           0x00000014
+#define TI_ADDR_LIMIT          0x00000018
+#define TI_RESTART_BLOCK       0x0000001C
+
+#endif
+
+#define PREEMPT_ACTIVE         0x4000000
+
+/*
+ * macros/functions for gaining access to the thread information structure
+ *
+ * preempt_count needs to be 1 initially, until the scheduler is functional.
+ */
+#ifndef __ASSEMBLY__
+
+#define INIT_THREAD_INFO(tsk)                  \
+{                                              \
+       .task           = &tsk,                 \
+       .exec_domain    = &default_exec_domain, \
+       .flags          = 0,                    \
+       .cpu            = 0,                    \
+       .preempt_count  = 1,                    \
+       .addr_limit     = KERNEL_DS,            \
+       .restart_block = {                      \
+               .fn = do_no_restart_syscall,    \
+       },                                      \
+}
+
+#define init_thread_info       (init_thread_union.thread_info)
+#define init_stack             (init_thread_union.stack)
+
+#ifdef CONFIG_SMALL_TASKS
+#define THREAD_SIZE            4096
+#else
+#define THREAD_SIZE            8192
+#endif
+
+/* how to get the thread information struct from C */
+register struct thread_info *__current_thread_info asm("gr15");
+
+#define current_thread_info() ({ __current_thread_info; })
+
+/* thread information allocation */
+#ifdef CONFIG_DEBUG_STACK_USAGE
+#define alloc_thread_info(tsk)                                 \
+       ({                                                      \
+               struct thread_info *ret;                        \
+                                                               \
+               ret = kmalloc(THREAD_SIZE, GFP_KERNEL);         \
+               if (ret)                                        \
+                       memset(ret, 0, THREAD_SIZE);            \
+               ret;                                            \
+       })
+#else
+#define alloc_thread_info(tsk) kmalloc(THREAD_SIZE, GFP_KERNEL)
+#endif
+
+#define free_thread_info(info) kfree(info)
+#define get_thread_info(ti)    get_task_struct((ti)->task)
+#define put_thread_info(ti)    put_task_struct((ti)->task)
+
+#else /* !__ASSEMBLY__ */
+
+#define THREAD_SIZE    8192
+
+#endif
+
+/*
+ * thread information flags
+ * - these are process state flags that various assembly files may need to access
+ * - pending work-to-be-done flags are in LSW
+ * - other flags in MSW
+ */
+#define TIF_SYSCALL_TRACE      0       /* syscall trace active */
+#define TIF_NOTIFY_RESUME      1       /* resumption notification requested */
+#define TIF_SIGPENDING         2       /* signal pending */
+#define TIF_NEED_RESCHED       3       /* rescheduling necessary */
+#define TIF_SINGLESTEP         4       /* restore singlestep on return to user mode */
+#define TIF_IRET               5       /* return with iret */
+#define TIF_POLLING_NRFLAG     16      /* true if poll_idle() is polling TIF_NEED_RESCHED */
+#define TIF_MEMDIE             17      /* OOM killer killed process */
+
+#define _TIF_SYSCALL_TRACE     (1 << TIF_SYSCALL_TRACE)
+#define _TIF_NOTIFY_RESUME     (1 << TIF_NOTIFY_RESUME)
+#define _TIF_SIGPENDING                (1 << TIF_SIGPENDING)
+#define _TIF_NEED_RESCHED      (1 << TIF_NEED_RESCHED)
+#define _TIF_SINGLESTEP                (1 << TIF_SINGLESTEP)
+#define _TIF_IRET              (1 << TIF_IRET)
+#define _TIF_POLLING_NRFLAG    (1 << TIF_POLLING_NRFLAG)
+
+#define _TIF_WORK_MASK         0x0000FFFE      /* work to do on interrupt/exception return */
+#define _TIF_ALLWORK_MASK      0x0000FFFF      /* work to do on any return to u-space */
+
+/*
+ * Thread-synchronous status.
+ *
+ * This is different from the flags in that nobody else
+ * ever touches our thread-synchronous status, so we don't
+ * have to worry about atomic accesses.
+ */
+#define TS_USEDFPM             0x0001  /* FPU/Media was used by this task this quantum (SMP) */
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_THREAD_INFO_H */
diff --git a/include/asm-frv/timer-regs.h b/include/asm-frv/timer-regs.h
new file mode 100644 (file)
index 0000000..6c5a871
--- /dev/null
@@ -0,0 +1,106 @@
+/* timer-regs.h: hardware timer register definitions
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_TIMER_REGS_H
+#define _ASM_TIMER_REGS_H
+
+#include <asm/sections.h>
+
+extern unsigned long __nongprelbss __clkin_clock_speed_HZ;
+extern unsigned long __nongprelbss __ext_bus_clock_speed_HZ;
+extern unsigned long __nongprelbss __res_bus_clock_speed_HZ;
+extern unsigned long __nongprelbss __sdram_clock_speed_HZ;
+extern unsigned long __nongprelbss __core_bus_clock_speed_HZ;
+extern unsigned long __nongprelbss __core_clock_speed_HZ;
+extern unsigned long __nongprelbss __dsu_clock_speed_HZ;
+extern unsigned long __nongprelbss __serial_clock_speed_HZ;
+
+#define __get_CLKC()   ({ *(volatile unsigned long *)(0xfeff9a00); })
+
+static inline void __set_CLKC(unsigned long v)
+{
+       int tmp;
+
+       asm volatile("  st%I0.p %2,%M0          \n"
+                    "  setlos  %3,%1           \n"
+                    "  membar                  \n"
+                    "0:                        \n"
+                    "  subicc  %1,#1,%1,icc0   \n"
+                    "  bnc     icc0,#1,0b      \n"
+                    : "=m"(*(volatile unsigned long *) 0xfeff9a00), "=r"(tmp)
+                    : "r"(v), "i"(256)
+                    : "icc0");
+}
+
+#define __get_TCTR()   ({ *(volatile unsigned long *)(0xfeff9418); })
+#define __get_TPRV()   ({ *(volatile unsigned long *)(0xfeff9420); })
+#define __get_TPRCKSL()        ({ *(volatile unsigned long *)(0xfeff9428); })
+#define __get_TCSR(T)  ({ *(volatile unsigned long *)(0xfeff9400 + 8 * (T)); })
+#define __get_TxCKSL(T)        ({ *(volatile unsigned long *)(0xfeff9430 + 8 * (T)); })
+
+#define __get_TCSR_DATA(T) ({ __get_TCSR(T) >> 24; })
+
+#define __set_TCTR(V)  do { *(volatile unsigned long *)(0xfeff9418) = (V); mb(); } while(0)
+#define __set_TPRV(V)  do { *(volatile unsigned long *)(0xfeff9420) = (V) << 24; mb(); } while(0)
+#define __set_TPRCKSL(V) do { *(volatile unsigned long *)(0xfeff9428) = (V); mb(); } while(0)
+#define __set_TCSR(T,V)        \
+do { *(volatile unsigned long *)(0xfeff9400 + 8 * (T)) = (V); mb(); } while(0)
+
+#define __set_TxCKSL(T,V) \
+do { *(volatile unsigned long *)(0xfeff9430 + 8 * (T)) = (V); mb(); } while(0)
+
+#define __set_TCSR_DATA(T,V) __set_TCSR(T, (V) << 24)
+#define __set_TxCKSL_DATA(T,V) __set_TxCKSL(T, TxCKSL_EIGHT | __TxCKSL_SELECT((V)))
+
+/* clock control register */
+#define CLKC_CMODE             0x0f000000
+#define CLKC_SLPL              0x000f0000
+#define CLKC_P0                        0x00000100
+#define CLKC_CM                        0x00000003
+
+#define CLKC_CMODE_s           24
+
+/* timer control register - non-readback mode */
+#define TCTR_MODE_0            0x00000000
+#define TCTR_MODE_2            0x04000000
+#define TCTR_MODE_4            0x08000000
+#define TCTR_MODE_5            0x0a000000
+#define TCTR_RL_LATCH          0x00000000
+#define TCTR_RL_RW_LOW8                0x10000000
+#define TCTR_RL_RW_HIGH8       0x20000000
+#define TCTR_RL_RW_LH8         0x30000000
+#define TCTR_SC_CTR0           0x00000000
+#define TCTR_SC_CTR1           0x40000000
+#define TCTR_SC_CTR2           0x80000000
+
+/* timer control register - readback mode */
+#define TCTR_CNT0              0x02000000
+#define TCTR_CNT1              0x04000000
+#define TCTR_CNT2              0x08000000
+#define TCTR_NSTATUS           0x10000000
+#define TCTR_NCOUNT            0x20000000
+#define TCTR_SC_READBACK       0xc0000000
+
+/* timer control status registers - non-readback mode */
+#define TCSRx_DATA             0xff000000
+
+/* timer control status registers - readback mode */
+#define TCSRx_OUTPUT           0x80000000
+#define TCSRx_NULLCOUNT                0x40000000
+#define TCSRx_RL               0x30000000
+#define TCSRx_MODE             0x07000000
+
+/* timer clock select registers */
+#define TxCKSL_SELECT          0x0f000000
+#define __TxCKSL_SELECT(X)     ((X) << 24)
+#define TxCKSL_EIGHT           0xf0000000
+
+#endif /* _ASM_TIMER_REGS_H */
diff --git a/include/asm-frv/timex.h b/include/asm-frv/timex.h
new file mode 100644 (file)
index 0000000..2aa562f
--- /dev/null
@@ -0,0 +1,25 @@
+/* timex.h: FR-V architecture timex specifications
+ */
+#ifndef _ASM_TIMEX_H
+#define _ASM_TIMEX_H
+
+#define CLOCK_TICK_RATE                1193180 /* Underlying HZ */
+#define CLOCK_TICK_FACTOR      20      /* Factor of both 1000000 and CLOCK_TICK_RATE */
+
+#define FINETUNE                                                       \
+((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) *                        \
+   (1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR))  \
+  << (SHIFT_SCALE-SHIFT_HZ)) / HZ)
+
+typedef unsigned long cycles_t;
+
+static inline cycles_t get_cycles(void)
+{
+       return 0;
+}
+
+#define vxtime_lock()          do {} while (0)
+#define vxtime_unlock()                do {} while (0)
+
+#endif
+
diff --git a/include/asm-frv/tlb.h b/include/asm-frv/tlb.h
new file mode 100644 (file)
index 0000000..f94fe5c
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _ASM_TLB_H
+#define _ASM_TLB_H
+
+#include <asm/tlbflush.h>
+
+#define check_pgt_cache() do {} while(0)
+
+/*
+ * we don't need any special per-pte or per-vma handling...
+ */
+#define tlb_start_vma(tlb, vma)                                do { } while (0)
+#define tlb_end_vma(tlb, vma)                          do { } while (0)
+#define __tlb_remove_tlb_entry(tlb, ptep, address)     do { } while (0)
+
+/*
+ * .. because we flush the whole mm when it fills up
+ */
+#define tlb_flush(tlb)         flush_tlb_mm((tlb)->mm)
+
+#include <asm-generic/tlb.h>
+
+#endif /* _ASM_TLB_H */
+
diff --git a/include/asm-frv/tlbflush.h b/include/asm-frv/tlbflush.h
new file mode 100644 (file)
index 0000000..c66aeb1
--- /dev/null
@@ -0,0 +1,76 @@
+/* tlbflush.h: TLB flushing functions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_TLBFLUSH_H
+#define _ASM_TLBFLUSH_H
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <asm/processor.h>
+
+#ifdef CONFIG_MMU
+
+#ifndef __ASSEMBLY__
+extern void asmlinkage __flush_tlb_all(void);
+extern void asmlinkage __flush_tlb_mm(unsigned long contextid);
+extern void asmlinkage __flush_tlb_page(unsigned long contextid, unsigned long start);
+extern void asmlinkage __flush_tlb_range(unsigned long contextid,
+                                        unsigned long start, unsigned long end);
+#endif /* !__ASSEMBLY__ */
+
+#define flush_tlb_all()                                \
+do {                                           \
+       preempt_disable();                      \
+       __flush_tlb_all();                      \
+       preempt_enable();                       \
+} while(0)
+
+#define flush_tlb_mm(mm)                       \
+do {                                           \
+       preempt_disable();                      \
+       __flush_tlb_mm((mm)->context.id);       \
+       preempt_enable();                       \
+} while(0)
+
+#define flush_tlb_range(vma,start,end)                                 \
+do {                                                                   \
+       preempt_disable();                                              \
+       __flush_tlb_range((vma)->vm_mm->context.id, start, end);        \
+       preempt_enable();                                               \
+} while(0)
+
+#define flush_tlb_page(vma,addr)                               \
+do {                                                           \
+       preempt_disable();                                      \
+       __flush_tlb_page((vma)->vm_mm->context.id, addr);       \
+       preempt_enable();                                       \
+} while(0)
+
+
+#define __flush_tlb_global()                   flush_tlb_all()
+#define flush_tlb()                            flush_tlb_all()
+#define flush_tlb_kernel_range(start, end)     flush_tlb_all()
+#define flush_tlb_pgtables(mm,start,end)       asm volatile("movgs gr0,scr0 ! movgs gr0,scr1");
+
+#else
+
+#define flush_tlb()                            BUG()
+#define flush_tlb_all()                                BUG()
+#define flush_tlb_mm(mm)                       BUG()
+#define flush_tlb_page(vma,addr)               BUG()
+#define flush_tlb_range(mm,start,end)          BUG()
+#define flush_tlb_pgtables(mm,start,end)       BUG()
+#define flush_tlb_kernel_range(start, end)     BUG()
+
+#endif
+
+
+#endif /* _ASM_TLBFLUSH_H */
diff --git a/include/asm-frv/topology.h b/include/asm-frv/topology.h
new file mode 100644 (file)
index 0000000..abe7298
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _ASM_TOPOLOGY_H
+#define _ASM_TOPOLOGY_H
+
+#ifdef CONFIG_NUMA
+
+#error NUMA not supported yet
+
+#else /* !CONFIG_NUMA */
+
+#include <asm-generic/topology.h>
+
+#endif /* CONFIG_NUMA */
+
+#endif /* _ASM_TOPOLOGY_H */
diff --git a/include/asm-frv/types.h b/include/asm-frv/types.h
new file mode 100644 (file)
index 0000000..1a5b654
--- /dev/null
@@ -0,0 +1,74 @@
+/* types.h: FRV types
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_TYPES_H
+#define _ASM_TYPES_H
+
+#ifndef __ASSEMBLY__
+
+typedef unsigned short umode_t;
+
+/*
+ * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the
+ * header files exported to user space
+ */
+
+typedef __signed__ char __s8;
+typedef unsigned char __u8;
+
+typedef __signed__ short __s16;
+typedef unsigned short __u16;
+
+typedef __signed__ int __s32;
+typedef unsigned int __u32;
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+typedef __signed__ long long __s64;
+typedef unsigned long long __u64;
+#endif
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * These aren't exported outside the kernel to avoid name space clashes
+ */
+#ifdef __KERNEL__
+
+#define BITS_PER_LONG 32
+
+#ifndef __ASSEMBLY__
+
+#include <linux/config.h>
+
+typedef signed char s8;
+typedef unsigned char u8;
+
+typedef signed short s16;
+typedef unsigned short u16;
+
+typedef signed int s32;
+typedef unsigned int u32;
+
+typedef signed long long s64;
+typedef unsigned long long u64;
+typedef u64 u_quad_t;
+
+/* Dma addresses are 32-bits wide.  */
+
+typedef u32 dma_addr_t;
+
+typedef unsigned short kmem_bufctl_t;
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_TYPES_H */
diff --git a/include/asm-frv/uaccess.h b/include/asm-frv/uaccess.h
new file mode 100644 (file)
index 0000000..a5bd9bd
--- /dev/null
@@ -0,0 +1,317 @@
+/* uaccess.h: userspace accessor functions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_UACCESS_H
+#define _ASM_UACCESS_H
+
+/*
+ * User space memory access functions
+ */
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+#include <asm/sections.h>
+
+#define HAVE_ARCH_UNMAPPED_AREA        /* we decide where to put mmaps */
+
+#define __ptr(x) ((unsigned long *)(x))
+
+#define VERIFY_READ    0
+#define VERIFY_WRITE   1
+
+#define __addr_ok(addr) ((unsigned long)(addr) < get_addr_limit())
+
+/*
+ * check that a range of addresses falls within the current address limit
+ */
+static inline int ___range_ok(unsigned long addr, unsigned long size)
+{
+#ifdef CONFIG_MMU
+       int flag = -EFAULT, tmp;
+
+       asm volatile (
+               "       addcc   %3,%2,%1,icc0   \n"     /* set C-flag if addr+size>4GB */
+               "       subcc.p %1,%4,gr0,icc1  \n"     /* jump if addr+size>limit */
+               "       bc      icc0,#0,0f      \n"
+               "       bhi     icc1,#0,0f      \n"
+               "       setlos  #0,%0           \n"     /* mark okay */
+               "0:                             \n"
+               : "=r"(flag), "=&r"(tmp)
+               : "r"(addr), "r"(size), "r"(get_addr_limit()), "0"(flag)
+               );
+
+       return flag;
+
+#else
+
+       if (addr < memory_start ||
+           addr > memory_end ||
+           size > memory_end - memory_start ||
+           addr + size > memory_end)
+               return -EFAULT;
+
+       return 0;
+#endif
+}
+
+#define __range_ok(addr,size) ___range_ok((unsigned long) (addr), (unsigned long) (size))
+
+#define access_ok(type,addr,size) (__range_ok((addr), (size)) == 0)
+#define __access_ok(addr,size) (__range_ok((addr), (size)) == 0)
+
+static inline int verify_area(int type, const void * addr, unsigned long size)
+{
+       return __range_ok(addr, size);
+}
+
+/*
+ * The exception table consists of pairs of addresses: the first is the
+ * address of an instruction that is allowed to fault, and the second is
+ * the address at which the program should continue.  No registers are
+ * modified, so it is entirely up to the continuation code to figure out
+ * what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path.  This means when everything is well,
+ * we don't even have to jump over them.  Further, they do not intrude
+ * on our cache or tlb entries.
+ */
+struct exception_table_entry
+{
+       unsigned long insn, fixup;
+};
+
+/* Returns 0 if exception not found and fixup otherwise.  */
+extern unsigned long search_exception_table(unsigned long);
+
+
+/*
+ * These are the main single-value transfer routines.  They automatically
+ * use the right size if we just have the right pointer type.
+ */
+#define __put_user(x, ptr)                                             \
+({                                                                     \
+       int __pu_err = 0;                                               \
+                                                                       \
+       typeof(*(ptr)) __pu_val = (x);                                  \
+                                                                       \
+       switch (sizeof (*(ptr))) {                                      \
+       case 1:                                                         \
+               __put_user_asm(__pu_err, __pu_val, ptr, "b", "r");      \
+               break;                                                  \
+       case 2:                                                         \
+               __put_user_asm(__pu_err, __pu_val, ptr, "h", "r");      \
+               break;                                                  \
+       case 4:                                                         \
+               __put_user_asm(__pu_err, __pu_val, ptr, "",  "r");      \
+               break;                                                  \
+       case 8:                                                         \
+               __put_user_asm(__pu_err, __pu_val, ptr, "d", "e");      \
+               break;                                                  \
+       default:                                                        \
+               __pu_err = __put_user_bad();                            \
+               break;                                                  \
+       }                                                               \
+       __pu_err;                                                       \
+})
+
+#define put_user(x, ptr)                       \
+({                                             \
+       typeof(&*ptr) _p = (ptr);               \
+       int _e;                                 \
+                                               \
+       _e = __range_ok(_p, sizeof(*_p));       \
+       if (_e == 0)                            \
+               _e = __put_user((x), _p);       \
+       _e;                                     \
+})
+
+extern int __put_user_bad(void);
+
+/*
+ * Tell gcc we read from memory instead of writing: this is because
+ * we do not write to any memory gcc knows about, so there are no
+ * aliasing issues.
+ */
+
+#ifdef CONFIG_MMU
+
+#define __put_user_asm(err,x,ptr,dsize,constraint)                                     \
+do {                                                                                   \
+       asm volatile("1:        st"dsize"%I1    %2,%M1  \n"                             \
+                    "2:                                \n"                             \
+                    ".subsection 2                     \n"                             \
+                    "3:        setlos          %3,%0   \n"                             \
+                    "          bra             2b      \n"                             \
+                    ".previous                         \n"                             \
+                    ".section __ex_table,\"a\"         \n"                             \
+                    "          .balign         8       \n"                             \
+                    "          .long           1b,3b   \n"                             \
+                    ".previous"                                                        \
+                    : "=r" (err)                                                       \
+                    : "m" (*__ptr(ptr)), constraint (x), "i"(-EFAULT), "0"(err)        \
+                    : "memory");                                                       \
+} while (0)
+
+#else
+
+#define __put_user_asm(err,x,ptr,bwl,con)      \
+do {                                           \
+       asm("   st"bwl"%I0      %1,%M0  \n"     \
+           "   membar                  \n"     \
+           :                                   \
+           : "m" (*__ptr(ptr)), con (x)        \
+           : "memory");                        \
+} while (0)
+
+#endif
+
+/*****************************************************************************/
+/*
+ *
+ */
+#define __get_user(x, ptr)                                             \
+({                                                                     \
+       typeof(*(ptr)) __gu_val = 0;                                    \
+       int __gu_err = 0;                                               \
+                                                                       \
+       switch (sizeof(*(ptr))) {                                       \
+       case 1:                                                         \
+               __get_user_asm(__gu_err, __gu_val, ptr, "ub", "=r");    \
+               break;                                                  \
+       case 2:                                                         \
+               __get_user_asm(__gu_err, __gu_val, ptr, "uh", "=r");    \
+               break;                                                  \
+       case 4:                                                         \
+               __get_user_asm(__gu_err, __gu_val, ptr, "", "=r");      \
+               break;                                                  \
+       case 8:                                                         \
+               __get_user_asm(__gu_err, __gu_val, ptr, "d", "=e");     \
+               break;                                                  \
+       default:                                                        \
+               __gu_err = __get_user_bad();                            \
+               break;                                                  \
+       }                                                               \
+       (x) = __gu_val;                                                 \
+       __gu_err;                                                       \
+})
+
+#define get_user(x, ptr)                       \
+({                                             \
+       typeof(&*ptr) _p = (ptr);               \
+       int _e;                                 \
+                                               \
+       _e = __range_ok(_p, sizeof(*_p));       \
+       if (likely(_e == 0))                    \
+               _e = __get_user((x), _p);       \
+       else                                    \
+               (x) = (typeof(x)) 0;            \
+       _e;                                     \
+})
+
+extern int __get_user_bad(void);
+
+#ifdef CONFIG_MMU
+
+#define __get_user_asm(err,x,ptr,dtype,constraint)     \
+do {                                                   \
+       asm("1:         ld"dtype"%I2    %M2,%1  \n"     \
+           "2:                                 \n"     \
+           ".subsection 2                      \n"     \
+           "3:         setlos          %3,%0   \n"     \
+           "           setlos          #0,%1   \n"     \
+           "           bra             2b      \n"     \
+           ".previous                          \n"     \
+           ".section __ex_table,\"a\"          \n"     \
+           "           .balign         8       \n"     \
+           "           .long           1b,3b   \n"     \
+           ".previous"                                 \
+           : "=r" (err), constraint (x)                \
+           : "m" (*__ptr(ptr)), "i"(-EFAULT), "0"(err) \
+           );                                          \
+} while(0)
+
+#else
+
+#define __get_user_asm(err,x,ptr,bwl,con)      \
+       asm("   ld"bwl"%I1      %M1,%0  \n"     \
+           "   membar                  \n"     \
+           : con(x)                            \
+           : "m" (*__ptr(ptr)))
+
+#endif
+
+/*****************************************************************************/
+/*
+ *
+ */
+#ifdef CONFIG_MMU
+extern long __memset_user(void *dst, unsigned long count);
+extern long __memcpy_user(void *dst, const void *src, unsigned long count);
+
+#define clear_user(dst,count)                  __memset_user((dst), (count))
+#define __copy_from_user_inatomic(to, from, n) __memcpy_user((to), (from), (n))
+#define __copy_to_user_inatomic(to, from, n)   __memcpy_user((to), (from), (n))
+
+#else
+
+#define clear_user(dst,count)                  (memset((dst), 0, (count)), 0)
+#define __copy_from_user_inatomic(to, from, n) (memcpy((to), (from), (n)), 0)
+#define __copy_to_user_inatomic(to, from, n)   (memcpy((to), (from), (n)), 0)
+
+#endif
+
+static inline unsigned long __must_check
+__copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+       might_sleep();
+       return __copy_to_user_inatomic(to, from, n);
+}
+
+static inline unsigned long
+__copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+       might_sleep();
+       return __copy_from_user_inatomic(to, from, n);
+}
+
+static inline long copy_from_user(void *to, const void *from, unsigned long n)
+{
+       unsigned long ret = n;
+
+       if (likely(__access_ok(from, n)))
+               ret = __copy_from_user(to, from, n);
+
+       if (unlikely(ret != 0))
+               memset(to + (n - ret), 0, ret);
+
+       return ret;
+}
+
+static inline long copy_to_user(void *to, const void *from, unsigned long n)
+{
+       return likely(__access_ok(to, n)) ? __copy_to_user(to, from, n) : n;
+}
+
+#define copy_to_user_ret(to,from,n,retval)     ({ if (copy_to_user(to,from,n)) return retval; })
+#define copy_from_user_ret(to,from,n,retval)   ({ if (copy_from_user(to,from,n)) return retval; })
+
+extern long strncpy_from_user(char *dst, const char *src, long count);
+extern long strnlen_user(const char *src, long count);
+
+#define strlen_user(str) strnlen_user(str, 32767)
+
+extern unsigned long search_exception_table(unsigned long addr);
+
+#define copy_to_user_page(vma, page, vaddr, dst, src, len)     memcpy(dst, src, len)
+#define copy_from_user_page(vma, page, vaddr, dst, src, len)   memcpy(dst, src, len)
+
+#endif /* _ASM_UACCESS_H */
diff --git a/include/asm-frv/ucontext.h b/include/asm-frv/ucontext.h
new file mode 100644 (file)
index 0000000..8d8c0c9
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _ASM_UCONTEXT_H
+#define _ASM_UCONTEXT_H
+
+struct ucontext {
+       unsigned long           uc_flags;
+       struct ucontext         *uc_link;
+       stack_t                 uc_stack;
+       struct sigcontext       uc_mcontext;
+       sigset_t                uc_sigmask;     /* mask last for extensibility */
+};
+
+#endif
diff --git a/include/asm-frv/unaligned.h b/include/asm-frv/unaligned.h
new file mode 100644 (file)
index 0000000..a0d199b
--- /dev/null
@@ -0,0 +1,203 @@
+/* unaligned.h: unaligned access handler
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ASM_UNALIGNED_H
+#define _ASM_UNALIGNED_H
+
+#include <linux/config.h>
+
+/*
+ * Unaligned accesses on uClinux can't be performed in a fault handler - the
+ * CPU detects them as imprecise exceptions making this impossible.
+ *
+ * With the FR451, however, they are precise, and so we used to fix them up in
+ * the memory access fault handler.  However, instruction bundling make this
+ * impractical.  So, now we fall back to using memcpy.
+ */
+#ifdef CONFIG_MMU
+
+/*
+ * The asm statement in the macros below is a way to get GCC to copy a
+ * value from one variable to another without having any clue it's
+ * actually doing so, so that it won't have any idea that the values
+ * in the two variables are related.
+ */
+
+#define get_unaligned(ptr) ({                          \
+       typeof((*(ptr))) __x;                           \
+       void *__ptrcopy;                                \
+       asm("" : "=r" (__ptrcopy) : "0" (ptr));         \
+       memcpy(&__x, __ptrcopy, sizeof(*(ptr)));        \
+       __x;                                            \
+})
+
+#define put_unaligned(val, ptr) ({                     \
+       typeof((*(ptr))) __x = (val);                   \
+       void *__ptrcopy;                                \
+       asm("" : "=r" (__ptrcopy) : "0" (ptr));         \
+       memcpy(__ptrcopy, &__x, sizeof(*(ptr)));        \
+})
+
+extern int handle_misalignment(unsigned long esr0, unsigned long ear0, unsigned long epcr0);
+
+#else
+
+#define get_unaligned(ptr)                                                     \
+({                                                                             \
+       typeof(*(ptr)) x;                                                       \
+       const char *__p = (const char *) (ptr);                                 \
+                                                                               \
+       switch (sizeof(x)) {                                                    \
+       case 1:                                                                 \
+               x = *(ptr);                                                     \
+               break;                                                          \
+       case 2:                                                                 \
+       {                                                                       \
+               uint8_t a;                                                      \
+               asm("   ldub%I2         %M2,%0          \n"                     \
+                   "   ldub%I3.p       %M3,%1          \n"                     \
+                   "   slli            %0,#8,%0        \n"                     \
+                   "   or              %0,%1,%0        \n"                     \
+                   : "=&r"(x), "=&r"(a)                                        \
+                   : "m"(__p[0]),  "m"(__p[1])                                 \
+                   );                                                          \
+               break;                                                          \
+       }                                                                       \
+                                                                               \
+       case 4:                                                                 \
+       {                                                                       \
+               uint8_t a;                                                      \
+               asm("   ldub%I2         %M2,%0          \n"                     \
+                   "   ldub%I3.p       %M3,%1          \n"                     \
+                   "   slli            %0,#8,%0        \n"                     \
+                   "   or              %0,%1,%0        \n"                     \
+                   "   ldub%I4.p       %M4,%1          \n"                     \
+                   "   slli            %0,#8,%0        \n"                     \
+                   "   or              %0,%1,%0        \n"                     \
+                   "   ldub%I5.p       %M5,%1          \n"                     \
+                   "   slli            %0,#8,%0        \n"                     \
+                   "   or              %0,%1,%0        \n"                     \
+                   : "=&r"(x), "=&r"(a)                                        \
+                   : "m"(__p[0]),  "m"(__p[1]), "m"(__p[2]), "m"(__p[3])       \
+                   );                                                          \
+               break;                                                          \
+       }                                                                       \
+                                                                               \
+       case 8:                                                                 \
+       {                                                                       \
+               union { uint64_t x; u32 y[2]; } z;                              \
+               uint8_t a;                                                      \
+               asm("   ldub%I3         %M3,%0          \n"                     \
+                   "   ldub%I4.p       %M4,%2          \n"                     \
+                   "   slli            %0,#8,%0        \n"                     \
+                   "   or              %0,%2,%0        \n"                     \
+                   "   ldub%I5.p       %M5,%2          \n"                     \
+                   "   slli            %0,#8,%0        \n"                     \
+                   "   or              %0,%2,%0        \n"                     \
+                   "   ldub%I6.p       %M6,%2          \n"                     \
+                   "   slli            %0,#8,%0        \n"                     \
+                   "   or              %0,%2,%0        \n"                     \
+                   "   ldub%I7         %M7,%1          \n"                     \
+                   "   ldub%I8.p       %M8,%2          \n"                     \
+                   "   slli            %1,#8,%1        \n"                     \
+                   "   or              %1,%2,%1        \n"                     \
+                   "   ldub%I9.p       %M9,%2          \n"                     \
+                   "   slli            %1,#8,%1        \n"                     \
+                   "   or              %1,%2,%1        \n"                     \
+                   "   ldub%I10.p      %M10,%2         \n"                     \
+                   "   slli            %1,#8,%1        \n"                     \
+                   "   or              %1,%2,%1        \n"                     \
+                   : "=&r"(z.y[0]), "=&r"(z.y[1]), "=&r"(a)                    \
+                   : "m"(__p[0]), "m"(__p[1]), "m"(__p[2]), "m"(__p[3]),       \
+                     "m"(__p[4]), "m"(__p[5]), "m"(__p[6]), "m"(__p[7])        \
+                   );                                                          \
+               x = z.x;                                                        \
+               break;                                                          \
+       }                                                                       \
+                                                                               \
+       default:                                                                \
+               x = 0;                                                          \
+               BUG();                                                          \
+               break;                                                          \
+       }                                                                       \
+                                                                               \
+       x;                                                                      \
+})
+
+#define put_unaligned(val, ptr)                                                                \
+do {                                                                                   \
+       char *__p = (char *) (ptr);                                                     \
+       int x;                                                                          \
+                                                                                       \
+       switch (sizeof(*ptr)) {                                                         \
+       case 2:                                                                         \
+       {                                                                               \
+               asm("   stb%I1.p        %0,%M1          \n"                             \
+                   "   srli            %0,#8,%0        \n"                             \
+                   "   stb%I2          %0,%M2          \n"                             \
+                   : "=r"(x), "=m"(__p[1]),  "=m"(__p[0])                              \
+                   : "0"(val)                                                          \
+                   );                                                                  \
+               break;                                                                  \
+       }                                                                               \
+                                                                                       \
+       case 4:                                                                         \
+       {                                                                               \
+               asm("   stb%I1.p        %0,%M1          \n"                             \
+                   "   srli            %0,#8,%0        \n"                             \
+                   "   stb%I2.p        %0,%M2          \n"                             \
+                   "   srli            %0,#8,%0        \n"                             \
+                   "   stb%I3.p        %0,%M3          \n"                             \
+                   "   srli            %0,#8,%0        \n"                             \
+                   "   stb%I4          %0,%M4          \n"                             \
+                   : "=r"(x), "=m"(__p[3]),  "=m"(__p[2]), "=m"(__p[1]), "=m"(__p[0])  \
+                   : "0"(val)                                                          \
+                   );                                                                  \
+               break;                                                                  \
+       }                                                                               \
+                                                                                       \
+       case 8:                                                                         \
+       {                                                                               \
+               uint32_t __high, __low;                                                 \
+               __high = (uint64_t)val >> 32;                                           \
+               __low = val & 0xffffffff;                                               \
+               asm("   stb%I2.p        %0,%M2          \n"                             \
+                   "   srli            %0,#8,%0        \n"                             \
+                   "   stb%I3.p        %0,%M3          \n"                             \
+                   "   srli            %0,#8,%0        \n"                             \
+                   "   stb%I4.p        %0,%M4          \n"                             \
+                   "   srli            %0,#8,%0        \n"                             \
+                   "   stb%I5.p        %0,%M5          \n"                             \
+                   "   srli            %0,#8,%0        \n"                             \
+                   "   stb%I6.p        %1,%M6          \n"                             \
+                   "   srli            %1,#8,%1        \n"                             \
+                   "   stb%I7.p        %1,%M7          \n"                             \
+                   "   srli            %1,#8,%1        \n"                             \
+                   "   stb%I8.p        %1,%M8          \n"                             \
+                   "   srli            %1,#8,%1        \n"                             \
+                   "   stb%I9          %1,%M9          \n"                             \
+                   : "=&r"(__low), "=&r"(__high), "=m"(__p[7]), "=m"(__p[6]),          \
+                     "=m"(__p[5]), "=m"(__p[4]), "=m"(__p[3]), "=m"(__p[2]),           \
+                     "=m"(__p[1]), "=m"(__p[0])                                        \
+                   : "0"(__low), "1"(__high)                                           \
+                   );                                                                  \
+               break;                                                                  \
+       }                                                                               \
+                                                                                       \
+        default:                                                                       \
+               *(ptr) = (val);                                                         \
+               break;                                                                  \
+       }                                                                               \
+} while(0)
+
+#endif
+
+#endif
diff --git a/include/asm-frv/unistd.h b/include/asm-frv/unistd.h
new file mode 100644 (file)
index 0000000..7299398
--- /dev/null
@@ -0,0 +1,501 @@
+#ifndef _ASM_UNISTD_H_
+#define _ASM_UNISTD_H_
+
+/*
+ * This file contains the system call numbers.
+ */
+
+#define __NR_restart_syscall      0
+#define __NR_exit                1
+#define __NR_fork                2
+#define __NR_read                3
+#define __NR_write               4
+#define __NR_open                5
+#define __NR_close               6
+#define __NR_waitpid             7
+#define __NR_creat               8
+#define __NR_link                9
+#define __NR_unlink             10
+#define __NR_execve             11
+#define __NR_chdir              12
+#define __NR_time               13
+#define __NR_mknod              14
+#define __NR_chmod              15
+#define __NR_lchown             16
+#define __NR_break              17
+#define __NR_oldstat            18
+#define __NR_lseek              19
+#define __NR_getpid             20
+#define __NR_mount              21
+#define __NR_umount             22
+#define __NR_setuid             23
+#define __NR_getuid             24
+#define __NR_stime              25
+#define __NR_ptrace             26
+#define __NR_alarm              27
+#define __NR_oldfstat           28
+#define __NR_pause              29
+#define __NR_utime              30
+#define __NR_stty               31
+#define __NR_gtty               32
+#define __NR_access             33
+#define __NR_nice               34
+#define __NR_ftime              35
+#define __NR_sync               36
+#define __NR_kill               37
+#define __NR_rename             38
+#define __NR_mkdir              39
+#define __NR_rmdir              40
+#define __NR_dup                41
+#define __NR_pipe               42
+#define __NR_times              43
+#define __NR_prof               44
+#define __NR_brk                45
+#define __NR_setgid             46
+#define __NR_getgid             47
+#define __NR_signal             48
+#define __NR_geteuid            49
+#define __NR_getegid            50
+#define __NR_acct               51
+#define __NR_umount2            52
+#define __NR_lock               53
+#define __NR_ioctl              54
+#define __NR_fcntl              55
+#define __NR_mpx                56
+#define __NR_setpgid            57
+#define __NR_ulimit             58
+// #define __NR_oldolduname     /* 59 */ obsolete
+#define __NR_umask              60
+#define __NR_chroot             61
+#define __NR_ustat              62
+#define __NR_dup2               63
+#define __NR_getppid            64
+#define __NR_getpgrp            65
+#define __NR_setsid             66
+#define __NR_sigaction          67
+#define __NR_sgetmask           68
+#define __NR_ssetmask           69
+#define __NR_setreuid           70
+#define __NR_setregid           71
+#define __NR_sigsuspend                 72
+#define __NR_sigpending                 73
+#define __NR_sethostname        74
+#define __NR_setrlimit          75
+#define __NR_getrlimit          76     /* Back compatible 2Gig limited rlimit */
+#define __NR_getrusage          77
+#define __NR_gettimeofday       78
+#define __NR_settimeofday       79
+#define __NR_getgroups          80
+#define __NR_setgroups          81
+#define __NR_select             82
+#define __NR_symlink            83
+#define __NR_oldlstat           84
+#define __NR_readlink           85
+#define __NR_uselib             86
+#define __NR_swapon             87
+#define __NR_reboot             88
+#define __NR_readdir            89
+// #define __NR_mmap            90     /* obsolete - not implemented */
+#define __NR_munmap             91
+#define __NR_truncate           92
+#define __NR_ftruncate          93
+#define __NR_fchmod             94
+#define __NR_fchown             95
+#define __NR_getpriority        96
+#define __NR_setpriority        97
+// #define __NR_profil          /* 98 */ obsolete
+#define __NR_statfs             99
+#define __NR_fstatfs           100
+// #define __NR_ioperm         /* 101 */ not supported
+#define __NR_socketcall                102
+#define __NR_syslog            103
+#define __NR_setitimer         104
+#define __NR_getitimer         105
+#define __NR_stat              106
+#define __NR_lstat             107
+#define __NR_fstat             108
+// #define __NR_olduname               /* 109 */ obsolete
+// #define __NR_iopl           /* 110 */ not supported
+#define __NR_vhangup           111
+// #define __NR_idle           /* 112 */ Obsolete
+// #define __NR_vm86old                /* 113 */ not supported
+#define __NR_wait4             114
+#define __NR_swapoff           115
+#define __NR_sysinfo           116
+#define __NR_ipc               117
+#define __NR_fsync             118
+#define __NR_sigreturn         119
+#define __NR_clone             120
+#define __NR_setdomainname     121
+#define __NR_uname             122
+// #define __NR_modify_ldt     /* 123 */ not supported
+#define __NR_cacheflush                123
+#define __NR_adjtimex          124
+#define __NR_mprotect          125
+#define __NR_sigprocmask       126
+#define __NR_create_module     127
+#define __NR_init_module       128
+#define __NR_delete_module     129
+#define __NR_get_kernel_syms   130
+#define __NR_quotactl          131
+#define __NR_getpgid           132
+#define __NR_fchdir            133
+#define __NR_bdflush           134
+#define __NR_sysfs             135
+#define __NR_personality       136
+#define __NR_afs_syscall       137 /* Syscall for Andrew File System */
+#define __NR_setfsuid          138
+#define __NR_setfsgid          139
+#define __NR__llseek           140
+#define __NR_getdents          141
+#define __NR__newselect                142
+#define __NR_flock             143
+#define __NR_msync             144
+#define __NR_readv             145
+#define __NR_writev            146
+#define __NR_getsid            147
+#define __NR_fdatasync         148
+#define __NR__sysctl           149
+#define __NR_mlock             150
+#define __NR_munlock           151
+#define __NR_mlockall          152
+#define __NR_munlockall                153
+#define __NR_sched_setparam            154
+#define __NR_sched_getparam            155
+#define __NR_sched_setscheduler                156
+#define __NR_sched_getscheduler                157
+#define __NR_sched_yield               158
+#define __NR_sched_get_priority_max    159
+#define __NR_sched_get_priority_min    160
+#define __NR_sched_rr_get_interval     161
+#define __NR_nanosleep         162
+#define __NR_mremap            163
+#define __NR_setresuid         164
+#define __NR_getresuid         165
+// #define __NR_vm86           /* 166 */ not supported
+#define __NR_query_module      167
+#define __NR_poll              168
+#define __NR_nfsservctl                169
+#define __NR_setresgid         170
+#define __NR_getresgid         171
+#define __NR_prctl             172
+#define __NR_rt_sigreturn      173
+#define __NR_rt_sigaction      174
+#define __NR_rt_sigprocmask    175
+#define __NR_rt_sigpending     176
+#define __NR_rt_sigtimedwait   177
+#define __NR_rt_sigqueueinfo   178
+#define __NR_rt_sigsuspend     179
+#define __NR_pread             180
+#define __NR_pwrite            181
+#define __NR_chown             182
+#define __NR_getcwd            183
+#define __NR_capget            184
+#define __NR_capset            185
+#define __NR_sigaltstack       186
+#define __NR_sendfile          187
+#define __NR_getpmsg           188     /* some people actually want streams */
+#define __NR_putpmsg           189     /* some people actually want streams */
+#define __NR_vfork             190
+#define __NR_ugetrlimit                191     /* SuS compliant getrlimit */
+#define __NR_mmap2             192
+#define __NR_truncate64                193
+#define __NR_ftruncate64       194
+#define __NR_stat64            195
+#define __NR_lstat64           196
+#define __NR_fstat64           197
+#define __NR_lchown32          198
+#define __NR_getuid32          199
+#define __NR_getgid32          200
+#define __NR_geteuid32         201
+#define __NR_getegid32         202
+#define __NR_setreuid32                203
+#define __NR_setregid32                204
+#define __NR_getgroups32       205
+#define __NR_setgroups32       206
+#define __NR_fchown32          207
+#define __NR_setresuid32       208
+#define __NR_getresuid32       209
+#define __NR_setresgid32       210
+#define __NR_getresgid32       211
+#define __NR_chown32           212
+#define __NR_setuid32          213
+#define __NR_setgid32          214
+#define __NR_setfsuid32                215
+#define __NR_setfsgid32                216
+#define __NR_pivot_root                217
+#define __NR_mincore           218
+#define __NR_madvise           219
+
+#define __NR_getdents64                220
+#define __NR_fcntl64           221
+#define __NR_security          223     /* syscall for security modules */
+#define __NR_gettid            224
+#define __NR_readahead         225
+#define __NR_setxattr          226
+#define __NR_lsetxattr         227
+#define __NR_fsetxattr         228
+#define __NR_getxattr          229
+#define __NR_lgetxattr         230
+#define __NR_fgetxattr         231
+#define __NR_listxattr         232
+#define __NR_llistxattr                233
+#define __NR_flistxattr                234
+#define __NR_removexattr       235
+#define __NR_lremovexattr      236
+#define __NR_fremovexattr      237
+#define __NR_tkill             238
+#define __NR_sendfile64                239
+#define __NR_futex             240
+#define __NR_sched_setaffinity 241
+#define __NR_sched_getaffinity 242
+#define __NR_set_thread_area   243
+#define __NR_get_thread_area   244
+#define __NR_io_setup          245
+#define __NR_io_destroy                246
+#define __NR_io_getevents      247
+#define __NR_io_submit         248
+#define __NR_io_cancel         249
+#define __NR_fadvise64         250
+
+#define __NR_exit_group                252
+#define __NR_lookup_dcookie    253
+#define __NR_epoll_create      254
+#define __NR_epoll_ctl         255
+#define __NR_epoll_wait                256
+#define __NR_remap_file_pages  257
+#define __NR_set_tid_address   258
+#define __NR_timer_create      259
+#define __NR_timer_settime     (__NR_timer_create+1)
+#define __NR_timer_gettime     (__NR_timer_create+2)
+#define __NR_timer_getoverrun  (__NR_timer_create+3)
+#define __NR_timer_delete      (__NR_timer_create+4)
+#define __NR_clock_settime     (__NR_timer_create+5)
+#define __NR_clock_gettime     (__NR_timer_create+6)
+#define __NR_clock_getres      (__NR_timer_create+7)
+#define __NR_clock_nanosleep   (__NR_timer_create+8)
+#define __NR_statfs64          268
+#define __NR_fstatfs64         269
+#define __NR_tgkill            270
+#define __NR_utimes            271
+#define __NR_fadvise64_64      272
+#define __NR_vserver           273
+#define __NR_mbind             274
+#define __NR_get_mempolicy     275
+#define __NR_set_mempolicy     276
+#define __NR_mq_open           277
+#define __NR_mq_unlink         (__NR_mq_open+1)
+#define __NR_mq_timedsend      (__NR_mq_open+2)
+#define __NR_mq_timedreceive   (__NR_mq_open+3)
+#define __NR_mq_notify         (__NR_mq_open+4)
+#define __NR_mq_getsetattr     (__NR_mq_open+5)
+#define __NR_sys_kexec_load    283
+#define __NR_waitid            284
+/* #define __NR_sys_setaltroot 285 */
+#define __NR_add_key           286
+#define __NR_request_key       287
+#define __NR_keyctl            288
+#define __NR_vperfctr_open     289
+#define __NR_vperfctr_control  (__NR_perfctr_info+1)
+#define __NR_vperfctr_unlink   (__NR_perfctr_info+2)
+#define __NR_vperfctr_iresume  (__NR_perfctr_info+3)
+#define __NR_vperfctr_read     (__NR_perfctr_info+4)
+
+#define NR_syscalls 294
+
+/*
+ * process the return value of a syscall, consigning it to one of two possible fates
+ * - user-visible error numbers are in the range -1 - -4095: see <asm-frv/errno.h>
+ */
+#undef __syscall_return
+#define __syscall_return(type, res)                                    \
+do {                                                                   \
+        unsigned long __sr2 = (res);                                   \
+       if (__builtin_expect(__sr2 >= (unsigned long)(-4095), 0)) {     \
+               errno = (-__sr2);                                       \
+               __sr2 = ULONG_MAX;                                      \
+       }                                                               \
+       return (type) __sr2;                                            \
+} while (0)
+
+/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
+
+#undef _syscall0
+#define _syscall0(type,name)                                           \
+type name(void)                                                                \
+{                                                                      \
+       register unsigned long __scnum __asm__ ("gr7") = (__NR_##name); \
+       register unsigned long __sc0 __asm__ ("gr8");                   \
+       __asm__ __volatile__ ("tira gr0,#0"                             \
+                             : "=r" (__sc0)                            \
+                             : "r" (__scnum));                         \
+       __syscall_return(type, __sc0);                                  \
+}
+
+#undef _syscall1
+#define _syscall1(type,name,type1,arg1)                                                \
+type name(type1 arg1)                                                          \
+{                                                                              \
+       register unsigned long __scnum __asm__ ("gr7") = (__NR_##name);         \
+       register unsigned long __sc0 __asm__ ("gr8") = (unsigned long) arg1;    \
+       __asm__ __volatile__ ("tira gr0,#0"                                     \
+                             : "+r" (__sc0)                                    \
+                             : "r" (__scnum));                                 \
+       __syscall_return(type, __sc0);                                          \
+}
+
+#undef _syscall2
+#define _syscall2(type,name,type1,arg1,type2,arg2)                             \
+type name(type1 arg1,type2 arg2)                                               \
+{                                                                              \
+       register unsigned long __scnum __asm__ ("gr7") = (__NR_##name);         \
+       register unsigned long __sc0 __asm__ ("gr8") = (unsigned long) arg1;    \
+       register unsigned long __sc1 __asm__ ("gr9") = (unsigned long) arg2;    \
+       __asm__ __volatile__ ("tira gr0,#0"                                     \
+                             : "+r" (__sc0)                                    \
+                             : "r" (__scnum), "r" (__sc1));                    \
+       __syscall_return(type, __sc0);                                          \
+}
+
+#undef _syscall3
+#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)                  \
+type name(type1 arg1,type2 arg2,type3 arg3)                                    \
+{                                                                              \
+       register unsigned long __scnum __asm__ ("gr7") = (__NR_##name);         \
+       register unsigned long __sc0 __asm__ ("gr8") = (unsigned long) arg1;    \
+       register unsigned long __sc1 __asm__ ("gr9") = (unsigned long) arg2;    \
+       register unsigned long __sc2 __asm__ ("gr10") = (unsigned long) arg3;   \
+       __asm__ __volatile__ ("tira gr0,#0"                                     \
+                             : "+r" (__sc0)                                    \
+                             : "r" (__scnum), "r" (__sc1), "r" (__sc2));       \
+       __syscall_return(type, __sc0);                                          \
+}
+
+#undef _syscall4
+#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)               \
+type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4)                             \
+{                                                                                      \
+       register unsigned long __scnum __asm__ ("gr7") = (__NR_##name);                 \
+       register unsigned long __sc0 __asm__ ("gr8") = (unsigned long) arg1;            \
+       register unsigned long __sc1 __asm__ ("gr9") = (unsigned long) arg2;            \
+       register unsigned long __sc2 __asm__ ("gr10") = (unsigned long) arg3;           \
+       register unsigned long __sc3 __asm__ ("gr11") = (unsigned long) arg4;           \
+       __asm__ __volatile__ ("tira gr0,#0"                                             \
+                             : "+r" (__sc0)                                            \
+                             : "r" (__scnum), "r" (__sc1), "r" (__sc2), "r" (__sc3));  \
+       __syscall_return(type, __sc0);                                                  \
+}
+
+#undef _syscall5
+#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5)    \
+type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5)                 \
+{                                                                                      \
+       register unsigned long __scnum __asm__ ("gr7") = (__NR_##name);                 \
+       register unsigned long __sc0 __asm__ ("gr8") = (unsigned long) arg1;            \
+       register unsigned long __sc1 __asm__ ("gr9") = (unsigned long) arg2;            \
+       register unsigned long __sc2 __asm__ ("gr10") = (unsigned long) arg3;           \
+       register unsigned long __sc3 __asm__ ("gr11") = (unsigned long) arg4;           \
+       register unsigned long __sc4 __asm__ ("gr12") = (unsigned long) arg5;           \
+       __asm__ __volatile__ ("tira gr0,#0"                                             \
+                             : "+r" (__sc0)                                            \
+                             : "r" (__scnum), "r" (__sc1), "r" (__sc2),                \
+                             "r" (__sc3), "r" (__sc4));                                \
+       __syscall_return(type, __sc0);                                                  \
+}
+
+#undef _syscall6
+#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5, type6, arg6) \
+type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6)              \
+{                                                                                               \
+       register unsigned long __scnum __asm__ ("gr7") = (__NR_##name);                          \
+       register unsigned long __sc0 __asm__ ("gr8") = (unsigned long) arg1;                     \
+       register unsigned long __sc1 __asm__ ("gr9") = (unsigned long) arg2;                     \
+       register unsigned long __sc2 __asm__ ("gr10") = (unsigned long) arg3;                    \
+       register unsigned long __sc3 __asm__ ("gr11") = (unsigned long) arg4;                    \
+       register unsigned long __sc4 __asm__ ("gr12") = (unsigned long) arg5;                    \
+       register unsigned long __sc5 __asm__ ("gr13") = (unsigned long) arg6;                    \
+       __asm__ __volatile__ ("tira gr0,#0"                                                      \
+                             : "+r" (__sc0)                                                     \
+                             : "r" (__scnum), "r" (__sc1), "r" (__sc2),                         \
+                             "r" (__sc3), "r" (__sc4), "r" (__sc5));                            \
+       __syscall_return(type, __sc0);                                                           \
+}
+
+
+#ifdef __KERNEL_SYSCALLS__
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/linkage.h>
+#include <asm/ptrace.h>
+
+/*
+ * we need this inline - forking from kernel space will result
+ * in NO COPY ON WRITE (!!!), until an execve is executed. This
+ * is no problem, but for the stack. This is handled by not letting
+ * main() use the stack at all after fork(). Thus, no function
+ * calls - which means inline code for fork too, as otherwise we
+ * would use the stack upon exit from 'fork()'.
+ *
+ * Actually only pause and fork are needed inline, so that there
+ * won't be any messing with the stack from main(), but we define
+ * some others too.
+ */
+#define __NR__exit __NR_exit
+static inline _syscall0(int,pause)
+static inline _syscall0(int,sync)
+static inline _syscall0(pid_t,setsid)
+static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
+static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
+static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
+static inline _syscall1(int,dup,int,fd)
+static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
+static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
+static inline _syscall1(int,close,int,fd)
+static inline _syscall1(int,_exit,int,exitcode)
+static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static inline _syscall1(int,delete_module,const char *,name)
+
+static inline pid_t wait(int * wait_stat)
+{
+       return waitpid(-1,wait_stat,0);
+}
+
+#endif
+
+#ifdef __KERNEL__
+#define __ARCH_WANT_IPC_PARSE_VERSION
+/* #define __ARCH_WANT_OLD_READDIR */
+#define __ARCH_WANT_OLD_STAT
+#define __ARCH_WANT_STAT64
+#define __ARCH_WANT_SYS_ALARM
+/* #define __ARCH_WANT_SYS_GETHOSTNAME */
+#define __ARCH_WANT_SYS_PAUSE
+/* #define __ARCH_WANT_SYS_SGETMASK */
+/* #define __ARCH_WANT_SYS_SIGNAL */
+#define __ARCH_WANT_SYS_TIME
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_WAITPID
+#define __ARCH_WANT_SYS_SOCKETCALL
+#define __ARCH_WANT_SYS_FADVISE64
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+/* #define __ARCH_WANT_SYS_OLD_GETRLIMIT */
+#define __ARCH_WANT_SYS_OLDUMOUNT
+/* #define __ARCH_WANT_SYS_SIGPENDING */
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_SYS_RT_SIGACTION
+#endif
+
+/*
+ * "Conditional" syscalls
+ *
+ * What we want is __attribute__((weak,alias("sys_ni_syscall"))),
+ * but it doesn't work on all toolchains, so we just do it by hand
+ */
+#ifndef cond_syscall
+#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall");
+#endif
+
+#endif /* _ASM_UNISTD_H_ */
diff --git a/include/asm-frv/user.h b/include/asm-frv/user.h
new file mode 100644 (file)
index 0000000..82fa8fa
--- /dev/null
@@ -0,0 +1,80 @@
+/* user.h: FR-V core file format stuff
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ASM_USER_H
+#define _ASM_USER_H
+
+#include <asm/page.h>
+#include <asm/registers.h>
+
+/* Core file format: The core file is written in such a way that gdb
+ * can understand it and provide useful information to the user (under
+ * linux we use the 'trad-core' bfd).  There are quite a number of
+ * obstacles to being able to view the contents of the floating point
+ * registers, and until these are solved you will not be able to view
+ * the contents of them.  Actually, you can read in the core file and
+ * look at the contents of the user struct to find out what the
+ * floating point registers contain.
+ *
+ * The actual file contents are as follows:
+ * UPAGE:
+ *   1 page consisting of a user struct that tells gdb what is present
+ *   in the file.  Directly after this is a copy of the task_struct,
+ *   which is currently not used by gdb, but it may come in useful at
+ *   some point.  All of the registers are stored as part of the
+ *   upage.  The upage should always be only one page.
+ *
+ * DATA:
+ *   The data area is stored.  We use current->end_text to
+ *   current->brk to pick up all of the user variables, plus any
+ *   memory that may have been malloced.  No attempt is made to
+ *   determine if a page is demand-zero or if a page is totally
+ *   unused, we just cover the entire range.  All of the addresses are
+ *   rounded in such a way that an integral number of pages is
+ *   written.
+ *
+ * STACK:
+ *   We need the stack information in order to get a meaningful
+ *   backtrace.  We need to write the data from (esp) to
+ *   current->start_stack, so we round each of these off in order to
+ *   be able to write an integer number of pages.  The minimum core
+ *   file size is 3 pages, or 12288 bytes.
+ */
+
+/* When the kernel dumps core, it starts by dumping the user struct -
+ * this will be used by gdb to figure out where the data and stack segments
+ *  are within the file, and what virtual addresses to use.
+ */
+struct user {
+       /* We start with the registers, to mimic the way that "memory" is returned
+        * from the ptrace(3,...) function.  */
+       struct user_context     regs;
+
+       /* The rest of this junk is to help gdb figure out what goes where */
+       unsigned long           u_tsize;        /* Text segment size (pages). */
+       unsigned long           u_dsize;        /* Data segment size (pages). */
+       unsigned long           u_ssize;        /* Stack segment size (pages). */
+       unsigned long           start_code;     /* Starting virtual address of text. */
+       unsigned long           start_stack;    /* Starting virtual address of stack area.
+                                                * This is actually the bottom of the stack,
+                                                * the top of the stack is always found in the
+                                                * esp register.  */
+       long int                signal;         /* Signal that caused the core dump. */
+
+       unsigned long           magic;          /* To uniquely identify a core file */
+       char                    u_comm[32];     /* User command that was responsible */
+};
+
+#define NBPG                   PAGE_SIZE
+#define UPAGES                 1
+#define HOST_TEXT_START_ADDR   (u.start_code)
+#define HOST_STACK_END_ADDR    (u.start_stack + u.u_ssize * NBPG)
+
+#endif
diff --git a/include/asm-frv/virtconvert.h b/include/asm-frv/virtconvert.h
new file mode 100644 (file)
index 0000000..a29a0ae
--- /dev/null
@@ -0,0 +1,42 @@
+/* virtconvert.h: virtual/physical/page address convertion
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ASM_VIRTCONVERT_H
+#define _ASM_VIRTCONVERT_H
+
+/*
+ * Macros used for converting between virtual and physical mappings.
+ */
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <asm/setup.h>
+
+#ifdef CONFIG_MMU
+
+#define phys_to_virt(vaddr)    ((void *) ((unsigned long)(vaddr) + PAGE_OFFSET))
+#define virt_to_phys(vaddr)    ((unsigned long) (vaddr) - PAGE_OFFSET)
+
+#else
+
+#define phys_to_virt(vaddr)    ((void *) (vaddr))
+#define virt_to_phys(vaddr)    ((unsigned long) (vaddr))
+
+#endif
+
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+
+#define __page_address(page)   (PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT))
+#define page_to_phys(page)     virt_to_phys((void *)__page_address(page))
+
+#endif
+#endif
diff --git a/include/asm-generic/4level-fixup.h b/include/asm-generic/4level-fixup.h
new file mode 100644 (file)
index 0000000..0267574
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef _4LEVEL_FIXUP_H
+#define _4LEVEL_FIXUP_H
+
+#define __ARCH_HAS_4LEVEL_HACK
+
+#define PUD_SIZE                       PGDIR_SIZE
+#define PUD_MASK                       PGDIR_MASK
+#define PTRS_PER_PUD                   1
+
+#define pud_t                          pgd_t
+
+#define pmd_alloc(mm, pud, address)                    \
+({     pmd_t *ret;                                     \
+       if (pgd_none(*pud))                             \
+               ret = __pmd_alloc(mm, pud, address);    \
+       else                                            \
+               ret = pmd_offset(pud, address);         \
+       ret;                                            \
+})
+
+#define pud_alloc(mm, pgd, address)    (pgd)
+#define pud_offset(pgd, start)         (pgd)
+#define pud_none(pud)                  0
+#define pud_bad(pud)                   0
+#define pud_present(pud)               1
+#define pud_ERROR(pud)                 do { } while (0)
+#define pud_clear(pud)                 pgd_clear(pud)
+
+#undef pud_free_tlb
+#define pud_free_tlb(tlb, x)            do { } while (0)
+#define pud_free(x)                    do { } while (0)
+#define __pud_free_tlb(tlb, x)         do { } while (0)
+
+#endif
diff --git a/include/asm-generic/cputime.h b/include/asm-generic/cputime.h
new file mode 100644 (file)
index 0000000..7943c66
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef _ASM_GENERIC_CPUTIME_H
+#define _ASM_GENERIC_CPUTIME_H
+
+#include <linux/time.h>
+#include <linux/jiffies.h>
+
+typedef unsigned long cputime_t;
+
+#define cputime_zero                   (0UL)
+#define cputime_max                    ((~0UL >> 1) - 1)
+#define cputime_add(__a, __b)          ((__a) +  (__b))
+#define cputime_sub(__a, __b)          ((__a) -  (__b))
+#define cputime_eq(__a, __b)           ((__a) == (__b))
+#define cputime_gt(__a, __b)           ((__a) >  (__b))
+#define cputime_ge(__a, __b)           ((__a) >= (__b))
+#define cputime_lt(__a, __b)           ((__a) <  (__b))
+#define cputime_le(__a, __b)           ((__a) <= (__b))
+#define cputime_to_jiffies(__ct)       (__ct)
+#define jiffies_to_cputime(__hz)       (__hz)
+
+typedef u64 cputime64_t;
+
+#define cputime64_zero (0ULL)
+#define cputime64_add(__a, __b)                ((__a) + (__b))
+#define cputime64_to_jiffies64(__ct)   (__ct)
+#define cputime_to_cputime64(__ct)     ((u64) __ct)
+
+
+/*
+ * Convert cputime to milliseconds and back.
+ */
+#define cputime_to_msecs(__ct)         jiffies_to_msecs(__ct)
+#define msecs_to_cputime(__msecs)      msecs_to_jiffies(__msecs)
+
+/*
+ * Convert cputime to seconds and back.
+ */
+#define cputime_to_secs(jif)           ((jif) / HZ)
+#define secs_to_cputime(sec)           ((sec) * HZ)
+
+/*
+ * Convert cputime to timespec and back.
+ */
+#define timespec_to_cputime(__val)     timespec_to_jiffies(__val)
+#define cputime_to_timespec(__ct,__val)        jiffies_to_timespec(__ct,__val)
+
+/*
+ * Convert cputime to timeval and back.
+ */
+#define timeval_to_cputime(__val)      timeval_to_jiffies(__val)
+#define cputime_to_timeval(__ct,__val) jiffies_to_timeval(__ct,__val)
+
+/*
+ * Convert cputime to clock and back.
+ */
+#define cputime_to_clock_t(__ct)       jiffies_to_clock_t(__ct)
+#define clock_t_to_cputime(__x)                clock_t_to_jiffies(__x)
+
+/*
+ * Convert cputime64 to clock.
+ */
+#define cputime64_to_clock_t(__ct)     jiffies_64_to_clock_t(__ct)
+
+#endif
diff --git a/include/asm-generic/pgtable-nopmd.h b/include/asm-generic/pgtable-nopmd.h
new file mode 100644 (file)
index 0000000..b7714d4
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef _PGTABLE_NOPMD_H
+#define _PGTABLE_NOPMD_H
+
+#ifndef __ASSEMBLY__
+
+#include <asm-generic/pgtable-nopud.h>
+
+/*
+ * Having the pmd type consist of a pud gets the size right, and allows
+ * us to conceptually access the pud entry that this pmd is folded into
+ * without casting.
+ */
+typedef struct { pud_t pud; } pmd_t;
+
+#define PMD_SHIFT      PUD_SHIFT
+#define PTRS_PER_PMD   1
+#define PMD_SIZE       (1UL << PMD_SHIFT)
+#define PMD_MASK       (~(PMD_SIZE-1))
+
+/*
+ * The "pud_xxx()" functions here are trivial for a folded two-level
+ * setup: the pmd is never bad, and a pmd always exists (as it's folded
+ * into the pud entry)
+ */
+static inline int pud_none(pud_t pud)          { return 0; }
+static inline int pud_bad(pud_t pud)           { return 0; }
+static inline int pud_present(pud_t pud)       { return 1; }
+static inline void pud_clear(pud_t *pud)       { }
+#define pmd_ERROR(pmd)                         (pud_ERROR((pmd).pud))
+
+#define pud_populate(mm, pmd, pte)             do { } while (0)
+
+/*
+ * (pmds are folded into puds so this doesn't get actually called,
+ * but the define is needed for a generic inline function.)
+ */
+#define set_pud(pudptr, pudval)                        set_pmd((pmd_t *)(pudptr), (pmd_t) { pudval })
+
+static inline pmd_t * pmd_offset(pud_t * pud, unsigned long address)
+{
+       return (pmd_t *)pud;
+}
+
+#define pmd_val(x)                             (pud_val((x).pud))
+#define __pmd(x)                               ((pmd_t) { __pud(x) } )
+
+#define pud_page(pud)                          (pmd_page((pmd_t){ pud }))
+#define pud_page_kernel(pud)                   (pmd_page_kernel((pmd_t){ pud }))
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pud, so has no extra memory associated with it.
+ */
+#define pmd_alloc_one(mm, address)             NULL
+#define pmd_free(x)                            do { } while (0)
+#define __pmd_free_tlb(tlb, x)                 do { } while (0)
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _PGTABLE_NOPMD_H */
diff --git a/include/asm-generic/pgtable-nopud.h b/include/asm-generic/pgtable-nopud.h
new file mode 100644 (file)
index 0000000..ffce31f
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef _PGTABLE_NOPUD_H
+#define _PGTABLE_NOPUD_H
+
+#ifndef __ASSEMBLY__
+
+/*
+ * Having the pud type consist of a pgd gets the size right, and allows
+ * us to conceptually access the pgd entry that this pud is folded into
+ * without casting.
+ */
+typedef struct { pgd_t pgd; } pud_t;
+
+#define PUD_SHIFT      PGDIR_SHIFT
+#define PTRS_PER_PUD   1
+#define PUD_SIZE       (1UL << PUD_SHIFT)
+#define PUD_MASK       (~(PUD_SIZE-1))
+
+/*
+ * The "pgd_xxx()" functions here are trivial for a folded two-level
+ * setup: the pud is never bad, and a pud always exists (as it's folded
+ * into the pgd entry)
+ */
+static inline int pgd_none(pgd_t pgd)          { return 0; }
+static inline int pgd_bad(pgd_t pgd)           { return 0; }
+static inline int pgd_present(pgd_t pgd)       { return 1; }
+static inline void pgd_clear(pgd_t *pgd)       { }
+#define pud_ERROR(pud)                         (pgd_ERROR((pud).pgd))
+
+#define pgd_populate(mm, pgd, pud)             do { } while (0)
+/*
+ * (puds are folded into pgds so this doesn't get actually called,
+ * but the define is needed for a generic inline function.)
+ */
+#define set_pgd(pgdptr, pgdval)                        set_pud((pud_t *)(pgdptr), (pud_t) { pgdval })
+
+static inline pud_t * pud_offset(pgd_t * pgd, unsigned long address)
+{
+       return (pud_t *)pgd;
+}
+
+#define pud_val(x)                             (pgd_val((x).pgd))
+#define __pud(x)                               ((pud_t) { __pgd(x) } )
+
+#define pgd_page(pgd)                          (pud_page((pud_t){ pgd }))
+#define pgd_page_kernel(pgd)                   (pud_page_kernel((pud_t){ pgd }))
+
+/*
+ * allocating and freeing a pud is trivial: the 1-entry pud is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+#define pud_alloc_one(mm, address)             NULL
+#define pud_free(x)                            do { } while (0)
+#define __pud_free_tlb(tlb, x)                 do { } while (0)
+
+#endif /* __ASSEMBLY__ */
+#endif /* _PGTABLE_NOPUD_H */
diff --git a/include/asm-generic/resource.h b/include/asm-generic/resource.h
new file mode 100644 (file)
index 0000000..7ba1bfc
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef _ASM_GENERIC_RESOURCE_H
+#define _ASM_GENERIC_RESOURCE_H
+
+/*
+ * Resource limits
+ */
+
+/* Allow arch to control resource order */
+#ifndef __ARCH_RLIMIT_ORDER
+#define RLIMIT_CPU             0       /* CPU time in ms */
+#define RLIMIT_FSIZE           1       /* Maximum filesize */
+#define RLIMIT_DATA            2       /* max data size */
+#define RLIMIT_STACK           3       /* max stack size */
+#define RLIMIT_CORE            4       /* max core file size */
+#define RLIMIT_RSS             5       /* max resident set size */
+#define RLIMIT_NPROC           6       /* max number of processes */
+#define RLIMIT_NOFILE          7       /* max number of open files */
+#define RLIMIT_MEMLOCK         8       /* max locked-in-memory address space */
+#define RLIMIT_AS              9       /* address space limit */
+#define RLIMIT_LOCKS           10      /* maximum file locks held */
+#define RLIMIT_SIGPENDING      11      /* max number of pending signals */
+#define RLIMIT_MSGQUEUE                12      /* maximum bytes in POSIX mqueues */
+
+#define RLIM_NLIMITS           13
+#endif
+
+/*
+ * SuS says limits have to be unsigned.
+ * Which makes a ton more sense anyway.
+ */
+#ifndef RLIM_INFINITY
+#define RLIM_INFINITY  (~0UL)
+#endif
+
+#ifndef _STK_LIM_MAX
+#define _STK_LIM_MAX   RLIM_INFINITY
+#endif
+
+#ifdef __KERNEL__
+
+#define INIT_RLIMITS                                                   \
+{                                                                      \
+       [RLIMIT_CPU]            = { RLIM_INFINITY, RLIM_INFINITY },     \
+       [RLIMIT_FSIZE]          = { RLIM_INFINITY, RLIM_INFINITY },     \
+       [RLIMIT_DATA]           = { RLIM_INFINITY, RLIM_INFINITY },     \
+       [RLIMIT_STACK]          = {      _STK_LIM, _STK_LIM_MAX  },     \
+       [RLIMIT_CORE]           = {             0, RLIM_INFINITY },     \
+       [RLIMIT_RSS]            = { RLIM_INFINITY, RLIM_INFINITY },     \
+       [RLIMIT_NPROC]          = {             0,             0 },     \
+       [RLIMIT_NOFILE]         = {      INR_OPEN,     INR_OPEN  },     \
+       [RLIMIT_MEMLOCK]        = {   MLOCK_LIMIT,   MLOCK_LIMIT },     \
+       [RLIMIT_AS]             = { RLIM_INFINITY, RLIM_INFINITY },     \
+       [RLIMIT_LOCKS]          = { RLIM_INFINITY, RLIM_INFINITY },     \
+       [RLIMIT_SIGPENDING]     = { MAX_SIGPENDING, MAX_SIGPENDING },   \
+       [RLIMIT_MSGQUEUE]       = { MQ_BYTES_MAX, MQ_BYTES_MAX },       \
+}
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/include/asm-generic/termios.h b/include/asm-generic/termios.h
new file mode 100644 (file)
index 0000000..1e58ca3
--- /dev/null
@@ -0,0 +1,69 @@
+/* termios.h: generic termios/termio user copying/translation
+ */
+
+#ifndef _ASM_GENERIC_TERMIOS_H
+#define _ASM_GENERIC_TERMIOS_H
+
+#include <asm/uaccess.h>
+
+#ifndef __ARCH_TERMIO_GETPUT
+
+/*
+ * Translate a "termio" structure into a "termios". Ugh.
+ */
+static inline int user_termio_to_kernel_termios(struct termios *termios,
+                                               struct termio __user *termio)
+{
+       unsigned short tmp;
+
+       if (get_user(tmp, &termio->c_iflag) < 0)
+               goto fault;
+       termios->c_iflag = (0xffff0000 & termios->c_iflag) | tmp;
+
+       if (get_user(tmp, &termio->c_oflag) < 0)
+               goto fault;
+       termios->c_oflag = (0xffff0000 & termios->c_oflag) | tmp;
+
+       if (get_user(tmp, &termio->c_cflag) < 0)
+               goto fault;
+       termios->c_cflag = (0xffff0000 & termios->c_cflag) | tmp;
+
+       if (get_user(tmp, &termio->c_lflag) < 0)
+               goto fault;
+       termios->c_lflag = (0xffff0000 & termios->c_lflag) | tmp;
+
+       if (get_user(termios->c_line, &termio->c_line) < 0)
+               goto fault;
+
+       if (copy_from_user(termios->c_cc, termio->c_cc, NCC) != 0)
+               goto fault;
+
+       return 0;
+
+ fault:
+       return -EFAULT;
+}
+
+/*
+ * Translate a "termios" structure into a "termio". Ugh.
+ */
+static inline int kernel_termios_to_user_termio(struct termio __user *termio,
+                                               struct termios *termios)
+{
+       if (put_user(termios->c_iflag, &termio->c_iflag) < 0 ||
+           put_user(termios->c_oflag, &termio->c_oflag) < 0 ||
+           put_user(termios->c_cflag, &termio->c_cflag) < 0 ||
+           put_user(termios->c_lflag, &termio->c_lflag) < 0 ||
+           put_user(termios->c_line,  &termio->c_line) < 0 ||
+           copy_to_user(termio->c_cc, termios->c_cc, NCC) != 0)
+               return -EFAULT;
+
+       return 0;
+}
+
+#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios))
+#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios))
+
+#endif /* __ARCH_TERMIO_GETPUT */
+
+#endif /* _ASM_GENERIC_TERMIOS_H */
diff --git a/include/asm-h8300/cputime.h b/include/asm-h8300/cputime.h
new file mode 100644 (file)
index 0000000..092e187
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __H8300_CPUTIME_H
+#define __H8300_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __H8300_CPUTIME_H */
diff --git a/include/asm-i386/cputime.h b/include/asm-i386/cputime.h
new file mode 100644 (file)
index 0000000..398ed7c
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __I386_CPUTIME_H
+#define __I386_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __I386_CPUTIME_H */
diff --git a/include/asm-i386/pci-direct.h b/include/asm-i386/pci-direct.h
new file mode 100644 (file)
index 0000000..4f6738b
--- /dev/null
@@ -0,0 +1 @@
+#include "asm-x86_64/pci-direct.h"
diff --git a/include/asm-ia64/cputime.h b/include/asm-ia64/cputime.h
new file mode 100644 (file)
index 0000000..72400a7
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __IA64_CPUTIME_H
+#define __IA64_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __IA64_CPUTIME_H */
diff --git a/include/asm-ia64/machvec_hpzx1_swiotlb.h b/include/asm-ia64/machvec_hpzx1_swiotlb.h
new file mode 100644 (file)
index 0000000..9924b1b
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef _ASM_IA64_MACHVEC_HPZX1_SWIOTLB_h
+#define _ASM_IA64_MACHVEC_HPZX1_SWIOTLB_h
+
+extern ia64_mv_setup_t                         dig_setup;
+extern ia64_mv_dma_init                                hwsw_init;
+extern ia64_mv_dma_alloc_coherent              hwsw_alloc_coherent;
+extern ia64_mv_dma_free_coherent               hwsw_free_coherent;
+extern ia64_mv_dma_map_single                  hwsw_map_single;
+extern ia64_mv_dma_unmap_single                        hwsw_unmap_single;
+extern ia64_mv_dma_map_sg                      hwsw_map_sg;
+extern ia64_mv_dma_unmap_sg                    hwsw_unmap_sg;
+extern ia64_mv_dma_supported                   hwsw_dma_supported;
+extern ia64_mv_dma_mapping_error               hwsw_dma_mapping_error;
+extern ia64_mv_dma_sync_single_for_cpu         hwsw_sync_single_for_cpu;
+extern ia64_mv_dma_sync_sg_for_cpu             hwsw_sync_sg_for_cpu;
+extern ia64_mv_dma_sync_single_for_device      hwsw_sync_single_for_device;
+extern ia64_mv_dma_sync_sg_for_device          hwsw_sync_sg_for_device;
+
+/*
+ * This stuff has dual use!
+ *
+ * For a generic kernel, the macros are used to initialize the
+ * platform's machvec structure.  When compiling a non-generic kernel,
+ * the macros are used directly.
+ */
+#define platform_name                          "hpzx1_swiotlb"
+
+#define platform_setup                         dig_setup
+#define platform_dma_init                      hwsw_init
+#define platform_dma_alloc_coherent            hwsw_alloc_coherent
+#define platform_dma_free_coherent             hwsw_free_coherent
+#define platform_dma_map_single                        hwsw_map_single
+#define platform_dma_unmap_single              hwsw_unmap_single
+#define platform_dma_map_sg                    hwsw_map_sg
+#define platform_dma_unmap_sg                  hwsw_unmap_sg
+#define platform_dma_supported                 hwsw_dma_supported
+#define platform_dma_mapping_error             hwsw_dma_mapping_error
+#define platform_dma_sync_single_for_cpu       hwsw_sync_single_for_cpu
+#define platform_dma_sync_sg_for_cpu           hwsw_sync_sg_for_cpu
+#define platform_dma_sync_single_for_device    hwsw_sync_single_for_device
+#define platform_dma_sync_sg_for_device                hwsw_sync_sg_for_device
+
+#endif /* _ASM_IA64_MACHVEC_HPZX1_SWIOTLB_h */
diff --git a/include/asm-ia64/sn/l1.h b/include/asm-ia64/sn/l1.h
new file mode 100644 (file)
index 0000000..d5dbd55
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992-1997,2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+#ifndef _ASM_IA64_SN_L1_H
+#define _ASM_IA64_SN_L1_H
+
+/* brick type response codes */
+#define L1_BRICKTYPE_PX         0x23            /* # */
+#define L1_BRICKTYPE_PE         0x25            /* % */
+#define L1_BRICKTYPE_N_p0       0x26            /* & */
+#define L1_BRICKTYPE_IP45       0x34            /* 4 */
+#define L1_BRICKTYPE_IP41       0x35            /* 5 */
+#define L1_BRICKTYPE_TWISTER    0x36            /* 6 */ /* IP53 & ROUTER */
+#define L1_BRICKTYPE_IX         0x3d            /* = */
+#define L1_BRICKTYPE_IP34       0x61            /* a */
+#define L1_BRICKTYPE_GA                0x62            /* b */
+#define L1_BRICKTYPE_C          0x63            /* c */
+#define L1_BRICKTYPE_OPUS_TIO  0x66            /* f */
+#define L1_BRICKTYPE_I          0x69            /* i */
+#define L1_BRICKTYPE_N          0x6e            /* n */
+#define L1_BRICKTYPE_OPUS       0x6f           /* o */
+#define L1_BRICKTYPE_P          0x70            /* p */
+#define L1_BRICKTYPE_R          0x72            /* r */
+#define L1_BRICKTYPE_CHI_CG     0x76            /* v */
+#define L1_BRICKTYPE_X          0x78            /* x */
+#define L1_BRICKTYPE_X2         0x79            /* y */
+#define L1_BRICKTYPE_SA                0x5e            /* ^ */ /* TIO bringup brick */
+#define L1_BRICKTYPE_PA                0x6a            /* j */
+#define L1_BRICKTYPE_IA                0x6b            /* k */
+
+#endif /* _ASM_IA64_SN_L1_H */
diff --git a/include/asm-ia64/sn/shub_mmr.h b/include/asm-ia64/sn/shub_mmr.h
new file mode 100644 (file)
index 0000000..430c50f
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2001-2004 Silicon Graphics, Inc.  All rights reserved.
+ */
+
+#ifndef _ASM_IA64_SN_SHUB_MMR_H
+#define _ASM_IA64_SN_SHUB_MMR_H
+
+/* ==================================================================== */
+/*                        Register "SH_IPI_INT"                         */
+/*               SHub Inter-Processor Interrupt Registers               */
+/* ==================================================================== */
+#define SH_IPI_INT                               0x0000000110000380UL
+#define SH_IPI_INT_MASK                          0x8ff3ffffffefffffUL
+#define SH_IPI_INT_INIT                          0x0000000000000000UL
+
+/*   SH_IPI_INT_TYPE                                                    */
+/*   Description:  Type of Interrupt: 0=INT, 2=PMI, 4=NMI, 5=INIT       */
+#define SH_IPI_INT_TYPE_SHFT                     0
+#define SH_IPI_INT_TYPE_MASK                     0x0000000000000007UL
+
+/*   SH_IPI_INT_AGT                                                     */
+/*   Description:  Agent, must be 0 for SHub                            */
+#define SH_IPI_INT_AGT_SHFT                      3
+#define SH_IPI_INT_AGT_MASK                      0x0000000000000008UL
+
+/*   SH_IPI_INT_PID                                                     */
+/*   Description:  Processor ID, same setting as on targeted McKinley  */
+#define SH_IPI_INT_PID_SHFT                      4
+#define SH_IPI_INT_PID_MASK                      0x00000000000ffff0UL
+
+/*   SH_IPI_INT_BASE                                                    */
+/*   Description:  Optional interrupt vector area, 2MB aligned          */
+#define SH_IPI_INT_BASE_SHFT                     21
+#define SH_IPI_INT_BASE_MASK                     0x0003ffffffe00000UL
+
+/*   SH_IPI_INT_IDX                                                     */
+/*   Description:  Targeted McKinley interrupt vector                   */
+#define SH_IPI_INT_IDX_SHFT                      52
+#define SH_IPI_INT_IDX_MASK                      0x0ff0000000000000UL
+
+/*   SH_IPI_INT_SEND                                                    */
+/*   Description:  Send Interrupt Message to PI, This generates a puls  */
+#define SH_IPI_INT_SEND_SHFT                     63
+#define SH_IPI_INT_SEND_MASK                     0x8000000000000000UL
+
+/* ==================================================================== */
+/*                     Register "SH_EVENT_OCCURRED"                     */
+/*                    SHub Interrupt Event Occurred                     */
+/* ==================================================================== */
+#define SH_EVENT_OCCURRED                        0x0000000110010000UL
+#define SH_EVENT_OCCURRED_ALIAS                  0x0000000110010008UL
+
+/* ==================================================================== */
+/*                     Register "SH_PI_CAM_CONTROL"                     */
+/*                      CRB CAM MMR Access Control                      */
+/* ==================================================================== */
+#ifndef __ASSEMBLY__
+#define SH_PI_CAM_CONTROL                        0x0000000120050300UL
+#else
+#define SH_PI_CAM_CONTROL                        0x0000000120050300
+#endif
+
+/* ==================================================================== */
+/*                        Register "SH_SHUB_ID"                         */
+/*                            SHub ID Number                            */
+/* ==================================================================== */
+#define SH_SHUB_ID                               0x0000000110060580UL
+#define SH_SHUB_ID_REVISION_SHFT                 28
+#define SH_SHUB_ID_REVISION_MASK                 0x00000000f0000000
+
+/* ==================================================================== */
+/*                         Register "SH_PTC_0"                          */
+/*       Puge Translation Cache Message Configuration Information       */
+/* ==================================================================== */
+#define SH_PTC_0                                 0x00000001101a0000UL
+#define SH_PTC_1                                 0x00000001101a0080UL
+
+/* ==================================================================== */
+/*                          Register "SH_RTC"                           */
+/*                           Real-time Clock                            */
+/* ==================================================================== */
+#define SH_RTC                                   0x00000001101c0000UL
+#define SH_RTC_MASK                              0x007fffffffffffffUL
+
+/* ==================================================================== */
+/*                 Register "SH_MEMORY_WRITE_STATUS_0|1"                */
+/*                    Memory Write Status for CPU 0 & 1                 */
+/* ==================================================================== */
+#define SH_MEMORY_WRITE_STATUS_0                 0x0000000120070000UL
+#define SH_MEMORY_WRITE_STATUS_1                 0x0000000120070080UL
+
+/* ==================================================================== */
+/*                   Register "SH_PIO_WRITE_STATUS_0|1"                 */
+/*                      PIO Write Status for CPU 0 & 1                  */
+/* ==================================================================== */
+#ifndef __ASSEMBLY__
+#define SH_PIO_WRITE_STATUS_0                    0x0000000120070200UL
+#define SH_PIO_WRITE_STATUS_1                    0x0000000120070280UL
+
+/*   SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK                               */
+/*   Description:  Deadlock response detected                           */
+#define SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK_SHFT 1
+#define SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK_MASK 0x0000000000000002
+
+/*   SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT                          */
+/*   Description:  Count of currently pending PIO writes                */
+#define SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_SHFT 56
+#define SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_MASK 0x3f00000000000000UL
+#else
+#define SH_PIO_WRITE_STATUS_0                    0x0000000120070200
+#define SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_SHFT 56
+#define SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK_SHFT 1
+#endif
+
+/* ==================================================================== */
+/*                Register "SH_PIO_WRITE_STATUS_0_ALIAS"                */
+/* ==================================================================== */
+#ifndef __ASSEMBLY__
+#define SH_PIO_WRITE_STATUS_0_ALIAS              0x0000000120070208UL
+#else
+#define SH_PIO_WRITE_STATUS_0_ALIAS              0x0000000120070208
+#endif
+
+/* ==================================================================== */
+/*                     Register "SH_EVENT_OCCURRED"                     */
+/*                    SHub Interrupt Event Occurred                     */
+/* ==================================================================== */
+/*   SH_EVENT_OCCURRED_UART_INT                                         */
+/*   Description:  Pending Junk Bus UART Interrupt                      */
+#define SH_EVENT_OCCURRED_UART_INT_SHFT          20
+#define SH_EVENT_OCCURRED_UART_INT_MASK          0x0000000000100000
+
+/*   SH_EVENT_OCCURRED_IPI_INT                                          */
+/*   Description:  Pending IPI Interrupt                                */
+#define SH_EVENT_OCCURRED_IPI_INT_SHFT           28
+#define SH_EVENT_OCCURRED_IPI_INT_MASK           0x0000000010000000
+
+/*   SH_EVENT_OCCURRED_II_INT0                                          */
+/*   Description:  Pending II 0 Interrupt                               */
+#define SH_EVENT_OCCURRED_II_INT0_SHFT           29
+#define SH_EVENT_OCCURRED_II_INT0_MASK           0x0000000020000000
+
+/*   SH_EVENT_OCCURRED_II_INT1                                          */
+/*   Description:  Pending II 1 Interrupt                               */
+#define SH_EVENT_OCCURRED_II_INT1_SHFT           30
+#define SH_EVENT_OCCURRED_II_INT1_MASK           0x0000000040000000
+
+/* ==================================================================== */
+/*                         Register "SH_PTC_0"                          */
+/*       Puge Translation Cache Message Configuration Information       */
+/* ==================================================================== */
+#define SH_PTC_0                                 0x00000001101a0000UL
+#define SH_PTC_0_MASK                            0x80000000fffffffd
+#define SH_PTC_0_INIT                            0x0000000000000000
+
+/*   SH_PTC_0_A                                                         */
+/*   Description:  Type                                                 */
+#define SH_PTC_0_A_SHFT                          0
+#define SH_PTC_0_A_MASK                          0x0000000000000001
+
+/*   SH_PTC_0_PS                                                        */
+/*   Description:  Page Size                                            */
+#define SH_PTC_0_PS_SHFT                         2
+#define SH_PTC_0_PS_MASK                         0x00000000000000fc
+
+/*   SH_PTC_0_RID                                                       */
+/*   Description:  Region ID                                            */
+#define SH_PTC_0_RID_SHFT                        8
+#define SH_PTC_0_RID_MASK                        0x00000000ffffff00
+
+/*   SH_PTC_0_START                                                     */
+/*   Description:  Start                                                */
+#define SH_PTC_0_START_SHFT                      63
+#define SH_PTC_0_START_MASK                      0x8000000000000000
+
+/* ==================================================================== */
+/*                         Register "SH_PTC_1"                          */
+/*       Puge Translation Cache Message Configuration Information       */
+/* ==================================================================== */
+#define SH_PTC_1                                 0x00000001101a0080UL
+#define SH_PTC_1_MASK                            0x9ffffffffffff000
+#define SH_PTC_1_INIT                            0x0000000000000000
+
+/*   SH_PTC_1_VPN                                                       */
+/*   Description:  Virtual page number                                  */
+#define SH_PTC_1_VPN_SHFT                        12
+#define SH_PTC_1_VPN_MASK                        0x1ffffffffffff000
+
+/*   SH_PTC_1_START                                                     */
+/*   Description:  PTC_1 Start                                          */
+#define SH_PTC_1_START_SHFT                      63
+#define SH_PTC_1_START_MASK                      0x8000000000000000
+
+#endif /* _ASM_IA64_SN_SHUB_MMR_H */
diff --git a/include/asm-ia64/sn/shubio.h b/include/asm-ia64/sn/shubio.h
new file mode 100644 (file)
index 0000000..fbd880e
--- /dev/null
@@ -0,0 +1,3476 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#ifndef _ASM_IA64_SN_SHUBIO_H
+#define _ASM_IA64_SN_SHUBIO_H
+
+#define HUB_WIDGET_ID_MAX 0xf
+#define IIO_NUM_ITTES   7
+#define HUB_NUM_BIG_WINDOW      (IIO_NUM_ITTES - 1)
+
+#define    IIO_WID                   0x00400000    /* Crosstalk Widget Identification */
+                                                   /* This register is also accessible from
+                                                    * Crosstalk at address 0x0.  */
+#define    IIO_WSTAT                 0x00400008    /* Crosstalk Widget Status */
+#define    IIO_WCR                   0x00400020    /* Crosstalk Widget Control Register */
+#define    IIO_ILAPR                 0x00400100    /* IO Local Access Protection Register */
+#define    IIO_ILAPO                 0x00400108    /* IO Local Access Protection Override */
+#define    IIO_IOWA                  0x00400110    /* IO Outbound Widget Access */
+#define    IIO_IIWA                  0x00400118    /* IO Inbound Widget Access */
+#define    IIO_IIDEM                 0x00400120    /* IO Inbound Device Error Mask */
+#define    IIO_ILCSR                 0x00400128    /* IO LLP Control and Status Register */
+#define    IIO_ILLR                  0x00400130    /* IO LLP Log Register    */
+#define    IIO_IIDSR                 0x00400138    /* IO Interrupt Destination */
+
+#define    IIO_IGFX0                 0x00400140    /* IO Graphics Node-Widget Map 0 */
+#define    IIO_IGFX1                 0x00400148    /* IO Graphics Node-Widget Map 1 */
+
+#define    IIO_ISCR0                 0x00400150    /* IO Scratch Register 0 */
+#define    IIO_ISCR1                 0x00400158    /* IO Scratch Register 1 */
+
+#define    IIO_ITTE1                 0x00400160    /* IO Translation Table Entry 1 */
+#define    IIO_ITTE2                 0x00400168    /* IO Translation Table Entry 2 */
+#define    IIO_ITTE3                 0x00400170    /* IO Translation Table Entry 3 */
+#define    IIO_ITTE4                 0x00400178    /* IO Translation Table Entry 4 */
+#define    IIO_ITTE5                 0x00400180    /* IO Translation Table Entry 5 */
+#define    IIO_ITTE6                 0x00400188    /* IO Translation Table Entry 6 */
+#define    IIO_ITTE7                 0x00400190    /* IO Translation Table Entry 7 */
+
+#define    IIO_IPRB0                 0x00400198    /* IO PRB Entry 0         */
+#define    IIO_IPRB8                 0x004001A0    /* IO PRB Entry 8         */
+#define    IIO_IPRB9                 0x004001A8    /* IO PRB Entry 9         */
+#define    IIO_IPRBA                 0x004001B0    /* IO PRB Entry A         */
+#define    IIO_IPRBB                 0x004001B8    /* IO PRB Entry B         */
+#define    IIO_IPRBC                 0x004001C0    /* IO PRB Entry C         */
+#define    IIO_IPRBD                 0x004001C8    /* IO PRB Entry D         */
+#define    IIO_IPRBE                 0x004001D0    /* IO PRB Entry E         */
+#define    IIO_IPRBF                 0x004001D8    /* IO PRB Entry F         */
+
+#define    IIO_IXCC                  0x004001E0    /* IO Crosstalk Credit Count Timeout */
+#define    IIO_IMEM                  0x004001E8    /* IO Miscellaneous Error Mask */
+#define    IIO_IXTT                  0x004001F0    /* IO Crosstalk Timeout Threshold */
+#define    IIO_IECLR                 0x004001F8    /* IO Error Clear Register */
+#define    IIO_IBCR                  0x00400200    /* IO BTE Control Register */
+
+#define    IIO_IXSM                  0x00400208    /* IO Crosstalk Spurious Message */
+#define    IIO_IXSS                  0x00400210    /* IO Crosstalk Spurious Sideband */
+
+#define    IIO_ILCT                  0x00400218    /* IO LLP Channel Test    */
+
+#define    IIO_IIEPH1                0x00400220    /* IO Incoming Error Packet Header, Part 1 */
+#define    IIO_IIEPH2                0x00400228    /* IO Incoming Error Packet Header, Part 2 */
+
+
+#define    IIO_ISLAPR                0x00400230    /* IO SXB Local Access Protection Regster */
+#define    IIO_ISLAPO                0x00400238    /* IO SXB Local Access Protection Override */
+
+#define    IIO_IWI                   0x00400240    /* IO Wrapper Interrupt Register */
+#define    IIO_IWEL                  0x00400248    /* IO Wrapper Error Log Register */
+#define    IIO_IWC                   0x00400250    /* IO Wrapper Control Register */
+#define    IIO_IWS                   0x00400258    /* IO Wrapper Status Register */
+#define    IIO_IWEIM                 0x00400260    /* IO Wrapper Error Interrupt Masking Register */
+
+#define    IIO_IPCA                  0x00400300    /* IO PRB Counter Adjust */
+
+#define    IIO_IPRTE0_A              0x00400308    /* IO PIO Read Address Table Entry 0, Part A */
+#define    IIO_IPRTE1_A              0x00400310    /* IO PIO Read Address Table Entry 1, Part A */
+#define    IIO_IPRTE2_A              0x00400318    /* IO PIO Read Address Table Entry 2, Part A */
+#define    IIO_IPRTE3_A               0x00400320    /* IO PIO Read Address Table Entry 3, Part A */
+#define    IIO_IPRTE4_A               0x00400328    /* IO PIO Read Address Table Entry 4, Part A */
+#define    IIO_IPRTE5_A               0x00400330    /* IO PIO Read Address Table Entry 5, Part A */
+#define    IIO_IPRTE6_A               0x00400338    /* IO PIO Read Address Table Entry 6, Part A */
+#define    IIO_IPRTE7_A               0x00400340    /* IO PIO Read Address Table Entry 7, Part A */
+
+#define    IIO_IPRTE0_B              0x00400348    /* IO PIO Read Address Table Entry 0, Part B */
+#define    IIO_IPRTE1_B              0x00400350    /* IO PIO Read Address Table Entry 1, Part B */
+#define    IIO_IPRTE2_B              0x00400358    /* IO PIO Read Address Table Entry 2, Part B */
+#define    IIO_IPRTE3_B               0x00400360    /* IO PIO Read Address Table Entry 3, Part B */
+#define    IIO_IPRTE4_B               0x00400368    /* IO PIO Read Address Table Entry 4, Part B */
+#define    IIO_IPRTE5_B               0x00400370    /* IO PIO Read Address Table Entry 5, Part B */
+#define    IIO_IPRTE6_B               0x00400378    /* IO PIO Read Address Table Entry 6, Part B */
+#define    IIO_IPRTE7_B               0x00400380    /* IO PIO Read Address Table Entry 7, Part B */
+
+#define    IIO_IPDR                  0x00400388    /* IO PIO Deallocation Register */
+#define    IIO_ICDR                  0x00400390    /* IO CRB Entry Deallocation Register */
+#define    IIO_IFDR                  0x00400398    /* IO IOQ FIFO Depth Register */
+#define    IIO_IIAP                  0x004003A0    /* IO IIQ Arbitration Parameters */
+#define    IIO_ICMR                  0x004003A8    /* IO CRB Management Register */
+#define    IIO_ICCR                  0x004003B0    /* IO CRB Control Register */
+#define    IIO_ICTO                  0x004003B8    /* IO CRB Timeout         */
+#define    IIO_ICTP                  0x004003C0    /* IO CRB Timeout Prescalar */
+
+#define    IIO_ICRB0_A               0x00400400    /* IO CRB Entry 0_A       */
+#define    IIO_ICRB0_B               0x00400408    /* IO CRB Entry 0_B       */
+#define    IIO_ICRB0_C               0x00400410    /* IO CRB Entry 0_C       */
+#define    IIO_ICRB0_D               0x00400418    /* IO CRB Entry 0_D       */
+#define    IIO_ICRB0_E               0x00400420    /* IO CRB Entry 0_E       */
+
+#define    IIO_ICRB1_A               0x00400430    /* IO CRB Entry 1_A       */
+#define    IIO_ICRB1_B               0x00400438    /* IO CRB Entry 1_B       */
+#define    IIO_ICRB1_C               0x00400440    /* IO CRB Entry 1_C       */
+#define    IIO_ICRB1_D               0x00400448    /* IO CRB Entry 1_D       */
+#define    IIO_ICRB1_E               0x00400450    /* IO CRB Entry 1_E       */
+
+#define    IIO_ICRB2_A               0x00400460    /* IO CRB Entry 2_A       */
+#define    IIO_ICRB2_B               0x00400468    /* IO CRB Entry 2_B       */
+#define    IIO_ICRB2_C               0x00400470    /* IO CRB Entry 2_C       */
+#define    IIO_ICRB2_D               0x00400478    /* IO CRB Entry 2_D       */
+#define    IIO_ICRB2_E               0x00400480    /* IO CRB Entry 2_E       */
+
+#define    IIO_ICRB3_A               0x00400490    /* IO CRB Entry 3_A       */
+#define    IIO_ICRB3_B               0x00400498    /* IO CRB Entry 3_B       */
+#define    IIO_ICRB3_C               0x004004a0    /* IO CRB Entry 3_C       */
+#define    IIO_ICRB3_D               0x004004a8    /* IO CRB Entry 3_D       */
+#define    IIO_ICRB3_E               0x004004b0    /* IO CRB Entry 3_E       */
+
+#define    IIO_ICRB4_A               0x004004c0    /* IO CRB Entry 4_A       */
+#define    IIO_ICRB4_B               0x004004c8    /* IO CRB Entry 4_B       */
+#define    IIO_ICRB4_C               0x004004d0    /* IO CRB Entry 4_C       */
+#define    IIO_ICRB4_D               0x004004d8    /* IO CRB Entry 4_D       */
+#define    IIO_ICRB4_E               0x004004e0    /* IO CRB Entry 4_E       */
+
+#define    IIO_ICRB5_A               0x004004f0    /* IO CRB Entry 5_A       */
+#define    IIO_ICRB5_B               0x004004f8    /* IO CRB Entry 5_B       */
+#define    IIO_ICRB5_C               0x00400500    /* IO CRB Entry 5_C       */
+#define    IIO_ICRB5_D               0x00400508    /* IO CRB Entry 5_D       */
+#define    IIO_ICRB5_E               0x00400510    /* IO CRB Entry 5_E       */
+
+#define    IIO_ICRB6_A               0x00400520    /* IO CRB Entry 6_A       */
+#define    IIO_ICRB6_B               0x00400528    /* IO CRB Entry 6_B       */
+#define    IIO_ICRB6_C               0x00400530    /* IO CRB Entry 6_C       */
+#define    IIO_ICRB6_D               0x00400538    /* IO CRB Entry 6_D       */
+#define    IIO_ICRB6_E               0x00400540    /* IO CRB Entry 6_E       */
+
+#define    IIO_ICRB7_A               0x00400550    /* IO CRB Entry 7_A       */
+#define    IIO_ICRB7_B               0x00400558    /* IO CRB Entry 7_B       */
+#define    IIO_ICRB7_C               0x00400560    /* IO CRB Entry 7_C       */
+#define    IIO_ICRB7_D               0x00400568    /* IO CRB Entry 7_D       */
+#define    IIO_ICRB7_E               0x00400570    /* IO CRB Entry 7_E       */
+
+#define    IIO_ICRB8_A               0x00400580    /* IO CRB Entry 8_A       */
+#define    IIO_ICRB8_B               0x00400588    /* IO CRB Entry 8_B       */
+#define    IIO_ICRB8_C               0x00400590    /* IO CRB Entry 8_C       */
+#define    IIO_ICRB8_D               0x00400598    /* IO CRB Entry 8_D       */
+#define    IIO_ICRB8_E               0x004005a0    /* IO CRB Entry 8_E       */
+
+#define    IIO_ICRB9_A               0x004005b0    /* IO CRB Entry 9_A       */
+#define    IIO_ICRB9_B               0x004005b8    /* IO CRB Entry 9_B       */
+#define    IIO_ICRB9_C               0x004005c0    /* IO CRB Entry 9_C       */
+#define    IIO_ICRB9_D               0x004005c8    /* IO CRB Entry 9_D       */
+#define    IIO_ICRB9_E               0x004005d0    /* IO CRB Entry 9_E       */
+
+#define    IIO_ICRBA_A               0x004005e0    /* IO CRB Entry A_A       */
+#define    IIO_ICRBA_B               0x004005e8    /* IO CRB Entry A_B       */
+#define    IIO_ICRBA_C               0x004005f0    /* IO CRB Entry A_C       */
+#define    IIO_ICRBA_D               0x004005f8    /* IO CRB Entry A_D       */
+#define    IIO_ICRBA_E               0x00400600    /* IO CRB Entry A_E       */
+
+#define    IIO_ICRBB_A               0x00400610    /* IO CRB Entry B_A       */
+#define    IIO_ICRBB_B               0x00400618    /* IO CRB Entry B_B       */
+#define    IIO_ICRBB_C               0x00400620    /* IO CRB Entry B_C       */
+#define    IIO_ICRBB_D               0x00400628    /* IO CRB Entry B_D       */
+#define    IIO_ICRBB_E               0x00400630    /* IO CRB Entry B_E       */
+
+#define    IIO_ICRBC_A               0x00400640    /* IO CRB Entry C_A       */
+#define    IIO_ICRBC_B               0x00400648    /* IO CRB Entry C_B       */
+#define    IIO_ICRBC_C               0x00400650    /* IO CRB Entry C_C       */
+#define    IIO_ICRBC_D               0x00400658    /* IO CRB Entry C_D       */
+#define    IIO_ICRBC_E               0x00400660    /* IO CRB Entry C_E       */
+
+#define    IIO_ICRBD_A               0x00400670    /* IO CRB Entry D_A       */
+#define    IIO_ICRBD_B               0x00400678    /* IO CRB Entry D_B       */
+#define    IIO_ICRBD_C               0x00400680    /* IO CRB Entry D_C       */
+#define    IIO_ICRBD_D               0x00400688    /* IO CRB Entry D_D       */
+#define    IIO_ICRBD_E               0x00400690    /* IO CRB Entry D_E       */
+
+#define    IIO_ICRBE_A               0x004006a0    /* IO CRB Entry E_A       */
+#define    IIO_ICRBE_B               0x004006a8    /* IO CRB Entry E_B       */
+#define    IIO_ICRBE_C               0x004006b0    /* IO CRB Entry E_C       */
+#define    IIO_ICRBE_D               0x004006b8    /* IO CRB Entry E_D       */
+#define    IIO_ICRBE_E               0x004006c0    /* IO CRB Entry E_E       */
+
+#define    IIO_ICSML                 0x00400700    /* IO CRB Spurious Message Low */
+#define    IIO_ICSMM                 0x00400708    /* IO CRB Spurious Message Middle */
+#define    IIO_ICSMH                 0x00400710    /* IO CRB Spurious Message High */
+
+#define    IIO_IDBSS                 0x00400718    /* IO Debug Submenu Select */
+
+#define    IIO_IBLS0                 0x00410000    /* IO BTE Length Status 0 */
+#define    IIO_IBSA0                 0x00410008    /* IO BTE Source Address 0 */
+#define    IIO_IBDA0                 0x00410010    /* IO BTE Destination Address 0 */
+#define    IIO_IBCT0                 0x00410018    /* IO BTE Control Terminate 0 */
+#define    IIO_IBNA0                 0x00410020    /* IO BTE Notification Address 0 */
+#define    IIO_IBIA0                 0x00410028    /* IO BTE Interrupt Address 0 */
+#define    IIO_IBLS1                 0x00420000    /* IO BTE Length Status 1 */
+#define    IIO_IBSA1                 0x00420008    /* IO BTE Source Address 1 */
+#define    IIO_IBDA1                 0x00420010    /* IO BTE Destination Address 1 */
+#define    IIO_IBCT1                 0x00420018    /* IO BTE Control Terminate 1 */
+#define    IIO_IBNA1                 0x00420020    /* IO BTE Notification Address 1 */
+#define    IIO_IBIA1                 0x00420028    /* IO BTE Interrupt Address 1 */
+
+#define    IIO_IPCR                  0x00430000    /* IO Performance Control */
+#define    IIO_IPPR                  0x00430008    /* IO Performance Profiling */
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  This register echoes some information from the         *
+ * LB_REV_ID register. It is available through Crosstalk as described   *
+ * above. The REV_NUM and MFG_NUM fields receive their values from      *
+ * the REVISION and MANUFACTURER fields in the LB_REV_ID register.      *
+ * The PART_NUM field's value is the Crosstalk device ID number that    *
+ * Steve Miller assigned to the SHub chip.                              *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_wid_u {
+       uint64_t        ii_wid_regval;
+       struct  {
+               uint64_t        w_rsvd_1                  :      1;
+               uint64_t        w_mfg_num                 :     11;
+               uint64_t        w_part_num                :     16;
+               uint64_t        w_rev_num                 :      4;
+               uint64_t        w_rsvd                    :     32;
+       } ii_wid_fld_s;
+} ii_wid_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  The fields in this register are set upon detection of an error      *
+ * and cleared by various mechanisms, as explained in the               *
+ * description.                                                         *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_wstat_u {
+       uint64_t        ii_wstat_regval;
+       struct  {
+               uint64_t        w_pending                 :      4;
+               uint64_t        w_xt_crd_to               :      1;
+               uint64_t        w_xt_tail_to              :      1;
+               uint64_t        w_rsvd_3                  :      3;
+               uint64_t       w_tx_mx_rty               :      1;
+               uint64_t        w_rsvd_2                  :      6;
+               uint64_t        w_llp_tx_cnt              :      8;
+               uint64_t        w_rsvd_1                  :      8;
+               uint64_t        w_crazy                   :      1;
+               uint64_t        w_rsvd                    :     31;
+       } ii_wstat_fld_s;
+} ii_wstat_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  This is a read-write enabled register. It controls     *
+ * various aspects of the Crosstalk flow control.                       *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_wcr_u {
+       uint64_t        ii_wcr_regval;
+       struct  {
+               uint64_t        w_wid                     :      4;
+               uint64_t        w_tag                     :      1;
+               uint64_t        w_rsvd_1                  :      8;
+               uint64_t        w_dst_crd                 :      3;
+               uint64_t        w_f_bad_pkt               :      1;
+               uint64_t        w_dir_con                 :      1;
+               uint64_t        w_e_thresh                :      5;
+               uint64_t        w_rsvd                    :     41;
+       } ii_wcr_fld_s;
+} ii_wcr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  This register's value is a bit vector that guards      *
+ * access to local registers within the II as well as to external       *
+ * Crosstalk widgets. Each bit in the register corresponds to a         *
+ * particular region in the system; a region consists of one, two or    *
+ * four nodes (depending on the value of the REGION_SIZE field in the   *
+ * LB_REV_ID register, which is documented in Section 8.3.1.1). The     *
+ * protection provided by this register applies to PIO read             *
+ * operations as well as PIO write operations. The II will perform a    *
+ * PIO read or write request only if the bit for the requestor's        *
+ * region is set; otherwise, the II will not perform the requested      *
+ * operation and will return an error response. When a PIO read or      *
+ * write request targets an external Crosstalk widget, then not only    *
+ * must the bit for the requestor's region be set in the ILAPR, but     *
+ * also the target widget's bit in the IOWA register must be set in     *
+ * order for the II to perform the requested operation; otherwise,      *
+ * the II will return an error response. Hence, the protection          *
+ * provided by the IOWA register supplements the protection provided    *
+ * by the ILAPR for requests that target external Crosstalk widgets.    *
+ * This register itself can be accessed only by the nodes whose         *
+ * region ID bits are enabled in this same register. It can also be     *
+ * accessed through the IAlias space by the local processors.           *
+ * The reset value of this register allows access by all nodes.         *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ilapr_u {
+       uint64_t        ii_ilapr_regval;
+       struct  {
+               uint64_t        i_region                  :     64;
+       } ii_ilapr_fld_s;
+} ii_ilapr_u_t;
+
+
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  A write to this register of the 64-bit value           *
+ * "SGIrules" in ASCII, will cause the bit in the ILAPR register        *
+ * corresponding to the region of the requestor to be set (allow        *
+ * access). A write of any other value will be ignored. Access          *
+ * protection for this register is "SGIrules".                          *
+ * This register can also be accessed through the IAlias space.         *
+ * However, this access will not change the access permissions in the   *
+ * ILAPR.                                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ilapo_u {
+       uint64_t        ii_ilapo_regval;
+       struct  {
+               uint64_t        i_io_ovrride            :       64;
+       } ii_ilapo_fld_s;
+} ii_ilapo_u_t;
+
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register qualifies all the PIO and Graphics writes launched    *
+ * from the SHUB towards a widget.                                      *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iowa_u {
+       uint64_t        ii_iowa_regval;
+       struct  {
+               uint64_t        i_w0_oac                  :      1;
+               uint64_t        i_rsvd_1                  :      7;
+                uint64_t       i_wx_oac                  :      8;
+               uint64_t        i_rsvd                    :     48;
+       } ii_iowa_fld_s;
+} ii_iowa_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  This register qualifies all the requests launched      *
+ * from a widget towards the Shub. This register is intended to be      *
+ * used by software in case of misbehaving widgets.                     *
+ *                                                                      *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iiwa_u {
+       uint64_t        ii_iiwa_regval;
+       struct  {
+               uint64_t        i_w0_iac                  :      1;
+               uint64_t        i_rsvd_1                  :      7;
+               uint64_t        i_wx_iac                  :      8;
+               uint64_t        i_rsvd                    :     48;
+       } ii_iiwa_fld_s;
+} ii_iiwa_u_t;
+
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  This register qualifies all the operations launched    *
+ * from a widget towards the SHub. It allows individual access          *
+ * control for up to 8 devices per widget. A device refers to           *
+ * individual DMA master hosted by a widget.                            *
+ * The bits in each field of this register are cleared by the Shub      *
+ * upon detection of an error which requires the device to be           *
+ * disabled. These fields assume that 0=TNUM=7 (i.e., Bridge-centric    *
+ * Crosstalk). Whether or not a device has access rights to this        *
+ * Shub is determined by an AND of the device enable bit in the         *
+ * appropriate field of this register and the corresponding bit in      *
+ * the Wx_IAC field (for the widget which this device belongs to).      *
+ * The bits in this field are set by writing a 1 to them. Incoming      *
+ * replies from Crosstalk are not subject to this access control        *
+ * mechanism.                                                           *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iidem_u {
+       uint64_t        ii_iidem_regval;
+       struct  {
+               uint64_t        i_w8_dxs                  :      8;
+               uint64_t        i_w9_dxs                  :      8;
+               uint64_t        i_wa_dxs                  :      8;
+               uint64_t        i_wb_dxs                  :      8;
+               uint64_t        i_wc_dxs                  :      8;
+               uint64_t        i_wd_dxs                  :      8;
+               uint64_t        i_we_dxs                  :      8;
+               uint64_t        i_wf_dxs                  :      8;
+       } ii_iidem_fld_s;
+} ii_iidem_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register contains the various programmable fields necessary    *
+ * for controlling and observing the LLP signals.                       *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ilcsr_u {
+       uint64_t        ii_ilcsr_regval;
+       struct  {
+               uint64_t        i_nullto                  :      6;
+               uint64_t        i_rsvd_4                  :      2;
+               uint64_t        i_wrmrst                  :      1;
+               uint64_t        i_rsvd_3                  :      1;
+               uint64_t        i_llp_en                  :      1;
+               uint64_t        i_bm8                     :      1;
+               uint64_t        i_llp_stat                :      2;
+               uint64_t        i_remote_power            :      1;
+               uint64_t        i_rsvd_2                  :      1;
+               uint64_t        i_maxrtry                 :     10;
+               uint64_t        i_d_avail_sel             :      2;
+               uint64_t        i_rsvd_1                  :      4;
+               uint64_t        i_maxbrst                 :     10;
+                uint64_t       i_rsvd                    :     22;
+
+       } ii_ilcsr_fld_s;
+} ii_ilcsr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This is simply a status registers that monitors the LLP error       *
+ * rate.                                                                *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_illr_u {
+       uint64_t        ii_illr_regval;
+       struct  {
+               uint64_t        i_sn_cnt                  :     16;
+               uint64_t        i_cb_cnt                  :     16;
+               uint64_t        i_rsvd                    :     32;
+       } ii_illr_fld_s;
+} ii_illr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  All II-detected non-BTE error interrupts are           *
+ * specified via this register.                                         *
+ * NOTE: The PI interrupt register address is hardcoded in the II. If   *
+ * PI_ID==0, then the II sends an interrupt request (Duplonet PWRI      *
+ * packet) to address offset 0x0180_0090 within the local register      *
+ * address space of PI0 on the node specified by the NODE field. If     *
+ * PI_ID==1, then the II sends the interrupt request to address         *
+ * offset 0x01A0_0090 within the local register address space of PI1    *
+ * on the node specified by the NODE field.                             *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iidsr_u {
+       uint64_t        ii_iidsr_regval;
+       struct  {
+               uint64_t        i_level                   :      8;
+               uint64_t        i_pi_id                   :      1;
+               uint64_t        i_node                    :     11;
+               uint64_t       i_rsvd_3                  :      4;
+               uint64_t        i_enable                  :      1;
+               uint64_t        i_rsvd_2                  :      3;
+               uint64_t        i_int_sent                :      2;
+               uint64_t       i_rsvd_1                  :      2;
+               uint64_t        i_pi0_forward_int         :      1;
+               uint64_t        i_pi1_forward_int         :      1;
+               uint64_t        i_rsvd                    :     30;
+       } ii_iidsr_fld_s;
+} ii_iidsr_u_t;
+
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are two instances of this register. This register is used     *
+ * for matching up the incoming responses from the graphics widget to   *
+ * the processor that initiated the graphics operation. The             *
+ * write-responses are converted to graphics credits and returned to    *
+ * the processor so that the processor interface can manage the flow    *
+ * control.                                                             *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_igfx0_u {
+       uint64_t        ii_igfx0_regval;
+       struct  {
+               uint64_t        i_w_num                   :      4;
+               uint64_t       i_pi_id                   :      1;
+               uint64_t        i_n_num                   :     12;
+               uint64_t       i_p_num                   :      1;
+               uint64_t       i_rsvd                    :     46;
+       } ii_igfx0_fld_s;
+} ii_igfx0_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are two instances of this register. This register is used     *
+ * for matching up the incoming responses from the graphics widget to   *
+ * the processor that initiated the graphics operation. The             *
+ * write-responses are converted to graphics credits and returned to    *
+ * the processor so that the processor interface can manage the flow    *
+ * control.                                                             *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_igfx1_u {
+       uint64_t        ii_igfx1_regval;
+       struct  {
+               uint64_t        i_w_num                   :      4;
+               uint64_t       i_pi_id                   :      1;
+               uint64_t        i_n_num                   :     12;
+               uint64_t       i_p_num                   :      1;
+               uint64_t       i_rsvd                    :     46;
+       } ii_igfx1_fld_s;
+} ii_igfx1_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are two instances of this registers. These registers are      *
+ * used as scratch registers for software use.                          *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iscr0_u {
+       uint64_t        ii_iscr0_regval;
+       struct  {
+               uint64_t        i_scratch                 :     64;
+       } ii_iscr0_fld_s;
+} ii_iscr0_u_t;
+
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are two instances of this registers. These registers are      *
+ * used as scratch registers for software use.                          *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iscr1_u {
+       uint64_t        ii_iscr1_regval;
+       struct  {
+               uint64_t        i_scratch                 :     64;
+       } ii_iscr1_fld_s;
+} ii_iscr1_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are seven instances of translation table entry   *
+ * registers. Each register maps a Shub Big Window to a 48-bit          *
+ * address on Crosstalk.                                                *
+ * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window      *
+ * number) are used to select one of these 7 registers. The Widget      *
+ * number field is then derived from the W_NUM field for synthesizing   *
+ * a Crosstalk packet. The 5 bits of OFFSET are concatenated with       *
+ * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34]      *
+ * are padded with zeros. Although the maximum Crosstalk space          *
+ * addressable by the SHub is thus the lower 16 GBytes per widget       * 
+ * (M-mode), however only <SUP >7</SUP>/<SUB >32nds</SUB> of this       *
+ * space can be accessed.                                               *
+ * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big         *
+ * Window number) are used to select one of these 7 registers. The      *
+ * Widget number field is then derived from the W_NUM field for         *
+ * synthesizing a Crosstalk packet. The 5 bits of OFFSET are            *
+ * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP      *
+ * field is used as Crosstalk[47], and remainder of the Crosstalk       *
+ * address bits (Crosstalk[46:34]) are always zero. While the maximum   *
+ * Crosstalk space addressable by the Shub is thus the lower            *
+ * 8-GBytes per widget (N-mode), only <SUP >7</SUP>/<SUB >32nds</SUB>   *
+ * of this space can be accessed.                                       *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_itte1_u {
+       uint64_t        ii_itte1_regval;
+       struct  {
+               uint64_t        i_offset                  :      5;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_w_num                   :      4;
+               uint64_t        i_iosp                    :      1;
+               uint64_t        i_rsvd                    :     51;
+       } ii_itte1_fld_s;
+} ii_itte1_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are seven instances of translation table entry   *
+ * registers. Each register maps a Shub Big Window to a 48-bit          *
+ * address on Crosstalk.                                                *
+ * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window      *
+ * number) are used to select one of these 7 registers. The Widget      *
+ * number field is then derived from the W_NUM field for synthesizing   *
+ * a Crosstalk packet. The 5 bits of OFFSET are concatenated with       *
+ * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34]      *
+ * are padded with zeros. Although the maximum Crosstalk space          *
+ * addressable by the Shub is thus the lower 16 GBytes per widget       *
+ * (M-mode), however only <SUP >7</SUP>/<SUB >32nds</SUB> of this       *
+ * space can be accessed.                                               *
+ * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big         *
+ * Window number) are used to select one of these 7 registers. The      *
+ * Widget number field is then derived from the W_NUM field for         *
+ * synthesizing a Crosstalk packet. The 5 bits of OFFSET are            *
+ * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP      *
+ * field is used as Crosstalk[47], and remainder of the Crosstalk       *
+ * address bits (Crosstalk[46:34]) are always zero. While the maximum   *
+ * Crosstalk space addressable by the Shub is thus the lower            *
+ * 8-GBytes per widget (N-mode), only <SUP >7</SUP>/<SUB >32nds</SUB>   *
+ * of this space can be accessed.                                       *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_itte2_u {
+       uint64_t        ii_itte2_regval;
+       struct  {
+               uint64_t        i_offset                  :      5;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_w_num                   :      4;
+               uint64_t        i_iosp                    :      1;
+               uint64_t       i_rsvd                    :     51;
+       } ii_itte2_fld_s;
+} ii_itte2_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are seven instances of translation table entry   *
+ * registers. Each register maps a Shub Big Window to a 48-bit          *
+ * address on Crosstalk.                                                *
+ * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window      *
+ * number) are used to select one of these 7 registers. The Widget      *
+ * number field is then derived from the W_NUM field for synthesizing   *
+ * a Crosstalk packet. The 5 bits of OFFSET are concatenated with       *
+ * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34]      *
+ * are padded with zeros. Although the maximum Crosstalk space          *
+ * addressable by the Shub is thus the lower 16 GBytes per widget       *
+ * (M-mode), however only <SUP >7</SUP>/<SUB >32nds</SUB> of this       *
+ * space can be accessed.                                               *
+ * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big         *
+ * Window number) are used to select one of these 7 registers. The      *
+ * Widget number field is then derived from the W_NUM field for         *
+ * synthesizing a Crosstalk packet. The 5 bits of OFFSET are            *
+ * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP      *
+ * field is used as Crosstalk[47], and remainder of the Crosstalk       *
+ * address bits (Crosstalk[46:34]) are always zero. While the maximum   *
+ * Crosstalk space addressable by the SHub is thus the lower            *
+ * 8-GBytes per widget (N-mode), only <SUP >7</SUP>/<SUB >32nds</SUB>   *
+ * of this space can be accessed.                                       *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_itte3_u {
+       uint64_t        ii_itte3_regval;
+       struct  {
+               uint64_t        i_offset                  :      5;
+               uint64_t       i_rsvd_1                  :      3;
+               uint64_t       i_w_num                   :      4;
+               uint64_t       i_iosp                    :      1;
+               uint64_t       i_rsvd                    :     51;
+       } ii_itte3_fld_s;
+} ii_itte3_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are seven instances of translation table entry   *
+ * registers. Each register maps a SHub Big Window to a 48-bit          *
+ * address on Crosstalk.                                                *
+ * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window      *
+ * number) are used to select one of these 7 registers. The Widget      *
+ * number field is then derived from the W_NUM field for synthesizing   *
+ * a Crosstalk packet. The 5 bits of OFFSET are concatenated with       *
+ * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34]      *
+ * are padded with zeros. Although the maximum Crosstalk space          *
+ * addressable by the SHub is thus the lower 16 GBytes per widget       *
+ * (M-mode), however only <SUP >7</SUP>/<SUB >32nds</SUB> of this       *
+ * space can be accessed.                                               *
+ * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big         *
+ * Window number) are used to select one of these 7 registers. The      *
+ * Widget number field is then derived from the W_NUM field for         *
+ * synthesizing a Crosstalk packet. The 5 bits of OFFSET are            *
+ * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP      *
+ * field is used as Crosstalk[47], and remainder of the Crosstalk       *
+ * address bits (Crosstalk[46:34]) are always zero. While the maximum   *
+ * Crosstalk space addressable by the SHub is thus the lower            *
+ * 8-GBytes per widget (N-mode), only <SUP >7</SUP>/<SUB >32nds</SUB>   *
+ * of this space can be accessed.                                       *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_itte4_u {
+       uint64_t        ii_itte4_regval;
+       struct  {
+               uint64_t        i_offset                  :      5;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t       i_w_num                   :      4;
+               uint64_t       i_iosp                    :      1;
+               uint64_t       i_rsvd                    :     51;
+       } ii_itte4_fld_s;
+} ii_itte4_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are seven instances of translation table entry   *
+ * registers. Each register maps a SHub Big Window to a 48-bit          *
+ * address on Crosstalk.                                                *
+ * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window      *
+ * number) are used to select one of these 7 registers. The Widget      *
+ * number field is then derived from the W_NUM field for synthesizing   *
+ * a Crosstalk packet. The 5 bits of OFFSET are concatenated with       *
+ * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34]      *
+ * are padded with zeros. Although the maximum Crosstalk space          *
+ * addressable by the Shub is thus the lower 16 GBytes per widget       *
+ * (M-mode), however only <SUP >7</SUP>/<SUB >32nds</SUB> of this       *
+ * space can be accessed.                                               *
+ * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big         *
+ * Window number) are used to select one of these 7 registers. The      *
+ * Widget number field is then derived from the W_NUM field for         *
+ * synthesizing a Crosstalk packet. The 5 bits of OFFSET are            *
+ * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP      *
+ * field is used as Crosstalk[47], and remainder of the Crosstalk       *
+ * address bits (Crosstalk[46:34]) are always zero. While the maximum   *
+ * Crosstalk space addressable by the Shub is thus the lower            *
+ * 8-GBytes per widget (N-mode), only <SUP >7</SUP>/<SUB >32nds</SUB>   *
+ * of this space can be accessed.                                       *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_itte5_u {
+       uint64_t        ii_itte5_regval;
+       struct  {
+               uint64_t        i_offset                  :      5;
+               uint64_t       i_rsvd_1                  :      3;
+               uint64_t       i_w_num                   :      4;
+               uint64_t       i_iosp                    :      1;
+               uint64_t       i_rsvd                    :     51;
+       } ii_itte5_fld_s;
+} ii_itte5_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are seven instances of translation table entry   *
+ * registers. Each register maps a Shub Big Window to a 48-bit          *
+ * address on Crosstalk.                                                *
+ * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window      *
+ * number) are used to select one of these 7 registers. The Widget      *
+ * number field is then derived from the W_NUM field for synthesizing   *
+ * a Crosstalk packet. The 5 bits of OFFSET are concatenated with       *
+ * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34]      *
+ * are padded with zeros. Although the maximum Crosstalk space          *
+ * addressable by the Shub is thus the lower 16 GBytes per widget       *
+ * (M-mode), however only <SUP >7</SUP>/<SUB >32nds</SUB> of this       *
+ * space can be accessed.                                               *
+ * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big         *
+ * Window number) are used to select one of these 7 registers. The      *
+ * Widget number field is then derived from the W_NUM field for         *
+ * synthesizing a Crosstalk packet. The 5 bits of OFFSET are            *
+ * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP      *
+ * field is used as Crosstalk[47], and remainder of the Crosstalk       *
+ * address bits (Crosstalk[46:34]) are always zero. While the maximum   *
+ * Crosstalk space addressable by the Shub is thus the lower            *
+ * 8-GBytes per widget (N-mode), only <SUP >7</SUP>/<SUB >32nds</SUB>   *
+ * of this space can be accessed.                                       *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_itte6_u {
+       uint64_t        ii_itte6_regval;
+       struct  {
+               uint64_t        i_offset                  :      5;
+               uint64_t       i_rsvd_1                  :      3;
+               uint64_t       i_w_num                   :      4;
+               uint64_t       i_iosp                    :      1;
+               uint64_t       i_rsvd                    :     51;
+       } ii_itte6_fld_s;
+} ii_itte6_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are seven instances of translation table entry   *
+ * registers. Each register maps a Shub Big Window to a 48-bit          *
+ * address on Crosstalk.                                                *
+ * For M-mode (128 nodes, 8 GBytes/node), SysAD[31:29] (Big Window      *
+ * number) are used to select one of these 7 registers. The Widget      *
+ * number field is then derived from the W_NUM field for synthesizing   *
+ * a Crosstalk packet. The 5 bits of OFFSET are concatenated with       *
+ * SysAD[28:0] to form Crosstalk[33:0]. The upper Crosstalk[47:34]      *
+ * are padded with zeros. Although the maximum Crosstalk space          *
+ * addressable by the Shub is thus the lower 16 GBytes per widget       *
+ * (M-mode), however only <SUP >7</SUP>/<SUB >32nds</SUB> of this       *
+ * space can be accessed.                                               *
+ * For the N-mode (256 nodes, 4 GBytes/node), SysAD[30:28] (Big         *
+ * Window number) are used to select one of these 7 registers. The      *
+ * Widget number field is then derived from the W_NUM field for         *
+ * synthesizing a Crosstalk packet. The 5 bits of OFFSET are            *
+ * concatenated with SysAD[27:0] to form Crosstalk[33:0]. The IOSP      *
+ * field is used as Crosstalk[47], and remainder of the Crosstalk       *
+ * address bits (Crosstalk[46:34]) are always zero. While the maximum   *
+ * Crosstalk space addressable by the SHub is thus the lower            *
+ * 8-GBytes per widget (N-mode), only <SUP >7</SUP>/<SUB >32nds</SUB>   *
+ * of this space can be accessed.                                       *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_itte7_u {
+       uint64_t        ii_itte7_regval;
+       struct  {
+               uint64_t        i_offset                  :      5;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t       i_w_num                   :      4;
+               uint64_t       i_iosp                    :      1;
+               uint64_t       i_rsvd                    :     51;
+       } ii_itte7_fld_s;
+} ii_itte7_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 9 instances of this register, one per        *
+ * actual widget in this implementation of SHub and Crossbow.           *
+ * Note: Crossbow only has ports for Widgets 8 through F, widget 0      *
+ * refers to Crossbow's internal space.                                 *
+ * This register contains the state elements per widget that are        *
+ * necessary to manage the PIO flow control on Crosstalk and on the     *
+ * Router Network. See the PIO Flow Control chapter for a complete      *
+ * description of this register                                         *
+ * The SPUR_WR bit requires some explanation. When this register is     *
+ * written, the new value of the C field is captured in an internal     *
+ * register so the hardware can remember what the programmer wrote      *
+ * into the credit counter. The SPUR_WR bit sets whenever the C field   *
+ * increments above this stored value, which indicates that there       *
+ * have been more responses received than requests sent. The SPUR_WR    *
+ * bit cannot be cleared until a value is written to the IPRBx          *
+ * register; the write will correct the C field and capture its new     *
+ * value in the internal register. Even if IECLR[E_PRB_x] is set, the   *
+ * SPUR_WR bit will persist if IPRBx hasn't yet been written.           *
+ * .                                                                    *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprb0_u {
+       uint64_t        ii_iprb0_regval;
+       struct  {
+               uint64_t        i_c                       :      8;
+               uint64_t        i_na                      :     14;
+               uint64_t       i_rsvd_2                  :      2;
+               uint64_t        i_nb                      :     14;
+               uint64_t        i_rsvd_1                  :      2;
+               uint64_t        i_m                       :      2;
+               uint64_t        i_f                       :      1;
+               uint64_t        i_of_cnt                  :      5;
+               uint64_t        i_error                   :      1;
+               uint64_t        i_rd_to                   :      1;
+               uint64_t        i_spur_wr                 :      1;
+               uint64_t        i_spur_rd                 :      1;
+               uint64_t        i_rsvd                    :     11;
+               uint64_t        i_mult_err                :      1;
+       } ii_iprb0_fld_s;
+} ii_iprb0_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 9 instances of this register, one per        *
+ * actual widget in this implementation of SHub and Crossbow.           *
+ * Note: Crossbow only has ports for Widgets 8 through F, widget 0      *
+ * refers to Crossbow's internal space.                                 *
+ * This register contains the state elements per widget that are        *
+ * necessary to manage the PIO flow control on Crosstalk and on the     *
+ * Router Network. See the PIO Flow Control chapter for a complete      *
+ * description of this register                                         *
+ * The SPUR_WR bit requires some explanation. When this register is     *
+ * written, the new value of the C field is captured in an internal     *
+ * register so the hardware can remember what the programmer wrote      *
+ * into the credit counter. The SPUR_WR bit sets whenever the C field   *
+ * increments above this stored value, which indicates that there       *
+ * have been more responses received than requests sent. The SPUR_WR    *
+ * bit cannot be cleared until a value is written to the IPRBx          *
+ * register; the write will correct the C field and capture its new     *
+ * value in the internal register. Even if IECLR[E_PRB_x] is set, the   *
+ * SPUR_WR bit will persist if IPRBx hasn't yet been written.           *
+ * .                                                                    *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprb8_u {
+       uint64_t        ii_iprb8_regval;
+       struct  {
+               uint64_t        i_c                       :      8;
+               uint64_t        i_na                      :     14;
+               uint64_t       i_rsvd_2                  :      2;
+               uint64_t        i_nb                      :     14;
+               uint64_t       i_rsvd_1                  :      2;
+               uint64_t       i_m                       :      2;
+               uint64_t       i_f                       :      1;
+               uint64_t       i_of_cnt                  :      5;
+               uint64_t       i_error                   :      1;
+               uint64_t       i_rd_to                   :      1;
+               uint64_t       i_spur_wr                 :      1;
+               uint64_t        i_spur_rd                 :      1;
+               uint64_t       i_rsvd                    :     11;
+               uint64_t        i_mult_err                :      1;
+       } ii_iprb8_fld_s;
+} ii_iprb8_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 9 instances of this register, one per        *
+ * actual widget in this implementation of SHub and Crossbow.           *
+ * Note: Crossbow only has ports for Widgets 8 through F, widget 0      *
+ * refers to Crossbow's internal space.                                 *
+ * This register contains the state elements per widget that are        *
+ * necessary to manage the PIO flow control on Crosstalk and on the     *
+ * Router Network. See the PIO Flow Control chapter for a complete      *
+ * description of this register                                         *
+ * The SPUR_WR bit requires some explanation. When this register is     *
+ * written, the new value of the C field is captured in an internal     *
+ * register so the hardware can remember what the programmer wrote      *
+ * into the credit counter. The SPUR_WR bit sets whenever the C field   *
+ * increments above this stored value, which indicates that there       *
+ * have been more responses received than requests sent. The SPUR_WR    *
+ * bit cannot be cleared until a value is written to the IPRBx          *
+ * register; the write will correct the C field and capture its new     *
+ * value in the internal register. Even if IECLR[E_PRB_x] is set, the   *
+ * SPUR_WR bit will persist if IPRBx hasn't yet been written.           *
+ * .                                                                    *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprb9_u {
+       uint64_t        ii_iprb9_regval;
+       struct  {
+               uint64_t        i_c                       :      8;
+               uint64_t        i_na                      :     14;
+               uint64_t        i_rsvd_2                  :      2;
+               uint64_t        i_nb                      :     14;
+               uint64_t        i_rsvd_1                  :      2;
+               uint64_t        i_m                       :      2;
+               uint64_t        i_f                       :      1;
+               uint64_t        i_of_cnt                  :      5;
+               uint64_t        i_error                   :      1;
+               uint64_t        i_rd_to                   :      1;
+               uint64_t        i_spur_wr                 :      1;
+               uint64_t        i_spur_rd                 :      1;
+               uint64_t        i_rsvd                    :     11;
+               uint64_t        i_mult_err                :      1;
+       } ii_iprb9_fld_s;
+} ii_iprb9_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 9 instances of this register, one per        *
+ * actual widget in this implementation of SHub and Crossbow.        *
+ * Note: Crossbow only has ports for Widgets 8 through F, widget 0      *
+ * refers to Crossbow's internal space.                                 *
+ * This register contains the state elements per widget that are        *
+ * necessary to manage the PIO flow control on Crosstalk and on the     *
+ * Router Network. See the PIO Flow Control chapter for a complete      *
+ * description of this register                                         *
+ * The SPUR_WR bit requires some explanation. When this register is     *
+ * written, the new value of the C field is captured in an internal     *
+ * register so the hardware can remember what the programmer wrote      *
+ * into the credit counter. The SPUR_WR bit sets whenever the C field   *
+ * increments above this stored value, which indicates that there       *
+ * have been more responses received than requests sent. The SPUR_WR    *
+ * bit cannot be cleared until a value is written to the IPRBx          *
+ * register; the write will correct the C field and capture its new     *
+ * value in the internal register. Even if IECLR[E_PRB_x] is set, the   *
+ * SPUR_WR bit will persist if IPRBx hasn't yet been written.           *
+ *                                                                      *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprba_u {
+       uint64_t        ii_iprba_regval;
+       struct  {
+               uint64_t        i_c                       :      8;
+               uint64_t        i_na                      :     14;
+               uint64_t       i_rsvd_2                  :      2;
+               uint64_t        i_nb                      :     14;
+               uint64_t        i_rsvd_1                  :      2;
+               uint64_t        i_m                       :      2;
+               uint64_t        i_f                       :      1;
+               uint64_t        i_of_cnt                  :      5;
+               uint64_t        i_error                   :      1;
+               uint64_t        i_rd_to                   :      1;
+               uint64_t        i_spur_wr                 :      1;
+               uint64_t        i_spur_rd                 :      1;
+               uint64_t        i_rsvd                    :     11;
+               uint64_t        i_mult_err                :      1;
+       } ii_iprba_fld_s;
+} ii_iprba_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 9 instances of this register, one per        *
+ * actual widget in this implementation of SHub and Crossbow.           *
+ * Note: Crossbow only has ports for Widgets 8 through F, widget 0      *
+ * refers to Crossbow's internal space.                                 *
+ * This register contains the state elements per widget that are        *
+ * necessary to manage the PIO flow control on Crosstalk and on the     *
+ * Router Network. See the PIO Flow Control chapter for a complete      *
+ * description of this register                                         *
+ * The SPUR_WR bit requires some explanation. When this register is     *
+ * written, the new value of the C field is captured in an internal     *
+ * register so the hardware can remember what the programmer wrote      *
+ * into the credit counter. The SPUR_WR bit sets whenever the C field   *
+ * increments above this stored value, which indicates that there       *
+ * have been more responses received than requests sent. The SPUR_WR    *
+ * bit cannot be cleared until a value is written to the IPRBx          *
+ * register; the write will correct the C field and capture its new     *
+ * value in the internal register. Even if IECLR[E_PRB_x] is set, the   *
+ * SPUR_WR bit will persist if IPRBx hasn't yet been written.           *
+ * .                                                                    *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprbb_u {
+       uint64_t        ii_iprbb_regval;
+       struct  {
+               uint64_t        i_c                       :      8;
+               uint64_t        i_na                      :     14;
+               uint64_t        i_rsvd_2                  :      2;
+               uint64_t        i_nb                      :     14;
+               uint64_t        i_rsvd_1                  :      2;
+               uint64_t        i_m                       :      2;
+               uint64_t        i_f                       :      1;
+               uint64_t        i_of_cnt                  :      5;
+               uint64_t        i_error                   :      1;
+               uint64_t        i_rd_to                   :      1;
+               uint64_t        i_spur_wr                 :      1;
+               uint64_t        i_spur_rd                 :      1;
+               uint64_t        i_rsvd                    :     11;
+               uint64_t        i_mult_err                :      1;
+       } ii_iprbb_fld_s;
+} ii_iprbb_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 9 instances of this register, one per        *
+ * actual widget in this implementation of SHub and Crossbow.           *
+ * Note: Crossbow only has ports for Widgets 8 through F, widget 0      *
+ * refers to Crossbow's internal space.                                 *
+ * This register contains the state elements per widget that are        *
+ * necessary to manage the PIO flow control on Crosstalk and on the     *
+ * Router Network. See the PIO Flow Control chapter for a complete      *
+ * description of this register                                         *
+ * The SPUR_WR bit requires some explanation. When this register is     *
+ * written, the new value of the C field is captured in an internal     *
+ * register so the hardware can remember what the programmer wrote      *
+ * into the credit counter. The SPUR_WR bit sets whenever the C field   *
+ * increments above this stored value, which indicates that there       *
+ * have been more responses received than requests sent. The SPUR_WR    *
+ * bit cannot be cleared until a value is written to the IPRBx          *
+ * register; the write will correct the C field and capture its new     *
+ * value in the internal register. Even if IECLR[E_PRB_x] is set, the   *
+ * SPUR_WR bit will persist if IPRBx hasn't yet been written.           *
+ * .                                                                    *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprbc_u {
+       uint64_t        ii_iprbc_regval;
+       struct  {
+               uint64_t        i_c                       :      8;
+               uint64_t        i_na                      :     14;
+               uint64_t        i_rsvd_2                  :      2;
+               uint64_t        i_nb                      :     14;
+               uint64_t        i_rsvd_1                  :      2;
+               uint64_t        i_m                       :      2;
+               uint64_t        i_f                       :      1;
+               uint64_t        i_of_cnt                  :      5;
+               uint64_t        i_error                   :      1;
+               uint64_t        i_rd_to                   :      1;
+               uint64_t        i_spur_wr                 :      1;
+               uint64_t        i_spur_rd                 :      1;
+               uint64_t        i_rsvd                    :     11;
+               uint64_t        i_mult_err                :      1;
+       } ii_iprbc_fld_s;
+} ii_iprbc_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 9 instances of this register, one per        *
+ * actual widget in this implementation of SHub and Crossbow.           *
+ * Note: Crossbow only has ports for Widgets 8 through F, widget 0      *
+ * refers to Crossbow's internal space.                                 *
+ * This register contains the state elements per widget that are        *
+ * necessary to manage the PIO flow control on Crosstalk and on the     *
+ * Router Network. See the PIO Flow Control chapter for a complete      *
+ * description of this register                                         *
+ * The SPUR_WR bit requires some explanation. When this register is     *
+ * written, the new value of the C field is captured in an internal     *
+ * register so the hardware can remember what the programmer wrote      *
+ * into the credit counter. The SPUR_WR bit sets whenever the C field   *
+ * increments above this stored value, which indicates that there       *
+ * have been more responses received than requests sent. The SPUR_WR    *
+ * bit cannot be cleared until a value is written to the IPRBx          *
+ * register; the write will correct the C field and capture its new     *
+ * value in the internal register. Even if IECLR[E_PRB_x] is set, the   *
+ * SPUR_WR bit will persist if IPRBx hasn't yet been written.           *
+ * .                                                                    *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprbd_u {
+       uint64_t        ii_iprbd_regval;
+       struct  {
+               uint64_t        i_c                       :      8;
+               uint64_t        i_na                      :     14;
+               uint64_t        i_rsvd_2                  :      2;
+               uint64_t        i_nb                      :     14;
+               uint64_t        i_rsvd_1                  :      2;
+               uint64_t        i_m                       :      2;
+               uint64_t        i_f                       :      1;
+               uint64_t        i_of_cnt                  :      5;
+               uint64_t        i_error                   :      1;
+               uint64_t        i_rd_to                   :      1;
+               uint64_t        i_spur_wr                 :      1;
+               uint64_t        i_spur_rd                 :      1;
+               uint64_t        i_rsvd                    :     11;
+               uint64_t        i_mult_err                :      1;
+       } ii_iprbd_fld_s;
+} ii_iprbd_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 9 instances of this register, one per        *
+ * actual widget in this implementation of SHub and Crossbow.           *
+ * Note: Crossbow only has ports for Widgets 8 through F, widget 0      *
+ * refers to Crossbow's internal space.                                 *
+ * This register contains the state elements per widget that are        *
+ * necessary to manage the PIO flow control on Crosstalk and on the     *
+ * Router Network. See the PIO Flow Control chapter for a complete      *
+ * description of this register                                         *
+ * The SPUR_WR bit requires some explanation. When this register is     *
+ * written, the new value of the C field is captured in an internal     *
+ * register so the hardware can remember what the programmer wrote      *
+ * into the credit counter. The SPUR_WR bit sets whenever the C field   *
+ * increments above this stored value, which indicates that there       *
+ * have been more responses received than requests sent. The SPUR_WR    *
+ * bit cannot be cleared until a value is written to the IPRBx          *
+ * register; the write will correct the C field and capture its new     *
+ * value in the internal register. Even if IECLR[E_PRB_x] is set, the   *
+ * SPUR_WR bit will persist if IPRBx hasn't yet been written.           *
+ * .                                                                    *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprbe_u {
+       uint64_t        ii_iprbe_regval;
+       struct  {
+               uint64_t        i_c                       :      8;
+               uint64_t        i_na                      :     14;
+               uint64_t        i_rsvd_2                  :      2;
+               uint64_t        i_nb                      :     14;
+               uint64_t        i_rsvd_1                  :      2;
+               uint64_t        i_m                       :      2;
+               uint64_t        i_f                       :      1;
+               uint64_t        i_of_cnt                  :      5;
+               uint64_t        i_error                   :      1;
+               uint64_t        i_rd_to                   :      1;
+               uint64_t        i_spur_wr                 :      1;
+               uint64_t        i_spur_rd                 :      1;
+               uint64_t        i_rsvd                    :     11;
+               uint64_t        i_mult_err                :      1;
+       } ii_iprbe_fld_s;
+} ii_iprbe_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 9 instances of this register, one per        *
+ * actual widget in this implementation of Shub and Crossbow.           *
+ * Note: Crossbow only has ports for Widgets 8 through F, widget 0      *
+ * refers to Crossbow's internal space.                                 *
+ * This register contains the state elements per widget that are        *
+ * necessary to manage the PIO flow control on Crosstalk and on the     *
+ * Router Network. See the PIO Flow Control chapter for a complete      *
+ * description of this register                                         *
+ * The SPUR_WR bit requires some explanation. When this register is     *
+ * written, the new value of the C field is captured in an internal     *
+ * register so the hardware can remember what the programmer wrote      *
+ * into the credit counter. The SPUR_WR bit sets whenever the C field   *
+ * increments above this stored value, which indicates that there       *
+ * have been more responses received than requests sent. The SPUR_WR    *
+ * bit cannot be cleared until a value is written to the IPRBx          *
+ * register; the write will correct the C field and capture its new     *
+ * value in the internal register. Even if IECLR[E_PRB_x] is set, the   *
+ * SPUR_WR bit will persist if IPRBx hasn't yet been written.           *
+ * .                                                                    *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprbf_u {
+        uint64_t       ii_iprbf_regval;
+        struct  {
+                uint64_t       i_c                       :      8;
+                uint64_t       i_na                      :     14;
+                uint64_t       i_rsvd_2                  :      2;
+                uint64_t       i_nb                      :     14;
+                uint64_t       i_rsvd_1                  :      2;
+                uint64_t       i_m                       :      2;
+                uint64_t       i_f                       :      1;
+                uint64_t       i_of_cnt                  :      5;
+                uint64_t       i_error                   :      1;
+                uint64_t       i_rd_to                   :      1;
+                uint64_t       i_spur_wr                 :      1;
+                uint64_t       i_spur_rd                 :      1;
+                uint64_t       i_rsvd                    :     11;
+                uint64_t       i_mult_err                :      1;
+        } ii_iprbe_fld_s;
+} ii_iprbf_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register specifies the timeout value to use for monitoring     *
+ * Crosstalk credits which are used outbound to Crosstalk. An           *
+ * internal counter called the Crosstalk Credit Timeout Counter         *
+ * increments every 128 II clocks. The counter starts counting          *
+ * anytime the credit count drops below a threshold, and resets to      *
+ * zero (stops counting) anytime the credit count is at or above the    *
+ * threshold. The threshold is 1 credit in direct connect mode and 2    *
+ * in Crossbow connect mode. When the internal Crosstalk Credit         *
+ * Timeout Counter reaches the value programmed in this register, a     *
+ * Crosstalk Credit Timeout has occurred. The internal counter is not   *
+ * readable from software, and stops counting at its maximum value,     *
+ * so it cannot cause more than one interrupt.                          *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ixcc_u {
+       uint64_t        ii_ixcc_regval;
+       struct  {
+               uint64_t        i_time_out                :     26;
+               uint64_t        i_rsvd                    :     38;
+       } ii_ixcc_fld_s;
+} ii_ixcc_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  This register qualifies all the PIO and DMA            *
+ * operations launched from widget 0 towards the SHub. In               *
+ * addition, it also qualifies accesses by the BTE streams.             *
+ * The bits in each field of this register are cleared by the SHub      *
+ * upon detection of an error which requires widget 0 or the BTE        *
+ * streams to be terminated. Whether or not widget x has access         *
+ * rights to this SHub is determined by an AND of the device            *
+ * enable bit in the appropriate field of this register and bit 0 in    *
+ * the Wx_IAC field. The bits in this field are set by writing a 1 to   *
+ * them. Incoming replies from Crosstalk are not subject to this        *
+ * access control mechanism.                                            *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_imem_u {
+       uint64_t        ii_imem_regval;
+       struct  {
+               uint64_t        i_w0_esd                  :      1;
+               uint64_t        i_rsvd_3                  :      3;
+               uint64_t        i_b0_esd                  :      1;
+               uint64_t        i_rsvd_2                  :      3;
+               uint64_t        i_b1_esd                  :      1;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_clr_precise             :      1;
+               uint64_t       i_rsvd                    :     51;
+       } ii_imem_fld_s;
+} ii_imem_u_t;
+
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  This register specifies the timeout value to use for   *
+ * monitoring Crosstalk tail flits coming into the Shub in the          *
+ * TAIL_TO field. An internal counter associated with this register     *
+ * is incremented every 128 II internal clocks (7 bits). The counter    *
+ * starts counting anytime a header micropacket is received and stops   *
+ * counting (and resets to zero) any time a micropacket with a Tail     *
+ * bit is received. Once the counter reaches the threshold value        *
+ * programmed in this register, it generates an interrupt to the        *
+ * processor that is programmed into the IIDSR. The counter saturates   *
+ * (does not roll over) at its maximum value, so it cannot cause        *
+ * another interrupt until after it is cleared.                         *
+ * The register also contains the Read Response Timeout values. The     *
+ * Prescalar is 23 bits, and counts II clocks. An internal counter      *
+ * increments on every II clock and when it reaches the value in the    *
+ * Prescalar field, all IPRTE registers with their valid bits set       *
+ * have their Read Response timers bumped. Whenever any of them match   *
+ * the value in the RRSP_TO field, a Read Response Timeout has          *
+ * occurred, and error handling occurs as described in the Error        *
+ * Handling section of this document.                                   *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ixtt_u {
+       uint64_t        ii_ixtt_regval;
+       struct  {
+               uint64_t        i_tail_to                 :     26;
+               uint64_t        i_rsvd_1                  :      6;
+               uint64_t        i_rrsp_ps                 :     23;
+               uint64_t        i_rrsp_to                 :      5;
+               uint64_t        i_rsvd                    :      4;
+       } ii_ixtt_fld_s;
+} ii_ixtt_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  Writing a 1 to the fields of this register clears the appropriate   *
+ * error bits in other areas of SHub. Note that when the                *
+ * E_PRB_x bits are used to clear error bits in PRB registers,          *
+ * SPUR_RD and SPUR_WR may persist, because they require additional     *
+ * action to clear them. See the IPRBx and IXSS Register                *
+ * specifications.                                                      *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ieclr_u {
+       uint64_t        ii_ieclr_regval;
+       struct  {
+               uint64_t        i_e_prb_0                 :      1;
+               uint64_t        i_rsvd                    :      7;
+               uint64_t        i_e_prb_8                 :      1;
+               uint64_t        i_e_prb_9                 :      1;
+               uint64_t        i_e_prb_a                 :      1;
+               uint64_t        i_e_prb_b                 :      1;
+               uint64_t        i_e_prb_c                 :      1;
+               uint64_t        i_e_prb_d                 :      1;
+               uint64_t        i_e_prb_e                 :      1;
+               uint64_t        i_e_prb_f                 :      1;
+               uint64_t        i_e_crazy                 :      1;
+               uint64_t        i_e_bte_0                 :      1;
+               uint64_t        i_e_bte_1                 :      1;
+               uint64_t        i_reserved_1              :     10;
+               uint64_t        i_spur_rd_hdr             :      1;
+               uint64_t        i_cam_intr_to             :      1;
+               uint64_t        i_cam_overflow            :      1;
+               uint64_t        i_cam_read_miss           :      1;
+               uint64_t        i_ioq_rep_underflow       :      1;
+               uint64_t        i_ioq_req_underflow       :      1;
+               uint64_t        i_ioq_rep_overflow        :      1;
+               uint64_t        i_ioq_req_overflow        :      1;
+               uint64_t        i_iiq_rep_overflow        :      1;
+               uint64_t        i_iiq_req_overflow        :      1;
+               uint64_t        i_ii_xn_rep_cred_overflow :      1;
+               uint64_t        i_ii_xn_req_cred_overflow :      1;
+               uint64_t        i_ii_xn_invalid_cmd       :      1;
+               uint64_t        i_xn_ii_invalid_cmd       :      1;
+               uint64_t        i_reserved_2              :     21;
+       } ii_ieclr_fld_s;
+} ii_ieclr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register controls both BTEs. SOFT_RESET is intended for        *
+ * recovery after an error. COUNT controls the total number of CRBs     *
+ * that both BTEs (combined) can use, which affects total BTE           *
+ * bandwidth.                                                           *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibcr_u {
+       uint64_t        ii_ibcr_regval;
+       struct  {
+               uint64_t        i_count                   :      4;
+               uint64_t        i_rsvd_1                  :      4;
+               uint64_t        i_soft_reset              :      1;
+               uint64_t        i_rsvd                    :     55;
+       } ii_ibcr_fld_s;
+} ii_ibcr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register contains the header of a spurious read response       *
+ * received from Crosstalk. A spurious read response is defined as a    *
+ * read response received by II from a widget for which (1) the SIDN    *
+ * has a value between 1 and 7, inclusive (II never sends requests to   *
+ * these widgets (2) there is no valid IPRTE register which             *
+ * corresponds to the TNUM, or (3) the widget indicated in SIDN is      *
+ * not the same as the widget recorded in the IPRTE register            *
+ * referenced by the TNUM. If this condition is true, and if the        *
+ * IXSS[VALID] bit is clear, then the header of the spurious read       *
+ * response is capture in IXSM and IXSS, and IXSS[VALID] is set. The    *
+ * errant header is thereby captured, and no further spurious read      *
+ * respones are captured until IXSS[VALID] is cleared by setting the    *
+ * appropriate bit in IECLR.Everytime a spurious read response is       *
+ * detected, the SPUR_RD bit of the PRB corresponding to the incoming   *
+ * message's SIDN field is set. This always happens, regarless of       *
+ * whether a header is captured. The programmer should check            *
+ * IXSM[SIDN] to determine which widget sent the spurious response,     *
+ * because there may be more than one SPUR_RD bit set in the PRB        *
+ * registers. The widget indicated by IXSM[SIDN] was the first          *
+ * spurious read response to be received since the last time            *
+ * IXSS[VALID] was clear. The SPUR_RD bit of the corresponding PRB      *
+ * will be set. Any SPUR_RD bits in any other PRB registers indicate    *
+ * spurious messages from other widets which were detected after the    *
+ * header was captured..                                                *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ixsm_u {
+       uint64_t        ii_ixsm_regval;
+       struct  {
+               uint64_t        i_byte_en                 :     32;
+               uint64_t        i_reserved                :      1;
+               uint64_t        i_tag                     :      3;
+               uint64_t        i_alt_pactyp              :      4;
+               uint64_t        i_bo                      :      1;
+               uint64_t        i_error                   :      1;
+               uint64_t        i_vbpm                    :      1;
+               uint64_t        i_gbr                     :      1;
+               uint64_t        i_ds                      :      2;
+               uint64_t        i_ct                      :      1;
+               uint64_t        i_tnum                    :      5;
+               uint64_t        i_pactyp                  :      4;
+               uint64_t        i_sidn                    :      4;
+               uint64_t        i_didn                    :      4;
+       } ii_ixsm_fld_s;
+} ii_ixsm_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register contains the sideband bits of a spurious read         *
+ * response received from Crosstalk.                                    *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ixss_u {
+       uint64_t        ii_ixss_regval;
+       struct  {
+               uint64_t        i_sideband                :      8;
+               uint64_t        i_rsvd                    :     55;
+               uint64_t        i_valid                   :      1;
+       } ii_ixss_fld_s;
+} ii_ixss_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register enables software to access the II LLP's test port.    *
+ * Refer to the LLP 2.5 documentation for an explanation of the test    *
+ * port. Software can write to this register to program the values      *
+ * for the control fields (TestErrCapture, TestClear, TestFlit,         *
+ * TestMask and TestSeed). Similarly, software can read from this       *
+ * register to obtain the values of the test port's status outputs      *
+ * (TestCBerr, TestValid and TestData).                                 *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ilct_u {
+       uint64_t        ii_ilct_regval;
+       struct  {
+               uint64_t        i_test_seed               :     20;
+               uint64_t        i_test_mask               :      8;
+               uint64_t        i_test_data               :     20;
+               uint64_t        i_test_valid              :      1;
+               uint64_t        i_test_cberr              :      1;
+               uint64_t        i_test_flit               :      3;
+               uint64_t        i_test_clear              :      1;
+               uint64_t        i_test_err_capture        :      1;
+               uint64_t        i_rsvd                    :      9;
+       } ii_ilct_fld_s;
+} ii_ilct_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  If the II detects an illegal incoming Duplonet packet (request or   *
+ * reply) when VALID==0 in the IIEPH1 register, then it saves the       *
+ * contents of the packet's header flit in the IIEPH1 and IIEPH2        *
+ * registers, sets the VALID bit in IIEPH1, clears the OVERRUN bit,     *
+ * and assigns a value to the ERR_TYPE field which indicates the        *
+ * specific nature of the error. The II recognizes four different       *
+ * types of errors: short request packets (ERR_TYPE==2), short reply    *
+ * packets (ERR_TYPE==3), long request packets (ERR_TYPE==4) and long   *
+ * reply packets (ERR_TYPE==5). The encodings for these types of        *
+ * errors were chosen to be consistent with the same types of errors    *
+ * indicated by the ERR_TYPE field in the LB_ERROR_HDR1 register (in    *
+ * the LB unit). If the II detects an illegal incoming Duplonet         *
+ * packet when VALID==1 in the IIEPH1 register, then it merely sets     *
+ * the OVERRUN bit to indicate that a subsequent error has happened,    *
+ * and does nothing further.                                            *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iieph1_u {
+       uint64_t        ii_iieph1_regval;
+       struct  {
+               uint64_t        i_command                 :      7;
+               uint64_t        i_rsvd_5                  :      1;
+               uint64_t        i_suppl                   :     14;
+               uint64_t        i_rsvd_4                  :      1;
+               uint64_t        i_source                  :     14;
+               uint64_t        i_rsvd_3                  :      1;
+               uint64_t        i_err_type                :      4;
+               uint64_t        i_rsvd_2                  :      4;
+               uint64_t        i_overrun                 :      1;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_valid                   :      1;
+               uint64_t        i_rsvd                    :     13;
+       } ii_iieph1_fld_s;
+} ii_iieph1_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register holds the Address field from the header flit of an    *
+ * incoming erroneous Duplonet packet, along with the tail bit which    *
+ * accompanied this header flit. This register is essentially an        *
+ * extension of IIEPH1. Two registers were necessary because the 64     *
+ * bits available in only a single register were insufficient to        *
+ * capture the entire header flit of an erroneous packet.               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iieph2_u {
+       uint64_t        ii_iieph2_regval;
+       struct  {
+               uint64_t        i_rsvd_0                  :      3;
+               uint64_t        i_address                 :     47;
+               uint64_t        i_rsvd_1                  :     10;
+               uint64_t        i_tail                    :      1;
+               uint64_t        i_rsvd                    :      3;
+       } ii_iieph2_fld_s;
+} ii_iieph2_u_t;
+
+
+/******************************/
+
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register's value is a bit vector that guards access from SXBs  *
+ * to local registers within the II as well as to external Crosstalk    *
+ * widgets                                                             *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_islapr_u {
+       uint64_t        ii_islapr_regval;
+       struct  {
+               uint64_t        i_region                  :     64;
+       } ii_islapr_fld_s;
+} ii_islapr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  A write to this register of the 56-bit value "Pup+Bun" will cause  *
+ * the bit in the ISLAPR register corresponding to the region of the   *
+ * requestor to be set (access allowed).                               (
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_islapo_u {
+       uint64_t        ii_islapo_regval;
+       struct  {
+               uint64_t        i_io_sbx_ovrride          :     56;
+               uint64_t        i_rsvd                    :      8;
+       } ii_islapo_fld_s;
+} ii_islapo_u_t;
+
+/************************************************************************
+ *                                                                      *
+ *  Determines how long the wrapper will wait aftr an interrupt is     *
+ * initially issued from the II before it times out the outstanding    *
+ * interrupt and drops it from the interrupt queue.                    * 
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iwi_u {
+       uint64_t        ii_iwi_regval;
+       struct  {
+               uint64_t        i_prescale                :     24;
+               uint64_t        i_rsvd                    :      8;
+               uint64_t        i_timeout                 :      8;
+               uint64_t        i_rsvd1                   :      8;
+               uint64_t        i_intrpt_retry_period     :      8;
+               uint64_t        i_rsvd2                   :      8;
+       } ii_iwi_fld_s;
+} ii_iwi_u_t;
+
+/************************************************************************
+ *                                                                      *
+ *  Log errors which have occurred in the II wrapper. The errors are   *
+ * cleared by writing to the IECLR register.                           * 
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iwel_u {
+       uint64_t        ii_iwel_regval;
+       struct  {
+               uint64_t        i_intr_timed_out          :      1;
+               uint64_t        i_rsvd                    :      7;
+               uint64_t        i_cam_overflow            :      1;
+               uint64_t        i_cam_read_miss           :      1;
+               uint64_t        i_rsvd1                   :      2;
+               uint64_t        i_ioq_rep_underflow       :      1;
+               uint64_t        i_ioq_req_underflow       :      1;
+               uint64_t        i_ioq_rep_overflow        :      1;
+               uint64_t        i_ioq_req_overflow        :      1;
+               uint64_t        i_iiq_rep_overflow        :      1;
+               uint64_t        i_iiq_req_overflow        :      1;
+               uint64_t        i_rsvd2                   :      6;
+               uint64_t        i_ii_xn_rep_cred_over_under:     1;
+               uint64_t        i_ii_xn_req_cred_over_under:     1;
+               uint64_t        i_rsvd3                   :      6;
+               uint64_t        i_ii_xn_invalid_cmd       :      1;
+               uint64_t        i_xn_ii_invalid_cmd       :      1;
+               uint64_t        i_rsvd4                   :     30;
+       } ii_iwel_fld_s;
+} ii_iwel_u_t;
+
+/************************************************************************
+ *                                                                      *
+ *  Controls the II wrapper.                                           * 
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iwc_u {
+       uint64_t        ii_iwc_regval;
+       struct  {
+               uint64_t        i_dma_byte_swap           :      1;
+               uint64_t        i_rsvd                    :      3;
+               uint64_t        i_cam_read_lines_reset    :      1;
+               uint64_t        i_rsvd1                   :      3;
+               uint64_t        i_ii_xn_cred_over_under_log:     1;
+               uint64_t        i_rsvd2                   :     19;
+               uint64_t        i_xn_rep_iq_depth         :      5;
+               uint64_t        i_rsvd3                   :      3;
+               uint64_t        i_xn_req_iq_depth         :      5;
+               uint64_t        i_rsvd4                   :      3;
+               uint64_t        i_iiq_depth               :      6;
+               uint64_t        i_rsvd5                   :     12;
+               uint64_t        i_force_rep_cred          :      1;
+               uint64_t        i_force_req_cred          :      1;
+       } ii_iwc_fld_s;
+} ii_iwc_u_t;
+
+/************************************************************************
+ *                                                                      *
+ *  Status in the II wrapper.                                          * 
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iws_u {
+       uint64_t        ii_iws_regval;
+       struct  {
+               uint64_t        i_xn_rep_iq_credits       :      5;
+               uint64_t        i_rsvd                    :      3;
+               uint64_t        i_xn_req_iq_credits       :      5;
+               uint64_t        i_rsvd1                   :     51;
+       } ii_iws_fld_s;
+} ii_iws_u_t;
+
+/************************************************************************
+ *                                                                      *
+ *  Masks errors in the IWEL register.                                 *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iweim_u {
+       uint64_t        ii_iweim_regval;
+       struct  {
+               uint64_t        i_intr_timed_out          :      1;
+               uint64_t        i_rsvd                    :      7;
+               uint64_t        i_cam_overflow            :      1;
+               uint64_t        i_cam_read_miss           :      1;
+               uint64_t        i_rsvd1                   :      2;
+               uint64_t        i_ioq_rep_underflow       :      1;
+               uint64_t        i_ioq_req_underflow       :      1;
+               uint64_t        i_ioq_rep_overflow        :      1;
+               uint64_t        i_ioq_req_overflow        :      1;
+               uint64_t        i_iiq_rep_overflow        :      1;
+               uint64_t        i_iiq_req_overflow        :      1;
+               uint64_t        i_rsvd2                   :      6;
+               uint64_t        i_ii_xn_rep_cred_overflow :      1;
+               uint64_t        i_ii_xn_req_cred_overflow :      1;
+               uint64_t        i_rsvd3                   :      6;
+               uint64_t        i_ii_xn_invalid_cmd       :      1;
+               uint64_t        i_xn_ii_invalid_cmd       :      1;
+               uint64_t        i_rsvd4                   :     30;
+       } ii_iweim_fld_s;
+} ii_iweim_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  A write to this register causes a particular field in the           *
+ * corresponding widget's PRB entry to be adjusted up or down by 1.     *
+ * This counter should be used when recovering from error and reset     *
+ * conditions. Note that software would be capable of causing           *
+ * inadvertent overflow or underflow of these counters.                 *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ipca_u {
+       uint64_t        ii_ipca_regval;
+       struct  {
+               uint64_t        i_wid                     :      4;
+               uint64_t        i_adjust                  :      1;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_field                   :      2;
+               uint64_t        i_rsvd                    :     54;
+       } ii_ipca_fld_s;
+} ii_ipca_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+
+typedef union ii_iprte0a_u {
+       uint64_t        ii_iprte0a_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :     54;
+               uint64_t        i_widget                  :      4;
+               uint64_t        i_to_cnt                  :      5;
+               uint64_t       i_vld                     :      1;
+       } ii_iprte0a_fld_s;
+} ii_iprte0a_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte1a_u {
+       uint64_t        ii_iprte1a_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :     54;
+               uint64_t        i_widget                  :      4;
+               uint64_t        i_to_cnt                  :      5;
+               uint64_t       i_vld                     :      1;
+       } ii_iprte1a_fld_s;
+} ii_iprte1a_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte2a_u {
+       uint64_t        ii_iprte2a_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :     54;
+               uint64_t        i_widget                  :      4;
+               uint64_t        i_to_cnt                  :      5;
+               uint64_t       i_vld                     :      1;
+       } ii_iprte2a_fld_s;
+} ii_iprte2a_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte3a_u {
+       uint64_t        ii_iprte3a_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :     54;
+               uint64_t        i_widget                  :      4;
+               uint64_t        i_to_cnt                  :      5;
+               uint64_t        i_vld                     :      1;
+       } ii_iprte3a_fld_s;
+} ii_iprte3a_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte4a_u {
+       uint64_t        ii_iprte4a_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :     54;
+               uint64_t        i_widget                  :      4;
+               uint64_t        i_to_cnt                  :      5;
+               uint64_t        i_vld                     :      1;
+       } ii_iprte4a_fld_s;
+} ii_iprte4a_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte5a_u {
+       uint64_t        ii_iprte5a_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :     54;
+               uint64_t        i_widget                  :      4;
+               uint64_t        i_to_cnt                  :      5;
+               uint64_t        i_vld                     :      1;
+       } ii_iprte5a_fld_s;
+} ii_iprte5a_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte6a_u {
+       uint64_t        ii_iprte6a_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :     54;
+               uint64_t        i_widget                  :      4;
+               uint64_t        i_to_cnt                  :      5;
+               uint64_t        i_vld                     :      1;
+       } ii_iprte6a_fld_s;
+} ii_iprte6a_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte7a_u {
+        uint64_t       ii_iprte7a_regval;
+        struct  {
+                uint64_t       i_rsvd_1                  :     54;
+                uint64_t       i_widget                  :      4;
+                uint64_t       i_to_cnt                  :      5;
+                uint64_t       i_vld                     :      1;
+        } ii_iprtea7_fld_s;
+} ii_iprte7a_u_t;
+
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+
+typedef union ii_iprte0b_u {
+       uint64_t        ii_iprte0b_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_address                 :     47;
+               uint64_t        i_init                    :      3;
+               uint64_t       i_source                  :     11;
+       } ii_iprte0b_fld_s;
+} ii_iprte0b_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte1b_u {
+       uint64_t        ii_iprte1b_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_address                 :     47;
+               uint64_t        i_init                    :      3;
+               uint64_t       i_source                  :     11;
+       } ii_iprte1b_fld_s;
+} ii_iprte1b_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte2b_u {
+       uint64_t        ii_iprte2b_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_address                 :     47;
+               uint64_t        i_init                    :      3;
+               uint64_t       i_source                  :     11;
+       } ii_iprte2b_fld_s;
+} ii_iprte2b_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte3b_u {
+       uint64_t        ii_iprte3b_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_address                 :     47;
+               uint64_t        i_init                    :      3;
+               uint64_t       i_source                  :     11;
+       } ii_iprte3b_fld_s;
+} ii_iprte3b_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte4b_u {
+       uint64_t        ii_iprte4b_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_address                 :     47;
+               uint64_t        i_init                    :      3;
+               uint64_t       i_source                  :     11;
+       } ii_iprte4b_fld_s;
+} ii_iprte4b_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte5b_u {
+       uint64_t        ii_iprte5b_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_address                 :     47;
+               uint64_t        i_init                    :      3;
+               uint64_t       i_source                  :     11;
+       } ii_iprte5b_fld_s;
+} ii_iprte5b_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte6b_u {
+       uint64_t        ii_iprte6b_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_address                 :     47;
+               uint64_t        i_init                    :      3;
+               uint64_t       i_source                  :     11;
+
+       } ii_iprte6b_fld_s;
+} ii_iprte6b_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  There are 8 instances of this register. This register contains      *
+ * the information that the II has to remember once it has launched a   *
+ * PIO Read operation. The contents are used to form the correct        *
+ * Router Network packet and direct the Crosstalk reply to the          *
+ * appropriate processor.                                               *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iprte7b_u {
+        uint64_t       ii_iprte7b_regval;
+        struct  {
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_address                 :     47;
+               uint64_t        i_init                    :      3;
+               uint64_t       i_source                  :     11;
+        } ii_iprte7b_fld_s;
+} ii_iprte7b_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  SHub II contains a feature which did not exist in      *
+ * the Hub which automatically cleans up after a Read Response          *
+ * timeout, including deallocation of the IPRTE and recovery of IBuf    *
+ * space. The inclusion of this register in SHub is for backward        *
+ * compatibility                                                        *
+ * A write to this register causes an entry from the table of           *
+ * outstanding PIO Read Requests to be freed and returned to the        *
+ * stack of free entries. This register is used in handling the         *
+ * timeout errors that result in a PIO Reply never returning from       *
+ * Crosstalk.                                                           *
+ * Note that this register does not affect the contents of the IPRTE    *
+ * registers. The Valid bits in those registers have to be              *
+ * specifically turned off by software.                                 *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ipdr_u {
+       uint64_t        ii_ipdr_regval;
+       struct  {
+               uint64_t        i_te                      :      3;
+               uint64_t        i_rsvd_1                  :      1;
+               uint64_t        i_pnd                     :      1;
+               uint64_t        i_init_rpcnt              :      1;
+               uint64_t        i_rsvd                    :     58;
+       } ii_ipdr_fld_s;
+} ii_ipdr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  A write to this register causes a CRB entry to be returned to the   *
+ * queue of free CRBs. The entry should have previously been cleared    *
+ * (mark bit) via backdoor access to the pertinent CRB entry. This      *
+ * register is used in the last step of handling the errors that are    *
+ * captured and marked in CRB entries.  Briefly: 1) first error for     *
+ * DMA write from a particular device, and first error for a            *
+ * particular BTE stream, lead to a marked CRB entry, and processor     *
+ * interrupt, 2) software reads the error information captured in the   *
+ * CRB entry, and presumably takes some corrective action, 3)           *
+ * software clears the mark bit, and finally 4) software writes to      *
+ * the ICDR register to return the CRB entry to the list of free CRB    *
+ * entries.                                                             *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icdr_u {
+       uint64_t        ii_icdr_regval;
+       struct  {
+               uint64_t        i_crb_num                 :      4;
+               uint64_t        i_pnd                     :      1;
+               uint64_t       i_rsvd                    :     59;
+       } ii_icdr_fld_s;
+} ii_icdr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register provides debug access to two FIFOs inside of II.      *
+ * Both IOQ_MAX* fields of this register contain the instantaneous      *
+ * depth (in units of the number of available entries) of the           *
+ * associated IOQ FIFO.  A read of this register will return the        *
+ * number of free entries on each FIFO at the time of the read.  So     *
+ * when a FIFO is idle, the associated field contains the maximum       *
+ * depth of the FIFO.  This register is writable for debug reasons      *
+ * and is intended to be written with the maximum desired FIFO depth    *
+ * while the FIFO is idle. Software must assure that II is idle when    *
+ * this register is written. If there are any active entries in any     *
+ * of these FIFOs when this register is written, the results are        *
+ * undefined.                                                           *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ifdr_u {
+       uint64_t        ii_ifdr_regval;
+       struct  {
+               uint64_t        i_ioq_max_rq              :      7;
+               uint64_t        i_set_ioq_rq              :      1;
+               uint64_t        i_ioq_max_rp              :      7;
+               uint64_t        i_set_ioq_rp              :      1;
+               uint64_t        i_rsvd                    :     48;
+       } ii_ifdr_fld_s;
+} ii_ifdr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register allows the II to become sluggish in removing          *
+ * messages from its inbound queue (IIQ). This will cause messages to   *
+ * back up in either virtual channel. Disabling the "molasses" mode     *
+ * subsequently allows the II to be tested under stress. In the         *
+ * sluggish ("Molasses") mode, the localized effects of congestion      *
+ * can be observed.                                                     *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iiap_u {
+        uint64_t       ii_iiap_regval;
+        struct  {
+                uint64_t       i_rq_mls                  :      6;
+               uint64_t        i_rsvd_1                  :      2;
+               uint64_t        i_rp_mls                  :      6;
+               uint64_t       i_rsvd                    :     50;
+        } ii_iiap_fld_s;
+} ii_iiap_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register allows several parameters of CRB operation to be      *
+ * set. Note that writing to this register can have catastrophic side   *
+ * effects, if the CRB is not quiescent, i.e. if the CRB is             *
+ * processing protocol messages when the write occurs.                  *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icmr_u {
+       uint64_t        ii_icmr_regval;
+       struct  {
+               uint64_t        i_sp_msg                  :      1;
+               uint64_t        i_rd_hdr                  :      1;
+               uint64_t        i_rsvd_4                  :      2;
+               uint64_t        i_c_cnt                   :      4;
+               uint64_t        i_rsvd_3                  :      4;
+               uint64_t        i_clr_rqpd                :      1;
+               uint64_t        i_clr_rppd                :      1;
+               uint64_t        i_rsvd_2                  :      2;
+               uint64_t        i_fc_cnt                  :      4;
+               uint64_t        i_crb_vld                 :     15;
+               uint64_t        i_crb_mark                :     15;
+               uint64_t        i_rsvd_1                  :      2;
+               uint64_t        i_precise                 :      1;
+               uint64_t        i_rsvd                    :     11;
+       } ii_icmr_fld_s;
+} ii_icmr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register allows control of the table portion of the CRB        *
+ * logic via software. Control operations from this register have       *
+ * priority over all incoming Crosstalk or BTE requests.                *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_iccr_u {
+       uint64_t        ii_iccr_regval;
+       struct  {
+               uint64_t        i_crb_num                 :      4;
+               uint64_t        i_rsvd_1                  :      4;
+               uint64_t        i_cmd                     :      8;
+               uint64_t        i_pending                 :      1;
+               uint64_t        i_rsvd                    :     47;
+       } ii_iccr_fld_s;
+} ii_iccr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register allows the maximum timeout value to be programmed.    *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icto_u {
+       uint64_t        ii_icto_regval;
+       struct  {
+               uint64_t        i_timeout                 :      8;
+               uint64_t        i_rsvd                    :     56;
+       } ii_icto_fld_s;
+} ii_icto_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register allows the timeout prescalar to be programmed. An     *
+ * internal counter is associated with this register. When the          *
+ * internal counter reaches the value of the PRESCALE field, the        *
+ * timer registers in all valid CRBs are incremented (CRBx_D[TIMEOUT]   *
+ * field). The internal counter resets to zero, and then continues      *
+ * counting.                                                            *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ictp_u {
+       uint64_t        ii_ictp_regval;
+       struct  {
+               uint64_t        i_prescale                :     24;
+               uint64_t        i_rsvd                    :     40;
+       } ii_ictp_fld_s;
+} ii_ictp_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 15 CRB Entries (ICRB0 to ICRBE) that are     *
+ * used for Crosstalk operations (both cacheline and partial            *
+ * operations) or BTE/IO. Because the CRB entries are very wide, five   *
+ * registers (_A to _E) are required to read and write each entry.      *
+ * The CRB Entry registers can be conceptualized as rows and columns    *
+ * (illustrated in the table above). Each row contains the 4            *
+ * registers required for a single CRB Entry. The first doubleword      *
+ * (column) for each entry is labeled A, and the second doubleword      *
+ * (higher address) is labeled B, the third doubleword is labeled C,    *
+ * the fourth doubleword is labeled D and the fifth doubleword is       *
+ * labeled E. All CRB entries have their addresses on a quarter         *
+ * cacheline aligned boundary.                   *
+ * Upon reset, only the following fields are initialized: valid         *
+ * (VLD), priority count, timeout, timeout valid, and context valid.    *
+ * All other bits should be cleared by software before use (after       *
+ * recovering any potential error state from before the reset).         *
+ * The following four tables summarize the format for the four          *
+ * registers that are used for each ICRB# Entry.                        *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icrb0_a_u {
+       uint64_t        ii_icrb0_a_regval;
+       struct  {
+               uint64_t        ia_iow                    :      1;
+               uint64_t        ia_vld                    :      1;
+               uint64_t        ia_addr                   :     47;
+               uint64_t        ia_tnum                   :      5;
+               uint64_t        ia_sidn                   :      4;
+               uint64_t       ia_rsvd                   :      6;
+       } ii_icrb0_a_fld_s;
+} ii_icrb0_a_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 15 CRB Entries (ICRB0 to ICRBE) that are     *
+ * used for Crosstalk operations (both cacheline and partial            *
+ * operations) or BTE/IO. Because the CRB entries are very wide, five   *
+ * registers (_A to _E) are required to read and write each entry.      *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icrb0_b_u {
+       uint64_t        ii_icrb0_b_regval;
+       struct  {
+               uint64_t        ib_xt_err                 :      1;
+               uint64_t        ib_mark                   :      1;
+               uint64_t        ib_ln_uce                 :      1;
+               uint64_t        ib_errcode                :      3;
+               uint64_t        ib_error                  :      1;
+               uint64_t        ib_stall__bte_1           :      1;
+               uint64_t        ib_stall__bte_0           :      1;
+               uint64_t        ib_stall__intr            :      1;
+               uint64_t        ib_stall_ib               :      1;
+               uint64_t        ib_intvn                  :      1;
+               uint64_t        ib_wb                     :      1;
+               uint64_t        ib_hold                   :      1;
+               uint64_t        ib_ack                    :      1;
+               uint64_t        ib_resp                   :      1;
+               uint64_t        ib_ack_cnt                :     11;
+               uint64_t        ib_rsvd                   :      7;
+               uint64_t        ib_exc                    :      5;
+               uint64_t        ib_init                   :      3;
+               uint64_t        ib_imsg                   :      8;
+               uint64_t        ib_imsgtype               :      2;
+               uint64_t        ib_use_old                :      1;
+               uint64_t        ib_rsvd_1                 :     11;
+       } ii_icrb0_b_fld_s;
+} ii_icrb0_b_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 15 CRB Entries (ICRB0 to ICRBE) that are     *
+ * used for Crosstalk operations (both cacheline and partial            *
+ * operations) or BTE/IO. Because the CRB entries are very wide, five   *
+ * registers (_A to _E) are required to read and write each entry.      *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icrb0_c_u {
+       uint64_t        ii_icrb0_c_regval;
+       struct  {
+               uint64_t        ic_source                 :     15;
+               uint64_t        ic_size                   :      2;
+               uint64_t        ic_ct                     :      1;
+               uint64_t        ic_bte_num                :      1;
+               uint64_t        ic_gbr                    :      1;
+               uint64_t        ic_resprqd                :      1;
+               uint64_t        ic_bo                     :      1;
+               uint64_t        ic_suppl                  :     15;
+               uint64_t        ic_rsvd                   :     27;
+       } ii_icrb0_c_fld_s;
+} ii_icrb0_c_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 15 CRB Entries (ICRB0 to ICRBE) that are     *
+ * used for Crosstalk operations (both cacheline and partial            *
+ * operations) or BTE/IO. Because the CRB entries are very wide, five   *
+ * registers (_A to _E) are required to read and write each entry.      *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icrb0_d_u {
+       uint64_t        ii_icrb0_d_regval;
+       struct  {
+               uint64_t        id_pa_be                  :     43;
+               uint64_t        id_bte_op                 :      1;
+               uint64_t        id_pr_psc                 :      4;
+               uint64_t        id_pr_cnt                 :      4;
+               uint64_t        id_sleep                  :      1;
+               uint64_t        id_rsvd                   :     11;
+       } ii_icrb0_d_fld_s;
+} ii_icrb0_d_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  There are 15 CRB Entries (ICRB0 to ICRBE) that are     *
+ * used for Crosstalk operations (both cacheline and partial            *
+ * operations) or BTE/IO. Because the CRB entries are very wide, five   *
+ * registers (_A to _E) are required to read and write each entry.      *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icrb0_e_u {
+       uint64_t        ii_icrb0_e_regval;
+       struct  {
+               uint64_t        ie_timeout                :      8;
+               uint64_t        ie_context                :     15;
+               uint64_t        ie_rsvd                   :      1;
+               uint64_t        ie_tvld                   :      1;
+               uint64_t        ie_cvld                   :      1;
+               uint64_t        ie_rsvd_0                 :     38;
+       } ii_icrb0_e_fld_s;
+} ii_icrb0_e_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register contains the lower 64 bits of the header of the       *
+ * spurious message captured by II. Valid when the SP_MSG bit in ICMR   *
+ * register is set.                                                     *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icsml_u {
+       uint64_t        ii_icsml_regval;
+       struct  {
+               uint64_t        i_tt_addr                 :     47;
+               uint64_t        i_newsuppl_ex             :     14;
+               uint64_t        i_reserved                :      2;
+               uint64_t       i_overflow                :      1;
+       } ii_icsml_fld_s;
+} ii_icsml_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register contains the middle 64 bits of the header of the      *
+ * spurious message captured by II. Valid when the SP_MSG bit in ICMR   *
+ * register is set.                                                     *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icsmm_u {
+       uint64_t        ii_icsmm_regval;
+       struct  {
+               uint64_t        i_tt_ack_cnt              :     11;
+               uint64_t        i_reserved                :     53;
+       } ii_icsmm_fld_s;
+} ii_icsmm_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register contains the microscopic state, all the inputs to     *
+ * the protocol table, captured with the spurious message. Valid when   *
+ * the SP_MSG bit in the ICMR register is set.                          *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_icsmh_u {
+       uint64_t        ii_icsmh_regval;
+       struct  {
+               uint64_t        i_tt_vld                  :      1;
+               uint64_t        i_xerr                    :      1;
+               uint64_t        i_ft_cwact_o              :      1;
+               uint64_t        i_ft_wact_o               :      1;
+               uint64_t       i_ft_active_o             :      1;
+               uint64_t        i_sync                    :      1;
+               uint64_t        i_mnusg                   :      1;
+               uint64_t        i_mnusz                   :      1;
+               uint64_t        i_plusz                   :      1;
+               uint64_t        i_plusg                   :      1;
+               uint64_t        i_tt_exc                  :      5;
+               uint64_t        i_tt_wb                   :      1;
+               uint64_t        i_tt_hold                 :      1;
+               uint64_t        i_tt_ack                  :      1;
+               uint64_t        i_tt_resp                 :      1;
+               uint64_t        i_tt_intvn                :      1;
+               uint64_t        i_g_stall_bte1            :      1;
+               uint64_t        i_g_stall_bte0            :      1;
+               uint64_t        i_g_stall_il              :      1;
+               uint64_t        i_g_stall_ib              :      1;
+               uint64_t        i_tt_imsg                 :      8;
+               uint64_t        i_tt_imsgtype             :      2;
+               uint64_t        i_tt_use_old              :      1;
+               uint64_t        i_tt_respreqd             :      1;
+               uint64_t        i_tt_bte_num              :      1;
+               uint64_t        i_cbn                     :      1;
+               uint64_t        i_match                   :      1;
+               uint64_t        i_rpcnt_lt_34             :      1;
+               uint64_t        i_rpcnt_ge_34             :      1;
+               uint64_t        i_rpcnt_lt_18             :      1;
+               uint64_t        i_rpcnt_ge_18             :      1;
+               uint64_t       i_rpcnt_lt_2              :      1;
+               uint64_t        i_rpcnt_ge_2              :      1;
+               uint64_t        i_rqcnt_lt_18             :      1;
+               uint64_t        i_rqcnt_ge_18             :      1;
+               uint64_t        i_rqcnt_lt_2              :      1;
+               uint64_t        i_rqcnt_ge_2              :      1;
+               uint64_t        i_tt_device               :      7;
+               uint64_t        i_tt_init                 :      3;
+               uint64_t        i_reserved                :      5;
+       } ii_icsmh_fld_s;
+} ii_icsmh_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  The Shub DEBUG unit provides a 3-bit selection signal to the        *
+ * II core and a 3-bit selection signal to the fsbclk domain in the II  *
+ * wrapper.                                                             *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_idbss_u {
+       uint64_t        ii_idbss_regval;
+       struct  {
+               uint64_t        i_iioclk_core_submenu     :      3;
+               uint64_t        i_rsvd                    :      5;
+               uint64_t        i_fsbclk_wrapper_submenu  :      3;
+               uint64_t        i_rsvd_1                  :      5;
+               uint64_t        i_iioclk_menu             :      5;
+               uint64_t        i_rsvd_2                  :     43;
+       } ii_idbss_fld_s;
+} ii_idbss_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  This register is used to set up the length for a       *
+ * transfer and then to monitor the progress of that transfer. This     *
+ * register needs to be initialized before a transfer is started. A     *
+ * legitimate write to this register will set the Busy bit, clear the   *
+ * Error bit, and initialize the length to the value desired.           *
+ * While the transfer is in progress, hardware will decrement the       *
+ * length field with each successful block that is copied. Once the     *
+ * transfer completes, hardware will clear the Busy bit. The length     *
+ * field will also contain the number of cache lines left to be         *
+ * transferred.                                                         *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibls0_u {
+       uint64_t        ii_ibls0_regval;
+       struct  {
+               uint64_t        i_length                  :     16;
+               uint64_t        i_error                   :      1;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_busy                    :      1;
+               uint64_t       i_rsvd                    :     43;
+       } ii_ibls0_fld_s;
+} ii_ibls0_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register should be loaded before a transfer is started. The    *
+ * address to be loaded in bits 39:0 is the 40-bit TRex+ physical       *
+ * address as described in Section 1.3, Figure2 and Figure3. Since      *
+ * the bottom 7 bits of the address are always taken to be zero, BTE    *
+ * transfers are always cacheline-aligned.                              *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibsa0_u {
+       uint64_t        ii_ibsa0_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      7;
+               uint64_t        i_addr                    :     42;
+               uint64_t       i_rsvd                    :     15;
+       } ii_ibsa0_fld_s;
+} ii_ibsa0_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register should be loaded before a transfer is started. The    *
+ * address to be loaded in bits 39:0 is the 40-bit TRex+ physical       *
+ * address as described in Section 1.3, Figure2 and Figure3. Since      *
+ * the bottom 7 bits of the address are always taken to be zero, BTE    *
+ * transfers are always cacheline-aligned.                              *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibda0_u {
+       uint64_t        ii_ibda0_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      7;
+               uint64_t        i_addr                    :     42;
+               uint64_t        i_rsvd                    :     15;
+       } ii_ibda0_fld_s;
+} ii_ibda0_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  Writing to this register sets up the attributes of the transfer     *
+ * and initiates the transfer operation. Reading this register has      *
+ * the side effect of terminating any transfer in progress. Note:       *
+ * stopping a transfer midstream could have an adverse impact on the    *
+ * other BTE. If a BTE stream has to be stopped (due to error           *
+ * handling for example), both BTE streams should be stopped and        *
+ * their transfers discarded.                                           *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibct0_u {
+       uint64_t        ii_ibct0_regval;
+       struct  {
+               uint64_t        i_zerofill                :      1;
+               uint64_t        i_rsvd_2                  :      3;
+               uint64_t        i_notify                  :      1;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t       i_poison                  :      1;
+               uint64_t       i_rsvd                    :     55;
+       } ii_ibct0_fld_s;
+} ii_ibct0_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register contains the address to which the WINV is sent.       *
+ * This address has to be cache line aligned.                           *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibna0_u {
+       uint64_t        ii_ibna0_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      7;
+               uint64_t        i_addr                    :     42;
+               uint64_t        i_rsvd                    :     15;
+       } ii_ibna0_fld_s;
+} ii_ibna0_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register contains the programmable level as well as the node   *
+ * ID and PI unit of the processor to which the interrupt will be       *
+ * sent.                                                                *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibia0_u {
+       uint64_t        ii_ibia0_regval;
+       struct  {
+               uint64_t        i_rsvd_2                   :     1;
+               uint64_t        i_node_id                 :     11;
+               uint64_t        i_rsvd_1                  :      4;
+               uint64_t        i_level                   :      7;
+               uint64_t       i_rsvd                    :     41;
+       } ii_ibia0_fld_s;
+} ii_ibia0_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ * Description:  This register is used to set up the length for a       *
+ * transfer and then to monitor the progress of that transfer. This     *
+ * register needs to be initialized before a transfer is started. A     *
+ * legitimate write to this register will set the Busy bit, clear the   *
+ * Error bit, and initialize the length to the value desired.           *
+ * While the transfer is in progress, hardware will decrement the       *
+ * length field with each successful block that is copied. Once the     *
+ * transfer completes, hardware will clear the Busy bit. The length     *
+ * field will also contain the number of cache lines left to be         *
+ * transferred.                                                         *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibls1_u {
+       uint64_t        ii_ibls1_regval;
+       struct  {
+               uint64_t        i_length                  :     16;
+               uint64_t        i_error                   :      1;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_busy                    :      1;
+               uint64_t       i_rsvd                    :     43;
+       } ii_ibls1_fld_s;
+} ii_ibls1_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register should be loaded before a transfer is started. The    *
+ * address to be loaded in bits 39:0 is the 40-bit TRex+ physical       *
+ * address as described in Section 1.3, Figure2 and Figure3. Since      *
+ * the bottom 7 bits of the address are always taken to be zero, BTE    *
+ * transfers are always cacheline-aligned.                              *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibsa1_u {
+       uint64_t        ii_ibsa1_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      7;
+               uint64_t        i_addr                    :     33;
+               uint64_t        i_rsvd                    :     24;
+       } ii_ibsa1_fld_s;
+} ii_ibsa1_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register should be loaded before a transfer is started. The    *
+ * address to be loaded in bits 39:0 is the 40-bit TRex+ physical       *
+ * address as described in Section 1.3, Figure2 and Figure3. Since      *
+ * the bottom 7 bits of the address are always taken to be zero, BTE    *
+ * transfers are always cacheline-aligned.                              *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibda1_u {
+       uint64_t        ii_ibda1_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      7;
+               uint64_t        i_addr                    :     33;
+               uint64_t        i_rsvd                    :     24;
+       } ii_ibda1_fld_s;
+} ii_ibda1_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  Writing to this register sets up the attributes of the transfer     *
+ * and initiates the transfer operation. Reading this register has      *
+ * the side effect of terminating any transfer in progress. Note:       *
+ * stopping a transfer midstream could have an adverse impact on the    *
+ * other BTE. If a BTE stream has to be stopped (due to error           *
+ * handling for example), both BTE streams should be stopped and        *
+ * their transfers discarded.                                           *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibct1_u {
+       uint64_t        ii_ibct1_regval;
+       struct  {
+               uint64_t        i_zerofill                :      1;
+               uint64_t        i_rsvd_2                  :      3;
+               uint64_t        i_notify                  :      1;
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_poison                  :      1;
+               uint64_t        i_rsvd                    :     55;
+       } ii_ibct1_fld_s;
+} ii_ibct1_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register contains the address to which the WINV is sent.       *
+ * This address has to be cache line aligned.                           *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibna1_u {
+       uint64_t        ii_ibna1_regval;
+       struct  {
+               uint64_t        i_rsvd_1                  :      7;
+               uint64_t        i_addr                    :     33;
+               uint64_t       i_rsvd                    :     24;
+       } ii_ibna1_fld_s;
+} ii_ibna1_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register contains the programmable level as well as the node   *
+ * ID and PI unit of the processor to which the interrupt will be       *
+ * sent.                                                                *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ibia1_u {
+       uint64_t        ii_ibia1_regval;
+       struct  {
+               uint64_t        i_pi_id                   :      1;
+               uint64_t        i_node_id                 :      8;
+               uint64_t        i_rsvd_1                  :      7;
+               uint64_t        i_level                   :      7;
+               uint64_t        i_rsvd                    :     41;
+       } ii_ibia1_fld_s;
+} ii_ibia1_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *  This register defines the resources that feed information into      *
+ * the two performance counters located in the IO Performance           *
+ * Profiling Register. There are 17 different quantities that can be    *
+ * measured. Given these 17 different options, the two performance      *
+ * counters have 15 of them in common; menu selections 0 through 0xE    *
+ * are identical for each performance counter. As for the other two     *
+ * options, one is available from one performance counter and the       *
+ * other is available from the other performance counter. Hence, the    *
+ * II supports all 17*16=272 possible combinations of quantities to     *
+ * measure.                                                             *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ipcr_u {
+       uint64_t        ii_ipcr_regval;
+       struct  {
+               uint64_t        i_ippr0_c                 :      4;
+               uint64_t        i_ippr1_c                 :      4;
+               uint64_t        i_icct                    :      8;
+               uint64_t       i_rsvd                    :     48;
+       } ii_ipcr_fld_s;
+} ii_ipcr_u_t;
+
+
+/************************************************************************
+ *                                                                      *
+ *                                                                      *
+ *                                                                      *
+ ************************************************************************/
+
+typedef union ii_ippr_u {
+       uint64_t        ii_ippr_regval;
+       struct  {
+               uint64_t        i_ippr0                   :     32;
+               uint64_t        i_ippr1                   :     32;
+       } ii_ippr_fld_s;
+} ii_ippr_u_t;
+
+
+
+/**************************************************************************
+ *                                                                        *
+ * The following defines which were not formed into structures are        *
+ * probably indentical to another register, and the name of the           *
+ * register is provided against each of these registers. This             *
+ * information needs to be checked carefully                              *
+ *                                                                        *
+ *           IIO_ICRB1_A                IIO_ICRB0_A                       *
+ *           IIO_ICRB1_B                IIO_ICRB0_B                       *
+ *           IIO_ICRB1_C                IIO_ICRB0_C                       *
+ *           IIO_ICRB1_D                IIO_ICRB0_D                       *
+ *           IIO_ICRB1_E                IIO_ICRB0_E                       *
+ *           IIO_ICRB2_A                IIO_ICRB0_A                       *
+ *           IIO_ICRB2_B                IIO_ICRB0_B                       *
+ *           IIO_ICRB2_C                IIO_ICRB0_C                       *
+ *           IIO_ICRB2_D                IIO_ICRB0_D                       *
+ *           IIO_ICRB2_E                IIO_ICRB0_E                       *
+ *           IIO_ICRB3_A                IIO_ICRB0_A                       *
+ *           IIO_ICRB3_B                IIO_ICRB0_B                       *
+ *           IIO_ICRB3_C                IIO_ICRB0_C                       *
+ *           IIO_ICRB3_D                IIO_ICRB0_D                       *
+ *           IIO_ICRB3_E                IIO_ICRB0_E                       *
+ *           IIO_ICRB4_A                IIO_ICRB0_A                       *
+ *           IIO_ICRB4_B                IIO_ICRB0_B                       *
+ *           IIO_ICRB4_C                IIO_ICRB0_C                       *
+ *           IIO_ICRB4_D                IIO_ICRB0_D                       *
+ *           IIO_ICRB4_E                IIO_ICRB0_E                       *
+ *           IIO_ICRB5_A                IIO_ICRB0_A                       *
+ *           IIO_ICRB5_B                IIO_ICRB0_B                       *
+ *           IIO_ICRB5_C                IIO_ICRB0_C                       *
+ *           IIO_ICRB5_D                IIO_ICRB0_D                       *
+ *           IIO_ICRB5_E                IIO_ICRB0_E                       *
+ *           IIO_ICRB6_A                IIO_ICRB0_A                       *
+ *           IIO_ICRB6_B                IIO_ICRB0_B                       *
+ *           IIO_ICRB6_C                IIO_ICRB0_C                       *
+ *           IIO_ICRB6_D                IIO_ICRB0_D                       *
+ *           IIO_ICRB6_E                IIO_ICRB0_E                       *
+ *           IIO_ICRB7_A                IIO_ICRB0_A                       *
+ *           IIO_ICRB7_B                IIO_ICRB0_B                       *
+ *           IIO_ICRB7_C                IIO_ICRB0_C                       *
+ *           IIO_ICRB7_D                IIO_ICRB0_D                       *
+ *           IIO_ICRB7_E                IIO_ICRB0_E                       *
+ *           IIO_ICRB8_A                IIO_ICRB0_A                       *
+ *           IIO_ICRB8_B                IIO_ICRB0_B                       *
+ *           IIO_ICRB8_C                IIO_ICRB0_C                       *
+ *           IIO_ICRB8_D                IIO_ICRB0_D                       *
+ *           IIO_ICRB8_E                IIO_ICRB0_E                       *
+ *           IIO_ICRB9_A                IIO_ICRB0_A                       *
+ *           IIO_ICRB9_B                IIO_ICRB0_B                       *
+ *           IIO_ICRB9_C                IIO_ICRB0_C                       *
+ *           IIO_ICRB9_D                IIO_ICRB0_D                       *
+ *           IIO_ICRB9_E                IIO_ICRB0_E                       *
+ *           IIO_ICRBA_A                IIO_ICRB0_A                       *
+ *           IIO_ICRBA_B                IIO_ICRB0_B                       *
+ *           IIO_ICRBA_C                IIO_ICRB0_C                       *
+ *           IIO_ICRBA_D                IIO_ICRB0_D                       *
+ *           IIO_ICRBA_E                IIO_ICRB0_E                       *
+ *           IIO_ICRBB_A                IIO_ICRB0_A                       *
+ *           IIO_ICRBB_B                IIO_ICRB0_B                       *
+ *           IIO_ICRBB_C                IIO_ICRB0_C                       *
+ *           IIO_ICRBB_D                IIO_ICRB0_D                       *
+ *           IIO_ICRBB_E                IIO_ICRB0_E                       *
+ *           IIO_ICRBC_A                IIO_ICRB0_A                       *
+ *           IIO_ICRBC_B                IIO_ICRB0_B                       *
+ *           IIO_ICRBC_C                IIO_ICRB0_C                       *
+ *           IIO_ICRBC_D                IIO_ICRB0_D                       *
+ *           IIO_ICRBC_E                IIO_ICRB0_E                       *
+ *           IIO_ICRBD_A                IIO_ICRB0_A                       *
+ *           IIO_ICRBD_B                IIO_ICRB0_B                       *
+ *           IIO_ICRBD_C                IIO_ICRB0_C                       *
+ *           IIO_ICRBD_D                IIO_ICRB0_D                       *
+ *           IIO_ICRBD_E                IIO_ICRB0_E                       *
+ *           IIO_ICRBE_A                IIO_ICRB0_A                       *
+ *           IIO_ICRBE_B                IIO_ICRB0_B                       *
+ *           IIO_ICRBE_C                IIO_ICRB0_C                       *
+ *           IIO_ICRBE_D                IIO_ICRB0_D                       *
+ *           IIO_ICRBE_E                IIO_ICRB0_E                       *
+ *                                                                        *
+ **************************************************************************/
+
+
+/*
+ * Slightly friendlier names for some common registers.
+ */
+#define IIO_WIDGET              IIO_WID      /* Widget identification */
+#define IIO_WIDGET_STAT         IIO_WSTAT    /* Widget status register */
+#define IIO_WIDGET_CTRL         IIO_WCR      /* Widget control register */
+#define IIO_PROTECT             IIO_ILAPR    /* IO interface protection */
+#define IIO_PROTECT_OVRRD       IIO_ILAPO    /* IO protect override */
+#define IIO_OUTWIDGET_ACCESS    IIO_IOWA     /* Outbound widget access */
+#define IIO_INWIDGET_ACCESS     IIO_IIWA     /* Inbound widget access */
+#define IIO_INDEV_ERR_MASK      IIO_IIDEM    /* Inbound device error mask */
+#define IIO_LLP_CSR             IIO_ILCSR    /* LLP control and status */
+#define IIO_LLP_LOG             IIO_ILLR     /* LLP log */
+#define IIO_XTALKCC_TOUT        IIO_IXCC     /* Xtalk credit count timeout*/
+#define IIO_XTALKTT_TOUT        IIO_IXTT     /* Xtalk tail timeout */
+#define IIO_IO_ERR_CLR          IIO_IECLR    /* IO error clear */
+#define IIO_IGFX_0             IIO_IGFX0
+#define IIO_IGFX_1             IIO_IGFX1
+#define IIO_IBCT_0             IIO_IBCT0
+#define IIO_IBCT_1             IIO_IBCT1
+#define IIO_IBLS_0             IIO_IBLS0
+#define IIO_IBLS_1             IIO_IBLS1
+#define IIO_IBSA_0             IIO_IBSA0
+#define IIO_IBSA_1             IIO_IBSA1
+#define IIO_IBDA_0             IIO_IBDA0
+#define IIO_IBDA_1             IIO_IBDA1
+#define IIO_IBNA_0             IIO_IBNA0
+#define IIO_IBNA_1             IIO_IBNA1
+#define IIO_IBIA_0             IIO_IBIA0
+#define IIO_IBIA_1             IIO_IBIA1
+#define IIO_IOPRB_0            IIO_IPRB0
+
+#define IIO_PRTE_A(_x)         (IIO_IPRTE0_A + (8 * (_x)))
+#define IIO_PRTE_B(_x)         (IIO_IPRTE0_B + (8 * (_x)))
+#define IIO_NUM_PRTES          8       /* Total number of PRB table entries */
+#define IIO_WIDPRTE_A(x)       IIO_PRTE_A(((x) - 8)) /* widget ID to its PRTE num */
+#define IIO_WIDPRTE_B(x)       IIO_PRTE_B(((x) - 8)) /* widget ID to its PRTE num */
+
+#define IIO_NUM_IPRBS          (9) 
+
+#define IIO_LLP_CSR_IS_UP               0x00002000
+#define IIO_LLP_CSR_LLP_STAT_MASK       0x00003000
+#define IIO_LLP_CSR_LLP_STAT_SHFT       12
+
+#define IIO_LLP_CB_MAX  0xffff /* in ILLR CB_CNT, Max Check Bit errors */
+#define IIO_LLP_SN_MAX  0xffff /* in ILLR SN_CNT, Max Sequence Number errors */
+
+/* key to IIO_PROTECT_OVRRD */
+#define IIO_PROTECT_OVRRD_KEY   0x53474972756c6573ull   /* "SGIrules" */
+
+/* BTE register names */
+#define IIO_BTE_STAT_0          IIO_IBLS_0   /* Also BTE length/status 0 */
+#define IIO_BTE_SRC_0           IIO_IBSA_0   /* Also BTE source address  0 */
+#define IIO_BTE_DEST_0          IIO_IBDA_0   /* Also BTE dest. address 0 */
+#define IIO_BTE_CTRL_0          IIO_IBCT_0   /* Also BTE control/terminate 0 */
+#define IIO_BTE_NOTIFY_0        IIO_IBNA_0   /* Also BTE notification 0 */
+#define IIO_BTE_INT_0           IIO_IBIA_0   /* Also BTE interrupt 0 */
+#define IIO_BTE_OFF_0           0            /* Base offset from BTE 0 regs. */
+#define IIO_BTE_OFF_1          (IIO_IBLS_1 - IIO_IBLS_0) /* Offset from base to BTE 1 */
+
+/* BTE register offsets from base */
+#define BTEOFF_STAT             0
+#define BTEOFF_SRC              (IIO_BTE_SRC_0 - IIO_BTE_STAT_0)
+#define BTEOFF_DEST             (IIO_BTE_DEST_0 - IIO_BTE_STAT_0)
+#define BTEOFF_CTRL             (IIO_BTE_CTRL_0 - IIO_BTE_STAT_0)
+#define BTEOFF_NOTIFY           (IIO_BTE_NOTIFY_0 - IIO_BTE_STAT_0)
+#define BTEOFF_INT              (IIO_BTE_INT_0 - IIO_BTE_STAT_0)
+
+
+/* names used in shub diags */
+#define IIO_BASE_BTE0   IIO_IBLS_0             
+#define IIO_BASE_BTE1   IIO_IBLS_1             
+
+/*
+ * Macro which takes the widget number, and returns the
+ * IO PRB address of that widget.
+ * value _x is expected to be a widget number in the range
+ * 0, 8 - 0xF
+ */
+#define IIO_IOPRB(_x)   (IIO_IOPRB_0 + ( ( (_x) < HUB_WIDGET_ID_MIN ? \
+                        (_x) : \
+                        (_x) - (HUB_WIDGET_ID_MIN-1)) << 3) )
+
+
+/* GFX Flow Control Node/Widget Register */
+#define IIO_IGFX_W_NUM_BITS    4       /* size of widget num field */
+#define IIO_IGFX_W_NUM_MASK    ((1<<IIO_IGFX_W_NUM_BITS)-1)
+#define IIO_IGFX_W_NUM_SHIFT   0
+#define IIO_IGFX_PI_NUM_BITS   1       /* size of PI num field */
+#define IIO_IGFX_PI_NUM_MASK   ((1<<IIO_IGFX_PI_NUM_BITS)-1)
+#define IIO_IGFX_PI_NUM_SHIFT  4
+#define IIO_IGFX_N_NUM_BITS    8       /* size of node num field */
+#define IIO_IGFX_N_NUM_MASK    ((1<<IIO_IGFX_N_NUM_BITS)-1)
+#define IIO_IGFX_N_NUM_SHIFT   5
+#define IIO_IGFX_P_NUM_BITS    1       /* size of processor num field */
+#define IIO_IGFX_P_NUM_MASK    ((1<<IIO_IGFX_P_NUM_BITS)-1)
+#define IIO_IGFX_P_NUM_SHIFT   16
+#define IIO_IGFX_INIT(widget, pi, node, cpu)                           (\
+       (((widget) & IIO_IGFX_W_NUM_MASK) << IIO_IGFX_W_NUM_SHIFT) |     \
+       (((pi)     & IIO_IGFX_PI_NUM_MASK)<< IIO_IGFX_PI_NUM_SHIFT)|     \
+       (((node)   & IIO_IGFX_N_NUM_MASK) << IIO_IGFX_N_NUM_SHIFT) |     \
+       (((cpu)    & IIO_IGFX_P_NUM_MASK) << IIO_IGFX_P_NUM_SHIFT))
+
+
+/* Scratch registers (all bits available) */
+#define IIO_SCRATCH_REG0        IIO_ISCR0
+#define IIO_SCRATCH_REG1        IIO_ISCR1
+#define IIO_SCRATCH_MASK        0xffffffffffffffffUL
+
+#define IIO_SCRATCH_BIT0_0      0x0000000000000001UL
+#define IIO_SCRATCH_BIT0_1      0x0000000000000002UL
+#define IIO_SCRATCH_BIT0_2      0x0000000000000004UL
+#define IIO_SCRATCH_BIT0_3      0x0000000000000008UL
+#define IIO_SCRATCH_BIT0_4      0x0000000000000010UL
+#define IIO_SCRATCH_BIT0_5      0x0000000000000020UL
+#define IIO_SCRATCH_BIT0_6      0x0000000000000040UL
+#define IIO_SCRATCH_BIT0_7      0x0000000000000080UL
+#define IIO_SCRATCH_BIT0_8      0x0000000000000100UL
+#define IIO_SCRATCH_BIT0_9      0x0000000000000200UL
+#define IIO_SCRATCH_BIT0_A      0x0000000000000400UL
+
+#define IIO_SCRATCH_BIT1_0      0x0000000000000001UL
+#define IIO_SCRATCH_BIT1_1      0x0000000000000002UL
+/* IO Translation Table Entries */
+#define IIO_NUM_ITTES   7               /* ITTEs numbered 0..6 */
+                                        /* Hw manuals number them 1..7! */
+/*
+ * IIO_IMEM Register fields.
+ */
+#define IIO_IMEM_W0ESD  0x1UL             /* Widget 0 shut down due to error */
+#define IIO_IMEM_B0ESD  (1UL << 4)        /* BTE 0 shut down due to error */
+#define IIO_IMEM_B1ESD  (1UL << 8)        /* BTE 1 Shut down due to error */
+
+/*
+ * As a permanent workaround for a bug in the PI side of the shub, we've
+ * redefined big window 7 as small window 0.
+ XXX does this still apply for SN1??
+ */
+#define HUB_NUM_BIG_WINDOW      (IIO_NUM_ITTES - 1)
+
+/*
+ * Use the top big window as a surrogate for the first small window
+ */
+#define SWIN0_BIGWIN            HUB_NUM_BIG_WINDOW
+
+#define ILCSR_WARM_RESET        0x100
+
+/*
+ * CRB manipulation macros
+ *      The CRB macros are slightly complicated, since there are up to
+ *      four registers associated with each CRB entry.
+ */
+#define IIO_NUM_CRBS            15      /* Number of CRBs */
+#define IIO_NUM_PC_CRBS         4       /* Number of partial cache CRBs */
+#define IIO_ICRB_OFFSET         8
+#define IIO_ICRB_0              IIO_ICRB0_A
+#define IIO_ICRB_ADDR_SHFT     2       /* Shift to get proper address */
+/* XXX - This is now tuneable:
+        #define IIO_FIRST_PC_ENTRY 12
+ */
+
+#define IIO_ICRB_A(_x)  ((u64)(IIO_ICRB_0 + (6 * IIO_ICRB_OFFSET * (_x))))
+#define IIO_ICRB_B(_x)  ((u64)((char *)IIO_ICRB_A(_x) + 1*IIO_ICRB_OFFSET))
+#define IIO_ICRB_C(_x)  ((u64)((char *)IIO_ICRB_A(_x) + 2*IIO_ICRB_OFFSET))
+#define IIO_ICRB_D(_x)  ((u64)((char *)IIO_ICRB_A(_x) + 3*IIO_ICRB_OFFSET))
+#define IIO_ICRB_E(_x)  ((u64)((char *)IIO_ICRB_A(_x) + 4*IIO_ICRB_OFFSET))
+
+#define TNUM_TO_WIDGET_DEV(_tnum)      (_tnum & 0x7)
+
+/*
+ * values for "ecode" field
+ */
+#define IIO_ICRB_ECODE_DERR     0       /* Directory error due to IIO access */
+#define IIO_ICRB_ECODE_PERR     1       /* Poison error on IO access */
+#define IIO_ICRB_ECODE_WERR     2       /* Write error by IIO access
+                                         * e.g. WINV to a Read only line. */
+#define IIO_ICRB_ECODE_AERR     3       /* Access error caused by IIO access */
+#define IIO_ICRB_ECODE_PWERR    4       /* Error on partial write       */
+#define IIO_ICRB_ECODE_PRERR    5       /* Error on partial read        */
+#define IIO_ICRB_ECODE_TOUT     6       /* CRB timeout before deallocating */
+#define IIO_ICRB_ECODE_XTERR    7       /* Incoming xtalk pkt had error bit */
+
+/*
+ * Values for field imsgtype
+ */
+#define IIO_ICRB_IMSGT_XTALK    0       /* Incoming Meessage from Xtalk */
+#define IIO_ICRB_IMSGT_BTE      1       /* Incoming message from BTE    */
+#define IIO_ICRB_IMSGT_SN1NET   2       /* Incoming message from SN1 net */
+#define IIO_ICRB_IMSGT_CRB      3       /* Incoming message from CRB ???  */
+
+/*
+ * values for field initiator.
+ */
+#define IIO_ICRB_INIT_XTALK     0       /* Message originated in xtalk  */
+#define IIO_ICRB_INIT_BTE0      0x1     /* Message originated in BTE 0  */
+#define IIO_ICRB_INIT_SN1NET    0x2     /* Message originated in SN1net */
+#define IIO_ICRB_INIT_CRB       0x3     /* Message originated in CRB ?  */
+#define IIO_ICRB_INIT_BTE1      0x5     /* MEssage originated in BTE 1  */
+
+/*
+ * Number of credits Hub widget has while sending req/response to
+ * xbow.
+ * Value of 3 is required by Xbow 1.1
+ * We may be able to increase this to 4 with Xbow 1.2.
+ */
+#define       HUBII_XBOW_CREDIT       3
+#define       HUBII_XBOW_REV2_CREDIT  4
+
+/*
+ * Number of credits that xtalk devices should use when communicating
+ * with a SHub (depth of SHub's queue).
+ */
+#define HUB_CREDIT 4
+
+/*
+ * Some IIO_PRB fields
+ */
+#define IIO_PRB_MULTI_ERR      (1LL << 63)
+#define IIO_PRB_SPUR_RD                (1LL << 51)
+#define IIO_PRB_SPUR_WR                (1LL << 50)
+#define IIO_PRB_RD_TO          (1LL << 49)
+#define IIO_PRB_ERROR          (1LL << 48)
+
+/*************************************************************************
+
+ Some of the IIO field masks and shifts are defined here.
+ This is in order to maintain compatibility in SN0 and SN1 code
+**************************************************************************/
+
+/*
+ * ICMR register fields
+ * (Note: the IIO_ICMR_P_CNT and IIO_ICMR_PC_VLD from Hub are not
+ * present in SHub)
+ */
+
+#define IIO_ICMR_CRB_VLD_SHFT   20
+#define IIO_ICMR_CRB_VLD_MASK   (0x7fffUL << IIO_ICMR_CRB_VLD_SHFT)
+
+#define IIO_ICMR_FC_CNT_SHFT    16
+#define IIO_ICMR_FC_CNT_MASK    (0xf << IIO_ICMR_FC_CNT_SHFT)
+
+#define IIO_ICMR_C_CNT_SHFT     4
+#define IIO_ICMR_C_CNT_MASK     (0xf << IIO_ICMR_C_CNT_SHFT)
+
+#define IIO_ICMR_PRECISE        (1UL << 52)
+#define IIO_ICMR_CLR_RPPD       (1UL << 13)
+#define IIO_ICMR_CLR_RQPD       (1UL << 12)
+
+/*
+ * IIO PIO Deallocation register field masks : (IIO_IPDR)
+ XXX present but not needed in bedrock?  See the manual.
+ */
+#define IIO_IPDR_PND    (1 << 4)
+
+/*
+ * IIO CRB deallocation register field masks: (IIO_ICDR)
+ */
+#define IIO_ICDR_PND    (1 << 4)
+
+/* 
+ * IO BTE Length/Status (IIO_IBLS) register bit field definitions
+ */
+#define IBLS_BUSY              (0x1UL << 20)
+#define IBLS_ERROR_SHFT                16
+#define IBLS_ERROR             (0x1UL << IBLS_ERROR_SHFT)
+#define IBLS_LENGTH_MASK       0xffff
+
+/*
+ * IO BTE Control/Terminate register (IBCT) register bit field definitions
+ */
+#define IBCT_POISON            (0x1UL << 8)
+#define IBCT_NOTIFY            (0x1UL << 4)
+#define IBCT_ZFIL_MODE         (0x1UL << 0)
+
+/*
+ * IIO Incoming Error Packet Header (IIO_IIEPH1/IIO_IIEPH2)
+ */
+#define IIEPH1_VALID           (1UL << 44)
+#define IIEPH1_OVERRUN         (1UL << 40)
+#define IIEPH1_ERR_TYPE_SHFT   32
+#define IIEPH1_ERR_TYPE_MASK   0xf
+#define IIEPH1_SOURCE_SHFT     20
+#define IIEPH1_SOURCE_MASK     11
+#define IIEPH1_SUPPL_SHFT      8
+#define IIEPH1_SUPPL_MASK      11
+#define IIEPH1_CMD_SHFT                0
+#define IIEPH1_CMD_MASK                7
+
+#define IIEPH2_TAIL            (1UL << 40)
+#define IIEPH2_ADDRESS_SHFT    0
+#define IIEPH2_ADDRESS_MASK    38
+
+#define IIEPH1_ERR_SHORT_REQ   2
+#define IIEPH1_ERR_SHORT_REPLY 3
+#define IIEPH1_ERR_LONG_REQ    4
+#define IIEPH1_ERR_LONG_REPLY  5
+
+/*
+ * IO Error Clear register bit field definitions
+ */
+#define IECLR_PI1_FWD_INT      (1UL << 31)  /* clear PI1_FORWARD_INT in iidsr */
+#define IECLR_PI0_FWD_INT      (1UL << 30)  /* clear PI0_FORWARD_INT in iidsr */
+#define IECLR_SPUR_RD_HDR      (1UL << 29)  /* clear valid bit in ixss reg */
+#define IECLR_BTE1             (1UL << 18)  /* clear bte error 1 */
+#define IECLR_BTE0             (1UL << 17)  /* clear bte error 0 */
+#define IECLR_CRAZY            (1UL << 16)  /* clear crazy bit in wstat reg */
+#define IECLR_PRB_F            (1UL << 15)  /* clear err bit in PRB_F reg */
+#define IECLR_PRB_E            (1UL << 14)  /* clear err bit in PRB_E reg */
+#define IECLR_PRB_D            (1UL << 13)  /* clear err bit in PRB_D reg */
+#define IECLR_PRB_C            (1UL << 12)  /* clear err bit in PRB_C reg */
+#define IECLR_PRB_B            (1UL << 11)  /* clear err bit in PRB_B reg */
+#define IECLR_PRB_A            (1UL << 10)  /* clear err bit in PRB_A reg */
+#define IECLR_PRB_9            (1UL << 9)   /* clear err bit in PRB_9 reg */
+#define IECLR_PRB_8            (1UL << 8)   /* clear err bit in PRB_8 reg */
+#define IECLR_PRB_0            (1UL << 0)   /* clear err bit in PRB_0 reg */
+
+/*
+ * IIO CRB control register Fields: IIO_ICCR 
+ */
+#define        IIO_ICCR_PENDING        (0x10000)
+#define        IIO_ICCR_CMD_MASK       (0xFF)
+#define        IIO_ICCR_CMD_SHFT       (7)
+#define        IIO_ICCR_CMD_NOP        (0x0)   /* No Op */
+#define        IIO_ICCR_CMD_WAKE       (0x100) /* Reactivate CRB entry and process */
+#define        IIO_ICCR_CMD_TIMEOUT    (0x200) /* Make CRB timeout & mark invalid */
+#define        IIO_ICCR_CMD_EJECT      (0x400) /* Contents of entry written to memory 
+                                        * via a WB
+                                        */
+#define        IIO_ICCR_CMD_FLUSH      (0x800)
+
+/*
+ *
+ * CRB Register description.
+ *
+ * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING
+ * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING
+ * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING
+ * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING
+ * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING
+ *
+ * Many of the fields in CRB are status bits used by hardware
+ * for implementation of the protocol. It's very dangerous to
+ * mess around with the CRB registers.
+ *
+ * It's OK to read the CRB registers and try to make sense out of the
+ * fields in CRB.
+ *
+ * Updating CRB requires all activities in Hub IIO to be quiesced.
+ * otherwise, a write to CRB could corrupt other CRB entries.
+ * CRBs are here only as a back door peek to shub IIO's status.
+ * Quiescing implies  no dmas no PIOs
+ * either directly from the cpu or from sn0net.
+ * this is not something that can be done easily. So, AVOID updating
+ * CRBs.
+ */
+
+/*
+ * Easy access macros for CRBs, all 5 registers (A-E)
+ */
+typedef ii_icrb0_a_u_t icrba_t;
+#define a_sidn          ii_icrb0_a_fld_s.ia_sidn
+#define a_tnum          ii_icrb0_a_fld_s.ia_tnum
+#define a_addr          ii_icrb0_a_fld_s.ia_addr
+#define a_valid         ii_icrb0_a_fld_s.ia_vld
+#define a_iow           ii_icrb0_a_fld_s.ia_iow
+#define a_regvalue     ii_icrb0_a_regval
+
+typedef ii_icrb0_b_u_t icrbb_t;
+#define b_use_old       ii_icrb0_b_fld_s.ib_use_old
+#define b_imsgtype      ii_icrb0_b_fld_s.ib_imsgtype
+#define b_imsg          ii_icrb0_b_fld_s.ib_imsg
+#define b_initiator     ii_icrb0_b_fld_s.ib_init
+#define b_exc           ii_icrb0_b_fld_s.ib_exc
+#define b_ackcnt        ii_icrb0_b_fld_s.ib_ack_cnt
+#define b_resp          ii_icrb0_b_fld_s.ib_resp
+#define b_ack           ii_icrb0_b_fld_s.ib_ack
+#define b_hold          ii_icrb0_b_fld_s.ib_hold
+#define b_wb            ii_icrb0_b_fld_s.ib_wb
+#define b_intvn         ii_icrb0_b_fld_s.ib_intvn
+#define b_stall_ib      ii_icrb0_b_fld_s.ib_stall_ib
+#define b_stall_int     ii_icrb0_b_fld_s.ib_stall__intr
+#define b_stall_bte_0   ii_icrb0_b_fld_s.ib_stall__bte_0
+#define b_stall_bte_1   ii_icrb0_b_fld_s.ib_stall__bte_1
+#define b_error         ii_icrb0_b_fld_s.ib_error
+#define b_ecode         ii_icrb0_b_fld_s.ib_errcode
+#define b_lnetuce       ii_icrb0_b_fld_s.ib_ln_uce
+#define b_mark          ii_icrb0_b_fld_s.ib_mark
+#define b_xerr          ii_icrb0_b_fld_s.ib_xt_err
+#define b_regvalue     ii_icrb0_b_regval
+
+typedef ii_icrb0_c_u_t icrbc_t;
+#define c_suppl         ii_icrb0_c_fld_s.ic_suppl
+#define c_barrop        ii_icrb0_c_fld_s.ic_bo
+#define c_doresp        ii_icrb0_c_fld_s.ic_resprqd
+#define c_gbr           ii_icrb0_c_fld_s.ic_gbr
+#define c_btenum        ii_icrb0_c_fld_s.ic_bte_num
+#define c_cohtrans      ii_icrb0_c_fld_s.ic_ct
+#define c_xtsize        ii_icrb0_c_fld_s.ic_size
+#define c_source        ii_icrb0_c_fld_s.ic_source
+#define c_regvalue     ii_icrb0_c_regval
+
+
+typedef ii_icrb0_d_u_t icrbd_t;
+#define d_sleep         ii_icrb0_d_fld_s.id_sleep
+#define d_pricnt        ii_icrb0_d_fld_s.id_pr_cnt
+#define d_pripsc        ii_icrb0_d_fld_s.id_pr_psc
+#define d_bteop         ii_icrb0_d_fld_s.id_bte_op
+#define d_bteaddr       ii_icrb0_d_fld_s.id_pa_be /* ic_pa_be fld has 2 names*/
+#define d_benable       ii_icrb0_d_fld_s.id_pa_be /* ic_pa_be fld has 2 names*/
+#define d_regvalue     ii_icrb0_d_regval
+
+typedef ii_icrb0_e_u_t icrbe_t;
+#define icrbe_ctxtvld   ii_icrb0_e_fld_s.ie_cvld
+#define icrbe_toutvld   ii_icrb0_e_fld_s.ie_tvld
+#define icrbe_context   ii_icrb0_e_fld_s.ie_context
+#define icrbe_timeout   ii_icrb0_e_fld_s.ie_timeout
+#define e_regvalue     ii_icrb0_e_regval
+
+
+/* Number of widgets supported by shub */
+#define HUB_NUM_WIDGET          9
+#define HUB_WIDGET_ID_MIN       0x8
+#define HUB_WIDGET_ID_MAX       0xf
+
+#define HUB_WIDGET_PART_NUM     0xc120
+#define MAX_HUBS_PER_XBOW       2
+
+/* A few more #defines for backwards compatibility */
+#define iprb_t          ii_iprb0_u_t
+#define iprb_regval     ii_iprb0_regval
+#define iprb_mult_err  ii_iprb0_fld_s.i_mult_err
+#define iprb_spur_rd   ii_iprb0_fld_s.i_spur_rd
+#define iprb_spur_wr   ii_iprb0_fld_s.i_spur_wr
+#define iprb_rd_to     ii_iprb0_fld_s.i_rd_to
+#define iprb_ovflow     ii_iprb0_fld_s.i_of_cnt
+#define iprb_error      ii_iprb0_fld_s.i_error
+#define iprb_ff         ii_iprb0_fld_s.i_f
+#define iprb_mode       ii_iprb0_fld_s.i_m
+#define iprb_bnakctr    ii_iprb0_fld_s.i_nb
+#define iprb_anakctr    ii_iprb0_fld_s.i_na
+#define iprb_xtalkctr   ii_iprb0_fld_s.i_c
+
+#define LNK_STAT_WORKING        0x2            /* LLP is working */
+
+#define IIO_WSTAT_ECRAZY        (1ULL << 32)    /* Hub gone crazy */
+#define IIO_WSTAT_TXRETRY       (1ULL << 9)     /* Hub Tx Retry timeout */
+#define IIO_WSTAT_TXRETRY_MASK  (0x7F)   /* should be 0xFF?? */
+#define IIO_WSTAT_TXRETRY_SHFT  (16)
+#define IIO_WSTAT_TXRETRY_CNT(w)        (((w) >> IIO_WSTAT_TXRETRY_SHFT) & \
+                                          IIO_WSTAT_TXRETRY_MASK)
+
+/* Number of II perf. counters we can multiplex at once */
+
+#define IO_PERF_SETS   32
+
+/* Bit for the widget in inbound access register */
+#define IIO_IIWA_WIDGET(_w)     ((uint64_t)(1ULL << _w))
+/* Bit for the widget in outbound access register */
+#define IIO_IOWA_WIDGET(_w)     ((uint64_t)(1ULL << _w))
+
+/* NOTE: The following define assumes that we are going to get
+ * widget numbers from 8 thru F and the device numbers within
+ * widget from 0 thru 7.
+ */
+#define IIO_IIDEM_WIDGETDEV_MASK(w, d)  ((uint64_t)(1ULL << (8 * ((w) - 8) + (d))))
+
+/* IO Interrupt Destination Register */
+#define IIO_IIDSR_SENT_SHIFT    28
+#define IIO_IIDSR_SENT_MASK     0x30000000
+#define IIO_IIDSR_ENB_SHIFT     24
+#define IIO_IIDSR_ENB_MASK      0x01000000
+#define IIO_IIDSR_NODE_SHIFT    9
+#define IIO_IIDSR_NODE_MASK     0x000ff700
+#define IIO_IIDSR_PI_ID_SHIFT   8
+#define IIO_IIDSR_PI_ID_MASK    0x00000100
+#define IIO_IIDSR_LVL_SHIFT     0
+#define IIO_IIDSR_LVL_MASK      0x000000ff
+
+/* Xtalk timeout threshhold register (IIO_IXTT) */
+#define IXTT_RRSP_TO_SHFT      55         /* read response timeout */
+#define IXTT_RRSP_TO_MASK      (0x1FULL << IXTT_RRSP_TO_SHFT)
+#define IXTT_RRSP_PS_SHFT      32         /* read responsed TO prescalar */
+#define IXTT_RRSP_PS_MASK      (0x7FFFFFULL << IXTT_RRSP_PS_SHFT)
+#define IXTT_TAIL_TO_SHFT      0          /* tail timeout counter threshold */
+#define IXTT_TAIL_TO_MASK      (0x3FFFFFFULL << IXTT_TAIL_TO_SHFT)
+
+/*
+ * The IO LLP control status register and widget control register
+ */
+
+typedef union hubii_wcr_u {
+        uint64_t      wcr_reg_value;
+        struct {
+         uint64_t      wcr_widget_id:   4,     /* LLP crossbar credit */
+                       wcr_tag_mode:    1,     /* Tag mode */
+                       wcr_rsvd1:       8,     /* Reserved */
+                       wcr_xbar_crd:    3,     /* LLP crossbar credit */
+                       wcr_f_bad_pkt:   1,     /* Force bad llp pkt enable */
+                       wcr_dir_con:     1,     /* widget direct connect */
+                       wcr_e_thresh:    5,     /* elasticity threshold */
+                       wcr_rsvd:       41;     /* unused */
+        } wcr_fields_s;
+} hubii_wcr_t;
+
+#define iwcr_dir_con    wcr_fields_s.wcr_dir_con
+
+/* The structures below are defined to extract and modify the ii
+performance registers */
+
+/* io_perf_sel allows the caller to specify what tests will be
+   performed */
+
+typedef union io_perf_sel {
+        uint64_t perf_sel_reg;
+        struct {
+               uint64_t        perf_ippr0 :  4,
+                               perf_ippr1 :  4,
+                               perf_icct  :  8,
+                               perf_rsvd  : 48;
+        } perf_sel_bits;
+} io_perf_sel_t;
+
+/* io_perf_cnt is to extract the count from the shub registers. Due to
+   hardware problems there is only one counter, not two. */
+
+typedef union io_perf_cnt {
+        uint64_t      perf_cnt;
+        struct {
+               uint64_t        perf_cnt   : 20,
+                               perf_rsvd2 : 12,
+                               perf_rsvd1 : 32;
+        } perf_cnt_bits;
+
+} io_perf_cnt_t;
+
+typedef union iprte_a {
+       uint64_t        entry;
+       struct {
+               uint64_t        i_rsvd_1                  :      3;
+               uint64_t        i_addr                    :     38;
+               uint64_t        i_init                    :      3;
+               uint64_t        i_source                  :      8;
+               uint64_t        i_rsvd                    :      2;
+               uint64_t        i_widget                  :      4;
+               uint64_t        i_to_cnt                  :      5;
+               uint64_t       i_vld                     :      1;
+       } iprte_fields;
+} iprte_a_t;
+
+#endif /* _ASM_IA64_SN_SHUBIO_H */
+
diff --git a/include/asm-m32r/cputime.h b/include/asm-m32r/cputime.h
new file mode 100644 (file)
index 0000000..0a47550
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __M32R_CPUTIME_H
+#define __M32R_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __M32R_CPUTIME_H */
diff --git a/include/asm-m68k/cputime.h b/include/asm-m68k/cputime.h
new file mode 100644 (file)
index 0000000..c79c5e8
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __M68K_CPUTIME_H
+#define __M68K_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __M68K_CPUTIME_H */
diff --git a/include/asm-m68k/hp300hw.h b/include/asm-m68k/hp300hw.h
new file mode 100644 (file)
index 0000000..d998ea6
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _M68K_HP300HW_H
+#define _M68K_HP300HW_H
+
+extern unsigned long hp300_model;
+
+/* This information was taken from NetBSD */
+#define        HP_320          (0)     /* 16MHz 68020+HP MMU+16K external cache */
+#define        HP_330          (1)     /* 16MHz 68020+68851 MMU */
+#define        HP_340          (2)     /* 16MHz 68030 */
+#define        HP_345          (3)     /* 50MHz 68030+32K external cache */
+#define        HP_350          (4)     /* 25MHz 68020+HP MMU+32K external cache */
+#define        HP_360          (5)     /* 25MHz 68030 */
+#define        HP_370          (6)     /* 33MHz 68030+64K external cache */
+#define        HP_375          (7)     /* 50MHz 68030+32K external cache */
+#define        HP_380          (8)     /* 25MHz 68040 */
+#define        HP_385          (9)     /* 33MHz 68040 */
+
+#define        HP_400          (10)    /* 50MHz 68030+32K external cache */
+#define        HP_425T         (11)    /* 25MHz 68040 - model 425t */
+#define        HP_425S         (12)    /* 25MHz 68040 - model 425s */
+#define HP_425E                (13)    /* 25MHz 68040 - model 425e */
+#define HP_433T                (14)    /* 33MHz 68040 - model 433t */
+#define HP_433S                (15)    /* 33MHz 68040 - model 433s */
+
+#endif /* _M68K_HP300HW_H */
diff --git a/include/asm-m68knommu/cputime.h b/include/asm-m68knommu/cputime.h
new file mode 100644 (file)
index 0000000..a0c4a66
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __M68KNOMMU_CPUTIME_H
+#define __M68KNOMMU_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __M68KNOMMU_CPUTIME_H */
diff --git a/include/asm-m68knommu/m527xsim.h b/include/asm-m68knommu/m527xsim.h
new file mode 100644 (file)
index 0000000..82a39bc
--- /dev/null
@@ -0,0 +1,38 @@
+/****************************************************************************/
+
+/*
+ *     m527xsim.h -- ColdFire 5270/5271 System Integration Module support.
+ *
+ *     (C) Copyright 2004, Greg Ungerer (gerg@snapgear.com)
+ */
+
+/****************************************************************************/
+#ifndef        m527xsim_h
+#define        m527xsim_h
+/****************************************************************************/
+
+#include <linux/config.h>
+
+/*
+ *     Define the 5270/5271 SIM register set addresses.
+ */
+#define        MCFICM_INTC0            0x0c00          /* Base for Interrupt Ctrl 0 */
+#define        MCFICM_INTC1            0x0d00          /* Base for Interrupt Ctrl 1 */
+#define        MCFINTC_IPRH            0x00            /* Interrupt pending 32-63 */
+#define        MCFINTC_IPRL            0x04            /* Interrupt pending 1-31 */
+#define        MCFINTC_IMRH            0x08            /* Interrupt mask 32-63 */
+#define        MCFINTC_IMRL            0x0c            /* Interrupt mask 1-31 */
+#define        MCFINTC_INTFRCH         0x10            /* Interrupt force 32-63 */
+#define        MCFINTC_INTFRCL         0x14            /* Interrupt force 1-31 */
+#define        MCFINTC_IRLR            0x18            /* */
+#define        MCFINTC_IACKL           0x19            /* */
+#define        MCFINTC_ICR0            0x40            /* Base ICR register */
+
+#define        MCFINT_VECBASE          64              /* Vector base number */
+#define        MCFINT_UART0            13              /* Interrupt number for UART0 */
+#define        MCFINT_UART1            14              /* Interrupt number for UART1 */
+#define        MCFINT_UART2            15              /* Interrupt number for UART2 */
+#define        MCFINT_PIT1             36              /* Interrupt number for PIT1 */
+
+/****************************************************************************/
+#endif /* m527xsim_h */
diff --git a/include/asm-m68knommu/m528xsim.h b/include/asm-m68knommu/m528xsim.h
new file mode 100644 (file)
index 0000000..bc91a4b
--- /dev/null
@@ -0,0 +1,36 @@
+/****************************************************************************/
+
+/*
+ *     m528xsim.h -- ColdFire 5280/5282 System Integration Module support.
+ *
+ *     (C) Copyright 2003, Greg Ungerer (gerg@snapgear.com)
+ */
+
+/****************************************************************************/
+#ifndef        m528xsim_h
+#define        m528xsim_h
+/****************************************************************************/
+
+#include <linux/config.h>
+
+/*
+ *     Define the 5280/5282 SIM register set addresses.
+ */
+#define        MCFICM_INTC0            0x0c00          /* Base for Interrupt Ctrl 0 */
+#define        MCFICM_INTC1            0x0d00          /* Base for Interrupt Ctrl 0 */
+#define        MCFINTC_IPRH            0x00            /* Interrupt pending 32-63 */
+#define        MCFINTC_IPRL            0x04            /* Interrupt pending 1-31 */
+#define        MCFINTC_IMRH            0x08            /* Interrupt mask 32-63 */
+#define        MCFINTC_IMRL            0x0c            /* Interrupt mask 1-31 */
+#define        MCFINTC_INTFRCH         0x10            /* Interrupt force 32-63 */
+#define        MCFINTC_INTFRCL         0x14            /* Interrupt force 1-31 */
+#define        MCFINTC_IRLR            0x18            /* */
+#define        MCFINTC_IACKL           0x19            /* */
+#define        MCFINTC_ICR0            0x40            /* Base ICR register */
+
+#define        MCFINT_VECBASE          64              /* Vector base number */
+#define        MCFINT_UART0            13              /* Interrupt number for UART0 */
+#define        MCFINT_PIT1             55              /* Interrupt number for PIT1 */
+
+/****************************************************************************/
+#endif /* m528xsim_h */
diff --git a/include/asm-m68knommu/mcfcache.h b/include/asm-m68knommu/mcfcache.h
new file mode 100644 (file)
index 0000000..136587b
--- /dev/null
@@ -0,0 +1,125 @@
+/****************************************************************************/
+
+/*
+ *     mcfcache.h -- ColdFire CPU cache support code
+ *
+ *     (C) Copyright 2004, Greg Ungerer <gerg@snapgear.com>
+ */
+
+/****************************************************************************/
+#ifndef        __M68KNOMMU_MCFCACHE_H
+#define        __M68KNOMMU_MCFCACHE_H
+/****************************************************************************/
+
+#include <linux/config.h>
+
+/*
+ *     The different ColdFire families have different cache arrangments.
+ *     Everything from a small linstruction only cache, to configurable
+ *     data and/or instruction cache, to unified instruction/data, to 
+ *     harvard style separate instruction and data caches.
+ */
+
+#if defined(CONFIG_M5206) || defined(CONFIG_M5206e) || defined(CONFIG_M5272)
+/*
+ *     Simple verion 2 core cache. These have instruction cache only,
+ *     we just need to invalidate it and enable it.
+ */
+.macro CACHE_ENABLE
+       movel   #0x01000000,%d0         /* invalidate cache cmd */
+       movec   %d0,%CACR               /* do invalidate cache */
+       movel   #0x80000100,%d0         /* setup cache mask */
+       movec   %d0,%CACR               /* enable cache */
+.endm
+#endif /* CONFIG_M5206 || CONFIG_M5206e || CONFIG_M5272 */
+
+#if defined(CONFIG_M527x)
+/*
+ *     New version 2 cores have a configurable split cache arrangement.
+ *     For now I am just enabling instruction cache - but ultimately I
+ *     think a split instruction/data cache would be better.
+ */
+.macro CACHE_ENABLE
+       movel   #0x01400000,%d0
+       movec   %d0,%CACR               /* invalidate cache */
+       nop
+       movel   #0x0000c000,%d0         /* set SDRAM cached only */
+       movec   %d0,%ACR0
+       movel   #0x00000000,%d0         /* no other regions cached */
+       movec   %d0,%ACR1
+       movel   #0x80400100,%d0         /* configure cache */
+       movec   %d0,%CACR               /* enable cache */
+       nop
+.endm
+#endif /* CONFIG_M527x */
+
+#if defined(CONFIG_M528x)
+/*
+ *     Cache is totally broken on early 5282 silicon. So far now we
+ *     disable its cache all together.
+ */
+.macro CACHE_ENABLE
+       movel   #0x01000000,%d0
+       movec   %d0,%CACR               /* invalidate cache */
+       nop
+       movel   #0x0000c000,%d0         /* set SDRAM cached only */
+       movec   %d0,%ACR0
+       movel   #0x00000000,%d0         /* no other regions cached */
+       movec   %d0,%ACR1
+       movel   #0x00000000,%d0         /* configure cache */
+       movec   %d0,%CACR               /* enable cache */
+       nop
+.endm
+#endif /* CONFIG_M528x */
+
+#if defined(CONFIG_M5249) || defined(CONFIG_M5307)
+/*
+ *     The version 3 core cache. Oddly enough the version 2 core 5249
+ *     has the same SDRAM and cache setup as the version 3 cores.
+ *     This is a single unified instruction/data cache.
+ */
+.macro CACHE_ENABLE
+       movel   #0x01000000,%d0         /* invalidate whole cache */
+       movec   %d0,%CACR
+       nop
+#if defined(DEBUGGER_COMPATIBLE_CACHE) || defined(CONFIG_SECUREEDGEMP3)
+       movel   #0x0000c000,%d0         /* set SDRAM cached (write-thru) */
+#else
+       movel   #0x0000c020,%d0         /* set SDRAM cached (copyback) */
+#endif
+       movec   %d0,%ACR0
+       movel   #0x00000000,%d0         /* no other regions cached */
+       movec   %d0,%ACR1
+       movel   #0xa0000200,%d0         /* enable cache */
+       movec   %d0,%CACR
+       nop
+.endm
+#endif /* CONFIG_M5249 || CONFIG_M5307 */
+
+#if defined(CONFIG_M5407)
+/*
+ *     Version 4 cores have a true hardvard style separate instruction
+ *     and data cache. Invalidate and enable cache, also enable write
+ *     buffers and branch accelerator.
+ */
+.macro CACHE_ENABLE
+       movel   #0x01040100,%d0         /* invalidate whole cache */
+       movec   %d0,%CACR
+       nop
+       movel   #0x000fc000,%d0         /* set SDRAM cached only */
+       movec   %d0, %ACR0
+       movel   #0x00000000,%d0         /* no other regions cached */
+       movec   %d0, %ACR1
+       movel   #0x000fc000,%d0         /* set SDRAM cached only */
+       movec   %d0, %ACR2
+       movel   #0x00000000,%d0         /* no other regions cached */
+       movec   %d0, %ACR3
+       movel   #0xb6088400,%d0         /* enable caches */
+       movec   %d0,%CACR
+       nop
+.endm
+#endif /* CONFIG_M5407 */
+
+
+/****************************************************************************/
+#endif /* __M68KNOMMU_MCFCACHE_H */
diff --git a/include/asm-mips/compiler.h b/include/asm-mips/compiler.h
new file mode 100644 (file)
index 0000000..169ae26
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2004  Maciej W. Rozycki
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#ifndef _ASM_COMPILER_H
+#define _ASM_COMPILER_H
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define GCC_REG_ACCUM "$0"
+#else
+#define GCC_REG_ACCUM "accum"
+#endif
+
+#endif /* _ASM_COMPILER_H */
diff --git a/include/asm-mips/cpu-info.h b/include/asm-mips/cpu-info.h
new file mode 100644 (file)
index 0000000..984277a
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994 Waldorf GMBH
+ * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2001, 2002, 2003 Ralf Baechle
+ * Copyright (C) 1996 Paul M. Antoine
+ * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
+ */
+#ifndef __ASM_CPU_INFO_H
+#define __ASM_CPU_INFO_H
+
+#include <linux/config.h>
+#include <asm/cache.h>
+
+#ifdef CONFIG_SGI_IP27
+#include <asm/sn/types.h>
+#endif
+
+/*
+ * Descriptor for a cache
+ */
+struct cache_desc {
+       unsigned short linesz;  /* Size of line in bytes */
+       unsigned short ways;    /* Number of ways */
+       unsigned short sets;    /* Number of lines per set */
+       unsigned int waysize;   /* Bytes per way */
+       unsigned int waybit;    /* Bits to select in a cache set */
+       unsigned int flags;     /* Flags describing cache properties */
+};
+
+/*
+ * Flag definitions
+ */
+#define MIPS_CACHE_NOT_PRESENT 0x00000001
+#define MIPS_CACHE_VTAG                0x00000002      /* Virtually tagged cache */
+#define MIPS_CACHE_ALIASES     0x00000004      /* Cache could have aliases */
+#define MIPS_CACHE_IC_F_DC     0x00000008      /* Ic can refill from D-cache */
+
+struct cpuinfo_mips {
+       unsigned long           udelay_val;
+       unsigned long           asid_cache;
+#if defined(CONFIG_SGI_IP27)
+//     cpuid_t         p_cpuid;        /* PROM assigned cpuid */
+       cnodeid_t       p_nodeid;       /* my node ID in compact-id-space */
+       nasid_t         p_nasid;        /* my node ID in numa-as-id-space */
+       unsigned char   p_slice;        /* Physical position on node board */
+#endif
+#if 0
+       unsigned long           loops_per_sec;
+       unsigned long           ipi_count;
+       unsigned long           irq_attempt[NR_IRQS];
+       unsigned long           smp_local_irq_count;
+       unsigned long           prof_multiplier;
+       unsigned long           prof_counter;
+#endif
+
+       /*
+        * Capability and feature descriptor structure for MIPS CPU
+        */
+       unsigned long           options;
+       unsigned int            processor_id;
+       unsigned int            fpu_id;
+       unsigned int            cputype;
+       int                     isa_level;
+       int                     tlbsize;
+       struct cache_desc       icache; /* Primary I-cache */
+       struct cache_desc       dcache; /* Primary D or combined I/D cache */
+       struct cache_desc       scache; /* Secondary cache */
+       struct cache_desc       tcache; /* Tertiary/split secondary cache */
+       void                    *data;  /* Additional data */
+} __attribute__((aligned(SMP_CACHE_BYTES)));
+
+extern struct cpuinfo_mips cpu_data[];
+#define current_cpu_data cpu_data[smp_processor_id()]
+
+extern void cpu_probe(void);
+extern void cpu_report(void);
+
+#endif /* __ASM_CPU_INFO_H */
diff --git a/include/asm-mips/cputime.h b/include/asm-mips/cputime.h
new file mode 100644 (file)
index 0000000..c00eacb
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __MIPS_CPUTIME_H
+#define __MIPS_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __MIPS_CPUTIME_H */
diff --git a/include/asm-mips/dec/serial.h b/include/asm-mips/dec/serial.h
new file mode 100644 (file)
index 0000000..acad758
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *     include/asm-mips/dec/serial.h
+ *
+ *     Definitions common to all DECstation serial devices.
+ *
+ *     Copyright (C) 2004  Maciej W. Rozycki
+ *
+ *     Based on bits extracted from drivers/tc/zs.h for which
+ *     the following copyrights apply:
+ *
+ *     Copyright (C) 1995  David S. Miller (davem@caip.rutgers.edu)
+ *     Copyright (C) 1996  Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
+ *     Copyright (C)       Harald Koerfgen
+ *
+ *     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 __ASM_MIPS_DEC_SERIAL_H
+#define __ASM_MIPS_DEC_SERIAL_H
+
+struct dec_serial_hook {
+       int (*init_channel)(void *handle);
+       void (*init_info)(void *handle);
+       void (*rx_char)(unsigned char ch, unsigned char fl);
+       int (*poll_rx_char)(void *handle);
+       int (*poll_tx_char)(void *handle, unsigned char ch);
+       unsigned int cflags;
+};
+
+extern int register_dec_serial_hook(unsigned int channel,
+                                   struct dec_serial_hook *hook);
+extern int unregister_dec_serial_hook(unsigned int channel);
+
+#endif /* __ASM_MIPS_DEC_SERIAL_H */
diff --git a/include/asm-mips/interrupt.h b/include/asm-mips/interrupt.h
new file mode 100644 (file)
index 0000000..e8357f5
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994, 95, 96, 97, 98, 99, 2003 by Ralf Baechle
+ * Copyright (C) 1996 by Paul M. Antoine
+ * Copyright (C) 1999 Silicon Graphics
+ * Copyright (C) 2000 MIPS Technologies, Inc.
+ */
+#ifndef _ASM_INTERRUPT_H
+#define _ASM_INTERRUPT_H
+
+#include <asm/hazards.h>
+
+__asm__ (
+       ".macro\tlocal_irq_enable\n\t"
+       ".set\tpush\n\t"
+       ".set\treorder\n\t"
+       ".set\tnoat\n\t"
+       "mfc0\t$1,$12\n\t"
+       "ori\t$1,0x1f\n\t"
+       "xori\t$1,0x1e\n\t"
+       "mtc0\t$1,$12\n\t"
+       "irq_enable_hazard\n\t"
+       ".set\tpop\n\t"
+       ".endm");
+
+static inline void local_irq_enable(void)
+{
+       __asm__ __volatile__(
+               "local_irq_enable"
+               : /* no outputs */
+               : /* no inputs */
+               : "memory");
+}
+
+/*
+ * For cli() we have to insert nops to make sure that the new value
+ * has actually arrived in the status register before the end of this
+ * macro.
+ * R4000/R4400 need three nops, the R4600 two nops and the R10000 needs
+ * no nops at all.
+ */
+__asm__ (
+       ".macro\tlocal_irq_disable\n\t"
+       ".set\tpush\n\t"
+       ".set\tnoat\n\t"
+       "mfc0\t$1,$12\n\t"
+       "ori\t$1,1\n\t"
+       "xori\t$1,1\n\t"
+       ".set\tnoreorder\n\t"
+       "mtc0\t$1,$12\n\t"
+       "irq_disable_hazard\n\t"
+       ".set\tpop\n\t"
+       ".endm");
+
+static inline void local_irq_disable(void)
+{
+       __asm__ __volatile__(
+               "local_irq_disable"
+               : /* no outputs */
+               : /* no inputs */
+               : "memory");
+}
+
+__asm__ (
+       ".macro\tlocal_save_flags flags\n\t"
+       ".set\tpush\n\t"
+       ".set\treorder\n\t"
+       "mfc0\t\\flags, $12\n\t"
+       ".set\tpop\n\t"
+       ".endm");
+
+#define local_save_flags(x)                                            \
+__asm__ __volatile__(                                                  \
+       "local_save_flags %0"                                           \
+       : "=r" (x))
+
+__asm__ (
+       ".macro\tlocal_irq_save result\n\t"
+       ".set\tpush\n\t"
+       ".set\treorder\n\t"
+       ".set\tnoat\n\t"
+       "mfc0\t\\result, $12\n\t"
+       "ori\t$1, \\result, 1\n\t"
+       "xori\t$1, 1\n\t"
+       ".set\tnoreorder\n\t"
+       "mtc0\t$1, $12\n\t"
+       "irq_disable_hazard\n\t"
+       ".set\tpop\n\t"
+       ".endm");
+
+#define local_irq_save(x)                                              \
+__asm__ __volatile__(                                                  \
+       "local_irq_save\t%0"                                            \
+       : "=r" (x)                                                      \
+       : /* no inputs */                                               \
+       : "memory")
+
+__asm__ (
+       ".macro\tlocal_irq_restore flags\n\t"
+       ".set\tnoreorder\n\t"
+       ".set\tnoat\n\t"
+       "mfc0\t$1, $12\n\t"
+       "andi\t\\flags, 1\n\t"
+       "ori\t$1, 1\n\t"
+       "xori\t$1, 1\n\t"
+       "or\t\\flags, $1\n\t"
+       "mtc0\t\\flags, $12\n\t"
+       "irq_disable_hazard\n\t"
+       ".set\tat\n\t"
+       ".set\treorder\n\t"
+       ".endm");
+
+#define local_irq_restore(flags)                                       \
+do {                                                                   \
+       unsigned long __tmp1;                                           \
+                                                                       \
+       __asm__ __volatile__(                                           \
+               "local_irq_restore\t%0"                                 \
+               : "=r" (__tmp1)                                         \
+               : "0" (flags)                                           \
+               : "memory");                                            \
+} while(0)
+
+#define irqs_disabled()                                                        \
+({                                                                     \
+       unsigned long flags;                                            \
+       local_save_flags(flags);                                        \
+       !(flags & 1);                                                   \
+})
+
+#endif /* _ASM_INTERRUPT_H */
diff --git a/include/asm-mips/m48t37.h b/include/asm-mips/m48t37.h
new file mode 100644 (file)
index 0000000..cabf862
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  Registers for the SGS-Thomson M48T37 Timekeeper RAM chip
+ */
+#ifndef _ASM_M48T37_H
+#define _ASM_M48T37_H
+
+#include <linux/spinlock.h>
+
+extern spinlock_t rtc_lock;
+
+struct m48t37_rtc {
+       volatile u8     pad[0x7ff0];    /* NVRAM */
+       volatile u8     flags;
+       volatile u8     century;
+       volatile u8     alarm_sec;
+       volatile u8     alarm_min;
+       volatile u8     alarm_hour;
+       volatile u8     alarm_data;
+       volatile u8     interrupts;
+       volatile u8     watchdog;
+       volatile u8     control;
+       volatile u8     sec;
+       volatile u8     min;
+       volatile u8     hour;
+       volatile u8     day;
+       volatile u8     date;
+       volatile u8     month;
+       volatile u8     year;
+};
+
+#define M48T37_RTC_SET         0x80
+#define M48T37_RTC_STOPPED     0x80
+#define M48T37_RTC_READ                0x40
+
+#endif /* _ASM_M48T37_H */
diff --git a/include/asm-mips/mach-ip22/spaces.h b/include/asm-mips/mach-ip22/spaces.h
new file mode 100644 (file)
index 0000000..30d42fc
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994 - 1999, 2000, 03, 04 Ralf Baechle
+ * Copyright (C) 2000, 2002  Maciej W. Rozycki
+ * Copyright (C) 1990, 1999, 2000 Silicon Graphics, Inc.
+ */
+#ifndef _ASM_MACH_IP22_SPACES_H
+#define _ASM_MACH_IP22_SPACES_H
+
+#include <linux/config.h>
+
+#ifdef CONFIG_MIPS32
+
+#define CAC_BASE               0x80000000
+#define IO_BASE                        0xa0000000
+#define UNCAC_BASE             0xa0000000
+#define MAP_BASE               0xc0000000
+
+/*
+ * This handles the memory map.
+ * We handle pages at KSEG0 for kernels with 32 bit address space.
+ */
+#define PAGE_OFFSET            0x80000000UL
+
+/*
+ * Memory above this physical address will be considered highmem.
+ */
+#ifndef HIGHMEM_START
+#define HIGHMEM_START          0x20000000UL
+#endif
+
+#endif /* CONFIG_MIPS32 */
+
+#ifdef CONFIG_MIPS64
+#define PAGE_OFFSET            0xffffffff80000000UL
+
+#ifndef HIGHMEM_START
+#define HIGHMEM_START          (1UL << 59UL)
+#endif
+
+#define CAC_BASE               0xffffffff80000000
+#define IO_BASE                        0xffffffffa0000000
+#define UNCAC_BASE             0xffffffffa0000000
+#define MAP_BASE               0xffffffffc0000000
+
+#define TO_PHYS(x)             (             ((x) & TO_PHYS_MASK))
+#define TO_CAC(x)              (CAC_BASE   | ((x) & TO_PHYS_MASK))
+#define TO_UNCAC(x)            (UNCAC_BASE | ((x) & TO_PHYS_MASK))
+
+#endif /* CONFIG_MIPS64 */
+
+#endif /* __ASM_MACH_IP22_SPACES_H */
diff --git a/include/asm-mips/mach-ip32/cpu-feature-overrides.h b/include/asm-mips/mach-ip32/cpu-feature-overrides.h
new file mode 100644 (file)
index 0000000..b932237
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005 Ilya A. Volynets-Evenbakh
+ * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org)
+ */
+#ifndef __ASM_MACH_IP32_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_IP32_CPU_FEATURE_OVERRIDES_H
+
+#include <linux/config.h>
+
+/*
+ * R5000 has an interesting "restriction":  ll(d)/sc(d)
+ * instructions to XKPHYS region simply do uncached bus
+ * requests. This breaks all the atomic bitops functions.
+ * so, for 64bit IP32 kernel we just don't use ll/sc.
+ * This does not affect luserland.
+ */
+#if defined(CONFIG_CPU_R5000) && defined(CONFIG_MIPS64)
+#define cpu_has_llsc           0
+#else
+#define cpu_has_llsc           1
+#endif
+
+/* Settings which are common for all ip32 CPUs */
+#define cpu_has_tlb            1
+#define cpu_has_4kex           1
+#define cpu_has_fpu            1
+#define cpu_has_32fpr          1
+#define cpu_has_counter                1
+#define cpu_has_mips16         0
+#define cpu_has_vce            0
+#define cpu_has_cache_cdex_s   0
+#define cpu_has_mcheck         0
+#define cpu_has_ejtag          0
+#define cpu_has_vtag_icache    0
+#define cpu_has_ic_fills_f_dc  0
+
+#endif /* __ASM_MACH_IP32_CPU_FEATURE_OVERRIDES_H */
diff --git a/include/asm-mips/mach-ip32/spaces.h b/include/asm-mips/mach-ip32/spaces.h
new file mode 100644 (file)
index 0000000..649864c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994 - 1999, 2000, 03, 04 Ralf Baechle
+ * Copyright (C) 2000, 2002  Maciej W. Rozycki
+ * Copyright (C) 1990, 1999, 2000 Silicon Graphics, Inc.
+ */
+#ifndef _ASM_MACH_IP32_SPACES_H
+#define _ASM_MACH_IP32_SPACES_H
+
+#include <linux/config.h>
+
+/*
+ * This handles the memory map.
+ */
+#define PAGE_OFFSET            0xffffffff80000000
+
+/*
+ * Memory above this physical address will be considered highmem.
+ * Fixme: 59 bits is a fictive number and makes assumptions about processors
+ * in the distant future.  Nobody will care for a few years :-)
+ */
+#ifndef HIGHMEM_START
+#define HIGHMEM_START          (1UL << 59UL)
+#endif
+
+#ifdef CONFIG_DMA_NONCOHERENT
+#define CAC_BASE               0x9800000000000000
+#else
+#define CAC_BASE               0xa800000000000000
+#endif
+#define IO_BASE                        0x9000000000000000
+#define UNCAC_BASE             0x9000000000000000
+#define MAP_BASE               0xc000000000000000
+
+#define TO_PHYS(x)             (             ((x) & TO_PHYS_MASK))
+#define TO_CAC(x)              (CAC_BASE   | ((x) & TO_PHYS_MASK))
+#define TO_UNCAC(x)            (UNCAC_BASE | ((x) & TO_PHYS_MASK))
+
+#endif /* __ASM_MACH_IP32_SPACES_H */
diff --git a/include/asm-mips/mach-mips/cpu-feature-overrides.h b/include/asm-mips/mach-mips/cpu-feature-overrides.h
new file mode 100644 (file)
index 0000000..2aba654
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003, 2004 Chris Dearman
+ */
+#ifndef __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H
+
+/*
+ * CPU feature overrides for MIPS boards
+ */
+#ifdef CONFIG_CPU_MIPS32
+#define cpu_has_tlb            1
+#define cpu_has_4kex           1
+#define cpu_has_4ktlb          1
+/* #define cpu_has_fpu         ? */
+/* #define cpu_has_32fpr       ? */
+#define cpu_has_counter                1
+/* #define cpu_has_watch       ? */
+#define cpu_has_divec          1
+#define cpu_has_vce            0
+/* #define cpu_has_cache_cdex_p        ? */
+/* #define cpu_has_cache_cdex_s        ? */
+/* #define cpu_has_prefetch    ? */
+#define cpu_has_mcheck         1
+/* #define cpu_has_ejtag       ? */
+#define cpu_has_llsc           1
+/* #define cpu_has_vtag_icache ? */
+/* #define cpu_has_dc_aliases  ? */
+/* #define cpu_has_ic_fills_f_dc ? */
+#define cpu_has_nofpuex                0
+/* #define cpu_has_64bits      ? */
+/* #define cpu_has_64bit_zero_reg ? */
+/* #define cpu_has_subset_pcaches ? */
+#endif
+
+#ifdef CONFIG_CPU_MIPS64
+#define cpu_has_tlb            1
+#define cpu_has_4kex           1
+#define cpu_has_4ktlb          1
+/* #define cpu_has_fpu         ? */
+/* #define cpu_has_32fpr       ? */
+#define cpu_has_counter                1
+/* #define cpu_has_watch       ? */
+#define cpu_has_divec          1
+#define cpu_has_vce            0
+/* #define cpu_has_cache_cdex_p        ? */
+/* #define cpu_has_cache_cdex_s        ? */
+/* #define cpu_has_prefetch    ? */
+#define cpu_has_mcheck         1
+/* #define cpu_has_ejtag       ? */
+#define cpu_has_llsc           1
+/* #define cpu_has_vtag_icache ? */
+/* #define cpu_has_dc_aliases  ? */
+/* #define cpu_has_ic_fills_f_dc ? */
+#define cpu_has_nofpuex                0
+/* #define cpu_has_64bits      ? */
+/* #define cpu_has_64bit_zero_reg ? */
+/* #define cpu_has_subset_pcaches ? */
+#endif
+
+#endif /* __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H */
diff --git a/include/asm-mips/mach-ocelot3/cpu-feature-overrides.h b/include/asm-mips/mach-ocelot3/cpu-feature-overrides.h
new file mode 100644 (file)
index 0000000..6cebe12
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Author: Manish Lachwani, mlachwani@mvista.com
+ * Copyright (C) 2004 Ralf Baechle
+ */
+#ifndef __ASM_MACH_JA_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_JA_CPU_FEATURE_OVERRIDES_H
+
+/*
+ * Momentum Ocelot-3 is based on Rm7900 processor which
+ * is based on the E9000 core.
+ */
+#define cpu_has_watch          1
+#define cpu_has_mips16         0
+#define cpu_has_divec          0
+#define cpu_has_vce            0
+#define cpu_has_cache_cdex_p   0
+#define cpu_has_cache_cdex_s   0
+#define cpu_has_prefetch       1
+#define cpu_has_mcheck         0
+#define cpu_has_ejtag          0
+
+#define cpu_has_llsc           1
+#define cpu_has_vtag_icache    0
+#define cpu_has_dc_aliases     0
+#define cpu_has_ic_fills_f_dc  0
+
+#define cpu_has_nofpuex        0
+#define cpu_has_64bits         1
+
+#define cpu_has_subset_pcaches 0
+
+#define cpu_dcache_line_size() 32
+#define cpu_icache_line_size() 32
+#define cpu_scache_line_size() 32
+
+/*
+ * On the RM9000 we need to ensure that I-cache lines being fetches only
+ * contain valid instructions are funny things will happen.
+ */
+#define PLAT_TRAMPOLINE_STUFF_LINE     32UL
+
+#endif /* __ASM_MACH_JA_CPU_FEATURE_OVERRIDES_H */
diff --git a/include/asm-mips/mach-sibyte/cpu-feature-overrides.h b/include/asm-mips/mach-sibyte/cpu-feature-overrides.h
new file mode 100644 (file)
index 0000000..5bb8b89
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003, 2004 Ralf Baechle
+ */
+#ifndef __ASM_MACH_SIBYTE_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_SIBYTE_CPU_FEATURE_OVERRIDES_H
+
+/*
+ * Sibyte are MIPS64 processors weired to a specific configuration
+ */
+#define cpu_has_watch          1
+#define cpu_has_mips16         0
+#define cpu_has_divec          1
+#define cpu_has_vce            0
+#define cpu_has_cache_cdex_p   0
+#define cpu_has_cache_cdex_s   0
+#define cpu_has_prefetch       1
+#define cpu_has_mcheck         1
+#define cpu_has_ejtag          1
+
+#define cpu_has_llsc           1
+#define cpu_has_vtag_icache    1
+#define cpu_has_dc_aliases     0
+#define cpu_has_ic_fills_f_dc  0
+
+#define cpu_has_nofpuex                0
+#define cpu_has_64bits         1
+
+#define cpu_has_subset_pcaches 0
+
+#define cpu_dcache_line_size() 32
+#define cpu_icache_line_size() 32
+#define cpu_scache_line_size() 32
+
+#endif /* __ASM_MACH_SIBYTE_CPU_FEATURE_OVERRIDES_H */
diff --git a/include/asm-mips/msc01_ic.h b/include/asm-mips/msc01_ic.h
new file mode 100644 (file)
index 0000000..64f1720
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * PCI Register definitions for the MIPS System Controller.
+ *
+ * Copyright (C) 2004 MIPS Technologies, Inc.  All rights reserved.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef __ASM_MIPS_BOARDS_MSC01_IC_H
+#define __ASM_MIPS_BOARDS_MSC01_IC_H
+
+/*****************************************************************************
+ * Register offset addresses
+ *****************************************************************************/
+
+#define MSC01_IC_RST_OFS     0x00008    /* Software reset              */
+#define MSC01_IC_ENAL_OFS    0x00100    /* Int_in enable mask 31:0     */
+#define MSC01_IC_ENAH_OFS    0x00108    /* Int_in enable mask 63:32    */
+#define MSC01_IC_DISL_OFS    0x00120    /* Int_in disable mask 31:0    */
+#define MSC01_IC_DISH_OFS    0x00128    /* Int_in disable mask 63:32   */
+#define MSC01_IC_ISBL_OFS    0x00140    /* Raw int_in 31:0             */
+#define MSC01_IC_ISBH_OFS    0x00148    /* Raw int_in 63:32            */
+#define MSC01_IC_ISAL_OFS    0x00160    /* Masked int_in 31:0          */
+#define MSC01_IC_ISAH_OFS    0x00168    /* Masked int_in 63:32         */
+#define MSC01_IC_LVL_OFS     0x00180    /* Disable priority int_out    */
+#define MSC01_IC_RAMW_OFS    0x00180    /* Shadow set RAM (EI)        */
+#define MSC01_IC_OSB_OFS     0x00188    /* Raw int_out                 */
+#define MSC01_IC_OSA_OFS     0x00190    /* Masked int_out              */
+#define MSC01_IC_GENA_OFS    0x00198    /* Global HW int enable        */
+#define MSC01_IC_BASE_OFS    0x001a0    /* Base address of IC_VEC      */
+#define MSC01_IC_VEC_OFS     0x001b0    /* Active int's vector address */
+#define MSC01_IC_EOI_OFS     0x001c0    /* Enable lower level ints     */
+#define MSC01_IC_CFG_OFS     0x001c8    /* Configuration register      */
+#define MSC01_IC_TRLD_OFS    0x001d0    /* Interval timer reload val   */
+#define MSC01_IC_TVAL_OFS    0x001e0    /* Interval timer current val  */
+#define MSC01_IC_TCFG_OFS    0x001f0    /* Interval timer config       */
+#define MSC01_IC_SUP_OFS     0x00200    /* Set up int_in line 0        */
+#define MSC01_IC_ENA_OFS     0x00800    /* Int_in enable mask 63:0     */
+#define MSC01_IC_DIS_OFS     0x00820    /* Int_in disable mask 63:0    */
+#define MSC01_IC_ISB_OFS     0x00840    /* Raw int_in 63:0             */
+#define MSC01_IC_ISA_OFS     0x00860    /* Masked int_in 63:0          */
+
+/*****************************************************************************
+ * Register field encodings
+ *****************************************************************************/
+
+#define MSC01_IC_RST_RST_SHF      0
+#define MSC01_IC_RST_RST_MSK      0x00000001
+#define MSC01_IC_RST_RST_BIT      MSC01_IC_RST_RST_MSK
+#define MSC01_IC_LVL_LVL_SHF      0
+#define MSC01_IC_LVL_LVL_MSK      0x000000ff
+#define MSC01_IC_LVL_SPUR_SHF     16
+#define MSC01_IC_LVL_SPUR_MSK     0x00010000
+#define MSC01_IC_LVL_SPUR_BIT     MSC01_IC_LVL_SPUR_MSK
+#define MSC01_IC_RAMW_RIPL_SHF   0
+#define MSC01_IC_RAMW_RIPL_MSK   0x0000003f
+#define MSC01_IC_RAMW_DATA_SHF   6
+#define MSC01_IC_RAMW_DATA_MSK   0x00000fc0
+#define MSC01_IC_RAMW_ADDR_SHF   25
+#define MSC01_IC_RAMW_ADDR_MSK   0x7e000000
+#define MSC01_IC_RAMW_READ_SHF   31
+#define MSC01_IC_RAMW_READ_MSK   0x80000000
+#define MSC01_IC_RAMW_READ_BIT   MSC01_IC_RAMW_READ_MSK
+#define MSC01_IC_OSB_OSB_SHF      0
+#define MSC01_IC_OSB_OSB_MSK      0x000000ff
+#define MSC01_IC_OSA_OSA_SHF      0
+#define MSC01_IC_OSA_OSA_MSK      0x000000ff
+#define MSC01_IC_GENA_GENA_SHF    0
+#define MSC01_IC_GENA_GENA_MSK    0x00000001
+#define MSC01_IC_GENA_GENA_BIT    MSC01_IC_GENA_GENA_MSK
+#define MSC01_IC_CFG_DIS_SHF      0
+#define MSC01_IC_CFG_DIS_MSK      0x00000001
+#define MSC01_IC_CFG_DIS_BIT      MSC01_IC_CFG_DIS_MSK
+#define MSC01_IC_CFG_SHFT_SHF     8
+#define MSC01_IC_CFG_SHFT_MSK     0x00000f00
+#define MSC01_IC_TCFG_ENA_SHF     0
+#define MSC01_IC_TCFG_ENA_MSK     0x00000001
+#define MSC01_IC_TCFG_ENA_BIT     MSC01_IC_TCFG_ENA_MSK
+#define MSC01_IC_TCFG_INT_SHF     8
+#define MSC01_IC_TCFG_INT_MSK     0x00000100
+#define MSC01_IC_TCFG_INT_BIT     MSC01_IC_TCFG_INT_MSK
+#define MSC01_IC_TCFG_EDGE_SHF    16
+#define MSC01_IC_TCFG_EDGE_MSK    0x00010000
+#define MSC01_IC_TCFG_EDGE_BIT    MSC01_IC_TCFG_EDGE_MSK
+#define MSC01_IC_SUP_PRI_SHF      0
+#define MSC01_IC_SUP_PRI_MSK      0x00000007
+#define MSC01_IC_SUP_EDGE_SHF     8
+#define MSC01_IC_SUP_EDGE_MSK     0x00000100
+#define MSC01_IC_SUP_EDGE_BIT     MSC01_IC_SUP_EDGE_MSK
+#define MSC01_IC_SUP_STEP         8
+
+/*
+ * MIPS System controller interrupt register base.
+ *
+ * FIXME - are these macros specific to Malta and co or to the MSC?  If the
+ * latter, they should be moved elsewhere.
+ */
+#define MIPS_MSC01_IC_REG_BASE 0x1bc40000
+
+/*****************************************************************************
+ * Absolute register addresses
+ *****************************************************************************/
+
+#define MSC01_IC_RST     (MSC01_IC_REG_BASE + MSC01_IC_RST_OFS)
+#define MSC01_IC_ENAL    (MSC01_IC_REG_BASE + MSC01_IC_ENAL_OFS)
+#define MSC01_IC_ENAH    (MSC01_IC_REG_BASE + MSC01_IC_ENAH_OFS)
+#define MSC01_IC_DISL    (MSC01_IC_REG_BASE + MSC01_IC_DISL_OFS)
+#define MSC01_IC_DISH    (MSC01_IC_REG_BASE + MSC01_IC_DISH_OFS)
+#define MSC01_IC_ISBL    (MSC01_IC_REG_BASE + MSC01_IC_ISBL_OFS)
+#define MSC01_IC_ISBH    (MSC01_IC_REG_BASE + MSC01_IC_ISBH_OFS)
+#define MSC01_IC_ISAL    (MSC01_IC_REG_BASE + MSC01_IC_ISAL_OFS)
+#define MSC01_IC_ISAH    (MSC01_IC_REG_BASE + MSC01_IC_ISAH_OFS)
+#define MSC01_IC_LVL     (MSC01_IC_REG_BASE + MSC01_IC_LVL_OFS)
+#define MSC01_IC_RAMW    (MSC01_IC_REG_BASE + MSC01_IC_RAMW_OFS)
+#define MSC01_IC_OSB     (MSC01_IC_REG_BASE + MSC01_IC_OSB_OFS)
+#define MSC01_IC_OSA     (MSC01_IC_REG_BASE + MSC01_IC_OSA_OFS)
+#define MSC01_IC_GENA    (MSC01_IC_REG_BASE + MSC01_IC_GENA_OFS)
+#define MSC01_IC_BASE    (MSC01_IC_REG_BASE + MSC01_IC_BASE_OFS)
+#define MSC01_IC_VEC     (MSC01_IC_REG_BASE + MSC01_IC_VEC_OFS)
+#define MSC01_IC_EOI     (MSC01_IC_REG_BASE + MSC01_IC_EOI_OFS)
+#define MSC01_IC_CFG     (MSC01_IC_REG_BASE + MSC01_IC_CFG_OFS)
+#define MSC01_IC_TRLD    (MSC01_IC_REG_BASE + MSC01_IC_TRLD_OFS)
+#define MSC01_IC_TVAL    (MSC01_IC_REG_BASE + MSC01_IC_TVAL_OFS)
+#define MSC01_IC_TCFG    (MSC01_IC_REG_BASE + MSC01_IC_TCFG_OFS)
+#define MSC01_IC_SUP     (MSC01_IC_REG_BASE + MSC01_IC_SUP_OFS)
+#define MSC01_IC_ENA     (MSC01_IC_REG_BASE + MSC01_IC_ENA_OFS)
+#define MSC01_IC_DIS     (MSC01_IC_REG_BASE + MSC01_IC_DIS_OFS)
+#define MSC01_IC_ISB     (MSC01_IC_REG_BASE + MSC01_IC_ISB_OFS)
+#define MSC01_IC_ISA     (MSC01_IC_REG_BASE + MSC01_IC_ISA_OFS)
+
+/*
+ * Soc-it interrupts are configurable.
+ * Every board describes its IRQ mapping with this table.
+ */
+typedef struct msc_irqmap {
+       int     im_irq;
+       int     im_type;
+       int     im_lvl;
+} msc_irqmap_t;
+
+/* im_type */
+#define MSC01_IRQ_LEVEL                0
+#define MSC01_IRQ_EDGE         1
+
+extern void __init init_msc_irqs(unsigned int base, msc_irqmap_t *imp, int nirq);
+extern void ll_msc_irq(struct pt_regs *regs);
+
+#endif /* __ASM_MIPS_BOARDS_MSC01_IC_H */
+
diff --git a/include/asm-mips/reg.h b/include/asm-mips/reg.h
new file mode 100644 (file)
index 0000000..7b33bbc
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Various register offset definitions for debuggers, core file
+ * examiners and whatnot.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995, 1999 Ralf Baechle
+ * Copyright (C) 1995, 1999 Silicon Graphics
+ */
+#ifndef __ASM_MIPS_REG_H
+#define __ASM_MIPS_REG_H
+
+#include <linux/config.h>
+
+#if defined(CONFIG_MIPS32) || defined(WANT_COMPAT_REG_H)
+
+#define EF_R0                  6
+#define EF_R1                  7
+#define EF_R2                  8
+#define EF_R3                  9
+#define EF_R4                  10
+#define EF_R5                  11
+#define EF_R6                  12
+#define EF_R7                  13
+#define EF_R8                  14
+#define EF_R9                  15
+#define EF_R10                 16
+#define EF_R11                 17
+#define EF_R12                 18
+#define EF_R13                 19
+#define EF_R14                 20
+#define EF_R15                 21
+#define EF_R16                 22
+#define EF_R17                 23
+#define EF_R18                 24
+#define EF_R19                 25
+#define EF_R20                 26
+#define EF_R21                 27
+#define EF_R22                 28
+#define EF_R23                 29
+#define EF_R24                 30
+#define EF_R25                 31
+
+/*
+ * k0/k1 unsaved
+ */
+#define EF_R26                 32
+#define EF_R27                 33
+
+#define EF_R28                 34
+#define EF_R29                 35
+#define EF_R30                 36
+#define EF_R31                 37
+
+/*
+ * Saved special registers
+ */
+#define EF_LO                  38
+#define EF_HI                  39
+
+#define EF_CP0_EPC             40
+#define EF_CP0_BADVADDR                41
+#define EF_CP0_STATUS          42
+#define EF_CP0_CAUSE           43
+#define EF_UNUSED0             44
+
+#define EF_SIZE                        180
+
+#endif
+
+#if CONFIG_MIPS64
+
+#define EF_R0                   0
+#define EF_R1                   1
+#define EF_R2                   2
+#define EF_R3                   3
+#define EF_R4                   4
+#define EF_R5                   5
+#define EF_R6                   6
+#define EF_R7                   7
+#define EF_R8                   8
+#define EF_R9                   9
+#define EF_R10                 10
+#define EF_R11                 11
+#define EF_R12                 12
+#define EF_R13                 13
+#define EF_R14                 14
+#define EF_R15                 15
+#define EF_R16                 16
+#define EF_R17                 17
+#define EF_R18                 18
+#define EF_R19                 19
+#define EF_R20                 20
+#define EF_R21                 21
+#define EF_R22                 22
+#define EF_R23                 23
+#define EF_R24                 24
+#define EF_R25                 25
+
+/*
+ * k0/k1 unsaved
+ */
+#define EF_R26                 26
+#define EF_R27                 27
+
+
+#define EF_R28                 28
+#define EF_R29                 29
+#define EF_R30                 30
+#define EF_R31                 31
+
+/*
+ * Saved special registers
+ */
+#define EF_LO                  32
+#define EF_HI                  33
+
+#define EF_CP0_EPC             34
+#define EF_CP0_BADVADDR                35
+#define EF_CP0_STATUS          36
+#define EF_CP0_CAUSE           37
+
+#define EF_SIZE                        304     /* size in bytes */
+
+#endif /* CONFIG_MIPS64 */
+
+#endif /* __ASM_MIPS_REG_H */
diff --git a/include/asm-mips/tx4927/smsc_fdc37m81x.h b/include/asm-mips/tx4927/smsc_fdc37m81x.h
new file mode 100644 (file)
index 0000000..5d93bab
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * linux/include/asm-mips/tx4927/smsc_fdc37m81x.h
+ *
+ * Interface for smsc fdc48m81x Super IO chip
+ *
+ * Author: MontaVista Software, Inc. source@mvista.com
+ *
+ * 2001-2003 (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.
+ *
+ * Copyright (C) 2004 MontaVista Software Inc.
+ * Manish Lachwani, mlachwani@mvista.com
+ */
+
+#ifndef _SMSC_FDC37M81X_H_
+#define _SMSC_FDC37M81X_H_
+
+/* Common Registers */
+#define SMSC_FDC37M81X_CONFIG_INDEX  0x00
+#define SMSC_FDC37M81X_CONFIG_DATA   0x01
+#define SMSC_FDC37M81X_CONF          0x02
+#define SMSC_FDC37M81X_INDEX         0x03
+#define SMSC_FDC37M81X_DNUM          0x07
+#define SMSC_FDC37M81X_DID           0x20
+#define SMSC_FDC37M81X_DREV          0x21
+#define SMSC_FDC37M81X_PCNT          0x22
+#define SMSC_FDC37M81X_PMGT          0x23
+#define SMSC_FDC37M81X_OSC           0x24
+#define SMSC_FDC37M81X_CONFPA0       0x26
+#define SMSC_FDC37M81X_CONFPA1       0x27
+#define SMSC_FDC37M81X_TEST4         0x2B
+#define SMSC_FDC37M81X_TEST5         0x2C
+#define SMSC_FDC37M81X_TEST1         0x2D
+#define SMSC_FDC37M81X_TEST2         0x2E
+#define SMSC_FDC37M81X_TEST3         0x2F
+
+/* Logical device numbers */
+#define SMSC_FDC37M81X_FDD           0x00
+#define SMSC_FDC37M81X_PARALLEL      0x03
+#define SMSC_FDC37M81X_SERIAL1       0x04
+#define SMSC_FDC37M81X_SERIAL2       0x05
+#define SMSC_FDC37M81X_KBD           0x07
+#define SMSC_FDC37M81X_AUXIO         0x08
+#define SMSC_FDC37M81X_NONE          0xff
+
+/* Logical device Config Registers */
+#define SMSC_FDC37M81X_ACTIVE        0x30
+#define SMSC_FDC37M81X_BASEADDR0     0x60
+#define SMSC_FDC37M81X_BASEADDR1     0x61
+#define SMSC_FDC37M81X_INT           0x70
+#define SMSC_FDC37M81X_INT2          0x72
+#define SMSC_FDC37M81X_LDCR_F0       0xF0
+
+/* Chip Config Values */
+#define SMSC_FDC37M81X_CONFIG_ENTER  0x55
+#define SMSC_FDC37M81X_CONFIG_EXIT   0xaa
+#define SMSC_FDC37M81X_CHIP_ID       0x4d
+
+unsigned long __init smsc_fdc37m81x_init(unsigned long port);
+
+void smsc_fdc37m81x_config_beg(void);
+
+void smsc_fdc37m81x_config_end(void);
+
+void smsc_fdc37m81x_config_set(u8 reg, u8 val);
+
+#endif
diff --git a/include/asm-mips/vr41xx/cmbvr4133.h b/include/asm-mips/vr41xx/cmbvr4133.h
new file mode 100644 (file)
index 0000000..42af389
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * include/asm-mips/vr41xx/cmbvr4133.h
+ *
+ * Include file for NEC CMB-VR4133.
+ *
+ * Author: Yoichi Yuasa <yyuasa@mvista.com, or source@mvista.com> and
+ *         Jun Sun <jsun@mvista.com, or source@mvista.com> and
+ *         Alex Sapkov <asapkov@ru.mvista.com>
+ *
+ * 2002-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.
+ */
+#ifndef __NEC_CMBVR4133_H
+#define __NEC_CMBVR4133_H
+
+#include <asm/addrspace.h>
+#include <asm/vr41xx/vr41xx.h>
+
+/*
+ * General-Purpose I/O Pin Number
+ */
+#define CMBVR41XX_INTA_PIN             1
+#define CMBVR41XX_INTB_PIN             1
+#define CMBVR41XX_INTC_PIN             3
+#define CMBVR41XX_INTD_PIN             1
+#define CMBVR41XX_INTE_PIN             1
+
+/*
+ * Interrupt Number
+ */
+#define CMBVR41XX_INTA_IRQ             GIU_IRQ(CMBVR41XX_INTA_PIN)
+#define CMBVR41XX_INTB_IRQ             GIU_IRQ(CMBVR41XX_INTB_PIN)
+#define CMBVR41XX_INTC_IRQ             GIU_IRQ(CMBVR41XX_INTC_PIN)
+#define CMBVR41XX_INTD_IRQ             GIU_IRQ(CMBVR41XX_INTD_PIN)
+#define CMBVR41XX_INTE_IRQ             GIU_IRQ(CMBVR41XX_INTE_PIN)
+
+#define I8259_IRQ_BASE                 72
+#define I8259_IRQ(x)                   (I8259_IRQ_BASE + (x))
+#define TIMER_IRQ                      I8259_IRQ(0)
+#define KEYBOARD_IRQ                   I8259_IRQ(1)
+#define I8259_SLAVE_IRQ                        I8259_IRQ(2)
+#define UART3_IRQ                      I8259_IRQ(3)
+#define UART1_IRQ                      I8259_IRQ(4)
+#define UART2_IRQ                      I8259_IRQ(5)
+#define FDC_IRQ                                I8259_IRQ(6)
+#define PARPORT_IRQ                    I8259_IRQ(7)
+#define RTC_IRQ                                I8259_IRQ(8)
+#define USB_IRQ                                I8259_IRQ(9)
+#define I8259_INTA_IRQ                 I8259_IRQ(10)
+#define AUDIO_IRQ                      I8259_IRQ(11)
+#define AUX_IRQ                                I8259_IRQ(12)
+#define IDE_PRIMARY_IRQ                        I8259_IRQ(14)
+#define IDE_SECONDARY_IRQ              I8259_IRQ(15)
+#define I8259_IRQ_LAST                 IDE_SECONDARY_IRQ
+
+#define RTC_PORT(x)    (0xaf000100 + (x))
+#define RTC_IO_EXTENT  0x140
+
+#endif /* __NEC_CMBVR4133_H */
diff --git a/include/asm-parisc/cputime.h b/include/asm-parisc/cputime.h
new file mode 100644 (file)
index 0000000..dcdf2fb
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __PARISC_CPUTIME_H
+#define __PARISC_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __PARISC_CPUTIME_H */
diff --git a/include/asm-ppc/cputime.h b/include/asm-ppc/cputime.h
new file mode 100644 (file)
index 0000000..8e9faf5
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __PPC_CPUTIME_H
+#define __PPC_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __PPC_CPUTIME_H */
diff --git a/include/asm-ppc/perfmon.h b/include/asm-ppc/perfmon.h
new file mode 100644 (file)
index 0000000..5e7a89c
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __PERFMON_H
+#define __PERFMON_H
+
+extern void (*perf_irq)(struct pt_regs *);
+
+int request_perfmon_irq(void (*handler)(struct pt_regs *));
+void free_perfmon_irq(void);
+
+#ifdef CONFIG_FSL_BOOKE
+void init_pmc_stop(int ctr);
+void set_pmc_event(int ctr, int event);
+void set_pmc_user_kernel(int ctr, int user, int kernel);
+void set_pmc_marked(int ctr, int mark0, int mark1);
+void pmc_start_ctr(int ctr, int enable);
+void pmc_start_ctrs(int enable);
+void pmc_stop_ctrs(void);
+void dump_pmcs(void);
+
+extern struct op_ppc32_model op_model_fsl_booke;
+#endif
+
+#endif /* __PERFMON_H */
diff --git a/include/asm-ppc/ppc_sys.h b/include/asm-ppc/ppc_sys.h
new file mode 100644 (file)
index 0000000..6f5ab76
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * include/asm-ppc/ppc_sys.h
+ *
+ * PPC system definitions and library functions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor, 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.
+ */
+
+#ifdef __KERNEL__
+#ifndef __ASM_PPC_SYS_H
+#define __ASM_PPC_SYS_H
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/types.h>
+
+#if defined(CONFIG_85xx)
+#include <asm/mpc85xx.h>
+#else
+#error "need definition of ppc_sys_devices"
+#endif
+
+struct ppc_sys_spec {
+       /* PPC sys is matched via (ID & mask) == value, id could be
+        * PVR, SVR, IMMR, * etc. */
+       u32                     mask;
+       u32                     value;
+       u32                     num_devices;
+       char                    *ppc_sys_name;
+       enum ppc_sys_devices    *device_list;
+};
+
+/* describes all specific chips and which devices they have on them */
+extern struct ppc_sys_spec ppc_sys_specs[];
+extern struct ppc_sys_spec *cur_ppc_sys_spec;
+
+/* determine which specific SOC we are */
+extern void identify_ppc_sys_by_id(u32 id) __init;
+extern void identify_ppc_sys_by_name(char *name) __init;
+
+/* describes all devices that may exist in a given family of processors */
+extern struct platform_device ppc_sys_platform_devices[];
+
+/* allow any platform_device fixup to occur before device is registered */
+extern int (*ppc_sys_device_fixup) (struct platform_device * pdev);
+
+/* Update all memory resources by paddr, call before platform_device_register */
+extern void ppc_sys_fixup_mem_resource(struct platform_device *pdev,
+                                      phys_addr_t paddr) __init;
+
+/* Get platform_data pointer out of platform device, call before platform_device_register */
+extern void *ppc_sys_get_pdata(enum ppc_sys_devices dev) __init;
+
+/* remove a device from the system */
+extern void ppc_sys_device_remove(enum ppc_sys_devices dev);
+
+#endif                         /* __ASM_PPC_SYS_H */
+#endif                         /* __KERNEL__ */
diff --git a/include/asm-ppc/xparameters.h b/include/asm-ppc/xparameters.h
new file mode 100644 (file)
index 0000000..fe4eac6
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * include/asm-ppc/xparameters.h
+ *
+ * This file includes the correct xparameters.h for the CONFIG'ed board
+ *
+ * Author: MontaVista Software, Inc.
+ *         source@mvista.com
+ *
+ * 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 <linux/config.h>
+
+#if defined(CONFIG_XILINX_ML300)
+#include <platforms/4xx/xparameters/xparameters_ml300.h>
+#endif
diff --git a/include/asm-ppc64/cputime.h b/include/asm-ppc64/cputime.h
new file mode 100644 (file)
index 0000000..8e9faf5
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __PPC_CPUTIME_H
+#define __PPC_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __PPC_CPUTIME_H */
diff --git a/include/asm-ppc64/kdebug.h b/include/asm-ppc64/kdebug.h
new file mode 100644 (file)
index 0000000..4886342
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef _PPC64_KDEBUG_H
+#define _PPC64_KDEBUG_H 1
+
+/* nearly identical to x86_64/i386 code */
+
+#include <linux/notifier.h>
+
+struct pt_regs;
+
+struct die_args {
+       struct pt_regs *regs;
+       const char *str;
+       long err;
+       int trapnr;
+       int signr;
+};
+
+/*
+   Note - you should never unregister because that can race with NMIs.
+   If you really want to do it first unregister - then synchronize_kernel -
+   then free.
+ */
+int register_die_notifier(struct notifier_block *nb);
+extern struct notifier_block *ppc64_die_chain;
+
+/* Grossly misnamed. */
+enum die_val {
+       DIE_OOPS = 1,
+       DIE_IABR_MATCH,
+       DIE_DABR_MATCH,
+       DIE_BPT,
+       DIE_SSTEP,
+       DIE_GPF,
+       DIE_PAGE_FAULT,
+};
+
+static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig)
+{
+       struct die_args args = { .regs=regs, .str=str, .err=err, .trapnr=trap,.signr=sig };
+       return notifier_call_chain(&ppc64_die_chain, val, &args);
+}
+
+#endif
diff --git a/include/asm-ppc64/kprobes.h b/include/asm-ppc64/kprobes.h
new file mode 100644 (file)
index 0000000..097f0d6
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef _ASM_KPROBES_H
+#define _ASM_KPROBES_H
+/*
+ *  Kernel Probes (KProbes)
+ *  include/asm-ppc64/kprobes.h
+ *
+ * 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.
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ *
+ * 2002-Oct    Created by Vamsi Krishna S <vamsi_krishna@in.ibm.com> Kernel
+ *             Probes initial implementation ( includes suggestions from
+ *             Rusty Russell).
+ * 2004-Nov    Modified for PPC64 by Ananth N Mavinakayanahalli
+ *             <ananth@in.ibm.com>
+ */
+#include <linux/types.h>
+#include <linux/ptrace.h>
+
+struct pt_regs;
+
+typedef unsigned int kprobe_opcode_t;
+#define BREAKPOINT_INSTRUCTION 0x7fe00008      /* trap */
+#define MAX_INSN_SIZE 1
+
+#define JPROBE_ENTRY(pentry)   (kprobe_opcode_t *)((func_descr_t *)pentry)
+
+/* Architecture specific copy of original instruction */
+struct arch_specific_insn {
+       /* copy of original instruction */
+       kprobe_opcode_t insn[MAX_INSN_SIZE];
+};
+
+#ifdef CONFIG_KPROBES
+extern int kprobe_exceptions_notify(struct notifier_block *self,
+                                   unsigned long val, void *data);
+#else                          /* !CONFIG_KPROBES */
+static inline int kprobe_exceptions_notify(struct notifier_block *self,
+                                          unsigned long val, void *data)
+{
+       return 0;
+}
+#endif
+#endif                         /* _ASM_KPROBES_H */
diff --git a/include/asm-ppc64/lppaca.h b/include/asm-ppc64/lppaca.h
new file mode 100644 (file)
index 0000000..70766b5
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * lppaca.h
+ * Copyright (C) 2001  Mike Corrigan 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 _ASM_LPPACA_H
+#define _ASM_LPPACA_H
+
+//=============================================================================
+//
+//     This control block contains the data that is shared between the
+//     hypervisor (PLIC) and the OS.
+//
+//
+//----------------------------------------------------------------------------
+#include <asm/types.h>
+
+struct lppaca
+{
+//=============================================================================
+// CACHE_LINE_1 0x0000 - 0x007F Contains read-only data
+// NOTE: The xDynXyz fields are fields that will be dynamically changed by
+// PLIC when preparing to bring a processor online or when dispatching a
+// virtual processor!
+//=============================================================================
+       u32     desc;                   // Eye catcher 0xD397D781       x00-x03
+       u16     size;                   // Size of this struct          x04-x05
+       u16     reserved1;              // Reserved                     x06-x07
+       u16     reserved2:14;           // Reserved                     x08-x09
+       u8      shared_proc:1;          // Shared processor indicator   ...
+       u8      secondary_thread:1;     // Secondary thread indicator   ...
+       volatile u8 dyn_proc_status:8;  // Dynamic Status of this proc  x0A-x0A
+       u8      secondary_thread_count; // Secondary thread count       x0B-x0B
+       volatile u16 dyn_hv_phys_proc_index;// Dynamic HV Physical Proc Index0C-x0D
+       volatile u16 dyn_hv_log_proc_index;// Dynamic HV Logical Proc Indexx0E-x0F
+       u32     decr_val;               // Value for Decr programming   x10-x13
+       u32     pmc_val;                // Value for PMC regs           x14-x17
+       volatile u32 dyn_hw_node_id;    // Dynamic Hardware Node id     x18-x1B
+       volatile u32 dyn_hw_proc_id;    // Dynamic Hardware Proc Id     x1C-x1F
+       volatile u32 dyn_pir;           // Dynamic ProcIdReg value      x20-x23
+       u32     dsei_data;              // DSEI data                    x24-x27
+       u64     sprg3;                  // SPRG3 value                  x28-x2F
+       u8      reserved3[80];          // Reserved                     x30-x7F
+
+//=============================================================================
+// CACHE_LINE_2 0x0080 - 0x00FF Contains local read-write data
+//=============================================================================
+       // This Dword contains a byte for each type of interrupt that can occur.
+       // The IPI is a count while the others are just a binary 1 or 0.
+       union {
+               u64     any_int;
+               struct {
+                       u16     reserved;       // Reserved - cleared by #mpasmbl
+                       u8      xirr_int;       // Indicates xXirrValue is valid or Immed IO
+                       u8      ipi_cnt;        // IPI Count
+                       u8      decr_int;       // DECR interrupt occurred
+                       u8      pdc_int;        // PDC interrupt occurred
+                       u8      quantum_int;    // Interrupt quantum reached
+                       u8      old_plic_deferred_ext_int;      // Old PLIC has a deferred XIRR pending
+               } fields;
+       } int_dword;
+
+       // Whenever any fields in this Dword are set then PLIC will defer the
+       // processing of external interrupts.  Note that PLIC will store the
+       // XIRR directly into the xXirrValue field so that another XIRR will
+       // not be presented until this one clears.  The layout of the low
+       // 4-bytes of this Dword is upto SLIC - PLIC just checks whether the
+       // entire Dword is zero or not.  A non-zero value in the low order
+       // 2-bytes will result in SLIC being granted the highest thread
+       // priority upon return.  A 0 will return to SLIC as medium priority.
+       u64     plic_defer_ints_area;   // Entire Dword
+
+       // Used to pass the real SRR0/1 from PLIC to SLIC as well as to
+       // pass the target SRR0/1 from SLIC to PLIC on a SetAsrAndRfid.
+       u64     saved_srr0;             // Saved SRR0                   x10-x17
+       u64     saved_srr1;             // Saved SRR1                   x18-x1F
+
+       // Used to pass parms from the OS to PLIC for SetAsrAndRfid
+       u64     saved_gpr3;             // Saved GPR3                   x20-x27
+       u64     saved_gpr4;             // Saved GPR4                   x28-x2F
+       u64     saved_gpr5;             // Saved GPR5                   x30-x37
+
+       u8      reserved4;              // Reserved                     x38-x38
+       u8      cpuctls_task_attrs;     // Task attributes for cpuctls  x39-x39
+       u8      fpregs_in_use;          // FP regs in use               x3A-x3A
+       u8      pmcregs_in_use;         // PMC regs in use              x3B-x3B
+       volatile u32 saved_decr;        // Saved Decr Value             x3C-x3F
+       volatile u64 emulated_time_base;// Emulated TB for this thread  x40-x47
+       volatile u64 cur_plic_latency;  // Unaccounted PLIC latency     x48-x4F
+       u64     tot_plic_latency;       // Accumulated PLIC latency     x50-x57
+       u64     wait_state_cycles;      // Wait cycles for this proc    x58-x5F
+       u64     end_of_quantum;         // TB at end of quantum         x60-x67
+       u64     pdc_saved_sprg1;        // Saved SPRG1 for PMC int      x68-x6F
+       u64     pdc_saved_srr0;         // Saved SRR0 for PMC int       x70-x77
+       volatile u32 virtual_decr;      // Virtual DECR for shared procsx78-x7B
+       u16     slb_count;              // # of SLBs to maintain        x7C-x7D
+       u8      idle;                   // Indicate OS is idle          x7E
+       u8      reserved5;              // Reserved                     x7F
+
+
+//=============================================================================
+// CACHE_LINE_3 0x0100 - 0x007F: This line is shared with other processors
+//=============================================================================
+       // This is the yield_count.  An "odd" value (low bit on) means that
+       // the processor is yielded (either because of an OS yield or a PLIC
+       // preempt).  An even value implies that the processor is currently
+       // executing.
+       // NOTE: This value will ALWAYS be zero for dedicated processors and
+       // will NEVER be zero for shared processors (ie, initialized to a 1).
+       volatile u32 yield_count;       // PLIC increments each dispatchx00-x03
+       u8      reserved6[124];         // Reserved                     x04-x7F
+
+//=============================================================================
+// CACHE_LINE_4-5 0x0100 - 0x01FF Contains PMC interrupt data
+//=============================================================================
+       u8      pmc_save_area[256];     // PMC interrupt Area           x00-xFF
+};
+
+#endif /* _ASM_LPPACA_H */
diff --git a/include/asm-ppc64/sstep.h b/include/asm-ppc64/sstep.h
new file mode 100644 (file)
index 0000000..448190d
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) 2004 Paul Mackerras <paulus@au.ibm.com>, IBM
+ *
+ * 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.
+ */
+
+struct pt_regs;
+
+/* Emulate instructions that cause a transfer of control. */
+extern int emulate_step(struct pt_regs *regs, unsigned int instr);
diff --git a/include/asm-s390/cputime.h b/include/asm-s390/cputime.h
new file mode 100644 (file)
index 0000000..216d861
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ *  include/asm-s390/cputime.h
+ *
+ *  (C) Copyright IBM Corp. 2004
+ *
+ *  Author: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+
+#ifndef _S390_CPUTIME_H
+#define _S390_CPUTIME_H
+
+/* We want to use micro-second resolution. */
+
+typedef unsigned long long cputime_t;
+typedef unsigned long long cputime64_t;
+
+#ifndef __s390x__
+
+static inline unsigned int
+__div(unsigned long long n, unsigned int base)
+{
+       register_pair rp;
+
+       rp.pair = n >> 1;
+       asm ("dr %0,%1" : "+d" (rp) : "d" (base >> 1));
+       return rp.subreg.odd;
+}
+
+#else /* __s390x__ */
+
+static inline unsigned int
+__div(unsigned long long n, unsigned int base)
+{
+       return n / base;
+}
+
+#endif /* __s390x__ */
+
+#define cputime_zero                   (0ULL)
+#define cputime_max                    ((~0UL >> 1) - 1)
+#define cputime_add(__a, __b)          ((__a) +  (__b))
+#define cputime_sub(__a, __b)          ((__a) -  (__b))
+#define cputime_eq(__a, __b)           ((__a) == (__b))
+#define cputime_gt(__a, __b)           ((__a) >  (__b))
+#define cputime_ge(__a, __b)           ((__a) >= (__b))
+#define cputime_lt(__a, __b)           ((__a) <  (__b))
+#define cputime_le(__a, __b)           ((__a) <= (__b))
+#define cputime_to_jiffies(__ct)       (__div((__ct), 1000000 / HZ))
+#define jiffies_to_cputime(__hz)       ((cputime_t)(__hz) * (1000000 / HZ))
+
+#define cputime64_zero                 (0ULL)
+#define cputime64_add(__a, __b)                ((__a) + (__b))
+#define cputime_to_cputime64(__ct)     (__ct)
+
+static inline u64
+cputime64_to_jiffies64(cputime64_t cputime)
+{
+       do_div(cputime, 1000000 / HZ);
+       return cputime;
+}
+
+/*
+ * Convert cputime to milliseconds and back.
+ */
+static inline unsigned int
+cputime_to_msecs(const cputime_t cputime)
+{
+       return __div(cputime, 1000);
+}
+
+static inline cputime_t
+msecs_to_cputime(const unsigned int m)
+{
+       return (cputime_t) m * 1000;
+}
+
+/*
+ * Convert cputime to milliseconds and back.
+ */
+static inline unsigned int
+cputime_to_secs(const cputime_t cputime)
+{
+       return __div(cputime, 1000000);
+}
+
+static inline cputime_t
+secs_to_cputime(const unsigned int s)
+{
+       return (cputime_t) s * 1000000;
+}
+
+/*
+ * Convert cputime to timespec and back.
+ */
+static inline cputime_t
+timespec_to_cputime(const struct timespec *value)
+{
+        return value->tv_nsec / 1000 + (u64) value->tv_sec * 1000000;
+}
+
+static inline void
+cputime_to_timespec(const cputime_t cputime, struct timespec *value)
+{
+#ifndef __s390x__
+       register_pair rp;
+
+       rp.pair = cputime >> 1;
+       asm ("dr %0,%1" : "+d" (rp) : "d" (1000000 >> 1));
+       value->tv_nsec = rp.subreg.even * 1000;
+       value->tv_sec = rp.subreg.odd;
+#else
+       value->tv_nsec = (cputime % 1000000) * 1000;
+       value->tv_sec = cputime / 1000000;
+#endif
+}
+
+/*
+ * Convert cputime to timeval and back.
+ * Since cputime and timeval have the same resolution (microseconds)
+ * this is easy.
+ */
+static inline cputime_t
+timeval_to_cputime(const struct timeval *value)
+{
+        return value->tv_usec + (u64) value->tv_sec * 1000000;
+}
+
+static inline void
+cputime_to_timeval(const cputime_t cputime, struct timeval *value)
+{
+#ifndef __s390x__
+       register_pair rp;
+
+       rp.pair = cputime >> 1;
+       asm ("dr %0,%1" : "+d" (rp) : "d" (1000000 >> 1));
+       value->tv_usec = rp.subreg.even;
+       value->tv_sec = rp.subreg.odd;
+#else
+       value->tv_usec = cputime % 1000000;
+       value->tv_sec = cputime / 1000000;
+#endif
+}
+
+/*
+ * Convert cputime to clock and back.
+ */
+static inline clock_t
+cputime_to_clock_t(cputime_t cputime)
+{
+       return __div(cputime, 1000000 / USER_HZ);
+}
+
+static inline cputime_t
+clock_t_to_cputime(unsigned long x)
+{
+       return (cputime_t) x * (1000000 / USER_HZ);
+}
+
+/*
+ * Convert cputime64 to clock.
+ */
+static inline clock_t
+cputime64_to_clock_t(cputime64_t cputime)
+{
+       return __div(cputime, 1000000 / USER_HZ);
+}
+
+#endif /* _S390_CPUTIME_H */
diff --git a/include/asm-sh/cputime.h b/include/asm-sh/cputime.h
new file mode 100644 (file)
index 0000000..6ca395d
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __SH_CPUTIME_H
+#define __SH_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __SH_CPUTIME_H */
diff --git a/include/asm-sh/edosk7705/io.h b/include/asm-sh/edosk7705/io.h
new file mode 100644 (file)
index 0000000..a1089a6
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * include/asm-sh/edosk7705/io.h
+ *
+ * Modified version of io_se.h for the EDOSK7705 specific functions.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * IO functions for an Hitachi EDOSK7705 development board
+ */
+
+#ifndef __ASM_SH_EDOSK7705_IO_H
+#define __ASM_SH_EDOSK7705_IO_H
+
+#include <asm/io_generic.h>
+
+extern unsigned char sh_edosk7705_inb(unsigned long port);
+extern unsigned int sh_edosk7705_inl(unsigned long port);
+
+extern void sh_edosk7705_outb(unsigned char value, unsigned long port);
+extern void sh_edosk7705_outl(unsigned int value, unsigned long port);
+
+extern void sh_edosk7705_insb(unsigned long port, void *addr, unsigned long count);
+extern void sh_edosk7705_insl(unsigned long port, void *addr, unsigned long count);
+extern void sh_edosk7705_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void sh_edosk7705_outsl(unsigned long port, const void *addr, unsigned long count);
+
+extern unsigned long sh_edosk7705_isa_port2addr(unsigned long offset);
+
+#endif /* __ASM_SH_EDOSK7705_IO_H */
diff --git a/include/asm-sh/irq-sh73180.h b/include/asm-sh/irq-sh73180.h
new file mode 100644 (file)
index 0000000..bf2e431
--- /dev/null
@@ -0,0 +1,350 @@
+#ifndef __ASM_SH_IRQ_SH73180_H
+#define __ASM_SH_IRQ_SH73180_H
+
+/*
+ * linux/include/asm-sh/irq-sh73180.h
+ *
+ * Copyright (C) 2004 Takashi SHUDO <shudo@hitachi-ul.co.jp>
+ */
+
+#undef INTC_IPRA
+#undef INTC_IPRB
+#undef INTC_IPRC
+#undef INTC_IPRD
+
+#undef DMTE0_IRQ
+#undef DMTE1_IRQ
+#undef DMTE2_IRQ
+#undef DMTE3_IRQ
+#undef DMTE4_IRQ
+#undef DMTE5_IRQ
+#undef DMTE6_IRQ
+#undef DMTE7_IRQ
+#undef DMAE_IRQ
+#undef DMA_IPR_ADDR
+#undef DMA_IPR_POS
+#undef DMA_PRIORITY
+
+#undef NR_IRQS
+
+#undef __irq_demux
+#undef irq_demux
+
+#undef INTC_IMCR0
+#undef INTC_IMCR1
+#undef INTC_IMCR2
+#undef INTC_IMCR3
+#undef INTC_IMCR4
+#undef INTC_IMCR5
+#undef INTC_IMCR6
+#undef INTC_IMCR7
+#undef INTC_IMCR8
+#undef INTC_IMCR9
+#undef INTC_IMCR10
+
+
+#define INTC_IPRA      0xA4080000UL
+#define INTC_IPRB      0xA4080004UL
+#define INTC_IPRC      0xA4080008UL
+#define INTC_IPRD      0xA408000CUL
+#define INTC_IPRE      0xA4080010UL
+#define INTC_IPRF      0xA4080014UL
+#define INTC_IPRG      0xA4080018UL
+#define INTC_IPRH      0xA408001CUL
+#define INTC_IPRI      0xA4080020UL
+#define INTC_IPRJ      0xA4080024UL
+#define INTC_IPRK      0xA4080028UL
+
+#define INTC_IMR0      0xA4080080UL
+#define INTC_IMR1      0xA4080084UL
+#define INTC_IMR2      0xA4080088UL
+#define INTC_IMR3      0xA408008CUL
+#define INTC_IMR4      0xA4080090UL
+#define INTC_IMR5      0xA4080094UL
+#define INTC_IMR6      0xA4080098UL
+#define INTC_IMR7      0xA408009CUL
+#define INTC_IMR8      0xA40800A0UL
+#define INTC_IMR9      0xA40800A4UL
+#define INTC_IMR10     0xA40800A8UL
+#define INTC_IMR11     0xA40800ACUL
+
+#define INTC_IMCR0     0xA40800C0UL
+#define INTC_IMCR1     0xA40800C4UL
+#define INTC_IMCR2     0xA40800C8UL
+#define INTC_IMCR3     0xA40800CCUL
+#define INTC_IMCR4     0xA40800D0UL
+#define INTC_IMCR5     0xA40800D4UL
+#define INTC_IMCR6     0xA40800D8UL
+#define INTC_IMCR7     0xA40800DCUL
+#define INTC_IMCR8     0xA40800E0UL
+#define INTC_IMCR9     0xA40800E4UL
+#define INTC_IMCR10    0xA40800E8UL
+#define INTC_IMCR11    0xA40800ECUL
+
+#define INTC_ICR0      0xA4140000UL
+#define INTC_ICR1      0xA414001CUL
+
+#define INTMSK0                0xa4140044
+#define INTMSKCLR0     0xa4140064
+#define INTC_INTPRI0   0xa4140010
+
+/*
+  NOTE:
+
+  *_IRQ = (INTEVT2 - 0x200)/0x20
+*/
+
+/* TMU0 */
+#define TMU0_IRQ       16
+#define TMU0_IPR_ADDR  INTC_IPRA
+#define TMU0_IPR_POS    3
+#define TMU0_PRIORITY   2
+
+#define TIMER_IRQ       16
+#define TIMER_IPR_ADDR  INTC_IPRA
+#define TIMER_IPR_POS    3
+#define TIMER_PRIORITY   2
+
+/* TMU1 */
+#define TMU1_IRQ       17
+#define TMU1_IPR_ADDR  INTC_IPRA
+#define TMU1_IPR_POS    2
+#define TMU1_PRIORITY   2
+
+/* TMU2 */
+#define TMU2_IRQ       18
+#define TMU2_IPR_ADDR  INTC_IPRA
+#define TMU2_IPR_POS    1
+#define TMU2_PRIORITY   2
+
+/* LCDC */
+#define LCDC_IRQ       28
+#define LCDC_IPR_ADDR  INTC_IPRB
+#define LCDC_IPR_POS    2
+#define LCDC_PRIORITY   2
+
+/* VIO (Video I/O) */
+#define CEU_IRQ                52
+#define BEU_IRQ                53
+#define VEU_IRQ                54
+#define VOU_IRQ                55
+#define VIO_IPR_ADDR   INTC_IPRE
+#define VIO_IPR_POS     2
+#define VIO_PRIORITY    2
+
+/* MFI (Multi Functional Interface) */
+#define MFI_IRQ                56
+#define MFI_IPR_ADDR   INTC_IPRE
+#define MFI_IPR_POS     1
+#define MFI_PRIORITY    2
+
+/* VPU (Video Processing Unit) */
+#define VPU_IRQ                60
+#define VPU_IPR_ADDR   INTC_IPRE
+#define VPU_IPR_POS     0
+#define VPU_PRIORITY    2
+
+/* 3DG */
+#define TDG_IRQ                63
+#define TDG_IPR_ADDR   INTC_IPRJ
+#define TDG_IPR_POS     2
+#define TDG_PRIORITY    2
+
+/* DMAC(1) */
+#define DMTE0_IRQ      48
+#define DMTE1_IRQ      49
+#define DMTE2_IRQ      50
+#define DMTE3_IRQ      51
+#define DMA1_IPR_ADDR  INTC_IPRE
+#define DMA1_IPR_POS   3
+#define DMA1_PRIORITY  7
+
+/* DMAC(2) */
+#define DMTE4_IRQ      76
+#define DMTE5_IRQ      77
+#define DMA2_IPR_ADDR  INTC_IPRF
+#define DMA2_IPR_POS   2
+#define DMA2_PRIORITY  7
+
+/* SCIF0 */
+#define SCIF_ERI_IRQ   80
+#define SCIF_RXI_IRQ   81
+#define SCIF_BRI_IRQ   82
+#define SCIF_TXI_IRQ   83
+#define SCIF_IPR_ADDR  INTC_IPRG
+#define SCIF_IPR_POS   3
+#define SCIF_PRIORITY  3
+
+/* SIOF0 */
+#define SIOF0_IRQ      84
+#define SIOF0_IPR_ADDR INTC_IPRH
+#define SIOF0_IPR_POS  3
+#define SIOF0_PRIORITY 3
+
+/* FLCTL (Flash Memory Controller) */
+#define FLSTE_IRQ      92
+#define FLTEND_IRQ     93
+#define FLTRQ0_IRQ     94
+#define FLTRQ1_IRQ     95
+#define FLCTL_IPR_ADDR INTC_IPRH
+#define FLCTL_IPR_POS  1
+#define FLCTL_PRIORITY 3
+
+/* IIC(0) (IIC Bus Interface) */
+#define IIC0_ALI_IRQ   96
+#define IIC0_TACKI_IRQ 97
+#define IIC0_WAITI_IRQ 98
+#define IIC0_DTEI_IRQ  99
+#define IIC0_IPR_ADDR  INTC_IPRH
+#define IIC0_IPR_POS   0
+#define IIC0_PRIORITY  3
+
+/* IIC(1) (IIC Bus Interface) */
+#define IIC1_ALI_IRQ   44
+#define IIC1_TACKI_IRQ 45
+#define IIC1_WAITI_IRQ 46
+#define IIC1_DTEI_IRQ  47
+#define IIC1_IPR_ADDR  INTC_IPRG
+#define IIC1_IPR_POS   0
+#define IIC1_PRIORITY  3
+
+/* SIO0 */
+#define SIO0_IRQ       88
+#define SIO0_IPR_ADDR  INTC_IPRI
+#define SIO0_IPR_POS   3
+#define SIO0_PRIORITY  3
+
+/* SDHI */
+#define SDHI_SDHII0_IRQ        100
+#define SDHI_SDHII1_IRQ        101
+#define SDHI_SDHII2_IRQ        102
+#define SDHI_SDHII3_IRQ        103
+#define SDHI_IPR_ADDR  INTC_IPRK
+#define SDHI_IPR_POS   0
+#define SDHI_PRIORITY  3
+
+/* SIU (Sound Interface Unit) */
+#define SIU_IRQ                108
+#define SIU_IPR_ADDR   INTC_IPRJ
+#define SIU_IPR_POS    1
+#define SIU_PRIORITY   3
+
+
+/* ONCHIP_NR_IRQS */
+#define NR_IRQS 109
+
+/* In a generic kernel, NR_IRQS is an upper bound, and we should use
+ * ACTUAL_NR_IRQS (which uses the machine vector) to get the correct value.
+ */
+#define ACTUAL_NR_IRQS NR_IRQS
+
+
+extern void disable_irq(unsigned int);
+extern void disable_irq_nosync(unsigned int);
+extern void enable_irq(unsigned int);
+
+/*
+ * Simple Mask Register Support
+ */
+extern void make_maskreg_irq(unsigned int irq);
+extern unsigned short *irq_mask_register;
+
+/*
+ * Function for "on chip support modules".
+ */
+extern void make_ipr_irq(unsigned int irq, unsigned int addr,
+                        int pos,  int priority);
+extern void make_imask_irq(unsigned int irq);
+
+#define PORT_PACR      0xA4050100UL
+#define PORT_PBCR      0xA4050102UL
+#define PORT_PCCR      0xA4050104UL
+#define PORT_PDCR      0xA4050106UL
+#define PORT_PECR      0xA4050108UL
+#define PORT_PFCR      0xA405010AUL
+#define PORT_PGCR      0xA405010CUL
+#define PORT_PHCR      0xA405010EUL
+#define PORT_PJCR      0xA4050110UL
+#define PORT_PKCR      0xA4050112UL
+#define PORT_PLCR      0xA4050114UL
+#define PORT_SCPCR     0xA4050116UL
+#define PORT_PMCR      0xA4050118UL
+#define PORT_PNCR      0xA405011AUL
+#define PORT_PQCR      0xA405011CUL
+#define PORT_PRCR      0xA405011EUL
+#define PORT_PTCR      0xA405014CUL
+#define PORT_PUCR      0xA405014EUL
+#define PORT_PVCR      0xA4050150UL
+
+#define PORT_PSELA     0xA4050140UL
+#define PORT_PSELB     0xA4050142UL
+#define PORT_PSELC     0xA4050144UL
+#define PORT_PSELE     0xA4050158UL
+
+#define PORT_HIZCRA    0xA4050146UL
+#define PORT_HIZCRB    0xA4050148UL
+#define PORT_DRVCR     0xA405014AUL
+
+#define PORT_PADR      0xA4050120UL
+#define PORT_PBDR      0xA4050122UL
+#define PORT_PCDR      0xA4050124UL
+#define PORT_PDDR      0xA4050126UL
+#define PORT_PEDR      0xA4050128UL
+#define PORT_PFDR      0xA405012AUL
+#define PORT_PGDR      0xA405012CUL
+#define PORT_PHDR      0xA405012EUL
+#define PORT_PJDR      0xA4050130UL
+#define PORT_PKDR      0xA4050132UL
+#define PORT_PLDR      0xA4050134UL
+#define PORT_SCPDR     0xA4050136UL
+#define PORT_PMDR      0xA4050138UL
+#define PORT_PNDR      0xA405013AUL
+#define PORT_PQDR      0xA405013CUL
+#define PORT_PRDR      0xA405013EUL
+#define PORT_PTDR      0xA405016CUL
+#define PORT_PUDR      0xA405016EUL
+#define PORT_PVDR      0xA4050170UL
+
+#define IRQ0_IRQ       32
+#define IRQ1_IRQ       33
+#define IRQ2_IRQ       34
+#define IRQ3_IRQ       35
+#define IRQ4_IRQ       36
+#define IRQ5_IRQ       37
+#define IRQ6_IRQ       38
+#define IRQ7_IRQ       39
+
+#define INTPRI00       0xA4140010UL
+
+#define IRQ0_IPR_ADDR  INTPRI00
+#define IRQ1_IPR_ADDR  INTPRI00
+#define IRQ2_IPR_ADDR  INTPRI00
+#define IRQ3_IPR_ADDR  INTPRI00
+#define IRQ4_IPR_ADDR  INTPRI00
+#define IRQ5_IPR_ADDR  INTPRI00
+#define IRQ6_IPR_ADDR  INTPRI00
+#define IRQ7_IPR_ADDR  INTPRI00
+
+#define IRQ0_IPR_POS   7
+#define IRQ1_IPR_POS   6
+#define IRQ2_IPR_POS   5
+#define IRQ3_IPR_POS   4
+#define IRQ4_IPR_POS   3
+#define IRQ5_IPR_POS   2
+#define IRQ6_IPR_POS   1
+#define IRQ7_IPR_POS   0
+
+#define IRQ0_PRIORITY  1
+#define IRQ1_PRIORITY  1
+#define IRQ2_PRIORITY  1
+#define IRQ3_PRIORITY  1
+#define IRQ4_PRIORITY  1
+#define IRQ5_PRIORITY  1
+#define IRQ6_PRIORITY  1
+#define IRQ7_PRIORITY  1
+
+extern int shmse_irq_demux(int irq);
+#define __irq_demux(irq) shmse_irq_demux(irq)
+#define irq_demux(irq) __irq_demux(irq)
+
+#endif /* __ASM_SH_IRQ_SH73180_H */
diff --git a/include/asm-sh/microdev/io.h b/include/asm-sh/microdev/io.h
new file mode 100644 (file)
index 0000000..f2ca4ac
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * linux/include/asm-sh/io_microdev.h
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ *
+ * IO functions for the SuperH SH4-202 MicroDev board.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ */
+
+
+#ifndef _ASM_SH_IO_MICRODEV_H
+#define _ASM_SH_IO_MICRODEV_H
+
+extern unsigned long microdev_isa_port2addr(unsigned long offset);
+
+extern unsigned char microdev_inb(unsigned long port);
+extern unsigned short microdev_inw(unsigned long port);
+extern unsigned int microdev_inl(unsigned long port);
+
+extern void microdev_outb(unsigned char value, unsigned long port);
+extern void microdev_outw(unsigned short value, unsigned long port);
+extern void microdev_outl(unsigned int value, unsigned long port);
+
+extern unsigned char microdev_inb_p(unsigned long port);
+extern unsigned short microdev_inw_p(unsigned long port);
+extern unsigned int microdev_inl_p(unsigned long port);
+
+extern void microdev_outb_p(unsigned char value, unsigned long port);
+extern void microdev_outw_p(unsigned short value, unsigned long port);
+extern void microdev_outl_p(unsigned int value, unsigned long port);
+
+extern void microdev_insb(unsigned long port, void *addr, unsigned long count);
+extern void microdev_insw(unsigned long port, void *addr, unsigned long count);
+extern void microdev_insl(unsigned long port, void *addr, unsigned long count);
+
+extern void microdev_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void microdev_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void microdev_outsl(unsigned long port, const void *addr, unsigned long count);
+
+#if defined(CONFIG_PCI)
+extern unsigned char  microdev_pci_inb(unsigned long port);
+extern unsigned short microdev_pci_inw(unsigned long port);
+extern unsigned long  microdev_pci_inl(unsigned long port);
+extern void           microdev_pci_outb(unsigned char  data, unsigned long port);
+extern void           microdev_pci_outw(unsigned short data, unsigned long port);
+extern void           microdev_pci_outl(unsigned long  data, unsigned long port);
+#endif
+
+#endif /* _ASM_SH_IO_MICRODEV_H */
+
diff --git a/include/asm-sh/microdev/irq.h b/include/asm-sh/microdev/irq.h
new file mode 100644 (file)
index 0000000..47f6f77
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * linux/include/asm-sh/irq_microdev.h
+ *
+ * Copyright (C) 2003 Sean McGoogan (Sean.McGoogan@superh.com)
+ *
+ * IRQ functions for the SuperH SH4-202 MicroDev board.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ */
+
+
+#ifndef _ASM_SH_IRQ_MICRODEV_H
+#define _ASM_SH_IRQ_MICRODEV_H
+
+extern void init_microdev_irq(void);
+extern void microdev_print_fpga_intc_status(void);
+
+
+       /*
+        *      The following are useful macros for manipulating the
+        *      interrupt controller (INTC) on the CPU-board FPGA.
+        *      It should be noted that there is an INTC on the FPGA,
+        *      and a seperate INTC on the SH4-202 core - these are
+        *      two different things, both of which need to be prorammed
+        *      to correctly route - unfortunately, they have the
+        *      same name and abbreviations!
+        */
+#define        MICRODEV_FPGA_INTC_BASE         0xa6110000ul                            /* INTC base address on CPU-board FPGA */
+#define        MICRODEV_FPGA_INTENB_REG        (MICRODEV_FPGA_INTC_BASE+0ul)           /* Interrupt Enable Register on INTC on CPU-board FPGA */
+#define        MICRODEV_FPGA_INTDSB_REG        (MICRODEV_FPGA_INTC_BASE+8ul)           /* Interrupt Disable Register on INTC on CPU-board FPGA */
+#define        MICRODEV_FPGA_INTC_MASK(n)      (1ul<<(n))                              /* Interupt mask to enable/disable INTC in CPU-board FPGA */
+#define        MICRODEV_FPGA_INTPRI_REG(n)     (MICRODEV_FPGA_INTC_BASE+0x10+((n)/8)*8)/* Interrupt Priority Register on INTC on CPU-board FPGA */
+#define        MICRODEV_FPGA_INTPRI_LEVEL(n,x) ((x)<<(((n)%8)*4))                      /* MICRODEV_FPGA_INTPRI_LEVEL(int_number, int_level) */
+#define        MICRODEV_FPGA_INTPRI_MASK(n)    (MICRODEV_FPGA_INTPRI_LEVEL((n),0xful)) /* Interrupt Priority Mask on INTC on CPU-board FPGA */
+#define        MICRODEV_FPGA_INTSRC_REG        (MICRODEV_FPGA_INTC_BASE+0x30ul)        /* Interrupt Source Register on INTC on CPU-board FPGA */
+#define        MICRODEV_FPGA_INTREQ_REG        (MICRODEV_FPGA_INTC_BASE+0x38ul)        /* Interrupt Request Register on INTC on CPU-board FPGA */
+
+
+       /*
+        *      The following are the IRQ numbers for the Linux Kernel for external interrupts.
+        *      i.e. the numbers seen by 'cat /proc/interrupt'.
+        */
+#define MICRODEV_LINUX_IRQ_KEYBOARD     1      /* SuperIO Keyboard */
+#define MICRODEV_LINUX_IRQ_SERIAL1      2      /* SuperIO Serial #1 */
+#define MICRODEV_LINUX_IRQ_ETHERNET     3      /* on-board Ethnernet */
+#define MICRODEV_LINUX_IRQ_SERIAL2      4      /* SuperIO Serial #2 */
+#define MICRODEV_LINUX_IRQ_USB_HC       7      /* on-board USB HC */
+#define MICRODEV_LINUX_IRQ_MOUSE       12      /* SuperIO PS/2 Mouse */
+#define MICRODEV_LINUX_IRQ_IDE2                13      /* SuperIO IDE #2 */
+#define MICRODEV_LINUX_IRQ_IDE1                14      /* SuperIO IDE #1 */
+
+       /*
+        *      The following are the IRQ numbers for the INTC on the FPGA for external interrupts.
+        *      i.e. the bits in the INTC registers in the FPGA.
+        */
+#define MICRODEV_FPGA_IRQ_KEYBOARD      1      /* SuperIO Keyboard */
+#define MICRODEV_FPGA_IRQ_SERIAL1       3      /* SuperIO Serial #1 */
+#define MICRODEV_FPGA_IRQ_SERIAL2       4      /* SuperIO Serial #2 */
+#define MICRODEV_FPGA_IRQ_MOUSE                12      /* SuperIO PS/2 Mouse */
+#define MICRODEV_FPGA_IRQ_IDE1         14      /* SuperIO IDE #1 */
+#define MICRODEV_FPGA_IRQ_IDE2         15      /* SuperIO IDE #2 */
+#define MICRODEV_FPGA_IRQ_USB_HC       16      /* on-board USB HC */
+#define MICRODEV_FPGA_IRQ_ETHERNET     18      /* on-board Ethnernet */
+
+#define MICRODEV_IRQ_PCI_INTA           8
+#define MICRODEV_IRQ_PCI_INTB           9
+#define MICRODEV_IRQ_PCI_INTC          10
+#define MICRODEV_IRQ_PCI_INTD          11
+
+#endif /* _ASM_SH_IRQ_MICRODEV_H */
diff --git a/include/asm-sh/se73180/io.h b/include/asm-sh/se73180/io.h
new file mode 100644 (file)
index 0000000..c9cb1b9
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * include/asm-sh/se73180/io.h
+ *
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ * Based on include/asm-sh/se7300/io.h
+ *
+ * IO functions for SH-Mobile3(SH73180) SolutionEngine
+ *
+ */
+
+#ifndef _ASM_SH_IO_73180SE_H
+#define _ASM_SH_IO_73180SE_H
+
+extern unsigned char sh73180se_inb(unsigned long port);
+extern unsigned short sh73180se_inw(unsigned long port);
+extern unsigned int sh73180se_inl(unsigned long port);
+
+extern void sh73180se_outb(unsigned char value, unsigned long port);
+extern void sh73180se_outw(unsigned short value, unsigned long port);
+extern void sh73180se_outl(unsigned int value, unsigned long port);
+
+extern unsigned char sh73180se_inb_p(unsigned long port);
+extern void sh73180se_outb_p(unsigned char value, unsigned long port);
+
+extern void sh73180se_insb(unsigned long port, void *addr, unsigned long count);
+extern void sh73180se_insw(unsigned long port, void *addr, unsigned long count);
+extern void sh73180se_insl(unsigned long port, void *addr, unsigned long count);
+extern void sh73180se_outsb(unsigned long port, const void *addr, unsigned long count);
+extern void sh73180se_outsw(unsigned long port, const void *addr, unsigned long count);
+extern void sh73180se_outsl(unsigned long port, const void *addr, unsigned long count);
+
+#endif /* _ASM_SH_IO_73180SE_H */
diff --git a/include/asm-sh/se73180/se73180.h b/include/asm-sh/se73180/se73180.h
new file mode 100644 (file)
index 0000000..f5b93e3
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef __ASM_SH_HITACHI_SE73180_H
+#define __ASM_SH_HITACHI_SE73180_H
+
+/*
+ * include/asm-sh/se/se73180.h
+ *
+ * Copyright (C) 2003 Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
+ *
+ * SH-Mobile SolutionEngine 73180 support
+ */
+
+/* Box specific addresses.  */
+
+/* Area 0 */
+#define PA_ROM         0x00000000      /* EPROM */
+#define PA_ROM_SIZE    0x00400000      /* EPROM size 4M byte(Actually 2MB) */
+#define PA_FROM                0x00400000      /* Flash ROM */
+#define PA_FROM_SIZE   0x00400000      /* Flash size 4M byte */
+#define PA_SRAM                0x00800000      /* SRAM */
+#define PA_FROM_SIZE   0x00400000      /* SRAM size 4M byte */
+/* Area 1 */
+#define PA_EXT1                0x04000000
+#define PA_EXT1_SIZE   0x04000000
+/* Area 2 */
+#define PA_EXT2                0x08000000
+#define PA_EXT2_SIZE   0x04000000
+/* Area 3 */
+#define PA_SDRAM       0x0c000000
+#define PA_SDRAM_SIZE  0x04000000
+/* Area 4 */
+#define PA_PCIC                0x10000000      /* MR-SHPC-01 PCMCIA */
+#define PA_MRSHPC       0xb03fffe0      /* MR-SHPC-01 PCMCIA controller */
+#define PA_MRSHPC_MW1   0xb0400000      /* MR-SHPC-01 memory window base */
+#define PA_MRSHPC_MW2   0xb0500000      /* MR-SHPC-01 attribute window base */
+#define PA_MRSHPC_IO    0xb0600000      /* MR-SHPC-01 I/O window base */
+#define MRSHPC_OPTION   (PA_MRSHPC + 6)
+#define MRSHPC_CSR      (PA_MRSHPC + 8)
+#define MRSHPC_ISR      (PA_MRSHPC + 10)
+#define MRSHPC_ICR      (PA_MRSHPC + 12)
+#define MRSHPC_CPWCR    (PA_MRSHPC + 14)
+#define MRSHPC_MW0CR1   (PA_MRSHPC + 16)
+#define MRSHPC_MW1CR1   (PA_MRSHPC + 18)
+#define MRSHPC_IOWCR1   (PA_MRSHPC + 20)
+#define MRSHPC_MW0CR2   (PA_MRSHPC + 22)
+#define MRSHPC_MW1CR2   (PA_MRSHPC + 24)
+#define MRSHPC_IOWCR2   (PA_MRSHPC + 26)
+#define MRSHPC_CDCR     (PA_MRSHPC + 28)
+#define MRSHPC_PCIC_INFO (PA_MRSHPC + 30)
+#define PA_LED         0xb0C00000      /* LED */
+#define LED_SHIFT       0
+#define PA_DIPSW       0xb0900000      /* Dip switch 31 */
+#define PA_EPLD_MODESET        0xb0a00000      /* FPGA Mode set register */
+#define PA_EPLD_ST1    0xb0a80000      /* FPGA Interrupt status register1 */
+#define PA_EPLD_ST2    0xb0ac0000      /* FPGA Interrupt status register2 */
+/* Area 5 */
+#define PA_EXT5                0x14000000
+#define PA_EXT5_SIZE   0x04000000
+/* Area 6 */
+#define PA_LCD1                0xb8000000
+#define PA_LCD2                0xb8800000
+
+#endif  /* __ASM_SH_HITACHI_SE73180_H */
diff --git a/include/asm-sh/sh03/io.h b/include/asm-sh/sh03/io.h
new file mode 100644 (file)
index 0000000..25792e9
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * include/asm-sh/sh03/io.h
+ *
+ * Copyright 2004 Interface Co.,Ltd. Saito.K
+ *
+ * IO functions for an Interface CTP/PCI-SH03
+ */
+
+#ifndef _ASM_SH_IO_SH03_H
+#define _ASM_SH_IO_SH03_H
+
+#include <linux/time.h>
+
+#define INTC_IPRD      0xffd00010UL
+
+#define IRL0_IRQ       2
+#define IRL0_IPR_ADDR  INTC_IPRD
+#define IRL0_IPR_POS   3
+#define IRL0_PRIORITY  13
+
+#define IRL1_IRQ       5
+#define IRL1_IPR_ADDR  INTC_IPRD
+#define IRL1_IPR_POS   2
+#define IRL1_PRIORITY  10
+
+#define IRL2_IRQ       8
+#define IRL2_IPR_ADDR  INTC_IPRD
+#define IRL2_IPR_POS   1
+#define IRL2_PRIORITY  7
+
+#define IRL3_IRQ       11
+#define IRL3_IPR_ADDR  INTC_IPRD
+#define IRL3_IPR_POS   0
+#define IRL3_PRIORITY  4
+
+
+extern unsigned long sh03_isa_port2addr(unsigned long offset);
+
+extern void setup_sh03(void);
+extern void init_sh03_IRQ(void);
+extern void heartbeat_sh03(void);
+
+extern void sh03_rtc_gettimeofday(struct timeval *tv);
+extern int sh03_rtc_settimeofday(const struct timeval *tv);
+
+#endif /* _ASM_SH_IO_SH03_H */
diff --git a/include/asm-sh/sh03/sh03.h b/include/asm-sh/sh03/sh03.h
new file mode 100644 (file)
index 0000000..19c40b8
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __ASM_SH_SH03_H
+#define __ASM_SH_SH03_H
+
+/*
+ * linux/include/asm-sh/sh03/sh03.h
+ *
+ * Copyright (C) 2004  Interface Co., Ltd. Saito.K
+ *
+ * Interface CTP/PCI-SH03 support
+ */
+
+#define PA_PCI_IO       (0xbe240000)    /* PCI I/O space */
+#define PA_PCI_MEM      (0xbd000000)    /* PCI MEM space */
+
+#define PCIPAR          (0xa4000cf8)    /* PCI Config address */
+#define PCIPDR          (0xa4000cfc)    /* PCI Config data    */
+
+#endif  /* __ASM_SH_SH03_H */
diff --git a/include/asm-sh64/cputime.h b/include/asm-sh64/cputime.h
new file mode 100644 (file)
index 0000000..0fd89da
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __SH64_CPUTIME_H
+#define __SH64_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __SH64_CPUTIME_H */
diff --git a/include/asm-sparc/cputime.h b/include/asm-sparc/cputime.h
new file mode 100644 (file)
index 0000000..1a642b8
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __SPARC_CPUTIME_H
+#define __SPARC_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __SPARC_CPUTIME_H */
diff --git a/include/asm-sparc64/cputime.h b/include/asm-sparc64/cputime.h
new file mode 100644 (file)
index 0000000..dec2fc7
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __SPARC64_CPUTIME_H
+#define __SPARC64_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __SPARC64_CPUTIME_H */
diff --git a/include/asm-um/apic.h b/include/asm-um/apic.h
new file mode 100644 (file)
index 0000000..876dee8
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef __UM_APIC_H
+#define __UM_APIC_H
+
+#endif
diff --git a/include/asm-um/archparam-x86_64.h b/include/asm-um/archparam-x86_64.h
new file mode 100644 (file)
index 0000000..96321c4
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_ARCHPARAM_X86_64_H
+#define __UM_ARCHPARAM_X86_64_H
+
+#include <asm/user.h>
+
+#define ELF_PLATFORM "x86_64"
+
+#define ELF_ET_DYN_BASE (2 * TASK_SIZE / 3)
+
+typedef unsigned long elf_greg_t;
+typedef struct { } elf_fpregset_t;
+
+#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t))
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+
+#define ELF_DATA        ELFDATA2LSB
+#define ELF_ARCH        EM_X86_64
+
+#define ELF_PLAT_INIT(regs, load_addr)    do { \
+       PT_REGS_RBX(regs) = 0; \
+       PT_REGS_RCX(regs) = 0; \
+       PT_REGS_RDX(regs) = 0; \
+       PT_REGS_RSI(regs) = 0; \
+       PT_REGS_RDI(regs) = 0; \
+       PT_REGS_RBP(regs) = 0; \
+       PT_REGS_RAX(regs) = 0; \
+       PT_REGS_R8(regs) = 0; \
+       PT_REGS_R9(regs) = 0; \
+       PT_REGS_R10(regs) = 0; \
+       PT_REGS_R11(regs) = 0; \
+       PT_REGS_R12(regs) = 0; \
+       PT_REGS_R13(regs) = 0; \
+       PT_REGS_R14(regs) = 0; \
+       PT_REGS_R15(regs) = 0; \
+} while (0)
+
+#ifdef TIF_IA32 /* XXX */
+        clear_thread_flag(TIF_IA32);
+#endif
+
+/* No user-accessible fixmap addresses, i.e. vsyscall */
+#define FIXADDR_USER_START     0
+#define FIXADDR_USER_END       0
+
+#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/include/asm-um/calling.h b/include/asm-um/calling.h
new file mode 100644 (file)
index 0000000..0b2384c
--- /dev/null
@@ -0,0 +1,9 @@
+# Copyright 2003 - 2004 Pathscale, Inc
+# Released under the GPL
+
+#ifndef __UM_CALLING_H /* XXX x86_64 */
+#define __UM_CALLING_H
+
+#include "asm/arch/calling.h"
+
+#endif
diff --git a/include/asm-um/cputime.h b/include/asm-um/cputime.h
new file mode 100644 (file)
index 0000000..c84acba
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __UM_CPUTIME_H
+#define __UM_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __UM_CPUTIME_H */
diff --git a/include/asm-um/dwarf2.h b/include/asm-um/dwarf2.h
new file mode 100644 (file)
index 0000000..d1a02e7
--- /dev/null
@@ -0,0 +1,11 @@
+/* Copyright 2003 - 2004 Pathscale, Inc
+ * Released under the GPL
+ */
+
+/* Needed on x86_64 by thunk.S */
+#ifndef __UM_DWARF2_H
+#define __UM_DWARF2_H
+
+#include "asm/arch/dwarf2.h"
+
+#endif
diff --git a/include/asm-um/module-x86_64.h b/include/asm-um/module-x86_64.h
new file mode 100644 (file)
index 0000000..35b5491
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_MODULE_X86_64_H
+#define __UM_MODULE_X86_64_H
+
+/* UML is simple */
+struct mod_arch_specific
+{
+};
+
+#define Elf_Shdr Elf64_Shdr
+#define Elf_Sym Elf64_Sym
+#define Elf_Ehdr Elf64_Ehdr
+
+#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/include/asm-um/pda.h b/include/asm-um/pda.h
new file mode 100644 (file)
index 0000000..0d8bf33
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_PDA_X86_64_H
+#define __UM_PDA_X86_64_H
+
+/* XXX */
+struct foo {
+       unsigned int __softirq_pending;
+       unsigned int __nmi_count;
+};
+
+extern struct foo me;
+
+#define read_pda(me) (&me)
+
+#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/include/asm-um/pgtable-2level.h b/include/asm-um/pgtable-2level.h
new file mode 100644 (file)
index 0000000..f6263d1
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
+ * Copyright 2003 PathScale, Inc.
+ * Derived from include/asm-i386/pgtable.h
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_PGTABLE_2LEVEL_H
+#define __UM_PGTABLE_2LEVEL_H
+
+#include <asm-generic/pgtable-nopmd.h>
+
+/* PGDIR_SHIFT determines what a third-level page table entry can map */
+
+#define PGDIR_SHIFT    22
+#define PGDIR_SIZE     (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK     (~(PGDIR_SIZE-1))
+
+/*
+ * entries per page directory level: the i386 is two-level, so
+ * we don't really have any PMD directory physically.
+ */
+#define PTRS_PER_PTE   1024
+#define PTRS_PER_PMD   1
+#define USER_PTRS_PER_PGD ((TASK_SIZE + (PGDIR_SIZE - 1)) / PGDIR_SIZE)
+#define PTRS_PER_PGD   1024
+#define FIRST_USER_PGD_NR       0
+
+#define pte_ERROR(e) \
+        printk("%s:%d: bad pte %p(%08lx).\n", __FILE__, __LINE__, &(e), \
+              pte_val(e))
+#define pgd_ERROR(e) \
+        printk("%s:%d: bad pgd %p(%08lx).\n", __FILE__, __LINE__, &(e), \
+              pgd_val(e))
+
+static inline int pgd_newpage(pgd_t pgd)       { return 0; }
+static inline void pgd_mkuptodate(pgd_t pgd)   { }
+
+#define pte_present(x) (pte_val(x) & (_PAGE_PRESENT | _PAGE_PROTNONE))
+
+static inline pte_t pte_mknewprot(pte_t pte)
+{
+       pte_val(pte) |= _PAGE_NEWPROT;
+       return(pte);
+}
+
+static inline pte_t pte_mknewpage(pte_t pte)
+{
+       pte_val(pte) |= _PAGE_NEWPAGE;
+       return(pte);
+}
+
+static inline void set_pte(pte_t *pteptr, pte_t pteval)
+{
+       /* If it's a swap entry, it needs to be marked _PAGE_NEWPAGE so
+        * fix_range knows to unmap it.  _PAGE_NEWPROT is specific to
+        * mapped pages.
+        */
+       *pteptr = pte_mknewpage(pteval);
+       if(pte_present(*pteptr)) *pteptr = pte_mknewprot(*pteptr);
+}
+
+#define set_pmd(pmdptr, pmdval) (*(pmdptr) = (pmdval))
+
+#define pte_page(x) pfn_to_page(pte_pfn(x))
+#define pte_none(x) !(pte_val(x) & ~_PAGE_NEWPAGE)
+#define pte_pfn(x) phys_to_pfn(pte_val(x))
+#define pfn_pte(pfn, prot) __pte(pfn_to_phys(pfn) | pgprot_val(prot))
+#define pfn_pmd(pfn, prot) __pmd(pfn_to_phys(pfn) | pgprot_val(prot))
+
+#define pmd_page_kernel(pmd) \
+       ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK))
+
+/*
+ * Bits 0 through 3 are taken
+ */
+#define PTE_FILE_MAX_BITS      28
+
+#define pte_to_pgoff(pte) (pte_val(pte) >> 4)
+
+#define pgoff_to_pte(off) ((pte_t) { ((off) << 4) + _PAGE_FILE })
+
+#endif
diff --git a/include/asm-um/pgtable-3level.h b/include/asm-um/pgtable-3level.h
new file mode 100644 (file)
index 0000000..acebb59
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2003 PathScale Inc
+ * Derived from include/asm-i386/pgtable.h
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_PGTABLE_3LEVEL_H
+#define __UM_PGTABLE_3LEVEL_H
+
+#include <asm-generic/pgtable-nopud.h>
+
+/* PGDIR_SHIFT determines what a third-level page table entry can map */
+
+#define PGDIR_SHIFT    30
+#define PGDIR_SIZE     (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK     (~(PGDIR_SIZE-1))
+
+/* PMD_SHIFT determines the size of the area a second-level page table can
+ * map
+ */
+
+#define PMD_SHIFT      21
+#define PMD_SIZE       (1UL << PMD_SHIFT)
+#define PMD_MASK       (~(PMD_SIZE-1))
+
+/*
+ * entries per page directory level
+ */
+
+#define PTRS_PER_PTE 512
+#define PTRS_PER_PMD 512
+#define USER_PTRS_PER_PGD ((TASK_SIZE + (PGDIR_SIZE - 1)) / PGDIR_SIZE)
+#define PTRS_PER_PGD 512
+#define FIRST_USER_PGD_NR       0
+
+#define pte_ERROR(e) \
+        printk("%s:%d: bad pte %p(%016lx).\n", __FILE__, __LINE__, &(e), \
+              pte_val(e))
+#define pmd_ERROR(e) \
+        printk("%s:%d: bad pmd %p(%016lx).\n", __FILE__, __LINE__, &(e), \
+              pmd_val(e))
+#define pgd_ERROR(e) \
+        printk("%s:%d: bad pgd %p(%016lx).\n", __FILE__, __LINE__, &(e), \
+              pgd_val(e))
+
+#define pud_none(x)    (!(pud_val(x) & ~_PAGE_NEWPAGE))
+#define        pud_bad(x)      ((pud_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE)
+#define pud_present(x) (pud_val(x) & _PAGE_PRESENT)
+#define pud_populate(mm, pud, pmd) \
+       set_pud(pud, __pud(_PAGE_TABLE + __pa(pmd)))
+
+#define set_pud(pudptr, pudval) set_64bit((phys_t *) (pudptr), pud_val(pudval))
+static inline int pgd_newpage(pgd_t pgd)
+{
+       return(pgd_val(pgd) & _PAGE_NEWPAGE);
+}
+
+static inline void pgd_mkuptodate(pgd_t pgd) { pgd_val(pgd) &= ~_PAGE_NEWPAGE; }
+
+
+#define pte_present(x) pte_get_bits(x, (_PAGE_PRESENT | _PAGE_PROTNONE))
+
+static inline pte_t pte_mknewprot(pte_t pte)
+{
+        pte_set_bits(pte, _PAGE_NEWPROT);
+       return(pte);
+}
+
+static inline pte_t pte_mknewpage(pte_t pte)
+{
+       pte_set_bits(pte, _PAGE_NEWPAGE);
+       return(pte);
+}
+
+static inline void set_pte(pte_t *pteptr, pte_t pteval)
+{
+       pte_copy(*pteptr, pteval);
+
+       /* If it's a swap entry, it needs to be marked _PAGE_NEWPAGE so
+        * fix_range knows to unmap it.  _PAGE_NEWPROT is specific to
+        * mapped pages.
+        */
+
+       *pteptr = pte_mknewpage(*pteptr);
+       if(pte_present(*pteptr)) *pteptr = pte_mknewprot(*pteptr);
+}
+
+#define set_pmd(pmdptr, pmdval) set_64bit((phys_t *) (pmdptr), pmd_val(pmdval))
+
+static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address)
+{
+        pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL);
+
+        if(pmd)
+                memset(pmd, 0, PAGE_SIZE);
+
+        return pmd;
+}
+
+static inline void pmd_free(pmd_t *pmd){
+       free_page((unsigned long) pmd);
+}
+
+#define __pmd_free_tlb(tlb,x)   do { } while (0)
+
+static inline void pud_clear (pud_t * pud) { }
+
+#define pud_page(pud) \
+       ((struct page *) __va(pud_val(pud) & PAGE_MASK))
+
+/* Find an entry in the second-level page table.. */
+#define pmd_offset(pud, address) ((pmd_t *) pud_page(*(pud)) + \
+                       pmd_index(address))
+
+#define pte_page(x) pfn_to_page(pte_pfn(x))
+
+static inline int pte_none(pte_t pte)
+{
+       return pte_is_zero(pte);
+}
+
+static inline unsigned long pte_pfn(pte_t pte)
+{
+       return phys_to_pfn(pte_val(pte));
+}
+
+static inline pte_t pfn_pte(pfn_t page_nr, pgprot_t pgprot)
+{
+       pte_t pte;
+       phys_t phys = pfn_to_phys(page_nr);
+
+       pte_set_val(pte, phys, pgprot);
+       return pte;
+}
+
+static inline pmd_t pfn_pmd(pfn_t page_nr, pgprot_t pgprot)
+{
+       return __pmd((page_nr << PAGE_SHIFT) | pgprot_val(pgprot));
+}
+
+/*
+ * Bits 0 through 3 are taken in the low part of the pte,
+ * put the 32 bits of offset into the high part.
+ */
+#define PTE_FILE_MAX_BITS      32
+
+#ifdef CONFIG_64_BIT
+
+#define pte_to_pgoff(p) ((p).pte >> 32)
+
+#define pgoff_to_pte(off) ((pte_t) { ((off) < 32) | _PAGE_FILE })
+
+#else
+
+#define pte_to_pgoff(pte) ((pte).pte_high)
+
+#define pgoff_to_pte(off) ((pte_t) { _PAGE_FILE, (off) })
+
+#endif
+
+#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/include/asm-um/prctl.h b/include/asm-um/prctl.h
new file mode 100644 (file)
index 0000000..64b6d09
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __UM_PRCTL_H
+#define __UM_PRCTL_H
+
+#include "asm/arch/prctl.h"
+
+#endif
diff --git a/include/asm-um/processor-x86_64.h b/include/asm-um/processor-x86_64.h
new file mode 100644 (file)
index 0000000..6bfd787
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_PROCESSOR_X86_64_H
+#define __UM_PROCESSOR_X86_64_H
+
+#include "asm/arch/user.h"
+
+struct arch_thread {
+};
+
+#define INIT_ARCH_THREAD { }
+
+#define current_text_addr() \
+       ({ void *pc; __asm__("movq $1f,%0\n1:":"=g" (pc)); pc; })
+
+#define ARCH_IS_STACKGROW(address) \
+        (address + 128 >= UPT_SP(&current->thread.regs.regs))
+
+#include "asm/processor-generic.h"
+
+#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/include/asm-um/ptrace-x86_64.h b/include/asm-um/ptrace-x86_64.h
new file mode 100644 (file)
index 0000000..c34be39
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_PTRACE_X86_64_H
+#define __UM_PTRACE_X86_64_H
+
+#include "linux/compiler.h"
+
+#define signal_fault signal_fault_x86_64
+#define __FRAME_OFFSETS /* Needed to get the R* macros */
+#include "asm/ptrace-generic.h"
+#undef signal_fault
+
+void signal_fault(struct pt_regs_subarch *regs, void *frame, char *where);
+
+#define FS_BASE (21 * sizeof(unsigned long))
+#define GS_BASE (22 * sizeof(unsigned long))
+#define DS (23 * sizeof(unsigned long))
+#define ES (24 * sizeof(unsigned long))
+#define FS (25 * sizeof(unsigned long))
+#define GS (26 * sizeof(unsigned long))
+
+#define PT_REGS_RBX(r) UPT_RBX(&(r)->regs)
+#define PT_REGS_RCX(r) UPT_RCX(&(r)->regs)
+#define PT_REGS_RDX(r) UPT_RDX(&(r)->regs)
+#define PT_REGS_RSI(r) UPT_RSI(&(r)->regs)
+#define PT_REGS_RDI(r) UPT_RDI(&(r)->regs)
+#define PT_REGS_RBP(r) UPT_RBP(&(r)->regs)
+#define PT_REGS_RAX(r) UPT_RAX(&(r)->regs)
+#define PT_REGS_R8(r) UPT_R8(&(r)->regs)
+#define PT_REGS_R9(r) UPT_R9(&(r)->regs)
+#define PT_REGS_R10(r) UPT_R10(&(r)->regs)
+#define PT_REGS_R11(r) UPT_R11(&(r)->regs)
+#define PT_REGS_R12(r) UPT_R12(&(r)->regs)
+#define PT_REGS_R13(r) UPT_R13(&(r)->regs)
+#define PT_REGS_R14(r) UPT_R14(&(r)->regs)
+#define PT_REGS_R15(r) UPT_R15(&(r)->regs)
+
+#define PT_REGS_FS(r) UPT_FS(&(r)->regs)
+#define PT_REGS_GS(r) UPT_GS(&(r)->regs)
+#define PT_REGS_DS(r) UPT_DS(&(r)->regs)
+#define PT_REGS_ES(r) UPT_ES(&(r)->regs)
+#define PT_REGS_SS(r) UPT_SS(&(r)->regs)
+#define PT_REGS_CS(r) UPT_CS(&(r)->regs)
+
+#define PT_REGS_ORIG_RAX(r) UPT_ORIG_RAX(&(r)->regs)
+#define PT_REGS_RIP(r) UPT_IP(&(r)->regs)
+#define PT_REGS_RSP(r) UPT_SP(&(r)->regs)
+
+#define PT_REGS_EFLAGS(r) UPT_EFLAGS(&(r)->regs)
+
+/* XXX */
+#define user_mode(r) UPT_IS_USER(&(r)->regs)
+#define PT_REGS_ORIG_SYSCALL(r) PT_REGS_RAX(r)
+#define PT_REGS_SYSCALL_RET(r) PT_REGS_RAX(r)
+
+#define PT_FIX_EXEC_STACK(sp) do ; while(0)
+
+#define profile_pc(regs) PT_REGS_IP(regs)
+
+#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/include/asm-um/sigcontext-x86_64.h b/include/asm-um/sigcontext-x86_64.h
new file mode 100644 (file)
index 0000000..b600e0b
--- /dev/null
@@ -0,0 +1,22 @@
+/* Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_SIGCONTEXT_X86_64_H
+#define __UM_SIGCONTEXT_X86_64_H
+
+#include "asm/sigcontext-generic.h"
+
+#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/include/asm-um/system-x86_64.h b/include/asm-um/system-x86_64.h
new file mode 100644 (file)
index 0000000..e1b61b5
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2003 PathScale, Inc.
+ *
+ * Licensed under the GPL
+ */
+
+#ifndef __UM_SYSTEM_X86_64_H
+#define __UM_SYSTEM_X86_64_H
+
+#include "asm/system-generic.h"
+
+#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/include/asm-um/vm-flags-i386.h b/include/asm-um/vm-flags-i386.h
new file mode 100644 (file)
index 0000000..e0d24c5
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Licensed under the GPL
+ */
+
+#ifndef __VM_FLAGS_I386_H
+#define __VM_FLAGS_I386_H
+
+#define VM_DATA_DEFAULT_FLAGS \
+       (VM_READ | VM_WRITE | \
+       ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0 ) | \
+                VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+
+#endif
diff --git a/include/asm-um/vm-flags-x86_64.h b/include/asm-um/vm-flags-x86_64.h
new file mode 100644 (file)
index 0000000..3213edf
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
+ * Copyright 2003 PathScale, Inc.
+ * Licensed under the GPL
+ */
+
+#ifndef __VM_FLAGS_X86_64_H
+#define __VM_FLAGS_X86_64_H
+
+#define __VM_DATA_DEFAULT_FLAGS        (VM_READ | VM_WRITE | VM_EXEC | \
+                                VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+#define __VM_STACK_FLAGS       (VM_GROWSDOWN | VM_READ | VM_WRITE | \
+                                VM_EXEC | VM_MAYREAD | VM_MAYWRITE | \
+                                VM_MAYEXEC)
+
+extern unsigned long vm_stack_flags, vm_stack_flags32;
+extern unsigned long vm_data_default_flags, vm_data_default_flags32;
+extern unsigned long vm_force_exec32;
+
+#ifdef TIF_IA32
+#define VM_DATA_DEFAULT_FLAGS \
+       (test_thread_flag(TIF_IA32) ? vm_data_default_flags32 : \
+         vm_data_default_flags)
+
+#define VM_STACK_DEFAULT_FLAGS \
+       (test_thread_flag(TIF_IA32) ? vm_stack_flags32 : vm_stack_flags)
+#endif
+
+#define VM_DATA_DEFAULT_FLAGS vm_data_default_flags
+
+#define VM_STACK_DEFAULT_FLAGS vm_stack_flags
+
+#endif
diff --git a/include/asm-v850/cputime.h b/include/asm-v850/cputime.h
new file mode 100644 (file)
index 0000000..7c799c3
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __V850_CPUTIME_H
+#define __V850_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __V850_CPUTIME_H */
diff --git a/include/asm-x86_64/cputime.h b/include/asm-x86_64/cputime.h
new file mode 100644 (file)
index 0000000..a07012d
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __X86_64_CPUTIME_H
+#define __X86_64_CPUTIME_H
+
+#include <asm-generic/cputime.h>
+
+#endif /* __X86_64_CPUTIME_H */
diff --git a/include/asm-x86_64/crash.h b/include/asm-x86_64/crash.h
new file mode 100644 (file)
index 0000000..389654f
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef _ASM_X86_64_CRASH_H
+#define _ASM_X86_64_CRASH_H
+
+/*
+ * linux/include/asm-x86_64/crash.h
+ *
+ * Copyright (c) 2004 Red Hat, Inc. 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifdef __KERNEL__
+
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <asm/mmzone.h>
+
+extern int page_is_ram(unsigned long);
+
+static inline void *
+map_virtual(u64 offset, struct page **pp)
+{
+       struct page *page;
+       unsigned long pfn;
+       void *vaddr;
+
+        pfn = (unsigned long)(offset >> PAGE_SHIFT);
+
+        if (!page_is_ram(pfn)) {
+               printk(KERN_INFO
+                   "crash memory driver: !page_is_ram(pfn: %lx)\n", pfn);
+                return NULL;
+       }
+
+       if (!pfn_valid(pfn)) {
+               printk(KERN_INFO
+                   "crash memory driver: invalid pfn: %lx )\n", pfn);
+               return NULL;
+       }
+
+       page = pfn_to_page(pfn);
+
+       vaddr = kmap(page);
+       if (!vaddr) {
+               printk(KERN_INFO
+                   "crash memory driver: pfn: %lx kmap(page: %lx) failed\n", 
+                       pfn, (unsigned long)page);
+               return NULL;
+       }
+
+       *pp = page;
+       return (vaddr + (offset & (PAGE_SIZE-1)));
+}
+
+static inline void unmap_virtual(struct page *page) 
+{ 
+       kunmap(page);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_X86_64_CRASH_H */
diff --git a/include/asm-x86_64/genapic.h b/include/asm-x86_64/genapic.h
new file mode 100644 (file)
index 0000000..50b38e7
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef _ASM_GENAPIC_H
+#define _ASM_GENAPIC_H 1
+
+/*
+ * Copyright 2004 James Cleverdon, IBM.
+ * Subject to the GNU Public License, v.2
+ *
+ * Generic APIC sub-arch data struct.
+ *
+ * Hacked for x86-64 by James Cleverdon from i386 architecture code by
+ * Martin Bligh, Andi Kleen, James Bottomley, John Stultz, and
+ * James Cleverdon.
+ */
+
+struct genapic {
+       char *name;
+       u32 int_delivery_mode;
+       u32 int_dest_mode;
+       u32 int_delivery_dest;  /* for quick IPIs */
+       int (*apic_id_registered)(void);
+       cpumask_t (*target_cpus)(void);
+       void (*init_apic_ldr)(void);
+       /* ipi */
+       void (*send_IPI_mask)(cpumask_t mask, int vector);
+       void (*send_IPI_allbutself)(int vector);
+       void (*send_IPI_all)(int vector);
+       /* */
+       unsigned int (*cpu_mask_to_apicid)(cpumask_t cpumask);
+       unsigned int (*phys_pkg_id)(int index_msb);
+};
+
+
+extern struct genapic *genapic;
+
+#endif
diff --git a/include/asm-x86_64/ipi.h b/include/asm-x86_64/ipi.h
new file mode 100644 (file)
index 0000000..d184184
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef __ASM_IPI_H
+#define __ASM_IPI_H
+
+/*
+ * Copyright 2004 James Cleverdon, IBM.
+ * Subject to the GNU Public License, v.2
+ *
+ * Generic APIC InterProcessor Interrupt code.
+ *
+ * Moved to include file by James Cleverdon from
+ * arch/x86-64/kernel/smp.c
+ *
+ * Copyrights from kernel/smp.c:
+ *
+ * (c) 1995 Alan Cox, Building #3 <alan@redhat.com>
+ * (c) 1998-99, 2000 Ingo Molnar <mingo@redhat.com>
+ * (c) 2002,2003 Andi Kleen, SuSE Labs.
+ * Subject to the GNU Public License, v.2
+ */
+
+#include <asm/fixmap.h>
+#include <asm/hw_irq.h>
+#include <asm/apicdef.h>
+#include <asm/genapic.h>
+
+/*
+ * the following functions deal with sending IPIs between CPUs.
+ *
+ * We use 'broadcast', CPU->CPU IPIs and self-IPIs too.
+ */
+
+static inline unsigned int __prepare_ICR (unsigned int shortcut, int vector, unsigned int dest)
+{
+       unsigned int icr =  APIC_DM_FIXED | shortcut | vector | dest;
+       if (vector == KDB_VECTOR)
+               icr = (icr & (~APIC_VECTOR_MASK)) | APIC_DM_NMI;
+       return icr;
+}
+
+static inline int __prepare_ICR2 (unsigned int mask)
+{
+       return SET_APIC_DEST_FIELD(mask);
+}
+
+static inline void __send_IPI_shortcut(unsigned int shortcut, int vector, unsigned int dest)
+{
+       /*
+        * Subtle. In the case of the 'never do double writes' workaround
+        * we have to lock out interrupts to be safe.  As we don't care
+        * of the value read we use an atomic rmw access to avoid costly
+        * cli/sti.  Otherwise we use an even cheaper single atomic write
+        * to the APIC.
+        */
+       unsigned int cfg;
+
+       /*
+        * Wait for idle.
+        */
+       apic_wait_icr_idle();
+
+       /*
+        * No need to touch the target chip field
+        */
+       cfg = __prepare_ICR(shortcut, vector, dest);
+
+       /*
+        * Send the IPI. The write to APIC_ICR fires this off.
+        */
+       apic_write_around(APIC_ICR, cfg);
+}
+
+
+static inline void send_IPI_mask_sequence(cpumask_t mask, int vector)
+{
+       unsigned long cfg, flags;
+       unsigned long query_cpu;
+
+       /*
+        * Hack. The clustered APIC addressing mode doesn't allow us to send
+        * to an arbitrary mask, so I do a unicast to each CPU instead.
+        * - mbligh
+        */
+       local_irq_save(flags);
+
+       for (query_cpu = 0; query_cpu < NR_CPUS; ++query_cpu) {
+               if (cpu_isset(query_cpu, mask)) {
+
+                       /*
+                        * Wait for idle.
+                        */
+                       apic_wait_icr_idle();
+
+                       /*
+                        * prepare target chip field
+                        */
+                       cfg = __prepare_ICR2(x86_cpu_to_apicid[query_cpu]);
+                       apic_write_around(APIC_ICR2, cfg);
+
+                       /*
+                        * program the ICR
+                        */
+                       cfg = __prepare_ICR(0, vector, APIC_DEST_PHYSICAL);
+
+                       /*
+                        * Send the IPI. The write to APIC_ICR fires this off.
+                        */
+                       apic_write_around(APIC_ICR, cfg);
+               }
+       }
+       local_irq_restore(flags);
+}
+
+#endif /* __ASM_IPI_H */
diff --git a/include/asm-x86_64/kexec.h b/include/asm-x86_64/kexec.h
new file mode 100644 (file)
index 0000000..b0531c5
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _X86_64_KEXEC_H
+#define _X86_64_KEXEC_H
+
+#include <asm/page.h>
+#include <asm/proto.h>
+
+/*
+ * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return.
+ * I.e. Maximum page that is mapped directly into kernel memory,
+ * and kmap is not required.
+ *
+ * So far x86_64 is limited to 40 physical address bits.
+ */
+
+/* Maximum physical address we can use pages from */
+#define KEXEC_SOURCE_MEMORY_LIMIT      (0xFFFFFFFFFFUL)
+/* Maximum address we can reach in physical address mode */
+#define KEXEC_DESTINATION_MEMORY_LIMIT (0xFFFFFFFFFFUL)
+/* Maximum address we can use for the control pages */
+#define KEXEC_CONTROL_MEMORY_LIMIT     (0xFFFFFFFFFFUL)
+
+/* Allocate one page for the pdp and the second for the code */
+#define KEXEC_CONTROL_CODE_SIZE  (4096UL + 4096UL)
+
+#endif /* _X86_64_KEXEC_H */
diff --git a/include/asm-x86_64/kprobes.h b/include/asm-x86_64/kprobes.h
new file mode 100644 (file)
index 0000000..3f82412
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef _ASM_KPROBES_H
+#define _ASM_KPROBES_H
+/*
+ *  Kernel Probes (KProbes)
+ *  include/asm-x86_64/kprobes.h
+ *
+ * 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.
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ *
+ * 2004-Oct    Prasanna S Panchamukhi <prasanna@in.ibm.com> and Jim Keniston
+ *             kenistoj@us.ibm.com adopted from i386.
+ */
+#include <linux/types.h>
+#include <linux/ptrace.h>
+
+struct pt_regs;
+
+typedef u8 kprobe_opcode_t;
+#define BREAKPOINT_INSTRUCTION 0xcc
+#define MAX_INSN_SIZE 15
+#define MAX_STACK_SIZE 64
+#define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \
+       (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) \
+       ? (MAX_STACK_SIZE) \
+       : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
+
+/* Architecture specific copy of original instruction*/
+struct arch_specific_insn {
+       /* copy of the original instruction */
+       kprobe_opcode_t *insn;
+};
+
+/* trap3/1 are intr gates for kprobes.  So, restore the status of IF,
+ * if necessary, before executing the original int3/1 (trap) handler.
+ */
+static inline void restore_interrupts(struct pt_regs *regs)
+{
+       if (regs->eflags & IF_MASK)
+               local_irq_enable();
+}
+
+extern int post_kprobe_handler(struct pt_regs *regs);
+extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
+extern int kprobe_handler(struct pt_regs *regs);
+
+extern int kprobe_exceptions_notify(struct notifier_block *self,
+                                   unsigned long val, void *data);
+#endif                         /* _ASM_KPROBES_H */
diff --git a/include/asm-x86_64/mach_apic.h b/include/asm-x86_64/mach_apic.h
new file mode 100644 (file)
index 0000000..0acea44
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __ASM_MACH_APIC_H
+#define __ASM_MACH_APIC_H
+
+/*
+ * Copyright 2004 James Cleverdon, IBM.
+ * Subject to the GNU Public License, v.2
+ *
+ * Generic APIC sub-arch defines.
+ *
+ * Hacked for x86-64 by James Cleverdon from i386 architecture code by
+ * Martin Bligh, Andi Kleen, James Bottomley, John Stultz, and
+ * James Cleverdon.
+ */
+
+#include <asm/genapic.h>
+
+#define INT_DELIVERY_MODE (genapic->int_delivery_mode)
+#define INT_DEST_MODE (genapic->int_dest_mode)
+#define INT_DELIVERY_DEST (genapic->int_delivery_dest)
+#define TARGET_CPUS      (genapic->target_cpus())
+#define apic_id_registered (genapic->apic_id_registered)
+#define init_apic_ldr (genapic->init_apic_ldr)
+#define send_IPI_mask (genapic->send_IPI_mask)
+#define send_IPI_allbutself (genapic->send_IPI_allbutself)
+#define send_IPI_all (genapic->send_IPI_all)
+#define cpu_mask_to_apicid (genapic->cpu_mask_to_apicid)
+#define phys_pkg_id    (genapic->phys_pkg_id)
+
+#endif /* __ASM_MACH_APIC_H */
diff --git a/include/linux/attribute_container.h b/include/linux/attribute_container.h
new file mode 100644 (file)
index 0000000..af1010b
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * class_container.h - a generic container for all classes
+ *
+ * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
+ *
+ * This file is licensed under GPLv2
+ */
+
+#ifndef _ATTRIBUTE_CONTAINER_H_
+#define _ATTRIBUTE_CONTAINER_H_
+
+#include <linux/device.h>
+#include <linux/list.h>
+
+struct attribute_container {
+       struct list_head        node;
+       struct list_head        containers;
+       struct class            *class;
+       struct class_device_attribute **attrs;
+       int (*match)(struct attribute_container *, struct device *);
+#define        ATTRIBUTE_CONTAINER_NO_CLASSDEVS        0x01
+       unsigned long           flags;
+};
+
+static inline int
+attribute_container_no_classdevs(struct attribute_container *atc)
+{
+       return atc->flags & ATTRIBUTE_CONTAINER_NO_CLASSDEVS;
+}
+
+static inline void
+attribute_container_set_no_classdevs(struct attribute_container *atc)
+{
+       atc->flags |= ATTRIBUTE_CONTAINER_NO_CLASSDEVS;
+}
+
+int attribute_container_register(struct attribute_container *cont);
+int attribute_container_unregister(struct attribute_container *cont);
+void attribute_container_create_device(struct device *dev,
+                                      int (*fn)(struct attribute_container *,
+                                                struct device *,
+                                                struct class_device *));
+void attribute_container_add_device(struct device *dev,
+                                   int (*fn)(struct attribute_container *,
+                                             struct device *,
+                                             struct class_device *));
+void attribute_container_remove_device(struct device *dev,
+                                      void (*fn)(struct attribute_container *,
+                                                 struct device *,
+                                                 struct class_device *));
+void attribute_container_device_trigger(struct device *dev, 
+                                       int (*fn)(struct attribute_container *,
+                                                 struct device *,
+                                                 struct class_device *));
+void attribute_container_trigger(struct device *dev, 
+                                int (*fn)(struct attribute_container *,
+                                          struct device *));
+int attribute_container_add_attrs(struct class_device *classdev);
+int attribute_container_add_class_device(struct class_device *classdev);
+int attribute_container_add_class_device_adapter(struct attribute_container *cont,
+                                                struct device *dev,
+                                                struct class_device *classdev);
+void attribute_container_remove_attrs(struct class_device *classdev);
+void attribute_container_class_device_del(struct class_device *classdev);
+
+
+
+
+
+
+struct class_device_attribute **attribute_container_classdev_to_attrs(const struct class_device *classdev);
+
+#endif
diff --git a/include/linux/backlight.h b/include/linux/backlight.h
new file mode 100644 (file)
index 0000000..bb9e543
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Backlight Lowlevel Control Abstraction
+ *
+ * Copyright (C) 2003,2004 Hewlett-Packard Company
+ *
+ */
+
+#ifndef _LINUX_BACKLIGHT_H
+#define _LINUX_BACKLIGHT_H
+
+#include <linux/device.h>
+#include <linux/notifier.h>
+
+struct backlight_device;
+struct fb_info;
+
+/* This structure defines all the properties of a backlight
+   (usually attached to a LCD). */
+struct backlight_properties {
+       /* Owner module */
+       struct module *owner;
+       /* Get the backlight power status (0: full on, 1..3: power saving
+          modes; 4: full off), see FB_BLANK_XXX */
+       int (*get_power)(struct backlight_device *);
+       /* Enable or disable power to the LCD (0: on; 4: off, see FB_BLANK_XXX) */
+       int (*set_power)(struct backlight_device *, int power);
+       /* Maximal value for brightness (read-only) */
+       int max_brightness;
+       /* Get current backlight brightness */
+       int (*get_brightness)(struct backlight_device *);
+       /* Set backlight brightness (0..max_brightness) */
+       int (*set_brightness)(struct backlight_device *, int brightness);
+       /* Check if given framebuffer device is the one bound to this backlight;
+          return 0 if not, !=0 if it is. If NULL, backlight always matches the fb. */
+       int (*check_fb)(struct fb_info *);
+};
+
+struct backlight_device {
+       /* This protects the 'props' field. If 'props' is NULL, the driver that
+          registered this device has been unloaded, and if class_get_devdata()
+          points to something in the body of that driver, it is also invalid. */
+       struct semaphore sem;
+       /* If this is NULL, the backing module is unloaded */
+       struct backlight_properties *props;
+       /* The framebuffer notifier block */
+       struct notifier_block fb_notif;
+       /* The class device structure */
+       struct class_device class_dev;
+};
+
+extern struct backlight_device *backlight_device_register(const char *name,
+       void *devdata, struct backlight_properties *bp);
+extern void backlight_device_unregister(struct backlight_device *bd);
+
+#define to_backlight_device(obj) container_of(obj, struct backlight_device, class_dev)
+
+#endif
diff --git a/include/linux/crash_dump.h b/include/linux/crash_dump.h
new file mode 100644 (file)
index 0000000..11c65e9
--- /dev/null
@@ -0,0 +1,34 @@
+#include <linux/kexec.h>
+#include <linux/smp_lock.h>
+#include <linux/device.h>
+#include <linux/proc_fs.h>
+#ifdef CONFIG_CRASH_DUMP
+#include <asm/crash_dump.h>
+#endif
+
+extern unsigned long saved_max_pfn;
+extern struct memelfnote memelfnote;
+extern int notesize(struct memelfnote *);
+extern char *storenote(struct memelfnote *, char *);
+extern void elf_kcore_store_hdr(char *, int, int, struct kcore_list *);
+
+#ifdef CONFIG_CRASH_DUMP
+extern ssize_t copy_oldmem_page(unsigned long, char *, size_t, int);
+extern void __crash_machine_kexec(void);
+extern int crash_dump_on;
+static inline void crash_machine_kexec(void)
+{
+        __crash_machine_kexec();
+}
+#else
+#define crash_machine_kexec()  do { } while(0)
+#endif
+
+
+#if defined(CONFIG_CRASH_DUMP) && defined(CONFIG_PROC_FS)
+extern void crash_enable_by_proc(void);
+extern void crash_create_proc_entry(void);
+#else
+#define crash_enable_by_proc() do { } while(0)
+#define crash_create_proc_entry() do { } while(0)
+#endif
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
new file mode 100644 (file)
index 0000000..6dc7e3e
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ *  debugfs.h - a tiny little debug file system
+ *
+ *  Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *  Copyright (C) 2004 IBM 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.
+ *
+ *  debugfs is for people to use instead of /proc or /sys.
+ *  See Documentation/DocBook/kernel-api for more details.
+ */
+
+#ifndef _DEBUGFS_H_
+#define _DEBUGFS_H_
+
+#if defined(CONFIG_DEBUG_FS)
+struct dentry *debugfs_create_file(const char *name, mode_t mode,
+                                  struct dentry *parent, void *data,
+                                  struct file_operations *fops);
+
+struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
+
+void debugfs_remove(struct dentry *dentry);
+
+struct dentry *debugfs_create_u8(const char *name, mode_t mode,
+                                struct dentry *parent, u8 *value);
+struct dentry *debugfs_create_u16(const char *name, mode_t mode,
+                                 struct dentry *parent, u16 *value);
+struct dentry *debugfs_create_u32(const char *name, mode_t mode,
+                                 struct dentry *parent, u32 *value);
+struct dentry *debugfs_create_bool(const char *name, mode_t mode,
+                                 struct dentry *parent, u32 *value);
+
+#else
+/* 
+ * We do not return NULL from these functions if CONFIG_DEBUG_FS is not enabled
+ * so users have a chance to detect if there was a real error or not.  We don't
+ * want to duplicate the design decision mistakes of procfs and devfs again.
+ */
+
+static inline struct dentry *debugfs_create_file(const char *name, mode_t mode,
+                                                struct dentry *parent,
+                                                void *data,
+                                                struct file_operations *fops)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline struct dentry *debugfs_create_dir(const char *name,
+                                               struct dentry *parent)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline void debugfs_remove(struct dentry *dentry)
+{ }
+
+static inline struct dentry *debugfs_create_u8(const char *name, mode_t mode,
+                                              struct dentry *parent,
+                                              u8 *value)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline struct dentry *debugfs_create_u16(const char *name, mode_t mode,
+                                               struct dentry *parent,
+                                               u8 *value)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline struct dentry *debugfs_create_u32(const char *name, mode_t mode,
+                                               struct dentry *parent,
+                                               u8 *value)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+static inline struct dentry *debugfs_create_bool(const char *name, mode_t mode,
+                                                struct dentry *parent,
+                                                u8 *value)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+#endif
+
+#endif
diff --git a/include/linux/elf-fdpic.h b/include/linux/elf-fdpic.h
new file mode 100644 (file)
index 0000000..9f5b745
--- /dev/null
@@ -0,0 +1,68 @@
+/* elf-fdpic.h: FDPIC ELF load map
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_ELF_FDPIC_H
+#define _LINUX_ELF_FDPIC_H
+
+#include <linux/elf.h>
+
+#define PT_GNU_STACK    (PT_LOOS + 0x474e551)
+
+/* segment mappings for ELF FDPIC libraries/executables/interpreters */
+struct elf32_fdpic_loadseg {
+       Elf32_Addr      addr;           /* core address to which mapped */
+       Elf32_Addr      p_vaddr;        /* VMA recorded in file */
+       Elf32_Word      p_memsz;        /* allocation size recorded in file */
+};
+
+struct elf32_fdpic_loadmap {
+       Elf32_Half      version;        /* version of these structures, just in case... */
+       Elf32_Half      nsegs;          /* number of segments */
+       struct elf32_fdpic_loadseg segs[];
+};
+
+#define ELF32_FDPIC_LOADMAP_VERSION    0x0000
+
+/*
+ * binfmt binary parameters structure
+ */
+struct elf_fdpic_params {
+       struct elfhdr                   hdr;            /* ref copy of ELF header */
+       struct elf_phdr                 *phdrs;         /* ref copy of PT_PHDR table */
+       struct elf32_fdpic_loadmap      *loadmap;       /* loadmap to be passed to userspace */
+       unsigned long                   elfhdr_addr;    /* mapped ELF header user address */
+       unsigned long                   ph_addr;        /* mapped PT_PHDR user address */
+       unsigned long                   map_addr;       /* mapped loadmap user address */
+       unsigned long                   entry_addr;     /* mapped entry user address */
+       unsigned long                   stack_size;     /* stack size requested (PT_GNU_STACK) */
+       unsigned long                   dynamic_addr;   /* mapped PT_DYNAMIC user address */
+       unsigned long                   load_addr;      /* user address at which to map binary */
+       unsigned long                   flags;
+#define ELF_FDPIC_FLAG_ARRANGEMENT     0x0000000f      /* PT_LOAD arrangement flags */
+#define ELF_FDPIC_FLAG_INDEPENDENT     0x00000000      /* PT_LOADs can be put anywhere */
+#define ELF_FDPIC_FLAG_HONOURVADDR     0x00000001      /* PT_LOAD.vaddr must be honoured */
+#define ELF_FDPIC_FLAG_CONSTDISP       0x00000002      /* PT_LOADs require constant
+                                                        * displacement */
+#define ELF_FDPIC_FLAG_CONTIGUOUS      0x00000003      /* PT_LOADs should be contiguous */
+#define ELF_FDPIC_FLAG_EXEC_STACK      0x00000010      /* T if stack to be executable */
+#define ELF_FDPIC_FLAG_NOEXEC_STACK    0x00000020      /* T if stack not to be executable */
+#define ELF_FDPIC_FLAG_EXECUTABLE      0x00000040      /* T if this object is the executable */
+#define ELF_FDPIC_FLAG_PRESENT         0x80000000      /* T if this object is present */
+};
+
+#ifdef CONFIG_MMU
+extern void elf_fdpic_arch_lay_out_mm(struct elf_fdpic_params *exec_params,
+                                     struct elf_fdpic_params *interp_params,
+                                     unsigned long *start_stack,
+                                     unsigned long *start_brk);
+#endif
+
+#endif /* _LINUX_ELF_FDPIC_H */
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
new file mode 100644 (file)
index 0000000..faaff4c
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * include/linux/fsl_devices.h
+ *
+ * Definitions for any platform device related flags or structures for
+ * Freescale processor devices
+ *
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, 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.
+ */
+
+#ifdef __KERNEL__
+#ifndef _FSL_DEVICE_H_
+#define _FSL_DEVICE_H_
+
+#include <linux/types.h>
+
+/*
+ * Some conventions on how we handle peripherals on Freescale chips
+ *
+ * unique device: a platform_device entry in fsl_plat_devs[] plus
+ * associated device information in its platform_data structure.
+ *
+ * A chip is described by a set of unique devices.
+ *
+ * Each sub-arch has its own master list of unique devices and
+ * enumerates them by enum fsl_devices in a sub-arch specific header
+ *
+ * The platform data structure is broken into two parts.  The
+ * first is device specific information that help identify any
+ * unique features of a peripheral.  The second is any
+ * information that may be defined by the board or how the device
+ * is connected externally of the chip.
+ *
+ * naming conventions:
+ * - platform data structures: <driver>_platform_data
+ * - platform data device flags: FSL_<driver>_DEV_<FLAG>
+ * - platform data board flags: FSL_<driver>_BRD_<FLAG>
+ *
+ */
+
+struct gianfar_platform_data {
+       /* device specific information */
+       u32 device_flags;
+       u32 phy_reg_addr;
+
+       /* board specific information */
+       u32 board_flags;
+       u32 phyid;
+       u32 interruptPHY;
+       u8 mac_addr[6];
+};
+
+/* Flags related to gianfar device features */
+#define FSL_GIANFAR_DEV_HAS_GIGABIT            0x00000001
+#define FSL_GIANFAR_DEV_HAS_COALESCE           0x00000002
+#define FSL_GIANFAR_DEV_HAS_RMON               0x00000004
+#define FSL_GIANFAR_DEV_HAS_MULTI_INTR         0x00000008
+
+/* Flags in gianfar_platform_data */
+#define FSL_GIANFAR_BRD_HAS_PHY_INTR   0x00000001      /* if not set use a timer */
+
+struct fsl_i2c_platform_data {
+       /* device specific information */
+       u32 device_flags;
+};
+
+/* Flags related to I2C device features */
+#define FSL_I2C_DEV_SEPARATE_DFSRR     0x00000001
+#define FSL_I2C_DEV_CLOCK_5200         0x00000002
+
+#endif                         /* _FSL_DEVICE_H_ */
+#endif                         /* __KERNEL__ */
diff --git a/include/linux/i2c-algo-sgi.h b/include/linux/i2c-algo-sgi.h
new file mode 100644 (file)
index 0000000..4a0113d
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ */
+
+#ifndef I2C_ALGO_SGI_H
+#define I2C_ALGO_SGI_H 1
+
+#include <linux/i2c.h>
+
+struct i2c_algo_sgi_data {
+       void *data;     /* private data for lowlevel routines */
+       unsigned (*getctrl)(void *data);
+       void (*setctrl)(void *data, unsigned val);
+       unsigned (*rdata)(void *data);
+       void (*wdata)(void *data, unsigned val);
+
+       int xfer_timeout;
+       int ack_timeout;
+};
+
+int i2c_sgi_add_bus(struct i2c_adapter *);
+int i2c_sgi_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_ALGO_SGI_H */
diff --git a/include/linux/i2c-algo-sibyte.h b/include/linux/i2c-algo-sibyte.h
new file mode 100644 (file)
index 0000000..03914de
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2001,2002,2003 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.
+ */
+
+#ifndef I2C_ALGO_SIBYTE_H
+#define I2C_ALGO_SIBYTE_H 1
+
+#include <linux/i2c.h>
+
+struct i2c_algo_sibyte_data {
+       void *data;             /* private data */
+        int   bus;             /* which bus */
+        void *reg_base;                /* CSR base */
+};
+
+int i2c_sibyte_add_bus(struct i2c_adapter *, int speed);
+int i2c_sibyte_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_ALGO_SIBYTE_H */
diff --git a/include/linux/if_infiniband.h b/include/linux/if_infiniband.h
new file mode 100644 (file)
index 0000000..3e659ec
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available at
+ * <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
+ * license, available in the LICENSE.TXT file accompanying this
+ * software.  These details are also available at
+ * <http://openib.org/license.html>.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * Copyright (c) 2004 Topspin Communications.  All rights reserved.
+ *
+ * $Id$
+ */
+
+#ifndef _LINUX_IF_INFINIBAND_H
+#define _LINUX_IF_INFINIBAND_H
+
+#define INFINIBAND_ALEN                20      /* Octets in IPoIB HW addr      */
+
+#endif /* _LINUX_IF_INFINIBAND_H */
diff --git a/include/linux/key-ui.h b/include/linux/key-ui.h
new file mode 100644 (file)
index 0000000..60cc7b7
--- /dev/null
@@ -0,0 +1,97 @@
+/* key-ui.h: key userspace interface stuff for use by keyfs
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_KEY_UI_H
+#define _LINUX_KEY_UI_H
+
+#include <linux/key.h>
+
+/* the key tree */
+extern struct rb_root key_serial_tree;
+extern spinlock_t key_serial_lock;
+
+/* required permissions */
+#define        KEY_VIEW        0x01    /* require permission to view attributes */
+#define        KEY_READ        0x02    /* require permission to read content */
+#define        KEY_WRITE       0x04    /* require permission to update / modify */
+#define        KEY_SEARCH      0x08    /* require permission to search (keyring) or find (key) */
+#define        KEY_LINK        0x10    /* require permission to link */
+#define        KEY_ALL         0x1f    /* all the above permissions */
+
+/*
+ * the keyring payload contains a list of the keys to which the keyring is
+ * subscribed
+ */
+struct keyring_list {
+       unsigned        maxkeys;        /* max keys this list can hold */
+       unsigned        nkeys;          /* number of keys currently held */
+       struct key      *keys[0];
+};
+
+
+/*
+ * check to see whether permission is granted to use a key in the desired way
+ */
+static inline int key_permission(const struct key *key, key_perm_t perm)
+{
+       key_perm_t kperm;
+
+       if (key->uid == current->fsuid)
+               kperm = key->perm >> 16;
+       else if (key->gid != -1 &&
+                key->perm & KEY_GRP_ALL &&
+                in_group_p(key->gid)
+                )
+               kperm = key->perm >> 8;
+       else
+               kperm = key->perm;
+
+       kperm = kperm & perm & KEY_ALL;
+
+       return kperm == perm;
+}
+
+/*
+ * check to see whether permission is granted to use a key in at least one of
+ * the desired ways
+ */
+static inline int key_any_permission(const struct key *key, key_perm_t perm)
+{
+       key_perm_t kperm;
+
+       if (key->uid == current->fsuid)
+               kperm = key->perm >> 16;
+       else if (key->gid != -1 &&
+                key->perm & KEY_GRP_ALL &&
+                in_group_p(key->gid)
+                )
+               kperm = key->perm >> 8;
+       else
+               kperm = key->perm;
+
+       kperm = kperm & perm & KEY_ALL;
+
+       return kperm != 0;
+}
+
+
+extern struct key *lookup_user_key(key_serial_t id, int create, int part,
+                                  key_perm_t perm);
+
+extern long join_session_keyring(const char *name);
+
+extern struct key_type *key_type_lookup(const char *type);
+extern void key_type_put(struct key_type *ktype);
+
+#define key_negative_timeout   60      /* default timeout on a negative key's existence */
+
+
+#endif /* _LINUX_KEY_UI_H */
diff --git a/include/linux/key.h b/include/linux/key.h
new file mode 100644 (file)
index 0000000..15eaba5
--- /dev/null
@@ -0,0 +1,286 @@
+/* key.h: authentication token and access key management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ *
+ * See Documentation/keys.txt for information on keys/keyrings.
+ */
+
+#ifndef _LINUX_KEY_H
+#define _LINUX_KEY_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+#ifdef __KERNEL__
+
+/* key handle serial number */
+typedef int32_t key_serial_t;
+
+/* key handle permissions mask */
+typedef uint32_t key_perm_t;
+
+struct key;
+
+#ifdef CONFIG_KEYS
+
+#undef KEY_DEBUGGING
+
+#define KEY_USR_VIEW   0x00010000      /* user can view a key's attributes */
+#define KEY_USR_READ   0x00020000      /* user can read key payload / view keyring */
+#define KEY_USR_WRITE  0x00040000      /* user can update key payload / add link to keyring */
+#define KEY_USR_SEARCH 0x00080000      /* user can find a key in search / search a keyring */
+#define KEY_USR_LINK   0x00100000      /* user can create a link to a key/keyring */
+#define KEY_USR_ALL    0x001f0000
+
+#define KEY_GRP_VIEW   0x00000100      /* group permissions... */
+#define KEY_GRP_READ   0x00000200
+#define KEY_GRP_WRITE  0x00000400
+#define KEY_GRP_SEARCH 0x00000800
+#define KEY_GRP_LINK   0x00001000
+#define KEY_GRP_ALL    0x00001f00
+
+#define KEY_OTH_VIEW   0x00000001      /* third party permissions... */
+#define KEY_OTH_READ   0x00000002
+#define KEY_OTH_WRITE  0x00000004
+#define KEY_OTH_SEARCH 0x00000008
+#define KEY_OTH_LINK   0x00000010
+#define KEY_OTH_ALL    0x0000001f
+
+struct seq_file;
+struct user_struct;
+
+struct key_type;
+struct key_owner;
+struct keyring_list;
+struct keyring_name;
+
+/*****************************************************************************/
+/*
+ * authentication token / access credential / keyring
+ * - types of key include:
+ *   - keyrings
+ *   - disk encryption IDs
+ *   - Kerberos TGTs and tickets
+ */
+struct key {
+       atomic_t                usage;          /* number of references */
+       key_serial_t            serial;         /* key serial number */
+       struct rb_node          serial_node;
+       struct key_type         *type;          /* type of key */
+       rwlock_t                lock;           /* examination vs change lock */
+       struct rw_semaphore     sem;            /* change vs change sem */
+       struct key_user         *user;          /* owner of this key */
+       time_t                  expiry;         /* time at which key expires (or 0) */
+       uid_t                   uid;
+       gid_t                   gid;
+       key_perm_t              perm;           /* access permissions */
+       unsigned short          quotalen;       /* length added to quota */
+       unsigned short          datalen;        /* payload data length */
+       unsigned short          flags;          /* status flags (change with lock writelocked) */
+#define KEY_FLAG_INSTANTIATED  0x00000001      /* set if key has been instantiated */
+#define KEY_FLAG_DEAD          0x00000002      /* set if key type has been deleted */
+#define KEY_FLAG_REVOKED       0x00000004      /* set if key had been revoked */
+#define KEY_FLAG_IN_QUOTA      0x00000008      /* set if key consumes quota */
+#define KEY_FLAG_USER_CONSTRUCT        0x00000010      /* set if key is being constructed in userspace */
+#define KEY_FLAG_NEGATIVE      0x00000020      /* set if key is negative */
+
+#ifdef KEY_DEBUGGING
+       unsigned                magic;
+#define KEY_DEBUG_MAGIC                0x18273645u
+#define KEY_DEBUG_MAGIC_X      0xf8e9dacbu
+#endif
+
+       /* the description string
+        * - this is used to match a key against search criteria
+        * - this should be a printable string
+        * - eg: for krb5 AFS, this might be "afs@REDHAT.COM"
+        */
+       char                    *description;
+
+       /* type specific data
+        * - this is used by the keyring type to index the name
+        */
+       union {
+               struct list_head        link;
+       } type_data;
+
+       /* key data
+        * - this is used to hold the data actually used in cryptography or
+        *   whatever
+        */
+       union {
+               unsigned long           value;
+               void                    *data;
+               struct keyring_list     *subscriptions;
+       } payload;
+};
+
+/*****************************************************************************/
+/*
+ * kernel managed key type definition
+ */
+struct key_type {
+       /* name of the type */
+       const char *name;
+
+       /* default payload length for quota precalculation (optional)
+        * - this can be used instead of calling key_payload_reserve(), that
+        *   function only needs to be called if the real datalen is different
+        */
+       size_t def_datalen;
+
+       /* instantiate a key of this type
+        * - this method should call key_payload_reserve() to determine if the
+        *   user's quota will hold the payload
+        */
+       int (*instantiate)(struct key *key, const void *data, size_t datalen);
+
+       /* duplicate a key of this type (optional)
+        * - the source key will be locked against change
+        * - the new description will be attached
+        * - the quota will have been adjusted automatically from
+        *   source->quotalen
+        */
+       int (*duplicate)(struct key *key, const struct key *source);
+
+       /* update a key of this type (optional)
+        * - this method should call key_payload_reserve() to recalculate the
+        *   quota consumption
+        * - the key must be locked against read when modifying
+        */
+       int (*update)(struct key *key, const void *data, size_t datalen);
+
+       /* match a key against a description */
+       int (*match)(const struct key *key, const void *desc);
+
+       /* clear the data from a key (optional) */
+       void (*destroy)(struct key *key);
+
+       /* describe a key */
+       void (*describe)(const struct key *key, struct seq_file *p);
+
+       /* read a key's data (optional)
+        * - permission checks will be done by the caller
+        * - the key's semaphore will be readlocked by the caller
+        * - should return the amount of data that could be read, no matter how
+        *   much is copied into the buffer
+        * - shouldn't do the copy if the buffer is NULL
+        */
+       long (*read)(const struct key *key, char __user *buffer, size_t buflen);
+
+       /* internal fields */
+       struct list_head        link;           /* link in types list */
+};
+
+extern struct key_type key_type_keyring;
+
+extern int register_key_type(struct key_type *ktype);
+extern void unregister_key_type(struct key_type *ktype);
+
+extern struct key *key_alloc(struct key_type *type,
+                            const char *desc,
+                            uid_t uid, gid_t gid, key_perm_t perm,
+                            int not_in_quota);
+extern int key_payload_reserve(struct key *key, size_t datalen);
+extern int key_instantiate_and_link(struct key *key,
+                                   const void *data,
+                                   size_t datalen,
+                                   struct key *keyring);
+extern int key_negate_and_link(struct key *key,
+                              unsigned timeout,
+                              struct key *keyring);
+extern void key_revoke(struct key *key);
+extern void key_put(struct key *key);
+
+static inline struct key *key_get(struct key *key)
+{
+       if (key)
+               atomic_inc(&key->usage);
+       return key;
+}
+
+extern struct key *request_key(struct key_type *type,
+                              const char *description,
+                              const char *callout_info);
+
+extern int key_validate(struct key *key);
+
+extern struct key *key_create_or_update(struct key *keyring,
+                                       const char *type,
+                                       const char *description,
+                                       const void *payload,
+                                       size_t plen,
+                                       int not_in_quota);
+
+extern int key_update(struct key *key,
+                     const void *payload,
+                     size_t plen);
+
+extern int key_link(struct key *keyring,
+                   struct key *key);
+
+extern int key_unlink(struct key *keyring,
+                     struct key *key);
+
+extern struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
+                                int not_in_quota, struct key *dest);
+
+extern int keyring_clear(struct key *keyring);
+
+extern struct key *keyring_search(struct key *keyring,
+                                 struct key_type *type,
+                                 const char *description);
+
+extern struct key *search_process_keyrings(struct key_type *type,
+                                          const char *description);
+
+extern int keyring_add_key(struct key *keyring,
+                          struct key *key);
+
+extern struct key *key_lookup(key_serial_t id);
+
+#define key_serial(key) ((key) ? (key)->serial : 0)
+
+/*
+ * the userspace interface
+ */
+extern struct key root_user_keyring, root_session_keyring;
+extern int alloc_uid_keyring(struct user_struct *user);
+extern void switch_uid_keyring(struct user_struct *new_user);
+extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk);
+extern void exit_keys(struct task_struct *tsk);
+extern int suid_keys(struct task_struct *tsk);
+extern int exec_keys(struct task_struct *tsk);
+extern void key_fsuid_changed(struct task_struct *tsk);
+extern void key_fsgid_changed(struct task_struct *tsk);
+extern void key_init(void);
+
+#else /* CONFIG_KEYS */
+
+#define key_validate(k)                        0
+#define key_serial(k)                  0
+#define key_get(k)                     NULL
+#define key_put(k)                     do { } while(0)
+#define alloc_uid_keyring(u)           0
+#define switch_uid_keyring(u)          do { } while(0)
+#define copy_keys(f,t)                 0
+#define exit_keys(t)                   do { } while(0)
+#define suid_keys(t)                   do { } while(0)
+#define exec_keys(t)                   do { } while(0)
+#define key_fsuid_changed(t)           do { } while(0)
+#define key_fsgid_changed(t)           do { } while(0)
+#define key_init()                     do { } while(0)
+
+#endif /* CONFIG_KEYS */
+#endif /* __KERNEL__ */
+#endif /* _LINUX_KEY_H */
diff --git a/include/linux/keyctl.h b/include/linux/keyctl.h
new file mode 100644 (file)
index 0000000..381dedc
--- /dev/null
@@ -0,0 +1,39 @@
+/* keyctl.h: keyctl command IDs
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_KEYCTL_H
+#define _LINUX_KEYCTL_H
+
+/* special process keyring shortcut IDs */
+#define KEY_SPEC_THREAD_KEYRING                -1      /* - key ID for thread-specific keyring */
+#define KEY_SPEC_PROCESS_KEYRING       -2      /* - key ID for process-specific keyring */
+#define KEY_SPEC_SESSION_KEYRING       -3      /* - key ID for session-specific keyring */
+#define KEY_SPEC_USER_KEYRING          -4      /* - key ID for UID-specific keyring */
+#define KEY_SPEC_USER_SESSION_KEYRING  -5      /* - key ID for UID-session keyring */
+#define KEY_SPEC_GROUP_KEYRING         -6      /* - key ID for GID-specific keyring */
+
+/* keyctl commands */
+#define KEYCTL_GET_KEYRING_ID          0       /* ask for a keyring's ID */
+#define KEYCTL_JOIN_SESSION_KEYRING    1       /* join or start named session keyring */
+#define KEYCTL_UPDATE                  2       /* update a key */
+#define KEYCTL_REVOKE                  3       /* revoke a key */
+#define KEYCTL_CHOWN                   4       /* set ownership of a key */
+#define KEYCTL_SETPERM                 5       /* set perms on a key */
+#define KEYCTL_DESCRIBE                        6       /* describe a key */
+#define KEYCTL_CLEAR                   7       /* clear contents of a keyring */
+#define KEYCTL_LINK                    8       /* link a key into a keyring */
+#define KEYCTL_UNLINK                  9       /* unlink a key from a keyring */
+#define KEYCTL_SEARCH                  10      /* search for a key in a keyring */
+#define KEYCTL_READ                    11      /* read a key or keyring's contents */
+#define KEYCTL_INSTANTIATE             12      /* instantiate a partially constructed key */
+#define KEYCTL_NEGATE                  13      /* negate a partially constructed key */
+
+#endif /*  _LINUX_KEYCTL_H */
diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
new file mode 100644 (file)
index 0000000..0e6e972
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * A simple kernel FIFO implementation.
+ *
+ * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#ifndef _LINUX_KFIFO_H
+#define _LINUX_KFIFO_H
+
+#ifdef __KERNEL__
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+
+struct kfifo {
+       unsigned char *buffer;  /* the buffer holding the data */
+       unsigned int size;      /* the size of the allocated buffer */
+       unsigned int in;        /* data is added at offset (in % size) */
+       unsigned int out;       /* data is extracted from off. (out % size) */
+       spinlock_t *lock;       /* protects concurrent modifications */
+};
+
+extern struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
+                               int gfp_mask, spinlock_t *lock);
+extern struct kfifo *kfifo_alloc(unsigned int size, int gfp_mask,
+                                spinlock_t *lock);
+extern void kfifo_free(struct kfifo *fifo);
+extern unsigned int __kfifo_put(struct kfifo *fifo,
+                               unsigned char *buffer, unsigned int len);
+extern unsigned int __kfifo_get(struct kfifo *fifo,
+                               unsigned char *buffer, unsigned int len);
+
+/*
+ * __kfifo_reset - removes the entire FIFO contents, no locking version
+ * @fifo: the fifo to be emptied.
+ */
+static inline void __kfifo_reset(struct kfifo *fifo)
+{
+       fifo->in = fifo->out = 0;
+}
+
+/*
+ * kfifo_reset - removes the entire FIFO contents
+ * @fifo: the fifo to be emptied.
+ */
+static inline void kfifo_reset(struct kfifo *fifo)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(fifo->lock, flags);
+
+       __kfifo_reset(fifo);
+
+       spin_unlock_irqrestore(fifo->lock, flags);
+}
+
+/*
+ * kfifo_put - puts some data into the FIFO
+ * @fifo: the fifo to be used.
+ * @buffer: the data to be added.
+ * @len: the length of the data to be added.
+ *
+ * This function copies at most 'len' bytes from the 'buffer' into
+ * the FIFO depending on the free space, and returns the number of
+ * bytes copied.
+ */
+static inline unsigned int kfifo_put(struct kfifo *fifo,
+                                    unsigned char *buffer, unsigned int len)
+{
+       unsigned long flags;
+       unsigned int ret;
+
+       spin_lock_irqsave(fifo->lock, flags);
+
+       ret = __kfifo_put(fifo, buffer, len);
+
+       spin_unlock_irqrestore(fifo->lock, flags);
+
+       return ret;
+}
+
+/*
+ * kfifo_get - gets some data from the FIFO
+ * @fifo: the fifo to be used.
+ * @buffer: where the data must be copied.
+ * @len: the size of the destination buffer.
+ *
+ * This function copies at most 'len' bytes from the FIFO into the
+ * 'buffer' and returns the number of copied bytes.
+ */
+static inline unsigned int kfifo_get(struct kfifo *fifo,
+                                    unsigned char *buffer, unsigned int len)
+{
+       unsigned long flags;
+       unsigned int ret;
+
+       spin_lock_irqsave(fifo->lock, flags);
+
+       ret = __kfifo_get(fifo, buffer, len);
+
+       /*
+        * optimization: if the FIFO is empty, set the indices to 0
+        * so we don't wrap the next time
+        */
+       if (fifo->in == fifo->out)
+               fifo->in = fifo->out = 0;
+
+       spin_unlock_irqrestore(fifo->lock, flags);
+
+       return ret;
+}
+
+/*
+ * __kfifo_len - returns the number of bytes available in the FIFO, no locking version
+ * @fifo: the fifo to be used.
+ */
+static inline unsigned int __kfifo_len(struct kfifo *fifo)
+{
+       return fifo->in - fifo->out;
+}
+
+/*
+ * kfifo_len - returns the number of bytes available in the FIFO
+ * @fifo: the fifo to be used.
+ */
+static inline unsigned int kfifo_len(struct kfifo *fifo)
+{
+       unsigned long flags;
+       unsigned int ret;
+
+       spin_lock_irqsave(fifo->lock, flags);
+
+       ret = __kfifo_len(fifo);
+
+       spin_unlock_irqrestore(fifo->lock, flags);
+
+       return ret;
+}
+
+#else
+#warning "don't include kernel headers in userspace"
+#endif /* __KERNEL__ */
+#endif
diff --git a/include/linux/kobject_uevent.h b/include/linux/kobject_uevent.h
new file mode 100644 (file)
index 0000000..aa664fe
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * kobject_uevent.h - list of kobject user events that can be generated
+ *
+ * Copyright (C) 2004 IBM Corp.
+ * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * This file is released under the GPLv2.
+ *
+ */
+
+#ifndef _KOBJECT_EVENT_H_
+#define _KOBJECT_EVENT_H_
+
+#define HOTPLUG_PATH_LEN       256
+
+/* path to the hotplug userspace helper executed on an event */
+extern char hotplug_path[];
+
+/*
+ * If you add an action here, you must also add the proper string to the
+ * lib/kobject_uevent.c file.
+ */
+typedef int __bitwise kobject_action_t;
+enum kobject_action {
+       KOBJ_ADD        = (__force kobject_action_t) 0x01,      /* add event, for hotplug */
+       KOBJ_REMOVE     = (__force kobject_action_t) 0x02,      /* remove event, for hotplug */
+       KOBJ_CHANGE     = (__force kobject_action_t) 0x03,      /* a sysfs attribute file has changed */
+       KOBJ_MOUNT      = (__force kobject_action_t) 0x04,      /* mount event for block devices */
+       KOBJ_UMOUNT     = (__force kobject_action_t) 0x05,      /* umount event for block devices */
+       KOBJ_OFFLINE    = (__force kobject_action_t) 0x06,      /* offline event for hotplug devices */
+       KOBJ_ONLINE     = (__force kobject_action_t) 0x07,      /* online event for hotplug devices */
+};
+
+
+#ifdef CONFIG_KOBJECT_UEVENT
+int kobject_uevent(struct kobject *kobj,
+                  enum kobject_action action,
+                  struct attribute *attr);
+int kobject_uevent_atomic(struct kobject *kobj,
+                         enum kobject_action action,
+                         struct attribute *attr);
+#else
+static inline int kobject_uevent(struct kobject *kobj,
+                                enum kobject_action action,
+                                struct attribute *attr)
+{
+       return 0;
+}
+static inline int kobject_uevent_atomic(struct kobject *kobj,
+                                       enum kobject_action action,
+                                       struct attribute *attr)
+{
+       return 0;
+}
+#endif
+
+#endif
diff --git a/include/linux/lcd.h b/include/linux/lcd.h
new file mode 100644 (file)
index 0000000..d739b2e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * LCD Lowlevel Control Abstraction
+ *
+ * Copyright (C) 2003,2004 Hewlett-Packard Company
+ *
+ */
+
+#ifndef _LINUX_LCD_H
+#define _LINUX_LCD_H
+
+#include <linux/device.h>
+#include <linux/notifier.h>
+
+struct lcd_device;
+struct fb_info;
+
+/* This structure defines all the properties of a LCD flat panel. */
+struct lcd_properties {
+       /* Owner module */
+       struct module *owner;
+       /* Get the LCD panel power status (0: full on, 1..3: controller
+          power on, flat panel power off, 4: full off), see FB_BLANK_XXX */
+       int (*get_power)(struct lcd_device *);
+       /* Enable or disable power to the LCD (0: on; 4: off, see FB_BLANK_XXX) */
+       int (*set_power)(struct lcd_device *, int power);
+       /* The maximum value for contrast (read-only) */
+       int max_contrast;
+       /* Get the current contrast setting (0-max_contrast) */
+       int (*get_contrast)(struct lcd_device *);
+       /* Set LCD panel contrast */
+        int (*set_contrast)(struct lcd_device *, int contrast);
+       /* Check if given framebuffer device is the one LCD is bound to;
+          return 0 if not, !=0 if it is. If NULL, lcd always matches the fb. */
+       int (*check_fb)(struct fb_info *);
+};
+
+struct lcd_device {
+       /* This protects the 'props' field. If 'props' is NULL, the driver that
+          registered this device has been unloaded, and if class_get_devdata()
+          points to something in the body of that driver, it is also invalid. */
+       struct semaphore sem;
+       /* If this is NULL, the backing module is unloaded */
+       struct lcd_properties *props;
+       /* The framebuffer notifier block */
+       struct notifier_block fb_notif;
+       /* The class device structure */
+       struct class_device class_dev;
+};
+
+extern struct lcd_device *lcd_device_register(const char *name,
+       void *devdata, struct lcd_properties *lp);
+extern void lcd_device_unregister(struct lcd_device *ld);
+
+#define to_lcd_device(obj) container_of(obj, struct lcd_device, class_dev)
+
+#endif
diff --git a/include/linux/libps2.h b/include/linux/libps2.h
new file mode 100644 (file)
index 0000000..923bdbc
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef _LIBPS2_H
+#define _LIBPS2_H
+
+/*
+ * Copyright (C) 1999-2002 Vojtech Pavlik
+ * Copyright (C) 2004 Dmitry Torokhov
+ *
+ * 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.
+ */
+
+
+#define PS2_CMD_GETID          0x02f2
+#define PS2_CMD_RESET_BAT      0x02ff
+
+#define PS2_RET_BAT            0xaa
+#define PS2_RET_ID             0x00
+#define PS2_RET_ACK            0xfa
+#define PS2_RET_NAK            0xfe
+
+#define PS2_FLAG_ACK           1       /* Waiting for ACK/NAK */
+#define PS2_FLAG_CMD           2       /* Waiting for command to finish */
+#define PS2_FLAG_CMD1          4       /* Waiting for the first byte of command response */
+#define PS2_FLAG_WAITID                8       /* Command execiting is GET ID */
+
+struct ps2dev {
+       struct serio *serio;
+
+       /* Ensures that only one command is executing at a time */
+       struct semaphore cmd_sem;
+
+       /* Used to signal completion from interrupt handler */
+       wait_queue_head_t wait;
+
+       unsigned long flags;
+       unsigned char cmdbuf[6];
+       unsigned char cmdcnt;
+       unsigned char nak;
+};
+
+void ps2_init(struct ps2dev *ps2dev, struct serio *serio);
+int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout);
+int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command);
+int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command);
+int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data);
+int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data);
+void ps2_cmd_aborted(struct ps2dev *ps2dev);
+
+#endif /* _LIBPS2_H */
diff --git a/include/linux/mtd/xip.h b/include/linux/mtd/xip.h
new file mode 100644 (file)
index 0000000..fc07112
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * MTD primitives for XIP support
+ *
+ * Author:     Nicolas Pitre
+ * Created:    Nov 2, 2004
+ * Copyright:  (C) 2004 MontaVista Software, Inc.
+ *
+ * This XIP support for MTD has been loosely inspired
+ * by an earlier patch authored by David Woodhouse.
+ *
+ * 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.
+ *
+ * $Id: xip.h,v 1.2 2004/12/01 15:49:10 nico Exp $
+ */
+
+#ifndef __LINUX_MTD_XIP_H__
+#define __LINUX_MTD_XIP_H__
+
+#include <linux/config.h>
+
+#ifdef CONFIG_MTD_XIP
+
+/*
+ * Function that are modifying the flash state away from array mode must
+ * obviously not be running from flash.  The __xipram is therefore marking
+ * those functions so they get relocated to ram.
+ */
+#define __xipram __attribute__ ((__section__ (".data")))
+
+/*
+ * We really don't want gcc to guess anything.
+ * We absolutely _need_ proper inlining.
+ */
+#include <linux/compiler.h>
+
+/*
+ * Each architecture has to provide the following macros.  They must access
+ * the hardware directly and not rely on any other (XIP) functions since they
+ * won't be available when used (flash not in array mode).
+ *
+ * xip_irqpending()
+ *
+ *     return non zero when any hardware interrupt is pending.
+ *
+ * xip_currtime()
+ *
+ *     return a platform specific time reference to be used with
+ *     xip_elapsed_since().
+ *
+ * xip_elapsed_since(x)
+ *
+ *     return in usecs the elapsed timebetween now and the reference x as
+ *     returned by xip_currtime().
+ *
+ *     note 1: convertion to usec can be approximated, as long as the
+ *             returned value is <= the real elapsed time.
+ *     note 2: this should be able to cope with a few seconds without
+ *             overflowing.
+ */
+
+#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_PXA)
+
+#include <asm/hardware.h>
+#ifdef CONFIG_ARCH_PXA
+#include <asm/arch/pxa-regs.h>
+#endif
+
+#define xip_irqpending()       (ICIP & ICMR)
+
+/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
+#define xip_currtime()         (OSCR)
+#define xip_elapsed_since(x)   (signed)((OSCR - (x)) / 4)
+
+#else
+
+#warning "missing IRQ and timer primitives for XIP MTD support"
+#warning "some of the XIP MTD support code will be disabled"
+#warning "your system will therefore be unresponsive when writing or erasing flash"
+
+#define xip_irqpending()       (0)
+#define xip_currtime()         (0)
+#define xip_elapsed_since(x)   (0)
+
+#endif
+
+/*
+ * xip_cpu_idle() is used when waiting for a delay equal or larger than
+ * the system timer tick period.  This should put the CPU into idle mode
+ * to save power and to be woken up only when some interrupts are pending.
+ * As above, this should not rely upon standard kernel code.
+ */
+
+#if defined(CONFIG_CPU_XSCALE)
+#define xip_cpu_idle()  asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1))
+#else
+#define xip_cpu_idle()  do { } while (0)
+#endif
+
+#else
+
+#define __xipram
+
+#endif /* CONFIG_MTD_XIP */
+
+#endif /* __LINUX_MTD_XIP_H__ */
diff --git a/include/linux/netfilter_bridge/ebt_ulog.h b/include/linux/netfilter_bridge/ebt_ulog.h
new file mode 100644 (file)
index 0000000..b677e26
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _EBT_ULOG_H
+#define _EBT_ULOG_H
+
+#define EBT_ULOG_DEFAULT_NLGROUP 0
+#define EBT_ULOG_DEFAULT_QTHRESHOLD 1
+#define EBT_ULOG_MAXNLGROUPS 32 /* hardcoded netlink max */
+#define EBT_ULOG_PREFIX_LEN 32
+#define EBT_ULOG_MAX_QLEN 50
+#define EBT_ULOG_WATCHER "ulog"
+#define EBT_ULOG_VERSION 1
+
+struct ebt_ulog_info {
+       uint32_t nlgroup;
+       unsigned int cprange;
+       unsigned int qthreshold;
+       char prefix[EBT_ULOG_PREFIX_LEN];
+};
+
+typedef struct ebt_ulog_packet_msg {
+       int version;
+       char indev[IFNAMSIZ];
+       char outdev[IFNAMSIZ];
+       char physindev[IFNAMSIZ];
+       char physoutdev[IFNAMSIZ];
+       char prefix[EBT_ULOG_PREFIX_LEN];
+       struct timeval stamp;
+       unsigned long mark;
+       unsigned int hook;
+       size_t data_len;
+       /* The complete packet, including Ethernet header and perhaps
+        * the VLAN header is appended */
+       unsigned char data[0] __attribute__
+                             ((aligned (__alignof__(struct ebt_ulog_info))));
+} ebt_ulog_packet_msg_t;
+
+#endif /* _EBT_ULOG_H */
diff --git a/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h b/include/linux/netfilter_ipv4/ipt_CLUSTERIP.h
new file mode 100644 (file)
index 0000000..baa83e7
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _IPT_CLUSTERIP_H_target
+#define _IPT_CLUSTERIP_H_target
+
+enum clusterip_hashmode {
+    CLUSTERIP_HASHMODE_SIP = 0,
+    CLUSTERIP_HASHMODE_SIP_SPT,
+    CLUSTERIP_HASHMODE_SIP_SPT_DPT,
+};
+
+#define CLUSTERIP_HASHMODE_MAX CLUSTERIP_HASHMODE_SIP_SPT_DPT
+
+#define CLUSTERIP_MAX_NODES 16
+
+#define CLUSTERIP_FLAG_NEW 0x00000001
+
+struct clusterip_config;
+
+struct ipt_clusterip_tgt_info {
+
+       u_int32_t flags;
+       struct clusterip_config *config;
+       
+       /* only relevant for new ones */
+       u_int8_t clustermac[6];
+       u_int16_t num_total_nodes;
+       u_int16_t num_local_nodes;
+       u_int16_t local_nodes[CLUSTERIP_MAX_NODES];
+       enum clusterip_hashmode hash_mode;
+       u_int32_t hash_initval;
+};
+
+#endif /*_IPT_CLUSTERIP_H_target*/
diff --git a/include/linux/netfilter_ipv4/ipt_CONNMARK.h b/include/linux/netfilter_ipv4/ipt_CONNMARK.h
new file mode 100644 (file)
index 0000000..d3c0253
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _IPT_CONNMARK_H_target
+#define _IPT_CONNMARK_H_target
+
+/* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+enum {
+       IPT_CONNMARK_SET = 0,
+       IPT_CONNMARK_SAVE,
+       IPT_CONNMARK_RESTORE
+};
+
+struct ipt_connmark_target_info {
+       unsigned long mark;
+       unsigned long mask;
+       u_int8_t mode;
+};
+
+#endif /*_IPT_CONNMARK_H_target*/
diff --git a/include/linux/netfilter_ipv4/ipt_connmark.h b/include/linux/netfilter_ipv4/ipt_connmark.h
new file mode 100644 (file)
index 0000000..4657327
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _IPT_CONNMARK_H
+#define _IPT_CONNMARK_H
+
+/* Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+struct ipt_connmark_info {
+       unsigned long mark, mask;
+       u_int8_t invert;
+};
+
+#endif /*_IPT_CONNMARK_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_hashlimit.h b/include/linux/netfilter_ipv4/ipt_hashlimit.h
new file mode 100644 (file)
index 0000000..ac2cb64
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _IPT_HASHLIMIT_H
+#define _IPT_HASHLIMIT_H
+
+/* timings are in milliseconds. */
+#define IPT_HASHLIMIT_SCALE 10000
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+
+/* details of this structure hidden by the implementation */
+struct ipt_hashlimit_htable;
+
+#define IPT_HASHLIMIT_HASH_DIP 0x0001
+#define IPT_HASHLIMIT_HASH_DPT 0x0002
+#define IPT_HASHLIMIT_HASH_SIP 0x0004
+#define IPT_HASHLIMIT_HASH_SPT 0x0008
+
+struct hashlimit_cfg {
+       u_int32_t mode;   /* bitmask of IPT_HASHLIMIT_HASH_* */
+       u_int32_t avg;    /* Average secs between packets * scale */
+       u_int32_t burst;  /* Period multiplier for upper limit. */
+
+       /* user specified */
+       u_int32_t size;         /* how many buckets */
+       u_int32_t max;          /* max number of entries */
+       u_int32_t gc_interval;  /* gc interval */
+       u_int32_t expire;       /* when do entries expire? */
+};
+
+struct ipt_hashlimit_info {
+       char name [IFNAMSIZ];           /* name */
+       struct hashlimit_cfg cfg;
+       struct ipt_hashlimit_htable *hinfo;
+
+       /* Used internally by the kernel */
+       union {
+               void *ptr;
+               struct ipt_hashlimit_info *master;
+       } u;
+};
+#endif /*_IPT_HASHLIMIT_H*/
diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h
new file mode 100644 (file)
index 0000000..4de843d
--- /dev/null
@@ -0,0 +1,326 @@
+#ifndef __LINUX_NODEMASK_H
+#define __LINUX_NODEMASK_H
+
+/*
+ * Nodemasks provide a bitmap suitable for representing the
+ * set of Node's in a system, one bit position per Node number.
+ *
+ * See detailed comments in the file linux/bitmap.h describing the
+ * data type on which these nodemasks are based.
+ *
+ * For details of nodemask_scnprintf() and nodemask_parse(),
+ * see bitmap_scnprintf() and bitmap_parse() in lib/bitmap.c.
+ *
+ * The available nodemask operations are:
+ *
+ * void node_set(node, mask)           turn on bit 'node' in mask
+ * void node_clear(node, mask)         turn off bit 'node' in mask
+ * void nodes_setall(mask)             set all bits
+ * void nodes_clear(mask)              clear all bits
+ * int node_isset(node, mask)          true iff bit 'node' set in mask
+ * int node_test_and_set(node, mask)   test and set bit 'node' in mask
+ *
+ * void nodes_and(dst, src1, src2)     dst = src1 & src2  [intersection]
+ * void nodes_or(dst, src1, src2)      dst = src1 | src2  [union]
+ * void nodes_xor(dst, src1, src2)     dst = src1 ^ src2
+ * void nodes_andnot(dst, src1, src2)  dst = src1 & ~src2
+ * void nodes_complement(dst, src)     dst = ~src
+ *
+ * int nodes_equal(mask1, mask2)       Does mask1 == mask2?
+ * int nodes_intersects(mask1, mask2)  Do mask1 and mask2 intersect?
+ * int nodes_subset(mask1, mask2)      Is mask1 a subset of mask2?
+ * int nodes_empty(mask)               Is mask empty (no bits sets)?
+ * int nodes_full(mask)                        Is mask full (all bits sets)?
+ * int nodes_weight(mask)              Hamming weight - number of set bits
+ *
+ * void nodes_shift_right(dst, src, n) Shift right
+ * void nodes_shift_left(dst, src, n)  Shift left
+ *
+ * int first_node(mask)                        Number lowest set bit, or MAX_NUMNODES
+ * int next_node(node, mask)           Next node past 'node', or MAX_NUMNODES
+ *
+ * nodemask_t nodemask_of_node(node)   Return nodemask with bit 'node' set
+ * NODE_MASK_ALL                       Initializer - all bits set
+ * NODE_MASK_NONE                      Initializer - no bits set
+ * unsigned long *nodes_addr(mask)     Array of unsigned long's in mask
+ *
+ * int nodemask_scnprintf(buf, len, mask) Format nodemask for printing
+ * int nodemask_parse(ubuf, ulen, mask)        Parse ascii string as nodemask
+ *
+ * for_each_node_mask(node, mask)      for-loop node over mask
+ *
+ * int num_online_nodes()              Number of online Nodes
+ * int num_possible_nodes()            Number of all possible Nodes
+ *
+ * int node_online(node)               Is some node online?
+ * int node_possible(node)             Is some node possible?
+ *
+ * int any_online_node(mask)           First online node in mask
+ *
+ * node_set_online(node)               set bit 'node' in node_online_map
+ * node_set_offline(node)              clear bit 'node' in node_online_map
+ *
+ * for_each_node(node)                 for-loop node over node_possible_map
+ * for_each_online_node(node)          for-loop node over node_online_map
+ *
+ * Subtlety:
+ * 1) The 'type-checked' form of node_isset() causes gcc (3.3.2, anyway)
+ *    to generate slightly worse code.  So use a simple one-line #define
+ *    for node_isset(), instead of wrapping an inline inside a macro, the
+ *    way we do the other calls.
+ */
+
+#include <linux/kernel.h>
+#include <linux/threads.h>
+#include <linux/bitmap.h>
+#include <linux/numa.h>
+#include <asm/bug.h>
+
+typedef struct { DECLARE_BITMAP(bits, MAX_NUMNODES); } nodemask_t;
+extern nodemask_t _unused_nodemask_arg_;
+
+#define node_set(node, dst) __node_set((node), &(dst))
+static inline void __node_set(int node, volatile nodemask_t *dstp)
+{
+       set_bit(node, dstp->bits);
+}
+
+#define node_clear(node, dst) __node_clear((node), &(dst))
+static inline void __node_clear(int node, volatile nodemask_t *dstp)
+{
+       clear_bit(node, dstp->bits);
+}
+
+#define nodes_setall(dst) __nodes_setall(&(dst), MAX_NUMNODES)
+static inline void __nodes_setall(nodemask_t *dstp, int nbits)
+{
+       bitmap_fill(dstp->bits, nbits);
+}
+
+#define nodes_clear(dst) __nodes_clear(&(dst), MAX_NUMNODES)
+static inline void __nodes_clear(nodemask_t *dstp, int nbits)
+{
+       bitmap_zero(dstp->bits, nbits);
+}
+
+/* No static inline type checking - see Subtlety (1) above. */
+#define node_isset(node, nodemask) test_bit((node), (nodemask).bits)
+
+#define node_test_and_set(node, nodemask) \
+                       __node_test_and_set((node), &(nodemask))
+static inline int __node_test_and_set(int node, nodemask_t *addr)
+{
+       return test_and_set_bit(node, addr->bits);
+}
+
+#define nodes_and(dst, src1, src2) \
+                       __nodes_and(&(dst), &(src1), &(src2), MAX_NUMNODES)
+static inline void __nodes_and(nodemask_t *dstp, const nodemask_t *src1p,
+                                       const nodemask_t *src2p, int nbits)
+{
+       bitmap_and(dstp->bits, src1p->bits, src2p->bits, nbits);
+}
+
+#define nodes_or(dst, src1, src2) \
+                       __nodes_or(&(dst), &(src1), &(src2), MAX_NUMNODES)
+static inline void __nodes_or(nodemask_t *dstp, const nodemask_t *src1p,
+                                       const nodemask_t *src2p, int nbits)
+{
+       bitmap_or(dstp->bits, src1p->bits, src2p->bits, nbits);
+}
+
+#define nodes_xor(dst, src1, src2) \
+                       __nodes_xor(&(dst), &(src1), &(src2), MAX_NUMNODES)
+static inline void __nodes_xor(nodemask_t *dstp, const nodemask_t *src1p,
+                                       const nodemask_t *src2p, int nbits)
+{
+       bitmap_xor(dstp->bits, src1p->bits, src2p->bits, nbits);
+}
+
+#define nodes_andnot(dst, src1, src2) \
+                       __nodes_andnot(&(dst), &(src1), &(src2), MAX_NUMNODES)
+static inline void __nodes_andnot(nodemask_t *dstp, const nodemask_t *src1p,
+                                       const nodemask_t *src2p, int nbits)
+{
+       bitmap_andnot(dstp->bits, src1p->bits, src2p->bits, nbits);
+}
+
+#define nodes_complement(dst, src) \
+                       __nodes_complement(&(dst), &(src), MAX_NUMNODES)
+static inline void __nodes_complement(nodemask_t *dstp,
+                                       const nodemask_t *srcp, int nbits)
+{
+       bitmap_complement(dstp->bits, srcp->bits, nbits);
+}
+
+#define nodes_equal(src1, src2) \
+                       __nodes_equal(&(src1), &(src2), MAX_NUMNODES)
+static inline int __nodes_equal(const nodemask_t *src1p,
+                                       const nodemask_t *src2p, int nbits)
+{
+       return bitmap_equal(src1p->bits, src2p->bits, nbits);
+}
+
+#define nodes_intersects(src1, src2) \
+                       __nodes_intersects(&(src1), &(src2), MAX_NUMNODES)
+static inline int __nodes_intersects(const nodemask_t *src1p,
+                                       const nodemask_t *src2p, int nbits)
+{
+       return bitmap_intersects(src1p->bits, src2p->bits, nbits);
+}
+
+#define nodes_subset(src1, src2) \
+                       __nodes_subset(&(src1), &(src2), MAX_NUMNODES)
+static inline int __nodes_subset(const nodemask_t *src1p,
+                                       const nodemask_t *src2p, int nbits)
+{
+       return bitmap_subset(src1p->bits, src2p->bits, nbits);
+}
+
+#define nodes_empty(src) __nodes_empty(&(src), MAX_NUMNODES)
+static inline int __nodes_empty(const nodemask_t *srcp, int nbits)
+{
+       return bitmap_empty(srcp->bits, nbits);
+}
+
+#define nodes_full(nodemask) __nodes_full(&(nodemask), MAX_NUMNODES)
+static inline int __nodes_full(const nodemask_t *srcp, int nbits)
+{
+       return bitmap_full(srcp->bits, nbits);
+}
+
+#define nodes_weight(nodemask) __nodes_weight(&(nodemask), MAX_NUMNODES)
+static inline int __nodes_weight(const nodemask_t *srcp, int nbits)
+{
+       return bitmap_weight(srcp->bits, nbits);
+}
+
+#define nodes_shift_right(dst, src, n) \
+                       __nodes_shift_right(&(dst), &(src), (n), MAX_NUMNODES)
+static inline void __nodes_shift_right(nodemask_t *dstp,
+                                       const nodemask_t *srcp, int n, int nbits)
+{
+       bitmap_shift_right(dstp->bits, srcp->bits, n, nbits);
+}
+
+#define nodes_shift_left(dst, src, n) \
+                       __nodes_shift_left(&(dst), &(src), (n), MAX_NUMNODES)
+static inline void __nodes_shift_left(nodemask_t *dstp,
+                                       const nodemask_t *srcp, int n, int nbits)
+{
+       bitmap_shift_left(dstp->bits, srcp->bits, n, nbits);
+}
+
+#define first_node(src) __first_node(&(src), MAX_NUMNODES)
+static inline int __first_node(const nodemask_t *srcp, int nbits)
+{
+       return min_t(int, nbits, find_first_bit(srcp->bits, nbits));
+}
+
+#define next_node(n, src) __next_node((n), &(src), MAX_NUMNODES)
+static inline int __next_node(int n, const nodemask_t *srcp, int nbits)
+{
+       return min_t(int, nbits, find_next_bit(srcp->bits, nbits, n+1));
+}
+
+#define nodemask_of_node(node)                                         \
+({                                                                     \
+       typeof(_unused_nodemask_arg_) m;                                \
+       if (sizeof(m) == sizeof(unsigned long)) {                       \
+               m.bits[0] = 1UL<<(node);                                \
+       } else {                                                        \
+               nodes_clear(m);                                         \
+               node_set((node), m);                                    \
+       }                                                               \
+       m;                                                              \
+})
+
+#define NODE_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(MAX_NUMNODES)
+
+#if MAX_NUMNODES <= BITS_PER_LONG
+
+#define NODE_MASK_ALL                                                  \
+((nodemask_t) { {                                                      \
+       [BITS_TO_LONGS(MAX_NUMNODES)-1] = NODE_MASK_LAST_WORD           \
+} })
+
+#else
+
+#define NODE_MASK_ALL                                                  \
+((nodemask_t) { {                                                      \
+       [0 ... BITS_TO_LONGS(MAX_NUMNODES)-2] = ~0UL,                   \
+       [BITS_TO_LONGS(MAX_NUMNODES)-1] = NODE_MASK_LAST_WORD           \
+} })
+
+#endif
+
+#define NODE_MASK_NONE                                                 \
+((nodemask_t) { {                                                      \
+       [0 ... BITS_TO_LONGS(MAX_NUMNODES)-1] =  0UL                    \
+} })
+
+#define nodes_addr(src) ((src).bits)
+
+#define nodemask_scnprintf(buf, len, src) \
+                       __nodemask_scnprintf((buf), (len), &(src), MAX_NUMNODES)
+static inline int __nodemask_scnprintf(char *buf, int len,
+                                       const nodemask_t *srcp, int nbits)
+{
+       return bitmap_scnprintf(buf, len, srcp->bits, nbits);
+}
+
+#define nodemask_parse(ubuf, ulen, src) \
+                       __nodemask_parse((ubuf), (ulen), &(src), MAX_NUMNODES)
+static inline int __nodemask_parse(const char __user *buf, int len,
+                                       nodemask_t *dstp, int nbits)
+{
+       return bitmap_parse(buf, len, dstp->bits, nbits);
+}
+
+#if MAX_NUMNODES > 1
+#define for_each_node_mask(node, mask)                 \
+       for ((node) = first_node(mask);                 \
+               (node) < MAX_NUMNODES;                  \
+               (node) = next_node((node), (mask)))
+#else /* MAX_NUMNODES == 1 */
+#define for_each_node_mask(node, mask)                 \
+       if (!nodes_empty(mask))                         \
+               for ((node) = 0; (node) < 1; (node)++)
+#endif /* MAX_NUMNODES */
+
+/*
+ * The following particular system nodemasks and operations
+ * on them manage all possible and online nodes.
+ */
+
+extern nodemask_t node_online_map;
+extern nodemask_t node_possible_map;
+
+#if MAX_NUMNODES > 1
+#define num_online_nodes()     nodes_weight(node_online_map)
+#define num_possible_nodes()   nodes_weight(node_possible_map)
+#define node_online(node)      node_isset((node), node_online_map)
+#define node_possible(node)    node_isset((node), node_possible_map)
+#else
+#define num_online_nodes()     1
+#define num_possible_nodes()   1
+#define node_online(node)      ((node) == 0)
+#define node_possible(node)    ((node) == 0)
+#endif
+
+#define any_online_node(mask)                  \
+({                                             \
+       int node;                               \
+       for_each_node_mask(node, (mask))        \
+               if (node_online(node))          \
+                       break;                  \
+       node;                                   \
+})
+
+#define node_set_online(node)     set_bit((node), node_online_map.bits)
+#define node_set_offline(node)    clear_bit((node), node_online_map.bits)
+
+#define for_each_node(node)       for_each_node_mask((node), node_possible_map)
+#define for_each_online_node(node) for_each_node_mask((node), node_online_map)
+
+#endif /* __LINUX_NODEMASK_H */
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
new file mode 100644 (file)
index 0000000..857126a
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * File                pci-acpi.h
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#ifndef _PCI_ACPI_H_
+#define _PCI_ACPI_H_
+
+#define OSC_QUERY_TYPE                 0
+#define OSC_SUPPORT_TYPE               1
+#define OSC_CONTROL_TYPE               2
+#define OSC_SUPPORT_MASKS              0x1f
+
+/*
+ * _OSC DW0 Definition 
+ */
+#define OSC_QUERY_ENABLE               1
+#define OSC_REQUEST_ERROR              2
+#define OSC_INVALID_UUID_ERROR         4
+#define OSC_INVALID_REVISION_ERROR     8
+#define OSC_CAPABILITIES_MASK_ERROR    16
+
+/*
+ * _OSC DW1 Definition (OS Support Fields)
+ */
+#define OSC_EXT_PCI_CONFIG_SUPPORT             1
+#define OSC_ACTIVE_STATE_PWR_SUPPORT           2
+#define OSC_CLOCK_PWR_CAPABILITY_SUPPORT       4
+#define OSC_PCI_SEGMENT_GROUPS_SUPPORT         8
+#define OSC_MSI_SUPPORT                                16
+
+/*
+ * _OSC DW1 Definition (OS Control Fields)
+ */
+#define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL      1
+#define OSC_SHPC_NATIVE_HP_CONTROL             2
+#define OSC_PCI_EXPRESS_PME_CONTROL            4
+#define OSC_PCI_EXPRESS_AER_CONTROL            8
+#define OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL  16
+
+#define OSC_CONTROL_MASKS      (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |    \
+                               OSC_SHPC_NATIVE_HP_CONTROL |            \
+                               OSC_PCI_EXPRESS_PME_CONTROL |           \
+                               OSC_PCI_EXPRESS_AER_CONTROL |           \
+                               OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
+
+#ifdef CONFIG_ACPI
+extern acpi_status pci_osc_control_set(u32 flags);
+extern acpi_status pci_osc_support_set(u32 flags);
+#else
+#if !defined(acpi_status)
+typedef u32            acpi_status;
+#define AE_ERROR       (acpi_status) (0x0001)
+#endif    
+static inline acpi_status pci_osc_control_set(u32 flags) {return AE_ERROR;}
+static inline acpi_status pci_osc_support_set(u32 flags) {return AE_ERROR;} 
+#endif
+
+#endif /* _PCI_ACPI_H_ */
diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h
new file mode 100644 (file)
index 0000000..cd3eafc
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * File:       pcieport_if.h
+ * Purpose:    PCI Express Port Bus Driver's IF Data Structure
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#ifndef _PCIEPORT_IF_H_
+#define _PCIEPORT_IF_H_
+
+/* Port Type */
+#define PCIE_RC_PORT                   4       /* Root port of RC */
+#define PCIE_SW_UPSTREAM_PORT          5       /* Upstream port of Switch */
+#define PCIE_SW_DOWNSTREAM_PORT                6       /* Downstream port of Switch */
+#define PCIE_ANY_PORT                  7
+
+/* Service Type */
+#define PCIE_PORT_SERVICE_PME          1       /* Power Management Event */
+#define PCIE_PORT_SERVICE_AER          2       /* Advanced Error Reporting */
+#define PCIE_PORT_SERVICE_HP           4       /* Native Hotplug */
+#define PCIE_PORT_SERVICE_VC           8       /* Virtual Channel */
+
+/* Root/Upstream/Downstream Port's Interrupt Mode */
+#define PCIE_PORT_INTx_MODE            0
+#define PCIE_PORT_MSI_MODE             1
+#define PCIE_PORT_MSIX_MODE            2
+
+struct pcie_port_service_id {
+       __u32 vendor, device;           /* Vendor and device ID or PCI_ANY_ID*/
+       __u32 subvendor, subdevice;     /* Subsystem ID's or PCI_ANY_ID */
+       __u32 class, class_mask;        /* (class,subclass,prog-if) triplet */
+       __u32 port_type, service_type;  /* Port Entity */
+       kernel_ulong_t driver_data;
+};
+
+struct pcie_device {
+       int             irq;        /* Service IRQ/MSI/MSI-X Vector */
+       int             interrupt_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */        
+       struct pcie_port_service_id id; /* Service ID */
+       struct pci_dev  *port;      /* Root/Upstream/Downstream Port */
+       void            *priv_data; /* Service Private Data */
+       struct device   device;     /* Generic Device Interface */
+};
+#define to_pcie_device(d) container_of(d, struct pcie_device, device)
+
+static inline void set_service_data(struct pcie_device *dev, void *data)
+{
+       dev->priv_data = data;
+}
+
+static inline void* get_service_data(struct pcie_device *dev)
+{
+       return dev->priv_data;
+}
+
+struct pcie_port_service_driver {
+       const char *name;
+       int (*probe) (struct pcie_device *dev, 
+               const struct pcie_port_service_id *id);
+       void (*remove) (struct pcie_device *dev);
+       int (*suspend) (struct pcie_device *dev, u32 state);
+       int (*resume) (struct pcie_device *dev);
+
+       const struct pcie_port_service_id *id_table;
+       struct device_driver driver;
+};
+#define to_service_driver(d) \
+       container_of(d, struct pcie_port_service_driver, driver)
+
+extern int pcie_port_service_register(struct pcie_port_service_driver *new);
+extern void pcie_port_service_unregister(struct pcie_port_service_driver *new);
+
+#endif /* _PCIEPORT_IF_H_ */
diff --git a/include/linux/pktcdvd.h b/include/linux/pktcdvd.h
new file mode 100644 (file)
index 0000000..4e2d2a9
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2000 Jens Axboe <axboe@suse.de>
+ * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and
+ * DVD-RW devices.
+ *
+ */
+#ifndef __PKTCDVD_H
+#define __PKTCDVD_H
+
+#include <linux/types.h>
+
+/*
+ * 1 for normal debug messages, 2 is very verbose. 0 to turn it off.
+ */
+#define PACKET_DEBUG           1
+
+#define        MAX_WRITERS             8
+
+#define PKT_RB_POOL_SIZE       512
+
+/*
+ * How long we should hold a non-full packet before starting data gathering.
+ */
+#define PACKET_WAIT_TIME       (HZ * 5 / 1000)
+
+/*
+ * use drive write caching -- we need deferred error handling to be
+ * able to sucessfully recover with this option (drive will return good
+ * status as soon as the cdb is validated).
+ */
+#if defined(CONFIG_CDROM_PKTCDVD_WCACHE)
+#define USE_WCACHING           1
+#else
+#define USE_WCACHING           0
+#endif
+
+/*
+ * No user-servicable parts beyond this point ->
+ */
+
+/*
+ * device types
+ */
+#define PACKET_CDR             1
+#define        PACKET_CDRW             2
+#define PACKET_DVDR            3
+#define PACKET_DVDRW           4
+
+/*
+ * flags
+ */
+#define PACKET_WRITABLE                1       /* pd is writable */
+#define PACKET_NWA_VALID       2       /* next writable address valid */
+#define PACKET_LRA_VALID       3       /* last recorded address valid */
+#define PACKET_MERGE_SEGS      4       /* perform segment merging to keep */
+                                       /* underlying cdrom device happy */
+
+/*
+ * Disc status -- from READ_DISC_INFO
+ */
+#define PACKET_DISC_EMPTY      0
+#define PACKET_DISC_INCOMPLETE 1
+#define PACKET_DISC_COMPLETE   2
+#define PACKET_DISC_OTHER      3
+
+/*
+ * write type, and corresponding data block type
+ */
+#define PACKET_MODE1           1
+#define PACKET_MODE2           2
+#define PACKET_BLOCK_MODE1     8
+#define PACKET_BLOCK_MODE2     10
+
+/*
+ * Last session/border status
+ */
+#define PACKET_SESSION_EMPTY           0
+#define PACKET_SESSION_INCOMPLETE      1
+#define PACKET_SESSION_RESERVED                2
+#define PACKET_SESSION_COMPLETE                3
+
+#define PACKET_MCN                     "4a656e734178626f65323030300000"
+
+#undef PACKET_USE_LS
+
+#define PKT_CTRL_CMD_SETUP     0
+#define PKT_CTRL_CMD_TEARDOWN  1
+#define PKT_CTRL_CMD_STATUS    2
+
+struct pkt_ctrl_command {
+       __u32 command;                          /* in: Setup, teardown, status */
+       __u32 dev_index;                        /* in/out: Device index */
+       __u32 dev;                              /* in/out: Device nr for cdrw device */
+       __u32 pkt_dev;                          /* in/out: Device nr for packet device */
+       __u32 num_devices;                      /* out: Largest device index + 1 */
+       __u32 padding;                          /* Not used */
+};
+
+/*
+ * packet ioctls
+ */
+#define PACKET_IOCTL_MAGIC     ('X')
+#define PACKET_CTRL_CMD                _IOWR(PACKET_IOCTL_MAGIC, 1, struct pkt_ctrl_command)
+
+#ifdef __KERNEL__
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/cdrom.h>
+
+struct packet_settings
+{
+       __u8                    size;           /* packet size in (512 byte) sectors */
+       __u8                    fp;             /* fixed packets */
+       __u8                    link_loss;      /* the rest is specified
+                                                * as per Mt Fuji */
+       __u8                    write_type;
+       __u8                    track_mode;
+       __u8                    block_mode;
+};
+
+/*
+ * Very crude stats for now
+ */
+struct packet_stats
+{
+       unsigned long           pkt_started;
+       unsigned long           pkt_ended;
+       unsigned long           secs_w;
+       unsigned long           secs_rg;
+       unsigned long           secs_r;
+};
+
+struct packet_cdrw
+{
+       struct list_head        pkt_free_list;
+       struct list_head        pkt_active_list;
+       spinlock_t              active_list_lock; /* Serialize access to pkt_active_list */
+       struct task_struct      *thread;
+       atomic_t                pending_bios;
+};
+
+/*
+ * Switch to high speed reading after reading this many kilobytes
+ * with no interspersed writes.
+ */
+#define HI_SPEED_SWITCH 512
+
+struct packet_iosched
+{
+       atomic_t                attention;      /* Set to non-zero when queue processing is needed */
+       int                     writing;        /* Non-zero when writing, zero when reading */
+       spinlock_t              lock;           /* Protecting read/write queue manipulations */
+       struct bio              *read_queue;
+       struct bio              *read_queue_tail;
+       struct bio              *write_queue;
+       struct bio              *write_queue_tail;
+       int                     high_prio_read; /* An important read request has been queued */
+       int                     successive_reads;
+};
+
+/*
+ * 32 buffers of 2048 bytes
+ */
+#define PACKET_MAX_SIZE                32
+#define PAGES_PER_PACKET       (PACKET_MAX_SIZE * CD_FRAMESIZE / PAGE_SIZE)
+#define PACKET_MAX_SECTORS     (PACKET_MAX_SIZE * CD_FRAMESIZE >> 9)
+
+enum packet_data_state {
+       PACKET_IDLE_STATE,                      /* Not used at the moment */
+       PACKET_WAITING_STATE,                   /* Waiting for more bios to arrive, so */
+                                               /* we don't have to do as much */
+                                               /* data gathering */
+       PACKET_READ_WAIT_STATE,                 /* Waiting for reads to fill in holes */
+       PACKET_WRITE_WAIT_STATE,                /* Waiting for the write to complete */
+       PACKET_RECOVERY_STATE,                  /* Recover after read/write errors */
+       PACKET_FINISHED_STATE,                  /* After write has finished */
+
+       PACKET_NUM_STATES                       /* Number of possible states */
+};
+
+/*
+ * Information needed for writing a single packet
+ */
+struct pktcdvd_device;
+
+struct packet_data
+{
+       struct list_head        list;
+
+       spinlock_t              lock;           /* Lock protecting state transitions and */
+                                               /* orig_bios list */
+
+       struct bio              *orig_bios;     /* Original bios passed to pkt_make_request */
+       struct bio              *orig_bios_tail;/* that will be handled by this packet */
+       int                     write_size;     /* Total size of all bios in the orig_bios */
+                                               /* list, measured in number of frames */
+
+       struct bio              *w_bio;         /* The bio we will send to the real CD */
+                                               /* device once we have all data for the */
+                                               /* packet we are going to write */
+       sector_t                sector;         /* First sector in this packet */
+       int                     frames;         /* Number of frames in this packet */
+
+       enum packet_data_state  state;          /* Current state */
+       atomic_t                run_sm;         /* Incremented whenever the state */
+                                               /* machine needs to be run */
+       long                    sleep_time;     /* Set this to non-zero to make the state */
+                                               /* machine run after this many jiffies. */
+
+       atomic_t                io_wait;        /* Number of pending IO operations */
+       atomic_t                io_errors;      /* Number of read/write errors during IO */
+
+       struct bio              *r_bios[PACKET_MAX_SIZE]; /* bios to use during data gathering */
+       struct page             *pages[PAGES_PER_PACKET];
+
+       int                     cache_valid;    /* If non-zero, the data for the zone defined */
+                                               /* by the sector variable is completely cached */
+                                               /* in the pages[] vector. */
+
+       int                     id;             /* ID number for debugging */
+       struct pktcdvd_device   *pd;
+};
+
+struct pkt_rb_node {
+       struct rb_node          rb_node;
+       struct bio              *bio;
+};
+
+struct packet_stacked_data
+{
+       struct bio              *bio;           /* Original read request bio */
+       struct pktcdvd_device   *pd;
+};
+#define PSD_POOL_SIZE          64
+
+struct pktcdvd_device
+{
+       struct block_device     *bdev;          /* dev attached */
+       dev_t                   pkt_dev;        /* our dev */
+       char                    name[20];
+       struct packet_settings  settings;
+       struct packet_stats     stats;
+       int                     refcnt;         /* Open count */
+       int                     write_speed;    /* current write speed, kB/s */
+       int                     read_speed;     /* current read speed, kB/s */
+       unsigned long           offset;         /* start offset */
+       __u8                    mode_offset;    /* 0 / 8 */
+       __u8                    type;
+       unsigned long           flags;
+       __u16                   mmc3_profile;
+       __u32                   nwa;            /* next writable address */
+       __u32                   lra;            /* last recorded address */
+       struct packet_cdrw      cdrw;
+       wait_queue_head_t       wqueue;
+
+       spinlock_t              lock;           /* Serialize access to bio_queue */
+       struct rb_root          bio_queue;      /* Work queue of bios we need to handle */
+       int                     bio_queue_size; /* Number of nodes in bio_queue */
+       sector_t                current_sector; /* Keep track of where the elevator is */
+       atomic_t                scan_queue;     /* Set to non-zero when pkt_handle_queue */
+                                               /* needs to be run. */
+       mempool_t               *rb_pool;       /* mempool for pkt_rb_node allocations */
+
+       struct packet_iosched   iosched;
+       struct gendisk          *disk;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* __PKTCDVD_H */
diff --git a/include/linux/rslib.h b/include/linux/rslib.h
new file mode 100644 (file)
index 0000000..980c8f7
--- /dev/null
@@ -0,0 +1,105 @@
+/* 
+ * include/linux/rslib.h
+ *
+ * Overview:
+ *   Generic Reed Solomon encoder / decoder library
+ *   
+ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * RS code lifted from reed solomon library written by Phil Karn
+ * Copyright 2002 Phil Karn, KA9Q
+ *
+ * $Id: rslib.h,v 1.3 2004/10/05 22:08:22 gleixner Exp $
+ *
+ * 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.
+ */
+
+#ifndef _RSLIB_H_
+#define _RSLIB_H_
+
+#include <linux/list.h>
+
+/** 
+ * struct rs_control - rs control structure
+ * 
+ * @mm:                Bits per symbol
+ * @nn:                Symbols per block (= (1<<mm)-1)
+ * @alpha_to:  log lookup table
+ * @index_of:  Antilog lookup table
+ * @genpoly:   Generator polynomial 
+ * @nroots:    Number of generator roots = number of parity symbols
+ * @fcr:       First consecutive root, index form
+ * @prim:      Primitive element, index form 
+ * @iprim:     prim-th root of 1, index form 
+ * @gfpoly:    The primitive generator polynominal 
+ * @users:     Users of this structure 
+ * @list:      List entry for the rs control list
+*/
+struct rs_control {
+       int             mm;
+       int             nn;
+       uint16_t        *alpha_to;
+       uint16_t        *index_of;
+       uint16_t        *genpoly;
+       int             nroots;
+       int             fcr;
+       int             prim;
+       int             iprim;
+       int             gfpoly;
+       int             users;
+       struct list_head list;
+};
+
+/* General purpose RS codec, 8-bit data width, symbol width 1-15 bit  */
+#ifdef CONFIG_REED_SOLOMON_ENC8
+int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par,
+              uint16_t invmsk);
+#endif
+#ifdef CONFIG_REED_SOLOMON_DEC8
+int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len, 
+               uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
+              uint16_t *corr);
+#endif
+
+/* General purpose RS codec, 16-bit data width, symbol width 1-15 bit  */
+#ifdef CONFIG_REED_SOLOMON_ENC16
+int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par,
+               uint16_t invmsk);
+#endif
+#ifdef CONFIG_REED_SOLOMON_DEC16
+int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
+               uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
+               uint16_t *corr);
+#endif
+
+/* Create or get a matching rs control structure */
+struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, 
+                          int nroots);
+
+/* Release a rs control structure */
+void free_rs(struct rs_control *rs);
+
+/** modulo replacement for galois field arithmetics
+ *
+ *  @rs:       the rs control structure
+ *  @x:                the value to reduce
+ *
+ *  where
+ *  rs->mm = number of bits per symbol 
+ *  rs->nn = (2^rs->mm) - 1
+ *  
+ *  Simple arithmetic modulo would return a wrong result for values
+ *  >= 3 * rs->nn
+*/
+static inline int rs_modnn(struct rs_control *rs, int x)
+{
+       while (x >= rs->nn) {
+               x -= rs->nn;
+               x = (x >> rs->mm) + (x & rs->nn);
+       }
+       return x;
+}
+
+#endif
diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h
new file mode 100644 (file)
index 0000000..7f717e9
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _LINUX_SCATTERLIST_H
+#define _LINUX_SCATTERLIST_H
+
+static inline void sg_init_one(struct scatterlist *sg,
+                              u8 *buf, unsigned int buflen)
+{
+       memset(sg, 0, sizeof(*sg));
+
+       sg->page = virt_to_page(buf);
+       sg->offset = offset_in_page(buf);
+       sg->length = buflen;
+}
+
+#endif /* _LINUX_SCATTERLIST_H */
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
new file mode 100644 (file)
index 0000000..823181a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  linux/include/linux/serial_8250.h
+ *
+ *  Copyright (C) 2004 Russell King
+ *
+ * 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 _LINUX_SERIAL_8250_H
+#define _LINUX_SERIAL_8250_H
+
+#include <linux/serial_core.h>
+#include <linux/device.h>
+
+struct plat_serial8250_port {
+       unsigned long   iobase;         /* io base address */
+       void __iomem    *membase;       /* ioremap cookie or NULL */
+       unsigned long   mapbase;        /* resource base */
+       unsigned int    irq;            /* interrupt number */
+       unsigned int    uartclk;        /* UART clock rate */
+       unsigned char   regshift;       /* register shift */
+       unsigned char   iotype;         /* UPIO_* */
+       unsigned int    flags;          /* UPF_* flags */
+};
+
+#endif
diff --git a/include/linux/tc_act/tc_ipt.h b/include/linux/tc_act/tc_ipt.h
new file mode 100644 (file)
index 0000000..4b6f7b6
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef __LINUX_TC_IPT_H
+#define __LINUX_TC_IPT_H
+
+#include <linux/pkt_cls.h>
+
+#define TCA_ACT_IPT 6
+
+enum
+{
+       TCA_IPT_UNSPEC,
+       TCA_IPT_TABLE,
+       TCA_IPT_HOOK,
+       TCA_IPT_INDEX,
+       TCA_IPT_CNT,
+       TCA_IPT_TM,
+       TCA_IPT_TARG,
+       __TCA_IPT_MAX
+};
+#define TCA_IPT_MAX (__TCA_IPT_MAX - 1)
+                                                                                
+#endif
diff --git a/include/linux/tc_act/tc_mirred.h b/include/linux/tc_act/tc_mirred.h
new file mode 100644 (file)
index 0000000..71d6340
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __LINUX_TC_MIR_H
+#define __LINUX_TC_MIR_H
+
+#include <linux/pkt_cls.h>
+
+#define TCA_ACT_MIRRED 8
+#define TCA_EGRESS_REDIR 1  /* packet redirect to EGRESS*/
+#define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
+#define TCA_INGRESS_REDIR 3  /* packet redirect to INGRESS*/
+#define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
+                                                                                
+struct tc_mirred
+{
+       tc_gen;
+       int                     eaction;   /* one of IN/EGRESS_MIRROR/REDIR */
+       __u32                   ifindex;  /* ifindex of egress port */
+};
+                                                                                
+enum
+{
+       TCA_MIRRED_UNSPEC,
+       TCA_MIRRED_TM,
+       TCA_MIRRED_PARMS,
+       __TCA_MIRRED_MAX
+};
+#define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
+                                                                                
+#endif
diff --git a/include/linux/tc_act/tc_pedit.h b/include/linux/tc_act/tc_pedit.h
new file mode 100644 (file)
index 0000000..83e56e3
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef __LINUX_TC_PED_H
+#define __LINUX_TC_PED_H
+
+#include <linux/pkt_cls.h>
+
+#define TCA_ACT_PEDIT 7
+
+enum
+{
+       TCA_PEDIT_UNSPEC,
+       TCA_PEDIT_TM,
+       TCA_PEDIT_PARMS,
+       __TCA_PEDIT_MAX
+};
+#define TCA_PEDIT_MAX (__TCA_PEDIT_MAX - 1)
+                                                                                
+struct tc_pedit_key
+{
+       __u32           mask;  /* AND */
+       __u32           val;   /*XOR */
+       __u32           off;  /*offset */
+       __u32           at;
+       __u32           offmask;
+       __u32           shift;
+};
+                                                                                
+struct tc_pedit_sel
+{
+       tc_gen;
+       unsigned char           nkeys;
+       unsigned char           flags;
+       struct tc_pedit_key     keys[0];
+};
+#define tc_pedit tc_pedit_sel
+
+#endif
diff --git a/include/linux/transport_class.h b/include/linux/transport_class.h
new file mode 100644 (file)
index 0000000..57e6020
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * transport_class.h - a generic container for all transport classes
+ *
+ * Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
+ *
+ * This file is licensed under GPLv2
+ */
+
+#ifndef _TRANSPORT_CLASS_H_
+#define _TRANSPORT_CLASS_H_
+
+#include <linux/device.h>
+#include <linux/attribute_container.h>
+
+struct transport_class {
+       struct class class;
+       int (*setup)(struct device *);
+       int (*configure)(struct device *);
+       int (*remove)(struct device *);
+};
+
+#define DECLARE_TRANSPORT_CLASS(cls, nm, su, rm, cfg)                  \
+struct transport_class cls = {                                         \
+       .class = {                                                      \
+               .name = nm,                                             \
+       },                                                              \
+       .setup = su,                                                    \
+       .remove = rm,                                                   \
+       .configure = cfg,                                               \
+}
+
+
+struct anon_transport_class {
+       struct transport_class tclass;
+       struct attribute_container container;
+};
+
+#define DECLARE_ANON_TRANSPORT_CLASS(cls, mtch, cfg)           \
+struct anon_transport_class cls = {                            \
+       .tclass = {                                             \
+               .configure = cfg,                               \
+       },                                                      \
+       . container = {                                         \
+               .match = mtch,                                  \
+       },                                                      \
+}
+
+#define class_to_transport_class(x) \
+       container_of(x, struct transport_class, class)
+
+void transport_remove_device(struct device *);
+void transport_add_device(struct device *);
+void transport_setup_device(struct device *);
+void transport_configure_device(struct device *);
+void transport_destroy_device(struct device *);
+
+static inline void
+transport_register_device(struct device *dev)
+{
+       transport_setup_device(dev);
+       transport_add_device(dev);
+}
+
+static inline void
+transport_unregister_device(struct device *dev)
+{
+       transport_remove_device(dev);
+       transport_destroy_device(dev);
+}
+
+int transport_class_register(struct transport_class *);
+int anon_transport_class_register(struct anon_transport_class *);
+void transport_class_unregister(struct transport_class *);
+void anon_transport_class_unregister(struct anon_transport_class *);
+
+
+#endif
diff --git a/include/linux/usb_sl811.h b/include/linux/usb_sl811.h
new file mode 100644 (file)
index 0000000..4f2d012
--- /dev/null
@@ -0,0 +1,26 @@
+
+/*
+ * board initialization should put one of these into dev->platform_data
+ * and place the sl811hs onto platform_bus named "sl811-hcd".
+ */
+
+struct sl811_platform_data {
+       unsigned        can_wakeup:1;
+
+       /* given port_power, msec/2 after power on till power good */
+       u8              potpg;
+
+       /* mA/2 power supplied on this port (max = default = 250) */
+       u8              power;
+
+       /* sl811 relies on an external source of VBUS current */
+       void            (*port_power)(struct device *dev, int is_on);
+
+       /* pulse sl811 nRST (probably with a GPIO) */
+       void            (*reset)(struct device *dev);
+
+       // some boards need something like these:
+       // int          (*check_overcurrent)(struct device *dev);
+       // void         (*clock_enable)(struct device *dev, int is_on);
+};
+
diff --git a/include/linux/via.h b/include/linux/via.h
new file mode 100644 (file)
index 0000000..86ae3bc
--- /dev/null
@@ -0,0 +1,22 @@
+/* Miscellaneous definitions for VIA chipsets
+   Currently used only by drivers/parport/parport_pc.c */
+
+/* Values for SuperIO function select configuration register */
+#define VIA_FUNCTION_PARPORT_SPP     0x00
+#define VIA_FUNCTION_PARPORT_ECP     0x01
+#define VIA_FUNCTION_PARPORT_EPP     0x02
+#define VIA_FUNCTION_PARPORT_DISABLE 0x03
+#define VIA_FUNCTION_PROBE           0xFF /* Special magic value to be used in code, not to be written into chip */
+
+/* Bits for parallel port mode configuration register */
+#define VIA_PARPORT_ECPEPP 0X20
+#define VIA_PARPORT_BIDIR  0x80
+
+/* VIA configuration registers */
+#define VIA_CONFIG_INDEX 0x3F0
+#define VIA_CONFIG_DATA  0x3F1
+
+/* Mask for parallel port IRQ bits (in ISA PnP IRQ routing register 1) */
+#define VIA_IRQCONTROL_PARALLEL 0xF0
+/* Mask for parallel port DMA bits (in ISA PnP DMA routing register) */
+#define VIA_DMACONTROL_PARALLEL 0x0C
diff --git a/include/media/tveeprom.h b/include/media/tveeprom.h
new file mode 100644 (file)
index 0000000..627603e
--- /dev/null
@@ -0,0 +1,23 @@
+struct tveeprom {
+       u32 has_radio;
+
+       u32 tuner_type;
+       u32 tuner_formats;
+
+       u32 digitizer;
+       u32 digitizer_formats;
+
+       u32 audio_processor;
+       /* a_p_fmts? */
+
+       u32 model;
+       u32 revision;
+       u32 serial_number;
+       char rev_str[5];
+};
+
+void tveeprom_hauppauge_analog(struct tveeprom *tvee,
+                              unsigned char *eeprom_data);
+
+int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len);
+int tveeprom_dump(unsigned char *eedata, int len);
diff --git a/include/media/video-buf-dvb.h b/include/media/video-buf-dvb.h
new file mode 100644 (file)
index 0000000..53eac76
--- /dev/null
@@ -0,0 +1,34 @@
+#include <dvbdev.h>
+#include <dmxdev.h>
+#include <dvb_demux.h>
+#include <dvb_net.h>
+#include <dvb_frontend.h>
+
+struct videobuf_dvb {
+       /* filling that the job of the driver */
+       char                       *name;
+       struct dvb_frontend        *frontend;
+       struct videobuf_queue      dvbq;
+
+       /* video-buf-dvb state info */
+       struct semaphore           lock;
+       struct task_struct         *thread;
+       int                        nfeeds;
+
+       /* videobuf_dvb_(un)register manges this */
+       struct dvb_adapter         *adapter;
+       struct dvb_demux           demux;
+       struct dmxdev              dmxdev;
+       struct dmx_frontend        fe_hw;
+       struct dmx_frontend        fe_mem;
+       struct dvb_net             net;
+};
+
+int videobuf_dvb_register(struct videobuf_dvb *dvb);
+void videobuf_dvb_unregister(struct videobuf_dvb *dvb);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/include/net/act_api.h b/include/net/act_api.h
new file mode 100644 (file)
index 0000000..749637e
--- /dev/null
@@ -0,0 +1,119 @@
+#ifndef __NET_ACT_API_H
+#define __NET_ACT_API_H
+
+/*
+ * Public police action API for classifiers/qdiscs
+ */
+
+#include <net/sch_generic.h>
+#include <net/pkt_sched.h>
+
+#define tca_gen(name) \
+struct tcf_##name *next; \
+       u32 index; \
+       int refcnt; \
+       int bindcnt; \
+       u32 capab; \
+       int action; \
+       struct tcf_t tm; \
+       struct gnet_stats_basic bstats; \
+       struct gnet_stats_queue qstats; \
+       struct gnet_stats_rate_est rate_est; \
+       spinlock_t *stats_lock; \
+       spinlock_t lock
+
+struct tcf_police
+{
+       tca_gen(police);
+       int             result;
+       u32             ewma_rate;
+       u32             burst;
+       u32             mtu;
+       u32             toks;
+       u32             ptoks;
+       psched_time_t   t_c;
+       struct qdisc_rate_table *R_tab;
+       struct qdisc_rate_table *P_tab;
+};
+
+#ifdef CONFIG_NET_CLS_ACT
+
+#define ACT_P_CREATED 1
+#define ACT_P_DELETED 1
+
+struct tcf_act_hdr
+{
+       tca_gen(act_hdr);
+};
+
+struct tc_action
+{
+       void *priv;
+       struct tc_action_ops *ops;
+       __u32   type;   /* for backward compat(TCA_OLD_COMPAT) */
+       __u32   order; 
+       struct tc_action *next;
+};
+
+#define TCA_CAP_NONE 0
+struct tc_action_ops
+{
+       struct tc_action_ops *next;
+       char    kind[IFNAMSIZ];
+       __u32   type; /* TBD to match kind */
+       __u32   capab;  /* capabilities includes 4 bit version */
+       struct module           *owner;
+       int     (*act)(struct sk_buff **, struct tc_action *);
+       int     (*get_stats)(struct sk_buff *, struct tc_action *);
+       int     (*dump)(struct sk_buff *, struct tc_action *,int , int);
+       int     (*cleanup)(struct tc_action *, int bind);
+       int     (*lookup)(struct tc_action *, u32 );
+       int     (*init)(struct rtattr *,struct rtattr *,struct tc_action *, int , int );
+       int     (*walk)(struct sk_buff *, struct netlink_callback *, int , struct tc_action *);
+};
+
+extern int tcf_register_action(struct tc_action_ops *a);
+extern int tcf_unregister_action(struct tc_action_ops *a);
+extern void tcf_action_destroy(struct tc_action *a, int bind);
+extern int tcf_action_exec(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res);
+extern int tcf_action_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a,char *n, int ovr, int bind);
+extern int tcf_action_init_1(struct rtattr *rta, struct rtattr *est, struct tc_action *a,char *n, int ovr, int bind);
+extern int tcf_action_dump(struct sk_buff *skb, struct tc_action *a, int, int);
+extern int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int);
+extern int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int);
+extern int tcf_action_copy_stats (struct sk_buff *,struct tc_action *);
+extern int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est,struct tc_action *,int , int );
+extern int tcf_act_police_dump(struct sk_buff *, struct tc_action *, int, int);
+extern int tcf_act_police(struct sk_buff **skb, struct tc_action *a);
+#endif /* CONFIG_NET_CLS_ACT */
+
+extern int tcf_police(struct sk_buff *skb, struct tcf_police *p);
+extern void tcf_police_destroy(struct tcf_police *p);
+extern struct tcf_police * tcf_police_locate(struct rtattr *rta, struct rtattr *est);
+extern int tcf_police_dump(struct sk_buff *skb, struct tcf_police *p);
+extern int tcf_police_dump_stats(struct sk_buff *skb, struct tcf_police *p);
+
+static inline int
+tcf_police_release(struct tcf_police *p, int bind)
+{
+       int ret = 0;
+#ifdef CONFIG_NET_CLS_ACT
+       if (p) {
+               if (bind) {
+                        p->bindcnt--;
+               }
+               p->refcnt--;
+               if (p->refcnt <= 0 && !p->bindcnt) {
+                       tcf_police_destroy(p);
+                       ret = 1;
+               }
+       }
+#else
+       if (p && --p->refcnt == 0)
+               tcf_police_destroy(p);
+
+#endif /* CONFIG_NET_CLS_ACT */
+       return ret;
+}
+
+#endif
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
new file mode 100644 (file)
index 0000000..c57504b
--- /dev/null
@@ -0,0 +1,175 @@
+#ifndef __NET_SCHED_GENERIC_H
+#define __NET_SCHED_GENERIC_H
+
+#include <linux/config.h>
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+#include <linux/pkt_sched.h>
+#include <linux/pkt_cls.h>
+#include <net/gen_stats.h>
+
+struct Qdisc_ops;
+struct qdisc_walker;
+struct tcf_walker;
+struct module;
+
+struct qdisc_rate_table
+{
+       struct tc_ratespec rate;
+       u32             data[256];
+       struct qdisc_rate_table *next;
+       int             refcnt;
+};
+
+struct Qdisc
+{
+       int                     (*enqueue)(struct sk_buff *skb, struct Qdisc *dev);
+       struct sk_buff *        (*dequeue)(struct Qdisc *dev);
+       unsigned                flags;
+#define TCQ_F_BUILTIN  1
+#define TCQ_F_THROTTLED        2
+#define TCQ_F_INGRESS  4
+       int                     padded;
+       struct Qdisc_ops        *ops;
+       u32                     handle;
+       u32                     parent;
+       atomic_t                refcnt;
+       struct sk_buff_head     q;
+       struct net_device       *dev;
+       struct list_head        list;
+
+       struct gnet_stats_basic bstats;
+       struct gnet_stats_queue qstats;
+       struct gnet_stats_rate_est      rate_est;
+       spinlock_t              *stats_lock;
+       struct rcu_head         q_rcu;
+       int                     (*reshape_fail)(struct sk_buff *skb,
+                                       struct Qdisc *q);
+
+       /* This field is deprecated, but it is still used by CBQ
+        * and it will live until better solution will be invented.
+        */
+       struct Qdisc            *__parent;
+};
+
+struct Qdisc_class_ops
+{
+       /* Child qdisc manipulation */
+       int                     (*graft)(struct Qdisc *, unsigned long cl,
+                                       struct Qdisc *, struct Qdisc **);
+       struct Qdisc *          (*leaf)(struct Qdisc *, unsigned long cl);
+
+       /* Class manipulation routines */
+       unsigned long           (*get)(struct Qdisc *, u32 classid);
+       void                    (*put)(struct Qdisc *, unsigned long);
+       int                     (*change)(struct Qdisc *, u32, u32,
+                                       struct rtattr **, unsigned long *);
+       int                     (*delete)(struct Qdisc *, unsigned long);
+       void                    (*walk)(struct Qdisc *, struct qdisc_walker * arg);
+
+       /* Filter manipulation */
+       struct tcf_proto **     (*tcf_chain)(struct Qdisc *, unsigned long);
+       unsigned long           (*bind_tcf)(struct Qdisc *, unsigned long,
+                                       u32 classid);
+       void                    (*unbind_tcf)(struct Qdisc *, unsigned long);
+
+       /* rtnetlink specific */
+       int                     (*dump)(struct Qdisc *, unsigned long,
+                                       struct sk_buff *skb, struct tcmsg*);
+       int                     (*dump_stats)(struct Qdisc *, unsigned long,
+                                       struct gnet_dump *);
+};
+
+struct Qdisc_ops
+{
+       struct Qdisc_ops        *next;
+       struct Qdisc_class_ops  *cl_ops;
+       char                    id[IFNAMSIZ];
+       int                     priv_size;
+
+       int                     (*enqueue)(struct sk_buff *, struct Qdisc *);
+       struct sk_buff *        (*dequeue)(struct Qdisc *);
+       int                     (*requeue)(struct sk_buff *, struct Qdisc *);
+       unsigned int            (*drop)(struct Qdisc *);
+
+       int                     (*init)(struct Qdisc *, struct rtattr *arg);
+       void                    (*reset)(struct Qdisc *);
+       void                    (*destroy)(struct Qdisc *);
+       int                     (*change)(struct Qdisc *, struct rtattr *arg);
+
+       int                     (*dump)(struct Qdisc *, struct sk_buff *);
+       int                     (*dump_stats)(struct Qdisc *, struct gnet_dump *);
+
+       struct module           *owner;
+};
+
+
+struct tcf_result
+{
+       unsigned long   class;
+       u32             classid;
+};
+
+struct tcf_proto_ops
+{
+       struct tcf_proto_ops    *next;
+       char                    kind[IFNAMSIZ];
+
+       int                     (*classify)(struct sk_buff*, struct tcf_proto*,
+                                       struct tcf_result *);
+       int                     (*init)(struct tcf_proto*);
+       void                    (*destroy)(struct tcf_proto*);
+
+       unsigned long           (*get)(struct tcf_proto*, u32 handle);
+       void                    (*put)(struct tcf_proto*, unsigned long);
+       int                     (*change)(struct tcf_proto*, unsigned long,
+                                       u32 handle, struct rtattr **,
+                                       unsigned long *);
+       int                     (*delete)(struct tcf_proto*, unsigned long);
+       void                    (*walk)(struct tcf_proto*, struct tcf_walker *arg);
+
+       /* rtnetlink specific */
+       int                     (*dump)(struct tcf_proto*, unsigned long,
+                                       struct sk_buff *skb, struct tcmsg*);
+
+       struct module           *owner;
+};
+
+struct tcf_proto
+{
+       /* Fast access part */
+       struct tcf_proto        *next;
+       void                    *root;
+       int                     (*classify)(struct sk_buff*, struct tcf_proto*,
+                                       struct tcf_result *);
+       u32                     protocol;
+
+       /* All the rest */
+       u32                     prio;
+       u32                     classid;
+       struct Qdisc            *q;
+       void                    *data;
+       struct tcf_proto_ops    *ops;
+};
+
+
+extern void qdisc_lock_tree(struct net_device *dev);
+extern void qdisc_unlock_tree(struct net_device *dev);
+
+#define sch_tree_lock(q)       qdisc_lock_tree((q)->dev)
+#define sch_tree_unlock(q)     qdisc_unlock_tree((q)->dev)
+#define tcf_tree_lock(tp)      qdisc_lock_tree((tp)->q->dev)
+#define tcf_tree_unlock(tp)    qdisc_unlock_tree((tp)->q->dev)
+
+static inline void
+tcf_destroy(struct tcf_proto *tp)
+{
+       tp->ops->destroy(tp);
+       module_put(tp->ops->owner);
+       kfree(tp);
+}
+
+#endif
diff --git a/include/net/tc_act/tc_ipt.h b/include/net/tc_act/tc_ipt.h
new file mode 100644 (file)
index 0000000..02ecceb
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __NET_TC_IPT_H
+#define __NET_TC_IPT_H
+
+#include <net/act_api.h>
+
+struct ipt_entry_target;
+
+struct tcf_ipt
+{
+       tca_gen(ipt);
+       u32 hook;
+       char *tname;
+       struct ipt_entry_target *t;
+};
+
+#endif
diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h
new file mode 100644 (file)
index 0000000..b5c32f6
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __NET_TC_MIR_H
+#define __NET_TC_MIR_H
+
+#include <net/act_api.h>
+
+struct tcf_mirred
+{
+       tca_gen(mirred);
+       int eaction;
+       int ifindex;
+       int ok_push;
+       struct net_device *dev;
+};
+
+#endif
diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h
new file mode 100644 (file)
index 0000000..29ddb66
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __NET_TC_PED_H
+#define __NET_TC_PED_H
+
+#include <net/act_api.h>
+
+struct tcf_pedit
+{
+       tca_gen(pedit);
+       unsigned char           nkeys;
+       unsigned char           flags;
+       struct tc_pedit_key     keys[0];
+};
+
+#endif
diff --git a/include/net/x25device.h b/include/net/x25device.h
new file mode 100644 (file)
index 0000000..cf36a20
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _X25DEVICE_H
+#define _X25DEVICE_H
+
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/skbuff.h>
+
+static inline unsigned short x25_type_trans(struct sk_buff *skb,
+                                           struct net_device *dev)
+{
+       skb->mac.raw = skb->data;
+       skb->input_dev = skb->dev = dev;
+       skb->pkt_type = PACKET_HOST;
+       
+       return htons(ETH_P_X25);
+}
+#endif
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
new file mode 100644 (file)
index 0000000..1b26a6c
--- /dev/null
@@ -0,0 +1,178 @@
+/* 
+ * iSCSI transport class definitions
+ *
+ * Copyright (C) IBM Corporation, 2004
+ * Copyright (C) Mike Christie, 2004
+ *
+ * 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 SCSI_TRANSPORT_ISCSI_H
+#define SCSI_TRANSPORT_ISCSI_H
+
+#include <linux/config.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+
+struct scsi_transport_template;
+
+struct iscsi_class_session {
+       uint8_t isid[6];
+       uint16_t tsih;
+       int header_digest;              /* 1 CRC32, 0 None */
+       int data_digest;                /* 1 CRC32, 0 None */
+       uint16_t tpgt;
+       union {
+               struct in6_addr sin6_addr;
+               struct in_addr sin_addr;
+       } u;
+       sa_family_t addr_type;          /* must be AF_INET or AF_INET6 */
+       uint16_t port;                  /* must be in network byte order */
+       int initial_r2t;                /* 1 Yes, 0 No */
+       int immediate_data;             /* 1 Yes, 0 No */
+       uint32_t max_recv_data_segment_len;
+       uint32_t max_burst_len;
+       uint32_t first_burst_len;
+       uint16_t def_time2wait;
+       uint16_t def_time2retain;
+       uint16_t max_outstanding_r2t;
+       int data_pdu_in_order;          /* 1 Yes, 0 No */
+       int data_sequence_in_order;     /* 1 Yes, 0 No */
+       int erl;
+};
+
+/*
+ * accessor macros
+ */
+#define iscsi_isid(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->isid)
+#define iscsi_tsih(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->tsih)
+#define iscsi_header_digest(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->header_digest)
+#define iscsi_data_digest(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->data_digest)
+#define iscsi_port(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->port)
+#define iscsi_addr_type(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->addr_type)
+#define iscsi_sin_addr(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->u.sin_addr)
+#define iscsi_sin6_addr(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->u.sin6_addr)
+#define iscsi_tpgt(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->tpgt)
+#define iscsi_initial_r2t(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->initial_r2t)
+#define iscsi_immediate_data(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->immediate_data)
+#define iscsi_max_recv_data_segment_len(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->max_recv_data_segment_len)
+#define iscsi_max_burst_len(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->max_burst_len)
+#define iscsi_first_burst_len(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->first_burst_len)
+#define iscsi_def_time2wait(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->def_time2wait)
+#define iscsi_def_time2retain(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->def_time2retain)
+#define iscsi_max_outstanding_r2t(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->max_outstanding_r2t)
+#define iscsi_data_pdu_in_order(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->data_pdu_in_order)
+#define iscsi_data_sequence_in_order(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->data_sequence_in_order)
+#define iscsi_erl(x) \
+       (((struct iscsi_class_session *)&(x)->starget_data)->erl)
+
+/*
+ * The functions by which the transport class and the driver communicate
+ */
+struct iscsi_function_template {
+       /*
+        * target attrs
+        */
+       void (*get_isid)(struct scsi_target *);
+       void (*get_tsih)(struct scsi_target *);
+       void (*get_header_digest)(struct scsi_target *);
+       void (*get_data_digest)(struct scsi_target *);
+       void (*get_port)(struct scsi_target *);
+       void (*get_tpgt)(struct scsi_target *);
+       /*
+        * In get_ip_address the lld must set the address and
+        * the address type
+        */
+       void (*get_ip_address)(struct scsi_target *);
+       /*
+        * The lld should snprintf the name or alias to the buffer
+        */
+       ssize_t (*get_target_name)(struct scsi_target *, char *, ssize_t);
+       ssize_t (*get_target_alias)(struct scsi_target *, char *, ssize_t);
+       void (*get_initial_r2t)(struct scsi_target *);
+       void (*get_immediate_data)(struct scsi_target *);
+       void (*get_max_recv_data_segment_len)(struct scsi_target *);
+       void (*get_max_burst_len)(struct scsi_target *);
+       void (*get_first_burst_len)(struct scsi_target *);
+       void (*get_def_time2wait)(struct scsi_target *);
+       void (*get_def_time2retain)(struct scsi_target *);
+       void (*get_max_outstanding_r2t)(struct scsi_target *);
+       void (*get_data_pdu_in_order)(struct scsi_target *);
+       void (*get_data_sequence_in_order)(struct scsi_target *);
+       void (*get_erl)(struct scsi_target *);
+
+       /*
+        * host atts
+        */
+
+       /*
+        * The lld should snprintf the name or alias to the buffer
+        */
+       ssize_t (*get_initiator_alias)(struct Scsi_Host *, char *, ssize_t);
+       ssize_t (*get_initiator_name)(struct Scsi_Host *, char *, ssize_t);
+       /*
+        * The driver sets these to tell the transport class it
+        * wants the attributes displayed in sysfs.  If the show_ flag
+        * is not set, the attribute will be private to the transport
+        * class. We could probably just test if a get_ fn was set
+        * since we only use the values for sysfs but this is how
+        * fc does it too.
+        */
+       unsigned long show_isid:1;
+       unsigned long show_tsih:1;
+       unsigned long show_header_digest:1;
+       unsigned long show_data_digest:1;
+       unsigned long show_port:1;
+       unsigned long show_tpgt:1;
+       unsigned long show_ip_address:1;
+       unsigned long show_target_name:1;
+       unsigned long show_target_alias:1;
+       unsigned long show_initial_r2t:1;
+       unsigned long show_immediate_data:1;
+       unsigned long show_max_recv_data_segment_len:1;
+       unsigned long show_max_burst_len:1;
+       unsigned long show_first_burst_len:1;
+       unsigned long show_def_time2wait:1;
+       unsigned long show_def_time2retain:1;
+       unsigned long show_max_outstanding_r2t:1;
+       unsigned long show_data_pdu_in_order:1;
+       unsigned long show_data_sequence_in_order:1;
+       unsigned long show_erl:1;
+       unsigned long show_initiator_name:1;
+       unsigned long show_initiator_alias:1;
+};
+
+struct scsi_transport_template *iscsi_attach_transport(struct iscsi_function_template *);
+void iscsi_release_transport(struct scsi_transport_template *);
+
+#endif
diff --git a/include/video/w100fb.h b/include/video/w100fb.h
new file mode 100644 (file)
index 0000000..bd548c2
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ *  Support for the w100 frame buffer.
+ *
+ *  Copyright (c) 2004 Richard Purdie
+ *
+ *  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 structure describes the machine which we are running on.
+ * It is set by machine specific code and used in the probe routine
+ * of drivers/video/w100fb.c
+ */
+
+struct w100fb_mach_info {
+       void (*w100fb_ssp_send)(u8 adrs, u8 data);
+       int comadj;
+       int phadadj;
+};
diff --git a/init/calibrate.c b/init/calibrate.c
new file mode 100644 (file)
index 0000000..c698e04
--- /dev/null
@@ -0,0 +1,79 @@
+/* calibrate.c: default delay calibration
+ *
+ * Excised from init/main.c
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+static unsigned long preset_lpj;
+static int __init lpj_setup(char *str)
+{
+       preset_lpj = simple_strtoul(str,NULL,0);
+       return 1;
+}
+
+__setup("lpj=", lpj_setup);
+
+/*
+ * This is the number of bits of precision for the loops_per_jiffy.  Each
+ * bit takes on average 1.5/HZ seconds.  This (like the original) is a little
+ * better than 1%
+ */
+#define LPS_PREC 8
+
+void __devinit calibrate_delay(void)
+{
+       unsigned long ticks, loopbit;
+       int lps_precision = LPS_PREC;
+
+       if (preset_lpj) {
+               loops_per_jiffy = preset_lpj;
+               printk("Calibrating delay loop (skipped)... "
+                       "%lu.%02lu BogoMIPS preset\n",
+                       loops_per_jiffy/(500000/HZ),
+                       (loops_per_jiffy/(5000/HZ)) % 100);
+       } else {
+               loops_per_jiffy = (1<<12);
+
+               printk(KERN_DEBUG "Calibrating delay loop... ");
+               while ((loops_per_jiffy <<= 1) != 0) {
+                       /* wait for "start of" clock tick */
+                       ticks = jiffies;
+                       while (ticks == jiffies)
+                               /* nothing */;
+                       /* Go .. */
+                       ticks = jiffies;
+                       __delay(loops_per_jiffy);
+                       ticks = jiffies - ticks;
+                       if (ticks)
+                               break;
+               }
+
+               /*
+                * Do a binary approximation to get loops_per_jiffy set to
+                * equal one clock (up to lps_precision bits)
+                */
+               loops_per_jiffy >>= 1;
+               loopbit = loops_per_jiffy;
+               while (lps_precision-- && (loopbit >>= 1)) {
+                       loops_per_jiffy |= loopbit;
+                       ticks = jiffies;
+                       while (ticks == jiffies)
+                               /* nothing */;
+                       ticks = jiffies;
+                       __delay(loops_per_jiffy);
+                       if (jiffies != ticks)   /* longer than 1 tick */
+                               loops_per_jiffy &= ~loopbit;
+               }
+
+               /* Round the value and print it */
+               printk("%lu.%02lu BogoMIPS (lpj=%lu)\n",
+                       loops_per_jiffy/(500000/HZ),
+                       (loops_per_jiffy/(5000/HZ)) % 100,
+                       loops_per_jiffy);
+       }
+
+}
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
new file mode 100644 (file)
index 0000000..4937873
--- /dev/null
@@ -0,0 +1,5 @@
+
+obj-y := handle.o manage.o spurious.o
+obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
+obj-$(CONFIG_PROC_FS) += proc.o
+
diff --git a/kernel/irq/autoprobe.c b/kernel/irq/autoprobe.c
new file mode 100644 (file)
index 0000000..1681872
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * linux/kernel/irq/autoprobe.c
+ *
+ * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
+ *
+ * This file contains the interrupt probing code and driver APIs.
+ */
+
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+/*
+ * Autodetection depends on the fact that any interrupt that
+ * comes in on to an unassigned handler will get stuck with
+ * "IRQ_WAITING" cleared and the interrupt disabled.
+ */
+static DECLARE_MUTEX(probe_sem);
+
+/**
+ *     probe_irq_on    - begin an interrupt autodetect
+ *
+ *     Commence probing for an interrupt. The interrupts are scanned
+ *     and a mask of potential interrupt lines is returned.
+ *
+ */
+unsigned long probe_irq_on(void)
+{
+       unsigned long val, delay;
+       irq_desc_t *desc;
+       unsigned int i;
+
+       down(&probe_sem);
+       /*
+        * something may have generated an irq long ago and we want to
+        * flush such a longstanding irq before considering it as spurious.
+        */
+       for (i = NR_IRQS-1; i > 0; i--) {
+               desc = irq_desc + i;
+
+               spin_lock_irq(&desc->lock);
+               if (!irq_desc[i].action)
+                       irq_desc[i].handler->startup(i);
+               spin_unlock_irq(&desc->lock);
+       }
+
+       /* Wait for longstanding interrupts to trigger. */
+       for (delay = jiffies + HZ/50; time_after(delay, jiffies); )
+               /* about 20ms delay */ barrier();
+
+       /*
+        * enable any unassigned irqs
+        * (we must startup again here because if a longstanding irq
+        * happened in the previous stage, it may have masked itself)
+        */
+       for (i = NR_IRQS-1; i > 0; i--) {
+               desc = irq_desc + i;
+
+               spin_lock_irq(&desc->lock);
+               if (!desc->action) {
+                       desc->status |= IRQ_AUTODETECT | IRQ_WAITING;
+                       if (desc->handler->startup(i))
+                               desc->status |= IRQ_PENDING;
+               }
+               spin_unlock_irq(&desc->lock);
+       }
+
+       /*
+        * Wait for spurious interrupts to trigger
+        */
+       for (delay = jiffies + HZ/10; time_after(delay, jiffies); )
+               /* about 100ms delay */ barrier();
+
+       /*
+        * Now filter out any obviously spurious interrupts
+        */
+       val = 0;
+       for (i = 0; i < NR_IRQS; i++) {
+               irq_desc_t *desc = irq_desc + i;
+               unsigned int status;
+
+               spin_lock_irq(&desc->lock);
+               status = desc->status;
+
+               if (status & IRQ_AUTODETECT) {
+                       /* It triggered already - consider it spurious. */
+                       if (!(status & IRQ_WAITING)) {
+                               desc->status = status & ~IRQ_AUTODETECT;
+                               desc->handler->shutdown(i);
+                       } else
+                               if (i < 32)
+                                       val |= 1 << i;
+               }
+               spin_unlock_irq(&desc->lock);
+       }
+
+       return val;
+}
+
+EXPORT_SYMBOL(probe_irq_on);
+
+/**
+ *     probe_irq_mask - scan a bitmap of interrupt lines
+ *     @val:   mask of interrupts to consider
+ *
+ *     Scan the interrupt lines and return a bitmap of active
+ *     autodetect interrupts. The interrupt probe logic state
+ *     is then returned to its previous value.
+ *
+ *     Note: we need to scan all the irq's even though we will
+ *     only return autodetect irq numbers - just so that we reset
+ *     them all to a known state.
+ */
+unsigned int probe_irq_mask(unsigned long val)
+{
+       unsigned int mask;
+       int i;
+
+       mask = 0;
+       for (i = 0; i < NR_IRQS; i++) {
+               irq_desc_t *desc = irq_desc + i;
+               unsigned int status;
+
+               spin_lock_irq(&desc->lock);
+               status = desc->status;
+
+               if (status & IRQ_AUTODETECT) {
+                       if (i < 16 && !(status & IRQ_WAITING))
+                               mask |= 1 << i;
+
+                       desc->status = status & ~IRQ_AUTODETECT;
+                       desc->handler->shutdown(i);
+               }
+               spin_unlock_irq(&desc->lock);
+       }
+       up(&probe_sem);
+
+       return mask & val;
+}
+
+/**
+ *     probe_irq_off   - end an interrupt autodetect
+ *     @val: mask of potential interrupts (unused)
+ *
+ *     Scans the unused interrupt lines and returns the line which
+ *     appears to have triggered the interrupt. If no interrupt was
+ *     found then zero is returned. If more than one interrupt is
+ *     found then minus the first candidate is returned to indicate
+ *     their is doubt.
+ *
+ *     The interrupt probe logic state is returned to its previous
+ *     value.
+ *
+ *     BUGS: When used in a module (which arguably shouldn't happen)
+ *     nothing prevents two IRQ probe callers from overlapping. The
+ *     results of this are non-optimal.
+ */
+int probe_irq_off(unsigned long val)
+{
+       int i, irq_found = 0, nr_irqs = 0;
+
+       for (i = 0; i < NR_IRQS; i++) {
+               irq_desc_t *desc = irq_desc + i;
+               unsigned int status;
+
+               spin_lock_irq(&desc->lock);
+               status = desc->status;
+
+               if (status & IRQ_AUTODETECT) {
+                       if (!(status & IRQ_WAITING)) {
+                               if (!nr_irqs)
+                                       irq_found = i;
+                               nr_irqs++;
+                       }
+                       desc->status = status & ~IRQ_AUTODETECT;
+                       desc->handler->shutdown(i);
+               }
+               spin_unlock_irq(&desc->lock);
+       }
+       up(&probe_sem);
+
+       if (nr_irqs > 1)
+               irq_found = -irq_found;
+       return irq_found;
+}
+
+EXPORT_SYMBOL(probe_irq_off);
+
diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c
new file mode 100644 (file)
index 0000000..ebc2582
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * linux/kernel/irq/handle.c
+ *
+ * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
+ *
+ * This file contains the core interrupt handling code.
+ */
+
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+
+#include "internals.h"
+
+/*
+ * Linux has a controller-independent interrupt architecture.
+ * Every controller has a 'controller-template', that is used
+ * by the main code to do the right thing. Each driver-visible
+ * interrupt source is transparently wired to the apropriate
+ * controller. Thus drivers need not be aware of the
+ * interrupt-controller.
+ *
+ * The code is designed to be easily extended with new/different
+ * interrupt controllers, without having to do assembly magic or
+ * having to touch the generic code.
+ *
+ * Controller mappings for all interrupt sources:
+ */
+irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = {
+       [0 ... NR_IRQS-1] = {
+               .handler = &no_irq_type,
+               .lock = SPIN_LOCK_UNLOCKED
+       }
+};
+
+/*
+ * Generic 'no controller' code
+ */
+static void end_none(unsigned int irq) { }
+static void enable_none(unsigned int irq) { }
+static void disable_none(unsigned int irq) { }
+static void shutdown_none(unsigned int irq) { }
+static unsigned int startup_none(unsigned int irq) { return 0; }
+
+static void ack_none(unsigned int irq)
+{
+       /*
+        * 'what should we do if we get a hw irq event on an illegal vector'.
+        * each architecture has to answer this themself.
+        */
+       ack_bad_irq(irq);
+}
+
+struct hw_interrupt_type no_irq_type = {
+       .typename =     "none",
+       .startup =      startup_none,
+       .shutdown =     shutdown_none,
+       .enable =       enable_none,
+       .disable =      disable_none,
+       .ack =          ack_none,
+       .end =          end_none,
+       .set_affinity = NULL
+};
+
+/*
+ * Special, empty irq handler:
+ */
+irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs)
+{
+       return IRQ_NONE;
+}
+
+/*
+ * Exit an interrupt context. Process softirqs if needed and possible:
+ */
+void irq_exit(void)
+{
+       preempt_count() -= IRQ_EXIT_OFFSET;
+       if (!in_interrupt() && local_softirq_pending())
+               do_softirq();
+       preempt_enable_no_resched();
+}
+
+/*
+ * Have got an event to handle:
+ */
+fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,
+                               struct irqaction *action)
+{
+       int ret, retval = 0, status = 0;
+
+       if (!(action->flags & SA_INTERRUPT))
+               local_irq_enable();
+
+       do {
+               ret = action->handler(irq, action->dev_id, regs);
+               if (ret == IRQ_HANDLED)
+                       status |= action->flags;
+               retval |= ret;
+               action = action->next;
+       } while (action);
+
+       if (status & SA_SAMPLE_RANDOM)
+               add_interrupt_randomness(irq);
+       local_irq_disable();
+
+       return retval;
+}
+
+/*
+ * do_IRQ handles all normal device IRQ's (the special
+ * SMP cross-CPU interrupts have their own specific
+ * handlers).
+ */
+fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
+{
+       irq_desc_t *desc = irq_desc + irq;
+       struct irqaction * action;
+       unsigned int status;
+
+       kstat_this_cpu.irqs[irq]++;
+       if (desc->status & IRQ_PER_CPU) {
+               irqreturn_t action_ret;
+
+               /*
+                * No locking required for CPU-local interrupts:
+                */
+               desc->handler->ack(irq);
+               action_ret = handle_IRQ_event(irq, regs, desc->action);
+               if (!noirqdebug)
+                       note_interrupt(irq, desc, action_ret);
+               desc->handler->end(irq);
+               return 1;
+       }
+
+       spin_lock(&desc->lock);
+       desc->handler->ack(irq);
+       /*
+        * REPLAY is when Linux resends an IRQ that was dropped earlier
+        * WAITING is used by probe to mark irqs that are being tested
+        */
+       status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
+       status |= IRQ_PENDING; /* we _want_ to handle it */
+
+       /*
+        * If the IRQ is disabled for whatever reason, we cannot
+        * use the action we have.
+        */
+       action = NULL;
+       if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
+               action = desc->action;
+               status &= ~IRQ_PENDING; /* we commit to handling */
+               status |= IRQ_INPROGRESS; /* we are handling it */
+       }
+       desc->status = status;
+
+       /*
+        * If there is no IRQ handler or it was disabled, exit early.
+        * Since we set PENDING, if another processor is handling
+        * a different instance of this same irq, the other processor
+        * will take care of it.
+        */
+       if (unlikely(!action))
+               goto out;
+
+       /*
+        * Edge triggered interrupts need to remember
+        * pending events.
+        * This applies to any hw interrupts that allow a second
+        * instance of the same irq to arrive while we are in do_IRQ
+        * or in the handler. But the code here only handles the _second_
+        * instance of the irq, not the third or fourth. So it is mostly
+        * useful for irq hardware that does not mask cleanly in an
+        * SMP environment.
+        */
+       for (;;) {
+               irqreturn_t action_ret;
+
+               spin_unlock(&desc->lock);
+
+               action_ret = handle_IRQ_event(irq, regs, action);
+
+               spin_lock(&desc->lock);
+               if (!noirqdebug)
+                       note_interrupt(irq, desc, action_ret);
+               if (likely(!(desc->status & IRQ_PENDING)))
+                       break;
+               desc->status &= ~IRQ_PENDING;
+       }
+       desc->status &= ~IRQ_INPROGRESS;
+
+out:
+       /*
+        * The ->end() handler has to deal with interrupts which got
+        * disabled while the handler was running.
+        */
+       desc->handler->end(irq);
+       spin_unlock(&desc->lock);
+
+       return 1;
+}
+
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
new file mode 100644 (file)
index 0000000..46feba6
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * IRQ subsystem internal functions and variables:
+ */
+
+extern int noirqdebug;
+
+#ifdef CONFIG_PROC_FS
+extern void register_irq_proc(unsigned int irq);
+extern void register_handler_proc(unsigned int irq, struct irqaction *action);
+extern void unregister_handler_proc(unsigned int irq, struct irqaction *action);
+#else
+static inline void register_irq_proc(unsigned int irq) { }
+static inline void register_handler_proc(unsigned int irq,
+                                        struct irqaction *action) { }
+static inline void unregister_handler_proc(unsigned int irq,
+                                          struct irqaction *action) { }
+#endif
+
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
new file mode 100644 (file)
index 0000000..a164fe2
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * linux/kernel/irq/manage.c
+ *
+ * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
+ *
+ * This file contains driver APIs to the irq subsystem.
+ */
+
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/interrupt.h>
+
+#include "internals.h"
+
+#ifdef CONFIG_SMP
+
+/**
+ *     synchronize_irq - wait for pending IRQ handlers (on other CPUs)
+ *
+ *     This function waits for any pending IRQ handlers for this interrupt
+ *     to complete before returning. If you use this function while
+ *     holding a resource the IRQ handler may need you will deadlock.
+ *
+ *     This function may be called - with care - from IRQ context.
+ */
+void synchronize_irq(unsigned int irq)
+{
+       struct irq_desc *desc = irq_desc + irq;
+
+       while (desc->status & IRQ_INPROGRESS)
+               cpu_relax();
+}
+
+EXPORT_SYMBOL(synchronize_irq);
+
+#endif
+
+/**
+ *     disable_irq_nosync - disable an irq without waiting
+ *     @irq: Interrupt to disable
+ *
+ *     Disable the selected interrupt line.  Disables and Enables are
+ *     nested.
+ *     Unlike disable_irq(), this function does not ensure existing
+ *     instances of the IRQ handler have completed before returning.
+ *
+ *     This function may be called from IRQ context.
+ */
+void disable_irq_nosync(unsigned int irq)
+{
+       irq_desc_t *desc = irq_desc + irq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&desc->lock, flags);
+       if (!desc->depth++) {
+               desc->status |= IRQ_DISABLED;
+               desc->handler->disable(irq);
+       }
+       spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+EXPORT_SYMBOL(disable_irq_nosync);
+
+/**
+ *     disable_irq - disable an irq and wait for completion
+ *     @irq: Interrupt to disable
+ *
+ *     Disable the selected interrupt line.  Enables and Disables are
+ *     nested.
+ *     This function waits for any pending IRQ handlers for this interrupt
+ *     to complete before returning. If you use this function while
+ *     holding a resource the IRQ handler may need you will deadlock.
+ *
+ *     This function may be called - with care - from IRQ context.
+ */
+void disable_irq(unsigned int irq)
+{
+       irq_desc_t *desc = irq_desc + irq;
+
+       disable_irq_nosync(irq);
+       if (desc->action)
+               synchronize_irq(irq);
+}
+
+EXPORT_SYMBOL(disable_irq);
+
+/**
+ *     enable_irq - enable handling of an irq
+ *     @irq: Interrupt to enable
+ *
+ *     Undoes the effect of one call to disable_irq().  If this
+ *     matches the last disable, processing of interrupts on this
+ *     IRQ line is re-enabled.
+ *
+ *     This function may be called from IRQ context.
+ */
+void enable_irq(unsigned int irq)
+{
+       irq_desc_t *desc = irq_desc + irq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&desc->lock, flags);
+       switch (desc->depth) {
+       case 0:
+               WARN_ON(1);
+               break;
+       case 1: {
+               unsigned int status = desc->status & ~IRQ_DISABLED;
+
+               desc->status = status;
+               if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
+                       desc->status = status | IRQ_REPLAY;
+                       hw_resend_irq(desc->handler,irq);
+               }
+               desc->handler->enable(irq);
+               /* fall-through */
+       }
+       default:
+               desc->depth--;
+       }
+       spin_unlock_irqrestore(&desc->lock, flags);
+}
+
+EXPORT_SYMBOL(enable_irq);
+
+/*
+ * Internal function that tells the architecture code whether a
+ * particular irq has been exclusively allocated or is available
+ * for driver use.
+ */
+int can_request_irq(unsigned int irq, unsigned long irqflags)
+{
+       struct irqaction *action;
+
+       if (irq >= NR_IRQS)
+               return 0;
+
+       action = irq_desc[irq].action;
+       if (action)
+               if (irqflags & action->flags & SA_SHIRQ)
+                       action = NULL;
+
+       return !action;
+}
+
+/*
+ * Internal function to register an irqaction - typically used to
+ * allocate special interrupts that are part of the architecture.
+ */
+int setup_irq(unsigned int irq, struct irqaction * new)
+{
+       struct irq_desc *desc = irq_desc + irq;
+       struct irqaction *old, **p;
+       unsigned long flags;
+       int shared = 0;
+
+       if (desc->handler == &no_irq_type)
+               return -ENOSYS;
+       /*
+        * Some drivers like serial.c use request_irq() heavily,
+        * so we have to be careful not to interfere with a
+        * running system.
+        */
+       if (new->flags & SA_SAMPLE_RANDOM) {
+               /*
+                * This function might sleep, we want to call it first,
+                * outside of the atomic block.
+                * Yes, this might clear the entropy pool if the wrong
+                * driver is attempted to be loaded, without actually
+                * installing a new handler, but is this really a problem,
+                * only the sysadmin is able to do this.
+                */
+               rand_initialize_irq(irq);
+       }
+
+       /*
+        * The following block of code has to be executed atomically
+        */
+       spin_lock_irqsave(&desc->lock,flags);
+       p = &desc->action;
+       if ((old = *p) != NULL) {
+               /* Can't share interrupts unless both agree to */
+               if (!(old->flags & new->flags & SA_SHIRQ)) {
+                       spin_unlock_irqrestore(&desc->lock,flags);
+                       return -EBUSY;
+               }
+
+               /* add new interrupt at end of irq queue */
+               do {
+                       p = &old->next;
+                       old = *p;
+               } while (old);
+               shared = 1;
+       }
+
+       *p = new;
+
+       if (!shared) {
+               desc->depth = 0;
+               desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT |
+                                 IRQ_WAITING | IRQ_INPROGRESS);
+               if (desc->handler->startup)
+                       desc->handler->startup(irq);
+               else
+                       desc->handler->enable(irq);
+       }
+       spin_unlock_irqrestore(&desc->lock,flags);
+
+       new->irq = irq;
+       register_irq_proc(irq);
+       new->dir = NULL;
+       register_handler_proc(irq, new);
+
+       return 0;
+}
+
+/**
+ *     free_irq - free an interrupt
+ *     @irq: Interrupt line to free
+ *     @dev_id: Device identity to free
+ *
+ *     Remove an interrupt handler. The handler is removed and if the
+ *     interrupt line is no longer in use by any driver it is disabled.
+ *     On a shared IRQ the caller must ensure the interrupt is disabled
+ *     on the card it drives before calling this function. The function
+ *     does not return until any executing interrupts for this IRQ
+ *     have completed.
+ *
+ *     This function must not be called from interrupt context.
+ */
+void free_irq(unsigned int irq, void *dev_id)
+{
+       struct irq_desc *desc;
+       struct irqaction **p;
+       unsigned long flags;
+
+       if (irq >= NR_IRQS)
+               return;
+
+       desc = irq_desc + irq;
+       spin_lock_irqsave(&desc->lock,flags);
+       p = &desc->action;
+       for (;;) {
+               struct irqaction * action = *p;
+
+               if (action) {
+                       struct irqaction **pp = p;
+
+                       p = &action->next;
+                       if (action->dev_id != dev_id)
+                               continue;
+
+                       /* Found it - now remove it from the list of entries */
+                       *pp = action->next;
+                       if (!desc->action) {
+                               desc->status |= IRQ_DISABLED;
+                               if (desc->handler->shutdown)
+                                       desc->handler->shutdown(irq);
+                               else
+                                       desc->handler->disable(irq);
+                       }
+                       spin_unlock_irqrestore(&desc->lock,flags);
+                       unregister_handler_proc(irq, action);
+
+                       /* Make sure it's not being used on another CPU */
+                       synchronize_irq(irq);
+                       kfree(action);
+                       return;
+               }
+               printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
+               spin_unlock_irqrestore(&desc->lock,flags);
+               return;
+       }
+}
+
+EXPORT_SYMBOL(free_irq);
+
+/**
+ *     request_irq - allocate an interrupt line
+ *     @irq: Interrupt line to allocate
+ *     @handler: Function to be called when the IRQ occurs
+ *     @irqflags: Interrupt type flags
+ *     @devname: An ascii name for the claiming device
+ *     @dev_id: A cookie passed back to the handler function
+ *
+ *     This call allocates interrupt resources and enables the
+ *     interrupt line and IRQ handling. From the point this
+ *     call is made your handler function may be invoked. Since
+ *     your handler function must clear any interrupt the board
+ *     raises, you must take care both to initialise your hardware
+ *     and to set up the interrupt handler in the right order.
+ *
+ *     Dev_id must be globally unique. Normally the address of the
+ *     device data structure is used as the cookie. Since the handler
+ *     receives this value it makes sense to use it.
+ *
+ *     If your interrupt is shared you must pass a non NULL dev_id
+ *     as this is required when freeing the interrupt.
+ *
+ *     Flags:
+ *
+ *     SA_SHIRQ                Interrupt is shared
+ *     SA_INTERRUPT            Disable local interrupts while processing
+ *     SA_SAMPLE_RANDOM        The interrupt can be used for entropy
+ *
+ */
+int request_irq(unsigned int irq,
+               irqreturn_t (*handler)(int, void *, struct pt_regs *),
+               unsigned long irqflags, const char * devname, void *dev_id)
+{
+       struct irqaction * action;
+       int retval;
+
+       /*
+        * Sanity-check: shared interrupts must pass in a real dev-ID,
+        * otherwise we'll have trouble later trying to figure out
+        * which interrupt is which (messes up the interrupt freeing
+        * logic etc).
+        */
+       if ((irqflags & SA_SHIRQ) && !dev_id)
+               return -EINVAL;
+       if (irq >= NR_IRQS)
+               return -EINVAL;
+       if (!handler)
+               return -EINVAL;
+
+       action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
+       if (!action)
+               return -ENOMEM;
+
+       action->handler = handler;
+       action->flags = irqflags;
+       cpus_clear(action->mask);
+       action->name = devname;
+       action->next = NULL;
+       action->dev_id = dev_id;
+
+       retval = setup_irq(irq, action);
+       if (retval)
+               kfree(action);
+
+       return retval;
+}
+
+EXPORT_SYMBOL(request_irq);
+
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
new file mode 100644 (file)
index 0000000..f555f87
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * linux/kernel/irq/proc.c
+ *
+ * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
+ *
+ * This file contains the /proc/irq/ handling code.
+ */
+
+#include <linux/irq.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+
+static struct proc_dir_entry *root_irq_dir, *irq_dir[NR_IRQS];
+
+#ifdef CONFIG_SMP
+
+/*
+ * The /proc/irq/<irq>/smp_affinity values:
+ */
+static struct proc_dir_entry *smp_affinity_entry[NR_IRQS];
+
+cpumask_t irq_affinity[NR_IRQS] = { [0 ... NR_IRQS-1] = CPU_MASK_ALL };
+
+static int irq_affinity_read_proc(char *page, char **start, off_t off,
+                                 int count, int *eof, void *data)
+{
+       int len = cpumask_scnprintf(page, count, irq_affinity[(long)data]);
+
+       if (count - len < 2)
+               return -EINVAL;
+       len += sprintf(page + len, "\n");
+       return len;
+}
+
+int no_irq_affinity;
+static int irq_affinity_write_proc(struct file *file, const char __user *buffer,
+                                  unsigned long count, void *data)
+{
+       unsigned int irq = (int)(long)data, full_count = count, err;
+       cpumask_t new_value, tmp;
+
+       if (!irq_desc[irq].handler->set_affinity || no_irq_affinity)
+               return -EIO;
+
+       err = cpumask_parse(buffer, count, new_value);
+       if (err)
+               return err;
+
+       /*
+        * Do not allow disabling IRQs completely - it's a too easy
+        * way to make the system unusable accidentally :-) At least
+        * one online CPU still has to be targeted.
+        */
+       cpus_and(tmp, new_value, cpu_online_map);
+       if (cpus_empty(tmp))
+               return -EINVAL;
+
+       irq_affinity[irq] = new_value;
+       irq_desc[irq].handler->set_affinity(irq, new_value);
+
+       return full_count;
+}
+
+#endif
+
+#define MAX_NAMELEN 128
+
+static int name_unique(unsigned int irq, struct irqaction *new_action)
+{
+       struct irq_desc *desc = irq_desc + irq;
+       struct irqaction *action;
+
+       for (action = desc->action ; action; action = action->next)
+               if ((action != new_action) && action->name &&
+                               !strcmp(new_action->name, action->name))
+                       return 0;
+       return 1;
+}
+
+void register_handler_proc(unsigned int irq, struct irqaction *action)
+{
+       char name [MAX_NAMELEN];
+
+       if (!irq_dir[irq] || action->dir || !action->name ||
+                                       !name_unique(irq, action))
+               return;
+
+       memset(name, 0, MAX_NAMELEN);
+       snprintf(name, MAX_NAMELEN, "%s", action->name);
+
+       /* create /proc/irq/1234/handler/ */
+       action->dir = proc_mkdir(name, irq_dir[irq]);
+}
+
+#undef MAX_NAMELEN
+
+#define MAX_NAMELEN 10
+
+void register_irq_proc(unsigned int irq)
+{
+       char name [MAX_NAMELEN];
+
+       if (!root_irq_dir ||
+               (irq_desc[irq].handler == &no_irq_type) ||
+                       irq_dir[irq])
+               return;
+
+       memset(name, 0, MAX_NAMELEN);
+       sprintf(name, "%d", irq);
+
+       /* create /proc/irq/1234 */
+       irq_dir[irq] = proc_mkdir(name, root_irq_dir);
+
+#ifdef CONFIG_SMP
+       {
+               struct proc_dir_entry *entry;
+
+               /* create /proc/irq/<irq>/smp_affinity */
+               entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]);
+
+               if (entry) {
+                       entry->nlink = 1;
+                       entry->data = (void *)(long)irq;
+                       entry->read_proc = irq_affinity_read_proc;
+                       entry->write_proc = irq_affinity_write_proc;
+               }
+               smp_affinity_entry[irq] = entry;
+       }
+#endif
+}
+
+#undef MAX_NAMELEN
+
+void unregister_handler_proc(unsigned int irq, struct irqaction *action)
+{
+       if (action->dir)
+               remove_proc_entry(action->dir->name, irq_dir[irq]);
+}
+
+void init_irq_proc(void)
+{
+       int i;
+
+       /* create /proc/irq */
+       root_irq_dir = proc_mkdir("irq", NULL);
+       if (!root_irq_dir)
+               return;
+
+       /*
+        * Create entries for all existing IRQs.
+        */
+       for (i = 0; i < NR_IRQS; i++)
+               register_irq_proc(i);
+}
+
diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c
new file mode 100644 (file)
index 0000000..f6297c3
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * linux/kernel/irq/spurious.c
+ *
+ * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
+ *
+ * This file contains spurious interrupt handling.
+ */
+
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/interrupt.h>
+
+/*
+ * If 99,900 of the previous 100,000 interrupts have not been handled
+ * then assume that the IRQ is stuck in some manner. Drop a diagnostic
+ * and try to turn the IRQ off.
+ *
+ * (The other 100-of-100,000 interrupts may have been a correctly
+ *  functioning device sharing an IRQ with the failing one)
+ *
+ * Called under desc->lock
+ */
+
+static void
+__report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret)
+{
+       struct irqaction *action;
+
+       if (action_ret != IRQ_HANDLED && action_ret != IRQ_NONE) {
+               printk(KERN_ERR "irq event %d: bogus return value %x\n",
+                               irq, action_ret);
+       } else {
+               printk(KERN_ERR "irq %d: nobody cared!\n", irq);
+       }
+       dump_stack();
+       printk(KERN_ERR "handlers:\n");
+       action = desc->action;
+       while (action) {
+               printk(KERN_ERR "[<%p>]", action->handler);
+               print_symbol(" (%s)",
+                       (unsigned long)action->handler);
+               printk("\n");
+               action = action->next;
+       }
+}
+
+void report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret)
+{
+       static int count = 100;
+
+       if (count > 0) {
+               count--;
+               __report_bad_irq(irq, desc, action_ret);
+       }
+}
+
+void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret)
+{
+       if (action_ret != IRQ_HANDLED) {
+               desc->irqs_unhandled++;
+               if (action_ret != IRQ_NONE)
+                       report_bad_irq(irq, desc, action_ret);
+       }
+
+       desc->irq_count++;
+       if (desc->irq_count < 100000)
+               return;
+
+       desc->irq_count = 0;
+       if (desc->irqs_unhandled > 99900) {
+               /*
+                * The interrupt is stuck
+                */
+               __report_bad_irq(irq, desc, action_ret);
+               /*
+                * Now kill the IRQ
+                */
+               printk(KERN_EMERG "Disabling IRQ #%d\n", irq);
+               desc->status |= IRQ_DISABLED;
+               desc->handler->disable(irq);
+       }
+       desc->irqs_unhandled = 0;
+}
+
+int noirqdebug;
+
+int __init noirqdebug_setup(char *str)
+{
+       noirqdebug = 1;
+       printk(KERN_INFO "IRQ lockup detection disabled\n");
+       return 1;
+}
+
+__setup("noirqdebug", noirqdebug_setup);
+
diff --git a/kernel/kfifo.c b/kernel/kfifo.c
new file mode 100644 (file)
index 0000000..2fbe06a
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * A simple kernel FIFO implementation.
+ *
+ * Copyright (C) 2004 Stelian Pop <stelian@popies.net>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/kfifo.h>
+
+/*
+ * kfifo_init - allocates a new FIFO using a preallocated buffer
+ * @buffer: the preallocated buffer to be used.
+ * @size: the size of the internal buffer, this have to be a power of 2.
+ * @gfp_mask: get_free_pages mask, passed to kmalloc()
+ * @lock: the lock to be used to protect the fifo buffer
+ *
+ * Do NOT pass the kfifo to kfifo_free() after use ! Simply free the
+ * struct kfifo with kfree().
+ */
+struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
+                        int gfp_mask, spinlock_t *lock)
+{
+       struct kfifo *fifo;
+
+       /* size must be a power of 2 */
+       BUG_ON(size & (size - 1));
+
+       fifo = kmalloc(sizeof(struct kfifo), gfp_mask);
+       if (!fifo)
+               return ERR_PTR(-ENOMEM);
+
+       fifo->buffer = buffer;
+       fifo->size = size;
+       fifo->in = fifo->out = 0;
+       fifo->lock = lock;
+
+       return fifo;
+}
+EXPORT_SYMBOL(kfifo_init);
+
+/*
+ * kfifo_alloc - allocates a new FIFO and its internal buffer
+ * @size: the size of the internal buffer to be allocated.
+ * @gfp_mask: get_free_pages mask, passed to kmalloc()
+ * @lock: the lock to be used to protect the fifo buffer
+ *
+ * The size will be rounded-up to a power of 2.
+ */
+struct kfifo *kfifo_alloc(unsigned int size, int gfp_mask, spinlock_t *lock)
+{
+       unsigned char *buffer;
+       struct kfifo *ret;
+
+       /*
+        * round up to the next power of 2, since our 'let the indices
+        * wrap' tachnique works only in this case.
+        */
+       if (size & (size - 1)) {
+               BUG_ON(size > 0x80000000);
+               size = roundup_pow_of_two(size);
+       }
+
+       buffer = kmalloc(size, gfp_mask);
+       if (!buffer)
+               return ERR_PTR(-ENOMEM);
+
+       ret = kfifo_init(buffer, size, gfp_mask, lock);
+
+       if (IS_ERR(ret))
+               kfree(buffer);
+
+       return ret;
+}
+EXPORT_SYMBOL(kfifo_alloc);
+
+/*
+ * kfifo_free - frees the FIFO
+ * @fifo: the fifo to be freed.
+ */
+void kfifo_free(struct kfifo *fifo)
+{
+       kfree(fifo->buffer);
+       kfree(fifo);
+}
+EXPORT_SYMBOL(kfifo_free);
+
+/*
+ * __kfifo_put - puts some data into the FIFO, no locking version
+ * @fifo: the fifo to be used.
+ * @buffer: the data to be added.
+ * @len: the length of the data to be added.
+ *
+ * This function copies at most 'len' bytes from the 'buffer' into
+ * the FIFO depending on the free space, and returns the number of
+ * bytes copied.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these functions.
+ */
+unsigned int __kfifo_put(struct kfifo *fifo,
+                        unsigned char *buffer, unsigned int len)
+{
+       unsigned int l;
+
+       len = min(len, fifo->size - fifo->in + fifo->out);
+
+       /* first put the data starting from fifo->in to buffer end */
+       l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
+       memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);
+
+       /* then put the rest (if any) at the beginning of the buffer */
+       memcpy(fifo->buffer, buffer + l, len - l);
+
+       fifo->in += len;
+
+       return len;
+}
+EXPORT_SYMBOL(__kfifo_put);
+
+/*
+ * __kfifo_get - gets some data from the FIFO, no locking version
+ * @fifo: the fifo to be used.
+ * @buffer: where the data must be copied.
+ * @len: the size of the destination buffer.
+ *
+ * This function copies at most 'len' bytes from the FIFO into the
+ * 'buffer' and returns the number of copied bytes.
+ *
+ * Note that with only one concurrent reader and one concurrent
+ * writer, you don't need extra locking to use these functions.
+ */
+unsigned int __kfifo_get(struct kfifo *fifo,
+                        unsigned char *buffer, unsigned int len)
+{
+       unsigned int l;
+
+       len = min(len, fifo->in - fifo->out);
+
+       /* first get the data from fifo->out until the end of the buffer */
+       l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
+       memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);
+
+       /* then get the rest (if any) from the beginning of the buffer */
+       memcpy(buffer + l, fifo->buffer, len - l);
+
+       fifo->out += len;
+
+       return len;
+}
+EXPORT_SYMBOL(__kfifo_get);
diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c
new file mode 100644 (file)
index 0000000..31f1a60
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * kernel/ksysfs.c - sysfs attributes in /sys/kernel, which
+ *                  are not related to any other subsystem
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * 
+ * This file is release under the GPLv2
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#define KERNEL_ATTR_RO(_name) \
+static struct subsys_attribute _name##_attr = __ATTR_RO(_name)
+
+#define KERNEL_ATTR_RW(_name) \
+static struct subsys_attribute _name##_attr = \
+       __ATTR(_name, 0644, _name##_show, _name##_store)
+
+#ifdef CONFIG_HOTPLUG
+static ssize_t hotplug_seqnum_show(struct subsystem *subsys, char *page)
+{
+       return sprintf(page, "%llu\n", (unsigned long long)hotplug_seqnum);
+}
+KERNEL_ATTR_RO(hotplug_seqnum);
+#endif
+
+static decl_subsys(kernel, NULL, NULL);
+
+static struct attribute * kernel_attrs[] = {
+#ifdef CONFIG_HOTPLUG
+       &hotplug_seqnum_attr.attr,
+#endif
+       NULL
+};
+
+static struct attribute_group kernel_attr_group = {
+       .attrs = kernel_attrs,
+};
+
+static int __init ksysfs_init(void)
+{
+       int error = subsystem_register(&kernel_subsys);
+       if (!error)
+               error = sysfs_create_group(&kernel_subsys.kset.kobj,
+                                          &kernel_attr_group);
+
+       return error;
+}
+
+core_initcall(ksysfs_init);
diff --git a/kernel/module-verify-sig.c b/kernel/module-verify-sig.c
new file mode 100644 (file)
index 0000000..2a75a10
--- /dev/null
@@ -0,0 +1,442 @@
+/* module-verify-sig.c: module signature checker
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ * - Derived from GregKH's RSA module signer
+ *
+ * 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 <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/elf.h>
+#include <linux/crypto.h>
+#include <linux/crypto/ksign.h>
+#include "module-verify.h"
+
+#undef MODSIGN_DEBUG
+
+#ifdef MODSIGN_DEBUG
+#define _debug(FMT, ...) printk(FMT, ##__VA_ARGS__)
+#else
+#define _debug(FMT, ...) do {} while (0)
+#endif
+
+#ifdef MODSIGN_DEBUG
+#define count_and_csum(C, __p,__n)                     \
+do {                                                   \
+       int __loop;                                     \
+       for (__loop = 0; __loop < __n; __loop++) {      \
+               (C)->csum += __p[__loop];               \
+               (C)->xcsum += __p[__loop];              \
+       }                                               \
+       (C)->signed_size += __n;                        \
+} while(0)
+#else
+#define count_and_csum(C, __p,__n)             \
+do {                                           \
+       (C)->signed_size += __n;                \
+} while(0)
+#endif
+
+#define crypto_digest_update_data(C,PTR,N)                     \
+do {                                                           \
+       size_t __n = (N);                                       \
+       uint8_t *__p = (uint8_t *)(PTR);                        \
+       count_and_csum((C), __p, __n);                          \
+       crypto_digest_update_kernel((C)->digest, __p, __n);     \
+} while(0)
+
+#define crypto_digest_update_val(C,VAL)                                \
+do {                                                           \
+       size_t __n = sizeof(VAL);                               \
+       uint8_t *__p = (uint8_t *)&(VAL);                       \
+       count_and_csum((C), __p, __n);                          \
+       crypto_digest_update_kernel((C)->digest, __p, __n);     \
+} while(0)
+
+static int module_verify_canonicalise(struct module_verify_data *mvdata);
+
+static int extract_elf_rela(struct module_verify_data *mvdata,
+                           int secix,
+                           const Elf_Rela *relatab, size_t nrels,
+                           const char *sh_name);
+
+static int extract_elf_rel(struct module_verify_data *mvdata,
+                          int secix,
+                          const Elf_Rel *reltab, size_t nrels,
+                          const char *sh_name);
+
+static int signedonly;
+
+/*****************************************************************************/
+/*
+ * verify a module's signature
+ */
+int module_verify_signature(struct module_verify_data *mvdata)
+{
+       const Elf_Shdr *sechdrs = mvdata->sections;
+       const char *secstrings = mvdata->secstrings;
+       const char *sig;
+       unsigned sig_size;
+       int i, ret;
+
+       for (i = 1; i < mvdata->nsects; i++) {
+               switch (sechdrs[i].sh_type) {
+               case SHT_PROGBITS:
+                       if (strcmp(mvdata->secstrings + sechdrs[i].sh_name,
+                                  ".module_sig") == 0) {
+                               mvdata->sig_index = i;
+                       }
+                       break;
+               }
+       }
+
+       if (mvdata->sig_index <= 0)
+               goto no_signature;
+
+       sig = mvdata->buffer + sechdrs[mvdata->sig_index].sh_offset;
+       sig_size = sechdrs[mvdata->sig_index].sh_size;
+
+       _debug("sig in section %d (size %d)\n",
+              mvdata->sig_index, sig_size);
+
+       /* produce a canonicalisation map for the sections */
+       ret = module_verify_canonicalise(mvdata);
+       if (ret < 0)
+               return ret;
+
+       /* grab an SHA1 transformation context
+        * - !!! if this tries to load the sha1.ko module, we will deadlock!!!
+        */
+       mvdata->digest = crypto_alloc_tfm2("sha1", 0, 1);
+       if (!mvdata->digest) {
+               printk("Couldn't load module - SHA1 transform unavailable\n");
+               return -EPERM;
+       }
+
+       crypto_digest_init(mvdata->digest);
+
+#ifdef MODSIGN_DEBUG
+       mvdata->xcsum = 0;
+#endif
+
+       /* load data from each relevant section into the digest */
+       for (i = 1; i < mvdata->nsects; i++) {
+               unsigned long sh_type = sechdrs[i].sh_type;
+               unsigned long sh_info = sechdrs[i].sh_info;
+               unsigned long sh_size = sechdrs[i].sh_size;
+               unsigned long sh_flags = sechdrs[i].sh_flags;
+               const char *sh_name = secstrings + sechdrs[i].sh_name;
+               const void *data = mvdata->buffer + sechdrs[i].sh_offset;
+
+               if (i == mvdata->sig_index)
+                       continue;
+
+#ifdef MODSIGN_DEBUG
+               mvdata->csum = 0;
+#endif
+
+               /* it would be nice to include relocation sections, but the act
+                * of adding a signature to the module seems changes their
+                * contents, because the symtab gets changed when sections are
+                * added or removed */
+               if (sh_type == SHT_REL || sh_type == SHT_RELA) {
+                       if (mvdata->canonlist[sh_info]) {
+                               uint32_t xsh_info = mvdata->canonmap[sh_info];
+
+                               crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+                               crypto_digest_update_val(mvdata, sechdrs[i].sh_type);
+                               crypto_digest_update_val(mvdata, sechdrs[i].sh_flags);
+                               crypto_digest_update_val(mvdata, sechdrs[i].sh_size);
+                               crypto_digest_update_val(mvdata, sechdrs[i].sh_addralign);
+                               crypto_digest_update_val(mvdata, xsh_info);
+
+                               if (sh_type == SHT_RELA)
+                                       ret = extract_elf_rela(
+                                               mvdata, i,
+                                               data,
+                                               sh_size / sizeof(Elf_Rela),
+                                               sh_name);
+                               else
+                                       ret = extract_elf_rel(
+                                               mvdata, i,
+                                               data,
+                                               sh_size / sizeof(Elf_Rel),
+                                               sh_name);
+
+                               if (ret < 0)
+                                       goto format_error;
+                       }
+
+                       continue;
+               }
+
+               /* include allocatable loadable sections */
+               if (sh_type != SHT_NOBITS && sh_flags & SHF_ALLOC)
+                       goto include_section;
+
+               continue;
+
+       include_section:
+               crypto_digest_update_data(mvdata, sh_name, strlen(sh_name));
+               crypto_digest_update_val(mvdata, sechdrs[i].sh_type);
+               crypto_digest_update_val(mvdata, sechdrs[i].sh_flags);
+               crypto_digest_update_val(mvdata, sechdrs[i].sh_size);
+               crypto_digest_update_val(mvdata, sechdrs[i].sh_addralign);
+               crypto_digest_update_data(mvdata, data, sh_size);
+
+               _debug("%08zx %02x digested the %s section, size %ld\n",
+                      mvdata->signed_size, mvdata->csum, sh_name, sh_size);
+
+               mvdata->canonlist[i] = 1;
+       }
+
+       _debug("Contributed %zu bytes to the digest (csum 0x%02x)\n",
+              mvdata->signed_size, mvdata->xcsum);
+
+       /* do the actual signature verification */
+       i = ksign_verify_signature(sig, sig_size, mvdata->digest);
+
+       _debug("verify-sig : %d\n", i);
+
+       if (i == 0)
+               i = 1;
+       return i;
+
+ format_error:
+       crypto_free_tfm(mvdata->digest);
+       return -ELIBBAD;
+
+       /* deal with the case of an unsigned module */
+ no_signature:
+       if (!signedonly)
+               return 0;
+       printk("An attempt to load unsigned module was rejected\n");
+       return -EPERM;
+
+} /* end module_verify_signature() */
+
+/*****************************************************************************/
+/*
+ * canonicalise the section table index numbers
+ */
+static int module_verify_canonicalise(struct module_verify_data *mvdata)
+{
+       int canon, loop, changed, tmp;
+
+       /* produce a list of index numbers of sections that contribute
+        * to the kernel's module image
+        */
+       mvdata->canonlist =
+               kmalloc(sizeof(int) * mvdata->nsects * 2, GFP_KERNEL);
+       if (!mvdata->canonlist)
+               return -ENOMEM;
+
+       mvdata->canonmap = mvdata->canonlist + mvdata->nsects;
+       canon = 0;
+
+       for (loop = 1; loop < mvdata->nsects; loop++) {
+               const Elf_Shdr *section = mvdata->sections + loop;
+
+               if (loop != mvdata->sig_index) {
+                       /* we only need to canonicalise allocatable sections */
+                       if (section->sh_flags & SHF_ALLOC)
+                               mvdata->canonlist[canon++] = loop;
+               }
+       }
+
+       /* canonicalise the index numbers of the contributing section */
+       do {
+               changed = 0;
+
+               for (loop = 0; loop < canon - 1; loop++) {
+                       const char *x, *y;
+
+                       x = mvdata->secstrings +
+                               mvdata->sections[mvdata->canonlist[loop + 0]].sh_name;
+                       y = mvdata->secstrings +
+                               mvdata->sections[mvdata->canonlist[loop + 1]].sh_name;
+
+                       if (strcmp(x, y) > 0) {
+                               tmp = mvdata->canonlist[loop + 0];
+                               mvdata->canonlist[loop + 0] =
+                                       mvdata->canonlist[loop + 1];
+                               mvdata->canonlist[loop + 1] = tmp;
+                               changed = 1;
+                       }
+               }
+
+       } while(changed);
+
+       for (loop = 0; loop < canon; loop++)
+               mvdata->canonmap[mvdata->canonlist[loop]] = loop + 1;
+
+       return 0;
+
+} /* end module_verify_canonicalise() */
+
+/*****************************************************************************/
+/*
+ * extract a RELA table
+ * - need to canonicalise the entries in case section addition/removal has
+ *   rearranged the symbol table and the section table
+ */
+static int extract_elf_rela(struct module_verify_data *mvdata,
+                           int secix,
+                           const Elf_Rela *relatab, size_t nrels,
+                           const char *sh_name)
+{
+       struct {
+#if defined(MODULES_ARE_ELF32)
+               uint32_t        r_offset;
+               uint32_t        r_addend;
+               uint32_t        st_value;
+               uint32_t        st_size;
+               uint16_t        st_shndx;
+               uint8_t         r_type;
+               uint8_t         st_info;
+               uint8_t         st_other;
+#elif defined(MODULES_ARE_ELF64)
+               uint64_t        r_offset;
+               uint64_t        r_addend;
+               uint64_t        st_value;
+               uint64_t        st_size;
+               uint32_t        r_type;
+               uint16_t        st_shndx;
+               uint8_t         st_info;
+               uint8_t         st_other;
+#else
+#error unsupported module type
+#endif
+       } __attribute__((packed)) relocation;
+
+       const Elf_Rela *reloc;
+       const Elf_Sym *symbol;
+       size_t loop;
+
+       /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+       for (loop = 0; loop < nrels; loop++) {
+               int st_shndx;
+
+               reloc = &relatab[loop];
+
+               /* decode the relocation */
+               relocation.r_offset = reloc->r_offset;
+               relocation.r_addend = reloc->r_addend;
+               relocation.r_type = ELF_R_TYPE(reloc->r_info);
+
+               /* decode the symbol referenced by the relocation */
+               symbol = &mvdata->symbols[ELF_R_SYM(reloc->r_info)];
+               relocation.st_info = symbol->st_info;
+               relocation.st_other = symbol->st_other;
+               relocation.st_value = symbol->st_value;
+               relocation.st_size = symbol->st_size;
+               relocation.st_shndx = symbol->st_shndx;
+               st_shndx = symbol->st_shndx;
+
+               /* canonicalise the section used by the symbol */
+               if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects)
+                       relocation.st_shndx = mvdata->canonmap[st_shndx];
+
+               crypto_digest_update_val(mvdata, relocation);
+
+               /* undefined symbols must be named if referenced */
+               if (st_shndx == SHN_UNDEF) {
+                       const char *name = mvdata->strings + symbol->st_name;
+                       crypto_digest_update_data(mvdata,
+                                                 name, strlen(name) + 1);
+               }
+       }
+
+       _debug("%08zx %02x digested the %s section, nrels %zu\n",
+              mvdata->signed_size, mvdata->csum, sh_name, nrels);
+
+       return 0;
+} /* end extract_elf_rela() */
+
+/*****************************************************************************/
+/*
+ *
+ */
+static int extract_elf_rel(struct module_verify_data *mvdata,
+                          int secix,
+                          const Elf_Rel *reltab, size_t nrels,
+                          const char *sh_name)
+{
+       struct {
+#if defined(MODULES_ARE_ELF32)
+               uint32_t        r_offset;
+               uint32_t        st_value;
+               uint32_t        st_size;
+               uint16_t        st_shndx;
+               uint8_t         r_type;
+               uint8_t         st_info;
+               uint8_t         st_other;
+#elif defined(MODULES_ARE_ELF64)
+               uint64_t        r_offset;
+               uint64_t        st_value;
+               uint64_t        st_size;
+               uint32_t        r_type;
+               uint16_t        st_shndx;
+               uint8_t         st_info;
+               uint8_t         st_other;
+#else
+#error unsupported module type
+#endif
+       } __attribute__((packed)) relocation;
+
+       const Elf_Rel *reloc;
+       const Elf_Sym *symbol;
+       size_t loop;
+
+       /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */
+       for (loop = 0; loop < nrels; loop++) {
+               int st_shndx;
+
+               reloc = &reltab[loop];
+
+               /* decode the relocation */
+               relocation.r_offset = reloc->r_offset;
+               relocation.r_type = ELF_R_TYPE(reloc->r_info);
+
+               /* decode the symbol referenced by the relocation */
+               symbol = &mvdata->symbols[ELF_R_SYM(reloc->r_info)];
+               relocation.st_info = symbol->st_info;
+               relocation.st_other = symbol->st_other;
+               relocation.st_value = symbol->st_value;
+               relocation.st_size = symbol->st_size;
+               relocation.st_shndx = symbol->st_shndx;
+               st_shndx = symbol->st_shndx;
+
+               /* canonicalise the section used by the symbol */
+               if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects)
+                       relocation.st_shndx = mvdata->canonmap[st_shndx];
+
+               crypto_digest_update_val(mvdata, relocation);
+
+               /* undefined symbols must be named if referenced */
+               if (st_shndx == SHN_UNDEF) {
+                       const char *name = mvdata->strings + symbol->st_name;
+                       crypto_digest_update_data(mvdata,
+                                                 name, strlen(name) + 1);
+               }
+       }
+
+       _debug("%08zx %02x digested the %s section, nrels %zu\n",
+              mvdata->signed_size, mvdata->csum, sh_name, nrels);
+
+       return 0;
+} /* end extract_elf_rel() */
+
+static int __init sign_setup(char *str)
+{
+       signedonly = 1;
+       return 0;
+}
+__setup("enforcemodulesig", sign_setup);
diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c
new file mode 100644 (file)
index 0000000..9f36b40
--- /dev/null
@@ -0,0 +1,84 @@
+
+#include <linux/linkage.h>
+#include <linux/errno.h>
+
+#include <asm/unistd.h>
+
+/*
+ * Non-implemented system calls get redirected here.
+ */
+asmlinkage long sys_ni_syscall(void)
+{
+       return -ENOSYS;
+}
+
+cond_syscall(sys_nfsservctl)
+cond_syscall(sys_quotactl)
+cond_syscall(sys_acct)
+cond_syscall(sys_lookup_dcookie)
+cond_syscall(sys_swapon)
+cond_syscall(sys_swapoff)
+cond_syscall(sys_init_module)
+cond_syscall(sys_delete_module)
+cond_syscall(sys_socketpair)
+cond_syscall(sys_bind)
+cond_syscall(sys_listen)
+cond_syscall(sys_accept)
+cond_syscall(sys_connect)
+cond_syscall(sys_getsockname)
+cond_syscall(sys_getpeername)
+cond_syscall(sys_sendto)
+cond_syscall(sys_send)
+cond_syscall(sys_recvfrom)
+cond_syscall(sys_recv)
+cond_syscall(sys_socket)
+cond_syscall(sys_setsockopt)
+cond_syscall(sys_getsockopt)
+cond_syscall(sys_shutdown)
+cond_syscall(sys_sendmsg)
+cond_syscall(sys_recvmsg)
+cond_syscall(sys_socketcall)
+cond_syscall(sys_futex)
+cond_syscall(compat_sys_futex)
+cond_syscall(sys_epoll_create)
+cond_syscall(sys_epoll_ctl)
+cond_syscall(sys_epoll_wait)
+cond_syscall(sys_semget)
+cond_syscall(sys_semop)
+cond_syscall(sys_semtimedop)
+cond_syscall(sys_semctl)
+cond_syscall(sys_msgget)
+cond_syscall(sys_msgsnd)
+cond_syscall(sys_msgrcv)
+cond_syscall(sys_msgctl)
+cond_syscall(sys_shmget)
+cond_syscall(sys_shmdt)
+cond_syscall(sys_shmctl)
+cond_syscall(sys_mq_open)
+cond_syscall(sys_mq_unlink)
+cond_syscall(sys_mq_timedsend)
+cond_syscall(sys_mq_timedreceive)
+cond_syscall(sys_mq_notify)
+cond_syscall(sys_mq_getsetattr)
+cond_syscall(compat_sys_mq_open)
+cond_syscall(compat_sys_mq_timedsend)
+cond_syscall(compat_sys_mq_timedreceive)
+cond_syscall(compat_sys_mq_notify)
+cond_syscall(compat_sys_mq_getsetattr)
+cond_syscall(sys_mbind)
+cond_syscall(sys_get_mempolicy)
+cond_syscall(sys_set_mempolicy)
+cond_syscall(compat_sys_mbind)
+cond_syscall(compat_sys_get_mempolicy)
+cond_syscall(compat_sys_set_mempolicy)
+cond_syscall(sys_add_key)
+cond_syscall(sys_request_key)
+cond_syscall(sys_keyctl)
+cond_syscall(compat_sys_keyctl)
+cond_syscall(compat_sys_socketcall)
+
+/* arch-specific weak syscall entries */
+cond_syscall(sys_pciconfig_read)
+cond_syscall(sys_pciconfig_write)
+cond_syscall(sys_pciconfig_iobase)
+
diff --git a/kernel/wait.c b/kernel/wait.c
new file mode 100644 (file)
index 0000000..791681c
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Generic waiting primitives.
+ *
+ * (C) 2004 William Irwin, Oracle
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/wait.h>
+#include <linux/hash.h>
+
+void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
+{
+       unsigned long flags;
+
+       wait->flags &= ~WQ_FLAG_EXCLUSIVE;
+       spin_lock_irqsave(&q->lock, flags);
+       __add_wait_queue(q, wait);
+       spin_unlock_irqrestore(&q->lock, flags);
+}
+EXPORT_SYMBOL(add_wait_queue);
+
+void fastcall add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait)
+{
+       unsigned long flags;
+
+       wait->flags |= WQ_FLAG_EXCLUSIVE;
+       spin_lock_irqsave(&q->lock, flags);
+       __add_wait_queue_tail(q, wait);
+       spin_unlock_irqrestore(&q->lock, flags);
+}
+EXPORT_SYMBOL(add_wait_queue_exclusive);
+
+void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&q->lock, flags);
+       __remove_wait_queue(q, wait);
+       spin_unlock_irqrestore(&q->lock, flags);
+}
+EXPORT_SYMBOL(remove_wait_queue);
+
+
+/*
+ * Note: we use "set_current_state()" _after_ the wait-queue add,
+ * because we need a memory barrier there on SMP, so that any
+ * wake-function that tests for the wait-queue being active
+ * will be guaranteed to see waitqueue addition _or_ subsequent
+ * tests in this thread will see the wakeup having taken place.
+ *
+ * The spin_unlock() itself is semi-permeable and only protects
+ * one way (it only protects stuff inside the critical region and
+ * stops them from bleeding out - it would still allow subsequent
+ * loads to move into the the critical region).
+ */
+void fastcall
+prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
+{
+       unsigned long flags;
+
+       wait->flags &= ~WQ_FLAG_EXCLUSIVE;
+       spin_lock_irqsave(&q->lock, flags);
+       if (list_empty(&wait->task_list))
+               __add_wait_queue(q, wait);
+       /*
+        * don't alter the task state if this is just going to
+        * queue an async wait queue callback
+        */
+       if (is_sync_wait(wait))
+               set_current_state(state);
+       spin_unlock_irqrestore(&q->lock, flags);
+}
+EXPORT_SYMBOL(prepare_to_wait);
+
+void fastcall
+prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state)
+{
+       unsigned long flags;
+
+       wait->flags |= WQ_FLAG_EXCLUSIVE;
+       spin_lock_irqsave(&q->lock, flags);
+       if (list_empty(&wait->task_list))
+               __add_wait_queue_tail(q, wait);
+       /*
+        * don't alter the task state if this is just going to
+        * queue an async wait queue callback
+        */
+       if (is_sync_wait(wait))
+               set_current_state(state);
+       spin_unlock_irqrestore(&q->lock, flags);
+}
+EXPORT_SYMBOL(prepare_to_wait_exclusive);
+
+void fastcall finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
+{
+       unsigned long flags;
+
+       __set_current_state(TASK_RUNNING);
+       /*
+        * We can check for list emptiness outside the lock
+        * IFF:
+        *  - we use the "careful" check that verifies both
+        *    the next and prev pointers, so that there cannot
+        *    be any half-pending updates in progress on other
+        *    CPU's that we haven't seen yet (and that might
+        *    still change the stack area.
+        * and
+        *  - all other users take the lock (ie we can only
+        *    have _one_ other CPU that looks at or modifies
+        *    the list).
+        */
+       if (!list_empty_careful(&wait->task_list)) {
+               spin_lock_irqsave(&q->lock, flags);
+               list_del_init(&wait->task_list);
+               spin_unlock_irqrestore(&q->lock, flags);
+       }
+}
+EXPORT_SYMBOL(finish_wait);
+
+int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+       int ret = default_wake_function(wait, mode, sync, key);
+
+       if (ret)
+               list_del_init(&wait->task_list);
+       return ret;
+}
+EXPORT_SYMBOL(autoremove_wake_function);
+
+int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *arg)
+{
+       struct wait_bit_key *key = arg;
+       struct wait_bit_queue *wait_bit
+               = container_of(wait, struct wait_bit_queue, wait);
+
+       if (wait_bit->key.flags != key->flags ||
+                       wait_bit->key.bit_nr != key->bit_nr ||
+                       test_bit(key->bit_nr, key->flags))
+               return 0;
+       else
+               return autoremove_wake_function(wait, mode, sync, key);
+}
+EXPORT_SYMBOL(wake_bit_function);
+
+/*
+ * To allow interruptible waiting and asynchronous (i.e. nonblocking)
+ * waiting, the actions of __wait_on_bit() and __wait_on_bit_lock() are
+ * permitted return codes. Nonzero return codes halt waiting and return.
+ */
+int __sched fastcall
+__wait_on_bit(wait_queue_head_t *wq, struct wait_bit_queue *q,
+                       int (*action)(void *), unsigned mode)
+{
+       int ret = 0;
+
+       do {
+               prepare_to_wait(wq, &q->wait, mode);
+               if (test_bit(q->key.bit_nr, q->key.flags))
+                       ret = (*action)(q->key.flags);
+       } while (test_bit(q->key.bit_nr, q->key.flags) && !ret);
+       finish_wait(wq, &q->wait);
+       return ret;
+}
+EXPORT_SYMBOL(__wait_on_bit);
+
+int __sched fastcall out_of_line_wait_on_bit(void *word, int bit,
+                                       int (*action)(void *), unsigned mode)
+{
+       wait_queue_head_t *wq = bit_waitqueue(word, bit);
+       DEFINE_WAIT_BIT(wait, word, bit);
+
+       return __wait_on_bit(wq, &wait, action, mode);
+}
+EXPORT_SYMBOL(out_of_line_wait_on_bit);
+
+int __sched fastcall
+__wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q,
+                       int (*action)(void *), unsigned mode)
+{
+       int ret = 0;
+
+       do {
+               prepare_to_wait_exclusive(wq, &q->wait, mode);
+               if (test_bit(q->key.bit_nr, q->key.flags)) {
+                       if ((ret = (*action)(q->key.flags)))
+                               break;
+               }
+       } while (test_and_set_bit(q->key.bit_nr, q->key.flags));
+       finish_wait(wq, &q->wait);
+       return ret;
+}
+EXPORT_SYMBOL(__wait_on_bit_lock);
+
+int __sched fastcall out_of_line_wait_on_bit_lock(void *word, int bit,
+                                       int (*action)(void *), unsigned mode)
+{
+       wait_queue_head_t *wq = bit_waitqueue(word, bit);
+       DEFINE_WAIT_BIT(wait, word, bit);
+
+       return __wait_on_bit_lock(wq, &wait, action, mode);
+}
+EXPORT_SYMBOL(out_of_line_wait_on_bit_lock);
+
+void fastcall __wake_up_bit(wait_queue_head_t *wq, void *word, int bit)
+{
+       struct wait_bit_key key = __WAIT_BIT_KEY_INITIALIZER(word, bit);
+       if (waitqueue_active(wq))
+               __wake_up(wq, TASK_INTERRUPTIBLE|TASK_UNINTERRUPTIBLE, 1, &key);
+}
+EXPORT_SYMBOL(__wake_up_bit);
+
+/**
+ * wake_up_bit - wake up a waiter on a bit
+ * @word: the word being waited on, a kernel virtual address
+ * @bit: the bit of the word being waited on
+ *
+ * There is a standard hashed waitqueue table for generic use. This
+ * is the part of the hashtable's accessor API that wakes up waiters
+ * on a bit. For instance, if one were to have waiters on a bitflag,
+ * one would call wake_up_bit() after clearing the bit.
+ *
+ * In order for this to function properly, as it uses waitqueue_active()
+ * internally, some kind of memory barrier must be done prior to calling
+ * this. Typically, this will be smp_mb__after_clear_bit(), but in some
+ * cases where bitflags are manipulated non-atomically under a lock, one
+ * may need to use a less regular barrier, such fs/inode.c's smp_mb(),
+ * because spin_unlock() does not guarantee a memory barrier.
+ */
+void fastcall wake_up_bit(void *word, int bit)
+{
+       __wake_up_bit(bit_waitqueue(word, bit), word, bit);
+}
+EXPORT_SYMBOL(wake_up_bit);
+
+fastcall wait_queue_head_t *bit_waitqueue(void *word, int bit)
+{
+       const int shift = BITS_PER_LONG == 32 ? 5 : 6;
+       const struct zone *zone = page_zone(virt_to_page(word));
+       unsigned long val = (unsigned long)word << shift | bit;
+
+       return &zone->wait_table[hash_long(val, zone->wait_table_bits)];
+}
+EXPORT_SYMBOL(bit_waitqueue);
diff --git a/lib/find_next_bit.c b/lib/find_next_bit.c
new file mode 100644 (file)
index 0000000..d08302d
--- /dev/null
@@ -0,0 +1,55 @@
+/* find_next_bit.c: fallback find next bit implementation
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/bitops.h>
+
+int find_next_bit(const unsigned long *addr, int size, int offset)
+{
+       const unsigned long *base;
+       const int NBITS = sizeof(*addr) * 8;
+       unsigned long tmp;
+
+       base = addr;
+       if (offset) {
+               int suboffset;
+
+               addr += offset / NBITS;
+
+               suboffset = offset % NBITS;
+               if (suboffset) {
+                       tmp = *addr;
+                       tmp >>= suboffset;
+                       if (tmp)
+                               goto finish;
+               }
+
+               addr++;
+       }
+
+       while ((tmp = *addr) == 0)
+               addr++;
+
+       offset = (addr - base) * NBITS;
+
+ finish:
+       /* count the remaining bits without using __ffs() since that takes a 32-bit arg */
+       while (!(tmp & 0xff)) {
+               offset += 8;
+               tmp >>= 8;
+       }
+
+       while (!(tmp & 1)) {
+               offset++;
+               tmp >>= 1;
+       }
+
+       return offset;
+}
diff --git a/lib/kernel_lock.c b/lib/kernel_lock.c
new file mode 100644 (file)
index 0000000..48dc05a
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * lib/kernel_lock.c
+ *
+ * This is the traditional BKL - big kernel lock. Largely
+ * relegated to obsolescense, but used by various less
+ * important (or lazy) subsystems.
+ */
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+
+/*
+ * The 'big kernel lock'
+ *
+ * This spinlock is taken and released recursively by lock_kernel()
+ * and unlock_kernel().  It is transparently dropped and reaquired
+ * over schedule().  It is used to protect legacy code that hasn't
+ * been migrated to a proper locking design yet.
+ *
+ * Don't use in new code.
+ */
+static spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
+
+
+/*
+ * Acquire/release the underlying lock from the scheduler.
+ *
+ * This is called with preemption disabled, and should
+ * return an error value if it cannot get the lock and
+ * TIF_NEED_RESCHED gets set.
+ *
+ * If it successfully gets the lock, it should increment
+ * the preemption count like any spinlock does.
+ *
+ * (This works on UP too - _raw_spin_trylock will never
+ * return false in that case)
+ */
+int __lockfunc get_kernel_lock(void)
+{
+       while (!_raw_spin_trylock(&kernel_flag)) {
+               if (test_thread_flag(TIF_NEED_RESCHED))
+                       return -EAGAIN;
+               cpu_relax();
+       }
+       preempt_disable();
+       return 0;
+}
+
+void __lockfunc put_kernel_lock(void)
+{
+       _raw_spin_unlock(&kernel_flag);
+       preempt_enable_no_resched();
+}
+
+/*
+ * These are the BKL spinlocks - we try to be polite about preemption. 
+ * If SMP is not on (ie UP preemption), this all goes away because the
+ * _raw_spin_trylock() will always succeed.
+ */
+#ifdef CONFIG_PREEMPT
+static inline void __lock_kernel(void)
+{
+       preempt_disable();
+       if (unlikely(!_raw_spin_trylock(&kernel_flag))) {
+               /*
+                * If preemption was disabled even before this
+                * was called, there's nothing we can be polite
+                * about - just spin.
+                */
+               if (preempt_count() > 1) {
+                       _raw_spin_lock(&kernel_flag);
+                       return;
+               }
+
+               /*
+                * Otherwise, let's wait for the kernel lock
+                * with preemption enabled..
+                */
+               do {
+                       preempt_enable();
+                       while (spin_is_locked(&kernel_flag))
+                               cpu_relax();
+                       preempt_disable();
+               } while (!_raw_spin_trylock(&kernel_flag));
+       }
+}
+
+#else
+
+/*
+ * Non-preemption case - just get the spinlock
+ */
+static inline void __lock_kernel(void)
+{
+       _raw_spin_lock(&kernel_flag);
+}
+#endif
+
+static inline void __unlock_kernel(void)
+{
+       _raw_spin_unlock(&kernel_flag);
+       preempt_enable();
+}
+
+/*
+ * Getting the big kernel lock.
+ *
+ * This cannot happen asynchronously, so we only need to
+ * worry about other CPU's.
+ */
+void __lockfunc lock_kernel(void)
+{
+       int depth = current->lock_depth+1;
+       if (likely(!depth))
+               __lock_kernel();
+       current->lock_depth = depth;
+}
+
+void __lockfunc unlock_kernel(void)
+{
+       BUG_ON(current->lock_depth < 0);
+       if (likely(--current->lock_depth < 0))
+               __unlock_kernel();
+}
+
+EXPORT_SYMBOL(lock_kernel);
+EXPORT_SYMBOL(unlock_kernel);
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
new file mode 100644 (file)
index 0000000..5c78b21
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * kernel userspace event delivery
+ *
+ * Copyright (C) 2004 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2004 Novell, Inc.  All rights reserved.
+ * Copyright (C) 2004 IBM, Inc. All rights reserved.
+ *
+ * Licensed under the GNU GPL v2.
+ *
+ * Authors:
+ *     Robert Love             <rml@novell.com>
+ *     Kay Sievers             <kay.sievers@vrfy.org>
+ *     Arjan van de Ven        <arjanv@redhat.com>
+ *     Greg Kroah-Hartman      <greg@kroah.com>
+ */
+
+#include <linux/spinlock.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/string.h>
+#include <linux/kobject_uevent.h>
+#include <linux/kobject.h>
+#include <net/sock.h>
+
+#define BUFFER_SIZE    1024    /* buffer for the hotplug env */
+#define NUM_ENVP       32      /* number of env pointers */
+
+#if defined(CONFIG_KOBJECT_UEVENT) || defined(CONFIG_HOTPLUG)
+static char *action_to_string(enum kobject_action action)
+{
+       switch (action) {
+       case KOBJ_ADD:
+               return "add";
+       case KOBJ_REMOVE:
+               return "remove";
+       case KOBJ_CHANGE:
+               return "change";
+       case KOBJ_MOUNT:
+               return "mount";
+       case KOBJ_UMOUNT:
+               return "umount";
+       case KOBJ_OFFLINE:
+               return "offline";
+       case KOBJ_ONLINE:
+               return "online";
+       default:
+               return NULL;
+       }
+}
+#endif
+
+#ifdef CONFIG_KOBJECT_UEVENT
+static struct sock *uevent_sock;
+
+/**
+ * send_uevent - notify userspace by sending event trough netlink socket
+ *
+ * @signal: signal name
+ * @obj: object path (kobject)
+ * @envp: possible hotplug environment to pass with the message
+ * @gfp_mask:
+ */
+static int send_uevent(const char *signal, const char *obj,
+                      char **envp, int gfp_mask)
+{
+       struct sk_buff *skb;
+       char *pos;
+       int len;
+
+       if (!uevent_sock)
+               return -EIO;
+
+       len = strlen(signal) + 1;
+       len += strlen(obj) + 1;
+
+       /* allocate buffer with the maximum possible message size */
+       skb = alloc_skb(len + BUFFER_SIZE, gfp_mask);
+       if (!skb)
+               return -ENOMEM;
+
+       pos = skb_put(skb, len);
+       sprintf(pos, "%s@%s", signal, obj);
+
+       /* copy the environment key by key to our continuous buffer */
+       if (envp) {
+               int i;
+
+               for (i = 2; envp[i]; i++) {
+                       len = strlen(envp[i]) + 1;
+                       pos = skb_put(skb, len);
+                       strcpy(pos, envp[i]);
+               }
+       }
+
+       return netlink_broadcast(uevent_sock, skb, 0, 1, gfp_mask);
+}
+
+static int do_kobject_uevent(struct kobject *kobj, enum kobject_action action, 
+                            struct attribute *attr, int gfp_mask)
+{
+       char *path;
+       char *attrpath;
+       char *signal;
+       int len;
+       int rc = -ENOMEM;
+
+       path = kobject_get_path(kobj, gfp_mask);
+       if (!path)
+               return -ENOMEM;
+
+       signal = action_to_string(action);
+       if (!signal)
+               return -EINVAL;
+
+       if (attr) {
+               len = strlen(path);
+               len += strlen(attr->name) + 2;
+               attrpath = kmalloc(len, gfp_mask);
+               if (!attrpath)
+                       goto exit;
+               sprintf(attrpath, "%s/%s", path, attr->name);
+               rc = send_uevent(signal, attrpath, NULL, gfp_mask);
+               kfree(attrpath);
+       } else
+               rc = send_uevent(signal, path, NULL, gfp_mask);
+
+exit:
+       kfree(path);
+       return rc;
+}
+
+/**
+ * kobject_uevent - notify userspace by sending event through netlink socket
+ * 
+ * @signal: signal name
+ * @kobj: struct kobject that the event is happening to
+ * @attr: optional struct attribute the event belongs to
+ */
+int kobject_uevent(struct kobject *kobj, enum kobject_action action,
+                  struct attribute *attr)
+{
+       return do_kobject_uevent(kobj, action, attr, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(kobject_uevent);
+
+int kobject_uevent_atomic(struct kobject *kobj, enum kobject_action action,
+                         struct attribute *attr)
+{
+       return do_kobject_uevent(kobj, action, attr, GFP_ATOMIC);
+}
+EXPORT_SYMBOL_GPL(kobject_uevent_atomic);
+
+static int __init kobject_uevent_init(void)
+{
+       uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, NULL);
+
+       if (!uevent_sock) {
+               printk(KERN_ERR
+                      "kobject_uevent: unable to create netlink socket!\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+postcore_initcall(kobject_uevent_init);
+
+#else
+static inline int send_uevent(const char *signal, const char *obj,
+                             char **envp, int gfp_mask)
+{
+       return 0;
+}
+
+#endif /* CONFIG_KOBJECT_UEVENT */
+
+
+#ifdef CONFIG_HOTPLUG
+char hotplug_path[HOTPLUG_PATH_LEN] = "/sbin/hotplug";
+u64 hotplug_seqnum;
+static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED;
+
+/**
+ * kobject_hotplug - notify userspace by executing /sbin/hotplug
+ *
+ * @action: action that is happening (usually "ADD" or "REMOVE")
+ * @kobj: struct kobject that the action is happening to
+ */
+void kobject_hotplug(struct kobject *kobj, enum kobject_action action)
+{
+       char *argv [3];
+       char **envp = NULL;
+       char *buffer = NULL;
+       char *seq_buff;
+       char *scratch;
+       int i = 0;
+       int retval;
+       char *kobj_path = NULL;
+       char *name = NULL;
+       char *action_string;
+       u64 seq;
+       struct kobject *top_kobj = kobj;
+       struct kset *kset;
+       static struct kset_hotplug_ops null_hotplug_ops;
+       struct kset_hotplug_ops *hotplug_ops = &null_hotplug_ops;
+
+       /* If this kobj does not belong to a kset,
+          try to find a parent that does. */
+       if (!top_kobj->kset && top_kobj->parent) {
+               do {
+                       top_kobj = top_kobj->parent;
+               } while (!top_kobj->kset && top_kobj->parent);
+       }
+
+       if (top_kobj->kset)
+               kset = top_kobj->kset;
+       else
+               return;
+
+       if (kset->hotplug_ops)
+               hotplug_ops = kset->hotplug_ops;
+
+       /* If the kset has a filter operation, call it.
+          Skip the event, if the filter returns zero. */
+       if (hotplug_ops->filter) {
+               if (!hotplug_ops->filter(kset, kobj))
+                       return;
+       }
+
+       pr_debug ("%s\n", __FUNCTION__);
+
+       action_string = action_to_string(action);
+       if (!action_string)
+               return;
+
+       envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
+       if (!envp)
+               return;
+       memset (envp, 0x00, NUM_ENVP * sizeof (char *));
+
+       buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
+       if (!buffer)
+               goto exit;
+
+       if (hotplug_ops->name)
+               name = hotplug_ops->name(kset, kobj);
+       if (name == NULL)
+               name = kset->kobj.name;
+
+       argv [0] = hotplug_path;
+       argv [1] = name;
+       argv [2] = NULL;
+
+       /* minimal command environment */
+       envp [i++] = "HOME=/";
+       envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+       scratch = buffer;
+
+       envp [i++] = scratch;
+       scratch += sprintf(scratch, "ACTION=%s", action_string) + 1;
+
+       kobj_path = kobject_get_path(kobj, GFP_KERNEL);
+       if (!kobj_path)
+               goto exit;
+
+       envp [i++] = scratch;
+       scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1;
+
+       envp [i++] = scratch;
+       scratch += sprintf(scratch, "SUBSYSTEM=%s", name) + 1;
+
+       /* reserve space for the sequence,
+        * put the real one in after the hotplug call */
+       envp[i++] = seq_buff = scratch;
+       scratch += strlen("SEQNUM=18446744073709551616") + 1;
+
+       if (hotplug_ops->hotplug) {
+               /* have the kset specific function add its stuff */
+               retval = hotplug_ops->hotplug (kset, kobj,
+                                 &envp[i], NUM_ENVP - i, scratch,
+                                 BUFFER_SIZE - (scratch - buffer));
+               if (retval) {
+                       pr_debug ("%s - hotplug() returned %d\n",
+                                 __FUNCTION__, retval);
+                       goto exit;
+               }
+       }
+
+       spin_lock(&sequence_lock);
+       seq = ++hotplug_seqnum;
+       spin_unlock(&sequence_lock);
+       sprintf(seq_buff, "SEQNUM=%llu", (unsigned long long)seq);
+
+       pr_debug ("%s: %s %s seq=%llu %s %s %s %s %s\n",
+                 __FUNCTION__, argv[0], argv[1], (unsigned long long)seq,
+                 envp[0], envp[1], envp[2], envp[3], envp[4]);
+
+       send_uevent(action_string, kobj_path, envp, GFP_KERNEL);
+
+       if (!hotplug_path[0])
+               goto exit;
+
+       retval = call_usermodehelper (argv[0], argv, envp, 0);
+       if (retval)
+               pr_debug ("%s - call_usermodehelper returned %d\n",
+                         __FUNCTION__, retval);
+
+exit:
+       kfree(kobj_path);
+       kfree(buffer);
+       kfree(envp);
+       return;
+}
+EXPORT_SYMBOL(kobject_hotplug);
+
+/**
+ * add_hotplug_env_var - helper for creating hotplug environment variables
+ * @envp: Pointer to table of environment variables, as passed into
+ * hotplug() method.
+ * @num_envp: Number of environment variable slots available, as
+ * passed into hotplug() method.
+ * @cur_index: Pointer to current index into @envp.  It should be
+ * initialized to 0 before the first call to add_hotplug_env_var(),
+ * and will be incremented on success.
+ * @buffer: Pointer to buffer for environment variables, as passed
+ * into hotplug() method.
+ * @buffer_size: Length of @buffer, as passed into hotplug() method.
+ * @cur_len: Pointer to current length of space used in @buffer.
+ * Should be initialized to 0 before the first call to
+ * add_hotplug_env_var(), and will be incremented on success.
+ * @format: Format for creating environment variable (of the form
+ * "XXX=%x") for snprintf().
+ *
+ * Returns 0 if environment variable was added successfully or -ENOMEM
+ * if no space was available.
+ */
+int add_hotplug_env_var(char **envp, int num_envp, int *cur_index,
+                       char *buffer, int buffer_size, int *cur_len,
+                       const char *format, ...)
+{
+       va_list args;
+
+       /*
+        * We check against num_envp - 1 to make sure there is at
+        * least one slot left after we return, since the hotplug
+        * method needs to set the last slot to NULL.
+        */
+       if (*cur_index >= num_envp - 1)
+               return -ENOMEM;
+
+       envp[*cur_index] = buffer + *cur_len;
+
+       va_start(args, format);
+       *cur_len += vsnprintf(envp[*cur_index],
+                             max(buffer_size - *cur_len, 0),
+                             format, args) + 1;
+       va_end(args);
+
+       if (*cur_len > buffer_size)
+               return -ENOMEM;
+
+       (*cur_index)++;
+       return 0;
+}
+EXPORT_SYMBOL(add_hotplug_env_var);
+
+#endif /* CONFIG_HOTPLUG */
diff --git a/lib/prio_tree.c b/lib/prio_tree.c
new file mode 100644 (file)
index 0000000..ccfd850
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * lib/prio_tree.c - priority search tree
+ *
+ * Copyright (C) 2004, Rajesh Venkatasubramanian <vrajesh@umich.edu>
+ *
+ * This file is released under the GPL v2.
+ *
+ * Based on the radix priority search tree proposed by Edward M. McCreight
+ * SIAM Journal of Computing, vol. 14, no.2, pages 257-276, May 1985
+ *
+ * 02Feb2004   Initial version
+ */
+
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/prio_tree.h>
+
+/*
+ * A clever mix of heap and radix trees forms a radix priority search tree (PST)
+ * which is useful for storing intervals, e.g, we can consider a vma as a closed
+ * interval of file pages [offset_begin, offset_end], and store all vmas that
+ * map a file in a PST. Then, using the PST, we can answer a stabbing query,
+ * i.e., selecting a set of stored intervals (vmas) that overlap with (map) a
+ * given input interval X (a set of consecutive file pages), in "O(log n + m)"
+ * time where 'log n' is the height of the PST, and 'm' is the number of stored
+ * intervals (vmas) that overlap (map) with the input interval X (the set of
+ * consecutive file pages).
+ *
+ * In our implementation, we store closed intervals of the form [radix_index,
+ * heap_index]. We assume that always radix_index <= heap_index. McCreight's PST
+ * is designed for storing intervals with unique radix indices, i.e., each
+ * interval have different radix_index. However, this limitation can be easily
+ * overcome by using the size, i.e., heap_index - radix_index, as part of the
+ * index, so we index the tree using [(radix_index,size), heap_index].
+ *
+ * When the above-mentioned indexing scheme is used, theoretically, in a 32 bit
+ * machine, the maximum height of a PST can be 64. We can use a balanced version
+ * of the priority search tree to optimize the tree height, but the balanced
+ * tree proposed by McCreight is too complex and memory-hungry for our purpose.
+ */
+
+/*
+ * The following macros are used for implementing prio_tree for i_mmap
+ */
+
+#define RADIX_INDEX(vma)  ((vma)->vm_pgoff)
+#define VMA_SIZE(vma)    (((vma)->vm_end - (vma)->vm_start) >> PAGE_SHIFT)
+/* avoid overflow */
+#define HEAP_INDEX(vma)          ((vma)->vm_pgoff + (VMA_SIZE(vma) - 1))
+
+
+static void get_index(const struct prio_tree_root *root,
+    const struct prio_tree_node *node,
+    unsigned long *radix, unsigned long *heap)
+{
+       if (root->raw) {
+               struct vm_area_struct *vma = prio_tree_entry(
+                   node, struct vm_area_struct, shared.prio_tree_node);
+
+               *radix = RADIX_INDEX(vma);
+               *heap = HEAP_INDEX(vma);
+       }
+       else {
+               *radix = node->start;
+               *heap = node->last;
+       }
+}
+
+static unsigned long index_bits_to_maxindex[BITS_PER_LONG];
+
+void __init prio_tree_init(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(index_bits_to_maxindex) - 1; i++)
+               index_bits_to_maxindex[i] = (1UL << (i + 1)) - 1;
+       index_bits_to_maxindex[ARRAY_SIZE(index_bits_to_maxindex) - 1] = ~0UL;
+}
+
+/*
+ * Maximum heap_index that can be stored in a PST with index_bits bits
+ */
+static inline unsigned long prio_tree_maxindex(unsigned int bits)
+{
+       return index_bits_to_maxindex[bits - 1];
+}
+
+/*
+ * Extend a priority search tree so that it can store a node with heap_index
+ * max_heap_index. In the worst case, this algorithm takes O((log n)^2).
+ * However, this function is used rarely and the common case performance is
+ * not bad.
+ */
+static struct prio_tree_node *prio_tree_expand(struct prio_tree_root *root,
+               struct prio_tree_node *node, unsigned long max_heap_index)
+{
+       struct prio_tree_node *first = NULL, *prev, *last = NULL;
+
+       if (max_heap_index > prio_tree_maxindex(root->index_bits))
+               root->index_bits++;
+
+       while (max_heap_index > prio_tree_maxindex(root->index_bits)) {
+               root->index_bits++;
+
+               if (prio_tree_empty(root))
+                       continue;
+
+               if (first == NULL) {
+                       first = root->prio_tree_node;
+                       prio_tree_remove(root, root->prio_tree_node);
+                       INIT_PRIO_TREE_NODE(first);
+                       last = first;
+               } else {
+                       prev = last;
+                       last = root->prio_tree_node;
+                       prio_tree_remove(root, root->prio_tree_node);
+                       INIT_PRIO_TREE_NODE(last);
+                       prev->left = last;
+                       last->parent = prev;
+               }
+       }
+
+       INIT_PRIO_TREE_NODE(node);
+
+       if (first) {
+               node->left = first;
+               first->parent = node;
+       } else
+               last = node;
+
+       if (!prio_tree_empty(root)) {
+               last->left = root->prio_tree_node;
+               last->left->parent = last;
+       }
+
+       root->prio_tree_node = node;
+       return node;
+}
+
+/*
+ * Replace a prio_tree_node with a new node and return the old node
+ */
+struct prio_tree_node *prio_tree_replace(struct prio_tree_root *root,
+               struct prio_tree_node *old, struct prio_tree_node *node)
+{
+       INIT_PRIO_TREE_NODE(node);
+
+       if (prio_tree_root(old)) {
+               BUG_ON(root->prio_tree_node != old);
+               /*
+                * We can reduce root->index_bits here. However, it is complex
+                * and does not help much to improve performance (IMO).
+                */
+               node->parent = node;
+               root->prio_tree_node = node;
+       } else {
+               node->parent = old->parent;
+               if (old->parent->left == old)
+                       old->parent->left = node;
+               else
+                       old->parent->right = node;
+       }
+
+       if (!prio_tree_left_empty(old)) {
+               node->left = old->left;
+               old->left->parent = node;
+       }
+
+       if (!prio_tree_right_empty(old)) {
+               node->right = old->right;
+               old->right->parent = node;
+       }
+
+       return old;
+}
+
+/*
+ * Insert a prio_tree_node @node into a radix priority search tree @root. The
+ * algorithm typically takes O(log n) time where 'log n' is the number of bits
+ * required to represent the maximum heap_index. In the worst case, the algo
+ * can take O((log n)^2) - check prio_tree_expand.
+ *
+ * If a prior node with same radix_index and heap_index is already found in
+ * the tree, then returns the address of the prior node. Otherwise, inserts
+ * @node into the tree and returns @node.
+ */
+struct prio_tree_node *prio_tree_insert(struct prio_tree_root *root,
+               struct prio_tree_node *node)
+{
+       struct prio_tree_node *cur, *res = node;
+       unsigned long radix_index, heap_index;
+       unsigned long r_index, h_index, index, mask;
+       int size_flag = 0;
+
+       get_index(root, node, &radix_index, &heap_index);
+
+       if (prio_tree_empty(root) ||
+                       heap_index > prio_tree_maxindex(root->index_bits))
+               return prio_tree_expand(root, node, heap_index);
+
+       cur = root->prio_tree_node;
+       mask = 1UL << (root->index_bits - 1);
+
+       while (mask) {
+               get_index(root, cur, &r_index, &h_index);
+
+               if (r_index == radix_index && h_index == heap_index)
+                       return cur;
+
+                if (h_index < heap_index ||
+                   (h_index == heap_index && r_index > radix_index)) {
+                       struct prio_tree_node *tmp = node;
+                       node = prio_tree_replace(root, cur, node);
+                       cur = tmp;
+                       /* swap indices */
+                       index = r_index;
+                       r_index = radix_index;
+                       radix_index = index;
+                       index = h_index;
+                       h_index = heap_index;
+                       heap_index = index;
+               }
+
+               if (size_flag)
+                       index = heap_index - radix_index;
+               else
+                       index = radix_index;
+
+               if (index & mask) {
+                       if (prio_tree_right_empty(cur)) {
+                               INIT_PRIO_TREE_NODE(node);
+                               cur->right = node;
+                               node->parent = cur;
+                               return res;
+                       } else
+                               cur = cur->right;
+               } else {
+                       if (prio_tree_left_empty(cur)) {
+                               INIT_PRIO_TREE_NODE(node);
+                               cur->left = node;
+                               node->parent = cur;
+                               return res;
+                       } else
+                               cur = cur->left;
+               }
+
+               mask >>= 1;
+
+               if (!mask) {
+                       mask = 1UL << (BITS_PER_LONG - 1);
+                       size_flag = 1;
+               }
+       }
+       /* Should not reach here */
+       BUG();
+       return NULL;
+}
+
+/*
+ * Remove a prio_tree_node @node from a radix priority search tree @root. The
+ * algorithm takes O(log n) time where 'log n' is the number of bits required
+ * to represent the maximum heap_index.
+ */
+void prio_tree_remove(struct prio_tree_root *root, struct prio_tree_node *node)
+{
+       struct prio_tree_node *cur;
+       unsigned long r_index, h_index_right, h_index_left;
+
+       cur = node;
+
+       while (!prio_tree_left_empty(cur) || !prio_tree_right_empty(cur)) {
+               if (!prio_tree_left_empty(cur))
+                       get_index(root, cur->left, &r_index, &h_index_left);
+               else {
+                       cur = cur->right;
+                       continue;
+               }
+
+               if (!prio_tree_right_empty(cur))
+                       get_index(root, cur->right, &r_index, &h_index_right);
+               else {
+                       cur = cur->left;
+                       continue;
+               }
+
+               /* both h_index_left and h_index_right cannot be 0 */
+               if (h_index_left >= h_index_right)
+                       cur = cur->left;
+               else
+                       cur = cur->right;
+       }
+
+       if (prio_tree_root(cur)) {
+               BUG_ON(root->prio_tree_node != cur);
+               __INIT_PRIO_TREE_ROOT(root, root->raw);
+               return;
+       }
+
+       if (cur->parent->right == cur)
+               cur->parent->right = cur->parent;
+       else
+               cur->parent->left = cur->parent;
+
+       while (cur != node)
+               cur = prio_tree_replace(root, cur->parent, cur);
+}
+
+/*
+ * Following functions help to enumerate all prio_tree_nodes in the tree that
+ * overlap with the input interval X [radix_index, heap_index]. The enumeration
+ * takes O(log n + m) time where 'log n' is the height of the tree (which is
+ * proportional to # of bits required to represent the maximum heap_index) and
+ * 'm' is the number of prio_tree_nodes that overlap the interval X.
+ */
+
+static struct prio_tree_node *prio_tree_left(struct prio_tree_iter *iter,
+               unsigned long *r_index, unsigned long *h_index)
+{
+       if (prio_tree_left_empty(iter->cur))
+               return NULL;
+
+       get_index(iter->root, iter->cur->left, r_index, h_index);
+
+       if (iter->r_index <= *h_index) {
+               iter->cur = iter->cur->left;
+               iter->mask >>= 1;
+               if (iter->mask) {
+                       if (iter->size_level)
+                               iter->size_level++;
+               } else {
+                       if (iter->size_level) {
+                               BUG_ON(!prio_tree_left_empty(iter->cur));
+                               BUG_ON(!prio_tree_right_empty(iter->cur));
+                               iter->size_level++;
+                               iter->mask = ULONG_MAX;
+                       } else {
+                               iter->size_level = 1;
+                               iter->mask = 1UL << (BITS_PER_LONG - 1);
+                       }
+               }
+               return iter->cur;
+       }
+
+       return NULL;
+}
+
+static struct prio_tree_node *prio_tree_right(struct prio_tree_iter *iter,
+               unsigned long *r_index, unsigned long *h_index)
+{
+       unsigned long value;
+
+       if (prio_tree_right_empty(iter->cur))
+               return NULL;
+
+       if (iter->size_level)
+               value = iter->value;
+       else
+               value = iter->value | iter->mask;
+
+       if (iter->h_index < value)
+               return NULL;
+
+       get_index(iter->root, iter->cur->right, r_index, h_index);
+
+       if (iter->r_index <= *h_index) {
+               iter->cur = iter->cur->right;
+               iter->mask >>= 1;
+               iter->value = value;
+               if (iter->mask) {
+                       if (iter->size_level)
+                               iter->size_level++;
+               } else {
+                       if (iter->size_level) {
+                               BUG_ON(!prio_tree_left_empty(iter->cur));
+                               BUG_ON(!prio_tree_right_empty(iter->cur));
+                               iter->size_level++;
+                               iter->mask = ULONG_MAX;
+                       } else {
+                               iter->size_level = 1;
+                               iter->mask = 1UL << (BITS_PER_LONG - 1);
+                       }
+               }
+               return iter->cur;
+       }
+
+       return NULL;
+}
+
+static struct prio_tree_node *prio_tree_parent(struct prio_tree_iter *iter)
+{
+       iter->cur = iter->cur->parent;
+       if (iter->mask == ULONG_MAX)
+               iter->mask = 1UL;
+       else if (iter->size_level == 1)
+               iter->mask = 1UL;
+       else
+               iter->mask <<= 1;
+       if (iter->size_level)
+               iter->size_level--;
+       if (!iter->size_level && (iter->value & iter->mask))
+               iter->value ^= iter->mask;
+       return iter->cur;
+}
+
+static inline int overlap(struct prio_tree_iter *iter,
+               unsigned long r_index, unsigned long h_index)
+{
+       return iter->h_index >= r_index && iter->r_index <= h_index;
+}
+
+/*
+ * prio_tree_first:
+ *
+ * Get the first prio_tree_node that overlaps with the interval [radix_index,
+ * heap_index]. Note that always radix_index <= heap_index. We do a pre-order
+ * traversal of the tree.
+ */
+static struct prio_tree_node *prio_tree_first(struct prio_tree_iter *iter)
+{
+       struct prio_tree_root *root;
+       unsigned long r_index, h_index;
+
+       INIT_PRIO_TREE_ITER(iter);
+
+       root = iter->root;
+       if (prio_tree_empty(root))
+               return NULL;
+
+       get_index(root, root->prio_tree_node, &r_index, &h_index);
+
+       if (iter->r_index > h_index)
+               return NULL;
+
+       iter->mask = 1UL << (root->index_bits - 1);
+       iter->cur = root->prio_tree_node;
+
+       while (1) {
+               if (overlap(iter, r_index, h_index))
+                       return iter->cur;
+
+               if (prio_tree_left(iter, &r_index, &h_index))
+                       continue;
+
+               if (prio_tree_right(iter, &r_index, &h_index))
+                       continue;
+
+               break;
+       }
+       return NULL;
+}
+
+/*
+ * prio_tree_next:
+ *
+ * Get the next prio_tree_node that overlaps with the input interval in iter
+ */
+struct prio_tree_node *prio_tree_next(struct prio_tree_iter *iter)
+{
+       unsigned long r_index, h_index;
+
+       if (iter->cur == NULL)
+               return prio_tree_first(iter);
+
+repeat:
+       while (prio_tree_left(iter, &r_index, &h_index))
+               if (overlap(iter, r_index, h_index))
+                       return iter->cur;
+
+       while (!prio_tree_right(iter, &r_index, &h_index)) {
+               while (!prio_tree_root(iter->cur) &&
+                               iter->cur->parent->right == iter->cur)
+                       prio_tree_parent(iter);
+
+               if (prio_tree_root(iter->cur))
+                       return NULL;
+
+               prio_tree_parent(iter);
+       }
+
+       if (overlap(iter, r_index, h_index))
+               return iter->cur;
+
+       goto repeat;
+}
diff --git a/lib/reed_solomon/Makefile b/lib/reed_solomon/Makefile
new file mode 100644 (file)
index 0000000..747a2de
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# This is a modified version of reed solomon lib, 
+#
+
+obj-$(CONFIG_REED_SOLOMON) += reed_solomon.o
+
diff --git a/lib/reed_solomon/decode_rs.c b/lib/reed_solomon/decode_rs.c
new file mode 100644 (file)
index 0000000..d401dec
--- /dev/null
@@ -0,0 +1,272 @@
+/* 
+ * lib/reed_solomon/decode_rs.c
+ *
+ * Overview:
+ *   Generic Reed Solomon encoder / decoder library
+ *   
+ * Copyright 2002, Phil Karn, KA9Q
+ * May be used under the terms of the GNU General Public License (GPL)
+ *
+ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
+ *
+ * $Id: decode_rs.c,v 1.6 2004/10/22 15:41:47 gleixner Exp $
+ *
+ */
+
+/* Generic data width independent code which is included by the 
+ * wrappers.
+ */
+{ 
+       int deg_lambda, el, deg_omega;
+       int i, j, r, k, pad;
+       int nn = rs->nn;
+       int nroots = rs->nroots;
+       int fcr = rs->fcr;
+       int prim = rs->prim;
+       int iprim = rs->iprim;
+       uint16_t *alpha_to = rs->alpha_to;
+       uint16_t *index_of = rs->index_of;
+       uint16_t u, q, tmp, num1, num2, den, discr_r, syn_error;
+       /* Err+Eras Locator poly and syndrome poly The maximum value
+        * of nroots is 8. So the necessary stack size will be about
+        * 220 bytes max.
+        */
+       uint16_t lambda[nroots + 1], syn[nroots];
+       uint16_t b[nroots + 1], t[nroots + 1], omega[nroots + 1];
+       uint16_t root[nroots], reg[nroots + 1], loc[nroots];
+       int count = 0;
+       uint16_t msk = (uint16_t) rs->nn;
+
+       /* Check length parameter for validity */
+       pad = nn - nroots - len;
+       if (pad < 0 || pad >= nn)
+               return -ERANGE;
+               
+       /* Does the caller provide the syndrome ? */
+       if (s != NULL) 
+               goto decode;
+
+       /* form the syndromes; i.e., evaluate data(x) at roots of
+        * g(x) */
+       for (i = 0; i < nroots; i++)
+               syn[i] = (((uint16_t) data[0]) ^ invmsk) & msk;
+
+       for (j = 1; j < len; j++) {
+               for (i = 0; i < nroots; i++) {
+                       if (syn[i] == 0) {
+                               syn[i] = (((uint16_t) data[j]) ^ 
+                                         invmsk) & msk;
+                       } else {
+                               syn[i] = ((((uint16_t) data[j]) ^
+                                          invmsk) & msk) ^ 
+                                       alpha_to[rs_modnn(rs, index_of[syn[i]] +
+                                                      (fcr + i) * prim)];
+                       }
+               }
+       }
+
+       for (j = 0; j < nroots; j++) {
+               for (i = 0; i < nroots; i++) {
+                       if (syn[i] == 0) {
+                               syn[i] = ((uint16_t) par[j]) & msk;
+                       } else {
+                               syn[i] = (((uint16_t) par[j]) & msk) ^ 
+                                       alpha_to[rs_modnn(rs, index_of[syn[i]] +
+                                                      (fcr+i)*prim)];
+                       }
+               }
+       }
+       s = syn;
+
+       /* Convert syndromes to index form, checking for nonzero condition */
+       syn_error = 0;
+       for (i = 0; i < nroots; i++) {
+               syn_error |= s[i];
+               s[i] = index_of[s[i]];
+       }
+
+       if (!syn_error) {
+               /* if syndrome is zero, data[] is a codeword and there are no
+                * errors to correct. So return data[] unmodified
+                */
+               count = 0;
+               goto finish;
+       }
+
+ decode:
+       memset(&lambda[1], 0, nroots * sizeof(lambda[0]));
+       lambda[0] = 1;
+
+       if (no_eras > 0) {
+               /* Init lambda to be the erasure locator polynomial */
+               lambda[1] = alpha_to[rs_modnn(rs, 
+                                             prim * (nn - 1 - eras_pos[0]))];
+               for (i = 1; i < no_eras; i++) {
+                       u = rs_modnn(rs, prim * (nn - 1 - eras_pos[i]));
+                       for (j = i + 1; j > 0; j--) {
+                               tmp = index_of[lambda[j - 1]];
+                               if (tmp != nn) {
+                                       lambda[j] ^= 
+                                               alpha_to[rs_modnn(rs, u + tmp)];
+                               }
+                       }
+               }
+       }
+
+       for (i = 0; i < nroots + 1; i++)
+               b[i] = index_of[lambda[i]];
+
+       /*
+        * Begin Berlekamp-Massey algorithm to determine error+erasure
+        * locator polynomial
+        */
+       r = no_eras;
+       el = no_eras;
+       while (++r <= nroots) { /* r is the step number */
+               /* Compute discrepancy at the r-th step in poly-form */
+               discr_r = 0;
+               for (i = 0; i < r; i++) {
+                       if ((lambda[i] != 0) && (s[r - i - 1] != nn)) {
+                               discr_r ^= 
+                                       alpha_to[rs_modnn(rs, 
+                                                         index_of[lambda[i]] +
+                                                         s[r - i - 1])];
+                       }
+               }
+               discr_r = index_of[discr_r];    /* Index form */
+               if (discr_r == nn) {
+                       /* 2 lines below: B(x) <-- x*B(x) */
+                       memmove (&b[1], b, nroots * sizeof (b[0]));
+                       b[0] = nn;
+               } else {
+                       /* 7 lines below: T(x) <-- lambda(x)-discr_r*x*b(x) */
+                       t[0] = lambda[0];
+                       for (i = 0; i < nroots; i++) {
+                               if (b[i] != nn) {
+                                       t[i + 1] = lambda[i + 1] ^ 
+                                               alpha_to[rs_modnn(rs, discr_r +
+                                                                 b[i])];
+                               } else
+                                       t[i + 1] = lambda[i + 1];
+                       }
+                       if (2 * el <= r + no_eras - 1) {
+                               el = r + no_eras - el;
+                               /*
+                                * 2 lines below: B(x) <-- inv(discr_r) *
+                                * lambda(x)
+                                */
+                               for (i = 0; i <= nroots; i++) {
+                                       b[i] = (lambda[i] == 0) ? nn :
+                                               rs_modnn(rs, index_of[lambda[i]]
+                                                        - discr_r + nn);
+                               }
+                       } else {
+                               /* 2 lines below: B(x) <-- x*B(x) */
+                               memmove(&b[1], b, nroots * sizeof(b[0]));
+                               b[0] = nn;
+                       }
+                       memcpy(lambda, t, (nroots + 1) * sizeof(t[0]));
+               }
+       }
+
+       /* Convert lambda to index form and compute deg(lambda(x)) */
+       deg_lambda = 0;
+       for (i = 0; i < nroots + 1; i++) {
+               lambda[i] = index_of[lambda[i]];
+               if (lambda[i] != nn)
+                       deg_lambda = i;
+       }
+       /* Find roots of error+erasure locator polynomial by Chien search */
+       memcpy(&reg[1], &lambda[1], nroots * sizeof(reg[0]));
+       count = 0;              /* Number of roots of lambda(x) */
+       for (i = 1, k = iprim - 1; i <= nn; i++, k = rs_modnn(rs, k + iprim)) {
+               q = 1;          /* lambda[0] is always 0 */
+               for (j = deg_lambda; j > 0; j--) {
+                       if (reg[j] != nn) {
+                               reg[j] = rs_modnn(rs, reg[j] + j);
+                               q ^= alpha_to[reg[j]];
+                       }
+               }
+               if (q != 0)
+                       continue;       /* Not a root */
+               /* store root (index-form) and error location number */
+               root[count] = i;
+               loc[count] = k;
+               /* If we've already found max possible roots,
+                * abort the search to save time
+                */
+               if (++count == deg_lambda)
+                       break;
+       }
+       if (deg_lambda != count) {
+               /*
+                * deg(lambda) unequal to number of roots => uncorrectable
+                * error detected
+                */
+               count = -1;
+               goto finish;
+       }
+       /*
+        * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
+        * x**nroots). in index form. Also find deg(omega).
+        */
+       deg_omega = deg_lambda - 1;
+       for (i = 0; i <= deg_omega; i++) {
+               tmp = 0;
+               for (j = i; j >= 0; j--) {
+                       if ((s[i - j] != nn) && (lambda[j] != nn))
+                               tmp ^=
+                                   alpha_to[rs_modnn(rs, s[i - j] + lambda[j])];
+               }
+               omega[i] = index_of[tmp];
+       }
+
+       /*
+        * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
+        * inv(X(l))**(fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form
+        */
+       for (j = count - 1; j >= 0; j--) {
+               num1 = 0;
+               for (i = deg_omega; i >= 0; i--) {
+                       if (omega[i] != nn)
+                               num1 ^= alpha_to[rs_modnn(rs, omega[i] + 
+                                                       i * root[j])];
+               }
+               num2 = alpha_to[rs_modnn(rs, root[j] * (fcr - 1) + nn)];
+               den = 0;
+
+               /* lambda[i+1] for i even is the formal derivative
+                * lambda_pr of lambda[i] */
+               for (i = min(deg_lambda, nroots - 1) & ~1; i >= 0; i -= 2) {
+                       if (lambda[i + 1] != nn) {
+                               den ^= alpha_to[rs_modnn(rs, lambda[i + 1] + 
+                                                      i * root[j])];
+                       }
+               }
+               /* Apply error to data */
+               if (num1 != 0 && loc[j] >= pad) {
+                       uint16_t cor = alpha_to[rs_modnn(rs,index_of[num1] + 
+                                                      index_of[num2] +
+                                                      nn - index_of[den])];
+                       /* Store the error correction pattern, if a
+                        * correction buffer is available */
+                       if (corr) {
+                               corr[j] = cor;
+                       } else {
+                               /* If a data buffer is given and the
+                                * error is inside the message,
+                                * correct it */
+                               if (data && (loc[j] < (nn - nroots)))
+                                       data[loc[j] - pad] ^= cor;
+                       }
+               }
+       }
+
+finish:
+       if (eras_pos != NULL) {
+               for (i = 0; i < count; i++)
+                       eras_pos[i] = loc[i] - pad;
+       }
+       return count;
+
+}
diff --git a/lib/reed_solomon/encode_rs.c b/lib/reed_solomon/encode_rs.c
new file mode 100644 (file)
index 0000000..237bf65
--- /dev/null
@@ -0,0 +1,54 @@
+/* 
+ * lib/reed_solomon/encode_rs.c
+ *
+ * Overview:
+ *   Generic Reed Solomon encoder / decoder library
+ *   
+ * Copyright 2002, Phil Karn, KA9Q
+ * May be used under the terms of the GNU General Public License (GPL)
+ *
+ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
+ *
+ * $Id: encode_rs.c,v 1.4 2004/10/22 15:41:47 gleixner Exp $
+ *
+ */
+
+/* Generic data width independent code which is included by the 
+ * wrappers.
+ * int encode_rsX (struct rs_control *rs, uintX_t *data, int len, uintY_t *par)
+ */
+{
+       int i, j, pad;
+       int nn = rs->nn;
+       int nroots = rs->nroots;
+       uint16_t *alpha_to = rs->alpha_to;
+       uint16_t *index_of = rs->index_of;
+       uint16_t *genpoly = rs->genpoly;
+       uint16_t fb;
+       uint16_t msk = (uint16_t) rs->nn;
+
+       /* Check length parameter for validity */
+       pad = nn - nroots - len;
+       if (pad < 0 || pad >= nn)
+               return -ERANGE;
+
+       for (i = 0; i < len; i++) {
+               fb = index_of[((((uint16_t) data[i])^invmsk) & msk) ^ par[0]];
+               /* feedback term is non-zero */
+               if (fb != nn) { 
+                       for (j = 1; j < nroots; j++) {
+                               par[j] ^= alpha_to[rs_modnn(rs, fb + 
+                                                        genpoly[nroots - j])];
+                       }
+               }
+               /* Shift */
+               memmove(&par[0], &par[1], sizeof(uint16_t) * (nroots - 1));
+               if (fb != nn) {
+                       par[nroots - 1] = alpha_to[rs_modnn(rs, 
+                                                           fb + genpoly[0])];
+               } else {
+                       par[nroots - 1] = 0;
+               }
+       }
+       return 0;
+}
diff --git a/lib/reed_solomon/reed_solomon.c b/lib/reed_solomon/reed_solomon.c
new file mode 100644 (file)
index 0000000..87d4259
--- /dev/null
@@ -0,0 +1,335 @@
+/* 
+ * lib/reed_solomon/rslib.c
+ *
+ * Overview:
+ *   Generic Reed Solomon encoder / decoder library
+ *   
+ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * Reed Solomon code lifted from reed solomon library written by Phil Karn
+ * Copyright 2002 Phil Karn, KA9Q
+ *
+ * $Id: rslib.c,v 1.4 2004/10/05 22:07:53 gleixner Exp $
+ *
+ * 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.
+ *
+ * Description:
+ *     
+ * The generic Reed Solomon library provides runtime configurable
+ * encoding / decoding of RS codes.
+ * Each user must call init_rs to get a pointer to a rs_control
+ * structure for the given rs parameters. This structure is either
+ * generated or a already available matching control structure is used.
+ * If a structure is generated then the polynominal arrays for
+ * fast encoding / decoding are built. This can take some time so
+ * make sure not to call this function from a timecritical path.
+ * Usually a module / driver should initialize the neccecary 
+ * rs_control structure on module / driver init and release it
+ * on exit.
+ * The encoding puts the calculated syndrome into a given syndrom 
+ * buffer. 
+ * The decoding is a two step process. The first step calculates
+ * the syndrome over the received (data + syndrom) and calls the
+ * second stage, which does the decoding / error correction itself.
+ * Many hw encoders provide a syndrom calculation over the received
+ * data + syndrom and can call the second stage directly.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/rslib.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+
+/* This list holds all currently allocated rs control structures */
+static LIST_HEAD (rslist);
+/* Protection for the list */
+static DECLARE_MUTEX(rslistlock);
+
+/** 
+ * rs_init - Initialize a Reed-Solomon codec
+ *
+ * @symsize:   symbol size, bits (1-8)
+ * @gfpoly:    Field generator polynomial coefficients
+ * @fcr:       first root of RS code generator polynomial, index form
+ * @prim:      primitive element to generate polynomial roots
+ * @nroots:    RS code generator polynomial degree (number of roots)
+ *
+ * Allocate a control structure and the polynom arrays for faster
+ * en/decoding. Fill the arrays according to the given parameters
+ */
+static struct rs_control *rs_init(int symsize, int gfpoly, int fcr, 
+                                  int prim, int nroots)
+{
+       struct rs_control *rs;
+       int i, j, sr, root, iprim;
+
+       /* Allocate the control structure */
+       rs = kmalloc(sizeof (struct rs_control), GFP_KERNEL);
+       if (rs == NULL)
+               return NULL;
+
+       INIT_LIST_HEAD(&rs->list);
+
+       rs->mm = symsize;
+       rs->nn = (1 << symsize) - 1;
+       rs->fcr = fcr;
+       rs->prim = prim;
+       rs->nroots = nroots;
+       rs->gfpoly = gfpoly;
+
+       /* Allocate the arrays */
+       rs->alpha_to = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
+       if (rs->alpha_to == NULL)
+               goto errrs;
+
+       rs->index_of = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
+       if (rs->index_of == NULL)
+               goto erralp;
+
+       rs->genpoly = kmalloc(sizeof(uint16_t) * (rs->nroots + 1), GFP_KERNEL);
+       if(rs->genpoly == NULL)
+               goto erridx;
+
+       /* Generate Galois field lookup tables */
+       rs->index_of[0] = rs->nn;       /* log(zero) = -inf */
+       rs->alpha_to[rs->nn] = 0;       /* alpha**-inf = 0 */
+       sr = 1;
+       for (i = 0; i < rs->nn; i++) {
+               rs->index_of[sr] = i;
+               rs->alpha_to[i] = sr;
+               sr <<= 1;
+               if (sr & (1 << symsize))
+                       sr ^= gfpoly;
+               sr &= rs->nn;
+       }
+       /* If it's not primitive, exit */
+       if(sr != 1)
+               goto errpol;
+
+       /* Find prim-th root of 1, used in decoding */
+       for(iprim = 1; (iprim % prim) != 0; iprim += rs->nn);
+       /* prim-th root of 1, index form */
+       rs->iprim = iprim / prim;
+
+       /* Form RS code generator polynomial from its roots */
+       rs->genpoly[0] = 1;
+       for (i = 0, root = fcr * prim; i < nroots; i++, root += prim) {
+               rs->genpoly[i + 1] = 1;
+               /* Multiply rs->genpoly[] by  @**(root + x) */
+               for (j = i; j > 0; j--) {
+                       if (rs->genpoly[j] != 0) {
+                               rs->genpoly[j] = rs->genpoly[j -1] ^ 
+                                       rs->alpha_to[rs_modnn(rs, 
+                                       rs->index_of[rs->genpoly[j]] + root)];
+                       } else
+                               rs->genpoly[j] = rs->genpoly[j - 1];
+               }
+               /* rs->genpoly[0] can never be zero */
+               rs->genpoly[0] = 
+                       rs->alpha_to[rs_modnn(rs, 
+                               rs->index_of[rs->genpoly[0]] + root)];
+       }
+       /* convert rs->genpoly[] to index form for quicker encoding */
+       for (i = 0; i <= nroots; i++)
+               rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
+       return rs;
+
+       /* Error exit */
+errpol:
+       kfree(rs->genpoly);
+erridx:
+       kfree(rs->index_of);
+erralp:
+       kfree(rs->alpha_to);
+errrs:
+       kfree(rs);
+       return NULL;
+}
+
+
+/** 
+ *  free_rs - Free the rs control structure, if its not longer used
+ *
+ *  @rs:       the control structure which is not longer used by the
+ *             caller
+ */
+void free_rs(struct rs_control *rs)
+{
+       down(&rslistlock);
+       rs->users--;
+       if(!rs->users) {
+               list_del(&rs->list);
+               kfree(rs->alpha_to);
+               kfree(rs->index_of);
+               kfree(rs->genpoly);
+               kfree(rs);
+       }
+       up(&rslistlock);
+}
+
+/** 
+ * init_rs - Find a matching or allocate a new rs control structure
+ *
+ *  @symsize:  the symbol size (number of bits)
+ *  @gfpoly:   the extended Galois field generator polynomial coefficients,
+ *             with the 0th coefficient in the low order bit. The polynomial
+ *             must be primitive;
+ *  @fcr:      the first consecutive root of the rs code generator polynomial 
+ *             in index form
+ *  @prim:     primitive element to generate polynomial roots
+ *  @nroots:   RS code generator polynomial degree (number of roots)
+ */
+struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, 
+                          int nroots)
+{
+       struct list_head        *tmp;
+       struct rs_control       *rs;
+
+       /* Sanity checks */
+       if (symsize < 1)
+               return NULL;
+       if (fcr < 0 || fcr >= (1<<symsize))
+               return NULL;
+       if (prim <= 0 || prim >= (1<<symsize))
+               return NULL;
+       if (nroots < 0 || nroots >= (1<<symsize) || nroots > 8)
+               return NULL;
+       
+       down(&rslistlock);
+
+       /* Walk through the list and look for a matching entry */
+       list_for_each(tmp, &rslist) {
+               rs = list_entry(tmp, struct rs_control, list);
+               if (symsize != rs->mm)
+                       continue;
+               if (gfpoly != rs->gfpoly)
+                       continue;
+               if (fcr != rs->fcr)
+                       continue;       
+               if (prim != rs->prim)
+                       continue;       
+               if (nroots != rs->nroots)
+                       continue;
+               /* We have a matching one already */
+               rs->users++;
+               goto out;
+       }
+
+       /* Create a new one */
+       rs = rs_init(symsize, gfpoly, fcr, prim, nroots);
+       if (rs) {
+               rs->users = 1;
+               list_add(&rs->list, &rslist);
+       }
+out:   
+       up(&rslistlock);
+       return rs;
+}
+
+#ifdef CONFIG_REED_SOLOMON_ENC8
+/** 
+ *  encode_rs8 - Calculate the parity for data values (8bit data width)
+ *
+ *  @rs:       the rs control structure
+ *  @data:     data field of a given type
+ *  @len:      data length 
+ *  @par:      parity data, must be initialized by caller (usually all 0)
+ *  @invmsk:   invert data mask (will be xored on data)
+ *
+ *  The parity uses a uint16_t data type to enable
+ *  symbol size > 8. The calling code must take care of encoding of the
+ *  syndrome result for storage itself.
+ */
+int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par, 
+              uint16_t invmsk)
+{
+#include "encode_rs.c"
+}
+EXPORT_SYMBOL_GPL(encode_rs8);
+#endif
+
+#ifdef CONFIG_REED_SOLOMON_DEC8
+/** 
+ *  decode_rs8 - Decode codeword (8bit data width)
+ *
+ *  @rs:       the rs control structure
+ *  @data:     data field of a given type
+ *  @par:      received parity data field
+ *  @len:      data length
+ *  @s:                syndrome data field (if NULL, syndrome is calculated)
+ *  @no_eras:  number of erasures
+ *  @eras_pos: position of erasures, can be NULL
+ *  @invmsk:   invert data mask (will be xored on data, not on parity!)
+ *  @corr:     buffer to store correction bitmask on eras_pos
+ *
+ *  The syndrome and parity uses a uint16_t data type to enable
+ *  symbol size > 8. The calling code must take care of decoding of the
+ *  syndrome result and the received parity before calling this code.
+ */
+int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len,
+              uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, 
+              uint16_t *corr)
+{
+#include "decode_rs.c"
+}
+EXPORT_SYMBOL_GPL(decode_rs8);
+#endif
+
+#ifdef CONFIG_REED_SOLOMON_ENC16
+/**
+ *  encode_rs16 - Calculate the parity for data values (16bit data width)
+ *
+ *  @rs:       the rs control structure
+ *  @data:     data field of a given type
+ *  @len:      data length 
+ *  @par:      parity data, must be initialized by caller (usually all 0)
+ *  @invmsk:   invert data mask (will be xored on data, not on parity!)
+ *
+ *  Each field in the data array contains up to symbol size bits of valid data.
+ */
+int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par, 
+       uint16_t invmsk)
+{
+#include "encode_rs.c"
+}
+EXPORT_SYMBOL_GPL(encode_rs16);
+#endif
+
+#ifdef CONFIG_REED_SOLOMON_DEC16
+/** 
+ *  decode_rs16 - Decode codeword (16bit data width)
+ *
+ *  @rs:       the rs control structure
+ *  @data:     data field of a given type
+ *  @par:      received parity data field
+ *  @len:      data length
+ *  @s:                syndrome data field (if NULL, syndrome is calculated)
+ *  @no_eras:  number of erasures
+ *  @eras_pos: position of erasures, can be NULL
+ *  @invmsk:   invert data mask (will be xored on data, not on parity!) 
+ *  @corr:     buffer to store correction bitmask on eras_pos
+ *
+ *  Each field in the data array contains up to symbol size bits of valid data.
+ */
+int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
+               uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, 
+               uint16_t *corr)
+{
+#include "decode_rs.c"
+}
+EXPORT_SYMBOL_GPL(decode_rs16);
+#endif
+
+EXPORT_SYMBOL_GPL(init_rs);
+EXPORT_SYMBOL_GPL(free_rs);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Reed Solomon encoder/decoder");
+MODULE_AUTHOR("Phil Karn, Thomas Gleixner");
+
diff --git a/mm/internal.h b/mm/internal.h
new file mode 100644 (file)
index 0000000..6bf134e
--- /dev/null
@@ -0,0 +1,13 @@
+/* internal.h: mm/ internal definitions
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/* page_alloc.c */
+extern void set_page_refs(struct page *page, int order);
diff --git a/net/appletalk/dev.c b/net/appletalk/dev.c
new file mode 100644 (file)
index 0000000..7659844
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Moved here from drivers/net/net_init.c, which is:
+ *     Written 1993,1994,1995 by Donald Becker.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ltalk.h>
+
+static int ltalk_change_mtu(struct net_device *dev, int mtu)
+{
+       return -EINVAL;
+}
+
+static int ltalk_mac_addr(struct net_device *dev, void *addr)
+{      
+       return -EINVAL;
+}
+
+void ltalk_setup(struct net_device *dev)
+{
+       /* Fill in the fields of the device structure with localtalk-generic values. */
+       
+       dev->change_mtu         = ltalk_change_mtu;
+       dev->hard_header        = NULL;
+       dev->rebuild_header     = NULL;
+       dev->set_mac_address    = ltalk_mac_addr;
+       dev->hard_header_cache  = NULL;
+       dev->header_cache_update= NULL;
+
+       dev->type               = ARPHRD_LOCALTLK;
+       dev->hard_header_len    = LTALK_HLEN;
+       dev->mtu                = LTALK_MTU;
+       dev->addr_len           = LTALK_ALEN;
+       dev->tx_queue_len       = 10;   
+       
+       dev->broadcast[0]       = 0xFF;
+
+       dev->flags              = IFF_BROADCAST|IFF_MULTICAST|IFF_NOARP;
+}
+EXPORT_SYMBOL(ltalk_setup);
diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c
new file mode 100644 (file)
index 0000000..01af4fc
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * netfilter module for userspace bridged Ethernet frames logging daemons
+ *
+ *     Authors:
+ *     Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  November, 2004
+ *
+ * Based on ipt_ULOG.c, which is
+ * (C) 2000-2002 by Harald Welte <laforge@netfilter.org>
+ *
+ * This module accepts two parameters: 
+ * 
+ * nlbufsiz:
+ *   The parameter specifies how big the buffer for each netlink multicast
+ * group is. e.g. If you say nlbufsiz=8192, up to eight kb of packets will
+ * get accumulated in the kernel until they are sent to userspace. It is
+ * NOT possible to allocate more than 128kB, and it is strongly discouraged,
+ * because atomically allocating 128kB inside the network rx softirq is not
+ * reliable. Please also keep in mind that this buffer size is allocated for
+ * each nlgroup you are using, so the total kernel memory usage increases
+ * by that factor.
+ *
+ * flushtimeout:
+ *   Specify, after how many hundredths of a second the queue should be
+ *   flushed even if it is not full yet.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/spinlock.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/netlink.h>
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter_bridge/ebt_ulog.h>
+#include <net/sock.h>
+#include "../br_private.h"
+
+#define PRINTR(format, args...) do { if (net_ratelimit()) \
+                                printk(format , ## args); } while (0)
+
+static unsigned int nlbufsiz = 4096;
+module_param(nlbufsiz, uint, 0600);
+MODULE_PARM_DESC(nlbufsiz, "netlink buffer size (number of bytes) "
+                           "(defaults to 4096)");
+
+static unsigned int flushtimeout = 10;
+module_param(flushtimeout, uint, 0600);
+MODULE_PARM_DESC(flushtimeout, "buffer flush timeout (hundredths ofa second) "
+                               "(defaults to 10)");
+
+typedef struct {
+       unsigned int qlen;              /* number of nlmsgs' in the skb */
+       struct nlmsghdr *lastnlh;       /* netlink header of last msg in skb */
+       struct sk_buff *skb;            /* the pre-allocated skb */
+       struct timer_list timer;        /* the timer function */
+       spinlock_t lock;                /* the per-queue lock */
+} ebt_ulog_buff_t;
+
+static ebt_ulog_buff_t ulog_buffers[EBT_ULOG_MAXNLGROUPS];
+static struct sock *ebtulognl;
+
+/* send one ulog_buff_t to userspace */
+static void ulog_send(unsigned int nlgroup)
+{
+       ebt_ulog_buff_t *ub = &ulog_buffers[nlgroup];
+
+       if (timer_pending(&ub->timer))
+               del_timer(&ub->timer);
+
+       /* last nlmsg needs NLMSG_DONE */
+       if (ub->qlen > 1)
+               ub->lastnlh->nlmsg_type = NLMSG_DONE;
+
+       NETLINK_CB(ub->skb).dst_groups = 1 << nlgroup;
+       netlink_broadcast(ebtulognl, ub->skb, 0, 1 << nlgroup, GFP_ATOMIC);
+
+       ub->qlen = 0;
+       ub->skb = NULL;
+}
+
+/* timer function to flush queue in flushtimeout time */
+static void ulog_timer(unsigned long data)
+{
+       spin_lock_bh(&ulog_buffers[data].lock);
+       if (ulog_buffers[data].skb)
+               ulog_send(data);
+       spin_unlock_bh(&ulog_buffers[data].lock);
+}
+
+static struct sk_buff *ulog_alloc_skb(unsigned int size)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(nlbufsiz, GFP_ATOMIC);
+       if (!skb) {
+               PRINTR(KERN_ERR "ebt_ulog: can't alloc whole buffer "
+                      "of size %ub!\n", nlbufsiz);
+               if (size < nlbufsiz) {
+                       /* try to allocate only as much as we need for
+                        * current packet */
+                       skb = alloc_skb(size, GFP_ATOMIC);
+                       if (!skb)
+                               PRINTR(KERN_ERR "ebt_ulog: can't even allocate "
+                                      "buffer of size %ub\n", size);
+               }
+       }
+
+       return skb;
+}
+
+static void ebt_ulog(const struct sk_buff *skb, unsigned int hooknr,
+   const struct net_device *in, const struct net_device *out,
+   const void *data, unsigned int datalen)
+{
+       ebt_ulog_packet_msg_t *pm;
+       size_t size, copy_len;
+       struct nlmsghdr *nlh;
+       struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)data;
+       unsigned int group = uloginfo->nlgroup;
+       ebt_ulog_buff_t *ub = &ulog_buffers[group];
+       spinlock_t *lock = &ub->lock;
+
+       if ((uloginfo->cprange == 0) ||
+           (uloginfo->cprange > skb->len + ETH_HLEN))
+               copy_len = skb->len + ETH_HLEN;
+       else
+               copy_len = uloginfo->cprange;
+
+       size = NLMSG_SPACE(sizeof(*pm) + copy_len);
+       if (size > nlbufsiz) {
+               PRINTR("ebt_ulog: Size %Zd needed, but nlbufsiz=%d\n",
+                      size, nlbufsiz);
+               return;
+       }
+
+       spin_lock_bh(lock);
+
+       if (!ub->skb) {
+               if (!(ub->skb = ulog_alloc_skb(size)))
+                       goto alloc_failure;
+       } else if (size > skb_tailroom(ub->skb)) {
+               ulog_send(group);
+
+               if (!(ub->skb = ulog_alloc_skb(size)))
+                       goto alloc_failure;
+       }
+
+       nlh = NLMSG_PUT(ub->skb, 0, ub->qlen, 0,
+                       size - NLMSG_ALIGN(sizeof(*nlh)));
+       ub->qlen++;
+
+       pm = NLMSG_DATA(nlh);
+
+       /* Fill in the ulog data */
+       pm->version = EBT_ULOG_VERSION;
+       do_gettimeofday(&pm->stamp);
+       if (ub->qlen == 1)
+               ub->skb->stamp = pm->stamp;
+       pm->data_len = copy_len;
+       pm->mark = skb->nfmark;
+       pm->hook = hooknr;
+       if (uloginfo->prefix != NULL)
+               strcpy(pm->prefix, uloginfo->prefix);
+       else
+               *(pm->prefix) = '\0';
+
+       if (in) {
+               strcpy(pm->physindev, in->name);
+               /* If in isn't a bridge, then physindev==indev */
+               if (in->br_port)
+                       strcpy(pm->indev, in->br_port->br->dev->name);
+               else
+                       strcpy(pm->indev, in->name);
+       } else
+               pm->indev[0] = pm->physindev[0] = '\0';
+
+       if (out) {
+               /* If out exists, then out is a bridge port */
+               strcpy(pm->physoutdev, out->name);
+               strcpy(pm->outdev, out->br_port->br->dev->name);
+       } else
+               pm->outdev[0] = pm->physoutdev[0] = '\0';
+
+       if (skb_copy_bits(skb, -ETH_HLEN, pm->data, copy_len) < 0)
+               BUG();
+
+       if (ub->qlen > 1)
+               ub->lastnlh->nlmsg_flags |= NLM_F_MULTI;
+
+       ub->lastnlh = nlh;
+
+       if (ub->qlen >= uloginfo->qthreshold)
+               ulog_send(group);
+       else if (!timer_pending(&ub->timer)) {
+               ub->timer.expires = jiffies + flushtimeout * HZ / 100;
+               add_timer(&ub->timer);
+       }
+
+unlock:
+       spin_unlock_bh(lock);
+
+       return;
+
+nlmsg_failure:
+       printk(KERN_CRIT "ebt_ulog: error during NLMSG_PUT. This should "
+              "not happen, please report to author.\n");
+       goto unlock;
+alloc_failure:
+       goto unlock;
+}
+
+static int ebt_ulog_check(const char *tablename, unsigned int hookmask,
+   const struct ebt_entry *e, void *data, unsigned int datalen)
+{
+       struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)data;
+
+       if (datalen != EBT_ALIGN(sizeof(struct ebt_ulog_info)) ||
+           uloginfo->nlgroup > 31)
+               return -EINVAL;
+
+       uloginfo->prefix[EBT_ULOG_PREFIX_LEN - 1] = '\0';
+
+       if (uloginfo->qthreshold > EBT_ULOG_MAX_QLEN)
+               uloginfo->qthreshold = EBT_ULOG_MAX_QLEN;
+
+       return 0;
+}
+
+static struct ebt_watcher ulog = {
+       .name           = EBT_ULOG_WATCHER,
+       .watcher        = ebt_ulog,
+       .check          = ebt_ulog_check,
+       .me             = THIS_MODULE,
+};
+
+static int __init init(void)
+{
+       int i, ret = 0;
+
+       if (nlbufsiz >= 128*1024) {
+               printk(KERN_NOTICE "ebt_ulog: Netlink buffer has to be <= 128kB,"
+                      " please try a smaller nlbufsiz parameter.\n");
+               return -EINVAL;
+       }
+
+       /* initialize ulog_buffers */
+       for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
+               init_timer(&ulog_buffers[i].timer);
+               ulog_buffers[i].timer.function = ulog_timer;
+               ulog_buffers[i].timer.data = i;
+               spin_lock_init(&ulog_buffers[i].lock);
+       }
+
+       ebtulognl = netlink_kernel_create(NETLINK_NFLOG, NULL);
+       if (!ebtulognl)
+               ret = -ENOMEM;
+       else if ((ret = ebt_register_watcher(&ulog)))
+               sock_release(ebtulognl->sk_socket);
+
+       return ret;
+}
+
+static void __exit fini(void)
+{
+       ebt_ulog_buff_t *ub;
+       int i;
+
+       ebt_unregister_watcher(&ulog);
+       for (i = 0; i < EBT_ULOG_MAXNLGROUPS; i++) {
+               ub = &ulog_buffers[i];
+               if (timer_pending(&ub->timer))
+                       del_timer(&ub->timer);
+               spin_lock_bh(&ub->lock);
+               if (ub->skb) {
+                       kfree_skb(ub->skb);
+                       ub->skb = NULL;
+               }
+               spin_unlock_bh(&ub->lock);
+       }
+       sock_release(ebtulognl->sk_socket);
+}
+
+module_init(init);
+module_exit(fini);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bart De Schuymer <bdschuym@pandora.be>");
+MODULE_DESCRIPTION("ebtables userspace logging module for bridged Ethernet"
+                   " frames");
diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
new file mode 100644 (file)
index 0000000..68002ff
--- /dev/null
@@ -0,0 +1,760 @@
+/* Cluster IP hashmark target 
+ * (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
+ * based on ideas of Fabio Olive Leite <olive@unixforge.org>
+ *
+ * Development of this code funded by SuSE Linux AG, http://www.suse.com/
+ *
+ * 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 <linux/module.h>
+#include <linux/config.h>
+#include <linux/proc_fs.h>
+#include <linux/jhash.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/icmp.h>
+#include <linux/if_arp.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <net/checksum.h>
+
+#include <linux/netfilter_arp.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_CLUSTERIP.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+
+#define CLUSTERIP_VERSION "0.6"
+
+#define DEBUG_CLUSTERIP
+
+#ifdef DEBUG_CLUSTERIP
+#define DEBUGP printk
+#else
+#define DEBUGP
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
+MODULE_DESCRIPTION("iptables target for CLUSTERIP");
+
+struct clusterip_config {
+       struct list_head list;                  /* list of all configs */
+       atomic_t refcount;                      /* reference count */
+
+       u_int32_t clusterip;                    /* the IP address */
+       u_int8_t clustermac[ETH_ALEN];          /* the MAC address */
+       struct net_device *dev;                 /* device */
+       u_int16_t num_total_nodes;              /* total number of nodes */
+       u_int16_t num_local_nodes;              /* number of local nodes */
+       u_int16_t local_nodes[CLUSTERIP_MAX_NODES];     /* node number array */
+
+#ifdef CONFIG_PROC_FS
+       struct proc_dir_entry *pde;             /* proc dir entry */
+#endif
+       enum clusterip_hashmode hash_mode;      /* which hashing mode */
+       u_int32_t hash_initval;                 /* hash initialization */
+};
+
+static LIST_HEAD(clusterip_configs);
+
+/* clusterip_lock protects the clusterip_configs list _AND_ the configurable
+ * data within all structurses (num_local_nodes, local_nodes[]) */
+DECLARE_RWLOCK(clusterip_lock);
+
+#ifdef CONFIG_PROC_FS
+static struct file_operations clusterip_proc_fops;
+static struct proc_dir_entry *clusterip_procdir;
+#endif
+
+static inline void
+clusterip_config_get(struct clusterip_config *c) {
+       atomic_inc(&c->refcount);
+}
+
+static inline void
+clusterip_config_put(struct clusterip_config *c) {
+       if (atomic_dec_and_test(&c->refcount)) {
+               WRITE_LOCK(&clusterip_lock);
+               list_del(&c->list);
+               WRITE_UNLOCK(&clusterip_lock);
+               dev_mc_delete(c->dev, c->clustermac, ETH_ALEN, 0);
+               dev_put(c->dev);
+               kfree(c);
+       }
+}
+
+
+static struct clusterip_config *
+__clusterip_config_find(u_int32_t clusterip)
+{
+       struct list_head *pos;
+
+       MUST_BE_READ_LOCKED(&clusterip_lock);
+       list_for_each(pos, &clusterip_configs) {
+               struct clusterip_config *c = list_entry(pos, 
+                                       struct clusterip_config, list);
+               if (c->clusterip == clusterip) {
+                       return c;
+               }
+       }
+
+       return NULL;
+}
+
+static inline struct clusterip_config *
+clusterip_config_find_get(u_int32_t clusterip)
+{
+       struct clusterip_config *c;
+
+       READ_LOCK(&clusterip_lock);
+       c = __clusterip_config_find(clusterip);
+       if (!c) {
+               READ_UNLOCK(&clusterip_lock);
+               return NULL;
+       }
+       atomic_inc(&c->refcount);
+       READ_UNLOCK(&clusterip_lock);
+
+       return c;
+}
+
+static struct clusterip_config *
+clusterip_config_init(struct ipt_clusterip_tgt_info *i, u_int32_t ip,
+                       struct net_device *dev)
+{
+       struct clusterip_config *c;
+       char buffer[16];
+
+       c = kmalloc(sizeof(*c), GFP_ATOMIC);
+       if (!c)
+               return NULL;
+
+       memset(c, 0, sizeof(*c));
+       c->dev = dev;
+       c->clusterip = ip;
+       memcpy(&c->clustermac, &i->clustermac, ETH_ALEN);
+       c->num_total_nodes = i->num_total_nodes;
+       c->num_local_nodes = i->num_local_nodes;
+       memcpy(&c->local_nodes, &i->local_nodes, sizeof(&c->local_nodes));
+       c->hash_mode = i->hash_mode;
+       c->hash_initval = i->hash_initval;
+       atomic_set(&c->refcount, 1);
+
+#ifdef CONFIG_PROC_FS
+       /* create proc dir entry */
+       sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(ip));
+       c->pde = create_proc_entry(buffer, S_IWUSR|S_IRUSR, clusterip_procdir);
+       if (!c->pde) {
+               kfree(c);
+               return NULL;
+       }
+       c->pde->proc_fops = &clusterip_proc_fops;
+       c->pde->data = c;
+#endif
+
+       WRITE_LOCK(&clusterip_lock);
+       list_add(&c->list, &clusterip_configs);
+       WRITE_UNLOCK(&clusterip_lock);
+
+       return c;
+}
+
+static int
+clusterip_add_node(struct clusterip_config *c, u_int16_t nodenum)
+{
+       int i;
+
+       WRITE_LOCK(&clusterip_lock);
+
+       if (c->num_local_nodes >= CLUSTERIP_MAX_NODES
+           || nodenum > CLUSTERIP_MAX_NODES) {
+               WRITE_UNLOCK(&clusterip_lock);
+               return 1;
+       }
+
+       /* check if we alrady have this number in our array */
+       for (i = 0; i < c->num_local_nodes; i++) {
+               if (c->local_nodes[i] == nodenum) {
+                       WRITE_UNLOCK(&clusterip_lock);
+                       return 1;
+               }
+       }
+
+       c->local_nodes[c->num_local_nodes++] = nodenum;
+
+       WRITE_UNLOCK(&clusterip_lock);
+       return 0;
+}
+
+static int
+clusterip_del_node(struct clusterip_config *c, u_int16_t nodenum)
+{
+       int i;
+
+       WRITE_LOCK(&clusterip_lock);
+
+       if (c->num_local_nodes <= 1 || nodenum > CLUSTERIP_MAX_NODES) {
+               WRITE_UNLOCK(&clusterip_lock);
+               return 1;
+       }
+               
+       for (i = 0; i < c->num_local_nodes; i++) {
+               if (c->local_nodes[i] == nodenum) {
+                       int size = sizeof(u_int16_t)*(c->num_local_nodes-(i+1));
+                       memmove(&c->local_nodes[i], &c->local_nodes[i+1], size);
+                       c->num_local_nodes--;
+                       WRITE_UNLOCK(&clusterip_lock);
+                       return 0;
+               }
+       }
+
+       WRITE_UNLOCK(&clusterip_lock);
+       return 1;
+}
+
+static inline u_int32_t
+clusterip_hashfn(struct sk_buff *skb, struct clusterip_config *config)
+{
+       struct iphdr *iph = skb->nh.iph;
+       unsigned long hashval;
+       u_int16_t sport, dport;
+       struct tcphdr *th;
+       struct udphdr *uh;
+       struct icmphdr *ih;
+
+       switch (iph->protocol) {
+       case IPPROTO_TCP:
+               th = (void *)iph+iph->ihl*4;
+               sport = ntohs(th->source);
+               dport = ntohs(th->dest);
+               break;
+       case IPPROTO_UDP:
+               uh = (void *)iph+iph->ihl*4;
+               sport = ntohs(uh->source);
+               dport = ntohs(uh->dest);
+               break;
+       case IPPROTO_ICMP:
+               ih = (void *)iph+iph->ihl*4;
+               sport = ntohs(ih->un.echo.id);
+               dport = (ih->type<<8)|ih->code;
+               break;
+       default:
+               if (net_ratelimit()) {
+                       printk(KERN_NOTICE "CLUSTERIP: unknown protocol `%u'\n",
+                               iph->protocol);
+               }
+               sport = dport = 0;
+       }
+
+       switch (config->hash_mode) {
+       case CLUSTERIP_HASHMODE_SIP:
+               hashval = jhash_1word(ntohl(iph->saddr),
+                                     config->hash_initval);
+               break;
+       case CLUSTERIP_HASHMODE_SIP_SPT:
+               hashval = jhash_2words(ntohl(iph->saddr), sport, 
+                                      config->hash_initval);
+               break;
+       case CLUSTERIP_HASHMODE_SIP_SPT_DPT:
+               hashval = jhash_3words(ntohl(iph->saddr), sport, dport,
+                                      config->hash_initval);
+               break;
+       default:
+               /* to make gcc happy */
+               hashval = 0;
+               /* This cannot happen, unless the check function wasn't called
+                * at rule load time */
+               printk("CLUSTERIP: unknown mode `%u'\n", config->hash_mode);
+               BUG();
+               break;
+       }
+
+       /* node numbers are 1..n, not 0..n */
+       return ((hashval % config->num_total_nodes)+1);
+}
+
+static inline int
+clusterip_responsible(struct clusterip_config *config, u_int32_t hash)
+{
+       int i;
+
+       READ_LOCK(&clusterip_lock);
+
+       if (config->num_local_nodes == 0) {
+               READ_UNLOCK(&clusterip_lock);
+               return 0;
+       }
+
+       for (i = 0; i < config->num_local_nodes; i++) {
+               if (config->local_nodes[i] == hash) {
+                       READ_UNLOCK(&clusterip_lock);
+                       return 1;
+               }
+       }
+
+       READ_UNLOCK(&clusterip_lock);
+
+       return 0;
+}
+
+/*********************************************************************** 
+ * IPTABLES TARGET 
+ ***********************************************************************/
+
+static unsigned int
+target(struct sk_buff **pskb,
+       const struct net_device *in,
+       const struct net_device *out,
+       unsigned int hooknum,
+       const void *targinfo,
+       void *userinfo)
+{
+       const struct ipt_clusterip_tgt_info *cipinfo = targinfo;
+       enum ip_conntrack_info ctinfo;
+       struct ip_conntrack *ct = ip_conntrack_get((*pskb), &ctinfo);
+       u_int32_t hash;
+
+       /* don't need to clusterip_config_get() here, since refcount
+        * is only decremented by destroy() - and ip_tables guarantees
+        * that the ->target() function isn't called after ->destroy() */
+
+       if (!ct) {
+               printk(KERN_ERR "CLUSTERIP: no conntrack!\n");
+                       /* FIXME: need to drop invalid ones, since replies
+                        * to outgoing connections of other nodes will be 
+                        * marked as INVALID */
+               return NF_DROP;
+       }
+
+       /* special case: ICMP error handling. conntrack distinguishes between
+        * error messages (RELATED) and information requests (see below) */
+       if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP
+           && (ctinfo == IP_CT_RELATED 
+               || ctinfo == IP_CT_IS_REPLY+IP_CT_IS_REPLY))
+               return IPT_CONTINUE;
+
+       /* ip_conntrack_icmp guarantees us that we only have ICMP_ECHO, 
+        * TIMESTAMP, INFO_REQUEST or ADDRESS type icmp packets from here
+        * on, which all have an ID field [relevant for hashing]. */
+
+       hash = clusterip_hashfn(*pskb, cipinfo->config);
+
+       switch (ctinfo) {
+               case IP_CT_NEW:
+                       ct->mark = hash;
+                       break;
+               case IP_CT_RELATED:
+               case IP_CT_RELATED+IP_CT_IS_REPLY:
+                       /* FIXME: we don't handle expectations at the
+                        * moment.  they can arrive on a different node than
+                        * the master connection (e.g. FTP passive mode) */
+               case IP_CT_ESTABLISHED:
+               case IP_CT_ESTABLISHED+IP_CT_IS_REPLY:
+                       break;
+               default:
+                       break;
+       }
+
+#ifdef DEBUG_CLUSTERP
+       DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
+#endif
+       DEBUGP("hash=%u ct_hash=%lu ", hash, ct->mark);
+       if (!clusterip_responsible(cipinfo->config, hash)) {
+               DEBUGP("not responsible\n");
+               return NF_DROP;
+       }
+       DEBUGP("responsible\n");
+
+       /* despite being received via linklayer multicast, this is
+        * actually a unicast IP packet. TCP doesn't like PACKET_MULTICAST */
+       (*pskb)->pkt_type = PACKET_HOST;
+
+       return IPT_CONTINUE;
+}
+
+static int
+checkentry(const char *tablename,
+          const struct ipt_entry *e,
+           void *targinfo,
+           unsigned int targinfosize,
+           unsigned int hook_mask)
+{
+       struct ipt_clusterip_tgt_info *cipinfo = targinfo;
+
+       struct clusterip_config *config;
+
+       if (targinfosize != IPT_ALIGN(sizeof(struct ipt_clusterip_tgt_info))) {
+               printk(KERN_WARNING "CLUSTERIP: targinfosize %u != %Zu\n",
+                      targinfosize,
+                      IPT_ALIGN(sizeof(struct ipt_clusterip_tgt_info)));
+               return 0;
+       }
+
+       if (cipinfo->hash_mode != CLUSTERIP_HASHMODE_SIP &&
+           cipinfo->hash_mode != CLUSTERIP_HASHMODE_SIP_SPT &&
+           cipinfo->hash_mode != CLUSTERIP_HASHMODE_SIP_SPT_DPT) {
+               printk(KERN_WARNING "CLUSTERIP: unknown mode `%u'\n",
+                       cipinfo->hash_mode);
+               return 0;
+
+       }
+       if (e->ip.dmsk.s_addr != 0xffffffff
+           || e->ip.dst.s_addr == 0) {
+               printk(KERN_ERR "CLUSTERIP: Please specify destination IP\n");
+               return 0;
+       }
+
+       /* FIXME: further sanity checks */
+
+       config = clusterip_config_find_get(e->ip.dst.s_addr);
+       if (!config) {
+               if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) {
+                       printk(KERN_WARNING "CLUSTERIP: no config found for %u.%u.%u.%u, need 'new'\n", NIPQUAD(e->ip.dst.s_addr));
+                       return 0;
+               } else {
+                       struct net_device *dev;
+
+                       if (e->ip.iniface[0] == '\0') {
+                               printk(KERN_WARNING "CLUSTERIP: Please specify an interface name\n");
+                               return 0;
+                       }
+
+                       dev = dev_get_by_name(e->ip.iniface);
+                       if (!dev) {
+                               printk(KERN_WARNING "CLUSTERIP: no such interface %s\n", e->ip.iniface);
+                               return 0;
+                       }
+
+                       config = clusterip_config_init(cipinfo, 
+                                                       e->ip.dst.s_addr, dev);
+                       if (!config) {
+                               printk(KERN_WARNING "CLUSTERIP: cannot allocate config\n");
+                               dev_put(dev);
+                               return 0;
+                       }
+                       dev_mc_add(config->dev,config->clustermac, ETH_ALEN, 0);
+               }
+       }
+
+       cipinfo->config = config;
+
+       return 1;
+}
+
+/* drop reference count of cluster config when rule is deleted */
+static void destroy(void *matchinfo, unsigned int matchinfosize)
+{
+       struct ipt_clusterip_tgt_info *cipinfo = matchinfo;
+
+       /* we first remove the proc entry and then drop the reference
+        * count.  In case anyone still accesses the file, the open/close
+        * functions are also incrementing the refcount on their own */
+#ifdef CONFIG_PROC_FS
+       remove_proc_entry(cipinfo->config->pde->name,
+                         cipinfo->config->pde->parent);
+#endif
+       clusterip_config_put(cipinfo->config);
+}
+
+static struct ipt_target clusterip_tgt = { 
+       .name = "CLUSTERIP",
+       .target = &target, 
+       .checkentry = &checkentry, 
+       .destroy = &destroy,
+       .me = THIS_MODULE
+};
+
+
+/*********************************************************************** 
+ * ARP MANGLING CODE 
+ ***********************************************************************/
+
+/* hardcoded for 48bit ethernet and 32bit ipv4 addresses */
+struct arp_payload {
+       u_int8_t src_hw[ETH_ALEN];
+       u_int32_t src_ip;
+       u_int8_t dst_hw[ETH_ALEN];
+       u_int32_t dst_ip;
+} __attribute__ ((packed));
+
+#ifdef CLUSTERIP_DEBUG
+static void arp_print(struct arp_payload *payload) 
+{
+#define HBUFFERLEN 30
+       char hbuffer[HBUFFERLEN];
+       int j,k;
+       const char hexbuf[]= "0123456789abcdef";
+
+       for (k=0, j=0; k < HBUFFERLEN-3 && j < ETH_ALEN; j++) {
+               hbuffer[k++]=hexbuf[(payload->src_hw[j]>>4)&15];
+               hbuffer[k++]=hexbuf[payload->src_hw[j]&15];
+               hbuffer[k++]=':';
+       }
+       hbuffer[--k]='\0';
+
+       printk("src %u.%u.%u.%u@%s, dst %u.%u.%u.%u\n", 
+               NIPQUAD(payload->src_ip), hbuffer,
+               NIPQUAD(payload->dst_ip));
+}
+#endif
+
+static unsigned int
+arp_mangle(unsigned int hook,
+          struct sk_buff **pskb,
+          const struct net_device *in,
+          const struct net_device *out,
+          int (*okfn)(struct sk_buff *))
+{
+       struct arphdr *arp = (*pskb)->nh.arph;
+       struct arp_payload *payload;
+       struct clusterip_config *c;
+
+       /* we don't care about non-ethernet and non-ipv4 ARP */
+       if (arp->ar_hrd != htons(ARPHRD_ETHER)
+           || arp->ar_pro != htons(ETH_P_IP)
+           || arp->ar_pln != 4 || arp->ar_hln != ETH_ALEN)
+               return NF_ACCEPT;
+
+       /* we only want to mangle arp replies */
+       if (arp->ar_op != htons(ARPOP_REPLY))
+               return NF_ACCEPT;
+
+       payload = (void *)(arp+1);
+
+       /* if there is no clusterip configuration for the arp reply's 
+        * source ip, we don't want to mangle it */
+       c = clusterip_config_find_get(payload->src_ip);
+       if (!c)
+               return NF_ACCEPT;
+
+       /* normally the linux kernel always replies to arp queries of 
+        * addresses on different interfacs.  However, in the CLUSTERIP case
+        * this wouldn't work, since we didn't subscribe the mcast group on
+        * other interfaces */
+       if (c->dev != out) {
+               DEBUGP("CLUSTERIP: not mangling arp reply on different "
+                      "interface: cip'%s'-skb'%s'\n", c->dev->name, out->name);
+               clusterip_config_put(c);
+               return NF_ACCEPT;
+       }
+
+       /* mangle reply hardware address */
+       memcpy(payload->src_hw, c->clustermac, arp->ar_hln);
+
+#ifdef CLUSTERIP_DEBUG
+       DEBUGP(KERN_DEBUG "CLUSTERIP mangled arp reply: ");
+       arp_print(payload);
+#endif
+
+       clusterip_config_put(c);
+
+       return NF_ACCEPT;
+}
+
+static struct nf_hook_ops cip_arp_ops = {
+       .hook = arp_mangle,
+       .pf = NF_ARP,
+       .hooknum = NF_ARP_OUT,
+       .priority = -1
+};
+
+/*********************************************************************** 
+ * PROC DIR HANDLING 
+ ***********************************************************************/
+
+#ifdef CONFIG_PROC_FS
+
+static void *clusterip_seq_start(struct seq_file *s, loff_t *pos)
+{
+       struct proc_dir_entry *pde = s->private;
+       struct clusterip_config *c = pde->data;
+       unsigned int *nodeidx;
+
+       READ_LOCK(&clusterip_lock);
+       if (*pos >= c->num_local_nodes)
+               return NULL;
+
+       nodeidx = kmalloc(sizeof(unsigned int), GFP_KERNEL);
+       if (!nodeidx)
+               return ERR_PTR(-ENOMEM);
+
+       *nodeidx = *pos;
+       return nodeidx;
+}
+
+static void *clusterip_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+       struct proc_dir_entry *pde = s->private;
+       struct clusterip_config *c = pde->data;
+       unsigned int *nodeidx = (unsigned int *)v;
+
+       *pos = ++(*nodeidx);
+       if (*pos >= c->num_local_nodes) {
+               kfree(v);
+               return NULL;
+       }
+       return nodeidx;
+}
+
+static void clusterip_seq_stop(struct seq_file *s, void *v)
+{
+       kfree(v);
+
+       READ_UNLOCK(&clusterip_lock);
+}
+
+static int clusterip_seq_show(struct seq_file *s, void *v)
+{
+       struct proc_dir_entry *pde = s->private;
+       struct clusterip_config *c = pde->data;
+       unsigned int *nodeidx = (unsigned int *)v;
+
+       if (*nodeidx != 0) 
+               seq_putc(s, ',');
+       seq_printf(s, "%u", c->local_nodes[*nodeidx]);
+
+       if (*nodeidx == c->num_local_nodes-1)
+               seq_putc(s, '\n');
+
+       return 0;
+}
+
+static struct seq_operations clusterip_seq_ops = {
+       .start  = clusterip_seq_start,
+       .next   = clusterip_seq_next,
+       .stop   = clusterip_seq_stop,
+       .show   = clusterip_seq_show,
+};
+
+static int clusterip_proc_open(struct inode *inode, struct file *file)
+{
+       int ret = seq_open(file, &clusterip_seq_ops);
+
+       if (!ret) {
+               struct seq_file *sf = file->private_data;
+               struct proc_dir_entry *pde = PDE(inode);
+               struct clusterip_config *c = pde->data;
+
+               sf->private = pde;
+
+               clusterip_config_get(c);
+       }
+
+       return ret;
+}
+
+static int clusterip_proc_release(struct inode *inode, struct file *file)
+{
+       struct proc_dir_entry *pde = PDE(inode);
+       struct clusterip_config *c = pde->data;
+       int ret;
+
+       ret = seq_release(inode, file);
+
+       if (!ret)
+               clusterip_config_put(c);
+
+       return ret;
+}
+
+static ssize_t clusterip_proc_write(struct file *file, const char __user *input,
+                               size_t size, loff_t *ofs)
+{
+#define PROC_WRITELEN  10
+       char buffer[PROC_WRITELEN+1];
+       struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
+       struct clusterip_config *c = pde->data;
+       unsigned long nodenum;
+
+       if (copy_from_user(buffer, input, PROC_WRITELEN))
+               return -EFAULT;
+
+       if (*buffer == '+') {
+               nodenum = simple_strtoul(buffer+1, NULL, 10);
+               if (clusterip_add_node(c, nodenum))
+                       return -ENOMEM;
+       } else if (*buffer == '-') {
+               nodenum = simple_strtoul(buffer+1, NULL,10);
+               if (clusterip_del_node(c, nodenum))
+                       return -ENOENT;
+       } else
+               return -EIO;
+
+       return size;
+}
+
+static struct file_operations clusterip_proc_fops = {
+       .owner   = THIS_MODULE,
+       .open    = clusterip_proc_open,
+       .read    = seq_read,
+       .write   = clusterip_proc_write,
+       .llseek  = seq_lseek,
+       .release = clusterip_proc_release,
+};
+
+#endif /* CONFIG_PROC_FS */
+
+static int init_or_cleanup(int fini)
+{
+       int ret;
+
+       if (fini)
+               goto cleanup;
+
+       if (ipt_register_target(&clusterip_tgt)) {
+               ret = -EINVAL;
+               goto cleanup_none;
+       }
+
+       if (nf_register_hook(&cip_arp_ops) < 0) {
+               ret = -EINVAL;
+               goto cleanup_target;
+       }
+
+#ifdef CONFIG_PROC_FS
+       clusterip_procdir = proc_mkdir("ipt_CLUSTERIP", proc_net);
+       if (!clusterip_procdir) {
+               printk(KERN_ERR "CLUSTERIP: Unable to proc dir entry\n");
+               ret = -ENOMEM;
+               goto cleanup_hook;
+       }
+#endif /* CONFIG_PROC_FS */
+
+       printk(KERN_NOTICE "ClusterIP Version %s loaded successfully\n",
+               CLUSTERIP_VERSION);
+
+       return 0;
+
+cleanup:
+       printk(KERN_NOTICE "ClusterIP Version %s unloading\n",
+               CLUSTERIP_VERSION);
+#ifdef CONFIG_PROC_FS
+       remove_proc_entry(clusterip_procdir->name, clusterip_procdir->parent);
+#endif
+cleanup_hook:
+       nf_unregister_hook(&cip_arp_ops);
+cleanup_target:
+       ipt_unregister_target(&clusterip_tgt);
+cleanup_none:
+       return -EINVAL;
+}
+
+static int __init init(void)
+{
+       return init_or_cleanup(0);
+}
+
+static void __exit fini(void)
+{
+       init_or_cleanup(1);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_CONNMARK.c b/net/ipv4/netfilter/ipt_CONNMARK.c
new file mode 100644 (file)
index 0000000..30ddd3e
--- /dev/null
@@ -0,0 +1,118 @@
+/* This kernel module is used to modify the connection mark values, or
+ * to optionally restore the skb nfmark from the connection mark
+ *
+ * Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <net/checksum.h>
+
+MODULE_AUTHOR("Henrik Nordstrom <hno@marasytems.com>");
+MODULE_DESCRIPTION("IP tables CONNMARK matching module");
+MODULE_LICENSE("GPL");
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_CONNMARK.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+
+static unsigned int
+target(struct sk_buff **pskb,
+       const struct net_device *in,
+       const struct net_device *out,
+       unsigned int hooknum,
+       const void *targinfo,
+       void *userinfo)
+{
+       const struct ipt_connmark_target_info *markinfo = targinfo;
+       unsigned long diff;
+       unsigned long nfmark;
+       unsigned long newmark;
+
+       enum ip_conntrack_info ctinfo;
+       struct ip_conntrack *ct = ip_conntrack_get((*pskb), &ctinfo);
+       if (ct) {
+           switch(markinfo->mode) {
+           case IPT_CONNMARK_SET:
+               newmark = (ct->mark & ~markinfo->mask) | markinfo->mark;
+               if (newmark != ct->mark)
+                   ct->mark = newmark;
+               break;
+           case IPT_CONNMARK_SAVE:
+               newmark = (ct->mark & ~markinfo->mask) | ((*pskb)->nfmark & markinfo->mask);
+               if (ct->mark != newmark)
+                   ct->mark = newmark;
+               break;
+           case IPT_CONNMARK_RESTORE:
+               nfmark = (*pskb)->nfmark;
+               diff = (ct->mark ^ nfmark) & markinfo->mask;
+               if (diff != 0) {
+                   (*pskb)->nfmark = nfmark ^ diff;
+                   (*pskb)->nfcache |= NFC_ALTERED;
+               }
+               break;
+           }
+       }
+
+       return IPT_CONTINUE;
+}
+
+static int
+checkentry(const char *tablename,
+          const struct ipt_entry *e,
+          void *targinfo,
+          unsigned int targinfosize,
+          unsigned int hook_mask)
+{
+       struct ipt_connmark_target_info *matchinfo = targinfo;
+       if (targinfosize != IPT_ALIGN(sizeof(struct ipt_connmark_target_info))) {
+               printk(KERN_WARNING "CONNMARK: targinfosize %u != %Zu\n",
+                      targinfosize,
+                      IPT_ALIGN(sizeof(struct ipt_connmark_target_info)));
+               return 0;
+       }
+
+       if (matchinfo->mode == IPT_CONNMARK_RESTORE) {
+           if (strcmp(tablename, "mangle") != 0) {
+                   printk(KERN_WARNING "CONNMARK: restore can only be called from \"mangle\" table, not \"%s\"\n", tablename);
+                   return 0;
+           }
+       }
+
+       return 1;
+}
+
+static struct ipt_target ipt_connmark_reg = {
+       .name = "CONNMARK",
+       .target = &target,
+       .checkentry = &checkentry,
+       .me = THIS_MODULE
+};
+
+static int __init init(void)
+{
+       return ipt_register_target(&ipt_connmark_reg);
+}
+
+static void __exit fini(void)
+{
+       ipt_unregister_target(&ipt_connmark_reg);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_connmark.c b/net/ipv4/netfilter/ipt_connmark.c
new file mode 100644 (file)
index 0000000..2706f96
--- /dev/null
@@ -0,0 +1,81 @@
+/* This kernel module matches connection mark values set by the
+ * CONNMARK target
+ *
+ * Copyright (C) 2002,2004 MARA Systems AB <http://www.marasystems.com>
+ * by Henrik Nordstrom <hno@marasystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+MODULE_AUTHOR("Henrik Nordstrom <hno@marasytems.com>");
+MODULE_DESCRIPTION("IP tables connmark match module");
+MODULE_LICENSE("GPL");
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_connmark.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      int *hotdrop)
+{
+       const struct ipt_connmark_info *info = matchinfo;
+       enum ip_conntrack_info ctinfo;
+       struct ip_conntrack *ct = ip_conntrack_get((struct sk_buff *)skb, &ctinfo);
+       if (!ct)
+               return 0;
+
+       return ((ct->mark & info->mask) == info->mark) ^ info->invert;
+}
+
+static int
+checkentry(const char *tablename,
+          const struct ipt_ip *ip,
+          void *matchinfo,
+          unsigned int matchsize,
+          unsigned int hook_mask)
+{
+       if (matchsize != IPT_ALIGN(sizeof(struct ipt_connmark_info)))
+               return 0;
+
+       return 1;
+}
+
+static struct ipt_match connmark_match = {
+       .name = "connmark",
+       .match = &match,
+       .checkentry = &checkentry,
+       .me = THIS_MODULE
+};
+
+static int __init init(void)
+{
+       return ipt_register_match(&connmark_match);
+}
+
+static void __exit fini(void)
+{
+       ipt_unregister_match(&connmark_match);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/ipv4/netfilter/ipt_hashlimit.c b/net/ipv4/netfilter/ipt_hashlimit.c
new file mode 100644 (file)
index 0000000..04c95d8
--- /dev/null
@@ -0,0 +1,713 @@
+/* iptables match extension to limit the number of packets per second
+ * seperately for each hashbucket (sourceip/sourceport/dstip/dstport)
+ *
+ * (C) 2003-2004 by Harald Welte <laforge@netfilter.org>
+ *
+ * $Id: ipt_hashlimit.c 3244 2004-10-20 16:24:29Z laforge@netfilter.org $
+ *
+ * Development of this code was funded by Astaro AG, http://www.astaro.com/
+ *
+ * based on ipt_limit.c by:
+ * Jérôme de Vivie     <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne      <eychenne@info.enserb.u-bordeaux.fr>
+ * Rusty Russell       <rusty@rustcorp.com.au>
+ *
+ * The general idea is to create a hash table for every dstip and have a
+ * seperate limit counter per tuple.  This way you can do something like 'limit
+ * the number of syn packets for each of my internal addresses.
+ *
+ * Ideally this would just be implemented as a general 'hash' match, which would
+ * allow us to attach any iptables target to it's hash buckets.  But this is
+ * not possible in the current iptables architecture.  As always, pkttables for
+ * 2.7.x will help ;)
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/sctp.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#define ASSERT_READ_LOCK(x) 
+#define ASSERT_WRITE_LOCK(x) 
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <linux/netfilter_ipv4/listhelp.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_hashlimit.h>
+
+/* FIXME: this is just for IP_NF_ASSERRT */
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+
+#define MS2JIFFIES(x) ((x*HZ)/1000)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
+MODULE_DESCRIPTION("iptables match for limiting per hash-bucket");
+
+/* need to declare this at the top */
+static struct proc_dir_entry *hashlimit_procdir;
+static struct file_operations dl_file_ops;
+
+/* hash table crap */
+
+struct dsthash_dst {
+       u_int32_t src_ip;
+       u_int32_t dst_ip;
+       /* ports have to be consecutive !!! */
+       u_int16_t src_port;
+       u_int16_t dst_port;
+};
+
+struct dsthash_ent {
+       /* static / read-only parts in the beginning */
+       struct list_head list;
+       struct dsthash_dst dst;
+
+       /* modified structure members in the end */
+       unsigned long expires;          /* precalculated expiry time */
+       struct {
+               unsigned long prev;     /* last modification */
+               u_int32_t credit;
+               u_int32_t credit_cap, cost;
+       } rateinfo;
+};
+
+struct ipt_hashlimit_htable {
+       struct list_head list;          /* global list of all htables */
+       atomic_t use;
+
+       struct hashlimit_cfg cfg;       /* config */
+
+       /* used internally */
+       spinlock_t lock;                /* lock for list_head */
+       u_int32_t rnd;                  /* random seed for hash */
+       struct timer_list timer;        /* timer for gc */
+       atomic_t count;                 /* number entries in table */
+
+       /* seq_file stuff */
+       struct proc_dir_entry *pde;
+
+       struct list_head hash[0];       /* hashtable itself */
+};
+
+DECLARE_RWLOCK(hashlimit_lock);                /* protects htables list */
+static LIST_HEAD(hashlimit_htables);
+static kmem_cache_t *hashlimit_cachep;
+
+static inline int dst_cmp(const struct dsthash_ent *ent, struct dsthash_dst *b)
+{
+       return (ent->dst.dst_ip == b->dst_ip 
+               && ent->dst.dst_port == b->dst_port
+               && ent->dst.src_port == b->src_port
+               && ent->dst.src_ip == b->src_ip);
+}
+
+static inline u_int32_t
+hash_dst(const struct ipt_hashlimit_htable *ht, const struct dsthash_dst *dst)
+{
+       return (jhash_3words(dst->dst_ip, (dst->dst_port<<16 & dst->src_port), 
+                            dst->src_ip, ht->rnd) % ht->cfg.size);
+}
+
+static inline struct dsthash_ent *
+__dsthash_find(const struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst)
+{
+       struct dsthash_ent *ent;
+       u_int32_t hash = hash_dst(ht, dst);
+       ent = LIST_FIND(&ht->hash[hash], dst_cmp, struct dsthash_ent *, dst);
+       return ent;
+}
+
+/* allocate dsthash_ent, initialize dst, put in htable and lock it */
+static struct dsthash_ent *
+__dsthash_alloc_init(struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst)
+{
+       struct dsthash_ent *ent;
+
+       /* initialize hash with random val at the time we allocate
+        * the first hashtable entry */
+       if (!ht->rnd)
+               get_random_bytes(&ht->rnd, 4);
+
+       if (ht->cfg.max &&
+           atomic_read(&ht->count) >= ht->cfg.max) {
+               /* FIXME: do something. question is what.. */
+               if (net_ratelimit())
+                       printk(KERN_WARNING 
+                               "ipt_hashlimit: max count of %u reached\n", 
+                               ht->cfg.max);
+               return NULL;
+       }
+
+       ent = kmem_cache_alloc(hashlimit_cachep, GFP_ATOMIC);
+       if (!ent) {
+               if (net_ratelimit())
+                       printk(KERN_ERR 
+                               "ipt_hashlimit: can't allocate dsthash_ent\n");
+               return NULL;
+       }
+
+       atomic_inc(&ht->count);
+
+       ent->dst.dst_ip = dst->dst_ip;
+       ent->dst.dst_port = dst->dst_port;
+       ent->dst.src_ip = dst->src_ip;
+       ent->dst.src_port = dst->src_port;
+
+       list_add(&ent->list, &ht->hash[hash_dst(ht, dst)]);
+
+       return ent;
+}
+
+static inline void 
+__dsthash_free(struct ipt_hashlimit_htable *ht, struct dsthash_ent *ent)
+{
+       list_del(&ent->list);
+       kmem_cache_free(hashlimit_cachep, ent);
+       atomic_dec(&ht->count);
+}
+static void htable_gc(unsigned long htlong);
+
+static int htable_create(struct ipt_hashlimit_info *minfo)
+{
+       int i;
+       unsigned int size;
+       struct ipt_hashlimit_htable *hinfo;
+
+       if (minfo->cfg.size)
+               size = minfo->cfg.size;
+       else {
+               size = (((num_physpages << PAGE_SHIFT) / 16384)
+                        / sizeof(struct list_head));
+               if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE))
+                       size = 8192;
+               if (size < 16)
+                       size = 16;
+       }
+       /* FIXME: don't use vmalloc() here or anywhere else -HW */
+       hinfo = vmalloc(sizeof(struct ipt_hashlimit_htable)
+                       + (sizeof(struct list_head) * size));
+       if (!hinfo) {
+               printk(KERN_ERR "ipt_hashlimit: Unable to create hashtable\n");
+               return -1;
+       }
+       minfo->hinfo = hinfo;
+
+       /* copy match config into hashtable config */
+       memcpy(&hinfo->cfg, &minfo->cfg, sizeof(hinfo->cfg));
+       hinfo->cfg.size = size;
+       if (!hinfo->cfg.max)
+               hinfo->cfg.max = 8 * hinfo->cfg.size;
+       else if (hinfo->cfg.max < hinfo->cfg.size)
+               hinfo->cfg.max = hinfo->cfg.size;
+
+       for (i = 0; i < hinfo->cfg.size; i++)
+               INIT_LIST_HEAD(&hinfo->hash[i]);
+
+       atomic_set(&hinfo->count, 0);
+       atomic_set(&hinfo->use, 1);
+       hinfo->rnd = 0;
+       spin_lock_init(&hinfo->lock);
+       hinfo->pde = create_proc_entry(minfo->name, 0, hashlimit_procdir);
+       if (!hinfo->pde) {
+               vfree(hinfo);
+               return -1;
+       }
+       hinfo->pde->proc_fops = &dl_file_ops;
+       hinfo->pde->data = hinfo;
+
+       init_timer(&hinfo->timer);
+       hinfo->timer.expires = jiffies + MS2JIFFIES(hinfo->cfg.gc_interval);
+       hinfo->timer.data = (unsigned long )hinfo;
+       hinfo->timer.function = htable_gc;
+       add_timer(&hinfo->timer);
+
+       WRITE_LOCK(&hashlimit_lock);
+       list_add(&hinfo->list, &hashlimit_htables);
+       WRITE_UNLOCK(&hashlimit_lock);
+
+       return 0;
+}
+
+static int select_all(struct ipt_hashlimit_htable *ht, struct dsthash_ent *he)
+{
+       return 1;
+}
+
+static int select_gc(struct ipt_hashlimit_htable *ht, struct dsthash_ent *he)
+{
+       return (jiffies >= he->expires);
+}
+
+static void htable_selective_cleanup(struct ipt_hashlimit_htable *ht,
+                               int (*select)(struct ipt_hashlimit_htable *ht, 
+                                             struct dsthash_ent *he))
+{
+       int i;
+
+       IP_NF_ASSERT(ht->cfg.size && ht->cfg.max);
+
+       /* lock hash table and iterate over it */
+       spin_lock_bh(&ht->lock);
+       for (i = 0; i < ht->cfg.size; i++) {
+               struct dsthash_ent *dh, *n;
+               list_for_each_entry_safe(dh, n, &ht->hash[i], list) {
+                       if ((*select)(ht, dh))
+                               __dsthash_free(ht, dh);
+               }
+       }
+       spin_unlock_bh(&ht->lock);
+}
+
+/* hash table garbage collector, run by timer */
+static void htable_gc(unsigned long htlong)
+{
+       struct ipt_hashlimit_htable *ht = (struct ipt_hashlimit_htable *)htlong;
+
+       htable_selective_cleanup(ht, select_gc);
+
+       /* re-add the timer accordingly */
+       ht->timer.expires = jiffies + MS2JIFFIES(ht->cfg.gc_interval);
+       add_timer(&ht->timer);
+}
+
+static void htable_destroy(struct ipt_hashlimit_htable *hinfo)
+{
+       /* remove timer, if it is pending */
+       if (timer_pending(&hinfo->timer))
+               del_timer(&hinfo->timer);
+
+       /* remove proc entry */
+       remove_proc_entry(hinfo->pde->name, hashlimit_procdir);
+
+       htable_selective_cleanup(hinfo, select_all);
+       vfree(hinfo);
+}
+
+static struct ipt_hashlimit_htable *htable_find_get(char *name)
+{
+       struct ipt_hashlimit_htable *hinfo;
+
+       READ_LOCK(&hashlimit_lock);
+       list_for_each_entry(hinfo, &hashlimit_htables, list) {
+               if (!strcmp(name, hinfo->pde->name)) {
+                       atomic_inc(&hinfo->use);
+                       READ_UNLOCK(&hashlimit_lock);
+                       return hinfo;
+               }
+       }
+       READ_UNLOCK(&hashlimit_lock);
+
+       return NULL;
+}
+
+static void htable_put(struct ipt_hashlimit_htable *hinfo)
+{
+       if (atomic_dec_and_test(&hinfo->use)) {
+               WRITE_LOCK(&hashlimit_lock);
+               list_del(&hinfo->list);
+               WRITE_UNLOCK(&hashlimit_lock);
+               htable_destroy(hinfo);
+       }
+}
+
+
+/* The algorithm used is the Simple Token Bucket Filter (TBF)
+ * see net/sched/sch_tbf.c in the linux source tree
+ */
+
+/* Rusty: This is my (non-mathematically-inclined) understanding of
+   this algorithm.  The `average rate' in jiffies becomes your initial
+   amount of credit `credit' and the most credit you can ever have
+   `credit_cap'.  The `peak rate' becomes the cost of passing the
+   test, `cost'.
+
+   `prev' tracks the last packet hit: you gain one credit per jiffy.
+   If you get credit balance more than this, the extra credit is
+   discarded.  Every time the match passes, you lose `cost' credits;
+   if you don't have that many, the test fails.
+
+   See Alexey's formal explanation in net/sched/sch_tbf.c.
+
+   To get the maximum range, we multiply by this factor (ie. you get N
+   credits per jiffy).  We want to allow a rate as low as 1 per day
+   (slowest userspace tool allows), which means
+   CREDITS_PER_JIFFY*HZ*60*60*24 < 2^32 ie.
+*/
+#define MAX_CPJ (0xFFFFFFFF / (HZ*60*60*24))
+
+/* Repeated shift and or gives us all 1s, final shift and add 1 gives
+ * us the power of 2 below the theoretical max, so GCC simply does a
+ * shift. */
+#define _POW2_BELOW2(x) ((x)|((x)>>1))
+#define _POW2_BELOW4(x) (_POW2_BELOW2(x)|_POW2_BELOW2((x)>>2))
+#define _POW2_BELOW8(x) (_POW2_BELOW4(x)|_POW2_BELOW4((x)>>4))
+#define _POW2_BELOW16(x) (_POW2_BELOW8(x)|_POW2_BELOW8((x)>>8))
+#define _POW2_BELOW32(x) (_POW2_BELOW16(x)|_POW2_BELOW16((x)>>16))
+#define POW2_BELOW32(x) ((_POW2_BELOW32(x)>>1) + 1)
+
+#define CREDITS_PER_JIFFY POW2_BELOW32(MAX_CPJ)
+
+/* Precision saver. */
+static inline u_int32_t
+user2credits(u_int32_t user)
+{
+       /* If multiplying would overflow... */
+       if (user > 0xFFFFFFFF / (HZ*CREDITS_PER_JIFFY))
+               /* Divide first. */
+               return (user / IPT_HASHLIMIT_SCALE) * HZ * CREDITS_PER_JIFFY;
+
+       return (user * HZ * CREDITS_PER_JIFFY) / IPT_HASHLIMIT_SCALE;
+}
+
+static inline void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now)
+{
+       dh->rateinfo.credit += (now - xchg(&dh->rateinfo.prev, now)) 
+                                       * CREDITS_PER_JIFFY;
+       if (dh->rateinfo.credit > dh->rateinfo.credit_cap)
+               dh->rateinfo.credit = dh->rateinfo.credit_cap;
+}
+
+static inline int get_ports(const struct sk_buff *skb, int offset, 
+                           u16 ports[2])
+{
+       union {
+               struct tcphdr th;
+               struct udphdr uh;
+               sctp_sctphdr_t sctph;
+       } hdr_u, *ptr_u;
+
+       /* Must not be a fragment. */
+       if (offset)
+               return 1;
+
+       /* Must be big enough to read ports (both UDP and TCP have
+          them at the start). */
+       ptr_u = skb_header_pointer(skb, skb->nh.iph->ihl*4, 8, &hdr_u); 
+       if (!ptr_u)
+               return 1;
+
+       switch (skb->nh.iph->protocol) {
+               case IPPROTO_TCP:
+                       ports[0] = ptr_u->th.source;
+                       ports[1] = ptr_u->th.dest;
+                       break;
+               case IPPROTO_UDP:
+                       ports[0] = ptr_u->uh.source;
+                       ports[1] = ptr_u->uh.dest;
+                       break;
+               case IPPROTO_SCTP:
+                       ports[0] = ptr_u->sctph.source;
+                       ports[1] = ptr_u->sctph.dest;
+                       break;
+               default:
+                       /* all other protocols don't supprot per-port hash
+                        * buckets */
+                       ports[0] = ports[1] = 0;
+                       break;
+       }
+
+       return 0;
+}
+
+
+static int
+hashlimit_match(const struct sk_buff *skb,
+               const struct net_device *in,
+               const struct net_device *out,
+               const void *matchinfo,
+               int offset,
+               int *hotdrop)
+{
+       struct ipt_hashlimit_info *r = 
+               ((struct ipt_hashlimit_info *)matchinfo)->u.master;
+       struct ipt_hashlimit_htable *hinfo = r->hinfo;
+       unsigned long now = jiffies;
+       struct dsthash_ent *dh;
+       struct dsthash_dst dst;
+
+       /* build 'dst' according to hinfo->cfg and current packet */
+       memset(&dst, 0, sizeof(dst));
+       if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_DIP)
+               dst.dst_ip = skb->nh.iph->daddr;
+       if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_SIP)
+               dst.src_ip = skb->nh.iph->saddr;
+       if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_DPT
+           ||hinfo->cfg.mode & IPT_HASHLIMIT_HASH_SPT) {
+               u_int16_t ports[2];
+               if (get_ports(skb, offset, ports)) {
+                       /* We've been asked to examine this packet, and we
+                         can't.  Hence, no choice but to drop. */
+                       *hotdrop = 1;
+                       return 0;
+               }
+               if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_SPT)
+                       dst.src_port = ports[0];
+               if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_DPT)
+                       dst.dst_port = ports[1];
+       } 
+
+       spin_lock_bh(&hinfo->lock);
+       dh = __dsthash_find(hinfo, &dst);
+       if (!dh) {
+               dh = __dsthash_alloc_init(hinfo, &dst);
+
+               if (!dh) {
+                       /* enomem... don't match == DROP */
+                       if (net_ratelimit())
+                               printk(KERN_ERR "%s: ENOMEM\n", __FUNCTION__);
+                       spin_unlock_bh(&hinfo->lock);
+                       return 0;
+               }
+
+               dh->expires = jiffies + MS2JIFFIES(hinfo->cfg.expire);
+
+               dh->rateinfo.prev = jiffies;
+               dh->rateinfo.credit = user2credits(hinfo->cfg.avg * 
+                                                       hinfo->cfg.burst);
+               dh->rateinfo.credit_cap = user2credits(hinfo->cfg.avg * 
+                                                       hinfo->cfg.burst);
+               dh->rateinfo.cost = user2credits(hinfo->cfg.avg);
+
+               spin_unlock_bh(&hinfo->lock);
+               return 1;
+       }
+
+       /* update expiration timeout */
+       dh->expires = now + MS2JIFFIES(hinfo->cfg.expire);
+
+       rateinfo_recalc(dh, now);
+       if (dh->rateinfo.credit >= dh->rateinfo.cost) {
+               /* We're underlimit. */
+               dh->rateinfo.credit -= dh->rateinfo.cost;
+               spin_unlock_bh(&hinfo->lock);
+               return 1;
+       }
+
+               spin_unlock_bh(&hinfo->lock);
+
+       /* default case: we're overlimit, thus don't match */
+       return 0;
+}
+
+static int
+hashlimit_checkentry(const char *tablename,
+                    const struct ipt_ip *ip,
+                    void *matchinfo,
+                    unsigned int matchsize,
+                    unsigned int hook_mask)
+{
+       struct ipt_hashlimit_info *r = matchinfo;
+
+       if (matchsize != IPT_ALIGN(sizeof(struct ipt_hashlimit_info)))
+               return 0;
+
+       /* Check for overflow. */
+       if (r->cfg.burst == 0
+           || user2credits(r->cfg.avg * r->cfg.burst) < 
+                                       user2credits(r->cfg.avg)) {
+               printk(KERN_ERR "ipt_hashlimit: Overflow, try lower: %u/%u\n",
+                      r->cfg.avg, r->cfg.burst);
+               return 0;
+       }
+
+       if (r->cfg.mode == 0 
+           || r->cfg.mode > (IPT_HASHLIMIT_HASH_DPT
+                         |IPT_HASHLIMIT_HASH_DIP
+                         |IPT_HASHLIMIT_HASH_SIP
+                         |IPT_HASHLIMIT_HASH_SPT))
+               return 0;
+
+       if (!r->cfg.gc_interval)
+               return 0;
+       
+       if (!r->cfg.expire)
+               return 0;
+
+       r->hinfo = htable_find_get(r->name);
+       if (!r->hinfo && (htable_create(r) != 0)) {
+               return 0;
+       }
+
+       /* Ugly hack: For SMP, we only want to use one set */
+       r->u.master = r;
+
+       return 1;
+}
+
+static void
+hashlimit_destroy(void *matchinfo, unsigned int matchsize)
+{
+       struct ipt_hashlimit_info *r = (struct ipt_hashlimit_info *) matchinfo;
+
+       htable_put(r->hinfo);
+}
+
+static struct ipt_match ipt_hashlimit = { 
+       .name = "hashlimit", 
+       .match = hashlimit_match, 
+       .checkentry = hashlimit_checkentry, 
+       .destroy = hashlimit_destroy,
+       .me = THIS_MODULE 
+};
+
+/* PROC stuff */
+
+static void *dl_seq_start(struct seq_file *s, loff_t *pos)
+{
+       struct proc_dir_entry *pde = s->private;
+       struct ipt_hashlimit_htable *htable = pde->data;
+       unsigned int *bucket;
+
+       spin_lock_bh(&htable->lock);
+       if (*pos >= htable->cfg.size)
+               return NULL;
+
+       bucket = kmalloc(sizeof(unsigned int), GFP_KERNEL);
+       if (!bucket)
+               return ERR_PTR(-ENOMEM);
+
+       *bucket = *pos;
+       return bucket;
+}
+
+static void *dl_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+       struct proc_dir_entry *pde = s->private;
+       struct ipt_hashlimit_htable *htable = pde->data;
+       unsigned int *bucket = (unsigned int *)v;
+
+       *pos = ++(*bucket);
+       if (*pos >= htable->cfg.size) {
+               kfree(v);
+               return NULL;
+       }
+       return bucket;
+}
+
+static void dl_seq_stop(struct seq_file *s, void *v)
+{
+       struct proc_dir_entry *pde = s->private;
+       struct ipt_hashlimit_htable *htable = pde->data;
+       unsigned int *bucket = (unsigned int *)v;
+
+       kfree(bucket);
+
+       spin_unlock_bh(&htable->lock);
+}
+
+static inline int dl_seq_real_show(struct dsthash_ent *ent, struct seq_file *s)
+{
+       /* recalculate to show accurate numbers */
+       rateinfo_recalc(ent, jiffies);
+
+       return seq_printf(s, "%ld %u.%u.%u.%u:%u->%u.%u.%u.%u:%u %u %u %u\n",
+                       (ent->expires - jiffies)/HZ,
+                       NIPQUAD(ent->dst.src_ip), ntohs(ent->dst.src_port),
+                       NIPQUAD(ent->dst.dst_ip), ntohs(ent->dst.dst_port),
+                       ent->rateinfo.credit, ent->rateinfo.credit_cap,
+                       ent->rateinfo.cost);
+}
+
+static int dl_seq_show(struct seq_file *s, void *v)
+{
+       struct proc_dir_entry *pde = s->private;
+       struct ipt_hashlimit_htable *htable = pde->data;
+       unsigned int *bucket = (unsigned int *)v;
+
+       if (LIST_FIND_W(&htable->hash[*bucket], dl_seq_real_show,
+                     struct dsthash_ent *, s)) {
+               /* buffer was filled and unable to print that tuple */
+               return 1;
+       }
+       return 0;
+}
+
+static struct seq_operations dl_seq_ops = {
+       .start = dl_seq_start,
+       .next  = dl_seq_next,
+       .stop  = dl_seq_stop,
+       .show  = dl_seq_show
+};
+
+static int dl_proc_open(struct inode *inode, struct file *file)
+{
+       int ret = seq_open(file, &dl_seq_ops);
+
+       if (!ret) {
+               struct seq_file *sf = file->private_data;
+               sf->private = PDE(inode);
+       }
+       return ret;
+}
+
+static struct file_operations dl_file_ops = {
+       .owner   = THIS_MODULE,
+       .open    = dl_proc_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = seq_release
+};
+
+static int init_or_fini(int fini)
+{
+       int ret = 0;
+
+       if (fini)
+               goto cleanup;
+
+       if (ipt_register_match(&ipt_hashlimit)) {
+               ret = -EINVAL;
+               goto cleanup_nothing;
+       }
+
+       /* FIXME: do we really want HWCACHE_ALIGN since our objects are
+        * quite small ? */
+       hashlimit_cachep = kmem_cache_create("ipt_hashlimit",
+                                           sizeof(struct dsthash_ent), 0,
+                                           SLAB_HWCACHE_ALIGN, NULL, NULL);
+       if (!hashlimit_cachep) {
+               printk(KERN_ERR "Unable to create ipt_hashlimit slab cache\n");
+               ret = -ENOMEM;
+               goto cleanup_unreg_match;
+       }
+
+       hashlimit_procdir = proc_mkdir("ipt_hashlimit", proc_net);
+       if (!hashlimit_procdir) {
+               printk(KERN_ERR "Unable to create proc dir entry\n");
+               ret = -ENOMEM;
+               goto cleanup_free_slab;
+       }
+
+       return ret;
+
+cleanup:
+       remove_proc_entry("ipt_hashlimit", proc_net);
+cleanup_free_slab:
+       kmem_cache_destroy(hashlimit_cachep);
+cleanup_unreg_match:
+       ipt_unregister_match(&ipt_hashlimit);
+cleanup_nothing:
+       return ret;
+       
+}
+
+static int __init init(void)
+{
+       return init_or_fini(0);
+}
+
+static void __exit fini(void)
+{
+       init_or_fini(1);
+}
+
+module_init(init);
+module_exit(fini);
diff --git a/net/sched/ipt.c b/net/sched/ipt.c
new file mode 100644 (file)
index 0000000..386d948
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * net/sched/ipt.c     iptables target interface
+ *
+ *TODO: Add other tables. For now we only support the ipv4 table targets
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the GNU General Public License
+ *             as published by the Free Software Foundation; either version
+ *             2 of the License, or (at your option) any later version.
+ *
+ * Copyright:  Jamal Hadi Salim (2002-4)
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+#include <linux/tc_act/tc_ipt.h>
+#include <net/tc_act/tc_ipt.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* use generic hash table */
+#define MY_TAB_SIZE     16
+#define MY_TAB_MASK     15
+
+static u32 idx_gen;
+static struct tcf_ipt *tcf_ipt_ht[MY_TAB_SIZE];
+/* ipt hash table lock */
+static rwlock_t ipt_lock = RW_LOCK_UNLOCKED;
+
+/* ovewrride the defaults */
+#define tcf_st  tcf_ipt
+#define tcf_t_lock   ipt_lock
+#define tcf_ht tcf_ipt_ht
+
+#include <net/pkt_act.h>
+
+static inline int
+init_targ(struct tcf_ipt *p)
+{
+       struct ipt_target *target;
+       int ret = 0;
+       struct ipt_entry_target *t = p->t;
+       target = __ipt_find_target_lock(t->u.user.name, &ret);
+
+       if (!target) {
+               printk("init_targ: Failed to find %s\n", t->u.user.name);
+               return -1;
+       }
+
+       DPRINTK("init_targ: found %s\n", target->name);
+       /* we really need proper ref counting
+        seems to be only needed for modules?? Talk to laforge */
+/*      if (target->me)
+              __MOD_INC_USE_COUNT(target->me);
+*/
+       t->u.kernel.target = target;
+
+       __ipt_mutex_up();
+
+       if (t->u.kernel.target->checkentry
+           && !t->u.kernel.target->checkentry(p->tname, NULL, t->data,
+                                              t->u.target_size
+                                              - sizeof (*t), p->hook)) {
+/*              if (t->u.kernel.target->me)
+             __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
+*/
+               DPRINTK("ip_tables: check failed for `%s'.\n",
+                       t->u.kernel.target->name);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int
+tcf_ipt_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a, int ovr, int bind)
+{
+       struct ipt_entry_target *t;
+       unsigned h;
+       struct rtattr *tb[TCA_IPT_MAX];
+       struct tcf_ipt *p;
+       int ret = 0;
+       u32 index = 0;
+       u32 hook = 0;
+
+       if (NULL == a || NULL == rta ||
+           (rtattr_parse(tb, TCA_IPT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)) <
+            0)) {
+               return -1;
+       }
+
+
+       if (tb[TCA_IPT_INDEX - 1]) {
+               index = *(u32 *) RTA_DATA(tb[TCA_IPT_INDEX - 1]);
+               DPRINTK("ipt index %d\n", index);
+       }
+
+       if (index && (p = tcf_hash_lookup(index)) != NULL) {
+               a->priv = (void *) p;
+               spin_lock(&p->lock);
+               if (bind) {
+                       p->bindcnt += 1;
+                       p->refcnt += 1;
+               }
+               if (ovr) {
+                       goto override;
+               }
+               spin_unlock(&p->lock);
+               return ret;
+       }
+
+       if (NULL == tb[TCA_IPT_TARG - 1] || NULL == tb[TCA_IPT_HOOK - 1]) {
+               return -1;
+       }
+
+       p = kmalloc(sizeof (*p), GFP_KERNEL);
+       if (p == NULL)
+               return -1;
+
+       memset(p, 0, sizeof (*p));
+       p->refcnt = 1;
+       ret = 1;
+       spin_lock_init(&p->lock);
+       p->stats_lock = &p->lock;
+       if (bind)
+               p->bindcnt = 1;
+
+override:
+       hook = *(u32 *) RTA_DATA(tb[TCA_IPT_HOOK - 1]);
+
+       t = (struct ipt_entry_target *) RTA_DATA(tb[TCA_IPT_TARG - 1]);
+
+       p->t = kmalloc(t->u.target_size, GFP_KERNEL);
+       if (p->t == NULL) {
+               if (ovr) {
+                       printk("ipt policy messed up \n");
+                       spin_unlock(&p->lock);
+                       return -1;
+               }
+               kfree(p);
+               return -1;
+       }
+
+       memcpy(p->t, RTA_DATA(tb[TCA_IPT_TARG - 1]), t->u.target_size);
+       DPRINTK(" target NAME %s size %d data[0] %x data[1] %x\n",
+               t->u.user.name, t->u.target_size, t->data[0], t->data[1]);
+
+       p->tname = kmalloc(IFNAMSIZ, GFP_KERNEL);
+
+       if (p->tname == NULL) {
+               if (ovr) {
+                       printk("ipt policy messed up 2 \n");
+                       spin_unlock(&p->lock);
+                       return -1;
+               }
+               kfree(p->t);
+               kfree(p);
+               return -1;
+       } else {
+               int csize = IFNAMSIZ - 1;
+
+               memset(p->tname, 0, IFNAMSIZ);
+               if (tb[TCA_IPT_TABLE - 1]) {
+                       if (strlen((char *) RTA_DATA(tb[TCA_IPT_TABLE - 1])) <
+                           csize)
+                               csize = strlen(RTA_DATA(tb[TCA_IPT_TABLE - 1]));
+                       strncpy(p->tname, RTA_DATA(tb[TCA_IPT_TABLE - 1]),
+                               csize);
+                       DPRINTK("table name %s\n", p->tname);
+               } else {
+                       strncpy(p->tname, "mangle", 1 + strlen("mangle"));
+               }
+       }
+
+       if (0 > init_targ(p)) {
+               if (ovr) {
+                       printk("ipt policy messed up 2 \n");
+                       spin_unlock(&p->lock);
+                       return -1;
+               }
+               kfree(p->tname);
+               kfree(p->t);
+               kfree(p);
+               return -1;
+       }
+
+       if (ovr) {
+               spin_unlock(&p->lock);
+               return -1;
+       }
+
+       p->index = index ? : tcf_hash_new_index();
+
+       p->tm.lastuse = jiffies;
+       /*
+       p->tm.expires = jiffies;
+       */
+       p->tm.install = jiffies;
+#ifdef CONFIG_NET_ESTIMATOR
+       if (est)
+               gen_new_estimator(&p->bstats, &p->rate_est, p->stats_lock, est);
+#endif
+       h = tcf_hash(p->index);
+       write_lock_bh(&ipt_lock);
+       p->next = tcf_ipt_ht[h];
+       tcf_ipt_ht[h] = p;
+       write_unlock_bh(&ipt_lock);
+       a->priv = (void *) p;
+       return ret;
+
+}
+
+static int
+tcf_ipt_cleanup(struct tc_action *a, int bind)
+{
+       struct tcf_ipt *p;
+       p = PRIV(a,ipt);
+       if (NULL != p)
+               return tcf_hash_release(p, bind);
+       return 0;
+}
+
+static int
+tcf_ipt(struct sk_buff **pskb, struct tc_action *a)
+{
+       int ret = 0, result = 0;
+       struct tcf_ipt *p;
+       struct sk_buff *skb = *pskb;
+
+       p = PRIV(a,ipt);
+
+       if (NULL == p || NULL == skb) {
+               return -1;
+       }
+
+       spin_lock(&p->lock);
+
+       p->tm.lastuse = jiffies;
+       p->bstats.bytes += skb->len;
+       p->bstats.packets++;
+
+       if (skb_cloned(skb) ) {
+               if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
+                       return -1;
+               }
+       }
+       /* yes, we have to worry about both in and out dev
+        worry later - danger - this API seems to have changed
+        from earlier kernels */
+
+       ret = p->t->u.kernel.target->target(&skb, skb->dev, NULL,
+                                           p->hook, p->t->data, (void *)NULL);
+       switch (ret) {
+       case NF_ACCEPT:
+               result = TC_ACT_OK;
+               break;
+       case NF_DROP:
+               result = TC_ACT_SHOT;
+               p->qstats.drops++;
+               break;
+       case IPT_CONTINUE:
+               result = TC_ACT_PIPE;
+               break;
+       default:
+               if (net_ratelimit())
+                       printk("Bogus netfilter code %d assume ACCEPT\n", ret);
+               result = TC_POLICE_OK;
+               break;
+       }
+       spin_unlock(&p->lock);
+       return result;
+
+}
+
+static int
+tcf_ipt_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
+{
+       struct ipt_entry_target *t;
+       struct tcf_t tm;
+       struct tc_cnt c;
+       unsigned char *b = skb->tail;
+
+       struct tcf_ipt *p;
+
+       p = PRIV(a,ipt);
+       if (NULL == p) {
+               printk("BUG: tcf_ipt_dump called with NULL params\n");
+               goto rtattr_failure;
+       }
+       /* for simple targets kernel size == user size
+       ** user name = target name
+       ** for foolproof you need to not assume this
+       */
+
+       t = kmalloc(p->t->u.user.target_size, GFP_ATOMIC);
+
+       if (NULL == t)
+               goto rtattr_failure;
+
+       c.bindcnt = p->bindcnt - bind;
+       c.refcnt = p->refcnt - ref;
+       memcpy(t, p->t, p->t->u.user.target_size);
+       strcpy(t->u.user.name, p->t->u.kernel.target->name);
+
+       DPRINTK("\ttcf_ipt_dump tablename %s length %d\n", p->tname,
+               strlen(p->tname));
+       DPRINTK
+           ("\tdump target name %s size %d size user %d data[0] %x data[1] %x\n",
+            p->t->u.kernel.target->name, p->t->u.target_size, p->t->u.user.target_size,
+            p->t->data[0], p->t->data[1]);
+       RTA_PUT(skb, TCA_IPT_TARG, p->t->u.user.target_size, t);
+       RTA_PUT(skb, TCA_IPT_INDEX, 4, &p->index);
+       RTA_PUT(skb, TCA_IPT_HOOK, 4, &p->hook);
+       RTA_PUT(skb, TCA_IPT_CNT, sizeof(struct tc_cnt), &c);
+       RTA_PUT(skb, TCA_IPT_TABLE, IFNAMSIZ, p->tname);
+       tm.install = jiffies_to_clock_t(jiffies - p->tm.install);
+       tm.lastuse = jiffies_to_clock_t(jiffies - p->tm.lastuse);
+       tm.expires = jiffies_to_clock_t(p->tm.expires);
+       RTA_PUT(skb, TCA_IPT_TM, sizeof (tm), &tm);
+       return skb->len;
+
+      rtattr_failure:
+       skb_trim(skb, b - skb->data);
+       return -1;
+}
+
+static struct tc_action_ops act_ipt_ops = {
+       .next           =       NULL,
+       .kind           =       "ipt",
+       .type           =       TCA_ACT_IPT,
+       .capab          =       TCA_CAP_NONE,
+       .owner          =       THIS_MODULE,
+       .act            =       tcf_ipt,
+       .dump           =       tcf_ipt_dump,
+       .cleanup        =       tcf_ipt_cleanup,
+       .lookup         =       tcf_hash_search,
+       .init           =       tcf_ipt_init,
+       .walk           =       tcf_generic_walker
+};
+
+MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
+MODULE_DESCRIPTION("Iptables target actions");
+MODULE_LICENSE("GPL");
+
+static int __init
+ipt_init_module(void)
+{
+       return tcf_register_action(&act_ipt_ops);
+}
+
+static void __exit
+ipt_cleanup_module(void)
+{
+       tcf_unregister_action(&act_ipt_ops);
+}
+
+module_init(ipt_init_module);
+module_exit(ipt_cleanup_module);
diff --git a/net/sched/mirred.c b/net/sched/mirred.c
new file mode 100644 (file)
index 0000000..07e3ba1
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * net/sched/mirred.c  packet mirroring and redirect actions
+ *
+ *             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.
+ *
+ * Authors:    Jamal Hadi Salim (2002-4)
+ *
+ * TODO: Add ingress support (and socket redirect support)
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+#include <linux/tc_act/tc_mirred.h>
+#include <net/tc_act/tc_mirred.h>
+
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+
+
+/* use generic hash table */
+#define MY_TAB_SIZE     8
+#define MY_TAB_MASK     (MY_TAB_SIZE - 1)
+static u32 idx_gen;
+static struct tcf_mirred *tcf_mirred_ht[MY_TAB_SIZE];
+static rwlock_t mirred_lock = RW_LOCK_UNLOCKED;
+
+/* ovewrride the defaults */
+#define tcf_st  tcf_mirred
+#define tc_st  tc_mirred
+#define tcf_t_lock   mirred_lock
+#define tcf_ht tcf_mirred_ht
+
+#define CONFIG_NET_ACT_INIT 1
+#include <net/pkt_act.h>
+
+static inline int
+tcf_mirred_release(struct tcf_mirred *p, int bind)
+{
+       if (p) {
+               if (bind) {
+                       p->bindcnt--;
+               }
+
+               p->refcnt--;
+               if(!p->bindcnt && p->refcnt <= 0) {
+                       dev_put(p->dev);
+                       tcf_hash_destroy(p);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+static int
+tcf_mirred_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a,int ovr, int bind)
+{
+       struct rtattr *tb[TCA_MIRRED_MAX];
+       struct tc_mirred *parm;
+       struct tcf_mirred *p;
+       struct net_device *dev = NULL;
+       int size = sizeof (*p), new = 0;
+
+
+       if (rtattr_parse(tb, TCA_MIRRED_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)) < 0) {
+               DPRINTK("tcf_mirred_init BUG in user space couldnt parse properly\n");
+               return -1;
+       }
+
+       if (NULL == a || NULL == tb[TCA_MIRRED_PARMS - 1]) {
+               DPRINTK("BUG: tcf_mirred_init called with NULL params\n");
+               return -1;
+       }
+
+       parm = RTA_DATA(tb[TCA_MIRRED_PARMS - 1]);
+
+       p = tcf_hash_check(parm, a, ovr, bind);
+       if (NULL == p) { /* new */
+               p = tcf_hash_create(parm,est,a,size,ovr,bind);
+               new = 1;
+               if (NULL == p)
+                       return -1;
+       }
+
+       if (parm->ifindex) {
+               dev = dev_get_by_index(parm->ifindex);
+               if (NULL == dev) {
+                       printk("BUG: tcf_mirred_init called with bad device\n");
+                       return -1;
+               }
+               switch (dev->type) {
+                       case ARPHRD_TUNNEL:
+                       case ARPHRD_TUNNEL6:
+                       case ARPHRD_SIT:
+                       case ARPHRD_IPGRE:
+                       case ARPHRD_VOID:
+                       case ARPHRD_NONE:
+                               p->ok_push = 0;
+                               break;
+                       default:
+                               p->ok_push = 1;
+                               break;
+               }
+       } else {
+               if (new) {
+                       kfree(p);
+                       return -1;
+               }       
+       }
+
+       if (new || ovr) {
+               spin_lock(&p->lock);
+               p->action = parm->action;
+               p->eaction = parm->eaction;
+               if (parm->ifindex) {
+                       p->ifindex = parm->ifindex;
+                       if (ovr)
+                               dev_put(p->dev);
+                       p->dev = dev;
+               }
+               spin_unlock(&p->lock);
+       }
+
+
+       DPRINTK(" tcf_mirred_init index %d action %d eaction %d device %s ifndex %d\n",parm->index,parm->action,parm->eaction,dev->name,parm->ifindex);
+       return new;
+
+}
+
+static int
+tcf_mirred_cleanup(struct tc_action *a, int bind)
+{
+       struct tcf_mirred *p;
+       p = PRIV(a,mirred);
+       if (NULL != p)
+               return tcf_mirred_release(p, bind);
+       return 0;
+}
+
+static int
+tcf_mirred(struct sk_buff **pskb, struct tc_action *a)
+{
+       struct tcf_mirred *p;
+       struct net_device *dev;
+       struct sk_buff *skb2 = NULL;
+       struct sk_buff *skb = *pskb;
+       __u32 at = G_TC_AT(skb->tc_verd);
+
+       if (NULL == a) {
+               if (net_ratelimit())
+                       printk("BUG: tcf_mirred called with NULL action!\n");
+               return -1;
+       }
+
+       p = PRIV(a,mirred);
+
+       if (NULL == p) {
+               if (net_ratelimit())
+                       printk("BUG: tcf_mirred called with NULL params\n");
+               return -1;
+       }
+
+       spin_lock(&p->lock);
+
+               dev = p->dev;
+       p->tm.lastuse = jiffies;
+
+       if (NULL == dev || !(dev->flags&IFF_UP) ) {
+               if (net_ratelimit())
+                       printk("mirred to Houston: device %s is gone!\n",
+                                       dev?dev->name:"");
+bad_mirred:
+               if (NULL != skb2)
+                       kfree_skb(skb2);
+               p->qstats.overlimits++;
+               p->bstats.bytes += skb->len;
+               p->bstats.packets++;
+               spin_unlock(&p->lock);
+               /* should we be asking for packet to be dropped?
+                * may make sense for redirect case only 
+               */
+               return TC_ACT_SHOT;
+       } 
+
+       skb2 = skb_clone(skb, GFP_ATOMIC);
+       if (skb2 == NULL) {
+               goto bad_mirred;
+       }
+       if (TCA_EGRESS_MIRROR != p->eaction &&
+               TCA_EGRESS_REDIR != p->eaction) {
+               if (net_ratelimit())
+                       printk("tcf_mirred unknown action %d\n",p->eaction);
+               goto bad_mirred;
+       }
+
+       p->bstats.bytes += skb2->len;
+       p->bstats.packets++;
+       if ( !(at & AT_EGRESS)) {
+               if (p->ok_push) {
+                       skb_push(skb2, skb2->dev->hard_header_len);
+               }
+       }
+
+       /* mirror is always swallowed */
+       if (TCA_EGRESS_MIRROR != p->eaction)
+               skb2->tc_verd = SET_TC_FROM(skb2->tc_verd,at);
+
+       skb2->dev = dev;
+       skb2->input_dev = skb->dev;
+       dev_queue_xmit(skb2);
+       spin_unlock(&p->lock);
+       return p->action;
+}
+
+static int
+tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a,int bind, int ref)
+{
+       unsigned char *b = skb->tail;
+       struct tc_mirred opt;
+       struct tcf_mirred *p;
+       struct tcf_t t;
+
+       p = PRIV(a,mirred);
+       if (NULL == p) {
+               printk("BUG: tcf_mirred_dump called with NULL params\n");
+               goto rtattr_failure;
+       }
+
+       opt.index = p->index;
+       opt.action = p->action;
+       opt.refcnt = p->refcnt - ref;
+       opt.bindcnt = p->bindcnt - bind;
+       opt.eaction = p->eaction;
+       opt.ifindex = p->ifindex;
+       DPRINTK(" tcf_mirred_dump index %d action %d eaction %d ifndex %d\n",p->index,p->action,p->eaction,p->ifindex);
+       RTA_PUT(skb, TCA_MIRRED_PARMS, sizeof (opt), &opt);
+       t.install = jiffies_to_clock_t(jiffies - p->tm.install);
+       t.lastuse = jiffies_to_clock_t(jiffies - p->tm.lastuse);
+       t.expires = jiffies_to_clock_t(p->tm.expires);
+       RTA_PUT(skb, TCA_MIRRED_TM, sizeof (t), &t);
+       return skb->len;
+
+      rtattr_failure:
+       skb_trim(skb, b - skb->data);
+       return -1;
+}
+
+static struct tc_action_ops act_mirred_ops = {
+       .next           =       NULL,
+       .kind           =       "mirred",
+       .type           =       TCA_ACT_MIRRED,
+       .capab          =       TCA_CAP_NONE,
+       .owner          =       THIS_MODULE,
+       .act            =       tcf_mirred,
+       .dump           =       tcf_mirred_dump,
+       .cleanup        =       tcf_mirred_cleanup,
+       .lookup         =       tcf_hash_search,
+       .init           =       tcf_mirred_init,
+       .walk           =       tcf_generic_walker
+};
+
+MODULE_AUTHOR("Jamal Hadi Salim(2002)");
+MODULE_DESCRIPTION("Device Mirror/redirect actions");
+MODULE_LICENSE("GPL");
+
+
+static int __init
+mirred_init_module(void)
+{
+       printk("Mirror/redirect action on\n");
+       return tcf_register_action(&act_mirred_ops);
+}
+
+static void __exit
+mirred_cleanup_module(void)
+{
+       tcf_unregister_action(&act_mirred_ops);
+}
+
+module_init(mirred_init_module);
+module_exit(mirred_cleanup_module);
+
diff --git a/net/sched/pedit.c b/net/sched/pedit.c
new file mode 100644 (file)
index 0000000..23debf3
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * net/sched/pedit.c   Generic packet editor
+ *
+ *             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.
+ *
+ * Authors:    Jamal Hadi Salim (2002-4)
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <net/sock.h>
+#include <net/pkt_sched.h>
+#include <linux/tc_act/tc_pedit.h>
+#include <net/tc_act/tc_pedit.h>
+
+
+#define PEDIT_DEB 1
+
+/* use generic hash table */
+#define MY_TAB_SIZE     16
+#define MY_TAB_MASK     15
+static u32 idx_gen;
+static struct tcf_pedit *tcf_pedit_ht[MY_TAB_SIZE];
+static rwlock_t pedit_lock = RW_LOCK_UNLOCKED;
+
+#define tcf_st  tcf_pedit
+#define tc_st  tc_pedit
+#define tcf_t_lock   pedit_lock
+#define tcf_ht tcf_pedit_ht
+
+#define CONFIG_NET_ACT_INIT 1
+#include <net/pkt_act.h>
+
+
+static int
+tcf_pedit_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a,int ovr, int bind)
+{
+       struct rtattr *tb[TCA_PEDIT_MAX];
+       struct tc_pedit *parm;
+       int size = 0;
+       int ret = 0;
+       struct tcf_pedit *p = NULL;
+
+       if (rtattr_parse(tb, TCA_PEDIT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)) < 0)
+               return -1;
+
+       if (NULL == a || NULL == tb[TCA_PEDIT_PARMS - 1]) {
+               printk("BUG: tcf_pedit_init called with NULL params\n");
+               return -1;
+       }
+
+       parm = RTA_DATA(tb[TCA_PEDIT_PARMS - 1]);
+
+       p = tcf_hash_check(parm, a, ovr, bind);
+
+       if (NULL == p) { /* new */
+
+               if (!parm->nkeys)
+                       return -1;
+
+               size = sizeof (*p)+ (parm->nkeys*sizeof(struct tc_pedit_key));
+
+               p = tcf_hash_create(parm,est,a,size,ovr,bind);
+
+               if (NULL == p)
+                       return -1;
+               ret = 1;
+               goto override;
+       } 
+
+       if (ovr) {
+override:
+               p->flags = parm->flags;
+               p->nkeys = parm->nkeys;
+               p->action = parm->action;
+               memcpy(p->keys,parm->keys,parm->nkeys*(sizeof(struct tc_pedit_key)));
+       }
+
+       return ret;
+}
+
+static int
+tcf_pedit_cleanup(struct tc_action *a, int bind)
+{
+       struct tcf_pedit *p;
+       p = PRIV(a,pedit);
+       if (NULL != p)
+               return  tcf_hash_release(p, bind);
+       return 0;
+}
+
+/*
+**
+*/
+static int
+tcf_pedit(struct sk_buff **pskb, struct tc_action *a)
+{
+       struct tcf_pedit *p;
+       struct sk_buff *skb = *pskb;
+       int i, munged = 0;
+       u8 *pptr;
+
+       p = PRIV(a,pedit);
+
+       if (NULL == p) {
+               printk("BUG: tcf_pedit called with NULL params\n");
+               return -1; /* change to something symbolic */
+       }
+
+       if (!(skb->tc_verd & TC_OK2MUNGE)) {
+               /* should we set skb->cloned? */
+               if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
+                       return p->action;
+               }
+       }
+
+       pptr = skb->nh.raw;
+
+       spin_lock(&p->lock);
+
+       p->tm.lastuse = jiffies;
+
+       if (0 < p->nkeys) {
+               struct tc_pedit_key *tkey = p->keys;
+
+               for (i = p->nkeys; i > 0; i--, tkey++) {
+                       u32 *ptr ;
+
+                       int offset = tkey->off;
+                       if (tkey->offmask) {
+                               if (skb->len > tkey->at) {
+                                        char *j = pptr+tkey->at;
+                                        offset +=((*j&tkey->offmask)>>tkey->shift);
+                               } else {
+                                       goto bad;
+                               }
+                       }
+
+                       if (offset % 4) {
+                               printk("offset must be on 32 bit boundaries\n");
+                               goto bad;
+                       }
+
+                       if (skb->len < 0 || (offset > 0 && offset > skb->len)) {
+                               printk("offset %d cant exceed pkt length %d\n",
+                                               offset, skb->len);
+                               goto bad;
+                       }
+
+
+                       ptr = (u32 *)(pptr+offset);
+                       /* just do it, baby */
+                       *ptr = ((*ptr & tkey->mask) ^ tkey->val);
+                       munged++;
+               }
+               
+               if (munged)
+                       skb->tc_verd = SET_TC_MUNGED(skb->tc_verd);
+               goto done;
+       } else {
+               printk("pedit BUG: index %d\n",p->index);
+       }
+
+bad:
+       p->qstats.overlimits++;
+done:
+       p->bstats.bytes += skb->len;
+       p->bstats.packets++;
+       spin_unlock(&p->lock);
+       return p->action;
+}
+
+static int
+tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a,int bind, int ref)
+{
+       unsigned char *b = skb->tail;
+       struct tc_pedit *opt;
+       struct tcf_pedit *p;
+       struct tcf_t t;
+       int s; 
+               
+
+       p = PRIV(a,pedit);
+
+       if (NULL == p) {
+               printk("BUG: tcf_pedit_dump called with NULL params\n");
+               goto rtattr_failure;
+       }
+
+       s = sizeof (*opt)+(p->nkeys*sizeof(struct tc_pedit_key));
+
+       /* netlink spinlocks held above us - must use ATOMIC
+        * */
+       opt = kmalloc(s, GFP_ATOMIC);
+       if (opt == NULL)
+               return -ENOBUFS;
+
+       memset(opt, 0, s);
+
+       memcpy(opt->keys,p->keys,p->nkeys*(sizeof(struct tc_pedit_key)));
+       opt->index = p->index;
+       opt->nkeys = p->nkeys;
+       opt->flags = p->flags;
+       opt->action = p->action;
+       opt->refcnt = p->refcnt - ref;
+       opt->bindcnt = p->bindcnt - bind;
+
+
+#ifdef PEDIT_DEB
+       {                
+               /* Debug - get rid of later */
+               int i;
+               struct tc_pedit_key *key = opt->keys;
+
+               for (i=0; i<opt->nkeys; i++, key++) {
+                       printk( "\n key #%d",i);
+                       printk( "  at %d: val %08x mask %08x",
+                       (unsigned int)key->off,
+                       (unsigned int)key->val,
+                       (unsigned int)key->mask);
+                                                                                               }
+                                                                                       }
+#endif
+
+       RTA_PUT(skb, TCA_PEDIT_PARMS, s, opt);
+       t.install = jiffies_to_clock_t(jiffies - p->tm.install);
+       t.lastuse = jiffies_to_clock_t(jiffies - p->tm.lastuse);
+       t.expires = jiffies_to_clock_t(p->tm.expires);
+       RTA_PUT(skb, TCA_PEDIT_TM, sizeof (t), &t);
+       return skb->len;
+
+rtattr_failure:
+       skb_trim(skb, b - skb->data);
+       return -1;
+}
+
+static
+struct tc_action_ops act_pedit_ops = {
+       .kind           =       "pedit",
+       .type           =       TCA_ACT_PEDIT,
+       .capab          =       TCA_CAP_NONE,
+       .owner          =       THIS_MODULE,
+       .act            =       tcf_pedit,
+       .dump           =       tcf_pedit_dump,
+       .cleanup        =       tcf_pedit_cleanup,
+       .lookup         =       tcf_hash_search,
+       .init           =       tcf_pedit_init,
+       .walk           =       tcf_generic_walker
+};
+
+MODULE_AUTHOR("Jamal Hadi Salim(2002-4)");
+MODULE_DESCRIPTION("Generic Packet Editor actions");
+MODULE_LICENSE("GPL");
+
+static int __init
+pedit_init_module(void)
+{
+       return tcf_register_action(&act_pedit_ops);
+}
+
+static void __exit
+pedit_cleanup_module(void)
+{
+       tcf_unregister_action(&act_pedit_ops);
+}
+
+module_init(pedit_init_module);
+module_exit(pedit_cleanup_module);
+
diff --git a/scripts/gen_initramfs_list.sh b/scripts/gen_initramfs_list.sh
new file mode 100644 (file)
index 0000000..2a697c3
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/bash
+# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
+# Released under the terms of the GNU GPL
+#
+# Generate a newline separated list of entries from the file/directory pointed
+# out by the environment variable: CONFIG_INITRAMFS_SOURCE
+#
+# If CONFIG_INITRAMFS_SOURCE is non-existing then generate a small dummy file.
+#
+# The output is suitable for gen_init_cpio as found in usr/Makefile.
+#
+# TODO:  Add support for symlinks, sockets and pipes when gen_init_cpio
+#        supports them.
+
+simple_initramfs() {
+       cat <<-EOF
+               # This is a very simple initramfs
+
+               dir /dev 0755 0 0
+               nod /dev/console 0600 0 0 c 5 1
+               dir /root 0700 0 0
+       EOF
+}
+
+filetype() {
+       local argv1="$1"
+
+       if [ -f "${argv1}" ]; then
+               echo "file"
+       elif [ -d "${argv1}" ]; then
+               echo "dir"
+       elif [ -b "${argv1}" -o -c "${argv1}" ]; then
+               echo "nod"
+       else
+               echo "invalid"
+       fi
+       return 0
+}
+
+print_mtime() {
+       local argv1="$1"
+       local my_mtime="0"
+
+       if [ -e "${argv1}" ]; then
+               my_mtime=$(find "${argv1}" -printf "%T@\n" | sort -r | head -n 1)
+       fi
+       
+       echo "# Last modified: ${my_mtime}"
+       echo
+}
+
+parse() {
+       local location="$1"
+       local name="${location/${srcdir}//}"
+       local mode="$2"
+       local uid="$3"
+       local gid="$4"
+       local ftype=$(filetype "${location}")
+       local str="${mode} ${uid} ${gid}"
+
+       [ "${ftype}" == "invalid" ] && return 0
+       [ "${location}" == "${srcdir}" ] && return 0
+
+       case "${ftype}" in
+               "file")
+                       str="${ftype} ${name} ${location} ${str}"
+                       ;;
+               "nod")
+                       local dev_type=
+                       local maj=$(LC_ALL=C ls -l "${location}" | \
+                                       gawk '{sub(/,/, "", $5); print $5}')
+                       local min=$(LC_ALL=C ls -l "${location}" | \
+                                       gawk '{print $6}')
+
+                       if [ -b "${location}" ]; then
+                               dev_type="b"
+                       else
+                               dev_type="c"
+                       fi
+                       str="${ftype} ${name} ${str} ${dev_type} ${maj} ${min}"
+                       ;;
+               *)
+                       str="${ftype} ${name} ${str}"
+                       ;;
+       esac
+
+       echo "${str}"
+
+       return 0
+}
+
+if [ -z "$1" ]; then
+       simple_initramfs
+elif [ -f "$1" ]; then
+       print_mtime "$1"
+       cat "$1"
+elif [ -d "$1" ]; then
+       srcdir=$(echo "$1" | sed -e 's://*:/:g')
+       dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" 2>/dev/null)
+
+       # If $dirlist is only one line, then the directory is empty
+       if [  "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
+               print_mtime "$1"
+               
+               echo "${dirlist}" | \
+               while read x; do
+                       parse ${x}
+               done
+       else
+               # Failsafe in case directory is empty
+               simple_initramfs
+       fi
+else
+       echo "  $0: Cannot open '$1' (CONFIG_INITRAMFS_SOURCE)" >&2
+       exit 1
+fi
+
+exit 0
diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c
new file mode 100644 (file)
index 0000000..1fa4c0b
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org>
+ * Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <string.h>
+#include "lkc.h"
+
+/* file already present in list? If not add it */
+struct file *file_lookup(const char *name)
+{
+       struct file *file;
+
+       for (file = file_list; file; file = file->next) {
+               if (!strcmp(name, file->name))
+                       return file;
+       }
+
+       file = malloc(sizeof(*file));
+       memset(file, 0, sizeof(*file));
+       file->name = strdup(name);
+       file->next = file_list;
+       file_list = file;
+       return file;
+}
+
+/* write a dependency file as used by kbuild to track dependencies */
+int file_write_dep(const char *name)
+{
+       struct file *file;
+       FILE *out;
+
+       if (!name)
+               name = ".config.cmd";
+       out = fopen("..config.tmp", "w");
+       if (!out)
+               return 1;
+       fprintf(out, "deps_config := \\\n");
+       for (file = file_list; file; file = file->next) {
+               if (file->next)
+                       fprintf(out, "\t%s \\\n", file->name);
+               else
+                       fprintf(out, "\t%s\n", file->name);
+       }
+       fprintf(out, "\n.config include/linux/autoconf.h: $(deps_config)\n\n$(deps_config):\n");
+       fclose(out);
+       rename("..config.tmp", name);
+       return 0;
+}
+
+
+/* Allocate initial growable sting */
+struct gstr str_new(void)
+{
+       struct gstr gs;
+       gs.s = malloc(sizeof(char) * 64);
+       gs.len = 16;
+       strcpy(gs.s, "\0");
+       return gs;
+}
+
+/* Allocate and assign growable string */
+struct gstr str_assign(const char *s)
+{
+       struct gstr gs;
+       gs.s = strdup(s);
+       gs.len = strlen(s) + 1;
+       return gs;
+}
+
+/* Free storage for growable string */
+void str_free(struct gstr *gs)
+{
+       if (gs->s)
+               free(gs->s);
+       gs->s = NULL;
+       gs->len = 0;
+}
+
+/* Append to growable string */
+void str_append(struct gstr *gs, const char *s)
+{
+       size_t l = strlen(gs->s) + strlen(s) + 1;
+       if (l > gs->len) {
+               gs->s   = realloc(gs->s, l);
+               gs->len = l;
+       }
+       strcat(gs->s, s);
+}
+
+/* Append printf formatted string to growable string */
+void str_printf(struct gstr *gs, const char *fmt, ...)
+{
+       va_list ap;
+       char s[10000]; /* big enough... */
+       va_start(ap, fmt);
+       vsnprintf(s, sizeof(s), fmt, ap);
+       str_append(gs, s);
+       va_end(ap);
+}
+
+/* Retreive value of growable string */
+const char *str_get(struct gstr *gs)
+{
+       return gs->s;
+}
+
diff --git a/security/keys/Makefile b/security/keys/Makefile
new file mode 100644 (file)
index 0000000..ddb495d
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Makefile for key management
+#
+
+obj-y := \
+       key.o \
+       keyring.o \
+       keyctl.o \
+       process_keys.o \
+       user_defined.o \
+       request_key.o
+
+obj-$(CONFIG_KEYS_COMPAT) += compat.o
+obj-$(CONFIG_PROC_FS) += proc.o
diff --git a/security/keys/compat.c b/security/keys/compat.c
new file mode 100644 (file)
index 0000000..17c038c
--- /dev/null
@@ -0,0 +1,78 @@
+/* compat.c: 32-bit compatibility syscall for 64-bit systems
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <linux/keyctl.h>
+#include <linux/compat.h>
+#include "internal.h"
+
+/*****************************************************************************/
+/*
+ * the key control system call, 32-bit compatibility version for 64-bit archs
+ * - this should only be called if the 64-bit arch uses weird pointers in
+ *   32-bit mode or doesn't guarantee that the top 32-bits of the argument
+ *   registers on taking a 32-bit syscall are zero
+ * - if you can, you should call sys_keyctl directly
+ */
+asmlinkage long compat_sys_keyctl(u32 option,
+                             u32 arg2, u32 arg3, u32 arg4, u32 arg5)
+{
+       switch (option) {
+       case KEYCTL_GET_KEYRING_ID:
+               return keyctl_get_keyring_ID(arg2, arg3);
+
+       case KEYCTL_JOIN_SESSION_KEYRING:
+               return keyctl_join_session_keyring(compat_ptr(arg3));
+
+       case KEYCTL_UPDATE:
+               return keyctl_update_key(arg2, compat_ptr(arg3), arg4);
+
+       case KEYCTL_REVOKE:
+               return keyctl_revoke_key(arg2);
+
+       case KEYCTL_DESCRIBE:
+               return keyctl_describe_key(arg2, compat_ptr(arg3), arg4);
+
+       case KEYCTL_CLEAR:
+               return keyctl_keyring_clear(arg2);
+
+       case KEYCTL_LINK:
+               return keyctl_keyring_link(arg2, arg3);
+
+       case KEYCTL_UNLINK:
+               return keyctl_keyring_unlink(arg2, arg3);
+
+       case KEYCTL_SEARCH:
+               return keyctl_keyring_search(arg2, compat_ptr(arg3),
+                                            compat_ptr(arg4), arg5);
+
+       case KEYCTL_READ:
+               return keyctl_read_key(arg2, compat_ptr(arg3), arg4);
+
+       case KEYCTL_CHOWN:
+               return keyctl_chown_key(arg2, arg3, arg4);
+
+       case KEYCTL_SETPERM:
+               return keyctl_setperm_key(arg2, arg3);
+
+       case KEYCTL_INSTANTIATE:
+               return keyctl_instantiate_key(arg2, compat_ptr(arg3), arg4,
+                                             arg5);
+
+       case KEYCTL_NEGATE:
+               return keyctl_negate_key(arg2, arg3, arg4);
+
+       default:
+               return -EOPNOTSUPP;
+       }
+
+} /* end compat_sys_keyctl() */
diff --git a/security/keys/internal.h b/security/keys/internal.h
new file mode 100644 (file)
index 0000000..67b2b93
--- /dev/null
@@ -0,0 +1,123 @@
+/* internal.h: authentication token and access key management internal defs
+ *
+ * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _INTERNAL_H
+#define _INTERNAL_H
+
+#include <linux/key.h>
+#include <linux/key-ui.h>
+
+extern struct key_type key_type_dead;
+extern struct key_type key_type_user;
+
+/*****************************************************************************/
+/*
+ * keep track of keys for a user
+ * - this needs to be separate to user_struct to avoid a refcount-loop
+ *   (user_struct pins some keyrings which pin this struct)
+ * - this also keeps track of keys under request from userspace for this UID
+ */
+struct key_user {
+       struct rb_node          node;
+       struct list_head        consq;          /* construction queue */
+       spinlock_t              lock;
+       atomic_t                usage;          /* for accessing qnkeys & qnbytes */
+       atomic_t                nkeys;          /* number of keys */
+       atomic_t                nikeys;         /* number of instantiated keys */
+       uid_t                   uid;
+       int                     qnkeys;         /* number of keys allocated to this user */
+       int                     qnbytes;        /* number of bytes allocated to this user */
+};
+
+#define KEYQUOTA_MAX_KEYS      100
+#define KEYQUOTA_MAX_BYTES     10000
+#define KEYQUOTA_LINK_BYTES    4               /* a link in a keyring is worth 4 bytes */
+
+extern struct rb_root  key_user_tree;
+extern spinlock_t      key_user_lock;
+extern struct key_user root_key_user;
+
+extern struct key_user *key_user_lookup(uid_t uid);
+extern void key_user_put(struct key_user *user);
+
+
+
+extern struct rb_root key_serial_tree;
+extern spinlock_t key_serial_lock;
+extern struct semaphore key_alloc_sem;
+extern struct rw_semaphore key_construction_sem;
+extern wait_queue_head_t request_key_conswq;
+
+
+extern void keyring_publish_name(struct key *keyring);
+
+extern int __key_link(struct key *keyring, struct key *key);
+
+extern struct key *__keyring_search_one(struct key *keyring,
+                                       const struct key_type *type,
+                                       const char *description,
+                                       key_perm_t perm);
+
+typedef int (*key_match_func_t)(const struct key *, const void *);
+
+extern struct key *keyring_search_aux(struct key *keyring,
+                                     struct key_type *type,
+                                     const void *description,
+                                     key_match_func_t match);
+
+extern struct key *search_process_keyrings_aux(struct key_type *type,
+                                              const void *description,
+                                              key_match_func_t match);
+
+extern struct key *find_keyring_by_name(const char *name, key_serial_t bound);
+
+extern int install_thread_keyring(struct task_struct *tsk);
+
+/*
+ * keyctl functions
+ */
+extern long keyctl_get_keyring_ID(key_serial_t, int);
+extern long keyctl_join_session_keyring(const char __user *);
+extern long keyctl_update_key(key_serial_t, const void __user *, size_t);
+extern long keyctl_revoke_key(key_serial_t);
+extern long keyctl_keyring_clear(key_serial_t);
+extern long keyctl_keyring_link(key_serial_t, key_serial_t);
+extern long keyctl_keyring_unlink(key_serial_t, key_serial_t);
+extern long keyctl_describe_key(key_serial_t, char __user *, size_t);
+extern long keyctl_keyring_search(key_serial_t, const char __user *,
+                                 const char __user *, key_serial_t);
+extern long keyctl_read_key(key_serial_t, char __user *, size_t);
+extern long keyctl_chown_key(key_serial_t, uid_t, gid_t);
+extern long keyctl_setperm_key(key_serial_t, key_perm_t);
+extern long keyctl_instantiate_key(key_serial_t, const void __user *,
+                                  size_t, key_serial_t);
+extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t);
+
+
+/*
+ * debugging key validation
+ */
+#ifdef KEY_DEBUGGING
+extern void __key_check(const struct key *);
+
+static inline void key_check(const struct key *key)
+{
+       if (key && (IS_ERR(key) || key->magic != KEY_DEBUG_MAGIC))
+               __key_check(key);
+}
+
+#else
+
+#define key_check(key) do {} while(0)
+
+#endif
+
+#endif /* _INTERNAL_H */
diff --git a/security/keys/key.c b/security/keys/key.c
new file mode 100644 (file)
index 0000000..df264ad
--- /dev/null
@@ -0,0 +1,1039 @@
+/* key.c: basic authentication token and access key management
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/err.h>
+#include "internal.h"
+
+static kmem_cache_t    *key_jar;
+static key_serial_t    key_serial_next = 3;
+struct rb_root         key_serial_tree; /* tree of keys indexed by serial */
+spinlock_t             key_serial_lock = SPIN_LOCK_UNLOCKED;
+
+struct rb_root key_user_tree; /* tree of quota records indexed by UID */
+spinlock_t     key_user_lock = SPIN_LOCK_UNLOCKED;
+
+static LIST_HEAD(key_types_list);
+static DECLARE_RWSEM(key_types_sem);
+
+static void key_cleanup(void *data);
+static DECLARE_WORK(key_cleanup_task, key_cleanup, NULL);
+
+/* we serialise key instantiation and link */
+DECLARE_RWSEM(key_construction_sem);
+
+/* any key who's type gets unegistered will be re-typed to this */
+struct key_type key_type_dead = {
+       .name           = "dead",
+};
+
+#ifdef KEY_DEBUGGING
+void __key_check(const struct key *key)
+{
+       printk("__key_check: key %p {%08x} should be {%08x}\n",
+              key, key->magic, KEY_DEBUG_MAGIC);
+       BUG();
+}
+#endif
+
+/*****************************************************************************/
+/*
+ * get the key quota record for a user, allocating a new record if one doesn't
+ * already exist
+ */
+struct key_user *key_user_lookup(uid_t uid)
+{
+       struct key_user *candidate = NULL, *user;
+       struct rb_node *parent = NULL;
+       struct rb_node **p = &key_user_tree.rb_node;
+
+ try_again:
+       spin_lock(&key_user_lock);
+
+       /* search the tree for a user record with a matching UID */
+       while (*p) {
+               parent = *p;
+               user = rb_entry(parent, struct key_user, node);
+
+               if (uid < user->uid)
+                       p = &(*p)->rb_left;
+               else if (uid > user->uid)
+                       p = &(*p)->rb_right;
+               else
+                       goto found;
+       }
+
+       /* if we get here, we failed to find a match in the tree */
+       if (!candidate) {
+               /* allocate a candidate user record if we don't already have
+                * one */
+               spin_unlock(&key_user_lock);
+
+               user = NULL;
+               candidate = kmalloc(sizeof(struct key_user), GFP_KERNEL);
+               if (unlikely(!candidate))
+                       goto out;
+
+               /* the allocation may have scheduled, so we need to repeat the
+                * search lest someone else added the record whilst we were
+                * asleep */
+               goto try_again;
+       }
+
+       /* if we get here, then the user record still hadn't appeared on the
+        * second pass - so we use the candidate record */
+       atomic_set(&candidate->usage, 1);
+       atomic_set(&candidate->nkeys, 0);
+       atomic_set(&candidate->nikeys, 0);
+       candidate->uid = uid;
+       candidate->qnkeys = 0;
+       candidate->qnbytes = 0;
+       spin_lock_init(&candidate->lock);
+       INIT_LIST_HEAD(&candidate->consq);
+
+       rb_link_node(&candidate->node, parent, p);
+       rb_insert_color(&candidate->node, &key_user_tree);
+       spin_unlock(&key_user_lock);
+       user = candidate;
+       goto out;
+
+       /* okay - we found a user record for this UID */
+ found:
+       atomic_inc(&user->usage);
+       spin_unlock(&key_user_lock);
+       if (candidate)
+               kfree(candidate);
+ out:
+       return user;
+
+} /* end key_user_lookup() */
+
+/*****************************************************************************/
+/*
+ * dispose of a user structure
+ */
+void key_user_put(struct key_user *user)
+{
+       if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
+               rb_erase(&user->node, &key_user_tree);
+               spin_unlock(&key_user_lock);
+
+               kfree(user);
+       }
+
+} /* end key_user_put() */
+
+/*****************************************************************************/
+/*
+ * insert a key with a fixed serial number
+ */
+static void __init __key_insert_serial(struct key *key)
+{
+       struct rb_node *parent, **p;
+       struct key *xkey;
+
+       parent = NULL;
+       p = &key_serial_tree.rb_node;
+
+       while (*p) {
+               parent = *p;
+               xkey = rb_entry(parent, struct key, serial_node);
+
+               if (key->serial < xkey->serial)
+                       p = &(*p)->rb_left;
+               else if (key->serial > xkey->serial)
+                       p = &(*p)->rb_right;
+               else
+                       BUG();
+       }
+
+       /* we've found a suitable hole - arrange for this key to occupy it */
+       rb_link_node(&key->serial_node, parent, p);
+       rb_insert_color(&key->serial_node, &key_serial_tree);
+
+} /* end __key_insert_serial() */
+
+/*****************************************************************************/
+/*
+ * assign a key the next unique serial number
+ * - we work through all the serial numbers between 2 and 2^31-1 in turn and
+ *   then wrap
+ */
+static inline void key_alloc_serial(struct key *key)
+{
+       struct rb_node *parent, **p;
+       struct key *xkey;
+
+       spin_lock(&key_serial_lock);
+
+       /* propose a likely serial number and look for a hole for it in the
+        * serial number tree */
+       key->serial = key_serial_next;
+       if (key->serial < 3)
+               key->serial = 3;
+       key_serial_next = key->serial + 1;
+
+       parent = NULL;
+       p = &key_serial_tree.rb_node;
+
+       while (*p) {
+               parent = *p;
+               xkey = rb_entry(parent, struct key, serial_node);
+
+               if (key->serial < xkey->serial)
+                       p = &(*p)->rb_left;
+               else if (key->serial > xkey->serial)
+                       p = &(*p)->rb_right;
+               else
+                       goto serial_exists;
+       }
+       goto insert_here;
+
+       /* we found a key with the proposed serial number - walk the tree from
+        * that point looking for the next unused serial number */
+ serial_exists:
+       for (;;) {
+               key->serial = key_serial_next;
+               if (key->serial < 2)
+                       key->serial = 2;
+               key_serial_next = key->serial + 1;
+
+               if (!parent->rb_parent)
+                       p = &key_serial_tree.rb_node;
+               else if (parent->rb_parent->rb_left == parent)
+                       p = &parent->rb_parent->rb_left;
+               else
+                       p = &parent->rb_parent->rb_right;
+
+               parent = rb_next(parent);
+               if (!parent)
+                       break;
+
+               xkey = rb_entry(parent, struct key, serial_node);
+               if (key->serial < xkey->serial)
+                       goto insert_here;
+       }
+
+       /* we've found a suitable hole - arrange for this key to occupy it */
+ insert_here:
+       rb_link_node(&key->serial_node, parent, p);
+       rb_insert_color(&key->serial_node, &key_serial_tree);
+
+       spin_unlock(&key_serial_lock);
+
+} /* end key_alloc_serial() */
+
+/*****************************************************************************/
+/*
+ * allocate a key of the specified type
+ * - update the user's quota to reflect the existence of the key
+ * - called from a key-type operation with key_types_sem read-locked by either
+ *   key_create_or_update() or by key_duplicate(); this prevents unregistration
+ *   of the key type
+ * - upon return the key is as yet uninstantiated; the caller needs to either
+ *   instantiate the key or discard it before returning
+ */
+struct key *key_alloc(struct key_type *type, const char *desc,
+                     uid_t uid, gid_t gid, key_perm_t perm,
+                     int not_in_quota)
+{
+       struct key_user *user = NULL;
+       struct key *key;
+       size_t desclen, quotalen;
+
+       key = ERR_PTR(-EINVAL);
+       if (!desc || !*desc)
+               goto error;
+
+       desclen = strlen(desc) + 1;
+       quotalen = desclen + type->def_datalen;
+
+       /* get hold of the key tracking for this user */
+       user = key_user_lookup(uid);
+       if (!user)
+               goto no_memory_1;
+
+       /* check that the user's quota permits allocation of another key and
+        * its description */
+       if (!not_in_quota) {
+               spin_lock(&user->lock);
+               if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS &&
+                   user->qnbytes + quotalen >= KEYQUOTA_MAX_BYTES
+                   )
+                       goto no_quota;
+
+               user->qnkeys++;
+               user->qnbytes += quotalen;
+               spin_unlock(&user->lock);
+       }
+
+       /* allocate and initialise the key and its description */
+       key = kmem_cache_alloc(key_jar, SLAB_KERNEL);
+       if (!key)
+               goto no_memory_2;
+
+       if (desc) {
+               key->description = kmalloc(desclen, GFP_KERNEL);
+               if (!key->description)
+                       goto no_memory_3;
+
+               memcpy(key->description, desc, desclen);
+       }
+
+       atomic_set(&key->usage, 1);
+       rwlock_init(&key->lock);
+       init_rwsem(&key->sem);
+       key->type = type;
+       key->user = user;
+       key->quotalen = quotalen;
+       key->datalen = type->def_datalen;
+       key->uid = uid;
+       key->gid = gid;
+       key->perm = perm;
+       key->flags = 0;
+       key->expiry = 0;
+       key->payload.data = NULL;
+
+       if (!not_in_quota)
+               key->flags |= KEY_FLAG_IN_QUOTA;
+
+       memset(&key->type_data, 0, sizeof(key->type_data));
+
+#ifdef KEY_DEBUGGING
+       key->magic = KEY_DEBUG_MAGIC;
+#endif
+
+       /* publish the key by giving it a serial number */
+       atomic_inc(&user->nkeys);
+       key_alloc_serial(key);
+
+ error:
+       return key;
+
+ no_memory_3:
+       kmem_cache_free(key_jar, key);
+ no_memory_2:
+       if (!not_in_quota) {
+               spin_lock(&user->lock);
+               user->qnkeys--;
+               user->qnbytes -= quotalen;
+               spin_unlock(&user->lock);
+       }
+       key_user_put(user);
+ no_memory_1:
+       key = ERR_PTR(-ENOMEM);
+       goto error;
+
+ no_quota:
+       spin_unlock(&user->lock);
+       key_user_put(user);
+       key = ERR_PTR(-EDQUOT);
+       goto error;
+
+} /* end key_alloc() */
+
+EXPORT_SYMBOL(key_alloc);
+
+/*****************************************************************************/
+/*
+ * reserve an amount of quota for the key's payload
+ */
+int key_payload_reserve(struct key *key, size_t datalen)
+{
+       int delta = (int) datalen - key->datalen;
+       int ret = 0;
+
+       key_check(key);
+
+       /* contemplate the quota adjustment */
+       if (delta != 0 && key->flags & KEY_FLAG_IN_QUOTA) {
+               spin_lock(&key->user->lock);
+
+               if (delta > 0 &&
+                   key->user->qnbytes + delta > KEYQUOTA_MAX_BYTES
+                   ) {
+                       ret = -EDQUOT;
+               }
+               else {
+                       key->user->qnbytes += delta;
+                       key->quotalen += delta;
+               }
+               spin_unlock(&key->user->lock);
+       }
+
+       /* change the recorded data length if that didn't generate an error */
+       if (ret == 0)
+               key->datalen = datalen;
+
+       return ret;
+
+} /* end key_payload_reserve() */
+
+EXPORT_SYMBOL(key_payload_reserve);
+
+/*****************************************************************************/
+/*
+ * instantiate a key and link it into the target keyring atomically
+ * - called with the target keyring's semaphore writelocked
+ */
+static int __key_instantiate_and_link(struct key *key,
+                                     const void *data,
+                                     size_t datalen,
+                                     struct key *keyring)
+{
+       int ret, awaken;
+
+       key_check(key);
+       key_check(keyring);
+
+       awaken = 0;
+       ret = -EBUSY;
+
+       down_write(&key_construction_sem);
+
+       /* can't instantiate twice */
+       if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
+               /* instantiate the key */
+               ret = key->type->instantiate(key, data, datalen);
+
+               if (ret == 0) {
+                       /* mark the key as being instantiated */
+                       write_lock(&key->lock);
+
+                       atomic_inc(&key->user->nikeys);
+                       key->flags |= KEY_FLAG_INSTANTIATED;
+
+                       if (key->flags & KEY_FLAG_USER_CONSTRUCT) {
+                               key->flags &= ~KEY_FLAG_USER_CONSTRUCT;
+                               awaken = 1;
+                       }
+
+                       write_unlock(&key->lock);
+
+                       /* and link it into the destination keyring */
+                       if (keyring)
+                               ret = __key_link(keyring, key);
+               }
+       }
+
+       up_write(&key_construction_sem);
+
+       /* wake up anyone waiting for a key to be constructed */
+       if (awaken)
+               wake_up_all(&request_key_conswq);
+
+       return ret;
+
+} /* end __key_instantiate_and_link() */
+
+/*****************************************************************************/
+/*
+ * instantiate a key and link it into the target keyring atomically
+ */
+int key_instantiate_and_link(struct key *key,
+                            const void *data,
+                            size_t datalen,
+                            struct key *keyring)
+{
+       int ret;
+
+       if (keyring)
+               down_write(&keyring->sem);
+
+       ret = __key_instantiate_and_link(key, data, datalen, keyring);
+
+       if (keyring)
+               up_write(&keyring->sem);
+
+       return ret;
+} /* end key_instantiate_and_link() */
+
+EXPORT_SYMBOL(key_instantiate_and_link);
+
+/*****************************************************************************/
+/*
+ * negatively instantiate a key and link it into the target keyring atomically
+ */
+int key_negate_and_link(struct key *key,
+                       unsigned timeout,
+                       struct key *keyring)
+{
+       struct timespec now;
+       int ret, awaken;
+
+       key_check(key);
+       key_check(keyring);
+
+       awaken = 0;
+       ret = -EBUSY;
+
+       if (keyring)
+               down_write(&keyring->sem);
+
+       down_write(&key_construction_sem);
+
+       /* can't instantiate twice */
+       if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
+               /* mark the key as being negatively instantiated */
+               write_lock(&key->lock);
+
+               atomic_inc(&key->user->nikeys);
+               key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE;
+               now = current_kernel_time();
+               key->expiry = now.tv_sec + timeout;
+
+               if (key->flags & KEY_FLAG_USER_CONSTRUCT) {
+                       key->flags &= ~KEY_FLAG_USER_CONSTRUCT;
+                       awaken = 1;
+               }
+
+               write_unlock(&key->lock);
+               ret = 0;
+
+               /* and link it into the destination keyring */
+               if (keyring)
+                       ret = __key_link(keyring, key);
+       }
+
+       up_write(&key_construction_sem);
+
+       if (keyring)
+               up_write(&keyring->sem);
+
+       /* wake up anyone waiting for a key to be constructed */
+       if (awaken)
+               wake_up_all(&request_key_conswq);
+
+       return ret;
+
+} /* end key_negate_and_link() */
+
+EXPORT_SYMBOL(key_negate_and_link);
+
+/*****************************************************************************/
+/*
+ * do cleaning up in process context so that we don't have to disable
+ * interrupts all over the place
+ */
+static void key_cleanup(void *data)
+{
+       struct rb_node *_n;
+       struct key *key;
+
+ go_again:
+       /* look for a dead key in the tree */
+       spin_lock(&key_serial_lock);
+
+       for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
+               key = rb_entry(_n, struct key, serial_node);
+
+               if (atomic_read(&key->usage) == 0)
+                       goto found_dead_key;
+       }
+
+       spin_unlock(&key_serial_lock);
+       return;
+
+ found_dead_key:
+       /* we found a dead key - once we've removed it from the tree, we can
+        * drop the lock */
+       rb_erase(&key->serial_node, &key_serial_tree);
+       spin_unlock(&key_serial_lock);
+
+       /* deal with the user's key tracking and quota */
+       if (key->flags & KEY_FLAG_IN_QUOTA) {
+               spin_lock(&key->user->lock);
+               key->user->qnkeys--;
+               key->user->qnbytes -= key->quotalen;
+               spin_unlock(&key->user->lock);
+       }
+
+       atomic_dec(&key->user->nkeys);
+       if (key->flags & KEY_FLAG_INSTANTIATED)
+               atomic_dec(&key->user->nikeys);
+
+       key_user_put(key->user);
+
+       /* now throw away the key memory */
+       if (key->type->destroy)
+               key->type->destroy(key);
+
+       kfree(key->description);
+
+#ifdef KEY_DEBUGGING
+       key->magic = KEY_DEBUG_MAGIC_X;
+#endif
+       kmem_cache_free(key_jar, key);
+
+       /* there may, of course, be more than one key to destroy */
+       goto go_again;
+
+} /* end key_cleanup() */
+
+/*****************************************************************************/
+/*
+ * dispose of a reference to a key
+ * - when all the references are gone, we schedule the cleanup task to come and
+ *   pull it out of the tree in definite process context
+ */
+void key_put(struct key *key)
+{
+       if (key) {
+               key_check(key);
+
+               if (atomic_dec_and_test(&key->usage))
+                       schedule_work(&key_cleanup_task);
+       }
+
+} /* end key_put() */
+
+EXPORT_SYMBOL(key_put);
+
+/*****************************************************************************/
+/*
+ * find a key by its serial number
+ */
+struct key *key_lookup(key_serial_t id)
+{
+       struct rb_node *n;
+       struct key *key;
+
+       spin_lock(&key_serial_lock);
+
+       /* search the tree for the specified key */
+       n = key_serial_tree.rb_node;
+       while (n) {
+               key = rb_entry(n, struct key, serial_node);
+
+               if (id < key->serial)
+                       n = n->rb_left;
+               else if (id > key->serial)
+                       n = n->rb_right;
+               else
+                       goto found;
+       }
+
+ not_found:
+       key = ERR_PTR(-ENOKEY);
+       goto error;
+
+ found:
+       /* pretent doesn't exist if it's dead */
+       if (atomic_read(&key->usage) == 0 ||
+           (key->flags & KEY_FLAG_DEAD) ||
+           key->type == &key_type_dead)
+               goto not_found;
+
+       /* this races with key_put(), but that doesn't matter since key_put()
+        * doesn't actually change the key
+        */
+       atomic_inc(&key->usage);
+
+ error:
+       spin_unlock(&key_serial_lock);
+       return key;
+
+} /* end key_lookup() */
+
+/*****************************************************************************/
+/*
+ * find and lock the specified key type against removal
+ * - we return with the sem readlocked
+ */
+struct key_type *key_type_lookup(const char *type)
+{
+       struct key_type *ktype;
+
+       down_read(&key_types_sem);
+
+       /* look up the key type to see if it's one of the registered kernel
+        * types */
+       list_for_each_entry(ktype, &key_types_list, link) {
+               if (strcmp(ktype->name, type) == 0)
+                       goto found_kernel_type;
+       }
+
+       up_read(&key_types_sem);
+       ktype = ERR_PTR(-ENOKEY);
+
+ found_kernel_type:
+       return ktype;
+
+} /* end key_type_lookup() */
+
+/*****************************************************************************/
+/*
+ * unlock a key type
+ */
+void key_type_put(struct key_type *ktype)
+{
+       up_read(&key_types_sem);
+
+} /* end key_type_put() */
+
+/*****************************************************************************/
+/*
+ * attempt to update an existing key
+ * - the key has an incremented refcount
+ * - we need to put the key if we get an error
+ */
+static inline struct key *__key_update(struct key *key, const void *payload,
+                                      size_t plen)
+{
+       int ret;
+
+       /* need write permission on the key to update it */
+       ret = -EACCES;
+       if (!key_permission(key, KEY_WRITE))
+               goto error;
+
+       ret = -EEXIST;
+       if (!key->type->update)
+               goto error;
+
+       down_write(&key->sem);
+
+       ret = key->type->update(key, payload, plen);
+
+       if (ret == 0) {
+               /* updating a negative key instantiates it */
+               write_lock(&key->lock);
+               key->flags &= ~KEY_FLAG_NEGATIVE;
+               write_unlock(&key->lock);
+       }
+
+       up_write(&key->sem);
+
+       if (ret < 0)
+               goto error;
+ out:
+       return key;
+
+ error:
+       key_put(key);
+       key = ERR_PTR(ret);
+       goto out;
+
+} /* end __key_update() */
+
+/*****************************************************************************/
+/*
+ * search the specified keyring for a key of the same description; if one is
+ * found, update it, otherwise add a new one
+ */
+struct key *key_create_or_update(struct key *keyring,
+                                const char *type,
+                                const char *description,
+                                const void *payload,
+                                size_t plen,
+                                int not_in_quota)
+{
+       struct key_type *ktype;
+       struct key *key = NULL;
+       key_perm_t perm;
+       int ret;
+
+       key_check(keyring);
+
+       /* look up the key type to see if it's one of the registered kernel
+        * types */
+       ktype = key_type_lookup(type);
+       if (IS_ERR(ktype)) {
+               key = ERR_PTR(-ENODEV);
+               goto error;
+       }
+
+       ret = -EINVAL;
+       if (!ktype->match || !ktype->instantiate)
+               goto error_2;
+
+       /* search for an existing key of the same type and description in the
+        * destination keyring
+        */
+       down_write(&keyring->sem);
+
+       key = __keyring_search_one(keyring, ktype, description, 0);
+       if (!IS_ERR(key))
+               goto found_matching_key;
+
+       /* if we're going to allocate a new key, we're going to have to modify
+        * the keyring */
+       ret = -EACCES;
+       if (!key_permission(keyring, KEY_WRITE))
+               goto error_3;
+
+       /* decide on the permissions we want */
+       perm = KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK;
+
+       if (ktype->read)
+               perm |= KEY_USR_READ;
+
+       if (ktype == &key_type_keyring || ktype->update)
+               perm |= KEY_USR_WRITE;
+
+       /* allocate a new key */
+       key = key_alloc(ktype, description, current->fsuid, current->fsgid,
+                       perm, not_in_quota);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error_3;
+       }
+
+       /* instantiate it and link it into the target keyring */
+       ret = __key_instantiate_and_link(key, payload, plen, keyring);
+       if (ret < 0) {
+               key_put(key);
+               key = ERR_PTR(ret);
+       }
+
+ error_3:
+       up_write(&keyring->sem);
+ error_2:
+       key_type_put(ktype);
+ error:
+       return key;
+
+ found_matching_key:
+       /* we found a matching key, so we're going to try to update it
+        * - we can drop the locks first as we have the key pinned
+        */
+       up_write(&keyring->sem);
+       key_type_put(ktype);
+
+       key = __key_update(key, payload, plen);
+       goto error;
+
+} /* end key_create_or_update() */
+
+EXPORT_SYMBOL(key_create_or_update);
+
+/*****************************************************************************/
+/*
+ * update a key
+ */
+int key_update(struct key *key, const void *payload, size_t plen)
+{
+       int ret;
+
+       key_check(key);
+
+       /* the key must be writable */
+       ret = -EACCES;
+       if (!key_permission(key, KEY_WRITE))
+               goto error;
+
+       /* attempt to update it if supported */
+       ret = -EOPNOTSUPP;
+       if (key->type->update) {
+               down_write(&key->sem);
+               ret = key->type->update(key, payload, plen);
+
+               if (ret == 0) {
+                       /* updating a negative key instantiates it */
+                       write_lock(&key->lock);
+                       key->flags &= ~KEY_FLAG_NEGATIVE;
+                       write_unlock(&key->lock);
+               }
+
+               up_write(&key->sem);
+       }
+
+ error:
+       return ret;
+
+} /* end key_update() */
+
+EXPORT_SYMBOL(key_update);
+
+/*****************************************************************************/
+/*
+ * duplicate a key, potentially with a revised description
+ * - must be supported by the keytype (keyrings for instance can be duplicated)
+ */
+struct key *key_duplicate(struct key *source, const char *desc)
+{
+       struct key *key;
+       int ret;
+
+       key_check(source);
+
+       if (!desc)
+               desc = source->description;
+
+       down_read(&key_types_sem);
+
+       ret = -EINVAL;
+       if (!source->type->duplicate)
+               goto error;
+
+       /* allocate and instantiate a key */
+       key = key_alloc(source->type, desc, current->fsuid, current->fsgid,
+                       source->perm, 0);
+       if (IS_ERR(key))
+               goto error_k;
+
+       down_read(&source->sem);
+       ret = key->type->duplicate(key, source);
+       up_read(&source->sem);
+       if (ret < 0)
+               goto error2;
+
+       atomic_inc(&key->user->nikeys);
+
+       write_lock(&key->lock);
+       key->flags |= KEY_FLAG_INSTANTIATED;
+       write_unlock(&key->lock);
+
+ error_k:
+       up_read(&key_types_sem);
+ out:
+       return key;
+
+ error2:
+       key_put(key);
+ error:
+       up_read(&key_types_sem);
+       key = ERR_PTR(ret);
+       goto out;
+
+} /* end key_duplicate() */
+
+/*****************************************************************************/
+/*
+ * revoke a key
+ */
+void key_revoke(struct key *key)
+{
+       key_check(key);
+
+       /* make sure no one's trying to change or use the key when we mark
+        * it */
+       down_write(&key->sem);
+       write_lock(&key->lock);
+       key->flags |= KEY_FLAG_REVOKED;
+       write_unlock(&key->lock);
+       up_write(&key->sem);
+
+} /* end key_revoke() */
+
+EXPORT_SYMBOL(key_revoke);
+
+/*****************************************************************************/
+/*
+ * register a type of key
+ */
+int register_key_type(struct key_type *ktype)
+{
+       struct key_type *p;
+       int ret;
+
+       ret = -EEXIST;
+       down_write(&key_types_sem);
+
+       /* disallow key types with the same name */
+       list_for_each_entry(p, &key_types_list, link) {
+               if (strcmp(p->name, ktype->name) == 0)
+                       goto out;
+       }
+
+       /* store the type */
+       list_add(&ktype->link, &key_types_list);
+       ret = 0;
+
+ out:
+       up_write(&key_types_sem);
+       return ret;
+
+} /* end register_key_type() */
+
+EXPORT_SYMBOL(register_key_type);
+
+/*****************************************************************************/
+/*
+ * unregister a type of key
+ */
+void unregister_key_type(struct key_type *ktype)
+{
+       struct rb_node *_n;
+       struct key *key;
+
+       down_write(&key_types_sem);
+
+       /* withdraw the key type */
+       list_del_init(&ktype->link);
+
+       /* need to withdraw all keys of this type */
+       spin_lock(&key_serial_lock);
+
+       for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
+               key = rb_entry(_n, struct key, serial_node);
+
+               if (key->type != ktype)
+                       continue;
+
+               write_lock(&key->lock);
+               key->type = &key_type_dead;
+               write_unlock(&key->lock);
+
+               /* there shouldn't be anyone looking at the description or
+                * payload now */
+               if (ktype->destroy)
+                       ktype->destroy(key);
+               memset(&key->payload, 0xbd, sizeof(key->payload));
+       }
+
+       spin_unlock(&key_serial_lock);
+       up_write(&key_types_sem);
+
+} /* end unregister_key_type() */
+
+EXPORT_SYMBOL(unregister_key_type);
+
+/*****************************************************************************/
+/*
+ * initialise the key management stuff
+ */
+void __init key_init(void)
+{
+       /* allocate a slab in which we can store keys */
+       key_jar = kmem_cache_create("key_jar", sizeof(struct key),
+                       0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
+
+       /* add the special key types */
+       list_add_tail(&key_type_keyring.link, &key_types_list);
+       list_add_tail(&key_type_dead.link, &key_types_list);
+       list_add_tail(&key_type_user.link, &key_types_list);
+
+       /* record the root user tracking */
+       rb_link_node(&root_key_user.node,
+                    NULL,
+                    &key_user_tree.rb_node);
+
+       rb_insert_color(&root_key_user.node,
+                       &key_user_tree);
+
+       /* record root's user standard keyrings */
+       key_check(&root_user_keyring);
+       key_check(&root_session_keyring);
+
+       __key_insert_serial(&root_user_keyring);
+       __key_insert_serial(&root_session_keyring);
+
+       keyring_publish_name(&root_user_keyring);
+       keyring_publish_name(&root_session_keyring);
+
+       /* link the two root keyrings together */
+       key_link(&root_session_keyring, &root_user_keyring);
+} /* end key_init() */
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
new file mode 100644 (file)
index 0000000..4d95fdb
--- /dev/null
@@ -0,0 +1,987 @@
+/* keyctl.c: userspace keyctl operations
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/keyctl.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/*****************************************************************************/
+/*
+ * extract the description of a new key from userspace and either add it as a
+ * new key to the specified keyring or update a matching key in that keyring
+ * - the keyring must be writable
+ * - returns the new key's serial number
+ * - implements add_key()
+ */
+asmlinkage long sys_add_key(const char __user *_type,
+                           const char __user *_description,
+                           const void __user *_payload,
+                           size_t plen,
+                           key_serial_t ringid)
+{
+       struct key *keyring, *key;
+       char type[32], *description;
+       void *payload;
+       long dlen, ret;
+
+       ret = -EINVAL;
+       if (plen > 32767)
+               goto error;
+
+       /* draw all the data into kernel space */
+       ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+       if (ret < 0)
+               goto error;
+       type[31] = '\0';
+
+       ret = -EFAULT;
+       dlen = strnlen_user(_description, PAGE_SIZE - 1);
+       if (dlen <= 0)
+               goto error;
+
+       ret = -EINVAL;
+       if (dlen > PAGE_SIZE - 1)
+               goto error;
+
+       ret = -ENOMEM;
+       description = kmalloc(dlen + 1, GFP_KERNEL);
+       if (!description)
+               goto error;
+
+       ret = -EFAULT;
+       if (copy_from_user(description, _description, dlen + 1) != 0)
+               goto error2;
+
+       /* pull the payload in if one was supplied */
+       payload = NULL;
+
+       if (_payload) {
+               ret = -ENOMEM;
+               payload = kmalloc(plen, GFP_KERNEL);
+               if (!payload)
+                       goto error2;
+
+               ret = -EFAULT;
+               if (copy_from_user(payload, _payload, plen) != 0)
+                       goto error3;
+       }
+
+       /* find the target keyring (which must be writable) */
+       keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+       if (IS_ERR(keyring)) {
+               ret = PTR_ERR(keyring);
+               goto error3;
+       }
+
+       /* create or update the requested key and add it to the target
+        * keyring */
+       key = key_create_or_update(keyring, type, description,
+                                  payload, plen, 0);
+       if (!IS_ERR(key)) {
+               ret = key->serial;
+               key_put(key);
+       }
+       else {
+               ret = PTR_ERR(key);
+       }
+
+       key_put(keyring);
+ error3:
+       kfree(payload);
+ error2:
+       kfree(description);
+ error:
+       return ret;
+
+} /* end sys_add_key() */
+
+/*****************************************************************************/
+/*
+ * search the process keyrings for a matching key
+ * - nested keyrings may also be searched if they have Search permission
+ * - if a key is found, it will be attached to the destination keyring if
+ *   there's one specified
+ * - /sbin/request-key will be invoked if _callout_info is non-NULL
+ *   - the _callout_info string will be passed to /sbin/request-key
+ *   - if the _callout_info string is empty, it will be rendered as "-"
+ * - implements request_key()
+ */
+asmlinkage long sys_request_key(const char __user *_type,
+                               const char __user *_description,
+                               const char __user *_callout_info,
+                               key_serial_t destringid)
+{
+       struct key_type *ktype;
+       struct key *key, *dest;
+       char type[32], *description, *callout_info;
+       long dlen, ret;
+
+       /* pull the type into kernel space */
+       ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+       if (ret < 0)
+               goto error;
+       type[31] = '\0';
+
+       /* pull the description into kernel space */
+       ret = -EFAULT;
+       dlen = strnlen_user(_description, PAGE_SIZE - 1);
+       if (dlen <= 0)
+               goto error;
+
+       ret = -EINVAL;
+       if (dlen > PAGE_SIZE - 1)
+               goto error;
+
+       ret = -ENOMEM;
+       description = kmalloc(dlen + 1, GFP_KERNEL);
+       if (!description)
+               goto error;
+
+       ret = -EFAULT;
+       if (copy_from_user(description, _description, dlen + 1) != 0)
+               goto error2;
+
+       /* pull the callout info into kernel space */
+       callout_info = NULL;
+       if (_callout_info) {
+               ret = -EFAULT;
+               dlen = strnlen_user(_callout_info, PAGE_SIZE - 1);
+               if (dlen <= 0)
+                       goto error2;
+
+               ret = -EINVAL;
+               if (dlen > PAGE_SIZE - 1)
+                       goto error2;
+
+               ret = -ENOMEM;
+               callout_info = kmalloc(dlen + 1, GFP_KERNEL);
+               if (!callout_info)
+                       goto error2;
+
+               ret = -EFAULT;
+               if (copy_from_user(callout_info, _callout_info, dlen + 1) != 0)
+                       goto error3;
+       }
+
+       /* get the destination keyring if specified */
+       dest = NULL;
+       if (destringid) {
+               dest = lookup_user_key(destringid, 1, 0, KEY_WRITE);
+               if (IS_ERR(dest)) {
+                       ret = PTR_ERR(dest);
+                       goto error3;
+               }
+       }
+
+       /* find the key type */
+       ktype = key_type_lookup(type);
+       if (IS_ERR(ktype)) {
+               ret = PTR_ERR(ktype);
+               goto error4;
+       }
+
+       /* do the search */
+       key = request_key(ktype, description, callout_info);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error5;
+       }
+
+       /* link the resulting key to the destination keyring */
+       if (dest) {
+               ret = key_link(dest, key);
+               if (ret < 0)
+                       goto error6;
+       }
+
+       ret = key->serial;
+
+ error6:
+       key_put(key);
+ error5:
+       key_type_put(ktype);
+ error4:
+       key_put(dest);
+ error3:
+       kfree(callout_info);
+ error2:
+       kfree(description);
+ error:
+       return ret;
+
+} /* end sys_request_key() */
+
+/*****************************************************************************/
+/*
+ * get the ID of the specified process keyring
+ * - the keyring must have search permission to be found
+ * - implements keyctl(KEYCTL_GET_KEYRING_ID)
+ */
+long keyctl_get_keyring_ID(key_serial_t id, int create)
+{
+       struct key *key;
+       long ret;
+
+       key = lookup_user_key(id, create, 0, KEY_SEARCH);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error;
+       }
+
+       ret = key->serial;
+       key_put(key);
+ error:
+       return ret;
+
+} /* end keyctl_get_keyring_ID() */
+
+/*****************************************************************************/
+/*
+ * join the session keyring
+ * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING)
+ */
+long keyctl_join_session_keyring(const char __user *_name)
+{
+       char *name;
+       long nlen, ret;
+
+       /* fetch the name from userspace */
+       name = NULL;
+       if (_name) {
+               ret = -EFAULT;
+               nlen = strnlen_user(_name, PAGE_SIZE - 1);
+               if (nlen <= 0)
+                       goto error;
+
+               ret = -EINVAL;
+               if (nlen > PAGE_SIZE - 1)
+                       goto error;
+
+               ret = -ENOMEM;
+               name = kmalloc(nlen + 1, GFP_KERNEL);
+               if (!name)
+                       goto error;
+
+               ret = -EFAULT;
+               if (copy_from_user(name, _name, nlen + 1) != 0)
+                       goto error2;
+       }
+
+       /* join the session */
+       ret = join_session_keyring(name);
+
+ error2:
+       kfree(name);
+ error:
+       return ret;
+
+} /* end keyctl_join_session_keyring() */
+
+/*****************************************************************************/
+/*
+ * update a key's data payload
+ * - the key must be writable
+ * - implements keyctl(KEYCTL_UPDATE)
+ */
+long keyctl_update_key(key_serial_t id,
+                      const void __user *_payload,
+                      size_t plen)
+{
+       struct key *key;
+       void *payload;
+       long ret;
+
+       ret = -EINVAL;
+       if (plen > PAGE_SIZE)
+               goto error;
+
+       /* pull the payload in if one was supplied */
+       payload = NULL;
+       if (_payload) {
+               ret = -ENOMEM;
+               payload = kmalloc(plen, GFP_KERNEL);
+               if (!payload)
+                       goto error;
+
+               ret = -EFAULT;
+               if (copy_from_user(payload, _payload, plen) != 0)
+                       goto error2;
+       }
+
+       /* find the target key (which must be writable) */
+       key = lookup_user_key(id, 0, 0, KEY_WRITE);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error2;
+       }
+
+       /* update the key */
+       ret = key_update(key, payload, plen);
+
+       key_put(key);
+ error2:
+       kfree(payload);
+ error:
+       return ret;
+
+} /* end keyctl_update_key() */
+
+/*****************************************************************************/
+/*
+ * revoke a key
+ * - the key must be writable
+ * - implements keyctl(KEYCTL_REVOKE)
+ */
+long keyctl_revoke_key(key_serial_t id)
+{
+       struct key *key;
+       long ret;
+
+       key = lookup_user_key(id, 0, 0, KEY_WRITE);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error;
+       }
+
+       key_revoke(key);
+       ret = 0;
+
+       key_put(key);
+ error:
+       return 0;
+
+} /* end keyctl_revoke_key() */
+
+/*****************************************************************************/
+/*
+ * clear the specified process keyring
+ * - the keyring must be writable
+ * - implements keyctl(KEYCTL_CLEAR)
+ */
+long keyctl_keyring_clear(key_serial_t ringid)
+{
+       struct key *keyring;
+       long ret;
+
+       keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+       if (IS_ERR(keyring)) {
+               ret = PTR_ERR(keyring);
+               goto error;
+       }
+
+       ret = keyring_clear(keyring);
+
+       key_put(keyring);
+ error:
+       return ret;
+
+} /* end keyctl_keyring_clear() */
+
+/*****************************************************************************/
+/*
+ * link a key into a keyring
+ * - the keyring must be writable
+ * - the key must be linkable
+ * - implements keyctl(KEYCTL_LINK)
+ */
+long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
+{
+       struct key *keyring, *key;
+       long ret;
+
+       keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+       if (IS_ERR(keyring)) {
+               ret = PTR_ERR(keyring);
+               goto error;
+       }
+
+       key = lookup_user_key(id, 1, 0, KEY_LINK);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error2;
+       }
+
+       ret = key_link(keyring, key);
+
+       key_put(key);
+ error2:
+       key_put(keyring);
+ error:
+       return ret;
+
+} /* end keyctl_keyring_link() */
+
+/*****************************************************************************/
+/*
+ * unlink the first attachment of a key from a keyring
+ * - the keyring must be writable
+ * - we don't need any permissions on the key
+ * - implements keyctl(KEYCTL_UNLINK)
+ */
+long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
+{
+       struct key *keyring, *key;
+       long ret;
+
+       keyring = lookup_user_key(ringid, 0, 0, KEY_WRITE);
+       if (IS_ERR(keyring)) {
+               ret = PTR_ERR(keyring);
+               goto error;
+       }
+
+       key = lookup_user_key(id, 0, 0, 0);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error2;
+       }
+
+       ret = key_unlink(keyring, key);
+
+       key_put(key);
+ error2:
+       key_put(keyring);
+ error:
+       return ret;
+
+} /* end keyctl_keyring_unlink() */
+
+/*****************************************************************************/
+/*
+ * describe a user key
+ * - the key must have view permission
+ * - if there's a buffer, we place up to buflen bytes of data into it
+ * - unless there's an error, we return the amount of description available,
+ *   irrespective of how much we may have copied
+ * - the description is formatted thus:
+ *     type;uid;gid;perm;description<NUL>
+ * - implements keyctl(KEYCTL_DESCRIBE)
+ */
+long keyctl_describe_key(key_serial_t keyid,
+                        char __user *buffer,
+                        size_t buflen)
+{
+       struct key *key;
+       char *tmpbuf;
+       long ret;
+
+       key = lookup_user_key(keyid, 0, 1, KEY_VIEW);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error;
+       }
+
+       /* calculate how much description we're going to return */
+       ret = -ENOMEM;
+       tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!tmpbuf)
+               goto error2;
+
+       ret = snprintf(tmpbuf, PAGE_SIZE - 1,
+                      "%s;%d;%d;%06x;%s",
+                      key->type->name,
+                      key->uid,
+                      key->gid,
+                      key->perm,
+                      key->description ? key->description :""
+                      );
+
+       /* include a NUL char at the end of the data */
+       if (ret > PAGE_SIZE - 1)
+               ret = PAGE_SIZE - 1;
+       tmpbuf[ret] = 0;
+       ret++;
+
+       /* consider returning the data */
+       if (buffer && buflen > 0) {
+               if (buflen > ret)
+                       buflen = ret;
+
+               if (copy_to_user(buffer, tmpbuf, buflen) != 0)
+                       ret = -EFAULT;
+       }
+
+       kfree(tmpbuf);
+ error2:
+       key_put(key);
+ error:
+       return ret;
+
+} /* end keyctl_describe_key() */
+
+/*****************************************************************************/
+/*
+ * search the specified keyring for a matching key
+ * - the start keyring must be searchable
+ * - nested keyrings may also be searched if they are searchable
+ * - only keys with search permission may be found
+ * - if a key is found, it will be attached to the destination keyring if
+ *   there's one specified
+ * - implements keyctl(KEYCTL_SEARCH)
+ */
+long keyctl_keyring_search(key_serial_t ringid,
+                          const char __user *_type,
+                          const char __user *_description,
+                          key_serial_t destringid)
+{
+       struct key_type *ktype;
+       struct key *keyring, *key, *dest;
+       char type[32], *description;
+       long dlen, ret;
+
+       /* pull the type and description into kernel space */
+       ret = strncpy_from_user(type, _type, sizeof(type) - 1);
+       if (ret < 0)
+               goto error;
+       type[31] = '\0';
+
+       ret = -EFAULT;
+       dlen = strnlen_user(_description, PAGE_SIZE - 1);
+       if (dlen <= 0)
+               goto error;
+
+       ret = -EINVAL;
+       if (dlen > PAGE_SIZE - 1)
+               goto error;
+
+       ret = -ENOMEM;
+       description = kmalloc(dlen + 1, GFP_KERNEL);
+       if (!description)
+               goto error;
+
+       ret = -EFAULT;
+       if (copy_from_user(description, _description, dlen + 1) != 0)
+               goto error2;
+
+       /* get the keyring at which to begin the search */
+       keyring = lookup_user_key(ringid, 0, 0, KEY_SEARCH);
+       if (IS_ERR(keyring)) {
+               ret = PTR_ERR(keyring);
+               goto error2;
+       }
+
+       /* get the destination keyring if specified */
+       dest = NULL;
+       if (destringid) {
+               dest = lookup_user_key(destringid, 1, 0, KEY_WRITE);
+               if (IS_ERR(dest)) {
+                       ret = PTR_ERR(dest);
+                       goto error3;
+               }
+       }
+
+       /* find the key type */
+       ktype = key_type_lookup(type);
+       if (IS_ERR(ktype)) {
+               ret = PTR_ERR(ktype);
+               goto error4;
+       }
+
+       /* do the search */
+       key = keyring_search(keyring, ktype, description);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+
+               /* treat lack or presence of a negative key the same */
+               if (ret == -EAGAIN)
+                       ret = -ENOKEY;
+               goto error5;
+       }
+
+       /* link the resulting key to the destination keyring if we can */
+       if (dest) {
+               ret = -EACCES;
+               if (!key_permission(key, KEY_LINK))
+                       goto error6;
+
+               ret = key_link(dest, key);
+               if (ret < 0)
+                       goto error6;
+       }
+
+       ret = key->serial;
+
+ error6:
+       key_put(key);
+ error5:
+       key_type_put(ktype);
+ error4:
+       key_put(dest);
+ error3:
+       key_put(keyring);
+ error2:
+       kfree(description);
+ error:
+       return ret;
+
+} /* end keyctl_keyring_search() */
+
+/*****************************************************************************/
+/*
+ * see if the key we're looking at is the target key
+ */
+static int keyctl_read_key_same(const struct key *key, const void *target)
+{
+       return key == target;
+
+} /* end keyctl_read_key_same() */
+
+/*****************************************************************************/
+/*
+ * read a user key's payload
+ * - the keyring must be readable or the key must be searchable from the
+ *   process's keyrings
+ * - if there's a buffer, we place up to buflen bytes of data into it
+ * - unless there's an error, we return the amount of data in the key,
+ *   irrespective of how much we may have copied
+ * - implements keyctl(KEYCTL_READ)
+ */
+long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
+{
+       struct key *key, *skey;
+       long ret;
+
+       /* find the key first */
+       key = lookup_user_key(keyid, 0, 0, 0);
+       if (!IS_ERR(key)) {
+               /* see if we can read it directly */
+               if (key_permission(key, KEY_READ))
+                       goto can_read_key;
+
+               /* can't; see if it's searchable from this process's
+                * keyrings */
+               ret = -ENOKEY;
+               if (key_permission(key, KEY_SEARCH)) {
+                       /* okay - we do have search permission on the key
+                        * itself, but do we have the key? */
+                       skey = search_process_keyrings_aux(key->type, key,
+                                                          keyctl_read_key_same);
+                       if (!IS_ERR(skey))
+                               goto can_read_key2;
+               }
+
+               goto error2;
+       }
+
+       ret = -ENOKEY;
+       goto error;
+
+       /* the key is probably readable - now try to read it */
+ can_read_key2:
+       key_put(skey);
+ can_read_key:
+       ret = key_validate(key);
+       if (ret == 0) {
+               ret = -EOPNOTSUPP;
+               if (key->type->read) {
+                       /* read the data with the semaphore held (since we
+                        * might sleep) */
+                       down_read(&key->sem);
+                       ret = key->type->read(key, buffer, buflen);
+                       up_read(&key->sem);
+               }
+       }
+
+ error2:
+       key_put(key);
+ error:
+       return ret;
+
+} /* end keyctl_read_key() */
+
+/*****************************************************************************/
+/*
+ * change the ownership of a key
+ * - the keyring owned by the changer
+ * - if the uid or gid is -1, then that parameter is not changed
+ * - implements keyctl(KEYCTL_CHOWN)
+ */
+long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
+{
+       struct key *key;
+       long ret;
+
+       ret = 0;
+       if (uid == (uid_t) -1 && gid == (gid_t) -1)
+               goto error;
+
+       key = lookup_user_key(id, 1, 1, 0);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error;
+       }
+
+       /* make the changes with the locks held to prevent chown/chown races */
+       ret = -EACCES;
+       down_write(&key->sem);
+       write_lock(&key->lock);
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               /* only the sysadmin can chown a key to some other UID */
+               if (uid != (uid_t) -1 && key->uid != uid)
+                       goto no_access;
+
+               /* only the sysadmin can set the key's GID to a group other
+                * than one of those that the current process subscribes to */
+               if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid))
+                       goto no_access;
+       }
+
+       /* change the UID (have to update the quotas) */
+       if (uid != (uid_t) -1 && uid != key->uid) {
+               /* don't support UID changing yet */
+               ret = -EOPNOTSUPP;
+               goto no_access;
+       }
+
+       /* change the GID */
+       if (gid != (gid_t) -1)
+               key->gid = gid;
+
+       ret = 0;
+
+ no_access:
+       write_unlock(&key->lock);
+       up_write(&key->sem);
+       key_put(key);
+ error:
+       return ret;
+
+} /* end keyctl_chown_key() */
+
+/*****************************************************************************/
+/*
+ * change the permission mask on a key
+ * - the keyring owned by the changer
+ * - implements keyctl(KEYCTL_SETPERM)
+ */
+long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
+{
+       struct key *key;
+       long ret;
+
+       ret = -EINVAL;
+       if (perm & ~(KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
+               goto error;
+
+       key = lookup_user_key(id, 1, 1, 0);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error;
+       }
+
+       /* make the changes with the locks held to prevent chown/chmod
+        * races */
+       ret = -EACCES;
+       down_write(&key->sem);
+       write_lock(&key->lock);
+
+       /* if we're not the sysadmin, we can only chmod a key that we
+        * own */
+       if (!capable(CAP_SYS_ADMIN) && key->uid != current->fsuid)
+               goto no_access;
+
+       /* changing the permissions mask */
+       key->perm = perm;
+       ret = 0;
+
+ no_access:
+       write_unlock(&key->lock);
+       up_write(&key->sem);
+       key_put(key);
+ error:
+       return ret;
+
+} /* end keyctl_setperm_key() */
+
+/*****************************************************************************/
+/*
+ * instantiate the key with the specified payload, and, if one is given, link
+ * the key into the keyring
+ */
+long keyctl_instantiate_key(key_serial_t id,
+                           const void __user *_payload,
+                           size_t plen,
+                           key_serial_t ringid)
+{
+       struct key *key, *keyring;
+       void *payload;
+       long ret;
+
+       ret = -EINVAL;
+       if (plen > 32767)
+               goto error;
+
+       /* pull the payload in if one was supplied */
+       payload = NULL;
+
+       if (_payload) {
+               ret = -ENOMEM;
+               payload = kmalloc(plen, GFP_KERNEL);
+               if (!payload)
+                       goto error;
+
+               ret = -EFAULT;
+               if (copy_from_user(payload, _payload, plen) != 0)
+                       goto error2;
+       }
+
+       /* find the target key (which must be writable) */
+       key = lookup_user_key(id, 0, 1, KEY_WRITE);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error2;
+       }
+
+       /* find the destination keyring if present (which must also be
+        * writable) */
+       keyring = NULL;
+       if (ringid) {
+               keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+               if (IS_ERR(keyring)) {
+                       ret = PTR_ERR(keyring);
+                       goto error3;
+               }
+       }
+
+       /* instantiate the key and link it into a keyring */
+       ret = key_instantiate_and_link(key, payload, plen, keyring);
+
+       key_put(keyring);
+ error3:
+       key_put(key);
+ error2:
+       kfree(payload);
+ error:
+       return ret;
+
+} /* end keyctl_instantiate_key() */
+
+/*****************************************************************************/
+/*
+ * negatively instantiate the key with the given timeout (in seconds), and, if
+ * one is given, link the key into the keyring
+ */
+long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
+{
+       struct key *key, *keyring;
+       long ret;
+
+       /* find the target key (which must be writable) */
+       key = lookup_user_key(id, 0, 1, KEY_WRITE);
+       if (IS_ERR(key)) {
+               ret = PTR_ERR(key);
+               goto error;
+       }
+
+       /* find the destination keyring if present (which must also be
+        * writable) */
+       keyring = NULL;
+       if (ringid) {
+               keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE);
+               if (IS_ERR(keyring)) {
+                       ret = PTR_ERR(keyring);
+                       goto error2;
+               }
+       }
+
+       /* instantiate the key and link it into a keyring */
+       ret = key_negate_and_link(key, timeout, keyring);
+
+       key_put(keyring);
+ error2:
+       key_put(key);
+ error:
+       return ret;
+
+} /* end keyctl_negate_key() */
+
+/*****************************************************************************/
+/*
+ * the key control system call
+ */
+asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3,
+                          unsigned long arg4, unsigned long arg5)
+{
+       switch (option) {
+       case KEYCTL_GET_KEYRING_ID:
+               return keyctl_get_keyring_ID((key_serial_t) arg2,
+                                            (int) arg3);
+
+       case KEYCTL_JOIN_SESSION_KEYRING:
+               return keyctl_join_session_keyring((const char __user *) arg3);
+
+       case KEYCTL_UPDATE:
+               return keyctl_update_key((key_serial_t) arg2,
+                                        (const void __user *) arg3,
+                                        (size_t) arg4);
+
+       case KEYCTL_REVOKE:
+               return keyctl_revoke_key((key_serial_t) arg2);
+
+       case KEYCTL_DESCRIBE:
+               return keyctl_describe_key((key_serial_t) arg2,
+                                          (char __user *) arg3,
+                                          (unsigned) arg4);
+
+       case KEYCTL_CLEAR:
+               return keyctl_keyring_clear((key_serial_t) arg2);
+
+       case KEYCTL_LINK:
+               return keyctl_keyring_link((key_serial_t) arg2,
+                                          (key_serial_t) arg3);
+
+       case KEYCTL_UNLINK:
+               return keyctl_keyring_unlink((key_serial_t) arg2,
+                                            (key_serial_t) arg3);
+
+       case KEYCTL_SEARCH:
+               return keyctl_keyring_search((key_serial_t) arg2,
+                                            (const char __user *) arg3,
+                                            (const char __user *) arg4,
+                                            (key_serial_t) arg5);
+
+       case KEYCTL_READ:
+               return keyctl_read_key((key_serial_t) arg2,
+                                      (char __user *) arg3,
+                                      (size_t) arg4);
+
+       case KEYCTL_CHOWN:
+               return keyctl_chown_key((key_serial_t) arg2,
+                                       (uid_t) arg3,
+                                       (gid_t) arg4);
+
+       case KEYCTL_SETPERM:
+               return keyctl_setperm_key((key_serial_t) arg2,
+                                         (key_perm_t) arg3);
+
+       case KEYCTL_INSTANTIATE:
+               return keyctl_instantiate_key((key_serial_t) arg2,
+                                             (const void __user *) arg3,
+                                             (size_t) arg4,
+                                             (key_serial_t) arg5);
+
+       case KEYCTL_NEGATE:
+               return keyctl_negate_key((key_serial_t) arg2,
+                                        (unsigned) arg3,
+                                        (key_serial_t) arg4);
+
+       default:
+               return -EOPNOTSUPP;
+       }
+
+} /* end sys_keyctl() */
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
new file mode 100644 (file)
index 0000000..98d4b0b
--- /dev/null
@@ -0,0 +1,895 @@
+/* keyring.c: keyring handling
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/*
+ * when plumbing the depths of the key tree, this sets a hard limit set on how
+ * deep we're willing to go
+ */
+#define KEYRING_SEARCH_MAX_DEPTH 6
+
+/*
+ * we keep all named keyrings in a hash to speed looking them up
+ */
+#define KEYRING_NAME_HASH_SIZE (1 << 5)
+
+static struct list_head        keyring_name_hash[KEYRING_NAME_HASH_SIZE];
+static rwlock_t                keyring_name_lock = RW_LOCK_UNLOCKED;
+
+static inline unsigned keyring_hash(const char *desc)
+{
+       unsigned bucket = 0;
+
+       for (; *desc; desc++)
+               bucket += (unsigned char) *desc;
+
+       return bucket & (KEYRING_NAME_HASH_SIZE - 1);
+}
+
+/*
+ * the keyring type definition
+ */
+static int keyring_instantiate(struct key *keyring,
+                              const void *data, size_t datalen);
+static int keyring_duplicate(struct key *keyring, const struct key *source);
+static int keyring_match(const struct key *keyring, const void *criterion);
+static void keyring_destroy(struct key *keyring);
+static void keyring_describe(const struct key *keyring, struct seq_file *m);
+static long keyring_read(const struct key *keyring,
+                        char __user *buffer, size_t buflen);
+
+struct key_type key_type_keyring = {
+       .name           = "keyring",
+       .def_datalen    = sizeof(struct keyring_list),
+       .instantiate    = keyring_instantiate,
+       .duplicate      = keyring_duplicate,
+       .match          = keyring_match,
+       .destroy        = keyring_destroy,
+       .describe       = keyring_describe,
+       .read           = keyring_read,
+};
+
+/*
+ * semaphore to serialise link/link calls to prevent two link calls in parallel
+ * introducing a cycle
+ */
+DECLARE_RWSEM(keyring_serialise_link_sem);
+
+/*****************************************************************************/
+/*
+ * publish the name of a keyring so that it can be found by name (if it has
+ * one)
+ */
+void keyring_publish_name(struct key *keyring)
+{
+       int bucket;
+
+       if (keyring->description) {
+               bucket = keyring_hash(keyring->description);
+
+               write_lock(&keyring_name_lock);
+
+               if (!keyring_name_hash[bucket].next)
+                       INIT_LIST_HEAD(&keyring_name_hash[bucket]);
+
+               list_add_tail(&keyring->type_data.link,
+                             &keyring_name_hash[bucket]);
+
+               write_unlock(&keyring_name_lock);
+       }
+
+} /* end keyring_publish_name() */
+
+/*****************************************************************************/
+/*
+ * initialise a keyring
+ * - we object if we were given any data
+ */
+static int keyring_instantiate(struct key *keyring,
+                              const void *data, size_t datalen)
+{
+       int ret;
+
+       ret = -EINVAL;
+       if (datalen == 0) {
+               /* make the keyring available by name if it has one */
+               keyring_publish_name(keyring);
+               ret = 0;
+       }
+
+       return ret;
+
+} /* end keyring_instantiate() */
+
+/*****************************************************************************/
+/*
+ * duplicate the list of subscribed keys from a source keyring into this one
+ */
+static int keyring_duplicate(struct key *keyring, const struct key *source)
+{
+       struct keyring_list *sklist, *klist;
+       unsigned max;
+       size_t size;
+       int loop, ret;
+
+       const unsigned limit =
+               (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key);
+
+       ret = 0;
+       sklist = source->payload.subscriptions;
+
+       if (sklist && sklist->nkeys > 0) {
+               max = sklist->nkeys;
+               BUG_ON(max > limit);
+
+               max = (max + 3) & ~3;
+               if (max > limit)
+                       max = limit;
+
+               ret = -ENOMEM;
+               size = sizeof(*klist) + sizeof(struct key) * max;
+               klist = kmalloc(size, GFP_KERNEL);
+               if (!klist)
+                       goto error;
+
+               klist->maxkeys = max;
+               klist->nkeys = sklist->nkeys;
+               memcpy(klist->keys,
+                      sklist->keys,
+                      sklist->nkeys * sizeof(struct key));
+
+               for (loop = klist->nkeys - 1; loop >= 0; loop--)
+                       atomic_inc(&klist->keys[loop]->usage);
+
+               keyring->payload.subscriptions = klist;
+               ret = 0;
+       }
+
+ error:
+       return ret;
+
+} /* end keyring_duplicate() */
+
+/*****************************************************************************/
+/*
+ * match keyrings on their name
+ */
+static int keyring_match(const struct key *keyring, const void *description)
+{
+       return keyring->description &&
+               strcmp(keyring->description, description) == 0;
+
+} /* end keyring_match() */
+
+/*****************************************************************************/
+/*
+ * dispose of the data dangling from the corpse of a keyring
+ */
+static void keyring_destroy(struct key *keyring)
+{
+       struct keyring_list *klist;
+       int loop;
+
+       if (keyring->description) {
+               write_lock(&keyring_name_lock);
+               list_del(&keyring->type_data.link);
+               write_unlock(&keyring_name_lock);
+       }
+
+       klist = keyring->payload.subscriptions;
+       if (klist) {
+               for (loop = klist->nkeys - 1; loop >= 0; loop--)
+                       key_put(klist->keys[loop]);
+               kfree(klist);
+       }
+
+} /* end keyring_destroy() */
+
+/*****************************************************************************/
+/*
+ * describe the keyring
+ */
+static void keyring_describe(const struct key *keyring, struct seq_file *m)
+{
+       struct keyring_list *klist;
+
+       if (keyring->description) {
+               seq_puts(m, keyring->description);
+       }
+       else {
+               seq_puts(m, "[anon]");
+       }
+
+       klist = keyring->payload.subscriptions;
+       if (klist)
+               seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
+       else
+               seq_puts(m, ": empty");
+
+} /* end keyring_describe() */
+
+/*****************************************************************************/
+/*
+ * read a list of key IDs from the keyring's contents
+ */
+static long keyring_read(const struct key *keyring,
+                        char __user *buffer, size_t buflen)
+{
+       struct keyring_list *klist;
+       struct key *key;
+       size_t qty, tmp;
+       int loop, ret;
+
+       ret = 0;
+       klist = keyring->payload.subscriptions;
+
+       if (klist) {
+               /* calculate how much data we could return */
+               qty = klist->nkeys * sizeof(key_serial_t);
+
+               if (buffer && buflen > 0) {
+                       if (buflen > qty)
+                               buflen = qty;
+
+                       /* copy the IDs of the subscribed keys into the
+                        * buffer */
+                       ret = -EFAULT;
+
+                       for (loop = 0; loop < klist->nkeys; loop++) {
+                               key = klist->keys[loop];
+
+                               tmp = sizeof(key_serial_t);
+                               if (tmp > buflen)
+                                       tmp = buflen;
+
+                               if (copy_to_user(buffer,
+                                                &key->serial,
+                                                tmp) != 0)
+                                       goto error;
+
+                               buflen -= tmp;
+                               if (buflen == 0)
+                                       break;
+                               buffer += tmp;
+                       }
+               }
+
+               ret = qty;
+       }
+
+ error:
+       return ret;
+
+} /* end keyring_read() */
+
+/*****************************************************************************/
+/*
+ * allocate a keyring and link into the destination keyring
+ */
+struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
+                         int not_in_quota, struct key *dest)
+{
+       struct key *keyring;
+       int ret;
+
+       keyring = key_alloc(&key_type_keyring, description,
+                           uid, gid, KEY_USR_ALL, not_in_quota);
+
+       if (!IS_ERR(keyring)) {
+               ret = key_instantiate_and_link(keyring, NULL, 0, dest);
+               if (ret < 0) {
+                       key_put(keyring);
+                       keyring = ERR_PTR(ret);
+               }
+       }
+
+       return keyring;
+
+} /* end keyring_alloc() */
+
+/*****************************************************************************/
+/*
+ * search the supplied keyring tree for a key that matches the criterion
+ * - perform a breadth-then-depth search up to the prescribed limit
+ * - we only find keys on which we have search permission
+ * - we use the supplied match function to see if the description (or other
+ *   feature of interest) matches
+ * - we readlock the keyrings as we search down the tree
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we only found negative matching keys
+ */
+struct key *keyring_search_aux(struct key *keyring,
+                              struct key_type *type,
+                              const void *description,
+                              key_match_func_t match)
+{
+       struct {
+               struct key *keyring;
+               int kix;
+       } stack[KEYRING_SEARCH_MAX_DEPTH];
+
+       struct keyring_list *keylist;
+       struct timespec now;
+       struct key *key;
+       long err;
+       int sp, psp, kix;
+
+       key_check(keyring);
+
+       /* top keyring must have search permission to begin the search */
+       key = ERR_PTR(-EACCES);
+       if (!key_permission(keyring, KEY_SEARCH))
+               goto error;
+
+       key = ERR_PTR(-ENOTDIR);
+       if (keyring->type != &key_type_keyring)
+               goto error;
+
+       now = current_kernel_time();
+       err = -EAGAIN;
+       sp = 0;
+
+       /* start processing a new keyring */
+ descend:
+       read_lock(&keyring->lock);
+       if (keyring->flags & KEY_FLAG_REVOKED)
+               goto not_this_keyring;
+
+       keylist = keyring->payload.subscriptions;
+       if (!keylist)
+               goto not_this_keyring;
+
+       /* iterate through the keys in this keyring first */
+       for (kix = 0; kix < keylist->nkeys; kix++) {
+               key = keylist->keys[kix];
+
+               /* ignore keys not of this type */
+               if (key->type != type)
+                       continue;
+
+               /* skip revoked keys and expired keys */
+               if (key->flags & KEY_FLAG_REVOKED)
+                       continue;
+
+               if (key->expiry && now.tv_sec >= key->expiry)
+                       continue;
+
+               /* keys that don't match */
+               if (!match(key, description))
+                       continue;
+
+               /* key must have search permissions */
+               if (!key_permission(key, KEY_SEARCH))
+                       continue;
+
+               /* we set a different error code if we find a negative key */
+               if (key->flags & KEY_FLAG_NEGATIVE) {
+                       err = -ENOKEY;
+                       continue;
+               }
+
+               goto found;
+       }
+
+       /* search through the keyrings nested in this one */
+       kix = 0;
+ ascend:
+       while (kix < keylist->nkeys) {
+               key = keylist->keys[kix];
+               if (key->type != &key_type_keyring)
+                       goto next;
+
+               /* recursively search nested keyrings
+                * - only search keyrings for which we have search permission
+                */
+               if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+                       goto next;
+
+               if (!key_permission(key, KEY_SEARCH))
+                       goto next;
+
+               /* evade loops in the keyring tree */
+               for (psp = 0; psp < sp; psp++)
+                       if (stack[psp].keyring == keyring)
+                               goto next;
+
+               /* stack the current position */
+               stack[sp].keyring = keyring;
+               stack[sp].kix = kix;
+               sp++;
+
+               /* begin again with the new keyring */
+               keyring = key;
+               goto descend;
+
+       next:
+               kix++;
+       }
+
+       /* the keyring we're looking at was disqualified or didn't contain a
+        * matching key */
+ not_this_keyring:
+       read_unlock(&keyring->lock);
+
+       if (sp > 0) {
+               /* resume the processing of a keyring higher up in the tree */
+               sp--;
+               keyring = stack[sp].keyring;
+               keylist = keyring->payload.subscriptions;
+               kix = stack[sp].kix + 1;
+               goto ascend;
+       }
+
+       key = ERR_PTR(err);
+       goto error;
+
+       /* we found a viable match */
+ found:
+       atomic_inc(&key->usage);
+       read_unlock(&keyring->lock);
+
+       /* unwind the keyring stack */
+       while (sp > 0) {
+               sp--;
+               read_unlock(&stack[sp].keyring->lock);
+       }
+
+       key_check(key);
+ error:
+       return key;
+
+} /* end keyring_search_aux() */
+
+/*****************************************************************************/
+/*
+ * search the supplied keyring tree for a key that matches the criterion
+ * - perform a breadth-then-depth search up to the prescribed limit
+ * - we only find keys on which we have search permission
+ * - we readlock the keyrings as we search down the tree
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we only found negative matching keys
+ */
+struct key *keyring_search(struct key *keyring,
+                          struct key_type *type,
+                          const char *description)
+{
+       return keyring_search_aux(keyring, type, description, type->match);
+
+} /* end keyring_search() */
+
+EXPORT_SYMBOL(keyring_search);
+
+/*****************************************************************************/
+/*
+ * search the given keyring only (no recursion)
+ * - keyring must be locked by caller
+ */
+struct key *__keyring_search_one(struct key *keyring,
+                                const struct key_type *ktype,
+                                const char *description,
+                                key_perm_t perm)
+{
+       struct keyring_list *klist;
+       struct key *key;
+       int loop;
+
+       klist = keyring->payload.subscriptions;
+       if (klist) {
+               for (loop = 0; loop < klist->nkeys; loop++) {
+                       key = klist->keys[loop];
+
+                       if (key->type == ktype &&
+                           key->type->match(key, description) &&
+                           key_permission(key, perm) &&
+                           !(key->flags & KEY_FLAG_REVOKED)
+                           )
+                               goto found;
+               }
+       }
+
+       key = ERR_PTR(-ENOKEY);
+       goto error;
+
+ found:
+       atomic_inc(&key->usage);
+ error:
+       return key;
+
+} /* end __keyring_search_one() */
+
+/*****************************************************************************/
+/*
+ * find a keyring with the specified name
+ * - all named keyrings are searched
+ * - only find keyrings with search permission for the process
+ * - only find keyrings with a serial number greater than the one specified
+ */
+struct key *find_keyring_by_name(const char *name, key_serial_t bound)
+{
+       struct key *keyring;
+       int bucket;
+
+       keyring = ERR_PTR(-EINVAL);
+       if (!name)
+               goto error;
+
+       bucket = keyring_hash(name);
+
+       read_lock(&keyring_name_lock);
+
+       if (keyring_name_hash[bucket].next) {
+               /* search this hash bucket for a keyring with a matching name
+                * that's readable and that hasn't been revoked */
+               list_for_each_entry(keyring,
+                                   &keyring_name_hash[bucket],
+                                   type_data.link
+                                   ) {
+                       if (keyring->flags & KEY_FLAG_REVOKED)
+                               continue;
+
+                       if (strcmp(keyring->description, name) != 0)
+                               continue;
+
+                       if (!key_permission(keyring, KEY_SEARCH))
+                               continue;
+
+                       /* found a potential candidate, but we still need to
+                        * check the serial number */
+                       if (keyring->serial <= bound)
+                               continue;
+
+                       /* we've got a match */
+                       atomic_inc(&keyring->usage);
+                       read_unlock(&keyring_name_lock);
+                       goto error;
+               }
+       }
+
+       read_unlock(&keyring_name_lock);
+       keyring = ERR_PTR(-ENOKEY);
+
+ error:
+       return keyring;
+
+} /* end find_keyring_by_name() */
+
+/*****************************************************************************/
+/*
+ * see if a cycle will will be created by inserting acyclic tree B in acyclic
+ * tree A at the topmost level (ie: as a direct child of A)
+ * - since we are adding B to A at the top level, checking for cycles should
+ *   just be a matter of seeing if node A is somewhere in tree B
+ */
+static int keyring_detect_cycle(struct key *A, struct key *B)
+{
+       struct {
+               struct key *subtree;
+               int kix;
+       } stack[KEYRING_SEARCH_MAX_DEPTH];
+
+       struct keyring_list *keylist;
+       struct key *subtree, *key;
+       int sp, kix, ret;
+
+       ret = -EDEADLK;
+       if (A == B)
+               goto error;
+
+       subtree = B;
+       sp = 0;
+
+       /* start processing a new keyring */
+ descend:
+       read_lock(&subtree->lock);
+       if (subtree->flags & KEY_FLAG_REVOKED)
+               goto not_this_keyring;
+
+       keylist = subtree->payload.subscriptions;
+       if (!keylist)
+               goto not_this_keyring;
+       kix = 0;
+
+ ascend:
+       /* iterate through the remaining keys in this keyring */
+       for (; kix < keylist->nkeys; kix++) {
+               key = keylist->keys[kix];
+
+               if (key == A)
+                       goto cycle_detected;
+
+               /* recursively check nested keyrings */
+               if (key->type == &key_type_keyring) {
+                       if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+                               goto too_deep;
+
+                       /* stack the current position */
+                       stack[sp].subtree = subtree;
+                       stack[sp].kix = kix;
+                       sp++;
+
+                       /* begin again with the new keyring */
+                       subtree = key;
+                       goto descend;
+               }
+       }
+
+       /* the keyring we're looking at was disqualified or didn't contain a
+        * matching key */
+ not_this_keyring:
+       read_unlock(&subtree->lock);
+
+       if (sp > 0) {
+               /* resume the checking of a keyring higher up in the tree */
+               sp--;
+               subtree = stack[sp].subtree;
+               keylist = subtree->payload.subscriptions;
+               kix = stack[sp].kix + 1;
+               goto ascend;
+       }
+
+       ret = 0; /* no cycles detected */
+
+ error:
+       return ret;
+
+ too_deep:
+       ret = -ELOOP;
+       goto error_unwind;
+ cycle_detected:
+       ret = -EDEADLK;
+ error_unwind:
+       read_unlock(&subtree->lock);
+
+       /* unwind the keyring stack */
+       while (sp > 0) {
+               sp--;
+               read_unlock(&stack[sp].subtree->lock);
+       }
+
+       goto error;
+
+} /* end keyring_detect_cycle() */
+
+/*****************************************************************************/
+/*
+ * link a key into to a keyring
+ * - must be called with the keyring's semaphore held
+ */
+int __key_link(struct key *keyring, struct key *key)
+{
+       struct keyring_list *klist, *nklist;
+       unsigned max;
+       size_t size;
+       int ret;
+
+       ret = -EKEYREVOKED;
+       if (keyring->flags & KEY_FLAG_REVOKED)
+               goto error;
+
+       ret = -ENOTDIR;
+       if (keyring->type != &key_type_keyring)
+               goto error;
+
+       /* serialise link/link calls to prevent parallel calls causing a
+        * cycle when applied to two keyring in opposite orders */
+       down_write(&keyring_serialise_link_sem);
+
+       /* check that we aren't going to create a cycle adding one keyring to
+        * another */
+       if (key->type == &key_type_keyring) {
+               ret = keyring_detect_cycle(keyring, key);
+               if (ret < 0)
+                       goto error2;
+       }
+
+       /* check that we aren't going to overrun the user's quota */
+       ret = key_payload_reserve(keyring,
+                                 keyring->datalen + KEYQUOTA_LINK_BYTES);
+       if (ret < 0)
+               goto error2;
+
+       klist = keyring->payload.subscriptions;
+
+       if (klist && klist->nkeys < klist->maxkeys) {
+               /* there's sufficient slack space to add directly */
+               atomic_inc(&key->usage);
+
+               write_lock(&keyring->lock);
+               klist->keys[klist->nkeys++] = key;
+               write_unlock(&keyring->lock);
+
+               ret = 0;
+       }
+       else {
+               /* grow the key list */
+               max = 4;
+               if (klist)
+                       max += klist->maxkeys;
+
+               ret = -ENFILE;
+               size = sizeof(*klist) + sizeof(*key) * max;
+               if (size > PAGE_SIZE)
+                       goto error3;
+
+               ret = -ENOMEM;
+               nklist = kmalloc(size, GFP_KERNEL);
+               if (!nklist)
+                       goto error3;
+               nklist->maxkeys = max;
+               nklist->nkeys = 0;
+
+               if (klist) {
+                       nklist->nkeys = klist->nkeys;
+                       memcpy(nklist->keys,
+                              klist->keys,
+                              sizeof(struct key *) * klist->nkeys);
+               }
+
+               /* add the key into the new space */
+               atomic_inc(&key->usage);
+
+               write_lock(&keyring->lock);
+               keyring->payload.subscriptions = nklist;
+               nklist->keys[nklist->nkeys++] = key;
+               write_unlock(&keyring->lock);
+
+               /* dispose of the old keyring list */
+               kfree(klist);
+
+               ret = 0;
+       }
+
+ error2:
+       up_write(&keyring_serialise_link_sem);
+ error:
+       return ret;
+
+ error3:
+       /* undo the quota changes */
+       key_payload_reserve(keyring,
+                           keyring->datalen - KEYQUOTA_LINK_BYTES);
+       goto error2;
+
+} /* end __key_link() */
+
+/*****************************************************************************/
+/*
+ * link a key to a keyring
+ */
+int key_link(struct key *keyring, struct key *key)
+{
+       int ret;
+
+       key_check(keyring);
+       key_check(key);
+
+       down_write(&keyring->sem);
+       ret = __key_link(keyring, key);
+       up_write(&keyring->sem);
+
+       return ret;
+
+} /* end key_link() */
+
+EXPORT_SYMBOL(key_link);
+
+/*****************************************************************************/
+/*
+ * unlink the first link to a key from a keyring
+ */
+int key_unlink(struct key *keyring, struct key *key)
+{
+       struct keyring_list *klist;
+       int loop, ret;
+
+       key_check(keyring);
+       key_check(key);
+
+       ret = -ENOTDIR;
+       if (keyring->type != &key_type_keyring)
+               goto error;
+
+       down_write(&keyring->sem);
+
+       klist = keyring->payload.subscriptions;
+       if (klist) {
+               /* search the keyring for the key */
+               for (loop = 0; loop < klist->nkeys; loop++)
+                       if (klist->keys[loop] == key)
+                               goto key_is_present;
+       }
+
+       up_write(&keyring->sem);
+       ret = -ENOENT;
+       goto error;
+
+ key_is_present:
+       /* adjust the user's quota */
+       key_payload_reserve(keyring,
+                           keyring->datalen - KEYQUOTA_LINK_BYTES);
+
+       /* shuffle down the key pointers
+        * - it might be worth shrinking the allocated memory, but that runs
+        *   the risk of ENOMEM as we would have to copy
+        */
+       write_lock(&keyring->lock);
+
+       klist->nkeys--;
+       if (loop < klist->nkeys)
+               memcpy(&klist->keys[loop],
+                      &klist->keys[loop + 1],
+                      (klist->nkeys - loop) * sizeof(struct key *));
+
+       write_unlock(&keyring->lock);
+
+       up_write(&keyring->sem);
+       key_put(key);
+       ret = 0;
+
+ error:
+       return ret;
+
+} /* end key_unlink() */
+
+EXPORT_SYMBOL(key_unlink);
+
+/*****************************************************************************/
+/*
+ * clear the specified process keyring
+ * - implements keyctl(KEYCTL_CLEAR)
+ */
+int keyring_clear(struct key *keyring)
+{
+       struct keyring_list *klist;
+       int loop, ret;
+
+       ret = -ENOTDIR;
+       if (keyring->type == &key_type_keyring) {
+               /* detach the pointer block with the locks held */
+               down_write(&keyring->sem);
+
+               klist = keyring->payload.subscriptions;
+               if (klist) {
+                       /* adjust the quota */
+                       key_payload_reserve(keyring,
+                                           sizeof(struct keyring_list));
+
+                       write_lock(&keyring->lock);
+                       keyring->payload.subscriptions = NULL;
+                       write_unlock(&keyring->lock);
+               }
+
+               up_write(&keyring->sem);
+
+               /* free the keys after the locks have been dropped */
+               if (klist) {
+                       for (loop = klist->nkeys - 1; loop >= 0; loop--)
+                               key_put(klist->keys[loop]);
+
+                       kfree(klist);
+               }
+
+               ret = 0;
+       }
+
+       return ret;
+
+} /* end keyring_clear() */
+
+EXPORT_SYMBOL(keyring_clear);
diff --git a/security/keys/proc.c b/security/keys/proc.c
new file mode 100644 (file)
index 0000000..91343b8
--- /dev/null
@@ -0,0 +1,251 @@
+/* proc.c: proc files for key database enumeration
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/errno.h>
+#include "internal.h"
+
+#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
+static int proc_keys_open(struct inode *inode, struct file *file);
+static void *proc_keys_start(struct seq_file *p, loff_t *_pos);
+static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos);
+static void proc_keys_stop(struct seq_file *p, void *v);
+static int proc_keys_show(struct seq_file *m, void *v);
+
+static struct seq_operations proc_keys_ops = {
+       .start  = proc_keys_start,
+       .next   = proc_keys_next,
+       .stop   = proc_keys_stop,
+       .show   = proc_keys_show,
+};
+
+static struct file_operations proc_keys_fops = {
+       .open           = proc_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+#endif
+
+static int proc_key_users_open(struct inode *inode, struct file *file);
+static void *proc_key_users_start(struct seq_file *p, loff_t *_pos);
+static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos);
+static void proc_key_users_stop(struct seq_file *p, void *v);
+static int proc_key_users_show(struct seq_file *m, void *v);
+
+static struct seq_operations proc_key_users_ops = {
+       .start  = proc_key_users_start,
+       .next   = proc_key_users_next,
+       .stop   = proc_key_users_stop,
+       .show   = proc_key_users_show,
+};
+
+static struct file_operations proc_key_users_fops = {
+       .open           = proc_key_users_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release,
+};
+
+/*****************************************************************************/
+/*
+ * declare the /proc files
+ */
+static int __init key_proc_init(void)
+{
+       struct proc_dir_entry *p;
+
+#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
+       p = create_proc_entry("keys", 0, NULL);
+       if (!p)
+               panic("Cannot create /proc/keys\n");
+
+       p->proc_fops = &proc_keys_fops;
+#endif
+
+       p = create_proc_entry("key-users", 0, NULL);
+       if (!p)
+               panic("Cannot create /proc/key-users\n");
+
+       p->proc_fops = &proc_key_users_fops;
+
+       return 0;
+
+} /* end key_proc_init() */
+
+__initcall(key_proc_init);
+
+/*****************************************************************************/
+/*
+ * implement "/proc/keys" to provides a list of the keys on the system
+ */
+#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
+
+static int proc_keys_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &proc_keys_ops);
+
+}
+
+static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
+{
+       struct rb_node *_p;
+       loff_t pos = *_pos;
+
+       spin_lock(&key_serial_lock);
+
+       _p = rb_first(&key_serial_tree);
+       while (pos > 0 && _p) {
+               pos--;
+               _p = rb_next(_p);
+       }
+
+       return _p;
+
+}
+
+static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
+{
+       (*_pos)++;
+       return rb_next((struct rb_node *) v);
+
+}
+
+static void proc_keys_stop(struct seq_file *p, void *v)
+{
+       spin_unlock(&key_serial_lock);
+}
+
+static int proc_keys_show(struct seq_file *m, void *v)
+{
+       struct rb_node *_p = v;
+       struct key *key = rb_entry(_p, struct key, serial_node);
+       struct timespec now;
+       unsigned long timo;
+       char xbuf[12];
+
+       now = current_kernel_time();
+
+       read_lock(&key->lock);
+
+       /* come up with a suitable timeout value */
+       if (key->expiry == 0) {
+               memcpy(xbuf, "perm", 5);
+       }
+       else if (now.tv_sec >= key->expiry) {
+               memcpy(xbuf, "expd", 5);
+       }
+       else {
+               timo = key->expiry - now.tv_sec;
+
+               if (timo < 60)
+                       sprintf(xbuf, "%lus", timo);
+               else if (timo < 60*60)
+                       sprintf(xbuf, "%lum", timo / 60);
+               else if (timo < 60*60*24)
+                       sprintf(xbuf, "%luh", timo / (60*60));
+               else if (timo < 60*60*24*7)
+                       sprintf(xbuf, "%lud", timo / (60*60*24));
+               else
+                       sprintf(xbuf, "%luw", timo / (60*60*24*7));
+       }
+
+       seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %06x %5d %5d %-9.9s ",
+                  key->serial,
+                  key->flags & KEY_FLAG_INSTANTIATED   ? 'I' : '-',
+                  key->flags & KEY_FLAG_REVOKED        ? 'R' : '-',
+                  key->flags & KEY_FLAG_DEAD           ? 'D' : '-',
+                  key->flags & KEY_FLAG_IN_QUOTA       ? 'Q' : '-',
+                  key->flags & KEY_FLAG_USER_CONSTRUCT ? 'U' : '-',
+                  key->flags & KEY_FLAG_NEGATIVE       ? 'N' : '-',
+                  atomic_read(&key->usage),
+                  xbuf,
+                  key->perm,
+                  key->uid,
+                  key->gid,
+                  key->type->name);
+
+       if (key->type->describe)
+               key->type->describe(key, m);
+       seq_putc(m, '\n');
+
+       read_unlock(&key->lock);
+
+       return 0;
+
+}
+
+#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
+
+/*****************************************************************************/
+/*
+ * implement "/proc/key-users" to provides a list of the key users
+ */
+static int proc_key_users_open(struct inode *inode, struct file *file)
+{
+       return seq_open(file, &proc_key_users_ops);
+
+}
+
+static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
+{
+       struct rb_node *_p;
+       loff_t pos = *_pos;
+
+       spin_lock(&key_user_lock);
+
+       _p = rb_first(&key_user_tree);
+       while (pos > 0 && _p) {
+               pos--;
+               _p = rb_next(_p);
+       }
+
+       return _p;
+
+}
+
+static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
+{
+       (*_pos)++;
+       return rb_next((struct rb_node *) v);
+
+}
+
+static void proc_key_users_stop(struct seq_file *p, void *v)
+{
+       spin_unlock(&key_user_lock);
+}
+
+static int proc_key_users_show(struct seq_file *m, void *v)
+{
+       struct rb_node *_p = v;
+       struct key_user *user = rb_entry(_p, struct key_user, node);
+
+       seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
+                  user->uid,
+                  atomic_read(&user->usage),
+                  atomic_read(&user->nkeys),
+                  atomic_read(&user->nikeys),
+                  user->qnkeys,
+                  KEYQUOTA_MAX_KEYS,
+                  user->qnbytes,
+                  KEYQUOTA_MAX_BYTES
+                  );
+
+       return 0;
+
+}
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
new file mode 100644 (file)
index 0000000..e5bd7b9
--- /dev/null
@@ -0,0 +1,640 @@
+/* process_keys.c: management of a process's keyrings
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/keyctl.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/* session keyring create vs join semaphore */
+static DECLARE_MUTEX(key_session_sem);
+
+/* the root user's tracking struct */
+struct key_user root_key_user = {
+       .usage          = ATOMIC_INIT(3),
+       .consq          = LIST_HEAD_INIT(root_key_user.consq),
+       .lock           = SPIN_LOCK_UNLOCKED,
+       .nkeys          = ATOMIC_INIT(2),
+       .nikeys         = ATOMIC_INIT(2),
+       .uid            = 0,
+};
+
+/* the root user's UID keyring */
+struct key root_user_keyring = {
+       .usage          = ATOMIC_INIT(1),
+       .serial         = 2,
+       .type           = &key_type_keyring,
+       .user           = &root_key_user,
+       .lock           = RW_LOCK_UNLOCKED,
+       .sem            = __RWSEM_INITIALIZER(root_user_keyring.sem),
+       .perm           = KEY_USR_ALL,
+       .flags          = KEY_FLAG_INSTANTIATED,
+       .description    = "_uid.0",
+#ifdef KEY_DEBUGGING
+       .magic          = KEY_DEBUG_MAGIC,
+#endif
+};
+
+/* the root user's default session keyring */
+struct key root_session_keyring = {
+       .usage          = ATOMIC_INIT(1),
+       .serial         = 1,
+       .type           = &key_type_keyring,
+       .user           = &root_key_user,
+       .lock           = RW_LOCK_UNLOCKED,
+       .sem            = __RWSEM_INITIALIZER(root_session_keyring.sem),
+       .perm           = KEY_USR_ALL,
+       .flags          = KEY_FLAG_INSTANTIATED,
+       .description    = "_uid_ses.0",
+#ifdef KEY_DEBUGGING
+       .magic          = KEY_DEBUG_MAGIC,
+#endif
+};
+
+/*****************************************************************************/
+/*
+ * allocate the keyrings to be associated with a UID
+ */
+int alloc_uid_keyring(struct user_struct *user)
+{
+       struct key *uid_keyring, *session_keyring;
+       char buf[20];
+       int ret;
+
+       /* concoct a default session keyring */
+       sprintf(buf, "_uid_ses.%u", user->uid);
+
+       session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, NULL);
+       if (IS_ERR(session_keyring)) {
+               ret = PTR_ERR(session_keyring);
+               goto error;
+       }
+
+       /* and a UID specific keyring, pointed to by the default session
+        * keyring */
+       sprintf(buf, "_uid.%u", user->uid);
+
+       uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0,
+                                   session_keyring);
+       if (IS_ERR(uid_keyring)) {
+               key_put(session_keyring);
+               ret = PTR_ERR(uid_keyring);
+               goto error;
+       }
+
+       /* install the keyrings */
+       user->uid_keyring = uid_keyring;
+       user->session_keyring = session_keyring;
+       ret = 0;
+
+ error:
+       return ret;
+
+} /* end alloc_uid_keyring() */
+
+/*****************************************************************************/
+/*
+ * deal with the UID changing
+ */
+void switch_uid_keyring(struct user_struct *new_user)
+{
+#if 0 /* do nothing for now */
+       struct key *old;
+
+       /* switch to the new user's session keyring if we were running under
+        * root's default session keyring */
+       if (new_user->uid != 0 &&
+           current->session_keyring == &root_session_keyring
+           ) {
+               atomic_inc(&new_user->session_keyring->usage);
+
+               task_lock(current);
+               old = current->session_keyring;
+               current->session_keyring = new_user->session_keyring;
+               task_unlock(current);
+
+               key_put(old);
+       }
+#endif
+
+} /* end switch_uid_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh thread keyring, discarding the old one
+ */
+int install_thread_keyring(struct task_struct *tsk)
+{
+       struct key *keyring, *old;
+       char buf[20];
+       int ret;
+
+       sprintf(buf, "_tid.%u", tsk->pid);
+
+       keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
+       if (IS_ERR(keyring)) {
+               ret = PTR_ERR(keyring);
+               goto error;
+       }
+
+       task_lock(tsk);
+       old = tsk->thread_keyring;
+       tsk->thread_keyring = keyring;
+       task_unlock(tsk);
+
+       ret = 0;
+
+       key_put(old);
+ error:
+       return ret;
+
+} /* end install_thread_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh process keyring, discarding the old one
+ */
+static int install_process_keyring(struct task_struct *tsk)
+{
+       struct key *keyring, *old;
+       char buf[20];
+       int ret;
+
+       sprintf(buf, "_pid.%u", tsk->tgid);
+
+       keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
+       if (IS_ERR(keyring)) {
+               ret = PTR_ERR(keyring);
+               goto error;
+       }
+
+       task_lock(tsk);
+       old = tsk->process_keyring;
+       tsk->process_keyring = keyring;
+       task_unlock(tsk);
+
+       ret = 0;
+
+       key_put(old);
+ error:
+       return ret;
+
+} /* end install_process_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a session keyring, discarding the old one
+ * - if a keyring is not supplied, an empty one is invented
+ */
+static int install_session_keyring(struct task_struct *tsk,
+                                  struct key *keyring)
+{
+       struct key *old;
+       char buf[20];
+       int ret;
+
+       /* create an empty session keyring */
+       if (!keyring) {
+               sprintf(buf, "_ses.%u", tsk->tgid);
+
+               keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
+               if (IS_ERR(keyring)) {
+                       ret = PTR_ERR(keyring);
+                       goto error;
+               }
+       }
+       else {
+               atomic_inc(&keyring->usage);
+       }
+
+       /* install the keyring */
+       task_lock(tsk);
+       old = tsk->session_keyring;
+       tsk->session_keyring = keyring;
+       task_unlock(tsk);
+
+       ret = 0;
+
+       key_put(old);
+ error:
+       return ret;
+
+} /* end install_session_keyring() */
+
+/*****************************************************************************/
+/*
+ * copy the keys for fork
+ */
+int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
+{
+       int ret = 0;
+
+       key_check(tsk->session_keyring);
+       key_check(tsk->process_keyring);
+       key_check(tsk->thread_keyring);
+
+       if (tsk->session_keyring)
+               atomic_inc(&tsk->session_keyring->usage);
+
+       if (tsk->process_keyring) {
+               if (clone_flags & CLONE_THREAD) {
+                       atomic_inc(&tsk->process_keyring->usage);
+               }
+               else {
+                       tsk->process_keyring = NULL;
+                       ret = install_process_keyring(tsk);
+               }
+       }
+
+       tsk->thread_keyring = NULL;
+       return ret;
+
+} /* end copy_keys() */
+
+/*****************************************************************************/
+/*
+ * dispose of keys upon exit
+ */
+void exit_keys(struct task_struct *tsk)
+{
+       key_put(tsk->session_keyring);
+       key_put(tsk->process_keyring);
+       key_put(tsk->thread_keyring);
+
+} /* end exit_keys() */
+
+/*****************************************************************************/
+/*
+ * deal with execve()
+ */
+int exec_keys(struct task_struct *tsk)
+{
+       struct key *old;
+
+       /* newly exec'd tasks don't get a thread keyring */
+       task_lock(tsk);
+       old = tsk->thread_keyring;
+       tsk->thread_keyring = NULL;
+       task_unlock(tsk);
+
+       key_put(old);
+
+       /* newly exec'd tasks get a fresh process keyring */
+       return install_process_keyring(tsk);
+
+} /* end exec_keys() */
+
+/*****************************************************************************/
+/*
+ * deal with SUID programs
+ * - we might want to make this invent a new session keyring
+ */
+int suid_keys(struct task_struct *tsk)
+{
+       return 0;
+
+} /* end suid_keys() */
+
+/*****************************************************************************/
+/*
+ * the filesystem user ID changed
+ */
+void key_fsuid_changed(struct task_struct *tsk)
+{
+       /* update the ownership of the process keyring */
+       if (tsk->process_keyring) {
+               down_write(&tsk->process_keyring->sem);
+               write_lock(&tsk->process_keyring->lock);
+               tsk->process_keyring->uid = tsk->fsuid;
+               write_unlock(&tsk->process_keyring->lock);
+               up_write(&tsk->process_keyring->sem);
+       }
+
+       /* update the ownership of the thread keyring */
+       if (tsk->thread_keyring) {
+               down_write(&tsk->thread_keyring->sem);
+               write_lock(&tsk->thread_keyring->lock);
+               tsk->thread_keyring->uid = tsk->fsuid;
+               write_unlock(&tsk->thread_keyring->lock);
+               up_write(&tsk->thread_keyring->sem);
+       }
+
+} /* end key_fsuid_changed() */
+
+/*****************************************************************************/
+/*
+ * the filesystem group ID changed
+ */
+void key_fsgid_changed(struct task_struct *tsk)
+{
+       /* update the ownership of the process keyring */
+       if (tsk->process_keyring) {
+               down_write(&tsk->process_keyring->sem);
+               write_lock(&tsk->process_keyring->lock);
+               tsk->process_keyring->gid = tsk->fsgid;
+               write_unlock(&tsk->process_keyring->lock);
+               up_write(&tsk->process_keyring->sem);
+       }
+
+       /* update the ownership of the thread keyring */
+       if (tsk->thread_keyring) {
+               down_write(&tsk->thread_keyring->sem);
+               write_lock(&tsk->thread_keyring->lock);
+               tsk->thread_keyring->gid = tsk->fsgid;
+               write_unlock(&tsk->thread_keyring->lock);
+               up_write(&tsk->thread_keyring->sem);
+       }
+
+} /* end key_fsgid_changed() */
+
+/*****************************************************************************/
+/*
+ * search the process keyrings for the first matching key
+ * - we use the supplied match function to see if the description (or other
+ *   feature of interest) matches
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we found only negative matching keys
+ */
+struct key *search_process_keyrings_aux(struct key_type *type,
+                                       const void *description,
+                                       key_match_func_t match)
+{
+       struct task_struct *tsk = current;
+       struct key *key, *ret, *err, *session;
+
+       /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
+        * searchable, but we failed to find a key or we found a negative key;
+        * otherwise we want to return a sample error (probably -EACCES) if
+        * none of the keyrings were searchable
+        *
+        * in terms of priority: success > -ENOKEY > -EAGAIN > other error
+        */
+       key = NULL;
+       ret = NULL;
+       err = ERR_PTR(-EAGAIN);
+
+       /* search the thread keyring first */
+       if (tsk->thread_keyring) {
+               key = keyring_search_aux(tsk->thread_keyring, type,
+                                        description, match);
+               if (!IS_ERR(key))
+                       goto found;
+
+               switch (PTR_ERR(key)) {
+               case -EAGAIN: /* no key */
+                       if (ret)
+                               break;
+               case -ENOKEY: /* negative key */
+                       ret = key;
+                       break;
+               default:
+                       err = key;
+                       break;
+               }
+       }
+
+       /* search the process keyring second */
+       if (tsk->process_keyring) {
+               key = keyring_search_aux(tsk->process_keyring, type,
+                                        description, match);
+               if (!IS_ERR(key))
+                       goto found;
+
+               switch (PTR_ERR(key)) {
+               case -EAGAIN: /* no key */
+                       if (ret)
+                               break;
+               case -ENOKEY: /* negative key */
+                       ret = key;
+                       break;
+               default:
+                       err = key;
+                       break;
+               }
+       }
+
+       /* search the session keyring last */
+       session = tsk->session_keyring;
+       if (!session)
+               session = tsk->user->session_keyring;
+
+       key = keyring_search_aux(session, type,
+                                description, match);
+       if (!IS_ERR(key))
+               goto found;
+
+       switch (PTR_ERR(key)) {
+       case -EAGAIN: /* no key */
+               if (ret)
+                       break;
+       case -ENOKEY: /* negative key */
+               ret = key;
+               break;
+       default:
+               err = key;
+               break;
+       }
+
+       /* no key - decide on the error we're going to go for */
+       key = ret ? ret : err;
+
+ found:
+       return key;
+
+} /* end search_process_keyrings_aux() */
+
+/*****************************************************************************/
+/*
+ * search the process keyrings for the first matching key
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we found only negative matching keys
+ */
+struct key *search_process_keyrings(struct key_type *type,
+                                   const char *description)
+{
+       return search_process_keyrings_aux(type, description, type->match);
+
+} /* end search_process_keyrings() */
+
+/*****************************************************************************/
+/*
+ * lookup a key given a key ID from userspace with a given permissions mask
+ * - don't create special keyrings unless so requested
+ * - partially constructed keys aren't found unless requested
+ */
+struct key *lookup_user_key(key_serial_t id, int create, int partial,
+                           key_perm_t perm)
+{
+       struct task_struct *tsk = current;
+       struct key *key;
+       int ret;
+
+       key = ERR_PTR(-ENOKEY);
+
+       switch (id) {
+       case KEY_SPEC_THREAD_KEYRING:
+               if (!tsk->thread_keyring) {
+                       if (!create)
+                               goto error;
+
+                       ret = install_thread_keyring(tsk);
+                       if (ret < 0) {
+                               key = ERR_PTR(ret);
+                               goto error;
+                       }
+               }
+
+               key = tsk->thread_keyring;
+               atomic_inc(&key->usage);
+               break;
+
+       case KEY_SPEC_PROCESS_KEYRING:
+               if (!tsk->process_keyring) {
+                       if (!create)
+                               goto error;
+
+                       ret = install_process_keyring(tsk);
+                       if (ret < 0) {
+                               key = ERR_PTR(ret);
+                               goto error;
+                       }
+               }
+
+               key = tsk->process_keyring;
+               atomic_inc(&key->usage);
+               break;
+
+       case KEY_SPEC_SESSION_KEYRING:
+               if (!tsk->session_keyring) {
+                       /* always install a session keyring upon access if one
+                        * doesn't exist yet */
+                       ret = install_session_keyring(
+                              tsk, tsk->user->session_keyring);
+                       if (ret < 0)
+                               goto error;
+               }
+
+               key = tsk->session_keyring;
+               atomic_inc(&key->usage);
+               break;
+
+       case KEY_SPEC_USER_KEYRING:
+               key = tsk->user->uid_keyring;
+               atomic_inc(&key->usage);
+               break;
+
+       case KEY_SPEC_USER_SESSION_KEYRING:
+               key = tsk->user->session_keyring;
+               atomic_inc(&key->usage);
+               break;
+
+       case KEY_SPEC_GROUP_KEYRING:
+               /* group keyrings are not yet supported */
+               key = ERR_PTR(-EINVAL);
+               goto error;
+
+       default:
+               key = ERR_PTR(-EINVAL);
+               if (id < 1)
+                       goto error;
+
+               key = key_lookup(id);
+               if (IS_ERR(key))
+                       goto error;
+               break;
+       }
+
+       /* check the status and permissions */
+       if (perm) {
+               ret = key_validate(key);
+               if (ret < 0)
+                       goto invalid_key;
+       }
+
+       ret = -EIO;
+       if (!partial && !(key->flags & KEY_FLAG_INSTANTIATED))
+               goto invalid_key;
+
+       ret = -EACCES;
+       if (!key_permission(key, perm))
+               goto invalid_key;
+
+ error:
+       return key;
+
+ invalid_key:
+       key_put(key);
+       key = ERR_PTR(ret);
+       goto error;
+
+} /* end lookup_user_key() */
+
+/*****************************************************************************/
+/*
+ * join the named keyring as the session keyring if possible, or attempt to
+ * create a new one of that name if not
+ * - if the name is NULL, an empty anonymous keyring is installed instead
+ * - named session keyring joining is done with a semaphore held
+ */
+long join_session_keyring(const char *name)
+{
+       struct task_struct *tsk = current;
+       struct key *keyring;
+       long ret;
+
+       /* if no name is provided, install an anonymous keyring */
+       if (!name) {
+               ret = install_session_keyring(tsk, NULL);
+               if (ret < 0)
+                       goto error;
+
+               ret = tsk->session_keyring->serial;
+               goto error;
+       }
+
+       /* allow the user to join or create a named keyring */
+       down(&key_session_sem);
+
+       /* look for an existing keyring of this name */
+       keyring = find_keyring_by_name(name, 0);
+       if (PTR_ERR(keyring) == -ENOKEY) {
+               /* not found - try and create a new one */
+               keyring = keyring_alloc(name, tsk->uid, tsk->gid, 0, NULL);
+               if (IS_ERR(keyring)) {
+                       ret = PTR_ERR(keyring);
+                       goto error;
+               }
+       }
+       else if (IS_ERR(keyring)) {
+               ret = PTR_ERR(keyring);
+               goto error2;
+       }
+
+       /* we've got a keyring - now to install it */
+       ret = install_session_keyring(tsk, keyring);
+       if (ret < 0)
+               goto error2;
+
+       key_put(keyring);
+
+       ret = tsk->session_keyring->serial;
+
+ error2:
+       up(&key_session_sem);
+ error:
+       return ret;
+
+} /* end join_session_keyring() */
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
new file mode 100644 (file)
index 0000000..fd6ba06
--- /dev/null
@@ -0,0 +1,337 @@
+/* request_key.c: request a key from userspace
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kmod.h>
+#include <linux/err.h>
+#include "internal.h"
+
+struct key_construction {
+       struct list_head        link;   /* link in construction queue */
+       struct key              *key;   /* key being constructed */
+};
+
+/* when waiting for someone else's keys, you get added to this */
+DECLARE_WAIT_QUEUE_HEAD(request_key_conswq);
+
+/*****************************************************************************/
+/*
+ * request userspace finish the construction of a key
+ * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> <info>"
+ * - if callout_info is an empty string, it'll be rendered as a "-" instead
+ */
+static int call_request_key(struct key *key,
+                           const char *op,
+                           const char *callout_info)
+{
+       struct task_struct *tsk = current;
+       char *argv[10], *envp[3], uid_str[12], gid_str[12];
+       char key_str[12], keyring_str[3][12];
+       int i;
+
+       /* record the UID and GID */
+       sprintf(uid_str, "%d", current->fsuid);
+       sprintf(gid_str, "%d", current->fsgid);
+
+       /* we say which key is under construction */
+       sprintf(key_str, "%d", key->serial);
+
+       /* we specify the process's default keyrings */
+       task_lock(current);
+       sprintf(keyring_str[0], "%d",
+               tsk->thread_keyring ? tsk->thread_keyring->serial : 0);
+       sprintf(keyring_str[1], "%d",
+               tsk->process_keyring ? tsk->process_keyring->serial : 0);
+       sprintf(keyring_str[2], "%d",
+               (tsk->session_keyring ?
+                tsk->session_keyring->serial :
+                tsk->user->session_keyring->serial));
+       task_unlock(tsk);
+
+       /* set up a minimal environment */
+       i = 0;
+       envp[i++] = "HOME=/";
+       envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+       envp[i] = NULL;
+
+       /* set up the argument list */
+       i = 0;
+       argv[i++] = "/sbin/request-key";
+       argv[i++] = (char *) op;
+       argv[i++] = key_str;
+       argv[i++] = uid_str;
+       argv[i++] = gid_str;
+       argv[i++] = keyring_str[0];
+       argv[i++] = keyring_str[1];
+       argv[i++] = keyring_str[2];
+       argv[i++] = callout_info[0] ? (char *) callout_info : "-";
+       argv[i] = NULL;
+
+       /* do it */
+       return call_usermodehelper(argv[0], argv, envp, 1);
+
+} /* end call_request_key() */
+
+/*****************************************************************************/
+/*
+ * call out to userspace for the key
+ * - called with the construction sem held, but the sem is dropped here
+ * - we ignore program failure and go on key status instead
+ */
+static struct key *__request_key_construction(struct key_type *type,
+                                             const char *description,
+                                             const char *callout_info)
+{
+       struct key_construction cons;
+       struct timespec now;
+       struct key *key;
+       int ret, negative;
+
+       /* create a key and add it to the queue */
+       key = key_alloc(type, description,
+                       current->fsuid, current->fsgid, KEY_USR_ALL, 0);
+       if (IS_ERR(key))
+               goto alloc_failed;
+
+       write_lock(&key->lock);
+       key->flags |= KEY_FLAG_USER_CONSTRUCT;
+       write_unlock(&key->lock);
+
+       cons.key = key;
+       list_add_tail(&cons.link, &key->user->consq);
+
+       /* we drop the construction sem here on behalf of the caller */
+       up_write(&key_construction_sem);
+
+       /* make the call */
+       ret = call_request_key(key, "create", callout_info);
+       if (ret < 0)
+               goto request_failed;
+
+       /* if the key wasn't instantiated, then we want to give an error */
+       ret = -ENOKEY;
+       if (!(key->flags & KEY_FLAG_INSTANTIATED))
+               goto request_failed;
+
+       down_write(&key_construction_sem);
+       list_del(&cons.link);
+       up_write(&key_construction_sem);
+
+       /* also give an error if the key was negatively instantiated */
+ check_not_negative:
+       if (key->flags & KEY_FLAG_NEGATIVE) {
+               key_put(key);
+               key = ERR_PTR(-ENOKEY);
+       }
+
+ out:
+       return key;
+
+ request_failed:
+       /* it wasn't instantiated
+        * - remove from construction queue
+        * - mark the key as dead
+        */
+       negative = 0;
+       down_write(&key_construction_sem);
+
+       list_del(&cons.link);
+
+       write_lock(&key->lock);
+       key->flags &= ~KEY_FLAG_USER_CONSTRUCT;
+
+       /* check it didn't get instantiated between the check and the down */
+       if (!(key->flags & KEY_FLAG_INSTANTIATED)) {
+               key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE;
+               negative = 1;
+       }
+
+       write_unlock(&key->lock);
+       up_write(&key_construction_sem);
+
+       if (!negative)
+               goto check_not_negative; /* surprisingly, the key got
+                                         * instantiated */
+
+       /* set the timeout and store in the session keyring if we can */
+       now = current_kernel_time();
+       key->expiry = now.tv_sec + key_negative_timeout;
+
+       if (current->session_keyring)
+               key_link(current->session_keyring, key);
+       key_put(key);
+
+       /* notify anyone who was waiting */
+       wake_up_all(&request_key_conswq);
+
+       key = ERR_PTR(ret);
+       goto out;
+
+ alloc_failed:
+       up_write(&key_construction_sem);
+       goto out;
+
+} /* end __request_key_construction() */
+
+/*****************************************************************************/
+/*
+ * call out to userspace to request the key
+ * - we check the construction queue first to see if an appropriate key is
+ *   already being constructed by userspace
+ */
+static struct key *request_key_construction(struct key_type *type,
+                                           const char *description,
+                                           struct key_user *user,
+                                           const char *callout_info)
+{
+       struct key_construction *pcons;
+       struct key *key, *ckey;
+
+       DECLARE_WAITQUEUE(myself, current);
+
+       /* see if there's such a key under construction already */
+       down_write(&key_construction_sem);
+
+       list_for_each_entry(pcons, &user->consq, link) {
+               ckey = pcons->key;
+
+               if (ckey->type != type)
+                       continue;
+
+               if (type->match(ckey, description))
+                       goto found_key_under_construction;
+       }
+
+       /* see about getting userspace to construct the key */
+       key = __request_key_construction(type, description, callout_info);
+ error:
+       return key;
+
+       /* someone else has the same key under construction
+        * - we want to keep an eye on their key
+        */
+ found_key_under_construction:
+       atomic_inc(&ckey->usage);
+       up_write(&key_construction_sem);
+
+       /* wait for the key to be completed one way or another */
+       add_wait_queue(&request_key_conswq, &myself);
+
+       for (;;) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               if (!(ckey->flags & KEY_FLAG_USER_CONSTRUCT))
+                       break;
+               schedule();
+       }
+
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&request_key_conswq, &myself);
+
+       /* we'll need to search this process's keyrings to see if the key is
+        * now there since we can't automatically assume it's also available
+        * there */
+       key_put(ckey);
+       ckey = NULL;
+
+       key = NULL; /* request a retry */
+       goto error;
+
+} /* end request_key_construction() */
+
+/*****************************************************************************/
+/*
+ * request a key
+ * - search the process's keyrings
+ * - check the list of keys being created or updated
+ * - call out to userspace for a key if requested (supplementary info can be
+ *   passed)
+ */
+struct key *request_key(struct key_type *type,
+                       const char *description,
+                       const char *callout_info)
+{
+       struct key_user *user;
+       struct key *key;
+
+       /* search all the process keyrings for a key */
+       key = search_process_keyrings_aux(type, description, type->match);
+
+       if (PTR_ERR(key) == -EAGAIN) {
+               /* the search failed, but the keyrings were searchable, so we
+                * should consult userspace if we can */
+               key = ERR_PTR(-ENOKEY);
+               if (!callout_info)
+                       goto error;
+
+               /* - get hold of the user's construction queue */
+               user = key_user_lookup(current->fsuid);
+               if (IS_ERR(user)) {
+                       key = ERR_PTR(PTR_ERR(user));
+                       goto error;
+               }
+
+               for (;;) {
+                       /* ask userspace (returns NULL if it waited on a key
+                        * being constructed) */
+                       key = request_key_construction(type, description,
+                                                      user, callout_info);
+                       if (key)
+                               break;
+
+                       /* someone else made the key we want, so we need to
+                        * search again as it might now be available to us */
+                       key = search_process_keyrings_aux(type, description,
+                                                         type->match);
+                       if (PTR_ERR(key) != -EAGAIN)
+                               break;
+               }
+
+               key_user_put(user);
+       }
+
+ error:
+       return key;
+
+} /* end request_key() */
+
+EXPORT_SYMBOL(request_key);
+
+/*****************************************************************************/
+/*
+ * validate a key
+ */
+int key_validate(struct key *key)
+{
+       struct timespec now;
+       int ret = 0;
+
+       if (key) {
+               /* check it's still accessible */
+               ret = -EKEYREVOKED;
+               if (key->flags & (KEY_FLAG_REVOKED | KEY_FLAG_DEAD))
+                       goto error;
+
+               /* check it hasn't expired */
+               ret = 0;
+               if (key->expiry) {
+                       now = current_kernel_time();
+                       if (now.tv_sec >= key->expiry)
+                               ret = -EKEYEXPIRED;
+               }
+       }
+
+ error:
+       return ret;
+
+} /* end key_validate() */
+
+EXPORT_SYMBOL(key_validate);
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
new file mode 100644 (file)
index 0000000..8d65b3a
--- /dev/null
@@ -0,0 +1,191 @@
+/* user_defined.c: user defined key type
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+static int user_instantiate(struct key *key, const void *data, size_t datalen);
+static int user_duplicate(struct key *key, const struct key *source);
+static int user_update(struct key *key, const void *data, size_t datalen);
+static int user_match(const struct key *key, const void *criterion);
+static void user_destroy(struct key *key);
+static void user_describe(const struct key *user, struct seq_file *m);
+static long user_read(const struct key *key,
+                     char __user *buffer, size_t buflen);
+
+/*
+ * user defined keys take an arbitrary string as the description and an
+ * arbitrary blob of data as the payload
+ */
+struct key_type key_type_user = {
+       .name           = "user",
+       .instantiate    = user_instantiate,
+       .duplicate      = user_duplicate,
+       .update         = user_update,
+       .match          = user_match,
+       .destroy        = user_destroy,
+       .describe       = user_describe,
+       .read           = user_read,
+};
+
+/*****************************************************************************/
+/*
+ * instantiate a user defined key
+ */
+static int user_instantiate(struct key *key, const void *data, size_t datalen)
+{
+       int ret;
+
+       ret = -EINVAL;
+       if (datalen <= 0 || datalen > 32767 || !data)
+               goto error;
+
+       ret = key_payload_reserve(key, datalen);
+       if (ret < 0)
+               goto error;
+
+       /* attach the data */
+       ret = -ENOMEM;
+       key->payload.data = kmalloc(datalen, GFP_KERNEL);
+       if (!key->payload.data)
+               goto error;
+
+       memcpy(key->payload.data, data, datalen);
+       ret = 0;
+
+ error:
+       return ret;
+
+} /* end user_instantiate() */
+
+/*****************************************************************************/
+/*
+ * duplicate a user defined key
+ */
+static int user_duplicate(struct key *key, const struct key *source)
+{
+       int ret;
+
+       /* just copy the payload */
+       ret = -ENOMEM;
+       key->payload.data = kmalloc(source->datalen, GFP_KERNEL);
+
+       if (key->payload.data) {
+               key->datalen = source->datalen;
+               memcpy(key->payload.data, source->payload.data, source->datalen);
+               ret = 0;
+       }
+
+       return ret;
+
+} /* end user_duplicate() */
+
+/*****************************************************************************/
+/*
+ * update a user defined key
+ */
+static int user_update(struct key *key, const void *data, size_t datalen)
+{
+       void *new, *zap;
+       int ret;
+
+       ret = -EINVAL;
+       if (datalen <= 0 || datalen > 32767 || !data)
+               goto error;
+
+       /* copy the data */
+       ret = -ENOMEM;
+       new = kmalloc(datalen, GFP_KERNEL);
+       if (!new)
+               goto error;
+
+       memcpy(new, data, datalen);
+
+       /* check the quota and attach the new data */
+       zap = new;
+       write_lock(&key->lock);
+
+       ret = key_payload_reserve(key, datalen);
+
+       if (ret == 0) {
+               /* attach the new data, displacing the old */
+               zap = key->payload.data;
+               key->payload.data = new;
+               key->expiry = 0;
+       }
+
+       write_unlock(&key->lock);
+       kfree(zap);
+
+ error:
+       return ret;
+
+} /* end user_update() */
+
+/*****************************************************************************/
+/*
+ * match users on their name
+ */
+static int user_match(const struct key *key, const void *description)
+{
+       return strcmp(key->description, description) == 0;
+
+} /* end user_match() */
+
+/*****************************************************************************/
+/*
+ * dispose of the data dangling from the corpse of a user
+ */
+static void user_destroy(struct key *key)
+{
+       kfree(key->payload.data);
+
+} /* end user_destroy() */
+
+/*****************************************************************************/
+/*
+ * describe the user
+ */
+static void user_describe(const struct key *key, struct seq_file *m)
+{
+       seq_puts(m, key->description);
+
+       seq_printf(m, ": %u", key->datalen);
+
+} /* end user_describe() */
+
+/*****************************************************************************/
+/*
+ * read the key data
+ */
+static long user_read(const struct key *key,
+                     char __user *buffer, size_t buflen)
+{
+       long ret = key->datalen;
+
+       /* we can return the data as is */
+       if (buffer && buflen > 0) {
+               if (buflen > key->datalen)
+                       buflen = key->datalen;
+
+               if (copy_to_user(buffer, key->payload.data, buflen) != 0)
+                       ret = -EFAULT;
+       }
+
+       return ret;
+
+} /* end user_read() */
diff --git a/security/seclvl.c b/security/seclvl.c
new file mode 100644 (file)
index 0000000..6a06bb2
--- /dev/null
@@ -0,0 +1,747 @@
+/**
+ * BSD Secure Levels LSM
+ *
+ * Maintainers:
+ *     Michael A. Halcrow <mike@halcrow.us>
+ *     Serge Hallyn <hallyn@cs.wm.edu>
+ *
+ * Copyright (c) 2001 WireX Communications, Inc <chris@wirex.com>
+ * Copyright (c) 2001 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (c) 2002 International Business Machines <robb@austin.ibm.com>
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+#include <linux/netlink.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/capability.h>
+#include <linux/time.h>
+#include <linux/proc_fs.h>
+#include <linux/kobject.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+#include <linux/gfp.h>
+#include <linux/sysfs.h>
+
+#define SHA1_DIGEST_SIZE 20
+
+/**
+ * Module parameter that defines the initial secure level.
+ *
+ * When built as a module, it defaults to seclvl 1, which is the
+ * behavior of BSD secure levels.  Note that this default behavior
+ * wrecks havoc on a machine when the seclvl module is compiled into
+ * the kernel. In that case, we default to seclvl 0.
+ */
+#ifdef CONFIG_SECURITY_SECLVL_MODULE
+static int initlvl = 1;
+#else
+static int initlvl;
+#endif
+module_param(initlvl, int, 0);
+MODULE_PARM_DESC(initlvl, "Initial secure level (defaults to 1)");
+
+/* Module parameter that defines the verbosity level */
+static int verbosity;
+module_param(verbosity, int, 0);
+MODULE_PARM_DESC(verbosity, "Initial verbosity level (0 or 1; defaults to "
+                "0, which is Quiet)");
+
+/**
+ * Optional password which can be passed in to bring seclvl to 0
+ * (i.e., for halt/reboot).  Defaults to NULL (the passwd attribute
+ * file will not be registered in sysfs).
+ *
+ * This gets converted to its SHA1 hash when stored.  It's probably
+ * not a good idea to use this parameter when loading seclvl from a
+ * script; use sha1_passwd instead.
+ */
+
+#define MAX_PASSWD_SIZE        32
+static char passwd[MAX_PASSWD_SIZE];
+module_param_string(passwd, passwd, sizeof(passwd), 0);
+MODULE_PARM_DESC(passwd,
+                "Plaintext of password that sets seclvl=0 when written to "
+                "(sysfs mount point)/seclvl/passwd\n");
+
+/**
+ * SHA1 hashed version of the optional password which can be passed in
+ * to bring seclvl to 0 (i.e., for halt/reboot).  Must be in
+ * hexadecimal format (40 characters). Defaults to NULL (the passwd
+ * attribute file will not be registered in sysfs).
+ *
+ * Use the sha1sum utility to generate the SHA1 hash of a password:
+ *
+ * echo -n "secret" | sha1sum
+ */
+#define MAX_SHA1_PASSWD        41
+static char sha1_passwd[MAX_SHA1_PASSWD];
+module_param_string(sha1_passwd, sha1_passwd, sizeof(sha1_passwd), 0);
+MODULE_PARM_DESC(sha1_passwd,
+                "SHA1 hash (40 hexadecimal characters) of password that "
+                "sets seclvl=0 when plaintext password is written to "
+                "(sysfs mount point)/seclvl/passwd\n");
+
+static int hideHash = 1;
+module_param(hideHash, int, 0);
+MODULE_PARM_DESC(hideHash, "When set to 0, reading seclvl/passwd from sysfs "
+                "will return the SHA1-hashed value of the password that "
+                "lowers the secure level to 0.\n");
+
+#define MY_NAME "seclvl"
+
+/**
+ * This time-limits log writes to one per second.
+ */
+#define seclvl_printk(verb, type, fmt, arg...)                 \
+       do {                                                    \
+               if (verbosity >= verb) {                        \
+                       static unsigned long _prior;            \
+                       unsigned long _now = jiffies;           \
+                       if ((_now - _prior) > HZ) {             \
+                               printk(type "%s: %s: " fmt,     \
+                                       MY_NAME, __FUNCTION__ , \
+                                       ## arg);                \
+                               _prior = _now;                  \
+                       }                                       \
+               }                                               \
+       } while (0)
+
+/**
+ * kobject stuff
+ */
+
+struct subsystem seclvl_subsys;
+
+struct seclvl_obj {
+       char *name;
+       struct list_head slot_list;
+       struct kobject kobj;
+};
+
+/**
+ * There is a seclvl_attribute struct for each file in sysfs.
+ *
+ * In our case, we have one of these structs for "passwd" and another
+ * for "seclvl".
+ */
+struct seclvl_attribute {
+       struct attribute attr;
+       ssize_t(*show) (struct seclvl_obj *, char *);
+       ssize_t(*store) (struct seclvl_obj *, const char *, size_t);
+};
+
+/**
+ * When this function is called, one of the files in sysfs is being
+ * written to.  attribute->store is a function pointer to whatever the
+ * struct seclvl_attribute store function pointer points to.  It is
+ * unique for "passwd" and "seclvl".
+ */
+static ssize_t
+seclvl_attr_store(struct kobject *kobj,
+                 struct attribute *attr, const char *buf, size_t len)
+{
+       struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj);
+       struct seclvl_attribute *attribute =
+           container_of(attr, struct seclvl_attribute, attr);
+       return (attribute->store ? attribute->store(obj, buf, len) : 0);
+}
+
+static ssize_t
+seclvl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+       struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj);
+       struct seclvl_attribute *attribute =
+           container_of(attr, struct seclvl_attribute, attr);
+       return (attribute->show ? attribute->show(obj, buf) : 0);
+}
+
+/**
+ * Callback function pointers for show and store
+ */
+struct sysfs_ops seclvlfs_sysfs_ops = {
+       .show = seclvl_attr_show,
+       .store = seclvl_attr_store,
+};
+
+static struct kobj_type seclvl_ktype = {
+       .sysfs_ops = &seclvlfs_sysfs_ops
+};
+
+decl_subsys(seclvl, &seclvl_ktype, NULL);
+
+/**
+ * The actual security level.  Ranges between -1 and 2 inclusive.
+ */
+static int seclvl;
+
+/**
+ * flag to keep track of how we were registered
+ */
+static int secondary;
+
+/**
+ * Verifies that the requested secure level is valid, given the current
+ * secure level.
+ */
+static int seclvl_sanity(int reqlvl)
+{
+       if ((reqlvl < -1) || (reqlvl > 2)) {
+               seclvl_printk(1, KERN_WARNING, "Attempt to set seclvl out of "
+                             "range: [%d]\n", reqlvl);
+               return -EINVAL;
+       }
+       if ((seclvl == 0) && (reqlvl == -1))
+               return 0;
+       if (reqlvl < seclvl) {
+               seclvl_printk(1, KERN_WARNING, "Attempt to lower seclvl to "
+                             "[%d]\n", reqlvl);
+               return -EPERM;
+       }
+       return 0;
+}
+
+/**
+ * Called whenever the user reads the sysfs handle to this kernel
+ * object
+ */
+static ssize_t seclvl_read_file(struct seclvl_obj *obj, char *buff)
+{
+       return snprintf(buff, PAGE_SIZE, "%d\n", seclvl);
+}
+
+/**
+ * security level advancement rules:
+ *   Valid levels are -1 through 2, inclusive.
+ *   From -1, stuck.  [ in case compiled into kernel ]
+ *   From 0 or above, can only increment.
+ */
+static int do_seclvl_advance(int newlvl)
+{
+       if (newlvl <= seclvl) {
+               seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl "
+                             "[%d]\n", newlvl);
+               return -EINVAL;
+       }
+       if (newlvl > 2) {
+               seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl "
+                             "[%d]\n", newlvl);
+               return -EINVAL;
+       }
+       if (seclvl == -1) {
+               seclvl_printk(1, KERN_WARNING, "Not allowed to advance to "
+                             "seclvl [%d]\n", seclvl);
+               return -EPERM;
+       }
+       seclvl = newlvl;
+       return 0;
+}
+
+/**
+ * Called whenever the user writes to the sysfs handle to this kernel
+ * object (seclvl/seclvl).  It expects a single-digit number.
+ */
+static ssize_t
+seclvl_write_file(struct seclvl_obj *obj, const char *buff, size_t count)
+{
+       unsigned long val;
+       if (count > 2 || (count == 2 && buff[1] != '\n')) {
+               seclvl_printk(1, KERN_WARNING, "Invalid value passed to "
+                             "seclvl: [%s]\n", buff);
+               return -EINVAL;
+       }
+       val = buff[0] - 48;
+       if (seclvl_sanity(val)) {
+               seclvl_printk(1, KERN_WARNING, "Illegal secure level "
+                             "requested: [%d]\n", (int)val);
+               return -EPERM;
+       }
+       if (do_seclvl_advance(val)) {
+               seclvl_printk(0, KERN_ERR, "Failure advancing security level "
+                             "to %lu\n", val);
+       }
+       return count;
+}
+
+/* Generate sysfs_attr_seclvl */
+struct seclvl_attribute sysfs_attr_seclvl =
+__ATTR(seclvl, (S_IFREG | S_IRUGO | S_IWUSR), seclvl_read_file,
+       seclvl_write_file);
+
+static unsigned char hashedPassword[SHA1_DIGEST_SIZE];
+
+/**
+ * Called whenever the user reads the sysfs passwd handle.
+ */
+static ssize_t seclvl_read_passwd(struct seclvl_obj *obj, char *buff)
+{
+       /* So just how good *is* your password? :-) */
+       char tmp[3];
+       int i = 0;
+       buff[0] = '\0';
+       if (hideHash) {
+               /* Security through obscurity */
+               return 0;
+       }
+       while (i < SHA1_DIGEST_SIZE) {
+               snprintf(tmp, 3, "%02x", hashedPassword[i]);
+               strncat(buff, tmp, 2);
+               i++;
+       }
+       strcat(buff, "\n");
+       return ((SHA1_DIGEST_SIZE * 2) + 1);
+}
+
+/**
+ * Converts a block of plaintext of into its SHA1 hashed value.
+ *
+ * It would be nice if crypto had a wrapper to do this for us linear
+ * people...
+ */
+static int
+plaintext_to_sha1(unsigned char *hash, const char *plaintext, int len)
+{
+       char *pgVirtAddr;
+       struct crypto_tfm *tfm;
+       struct scatterlist sg[1];
+       if (len > PAGE_SIZE) {
+               seclvl_printk(0, KERN_ERR, "Plaintext password too large (%d "
+                             "characters).  Largest possible is %lu "
+                             "bytes.\n", len, PAGE_SIZE);
+               return -ENOMEM;
+       }
+       tfm = crypto_alloc_tfm("sha1", 0);
+       if (tfm == NULL) {
+               seclvl_printk(0, KERN_ERR,
+                             "Failed to load transform for SHA1\n");
+               return -ENOSYS;
+       }
+       // Just get a new page; don't play around with page boundaries
+       // and scatterlists.
+       pgVirtAddr = (char *)__get_free_page(GFP_KERNEL);
+       sg[0].page = virt_to_page(pgVirtAddr);
+       sg[0].offset = 0;
+       sg[0].length = len;
+       strncpy(pgVirtAddr, plaintext, len);
+       crypto_digest_init(tfm);
+       crypto_digest_update(tfm, sg, 1);
+       crypto_digest_final(tfm, hash);
+       crypto_free_tfm(tfm);
+       free_page((unsigned long)pgVirtAddr);
+       return 0;
+}
+
+/**
+ * Called whenever the user writes to the sysfs passwd handle to this kernel
+ * object.  It hashes the password and compares the hashed results.
+ */
+static ssize_t
+seclvl_write_passwd(struct seclvl_obj *obj, const char *buff, size_t count)
+{
+       int i;
+       unsigned char tmp[SHA1_DIGEST_SIZE];
+       int rc;
+       int len;
+       if (!*passwd && !*sha1_passwd) {
+               seclvl_printk(0, KERN_ERR, "Attempt to password-unlock the "
+                             "seclvl module, but neither a plain text "
+                             "password nor a SHA1 hashed password was "
+                             "passed in as a module parameter!  This is a "
+                             "bug, since it should not be possible to be in "
+                             "this part of the module; please tell a "
+                             "maintainer about this event.\n");
+               return -EINVAL;
+       }
+       len = strlen(buff);
+       /* ``echo "secret" > seclvl/passwd'' includes a newline */
+       if (buff[len - 1] == '\n') {
+               len--;
+       }
+       /* Hash the password, then compare the hashed values */
+       if ((rc = plaintext_to_sha1(tmp, buff, len))) {
+               seclvl_printk(0, KERN_ERR, "Error hashing password: rc = "
+                             "[%d]\n", rc);
+               return rc;
+       }
+       for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+               if (hashedPassword[i] != tmp[i]) {
+                       return -EPERM;
+               }
+       }
+       seclvl_printk(0, KERN_INFO,
+                     "Password accepted; seclvl reduced to 0.\n");
+       seclvl = 0;
+       return count;
+}
+
+/* Generate sysfs_attr_passwd */
+struct seclvl_attribute sysfs_attr_passwd =
+__ATTR(passwd, (S_IFREG | S_IRUGO | S_IWUSR), seclvl_read_passwd,
+       seclvl_write_passwd);
+
+/**
+ * Explicitely disallow ptrace'ing the init process.
+ */
+static int seclvl_ptrace(struct task_struct *parent, struct task_struct *child)
+{
+       if (seclvl >= 0) {
+               if (child->pid == 1) {
+                       seclvl_printk(1, KERN_WARNING, "Attempt to ptrace "
+                                     "the init process dissallowed in "
+                                     "secure level %d\n", seclvl);
+                       return -EPERM;
+               }
+       }
+       return 0;
+}
+
+/**
+ * Capability checks for seclvl.  The majority of the policy
+ * enforcement for seclvl takes place here.
+ */
+static int seclvl_capable(struct task_struct *tsk, int cap)
+{
+       /* init can do anything it wants */
+       if (tsk->pid == 1)
+               return 0;
+
+       switch (seclvl) {
+       case 2:
+               /* fall through */
+       case 1:
+               if (cap == CAP_LINUX_IMMUTABLE) {
+                       seclvl_printk(1, KERN_WARNING, "Attempt to modify "
+                                     "the IMMUTABLE and/or APPEND extended "
+                                     "attribute on a file with the IMMUTABLE "
+                                     "and/or APPEND extended attribute set "
+                                     "denied in seclvl [%d]\n", seclvl);
+                       return -EPERM;
+               } else if (cap == CAP_SYS_RAWIO) {      // Somewhat broad...
+                       seclvl_printk(1, KERN_WARNING, "Attempt to perform "
+                                     "raw I/O while in secure level [%d] "
+                                     "denied\n", seclvl);
+                       return -EPERM;
+               } else if (cap == CAP_NET_ADMIN) {
+                       seclvl_printk(1, KERN_WARNING, "Attempt to perform "
+                                     "network administrative task while "
+                                     "in secure level [%d] denied\n", seclvl);
+                       return -EPERM;
+               } else if (cap == CAP_SETUID) {
+                       seclvl_printk(1, KERN_WARNING, "Attempt to setuid "
+                                     "while in secure level [%d] denied\n",
+                                     seclvl);
+                       return -EPERM;
+               } else if (cap == CAP_SETGID) {
+                       seclvl_printk(1, KERN_WARNING, "Attempt to setgid "
+                                     "while in secure level [%d] denied\n",
+                                     seclvl);
+               } else if (cap == CAP_SYS_MODULE) {
+                       seclvl_printk(1, KERN_WARNING, "Attempt to perform "
+                                     "a module operation while in secure "
+                                     "level [%d] denied\n", seclvl);
+                       return -EPERM;
+               }
+               break;
+       default:
+               break;
+       }
+       /* from dummy.c */
+       if (cap_is_fs_cap(cap) ? tsk->fsuid == 0 : tsk->euid == 0)
+               return 0;       /* capability granted */
+       seclvl_printk(1, KERN_WARNING, "Capability denied\n");
+       return -EPERM;          /* capability denied */
+}
+
+/**
+ * Disallow reversing the clock in seclvl > 1
+ */
+static int seclvl_settime(struct timespec *tv, struct timezone *tz)
+{
+       struct timespec now;
+       if (seclvl > 1) {
+               now = current_kernel_time();
+               if (tv->tv_sec < now.tv_sec ||
+                   (tv->tv_sec == now.tv_sec && tv->tv_nsec < now.tv_nsec)) {
+                       seclvl_printk(1, KERN_WARNING, "Attempt to decrement "
+                                     "time in secure level %d denied: "
+                                     "current->pid = [%d], "
+                                     "current->group_leader->pid = [%d]\n",
+                                     seclvl, current->pid,
+                                     current->group_leader->pid);
+                       return -EPERM;
+               }               /* if attempt to decrement time */
+       }                       /* if seclvl > 1 */
+       return 0;
+}
+
+/* claim the blockdev to exclude mounters, release on file close */
+static int seclvl_bd_claim(struct inode *inode)
+{
+       int holder;
+       struct block_device *bdev = NULL;
+       dev_t dev = inode->i_rdev;
+       bdev = open_by_devnum(dev, FMODE_WRITE);
+       if (bdev) {
+               if (bd_claim(bdev, &holder)) {
+                       blkdev_put(bdev);
+                       return -EPERM;
+               }
+               /* claimed, mark it to release on close */
+               inode->i_security = current;
+       }
+       return 0;
+}
+
+/* release the blockdev if you claimed it */
+static void seclvl_bd_release(struct inode *inode)
+{
+       if (inode && S_ISBLK(inode->i_mode) && inode->i_security == current) {
+               struct block_device *bdev = inode->i_bdev;
+               if (bdev) {
+                       bd_release(bdev);
+                       blkdev_put(bdev);
+                       inode->i_security = NULL;
+               }
+       }
+}
+
+/**
+ * Security for writes to block devices is regulated by this seclvl
+ * function.  Deny all writes to block devices in seclvl 2.  In
+ * seclvl 1, we only deny writes to *mounted* block devices.
+ */
+static int
+seclvl_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+       if (current->pid != 1 && S_ISBLK(inode->i_mode) && (mask & MAY_WRITE)) {
+               switch (seclvl) {
+               case 2:
+                       seclvl_printk(1, KERN_WARNING, "Write to block device "
+                                     "denied in secure level [%d]\n", seclvl);
+                       return -EPERM;
+               case 1:
+                       if (seclvl_bd_claim(inode)) {
+                               seclvl_printk(1, KERN_WARNING,
+                                             "Write to mounted block device "
+                                             "denied in secure level [%d]\n",
+                                             seclvl);
+                               return -EPERM;
+                       }
+               }
+       }
+       return 0;
+}
+
+/**
+ * The SUID and SGID bits cannot be set in seclvl >= 1
+ */
+static int seclvl_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+       if (seclvl > 0) {
+               if (iattr->ia_valid & ATTR_MODE)
+                       if (iattr->ia_mode & S_ISUID ||
+                           iattr->ia_mode & S_ISGID) {
+                               seclvl_printk(1, KERN_WARNING, "Attempt to "
+                                             "modify SUID or SGID bit "
+                                             "denied in seclvl [%d]\n",
+                                             seclvl);
+                               return -EPERM;
+                       }
+       }
+       return 0;
+}
+
+/* release busied block devices */
+static void seclvl_file_free_security(struct file *filp)
+{
+       struct dentry *dentry = filp->f_dentry;
+       struct inode *inode = NULL;
+
+       if (dentry) {
+               inode = dentry->d_inode;
+               seclvl_bd_release(inode);
+       }
+}
+
+/**
+ * Cannot unmount in secure level 2
+ */
+static int seclvl_umount(struct vfsmount *mnt, int flags)
+{
+       if (current->pid == 1) {
+               return 0;
+       }
+       if (seclvl == 2) {
+               seclvl_printk(1, KERN_WARNING, "Attempt to unmount in secure "
+                             "level %d\n", seclvl);
+               return -EPERM;
+       }
+       return 0;
+}
+
+static struct security_operations seclvl_ops = {
+       .ptrace = seclvl_ptrace,
+       .capable = seclvl_capable,
+       .inode_permission = seclvl_inode_permission,
+       .inode_setattr = seclvl_inode_setattr,
+       .file_free_security = seclvl_file_free_security,
+       .settime = seclvl_settime,
+       .sb_umount = seclvl_umount,
+};
+
+/**
+ * Process the password-related module parameters
+ */
+static int processPassword(void)
+{
+       int rc = 0;
+       hashedPassword[0] = '\0';
+       if (*passwd) {
+               if (*sha1_passwd) {
+                       seclvl_printk(0, KERN_ERR, "Error: Both "
+                                     "passwd and sha1_passwd "
+                                     "were set, but they are mutually "
+                                     "exclusive.\n");
+                       return -EINVAL;
+               }
+               if ((rc = plaintext_to_sha1(hashedPassword, passwd,
+                                           strlen(passwd)))) {
+                       seclvl_printk(0, KERN_ERR, "Error: SHA1 support not "
+                                     "in kernel\n");
+                       return rc;
+               }
+               /* All static data goes to the BSS, which zero's the
+                * plaintext password out for us. */
+       } else if (*sha1_passwd) {      // Base 16
+               int i;
+               i = strlen(sha1_passwd);
+               if (i != (SHA1_DIGEST_SIZE * 2)) {
+                       seclvl_printk(0, KERN_ERR, "Received [%d] bytes; "
+                                     "expected [%d] for the hexadecimal "
+                                     "representation of the SHA1 hash of "
+                                     "the password.\n",
+                                     i, (SHA1_DIGEST_SIZE * 2));
+                       return -EINVAL;
+               }
+               while ((i -= 2) + 2) {
+                       unsigned char tmp;
+                       tmp = sha1_passwd[i + 2];
+                       sha1_passwd[i + 2] = '\0';
+                       hashedPassword[i / 2] = (unsigned char)
+                           simple_strtol(&sha1_passwd[i], NULL, 16);
+                       sha1_passwd[i + 2] = tmp;
+               }
+       }
+       return 0;
+}
+
+/**
+ * Sysfs registrations
+ */
+static int doSysfsRegistrations(void)
+{
+       int rc = 0;
+       if ((rc = subsystem_register(&seclvl_subsys))) {
+               seclvl_printk(0, KERN_WARNING,
+                             "Error [%d] registering seclvl subsystem\n", rc);
+               return rc;
+       }
+       sysfs_create_file(&seclvl_subsys.kset.kobj, &sysfs_attr_seclvl.attr);
+       if (*passwd || *sha1_passwd) {
+               sysfs_create_file(&seclvl_subsys.kset.kobj,
+                                 &sysfs_attr_passwd.attr);
+       }
+       return 0;
+}
+
+/**
+ * Initialize the seclvl module.
+ */
+static int __init seclvl_init(void)
+{
+       int rc = 0;
+       if (verbosity < 0 || verbosity > 1) {
+               printk(KERN_ERR "Error: bad verbosity [%d]; only 0 or 1 "
+                      "are valid values\n", verbosity);
+               rc = -EINVAL;
+               goto exit;
+       }
+       sysfs_attr_seclvl.attr.owner = THIS_MODULE;
+       sysfs_attr_passwd.attr.owner = THIS_MODULE;
+       if (initlvl < -1 || initlvl > 2) {
+               seclvl_printk(0, KERN_ERR, "Error: bad initial securelevel "
+                             "[%d].\n", initlvl);
+               rc = -EINVAL;
+               goto exit;
+       }
+       seclvl = initlvl;
+       if ((rc = processPassword())) {
+               seclvl_printk(0, KERN_ERR, "Error processing the password "
+                             "module parameter(s): rc = [%d]\n", rc);
+               goto exit;
+       }
+       /* register ourselves with the security framework */
+       if (register_security(&seclvl_ops)) {
+               seclvl_printk(0, KERN_ERR,
+                             "seclvl: Failure registering with the "
+                             "kernel.\n");
+               /* try registering with primary module */
+               rc = mod_reg_security(MY_NAME, &seclvl_ops);
+               if (rc) {
+                       seclvl_printk(0, KERN_ERR, "seclvl: Failure "
+                                     "registering with primary security "
+                                     "module.\n");
+                       goto exit;
+               }               /* if primary module registered */
+               secondary = 1;
+       }                       /* if we registered ourselves with the security framework */
+       if ((rc = doSysfsRegistrations())) {
+               seclvl_printk(0, KERN_ERR, "Error registering with sysfs\n");
+               goto exit;
+       }
+       seclvl_printk(0, KERN_INFO, "seclvl: Successfully initialized.\n");
+ exit:
+       if (rc) {
+               printk(KERN_ERR "seclvl: Error during initialization: rc = "
+                      "[%d]\n", rc);
+       }
+       return rc;
+}
+
+/**
+ * Remove the seclvl module.
+ */
+static void __exit seclvl_exit(void)
+{
+       sysfs_remove_file(&seclvl_subsys.kset.kobj, &sysfs_attr_seclvl.attr);
+       if (*passwd || *sha1_passwd) {
+               sysfs_remove_file(&seclvl_subsys.kset.kobj,
+                                 &sysfs_attr_passwd.attr);
+       }
+       subsystem_unregister(&seclvl_subsys);
+       if (secondary == 1) {
+               mod_unreg_security(MY_NAME, &seclvl_ops);
+       } else if (unregister_security(&seclvl_ops)) {
+               seclvl_printk(0, KERN_INFO,
+                             "seclvl: Failure unregistering with the "
+                             "kernel\n");
+       }
+}
+
+module_init(seclvl_init);
+module_exit(seclvl_exit);
+
+MODULE_AUTHOR("Michael A. Halcrow <mike@halcrow.us>");
+MODULE_DESCRIPTION("LSM implementation of the BSD Secure Levels");
+MODULE_LICENSE("GPL");
diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig
new file mode 100644 (file)
index 0000000..531f8ba
--- /dev/null
@@ -0,0 +1,15 @@
+# ALSA MIPS drivers
+
+menu "ALSA MIPS devices"
+       depends on SND!=n && MIPS
+
+config SND_AU1X00
+       tristate "Au1x00 AC97 Port Driver"
+       depends on (SOC_AU1000 || SOC_AU1100 || SOC_AU1500) && SND
+       select SND_PCM
+       select SND_AC97_CODEC
+       help
+         ALSA Sound driver for the Au1x00's AC97 port.
+
+endmenu
+
diff --git a/sound/mips/Makefile b/sound/mips/Makefile
new file mode 100644 (file)
index 0000000..47afed9
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+#
+
+snd-au1x00-objs := au1x00.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
new file mode 100644 (file)
index 0000000..c20522b
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *  Driver for AMD Au1000 MIPS Processor, AC'97 Sound Port
+ *
+ * Copyright 2004 Cooper Street Innovations Inc.
+ * Author: Charles Eidsness    <charles@cooper-street.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  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.
+ *
+ * History:
+ *
+ * 2004-09-09 Charles Eidsness -- Original verion -- based on
+ *                               sa11xx-uda1341.c ALSA driver and the
+ *                               au1000.c OSS driver.
+ * 2004-09-09 Matt Porter      -- Added support for ALSA 1.0.6
+ *
+ */
+
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1000_dma.h>
+
+MODULE_AUTHOR("Charles Eidsness <charles@cooper-street.com>");
+MODULE_DESCRIPTION("Au1000 AC'97 ALSA Driver");
+MODULE_LICENSE("GPL");
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,8)
+MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}");
+#else
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{AMD,Au1000 AC'97}}");
+#endif
+
+#define chip_t au1000_t
+
+#define PLAYBACK 0
+#define CAPTURE 1
+#define AC97_SLOT_3 0x01
+#define AC97_SLOT_4 0x02
+#define AC97_SLOT_6 0x08
+#define AC97_CMD_IRQ 31
+#define READ 0
+#define WRITE 1
+#define READ_WAIT 2
+#define RW_DONE 3
+
+DECLARE_WAIT_QUEUE_HEAD(ac97_command_wq);
+
+typedef struct au1000_period au1000_period_t;
+struct au1000_period
+{
+       u32 start;
+       u32 relative_end;       /*realtive to start of buffer*/
+       au1000_period_t * next;
+};
+
+/*Au1000 AC97 Port Control Reisters*/
+typedef struct au1000_ac97_reg au1000_ac97_reg_t;
+struct au1000_ac97_reg {
+       u32 volatile config;
+       u32 volatile status;
+       u32 volatile data;
+       u32 volatile cmd;
+       u32 volatile cntrl;
+};
+
+typedef struct audio_stream audio_stream_t;
+struct audio_stream {
+       snd_pcm_substream_t * substream;
+       int dma;
+       spinlock_t dma_lock;
+       au1000_period_t * buffer;
+       unsigned long period_size;
+};
+
+typedef struct snd_card_au1000 {
+       snd_card_t *card;
+       au1000_ac97_reg_t volatile *ac97_ioport;
+
+       struct resource *ac97_res_port;
+       spinlock_t ac97_lock;
+       ac97_t *ac97;
+
+       snd_pcm_t *pcm;
+       audio_stream_t *stream[2];      /* playback & capture */
+} au1000_t;
+
+static au1000_t *au1000 = NULL;
+
+/*--------------------------- Local Functions --------------------------------*/
+static void
+au1000_set_ac97_xmit_slots(long xmit_slots)
+{
+       u32 volatile ac97_config;
+
+       spin_lock(&au1000->ac97_lock);
+       ac97_config = au1000->ac97_ioport->config;
+       ac97_config = ac97_config & ~AC97C_XMIT_SLOTS_MASK;
+       ac97_config |= (xmit_slots << AC97C_XMIT_SLOTS_BIT);
+       au1000->ac97_ioport->config = ac97_config;
+       spin_unlock(&au1000->ac97_lock);
+}
+
+static void
+au1000_set_ac97_recv_slots(long recv_slots)
+{
+       u32 volatile ac97_config;
+
+       spin_lock(&au1000->ac97_lock);
+       ac97_config = au1000->ac97_ioport->config;
+       ac97_config = ac97_config & ~AC97C_RECV_SLOTS_MASK;
+       ac97_config |= (recv_slots << AC97C_RECV_SLOTS_BIT);
+       au1000->ac97_ioport->config = ac97_config;
+       spin_unlock(&au1000->ac97_lock);
+}
+
+
+static void
+au1000_dma_stop(audio_stream_t *stream)
+{
+       unsigned long   flags;
+       au1000_period_t * pointer;
+       au1000_period_t * pointer_next;
+
+       if (stream->buffer != NULL) {
+               spin_lock_irqsave(&stream->dma_lock, flags);
+               disable_dma(stream->dma);
+               spin_unlock_irqrestore(&stream->dma_lock, flags);
+
+               pointer = stream->buffer;
+               pointer_next = stream->buffer->next;
+
+               do {
+                       kfree(pointer);
+                       pointer = pointer_next;
+                       pointer_next = pointer->next;
+               } while (pointer != stream->buffer);
+
+               stream->buffer = NULL;
+       }
+}
+
+static void
+au1000_dma_start(audio_stream_t *stream)
+{
+       snd_pcm_substream_t *substream = stream->substream;
+       snd_pcm_runtime_t *runtime = substream->runtime;
+
+       unsigned long flags, dma_start;
+       int i;
+       au1000_period_t * pointer;
+
+       if (stream->buffer == NULL) {
+               dma_start = virt_to_phys(runtime->dma_area);
+
+               stream->period_size = frames_to_bytes(runtime,
+                       runtime->period_size);
+               stream->buffer = kmalloc(sizeof(au1000_period_t), GFP_KERNEL);
+               pointer = stream->buffer;
+               for (i = 0 ; i < runtime->periods ; i++) {
+                       pointer->start = (u32)(dma_start +
+                               (i * stream->period_size));
+                       pointer->relative_end = (u32)
+                               (((i+1) * stream->period_size) - 0x1);
+                       if ( i < runtime->periods - 1) {
+                               pointer->next = kmalloc(sizeof(au1000_period_t)
+                                       , GFP_KERNEL);
+                               pointer = pointer->next;
+                       }
+               }
+               pointer->next = stream->buffer;
+
+               spin_lock_irqsave(&stream->dma_lock, flags);
+               init_dma(stream->dma);
+               if (get_dma_active_buffer(stream->dma) == 0) {
+                       clear_dma_done0(stream->dma);
+                       set_dma_addr0(stream->dma, stream->buffer->start);
+                       set_dma_count0(stream->dma, stream->period_size >> 1);
+                       set_dma_addr1(stream->dma, stream->buffer->next->start);
+                       set_dma_count1(stream->dma, stream->period_size >> 1);
+               } else {
+                       clear_dma_done1(stream->dma);
+                       set_dma_addr1(stream->dma, stream->buffer->start);
+                       set_dma_count1(stream->dma, stream->period_size >> 1);
+                       set_dma_addr0(stream->dma, stream->buffer->next->start);
+                       set_dma_count0(stream->dma, stream->period_size >> 1);
+               }
+               enable_dma_buffers(stream->dma);
+               start_dma(stream->dma);
+               spin_unlock_irqrestore(&stream->dma_lock, flags);
+       }
+}
+
+static irqreturn_t
+au1000_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       audio_stream_t *stream = (audio_stream_t *) dev_id;
+       snd_pcm_substream_t *substream = stream->substream;
+
+       spin_lock(&stream->dma_lock);
+       switch (get_dma_buffer_done(stream->dma)) {
+       case DMA_D0:
+               stream->buffer = stream->buffer->next;
+               clear_dma_done0(stream->dma);
+               set_dma_addr0(stream->dma, stream->buffer->next->start);
+               set_dma_count0(stream->dma, stream->period_size >> 1);
+               enable_dma_buffer0(stream->dma);
+               break;
+       case DMA_D1:
+               stream->buffer = stream->buffer->next;
+               clear_dma_done1(stream->dma);
+               set_dma_addr1(stream->dma, stream->buffer->next->start);
+               set_dma_count1(stream->dma, stream->period_size >> 1);
+               enable_dma_buffer1(stream->dma);
+               break;
+       case (DMA_D0 | DMA_D1):
+               spin_unlock(&stream->dma_lock);
+               printk(KERN_ERR "DMA %d missed interrupt.\n",stream->dma);
+               au1000_dma_stop(stream);
+               au1000_dma_start(stream);
+               spin_lock(&stream->dma_lock);
+               break;
+       case (~DMA_D0 & ~DMA_D1):
+               printk(KERN_ERR "DMA %d empty irq.\n",stream->dma);
+       }
+       spin_unlock(&stream->dma_lock);
+       snd_pcm_period_elapsed(substream);
+       return IRQ_HANDLED;
+}
+
+/*-------------------------- PCM Audio Streams -------------------------------*/
+
+static unsigned int rates[] = {8000, 11025, 16000, 22050};
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+       .count  =  sizeof(rates) / sizeof(rates[0]),
+       .list   = rates,
+       .mask   = 0,
+};
+
+static snd_pcm_hardware_t snd_au1000 =
+{
+       .info                   = (SNDRV_PCM_INFO_INTERLEAVED | \
+                               SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
+       .rates                  = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050),
+       .rate_min               = 8000,
+       .rate_max               = 22050,
+       .channels_min           = 1,
+       .channels_max           = 2,
+       .buffer_bytes_max       = 128*1024,
+       .period_bytes_min       = 32,
+       .period_bytes_max       = 16*1024,
+       .periods_min            = 8,
+       .periods_max            = 255,
+       .fifo_size              = 16,
+};
+
+static int
+snd_au1000_playback_open(snd_pcm_substream_t * substream)
+{
+       au1000->stream[PLAYBACK]->substream = substream;
+       au1000->stream[PLAYBACK]->buffer = NULL;
+       substream->private_data = au1000->stream[PLAYBACK];
+       substream->runtime->hw = snd_au1000;
+       return (snd_pcm_hw_constraint_list(substream->runtime, 0,
+               SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
+}
+
+static int
+snd_au1000_capture_open(snd_pcm_substream_t * substream)
+{
+       au1000->stream[CAPTURE]->substream = substream;
+       au1000->stream[CAPTURE]->buffer = NULL;
+       substream->private_data = au1000->stream[CAPTURE];
+       substream->runtime->hw = snd_au1000;
+       return (snd_pcm_hw_constraint_list(substream->runtime, 0,
+               SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
+
+}
+
+static int
+snd_au1000_playback_close(snd_pcm_substream_t * substream)
+{
+       au1000->stream[PLAYBACK]->substream = NULL;
+       return 0;
+}
+
+static int
+snd_au1000_capture_close(snd_pcm_substream_t * substream)
+{
+       au1000->stream[CAPTURE]->substream = NULL;
+       return 0;
+}
+
+static int
+snd_au1000_hw_params(snd_pcm_substream_t * substream,
+                                       snd_pcm_hw_params_t * hw_params)
+{
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+static int
+snd_au1000_hw_free(snd_pcm_substream_t * substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int
+snd_au1000_playback_prepare(snd_pcm_substream_t * substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+
+       if (runtime->channels == 1 )
+               au1000_set_ac97_xmit_slots(AC97_SLOT_4);
+       else
+               au1000_set_ac97_xmit_slots(AC97_SLOT_3 | AC97_SLOT_4);
+       snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+       return 0;
+}
+
+static int
+snd_au1000_capture_prepare(snd_pcm_substream_t * substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+
+       if (runtime->channels == 1 )
+               au1000_set_ac97_recv_slots(AC97_SLOT_4);
+       else
+               au1000_set_ac97_recv_slots(AC97_SLOT_3 | AC97_SLOT_4);
+       snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+       return 0;
+}
+
+static int
+snd_au1000_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+       audio_stream_t *stream = substream->private_data;
+       int err = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               au1000_dma_start(stream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               au1000_dma_stop(stream);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+       return err;
+}
+
+static snd_pcm_uframes_t
+snd_au1000_pointer(snd_pcm_substream_t * substream)
+{
+       audio_stream_t *stream = substream->private_data;
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       unsigned long flags;
+       long location;
+
+       spin_lock_irqsave(&stream->dma_lock, flags);
+       location = get_dma_residue(stream->dma);
+       spin_unlock_irqrestore(&stream->dma_lock, flags);
+       location = stream->buffer->relative_end - location;
+       if (location == -1)
+               location = 0;
+       return bytes_to_frames(runtime,location);
+}
+
+static snd_pcm_ops_t snd_card_au1000_playback_ops = {
+       .open                   = snd_au1000_playback_open,
+       .close                  = snd_au1000_playback_close,
+       .ioctl                  = snd_pcm_lib_ioctl,
+       .hw_params              = snd_au1000_hw_params,
+       .hw_free                = snd_au1000_hw_free,
+       .prepare                = snd_au1000_playback_prepare,
+       .trigger                = snd_au1000_trigger,
+       .pointer                = snd_au1000_pointer,
+};
+
+static snd_pcm_ops_t snd_card_au1000_capture_ops = {
+       .open                   = snd_au1000_capture_open,
+       .close                  = snd_au1000_capture_close,
+       .ioctl                  = snd_pcm_lib_ioctl,
+       .hw_params              = snd_au1000_hw_params,
+       .hw_free                = snd_au1000_hw_free,
+       .prepare                = snd_au1000_capture_prepare,
+       .trigger                = snd_au1000_trigger,
+       .pointer                = snd_au1000_pointer,
+};
+
+static int __devinit
+snd_au1000_pcm_new(void)
+{
+       snd_pcm_t *pcm;
+       int err;
+       unsigned long flags;
+
+       if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0)
+               return err;
+
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+               snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024);
+
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+               &snd_card_au1000_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+               &snd_card_au1000_capture_ops);
+
+       pcm->private_data = au1000;
+       pcm->info_flags = 0;
+       strcpy(pcm->name, "Au1000 AC97 PCM");
+
+       flags = claim_dma_lock();
+       if ((au1000->stream[PLAYBACK]->dma = request_au1000_dma(DMA_ID_AC97C_TX,
+                       "AC97 TX", au1000_dma_interrupt, SA_INTERRUPT,
+                       au1000->stream[PLAYBACK])) < 0) {
+               release_dma_lock(flags);
+               return -EBUSY;
+       }
+       if ((au1000->stream[CAPTURE]->dma = request_au1000_dma(DMA_ID_AC97C_RX,
+                       "AC97 RX", au1000_dma_interrupt, SA_INTERRUPT,
+                       au1000->stream[CAPTURE])) < 0){
+               release_dma_lock(flags);
+               return -EBUSY;
+       }
+       /* enable DMA coherency in read/write DMA channels */
+       set_dma_mode(au1000->stream[PLAYBACK]->dma,
+                    get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC);
+       set_dma_mode(au1000->stream[CAPTURE]->dma,
+                    get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC);
+       release_dma_lock(flags);
+       spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock);
+       spin_lock_init(&au1000->stream[CAPTURE]->dma_lock);
+       au1000->pcm = pcm;
+       return 0;
+}
+
+
+/*-------------------------- AC97 CODEC Control ------------------------------*/
+
+static unsigned short
+snd_au1000_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+       u32 volatile cmd;
+       u16 volatile data;
+       int             i;
+       spin_lock(au1000->ac97_lock);
+/* would rather use the interupt than this polling but it works and I can't
+get the interupt driven case to work efficiently */
+       for (i = 0; i < 0x5000; i++)
+               if (!(au1000->ac97_ioport->status & AC97C_CP))
+                       break;
+       if (i == 0x5000)
+               printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
+
+       cmd = (u32) reg & AC97C_INDEX_MASK;
+       cmd |= AC97C_READ;
+       au1000->ac97_ioport->cmd = cmd;
+
+       /* now wait for the data */
+       for (i = 0; i < 0x5000; i++)
+               if (!(au1000->ac97_ioport->status & AC97C_CP))
+                       break;
+       if (i == 0x5000) {
+               printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
+               return 0;
+       }
+
+       data = au1000->ac97_ioport->cmd & 0xffff;
+       spin_unlock(au1000->ac97_lock);
+
+       return data;
+
+}
+
+
+static void
+snd_au1000_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+       u32 cmd;
+       int i;
+       spin_lock(au1000->ac97_lock);
+/* would rather use the interupt than this polling but it works and I can't
+get the interupt driven case to work efficiently */
+       for (i = 0; i < 0x5000; i++)
+               if (!(au1000->ac97_ioport->status & AC97C_CP))
+                       break;
+       if (i == 0x5000)
+               printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n");
+
+       cmd = (u32) reg & AC97C_INDEX_MASK;
+       cmd &= ~AC97C_READ;
+       cmd |= ((u32) val << AC97C_WD_BIT);
+       au1000->ac97_ioport->cmd = cmd;
+       spin_unlock(au1000->ac97_lock);
+}
+static void
+snd_au1000_ac97_free(ac97_t *ac97)
+{
+       au1000->ac97 = NULL;
+}
+
+static int __devinit
+snd_au1000_ac97_new(void)
+{
+       int err;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,8)
+       ac97_bus_t *pbus;
+       ac97_template_t ac97;
+       static ac97_bus_ops_t ops = {
+               .write = snd_au1000_ac97_write,
+               .read = snd_au1000_ac97_read,
+       };
+#else
+       ac97_bus_t bus, *pbus;
+       ac97_t ac97;
+#endif
+
+       if ((au1000->ac97_res_port = request_region(AC97C_CONFIG,
+                       sizeof(au1000_ac97_reg_t), "Au1x00 AC97")) == NULL) {
+               snd_printk(KERN_ERR "ALSA AC97: can't grap AC97 port\n");
+               return -EBUSY;
+       }
+       au1000->ac97_ioport = (au1000_ac97_reg_t *) au1000->ac97_res_port->start;
+
+       spin_lock_init(&au1000->ac97_lock);
+
+       spin_lock(&au1000->ac97_lock);
+
+       /* configure pins for AC'97
+       TODO: move to board_setup.c */
+       au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC);
+
+       /* Initialise Au1000's AC'97 Control Block */
+       au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE;
+       udelay(10);
+       au1000->ac97_ioport->cntrl = AC97C_CE;
+       udelay(10);
+
+       /* Initialise External CODEC -- cold reset */
+       au1000->ac97_ioport->config = AC97C_RESET;
+       udelay(10);
+       au1000->ac97_ioport->config = 0x0;
+       mdelay(5);
+
+       spin_unlock(&au1000->ac97_lock);
+
+       /* Initialise AC97 middle-layer */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,8)
+       if ((err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus)) < 0)
+               return err;
+#else
+       memset(&bus, 0, sizeof(bus));
+       bus.write = snd_au1000_ac97_write;
+       bus.read = snd_au1000_ac97_read;
+       if ((err = snd_ac97_bus(au1000->card, &bus, &pbus)) < 0)
+               return err;
+#endif
+       memset(&ac97, 0, sizeof(ac97));
+       ac97.private_data = au1000;
+       ac97.private_free = snd_au1000_ac97_free;
+       if ((err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97)) < 0)
+               return err;
+       return 0;
+
+}
+
+/*------------------------------ Setup / Destroy ----------------------------*/
+
+void
+snd_au1000_free(snd_card_t *card)
+{
+
+       if (au1000->ac97_res_port) {
+               /* put internal AC97 block into reset */
+               au1000->ac97_ioport->cntrl = AC97C_RS;
+               au1000->ac97_ioport = NULL;
+               release_resource(au1000->ac97_res_port);
+               kfree_nocheck(au1000->ac97_res_port);
+       }
+
+       if (au1000->stream[PLAYBACK]->dma >= 0)
+               free_au1000_dma(au1000->stream[PLAYBACK]->dma);
+
+       if (au1000->stream[CAPTURE]->dma >= 0)
+               free_au1000_dma(au1000->stream[CAPTURE]->dma);
+
+       kfree(au1000->stream[PLAYBACK]);
+       au1000->stream[PLAYBACK] = NULL;
+       kfree(au1000->stream[CAPTURE]);
+       au1000->stream[CAPTURE] = NULL;
+       kfree(au1000);
+       au1000 = NULL;
+
+}
+
+static int __init
+au1000_init(void)
+{
+       int err;
+
+       au1000 = kmalloc(sizeof(au1000_t), GFP_KERNEL);
+       if (au1000 == NULL)
+               return -ENOMEM;
+       au1000->stream[PLAYBACK] = kmalloc(sizeof(audio_stream_t), GFP_KERNEL);
+       if (au1000->stream[PLAYBACK] == NULL)
+               return -ENOMEM;
+       au1000->stream[CAPTURE] = kmalloc(sizeof(audio_stream_t), GFP_KERNEL);
+       if (au1000->stream[CAPTURE] == NULL)
+               return -ENOMEM;
+       /* so that snd_au1000_free will work as intended */
+       au1000->stream[PLAYBACK]->dma = -1;
+       au1000->stream[CAPTURE]->dma = -1;
+       au1000->ac97_res_port = NULL;
+
+       au1000->card = snd_card_new(-1, "AC97", THIS_MODULE, sizeof(au1000_t));
+       if (au1000->card == NULL) {
+               snd_au1000_free(au1000->card);
+               return -ENOMEM;
+       }
+
+       au1000->card->private_data = (au1000_t *)au1000;
+       au1000->card->private_free = snd_au1000_free;
+
+       if ((err = snd_au1000_ac97_new()) < 0 ) {
+               snd_card_free(au1000->card);
+               return err;
+       }
+
+       if ((err = snd_au1000_pcm_new()) < 0) {
+               snd_card_free(au1000->card);
+               return err;
+       }
+
+       strcpy(au1000->card->driver, "AMD-Au1000-AC97");
+       strcpy(au1000->card->shortname, "Au1000-AC97");
+       sprintf(au1000->card->longname, "AMD Au1000--AC97 ALSA Driver");
+
+       if ((err = snd_card_register(au1000->card)) < 0) {
+               snd_card_free(au1000->card);
+               return err;
+       }
+
+       printk( KERN_INFO "ALSA AC97: Driver Initialized\n" );
+       return 0;
+}
+
+static void __exit au1000_exit(void)
+{
+       snd_card_free(au1000->card);
+}
+
+module_init(au1000_init);
+module_exit(au1000_exit);
+
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c
new file mode 100644 (file)
index 0000000..a78e48d
--- /dev/null
@@ -0,0 +1,2119 @@
+/*
+ * au1550_ac97.c  --  Sound driver for Alchemy Au1550 MIPS Internet Edge
+ *                    Processor.
+ *
+ * Copyright 2004 Embedded Edge, LLC
+ *     dan@embeddededge.com
+ *
+ * Mostly copied from the au1000.c driver and some from the
+ * PowerMac dbdma driver.
+ * We assume the processor can do memory coherent DMA.
+ *
+ * Ported to 2.6 by Matt Porter <mporter@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.
+ *
+ *  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.
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/ac97_codec.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_psc.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+/* misc stuff */
+#define POLL_COUNT   0x50000
+#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC)
+
+/* The number of DBDMA ring descriptors to allocate.  No sense making
+ * this too large....if you can't keep up with a few you aren't likely
+ * to be able to with lots of them, either.
+ */
+#define NUM_DBDMA_DESCRIPTORS 4
+
+#define err(format, arg...) printk(KERN_ERR format "\n" , ## arg)
+
+/* Boot options
+ * 0 = no VRA, 1 = use VRA if codec supports it
+ */
+static int      vra = 1;
+MODULE_PARM(vra, "i");
+MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
+
+static struct au1550_state {
+       /* soundcore stuff */
+       int             dev_audio;
+
+       struct ac97_codec *codec;
+       unsigned        codec_base_caps; /* AC'97 reg 00h, "Reset Register" */
+       unsigned        codec_ext_caps;  /* AC'97 reg 28h, "Extended Audio ID" */
+       int             no_vra;         /* do not use VRA */
+
+       spinlock_t      lock;
+       struct semaphore open_sem;
+       struct semaphore sem;
+       mode_t          open_mode;
+       wait_queue_head_t open_wait;
+
+       struct dmabuf {
+               u32             dmanr;
+               unsigned        sample_rate;
+               unsigned        src_factor;
+               unsigned        sample_size;
+               int             num_channels;
+               int             dma_bytes_per_sample;
+               int             user_bytes_per_sample;
+               int             cnt_factor;
+
+               void            *rawbuf;
+               unsigned        buforder;
+               unsigned        numfrag;
+               unsigned        fragshift;
+               void            *nextIn;
+               void            *nextOut;
+               int             count;
+               unsigned        total_bytes;
+               unsigned        error;
+               wait_queue_head_t wait;
+
+               /* redundant, but makes calculations easier */
+               unsigned        fragsize;
+               unsigned        dma_fragsize;
+               unsigned        dmasize;
+               unsigned        dma_qcount;
+
+               /* OSS stuff */
+               unsigned        mapped:1;
+               unsigned        ready:1;
+               unsigned        stopped:1;
+               unsigned        ossfragshift;
+               int             ossmaxfrags;
+               unsigned        subdivision;
+       } dma_dac, dma_adc;
+} au1550_state;
+
+static unsigned
+ld2(unsigned int x)
+{
+       unsigned        r = 0;
+
+       if (x >= 0x10000) {
+               x >>= 16;
+               r += 16;
+       }
+       if (x >= 0x100) {
+               x >>= 8;
+               r += 8;
+       }
+       if (x >= 0x10) {
+               x >>= 4;
+               r += 4;
+       }
+       if (x >= 4) {
+               x >>= 2;
+               r += 2;
+       }
+       if (x >= 2)
+               r++;
+       return r;
+}
+
+static void
+au1550_delay(int msec)
+{
+       unsigned long   tmo;
+       signed long     tmo2;
+
+       if (in_interrupt())
+               return;
+
+       tmo = jiffies + (msec * HZ) / 1000;
+       for (;;) {
+               tmo2 = tmo - jiffies;
+               if (tmo2 <= 0)
+                       break;
+               schedule_timeout(tmo2);
+       }
+}
+
+static u16
+rdcodec(struct ac97_codec *codec, u8 addr)
+{
+       struct au1550_state *s = (struct au1550_state *)codec->private_data;
+       unsigned long   flags;
+       u32             cmd, val;
+       u16             data;
+       int             i;
+
+       spin_lock_irqsave(&s->lock, flags);
+
+       for (i = 0; i < POLL_COUNT; i++) {
+               val = au_readl(PSC_AC97STAT);
+               au_sync();
+               if (!(val & PSC_AC97STAT_CP))
+                       break;
+       }
+       if (i == POLL_COUNT)
+               err("rdcodec: codec cmd pending expired!");
+
+       cmd = (u32)PSC_AC97CDC_INDX(addr);
+       cmd |= PSC_AC97CDC_RD;  /* read command */
+       au_writel(cmd, PSC_AC97CDC);
+       au_sync();
+
+       /* now wait for the data
+       */
+       for (i = 0; i < POLL_COUNT; i++) {
+               val = au_readl(PSC_AC97STAT);
+               au_sync();
+               if (!(val & PSC_AC97STAT_CP))
+                       break;
+       }
+       if (i == POLL_COUNT) {
+               err("rdcodec: read poll expired!");
+               return 0;
+       }
+
+       /* wait for command done?
+       */
+       for (i = 0; i < POLL_COUNT; i++) {
+               val = au_readl(PSC_AC97EVNT);
+               au_sync();
+               if (val & PSC_AC97EVNT_CD)
+                       break;
+       }
+       if (i == POLL_COUNT) {
+               err("rdcodec: read cmdwait expired!");
+               return 0;
+       }
+
+       data = au_readl(PSC_AC97CDC) & 0xffff;
+       au_sync();
+
+       /* Clear command done event.
+       */
+       au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
+       au_sync();
+
+       spin_unlock_irqrestore(&s->lock, flags);
+
+       return data;
+}
+
+
+static void
+wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
+{
+       struct au1550_state *s = (struct au1550_state *)codec->private_data;
+       unsigned long   flags;
+       u32             cmd, val;
+       int             i;
+
+       spin_lock_irqsave(&s->lock, flags);
+
+       for (i = 0; i < POLL_COUNT; i++) {
+               val = au_readl(PSC_AC97STAT);
+               au_sync();
+               if (!(val & PSC_AC97STAT_CP))
+                       break;
+       }
+       if (i == POLL_COUNT)
+               err("wrcodec: codec cmd pending expired!");
+
+       cmd = (u32)PSC_AC97CDC_INDX(addr);
+       cmd |= (u32)data;
+       au_writel(cmd, PSC_AC97CDC);
+       au_sync();
+
+       for (i = 0; i < POLL_COUNT; i++) {
+               val = au_readl(PSC_AC97STAT);
+               au_sync();
+               if (!(val & PSC_AC97STAT_CP))
+                       break;
+       }
+       if (i == POLL_COUNT)
+               err("wrcodec: codec cmd pending expired!");
+
+       for (i = 0; i < POLL_COUNT; i++) {
+               val = au_readl(PSC_AC97EVNT);
+               au_sync();
+               if (val & PSC_AC97EVNT_CD)
+                       break;
+       }
+       if (i == POLL_COUNT)
+               err("wrcodec: read cmdwait expired!");
+
+       /* Clear command done event.
+       */
+       au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
+       au_sync();
+
+       spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void
+waitcodec(struct ac97_codec *codec)
+{
+       u16     temp;
+       u32     val;
+       int     i;
+
+       /* codec_wait is used to wait for a ready state after
+        * an AC97C_RESET.
+        */
+       au1550_delay(10);
+
+       /* first poll the CODEC_READY tag bit
+       */
+       for (i = 0; i < POLL_COUNT; i++) {
+               val = au_readl(PSC_AC97STAT);
+               au_sync();
+               if (val & PSC_AC97STAT_CR)
+                       break;
+       }
+       if (i == POLL_COUNT) {
+               err("waitcodec: CODEC_READY poll expired!");
+               return;
+       }
+
+       /* get AC'97 powerdown control/status register
+       */
+       temp = rdcodec(codec, AC97_POWER_CONTROL);
+
+       /* If anything is powered down, power'em up
+       */
+       if (temp & 0x7f00) {
+               /* Power on
+               */
+               wrcodec(codec, AC97_POWER_CONTROL, 0);
+               au1550_delay(100);
+
+               /* Reread
+               */
+               temp = rdcodec(codec, AC97_POWER_CONTROL);
+       }
+
+       /* Check if Codec REF,ANL,DAC,ADC ready
+       */
+       if ((temp & 0x7f0f) != 0x000f)
+               err("codec reg 26 status (0x%x) not ready!!", temp);
+}
+
+/* stop the ADC before calling */
+static void
+set_adc_rate(struct au1550_state *s, unsigned rate)
+{
+       struct dmabuf  *adc = &s->dma_adc;
+       struct dmabuf  *dac = &s->dma_dac;
+       unsigned        adc_rate, dac_rate;
+       u16             ac97_extstat;
+
+       if (s->no_vra) {
+               /* calc SRC factor
+               */
+               adc->src_factor = ((96000 / rate) + 1) >> 1;
+               adc->sample_rate = 48000 / adc->src_factor;
+               return;
+       }
+
+       adc->src_factor = 1;
+
+       ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
+
+       rate = rate > 48000 ? 48000 : rate;
+
+       /* enable VRA
+       */
+       wrcodec(s->codec, AC97_EXTENDED_STATUS,
+               ac97_extstat | AC97_EXTSTAT_VRA);
+
+       /* now write the sample rate
+       */
+       wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate);
+
+       /* read it back for actual supported rate
+       */
+       adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
+
+       pr_debug("set_adc_rate: set to %d Hz\n", adc_rate);
+
+       /* some codec's don't allow unequal DAC and ADC rates, in which case
+        * writing one rate reg actually changes both.
+        */
+       dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
+       if (dac->num_channels > 2)
+               wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate);
+       if (dac->num_channels > 4)
+               wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate);
+
+       adc->sample_rate = adc_rate;
+       dac->sample_rate = dac_rate;
+}
+
+/* stop the DAC before calling */
+static void
+set_dac_rate(struct au1550_state *s, unsigned rate)
+{
+       struct dmabuf  *dac = &s->dma_dac;
+       struct dmabuf  *adc = &s->dma_adc;
+       unsigned        adc_rate, dac_rate;
+       u16             ac97_extstat;
+
+       if (s->no_vra) {
+               /* calc SRC factor
+               */
+               dac->src_factor = ((96000 / rate) + 1) >> 1;
+               dac->sample_rate = 48000 / dac->src_factor;
+               return;
+       }
+
+       dac->src_factor = 1;
+
+       ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
+
+       rate = rate > 48000 ? 48000 : rate;
+
+       /* enable VRA
+       */
+       wrcodec(s->codec, AC97_EXTENDED_STATUS,
+               ac97_extstat | AC97_EXTSTAT_VRA);
+
+       /* now write the sample rate
+       */
+       wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate);
+
+       /* I don't support different sample rates for multichannel,
+        * so make these channels the same.
+        */
+       if (dac->num_channels > 2)
+               wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate);
+       if (dac->num_channels > 4)
+               wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate);
+       /* read it back for actual supported rate
+       */
+       dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
+
+       pr_debug("set_dac_rate: set to %d Hz\n", dac_rate);
+
+       /* some codec's don't allow unequal DAC and ADC rates, in which case
+        * writing one rate reg actually changes both.
+        */
+       adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
+
+       dac->sample_rate = dac_rate;
+       adc->sample_rate = adc_rate;
+}
+
+static void
+stop_dac(struct au1550_state *s)
+{
+       struct dmabuf  *db = &s->dma_dac;
+       u32             stat;
+       unsigned long   flags;
+
+       if (db->stopped)
+               return;
+
+       spin_lock_irqsave(&s->lock, flags);
+
+       au_writel(PSC_AC97PCR_TP, PSC_AC97PCR);
+       au_sync();
+
+       /* Wait for Transmit Busy to show disabled.
+       */
+       do {
+               stat = readl((void *)PSC_AC97STAT);
+               au_sync();
+       } while ((stat & PSC_AC97STAT_TB) != 0);
+
+       au1xxx_dbdma_reset(db->dmanr);
+
+       db->stopped = 1;
+
+       spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void
+stop_adc(struct au1550_state *s)
+{
+       struct dmabuf  *db = &s->dma_adc;
+       unsigned long   flags;
+       u32             stat;
+
+       if (db->stopped)
+               return;
+
+       spin_lock_irqsave(&s->lock, flags);
+
+       au_writel(PSC_AC97PCR_RP, PSC_AC97PCR);
+       au_sync();
+
+       /* Wait for Receive Busy to show disabled.
+       */
+       do {
+               stat = readl((void *)PSC_AC97STAT);
+               au_sync();
+       } while ((stat & PSC_AC97STAT_RB) != 0);
+
+       au1xxx_dbdma_reset(db->dmanr);
+
+       db->stopped = 1;
+
+       spin_unlock_irqrestore(&s->lock, flags);
+}
+
+
+static void
+set_xmit_slots(int num_channels)
+{
+       u32     ac97_config, stat;
+
+       ac97_config = au_readl(PSC_AC97CFG);
+       au_sync();
+       ac97_config &= ~(PSC_AC97CFG_TXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
+       au_writel(ac97_config, PSC_AC97CFG);
+       au_sync();
+
+       switch (num_channels) {
+       case 6:         /* stereo with surround and center/LFE,
+                        * slots 3,4,6,7,8,9
+                        */
+               ac97_config |= PSC_AC97CFG_TXSLOT_ENA(6);
+               ac97_config |= PSC_AC97CFG_TXSLOT_ENA(9);
+
+       case 4:         /* stereo with surround, slots 3,4,7,8 */
+               ac97_config |= PSC_AC97CFG_TXSLOT_ENA(7);
+               ac97_config |= PSC_AC97CFG_TXSLOT_ENA(8);
+
+       case 2:         /* stereo, slots 3,4 */
+       case 1:         /* mono */
+               ac97_config |= PSC_AC97CFG_TXSLOT_ENA(3);
+               ac97_config |= PSC_AC97CFG_TXSLOT_ENA(4);
+       }
+
+       au_writel(ac97_config, PSC_AC97CFG);
+       au_sync();
+
+       ac97_config |= PSC_AC97CFG_DE_ENABLE;
+       au_writel(ac97_config, PSC_AC97CFG);
+       au_sync();
+
+       /* Wait for Device ready.
+       */
+       do {
+               stat = readl((void *)PSC_AC97STAT);
+               au_sync();
+       } while ((stat & PSC_AC97STAT_DR) == 0);
+}
+
+static void
+set_recv_slots(int num_channels)
+{
+       u32     ac97_config, stat;
+
+       ac97_config = au_readl(PSC_AC97CFG);
+       au_sync();
+       ac97_config &= ~(PSC_AC97CFG_RXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
+       au_writel(ac97_config, PSC_AC97CFG);
+       au_sync();
+
+       /* Always enable slots 3 and 4 (stereo). Slot 6 is
+        * optional Mic ADC, which we don't support yet.
+        */
+       ac97_config |= PSC_AC97CFG_RXSLOT_ENA(3);
+       ac97_config |= PSC_AC97CFG_RXSLOT_ENA(4);
+
+       au_writel(ac97_config, PSC_AC97CFG);
+       au_sync();
+
+       ac97_config |= PSC_AC97CFG_DE_ENABLE;
+       au_writel(ac97_config, PSC_AC97CFG);
+       au_sync();
+
+       /* Wait for Device ready.
+       */
+       do {
+               stat = readl((void *)PSC_AC97STAT);
+               au_sync();
+       } while ((stat & PSC_AC97STAT_DR) == 0);
+}
+
+static void
+start_dac(struct au1550_state *s)
+{
+       struct dmabuf  *db = &s->dma_dac;
+       unsigned long   flags;
+
+       if (!db->stopped)
+               return;
+
+       spin_lock_irqsave(&s->lock, flags);
+
+       set_xmit_slots(db->num_channels);
+       au_writel(PSC_AC97PCR_TC, PSC_AC97PCR);
+       au_sync();
+       au_writel(PSC_AC97PCR_TS, PSC_AC97PCR);
+       au_sync();
+
+       au1xxx_dbdma_start(db->dmanr);
+
+       db->stopped = 0;
+
+       spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void
+start_adc(struct au1550_state *s)
+{
+       struct dmabuf  *db = &s->dma_adc;
+       int     i;
+
+       if (!db->stopped)
+               return;
+
+       /* Put two buffers on the ring to get things started.
+       */
+       for (i=0; i<2; i++) {
+               au1xxx_dbdma_put_dest(db->dmanr, db->nextIn, db->dma_fragsize);
+
+               db->nextIn += db->dma_fragsize;
+               if (db->nextIn >= db->rawbuf + db->dmasize)
+                       db->nextIn -= db->dmasize;
+       }
+
+       set_recv_slots(db->num_channels);
+       au1xxx_dbdma_start(db->dmanr);
+       au_writel(PSC_AC97PCR_RC, PSC_AC97PCR);
+       au_sync();
+       au_writel(PSC_AC97PCR_RS, PSC_AC97PCR);
+       au_sync();
+
+       db->stopped = 0;
+}
+
+static int
+prog_dmabuf(struct au1550_state *s, struct dmabuf *db)
+{
+       unsigned user_bytes_per_sec;
+       unsigned        bufs;
+       unsigned        rate = db->sample_rate;
+
+       if (!db->rawbuf) {
+               db->ready = db->mapped = 0;
+               db->buforder = 5;       /* 32 * PAGE_SIZE */
+               db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL);
+               if (!db->rawbuf)
+                       return -ENOMEM;
+       }
+
+       db->cnt_factor = 1;
+       if (db->sample_size == 8)
+               db->cnt_factor *= 2;
+       if (db->num_channels == 1)
+               db->cnt_factor *= 2;
+       db->cnt_factor *= db->src_factor;
+
+       db->count = 0;
+       db->dma_qcount = 0;
+       db->nextIn = db->nextOut = db->rawbuf;
+
+       db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels;
+       db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ?
+                                       2 : db->num_channels);
+
+       user_bytes_per_sec = rate * db->user_bytes_per_sample;
+       bufs = PAGE_SIZE << db->buforder;
+       if (db->ossfragshift) {
+               if ((1000 << db->ossfragshift) < user_bytes_per_sec)
+                       db->fragshift = ld2(user_bytes_per_sec/1000);
+               else
+                       db->fragshift = db->ossfragshift;
+       } else {
+               db->fragshift = ld2(user_bytes_per_sec / 100 /
+                                   (db->subdivision ? db->subdivision : 1));
+               if (db->fragshift < 3)
+                       db->fragshift = 3;
+       }
+
+       db->fragsize = 1 << db->fragshift;
+       db->dma_fragsize = db->fragsize * db->cnt_factor;
+       db->numfrag = bufs / db->dma_fragsize;
+
+       while (db->numfrag < 4 && db->fragshift > 3) {
+               db->fragshift--;
+               db->fragsize = 1 << db->fragshift;
+               db->dma_fragsize = db->fragsize * db->cnt_factor;
+               db->numfrag = bufs / db->dma_fragsize;
+       }
+
+       if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+               db->numfrag = db->ossmaxfrags;
+
+       db->dmasize = db->dma_fragsize * db->numfrag;
+       memset(db->rawbuf, 0, bufs);
+
+       pr_debug("prog_dmabuf: rate=%d, samplesize=%d, channels=%d\n",
+           rate, db->sample_size, db->num_channels);
+       pr_debug("prog_dmabuf: fragsize=%d, cnt_factor=%d, dma_fragsize=%d\n",
+           db->fragsize, db->cnt_factor, db->dma_fragsize);
+       pr_debug("prog_dmabuf: numfrag=%d, dmasize=%d\n", db->numfrag, db->dmasize);
+
+       db->ready = 1;
+       return 0;
+}
+
+static int
+prog_dmabuf_adc(struct au1550_state *s)
+{
+       stop_adc(s);
+       return prog_dmabuf(s, &s->dma_adc);
+
+}
+
+static int
+prog_dmabuf_dac(struct au1550_state *s)
+{
+       stop_dac(s);
+       return prog_dmabuf(s, &s->dma_dac);
+}
+
+
+/* hold spinlock for the following */
+static void
+dac_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct au1550_state *s = (struct au1550_state *) dev_id;
+       struct dmabuf  *db = &s->dma_dac;
+       u32     ac97c_stat;
+
+       ac97c_stat = au_readl(PSC_AC97STAT);
+       if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
+               pr_debug("AC97C status = 0x%08x\n", ac97c_stat);
+       db->dma_qcount--;
+
+       if (db->count >= db->fragsize) {
+               if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,
+                                                       db->fragsize) == 0) {
+                       err("qcount < 2 and no ring room!");
+               }
+               db->nextOut += db->fragsize;
+               if (db->nextOut >= db->rawbuf + db->dmasize)
+                       db->nextOut -= db->dmasize;
+               db->count -= db->fragsize;
+               db->total_bytes += db->dma_fragsize;
+               db->dma_qcount++;
+       }
+
+       /* wake up anybody listening */
+       if (waitqueue_active(&db->wait))
+               wake_up(&db->wait);
+}
+
+
+static void
+adc_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct  au1550_state *s = (struct au1550_state *)dev_id;
+       struct  dmabuf  *dp = &s->dma_adc;
+       u32     obytes;
+       char    *obuf;
+
+       /* Pull the buffer from the dma queue.
+       */
+       au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes);
+
+       if ((dp->count + obytes) > dp->dmasize) {
+               /* Overrun. Stop ADC and log the error
+               */
+               stop_adc(s);
+               dp->error++;
+               err("adc overrun");
+               return;
+       }
+
+       /* Put a new empty buffer on the destination DMA.
+       */
+       au1xxx_dbdma_put_dest(dp->dmanr, dp->nextIn, dp->dma_fragsize);
+
+       dp->nextIn += dp->dma_fragsize;
+       if (dp->nextIn >= dp->rawbuf + dp->dmasize)
+               dp->nextIn -= dp->dmasize;
+
+       dp->count += obytes;
+       dp->total_bytes += obytes;
+
+       /* wake up anybody listening
+       */
+       if (waitqueue_active(&dp->wait))
+               wake_up(&dp->wait);
+
+}
+
+static loff_t
+au1550_llseek(struct file *file, loff_t offset, int origin)
+{
+       return -ESPIPE;
+}
+
+
+static int
+au1550_open_mixdev(struct inode *inode, struct file *file)
+{
+       file->private_data = &au1550_state;
+       return 0;
+}
+
+static int
+au1550_release_mixdev(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static int
+mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
+                        unsigned long arg)
+{
+       return codec->mixer_ioctl(codec, cmd, arg);
+}
+
+static int
+au1550_ioctl_mixdev(struct inode *inode, struct file *file,
+                              unsigned int cmd, unsigned long arg)
+{
+       struct au1550_state *s = (struct au1550_state *)file->private_data;
+       struct ac97_codec *codec = s->codec;
+
+       return mixdev_ioctl(codec, cmd, arg);
+}
+
+static /*const */ struct file_operations au1550_mixer_fops = {
+       owner:THIS_MODULE,
+       llseek:au1550_llseek,
+       ioctl:au1550_ioctl_mixdev,
+       open:au1550_open_mixdev,
+       release:au1550_release_mixdev,
+};
+
+static int
+drain_dac(struct au1550_state *s, int nonblock)
+{
+       unsigned long   flags;
+       int             count, tmo;
+
+       if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped)
+               return 0;
+
+       for (;;) {
+               spin_lock_irqsave(&s->lock, flags);
+               count = s->dma_dac.count;
+               spin_unlock_irqrestore(&s->lock, flags);
+               if (count <= s->dma_dac.fragsize)
+                       break;
+               if (signal_pending(current))
+                       break;
+               if (nonblock)
+                       return -EBUSY;
+               tmo = 1000 * count / (s->no_vra ?
+                                     48000 : s->dma_dac.sample_rate);
+               tmo /= s->dma_dac.dma_bytes_per_sample;
+               au1550_delay(tmo);
+       }
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+       return 0;
+}
+
+static inline u8 S16_TO_U8(s16 ch)
+{
+       return (u8) (ch >> 8) + 0x80;
+}
+static inline s16 U8_TO_S16(u8 ch)
+{
+       return (s16) (ch - 0x80) << 8;
+}
+
+/*
+ * Translates user samples to dma buffer suitable for AC'97 DAC data:
+ *     If mono, copy left channel to right channel in dma buffer.
+ *     If 8 bit samples, cvt to 16-bit before writing to dma buffer.
+ *     If interpolating (no VRA), duplicate every audio frame src_factor times.
+ */
+static int
+translate_from_user(struct dmabuf *db, char* dmabuf, char* userbuf,
+                                                              int dmacount)
+{
+       int             sample, i;
+       int             interp_bytes_per_sample;
+       int             num_samples;
+       int             mono = (db->num_channels == 1);
+       char            usersample[12];
+       s16             ch, dmasample[6];
+
+       if (db->sample_size == 16 && !mono && db->src_factor == 1) {
+               /* no translation necessary, just copy
+               */
+               if (copy_from_user(dmabuf, userbuf, dmacount))
+                       return -EFAULT;
+               return dmacount;
+       }
+
+       interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
+       num_samples = dmacount / interp_bytes_per_sample;
+
+       for (sample = 0; sample < num_samples; sample++) {
+               if (copy_from_user(usersample, userbuf,
+                                  db->user_bytes_per_sample)) {
+                       return -EFAULT;
+               }
+
+               for (i = 0; i < db->num_channels; i++) {
+                       if (db->sample_size == 8)
+                               ch = U8_TO_S16(usersample[i]);
+                       else
+                               ch = *((s16 *) (&usersample[i * 2]));
+                       dmasample[i] = ch;
+                       if (mono)
+                               dmasample[i + 1] = ch;  /* right channel */
+               }
+
+               /* duplicate every audio frame src_factor times
+               */
+               for (i = 0; i < db->src_factor; i++)
+                       memcpy(dmabuf, dmasample, db->dma_bytes_per_sample);
+
+               userbuf += db->user_bytes_per_sample;
+               dmabuf += interp_bytes_per_sample;
+       }
+
+       return num_samples * interp_bytes_per_sample;
+}
+
+/*
+ * Translates AC'97 ADC samples to user buffer:
+ *     If mono, send only left channel to user buffer.
+ *     If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer.
+ *     If decimating (no VRA), skip over src_factor audio frames.
+ */
+static int
+translate_to_user(struct dmabuf *db, char* userbuf, char* dmabuf,
+                                                            int dmacount)
+{
+       int             sample, i;
+       int             interp_bytes_per_sample;
+       int             num_samples;
+       int             mono = (db->num_channels == 1);
+       char            usersample[12];
+
+       if (db->sample_size == 16 && !mono && db->src_factor == 1) {
+               /* no translation necessary, just copy
+               */
+               if (copy_to_user(userbuf, dmabuf, dmacount))
+                       return -EFAULT;
+               return dmacount;
+       }
+
+       interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
+       num_samples = dmacount / interp_bytes_per_sample;
+
+       for (sample = 0; sample < num_samples; sample++) {
+               for (i = 0; i < db->num_channels; i++) {
+                       if (db->sample_size == 8)
+                               usersample[i] =
+                                       S16_TO_U8(*((s16 *) (&dmabuf[i * 2])));
+                       else
+                               *((s16 *) (&usersample[i * 2])) =
+                                       *((s16 *) (&dmabuf[i * 2]));
+               }
+
+               if (copy_to_user(userbuf, usersample,
+                                db->user_bytes_per_sample)) {
+                       return -EFAULT;
+               }
+
+               userbuf += db->user_bytes_per_sample;
+               dmabuf += interp_bytes_per_sample;
+       }
+
+       return num_samples * interp_bytes_per_sample;
+}
+
+/*
+ * Copy audio data to/from user buffer from/to dma buffer, taking care
+ * that we wrap when reading/writing the dma buffer. Returns actual byte
+ * count written to or read from the dma buffer.
+ */
+static int
+copy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user)
+{
+       char           *bufptr = to_user ? db->nextOut : db->nextIn;
+       char           *bufend = db->rawbuf + db->dmasize;
+       int             cnt, ret;
+
+       if (bufptr + count > bufend) {
+               int             partial = (int) (bufend - bufptr);
+               if (to_user) {
+                       if ((cnt = translate_to_user(db, userbuf,
+                                                    bufptr, partial)) < 0)
+                               return cnt;
+                       ret = cnt;
+                       if ((cnt = translate_to_user(db, userbuf + partial,
+                                                    db->rawbuf,
+                                                    count - partial)) < 0)
+                               return cnt;
+                       ret += cnt;
+               } else {
+                       if ((cnt = translate_from_user(db, bufptr, userbuf,
+                                                      partial)) < 0)
+                               return cnt;
+                       ret = cnt;
+                       if ((cnt = translate_from_user(db, db->rawbuf,
+                                                      userbuf + partial,
+                                                      count - partial)) < 0)
+                               return cnt;
+                       ret += cnt;
+               }
+       } else {
+               if (to_user)
+                       ret = translate_to_user(db, userbuf, bufptr, count);
+               else
+                       ret = translate_from_user(db, bufptr, userbuf, count);
+       }
+
+       return ret;
+}
+
+
+static ssize_t
+au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+       struct au1550_state *s = (struct au1550_state *)file->private_data;
+       struct dmabuf  *db = &s->dma_adc;
+       DECLARE_WAITQUEUE(wait, current);
+       ssize_t         ret;
+       unsigned long   flags;
+       int             cnt, usercnt, avail;
+
+       if (db->mapped)
+               return -ENXIO;
+       if (!access_ok(VERIFY_WRITE, buffer, count))
+               return -EFAULT;
+       ret = 0;
+
+       count *= db->cnt_factor;
+
+       down(&s->sem);
+       add_wait_queue(&db->wait, &wait);
+
+       while (count > 0) {
+               /* wait for samples in ADC dma buffer
+               */
+               do {
+                       if (db->stopped)
+                               start_adc(s);
+                       spin_lock_irqsave(&s->lock, flags);
+                       avail = db->count;
+                       if (avail <= 0)
+                               __set_current_state(TASK_INTERRUPTIBLE);
+                       spin_unlock_irqrestore(&s->lock, flags);
+                       if (avail <= 0) {
+                               if (file->f_flags & O_NONBLOCK) {
+                                       if (!ret)
+                                               ret = -EAGAIN;
+                                       goto out;
+                               }
+                               up(&s->sem);
+                               schedule();
+                               if (signal_pending(current)) {
+                                       if (!ret)
+                                               ret = -ERESTARTSYS;
+                                       goto out2;
+                               }
+                               down(&s->sem);
+                       }
+               } while (avail <= 0);
+
+               /* copy from nextOut to user
+               */
+               if ((cnt = copy_dmabuf_user(db, buffer,
+                                           count > avail ?
+                                           avail : count, 1)) < 0) {
+                       if (!ret)
+                               ret = -EFAULT;
+                       goto out;
+               }
+
+               spin_lock_irqsave(&s->lock, flags);
+               db->count -= cnt;
+               db->nextOut += cnt;
+               if (db->nextOut >= db->rawbuf + db->dmasize)
+                       db->nextOut -= db->dmasize;
+               spin_unlock_irqrestore(&s->lock, flags);
+
+               count -= cnt;
+               usercnt = cnt / db->cnt_factor;
+               buffer += usercnt;
+               ret += usercnt;
+       }                       /* while (count > 0) */
+
+out:
+       up(&s->sem);
+out2:
+       remove_wait_queue(&db->wait, &wait);
+       set_current_state(TASK_RUNNING);
+       return ret;
+}
+
+static ssize_t
+au1550_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
+{
+       struct au1550_state *s = (struct au1550_state *)file->private_data;
+       struct dmabuf  *db = &s->dma_dac;
+       DECLARE_WAITQUEUE(wait, current);
+       ssize_t         ret = 0;
+       unsigned long   flags;
+       int             cnt, usercnt, avail;
+
+       pr_debug("write: count=%d\n", count);
+
+       if (db->mapped)
+               return -ENXIO;
+       if (!access_ok(VERIFY_READ, buffer, count))
+               return -EFAULT;
+
+       count *= db->cnt_factor;
+
+       down(&s->sem);
+       add_wait_queue(&db->wait, &wait);
+
+       while (count > 0) {
+               /* wait for space in playback buffer
+               */
+               do {
+                       spin_lock_irqsave(&s->lock, flags);
+                       avail = (int) db->dmasize - db->count;
+                       if (avail <= 0)
+                               __set_current_state(TASK_INTERRUPTIBLE);
+                       spin_unlock_irqrestore(&s->lock, flags);
+                       if (avail <= 0) {
+                               if (file->f_flags & O_NONBLOCK) {
+                                       if (!ret)
+                                               ret = -EAGAIN;
+                                       goto out;
+                               }
+                               up(&s->sem);
+                               schedule();
+                               if (signal_pending(current)) {
+                                       if (!ret)
+                                               ret = -ERESTARTSYS;
+                                       goto out2;
+                               }
+                               down(&s->sem);
+                       }
+               } while (avail <= 0);
+
+               /* copy from user to nextIn
+               */
+               if ((cnt = copy_dmabuf_user(db, (char *) buffer,
+                                           count > avail ?
+                                           avail : count, 0)) < 0) {
+                       if (!ret)
+                               ret = -EFAULT;
+                       goto out;
+               }
+
+               spin_lock_irqsave(&s->lock, flags);
+               db->count += cnt;
+               db->nextIn += cnt;
+               if (db->nextIn >= db->rawbuf + db->dmasize)
+                       db->nextIn -= db->dmasize;
+
+               /* If the data is available, we want to keep two buffers
+                * on the dma queue.  If the queue count reaches zero,
+                * we know the dma has stopped.
+                */
+               while ((db->dma_qcount < 2) && (db->count >= db->fragsize)) {
+                       if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,
+                                                       db->fragsize) == 0) {
+                               err("qcount < 2 and no ring room!");
+                       }
+                       db->nextOut += db->fragsize;
+                       if (db->nextOut >= db->rawbuf + db->dmasize)
+                               db->nextOut -= db->dmasize;
+                       db->total_bytes += db->dma_fragsize;
+                       if (db->dma_qcount == 0)
+                               start_dac(s);
+                       db->dma_qcount++;
+               }
+               spin_unlock_irqrestore(&s->lock, flags);
+
+               count -= cnt;
+               usercnt = cnt / db->cnt_factor;
+               buffer += usercnt;
+               ret += usercnt;
+       }                       /* while (count > 0) */
+
+out:
+       up(&s->sem);
+out2:
+       remove_wait_queue(&db->wait, &wait);
+       set_current_state(TASK_RUNNING);
+       return ret;
+}
+
+
+/* No kernel lock - we have our own spinlock */
+static unsigned int
+au1550_poll(struct file *file, struct poll_table_struct *wait)
+{
+       struct au1550_state *s = (struct au1550_state *)file->private_data;
+       unsigned long   flags;
+       unsigned int    mask = 0;
+
+       if (file->f_mode & FMODE_WRITE) {
+               if (!s->dma_dac.ready)
+                       return 0;
+               poll_wait(file, &s->dma_dac.wait, wait);
+       }
+       if (file->f_mode & FMODE_READ) {
+               if (!s->dma_adc.ready)
+                       return 0;
+               poll_wait(file, &s->dma_adc.wait, wait);
+       }
+
+       spin_lock_irqsave(&s->lock, flags);
+
+       if (file->f_mode & FMODE_READ) {
+               if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize)
+                       mask |= POLLIN | POLLRDNORM;
+       }
+       if (file->f_mode & FMODE_WRITE) {
+               if (s->dma_dac.mapped) {
+                       if (s->dma_dac.count >=
+                           (signed)s->dma_dac.dma_fragsize)
+                               mask |= POLLOUT | POLLWRNORM;
+               } else {
+                       if ((signed) s->dma_dac.dmasize >=
+                           s->dma_dac.count + (signed)s->dma_dac.dma_fragsize)
+                               mask |= POLLOUT | POLLWRNORM;
+               }
+       }
+       spin_unlock_irqrestore(&s->lock, flags);
+       return mask;
+}
+
+static int
+au1550_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct au1550_state *s = (struct au1550_state *)file->private_data;
+       struct dmabuf  *db;
+       unsigned long   size;
+       int ret = 0;
+
+       lock_kernel();
+       down(&s->sem);
+       if (vma->vm_flags & VM_WRITE)
+               db = &s->dma_dac;
+       else if (vma->vm_flags & VM_READ)
+               db = &s->dma_adc;
+       else {
+               ret = -EINVAL;
+               goto out;
+       }
+       if (vma->vm_pgoff != 0) {
+               ret = -EINVAL;
+               goto out;
+       }
+       size = vma->vm_end - vma->vm_start;
+       if (size > (PAGE_SIZE << db->buforder)) {
+               ret = -EINVAL;
+               goto out;
+       }
+       if (remap_pfn_range(vma, vma->vm_start, page_to_pfn(virt_to_page(db->rawbuf)),
+                            size, vma->vm_page_prot)) {
+               ret = -EAGAIN;
+               goto out;
+       }
+       vma->vm_flags &= ~VM_IO;
+       db->mapped = 1;
+out:
+       up(&s->sem);
+       unlock_kernel();
+       return ret;
+}
+
+#ifdef DEBUG
+static struct ioctl_str_t {
+       unsigned int    cmd;
+       const char     *str;
+} ioctl_str[] = {
+       {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
+       {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
+       {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
+       {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
+       {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
+       {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
+       {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
+       {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
+       {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
+       {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
+       {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"},
+       {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"},
+       {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
+       {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
+       {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
+       {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
+       {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
+       {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
+       {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
+       {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
+       {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"},
+       {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"},
+       {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
+       {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
+       {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
+       {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
+       {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
+       {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
+       {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
+       {OSS_GETVERSION, "OSS_GETVERSION"},
+       {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
+       {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
+       {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
+       {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"}
+};
+#endif
+
+static int
+dma_count_done(struct dmabuf *db)
+{
+       if (db->stopped)
+               return 0;
+
+       return db->dma_fragsize - au1xxx_get_dma_residue(db->dmanr);
+}
+
+
+static int
+au1550_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+                                                       unsigned long arg)
+{
+       struct au1550_state *s = (struct au1550_state *)file->private_data;
+       unsigned long   flags;
+       audio_buf_info  abinfo;
+       count_info      cinfo;
+       int             count;
+       int             val, mapped, ret, diff;
+
+       mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+               ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+
+#ifdef DEBUG
+       for (count=0; count<sizeof(ioctl_str)/sizeof(ioctl_str[0]); count++) {
+               if (ioctl_str[count].cmd == cmd)
+                       break;
+       }
+       if (count < sizeof(ioctl_str) / sizeof(ioctl_str[0]))
+               pr_debug("ioctl %s, arg=0x%lxn", ioctl_str[count].str, arg);
+       else
+               pr_debug("ioctl 0x%x unknown, arg=0x%lx\n", cmd, arg);
+#endif
+
+       switch (cmd) {
+       case OSS_GETVERSION:
+               return put_user(SOUND_VERSION, (int *) arg);
+
+       case SNDCTL_DSP_SYNC:
+               if (file->f_mode & FMODE_WRITE)
+                       return drain_dac(s, file->f_flags & O_NONBLOCK);
+               return 0;
+
+       case SNDCTL_DSP_SETDUPLEX:
+               return 0;
+
+       case SNDCTL_DSP_GETCAPS:
+               return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
+                               DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+
+       case SNDCTL_DSP_RESET:
+               if (file->f_mode & FMODE_WRITE) {
+                       stop_dac(s);
+                       synchronize_irq();
+                       s->dma_dac.count = s->dma_dac.total_bytes = 0;
+                       s->dma_dac.nextIn = s->dma_dac.nextOut =
+                               s->dma_dac.rawbuf;
+               }
+               if (file->f_mode & FMODE_READ) {
+                       stop_adc(s);
+                       synchronize_irq();
+                       s->dma_adc.count = s->dma_adc.total_bytes = 0;
+                       s->dma_adc.nextIn = s->dma_adc.nextOut =
+                               s->dma_adc.rawbuf;
+               }
+               return 0;
+
+       case SNDCTL_DSP_SPEED:
+               if (get_user(val, (int *) arg))
+                       return -EFAULT;
+               if (val >= 0) {
+                       if (file->f_mode & FMODE_READ) {
+                               stop_adc(s);
+                               set_adc_rate(s, val);
+                       }
+                       if (file->f_mode & FMODE_WRITE) {
+                               stop_dac(s);
+                               set_dac_rate(s, val);
+                       }
+                       if (s->open_mode & FMODE_READ)
+                               if ((ret = prog_dmabuf_adc(s)))
+                                       return ret;
+                       if (s->open_mode & FMODE_WRITE)
+                               if ((ret = prog_dmabuf_dac(s)))
+                                       return ret;
+               }
+               return put_user((file->f_mode & FMODE_READ) ?
+                               s->dma_adc.sample_rate :
+                               s->dma_dac.sample_rate,
+                               (int *)arg);
+
+       case SNDCTL_DSP_STEREO:
+               if (get_user(val, (int *) arg))
+                       return -EFAULT;
+               if (file->f_mode & FMODE_READ) {
+                       stop_adc(s);
+                       s->dma_adc.num_channels = val ? 2 : 1;
+                       if ((ret = prog_dmabuf_adc(s)))
+                               return ret;
+               }
+               if (file->f_mode & FMODE_WRITE) {
+                       stop_dac(s);
+                       s->dma_dac.num_channels = val ? 2 : 1;
+                       if (s->codec_ext_caps & AC97_EXT_DACS) {
+                               /* disable surround and center/lfe in AC'97
+                               */
+                               u16 ext_stat = rdcodec(s->codec,
+                                                      AC97_EXTENDED_STATUS);
+                               wrcodec(s->codec, AC97_EXTENDED_STATUS,
+                                       ext_stat | (AC97_EXTSTAT_PRI |
+                                                   AC97_EXTSTAT_PRJ |
+                                                   AC97_EXTSTAT_PRK));
+                       }
+                       if ((ret = prog_dmabuf_dac(s)))
+                               return ret;
+               }
+               return 0;
+
+       case SNDCTL_DSP_CHANNELS:
+               if (get_user(val, (int *) arg))
+                       return -EFAULT;
+               if (val != 0) {
+                       if (file->f_mode & FMODE_READ) {
+                               if (val < 0 || val > 2)
+                                       return -EINVAL;
+                               stop_adc(s);
+                               s->dma_adc.num_channels = val;
+                               if ((ret = prog_dmabuf_adc(s)))
+                                       return ret;
+                       }
+                       if (file->f_mode & FMODE_WRITE) {
+                               switch (val) {
+                               case 1:
+                               case 2:
+                                       break;
+                               case 3:
+                               case 5:
+                                       return -EINVAL;
+                               case 4:
+                                       if (!(s->codec_ext_caps &
+                                             AC97_EXTID_SDAC))
+                                               return -EINVAL;
+                                       break;
+                               case 6:
+                                       if ((s->codec_ext_caps &
+                                            AC97_EXT_DACS) != AC97_EXT_DACS)
+                                               return -EINVAL;
+                                       break;
+                               default:
+                                       return -EINVAL;
+                               }
+
+                               stop_dac(s);
+                               if (val <= 2 &&
+                                   (s->codec_ext_caps & AC97_EXT_DACS)) {
+                                       /* disable surround and center/lfe
+                                        * channels in AC'97
+                                        */
+                                       u16             ext_stat =
+                                               rdcodec(s->codec,
+                                                       AC97_EXTENDED_STATUS);
+                                       wrcodec(s->codec,
+                                               AC97_EXTENDED_STATUS,
+                                               ext_stat | (AC97_EXTSTAT_PRI |
+                                                           AC97_EXTSTAT_PRJ |
+                                                           AC97_EXTSTAT_PRK));
+                               } else if (val >= 4) {
+                                       /* enable surround, center/lfe
+                                        * channels in AC'97
+                                        */
+                                       u16             ext_stat =
+                                               rdcodec(s->codec,
+                                                       AC97_EXTENDED_STATUS);
+                                       ext_stat &= ~AC97_EXTSTAT_PRJ;
+                                       if (val == 6)
+                                               ext_stat &=
+                                                       ~(AC97_EXTSTAT_PRI |
+                                                         AC97_EXTSTAT_PRK);
+                                       wrcodec(s->codec,
+                                               AC97_EXTENDED_STATUS,
+                                               ext_stat);
+                               }
+
+                               s->dma_dac.num_channels = val;
+                               if ((ret = prog_dmabuf_dac(s)))
+                                       return ret;
+                       }
+               }
+               return put_user(val, (int *) arg);
+
+       case SNDCTL_DSP_GETFMTS:        /* Returns a mask */
+               return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg);
+
+       case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */
+               if (get_user(val, (int *) arg))
+                       return -EFAULT;
+               if (val != AFMT_QUERY) {
+                       if (file->f_mode & FMODE_READ) {
+                               stop_adc(s);
+                               if (val == AFMT_S16_LE)
+                                       s->dma_adc.sample_size = 16;
+                               else {
+                                       val = AFMT_U8;
+                                       s->dma_adc.sample_size = 8;
+                               }
+                               if ((ret = prog_dmabuf_adc(s)))
+                                       return ret;
+                       }
+                       if (file->f_mode & FMODE_WRITE) {
+                               stop_dac(s);
+                               if (val == AFMT_S16_LE)
+                                       s->dma_dac.sample_size = 16;
+                               else {
+                                       val = AFMT_U8;
+                                       s->dma_dac.sample_size = 8;
+                               }
+                               if ((ret = prog_dmabuf_dac(s)))
+                                       return ret;
+                       }
+               } else {
+                       if (file->f_mode & FMODE_READ)
+                               val = (s->dma_adc.sample_size == 16) ?
+                                       AFMT_S16_LE : AFMT_U8;
+                       else
+                               val = (s->dma_dac.sample_size == 16) ?
+                                       AFMT_S16_LE : AFMT_U8;
+               }
+               return put_user(val, (int *) arg);
+
+       case SNDCTL_DSP_POST:
+               return 0;
+
+       case SNDCTL_DSP_GETTRIGGER:
+               val = 0;
+               spin_lock_irqsave(&s->lock, flags);
+               if (file->f_mode & FMODE_READ && !s->dma_adc.stopped)
+                       val |= PCM_ENABLE_INPUT;
+               if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped)
+                       val |= PCM_ENABLE_OUTPUT;
+               spin_unlock_irqrestore(&s->lock, flags);
+               return put_user(val, (int *) arg);
+
+       case SNDCTL_DSP_SETTRIGGER:
+               if (get_user(val, (int *) arg))
+                       return -EFAULT;
+               if (file->f_mode & FMODE_READ) {
+                       if (val & PCM_ENABLE_INPUT)
+                               start_adc(s);
+                       else
+                               stop_adc(s);
+               }
+               if (file->f_mode & FMODE_WRITE) {
+                       if (val & PCM_ENABLE_OUTPUT)
+                               start_dac(s);
+                       else
+                               stop_dac(s);
+               }
+               return 0;
+
+       case SNDCTL_DSP_GETOSPACE:
+               if (!(file->f_mode & FMODE_WRITE))
+                       return -EINVAL;
+               abinfo.fragsize = s->dma_dac.fragsize;
+               spin_lock_irqsave(&s->lock, flags);
+               count = s->dma_dac.count;
+               count -= dma_count_done(&s->dma_dac);
+               spin_unlock_irqrestore(&s->lock, flags);
+               if (count < 0)
+                       count = 0;
+               abinfo.bytes = (s->dma_dac.dmasize - count) /
+                       s->dma_dac.cnt_factor;
+               abinfo.fragstotal = s->dma_dac.numfrag;
+               abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
+               pr_debug("ioctl SNDCTL_DSP_GETOSPACE: bytes=%d, fragments=%d\n", abinfo.bytes, abinfo.fragments);
+               return copy_to_user((void *) arg, &abinfo,
+                                   sizeof(abinfo)) ? -EFAULT : 0;
+
+       case SNDCTL_DSP_GETISPACE:
+               if (!(file->f_mode & FMODE_READ))
+                       return -EINVAL;
+               abinfo.fragsize = s->dma_adc.fragsize;
+               spin_lock_irqsave(&s->lock, flags);
+               count = s->dma_adc.count;
+               count += dma_count_done(&s->dma_adc);
+               spin_unlock_irqrestore(&s->lock, flags);
+               if (count < 0)
+                       count = 0;
+               abinfo.bytes = count / s->dma_adc.cnt_factor;
+               abinfo.fragstotal = s->dma_adc.numfrag;
+               abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;
+               return copy_to_user((void *) arg, &abinfo,
+                                   sizeof(abinfo)) ? -EFAULT : 0;
+
+       case SNDCTL_DSP_NONBLOCK:
+               file->f_flags |= O_NONBLOCK;
+               return 0;
+
+       case SNDCTL_DSP_GETODELAY:
+               if (!(file->f_mode & FMODE_WRITE))
+                       return -EINVAL;
+               spin_lock_irqsave(&s->lock, flags);
+               count = s->dma_dac.count;
+               count -= dma_count_done(&s->dma_dac);
+               spin_unlock_irqrestore(&s->lock, flags);
+               if (count < 0)
+                       count = 0;
+               count /= s->dma_dac.cnt_factor;
+               return put_user(count, (int *) arg);
+
+       case SNDCTL_DSP_GETIPTR:
+               if (!(file->f_mode & FMODE_READ))
+                       return -EINVAL;
+               spin_lock_irqsave(&s->lock, flags);
+               cinfo.bytes = s->dma_adc.total_bytes;
+               count = s->dma_adc.count;
+               if (!s->dma_adc.stopped) {
+                       diff = dma_count_done(&s->dma_adc);
+                       count += diff;
+                       cinfo.bytes += diff;
+                       cinfo.ptr =  virt_to_phys(s->dma_adc.nextIn) + diff -
+                               virt_to_phys(s->dma_adc.rawbuf);
+               } else
+                       cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) -
+                               virt_to_phys(s->dma_adc.rawbuf);
+               if (s->dma_adc.mapped)
+                       s->dma_adc.count &= (s->dma_adc.dma_fragsize-1);
+               spin_unlock_irqrestore(&s->lock, flags);
+               if (count < 0)
+                       count = 0;
+               cinfo.blocks = count >> s->dma_adc.fragshift;
+               return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
+
+       case SNDCTL_DSP_GETOPTR:
+               if (!(file->f_mode & FMODE_READ))
+                       return -EINVAL;
+               spin_lock_irqsave(&s->lock, flags);
+               cinfo.bytes = s->dma_dac.total_bytes;
+               count = s->dma_dac.count;
+               if (!s->dma_dac.stopped) {
+                       diff = dma_count_done(&s->dma_dac);
+                       count -= diff;
+                       cinfo.bytes += diff;
+                       cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) + diff -
+                               virt_to_phys(s->dma_dac.rawbuf);
+               } else
+                       cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) -
+                               virt_to_phys(s->dma_dac.rawbuf);
+               if (s->dma_dac.mapped)
+                       s->dma_dac.count &= (s->dma_dac.dma_fragsize-1);
+               spin_unlock_irqrestore(&s->lock, flags);
+               if (count < 0)
+                       count = 0;
+               cinfo.blocks = count >> s->dma_dac.fragshift;
+               return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
+
+       case SNDCTL_DSP_GETBLKSIZE:
+               if (file->f_mode & FMODE_WRITE)
+                       return put_user(s->dma_dac.fragsize, (int *) arg);
+               else
+                       return put_user(s->dma_adc.fragsize, (int *) arg);
+
+       case SNDCTL_DSP_SETFRAGMENT:
+               if (get_user(val, (int *) arg))
+                       return -EFAULT;
+               if (file->f_mode & FMODE_READ) {
+                       stop_adc(s);
+                       s->dma_adc.ossfragshift = val & 0xffff;
+                       s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+                       if (s->dma_adc.ossfragshift < 4)
+                               s->dma_adc.ossfragshift = 4;
+                       if (s->dma_adc.ossfragshift > 15)
+                               s->dma_adc.ossfragshift = 15;
+                       if (s->dma_adc.ossmaxfrags < 4)
+                               s->dma_adc.ossmaxfrags = 4;
+                       if ((ret = prog_dmabuf_adc(s)))
+                               return ret;
+               }
+               if (file->f_mode & FMODE_WRITE) {
+                       stop_dac(s);
+                       s->dma_dac.ossfragshift = val & 0xffff;
+                       s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+                       if (s->dma_dac.ossfragshift < 4)
+                               s->dma_dac.ossfragshift = 4;
+                       if (s->dma_dac.ossfragshift > 15)
+                               s->dma_dac.ossfragshift = 15;
+                       if (s->dma_dac.ossmaxfrags < 4)
+                               s->dma_dac.ossmaxfrags = 4;
+                       if ((ret = prog_dmabuf_dac(s)))
+                               return ret;
+               }
+               return 0;
+
+       case SNDCTL_DSP_SUBDIVIDE:
+               if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+                   (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+                       return -EINVAL;
+               if (get_user(val, (int *) arg))
+                       return -EFAULT;
+               if (val != 1 && val != 2 && val != 4)
+                       return -EINVAL;
+               if (file->f_mode & FMODE_READ) {
+                       stop_adc(s);
+                       s->dma_adc.subdivision = val;
+                       if ((ret = prog_dmabuf_adc(s)))
+                               return ret;
+               }
+               if (file->f_mode & FMODE_WRITE) {
+                       stop_dac(s);
+                       s->dma_dac.subdivision = val;
+                       if ((ret = prog_dmabuf_dac(s)))
+                               return ret;
+               }
+               return 0;
+
+       case SOUND_PCM_READ_RATE:
+               return put_user((file->f_mode & FMODE_READ) ?
+                               s->dma_adc.sample_rate :
+                               s->dma_dac.sample_rate,
+                               (int *)arg);
+
+       case SOUND_PCM_READ_CHANNELS:
+               if (file->f_mode & FMODE_READ)
+                       return put_user(s->dma_adc.num_channels, (int *)arg);
+               else
+                       return put_user(s->dma_dac.num_channels, (int *)arg);
+
+       case SOUND_PCM_READ_BITS:
+               if (file->f_mode & FMODE_READ)
+                       return put_user(s->dma_adc.sample_size, (int *)arg);
+               else
+                       return put_user(s->dma_dac.sample_size, (int *)arg);
+
+       case SOUND_PCM_WRITE_FILTER:
+       case SNDCTL_DSP_SETSYNCRO:
+       case SOUND_PCM_READ_FILTER:
+               return -EINVAL;
+       }
+
+       return mixdev_ioctl(s->codec, cmd, arg);
+}
+
+
+static int
+au1550_open(struct inode *inode, struct file *file)
+{
+       int             minor = MINOR(inode->i_rdev);
+       DECLARE_WAITQUEUE(wait, current);
+       struct au1550_state *s = &au1550_state;
+       int             ret;
+
+#ifdef DEBUG
+       if (file->f_flags & O_NONBLOCK)
+               pr_debug("open: non-blocking\n");
+       else
+               pr_debug("open: blocking\n");
+#endif
+
+       file->private_data = s;
+       /* wait for device to become free */
+       down(&s->open_sem);
+       while (s->open_mode & file->f_mode) {
+               if (file->f_flags & O_NONBLOCK) {
+                       up(&s->open_sem);
+                       return -EBUSY;
+               }
+               add_wait_queue(&s->open_wait, &wait);
+               __set_current_state(TASK_INTERRUPTIBLE);
+               up(&s->open_sem);
+               schedule();
+               remove_wait_queue(&s->open_wait, &wait);
+               set_current_state(TASK_RUNNING);
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+               down(&s->open_sem);
+       }
+
+       stop_dac(s);
+       stop_adc(s);
+
+       if (file->f_mode & FMODE_READ) {
+               s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
+                       s->dma_adc.subdivision = s->dma_adc.total_bytes = 0;
+               s->dma_adc.num_channels = 1;
+               s->dma_adc.sample_size = 8;
+               set_adc_rate(s, 8000);
+               if ((minor & 0xf) == SND_DEV_DSP16)
+                       s->dma_adc.sample_size = 16;
+       }
+
+       if (file->f_mode & FMODE_WRITE) {
+               s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
+                       s->dma_dac.subdivision = s->dma_dac.total_bytes = 0;
+               s->dma_dac.num_channels = 1;
+               s->dma_dac.sample_size = 8;
+               set_dac_rate(s, 8000);
+               if ((minor & 0xf) == SND_DEV_DSP16)
+                       s->dma_dac.sample_size = 16;
+       }
+
+       if (file->f_mode & FMODE_READ) {
+               if ((ret = prog_dmabuf_adc(s)))
+                       return ret;
+       }
+       if (file->f_mode & FMODE_WRITE) {
+               if ((ret = prog_dmabuf_dac(s)))
+                       return ret;
+       }
+
+       s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+       up(&s->open_sem);
+       init_MUTEX(&s->sem);
+       return 0;
+}
+
+static int
+au1550_release(struct inode *inode, struct file *file)
+{
+       struct au1550_state *s = (struct au1550_state *)file->private_data;
+
+       lock_kernel();
+
+       if (file->f_mode & FMODE_WRITE) {
+               unlock_kernel();
+               drain_dac(s, file->f_flags & O_NONBLOCK);
+               lock_kernel();
+       }
+
+       down(&s->open_sem);
+       if (file->f_mode & FMODE_WRITE) {
+               stop_dac(s);
+               kfree(s->dma_dac.rawbuf);
+               s->dma_dac.rawbuf = NULL;
+       }
+       if (file->f_mode & FMODE_READ) {
+               stop_adc(s);
+               kfree(s->dma_adc.rawbuf);
+               s->dma_adc.rawbuf = NULL;
+       }
+       s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE));
+       up(&s->open_sem);
+       wake_up(&s->open_wait);
+       unlock_kernel();
+       return 0;
+}
+
+static /*const */ struct file_operations au1550_audio_fops = {
+       owner:          THIS_MODULE,
+       llseek:         au1550_llseek,
+       read:           au1550_read,
+       write:          au1550_write,
+       poll:           au1550_poll,
+       ioctl:          au1550_ioctl,
+       mmap:           au1550_mmap,
+       open:           au1550_open,
+       release:        au1550_release,
+};
+
+MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com");
+MODULE_DESCRIPTION("Au1550 AC97 Audio Driver");
+
+static int __devinit
+au1550_probe(void)
+{
+       struct au1550_state *s = &au1550_state;
+       int             val;
+
+       memset(s, 0, sizeof(struct au1550_state));
+
+       init_waitqueue_head(&s->dma_adc.wait);
+       init_waitqueue_head(&s->dma_dac.wait);
+       init_waitqueue_head(&s->open_wait);
+       init_MUTEX(&s->open_sem);
+       spin_lock_init(&s->lock);
+
+       s->codec = ac97_alloc_codec();
+       if(s->codec == NULL) {
+               err("Out of memory");
+               return -1;
+       }
+       s->codec->private_data = s;
+       s->codec->id = 0;
+       s->codec->codec_read = rdcodec;
+       s->codec->codec_write = wrcodec;
+       s->codec->codec_wait = waitcodec;
+
+       if (!request_mem_region(CPHYSADDR(AC97_PSC_SEL),
+                           0x30, "Au1550 AC97")) {
+               err("AC'97 ports in use");
+       }
+
+       /* Allocate the DMA Channels
+       */
+       if ((s->dma_dac.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_MEM_CHAN,
+           DBDMA_AC97_TX_CHAN, dac_dma_interrupt, (void *)s)) == 0) {
+               err("Can't get DAC DMA");
+               goto err_dma1;
+       }
+       au1xxx_dbdma_set_devwidth(s->dma_dac.dmanr, 16);
+       if (au1xxx_dbdma_ring_alloc(s->dma_dac.dmanr,
+                                       NUM_DBDMA_DESCRIPTORS) == 0) {
+               err("Can't get DAC DMA descriptors");
+               goto err_dma1;
+       }
+
+       if ((s->dma_adc.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_AC97_RX_CHAN,
+           DBDMA_MEM_CHAN, adc_dma_interrupt, (void *)s)) == 0) {
+               err("Can't get ADC DMA");
+               goto err_dma2;
+       }
+       au1xxx_dbdma_set_devwidth(s->dma_adc.dmanr, 16);
+       if (au1xxx_dbdma_ring_alloc(s->dma_adc.dmanr,
+                                       NUM_DBDMA_DESCRIPTORS) == 0) {
+               err("Can't get ADC DMA descriptors");
+               goto err_dma2;
+       }
+
+       pr_info("DAC: DMA%d, ADC: DMA%d", DBDMA_AC97_TX_CHAN, DBDMA_AC97_RX_CHAN);
+
+       /* register devices */
+
+       if ((s->dev_audio = register_sound_dsp(&au1550_audio_fops, -1)) < 0)
+               goto err_dev1;
+       if ((s->codec->dev_mixer =
+            register_sound_mixer(&au1550_mixer_fops, -1)) < 0)
+               goto err_dev2;
+
+       /* The GPIO for the appropriate PSC was configured by the
+        * board specific start up.
+        *
+        * configure PSC for AC'97
+        */
+       au_writel(0, AC97_PSC_CTRL);    /* Disable PSC */
+       au_sync();
+       au_writel((PSC_SEL_CLK_SERCLK | PSC_SEL_PS_AC97MODE), AC97_PSC_SEL);
+       au_sync();
+
+       /* cold reset the AC'97
+       */
+       au_writel(PSC_AC97RST_RST, PSC_AC97RST);
+       au_sync();
+       au1550_delay(10);
+       au_writel(0, PSC_AC97RST);
+       au_sync();
+
+       /* need to delay around 500msec(bleech) to give
+          some CODECs enough time to wakeup */
+       au1550_delay(500);
+
+       /* warm reset the AC'97 to start the bitclk
+       */
+       au_writel(PSC_AC97RST_SNC, PSC_AC97RST);
+       au_sync();
+       udelay(100);
+       au_writel(0, PSC_AC97RST);
+       au_sync();
+
+       /* Enable PSC
+       */
+       au_writel(PSC_CTRL_ENABLE, AC97_PSC_CTRL);
+       au_sync();
+
+       /* Wait for PSC ready.
+       */
+       do {
+               val = readl((void *)PSC_AC97STAT);
+               au_sync();
+       } while ((val & PSC_AC97STAT_SR) == 0);
+
+       /* Configure AC97 controller.
+        * Deep FIFO, 16-bit sample, DMA, make sure DMA matches fifo size.
+        */
+       val = PSC_AC97CFG_SET_LEN(16);
+       val |= PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8;
+
+       /* Enable device so we can at least
+        * talk over the AC-link.
+        */
+       au_writel(val, PSC_AC97CFG);
+       au_writel(PSC_AC97MSK_ALLMASK, PSC_AC97MSK);
+       au_sync();
+       val |= PSC_AC97CFG_DE_ENABLE;
+       au_writel(val, PSC_AC97CFG);
+       au_sync();
+
+       /* Wait for Device ready.
+       */
+       do {
+               val = readl((void *)PSC_AC97STAT);
+               au_sync();
+       } while ((val & PSC_AC97STAT_DR) == 0);
+
+       /* codec init */
+       if (!ac97_probe_codec(s->codec))
+               goto err_dev3;
+
+       s->codec_base_caps = rdcodec(s->codec, AC97_RESET);
+       s->codec_ext_caps = rdcodec(s->codec, AC97_EXTENDED_ID);
+       pr_info("AC'97 Base/Extended ID = %04x/%04x",
+            s->codec_base_caps, s->codec_ext_caps);
+
+       if (!(s->codec_ext_caps & AC97_EXTID_VRA)) {
+               /* codec does not support VRA
+               */
+               s->no_vra = 1;
+       } else if (!vra) {
+               /* Boot option says disable VRA
+               */
+               u16 ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
+               wrcodec(s->codec, AC97_EXTENDED_STATUS,
+                       ac97_extstat & ~AC97_EXTSTAT_VRA);
+               s->no_vra = 1;
+       }
+       if (s->no_vra)
+               pr_info("no VRA, interpolating and decimating");
+
+       /* set mic to be the recording source */
+       val = SOUND_MASK_MIC;
+       mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC,
+                    (unsigned long) &val);
+
+       return 0;
+
+ err_dev3:
+       unregister_sound_mixer(s->codec->dev_mixer);
+ err_dev2:
+       unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+       au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
+ err_dma2:
+       au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
+ err_dma1:
+       release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
+
+       ac97_release_codec(s->codec);
+       return -1;
+}
+
+static void __devinit
+au1550_remove(void)
+{
+       struct au1550_state *s = &au1550_state;
+
+       if (!s)
+               return;
+       synchronize_irq();
+       au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
+       au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
+       release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
+       unregister_sound_dsp(s->dev_audio);
+       unregister_sound_mixer(s->codec->dev_mixer);
+       ac97_release_codec(s->codec);
+}
+
+static int __init
+init_au1550(void)
+{
+       return au1550_probe();
+}
+
+static void __exit
+cleanup_au1550(void)
+{
+       au1550_remove();
+}
+
+module_init(init_au1550);
+module_exit(cleanup_au1550);
+
+#ifndef MODULE
+
+static int __init
+au1550_setup(char *options)
+{
+       char           *this_opt;
+
+       if (!options || !*options)
+               return 0;
+
+       while ((this_opt = strsep(&options, ","))) {
+               if (!*this_opt)
+                       continue;
+               if (!strncmp(this_opt, "vra", 3)) {
+                       vra = 1;
+               }
+       }
+
+       return 1;
+}
+
+__setup("au1550_audio=", au1550_setup);
+
+#endif /* MODULE */
diff --git a/sound/pci/ca0106/Makefile b/sound/pci/ca0106/Makefile
new file mode 100644 (file)
index 0000000..89c6cee
--- /dev/null
@@ -0,0 +1,3 @@
+snd-ca0106-objs := ca0106_main.o ca0106_proc.o ca0106_mixer.o
+
+obj-$(CONFIG_SND_CA0106) += snd-ca0106.o
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
new file mode 100644 (file)
index 0000000..deb0288
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ *  Version: 0.0.20
+ *
+ *  FEATURES currently supported:
+ *    See ca0106_main.c for features.
+ * 
+ *  Changelog:
+ *    Support interrupts per period.
+ *    Removed noise from Center/LFE channel when in Analog mode.
+ *    Rename and remove mixer controls.
+ *  0.0.6
+ *    Use separate card based DMA buffer for periods table list.
+ *  0.0.7
+ *    Change remove and rename ctrls into lists.
+ *  0.0.8
+ *    Try to fix capture sources.
+ *  0.0.9
+ *    Fix AC3 output.
+ *    Enable S32_LE format support.
+ *  0.0.10
+ *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ *  0.0.11
+ *    Add Model name recognition.
+ *  0.0.12
+ *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ *    Remove redundent "voice" handling.
+ *  0.0.13
+ *    Single trigger call for multi channels.
+ *  0.0.14
+ *    Set limits based on what the sound card hardware can do.
+ *    playback periods_min=2, periods_max=8
+ *    capture hw constraints require period_size = n * 64 bytes.
+ *    playback hw constraints require period_size = n * 64 bytes.
+ *  0.0.15
+ *    Separated ca0106.c into separate functional .c files.
+ *  0.0.16
+ *    Implement 192000 sample rate.
+ *  0.0.17
+ *    Add support for SB0410 and SB0413.
+ *  0.0.18
+ *    Modified Copyright message.
+ *  0.0.19
+ *    Added I2C and SPI registers. Filled in interrupt enable.
+ *  0.0.20
+ *    Added GPIO info for SB Live 24bit.
+ *
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0                                                */
+/************************************************************************************************/
+
+#define PTR                    0x00            /* Indexed register set pointer register        */
+                                               /* NOTE: The CHANNELNUM and ADDRESS words can   */
+                                               /* be modified independently of each other.     */
+                                               /* CNL[1:0], ADDR[27:16]                        */
+
+#define DATA                   0x04            /* Indexed register set data register           */
+                                               /* DATA[31:0]                                   */
+
+#define IPR                    0x08            /* Global interrupt pending register            */
+                                               /* Clear pending interrupts by writing a 1 to   */
+                                               /* the relevant bits and zero to the other bits */
+#define IPR_MIDI_RX_B          0x00020000      /* MIDI UART-B Receive buffer non-empty         */
+#define IPR_MIDI_TX_B          0x00010000      /* MIDI UART-B Transmit buffer empty            */
+#define IPR_SPDIF_IN_USER      0x00004000      /* SPDIF input user data has 16 more bits       */
+#define IPR_SPDIF_OUT_USER     0x00002000      /* SPDIF output user data needs 16 more bits    */
+#define IPR_SPDIF_OUT_FRAME    0x00001000      /* SPDIF frame about to start                   */
+#define IPR_SPI                        0x00000800      /* SPI transaction completed                    */
+#define IPR_I2C_EEPROM         0x00000400      /* I2C EEPROM transaction completed             */
+#define IPR_I2C_DAC            0x00000200      /* I2C DAC transaction completed                */
+#define IPR_AI                 0x00000100      /* Audio pending register changed. See PTR reg 0x76     */
+#define IPR_GPI                        0x00000080      /* General Purpose input changed                */
+#define IPR_SRC_LOCKED          0x00000040      /* SRC lock status changed                     */
+#define IPR_SPDIF_STATUS        0x00000020      /* SPDIF status changed                                */
+#define IPR_TIMER2              0x00000010      /* 192000Hz Timer                              */
+#define IPR_TIMER1              0x00000008      /* 44100Hz Timer                               */
+#define IPR_MIDI_RX_A          0x00000004      /* MIDI UART-A Receive buffer non-empty         */
+#define IPR_MIDI_TX_A          0x00000002      /* MIDI UART-A Transmit buffer empty            */
+#define IPR_PCI                        0x00000001      /* PCI Bus error                                */
+
+#define INTE                   0x0c            /* Interrupt enable register                    */
+
+#define INTE_MIDI_RX_B         0x00020000      /* MIDI UART-B Receive buffer non-empty         */
+#define INTE_MIDI_TX_B         0x00010000      /* MIDI UART-B Transmit buffer empty            */
+#define INTE_SPDIF_IN_USER     0x00004000      /* SPDIF input user data has 16 more bits       */
+#define INTE_SPDIF_OUT_USER    0x00002000      /* SPDIF output user data needs 16 more bits    */
+#define INTE_SPDIF_OUT_FRAME   0x00001000      /* SPDIF frame about to start                   */
+#define INTE_SPI               0x00000800      /* SPI transaction completed                    */
+#define INTE_I2C_EEPROM                0x00000400      /* I2C EEPROM transaction completed             */
+#define INTE_I2C_DAC           0x00000200      /* I2C DAC transaction completed                */
+#define INTE_AI                        0x00000100      /* Audio pending register changed. See PTR reg 0x75 */
+#define INTE_GPI               0x00000080      /* General Purpose input changed                */
+#define INTE_SRC_LOCKED         0x00000040      /* SRC lock status changed                     */
+#define INTE_SPDIF_STATUS       0x00000020      /* SPDIF status changed                                */
+#define INTE_TIMER2             0x00000010      /* 192000Hz Timer                              */
+#define INTE_TIMER1             0x00000008      /* 44100Hz Timer                               */
+#define INTE_MIDI_RX_A         0x00000004      /* MIDI UART-A Receive buffer non-empty         */
+#define INTE_MIDI_TX_A         0x00000002      /* MIDI UART-A Transmit buffer empty            */
+#define INTE_PCI               0x00000001      /* PCI Bus error                                */
+
+#define UNKNOWN10              0x10            /* Unknown ??. Defaults to 0 */
+#define HCFG                   0x14            /* Hardware config register                     */
+                                               /* 0x1000 causes AC3 to fails. It adds a dither bit. */
+
+#define HCFG_STAC              0x10000000      /* Special mode for STAC9460 Codec. */
+#define HCFG_CAPTURE_I2S_BYPASS        0x08000000      /* 1 = bypass I2S input async SRC. */
+#define HCFG_CAPTURE_SPDIF_BYPASS 0x04000000   /* 1 = bypass SPDIF input async SRC. */
+#define HCFG_PLAYBACK_I2S_BYPASS 0x02000000    /* 0 = I2S IN mixer output, 1 = I2S IN1. */
+#define HCFG_FORCE_LOCK                0x01000000      /* For test only. Force input SRC tracker to lock. */
+#define HCFG_PLAYBACK_ATTENUATION 0x00006000   /* Playback attenuation mask. 0 = 0dB, 1 = 6dB, 2 = 12dB, 3 = Mute. */
+#define HCFG_PLAYBACK_DITHER   0x00001000      /* 1 = Add dither bit to all playback channels. */
+#define HCFG_PLAYBACK_S32_LE   0x00000800      /* 1 = S32_LE, 0 = S16_LE                       */
+#define HCFG_CAPTURE_S32_LE    0x00000400      /* 1 = S32_LE, 0 = S16_LE (S32_LE current not working)  */
+#define HCFG_8_CHANNEL_PLAY    0x00000200      /* 1 = 8 channels, 0 = 2 channels per substream.*/
+#define HCFG_8_CHANNEL_CAPTURE 0x00000100      /* 1 = 8 channels, 0 = 2 channels per substream.*/
+#define HCFG_MONO              0x00000080      /* 1 = I2S Input mono                           */
+#define HCFG_I2S_OUTPUT                0x00000010      /* 1 = I2S Output disabled                      */
+#define HCFG_AC97              0x00000008      /* 0 = AC97 1.0, 1 = AC97 2.0                   */
+#define HCFG_LOCK_PLAYBACK_CACHE 0x00000004    /* 1 = Cancel bustmaster accesses to soundcache */
+                                               /* NOTE: This should generally never be used.   */
+#define HCFG_LOCK_CAPTURE_CACHE        0x00000002      /* 1 = Cancel bustmaster accesses to soundcache */
+                                               /* NOTE: This should generally never be used.   */
+#define HCFG_AUDIOENABLE       0x00000001      /* 0 = CODECs transmit zero-valued samples      */
+                                               /* Should be set to 1 when the EMU10K1 is       */
+                                               /* completely initialized.                      */
+#define GPIO                   0x18            /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF.   */
+                                               /* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */
+                                               /* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */
+                                               /* SB Live 24bit:
+                                                * bit 8 0 = SPDIF in and out / 1 = Analog (Mic or Line)-in.
+                                                * bit 9 0 = Mute / 1 = Analog out.
+                                                * bit 10 0 = Line-in / 1 = Mic-in.
+                                                * bit 11 0 = ? / 1 = ?
+                                                * bit 12 0 = ? / 1 = ?
+                                                * bit 13 0 = ? / 1 = ?
+                                                * bit 14 0 = Mute / 1 = Analog out
+                                                * bit 15 0 = ? / 1 = ?
+                                                * Both bit 9 and bit 14 have to be set for analog sound to work on the SB Live 24bit.
+                                                */
+                                               /* 8 general purpose programmable In/Out pins.
+                                                * GPI [8:0] Read only. Default 0.
+                                                * GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF)
+                                                * GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin.
+                                                */
+#define AC97DATA               0x1c            /* AC97 register set data register (16 bit)     */
+
+#define AC97ADDRESS            0x1e            /* AC97 register set address register (8 bit)   */
+
+/********************************************************************************************************/
+/* CA0106 pointer-offset register set, accessed through the PTR and DATA registers                     */
+/********************************************************************************************************/
+                                                                                                                           
+/* Initally all registers from 0x00 to 0x3f have zero contents. */
+#define PLAYBACK_LIST_ADDR     0x00            /* Base DMA address of a list of pointers to each period/size */
+                                               /* One list entry: 4 bytes for DMA address, 
+                                                * 4 bytes for period_size << 16.
+                                                * One list entry is 8 bytes long.
+                                                * One list entry for each period in the buffer.
+                                                */
+                                               /* ADDR[31:0], Default: 0x0 */
+#define PLAYBACK_LIST_SIZE     0x01            /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000  */
+                                               /* SIZE[21:16], Default: 0x8 */
+#define PLAYBACK_LIST_PTR      0x02            /* Pointer to the current period being played */
+                                               /* PTR[5:0], Default: 0x0 */
+#define PLAYBACK_UNKNOWN3      0x03            /* Not used ?? */
+#define PLAYBACK_DMA_ADDR      0x04            /* Playback DMA addresss */
+                                               /* DMA[31:0], Default: 0x0 */
+#define PLAYBACK_PERIOD_SIZE   0x05            /* Playback period size. win2000 uses 0x04000000 */
+                                               /* SIZE[31:16], Default: 0x0 */
+#define PLAYBACK_POINTER       0x06            /* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */
+                                               /* POINTER[15:0], Default: 0x0 */
+#define PLAYBACK_PERIOD_END_ADDR 0x07          /* Playback fifo end address */
+                                               /* END_ADDR[15:0], FLAG[16] 0 = don't stop, 1 = stop */
+#define PLAYBACK_FIFO_OFFSET_ADDRESS   0x08    /* Current fifo offset address [21:16] */
+                                               /* Cache size valid [5:0] */
+#define PLAYBACK_UNKNOWN9      0x09            /* 0x9 to 0xf Unused */
+#define CAPTURE_DMA_ADDR       0x10            /* Capture DMA address */
+                                               /* DMA[31:0], Default: 0x0 */
+#define CAPTURE_BUFFER_SIZE    0x11            /* Capture buffer size */
+                                               /* SIZE[31:16], Default: 0x0 */
+#define CAPTURE_POINTER                0x12            /* Capture buffer pointer. Sample currently in ADC */
+                                               /* POINTER[15:0], Default: 0x0 */
+#define CAPTURE_FIFO_OFFSET_ADDRESS    0x13    /* Current fifo offset address [21:16] */
+                                               /* Cache size valid [5:0] */
+#define PLAYBACK_LAST_SAMPLE    0x20           /* The sample currently being played */
+/* 0x21 - 0x3f unused */
+#define BASIC_INTERRUPT         0x40           /* Used by both playback and capture interrupt handler */
+                                               /* Playback (0x1<<channel_id) */
+                                               /* Capture  (0x100<<channel_id) */
+                                               /* Playback sample rate 96000 = 0x20000 */
+                                               /* Start Playback [3:0] (one bit per channel)
+                                                * Start Capture [11:8] (one bit per channel)
+                                                * Playback rate [23:16] (2 bits per channel) (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+                                                * Playback mixer in enable [27:24] (one bit per channel)
+                                                * Playback mixer out enable [31:28] (one bit per channel)
+                                                */
+/* The Digital out jack is shared with the Center/LFE Analogue output. 
+ * The jack has 4 poles. I will call 1 - Tip, 2 - Next to 1, 3 - Next to 2, 4 - Next to 3
+ * For Analogue: 1 -> Center Speaker, 2 -> Sub Woofer, 3 -> Ground, 4 -> Ground
+ * For Digital: 1 -> Front SPDIF, 2 -> Rear SPDIF, 3 -> Center/Subwoofer SPDIF, 4 -> Ground.
+ * Standard 4 pole Video A/V cable with RCA outputs: 1 -> White, 2 -> Yellow, 3 -> Sheild on all three, 4 -> Red.
+ * So, from this you can see that you cannot use a Standard 4 pole Video A/V cable with the SB Audigy LS card.
+ */
+/* The Front SPDIF PCM gets mixed with samples from the AC97 codec, so can only work for Stereo PCM and not AC3/DTS
+ * The Rear SPDIF can be used for Stereo PCM and also AC3/DTS
+ * The Center/LFE SPDIF cannot be used for AC3/DTS, but can be used for Stereo PCM.
+ * Summary: For ALSA we use the Rear channel for SPDIF Digital AC3/DTS output
+ */
+/* A standard 2 pole mono mini-jack to RCA plug can be used for SPDIF Stereo PCM output from the Front channel.
+ * A standard 3 pole stereo mini-jack to 2 RCA plugs can be used for SPDIF AC3/DTS and Stereo PCM output utilising the Rear channel and just one of the RCA plugs. 
+ */
+#define SPCS0                  0x41            /* SPDIF output Channel Status 0 register. For Rear. default=0x02108004, non-audio=0x02108006   */
+#define SPCS1                  0x42            /* SPDIF output Channel Status 1 register. For Front */
+#define SPCS2                  0x43            /* SPDIF output Channel Status 2 register. For Center/LFE */
+#define SPCS3                  0x44            /* SPDIF output Channel Status 3 register. Unknown */
+                                               /* When Channel set to 0: */
+#define SPCS_CLKACCYMASK       0x30000000      /* Clock accuracy                               */
+#define SPCS_CLKACCY_1000PPM   0x00000000      /* 1000 parts per million                       */
+#define SPCS_CLKACCY_50PPM     0x10000000      /* 50 parts per million                         */
+#define SPCS_CLKACCY_VARIABLE  0x20000000      /* Variable accuracy                            */
+#define SPCS_SAMPLERATEMASK    0x0f000000      /* Sample rate                                  */
+#define SPCS_SAMPLERATE_44     0x00000000      /* 44.1kHz sample rate                          */
+#define SPCS_SAMPLERATE_48     0x02000000      /* 48kHz sample rate                            */
+#define SPCS_SAMPLERATE_32     0x03000000      /* 32kHz sample rate                            */
+#define SPCS_CHANNELNUMMASK    0x00f00000      /* Channel number                               */
+#define SPCS_CHANNELNUM_UNSPEC 0x00000000      /* Unspecified channel number                   */
+#define SPCS_CHANNELNUM_LEFT   0x00100000      /* Left channel                                 */
+#define SPCS_CHANNELNUM_RIGHT  0x00200000      /* Right channel                                */
+#define SPCS_SOURCENUMMASK     0x000f0000      /* Source number                                */
+#define SPCS_SOURCENUM_UNSPEC  0x00000000      /* Unspecified source number                    */
+#define SPCS_GENERATIONSTATUS  0x00008000      /* Originality flag (see IEC-958 spec)          */
+#define SPCS_CATEGORYCODEMASK  0x00007f00      /* Category code (see IEC-958 spec)             */
+#define SPCS_MODEMASK          0x000000c0      /* Mode (see IEC-958 spec)                      */
+#define SPCS_EMPHASISMASK      0x00000038      /* Emphasis                                     */
+#define SPCS_EMPHASIS_NONE     0x00000000      /* No emphasis                                  */
+#define SPCS_EMPHASIS_50_15    0x00000008      /* 50/15 usec 2 channel                         */
+#define SPCS_COPYRIGHT         0x00000004      /* Copyright asserted flag -- do not modify     */
+#define SPCS_NOTAUDIODATA      0x00000002      /* 0 = Digital audio, 1 = not audio             */
+#define SPCS_PROFESSIONAL      0x00000001      /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992)  */
+
+                                               /* When Channel set to 1: */
+#define SPCS_WORD_LENGTH_MASK  0x0000000f      /* Word Length Mask                             */
+#define SPCS_WORD_LENGTH_16    0x00000008      /* Word Length 16 bit                           */
+#define SPCS_WORD_LENGTH_17    0x00000006      /* Word Length 17 bit                           */
+#define SPCS_WORD_LENGTH_18    0x00000004      /* Word Length 18 bit                           */
+#define SPCS_WORD_LENGTH_19    0x00000002      /* Word Length 19 bit                           */
+#define SPCS_WORD_LENGTH_20A   0x0000000a      /* Word Length 20 bit                           */
+#define SPCS_WORD_LENGTH_20    0x00000009      /* Word Length 20 bit (both 0xa and 0x9 are 20 bit) */
+#define SPCS_WORD_LENGTH_21    0x00000007      /* Word Length 21 bit                           */
+#define SPCS_WORD_LENGTH_21    0x00000007      /* Word Length 21 bit                           */
+#define SPCS_WORD_LENGTH_22    0x00000005      /* Word Length 22 bit                           */
+#define SPCS_WORD_LENGTH_23    0x00000003      /* Word Length 23 bit                           */
+#define SPCS_WORD_LENGTH_24    0x0000000b      /* Word Length 24 bit                           */
+#define SPCS_ORIGINAL_SAMPLE_RATE_MASK 0x000000f0 /* Original Sample rate                      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_NONE 0x00000000 /* Original Sample rate not indicated        */
+#define SPCS_ORIGINAL_SAMPLE_RATE_16000        0x00000010 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_RES1 0x00000020 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_32000        0x00000030 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_12000        0x00000040 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_11025        0x00000050 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_8000 0x00000060 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_RES2 0x00000070 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_192000 0x00000080 /* Original Sample rate    */
+#define SPCS_ORIGINAL_SAMPLE_RATE_24000        0x00000090 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_96000        0x000000a0 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_48000        0x000000b0 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_176400 0x000000c0 /* Original Sample rate    */
+#define SPCS_ORIGINAL_SAMPLE_RATE_22050        0x000000d0 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_88200        0x000000e0 /* Original Sample rate      */
+#define SPCS_ORIGINAL_SAMPLE_RATE_44100        0x000000f0 /* Original Sample rate      */
+
+#define SPDIF_SELECT1          0x45            /* Enables SPDIF or Analogue outputs 0-SPDIF, 0xf00-Analogue */
+                                               /* 0x100 - Front, 0x800 - Rear, 0x200 - Center/LFE.
+                                                * But as the jack is shared, use 0xf00.
+                                                * The Windows2000 driver uses 0x0000000f for both digital and analog.
+                                                * 0xf00 introduces interesting noises onto the Center/LFE.
+                                                * If you turn the volume up, you hear computer noise,
+                                                * e.g. mouse moving, changing between app windows etc.
+                                                * So, I am going to set this to 0x0000000f all the time now,
+                                                * same as the windows driver does.
+                                                * Use register SPDIF_SELECT2(0x72) to switch between SPDIF and Analog.
+                                                */
+                                               /* When Channel = 0:
+                                                * Wide SPDIF format [3:0] (one bit for each channel) (0=20bit, 1=24bit)
+                                                * Tristate SPDIF Output [11:8] (one bit for each channel) (0=Not tristate, 1=Tristate)
+                                                * SPDIF Bypass enable [19:16] (one bit for each channel) (0=Not bypass, 1=Bypass)
+                                                */
+                                               /* When Channel = 1:
+                                                * SPDIF 0 User data [7:0]
+                                                * SPDIF 1 User data [15:8]
+                                                * SPDIF 0 User data [23:16]
+                                                * SPDIF 0 User data [31:24]
+                                                * User data can be sent by using the SPDIF output frame pending and SPDIF output user bit interrupts.
+                                                */
+#define WATERMARK              0x46            /* Test bit to indicate cache usage level */
+#define SPDIF_INPUT_STATUS     0x49            /* SPDIF Input status register. Bits the same as SPCS.
+                                                * When Channel = 0: Bits the same as SPCS channel 0.
+                                                * When Channel = 1: Bits the same as SPCS channel 1.
+                                                * When Channel = 2:
+                                                * SPDIF Input User data [16:0]
+                                                * SPDIF Input Frame count [21:16]
+                                                */
+#define CAPTURE_CACHE_DATA     0x50            /* 0x50-0x5f Recorded samples. */
+#define CAPTURE_SOURCE          0x60            /* Capture Source 0 = MIC */
+#define CAPTURE_SOURCE_CHANNEL0 0xf0000000     /* Mask for selecting the Capture sources */
+#define CAPTURE_SOURCE_CHANNEL1 0x0f000000     /* 0 - SPDIF mixer output. */
+#define CAPTURE_SOURCE_CHANNEL2 0x00f00000      /* 1 - What you hear or . 2 - ?? */
+#define CAPTURE_SOURCE_CHANNEL3 0x000f0000     /* 3 - Mic in, Line in, TAD in, Aux in. */
+#define CAPTURE_SOURCE_RECORD_MAP 0x0000ffff   /* Default 0x00e4 */
+                                               /* Record Map [7:0] (2 bits per channel) 0=mapped to channel 0, 1=mapped to channel 1, 2=mapped to channel2, 3=mapped to channel3 
+                                                * Record source select for channel 0 [18:16]
+                                                * Record source select for channel 1 [22:20]
+                                                * Record source select for channel 2 [26:24]
+                                                * Record source select for channel 3 [30:28]
+                                                * 0 - SPDIF mixer output.
+                                                * 1 - i2s mixer output.
+                                                * 2 - SPDIF input.
+                                                * 3 - i2s input.
+                                                * 4 - AC97 capture.
+                                                * 5 - SRC output.
+                                                */
+#define CAPTURE_VOLUME1         0x61            /* Capture  volume per channel 0-3 */
+#define CAPTURE_VOLUME2         0x62            /* Capture  volume per channel 4-7 */
+
+#define PLAYBACK_ROUTING1       0x63            /* Playback routing of channels 0-7. Effects AC3 output. Default 0x32765410 */
+#define ROUTING1_REAR           0x77000000      /* Channel_id 0 sends to 10, Channel_id 1 sends to 32 */
+#define ROUTING1_NULL           0x00770000      /* Channel_id 2 sends to 54, Channel_id 3 sends to 76 */
+#define ROUTING1_CENTER_LFE     0x00007700      /* 0x32765410 means, send Channel_id 0 to FRONT, Channel_id 1 to REAR */
+#define ROUTING1_FRONT          0x00000077     /* Channel_id 2 to CENTER_LFE, Channel_id 3 to NULL. */
+                                               /* Channel_id's handle stereo channels. Channel X is a single mono channel */
+                                               /* Host is input from the PCI bus. */
+                                               /* Host channel 0 [2:0] -> SPDIF Mixer/Router channel 0-7.
+                                                * Host channel 1 [6:4] -> SPDIF Mixer/Router channel 0-7.
+                                                * Host channel 2 [10:8] -> SPDIF Mixer/Router channel 0-7.
+                                                * Host channel 3 [14:12] -> SPDIF Mixer/Router channel 0-7.
+                                                * Host channel 4 [18:16] -> SPDIF Mixer/Router channel 0-7.
+                                                * Host channel 5 [22:20] -> SPDIF Mixer/Router channel 0-7.
+                                                * Host channel 6 [26:24] -> SPDIF Mixer/Router channel 0-7.
+                                                * Host channel 7 [30:28] -> SPDIF Mixer/Router channel 0-7.
+                                                */
+
+#define PLAYBACK_ROUTING2       0x64            /* Playback Routing . Feeding Capture channels back into Playback. Effects AC3 output. Default 0x76767676 */
+                                               /* SRC is input from the capture inputs. */
+                                               /* SRC channel 0 [2:0] -> SPDIF Mixer/Router channel 0-7.
+                                                * SRC channel 1 [6:4] -> SPDIF Mixer/Router channel 0-7.
+                                                * SRC channel 2 [10:8] -> SPDIF Mixer/Router channel 0-7.
+                                                * SRC channel 3 [14:12] -> SPDIF Mixer/Router channel 0-7.
+                                                * SRC channel 4 [18:16] -> SPDIF Mixer/Router channel 0-7.
+                                                * SRC channel 5 [22:20] -> SPDIF Mixer/Router channel 0-7.
+                                                * SRC channel 6 [26:24] -> SPDIF Mixer/Router channel 0-7.
+                                                * SRC channel 7 [30:28] -> SPDIF Mixer/Router channel 0-7.
+                                                */
+
+#define PLAYBACK_MUTE           0x65            /* Unknown. While playing 0x0, while silent 0x00fc0000 */
+                                               /* SPDIF Mixer input control:
+                                                * Invert SRC to SPDIF Mixer [7-0] (One bit per channel)
+                                                * Invert Host to SPDIF Mixer [15:8] (One bit per channel)
+                                                * SRC to SPDIF Mixer disable [23:16] (One bit per channel)
+                                                * Host to SPDIF Mixer disable [31:24] (One bit per channel)
+                                                */
+#define PLAYBACK_VOLUME1        0x66            /* Playback SPDIF volume per channel. Set to the same PLAYBACK_VOLUME(0x6a) */
+                                               /* PLAYBACK_VOLUME1 must be set to 30303030 for SPDIF AC3 Playback */
+                                               /* SPDIF mixer input volume. 0=12dB, 0x30=0dB, 0xFE=-51.5dB, 0xff=Mute */
+                                               /* One register for each of the 4 stereo streams. */
+                                               /* SRC Right volume [7:0]
+                                                * SRC Left  volume [15:8]
+                                                * Host Right volume [23:16]
+                                                * Host Left  volume [31:24]
+                                                */
+#define CAPTURE_ROUTING1        0x67            /* Capture Routing. Default 0x32765410 */
+                                               /* Similar to register 0x63, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define CAPTURE_ROUTING2        0x68            /* Unknown Routing. Default 0x76767676 */
+                                               /* Similar to register 0x64, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define CAPTURE_MUTE            0x69            /* Unknown. While capturing 0x0, while silent 0x00fc0000 */
+                                               /* Similar to register 0x65, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define PLAYBACK_VOLUME2        0x6a            /* Playback Analog volume per channel. Does not effect AC3 output */
+                                               /* Similar to register 0x66, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */
+#define UNKNOWN6b               0x6b            /* Unknown. Readonly. Default 00400000 00400000 00400000 00400000 */
+#define UART_A_DATA            0x6c            /* Uart, used in setting sample rates, bits per sample etc. */
+#define UART_A_CMD             0x6d            /* Uart, used in setting sample rates, bits per sample etc. */
+#define UART_B_DATA            0x6e            /* Uart, Unknown. */
+#define UART_B_CMD             0x6f            /* Uart, Unknown. */
+#define SAMPLE_RATE_TRACKER_STATUS 0x70         /* Readonly. Default 00108000 00108000 00500000 00500000 */
+                                               /* Estimated sample rate [19:0] Relative to 48kHz. 0x8000 =  1.0
+                                                * Rate Locked [20]
+                                                * SPDIF Locked [21] For SPDIF channel only.
+                                                * Valid Audio [22] For SPDIF channel only.
+                                                */
+#define CAPTURE_CONTROL         0x71            /* Some sort of routing. default = 40c81000 30303030 30300000 00700000 */
+                                               /* Channel_id 0: 0x40c81000 must be changed to 0x40c80000 for SPDIF AC3 input or output. */
+                                               /* Channel_id 1: 0xffffffff(mute) 0x30303030(max) controls CAPTURE feedback into PLAYBACK. */
+                                               /* Sample rate output control register Channel=0
+                                                * Sample output rate [1:0] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+                                                * Sample input rate [3:2] (0=48kHz, 1=Not available, 2=96kHz, 3=192Khz)
+                                                * SRC input source select [4] 0=Audio from digital mixer, 1=Audio from analog source.
+                                                * Record rate [9:8] (0=48kHz, 1=Not available, 2=96kHz, 3=192Khz)
+                                                * Record mixer output enable [12:10] 
+                                                * I2S input rate master mode [15:14] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+                                                * I2S output rate [17:16] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+                                                * I2S output source select [18] (0=Audio from host, 1=Audio from SRC)
+                                                * Record mixer I2S enable [20:19] (enable/disable i2sin1 and i2sin0)
+                                                * I2S output master clock select [21] (0=256*I2S output rate, 1=512*I2S output rate.)
+                                                * I2S input master clock select [22] (0=256*I2S input rate, 1=512*I2S input rate.)
+                                                * I2S input mode [23] (0=Slave, 1=Master)
+                                                * SPDIF output rate [25:24] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz)
+                                                * SPDIF output source select [26] (0=host, 1=SRC)
+                                                * Not used [27]
+                                                * Record Source 0 input [29:28] (0=SPDIF in, 1=I2S in, 2=AC97 Mic, 3=AC97 PCM)
+                                                * Record Source 1 input [31:30] (0=SPDIF in, 1=I2S in, 2=AC97 Mic, 3=AC97 PCM)
+                                                */ 
+                                               /* Sample rate output control register Channel=1
+                                                * I2S Input 0 volume Right [7:0]
+                                                * I2S Input 0 volume Left [15:8]
+                                                * I2S Input 1 volume Right [23:16]
+                                                * I2S Input 1 volume Left [31:24]
+                                                */
+                                               /* Sample rate output control register Channel=2
+                                                * SPDIF Input volume Right [23:16]
+                                                * SPDIF Input volume Left [31:24]
+                                                */
+                                               /* Sample rate output control register Channel=3
+                                                * No used
+                                                */
+#define SPDIF_SELECT2           0x72            /* Some sort of routing. Channel_id 0 only. default = 0x0f0f003f. Analog 0x000b0000, Digital 0x0b000000 */
+#define ROUTING2_FRONT_MASK     0x00010000      /* Enable for Front speakers. */
+#define ROUTING2_CENTER_LFE_MASK 0x00020000     /* Enable for Center/LFE speakers. */
+#define ROUTING2_REAR_MASK      0x00080000      /* Enable for Rear speakers. */
+                                               /* Audio output control
+                                                * AC97 output enable [5:0]
+                                                * I2S output enable [19:16]
+                                                * SPDIF output enable [27:24]
+                                                */ 
+#define UNKNOWN73               0x73            /* Unknown. Readonly. Default 0x0 */
+#define CHIP_VERSION            0x74            /* P17 Chip version. Channel_id 0 only. Default 00000071 */
+#define EXTENDED_INT_MASK       0x75            /* Used by both playback and capture interrupt handler */
+                                               /* Sets which Interrupts are enabled. */
+                                               /* 0x00000001 = Half period. Playback.
+                                                * 0x00000010 = Full period. Playback.
+                                                * 0x00000100 = Half buffer. Playback.
+                                                * 0x00001000 = Full buffer. Playback.
+                                                * 0x00010000 = Half buffer. Capture.
+                                                * 0x00100000 = Full buffer. Capture.
+                                                * Capture can only do 2 periods.
+                                                * 0x01000000 = End audio. Playback.
+                                                * 0x40000000 = Half buffer Playback,Caputre xrun.
+                                                * 0x80000000 = Full buffer Playback,Caputre xrun.
+                                                */
+#define EXTENDED_INT            0x76            /* Used by both playback and capture interrupt handler */
+                                               /* Shows which interrupts are active at the moment. */
+                                               /* Same bit layout as EXTENDED_INT_MASK */
+#define COUNTER77               0x77           /* Counter range 0 to 0x3fffff, 192000 counts per second. */
+#define COUNTER78               0x78           /* Counter range 0 to 0x3fffff, 44100 counts per second. */
+#define EXTENDED_INT_TIMER      0x79            /* Channel_id 0 only. Used by both playback and capture interrupt handler */
+                                               /* Causes interrupts based on timer intervals. */
+#define SPI                    0x7a            /* SPI: Serial Interface Register */
+#define I2C_A                  0x7b            /* I2C Address. 32 bit */
+#define I2C_0                  0x7c            /* I2C Data Port 0. 32 bit */
+#define I2C_1                  0x7d            /* I2C Data Port 1. 32 bit */
+
+
+#define SET_CHANNEL 0  /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
+#define PCM_FRONT_CHANNEL 0
+#define PCM_REAR_CHANNEL 1
+#define PCM_CENTER_LFE_CHANNEL 2
+#define PCM_UNKNOWN_CHANNEL 3
+#define CONTROL_FRONT_CHANNEL 0
+#define CONTROL_REAR_CHANNEL 3
+#define CONTROL_CENTER_LFE_CHANNEL 1
+#define CONTROL_UNKNOWN_CHANNEL 2
+
+typedef struct snd_ca0106_channel ca0106_channel_t;
+typedef struct snd_ca0106 ca0106_t;
+typedef struct snd_ca0106_pcm ca0106_pcm_t;
+
+struct snd_ca0106_channel {
+       ca0106_t *emu;
+       int number;
+       int use;
+       void (*interrupt)(ca0106_t *emu, ca0106_channel_t *channel);
+       ca0106_pcm_t *epcm;
+};
+
+struct snd_ca0106_pcm {
+       ca0106_t *emu;
+       snd_pcm_substream_t *substream;
+        int channel_id;
+       unsigned short running;
+};
+
+// definition of the chip-specific record
+struct snd_ca0106 {
+       snd_card_t *card;
+       struct pci_dev *pci;
+
+       unsigned long port;
+       struct resource *res_port;
+       int irq;
+
+       unsigned int revision;          /* chip revision */
+       unsigned int serial;            /* serial number */
+       unsigned short model;           /* subsystem id */
+
+       spinlock_t emu_lock;
+
+       ac97_t *ac97;
+       snd_pcm_t *pcm;
+
+       ca0106_channel_t playback_channels[4];
+       ca0106_channel_t capture_channels[4];
+       u32 spdif_bits[4];             /* s/pdif out setup */
+       int spdif_enable;
+       int capture_source;
+
+       struct snd_dma_buffer buffer;
+};
+
+int __devinit snd_ca0106_mixer(ca0106_t *emu);
+int __devinit snd_ca0106_proc_init(ca0106_t * emu);
+
+unsigned int snd_ca0106_ptr_read(ca0106_t * emu, 
+                                         unsigned int reg, 
+                                         unsigned int chn);
+
+void snd_ca0106_ptr_write(ca0106_t *emu, 
+                                  unsigned int reg, 
+                                  unsigned int chn, 
+                                  unsigned int data);
+
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
new file mode 100644 (file)
index 0000000..a6f3fa5
--- /dev/null
@@ -0,0 +1,1274 @@
+/*
+ *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ *  Version: 0.0.21
+ *
+ *  FEATURES currently supported:
+ *    Front, Rear and Center/LFE.
+ *    Surround40 and Surround51.
+ *    Capture from MIC an LINE IN input.
+ *    SPDIF digital playback of PCM stereo and AC3/DTS works.
+ *    (One can use a standard mono mini-jack to one RCA plugs cable.
+ *     or one can use a standard stereo mini-jack to two RCA plugs cable.
+ *     Plug one of the RCA plugs into the Coax input of the external decoder/receiver.)
+ *    ( In theory one could output 3 different AC3 streams at once, to 3 different SPDIF outputs. )
+ *    Notes on how to capture sound:
+ *      The AC97 is used in the PLAYBACK direction.
+ *      The output from the AC97 chip, instead of reaching the speakers, is fed into the Philips 1361T ADC.
+ *      So, to record from the MIC, set the MIC Playback volume to max,
+ *      unmute the MIC and turn up the MASTER Playback volume.
+ *      So, to prevent feedback when capturing, minimise the "Capture feedback into Playback" volume.
+ *   
+ *    The only playback controls that currently do anything are: -
+ *    Analog Front
+ *    Analog Rear
+ *    Analog Center/LFE
+ *    SPDIF Front
+ *    SPDIF Rear
+ *    SPDIF Center/LFE
+ *   
+ *    For capture from Mic in or Line in.
+ *    Digital/Analog ( switch must be in Analog mode for CAPTURE. )
+ * 
+ *    CAPTURE feedback into PLAYBACK
+ * 
+ *  Changelog:
+ *    Support interrupts per period.
+ *    Removed noise from Center/LFE channel when in Analog mode.
+ *    Rename and remove mixer controls.
+ *  0.0.6
+ *    Use separate card based DMA buffer for periods table list.
+ *  0.0.7
+ *    Change remove and rename ctrls into lists.
+ *  0.0.8
+ *    Try to fix capture sources.
+ *  0.0.9
+ *    Fix AC3 output.
+ *    Enable S32_LE format support.
+ *  0.0.10
+ *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ *  0.0.11
+ *    Add Model name recognition.
+ *  0.0.12
+ *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ *    Remove redundent "voice" handling.
+ *  0.0.13
+ *    Single trigger call for multi channels.
+ *  0.0.14
+ *    Set limits based on what the sound card hardware can do.
+ *    playback periods_min=2, periods_max=8
+ *    capture hw constraints require period_size = n * 64 bytes.
+ *    playback hw constraints require period_size = n * 64 bytes.
+ *  0.0.15
+ *    Minor updates.
+ *  0.0.16
+ *    Implement 192000 sample rate.
+ *  0.0.17
+ *    Add support for SB0410 and SB0413.
+ *  0.0.18
+ *    Modified Copyright message.
+ *  0.0.19
+ *    Finally fix support for SB Live 24 bit. SB0410 and SB0413.
+ *    The output codec needs resetting, otherwise all output is muted.
+ *  0.0.20
+ *    Merge "pci_disable_device(pci);" fixes.
+ *  0.0.21
+ *    Add 4 capture channels. (SPDIF only comes in on channel 0. )
+ *    Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.)
+ *
+ *  BUGS:
+ *    Some stability problems when unloading the snd-ca0106 kernel module.
+ *    --
+ *
+ *  TODO:
+ *    4 Capture channels, only one implemented so far.
+ *    Other capture rates apart from 48khz not implemented.
+ *    MIDI
+ *    --
+ *  GENERAL INFO:
+ *    Model: SB0310
+ *    P17 Chip: CA0106-DAT
+ *    AC97 Codec: STAC 9721
+ *    ADC: Philips 1361T (Stereo 24bit)
+ *    DAC: WM8746EDS (6-channel, 24bit, 192Khz)
+ *
+ *  GENERAL INFO:
+ *    Model: SB0410
+ *    P17 Chip: CA0106-DAT
+ *    AC97 Codec: None
+ *    ADC: WM8775EDS (4 Channel)
+ *    DAC: CS4382 (114 dB, 24-Bit, 192 kHz, 8-Channel D/A Converter with DSD Support)
+ *    SPDIF Out control switches between Mic in and SPDIF out.
+ *    No sound out or mic input working yet.
+ * 
+ *  GENERAL INFO:
+ *    Model: SB0413
+ *    P17 Chip: CA0106-DAT
+ *    AC97 Codec: None.
+ *    ADC: Unknown
+ *    DAC: Unknown
+ *    Trying to handle it like the SB0410.
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("James Courtier-Dutton <James@superbug.demon.co.uk>");
+MODULE_DESCRIPTION("CA0106");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Creative,SB CA0106 chip}}");
+
+// module parameters (see "Module Parameters")
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the CA0106 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the CA0106 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the CA0106 soundcard.");
+
+#include "ca0106.h"
+
+typedef struct {
+       u32 serial;
+       char * name;
+} ca0106_names_t;
+
+static ca0106_names_t ca0106_chip_names[] = {
+        { 0x10021102, "AudigyLS [SB0310]"} , 
+        { 0x10051102, "AudigyLS [SB0310b]"} , /* Unknown AudigyLS that also says SB0310 on it */
+        { 0x10061102, "Live! 7.1 24bit [SB0410]"} , /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
+        { 0x10071102, "Live! 7.1 24bit [SB0413]"} , /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
+        { 0, "AudigyLS [Unknown]" }
+};
+
+/* hardware definition */
+static snd_pcm_hardware_t snd_ca0106_playback_hw = {
+       .info =                 (SNDRV_PCM_INFO_MMAP | 
+                                SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =              SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+       .rates =                SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+       .rate_min =             48000,
+       .rate_max =             192000,
+       .channels_min =         2,  //1,
+       .channels_max =         2,  //6,
+       .buffer_bytes_max =     (32*1024),
+       .period_bytes_min =     64,
+       .period_bytes_max =     (16*1024),
+       .periods_min =          2,
+       .periods_max =          8,
+       .fifo_size =            0,
+};
+
+static snd_pcm_hardware_t snd_ca0106_capture_hw = {
+       .info =                 (SNDRV_PCM_INFO_MMAP | 
+                                SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =              SNDRV_PCM_FMTBIT_S16_LE,
+       .rates =                SNDRV_PCM_RATE_48000,
+       .rate_min =             48000,
+       .rate_max =             48000,
+       .channels_min =         2,
+       .channels_max =         2,
+       .buffer_bytes_max =     (32*1024),
+       .period_bytes_min =     64,
+       .period_bytes_max =     (16*1024),
+       .periods_min =          2,
+       .periods_max =          2,
+       .fifo_size =            0,
+};
+
+unsigned int snd_ca0106_ptr_read(ca0106_t * emu, 
+                                         unsigned int reg, 
+                                         unsigned int chn)
+{
+       unsigned long flags;
+       unsigned int regptr, val;
+  
+       regptr = (reg << 16) | chn;
+
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outl(regptr, emu->port + PTR);
+       val = inl(emu->port + DATA);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+       return val;
+}
+
+void snd_ca0106_ptr_write(ca0106_t *emu, 
+                                  unsigned int reg, 
+                                  unsigned int chn, 
+                                  unsigned int data)
+{
+       unsigned int regptr;
+       unsigned long flags;
+
+       regptr = (reg << 16) | chn;
+
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outl(regptr, emu->port + PTR);
+       outl(data, emu->port + DATA);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_ca0106_intr_enable(ca0106_t *emu, unsigned int intrenb)
+{
+       unsigned long flags;
+       unsigned int enable;
+  
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       enable = inl(emu->port + INTE) | intrenb;
+       outl(enable, emu->port + INTE);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_ca0106_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+       ca0106_pcm_t *epcm = runtime->private_data;
+  
+       if (epcm) {
+               kfree(epcm);
+       }
+}
+
+/* open_playback callback */
+static int snd_ca0106_pcm_open_playback_channel(snd_pcm_substream_t *substream, int channel_id)
+{
+       ca0106_t *chip = snd_pcm_substream_chip(substream);
+        ca0106_channel_t *channel = &(chip->playback_channels[channel_id]);
+       ca0106_pcm_t *epcm;
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       int err;
+
+       epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+
+       if (epcm == NULL)
+               return -ENOMEM;
+       epcm->emu = chip;
+       epcm->substream = substream;
+        epcm->channel_id=channel_id;
+  
+       runtime->private_data = epcm;
+       runtime->private_free = snd_ca0106_pcm_free_substream;
+  
+       runtime->hw = snd_ca0106_playback_hw;
+
+        channel->emu = chip;
+        channel->number = channel_id;
+
+        channel->use=1;
+        //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+        //channel->interrupt = snd_ca0106_pcm_channel_interrupt;
+        channel->epcm=epcm;
+       if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+                return err;
+       if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+                return err;
+       return 0;
+}
+
+/* close callback */
+static int snd_ca0106_pcm_close_playback(snd_pcm_substream_t *substream)
+{
+       ca0106_t *chip = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+        ca0106_pcm_t *epcm = runtime->private_data;
+        chip->playback_channels[epcm->channel_id].use=0;
+/* FIXME: maybe zero others */
+       return 0;
+}
+
+static int snd_ca0106_pcm_open_playback_front(snd_pcm_substream_t *substream)
+{
+       return snd_ca0106_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_center_lfe(snd_pcm_substream_t *substream)
+{
+       return snd_ca0106_pcm_open_playback_channel(substream, PCM_CENTER_LFE_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_unknown(snd_pcm_substream_t *substream)
+{
+       return snd_ca0106_pcm_open_playback_channel(substream, PCM_UNKNOWN_CHANNEL);
+}
+
+static int snd_ca0106_pcm_open_playback_rear(snd_pcm_substream_t *substream)
+{
+       return snd_ca0106_pcm_open_playback_channel(substream, PCM_REAR_CHANNEL);
+}
+
+/* open_capture callback */
+static int snd_ca0106_pcm_open_capture_channel(snd_pcm_substream_t *substream, int channel_id)
+{
+       ca0106_t *chip = snd_pcm_substream_chip(substream);
+        ca0106_channel_t *channel = &(chip->capture_channels[channel_id]);
+       ca0106_pcm_t *epcm;
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       int err;
+
+       epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+       if (epcm == NULL) {
+                snd_printk("open_capture_channel: failed epcm alloc\n");
+               return -ENOMEM;
+        }
+       epcm->emu = chip;
+       epcm->substream = substream;
+        epcm->channel_id=channel_id;
+  
+       runtime->private_data = epcm;
+       runtime->private_free = snd_ca0106_pcm_free_substream;
+  
+       runtime->hw = snd_ca0106_capture_hw;
+
+        channel->emu = chip;
+        channel->number = channel_id;
+
+        channel->use=1;
+        //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+        //channel->interrupt = snd_ca0106_pcm_channel_interrupt;
+        channel->epcm=epcm;
+       if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+                return err;
+       //snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes);
+       if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+                return err;
+       return 0;
+}
+
+/* close callback */
+static int snd_ca0106_pcm_close_capture(snd_pcm_substream_t *substream)
+{
+       ca0106_t *chip = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+        ca0106_pcm_t *epcm = runtime->private_data;
+        chip->capture_channels[epcm->channel_id].use=0;
+/* FIXME: maybe zero others */
+       return 0;
+}
+
+static int snd_ca0106_pcm_open_0_capture(snd_pcm_substream_t *substream)
+{
+       return snd_ca0106_pcm_open_capture_channel(substream, 0);
+}
+
+static int snd_ca0106_pcm_open_1_capture(snd_pcm_substream_t *substream)
+{
+       return snd_ca0106_pcm_open_capture_channel(substream, 1);
+}
+
+static int snd_ca0106_pcm_open_2_capture(snd_pcm_substream_t *substream)
+{
+       return snd_ca0106_pcm_open_capture_channel(substream, 2);
+}
+
+static int snd_ca0106_pcm_open_3_capture(snd_pcm_substream_t *substream)
+{
+       return snd_ca0106_pcm_open_capture_channel(substream, 3);
+}
+
+/* hw_params callback */
+static int snd_ca0106_pcm_hw_params_playback(snd_pcm_substream_t *substream,
+                                     snd_pcm_hw_params_t * hw_params)
+{
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_ca0106_pcm_hw_free_playback(snd_pcm_substream_t *substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+/* hw_params callback */
+static int snd_ca0106_pcm_hw_params_capture(snd_pcm_substream_t *substream,
+                                     snd_pcm_hw_params_t * hw_params)
+{
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_ca0106_pcm_hw_free_capture(snd_pcm_substream_t *substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare playback callback */
+static int snd_ca0106_pcm_prepare_playback(snd_pcm_substream_t *substream)
+{
+       ca0106_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       ca0106_pcm_t *epcm = runtime->private_data;
+       int channel = epcm->channel_id;
+       u32 *table_base = (u32 *)(emu->buffer.area+(8*16*channel));
+       u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+       u32 hcfg_mask = HCFG_PLAYBACK_S32_LE;
+       u32 hcfg_set = 0x00000000;
+       u32 hcfg;
+       u32 reg40_mask = 0x30000 << (channel<<1);
+       u32 reg40_set = 0;
+       u32 reg40;
+       /* FIXME: Depending on mixer selection of SPDIF out or not, select the spdif rate or the DAC rate. */
+       u32 reg71_mask = 0x03030000 ; /* Global. Set SPDIF rate. We only support 44100 to spdif, not to DAC. */
+       u32 reg71_set = 0;
+       u32 reg71;
+       int i;
+       
+        //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
+        //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
+       //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+       /* Rate can be set per channel. */
+       /* reg40 control host to fifo */
+       /* reg71 controls DAC rate. */
+       switch (runtime->rate) {
+       case 44100:
+               reg40_set = 0x10000 << (channel<<1);
+               reg71_set = 0x01010000; 
+               break;
+        case 48000:
+               reg40_set = 0;
+               reg71_set = 0; 
+               break;
+       case 96000:
+               reg40_set = 0x20000 << (channel<<1);
+               reg71_set = 0x02020000; 
+               break;
+       case 192000:
+               reg40_set = 0x30000 << (channel<<1);
+               reg71_set = 0x03030000; 
+               break;
+       default:
+               reg40_set = 0;
+               reg71_set = 0; 
+               break;
+       }
+       /* Format is a global setting */
+       /* FIXME: Only let the first channel accessed set this. */
+       switch (runtime->format) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               hcfg_set = 0;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               hcfg_set = HCFG_PLAYBACK_S32_LE;
+               break;
+       default:
+               hcfg_set = 0;
+               break;
+       }
+       hcfg = inl(emu->port + HCFG) ;
+       hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
+       outl(hcfg, emu->port + HCFG);
+       reg40 = snd_ca0106_ptr_read(emu, 0x40, 0);
+       reg40 = (reg40 & ~reg40_mask) | reg40_set;
+       snd_ca0106_ptr_write(emu, 0x40, 0, reg40);
+       reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
+       reg71 = (reg71 & ~reg71_mask) | reg71_set;
+       snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
+
+       /* FIXME: Check emu->buffer.size before actually writing to it. */
+        for(i=0; i < runtime->periods; i++) {
+               table_base[i*2]=runtime->dma_addr+(i*period_size_bytes);
+               table_base[(i*2)+1]=period_size_bytes<<16;
+       }
+       snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer.addr+(8*16*channel));
+       snd_ca0106_ptr_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
+       snd_ca0106_ptr_write(emu, PLAYBACK_LIST_PTR, channel, 0);
+       snd_ca0106_ptr_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
+       snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+       snd_ca0106_ptr_write(emu, PLAYBACK_POINTER, channel, 0);
+       snd_ca0106_ptr_write(emu, 0x07, channel, 0x0);
+       snd_ca0106_ptr_write(emu, 0x08, channel, 0);
+        snd_ca0106_ptr_write(emu, PLAYBACK_MUTE, 0x0, 0x0); /* Unmute output */
+#if 0
+       snd_ca0106_ptr_write(emu, SPCS0, 0,
+                              SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                              SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                              SPCS_GENERATIONSTATUS | 0x00001200 |
+                              0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT );
+       }
+#endif
+
+       return 0;
+}
+
+/* prepare capture callback */
+static int snd_ca0106_pcm_prepare_capture(snd_pcm_substream_t *substream)
+{
+       ca0106_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       ca0106_pcm_t *epcm = runtime->private_data;
+       int channel = epcm->channel_id;
+        //printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size,  frames_to_bytes(runtime, 1));
+       snd_ca0106_ptr_write(emu, 0x13, channel, 0);
+       snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
+       snd_ca0106_ptr_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+       snd_ca0106_ptr_write(emu, CAPTURE_POINTER, channel, 0);
+
+       return 0;
+}
+
+/* trigger_playback callback */
+static int snd_ca0106_pcm_trigger_playback(snd_pcm_substream_t *substream,
+                                   int cmd)
+{
+       ca0106_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime;
+       ca0106_pcm_t *epcm;
+       int channel;
+       int result = 0;
+       struct list_head *pos;
+        snd_pcm_substream_t *s;
+       u32 basic = 0;
+       u32 extended = 0;
+       int running=0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               running=1;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       default:
+               running=0;
+               break;
+       }
+        snd_pcm_group_for_each(pos, substream) {
+                s = snd_pcm_group_substream_entry(pos);
+               runtime = s->runtime;
+               epcm = runtime->private_data;
+               channel = epcm->channel_id;
+               //snd_printk("channel=%d\n",channel);
+               epcm->running = running;
+               basic |= (0x1<<channel);
+               extended |= (0x10<<channel);
+                snd_pcm_trigger_done(s, substream);
+        }
+       //snd_printk("basic=0x%x, extended=0x%x\n",basic, extended);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (extended));
+               snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(basic));
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
+               snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(extended));
+               break;
+       default:
+               result = -EINVAL;
+               break;
+       }
+       return result;
+}
+
+/* trigger_capture callback */
+static int snd_ca0106_pcm_trigger_capture(snd_pcm_substream_t *substream,
+                                   int cmd)
+{
+       ca0106_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       ca0106_pcm_t *epcm = runtime->private_data;
+       int channel = epcm->channel_id;
+       int result = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
+               snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
+               epcm->running = 1;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
+               snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
+               epcm->running = 0;
+               break;
+       default:
+               result = -EINVAL;
+               break;
+       }
+       return result;
+}
+
+/* pointer_playback callback */
+static snd_pcm_uframes_t
+snd_ca0106_pcm_pointer_playback(snd_pcm_substream_t *substream)
+{
+       ca0106_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       ca0106_pcm_t *epcm = runtime->private_data;
+       snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+       int channel = epcm->channel_id;
+
+       if (!epcm->running)
+               return 0;
+
+       ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+       ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
+       ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+       if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel);
+       ptr2 = bytes_to_frames(runtime, ptr1);
+       ptr2+= (ptr4 >> 3) * runtime->period_size;
+       ptr=ptr2;
+        if (ptr >= runtime->buffer_size)
+               ptr -= runtime->buffer_size;
+       //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+       return ptr;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_ca0106_pcm_pointer_capture(snd_pcm_substream_t *substream)
+{
+       ca0106_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       ca0106_pcm_t *epcm = runtime->private_data;
+       snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
+       int channel = channel=epcm->channel_id;
+
+       if (!epcm->running)
+               return 0;
+
+       ptr1 = snd_ca0106_ptr_read(emu, CAPTURE_POINTER, channel);
+       ptr2 = bytes_to_frames(runtime, ptr1);
+       ptr=ptr2;
+        if (ptr >= runtime->buffer_size)
+               ptr -= runtime->buffer_size;
+       //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+       return ptr;
+}
+
+/* operators */
+static snd_pcm_ops_t snd_ca0106_playback_front_ops = {
+       .open =        snd_ca0106_pcm_open_playback_front,
+       .close =       snd_ca0106_pcm_close_playback,
+       .ioctl =       snd_pcm_lib_ioctl,
+       .hw_params =   snd_ca0106_pcm_hw_params_playback,
+       .hw_free =     snd_ca0106_pcm_hw_free_playback,
+       .prepare =     snd_ca0106_pcm_prepare_playback,
+       .trigger =     snd_ca0106_pcm_trigger_playback,
+       .pointer =     snd_ca0106_pcm_pointer_playback,
+};
+
+static snd_pcm_ops_t snd_ca0106_capture_0_ops = {
+       .open =        snd_ca0106_pcm_open_0_capture,
+       .close =       snd_ca0106_pcm_close_capture,
+       .ioctl =       snd_pcm_lib_ioctl,
+       .hw_params =   snd_ca0106_pcm_hw_params_capture,
+       .hw_free =     snd_ca0106_pcm_hw_free_capture,
+       .prepare =     snd_ca0106_pcm_prepare_capture,
+       .trigger =     snd_ca0106_pcm_trigger_capture,
+       .pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static snd_pcm_ops_t snd_ca0106_capture_1_ops = {
+       .open =        snd_ca0106_pcm_open_1_capture,
+       .close =       snd_ca0106_pcm_close_capture,
+       .ioctl =       snd_pcm_lib_ioctl,
+       .hw_params =   snd_ca0106_pcm_hw_params_capture,
+       .hw_free =     snd_ca0106_pcm_hw_free_capture,
+       .prepare =     snd_ca0106_pcm_prepare_capture,
+       .trigger =     snd_ca0106_pcm_trigger_capture,
+       .pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static snd_pcm_ops_t snd_ca0106_capture_2_ops = {
+       .open =        snd_ca0106_pcm_open_2_capture,
+       .close =       snd_ca0106_pcm_close_capture,
+       .ioctl =       snd_pcm_lib_ioctl,
+       .hw_params =   snd_ca0106_pcm_hw_params_capture,
+       .hw_free =     snd_ca0106_pcm_hw_free_capture,
+       .prepare =     snd_ca0106_pcm_prepare_capture,
+       .trigger =     snd_ca0106_pcm_trigger_capture,
+       .pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static snd_pcm_ops_t snd_ca0106_capture_3_ops = {
+       .open =        snd_ca0106_pcm_open_3_capture,
+       .close =       snd_ca0106_pcm_close_capture,
+       .ioctl =       snd_pcm_lib_ioctl,
+       .hw_params =   snd_ca0106_pcm_hw_params_capture,
+       .hw_free =     snd_ca0106_pcm_hw_free_capture,
+       .prepare =     snd_ca0106_pcm_prepare_capture,
+       .trigger =     snd_ca0106_pcm_trigger_capture,
+       .pointer =     snd_ca0106_pcm_pointer_capture,
+};
+
+static snd_pcm_ops_t snd_ca0106_playback_center_lfe_ops = {
+        .open =         snd_ca0106_pcm_open_playback_center_lfe,
+        .close =        snd_ca0106_pcm_close_playback,
+        .ioctl =        snd_pcm_lib_ioctl,
+        .hw_params =    snd_ca0106_pcm_hw_params_playback,
+        .hw_free =      snd_ca0106_pcm_hw_free_playback,
+        .prepare =      snd_ca0106_pcm_prepare_playback,     
+        .trigger =      snd_ca0106_pcm_trigger_playback,  
+        .pointer =      snd_ca0106_pcm_pointer_playback, 
+};
+
+static snd_pcm_ops_t snd_ca0106_playback_unknown_ops = {
+        .open =         snd_ca0106_pcm_open_playback_unknown,
+        .close =        snd_ca0106_pcm_close_playback,
+        .ioctl =        snd_pcm_lib_ioctl,
+        .hw_params =    snd_ca0106_pcm_hw_params_playback,
+        .hw_free =      snd_ca0106_pcm_hw_free_playback,
+        .prepare =      snd_ca0106_pcm_prepare_playback,     
+        .trigger =      snd_ca0106_pcm_trigger_playback,  
+        .pointer =      snd_ca0106_pcm_pointer_playback, 
+};
+
+static snd_pcm_ops_t snd_ca0106_playback_rear_ops = {
+        .open =         snd_ca0106_pcm_open_playback_rear,
+        .close =        snd_ca0106_pcm_close_playback,
+        .ioctl =        snd_pcm_lib_ioctl,
+        .hw_params =    snd_ca0106_pcm_hw_params_playback,
+               .hw_free =      snd_ca0106_pcm_hw_free_playback,
+        .prepare =      snd_ca0106_pcm_prepare_playback,     
+        .trigger =      snd_ca0106_pcm_trigger_playback,  
+        .pointer =      snd_ca0106_pcm_pointer_playback, 
+};
+
+
+static unsigned short snd_ca0106_ac97_read(ac97_t *ac97,
+                                            unsigned short reg)
+{
+       ca0106_t *emu = ac97->private_data;
+       unsigned long flags;
+       unsigned short val;
+
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outb(reg, emu->port + AC97ADDRESS);
+       val = inw(emu->port + AC97DATA);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+       return val;
+}
+
+static void snd_ca0106_ac97_write(ac97_t *ac97,
+                                   unsigned short reg, unsigned short val)
+{
+       ca0106_t *emu = ac97->private_data;
+       unsigned long flags;
+  
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outb(reg, emu->port + AC97ADDRESS);
+       outw(val, emu->port + AC97DATA);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static int snd_ca0106_ac97(ca0106_t *chip)
+{
+       ac97_bus_t *pbus;
+       ac97_template_t ac97;
+       int err;
+       static ac97_bus_ops_t ops = {
+               .write = snd_ca0106_ac97_write,
+               .read = snd_ca0106_ac97_read,
+       };
+  
+       if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+               return err;
+       pbus->no_vra = 1; /* we don't need VRA */
+
+       memset(&ac97, 0, sizeof(ac97));
+       ac97.private_data = chip;
+       return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+}
+
+static int snd_ca0106_free(ca0106_t *chip)
+{
+       if (chip->res_port != NULL) {    /* avoid access to already used hardware */
+               // disable interrupts
+               snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
+               outl(0, chip->port + INTE);
+               snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
+               udelay(1000);
+               // disable audio
+               //outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
+               outl(0, chip->port + HCFG);
+               /* FIXME: We need to stop and DMA transfers here.
+                *        But as I am not sure how yet, we cannot from the dma pages.
+                * So we can fix: snd-malloc: Memory leak?  pages not freed = 8
+                */
+       }
+       // release the data
+#if 1
+       if (chip->buffer.area)
+               snd_dma_free_pages(&chip->buffer);
+#endif
+
+       // release the i/o port
+       if (chip->res_port) {
+               release_resource(chip->res_port);
+               kfree_nocheck(chip->res_port);
+       }
+       // release the irq
+       if (chip->irq >= 0)
+               free_irq(chip->irq, (void *)chip);
+       pci_disable_device(chip->pci);
+       kfree(chip);
+       return 0;
+}
+
+static int snd_ca0106_dev_free(snd_device_t *device)
+{
+       ca0106_t *chip = device->device_data;
+       return snd_ca0106_free(chip);
+}
+
+static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id,
+                                         struct pt_regs *regs)
+{
+       unsigned int status;
+
+       ca0106_t *chip = dev_id;
+       int i;
+       int mask;
+        unsigned int stat76;
+       ca0106_channel_t *pchannel;
+
+       spin_lock(&chip->emu_lock);
+
+       status = inl(chip->port + IPR);
+
+       // call updater, unlock before it
+       spin_unlock(&chip->emu_lock);
+  
+       if (! status)
+               return IRQ_NONE;
+
+        stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0);
+       //snd_printk("interrupt status = 0x%08x, stat76=0x%08x\n", status, stat76);
+       //snd_printk("ptr=0x%08x\n",snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0));
+        mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */
+       for(i = 0; i < 4; i++) {
+               pchannel = &(chip->playback_channels[i]);
+               if(stat76 & mask) {
+/* FIXME: Select the correct substream for period elapsed */
+                       if(pchannel->use) {
+                          snd_pcm_period_elapsed(pchannel->epcm->substream);
+                       //printk(KERN_INFO "interrupt [%d] used\n", i);
+                        }
+               }
+               //printk(KERN_INFO "channel=%p\n",pchannel);
+               //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+               mask <<= 1;
+       }
+        mask = 0x110000; /* 0x1 for one half, 0x10 for the other half period. */
+       for(i = 0; i < 4; i++) {
+               pchannel = &(chip->capture_channels[i]);
+               if(stat76 & mask) {
+/* FIXME: Select the correct substream for period elapsed */
+                       if(pchannel->use) {
+                          snd_pcm_period_elapsed(pchannel->epcm->substream);
+                       //printk(KERN_INFO "interrupt [%d] used\n", i);
+                        }
+               }
+               //printk(KERN_INFO "channel=%p\n",pchannel);
+               //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+               mask <<= 1;
+       }
+
+        snd_ca0106_ptr_write(chip, EXTENDED_INT, 0, stat76);
+       spin_lock(&chip->emu_lock);
+       // acknowledge the interrupt if necessary
+       outl(status, chip->port+IPR);
+
+       spin_unlock(&chip->emu_lock);
+
+       return IRQ_HANDLED;
+}
+
+static void snd_ca0106_pcm_free(snd_pcm_t *pcm)
+{
+       ca0106_t *emu = pcm->private_data;
+       emu->pcm = NULL;
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ca0106_pcm(ca0106_t *emu, int device, snd_pcm_t **rpcm)
+{
+       snd_pcm_t *pcm;
+       snd_pcm_substream_t *substream;
+       int err;
+  
+       if (rpcm)
+               *rpcm = NULL;
+       if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0)
+               return err;
+  
+       pcm->private_data = emu;
+       pcm->private_free = snd_ca0106_pcm_free;
+
+       switch (device) {
+       case 0:
+         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops);
+         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops);
+          break;
+       case 1:
+         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops);
+         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops);
+          break;
+       case 2:
+         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops);
+         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops);
+          break;
+       case 3:
+         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops);
+         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops);
+          break;
+        }
+
+       pcm->info_flags = 0;
+       pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+       strcpy(pcm->name, "CA0106");
+       emu->pcm = pcm;
+
+       for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 
+           substream; 
+           substream = substream->next) {
+               if ((err = snd_pcm_lib_preallocate_pages(substream, 
+                                                        SNDRV_DMA_TYPE_DEV, 
+                                                        snd_dma_pci_data(emu->pci), 
+                                                        64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */
+                       return err;
+       }
+
+       for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 
+             substream; 
+             substream = substream->next) {
+               if ((err = snd_pcm_lib_preallocate_pages(substream, 
+                                                  SNDRV_DMA_TYPE_DEV, 
+                                                  snd_dma_pci_data(emu->pci), 
+                                                  64*1024, 64*1024)) < 0)
+                       return err;
+       }
+  
+       if (rpcm)
+               *rpcm = pcm;
+  
+       return 0;
+}
+
+static int __devinit snd_ca0106_create(snd_card_t *card,
+                                        struct pci_dev *pci,
+                                        ca0106_t **rchip)
+{
+       ca0106_t *chip;
+       int err;
+       int ch;
+       static snd_device_ops_t ops = {
+               .dev_free = snd_ca0106_dev_free,
+       };
+  
+       *rchip = NULL;
+  
+       if ((err = pci_enable_device(pci)) < 0)
+               return err;
+       if (pci_set_dma_mask(pci, 0xffffffffUL) < 0 ||
+           pci_set_consistent_dma_mask(pci, 0xffffffffUL) < 0) {
+               printk(KERN_ERR "error to set 32bit mask DMA\n");
+               pci_disable_device(pci);
+               return -ENXIO;
+       }
+  
+       chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+       if (chip == NULL) {
+               pci_disable_device(pci);
+               return -ENOMEM;
+       }
+  
+       chip->card = card;
+       chip->pci = pci;
+       chip->irq = -1;
+
+       spin_lock_init(&chip->emu_lock);
+  
+       chip->port = pci_resource_start(pci, 0);
+       if ((chip->res_port = request_region(chip->port, 0x20,
+                                            "snd_ca0106")) == NULL) { 
+               snd_ca0106_free(chip);
+               printk(KERN_ERR "cannot allocate the port\n");
+               return -EBUSY;
+       }
+
+       if (request_irq(pci->irq, snd_ca0106_interrupt,
+                       SA_INTERRUPT|SA_SHIRQ, "snd_ca0106",
+                       (void *)chip)) {
+               snd_ca0106_free(chip);
+               printk(KERN_ERR "cannot grab irq\n");
+               return -EBUSY;
+       }
+       chip->irq = pci->irq;
+  
+       /* This stores the periods table. */ 
+       if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &chip->buffer) < 0) {
+               snd_ca0106_free(chip);
+               return -ENOMEM;
+       }
+
+       pci_set_master(pci);
+       /* read revision & serial */
+       pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&chip->revision);
+       pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
+       pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
+#if 1
+       printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
+              chip->revision, chip->serial);
+#endif
+
+       outl(0, chip->port + INTE);
+
+       /*
+        *  Init to 0x02109204 :
+        *  Clock accuracy    = 0     (1000ppm)
+        *  Sample Rate       = 2     (48kHz)
+        *  Audio Channel     = 1     (Left of 2)
+        *  Source Number     = 0     (Unspecified)
+        *  Generation Status = 1     (Original for Cat Code 12)
+        *  Cat Code          = 12    (Digital Signal Mixer)
+        *  Mode              = 0     (Mode 0)
+        *  Emphasis          = 0     (None)
+        *  CP                = 1     (Copyright unasserted)
+        *  AN                = 0     (Audio data)
+        *  P                 = 0     (Consumer)
+        */
+       snd_ca0106_ptr_write(chip, SPCS0, 0,
+                               chip->spdif_bits[0] =
+                               SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                               SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                               SPCS_GENERATIONSTATUS | 0x00001200 |
+                               0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+       /* Only SPCS1 has been tested */
+       snd_ca0106_ptr_write(chip, SPCS1, 0,
+                               chip->spdif_bits[1] =
+                               SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                               SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                               SPCS_GENERATIONSTATUS | 0x00001200 |
+                               0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+       snd_ca0106_ptr_write(chip, SPCS2, 0,
+                               chip->spdif_bits[2] =
+                               SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                               SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                               SPCS_GENERATIONSTATUS | 0x00001200 |
+                               0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+       snd_ca0106_ptr_write(chip, SPCS3, 0,
+                               chip->spdif_bits[3] =
+                               SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                               SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                               SPCS_GENERATIONSTATUS | 0x00001200 |
+                               0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+
+        snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000);
+        snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
+
+        /* Write 0x8000 to AC97_REC_GAIN to mute it. */
+        outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
+        outw(0x8000, chip->port + AC97DATA);
+#if 0
+       snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
+       snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
+       snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006);
+       snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006);
+#endif
+
+       //snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
+       /* Analog or Digital output */
+       snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
+       snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000b0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers */
+       chip->spdif_enable = 0; /* Set digital SPDIF output off */
+       chip->capture_source = 3; /* Set CAPTURE_SOURCE */
+       //snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
+       //snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */
+
+       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); /* goes to 0x40c80000 when doing SPDIF IN/OUT */
+       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); /* (Mute) CAPTURE feedback into PLAYBACK volume. Only lower 16 bits matter. */
+       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); /* SPDIF IN Volume */
+       snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */
+       snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410);
+       snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676);
+       snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410);
+       snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676);
+       for(ch = 0; ch < 4; ch++) {
+               snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); /* Only high 16 bits matter */
+               snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030);
+               //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); /* Mute */
+               //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); /* Mute */
+               snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */
+               snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */
+       }
+        snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */
+       chip->capture_source = 3; /* Set CAPTURE_SOURCE */
+
+        if ((chip->serial == 0x10061102) || (chip->serial == 0x10071102) ) { /* The SB0410 and SB0413 use GPIO differently. */
+               /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
+               outl(0x0, chip->port+GPIO);
+               //outl(0x00f0e000, chip->port+GPIO); /* Analog */
+               outl(0x005f4300, chip->port+GPIO); /* Analog */
+       } else {
+               outl(0x0, chip->port+GPIO);
+               outl(0x005f03a3, chip->port+GPIO); /* Analog */
+               //outl(0x005f02a2, chip->port+GPIO);   /* SPDIF */
+       }
+       snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
+
+       //outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
+       //outl(0x00001409, chip->port+HCFG); /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
+       //outl(0x00000009, chip->port+HCFG);
+       outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
+
+       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
+                                 chip, &ops)) < 0) {
+               snd_ca0106_free(chip);
+               return err;
+       }
+       *rchip = chip;
+       return 0;
+}
+
+static int __devinit snd_ca0106_probe(struct pci_dev *pci,
+                                       const struct pci_device_id *pci_id)
+{
+       static int dev;
+       snd_card_t *card;
+       ca0106_t *chip;
+       ca0106_names_t *c;
+       int err;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+       if (card == NULL)
+               return -ENOMEM;
+
+       if ((err = snd_ca0106_create(card, pci, &chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       if ((err = snd_ca0106_pcm(chip, 0, NULL)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       if ((err = snd_ca0106_pcm(chip, 1, NULL)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       if ((err = snd_ca0106_pcm(chip, 2, NULL)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       if ((err = snd_ca0106_pcm(chip, 3, NULL)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+        if ((chip->serial != 0x10061102) && (chip->serial != 0x10071102) ) { /* The SB0410 and SB0413 do not have an ac97 chip. */
+               if ((err = snd_ca0106_ac97(chip)) < 0) {
+                       snd_card_free(card);
+                       return err;
+               }
+       }
+       if ((err = snd_ca0106_mixer(chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       snd_ca0106_proc_init(chip);
+
+       strcpy(card->driver, "CA0106");
+       strcpy(card->shortname, "CA0106");
+
+       for (c=ca0106_chip_names; c->serial; c++) {
+               if (c->serial == chip->serial) break;
+       }
+       sprintf(card->longname, "%s at 0x%lx irq %i",
+               c->name, chip->port, chip->irq);
+
+       if ((err = snd_card_register(card)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       pci_set_drvdata(pci, card);
+       dev++;
+       return 0;
+}
+
+static void __devexit snd_ca0106_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+
+// PCI IDs
+static struct pci_device_id snd_ca0106_ids[] = {
+       { 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },    /* Audigy LS or Live 24bit */
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
+
+// pci_driver definition
+static struct pci_driver driver = {
+       .name = "CA0106",
+       .id_table = snd_ca0106_ids,
+       .probe = snd_ca0106_probe,
+       .remove = __devexit_p(snd_ca0106_remove),
+};
+
+// initialization of the module
+static int __init alsa_card_ca0106_init(void)
+{
+       int err;
+
+       if ((err = pci_module_init(&driver)) > 0)
+               return err;
+
+       return 0;
+}
+
+// clean up the module
+static void __exit alsa_card_ca0106_exit(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ca0106_init)
+module_exit(alsa_card_ca0106_exit)
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
new file mode 100644 (file)
index 0000000..97bed1b
--- /dev/null
@@ -0,0 +1,634 @@
+/*
+ *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ *  Version: 0.0.16
+ *
+ *  FEATURES currently supported:
+ *    See ca0106_main.c for features.
+ * 
+ *  Changelog:
+ *    Support interrupts per period.
+ *    Removed noise from Center/LFE channel when in Analog mode.
+ *    Rename and remove mixer controls.
+ *  0.0.6
+ *    Use separate card based DMA buffer for periods table list.
+ *  0.0.7
+ *    Change remove and rename ctrls into lists.
+ *  0.0.8
+ *    Try to fix capture sources.
+ *  0.0.9
+ *    Fix AC3 output.
+ *    Enable S32_LE format support.
+ *  0.0.10
+ *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ *  0.0.11
+ *    Add Model name recognition.
+ *  0.0.12
+ *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ *    Remove redundent "voice" handling.
+ *  0.0.13
+ *    Single trigger call for multi channels.
+ *  0.0.14
+ *    Set limits based on what the sound card hardware can do.
+ *    playback periods_min=2, periods_max=8
+ *    capture hw constraints require period_size = n * 64 bytes.
+ *    playback hw constraints require period_size = n * 64 bytes.
+ *  0.0.15
+ *    Separated ca0106.c into separate functional .c files.
+ *  0.0.16
+ *    Modified Copyright message.
+ *
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+
+#include "ca0106.h"
+
+static int snd_ca0106_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int snd_ca0106_shared_spdif_get(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = emu->spdif_enable;
+       return 0;
+}
+
+static int snd_ca0106_shared_spdif_put(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int val;
+       int change = 0;
+       u32 mask;
+
+       val = ucontrol->value.enumerated.item[0] ;
+       change = (emu->spdif_enable != val);
+       if (change) {
+               emu->spdif_enable = val;
+               if (val == 1) {
+                       /* Digital */
+                       snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
+                       snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
+                       snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
+                               snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
+                       mask = inl(emu->port + GPIO) & ~0x101;
+                       outl(mask, emu->port + GPIO);
+
+               } else {
+                       /* Analog */
+                       snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
+                       snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000b0000);
+                       snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
+                               snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
+                       mask = inl(emu->port + GPIO) | 0x101;
+                       outl(mask, emu->port + GPIO);
+               }
+       }
+        return change;
+}
+
+static snd_kcontrol_new_t snd_ca0106_shared_spdif __devinitdata =
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name =         "SPDIF Out",
+       .info =         snd_ca0106_shared_spdif_info,
+       .get =          snd_ca0106_shared_spdif_get,
+       .put =          snd_ca0106_shared_spdif_put
+};
+
+static int snd_ca0106_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[6] = { "SPDIF out", "i2s mixer out", "SPDIF in", "i2s in", "AC97 in", "SRC out" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 6;
+       if (uinfo->value.enumerated.item > 5)
+                uinfo->value.enumerated.item = 5;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_ca0106_capture_source_get(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = emu->capture_source;
+       return 0;
+}
+
+static int snd_ca0106_capture_source_put(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int val;
+       int change = 0;
+       u32 mask;
+       u32 source;
+
+       val = ucontrol->value.enumerated.item[0] ;
+       change = (emu->capture_source != val);
+       if (change) {
+               emu->capture_source = val;
+               source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
+               mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
+               snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
+       }
+        return change;
+}
+
+static snd_kcontrol_new_t snd_ca0106_capture_source __devinitdata =
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name =         "Capture Source",
+       .info =         snd_ca0106_capture_source_info,
+       .get =          snd_ca0106_capture_source_get,
+       .put =          snd_ca0106_capture_source_put
+};
+
+static int snd_ca0106_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+       uinfo->count = 1;
+       return 0;
+}
+
+static int snd_ca0106_spdif_get(snd_kcontrol_t * kcontrol,
+                                 snd_ctl_elem_value_t * ucontrol)
+{
+       ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+       ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+       ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+       ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+       ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+        return 0;
+}
+
+static int snd_ca0106_spdif_get_mask(snd_kcontrol_t * kcontrol,
+                                     snd_ctl_elem_value_t * ucontrol)
+{
+       ucontrol->value.iec958.status[0] = 0xff;
+       ucontrol->value.iec958.status[1] = 0xff;
+       ucontrol->value.iec958.status[2] = 0xff;
+       ucontrol->value.iec958.status[3] = 0xff;
+        return 0;
+}
+
+static int snd_ca0106_spdif_put(snd_kcontrol_t * kcontrol,
+                                 snd_ctl_elem_value_t * ucontrol)
+{
+       ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       int change;
+       unsigned int val;
+
+       val = (ucontrol->value.iec958.status[0] << 0) |
+             (ucontrol->value.iec958.status[1] << 8) |
+             (ucontrol->value.iec958.status[2] << 16) |
+             (ucontrol->value.iec958.status[3] << 24);
+       change = val != emu->spdif_bits[idx];
+       if (change) {
+               snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
+               emu->spdif_bits[idx] = val;
+       }
+        return change;
+}
+
+static snd_kcontrol_new_t snd_ca0106_spdif_mask_control =
+{
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ,
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+       .count =        4,
+        .info =         snd_ca0106_spdif_info,
+        .get =          snd_ca0106_spdif_get_mask
+};
+
+static snd_kcontrol_new_t snd_ca0106_spdif_control =
+{
+        .iface =       SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+       .count =        4,
+        .info =         snd_ca0106_spdif_info,
+        .get =          snd_ca0106_spdif_get,
+        .put =          snd_ca0106_spdif_put
+};
+
+static int snd_ca0106_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+        uinfo->count = 2;
+        uinfo->value.integer.min = 0;
+        uinfo->value.integer.max = 255;
+        return 0;
+}
+
+static int snd_ca0106_volume_get(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol, int reg, int channel_id)
+{
+        ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+        unsigned int value;
+
+        value = snd_ca0106_ptr_read(emu, reg, channel_id);
+        ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
+        ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
+        return 0;
+}
+
+static int snd_ca0106_volume_get_spdif_front(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_FRONT_CHANNEL;
+       int reg = PLAYBACK_VOLUME1;
+        return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+
+static int snd_ca0106_volume_get_spdif_center_lfe(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_CENTER_LFE_CHANNEL;
+       int reg = PLAYBACK_VOLUME1;
+        return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_get_spdif_unknown(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_UNKNOWN_CHANNEL;
+       int reg = PLAYBACK_VOLUME1;
+        return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_get_spdif_rear(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_REAR_CHANNEL;
+       int reg = PLAYBACK_VOLUME1;
+        return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_get_analog_front(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_FRONT_CHANNEL;
+       int reg = PLAYBACK_VOLUME2;
+        return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+
+static int snd_ca0106_volume_get_analog_center_lfe(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_CENTER_LFE_CHANNEL;
+       int reg = PLAYBACK_VOLUME2;
+        return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_get_analog_unknown(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_UNKNOWN_CHANNEL;
+       int reg = PLAYBACK_VOLUME2;
+        return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_get_analog_rear(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_REAR_CHANNEL;
+       int reg = PLAYBACK_VOLUME2;
+        return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+
+static int snd_ca0106_volume_get_feedback(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = 1;
+       int reg = CAPTURE_CONTROL;
+        return snd_ca0106_volume_get(kcontrol, ucontrol, reg, channel_id);
+}
+                                                                                                                           
+static int snd_ca0106_volume_put(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol, int reg, int channel_id)
+{
+        ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+        unsigned int value;
+        //value = snd_ca0106_ptr_read(emu, reg, channel_id);
+        //value = value & 0xffff;
+        value = ((0xff - ucontrol->value.integer.value[0]) << 24) | ((0xff - ucontrol->value.integer.value[1]) << 16);
+        value = value | ((0xff - ucontrol->value.integer.value[0]) << 8) | ((0xff - ucontrol->value.integer.value[1]) );
+        snd_ca0106_ptr_write(emu, reg, channel_id, value);
+        return 1;
+}
+static int snd_ca0106_volume_put_spdif_front(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_FRONT_CHANNEL;
+       int reg = PLAYBACK_VOLUME1;
+        return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_spdif_center_lfe(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_CENTER_LFE_CHANNEL;
+       int reg = PLAYBACK_VOLUME1;
+        return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_spdif_unknown(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_UNKNOWN_CHANNEL;
+       int reg = PLAYBACK_VOLUME1;
+        return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_spdif_rear(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_REAR_CHANNEL;
+       int reg = PLAYBACK_VOLUME1;
+        return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_analog_front(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_FRONT_CHANNEL;
+       int reg = PLAYBACK_VOLUME2;
+        return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_analog_center_lfe(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_CENTER_LFE_CHANNEL;
+       int reg = PLAYBACK_VOLUME2;
+        return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_analog_unknown(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_UNKNOWN_CHANNEL;
+       int reg = PLAYBACK_VOLUME2;
+        return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+static int snd_ca0106_volume_put_analog_rear(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = CONTROL_REAR_CHANNEL;
+       int reg = PLAYBACK_VOLUME2;
+        return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+
+static int snd_ca0106_volume_put_feedback(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int channel_id = 1;
+       int reg = CAPTURE_CONTROL;
+        return snd_ca0106_volume_put(kcontrol, ucontrol, reg, channel_id);
+}
+
+static snd_kcontrol_new_t snd_ca0106_volume_control_analog_front =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "Analog Front Volume",
+        .info =         snd_ca0106_volume_info,
+        .get =          snd_ca0106_volume_get_analog_front,
+        .put =          snd_ca0106_volume_put_analog_front
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_analog_center_lfe =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "Analog Center/LFE Volume",
+        .info =         snd_ca0106_volume_info,
+        .get =          snd_ca0106_volume_get_analog_center_lfe,
+        .put =          snd_ca0106_volume_put_analog_center_lfe
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_analog_unknown =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "Analog Unknown Volume",
+        .info =         snd_ca0106_volume_info,
+        .get =          snd_ca0106_volume_get_analog_unknown,
+        .put =          snd_ca0106_volume_put_analog_unknown
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_analog_rear =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "Analog Rear Volume",
+        .info =         snd_ca0106_volume_info,
+        .get =          snd_ca0106_volume_get_analog_rear,
+        .put =          snd_ca0106_volume_put_analog_rear
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_front =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "SPDIF Front Volume",
+        .info =         snd_ca0106_volume_info,
+        .get =          snd_ca0106_volume_get_spdif_front,
+        .put =          snd_ca0106_volume_put_spdif_front
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_center_lfe =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "SPDIF Center/LFE Volume",
+        .info =         snd_ca0106_volume_info,
+        .get =          snd_ca0106_volume_get_spdif_center_lfe,
+        .put =          snd_ca0106_volume_put_spdif_center_lfe
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_unknown =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "SPDIF Unknown Volume",
+        .info =         snd_ca0106_volume_info,
+        .get =          snd_ca0106_volume_get_spdif_unknown,
+        .put =          snd_ca0106_volume_put_spdif_unknown
+};
+static snd_kcontrol_new_t snd_ca0106_volume_control_spdif_rear =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "SPDIF Rear Volume",
+        .info =         snd_ca0106_volume_info,
+        .get =          snd_ca0106_volume_get_spdif_rear,
+        .put =          snd_ca0106_volume_put_spdif_rear
+};
+
+static snd_kcontrol_new_t snd_ca0106_volume_control_feedback =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "CAPTURE feedback into PLAYBACK",
+        .info =         snd_ca0106_volume_info,
+        .get =          snd_ca0106_volume_get_feedback,
+        .put =          snd_ca0106_volume_put_feedback
+};
+
+
+static int remove_ctl(snd_card_t *card, const char *name)
+{
+       snd_ctl_elem_id_t id;
+       memset(&id, 0, sizeof(id));
+       strcpy(id.name, name);
+       id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       return snd_ctl_remove_id(card, &id);
+}
+
+static snd_kcontrol_t *ctl_find(snd_card_t *card, const char *name)
+{
+       snd_ctl_elem_id_t sid;
+       memset(&sid, 0, sizeof(sid));
+       /* FIXME: strcpy is bad. */
+       strcpy(sid.name, name);
+       sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       return snd_ctl_find_id(card, &sid);
+}
+
+static int rename_ctl(snd_card_t *card, const char *src, const char *dst)
+{
+       snd_kcontrol_t *kctl = ctl_find(card, src);
+       if (kctl) {
+               strcpy(kctl->id.name, dst);
+               return 0;
+       }
+       return -ENOENT;
+}
+
+int __devinit snd_ca0106_mixer(ca0106_t *emu)
+{
+        int err;
+        snd_kcontrol_t *kctl;
+        snd_card_t *card = emu->card;
+       char **c;
+       static char *ca0106_remove_ctls[] = {
+               "Master Mono Playback Switch",
+               "Master Mono Playback Volume",
+               "3D Control - Switch",
+               "3D Control Sigmatel - Depth",
+               "PCM Playback Switch",
+               "PCM Playback Volume",
+               "CD Playback Switch",
+               "CD Playback Volume",
+               "Phone Playback Switch",
+               "Phone Playback Volume",
+               "Video Playback Switch",
+               "Video Playback Volume",
+               "PC Speaker Playback Switch",
+               "PC Speaker Playback Volume",
+               "Mono Output Select",
+               "Capture Source",
+               "Capture Switch",
+               "Capture Volume",
+               "External Amplifier",
+               "Sigmatel 4-Speaker Stereo Playback Switch",
+               "Sigmatel Surround Phase Inversion Playback ",
+               NULL
+       };
+       static char *ca0106_rename_ctls[] = {
+               "Master Playback Switch", "Capture Switch",
+               "Master Playback Volume", "Capture Volume",
+               "Line Playback Switch", "AC97 Line Capture Switch",
+               "Line Playback Volume", "AC97 Line Capture Volume",
+               "Aux Playback Switch", "AC97 Aux Capture Switch",
+               "Aux Playback Volume", "AC97 Aux Capture Volume",
+               "Mic Playback Switch", "AC97 Mic Capture Switch",
+               "Mic Playback Volume", "AC97 Mic Capture Volume",
+               "Mic Select", "AC97 Mic Select",
+               "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
+               NULL
+       };
+#if 1
+       for (c=ca0106_remove_ctls; *c; c++)
+               remove_ctl(card, *c);
+       for (c=ca0106_rename_ctls; *c; c += 2)
+               rename_ctl(card, c[0], c[1]);
+#endif
+
+        if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_front, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_rear, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_center_lfe, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_analog_unknown, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_front, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_rear, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_center_lfe, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_spdif_unknown, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_ca0106_volume_control_feedback, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+       if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_mask_control, emu)) == NULL)
+               return -ENOMEM;
+       if ((err = snd_ctl_add(card, kctl)))
+               return err;
+       if ((kctl = snd_ctl_new1(&snd_ca0106_shared_spdif, emu)) == NULL)
+               return -ENOMEM;
+       if ((err = snd_ctl_add(card, kctl)))
+               return err;
+       if ((kctl = snd_ctl_new1(&snd_ca0106_capture_source, emu)) == NULL)
+               return -ENOMEM;
+       if ((err = snd_ctl_add(card, kctl)))
+               return err;
+       if ((kctl = ctl_find(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT))) != NULL) {
+               /* already defined by ac97, remove it */
+               /* FIXME: or do we need both controls? */
+               remove_ctl(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT));
+       }
+       if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_control, emu)) == NULL)
+               return -ENOMEM;
+       if ((err = snd_ctl_add(card, kctl)))
+               return err;
+        return 0;
+}
+
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
new file mode 100644 (file)
index 0000000..afb7114
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
+ *  Version: 0.0.17
+ *
+ *  FEATURES currently supported:
+ *    See ca0106_main.c for features.
+ * 
+ *  Changelog:
+ *    Support interrupts per period.
+ *    Removed noise from Center/LFE channel when in Analog mode.
+ *    Rename and remove mixer controls.
+ *  0.0.6
+ *    Use separate card based DMA buffer for periods table list.
+ *  0.0.7
+ *    Change remove and rename ctrls into lists.
+ *  0.0.8
+ *    Try to fix capture sources.
+ *  0.0.9
+ *    Fix AC3 output.
+ *    Enable S32_LE format support.
+ *  0.0.10
+ *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
+ *  0.0.11
+ *    Add Model name recognition.
+ *  0.0.12
+ *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
+ *    Remove redundent "voice" handling.
+ *  0.0.13
+ *    Single trigger call for multi channels.
+ *  0.0.14
+ *    Set limits based on what the sound card hardware can do.
+ *    playback periods_min=2, periods_max=8
+ *    capture hw constraints require period_size = n * 64 bytes.
+ *    playback hw constraints require period_size = n * 64 bytes.
+ *  0.0.15
+ *    Separate ca0106.c into separate functional .c files.
+ *  0.0.16
+ *    Modified Copyright message.
+ *  0.0.17
+ *    Add iec958 file in proc file system to show status of SPDIF in.
+ *    
+ *  This code was initally based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+
+#include "ca0106.h"
+
+
+struct snd_ca0106_category_str {
+       int val;
+       const char *name;
+};
+
+static struct snd_ca0106_category_str snd_ca0106_con_category[] = {
+       { IEC958_AES1_CON_DAT, "DAT" },
+       { IEC958_AES1_CON_VCR, "VCR" },
+       { IEC958_AES1_CON_MICROPHONE, "microphone" },
+       { IEC958_AES1_CON_SYNTHESIZER, "synthesizer" },
+       { IEC958_AES1_CON_RATE_CONVERTER, "rate converter" },
+       { IEC958_AES1_CON_MIXER, "mixer" },
+       { IEC958_AES1_CON_SAMPLER, "sampler" },
+       { IEC958_AES1_CON_PCM_CODER, "PCM coder" },
+       { IEC958_AES1_CON_IEC908_CD, "CD" },
+       { IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" },
+       { IEC958_AES1_CON_GENERAL, "general" },
+};
+
+
+void snd_ca0106_proc_dump_iec958( snd_info_buffer_t *buffer, u32 value)
+{
+       int i;
+       u32 status[4];
+       status[0] = value & 0xff;
+       status[1] = (value >> 8) & 0xff;
+       status[2] = (value >> 16)  & 0xff;
+       status[3] = (value >> 24)  & 0xff;
+       
+       if (! (status[0] & IEC958_AES0_PROFESSIONAL)) {
+               /* consumer */
+               snd_iprintf(buffer, "Mode: consumer\n");
+               snd_iprintf(buffer, "Data: ");
+               if (!(status[0] & IEC958_AES0_NONAUDIO)) {
+                       snd_iprintf(buffer, "audio\n");
+               } else {
+                       snd_iprintf(buffer, "non-audio\n");
+               }
+               snd_iprintf(buffer, "Rate: ");
+               switch (status[3] & IEC958_AES3_CON_FS) {
+               case IEC958_AES3_CON_FS_44100:
+                       snd_iprintf(buffer, "44100 Hz\n");
+                       break;
+               case IEC958_AES3_CON_FS_48000:
+                       snd_iprintf(buffer, "48000 Hz\n");
+                       break;
+               case IEC958_AES3_CON_FS_32000:
+                       snd_iprintf(buffer, "32000 Hz\n");
+                       break;
+               default:
+                       snd_iprintf(buffer, "unknown\n");
+                       break;
+               }
+               snd_iprintf(buffer, "Copyright: ");
+               if (status[0] & IEC958_AES0_CON_NOT_COPYRIGHT) {
+                       snd_iprintf(buffer, "permitted\n");
+               } else {
+                       snd_iprintf(buffer, "protected\n");
+               }
+               snd_iprintf(buffer, "Emphasis: ");
+               if ((status[0] & IEC958_AES0_CON_EMPHASIS) != IEC958_AES0_CON_EMPHASIS_5015) {
+                       snd_iprintf(buffer, "none\n");
+               } else {
+                       snd_iprintf(buffer, "50/15us\n");
+               }
+               snd_iprintf(buffer, "Category: ");
+               for (i = 0; i < ARRAY_SIZE(snd_ca0106_con_category); i++) {
+                       if ((status[1] & IEC958_AES1_CON_CATEGORY) == snd_ca0106_con_category[i].val) {
+                               snd_iprintf(buffer, "%s\n", snd_ca0106_con_category[i].name);
+                               break;
+                       }
+               }
+               if (i >= ARRAY_SIZE(snd_ca0106_con_category)) {
+                       snd_iprintf(buffer, "unknown 0x%x\n", status[1] & IEC958_AES1_CON_CATEGORY);
+               }
+               snd_iprintf(buffer, "Original: ");
+               if (status[1] & IEC958_AES1_CON_ORIGINAL) {
+                       snd_iprintf(buffer, "original\n");
+               } else {
+                       snd_iprintf(buffer, "1st generation\n");
+               }
+               snd_iprintf(buffer, "Clock: ");
+               switch (status[3] & IEC958_AES3_CON_CLOCK) {
+               case IEC958_AES3_CON_CLOCK_1000PPM:
+                       snd_iprintf(buffer, "1000 ppm\n");
+                       break;
+               case IEC958_AES3_CON_CLOCK_50PPM:
+                       snd_iprintf(buffer, "50 ppm\n");
+                       break;
+               case IEC958_AES3_CON_CLOCK_VARIABLE:
+                       snd_iprintf(buffer, "variable pitch\n");
+                       break;
+               default:
+                       snd_iprintf(buffer, "unknown\n");
+                       break;
+               }
+       } else {
+               snd_iprintf(buffer, "Mode: professional\n");
+               snd_iprintf(buffer, "Data: ");
+               if (!(status[0] & IEC958_AES0_NONAUDIO)) {
+                       snd_iprintf(buffer, "audio\n");
+               } else {
+                       snd_iprintf(buffer, "non-audio\n");
+               }
+               snd_iprintf(buffer, "Rate: ");
+               switch (status[0] & IEC958_AES0_PRO_FS) {
+               case IEC958_AES0_PRO_FS_44100:
+                       snd_iprintf(buffer, "44100 Hz\n");
+                       break;
+               case IEC958_AES0_PRO_FS_48000:
+                       snd_iprintf(buffer, "48000 Hz\n");
+                       break;
+               case IEC958_AES0_PRO_FS_32000:
+                       snd_iprintf(buffer, "32000 Hz\n");
+                       break;
+               default:
+                       snd_iprintf(buffer, "unknown\n");
+                       break;
+               }
+               snd_iprintf(buffer, "Rate Locked: ");
+               if (status[0] & IEC958_AES0_PRO_FREQ_UNLOCKED)
+                       snd_iprintf(buffer, "no\n");
+               else
+                       snd_iprintf(buffer, "yes\n");
+               snd_iprintf(buffer, "Emphasis: ");
+               switch (status[0] & IEC958_AES0_PRO_EMPHASIS) {
+               case IEC958_AES0_PRO_EMPHASIS_CCITT:
+                       snd_iprintf(buffer, "CCITT J.17\n");
+                       break;
+               case IEC958_AES0_PRO_EMPHASIS_NONE:
+                       snd_iprintf(buffer, "none\n");
+                       break;
+               case IEC958_AES0_PRO_EMPHASIS_5015:
+                       snd_iprintf(buffer, "50/15us\n");
+                       break;
+               case IEC958_AES0_PRO_EMPHASIS_NOTID:
+               default:
+                       snd_iprintf(buffer, "unknown\n");
+                       break;
+               }
+               snd_iprintf(buffer, "Stereophonic: ");
+               if ((status[1] & IEC958_AES1_PRO_MODE) == IEC958_AES1_PRO_MODE_STEREOPHONIC) {
+                       snd_iprintf(buffer, "stereo\n");
+               } else {
+                       snd_iprintf(buffer, "not indicated\n");
+               }
+               snd_iprintf(buffer, "Userbits: ");
+               switch (status[1] & IEC958_AES1_PRO_USERBITS) {
+               case IEC958_AES1_PRO_USERBITS_192:
+                       snd_iprintf(buffer, "192bit\n");
+                       break;
+               case IEC958_AES1_PRO_USERBITS_UDEF:
+                       snd_iprintf(buffer, "user-defined\n");
+                       break;
+               default:
+                       snd_iprintf(buffer, "unkown\n");
+                       break;
+               }
+               snd_iprintf(buffer, "Sample Bits: ");
+               switch (status[2] & IEC958_AES2_PRO_SBITS) {
+               case IEC958_AES2_PRO_SBITS_20:
+                       snd_iprintf(buffer, "20 bit\n");
+                       break;
+               case IEC958_AES2_PRO_SBITS_24:
+                       snd_iprintf(buffer, "24 bit\n");
+                       break;
+               case IEC958_AES2_PRO_SBITS_UDEF:
+                       snd_iprintf(buffer, "user defined\n");
+                       break;
+               default:
+                       snd_iprintf(buffer, "unknown\n");
+                       break;
+               }
+               snd_iprintf(buffer, "Word Length: ");
+               switch (status[2] & IEC958_AES2_PRO_WORDLEN) {
+               case IEC958_AES2_PRO_WORDLEN_22_18:
+                       snd_iprintf(buffer, "22 bit or 18 bit\n");
+                       break;
+               case IEC958_AES2_PRO_WORDLEN_23_19:
+                       snd_iprintf(buffer, "23 bit or 19 bit\n");
+                       break;
+               case IEC958_AES2_PRO_WORDLEN_24_20:
+                       snd_iprintf(buffer, "24 bit or 20 bit\n");
+                       break;
+               case IEC958_AES2_PRO_WORDLEN_20_16:
+                       snd_iprintf(buffer, "20 bit or 16 bit\n");
+                       break;
+               default:
+                       snd_iprintf(buffer, "unknown\n");
+                       break;
+               }
+       }
+}
+
+static void snd_ca0106_proc_iec958(snd_info_entry_t *entry, 
+                                      snd_info_buffer_t * buffer)
+{
+       ca0106_t *emu = entry->private_data;
+       u32 value;
+
+        value = snd_ca0106_ptr_read(emu, SAMPLE_RATE_TRACKER_STATUS, 0);
+       snd_iprintf(buffer, "Status: %s, %s, %s\n",
+                 (value & 0x100000) ? "Rate Locked" : "Not Rate Locked",
+                 (value & 0x200000) ? "SPDIF Locked" : "No SPDIF Lock",
+                 (value & 0x400000) ? "Audio Valid" : "No valid audio" );
+       snd_iprintf(buffer, "Estimated sample rate: %u\n", 
+                 ((value & 0xfffff) * 48000) / 0x8000 );
+       if (value & 0x200000) {
+               snd_iprintf(buffer, "IEC958/SPDIF input status:\n");
+               value = snd_ca0106_ptr_read(emu, SPDIF_INPUT_STATUS, 0);
+               snd_ca0106_proc_dump_iec958(buffer, value);
+       }
+
+       snd_iprintf(buffer, "\n");
+}
+
+static void snd_ca0106_proc_reg_write32(snd_info_entry_t *entry, 
+                                      snd_info_buffer_t * buffer)
+{
+       ca0106_t *emu = entry->private_data;
+       unsigned long flags;
+        char line[64];
+        u32 reg, val;
+        while (!snd_info_get_line(buffer, line, sizeof(line))) {
+                if (sscanf(line, "%x %x", &reg, &val) != 2)
+                        continue;
+                if ((reg < 0x40) && (reg >=0) && (val <= 0xffffffff) ) {
+                       spin_lock_irqsave(&emu->emu_lock, flags);
+                       outl(val, emu->port + (reg & 0xfffffffc));
+                       spin_unlock_irqrestore(&emu->emu_lock, flags);
+               }
+        }
+}
+
+static void snd_ca0106_proc_reg_read32(snd_info_entry_t *entry, 
+                                      snd_info_buffer_t * buffer)
+{
+       ca0106_t *emu = entry->private_data;
+       unsigned long value;
+       unsigned long flags;
+       int i;
+       snd_iprintf(buffer, "Registers:\n\n");
+       for(i = 0; i < 0x20; i+=4) {
+               spin_lock_irqsave(&emu->emu_lock, flags);
+               value = inl(emu->port + i);
+               spin_unlock_irqrestore(&emu->emu_lock, flags);
+               snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
+       }
+}
+
+static void snd_ca0106_proc_reg_read16(snd_info_entry_t *entry, 
+                                      snd_info_buffer_t * buffer)
+{
+       ca0106_t *emu = entry->private_data;
+        unsigned int value;
+       unsigned long flags;
+       int i;
+       snd_iprintf(buffer, "Registers:\n\n");
+       for(i = 0; i < 0x20; i+=2) {
+               spin_lock_irqsave(&emu->emu_lock, flags);
+               value = inw(emu->port + i);
+               spin_unlock_irqrestore(&emu->emu_lock, flags);
+               snd_iprintf(buffer, "Register %02X: %04X\n", i, value);
+       }
+}
+
+static void snd_ca0106_proc_reg_read8(snd_info_entry_t *entry, 
+                                      snd_info_buffer_t * buffer)
+{
+       ca0106_t *emu = entry->private_data;
+       unsigned int value;
+       unsigned long flags;
+       int i;
+       snd_iprintf(buffer, "Registers:\n\n");
+       for(i = 0; i < 0x20; i+=1) {
+               spin_lock_irqsave(&emu->emu_lock, flags);
+               value = inb(emu->port + i);
+               spin_unlock_irqrestore(&emu->emu_lock, flags);
+               snd_iprintf(buffer, "Register %02X: %02X\n", i, value);
+       }
+}
+
+static void snd_ca0106_proc_reg_read1(snd_info_entry_t *entry, 
+                                      snd_info_buffer_t * buffer)
+{
+       ca0106_t *emu = entry->private_data;
+       unsigned long value;
+       int i,j;
+
+       snd_iprintf(buffer, "Registers\n");
+       for(i = 0; i < 0x40; i++) {
+               snd_iprintf(buffer, "%02X: ",i);
+               for (j = 0; j < 4; j++) {
+                  value = snd_ca0106_ptr_read(emu, i, j);
+                 snd_iprintf(buffer, "%08lX ", value);
+                }
+               snd_iprintf(buffer, "\n");
+       }
+}
+
+static void snd_ca0106_proc_reg_read2(snd_info_entry_t *entry, 
+                                      snd_info_buffer_t * buffer)
+{
+       ca0106_t *emu = entry->private_data;
+       unsigned long value;
+       int i,j;
+
+       snd_iprintf(buffer, "Registers\n");
+       for(i = 0x40; i < 0x80; i++) {
+               snd_iprintf(buffer, "%02X: ",i);
+               for (j = 0; j < 4; j++) {
+                  value = snd_ca0106_ptr_read(emu, i, j);
+                 snd_iprintf(buffer, "%08lX ", value);
+                }
+               snd_iprintf(buffer, "\n");
+       }
+}
+
+static void snd_ca0106_proc_reg_write(snd_info_entry_t *entry, 
+                                      snd_info_buffer_t * buffer)
+{
+       ca0106_t *emu = entry->private_data;
+        char line[64];
+        unsigned int reg, channel_id , val;
+        while (!snd_info_get_line(buffer, line, sizeof(line))) {
+                if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+                        continue;
+                if ((reg < 0x80) && (reg >=0) && (val <= 0xffffffff) && (channel_id >=0) && (channel_id <= 3) )
+                        snd_ca0106_ptr_write(emu, reg, channel_id, val);
+        }
+}
+
+
+int __devinit snd_ca0106_proc_init(ca0106_t * emu)
+{
+       snd_info_entry_t *entry;
+       
+       if(! snd_card_proc_new(emu->card, "iec958", &entry))
+               snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_iec958);
+       if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) {
+               snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read32);
+               entry->c.text.write_size = 64;
+               entry->c.text.write = snd_ca0106_proc_reg_write32;
+       }
+       if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
+               snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read16);
+       if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry))
+               snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read8);
+       if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) {
+               snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read1);
+               entry->c.text.write_size = 64;
+               entry->c.text.write = snd_ca0106_proc_reg_write;
+//             entry->private_data = emu;
+       }
+       if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry)) 
+               snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read2);
+       return 0;
+}
+
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
new file mode 100644 (file)
index 0000000..e60d581
--- /dev/null
@@ -0,0 +1,1644 @@
+/*
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *  Driver EMU10K1X chips
+ *
+ *  Parts of this code were adapted from audigyls.c driver which is
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *
+ *  Chips (SB0200 model):
+ *    - EMU10K1X-DBQ
+ *    - STAC 9708T
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+
+MODULE_AUTHOR("Francisco Moraes <fmoraes@nc.rr.com>");
+MODULE_DESCRIPTION("EMU10K1X");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Dell Creative Labs,SB Live!}");
+
+// module parameters (see "Module Parameters")
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the EMU10K1X soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the EMU10K1X soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard.");
+
+
+// some definitions were borrowed from emu10k1 driver as they seem to be the same
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0                                                */
+/************************************************************************************************/
+
+#define PTR                    0x00            /* Indexed register set pointer register        */
+                                               /* NOTE: The CHANNELNUM and ADDRESS words can   */
+                                               /* be modified independently of each other.     */
+
+#define DATA                   0x04            /* Indexed register set data register           */
+
+#define IPR                    0x08            /* Global interrupt pending register            */
+                                               /* Clear pending interrupts by writing a 1 to   */
+                                               /* the relevant bits and zero to the other bits */
+#define IPR_MIDITRANSBUFEMPTY   0x00000001     /* MIDI UART transmit buffer empty              */
+#define IPR_MIDIRECVBUFEMPTY    0x00000002     /* MIDI UART receive buffer empty               */
+#define IPR_CH_0_LOOP           0x00000800      /* Channel 0 loop                               */
+#define IPR_CH_0_HALF_LOOP      0x00000100      /* Channel 0 half loop                          */
+#define IPR_CAP_0_LOOP          0x00080000      /* Channel capture loop                         */
+#define IPR_CAP_0_HALF_LOOP     0x00010000      /* Channel capture half loop                    */
+
+#define INTE                   0x0c            /* Interrupt enable register                    */
+#define INTE_MIDITXENABLE       0x00000001     /* Enable MIDI transmit-buffer-empty interrupts */
+#define INTE_MIDIRXENABLE       0x00000002     /* Enable MIDI receive-buffer-empty interrupts  */
+#define INTE_CH_0_LOOP          0x00000800      /* Channel 0 loop                               */
+#define INTE_CH_0_HALF_LOOP     0x00000100      /* Channel 0 half loop                          */
+#define INTE_CAP_0_LOOP         0x00080000      /* Channel capture loop                         */
+#define INTE_CAP_0_HALF_LOOP    0x00010000      /* Channel capture half loop                    */
+
+#define HCFG                   0x14            /* Hardware config register                     */
+
+#define HCFG_LOCKSOUNDCACHE    0x00000008      /* 1 = Cancel bustmaster accesses to soundcache */
+                                               /* NOTE: This should generally never be used.   */
+#define HCFG_AUDIOENABLE       0x00000001      /* 0 = CODECs transmit zero-valued samples      */
+                                               /* Should be set to 1 when the EMU10K1 is       */
+                                               /* completely initialized.                      */
+#define GPIO                   0x18            /* Defaults: 00001080-Analog, 00001000-SPDIF.   */
+
+
+#define AC97DATA               0x1c            /* AC97 register set data register (16 bit)     */
+
+#define AC97ADDRESS            0x1e            /* AC97 register set address register (8 bit)   */
+
+/********************************************************************************************************/
+/* Emu10k1x pointer-offset register set, accessed through the PTR and DATA registers                   */
+/********************************************************************************************************/
+#define PLAYBACK_LIST_ADDR     0x00            /* Base DMA address of a list of pointers to each period/size */
+                                               /* One list entry: 4 bytes for DMA address, 
+                                                * 4 bytes for period_size << 16.
+                                                * One list entry is 8 bytes long.
+                                                * One list entry for each period in the buffer.
+                                                */
+#define PLAYBACK_LIST_SIZE     0x01            /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000  */
+#define PLAYBACK_LIST_PTR      0x02            /* Pointer to the current period being played */
+#define PLAYBACK_DMA_ADDR      0x04            /* Playback DMA addresss */
+#define PLAYBACK_PERIOD_SIZE   0x05            /* Playback period size */
+#define PLAYBACK_POINTER       0x06            /* Playback period pointer. Sample currently in DAC */
+#define PLAYBACK_UNKNOWN1       0x07
+#define PLAYBACK_UNKNOWN2       0x08
+
+/* Only one capture channel supported */
+#define CAPTURE_DMA_ADDR       0x10            /* Capture DMA address */
+#define CAPTURE_BUFFER_SIZE    0x11            /* Capture buffer size */
+#define CAPTURE_POINTER                0x12            /* Capture buffer pointer. Sample currently in ADC */
+#define CAPTURE_UNKNOWN         0x13
+
+/* From 0x20 - 0x3f, last samples played on each channel */
+
+#define TRIGGER_CHANNEL         0x40            /* Trigger channel playback                     */
+#define TRIGGER_CHANNEL_0       0x00000001      /* Trigger channel 0                            */
+#define TRIGGER_CHANNEL_1       0x00000002      /* Trigger channel 1                            */
+#define TRIGGER_CHANNEL_2       0x00000004      /* Trigger channel 2                            */
+#define TRIGGER_CAPTURE         0x00000100      /* Trigger capture channel                      */
+
+#define ROUTING                 0x41            /* Setup sound routing ?                        */
+#define ROUTING_FRONT_LEFT      0x00000001
+#define ROUTING_FRONT_RIGHT     0x00000002
+#define ROUTING_REAR_LEFT       0x00000004
+#define ROUTING_REAR_RIGHT      0x00000008
+#define ROUTING_CENTER_LFE      0x00010000
+
+#define SPCS0                  0x42            /* SPDIF output Channel Status 0 register       */
+
+#define SPCS1                  0x43            /* SPDIF output Channel Status 1 register       */
+
+#define SPCS2                  0x44            /* SPDIF output Channel Status 2 register       */
+
+#define SPCS_CLKACCYMASK       0x30000000      /* Clock accuracy                               */
+#define SPCS_CLKACCY_1000PPM   0x00000000      /* 1000 parts per million                       */
+#define SPCS_CLKACCY_50PPM     0x10000000      /* 50 parts per million                         */
+#define SPCS_CLKACCY_VARIABLE  0x20000000      /* Variable accuracy                            */
+#define SPCS_SAMPLERATEMASK    0x0f000000      /* Sample rate                                  */
+#define SPCS_SAMPLERATE_44     0x00000000      /* 44.1kHz sample rate                          */
+#define SPCS_SAMPLERATE_48     0x02000000      /* 48kHz sample rate                            */
+#define SPCS_SAMPLERATE_32     0x03000000      /* 32kHz sample rate                            */
+#define SPCS_CHANNELNUMMASK    0x00f00000      /* Channel number                               */
+#define SPCS_CHANNELNUM_UNSPEC 0x00000000      /* Unspecified channel number                   */
+#define SPCS_CHANNELNUM_LEFT   0x00100000      /* Left channel                                 */
+#define SPCS_CHANNELNUM_RIGHT  0x00200000      /* Right channel                                */
+#define SPCS_SOURCENUMMASK     0x000f0000      /* Source number                                */
+#define SPCS_SOURCENUM_UNSPEC  0x00000000      /* Unspecified source number                    */
+#define SPCS_GENERATIONSTATUS  0x00008000      /* Originality flag (see IEC-958 spec)          */
+#define SPCS_CATEGORYCODEMASK  0x00007f00      /* Category code (see IEC-958 spec)             */
+#define SPCS_MODEMASK          0x000000c0      /* Mode (see IEC-958 spec)                      */
+#define SPCS_EMPHASISMASK      0x00000038      /* Emphasis                                     */
+#define SPCS_EMPHASIS_NONE     0x00000000      /* No emphasis                                  */
+#define SPCS_EMPHASIS_50_15    0x00000008      /* 50/15 usec 2 channel                         */
+#define SPCS_COPYRIGHT         0x00000004      /* Copyright asserted flag -- do not modify     */
+#define SPCS_NOTAUDIODATA      0x00000002      /* 0 = Digital audio, 1 = not audio             */
+#define SPCS_PROFESSIONAL      0x00000001      /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992)  */
+
+#define SPDIF_SELECT           0x45            /* Enables SPDIF or Analogue outputs 0-Analogue, 0x700-SPDIF */
+
+/* This is the MPU port on the card                                                            */
+#define MUDATA         0x47
+#define MUCMD          0x48
+#define MUSTAT         MUCMD
+
+/* From 0x50 - 0x5f, last samples captured */
+
+/**
+ * The hardware has 3 channels for playback and 1 for capture.
+ *  - channel 0 is the front channel
+ *  - channel 1 is the rear channel
+ *  - channel 2 is the center/lfe chanel
+ * Volume is controlled by the AC97 for the front and rear channels by
+ * the PCM Playback Volume, Sigmatel Surround Playback Volume and 
+ * Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects
+ * the front/rear channel mixing in the REAR OUT jack. When using the
+ * 4-Speaker Stereo, both front and rear channels will be mixed in the
+ * REAR OUT.
+ * The center/lfe channel has no volume control and cannot be muted during
+ * playback.
+ */
+
+typedef struct snd_emu10k1x_voice emu10k1x_voice_t;
+typedef struct snd_emu10k1x emu10k1x_t;
+typedef struct snd_emu10k1x_pcm emu10k1x_pcm_t;
+
+struct snd_emu10k1x_voice {
+       emu10k1x_t *emu;
+       int number;
+       int use;
+  
+       emu10k1x_pcm_t *epcm;
+};
+
+struct snd_emu10k1x_pcm {
+       emu10k1x_t *emu;
+       snd_pcm_substream_t *substream;
+       emu10k1x_voice_t *voice;
+       unsigned short running;
+};
+
+typedef struct {
+       struct snd_emu10k1x *emu;
+       snd_rawmidi_t *rmidi;
+       snd_rawmidi_substream_t *substream_input;
+       snd_rawmidi_substream_t *substream_output;
+       unsigned int midi_mode;
+       spinlock_t input_lock;
+       spinlock_t output_lock;
+       spinlock_t open_lock;
+       int tx_enable, rx_enable;
+       int port;
+       int ipr_tx, ipr_rx;
+       void (*interrupt)(emu10k1x_t *emu, unsigned int status);
+} emu10k1x_midi_t;
+
+// definition of the chip-specific record
+struct snd_emu10k1x {
+       snd_card_t *card;
+       struct pci_dev *pci;
+
+       unsigned long port;
+       struct resource *res_port;
+       int irq;
+
+       unsigned int revision;          /* chip revision */
+       unsigned int serial;            /* serial number */
+       unsigned short model;           /* subsystem id */
+
+       spinlock_t emu_lock;
+       spinlock_t voice_lock;
+
+       ac97_t *ac97;
+       snd_pcm_t *pcm;
+
+       emu10k1x_voice_t voices[3];
+       emu10k1x_voice_t capture_voice;
+       u32 spdif_bits[3]; // SPDIF out setup
+
+       struct snd_dma_buffer dma_buffer;
+
+       emu10k1x_midi_t midi;
+};
+
+/* hardware definition */
+static snd_pcm_hardware_t snd_emu10k1x_playback_hw = {
+       .info =                 (SNDRV_PCM_INFO_MMAP | 
+                                SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =              SNDRV_PCM_FMTBIT_S16_LE,
+       .rates =                SNDRV_PCM_RATE_48000,
+       .rate_min =             48000,
+       .rate_max =             48000,
+       .channels_min =         2,
+       .channels_max =         2,
+       .buffer_bytes_max =     (32*1024),
+       .period_bytes_min =     64,
+       .period_bytes_max =     (16*1024),
+       .periods_min =          2,
+       .periods_max =          8,
+       .fifo_size =            0,
+};
+
+static snd_pcm_hardware_t snd_emu10k1x_capture_hw = {
+       .info =                 (SNDRV_PCM_INFO_MMAP | 
+                                SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =              SNDRV_PCM_FMTBIT_S16_LE,
+       .rates =                SNDRV_PCM_RATE_48000,
+       .rate_min =             48000,
+       .rate_max =             48000,
+       .channels_min =         2,
+       .channels_max =         2,
+       .buffer_bytes_max =     (32*1024),
+       .period_bytes_min =     64,
+       .period_bytes_max =     (16*1024),
+       .periods_min =          2,
+       .periods_max =          2,
+       .fifo_size =            0,
+};
+
+static unsigned int snd_emu10k1x_ptr_read(emu10k1x_t * emu, 
+                                         unsigned int reg, 
+                                         unsigned int chn)
+{
+       unsigned long flags;
+       unsigned int regptr, val;
+  
+       regptr = (reg << 16) | chn;
+
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outl(regptr, emu->port + PTR);
+       val = inl(emu->port + DATA);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+       return val;
+}
+
+static void snd_emu10k1x_ptr_write(emu10k1x_t *emu, 
+                                  unsigned int reg, 
+                                  unsigned int chn, 
+                                  unsigned int data)
+{
+       unsigned int regptr;
+       unsigned long flags;
+
+       regptr = (reg << 16) | chn;
+
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outl(regptr, emu->port + PTR);
+       outl(data, emu->port + DATA);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_intr_enable(emu10k1x_t *emu, unsigned int intrenb)
+{
+       unsigned long flags;
+       unsigned int enable;
+  
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       enable = inl(emu->port + INTE) | intrenb;
+       outl(enable, emu->port + INTE);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_intr_disable(emu10k1x_t *emu, unsigned int intrenb)
+{
+       unsigned long flags;
+       unsigned int enable;
+  
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       enable = inl(emu->port + INTE) & ~intrenb;
+       outl(enable, emu->port + INTE);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_gpio_write(emu10k1x_t *emu, unsigned int value)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outl(value, emu->port + GPIO);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+       emu10k1x_pcm_t *epcm = runtime->private_data;
+  
+       if (epcm)
+               kfree(epcm);
+}
+
+static void snd_emu10k1x_pcm_interrupt(emu10k1x_t *emu, emu10k1x_voice_t *voice)
+{
+       emu10k1x_pcm_t *epcm;
+
+       if ((epcm = voice->epcm) == NULL)
+               return;
+       if (epcm->substream == NULL)
+               return;
+#if 0
+       snd_printk(KERN_INFO "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+                  epcm->substream->ops->pointer(epcm->substream),
+                  snd_pcm_lib_period_bytes(epcm->substream),
+                  snd_pcm_lib_buffer_bytes(epcm->substream));
+#endif
+       snd_pcm_period_elapsed(epcm->substream);
+}
+
+/* open callback */
+static int snd_emu10k1x_playback_open(snd_pcm_substream_t *substream)
+{
+       emu10k1x_t *chip = snd_pcm_substream_chip(substream);
+       emu10k1x_pcm_t *epcm;
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       int err;
+
+       if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
+               return err;
+       }
+       if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+                return err;
+
+       epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+       if (epcm == NULL)
+               return -ENOMEM;
+       epcm->emu = chip;
+       epcm->substream = substream;
+  
+       runtime->private_data = epcm;
+       runtime->private_free = snd_emu10k1x_pcm_free_substream;
+  
+       runtime->hw = snd_emu10k1x_playback_hw;
+
+       return 0;
+}
+
+/* close callback */
+static int snd_emu10k1x_playback_close(snd_pcm_substream_t *substream)
+{
+       return 0;
+}
+
+/* hw_params callback */
+static int snd_emu10k1x_pcm_hw_params(snd_pcm_substream_t *substream,
+                                     snd_pcm_hw_params_t * hw_params)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1x_pcm_t *epcm = runtime->private_data;
+
+       if (! epcm->voice) {
+               epcm->voice = &epcm->emu->voices[substream->pcm->device];
+               epcm->voice->use = 1;
+               epcm->voice->epcm = epcm;
+       }
+
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_emu10k1x_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1x_pcm_t *epcm;
+
+       if (runtime->private_data == NULL)
+               return 0;
+       
+       epcm = runtime->private_data;
+
+       if (epcm->voice) {
+               epcm->voice->use = 0;
+               epcm->voice->epcm = NULL;
+               epcm->voice = NULL;
+       }
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare callback */
+static int snd_emu10k1x_pcm_prepare(snd_pcm_substream_t *substream)
+{
+       emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1x_pcm_t *epcm = runtime->private_data;
+       int voice = epcm->voice->number;
+       u32 *table_base = (u32 *)(emu->dma_buffer.area+1024*voice);
+       u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+       int i;
+       
+       for(i=0; i < runtime->periods; i++) {
+               *table_base++=runtime->dma_addr+(i*period_size_bytes);
+               *table_base++=period_size_bytes<<16;
+       }
+
+       snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_ADDR, voice, emu->dma_buffer.addr+1024*voice);
+       snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_SIZE, voice, (runtime->periods - 1) << 19);
+       snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_PTR, voice, 0);
+       snd_emu10k1x_ptr_write(emu, PLAYBACK_POINTER, voice, 0);
+       snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN1, voice, 0);
+       snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN2, voice, 0);
+       snd_emu10k1x_ptr_write(emu, PLAYBACK_DMA_ADDR, voice, runtime->dma_addr);
+
+       snd_emu10k1x_ptr_write(emu, PLAYBACK_PERIOD_SIZE, voice, frames_to_bytes(runtime, runtime->period_size)<<16);
+
+       return 0;
+}
+
+/* trigger callback */
+static int snd_emu10k1x_pcm_trigger(snd_pcm_substream_t *substream,
+                                   int cmd)
+{
+       emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1x_pcm_t *epcm = runtime->private_data;
+       int channel = epcm->voice->number;
+       int result = 0;
+
+//     snd_printk(KERN_INFO "trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n", (int)emu, cmd, (int)substream->ops->pointer(substream));
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               if(runtime->periods == 2)
+                       snd_emu10k1x_intr_enable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel);
+               else
+                       snd_emu10k1x_intr_enable(emu, INTE_CH_0_LOOP << channel);
+               epcm->running = 1;
+               snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|(TRIGGER_CHANNEL_0<<channel));
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               epcm->running = 0;
+               snd_emu10k1x_intr_disable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel);
+               snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CHANNEL_0<<channel));
+               break;
+       default:
+               result = -EINVAL;
+               break;
+       }
+       return result;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t
+snd_emu10k1x_pcm_pointer(snd_pcm_substream_t *substream)
+{
+       emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1x_pcm_t *epcm = runtime->private_data;
+       int channel = epcm->voice->number;
+       snd_pcm_uframes_t ptr = 0, ptr1 = 0, ptr2= 0,ptr3 = 0,ptr4 = 0;
+
+       if (!epcm->running)
+               return 0;
+
+       ptr3 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+       ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel);
+       ptr4 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+
+       if(ptr4 == 0 && ptr1 == frames_to_bytes(runtime, runtime->buffer_size))
+               return 0;
+       
+       if (ptr3 != ptr4) 
+               ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel);
+       ptr2 = bytes_to_frames(runtime, ptr1);
+       ptr2 += (ptr4 >> 3) * runtime->period_size;
+       ptr = ptr2;
+
+       if (ptr >= runtime->buffer_size)
+               ptr -= runtime->buffer_size;
+
+       return ptr;
+}
+
+/* operators */
+static snd_pcm_ops_t snd_emu10k1x_playback_ops = {
+       .open =        snd_emu10k1x_playback_open,
+       .close =       snd_emu10k1x_playback_close,
+       .ioctl =       snd_pcm_lib_ioctl,
+       .hw_params =   snd_emu10k1x_pcm_hw_params,
+       .hw_free =     snd_emu10k1x_pcm_hw_free,
+       .prepare =     snd_emu10k1x_pcm_prepare,
+       .trigger =     snd_emu10k1x_pcm_trigger,
+       .pointer =     snd_emu10k1x_pcm_pointer,
+};
+
+/* open_capture callback */
+static int snd_emu10k1x_pcm_open_capture(snd_pcm_substream_t *substream)
+{
+       emu10k1x_t *chip = snd_pcm_substream_chip(substream);
+       emu10k1x_pcm_t *epcm;
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       int err;
+
+       if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+                return err;
+       if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+                return err;
+
+       epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+       if (epcm == NULL)
+               return -ENOMEM;
+
+       epcm->emu = chip;
+       epcm->substream = substream;
+
+       runtime->private_data = epcm;
+       runtime->private_free = snd_emu10k1x_pcm_free_substream;
+
+       runtime->hw = snd_emu10k1x_capture_hw;
+
+       return 0;
+}
+
+/* close callback */
+static int snd_emu10k1x_pcm_close_capture(snd_pcm_substream_t *substream)
+{
+       return 0;
+}
+
+/* hw_params callback */
+static int snd_emu10k1x_pcm_hw_params_capture(snd_pcm_substream_t *substream,
+                                             snd_pcm_hw_params_t * hw_params)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1x_pcm_t *epcm = runtime->private_data;
+
+       if (! epcm->voice) {
+               if (epcm->emu->capture_voice.use)
+                       return -EBUSY;
+               epcm->voice = &epcm->emu->capture_voice;
+               epcm->voice->epcm = epcm;
+               epcm->voice->use = 1;
+       }
+
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_emu10k1x_pcm_hw_free_capture(snd_pcm_substream_t *substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+
+       emu10k1x_pcm_t *epcm;
+
+       if (runtime->private_data == NULL)
+               return 0;
+       epcm = runtime->private_data;
+
+       if (epcm->voice) {
+               epcm->voice->use = 0;
+               epcm->voice->epcm = NULL;
+               epcm->voice = NULL;
+       }
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare capture callback */
+static int snd_emu10k1x_pcm_prepare_capture(snd_pcm_substream_t *substream)
+{
+       emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+
+       snd_emu10k1x_ptr_write(emu, CAPTURE_DMA_ADDR, 0, runtime->dma_addr);
+       snd_emu10k1x_ptr_write(emu, CAPTURE_BUFFER_SIZE, 0, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+       snd_emu10k1x_ptr_write(emu, CAPTURE_POINTER, 0, 0);
+       snd_emu10k1x_ptr_write(emu, CAPTURE_UNKNOWN, 0, 0);
+
+       return 0;
+}
+
+/* trigger_capture callback */
+static int snd_emu10k1x_pcm_trigger_capture(snd_pcm_substream_t *substream,
+                                           int cmd)
+{
+       emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1x_pcm_t *epcm = runtime->private_data;
+       int result = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               snd_emu10k1x_intr_enable(emu, INTE_CAP_0_LOOP | 
+                                        INTE_CAP_0_HALF_LOOP);
+               snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|TRIGGER_CAPTURE);
+               epcm->running = 1;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               epcm->running = 0;
+               snd_emu10k1x_intr_disable(emu, INTE_CAP_0_LOOP | 
+                                         INTE_CAP_0_HALF_LOOP);
+               snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CAPTURE));
+               break;
+       default:
+               result = -EINVAL;
+               break;
+       }
+       return result;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_emu10k1x_pcm_pointer_capture(snd_pcm_substream_t *substream)
+{
+       emu10k1x_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1x_pcm_t *epcm = runtime->private_data;
+       snd_pcm_uframes_t ptr;
+
+       if (!epcm->running)
+               return 0;
+
+       ptr = bytes_to_frames(runtime, snd_emu10k1x_ptr_read(emu, CAPTURE_POINTER, 0));
+       if (ptr >= runtime->buffer_size)
+               ptr -= runtime->buffer_size;
+
+       return ptr;
+}
+
+static snd_pcm_ops_t snd_emu10k1x_capture_ops = {
+       .open =        snd_emu10k1x_pcm_open_capture,
+       .close =       snd_emu10k1x_pcm_close_capture,
+       .ioctl =       snd_pcm_lib_ioctl,
+       .hw_params =   snd_emu10k1x_pcm_hw_params_capture,
+       .hw_free =     snd_emu10k1x_pcm_hw_free_capture,
+       .prepare =     snd_emu10k1x_pcm_prepare_capture,
+       .trigger =     snd_emu10k1x_pcm_trigger_capture,
+       .pointer =     snd_emu10k1x_pcm_pointer_capture,
+};
+
+static unsigned short snd_emu10k1x_ac97_read(ac97_t *ac97,
+                                            unsigned short reg)
+{
+       emu10k1x_t *emu = ac97->private_data;
+       unsigned long flags;
+       unsigned short val;
+  
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outb(reg, emu->port + AC97ADDRESS);
+       val = inw(emu->port + AC97DATA);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+       return val;
+}
+
+static void snd_emu10k1x_ac97_write(ac97_t *ac97,
+                                   unsigned short reg, unsigned short val)
+{
+       emu10k1x_t *emu = ac97->private_data;
+       unsigned long flags;
+  
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       outb(reg, emu->port + AC97ADDRESS);
+       outw(val, emu->port + AC97DATA);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static int snd_emu10k1x_ac97(emu10k1x_t *chip)
+{
+       ac97_bus_t *pbus;
+       ac97_template_t ac97;
+       int err;
+       static ac97_bus_ops_t ops = {
+               .write = snd_emu10k1x_ac97_write,
+               .read = snd_emu10k1x_ac97_read,
+       };
+  
+       if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+               return err;
+       pbus->no_vra = 1; /* we don't need VRA */
+
+       memset(&ac97, 0, sizeof(ac97));
+       ac97.private_data = chip;
+       return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+}
+
+static int snd_emu10k1x_free(emu10k1x_t *chip)
+{
+       snd_emu10k1x_ptr_write(chip, TRIGGER_CHANNEL, 0, 0);
+       // disable interrupts
+       outl(0, chip->port + INTE);
+       // disable audio
+       outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
+
+       // release the i/o port
+       if (chip->res_port) {
+               release_resource(chip->res_port);
+               kfree_nocheck(chip->res_port);
+       }
+       // release the irq
+       if (chip->irq >= 0)
+               free_irq(chip->irq, (void *)chip);
+
+       // release the DMA
+       if (chip->dma_buffer.area) {
+               snd_dma_free_pages(&chip->dma_buffer);
+       }
+
+       pci_disable_device(chip->pci);
+
+       // release the data
+       kfree(chip);
+       return 0;
+}
+
+static int snd_emu10k1x_dev_free(snd_device_t *device)
+{
+       emu10k1x_t *chip = device->device_data;
+       return snd_emu10k1x_free(chip);
+}
+
+static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id,
+                                         struct pt_regs *regs)
+{
+       unsigned int status;
+
+       emu10k1x_t *chip = dev_id;
+       emu10k1x_voice_t *pvoice = chip->voices;
+       int i;
+       int mask;
+
+       status = inl(chip->port + IPR);
+
+       if(status) {
+               // capture interrupt
+               if(status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) {
+                       emu10k1x_voice_t *pvoice = &chip->capture_voice;
+                       if(pvoice->use)
+                               snd_emu10k1x_pcm_interrupt(chip, pvoice);
+                       else
+                               snd_emu10k1x_intr_disable(chip, 
+                                                         INTE_CAP_0_LOOP |
+                                                         INTE_CAP_0_HALF_LOOP);
+               }
+               
+               mask = IPR_CH_0_LOOP|IPR_CH_0_HALF_LOOP;
+               for(i = 0; i < 3; i++) {
+                       if(status & mask) {
+                               if(pvoice->use)
+                                       snd_emu10k1x_pcm_interrupt(chip, pvoice);
+                               else 
+                                       snd_emu10k1x_intr_disable(chip, mask);
+                       }
+                       pvoice++;
+                       mask <<= 1;
+               }
+               
+               if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
+                       if (chip->midi.interrupt)
+                               chip->midi.interrupt(chip, status);
+                       else
+                               snd_emu10k1x_intr_disable(chip, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
+               }
+               
+               // acknowledge the interrupt if necessary
+               if(status)
+                       outl(status, chip->port+IPR);
+
+//             snd_printk(KERN_INFO "interrupt %08x\n", status);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void snd_emu10k1x_pcm_free(snd_pcm_t *pcm)
+{
+       emu10k1x_t *emu = pcm->private_data;
+       emu->pcm = NULL;
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_emu10k1x_pcm(emu10k1x_t *emu, int device, snd_pcm_t **rpcm)
+{
+       snd_pcm_t *pcm;
+       int err;
+       int capture = 0;
+  
+       if (rpcm)
+               *rpcm = NULL;
+       if (device == 0)
+               capture = 1;
+       
+       if ((err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm)) < 0)
+               return err;
+  
+       pcm->private_data = emu;
+       pcm->private_free = snd_emu10k1x_pcm_free;
+       
+       switch(device) {
+       case 0:
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1x_capture_ops);
+               break;
+       case 1:
+       case 2:
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
+               break;
+       }
+
+       pcm->info_flags = 0;
+       pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+       switch(device) {
+       case 0:
+               strcpy(pcm->name, "EMU10K1X Front");
+               break;
+       case 1:
+               strcpy(pcm->name, "EMU10K1X Rear");
+               break;
+       case 2:
+               strcpy(pcm->name, "EMU10K1X Center/LFE");
+               break;
+       }
+       emu->pcm = pcm;
+
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                             snd_dma_pci_data(emu->pci), 
+                                             32*1024, 32*1024);
+  
+       if (rpcm)
+               *rpcm = pcm;
+  
+       return 0;
+}
+
+static int __devinit snd_emu10k1x_create(snd_card_t *card,
+                                        struct pci_dev *pci,
+                                        emu10k1x_t **rchip)
+{
+       emu10k1x_t *chip;
+       int err;
+       int ch;
+       static snd_device_ops_t ops = {
+               .dev_free = snd_emu10k1x_dev_free,
+       };
+  
+       *rchip = NULL;
+  
+       if ((err = pci_enable_device(pci)) < 0)
+               return err;
+       if (pci_set_dma_mask(pci, 0x0fffffff) < 0 ||
+           pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) {
+               snd_printk(KERN_ERR "error to set 28bit mask DMA\n");
+               pci_disable_device(pci);
+               return -ENXIO;
+       }
+  
+       chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+       if (chip == NULL) {
+               pci_disable_device(pci);
+               return -ENOMEM;
+       }
+  
+       chip->card = card;
+       chip->pci = pci;
+       chip->irq = -1;
+
+       spin_lock_init(&chip->emu_lock);
+       spin_lock_init(&chip->voice_lock);
+  
+       chip->port = pci_resource_start(pci, 0);
+       if ((chip->res_port = request_region(chip->port, 8,
+                                            "EMU10K1X")) == NULL) { 
+               snd_printk(KERN_ERR "emu10k1x: cannot allocate the port 0x%lx\n", chip->port);
+               snd_emu10k1x_free(chip);
+               return -EBUSY;
+       }
+
+       if (request_irq(pci->irq, snd_emu10k1x_interrupt,
+                       SA_INTERRUPT|SA_SHIRQ, "EMU10K1X",
+                       (void *)chip)) {
+               snd_printk(KERN_ERR "emu10k1x: cannot grab irq %d\n", pci->irq);
+               snd_emu10k1x_free(chip);
+               return -EBUSY;
+       }
+       chip->irq = pci->irq;
+  
+       if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+                              4 * 1024, &chip->dma_buffer) < 0) {
+               snd_emu10k1x_free(chip);
+               return -ENOMEM;
+       }
+
+       pci_set_master(pci);
+       /* read revision & serial */
+       pci_read_config_byte(pci, PCI_REVISION_ID, (char *)&chip->revision);
+       pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
+       pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
+       snd_printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
+                  chip->revision, chip->serial);
+
+       outl(0, chip->port + INTE);     
+
+       for(ch = 0; ch < 3; ch++) {
+               chip->voices[ch].emu = chip;
+               chip->voices[ch].number = ch;
+       }
+
+       /*
+        *  Init to 0x02109204 :
+        *  Clock accuracy    = 0     (1000ppm)
+        *  Sample Rate       = 2     (48kHz)
+        *  Audio Channel     = 1     (Left of 2)
+        *  Source Number     = 0     (Unspecified)
+        *  Generation Status = 1     (Original for Cat Code 12)
+        *  Cat Code          = 12    (Digital Signal Mixer)
+        *  Mode              = 0     (Mode 0)
+        *  Emphasis          = 0     (None)
+        *  CP                = 1     (Copyright unasserted)
+        *  AN                = 0     (Audio data)
+        *  P                 = 0     (Consumer)
+        */
+       snd_emu10k1x_ptr_write(chip, SPCS0, 0,
+                              chip->spdif_bits[0] = 
+                              SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                              SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                              SPCS_GENERATIONSTATUS | 0x00001200 |
+                              0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+       snd_emu10k1x_ptr_write(chip, SPCS1, 0,
+                              chip->spdif_bits[1] = 
+                              SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                              SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                              SPCS_GENERATIONSTATUS | 0x00001200 |
+                              0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+       snd_emu10k1x_ptr_write(chip, SPCS2, 0,
+                              chip->spdif_bits[2] = 
+                              SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+                              SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+                              SPCS_GENERATIONSTATUS | 0x00001200 |
+                              0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+
+       snd_emu10k1x_ptr_write(chip, SPDIF_SELECT, 0, 0x700); // disable SPDIF
+       snd_emu10k1x_ptr_write(chip, ROUTING, 0, 0x1003F); // routing
+       snd_emu10k1x_gpio_write(chip, 0x1080); // analog mode
+
+       outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
+
+       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
+                                 chip, &ops)) < 0) {
+               snd_emu10k1x_free(chip);
+               return err;
+       }
+       *rchip = chip;
+       return 0;
+}
+
+static void snd_emu10k1x_proc_reg_read(snd_info_entry_t *entry, 
+                                      snd_info_buffer_t * buffer)
+{
+       emu10k1x_t *emu = entry->private_data;
+       unsigned long value,value1,value2;
+       unsigned long flags;
+       int i;
+
+       snd_iprintf(buffer, "Registers:\n\n");
+       for(i = 0; i < 0x20; i+=4) {
+               spin_lock_irqsave(&emu->emu_lock, flags);
+               value = inl(emu->port + i);
+               spin_unlock_irqrestore(&emu->emu_lock, flags);
+               snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
+       }
+       snd_iprintf(buffer, "\nRegisters\n\n");
+       for(i = 0; i <= 0x48; i++) {
+               value = snd_emu10k1x_ptr_read(emu, i, 0);
+               if(i < 0x10 || (i >= 0x20 && i < 0x40)) {
+                       value1 = snd_emu10k1x_ptr_read(emu, i, 1);
+                       value2 = snd_emu10k1x_ptr_read(emu, i, 2);
+                       snd_iprintf(buffer, "%02X: %08lX %08lX %08lX\n", i, value, value1, value2);
+               } else {
+                       snd_iprintf(buffer, "%02X: %08lX\n", i, value);
+               }
+       }
+}
+
+static void snd_emu10k1x_proc_reg_write(snd_info_entry_t *entry, 
+                                       snd_info_buffer_t *buffer)
+{
+       emu10k1x_t *emu = entry->private_data;
+       char line[64];
+       unsigned int reg, channel_id , val;
+
+       while (!snd_info_get_line(buffer, line, sizeof(line))) {
+               if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+                       continue;
+
+               if ((reg < 0x49) && (reg >=0) && (val <= 0xffffffff) 
+                   && (channel_id >=0) && (channel_id <= 2) )
+                       snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
+       }
+}
+
+static int __devinit snd_emu10k1x_proc_init(emu10k1x_t * emu)
+{
+       snd_info_entry_t *entry;
+       
+       if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) {
+               snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1x_proc_reg_read);
+               entry->c.text.write_size = 64;
+               entry->c.text.write = snd_emu10k1x_proc_reg_write;
+               entry->private_data = emu;
+       }
+       
+       return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_get(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+       emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = (snd_emu10k1x_ptr_read(emu, SPDIF_SELECT, 0) == 0x700) ? 0 : 1;
+
+       return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_put(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+       emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int val;
+       int change = 0;
+
+       val = ucontrol->value.integer.value[0] ;
+
+       if (val) {
+               // enable spdif output
+               snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x000);
+               snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x700);
+               snd_emu10k1x_gpio_write(emu, 0x1000);
+       } else {
+               // disable spdif output
+               snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x700);
+               snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x1003F);
+               snd_emu10k1x_gpio_write(emu, 0x1080);
+       }
+       return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1x_shared_spdif __devinitdata =
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name =         "Analog/Digital Output Jack",
+       .info =         snd_emu10k1x_shared_spdif_info,
+       .get =          snd_emu10k1x_shared_spdif_get,
+       .put =          snd_emu10k1x_shared_spdif_put
+};
+
+static int snd_emu10k1x_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+       uinfo->count = 1;
+       return 0;
+}
+
+static int snd_emu10k1x_spdif_get(snd_kcontrol_t * kcontrol,
+                                 snd_ctl_elem_value_t * ucontrol)
+{
+       emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+       ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+       ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+       ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+       ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+       return 0;
+}
+
+static int snd_emu10k1x_spdif_get_mask(snd_kcontrol_t * kcontrol,
+                                      snd_ctl_elem_value_t * ucontrol)
+{
+       ucontrol->value.iec958.status[0] = 0xff;
+       ucontrol->value.iec958.status[1] = 0xff;
+       ucontrol->value.iec958.status[2] = 0xff;
+       ucontrol->value.iec958.status[3] = 0xff;
+       return 0;
+}
+
+static int snd_emu10k1x_spdif_put(snd_kcontrol_t * kcontrol,
+                                 snd_ctl_elem_value_t * ucontrol)
+{
+       emu10k1x_t *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       int change;
+       unsigned int val;
+
+       val = (ucontrol->value.iec958.status[0] << 0) |
+               (ucontrol->value.iec958.status[1] << 8) |
+               (ucontrol->value.iec958.status[2] << 16) |
+               (ucontrol->value.iec958.status[3] << 24);
+       change = val != emu->spdif_bits[idx];
+       if (change) {
+               snd_emu10k1x_ptr_write(emu, SPCS0 + idx, 0, val);
+               emu->spdif_bits[idx] = val;
+       }
+       return change;
+}
+
+static snd_kcontrol_new_t snd_emu10k1x_spdif_mask_control =
+{
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ,
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+       .count =        3,
+       .info =         snd_emu10k1x_spdif_info,
+       .get =          snd_emu10k1x_spdif_get_mask
+};
+
+static snd_kcontrol_new_t snd_emu10k1x_spdif_control =
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+       .count =        3,
+       .info =         snd_emu10k1x_spdif_info,
+       .get =          snd_emu10k1x_spdif_get,
+       .put =          snd_emu10k1x_spdif_put
+};
+
+static int __devinit snd_emu10k1x_mixer(emu10k1x_t *emu)
+{
+       int err;
+       snd_kcontrol_t *kctl;
+       snd_card_t *card = emu->card;
+
+       if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu)) == NULL)
+               return -ENOMEM;
+       if ((err = snd_ctl_add(card, kctl)))
+               return err;
+       if ((kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu)) == NULL)
+               return -ENOMEM;
+       if ((err = snd_ctl_add(card, kctl)))
+               return err;
+       if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu)) == NULL)
+               return -ENOMEM;
+       if ((err = snd_ctl_add(card, kctl)))
+               return err;
+
+       return 0;
+}
+
+#define EMU10K1X_MIDI_MODE_INPUT       (1<<0)
+#define EMU10K1X_MIDI_MODE_OUTPUT      (1<<1)
+
+static inline unsigned char mpu401_read(emu10k1x_t *emu, emu10k1x_midi_t *mpu, int idx)
+{
+       return (unsigned char)snd_emu10k1x_ptr_read(emu, mpu->port + idx, 0);
+}
+
+static inline void mpu401_write(emu10k1x_t *emu, emu10k1x_midi_t *mpu, int data, int idx)
+{
+       snd_emu10k1x_ptr_write(emu, mpu->port + idx, 0, data);
+}
+
+#define mpu401_write_data(emu, mpu, data)      mpu401_write(emu, mpu, data, 0)
+#define mpu401_write_cmd(emu, mpu, data)       mpu401_write(emu, mpu, data, 1)
+#define mpu401_read_data(emu, mpu)             mpu401_read(emu, mpu, 0)
+#define mpu401_read_stat(emu, mpu)             mpu401_read(emu, mpu, 1)
+
+#define mpu401_input_avail(emu,mpu)    (!(mpu401_read_stat(emu,mpu) & 0x80))
+#define mpu401_output_ready(emu,mpu)   (!(mpu401_read_stat(emu,mpu) & 0x40))
+
+#define MPU401_RESET           0xff
+#define MPU401_ENTER_UART      0x3f
+#define MPU401_ACK             0xfe
+
+static void mpu401_clear_rx(emu10k1x_t *emu, emu10k1x_midi_t *mpu)
+{
+       int timeout = 100000;
+       for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
+               mpu401_read_data(emu, mpu);
+#ifdef CONFIG_SND_DEBUG
+       if (timeout <= 0)
+               snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
+#endif
+}
+
+/*
+
+ */
+
+static void do_emu10k1x_midi_interrupt(emu10k1x_t *emu, emu10k1x_midi_t *midi, unsigned int status)
+{
+       unsigned char byte;
+
+       if (midi->rmidi == NULL) {
+               snd_emu10k1x_intr_disable(emu, midi->tx_enable | midi->rx_enable);
+               return;
+       }
+
+       spin_lock(&midi->input_lock);
+       if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+               if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+                       mpu401_clear_rx(emu, midi);
+               } else {
+                       byte = mpu401_read_data(emu, midi);
+                       spin_unlock(&midi->input_lock);
+                       if (midi->substream_input)
+                               snd_rawmidi_receive(midi->substream_input, &byte, 1);
+                       spin_lock(&midi->input_lock);
+               }
+       }
+       spin_unlock(&midi->input_lock);
+
+       spin_lock(&midi->output_lock);
+       if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+               if (midi->substream_output &&
+                   snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+                       mpu401_write_data(emu, midi, byte);
+               } else {
+                       snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+               }
+       }
+       spin_unlock(&midi->output_lock);
+}
+
+static void snd_emu10k1x_midi_interrupt(emu10k1x_t *emu, unsigned int status)
+{
+       do_emu10k1x_midi_interrupt(emu, &emu->midi, status);
+}
+
+static void snd_emu10k1x_midi_cmd(emu10k1x_t * emu, emu10k1x_midi_t *midi, unsigned char cmd, int ack)
+{
+       unsigned long flags;
+       int timeout, ok;
+
+       spin_lock_irqsave(&midi->input_lock, flags);
+       mpu401_write_data(emu, midi, 0x00);
+       /* mpu401_clear_rx(emu, midi); */
+
+       mpu401_write_cmd(emu, midi, cmd);
+       if (ack) {
+               ok = 0;
+               timeout = 10000;
+               while (!ok && timeout-- > 0) {
+                       if (mpu401_input_avail(emu, midi)) {
+                               if (mpu401_read_data(emu, midi) == MPU401_ACK)
+                                       ok = 1;
+                       }
+               }
+               if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+                       ok = 1;
+       } else {
+               ok = 1;
+       }
+       spin_unlock_irqrestore(&midi->input_lock, flags);
+       if (!ok)
+               snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+                          cmd, emu->port,
+                          mpu401_read_stat(emu, midi),
+                          mpu401_read_data(emu, midi));
+}
+
+static int snd_emu10k1x_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+       emu10k1x_t *emu;
+       emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+       unsigned long flags;
+       
+       emu = midi->emu;
+       snd_assert(emu, return -ENXIO);
+       spin_lock_irqsave(&midi->open_lock, flags);
+       midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;
+       midi->substream_input = substream;
+       if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
+               spin_unlock_irqrestore(&midi->open_lock, flags);
+               snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1);
+               snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+       } else {
+               spin_unlock_irqrestore(&midi->open_lock, flags);
+       }
+       return 0;
+}
+
+static int snd_emu10k1x_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+       emu10k1x_t *emu;
+       emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+       unsigned long flags;
+
+       emu = midi->emu;
+       snd_assert(emu, return -ENXIO);
+       spin_lock_irqsave(&midi->open_lock, flags);
+       midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;
+       midi->substream_output = substream;
+       if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+               spin_unlock_irqrestore(&midi->open_lock, flags);
+               snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1);
+               snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1);
+       } else {
+               spin_unlock_irqrestore(&midi->open_lock, flags);
+       }
+       return 0;
+}
+
+static int snd_emu10k1x_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+       emu10k1x_t *emu;
+       emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+       unsigned long flags;
+
+       emu = midi->emu;
+       snd_assert(emu, return -ENXIO);
+       spin_lock_irqsave(&midi->open_lock, flags);
+       snd_emu10k1x_intr_disable(emu, midi->rx_enable);
+       midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT;
+       midi->substream_input = NULL;
+       if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
+               spin_unlock_irqrestore(&midi->open_lock, flags);
+               snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
+       } else {
+               spin_unlock_irqrestore(&midi->open_lock, flags);
+       }
+       return 0;
+}
+
+static int snd_emu10k1x_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+       emu10k1x_t *emu;
+       emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+       unsigned long flags;
+
+       emu = midi->emu;
+       snd_assert(emu, return -ENXIO);
+       spin_lock_irqsave(&midi->open_lock, flags);
+       snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+       midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT;
+       midi->substream_output = NULL;
+       if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+               spin_unlock_irqrestore(&midi->open_lock, flags);
+               snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
+       } else {
+               spin_unlock_irqrestore(&midi->open_lock, flags);
+       }
+       return 0;
+}
+
+static void snd_emu10k1x_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+       emu10k1x_t *emu;
+       emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+       emu = midi->emu;
+       snd_assert(emu, return);
+
+       if (up)
+               snd_emu10k1x_intr_enable(emu, midi->rx_enable);
+       else
+               snd_emu10k1x_intr_disable(emu, midi->rx_enable);
+}
+
+static void snd_emu10k1x_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+       emu10k1x_t *emu;
+       emu10k1x_midi_t *midi = (emu10k1x_midi_t *)substream->rmidi->private_data;
+       unsigned long flags;
+
+       emu = midi->emu;
+       snd_assert(emu, return);
+
+       if (up) {
+               int max = 4;
+               unsigned char byte;
+       
+               /* try to send some amount of bytes here before interrupts */
+               spin_lock_irqsave(&midi->output_lock, flags);
+               while (max > 0) {
+                       if (mpu401_output_ready(emu, midi)) {
+                               if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT) ||
+                                   snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+                                       /* no more data */
+                                       spin_unlock_irqrestore(&midi->output_lock, flags);
+                                       return;
+                               }
+                               mpu401_write_data(emu, midi, byte);
+                               max--;
+                       } else {
+                               break;
+                       }
+               }
+               spin_unlock_irqrestore(&midi->output_lock, flags);
+               snd_emu10k1x_intr_enable(emu, midi->tx_enable);
+       } else {
+               snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+       }
+}
+
+/*
+
+ */
+
+static snd_rawmidi_ops_t snd_emu10k1x_midi_output =
+{
+       .open =         snd_emu10k1x_midi_output_open,
+       .close =        snd_emu10k1x_midi_output_close,
+       .trigger =      snd_emu10k1x_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_emu10k1x_midi_input =
+{
+       .open =         snd_emu10k1x_midi_input_open,
+       .close =        snd_emu10k1x_midi_input_close,
+       .trigger =      snd_emu10k1x_midi_input_trigger,
+};
+
+static void snd_emu10k1x_midi_free(snd_rawmidi_t *rmidi)
+{
+       emu10k1x_midi_t *midi = (emu10k1x_midi_t *)rmidi->private_data;
+       midi->interrupt = NULL;
+       midi->rmidi = NULL;
+}
+
+static int __devinit emu10k1x_midi_init(emu10k1x_t *emu, emu10k1x_midi_t *midi, int device, char *name)
+{
+       snd_rawmidi_t *rmidi;
+       int err;
+
+       if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+               return err;
+       midi->emu = emu;
+       spin_lock_init(&midi->open_lock);
+       spin_lock_init(&midi->input_lock);
+       spin_lock_init(&midi->output_lock);
+       strcpy(rmidi->name, name);
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output);
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input);
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+                            SNDRV_RAWMIDI_INFO_INPUT |
+                            SNDRV_RAWMIDI_INFO_DUPLEX;
+       rmidi->private_data = midi;
+       rmidi->private_free = snd_emu10k1x_midi_free;
+       midi->rmidi = rmidi;
+       return 0;
+}
+
+static int __devinit snd_emu10k1x_midi(emu10k1x_t *emu)
+{
+       emu10k1x_midi_t *midi = &emu->midi;
+       int err;
+
+       if ((err = emu10k1x_midi_init(emu, midi, 0, "EMU10K1X MPU-401 (UART)")) < 0)
+               return err;
+
+       midi->tx_enable = INTE_MIDITXENABLE;
+       midi->rx_enable = INTE_MIDIRXENABLE;
+       midi->port = MUDATA;
+       midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+       midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+       midi->interrupt = snd_emu10k1x_midi_interrupt;
+       return 0;
+}
+
+static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
+                                       const struct pci_device_id *pci_id)
+{
+       static int dev;
+       snd_card_t *card;
+       emu10k1x_t *chip;
+       int err;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+       if (card == NULL)
+               return -ENOMEM;
+
+       if ((err = snd_emu10k1x_create(card, pci, &chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       if ((err = snd_emu10k1x_pcm(chip, 0, NULL)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       if ((err = snd_emu10k1x_pcm(chip, 1, NULL)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       if ((err = snd_emu10k1x_pcm(chip, 2, NULL)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       if ((err = snd_emu10k1x_ac97(chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       if ((err = snd_emu10k1x_mixer(chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       
+       if ((err = snd_emu10k1x_midi(chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       snd_emu10k1x_proc_init(chip);
+
+       strcpy(card->driver, "EMU10K1X");
+       strcpy(card->shortname, "Dell Sound Blaster Live!");
+       sprintf(card->longname, "%s at 0x%lx irq %i",
+               card->shortname, chip->port, chip->irq);
+
+       if ((err = snd_card_register(card)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       pci_set_drvdata(pci, card);
+       dev++;
+       return 0;
+}
+
+static void __devexit snd_emu10k1x_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+
+// PCI IDs
+static struct pci_device_id snd_emu10k1x_ids[] = {
+       { 0x1102, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },    /* Dell OEM version (EMU10K1) */
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids);
+
+// pci_driver definition
+static struct pci_driver driver = {
+       .name = "EMU10K1X",
+       .id_table = snd_emu10k1x_ids,
+       .probe = snd_emu10k1x_probe,
+       .remove = __devexit_p(snd_emu10k1x_remove),
+};
+
+// initialization of the module
+static int __init alsa_card_emu10k1x_init(void)
+{
+       int err;
+
+       if ((err = pci_module_init(&driver)) > 0)
+               return err;
+
+       return 0;
+}
+
+// clean up the module
+static void __exit alsa_card_emu10k1x_exit(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_emu10k1x_init)
+module_exit(alsa_card_emu10k1x_exit)
diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c
new file mode 100644 (file)
index 0000000..c9c421a
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  Copyright (c) by Lee Revell <rlrevell@joe-job.com>
+ *  
+ *  Routines for control of EMU10K1 chips
+ *
+ *  Copied from similar code by Clemens Ladisch in the ymfpci driver
+ * 
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static int snd_emu10k1_timer_start(snd_timer_t *timer)
+{
+       emu10k1_t *emu;
+       unsigned long flags;
+       unsigned int delay;
+
+       emu = snd_timer_chip(timer);
+       delay = timer->sticks - 1;
+       if (delay < 5 ) /* minimum time is 5 ticks */
+               delay = 5;
+       spin_lock_irqsave(&emu->reg_lock, flags);
+       snd_emu10k1_intr_enable(emu, INTE_INTERVALTIMERENB);
+       outw(delay & TIMER_RATE_MASK, emu->port + TIMER);
+       spin_unlock_irqrestore(&emu->reg_lock, flags);
+       return 0;
+}
+
+static int snd_emu10k1_timer_stop(snd_timer_t *timer)
+{
+       emu10k1_t *emu;
+       unsigned long flags;
+
+       emu = snd_timer_chip(timer);
+       spin_lock_irqsave(&emu->reg_lock, flags);
+       snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
+       spin_unlock_irqrestore(&emu->reg_lock, flags);
+       return 0;
+}
+
+static int snd_emu10k1_timer_precise_resolution(snd_timer_t *timer,
+                                              unsigned long *num, unsigned long *den)
+{
+       *num = 1;
+       *den = 48000;
+       return 0;
+}
+
+static struct _snd_timer_hardware snd_emu10k1_timer_hw = {
+       .flags = SNDRV_TIMER_HW_AUTO,
+       .resolution = 20833, /* 1 sample @ 48KHZ = 20.833...us */
+       .ticks = 1024,
+       .start = snd_emu10k1_timer_start,
+       .stop = snd_emu10k1_timer_stop,
+       .precise_resolution = snd_emu10k1_timer_precise_resolution,
+};
+
+int __devinit snd_emu10k1_timer(emu10k1_t *emu, int device)
+{
+       snd_timer_t *timer = NULL;
+       snd_timer_id_t tid;
+       int err;
+
+       tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+       tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+       tid.card = emu->card->number;
+       tid.device = device;
+       tid.subdevice = 0;
+       if ((err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer)) >= 0) {
+               strcpy(timer->name, "EMU10K1 timer");
+               timer->private_data = emu;
+               timer->hw = snd_emu10k1_timer_hw;
+       }
+       emu->timer = timer;
+       return err;
+}
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
new file mode 100644 (file)
index 0000000..c8f59a2
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ *   Lowlevel functions for AudioTrak Prodigy 192 cards
+ *
+ *     Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
+ *      Copyright (c) 2003 Dimitromanolakis Apostolos <apostol@cs.utoronto.ca>
+ *      Copyright (c) 2004 Kouichi ONO <co2b@ceres.dti.ne.jp>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "prodigy192.h"
+#include "stac946x.h"
+
+static void stac9460_put(ice1712_t *ice, int reg, unsigned char val)
+{
+       snd_vt1724_write_i2c(ice, PRODIGY192_STAC9460_ADDR, reg, val);
+}
+
+static unsigned char stac9460_get(ice1712_t *ice, int reg)
+{
+       return snd_vt1724_read_i2c(ice, PRODIGY192_STAC9460_ADDR, reg);
+}
+
+/*
+ * DAC mute control
+ */
+static int stac9460_dac_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int stac9460_dac_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       unsigned char val;
+       int idx;
+
+       if (kcontrol->private_value)
+               idx = STAC946X_MASTER_VOLUME;
+       else
+               idx  = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
+       val = stac9460_get(ice, idx);
+       ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
+       return 0;
+}
+
+static int stac9460_dac_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       unsigned char new, old;
+       int idx;
+       int change;
+
+       if (kcontrol->private_value)
+               idx = STAC946X_MASTER_VOLUME;
+       else
+               idx  = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
+       old = stac9460_get(ice, idx);
+       new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80);
+       change = (new != old);
+       if (change)
+               stac9460_put(ice, idx, new);
+
+       return change;
+}
+
+/*
+ * DAC volume attenuation mixer control
+ */
+static int stac9460_dac_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;                   /* mute */
+       uinfo->value.integer.max = 0x7f;                /* 0dB */
+       return 0;
+}
+
+static int stac9460_dac_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int idx;
+       unsigned char vol;
+
+       if (kcontrol->private_value)
+               idx = STAC946X_MASTER_VOLUME;
+       else
+               idx  = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
+       vol = stac9460_get(ice, idx) & 0x7f;
+       ucontrol->value.integer.value[0] = 0x7f - vol;
+
+       return 0;
+}
+
+static int stac9460_dac_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int idx;
+       unsigned char tmp, ovol, nvol;
+       int change;
+
+       if (kcontrol->private_value)
+               idx = STAC946X_MASTER_VOLUME;
+       else
+               idx  = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
+       nvol = ucontrol->value.integer.value[0];
+       tmp = stac9460_get(ice, idx);
+       ovol = 0x7f - (tmp & 0x7f);
+       change = (ovol != nvol);
+       if (change) {
+               stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
+       }
+       return change;
+}
+
+/*
+ * ADC mute control
+ */
+static int stac9460_adc_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int stac9460_adc_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       unsigned char val;
+       int i;
+
+       for (i = 0; i < 2; ++i) {
+               val = stac9460_get(ice, STAC946X_MIC_L_VOLUME + i);
+               ucontrol->value.integer.value[i] = ~val>>7 & 0x1;
+       }
+
+       return 0;
+}
+
+static int stac9460_adc_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       unsigned char new, old;
+       int i, reg;
+       int change;
+
+       for (i = 0; i < 2; ++i) {
+               reg = STAC946X_MIC_L_VOLUME + i;
+               old = stac9460_get(ice, reg);
+               new = (~ucontrol->value.integer.value[i]<<7&0x80) | (old&~0x80);
+               change = (new != old);
+               if (change)
+                       stac9460_put(ice, reg, new);
+       }
+
+       return change;
+}
+
+/*
+ * ADC gain mixer control
+ */
+static int stac9460_adc_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;           /* 0dB */
+       uinfo->value.integer.max = 0x0f;        /* 22.5dB */
+       return 0;
+}
+
+static int stac9460_adc_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int i, reg;
+       unsigned char vol;
+
+       for (i = 0; i < 2; ++i) {
+               reg = STAC946X_MIC_L_VOLUME + i;
+               vol = stac9460_get(ice, reg) & 0x0f;
+               ucontrol->value.integer.value[i] = 0x0f - vol;
+       }
+
+       return 0;
+}
+
+static int stac9460_adc_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int i, reg;
+       unsigned char ovol, nvol;
+       int change;
+
+       for (i = 0; i < 2; ++i) {
+               reg = STAC946X_MIC_L_VOLUME + i;
+               nvol = ucontrol->value.integer.value[i];
+               ovol = 0x0f - stac9460_get(ice, reg);
+               change = ((ovol & 0x0f)  != nvol);
+               if (change)
+                       stac9460_put(ice, reg, (0x0f - nvol) | (ovol & ~0x0f));
+       }
+
+       return change;
+}
+
+#if 0
+/*
+ * Headphone Amplifier
+ */
+static int aureon_set_headphone_amp(ice1712_t *ice, int enable)
+{
+       unsigned int tmp, tmp2;
+
+       tmp2 = tmp = snd_ice1712_gpio_read(ice);
+       if (enable)
+               tmp |= AUREON_HP_SEL;
+       else
+               tmp &= ~ AUREON_HP_SEL;
+       if (tmp != tmp2) {
+               snd_ice1712_gpio_write(ice, tmp);
+               return 1;
+       }
+       return 0;
+}
+
+static int aureon_get_headphone_amp(ice1712_t *ice)
+{
+       unsigned int tmp = snd_ice1712_gpio_read(ice);
+
+       return ( tmp & AUREON_HP_SEL )!= 0;
+}
+
+static int aureon_bool_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int aureon_hpamp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = aureon_get_headphone_amp(ice);
+       return 0;
+}
+
+
+static int aureon_hpamp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+       return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]);
+}
+
+/*
+ * Deemphasis
+ */
+static int aureon_deemp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
+       return 0;
+}
+
+static int aureon_deemp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int temp, temp2;
+       temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
+       if (ucontrol->value.integer.value[0])
+               temp |= 0xf;
+       else
+               temp &= ~0xf;
+       if (temp != temp2) {
+               wm_put(ice, WM_DAC_CTRL2, temp);
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * ADC Oversampling
+ */
+static int aureon_oversampling_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
+{
+       static char *texts[2] = { "128x", "64x" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+
+        return 0;
+}
+
+static int aureon_oversampling_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
+       return 0;
+}
+
+static int aureon_oversampling_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       int temp, temp2;
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+       temp2 = temp = wm_get(ice, WM_MASTER);
+
+       if (ucontrol->value.enumerated.item[0])
+               temp |= 0x8;
+       else
+               temp &= ~0x8;
+
+       if (temp != temp2) {
+               wm_put(ice, WM_MASTER, temp);
+               return 1;
+       }
+       return 0;
+}
+#endif
+
+/*
+ * mixers
+ */
+
+static snd_kcontrol_new_t stac_controls[] __devinitdata = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = stac9460_dac_mute_info,
+               .get = stac9460_dac_mute_get,
+               .put = stac9460_dac_mute_put,
+               .private_value = 1,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Volume",
+               .info = stac9460_dac_vol_info,
+               .get = stac9460_dac_vol_get,
+               .put = stac9460_dac_vol_put,
+               .private_value = 1,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "DAC Switch",
+               .count = 6,
+               .info = stac9460_dac_mute_info,
+               .get = stac9460_dac_mute_get,
+               .put = stac9460_dac_mute_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "DAC Volume",
+               .count = 6,
+               .info = stac9460_dac_vol_info,
+               .get = stac9460_dac_vol_get,
+               .put = stac9460_dac_vol_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "ADC Switch",
+               .count = 1,
+               .info = stac9460_adc_mute_info,
+               .get = stac9460_adc_mute_get,
+               .put = stac9460_adc_mute_put,
+
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "ADC Volume",
+               .count = 1,
+               .info = stac9460_adc_vol_info,
+               .get = stac9460_adc_vol_get,
+               .put = stac9460_adc_vol_put,
+       },
+#if 0
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Route",
+               .info = wm_adc_mux_info,
+               .get = wm_adc_mux_get,
+               .put = wm_adc_mux_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Headphone Amplifier Switch",
+               .info = aureon_bool_info,
+               .get = aureon_hpamp_get,
+               .put = aureon_hpamp_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "DAC Deemphasis Switch",
+               .info = aureon_bool_info,
+               .get = aureon_deemp_get,
+               .put = aureon_deemp_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "ADC Oversampling",
+               .info = aureon_oversampling_info,
+               .get = aureon_oversampling_get,
+               .put = aureon_oversampling_put
+       },
+#endif
+};
+
+static int __devinit prodigy192_add_controls(ice1712_t *ice)
+{
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < ARRAY_SIZE(stac_controls); i++) {
+               err = snd_ctl_add(ice->card, snd_ctl_new1(&stac_controls[i], ice));
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+
+/*
+ * initialize the chip
+ */
+static int __devinit prodigy192_init(ice1712_t *ice)
+{
+       static unsigned short stac_inits_prodigy[] = {
+               STAC946X_RESET, 0,
+/*             STAC946X_MASTER_VOLUME, 0,
+               STAC946X_LF_VOLUME, 0,
+               STAC946X_RF_VOLUME, 0,
+               STAC946X_LR_VOLUME, 0,
+               STAC946X_RR_VOLUME, 0,
+               STAC946X_CENTER_VOLUME, 0,
+               STAC946X_LFE_VOLUME, 0,*/
+               (unsigned short)-1
+       };
+       unsigned short *p;
+
+       /* prodigy 192 */
+       ice->num_total_dacs = 6;
+       ice->num_total_adcs = 2;
+       
+       /* initialize codec */
+       p = stac_inits_prodigy;
+       for (; *p != (unsigned short)-1; p += 2)
+               stac9460_put(ice, p[0], p[1]);
+
+       return 0;
+}
+
+
+/*
+ * Aureon boards don't provide the EEPROM data except for the vendor IDs.
+ * hence the driver needs to sets up it properly.
+ */
+
+static unsigned char prodigy71_eeprom[] __devinitdata = {
+       0x2b,   /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */
+       0x80,   /* ACLINK: I2S */
+       0xf8,   /* I2S: vol, 96k, 24bit, 192k */
+       0xc3,   /* SPDIF: out-en, out-int, spdif-in */
+       0xff,   /* GPIO_DIR */
+       0xff,   /* GPIO_DIR1 */
+       0xbf,   /* GPIO_DIR2 */
+       0x00,   /* GPIO_MASK */
+       0x00,   /* GPIO_MASK1 */
+       0x00,   /* GPIO_MASK2 */
+       0x00,   /* GPIO_STATE */
+       0x00,   /* GPIO_STATE1 */
+       0x00,   /* GPIO_STATE2 */
+};
+
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
+       {
+               .subvendor = VT1724_SUBDEVICE_PRODIGY192VE,
+               .name = "Audiotrak Prodigy 192",
+               .model = "prodigy192",
+               .chip_init = prodigy192_init,
+               .build_controls = prodigy192_add_controls,
+               .eeprom_size = sizeof(prodigy71_eeprom),
+               .eeprom_data = prodigy71_eeprom,
+       },
+       { } /* terminator */
+};
diff --git a/sound/pci/ice1712/prodigy192.h b/sound/pci/ice1712/prodigy192.h
new file mode 100644 (file)
index 0000000..94c824e
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __SOUND_PRODIGY192_H
+#define __SOUND_PRODIGY192_H
+
+#define PRODIGY192_DEVICE_DESC                "{AudioTrak,Prodigy 192},"
+#define PRODIGY192_STAC9460_ADDR       0x54
+
+#define VT1724_SUBDEVICE_PRODIGY192VE   0x34495345     /* PRODIGY 192 VE */
+
+extern struct snd_ice1712_card_info  snd_vt1724_prodigy192_cards[];
+
+#endif /* __SOUND_PRODIGY192_H */
diff --git a/sound/pci/ice1712/stac946x.h b/sound/pci/ice1712/stac946x.h
new file mode 100644 (file)
index 0000000..5b39095
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef __SOUND_STAC946X_H
+#define __SOUND_STAC946X_H
+
+#define STAC946X_RESET                 0x00
+#define STAC946X_STATUS                        0x01
+#define STAC946X_MASTER_VOLUME         0x02
+#define STAC946X_LF_VOLUME             0x03
+#define STAC946X_RF_VOLUME             0x04
+#define STAC946X_LR_VOLUME             0x05
+#define STAC946X_RR_VOLUME             0x06
+#define STAC946X_CENTER_VOLUME         0x07
+#define STAC946X_LFE_VOLUME            0x08
+#define STAC946X_MIC_L_VOLUME          0x09
+#define STAC946X_MIC_R_VOLUME          0x0a
+#define STAC946X_DEEMPHASIS            0x0c
+#define STAC946X_GENERAL_PURPOSE       0x0d
+#define STAC946X_AUDIO_PORT_CONTROL    0x0e
+#define STAC946X_MASTER_CLOCKING       0x0f
+#define STAC946X_POWERDOWN_CTRL1       0x10
+#define STAC946X_POWERDOWN_CTRL2       0x11
+#define STAC946X_REVISION_CODE         0x12
+#define STAC946X_ADDRESS_CONTROL       0x13
+#define STAC946X_ADDRESS               0x14
+
+#endif  /*  __SOUND_STAC946X_H */
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
new file mode 100644 (file)
index 0000000..0b5f2ad
--- /dev/null
@@ -0,0 +1,1256 @@
+/*
+ *   ALSA modem driver for VIA VT82xx (South Bridge)
+ *
+ *   VT82C686A/B/C, VT8233A/C, VT8235
+ *
+ *     Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *                        Tjeerd.Mulder <Tjeerd.Mulder@fujitsu-siemens.com>
+ *                    2002 Takashi Iwai <tiwai@suse.de>
+ *
+ *   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
+ *
+ */
+
+/*
+ * Changes:
+ *
+ * Sep. 2,  2004  Sasha Khapyorsky <sashak@smlink.com>
+ *      Modified from original audio driver 'via82xx.c' to support AC97
+ *      modems.
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+#if 0
+#define POINTER_DEBUG
+#endif
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("VIA VT82xx modem");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C modem,pci}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;     /* Enable this card */
+static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable modem part of VIA 82xx bridge.");
+module_param_array(ac97_clock, int, NULL, 0444);
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
+
+
+/*
+ *  Direct registers
+ */
+
+#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
+#define VIADEV_REG(viadev, x) ((viadev)->port + VIA_REG_##x)
+
+/* common offsets */
+#define VIA_REG_OFFSET_STATUS          0x00    /* byte - channel status */
+#define   VIA_REG_STAT_ACTIVE          0x80    /* RO */
+#define   VIA_REG_STAT_PAUSED          0x40    /* RO */
+#define   VIA_REG_STAT_TRIGGER_QUEUED  0x08    /* RO */
+#define   VIA_REG_STAT_STOPPED         0x04    /* RWC */
+#define   VIA_REG_STAT_EOL             0x02    /* RWC */
+#define   VIA_REG_STAT_FLAG            0x01    /* RWC */
+#define VIA_REG_OFFSET_CONTROL         0x01    /* byte - channel control */
+#define   VIA_REG_CTRL_START           0x80    /* WO */
+#define   VIA_REG_CTRL_TERMINATE       0x40    /* WO */
+#define   VIA_REG_CTRL_AUTOSTART       0x20
+#define   VIA_REG_CTRL_PAUSE           0x08    /* RW */
+#define   VIA_REG_CTRL_INT_STOP                0x04            
+#define   VIA_REG_CTRL_INT_EOL         0x02
+#define   VIA_REG_CTRL_INT_FLAG                0x01
+#define   VIA_REG_CTRL_RESET           0x01    /* RW - probably reset? undocumented */
+#define   VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART)
+#define VIA_REG_OFFSET_TYPE            0x02    /* byte - channel type (686 only) */
+#define   VIA_REG_TYPE_AUTOSTART       0x80    /* RW - autostart at EOL */
+#define   VIA_REG_TYPE_16BIT           0x20    /* RW */
+#define   VIA_REG_TYPE_STEREO          0x10    /* RW */
+#define   VIA_REG_TYPE_INT_LLINE       0x00
+#define   VIA_REG_TYPE_INT_LSAMPLE     0x04
+#define   VIA_REG_TYPE_INT_LESSONE     0x08
+#define   VIA_REG_TYPE_INT_MASK                0x0c
+#define   VIA_REG_TYPE_INT_EOL         0x02
+#define   VIA_REG_TYPE_INT_FLAG                0x01
+#define VIA_REG_OFFSET_TABLE_PTR       0x04    /* dword - channel table pointer */
+#define VIA_REG_OFFSET_CURR_PTR                0x04    /* dword - channel current pointer */
+#define VIA_REG_OFFSET_STOP_IDX                0x08    /* dword - stop index, channel type, sample rate */
+#define VIA_REG_OFFSET_CURR_COUNT      0x0c    /* dword - channel current count (24 bit) */
+#define VIA_REG_OFFSET_CURR_INDEX      0x0f    /* byte - channel current index (for via8233 only) */
+
+#define DEFINE_VIA_REGSET(name,val) \
+enum {\
+       VIA_REG_##name##_STATUS         = (val),\
+       VIA_REG_##name##_CONTROL        = (val) + 0x01,\
+       VIA_REG_##name##_TYPE           = (val) + 0x02,\
+       VIA_REG_##name##_TABLE_PTR      = (val) + 0x04,\
+       VIA_REG_##name##_CURR_PTR       = (val) + 0x04,\
+       VIA_REG_##name##_STOP_IDX       = (val) + 0x08,\
+       VIA_REG_##name##_CURR_COUNT     = (val) + 0x0c,\
+}
+
+/* modem block */
+DEFINE_VIA_REGSET(MO, 0x40);
+DEFINE_VIA_REGSET(MI, 0x50);
+
+/* AC'97 */
+#define VIA_REG_AC97                   0x80    /* dword */
+#define   VIA_REG_AC97_CODEC_ID_MASK   (3<<30)
+#define   VIA_REG_AC97_CODEC_ID_SHIFT  30
+#define   VIA_REG_AC97_CODEC_ID_PRIMARY        0x00
+#define   VIA_REG_AC97_CODEC_ID_SECONDARY 0x01
+#define   VIA_REG_AC97_SECONDARY_VALID (1<<27)
+#define   VIA_REG_AC97_PRIMARY_VALID   (1<<25)
+#define   VIA_REG_AC97_BUSY            (1<<24)
+#define   VIA_REG_AC97_READ            (1<<23)
+#define   VIA_REG_AC97_CMD_SHIFT       16
+#define   VIA_REG_AC97_CMD_MASK                0x7e
+#define   VIA_REG_AC97_DATA_SHIFT      0
+#define   VIA_REG_AC97_DATA_MASK       0xffff
+
+#define VIA_REG_SGD_SHADOW             0x84    /* dword */
+#define   VIA_REG_SGD_STAT_PB_FLAG     (1<<0)
+#define   VIA_REG_SGD_STAT_CP_FLAG     (1<<1)
+#define   VIA_REG_SGD_STAT_FM_FLAG     (1<<2)
+#define   VIA_REG_SGD_STAT_PB_EOL      (1<<4)
+#define   VIA_REG_SGD_STAT_CP_EOL      (1<<5)
+#define   VIA_REG_SGD_STAT_FM_EOL      (1<<6)
+#define   VIA_REG_SGD_STAT_PB_STOP     (1<<8)
+#define   VIA_REG_SGD_STAT_CP_STOP     (1<<9)
+#define   VIA_REG_SGD_STAT_FM_STOP     (1<<10)
+#define   VIA_REG_SGD_STAT_PB_ACTIVE   (1<<12)
+#define   VIA_REG_SGD_STAT_CP_ACTIVE   (1<<13)
+#define   VIA_REG_SGD_STAT_FM_ACTIVE   (1<<14)
+#define   VIA_REG_SGD_STAT_MR_FLAG      (1<<16)
+#define   VIA_REG_SGD_STAT_MW_FLAG      (1<<17)
+#define   VIA_REG_SGD_STAT_MR_EOL       (1<<20)
+#define   VIA_REG_SGD_STAT_MW_EOL       (1<<21)
+#define   VIA_REG_SGD_STAT_MR_STOP      (1<<24)
+#define   VIA_REG_SGD_STAT_MW_STOP      (1<<25)
+#define   VIA_REG_SGD_STAT_MR_ACTIVE    (1<<28)
+#define   VIA_REG_SGD_STAT_MW_ACTIVE    (1<<29)
+
+#define VIA_REG_GPI_STATUS             0x88
+#define VIA_REG_GPI_INTR               0x8c
+
+#define VIA_TBL_BIT_FLAG       0x40000000
+#define VIA_TBL_BIT_EOL                0x80000000
+
+/* pci space */
+#define VIA_ACLINK_STAT                0x40
+#define  VIA_ACLINK_C11_READY  0x20
+#define  VIA_ACLINK_C10_READY  0x10
+#define  VIA_ACLINK_C01_READY  0x04 /* secondary codec ready */
+#define  VIA_ACLINK_LOWPOWER   0x02 /* low-power state */
+#define  VIA_ACLINK_C00_READY  0x01 /* primary codec ready */
+#define VIA_ACLINK_CTRL                0x41
+#define  VIA_ACLINK_CTRL_ENABLE        0x80 /* 0: disable, 1: enable */
+#define  VIA_ACLINK_CTRL_RESET 0x40 /* 0: assert, 1: de-assert */
+#define  VIA_ACLINK_CTRL_SYNC  0x20 /* 0: release SYNC, 1: force SYNC hi */
+#define  VIA_ACLINK_CTRL_SDO   0x10 /* 0: release SDO, 1: force SDO hi */
+#define  VIA_ACLINK_CTRL_VRA   0x08 /* 0: disable VRA, 1: enable VRA */
+#define  VIA_ACLINK_CTRL_PCM   0x04 /* 0: disable PCM, 1: enable PCM */
+#define  VIA_ACLINK_CTRL_FM    0x02 /* via686 only */
+#define  VIA_ACLINK_CTRL_SB    0x01 /* via686 only */
+#define  VIA_ACLINK_CTRL_INIT  (VIA_ACLINK_CTRL_ENABLE|\
+                                VIA_ACLINK_CTRL_RESET|\
+                                VIA_ACLINK_CTRL_PCM)
+#define VIA_FUNC_ENABLE                0x42
+#define  VIA_FUNC_MIDI_PNP     0x80 /* FIXME: it's 0x40 in the datasheet! */
+#define  VIA_FUNC_MIDI_IRQMASK 0x40 /* FIXME: not documented! */
+#define  VIA_FUNC_RX2C_WRITE   0x20
+#define  VIA_FUNC_SB_FIFO_EMPTY        0x10
+#define  VIA_FUNC_ENABLE_GAME  0x08
+#define  VIA_FUNC_ENABLE_FM    0x04
+#define  VIA_FUNC_ENABLE_MIDI  0x02
+#define  VIA_FUNC_ENABLE_SB    0x01
+#define VIA_PNP_CONTROL                0x43
+#define VIA_MC97_CTRL          0x44
+#define  VIA_MC97_CTRL_ENABLE   0x80
+#define  VIA_MC97_CTRL_SECONDARY 0x40
+#define  VIA_MC97_CTRL_INIT     (VIA_MC97_CTRL_ENABLE|\
+                                 VIA_MC97_CTRL_SECONDARY)
+
+
+typedef struct _snd_via82xx_modem via82xx_t;
+typedef struct via_dev viadev_t;
+
+/*
+ * pcm stream
+ */
+
+struct snd_via_sg_table {
+       unsigned int offset;
+       unsigned int size;
+} ;
+
+#define VIA_TABLE_SIZE 255
+
+struct via_dev {
+       unsigned int reg_offset;
+       unsigned long port;
+       int direction;  /* playback = 0, capture = 1 */
+        snd_pcm_substream_t *substream;
+       int running;
+       unsigned int tbl_entries; /* # descriptors */
+       struct snd_dma_buffer table;
+       struct snd_via_sg_table *idx_table;
+       /* for recovery from the unexpected pointer */
+       unsigned int lastpos;
+       unsigned int bufsize;
+       unsigned int bufsize2;
+};
+
+enum { TYPE_CARD_VIA82XX_MODEM = 1 };
+
+#define VIA_MAX_MODEM_DEVS     2
+
+struct _snd_via82xx_modem {
+       int irq;
+
+       unsigned long port;
+
+       unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */
+
+       struct pci_dev *pci;
+       snd_card_t *card;
+
+       unsigned int num_devs;
+       unsigned int playback_devno, capture_devno;
+       viadev_t devs[VIA_MAX_MODEM_DEVS];
+
+       snd_pcm_t *pcms[2];
+
+       ac97_bus_t *ac97_bus;
+       ac97_t *ac97;
+       unsigned int ac97_clock;
+       unsigned int ac97_secondary;    /* secondary AC'97 codec is present */
+
+       spinlock_t reg_lock;
+       snd_info_entry_t *proc_entry;
+};
+
+static struct pci_device_id snd_via82xx_modem_ids[] = {
+       { 0x1106, 0x3068, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA82XX_MODEM, },
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_via82xx_modem_ids);
+
+/*
+ */
+
+/*
+ * allocate and initialize the descriptor buffers
+ * periods = number of periods
+ * fragsize = period size in bytes
+ */
+static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
+                          struct pci_dev *pci,
+                          unsigned int periods, unsigned int fragsize)
+{
+       unsigned int i, idx, ofs, rest;
+       via82xx_t *chip = snd_pcm_substream_chip(substream);
+       struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
+
+       if (dev->table.area == NULL) {
+               /* the start of each lists must be aligned to 8 bytes,
+                * but the kernel pages are much bigger, so we don't care
+                */
+               if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+                                       PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8),
+                                       &dev->table) < 0)
+                       return -ENOMEM;
+       }
+       if (! dev->idx_table) {
+               dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL);
+               if (! dev->idx_table)
+                       return -ENOMEM;
+       }
+
+       /* fill the entries */
+       idx = 0;
+       ofs = 0;
+       for (i = 0; i < periods; i++) {
+               rest = fragsize;
+               /* fill descriptors for a period.
+                * a period can be split to several descriptors if it's
+                * over page boundary.
+                */
+               do {
+                       unsigned int r;
+                       unsigned int flag;
+
+                       if (idx >= VIA_TABLE_SIZE) {
+                               snd_printk(KERN_ERR "via82xx: too much table size!\n");
+                               return -EINVAL;
+                       }
+                       ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs));
+                       r = PAGE_SIZE - (ofs % PAGE_SIZE);
+                       if (rest < r)
+                               r = rest;
+                       rest -= r;
+                       if (! rest) {
+                               if (i == periods - 1)
+                                       flag = VIA_TBL_BIT_EOL; /* buffer boundary */
+                               else
+                                       flag = VIA_TBL_BIT_FLAG; /* period boundary */
+                       } else
+                               flag = 0; /* period continues to the next */
+                       // printk("via: tbl %d: at %d  size %d (rest %d)\n", idx, ofs, r, rest);
+                       ((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag);
+                       dev->idx_table[idx].offset = ofs;
+                       dev->idx_table[idx].size = r;
+                       ofs += r;
+                       idx++;
+               } while (rest > 0);
+       }
+       dev->tbl_entries = idx;
+       dev->bufsize = periods * fragsize;
+       dev->bufsize2 = dev->bufsize / 2;
+       return 0;
+}
+
+
+static int clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
+                          struct pci_dev *pci)
+{
+       if (dev->table.area) {
+               snd_dma_free_pages(&dev->table);
+               dev->table.area = NULL;
+       }
+       if (dev->idx_table) {
+               kfree(dev->idx_table);
+               dev->idx_table = NULL;
+       }
+       return 0;
+}
+
+/*
+ *  Basic I/O
+ */
+
+static inline unsigned int snd_via82xx_codec_xread(via82xx_t *chip)
+{
+       return inl(VIAREG(chip, AC97));
+}
+static inline void snd_via82xx_codec_xwrite(via82xx_t *chip, unsigned int val)
+{
+       outl(val, VIAREG(chip, AC97));
+}
+static int snd_via82xx_codec_ready(via82xx_t *chip, int secondary)
+{
+       unsigned int timeout = 1000;    /* 1ms */
+       unsigned int val;
+       
+       while (timeout-- > 0) {
+               udelay(1);
+               if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY))
+                       return val & 0xffff;
+       }
+       snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via82xx_codec_xread(chip));
+       return -EIO;
+}
+static int snd_via82xx_codec_valid(via82xx_t *chip, int secondary)
+{
+       unsigned int timeout = 1000;    /* 1ms */
+       unsigned int val, val1;
+       unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID :
+                                        VIA_REG_AC97_SECONDARY_VALID;
+       
+       while (timeout-- > 0) {
+               val = snd_via82xx_codec_xread(chip);
+               val1 = val & (VIA_REG_AC97_BUSY | stat);
+               if (val1 == stat)
+                       return val & 0xffff;
+               udelay(1);
+       }
+       return -EIO;
+}
+static void snd_via82xx_codec_wait(ac97_t *ac97)
+{
+       via82xx_t *chip = ac97->private_data;
+       int err;
+       err = snd_via82xx_codec_ready(chip, ac97->num);
+       /* here we need to wait fairly for long time.. */
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(HZ/2);
+}
+
+static void snd_via82xx_codec_write(ac97_t *ac97,
+                                   unsigned short reg,
+                                   unsigned short val)
+{
+       via82xx_t *chip = ac97->private_data;
+       unsigned int xval;
+       
+       xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
+       xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
+       xval |= reg << VIA_REG_AC97_CMD_SHIFT;
+       xval |= val << VIA_REG_AC97_DATA_SHIFT;
+       snd_via82xx_codec_xwrite(chip, xval);
+       snd_via82xx_codec_ready(chip, ac97->num);
+}
+
+static unsigned short snd_via82xx_codec_read(ac97_t *ac97, unsigned short reg)
+{
+       via82xx_t *chip = ac97->private_data;
+       unsigned int xval, val = 0xffff;
+       int again = 0;
+
+       xval = ac97->num << VIA_REG_AC97_CODEC_ID_SHIFT;
+       xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
+       xval |= VIA_REG_AC97_READ;
+       xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
+       while (1) {
+               if (again++ > 3) {
+                       snd_printk(KERN_ERR "codec_read: codec %i is not valid [0x%x]\n", ac97->num, snd_via82xx_codec_xread(chip));
+                       return 0xffff;
+               }
+               snd_via82xx_codec_xwrite(chip, xval);
+               udelay (20);
+               if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) {
+                       udelay(25);
+                       val = snd_via82xx_codec_xread(chip);
+                       break;
+               }
+       }
+       return val & 0xffff;
+}
+
+static void snd_via82xx_channel_reset(via82xx_t *chip, viadev_t *viadev)
+{
+       outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET,
+            VIADEV_REG(viadev, OFFSET_CONTROL));
+       inb(VIADEV_REG(viadev, OFFSET_CONTROL));
+       udelay(50);
+       /* disable interrupts */
+       outb(0x00, VIADEV_REG(viadev, OFFSET_CONTROL));
+       /* clear interrupts */
+       outb(0x03, VIADEV_REG(viadev, OFFSET_STATUS));
+       outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */
+       // outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR));
+       viadev->lastpos = 0;
+}
+
+
+/*
+ *  Interrupt handler
+ */
+
+static irqreturn_t snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       via82xx_t *chip = dev_id;
+       unsigned int status;
+       unsigned int i;
+
+       status = inl(VIAREG(chip, SGD_SHADOW));
+       if (! (status & chip->intr_mask)) {
+               return IRQ_NONE;
+       }
+// _skip_sgd:
+
+       /* check status for each stream */
+       spin_lock(&chip->reg_lock);
+       for (i = 0; i < chip->num_devs; i++) {
+               viadev_t *viadev = &chip->devs[i];
+               unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS));
+               c_status &= (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG|VIA_REG_STAT_STOPPED);
+               if (! c_status)
+                       continue;
+               if (viadev->substream && viadev->running) {
+                       spin_unlock(&chip->reg_lock);
+                       snd_pcm_period_elapsed(viadev->substream);
+                       spin_lock(&chip->reg_lock);
+               }
+               outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */
+       }
+       spin_unlock(&chip->reg_lock);
+       return IRQ_HANDLED;
+}
+
+/*
+ *  PCM callbacks
+ */
+
+/*
+ * trigger callback
+ */
+static int snd_via82xx_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+       via82xx_t *chip = snd_pcm_substream_chip(substream);
+       viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+       unsigned char val = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               val |= VIA_REG_CTRL_START;
+               viadev->running = 1;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               val = VIA_REG_CTRL_TERMINATE;
+               viadev->running = 0;
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               val |= VIA_REG_CTRL_PAUSE;
+               viadev->running = 0;
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               viadev->running = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+       outb(val, VIADEV_REG(viadev, OFFSET_CONTROL));
+       if (cmd == SNDRV_PCM_TRIGGER_STOP)
+               snd_via82xx_channel_reset(chip, viadev);
+       return 0;
+}
+
+static int snd_via82xx_modem_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+       via82xx_t *chip = snd_pcm_substream_chip(substream);
+       unsigned int val = 0;
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               val = snd_ac97_read(chip->ac97, AC97_GPIO_STATUS);
+               outl(val|AC97_GPIO_LINE1_OH, VIAREG(chip, GPI_STATUS));
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               val = snd_ac97_read(chip->ac97, AC97_GPIO_STATUS);
+               outl(val&~AC97_GPIO_LINE1_OH, VIAREG(chip, GPI_STATUS));
+               break;
+       default:
+               break;
+       }
+       return snd_via82xx_pcm_trigger(substream, cmd);
+}
+
+/*
+ * pointer callbacks
+ */
+
+/*
+ * calculate the linear position at the given sg-buffer index and the rest count
+ */
+
+#define check_invalid_pos(viadev,pos) \
+       ((pos) < viadev->lastpos && ((pos) >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2))
+
+static inline unsigned int calc_linear_pos(viadev_t *viadev, unsigned int idx, unsigned int count)
+{
+       unsigned int size, res;
+
+       size = viadev->idx_table[idx].size;
+       res = viadev->idx_table[idx].offset + size - count;
+
+       /* check the validity of the calculated position */
+       if (size < count) {
+               snd_printd(KERN_ERR "invalid via82xx_cur_ptr (size = %d, count = %d)\n", (int)size, (int)count);
+               res = viadev->lastpos;
+       } else if (check_invalid_pos(viadev, res)) {
+#ifdef POINTER_DEBUG
+               printk("fail: idx = %i/%i, lastpos = 0x%x, bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, count = 0x%x\n", idx, viadev->tbl_entries, viadev->lastpos, viadev->bufsize2, viadev->idx_table[idx].offset, viadev->idx_table[idx].size, count);
+#endif
+               if (count && size < count) {
+                       snd_printd(KERN_ERR "invalid via82xx_cur_ptr, using last valid pointer\n");
+                       res = viadev->lastpos;
+               } else {
+                       if (! count)
+                               /* bogus count 0 on the DMA boundary? */
+                               res = viadev->idx_table[idx].offset;
+                       else
+                               /* count register returns full size when end of buffer is reached */
+                               res = viadev->idx_table[idx].offset + size;
+                       if (check_invalid_pos(viadev, res)) {
+                               snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), using last valid pointer\n");
+                               res = viadev->lastpos;
+                       }
+               }
+       }
+       viadev->lastpos = res; /* remember the last position */
+       if (res >= viadev->bufsize)
+               res -= viadev->bufsize;
+       return res;
+}
+
+/*
+ * get the current pointer on via686
+ */
+static snd_pcm_uframes_t snd_via686_pcm_pointer(snd_pcm_substream_t *substream)
+{
+       via82xx_t *chip = snd_pcm_substream_chip(substream);
+       viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+       unsigned int idx, ptr, count, res;
+
+       snd_assert(viadev->tbl_entries, return 0);
+       if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))
+               return 0;
+
+       spin_lock(&chip->reg_lock);
+       count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)) & 0xffffff;
+       /* The via686a does not have the current index register,
+        * so we need to calculate the index from CURR_PTR.
+        */
+       ptr = inl(VIADEV_REG(viadev, OFFSET_CURR_PTR));
+       if (ptr <= (unsigned int)viadev->table.addr)
+               idx = 0;
+       else /* CURR_PTR holds the address + 8 */
+               idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries;
+       res = calc_linear_pos(viadev, idx, count);
+       spin_unlock(&chip->reg_lock);
+
+       return bytes_to_frames(substream->runtime, res);
+}
+
+/*
+ * hw_params callback:
+ * allocate the buffer and build up the buffer description table
+ */
+static int snd_via82xx_hw_params(snd_pcm_substream_t * substream,
+                                snd_pcm_hw_params_t * hw_params)
+{
+       via82xx_t *chip = snd_pcm_substream_chip(substream);
+       viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+       int err;
+
+       err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+       err = build_via_table(viadev, substream, chip->pci,
+                             params_periods(hw_params),
+                             params_period_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       snd_ac97_write(chip->ac97, AC97_LINE1_RATE, params_rate(hw_params));
+       snd_ac97_write(chip->ac97, AC97_LINE1_LEVEL, 0);
+
+       return 0;
+}
+
+/*
+ * hw_free callback:
+ * clean up the buffer description table and release the buffer
+ */
+static int snd_via82xx_hw_free(snd_pcm_substream_t * substream)
+{
+       via82xx_t *chip = snd_pcm_substream_chip(substream);
+       viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+
+       clean_via_table(viadev, substream, chip->pci);
+       snd_pcm_lib_free_pages(substream);
+       return 0;
+}
+
+
+/*
+ * set up the table pointer
+ */
+static void snd_via82xx_set_table_ptr(via82xx_t *chip, viadev_t *viadev)
+{
+       snd_via82xx_codec_ready(chip, chip->ac97_secondary);
+       outl((u32)viadev->table.addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR));
+       udelay(20);
+       snd_via82xx_codec_ready(chip, chip->ac97_secondary);
+}
+
+/*
+ * prepare callback for playback and capture
+ */
+static int snd_via82xx_pcm_prepare(snd_pcm_substream_t *substream)
+{
+       via82xx_t *chip = snd_pcm_substream_chip(substream);
+       viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+
+       snd_via82xx_channel_reset(chip, viadev);
+       /* this must be set after channel_reset */
+       snd_via82xx_set_table_ptr(chip, viadev);
+       outb(VIA_REG_TYPE_AUTOSTART|VIA_REG_TYPE_INT_EOL|VIA_REG_TYPE_INT_FLAG,
+            VIADEV_REG(viadev, OFFSET_TYPE));
+       return 0;
+}
+
+/*
+ * pcm hardware definition, identical for both playback and capture
+ */
+static snd_pcm_hardware_t snd_via82xx_hw =
+{
+       .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                SNDRV_PCM_INFO_MMAP_VALID |
+                                SNDRV_PCM_INFO_RESUME |
+                                SNDRV_PCM_INFO_PAUSE),
+       .formats =              SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+       .rates =                SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_KNOT,
+       .rate_min =             8000,
+       .rate_max =             16000,
+       .channels_min =         1,
+       .channels_max =         1,
+       .buffer_bytes_max =     128 * 1024,
+       .period_bytes_min =     32,
+       .period_bytes_max =     128 * 1024,
+       .periods_min =          2,
+       .periods_max =          VIA_TABLE_SIZE / 2,
+       .fifo_size =            0,
+};
+
+
+/*
+ * open callback skeleton
+ */
+static int snd_via82xx_modem_pcm_open(via82xx_t *chip, viadev_t *viadev, snd_pcm_substream_t * substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       int err;
+        static unsigned int rates[] = { 8000,  9600, 12000, 16000 };
+        static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+                .count = ARRAY_SIZE(rates),
+                .list = rates,
+                .mask = 0,
+        };
+
+       runtime->hw = snd_via82xx_hw;
+       
+        if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0)
+                return err;
+
+       /* we may remove following constaint when we modify table entries
+          in interrupt */
+       if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+               return err;
+
+       runtime->private_data = viadev;
+       viadev->substream = substream;
+
+       return 0;
+}
+
+
+/*
+ * open callback for playback
+ */
+static int snd_via82xx_playback_open(snd_pcm_substream_t * substream)
+{
+       via82xx_t *chip = snd_pcm_substream_chip(substream);
+       viadev_t *viadev = &chip->devs[chip->playback_devno + substream->number];
+
+       return snd_via82xx_modem_pcm_open(chip, viadev, substream);
+}
+
+/*
+ * open callback for capture
+ */
+static int snd_via82xx_capture_open(snd_pcm_substream_t * substream)
+{
+       via82xx_t *chip = snd_pcm_substream_chip(substream);
+       viadev_t *viadev = &chip->devs[chip->capture_devno + substream->pcm->device];
+
+       return snd_via82xx_modem_pcm_open(chip, viadev, substream);
+}
+
+/*
+ * close callback
+ */
+static int snd_via82xx_pcm_close(snd_pcm_substream_t * substream)
+{
+       viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
+
+       viadev->substream = NULL;
+       return 0;
+}
+
+
+/* via686 playback callbacks */
+static snd_pcm_ops_t snd_via686_playback_ops = {
+       .open =         snd_via82xx_playback_open,
+       .close =        snd_via82xx_pcm_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_via82xx_hw_params,
+       .hw_free =      snd_via82xx_hw_free,
+       .prepare =      snd_via82xx_pcm_prepare,
+       .trigger =      snd_via82xx_modem_pcm_trigger,
+       .pointer =      snd_via686_pcm_pointer,
+       .page =         snd_pcm_sgbuf_ops_page,
+};
+
+/* via686 capture callbacks */
+static snd_pcm_ops_t snd_via686_capture_ops = {
+       .open =         snd_via82xx_capture_open,
+       .close =        snd_via82xx_pcm_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_via82xx_hw_params,
+       .hw_free =      snd_via82xx_hw_free,
+       .prepare =      snd_via82xx_pcm_prepare,
+       .trigger =      snd_via82xx_modem_pcm_trigger,
+       .pointer =      snd_via686_pcm_pointer,
+       .page =         snd_pcm_sgbuf_ops_page,
+};
+
+
+static void init_viadev(via82xx_t *chip, int idx, unsigned int reg_offset, int direction)
+{
+       chip->devs[idx].reg_offset = reg_offset;
+       chip->devs[idx].direction = direction;
+       chip->devs[idx].port = chip->port + reg_offset;
+}
+
+/*
+ * create a pcm instance for via686a/b
+ */
+static int __devinit snd_via686_pcm_new(via82xx_t *chip)
+{
+       snd_pcm_t *pcm;
+       int err;
+
+       chip->playback_devno = 0;
+       chip->capture_devno = 1;
+       chip->num_devs = 2;
+       chip->intr_mask = 0x330000; /* FLAGS | EOL for MR, MW */
+
+       err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm);
+       if (err < 0)
+               return err;
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops);
+       pcm->private_data = chip;
+       strcpy(pcm->name, chip->card->shortname);
+       chip->pcms[0] = pcm;
+       init_viadev(chip, 0, VIA_REG_MO_STATUS, 0);
+       init_viadev(chip, 1, VIA_REG_MI_STATUS, 1);
+
+       if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+                                                        snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0)
+               return err;
+
+       return 0;
+}
+
+
+/*
+ *  Mixer part
+ */
+
+
+static void snd_via82xx_mixer_free_ac97_bus(ac97_bus_t *bus)
+{
+       via82xx_t *chip = bus->private_data;
+       chip->ac97_bus = NULL;
+}
+
+static void snd_via82xx_mixer_free_ac97(ac97_t *ac97)
+{
+       via82xx_t *chip = ac97->private_data;
+       chip->ac97 = NULL;
+}
+
+
+static int __devinit snd_via82xx_mixer_new(via82xx_t *chip)
+{
+       ac97_template_t ac97;
+       int err;
+       static ac97_bus_ops_t ops = {
+               .write = snd_via82xx_codec_write,
+               .read = snd_via82xx_codec_read,
+               .wait = snd_via82xx_codec_wait,
+       };
+
+       if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+               return err;
+       chip->ac97_bus->private_free = snd_via82xx_mixer_free_ac97_bus;
+       chip->ac97_bus->clock = chip->ac97_clock;
+       chip->ac97_bus->shared_type = AC97_SHARED_TYPE_VIA;
+
+       memset(&ac97, 0, sizeof(ac97));
+       ac97.private_data = chip;
+       ac97.private_free = snd_via82xx_mixer_free_ac97;
+       ac97.pci = chip->pci;
+       ac97.scaps = AC97_SCAP_SKIP_AUDIO;
+       ac97.num = chip->ac97_secondary;
+
+       if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+               return err;
+
+       return 0;
+}
+
+
+/*
+ * proc interface
+ */
+static void snd_via82xx_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+       via82xx_t *chip = entry->private_data;
+       int i;
+       
+       snd_iprintf(buffer, "%s\n\n", chip->card->longname);
+       for (i = 0; i < 0xa0; i += 4) {
+               snd_iprintf(buffer, "%02x: %08x\n", i, inl(chip->port + i));
+       }
+}
+
+static void __devinit snd_via82xx_proc_init(via82xx_t *chip)
+{
+       snd_info_entry_t *entry;
+
+       if (! snd_card_proc_new(chip->card, "via82xx", &entry))
+               snd_info_set_text_ops(entry, chip, 1024, snd_via82xx_proc_read);
+}
+
+/*
+ *
+ */
+
+static int __devinit snd_via82xx_chip_init(via82xx_t *chip)
+{
+       ac97_t ac97;
+       unsigned int val;
+       int max_count;
+       unsigned char pval;
+
+       memset(&ac97, 0, sizeof(ac97));
+       ac97.private_data = chip;
+
+       pci_read_config_byte(chip->pci, VIA_MC97_CTRL, &pval);
+       if((pval & VIA_MC97_CTRL_INIT) != VIA_MC97_CTRL_INIT) {
+               pci_write_config_byte(chip->pci, 0x44, pval|VIA_MC97_CTRL_INIT);
+               udelay(100);
+       }
+
+       pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
+       if (! (pval & VIA_ACLINK_C00_READY)) { /* codec not ready? */
+               /* deassert ACLink reset, force SYNC */
+               pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL,
+                                     VIA_ACLINK_CTRL_ENABLE |
+                                     VIA_ACLINK_CTRL_RESET |
+                                     VIA_ACLINK_CTRL_SYNC);
+               udelay(100);
+#if 1 /* FIXME: should we do full reset here for all chip models? */
+               pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, 0x00);
+               udelay(100);
+#else
+               /* deassert ACLink reset, force SYNC (warm AC'97 reset) */
+               pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL,
+                                     VIA_ACLINK_CTRL_RESET|VIA_ACLINK_CTRL_SYNC);
+               udelay(2);
+#endif
+               /* ACLink on, deassert ACLink reset, VSR, SGD data out */
+               pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT);
+               udelay(100);
+       }
+       
+       pci_read_config_byte(chip->pci, VIA_ACLINK_CTRL, &pval);
+       if ((pval & VIA_ACLINK_CTRL_INIT) != VIA_ACLINK_CTRL_INIT) {
+               /* ACLink on, deassert ACLink reset, VSR, SGD data out */
+               pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT);
+               udelay(100);
+       }
+
+       /* wait until codec ready */
+       max_count = ((3 * HZ) / 4) + 1;
+       do {
+               pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
+               if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
+                       break;
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(1);
+       } while (--max_count > 0);
+
+       if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
+               snd_printk("AC'97 codec is not ready [0x%x]\n", val);
+
+       /* and then reset codec.. */
+#if 0 /* do we need it? when? */
+       snd_via82xx_codec_ready(chip, 0);
+       snd_via82xx_codec_write(&ac97, AC97_RESET, 0x0000);
+       snd_via82xx_codec_read(&ac97, 0);
+#endif
+
+       snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
+                                VIA_REG_AC97_SECONDARY_VALID |
+                                (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+       max_count = ((3 * HZ) / 4) + 1;
+       snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
+                                VIA_REG_AC97_SECONDARY_VALID |
+                                (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+       do {
+               if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) {
+                       chip->ac97_secondary = 1;
+                       goto __ac97_ok2;
+               }
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(1);
+       } while (--max_count > 0);
+       /* This is ok, the most of motherboards have only one codec */
+
+      __ac97_ok2:
+
+       /* route FM trap to IRQ, disable FM trap */
+       // pci_write_config_byte(chip->pci, VIA_FM_NMI_CTRL, 0);
+       /* disable all GPI interrupts */
+       outl(0, VIAREG(chip, GPI_INTR));
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int snd_via82xx_suspend(snd_card_t *card, unsigned int state)
+{
+       via82xx_t *chip = card->pm_private_data;
+       int i;
+
+       for (i = 0; i < 2; i++)
+               if (chip->pcms[i])
+                       snd_pcm_suspend_all(chip->pcms[i]);
+       for (i = 0; i < chip->num_devs; i++)
+               snd_via82xx_channel_reset(chip, &chip->devs[i]);
+       synchronize_irq(chip->irq);
+       snd_ac97_suspend(chip->ac97);
+       pci_set_power_state(chip->pci, 3);
+       pci_disable_device(chip->pci);
+       return 0;
+}
+
+static int snd_via82xx_resume(snd_card_t *card, unsigned int state)
+{
+       via82xx_t *chip = card->pm_private_data;
+       int i;
+
+       pci_enable_device(chip->pci);
+       pci_set_power_state(chip->pci, 0);
+       pci_set_master(chip->pci);
+
+       snd_via82xx_chip_init(chip);
+
+       snd_ac97_resume(chip->ac97);
+
+       for (i = 0; i < chip->num_devs; i++)
+               snd_via82xx_channel_reset(chip, &chip->devs[i]);
+
+       return 0;
+}
+#endif /* CONFIG_PM */
+
+static int snd_via82xx_free(via82xx_t *chip)
+{
+       unsigned int i;
+
+       if (chip->irq < 0)
+               goto __end_hw;
+       /* disable interrupts */
+       for (i = 0; i < chip->num_devs; i++)
+               snd_via82xx_channel_reset(chip, &chip->devs[i]);
+       synchronize_irq(chip->irq);
+      __end_hw:
+       if (chip->irq >= 0)
+               free_irq(chip->irq, (void *)chip);
+       pci_release_regions(chip->pci);
+       pci_disable_device(chip->pci);
+       kfree(chip);
+       return 0;
+}
+
+static int snd_via82xx_dev_free(snd_device_t *device)
+{
+       via82xx_t *chip = device->device_data;
+       return snd_via82xx_free(chip);
+}
+
+static int __devinit snd_via82xx_create(snd_card_t * card,
+                                       struct pci_dev *pci,
+                                       int chip_type,
+                                       int revision,
+                                       unsigned int ac97_clock,
+                                       via82xx_t ** r_via)
+{
+       via82xx_t *chip;
+       int err;
+        static snd_device_ops_t ops = {
+               .dev_free =     snd_via82xx_dev_free,
+        };
+
+       if ((err = pci_enable_device(pci)) < 0)
+               return err;
+
+       if ((chip = kcalloc(1, sizeof(*chip), GFP_KERNEL)) == NULL) {
+               pci_disable_device(pci);
+               return -ENOMEM;
+       }
+
+       spin_lock_init(&chip->reg_lock);
+       chip->card = card;
+       chip->pci = pci;
+       chip->irq = -1;
+
+       if ((err = pci_request_regions(pci, card->driver)) < 0) {
+               kfree(chip);
+               pci_disable_device(pci);
+               return err;
+       }
+       chip->port = pci_resource_start(pci, 0);
+       if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+                       card->driver, (void *)chip)) {
+               snd_printk("unable to grab IRQ %d\n", pci->irq);
+               snd_via82xx_free(chip);
+               return -EBUSY;
+       }
+       chip->irq = pci->irq;
+       if (ac97_clock >= 8000 && ac97_clock <= 48000)
+               chip->ac97_clock = ac97_clock;
+       synchronize_irq(chip->irq);
+
+       if ((err = snd_via82xx_chip_init(chip)) < 0) {
+               snd_via82xx_free(chip);
+               return err;
+       }
+
+       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+               snd_via82xx_free(chip);
+               return err;
+       }
+
+       /* The 8233 ac97 controller does not implement the master bit
+        * in the pci command register. IMHO this is a violation of the PCI spec.
+        * We call pci_set_master here because it does not hurt. */
+       pci_set_master(pci);
+
+       snd_card_set_dev(card, &pci->dev);
+
+       *r_via = chip;
+       return 0;
+}
+
+
+static int __devinit snd_via82xx_probe(struct pci_dev *pci,
+                                      const struct pci_device_id *pci_id)
+{
+       static int dev;
+       snd_card_t *card;
+       via82xx_t *chip;
+       unsigned char revision;
+       int chip_type = 0, card_type;
+       unsigned int i;
+       int err;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+       if (card == NULL)
+               return -ENOMEM;
+
+       card_type = pci_id->driver_data;
+       pci_read_config_byte(pci, PCI_REVISION_ID, &revision);
+       switch (card_type) {
+       case TYPE_CARD_VIA82XX_MODEM:
+               strcpy(card->driver, "VIA82XX-MODEM");
+               sprintf(card->shortname, "VIA 82XX modem");
+               break;
+       default:
+               snd_printk(KERN_ERR "invalid card type %d\n", card_type);
+               err = -EINVAL;
+               goto __error;
+       }
+               
+       if ((err = snd_via82xx_create(card, pci, chip_type, revision, ac97_clock[dev], &chip)) < 0)
+               goto __error;
+       if ((err = snd_via82xx_mixer_new(chip)) < 0)
+               goto __error;
+
+       if ((err = snd_via686_pcm_new(chip)) < 0 )
+               goto __error;
+
+       snd_card_set_pm_callback(card, snd_via82xx_suspend, snd_via82xx_resume, chip);
+
+       /* disable interrupts */
+       for (i = 0; i < chip->num_devs; i++)
+               snd_via82xx_channel_reset(chip, &chip->devs[i]);
+
+       sprintf(card->longname, "%s at 0x%lx, irq %d",
+               card->shortname, chip->port, chip->irq);
+
+       snd_via82xx_proc_init(chip);
+
+       if ((err = snd_card_register(card)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       pci_set_drvdata(pci, card);
+       dev++;
+       return 0;
+
+ __error:
+       snd_card_free(card);
+       return err;
+}
+
+static void __devexit snd_via82xx_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+       .name = "VIA 82xx Modem",
+       .id_table = snd_via82xx_modem_ids,
+       .probe = snd_via82xx_probe,
+       .remove = __devexit_p(snd_via82xx_remove),
+       SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_via82xx_init(void)
+{
+       return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_via82xx_exit(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_via82xx_init)
+module_exit(alsa_card_via82xx_exit)
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
new file mode 100644 (file)
index 0000000..bb2c8e9
--- /dev/null
@@ -0,0 +1,807 @@
+/*
+ *   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
+ */
+
+/* USX2Y "rawusb" aka hwdep_pcm implementation
+
+ Its usb's unableness to atomically handle power of 2 period sized data chuncs
+ at standard samplerates,
+ what led to this part of the usx2y module: 
+ It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
+ The pair uses a hardware dependant alsa-device for mmaped pcm transport.
+ Advantage achieved:
+         The usb_hc moves pcm data from/into memory via DMA.
+         That memory is mmaped by jack's usx2y driver.
+         Jack's usx2y driver is the first/last to read/write pcm data.
+         Read/write is a combination of power of 2 period shaping and
+         float/int conversation.
+         Compared to mainline alsa/jack we leave out power of 2 period shaping inside
+         snd-usb-usx2y which needs memcpy() and additional buffers.
+         As a side effect possible unwanted pcm-data coruption resulting of
+         standard alsa's snd-usb-usx2y period shaping scheme falls away.
+         Result is sane jack operation at buffering schemes down to 128frames,
+         2 periods.
+         plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
+         cost of easier triggered i.e. aeolus xruns (128 or 256frames,
+         2periods works but is useless cause of crackling).
+ This is a first "proof of concept" implementation.
+ Later, funcionalities should migrate to more apropriate places:
+ Userland:
+ - The jackd could mmap its float-pcm buffers directly from alsa-lib.
+ - alsa-lib could provide power of 2 period sized shaping combined with int/float
+   conversation.
+   Currently the usx2y jack driver provides above 2 services.
+ Kernel:
+ - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio
+   devices can use it.
+   Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y. 
+*/
+
+#include "usbusx2yaudio.c"
+
+#if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) &&  USX2Y_NRPACKS == 1)
+
+#include <sound/hwdep.h>
+
+
+static int usX2Y_usbpcm_urb_capt_retire(snd_usX2Y_substream_t *subs)
+{
+       struct urb      *urb = subs->completed_urb;
+       snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
+       int             i, lens = 0, hwptr_done = subs->hwptr_done;
+       usX2Ydev_t      *usX2Y = subs->usX2Y;
+       if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) { //FIXME
+               int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
+               if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
+                       head = 0;
+               usX2Y->hwdep_pcm_shm->capture_iso_start = head;
+               snd_printdd("cap start %i\n", head);
+       }
+       for (i = 0; i < nr_of_packs(); i++) {
+               if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
+                       snd_printk("activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
+                       return urb->iso_frame_desc[i].status;
+               }
+               lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
+       }
+       if ((hwptr_done += lens) >= runtime->buffer_size)
+               hwptr_done -= runtime->buffer_size;
+       subs->hwptr_done = hwptr_done;
+       subs->transfer_done += lens;
+       /* update the pointer, call callback if necessary */
+       if (subs->transfer_done >= runtime->period_size) {
+               subs->transfer_done -= runtime->period_size;
+               snd_pcm_period_elapsed(subs->pcm_substream);
+       }
+       return 0;
+}
+
+static inline int usX2Y_iso_frames_per_buffer(snd_pcm_runtime_t *runtime, usX2Ydev_t * usX2Y)
+{
+       return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
+}
+
+/*
+ * prepare urb for playback data pipe
+ *
+ * we copy the data directly from the pcm buffer.
+ * the current position to be copied is held in hwptr field.
+ * since a urb can handle only a single linear buffer, if the total
+ * transferred area overflows the buffer boundary, we cannot send
+ * it directly from the buffer.  thus the data is once copied to
+ * a temporary buffer and urb points to that.
+ */
+static int usX2Y_hwdep_urb_play_prepare(snd_usX2Y_substream_t *subs,
+                                 struct urb *urb)
+{
+       int count, counts, pack;
+       usX2Ydev_t *usX2Y = subs->usX2Y;
+       struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
+       snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
+
+       if (0 > shm->playback_iso_start) {
+               shm->playback_iso_start = shm->captured_iso_head -
+                       usX2Y_iso_frames_per_buffer(runtime, usX2Y);
+               if (0 > shm->playback_iso_start)
+                       shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
+               shm->playback_iso_head = shm->playback_iso_start;
+       }
+
+       count = 0;
+       for (pack = 0; pack < nr_of_packs(); pack++) {
+               /* calculate the size of a packet */
+               counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
+               if (counts < 43 || counts > 50) {
+                       snd_printk("should not be here with counts=%i\n", counts);
+                       return -EPIPE;
+               }
+               /* set up descriptor */
+               urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
+               urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
+               if (atomic_read(&subs->state) != state_RUNNING)
+                       memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
+                              urb->iso_frame_desc[pack].length);
+               if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
+                       shm->playback_iso_head = 0;
+               count += counts;
+       }
+       urb->transfer_buffer_length = count * usX2Y->stride;
+       return 0;
+}
+
+
+static inline void usX2Y_usbpcm_urb_capt_iso_advance(snd_usX2Y_substream_t *subs, struct urb *urb)
+{
+       int pack;
+       for (pack = 0; pack < nr_of_packs(); ++pack) {
+               struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
+               if (NULL != subs) {
+                       snd_usX2Y_hwdep_pcm_shm_t *shm = subs->usX2Y->hwdep_pcm_shm;
+                       int head = shm->captured_iso_head + 1;
+                       if (head >= ARRAY_SIZE(shm->captured_iso))
+                               head = 0;
+                       shm->captured_iso[head].frame = urb->start_frame + pack;
+                       shm->captured_iso[head].offset = desc->offset;
+                       shm->captured_iso[head].length = desc->actual_length;
+                       shm->captured_iso_head = head;
+                       shm->captured_iso_frames++;
+               }
+               if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
+                   desc->length >= SSS)
+                       desc->offset -= (SSS - desc->length);
+       }
+}
+
+static inline int usX2Y_usbpcm_usbframe_complete(snd_usX2Y_substream_t *capsubs,
+                                          snd_usX2Y_substream_t *capsubs2,
+                                          snd_usX2Y_substream_t *playbacksubs, int frame)
+{
+       int err, state;
+       struct urb *urb = playbacksubs->completed_urb;
+
+       state = atomic_read(&playbacksubs->state);
+       if (NULL != urb) {
+               if (state == state_RUNNING)
+                       usX2Y_urb_play_retire(playbacksubs, urb);
+               else
+                       if (state >= state_PRERUNNING) {
+                               atomic_inc(&playbacksubs->state);
+                       }
+       } else {
+               switch (state) {
+               case state_STARTING1:
+                       urb = playbacksubs->urb[0];
+                       atomic_inc(&playbacksubs->state);
+                       break;
+               case state_STARTING2:
+                       urb = playbacksubs->urb[1];
+                       atomic_inc(&playbacksubs->state);
+                       break;
+               }
+       }
+       if (urb) {
+               if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
+                   (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
+                       return err;
+               }
+       }
+       
+       playbacksubs->completed_urb = NULL;
+
+       state = atomic_read(&capsubs->state);
+       if (state >= state_PREPARED) {
+               if (state == state_RUNNING) {
+                       if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
+                               return err;
+               } else {
+                       if (state >= state_PRERUNNING)
+                               atomic_inc(&capsubs->state);
+               }
+               usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
+               if (NULL != capsubs2)
+                       usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
+               if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
+                       return err;
+               if (NULL != capsubs2)
+                       if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
+                               return err;
+       }
+       capsubs->completed_urb = NULL;
+       if (NULL != capsubs2)
+               capsubs2->completed_urb = NULL;
+       return 0;
+}
+
+
+static void i_usX2Y_usbpcm_urb_complete(struct urb *urb, struct pt_regs *regs)
+{
+       snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
+       usX2Ydev_t *usX2Y = subs->usX2Y;
+       snd_usX2Y_substream_t *capsubs, *capsubs2, *playbacksubs;
+
+       if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
+               snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n", usb_get_current_frame_number(usX2Y->chip.dev), subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", urb->status, urb->start_frame);
+               return;
+       }
+       if (unlikely(urb->status)) {
+               usX2Y_error_urb_status(usX2Y, subs, urb);
+               return;
+       }
+       if (likely((0xFFFF & urb->start_frame) == usX2Y->wait_iso_frame))
+               subs->completed_urb = urb;
+       else {
+               usX2Y_error_sequence(usX2Y, subs, urb);
+               return;
+       }
+
+       capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
+       capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
+       playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
+       if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
+           (NULL == capsubs2 || capsubs2->completed_urb) &&
+           (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
+               if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
+                       if (nr_of_packs() <= urb->start_frame &&
+                           urb->start_frame <= (2 * nr_of_packs() - 1))        // uhci and ohci
+                               usX2Y->wait_iso_frame = urb->start_frame - nr_of_packs();
+                       else
+                               usX2Y->wait_iso_frame +=  nr_of_packs();
+               } else {
+                       snd_printdd("\n");
+                       usX2Y_clients_stop(usX2Y);
+               }
+       }
+}
+
+
+static void usX2Y_hwdep_urb_release(struct urb** urb)
+{
+       usb_kill_urb(*urb);
+       usb_free_urb(*urb);
+       *urb = NULL;
+}
+
+/*
+ * release a substream
+ */
+static void usX2Y_usbpcm_urbs_release(snd_usX2Y_substream_t *subs)
+{
+       int i;
+       snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
+       for (i = 0; i < NRURBS; i++)
+               usX2Y_hwdep_urb_release(subs->urb + i);
+}
+
+static void usX2Y_usbpcm_subs_startup_finish(usX2Ydev_t * usX2Y)
+{
+       usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
+       usX2Y->prepare_subs = NULL;
+}
+
+static void i_usX2Y_usbpcm_subs_startup(struct urb *urb, struct pt_regs *regs)
+{
+       snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
+       usX2Ydev_t *usX2Y = subs->usX2Y;
+       snd_usX2Y_substream_t *prepare_subs = usX2Y->prepare_subs;
+       if (NULL != prepare_subs &&
+           urb->start_frame == prepare_subs->urb[0]->start_frame) {
+               atomic_inc(&prepare_subs->state);
+               if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
+                       snd_usX2Y_substream_t *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
+                       if (cap_subs2 != NULL)
+                               atomic_inc(&cap_subs2->state);
+               }
+               usX2Y_usbpcm_subs_startup_finish(usX2Y);
+               wake_up(&usX2Y->prepare_wait_queue);
+       }
+
+       i_usX2Y_usbpcm_urb_complete(urb, regs);
+}
+
+/*
+ * initialize a substream's urbs
+ */
+static int usX2Y_usbpcm_urbs_allocate(snd_usX2Y_substream_t *subs)
+{
+       int i;
+       unsigned int pipe;
+       int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
+       struct usb_device *dev = subs->usX2Y->chip.dev;
+
+       pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
+                       usb_rcvisocpipe(dev, subs->endpoint);
+       subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
+       if (!subs->maxpacksize)
+               return -EINVAL;
+
+       /* allocate and initialize data urbs */
+       for (i = 0; i < NRURBS; i++) {
+               struct urb** purb = subs->urb + i;
+               if (*purb) {
+                       usb_kill_urb(*purb);
+                       continue;
+               }
+               *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
+               if (NULL == *purb) {
+                       usX2Y_usbpcm_urbs_release(subs);
+                       return -ENOMEM;
+               }
+               (*purb)->transfer_buffer = is_playback ?
+                       subs->usX2Y->hwdep_pcm_shm->playback : (
+                               subs->endpoint == 0x8 ?
+                               subs->usX2Y->hwdep_pcm_shm->capture0x8 :
+                               subs->usX2Y->hwdep_pcm_shm->capture0xA);
+
+               (*purb)->dev = dev;
+               (*purb)->pipe = pipe;
+               (*purb)->number_of_packets = nr_of_packs();
+               (*purb)->context = subs;
+               (*purb)->interval = 1;
+               (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
+       }
+       return 0;
+}
+
+/*
+ * free the buffer
+ */
+static int snd_usX2Y_usbpcm_hw_free(snd_pcm_substream_t *substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data,
+               *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
+       down(&subs->usX2Y->prepare_mutex);
+       snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
+
+       if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
+               snd_usX2Y_substream_t *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
+               atomic_set(&subs->state, state_STOPPED);
+               usX2Y_usbpcm_urbs_release(subs);
+               if (!cap_subs->pcm_substream ||
+                   !cap_subs->pcm_substream->runtime ||
+                   !cap_subs->pcm_substream->runtime->status ||
+                   cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
+                       atomic_set(&cap_subs->state, state_STOPPED);
+                       if (NULL != cap_subs2)
+                               atomic_set(&cap_subs2->state, state_STOPPED);
+                       usX2Y_usbpcm_urbs_release(cap_subs);
+                       if (NULL != cap_subs2)
+                               usX2Y_usbpcm_urbs_release(cap_subs2);
+               }
+       } else {
+               snd_usX2Y_substream_t *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
+               if (atomic_read(&playback_subs->state) < state_PREPARED) {
+                       atomic_set(&subs->state, state_STOPPED);
+                       if (NULL != cap_subs2)
+                               atomic_set(&cap_subs2->state, state_STOPPED);
+                       usX2Y_usbpcm_urbs_release(subs);
+                       if (NULL != cap_subs2)
+                               usX2Y_usbpcm_urbs_release(cap_subs2);
+               }
+       }
+       up(&subs->usX2Y->prepare_mutex);
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static void usX2Y_usbpcm_subs_startup(snd_usX2Y_substream_t *subs)
+{
+       usX2Ydev_t * usX2Y = subs->usX2Y;
+       usX2Y->prepare_subs = subs;
+       subs->urb[0]->start_frame = -1;
+       smp_wmb();      // Make shure above modifications are seen by i_usX2Y_subs_startup()
+       usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
+}
+
+static int usX2Y_usbpcm_urbs_start(snd_usX2Y_substream_t *subs)
+{
+       int     p, u, err,
+               stream = subs->pcm_substream->stream;
+       usX2Ydev_t *usX2Y = subs->usX2Y;
+
+       if (SNDRV_PCM_STREAM_CAPTURE == stream) {
+               usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
+               usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
+       }
+
+       for (p = 0; 3 >= (stream + p); p += 2) {
+               snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
+               if (subs != NULL) {
+                       if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
+                               return err;
+                       subs->completed_urb = NULL;
+               }
+       }
+
+       for (p = 0; p < 4; p++) {
+               snd_usX2Y_substream_t *subs = usX2Y->subs[p];
+               if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
+                       goto start;
+       }
+       usX2Y->wait_iso_frame = -1;
+
+ start:
+       usX2Y_usbpcm_subs_startup(subs);
+       for (u = 0; u < NRURBS; u++) {
+               for (p = 0; 3 >= (stream + p); p += 2) {
+                       snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
+                       if (subs != NULL) {
+                               struct urb *urb = subs->urb[u];
+                               if (usb_pipein(urb->pipe)) {
+                                       unsigned long pack;
+                                       if (0 == u)
+                                               atomic_set(&subs->state, state_STARTING3);
+                                       urb->dev = usX2Y->chip.dev;
+                                       urb->transfer_flags = URB_ISO_ASAP;
+                                       for (pack = 0; pack < nr_of_packs(); pack++) {
+                                               urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
+                                               urb->iso_frame_desc[pack].length = subs->maxpacksize;
+                                       }
+                                       urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs(); 
+                                       if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
+                                               snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
+                                               err = -EPIPE;
+                                               goto cleanup;
+                                       }  else {
+                                               snd_printdd("%i\n", urb->start_frame);
+                                               if (0 > usX2Y->wait_iso_frame)
+                                                       usX2Y->wait_iso_frame = urb->start_frame;
+                                       }
+                                       urb->transfer_flags = 0;
+                               } else {
+                                       atomic_set(&subs->state, state_STARTING1);
+                                       break;
+                               }                       
+                       }
+               }
+       }
+       err = 0;
+       wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
+       if (atomic_read(&subs->state) != state_PREPARED)
+               err = -EPIPE;
+               
+ cleanup:
+       if (err) {
+               usX2Y_subs_startup_finish(usX2Y);       // Call it now
+               usX2Y_clients_stop(usX2Y);              // something is completely wroong > stop evrything                      
+       }
+       return err;
+}
+
+/*
+ * prepare callback
+ *
+ * set format and initialize urbs
+ */
+static int snd_usX2Y_usbpcm_prepare(snd_pcm_substream_t *substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
+       usX2Ydev_t *usX2Y = subs->usX2Y;
+       snd_usX2Y_substream_t *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
+       int err = 0;
+       snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
+
+       if (NULL == usX2Y->hwdep_pcm_shm) {
+               if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(snd_usX2Y_hwdep_pcm_shm_t), GFP_KERNEL)))
+                       return -ENOMEM;
+               memset(usX2Y->hwdep_pcm_shm, 0, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
+       }
+
+       down(&usX2Y->prepare_mutex);
+       usX2Y_subs_prepare(subs);
+// Start hardware streams
+// SyncStream first....
+       if (atomic_read(&capsubs->state) < state_PREPARED) {
+               if (usX2Y->format != runtime->format)
+                       if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
+                               goto up_prepare_mutex;
+               if (usX2Y->rate != runtime->rate)
+                       if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
+                               goto up_prepare_mutex;
+               snd_printdd("starting capture pipe for %s\n", subs == capsubs ? "self" : "playpipe");
+               if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
+                       goto up_prepare_mutex;
+       }
+
+       if (subs != capsubs) {
+               usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
+               if (atomic_read(&subs->state) < state_PREPARED) {
+                       while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) > usX2Y->hwdep_pcm_shm->captured_iso_frames) {
+                               signed long timeout;
+                               snd_printd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", usX2Y_iso_frames_per_buffer(runtime, usX2Y), usX2Y->hwdep_pcm_shm->captured_iso_frames);
+                               set_current_state(TASK_INTERRUPTIBLE);
+                               timeout = schedule_timeout(HZ/100 + 1);
+                               if (signal_pending(current)) {
+                                       err = -ERESTARTSYS;
+                                       goto up_prepare_mutex;
+                               }
+                       } 
+                       if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
+                               goto up_prepare_mutex;
+               }
+               snd_printd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", usX2Y_iso_frames_per_buffer(runtime, usX2Y), usX2Y->hwdep_pcm_shm->captured_iso_frames);
+       } else
+               usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
+
+ up_prepare_mutex:
+       up(&usX2Y->prepare_mutex);
+       return err;
+}
+
+static snd_pcm_hardware_t snd_usX2Y_4c =
+{
+       .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
+       .rates =                   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+       .rate_min =                44100,
+       .rate_max =                48000,
+       .channels_min =            2,
+       .channels_max =            4,
+       .buffer_bytes_max =     (2*128*1024),
+       .period_bytes_min =     64,
+       .period_bytes_max =     (128*1024),
+       .periods_min =          2,
+       .periods_max =          1024,
+       .fifo_size =              0
+};
+
+
+
+static int snd_usX2Y_usbpcm_open(snd_pcm_substream_t *substream)
+{
+       snd_usX2Y_substream_t   *subs = ((snd_usX2Y_substream_t **)
+                                        snd_pcm_substream_chip(substream))[substream->stream];
+       snd_pcm_runtime_t       *runtime = substream->runtime;
+
+       if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
+               return -EBUSY;
+
+       runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
+               (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
+       runtime->private_data = subs;
+       subs->pcm_substream = substream;
+       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
+       return 0;
+}
+
+
+static int snd_usX2Y_usbpcm_close(snd_pcm_substream_t *substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
+       int err = 0;
+       snd_printd("\n");
+       subs->pcm_substream = NULL;
+       return err;
+}
+
+
+static snd_pcm_ops_t snd_usX2Y_usbpcm_ops = 
+{
+       .open =         snd_usX2Y_usbpcm_open,
+       .close =        snd_usX2Y_usbpcm_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_usX2Y_pcm_hw_params,
+       .hw_free =      snd_usX2Y_usbpcm_hw_free,
+       .prepare =      snd_usX2Y_usbpcm_prepare,
+       .trigger =      snd_usX2Y_pcm_trigger,
+       .pointer =      snd_usX2Y_pcm_pointer,
+};
+
+
+static int usX2Y_pcms_lock_check(snd_card_t *card)
+{
+       struct list_head *list;
+       snd_device_t *dev;
+       snd_pcm_t *pcm;
+       int err = 0;
+       list_for_each(list, &card->devices) {
+               dev = snd_device(list);
+               if (dev->type != SNDRV_DEV_PCM)
+                       continue;
+               pcm = dev->device_data;
+               down(&pcm->open_mutex);
+       }
+       list_for_each(list, &card->devices) {
+               int s;
+               dev = snd_device(list);
+               if (dev->type != SNDRV_DEV_PCM)
+                       continue;
+               pcm = dev->device_data;
+               for (s = 0; s < 2; ++s) {
+                       snd_pcm_substream_t *substream;
+                       substream = pcm->streams[s].substream;
+                       if (substream && substream->open_flag)
+                               err = -EBUSY;
+               }
+       }
+       return err;
+}
+
+
+static void usX2Y_pcms_unlock(snd_card_t *card)
+{
+       struct list_head *list;
+       snd_device_t *dev;
+       snd_pcm_t *pcm;
+       list_for_each(list, &card->devices) {
+               dev = snd_device(list);
+               if (dev->type != SNDRV_DEV_PCM)
+                       continue;
+               pcm = dev->device_data;
+               up(&pcm->open_mutex);
+       }
+}
+
+
+static int snd_usX2Y_hwdep_pcm_open(snd_hwdep_t *hw, struct file *file)
+{
+       // we need to be the first 
+       snd_card_t *card = hw->card;
+       int err = usX2Y_pcms_lock_check(card);
+       if (0 == err)
+               usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
+       usX2Y_pcms_unlock(card);
+       return err;
+}
+
+
+static int snd_usX2Y_hwdep_pcm_release(snd_hwdep_t *hw, struct file *file)
+{
+       snd_card_t *card = hw->card;
+       int err = usX2Y_pcms_lock_check(card);
+       if (0 == err)
+               usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
+       usX2Y_pcms_unlock(card);
+       return err;
+}
+
+
+static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
+{
+}
+
+
+static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
+{
+}
+
+
+static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
+{
+       unsigned long offset;
+       struct page *page;
+       void *vaddr;
+
+       offset = area->vm_pgoff << PAGE_SHIFT;
+       offset += address - area->vm_start;
+       snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
+       vaddr = (char*)((usX2Ydev_t*)area->vm_private_data)->hwdep_pcm_shm + offset;
+       page = virt_to_page(vaddr);
+
+       if (type)
+               *type = VM_FAULT_MINOR;
+
+       return page;
+}
+
+
+static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
+       .open = snd_usX2Y_hwdep_pcm_vm_open,
+       .close = snd_usX2Y_hwdep_pcm_vm_close,
+       .nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
+};
+
+
+static int snd_usX2Y_hwdep_pcm_mmap(snd_hwdep_t * hw, struct file *filp, struct vm_area_struct *area)
+{
+       unsigned long   size = (unsigned long)(area->vm_end - area->vm_start);
+       usX2Ydev_t      *usX2Y = (usX2Ydev_t*)hw->private_data;
+
+       if (!(((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT))
+               return -EBUSY;
+
+       /* if userspace tries to mmap beyond end of our buffer, fail */ 
+       if (size > PAGE_ALIGN(sizeof(snd_usX2Y_hwdep_pcm_shm_t))) {
+               snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(snd_usX2Y_hwdep_pcm_shm_t)); 
+               return -EINVAL;
+       }
+
+       if (!usX2Y->hwdep_pcm_shm) {
+               return -ENODEV;
+       }
+       area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
+       area->vm_flags |= VM_RESERVED;
+       snd_printd("vm_flags=0x%lX\n", area->vm_flags);
+       area->vm_private_data = hw->private_data;
+       return 0;
+}
+
+
+static void snd_usX2Y_hwdep_pcm_private_free(snd_hwdep_t *hwdep)
+{
+       usX2Ydev_t *usX2Y = (usX2Ydev_t *)hwdep->private_data;
+       if (NULL != usX2Y->hwdep_pcm_shm)
+               snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
+}
+
+
+static void snd_usX2Y_usbpcm_private_free(snd_pcm_t *pcm)
+{
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+
+int usX2Y_hwdep_pcm_new(snd_card_t* card)
+{
+       int err;
+       snd_hwdep_t *hw;
+       snd_pcm_t *pcm;
+       struct usb_device *dev = usX2Y(card)->chip.dev;
+       if (1 != nr_of_packs())
+               return 0;
+
+       if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0) {
+               snd_printd("\n");
+               return err;
+       }
+       hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
+       hw->private_data = usX2Y(card);
+       hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
+       hw->ops.open = snd_usX2Y_hwdep_pcm_open;
+       hw->ops.release = snd_usX2Y_hwdep_pcm_release;
+       hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
+       hw->exclusive = 1;
+       sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
+
+       err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
+       if (err < 0) {
+               return err;
+       }
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
+
+       pcm->private_data = usX2Y(card)->subs;
+       pcm->private_free = snd_usX2Y_usbpcm_private_free;
+       pcm->info_flags = 0;
+
+       sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
+       if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+                                                    SNDRV_DMA_TYPE_CONTINUOUS,
+                                                    snd_dma_continuous_data(GFP_KERNEL),
+                                                    64*1024, 128*1024)) ||
+           0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+                                                    SNDRV_DMA_TYPE_CONTINUOUS,
+                                                    snd_dma_continuous_data(GFP_KERNEL),
+                                                    64*1024, 128*1024))) {
+               snd_usX2Y_usbpcm_private_free(pcm);
+               return err;
+       }
+
+
+       return 0;
+}
+
+#else
+
+int usX2Y_hwdep_pcm_new(snd_card_t* card)
+{
+       return 0;
+}
+
+#endif
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.h b/sound/usb/usx2y/usx2yhwdeppcm.h
new file mode 100644 (file)
index 0000000..d68f0cb
--- /dev/null
@@ -0,0 +1,21 @@
+#define MAXPACK 50
+#define MAXBUFFERMS 100
+#define MAXSTRIDE 3
+
+#define SSS (((MAXPACK*MAXBUFFERMS*MAXSTRIDE + 4096) / 4096) * 4096)
+struct snd_usX2Y_hwdep_pcm_shm {
+       char playback[SSS];
+       char capture0x8[SSS];
+       char capture0xA[SSS];
+       volatile int playback_iso_head;
+       int playback_iso_start;
+       struct {
+               int     frame,
+                       offset,
+                       length;
+       } captured_iso[128];
+       volatile int captured_iso_head;
+       volatile unsigned captured_iso_frames;
+       int capture_iso_start;
+};
+typedef struct snd_usX2Y_hwdep_pcm_shm snd_usX2Y_hwdep_pcm_shm_t;